Skip to content

feat(desktop): create an empty project and reassign sessions between projects (drag and drop) #5389

Closed

Description

@MoonOld

Problem

Creating a project in the desktop app goes through a native folder picker — the
composer's "Add project", the + next to the Projects header, and Settings →
Projects all end at projects.add(), which registers an existing directory. The
project is then named after that folder, and the name can only be changed
afterwards from the row menu.

Two gaps remain once that is stated plainly:

  1. No explicit "New project" flow. The only entry point is "Add project",
    which reads as "bring an existing folder in" rather than "create a
    container". Users who organize the way ChatGPT Projects do — make the
    container first, file conversations into it later — have to invent a folder
    for it.
  2. No way to move an existing task into a different project (or out to
    "No project" / Ungrouped) from the desktop UI. The session row menu only
    offers Pin / Rename / Archive / Unarchive / Delete.

Direction

A project stays directory-backed: creating one names a directory (picked, or
created inside the picker). We are deliberately not adding directory-less
projects, so this does not depend on the default-working-directory work in
#2322 / #2998 — those are about where a project-less session runs, which is a
separate concern.

One consequence worth stating explicitly: the Host resolves a project target to
that project's preferred directory (HostWorkspaceResolver), so moving a task
into a project also moves its working directory. That is intended — it is what
the CLI /move already does for the folder case (#1101).

Desired outcome

  • A "New project" action that names a directory (and, ideally, the project)
    in one step, instead of an "Add project" affordance that only accepts an
    existing folder.
  • Move an existing task into another project, or out of every project, from
    the session row.
  • Drag and drop as the fast path: drag a session row onto a project in the
    sidebar, or onto the Inbox / Ungrouped bucket to clear its association.
  • A non-drag fallback for accessibility: a "Move to project…" item on the
    session row menu.

Why the move half is cheap on the backend

The runtime already supports changing a session's project without a new
protocol:

  • packages/runtime/src/session-manager.ts — relocateSessionWorkspace(sessionId, { expectedRevision, cwd, projectId }) has a dedicated projectIdChanged
    branch that commits a projectId-only update when cwd is unchanged.
  • The Runtime Host exposes it as the operation session.workspace.relocate,
    which accepts a { kind: 'project', projectId } or a { kind: 'host_path', path } workspace target.

What is missing is purely the desktop surface: SessionRowActions in
packages/ui/src/session-history-list.tsx exposes only
onToggleFlag | onArchive | onUnarchive | onRename, and the existing
moveToProject / moveToInbox copy is wired only to Work Board items
(apps/desktop/src/renderer/work-board-panel.tsx), not to sessions.

Notes

Activity

  1. MoonOld commented on Sep 16, 2026

    @MoonOld
    ContributorAuthor

    I'd like to pick this up.

    Rough plan:

    1. Add a "New project" creation path that does not require a directory (empty, named project), keeping the existing folder-picker flow as the alternative.
    2. Expose a non-drag "Move to project…" action on the session row, backed by the existing session.workspace.relocate projectId-only path (no cwd change).
    3. Layer drag-and-drop onto sidebar projects / the Inbox (Ungrouped) bucket on top of that same action.

    Open question I'd like to settle before writing code: the intended cwd semantics for sessions filed into a directory-less project — this seems tied to #2322 / #2998. I'll start with steps 1–2 and open a PR against main once the direction is clear.

  2. MoonOld commented on Sep 16, 2026

    @MoonOld
    ContributorAuthor

    take

  3. MoonOld commented on Sep 16, 2026

    @MoonOld
    ContributorAuthor

    Updating the direction to close the open question in the original write-up.

    Decision: a project stays directory-backed. Creating one names a directory (picked, or created in the picker). We are not adding directory-less projects, so this no longer depends on #2322 / #2998 — those cover the fallback cwd for sessions with no project, which is a different problem.

    That also means "empty project" is already possible today: it is a folder-backed project with no sessions yet (#1545). The remaining gaps are (a) an explicit "New project" affordance instead of the "Add project"-only entry point, and (b) moving an existing task between projects — which the Host already supports via session.workspace.relocate and which the desktop UI simply does not expose yet.

    One consequence to be explicit about: because the Host resolves a project target to that project's preferred directory, moving a task into a project also moves its working directory (same as the CLI /move). I am treating that as intended.

    I am starting with (b): a "Move to project…" submenu on the session row, wired to session.workspace.relocate with a project target (and a host_path target seeded from the task's current cwd for the "remove from project" case). Drag and drop, and the "New project" dialog, follow.

  4. MoonOld commented on Sep 17, 2026

    @MoonOld
    ContributorAuthor

    Implemented in #5392, which now uses Fixes #5389 so this issue closes when the PR merges.

    The PR covers named directory-backed project creation, the task-row Move to project menu, and session drag/drop onto project rows (including empty projects) and the visible Ungrouped bucket. Moving into a project also changes cwd to its preferred directory; detaching preserves cwd.

    Real desktop validation caught an additional integration bug: the BrowserWindow file-drop guard intercepted session drags before React could receive them. The guard now permits the session MIME only on designated project targets. After restarting the development app, the reporter confirmed manual dragging works.

    The branch is synced with main, the architecture conflicts are resolved, and 75 targeted tests plus typecheck, lint, format, and architecture checks pass. CI/review and merge remain pending; keeping the issue open until merge. The default-directory and Work Board issues referenced above remain outside this PR's scope.

  5. MoonOld commented on Sep 20, 2026

    @MoonOld
    ContributorAuthor

    Visual evidence for the implementation in #5392 (which carries Fixes #5389).

    What shipped

    • New project — creating one names it: a dialog (项目名称 → 选择文件夹) runs before the folder picker, and the project is registered under the typed name. Entry points are the + on the rail's 项目 heading and Settings → Projects; on macOS the picker gets createDirectory, so a new folder can be made in place rather than having to exist beforehand.
    • Move a task between projects — a 移动到项目 item on the session row (keyboard path) and drag and drop onto a project row, including an empty project and the ungrouped bucket.
    • Directory-backed, as decided above — moving into a project also moves the task's cwd to that project's preferred directory; detaching keeps the cwd and the conversation history.

    Screenshots (from the development app, driven over CDP)

    New project dialog, opened from the + on the rail's 项目 heading A session dragged over a project row: the row shows the drop ring Before: the rail with the Projects heading

    Verified in the running app, not only in tests: dragover reaches React with types: ['application/x-maka-session'] and is accepted by the row under the pointer (and only by eligible rows), and an end-to-end move into an empty project resolves ok with the cwd following the project's directory and the rail regrouping live.

    From review (#5392) — three findings, all fixed: the detach now commits the revision it read instead of replaying a stale directory; Runtime Host groups are headings, not drop targets; and drop eligibility now matches the menu (available, unarchived). Move affordances also follow the Session's Host, since the rail holds only the local Host's project list.

    CI is green on the current head; the PR is waiting on a committer's approval.

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

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions