Skip to content

feat(prompts): add folders and reordering to prompts#2506

Draft
janburzinski wants to merge 2 commits into
mainfrom
emdash/reordering-prompts-0lg1t
Draft

feat(prompts): add folders and reordering to prompts#2506
janburzinski wants to merge 2 commits into
mainfrom
emdash/reordering-prompts-0lg1t

Conversation

@janburzinski

Copy link
Copy Markdown
Collaborator

Description

  • adds folders to prompts
  • adds the ability to reorder prompts

Screenshot/Recording (if applicable)

https://streamable.com/q96hk4

Checklist
  • I kept this PR small and focused
  • I ran a self-review before opening this PR
  • I ran the relevant local checks or explained why not
  • I updated docs when behavior or setup changed
  • I added or updated tests when behavior changed, or explained why not
  • I only added comments where the logic is not obvious
  • I used Conventional Commits for commit
    messages and, when possible, the PR title

@janburzinski

Copy link
Copy Markdown
Collaborator Author

@greptileai

@greptile-apps

greptile-apps Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds folder organization and drag-to-reorder support to the prompt library, touching the main-process service, IPC controller, the library view, the prompt/folder modals, the context popover, and the shared schema.

  • Schema & service: PromptLibraryPrompt gains an optional folderId; a new PromptLibraryFolder type and PromptLibraryState wrapper replace the plain array API. The service now reads/writes prompts and folders as two separate KV rows and applies sanitizePrompts to strip references to unknown folders \u2014 a useful guard, but the two writes are not wrapped in a transaction, creating a data-integrity window.
  • UI \u2014 library view: The view is substantially rewritten with dnd-kit\u2019s DndContext + SortableContext. Cross-section drag previews (draftPrompts), committed reorders (pendingOrder), and spring-open folder expansion during drags are managed with distinct state slices and are well-reasoned.
  • UI \u2014 context popover: Folder items are added as a navigable tier in the action combobox; keyboard Backspace exits a folder, and a keepOpenUntilRef timing guard suppresses the spurious input echo that Base UI emits on item selection.

Confidence Score: 3/5

The feature works correctly in the happy path, but the two-write pattern in updateState can permanently lose folder assignments if the process crashes or if the second KV write silently fails.

The prompt/folder library is the only persisted data path changed here. Both writes go through the same SQLite database, so wrapping them in a db.transaction() would fully close the gap. Until that is done, every folder create/rename/delete/reorder operation risks leaving prompts with orphaned folderId values that are silently stripped on the next boot.

apps/emdash-desktop/src/main/core/prompt-library/service.ts — the updateState method needs both writes in a single transaction before this is safe to ship.

Important Files Changed

Filename Overview
apps/emdash-desktop/src/main/core/prompt-library/service.ts Adds folder read/write and sanitization logic; introduces a non-atomic two-write pattern for prompts+folders that can silently lose folder assignments on crash or write error
apps/emdash-desktop/src/renderer/features/library/prompts/use-prompt-library.ts Migrates hook to PromptLibraryState; adds a reorder helper that pre-writes the cache before mutation, making optimistic rollback a no-op (recovery relies solely on invalidateQueries)
apps/emdash-desktop/src/renderer/features/library/prompts/prompt-library-view.tsx Large refactor adding dnd-kit drag-and-drop reordering and folder sections; drag state management (draftPrompts, pendingOrder, dragState) is well-structured with correct cross-section preview logic
apps/emdash-desktop/src/renderer/features/tasks/conversations/add-context-popover.tsx Adds folder navigation to the context popover with a keepOpenUntilRef timing guard to suppress input echo on folder selection; logic is sound
apps/emdash-desktop/src/shared/prompt-library.ts Adds folderId to prompt schema, new folder schema/type, and PromptLibraryState wrapper type; clean addition with proper Zod validation
apps/emdash-desktop/src/renderer/features/library/prompts/prompt-folder-modal.tsx New modal for creating/renaming folders; duplicate detection correctly handles case-insensitive comparison and excludes the current folder name on rename
apps/emdash-desktop/src/renderer/features/library/prompts/prompt-modal.tsx Adds optional folder select field; NO_FOLDER sentinel correctly prevents UUID collision and the select is only rendered when folders exist
apps/emdash-desktop/src/main/core/prompt-library/controller.ts Straightforward update to use new getState/updateState signatures; no logic changes

