Skip to content

feat(core): multi-level folder nesting in the slide organizer#199

Open
D4n1984 wants to merge 1 commit into
1weiho:mainfrom
D4n1984:feat/folder-nesting
Open

feat(core): multi-level folder nesting in the slide organizer#199
D4n1984 wants to merge 1 commit into
1weiho:mainfrom
D4n1984:feat/folder-nesting

Conversation

@D4n1984

@D4n1984 D4n1984 commented Jun 4, 2026

Copy link
Copy Markdown

Closes #227

Summary

Adds multi-level folder nesting to the slide organizer. Folders can now have an optional parentId, so they nest arbitrarily deep, and the home/sidebar surfaces the tree in a way that scales as the folder count grows.

Motivation: with a flat folder list, organizing many decks gets noisy fast. Nesting (e.g. Parent › Subfolder › Workshop) keeps things tidy, and parent folders give an aggregated view of everything underneath.

What's included

  • Nested foldersFolder gains an optional parentId. The sidebar renders the folder tree with expand/collapse chevrons (state persisted in localStorage), indented by depth. Folder counts are recursive (own slides + all descendants').
  • Aggregated listing — selecting a parent folder lists its own decks first, then each descendant subfolder's decks under a labelled, indented section. Search/sort act on the whole set (flattened while searching).
  • "All slides" view — a new built-in bucket that lists every slide regardless of folder, next to the existing Draft (unassigned) bucket.
  • Nested move pickers — the folder "Move under" menu uses nested submenus; the slide "Move to folder" dialog uses a collapsible tree (starts collapsed), so relocating stays manageable.
  • Server routesPOST/PATCH /__folders accept parentId (validated against self-parenting and cycles); DELETE re-parents a folder's children onto its parent instead of orphaning them.
  • Docs — the create-slide skill documents the folder model so authoring agents assign decks to the correct leaf folder.
  • Adds a home.allSlides locale key across en, ja, zh-CN, zh-TW.

Backward compatibility

Fully backward compatible: parentId is optional; existing .folders.json manifests with no parentId behave exactly as before (all folders top-level). No migration needed.

Testing

  • pnpm --filter @open-slide/core typecheck
  • pnpm --filter @open-slide/core build
  • biome check clean on the changed files ✅
  • A changeset is included (minor).

Note: I couldn't attach screenshots from my environment — happy to add a short clip/screenshots of the nested sidebar, aggregated listing and move pickers if useful.

Notes

Open to adjusting naming/labels (e.g. localizing the "Move into …" / "All slides" strings differently) or scoping the PR down (e.g. splitting the "All slides" bucket into a separate PR) if you'd prefer smaller changes.

Summary by CodeRabbit

  • New Features

    • Nested folders with expandable/collapsible tree in the sidebar
    • New "All slides" view showing every slide across folders
  • Improvements

    • Folder move UX: nested "Move under" menus, tree-based move dialog, and drag/drop to folders
    • Counts include descendant slides; expansion state persists; deleted folders reparent children
  • Bug Fixes

    • Validation prevents invalid moves (self/cycle)
  • Documentation

    • Updated create-slide guide with folder placement and assignment reporting

@vercel

vercel Bot commented Jun 4, 2026

Copy link
Copy Markdown

@D4n1984 is attempting to deploy a commit to the Yiwei Ho Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jun 4, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds multi-level nested folders (optional parentId), validates and preserves hierarchy in routes (create/patch/delete), updates SDK/hooks, refactors the sidebar into a persistent expandable tree with drag/move/reorder/drop support and an "All slides" view, and updates Home, MoveDialog, locales, docs, and changeset.

Changes

Nested folder hierarchy and tree UI

Layer / File(s) Summary
Folder schema and validation
packages/core/src/files/folders.ts, packages/core/src/app/lib/sdk.ts
Folder gains optional parentId; validateParentId normalizes top-level to null, prevents self-parenting, verifies parent existence, and detects ancestor cycles.
Backend folder routes
packages/core/src/vite/routes/folders.ts
POST /__folders validates/includes parentId; PATCH /__folders/:id validates/updates parentId; DELETE /__folders/:id re-parents direct children onto the deleted folder's parent before removal.
Frontend folders API and hook
packages/core/src/app/lib/folders.ts
postFolder/patchFolder accept optional parentId; useFolders.update signature expanded to accept parentId and forwards it to patch/create flows.
Sidebar tree infrastructure
packages/core/src/app/components/sidebar/sidebar.tsx
Refactors Sidebar to a recursive tree: exports ALL_ID, persists expanded-folder set to localStorage, builds childrenByParent, implements renderTree(parentId, depth) with drag-reorder logic, and renders ALL special row wired to allCount.
Sidebar folder item component
packages/core/src/app/components/sidebar/folder-item.tsx
FolderItem supports new row.kind values, depth/expanded props, depth-based styling, and adds move-target helpers (isDescendant, buildChildrenByParent, MoveTargetItems) to render a nested "Move under" submenu.
Home shell sidebar wiring
packages/core/src/app/routes/home-shell.tsx
Wires ALL_ID selection, computes descendant-aware slide counts via children traversal with cycle protection, passes allCount and onMove to Sidebar, and adds mobile "All slides" pill.
Home route views and Move dialog
packages/core/src/app/routes/home.tsx
Refactors Home to support ALL/DRAFT/folder selection, adds filterSlides/sortSlides, builds multi-section descendant-aware views, updates search/count rendering, and converts MoveDialog/FolderOption to an expandable recursive folder tree with chevron controls.
Localization for all slides
packages/core/src/locale/types.ts, packages/core/src/locale/*.ts
Adds home.allSlides and folder/move UI keys to Locale['home'] and provides translations for English, Japanese, Simplified Chinese, and Traditional Chinese.
Documentation and changeset
.changeset/nested-folders.md, packages/core/skills/create-slide/SKILL.md
Adds a changeset marking a minor release and updates the create-slide skill docs with folder JSON schema, parentId rules, and leaf-folder assignment guidance.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant MoveDialog
  participant Sidebar
  participant Server

  User->>MoveDialog: open move dialog
  MoveDialog->>Sidebar: build childrenByParent (manifest.folders)
  Sidebar-->>MoveDialog: nested folder tree (depth/expanded)
  User->>MoveDialog: choose parent target
  MoveDialog->>Server: onMove(folderId, parentId) -> update folder parentId
  Server-->>Sidebar: updated manifest/folders
  Sidebar-->>User: UI reflects new parent (reorder/re-render)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • 1weiho/open-slide#114: Prior changes to sidebar special rows and assets handling that overlap with extended Row kinds.
  • 1weiho/open-slide#165: Earlier sidebar drag/reorder work that this PR extends and reworks.
  • 1weiho/open-slide#125: Related Home view ordering/rendering changes that intersect with this PR's home sorting/refactor.

Suggested reviewers

  • 1weiho

Poem

🐇 In burrows deep the folders grow,

Branches nest where slide-rows flow.
"All slides" gleam for every view,
Moves hop safe — no loops accrue.
A rabbit nods: the tree is new.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.70% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the primary feature added in this pull request: multi-level folder nesting in the slide organizer. It is specific, concise, and clearly reflects the main change across all modified files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.changeset/nested-folders.md:
- Around line 5-12: Keep only a single present-tense, user-facing summary line
and remove the multi-line bullets: replace the entire file content with the
one-line summary "Add multi-level folder nesting to the slide organizer."
(remove the subsequent bullet points and explanation lines so the changeset is a
single present-tense line describing the user-visible change).

In `@packages/core/src/app/components/sidebar/folder-item.tsx`:
- Around line 154-156: The new nested-folder UI renders hardcoded English labels
("Move under", "Move into {name}", "(Top level)", expand/collapse labels) inside
DropdownMenuItem and related elements in the FolderItem component (e.g.,
DropdownMenuItem, FolderInput usage) which bypass useLocale; replace these
literal strings with locale lookups via useLocale (or the project's i18n helper)
and add corresponding locale keys (e.g., folder.moveUnder, folder.moveInto,
folder.topLevel, folder.expand/ collapse) so the dropdown text and
ARIA/screen-reader labels are localized; update all occurrences referenced in
FolderItem (lines around the DropdownMenuItem usages and the other locations you
noted at ~287-289 and ~379-389) to use the same locale keys and string
interpolation for the folder name.

In `@packages/core/src/app/routes/home.tsx`:
- Around line 243-245: The empty-state branch currently treats the "All slides"
bucket like a folder which yields undefined folderName and broken copy; update
the conditional around allSlides.length === 0 to check explicitly for the ALL_ID
selection (e.g. selectedFolder?.id === ALL_ID or selectedFolderId === ALL_ID)
and render a dedicated all-slides empty state (either by passing an explicit
flag like isAll or a folderName="All slides" to EmptyState) instead of the
folder variant; apply the same change to the identical branch around lines
407-412 so the all-slides path is handled separately from folder empty-state
rendering.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 542ac59b-aff1-4ac7-9dd7-653ba505362c

📥 Commits

Reviewing files that changed from the base of the PR and between 9357e8a and 93bde44.

📒 Files selected for processing (15)
  • .changeset/nested-folders.md
  • packages/core/skills/create-slide/SKILL.md
  • packages/core/src/app/components/sidebar/folder-item.tsx
  • packages/core/src/app/components/sidebar/sidebar.tsx
  • packages/core/src/app/lib/folders.ts
  • packages/core/src/app/lib/sdk.ts
  • packages/core/src/app/routes/home-shell.tsx
  • packages/core/src/app/routes/home.tsx
  • packages/core/src/files/folders.ts
  • packages/core/src/locale/en.ts
  • packages/core/src/locale/ja.ts
  • packages/core/src/locale/types.ts
  • packages/core/src/locale/zh-cn.ts
  • packages/core/src/locale/zh-tw.ts
  • packages/core/src/vite/routes/folders.ts

Comment thread .changeset/nested-folders.md Outdated
Comment thread packages/core/src/app/components/sidebar/folder-item.tsx Outdated
Comment thread packages/core/src/app/routes/home.tsx
@D4n1984 D4n1984 force-pushed the feat/folder-nesting branch from 72133d2 to d218a9c Compare June 10, 2026 14:02

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/core/skills/create-slide/SKILL.md`:
- Line 110: Update the prose nesting example to match the JSON example shown
earlier: replace the incorrect "Spain › Madrid › Test" and its parentId mapping
(e.g., Test.parentId → Madrid, Madrid.parentId → Spain) with the hierarchy from
the JSON "Testing › Spain › Portugal" and the correct parentId mappings
(Portugal.parentId → Spain, Spain.parentId → Testing); ensure references to
names like Test and Madrid are removed or replaced so the textual example aligns
with the JSON example near the JSON block.
- Around line 122-124: The example sentence referencing folder browsing contains
a duplicated folder name ("Spain" twice); update the text so it matches the JSON
hierarchy used elsewhere (e.g., reference "Testing", "Spain", and "Portugal" or
use the breadcrumb "Testing › Spain › Portugal")—locate the sentence that
currently reads "Test, Spain, and Spain" in SKILL.md near the folder example and
replace the duplicate with the correct third folder name to reflect the JSON
hierarchy.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6c9398a9-91e5-4557-992e-f20a6dea9783

📥 Commits

Reviewing files that changed from the base of the PR and between 72133d2 and d218a9c.

📒 Files selected for processing (15)
  • .changeset/nested-folders.md
  • packages/core/skills/create-slide/SKILL.md
  • packages/core/src/app/components/sidebar/folder-item.tsx
  • packages/core/src/app/components/sidebar/sidebar.tsx
  • packages/core/src/app/lib/folders.ts
  • packages/core/src/app/lib/sdk.ts
  • packages/core/src/app/routes/home-shell.tsx
  • packages/core/src/app/routes/home.tsx
  • packages/core/src/files/folders.ts
  • packages/core/src/locale/en.ts
  • packages/core/src/locale/ja.ts
  • packages/core/src/locale/types.ts
  • packages/core/src/locale/zh-cn.ts
  • packages/core/src/locale/zh-tw.ts
  • packages/core/src/vite/routes/folders.ts
✅ Files skipped from review due to trivial changes (3)
  • .changeset/nested-folders.md
  • packages/core/src/locale/zh-cn.ts
  • packages/core/src/locale/en.ts
🚧 Files skipped from review as they are similar to previous changes (11)
  • packages/core/src/locale/types.ts
  • packages/core/src/locale/ja.ts
  • packages/core/src/locale/zh-tw.ts
  • packages/core/src/app/routes/home-shell.tsx
  • packages/core/src/files/folders.ts
  • packages/core/src/app/lib/folders.ts
  • packages/core/src/app/routes/home.tsx
  • packages/core/src/app/lib/sdk.ts
  • packages/core/src/vite/routes/folders.ts
  • packages/core/src/app/components/sidebar/sidebar.tsx
  • packages/core/src/app/components/sidebar/folder-item.tsx

Comment thread packages/core/skills/create-slide/SKILL.md Outdated
Comment thread packages/core/skills/create-slide/SKILL.md Outdated
Folders can now nest arbitrarily deep via an optional `parentId`:

- Sidebar renders the folder tree with expand/collapse; folder counts are
  recursive (own slides + all descendants').
- Selecting a parent folder lists its own decks first, then each descendant
  subfolder's decks under a labelled section.
- New built-in "All slides" view listing every slide regardless of folder,
  next to the existing Draft (unassigned) bucket.
- The folder "Move under" menu uses nested submenus; the slide "Move to
  folder" dialog uses a collapsible tree, so relocation scales with the
  folder count.
- Folder create/patch accept `parentId` (validated against cycles and
  self-parenting); deleting a folder re-parents its children onto the
  deleted folder's parent.
- The create-slide skill documents the folder model so authoring agents
  place decks in the correct leaf folder.

All new UI strings (move-tree labels, "All slides") are localized across
en, ja, zh-CN and zh-TW, and the "All slides" bucket has its own empty state.
@D4n1984 D4n1984 force-pushed the feat/folder-nesting branch from d218a9c to 697733d Compare June 10, 2026 14:26
@D4n1984

D4n1984 commented Jun 10, 2026

Copy link
Copy Markdown
Author

Screenshots

Multi-level folders — nested sidebar + aggregated listing. Selecting a parent
folder lists its own decks first, then each descendant subfolder's decks under a
labelled section; sidebar/header counts are recursive.

1-nested-folders-aggregated

"All slides" view — every slide across folders, next to the Draft (unassigned) bucket.

2-all-slides

"Move to folder" dialog — collapsible folder tree for relocating a deck.

3-move-to-folder-tree

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Multi-level (nested) folders in the slide organizer

1 participant