gitmaps 1.1.0 → 1.1.2

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 +267 -118
  2. package/app/[...slug]/page.client.tsx +1 -0
  3. package/app/[...slug]/page.tsx +6 -0
  4. package/app/analytics.db +0 -0
  5. package/app/api/analytics/route.ts +64 -0
  6. package/app/api/auth/positions/route.ts +95 -33
  7. package/app/api/build-info/route.ts +19 -0
  8. package/app/api/chat/route.ts +13 -2
  9. package/app/api/og-image/route.ts +14 -0
  10. package/app/api/repo/file-content/route.ts +73 -20
  11. package/app/api/repo/load/route.test.ts +62 -0
  12. package/app/api/repo/load/route.ts +41 -1
  13. package/app/api/repo/pdf-thumb/route.ts +127 -0
  14. package/app/api/repo/resolve-slug/route.ts +51 -0
  15. package/app/api/repo/tree/route.ts +188 -104
  16. package/app/api/version/route.ts +26 -0
  17. package/app/globals.css +5706 -4938
  18. package/app/layout.tsx +1279 -490
  19. package/app/lib/auto-arrange.test.ts +158 -0
  20. package/app/lib/auto-arrange.ts +147 -0
  21. package/app/lib/canvas-export.ts +358 -358
  22. package/app/lib/canvas.ts +625 -564
  23. package/app/lib/cards.tsx +1361 -916
  24. package/app/lib/chat.tsx +65 -9
  25. package/app/lib/code-editor.ts +86 -2
  26. package/app/lib/context.test.ts +32 -0
  27. package/app/lib/context.ts +19 -3
  28. package/app/lib/cursor-sharing.ts +34 -0
  29. package/app/lib/events.tsx +71 -93
  30. package/app/lib/export-canvas.ts +287 -0
  31. package/app/lib/file-card-plugin.ts +148 -148
  32. package/app/lib/file-modal.tsx +49 -0
  33. package/app/lib/file-preview.ts +486 -427
  34. package/app/lib/github-import.test.ts +424 -0
  35. package/app/lib/initial-route-hydration.test.ts +283 -0
  36. package/app/lib/initial-route-hydration.ts +202 -0
  37. package/app/lib/landing-reset.test.ts +99 -0
  38. package/app/lib/landing-reset.ts +106 -0
  39. package/app/lib/landing-shell.test.ts +75 -0
  40. package/app/lib/large-repo-optimization.ts +37 -0
  41. package/app/lib/layout-snapshots.ts +320 -0
  42. package/app/lib/loading.test.ts +69 -0
  43. package/app/lib/loading.tsx +160 -45
  44. package/app/lib/mount-cleanup.test.ts +52 -0
  45. package/app/lib/mount-cleanup.ts +34 -0
  46. package/app/lib/mount-init.test.ts +123 -0
  47. package/app/lib/mount-init.ts +107 -0
  48. package/app/lib/mount-lifecycle.test.ts +39 -0
  49. package/app/lib/mount-lifecycle.ts +12 -0
  50. package/app/lib/mount-route-wiring.test.ts +87 -0
  51. package/app/lib/mount-route-wiring.ts +84 -0
  52. package/app/lib/multi-repo.ts +14 -0
  53. package/app/lib/onboarding-tutorial.ts +278 -0
  54. package/app/lib/positions.ts +190 -121
  55. package/app/lib/recent-commits.test.ts +947 -0
  56. package/app/lib/recent-commits.ts +227 -0
  57. package/app/lib/repo-handoff.test.ts +23 -0
  58. package/app/lib/repo-handoff.ts +16 -0
  59. package/app/lib/repo-progressive.ts +119 -0
  60. package/app/lib/repo-select.test.ts +61 -0
  61. package/app/lib/repo-select.ts +74 -0
  62. package/app/lib/repo.tsx +1383 -987
  63. package/app/lib/role.ts +228 -0
  64. package/app/lib/route-catchall.test.ts +27 -0
  65. package/app/lib/route-repo-entry.test.ts +95 -0
  66. package/app/lib/route-repo-entry.ts +36 -0
  67. package/app/lib/router-contract.test.ts +22 -0
  68. package/app/lib/router-contract.ts +19 -0
  69. package/app/lib/shared-layout.test.ts +86 -0
  70. package/app/lib/shared-layout.ts +82 -0
  71. package/app/lib/status-bar.test.ts +118 -0
  72. package/app/lib/status-bar.ts +365 -128
  73. package/app/lib/sync-controls.test.ts +43 -0
  74. package/app/lib/sync-controls.tsx +303 -0
  75. package/app/lib/test-dom.ts +145 -0
  76. package/app/lib/test-fixtures/router-contract/[...slug]/page.tsx +3 -0
  77. package/app/lib/test-fixtures/router-contract/api/health/route.ts +3 -0
  78. package/app/lib/test-fixtures/router-contract/api/version/route.ts +3 -0
  79. package/app/lib/test-fixtures/router-contract/galaxy-canvas/page.tsx +3 -0
  80. package/app/lib/test-fixtures/router-contract/page.tsx +3 -0
  81. package/app/lib/transclusion-smoke.test.ts +163 -0
  82. package/app/lib/tutorial.ts +301 -0
  83. package/app/lib/version.ts +93 -0
  84. package/app/lib/viewport-culling.ts +740 -735
  85. package/app/lib/virtual-files.ts +456 -0
  86. package/app/lib/webgl-text.ts +189 -0
  87. package/app/lib/{galaxydraw-bridge.ts → xydraw-bridge.ts} +485 -482
  88. package/app/lib/{galaxydraw.test.ts → xydraw.test.ts} +228 -229
  89. package/app/og-image.png +0 -0
  90. package/app/page.client.tsx +70 -269
  91. package/app/page.tsx +15 -16
  92. package/app/state/machine.js +13 -0
  93. package/package.json +84 -75
  94. package/server.ts +10 -0
  95. package/app/[owner]/[repo]/page.tsx +0 -6
  96. package/app/[slug]/page.tsx +0 -6
  97. package/packages/galaxydraw/README.md +0 -296
  98. package/packages/galaxydraw/banner.png +0 -0
  99. package/packages/galaxydraw/demo/build-static.ts +0 -100
  100. package/packages/galaxydraw/demo/client.ts +0 -154
  101. package/packages/galaxydraw/demo/dist/client.js +0 -8
  102. package/packages/galaxydraw/demo/index.html +0 -256
  103. package/packages/galaxydraw/demo/server.ts +0 -96
  104. package/packages/galaxydraw/dist/index.js +0 -984
  105. package/packages/galaxydraw/dist/index.js.map +0 -16
  106. package/packages/galaxydraw/node_modules/.bin/tsc.bunx +0 -0
  107. package/packages/galaxydraw/node_modules/.bin/tsc.exe +0 -0
  108. package/packages/galaxydraw/node_modules/.bin/tsserver.bunx +0 -0
  109. package/packages/galaxydraw/node_modules/.bin/tsserver.exe +0 -0
  110. package/packages/galaxydraw/package.json +0 -49
  111. package/packages/galaxydraw/perf.test.ts +0 -284
  112. package/packages/galaxydraw/src/core/cards.ts +0 -435
  113. package/packages/galaxydraw/src/core/engine.ts +0 -339
  114. package/packages/galaxydraw/src/core/events.ts +0 -81
  115. package/packages/galaxydraw/src/core/layout.ts +0 -136
  116. package/packages/galaxydraw/src/core/minimap.ts +0 -216
  117. package/packages/galaxydraw/src/core/state.ts +0 -177
  118. package/packages/galaxydraw/src/core/viewport.ts +0 -106
  119. package/packages/galaxydraw/src/galaxydraw.css +0 -166
  120. package/packages/galaxydraw/src/index.ts +0 -40
  121. package/packages/galaxydraw/tsconfig.json +0 -30
