@tuongaz/seeflow 0.1.71 → 0.1.75

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.
Files changed (72) hide show
  1. package/dist/web/assets/arc-DtxbFlka.js +1 -0
  2. package/dist/web/assets/architectureDiagram-3BPJPVTR-BE013nUd.js +36 -0
  3. package/dist/web/assets/band-BEGRCkg7.js +1 -0
  4. package/dist/web/assets/blockDiagram-GPEHLZMM-Dp6be3Ms.js +132 -0
  5. package/dist/web/assets/c4Diagram-AAUBKEIU-DaXH8gyS.js +10 -0
  6. package/dist/web/assets/channel-BXI2S8ym.js +1 -0
  7. package/dist/web/assets/chart-CTfwAwIW.js +73 -0
  8. package/dist/web/assets/chunk-2J33WTMH-DBOxtwXe.js +1 -0
  9. package/dist/web/assets/chunk-4BX2VUAB-BkqIaZzX.js +1 -0
  10. package/dist/web/assets/chunk-55IACEB6-Bf2GGBjw.js +1 -0
  11. package/dist/web/assets/chunk-727SXJPM-DT7H_Ach.js +206 -0
  12. package/dist/web/assets/chunk-AQP2D5EJ-VrM-sHKP.js +231 -0
  13. package/dist/web/assets/chunk-FMBD7UC4-Cl6A8JUa.js +15 -0
  14. package/dist/web/assets/chunk-ND2GUHAM-LRV96SE3.js +1 -0
  15. package/dist/web/assets/chunk-QZHKN3VN-Dj5_UVo9.js +1 -0
  16. package/dist/web/assets/classDiagram-4FO5ZUOK-Dm1vuLfb.js +1 -0
  17. package/dist/web/assets/classDiagram-v2-Q7XG4LA2-Dm1vuLfb.js +1 -0
  18. package/dist/web/assets/{code-block-CagXzbCC.js → code-block-Be3Lawg0.js} +1 -1
  19. package/dist/web/assets/cose-bilkent-S5V4N54A-4_XIQLKW.js +1 -0
  20. package/dist/web/assets/cytoscape.esm-BHYC38rz.js +331 -0
  21. package/dist/web/assets/dagre-BM42HDAG-Csr5BSLw.js +4 -0
  22. package/dist/web/assets/defaultLocale-CrowFXzY.js +1 -0
  23. package/dist/web/assets/diagram-2AECGRRQ-D9cawnSD.js +43 -0
  24. package/dist/web/assets/diagram-5GNKFQAL-Ba9mTl4r.js +10 -0
  25. package/dist/web/assets/diagram-KO2AKTUF-D_gNaP7M.js +3 -0
  26. package/dist/web/assets/diagram-LMA3HP47-BwPi3QWV.js +24 -0
  27. package/dist/web/assets/diagram-OG6HWLK6-C2iDWN55.js +24 -0
  28. package/dist/web/assets/erDiagram-TEJ5UH35-MBy10Qfr.js +85 -0
  29. package/dist/web/assets/flowDiagram-I6XJVG4X-CgVoK-B7.js +162 -0
  30. package/dist/web/assets/ganttDiagram-6RSMTGT7-DZZ43-Du.js +292 -0
  31. package/dist/web/assets/gitGraphDiagram-PVQCEYII-BwYPxvng.js +106 -0
  32. package/dist/web/assets/graph-Dqkl27Ch.js +1 -0
  33. package/dist/web/assets/index-DljfurDC.css +1 -0
  34. package/dist/web/assets/{index-NG9m5oBj.js → index-DrmDilPO.js} +1738 -1737
  35. package/dist/web/assets/{index.es-xel3SIo8.js → index.es-DpqYSimZ.js} +1 -1
  36. package/dist/web/assets/infoDiagram-5YYISTIA-j7zbBRny.js +2 -0
  37. package/dist/web/assets/init-BFKUnIhM.js +1 -0
  38. package/dist/web/assets/ishikawaDiagram-YF4QCWOH-DAp6W4Gj.js +70 -0
  39. package/dist/web/assets/journeyDiagram-JHISSGLW-Dde2JSR2.js +139 -0
  40. package/dist/web/assets/{jspdf.es.min-CIMMqiVE.js → jspdf.es.min-tC6T-F7r.js} +3 -3
  41. package/dist/web/assets/kanban-definition-UN3LZRKU-os60zjrz.js +89 -0
  42. package/dist/web/assets/katex-C5jXJg4s.js +257 -0
  43. package/dist/web/assets/layout-DO9UvE1P.js +1 -0
  44. package/dist/web/assets/linear-f3DePitM.js +1 -0
  45. package/dist/web/assets/markdown-Feo1eGLy.js +1 -0
  46. package/dist/web/assets/mermaid.core-DoXF1I4r.js +307 -0
  47. package/dist/web/assets/mindmap-definition-RKZ34NQL-D6K4uyrn.js +96 -0
  48. package/dist/web/assets/ordinal-BQ3KlzSu.js +1 -0
  49. package/dist/web/assets/pieDiagram-4H26LBE5-C4UX0DPX.js +30 -0
  50. package/dist/web/assets/quadrantDiagram-W4KKPZXB-iSE2IF5i.js +7 -0
  51. package/dist/web/assets/requirementDiagram-4Y6WPE33-D_lHCX3z.js +84 -0
  52. package/dist/web/assets/sankeyDiagram-5OEKKPKP-B0-DYq8M.js +40 -0
  53. package/dist/web/assets/sequenceDiagram-3UESZ5HK-BQM0i8wy.js +162 -0
  54. package/dist/web/assets/stateDiagram-AJRCARHV-DD0djwwD.js +1 -0
  55. package/dist/web/assets/stateDiagram-v2-BHNVJYJU-C-OF3f9a.js +1 -0
  56. package/dist/web/assets/step-CWvwoXpJ.js +1 -0
  57. package/dist/web/assets/time-BczydnL9.js +1 -0
  58. package/dist/web/assets/timeline-definition-PNZ67QCA-BjCNA-ke.js +120 -0
  59. package/dist/web/assets/vennDiagram-CIIHVFJN-G2L4S-_F.js +34 -0
  60. package/dist/web/assets/wardley-L42UT6IY-cmE8YucU.js +173 -0
  61. package/dist/web/assets/wardleyDiagram-YWT4CUSO-BM_S_KMT.js +78 -0
  62. package/dist/web/assets/xychartDiagram-2RQKCTM6-B_aQbaAw.js +7 -0
  63. package/dist/web/index.html +2 -2
  64. package/package.json +1 -1
  65. package/src/cli-manifest.ts +105 -37
  66. package/src/mcp.ts +8 -4
  67. package/src/schema-catalog.ts +29 -2
  68. package/src/schema.ts +91 -26
  69. package/src/watcher.ts +61 -13
  70. package/dist/web/assets/chart-CpHY42Vq.js +0 -73
  71. package/dist/web/assets/index-DJdVTUx5.css +0 -1
  72. package/dist/web/assets/markdown-DAvRjvM0.js +0 -1
