Add sidebar thread folders (folders view, scoped archived views, shared components)#258
Open
brsbl wants to merge 47 commits into
Open
Add sidebar thread folders (folders view, scoped archived views, shared components)#258brsbl wants to merge 47 commits into
brsbl wants to merge 47 commits into
Conversation
First session of the Sidebar Nested Folders feature: the client-only
foundation that later sessions build on.
- folderPath.ts: pure parseThreadFolderPath / normalizeThreadTitle /
titleCreatesFolder, plus buildFolderKey. "/" in a thread title is read
as a folder separator; titles are split → trimmed → emptied → re-joined.
- folderPath.test.ts: covers every normalization rule and the
titleCreatesFolder / buildFolderKey boundaries (18 cases).
- sidebarCollapsedAtoms.ts: three persisted prefs mirroring the existing
atom pattern — sidebarGroupByAtom ("none"|"folder", default "none"),
sidebarCollapsedFoldersAtom (string[]), folderOnboardingSeenAtom (bool).
No rendering, tree-building, or rename changes yet (later sessions).
Tests, typecheck, and lint pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds the opt-in "Group by: Folder" sidebar mode: top-level threads fold into nested, collapsible folders derived from "/" in their titles. Pure derived rendering — no DB/API/daemon changes. Assembly (S2): - SidebarFolderGroup item variant + bucketIntoFolders helper, folding the top-level item list into a nested folder tree. Folders render as a block above loose threads; folders, contents, and nested subfolders are each ordered by the active comparator (folders by their representative descendant). One parentKey-aware orderSiblingItems seam so S5 manual sort can swap ordering per list without re-cutting the tree walk. - buildProjectThreadGroups / buildChronologicalThreadList take folderOptions and early-return today's output untouched under Group by: None. - buildPinnedSidebarState folds pinned roots into folders ordered by comparePinnedRoots (pinned keeps its pinSortKey ordering), exposing rootItems while keeping rootNodes. Rendering (S3): - SidebarFolderRow collapsible header (icon, leaf name, descendant count, rolled-up activity) mirroring the parent/worktree row chrome. - ProjectRow renders folder items + recurses; ThreadRow gains displayTitle/accessibleTitle so a folder member shows its leaf while keeping the full "Work › Q3 › Planning" path for a11y + tooltip. - Collapse state in sidebarCollapsedFoldersAtom (read where rendered); selected thread's folder ancestors auto-expand. - SidebarViewOptionsMenu gains a Group by (None / Folder) section; the existing organization "Group by" is relabeled "Organize by" to avoid the label collision. - PinnedThreadTree renders folders statically (drag-reorder and derived folders don't compose), keeping the sortable flat list when no folders. Tests: bucketIntoFolders nesting, folders-first + representative-descendant ordering under both comparators, child-stays-under-parent, env-group-in- folder, pinned folder ordering, and the Group by: None regression (deep- equal to the pre-change builder + folder branch never entered). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Sort options use a single state-driven arrow (none / down=desc / up=asc) instead of a check plus dual direction buttons; re-selecting the active field flips its direction. - Organize trigger uses the Layers icon; Sort trigger uses up/down arrows. - Align the menu section labels to the standard DropdownMenuLabel hierarchy used by the rest of the app's dropdowns. - Make the View options menu story interactive and add TooltipProvider to the Ladle harness so tooltip-using stories render instead of crashing blank. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Resolve thread-create-helpers conflict: keep main's request-carried titleFallback and the branch's folderPath. Add folderPath: null to the ThreadActionsMenu test fixture for the now-required field. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reads "Open project settings to fix". Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Folder rows now expose a dedicated New thread (+) button alongside a "..." menu (Rename, Remove), mirroring the project row's action cluster instead of stuffing New thread into the menu. - Size the sidebar/project/thread action and view-options dropdowns to their content (drop fixed w-44/w-52/w-56; the shared min-w-[8rem] floor remains) so menus wrap their contents. - Clean up the project path-missing affordance: tooltip reads "Open project settings" and the icon's accessible name is "Project path not found". Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The icon-only New thread action read inconsistently next to the labeled Automations button below it. Bring back the "New thread" label (Search stays an icon button alongside). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Folder rows now have a "View archived threads" action, opening a folder-scoped archived list — the analog of a project's archived view. - Add an optional folderPath filter to the thread list query end to end: server-contract schema -> list route -> db listThreads filter. - useArchivedThreads accepts folderPath (keyed separately in the cache); the archived view reads it from a `?folder=` param and labels the folder. - getFolderArchivedRoutePath builds the projectless archived route scoped by folder (folders live in the personal section). - Thread the onViewArchivedThreadsInFolder callback through the folder row chain; the folder "..." menu shows View archived threads / Rename / Remove. - Cover the db folderPath filter with a regression test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The sidebar's TooltipProvider opened tooltips instantly (delayDuration 0), which felt flickery on hover. Use 300ms like MessageActionBar so sidebar icon tooltips open/close with the same cadence. Mirror it in the Ladle harness for story fidelity. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The personal "Threads" archived list (no folder selected) was showing threads that live in a folder. Add an `unfiled` thread-list filter (folderPath IS NULL) end to end and use it for the personal archived view, so foldered archived threads appear only in their folder's archived list. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The folder archived page diverged from the project/personal pages with a
bespoke in-body heading. Make all archived scopes render the one shared
view and carry their scope in the AppLayout breadcrumb instead:
- Rename ProjectArchivedThreadsView -> ArchivedThreadsView (it already
serves project, personal/loose, and folder scopes) and drop the bespoke
folder heading + folder-specific empty state so the body is identical.
- Add a folder segment to the projectless archived breadcrumb
("Threads > <folder> > Archived") and document title, mirroring how the
project name scopes the project archived page.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Smoke testing surfaced that the title sort inverted the wrong way: its base comparator ascends (A→Z), but getSidebarThreadComparator applied the same "asc inverts" rule used for the descending time sorts. So leaf threads sorted opposite to the folder/item comparator (folders Z→A while threads A→Z) and the ↑/↓ arrows read backwards for alphabetical. - Special-case the ascending title base: asc keeps it (A→Z), desc inverts it (Z→A), for both the leaf and mixed folder/thread comparators. - Default a newly selected Alphabetical sort to asc (A→Z); time sorts still default to desc (newest first). - Export getSidebarThreadComparator and add direction/consistency tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Dragging a thread onto a folder now reads as a real drop: - onDragOver resolves the target folder (shared with onDragEnd so they agree), expands it if collapsed, and records a drag preview. - The hovered folder renders an optimistic, non-interactive row for the dragged thread inside it (forcing the children area open) so there's a visible target to drop onto before the move commits. - Preview/auto-expand apply only to real folders (not the loose root) and clear on drop/cancel; state updates are guarded so drag-over doesn't thrash renders. useSidebarReorderDnd now forwards onDragStart/onDragOver/onDragCancel. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
When a project's local path is missing, the row now renders just the red warning icon (flush right) instead of also showing the actions menu and new thread button — the warning is the single relevant action (open settings to fix). Normal rows keep the "..." + "+" actions. Right-click context menu is unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Threads header in the folders view was missing the Organize/Sort actions,
and each view defined its own near-duplicate action clusters. Extract shared
components so the headers can't drift:
- SidebarDisplayOptionsActions: the Organize + Sort menu pair, used by the
Projects, Folders, and Threads headers.
- SidebarThreadsSectionActions: the full Threads-header cluster (archived menu
+ display options + new thread), used by the Threads header in BOTH project
mode and the folders view — so it now has Organize/Sort there too.
Rebalanced the two display-menu open states so each mode's two sections own one
("primary": Projects/Folders, plus Threads) and stay independent.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ProjectThreadTree, ChronologicalThreadTree, and the Folders view each had their own near-identical copy of "group + sortable list + map item rows" plus the loading skeleton, so every row-prop change had to be repeated per view. - Add ManualThreadTreeItems: the single place that maps thread-tree items to rows. Variants via props — fixed vs per-item projectId, and an optional sortableParentKey (wrap in a SortableContext, or let an outer one provide it for the split Folders/Threads view). - Add ThreadTreeLoadingSkeleton for the repeated loading state. - Route all three renderers through them. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Expanding the hovered folder and inserting the drop-preview row mid-drag shifted
layout under the in-flow dragged item, so dragging a thread up out of its folder
got shoved back down ("stuck"). Defer both behind a short hover dwell so passing
through a folder doesn't mutate layout; the expand + preview only fire once the
pointer settles over a target. Drop still works immediately regardless.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…sted-folders-thr_js7dkc3iwv
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds thread folders to the sidebar — group personal threads into folders via a dedicated Folders organization mode — and reworks the sidebar's view‑options and archived surfaces to use shared components with variants, so the project view and folders view can't drift. Built on the otto‑s1 spec, then refined across QA.
Folders
…menu with View archived threads / Rename / Remove.Organize & Sort menus
Archived views
ArchivedThreadsViewfor every scope (project / personal‑loose / folder); the scope shows in theAppLayoutbreadcrumb (Threads › <folder> › Archived), not a bespoke in‑body heading./archived?folder=…). The personal/loose list now excludes foldered threads via a newunfiledfilter. Menu items renamed to "View archived threads".Shared components (the no‑duplication goal)
ManualThreadTreeItems— the single place that maps tree items to rows for every view (project, chronological, folders), plusThreadTreeLoadingSkeleton.SidebarDisplayOptionsActions+SidebarThreadsSectionActions— shared header action clusters used by both modes.ArchivedThreadsView+ breadcrumb variants for the archived pages.Other polish
…/+).Server / data
folderPathandunfiledfilters on the thread‑list query, threaded contract → route → db, with db regression tests.Testing
typecheck(30/30),lint, andprettier --checkclean.sortComparator(sort direction + leaf/folder consistency) and dbfolderPath/unfiledfilter tests.Notes
🤖 Generated with Claude Code