@caddis/cli 0.4.49 → 0.4.50
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/bundles/antigravity-plugin/plugin.json +1 -1
- package/bundles/antigravity-plugin/skills/flow-studio/SKILL.md +1 -1
- package/bundles/antigravity-plugin/skills/flow-studio/model.schema.json +4 -1
- package/bundles/antigravity-plugin/skills/flow-studio/react/FlowStudioView.test.tsx +18 -1
- package/bundles/antigravity-plugin/skills/flow-studio/react/FlowStudioView.tsx +6 -1
- package/bundles/antigravity-plugin/skills/flow-studio/react/README.md +1 -1
- package/bundles/antigravity-plugin/skills/flow-studio/references/model-schema.md +16 -0
- package/bundles/antigravity-plugin/skills/flow-studio/src/bars.test.ts +90 -0
- package/bundles/antigravity-plugin/skills/flow-studio/src/bars.ts +53 -0
- package/bundles/antigravity-plugin/skills/flow-studio/src/ctx.ts +3 -0
- package/bundles/antigravity-plugin/skills/flow-studio/src/engine.ts +8 -3
- package/bundles/antigravity-plugin/skills/flow-studio/src/layout.test.ts +17 -0
- package/bundles/antigravity-plugin/skills/flow-studio/src/layout.ts +2 -1
- package/bundles/antigravity-plugin/skills/flow-studio/src/model.ts +11 -0
- package/bundles/antigravity-plugin/skills/flow-studio/src/render.ts +4 -2
- package/bundles/antigravity-plugin/skills/flow-studio/src/styles.css +5 -0
- package/bundles/antigravity-plugin/skills/flow-studio/templates/engine.html +61 -4
- package/bundles/antigravity-plugin/skills/flow-studio/templates/flow-studio.css +5 -0
- package/bundles/antigravity-plugin/skills/flow-studio/templates/flow-studio.lib.mjs +56 -4
- package/bundles/codex/.codex/skills/frontend/flow-studio/SKILL.md +1 -1
- package/bundles/codex/.codex/skills/frontend/flow-studio/model.schema.json +4 -1
- package/bundles/codex/.codex/skills/frontend/flow-studio/react/FlowStudioView.test.tsx +18 -1
- package/bundles/codex/.codex/skills/frontend/flow-studio/react/FlowStudioView.tsx +6 -1
- package/bundles/codex/.codex/skills/frontend/flow-studio/react/README.md +1 -1
- package/bundles/codex/.codex/skills/frontend/flow-studio/references/model-schema.md +16 -0
- package/bundles/codex/.codex/skills/frontend/flow-studio/src/bars.test.ts +90 -0
- package/bundles/codex/.codex/skills/frontend/flow-studio/src/bars.ts +53 -0
- package/bundles/codex/.codex/skills/frontend/flow-studio/src/ctx.ts +3 -0
- package/bundles/codex/.codex/skills/frontend/flow-studio/src/engine.ts +8 -3
- package/bundles/codex/.codex/skills/frontend/flow-studio/src/layout.test.ts +17 -0
- package/bundles/codex/.codex/skills/frontend/flow-studio/src/layout.ts +2 -1
- package/bundles/codex/.codex/skills/frontend/flow-studio/src/model.ts +11 -0
- package/bundles/codex/.codex/skills/frontend/flow-studio/src/render.ts +4 -2
- package/bundles/codex/.codex/skills/frontend/flow-studio/src/styles.css +5 -0
- package/bundles/codex/.codex/skills/frontend/flow-studio/templates/engine.html +61 -4
- package/bundles/codex/.codex/skills/frontend/flow-studio/templates/flow-studio.css +5 -0
- package/bundles/codex/.codex/skills/frontend/flow-studio/templates/flow-studio.lib.mjs +56 -4
- package/bundles/manifest.json +2 -2
- package/package.json +1 -1
|
@@ -198,6 +198,11 @@
|
|
|
198
198
|
.node .metric-badge i { display:block; height:2px; position:absolute; bottom:0; left:0; background:var(--accent); }
|
|
199
199
|
.node .s.has-spark { padding-right:60px; }
|
|
200
200
|
.node .spark { position:absolute; right:6px; bottom:4px; overflow:visible; }
|
|
201
|
+
.node svg.bars { position:absolute; left:10px; right:10px; bottom:6px; width:calc(100% - 20px); height:22px; overflow:visible; }
|
|
202
|
+
.node svg.bars rect { fill:var(--muted); opacity:.6; }
|
|
203
|
+
.node svg.bars rect.last { fill:var(--accent); opacity:1; }
|
|
204
|
+
.node svg.bars rect.low, .node svg.bars rect.miss { fill:var(--crit); opacity:1; }
|
|
205
|
+
.node svg.bars rect[data-hl="true"] { stroke:var(--fg); stroke-width:1.5px; vector-effect:non-scaling-stroke; opacity:1; }
|
|
201
206
|
.node.gateway .metric-badge { position:absolute; right:9px; top:18px; }
|
|
202
207
|
.node.gateway:has(.metric-badge) .gw-title, .node.gateway.has-metrics .gw-title { width:45%; margin-right:42px; }
|
|
203
208
|
.node .spark polyline, .metrics polyline { fill:none; stroke:var(--accent); stroke-width:1.5; }
|
|
@@ -326,6 +331,8 @@
|
|
|
326
331
|
const metric = n.metrics;
|
|
327
332
|
const invalidSeries = metric?.series !== void 0 && (!Array.isArray(metric.series) || metric.series.length > 60 || metric.series.some((v) => !Number.isFinite(v)));
|
|
328
333
|
if (metric !== void 0 && (!metric || !Number.isFinite(metric.value) || metric.max != null && !Number.isFinite(metric.max) || invalidSeries)) add("error", "metric-invalid", w, "Metrics need finite values and at most 60 finite series points.");
|
|
334
|
+
const badBars = n.bars !== void 0 && (!Array.isArray(n.bars) || n.bars.length > 120 || n.bars.some((b) => !b || typeof b.key !== "string" || b.value !== null && (typeof b.value !== "number" || !Number.isFinite(b.value) || b.value < 0)));
|
|
335
|
+
if (badBars) add("error", "bars-invalid", w, "Bars need a list of at most 120 items, each with a string key and a finite, non-negative value or null.");
|
|
329
336
|
if (n.sla !== void 0 && (!Number.isFinite(n.sla) || n.sla <= 0)) add("error", "sla-invalid", w, "SLA must be a positive finite number of seconds.");
|
|
330
337
|
}
|
|
331
338
|
for (const n of nodes) {
|
|
@@ -975,6 +982,7 @@
|
|
|
975
982
|
|
|
976
983
|
// src/layout.ts
|
|
977
984
|
var DEFAULTS = { pitch: 62, top: 64, gap: 34, padX: 24, visible: () => true };
|
|
985
|
+
var BARS_EXTRA = 26;
|
|
978
986
|
var LANE_PAD_TOP = 38;
|
|
979
987
|
var LANE_PAD_BOTTOM = 14;
|
|
980
988
|
function computeGeometry(m, opt = {}) {
|
|
@@ -988,7 +996,7 @@
|
|
|
988
996
|
const world = { w: Math.max(1, x - o.gap + o.padX), h: 1 };
|
|
989
997
|
const nodes = {};
|
|
990
998
|
for (const n of m.nodes) {
|
|
991
|
-
const h = NODE_HEIGHT[n.kind] ?? 54;
|
|
999
|
+
const h = (NODE_HEIGHT[n.kind] ?? 54) + (n.bars?.length ? BARS_EXTRA : 0);
|
|
992
1000
|
if (m.layout === "free") {
|
|
993
1001
|
nodes[n.id] = { id: n.id, x: n.x ?? 0, y: n.y ?? 0, w: n.w ?? 190, h: n.h ?? h };
|
|
994
1002
|
continue;
|
|
@@ -1105,6 +1113,47 @@
|
|
|
1105
1113
|
return `<svg class="spark" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}" aria-hidden="true"><polyline points="${points}"/></svg>`;
|
|
1106
1114
|
}
|
|
1107
1115
|
|
|
1116
|
+
// src/bars.ts
|
|
1117
|
+
var BAR_UNIT = 4;
|
|
1118
|
+
function barsHtml(bars, height = 22) {
|
|
1119
|
+
if (!bars || bars.length === 0) return "";
|
|
1120
|
+
const width = bars.length * BAR_UNIT;
|
|
1121
|
+
const max = Math.max(1, ...bars.map((b) => b.value ?? 0));
|
|
1122
|
+
const lastWithData = bars.map((b) => b.value !== null).lastIndexOf(true);
|
|
1123
|
+
const rects = bars.map((b, i) => {
|
|
1124
|
+
const x = i * BAR_UNIT;
|
|
1125
|
+
const w = BAR_UNIT - 1;
|
|
1126
|
+
const label = esc(b.label ?? (b.value === null ? `${b.key}: no data` : `${b.key}: ${b.value}`));
|
|
1127
|
+
if (b.value === null) {
|
|
1128
|
+
return `<rect class="miss" data-key="${esc(b.key)}" x="${x}" y="${height - 4}" width="${w}" height="4"><title>${label}</title></rect>`;
|
|
1129
|
+
}
|
|
1130
|
+
const h = Math.max(1, b.value / max * (height - 5));
|
|
1131
|
+
const cls = b.kind === "low" ? "low" : i === lastWithData ? "last" : "";
|
|
1132
|
+
return `<rect${cls ? ` class="${cls}"` : ""} data-key="${esc(b.key)}" x="${x}" y="${height - h}" width="${w}" height="${h}"><title>${label}</title></rect>`;
|
|
1133
|
+
});
|
|
1134
|
+
const first = esc(bars[0].key), last = esc(bars[bars.length - 1].key);
|
|
1135
|
+
return `<svg class="bars" viewBox="0 0 ${width} ${height}" preserveAspectRatio="none" role="img" aria-label="Bars, ${first} to ${last}">${rects.join("")}</svg>`;
|
|
1136
|
+
}
|
|
1137
|
+
function markKey(root, key) {
|
|
1138
|
+
for (const r of Array.from(root.querySelectorAll("rect[data-key]"))) {
|
|
1139
|
+
if (key !== null && r.getAttribute("data-key") === key) r.setAttribute("data-hl", "true");
|
|
1140
|
+
else r.removeAttribute("data-hl");
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
function installBars(ctx) {
|
|
1144
|
+
const set = (key) => {
|
|
1145
|
+
if (ctx.hoverKey === key) return;
|
|
1146
|
+
ctx.hoverKey = key;
|
|
1147
|
+
markKey(ctx.els.world, key);
|
|
1148
|
+
ctx.onHoverKey?.(key);
|
|
1149
|
+
};
|
|
1150
|
+
on(ctx, ctx.els.world, "mouseover", (e) => {
|
|
1151
|
+
const bar = e.target?.closest?.("rect[data-key]");
|
|
1152
|
+
set(bar ? bar.getAttribute("data-key") : null);
|
|
1153
|
+
});
|
|
1154
|
+
on(ctx, ctx.els.world, "mouseleave", () => set(null));
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1108
1157
|
// src/render.ts
|
|
1109
1158
|
function loopRoute(ctx, fromId, toId, from, to, channel) {
|
|
1110
1159
|
const detourFor = (end, other) => {
|
|
@@ -1141,7 +1190,7 @@
|
|
|
1141
1190
|
const arrow = n.kind === "chip" ? '<span aria-hidden="true">\u2191 </span>' : "";
|
|
1142
1191
|
const lead = showWord ? `<b class="ev st-${cssId(sid)}">${esc(word)}</b> \xB7 ` : "";
|
|
1143
1192
|
const fold = n.kind === "group" && n.contains?.length ? `<span class="fold">\u25B8 ${n.contains.length}</span>` : "";
|
|
1144
|
-
return `<div class="t"><span class="newdot" title="Added in this view"></span><span class="name">${arrow}${esc(n.title)}</span>${fold}${badge}</div><div class="s${spark ? " has-spark" : ""}" title="${esc(sub)}">${lead}${esc(sub)}</div>${spark}${bar}${improved}${upgrade}`;
|
|
1193
|
+
return `<div class="t"><span class="newdot" title="Added in this view"></span><span class="name">${arrow}${esc(n.title)}</span>${fold}${badge}</div><div class="s${spark ? " has-spark" : ""}" title="${esc(sub)}">${lead}${esc(sub)}</div>${spark}${barsHtml(n.bars)}${bar}${improved}${upgrade}`;
|
|
1145
1194
|
}
|
|
1146
1195
|
function build(ctx) {
|
|
1147
1196
|
const { model, els } = ctx;
|
|
@@ -1176,7 +1225,7 @@
|
|
|
1176
1225
|
for (const n of model.nodes) {
|
|
1177
1226
|
const p = ctx.geom.nodes[n.id];
|
|
1178
1227
|
if (!p) continue;
|
|
1179
|
-
const d = el("div", "node" + (n.kind === "chip" ? " ref" : "") + ((n.colSpan ?? 1) > 1 ? " wide" : ""));
|
|
1228
|
+
const d = el("div", "node" + (n.kind === "chip" ? " ref" : "") + ((n.colSpan ?? 1) > 1 ? " wide" : "") + (n.bars?.length ? " has-bars" : ""));
|
|
1180
1229
|
d.id = "n_" + n.id;
|
|
1181
1230
|
d.tabIndex = 0;
|
|
1182
1231
|
d.setAttribute("role", "button");
|
|
@@ -1311,6 +1360,7 @@
|
|
|
1311
1360
|
d.style.height = r.h + "px";
|
|
1312
1361
|
}
|
|
1313
1362
|
}
|
|
1363
|
+
markKey(ctx.els.world, ctx.hoverKey);
|
|
1314
1364
|
ctx.fn.buildChrome();
|
|
1315
1365
|
ctx.fn.trace(ctx.selected);
|
|
1316
1366
|
ctx.fn.renderInspector();
|
|
@@ -2506,6 +2556,8 @@
|
|
|
2506
2556
|
root,
|
|
2507
2557
|
els,
|
|
2508
2558
|
onSelect: opts.onSelect,
|
|
2559
|
+
onHoverKey: opts.onHoverKey,
|
|
2560
|
+
hoverKey: null,
|
|
2509
2561
|
notes: opts.notes && opts.notes.length ? opts.notes : model.notes ?? [],
|
|
2510
2562
|
nodes: new Map(model.nodes.map((n) => [n.id, n])),
|
|
2511
2563
|
links: model.links,
|
|
@@ -2548,6 +2600,7 @@
|
|
|
2548
2600
|
style.textContent = stateCss(model.states);
|
|
2549
2601
|
install(ctx);
|
|
2550
2602
|
install2(ctx);
|
|
2603
|
+
installBars(ctx);
|
|
2551
2604
|
install3(ctx);
|
|
2552
2605
|
install4(ctx);
|
|
2553
2606
|
install5(ctx);
|
|
@@ -2594,6 +2647,10 @@
|
|
|
2594
2647
|
ctx.fn.fit(animate);
|
|
2595
2648
|
},
|
|
2596
2649
|
select: (id) => ctx.fn.select(id),
|
|
2650
|
+
highlightKey: (key) => {
|
|
2651
|
+
ctx.hoverKey = key;
|
|
2652
|
+
markKey(ctx.els.world, key);
|
|
2653
|
+
},
|
|
2597
2654
|
selected: () => ctx.selected,
|
|
2598
2655
|
mode: () => ctx.mode,
|
|
2599
2656
|
setMode: (id) => ctx.fn.setMode(id),
|
|
@@ -2619,7 +2676,7 @@
|
|
|
2619
2676
|
const changes = diffModels(ctx.model, prepared);
|
|
2620
2677
|
const saved = { view: { ...ctx.view }, mode: ctx.mode, selected: ctx.selected, inspectorOpen: els.inspector.classList.contains("open"), query: ctx.query, hops: ctx.hops, filter: ctx.filter, collapsed: new Set(ctx.collapsed), knownGroups: new Set(ctx.model.nodes.filter((n) => n.kind === "group").map((n) => n.id)), playback: playback.playback() };
|
|
2621
2678
|
api.destroy();
|
|
2622
|
-
const replacement = mount(root, prepared, { notes: opts.notes, onSelect: opts.onSelect, restore: saved });
|
|
2679
|
+
const replacement = mount(root, prepared, { notes: opts.notes, onSelect: opts.onSelect, onHoverKey: opts.onHoverKey, restore: saved });
|
|
2623
2680
|
notifySelection(saved.selected, replacement.selected(), opts.onSelect);
|
|
2624
2681
|
Object.assign(api, replacement);
|
|
2625
2682
|
window.FlowStudio = api;
|
|
@@ -184,6 +184,11 @@
|
|
|
184
184
|
.node .metric-badge i { display:block; height:2px; position:absolute; bottom:0; left:0; background:var(--accent); }
|
|
185
185
|
.node .s.has-spark { padding-right:60px; }
|
|
186
186
|
.node .spark { position:absolute; right:6px; bottom:4px; overflow:visible; }
|
|
187
|
+
.node svg.bars { position:absolute; left:10px; right:10px; bottom:6px; width:calc(100% - 20px); height:22px; overflow:visible; }
|
|
188
|
+
.node svg.bars rect { fill:var(--muted); opacity:.6; }
|
|
189
|
+
.node svg.bars rect.last { fill:var(--accent); opacity:1; }
|
|
190
|
+
.node svg.bars rect.low, .node svg.bars rect.miss { fill:var(--crit); opacity:1; }
|
|
191
|
+
.node svg.bars rect[data-hl="true"] { stroke:var(--fg); stroke-width:1.5px; vector-effect:non-scaling-stroke; opacity:1; }
|
|
187
192
|
.node.gateway .metric-badge { position:absolute; right:9px; top:18px; }
|
|
188
193
|
.node.gateway:has(.metric-badge) .gw-title, .node.gateway.has-metrics .gw-title { width:45%; margin-right:42px; }
|
|
189
194
|
.node .spark polyline, .metrics polyline { fill:none; stroke:var(--accent); stroke-width:1.5; }
|
|
@@ -69,6 +69,8 @@ function validateModel(input) {
|
|
|
69
69
|
const metric = n.metrics;
|
|
70
70
|
const invalidSeries = metric?.series !== void 0 && (!Array.isArray(metric.series) || metric.series.length > 60 || metric.series.some((v) => !Number.isFinite(v)));
|
|
71
71
|
if (metric !== void 0 && (!metric || !Number.isFinite(metric.value) || metric.max != null && !Number.isFinite(metric.max) || invalidSeries)) add("error", "metric-invalid", w, "Metrics need finite values and at most 60 finite series points.");
|
|
72
|
+
const badBars = n.bars !== void 0 && (!Array.isArray(n.bars) || n.bars.length > 120 || n.bars.some((b) => !b || typeof b.key !== "string" || b.value !== null && (typeof b.value !== "number" || !Number.isFinite(b.value) || b.value < 0)));
|
|
73
|
+
if (badBars) add("error", "bars-invalid", w, "Bars need a list of at most 120 items, each with a string key and a finite, non-negative value or null.");
|
|
72
74
|
if (n.sla !== void 0 && (!Number.isFinite(n.sla) || n.sla <= 0)) add("error", "sla-invalid", w, "SLA must be a positive finite number of seconds.");
|
|
73
75
|
}
|
|
74
76
|
for (const n of nodes) {
|
|
@@ -749,6 +751,7 @@ function on(ctx, target, type, fn, opts) {
|
|
|
749
751
|
|
|
750
752
|
// src/layout.ts
|
|
751
753
|
var DEFAULTS = { pitch: 62, top: 64, gap: 34, padX: 24, visible: () => true };
|
|
754
|
+
var BARS_EXTRA = 26;
|
|
752
755
|
var LANE_PAD_TOP = 38;
|
|
753
756
|
var LANE_PAD_BOTTOM = 14;
|
|
754
757
|
function computeGeometry(m, opt = {}) {
|
|
@@ -762,7 +765,7 @@ function computeGeometry(m, opt = {}) {
|
|
|
762
765
|
const world = { w: Math.max(1, x - o.gap + o.padX), h: 1 };
|
|
763
766
|
const nodes = {};
|
|
764
767
|
for (const n of m.nodes) {
|
|
765
|
-
const h = NODE_HEIGHT[n.kind] ?? 54;
|
|
768
|
+
const h = (NODE_HEIGHT[n.kind] ?? 54) + (n.bars?.length ? BARS_EXTRA : 0);
|
|
766
769
|
if (m.layout === "free") {
|
|
767
770
|
nodes[n.id] = { id: n.id, x: n.x ?? 0, y: n.y ?? 0, w: n.w ?? 190, h: n.h ?? h };
|
|
768
771
|
continue;
|
|
@@ -884,6 +887,47 @@ function sparkline(metric, width = 56, height = 12) {
|
|
|
884
887
|
return `<svg class="spark" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}" aria-hidden="true"><polyline points="${points}"/></svg>`;
|
|
885
888
|
}
|
|
886
889
|
|
|
890
|
+
// src/bars.ts
|
|
891
|
+
var BAR_UNIT = 4;
|
|
892
|
+
function barsHtml(bars, height = 22) {
|
|
893
|
+
if (!bars || bars.length === 0) return "";
|
|
894
|
+
const width = bars.length * BAR_UNIT;
|
|
895
|
+
const max = Math.max(1, ...bars.map((b) => b.value ?? 0));
|
|
896
|
+
const lastWithData = bars.map((b) => b.value !== null).lastIndexOf(true);
|
|
897
|
+
const rects = bars.map((b, i) => {
|
|
898
|
+
const x = i * BAR_UNIT;
|
|
899
|
+
const w = BAR_UNIT - 1;
|
|
900
|
+
const label = esc(b.label ?? (b.value === null ? `${b.key}: no data` : `${b.key}: ${b.value}`));
|
|
901
|
+
if (b.value === null) {
|
|
902
|
+
return `<rect class="miss" data-key="${esc(b.key)}" x="${x}" y="${height - 4}" width="${w}" height="4"><title>${label}</title></rect>`;
|
|
903
|
+
}
|
|
904
|
+
const h = Math.max(1, b.value / max * (height - 5));
|
|
905
|
+
const cls = b.kind === "low" ? "low" : i === lastWithData ? "last" : "";
|
|
906
|
+
return `<rect${cls ? ` class="${cls}"` : ""} data-key="${esc(b.key)}" x="${x}" y="${height - h}" width="${w}" height="${h}"><title>${label}</title></rect>`;
|
|
907
|
+
});
|
|
908
|
+
const first = esc(bars[0].key), last = esc(bars[bars.length - 1].key);
|
|
909
|
+
return `<svg class="bars" viewBox="0 0 ${width} ${height}" preserveAspectRatio="none" role="img" aria-label="Bars, ${first} to ${last}">${rects.join("")}</svg>`;
|
|
910
|
+
}
|
|
911
|
+
function markKey(root, key) {
|
|
912
|
+
for (const r of Array.from(root.querySelectorAll("rect[data-key]"))) {
|
|
913
|
+
if (key !== null && r.getAttribute("data-key") === key) r.setAttribute("data-hl", "true");
|
|
914
|
+
else r.removeAttribute("data-hl");
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
function installBars(ctx) {
|
|
918
|
+
const set = (key) => {
|
|
919
|
+
if (ctx.hoverKey === key) return;
|
|
920
|
+
ctx.hoverKey = key;
|
|
921
|
+
markKey(ctx.els.world, key);
|
|
922
|
+
ctx.onHoverKey?.(key);
|
|
923
|
+
};
|
|
924
|
+
on(ctx, ctx.els.world, "mouseover", (e) => {
|
|
925
|
+
const bar = e.target?.closest?.("rect[data-key]");
|
|
926
|
+
set(bar ? bar.getAttribute("data-key") : null);
|
|
927
|
+
});
|
|
928
|
+
on(ctx, ctx.els.world, "mouseleave", () => set(null));
|
|
929
|
+
}
|
|
930
|
+
|
|
887
931
|
// src/render.ts
|
|
888
932
|
function loopRoute(ctx, fromId, toId, from, to, channel) {
|
|
889
933
|
const detourFor = (end, other) => {
|
|
@@ -920,7 +964,7 @@ function nodeHtml(ctx, n) {
|
|
|
920
964
|
const arrow = n.kind === "chip" ? '<span aria-hidden="true">\u2191 </span>' : "";
|
|
921
965
|
const lead = showWord ? `<b class="ev st-${cssId(sid)}">${esc(word)}</b> \xB7 ` : "";
|
|
922
966
|
const fold = n.kind === "group" && n.contains?.length ? `<span class="fold">\u25B8 ${n.contains.length}</span>` : "";
|
|
923
|
-
return `<div class="t"><span class="newdot" title="Added in this view"></span><span class="name">${arrow}${esc(n.title)}</span>${fold}${badge}</div><div class="s${spark ? " has-spark" : ""}" title="${esc(sub)}">${lead}${esc(sub)}</div>${spark}${bar}${improved}${upgrade}`;
|
|
967
|
+
return `<div class="t"><span class="newdot" title="Added in this view"></span><span class="name">${arrow}${esc(n.title)}</span>${fold}${badge}</div><div class="s${spark ? " has-spark" : ""}" title="${esc(sub)}">${lead}${esc(sub)}</div>${spark}${barsHtml(n.bars)}${bar}${improved}${upgrade}`;
|
|
924
968
|
}
|
|
925
969
|
function build(ctx) {
|
|
926
970
|
const { model, els } = ctx;
|
|
@@ -955,7 +999,7 @@ function build(ctx) {
|
|
|
955
999
|
for (const n of model.nodes) {
|
|
956
1000
|
const p = ctx.geom.nodes[n.id];
|
|
957
1001
|
if (!p) continue;
|
|
958
|
-
const d = el("div", "node" + (n.kind === "chip" ? " ref" : "") + ((n.colSpan ?? 1) > 1 ? " wide" : ""));
|
|
1002
|
+
const d = el("div", "node" + (n.kind === "chip" ? " ref" : "") + ((n.colSpan ?? 1) > 1 ? " wide" : "") + (n.bars?.length ? " has-bars" : ""));
|
|
959
1003
|
d.id = "n_" + n.id;
|
|
960
1004
|
d.tabIndex = 0;
|
|
961
1005
|
d.setAttribute("role", "button");
|
|
@@ -1090,6 +1134,7 @@ function paint(ctx, fromSwitch = false) {
|
|
|
1090
1134
|
d.style.height = r.h + "px";
|
|
1091
1135
|
}
|
|
1092
1136
|
}
|
|
1137
|
+
markKey(ctx.els.world, ctx.hoverKey);
|
|
1093
1138
|
ctx.fn.buildChrome();
|
|
1094
1139
|
ctx.fn.trace(ctx.selected);
|
|
1095
1140
|
ctx.fn.renderInspector();
|
|
@@ -2310,6 +2355,8 @@ function mount(root, model, opts = {}) {
|
|
|
2310
2355
|
root,
|
|
2311
2356
|
els,
|
|
2312
2357
|
onSelect: opts.onSelect,
|
|
2358
|
+
onHoverKey: opts.onHoverKey,
|
|
2359
|
+
hoverKey: null,
|
|
2313
2360
|
notes: opts.notes && opts.notes.length ? opts.notes : model.notes ?? [],
|
|
2314
2361
|
nodes: new Map(model.nodes.map((n) => [n.id, n])),
|
|
2315
2362
|
links: model.links,
|
|
@@ -2352,6 +2399,7 @@ function mount(root, model, opts = {}) {
|
|
|
2352
2399
|
style.textContent = stateCss(model.states);
|
|
2353
2400
|
install(ctx);
|
|
2354
2401
|
install2(ctx);
|
|
2402
|
+
installBars(ctx);
|
|
2355
2403
|
install3(ctx);
|
|
2356
2404
|
install4(ctx);
|
|
2357
2405
|
install5(ctx);
|
|
@@ -2398,6 +2446,10 @@ function mount(root, model, opts = {}) {
|
|
|
2398
2446
|
ctx.fn.fit(animate);
|
|
2399
2447
|
},
|
|
2400
2448
|
select: (id) => ctx.fn.select(id),
|
|
2449
|
+
highlightKey: (key) => {
|
|
2450
|
+
ctx.hoverKey = key;
|
|
2451
|
+
markKey(ctx.els.world, key);
|
|
2452
|
+
},
|
|
2401
2453
|
selected: () => ctx.selected,
|
|
2402
2454
|
mode: () => ctx.mode,
|
|
2403
2455
|
setMode: (id) => ctx.fn.setMode(id),
|
|
@@ -2423,7 +2475,7 @@ function mount(root, model, opts = {}) {
|
|
|
2423
2475
|
const changes = diffModels(ctx.model, prepared);
|
|
2424
2476
|
const saved = { view: { ...ctx.view }, mode: ctx.mode, selected: ctx.selected, inspectorOpen: els.inspector.classList.contains("open"), query: ctx.query, hops: ctx.hops, filter: ctx.filter, collapsed: new Set(ctx.collapsed), knownGroups: new Set(ctx.model.nodes.filter((n) => n.kind === "group").map((n) => n.id)), playback: playback.playback() };
|
|
2425
2477
|
api.destroy();
|
|
2426
|
-
const replacement = mount(root, prepared, { notes: opts.notes, onSelect: opts.onSelect, restore: saved });
|
|
2478
|
+
const replacement = mount(root, prepared, { notes: opts.notes, onSelect: opts.onSelect, onHoverKey: opts.onHoverKey, restore: saved });
|
|
2427
2479
|
notifySelection(saved.selected, replacement.selected(), opts.onSelect);
|
|
2428
2480
|
Object.assign(api, replacement);
|
|
2429
2481
|
window.FlowStudio = api;
|
package/bundles/manifest.json
CHANGED
package/package.json
CHANGED