@tuongaz/seeflow 0.1.105 → 0.1.107

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 (63) hide show
  1. package/dist/web/assets/{architectureDiagram-3BPJPVTR-Dn9FQt2V.js → architectureDiagram-3BPJPVTR-C4n2LFo2.js} +1 -1
  2. package/dist/web/assets/{blockDiagram-GPEHLZMM-Difk1XOK.js → blockDiagram-GPEHLZMM-DmYJl-4W.js} +1 -1
  3. package/dist/web/assets/{c4Diagram-AAUBKEIU-2-WiMvCN.js → c4Diagram-AAUBKEIU-BRtrAah9.js} +1 -1
  4. package/dist/web/assets/channel-Cwz40yCU.js +1 -0
  5. package/dist/web/assets/{chart-D2hW0kXw.js → chart-BJADfkvF.js} +1 -1
  6. package/dist/web/assets/{chunk-2J33WTMH-BPHmI1De.js → chunk-2J33WTMH-Cwse2MLm.js} +1 -1
  7. package/dist/web/assets/{chunk-4BX2VUAB-Ay38fe5g.js → chunk-4BX2VUAB-C7yQ1hNl.js} +1 -1
  8. package/dist/web/assets/{chunk-55IACEB6-5ygNV__l.js → chunk-55IACEB6-C4JH4uPC.js} +1 -1
  9. package/dist/web/assets/{chunk-727SXJPM-kQNWdsxV.js → chunk-727SXJPM-CH7_QMCX.js} +1 -1
  10. package/dist/web/assets/{chunk-AQP2D5EJ-DzeeRd3w.js → chunk-AQP2D5EJ-JANvO0oT.js} +1 -1
  11. package/dist/web/assets/{chunk-FMBD7UC4-CUar6TO-.js → chunk-FMBD7UC4-tMpbfhjf.js} +1 -1
  12. package/dist/web/assets/{chunk-ND2GUHAM-CNZ3763c.js → chunk-ND2GUHAM-D9eBypg9.js} +1 -1
  13. package/dist/web/assets/{chunk-QZHKN3VN-FKGOAesZ.js → chunk-QZHKN3VN-geJeqOM8.js} +1 -1
  14. package/dist/web/assets/classDiagram-4FO5ZUOK-DQJOq1dg.js +1 -0
  15. package/dist/web/assets/classDiagram-v2-Q7XG4LA2-DQJOq1dg.js +1 -0
  16. package/dist/web/assets/{code-block-SZDhhr7T.js → code-block-DNQhzmPc.js} +1 -1
  17. package/dist/web/assets/{cose-bilkent-S5V4N54A--DNtr1GO.js → cose-bilkent-S5V4N54A-BddhusFd.js} +1 -1
  18. package/dist/web/assets/{dagre-BM42HDAG-cIlGgsEQ.js → dagre-BM42HDAG-DgzvId-E.js} +1 -1
  19. package/dist/web/assets/{diagram-2AECGRRQ-DjoBzBLH.js → diagram-2AECGRRQ-MDnqZTUO.js} +1 -1
  20. package/dist/web/assets/{diagram-5GNKFQAL-mmesu8tu.js → diagram-5GNKFQAL-CNp33BbA.js} +1 -1
  21. package/dist/web/assets/{diagram-KO2AKTUF-DeKWe4-a.js → diagram-KO2AKTUF-KiwbE7kH.js} +1 -1
  22. package/dist/web/assets/{diagram-LMA3HP47-NHxUC_r2.js → diagram-LMA3HP47-CbVgSz8_.js} +1 -1
  23. package/dist/web/assets/{diagram-OG6HWLK6-DoVcjbAs.js → diagram-OG6HWLK6-CbuPIx97.js} +1 -1
  24. package/dist/web/assets/{erDiagram-TEJ5UH35-DOr0sCAJ.js → erDiagram-TEJ5UH35-Cb0DMX_h.js} +1 -1
  25. package/dist/web/assets/{flowDiagram-I6XJVG4X-Blcjaj5z.js → flowDiagram-I6XJVG4X-BXWZO2JG.js} +1 -1
  26. package/dist/web/assets/{ganttDiagram-6RSMTGT7-XOZAg1O8.js → ganttDiagram-6RSMTGT7-BLNBpY-f.js} +1 -1
  27. package/dist/web/assets/{gitGraphDiagram-PVQCEYII-CwWAqRfA.js → gitGraphDiagram-PVQCEYII-CezBV45Y.js} +1 -1
  28. package/dist/web/assets/{iconify-pbuBzLPb.js → iconify-Dq4nFnfE.js} +1 -1
  29. package/dist/web/assets/{index-COPB-kTN.js → index-DOA-g04P.js} +276 -276
  30. package/dist/web/assets/{index-BfhCAb_k.css → index-TDj3HTQM.css} +1 -1
  31. package/dist/web/assets/{index.es-BnIUUbgb.js → index.es-DcaYIQmY.js} +1 -1
  32. package/dist/web/assets/{infoDiagram-5YYISTIA-BDrdYBmZ.js → infoDiagram-5YYISTIA-CCWYa3Vp.js} +1 -1
  33. package/dist/web/assets/{ishikawaDiagram-YF4QCWOH-Ccq3BiV9.js → ishikawaDiagram-YF4QCWOH-CcOMRswO.js} +1 -1
  34. package/dist/web/assets/{journeyDiagram-JHISSGLW-Byhhmys_.js → journeyDiagram-JHISSGLW-CGh3OzMJ.js} +1 -1
  35. package/dist/web/assets/{jspdf.es.min-DRscdcGG.js → jspdf.es.min-BND60C1X.js} +3 -3
  36. package/dist/web/assets/{kanban-definition-UN3LZRKU-C4V7XsI8.js → kanban-definition-UN3LZRKU-wc19qf9j.js} +1 -1
  37. package/dist/web/assets/{linear-9s7Nhsku.js → linear-DI6gQRSO.js} +1 -1
  38. package/dist/web/assets/{markdown--co0piIh.js → markdown-CZ5OQFXP.js} +1 -1
  39. package/dist/web/assets/{mermaid.core-BzxthQo1.js → mermaid.core-CAZGzK0l.js} +4 -4
  40. package/dist/web/assets/{mindmap-definition-RKZ34NQL-DPEOdrP-.js → mindmap-definition-RKZ34NQL-DEN6ul-4.js} +1 -1
  41. package/dist/web/assets/{pieDiagram-4H26LBE5-JUVRBM8g.js → pieDiagram-4H26LBE5-BzK5WQ02.js} +1 -1
  42. package/dist/web/assets/{quadrantDiagram-W4KKPZXB-B-O3T7Ql.js → quadrantDiagram-W4KKPZXB-WpYq-pQF.js} +1 -1
  43. package/dist/web/assets/{requirementDiagram-4Y6WPE33-cMHqY0_G.js → requirementDiagram-4Y6WPE33-Ca5ORxLp.js} +1 -1
  44. package/dist/web/assets/{sankeyDiagram-5OEKKPKP-CO0aG09B.js → sankeyDiagram-5OEKKPKP-DBWaB1Is.js} +1 -1
  45. package/dist/web/assets/{sequenceDiagram-3UESZ5HK-DkvyDkYb.js → sequenceDiagram-3UESZ5HK-BGEYL2Uw.js} +1 -1
  46. package/dist/web/assets/{stateDiagram-AJRCARHV-DKseQ2AJ.js → stateDiagram-AJRCARHV-Bt3xrb-F.js} +1 -1
  47. package/dist/web/assets/stateDiagram-v2-BHNVJYJU-DyUVzYNm.js +1 -0
  48. package/dist/web/assets/{time-E94H-3Js.js → time-DRdnjU-r.js} +1 -1
  49. package/dist/web/assets/{timeline-definition-PNZ67QCA-B9uWXMTl.js → timeline-definition-PNZ67QCA-D3Avg3RH.js} +1 -1
  50. package/dist/web/assets/{vennDiagram-CIIHVFJN-DuHHMFEd.js → vennDiagram-CIIHVFJN-DK0Y12Yp.js} +1 -1
  51. package/dist/web/assets/{wardley-L42UT6IY-C_uCcesD.js → wardley-L42UT6IY-Th41Nnnt.js} +1 -1
  52. package/dist/web/assets/{wardleyDiagram-YWT4CUSO-DbA4lTWI.js → wardleyDiagram-YWT4CUSO-DYq6kX6q.js} +1 -1
  53. package/dist/web/assets/{xychartDiagram-2RQKCTM6-BX4GX1ip.js → xychartDiagram-2RQKCTM6-_nwsSoOi.js} +1 -1
  54. package/dist/web/index.html +2 -2
  55. package/package.json +1 -1
  56. package/src/api.ts +53 -3
  57. package/src/server.ts +1 -0
  58. package/src/share-envelope.ts +3 -0
  59. package/src/share.ts +111 -9
  60. package/dist/web/assets/channel-CrVPaFj0.js +0 -1
  61. package/dist/web/assets/classDiagram-4FO5ZUOK-yNcOh78q.js +0 -1
  62. package/dist/web/assets/classDiagram-v2-Q7XG4LA2-yNcOh78q.js +0 -1
  63. package/dist/web/assets/stateDiagram-v2-BHNVJYJU-C30cJIVE.js +0 -1
