studiograph 1.2.0-beta.9 → 1.2.1
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/README.md +13 -18
- package/dist/agent/orchestrator.d.ts +56 -0
- package/dist/agent/orchestrator.js +167 -22
- package/dist/agent/orchestrator.js.map +1 -1
- package/dist/agent/prompts/system.md +51 -63
- package/dist/agent/skill-loader.js +8 -0
- package/dist/agent/skill-loader.js.map +1 -1
- package/dist/agent/skills/entity-schema.md +4 -19
- package/dist/agent/skills/gather-context.md +1 -1
- package/dist/agent/tools/graph-tools.d.ts +41 -1
- package/dist/agent/tools/graph-tools.js +133 -57
- package/dist/agent/tools/graph-tools.js.map +1 -1
- package/dist/agent/tools/ops-tools.js +82 -0
- package/dist/agent/tools/ops-tools.js.map +1 -1
- package/dist/agent/tools/permission-tools.d.ts +90 -0
- package/dist/agent/tools/permission-tools.js +207 -0
- package/dist/agent/tools/permission-tools.js.map +1 -0
- package/dist/agent/tools/sync-tools.js +2 -2
- package/dist/agent/tools/sync-tools.js.map +1 -1
- package/dist/auth/github.d.ts +1 -1
- package/dist/auth/github.js +21 -13
- package/dist/auth/github.js.map +1 -1
- package/dist/cli/commands/access.d.ts +11 -0
- package/dist/cli/commands/access.js +156 -0
- package/dist/cli/commands/access.js.map +1 -0
- package/dist/cli/commands/app.js +25 -2
- package/dist/cli/commands/app.js.map +1 -1
- package/dist/cli/commands/clear.d.ts +5 -0
- package/dist/cli/commands/clear.js +36 -0
- package/dist/cli/commands/clear.js.map +1 -0
- package/dist/cli/commands/clone.js +3 -3
- package/dist/cli/commands/clone.js.map +1 -1
- package/dist/cli/commands/collection.d.ts +10 -0
- package/dist/cli/commands/collection.js +187 -0
- package/dist/cli/commands/collection.js.map +1 -0
- package/dist/cli/commands/commit.js +2 -1
- package/dist/cli/commands/commit.js.map +1 -1
- package/dist/cli/commands/config.d.ts +100 -0
- package/dist/cli/commands/config.js +1 -1
- package/dist/cli/commands/config.js.map +1 -1
- package/dist/cli/commands/deploy.js +10 -1
- package/dist/cli/commands/deploy.js.map +1 -1
- package/dist/cli/commands/index.js +1 -1
- package/dist/cli/commands/index.js.map +1 -1
- package/dist/cli/commands/init.js +32 -40
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/join.d.ts +0 -6
- package/dist/cli/commands/join.js +26 -111
- package/dist/cli/commands/join.js.map +1 -1
- package/dist/cli/commands/lint.js +1 -2
- package/dist/cli/commands/lint.js.map +1 -1
- package/dist/cli/commands/mcp.js +6 -5
- package/dist/cli/commands/mcp.js.map +1 -1
- package/dist/cli/commands/org.d.ts +10 -0
- package/dist/cli/commands/org.js +132 -0
- package/dist/cli/commands/org.js.map +1 -0
- package/dist/cli/commands/orphans.js +1 -2
- package/dist/cli/commands/orphans.js.map +1 -1
- package/dist/cli/commands/provision.js +3 -3
- package/dist/cli/commands/provision.js.map +1 -1
- package/dist/cli/commands/resolve.d.ts +8 -0
- package/dist/cli/commands/resolve.js +85 -0
- package/dist/cli/commands/resolve.js.map +1 -0
- package/dist/cli/commands/serve.js +55 -25
- package/dist/cli/commands/serve.js.map +1 -1
- package/dist/cli/commands/start.js +22 -205
- package/dist/cli/commands/start.js.map +1 -1
- package/dist/cli/commands/sync.js +53 -8
- package/dist/cli/commands/sync.js.map +1 -1
- package/dist/cli/commands/team.d.ts +12 -0
- package/dist/cli/commands/team.js +185 -0
- package/dist/cli/commands/team.js.map +1 -0
- package/dist/cli/index.js +41 -34
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/scaffolding.d.ts +5 -3
- package/dist/cli/scaffolding.js +191 -97
- package/dist/cli/scaffolding.js.map +1 -1
- package/dist/cli/setup-wizard.js +20 -10
- package/dist/cli/setup-wizard.js.map +1 -1
- package/dist/cli/sync-review-interactive.js +1 -1
- package/dist/cli/sync-review-interactive.js.map +1 -1
- package/dist/core/graph.d.ts +5 -10
- package/dist/core/graph.js +84 -85
- package/dist/core/graph.js.map +1 -1
- package/dist/core/migration-runner.d.ts +42 -0
- package/dist/core/migration-runner.js +232 -0
- package/dist/core/migration-runner.js.map +1 -0
- package/dist/core/migration-types.d.ts +101 -0
- package/dist/core/migration-types.js +21 -0
- package/dist/core/migration-types.js.map +1 -0
- package/dist/core/migrations/20260219-formalize-memory-location.d.ts +2 -0
- package/dist/core/migrations/20260219-formalize-memory-location.js +35 -0
- package/dist/core/migrations/20260219-formalize-memory-location.js.map +1 -0
- package/dist/core/migrations/20260220-add-workspace-metadata.d.ts +12 -0
- package/dist/core/migrations/20260220-add-workspace-metadata.js +65 -0
- package/dist/core/migrations/20260220-add-workspace-metadata.js.map +1 -0
- package/dist/core/migrations/20260220-add-workspace-readme.d.ts +11 -0
- package/dist/core/migrations/20260220-add-workspace-readme.js +82 -0
- package/dist/core/migrations/20260220-add-workspace-readme.js.map +1 -0
- package/dist/core/migrations/20260220-migrate-yaml-to-json.d.ts +9 -0
- package/dist/core/migrations/20260220-migrate-yaml-to-json.js +64 -0
- package/dist/core/migrations/20260220-migrate-yaml-to-json.js.map +1 -0
- package/dist/core/migrations/index.d.ts +11 -0
- package/dist/core/migrations/index.js +23 -0
- package/dist/core/migrations/index.js.map +1 -0
- package/dist/core/types.d.ts +19 -34
- package/dist/core/types.js +3 -21
- package/dist/core/types.js.map +1 -1
- package/dist/core/user-config.d.ts +27 -0
- package/dist/core/user-config.js +51 -0
- package/dist/core/user-config.js.map +1 -1
- package/dist/core/validation.d.ts +978 -2182
- package/dist/core/validation.js +242 -397
- package/dist/core/validation.js.map +1 -1
- package/dist/core/workspace-manager.d.ts +32 -31
- package/dist/core/workspace-manager.js +171 -186
- package/dist/core/workspace-manager.js.map +1 -1
- package/dist/core/workspace.d.ts +0 -5
- package/dist/core/workspace.js +33 -101
- package/dist/core/workspace.js.map +1 -1
- package/dist/lib/lib/utils.d.ts +2 -0
- package/dist/lib/lib/utils.js +6 -0
- package/dist/lib/lib/utils.js.map +1 -0
- package/dist/mcp/tools.js +13 -13
- package/dist/mcp/tools.js.map +1 -1
- package/dist/server/chrome/chrome.css +562 -0
- package/dist/server/chrome/chrome.js +540 -0
- package/dist/server/index.js +163 -10
- package/dist/server/index.js.map +1 -1
- package/dist/server/plugin-loader.d.ts +7 -0
- package/dist/server/plugin-loader.js +9 -1
- package/dist/server/plugin-loader.js.map +1 -1
- package/dist/server/routes/chat.d.ts +3 -2
- package/dist/server/routes/chat.js +67 -16
- package/dist/server/routes/chat.js.map +1 -1
- package/dist/server/routes/git-api.d.ts +9 -0
- package/dist/server/routes/git-api.js +82 -0
- package/dist/server/routes/git-api.js.map +1 -0
- package/dist/server/routes/graph-api.d.ts +2 -2
- package/dist/server/routes/graph-api.js +267 -3
- package/dist/server/routes/graph-api.js.map +1 -1
- package/dist/server/routes/permissions-api.d.ts +9 -0
- package/dist/server/routes/permissions-api.js +176 -0
- package/dist/server/routes/permissions-api.js.map +1 -0
- package/dist/server/routes/workspace-api.d.ts +9 -0
- package/dist/server/routes/workspace-api.js +283 -0
- package/dist/server/routes/workspace-api.js.map +1 -0
- package/dist/services/assets/base.d.ts +2 -2
- package/dist/services/assets/base.js +4 -4
- package/dist/services/assets/base.js.map +1 -1
- package/dist/services/assets/index.d.ts +6 -6
- package/dist/services/assets/index.js +12 -12
- package/dist/services/assets/index.js.map +1 -1
- package/dist/services/git.d.ts +35 -0
- package/dist/services/git.js +93 -0
- package/dist/services/git.js.map +1 -1
- package/dist/services/github-provisioner.js +1 -2
- package/dist/services/github-provisioner.js.map +1 -1
- package/dist/services/github-service.d.ts +99 -0
- package/dist/services/github-service.js +310 -0
- package/dist/services/github-service.js.map +1 -0
- package/dist/services/markdown.js +4 -9
- package/dist/services/markdown.js.map +1 -1
- package/dist/services/memory-service.d.ts +4 -0
- package/dist/services/memory-service.js +13 -1
- package/dist/services/memory-service.js.map +1 -1
- package/dist/services/sync/commit.d.ts +2 -0
- package/dist/services/sync/commit.js +21 -6
- package/dist/services/sync/commit.js.map +1 -1
- package/dist/services/sync/graphrag-indexer.js +2 -2
- package/dist/services/sync/graphrag-indexer.js.map +1 -1
- package/dist/utils/git.d.ts +5 -0
- package/dist/utils/git.js +10 -0
- package/dist/utils/git.js.map +1 -1
- package/dist/utils/merge-resolver.d.ts +33 -8
- package/dist/utils/merge-resolver.js +157 -55
- package/dist/utils/merge-resolver.js.map +1 -1
- package/dist/utils/preflight.d.ts +1 -1
- package/dist/utils/preflight.js +1 -1
- package/dist/web/_app/env.js +1 -0
- package/dist/web/_app/immutable/assets/0.CDbX4Cwz.css +1 -0
- package/dist/web/_app/immutable/assets/2.DRHi7ABa.css +1 -0
- package/dist/web/_app/immutable/assets/3.BJy7pVXi.css +1 -0
- package/dist/web/_app/immutable/assets/4.Ad16uh9o.css +1 -0
- package/dist/web/_app/immutable/assets/5.BhKgiXd2.css +1 -0
- package/dist/web/_app/immutable/assets/6.CeHKR5ZY.css +1 -0
- package/dist/web/_app/immutable/assets/AppShell.CJtUfb0N.css +1 -0
- package/dist/web/_app/immutable/assets/ChatPanel.RFD5GGYI.css +1 -0
- package/dist/web/_app/immutable/assets/editor.CPAf2SRV.css +1 -0
- package/dist/web/_app/immutable/chunks/3_5VIr68.js +1 -0
- package/dist/web/_app/immutable/chunks/4QY4j-jX.js +1 -0
- package/dist/web/_app/immutable/chunks/BB_5th5W.js +3383 -0
- package/dist/web/_app/immutable/chunks/BButMJ6M.js +1 -0
- package/dist/web/_app/immutable/chunks/BiubvAUI.js +2 -0
- package/dist/web/_app/immutable/chunks/CAXuTUkp.js +1 -0
- package/dist/web/_app/immutable/chunks/CQENNNNl.js +6 -0
- package/dist/web/_app/immutable/chunks/CUzqHQY_.js +1 -0
- package/dist/web/_app/immutable/chunks/CV8ganSj.js +23 -0
- package/dist/web/_app/immutable/chunks/CYrVHOHA.js +1 -0
- package/dist/web/_app/immutable/chunks/CqkleIqs.js +1 -0
- package/dist/web/_app/immutable/chunks/DKyztuK9.js +1 -0
- package/dist/web/_app/immutable/chunks/DdNsM6oV.js +64 -0
- package/dist/web/_app/immutable/chunks/Dh_H7Owr.js +18 -0
- package/dist/web/_app/immutable/chunks/DnlgZ_Tk.js +5 -0
- package/dist/web/_app/immutable/chunks/DtVH--hH.js +6 -0
- package/dist/web/_app/immutable/chunks/F20aIBpJ.js +1 -0
- package/dist/web/_app/immutable/chunks/PPVm8Dsz.js +1 -0
- package/dist/web/_app/immutable/chunks/WSUKABI_.js +1 -0
- package/dist/web/_app/immutable/chunks/YFT1281h.js +2 -0
- package/dist/web/_app/immutable/chunks/aosHekRC.js +1 -0
- package/dist/web/_app/immutable/chunks/fwnBoL5x.js +1 -0
- package/dist/web/_app/immutable/chunks/hHxe9oXh.js +1 -0
- package/dist/web/_app/immutable/entry/app.BFVUP2fV.js +2 -0
- package/dist/web/_app/immutable/entry/start.BqsXHYP6.js +1 -0
- package/dist/web/_app/immutable/nodes/0.DQ5KdeNU.js +1 -0
- package/dist/web/_app/immutable/nodes/1.DwnzyOvm.js +1 -0
- package/dist/web/_app/immutable/nodes/2.FZxrWJeh.js +1 -0
- package/dist/web/_app/immutable/nodes/3.CQVWlbKr.js +1 -0
- package/dist/web/_app/immutable/nodes/4.CEh40Z_h.js +16 -0
- package/dist/web/_app/immutable/nodes/5.D_QsCLEf.js +4 -0
- package/dist/web/_app/immutable/nodes/6.CndEQ0o5.js +2 -0
- package/dist/web/_app/version.json +1 -0
- package/dist/web/index.html +43 -0
- package/package.json +21 -3
- package/dist/agent/skills/bundled/enrich-entities.md +0 -124
- package/dist/agent/skills/bundled/gather-context.md +0 -46
|
@@ -0,0 +1,540 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Studiograph Chrome — Shared App Header & Footer
|
|
3
|
+
*
|
|
4
|
+
* Usage: Add to any app editor HTML:
|
|
5
|
+
* <link rel="stylesheet" href="/api/chrome/chrome.css">
|
|
6
|
+
* <script src="/api/chrome/chrome.js"></script>
|
|
7
|
+
*
|
|
8
|
+
* Body data attributes:
|
|
9
|
+
* data-sg-repo — repo name
|
|
10
|
+
* data-sg-entity-id — entity ID
|
|
11
|
+
* data-sg-entity-type — entity type (deck, document, etc.)
|
|
12
|
+
* data-sg-view-url — URL for the View/Present button
|
|
13
|
+
* data-sg-view-label — label for the view button (default "View")
|
|
14
|
+
*/
|
|
15
|
+
(function () {
|
|
16
|
+
'use strict';
|
|
17
|
+
|
|
18
|
+
// ── Config ──────────────────────────────────────────────
|
|
19
|
+
var body = document.body;
|
|
20
|
+
var REPO = body.dataset.sgRepo || '';
|
|
21
|
+
var WORKSPACE = body.dataset.sgWorkspace || '';
|
|
22
|
+
var ENTITY_ID = body.dataset.sgEntityId || '';
|
|
23
|
+
var VIEW_URL = body.dataset.sgViewUrl || '';
|
|
24
|
+
var VIEW_LABEL = body.dataset.sgViewLabel || 'View';
|
|
25
|
+
|
|
26
|
+
if (!REPO && !WORKSPACE) return; // no repo or workspace = don't inject chrome
|
|
27
|
+
|
|
28
|
+
var workspaceMode = !REPO && !!WORKSPACE; // footer only, no header
|
|
29
|
+
|
|
30
|
+
if (!workspaceMode) body.classList.add('sg-chrome-active');
|
|
31
|
+
|
|
32
|
+
// ── Helpers ─────────────────────────────────────────────
|
|
33
|
+
function esc(s) {
|
|
34
|
+
if (!s) return '';
|
|
35
|
+
var d = document.createElement('div');
|
|
36
|
+
d.textContent = s;
|
|
37
|
+
return d.innerHTML;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function el(tag, cls, html) {
|
|
41
|
+
var e = document.createElement(tag);
|
|
42
|
+
if (cls) e.className = cls;
|
|
43
|
+
if (html) e.innerHTML = html;
|
|
44
|
+
return e;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function titleCase(str) {
|
|
48
|
+
return str.replace(/-/g, ' ').replace(/\b\w/g, function (c) { return c.toUpperCase(); });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Returns { repo, type, entity } parts for structured tooltip rendering */
|
|
52
|
+
function friendlyParts(repo, path, entityName) {
|
|
53
|
+
var cleaned = path.replace(/\/$/, '').replace(/\/main\.md$/, '').replace(/\.md$/, '');
|
|
54
|
+
var segments = cleaned.split('/').filter(Boolean);
|
|
55
|
+
var entity = entityName || titleCase(segments[segments.length - 1]);
|
|
56
|
+
var type = segments.length > 1 ? titleCase(segments[0]) : (entityName ? titleCase(segments[0]) : null);
|
|
57
|
+
return { repo: repo, type: type, entity: entity };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function renderPathHtml(parts) {
|
|
61
|
+
var sep = '<span class="sg-path-sep">/</span>';
|
|
62
|
+
var html = '<span class="sg-path-repo">' + esc(parts.repo) + '</span>';
|
|
63
|
+
if (parts.type) html += sep + '<span class="sg-path-type">' + esc(parts.type) + '</span>';
|
|
64
|
+
html += sep + '<span class="sg-path-entity">' + esc(parts.entity) + '</span>';
|
|
65
|
+
return html;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function statusClass(status) {
|
|
69
|
+
switch (status) {
|
|
70
|
+
case 'D': return 'sg-chrome-status-deleted';
|
|
71
|
+
case 'A': case '?': return 'sg-chrome-status-added';
|
|
72
|
+
default: return 'sg-chrome-status-modified';
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ── Theme ───────────────────────────────────────────────
|
|
77
|
+
function getTheme() {
|
|
78
|
+
return document.documentElement.dataset.theme || localStorage.getItem('theme') || 'dark';
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function setTheme(theme) {
|
|
82
|
+
document.documentElement.dataset.theme = theme;
|
|
83
|
+
localStorage.setItem('theme', theme);
|
|
84
|
+
updateThemeIcon();
|
|
85
|
+
window.dispatchEvent(new CustomEvent('sg-theme-change', { detail: theme }));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function toggleTheme() {
|
|
89
|
+
setTheme(getTheme() === 'dark' ? 'light' : 'dark');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// ── Lucide SVG Icons ──────────────────────────────────
|
|
93
|
+
var SUN_ICON = '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4"/><path d="M12 2v2"/><path d="M12 20v2"/><path d="m4.93 4.93 1.41 1.41"/><path d="m17.66 17.66 1.41 1.41"/><path d="M2 12h2"/><path d="M20 12h2"/><path d="m6.34 17.66-1.41 1.41"/><path d="m19.07 4.93-1.41 1.41"/></svg>';
|
|
94
|
+
var MOON_ICON = '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"/></svg>';
|
|
95
|
+
var ARROW_DOWN = '<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5v14"/><path d="m19 12-7 7-7-7"/></svg>';
|
|
96
|
+
var ARROW_UP = '<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19V5"/><path d="m5 12 7-7 7 7"/></svg>';
|
|
97
|
+
|
|
98
|
+
var BACK_ICON = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="m15 18-6-6 6-6"/></svg>';
|
|
99
|
+
var DOTS_ICON = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="1"/><circle cx="19" cy="12" r="1"/><circle cx="5" cy="12" r="1"/></svg>';
|
|
100
|
+
var VIEW_ICON = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z"/><circle cx="12" cy="12" r="3"/></svg>';
|
|
101
|
+
var PRESENT_ICON = '<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" stroke="none"><polygon points="6 3 20 12 6 21 6 3"/></svg>';
|
|
102
|
+
|
|
103
|
+
var themeBtn;
|
|
104
|
+
function updateThemeIcon() {
|
|
105
|
+
if (themeBtn) themeBtn.innerHTML = getTheme() !== 'light' ? SUN_ICON : MOON_ICON;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// ── Build Header ────────────────────────────────────────
|
|
109
|
+
var header = el('header', 'sg-chrome-header');
|
|
110
|
+
|
|
111
|
+
// Left side: back + breadcrumb
|
|
112
|
+
var headerLeft = el('div', 'sg-chrome-header-left');
|
|
113
|
+
var backLink = el('a', 'sg-chrome-back', BACK_ICON);
|
|
114
|
+
backLink.href = '/';
|
|
115
|
+
backLink.title = 'Back to Studiograph';
|
|
116
|
+
backLink.addEventListener('click', function (e) {
|
|
117
|
+
if (window.history.length > 1) {
|
|
118
|
+
e.preventDefault();
|
|
119
|
+
window.history.back();
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
headerLeft.appendChild(backLink);
|
|
123
|
+
|
|
124
|
+
var breadcrumb = el('span', 'sg-chrome-breadcrumb',
|
|
125
|
+
esc(REPO) + '<span class="sg-chrome-sep">/</span>' + esc(ENTITY_ID));
|
|
126
|
+
headerLeft.appendChild(breadcrumb);
|
|
127
|
+
header.appendChild(headerLeft);
|
|
128
|
+
|
|
129
|
+
// Right side: save status, view, overflow
|
|
130
|
+
var headerRight = el('div', 'sg-chrome-header-right');
|
|
131
|
+
|
|
132
|
+
var saveStatusEl = el('span', 'sg-chrome-save-status');
|
|
133
|
+
saveStatusEl.id = 'sg-chrome-save-status';
|
|
134
|
+
headerRight.appendChild(saveStatusEl);
|
|
135
|
+
|
|
136
|
+
headerRight.appendChild(el('span', 'sg-chrome-divider'));
|
|
137
|
+
|
|
138
|
+
// View / Present button slot (always present for dynamic updates)
|
|
139
|
+
var viewBtnSlot = el('span', 'sg-chrome-view-slot');
|
|
140
|
+
|
|
141
|
+
// Static init for standalone mode (non-SPA)
|
|
142
|
+
if (VIEW_URL) {
|
|
143
|
+
var viewIcon = VIEW_LABEL === 'Present' ? PRESENT_ICON : VIEW_ICON;
|
|
144
|
+
var viewBtn = el('button', 'sg-chrome-btn sg-chrome-btn-accent',
|
|
145
|
+
viewIcon + '<span>' + esc(VIEW_LABEL) + '</span>');
|
|
146
|
+
viewBtn.title = VIEW_LABEL;
|
|
147
|
+
viewBtn.addEventListener('click', function () {
|
|
148
|
+
window.open(VIEW_URL, '_blank');
|
|
149
|
+
});
|
|
150
|
+
viewBtnSlot.appendChild(viewBtn);
|
|
151
|
+
}
|
|
152
|
+
headerRight.appendChild(viewBtnSlot);
|
|
153
|
+
headerRight.appendChild(el('span', 'sg-chrome-divider sg-chrome-view-divider'));
|
|
154
|
+
|
|
155
|
+
// Overflow menu
|
|
156
|
+
var overflowWrapper = el('div', 'sg-chrome-overflow-wrapper');
|
|
157
|
+
var overflowBtn = el('button', 'sg-chrome-overflow-btn', DOTS_ICON);
|
|
158
|
+
overflowBtn.title = 'More actions';
|
|
159
|
+
overflowWrapper.appendChild(overflowBtn);
|
|
160
|
+
|
|
161
|
+
var overflowMenu = el('div', 'sg-chrome-overflow-menu');
|
|
162
|
+
overflowWrapper.appendChild(overflowMenu);
|
|
163
|
+
headerRight.appendChild(overflowWrapper);
|
|
164
|
+
|
|
165
|
+
header.appendChild(headerRight);
|
|
166
|
+
|
|
167
|
+
// Overflow menu open/close
|
|
168
|
+
overflowBtn.addEventListener('click', function (e) {
|
|
169
|
+
e.stopPropagation();
|
|
170
|
+
overflowMenu.classList.toggle('open');
|
|
171
|
+
});
|
|
172
|
+
document.addEventListener('click', function () {
|
|
173
|
+
overflowMenu.classList.remove('open');
|
|
174
|
+
});
|
|
175
|
+
document.addEventListener('keydown', function (e) {
|
|
176
|
+
if (e.key === 'Escape') overflowMenu.classList.remove('open');
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
// ── Build Footer (matches StatusBar.svelte) ────────────
|
|
180
|
+
var footer = el('footer', 'sg-chrome-footer');
|
|
181
|
+
|
|
182
|
+
// Left: theme toggle + workspace stats
|
|
183
|
+
var footerLeft = el('div', 'sg-chrome-footer-left');
|
|
184
|
+
|
|
185
|
+
themeBtn = el('button', 'sg-chrome-theme-toggle');
|
|
186
|
+
themeBtn.title = 'Toggle theme';
|
|
187
|
+
themeBtn.addEventListener('click', toggleTheme);
|
|
188
|
+
footerLeft.appendChild(themeBtn);
|
|
189
|
+
updateThemeIcon();
|
|
190
|
+
|
|
191
|
+
var statsEl = el('span', 'sg-chrome-footer-stats');
|
|
192
|
+
footerLeft.appendChild(statsEl);
|
|
193
|
+
footer.appendChild(footerLeft);
|
|
194
|
+
|
|
195
|
+
// Center: Revert + Commit (absolute centered)
|
|
196
|
+
var footerCenter = el('div', 'sg-chrome-footer-center');
|
|
197
|
+
|
|
198
|
+
var revertBtn = el('button', 'sg-chrome-revert-btn', 'Revert');
|
|
199
|
+
revertBtn.title = 'Discard all unpublished changes';
|
|
200
|
+
revertBtn.hidden = true;
|
|
201
|
+
|
|
202
|
+
var commitAnchor = el('div', 'sg-chrome-tooltip-anchor');
|
|
203
|
+
var commitBtn = el('button', 'sg-chrome-commit-btn', '<span>Commit</span>');
|
|
204
|
+
commitBtn.title = 'Commit changes';
|
|
205
|
+
commitBtn.hidden = true;
|
|
206
|
+
var commitTooltip = el('div', 'sg-chrome-tooltip-popup');
|
|
207
|
+
commitAnchor.appendChild(commitBtn);
|
|
208
|
+
commitAnchor.appendChild(commitTooltip);
|
|
209
|
+
|
|
210
|
+
footerCenter.appendChild(revertBtn);
|
|
211
|
+
footerCenter.appendChild(commitAnchor);
|
|
212
|
+
footer.appendChild(footerCenter);
|
|
213
|
+
|
|
214
|
+
// Right: Pull + Push
|
|
215
|
+
var footerRight = el('div', 'sg-chrome-footer-right');
|
|
216
|
+
|
|
217
|
+
var pullBtn = el('button', 'sg-chrome-sync-btn',
|
|
218
|
+
ARROW_DOWN + '<span>Pull</span>');
|
|
219
|
+
pullBtn.title = 'Pull latest changes';
|
|
220
|
+
|
|
221
|
+
var pushAnchor = el('div', 'sg-chrome-tooltip-anchor');
|
|
222
|
+
var pushBtn = el('button', 'sg-chrome-sync-btn',
|
|
223
|
+
ARROW_UP + '<span>Push</span>');
|
|
224
|
+
pushBtn.title = 'Push changes to remote';
|
|
225
|
+
var pushTooltip = el('div', 'sg-chrome-tooltip-popup sg-chrome-tooltip-popup-right');
|
|
226
|
+
pushAnchor.appendChild(pushBtn);
|
|
227
|
+
pushAnchor.appendChild(pushTooltip);
|
|
228
|
+
|
|
229
|
+
footerRight.appendChild(pullBtn);
|
|
230
|
+
footerRight.appendChild(pushAnchor);
|
|
231
|
+
footer.appendChild(footerRight);
|
|
232
|
+
|
|
233
|
+
// ── Inject into DOM ─────────────────────────────────────
|
|
234
|
+
if (!workspaceMode) {
|
|
235
|
+
body.insertBefore(header, body.firstChild);
|
|
236
|
+
}
|
|
237
|
+
var footerMount = document.getElementById('sg-chrome-footer');
|
|
238
|
+
if (footerMount) {
|
|
239
|
+
footerMount.appendChild(footer);
|
|
240
|
+
} else if (workspaceMode) {
|
|
241
|
+
// SvelteKit SPA: mount point doesn't exist yet, wait for it
|
|
242
|
+
var observer = new MutationObserver(function (_mutations, obs) {
|
|
243
|
+
var el = document.getElementById('sg-chrome-footer');
|
|
244
|
+
if (el) {
|
|
245
|
+
el.appendChild(footer);
|
|
246
|
+
obs.disconnect();
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
observer.observe(body, { childList: true, subtree: true });
|
|
250
|
+
} else {
|
|
251
|
+
body.appendChild(footer);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// ── Git State ───────────────────────────────────────────
|
|
255
|
+
var gitState = {
|
|
256
|
+
repoStatuses: [], // [{repo, changes: [{path, status}]}]
|
|
257
|
+
totalChanges: 0,
|
|
258
|
+
ahead: 0,
|
|
259
|
+
behind: 0,
|
|
260
|
+
stats: { entities: 0, links: 0 }
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
function updateFooterUI() {
|
|
264
|
+
// ── Stats ──
|
|
265
|
+
if (gitState.stats.entities > 0) {
|
|
266
|
+
var e = gitState.stats.entities;
|
|
267
|
+
var l = gitState.stats.links;
|
|
268
|
+
statsEl.innerHTML =
|
|
269
|
+
'<span class="sg-chrome-stat">' + e.toLocaleString() + (e === 1 ? ' entity' : ' entities') + '</span>' +
|
|
270
|
+
'<span class="sg-chrome-stat-sep">|</span>' +
|
|
271
|
+
'<span class="sg-chrome-stat">' + l.toLocaleString() + (l === 1 ? ' link' : ' links') + '</span>';
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// ── Commit / Revert ──
|
|
275
|
+
if (gitState.totalChanges > 0) {
|
|
276
|
+
revertBtn.hidden = false;
|
|
277
|
+
commitBtn.hidden = false;
|
|
278
|
+
commitBtn.innerHTML =
|
|
279
|
+
'<span>Commit</span><span class="sg-chrome-sync-count">' + gitState.totalChanges + '</span>';
|
|
280
|
+
// Tooltip: list changed files
|
|
281
|
+
var tooltipHtml = '';
|
|
282
|
+
gitState.repoStatuses.forEach(function (rs) {
|
|
283
|
+
rs.changes.forEach(function (change) {
|
|
284
|
+
var parts = friendlyParts(rs.repo, change.path, change.entityName);
|
|
285
|
+
tooltipHtml +=
|
|
286
|
+
'<div class="sg-chrome-tooltip-file">' +
|
|
287
|
+
'<span class="sg-chrome-tooltip-dot ' + statusClass(change.status) + '"></span>' +
|
|
288
|
+
renderPathHtml(parts) +
|
|
289
|
+
'</div>';
|
|
290
|
+
});
|
|
291
|
+
});
|
|
292
|
+
commitTooltip.innerHTML = tooltipHtml;
|
|
293
|
+
} else {
|
|
294
|
+
revertBtn.hidden = true;
|
|
295
|
+
commitBtn.hidden = true;
|
|
296
|
+
commitTooltip.innerHTML = '';
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// ── Pull ──
|
|
300
|
+
if (gitState.behind > 0) {
|
|
301
|
+
pullBtn.hidden = false;
|
|
302
|
+
pullBtn.className = 'sg-chrome-sync-btn sg-chrome-sync-btn-accent';
|
|
303
|
+
pullBtn.innerHTML =
|
|
304
|
+
ARROW_DOWN + '<span>Pull</span><span class="sg-chrome-sync-count">' + gitState.behind + '</span>';
|
|
305
|
+
} else {
|
|
306
|
+
pullBtn.hidden = true;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// ── Push ──
|
|
310
|
+
if (gitState.ahead > 0) {
|
|
311
|
+
pushBtn.hidden = false;
|
|
312
|
+
pushBtn.className = 'sg-chrome-sync-btn sg-chrome-sync-btn-accent';
|
|
313
|
+
pushBtn.innerHTML =
|
|
314
|
+
ARROW_UP + '<span>Push</span><span class="sg-chrome-sync-count">' + gitState.ahead + '</span>';
|
|
315
|
+
pushTooltip.innerHTML =
|
|
316
|
+
'<div class="sg-chrome-tooltip-file">' +
|
|
317
|
+
gitState.ahead + ' commit' + (gitState.ahead === 1 ? '' : 's') + ' ahead of remote</div>';
|
|
318
|
+
} else {
|
|
319
|
+
pushBtn.hidden = true;
|
|
320
|
+
pushTooltip.innerHTML = '';
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// ── Git Polling ─────────────────────────────────────────
|
|
325
|
+
function fetchGitStatus() {
|
|
326
|
+
return Promise.all([
|
|
327
|
+
fetch('/api/status').then(function (r) { return r.json(); }).catch(function () { return []; }),
|
|
328
|
+
fetch('/api/sync-status').then(function (r) { return r.json(); }).catch(function () { return { ahead: 0, behind: 0 }; })
|
|
329
|
+
]).then(function (results) {
|
|
330
|
+
gitState.repoStatuses = results[0];
|
|
331
|
+
gitState.totalChanges = results[0].reduce(function (sum, r) { return sum + r.changes.length; }, 0);
|
|
332
|
+
gitState.ahead = results[1].ahead || 0;
|
|
333
|
+
gitState.behind = results[1].behind || 0;
|
|
334
|
+
updateFooterUI();
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
function fetchStats() {
|
|
339
|
+
fetch('/api/stats').then(function (r) { return r.json(); }).then(function (data) {
|
|
340
|
+
gitState.stats = data;
|
|
341
|
+
updateFooterUI();
|
|
342
|
+
}).catch(function () { /* silently fail */ });
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Initial fetch
|
|
346
|
+
fetchGitStatus();
|
|
347
|
+
fetchStats();
|
|
348
|
+
|
|
349
|
+
// Poll git status every 15s (stats less frequently — every 60s)
|
|
350
|
+
setInterval(fetchGitStatus, 15000);
|
|
351
|
+
setInterval(fetchStats, 60000);
|
|
352
|
+
|
|
353
|
+
// Sync with Svelte git store — refresh immediately when it updates
|
|
354
|
+
window.addEventListener('studiograph:git-refresh', function () {
|
|
355
|
+
fetchGitStatus();
|
|
356
|
+
fetchStats();
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
// ── Git Operations ──────────────────────────────────────
|
|
360
|
+
commitBtn.addEventListener('click', function () {
|
|
361
|
+
if (commitBtn.disabled) return;
|
|
362
|
+
// If a commit handler is registered, delegate to it
|
|
363
|
+
if (listeners['commit'] && listeners['commit'].length > 0) {
|
|
364
|
+
emitEvent('commit');
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
commitBtn.disabled = true;
|
|
368
|
+
commitBtn.innerHTML = '<span>Committing\u2026</span>';
|
|
369
|
+
fetch('/api/commit', {
|
|
370
|
+
method: 'POST',
|
|
371
|
+
headers: { 'Content-Type': 'application/json' },
|
|
372
|
+
body: JSON.stringify({ message: 'Update from editor' })
|
|
373
|
+
})
|
|
374
|
+
.then(function (res) { return res.json(); })
|
|
375
|
+
.then(function () {
|
|
376
|
+
commitBtn.disabled = false;
|
|
377
|
+
fetchGitStatus();
|
|
378
|
+
fetchStats();
|
|
379
|
+
})
|
|
380
|
+
.catch(function () {
|
|
381
|
+
commitBtn.disabled = false;
|
|
382
|
+
alert('Commit failed: network error');
|
|
383
|
+
fetchGitStatus();
|
|
384
|
+
});
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
revertBtn.addEventListener('click', function () {
|
|
388
|
+
emitEvent('revert:open');
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
pushBtn.addEventListener('click', function () {
|
|
392
|
+
if (pushBtn.disabled) return;
|
|
393
|
+
pushBtn.disabled = true;
|
|
394
|
+
var prevHtml = pushBtn.innerHTML;
|
|
395
|
+
pushBtn.innerHTML = ARROW_UP + '<span>Pushing\u2026</span>';
|
|
396
|
+
fetch('/api/push', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: '{}' })
|
|
397
|
+
.then(function () { pushBtn.disabled = false; fetchGitStatus(); })
|
|
398
|
+
.catch(function () { pushBtn.disabled = false; fetchGitStatus(); });
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
pullBtn.addEventListener('click', function () {
|
|
402
|
+
if (pullBtn.disabled) return;
|
|
403
|
+
pullBtn.disabled = true;
|
|
404
|
+
pullBtn.innerHTML = ARROW_DOWN + '<span>Pulling\u2026</span>';
|
|
405
|
+
emitEvent('pull:start');
|
|
406
|
+
fetch('/api/pull', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: '{}' })
|
|
407
|
+
.then(function () {
|
|
408
|
+
pullBtn.disabled = false;
|
|
409
|
+
fetchGitStatus();
|
|
410
|
+
fetchStats();
|
|
411
|
+
emitEvent('pull');
|
|
412
|
+
})
|
|
413
|
+
.catch(function () { pullBtn.disabled = false; fetchGitStatus(); emitEvent('pull'); });
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
// ── Event System ────────────────────────────────────────
|
|
417
|
+
var listeners = {};
|
|
418
|
+
|
|
419
|
+
function emitEvent(name, data) {
|
|
420
|
+
(listeners[name] || []).forEach(function (fn) {
|
|
421
|
+
try { fn(data); } catch (e) { console.error('StudioChrome event error:', e); }
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// ── Public API ──────────────────────────────────────────
|
|
426
|
+
window.StudioChrome = {
|
|
427
|
+
on: function (event, fn) {
|
|
428
|
+
if (!listeners[event]) listeners[event] = [];
|
|
429
|
+
listeners[event].push(fn);
|
|
430
|
+
},
|
|
431
|
+
|
|
432
|
+
off: function (event, fn) {
|
|
433
|
+
if (fn && listeners[event]) {
|
|
434
|
+
listeners[event] = listeners[event].filter(function (f) { return f !== fn; });
|
|
435
|
+
if (listeners[event].length === 0) delete listeners[event];
|
|
436
|
+
} else {
|
|
437
|
+
delete listeners[event];
|
|
438
|
+
}
|
|
439
|
+
},
|
|
440
|
+
|
|
441
|
+
/** Emit a mutation event (called by web UI after agent updates an entity) */
|
|
442
|
+
emitMutation: function (detail) {
|
|
443
|
+
emitEvent('mutation', detail);
|
|
444
|
+
},
|
|
445
|
+
|
|
446
|
+
/** Move footer into #sg-chrome-footer mount point (call after SPA navigation) */
|
|
447
|
+
mountFooter: function () {
|
|
448
|
+
var target = document.getElementById('sg-chrome-footer');
|
|
449
|
+
if (target && footer.parentNode !== target) {
|
|
450
|
+
target.appendChild(footer);
|
|
451
|
+
}
|
|
452
|
+
},
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* Set save status indicator.
|
|
456
|
+
* @param {string} text - Status text to display
|
|
457
|
+
* @param {string} [type] - 'saving'|'saved'|'error'|'' — controls styling
|
|
458
|
+
*/
|
|
459
|
+
setSaveStatus: function (text, type) {
|
|
460
|
+
type = type || '';
|
|
461
|
+
saveStatusEl.textContent = text || '';
|
|
462
|
+
saveStatusEl.className = 'sg-chrome-save-status' + (type === 'error' ? ' sg-chrome-save-error' : '');
|
|
463
|
+
},
|
|
464
|
+
|
|
465
|
+
refreshStatus: function () {
|
|
466
|
+
fetchGitStatus();
|
|
467
|
+
},
|
|
468
|
+
|
|
469
|
+
refreshStats: function () {
|
|
470
|
+
fetchStats();
|
|
471
|
+
},
|
|
472
|
+
|
|
473
|
+
addMenuItem: function (opts) {
|
|
474
|
+
var btn = el('button', 'sg-chrome-menu-item' + (opts.danger ? ' sg-chrome-danger' : ''),
|
|
475
|
+
(opts.icon || '') + ' ' + esc(opts.label));
|
|
476
|
+
btn.addEventListener('click', function () {
|
|
477
|
+
overflowMenu.classList.remove('open');
|
|
478
|
+
if (opts.onClick) opts.onClick();
|
|
479
|
+
});
|
|
480
|
+
overflowMenu.appendChild(btn);
|
|
481
|
+
return btn;
|
|
482
|
+
},
|
|
483
|
+
|
|
484
|
+
addMenuSeparator: function () {
|
|
485
|
+
overflowMenu.appendChild(el('div', 'sg-chrome-menu-sep'));
|
|
486
|
+
},
|
|
487
|
+
|
|
488
|
+
/** Mount header into a target element (for SPA embedding) */
|
|
489
|
+
mountHeader: function (target) {
|
|
490
|
+
if (typeof target === 'string') target = document.getElementById(target);
|
|
491
|
+
if (target && header) {
|
|
492
|
+
// Clear overflow menu items from previous editor before mounting
|
|
493
|
+
overflowMenu.innerHTML = '';
|
|
494
|
+
target.appendChild(header);
|
|
495
|
+
}
|
|
496
|
+
},
|
|
497
|
+
|
|
498
|
+
/** Remove header from DOM (keeps element alive for re-mount) */
|
|
499
|
+
unmountHeader: function () {
|
|
500
|
+
if (header && header.parentNode) header.parentNode.removeChild(header);
|
|
501
|
+
},
|
|
502
|
+
|
|
503
|
+
/** Update breadcrumb text (for SPA embedding where repo/entity change) */
|
|
504
|
+
updateBreadcrumb: function (repo, entityId) {
|
|
505
|
+
breadcrumb.innerHTML = esc(repo) + '<span class="sg-chrome-sep">/</span>' + esc(entityId || '');
|
|
506
|
+
},
|
|
507
|
+
|
|
508
|
+
/** Set dynamic View/Present button (for SPA embedding) */
|
|
509
|
+
setViewAction: function (url, label) {
|
|
510
|
+
if (!url) return;
|
|
511
|
+
label = label || 'View';
|
|
512
|
+
var icon = label === 'Present' ? PRESENT_ICON : VIEW_ICON;
|
|
513
|
+
viewBtnSlot.innerHTML = '';
|
|
514
|
+
var btn = el('button', 'sg-chrome-btn sg-chrome-btn-accent',
|
|
515
|
+
icon + '<span>' + esc(label) + '</span>');
|
|
516
|
+
btn.title = label;
|
|
517
|
+
btn.addEventListener('click', function () {
|
|
518
|
+
window.open(url, '_blank');
|
|
519
|
+
});
|
|
520
|
+
viewBtnSlot.appendChild(btn);
|
|
521
|
+
var divider = header.querySelector('.sg-chrome-view-divider');
|
|
522
|
+
if (divider) divider.style.display = '';
|
|
523
|
+
},
|
|
524
|
+
|
|
525
|
+
/** Clear View/Present button */
|
|
526
|
+
clearViewAction: function () {
|
|
527
|
+
viewBtnSlot.innerHTML = '';
|
|
528
|
+
var divider = header.querySelector('.sg-chrome-view-divider');
|
|
529
|
+
if (divider) divider.style.display = 'none';
|
|
530
|
+
},
|
|
531
|
+
|
|
532
|
+
/** No-op stubs for backward compatibility with app editors */
|
|
533
|
+
setSaveHandlers: function () {},
|
|
534
|
+
clearSaveHandlers: function () {
|
|
535
|
+
saveStatusEl.textContent = '';
|
|
536
|
+
saveStatusEl.className = 'sg-chrome-save-status';
|
|
537
|
+
},
|
|
538
|
+
isDirty: function () { return false; },
|
|
539
|
+
};
|
|
540
|
+
})();
|