package/src/schema.ts CHANGED
@@ -58,12 +58,30 @@ const NodeVisualBaseShape = {
58
58
  // every visual works without a label. `icon` is decorative-by-default; the
59
59
  // `type:'icon'` variant overrides it to required.
60
60
  const NodeSemanticBaseShape = {
61
- name: z.string().optional(),
62
- description: z.string().optional(),
63
- detail: z.string().optional(),
64
- // Decorative header glyph. Lucide icon name (kebab-case) resolved by the
65
- // canvas <Icon> primitive; falls back to a placeholder when unknown.
66
- icon: z.string().optional(),
61
+ name: z
62
+ .string()
63
+ .optional()
64
+ .describe(
65
+ "Short human-readable label rendered in the node header. Omit on decorative nodes (sticky, type:'text') where the body content IS the label.",
66
+ ),
67
+ description: z
68
+ .string()
69
+ .optional()
70
+ .describe(
71
+ 'One-sentence summary surfaced in the detail sidebar and tooltips. Set whenever a reader would benefit from more context than the name alone.',
72
+ ),
73
+ detail: z
74
+ .string()
75
+ .optional()
76
+ .describe(
77
+ 'Long-form markdown shown in the detail sidebar. Supports headings, lists, code, and ```mermaid``` blocks (the canvas renders mermaid inline). Use for runbooks, schemas, sequence diagrams.',
78
+ ),
79
+ icon: z
80
+ .string()
81
+ .optional()
82
+ .describe(
83
+ "Decorative header glyph (Lucide icon name in kebab-case, e.g. 'database', 'cloud-upload'). Falls back to a placeholder when unknown. On type:'icon' nodes the icon IS the visual and is required.",
84
+ ),
67
85
  };
68
86
 
69
87
  // Relative-path safety refine (textual). Mirrors the same rule used for
@@ -124,10 +142,22 @@ export const StatusReportSchema = z.object({
124
142
  ts: z.number().int().positive().optional(),
125
143
  });
126
144
 
127
- export const StateSourceSchema = z.discriminatedUnion('kind', [
128
- z.object({ kind: z.literal('request') }),
129
- z.object({ kind: z.literal('event') }),
130
- ]);
145
+ export const StateSourceSchema = z
146
+ .discriminatedUnion('kind', [
147
+ z
148
+ .object({ kind: z.literal('request') })
149
+ .describe(
150
+ 'Poll-based state: `statusAction` samples an endpoint on an interval (REST GET, healthcheck, DB query). Use for services you can probe.',
151
+ ),
152
+ z
153
+ .object({ kind: z.literal('event') })
154
+ .describe(
155
+ 'Push-based state: `statusAction` subscribes to a stream (SSE, webhook, queue topic). Use for message buses, async pipelines, anything that announces state changes.',
156
+ ),
157
+ ])
158
+ .describe(
159
+ "Declares how this node's live state is sourced. Pair with `statusAction` so observers can tell at a glance whether the node's status is polled or pushed.",
160
+ );
131
161
 
132
162
  // Capabilities — any subset of these makes a node Playable / Stateful. All
133
163
  // optional, valid on every node type. A node is Playable iff `playAction` is
@@ -135,16 +165,27 @@ export const StateSourceSchema = z.discriminatedUnion('kind', [
135
165
  // informational metadata that pairs with statusAction. `handlerModule` is
136
166
  // reserved for a future skills runtime and is schema-only at v1.
137
167
  const NodeCapabilitiesShape = {
138
- playAction: PlayActionSchema.optional(),
139
- statusAction: StatusActionSchema.optional(),
140
- stateSource: StateSourceSchema.optional(),
141
- handlerModule: z.string().optional(),
168
+ playAction: PlayActionSchema.optional().describe(
169
+ 'One-shot script the user invokes by clicking the node (a "Play" affordance). Studio spawns `<interpreter> [...args] <scriptPath>` from the project root with the optional `input` JSON-serialized to stdin. Use for HTTP calls, CLI invocations, anything triggered on demand.',
170
+ ),
171
+ statusAction: StatusActionSchema.optional().describe(
172
+ "Long-running status probe. Same spawn shape as `playAction` but the script ticks continuously and writes one JSON `StatusReport` per line to stdout; the canvas renders the most recent state badge. Pair with `stateSource` so observers know whether it's poll- or push-based.",
173
+ ),
174
+ stateSource: StateSourceSchema.optional().describe(
175
+ 'Set this on any node that has a `statusAction`. Choose `request` for poll-based sources, `event` for push-based sources. Omit on decorative nodes (sticky, label-only text) and on action nodes whose only behavior is `playAction`.',
176
+ ),
177
+ handlerModule: z
178
+ .string()
179
+ .optional()
180
+ .describe(
181
+ 'Reserved for the v2 skills runtime. Schema-only at v1 — set by tooling; leave undefined when authoring flows by hand.',
182
+ ),
142
183
  };
143
184
 
144
- // 12 flat node types. The first 9 are geometric/illustrative and share
145
- // GeometricNodeData. `image`, `html`, `icon` carry per-type fields.
146
- // The renderer picks the SVG / chrome by `type`; the schema treats them
147
- // (apart from the per-type fields below) as identical.
185
+ // 13 flat node types. The first 9 are geometric/illustrative and share
186
+ // GeometricNodeData. `image`, `html`, `icon`, `component` carry per-type
187
+ // fields. The renderer picks the SVG / chrome by `type`; the schema treats
188
+ // them (apart from the per-type fields below) as identical.
148
189
  export const GEOMETRIC_NODE_TYPES = [
149
190
  'rectangle',
150
191
  'ellipse',
@@ -454,10 +495,19 @@ const FlowImageNodeData = z
454
495
  .object({
455
496
  ...NodeSemanticBaseShape,
456
497
  ...NodeCapabilitiesShape,
457
- path: z.string().min(1).refine(isCleanRelativePath, {
458
- message: 'path must be a relative path under the project root (no absolute / traversal)',
459
- }),
460
- alt: z.string().optional(),
498
+ path: z
499
+ .string()
500
+ .min(1)
501
+ .refine(isCleanRelativePath, {
502
+ message: 'path must be a relative path under the project root (no absolute / traversal)',
503
+ })
504
+ .describe(
505
+ "Project-root-relative path to the image file. MUST start with 'nodes/<id>/' so the delete_node cascade owns cleanup. Supported formats: PNG, JPEG, SVG, GIF, WebP.",
506
+ ),
507
+ alt: z
508
+ .string()
509
+ .optional()
510
+ .describe('Accessibility alt text. Set on every non-decorative image.'),
461
511
  })
462
512
  .strict();
463
513
 
@@ -465,7 +515,12 @@ const FlowHtmlNodeData = z
465
515
  .object({
466
516
  ...NodeSemanticBaseShape,
467
517
  ...NodeCapabilitiesShape,
468
- html: z.string().optional(),
518
+ html: z
519
+ .string()
520
+ .optional()
521
+ .describe(
522
+ "Inline HTML rendered inside the node. Studio externalizes this to `<project>/nodes/<id>/view.html` on write and inlines it back on read, so authors always see the actual string. Sanitized before injection. Escape-hatch for content the geometric/icon/component visuals don't cover.",
523
+ ),
469
524
  })
470
525
  .strict();
471
526
 
@@ -473,8 +528,13 @@ const FlowIconNodeData = z
473
528
  .object({
474
529
  ...NodeSemanticBaseShape,
475
530
  ...NodeCapabilitiesShape,
476
- icon: z.string().min(1),
477
- alt: z.string().optional(),
531
+ icon: z
532
+ .string()
533
+ .min(1)
534
+ .describe(
535
+ "Required Lucide icon name (kebab-case, e.g. 'cloud-upload', 'database'). On type:'icon' nodes the icon IS the visual — overrides the optional decorative `icon` from the semantic base.",
536
+ ),
537
+ alt: z.string().optional().describe('Accessibility alt text for the icon glyph.'),
478
538
  })
479
539
  .strict();
480
540
 
@@ -486,7 +546,12 @@ const FlowComponentNodeData = z
486
546
  .object({
487
547
  ...NodeSemanticBaseShape,
488
548
  ...NodeCapabilitiesShape,
489
- autoSize: z.boolean().optional(),
549
+ autoSize: z
550
+ .boolean()
551
+ .optional()
552
+ .describe(
553
+ 'When true the renderer measures its content and React Flow sizes the wrapper around it. Default (undefined / false) uses the persisted width/height path.',
554
+ ),
490
555
  })
491
556
  .strict();
492
557
 
package/src/watcher.ts CHANGED
@@ -20,6 +20,15 @@ import {
20
20
 
21
21
  const DEFAULT_DEBOUNCE_MS = 100;
22
22
 
23
+ /**
24
+ * Polling backup interval. fs.watch on macOS occasionally drops events
25
+ * (notably right after a fresh watcher is registered), so a low-frequency
26
+ * stat poll over every watched handle guarantees external writes still
27
+ * trigger reparse+broadcast. Own-write echoes are still suppressed by
28
+ * the writtenHashes ring, so this doesn't double-fire on server writes.
29
+ */
30
+ const DEFAULT_POLL_INTERVAL_MS = 500;
31
+
23
32
  /** Max recent self-write hashes retained per flow for own-echo suppression. */
24
33
  const WRITTEN_HASH_RING_SIZE = 4;
25
34
 
@@ -52,6 +61,12 @@ export interface WatcherDeps {
52
61
  events: EventBus;
53
62
  /** Override for tests. */
54
63
  debounceMs?: number;
64
+ /**
65
+ * Polling backup interval for missed fs.watch events. Defaults to 500ms.
66
+ * Pass `0` to disable (tests that rely on deterministic fs.watch-only
67
+ * behaviour).
68
+ */
69
+ pollIntervalMs?: number;
55
70
  }
56
71
 
57
72
  export interface FlowWatcher {
@@ -276,6 +291,7 @@ const closeFileWatchers = (handle: WatchHandle): void => {
276
291
  export function createWatcher(deps: WatcherDeps): FlowWatcher {
277
292
  const { registry, events } = deps;
278
293
  const debounceMs = deps.debounceMs ?? DEFAULT_DEBOUNCE_MS;
294
+ const pollIntervalMs = deps.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
279
295
 
280
296
  const handles = new Map<string, WatchHandle>();
281
297
  const snapshots = new Map<string, FlowSnapshot>();
@@ -455,6 +471,49 @@ export function createWatcher(deps: WatcherDeps): FlowWatcher {
455
471
  });
456
472
  };
457
473
 
474
+ /**
475
+ * Common path for "something on disk may have changed" — used by both
476
+ * fs.watch callbacks and the polling backup. Debounces, drops own-write
477
+ * echoes, then reparses + broadcasts. Idempotent under repeated calls
478
+ * within the debounce window.
479
+ */
480
+ const scheduleChange = (flowId: string): void => {
481
+ const handle = handles.get(flowId);
482
+ if (!handle) return;
483
+ if (handle.debounceTimer) clearTimeout(handle.debounceTimer);
484
+ handle.debounceTimer = setTimeout(() => {
485
+ handle.debounceTimer = null;
486
+ // Own-write dedupe: if the on-disk bytes match what the server just
487
+ // wrote (recent hash in the ring), this is our own echo — drop it.
488
+ // notifyWritten already broadcast and seeded the snapshot.
489
+ const combined = readCombinedFromDisk(handle.filePath);
490
+ if (combined !== null && isOwnWriteEcho(flowId, sha256Hex(combined))) return;
491
+ const snap = reparse(flowId);
492
+ if (snap) broadcastReload(flowId, snap);
493
+ }, debounceMs);
494
+ };
495
+
496
+ // Single shared interval that stat-polls every watched handle. Cheap (one
497
+ // statSync per watched flow per tick) and bounded — flows that registry
498
+ // doesn't know about aren't watched, so the loop scales with active
499
+ // projects, not historic ones. Backs up macOS fs.watch's occasional
500
+ // dropped events; the debounce + own-write dedupe in scheduleChange
501
+ // keeps it from double-firing alongside a fs.watch hit.
502
+ const pollTimer: ReturnType<typeof setInterval> | null =
503
+ pollIntervalMs > 0
504
+ ? setInterval(() => {
505
+ for (const [flowId, handle] of handles) {
506
+ const seen = lastSeenMtimes.get(flowId);
507
+ if (seen === undefined) continue;
508
+ const now = combinedMtimeMs(handle.filePath);
509
+ if (now > seen) scheduleChange(flowId);
510
+ }
511
+ }, pollIntervalMs)
512
+ : null;
513
+ // Don't keep the event loop alive on this timer alone — the studio process
514
+ // should exit cleanly when nothing else is pending.
515
+ pollTimer?.unref?.();
516
+
458
517
  const startWatch = (flowId: string) => {
459
518
  const existing = handles.get(flowId);
460
519
  if (existing) {
@@ -484,19 +543,7 @@ export function createWatcher(deps: WatcherDeps): FlowWatcher {
484
543
  // React to flow.json, style.json, or rename-on-save events
485
544
  // (some platforms emit those with no filename).
486
545
  if (changed && changed !== base && changed !== 'style.json') return;
487
- const handle = handles.get(flowId);
488
- if (!handle) return;
489
- if (handle.debounceTimer) clearTimeout(handle.debounceTimer);
490
- handle.debounceTimer = setTimeout(() => {
491
- handle.debounceTimer = null;
492
- // Own-write dedupe: if the on-disk bytes match what the server just
493
- // wrote (recent hash in the ring), this is our own echo — drop it.
494
- // notifyWritten already broadcast and seeded the snapshot.
495
- const combined = readCombinedFromDisk(filePath);
496
- if (combined !== null && isOwnWriteEcho(flowId, sha256Hex(combined))) return;
497
- const snap = reparse(flowId);
498
- if (snap) broadcastReload(flowId, snap);
499
- }, debounceMs);
546
+ scheduleChange(flowId);
500
547
  });
501
548
  } catch (err) {
502
549
  console.error(`[watcher] failed to watch ${dir} for flow ${flowId}:`, err);
@@ -551,6 +598,7 @@ export function createWatcher(deps: WatcherDeps): FlowWatcher {
551
598
  for (const entry of registry.list()) startWatch(entry.id);
552
599
  },
553
600
  closeAll() {
601
+ if (pollTimer) clearInterval(pollTimer);
554
602
  for (const [, h] of handles) {
555
603
  h.fsWatcher.close();
556
604
  if (h.debounceTimer) clearTimeout(h.debounceTimer);