Skip to content

Project-first dashboard: New-project creation + Projects menu + chrome cleanup (amicode#203) - #46

Merged
kateebonner merged 18 commits into
local/amicodefrom
kate/projects-menu-203
Jul 23, 2026
Merged

kateebonner merged 18 commits into
local/amicodefrom
kate/projects-menu-203

Conversation

@kateebonner

Copy link
Copy Markdown
Contributor

Slice 1 of harmoniqs/amicode#202 (chrome/fork). Independent of slice 2 (harmoniqs/amicode#205, extension sidebar).

What changed

  • New-project creation (the headline): the dashboard add-project affordance opens a New-project dialog (name → slug preview) that creates a real directory via a new POST /amicode/project server route (mkdir + best-effort git init), registers it, and opens a first session. Inline recoverable errors (collision / unwritable); Open existing folder instead… falls back to the picker; git-absent still creates with a non-blocking toast.
  • Projects menu: the chrome 'Sessions' entry is now Projects (project-count pill). Its flyout shows the selected project's recent sessions; picking another project swaps the list, so non-selected projects' sessions stay hidden until selected. Orphan sessions (no registered project) surface under Other folders — reachable, never dropped (was a silent return []).
  • Connections popover: drops the Vaults tab (the native Armonia sidebar owns vaults now); name unchanged, per glossary — 'Devices' rename was rejected as a term collision.
  • Defaults capsule: copy 'Session defaults' → 'Defaults — model & solver, apply everywhere' (copy only).
  • Removed the now-dead time-grouping chain.

Verification: 21 new unit tests (10 app pure-logic: slug / create-error classify / orphan-safe grouping; 11 server route incl. a real-temp-dir end-to-end that git-inits) · app 425/425 · server route green · tsgo -b clean · oxlint 0 errors.

Note on AC6: nesting is v1 — the selected project's sessions render in a section below the switcher (selection-driven), not literally indented inside each row. Flagged for review; the in-row indent is a fast-follow if wanted.

Part of harmoniqs/amicode#203 / #202.

🤖 Generated with Claude Code

kateebonner and others added 18 commits July 22, 2026 18:57
…pover (amicode#203)

AC8: the home-chrome global popover (the 'Connections' entry) no longer shows
a Vaults tab — vaults live in the native Armonia sidebar now. Parameterized
the shared status tabs with includeVaults (default true), so the session-view
popover keeps its vaults tab; the global mount passes false. GLOBAL_STATUS_TABS
-> ['connections']; model test updated.
AC9: defaults capsule copy 'Session defaults' -> 'Defaults — model & solver,
apply everywhere' (copy only; no stored state touched).

Part of harmoniqs/amicode#203.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…phan-aware session grouping (amicode#203)

Foundation for the Projects menu + New-project flow (home-projects.ts): slug,
runtime create-error classification (collision/unwritable/other), best-effort
git-init notice, and groupSessionsByProject — which nests sessions under their
project and groups the rest as orphans by directory, replacing the old
drop-on-no-project behavior (AC7). 10 unit tests.

Part of harmoniqs/amicode#203.
…micode#203)

New-project creation as an amicode server route (same fs-mutation idiom as
vaults.ts): {name, parentDir} -> slugify -> validate absolute parent -> mkdir
-> best-effort git init. Never rejects; returns ok:false {collision|unwritable|
empty-name|bad-parent|other} for inline dialog errors, or ok:true {path, slug,
gitInitialized}. Slug is [a-z0-9-] only (no path traversal). git init is
best-effort — absent/failed git still creates the project (AC4). 10 tests
incl. a real-temp-dir end-to-end. Registered on authOnlyRouterLayer.

Part of harmoniqs/amicode#203.
…e#203)

The prominent add-project affordance now opens a New-project dialog (name ->
slug preview) that creates a real directory via POST /amicode/project, then
registers it and opens a first session. Inline recoverable errors for
collision/unwritable; 'Open existing folder instead…' falls back to the
directory picker; a git-absent create still opens the project with a
non-blocking toast (AC4). Uses the shared amicodePost helper.

Satisfies AC1-5 of harmoniqs/amicode#203 (creation flow).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…203 AC6 label, AC7)

- The chrome 'Sessions' entry is now 'Projects'; the pill shows the project
  count (or the unseen-session badge when there are unread sessions).
- buildHomeSessionRecords no longer drops sessions whose directory matches no
  registered project — they surface as orphans labeled by directory basename,
  so ghost sessions stay reachable (was a silent 'return []').

AC7 (reachability) done; AC6 label done. Per-project session *nesting* (AC6's
nesting clause) remains — the design-sensitive flyout rework.

Part of harmoniqs/amicode#203.
…ers hidden (amicode#203 AC6)

Replaces the time-grouped 'recent across all projects' list with a
selection-driven view: the SELECTED project's recent sessions show; picking a
different project in the switcher swaps the list, so non-selected projects'
sessions stay collapsed/hidden until selected (Kate's direction). Orphan
sessions (no registered project) surface under 'Other folders' — reachable,
never dropped. Removed the now-dead time-grouping chain (groups/groupSessions/
totalSessions/sessionsExpanded/HomeSessionGroup).

v1 of the nesting: sessions render in a section below the switcher, filtered to
the selection. If the literal in-row indent is wanted, that's a follow-up.

AC6 (nesting) + AC7 (orphans) of harmoniqs/amicode#203.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…witcher below (amicode#203, option b)

Kate's real-use feedback: nesting sessions under the selected project made
'resume what I was just doing' worse. Option (b): the flyout now leads with
Recent — recent sessions across ALL projects, each labeled with its project/
folder (orphans included, never dropped) — with the project switcher (switch/
create) below a divider. Restores resume-primacy; the orphan concept dissolves
into 'a recent session in a folder'. Removed the selected-project-only memos.

Part of harmoniqs/amicode#203.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…amicode#203)

Problem 1: the amicode extension runs the opencode server in an internal
scaffold dir (holds the injected amico config), which opencode registers as a
project — surfacing as a phantom 'opencode' project and the default new-session
target. The extension now passes that dir as the amicode_hide_project boot
param; entry.tsx adopts it (and strips it from the URL like auth_token), and
the dashboard projects() filters it out. Standalone opencode never sets the
param, so its real cwd project is untouched. Unit-tested.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…203)

Kate: the project-count pill is noise now that the flyout leads with Recent.
The pill renders only when there are unseen sessions (unread badge, accent);
otherwise the entry is just 'Projects' + chevron.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ession lists (amicode#203)

Two fixes from live testing:
1. Regression — hiding the scaffold from projects() also stopped loading its
   sessions (they default there), so Recent went empty. Now projects() stays
   full for records()/loading; a separate visibleProjects() hides the scaffold
   from the SWITCHER only; scaffold sessions show in Recent unlabeled.
2. Kate's model — one flat 'all sessions' list (most recent) on top, plus each
   project with its own dedicated session list: the selected project expands to
   its sessions nested under its row (others collapsed). Threaded sessionsFor/
   onOpenSession/activeServerKey through HomeProjectColumn -> HomeProjectList.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e chat composer (amicode#203)

Kate: the entry counter and the composer's project-name label are noise.
- Dashboard 'Projects' entry: drop the pill entirely (label + chevron only);
  removed the now-dead unseenTotal memo.
- Chat composer project switcher: empty the trigger label so no project name
  shows — the folder icon + chevron keep it reachable. ComposerPickerTrigger
  now hides an empty label span (clean icon+chevron for any picker).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…selected one (amicode#203)

Root cause of the inconsistent empty state + missing sessions: projectDirectories()
scoped session loading to the SELECTED project, so records() — the flat 'all
sessions' list — only ever held the selected project's sessions. Selecting an
empty project showed 'No sessions found'; selecting a populated one showed
'Recent sessions'; non-selected projects' sessions never loaded at all, so the
per-project nested lists had no data.

Now projectDirectories() always loads every project's sessions. The flat list is
truly all-sessions (consistent regardless of selection), and per-project nesting
filters the full allRecords() by worktree so each project's sessions populate.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…d global id (amicode#203)

DB investigation: opencode registers every amicode directory under ONE project
id ('global'), so projectForSession's id-first resolution collapsed all sessions
onto a single project (whichever won the id-map) — everything clustered under
kate-test, and kate-test-2 got nothing despite having sessions.

Fix: on the dashboard, resolve by directory (empty byID → projectForSession
matches session.directory to project.worktree). Applied in buildHomeSessionRecords
AND openSession (which otherwise opened the wrong project's worktree). Removed the
now-dead projectByID memo. Also filter the scaffold project out of the composer
project picker (Kate: 'opencode-project' still showed there).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… icon + blob (amicode#203)

Kate's polish:
- Project rows show a folder glyph instead of the first-letter avatar.
- Session rows (flyout list + search results) drop the leading chevron and the
  open-tab indicator blob. Removed the now-dead HomeSessionLeading/HomeSessionAvatar
  and their exclusive imports.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ode#203)

Kate: project and session rows looked inconsistent and didn't read as a
hierarchy. Now:
- Session rows share the project row's rhythm (h-7, px-1.5, rounded, same
  transitions) instead of the old taller h-10/px-6 block.
- They read as CHILDREN: lighter weight (440, was 530 — heavier than the
  parent), muted-until-hover, plain overlay hover vs the project row's bordered
  select.
- Nested sessions indent (pl-7) so their titles align under the project NAME
  (past the folder icon), and drop the redundant parent-project label. The flat
  'all sessions' list keeps the label (cross-project) and shows it faint.
Removed the now-dead HOME_ROW/HOME_ROW_BASE constants.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ECTS; drop selected-project border + Connections tab label (amicode#203)

Kate's flyout polish:
- Projects section moved to the top of the flyout; search + Recent-sessions
  list below a divider.
- RECENT SESSIONS header now matches the PROJECTS header (uppercase faint label
  + ghost icon-only New-session button, edit glyph) instead of the old text
  button. Removed the dead HomeSessionGroupHeader.
- Selected/hover project rows are background-only (no inset border).
- The global Connections popover drops its now-redundant single 'Connections'
  tab label (vaults live in the sidebar), showing the connection content
  directly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…atches the others (amicode#203)

- chrome-dropdown.ts: tiny shared coordinator — defaults capsule, Projects
  flyout, and the Connections popover each announce on open and close when
  another announces, so only one is ever open.
- The header-less global Connections mount drops the tabbed inset card
  (px-2 pb-2 + p-3 raised box) for a single flat 14px padding, matching the
  capsule popover / Projects flyout. The session-view tabbed mount keeps its
  card styling.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ally (amicode#203)

The coordinator's guard effect runs after EACH signal set, so setting the open
flag before announcing let the guard see openId!==mine while open=true and
instantly re-close the dropdown: first press opened-and-self-closed (announce
still landed), second press found openId already set and stayed open. Wrapped
announce+set in batch() in all three dropdowns (capsule, Projects flyout,
Connections popover).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@kateebonner

Copy link
Copy Markdown
Contributor Author

Live design review with Kate (each round tested in an installed VSIX) — the flyout and creation flow evolved substantially past the original issue spec, all within amicode#203's scope:

Creation (the headline, unchanged): dashboard New-project dialog → POST /amicode/project (mkdir + best-effort git init) → register → first session. Inline collision/unwritable errors; open-existing fallback; git-absent creates with a non-blocking toast.

Flyout structure (evolved through several rounds):

  • PROJECTS on top: folder-icon rows (no letter avatars, no selected-row border), the SELECTED project expands to its own nested session list (indented under the name, no redundant label); others stay collapsed
  • Search + RECENT SESSIONS below: one flat all-projects list, most-recent first, project label faint; header restyled to match PROJECTS (uppercase faint label + ghost icon New-session button)
  • Session rows share the project row's rhythm (h-7/px-1.5) at lighter weight — parent/child hierarchy reads correctly

Correctness fixes found by live testing:

  • Load ALL projects' sessions regardless of selection (was selection-scoped → inconsistent empty states + missing nested sessions)
  • Resolve session→project by DIRECTORY, not projectID (opencode lumps every amicode dir under one global id, collapsing all sessions onto one project) — applied in records + openSession
  • Phantom scaffold project (opencode/opencode-project) hidden from BOTH the dashboard switcher and the composer picker via the amicode_hide_project boot param — while still loading its sessions (they land there by default); standalone opencode unaffected
  • One chrome dropdown at a time (shared coordinator) — with the press-twice race fixed via batch() (announce + open must land atomically)
  • Global Connections popover: tab label removed (single surface now), padding flattened to match the other dropdowns

Removed noise (Kate's calls): Projects counter pill, project name in the chat composer trigger, session-row leading icon/blob, 'Session defaults' mislabel.

Verification: app 427/427 · ui 312/312 · server route 11/11 · oxlint 0 errors · typecheck clean.

🤖 Generated with Claude Code

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