package/src/share.ts CHANGED
@@ -81,13 +81,31 @@ import { type SseTap, createSseTap } from './share/sse-tap.ts';
81
81
  // re-validate join/leave with their strict required fields so malformed
82
82
  // join/leave frames are dropped rather than treated as sideband. Cursor,
83
83
  // viewport, and any other future `kind` no-op in v1 per the design doc.
84
- const PresenceBaseSchema = z.object({ kind: z.string() }).passthrough();
85
- const PresenceJoinSchema = z.object({
86
- kind: z.literal('join'),
87
- peerId: z.string(),
88
- displayName: z.string(),
89
- });
90
- const PresenceLeaveSchema = z.object({ kind: z.literal('leave'), peerId: z.string() });
84
+ // Presence payload shapes accepted on the wire. The cloud relay uses
85
+ // `state: 'joined' | 'left' | 'active' | 'idle' | 'host-offline'`; older host
86
+ // builds emitted `kind: 'join' | 'leave'`. We accept either to stay
87
+ // forward/backward compatible.
88
+ const PresenceBaseSchema = z
89
+ .object({
90
+ kind: z.string().optional(),
91
+ state: z.string().optional(),
92
+ })
93
+ .passthrough();
94
+ const PresenceJoinSchema = z
95
+ .object({
96
+ peerId: z.string(),
97
+ displayName: z.string(),
98
+ kind: z.literal('join').optional(),
99
+ state: z.literal('joined').optional(),
100
+ })
101
+ .passthrough();
102
+ const PresenceLeaveSchema = z
103
+ .object({
104
+ peerId: z.string(),
105
+ kind: z.literal('leave').optional(),
106
+ state: z.literal('left').optional(),
107
+ })
108
+ .passthrough();
91
109
 
