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,308 @@
1
+ // @ts-nocheck
2
+ /**
3
+ * Chat — AI chat sidebar for files and canvas.
4
+ * Uses melina/client render + JSX instead of innerHTML.
5
+ */
6
+ import { measure } from 'measure-fn';
7
+ import { render } from 'melina/client';
8
+ import type { CanvasContext } from './context';
9
+ import { escapeHtml } from './utils';
10
+
11
+ interface ChatMessage {
12
+ role: 'user' | 'assistant';
13
+ content: string;
14
+ }
15
+
16
+ interface ChatState {
17
+ messages: ChatMessage[];
18
+ isStreaming: boolean;
19
+ fileContext?: { path: string; content: string; status: string; diff?: string };
20
+ canvasContext?: { repoPath: string; commitHash: string; commitMessage: string; files: any[] };
21
+ }
22
+
23
+ // Per-file chat histories
24
+ const fileChatStates = new Map<string, ChatState>();
25
+ let canvasChatState: ChatState = { messages: [], isStreaming: false };
26
+
27
+ // ─── Render markdown-ish content to safe HTML string ────
28
+ function renderChatContent(text: string): string {
29
+ let html = escapeHtml(text);
30
+ html = html.replace(/```(\w+)?\n([\s\S]*?)```/g, (_, lang, code) =>
31
+ `<pre class="chat-code-block"><code class="language-${lang || 'text'}">${code.trim()}</code></pre>`);
32
+ html = html.replace(/`([^`]+)`/g, '<code class="chat-inline-code">$1</code>');
33
+ html = html.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');
34
+ html = html.replace(/\*([^*]+)\*/g, '<em>$1</em>');
35
+ html = html.replace(/\n/g, '<br>');
36
+ return html;
37
+ }
38
+
39
+ // ─── JSX Components ─────────────────────────────────────
40
+
41
+ function TypingIndicator() {
42
+ return (
43
+ <div className="chat-message chat-assistant">
44
+ <div className="chat-message-role">AI</div>
45
+ <div className="chat-message-content chat-typing">
46
+ <span className="typing-dot"></span>
47
+ <span className="typing-dot"></span>
48
+ <span className="typing-dot"></span>
49
+ </div>
50
+ </div>
51
+ );
52
+ }
53
+
54
+ function EmptyChat() {
55
+ return (
56
+ <div className="chat-empty">
57
+ <span style="opacity:0.3;font-size:28px">💬</span>
58
+ <span>Ask AI about this code</span>
59
+ </div>
60
+ );
61
+ }
62
+
63
+ function MessageList({ messages, isTyping }: { messages: ChatMessage[]; isTyping: boolean }) {
64
+ if (messages.length === 0 && !isTyping) {
65
+ return <EmptyChat />;
66
+ }
67
+ return (
68
+ <div className="chat-message-list-inner">
69
+ {messages.map((msg, i) => (
70
+ <div key={i} className={`chat-message chat-${msg.role}`}>
71
+ <div className="chat-message-role">{msg.role === 'user' ? 'You' : 'AI'}</div>
72
+ <div className="chat-message-content" dangerouslySetInnerHTML={{ __html: renderChatContent(msg.content) }} />
73
+ </div>
74
+ ))}
75
+ {isTyping && <TypingIndicator />}
76
+ </div>
77
+ );
78
+ }
79
+
80
+ function ChatPanel({
81
+ containerId, title, messages, isTyping, onSend, onClose
82
+ }: {
83
+ containerId: string; title: string; messages: ChatMessage[];
84
+ isTyping: boolean; onSend: (text: string) => void; onClose: () => void;
85
+ }) {
86
+ const handleSend = () => {
87
+ const input = document.getElementById(`${containerId}Input`) as HTMLTextAreaElement;
88
+ if (input && input.value.trim()) {
89
+ onSend(input.value);
90
+ input.value = '';
91
+ }
92
+ };
93
+
94
+ const handleKeydown = (e: KeyboardEvent) => {
95
+ if (e.key === 'Enter' && !e.shiftKey) {
96
+ e.preventDefault();
97
+ handleSend();
98
+ }
99
+ };
100
+
101
+ return (
102
+ <div className="chat-panel" id={containerId}>
103
+ <div className="chat-header">
104
+ <div className="chat-header-left">
105
+ <span style="font-size:14px">💬</span>
106
+ <span className="chat-title">{title}</span>
107
+ </div>
108
+ <button className="chat-close btn-ghost btn-xs" onClick={onClose}>✕</button>
109
+ </div>
110
+ <div className="chat-messages" id={`${containerId}Messages`}>
111
+ <MessageList messages={messages} isTyping={isTyping} />
112
+ </div>
113
+ <div className="chat-input-area">
114
+ <textarea
115
+ className="chat-input"
116
+ id={`${containerId}Input`}
117
+ placeholder="Ask about this code..."
118
+ rows={2}
119
+ onKeydown={handleKeydown}
120
+ />
121
+ <button className="chat-send" id={`${containerId}Send`} onClick={handleSend} disabled={isTyping}>
122
+
123
+ </button>
124
+ </div>
125
+ </div>
126
+ );
127
+ }
128
+
129
+ // ─── Render/re-render a chat panel into a container ─────
130
+ function renderChatPanel(container: HTMLElement, containerId: string, title: string, state: ChatState) {
131
+ const onSend = (text: string) => {
132
+ if (!text.trim() || state.isStreaming) return;
133
+ state.messages = [...state.messages, { role: 'user', content: text.trim() }];
134
+ rerender();
135
+ streamResponse(state, container, containerId, title, () => rerender());
136
+ };
137
+
138
+ const onClose = () => {
139
+ container.style.display = 'none';
140
+ };
141
+
142
+ function rerender() {
143
+ render(
144
+ <ChatPanel
145
+ containerId={containerId}
146
+ title={title}
147
+ messages={state.messages}
148
+ isTyping={state.isStreaming && state.messages[state.messages.length - 1]?.content === ''}
149
+ onSend={onSend}
150
+ onClose={onClose}
151
+ />,
152
+ container
153
+ );
154
+ // Auto-scroll
155
+ const msgEl = document.getElementById(`${containerId}Messages`);
156
+ if (msgEl) msgEl.scrollTop = msgEl.scrollHeight;
157
+ }
158
+
159
+ rerender();
160
+ return rerender;
161
+ }
162
+
163
+ // ─── Stream a response from the API ─────────────────────
164
+ async function streamResponse(
165
+ state: ChatState,
166
+ container: HTMLElement,
167
+ containerId: string,
168
+ title: string,
169
+ onUpdate: () => void
170
+ ) {
171
+ if (state.isStreaming) return;
172
+ state.isStreaming = true;
173
+
174
+ // Add assistant placeholder
175
+ state.messages = [...state.messages, { role: 'assistant', content: '' }];
176
+ const assistantIdx = state.messages.length - 1;
177
+ onUpdate();
178
+
179
+ try {
180
+ const body: any = {
181
+ messages: state.messages.slice(0, -1),
182
+ };
183
+ if (state.fileContext) body.fileContext = state.fileContext;
184
+ if (state.canvasContext) body.canvasContext = state.canvasContext;
185
+
186
+ const response = await fetch('/api/chat', {
187
+ method: 'POST',
188
+ headers: { 'Content-Type': 'application/json' },
189
+ body: JSON.stringify(body),
190
+ });
191
+
192
+ if (!response.ok) throw new Error(await response.text());
193
+
194
+ const reader = response.body?.getReader();
195
+ const decoder = new TextDecoder();
196
+ let fullText = '';
197
+ let buffer = '';
198
+
199
+ while (reader) {
200
+ const { done, value } = await reader.read();
201
+ if (done) break;
202
+
203
+ buffer += decoder.decode(value, { stream: true });
204
+ const lines = buffer.split('\n');
205
+ buffer = lines.pop() || '';
206
+
207
+ for (const line of lines) {
208
+ if (!line.startsWith('data: ')) continue;
209
+ const data = line.slice(6).trim();
210
+ if (!data || data === '[DONE]') break;
211
+
212
+ try {
213
+ const parsed = JSON.parse(data);
214
+ if (parsed.error) {
215
+ fullText += `\n\n**Error:** ${parsed.error}`;
216
+ state.messages = [...state.messages];
217
+ state.messages[assistantIdx].content = fullText;
218
+ onUpdate();
219
+ break;
220
+ }
221
+ if (parsed.text) {
222
+ fullText += parsed.text;
223
+ state.messages = [...state.messages];
224
+ state.messages[assistantIdx].content = fullText;
225
+ onUpdate();
226
+ }
227
+ } catch (e) { /* skip */ }
228
+ }
229
+ }
230
+
231
+ if (!fullText) state.messages[assistantIdx].content = '*No response received.*';
232
+
233
+ } catch (err) {
234
+ state.messages = [...state.messages];
235
+ state.messages[assistantIdx].content = `**Error:** ${err.message}`;
236
+ }
237
+
238
+ state.isStreaming = false;
239
+ onUpdate();
240
+ }
241
+
242
+ // ─── Open file chat in modal ────────────────────────────
243
+ export function openFileChatInModal(filePath: string, content: string, status: string, diff?: string) {
244
+ measure('chat:openFileChat', () => {
245
+ if (!fileChatStates.has(filePath)) {
246
+ fileChatStates.set(filePath, {
247
+ messages: [], isStreaming: false,
248
+ fileContext: { path: filePath, content, status, diff },
249
+ });
250
+ }
251
+ const state = fileChatStates.get(filePath)!;
252
+ state.fileContext = { path: filePath, content, status, diff };
253
+
254
+ let chatContainer = document.getElementById('modalChatContainer');
255
+ if (!chatContainer) return;
256
+
257
+ chatContainer.style.display = 'flex';
258
+ renderChatPanel(chatContainer, 'modalFileChat', 'AI Chat', state);
259
+ });
260
+ }
261
+
262
+ // ─── Open canvas-wide AI chat sidebar ───────────────────
263
+ export function openCanvasChat(ctx: CanvasContext) {
264
+ measure('chat:openCanvasChat', () => {
265
+ const state = ctx.snap().context;
266
+ const selected = state.selectedCards || [];
267
+
268
+ const files: any[] = [];
269
+ ctx.fileCards.forEach((card, path) => {
270
+ if (selected.length > 0 && !selected.includes(path)) return;
271
+ const status = card.querySelector('.diff-badge')?.textContent || 'file';
272
+ files.push({ path, status });
273
+ });
274
+
275
+ canvasChatState.canvasContext = {
276
+ repoPath: state.repoPath,
277
+ commitHash: state.currentCommitHash || '',
278
+ commitMessage: '',
279
+ files,
280
+ };
281
+
282
+ let chatSidebar = document.getElementById('canvasChatWrapper');
283
+ if (!chatSidebar) {
284
+ const main = document.querySelector('.canvas-area');
285
+ if (!main) return;
286
+ chatSidebar = document.createElement('div');
287
+ chatSidebar.id = 'canvasChatWrapper';
288
+ chatSidebar.style.display = 'flex';
289
+ main.appendChild(chatSidebar);
290
+ }
291
+
292
+ chatSidebar.style.display = 'flex';
293
+ renderChatPanel(chatSidebar, 'canvasChat', 'Canvas AI', canvasChatState);
294
+
295
+ const input = document.getElementById('canvasChatInput') as HTMLTextAreaElement;
296
+ if (input) setTimeout(() => input.focus(), 100);
297
+ });
298
+ }
299
+
300
+ // ─── Toggle canvas chat ─────────────────────────────────
301
+ export function toggleCanvasChat(ctx: CanvasContext) {
302
+ const chatSidebar = document.getElementById('canvasChatWrapper');
303
+ if (chatSidebar && chatSidebar.style.display !== 'none') {
304
+ chatSidebar.style.display = 'none';
305
+ } else {
306
+ openCanvasChat(ctx);
307
+ }
308
+ }