@sleetdrop/dsh-plugin-topology 0.3.1 → 0.5.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/NEXT-STEPS.md +14 -7
- package/README.md +33 -11
- package/lib/client/NodeDetailPanel.d.ts +25 -0
- package/lib/client/NodeDetailPanel.js +32 -0
- package/lib/client/NodePopover.d.ts +39 -0
- package/lib/client/NodePopover.js +96 -0
- package/lib/client/TopologyGraphView.d.ts +49 -6
- package/lib/client/TopologyGraphView.js +244 -27
- package/lib/client/TopologyView.d.ts +2 -2
- package/lib/client/TopologyView.js +84 -10
- package/lib/client/index.js +2 -2
- package/lib/client/locales.d.ts +1 -1
- package/lib/client/locales.js +10 -0
- package/lib/client/remote-client.js +16 -4
- package/lib/client.js +588 -72
- package/lib/client.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.js +7 -6
- package/lib/render.d.ts +22 -5
- package/lib/render.js +48 -19
- package/lib/typert.d.ts +16 -4
- package/lib/typert.js +15 -4
- package/package.json +22 -22
package/lib/index.d.ts
CHANGED
|
@@ -49,6 +49,6 @@ export declare class PluginTopologyService extends TypertRemoteService {
|
|
|
49
49
|
* @param rankdir - Graphviz layout direction for `dot`/`svg`; ignored by `json`.
|
|
50
50
|
* @returns the serialized document for the format.
|
|
51
51
|
*/
|
|
52
|
-
render(format: RenderFormat, rankdir: Rankdir): Promise<string>;
|
|
52
|
+
render(format: RenderFormat, rankdir: Rankdir, theme?: 'light' | 'dark'): Promise<string>;
|
|
53
53
|
}
|
|
54
54
|
export default PluginTopologyService;
|
package/lib/index.js
CHANGED
|
@@ -55,7 +55,7 @@ import { Context } from '@deepseek-ai/cordis';
|
|
|
55
55
|
import { Graphviz } from '@hpcc-js/wasm-graphviz';
|
|
56
56
|
import { Remote, TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol';
|
|
57
57
|
import { analyzeGraph } from "./metrics.js";
|
|
58
|
-
import { composeGraphvizSvgs, isolatedNodes, renderCompleteDot, renderDot, renderIsolatedDot, renderJson } from "./render.js";
|
|
58
|
+
import { composeGraphvizSvgs, DARK_PALETTE, isolatedNodes, LIGHT_PALETTE, renderCompleteDot, renderDot, renderIsolatedDot, renderJson } from "./render.js";
|
|
59
59
|
export { analyzeGraph, collapse } from "./metrics.js";
|
|
60
60
|
export { composeGraphvizSvgs, isolatedNodes, nodeLink, renderCompleteDot, renderDot, renderIsolatedDot, renderJson } from "./render.js";
|
|
61
61
|
/** Map a Cordis fiber state number to its stable name. */
|
|
@@ -179,18 +179,19 @@ let PluginTopologyService = (() => {
|
|
|
179
179
|
* @param rankdir - Graphviz layout direction for `dot`/`svg`; ignored by `json`.
|
|
180
180
|
* @returns the serialized document for the format.
|
|
181
181
|
*/
|
|
182
|
-
async render(format, rankdir) {
|
|
182
|
+
async render(format, rankdir, theme) {
|
|
183
|
+
const palette = theme === 'dark' ? DARK_PALETTE : LIGHT_PALETTE;
|
|
183
184
|
const analysis = this.analyze();
|
|
184
185
|
if (format === 'json')
|
|
185
186
|
return renderJson(analysis);
|
|
186
187
|
if (format === 'dot')
|
|
187
|
-
return renderCompleteDot(analysis, rankdir);
|
|
188
|
+
return renderCompleteDot(analysis, rankdir, palette);
|
|
188
189
|
const graphviz = await (this.graphvizPromise ??= Graphviz.load());
|
|
189
|
-
const mainSvg = graphviz.dot(renderDot(analysis, rankdir), 'svg');
|
|
190
|
+
const mainSvg = graphviz.dot(renderDot(analysis, rankdir, palette), 'svg');
|
|
190
191
|
if (isolatedNodes(analysis).length === 0)
|
|
191
192
|
return mainSvg;
|
|
192
|
-
const isolatedSvg = graphviz.dot(renderIsolatedDot(analysis, rankdir), 'svg');
|
|
193
|
-
return composeGraphvizSvgs(mainSvg, isolatedSvg, rankdir);
|
|
193
|
+
const isolatedSvg = graphviz.dot(renderIsolatedDot(analysis, rankdir, palette), 'svg');
|
|
194
|
+
return composeGraphvizSvgs(mainSvg, isolatedSvg, rankdir, palette.canvas);
|
|
194
195
|
}
|
|
195
196
|
};
|
|
196
197
|
})();
|
package/lib/render.d.ts
CHANGED
|
@@ -10,6 +10,22 @@
|
|
|
10
10
|
* @module @sleetdrop/dsh-plugin-topology/render
|
|
11
11
|
*/
|
|
12
12
|
import type { GraphMetrics, PluginNode, PluginState, Rankdir, TopologyAnalysis } from './types.ts';
|
|
13
|
+
/** Color palette for DOT rendering, keyed by theme. */
|
|
14
|
+
export interface DotPalette {
|
|
15
|
+
/** Graph background; matches the app viewport so the inline SVG looks seamless. */
|
|
16
|
+
readonly canvas: string;
|
|
17
|
+
readonly nodeFill: string;
|
|
18
|
+
readonly nodeStroke: string;
|
|
19
|
+
readonly nodeFont: string;
|
|
20
|
+
readonly edgeStroke: string;
|
|
21
|
+
readonly edgeFont: string;
|
|
22
|
+
readonly errorStroke: string;
|
|
23
|
+
readonly errorFont: string;
|
|
24
|
+
readonly clusterFill: string;
|
|
25
|
+
readonly clusterStroke: string;
|
|
26
|
+
}
|
|
27
|
+
export declare const LIGHT_PALETTE: DotPalette;
|
|
28
|
+
export declare const DARK_PALETTE: DotPalette;
|
|
13
29
|
/** One plugin node with its metrics in the node-link document. */
|
|
14
30
|
export interface NodeLinkNode {
|
|
15
31
|
readonly id: string;
|
|
@@ -52,21 +68,22 @@ export declare function renderJson(analysis: TopologyAnalysis): string;
|
|
|
52
68
|
* never stretch the layout. Plugins with an unresolved dependency get a red
|
|
53
69
|
* stroke.
|
|
54
70
|
*/
|
|
55
|
-
export declare function renderDot(analysis: TopologyAnalysis, rankdir: Rankdir): string;
|
|
71
|
+
export declare function renderDot(analysis: TopologyAnalysis, rankdir: Rankdir, palette?: DotPalette): string;
|
|
56
72
|
/** Plugins with no incident dependency edge (and not the synthetic root). */
|
|
57
73
|
export declare function isolatedNodes(analysis: TopologyAnalysis): readonly PluginNode[];
|
|
58
74
|
/** Serialize the isolated plugins as a standalone dashed-cluster DOT graph. */
|
|
59
|
-
export declare function renderIsolatedDot(analysis: TopologyAnalysis, rankdir: Rankdir): string;
|
|
75
|
+
export declare function renderIsolatedDot(analysis: TopologyAnalysis, rankdir: Rankdir, palette?: DotPalette): string;
|
|
60
76
|
/**
|
|
61
77
|
* The complete DOT source: every plugin instance (connected and isolated) with
|
|
62
78
|
* all dependency edges, isolated ones listed in a dashed cluster. Aligned with
|
|
63
79
|
* the JSON export; used for the downloadable DOT. The SVG split (main/isolated)
|
|
64
80
|
* is an internal layout tradeoff and is not exposed here.
|
|
65
81
|
*/
|
|
66
|
-
export declare function renderCompleteDot(analysis: TopologyAnalysis, rankdir: Rankdir): string;
|
|
82
|
+
export declare function renderCompleteDot(analysis: TopologyAnalysis, rankdir: Rankdir, palette?: DotPalette): string;
|
|
67
83
|
/**
|
|
68
84
|
* Compose two Graphviz SVGs into one document. The isolated graph is placed to
|
|
69
85
|
* the side (LR) or below (TB) of the main graph with a fixed gap, and centered
|
|
70
|
-
* along the graph's perpendicular axis, so the exported SVG is one image.
|
|
86
|
+
* along the graph's perpendicular axis, so the exported SVG is one image. A
|
|
87
|
+
* canvas rect behind both keeps the gap opaque instead of showing through.
|
|
71
88
|
*/
|
|
72
|
-
export declare function composeGraphvizSvgs(main: string, isolated: string, rankdir: Rankdir): string;
|
|
89
|
+
export declare function composeGraphvizSvgs(main: string, isolated: string, rankdir: Rankdir, canvas?: string): string;
|
package/lib/render.js
CHANGED
|
@@ -9,6 +9,31 @@
|
|
|
9
9
|
*
|
|
10
10
|
* @module @sleetdrop/dsh-plugin-topology/render
|
|
11
11
|
*/
|
|
12
|
+
export const LIGHT_PALETTE = {
|
|
13
|
+
canvas: '#ffffff',
|
|
14
|
+
nodeFill: '#ffffff',
|
|
15
|
+
nodeStroke: '#e2e8f0',
|
|
16
|
+
nodeFont: '#334155',
|
|
17
|
+
edgeStroke: '#e2e8f0',
|
|
18
|
+
edgeFont: '#94a3b8',
|
|
19
|
+
errorStroke: '#ec1313',
|
|
20
|
+
errorFont: '#ec1313',
|
|
21
|
+
clusterFill: '#f8fafc',
|
|
22
|
+
clusterStroke: '#94a3b8',
|
|
23
|
+
};
|
|
24
|
+
export const DARK_PALETTE = {
|
|
25
|
+
/** DeepSeek Harness dark `--dsw-alias-bg-layer-1`. */
|
|
26
|
+
canvas: '#232324',
|
|
27
|
+
nodeFill: '#2c2c2e',
|
|
28
|
+
nodeStroke: '#ffffff29',
|
|
29
|
+
nodeFont: '#e2e8f0',
|
|
30
|
+
edgeStroke: '#ffffff1f',
|
|
31
|
+
edgeFont: '#81858c',
|
|
32
|
+
errorStroke: '#f25a5a',
|
|
33
|
+
errorFont: '#f25a5a',
|
|
34
|
+
clusterFill: '#151517',
|
|
35
|
+
clusterStroke: '#81858c',
|
|
36
|
+
};
|
|
12
37
|
/** Merge the collapsed graph and its metrics into a node-link document. */
|
|
13
38
|
export function nodeLink(analysis) {
|
|
14
39
|
const nodes = analysis.collapsed.nodes.map((node) => {
|
|
@@ -57,7 +82,7 @@ function escapeDot(value) {
|
|
|
57
82
|
* never stretch the layout. Plugins with an unresolved dependency get a red
|
|
58
83
|
* stroke.
|
|
59
84
|
*/
|
|
60
|
-
export function renderDot(analysis, rankdir) {
|
|
85
|
+
export function renderDot(analysis, rankdir, palette = LIGHT_PALETTE) {
|
|
61
86
|
const unresolvedIds = new Set(analysis.graph.unresolved.map(dep => dep.plugin));
|
|
62
87
|
const nodes = analysis.collapsed.nodes;
|
|
63
88
|
const ROOT_ID = 'p:0';
|
|
@@ -73,15 +98,16 @@ export function renderDot(analysis, rankdir) {
|
|
|
73
98
|
const lines = [
|
|
74
99
|
'digraph plugin_topology {',
|
|
75
100
|
` rankdir=${rankdir};`,
|
|
101
|
+
` bgcolor="${palette.canvas}";`,
|
|
76
102
|
' nodesep=0.3;',
|
|
77
103
|
' ranksep=0.7;',
|
|
78
104
|
' splines=true;',
|
|
79
|
-
|
|
80
|
-
|
|
105
|
+
` node [shape=oval, style="filled", fillcolor="${palette.nodeFill}", color="${palette.nodeStroke}", fontcolor="${palette.nodeFont}", penwidth=0.8, fontname="Helvetica", fontsize=8, margin="0.16,0.11"];`,
|
|
106
|
+
` edge [color="${palette.edgeStroke}", penwidth=0.8, arrowsize=0.55, fontname="Helvetica", fontsize=7, fontcolor="${palette.edgeFont}"];`,
|
|
81
107
|
];
|
|
82
108
|
for (const node of merged.nodes) {
|
|
83
|
-
const stroke = node.unresolved ?
|
|
84
|
-
lines.push(` "${node.id}" [label="${mergedLabel(node)}"${stroke}];`);
|
|
109
|
+
const stroke = node.unresolved ? `, color="${palette.errorStroke}", fontcolor="${palette.errorFont}", penwidth=1.5` : '';
|
|
110
|
+
lines.push(` "${node.id}" [id="node-${node.id}", label="${mergedLabel(node)}"${stroke}];`);
|
|
85
111
|
}
|
|
86
112
|
const seen = new Set();
|
|
87
113
|
for (const edge of analysis.collapsed.edges) {
|
|
@@ -143,22 +169,23 @@ export function isolatedNodes(analysis) {
|
|
|
143
169
|
return analysis.collapsed.nodes.filter(node => node.id !== 'p:0' && !connected.has(node.id));
|
|
144
170
|
}
|
|
145
171
|
/** Serialize the isolated plugins as a standalone dashed-cluster DOT graph. */
|
|
146
|
-
export function renderIsolatedDot(analysis, rankdir) {
|
|
172
|
+
export function renderIsolatedDot(analysis, rankdir, palette = LIGHT_PALETTE) {
|
|
147
173
|
const isolated = isolatedNodes(analysis);
|
|
148
174
|
const merged = mergeByName(isolated, new Set());
|
|
149
175
|
const lines = [
|
|
150
176
|
'digraph plugin_topology_isolated {',
|
|
151
177
|
` rankdir=${rankdir};`,
|
|
178
|
+
` bgcolor="${palette.canvas}";`,
|
|
152
179
|
' nodesep=0.18;',
|
|
153
180
|
' ranksep=0.4;',
|
|
154
|
-
|
|
181
|
+
` node [shape=oval, style="filled", fillcolor="${palette.nodeFill}", color="${palette.nodeStroke}", fontcolor="${palette.nodeFont}", penwidth=0.8, fontname="Helvetica", fontsize=8, margin="0.16,0.11"];`,
|
|
155
182
|
' subgraph "cluster_isolated" {',
|
|
156
183
|
' label="isolated";',
|
|
157
|
-
|
|
184
|
+
` style="rounded,dashed"; fillcolor="${palette.clusterFill}"; color="${palette.clusterStroke}"; fontsize=9; fontcolor="${palette.nodeFont}";`,
|
|
158
185
|
' rank=same;',
|
|
159
186
|
];
|
|
160
187
|
for (const node of merged.nodes) {
|
|
161
|
-
lines.push(` "${node.id}" [label="${mergedLabel(node)}"];`);
|
|
188
|
+
lines.push(` "${node.id}" [id="node-${node.id}", label="${mergedLabel(node)}"];`);
|
|
162
189
|
}
|
|
163
190
|
lines.push(' }');
|
|
164
191
|
lines.push('}');
|
|
@@ -174,24 +201,25 @@ function instanceLabel(node) {
|
|
|
174
201
|
* the JSON export; used for the downloadable DOT. The SVG split (main/isolated)
|
|
175
202
|
* is an internal layout tradeoff and is not exposed here.
|
|
176
203
|
*/
|
|
177
|
-
export function renderCompleteDot(analysis, rankdir) {
|
|
204
|
+
export function renderCompleteDot(analysis, rankdir, palette = LIGHT_PALETTE) {
|
|
178
205
|
const unresolvedIds = new Set(analysis.graph.unresolved.map(dep => dep.plugin));
|
|
179
206
|
const isolated = isolatedNodes(analysis);
|
|
180
207
|
const isolatedIds = new Set(isolated.map(node => node.id));
|
|
181
208
|
const lines = [
|
|
182
209
|
'digraph plugin_topology {',
|
|
183
210
|
` rankdir=${rankdir};`,
|
|
211
|
+
` bgcolor="${palette.canvas}";`,
|
|
184
212
|
' nodesep=0.3;',
|
|
185
213
|
' ranksep=0.7;',
|
|
186
214
|
' splines=true;',
|
|
187
|
-
|
|
188
|
-
|
|
215
|
+
` node [shape=oval, style="filled", fillcolor="${palette.nodeFill}", color="${palette.nodeStroke}", fontcolor="${palette.nodeFont}", penwidth=0.8, fontname="Helvetica", fontsize=8, margin="0.16,0.11"];`,
|
|
216
|
+
` edge [color="${palette.edgeStroke}", penwidth=0.8, arrowsize=0.55, fontname="Helvetica", fontsize=7, fontcolor="${palette.edgeFont}"];`,
|
|
189
217
|
];
|
|
190
218
|
for (const node of analysis.collapsed.nodes) {
|
|
191
219
|
if (isolatedIds.has(node.id))
|
|
192
220
|
continue;
|
|
193
|
-
const stroke = unresolvedIds.has(node.id) ?
|
|
194
|
-
lines.push(` "${node.id}" [label="${instanceLabel(node)}"${stroke}];`);
|
|
221
|
+
const stroke = unresolvedIds.has(node.id) ? `, color="${palette.errorStroke}", fontcolor="${palette.errorFont}", penwidth=1.5` : '';
|
|
222
|
+
lines.push(` "${node.id}" [id="node-${node.id}", label="${instanceLabel(node)}"${stroke}];`);
|
|
195
223
|
}
|
|
196
224
|
for (const edge of analysis.collapsed.edges) {
|
|
197
225
|
lines.push(` "${edge.source}" -> "${edge.target}" [label="${escapeDot(edge.service)}"];`);
|
|
@@ -199,10 +227,10 @@ export function renderCompleteDot(analysis, rankdir) {
|
|
|
199
227
|
if (isolated.length > 0) {
|
|
200
228
|
lines.push(' subgraph "cluster_isolated" {');
|
|
201
229
|
lines.push(' label="isolated";');
|
|
202
|
-
lines.push(
|
|
230
|
+
lines.push(` style="rounded,dashed"; fillcolor="${palette.clusterFill}"; color="${palette.clusterStroke}"; fontsize=9; fontcolor="${palette.nodeFont}";`);
|
|
203
231
|
lines.push(' rank=same;');
|
|
204
232
|
for (const node of isolated) {
|
|
205
|
-
lines.push(` "${node.id}" [label="${instanceLabel(node)}"];`);
|
|
233
|
+
lines.push(` "${node.id}" [id="node-${node.id}", label="${instanceLabel(node)}"];`);
|
|
206
234
|
}
|
|
207
235
|
lines.push(' }');
|
|
208
236
|
}
|
|
@@ -226,9 +254,10 @@ function splitSvg(svg) {
|
|
|
226
254
|
/**
|
|
227
255
|
* Compose two Graphviz SVGs into one document. The isolated graph is placed to
|
|
228
256
|
* the side (LR) or below (TB) of the main graph with a fixed gap, and centered
|
|
229
|
-
* along the graph's perpendicular axis, so the exported SVG is one image.
|
|
257
|
+
* along the graph's perpendicular axis, so the exported SVG is one image. A
|
|
258
|
+
* canvas rect behind both keeps the gap opaque instead of showing through.
|
|
230
259
|
*/
|
|
231
|
-
export function composeGraphvizSvgs(main, isolated, rankdir) {
|
|
260
|
+
export function composeGraphvizSvgs(main, isolated, rankdir, canvas = '#ffffff') {
|
|
232
261
|
const m = splitSvg(main);
|
|
233
262
|
const iso = splitSvg(isolated);
|
|
234
263
|
if (iso.width === 0 || iso.height === 0)
|
|
@@ -258,5 +287,5 @@ export function composeGraphvizSvgs(main, isolated, rankdir) {
|
|
|
258
287
|
width = Math.max(m.width, ix + iso.width);
|
|
259
288
|
height = m.height + gap + iso.height;
|
|
260
289
|
}
|
|
261
|
-
return `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 ${width} ${height}" width="${width}" height="${height}" role="img"><g transform="translate(${mx},${my})">${m.inner}</g><g transform="translate(${ix},${iy})">${isoInner}</g></svg>`;
|
|
290
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 ${width} ${height}" width="${width}" height="${height}" role="img"><rect x="0" y="0" width="${width}" height="${height}" fill="${canvas}"/><g transform="translate(${mx},${my})">${m.inner}</g><g transform="translate(${ix},${iy})">${isoInner}</g></svg>`;
|
|
262
291
|
}
|
package/lib/typert.d.ts
CHANGED
|
@@ -26,7 +26,7 @@ export declare const TYPERT: {
|
|
|
26
26
|
result: {
|
|
27
27
|
mode: string;
|
|
28
28
|
typeSymbol: string;
|
|
29
|
-
|
|
29
|
+
create: () => z.ZodObject<{
|
|
30
30
|
graph: z.ZodReadonly<z.ZodObject<{
|
|
31
31
|
kind: z.ZodReadonly<z.ZodLiteral<"bipartite">>;
|
|
32
32
|
generatedAt: z.ZodReadonly<z.ZodString>;
|
|
@@ -112,8 +112,9 @@ export declare const TYPERT: {
|
|
|
112
112
|
codec: {
|
|
113
113
|
mode: string;
|
|
114
114
|
typeSymbol: string;
|
|
115
|
-
|
|
115
|
+
create: () => z.ZodUnion<readonly [z.ZodLiteral<"json">, z.ZodLiteral<"dot">, z.ZodLiteral<"svg">]>;
|
|
116
116
|
};
|
|
117
|
+
acceptsUndefined?: never;
|
|
117
118
|
} | {
|
|
118
119
|
name: string;
|
|
119
120
|
wire: string;
|
|
@@ -121,13 +122,24 @@ export declare const TYPERT: {
|
|
|
121
122
|
codec: {
|
|
122
123
|
mode: string;
|
|
123
124
|
typeSymbol: string;
|
|
124
|
-
|
|
125
|
+
create: () => z.ZodUnion<readonly [z.ZodLiteral<"TB">, z.ZodLiteral<"LR">]>;
|
|
126
|
+
};
|
|
127
|
+
acceptsUndefined?: never;
|
|
128
|
+
} | {
|
|
129
|
+
name: string;
|
|
130
|
+
wire: string;
|
|
131
|
+
source: string;
|
|
132
|
+
acceptsUndefined: boolean;
|
|
133
|
+
codec: {
|
|
134
|
+
mode: string;
|
|
135
|
+
typeSymbol: string;
|
|
136
|
+
create: () => z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"light">, z.ZodLiteral<"dark">]>>;
|
|
125
137
|
};
|
|
126
138
|
})[];
|
|
127
139
|
result: {
|
|
128
140
|
mode: string;
|
|
129
141
|
typeSymbol: string;
|
|
130
|
-
|
|
142
|
+
create: () => z.ZodString;
|
|
131
143
|
};
|
|
132
144
|
})[];
|
|
133
145
|
model: {
|
package/lib/typert.js
CHANGED
|
@@ -95,7 +95,7 @@ export const TYPERT = {
|
|
|
95
95
|
result: {
|
|
96
96
|
mode: 'strict',
|
|
97
97
|
typeSymbol: '@sleetdrop/dsh-plugin-topology/types#TopologyAnalysis',
|
|
98
|
-
|
|
98
|
+
create: () => topologyAnalysisSchema,
|
|
99
99
|
},
|
|
100
100
|
},
|
|
101
101
|
{
|
|
@@ -112,7 +112,7 @@ export const TYPERT = {
|
|
|
112
112
|
codec: {
|
|
113
113
|
mode: 'strict',
|
|
114
114
|
typeSymbol: '@sleetdrop/dsh-plugin-topology/types#RenderFormat',
|
|
115
|
-
|
|
115
|
+
create: () => z.union([z.literal('json'), z.literal('dot'), z.literal('svg')]),
|
|
116
116
|
},
|
|
117
117
|
},
|
|
118
118
|
{
|
|
@@ -122,14 +122,25 @@ export const TYPERT = {
|
|
|
122
122
|
codec: {
|
|
123
123
|
mode: 'strict',
|
|
124
124
|
typeSymbol: '@sleetdrop/dsh-plugin-topology/types#Rankdir',
|
|
125
|
-
|
|
125
|
+
create: () => z.union([z.literal('TB'), z.literal('LR')]),
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
name: 'theme',
|
|
130
|
+
wire: 'theme',
|
|
131
|
+
source: 'json',
|
|
132
|
+
acceptsUndefined: true,
|
|
133
|
+
codec: {
|
|
134
|
+
mode: 'strict',
|
|
135
|
+
typeSymbol: '@sleetdrop/dsh-plugin-topology/types#Theme',
|
|
136
|
+
create: () => z.union([z.literal('light'), z.literal('dark')]).optional(),
|
|
126
137
|
},
|
|
127
138
|
},
|
|
128
139
|
],
|
|
129
140
|
result: {
|
|
130
141
|
mode: 'strict',
|
|
131
142
|
typeSymbol: '@sleetdrop/dsh-plugin-topology#pluginTopology/render:result',
|
|
132
|
-
|
|
143
|
+
create: () => z.string(),
|
|
133
144
|
},
|
|
134
145
|
},
|
|
135
146
|
],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sleetdrop/dsh-plugin-topology",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Plugin dependency graph inspector for DeepSeek Harness: a host service that snapshots the live Cordis plugin fiber tree, a graph.render tool for the model, and a browser panel that renders the plugin/service topology as a zoomable graph with metrics, legend, and unresolved-dependency table.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -70,29 +70,29 @@
|
|
|
70
70
|
}
|
|
71
71
|
},
|
|
72
72
|
"peerDependencies": {
|
|
73
|
-
"@deepseek-ai/cordis": "^4.0.
|
|
74
|
-
"@deepseek-ai/dsh-api-remotes": "^0.
|
|
75
|
-
"@deepseek-ai/dsh-client-locale": "^0.
|
|
76
|
-
"@deepseek-ai/dsh-client-store": "^0.
|
|
77
|
-
"@deepseek-ai/dsh-client-connection": "^0.
|
|
78
|
-
"@deepseek-ai/dsh-client-ui-renderer": "^0.
|
|
79
|
-
"@deepseek-ai/dsh-client-ui-sidebar": "^0.
|
|
80
|
-
"@deepseek-ai/dsh-tools": "^0.
|
|
81
|
-
"@deepseek-ai/dsh-typert-protocol": "^0.
|
|
82
|
-
"@deepseek-ai/dsh-invariants": "^0.
|
|
73
|
+
"@deepseek-ai/cordis": "^4.0.4",
|
|
74
|
+
"@deepseek-ai/dsh-api-remotes": "^0.2.0-rc.2",
|
|
75
|
+
"@deepseek-ai/dsh-client-locale": "^0.2.0-rc.2",
|
|
76
|
+
"@deepseek-ai/dsh-client-store": "^0.2.0-rc.2",
|
|
77
|
+
"@deepseek-ai/dsh-client-connection": "^0.2.0-rc.2",
|
|
78
|
+
"@deepseek-ai/dsh-client-ui-renderer": "^0.2.0-rc.2",
|
|
79
|
+
"@deepseek-ai/dsh-client-ui-sidebar": "^0.2.0-rc.2",
|
|
80
|
+
"@deepseek-ai/dsh-tools": "^0.2.0-rc.2",
|
|
81
|
+
"@deepseek-ai/dsh-typert-protocol": "^0.2.0-rc.2",
|
|
82
|
+
"@deepseek-ai/dsh-invariants": "^0.2.0-rc.2"
|
|
83
83
|
},
|
|
84
84
|
"devDependencies": {
|
|
85
|
-
"@deepseek-ai/cordis": "4.0.
|
|
86
|
-
"@deepseek-ai/dsh-api-remotes": "0.
|
|
87
|
-
"@deepseek-ai/dsh-client-locale": "0.
|
|
88
|
-
"@deepseek-ai/dsh-client-store": "0.
|
|
89
|
-
"@deepseek-ai/dsh-client-connection": "0.
|
|
90
|
-
"@deepseek-ai/dsh-client-ui-renderer": "0.
|
|
91
|
-
"@deepseek-ai/dsh-client-ui-sidebar": "0.
|
|
92
|
-
"@deepseek-ai/dsh-tools": "0.
|
|
93
|
-
"@deepseek-ai/dsh-typert-protocol": "0.
|
|
94
|
-
"@deepseek-ai/dsh-invariants": "0.
|
|
95
|
-
"@deepseek-ai/dsh-client-ui-slots": "0.
|
|
85
|
+
"@deepseek-ai/cordis": "4.0.4",
|
|
86
|
+
"@deepseek-ai/dsh-api-remotes": "0.2.0-rc.2",
|
|
87
|
+
"@deepseek-ai/dsh-client-locale": "0.2.0-rc.2",
|
|
88
|
+
"@deepseek-ai/dsh-client-store": "0.2.0-rc.2",
|
|
89
|
+
"@deepseek-ai/dsh-client-connection": "0.2.0-rc.2",
|
|
90
|
+
"@deepseek-ai/dsh-client-ui-renderer": "0.2.0-rc.2",
|
|
91
|
+
"@deepseek-ai/dsh-client-ui-sidebar": "0.2.0-rc.2",
|
|
92
|
+
"@deepseek-ai/dsh-tools": "0.2.0-rc.2",
|
|
93
|
+
"@deepseek-ai/dsh-typert-protocol": "0.2.0-rc.2",
|
|
94
|
+
"@deepseek-ai/dsh-invariants": "0.2.0-rc.2",
|
|
95
|
+
"@deepseek-ai/dsh-client-ui-slots": "0.2.0-rc.2",
|
|
96
96
|
"@types/node": "^24.0.0",
|
|
97
97
|
"@types/react": "~18.3.1",
|
|
98
98
|
"react": "^18.2.0",
|