Repository navigation
Closed as not planned
Description
Foundation: Single-Block Selection with Visual Highlighting
Implement core single-block selection functionality with CSS-based visual highlighting as the foundation for multi-node selection features.
Dependencies
- Enhanced Visual Parent/Child Relationships with Connecting Lines #31 (Enhanced Visual Parent/Child Relationships) - Should be completed for full hierarchical context
Related Issues
- Part of multi-node selection epic (foundational component)
Requirements
Core Selection State Management
- Selection Store: Create reactive Svelte store for tracking selection state
- Block-Level State: Each node tracks its own selected state via store subscription
- State Persistence: Selection state persists across component re-renders
- State Cleanup: Automatic cleanup when components are destroyed
- File Location: Create
src/lib/stores/selectionStore.tsfor centralized state management
Visual Highlighting System
- CSS-Based Highlighting: Use CSS classes for selection visual feedback (no inline styles)
- Theme Integration: Leverage existing design system color tokens for highlighting
- Smooth Transitions: 200ms ease transition for selection state changes
- Accessibility: High contrast highlighting that meets WCAG standards
- File Location: Add selection styles to
src/lib/design/tokens/selection.css
Click-Based Selection
- Single Click: Focus and select individual nodes
- Clear Selection: Click on empty space or different node to clear current selection
- Visual Feedback: Immediate visual response to selection changes
- Event Handling: Proper event delegation to avoid performance issues
- File Location: Enhance existing
src/lib/design/components/BaseNode.svelte
Technical Implementation Notes
Selection State Architecture
// src/lib/stores/selectionStore.ts
interface SelectionState {
selectedBlocks: string[]; // Array of block IDs
mode: 'single' | 'range' | 'multi';
lastSelectedBlock: string | null;
direction: 'up' | 'down' | null;
}
interface SelectionStore extends Readable<SelectionState> {
selectBlock: (blockId: string) => void;
clearSelection: () => void;
isSelected: (blockId: string) => boolean;
getSelectedCount: () => number;
}CSS Design Tokens Pattern
/* src/lib/design/tokens/selection.css */
:root {
--selection-background: var(--color-accent-subtle);
--selection-background-hover: var(--color-accent-muted);
--selection-border: var(--color-accent-border);
--selection-transition: background-color 200ms ease;
}
[data-theme="dark"] {
--selection-background: var(--color-accent-subtle-dark);
--selection-background-hover: var(--color-accent-muted-dark);
--selection-border: var(--color-accent-border-dark);
}BaseNode Integration Pattern
<\!-- src/lib/design/components/BaseNode.svelte -->
<script lang="ts">
import { selectionStore } from '$lib/stores/selectionStore';
export let nodeId: string;
$: isSelected = $selectionStore.selectedBlocks.includes(nodeId);
function handleClick(event: MouseEvent) {
if (\!event.shiftKey && \!event.ctrlKey) {
selectionStore.selectBlock(nodeId);
}
}
</script>
<div
class="base-node"
class:selected={isSelected}
on:click={handleClick}
role="treeitem"
aria-selected={isSelected}
tabindex="0"
>
<\!-- existing node content -->
</div>
<style>
.base-node {
transition: var(--selection-transition);
cursor: pointer;
}
.base-node.selected {
background-color: var(--selection-background);
}
.base-node.selected:hover {
background-color: var(--selection-background-hover);
}
</style>Mock Data Integration
// src/lib/services/mockSelectionService.ts
export class MockSelectionService {
private selectedNodes = new Set<string>();
selectNode(nodeId: string): void {
this.selectedNodes.clear(); // Single selection mode
this.selectedNodes.add(nodeId);
this.notifySelectionChange();
}
clearSelection(): void {
this.selectedNodes.clear();
this.notifySelectionChange();
}
isNodeSelected(nodeId: string): boolean {
return this.selectedNodes.has(nodeId);
}
getSelectedNodes(): string[] {
return Array.from(this.selectedNodes);
}
private notifySelectionChange(): void {
// Integration point for future features
console.log('Selection changed:', this.getSelectedNodes());
}
}Acceptance Criteria
Visual Requirements
- Single click on node highlights it with themed background color
- Previously selected node is automatically deselected when new node is selected
- Smooth 200ms transition animation when selection state changes
- Highlighting uses design system color tokens (consistent with theme)
- High contrast highlighting meets WCAG AA standards for accessibility
- No visual glitches or flashing during selection state transitions
Interaction Requirements
- Click on node immediately selects it and provides visual feedback
- Click on empty space (NodeTree background) clears current selection
- Click on different node switches selection to that node
- Selection state persists during component re-renders and navigation
- No selection conflicts between multiple NodeTree instances
Accessibility Requirements
- Selected nodes have
aria-selected="true"attribute - Keyboard focus can navigate to selected nodes
- Screen readers announce selection state changes
- High contrast mode preserves selection visibility
- Focus indicators remain visible on selected nodes
Performance Requirements
- Selection changes respond within 16ms (60fps) for smooth animation
- No performance degradation with 100+ nodes in hierarchy
- Event handling uses efficient delegation patterns
- CSS-based highlighting avoids layout thrashing
- Memory usage remains stable during extended selection operations
Integration Requirements
- Works seamlessly with existing BaseNode and NodeTree components
- Compatible with current mock data service patterns
- No breaking changes to existing node rendering or state management
- Selection store can be easily extended for future multi-selection features
- Integrates with existing design system tokens and theme switching
Code Quality Requirements
- TypeScript compilation passes with strict mode enabled
- All ESLint rules pass with no errors (development warnings acceptable)
- Prettier formatting applied consistently
- Svelte component validation passes with no accessibility violations
- Unit test coverage >90% for selection store and node integration
Resources & References
Existing Implementation Patterns
- BaseNode Component:
src/lib/design/components/BaseNode.svelte- Foundation for selection integration - NodeTree Component:
src/lib/components/NodeTree.svelte- Container for selection context - Design System:
src/lib/design/tokens/- Color tokens and theming patterns - Store Patterns: Existing Svelte stores for state management examples
Architecture Documentation
- State Management:
docs/architecture/core/state-management.md- Store implementation patterns - Component Architecture:
docs/architecture/components/design-system.md- Component integration guidelines - Accessibility Standards: WCAG 2.1 guidelines for selection and focus management
UX Research References
- Logseq Analysis: Research findings from multi-node highlighting investigation
- Selection Patterns: Modern web application selection standards (VS Code, GitHub, Figma)
- Accessibility Guidelines: ARIA patterns for tree widgets and selection states
Definition of Done
Functionality
- Single-block selection works reliably in isolation
- Visual highlighting provides immediate, clear feedback
- Selection state management is robust and performant
- Accessibility features are complete and tested
- Integration with existing components is seamless
Code Quality
- All acceptance criteria verified through testing
- Component follows established Svelte and TypeScript patterns
- Performance requirements met through profiling
- No memory leaks or state management issues
- Clean interfaces ready for multi-selection extension
Testing
- Unit tests cover selection store logic comprehensively
- Integration tests verify BaseNode selection behavior
- Accessibility testing confirms WCAG compliance
- Performance testing validates smooth transitions
- Cross-browser compatibility verified (Chrome, Firefox, Safari)
Documentation
- Selection store API documented with usage examples
- Component integration patterns documented
- Accessibility features documented for future reference
- Extension points identified for multi-selection features
- Code comments explain complex selection logic
This foundation will enable seamless extension to range selection, multi-selection, and advanced selection features in subsequent issues.
Metadata
Metadata
Assignees
Labels
No labels
Activity
Update architecture documentation with advanced UI/UX capabilities
Update architecture documentation with advanced UI/UX capabilities
Update architecture documentation with advanced UI/UX capabilities
malibio commentedon Aug 3, 2026
Closing as superseded by #278 (Copy text across node boundaries).
This issue is part of the 2025 four-issue chain (#35 → #36 → #37 → #38) specifying a block-level, id-set selection model — a selection store of node ids, driven by click / shift+click / ctrl+click, intended to enable bulk operations on a set of nodes.
Two reasons for closing:
1. The specs are not implementable as written. They target a file layout that no longer exists:
src/lib/design/components/BaseNode.svelteandsrc/lib/components/NodeTree.svelte(the codebase is kebab-case —base-node.svelte,node-tree.svelte),src/lib/design/tokens/as a directory (it is a single file,lib/design/tokens.ts), Svelte 4 stores with$:reactivity (the codebase is on Svelte 5 runes), an in-repodocs/directory, and LanceDB.2. The actual need is text-level, not block-level. The requirement driving this work is copying a passage of text that spans several nodes — a range that can start and end partway through a node. Block selection copies whole nodes and cannot express that. #278 now covers it, scoped to text-level selection and copy.
Bulk operations on a set of nodes (delete, indent, move) remain unbuilt and untracked. There is currently no UI surface for node operations at all — no context menu (
contextmenuhas zero hits insrc/lib), no toolbar, and deletion exists only as a side effect of Backspace in the editor (base-node-viewer.svelte:809-905). If that becomes wanted, it should be filed fresh against the current architecture rather than revived from these.Worth noting what already exists and is not lost by closing these: arrow-key node-to-node navigation (
lib/commands/keyboard/navigate-up.command.ts:26,navigate-down.command.ts:26), a keyboard command registry (lib/services/keyboard-command-registry.ts:207-210), and single-node focus management (lib/services/focus-manager.svelte.ts).