feat(ui): the search palette, entry points and a trail that survives the URL (CG-45)
Search: `/` or ⌘K focuses the box; results arrive grouped by kind with their
glyph, signature and file:line, ↑/↓/Enter walk them, Esc dismisses. A group
appears where its best result did, so flattening the groups reproduces the
ranking the keyboard walks — the panel's flat item list IS that concatenation.
A flow question ("how does X reach Y", "X -> Y") is recognised and searches
both endpoints with a note, rather than offering a row that would land on the
phase-2 Flow view.
Entry points answer "where do I start" on the empty screen and in the resting
palette, all derived from the graph: routes, files that run something at module
level (the engine records a top-level statement as an edge out of the file node,
which is what makes src/bin/codegraph.ts the root of the CLI flow — ranked by
calls x the files they reach, so a registration table calling into itself does
not outrank the CLI), and the most depended-on symbols. Tests are excluded from
both derived lists.
Trail: hops record the direction they were walked (→ into a call, ← up to a
caller), clicking one truncates back to it, Clear keeps the place instead of
throwing it away, and the whole walk travels in the URL. A shared or reloaded
trail arrives as ids, so hops learn their names back through a new batch
endpoint and a session name cache — without it, walking back across a
truncation redrew earlier hops as raw hashes. "Read as flow" stays hidden until
there is a Flow view to send it to.
New endpoints: /api/entrypoints and /api/nodes. New engine reads:
getTopCallingFiles, getFileDependentCounts.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
e9596af1cf
commit
87afc50e76
@@ -3,6 +3,14 @@
|
||||
import { trail, hopLabel, encodeTrail } from '../lib/trail.svelte';
|
||||
import { navigate, symbolHref, flowHref } from '../lib/router.svelte';
|
||||
|
||||
/**
|
||||
* "Read as flow" replays the trail as a computed path in the Flow view,
|
||||
* which is phase 2 (CG-50). The control is built and wired; it stays hidden
|
||||
* until there is a view to send it to, because a button that lands on a
|
||||
* placeholder is worse than no button.
|
||||
*/
|
||||
const READ_AS_FLOW = false;
|
||||
|
||||
let hops = $derived(trail.hops);
|
||||
|
||||
function step(index: number) {
|
||||
@@ -17,9 +25,23 @@
|
||||
navigate(flowHref(encodeTrail(hops)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the path, keep the place.
|
||||
*
|
||||
* Emptying the trail while you are reading a symbol would also throw the
|
||||
* symbol away, which is not what "Clear" says. It restarts the trail at
|
||||
* where you are — one `start` hop — and only leaves for the empty screen
|
||||
* when there is nowhere to stay.
|
||||
*/
|
||||
function clear() {
|
||||
const here = trail.current;
|
||||
trail.clear();
|
||||
navigate('#/');
|
||||
if (!here) {
|
||||
navigate('#/');
|
||||
return;
|
||||
}
|
||||
trail.push({ id: here.id, name: here.name, kind: here.kind, dir: 'start' });
|
||||
navigate(symbolHref(here.id, { trail: encodeTrail(trail.hops) }), { replace: true });
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -27,7 +49,10 @@
|
||||
<span class="label">Trail</span>
|
||||
|
||||
{#if hops.length === 0}
|
||||
<span class="empty">Follow a call and the path you walked shows up here.</span>
|
||||
<span class="empty"
|
||||
>Step into a call on the right, or up to a caller on the left — the trail records the
|
||||
path.</span
|
||||
>
|
||||
{:else}
|
||||
{#each hops as hop, i (hop.id)}
|
||||
{#if i > 0}
|
||||
@@ -59,7 +84,7 @@
|
||||
|
||||
<span class="spacer"></span>
|
||||
|
||||
{#if hops.length > 1}
|
||||
{#if READ_AS_FLOW && hops.length > 1}
|
||||
<button type="button" class="tb-btn" onclick={readAsFlow}>Read as flow</button>
|
||||
{/if}
|
||||
{#if hops.length > 0}
|
||||
|
||||
Reference in New Issue
Block a user