gitmaps 1.0.0 → 1.1.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 +265 -122
- package/app/[...slug]/page.client.tsx +1 -0
- package/app/[...slug]/page.tsx +6 -0
- package/app/[owner]/[repo]/page.client.tsx +5 -0
- package/app/[slug]/page.client.tsx +5 -0
- package/app/analytics.db +0 -0
- package/app/api/analytics/route.ts +64 -0
- package/app/api/auth/positions/route.ts +95 -33
- package/app/api/build-info/route.ts +19 -0
- package/app/api/chat/route.ts +13 -2
- package/app/api/manifest.json/route.ts +20 -0
- package/app/api/og-image/route.ts +14 -0
- package/app/api/pwa-icon/route.ts +14 -0
- package/app/api/repo/clone-stream/route.ts +20 -12
- package/app/api/repo/file-content/route.ts +73 -20
- package/app/api/repo/imports/route.ts +21 -3
- package/app/api/repo/list/route.ts +30 -0
- package/app/api/repo/load/route.test.ts +62 -0
- package/app/api/repo/load/route.ts +41 -1
- package/app/api/repo/pdf-thumb/route.ts +127 -0
- package/app/api/repo/resolve-slug/route.ts +51 -0
- package/app/api/repo/tree/route.ts +188 -104
- package/app/api/repo/upload/route.ts +6 -9
- package/app/api/sw.js/route.ts +70 -0
- package/app/api/version/route.ts +26 -0
- package/app/galaxy-canvas/page.client.tsx +2 -0
- package/app/galaxy-canvas/page.tsx +5 -0
- package/app/globals.css +5844 -4694
- package/app/icon.png +0 -0
- package/app/layout.tsx +1284 -467
- package/app/lib/auto-arrange.test.ts +158 -0
- package/app/lib/auto-arrange.ts +147 -0
- package/app/lib/canvas-export.ts +358 -358
- package/app/lib/canvas-text.ts +4 -72
- package/app/lib/canvas.ts +625 -564
- package/app/lib/card-arrangement.ts +21 -7
- package/app/lib/card-context-menu.tsx +2 -2
- package/app/lib/card-groups.ts +9 -2
- package/app/lib/cards.tsx +1361 -914
- package/app/lib/chat.tsx +65 -9
- package/app/lib/code-editor.ts +86 -2
- package/app/lib/connections.tsx +34 -43
- package/app/lib/context.test.ts +32 -0
- package/app/lib/context.ts +19 -3
- package/app/lib/cursor-sharing.ts +34 -0
- package/app/lib/events.tsx +76 -73
- package/app/lib/export-canvas.ts +287 -0
- package/app/lib/file-card-plugin.ts +148 -134
- package/app/lib/file-modal.tsx +49 -0
- package/app/lib/file-preview.ts +486 -400
- package/app/lib/github-import.test.ts +424 -0
- package/app/lib/global-search.ts +48 -27
- package/app/lib/initial-route-hydration.test.ts +283 -0
- package/app/lib/initial-route-hydration.ts +202 -0
- package/app/lib/landing-reset.test.ts +99 -0
- package/app/lib/landing-reset.ts +106 -0
- package/app/lib/landing-shell.test.ts +75 -0
- package/app/lib/large-repo-optimization.ts +37 -0
- package/app/lib/layers.tsx +17 -18
- package/app/lib/layout-snapshots.ts +320 -0
- package/app/lib/loading.test.ts +69 -0
- package/app/lib/loading.tsx +160 -45
- package/app/lib/mount-cleanup.test.ts +52 -0
- package/app/lib/mount-cleanup.ts +34 -0
- package/app/lib/mount-init.test.ts +123 -0
- package/app/lib/mount-init.ts +107 -0
- package/app/lib/mount-lifecycle.test.ts +39 -0
- package/app/lib/mount-lifecycle.ts +12 -0
- package/app/lib/mount-route-wiring.test.ts +87 -0
- package/app/lib/mount-route-wiring.ts +84 -0
- package/app/lib/multi-repo.ts +14 -0
- package/app/lib/onboarding-tutorial.ts +278 -0
- package/app/lib/perf-overlay.ts +78 -0
- package/app/lib/positions.ts +191 -122
- package/app/lib/recent-commits.test.ts +869 -0
- package/app/lib/recent-commits.ts +227 -0
- package/app/lib/repo-handoff.test.ts +23 -0
- package/app/lib/repo-handoff.ts +16 -0
- package/app/lib/repo-progressive.ts +119 -0
- package/app/lib/repo-select.test.ts +61 -0
- package/app/lib/repo-select.ts +74 -0
- package/app/lib/repo.tsx +1383 -977
- package/app/lib/role.ts +228 -0
- package/app/lib/route-catchall.test.ts +27 -0
- package/app/lib/route-repo-entry.test.ts +95 -0
- package/app/lib/route-repo-entry.ts +36 -0
- package/app/lib/router-contract.test.ts +22 -0
- package/app/lib/router-contract.ts +19 -0
- package/app/lib/shared-layout.test.ts +86 -0
- package/app/lib/shared-layout.ts +82 -0
- package/app/lib/shortcuts-panel.ts +2 -0
- package/app/lib/status-bar.test.ts +118 -0
- package/app/lib/status-bar.ts +365 -128
- package/app/lib/sync-controls.test.ts +43 -0
- package/app/lib/sync-controls.tsx +303 -0
- package/app/lib/test-dom.ts +145 -0
- package/app/lib/test-fixtures/router-contract/[...slug]/page.tsx +3 -0
- package/app/lib/test-fixtures/router-contract/api/health/route.ts +3 -0
- package/app/lib/test-fixtures/router-contract/api/version/route.ts +3 -0
- package/app/lib/test-fixtures/router-contract/galaxy-canvas/page.tsx +3 -0
- package/app/lib/test-fixtures/router-contract/page.tsx +3 -0
- package/app/lib/transclusion-smoke.test.ts +163 -0
- package/app/lib/tutorial.ts +301 -0
- package/app/lib/version.ts +93 -0
- package/app/lib/viewport-culling.ts +740 -728
- package/app/lib/virtual-files.ts +456 -0
- package/app/lib/webgl-text.ts +189 -0
- package/app/lib/{galaxydraw-bridge.ts → xydraw-bridge.ts} +485 -477
- package/app/lib/{galaxydraw.test.ts → xydraw.test.ts} +228 -229
- package/app/og-image.png +0 -0
- package/app/page.client.tsx +70 -215
- package/app/page.tsx +27 -92
- package/app/state/machine.js +13 -0
- package/banner.png +0 -0
- package/package.json +17 -8
- package/server.ts +11 -1
- package/app/api/connections/route.ts +0 -72
- package/app/api/positions/route.ts +0 -80
- package/app/api/repo/browse/route.ts +0 -55
- package/app/lib/pr-review.ts +0 -374
- package/packages/galaxydraw/README.md +0 -296
- package/packages/galaxydraw/banner.png +0 -0
- package/packages/galaxydraw/demo/build-static.ts +0 -100
- package/packages/galaxydraw/demo/client.ts +0 -154
- package/packages/galaxydraw/demo/dist/client.js +0 -8
- package/packages/galaxydraw/demo/index.html +0 -256
- package/packages/galaxydraw/demo/server.ts +0 -96
- package/packages/galaxydraw/dist/index.js +0 -984
- package/packages/galaxydraw/dist/index.js.map +0 -16
- package/packages/galaxydraw/node_modules/.bin/tsc.bunx +0 -0
- package/packages/galaxydraw/node_modules/.bin/tsc.exe +0 -0
- package/packages/galaxydraw/node_modules/.bin/tsserver.bunx +0 -0
- package/packages/galaxydraw/node_modules/.bin/tsserver.exe +0 -0
- package/packages/galaxydraw/package.json +0 -49
- package/packages/galaxydraw/perf.test.ts +0 -284
- package/packages/galaxydraw/src/core/cards.ts +0 -435
- package/packages/galaxydraw/src/core/engine.ts +0 -339
- package/packages/galaxydraw/src/core/events.ts +0 -81
- package/packages/galaxydraw/src/core/layout.ts +0 -136
- package/packages/galaxydraw/src/core/minimap.ts +0 -216
- package/packages/galaxydraw/src/core/state.ts +0 -177
- package/packages/galaxydraw/src/core/viewport.ts +0 -106
- package/packages/galaxydraw/src/galaxydraw.css +0 -166
- package/packages/galaxydraw/src/index.ts +0 -40
- package/packages/galaxydraw/tsconfig.json +0 -30
package/app/layout.tsx
CHANGED
|
@@ -5,489 +5,1306 @@
|
|
|
5
5
|
* All interactivity is in page.client.tsx.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import path from 'path';
|
|
9
|
+
|
|
10
|
+
function getBuildInfo() {
|
|
11
|
+
const repoRoot = path.resolve(import.meta.dir, '..');
|
|
12
|
+
const commitProc = Bun.spawnSync(['git', 'rev-parse', '--short', 'HEAD'], {
|
|
13
|
+
cwd: repoRoot,
|
|
14
|
+
stdout: 'pipe',
|
|
15
|
+
stderr: 'pipe',
|
|
16
|
+
});
|
|
17
|
+
const dateProc = Bun.spawnSync(['git', 'log', '-1', '--format=%cs'], {
|
|
18
|
+
cwd: repoRoot,
|
|
19
|
+
stdout: 'pipe',
|
|
20
|
+
stderr: 'pipe',
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
commit: commitProc.exitCode === 0 ? commitProc.stdout.toString().trim() : 'unknown',
|
|
25
|
+
date: dateProc.exitCode === 0 ? dateProc.stdout.toString().trim() : '',
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
8
29
|
export default function RootLayout({ children }: { children: any }) {
|
|
9
|
-
|
|
10
|
-
<html lang="en">
|
|
11
|
-
<head>
|
|
12
|
-
<meta charSet="UTF-8" />
|
|
13
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
|
14
|
-
<meta name="description" content="GitMaps - See your codebase in a new dimension. Spatial code explorer." />
|
|
15
|
-
<title>GitMaps — Spatial Code Explorer</title>
|
|
16
|
-
<link rel="icon" href="data:," />
|
|
17
|
-
<link
|
|
18
|
-
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap"
|
|
19
|
-
rel="stylesheet"
|
|
20
|
-
/>
|
|
21
|
-
</head>
|
|
22
|
-
<body>
|
|
23
|
-
<div id="app">
|
|
24
|
-
<nav className="sidebar">
|
|
25
|
-
<div className="sidebar-header">
|
|
26
|
-
<div className="logo">
|
|
27
|
-
<svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" strokeWidth="2">
|
|
28
|
-
<circle cx="12" cy="12" r="3" />
|
|
29
|
-
<circle cx="12" cy="4" r="1.5" />
|
|
30
|
-
<circle cx="12" cy="20" r="1.5" />
|
|
31
|
-
<circle cx="4" cy="8" r="1.5" />
|
|
32
|
-
<circle cx="20" cy="8" r="1.5" />
|
|
33
|
-
<circle cx="4" cy="16" r="1.5" />
|
|
34
|
-
<circle cx="20" cy="16" r="1.5" />
|
|
35
|
-
<path d="M12 7v2M12 15v2M8.5 9.5l-2.5-1M15.5 9.5l2.5-1M8.5 14.5l-2.5 1M15.5 14.5l2.5 1" />
|
|
36
|
-
</svg>
|
|
37
|
-
<span>GitMaps</span>
|
|
38
|
-
</div>
|
|
39
|
-
</div>
|
|
30
|
+
const build = getBuildInfo();
|
|
40
31
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
32
|
+
return (
|
|
33
|
+
<html lang="en">
|
|
34
|
+
<head>
|
|
35
|
+
<meta charSet="UTF-8" />
|
|
36
|
+
<meta
|
|
37
|
+
name="viewport"
|
|
38
|
+
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
|
|
39
|
+
/>
|
|
40
|
+
<meta
|
|
41
|
+
name="description"
|
|
42
|
+
content="Transcend the file tree. GitMaps renders knowledge on an infinite canvas — with layers, time-travel, and a minimap to never lose context."
|
|
43
|
+
/>
|
|
44
|
+
<meta property="og:title" content="GitMaps — Spatial Code Explorer" />
|
|
45
|
+
<meta
|
|
46
|
+
property="og:description"
|
|
47
|
+
content="Every file in your repo. One infinite canvas. Layers, git time-travel, inline diffs."
|
|
48
|
+
/>
|
|
49
|
+
<meta property="og:image" content="https://gitmaps.xyz/api/og-image" />
|
|
50
|
+
<meta property="og:url" content="https://gitmaps.xyz" />
|
|
51
|
+
<meta name="twitter:card" content="summary_large_image" />
|
|
52
|
+
<meta name="twitter:image" content="https://gitmaps.xyz/api/og-image" />
|
|
53
|
+
<title>GitMaps — Spatial Code Explorer</title>
|
|
54
|
+
<link rel="icon" type="image/png" href="/api/pwa-icon" />
|
|
55
|
+
<link rel="manifest" href="/api/manifest.json" />
|
|
56
|
+
<meta name="theme-color" content="#7c3aed" />
|
|
57
|
+
<script
|
|
58
|
+
dangerouslySetInnerHTML={{
|
|
59
|
+
__html: `window.__GITMAPS_BUILD__ = ${JSON.stringify(build)};`,
|
|
60
|
+
}}
|
|
61
|
+
/>
|
|
62
|
+
<link
|
|
63
|
+
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap"
|
|
64
|
+
rel="stylesheet"
|
|
65
|
+
/>
|
|
66
|
+
</head>
|
|
67
|
+
<body>
|
|
68
|
+
{/* Mobile gate — canvas needs a real screen */}
|
|
69
|
+
<div
|
|
70
|
+
id="mobileGate"
|
|
71
|
+
style={{
|
|
72
|
+
display: "none",
|
|
73
|
+
position: "fixed",
|
|
74
|
+
inset: 0,
|
|
75
|
+
zIndex: 99999,
|
|
76
|
+
background: "#0a0a1a",
|
|
77
|
+
color: "#e2e8f0",
|
|
78
|
+
flexDirection: "column",
|
|
79
|
+
alignItems: "center",
|
|
80
|
+
justifyContent: "center",
|
|
81
|
+
padding: "2rem",
|
|
82
|
+
textAlign: "center",
|
|
83
|
+
fontFamily: "Inter, sans-serif",
|
|
84
|
+
gap: "1rem",
|
|
85
|
+
}}
|
|
86
|
+
>
|
|
87
|
+
<div style={{ fontSize: "3rem" }}>🗺️</div>
|
|
88
|
+
<h2 style={{ margin: 0, fontSize: "1.5rem", fontWeight: 600 }}>
|
|
89
|
+
GitMaps needs a bigger screen
|
|
90
|
+
</h2>
|
|
91
|
+
<p
|
|
92
|
+
style={{
|
|
93
|
+
margin: 0,
|
|
94
|
+
color: "#94a3b8",
|
|
95
|
+
maxWidth: "320px",
|
|
96
|
+
lineHeight: 1.6,
|
|
97
|
+
}}
|
|
98
|
+
>
|
|
99
|
+
The infinite canvas works best on desktop or tablet. Come back on a
|
|
100
|
+
larger screen to explore your repos spatially.
|
|
101
|
+
</p>
|
|
102
|
+
<a
|
|
103
|
+
href="https://gitmaps.xyz"
|
|
104
|
+
style={{
|
|
105
|
+
marginTop: "0.5rem",
|
|
106
|
+
padding: "0.75rem 1.5rem",
|
|
107
|
+
background: "#7c3aed",
|
|
108
|
+
color: "white",
|
|
109
|
+
borderRadius: "8px",
|
|
110
|
+
textDecoration: "none",
|
|
111
|
+
fontWeight: 500,
|
|
112
|
+
}}
|
|
113
|
+
>
|
|
114
|
+
Learn more
|
|
115
|
+
</a>
|
|
116
|
+
<button
|
|
117
|
+
id="mobileGateDismiss"
|
|
118
|
+
style={{
|
|
119
|
+
background: "none",
|
|
120
|
+
border: "1px solid #334155",
|
|
121
|
+
color: "#94a3b8",
|
|
122
|
+
padding: "0.5rem 1rem",
|
|
123
|
+
borderRadius: "6px",
|
|
124
|
+
cursor: "pointer",
|
|
125
|
+
fontSize: "0.85rem",
|
|
126
|
+
}}
|
|
127
|
+
>
|
|
128
|
+
Continue anyway →
|
|
129
|
+
</button>
|
|
130
|
+
</div>
|
|
131
|
+
<script
|
|
132
|
+
dangerouslySetInnerHTML={{
|
|
133
|
+
__html: `
|
|
134
|
+
(function() {
|
|
135
|
+
window.__GITMAPS_BUILD_COMMIT__ = ${JSON.stringify(build.commit)};
|
|
136
|
+
window.__GITMAPS_BUILD_DATE__ = ${JSON.stringify(build.date)};
|
|
137
|
+
if (window.innerWidth < 768) {
|
|
138
|
+
var g = document.getElementById('mobileGate');
|
|
139
|
+
if (g) { g.style.display = 'flex'; }
|
|
140
|
+
var d = document.getElementById('mobileGateDismiss');
|
|
141
|
+
if (d) d.onclick = function() { g.style.display = 'none'; };
|
|
142
|
+
}
|
|
143
|
+
})();
|
|
144
|
+
`,
|
|
145
|
+
}}
|
|
146
|
+
/>
|
|
147
|
+
<div id="app">
|
|
148
|
+
<nav className="sidebar">
|
|
149
|
+
<div className="sidebar-header">
|
|
150
|
+
<div className="logo">
|
|
151
|
+
<svg
|
|
152
|
+
viewBox="0 0 24 24"
|
|
153
|
+
width="22"
|
|
154
|
+
height="22"
|
|
155
|
+
fill="none"
|
|
156
|
+
stroke="currentColor"
|
|
157
|
+
strokeWidth="2"
|
|
158
|
+
>
|
|
159
|
+
<circle cx="12" cy="12" r="3" />
|
|
160
|
+
<circle cx="12" cy="4" r="1.5" />
|
|
161
|
+
<circle cx="12" cy="20" r="1.5" />
|
|
162
|
+
<circle cx="4" cy="8" r="1.5" />
|
|
163
|
+
<circle cx="20" cy="8" r="1.5" />
|
|
164
|
+
<circle cx="4" cy="16" r="1.5" />
|
|
165
|
+
<circle cx="20" cy="16" r="1.5" />
|
|
166
|
+
<path d="M12 7v2M12 15v2M8.5 9.5l-2.5-1M15.5 9.5l2.5-1M8.5 14.5l-2.5 1M15.5 14.5l2.5 1" />
|
|
167
|
+
</svg>
|
|
168
|
+
<span>GitMaps</span>
|
|
169
|
+
</div>
|
|
170
|
+
</div>
|
|
48
171
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
172
|
+
<div className="repo-selector">
|
|
173
|
+
<select id="repoSelect" className="repo-dropdown">
|
|
174
|
+
<option value="">Select a repository...</option>
|
|
175
|
+
</select>
|
|
176
|
+
<input type="text" id="repoPath" style={{ display: "none" }} />
|
|
177
|
+
<input
|
|
178
|
+
type="file"
|
|
179
|
+
id="folderPickerInput"
|
|
180
|
+
style={{ display: "none" }}
|
|
181
|
+
/>
|
|
182
|
+
<div
|
|
183
|
+
className="clone-status"
|
|
184
|
+
id="cloneStatus"
|
|
185
|
+
style={{ display: "none" }}
|
|
186
|
+
></div>
|
|
56
187
|
|
|
57
|
-
|
|
188
|
+
<button
|
|
189
|
+
id="githubImportBtn"
|
|
190
|
+
className="github-import-btn"
|
|
191
|
+
title="Import from GitHub"
|
|
192
|
+
>
|
|
193
|
+
<svg
|
|
194
|
+
viewBox="0 0 24 24"
|
|
195
|
+
width="14"
|
|
196
|
+
height="14"
|
|
197
|
+
fill="currentColor"
|
|
198
|
+
>
|
|
199
|
+
<path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z" />
|
|
200
|
+
</svg>
|
|
201
|
+
Import from GitHub
|
|
202
|
+
</button>
|
|
203
|
+
</div>
|
|
58
204
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
<path d="M12 6v6l4 2" />
|
|
69
|
-
</svg>
|
|
70
|
-
<p>Load a repository</p>
|
|
71
|
-
</div>
|
|
72
|
-
</div>
|
|
73
|
-
</div>
|
|
205
|
+
<div
|
|
206
|
+
id="repoTabs"
|
|
207
|
+
style={{
|
|
208
|
+
display: "none",
|
|
209
|
+
gap: "6px",
|
|
210
|
+
padding: "0 12px 8px",
|
|
211
|
+
flexWrap: "wrap",
|
|
212
|
+
}}
|
|
213
|
+
></div>
|
|
74
214
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
215
|
+
<div className="commit-timeline" id="commitTimeline">
|
|
216
|
+
<div className="section-header">
|
|
217
|
+
<span className="section-title">History</span>
|
|
218
|
+
<span className="badge" id="commitCount">
|
|
219
|
+
0
|
|
220
|
+
</span>
|
|
221
|
+
<button
|
|
222
|
+
id="pullBtn"
|
|
223
|
+
className="btn-ghost btn-xs"
|
|
224
|
+
title="Pull latest commits from remote"
|
|
225
|
+
style={{ marginLeft: "auto" }}
|
|
226
|
+
>
|
|
227
|
+
<svg
|
|
228
|
+
viewBox="0 0 24 24"
|
|
229
|
+
width="12"
|
|
230
|
+
height="12"
|
|
231
|
+
fill="none"
|
|
232
|
+
stroke="currentColor"
|
|
233
|
+
strokeWidth="2.5"
|
|
234
|
+
>
|
|
235
|
+
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
|
|
236
|
+
<polyline points="7 10 12 15 17 10" />
|
|
237
|
+
<line x1="12" y1="15" x2="12" y2="3" />
|
|
238
|
+
</svg>
|
|
239
|
+
Pull
|
|
240
|
+
</button>
|
|
241
|
+
</div>
|
|
242
|
+
<div className="timeline-container" id="timelineContainer">
|
|
243
|
+
<div className="empty-state">
|
|
244
|
+
<svg
|
|
245
|
+
viewBox="0 0 24 24"
|
|
246
|
+
width="24"
|
|
247
|
+
height="24"
|
|
248
|
+
fill="none"
|
|
249
|
+
stroke="currentColor"
|
|
250
|
+
strokeWidth="1.5"
|
|
251
|
+
opacity="0.3"
|
|
252
|
+
>
|
|
253
|
+
<circle cx="12" cy="12" r="10" />
|
|
254
|
+
<path d="M12 6v6l4 2" />
|
|
255
|
+
</svg>
|
|
256
|
+
</div>
|
|
257
|
+
</div>
|
|
258
|
+
</div>
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
<div className="sidebar-bottom">
|
|
263
|
+
<div className="canvas-controls">
|
|
264
|
+
<div className="control-row">
|
|
265
|
+
<button
|
|
266
|
+
id="resetView"
|
|
267
|
+
className="btn-ghost"
|
|
268
|
+
title="Reset View"
|
|
269
|
+
>
|
|
270
|
+
<svg
|
|
271
|
+
viewBox="0 0 24 24"
|
|
272
|
+
width="14"
|
|
273
|
+
height="14"
|
|
274
|
+
fill="none"
|
|
275
|
+
stroke="currentColor"
|
|
276
|
+
strokeWidth="2"
|
|
277
|
+
>
|
|
278
|
+
<path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" />
|
|
279
|
+
<path d="M3 3v5h5" />
|
|
280
|
+
</svg>
|
|
281
|
+
</button>
|
|
282
|
+
<button id="fitAll" className="btn-ghost" title="Fit All">
|
|
283
|
+
<svg
|
|
284
|
+
viewBox="0 0 24 24"
|
|
285
|
+
width="14"
|
|
286
|
+
height="14"
|
|
287
|
+
fill="none"
|
|
288
|
+
stroke="currentColor"
|
|
289
|
+
strokeWidth="2"
|
|
290
|
+
>
|
|
291
|
+
<path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7" />
|
|
292
|
+
</svg>
|
|
293
|
+
</button>
|
|
294
|
+
<div className="zoom-inline">
|
|
295
|
+
<input
|
|
296
|
+
type="range"
|
|
297
|
+
id="zoomSlider"
|
|
298
|
+
min="0.1"
|
|
299
|
+
max="3"
|
|
300
|
+
step="0.1"
|
|
301
|
+
defaultValue="1"
|
|
302
|
+
/>
|
|
303
|
+
<span id="zoomValue">100%</span>
|
|
304
|
+
</div>
|
|
305
|
+
</div>
|
|
306
|
+
</div>
|
|
95
307
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
308
|
+
<div className="hotkey-toggle-wrapper">
|
|
309
|
+
<button
|
|
310
|
+
id="hotkeyToggle"
|
|
311
|
+
className="btn-ghost hotkey-toggle"
|
|
312
|
+
title="Keyboard shortcuts"
|
|
313
|
+
>
|
|
314
|
+
<span>?</span>
|
|
315
|
+
</button>
|
|
316
|
+
<div className="hotkey-popup" id="hotkeyPopup">
|
|
317
|
+
<div className="hotkey-popup-title">Keyboard Shortcuts</div>
|
|
318
|
+
<div className="hotkey-grid">
|
|
319
|
+
<div className="hk">
|
|
320
|
+
<kbd>Scroll</kbd> Zoom
|
|
321
|
+
</div>
|
|
322
|
+
<div className="hk">
|
|
323
|
+
<kbd>Space+Drag</kbd> Pan
|
|
324
|
+
</div>
|
|
325
|
+
<div className="hk">
|
|
326
|
+
<kbd>Click</kbd> Select
|
|
327
|
+
</div>
|
|
328
|
+
<div className="hk">
|
|
329
|
+
<kbd>Shift+Click</kbd> Multi-select
|
|
330
|
+
</div>
|
|
331
|
+
<div className="hk">
|
|
332
|
+
<kbd>Drag canvas</kbd> Rect select
|
|
333
|
+
</div>
|
|
334
|
+
<div className="hk">
|
|
335
|
+
<kbd>Drag card</kbd> Move
|
|
336
|
+
</div>
|
|
337
|
+
<div className="hk">
|
|
338
|
+
<kbd>Del</kbd> Hide file
|
|
339
|
+
</div>
|
|
340
|
+
<div className="hk">
|
|
341
|
+
<kbd>H</kbd> Arrange row
|
|
342
|
+
</div>
|
|
343
|
+
<div className="hk">
|
|
344
|
+
<kbd>V</kbd> Arrange column
|
|
345
|
+
</div>
|
|
346
|
+
<div className="hk">
|
|
347
|
+
<kbd>G</kbd> Arrange grid
|
|
348
|
+
</div>
|
|
349
|
+
<div className="hk">
|
|
350
|
+
<kbd>W</kbd> Fit to screen
|
|
351
|
+
</div>
|
|
352
|
+
<div className="hk">
|
|
353
|
+
<kbd>Ctrl+F</kbd> Search across files
|
|
354
|
+
</div>
|
|
355
|
+
<div className="hk">
|
|
356
|
+
<kbd>Ctrl+O</kbd> Find file
|
|
357
|
+
</div>
|
|
358
|
+
<div className="hk">
|
|
359
|
+
<kbd>Ctrl +/-</kbd> Text zoom
|
|
360
|
+
</div>
|
|
361
|
+
<div className="hk">
|
|
362
|
+
<kbd>Dbl-click</kbd> Open editor
|
|
363
|
+
</div>
|
|
364
|
+
<div className="hk">
|
|
365
|
+
<kbd>Alt+Click</kbd> Connect lines
|
|
366
|
+
</div>
|
|
367
|
+
<div className="hk">
|
|
368
|
+
<kbd>Arrow keys</kbd> Prev/next commit
|
|
369
|
+
</div>
|
|
370
|
+
<div className="hk">
|
|
371
|
+
<kbd>Ctrl+N</kbd> New file
|
|
372
|
+
</div>
|
|
373
|
+
<div className="hk">
|
|
374
|
+
<kbd>Ctrl+S</kbd> Save (in editor)
|
|
375
|
+
</div>
|
|
376
|
+
</div>
|
|
377
|
+
</div>
|
|
378
|
+
</div>
|
|
379
|
+
</div>
|
|
380
|
+
</nav>
|
|
127
381
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
382
|
+
<main className="canvas-area">
|
|
383
|
+
<div className="canvas-header">
|
|
384
|
+
<div className="header-left">
|
|
385
|
+
<div className="current-commit" id="currentCommitInfo">
|
|
386
|
+
<span className="commit-hash-label">No commit selected</span>
|
|
387
|
+
</div>
|
|
388
|
+
</div>
|
|
389
|
+
<div className="header-right">
|
|
390
|
+
<div
|
|
391
|
+
className="arrange-toolbar"
|
|
392
|
+
id="arrangeToolbar"
|
|
393
|
+
style={{ display: "none" }}
|
|
394
|
+
>
|
|
395
|
+
<span className="arrange-label">Arrange:</span>
|
|
396
|
+
<button
|
|
397
|
+
id="arrangeRow"
|
|
398
|
+
className="btn-ghost btn-xs"
|
|
399
|
+
title="Arrange in row (H)"
|
|
400
|
+
>
|
|
401
|
+
<svg
|
|
402
|
+
viewBox="0 0 24 24"
|
|
403
|
+
width="14"
|
|
404
|
+
height="14"
|
|
405
|
+
fill="none"
|
|
406
|
+
stroke="currentColor"
|
|
407
|
+
strokeWidth="2"
|
|
408
|
+
>
|
|
409
|
+
<rect x="2" y="7" width="5" height="10" rx="1" />
|
|
410
|
+
<rect x="9.5" y="7" width="5" height="10" rx="1" />
|
|
411
|
+
<rect x="17" y="7" width="5" height="10" rx="1" />
|
|
412
|
+
</svg>
|
|
413
|
+
</button>
|
|
414
|
+
<button
|
|
415
|
+
id="arrangeCol"
|
|
416
|
+
className="btn-ghost btn-xs"
|
|
417
|
+
title="Arrange in column (V)"
|
|
418
|
+
>
|
|
419
|
+
<svg
|
|
420
|
+
viewBox="0 0 24 24"
|
|
421
|
+
width="14"
|
|
422
|
+
height="14"
|
|
423
|
+
fill="none"
|
|
424
|
+
stroke="currentColor"
|
|
425
|
+
strokeWidth="2"
|
|
426
|
+
>
|
|
427
|
+
<rect x="4" y="2" width="16" height="5" rx="1" />
|
|
428
|
+
<rect x="4" y="9.5" width="16" height="5" rx="1" />
|
|
429
|
+
<rect x="4" y="17" width="16" height="5" rx="1" />
|
|
430
|
+
</svg>
|
|
431
|
+
</button>
|
|
432
|
+
<button
|
|
433
|
+
id="arrangeGrid"
|
|
434
|
+
className="btn-ghost btn-xs"
|
|
435
|
+
title="Arrange in grid (G)"
|
|
436
|
+
>
|
|
437
|
+
<svg
|
|
438
|
+
viewBox="0 0 24 24"
|
|
439
|
+
width="14"
|
|
440
|
+
height="14"
|
|
441
|
+
fill="none"
|
|
442
|
+
stroke="currentColor"
|
|
443
|
+
strokeWidth="2"
|
|
444
|
+
>
|
|
445
|
+
<rect x="3" y="3" width="7" height="7" rx="1" />
|
|
446
|
+
<rect x="14" y="3" width="7" height="7" rx="1" />
|
|
447
|
+
<rect x="3" y="14" width="7" height="7" rx="1" />
|
|
448
|
+
<rect x="14" y="14" width="7" height="7" rx="1" />
|
|
449
|
+
</svg>
|
|
450
|
+
</button>
|
|
451
|
+
<div
|
|
452
|
+
style={{
|
|
453
|
+
width: 1,
|
|
454
|
+
height: 16,
|
|
455
|
+
background: "var(--border)",
|
|
456
|
+
margin: "0 4px",
|
|
457
|
+
}}
|
|
458
|
+
></div>
|
|
459
|
+
<button
|
|
460
|
+
id="arrangeFit"
|
|
461
|
+
className="btn-ghost btn-xs"
|
|
462
|
+
title="Reset Size (W)"
|
|
463
|
+
>
|
|
464
|
+
<svg
|
|
465
|
+
viewBox="0 0 24 24"
|
|
466
|
+
width="14"
|
|
467
|
+
height="14"
|
|
468
|
+
fill="none"
|
|
469
|
+
stroke="currentColor"
|
|
470
|
+
strokeWidth="2"
|
|
471
|
+
>
|
|
472
|
+
<path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7" />
|
|
473
|
+
</svg>
|
|
474
|
+
</button>
|
|
475
|
+
<button
|
|
476
|
+
id="arrangeAI"
|
|
477
|
+
className="btn-ghost btn-xs"
|
|
478
|
+
title="Explain with AI..."
|
|
479
|
+
>
|
|
480
|
+
<svg
|
|
481
|
+
viewBox="0 0 24 24"
|
|
482
|
+
width="14"
|
|
483
|
+
height="14"
|
|
484
|
+
fill="currentColor"
|
|
485
|
+
>
|
|
486
|
+
<path d="M11 2h2v4h-2zm0 16h2v4h-2zm11-7v2h-4v-2zm-16 0v2H2v-2zm12.3-5.3l1.4 1.4-2.8 2.8-1.4-1.4zm-9.8 9.8l1.4 1.4-2.8 2.8-1.4-1.4z" />
|
|
487
|
+
</svg>
|
|
488
|
+
</button>
|
|
489
|
+
</div>
|
|
490
|
+
<button
|
|
491
|
+
id="toggleChangedFiles"
|
|
492
|
+
className="btn-ghost btn-sm"
|
|
493
|
+
title="Show changed files"
|
|
494
|
+
>
|
|
495
|
+
<svg
|
|
496
|
+
viewBox="0 0 24 24"
|
|
497
|
+
width="14"
|
|
498
|
+
height="14"
|
|
499
|
+
fill="none"
|
|
500
|
+
stroke="currentColor"
|
|
501
|
+
strokeWidth="2"
|
|
502
|
+
>
|
|
503
|
+
<path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z" />
|
|
504
|
+
<polyline points="14 2 14 8 20 8" />
|
|
505
|
+
</svg>
|
|
506
|
+
<span id="fileCount">0</span>
|
|
507
|
+
</button>
|
|
508
|
+
<button
|
|
509
|
+
id="showHidden"
|
|
510
|
+
className="btn-ghost btn-sm"
|
|
511
|
+
title="Show hidden files"
|
|
512
|
+
style={{ display: "none" }}
|
|
513
|
+
>
|
|
514
|
+
<svg
|
|
515
|
+
viewBox="0 0 24 24"
|
|
516
|
+
width="14"
|
|
517
|
+
height="14"
|
|
518
|
+
fill="none"
|
|
519
|
+
stroke="currentColor"
|
|
520
|
+
strokeWidth="2"
|
|
521
|
+
>
|
|
522
|
+
<path d="M17.94 17.94A10.07 10.07 0 0112 20c-7 0-11-8-11-8a18.45 18.45 0 015.06-5.94" />
|
|
523
|
+
<path d="M9.9 4.24A9.12 9.12 0 0112 4c7 0 11 8 11 8a18.5 18.5 0 01-2.16 3.19" />
|
|
524
|
+
<line x1="1" y1="1" x2="23" y2="23" />
|
|
525
|
+
</svg>
|
|
526
|
+
<span id="hiddenCount">0</span>
|
|
527
|
+
</button>
|
|
528
|
+
<button
|
|
529
|
+
id="toggleConnections"
|
|
530
|
+
className="btn-ghost btn-sm"
|
|
531
|
+
title="Toggle connection lines"
|
|
532
|
+
>
|
|
533
|
+
<svg
|
|
534
|
+
viewBox="0 0 24 24"
|
|
535
|
+
width="14"
|
|
536
|
+
height="14"
|
|
537
|
+
fill="none"
|
|
538
|
+
stroke="currentColor"
|
|
539
|
+
strokeWidth="2"
|
|
540
|
+
>
|
|
541
|
+
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" />
|
|
542
|
+
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" />
|
|
543
|
+
</svg>
|
|
544
|
+
</button>
|
|
545
|
+
<button
|
|
546
|
+
id="dep-graph-btn"
|
|
547
|
+
className="btn-ghost btn-sm"
|
|
548
|
+
title="Toggle dependency graph (Ctrl+G)"
|
|
549
|
+
>
|
|
550
|
+
<svg
|
|
551
|
+
viewBox="0 0 24 24"
|
|
552
|
+
width="14"
|
|
553
|
+
height="14"
|
|
554
|
+
fill="none"
|
|
555
|
+
stroke="currentColor"
|
|
556
|
+
strokeWidth="2"
|
|
557
|
+
>
|
|
558
|
+
<circle cx="5" cy="5" r="2.5" />
|
|
559
|
+
<circle cx="19" cy="5" r="2.5" />
|
|
560
|
+
<circle cx="12" cy="19" r="2.5" />
|
|
561
|
+
<line x1="7" y1="6" x2="17" y2="6" />
|
|
562
|
+
<line x1="6" y1="7" x2="11" y2="17" />
|
|
563
|
+
<line x1="18" y1="7" x2="13" y2="17" />
|
|
564
|
+
</svg>
|
|
565
|
+
</button>
|
|
566
|
+
<button
|
|
567
|
+
id="toggleCanvasText"
|
|
568
|
+
className="btn-ghost btn-sm"
|
|
569
|
+
title="Toggle text rendering mode (DOM vs WebGL/Canvas)"
|
|
570
|
+
>
|
|
571
|
+
<svg
|
|
572
|
+
viewBox="0 0 24 24"
|
|
573
|
+
width="16"
|
|
574
|
+
height="16"
|
|
575
|
+
fill="none"
|
|
576
|
+
stroke="currentColor"
|
|
577
|
+
strokeWidth="2"
|
|
578
|
+
strokeLinecap="round"
|
|
579
|
+
strokeLinejoin="round"
|
|
580
|
+
>
|
|
581
|
+
<polyline points="4 7 4 4 20 4 20 7" />
|
|
582
|
+
<line x1="9" y1="20" x2="15" y2="20" />
|
|
583
|
+
<line x1="12" y1="4" x2="12" y2="20" />
|
|
584
|
+
</svg>
|
|
585
|
+
</button>
|
|
586
|
+
<button
|
|
587
|
+
id="autoDetectImports"
|
|
588
|
+
className="btn-ghost btn-sm"
|
|
589
|
+
title="Auto-detect import connections"
|
|
590
|
+
>
|
|
591
|
+
<svg
|
|
592
|
+
viewBox="0 0 24 24"
|
|
593
|
+
width="14"
|
|
594
|
+
height="14"
|
|
595
|
+
fill="none"
|
|
596
|
+
stroke="currentColor"
|
|
597
|
+
strokeWidth="2"
|
|
598
|
+
>
|
|
599
|
+
<path d="M9 3H5a2 2 0 00-2 2v4m6-6h10a2 2 0 012 2v4M9 3v18m0 0h10a2 2 0 002-2v-4M9 21H5a2 2 0 01-2-2v-4" />
|
|
600
|
+
<path d="M14 9l2 2-2 2" />
|
|
601
|
+
</svg>
|
|
602
|
+
</button>
|
|
603
|
+
<button
|
|
604
|
+
id="shareLayout"
|
|
605
|
+
className="btn-ghost btn-sm"
|
|
606
|
+
title="Share Layout (Copy URL)"
|
|
607
|
+
>
|
|
608
|
+
<svg
|
|
609
|
+
viewBox="0 0 24 24"
|
|
610
|
+
width="14"
|
|
611
|
+
height="14"
|
|
612
|
+
fill="none"
|
|
613
|
+
stroke="currentColor"
|
|
614
|
+
strokeWidth="2"
|
|
615
|
+
>
|
|
616
|
+
<circle cx="18" cy="5" r="3" />
|
|
617
|
+
<circle cx="6" cy="12" r="3" />
|
|
618
|
+
<circle cx="18" cy="19" r="3" />
|
|
619
|
+
<line x1="8.59" y1="13.51" x2="15.42" y2="17.49" />
|
|
620
|
+
<line x1="15.41" y1="6.51" x2="8.59" y2="10.49" />
|
|
621
|
+
</svg>
|
|
622
|
+
</button>
|
|
623
|
+
<button
|
|
624
|
+
id="toggleControlMode"
|
|
625
|
+
className="btn-ghost btn-sm"
|
|
626
|
+
title="Toggle control mode: Simple (drag=pan) / Advanced (space+drag=pan)"
|
|
627
|
+
>
|
|
628
|
+
<svg
|
|
629
|
+
id="controlModeIcon"
|
|
630
|
+
viewBox="0 0 24 24"
|
|
631
|
+
width="14"
|
|
632
|
+
height="14"
|
|
633
|
+
fill="none"
|
|
634
|
+
stroke="currentColor"
|
|
635
|
+
strokeWidth="2"
|
|
636
|
+
>
|
|
637
|
+
{/* Default: Advanced (crosshair) — gets swapped by JS */}
|
|
638
|
+
<circle cx="12" cy="12" r="10" />
|
|
639
|
+
<line x1="12" y1="2" x2="12" y2="6" />
|
|
640
|
+
<line x1="12" y1="18" x2="12" y2="22" />
|
|
641
|
+
<line x1="2" y1="12" x2="6" y2="12" />
|
|
642
|
+
<line x1="18" y1="12" x2="22" y2="12" />
|
|
643
|
+
</svg>
|
|
644
|
+
</button>
|
|
645
|
+
<button
|
|
646
|
+
id="openSnapshots"
|
|
647
|
+
className="btn-ghost btn-sm"
|
|
648
|
+
title="Layout Snapshots"
|
|
649
|
+
>
|
|
650
|
+
<svg
|
|
651
|
+
viewBox="0 0 24 24"
|
|
652
|
+
width="14"
|
|
653
|
+
height="14"
|
|
654
|
+
fill="none"
|
|
655
|
+
stroke="currentColor"
|
|
656
|
+
strokeWidth="2"
|
|
657
|
+
strokeLinecap="round"
|
|
658
|
+
strokeLinejoin="round"
|
|
659
|
+
>
|
|
660
|
+
<path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z" />
|
|
661
|
+
<circle cx="12" cy="13" r="4" />
|
|
662
|
+
<path d="M15 8h.01" />
|
|
663
|
+
</svg>
|
|
664
|
+
</button>
|
|
665
|
+
<button
|
|
666
|
+
id="openGlobalSearch"
|
|
667
|
+
className="btn-ghost btn-sm"
|
|
668
|
+
title="Search Files (Ctrl+F)"
|
|
669
|
+
>
|
|
670
|
+
<svg
|
|
671
|
+
viewBox="0 0 24 24"
|
|
672
|
+
width="14"
|
|
673
|
+
height="14"
|
|
674
|
+
fill="none"
|
|
675
|
+
stroke="currentColor"
|
|
676
|
+
strokeWidth="2"
|
|
677
|
+
strokeLinecap="round"
|
|
678
|
+
strokeLinejoin="round"
|
|
679
|
+
>
|
|
680
|
+
<circle cx="11" cy="11" r="8" />
|
|
681
|
+
<line x1="21" y1="21" x2="16.65" y2="16.65" />
|
|
682
|
+
</svg>
|
|
683
|
+
</button>
|
|
684
|
+
<button
|
|
685
|
+
id="openBranchCompare"
|
|
686
|
+
className="btn-ghost btn-sm"
|
|
687
|
+
title="Compare Branches"
|
|
688
|
+
>
|
|
689
|
+
<svg
|
|
690
|
+
viewBox="0 0 24 24"
|
|
691
|
+
width="14"
|
|
692
|
+
height="14"
|
|
693
|
+
fill="none"
|
|
694
|
+
stroke="currentColor"
|
|
695
|
+
strokeWidth="2"
|
|
696
|
+
strokeLinecap="round"
|
|
697
|
+
strokeLinejoin="round"
|
|
698
|
+
>
|
|
699
|
+
<circle cx="18" cy="18" r="3" />
|
|
700
|
+
<circle cx="6" cy="6" r="3" />
|
|
701
|
+
<path d="M13 6h3a2 2 0 0 1 2 2v7" />
|
|
702
|
+
<path d="M6 9v12" />
|
|
703
|
+
</svg>
|
|
704
|
+
</button>
|
|
705
|
+
<button
|
|
706
|
+
id="openSettings"
|
|
707
|
+
className="btn-ghost btn-sm"
|
|
708
|
+
title="Settings"
|
|
709
|
+
>
|
|
710
|
+
<svg
|
|
711
|
+
viewBox="0 0 24 24"
|
|
712
|
+
width="14"
|
|
713
|
+
height="14"
|
|
714
|
+
fill="none"
|
|
715
|
+
stroke="currentColor"
|
|
716
|
+
strokeWidth="2"
|
|
717
|
+
strokeLinecap="round"
|
|
718
|
+
strokeLinejoin="round"
|
|
719
|
+
>
|
|
720
|
+
<circle cx="12" cy="12" r="3" />
|
|
721
|
+
<path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 010 2.83 2 2 0 01-2.83 0l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-4 0v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83-2.83l.06-.06A1.65 1.65 0 004.68 15a1.65 1.65 0 00-1.51-1H3a2 2 0 010-4h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 012.83-2.83l.06.06A1.65 1.65 0 009 4.68a1.65 1.65 0 001-1.51V3a2 2 0 014 0v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 2.83l-.06.06A1.65 1.65 0 0019.4 9a1.65 1.65 0 001.51 1H21a2 2 0 010 4h-.09a1.65 1.65 0 00-1.51 1z" />
|
|
722
|
+
</svg>
|
|
723
|
+
</button>
|
|
724
|
+
<button
|
|
725
|
+
id="toggleCanvasChat"
|
|
726
|
+
className="btn-ghost btn-sm ai-chat-btn"
|
|
727
|
+
title="AI Chat"
|
|
728
|
+
>
|
|
729
|
+
<svg
|
|
730
|
+
viewBox="0 0 24 24"
|
|
731
|
+
width="14"
|
|
732
|
+
height="14"
|
|
733
|
+
fill="none"
|
|
734
|
+
stroke="currentColor"
|
|
735
|
+
strokeWidth="2"
|
|
736
|
+
>
|
|
737
|
+
<path d="M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2z" />
|
|
738
|
+
</svg>
|
|
739
|
+
AI
|
|
740
|
+
</button>
|
|
741
|
+
</div>
|
|
742
|
+
</div>
|
|
265
743
|
|
|
266
|
-
|
|
744
|
+
{children}
|
|
267
745
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
746
|
+
{/* Changed Files Panel */}
|
|
747
|
+
<div
|
|
748
|
+
className="changed-files-panel"
|
|
749
|
+
id="changedFilesPanel"
|
|
750
|
+
style={{ display: "none" }}
|
|
751
|
+
>
|
|
752
|
+
<div className="panel-header">
|
|
753
|
+
<span className="panel-title">Changed Files</span>
|
|
754
|
+
<button
|
|
755
|
+
id="closeChangedFiles"
|
|
756
|
+
className="btn-ghost btn-xs"
|
|
757
|
+
title="Close"
|
|
758
|
+
>
|
|
759
|
+
<svg
|
|
760
|
+
viewBox="0 0 24 24"
|
|
761
|
+
width="12"
|
|
762
|
+
height="12"
|
|
763
|
+
fill="none"
|
|
764
|
+
stroke="currentColor"
|
|
765
|
+
strok fill="none"
|
|
766
|
+
stroke="currentColor"
|
|
767
|
+
strokeWidth="2.5"
|
|
768
|
+
>
|
|
769
|
+
<line x1="18" y1="6" x2="6" y2="18" />
|
|
770
|
+
<line x1="6" y1="6" x2="18" y2="18" />
|
|
771
|
+
</svg>
|
|
772
|
+
</button>
|
|
773
|
+
</div>
|
|
774
|
+
<div className="changed-files-list" id="changedFilesList"></div>
|
|
775
|
+
</div>
|
|
281
776
|
|
|
777
|
+
{/* Connections Panel */}
|
|
778
|
+
<div
|
|
779
|
+
className="connections-panel"
|
|
780
|
+
id="connectionsPanel"
|
|
781
|
+
style={{ display: "none" }}
|
|
782
|
+
>
|
|
783
|
+
<div
|
|
784
|
+
className="panel-header"
|
|
785
|
+
style={{
|
|
786
|
+
display: "flex",
|
|
787
|
+
justifyContent: "space-between",
|
|
788
|
+
alignItems: "center",
|
|
789
|
+
padding: "12px 16px",
|
|
790
|
+
borderBottom: "1px solid var(--border)",
|
|
791
|
+
}}
|
|
792
|
+
>
|
|
793
|
+
<span
|
|
794
|
+
className="panel-title"
|
|
795
|
+
style={{
|
|
796
|
+
fontSize: "0.85rem",
|
|
797
|
+
fontWeight: 600,
|
|
798
|
+
color: "var(--text-primary)",
|
|
799
|
+
}}
|
|
800
|
+
>
|
|
801
|
+
Connections{" "}
|
|
802
|
+
<span
|
|
803
|
+
id="connCount"
|
|
804
|
+
style={{
|
|
805
|
+
marginLeft: "6px",
|
|
806
|
+
background: "rgba(255,255,255,0.1)",
|
|
807
|
+
padding: "2px 8px",
|
|
808
|
+
borderRadius: "12px",
|
|
809
|
+
fontSize: "0.75rem",
|
|
810
|
+
}}
|
|
811
|
+
>
|
|
812
|
+
0
|
|
813
|
+
</span>
|
|
814
|
+
</span>
|
|
815
|
+
<button
|
|
816
|
+
id="closeConnectionsPanel"
|
|
817
|
+
className="btn-ghost btn-xs"
|
|
818
|
+
title="Close"
|
|
819
|
+
>
|
|
820
|
+
<svg
|
|
821
|
+
viewBox="0 0 24 24"
|
|
822
|
+
width="12"
|
|
823
|
+
height="12"
|
|
824
|
+
fill="none"
|
|
825
|
+
stroke="currentColor"
|
|
826
|
+
strokeWidth="2.5"
|
|
827
|
+
>
|
|
828
|
+
<line x1="18" y1="6" x2="6" y2="18" />
|
|
829
|
+
<line x1="6" y1="6" x2="18" y2="18" />
|
|
830
|
+
</svg>
|
|
831
|
+
</button>
|
|
832
|
+
</div>
|
|
833
|
+
<div
|
|
834
|
+
style={{
|
|
835
|
+
padding: "8px 16px",
|
|
836
|
+
fontSize: "0.75rem",
|
|
837
|
+
color: "var(--text-muted)",
|
|
838
|
+
borderBottom: "1px solid var(--border)",
|
|
839
|
+
background: "rgba(0,0,0,0.2)",
|
|
840
|
+
}}
|
|
841
|
+
>
|
|
842
|
+
💡 Tip: <strong>Alt+Click</strong> any line number then select a
|
|
843
|
+
target to connect them.
|
|
844
|
+
</div>
|
|
845
|
+
<div
|
|
846
|
+
className="connections-list"
|
|
847
|
+
id="connectionsList"
|
|
848
|
+
style={{ flex: 1, overflowY: "auto" }}
|
|
849
|
+
></div>
|
|
850
|
+
</div>
|
|
282
851
|
|
|
852
|
+
<div className="minimap-container">
|
|
853
|
+
<div className="minimap" id="minimap">
|
|
854
|
+
<div className="minimap-viewport" id="minimapViewport"></div>
|
|
855
|
+
</div>
|
|
856
|
+
<button
|
|
857
|
+
id="expandMinimap"
|
|
858
|
+
className="btn-ghost btn-xs minimap-expand"
|
|
859
|
+
title="Expand minimap"
|
|
860
|
+
>
|
|
861
|
+
<svg
|
|
862
|
+
viewBox="0 0 24 24"
|
|
863
|
+
width="11"
|
|
864
|
+
height="11"
|
|
865
|
+
fill="none"
|
|
866
|
+
stroke="currentColor"
|
|
867
|
+
strokeWidth="2"
|
|
868
|
+
>
|
|
869
|
+
<polyline points="15 3 21 3 21 9" />
|
|
870
|
+
<polyline points="9 21 3 21 3 15" />
|
|
871
|
+
<line x1="21" y1="3" x2="14" y2="10" />
|
|
872
|
+
<line x1="3" y1="21" x2="10" y2="14" />
|
|
873
|
+
</svg>
|
|
874
|
+
</button>
|
|
875
|
+
</div>
|
|
283
876
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
877
|
+
{/* Sticky Zoom Controls — floating pill, bottom-right */}
|
|
878
|
+
<div id="stickyZoomControls" className="sticky-zoom-pill">
|
|
879
|
+
<button id="stickyZoomOut" className="sz-btn" title="Zoom out">
|
|
880
|
+
<svg
|
|
881
|
+
viewBox="0 0 24 24"
|
|
882
|
+
width="14"
|
|
883
|
+
height="14"
|
|
884
|
+
fill="none"
|
|
885
|
+
stroke="currentColor"
|
|
886
|
+
strokeWidth="2.5"
|
|
887
|
+
>
|
|
888
|
+
<line x1="5" y1="12" x2="19" y2="12" />
|
|
889
|
+
</svg>
|
|
890
|
+
</button>
|
|
891
|
+
<input
|
|
892
|
+
type="range"
|
|
893
|
+
id="stickyZoomSlider"
|
|
894
|
+
className="sz-slider"
|
|
895
|
+
min="0.1"
|
|
896
|
+
max="3"
|
|
897
|
+
step="0.05"
|
|
898
|
+
defaultValue="1"
|
|
899
|
+
/>
|
|
900
|
+
<button id="stickyZoomIn" className="sz-btn" title="Zoom in">
|
|
901
|
+
<svg
|
|
902
|
+
viewBox="0 0 24 24"
|
|
903
|
+
width="14"
|
|
904
|
+
height="14"
|
|
905
|
+
fill="none"
|
|
906
|
+
stroke="currentColor"
|
|
907
|
+
strokeWidth="2.5"
|
|
908
|
+
>
|
|
909
|
+
<line x1="12" y1="5" x2="12" y2="19" />
|
|
910
|
+
<line x1="5" y1="12" x2="19" y2="12" />
|
|
911
|
+
</svg>
|
|
912
|
+
</button>
|
|
913
|
+
<span id="stickyZoomValue" className="sz-value">
|
|
914
|
+
100%
|
|
915
|
+
</span>
|
|
916
|
+
<div className="sz-divider" />
|
|
917
|
+
<button
|
|
918
|
+
id="stickyFitAll"
|
|
919
|
+
className="sz-btn sz-fit"
|
|
920
|
+
title="Fit all cards"
|
|
921
|
+
>
|
|
922
|
+
<svg
|
|
923
|
+
viewBox="0 0 24 24"
|
|
924
|
+
width="14"
|
|
925
|
+
height="14"
|
|
926
|
+
fill="none"
|
|
927
|
+
stroke="currentColor"
|
|
928
|
+
strokeWidth="2"
|
|
929
|
+
>
|
|
930
|
+
<path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7" />
|
|
931
|
+
</svg>
|
|
932
|
+
</button>
|
|
933
|
+
</div>
|
|
297
934
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
<line x1="5" y1="12" x2="19" y2="12" />
|
|
303
|
-
</svg>
|
|
304
|
-
</button>
|
|
305
|
-
<input type="range" id="stickyZoomSlider" className="sz-slider" min="0.1" max="3" step="0.05" defaultValue="1" />
|
|
306
|
-
<button id="stickyZoomIn" className="sz-btn" title="Zoom in">
|
|
307
|
-
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="2.5">
|
|
308
|
-
<line x1="12" y1="5" x2="12" y2="19" />
|
|
309
|
-
<line x1="5" y1="12" x2="19" y2="12" />
|
|
310
|
-
</svg>
|
|
311
|
-
</button>
|
|
312
|
-
<span id="stickyZoomValue" className="sz-value">100%</span>
|
|
313
|
-
<div className="sz-divider" />
|
|
314
|
-
<button id="stickyFitAll" className="sz-btn sz-fit" title="Fit all cards">
|
|
315
|
-
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="2">
|
|
316
|
-
<path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7" />
|
|
317
|
-
</svg>
|
|
318
|
-
</button>
|
|
319
|
-
</div>
|
|
935
|
+
{/* Bottom Layers Bar */}
|
|
936
|
+
<div id="layersBarContainer"></div>
|
|
937
|
+
</main>
|
|
938
|
+
</div>
|
|
320
939
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
940
|
+
{/* File Preview Modal */}
|
|
941
|
+
<div className="file-preview-modal" id="filePreviewModal">
|
|
942
|
+
<div className="modal-backdrop"></div>
|
|
943
|
+
<div className="modal-content">
|
|
944
|
+
<div className="modal-header">
|
|
945
|
+
<div className="modal-header-left">
|
|
946
|
+
<span className="file-path" id="previewFilePath"></span>
|
|
947
|
+
<span
|
|
948
|
+
className="modal-file-status"
|
|
949
|
+
id="previewFileStatus"
|
|
950
|
+
></span>
|
|
951
|
+
</div>
|
|
952
|
+
<div className="modal-header-right">
|
|
953
|
+
<div
|
|
954
|
+
className="modal-diff-nav"
|
|
955
|
+
id="modalDiffNav"
|
|
956
|
+
style={{
|
|
957
|
+
display: "none",
|
|
958
|
+
marginRight: "16px",
|
|
959
|
+
gap: "4px",
|
|
960
|
+
alignItems: "center",
|
|
961
|
+
}}
|
|
962
|
+
>
|
|
963
|
+
<button
|
|
964
|
+
className="btn-ghost btn-xs"
|
|
965
|
+
id="diffNavPrev"
|
|
966
|
+
title="Previous changed file (k)"
|
|
967
|
+
>
|
|
968
|
+
<svg
|
|
969
|
+
viewBox="0 0 24 24"
|
|
970
|
+
width="14"
|
|
971
|
+
height="14"
|
|
972
|
+
fill="none"
|
|
973
|
+
stroke="currentColor"
|
|
974
|
+
strokeWidth="2.5"
|
|
975
|
+
>
|
|
976
|
+
<polyline points="15 18 9 12 15 6"></polyline>
|
|
977
|
+
</svg>
|
|
978
|
+
</button>
|
|
979
|
+
<button
|
|
980
|
+
className="btn-ghost btn-xs"
|
|
981
|
+
id="diffNavNext"
|
|
982
|
+
title="Next changed file (j)"
|
|
983
|
+
>
|
|
984
|
+
<svg
|
|
985
|
+
viewBox="0 0 24 24"
|
|
986
|
+
width="14"
|
|
987
|
+
height="14"
|
|
988
|
+
fill="none"
|
|
989
|
+
stroke="currentColor"
|
|
990
|
+
strokeWidth="2.5"
|
|
991
|
+
>
|
|
992
|
+
<polyline points="9 18 15 12 9 6"></polyline>
|
|
993
|
+
</svg>
|
|
994
|
+
</button>
|
|
324
995
|
</div>
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
</button>
|
|
353
|
-
<button className="modal-tab" data-view="diff">
|
|
354
|
-
<svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" strokeWidth="2">
|
|
355
|
-
<path d="M12 3v18M3 12h18" />
|
|
356
|
-
</svg>
|
|
357
|
-
Diff
|
|
358
|
-
</button>
|
|
359
|
-
</div>
|
|
360
|
-
<span className="modal-save-status" id="modalSaveStatus" style={{ display: 'none' }}></span>
|
|
361
|
-
<button className="btn-ghost btn-xs modal-outline-toggle" id="outlineToggle" title="Toggle symbol outline (Ctrl+Shift+O)">
|
|
362
|
-
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="2">
|
|
363
|
-
<line x1="8" y1="6" x2="21" y2="6" /><line x1="8" y1="12" x2="21" y2="12" /><line x1="8" y1="18" x2="21" y2="18" />
|
|
364
|
-
<line x1="3" y1="6" x2="3.01" y2="6" /><line x1="3" y1="12" x2="3.01" y2="12" /><line x1="3" y1="18" x2="3.01" y2="18" />
|
|
365
|
-
</svg>
|
|
366
|
-
</button>
|
|
367
|
-
<button className="modal-close" id="closePreview">×</button>
|
|
368
|
-
</div>
|
|
369
|
-
</div>
|
|
370
|
-
<div className="modal-body-wrapper">
|
|
371
|
-
<pre className="modal-body" id="modalBodyPre"><code id="previewContent"></code></pre>
|
|
372
|
-
<div className="modal-outline-panel" id="modalOutlinePanel" style={{ display: 'none' }}></div>
|
|
373
|
-
</div>
|
|
374
|
-
<div className="modal-blame-container" id="modalBlameContainer" style={{ display: 'none' }}></div>
|
|
375
|
-
<div className="modal-chat-container" id="modalChatContainer" style={{ display: 'none' }}></div>
|
|
376
|
-
<div className="modal-edit-container" id="modalEditContainer" style={{ display: 'none' }}>
|
|
377
|
-
<textarea id="modalEditTextarea" className="modal-edit-textarea" spellCheck={false} autoComplete="off"></textarea>
|
|
378
|
-
<div className="modal-edit-toolbar" id="modalEditToolbar">
|
|
379
|
-
<span className="edit-line-info" id="editLineInfo">Line 1, Col 1</span>
|
|
380
|
-
<div className="edit-toolbar-right">
|
|
381
|
-
<div className="edit-commit-section" id="editCommitSection" style={{ display: 'none' }}>
|
|
382
|
-
<input
|
|
383
|
-
type="text"
|
|
384
|
-
id="editCommitMsg"
|
|
385
|
-
className="edit-commit-input"
|
|
386
|
-
placeholder="Commit message..."
|
|
387
|
-
spellCheck={false}
|
|
388
|
-
autoComplete="off"
|
|
389
|
-
/>
|
|
390
|
-
<button className="btn-ghost btn-sm edit-commit-btn" id="editCommitBtn" title="Commit this file">
|
|
391
|
-
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="2">
|
|
392
|
-
<circle cx="12" cy="12" r="4" />
|
|
393
|
-
<line x1="1.05" y1="12" x2="7" y2="12" />
|
|
394
|
-
<line x1="17.01" y1="12" x2="22.96" y2="12" />
|
|
395
|
-
</svg>
|
|
396
|
-
Commit
|
|
397
|
-
</button>
|
|
398
|
-
<button className="btn-ghost btn-xs edit-commit-cancel" id="editCommitCancel" title="Cancel">
|
|
399
|
-
<svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" strokeWidth="2.5">
|
|
400
|
-
<line x1="18" y1="6" x2="6" y2="18" />
|
|
401
|
-
<line x1="6" y1="6" x2="18" y2="18" />
|
|
402
|
-
</svg>
|
|
403
|
-
</button>
|
|
404
|
-
</div>
|
|
405
|
-
<button className="btn-ghost btn-sm edit-save-btn" id="editSaveBtn" title="Save (Ctrl+S)">
|
|
406
|
-
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="2">
|
|
407
|
-
<path d="M19 21H5a2 2 0 01-2-2V5a2 2 0 012-2h11l5 5v11a2 2 0 01-2 2z" />
|
|
408
|
-
<polyline points="17 21 17 13 7 13 7 21" />
|
|
409
|
-
<polyline points="7 3 7 8 15 8" />
|
|
410
|
-
</svg>
|
|
411
|
-
Save
|
|
412
|
-
</button>
|
|
413
|
-
</div>
|
|
414
|
-
</div>
|
|
415
|
-
</div>
|
|
416
|
-
</div>
|
|
996
|
+
<div className="modal-view-tabs" id="modalViewTabs">
|
|
997
|
+
<button className="modal-tab active" data-view="edit">
|
|
998
|
+
<svg
|
|
999
|
+
viewBox="0 0 24 24"
|
|
1000
|
+
width="12"
|
|
1001
|
+
height="12"
|
|
1002
|
+
fill="none"
|
|
1003
|
+
stroke="currentColor"
|
|
1004
|
+
strokeWidth="2"
|
|
1005
|
+
>
|
|
1006
|
+
<path d="M18.5 2.5a2.121 2.121 0 013 3L12 15l-4 1 1-4 9.5-9.5z" />
|
|
1007
|
+
</svg>
|
|
1008
|
+
Edit
|
|
1009
|
+
</button>
|
|
1010
|
+
<button className="modal-tab" data-view="diff">
|
|
1011
|
+
<svg
|
|
1012
|
+
viewBox="0 0 24 24"
|
|
1013
|
+
width="12"
|
|
1014
|
+
height="12"
|
|
1015
|
+
fill="none"
|
|
1016
|
+
stroke="currentColor"
|
|
1017
|
+
strokeWidth="2"
|
|
1018
|
+
>
|
|
1019
|
+
<path d="M12 3v18M3 12h18" />
|
|
1020
|
+
</svg>
|
|
1021
|
+
Diff
|
|
1022
|
+
</button>
|
|
417
1023
|
</div>
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
1024
|
+
<span
|
|
1025
|
+
id="modalSaveStatus"
|
|
1026
|
+
style={{ display: "none" }}
|
|
1027
|
+
></span>
|
|
1028
|
+
<button
|
|
1029
|
+
className="btn-ghost btn-xs modal-outline-toggle"
|
|
1030
|
+
id="outlineToggle"
|
|
1031
|
+
title="Toggle symbol outline (Ctrl+Shift+O)"
|
|
1032
|
+
>
|
|
1033
|
+
<svg
|
|
1034
|
+
viewBox="0 0 24 24"
|
|
1035
|
+
width="14"
|
|
1036
|
+
height="14"
|
|
1037
|
+
fill="none"
|
|
1038
|
+
stroke="currentColor"
|
|
1039
|
+
strokeWidth="2"
|
|
1040
|
+
>
|
|
1041
|
+
<line x1="8" y1="6" x2="21" y2="6" />
|
|
1042
|
+
<line x1="8" y1="12" x2="21" y2="12" />
|
|
1043
|
+
<line x1="8" y1="18" x2="21" y2="18" />
|
|
1044
|
+
<line x1="3" y1="6" x2="3.01" y2="6" />
|
|
1045
|
+
<line x1="3" y1="12" x2="3.01" y2="12" />
|
|
1046
|
+
<line x1="3" y1="18" x2="3.01" y2="18" />
|
|
1047
|
+
</svg>
|
|
1048
|
+
</button>
|
|
1049
|
+
<button className="modal-close" id="closePreview">
|
|
1050
|
+
×
|
|
1051
|
+
</button>
|
|
1052
|
+
</div>
|
|
1053
|
+
</div>
|
|
1054
|
+
<div className="modal-body-wrapper">
|
|
1055
|
+
<pre className="modal-body" id="modalBodyPre">
|
|
1056
|
+
<code id="previewContent"></code>
|
|
1057
|
+
</pre>
|
|
1058
|
+
<div
|
|
1059
|
+
className="modal-outline-panel"
|
|
1060
|
+
id="modalOutlinePanel"
|
|
1061
|
+
style={{ display: "none" }}
|
|
1062
|
+
></div>
|
|
1063
|
+
</div>
|
|
1064
|
+
<div
|
|
1065
|
+
className="modal-blame-container"
|
|
1066
|
+
id="modalBlameContainer"
|
|
1067
|
+
style={{ display: "none" }}
|
|
1068
|
+
></div>
|
|
1069
|
+
<div
|
|
1070
|
+
className="modal-chat-container"
|
|
1071
|
+
id="modalChatContainer"
|
|
1072
|
+
style={{ display: "none" }}
|
|
1073
|
+
></div>
|
|
1074
|
+
<div
|
|
1075
|
+
className="modal-edit-container"
|
|
1076
|
+
id="modalEditContainer"
|
|
1077
|
+
style={{ display: "none" }}
|
|
1078
|
+
>
|
|
1079
|
+
<textarea
|
|
1080
|
+
id="modalEditTextarea"
|
|
1081
|
+
className="modal-edit-textarea"
|
|
1082
|
+
spellCheck={false}
|
|
1083
|
+
autoComplete="off"
|
|
1084
|
+
></textarea>
|
|
1085
|
+
<div className="modal-edit-toolbar" id="modalEditToolbar">
|
|
1086
|
+
<span className="edit-line-info" id="editLineInfo">
|
|
1087
|
+
Line 1, Col 1
|
|
1088
|
+
</span>
|
|
1089
|
+
<div className="edit-toolbar-right">
|
|
1090
|
+
<div
|
|
1091
|
+
className="edit-commit-section"
|
|
1092
|
+
id="editCommitSection"
|
|
1093
|
+
style={{ display: "none" }}
|
|
1094
|
+
>
|
|
1095
|
+
<input
|
|
1096
|
+
type="text"
|
|
1097
|
+
id="editCommitMsg"
|
|
1098
|
+
className="edit-commit-input"
|
|
1099
|
+
placeholder="Commit message..."
|
|
1100
|
+
spellCheck={false}
|
|
1101
|
+
autoComplete="off"
|
|
1102
|
+
/>
|
|
1103
|
+
<button
|
|
1104
|
+
className="btn-ghost btn-sm edit-commit-btn"
|
|
1105
|
+
id="editCommitBtn"
|
|
1106
|
+
title="Commit this file"
|
|
1107
|
+
>
|
|
1108
|
+
<svg
|
|
1109
|
+
viewBox="0 0 24 24"
|
|
1110
|
+
width="14"
|
|
1111
|
+
height="14"
|
|
1112
|
+
fill="none"
|
|
1113
|
+
stroke="currentColor"
|
|
1114
|
+
strokeWidth="2"
|
|
1115
|
+
>
|
|
1116
|
+
<circle cx="12" cy="12" r="4" />
|
|
1117
|
+
<path d="M5 12l5 5L20 7" />
|
|
1118
|
+
</svg>
|
|
1119
|
+
Commit
|
|
1120
|
+
</button>
|
|
1121
|
+
<button
|
|
1122
|
+
className="btn-ghost btn-xs edit-commit-cancel"
|
|
1123
|
+
id="editCommitCancel"
|
|
1124
|
+
title="Cancel"
|
|
1125
|
+
>
|
|
1126
|
+
<svg
|
|
1127
|
+
viewBox="0 0 24 24"
|
|
1128
|
+
width="12"
|
|
1129
|
+
height="12"
|
|
1130
|
+
fill="none"
|
|
1131
|
+
stroke="currentColor"
|
|
1132
|
+
strokeWidth="2.5"
|
|
1133
|
+
>
|
|
1134
|
+
<line x1="18" y1="6" x2="6" y2="18" />
|
|
1135
|
+
<line x1="6" y1="6" x2="18" y2="18" />
|
|
1136
|
+
</svg>
|
|
1137
|
+
</button>
|
|
1138
|
+
</div>
|
|
1139
|
+
<button
|
|
1140
|
+
className="btn-ghost btn-sm edit-save-btn"
|
|
1141
|
+
id="editSaveBtn"
|
|
1142
|
+
title="Save (Ctrl+S)"
|
|
1143
|
+
>
|
|
1144
|
+
<svg
|
|
1145
|
+
viewBox="0 0 24 24"
|
|
1146
|
+
width="14"
|
|
1147
|
+
height="14"
|
|
1148
|
+
fill="none"
|
|
1149
|
+
stroke="currentColor"
|
|
1150
|
+
strokeWidth="2"
|
|
1151
|
+
>
|
|
1152
|
+
<path d="M19 21H5a2 2 0 01-2-2V5a2 2 0 012-2h11l5 5v11a2 2 0 01-2 2z" />
|
|
1153
|
+
<polyline points="17 21 17 13 7 13 7 21" />
|
|
1154
|
+
<polyline points="7 3 7 8 15 8" />
|
|
1155
|
+
</svg>
|
|
1156
|
+
Save
|
|
1157
|
+
</button>
|
|
489
1158
|
</div>
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
1159
|
+
</div>
|
|
1160
|
+
</div>
|
|
1161
|
+
</div>
|
|
1162
|
+
</div>
|
|
1163
|
+
|
|
1164
|
+
{/* GitHub Import Modal */}
|
|
1165
|
+
<div className="github-modal" id="githubModal">
|
|
1166
|
+
<div className="github-modal-backdrop"></div>
|
|
1167
|
+
<div className="github-modal-content">
|
|
1168
|
+
<div className="github-modal-header">
|
|
1169
|
+
<div className="github-modal-title">
|
|
1170
|
+
<svg
|
|
1171
|
+
viewBox="0 0 24 24"
|
|
1172
|
+
width="20"
|
|
1173
|
+
height="20"
|
|
1174
|
+
fill="currentColor"
|
|
1175
|
+
style={{ opacity: 0.7 }}
|
|
1176
|
+
>
|
|
1177
|
+
<path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z" />
|
|
1178
|
+
</svg>
|
|
1179
|
+
<span>Import from GitHub</span>
|
|
1180
|
+
</div>
|
|
1181
|
+
<button className="github-modal-close" id="githubModalClose">
|
|
1182
|
+
×
|
|
1183
|
+
</button>
|
|
1184
|
+
</div>
|
|
1185
|
+
<div className="github-search-row">
|
|
1186
|
+
<input
|
|
1187
|
+
type="text"
|
|
1188
|
+
id="githubUserInput"
|
|
1189
|
+
className="github-user-input"
|
|
1190
|
+
placeholder="Username, org, or paste a GitHub URL..."
|
|
1191
|
+
spellCheck={false}
|
|
1192
|
+
autoComplete="off"
|
|
1193
|
+
/>
|
|
1194
|
+
<select id="githubSortSelect" className="github-sort-select">
|
|
1195
|
+
<option value="updated">Recently Updated</option>
|
|
1196
|
+
<option value="stars">Most Stars</option>
|
|
1197
|
+
<option value="name">Name A→Z</option>
|
|
1198
|
+
</select>
|
|
1199
|
+
<button id="githubSearchBtn" className="github-search-btn">
|
|
1200
|
+
<svg
|
|
1201
|
+
viewBox="0 0 24 24"
|
|
1202
|
+
width="14"
|
|
1203
|
+
height="14"
|
|
1204
|
+
fill="none"
|
|
1205
|
+
stroke="currentColor"
|
|
1206
|
+
strokeWidth="2.5"
|
|
1207
|
+
>
|
|
1208
|
+
<circle cx="11" cy="11" r="8" />
|
|
1209
|
+
<line x1="21" y1="21" x2="16.65" y2="16.65" />
|
|
1210
|
+
</svg>
|
|
1211
|
+
Search
|
|
1212
|
+
</button>
|
|
1213
|
+
</div>
|
|
1214
|
+
<div
|
|
1215
|
+
className="github-url-clone-row"
|
|
1216
|
+
id="githubUrlCloneRow"
|
|
1217
|
+
style={{ display: "none" }}
|
|
1218
|
+
>
|
|
1219
|
+
<div className="github-url-detected">
|
|
1220
|
+
<svg
|
|
1221
|
+
viewBox="0 0 24 24"
|
|
1222
|
+
width="14"
|
|
1223
|
+
height="14"
|
|
1224
|
+
fill="none"
|
|
1225
|
+
stroke="currentColor"
|
|
1226
|
+
strokeWidth="2"
|
|
1227
|
+
>
|
|
1228
|
+
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" />
|
|
1229
|
+
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" />
|
|
1230
|
+
</svg>
|
|
1231
|
+
<span id="githubDetectedUrl">URL detected</span>
|
|
1232
|
+
</div>
|
|
1233
|
+
<button
|
|
1234
|
+
id="githubUrlCloneBtn"
|
|
1235
|
+
className="github-clone-btn github-url-clone-btn"
|
|
1236
|
+
>
|
|
1237
|
+
Clone & Open
|
|
1238
|
+
</button>
|
|
1239
|
+
</div>
|
|
1240
|
+
<div
|
|
1241
|
+
className="github-filter-row"
|
|
1242
|
+
id="githubFilterRow"
|
|
1243
|
+
style={{ display: "none" }}
|
|
1244
|
+
>
|
|
1245
|
+
<input
|
|
1246
|
+
type="text"
|
|
1247
|
+
id="githubRepoFilter"
|
|
1248
|
+
className="github-repo-filter"
|
|
1249
|
+
placeholder="Filter repos by name..."
|
|
1250
|
+
spellCheck={false}
|
|
1251
|
+
autoComplete="off"
|
|
1252
|
+
/>
|
|
1253
|
+
</div>
|
|
1254
|
+
<div
|
|
1255
|
+
className="github-profile"
|
|
1256
|
+
id="githubProfile"
|
|
1257
|
+
style={{ display: "none" }}
|
|
1258
|
+
></div>
|
|
1259
|
+
<div className="github-repos-grid" id="githubReposGrid">
|
|
1260
|
+
<div className="github-empty-state">
|
|
1261
|
+
<svg
|
|
1262
|
+
viewBox="0 0 24 24"
|
|
1263
|
+
width="40"
|
|
1264
|
+
height="40"
|
|
1265
|
+
fill="none"
|
|
1266
|
+
stroke="currentColor"
|
|
1267
|
+
strokeWidth="1"
|
|
1268
|
+
opacity="0.2"
|
|
1269
|
+
>
|
|
1270
|
+
<path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z" />
|
|
1271
|
+
</svg>
|
|
1272
|
+
<p>Enter a GitHub username to browse their repositories</p>
|
|
1273
|
+
</div>
|
|
1274
|
+
</div>
|
|
1275
|
+
<div
|
|
1276
|
+
className="github-pagination"
|
|
1277
|
+
id="githubPagination"
|
|
1278
|
+
style={{ display: "none" }}
|
|
1279
|
+
>
|
|
1280
|
+
<button id="githubPrevPage" className="github-page-btn" disabled>
|
|
1281
|
+
← Previous
|
|
1282
|
+
</button>
|
|
1283
|
+
<span id="githubPageInfo" className="github-page-info">
|
|
1284
|
+
Page 1
|
|
1285
|
+
</span>
|
|
1286
|
+
<button id="githubNextPage" className="github-page-btn">
|
|
1287
|
+
Next →
|
|
1288
|
+
</button>
|
|
1289
|
+
</div>
|
|
1290
|
+
</div>
|
|
1291
|
+
</div>
|
|
1292
|
+
<script
|
|
1293
|
+
dangerouslySetInnerHTML={{
|
|
1294
|
+
__html: `
|
|
1295
|
+
if ('serviceWorker' in navigator) {
|
|
1296
|
+
navigator.serviceWorker.register('/api/sw.js', { scope: '/' })
|
|
1297
|
+
.catch(function(e) { console.warn('[SW] Registration failed:', e); });
|
|
1298
|
+
}
|
|
1299
|
+
fetch('/api/analytics', {
|
|
1300
|
+
method: 'POST',
|
|
1301
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1302
|
+
body: JSON.stringify({ path: location.pathname })
|
|
1303
|
+
}).catch(function(){});
|
|
1304
|
+
`,
|
|
1305
|
+
}}
|
|
1306
|
+
/>
|
|
1307
|
+
</body>
|
|
1308
|
+
</html>
|
|
1309
|
+
);
|
|
493
1310
|
}
|