@plebeiantech/lat.md 0.12.3-fork.3 → 0.12.3-fork.5
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 +2 -0
- package/dist/src/cli/check-mode.js +5 -1
- package/dist/src/cli/comment-guard.d.ts +14 -0
- package/dist/src/cli/comment-guard.js +10 -33
- package/dist/src/cli/comment-reminder.d.ts +65 -6
- package/dist/src/cli/comment-reminder.js +164 -28
- package/dist/src/cli/index.js +29 -4
- package/dist/src/cli/search.js +6 -4
- package/dist/src/cli/ui-build.d.ts +4 -0
- package/dist/src/cli/ui-build.js +13 -0
- package/dist/src/cli/ui.js +18 -2
- package/dist/src/code-refs.js +25 -4
- package/dist/src/fork/upstream-drift-cli.d.ts +1 -0
- package/dist/src/fork/upstream-drift-cli.js +2 -0
- package/dist/src/fork/upstream-drift.d.ts +26 -0
- package/dist/src/fork/upstream-drift.js +185 -0
- package/dist/src/fork/upstream-guard-cli.d.ts +1 -0
- package/dist/src/fork/upstream-guard-cli.js +2 -0
- package/dist/src/fork/upstream-guard.d.ts +72 -0
- package/dist/src/fork/upstream-guard.js +445 -0
- package/dist/src/graph-export.d.ts +2 -0
- package/dist/src/graph-export.js +31 -12
- package/dist/src/knowledge/claude-memory.d.ts +1 -0
- package/dist/src/knowledge/claude-memory.js +33 -4
- package/dist/src/lattice.d.ts +1 -0
- package/dist/src/search/search.d.ts +1 -0
- package/dist/src/search/search.js +15 -9
- package/dist/src/untrusted.d.ts +0 -6
- package/dist/src/untrusted.js +7 -11
- package/dist/src/view/client/assets/index-VZwPPiFE.js +334 -0
- package/dist/src/view/client/assets/index-W8ktvNj9.css +1 -0
- package/dist/src/view/client/assets/logo-CNF8zs47.svg +182 -0
- package/dist/src/view/client/index.html +2 -2
- package/dist/src/view/git-diff.js +17 -0
- package/dist/src/view/graph.d.ts +6 -0
- package/dist/src/view/graph.js +176 -0
- package/dist/src/view/markdown.d.ts +5 -1
- package/dist/src/view/markdown.js +112 -25
- package/dist/src/view/protocol.d.ts +43 -0
- package/dist/src/view/protocol.js +1 -1
- package/dist/src/view/references.d.ts +2 -0
- package/dist/src/view/references.js +30 -8
- package/dist/src/view/repository.d.ts +5 -2
- package/dist/src/view/repository.js +13 -12
- package/dist/src/view/search.js +1 -0
- package/dist/src/view/server.d.ts +3 -0
- package/dist/src/view/server.js +42 -9
- package/dist/src/view/source-target.d.ts +8 -0
- package/dist/src/view/source-target.js +22 -0
- package/dist/src/view/static-build.d.ts +16 -0
- package/dist/src/view/static-build.js +408 -0
- package/dist/src/view/static-protocol.d.ts +23 -0
- package/dist/src/view/static-protocol.js +10 -0
- package/dist/src/view/store.d.ts +9 -1
- package/dist/src/view/store.js +82 -15
- package/dist/src/view/table-of-contents.d.ts +9 -0
- package/dist/src/view/table-of-contents.js +103 -0
- package/package.json +3 -1
- package/templates/AGENTS.md +2 -0
- package/templates/skill/SKILL.md +2 -0
- package/dist/src/view/client/assets/index-B0n0u03y.css +0 -1
- package/dist/src/view/client/assets/index-xm2_px4q.js +0 -23
package/dist/src/view/store.js
CHANGED
|
@@ -8,10 +8,14 @@ import { toPosix } from '../walk.js';
|
|
|
8
8
|
import { renderMarkdown } from './markdown.js';
|
|
9
9
|
import { buildViewDiagnostics } from './diagnostics.js';
|
|
10
10
|
import { buildGitDiffTree } from './git-diff.js';
|
|
11
|
+
import { buildViewGraph } from './graph.js';
|
|
12
|
+
import { buildViewTableOfContents } from './table-of-contents.js';
|
|
11
13
|
import { emptyViewGitSnapshot, findViewGitRepository, readViewGitSnapshot, sameViewGitSnapshot, } from './git.js';
|
|
14
|
+
import { DEFAULT_VIEW_LOGO_TEXT } from './protocol.js';
|
|
12
15
|
import { createMarkdownWikiLinkResolver, getViewSource, ViewDocumentNotFoundError, } from './repository.js';
|
|
13
16
|
import { buildViewReferenceIndex, parseViewMarkdownFile, renderSectionBackReferences, } from './references.js';
|
|
14
17
|
const DEFAULT_DEBOUNCE_MS = 75;
|
|
18
|
+
const DEFAULT_GIT_POLL_MS = 2_000;
|
|
15
19
|
function isInside(root, candidate) {
|
|
16
20
|
const rel = relative(root, candidate);
|
|
17
21
|
return (rel === '' ||
|
|
@@ -21,6 +25,10 @@ function projectPath(projectRoot, path) {
|
|
|
21
25
|
const normalized = isAbsolute(path) ? relative(projectRoot, path) : path;
|
|
22
26
|
return toPosix(normalized).replace(/^\.\//, '');
|
|
23
27
|
}
|
|
28
|
+
function excludedCodePath(projectRoot, path, excludedPaths) {
|
|
29
|
+
const absolutePath = resolve(projectRoot, path);
|
|
30
|
+
return excludedPaths.some((root) => isInside(resolve(root), absolutePath));
|
|
31
|
+
}
|
|
24
32
|
function sourcePath(path) {
|
|
25
33
|
return SOURCE_EXTENSIONS.has(extname(path).toLowerCase());
|
|
26
34
|
}
|
|
@@ -64,11 +72,14 @@ async function loadCodeReferenceFiles(projectRoot, refs) {
|
|
|
64
72
|
}));
|
|
65
73
|
return files;
|
|
66
74
|
}
|
|
67
|
-
async function scanCodeState(projectRoot) {
|
|
75
|
+
async function scanCodeState(projectRoot, excludedPaths = []) {
|
|
68
76
|
const scan = await scanCodeRefs(projectRoot);
|
|
69
|
-
const
|
|
77
|
+
const allowed = (path) => !excludedCodePath(projectRoot, path, excludedPaths);
|
|
78
|
+
const files = scan.files.filter(allowed);
|
|
79
|
+
const refs = scan.refs.filter((ref) => allowed(ref.file));
|
|
80
|
+
const scope = new Set(files.map((path) => projectPath(projectRoot, path)));
|
|
70
81
|
return {
|
|
71
|
-
files: await loadCodeReferenceFiles(projectRoot,
|
|
82
|
+
files: await loadCodeReferenceFiles(projectRoot, refs),
|
|
72
83
|
scope,
|
|
73
84
|
};
|
|
74
85
|
}
|
|
@@ -89,18 +100,21 @@ function viewIndex(latDir, paths, diagnostics, git) {
|
|
|
89
100
|
files: Object.fromEntries([...git.files].map(([path, file]) => [path, file.status])),
|
|
90
101
|
}
|
|
91
102
|
: null,
|
|
103
|
+
logoText: DEFAULT_VIEW_LOGO_TEXT,
|
|
92
104
|
};
|
|
93
105
|
}
|
|
94
106
|
async function buildSnapshot(latDir, projectRoot, markdownFiles, codeFiles, git, generation, markdownGeneration) {
|
|
95
107
|
const files = new Map([...markdownFiles].sort(([left], [right]) => left.localeCompare(right)));
|
|
96
108
|
const allSections = [...files.values()].flatMap((file) => file.sections);
|
|
97
109
|
const diagnostics = await buildViewDiagnostics(files.values(), codeFiles.values(), allSections, projectRoot);
|
|
110
|
+
const references = buildViewReferenceIndex(files.values(), codeFiles.values(), allSections);
|
|
98
111
|
return {
|
|
99
112
|
generation,
|
|
100
113
|
markdownGeneration,
|
|
101
114
|
files,
|
|
102
115
|
allSections,
|
|
103
|
-
references
|
|
116
|
+
references,
|
|
117
|
+
graph: buildViewGraph(files.values(), codeFiles.values(), allSections, diagnostics, git, generation),
|
|
104
118
|
diagnostics,
|
|
105
119
|
git,
|
|
106
120
|
index: viewIndex(latDir, [...files.keys()], diagnostics, git),
|
|
@@ -121,6 +135,8 @@ export class ViewStore {
|
|
|
121
135
|
watcher = null;
|
|
122
136
|
pendingPaths = new Set();
|
|
123
137
|
debounceTimer = null;
|
|
138
|
+
gitPollTimer = null;
|
|
139
|
+
gitPollQueued = false;
|
|
124
140
|
refreshTail = Promise.resolve();
|
|
125
141
|
closed = false;
|
|
126
142
|
constructor(latDir, projectRoot, realLatDir, snapshot, codeFiles, codeScope, gitRepository, options) {
|
|
@@ -140,6 +156,7 @@ export class ViewStore {
|
|
|
140
156
|
return this.snapshotValue.markdownGeneration;
|
|
141
157
|
}
|
|
142
158
|
startWatching() {
|
|
159
|
+
this.startGitPolling();
|
|
143
160
|
if (this.options.watch === false || this.watcher)
|
|
144
161
|
return;
|
|
145
162
|
try {
|
|
@@ -159,6 +176,9 @@ export class ViewStore {
|
|
|
159
176
|
getIndex() {
|
|
160
177
|
return this.snapshotValue.index;
|
|
161
178
|
}
|
|
179
|
+
getGraph() {
|
|
180
|
+
return this.snapshotValue.graph;
|
|
181
|
+
}
|
|
162
182
|
async getDocument(requestedPath) {
|
|
163
183
|
if (!requestedPath ||
|
|
164
184
|
requestedPath.includes('\\') ||
|
|
@@ -171,19 +191,39 @@ export class ViewStore {
|
|
|
171
191
|
if (!file) {
|
|
172
192
|
throw new ViewDocumentNotFoundError('Markdown document not found');
|
|
173
193
|
}
|
|
174
|
-
const resolver = await createMarkdownWikiLinkResolver(this.latDir, requestedPath, snapshot.allSections);
|
|
194
|
+
const resolver = await createMarkdownWikiLinkResolver(this.latDir, requestedPath, snapshot.allSections, snapshot.references);
|
|
175
195
|
const rendered = await renderMarkdown(file.content, requestedPath, resolver, { errors: [...(snapshot.diagnostics.get(requestedPath) ?? [])] }, file.tree);
|
|
196
|
+
const errors = [...(snapshot.diagnostics.get(requestedPath) ?? [])];
|
|
176
197
|
const gitFile = snapshot.git.files.get(requestedPath);
|
|
177
|
-
const
|
|
178
|
-
?
|
|
198
|
+
const gitTree = gitFile
|
|
199
|
+
? buildGitDiffTree(gitFile.baseContent, file.content, file.tree)
|
|
200
|
+
: null;
|
|
201
|
+
const gitRendered = gitTree
|
|
202
|
+
? await renderMarkdown(file.content, requestedPath, resolver, { errors }, gitTree)
|
|
179
203
|
: null;
|
|
180
|
-
const errors = [...(snapshot.diagnostics.get(requestedPath) ?? [])];
|
|
181
204
|
return {
|
|
182
205
|
path: requestedPath,
|
|
183
206
|
...rendered,
|
|
184
207
|
gitHtml: gitRendered?.html ?? null,
|
|
208
|
+
tableOfContents: buildViewTableOfContents(file.sections, file.tree, {
|
|
209
|
+
errors,
|
|
210
|
+
gitTree,
|
|
211
|
+
}),
|
|
212
|
+
graphNodeIds: Object.fromEntries(snapshot.graph.nodes
|
|
213
|
+
.filter((node) => node.documentPath === requestedPath && node.kind === 'document')
|
|
214
|
+
.map((node) => {
|
|
215
|
+
const hash = new URL(node.url, 'http://lat.local').hash.slice(1);
|
|
216
|
+
let headingId = hash;
|
|
217
|
+
try {
|
|
218
|
+
headingId = decodeURIComponent(hash);
|
|
219
|
+
}
|
|
220
|
+
catch {
|
|
221
|
+
// Keep malformed fragments as-is; they cannot collide with valid ids.
|
|
222
|
+
}
|
|
223
|
+
return [headingId, node.id];
|
|
224
|
+
})),
|
|
185
225
|
errors,
|
|
186
|
-
backReferences: await renderSectionBackReferences(snapshot.references, file.sections, this.latDir, this.projectRoot, (path) => createMarkdownWikiLinkResolver(this.latDir, path, snapshot.allSections)),
|
|
226
|
+
backReferences: await renderSectionBackReferences(snapshot.references, file.sections, this.latDir, this.projectRoot, (path) => createMarkdownWikiLinkResolver(this.latDir, path, snapshot.allSections, snapshot.references)),
|
|
187
227
|
frontmatter: {
|
|
188
228
|
requireCodeMention: parseFrontmatter(file.content).requireCodeMention === true,
|
|
189
229
|
},
|
|
@@ -197,7 +237,10 @@ export class ViewStore {
|
|
|
197
237
|
if (this.closed)
|
|
198
238
|
return Promise.resolve();
|
|
199
239
|
const normalized = paths.map((path) => path ? projectPath(this.projectRoot, path) : '');
|
|
200
|
-
|
|
240
|
+
return this.queueRefresh(normalized, false);
|
|
241
|
+
}
|
|
242
|
+
queueRefresh(paths, pollGit) {
|
|
243
|
+
const refresh = this.refreshTail.then(() => this.applyRefresh(paths, pollGit));
|
|
201
244
|
this.refreshTail = refresh.catch(() => { });
|
|
202
245
|
return refresh;
|
|
203
246
|
}
|
|
@@ -206,6 +249,9 @@ export class ViewStore {
|
|
|
206
249
|
if (this.debounceTimer)
|
|
207
250
|
clearTimeout(this.debounceTimer);
|
|
208
251
|
this.debounceTimer = null;
|
|
252
|
+
if (this.gitPollTimer)
|
|
253
|
+
clearInterval(this.gitPollTimer);
|
|
254
|
+
this.gitPollTimer = null;
|
|
209
255
|
this.watcher?.close();
|
|
210
256
|
this.watcher = null;
|
|
211
257
|
await this.refreshTail;
|
|
@@ -226,6 +272,24 @@ export class ViewStore {
|
|
|
226
272
|
});
|
|
227
273
|
}, this.options.debounceMs ?? DEFAULT_DEBOUNCE_MS);
|
|
228
274
|
}
|
|
275
|
+
startGitPolling() {
|
|
276
|
+
const interval = this.options.gitPollMs ?? DEFAULT_GIT_POLL_MS;
|
|
277
|
+
if (!this.gitRepository || interval <= 0 || this.gitPollTimer)
|
|
278
|
+
return;
|
|
279
|
+
this.gitPollTimer = setInterval(() => {
|
|
280
|
+
if (this.closed || this.gitPollQueued)
|
|
281
|
+
return;
|
|
282
|
+
this.gitPollQueued = true;
|
|
283
|
+
void this.queueRefresh([], true)
|
|
284
|
+
.catch((error) => {
|
|
285
|
+
process.stderr.write(`lat ui git: ${error.message}\n`);
|
|
286
|
+
})
|
|
287
|
+
.finally(() => {
|
|
288
|
+
this.gitPollQueued = false;
|
|
289
|
+
});
|
|
290
|
+
}, interval);
|
|
291
|
+
this.gitPollTimer.unref();
|
|
292
|
+
}
|
|
229
293
|
async readMarkdownFile(absolutePath) {
|
|
230
294
|
let realFile;
|
|
231
295
|
try {
|
|
@@ -239,7 +303,7 @@ export class ViewStore {
|
|
|
239
303
|
const content = await readFile(realFile, 'utf8');
|
|
240
304
|
return parseViewMarkdownFile(absolutePath, content, this.latDir, this.projectRoot);
|
|
241
305
|
}
|
|
242
|
-
async applyRefresh(paths) {
|
|
306
|
+
async applyRefresh(paths, pollGit) {
|
|
243
307
|
const fullRefresh = paths.includes('');
|
|
244
308
|
const latPath = projectPath(this.projectRoot, this.latDir);
|
|
245
309
|
const touchesMarkdown = fullRefresh ||
|
|
@@ -298,7 +362,7 @@ export class ViewStore {
|
|
|
298
362
|
}
|
|
299
363
|
linkedResourceChanged = paths.some((path) => path.startsWith(`${latPath}/`) && !path.toLowerCase().endsWith('.md'));
|
|
300
364
|
}
|
|
301
|
-
if (touchesMarkdown && this.gitRepository) {
|
|
365
|
+
if ((touchesMarkdown || pollGit) && this.gitRepository) {
|
|
302
366
|
try {
|
|
303
367
|
const nextGit = await readViewGitSnapshot(this.gitRepository, markdownFiles);
|
|
304
368
|
gitChanged = !sameViewGitSnapshot(git, nextGit);
|
|
@@ -308,12 +372,15 @@ export class ViewStore {
|
|
|
308
372
|
process.stderr.write(`lat ui git: ${error.message}\n`);
|
|
309
373
|
}
|
|
310
374
|
}
|
|
311
|
-
const codePaths = paths.filter((path) => path &&
|
|
375
|
+
const codePaths = paths.filter((path) => path &&
|
|
376
|
+
sourcePath(path) &&
|
|
377
|
+
!obviouslyIgnoredCodePath(path, latPath) &&
|
|
378
|
+
!excludedCodePath(this.projectRoot, path, this.options.codeExcludePaths ?? []));
|
|
312
379
|
const refreshCodeScope = fullRefresh ||
|
|
313
380
|
paths.some((path) => path === '.gitignore' || path.endsWith('/.gitignore')) ||
|
|
314
381
|
codePaths.some((path) => !this.codeScope.has(path) && !this.ignoredCodePaths.has(path));
|
|
315
382
|
if (refreshCodeScope) {
|
|
316
|
-
const nextCode = await scanCodeState(this.projectRoot);
|
|
383
|
+
const nextCode = await scanCodeState(this.projectRoot, this.options.codeExcludePaths);
|
|
317
384
|
codeFiles = nextCode.files;
|
|
318
385
|
this.codeScope = nextCode.scope;
|
|
319
386
|
this.ignoredCodePaths.clear();
|
|
@@ -369,7 +436,7 @@ export async function createViewStore(latDir, projectRoot, options = {}) {
|
|
|
369
436
|
const [realLatDir, markdownPaths, codeState, gitRepository] = await Promise.all([
|
|
370
437
|
realpath(latDir),
|
|
371
438
|
listLatticeFiles(latDir),
|
|
372
|
-
scanCodeState(projectRoot),
|
|
439
|
+
scanCodeState(projectRoot, options.codeExcludePaths),
|
|
373
440
|
options.git === false
|
|
374
441
|
? Promise.resolve(null)
|
|
375
442
|
: findViewGitRepository(projectRoot, latDir),
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Root } from 'mdast';
|
|
2
|
+
import { type Section } from '../lattice.js';
|
|
3
|
+
import type { ViewDocumentError, ViewDocumentTocItem } from './protocol.js';
|
|
4
|
+
export type ViewTableOfContentsOptions = {
|
|
5
|
+
errors?: readonly ViewDocumentError[];
|
|
6
|
+
gitTree?: Root | null;
|
|
7
|
+
};
|
|
8
|
+
/** Project rendered headings into the document's local navigation. */
|
|
9
|
+
export declare function buildViewTableOfContents(sections: Section[], tree: Root, options?: ViewTableOfContentsOptions): ViewDocumentTocItem[];
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { visit } from 'unist-util-visit';
|
|
2
|
+
import { flattenSections } from '../lattice.js';
|
|
3
|
+
function headingText(node) {
|
|
4
|
+
if (!node || typeof node !== 'object')
|
|
5
|
+
return '';
|
|
6
|
+
const value = node;
|
|
7
|
+
if (value.type === 'text' || value.type === 'inlineCode') {
|
|
8
|
+
return value.value ?? '';
|
|
9
|
+
}
|
|
10
|
+
if (value.type === 'image' || value.type === 'imageReference') {
|
|
11
|
+
return value.alt ?? '';
|
|
12
|
+
}
|
|
13
|
+
if (value.type === 'wikiLink') {
|
|
14
|
+
return value.data?.alias ?? value.value ?? '';
|
|
15
|
+
}
|
|
16
|
+
return value.children?.map(headingText).join('') ?? '';
|
|
17
|
+
}
|
|
18
|
+
function sectionAtLine(sections, line) {
|
|
19
|
+
let owner = null;
|
|
20
|
+
for (const section of sections) {
|
|
21
|
+
if (line < section.startLine || line > section.endLine)
|
|
22
|
+
continue;
|
|
23
|
+
if (!owner || section.depth >= owner.depth)
|
|
24
|
+
owner = section;
|
|
25
|
+
}
|
|
26
|
+
return owner;
|
|
27
|
+
}
|
|
28
|
+
function classNames(node) {
|
|
29
|
+
const value = node.data?.hProperties?.className;
|
|
30
|
+
if (Array.isArray(value))
|
|
31
|
+
return value.map(String);
|
|
32
|
+
return value ? [String(value)] : [];
|
|
33
|
+
}
|
|
34
|
+
function hasGitChanges(node) {
|
|
35
|
+
if (classNames(node).some((name) => name.startsWith('git-'))) {
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
if (!('children' in node) || !Array.isArray(node.children))
|
|
39
|
+
return false;
|
|
40
|
+
return node.children.some((child) => hasGitChanges(child));
|
|
41
|
+
}
|
|
42
|
+
function gitChangedSections(sections, tree) {
|
|
43
|
+
const changed = new Set();
|
|
44
|
+
if (!tree)
|
|
45
|
+
return changed;
|
|
46
|
+
const headingStack = [];
|
|
47
|
+
const rootSection = sections[0] ?? null;
|
|
48
|
+
for (const node of tree.children) {
|
|
49
|
+
if (node.type === 'heading') {
|
|
50
|
+
while (headingStack.length > 0 &&
|
|
51
|
+
headingStack[headingStack.length - 1].depth >= node.depth) {
|
|
52
|
+
headingStack.pop();
|
|
53
|
+
}
|
|
54
|
+
const line = node.position?.start.line;
|
|
55
|
+
const section = line
|
|
56
|
+
? (sections.find((candidate) => candidate.startLine === line && candidate.depth === node.depth) ?? null)
|
|
57
|
+
: null;
|
|
58
|
+
if (section)
|
|
59
|
+
headingStack.push(section);
|
|
60
|
+
if (hasGitChanges(node)) {
|
|
61
|
+
const owner = section ?? headingStack[headingStack.length - 1] ?? rootSection;
|
|
62
|
+
if (owner)
|
|
63
|
+
changed.add(owner.id);
|
|
64
|
+
}
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
if (!hasGitChanges(node))
|
|
68
|
+
continue;
|
|
69
|
+
const line = node.position?.start.line;
|
|
70
|
+
const owner = (line ? sectionAtLine(sections, line) : null) ??
|
|
71
|
+
headingStack[headingStack.length - 1] ??
|
|
72
|
+
rootSection;
|
|
73
|
+
if (owner)
|
|
74
|
+
changed.add(owner.id);
|
|
75
|
+
}
|
|
76
|
+
return changed;
|
|
77
|
+
}
|
|
78
|
+
/** Project rendered headings into the document's local navigation. */
|
|
79
|
+
export function buildViewTableOfContents(sections, tree, options = {}) {
|
|
80
|
+
const flatSections = flattenSections(sections);
|
|
81
|
+
const titles = [];
|
|
82
|
+
visit(tree, 'heading', (node) => {
|
|
83
|
+
titles.push(headingText(node));
|
|
84
|
+
});
|
|
85
|
+
const errorCounts = new Map();
|
|
86
|
+
for (const error of options.errors ?? []) {
|
|
87
|
+
const owner = sectionAtLine(flatSections, error.line);
|
|
88
|
+
if (owner)
|
|
89
|
+
errorCounts.set(owner.id, (errorCounts.get(owner.id) ?? 0) + 1);
|
|
90
|
+
}
|
|
91
|
+
const gitChanges = gitChangedSections(flatSections, options.gitTree);
|
|
92
|
+
return flatSections.flatMap((section, index) => section.githubSlug
|
|
93
|
+
? [
|
|
94
|
+
{
|
|
95
|
+
id: section.githubSlug,
|
|
96
|
+
title: titles[index] || section.heading,
|
|
97
|
+
depth: section.depth,
|
|
98
|
+
errorCount: errorCounts.get(section.id) ?? 0,
|
|
99
|
+
hasGitChanges: gitChanges.has(section.id),
|
|
100
|
+
},
|
|
101
|
+
]
|
|
102
|
+
: []);
|
|
103
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plebeiantech/lat.md",
|
|
3
|
-
"version": "0.12.3-fork.
|
|
3
|
+
"version": "0.12.3-fork.5",
|
|
4
4
|
"description": "A knowledge graph for your codebase, written in markdown",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -32,12 +32,14 @@
|
|
|
32
32
|
"@vitejs/plugin-react": "^5.0.0",
|
|
33
33
|
"@types/mdast": "^4.0.4",
|
|
34
34
|
"@types/node": "^25.3.0",
|
|
35
|
+
"graphology": "^0.26.0",
|
|
35
36
|
"highlight.js": "^11.12.0",
|
|
36
37
|
"mdast-util-from-markdown": "^2.0.3",
|
|
37
38
|
"micromark-util-types": "^2.0.2",
|
|
38
39
|
"prettier": "^3.8.1",
|
|
39
40
|
"react": "^19.0.0",
|
|
40
41
|
"react-dom": "^19.0.0",
|
|
42
|
+
"sigma": "^3.0.3",
|
|
41
43
|
"tsx": "^4.21.0",
|
|
42
44
|
"typescript": "^5.7.0",
|
|
43
45
|
"vite": "^7.0.0",
|
package/templates/AGENTS.md
CHANGED
|
@@ -17,6 +17,8 @@ After EVERY task, before responding to the user:
|
|
|
17
17
|
|
|
18
18
|
This project uses [lat.md](https://www.npmjs.com/package/lat.md) to maintain a structured knowledge graph of its architecture, design decisions, and test specs in the `lat.md/` directory. It is a set of cross-linked markdown files that describe **what** this project does and **why** — the domain concepts, key design decisions, business logic, and test specifications. Use it to ground your work in the actual architecture rather than guessing. Do not treat `lat.md/` as a journal or changelog; it should be a focused snapshot of current or planned state, and should not grow just to note insignificant details.
|
|
19
19
|
|
|
20
|
+
Project-specific lat documentation belongs in `lat.md/`. Do not modify this generated instruction file or a generated `lat-md` `SKILL.md` to record project guidance: both are owned by lat tooling and may be replaced by a later `lat init`.
|
|
21
|
+
|
|
20
22
|
# Commands
|
|
21
23
|
|
|
22
24
|
```bash
|
package/templates/skill/SKILL.md
CHANGED
|
@@ -10,6 +10,8 @@ description: >-
|
|
|
10
10
|
|
|
11
11
|
This skill covers the syntax, structure rules, and conventions for writing `lat.md/` files. Load it whenever you need to create or edit sections in the `lat.md/` directory.
|
|
12
12
|
|
|
13
|
+
Project-specific lat documentation belongs in `lat.md/`. Do not modify this generated skill or a generated `AGENTS.md` to record project guidance: both are owned by lat tooling and may be replaced by a later `lat init`.
|
|
14
|
+
|
|
13
15
|
## What belongs in lat.md
|
|
14
16
|
|
|
15
17
|
`lat.md/` files describe **what** the project does and **why** — domain concepts, key design decisions, business logic, and test specifications. They do NOT duplicate source code. Think of each section as an anchor that source code references back to.
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
:root{color:#25231f;background:#f8f7f3;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;font-synthesis:none;text-rendering:optimizeLegibility;--background: #f8f7f3;--panel: #f0eee8;--panel-border: #dedbd1;--text: #25231f;--muted: #777268;--accent: #7357d9;--accent-soft: #e7e0ff;--danger: #b72f3d;--danger-soft: #f8dfe2;--danger-border: #e7a5ad;--warning: #a87000;--warning-soft: #f5e7b8;--success: #2f7a48;--success-soft: #dceede;--git-notification: #e47f19;--code: #ece9e1;--syntax-built-in: #9a3f75;--syntax-comment: #777268;--syntax-keyword: #6c4bc2;--syntax-number: #a85414;--syntax-string: #397348;--syntax-title: #26649a}@media(prefers-color-scheme:dark){:root{color:#e8e6df;background:#181816;--background: #181816;--panel: #201f1c;--panel-border: #34322d;--text: #e8e6df;--muted: #9c978d;--accent: #ad9aff;--accent-soft: #332b52;--danger: #ff7e8a;--danger-soft: #49262b;--danger-border: #713941;--warning: #e9bd4c;--warning-soft: #43391e;--success: #83d79a;--success-soft: #213d29;--git-notification: #f0a044;--code: #25241f;--syntax-built-in: #e6a2cc;--syntax-comment: #88847c;--syntax-keyword: #c2adff;--syntax-number: #efb271;--syntax-string: #9fd0a5;--syntax-title: #8fc6ef}}*{box-sizing:border-box}body{margin:0;min-width:320px;min-height:100vh;color:var(--text);background:var(--background)}a{color:var(--accent)}.app-shell{display:grid;grid-template-columns:260px minmax(0,1fr);min-height:100vh}.sidebar{position:sticky;top:0;height:100vh;padding:28px 18px;overflow-y:auto;background:var(--panel);border-right:1px solid var(--panel-border)}.sidebar-header{display:flex;margin:0 10px 34px;align-items:center;justify-content:space-between}.brand{display:inline-block;color:var(--text);font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:21px;font-weight:750;letter-spacing:-.04em;text-decoration:none}.brand span{color:var(--accent)}.sidebar-actions{display:flex;align-items:center;gap:3px}.sidebar-search,.sidebar-git{display:inline-flex;width:28px;height:28px;align-items:center;justify-content:center;color:var(--muted);cursor:pointer;background:transparent;border:0;border-radius:6px}.sidebar-search:hover,.sidebar-search[aria-current=page],.sidebar-git:hover{color:var(--accent);background:var(--accent-soft)}.sidebar-git[aria-pressed=true]{color:var(--warning);background:var(--warning-soft)}.sidebar-git{position:relative}.sidebar-git[data-has-changes=true]:after{position:absolute;top:3px;right:3px;width:6px;height:6px;content:"";background:var(--git-notification);border-radius:50%;box-shadow:0 0 0 1.5px var(--panel)}.sidebar-search svg,.sidebar-git svg{width:17px;height:17px;fill:none;stroke:currentColor;stroke-linecap:round;stroke-width:1.8}.file-tree{display:grid;min-width:0;gap:2px}.tree-directory>summary{display:flex;padding:7px 10px;align-items:center;gap:7px;color:var(--muted);font-size:12px;font-weight:650;cursor:pointer;list-style:none;border-radius:7px}.tree-directory>summary::-webkit-details-marker{display:none}.tree-directory>summary>a,.tree-directory>summary>span{min-width:0;flex:1;color:inherit;text-decoration:none}.tree-directory>summary>a,.tree-directory>summary>span{display:flex;align-items:center;gap:8px}.tree-directory>summary:before{content:"›";display:inline-block;width:10px;font-size:15px;line-height:1;transform-origin:center;transition:transform .12s ease}.tree-directory[open]>summary:before{transform:rotate(90deg)}.tree-directory>summary:hover{color:var(--text);background:color-mix(in srgb,var(--accent-soft) 55%,transparent)}.tree-children{display:grid;margin-left:15px;padding-left:7px;gap:2px;border-left:1px solid var(--panel-border)}.tree-directory[open]>.tree-children{margin-top:2px}.document-link{display:flex;padding:7px 10px;align-items:center;gap:8px;overflow:hidden;color:var(--muted);font-size:13px;line-height:1.35;text-decoration:none;text-overflow:ellipsis;white-space:nowrap;border-radius:7px}.document-link-name{min-width:0;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.document-state-disc{width:7px;height:7px;flex:0 0 auto;border-radius:50%}.document-state-disc.has-errors{background:var(--danger);box-shadow:0 0 0 2px color-mix(in srgb,var(--danger) 12%,transparent)}.document-state-disc.git-modified{background:var(--warning);box-shadow:0 0 0 2px color-mix(in srgb,var(--warning) 12%,transparent)}.document-state-disc.git-new{background:var(--success);box-shadow:0 0 0 2px color-mix(in srgb,var(--success) 12%,transparent)}.document-state-disc.has-errors.git-modified{background:linear-gradient(90deg,var(--danger) 0 50%,var(--warning) 50% 100%)}.document-state-disc.has-errors.git-new{background:linear-gradient(90deg,var(--danger) 0 50%,var(--success) 50% 100%)}.document-link:hover,.document-link.active{color:var(--text);background:var(--accent-soft)}.document-link.active{font-weight:650}.main{width:100%;max-width:980px;min-width:0;padding:46px clamp(28px,6vw,82px) 100px}.main.restoring-history-scroll{visibility:hidden}.document-header{max-width:760px;margin-bottom:34px}.document-metadata{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.document-path{color:var(--muted);font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:12px}.document-flag{padding:3px 8px;color:var(--accent);font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:10px;font-weight:700;letter-spacing:.025em;background:var(--accent-soft);border:1px solid color-mix(in srgb,var(--accent) 25%,transparent);border-radius:999px}.document-error-toggle{display:inline-flex;padding:3px 8px;align-items:center;color:var(--danger);font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:10px;font-weight:700;letter-spacing:.025em;cursor:pointer;background:var(--danger-soft);border:1px solid var(--danger-border);border-radius:999px}.document-error-toggle:hover,.document-error-toggle[aria-expanded=true]{color:var(--text)}.document-error-panel{margin-top:12px;overflow:hidden;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;background:color-mix(in srgb,var(--danger-soft) 55%,var(--background));border:1px solid var(--danger-border);border-radius:8px}.document-error-header{padding:9px 13px 7px;color:var(--danger);font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:10px;font-weight:750;letter-spacing:.08em;text-transform:uppercase}.document-error-item{display:grid;padding:10px 13px 11px;gap:5px;color:inherit;text-decoration:none}.document-error-item+.document-error-item{border-top:1px solid var(--danger-border)}.document-error-item:hover{background:color-mix(in srgb,var(--danger-soft) 75%,transparent)}.document-error-location{color:var(--danger);font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:11px;font-weight:700}.document-error-message{color:var(--muted);font-size:12px;line-height:1.45;white-space:pre-wrap}.source-context{position:relative;z-index:1;width:calc(100% - 5.25rem);max-width:760px;margin:12px 16px 12px 4.25rem;overflow:visible;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;background:color-mix(in srgb,var(--code) 72%,transparent);border:1px solid var(--panel-border);border-radius:8px}.source-context.has-references:not(.references-open){margin-bottom:24px}.source-context-header{display:flex;min-height:36px;padding:7px 10px 7px 13px;align-items:center;gap:12px;color:var(--muted);font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:10px;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.source-reference-toggle{position:relative;z-index:1;display:flex;height:0;align-items:center;gap:10px}.source-reference-toggle:before,.source-reference-toggle:after{height:1px;flex:1;content:"";background:var(--panel-border);visibility:hidden}.source-context.references-open .source-reference-toggle:before,.source-context.references-open .source-reference-toggle:after{visibility:visible}.source-reference-toggle button{display:inline-flex;padding:4px 7px;align-items:center;gap:6px;color:var(--accent);font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:10px;font-weight:650;line-height:1.3;letter-spacing:.02em;text-transform:none;cursor:pointer;background:var(--accent-soft);border:0;border-radius:5px}.source-reference-toggle button:hover{color:var(--text)}.source-reference-toggle button>span{min-width:16px;padding:1px 4px;color:var(--muted);text-align:center;background:color-mix(in srgb,var(--background) 65%,transparent);border-radius:999px}.source-context-current{padding:0 13px 13px}.source-context-current>a,.source-reference-item>a{color:var(--text);text-decoration:none}.source-breadcrumbs{display:flex;align-items:center;gap:5px;overflow:hidden;color:var(--accent);font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:12px;font-weight:650;white-space:nowrap}.source-breadcrumbs>span{display:inline-flex;min-width:0;align-items:center;gap:5px}.source-breadcrumbs>span>span{color:var(--muted)}.source-context-current>a:hover .source-breadcrumbs,.source-reference-item>a:hover .source-breadcrumbs{text-decoration:underline;text-underline-offset:3px}.source-context-paragraph p{margin:8px 0 0;color:var(--text);font-size:13px;line-height:1.5}.source-context-paragraph a,.source-reference-paragraph a{text-underline-offset:2px}.source-reference-list{padding:17px 0 3px}.source-reference-item{padding:10px 13px}.source-reference-item+.source-reference-item{border-top:1px solid var(--panel-border)}.source-reference-paragraph p{margin:5px 0 0;color:var(--muted);font-size:12px;line-height:1.4}.source-code{max-width:100%;padding:10px 0;overflow-x:auto;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:13px;line-height:1.55;background:var(--code);border:1px solid var(--panel-border);border-radius:10px;overflow-anchor:none}.source-collapse{display:flex;min-width:max-content;min-height:34px;align-items:center;color:var(--muted);background:color-mix(in srgb,var(--panel) 52%,transparent);border-top:1px solid color-mix(in srgb,var(--panel-border) 72%,transparent);border-bottom:1px solid color-mix(in srgb,var(--panel-border) 72%,transparent)}.source-collapse:first-child{margin-top:-10px;border-top:0;border-radius:10px 10px 0 0}.source-collapse:last-child{margin-bottom:-10px;border-bottom:0;border-radius:0 0 10px 10px}.source-collapse>span{flex:0 0 4.25rem;padding-right:14px;text-align:right;-webkit-user-select:none;user-select:none}.source-collapse button{padding:6px 20px 6px 0;color:var(--muted);font:inherit;font-size:11px;cursor:pointer;background:transparent;border:0}.source-collapse button:hover{color:var(--accent)}.source-line{display:flex;min-width:max-content}.source-line.focused{background:color-mix(in srgb,var(--accent-soft) 72%,transparent)}.source-line-number{flex:0 0 4.25rem;padding-right:14px;color:var(--muted);text-align:right;-webkit-user-select:none;user-select:none}.source-line.focused .source-line-number{color:var(--accent)}.source-line-content{flex:1 0 auto;padding-right:20px;color:var(--text);white-space:pre}.source-code .hljs-comment,.source-code .hljs-quote{color:var(--syntax-comment);font-style:italic}.source-code .hljs-keyword,.source-code .hljs-literal,.source-code .hljs-selector-tag,.source-code .hljs-section{color:var(--syntax-keyword)}.source-code .hljs-string,.source-code .hljs-regexp,.source-code .hljs-addition,.source-code .hljs-attribute{color:var(--syntax-string)}.source-code .hljs-number,.source-code .hljs-symbol,.source-code .hljs-bullet{color:var(--syntax-number)}.source-code .hljs-title,.source-code .hljs-function,.source-code .hljs-class,.source-code .hljs-type{color:var(--syntax-title)}.source-code .hljs-built_in,.source-code .hljs-variable.language_,.source-code .hljs-meta{color:var(--syntax-built-in)}.source-code .hljs-deletion{color:#bd3d3d}.source-code .hljs-emphasis{font-style:italic}.source-code .hljs-strong{font-weight:700}.markdown{max-width:760px;font-size:16px;line-height:1.72}.markdown h1,.markdown h2,.markdown h3,.markdown h4{margin:2em 0 .65em;line-height:1.2;letter-spacing:-.025em;scroll-margin-top:24px}.section-back-reference-toggle{display:inline-flex;padding:3px 5px 3px 7px;margin-left:10px;align-items:center;gap:5px;color:var(--accent);font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:10px;font-weight:650;line-height:1.3;letter-spacing:.01em;vertical-align:middle;cursor:pointer;background:var(--accent-soft);border:0;border-radius:5px}.section-back-reference-toggle:hover,.section-back-reference-toggle[aria-expanded=true]{color:var(--text)}.section-back-reference-count{min-width:16px;padding:1px 4px;color:var(--muted);text-align:center;background:color-mix(in srgb,var(--background) 65%,transparent);border-radius:999px}.section-back-reference-panel{margin:-.15em 0 1.5em;overflow:hidden;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;background:color-mix(in srgb,var(--code) 72%,transparent);border:1px solid var(--panel-border);border-radius:8px}.section-back-reference-panel[hidden]{display:none}.section-back-reference-header{padding:9px 13px 7px;color:var(--muted);font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:10px;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.section-back-reference-item{padding:10px 13px 11px}.section-back-reference-item+.section-back-reference-item{border-top:1px solid var(--panel-border)}.section-back-reference-location{display:inline-flex;align-items:center;gap:5px;color:var(--accent);font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:12px;font-weight:650;line-height:1.35;text-decoration:none}.section-back-reference-location:hover{text-decoration:underline;text-underline-offset:3px}.section-back-reference-location>span[aria-hidden=true]{color:var(--muted)}.section-back-reference-paragraph p{margin:5px 0 0;color:var(--muted);font-size:12px;line-height:1.45}.section-back-reference-paragraph a{text-underline-offset:2px}.section-back-reference-code .code-link-language{margin-right:0;font-size:7px}.markdown .section-back-reference-code>code{display:block;padding:7px 9px;margin-top:7px;overflow-x:auto;color:var(--muted);font-size:11px;line-height:1.4;white-space:pre;border:1px solid color-mix(in srgb,var(--panel-border) 75%,transparent)}.markdown h1{margin-top:0;font-size:clamp(32px,5vw,46px)}.markdown h2{padding-top:.4em;font-size:26px;border-top:1px solid var(--panel-border)}.markdown h3{font-size:20px}.markdown p,.markdown ul,.markdown ol,.markdown blockquote,.markdown pre{margin:0 0 1.25em}.markdown li+li{margin-top:.35em}.markdown a{text-decoration-thickness:1px;text-underline-offset:3px}.markdown .markdown-error{color:var(--danger);scroll-margin-top:24px;text-decoration-color:currentColor}.markdown p.markdown-error,.markdown li.markdown-error,.markdown blockquote.markdown-error,.markdown pre.markdown-error,.markdown h1.markdown-error,.markdown h2.markdown-error,.markdown h3.markdown-error,.markdown h4.markdown-error,.markdown h5.markdown-error,.markdown h6.markdown-error{padding-left:10px;background:color-mix(in srgb,var(--danger-soft) 42%,transparent);box-shadow:inset 3px 0 0 var(--danger)}.markdown a.markdown-error,.markdown span.markdown-error{padding:.08em .2em;margin:-.08em -.2em;background:var(--danger-soft);border-radius:3px;box-decoration-break:clone;-webkit-box-decoration-break:clone}.markdown ins.git-added,.markdown del.git-removed{padding:.08em .12em;margin:-.08em -.04em;border-radius:3px;box-decoration-break:clone;-webkit-box-decoration-break:clone}.markdown ins.git-added{color:var(--success);text-decoration:none;background:var(--success-soft)}.markdown del.git-removed{color:var(--danger);background:var(--danger-soft);text-decoration-thickness:1px}.markdown p.git-added,.markdown blockquote.git-added,.markdown pre.git-added,.markdown h1.git-added,.markdown h2.git-added,.markdown h3.git-added,.markdown h4.git-added,.markdown h5.git-added,.markdown h6.git-added,.markdown li.git-added{padding-left:10px;color:var(--success);background:color-mix(in srgb,var(--success-soft) 55%,transparent);box-shadow:inset 3px 0 0 var(--success)}.markdown p.git-removed,.markdown div.git-removed,.markdown blockquote.git-removed,.markdown pre.git-removed,.markdown li.git-removed{padding-left:10px;color:var(--danger);text-decoration:line-through;background:color-mix(in srgb,var(--danger-soft) 55%,transparent);box-shadow:inset 3px 0 0 var(--danger)}.wiki-link-context{color:var(--muted)}a.wiki-link-segmented{text-decoration:none}a.wiki-link-segmented>.wiki-link-context,a.wiki-link-segmented>.wiki-link-leaf{text-decoration-line:underline;text-decoration-color:currentColor;text-decoration-thickness:1px;text-underline-offset:3px}.code-link-language{display:inline-flex;width:1.55em;height:1.55em;margin-right:.38em;align-items:center;justify-content:center;color:#fff;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:.62em;font-style:normal;font-weight:800;line-height:1;text-decoration:none;vertical-align:.12em;background:var(--muted);border-radius:3px}.code-language-ts{background:#3178c6}.code-language-js{color:#25231f;background:#f0db4f}.code-language-py{background:#3776ab}.code-language-rs{background:#9a4b2f}.code-language-go{color:#15343b;background:#6ad7e5}.code-language-c{background:#5968ba}a.wiki-link-active{padding:.12em .28em;margin:-.12em -.28em;background:var(--accent-soft);border-radius:4px;box-decoration-break:clone;-webkit-box-decoration-break:clone}.markdown blockquote{margin-left:0;padding-left:18px;color:var(--muted);border-left:3px solid var(--panel-border)}.markdown code{padding:.16em .36em;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:.9em;background:var(--code);border-radius:5px}.markdown pre{padding:18px 20px;overflow-x:auto;background:var(--code);border:1px solid var(--panel-border);border-radius:10px}.markdown pre code{padding:0;background:transparent}.markdown img{max-width:100%}.markdown hr{margin:2.5em 0;border:0;border-top:1px solid var(--panel-border)}.visually-hidden{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;white-space:nowrap;border:0;clip:rect(0,0,0,0)}.search-page{width:100%;max-width:760px}.search-input-shell{display:flex;height:52px;padding:0 16px;align-items:center;gap:11px;color:var(--muted);background:var(--panel);border:1px solid var(--panel-border);border-radius:10px;box-shadow:0 1px 1px color-mix(in srgb,var(--text) 5%,transparent)}.search-input-shell:focus-within{color:var(--accent);border-color:color-mix(in srgb,var(--accent) 55%,var(--panel-border));box-shadow:0 0 0 3px color-mix(in srgb,var(--accent) 12%,transparent)}.search-input-shell svg{width:20px;height:20px;flex:0 0 auto;fill:none;stroke:currentColor;stroke-linecap:round;stroke-width:1.8}.search-input-shell input{width:100%;min-width:0;padding:0;color:var(--text);font:inherit;font-size:17px;line-height:1;background:transparent;border:0;outline:0}.search-input-shell input::placeholder{color:var(--muted)}.search-input-shell input::-webkit-search-cancel-button{cursor:pointer}.search-status{min-height:42px;padding:12px 2px 8px;color:var(--muted);font-size:12px}.search-results{display:grid;gap:10px}.search-result{display:block;padding:16px 18px;color:var(--text);text-decoration:none;background:color-mix(in srgb,var(--panel) 72%,transparent);border:1px solid var(--panel-border);border-radius:9px;transition:border-color .12s ease,background .12s ease,transform .12s ease}.search-result:hover{background:var(--panel);border-color:color-mix(in srgb,var(--accent) 38%,var(--panel-border));transform:translateY(-1px)}.search-result-breadcrumbs{display:flex;align-items:center;gap:5px;overflow:hidden;color:var(--accent);font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:11px;font-weight:650;white-space:nowrap}.search-result-breadcrumbs>span{display:inline-flex;min-width:0;align-items:center;gap:5px}.search-result-breadcrumbs>span>span{color:var(--muted)}.search-result h2{margin:7px 0 0;font-size:18px;line-height:1.3;letter-spacing:-.015em}.search-result p{margin:6px 0 0;color:var(--muted);font-size:13px;line-height:1.5}.state{display:flex;min-height:50vh;align-items:center;justify-content:center;color:var(--muted)}.state.error{flex-direction:column;gap:8px;text-align:center}.state.error strong{color:var(--text)}@media(max-width:760px){.app-shell{display:block}.sidebar{position:static;width:100%;height:auto;padding:18px;border-right:0;border-bottom:1px solid var(--panel-border)}.sidebar-header{margin-bottom:18px}.sidebar nav{display:none}.main{padding-top:28px}.source-context{width:calc(100% - 24px);margin-right:12px;margin-left:12px}}
|