@psnext/lscg 0.1.1 → 0.1.3
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 +69 -3
- package/dist/src/cli.js +68 -13
- package/dist/src/graph/repository.d.ts +4 -1
- package/dist/src/graph/repository.js +106 -6
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.js +2 -0
- package/dist/src/mcp/server.js +2 -2
- package/dist/src/scanner/discover.js +0 -2
- package/dist/src/scanner/packagePlugin.d.ts +3 -0
- package/dist/src/scanner/packagePlugin.js +125 -0
- package/dist/src/scanner/plugins.d.ts +86 -0
- package/dist/src/scanner/plugins.js +503 -0
- package/dist/src/storage/connection.d.ts +6 -0
- package/dist/src/storage/connection.js +133 -0
- package/dist/src/storage/context-queries.d.ts +20 -0
- package/dist/src/storage/context-queries.js +137 -0
- package/dist/src/storage/database.d.ts +12 -71
- package/dist/src/storage/database.js +12 -562
- package/dist/src/storage/graph-writes.d.ts +17 -0
- package/dist/src/storage/graph-writes.js +126 -0
- package/dist/src/storage/plugin-contributions.d.ts +15 -0
- package/dist/src/storage/plugin-contributions.js +28 -0
- package/dist/src/storage/plugin-graph.d.ts +6 -0
- package/dist/src/storage/plugin-graph.js +81 -0
- package/dist/src/storage/queries.d.ts +25 -0
- package/dist/src/storage/queries.js +129 -0
- package/dist/src/storage/row-decoders.d.ts +8 -0
- package/dist/src/storage/row-decoders.js +37 -0
- package/dist/src/storage/schema.d.ts +2 -2
- package/dist/src/storage/schema.js +13 -2
- package/dist/src/storage/traversal-queries.d.ts +15 -0
- package/dist/src/storage/traversal-queries.js +87 -0
- package/dist/src/types.d.ts +10 -4
- package/dist/src/view/index.d.ts +1 -1
- package/dist/src/view/index.js +2 -2
- package/dist/src/view/model.d.ts +2 -0
- package/dist/src/view/render.d.ts +5 -2
- package/dist/src/view/render.js +81 -13
- package/dist/src/view/templates/icons/call.svg +11 -0
- package/dist/src/view/templates/icons/export.svg +1 -0
- package/dist/src/view/templates/icons/file.svg +9 -0
- package/dist/src/view/templates/icons/import.svg +1 -0
- package/dist/src/view/templates/icons/package.svg +1 -0
- package/dist/src/view/templates/icons/symbol.svg +7 -0
- package/dist/src/view/templates/icons/user.svg +15 -0
- package/dist/src/view/templates/interactive.css +272 -0
- package/dist/src/view/templates/interactive.html +642 -0
- package/package.json +2 -1
package/dist/src/view/index.d.ts
CHANGED
|
@@ -21,7 +21,7 @@ export interface ViewRunDependencies {
|
|
|
21
21
|
}
|
|
22
22
|
export declare function viewGraph(options?: ViewCommandOptions, dependencies?: ViewRunDependencies): Promise<ViewRunResult>;
|
|
23
23
|
export { buildViewModel, loadViewSnapshot } from './model.js';
|
|
24
|
-
export type { ViewSnapshot, ViewNode, ViewEdge, ViewBox, ViewModel } from './model.js';
|
|
24
|
+
export type { DisplayMode, ViewSnapshot, ViewNode, ViewEdge, ViewBox, ViewModel } from './model.js';
|
|
25
25
|
export { renderInteractiveHtml, renderSvgMarkup, writeSvgSnapshot } from './render.js';
|
|
26
26
|
export { openHtmlArtifactInBrowser, openInDefaultBrowser, writeTemporaryHtmlArtifact } from './open.js';
|
|
27
27
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/src/view/index.js
CHANGED
|
@@ -7,7 +7,7 @@ export async function viewGraph(options = {}, dependencies = {}) {
|
|
|
7
7
|
const snapshot = loadViewSnapshot(options);
|
|
8
8
|
const model = buildViewModel(snapshot, options);
|
|
9
9
|
if (options.output) {
|
|
10
|
-
const svg = renderSvgMarkup(model, { includeHiddenNodes: false, interactive: false });
|
|
10
|
+
const svg = renderSvgMarkup(model, { includeHiddenNodes: false, interactive: false, display: options.display });
|
|
11
11
|
mkdirSync(path.dirname(options.output), { recursive: true });
|
|
12
12
|
writeSvgSnapshot(options.output, svg);
|
|
13
13
|
return {
|
|
@@ -21,7 +21,7 @@ export async function viewGraph(options = {}, dependencies = {}) {
|
|
|
21
21
|
edgeCount: model.edges.length
|
|
22
22
|
};
|
|
23
23
|
}
|
|
24
|
-
const html = renderInteractiveHtml(model);
|
|
24
|
+
const html = renderInteractiveHtml(model, { display: options.display });
|
|
25
25
|
const opened = openHtmlArtifactInBrowser(html, dependencies.opener);
|
|
26
26
|
return {
|
|
27
27
|
mode: 'interactive',
|
package/dist/src/view/model.d.ts
CHANGED
|
@@ -43,11 +43,13 @@ export interface ViewModel extends ViewSnapshot {
|
|
|
43
43
|
graphBounds: LayoutBounds;
|
|
44
44
|
startWithFullGraph: boolean;
|
|
45
45
|
}
|
|
46
|
+
export type DisplayMode = 'simple' | 'shape' | 'icon';
|
|
46
47
|
export interface ViewModelOptions {
|
|
47
48
|
root?: string | undefined;
|
|
48
49
|
scope?: StorageScope | 'both' | undefined;
|
|
49
50
|
anchor?: string | undefined;
|
|
50
51
|
search?: string | undefined;
|
|
52
|
+
display?: DisplayMode | undefined;
|
|
51
53
|
}
|
|
52
54
|
export declare function loadViewSnapshot({ root, scope }?: ViewModelOptions): ViewSnapshot;
|
|
53
55
|
export declare function buildViewModel(snapshot: ViewSnapshot, { anchor, search }?: ViewModelOptions): ViewModel;
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
import type { ViewModel } from './model.js';
|
|
1
|
+
import type { DisplayMode, ViewModel } from './model.js';
|
|
2
2
|
export interface RenderSvgOptions {
|
|
3
3
|
includeHiddenNodes?: boolean | undefined;
|
|
4
4
|
interactive?: boolean | undefined;
|
|
5
|
+
display?: DisplayMode | undefined;
|
|
5
6
|
}
|
|
6
7
|
export declare function renderSvgMarkup(model: ViewModel, options?: RenderSvgOptions): string;
|
|
7
|
-
export declare function renderInteractiveHtml(model: ViewModel
|
|
8
|
+
export declare function renderInteractiveHtml(model: ViewModel, options?: {
|
|
9
|
+
display?: DisplayMode | undefined;
|
|
10
|
+
}): string;
|
|
8
11
|
export declare function writeSvgSnapshot(filePath: string, svgMarkup: string): void;
|
|
9
12
|
//# sourceMappingURL=render.d.ts.map
|
package/dist/src/view/render.js
CHANGED
|
@@ -15,6 +15,8 @@ export function renderSvgMarkup(model, options = {}) {
|
|
|
15
15
|
const graphLabel = `${model.repository.name} store graph`;
|
|
16
16
|
const title = options.includeHiddenNodes ? `${graphLabel} · full graph` : graphLabel;
|
|
17
17
|
const mode = options.interactive ? 'interactive' : 'snapshot';
|
|
18
|
+
const display = options.display ?? 'shape';
|
|
19
|
+
const icons = display === 'icon' ? loadIconAssets() : null;
|
|
18
20
|
const renderedEdges = edges
|
|
19
21
|
.map((edge) => {
|
|
20
22
|
const source = nodes.find((node) => node.id === edge.sourceId);
|
|
@@ -28,10 +30,10 @@ export function renderSvgMarkup(model, options = {}) {
|
|
|
28
30
|
const renderedNodes = nodes
|
|
29
31
|
.map((node) => {
|
|
30
32
|
const presentation = nodePresentation(node);
|
|
31
|
-
const labelY = node.kind === 'file' ? 0 : presentation.height / 2 + 14;
|
|
33
|
+
const labelY = display === 'shape' && node.kind === 'file' ? 0 : presentation.height / 2 + 14;
|
|
32
34
|
return `
|
|
33
35
|
<g class="node node--${escapeHtml(node.kind)}" data-node-id="${escapeHtml(node.id)}" data-node-kind="${escapeHtml(node.kind)}" data-node-shape="${escapeHtml(presentation.shape)}" transform="translate(${formatNumber(node.x)},${formatNumber(node.y)})">
|
|
34
|
-
${renderNodeShapeMarkup(node, presentation)}
|
|
36
|
+
${renderNodeShapeMarkup(node, presentation, display, icons)}
|
|
35
37
|
<text class="node-label" text-anchor="middle" dominant-baseline="${node.kind === 'file' ? 'middle' : 'hanging'}" y="${formatNumber(labelY)}">${escapeHtml(node.label)}</text>
|
|
36
38
|
<title>${escapeHtml(node.label)} (${escapeHtml(node.id)})</title>
|
|
37
39
|
</g>`;
|
|
@@ -70,8 +72,8 @@ export function renderSvgMarkup(model, options = {}) {
|
|
|
70
72
|
}
|
|
71
73
|
const interactiveHtmlTemplate = loadTemplateAsset('interactive.html');
|
|
72
74
|
const interactiveCss = loadTemplateAsset('interactive.css');
|
|
73
|
-
export function renderInteractiveHtml(model) {
|
|
74
|
-
const data = serializeInteractiveGraph(model);
|
|
75
|
+
export function renderInteractiveHtml(model, options = {}) {
|
|
76
|
+
const data = serializeInteractiveGraph(model, options.display ?? 'shape');
|
|
75
77
|
const graphTitle = `${model.repository.name} store graph`;
|
|
76
78
|
const subtitle = model.anchorNodeId
|
|
77
79
|
? `Anchored on ${model.nodes.find((node) => node.id === model.anchorNodeId)?.label ?? model.anchorNodeId}`
|
|
@@ -103,8 +105,19 @@ export function writeSvgSnapshot(filePath, svgMarkup) {
|
|
|
103
105
|
mkdirSync(path.dirname(filePath), { recursive: true });
|
|
104
106
|
writeFileSync(filePath, svgMarkup, 'utf8');
|
|
105
107
|
}
|
|
106
|
-
function serializeInteractiveGraph(model) {
|
|
107
|
-
const
|
|
108
|
+
function serializeInteractiveGraph(model, display) {
|
|
109
|
+
const serializedGraph = model.anchorNodeId
|
|
110
|
+
? downstreamGraph(model.fullNodes, model.fullEdges, model.anchorNodeId)
|
|
111
|
+
: { nodes: model.fullNodes, edges: model.fullEdges };
|
|
112
|
+
const serializedNodeIds = new Set(serializedGraph.nodes.map((node) => node.id));
|
|
113
|
+
const serializedEdgeIds = new Set(serializedGraph.edges.map((edge) => edge.id));
|
|
114
|
+
const neighborhoods = Object.fromEntries(Object.entries(model.fullNeighborhoods)
|
|
115
|
+
.filter(([nodeId]) => serializedNodeIds.has(nodeId))
|
|
116
|
+
.map(([nodeId, neighborhood]) => [nodeId, {
|
|
117
|
+
nodeIds: neighborhood.nodeIds.filter((id) => serializedNodeIds.has(id)),
|
|
118
|
+
edgeIds: neighborhood.edgeIds.filter((id) => serializedEdgeIds.has(id))
|
|
119
|
+
}]));
|
|
120
|
+
const nodes = serializedGraph.nodes.map((node) => {
|
|
108
121
|
const presentation = nodePresentation(node);
|
|
109
122
|
return {
|
|
110
123
|
id: node.id,
|
|
@@ -122,7 +135,7 @@ function serializeInteractiveGraph(model) {
|
|
|
122
135
|
presentationRadius: presentation.presentationRadius
|
|
123
136
|
};
|
|
124
137
|
});
|
|
125
|
-
const edges =
|
|
138
|
+
const edges = serializedGraph.edges.map((edge) => ({
|
|
126
139
|
id: edge.id,
|
|
127
140
|
kind: edge.kind,
|
|
128
141
|
source: edge.sourceId,
|
|
@@ -135,12 +148,56 @@ function serializeInteractiveGraph(model) {
|
|
|
135
148
|
scope: model.scope,
|
|
136
149
|
search: model.search,
|
|
137
150
|
anchorNodeId: model.anchorNodeId,
|
|
138
|
-
|
|
151
|
+
display,
|
|
152
|
+
icons: loadIconAssets(),
|
|
153
|
+
fullNeighborhoods: neighborhoods,
|
|
139
154
|
nodes,
|
|
140
155
|
edges
|
|
141
156
|
}));
|
|
142
157
|
}
|
|
143
|
-
function
|
|
158
|
+
function downstreamGraph(nodes, edges, anchorNodeId) {
|
|
159
|
+
const downstreamByNodeId = new Map();
|
|
160
|
+
for (const edge of edges) {
|
|
161
|
+
const downstream = downstreamByNodeId.get(edge.sourceId) ?? [];
|
|
162
|
+
downstream.push(edge.targetId);
|
|
163
|
+
downstreamByNodeId.set(edge.sourceId, downstream);
|
|
164
|
+
}
|
|
165
|
+
const selectedNodeIds = new Set([anchorNodeId]);
|
|
166
|
+
const queue = [anchorNodeId];
|
|
167
|
+
for (let index = 0; index < queue.length; index += 1) {
|
|
168
|
+
const nodeId = queue[index];
|
|
169
|
+
for (const downstreamNodeId of downstreamByNodeId.get(nodeId) ?? []) {
|
|
170
|
+
if (selectedNodeIds.has(downstreamNodeId))
|
|
171
|
+
continue;
|
|
172
|
+
selectedNodeIds.add(downstreamNodeId);
|
|
173
|
+
queue.push(downstreamNodeId);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return {
|
|
177
|
+
nodes: nodes.filter((node) => selectedNodeIds.has(node.id)),
|
|
178
|
+
edges: edges.filter((edge) => selectedNodeIds.has(edge.sourceId) && selectedNodeIds.has(edge.targetId))
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
const iconKinds = ['file', 'symbol', 'import', 'export', 'call', 'user', 'package'];
|
|
182
|
+
function loadIconAssets() {
|
|
183
|
+
return Object.fromEntries(iconKinds.map((kind) => {
|
|
184
|
+
const source = loadTemplateAsset(`icons/${kind}.svg`);
|
|
185
|
+
const match = source.match(/<svg[^>]*viewBox="([^"]+)"[^>]*>([\s\S]*)<\/svg>/);
|
|
186
|
+
if (!match)
|
|
187
|
+
throw new Error(`Invalid icon asset for ${kind}`);
|
|
188
|
+
return [kind, { viewBox: match[1], markup: match[2] }];
|
|
189
|
+
}));
|
|
190
|
+
}
|
|
191
|
+
function renderNodeShapeMarkup(node, presentation, display, icons) {
|
|
192
|
+
if (display === 'simple') {
|
|
193
|
+
return `<circle class="node-shape node-shape--simple" cx="0" cy="0" r="${formatNumber(Math.min(presentation.width, presentation.height) / 2)}" fill="${colorForKind(node.kind)}" />`;
|
|
194
|
+
}
|
|
195
|
+
if (display === 'icon') {
|
|
196
|
+
const icon = icons?.[node.kind];
|
|
197
|
+
if (!icon)
|
|
198
|
+
throw new Error(`Missing icon asset for ${node.kind}`);
|
|
199
|
+
return `<svg class="node-shape node-shape--icon" x="${formatNumber(-presentation.width / 2)}" y="${formatNumber(-presentation.height / 2)}" width="${formatNumber(presentation.width)}" height="${formatNumber(presentation.height)}" viewBox="${escapeHtml(icon.viewBox)}" color="${colorForKind(node.kind)}" aria-hidden="true">${icon.markup}</svg>`;
|
|
200
|
+
}
|
|
144
201
|
const fill = colorForKind(node.kind);
|
|
145
202
|
switch (presentation.shape) {
|
|
146
203
|
case 'rounded-rect':
|
|
@@ -155,6 +212,8 @@ function renderNodeShapeMarkup(node, presentation) {
|
|
|
155
212
|
return `<polygon class="node-shape node-shape--hexagon" points="${shapePoints(presentation, 'hexagon')}" fill="${fill}" />`;
|
|
156
213
|
case 'user':
|
|
157
214
|
return renderUserNodeShapeMarkup(presentation, fill);
|
|
215
|
+
case 'package':
|
|
216
|
+
return renderPackageNodeShapeMarkup(presentation);
|
|
158
217
|
default:
|
|
159
218
|
return `<rect class="node-shape" x="${formatNumber(-presentation.width / 2)}" y="${formatNumber(-presentation.height / 2)}" width="${formatNumber(presentation.width)}" height="${formatNumber(presentation.height)}" rx="6" ry="6" fill="${fill}" />`;
|
|
160
219
|
}
|
|
@@ -173,12 +232,12 @@ function nodePresentation(node) {
|
|
|
173
232
|
const shape = shapeForKind(node.kind);
|
|
174
233
|
const width = node.kind === 'file'
|
|
175
234
|
? shapeWidthForKind(node.kind, labelLength)
|
|
176
|
-
: node.kind === 'user'
|
|
235
|
+
: node.kind === 'user' || node.kind === 'package'
|
|
177
236
|
? 72
|
|
178
237
|
: 16;
|
|
179
238
|
const height = node.kind === 'file'
|
|
180
239
|
? shapeHeightForKind(node.kind, labelLength)
|
|
181
|
-
: node.kind === 'user'
|
|
240
|
+
: node.kind === 'user' || node.kind === 'package'
|
|
182
241
|
? 72
|
|
183
242
|
: 16;
|
|
184
243
|
const presentationRadius = Math.max(width, height) / 2 + 10;
|
|
@@ -204,6 +263,8 @@ function shapeForKind(kind) {
|
|
|
204
263
|
return 'hexagon';
|
|
205
264
|
case 'user':
|
|
206
265
|
return 'user';
|
|
266
|
+
case 'package':
|
|
267
|
+
return 'hexagon';
|
|
207
268
|
default:
|
|
208
269
|
return 'box';
|
|
209
270
|
}
|
|
@@ -226,7 +287,8 @@ function shapeSquareSizeForKind(kind, labelLength) {
|
|
|
226
287
|
import: { base: 68, perChar: 7, min: 96, max: 220 },
|
|
227
288
|
export: { base: 68, perChar: 7, min: 96, max: 220 },
|
|
228
289
|
call: { base: 68, perChar: 7, min: 96, max: 220 },
|
|
229
|
-
user: { base: 68, perChar: 7, min: 72, max: 180 }
|
|
290
|
+
user: { base: 68, perChar: 7, min: 72, max: 180 },
|
|
291
|
+
package: { base: 72, perChar: 7, min: 96, max: 220 }
|
|
230
292
|
};
|
|
231
293
|
const spec = sizeByKind[kind];
|
|
232
294
|
return Math.max(spec.min, Math.min(spec.max, Math.round(spec.base + labelLength * spec.perChar)));
|
|
@@ -239,6 +301,9 @@ function expandViewBox(viewBox, padding) {
|
|
|
239
301
|
height: viewBox.height + padding * 2
|
|
240
302
|
};
|
|
241
303
|
}
|
|
304
|
+
function renderPackageNodeShapeMarkup(presentation) {
|
|
305
|
+
return `<svg class="node-shape node-shape--package" x="${formatNumber(-presentation.width / 2)}" y="${formatNumber(-presentation.height / 2)}" width="${formatNumber(presentation.width)}" height="${formatNumber(presentation.height)}" viewBox="0 0 512 512" aria-hidden="true"><polygon fill="#7E4F1F" points="511.516,107.105 511.788,107.036 264.065,31.004 0.479,106.898 0,106.759 0,411.849 238.676,480.721 238.676,480.159 238.888,480.996 512,412.125 512,107.036"/><polygon fill="#EDA637" points="264.065,31.004 0,107.036 238.676,175.907 511.788,107.036"/><polygon fill="#C27526" points="238.888,480.996 512,412.125 512,107.036 238.888,175.907"/><path fill="#FFEC99" d="M193.016 51.462 78.638 84.393l230.233 73.866v50.416c0 3.323 1.541 6.458 4.172 8.487 2.631 2.029 6.053 2.725 9.268 1.881l99.058-25.986c4.713-1.237 7.999-5.495 7.999-10.369v-54.816L193.016 51.462Z"/><path fill="#FFDC35" d="M486.158 374.572c0 6.29-4.278 11.775-10.378 13.307l-52.647 13.55c-2.779.698-5.736.663-8.41-.367-5.363-2.066-8.677-7.119-8.677-12.576V332.26c0-6.29 4.278-11.775 10.378-13.307l52.647-13.55c2.779-.698 5.736-.663 8.41.367 5.363 2.066 8.677 7.119 8.677 12.576v56.226Z"/><path fill="#FFDC35" d="M429.367 182.689c0 4.872-3.287 9.132-7.999 10.368l-99.058 25.986c-3.214.843-6.637.148-9.268-1.881-2.631-2.029-4.172-5.164-4.172-8.487v-50.416l120.497-30.386v54.816Z"/><path fill="#DF7400" d="m186.692 292.407-40.804-11.978c-5.273-1.548-8.895-6.385-8.895-11.881v-39.76c0-8.263 7.941-14.208 15.87-11.881l40.804 11.978c5.273 1.548 8.895 6.385 8.895 11.881v39.76c0 8.263-7.941 14.208-15.87 11.881Z"/></svg>`;
|
|
306
|
+
}
|
|
242
307
|
function renderUserNodeShapeMarkup(presentation, fill) {
|
|
243
308
|
const scale = Math.max(presentation.width, presentation.height) / 24;
|
|
244
309
|
return `<g class="node-shape node-shape--user" transform="scale(${formatNumber(scale)}) translate(-12,-12)" fill="none" stroke="${fill}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
|
@@ -261,6 +326,8 @@ function colorForKind(kind) {
|
|
|
261
326
|
return '#fb7185';
|
|
262
327
|
case 'user':
|
|
263
328
|
return '#14b8a6';
|
|
329
|
+
case 'package':
|
|
330
|
+
return '#f97316';
|
|
264
331
|
default:
|
|
265
332
|
return '#94a3b8';
|
|
266
333
|
}
|
|
@@ -296,7 +363,8 @@ function legendItems() {
|
|
|
296
363
|
['import', 'Import'],
|
|
297
364
|
['export', 'Export'],
|
|
298
365
|
['call', 'Call'],
|
|
299
|
-
['user', 'Contributor']
|
|
366
|
+
['user', 'Contributor'],
|
|
367
|
+
['package', 'Package']
|
|
300
368
|
];
|
|
301
369
|
return items
|
|
302
370
|
.map(([kind, label]) => `
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<svg viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<g id="call_icon">
|
|
3
|
+
<circle cx="24" cy="24" r="21" fill="#ff6d2e" stroke="inherit" stroke-width="4"></circle>
|
|
4
|
+
<path d="M37 22.0001L34 25.0001L23 14.0001L26 11.0001C27.5 9.50002 33 7.00005 37 11.0001C41 15.0001 38.5 20.5 37 22.0001Z" fill="#b71111" stroke="#3a1304" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path>
|
|
5
|
+
<path d="M42 6L37 11" stroke="#3a1304" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path>
|
|
6
|
+
<path d="M11 25.9999L14 22.9999L25 33.9999L22 36.9999C20.5 38.5 15 41 11 36.9999C7 32.9999 9.5 27.5 11 25.9999Z" fill="#b71111" stroke="#3a1304" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path>
|
|
7
|
+
<path d="M23 32L27 28" stroke="#3a1304" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path>
|
|
8
|
+
<path d="M6 42L11 37" stroke="#3a1304" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path>
|
|
9
|
+
<path d="M16 25L20 21" stroke="#3a1304" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path>
|
|
10
|
+
</g>
|
|
11
|
+
</svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M12 21V8M7 13l5-5 5 5M5 3h14"/></svg>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" >
|
|
2
|
+
<g id="file_icon" fill="#9ed2ff" stroke="#1d97e2" stroke-width="1" stroke-linecap="round" stroke-linejoin="miter">
|
|
3
|
+
<polygon points="14 2 3 2 3 22 21 22 21 9 14 9 14 2" fill="#059cf7" opacity="0.1" stroke-width="0"></polygon>
|
|
4
|
+
<polygon points="3 2 3 22 21 22 21 9 14 2 3 2"></polygon>
|
|
5
|
+
<polyline points="20.6 9 14 9 14 2.4" stroke-linecap="round"></polyline>
|
|
6
|
+
<line x1="16" y1="13" x2="8" y2="13"></line><line x1="10" y1="9" x2="8" y2="9"></line>
|
|
7
|
+
<line x1="16" y1="17" x2="8" y2="17"></line>
|
|
8
|
+
</g>
|
|
9
|
+
</svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3v13M7 11l5 5 5-5M5 21h14"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg viewBox="0 0 512 512"><polygon fill="#7E4F1F" points="511.516,107.105 511.788,107.036 264.065,31.004 0.479,106.898 0,106.759 0,411.849 238.676,480.721 238.676,480.159 238.888,480.996 512,412.125 512,107.036"/><polygon fill="#EDA637" points="264.065,31.004 0,107.036 238.676,175.907 511.788,107.036"/><polygon fill="#C27526" points="238.888,480.996 512,412.125 512,107.036 238.888,175.907"/><path fill="#FFEC99" d="M193.016 51.462 78.638 84.393l230.233 73.866v50.416c0 3.323 1.541 6.458 4.172 8.487 2.631 2.029 6.053 2.725 9.268 1.881l99.058-25.986c4.713-1.237 7.999-5.495 7.999-10.369v-54.816L193.016 51.462Z"/><path fill="#FFDC35" d="M486.158 374.572c0 6.29-4.278 11.775-10.378 13.307l-52.647 13.55c-2.779.698-5.736.663-8.41-.367-5.363-2.066-8.677-7.119-8.677-12.576V332.26c0-6.29 4.278-11.775 10.378-13.307l52.647-13.55c2.779-.698 5.736-.663 8.41.367 5.363 2.066 8.677 7.119 8.677 12.576v56.226Z"/><path fill="#FFDC35" d="M429.367 182.689c0 4.872-3.287 9.132-7.999 10.368l-99.058 25.986c-3.214.843-6.637.148-9.268-1.881-2.631-2.029-4.172-5.164-4.172-8.487v-50.416l120.497-30.386v54.816Z"/><path fill="#DF7400" d="m186.692 292.407-40.804-11.978c-5.273-1.548-8.895-6.385-8.895-11.881v-39.76c0-8.263 7.941-14.208 15.87-11.881l40.804 11.978c5.273 1.548 8.895 6.385 8.895 11.881v39.76c0 8.263-7.941 14.208-15.87 11.881Z"/></svg>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<svg viewBox="0 0 36 36" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" preserveAspectRatio="xMidYMid meet">
|
|
2
|
+
<g id="symbol_icon" fill="none" fill-rule="evenodd">
|
|
3
|
+
<path fill="#d5c007" d="M17 36a1.99 1.99 0 0 1-1.414-.586l-11-11A2 2 0 0 1 4 23V2A2 2 0 0 1 7.414.586L17 10.171L26.586.586A1.998 1.998 0 0 1 30 2v21a2 2 0 0 1-.586 1.414l-11 11A1.99 1.99 0 0 1 17 36z"></path>
|
|
4
|
+
<path fill="#47DED4" d="M17 13L28 2v21L17 34z"></path>
|
|
5
|
+
<path fill="#FFFF87" d="M6 2l11 11v21L6 23z"></path>
|
|
6
|
+
</g>
|
|
7
|
+
</svg>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" >
|
|
2
|
+
<g id="user_icon" fill="#c7e2f9" stroke="#1995d8"
|
|
3
|
+
stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round">
|
|
4
|
+
<defs>
|
|
5
|
+
<style>.cls-1{fill:#c7e2f9;}.cls-1,.cls-2{fill-rule:evenodd;}.cls-2{fill:#4285f4;}</style>
|
|
6
|
+
</defs>
|
|
7
|
+
<g data-name="Product Icons">
|
|
8
|
+
<g>
|
|
9
|
+
<rect fill="#234cbc" x="6" y="4" width="12" height="16" rx="4"></rect>
|
|
10
|
+
<path class="cls-1" d="M12,2,3.79,5.42v5.63c0,5.06,3.5,9.8,8.21,11,4.71-1.15,8.21-5.89,8.21-10.95V5.42Zm0,3.79a2.63,2.63,0,1,1-1.86.77A2.63,2.63,0,0,1,12,5.79Zm4.11,11.15A8.64,8.64,0,0,1,12,19.87a8.64,8.64,0,0,1-4.11-2.93V14.69c0-1.67,2.74-2.52,4.11-2.52s4.11.85,4.11,2.52v2.25Z"></path>
|
|
11
|
+
<path class="cls-2" d="M12,2V5.79a2.63,2.63,0,1,1,0,5.26v1.12c1.37,0,4.11.85,4.11,2.52v2.25A8.64,8.64,0,0,1,12,19.87V22c4.71-1.15,8.21-5.89,8.21-10.95V5.42Z"></path>
|
|
12
|
+
</g>
|
|
13
|
+
</g>
|
|
14
|
+
</g>
|
|
15
|
+
</svg>
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
|
|
2
|
+
:root {
|
|
3
|
+
color-scheme: dark;
|
|
4
|
+
--bg: #020617;
|
|
5
|
+
--panel: rgba(15, 23, 42, 0.88);
|
|
6
|
+
--panel-border: rgba(148, 163, 184, 0.2);
|
|
7
|
+
--text: #e2e8f0;
|
|
8
|
+
--muted: #94a3b8;
|
|
9
|
+
}
|
|
10
|
+
html, body {
|
|
11
|
+
margin: 0;
|
|
12
|
+
width: 100%;
|
|
13
|
+
height: 100%;
|
|
14
|
+
overflow: hidden;
|
|
15
|
+
background: var(--bg);
|
|
16
|
+
color: var(--text);
|
|
17
|
+
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
18
|
+
}
|
|
19
|
+
#app {
|
|
20
|
+
display: flex;
|
|
21
|
+
width: 100vw;
|
|
22
|
+
height: 100vh;
|
|
23
|
+
}
|
|
24
|
+
.graph-panel {
|
|
25
|
+
flex: 1 1 auto;
|
|
26
|
+
min-width: 0;
|
|
27
|
+
position: relative;
|
|
28
|
+
}
|
|
29
|
+
.hud {
|
|
30
|
+
width: 320px;
|
|
31
|
+
flex: 0 0 320px;
|
|
32
|
+
box-sizing: border-box;
|
|
33
|
+
margin: 16px 16px 16px 0;
|
|
34
|
+
display: grid;
|
|
35
|
+
align-content: start;
|
|
36
|
+
gap: 12px;
|
|
37
|
+
padding: 14px 16px;
|
|
38
|
+
border: 1px solid var(--panel-border);
|
|
39
|
+
border-radius: 16px;
|
|
40
|
+
background: var(--panel);
|
|
41
|
+
-webkit-backdrop-filter: blur(16px);
|
|
42
|
+
backdrop-filter: blur(16px);
|
|
43
|
+
box-shadow: 0 18px 50px rgba(15, 23, 42, 0.35);
|
|
44
|
+
overflow: auto;
|
|
45
|
+
}
|
|
46
|
+
.section {
|
|
47
|
+
display: grid;
|
|
48
|
+
gap: 8px;
|
|
49
|
+
}
|
|
50
|
+
.section-title {
|
|
51
|
+
margin: 0;
|
|
52
|
+
color: var(--muted);
|
|
53
|
+
font-size: 12px;
|
|
54
|
+
font-weight: 600;
|
|
55
|
+
letter-spacing: 0.04em;
|
|
56
|
+
text-transform: uppercase;
|
|
57
|
+
}
|
|
58
|
+
.info-grid {
|
|
59
|
+
display: grid;
|
|
60
|
+
gap: 8px;
|
|
61
|
+
font-size: 13px;
|
|
62
|
+
}
|
|
63
|
+
.info-row {
|
|
64
|
+
display: grid;
|
|
65
|
+
grid-template-columns: 92px minmax(0, 1fr);
|
|
66
|
+
gap: 10px;
|
|
67
|
+
}
|
|
68
|
+
.info-label {
|
|
69
|
+
color: var(--muted);
|
|
70
|
+
}
|
|
71
|
+
.info-value {
|
|
72
|
+
overflow-wrap: anywhere;
|
|
73
|
+
}
|
|
74
|
+
.info-empty {
|
|
75
|
+
color: var(--muted);
|
|
76
|
+
font-size: 13px;
|
|
77
|
+
}
|
|
78
|
+
.zoom-actions {
|
|
79
|
+
display: flex;
|
|
80
|
+
flex-wrap: wrap;
|
|
81
|
+
gap: 8px;
|
|
82
|
+
}
|
|
83
|
+
.zoom-action {
|
|
84
|
+
border: 1px solid var(--panel-border);
|
|
85
|
+
border-radius: 10px;
|
|
86
|
+
background: rgba(15, 23, 42, 0.6);
|
|
87
|
+
color: var(--text);
|
|
88
|
+
padding: 8px 10px;
|
|
89
|
+
font: inherit;
|
|
90
|
+
font-size: 12px;
|
|
91
|
+
cursor: pointer;
|
|
92
|
+
}
|
|
93
|
+
.zoom-action:disabled {
|
|
94
|
+
opacity: 0.45;
|
|
95
|
+
cursor: not-allowed;
|
|
96
|
+
}
|
|
97
|
+
.zoom-action.is-active {
|
|
98
|
+
border-color: rgba(148, 163, 184, 0.55);
|
|
99
|
+
box-shadow: inset 0 0 0 1px rgba(226, 232, 240, 0.12);
|
|
100
|
+
}
|
|
101
|
+
.hud h1 {
|
|
102
|
+
margin: 0;
|
|
103
|
+
font-size: 16px;
|
|
104
|
+
line-height: 1.2;
|
|
105
|
+
}
|
|
106
|
+
.hud p {
|
|
107
|
+
margin: 0;
|
|
108
|
+
color: var(--muted);
|
|
109
|
+
font-size: 13px;
|
|
110
|
+
}
|
|
111
|
+
.hud input,
|
|
112
|
+
.hud select {
|
|
113
|
+
width: 100%;
|
|
114
|
+
box-sizing: border-box;
|
|
115
|
+
padding: 10px 12px;
|
|
116
|
+
border-radius: 10px;
|
|
117
|
+
border: 1px solid var(--panel-border);
|
|
118
|
+
background: rgba(15, 23, 42, 0.6);
|
|
119
|
+
color: var(--text);
|
|
120
|
+
font: inherit;
|
|
121
|
+
}
|
|
122
|
+
.legend {
|
|
123
|
+
display: flex;
|
|
124
|
+
flex-wrap: wrap;
|
|
125
|
+
gap: 10px 14px;
|
|
126
|
+
font-size: 12px;
|
|
127
|
+
color: var(--muted);
|
|
128
|
+
}
|
|
129
|
+
.legend-toggle {
|
|
130
|
+
display: inline-flex;
|
|
131
|
+
align-items: center;
|
|
132
|
+
gap: 8px;
|
|
133
|
+
padding: 0;
|
|
134
|
+
border: 0;
|
|
135
|
+
background: transparent;
|
|
136
|
+
color: inherit;
|
|
137
|
+
font: inherit;
|
|
138
|
+
cursor: pointer;
|
|
139
|
+
}
|
|
140
|
+
.legend-toggle.is-hidden {
|
|
141
|
+
opacity: 0.45;
|
|
142
|
+
}
|
|
143
|
+
.legend-swatch {
|
|
144
|
+
width: 10px;
|
|
145
|
+
height: 10px;
|
|
146
|
+
border-radius: 999px;
|
|
147
|
+
display: inline-block;
|
|
148
|
+
}
|
|
149
|
+
.legend-icon {
|
|
150
|
+
width: 14px;
|
|
151
|
+
height: 14px;
|
|
152
|
+
display: inline-flex;
|
|
153
|
+
align-items: center;
|
|
154
|
+
justify-content: center;
|
|
155
|
+
flex: 0 0 auto;
|
|
156
|
+
}
|
|
157
|
+
.legend-icon svg {
|
|
158
|
+
width: 14px;
|
|
159
|
+
height: 14px;
|
|
160
|
+
display: block;
|
|
161
|
+
}
|
|
162
|
+
.graph-panel svg {
|
|
163
|
+
display: block;
|
|
164
|
+
width: 100%;
|
|
165
|
+
height: 100%;
|
|
166
|
+
}
|
|
167
|
+
.links {
|
|
168
|
+
z-index: 1;
|
|
169
|
+
}
|
|
170
|
+
.nodes {
|
|
171
|
+
z-index: 2;
|
|
172
|
+
}
|
|
173
|
+
.edge {
|
|
174
|
+
stroke: #64748b;
|
|
175
|
+
stroke-opacity: 0.5;
|
|
176
|
+
stroke-width: 1.3;
|
|
177
|
+
}
|
|
178
|
+
.edge--imports { stroke: #f59e0b; }
|
|
179
|
+
.edge--exports { stroke: #a855f7; }
|
|
180
|
+
.edge--calls { stroke: #fb7185; }
|
|
181
|
+
.edge--defines { stroke: #22c55e; }
|
|
182
|
+
.edge--contains { stroke: #38bdf8; }
|
|
183
|
+
.node { cursor: grab; }
|
|
184
|
+
.node:active { cursor: grabbing; }
|
|
185
|
+
.node-shape {
|
|
186
|
+
stroke: rgba(226, 232, 240, 0.18);
|
|
187
|
+
stroke-width: 1.5;
|
|
188
|
+
shape-rendering: geometricPrecision;
|
|
189
|
+
}
|
|
190
|
+
.node-label {
|
|
191
|
+
fill: var(--text);
|
|
192
|
+
font-size: 8px;
|
|
193
|
+
font-weight: 200;
|
|
194
|
+
paint-order: stroke;
|
|
195
|
+
stroke: #020617;
|
|
196
|
+
stroke-width: 3px;
|
|
197
|
+
stroke-linejoin: round;
|
|
198
|
+
-webkit-user-select: none;
|
|
199
|
+
user-select: none;
|
|
200
|
+
pointer-events: none;
|
|
201
|
+
}
|
|
202
|
+
.node.is-highlighted .node-shape,
|
|
203
|
+
.node.is-selected .node-shape {
|
|
204
|
+
stroke: white;
|
|
205
|
+
stroke-width: 2.5;
|
|
206
|
+
}
|
|
207
|
+
.node.is-hidden,
|
|
208
|
+
.edge.is-hidden {
|
|
209
|
+
display: none;
|
|
210
|
+
}
|
|
211
|
+
.node.is-dimmed {
|
|
212
|
+
opacity: 0.18;
|
|
213
|
+
}
|
|
214
|
+
.edge.is-dimmed {
|
|
215
|
+
opacity: 0.12;
|
|
216
|
+
}
|
|
217
|
+
.edge.is-highlighted {
|
|
218
|
+
stroke-opacity: 0.25;
|
|
219
|
+
stroke-width: 2.1;
|
|
220
|
+
}
|
|
221
|
+
.status {
|
|
222
|
+
color: var(--muted);
|
|
223
|
+
font-size: 12px;
|
|
224
|
+
}
|
|
225
|
+
.tooltip {
|
|
226
|
+
position: fixed;
|
|
227
|
+
pointer-events: none;
|
|
228
|
+
z-index: 3;
|
|
229
|
+
display: none;
|
|
230
|
+
padding: 8px 10px;
|
|
231
|
+
border-radius: 10px;
|
|
232
|
+
border: 1px solid var(--panel-border);
|
|
233
|
+
background: rgba(15, 23, 42, 0.96);
|
|
234
|
+
color: var(--text);
|
|
235
|
+
font-size: 12px;
|
|
236
|
+
box-shadow: 0 16px 30px rgba(15, 23, 42, 0.4);
|
|
237
|
+
}
|
|
238
|
+
#app.sidebar-collapsed .hud {
|
|
239
|
+
width: 48px;
|
|
240
|
+
flex-basis: 48px;
|
|
241
|
+
padding: 12px 8px;
|
|
242
|
+
}
|
|
243
|
+
#app.sidebar-collapsed .hud > :not(.section--toggle) {
|
|
244
|
+
display: none;
|
|
245
|
+
}
|
|
246
|
+
#app.sidebar-collapsed .sidebar-toggle {
|
|
247
|
+
justify-content: center;
|
|
248
|
+
width: 100%;
|
|
249
|
+
}
|
|
250
|
+
.sidebar-toggle {
|
|
251
|
+
display: inline-flex;
|
|
252
|
+
align-items: center;
|
|
253
|
+
justify-content: space-between;
|
|
254
|
+
gap: 8px;
|
|
255
|
+
width: 100%;
|
|
256
|
+
border: 1px solid var(--panel-border);
|
|
257
|
+
border-radius: 10px;
|
|
258
|
+
background: rgba(15, 23, 42, 0.6);
|
|
259
|
+
color: var(--text);
|
|
260
|
+
padding: 10px 12px;
|
|
261
|
+
font: inherit;
|
|
262
|
+
cursor: pointer;
|
|
263
|
+
}
|
|
264
|
+
.sidebar-toggle span {
|
|
265
|
+
color: var(--muted);
|
|
266
|
+
font-size: 12px;
|
|
267
|
+
}
|
|
268
|
+
.sidebar-toggle .icon {
|
|
269
|
+
font-size: 14px;
|
|
270
|
+
line-height: 1;
|
|
271
|
+
}
|
|
272
|
+
|