studiograph 1.3.3-next.9 → 1.3.4
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/dist/agent/orchestrator.d.ts +2 -0
- package/dist/agent/orchestrator.js +13 -5
- package/dist/agent/orchestrator.js.map +1 -1
- package/dist/agent/skills/sync-configuration.md +4 -29
- package/dist/agent/skills/sync-setup.md +2 -4
- package/dist/agent/tools/ops-tools.js +15 -126
- package/dist/agent/tools/ops-tools.js.map +1 -1
- package/dist/agent/tools/sync-tools.d.ts +7 -6
- package/dist/agent/tools/sync-tools.js +205 -178
- package/dist/agent/tools/sync-tools.js.map +1 -1
- package/dist/cli/commands/about.d.ts +13 -0
- package/dist/cli/commands/about.js +97 -0
- package/dist/cli/commands/about.js.map +1 -0
- package/dist/cli/commands/clone.d.ts +5 -2
- package/dist/cli/commands/clone.js +131 -62
- package/dist/cli/commands/clone.js.map +1 -1
- package/dist/cli/commands/connector.d.ts +2 -16
- package/dist/cli/commands/connector.js +32 -109
- package/dist/cli/commands/connector.js.map +1 -1
- package/dist/cli/commands/deploy.d.ts +0 -1
- package/dist/cli/commands/deploy.js +13 -103
- package/dist/cli/commands/deploy.js.map +1 -1
- package/dist/cli/commands/init.js +6 -93
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/join.js +51 -1
- package/dist/cli/commands/join.js.map +1 -1
- package/dist/cli/commands/redeploy.js +1 -2
- package/dist/cli/commands/redeploy.js.map +1 -1
- package/dist/cli/commands/serve.d.ts +1 -3
- package/dist/cli/commands/serve.js +29 -109
- package/dist/cli/commands/serve.js.map +1 -1
- package/dist/cli/commands/start.js +1 -1
- package/dist/cli/commands/start.js.map +1 -1
- package/dist/cli/commands/sync-collection.d.ts +14 -0
- package/dist/cli/commands/sync-collection.js +366 -0
- package/dist/cli/commands/sync-collection.js.map +1 -0
- package/dist/cli/commands/sync.js +518 -82
- package/dist/cli/commands/sync.js.map +1 -1
- package/dist/cli/index.js +12 -30
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/setup-wizard.d.ts +0 -13
- package/dist/cli/setup-wizard.js +6 -81
- package/dist/cli/setup-wizard.js.map +1 -1
- package/dist/core/types.d.ts +140 -21
- package/dist/core/types.js +15 -4
- package/dist/core/types.js.map +1 -1
- package/dist/core/workspace.d.ts +11 -4
- package/dist/core/workspace.js +61 -26
- package/dist/core/workspace.js.map +1 -1
- package/dist/integrations/asana.d.ts +26 -0
- package/dist/integrations/asana.js +77 -0
- package/dist/integrations/asana.js.map +1 -0
- package/dist/integrations/figma-local.d.ts +16 -0
- package/dist/integrations/figma-local.js +10 -0
- package/dist/integrations/figma-local.js.map +1 -0
- package/dist/integrations/figma.d.ts +17 -0
- package/dist/integrations/figma.js +16 -0
- package/dist/integrations/figma.js.map +1 -0
- package/dist/integrations/granola.d.ts +24 -0
- package/dist/integrations/granola.js +59 -0
- package/dist/integrations/granola.js.map +1 -0
- package/dist/integrations/linear.d.ts +14 -0
- package/dist/integrations/linear.js +70 -0
- package/dist/integrations/linear.js.map +1 -0
- package/dist/integrations/paper-local.d.ts +14 -0
- package/dist/integrations/paper-local.js +10 -0
- package/dist/integrations/paper-local.js.map +1 -0
- package/dist/integrations/paper.d.ts +2 -0
- package/dist/integrations/paper.js +10 -0
- package/dist/integrations/paper.js.map +1 -0
- package/dist/integrations/pipedrive.d.ts +26 -0
- package/dist/integrations/pipedrive.js +97 -0
- package/dist/integrations/pipedrive.js.map +1 -0
- package/dist/integrations/registry.d.ts +15 -0
- package/dist/integrations/registry.js +27 -0
- package/dist/integrations/registry.js.map +1 -0
- package/dist/integrations/types.d.ts +34 -0
- package/dist/integrations/types.js +9 -0
- package/dist/integrations/types.js.map +1 -0
- package/dist/mcp/connector-manager.d.ts +45 -31
- package/dist/mcp/connector-manager.js +164 -116
- package/dist/mcp/connector-manager.js.map +1 -1
- package/dist/mcp/server-oauth-provider.d.ts +56 -0
- package/dist/mcp/server-oauth-provider.js +138 -0
- package/dist/mcp/server-oauth-provider.js.map +1 -0
- package/dist/server/chrome/chrome.css +142 -28
- package/dist/server/chrome/chrome.js +51 -25
- package/dist/server/collab-authority.d.ts +70 -0
- package/dist/server/collab-authority.js +218 -0
- package/dist/server/collab-authority.js.map +1 -0
- package/dist/server/collab.d.ts +29 -0
- package/dist/server/collab.js +195 -0
- package/dist/server/collab.js.map +1 -0
- package/dist/server/commit-scheduler.d.ts +1 -1
- package/dist/server/commit-scheduler.js +18 -4
- package/dist/server/commit-scheduler.js.map +1 -1
- package/dist/server/index.d.ts +0 -2
- package/dist/server/index.js +89 -18
- package/dist/server/index.js.map +1 -1
- package/dist/server/routes/auth-api.d.ts +6 -0
- package/dist/server/routes/auth-api.js +55 -0
- package/dist/server/routes/auth-api.js.map +1 -1
- package/dist/server/routes/collab.d.ts +6 -0
- package/dist/server/routes/collab.js +10 -0
- package/dist/server/routes/collab.js.map +1 -0
- package/dist/server/routes/git-http.d.ts +1 -1
- package/dist/server/routes/git-http.js +53 -15
- package/dist/server/routes/git-http.js.map +1 -1
- package/dist/server/routes/graph-api.d.ts +2 -2
- package/dist/server/routes/graph-api.js +59 -55
- package/dist/server/routes/graph-api.js.map +1 -1
- package/dist/server/routes/mcp.d.ts +12 -0
- package/dist/server/routes/mcp.js +35 -0
- package/dist/server/routes/mcp.js.map +1 -0
- package/dist/server/routes/permissions-api.d.ts +2 -1
- package/dist/server/routes/permissions-api.js +16 -2
- package/dist/server/routes/permissions-api.js.map +1 -1
- package/dist/server/routes/sync-api.d.ts +26 -0
- package/dist/server/routes/sync-api.js +757 -0
- package/dist/server/routes/sync-api.js.map +1 -0
- package/dist/server/routes/ws.d.ts +4 -2
- package/dist/server/routes/ws.js +100 -4
- package/dist/server/routes/ws.js.map +1 -1
- package/dist/server/session-manager.d.ts +40 -0
- package/dist/server/session-manager.js +132 -0
- package/dist/server/session-manager.js.map +1 -0
- package/dist/server/ws-hub.d.ts +95 -1
- package/dist/server/ws-hub.js +192 -5
- package/dist/server/ws-hub.js.map +1 -1
- package/dist/server/yjs-manager.d.ts +59 -0
- package/dist/server/yjs-manager.js +194 -0
- package/dist/server/yjs-manager.js.map +1 -0
- package/dist/services/auth-service.d.ts +30 -2
- package/dist/services/auth-service.js +116 -11
- package/dist/services/auth-service.js.map +1 -1
- package/dist/services/git.d.ts +6 -0
- package/dist/services/git.js +32 -2
- package/dist/services/git.js.map +1 -1
- package/dist/services/sync/collection-sync.d.ts +73 -0
- package/dist/services/sync/collection-sync.js +726 -0
- package/dist/services/sync/collection-sync.js.map +1 -0
- package/dist/services/sync/commit.js +5 -20
- package/dist/services/sync/commit.js.map +1 -1
- package/dist/services/sync/data-fetcher.d.ts +31 -0
- package/dist/services/sync/data-fetcher.js +12 -0
- package/dist/services/sync/data-fetcher.js.map +1 -0
- package/dist/services/sync/entity-refresh.d.ts +30 -0
- package/dist/services/sync/entity-refresh.js +275 -0
- package/dist/services/sync/entity-refresh.js.map +1 -0
- package/dist/services/sync/frontmatter-extractor.d.ts +2 -2
- package/dist/services/sync/frontmatter-extractor.js +1 -2
- package/dist/services/sync/frontmatter-extractor.js.map +1 -1
- package/dist/services/sync/graph-match.js +1 -1
- package/dist/services/sync/graph-match.js.map +1 -1
- package/dist/services/sync/mcp-client.d.ts +16 -4
- package/dist/services/sync/mcp-client.js +34 -20
- package/dist/services/sync/mcp-client.js.map +1 -1
- package/dist/services/sync/prompts.js +1 -1
- package/dist/services/sync/reconciler.js +1 -2
- package/dist/services/sync/reconciler.js.map +1 -1
- package/dist/services/sync/rest-client.d.ts +40 -0
- package/dist/services/sync/rest-client.js +100 -0
- package/dist/services/sync/rest-client.js.map +1 -0
- package/dist/services/sync/source-config.d.ts +23 -1
- package/dist/services/sync/source-config.js +112 -16
- package/dist/services/sync/source-config.js.map +1 -1
- package/dist/services/sync/source-definitions/asana.d.ts +3 -4
- package/dist/services/sync/source-definitions/asana.js +7 -13
- package/dist/services/sync/source-definitions/asana.js.map +1 -1
- package/dist/services/sync/source-definitions/definitions.d.ts +5 -18
- package/dist/services/sync/source-definitions/definitions.js +4 -22
- package/dist/services/sync/source-definitions/definitions.js.map +1 -1
- package/dist/services/sync/source-definitions/granola.d.ts +1 -1
- package/dist/services/sync/source-definitions/granola.js +11 -18
- package/dist/services/sync/source-definitions/granola.js.map +1 -1
- package/dist/services/sync/source-definitions/linear.d.ts +1 -1
- package/dist/services/sync/source-definitions/linear.js +17 -15
- package/dist/services/sync/source-definitions/linear.js.map +1 -1
- package/dist/services/sync/source-definitions/pipedrive.d.ts +1 -1
- package/dist/services/sync/source-definitions/pipedrive.js +6 -15
- package/dist/services/sync/source-definitions/pipedrive.js.map +1 -1
- package/dist/services/sync/staging.js +1 -2
- package/dist/services/sync/staging.js.map +1 -1
- package/dist/services/sync/structured-extractor.d.ts +8 -2
- package/dist/services/sync/structured-extractor.js +243 -35
- package/dist/services/sync/structured-extractor.js.map +1 -1
- package/dist/services/sync/types.d.ts +192 -23
- package/dist/services/sync/unstructured-extractor.d.ts +1 -3
- package/dist/services/sync/unstructured-extractor.js +2 -14
- package/dist/services/sync/unstructured-extractor.js.map +1 -1
- package/dist/utils/git.d.ts +24 -20
- package/dist/utils/git.js +99 -65
- package/dist/utils/git.js.map +1 -1
- package/dist/utils/preflight.d.ts +1 -15
- package/dist/utils/preflight.js +1 -35
- package/dist/utils/preflight.js.map +1 -1
- package/dist/web/_app/immutable/assets/0.CupILLQs.css +1 -0
- package/dist/web/_app/immutable/assets/3.CtJi4Cy9.css +1 -0
- package/dist/web/_app/immutable/assets/5.CydFyZSu.css +1 -0
- package/dist/web/_app/immutable/assets/6.kqeOo0OW.css +1 -0
- package/dist/web/_app/immutable/assets/7.CseIx7qQ.css +1 -0
- package/dist/web/_app/immutable/assets/{8.Sm6jB3a0.css → 8.BYpFDZHK.css} +1 -1
- package/dist/web/_app/immutable/assets/AppShell.Ch_ef9hJ.css +1 -0
- package/dist/web/_app/immutable/assets/ChatPanel.CP-_8txt.css +1 -0
- package/dist/web/_app/immutable/chunks/0oxpWEgM.js +1 -0
- package/dist/web/_app/immutable/chunks/B1y7Wy5O.js +18 -0
- package/dist/web/_app/immutable/chunks/B7eduG_j.js +64 -0
- package/dist/web/_app/immutable/chunks/BBLgaWN8.js +1 -0
- package/dist/web/_app/immutable/chunks/BCB5cYCz.js +2 -0
- package/dist/web/_app/immutable/chunks/{aosHekRC.js → BPUy9_sS.js} +1 -1
- package/dist/web/_app/immutable/chunks/BVBRzmeQ.js +7 -0
- package/dist/web/_app/immutable/chunks/{CUzqHQY_.js → BXuvR8Ks.js} +2 -1
- package/dist/web/_app/immutable/chunks/BeBar3OL.js +1 -0
- package/dist/web/_app/immutable/chunks/BuOTIbJu.js +1 -0
- package/dist/web/_app/immutable/chunks/CLFba8FK.js +5 -0
- package/dist/web/_app/immutable/chunks/CQCkXCml.js +1 -0
- package/dist/web/_app/immutable/chunks/CXuhHL4d.js +1 -0
- package/dist/web/_app/immutable/chunks/Cg9NOuOl.js +27 -0
- package/dist/web/_app/immutable/chunks/Cs5oz2oJ.js +5 -0
- package/dist/web/_app/immutable/chunks/Cs_ROD7H.js +2 -0
- package/dist/web/_app/immutable/chunks/D2aTbzFm.js +3 -0
- package/dist/web/_app/immutable/chunks/D4FXhiC2.js +1 -0
- package/dist/web/_app/immutable/chunks/D4VHRYeB.js +1 -0
- package/dist/web/_app/immutable/chunks/DCGSm8Hl.js +1 -0
- package/dist/web/_app/immutable/chunks/DP09rP34.js +2 -0
- package/dist/web/_app/immutable/chunks/DiP47fAp.js +1 -0
- package/dist/web/_app/immutable/chunks/DptGlK8O.js +1 -0
- package/dist/web/_app/immutable/chunks/O0fx2ss6.js +1 -0
- package/dist/web/_app/immutable/chunks/xBRYfpah.js +1 -0
- package/dist/web/_app/immutable/entry/app.FgnywZP_.js +2 -0
- package/dist/web/_app/immutable/entry/start.Bsa-zlPf.js +1 -0
- package/dist/web/_app/immutable/nodes/0.D3SW-LMc.js +10 -0
- package/dist/web/_app/immutable/nodes/1.y0c5TQTP.js +1 -0
- package/dist/web/_app/immutable/nodes/2.BQfSep9-.js +1 -0
- package/dist/web/_app/immutable/nodes/3.CC4Y-xMM.js +11 -0
- package/dist/web/_app/immutable/nodes/4.Dp0Z-oPW.js +4 -0
- package/dist/web/_app/immutable/nodes/5.gjZ03DON.js +2 -0
- package/dist/web/_app/immutable/nodes/6.dRNIwcJQ.js +1 -0
- package/dist/web/_app/immutable/nodes/7.I4Gjes3o.js +2 -0
- package/dist/web/_app/immutable/nodes/8.Dj14D7uH.js +1 -0
- package/dist/web/_app/version.json +1 -1
- package/dist/web/index.html +10 -12
- package/package.json +4 -2
- package/dist/web/_app/immutable/assets/0.CF0XhAap.css +0 -1
- package/dist/web/_app/immutable/assets/3.BJy7pVXi.css +0 -1
- package/dist/web/_app/immutable/assets/4.Ad16uh9o.css +0 -1
- package/dist/web/_app/immutable/assets/6.Bm2i7O0j.css +0 -1
- package/dist/web/_app/immutable/assets/7.Cn2DG-J6.css +0 -1
- package/dist/web/_app/immutable/assets/AppShell.CztjTuKY.css +0 -1
- package/dist/web/_app/immutable/assets/ChatPanel.RFD5GGYI.css +0 -1
- package/dist/web/_app/immutable/assets/editor.CPAf2SRV.css +0 -1
- package/dist/web/_app/immutable/chunks/4QY4j-jX.js +0 -1
- package/dist/web/_app/immutable/chunks/B3Kdf1r4.js +0 -6
- package/dist/web/_app/immutable/chunks/BIo3H1KR.js +0 -2
- package/dist/web/_app/immutable/chunks/BJLM1w2L.js +0 -23
- package/dist/web/_app/immutable/chunks/BLCwEMdm.js +0 -1
- package/dist/web/_app/immutable/chunks/BSYvCVJt.js +0 -1
- package/dist/web/_app/immutable/chunks/Ba5JX1o9.js +0 -1
- package/dist/web/_app/immutable/chunks/Bj34y868.js +0 -64
- package/dist/web/_app/immutable/chunks/BrWpHgBJ.js +0 -1
- package/dist/web/_app/immutable/chunks/C3g_lwol.js +0 -1
- package/dist/web/_app/immutable/chunks/CXnPm09s.js +0 -1
- package/dist/web/_app/immutable/chunks/ClOTom10.js +0 -1
- package/dist/web/_app/immutable/chunks/CtT4aw_G.js +0 -1
- package/dist/web/_app/immutable/chunks/Dh_H7Owr.js +0 -18
- package/dist/web/_app/immutable/chunks/DnlgZ_Tk.js +0 -5
- package/dist/web/_app/immutable/chunks/DtVH--hH.js +0 -6
- package/dist/web/_app/immutable/chunks/Dzd9kdLj.js +0 -2
- package/dist/web/_app/immutable/chunks/L91a_BGe.js +0 -1
- package/dist/web/_app/immutable/chunks/bHAllEMt.js +0 -1
- package/dist/web/_app/immutable/entry/app.CdrgaaFb.js +0 -2
- package/dist/web/_app/immutable/entry/start.t9LMjt48.js +0 -1
- package/dist/web/_app/immutable/nodes/0.P-Xfebn4.js +0 -2
- package/dist/web/_app/immutable/nodes/1.DiZlq1e6.js +0 -1
- package/dist/web/_app/immutable/nodes/2.ByIZ5J2p.js +0 -1
- package/dist/web/_app/immutable/nodes/3.D7JhktsZ.js +0 -1
- package/dist/web/_app/immutable/nodes/4.BtMeWbPx.js +0 -16
- package/dist/web/_app/immutable/nodes/5.CCYG1pbQ.js +0 -4
- package/dist/web/_app/immutable/nodes/6.CL_Ah04j.js +0 -2
- package/dist/web/_app/immutable/nodes/7.BMTaosAj.js +0 -1
- package/dist/web/_app/immutable/nodes/8.mrPg67cz.js +0 -1
- /package/dist/web/_app/immutable/assets/{5.BhKgiXd2.css → 4.BhKgiXd2.css} +0 -0
|
@@ -412,46 +412,122 @@ body.sg-dragging iframe { pointer-events: none; }
|
|
|
412
412
|
color: var(--color-text-secondary);
|
|
413
413
|
}
|
|
414
414
|
|
|
415
|
-
/* ──
|
|
416
|
-
.
|
|
417
|
-
|
|
418
|
-
}
|
|
419
|
-
.sg-chrome-tooltip-popup {
|
|
415
|
+
/* ── Commit Confirmation Modal ────────────────────────── */
|
|
416
|
+
/* Matches shared Dialog component tokens (dialog-content.svelte) */
|
|
417
|
+
.sg-chrome-modal-overlay {
|
|
420
418
|
display: none;
|
|
421
|
-
position:
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
419
|
+
position: fixed;
|
|
420
|
+
inset: 0;
|
|
421
|
+
background: rgba(0, 0, 0, 0.6);
|
|
422
|
+
z-index: 1000;
|
|
423
|
+
align-items: flex-start;
|
|
424
|
+
justify-content: center;
|
|
425
|
+
padding-top: 80px;
|
|
426
|
+
}
|
|
427
|
+
.sg-chrome-modal-overlay.open {
|
|
428
|
+
display: flex;
|
|
429
|
+
}
|
|
430
|
+
.sg-chrome-modal {
|
|
431
|
+
background: var(--color-bg-surface, var(--surface));
|
|
432
|
+
border: 1px solid var(--color-border-strong, var(--border));
|
|
433
|
+
border-radius: 12px;
|
|
434
|
+
width: fit-content;
|
|
435
|
+
min-width: 320px;
|
|
436
|
+
max-width: min(640px, 90vw);
|
|
437
|
+
max-height: calc(100vh - 120px);
|
|
438
|
+
display: flex;
|
|
439
|
+
flex-direction: column;
|
|
440
|
+
overflow: hidden;
|
|
441
|
+
box-shadow: 0 24px 80px rgba(0, 0, 0, 0.5);
|
|
442
|
+
}
|
|
443
|
+
.sg-chrome-modal-header {
|
|
444
|
+
display: flex;
|
|
445
|
+
align-items: center;
|
|
446
|
+
justify-content: space-between;
|
|
447
|
+
padding: 20px 24px 0;
|
|
448
|
+
flex-shrink: 0;
|
|
449
|
+
}
|
|
450
|
+
.sg-chrome-modal-title {
|
|
451
|
+
font-size: 16px;
|
|
452
|
+
font-weight: 500;
|
|
453
|
+
color: var(--color-text, var(--text));
|
|
454
|
+
}
|
|
455
|
+
.sg-chrome-modal-close {
|
|
456
|
+
display: flex;
|
|
457
|
+
align-items: center;
|
|
458
|
+
justify-content: center;
|
|
459
|
+
width: 28px;
|
|
460
|
+
height: 28px;
|
|
461
|
+
border: none;
|
|
427
462
|
border-radius: 6px;
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
463
|
+
background: transparent;
|
|
464
|
+
color: var(--color-text-tertiary);
|
|
465
|
+
font-size: 16px;
|
|
466
|
+
cursor: pointer;
|
|
467
|
+
transition: color 0.15s, background 0.15s;
|
|
468
|
+
}
|
|
469
|
+
.sg-chrome-modal-close:hover {
|
|
470
|
+
color: var(--color-text, var(--text));
|
|
471
|
+
background: var(--color-bg-hover, var(--bg-hover));
|
|
472
|
+
}
|
|
473
|
+
.sg-chrome-modal-body {
|
|
474
|
+
padding: 12px 24px 20px;
|
|
432
475
|
overflow-y: auto;
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
font-size:
|
|
476
|
+
flex: 1;
|
|
477
|
+
min-height: 0;
|
|
478
|
+
font-size: 12px;
|
|
479
|
+
line-height: 1.6;
|
|
480
|
+
}
|
|
481
|
+
.sg-chrome-modal-footer {
|
|
482
|
+
display: flex;
|
|
483
|
+
align-items: center;
|
|
484
|
+
justify-content: flex-end;
|
|
485
|
+
gap: 8px;
|
|
486
|
+
padding: 16px 24px;
|
|
487
|
+
border-top: 1px solid var(--color-border, var(--border));
|
|
488
|
+
flex-shrink: 0;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/* Small modal variant (revert confirmation) */
|
|
492
|
+
.sg-chrome-modal-sm {
|
|
493
|
+
width: 420px;
|
|
494
|
+
min-width: 0;
|
|
495
|
+
}
|
|
496
|
+
.sg-chrome-modal-message {
|
|
497
|
+
font-size: 13px;
|
|
498
|
+
color: var(--color-text-secondary);
|
|
436
499
|
line-height: 1.5;
|
|
500
|
+
margin: 0;
|
|
437
501
|
}
|
|
438
|
-
|
|
439
|
-
|
|
502
|
+
|
|
503
|
+
/* Destructive button (used in revert modal) */
|
|
504
|
+
.sg-chrome-btn-destructive {
|
|
505
|
+
display: inline-flex;
|
|
506
|
+
align-items: center;
|
|
507
|
+
height: 24px;
|
|
508
|
+
padding: 0 12px;
|
|
509
|
+
border: none;
|
|
510
|
+
background: var(--color-destructive, #ea4335);
|
|
511
|
+
color: white;
|
|
512
|
+
font-family: inherit;
|
|
513
|
+
font-size: 12px;
|
|
514
|
+
font-weight: 500;
|
|
515
|
+
cursor: pointer;
|
|
516
|
+
border-radius: 4px;
|
|
517
|
+
transition: background 0.15s;
|
|
440
518
|
}
|
|
441
|
-
.sg-chrome-
|
|
442
|
-
|
|
443
|
-
right: 0;
|
|
444
|
-
transform: none;
|
|
445
|
-
min-width: auto;
|
|
446
|
-
width: max-content;
|
|
519
|
+
.sg-chrome-btn-destructive:hover:not(:disabled) {
|
|
520
|
+
background: color-mix(in srgb, var(--color-destructive, #ea4335) 85%, black);
|
|
447
521
|
}
|
|
522
|
+
.sg-chrome-btn-destructive:disabled { opacity: 0.7; cursor: default; }
|
|
523
|
+
|
|
524
|
+
/* ── File list items (used in commit modal) ──────────── */
|
|
448
525
|
.sg-chrome-tooltip-file {
|
|
449
526
|
color: var(--color-text-secondary);
|
|
450
|
-
white-space: nowrap;
|
|
451
|
-
overflow: hidden;
|
|
452
527
|
display: flex;
|
|
453
528
|
align-items: center;
|
|
454
529
|
gap: 0;
|
|
530
|
+
white-space: nowrap;
|
|
455
531
|
}
|
|
456
532
|
.sg-chrome-tooltip-file .sg-path-sep {
|
|
457
533
|
flex-shrink: 0;
|
|
@@ -550,6 +626,44 @@ body.sg-dragging iframe { pointer-events: none; }
|
|
|
550
626
|
margin: 0 4px;
|
|
551
627
|
}
|
|
552
628
|
|
|
629
|
+
/* ── Collaborator pills (footer right) ─────────────────── */
|
|
630
|
+
.sg-chrome-collaborators {
|
|
631
|
+
display: flex;
|
|
632
|
+
align-items: center;
|
|
633
|
+
gap: 6px;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
.sg-chrome-collaborator {
|
|
637
|
+
display: inline-flex;
|
|
638
|
+
align-items: center;
|
|
639
|
+
gap: 5px;
|
|
640
|
+
height: 20px;
|
|
641
|
+
padding: 0 7px 0 3px;
|
|
642
|
+
border-radius: 10px;
|
|
643
|
+
background: var(--color-surface);
|
|
644
|
+
border: 1px solid var(--color-border);
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
.sg-chrome-collaborator-avatar {
|
|
648
|
+
display: inline-flex;
|
|
649
|
+
align-items: center;
|
|
650
|
+
justify-content: center;
|
|
651
|
+
width: 14px;
|
|
652
|
+
height: 14px;
|
|
653
|
+
border-radius: 50%;
|
|
654
|
+
background: var(--color-accent);
|
|
655
|
+
color: #fff;
|
|
656
|
+
font-size: 9px;
|
|
657
|
+
font-weight: 600;
|
|
658
|
+
flex-shrink: 0;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
.sg-chrome-collaborator-name {
|
|
662
|
+
font-size: 11px;
|
|
663
|
+
color: var(--color-text-secondary);
|
|
664
|
+
white-space: nowrap;
|
|
665
|
+
}
|
|
666
|
+
|
|
553
667
|
/* ── Focus rings ───────────────────────────────────────── */
|
|
554
668
|
.sg-chrome-btn:focus-visible,
|
|
555
669
|
.sg-chrome-theme-toggle:focus-visible,
|
|
@@ -44,6 +44,10 @@
|
|
|
44
44
|
return e;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
function titleCase(str) {
|
|
48
|
+
return str.replace(/-/g, ' ').replace(/\b\w/g, function (c) { return c.toUpperCase(); });
|
|
49
|
+
}
|
|
50
|
+
|
|
47
51
|
// ── Theme ───────────────────────────────────────────────
|
|
48
52
|
function getTheme() {
|
|
49
53
|
return document.documentElement.dataset.theme || localStorage.getItem('theme') || 'dark';
|
|
@@ -144,7 +148,7 @@
|
|
|
144
148
|
if (e.key === 'Escape') overflowMenu.classList.remove('open');
|
|
145
149
|
});
|
|
146
150
|
|
|
147
|
-
// ── Build Footer
|
|
151
|
+
// ── Build Footer ────────────────────────────────────────
|
|
148
152
|
var footer = el('footer', 'sg-chrome-footer');
|
|
149
153
|
|
|
150
154
|
// Left: theme toggle + workspace stats
|
|
@@ -160,7 +164,11 @@
|
|
|
160
164
|
footerLeft.appendChild(statsEl);
|
|
161
165
|
footer.appendChild(footerLeft);
|
|
162
166
|
|
|
163
|
-
//
|
|
167
|
+
// Right: collaborators
|
|
168
|
+
var footerRight = el('div', 'sg-chrome-footer-right');
|
|
169
|
+
var collaboratorsEl = el('span', 'sg-chrome-collaborators');
|
|
170
|
+
footerRight.appendChild(collaboratorsEl);
|
|
171
|
+
footer.appendChild(footerRight);
|
|
164
172
|
|
|
165
173
|
// ── Inject into DOM ─────────────────────────────────────
|
|
166
174
|
if (!workspaceMode) {
|
|
@@ -183,36 +191,23 @@
|
|
|
183
191
|
body.appendChild(footer);
|
|
184
192
|
}
|
|
185
193
|
|
|
186
|
-
// ── Stats
|
|
187
|
-
var stats = { entities: 0, links: 0 };
|
|
188
|
-
|
|
189
|
-
function updateStatsUI() {
|
|
190
|
-
if (stats.entities > 0) {
|
|
191
|
-
var e = stats.entities;
|
|
192
|
-
var l = stats.links;
|
|
193
|
-
statsEl.innerHTML =
|
|
194
|
-
'<span class="sg-chrome-stat">' + e.toLocaleString() + (e === 1 ? ' entity' : ' entities') + '</span>' +
|
|
195
|
-
'<span class="sg-chrome-stat-sep">|</span>' +
|
|
196
|
-
'<span class="sg-chrome-stat">' + l.toLocaleString() + (l === 1 ? ' link' : ' links') + '</span>';
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
194
|
+
// ── Stats ────────────────────────────────────────────────
|
|
200
195
|
function fetchStats() {
|
|
201
196
|
fetch('/api/stats').then(function (r) { return r.json(); }).then(function (data) {
|
|
202
|
-
|
|
203
|
-
|
|
197
|
+
if (data.entities > 0) {
|
|
198
|
+
var e = data.entities;
|
|
199
|
+
var l = data.links;
|
|
200
|
+
statsEl.innerHTML =
|
|
201
|
+
'<span class="sg-chrome-stat">' + e.toLocaleString() + (e === 1 ? ' entity' : ' entities') + '</span>' +
|
|
202
|
+
'<span class="sg-chrome-stat-sep">|</span>' +
|
|
203
|
+
'<span class="sg-chrome-stat">' + l.toLocaleString() + (l === 1 ? ' link' : ' links') + '</span>';
|
|
204
|
+
}
|
|
204
205
|
}).catch(function () { /* silently fail */ });
|
|
205
206
|
}
|
|
206
207
|
|
|
207
|
-
// Initial fetch + poll every 60s
|
|
208
208
|
fetchStats();
|
|
209
209
|
setInterval(fetchStats, 60000);
|
|
210
210
|
|
|
211
|
-
// Refresh stats when Svelte store signals a save
|
|
212
|
-
window.addEventListener('studiograph:git-refresh', function () {
|
|
213
|
-
fetchStats();
|
|
214
|
-
});
|
|
215
|
-
|
|
216
211
|
// ── Event System ────────────────────────────────────────
|
|
217
212
|
var listeners = {};
|
|
218
213
|
|
|
@@ -222,6 +217,9 @@
|
|
|
222
217
|
});
|
|
223
218
|
}
|
|
224
219
|
|
|
220
|
+
// ── Save Status Auto-clear ───────────────────────────────
|
|
221
|
+
var saveStatusTimer = null;
|
|
222
|
+
|
|
225
223
|
// ── Public API ──────────────────────────────────────────
|
|
226
224
|
window.StudioChrome = {
|
|
227
225
|
on: function (event, fn) {
|
|
@@ -255,11 +253,20 @@
|
|
|
255
253
|
* Set save status indicator.
|
|
256
254
|
* @param {string} text - Status text to display
|
|
257
255
|
* @param {string} [type] - 'saving'|'saved'|'error'|'' — controls styling
|
|
256
|
+
* @param {number} [clearAfterMs] - auto-clear after this many ms (default 2000 for 'saved')
|
|
258
257
|
*/
|
|
259
|
-
setSaveStatus: function (text, type) {
|
|
258
|
+
setSaveStatus: function (text, type, clearAfterMs) {
|
|
260
259
|
type = type || '';
|
|
261
260
|
saveStatusEl.textContent = text || '';
|
|
262
261
|
saveStatusEl.className = 'sg-chrome-save-status' + (type === 'error' ? ' sg-chrome-save-error' : '');
|
|
262
|
+
if (saveStatusTimer) clearTimeout(saveStatusTimer);
|
|
263
|
+
var ms = clearAfterMs != null ? clearAfterMs : (type === 'saved' ? 2000 : 0);
|
|
264
|
+
if (ms > 0) {
|
|
265
|
+
saveStatusTimer = setTimeout(function () {
|
|
266
|
+
saveStatusEl.textContent = '';
|
|
267
|
+
saveStatusEl.className = 'sg-chrome-save-status';
|
|
268
|
+
}, ms);
|
|
269
|
+
}
|
|
263
270
|
},
|
|
264
271
|
|
|
265
272
|
refreshStatus: function () {
|
|
@@ -270,6 +277,25 @@
|
|
|
270
277
|
fetchStats();
|
|
271
278
|
},
|
|
272
279
|
|
|
280
|
+
/**
|
|
281
|
+
* Update the collaborators list in the footer right.
|
|
282
|
+
* @param {Array<{id: string, name: string, color?: string}>} users
|
|
283
|
+
*/
|
|
284
|
+
setCollaborators: function (users) {
|
|
285
|
+
collaboratorsEl.innerHTML = '';
|
|
286
|
+
if (!users || users.length === 0) return;
|
|
287
|
+
users.forEach(function (u) {
|
|
288
|
+
var pill = el('span', 'sg-chrome-collaborator');
|
|
289
|
+
var avatar = el('span', 'sg-chrome-collaborator-avatar');
|
|
290
|
+
avatar.textContent = u.name[0].toUpperCase();
|
|
291
|
+
pill.appendChild(avatar);
|
|
292
|
+
var name = el('span', 'sg-chrome-collaborator-name');
|
|
293
|
+
name.textContent = u.name;
|
|
294
|
+
pill.appendChild(name);
|
|
295
|
+
collaboratorsEl.appendChild(pill);
|
|
296
|
+
});
|
|
297
|
+
},
|
|
298
|
+
|
|
273
299
|
addMenuItem: function (opts) {
|
|
274
300
|
var btn = el('button', 'sg-chrome-menu-item' + (opts.danger ? ' sg-chrome-danger' : ''),
|
|
275
301
|
(opts.icon || '') + ' ' + esc(opts.label));
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OT Collab Authority — central document authority for @codemirror/collab.
|
|
3
|
+
*
|
|
4
|
+
* Manages one document per entity. Each document tracks:
|
|
5
|
+
* - doc: the current document text (string)
|
|
6
|
+
* - updates: array of serialized ChangeSet + clientID pairs
|
|
7
|
+
* - version: current version number (= updates.length)
|
|
8
|
+
* - pending: debounce timer for disk persistence
|
|
9
|
+
*
|
|
10
|
+
* Lifecycle:
|
|
11
|
+
* Created when first client connects to an entity.
|
|
12
|
+
* Seeded from disk content via graph.get().
|
|
13
|
+
* Updated when clients push changes.
|
|
14
|
+
* Persisted to disk via graph.update() on debounce.
|
|
15
|
+
* Evicted after all clients disconnect + idle timeout.
|
|
16
|
+
*
|
|
17
|
+
* No binary state files, no vector clocks, no CRDT — just a version counter.
|
|
18
|
+
* If a client reconnects after eviction, it fetches the current doc fresh.
|
|
19
|
+
*/
|
|
20
|
+
import type { WorkspaceManager } from '../core/workspace-manager.js';
|
|
21
|
+
import type { SessionManager } from './session-manager.js';
|
|
22
|
+
interface Update {
|
|
23
|
+
changes: any;
|
|
24
|
+
clientID: string;
|
|
25
|
+
}
|
|
26
|
+
export declare class CollabAuthority {
|
|
27
|
+
private workspaceManager;
|
|
28
|
+
private sessionManager;
|
|
29
|
+
private docs;
|
|
30
|
+
constructor(workspaceManager: WorkspaceManager, sessionManager: SessionManager);
|
|
31
|
+
/**
|
|
32
|
+
* Get or create a document for the given entity.
|
|
33
|
+
* Returns the current document text and version.
|
|
34
|
+
*/
|
|
35
|
+
getDocument(docName: string): {
|
|
36
|
+
version: number;
|
|
37
|
+
doc: string;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Pull updates since clientVersion.
|
|
41
|
+
* Returns serialized updates the client hasn't seen yet.
|
|
42
|
+
*/
|
|
43
|
+
pullUpdates(docName: string, clientVersion: number): Update[];
|
|
44
|
+
/**
|
|
45
|
+
* Push updates from a client.
|
|
46
|
+
* Returns true if accepted, false if version mismatch (client must pull first).
|
|
47
|
+
*/
|
|
48
|
+
pushUpdates(docName: string, clientVersion: number, updates: Array<{
|
|
49
|
+
changes: any;
|
|
50
|
+
clientID: string;
|
|
51
|
+
}>, gitUser?: {
|
|
52
|
+
id: string;
|
|
53
|
+
name: string;
|
|
54
|
+
email: string;
|
|
55
|
+
}): boolean;
|
|
56
|
+
/** Track a client joining a document. */
|
|
57
|
+
clientJoin(docName: string, clientId: string): void;
|
|
58
|
+
/** Track a client leaving a document. Persist immediately and schedule eviction if no clients remain. */
|
|
59
|
+
clientLeave(docName: string, clientId: string): void;
|
|
60
|
+
/** Persist all dirty documents immediately (for server shutdown). */
|
|
61
|
+
flushAll(): Promise<void>;
|
|
62
|
+
/** Notify the authority that an entity was updated externally (e.g., refresh from source). */
|
|
63
|
+
externalUpdate(docName: string, newContent: string): void;
|
|
64
|
+
destroy(): void;
|
|
65
|
+
private getOrCreate;
|
|
66
|
+
private schedulePersist;
|
|
67
|
+
private persistToDisk;
|
|
68
|
+
private scheduleEvict;
|
|
69
|
+
}
|
|
70
|
+
export {};
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OT Collab Authority — central document authority for @codemirror/collab.
|
|
3
|
+
*
|
|
4
|
+
* Manages one document per entity. Each document tracks:
|
|
5
|
+
* - doc: the current document text (string)
|
|
6
|
+
* - updates: array of serialized ChangeSet + clientID pairs
|
|
7
|
+
* - version: current version number (= updates.length)
|
|
8
|
+
* - pending: debounce timer for disk persistence
|
|
9
|
+
*
|
|
10
|
+
* Lifecycle:
|
|
11
|
+
* Created when first client connects to an entity.
|
|
12
|
+
* Seeded from disk content via graph.get().
|
|
13
|
+
* Updated when clients push changes.
|
|
14
|
+
* Persisted to disk via graph.update() on debounce.
|
|
15
|
+
* Evicted after all clients disconnect + idle timeout.
|
|
16
|
+
*
|
|
17
|
+
* No binary state files, no vector clocks, no CRDT — just a version counter.
|
|
18
|
+
* If a client reconnects after eviction, it fetches the current doc fresh.
|
|
19
|
+
*/
|
|
20
|
+
import { ChangeSet, Text } from '@codemirror/state';
|
|
21
|
+
const PERSIST_DEBOUNCE_MS = 2000;
|
|
22
|
+
const EVICT_AFTER_MS = 30_000; // 30s after last client disconnects
|
|
23
|
+
export class CollabAuthority {
|
|
24
|
+
workspaceManager;
|
|
25
|
+
sessionManager;
|
|
26
|
+
docs = new Map();
|
|
27
|
+
constructor(workspaceManager, sessionManager) {
|
|
28
|
+
this.workspaceManager = workspaceManager;
|
|
29
|
+
this.sessionManager = sessionManager;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Get or create a document for the given entity.
|
|
33
|
+
* Returns the current document text and version.
|
|
34
|
+
*/
|
|
35
|
+
getDocument(docName) {
|
|
36
|
+
const authority = this.getOrCreate(docName);
|
|
37
|
+
const result = { version: authority.updates.length, doc: authority.doc.toString() };
|
|
38
|
+
console.log(`[collab-authority] getDocument ${docName}: v${result.version}, ${result.doc.length} chars`);
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Pull updates since clientVersion.
|
|
43
|
+
* Returns serialized updates the client hasn't seen yet.
|
|
44
|
+
*/
|
|
45
|
+
pullUpdates(docName, clientVersion) {
|
|
46
|
+
const authority = this.docs.get(docName);
|
|
47
|
+
if (!authority)
|
|
48
|
+
return [];
|
|
49
|
+
if (clientVersion > authority.updates.length)
|
|
50
|
+
return [];
|
|
51
|
+
return authority.updates.slice(clientVersion);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Push updates from a client.
|
|
55
|
+
* Returns true if accepted, false if version mismatch (client must pull first).
|
|
56
|
+
*/
|
|
57
|
+
pushUpdates(docName, clientVersion, updates, gitUser) {
|
|
58
|
+
const authority = this.getOrCreate(docName);
|
|
59
|
+
if (clientVersion !== authority.updates.length)
|
|
60
|
+
return false;
|
|
61
|
+
for (const update of updates) {
|
|
62
|
+
const changes = ChangeSet.fromJSON(update.changes);
|
|
63
|
+
authority.updates.push({ changes: update.changes, clientID: update.clientID });
|
|
64
|
+
authority.doc = changes.apply(authority.doc);
|
|
65
|
+
}
|
|
66
|
+
authority.dirty = true;
|
|
67
|
+
console.log(`[collab-authority] pushUpdates ${docName}: accepted ${updates.length} update(s), now v${authority.updates.length}, doc ${authority.doc.length} chars`);
|
|
68
|
+
this.schedulePersist(docName, gitUser);
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
/** Track a client joining a document. */
|
|
72
|
+
clientJoin(docName, clientId) {
|
|
73
|
+
const authority = this.getOrCreate(docName);
|
|
74
|
+
authority.clients.add(clientId);
|
|
75
|
+
// Cancel eviction if a client reconnects
|
|
76
|
+
if (authority.evictTimer) {
|
|
77
|
+
clearTimeout(authority.evictTimer);
|
|
78
|
+
authority.evictTimer = null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/** Track a client leaving a document. Persist immediately and schedule eviction if no clients remain. */
|
|
82
|
+
clientLeave(docName, clientId) {
|
|
83
|
+
const authority = this.docs.get(docName);
|
|
84
|
+
if (!authority)
|
|
85
|
+
return;
|
|
86
|
+
authority.clients.delete(clientId);
|
|
87
|
+
console.log(`[collab-authority] clientLeave ${docName}: client=${clientId}, remaining=${authority.clients.size}, dirty=${authority.dirty}`);
|
|
88
|
+
// Persist immediately on client leave so navigating back reads fresh content from disk
|
|
89
|
+
if (authority.dirty) {
|
|
90
|
+
if (authority.persistTimer) {
|
|
91
|
+
clearTimeout(authority.persistTimer);
|
|
92
|
+
authority.persistTimer = null;
|
|
93
|
+
}
|
|
94
|
+
this.persistToDisk(docName, authority);
|
|
95
|
+
console.log(`[collab-authority] clientLeave ${docName}: persisted to disk (${authority.doc.length} chars)`);
|
|
96
|
+
}
|
|
97
|
+
if (authority.clients.size === 0) {
|
|
98
|
+
this.scheduleEvict(docName);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/** Persist all dirty documents immediately (for server shutdown). */
|
|
102
|
+
async flushAll() {
|
|
103
|
+
for (const [docName, authority] of this.docs) {
|
|
104
|
+
if (authority.persistTimer) {
|
|
105
|
+
clearTimeout(authority.persistTimer);
|
|
106
|
+
authority.persistTimer = null;
|
|
107
|
+
}
|
|
108
|
+
if (authority.dirty) {
|
|
109
|
+
this.persistToDisk(docName, authority);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/** Notify the authority that an entity was updated externally (e.g., refresh from source). */
|
|
114
|
+
externalUpdate(docName, newContent) {
|
|
115
|
+
const authority = this.docs.get(docName);
|
|
116
|
+
if (!authority)
|
|
117
|
+
return; // not loaded — next getOrCreate will read from disk
|
|
118
|
+
// Replace the authority doc. Connected clients will get the new content
|
|
119
|
+
// via an entity_change event → invalidateAll() on the client side.
|
|
120
|
+
authority.doc = Text.of(newContent.split('\n'));
|
|
121
|
+
authority.updates = []; // reset history — clients must re-fetch
|
|
122
|
+
authority.dirty = false; // already written to disk by the caller
|
|
123
|
+
}
|
|
124
|
+
destroy() {
|
|
125
|
+
for (const [, authority] of this.docs) {
|
|
126
|
+
if (authority.persistTimer)
|
|
127
|
+
clearTimeout(authority.persistTimer);
|
|
128
|
+
if (authority.evictTimer)
|
|
129
|
+
clearTimeout(authority.evictTimer);
|
|
130
|
+
}
|
|
131
|
+
this.docs.clear();
|
|
132
|
+
}
|
|
133
|
+
// ── Internal ──────────────────────────────────────────────────────────────
|
|
134
|
+
getOrCreate(docName) {
|
|
135
|
+
const existing = this.docs.get(docName);
|
|
136
|
+
if (existing)
|
|
137
|
+
return existing;
|
|
138
|
+
// Seed from disk
|
|
139
|
+
const [repo, entityType, entityId] = docName.split('/');
|
|
140
|
+
let content = '';
|
|
141
|
+
try {
|
|
142
|
+
const graph = this.workspaceManager.getGraph(repo);
|
|
143
|
+
const entity = graph.get(entityType, entityId);
|
|
144
|
+
content = entity?.document?.content ?? '';
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
// Entity not found — start with empty doc
|
|
148
|
+
}
|
|
149
|
+
const authority = {
|
|
150
|
+
doc: Text.of(content.split('\n')),
|
|
151
|
+
updates: [],
|
|
152
|
+
clients: new Set(),
|
|
153
|
+
dirty: false,
|
|
154
|
+
persistTimer: null,
|
|
155
|
+
evictTimer: null,
|
|
156
|
+
};
|
|
157
|
+
this.docs.set(docName, authority);
|
|
158
|
+
console.log(`[collab-authority] getOrCreate ${docName}: seeded from disk, ${content.length} chars`);
|
|
159
|
+
return authority;
|
|
160
|
+
}
|
|
161
|
+
schedulePersist(docName, gitUser) {
|
|
162
|
+
const authority = this.docs.get(docName);
|
|
163
|
+
if (!authority)
|
|
164
|
+
return;
|
|
165
|
+
if (authority.persistTimer)
|
|
166
|
+
clearTimeout(authority.persistTimer);
|
|
167
|
+
authority.persistTimer = setTimeout(() => {
|
|
168
|
+
authority.persistTimer = null;
|
|
169
|
+
this.persistToDisk(docName, authority, gitUser);
|
|
170
|
+
}, PERSIST_DEBOUNCE_MS);
|
|
171
|
+
}
|
|
172
|
+
persistToDisk(docName, authority, gitUser) {
|
|
173
|
+
if (!authority.dirty)
|
|
174
|
+
return;
|
|
175
|
+
const [repo, entityType, entityId] = docName.split('/');
|
|
176
|
+
try {
|
|
177
|
+
const graph = this.workspaceManager.getGraph(repo);
|
|
178
|
+
// Check if content actually changed
|
|
179
|
+
const existing = graph.get(entityType, entityId);
|
|
180
|
+
const existingBody = existing?.document?.content ?? '';
|
|
181
|
+
const newBody = authority.doc.toString();
|
|
182
|
+
console.log(`[collab-authority] persistToDisk ${docName}: disk=${existingBody.length} chars, authority=${newBody.length} chars, same=${newBody === existingBody}`);
|
|
183
|
+
if (newBody === existingBody) {
|
|
184
|
+
authority.dirty = false;
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
graph.update({
|
|
188
|
+
entityType: entityType,
|
|
189
|
+
entityId,
|
|
190
|
+
content: newBody,
|
|
191
|
+
skipCommit: true,
|
|
192
|
+
});
|
|
193
|
+
const user = gitUser ?? { id: 'collab', name: 'Collab', email: 'collab@studiograph' };
|
|
194
|
+
this.sessionManager.markDirty(repo, user, entityType, entityId, existing?.path);
|
|
195
|
+
authority.dirty = false;
|
|
196
|
+
}
|
|
197
|
+
catch (err) {
|
|
198
|
+
console.warn(`[collab-authority] Failed to persist ${docName}:`, err.message);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
scheduleEvict(docName) {
|
|
202
|
+
const authority = this.docs.get(docName);
|
|
203
|
+
if (!authority)
|
|
204
|
+
return;
|
|
205
|
+
if (authority.evictTimer)
|
|
206
|
+
clearTimeout(authority.evictTimer);
|
|
207
|
+
authority.evictTimer = setTimeout(() => {
|
|
208
|
+
// Persist any remaining changes before evicting
|
|
209
|
+
if (authority.dirty) {
|
|
210
|
+
this.persistToDisk(docName, authority);
|
|
211
|
+
}
|
|
212
|
+
if (authority.persistTimer)
|
|
213
|
+
clearTimeout(authority.persistTimer);
|
|
214
|
+
this.docs.delete(docName);
|
|
215
|
+
}, EVICT_AFTER_MS);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
//# sourceMappingURL=collab-authority.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collab-authority.js","sourceRoot":"","sources":["../../src/server/collab-authority.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CollabServer — real-time co-editing via Hocuspocus (Yjs CRDT).
|
|
3
|
+
*
|
|
4
|
+
* Each entity body is a shared Y.Text document named "{repo}/{entityType}/{entityId}".
|
|
5
|
+
* Frontmatter is NOT part of the shared document (edited via property inputs separately).
|
|
6
|
+
*
|
|
7
|
+
* - onAuthenticate: verifies __sg_token cookie or Hocuspocus token param
|
|
8
|
+
* - onLoadDocument: seeds Y.Text from the entity's body on disk (first connect only)
|
|
9
|
+
* - onStoreDocument: persists the merged body back to disk after debounce (2s idle)
|
|
10
|
+
*/
|
|
11
|
+
import type { IncomingMessage } from 'node:http';
|
|
12
|
+
import type { WebSocket } from 'ws';
|
|
13
|
+
import type { WorkspaceManager } from '../core/workspace-manager.js';
|
|
14
|
+
import type { SessionManager } from './session-manager.js';
|
|
15
|
+
import type { AuthService } from '../services/auth-service.js';
|
|
16
|
+
export declare class CollabServer {
|
|
17
|
+
private workspacePath;
|
|
18
|
+
private workspaceManager;
|
|
19
|
+
private sessionManager;
|
|
20
|
+
private authService;
|
|
21
|
+
private hocus;
|
|
22
|
+
constructor(workspacePath: string, workspaceManager: WorkspaceManager, sessionManager: SessionManager, authService: AuthService);
|
|
23
|
+
private onAuthenticate;
|
|
24
|
+
private yjsStatePath;
|
|
25
|
+
private onLoadDocument;
|
|
26
|
+
private onStoreDocument;
|
|
27
|
+
handleConnection(socket: WebSocket, req: IncomingMessage): void;
|
|
28
|
+
destroy(): void;
|
|
29
|
+
}
|