@dotdotgod/cli 0.6.7 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -42,7 +42,7 @@ dotdotgod init .
42
42
  dotdotgod validate .
43
43
  dotdotgod query . "project documentation"
44
44
  dotdotgod graph impact . --changed <path> --compact
45
- dotdotgod graph serve . --changed <path>
45
+ dotdotgod graph serve .
46
46
  ```
47
47
 
48
48
  ## What It Does
@@ -53,7 +53,8 @@ dotdotgod graph serve . --changed <path>
53
53
  - `query` defaults to hybrid vector/keyword search over full Markdown and code outlines/comments, not implementation bodies. Use `--scope docs|code|all`, `--search hybrid|keyword|vector` and `--limit 30`. Keyword mode works without embeddings; hybrid reports vector failures and retains keyword results. Selecting a remote embedding provider authorizes sending documentation and code metadata (names, signatures, and comments), without implementation bodies or an additional consent flag. Local E5 remains the default when its optional runtime is installed. Tree-sitter coverage and boundaries: [CODE_OUTLINES.md](../../docs/spec/cli/CODE_OUTLINES.md).
54
54
  - `resolve` and `expand` map explicit or high-signal prompt references to project files and can include related impact evidence.
55
55
  - `graph impact` ranks likely related specs, tests, docs, commands, and source files for one or more changed paths, with a combined ranking and per-file top five. Non-seed scores use fixed weighted PPR connection `80` plus memory policy `20`. A bounded request-local multilingual vector overlay participates in PPR when the query cache is available; vector preparation failures degrade to structural-only results.
56
- - `graph serve` starts a local read-only explorer rooted at one or more changed files. It keeps only connected shared structural nodes, lays them out by minimum structural hop, and supports persisted instant, layer, or one-by-one reveal modes.
56
+ - `graph serve` starts a standalone read-only traceability graph without Obsidian. It uses resolved project configuration, relation filters, document-group colors, and optional weighted line widths.
57
+ - `graph export` writes `.dotdotgod/traceability.json` for the desktop Obsidian plugin. Run it in the project/vault, then reload traceability in Obsidian. Exported paths are relative; referenced files must exist in the vault. See [plugin setup](../obsidian/README.md).
57
58
  - `traceability links` checks or repairs generated Markdown traceability-link sections.
58
59
 
59
60
  ## Commands
@@ -79,8 +80,8 @@ dotdotgod traceability links . --write
79
80
  dotdotgod graph impact . --changed <path>
80
81
  dotdotgod graph impact . --changed <path> --changed <another-path> --compact
81
82
  dotdotgod graph impact . --changed <path> --changed <another-path> --yml
82
- dotdotgod graph serve . --changed <path>
83
- dotdotgod graph serve . --changed <path> --changed <another-path> --port 4313
83
+ dotdotgod graph serve .
84
+ dotdotgod graph serve . --port 4313
84
85
  dotdotgod graph communities .
85
86
  ```
86
87
 
@@ -95,7 +96,7 @@ dotdotgod graph communities .
95
96
  | Resolve `[[...]]` references from a prompt | `dotdotgod expand . "Update [[PLAN_MODE]]"` |
96
97
  | Repair generated traceability-link sections | `dotdotgod traceability links . --write` |
97
98
  | See what else to inspect after a change | `dotdotgod graph impact . --changed <path> --compact` |
98
- | Explore the connected structural graph visually | `dotdotgod graph serve . --changed <path>` |
99
+ | Explore project traceability visually | `dotdotgod graph serve .` |
99
100
 
100
101
  ## Changed-File Impact
101
102
 
@@ -122,10 +123,10 @@ The indexed graph is built from maintained project files: Markdown links, README
122
123
  ## Local Impact Graph Explorer
123
124
 
124
125
  ```bash
125
- dotdotgod graph serve . --changed packages/cli/src/core.mjs
126
+ dotdotgod graph serve .
126
127
  ```
