gitmaps 1.1.16 → 1.1.18
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/app/analytics.db +0 -0
- package/app/lib/viewport-culling.ts +130 -5
- package/package.json +1 -1
package/app/analytics.db
CHANGED
|
Binary file
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
*/
|
|
24
24
|
import { measure } from 'measure-fn';
|
|
25
25
|
import type { CanvasContext } from './context';
|
|
26
|
-
import { estimatePreviewMaxScroll, getLowZoomScale, renderLowZoomPreviewCanvas } from './low-zoom-preview';
|
|
26
|
+
import { collectPreviewDiffMarkers, estimatePreviewMaxScroll, getLowZoomScale, getPreviewScrollMetrics, renderLowZoomPreviewCanvas } from './low-zoom-preview';
|
|
27
27
|
import { materializeViewport } from './xydraw-bridge';
|
|
28
28
|
|
|
29
29
|
// ── Culling state ──────────────────────────────────────────
|
|
@@ -170,6 +170,12 @@ function getSavedScrollTop(ctx: CanvasContext, path: string): number {
|
|
|
170
170
|
return saved?.x || 0;
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
+
function resolvePreviewFile(ctx: CanvasContext, path: string, fallback?: any) {
|
|
174
|
+
const commitFile = ctx.commitFilesData?.find(f => f.path === path) || null;
|
|
175
|
+
const allFile = ctx.allFilesData?.find(f => f.path === path) || null;
|
|
176
|
+
return commitFile || fallback || allFile || null;
|
|
177
|
+
}
|
|
178
|
+
|
|
173
179
|
/**
|
|
174
180
|
* Create a lightweight pill placeholder for a file.
|
|
175
181
|
* ~3 DOM nodes vs ~100+ for a full card = massive perf win at low zoom.
|
|
@@ -204,6 +210,16 @@ function createPillCard(ctx: CanvasContext, file: any, path: string, x: number,
|
|
|
204
210
|
canvas.style.cssText = 'display:block;width:100%;height:100%;pointer-events:none;';
|
|
205
211
|
pill.appendChild(canvas);
|
|
206
212
|
|
|
213
|
+
const diffOverlay = document.createElement('div');
|
|
214
|
+
diffOverlay.className = 'file-pill-diff-overlay';
|
|
215
|
+
diffOverlay.style.cssText = 'position:absolute;top:10px;right:26px;bottom:10px;width:12px;pointer-events:none;z-index:2;';
|
|
216
|
+
pill.appendChild(diffOverlay);
|
|
217
|
+
|
|
218
|
+
const scrollOverlay = document.createElement('div');
|
|
219
|
+
scrollOverlay.className = 'file-pill-scroll-overlay';
|
|
220
|
+
scrollOverlay.style.cssText = 'position:absolute;top:10px;right:15px;bottom:10px;width:9px;pointer-events:auto;z-index:3;';
|
|
221
|
+
pill.appendChild(scrollOverlay);
|
|
222
|
+
|
|
207
223
|
(pill as any)._fileData = file;
|
|
208
224
|
updatePillCardLayout(ctx, pill, zoom, isChanged);
|
|
209
225
|
|
|
@@ -223,8 +239,11 @@ function updatePillCardLayout(ctx: CanvasContext, pill: HTMLElement, zoom: numbe
|
|
|
223
239
|
const scale = getLowZoomScale(zoom);
|
|
224
240
|
const path = pill.dataset.path || '';
|
|
225
241
|
const canvas = pill.querySelector('.file-pill-canvas') as HTMLCanvasElement | null;
|
|
226
|
-
const
|
|
242
|
+
const diffOverlay = pill.querySelector('.file-pill-diff-overlay') as HTMLElement | null;
|
|
243
|
+
const scrollOverlay = pill.querySelector('.file-pill-scroll-overlay') as HTMLElement | null;
|
|
244
|
+
const file = resolvePreviewFile(ctx, path, (pill as any)._fileData);
|
|
227
245
|
const changed = isChanged ?? pill.dataset.changed === 'true';
|
|
246
|
+
const scrollTop = getSavedScrollTop(ctx, path);
|
|
228
247
|
pill.dataset.zoomBucket = zoom.toFixed(3);
|
|
229
248
|
pill.style.borderRadius = `${Math.max(6, scale.radius)}px`;
|
|
230
249
|
if (!canvas) return;
|
|
@@ -235,10 +254,116 @@ function updatePillCardLayout(ctx: CanvasContext, pill: HTMLElement, zoom: numbe
|
|
|
235
254
|
width: w,
|
|
236
255
|
height: h,
|
|
237
256
|
zoom,
|
|
238
|
-
scrollTop
|
|
257
|
+
scrollTop,
|
|
239
258
|
accentColor: getPillColor(path, changed),
|
|
240
259
|
isChanged: changed,
|
|
241
260
|
});
|
|
261
|
+
|
|
262
|
+
const metrics = getPreviewScrollMetrics(file, h, zoom, scrollTop);
|
|
263
|
+
|
|
264
|
+
if (diffOverlay) {
|
|
265
|
+
const markers = collectPreviewDiffMarkers(file, metrics.totalLines);
|
|
266
|
+
diffOverlay.innerHTML = '';
|
|
267
|
+
diffOverlay.style.top = `${metrics.trackPadding}px`;
|
|
268
|
+
diffOverlay.style.bottom = `${metrics.trackPadding}px`;
|
|
269
|
+
diffOverlay.style.right = '26px';
|
|
270
|
+
diffOverlay.style.width = '12px';
|
|
271
|
+
diffOverlay.style.pointerEvents = 'auto';
|
|
272
|
+
|
|
273
|
+
for (const marker of markers) {
|
|
274
|
+
const btn = document.createElement('button');
|
|
275
|
+
const markerHeight = marker.height === 1 ? metrics.trackHeight : 10;
|
|
276
|
+
const y = marker.height === 1 ? 0 : marker.ratio * Math.max(0, metrics.trackHeight - markerHeight);
|
|
277
|
+
btn.type = 'button';
|
|
278
|
+
btn.className = 'file-pill-diff-marker';
|
|
279
|
+
btn.title = marker.color === '#22c55e' ? 'Jump to added lines' : 'Jump to deleted lines';
|
|
280
|
+
btn.style.cssText = `position:absolute;left:0;width:12px;height:${markerHeight}px;top:${y}px;border:none;border-radius:3px;background:${marker.color};box-shadow:0 0 0 1px rgba(255,255,255,0.28),0 0 10px ${marker.color}88;cursor:pointer;padding:0;pointer-events:auto;opacity:1;`;
|
|
281
|
+
btn.addEventListener('mousedown', (e) => {
|
|
282
|
+
e.preventDefault();
|
|
283
|
+
e.stopPropagation();
|
|
284
|
+
});
|
|
285
|
+
btn.addEventListener('click', (e) => {
|
|
286
|
+
e.preventDefault();
|
|
287
|
+
e.stopPropagation();
|
|
288
|
+
const maxScroll = estimatePreviewMaxScroll(file, h, zoom);
|
|
289
|
+
const next = marker.height === 1 ? 0 : Math.max(0, Math.min(maxScroll, marker.ratio * maxScroll));
|
|
290
|
+
const key = `scroll:${path}`;
|
|
291
|
+
const existing = ctx.positions.get(key) || {};
|
|
292
|
+
ctx.positions.set(key, { ...existing, x: next, y: existing.y || 0 });
|
|
293
|
+
try {
|
|
294
|
+
const { debounceSaveScroll } = require('./cards');
|
|
295
|
+
debounceSaveScroll(ctx, path, next);
|
|
296
|
+
} catch { }
|
|
297
|
+
updatePillCardLayout(ctx, pill, zoom, changed);
|
|
298
|
+
});
|
|
299
|
+
diffOverlay.appendChild(btn);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if (scrollOverlay) {
|
|
304
|
+
scrollOverlay.innerHTML = '';
|
|
305
|
+
scrollOverlay.style.top = `${metrics.trackPadding}px`;
|
|
306
|
+
scrollOverlay.style.bottom = `${metrics.trackPadding}px`;
|
|
307
|
+
|
|
308
|
+
const track = document.createElement('button');
|
|
309
|
+
track.type = 'button';
|
|
310
|
+
track.className = 'file-pill-scroll-track';
|
|
311
|
+
track.title = 'Scroll preview';
|
|
312
|
+
track.style.cssText = `position:absolute;inset:0;border:none;border-radius:999px;background:rgba(255,255,255,0.08);padding:0;cursor:pointer;`;
|
|
313
|
+
track.addEventListener('mousedown', (e) => {
|
|
314
|
+
e.preventDefault();
|
|
315
|
+
e.stopPropagation();
|
|
316
|
+
const rect = track.getBoundingClientRect();
|
|
317
|
+
const thumbHeight = Math.max(18, metrics.thumbHeight);
|
|
318
|
+
const localY = Math.max(0, Math.min(rect.height, e.clientY - rect.top));
|
|
319
|
+
const thumbCenter = localY - thumbHeight / 2;
|
|
320
|
+
const ratio = rect.height > thumbHeight ? Math.max(0, Math.min(1, thumbCenter / (rect.height - thumbHeight))) : 0;
|
|
321
|
+
const next = ratio * metrics.maxScroll;
|
|
322
|
+
const key = `scroll:${path}`;
|
|
323
|
+
const existing = ctx.positions.get(key) || {};
|
|
324
|
+
ctx.positions.set(key, { ...existing, x: next, y: existing.y || 0 });
|
|
325
|
+
try {
|
|
326
|
+
const { debounceSaveScroll } = require('./cards');
|
|
327
|
+
debounceSaveScroll(ctx, path, next);
|
|
328
|
+
} catch { }
|
|
329
|
+
updatePillCardLayout(ctx, pill, zoom, changed);
|
|
330
|
+
});
|
|
331
|
+
scrollOverlay.appendChild(track);
|
|
332
|
+
|
|
333
|
+
const thumb = document.createElement('div');
|
|
334
|
+
thumb.className = 'file-pill-scroll-thumb';
|
|
335
|
+
thumb.style.cssText = `position:absolute;left:0;width:9px;height:${metrics.thumbHeight}px;top:${Math.max(0, metrics.thumbY - metrics.trackPadding)}px;border-radius:999px;background:rgba(196,181,253,0.98);box-shadow:0 0 0 1px rgba(255,255,255,0.24),0 4px 12px rgba(0,0,0,0.35);cursor:grab;`;
|
|
336
|
+
thumb.addEventListener('mousedown', (e) => {
|
|
337
|
+
e.preventDefault();
|
|
338
|
+
e.stopPropagation();
|
|
339
|
+
const startY = e.clientY;
|
|
340
|
+
const startScroll = scrollTop;
|
|
341
|
+
const dragTravel = Math.max(1, metrics.trackHeight - metrics.thumbHeight);
|
|
342
|
+
const maxScroll = Math.max(0, metrics.maxScroll);
|
|
343
|
+
|
|
344
|
+
const onMove = (moveEvent: MouseEvent) => {
|
|
345
|
+
moveEvent.preventDefault();
|
|
346
|
+
const deltaY = moveEvent.clientY - startY;
|
|
347
|
+
const next = maxScroll > 0 ? Math.max(0, Math.min(maxScroll, startScroll + (deltaY / dragTravel) * maxScroll)) : 0;
|
|
348
|
+
const key = `scroll:${path}`;
|
|
349
|
+
const existing = ctx.positions.get(key) || {};
|
|
350
|
+
ctx.positions.set(key, { ...existing, x: next, y: existing.y || 0 });
|
|
351
|
+
updatePillCardLayout(ctx, pill, zoom, changed);
|
|
352
|
+
};
|
|
353
|
+
const onUp = () => {
|
|
354
|
+
const finalScroll = getSavedScrollTop(ctx, path);
|
|
355
|
+
try {
|
|
356
|
+
const { debounceSaveScroll } = require('./cards');
|
|
357
|
+
debounceSaveScroll(ctx, path, finalScroll);
|
|
358
|
+
} catch { }
|
|
359
|
+
window.removeEventListener('mousemove', onMove);
|
|
360
|
+
window.removeEventListener('mouseup', onUp);
|
|
361
|
+
};
|
|
362
|
+
window.addEventListener('mousemove', onMove);
|
|
363
|
+
window.addEventListener('mouseup', onUp);
|
|
364
|
+
});
|
|
365
|
+
scrollOverlay.appendChild(thumb);
|
|
366
|
+
}
|
|
242
367
|
}
|
|
243
368
|
|
|
244
369
|
/**
|
|
@@ -482,7 +607,7 @@ export function performViewportCulling(ctx: CanvasContext) {
|
|
|
482
607
|
);
|
|
483
608
|
|
|
484
609
|
if (inView) {
|
|
485
|
-
const file =
|
|
610
|
+
const file = resolvePreviewFile(ctx, path);
|
|
486
611
|
const pill = createPillCard(ctx, file, path, x, y, w, h, isChanged, zoom, true);
|
|
487
612
|
if (isChanged) pill.dataset.changed = 'true';
|
|
488
613
|
ctx.canvas.appendChild(pill);
|
|
@@ -661,7 +786,7 @@ export function setupPillInteraction(ctx: CanvasContext) {
|
|
|
661
786
|
const path = pill.dataset.path || '';
|
|
662
787
|
if (!path) return;
|
|
663
788
|
|
|
664
|
-
const file = (pill as any)._fileData
|
|
789
|
+
const file = resolvePreviewFile(ctx, path, (pill as any)._fileData);
|
|
665
790
|
const zoom = ctx.snap().context.zoom || 1;
|
|
666
791
|
const height = parseFloat(pill.style.height) || 700;
|
|
667
792
|
const current = getSavedScrollTop(ctx, path);
|