gitmaps 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/README.md +167 -0
  2. package/app/api/auth/favorites/route.ts +56 -0
  3. package/app/api/auth/github/callback/route.ts +103 -0
  4. package/app/api/auth/github/route.ts +32 -0
  5. package/app/api/auth/me/route.ts +52 -0
  6. package/app/api/auth/positions/route.ts +50 -0
  7. package/app/api/chat/route.ts +101 -0
  8. package/app/api/connections/route.ts +72 -0
  9. package/app/api/github/repos/route.ts +111 -0
  10. package/app/api/positions/route.ts +80 -0
  11. package/app/api/repo/branch-diff/route.ts +201 -0
  12. package/app/api/repo/branches/route.ts +53 -0
  13. package/app/api/repo/browse/route.ts +55 -0
  14. package/app/api/repo/clone/route.ts +78 -0
  15. package/app/api/repo/clone-stream/route.ts +131 -0
  16. package/app/api/repo/file-content/route.ts +28 -0
  17. package/app/api/repo/file-delete/route.ts +62 -0
  18. package/app/api/repo/file-history/route.ts +45 -0
  19. package/app/api/repo/file-rename/route.ts +83 -0
  20. package/app/api/repo/file-save/route.ts +45 -0
  21. package/app/api/repo/files/route.ts +169 -0
  22. package/app/api/repo/git-blame/route.ts +86 -0
  23. package/app/api/repo/git-commit/route.ts +40 -0
  24. package/app/api/repo/git-heatmap/route.ts +55 -0
  25. package/app/api/repo/imports/route.ts +154 -0
  26. package/app/api/repo/load/route.ts +56 -0
  27. package/app/api/repo/mode/route.ts +14 -0
  28. package/app/api/repo/search/route.ts +127 -0
  29. package/app/api/repo/tree/route.ts +104 -0
  30. package/app/api/repo/upload/route.ts +53 -0
  31. package/app/api/repo/validate-path.ts +53 -0
  32. package/app/canvas_users.db +0 -0
  33. package/app/canvas_users.db-shm +0 -0
  34. package/app/canvas_users.db-wal +0 -0
  35. package/app/globals.css +7899 -0
  36. package/app/layout.tsx +493 -0
  37. package/app/lib/auth.ts +193 -0
  38. package/app/lib/auto-save.ts +137 -0
  39. package/app/lib/branch-compare.ts +443 -0
  40. package/app/lib/breadcrumbs.ts +170 -0
  41. package/app/lib/canvas-export.ts +358 -0
  42. package/app/lib/canvas-text.ts +912 -0
  43. package/app/lib/canvas.ts +564 -0
  44. package/app/lib/card-arrangement.ts +188 -0
  45. package/app/lib/card-context-menu.tsx +453 -0
  46. package/app/lib/card-diff-markers.ts +270 -0
  47. package/app/lib/card-expand.ts +189 -0
  48. package/app/lib/card-groups.ts +246 -0
  49. package/app/lib/cards.tsx +914 -0
  50. package/app/lib/chat.tsx +308 -0
  51. package/app/lib/code-editor.ts +508 -0
  52. package/app/lib/command-palette.ts +262 -0
  53. package/app/lib/connections.tsx +1037 -0
  54. package/app/lib/context.ts +94 -0
  55. package/app/lib/cursor-sharing.ts +281 -0
  56. package/app/lib/dependency-graph.ts +438 -0
  57. package/app/lib/events.tsx +1747 -0
  58. package/app/lib/file-card-plugin.ts +134 -0
  59. package/app/lib/file-modal.tsx +849 -0
  60. package/app/lib/file-preview.ts +400 -0
  61. package/app/lib/file-tabs.ts +318 -0
  62. package/app/lib/galaxydraw-bridge.ts +477 -0
  63. package/app/lib/galaxydraw.test.ts +229 -0
  64. package/app/lib/global-search.ts +264 -0
  65. package/app/lib/goto-definition.ts +224 -0
  66. package/app/lib/heatmap.ts +178 -0
  67. package/app/lib/hidden-files.tsx +222 -0
  68. package/app/lib/layers.ts +0 -0
  69. package/app/lib/layers.tsx +365 -0
  70. package/app/lib/loading.tsx +45 -0
  71. package/app/lib/multi-repo.ts +286 -0
  72. package/app/lib/new-file-dialog.tsx +230 -0
  73. package/app/lib/onboarding.tsx +213 -0
  74. package/app/lib/perf-overlay.ts +360 -0
  75. package/app/lib/positions.ts +176 -0
  76. package/app/lib/pr-review.ts +374 -0
  77. package/app/lib/production-mode.ts +47 -0
  78. package/app/lib/repo.tsx +977 -0
  79. package/app/lib/settings-modal.tsx +374 -0
  80. package/app/lib/settings.ts +97 -0
  81. package/app/lib/shortcuts-panel.ts +141 -0
  82. package/app/lib/status-bar.ts +128 -0
  83. package/app/lib/symbol-outline.ts +212 -0
  84. package/app/lib/syntax.ts +177 -0
  85. package/app/lib/tab-diff.ts +238 -0
  86. package/app/lib/user.tsx +133 -0
  87. package/app/lib/utils.ts +78 -0
  88. package/app/lib/viewport-culling.ts +728 -0
  89. package/app/page.client.tsx +215 -0
  90. package/app/page.tsx +291 -0
  91. package/app/state/machine.js +196 -0
  92. package/app/styles/main.css +2168 -0
  93. package/banner.png +0 -0
  94. package/cli.ts +44 -0
  95. package/package.json +75 -0
  96. package/packages/galaxydraw/README.md +296 -0
  97. package/packages/galaxydraw/banner.png +0 -0
  98. package/packages/galaxydraw/demo/build-static.ts +100 -0
  99. package/packages/galaxydraw/demo/client.ts +154 -0
  100. package/packages/galaxydraw/demo/dist/client.js +8 -0
  101. package/packages/galaxydraw/demo/index.html +256 -0
  102. package/packages/galaxydraw/demo/server.ts +96 -0
  103. package/packages/galaxydraw/dist/index.js +984 -0
  104. package/packages/galaxydraw/dist/index.js.map +16 -0
  105. package/packages/galaxydraw/node_modules/.bin/tsc.bunx +0 -0
  106. package/packages/galaxydraw/node_modules/.bin/tsc.exe +0 -0
  107. package/packages/galaxydraw/node_modules/.bin/tsserver.bunx +0 -0
  108. package/packages/galaxydraw/node_modules/.bin/tsserver.exe +0 -0
  109. package/packages/galaxydraw/package.json +49 -0
  110. package/packages/galaxydraw/perf.test.ts +284 -0
  111. package/packages/galaxydraw/src/core/cards.ts +435 -0
  112. package/packages/galaxydraw/src/core/engine.ts +339 -0
  113. package/packages/galaxydraw/src/core/events.ts +81 -0
  114. package/packages/galaxydraw/src/core/layout.ts +136 -0
  115. package/packages/galaxydraw/src/core/minimap.ts +216 -0
  116. package/packages/galaxydraw/src/core/state.ts +177 -0
  117. package/packages/galaxydraw/src/core/viewport.ts +106 -0
  118. package/packages/galaxydraw/src/galaxydraw.css +166 -0
  119. package/packages/galaxydraw/src/index.ts +40 -0
  120. package/packages/galaxydraw/tsconfig.json +30 -0
  121. package/server.ts +62 -0