92
110
  export interface PeerSummary {
93
111
  peerId: string;
@@ -800,15 +818,43 @@ export function createShareController(deps: ShareDeps): ShareController {
800
818
  });
801
819
  };
802
820
 
821
+ // The studio API receives a flat node-patch body (`name`, `description`,
822
+ // color tokens, `width` / `height`, …) which `mergeNodeUpdates` dispatches
823
+ // into `node.data.*`. The peer SPA applies the wire diff as a shallow merge
824
+ // against the node root, so a flat patch would land at the root and leave
825
+ // `data.*` stale — the renderer reads `data.name` and would silently keep
826
+ // the old label. Normalize the wire shape by reading the post-mutation node
827
+ // from the watcher's snapshot and forwarding the full merged `data` (plus
828
+ // `position` / `type` if they moved). Keeps the peer's shallow-merge
829
+ // semantics correct without changing the wire schema.
830
+ const NODE_ROOT_PATCH_KEYS = new Set(['position', 'type']);
831
+ const canonicalizePatchNode = (op: RpcOp): RpcOp => {
832
+ if (op.op !== 'patchNode') return op;
833
+ const watcher = deps.operationsDeps?.watcher;
834
+ const snap = watcher?.snapshot(op.flowId);
835
+ const node = snap?.flow?.nodes.find((n) => n.id === op.nodeId);
836
+ if (!node) return op;
837
+ const newPatch: Record<string, unknown> = {};
838
+ for (const key of Object.keys(op.patch)) {
839
+ if (NODE_ROOT_PATCH_KEYS.has(key)) {
840
+ newPatch[key] = (node as unknown as Record<string, unknown>)[key];
841
+ } else {
842
+ newPatch.data = node.data;
843
+ }
844
+ }
845
+ return { ...op, patch: newPatch };
846
+ };
847
+
803
848
  // Build + emit a `node-patched` envelope for an accepted op. Centralized so