@@ -1,435 +0,0 @@
1
- /**
2
- * CardManager — Manages cards (containers/widgets) on the canvas
3
- *
4
- * Handles:
5
- * - Card creation with custom content (via plugins)
6
- * - Drag, resize, z-order
7
- * - Selection (single, multi, rect-select)
8
- * - Collapse/expand
9
- * - Virtualized deferred rendering
10
- */
11
-
12
- import type { CanvasState, ViewportRect } from './state';
13
- import type { EventBus } from './events';
14
-
15
- // ─── Types ───────────────────────────────────────────────
16
-
17
- export interface CardData {
18
- id: string;
19
- x: number;
20
- y: number;
21
- width: number;
22
- height: number;
23
- collapsed?: boolean;
24
- /** Arbitrary data attached by the consumer */
25
- meta?: Record<string, any>;
26
- }
27
-
28
- export interface CardOptions {
29
- /** Default card width */
30
- defaultWidth?: number;
31
- /** Default card height */
32
- defaultHeight?: number;
33
- /** Minimum card width when resizing */
34
- minWidth?: number;
35
- /** Minimum card height when resizing */
36
- minHeight?: number;
37
- /** Grid snap size (0 = no snap) */
38
- gridSize?: number;
39
- /** Corner hit area for resize handle */
40
- cornerSize?: number;
41
- }
42
-
43
- /**
44
- * CardPlugin — how consumers define custom card types.
45
- *
46
- * Example: GitMaps provides a FileCardPlugin that renders source code.
47
- * WARMAPS provides a MapCardPlugin that renders MapLibre.
48
- */
49
- export interface CardPlugin {
50
- /** Unique type identifier */
51
- type: string;
52
- /** Create the DOM element for this card */
53
- render(data: CardData): HTMLElement;
54
- /** Called when the card is resized */
55
- onResize?(card: HTMLElement, width: number, height: number): void;
56
- /** Called when the card is destroyed */
57
- onDestroy?(card: HTMLElement): void;
58
- /** Should wheel events be passed through? (e.g. maps, scrollable content) */
59
- consumesWheel?(target: HTMLElement): boolean;
60
- /** Should mouse events be passed through? (e.g. maps, interactive widgets) */
61
- consumesMouse?(target: HTMLElement): boolean;
62
- }
63
-
64
- const DEFAULT_OPTIONS: Required<CardOptions> = {
65
- defaultWidth: 400,
66
- defaultHeight: 300,
67
- minWidth: 200,
68
- minHeight: 150,
69
- gridSize: 0,
70
- cornerSize: 40,
71
- };
72
-
73
- // ─── CardManager ─────────────────────────────────────────
74
-
75
- export class CardManager {
76
- /** Active DOM cards: id → element */
77
- readonly cards = new Map<string, HTMLElement>();
78
- /** Deferred cards (not yet in DOM): id → data */
79
- readonly deferred = new Map<string, CardData & { plugin?: string }>();
80
- /** Currently selected card IDs */
81
- readonly selected = new Set<string>();
82
-
83
- private topZ = 10;
84
- private plugins = new Map<string, CardPlugin>();
85
- private opts: Required<CardOptions>;
86
-
87
- constructor(
88
- private state: CanvasState,
89
- private bus: EventBus,
90
- private canvas: HTMLElement,
91
- options?: CardOptions,
92
- ) {
93
- this.opts = { ...DEFAULT_OPTIONS, ...options };
94
- }
95
-
96
- // ─── Plugin registration ─────────────────────────────
97
-
98
- registerPlugin(plugin: CardPlugin) {
99
- this.plugins.set(plugin.type, plugin);
100
- }
101
-
102
- // ─── Card lifecycle ──────────────────────────────────
103
-
104
- /** Create a card and add it to the canvas */
105
- create(type: string, data: Partial<CardData> & { id: string }): HTMLElement | null {
106
- const plugin = this.plugins.get(type);
107
- if (!plugin) {
108
- console.warn(`[galaxydraw] No plugin registered for card type "${type}"`);
109
- return null;
110
- }
111
-
112
- const full: CardData = {
113
- x: data.x ?? 0,
114
- y: data.y ?? 0,
115
- width: data.width ?? this.opts.defaultWidth,
116
- height: data.height ?? this.opts.defaultHeight,
117
- collapsed: data.collapsed ?? false,
118
- meta: data.meta ?? {},
119
- ...data,
120
- };
121
-
122
- const el = plugin.render(full);
123
- el.classList.add('gd-card');
124
- el.dataset.cardId = full.id;
125
- el.dataset.cardType = type;
126
- el.style.left = `${full.x}px`;
127
- el.style.top = `${full.y}px`;
128
- el.style.width = `${full.width}px`;
129
- if (!full.collapsed) {
130
- el.style.height = `${full.height}px`;
131
- }
132
-
133
- this.canvas.appendChild(el);
134
- this.cards.set(full.id, el);
135
- this.bringToFront(el);
136
-
137
- this.setupDrag(el);
138
- this.setupResize(el, type);
139
-
140
- this.bus.emit('card:create', { id: full.id, x: full.x, y: full.y });
141
- return el;
142
- }
143
-
144
- /** Remove a card from the canvas and clean up */
145
- remove(id: string) {
146
- const el = this.cards.get(id);
147
- if (!el) {
148
- this.deferred.delete(id);
149
- return;
150
- }
151
-
152
- const type = el.dataset.cardType;
153
- if (type) {
154
- this.plugins.get(type)?.onDestroy?.(el);
155
- }
156
-
157
- el.remove();
158
- this.cards.delete(id);
159
- this.selected.delete(id);
160
- this.bus.emit('card:remove', { id });
161
- }
162
-
163
- /** Defer a card (store data, don't create DOM until it enters viewport) */
164
- defer(type: string, data: CardData) {
165
- this.deferred.set(data.id, { ...data, plugin: type });
166
- }
167
-
168
- /** Materialize deferred cards that overlap the given world rect */
169
- materializeInRect(worldRect: ViewportRect): number {
170
- let count = 0;
171
- const toRemove: string[] = [];
172
-
173
- for (const [id, entry] of this.deferred) {
174
- const { x, y, width, height, plugin } = entry as any;
175
- const w = width || this.opts.defaultWidth;
176
- const h = height || this.opts.defaultHeight;
177
-
178
- if (
179
- x + w > worldRect.left &&
180
- x < worldRect.right &&
181
- y + h > worldRect.top &&
182
- y < worldRect.bottom
183
- ) {
184
- if (plugin) {
185
- this.create(plugin, entry);
186
- }
187
- toRemove.push(id);
188
- count++;
189
- }
190
- }
191
-
192
- for (const id of toRemove) {
193
- this.deferred.delete(id);
194
- }
195
- return count;
196
- }
197
-
198
- /** Remove all cards and deferred entries */
199
- clear() {
200
- for (const [id, el] of this.cards) {
201
- const type = el.dataset.cardType;
202
- if (type) this.plugins.get(type)?.onDestroy?.(el);
203
- el.remove();
204
- }
205
- this.cards.clear();
206
- this.deferred.clear();
207
- this.selected.clear();
208
- }
209
-
210
- // ─── Z-order ─────────────────────────────────────────
211
-
212
- bringToFront(el: HTMLElement) {
213
- this.topZ++;
214
- el.style.zIndex = String(this.topZ);
215
- }
216
-
217
- // ─── Selection ───────────────────────────────────────
218
-
219
- select(id: string, multi = false) {
220
- if (!multi) {
221
- this.deselectAll();
222
- }
223
- this.selected.add(id);
224
- this.cards.get(id)?.classList.add('gd-card--selected');
225
- this.bus.emit('card:select', { ids: [...this.selected] });
226
- }
227
-
228
- deselect(id: string) {
229
- this.selected.delete(id);
230
- this.cards.get(id)?.classList.remove('gd-card--selected');
231
- this.bus.emit('card:deselect', { ids: [id] });
232
- }
233
-
234
- deselectAll() {
235
- for (const id of this.selected) {
236
- this.cards.get(id)?.classList.remove('gd-card--selected');
237
- }
238
- const prev = [...this.selected];
239
- this.selected.clear();
240
- if (prev.length > 0) {
241
- this.bus.emit('card:deselect', { ids: prev });
242
- }
243
- }
244
-
245
- // ─── Collapse ────────────────────────────────────────
246
-
247
- toggleCollapse(id: string) {
248
- const el = this.cards.get(id);
249
- if (!el) return;
250
- const collapsed = el.classList.toggle('gd-card--collapsed');
251
- this.bus.emit('card:collapse', { id, collapsed });
252
- }
253
-
254
- // ─── Drag interaction ────────────────────────────────
255
-
256
- private setupDrag(card: HTMLElement) {
257
- let dragging = false;
258
- let dragStarted = false;
259
- let startScreenX = 0, startScreenY = 0;
260
- let startWorldX = 0, startWorldY = 0;
261
- let moveGroup: { el: HTMLElement; startX: number; startY: number }[] = [];
262
- const DRAG_THRESHOLD = 5;
263
-
264
- card.addEventListener('mousedown', (e: MouseEvent) => {
265
- if (e.button !== 0) return;
266
- // Don't start drag from interactive elements
267
- if ((e.target as HTMLElement).closest('button, a, input, textarea, select, .connect-btn')) return;
268
-
269
- // Don't prevent default yet — allow body scroll until threshold is exceeded
270
- dragging = true;
271
- dragStarted = false;
272
- startScreenX = e.clientX;
273
- startScreenY = e.clientY;
274
- this.bringToFront(card);
275
-
276
- const world = this.state.screenToWorld(e.clientX, e.clientY);
277
- startWorldX = world.x;
278
- startWorldY = world.y;
279
-
280
- // Build move group: if this card is selected and there are other selected cards, move them all
281
- const cardId = card.dataset.cardId || card.dataset.path || '';
282
- moveGroup = [];
283
-
284
- // Collect all selected card IDs from this.selected
285
- const allSelectedIds = new Set<string>(this.selected);
286
-
287
- if (allSelectedIds.has(cardId) && allSelectedIds.size > 1) {
288
- // Multi-drag: move all selected cards
289
- for (const selId of allSelectedIds) {
290
- const el = this.cards.get(selId);
291
- if (el) {
292
- moveGroup.push({
293
- el,
294
- startX: parseFloat(el.style.left) || 0,
295
- startY: parseFloat(el.style.top) || 0,
296
- });
297
- }
298
- }
299
- } else {
300
- // Single card drag
301
- moveGroup = [{
302
- el: card,
303
- startX: parseFloat(card.style.left) || 0,
304
- startY: parseFloat(card.style.top) || 0,
305
- }];
306
- }
307
-
308
- const onMove = (ev: MouseEvent) => {
309
- if (!dragging) return;
310
-
311
- // Check drag threshold before starting actual drag
312
- if (!dragStarted) {
313
- const screenDist = Math.sqrt(
314
- (ev.clientX - startScreenX) ** 2 + (ev.clientY - startScreenY) ** 2
315
- );
316
- if (screenDist < DRAG_THRESHOLD) return;
317
- dragStarted = true;
318
- card.classList.add('gd-card--dragging');
319
- // Prevent default only after threshold is met to avoid text selection etc.
320
- ev.preventDefault();
321
- } else {
322
- // Continue preventing default for subsequent moves if drag has started
323
- ev.preventDefault();
324
- }
325
-
326
- const curr = this.state.screenToWorld(ev.clientX, ev.clientY);
327
- const dx = curr.x - startWorldX;
328
- const dy = curr.y - startWorldY;
329
-
330
- for (const info of moveGroup) {
331
- let newX = info.startX + dx;
332
- let newY = info.startY + dy;
333
-
334
- // Snap to grid
335
- if (this.opts.gridSize > 0 && ev.shiftKey) {
336
- newX = Math.round(newX / this.opts.gridSize) * this.opts.gridSize;
337
- newY = Math.round(newY / this.opts.gridSize) * this.opts.gridSize;
338
- }
339
-
340
- info.el.style.left = `${newX}px`;
341
- info.el.style.top = `${newY}px`;
342
- }
343
- };
344
-
345
- const onUp = () => {
346
- dragging = false;
347
- card.classList.remove('gd-card--dragging');
348
- window.removeEventListener('mousemove', onMove);
349
- window.removeEventListener('mouseup', onUp);
350
-
351
- // Only emit move events if drag actually started
352
- if (dragStarted) {
353
- for (const info of moveGroup) {
354
- const x = parseFloat(info.el.style.left) || 0;
355
- const y = parseFloat(info.el.style.top) || 0;
356
- const id = info.el.dataset.cardId || info.el.dataset.path || '';
357
- this.bus.emit('card:move', { id, x, y });
358
- }
359
- }
360
- };
361
-
362
- window.addEventListener('mousemove', onMove);
363
- window.addEventListener('mouseup', onUp);
364
- });
365
- }
366
-
367
- // ─── Resize interaction ──────────────────────────────
368
-
369
- private setupResize(card: HTMLElement, type: string) {
370
- const handle = document.createElement('div');
371
- handle.className = 'gd-resize-handle';
372
- card.appendChild(handle);
373
-
374
- let resizing = false;
375
- let startW = 0, startH = 0, startX = 0, startY = 0;
376
-
377
- handle.addEventListener('mousedown', (e: MouseEvent) => {
378
- e.preventDefault();
379
- e.stopPropagation();
380
- resizing = true;
381
- startW = card.offsetWidth;
382
- startH = card.offsetHeight;
383
- startX = e.clientX;
384
- startY = e.clientY;
385
- card.classList.add('gd-card--resizing');
386
-
387
- const onMove = (ev: MouseEvent) => {
388
- if (!resizing) return;
389
- const dw = (ev.clientX - startX) / this.state.zoom;
390
- const dh = (ev.clientY - startY) / this.state.zoom;
391
- const w = Math.max(this.opts.minWidth, startW + dw);
392
- const h = Math.max(this.opts.minHeight, startH + dh);
393
- card.style.width = `${w}px`;
394
- card.style.height = `${h}px`;
395
- this.plugins.get(type)?.onResize?.(card, w, h);
396
- };
397
-
398
- const onUp = () => {
399
- resizing = false;
400
- card.classList.remove('gd-card--resizing');
401
- window.removeEventListener('mousemove', onMove);
402
- window.removeEventListener('mouseup', onUp);
403
- this.bus.emit('card:resize', {
404
- id: card.dataset.cardId!,
405
- width: card.offsetWidth,
406
- height: card.offsetHeight,
407
- });
408
- };
409
-
410
- window.addEventListener('mousemove', onMove);
411
- window.addEventListener('mouseup', onUp);
412
- });
413
- }
414
-
415
- // ─── Queries ─────────────────────────────────────────
416
-
417
- /** Check if a plugin says it handles wheel events for a target element */
418
- consumesWheel(target: HTMLElement): boolean {
419
- // Walk up to find the card — check both .gd-card (engine-created) and [data-card-type] (externally managed)
420
- const card = (target.closest('.gd-card') || target.closest('[data-card-type]')) as HTMLElement | null;
421
- if (!card) return false;
422
- const type = card.dataset.cardType;
423
- if (!type) return false;
424
- return this.plugins.get(type)?.consumesWheel?.(target) ?? false;
425
- }
426
-
427
- /** Check if a plugin says it handles mouse events for a target element */
428
- consumesMouse(target: HTMLElement): boolean {
429
- const card = (target.closest('.gd-card') || target.closest('[data-card-type]')) as HTMLElement | null;
430
- if (!card) return false;
431
- const type = card.dataset.cardType;
432
- if (!type) return false;
433
- return this.plugins.get(type)?.consumesMouse?.(target) ?? false;
434
- }
435
- }