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.
- package/README.md +267 -118
- package/app/[...slug]/page.client.tsx +1 -0
- package/app/[...slug]/page.tsx +6 -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/og-image/route.ts +14 -0
- package/app/api/repo/file-content/route.ts +73 -20
- 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/version/route.ts +26 -0
- package/app/globals.css +5706 -4938
- package/app/layout.tsx +1279 -490
- 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.ts +625 -564
- package/app/lib/cards.tsx +1361 -916
- package/app/lib/chat.tsx +65 -9
- package/app/lib/code-editor.ts +86 -2
- 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 +71 -93
- package/app/lib/export-canvas.ts +287 -0
- package/app/lib/file-card-plugin.ts +148 -148
- package/app/lib/file-modal.tsx +49 -0
- package/app/lib/file-preview.ts +486 -427
- package/app/lib/github-import.test.ts +424 -0
- 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/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/positions.ts +190 -121
- package/app/lib/recent-commits.test.ts +947 -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 -987
- 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/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 -735
- 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 -482
- package/app/lib/{galaxydraw.test.ts → xydraw.test.ts} +228 -229
- package/app/og-image.png +0 -0
- package/app/page.client.tsx +70 -269
- package/app/page.tsx +15 -16
- package/app/state/machine.js +13 -0
- package/package.json +84 -75
- package/server.ts +10 -0
- package/app/[owner]/[repo]/page.tsx +0 -6
- package/app/[slug]/page.tsx +0 -6
- 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
|
@@ -1,482 +1,485 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
|
-
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* Wires
|
|
6
|
-
* server-rendered DOM and XState persistence layer.
|
|
7
|
-
*
|
|
8
|
-
* Architecture:
|
|
9
|
-
* - CanvasState manages zoom/pan/transform (replaces manual math)
|
|
10
|
-
* - CardManager creates/defers cards via FileCardPlugin + DiffCardPlugin
|
|
11
|
-
* - XState actor remains source-of-truth for persistence
|
|
12
|
-
* - Server-rendered DOM (#canvasViewport, #canvasContent) stays intact
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
import { CanvasState } from '../../packages/galaxydraw/src/core/state';
|
|
16
|
-
import { CardManager } from '../../packages/galaxydraw/src/core/cards';
|
|
17
|
-
import { EventBus } from '../../packages/galaxydraw/src/core/events';
|
|
18
|
-
import { createFileCardPlugin, createDiffCardPlugin } from './file-card-plugin';
|
|
19
|
-
import type { CanvasContext } from './context';
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Shared
|
|
23
|
-
* Replaces manual `ctx.canvas.style.transform = ...` calls.
|
|
24
|
-
*/
|
|
25
|
-
let _gdState: CanvasState | null = null;
|
|
26
|
-
let _cardManager: CardManager | null = null;
|
|
27
|
-
let _eventBus: EventBus | null = null;
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Initialize the
|
|
31
|
-
* Call this after ctx.canvas and ctx.canvasViewport are set.
|
|
32
|
-
*/
|
|
33
|
-
export function
|
|
34
|
-
_gdState = new CanvasState();
|
|
35
|
-
|
|
36
|
-
if (ctx.canvasViewport && ctx.canvas) {
|
|
37
|
-
_gdState.bind(ctx.canvasViewport, ctx.canvas);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Sync initial state from XState
|
|
41
|
-
const state = ctx.snap().context;
|
|
42
|
-
if (state.zoom) _gdState.zoom = state.zoom;
|
|
43
|
-
if (state.offsetX) _gdState.offsetX = state.offsetX;
|
|
44
|
-
if (state.offsetY) _gdState.offsetY = state.offsetY;
|
|
45
|
-
_gdState.applyTransform();
|
|
46
|
-
|
|
47
|
-
return _gdState;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Get the shared CanvasState instance.
|
|
52
|
-
*/
|
|
53
|
-
export function
|
|
54
|
-
return _gdState;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
*
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const
|
|
85
|
-
const
|
|
86
|
-
const
|
|
87
|
-
const
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
-
|
|
127
|
-
|
|
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
|
-
const
|
|
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
|
-
import
|
|
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
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
ctx.
|
|
269
|
-
ctx.
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
const
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
const
|
|
302
|
-
const
|
|
303
|
-
const
|
|
304
|
-
const
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
const
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
const
|
|
311
|
-
const
|
|
312
|
-
const
|
|
313
|
-
const
|
|
314
|
-
const
|
|
315
|
-
const
|
|
316
|
-
const
|
|
317
|
-
const
|
|
318
|
-
const
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
const
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
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
|
-
const
|
|
455
|
-
const
|
|
456
|
-
const
|
|
457
|
-
const
|
|
458
|
-
const
|
|
459
|
-
|
|
460
|
-
const
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
}
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* XyDraw Bridge — Adapter between GitMaps and the xydraw engine.
|
|
4
|
+
*
|
|
5
|
+
* Wires xydraw's CanvasState + CardManager into the existing
|
|
6
|
+
* server-rendered DOM and XState persistence layer.
|
|
7
|
+
*
|
|
8
|
+
* Architecture:
|
|
9
|
+
* - CanvasState manages zoom/pan/transform (replaces manual math)
|
|
10
|
+
* - CardManager creates/defers cards via FileCardPlugin + DiffCardPlugin
|
|
11
|
+
* - XState actor remains source-of-truth for persistence
|
|
12
|
+
* - Server-rendered DOM (#canvasViewport, #canvasContent) stays intact
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { CanvasState } from '../../packages/galaxydraw/src/core/state';
|
|
16
|
+
import { CardManager } from '../../packages/galaxydraw/src/core/cards';
|
|
17
|
+
import { EventBus } from '../../packages/galaxydraw/src/core/events';
|
|
18
|
+
import { createFileCardPlugin, createDiffCardPlugin } from './file-card-plugin';
|
|
19
|
+
import type { CanvasContext } from './context';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Shared xydraw state instance.
|
|
23
|
+
* Replaces manual `ctx.canvas.style.transform = ...` calls.
|
|
24
|
+
*/
|
|
25
|
+
let _gdState: CanvasState | null = null;
|
|
26
|
+
let _cardManager: CardManager | null = null;
|
|
27
|
+
let _eventBus: EventBus | null = null;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Initialize the xydraw state engine and bind to existing DOM.
|
|
31
|
+
* Call this after ctx.canvas and ctx.canvasViewport are set.
|
|
32
|
+
*/
|
|
33
|
+
export function initXyDrawState(ctx: CanvasContext): CanvasState {
|
|
34
|
+
_gdState = new CanvasState();
|
|
35
|
+
|
|
36
|
+
if (ctx.canvasViewport && ctx.canvas) {
|
|
37
|
+
_gdState.bind(ctx.canvasViewport, ctx.canvas);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Sync initial state from XState
|
|
41
|
+
const state = ctx.snap().context;
|
|
42
|
+
if (state.zoom) _gdState.zoom = state.zoom;
|
|
43
|
+
if (state.offsetX) _gdState.offsetX = state.offsetX;
|
|
44
|
+
if (state.offsetY) _gdState.offsetY = state.offsetY;
|
|
45
|
+
_gdState.applyTransform();
|
|
46
|
+
|
|
47
|
+
return _gdState;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Get the shared CanvasState instance.
|
|
52
|
+
*/
|
|
53
|
+
export function getXyDrawState(): CanvasState | null {
|
|
54
|
+
return _gdState;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const initGalaxyDrawState = initXyDrawState;
|
|
58
|
+
export const getGalaxyDrawState = getXyDrawState;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Zoom toward a screen point using xydraw's engine,
|
|
62
|
+
* then sync the computed state back to XState for persistence.
|
|
63
|
+
*
|
|
64
|
+
* @returns The new zoom/offset values (for callers that need them)
|
|
65
|
+
*/
|
|
66
|
+
export function zoomTowardScreen(
|
|
67
|
+
ctx: CanvasContext,
|
|
68
|
+
screenX: number,
|
|
69
|
+
screenY: number,
|
|
70
|
+
factor: number,
|
|
71
|
+
): { zoom: number; offsetX: number; offsetY: number } {
|
|
72
|
+
const gd = _gdState;
|
|
73
|
+
|
|
74
|
+
if (gd) {
|
|
75
|
+
// Delegate to xydraw engine
|
|
76
|
+
gd.zoomToward(screenX, screenY, factor);
|
|
77
|
+
// Sync back to XState for persistence
|
|
78
|
+
ctx.actor.send({ type: 'SET_ZOOM', zoom: gd.zoom });
|
|
79
|
+
ctx.actor.send({ type: 'SET_OFFSET', x: gd.offsetX, y: gd.offsetY });
|
|
80
|
+
return { zoom: gd.zoom, offsetX: gd.offsetX, offsetY: gd.offsetY };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Fallback: manual math (pre-bridge init)
|
|
84
|
+
const state = ctx.snap().context;
|
|
85
|
+
const rect = ctx.canvasViewport?.getBoundingClientRect();
|
|
86
|
+
const mouseX = screenX - (rect?.left ?? 0);
|
|
87
|
+
const mouseY = screenY - (rect?.top ?? 0);
|
|
88
|
+
const newZoom = Math.min(3, Math.max(0.1, state.zoom * factor));
|
|
89
|
+
const scale = newZoom / state.zoom;
|
|
90
|
+
const newOffsetX = mouseX - (mouseX - state.offsetX) * scale;
|
|
91
|
+
const newOffsetY = mouseY - (mouseY - state.offsetY) * scale;
|
|
92
|
+
ctx.actor.send({ type: 'SET_ZOOM', zoom: newZoom });
|
|
93
|
+
ctx.actor.send({ type: 'SET_OFFSET', x: newOffsetX, y: newOffsetY });
|
|
94
|
+
return { zoom: newZoom, offsetX: newOffsetX, offsetY: newOffsetY };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Pan by pixel delta via xydraw's engine.
|
|
99
|
+
* Syncs back to XState for persistence.
|
|
100
|
+
*/
|
|
101
|
+
export function panByDelta(
|
|
102
|
+
ctx: CanvasContext,
|
|
103
|
+
dx: number,
|
|
104
|
+
dy: number,
|
|
105
|
+
): void {
|
|
106
|
+
const gd = _gdState;
|
|
107
|
+
|
|
108
|
+
if (gd) {
|
|
109
|
+
gd.pan(dx, dy);
|
|
110
|
+
ctx.actor.send({ type: 'SET_OFFSET', x: gd.offsetX, y: gd.offsetY });
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Fallback
|
|
115
|
+
const state = ctx.snap().context;
|
|
116
|
+
ctx.actor.send({ type: 'SET_OFFSET', x: state.offsetX + dx, y: state.offsetY + dy });
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Convert screen coordinates to world coordinates.
|
|
121
|
+
* Delegates to CanvasState.screenToWorld() when available.
|
|
122
|
+
*/
|
|
123
|
+
export function screenToWorld(
|
|
124
|
+
ctx: CanvasContext,
|
|
125
|
+
screenX: number,
|
|
126
|
+
screenY: number,
|
|
127
|
+
): { x: number; y: number } {
|
|
128
|
+
const gd = _gdState;
|
|
129
|
+
|
|
130
|
+
if (gd) {
|
|
131
|
+
return gd.screenToWorld(screenX, screenY);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Fallback
|
|
135
|
+
const state = ctx.snap().context;
|
|
136
|
+
const rect = ctx.canvasViewport?.getBoundingClientRect();
|
|
137
|
+
return {
|
|
138
|
+
x: (screenX - (rect?.left ?? 0) - state.offsetX) / state.zoom,
|
|
139
|
+
y: (screenY - (rect?.top ?? 0) - state.offsetY) / state.zoom,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Center the viewport on a world coordinate.
|
|
145
|
+
* Delegates to CanvasState.panTo() when available.
|
|
146
|
+
*/
|
|
147
|
+
export function panToWorld(
|
|
148
|
+
ctx: CanvasContext,
|
|
149
|
+
worldX: number,
|
|
150
|
+
worldY: number,
|
|
151
|
+
): void {
|
|
152
|
+
const gd = _gdState;
|
|
153
|
+
|
|
154
|
+
if (gd) {
|
|
155
|
+
gd.panTo(worldX, worldY);
|
|
156
|
+
ctx.actor.send({ type: 'SET_OFFSET', x: gd.offsetX, y: gd.offsetY });
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Fallback
|
|
161
|
+
const state = ctx.snap().context;
|
|
162
|
+
const vp = ctx.canvasViewport;
|
|
163
|
+
if (vp) {
|
|
164
|
+
const vpW = vp.clientWidth;
|
|
165
|
+
const vpH = vp.clientHeight;
|
|
166
|
+
const newOffsetX = vpW / 2 - worldX * state.zoom;
|
|
167
|
+
const newOffsetY = vpH / 2 - worldY * state.zoom;
|
|
168
|
+
ctx.actor.send({ type: 'SET_OFFSET', x: newOffsetX, y: newOffsetY });
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// ─── Card Manager ───────────────────────────────────────
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Initialize the CardManager with file card plugins.
|
|
176
|
+
* Call after initXyDrawState() when ctx.canvas is available.
|
|
177
|
+
*
|
|
178
|
+
* The CardManager handles:
|
|
179
|
+
* - Card creation via plugins (FileCardPlugin, DiffCardPlugin)
|
|
180
|
+
* - Drag, resize, z-order management
|
|
181
|
+
* - Selection (single, multi)
|
|
182
|
+
* - Deferred rendering (virtualization)
|
|
183
|
+
*/
|
|
184
|
+
import { scheduleRenderConnections } from './connections';
|
|
185
|
+
|
|
186
|
+
export function initCardManager(ctx: CanvasContext): CardManager | null {
|
|
187
|
+
if (!_gdState || !ctx.canvas) {
|
|
188
|
+
console.warn('[xydraw-bridge] Cannot init CardManager: state or canvas not ready');
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
_eventBus = new EventBus();
|
|
193
|
+
_cardManager = new CardManager(_gdState, _eventBus, ctx.canvas, {
|
|
194
|
+
defaultWidth: 580,
|
|
195
|
+
defaultHeight: 700,
|
|
196
|
+
minWidth: 280,
|
|
197
|
+
minHeight: 200,
|
|
198
|
+
gridSize: 0,
|
|
199
|
+
cornerSize: 40,
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
// Register plugins
|
|
203
|
+
_cardManager.registerPlugin(createFileCardPlugin());
|
|
204
|
+
_cardManager.registerPlugin(createDiffCardPlugin());
|
|
205
|
+
|
|
206
|
+
// Sync card events back to XState for persistence
|
|
207
|
+
_eventBus.on('card:move', (ev) => {
|
|
208
|
+
const { id, x, y } = ev;
|
|
209
|
+
ctx.actor.send({ type: 'SAVE_POSITION', path: id, x, y });
|
|
210
|
+
scheduleRenderConnections(ctx);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
_eventBus.on('card:resize', (ev) => {
|
|
214
|
+
const { id, width, height } = ev;
|
|
215
|
+
ctx.actor.send({ type: 'RESIZE_CARD', path: id, width, height });
|
|
216
|
+
scheduleRenderConnections(ctx);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
console.log('[xydraw-bridge] CardManager initialized with file + diff plugins');
|
|
220
|
+
return _cardManager;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Get the shared CardManager instance.
|
|
225
|
+
*/
|
|
226
|
+
export function getCardManager(): CardManager | null {
|
|
227
|
+
return _cardManager;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Get the shared EventBus instance.
|
|
232
|
+
*/
|
|
233
|
+
export function getEventBus(): EventBus | null {
|
|
234
|
+
return _eventBus;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// ─── Card Creation via CardManager ──────────────────────
|
|
238
|
+
|
|
239
|
+
import { FILE_CARD_TYPE, DIFF_CARD_TYPE } from './file-card-plugin';
|
|
240
|
+
import { getActiveLayer } from './layers';
|
|
241
|
+
import { updateHiddenUI } from './hidden-files';
|
|
242
|
+
import type { ViewportRect } from '../../packages/xydraw/src/core/state';
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Render all files on canvas using CardManager instead of direct DOM.
|
|
246
|
+
*
|
|
247
|
+
* This replaces the viewport culling logic in renderAllFilesOnCanvas():
|
|
248
|
+
* - Cards in/near viewport → CardManager.create() (immediate DOM)
|
|
249
|
+
* - Cards outside viewport → CardManager.defer() (lazy materialization)
|
|
250
|
+
* - On scroll/zoom → materializeViewport() creates deferred cards
|
|
251
|
+
*
|
|
252
|
+
* Benefits over the legacy approach:
|
|
253
|
+
* - Drag/resize/z-order handled uniformly by CardManager
|
|
254
|
+
* - EventBus emits card:create/card:move/card:resize for persistence
|
|
255
|
+
* - Cleaner separation between rendering and interaction
|
|
256
|
+
*/
|
|
257
|
+
export function renderAllFilesViaCardManager(ctx: CanvasContext, files: any[]) {
|
|
258
|
+
if (!_cardManager || !_gdState) {
|
|
259
|
+
// Fallback to legacy if CardManager not initialized
|
|
260
|
+
console.warn('[xydraw-bridge] CardManager not ready, falling back to legacy render');
|
|
261
|
+
return false; // Signal caller to use legacy path
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
_cardManager.clear();
|
|
265
|
+
|
|
266
|
+
// Also clear existing DOM cards, pills, and deferred state
|
|
267
|
+
// Without this, layer switching leaves orphaned elements
|
|
268
|
+
ctx.fileCards.forEach(card => card.remove());
|
|
269
|
+
ctx.fileCards.clear();
|
|
270
|
+
ctx.deferredCards.clear();
|
|
271
|
+
ctx.canvas?.querySelectorAll('.dir-label').forEach(el => el.remove());
|
|
272
|
+
ctx.canvas?.querySelectorAll('.file-pill').forEach(el => el.remove());
|
|
273
|
+
// Clear pill tracking Map
|
|
274
|
+
const { clearAllPills } = require('./viewport-culling');
|
|
275
|
+
clearAllPills(ctx);
|
|
276
|
+
if (ctx.svgOverlay) ctx.svgOverlay.innerHTML = '';
|
|
277
|
+
|
|
278
|
+
const { resetCardGroups, restoreCollapsedDirs } = require('./card-groups');
|
|
279
|
+
resetCardGroups();
|
|
280
|
+
|
|
281
|
+
const visibleFiles = files.filter(f => !ctx.hiddenFiles.has(f.path));
|
|
282
|
+
updateHiddenUI(ctx);
|
|
283
|
+
|
|
284
|
+
// Build changed file data map
|
|
285
|
+
const changedFileDataMap = new Map<string, any>();
|
|
286
|
+
if (ctx.commitFilesData) {
|
|
287
|
+
ctx.commitFilesData.forEach(f => changedFileDataMap.set(f.path, f));
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
let layerFiles = visibleFiles;
|
|
291
|
+
const activeLayer = getActiveLayer();
|
|
292
|
+
if (activeLayer) {
|
|
293
|
+
layerFiles = visibleFiles.filter(f => !!activeLayer.files[f.path]);
|
|
294
|
+
} else {
|
|
295
|
+
// Default layer: exclude files that have been moved to other layers
|
|
296
|
+
const { isFileMovedFromDefault } = require('./layers');
|
|
297
|
+
layerFiles = visibleFiles.filter(f => !isFileMovedFromDefault(f.path));
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Grid layout: square-ish
|
|
301
|
+
const count = layerFiles.length;
|
|
302
|
+
const cols = Math.max(1, Math.ceil(Math.sqrt(count)));
|
|
303
|
+
const defaultCardWidth = 580;
|
|
304
|
+
const defaultCardHeight = 700;
|
|
305
|
+
const gap = 20;
|
|
306
|
+
const cellW = defaultCardWidth + gap;
|
|
307
|
+
const cellH = defaultCardHeight + gap;
|
|
308
|
+
|
|
309
|
+
// Viewport rect for initial visibility check
|
|
310
|
+
const MARGIN = 800;
|
|
311
|
+
const state = _gdState.snapshot();
|
|
312
|
+
const vpEl = ctx.canvasViewport;
|
|
313
|
+
const vpW = vpEl?.clientWidth || window.innerWidth;
|
|
314
|
+
const vpH = vpEl?.clientHeight || window.innerHeight;
|
|
315
|
+
const zoom = state.zoom || 1;
|
|
316
|
+
const offsetX = state.offsetX || 0;
|
|
317
|
+
const offsetY = state.offsetY || 0;
|
|
318
|
+
const worldLeft = (-offsetX - MARGIN) / zoom;
|
|
319
|
+
const worldTop = (-offsetY - MARGIN) / zoom;
|
|
320
|
+
const worldRight = (vpW - offsetX + MARGIN) / zoom;
|
|
321
|
+
const worldBottom = (vpH - offsetY + MARGIN) / zoom;
|
|
322
|
+
|
|
323
|
+
let createdCount = 0;
|
|
324
|
+
let deferredCount = 0;
|
|
325
|
+
|
|
326
|
+
// Cache XState state once outside the loop — avoids N snapshots for N files
|
|
327
|
+
const cachedCardSizes = ctx.snap().context.cardSizes || {};
|
|
328
|
+
|
|
329
|
+
layerFiles.forEach((f, index) => {
|
|
330
|
+
const posKey = `allfiles:${f.path}`;
|
|
331
|
+
let x: number, y: number;
|
|
332
|
+
|
|
333
|
+
if (ctx.positions.has(posKey)) {
|
|
334
|
+
const pos = ctx.positions.get(posKey);
|
|
335
|
+
x = pos.x; y = pos.y;
|
|
336
|
+
} else {
|
|
337
|
+
const col = index % cols;
|
|
338
|
+
const row = Math.floor(index / cols);
|
|
339
|
+
x = 50 + col * cellW;
|
|
340
|
+
y = 50 + row * cellH;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Get saved size (from cached snapshot — no per-file ctx.snap() call)
|
|
344
|
+
let size = cachedCardSizes[f.path];
|
|
345
|
+
if (!size && ctx.positions.has(posKey)) {
|
|
346
|
+
const pos = ctx.positions.get(posKey);
|
|
347
|
+
if (pos.width) size = { width: pos.width, height: pos.height };
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Merge diff/layer data
|
|
351
|
+
let fileWithDiff = { ...f };
|
|
352
|
+
if (activeLayer && activeLayer.files[fileWithDiff.path]) {
|
|
353
|
+
fileWithDiff.layerSections = activeLayer.files[fileWithDiff.path].sections;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const isChanged = ctx.changedFilePaths.has(f.path);
|
|
357
|
+
if (isChanged && changedFileDataMap.has(fileWithDiff.path)) {
|
|
358
|
+
const diffData = changedFileDataMap.get(fileWithDiff.path);
|
|
359
|
+
if (diffData.content) {
|
|
360
|
+
fileWithDiff.content = diffData.content;
|
|
361
|
+
fileWithDiff.lines = diffData.content.split('\n').length;
|
|
362
|
+
}
|
|
363
|
+
fileWithDiff.status = diffData.status;
|
|
364
|
+
fileWithDiff.hunks = diffData.hunks;
|
|
365
|
+
|
|
366
|
+
if (diffData.hunks?.length > 0) {
|
|
367
|
+
const addedLines = new Set<number>();
|
|
368
|
+
const deletedBeforeLine = new Map<number, string[]>();
|
|
369
|
+
for (const hunk of diffData.hunks) {
|
|
370
|
+
let newLine = hunk.newStart;
|
|
371
|
+
let pendingDeleted: string[] = [];
|
|
372
|
+
for (const l of hunk.lines) {
|
|
373
|
+
if (l.type === 'add') {
|
|
374
|
+
addedLines.add(newLine);
|
|
375
|
+
if (pendingDeleted.length > 0) {
|
|
376
|
+
const existing = deletedBeforeLine.get(newLine) || [];
|
|
377
|
+
deletedBeforeLine.set(newLine, existing.concat(pendingDeleted));
|
|
378
|
+
pendingDeleted = [];
|
|
379
|
+
}
|
|
380
|
+
newLine++;
|
|
381
|
+
} else if (l.type === 'del') {
|
|
382
|
+
pendingDeleted.push(l.content);
|
|
383
|
+
} else {
|
|
384
|
+
if (pendingDeleted.length > 0) {
|
|
385
|
+
const existing = deletedBeforeLine.get(newLine) || [];
|
|
386
|
+
deletedBeforeLine.set(newLine, existing.concat(pendingDeleted));
|
|
387
|
+
pendingDeleted = [];
|
|
388
|
+
}
|
|
389
|
+
newLine++;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
if (pendingDeleted.length > 0) {
|
|
393
|
+
const existing = deletedBeforeLine.get(newLine) || [];
|
|
394
|
+
deletedBeforeLine.set(newLine, existing.concat(pendingDeleted));
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
fileWithDiff.addedLines = addedLines;
|
|
398
|
+
fileWithDiff.deletedBeforeLine = deletedBeforeLine;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const cardData = {
|
|
403
|
+
id: f.path,
|
|
404
|
+
x, y,
|
|
405
|
+
width: size?.width || defaultCardWidth,
|
|
406
|
+
height: size?.height || defaultCardHeight,
|
|
407
|
+
meta: { file: fileWithDiff, ctx, savedSize: size },
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
// Check if in viewport
|
|
411
|
+
const inViewport =
|
|
412
|
+
x + (size?.width || defaultCardWidth) > worldLeft &&
|
|
413
|
+
x < worldRight &&
|
|
414
|
+
y + (size?.height || defaultCardHeight) > worldTop &&
|
|
415
|
+
y < worldBottom;
|
|
416
|
+
|
|
417
|
+
if (inViewport) {
|
|
418
|
+
const card = _cardManager!.create(FILE_CARD_TYPE, cardData);
|
|
419
|
+
if (card) {
|
|
420
|
+
// Sync to ctx.fileCards so minimap, fitAll, etc. can find it
|
|
421
|
+
ctx.fileCards.set(f.path, card);
|
|
422
|
+
// Apply change markers for diff highlighting
|
|
423
|
+
if (isChanged) {
|
|
424
|
+
card.classList.add('file-card--changed');
|
|
425
|
+
card.dataset.changed = 'true';
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
createdCount++;
|
|
429
|
+
} else {
|
|
430
|
+
_cardManager!.defer(FILE_CARD_TYPE, cardData);
|
|
431
|
+
// Also store in ctx.deferredCards so minimap, fitAll, etc. can see ALL files
|
|
432
|
+
ctx.deferredCards.set(f.path, {
|
|
433
|
+
file: fileWithDiff, x, y,
|
|
434
|
+
size: { width: cardData.width, height: cardData.height },
|
|
435
|
+
isChanged,
|
|
436
|
+
});
|
|
437
|
+
deferredCount++;
|
|
438
|
+
}
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
restoreCollapsedDirs(ctx);
|
|
442
|
+
|
|
443
|
+
console.log(`[gd-bridge] ${createdCount} created, ${deferredCount} deferred (${layerFiles.length} total)`);
|
|
444
|
+
return true; // Signal: we handled it
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Materialize deferred cards that are now in the viewport.
|
|
449
|
+
* Call this on zoom/pan changes.
|
|
450
|
+
*/
|
|
451
|
+
export function materializeViewport(ctx: CanvasContext): number {
|
|
452
|
+
if (!_cardManager || !_gdState) return 0;
|
|
453
|
+
|
|
454
|
+
const MARGIN = 800;
|
|
455
|
+
const state = _gdState.snapshot();
|
|
456
|
+
const vpEl = ctx.canvasViewport;
|
|
457
|
+
const vpW = vpEl?.clientWidth || window.innerWidth;
|
|
458
|
+
const vpH = vpEl?.clientHeight || window.innerHeight;
|
|
459
|
+
const zoom = state.zoom || 1;
|
|
460
|
+
const offsetX = state.offsetX || 0;
|
|
461
|
+
const offsetY = state.offsetY || 0;
|
|
462
|
+
|
|
463
|
+
const rect: ViewportRect = {
|
|
464
|
+
left: (-offsetX - MARGIN) / zoom,
|
|
465
|
+
top: (-offsetY - MARGIN) / zoom,
|
|
466
|
+
right: (vpW - offsetX + MARGIN) / zoom,
|
|
467
|
+
bottom: (vpH - offsetY + MARGIN) / zoom,
|
|
468
|
+
};
|
|
469
|
+
|
|
470
|
+
const count = _cardManager.materializeInRect(rect);
|
|
471
|
+
|
|
472
|
+
// Sync newly materialized cards to ctx.fileCards for minimap/fitAll
|
|
473
|
+
// AND remove from ctx.deferredCards so viewport-culling doesn't re-create them
|
|
474
|
+
if (count > 0) {
|
|
475
|
+
for (const [id, card] of _cardManager.cards) {
|
|
476
|
+
if (!ctx.fileCards.has(id)) {
|
|
477
|
+
ctx.fileCards.set(id, card);
|
|
478
|
+
}
|
|
479
|
+
// Remove from deferredCards to prevent duplicate creation by viewport-culling
|
|
480
|
+
ctx.deferredCards.delete(id);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
return count;
|
|
485
|
+
}
|