From 792b3009dd1cb0ff3a135f99c66a3d2aab31d56b Mon Sep 17 00:00:00 2001 From: lucaronin Date: Wed, 29 Apr 2026 11:51:22 +0200 Subject: [PATCH] fix: keep saved view reorder UI native --- docs/ABSTRACTIONS.md | 2 +- docs/ARCHITECTURE.md | 2 +- src/App.tsx | 2 +- src/components/Sidebar.test.tsx | 21 ++------- src/components/Sidebar.tsx | 8 +--- src/components/sidebar/SidebarSections.tsx | 18 +------- src/components/sidebar/SidebarViewItem.tsx | 53 +--------------------- ui-design.pen | 14 +----- 8 files changed, 15 insertions(+), 105 deletions(-) diff --git a/docs/ABSTRACTIONS.md b/docs/ABSTRACTIONS.md index a475b263..8ce22df2 100644 --- a/docs/ABSTRACTIONS.md +++ b/docs/ABSTRACTIONS.md @@ -308,7 +308,7 @@ type SidebarSelection = Saved Views live as YAML files under `views/`. Their definition includes user-visible fields (`name`, `icon`, `color`), note-list preferences (`sort`, `listPropertiesDisplay`), filters, and an optional top-level `order` number. The `order` value is stored directly in the YAML document, not in Markdown frontmatter, and lower values render earlier in every saved-View list. Views without an explicit order sort after ordered views by filename for stable fallback behavior. -The renderer uses `viewOrdering` helpers to convert drag or move-button intent into dense order updates before saving each affected view file through `save_view_cmd`. The sidebar exposes both a drag handle and explicit move-up/move-down controls, and the command palette mirrors those move actions for the currently selected saved View. +The renderer uses `viewOrdering` helpers to convert drag or command-palette move intent into dense order updates before saving each affected view file through `save_view_cmd`. The sidebar keeps the saved View row visually unchanged: pointer users drag the row itself, and keyboard users use the command palette actions for the currently selected saved View. ### Neighborhood Mode diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 39f3a1c9..02792103 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -186,7 +186,7 @@ flowchart TD └──────────────────────────────────────────────────────────────┘ ``` -- **Sidebar** (150-400px, resizable): Top-level filters (All Notes, Changes, Pulse), saved Views, collapsible type-based section groups, and a dedicated folder tree. The folder tree shows user-created folders plus default vault folders such as `attachments/` and `views/`; only the dedicated `type/` directory stays hidden because note types already have their own sidebar section. Saved Views persist a top-level YAML `order` field in each view file and use the same ordered-list mental model as Types: pointer users can drag the handle, while keyboard users can use explicit move buttons or command-palette move actions. The folder tree supports inline folder creation and rename, exposes a right-click menu for rename/delete plus filesystem reveal/copy-path actions, and auto-expands ancestor folders when the current selection or rename target is nested. Type sections and folder rows also act as note drop targets: dropping a note on a type updates its `type:` frontmatter, while dropping it on a folder runs the same crash-safe move path as the command palette flow. Each type can have a custom icon, color, sort, and visibility set via its type document in `type/`. +- **Sidebar** (150-400px, resizable): Top-level filters (All Notes, Changes, Pulse), saved Views, collapsible type-based section groups, and a dedicated folder tree. The folder tree shows user-created folders plus default vault folders such as `attachments/` and `views/`; only the dedicated `type/` directory stays hidden because note types already have their own sidebar section. Saved Views persist a top-level YAML `order` field in each view file and use the same ordered-list mental model as Types: pointer users can drag the existing view row, while keyboard users can use command-palette move actions. The folder tree supports inline folder creation and rename, exposes a right-click menu for rename/delete plus filesystem reveal/copy-path actions, and auto-expands ancestor folders when the current selection or rename target is nested. Type sections and folder rows also act as note drop targets: dropping a note on a type updates its `type:` frontmatter, while dropping it on a folder runs the same crash-safe move path as the command palette flow. Each type can have a custom icon, color, sort, and visibility set via its type document in `type/`. - **Note List / Pulse View** (200-500px, resizable): When a section group, filter, or saved view is selected, shows filtered notes with snippets, modified dates, status indicators, and per-context note-list controls. When `selection.kind === 'entity'`, the same pane enters **Neighborhood** mode: the source note is pinned at the top as a normal active row, outgoing relationship groups render first, inverse/backlink groups follow, empty groups stay visible with `0`, and duplicates across groups are allowed when multiple relationships are true. Plain click / `Enter` open the focused note without replacing the current Neighborhood, while Cmd/Ctrl-click and Cmd/Ctrl-`Enter` pivot the pane into the clicked note's Neighborhood. Folder-backed lists also show non-Markdown files: previewable image binaries get an image indicator and open in the editor pane, while unsupported binaries remain muted instead of auto-launching an external app. Saved views reuse the same sort and visible-column controls as the built-in lists, and those changes persist back into the view `.yml` definition (`sort`, `listPropertiesDisplay`). When Pulse filter is active, shows `PulseView` — a chronological git activity feed grouped by day. - **Editor** (flex, fills remaining space): Single note open at a time (no tabs — see ADR-0003). Breadcrumb bar with word count and note-layout toggle, BlockNote rich text editor with wikilink support, Markdown-compatible inline/display math rendering, first-class Mermaid diagram blocks, markdown-safe formatting controls, and schema-backed fenced code block highlighting via `@blocknote/code-block`. Can toggle to diff view (modified files), raw CodeMirror view, or a wide-screen left-aligned note column while preserving the same readable max width. Binary image files render through `FilePreview` as ordinary vault files using Tauri asset URLs, with explicit unsupported/broken fallback states and keyboard focus returning to the note list on `Escape`. Decomposed into `Editor` (orchestrator), `EditorContent`, `FilePreview`, `EditorRightPanel`, `SingleEditorView`, with hooks `useDiffMode`, `useEditorFocus`, and `useEditorSave`, plus the `useRawMode`/`RawEditorView` pair for markdown source editing. Rich BlockNote input and raw CodeMirror input both route typed `->`, `<-`, and `<->` through the shared `src/utils/arrowLigatures.ts` resolver so arrow ligatures stay consistent across mode switches while escaped ASCII sequences remain literal. Navigation history (Cmd+[/]) replaces tabs. - **Inspector / AI Agent** (200-500px or 40px collapsed): Toggles between Inspector (frontmatter, relationships, instances, backlinks, git history) and AI Agent panel (the selected CLI agent with tool execution). The Sparkle icon in the breadcrumb bar toggles between them. Per-note `icon` is a suggested Inspector property and the command palette's "Set Note Icon" action opens that field directly. When viewing a Type note, the Inspector shows an **Instances** section listing all notes of that type (sorted by modified_at desc, capped at 50). diff --git a/src/App.tsx b/src/App.tsx index dee6f8b5..9a994bb6 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1593,7 +1593,7 @@ function App() { {sidebarVisible && ( <>
- +
diff --git a/src/components/Sidebar.test.tsx b/src/components/Sidebar.test.tsx index 2f236757..81ee5a3b 100644 --- a/src/components/Sidebar.test.tsx +++ b/src/components/Sidebar.test.tsx @@ -1384,31 +1384,20 @@ describe('Sidebar', () => { }, ] - it('renders keyboard-accessible move buttons for saved views', () => { - const onMoveView = vi.fn() + it('does not add visible reorder controls to saved view rows', () => { render( {}} views={mockViews} - onMoveView={onMoveView} + onReorderViews={vi.fn()} /> ) - const moveUpButtons = screen.getAllByTitle('Move view up') - const moveDownButtons = screen.getAllByTitle('Move view down') - - expect(moveUpButtons[0]).toBeDisabled() - expect(moveUpButtons[1]).not.toBeDisabled() - expect(moveDownButtons[0]).not.toBeDisabled() - expect(moveDownButtons[1]).toBeDisabled() - - fireEvent.click(moveUpButtons[1]) - expect(onMoveView).toHaveBeenCalledWith('all-topics.yml', 'up') - - fireEvent.click(moveDownButtons[0]) - expect(onMoveView).toHaveBeenCalledWith('active-projects.yml', 'down') + expect(screen.queryByTitle('Move view up')).not.toBeInTheDocument() + expect(screen.queryByTitle('Move view down')).not.toBeInTheDocument() + expect(screen.queryByTitle('Reorder view')).not.toBeInTheDocument() }) it('shows note count chip for each view matching the filter results', () => { diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index 4c03029b..9759625f 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -24,7 +24,6 @@ import { import { useSidebarTypeInteractions } from './sidebar/useSidebarTypeInteractions' import type { AppLocale } from '../lib/i18n' import type { FolderFileActions } from '../hooks/useFileActions' -import type { ViewMoveDirection } from '../utils/viewOrdering' interface SidebarProps { entries: VaultEntry[] @@ -45,7 +44,6 @@ interface SidebarProps { onEditView?: (filename: string) => void onDeleteView?: (filename: string) => void onReorderViews?: (orderedFilenames: string[]) => void - onMoveView?: (filename: string, direction: ViewMoveDirection) => void folders?: FolderNode[] onCreateFolder?: (name: string) => Promise | boolean onRenameFolder?: (folderPath: string, nextName: string) => Promise | boolean @@ -72,7 +70,6 @@ interface SidebarNavigationProps extends Pick< | 'onEditView' | 'onDeleteView' | 'onReorderViews' - | 'onMoveView' | 'folders' | 'onCreateFolder' | 'onRenameFolder' @@ -112,7 +109,6 @@ function SidebarNavigation({ onEditView, onDeleteView, onReorderViews, - onMoveView, folders = [], onCreateFolder, onRenameFolder, @@ -178,7 +174,6 @@ function SidebarNavigation({ onEditView={onEditView} onDeleteView={onDeleteView} onReorderViews={onReorderViews} - onMoveView={onMoveView} sensors={sensors} entries={entries} locale={locale} @@ -243,7 +238,6 @@ export const Sidebar = memo(function Sidebar({ onEditView, onDeleteView, onReorderViews, - onMoveView, folders = [], onCreateFolder, onRenameFolder, @@ -280,7 +274,7 @@ export const Sidebar = memo(function Sidebar({ const reordered = computeReorder(sectionIds, active.id as string, over.id as string) if (reordered) onReorderSections?.(reordered.map((typeName, order) => ({ typeName, order }))) }, [sectionIds, onReorderSections]) - const viewActions = { onCreateView, onEditView, onDeleteView, onReorderViews, onMoveView } + const viewActions = { onCreateView, onEditView, onDeleteView, onReorderViews } const sectionProps: SidebarSectionProps = { entries, diff --git a/src/components/sidebar/SidebarSections.tsx b/src/components/sidebar/SidebarSections.tsx index 1c47e422..605873c6 100644 --- a/src/components/sidebar/SidebarSections.tsx +++ b/src/components/sidebar/SidebarSections.tsx @@ -24,7 +24,6 @@ import { SidebarViewItem } from './SidebarViewItem' import { computeReorder } from './sidebarHooks' import { countByFilter } from '../../utils/noteListHelpers' import { translate, type AppLocale } from '../../lib/i18n' -import { canMoveView, type ViewMoveDirection } from '../../utils/viewOrdering' export { SidebarTopNav } from './SidebarTopNav' export { FavoritesSection } from './FavoritesSection' @@ -51,7 +50,6 @@ export function ViewsSection({ onEditView, onDeleteView, onReorderViews, - onMoveView, sensors, entries, locale = 'en', @@ -65,7 +63,6 @@ export function ViewsSection({ onEditView?: (filename: string) => void onDeleteView?: (filename: string) => void onReorderViews?: (orderedFilenames: string[]) => void - onMoveView?: (filename: string, direction: ViewMoveDirection) => void sensors: ReturnType entries: VaultEntry[] locale?: AppLocale @@ -85,9 +82,6 @@ export function ViewsSection({ onSelect={() => onSelect({ kind: 'view', filename: view.filename })} onEditView={onEditView} onDeleteView={onDeleteView} - onMoveView={onMoveView} - canMoveUp={canMoveView(views, view.filename, 'up')} - canMoveDown={canMoveView(views, view.filename, 'down')} entries={entries} locale={locale} /> @@ -119,12 +113,10 @@ export function ViewsSection({ @@ -140,22 +132,18 @@ export function ViewsSection({ function SortableViewItem({ view, - views, selection, onSelect, onEditView, onDeleteView, - onMoveView, entries, locale, }: { view: ViewFile - views: ViewFile[] selection: SidebarSelection onSelect: (selection: SidebarSelection) => void onEditView?: (filename: string) => void onDeleteView?: (filename: string) => void - onMoveView?: (filename: string, direction: ViewMoveDirection) => void entries: VaultEntry[] locale?: AppLocale }) { @@ -164,6 +152,7 @@ function SortableViewItem({ return (
onSelect({ kind: 'view', filename: view.filename })} onEditView={onEditView} onDeleteView={onDeleteView} - onMoveView={onMoveView} - canMoveUp={canMoveView(views, view.filename, 'up')} - canMoveDown={canMoveView(views, view.filename, 'down')} - dragHandleProps={{ ...attributes, ...listeners }} + dragHandleProps={listeners} entries={entries} locale={locale} /> diff --git a/src/components/sidebar/SidebarViewItem.tsx b/src/components/sidebar/SidebarViewItem.tsx index 3ca79c0c..09e3915a 100644 --- a/src/components/sidebar/SidebarViewItem.tsx +++ b/src/components/sidebar/SidebarViewItem.tsx @@ -2,13 +2,11 @@ import { useMemo, type HTMLAttributes } from 'react' import type { VaultEntry, ViewFile } from '../../types' import { evaluateView } from '../../utils/viewFilters' import { Funnel, PencilSimple, Trash } from '@phosphor-icons/react' -import { ArrowDown, ArrowUp, GripVertical } from 'lucide-react' import { Button } from '@/components/ui/button' import { NoteTitleIcon } from '../NoteTitleIcon' import { SidebarCountPill } from '../SidebarParts' import { SIDEBAR_ITEM_PADDING } from './sidebarStyles' import { translate, type AppLocale } from '../../lib/i18n' -import type { ViewMoveDirection } from '../../utils/viewOrdering' import { ACCENT_COLORS } from '../../utils/typeColors' interface ViewAccent { @@ -22,10 +20,7 @@ interface SidebarViewItemProps { onSelect: () => void onEditView?: (filename: string) => void onDeleteView?: (filename: string) => void - onMoveView?: (filename: string, direction: ViewMoveDirection) => void - canMoveUp?: boolean - canMoveDown?: boolean - dragHandleProps?: HTMLAttributes + dragHandleProps?: HTMLAttributes entries: VaultEntry[] locale?: AppLocale } @@ -88,9 +83,6 @@ export function SidebarViewItem({ onSelect, onEditView, onDeleteView, - onMoveView, - canMoveUp = false, - canMoveDown = false, dragHandleProps, entries, locale = 'en', @@ -105,54 +97,13 @@ export function SidebarViewItem({ className={`flex cursor-pointer select-none items-center gap-2 rounded transition-colors ${isActive ? 'bg-primary/10 text-primary' : 'text-foreground hover:bg-accent'}`} style={getViewRowStyle(showCount, isActive, accent)} onClick={onSelect} + {...dragHandleProps} > - {dragHandleProps && ( - - )} {view.definition.name}
- {onMoveView && ( - <> - - - - )} {onEditView && (