804
849
  // peer-rpc and host-local edits assemble the wire payload identically (only
805
850
  // the `attributedTo` value differs). Returns the version assigned so callers
806
851
  // can echo it back into rpc-result if needed.
807
852
  const broadcastNodePatched = (
808
- op: RpcOp,
853
+ rawOp: RpcOp,
809
854
  outcome: RpcDispatchOutcome,
810
855
  attributedTo: { peerId: string; displayName: string },
811
856
  ): number => {
857
+ const op = canonicalizePatchNode(rawOp);
812
858
  const nextVersion = (flowVersions.get(op.flowId) ?? 0) + 1;
813
859
  flowVersions.set(op.flowId, nextVersion);
814
860
  const diff = computeNodePatchedDiff(op, outcome);
@@ -986,6 +1032,50 @@ export function createShareController(deps: ShareDeps): ShareController {
986
1032
  }
987
1033
  };
988
1034
 
1035
+ // Emit a `flow-snapshot` envelope to a freshly-joined peer so its canvas can
1036
+ // render the host's flow graph. Prefers the watcher's resolved snapshot
1037
+ // (file refs inlined, style merged, schema-validated) so component / icon
1038
+ // / image nodes render without the peer needing to walk the host's
1039
+ // filesystem. Falls back to raw on-disk JSON for setups that boot without
1040
+ // a watcher (CLI smoke tests, integration). Without this the peer is stuck
1041
+ // on "CONNECTING…" forever (viewer's share-session.tsx blocks until
1042
+ // `snapshot` is populated). Errors only warn — the peer can refresh /
1043
+ // rotate to retry.
1044
+ const emitFlowSnapshotForPeer = (peerConnId: string): void => {
1045
+ const registry = deps.operationsDeps?.registry;
1046
+ const watcher = deps.operationsDeps?.watcher;
1047
+ if (!registry) return;
1048
+ const entries = registry.list();
1049
+ if (entries.length === 0) return;
1050
+ const flows: Record<string, unknown> = {};
1051
+ let activeFlowId: string | null = null;
1052
+ for (const entry of entries) {
1053
+ let flow: unknown = null;
1054
+ const snap = watcher?.snapshot(entry.id) ?? watcher?.reparse(entry.id) ?? null;
1055
+ if (snap?.valid && snap.flow) {
1056
+ flow = snap.flow;
1057
+ } else {
1058
+ try {
1059
+ flow = JSON.parse(readFileSync(join(entry.repoPath, entry.flowPath), 'utf8'));
1060
+ } catch (err) {
1061
+ console.warn(`[share] flow-snapshot read failed for ${entry.id}:`, err);
1062
+ continue;
1063
+ }
1064
+ }
1065
+ flows[entry.id] = flow;
1066
+ if (activeFlowId === null) activeFlowId = entry.id;
1067
+ if (entry.isDefault) activeFlowId = entry.id;
1068
+ }
1069
+ if (!activeFlowId) return;
1070
+ try {
1071
+ broadcast(
1072
+ makeEnvelope('flow-snapshot', { flows, activeFlowId }, { to: peerConnId, from: 'host' }),
1073
+ );
1074
+ } catch (err) {
1075
+ console.warn('[share] flow-snapshot broadcast failed:', err);
1076
+ }
1077
+ };
1078
+
989
1079
  const handleFrame = (env: Envelope) => {
990
1080
  if (env.type === 'presence') {
991
1081
  const base = PresenceBaseSchema.safeParse(env.payload);
@@ -993,7 +1083,15 @@ export function createShareController(deps: ShareDeps): ShareController {
993
1083
  console.warn('[share] dropped presence frame: invalid payload');
994
1084
  return;
995
1085
  }
996
- const kind = base.data.kind;
1086
+ // Relay uses `state: 'joined'/'left'/...`; older host builds emitted
1087
+ // `kind: 'join'/'leave'`. Map either onto our internal verbs.
1088
+ const kind =
1089
+ base.data.kind ??
1090
+ (base.data.state === 'joined'
1091
+ ? 'join'
1092
+ : base.data.state === 'left'
1093
+ ? 'leave'
1094
+ : base.data.state);
997
1095
  if (kind === 'join') {
998
1096
  const parsed = PresenceJoinSchema.safeParse(env.payload);
999
1097
  if (!parsed.success) {
@@ -1020,6 +1118,10 @@ export function createShareController(deps: ShareDeps): ShareController {
1020
1118
  ...current,
1021
1119
  peers: [...current.peers, { peerId, displayName, joinedAt: Date.now() }],
1022
1120
  });
1121
+ // Prime the new peer with the host's flow graph first — without this
1122
+ // the peer is stuck on "CONNECTING…" forever (viewer waits on
1123
+ // snapshot before rendering the canvas).
1124
+ emitFlowSnapshotForPeer(env.from);
1023
1125
  // Prime the new peer with a one-shot files-manifest snapshot so its
1024
1126
  // canvas can render placeholder sizing before any file-request fires.
1025
1127
  // Fire-and-forget; emit failures are warned, never propagated.
@@ -1 +0,0 @@
1
- import{U as a,C as n}from"./mermaid.core-BzxthQo1.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-kQNWdsxV.js";import{a as i}from"./mermaid.core-BzxthQo1.js";import"./index-COPB-kTN.js";import"./chunk-FMBD7UC4-CUar6TO-.js";import"./chunk-ND2GUHAM-CNZ3763c.js";import"./chunk-55IACEB6-5ygNV__l.js";import"./chunk-2J33WTMH-BPHmI1De.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-kQNWdsxV.js";import{a as i}from"./mermaid.core-BzxthQo1.js";import"./index-COPB-kTN.js";import"./chunk-FMBD7UC4-CUar6TO-.js";import"./chunk-ND2GUHAM-CNZ3763c.js";import"./chunk-55IACEB6-5ygNV__l.js";import"./chunk-2J33WTMH-BPHmI1De.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{b as r,a as e,s as a,S as s}from"./chunk-AQP2D5EJ-DzeeRd3w.js";import{a as i}from"./mermaid.core-BzxthQo1.js";import"./index-COPB-kTN.js";import"./chunk-55IACEB6-5ygNV__l.js";import"./chunk-2J33WTMH-BPHmI1De.js";import"./purify.es-CLGrRn1w.js";import"./step-CWvwoXpJ.js";var n={parser:a,get db(){return new s(2)},renderer:e,styles:r,init:i(t=>{t.state||(t.state={}),t.state.arrowMarkerAbsolute=t.arrowMarkerAbsolute},"init")};export{n as diagram};