@tuongaz/seeflow 0.1.93 → 0.1.98

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 (66) hide show
  1. package/dist/web/assets/{architectureDiagram-3BPJPVTR-vgRdQoho.js → architectureDiagram-3BPJPVTR-DU1dz67I.js} +1 -1
  2. package/dist/web/assets/{blockDiagram-GPEHLZMM-D1bbs0Ix.js → blockDiagram-GPEHLZMM-DSmqToeJ.js} +1 -1
  3. package/dist/web/assets/{c4Diagram-AAUBKEIU-BHmsWf1o.js → c4Diagram-AAUBKEIU-CNP992lo.js} +1 -1
  4. package/dist/web/assets/channel-v9-wsv2r.js +1 -0
  5. package/dist/web/assets/{chart-CawZdlOV.js → chart-DF01veTJ.js} +1 -1
  6. package/dist/web/assets/{chunk-2J33WTMH-BLQaRMqq.js → chunk-2J33WTMH-LVhrEzuA.js} +1 -1
  7. package/dist/web/assets/{chunk-4BX2VUAB-DTivSBmA.js → chunk-4BX2VUAB-7QT-8Ea8.js} +1 -1
  8. package/dist/web/assets/{chunk-55IACEB6-DZ5Ond8O.js → chunk-55IACEB6-DekK1DpQ.js} +1 -1
  9. package/dist/web/assets/{chunk-727SXJPM-CwKBkYmr.js → chunk-727SXJPM-B2Ar-3-Y.js} +1 -1
  10. package/dist/web/assets/{chunk-AQP2D5EJ-CAiwEMmH.js → chunk-AQP2D5EJ-DFL9j35G.js} +1 -1
  11. package/dist/web/assets/{chunk-FMBD7UC4-DCWBhj6w.js → chunk-FMBD7UC4-BDQ2jKs8.js} +1 -1
  12. package/dist/web/assets/{chunk-ND2GUHAM-vLte473x.js → chunk-ND2GUHAM-DbrZoSgU.js} +1 -1
  13. package/dist/web/assets/{chunk-QZHKN3VN-Bcu6ixss.js → chunk-QZHKN3VN-BBiKBSEG.js} +1 -1
  14. package/dist/web/assets/classDiagram-4FO5ZUOK-CY00ktDc.js +1 -0
  15. package/dist/web/assets/classDiagram-v2-Q7XG4LA2-CY00ktDc.js +1 -0
  16. package/dist/web/assets/{code-block-Q3inQ3vw.js → code-block-Dqo0Jd1G.js} +1 -1
  17. package/dist/web/assets/{cose-bilkent-S5V4N54A-Cb8kZ6Km.js → cose-bilkent-S5V4N54A-CGpaWYc4.js} +1 -1
  18. package/dist/web/assets/{dagre-BM42HDAG-Xws4A-Mi.js → dagre-BM42HDAG-DTV9Prbv.js} +1 -1
  19. package/dist/web/assets/{diagram-2AECGRRQ-S1kziNDg.js → diagram-2AECGRRQ-DLqTKsZ1.js} +1 -1
  20. package/dist/web/assets/{diagram-5GNKFQAL-eoeXtzaC.js → diagram-5GNKFQAL-WeRlWvVf.js} +1 -1
  21. package/dist/web/assets/{diagram-KO2AKTUF-DktxY0CQ.js → diagram-KO2AKTUF-Bla9fFa3.js} +1 -1
  22. package/dist/web/assets/{diagram-LMA3HP47-myBeIjhs.js → diagram-LMA3HP47-JmfK87A4.js} +1 -1
  23. package/dist/web/assets/{diagram-OG6HWLK6-CFj88ujv.js → diagram-OG6HWLK6-CWLzBnkl.js} +1 -1
  24. package/dist/web/assets/{erDiagram-TEJ5UH35-WaEyv1iP.js → erDiagram-TEJ5UH35-zIaBabk9.js} +1 -1
  25. package/dist/web/assets/{flowDiagram-I6XJVG4X-DAZ3T2Zd.js → flowDiagram-I6XJVG4X-Bue3Noo8.js} +1 -1
  26. package/dist/web/assets/{ganttDiagram-6RSMTGT7-uY09PoQq.js → ganttDiagram-6RSMTGT7-BHdGoBmM.js} +1 -1
  27. package/dist/web/assets/{gitGraphDiagram-PVQCEYII-BSJHmF4Z.js → gitGraphDiagram-PVQCEYII-DY8i3xDG.js} +1 -1
  28. package/dist/web/assets/index-B-NP-7Oo.js +8624 -0
  29. package/dist/web/assets/{index.es-BJkNyJb3.js → index.es-PfKZum8P.js} +1 -1
  30. package/dist/web/assets/{infoDiagram-5YYISTIA-BJIRmQdX.js → infoDiagram-5YYISTIA-CjlY190O.js} +1 -1
  31. package/dist/web/assets/{ishikawaDiagram-YF4QCWOH-Dk3i-Xkk.js → ishikawaDiagram-YF4QCWOH-UlDRmAcX.js} +1 -1
  32. package/dist/web/assets/{journeyDiagram-JHISSGLW-DnJaLB-P.js → journeyDiagram-JHISSGLW-BQU5iM6n.js} +1 -1
  33. package/dist/web/assets/{jspdf.es.min-C9Avn9P2.js → jspdf.es.min-DZwedYLb.js} +3 -3
  34. package/dist/web/assets/{kanban-definition-UN3LZRKU-CAvihSvB.js → kanban-definition-UN3LZRKU-BHjSnSao.js} +1 -1
  35. package/dist/web/assets/{linear-QJGLYtiK.js → linear-DfckWaYF.js} +1 -1
  36. package/dist/web/assets/{markdown-umDyoRvw.js → markdown-DK4rNWyg.js} +1 -1
  37. package/dist/web/assets/{mermaid.core-DjNa-8Hv.js → mermaid.core-Q5Rkziel.js} +4 -4
  38. package/dist/web/assets/{mindmap-definition-RKZ34NQL-DdGdY0IJ.js → mindmap-definition-RKZ34NQL-Cwl5O3Cf.js} +1 -1
  39. package/dist/web/assets/{pieDiagram-4H26LBE5-ByidPHli.js → pieDiagram-4H26LBE5-CfuqpLij.js} +1 -1
  40. package/dist/web/assets/{quadrantDiagram-W4KKPZXB-3-CAwQni.js → quadrantDiagram-W4KKPZXB-DPMXHfS6.js} +1 -1
  41. package/dist/web/assets/{requirementDiagram-4Y6WPE33-Bc0BnzZd.js → requirementDiagram-4Y6WPE33-DnkSyvnm.js} +1 -1
  42. package/dist/web/assets/{sankeyDiagram-5OEKKPKP-CnxTmkwV.js → sankeyDiagram-5OEKKPKP-qgBj0gIh.js} +1 -1
  43. package/dist/web/assets/{sequenceDiagram-3UESZ5HK-DrDm1r9r.js → sequenceDiagram-3UESZ5HK-Dv8lcfkG.js} +1 -1
  44. package/dist/web/assets/{stateDiagram-AJRCARHV-Dgco9NEU.js → stateDiagram-AJRCARHV-BHhRAlSF.js} +1 -1
  45. package/dist/web/assets/stateDiagram-v2-BHNVJYJU-Z4OeqbFi.js +1 -0
  46. package/dist/web/assets/{time-Yxy9gOXu.js → time-DLVqCHvN.js} +1 -1
  47. package/dist/web/assets/{timeline-definition-PNZ67QCA-CquekUx0.js → timeline-definition-PNZ67QCA-Dj-PUcDW.js} +1 -1
  48. package/dist/web/assets/{vennDiagram-CIIHVFJN-BGGVq4Mv.js → vennDiagram-CIIHVFJN-BVu8roqR.js} +1 -1
  49. package/dist/web/assets/{wardley-L42UT6IY-fkIRiPsZ.js → wardley-L42UT6IY-V3a5jBUh.js} +1 -1
  50. package/dist/web/assets/{wardleyDiagram-YWT4CUSO-ih5pIO2M.js → wardleyDiagram-YWT4CUSO-BMdJmf1X.js} +1 -1
  51. package/dist/web/assets/{xychartDiagram-2RQKCTM6-Rwg_Ry62.js → xychartDiagram-2RQKCTM6-iibKxvlU.js} +1 -1
  52. package/dist/web/index.html +1 -1
  53. package/package.json +1 -1
  54. package/src/api.ts +43 -6
  55. package/src/cli-manifest.ts +1 -1
  56. package/src/cli.ts +8 -2
  57. package/src/mcp.ts +2 -0
  58. package/src/operations.ts +51 -24
  59. package/src/schema-catalog.ts +62 -4
  60. package/src/schema.ts +6 -1
  61. package/src/server.ts +6 -1
  62. package/dist/web/assets/channel-C-YVVNMU.js +0 -1
  63. package/dist/web/assets/classDiagram-4FO5ZUOK-BklVRjbL.js +0 -1
  64. package/dist/web/assets/classDiagram-v2-Q7XG4LA2-BklVRjbL.js +0 -1
  65. package/dist/web/assets/index-5X7OVal6.js +0 -8624
  66. package/dist/web/assets/stateDiagram-v2-BHNVJYJU-F-kPNI2H.js +0 -1