127
128
 
128
- `graph serve` prints a local URL and binds to `127.0.0.1` by default. Repeat `--changed` to use multiple roots. Set `--port 4313` for a fixed port or `--host <host>` when the default loopback binding is not suitable.
129
+ `graph serve` prints a local URL and binds to `127.0.0.1` by default. Set `--port 4313` for a fixed port. Only loopback hosts are accepted. Use `graph impact --changed <path>` for changed-file ranking; the web command no longer accepts `--changed`. Reload refreshes project config and index data.
129
130
 
130
131
  The explorer includes only shared indexed nodes structurally connected to a retained changed-file root. Connectivity treats stored structural edges as undirected so incoming evidence remains discoverable, while rendered arrows preserve each relation's original direction. Local-memory nodes, disconnected nodes, and request-local vector edges are excluded from the canvas and search.
131
132
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dotdotgod/cli",
3
- "version": "0.6.7",
3
+ "version": "0.7.0",
4
4
  "description": "CLI for dotdotgod validation, hybrid document/code-outline search, and graph indexing.",
5
5
  "type": "module",
6
6
  "license": "Elastic-2.0",
@@ -31,10 +31,8 @@
31
31
  "dependencies": {
32
32
  "@vscode/tree-sitter-wasm": "0.3.1",
33
33
  "d3": "^7.9.0",
34
- "graphology": "^0.26.0",
35
34
  "ignore": "7.0.5",
36
- "leiden-ts": "^0.1.0",
37
- "sigma": "^3.0.3"
35
+ "leiden-ts": "^0.1.0"
38
36
  },
39
37
  "homepage": "https://github.com/dotdotgod/dotdotgod-kit/tree/main/packages/cli#readme",
