Add me serve command: local web UI for memories#47
Merged
Conversation
9ddba68 to
872ec59
Compare
jgpruitt
approved these changes
Apr 24, 2026
Split expandedPaths into expandedBrowse (default collapsed) and collapsedSearch (default expanded). Applying or clearing a filter no longer perturbs browse-mode expansion — pruning is scoped to the corresponding bucket. A selectIsExpanded helper centralizes the asymmetric membership semantics.
Drop the forceOpen override that pinned every search-matched path open. Rows now read straight from the store: in search context the default is expanded (membership in collapsedSearch flips a path closed), so new matches are still visible without any pre-seeding, and user clicks land in the search bucket where they're kept separate from browse state.
Replace the \u25b8 unicode glyph (small, font-dependent baseline) with a pixel-consistent inline SVG chevron in a 16\u00d716 box. The leaf bullet gets the same flex-centered 16\u00d716 wrapper so it lands in the same column as the caret above and stays centered regardless of line height.
Flex items default to min-width: auto, so a long label forced the count (or trailing edge) to overflow past the button's padding-right. Add min-w-0 flex-1 to the title spans so truncate actually kicks in, and shrink-0 to the count so it never gets squeezed out of its column.
Right-clicking a MemoryRow landed on the outer PathRow handler too,
which overwrote the context-menu target with { kind: 'path' } and
rendered the wrong menu items. Call stopPropagation in both handlers so
the innermost row determines the menu.
A fresh checkout fails with 'Could not resolve ./web-assets.generated.ts' because scripts/build-all.ts calls `bun build --compile` directly, bypassing the CLI package's `build` script (which chains build:web). Run build:web up front so the generated assets exist when every platform compile starts.
web-assets.generated.ts is gitignored and transitively imported by packages/cli/serve. Running scripts/bundle-web-assets.ts with no web dist/ emits a valid empty-map stub, which lets CI's lint/typecheck/tests resolve the module without the multi-second Vite build.
c1d0b71 to
8bce645
Compare
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.
me serve— local web UI for memoriesAdds a new
me servecommand that starts a local web server on127.0.0.1:3000(auto-incrementing if busy) and opens a browser to a React UI for browsing and editing memories in the currently-configured engine. The global--serverflag selects which engine to target; the CLI stores no additional credentials.What the UI does
Layout
Tree
memory.tree(no content, no row cap) — every path is visible without pagination.Search
memory.searchparameter — semantic, fulltext, grep, tree (ltree filter), meta (JSON), temporal (contains / overlaps / within), limit, candidateLimit, weights, orderBy. Meta JSON is validated live; invalid JSON is excluded from the RPC and flagged inline.?q=typescript&selected=<uuid>restore the full view.Viewer & editor
memory.update; invalid YAML shows an inline parse error.beforeunloadcovers tab close.How it's wired
packages/cli/serve/./rpcforwards JSON-RPC bodies byte-for-byte to the configured engine with the stored API key injected server-side;/healthzreturns{ ok: true }; everything else serves the embedded Vite build (hashed assets get immutable cache headers,index.htmlgetsno-cache, SPA fallback handled).packages/webworkspace: React 19, Vite 7, Tailwind v4, TanStack Query for RPC, zustand for UI state (selection, filter, editor-dirty, layout, UI dialogs), zustandpersistmiddleware for cross-reload preferences.build:webinpackages/cliruns the Vite build andscripts/bundle-web-assets.tsbase64-encodes every file intopackages/cli/serve/web-assets.generated.tsso the compiled binary is self-contained. The generated file is gitignored.resolveCredentials(--server)flow.Docs
docs/cli/me-serve.md(command reference, flags, UI overview, security posture, examples).docs/getting-started.md(new "Browse in the web UI" section) andmkdocs.ymlnav.Testing
./bun run checkgreen: 693 unit tests passing. New coverage includes the tree-building logic (browse vs. search path grouping, aggregate/direct counts, synthetic root bucket, leaf sort order), the URL round-trip, frontmatter parse/serialize, the HTTP server (healthz / proxy / SPA fallback / auth injection / upstream-unreachable), and the filter-summary helper. End-to-end smoke:me serve --no-open --port <n>with a dummy API key serves the embedded bundle correctly.Screenshots