package/src/operations.ts CHANGED
@@ -23,6 +23,7 @@ import {
23
23
  removeNodeDir,
24
24
  writeNodeFile,
25
25
  } from './node-files.ts';
26
+ import { seeflowHome } from './paths.ts';
26
27
  import { readProjectManifest, scanProject } from './project-scanner.ts';
27
28
  import { type FlowEntry, type Registry, slugify } from './registry.ts';
28
29
  import {
@@ -53,7 +54,9 @@ export const RegisterBodySchema = z.object({
53
54
  export type RegisterBody = z.infer<typeof RegisterBodySchema>;
54
55
 
55
56
  export const CreateProjectBodySchema = z.object({
56
- path: z.string().min(1),
57
+ // Optional: when omitted the project is scaffolded under
58
+ // <seeflowHome>/projects/<slug-of-name>. See createProjectImpl.
59
+ path: z.string().min(1).optional(),
57
60
  name: z.string().min(1),
58
61
  description: z.string().min(1).optional(),
59
62
  });
@@ -95,15 +98,19 @@ export const NodePatchBodySchema = z
95
98
  type: NodeTypeSchema.optional(),
96
99
  position: PositionBodySchema.optional(),
97
100
  name: z.string().optional(),
98
- borderColor: ColorTokenSchema.optional(),
99
- backgroundColor: ColorTokenSchema.optional(),
100
- borderSize: z.number().min(0).optional(),
101
- borderWidth: z.number().min(0).max(8).optional(),
102
- borderStyle: z.enum(['solid', 'dashed', 'dotted']).optional(),
103
- fontSize: z.number().positive().optional(),
104
- textAlign: z.enum(['left', 'center', 'right']).optional(),
105
- cornerRadius: z.number().min(0).optional(),
106
- shadow: z.number().int().min(0).max(5).optional(),
101
+ // Style/visual keys accept explicit `null` as the clear signal (mirrors
102
+ // `icon`): mergeNodeUpdates strips the key from disk, so undo can revert a
103
+ // style field back to its pre-edit "unset" default instead of being a
104
+ // no-op (a dropped-undefined PATCH leaves the field untouched).
105
+ borderColor: ColorTokenSchema.nullable().optional(),
106
+ backgroundColor: ColorTokenSchema.nullable().optional(),
107
+ borderSize: z.number().min(0).nullable().optional(),
108
+ borderWidth: z.number().min(0).max(8).nullable().optional(),
109
+ borderStyle: z.enum(['solid', 'dashed', 'dotted']).nullable().optional(),
110
+ fontSize: z.number().positive().nullable().optional(),
111
+ textAlign: z.enum(['left', 'center', 'right']).nullable().optional(),
112
+ cornerRadius: z.number().min(0).nullable().optional(),
113
+ shadow: z.number().int().min(0).max(5).nullable().optional(),
107
114
  width: z.number().positive().optional(),
108
115
  height: z.number().positive().optional(),
109
116
  // type:'html'-only: when true, the renderer measures content and React Flow
@@ -112,11 +119,12 @@ export const NodePatchBodySchema = z
112
119
  autoSize: z.boolean().optional(),
113
120
  // type:'icon'-only: stroke color token. Lands at data.color; the
114
121
  // post-merge ResolvedFlowSchema reparse gates that this is only valid on
115
- // type:'icon'.
116
- color: ColorTokenSchema.optional(),
122
+ // type:'icon'. Nullable: explicit null clears it (undo of an icon recolor).
123
+ color: ColorTokenSchema.nullable().optional(),
117
124
  // type:'icon'-only: glyph stroke width. Lands at data.strokeWidth; the
118
- // post-merge reparse gates the [0.5, 4] bound and arm validity.
119
- strokeWidth: z.number().min(0.5).max(4).optional(),
125
+ // post-merge reparse gates the [0.5, 4] bound and arm validity. Nullable:
126
+ // explicit null clears it (undo back to the default stroke width).
127
+ strokeWidth: z.number().min(0.5).max(4).nullable().optional(),
120
128
  // type:'icon'/type:'image'-only: accessible alt text. Lands at data.alt.
121
129
  alt: z.string().optional(),
122
130
  // kebab-case Lucide icon name. Lands at data.icon. The post-merge reparse
@@ -302,10 +310,12 @@ export const mergeNodeUpdates = (node: Record<string, unknown>, updates: NodePat
302
310
  }
303
311
  continue;
304
312
  }
305
- // US-009: explicit null on icon is the clear signal (`.min(1)` rules out
306
- // the empty-string convention used for description / detail). Strip the
307
- // key from disk so a re-parsed demo doesn't reintroduce it.
308
- if (key === 'icon' && updates[key] === null) {
313
+ // Explicit null is the clear signal for every nullable key (style/visual
314
+ // tokens + US-009's `icon`): strip the key from disk so a re-parsed demo
315
+ // doesn't reintroduce it, and so an undo can restore a field to its
316
+ // pre-edit "unset" default. Only keys declared `.nullable()` in
317
+ // NodePatchBodySchema can reach here carrying null.
318
+ if (updates[key] === null) {
309
319
  if (key in data) {
310
320
  delete data[key];
311
321
  touchedData = true;
@@ -1239,7 +1249,15 @@ export async function createProjectImpl(
1239
1249
  body: CreateProjectBody,
1240
1250
  ): Promise<CreateProjectOutcome> {
1241
1251
  const { registry, watcher } = deps;
1242
- const { path: folderPath, name, description } = body;
1252
+ const { name, description } = body;
1253
+
1254
+ // Path is optional: when the caller omits it, scaffold under the studio's
1255
+ // home at <seeflowHome>/projects/<slug-of-name> so projects created from the
1256
+ // UI without a path land in a predictable, writable location.
1257
+ const folderPath =
1258
+ body.path && body.path.trim().length > 0
1259
+ ? body.path
1260
+ : join(seeflowHome(), 'projects', slugify(name));
1243
1261
 
1244
1262
  // Manifest-driven layout (US-018): a project is the seeflow.json manifest
1245
1263
  // plus one flow folder under flows/<id>/. The default flow id for a
@@ -1644,8 +1662,14 @@ export async function patchNodeImpl(
1644
1662
  field: string;
1645
1663
  content: string;
1646
1664
  kind: 'ref' | 'sidecar';
1665
+ value: unknown;
1647
1666
  }> = [];
1648
- for (const spec of externalizedFieldsForNodeType(node.type)) {
1667
+ // Externalize against the node's *target* type: a retype-into-component
1668
+ // carrying `spec` in the same patch is still geometric at this point, so
1669
+ // keying off `node.type` would miss `spec` (only externalized for
1670
+ // 'component'), leaving spec.json unwritten and data.spec unpopulated.
1671
+ const targetType = updates.type ?? node.type;
1672
+ for (const spec of externalizedFieldsForNodeType(targetType)) {
1649
1673
  const incoming = (updates as Record<string, unknown>)[spec.field];
1650
1674
  if (incoming === undefined) continue;
1651
1675
  const serializer = spec.serialize ?? defaultExternalizedSerializer;
@@ -1657,6 +1681,7 @@ export async function patchNodeImpl(
1657
1681
  field: spec.field,
1658
1682
  content,
1659
1683
  kind: spec.kind ?? 'ref',
1684
+ value: incoming,
1660
1685
  });
1661
1686
  }
1662
1687
  mergeNodeUpdates(node, updates);
@@ -1676,10 +1701,12 @@ export async function patchNodeImpl(
1676
1701
  };
1677
1702
  }
1678
1703
  // 'ref' fields swap data[field] for a file:// pointer; 'sidecar'
1679
- // fields (e.g. component spec) leave the in-memory value alone so the
1680
- // post-mutation parse still sees it — splitFlow drops it from flow.json
1681
- // on write and the resolver inlines it back from disk on read.
1682
- if (w.kind === 'ref') data[w.field] = w.ref;
1704
+ // fields (e.g. component spec) keep the incoming value in memory so the
1705
+ // post-mutation reparse + SSE broadcast see it — splitFlow drops it
1706
+ // from flow.json on write and the resolver inlines it back from disk on
1707
+ // read. Setting it here is required for a retype-into-component patch,
1708
+ // where there is no pre-inlined data.spec to fall back on.
1709
+ data[w.field] = w.kind === 'ref' ? w.ref : w.value;
1683
1710
  }
1684
1711
  node.data = data;
1685
1712
  }
@@ -5,6 +5,7 @@
5
5
  // module load — each call returns a fresh shallow copy so callers can't
6
6
  // mutate the cached payload.
7
7
 
8
+ import { componentCatalog } from './vendored-canvas-catalog.ts';
8
9
  import type { ZodTypeAny } from 'zod';
9
10
  import { zodToJsonSchema } from 'zod-to-json-schema';
10
11
  import {
@@ -51,13 +52,25 @@ export interface SchemaPayload {
51
52
  // further without round-tripping. `examples` are ready-to-paste jq paths;
52
53
  // `dataFields` lists the node-variant `data.<field>` keys (single-variant
53
54
  // lookups only — undefined for non-node categories or category-level
54
- // responses).
55
+ // responses). `rootPath` is the jq prefix that reaches the schema body at
56
+ // this response level (`.categories` on the index, `.schemas` on a category,
57
+ // `.schemas.<subname>` on a drill) — present in the plain (non-`--jq`)
58
+ // response the agent reads first, so it never has to guess the prefix.
55
59
  export interface JqHints {
56
60
  dataFields?: string[];
57
61
  examples: string[];
62
+ rootPath: string;
58
63
  tip?: string;
59
64
  }
60
65
 
66
+ // Appended to every `jqHints.tip`. The `--jq` filter runs against the schema
67
+ // object itself; the `{ result }` wrapper the CLI prints under `--jq` is
68
+ // presentational, so a filter must never be prefixed with `.result`.
69
+ const JQ_RESULT_TIP =
70
+ '`--jq` runs against this object directly; the `result` wrapper in `--jq` output is presentational — never prefix your filter with `.result`.';
71
+
72
+ const withResultTip = (hint: string): string => `${hint} ${JQ_RESULT_TIP}`;
73
+
61
74
  // Draft-07 pin matches the widest tool support; the same target string is
62
75
  // used by the MCP `tools/list` JSON Schemas (default in zod-to-json-schema)
63
76
  // so consumers see one consistent dialect across the whole surface.
@@ -101,6 +114,11 @@ const CATEGORY_META: Array<Omit<SchemaCategory, 'subnames'>> = [
101
114
  description:
102
115
  "Sidecar shape written to <project>/nodes/<id>/spec.json for type:'component' nodes. Carries the json-render element tree, initial state, and named actions the renderer dispatches on user input.",
103
116
  },
117
+ {
118
+ name: 'componentCatalog',
119
+ description:
120
+ 'The legal values for componentSpec.elements[].type and the props each accepts. Drill: seeflow schema componentCatalog <Name>.',
121
+ },
104
122
  { name: 'style', description: 'style.json (studio-owned).' },
105
123
  ];
106
124
 
@@ -131,6 +149,7 @@ const PAYLOADS: Record<string, SchemaPayload> = {
131
149
  "type:'image' data.path must start with 'nodes/<id>/'.",
132
150
  "scriptPath in playAction/statusAction is relative to nodes/<nodeId>/ and may not contain '..' or absolute paths.",
133
151
  "type:'component' nodes have no `spec` field on disk — the spec lives in <project>/nodes/<id>/spec.json (see `seeflow schema componentSpec`). The resolver inlines it into data.spec for runtime / SSE broadcasts.",
152
+ 'The legal `elements[].type` values and their props are listed under `seeflow schema componentCatalog`.',
134
153
  "stateSource SHOULD be set on every node that has a statusAction — kind:'request' for poll-based (REST, healthcheck, DB query), kind:'event' for push-based (SSE, webhook, queue, message bus).",
135
154
  'stateSource may also be set without a statusAction on representational/architecture diagrams to signal data-flow intent (poll vs push) without wiring a runtime probe.',
136
155
  ],
@@ -162,6 +181,22 @@ const PAYLOADS: Record<string, SchemaPayload> = {
162
181
  "spec.json is the on-disk source of truth for type:'component' nodes; the resolver inlines it into data.spec at read time and splitFlow strips it back out before writing flow.json so the sidecar is never double-stored.",
163
182
  'elements is keyed by element id; `root` names the entry element. Element ids referenced from children / actions must exist in elements.',
164
183
  'state and actions are both keyed by user-chosen names. Action handles in the rendered UI reference these names; see `seeflow schema action` for the per-action shape.',
184
+ 'The legal `elements[].type` values and their props are listed under `seeflow schema componentCatalog`.',
185
+ ],
186
+ },
187
+ componentCatalog: {
188
+ // One subname per catalog entry; schema body = the props object that
189
+ // element type accepts. Built dynamically so adding a catalog component
190
+ // surfaces here automatically (the canvas catalog is the single source).
191
+ schemas: Object.fromEntries(
192
+ Object.entries(componentCatalog.components).map(([name, def]) => [
193
+ name,
194
+ toJsonSchema(def.props),
195
+ ]),
196
+ ),
197
+ notes: [
198
+ "Each key is a legal componentSpec.elements[].type; its schema is the props object that element type accepts. Set them on the element's `props` field.",
199
+ 'Any prop value may instead be a { $state } / { $action } / { $cond,$then,$else } ref resolved by the json-render runtime at render time — the per-prop schema shows the concrete (non-ref) shape.',
165
200
  ],
166
201
  },
167
202
  style: {
@@ -259,12 +294,19 @@ export function buildJqHints(category: string, subname?: string): JqHints | null
259
294
  return {
260
295
  ...(dataFields ? { dataFields } : {}),
261
296
  examples,
262
- tip: hint,
297
+ rootPath: `.schemas.${subname}`,
298
+ tip: withResultTip(hint),
263
299
  };
264
300
  }
265
301
  const subs = Object.keys(payload.schemas);
266
302
  const sample = subs[0];
267
- if (!sample) return { examples: ['.schemas', '.notes', '.notes[]'] };
303
+ if (!sample) {
304
+ return {
305
+ examples: ['.schemas', '.notes', '.notes[]'],
306
+ rootPath: '.schemas',
307
+ tip: JQ_RESULT_TIP,
308
+ };
309
+ }
268
310
  const examples = [
269
311
  '.schemas',
270
312
  `.schemas.${sample}`,
@@ -276,9 +318,25 @@ export function buildJqHints(category: string, subname?: string): JqHints | null
276
318
  ];
277
319
  return {
278
320
  examples,
279
- tip:
321
+ rootPath: '.schemas',
322
+ tip: withResultTip(
280
323
  subs.length > 1
281
324
  ? `Pass \`seeflow schema ${category} <subname>\` (one of: ${subs.join(', ')}) to drop the other ${subs.length - 1} variant(s) from the payload before \`--jq\`-ing.`
282
325
  : `Single-variant category — \`--jq\` paths drill straight into \`.schemas.${sample}\`.`,
326
+ ),
327
+ };
328
+ }
329
+
330
+ // jqHints for the schema index (`seeflow schema` with no category). The index
331
+ // payload is `{ categories, usage }`, so `--jq` filters root at `.categories`.
332
+ // Surfaced so the index carries the same `rootPath` affordance as every drill
333
+ // level — the agent never has to guess the prefix.
334
+ export function buildIndexJqHints(): JqHints {
335
+ return {
336
+ examples: ['.categories', '.categories[].name', '.usage', '.usage.examples'],
337
+ rootPath: '.categories',
338
+ tip: withResultTip(
339
+ 'Each entry under `.categories` names a drill target — pass its `name` as `<category>`.',
340
+ ),
283
341
  };
284
342
  }
package/src/schema.ts CHANGED
@@ -218,7 +218,12 @@ export const NodeTypeSchema = z.enum([
218
218
  // superRefine wired in a later story.
219
219
 
220
220
  export const ComponentSpecElementSchema = z.object({
221
- type: z.string().min(1),
221
+ type: z
222
+ .string()
223
+ .min(1)
224
+ .describe(
225
+ 'Component name from the catalog — see `seeflow schema componentCatalog` for the legal values and the props each type accepts.',
226
+ ),
222
227
  props: z.record(z.string(), z.unknown()).optional(),
223
228
  children: z.array(z.string()).optional(),
224
229
  watch: z.record(z.string(), z.unknown()).optional(),
package/src/server.ts CHANGED
@@ -227,7 +227,12 @@ export function serve(options: ServeOptions = {}) {
227
227
  const hostname = options.hostname ?? '0.0.0.0';
228
228
  mkdirSync(seeflowHome(), { recursive: true });
229
229
  const app = createApp(options);
230
- return Bun.serve({ port, hostname, fetch: app.fetch });
230
+ // Bun's default per-connection idle timeout (~10s) reaps long-lived SSE
231
+ // streams between heartbeats, forcing the browser's EventSource to reconnect
232
+ // (each reconnect re-fires `hello` → a client refetch). Raise it well above
233
+ // the SSE heartbeat cadence so the keep-alive lands first and the stream
234
+ // stays warm. Max accepted by Bun is 255s.
235
+ return Bun.serve({ port, hostname, idleTimeout: 120, fetch: app.fetch });
231
236
  }
232
237
 
233
238
  if (import.meta.main) {
@@ -1 +0,0 @@
1
- import{U as a,C as n}from"./mermaid.core-DjNa-8Hv.js";const t=(r,o)=>a.lang.round(n.parse(r)[o]);export{t as c};
@@ -1 +0,0 @@
1
- import{s as a,a as s,c as e,C as t}from"./chunk-727SXJPM-CwKBkYmr.js";import{a as i}from"./mermaid.core-DjNa-8Hv.js";import"./index-5X7OVal6.js";import"./chunk-FMBD7UC4-DCWBhj6w.js";import"./chunk-ND2GUHAM-vLte473x.js";import"./chunk-55IACEB6-DZ5Ond8O.js";import"./chunk-2J33WTMH-BLQaRMqq.js";import"./purify.es-CLGrRn1w.js";import"./step-CWvwoXpJ.js";var b={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{b as diagram};
@@ -1 +0,0 @@
1
- import{s as a,a as s,c as e,C as t}from"./chunk-727SXJPM-CwKBkYmr.js";import{a as i}from"./mermaid.core-DjNa-8Hv.js";import"./index-5X7OVal6.js";import"./chunk-FMBD7UC4-DCWBhj6w.js";import"./chunk-ND2GUHAM-vLte473x.js";import"./chunk-55IACEB6-DZ5Ond8O.js";import"./chunk-2J33WTMH-BLQaRMqq.js";import"./purify.es-CLGrRn1w.js";import"./step-CWvwoXpJ.js";var b={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{b as diagram};