40
38
  "repository": {
package/src/cli/usage.mjs CHANGED
@@ -11,7 +11,7 @@ export function commandUsage(command = 'root') {
11
11
  dotdotgod validate <root> [--include-local-memory] [--check-index] [--max-lines n] [--max-chars n] [--no-link-check] [--json]`;
12
12
  case 'init':
13
13
  return `Usage:
14
- dotdotgod init <root> [--project-name NAME] [--template NAME] [--documentation-root PATH] [--dotdot-setting] [--claude-plans] [--dry-run] [--json]
14
+ dotdotgod init <root> [--project-name NAME] [--template NAME] [--documentation-root PATH] [--claude-plans] [--dry-run] [--json]
15
15
 
16
16
  Create AGENTS.md, agent entrypoints, docs indexes, a project config from an initialization template, and local memory gitignore entries.
17
17
  Use --claude-plans only after opting into project-local native plan storage; existing Claude settings are preserved.`;
@@ -67,7 +67,8 @@ Checks or writes generated Markdown traceability link sections from canonical fe
67
67
  case 'graph':
68
68
  return `Usage:
69
69
  dotdotgod graph impact <root> --changed <path> [--changed <path> ...] [--compact|--json|--yml|--yaml]
70
- dotdotgod graph serve <root> --changed <path> [--changed <path> ...] [--host HOST] [--port PORT]
70
+ dotdotgod graph serve <root> [--host HOST] [--port PORT]
71
+ dotdotgod graph export <root> [--json]
71
72
  dotdotgod graph communities <root> [--json]`;
72
73
  case 'graph impact':
73
74
  return `Usage:
@@ -76,9 +77,11 @@ Checks or writes generated Markdown traceability link sections from canonical fe
76
77
  Ranks nodes related to one or more changed files. <root> is the project root; repeat --changed with project-relative file paths. Results include a combined ranking and the top five related nodes for each changed file. Use --compact for a short text summary or --yml/--yaml for structured agent-facing output.`;
77
78
  case 'graph serve':
78
79
  return `Usage:
79
- dotdotgod graph serve <root> --changed <path> [--changed <path> ...] [--host HOST] [--port PORT]
80
+ dotdotgod graph serve <root> [--host HOST] [--port PORT]
80
81
 
81
- Starts a local-only impact graph explorer. Changed files are rendered as root nodes and are excluded from related-result lists.`;
82
+ Starts a standalone local traceability graph without Obsidian.`;
83
+ case 'graph export':
84
+ return `Usage:\n dotdotgod graph export <root> [--json]\n\nWrite .dotdotgod/traceability.json for the Obsidian plugin.`;
82
85
  case 'graph communities':
83
86
  return `Usage:
84
87
  dotdotgod graph communities <root> [--json]`;
@@ -102,7 +105,8 @@ Commands:
102
105
  status Show local index status.
103
106
  traceability links Check or write generated traceability links.
104
107
  graph impact Find files related to changed files.
105
- graph serve Open a local impact graph explorer.
108
+ graph serve Open a standalone local traceability graph.
109
+ graph export Export traceability data for Obsidian.
106
110
  graph communities Find groups of related project-memory nodes.
107
111
 
108
112
  Options:
@@ -1,4 +1,7 @@
1
1
  import { commandUsage, parseCommon, usage } from '../cli/usage.mjs';
2
+ import { mkdir, writeFile, rename, rm } from 'node:fs/promises';
3
+ import { join } from 'node:path';
4
+ import { exportTraceability } from '../graph-view/traceability.mjs';
2
5
  import { buildCommunities } from '../graph/communities.mjs';
3
6
  import { graphSummary, readFreshIndex } from '../index/cache.mjs';
4
7
  import { buildCompactImpactReport, buildImpactReport } from '../impact/report.mjs';
@@ -42,9 +45,23 @@ export async function runGraph(argv) {
42
45
  const sub = argv[0];
43
46
  const isImpact = sub === 'impact';
44
47
  const isServe = sub === 'serve';
45
- if (!['impact', 'communities', 'serve'].includes(sub)) usage(sub ? `Unknown graph command: ${sub}` : 'Missing graph command.', 'graph');
48
+ if (!['impact', 'communities', 'serve', 'export'].includes(sub)) usage(sub ? `Unknown graph command: ${sub}` : 'Missing graph command.', 'graph');
46
49
  const options = parseGraphOptions(argv.slice(1));
47
- if (isServe && options.changed.length === 0) usage('Missing required option: --changed <path>.', 'graph serve');
50
+ if (sub === 'export') {
51
+ const graph = await exportTraceability(options.root);
52
+ delete graph.projectRoot;
53
+ const directory = join(options.root, '.dotdotgod');
54
+ await mkdir(directory, { recursive: true });
55
+ const path = join(directory, 'traceability.json');
56
+ const temporary = `${path}.${process.pid}.tmp`;
57
+ try {
58
+ await writeFile(temporary, JSON.stringify(graph, null, 2));
59
+ await rename(temporary, path);
60
+ } finally { await rm(temporary, { force: true }); }
61
+ console.log(options.json ? JSON.stringify({ ok: true, path, edges: graph.edges.length }) : `graph export: ${path} (${graph.edges.length} relations)`);
62
+ return;
63
+ }
64
+ if (isServe && options.changed.length > 0) usage('graph serve now shows project traceability. Use graph impact for --changed analysis.', 'graph serve');
48
65
  if (isServe && !Number.isInteger(options.port)) usage('Invalid --port. Use an integer from 0 through 65535.', 'graph serve');
49
66
  if (isServe) {
50
67
  options.changed = [...new Set(options.changed.map((path) => canonicalizeChangedPath(options.root, path)).filter(Boolean))];
@@ -7,9 +7,16 @@ import { isKebabCase } from '../docs/markdown.mjs';
7
7
  export const DEFAULT_TEMPLATE_NAME = 'software';
8
8
  export const BUILT_IN_TEMPLATE_NAMES = ['software', 'research', 'case-and-evidence', 'publication', 'portfolio', 'policy'];
9
9
 
10
+ const engineeringScaffold = [
11
+ ['docs/spec/README.md', 'Specs', 'Use this area for behavior specs, API contracts, and requirements. Follow `dotdotgod validate` traceability guidance for applicable documents.'],
12
+ ['docs/arch/README.md', 'Architecture', 'Use this area for architecture decisions, design rationale, conventions, and integration boundaries.'],
13
+ ['docs/test/README.md', 'Verification', 'Use this area for test strategy, evaluation methods, regression cases, and verification records.'],
14
+ ];
15
+
10
16
  const BUILT_IN_TEMPLATE_SCAFFOLDS = {
11
- software: [],
17
+ software: engineeringScaffold,
12
18
  research: [
19
+ ...engineeringScaffold,
13
20
  ['docs/research/README.md', 'Research', 'Use this area for research notes, sources, and findings.'],
14
21
  ['docs/record/README.md', 'Research Records', 'Use this area for dated measurements, experiments, and execution records.'],
15
22
  ['docs/report/README.md', 'Reports', 'Use this area for research diagnoses, analyses, results, and performance reports.'],
package/src/core.mjs CHANGED
@@ -28,8 +28,7 @@ export { addDeterministicSemanticEdges } from './graph/semantic.mjs';
28
28
  export { buildIndex, writeIndex, readIndex, getStatus, runIndex, runStatus, readFreshIndex, graphSummary } from './index/cache.mjs';
29
29
  export { runValidate } from './validate/run.mjs';
30
30
  export { buildImpactReport, buildCompactImpactReport } from './impact/report.mjs';
31
- export { buildImpactGraphPayload } from './graph-view/payload.mjs';
32
- export { buildServedImpact, startImpactGraphServer } from './graph-view/server.mjs';
31
+ export { startImpactGraphServer } from './graph-view/server.mjs';
33
32
  export { buildChangedFileProfile, MAX_VECTOR_PROFILE_CHARS, MAX_VECTOR_PROFILE_FILE_BYTES, MAX_VECTOR_PROFILE_METADATA_ITEMS } from './impact/vector-profile.mjs';
34
33
  export { buildVectorImpactOverlay, VECTOR_RELATION_WEIGHT } from './impact/vector-overlay.mjs';
35
34
  export { formatCompactImpactOutput, formatYmlGraphImpactError, formatYmlImpactOutput } from './impact/format.mjs';
@@ -1,115 +1,29 @@
1
- :root {
2
- color-scheme: light;
3
- --paper: #f4f6f3;
4
- --panel: #fbfcf8;
5
- --ink: #182028;
6
- --muted: #59656f;
7
- --rule: #c8d0cd;
8
- --cobalt: #1857c8;
9
- --amber: #c66a12;
10
- --green: #16734b;
11
- --violet: #6b47a8;
12
- --focus: #073f9b;
13
- font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
14
- }
1
+ :root { color-scheme: dark; font-family: system-ui; background: #1b1d24; color: #d8dee9; --file-row-height: 36px; }
15
2
  * { box-sizing: border-box; }
16
- html, body { margin: 0; min-height: 100%; background: var(--paper); color: var(--ink); }
17
- body { min-height: 100vh; overflow: hidden; }
18
- button, input, select { font: inherit; }
19
- button { border: 1px solid var(--ink); background: var(--ink); color: #fff; padding: .62rem .82rem; cursor: pointer; }
20
- button:hover { background: var(--cobalt); border-color: var(--cobalt); }
21
- button:focus-visible, input:focus-visible, select:focus-visible, .result-button:focus-visible { outline: 3px solid color-mix(in srgb, var(--focus), transparent 25%); outline-offset: 2px; }
22
- ::selection { background: var(--cobalt); color: white; }
23
- .command-rail { height: 64px; display: grid; grid-template-columns: 1fr minmax(420px, 620px) auto; align-items: center; gap: 1rem; padding: 0 1rem; border-bottom: 1px solid var(--ink); background: var(--panel); }
24
- .brand { display: flex; align-items: baseline; gap: .58rem; white-space: nowrap; }
25
- .brand strong { letter-spacing: -.025em; }
26
- .brand span:last-child { color: var(--muted); }
27
- .brand-mark { width: 12px; height: 12px; background: var(--cobalt); display: inline-block; }
28
- .reroot-form { display: grid; grid-template-columns: auto minmax(180px, 1fr) auto; align-items: center; gap: .55rem; }
29
- .reroot-form label { font-size: .76rem; font-weight: 750; text-transform: uppercase; letter-spacing: .07em; }
30
- .reroot-form input { min-width: 0; padding: .58rem .7rem; border: 1px solid var(--rule); background: white; color: var(--ink); }
31
- #status { margin: 0; color: var(--muted); font-size: .82rem; white-space: nowrap; }
32
- .workspace { height: calc(100vh - 64px); display: grid; grid-template-columns: 225px minmax(320px, 1fr) minmax(330px, 420px); }
33
- .filters, .evidence { background: var(--panel); overflow: auto; }
34
- .filters { border-right: 1px solid var(--rule); padding: 1.2rem 1rem; display: flex; flex-direction: column; gap: 1.3rem; }
35
- h1, h2, h3, p { margin-top: 0; }
36
- h1 { font-size: 1.42rem; margin-bottom: .42rem; letter-spacing: -.025em; }
37
- h2 { font-size: 1.12rem; margin-bottom: .2rem; }
38
- h3 { font-size: 1rem; }
39
- .filters p, .plot-heading p, .inspection p { color: var(--muted); font-size: .86rem; line-height: 1.5; }
40
- #relation-filters { border: 0; padding: 0; margin: 0; display: grid; gap: .35rem; }
41
- .filter-control { display: grid; grid-template-columns: 18px 1fr auto; align-items: center; gap: .48rem; padding: .42rem 0; border-bottom: 1px solid #e2e7e4; font-size: .82rem; }
42
- .filter-control input { accent-color: var(--cobalt); }
43
- .filter-control code { color: var(--muted); font-size: .7rem; }
44
- .motion-control { margin-top: .35rem; font-size: .72rem; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; }
45
- #motion-mode { width: 100%; border: 1px solid var(--ink); border-radius: 0; background: var(--panel); color: var(--ink); padding: .52rem; }
46
- #skip-reveal { width: 100%; padding: .48rem .62rem; background: transparent; color: var(--ink); }
47
- .legend { display: grid; gap: .62rem; font-size: .78rem; }
48
- .legend span { display: flex; align-items: center; gap: .55rem; }
49
- .node-key { display: inline-block; width: 10px; height: 10px; border: 1px solid var(--ink); }
50
- .node-key.seed { background: var(--cobalt); }
51
- .node-key.doc { background: var(--violet); }
52
- .node-key.test { background: var(--green); }
53
- .node-key.code { background: #42515e; }
54
- .node-key.contract { background: var(--amber); }
55
- .node-key.heading { width: 7px; height: 7px; margin-inline: 1.5px; border-color: #66736d; background: #8b9690; }
56
- .reading-note { margin-top: auto; padding-top: 1rem; border-top: 1px solid var(--rule); }
57
- .plot { min-width: 0; position: relative; background-color: #edf1ee; background-image: linear-gradient(#dce3df 1px, transparent 1px), linear-gradient(90deg, #dce3df 1px, transparent 1px); background-size: 32px 32px; }
58
- .plot-heading { height: 58px; display: flex; justify-content: space-between; align-items: center; padding: .65rem .85rem; background: color-mix(in srgb, var(--panel), transparent 6%); border-bottom: 1px solid var(--rule); }
59
- .plot-heading p { margin-bottom: 0; }
60
- .plot-heading button { background: transparent; color: var(--ink); padding: .42rem .62rem; }
61
- #graph { width: 100%; height: calc(100% - 58px); }
62
- .graph-fallback { position: absolute; inset: 58px 0 0; place-items: center; padding: 2rem; text-align: center; background: var(--paper); color: var(--muted); }
63
- .graph-fallback:not([hidden]) { display: grid; }
64
- .evidence { border-left: 1px solid var(--rule); display: grid; grid-template-rows: auto auto minmax(180px, 1fr) auto; }
65
- .node-search { padding: 1rem; border-bottom: 1px solid var(--rule); }
66
- .node-search label { display: block; margin-bottom: .45rem; font-size: .76rem; font-weight: 750; text-transform: uppercase; letter-spacing: .06em; }
67
- .node-search input { width: 100%; padding: .58rem .7rem; border: 1px solid var(--rule); background: white; color: var(--ink); }
68
- .node-search-results { list-style: none; margin: .45rem 0 0; padding: 0; max-height: 11rem; overflow: auto; }
69
- .node-search-results:empty { display: none; }
70
- .node-search-results button { width: 100%; padding: .5rem; border: 0; border-bottom: 1px solid #e2e7e4; background: transparent; color: var(--ink); text-align: left; font-family: ui-monospace, SFMono-Regular, Consolas, monospace; font-size: .72rem; overflow-wrap: anywhere; }
71
- .node-search-results button:hover { background: #e5ecfa; color: var(--ink); }
72
- .evidence-heading { display: flex; justify-content: space-between; align-items: baseline; padding: 1rem; border-bottom: 1px solid var(--rule); }
73
- .evidence-heading h2 { margin: 0; }
74
- #result-count { color: var(--muted); font-variant-numeric: tabular-nums; font-size: .78rem; }
75
- .result-list { list-style: none; margin: 0; padding: 0; overflow: auto; }
76
- .result-button { width: 100%; display: grid; grid-template-columns: 2.2rem 1fr auto; gap: .62rem; align-items: start; padding: .75rem 1rem; border: 0; border-bottom: 1px solid #dce2df; background: transparent; color: var(--ink); text-align: left; }
77
- .result-button:hover, .result-button[aria-current="true"] { background: #e5ecfa; color: var(--ink); }
78
- .rank { color: var(--muted); font-variant-numeric: tabular-nums; }
79
- .result-copy { min-width: 0; }
80
- .result-path { display: block; font-family: ui-monospace, SFMono-Regular, Consolas, monospace; font-size: .76rem; overflow-wrap: anywhere; }
81
- .result-reason { display: block; color: var(--muted); font-size: .72rem; margin-top: .26rem; }
82
- .score { color: var(--cobalt); font-weight: 800; font-variant-numeric: tabular-nums; }
83
- .inspection { border-top: 1px solid var(--ink); padding: 1rem; max-height: 38vh; overflow: auto; background: #f7f8f4; }
84
- .inspection dl { display: grid; grid-template-columns: 6.8rem 1fr; gap: .45rem .75rem; margin: 0; font-size: .78rem; }
85
- .inspection dt { color: var(--muted); }
86
- .inspection dd { margin: 0; overflow-wrap: anywhere; }
87
- .inspection .reroot-selected { margin-top: 1rem; width: 100%; }
88
- .sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; }
89
- @media (max-width: 1050px) {
90
- body { overflow: auto; }
91
- .command-rail { height: auto; min-height: 64px; grid-template-columns: 1fr auto; padding-block: .65rem; }
92
- .reroot-form { grid-column: 1 / -1; grid-row: 2; }
93
- .workspace { height: auto; min-height: calc(100vh - 110px); grid-template-columns: 190px minmax(300px, 1fr); }
94
- .evidence { grid-column: 1 / -1; border-left: 0; border-top: 1px solid var(--ink); grid-template-rows: auto auto minmax(220px, 42vh) auto; }
95
- .plot { min-height: 620px; }
96
- }
97
- @media (max-width: 680px) {
98
- .command-rail { display: flex; flex-wrap: wrap; }
99
- .brand { flex: 1; }
100
- #status { width: 100%; }
101
- .reroot-form { width: 100%; grid-template-columns: 1fr auto; }
102
- .reroot-form label { grid-column: 1 / -1; }
103
- .workspace { display: flex; flex-direction: column; }
104
- .filters { border-right: 0; border-bottom: 1px solid var(--rule); }
105
- #relation-filters { grid-template-columns: repeat(2, minmax(0, 1fr)); }
106
- .legend { grid-template-columns: repeat(2, minmax(0, 1fr)); }
107
- .reading-note { margin-top: 0; }
108
- .plot { min-height: 60vh; }
109
- .evidence { min-height: 70vh; }
3
+ body { margin: 0; overflow: hidden; }
4
+ header { position: absolute; top: 16px; left: 24px; right: 320px; z-index: 2; pointer-events: none; }
5
+ h1 { font-size: 16px; }
6
+ #legend { display: flex; flex-wrap: wrap; gap: 8px; }
7
+ button, input { font: inherit; color: inherit; background: #252832; border: 1px solid #586174; border-radius: 4px; padding: 8px; }
8
+ button { cursor: pointer; pointer-events: auto; }
9
+ button:focus-visible, input:focus-visible { outline: 2px solid #72b9e6; }
10
+ #legend button[aria-pressed=false] { opacity: .45; }
11
+ .swatch { display: inline-block; width: 18px; height: 3px; margin-right: 8px; vertical-align: middle; }
12
+ canvas { width: 100vw; height: 100vh; display: block; touch-action: none; }
13
+ aside { position: absolute; right: 16px; top: 16px; bottom: 16px; width: 280px; padding: 16px; background: #252832; border: 1px solid #434958; border-radius: 6px; display: flex; flex-direction: column; overflow: hidden; }
14
+ aside > details, aside > #search, aside > #copy, aside > #path, aside > #status { flex: none; }
15
+ summary { cursor: pointer; font-weight: 600; }
16
+ label { display: flex; gap: 8px; margin: 16px 0; font-size: 13px; }
17
+ #files { list-style: none; padding: 0; flex: 1; min-height: 72px; overflow: auto; overscroll-behavior: contain; margin: 12px 0; overflow-anchor: none; scrollbar-gutter: stable; }
18
+ #files li:not([aria-hidden]) { height: var(--file-row-height); }
19
+ #files button { width: 100%; height: var(--file-row-height); text-align: left; border: 0; background: none; font-size: 12px; white-space: nowrap; text-overflow: ellipsis; overflow: hidden; text-decoration: none; opacity: 1; }
20
+ #files button[aria-pressed=true] { background: #343b4c; }
21
+ #search { width: 100%; }
22
+ #path { font: 12px monospace; overflow-wrap: anywhere; }
23
+ #status { font-size: 12px; overflow-wrap: anywhere; max-height: 110px; overflow: auto; }
24
+ #tooltip { position: fixed; padding: 8px; background: #252832; border: 1px solid #68738a; pointer-events: none; font: 12px monospace; max-width: calc(100vw - 16px); overflow-wrap: anywhere; }
25
+ @media (max-width: 700px) {
26
+ aside { top: auto; height: 45vh; width: calc(100% - 24px); right: 12px; bottom: 12px; }
27
+ aside > details[open] { max-height: 140px; overflow: auto; }
28
+ header { right: 16px; left: 16px; }
110
29
  }
111
- @media (prefers-reduced-motion: reduce) { *, *::before, *::after { scroll-behavior: auto !important; transition: none !important; animation: none !important; } }
112
-
113
- .reference-warnings { padding: 12px; border: 1px solid var(--border, #ddd); border-radius: 8px; }
114
- .reference-warnings h3 { margin: 0; }
115
- .reference-warnings ul { padding-left: 18px; overflow-wrap: anywhere; }