feat(ui): saved trails — a walk you named, kept, and still true after a re-index (CG-60)
Save trail on the trail bar writes the walk to .codegraph/ui/trails/ as one JSON file, listed on the empty screen and on Entry points above the derived suggestions, reopened at the symbol you left with the whole path restored. A hop is stored by qualified name, kind and file — never by node id, which contains a start line and so changes the first time anybody edits above the symbol. Every hop is re-resolved against the current index on the way out and each row says what became of it: still here, moved to another file, now ambiguous, or gone. A hole is never stitched over: the row opens the longest run of CONSECUTIVE resolved hops and says which ones those are, because the trail is a path and a skipped hop would draw a call that does not exist. This is the first write the viewer makes, and the boundary moved with it: POST/DELETE answer under /api/ only, must carry X-CodeGraph-UI and application/json (neither of which a cross-origin form can produce without a preflight this server answers none of), and --read-only refuses both while still listing what is there. The blanket "read-only" claim is retired from the banner, the README, the CLI help and the docs site in favour of the narrower true one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
55a33055ee
commit
47576b392e
+34
-12
@@ -1856,8 +1856,10 @@ function printNoIndexGuidance(projectPath: string): void {
|
||||
* codegraph ui [path] (alias: web)
|
||||
*
|
||||
* The browser reader: serves the built viewer (`dist/viewer/`) over loopback
|
||||
* and opens it. Read-only in every sense — it answers GET, it opens the index
|
||||
* for reading, and it never writes to the project or the graph.
|
||||
* and opens it. It opens the index for reading and never writes to it, never
|
||||
* indexes, and never changes a line of the project's code. The single thing it
|
||||
* writes is a trail the reader saved, as JSON under `.codegraph/ui/trails/`;
|
||||
* `--read-only` turns even that off.
|
||||
*
|
||||
* Deliberately absent from TELEMETRY_FLUSH_COMMANDS above: the command's own
|
||||
* banner tells the user nothing leaves their machine, so it must not be the
|
||||
@@ -1870,6 +1872,7 @@ program
|
||||
.description('Open the CodeGraph viewer in your browser — read your indexed project as a graph')
|
||||
.option('--port <number>', `Port to listen on (default: ${DEFAULT_UI_PORT}, or the next free one)`)
|
||||
.option('--no-open', 'Print the URL instead of opening a browser')
|
||||
.option('--read-only', 'Refuse every write — saved trails can be opened but not saved or deleted')
|
||||
.addHelpText(
|
||||
'after',
|
||||
`
|
||||
@@ -1897,10 +1900,17 @@ The page keeps up with the project while it is open: save a file and it says so
|
||||
within about a third of a second, and whatever is on screen re-reads the graph
|
||||
when something re-indexes it. It watches for that; it never polls.
|
||||
|
||||
The viewer listens on 127.0.0.1 only, so nothing on your network can reach it,
|
||||
and it is read-only: it opens an index that already exists and never changes
|
||||
your project or your graph. Requests from any other host are refused, and
|
||||
nothing is sent anywhere: no code, no paths, no analytics.
|
||||
Save a walk you want to keep: name the trail and it is written to
|
||||
.codegraph/ui/trails/ (already gitignored) as plain JSON, listed on the empty
|
||||
screen, and reopened at the symbol you left. Hops are remembered by name rather
|
||||
than by position, so a saved trail survives re-indexing and says which hop moved
|
||||
when one does. Pass --read-only to refuse every write.
|
||||
|
||||
The viewer listens on 127.0.0.1 only, so nothing on your network can reach it.
|
||||
It opens an index that already exists, never indexes, and never changes a line
|
||||
of your code — the one thing it writes is a trail you asked it to save.
|
||||
Requests from any other host are refused, and nothing is sent anywhere: no code,
|
||||
no paths, no analytics.
|
||||
|
||||
Without --port it takes ${DEFAULT_UI_PORT}, or the next free port if that one is busy.
|
||||
|
||||
@@ -1908,7 +1918,7 @@ Set ${BROWSER_ENV}=<command> to choose which browser opens, or
|
||||
${BROWSER_ENV}=none to never open one.
|
||||
`
|
||||
)
|
||||
.action(async (pathArg: string | undefined, options: { port?: string; open?: boolean }) => {
|
||||
.action(async (pathArg: string | undefined, options: { port?: string; open?: boolean; readOnly?: boolean }) => {
|
||||
// An explicit --port stays explicit: a scripted `--port 8080` that quietly
|
||||
// lands on 8081 is worse than one that says the port is busy. The default
|
||||
// port is the only one we're free to walk away from.
|
||||
@@ -1942,10 +1952,17 @@ ${BROWSER_ENV}=none to never open one.
|
||||
'../ui-server'
|
||||
);
|
||||
|
||||
// The read-only JSON API the viewer reads its screens from. It opens the
|
||||
// index lazily on the first request, so a slow first paint is the only cost
|
||||
// of mounting it here rather than after the browser connects.
|
||||
const api = createGraphApi({ projectRoot: projectPath });
|
||||
// The JSON API the viewer reads its screens from. It opens the index lazily
|
||||
// on the first request, so a slow first paint is the only cost of mounting
|
||||
// it here rather than after the browser connects.
|
||||
const readOnly = options.readOnly === true;
|
||||
const api = createGraphApi({
|
||||
projectRoot: projectPath,
|
||||
readOnly,
|
||||
readOnlyReason: readOnly
|
||||
? 'This viewer was started with --read-only, so trails cannot be saved.'
|
||||
: undefined,
|
||||
});
|
||||
|
||||
let handle: UiServerHandle;
|
||||
try {
|
||||
@@ -1968,7 +1985,12 @@ ${BROWSER_ENV}=none to never open one.
|
||||
console.log('');
|
||||
console.log(` ${chalk.dim('Reading')} ${projectPath}`);
|
||||
console.log(` ${chalk.dim('URL')} ${chalk.cyan(handle.url)}`);
|
||||
console.log(` ${chalk.dim('Access')} this machine only ${getGlyphs().dash} read-only, nothing leaves your computer`);
|
||||
console.log(
|
||||
` ${chalk.dim('Access')} this machine only ${getGlyphs().dash} ` +
|
||||
(readOnly
|
||||
? 'read-only, nothing leaves your computer'
|
||||
: 'nothing leaves your computer; saved trails are the only thing written')
|
||||
);
|
||||
console.log('');
|
||||
|
||||
const opened = options.open === false ? false : openBrowser(handle.url);
|
||||
|
||||
Reference in New Issue
Block a user