@@ -0,0 +1,318 @@
1
+ // @ts-nocheck
2
+ /**
3
+ * Multi-tab file management for the modal.
4
+ * Allows opening multiple files that persist as tabs.
5
+ * Tab paths are saved to localStorage for session persistence.
6
+ */
7
+
8
+ const TAB_STORAGE_KEY = 'gitcanvas:openTabs';
9
+
10
+ export interface FileTab {
11
+ path: string;
12
+ name: string;
13
+ file: any; // Original file data object
14
+ rendered: { full: string; diff: string; full_raw: string };
15
+ currentView: string; // 'full' | 'diff' | 'edit' | 'chat'
16
+ scrollTop: number;
17
+ originalContent: string;
18
+ }
19
+
20
+ // ─── Global tab state ───────────────────────────────
21
+ let openTabs: FileTab[] = [];
22
+ let activeTabIndex = -1;
23
+ let tabBarEl: HTMLElement | null = null;
24
+
25
+ export function getOpenTabs(): FileTab[] { return openTabs; }
26
+ export function getActiveTab(): FileTab | null { return openTabs[activeTabIndex] || null; }
27
+ export function getActiveTabIndex(): number { return activeTabIndex; }
28
+
29
+ /**
30
+ * Initialize the tab bar element in the modal.
31
+ * Should be called once during app setup.
32
+ */
33
+ export function initTabBar() {
34
+ if (tabBarEl) return tabBarEl;
35
+
36
+ const modal = document.getElementById('filePreviewModal');
37
+ if (!modal) return null;
38
+
39
+ const modalContent = modal.querySelector('.modal-content');
40
+ const header = modal.querySelector('.modal-header');
41
+ if (!modalContent || !header) return null;
42
+
43
+ // Check if tab bar already exists
44
+ tabBarEl = document.getElementById('modalFileTabBar');
45
+ if (!tabBarEl) {
46
+ tabBarEl = document.createElement('div');
47
+ tabBarEl.id = 'modalFileTabBar';
48
+ tabBarEl.className = 'modal-file-tab-bar';
49
+ // Insert after the header
50
+ header.after(tabBarEl);
51
+ }
52
+
53
+ return tabBarEl;
54
+ }
55
+
56
+ /**
57
+ * Add a file to the tab bar or activate it if already open.
58
+ * Returns the tab index.
59
+ */
60
+ export function addTab(file: any): number {
61
+ // Check if already open
62
+ const existing = openTabs.findIndex(t => t.path === file.path);
63
+ if (existing >= 0) {
64
+ activeTabIndex = existing;
65
+ renderTabBar();
66
+ return existing;
67
+ }
68
+
69
+ const tab: FileTab = {
70
+ path: file.path,
71
+ name: file.name || file.path?.split('/').pop() || 'untitled',
72
+ file,
73
+ rendered: { full: '', diff: '', full_raw: '' },
74
+ currentView: 'full',
75
+ scrollTop: 0,
76
+ originalContent: file.content || '',
77
+ };
78
+
79
+ openTabs.push(tab);
80
+ activeTabIndex = openTabs.length - 1;
81
+ renderTabBar();
82
+ _persistTabPaths();
83
+ return activeTabIndex;
84
+ }
85
+
86
+ /**
87
+ * Close a tab by index. Returns the new active tab index, or -1 if none left.
88
+ */
89
+ export function closeTab(index: number, hasUnsavedChanges?: () => boolean): number {
90
+ if (index < 0 || index >= openTabs.length) return activeTabIndex;
91
+
92
+ // Check for unsaved changes on the active tab
93
+ if (index === activeTabIndex && hasUnsavedChanges?.()) {
94
+ if (!confirm('You have unsaved changes. Discard them?')) return activeTabIndex;
95
+ }
96
+
97
+ openTabs.splice(index, 1);
98
+
99
+ if (openTabs.length === 0) {
100
+ activeTabIndex = -1;
101
+ renderTabBar();
102
+ return -1;
103
+ }
104
+
105
+ // Adjust active index
106
+ if (activeTabIndex >= openTabs.length) {
107
+ activeTabIndex = openTabs.length - 1;
108
+ } else if (index < activeTabIndex) {
109
+ activeTabIndex--;
110
+ }
111
+
112
+ renderTabBar();
113
+ _persistTabPaths();
114
+ return activeTabIndex;
115
+ }
116
+
117
+ /**
118
+ * Set the active tab and update tab bar rendering.
119
+ */
120
+ export function setActiveTab(index: number): void {
121
+ if (index < 0 || index >= openTabs.length) return;
122
+ activeTabIndex = index;
123
+ renderTabBar();
124
+ }
125
+
126
+ /**
127
+ * Cycle to the next tab (Ctrl+Tab).
128
+ */
129
+ export function nextTab(): number {
130
+ if (openTabs.length <= 1) return activeTabIndex;
131
+ activeTabIndex = (activeTabIndex + 1) % openTabs.length;
132
+ renderTabBar();
133
+ return activeTabIndex;
134
+ }
135
+
136
+ /**
137
+ * Cycle to the previous tab (Ctrl+Shift+Tab).
138
+ */
139
+ export function prevTab(): number {
140
+ if (openTabs.length <= 1) return activeTabIndex;
141
+ activeTabIndex = (activeTabIndex - 1 + openTabs.length) % openTabs.length;
142
+ renderTabBar();
143
+ return activeTabIndex;
144
+ }
145
+
146
+ /**
147
+ * Clear all tabs (on modal close).
148
+ * Keeps saved tab paths in localStorage for session restore.
149
+ */
150
+ export function clearTabs(): void {
151
+ // Persist current tab paths before clearing in-memory state
152
+ _persistTabPaths();
153
+ openTabs = [];
154
+ activeTabIndex = -1;
155
+ renderTabBar();
156
+ }
157
+
158
+ /** Fully clear tabs AND remove from localStorage (discard all) */
159
+ export function clearTabsAndStorage(): void {
160
+ openTabs = [];
161
+ activeTabIndex = -1;
162
+ renderTabBar();
163
+ try { localStorage.removeItem(TAB_STORAGE_KEY); } catch { }
164
+ }
165
+
166
+ /** Get previously saved tab paths for session restore */
167
+ export function getSavedTabPaths(): { paths: string[]; activeIndex: number } {
168
+ try {
169
+ const raw = localStorage.getItem(TAB_STORAGE_KEY);
170
+ if (!raw) return { paths: [], activeIndex: -1 };
171
+ return JSON.parse(raw);
172
+ } catch {
173
+ return { paths: [], activeIndex: -1 };
174
+ }
175
+ }
176
+
177
+ /** Save current tab paths to localStorage */
178
+ function _persistTabPaths(): void {
179
+ try {
180
+ if (openTabs.length === 0) return; // Don't overwrite with empty on close
181
+ const data = {
182
+ paths: openTabs.map(t => t.path),
183
+ activeIndex: activeTabIndex,
184
+ };
185
+ localStorage.setItem(TAB_STORAGE_KEY, JSON.stringify(data));
186
+ } catch { }
187
+ }
188
+
189
+ // ─── Tab change callback ────────────────────────────
190
+ let _onTabChange: ((tab: FileTab, index: number) => void) | null = null;
191
+ let _onTabClose: ((index: number) => boolean) | null = null;
192
+
193
+ export function onTabChange(cb: (tab: FileTab, index: number) => void) {
194
+ _onTabChange = cb;
195
+ }
196
+
197
+ export function onTabCloseRequest(cb: (index: number) => boolean) {
198
+ _onTabClose = cb;
199
+ }
200
+
201
+ // ─── Language icons ─────────────────────────────────
202
+ function getFileIcon(name: string): string {
203
+ const ext = name.split('.').pop()?.toLowerCase() || '';
204
+ const icons: Record<string, string> = {
205
+ ts: '🔷', tsx: '⚛️', js: '🟨', jsx: '⚛️',
206
+ py: '🐍', css: '🎨', html: '🌐', json: '📋',
207
+ md: '📝', yaml: '⚙️', yml: '⚙️', toml: '⚙️',
208
+ sh: '🐚', sql: '🗃️', rs: '🦀', go: '🐹',
209
+ svg: '🖼️', png: '🖼️', jpg: '🖼️',
210
+ };
211
+ return icons[ext] || '📄';
212
+ }
213
+
214
+ // ─── Render tab bar ─────────────────────────────────
215
+ function renderTabBar() {
216
+ if (!tabBarEl) initTabBar();
217
+ if (!tabBarEl) return;
218
+
219
+ // Hide tab bar if no tabs or only 1
220
+ if (openTabs.length <= 1) {
221
+ tabBarEl.style.display = 'none';
222
+ return;
223
+ }
224
+
225
+ tabBarEl.style.display = 'flex';
226
+ tabBarEl.innerHTML = '';
227
+
228
+ openTabs.forEach((tab, i) => {
229
+ const tabEl = document.createElement('div');
230
+ tabEl.className = `file-tab${i === activeTabIndex ? ' active' : ''}`;
231
+ tabEl.title = tab.path;
232
+
233
+ const icon = document.createElement('span');
234
+ icon.className = 'file-tab-icon';
235
+ icon.textContent = getFileIcon(tab.name);
236
+ tabEl.appendChild(icon);
237
+
238
+ const label = document.createElement('span');
239
+ label.className = 'file-tab-label';
240
+ label.textContent = tab.name;
241
+ tabEl.appendChild(label);
242
+
243
+ // Modified indicator
244
+ if (tab.currentView === 'edit' && tab.rendered.full_raw && tab.rendered.full_raw !== tab.originalContent) {
245
+ const dot = document.createElement('span');
246
+ dot.className = 'file-tab-modified';
247
+ dot.textContent = '●';
248
+ tabEl.appendChild(dot);
249
+ }
250
+
251
+ // Close button
252
+ const close = document.createElement('button');
253
+ close.className = 'file-tab-close';
254
+ close.textContent = '×';
255
+ close.title = 'Close tab';
256
+ close.addEventListener('click', (e) => {
257
+ e.stopPropagation();
258
+ // Check callback for unsaved changes
259
+ if (_onTabClose && !_onTabClose(i)) return;
260
+ const newIndex = closeTab(i);
261
+ if (newIndex === -1) {
262
+ // No tabs left — close modal
263
+ const modal = document.getElementById('filePreviewModal');
264
+ if (modal) modal.classList.remove('active');
265
+ } else if (_onTabChange) {
266
+ _onTabChange(openTabs[newIndex], newIndex);
267
+ }
268
+ });
269
+ tabEl.appendChild(close);
270
+
271
+ // Click to switch
272
+ tabEl.addEventListener('click', () => {
273
+ if (i === activeTabIndex) return;
274
+
275
+ // Save current tab's scroll position
276
+ const currentTab = openTabs[activeTabIndex];
277
+ if (currentTab) {
278
+ const bodyPre = document.getElementById('modalBodyPre');
279
+ if (bodyPre) currentTab.scrollTop = bodyPre.scrollTop;
280
+ }
281
+
282
+ setActiveTab(i);
283
+ if (_onTabChange) _onTabChange(openTabs[i], i);
284
+ });
285
+
286
+ // Middle-click to close
287
+ tabEl.addEventListener('auxclick', (e) => {
288
+ if (e.button === 1) { // middle click
289
+ e.preventDefault();
290
+ if (_onTabClose && !_onTabClose(i)) return;
291
+ const newIndex = closeTab(i);
292
+ if (newIndex === -1) {
293
+ const modal = document.getElementById('filePreviewModal');
294
+ if (modal) modal.classList.remove('active');
295
+ } else if (_onTabChange) {
296
+ _onTabChange(openTabs[newIndex], newIndex);
297
+ }
298
+ }
299
+ });
300
+
301
+ tabBarEl!.appendChild(tabEl);
302
+ });
303
+
304
+ // Diff button — only when 2+ tabs are open
305
+ if (openTabs.length >= 2) {
306
+ const diffBtn = document.createElement('button');
307
+ diffBtn.className = 'file-tab file-tab-diff-btn';
308
+ diffBtn.title = 'Compare files (Diff)';
309
+ diffBtn.innerHTML = '<span style="font-size:11px">⇄ Diff</span>';
310
+ diffBtn.style.cssText = 'margin-left: auto; opacity: 0.6; font-size: 11px; border: 1px dashed rgba(139,92,246,0.3);';
311
+ diffBtn.addEventListener('click', () => {
312
+ import('./tab-diff').then(({ openTabDiffSelector }) => openTabDiffSelector());
313
+ });
314
+ diffBtn.addEventListener('mouseenter', () => { diffBtn.style.opacity = '1'; });
315
+ diffBtn.addEventListener('mouseleave', () => { diffBtn.style.opacity = '0.6'; });
316
+ tabBarEl!.appendChild(diffBtn);
317
+ }
318
+ }