@reqlan/analytical 0.5.1 → 0.6.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 +37 -2
- package/out/analysis/git-dates-analyser.js +41 -12
- package/out/analysis/git-dates-analyser.js.map +1 -1
- package/out/core/analytical-store.d.ts +9 -2
- package/out/core/analytical-store.js +7 -5
- package/out/core/analytical-store.js.map +1 -1
- package/out/core/application-memory.d.ts +17 -2
- package/out/core/application-memory.js +23 -5
- package/out/core/application-memory.js.map +1 -1
- package/out/core/base-discovery.d.ts +40 -0
- package/out/core/base-discovery.js +166 -0
- package/out/core/base-discovery.js.map +1 -0
- package/out/core/context-model.d.ts +17 -0
- package/out/core/context-model.js.map +1 -1
- package/out/core/create-base.d.ts +11 -0
- package/out/core/create-base.js +33 -0
- package/out/core/create-base.js.map +1 -0
- package/out/core/rqignore.d.ts +25 -0
- package/out/core/rqignore.js +149 -0
- package/out/core/rqignore.js.map +1 -0
- package/out/core/types.d.ts +5 -1
- package/out/core/types.js.map +1 -1
- package/out/create-runtime.d.ts +29 -4
- package/out/create-runtime.js +47 -8
- package/out/create-runtime.js.map +1 -1
- package/out/export/html-export-assets.d.ts +2 -2
- package/out/export/html-export-assets.js +259 -239
- package/out/export/html-export-assets.js.map +1 -1
- package/out/graph/physics-core.d.ts +141 -0
- package/out/graph/physics-core.js +237 -0
- package/out/graph/physics-core.js.map +1 -0
- package/out/headless-index-service.d.ts +6 -23
- package/out/headless-index-service.js +5 -171
- package/out/headless-index-service.js.map +1 -1
- package/out/index-store/base-registry.d.ts +57 -0
- package/out/index-store/base-registry.js +192 -0
- package/out/index-store/base-registry.js.map +1 -0
- package/out/index-store/index-diagnostics-store.d.ts +72 -0
- package/out/index-store/index-diagnostics-store.js +238 -0
- package/out/index-store/index-diagnostics-store.js.map +1 -0
- package/out/index-store/index-file-error.d.ts +16 -0
- package/out/index-store/index-file-error.js +27 -0
- package/out/index-store/index-file-error.js.map +1 -0
- package/out/index-store/index-parse-issues.d.ts +9 -0
- package/out/index-store/index-parse-issues.js +62 -0
- package/out/index-store/index-parse-issues.js.map +1 -0
- package/out/index-store/index-status.d.ts +20 -0
- package/out/index-store/index-status.js +2 -0
- package/out/index-store/index-status.js.map +1 -0
- package/out/index-store/overview-coverage.d.ts +23 -0
- package/out/index-store/overview-coverage.js +254 -0
- package/out/index-store/overview-coverage.js.map +1 -0
- package/out/index-store/schema.d.ts +1 -1
- package/out/index-store/schema.js +4 -1
- package/out/index-store/schema.js.map +1 -1
- package/out/index-store/sqlite-store.d.ts +31 -2
- package/out/index-store/sqlite-store.js +152 -13
- package/out/index-store/sqlite-store.js.map +1 -1
- package/out/index-store/webview-table-queries.d.ts +65 -1
- package/out/index-store/webview-table-queries.js +188 -26
- package/out/index-store/webview-table-queries.js.map +1 -1
- package/out/index-store/workspace-index-file.d.ts +27 -0
- package/out/index-store/workspace-index-file.js +98 -0
- package/out/index-store/workspace-index-file.js.map +1 -0
- package/out/index-store/workspace-index-sync.d.ts +53 -0
- package/out/index-store/workspace-index-sync.js +164 -0
- package/out/index-store/workspace-index-sync.js.map +1 -0
- package/out/index-store/workspace-index.d.ts +80 -0
- package/out/index-store/workspace-index.js +525 -0
- package/out/index-store/workspace-index.js.map +1 -0
- package/out/index-store/workspace-mtime.d.ts +14 -0
- package/out/index-store/workspace-mtime.js +56 -0
- package/out/index-store/workspace-mtime.js.map +1 -0
- package/out/index.d.ts +25 -6
- package/out/index.js +12 -3
- package/out/index.js.map +1 -1
- package/package.json +7 -2
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base-local `.reqlan/.rqignore` — gitignore-syntax path filters for discovery and indexing.
|
|
3
|
+
*
|
|
4
|
+
* rq:["../../../reqlan rq/extension/configuration.rq".configuration_rqignore]
|
|
5
|
+
* rq:["../../../reqlan rq/extension/module/index.rq".rqignore]
|
|
6
|
+
*/
|
|
7
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
8
|
+
import { join, relative, resolve, sep } from 'node:path';
|
|
9
|
+
import ignore from 'ignore';
|
|
10
|
+
import { APPLICATION_MEMORY_DIR, RQIGNORE_FILENAME } from './application-memory.js';
|
|
11
|
+
/**
|
|
12
|
+
* Built-in patterns always applied (also seeded into new `.rqignore` files).
|
|
13
|
+
* Users can un-ignore with `!pattern` in `.reqlan/.rqignore`.
|
|
14
|
+
*/
|
|
15
|
+
export const DEFAULT_RQIGNORE_PATTERNS = [
|
|
16
|
+
// Dependencies
|
|
17
|
+
'node_modules/',
|
|
18
|
+
'bower_components/',
|
|
19
|
+
'vendor/',
|
|
20
|
+
// Python / virtualenvs
|
|
21
|
+
'venv/',
|
|
22
|
+
'.venv/',
|
|
23
|
+
'env/',
|
|
24
|
+
'.env/',
|
|
25
|
+
'__pycache__/',
|
|
26
|
+
'*.pyc',
|
|
27
|
+
'.pytest_cache/',
|
|
28
|
+
'.mypy_cache/',
|
|
29
|
+
'.tox/',
|
|
30
|
+
'.ruff_cache/',
|
|
31
|
+
// Build / tooling output
|
|
32
|
+
'dist/',
|
|
33
|
+
'out/',
|
|
34
|
+
'build/',
|
|
35
|
+
'coverage/',
|
|
36
|
+
'.next/',
|
|
37
|
+
'.nuxt/',
|
|
38
|
+
'.turbo/',
|
|
39
|
+
'.cache/',
|
|
40
|
+
'*.tsbuildinfo',
|
|
41
|
+
// VCS / editor / package stores
|
|
42
|
+
'.git/',
|
|
43
|
+
'.svn/',
|
|
44
|
+
'.hg/',
|
|
45
|
+
'.cursor/',
|
|
46
|
+
'.pnpm-store/',
|
|
47
|
+
'.vscode/',
|
|
48
|
+
'.idea/',
|
|
49
|
+
// Hidden entries (preserves prior walk behaviour; un-ignore with !path)
|
|
50
|
+
'.*',
|
|
51
|
+
'.*/',
|
|
52
|
+
// Reqlan application memory (never crawl the marker dir for .rq sources)
|
|
53
|
+
`${APPLICATION_MEMORY_DIR}/`,
|
|
54
|
+
// Databases / binary stores
|
|
55
|
+
'*.db3',
|
|
56
|
+
'*.sqlite',
|
|
57
|
+
'*.sqlite3',
|
|
58
|
+
'*.db',
|
|
59
|
+
// Secrets / logs / temp
|
|
60
|
+
'*.secret.rq',
|
|
61
|
+
'*.log',
|
|
62
|
+
'tmp/',
|
|
63
|
+
'temp/',
|
|
64
|
+
];
|
|
65
|
+
/** Text written when seeding a new base's `.reqlan/.rqignore`. */
|
|
66
|
+
export function defaultRqIgnoreFileContents() {
|
|
67
|
+
return [
|
|
68
|
+
'# Reqlan path ignore (gitignore syntax).',
|
|
69
|
+
'# Applied relative to the base root (parent of .reqlan).',
|
|
70
|
+
'# Built-in defaults always apply; use !pattern to force-include.',
|
|
71
|
+
'',
|
|
72
|
+
...DEFAULT_RQIGNORE_PATTERNS,
|
|
73
|
+
'',
|
|
74
|
+
].join('\n');
|
|
75
|
+
}
|
|
76
|
+
function toPosixRelative(baseRoot, absPath) {
|
|
77
|
+
const rel = relative(resolve(baseRoot), resolve(absPath));
|
|
78
|
+
if (!rel || rel === '.') {
|
|
79
|
+
return '';
|
|
80
|
+
}
|
|
81
|
+
if (rel.startsWith('..')) {
|
|
82
|
+
// Outside base — treat as ignored for safety.
|
|
83
|
+
return '../';
|
|
84
|
+
}
|
|
85
|
+
return rel.split(sep).join('/');
|
|
86
|
+
}
|
|
87
|
+
function createFilter(ig) {
|
|
88
|
+
return {
|
|
89
|
+
ignores(relativePath, isDirectory = false) {
|
|
90
|
+
const normalized = relativePath.replace(/\\/g, '/').replace(/^\/+/, '');
|
|
91
|
+
if (!normalized || normalized === '.') {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
if (normalized.startsWith('../')) {
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
const candidate = isDirectory && !normalized.endsWith('/')
|
|
98
|
+
? `${normalized}/`
|
|
99
|
+
: normalized;
|
|
100
|
+
return ig.ignores(candidate);
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Build a filter from built-in defaults plus optional extra pattern lines
|
|
106
|
+
* (e.g. contents of `.reqlan/.rqignore`).
|
|
107
|
+
*/
|
|
108
|
+
export function createRqIgnoreFilter(extraPatterns) {
|
|
109
|
+
const ig = ignore();
|
|
110
|
+
ig.add([...DEFAULT_RQIGNORE_PATTERNS]);
|
|
111
|
+
if (typeof extraPatterns === 'string') {
|
|
112
|
+
if (extraPatterns.trim()) {
|
|
113
|
+
ig.add(extraPatterns);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
else if (extraPatterns?.length) {
|
|
117
|
+
ig.add([...extraPatterns]);
|
|
118
|
+
}
|
|
119
|
+
return createFilter(ig);
|
|
120
|
+
}
|
|
121
|
+
/** Absolute path to `<baseRoot>/.reqlan/.rqignore`. */
|
|
122
|
+
export function resolveRqIgnorePath(baseRoot) {
|
|
123
|
+
return join(resolve(baseRoot), APPLICATION_MEMORY_DIR, RQIGNORE_FILENAME);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Load `.reqlan/.rqignore` for a base when present; always includes built-in defaults.
|
|
127
|
+
* Missing or unreadable files still yield the default filter.
|
|
128
|
+
*/
|
|
129
|
+
export function loadRqIgnore(baseRoot) {
|
|
130
|
+
const path = resolveRqIgnorePath(baseRoot);
|
|
131
|
+
if (!existsSync(path)) {
|
|
132
|
+
return createRqIgnoreFilter();
|
|
133
|
+
}
|
|
134
|
+
try {
|
|
135
|
+
return createRqIgnoreFilter(readFileSync(path, 'utf8'));
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
return createRqIgnoreFilter();
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/** Whether an absolute path under `baseRoot` is ignored. */
|
|
142
|
+
export function isIgnoredPath(filter, baseRoot, absPath, isDirectory) {
|
|
143
|
+
const rel = toPosixRelative(baseRoot, absPath);
|
|
144
|
+
if (!rel) {
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
return filter.ignores(rel, isDirectory);
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=rqignore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rqignore.js","sourceRoot":"","sources":["../../src/core/rqignore.ts"],"names":[],"mappings":"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"}
|
package/out/core/types.d.ts
CHANGED
|
@@ -80,7 +80,7 @@ export interface OutlineNode {
|
|
|
80
80
|
}
|
|
81
81
|
export type { ActivityBarScope, CurrentFileSlice, ReqlanContextModel } from './context-model.js';
|
|
82
82
|
export { CONTEXT_DIMENSION_LABELS, CONTEXT_DIMENSION_WEIGHTS, DEFAULT_ENABLED_DIMENSIONS } from './context-model.js';
|
|
83
|
-
export type { ContextAnomaly, ContextDimensionContribution, ContextDimensionId, ContextFileEntry, ContextFocus, ContextFocusKind, ContextFootprint, ContextIdeaEntry, ContextProvenance, ContextReferencesSlice, GitAuthorRollup, GitContextSlice, GitFocusCommit, GitFocusStats, GitPeerChangeRate, WorkspaceContextSlice } from './context-model.js';
|
|
83
|
+
export type { ContextAnomaly, ContextDimensionContribution, ContextDimensionId, ContextFileEntry, ContextFocus, ContextFocusKind, ContextFootprint, ContextIdeaEntry, ContextProvenance, ContextReferencesSlice, GitAuthorRollup, GitContextSlice, GitFocusCommit, GitFocusStats, GitPeerChangeRate, WorkspaceBaseGlance, WorkspaceContextSlice } from './context-model.js';
|
|
84
84
|
export declare const ACTIVITY_BAR_MAX_NODES = 40;
|
|
85
85
|
export declare const BLOCKING_STATUSES: Set<string>;
|
|
86
86
|
export interface GraphSlice {
|
|
@@ -116,6 +116,8 @@ export interface IdeaTableRow {
|
|
|
116
116
|
id: string;
|
|
117
117
|
title: string;
|
|
118
118
|
path: string;
|
|
119
|
+
/** Idea kind for grouping (block / oneliner); never ideaset in this table. */
|
|
120
|
+
kind: 'block' | 'oneliner';
|
|
119
121
|
mainAttribute?: string;
|
|
120
122
|
otherAttributes: string;
|
|
121
123
|
otherAttributeItems: string[];
|
|
@@ -156,6 +158,8 @@ export interface ReferenceTableRow {
|
|
|
156
158
|
referenceType: ReferenceViewType;
|
|
157
159
|
sourceFileUri: string;
|
|
158
160
|
sourceLineStart: number;
|
|
161
|
+
/** Resolved path used to open the target (file refs and idea targets). */
|
|
162
|
+
targetFileUri?: string;
|
|
159
163
|
}
|
|
160
164
|
export interface FileRelatedRequirements {
|
|
161
165
|
fileUri: string;
|
package/out/core/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAgGH,OAAO,EACH,wBAAwB,EACxB,yBAAyB,EACzB,0BAA0B,EAC7B,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAgGH,OAAO,EACH,wBAAwB,EACxB,yBAAyB,EACzB,0BAA0B,EAC7B,MAAM,oBAAoB,CAAC;AAqB5B,MAAM,CAAC,MAAM,sBAAsB,GAAG,EAAE,CAAC;AAEzC,MAAM,CAAC,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC;IACrC,SAAS;IACT,MAAM;IACN,MAAM;IACN,SAAS;IACT,MAAM;IACN,aAAa;CAChB,CAAC,CAAC;AAmGH,MAAM,UAAU,MAAM,CAAC,OAAe,EAAE,IAAY;IAChD,OAAO,GAAG,OAAO,IAAI,IAAI,EAAE,CAAC;AAChC,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,QAAgB,EAAE,IAAc,EAAE,MAAc;IACnE,OAAO,GAAG,QAAQ,KAAK,IAAI,IAAI,MAAM,EAAE,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,IAAY;IACxC,IAAI,CAAC;QACD,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAqB,CAAC;IAChD,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,EAAE,CAAC;IACd,CAAC;AACL,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,UAA4B;IACjD,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC;IAC7B,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QACtB,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC5B,CAAC;IACD,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC3B,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAChD,CAAC;IACD,OAAO,EAAE,CAAC;AACd,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,UAA4B;IACnD,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC;IACjC,OAAO,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;AAC3D,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,UAA4B;IACrD,IAAI,UAAU,CAAC,UAAU,KAAK,IAAI,EAAE,CAAC;QACjC,OAAO,IAAI,CAAC;IAChB,CAAC;IACD,OAAO,QAAQ,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,WAAW,EAAE,KAAK,YAAY,CAAC,CAAC;AAChF,CAAC"}
|
package/out/create-runtime.d.ts
CHANGED
|
@@ -1,22 +1,47 @@
|
|
|
1
1
|
import { type AnalyticalStore } from './core/analytical-store.js';
|
|
2
|
+
import { type BaseDescriptor } from './core/base-discovery.js';
|
|
2
3
|
import { AnalyserRegistry } from './analysis/analyser-registry.js';
|
|
3
|
-
import {
|
|
4
|
+
import { WorkspaceIndex } from './index-store/workspace-index.js';
|
|
4
5
|
export interface AnalysisRuntimeOptions {
|
|
6
|
+
/**
|
|
7
|
+
* Search root for base discovery (VS Code workspace folder / CLI resolved root).
|
|
8
|
+
* Bases are directories under this root that own `.reqlan`.
|
|
9
|
+
*/
|
|
5
10
|
workspaceRoot: string;
|
|
6
|
-
/** Override application-memory directory; default is `<
|
|
11
|
+
/** Override application-memory directory for the selected base; default is `<base>/.reqlan`. */
|
|
7
12
|
storagePath?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Prefer this cwd when choosing among multiple bases (nearest containing base).
|
|
15
|
+
* Defaults to `workspaceRoot`.
|
|
16
|
+
*/
|
|
17
|
+
cwd?: string;
|
|
18
|
+
/**
|
|
19
|
+
* When set, skip discovery and use this absolute path as the single base root
|
|
20
|
+
* (must own `.reqlan`, or `storagePath` override).
|
|
21
|
+
*/
|
|
22
|
+
baseRoot?: string;
|
|
8
23
|
}
|
|
9
24
|
export interface AnalysisRuntime {
|
|
10
25
|
store: AnalyticalStore;
|
|
11
|
-
index:
|
|
26
|
+
index: WorkspaceIndex;
|
|
12
27
|
analysers: AnalyserRegistry;
|
|
28
|
+
/** Selected base root (may differ from discovery search root). */
|
|
13
29
|
workspaceRoot: string;
|
|
30
|
+
/** All bases discovered under the search root (empty when forced via storagePath alone). */
|
|
31
|
+
bases: BaseDescriptor[];
|
|
32
|
+
/** Selected base descriptor when discovery found one. */
|
|
33
|
+
base?: BaseDescriptor;
|
|
14
34
|
makeContext: () => {
|
|
15
|
-
store:
|
|
35
|
+
store: WorkspaceIndex['indexStore'];
|
|
16
36
|
analytical: AnalyticalStore;
|
|
17
37
|
workspaceRoot: string;
|
|
18
38
|
};
|
|
19
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Create a headless analysis runtime bound to one base.
|
|
42
|
+
* Discovers `.reqlan`-marked bases under `workspaceRoot` and selects the nearest to `cwd`.
|
|
43
|
+
* Throws when no base exists and no `storagePath` / `baseRoot` override is provided.
|
|
44
|
+
*/
|
|
20
45
|
export declare function createAnalysisRuntime(options: AnalysisRuntimeOptions): AnalysisRuntime;
|
|
21
46
|
export declare function activateAnalysisRuntime(runtime: AnalysisRuntime): Promise<void>;
|
|
22
47
|
export declare function deactivateAnalysisRuntime(runtime: AnalysisRuntime): Promise<void>;
|
package/out/create-runtime.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createAnalyticalStore } from './core/analytical-store.js';
|
|
2
2
|
import { resolveApplicationMemoryPath } from './core/application-memory.js';
|
|
3
|
+
import { discoverBases, selectDefaultBase } from './core/base-discovery.js';
|
|
3
4
|
import { AnalyserRegistry } from './analysis/analyser-registry.js';
|
|
4
5
|
import { listAllIdeasAnalyser } from './analysis/list-ideas-analyser.js';
|
|
5
6
|
import { fileRelatedAnalyser } from './analysis/file-related-analyser.js';
|
|
@@ -8,12 +9,8 @@ import { gitDatesAnalyser } from './analysis/git-dates-analyser.js';
|
|
|
8
9
|
import { completionTrackingAnalyser } from './analysis/completion-tracking-analyser.js';
|
|
9
10
|
import { localGraphAnalyser } from './analysis/local-graph-analyser.js';
|
|
10
11
|
import { semanticSearchAnalyser } from './analysis/semantic-search-analyser.js';
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
const store = createAnalyticalStore();
|
|
14
|
-
const storagePath = resolveApplicationMemoryPath(options.workspaceRoot, options.storagePath);
|
|
15
|
-
const index = new HeadlessIndexService(store, storagePath, options.workspaceRoot);
|
|
16
|
-
const analysers = new AnalyserRegistry();
|
|
12
|
+
import { WorkspaceIndex } from './index-store/workspace-index.js';
|
|
13
|
+
function registerDefaultAnalysers(analysers) {
|
|
17
14
|
analysers.register(listAllIdeasAnalyser);
|
|
18
15
|
analysers.register(fileRelatedAnalyser);
|
|
19
16
|
analysers.register(deprecationImpactAnalyser);
|
|
@@ -21,12 +18,54 @@ export function createAnalysisRuntime(options) {
|
|
|
21
18
|
analysers.register(completionTrackingAnalyser);
|
|
22
19
|
analysers.register(localGraphAnalyser);
|
|
23
20
|
analysers.register(semanticSearchAnalyser);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Create a headless analysis runtime bound to one base.
|
|
24
|
+
* Discovers `.reqlan`-marked bases under `workspaceRoot` and selects the nearest to `cwd`.
|
|
25
|
+
* Throws when no base exists and no `storagePath` / `baseRoot` override is provided.
|
|
26
|
+
*/
|
|
27
|
+
export function createAnalysisRuntime(options) {
|
|
28
|
+
const searchRoot = options.workspaceRoot;
|
|
29
|
+
const cwd = options.cwd ?? searchRoot;
|
|
30
|
+
let baseRoot;
|
|
31
|
+
let bases = [];
|
|
32
|
+
let base;
|
|
33
|
+
if (options.baseRoot) {
|
|
34
|
+
baseRoot = options.baseRoot;
|
|
35
|
+
bases = discoverBases([searchRoot]);
|
|
36
|
+
base = bases.find(b => b.root === baseRoot);
|
|
37
|
+
}
|
|
38
|
+
else if (options.storagePath?.trim()) {
|
|
39
|
+
// Explicit store override: treat workspaceRoot as the base root.
|
|
40
|
+
baseRoot = searchRoot;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
bases = discoverBases([searchRoot]);
|
|
44
|
+
base = selectDefaultBase(bases, cwd);
|
|
45
|
+
if (!base) {
|
|
46
|
+
throw new Error(`No reqlan base found under ${searchRoot}. Create a .reqlan folder at the project root to initialize a base.`);
|
|
47
|
+
}
|
|
48
|
+
baseRoot = base.root;
|
|
49
|
+
}
|
|
50
|
+
const store = createAnalyticalStore();
|
|
51
|
+
const storagePath = resolveApplicationMemoryPath(baseRoot, options.storagePath);
|
|
52
|
+
const index = new WorkspaceIndex(store, storagePath, baseRoot);
|
|
53
|
+
const analysers = new AnalyserRegistry();
|
|
54
|
+
registerDefaultAnalysers(analysers);
|
|
24
55
|
const makeContext = () => ({
|
|
25
56
|
store: index.indexStore,
|
|
26
57
|
analytical: store,
|
|
27
|
-
workspaceRoot:
|
|
58
|
+
workspaceRoot: baseRoot
|
|
28
59
|
});
|
|
29
|
-
return {
|
|
60
|
+
return {
|
|
61
|
+
store,
|
|
62
|
+
index,
|
|
63
|
+
analysers,
|
|
64
|
+
workspaceRoot: baseRoot,
|
|
65
|
+
bases,
|
|
66
|
+
base,
|
|
67
|
+
makeContext
|
|
68
|
+
};
|
|
30
69
|
}
|
|
31
70
|
export async function activateAnalysisRuntime(runtime) {
|
|
32
71
|
await runtime.index.activate();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-runtime.js","sourceRoot":"","sources":["../src/create-runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAwB,MAAM,4BAA4B,CAAC;AACzF,OAAO,EAAE,4BAA4B,EAAE,MAAM,8BAA8B,CAAC;AAC5E,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AACzE,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAC1E,OAAO,EAAE,yBAAyB,EAAE,MAAM,2CAA2C,CAAC;AACtF,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,EAAE,0BAA0B,EAAE,MAAM,4CAA4C,CAAC;AACxF,OAAO,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AACxE,OAAO,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AAChF,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"create-runtime.js","sourceRoot":"","sources":["../src/create-runtime.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export declare const SHARED_STYLES = "\n:root {\n color-scheme: dark;\n --bg: #14100e;\n --bg-elev: #1f1815;\n --bg-soft: #1f1815;\n --bg-raised: #2a211d;\n --fg: #ebe4de;\n --muted: #a89488;\n --faint: #6e5f56;\n --line: #3d2f28;\n --accent: #07a0e5;\n --accent-strong: #0bbefb;\n --accent-dim: #0371c1;\n --rust: #b85c38;\n --rust-muted: #8a4530;\n --good: #4ade80;\n --warn: #fbbf24;\n --bad: #fb7185;\n font-family: Inter, system-ui, sans-serif;\n}\n* { box-sizing: border-box; }\nbody {\n margin: 0;\n background: var(--bg);\n color: var(--fg);\n line-height: 1.55;\n}\na { color: var(--accent-strong); text-decoration: none; }\na:hover { color: var(--accent); }\nmain.layout {\n max-width: 1360px; margin: 0 auto; padding: 1.5rem;\n display: flex; flex-direction: column; gap: 1.5rem;\n}\n.topbar {\n position: sticky; top: 0; z-index: 10;\n background: var(--bg-elev); border-bottom: 1px solid var(--line); margin: 0 -1.5rem;\n padding: 0.9rem 1.5rem;\n}\n.topbar-inner, .hero, .split, .grid, .detail-grid { display: grid; gap: 1.5rem; }\n.topbar-inner { grid-template-columns: 1.3fr auto; align-items: center; }\n.nav { display: flex; flex-wrap: wrap; gap: 0.75rem; align-items: center; }\n.nav a {\n padding: 0.45rem 0.75rem; border-radius: 8px;\n background: var(--bg-raised); color: var(--muted); border: 1px solid var(--line);\n}\n.nav a:hover { color: var(--fg); border-color: color-mix(in srgb, var(--accent) 35%, var(--line)); }\n.nav a.active {\n background: color-mix(in srgb, var(--rust) 28%, var(--bg-raised));\n color: var(--fg); border-color: var(--rust);\n}\n.nav a.brand-link {\n background: transparent;\n border-color: transparent;\n color: var(--fg);\n font-weight: 650;\n letter-spacing: 0.02em;\n padding-left: 0.15rem;\n padding-right: 0.9rem;\n}\n.nav a.brand-link:hover {\n color: var(--accent);\n border-color: transparent;\n background: transparent;\n}\n.hero { grid-template-columns: 2fr 1fr; }\n.grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(min(100%, 140px), 1fr));\n gap: 1.5rem;\n row-gap: 1.5rem;\n column-gap: 1.5rem;\n}\n.split, .detail-grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr));\n gap: 1.5rem;\n}\n.panel, .metric, .card, .table-shell, .graph-shell, .print-card, .search-result {\n background: var(--bg-raised);\n border: 1px solid var(--line);\n border-radius: 10px;\n}\n.panel, .card, .table-shell, .graph-shell, .print-card, .search-result { padding: 1rem 1.1rem; }\n.metric { padding: 0; overflow: hidden; }\n.metric-link, .metric > span, .metric > strong {\n display: flex; flex-direction: column; gap: 0.35rem; padding: 1rem; color: inherit;\n}\n.metric-link:hover { background: color-mix(in srgb, var(--accent) 10%, var(--bg-raised)); }\n.metric-label, .subtle, .eyebrow, .breadcrumbs { color: var(--muted); }\n.metric-value { font-size: 1.75rem; color: var(--accent-strong); }\n.eyebrow { text-transform: uppercase; letter-spacing: 0.08em; font-size: 0.75rem; color: var(--rust); }\n.page-header { display: grid; gap: 0.55rem; }\n.page-header h1 { margin: 0; }\n.page-header .eyebrow { margin: 0; }\n.page-header .subtle { margin: 0; }\n.toolbar {\n display: flex; flex-wrap: wrap; gap: 0.75rem; align-items: center; justify-content: space-between;\n margin-bottom: 0.9rem;\n}\n.toolbar .actions, .pill-row, .breadcrumbs { display: flex; gap: 0.75rem; row-gap: 0.85rem; flex-wrap: wrap; align-items: center; }\n.chip, .pill {\n display: inline-flex; align-items: center; gap: 0.35rem;\n padding: 0.28rem 0.6rem; border-radius: 8px;\n border: 1px solid var(--line); background: var(--bg); color: var(--muted);\n}\na.pill:hover { color: var(--accent-strong); border-color: color-mix(in srgb, var(--accent) 40%, var(--line)); }\n.graph-controls-bar {\n display: flex;\n flex-wrap: wrap;\n gap: 0.65rem;\n align-items: center;\n margin-bottom: 0.9rem;\n padding: 0.8rem;\n border: 1px solid var(--line);\n border-radius: 10px;\n background: var(--bg);\n}\n.graph-shell > .pill-row { margin-bottom: 0.9rem; }\n.graph-filter {\n flex: 1 1 180px;\n min-width: 160px;\n}\n.graph-action {\n border: 1px solid var(--line);\n background: var(--bg-soft);\n color: var(--fg);\n border-radius: 8px;\n padding: 0.7rem 0.9rem;\n cursor: pointer;\n}\n.graph-action:hover { border-color: var(--accent-dim); color: var(--accent-strong); }\n.graph-action.is-active {\n background: color-mix(in srgb, var(--rust) 28%, var(--bg-soft));\n border-color: var(--rust);\n color: var(--fg);\n}\n.graph-status {\n color: var(--muted);\n font-size: 0.92rem;\n margin-left: auto;\n}\n.searchbar, select, input[type=\"search\"] {\n width: min(100%, 440px); background: var(--bg); color: var(--fg);\n border: 1px solid var(--line); border-radius: 8px; padding: 0.75rem 0.9rem;\n}\n.searchbar:focus, select:focus, input[type=\"search\"]:focus {\n outline: none;\n border-color: var(--accent);\n box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 25%, transparent);\n}\ntable { width: 100%; border-collapse: collapse; }\nth, td {\n padding: 0.75rem 0.65rem; text-align: left; border-bottom: 1px solid var(--line); vertical-align: top;\n}\nth { color: var(--muted); font-size: 0.94rem; }\ntbody tr:hover { background: var(--bg-raised); }\n.table-shell { overflow-x: auto; }\n.scroll-window {\n --scroll-window-max: min(28rem, 50vh);\n}\nbody[data-runtime-mode=\"interactive\"] .scroll-window {\n max-height: var(--scroll-window-max);\n overflow: auto;\n border: 1px solid var(--line);\n border-radius: 8px;\n background: var(--bg);\n}\nbody[data-runtime-mode=\"interactive\"] .scroll-window > table {\n margin: 0;\n}\nbody[data-runtime-mode=\"interactive\"] .scroll-window .entity-list,\nbody[data-runtime-mode=\"interactive\"] .scroll-window .rollup-list {\n padding: 0.75rem;\n}\n.sortable-th { padding: 0.45rem 0.4rem; }\n.sort-button {\n display: inline-flex; align-items: center; gap: 0.35rem;\n width: 100%; margin: 0; padding: 0.3rem 0.25rem;\n border: 0; background: transparent; color: inherit;\n font: inherit; text-align: left; cursor: pointer;\n}\n.sort-button:hover { color: var(--fg); }\n.sort-button.sort-active { color: var(--accent-strong); }\n.sort-indicator { color: var(--faint); font-size: 0.8em; min-width: 1em; }\n.sort-button.sort-active .sort-indicator { color: var(--accent-strong); }\n.table-filter-toggle {\n display: inline-flex; align-items: center; justify-content: center;\n width: 2.25rem; height: 2.25rem; padding: 0;\n border: 1px solid var(--line); border-radius: 8px;\n background: var(--bg-raised); color: var(--muted); cursor: pointer;\n}\n.table-filter-toggle:hover { color: var(--fg); border-color: color-mix(in srgb, var(--accent) 40%, var(--line)); }\n.table-filter-toggle.is-active {\n color: var(--fg);\n border-color: var(--accent);\n background: color-mix(in srgb, var(--accent) 18%, var(--bg-raised));\n}\n.table-filter-toggle.has-filters:not(.is-active) {\n color: var(--accent-strong);\n border-color: color-mix(in srgb, var(--accent) 45%, var(--line));\n}\n.table-filter-toggle svg { width: 1rem; height: 1rem; display: block; }\n.table-filter-actions {\n display: flex; justify-content: flex-end; gap: 0.55rem;\n margin-bottom: 0.65rem;\n}\n.column-filter-row { display: none; }\n.column-filter-row.is-open { display: table-row; }\n.column-filter-row th {\n padding: 0.35rem 0.4rem 0.75rem; border-bottom: 1px solid var(--line); font-weight: 400;\n}\n.column-filter {\n width: 100%; min-width: 4.5rem; background: var(--bg); color: var(--fg);\n border: 1px solid var(--line); border-radius: 6px; padding: 0.4rem 0.55rem; font-size: 0.85rem;\n}\n.column-filter:focus {\n outline: none;\n border-color: var(--accent);\n box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 25%, transparent);\n}\nbody[data-runtime-mode=\"interactive\"] .scroll-window thead tr:first-child th {\n position: sticky;\n top: 0;\n z-index: 2;\n background: var(--bg-raised);\n box-shadow: 0 1px 0 var(--line);\n}\nbody[data-runtime-mode=\"interactive\"] .scroll-window thead .column-filter-row.is-open th {\n position: sticky;\n top: 2.65rem;\n z-index: 1;\n background: var(--bg-raised);\n box-shadow: 0 1px 0 var(--line);\n}\n.entity-list { display: grid; gap: 1.5rem; }\n.entity-card {\n border: 1px solid var(--line); border-radius: 10px; padding: 0.9rem 1rem; background: var(--bg);\n color: inherit;\n}\n.entity-card:hover { border-color: color-mix(in srgb, var(--accent) 40%, var(--line)); }\n.entity-card h3, .entity-card p { margin: 0; }\n.entity-card p + p, .entity-card strong + p, .entity-card h3 + p { margin-top: 0.35rem; }\n.panel > h2, .table-shell > h2, .graph-shell > h2, .print-card > h3 { margin-top: 0; margin-bottom: 0.75rem; }\n.toolbar h2 { margin: 0; }\n.rollup-list { display: grid; gap: 0.5rem; }\n.rollup-list div {\n display: flex; justify-content: space-between; gap: 1rem; border-bottom: 1px solid var(--line); padding-bottom: 0.4rem;\n}\n.rollup-list dd { margin: 0; }\n.distribution-track {\n height: 0.7rem;\n min-width: 6rem;\n border-radius: 999px;\n background: var(--bg);\n border: 1px solid var(--line);\n overflow: hidden;\n}\n.distribution-fill {\n display: block;\n height: 100%;\n border-radius: inherit;\n background: linear-gradient(90deg, var(--accent-dim), var(--accent-strong));\n min-width: 0;\n}\n.graph-root svg {\n width: 100%; min-height: 620px; background: var(--bg); border-radius: 10px; border: 1px solid var(--line);\n}\n.graph-root line {\n stroke: color-mix(in srgb, var(--line) 80%, transparent); stroke-width: 1.2;\n opacity: 0.85; transition: opacity 180ms ease;\n}\n.graph-root .node { cursor: pointer; }\n.graph-root circle {\n fill: var(--accent); stroke: color-mix(in srgb, var(--fg) 35%, transparent); stroke-width: 1.25;\n transition: fill 180ms ease, stroke 180ms ease, r 180ms ease;\n}\n.graph-root .external circle { fill: var(--rust-muted); stroke: color-mix(in srgb, var(--rust) 50%, transparent); }\n.graph-root .ideaset circle { fill: color-mix(in srgb, #d18616 78%, white); stroke: #d18616; }\n.graph-root .subject circle {\n fill: var(--rust);\n stroke: var(--accent-strong);\n stroke-width: 2.75;\n animation: subject-pulse 2.4s ease-in-out infinite;\n}\n.graph-root text {\n fill: var(--fg); font-size: 11px; paint-order: stroke;\n stroke: var(--bg); stroke-width: 3px; stroke-linejoin: round;\n pointer-events: none;\n}\n.graph-root .subject text {\n fill: var(--accent-strong); font-size: 12.5px; font-weight: 600;\n}\n.graph-root .node:hover circle { stroke: var(--accent-strong); stroke-width: 2; }\n.graph-root .graph-label {\n width: 160px;\n color: var(--fg);\n font: 11px/1.3 Inter, system-ui, sans-serif;\n text-align: center;\n word-break: break-word;\n overflow-wrap: anywhere;\n pointer-events: none;\n user-select: none;\n}\n.graph-root .graph-label strong { display: block; font-weight: 600; }\n.graph-root .graph-label .meta { display: block; color: var(--muted); font-size: 10px; margin-top: 0.15rem; }\n.graph-root .graph-label .attrs { display: block; color: var(--rust); font-size: 9.5px; margin-top: 0.1rem; }\n.graph-root .subject .graph-label strong { color: var(--accent-strong); }\n.graph-root svg { touch-action: none; cursor: grab; }\n.graph-root svg.is-panning { cursor: grabbing; }\n.graph-root .node { cursor: pointer; }\n@keyframes subject-pulse {\n 0%, 100% { filter: drop-shadow(0 0 0 transparent); }\n 50% { filter: drop-shadow(0 0 8px color-mix(in srgb, var(--rust) 55%, transparent)); }\n}\n.search-results { display: grid; gap: 0.7rem; margin-top: 1rem; }\n.search-result:hover { border-color: var(--accent-dim); }\n.hidden { display: none !important; }\n.breadcrumbs a { color: var(--muted); }\n.breadcrumbs a:hover { color: var(--accent-strong); }\n.section-link { font-size: 0.92rem; }\n.prose p { line-height: 1.55; }\n.idea-summary { white-space: normal; }\n.idea-ref {\n display: inline;\n padding: 0.05em 0.35em;\n margin: 0 0.05em;\n border-radius: 0.3em;\n border: 1px solid color-mix(in srgb, var(--accent) 35%, var(--line));\n background: color-mix(in srgb, var(--accent) 14%, var(--bg-raised));\n color: var(--accent-strong);\n font-weight: 600;\n text-decoration: none;\n white-space: nowrap;\n}\na.idea-ref:hover {\n color: var(--fg);\n border-color: var(--accent);\n background: color-mix(in srgb, var(--accent) 22%, var(--bg-raised));\n}\n.idea-ref--file {\n border-color: color-mix(in srgb, var(--rust) 40%, var(--line));\n background: color-mix(in srgb, var(--rust) 16%, var(--bg-raised));\n color: #e8a07a;\n}\na.idea-ref--file:hover {\n color: var(--fg);\n border-color: var(--rust);\n}\n.idea-ref--unresolved {\n border-style: dashed;\n border-color: color-mix(in srgb, var(--warn) 45%, var(--line));\n background: color-mix(in srgb, var(--warn) 10%, var(--bg-raised));\n color: var(--warn);\n font-weight: 500;\n}\npre.code-like {\n white-space: pre-wrap; padding: 0.9rem; border-radius: 8px;\n background: var(--bg); border: 1px solid var(--line);\n}\n@media (max-width: 900px) {\n .hero, .topbar-inner { grid-template-columns: 1fr; }\n}\n@media print {\n :root { color-scheme: light; }\n body { background: white; color: black; }\n .topbar, .searchbar, .toolbar .actions, .graph-shell .toolbar, .column-filter-row, .table-filter-toggle, .table-filter-actions, .hide-on-print { display: none !important; }\n .sort-button { cursor: default; }\n .panel, .metric, .card, .table-shell, .graph-shell, .print-card, .search-result, .entity-card {\n background: white; color: black; border-color: #cbd5e1; box-shadow: none;\n }\n .scroll-window {\n max-height: none !important;\n overflow: visible !important;\n border: none !important;\n background: transparent !important;\n }\n .scroll-window thead th {\n position: static !important;\n box-shadow: none !important;\n }\n a { color: black; text-decoration: underline; }\n .print-break-avoid { break-inside: avoid; }\n}\n";
|
|
2
|
-
export declare const APP_JS = "\nconst searchIndexByRoot = new WeakMap();\n\nfunction exportRootPrefix(root) {\n const searchUrl = root.dataset.searchIndex || '';\n const dataIdx = searchUrl.lastIndexOf('data/');\n return dataIdx >= 0 ? searchUrl.slice(0, dataIdx) : '';\n}\n\nfunction resolveExportUrl(root, url) {\n const cleaned = String(url || '').replace(/^\\.\\//, '');\n return `${exportRootPrefix(root)}${cleaned}`;\n}\n\nasync function loadSearchIndex(root) {\n if (searchIndexByRoot.has(root)) return searchIndexByRoot.get(root);\n const embedded = typeof globalThis.__REQLAN_SEARCH_INDEX__ !== 'undefined'\n ? globalThis.__REQLAN_SEARCH_INDEX__\n : null;\n if (Array.isArray(embedded)) {\n const ready = Promise.resolve(embedded);\n searchIndexByRoot.set(root, ready);\n return ready;\n }\n const url = root.dataset.searchIndex;\n if (!url) return [];\n const loaded = fetch(url).then(response => {\n if (!response.ok) throw new Error('search index fetch failed');\n return response.json();\n }).catch(() => []);\n searchIndexByRoot.set(root, loaded);\n return loaded;\n}\n\nfunction lower(value) {\n return String(value || '').toLowerCase();\n}\n\nfunction scoreDocument(doc, query) {\n const haystack = [\n doc.title,\n doc.summary,\n ...(doc.tags || []),\n ...(doc.pathTokens || []),\n ...(doc.keywords || [])\n ].map(lower).join(' ');\n return haystack.includes(query) ? haystack.indexOf(query) : -1;\n}\n\nasync function wireGlobalSearch(root) {\n const input = root.querySelector('[data-global-search]');\n const results = root.querySelector('[data-search-results]');\n if (!input || !results) return;\n const docs = await loadSearchIndex(root);\n function render() {\n const query = lower(input.value.trim());\n if (!query) {\n results.innerHTML = '';\n results.classList.add('hidden');\n return;\n }\n const matches = docs\n .map(doc => ({ doc, score: scoreDocument(doc, query) }))\n .filter(item => item.score >= 0)\n .sort((a, b) => a.score - b.score || a.doc.title.localeCompare(b.doc.title))\n .slice(0, 18);\n results.classList.remove('hidden');\n results.innerHTML = matches.length === 0\n ? '<div class=\"search-result\"><strong>No matches</strong><p class=\"subtle\">Try idea names, tags, statuses, file paths, or cluster labels.</p></div>'\n : matches.map(({ doc }) => `\n <a class=\"search-result\" href=\"${resolveExportUrl(root, doc.url)}\">\n <div class=\"pill-row\">\n <span class=\"pill\">${doc.kind}</span>\n ${doc.status ? `<span class=\"pill\">${doc.status}</span>` : ''}\n </div>\n <strong>${doc.title}</strong>\n <p class=\"subtle\">${doc.summary || ''}</p>\n </a>\n `).join('');\n }\n input.addEventListener('input', render);\n}\n\nfunction cellSortValue(cell) {\n if (!cell) return '';\n const explicit = cell.getAttribute('data-sort-value');\n if (explicit != null) return explicit.trim();\n return String(cell.textContent || '').replace(/\\s+/g, ' ').trim();\n}\n\nfunction compareSortValues(left, right) {\n const leftNumber = Number(String(left).replace(/,/g, ''));\n const rightNumber = Number(String(right).replace(/,/g, ''));\n const bothNumeric = left !== '' && right !== ''\n && Number.isFinite(leftNumber)\n && Number.isFinite(rightNumber);\n if (bothNumeric) {\n return leftNumber - rightNumber;\n }\n return String(left).localeCompare(String(right), undefined, { numeric: true, sensitivity: 'base' });\n}\n\nfunction isPlaceholderRow(row) {\n if (!row || row.cells.length === 0) return true;\n return [...row.cells].some(cell => Number(cell.colSpan || 1) > 1);\n}\n\nconst FILTER_ICON_SVG = '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><polygon points=\"22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3\"></polygon></svg>';\n\nfunction ensureTableFilterToggle(table, filterRow, filterInputs, syncToggleState) {\n const shell = table.closest('.table-shell') || table.parentElement;\n const toolbar = shell ? shell.querySelector(':scope > .toolbar') : null;\n let actions = toolbar ? toolbar.querySelector('.actions') : null;\n if (toolbar && !actions) {\n actions = document.createElement('div');\n actions.className = 'actions';\n toolbar.appendChild(actions);\n }\n if (!actions) {\n actions = document.createElement('div');\n actions.className = 'actions table-filter-actions';\n table.parentElement?.insertBefore(actions, table);\n }\n\n const toggle = document.createElement('button');\n toggle.type = 'button';\n toggle.className = 'table-filter-toggle';\n toggle.setAttribute('aria-label', 'Toggle column filters');\n toggle.setAttribute('aria-expanded', 'false');\n toggle.setAttribute('title', 'Column filters');\n toggle.innerHTML = FILTER_ICON_SVG;\n toggle.addEventListener('click', () => {\n const open = !filterRow.classList.contains('is-open');\n filterRow.classList.toggle('is-open', open);\n toggle.classList.toggle('is-active', open);\n toggle.setAttribute('aria-expanded', open ? 'true' : 'false');\n syncToggleState();\n if (open) {\n filterInputs[0]?.focus();\n }\n });\n actions.appendChild(toggle);\n return toggle;\n}\n\nfunction wireTables(root) {\n const tables = root.querySelectorAll('table');\n const tableControllers = [];\n\n for (const table of tables) {\n const thead = table.tHead;\n const tbody = table.tBodies[0];\n if (!thead || !tbody || thead.rows.length === 0) continue;\n const headerRow = thead.rows[0];\n if (headerRow.dataset.enhanced === '1') continue;\n headerRow.dataset.enhanced = '1';\n\n const headers = [...headerRow.cells];\n if (headers.length === 0) continue;\n\n let sortIndex = -1;\n let sortDir = 'asc';\n const filterInputs = [];\n let filterToggle = null;\n const syncToggleState = () => {\n if (!filterToggle) return;\n const hasFilters = filterInputs.some(input => input.value.trim());\n filterToggle.classList.toggle('has-filters', hasFilters);\n };\n const controller = {\n table,\n tbody,\n filterInputs,\n applyFilters() {\n const sample = [...tbody.rows].find(row => row.hasAttribute('data-filter-row'));\n const filterId = sample?.getAttribute('data-filter-row');\n const globalInput = filterId\n ? root.querySelector(`[data-filter-input=\"${filterId}\"]`)\n : null;\n const globalQuery = lower(globalInput?.value.trim());\n const columnQueries = filterInputs.map(input => lower(input.value.trim()));\n for (const row of tbody.rows) {\n if (isPlaceholderRow(row)) continue;\n const globalText = lower(row.getAttribute('data-filter-text') || row.textContent);\n const matchesGlobal = !globalQuery || globalText.includes(globalQuery);\n const matchesColumns = columnQueries.every((query, index) => {\n if (!query) return true;\n return lower(cellSortValue(row.cells[index])).includes(query);\n });\n row.classList.toggle('hidden', !(matchesGlobal && matchesColumns));\n }\n syncToggleState();\n }\n };\n\n headers.forEach((th, index) => {\n const label = String(th.textContent || '').trim() || `Column ${index + 1}`;\n th.classList.add('sortable-th');\n th.replaceChildren();\n const button = document.createElement('button');\n button.type = 'button';\n button.className = 'sort-button';\n button.dataset.sortCol = String(index);\n button.setAttribute('aria-label', `Sort by ${label}`);\n const labelNode = document.createElement('span');\n labelNode.textContent = label;\n const indicator = document.createElement('span');\n indicator.className = 'sort-indicator';\n indicator.setAttribute('aria-hidden', 'true');\n button.append(labelNode, indicator);\n button.addEventListener('click', () => {\n if (sortIndex === index) {\n sortDir = sortDir === 'asc' ? 'desc' : 'asc';\n } else {\n sortIndex = index;\n sortDir = 'asc';\n }\n const dataRows = [...tbody.rows].filter(row => !isPlaceholderRow(row));\n dataRows.sort((left, right) => {\n const cmp = compareSortValues(\n cellSortValue(left.cells[index]),\n cellSortValue(right.cells[index])\n );\n return sortDir === 'asc' ? cmp : -cmp;\n });\n for (const row of dataRows) {\n tbody.appendChild(row);\n }\n for (const header of headers) {\n const sortButton = header.querySelector('.sort-button');\n const mark = header.querySelector('.sort-indicator');\n if (!sortButton || !mark) continue;\n const active = Number(sortButton.dataset.sortCol) === sortIndex;\n sortButton.classList.toggle('sort-active', active);\n mark.textContent = active ? (sortDir === 'asc' ? '\u25B2' : '\u25BC') : '';\n }\n });\n th.appendChild(button);\n });\n\n const filterRow = document.createElement('tr');\n filterRow.className = 'column-filter-row';\n headers.forEach((th, index) => {\n const label = String(th.querySelector('.sort-button span')?.textContent || `Column ${index + 1}`);\n const cell = document.createElement('th');\n const input = document.createElement('input');\n input.type = 'search';\n input.className = 'column-filter';\n input.placeholder = 'Filter\u2026';\n input.dataset.colFilter = String(index);\n input.setAttribute('aria-label', `Filter ${label}`);\n input.addEventListener('input', () => controller.applyFilters());\n filterInputs.push(input);\n cell.appendChild(input);\n filterRow.appendChild(cell);\n });\n thead.appendChild(filterRow);\n filterToggle = ensureTableFilterToggle(table, filterRow, filterInputs, syncToggleState);\n tableControllers.push(controller);\n }\n\n const inputs = root.querySelectorAll('[data-filter-input]');\n for (const input of inputs) {\n input.addEventListener('input', () => {\n const targetId = input.dataset.filterInput;\n for (const controller of tableControllers) {\n const ownsRows = controller.tbody.querySelector(`[data-filter-row=\"${targetId}\"]`);\n if (ownsRows) {\n controller.applyFilters();\n }\n }\n });\n }\n}\n\nfunction hashAngle(seed) {\n let hash = 0;\n for (let index = 0; index < seed.length; index += 1) {\n hash = (hash * 31 + seed.charCodeAt(index)) | 0;\n }\n return ((hash >>> 0) % 6283) / 1000;\n}\n\n// Matches packages/extension/webviews/shared/graph/graph-physics.ts DEFAULT_PHYSICS_SETTINGS.\nconst EXPORT_PHYSICS_SETTINGS = {\n gravity: 0.002,\n repulsion: 20000,\n linkStrength: 0.015,\n linkDistance: 120,\n damping: 0.5,\n maxVelocity: 10,\n minSeparation: 24,\n restSpeed: 0.02,\n restTicks: 90\n};\n\nfunction wireGraph(root) {\n const graphRoots = root.querySelectorAll('[data-graph-json]');\n for (const element of graphRoots) {\n const scriptId = element.dataset.graphJson;\n const dataElement = root.querySelector(`#${scriptId}`);\n if (!dataElement) continue;\n let graph;\n try {\n graph = JSON.parse(dataElement.textContent || '{}');\n } catch {\n continue;\n }\n let controls = root.querySelector(`[data-graph-controls=\"${scriptId}\"]`);\n if (!controls) {\n controls = document.createElement('div');\n controls.className = 'graph-controls-bar';\n controls.dataset.graphControls = scriptId;\n controls.innerHTML = '<button type=\"button\" class=\"graph-action\" data-graph-toggle-physics aria-pressed=\"false\">Live physics</button><button type=\"button\" class=\"graph-action\" data-graph-fit>Fit</button><span class=\"graph-status\" data-graph-status-text></span>';\n element.parentElement?.insertBefore(controls, element);\n }\n const searchInput = controls.querySelector('[data-graph-search]');\n const pathInput = controls.querySelector('[data-graph-path]');\n const statusSelect = controls.querySelector('[data-graph-status]');\n const tagSelect = controls.querySelector('[data-graph-tag]');\n const toggleExternal = controls.querySelector('[data-graph-toggle-external]');\n const toggleIdeasets = controls.querySelector('[data-graph-toggle-ideasets]');\n const togglePhysics = controls.querySelector('[data-graph-toggle-physics]');\n const fitButton = controls.querySelector('[data-graph-fit]');\n const resetButton = controls.querySelector('[data-graph-reset]');\n const statusText = controls.querySelector('[data-graph-status-text]');\n let hideExternal = false;\n let hideIdeasets = false;\n let livePhysics = false;\n let animationFrame = 0;\n let calmTicks = 0;\n let simNodes = [];\n let simEdges = [];\n const positions = new Map();\n const velocities = new Map();\n let svg;\n let edgeEls = [];\n let nodeEls = [];\n const pinnedIds = new Set();\n let view = { x: 0, y: 0, w: 1200, h: 640 };\n let userViewport = false;\n let dragNodeId = null;\n let dragMoved = false;\n let panMode = false;\n let panOrigin = null;\n let suppressClickUntil = 0;\n\n function resolveGraphNodeUrl(pageUrl) {\n let cleaned = String(pageUrl || '').replace(/^\\.\\//, '');\n if (cleaned && !cleaned.includes('/') && cleaned.endsWith('.html')) {\n cleaned = `ideas/${cleaned}`;\n }\n return resolveExportUrl(root, cleaned);\n }\n\n function escapeXml(value) {\n return String(value || '')\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"');\n }\n\n function formatNodeMeta(node) {\n const bits = [];\n if (node.status) bits.push(String(node.status));\n if (Array.isArray(node.tags) && node.tags.length) bits.push(node.tags.join(', '));\n return bits.join(' \u00B7 ');\n }\n\n function formatNodeAttrs(node) {\n const keys = Array.isArray(node.attributeKeys)\n ? node.attributeKeys\n : Object.keys(node.attributes || {});\n return keys.length ? keys.map(key => '@' + key).join(' ') : '';\n }\n\n function seedMissingPositions(nodes, width, height, centerId) {\n const cx = width / 2;\n const cy = height / 2;\n const subject = nodes.find(node => node.isSubject || node.id === centerId);\n const radius = Math.min(width, height) * 0.34;\n nodes.forEach((node, index) => {\n if (positions.has(node.id)) return;\n if (subject && node.id === subject.id) {\n positions.set(node.id, { x: cx, y: cy });\n return;\n }\n const angle = (Math.PI * 2 * index) / Math.max(nodes.length, 1) - Math.PI / 2;\n const ring = radius * (0.72 + (index % 3) * 0.14);\n positions.set(node.id, {\n x: cx + Math.cos(angle) * ring,\n y: cy + Math.sin(angle) * ring\n });\n });\n }\n\n function pruneState(validIds) {\n for (const id of [...positions.keys()]) {\n if (!validIds.has(id)) positions.delete(id);\n }\n for (const id of [...velocities.keys()]) {\n if (!validIds.has(id)) velocities.delete(id);\n }\n }\n\n function physicsStep(nodes, edges) {\n const settings = EXPORT_PHYSICS_SETTINGS;\n const count = nodes.length;\n if (count === 0) return 0;\n const ids = new Array(count);\n const xs = new Float64Array(count);\n const ys = new Float64Array(count);\n const fxs = new Float64Array(count);\n const fys = new Float64Array(count);\n const indexById = new Map();\n for (let i = 0; i < count; i += 1) {\n const node = nodes[i];\n const pos = positions.get(node.id) || { x: 0, y: 0 };\n ids[i] = node.id;\n xs[i] = pos.x;\n ys[i] = pos.y;\n indexById.set(node.id, i);\n }\n let centroidX = 0;\n let centroidY = 0;\n for (let i = 0; i < count; i += 1) {\n centroidX += xs[i];\n centroidY += ys[i];\n }\n centroidX /= count;\n centroidY /= count;\n for (let i = 0; i < count; i += 1) {\n fxs[i] -= settings.gravity * (xs[i] - centroidX);\n fys[i] -= settings.gravity * (ys[i] - centroidY);\n }\n const minSeparationSq = settings.minSeparation * settings.minSeparation;\n for (let i = 0; i < count; i += 1) {\n for (let j = i + 1; j < count; j += 1) {\n let dx = xs[j] - xs[i];\n let dy = ys[j] - ys[i];\n let distSq = dx * dx + dy * dy;\n if (distSq < 1e-6) {\n const angle = hashAngle(ids[i] + ids[j]);\n dx = Math.cos(angle);\n dy = Math.sin(angle);\n distSq = 1;\n }\n const clampedSq = Math.max(distSq, minSeparationSq);\n const dist = Math.sqrt(distSq);\n const force = settings.repulsion / clampedSq;\n const fx = (dx / dist) * force;\n const fy = (dy / dist) * force;\n fxs[i] -= fx;\n fys[i] -= fy;\n fxs[j] += fx;\n fys[j] += fy;\n }\n }\n for (const edge of edges) {\n const sourceIndex = indexById.get(edge.sourceId);\n const targetIndex = indexById.get(edge.targetId);\n if (sourceIndex === undefined || targetIndex === undefined || sourceIndex === targetIndex) continue;\n const dx = xs[targetIndex] - xs[sourceIndex];\n const dy = ys[targetIndex] - ys[sourceIndex];\n const dist = Math.sqrt(dx * dx + dy * dy);\n if (dist < 1e-3) continue;\n const force = settings.linkStrength * (dist - settings.linkDistance);\n const fx = (dx / dist) * force;\n const fy = (dy / dist) * force;\n fxs[sourceIndex] += fx;\n fys[sourceIndex] += fy;\n fxs[targetIndex] -= fx;\n fys[targetIndex] -= fy;\n }\n let speedSum = 0;\n let movingCount = 0;\n for (let i = 0; i < count; i += 1) {\n const id = ids[i];\n if (pinnedIds.has(id)) {\n continue;\n }\n const velocity = velocities.get(id) || { vx: 0, vy: 0 };\n let vx = (velocity.vx + fxs[i]) * settings.damping;\n let vy = (velocity.vy + fys[i]) * settings.damping;\n const speed = Math.sqrt(vx * vx + vy * vy);\n if (speed > settings.maxVelocity) {\n const scale = settings.maxVelocity / speed;\n vx *= scale;\n vy *= scale;\n }\n velocity.vx = vx;\n velocity.vy = vy;\n velocities.set(id, velocity);\n positions.set(id, { x: xs[i] + vx, y: ys[i] + vy });\n speedSum += Math.min(speed, settings.maxVelocity);\n movingCount += 1;\n }\n return movingCount > 0 ? speedSum / movingCount : 0;\n }\n\n function contentBounds() {\n let minX = Infinity;\n let minY = Infinity;\n let maxX = -Infinity;\n let maxY = -Infinity;\n for (const pos of positions.values()) {\n minX = Math.min(minX, pos.x);\n minY = Math.min(minY, pos.y);\n maxX = Math.max(maxX, pos.x);\n maxY = Math.max(maxY, pos.y);\n }\n if (!Number.isFinite(minX)) {\n return { minX: 0, minY: 0, maxX: 1200, maxY: 640 };\n }\n return { minX, minY, maxX, maxY };\n }\n\n function applyView() {\n if (!svg) return;\n svg.setAttribute('viewBox', `${view.x} ${view.y} ${view.w} ${view.h}`);\n }\n\n function fitView(force = false) {\n if (userViewport && !force) {\n applyView();\n return;\n }\n const bounds = contentBounds();\n const pad = 80;\n view = {\n x: bounds.minX - pad,\n y: bounds.minY - pad,\n w: Math.max(360, bounds.maxX - bounds.minX + pad * 2),\n h: Math.max(360, bounds.maxY - bounds.minY + pad * 2 + 70)\n };\n userViewport = false;\n applyView();\n }\n\n function paint() {\n if (!svg) return;\n for (const { node, circle, label } of nodeEls) {\n const pos = positions.get(node.id);\n if (!pos) continue;\n circle.setAttribute('cx', String(pos.x));\n circle.setAttribute('cy', String(pos.y));\n label.setAttribute('x', String(pos.x - 80));\n label.setAttribute('y', String(pos.y + 18));\n }\n for (const { edge, line } of edgeEls) {\n const source = positions.get(edge.sourceId);\n const target = positions.get(edge.targetId);\n if (!source || !target) continue;\n line.setAttribute('x1', String(source.x));\n line.setAttribute('y1', String(source.y));\n line.setAttribute('x2', String(target.x));\n line.setAttribute('y2', String(target.y));\n line.setAttribute('opacity', '0.85');\n }\n if (!userViewport) {\n fitView(false);\n } else {\n applyView();\n }\n }\n\n function stopLoop() {\n if (animationFrame) {\n cancelAnimationFrame(animationFrame);\n animationFrame = 0;\n }\n }\n\n function scheduleTick() {\n if (animationFrame) return;\n animationFrame = requestAnimationFrame(() => {\n animationFrame = 0;\n if (!livePhysics) return;\n const averageSpeed = physicsStep(simNodes, simEdges);\n paint();\n if (averageSpeed < EXPORT_PHYSICS_SETTINGS.restSpeed) {\n calmTicks += 1;\n if (calmTicks >= EXPORT_PHYSICS_SETTINGS.restTicks) {\n return;\n }\n } else {\n calmTicks = 0;\n }\n scheduleTick();\n });\n }\n\n function wake() {\n calmTicks = 0;\n if (livePhysics) scheduleTick();\n }\n\n function batchSettle(nodes, edges) {\n const iterations = Math.min(120, 48 + nodes.length);\n for (let i = 0; i < iterations; i += 1) {\n physicsStep(nodes, edges);\n }\n for (const velocity of velocities.values()) {\n velocity.vx = 0;\n velocity.vy = 0;\n }\n calmTicks = EXPORT_PHYSICS_SETTINGS.restTicks;\n }\n\n function mountGraph(filteredNodes, filteredEdges) {\n stopLoop();\n simNodes = filteredNodes;\n simEdges = filteredEdges;\n const validIds = new Set(filteredNodes.map(node => node.id));\n pruneState(validIds);\n const width = 1200;\n const height = Math.max(640, Math.min(1400, 220 + filteredNodes.length * 28));\n seedMissingPositions(filteredNodes, width, height, graph.centerId);\n svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');\n svg.setAttribute('viewBox', `0 0 ${width} ${height}`);\n edgeEls = [];\n for (const edge of filteredEdges) {\n const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');\n svg.appendChild(line);\n edgeEls.push({ edge, line });\n }\n nodeEls = [];\n for (const node of filteredNodes) {\n const group = document.createElementNS('http://www.w3.org/2000/svg', 'g');\n const classes = ['node'];\n if (node.isExternal) classes.push('external');\n if (node.kind === 'ideaset') classes.push('ideaset');\n if (node.isSubject || node.id === graph.centerId) classes.push('subject');\n group.setAttribute('class', classes.join(' '));\n group.dataset.nodeId = node.id;\n const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');\n circle.setAttribute('r', node.isSubject || node.id === graph.centerId ? '18' : '13');\n const label = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject');\n label.setAttribute('width', '160');\n label.setAttribute('height', '96');\n const meta = formatNodeMeta(node);\n const attrs = formatNodeAttrs(node);\n label.innerHTML = `<div xmlns=\"http://www.w3.org/1999/xhtml\" class=\"graph-label\"><strong>${escapeXml(node.name)}</strong>${meta ? `<span class=\"meta\">${escapeXml(meta)}</span>` : ''}${attrs ? `<span class=\"attrs\">${escapeXml(attrs)}</span>` : ''}</div>`;\n const title = document.createElementNS('http://www.w3.org/2000/svg', 'title');\n title.textContent = [node.name, meta, attrs].filter(Boolean).join('\\n');\n group.appendChild(title);\n group.appendChild(circle);\n group.appendChild(label);\n svg.appendChild(group);\n nodeEls.push({ node, circle, label, group });\n }\n element.innerHTML = '';\n element.appendChild(svg);\n wireViewport(svg);\n if (statusText) {\n statusText.textContent = `${filteredNodes.length} nodes, ${filteredEdges.length} edges`;\n }\n userViewport = false;\n if (!livePhysics) {\n batchSettle(filteredNodes, filteredEdges);\n paint();\n } else {\n paint();\n wake();\n }\n }\n\n function clientToGraph(event) {\n const rect = svg.getBoundingClientRect();\n const x = view.x + ((event.clientX - rect.left) / rect.width) * view.w;\n const y = view.y + ((event.clientY - rect.top) / rect.height) * view.h;\n return { x, y };\n }\n\n function wireViewport(target) {\n target.addEventListener('wheel', (event) => {\n event.preventDefault();\n const factor = event.deltaY < 0 ? 0.9 : 1.1;\n const before = clientToGraph(event);\n view.w = Math.min(8000, Math.max(180, view.w * factor));\n view.h = Math.min(8000, Math.max(180, view.h * factor));\n const after = clientToGraph(event);\n view.x += before.x - after.x;\n view.y += before.y - after.y;\n userViewport = true;\n applyView();\n }, { passive: false });\n\n target.addEventListener('pointerdown', (event) => {\n const nodeGroup = event.target.closest?.('.node');\n if (nodeGroup) {\n const nodeId = nodeGroup.dataset.nodeId;\n dragNodeId = nodeId;\n dragMoved = false;\n pinnedIds.add(nodeId);\n velocities.set(nodeId, { vx: 0, vy: 0 });\n target.setPointerCapture(event.pointerId);\n event.preventDefault();\n return;\n }\n panMode = true;\n panOrigin = { x: event.clientX, y: event.clientY, viewX: view.x, viewY: view.y };\n target.classList.add('is-panning');\n target.setPointerCapture(event.pointerId);\n event.preventDefault();\n });\n\n target.addEventListener('pointermove', (event) => {\n if (dragNodeId) {\n const point = clientToGraph(event);\n positions.set(dragNodeId, { x: point.x, y: point.y });\n dragMoved = true;\n wake();\n paint();\n return;\n }\n if (panMode && panOrigin) {\n const rect = target.getBoundingClientRect();\n const dx = ((event.clientX - panOrigin.x) / rect.width) * view.w;\n const dy = ((event.clientY - panOrigin.y) / rect.height) * view.h;\n view.x = panOrigin.viewX - dx;\n view.y = panOrigin.viewY - dy;\n userViewport = true;\n applyView();\n }\n });\n\n target.addEventListener('pointerup', (event) => {\n if (dragNodeId) {\n const node = simNodes.find(item => item.id === dragNodeId);\n const moved = dragMoved;\n const nodeId = dragNodeId;\n dragNodeId = null;\n pinnedIds.delete(nodeId);\n wake();\n if (!moved && node?.pageUrl && Date.now() > suppressClickUntil) {\n window.location.href = resolveGraphNodeUrl(node.pageUrl);\n } else if (moved) {\n suppressClickUntil = Date.now() + 250;\n }\n return;\n }\n if (panMode) {\n panMode = false;\n panOrigin = null;\n target.classList.remove('is-panning');\n }\n });\n\n target.addEventListener('pointercancel', () => {\n if (dragNodeId) {\n pinnedIds.delete(dragNodeId);\n dragNodeId = null;\n wake();\n }\n panMode = false;\n panOrigin = null;\n target.classList.remove('is-panning');\n });\n }\n\n function matches(node) {\n const query = lower(searchInput?.value?.trim());\n const pathQuery = lower(pathInput?.value?.trim());\n const statusQuery = lower(statusSelect?.value?.trim());\n const tagQuery = lower(tagSelect?.value?.trim());\n if (hideExternal && node.isExternal) return false;\n if (hideIdeasets && node.kind === 'ideaset') return false;\n const haystack = [node.name, node.kind, node.fileUri, node.status || '', ...(node.tags || [])].map(lower).join(' ');\n if (query && !haystack.includes(query)) return false;\n if (pathQuery && !lower(node.fileUri).includes(pathQuery)) return false;\n if (statusQuery && lower(node.status || '') !== statusQuery) return false;\n if (tagQuery && !(node.tags || []).some(tag => lower(tag) === tagQuery)) return false;\n return true;\n }\n\n function refresh() {\n const visibleNodes = (graph.nodes || []).filter(matches);\n const visibleIds = new Set(visibleNodes.map(node => node.id));\n const visibleEdges = (graph.edges || []).filter(edge =>\n visibleIds.has(edge.sourceId) && visibleIds.has(edge.targetId)\n );\n mountGraph(visibleNodes, visibleEdges);\n }\n\n for (const control of [searchInput, pathInput, statusSelect, tagSelect]) {\n control?.addEventListener('input', refresh);\n control?.addEventListener('change', refresh);\n }\n toggleExternal?.addEventListener('click', () => {\n hideExternal = !hideExternal;\n toggleExternal.classList.toggle('is-active', hideExternal);\n toggleExternal.textContent = hideExternal ? 'Show external' : 'Hide external';\n refresh();\n });\n toggleIdeasets?.addEventListener('click', () => {\n hideIdeasets = !hideIdeasets;\n toggleIdeasets.classList.toggle('is-active', hideIdeasets);\n toggleIdeasets.textContent = hideIdeasets ? 'Show ideasets' : 'Hide ideasets';\n refresh();\n });\n togglePhysics?.addEventListener('click', () => {\n livePhysics = !livePhysics;\n togglePhysics.classList.toggle('is-active', livePhysics);\n togglePhysics.setAttribute('aria-pressed', livePhysics ? 'true' : 'false');\n if (livePhysics) {\n wake();\n } else {\n stopLoop();\n }\n });\n fitButton?.addEventListener('click', () => {\n fitView(true);\n });\n resetButton?.addEventListener('click', () => {\n if (searchInput) searchInput.value = '';\n if (pathInput) pathInput.value = '';\n if (statusSelect) statusSelect.value = '';\n if (tagSelect) tagSelect.value = '';\n hideExternal = false;\n hideIdeasets = false;\n toggleExternal?.classList.remove('is-active');\n toggleIdeasets?.classList.remove('is-active');\n if (toggleExternal) toggleExternal.textContent = 'Hide external';\n if (toggleIdeasets) toggleIdeasets.textContent = 'Hide ideasets';\n positions.clear();\n velocities.clear();\n pinnedIds.clear();\n userViewport = false;\n refresh();\n });\n refresh();\n }\n}\n\nfunction boot() {\n const root = document.body;\n wireTables(root);\n wireGraph(root);\n void wireGlobalSearch(root);\n}\n\nif (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', boot);\n} else {\n boot();\n}\n";
|
|
1
|
+
export declare const SHARED_STYLES = "\n:root {\n color-scheme: dark;\n --bg: #14100e;\n --bg-elev: #1f1815;\n --bg-soft: #1f1815;\n --bg-raised: #2a211d;\n --fg: #ebe4de;\n --muted: #a89488;\n --faint: #6e5f56;\n --line: #3d2f28;\n --accent: #07a0e5;\n --accent-strong: #0bbefb;\n --accent-dim: #0371c1;\n --rust: #b85c38;\n --rust-muted: #8a4530;\n --good: #4ade80;\n --warn: #fbbf24;\n --bad: #fb7185;\n font-family: Inter, system-ui, sans-serif;\n}\n* { box-sizing: border-box; }\nbody {\n margin: 0;\n background: var(--bg);\n color: var(--fg);\n line-height: 1.55;\n}\na { color: var(--accent-strong); text-decoration: none; }\na:hover { color: var(--accent); }\nmain.layout {\n max-width: 1360px; margin: 0 auto; padding: 1.5rem;\n display: flex; flex-direction: column; gap: 1.5rem;\n}\n.topbar {\n position: sticky; top: 0; z-index: 10;\n background: var(--bg-elev); border-bottom: 1px solid var(--line); margin: 0 -1.5rem;\n padding: 0.9rem 1.5rem;\n}\n.topbar-inner, .hero, .split, .grid, .detail-grid { display: grid; gap: 1.5rem; }\n.topbar-inner { grid-template-columns: 1.3fr auto; align-items: center; }\n.nav { display: flex; flex-wrap: wrap; gap: 0.75rem; align-items: center; }\n.nav a {\n padding: 0.45rem 0.75rem; border-radius: 8px;\n background: var(--bg-raised); color: var(--muted); border: 1px solid var(--line);\n}\n.nav a:hover { color: var(--fg); border-color: color-mix(in srgb, var(--accent) 35%, var(--line)); }\n.nav a.active {\n background: color-mix(in srgb, var(--rust) 28%, var(--bg-raised));\n color: var(--fg); border-color: var(--rust);\n}\n.nav a.brand-link {\n background: transparent;\n border-color: transparent;\n color: var(--fg);\n font-weight: 650;\n letter-spacing: 0.02em;\n padding-left: 0.15rem;\n padding-right: 0.9rem;\n}\n.nav a.brand-link:hover {\n color: var(--accent);\n border-color: transparent;\n background: transparent;\n}\n.hero { grid-template-columns: 2fr 1fr; }\n.grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(min(100%, 140px), 1fr));\n gap: 1.5rem;\n row-gap: 1.5rem;\n column-gap: 1.5rem;\n}\n.split, .detail-grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr));\n gap: 1.5rem;\n}\n.panel, .metric, .card, .table-shell, .graph-shell, .print-card, .search-result {\n background: var(--bg-raised);\n border: 1px solid var(--line);\n border-radius: 10px;\n}\n.panel, .card, .table-shell, .graph-shell, .print-card, .search-result { padding: 1rem 1.1rem; }\n.metric { padding: 0; overflow: hidden; }\n.metric-link, .metric > span, .metric > strong {\n display: flex; flex-direction: column; gap: 0.35rem; padding: 1rem; color: inherit;\n}\n.metric-link:hover { background: color-mix(in srgb, var(--accent) 10%, var(--bg-raised)); }\n.metric-label, .subtle, .eyebrow, .breadcrumbs { color: var(--muted); }\n.metric-value { font-size: 1.75rem; color: var(--accent-strong); }\n.eyebrow { text-transform: uppercase; letter-spacing: 0.08em; font-size: 0.75rem; color: var(--rust); }\n.page-header { display: grid; gap: 0.55rem; }\n.page-header h1 { margin: 0; }\n.page-header .eyebrow { margin: 0; }\n.page-header .subtle { margin: 0; }\n.toolbar {\n display: flex; flex-wrap: wrap; gap: 0.75rem; align-items: center; justify-content: space-between;\n margin-bottom: 0.9rem;\n}\n.toolbar .actions, .pill-row, .breadcrumbs { display: flex; gap: 0.75rem; row-gap: 0.85rem; flex-wrap: wrap; align-items: center; }\n.chip, .pill {\n display: inline-flex; align-items: center; gap: 0.35rem;\n padding: 0.28rem 0.6rem; border-radius: 8px;\n border: 1px solid var(--line); background: var(--bg); color: var(--muted);\n}\na.pill:hover { color: var(--accent-strong); border-color: color-mix(in srgb, var(--accent) 40%, var(--line)); }\n.graph-controls-bar {\n display: flex;\n flex-wrap: wrap;\n gap: 0.65rem;\n align-items: center;\n margin-bottom: 0.9rem;\n padding: 0.8rem;\n border: 1px solid var(--line);\n border-radius: 10px;\n background: var(--bg);\n}\n.graph-shell > .pill-row { margin-bottom: 0.9rem; }\n.graph-filter {\n flex: 1 1 180px;\n min-width: 160px;\n}\n.graph-action {\n border: 1px solid var(--line);\n background: var(--bg-soft);\n color: var(--fg);\n border-radius: 8px;\n padding: 0.7rem 0.9rem;\n cursor: pointer;\n}\n.graph-action:hover { border-color: var(--accent-dim); color: var(--accent-strong); }\n.graph-action.is-active {\n background: color-mix(in srgb, var(--rust) 28%, var(--bg-soft));\n border-color: var(--rust);\n color: var(--fg);\n}\n.graph-status {\n color: var(--muted);\n font-size: 0.92rem;\n margin-left: auto;\n}\n.searchbar, select, input[type=\"search\"] {\n width: min(100%, 440px); background: var(--bg); color: var(--fg);\n border: 1px solid var(--line); border-radius: 8px; padding: 0.75rem 0.9rem;\n}\n.searchbar:focus, select:focus, input[type=\"search\"]:focus {\n outline: none;\n border-color: var(--accent);\n box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 25%, transparent);\n}\ntable { width: 100%; border-collapse: collapse; }\nth, td {\n padding: 0.75rem 0.65rem; text-align: left; border-bottom: 1px solid var(--line); vertical-align: top;\n}\nth { color: var(--muted); font-size: 0.94rem; }\ntbody tr:hover { background: var(--bg-raised); }\n.table-shell { overflow-x: auto; }\n.scroll-window {\n --scroll-window-max: min(28rem, 50vh);\n}\nbody[data-runtime-mode=\"interactive\"] .scroll-window {\n max-height: var(--scroll-window-max);\n overflow: auto;\n border: 1px solid var(--line);\n border-radius: 8px;\n background: var(--bg);\n}\nbody[data-runtime-mode=\"interactive\"] .scroll-window > table {\n margin: 0;\n}\nbody[data-runtime-mode=\"interactive\"] .scroll-window .entity-list,\nbody[data-runtime-mode=\"interactive\"] .scroll-window .rollup-list {\n padding: 0.75rem;\n}\n.sortable-th { padding: 0.45rem 0.4rem; }\n.sort-button {\n display: inline-flex; align-items: center; gap: 0.35rem;\n width: 100%; margin: 0; padding: 0.3rem 0.25rem;\n border: 0; background: transparent; color: inherit;\n font: inherit; text-align: left; cursor: pointer;\n}\n.sort-button:hover { color: var(--fg); }\n.sort-button.sort-active { color: var(--accent-strong); }\n.sort-indicator { color: var(--faint); font-size: 0.8em; min-width: 1em; }\n.sort-button.sort-active .sort-indicator { color: var(--accent-strong); }\n.table-filter-toggle {\n display: inline-flex; align-items: center; justify-content: center;\n width: 2.25rem; height: 2.25rem; padding: 0;\n border: 1px solid var(--line); border-radius: 8px;\n background: var(--bg-raised); color: var(--muted); cursor: pointer;\n}\n.table-filter-toggle:hover { color: var(--fg); border-color: color-mix(in srgb, var(--accent) 40%, var(--line)); }\n.table-filter-toggle.is-active {\n color: var(--fg);\n border-color: var(--accent);\n background: color-mix(in srgb, var(--accent) 18%, var(--bg-raised));\n}\n.table-filter-toggle.has-filters:not(.is-active) {\n color: var(--accent-strong);\n border-color: color-mix(in srgb, var(--accent) 45%, var(--line));\n}\n.table-filter-toggle svg { width: 1rem; height: 1rem; display: block; }\n.table-filter-actions {\n display: flex; justify-content: flex-end; gap: 0.55rem;\n margin-bottom: 0.65rem;\n}\n.column-filter-row { display: none; }\n.column-filter-row.is-open { display: table-row; }\n.column-filter-row th {\n padding: 0.35rem 0.4rem 0.75rem; border-bottom: 1px solid var(--line); font-weight: 400;\n}\n.column-filter {\n width: 100%; min-width: 4.5rem; background: var(--bg); color: var(--fg);\n border: 1px solid var(--line); border-radius: 6px; padding: 0.4rem 0.55rem; font-size: 0.85rem;\n}\n.column-filter:focus {\n outline: none;\n border-color: var(--accent);\n box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 25%, transparent);\n}\nbody[data-runtime-mode=\"interactive\"] .scroll-window thead tr:first-child th {\n position: sticky;\n top: 0;\n z-index: 2;\n background: var(--bg-raised);\n box-shadow: 0 1px 0 var(--line);\n}\nbody[data-runtime-mode=\"interactive\"] .scroll-window thead .column-filter-row.is-open th {\n position: sticky;\n top: 2.65rem;\n z-index: 1;\n background: var(--bg-raised);\n box-shadow: 0 1px 0 var(--line);\n}\n.entity-list { display: grid; gap: 1.5rem; }\n.entity-card {\n border: 1px solid var(--line); border-radius: 10px; padding: 0.9rem 1rem; background: var(--bg);\n color: inherit;\n}\n.entity-card:hover { border-color: color-mix(in srgb, var(--accent) 40%, var(--line)); }\n.entity-card h3, .entity-card p { margin: 0; }\n.entity-card p + p, .entity-card strong + p, .entity-card h3 + p { margin-top: 0.35rem; }\n.panel > h2, .table-shell > h2, .graph-shell > h2, .print-card > h3 { margin-top: 0; margin-bottom: 0.75rem; }\n.toolbar h2 { margin: 0; }\n.rollup-list { display: grid; gap: 0.5rem; }\n.rollup-list div {\n display: flex; justify-content: space-between; gap: 1rem; border-bottom: 1px solid var(--line); padding-bottom: 0.4rem;\n}\n.rollup-list dd { margin: 0; }\n.distribution-track {\n height: 0.7rem;\n min-width: 6rem;\n border-radius: 999px;\n background: var(--bg);\n border: 1px solid var(--line);\n overflow: hidden;\n}\n.distribution-fill {\n display: block;\n height: 100%;\n border-radius: inherit;\n background: linear-gradient(90deg, var(--accent-dim), var(--accent-strong));\n min-width: 0;\n}\n.graph-root canvas {\n display: block;\n width: 100%;\n min-height: 620px;\n background: var(--bg);\n border-radius: 10px;\n border: 1px solid var(--line);\n touch-action: none;\n cursor: grab;\n}\n.graph-root canvas.is-panning { cursor: grabbing; }\n.graph-root canvas.is-dragging-node { cursor: pointer; }\n.search-results { display: grid; gap: 0.7rem; margin-top: 1rem; }\n.search-result:hover { border-color: var(--accent-dim); }\n.hidden { display: none !important; }\n.breadcrumbs a { color: var(--muted); }\n.breadcrumbs a:hover { color: var(--accent-strong); }\n.section-link { font-size: 0.92rem; }\n.prose p { line-height: 1.55; }\n.idea-summary { white-space: normal; }\n.idea-ref {\n display: inline;\n padding: 0.05em 0.35em;\n margin: 0 0.05em;\n border-radius: 0.3em;\n border: 1px solid color-mix(in srgb, var(--accent) 35%, var(--line));\n background: color-mix(in srgb, var(--accent) 14%, var(--bg-raised));\n color: var(--accent-strong);\n font-weight: 600;\n text-decoration: none;\n white-space: nowrap;\n}\na.idea-ref:hover {\n color: var(--fg);\n border-color: var(--accent);\n background: color-mix(in srgb, var(--accent) 22%, var(--bg-raised));\n}\n.idea-ref--file {\n border-color: color-mix(in srgb, var(--rust) 40%, var(--line));\n background: color-mix(in srgb, var(--rust) 16%, var(--bg-raised));\n color: #e8a07a;\n}\na.idea-ref--file:hover {\n color: var(--fg);\n border-color: var(--rust);\n}\n.idea-ref--unresolved {\n border-style: dashed;\n border-color: color-mix(in srgb, var(--warn) 45%, var(--line));\n background: color-mix(in srgb, var(--warn) 10%, var(--bg-raised));\n color: var(--warn);\n font-weight: 500;\n}\npre.code-like {\n white-space: pre-wrap; padding: 0.9rem; border-radius: 8px;\n background: var(--bg); border: 1px solid var(--line);\n}\n@media (max-width: 900px) {\n .hero, .topbar-inner { grid-template-columns: 1fr; }\n}\n@media print {\n :root { color-scheme: light; }\n body { background: white; color: black; }\n .topbar, .searchbar, .toolbar .actions, .graph-shell .toolbar, .column-filter-row, .table-filter-toggle, .table-filter-actions, .hide-on-print { display: none !important; }\n .sort-button { cursor: default; }\n .panel, .metric, .card, .table-shell, .graph-shell, .print-card, .search-result, .entity-card {\n background: white; color: black; border-color: #cbd5e1; box-shadow: none;\n }\n .scroll-window {\n max-height: none !important;\n overflow: visible !important;\n border: none !important;\n background: transparent !important;\n }\n .scroll-window thead th {\n position: static !important;\n box-shadow: none !important;\n }\n a { color: black; text-decoration: underline; }\n .print-break-avoid { break-inside: avoid; }\n}\n";
|
|
2
|
+
export declare const APP_JS: string;
|
|
3
3
|
export declare function buildSearchIndexScript(documents: unknown): string;
|