Sequence Diagram

sequenceDiagram
    participant UI as PromptLibraryView
    participant Hook as usePromptLibrary
    participant QC as QueryClient
    participant IPC as RPC (IPC)
    participant Svc as PromptLibraryService
    participant KV as KV Store (SQLite)

    Note over UI: User drag-drops prompt
    UI->>Hook: "reorder({ prompts, folders })"
    Hook->>QC: setQueryData(newState) [immediate]
    Hook->>Hook: updateMutation.mutate(newState)
    Hook->>QC: cancelQueries
    Hook->>QC: "getQueryData -> previousState (= newState)"
    Hook->>QC: setQueryData(newState)
    Hook->>IPC: rpc.promptLibrary.update(state)
    IPC->>Svc: updateState(state)
    Svc->>KV: set('prompts', sanitizedPrompts)
    KV-->>Svc: ok
    Svc->>KV: set('folders', folders)
    KV-->>Svc: ok
    Svc-->>IPC: void
    IPC-->>Hook: resolved
    Hook->>QC: "invalidateQueries -> refetch"
    QC->>IPC: rpc.promptLibrary.get()
    IPC->>Svc: getState()
    Svc->>KV: get('prompts') + get('folders')
    KV-->>Svc: data
    Svc->>Svc: sanitizePrompts(prompts, folders)
    Svc-->>IPC: PromptLibraryState
    IPC-->>QC: fresh state
    QC-->>UI: re-render
Loading
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 2
apps/emdash-desktop/src/main/core/prompt-library/service.ts:130-135
**Non-atomic writes can silently lose folder assignments**

`updateState` writes prompts and folders as two independent SQLite `INSERT ... ON CONFLICT UPDATE` statements. `KV.set` also swallows write errors rather than rethrowing, so if the prompts write succeeds but the folders write fails (or the process crashes in between), the persisted state is inconsistent. On the next boot, `getState` reads the updated prompts (containing a `folderId` for the just-created folder) alongside the stale folders list (which doesn't include that folder yet), and `sanitizePrompts` silently strips all those `folderId` values — permanently losing the user's folder assignments.

Since both keys live in the same SQLite database through the Drizzle `db` instance, wrapping the two writes in a single `db.transaction(...)` call would make this atomic at zero cost.

### Issue 2 of 2
apps/emdash-desktop/src/renderer/features/library/prompts/use-prompt-library.ts:42-45
**`reorder` pre-writes the cache, so `onMutate` captures the new state as `previousState`**

When `reorder` is called it immediately runs `queryClient.setQueryData(promptLibraryQueryKey, state)` before kicking off the mutation. When the mutation's `onMutate` then fires, `previousState = queryClient.getQueryData(...)` reads back the *already-updated* value — not the original. If the IPC call subsequently fails, `onError` rolls the cache back to that same new-order snapshot, which is a no-op. The only recovery is the `invalidateQueries` in `onSettled`, which will eventually restore the server state, but during that window the UI shows the (failed) new order as if it succeeded.

The comment acknowledges this trade-off ("Errors are recovered by the mutation's invalidate"), so the intent is clear. Consider capturing the true prior state before the pre-write if a more accurate rollback is ever needed.

Reviews (1): Last reviewed commit: "feat(prompts): add folders and reorderin..." | Re-trigger Greptile

Comment thread apps/emdash-desktop/src/main/core/prompt-library/service.ts
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.

1 participant