restty 0.1.14 → 0.1.16
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 +22 -0
- package/dist/app/index.d.ts +1 -1
- package/dist/app/index.js +387 -94
- package/dist/app/types.d.ts +18 -0
- package/dist/index.js +387 -94
- package/dist/internal.js +387 -94
- package/package.json +1 -1
package/dist/app/index.js
CHANGED
|
@@ -50323,6 +50323,17 @@ function createRestty(options) {
|
|
|
50323
50323
|
}
|
|
50324
50324
|
|
|
50325
50325
|
// src/app/index.ts
|
|
50326
|
+
function normalizeTouchSelectionMode(value) {
|
|
50327
|
+
if (value === "drag" || value === "long-press" || value === "off")
|
|
50328
|
+
return value;
|
|
50329
|
+
return "long-press";
|
|
50330
|
+
}
|
|
50331
|
+
function clampFiniteNumber(value, fallback, min, max, round = false) {
|
|
50332
|
+
if (!Number.isFinite(value))
|
|
50333
|
+
return fallback;
|
|
50334
|
+
const numeric = round ? Math.round(value) : Number(value);
|
|
50335
|
+
return Math.min(max, Math.max(min, numeric));
|
|
50336
|
+
}
|
|
50326
50337
|
function createResttyApp(options) {
|
|
50327
50338
|
const { canvas: canvasInput, imeInput: imeInputInput, elements, callbacks } = options;
|
|
50328
50339
|
const session = options.session ?? getDefaultResttyAppSession();
|
|
@@ -50345,6 +50356,12 @@ function createResttyApp(options) {
|
|
|
50345
50356
|
const attachCanvasEvents = options.attachCanvasEvents ?? true;
|
|
50346
50357
|
const autoResize = options.autoResize ?? true;
|
|
50347
50358
|
const debugExpose = options.debugExpose ?? false;
|
|
50359
|
+
const touchSelectionMode = normalizeTouchSelectionMode(options.touchSelectionMode);
|
|
50360
|
+
const touchSelectionLongPressMs = clampFiniteNumber(options.touchSelectionLongPressMs, 450, 120, 2000, true);
|
|
50361
|
+
const touchSelectionMoveThresholdPx = clampFiniteNumber(options.touchSelectionMoveThresholdPx, 10, 1, 64);
|
|
50362
|
+
const hasCoarsePointer = typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(any-pointer: coarse)").matches;
|
|
50363
|
+
const hasTouchPoints = typeof navigator !== "undefined" && navigator.maxTouchPoints > 0;
|
|
50364
|
+
const showOverlayScrollbar = !(hasCoarsePointer || hasTouchPoints);
|
|
50348
50365
|
const nerdIconScale = Number.isFinite(options.nerdIconScale) ? Number(options.nerdIconScale) : 1;
|
|
50349
50366
|
const alphaBlending = options.alphaBlending ?? "linear-corrected";
|
|
50350
50367
|
const srgbChannelToLinear = (c3) => c3 <= 0.04045 ? c3 / 12.92 : Math.pow((c3 + 0.055) / 1.055, 2.4);
|
|
@@ -50546,6 +50563,17 @@ function createResttyApp(options) {
|
|
|
50546
50563
|
anchor: null,
|
|
50547
50564
|
focus: null
|
|
50548
50565
|
};
|
|
50566
|
+
const touchSelectionState = {
|
|
50567
|
+
pendingPointerId: null,
|
|
50568
|
+
activePointerId: null,
|
|
50569
|
+
panPointerId: null,
|
|
50570
|
+
pendingCell: null,
|
|
50571
|
+
pendingStartedAt: 0,
|
|
50572
|
+
pendingStartX: 0,
|
|
50573
|
+
pendingStartY: 0,
|
|
50574
|
+
panLastY: 0,
|
|
50575
|
+
pendingTimer: 0
|
|
50576
|
+
};
|
|
50549
50577
|
const linkState = {
|
|
50550
50578
|
hoverId: 0,
|
|
50551
50579
|
hoverUri: ""
|
|
@@ -50557,6 +50585,10 @@ function createResttyApp(options) {
|
|
|
50557
50585
|
lastOffset: 0,
|
|
50558
50586
|
lastLen: 0
|
|
50559
50587
|
};
|
|
50588
|
+
const scrollbarDragState = {
|
|
50589
|
+
pointerId: null,
|
|
50590
|
+
thumbGrabRatio: 0.5
|
|
50591
|
+
};
|
|
50560
50592
|
const KITTY_FLAG_REPORT_EVENTS2 = 1 << 1;
|
|
50561
50593
|
function updateCanvasCursor() {
|
|
50562
50594
|
if (!canvas)
|
|
@@ -50567,9 +50599,200 @@ function createResttyApp(options) {
|
|
|
50567
50599
|
}
|
|
50568
50600
|
canvas.style.cursor = linkState.hoverId ? "pointer" : "default";
|
|
50569
50601
|
}
|
|
50602
|
+
function isTouchPointer(event) {
|
|
50603
|
+
return event.pointerType === "touch";
|
|
50604
|
+
}
|
|
50605
|
+
function clearPendingTouchSelection() {
|
|
50606
|
+
if (touchSelectionState.pendingTimer) {
|
|
50607
|
+
clearTimeout(touchSelectionState.pendingTimer);
|
|
50608
|
+
touchSelectionState.pendingTimer = 0;
|
|
50609
|
+
}
|
|
50610
|
+
touchSelectionState.pendingPointerId = null;
|
|
50611
|
+
touchSelectionState.pendingCell = null;
|
|
50612
|
+
touchSelectionState.pendingStartedAt = 0;
|
|
50613
|
+
}
|
|
50614
|
+
function tryActivatePendingTouchSelection(pointerId) {
|
|
50615
|
+
if (touchSelectionMode !== "long-press")
|
|
50616
|
+
return false;
|
|
50617
|
+
if (touchSelectionState.pendingPointerId !== pointerId || !touchSelectionState.pendingCell) {
|
|
50618
|
+
return false;
|
|
50619
|
+
}
|
|
50620
|
+
if (performance.now() - touchSelectionState.pendingStartedAt < touchSelectionLongPressMs) {
|
|
50621
|
+
return false;
|
|
50622
|
+
}
|
|
50623
|
+
const pendingCell = touchSelectionState.pendingCell;
|
|
50624
|
+
clearPendingTouchSelection();
|
|
50625
|
+
beginSelectionDrag(pendingCell, pointerId);
|
|
50626
|
+
return true;
|
|
50627
|
+
}
|
|
50628
|
+
function beginSelectionDrag(cell, pointerId) {
|
|
50629
|
+
selectionState.active = true;
|
|
50630
|
+
selectionState.dragging = true;
|
|
50631
|
+
selectionState.anchor = cell;
|
|
50632
|
+
selectionState.focus = cell;
|
|
50633
|
+
touchSelectionState.activePointerId = pointerId;
|
|
50634
|
+
touchSelectionState.panPointerId = null;
|
|
50635
|
+
canvas.setPointerCapture?.(pointerId);
|
|
50636
|
+
updateCanvasCursor();
|
|
50637
|
+
needsRender = true;
|
|
50638
|
+
}
|
|
50570
50639
|
function noteScrollActivity() {
|
|
50571
50640
|
scrollbarState.lastInputAt = performance.now();
|
|
50572
50641
|
}
|
|
50642
|
+
function getViewportScrollOffset() {
|
|
50643
|
+
if (!wasmHandle || !wasmExports?.restty_scrollbar_offset)
|
|
50644
|
+
return 0;
|
|
50645
|
+
return wasmExports.restty_scrollbar_offset(wasmHandle) || 0;
|
|
50646
|
+
}
|
|
50647
|
+
function shiftSelectionByRows(deltaRows) {
|
|
50648
|
+
if (!deltaRows)
|
|
50649
|
+
return;
|
|
50650
|
+
if (!selectionState.active && !selectionState.dragging)
|
|
50651
|
+
return;
|
|
50652
|
+
if (!selectionState.anchor || !selectionState.focus)
|
|
50653
|
+
return;
|
|
50654
|
+
const maxAbs = Math.max(1024, (gridState.rows || 24) * 128);
|
|
50655
|
+
selectionState.anchor = {
|
|
50656
|
+
row: clamp(selectionState.anchor.row + deltaRows, -maxAbs, maxAbs),
|
|
50657
|
+
col: selectionState.anchor.col
|
|
50658
|
+
};
|
|
50659
|
+
selectionState.focus = {
|
|
50660
|
+
row: clamp(selectionState.focus.row + deltaRows, -maxAbs, maxAbs),
|
|
50661
|
+
col: selectionState.focus.col
|
|
50662
|
+
};
|
|
50663
|
+
needsRender = true;
|
|
50664
|
+
}
|
|
50665
|
+
function scrollViewportByLines(lines) {
|
|
50666
|
+
if (!wasmReady || !wasmHandle || !gridState.cellH)
|
|
50667
|
+
return;
|
|
50668
|
+
scrollRemainder += lines;
|
|
50669
|
+
const delta = Math.trunc(scrollRemainder);
|
|
50670
|
+
scrollRemainder -= delta;
|
|
50671
|
+
if (!delta)
|
|
50672
|
+
return;
|
|
50673
|
+
const beforeOffset = getViewportScrollOffset();
|
|
50674
|
+
wasm.scrollViewport(wasmHandle, delta);
|
|
50675
|
+
const afterOffset = getViewportScrollOffset();
|
|
50676
|
+
shiftSelectionByRows(beforeOffset - afterOffset);
|
|
50677
|
+
if (linkState.hoverId)
|
|
50678
|
+
updateLinkHover(null);
|
|
50679
|
+
wasm.renderUpdate(wasmHandle);
|
|
50680
|
+
needsRender = true;
|
|
50681
|
+
noteScrollActivity();
|
|
50682
|
+
}
|
|
50683
|
+
function setViewportScrollOffset(nextOffset) {
|
|
50684
|
+
if (!wasmReady || !wasmHandle || !wasmExports?.restty_scrollbar_total)
|
|
50685
|
+
return;
|
|
50686
|
+
const total = wasmExports.restty_scrollbar_total(wasmHandle) || 0;
|
|
50687
|
+
const len = wasmExports.restty_scrollbar_len ? wasmExports.restty_scrollbar_len(wasmHandle) : 0;
|
|
50688
|
+
const current = wasmExports.restty_scrollbar_offset ? wasmExports.restty_scrollbar_offset(wasmHandle) : 0;
|
|
50689
|
+
const maxOffset = Math.max(0, total - len);
|
|
50690
|
+
const clamped = clamp(Math.round(nextOffset), 0, maxOffset);
|
|
50691
|
+
const delta = clamped - current;
|
|
50692
|
+
if (!delta)
|
|
50693
|
+
return;
|
|
50694
|
+
const beforeOffset = getViewportScrollOffset();
|
|
50695
|
+
wasm.scrollViewport(wasmHandle, delta);
|
|
50696
|
+
const afterOffset = getViewportScrollOffset();
|
|
50697
|
+
shiftSelectionByRows(beforeOffset - afterOffset);
|
|
50698
|
+
if (linkState.hoverId)
|
|
50699
|
+
updateLinkHover(null);
|
|
50700
|
+
wasm.renderUpdate(wasmHandle);
|
|
50701
|
+
needsRender = true;
|
|
50702
|
+
noteScrollActivity();
|
|
50703
|
+
}
|
|
50704
|
+
function pointerToCanvasPx(event) {
|
|
50705
|
+
const rect = canvas.getBoundingClientRect();
|
|
50706
|
+
const scaleX = rect.width > 0 ? canvas.width / rect.width : 1;
|
|
50707
|
+
const scaleY = rect.height > 0 ? canvas.height / rect.height : 1;
|
|
50708
|
+
return {
|
|
50709
|
+
x: (event.clientX - rect.left) * scaleX,
|
|
50710
|
+
y: (event.clientY - rect.top) * scaleY
|
|
50711
|
+
};
|
|
50712
|
+
}
|
|
50713
|
+
function computeOverlayScrollbarLayout(rows, cellW, cellH, total, offset, len) {
|
|
50714
|
+
if (!(total > len && len > 0))
|
|
50715
|
+
return null;
|
|
50716
|
+
const trackHeight = rows * cellH;
|
|
50717
|
+
const width = Math.max(10, Math.round(cellW * 0.9));
|
|
50718
|
+
const margin = Math.max(6, Math.round(width * 0.5));
|
|
50719
|
+
const insetY = Math.max(2, Math.round(cellH * 0.2));
|
|
50720
|
+
const trackX = canvas.width - margin - width;
|
|
50721
|
+
const trackY = insetY;
|
|
50722
|
+
const trackH = Math.max(width, trackHeight - insetY * 2);
|
|
50723
|
+
const denom = Math.max(1, total - len);
|
|
50724
|
+
const thumbH = Math.max(width, Math.round(trackH * (len / total)));
|
|
50725
|
+
const thumbY = trackY + Math.round(offset / denom * (trackH - thumbH));
|
|
50726
|
+
return { total, offset, len, denom, width, trackX, trackY, trackH, thumbY, thumbH };
|
|
50727
|
+
}
|
|
50728
|
+
function getOverlayScrollbarLayout() {
|
|
50729
|
+
if (!showOverlayScrollbar || !wasmExports?.restty_scrollbar_total || !wasmHandle)
|
|
50730
|
+
return null;
|
|
50731
|
+
if (!gridState.rows || !gridState.cellW || !gridState.cellH)
|
|
50732
|
+
return null;
|
|
50733
|
+
const total = wasmExports.restty_scrollbar_total(wasmHandle) || 0;
|
|
50734
|
+
const offset = wasmExports.restty_scrollbar_offset ? wasmExports.restty_scrollbar_offset(wasmHandle) : 0;
|
|
50735
|
+
const len = wasmExports.restty_scrollbar_len ? wasmExports.restty_scrollbar_len(wasmHandle) : gridState.rows;
|
|
50736
|
+
return computeOverlayScrollbarLayout(gridState.rows, gridState.cellW, gridState.cellH, total, offset, len);
|
|
50737
|
+
}
|
|
50738
|
+
function isPointInScrollbarHitArea(layout, x3, y) {
|
|
50739
|
+
const hitPadX = Math.max(3, Math.round(layout.width * 0.35));
|
|
50740
|
+
return x3 >= layout.trackX - hitPadX && x3 <= layout.trackX + layout.width + hitPadX && y >= layout.trackY && y <= layout.trackY + layout.trackH;
|
|
50741
|
+
}
|
|
50742
|
+
function isPointInScrollbarThumb(layout, x3, y) {
|
|
50743
|
+
return x3 >= layout.trackX && x3 <= layout.trackX + layout.width && y >= layout.thumbY && y <= layout.thumbY + layout.thumbH;
|
|
50744
|
+
}
|
|
50745
|
+
function scrollbarOffsetForPointerY(layout, pointerY, thumbGrabRatio) {
|
|
50746
|
+
const thumbTop = pointerY - layout.thumbH * thumbGrabRatio;
|
|
50747
|
+
const trackSpan = Math.max(1, layout.trackH - layout.thumbH);
|
|
50748
|
+
const ratio = clamp((thumbTop - layout.trackY) / trackSpan, 0, 1);
|
|
50749
|
+
return Math.round(ratio * layout.denom);
|
|
50750
|
+
}
|
|
50751
|
+
function pushRoundedVerticalBar(out, x3, y, w, h, color) {
|
|
50752
|
+
const x02 = Math.round(x3);
|
|
50753
|
+
const y02 = Math.round(y);
|
|
50754
|
+
const width = Math.max(1, Math.round(w));
|
|
50755
|
+
const height = Math.max(1, Math.round(h));
|
|
50756
|
+
const radius = Math.min(Math.floor(width * 0.5), Math.floor(height * 0.5));
|
|
50757
|
+
if (radius <= 0) {
|
|
50758
|
+
pushRectBox(out, x02, y02, width, height, color);
|
|
50759
|
+
return;
|
|
50760
|
+
}
|
|
50761
|
+
const middleH = height - radius * 2;
|
|
50762
|
+
if (middleH > 0) {
|
|
50763
|
+
pushRectBox(out, x02, y02 + radius, width, middleH, color);
|
|
50764
|
+
}
|
|
50765
|
+
const radiusSq = radius * radius;
|
|
50766
|
+
for (let row = 0;row < radius; row += 1) {
|
|
50767
|
+
const dy = radius - row - 0.5;
|
|
50768
|
+
const inset = Math.max(0, Math.floor(radius - Math.sqrt(Math.max(0, radiusSq - dy * dy))));
|
|
50769
|
+
const rowW = width - inset * 2;
|
|
50770
|
+
if (rowW <= 0)
|
|
50771
|
+
continue;
|
|
50772
|
+
pushRectBox(out, x02 + inset, y02 + row, rowW, 1, color);
|
|
50773
|
+
pushRectBox(out, x02 + inset, y02 + height - 1 - row, rowW, 1, color);
|
|
50774
|
+
}
|
|
50775
|
+
}
|
|
50776
|
+
function appendOverlayScrollbar(overlayData, rows, cellW, cellH, total, offset, len) {
|
|
50777
|
+
if (!showOverlayScrollbar)
|
|
50778
|
+
return;
|
|
50779
|
+
const layout = computeOverlayScrollbarLayout(rows, cellW, cellH, total, offset, len);
|
|
50780
|
+
if (!layout)
|
|
50781
|
+
return;
|
|
50782
|
+
const since = performance.now() - scrollbarState.lastInputAt;
|
|
50783
|
+
const fadeDelay = 160;
|
|
50784
|
+
const fadeDuration = 520;
|
|
50785
|
+
let alpha = 0;
|
|
50786
|
+
if (since < fadeDelay) {
|
|
50787
|
+
alpha = 0.68;
|
|
50788
|
+
} else if (since < fadeDelay + fadeDuration) {
|
|
50789
|
+
alpha = 0.68 * (1 - (since - fadeDelay) / fadeDuration);
|
|
50790
|
+
}
|
|
50791
|
+
if (alpha <= 0.01)
|
|
50792
|
+
return;
|
|
50793
|
+
const thumbColor = [0.96, 0.96, 0.96, alpha * 0.75];
|
|
50794
|
+
pushRoundedVerticalBar(overlayData, layout.trackX, layout.thumbY, layout.width, layout.thumbH, thumbColor);
|
|
50795
|
+
}
|
|
50573
50796
|
function releaseKittyImage(entry) {
|
|
50574
50797
|
const source = entry?.source;
|
|
50575
50798
|
if (source && typeof source.close === "function") {
|
|
@@ -51167,6 +51390,7 @@ function createResttyApp(options) {
|
|
|
51167
51390
|
selectionState.dragging = false;
|
|
51168
51391
|
selectionState.anchor = null;
|
|
51169
51392
|
selectionState.focus = null;
|
|
51393
|
+
touchSelectionState.activePointerId = null;
|
|
51170
51394
|
updateCanvasCursor();
|
|
51171
51395
|
needsRender = true;
|
|
51172
51396
|
}
|
|
@@ -51380,30 +51604,116 @@ function createResttyApp(options) {
|
|
|
51380
51604
|
function bindCanvasEvents() {
|
|
51381
51605
|
if (!attachCanvasEvents)
|
|
51382
51606
|
return;
|
|
51607
|
+
canvas.style.touchAction = touchSelectionMode === "long-press" || touchSelectionMode === "drag" ? "none" : "pan-y pinch-zoom";
|
|
51383
51608
|
const onPointerDown = (event) => {
|
|
51609
|
+
if (!isTouchPointer(event) && event.button === 0) {
|
|
51610
|
+
const layout = getOverlayScrollbarLayout();
|
|
51611
|
+
if (layout) {
|
|
51612
|
+
const point = pointerToCanvasPx(event);
|
|
51613
|
+
if (isPointInScrollbarHitArea(layout, point.x, point.y)) {
|
|
51614
|
+
event.preventDefault();
|
|
51615
|
+
noteScrollActivity();
|
|
51616
|
+
const hitThumb = isPointInScrollbarThumb(layout, point.x, point.y);
|
|
51617
|
+
scrollbarDragState.pointerId = event.pointerId;
|
|
51618
|
+
scrollbarDragState.thumbGrabRatio = hitThumb ? clamp((point.y - layout.thumbY) / Math.max(1, layout.thumbH), 0, 1) : 0.5;
|
|
51619
|
+
const targetOffset = scrollbarOffsetForPointerY(layout, point.y, scrollbarDragState.thumbGrabRatio);
|
|
51620
|
+
setViewportScrollOffset(targetOffset);
|
|
51621
|
+
canvas.setPointerCapture?.(event.pointerId);
|
|
51622
|
+
return;
|
|
51623
|
+
}
|
|
51624
|
+
}
|
|
51625
|
+
}
|
|
51384
51626
|
if (inputHandler.sendMouseEvent("down", event)) {
|
|
51385
51627
|
event.preventDefault();
|
|
51386
51628
|
canvas.setPointerCapture?.(event.pointerId);
|
|
51387
51629
|
return;
|
|
51388
51630
|
}
|
|
51631
|
+
if (isTouchPointer(event)) {
|
|
51632
|
+
if (event.button !== 0)
|
|
51633
|
+
return;
|
|
51634
|
+
const cell2 = normalizeSelectionCell(positionToCell(event));
|
|
51635
|
+
touchSelectionState.activePointerId = null;
|
|
51636
|
+
touchSelectionState.panPointerId = null;
|
|
51637
|
+
if (touchSelectionMode === "off")
|
|
51638
|
+
return;
|
|
51639
|
+
if (touchSelectionMode === "drag") {
|
|
51640
|
+
event.preventDefault();
|
|
51641
|
+
beginSelectionDrag(cell2, event.pointerId);
|
|
51642
|
+
return;
|
|
51643
|
+
}
|
|
51644
|
+
clearPendingTouchSelection();
|
|
51645
|
+
touchSelectionState.pendingPointerId = event.pointerId;
|
|
51646
|
+
touchSelectionState.pendingCell = cell2;
|
|
51647
|
+
touchSelectionState.pendingStartedAt = performance.now();
|
|
51648
|
+
touchSelectionState.pendingStartX = event.clientX;
|
|
51649
|
+
touchSelectionState.pendingStartY = event.clientY;
|
|
51650
|
+
touchSelectionState.panPointerId = event.pointerId;
|
|
51651
|
+
touchSelectionState.panLastY = event.clientY;
|
|
51652
|
+
touchSelectionState.pendingTimer = setTimeout(() => {
|
|
51653
|
+
tryActivatePendingTouchSelection(event.pointerId);
|
|
51654
|
+
}, touchSelectionLongPressMs);
|
|
51655
|
+
return;
|
|
51656
|
+
}
|
|
51389
51657
|
if (event.button !== 0)
|
|
51390
51658
|
return;
|
|
51391
51659
|
event.preventDefault();
|
|
51392
51660
|
const cell = normalizeSelectionCell(positionToCell(event));
|
|
51393
51661
|
updateLinkHover(cell);
|
|
51394
|
-
|
|
51395
|
-
selectionState.dragging = true;
|
|
51396
|
-
selectionState.anchor = cell;
|
|
51397
|
-
selectionState.focus = cell;
|
|
51398
|
-
canvas.setPointerCapture?.(event.pointerId);
|
|
51399
|
-
updateCanvasCursor();
|
|
51400
|
-
needsRender = true;
|
|
51662
|
+
beginSelectionDrag(cell, event.pointerId);
|
|
51401
51663
|
};
|
|
51402
51664
|
const onPointerMove = (event) => {
|
|
51665
|
+
if (scrollbarDragState.pointerId === event.pointerId) {
|
|
51666
|
+
const layout = getOverlayScrollbarLayout();
|
|
51667
|
+
if (!layout) {
|
|
51668
|
+
scrollbarDragState.pointerId = null;
|
|
51669
|
+
return;
|
|
51670
|
+
}
|
|
51671
|
+
const point = pointerToCanvasPx(event);
|
|
51672
|
+
const targetOffset = scrollbarOffsetForPointerY(layout, point.y, scrollbarDragState.thumbGrabRatio);
|
|
51673
|
+
setViewportScrollOffset(targetOffset);
|
|
51674
|
+
event.preventDefault();
|
|
51675
|
+
return;
|
|
51676
|
+
}
|
|
51403
51677
|
if (inputHandler.sendMouseEvent("move", event)) {
|
|
51404
51678
|
event.preventDefault();
|
|
51405
51679
|
return;
|
|
51406
51680
|
}
|
|
51681
|
+
if (isTouchPointer(event)) {
|
|
51682
|
+
if (touchSelectionState.pendingPointerId === event.pointerId) {
|
|
51683
|
+
const dx = event.clientX - touchSelectionState.pendingStartX;
|
|
51684
|
+
const dy = event.clientY - touchSelectionState.pendingStartY;
|
|
51685
|
+
if (dx * dx + dy * dy >= touchSelectionMoveThresholdPx * touchSelectionMoveThresholdPx) {
|
|
51686
|
+
clearPendingTouchSelection();
|
|
51687
|
+
} else {
|
|
51688
|
+
tryActivatePendingTouchSelection(event.pointerId);
|
|
51689
|
+
}
|
|
51690
|
+
if (touchSelectionState.pendingPointerId === event.pointerId) {
|
|
51691
|
+
if (touchSelectionMode === "long-press" && touchSelectionState.panPointerId === event.pointerId) {
|
|
51692
|
+
const deltaPx = touchSelectionState.panLastY - event.clientY;
|
|
51693
|
+
touchSelectionState.panLastY = event.clientY;
|
|
51694
|
+
scrollViewportByLines(deltaPx / Math.max(1, gridState.cellH) * 1.5);
|
|
51695
|
+
event.preventDefault();
|
|
51696
|
+
}
|
|
51697
|
+
return;
|
|
51698
|
+
}
|
|
51699
|
+
}
|
|
51700
|
+
if (selectionState.dragging && touchSelectionState.activePointerId === event.pointerId) {
|
|
51701
|
+
const cell2 = normalizeSelectionCell(positionToCell(event));
|
|
51702
|
+
event.preventDefault();
|
|
51703
|
+
selectionState.focus = cell2;
|
|
51704
|
+
updateLinkHover(null);
|
|
51705
|
+
updateCanvasCursor();
|
|
51706
|
+
needsRender = true;
|
|
51707
|
+
return;
|
|
51708
|
+
}
|
|
51709
|
+
if (touchSelectionMode === "long-press" && touchSelectionState.panPointerId === event.pointerId) {
|
|
51710
|
+
const deltaPx = touchSelectionState.panLastY - event.clientY;
|
|
51711
|
+
touchSelectionState.panLastY = event.clientY;
|
|
51712
|
+
scrollViewportByLines(deltaPx / Math.max(1, gridState.cellH) * 1.5);
|
|
51713
|
+
event.preventDefault();
|
|
51714
|
+
}
|
|
51715
|
+
return;
|
|
51716
|
+
}
|
|
51407
51717
|
const cell = normalizeSelectionCell(positionToCell(event));
|
|
51408
51718
|
if (!selectionState.dragging) {
|
|
51409
51719
|
updateLinkHover(cell);
|
|
@@ -51416,10 +51726,41 @@ function createResttyApp(options) {
|
|
|
51416
51726
|
needsRender = true;
|
|
51417
51727
|
};
|
|
51418
51728
|
const onPointerUp = (event) => {
|
|
51729
|
+
if (scrollbarDragState.pointerId === event.pointerId) {
|
|
51730
|
+
scrollbarDragState.pointerId = null;
|
|
51731
|
+
event.preventDefault();
|
|
51732
|
+
return;
|
|
51733
|
+
}
|
|
51419
51734
|
if (inputHandler.sendMouseEvent("up", event)) {
|
|
51420
51735
|
event.preventDefault();
|
|
51421
51736
|
return;
|
|
51422
51737
|
}
|
|
51738
|
+
if (isTouchPointer(event)) {
|
|
51739
|
+
if (touchSelectionState.pendingPointerId === event.pointerId) {
|
|
51740
|
+
clearPendingTouchSelection();
|
|
51741
|
+
touchSelectionState.activePointerId = null;
|
|
51742
|
+
touchSelectionState.panPointerId = null;
|
|
51743
|
+
return;
|
|
51744
|
+
}
|
|
51745
|
+
if (selectionState.dragging && touchSelectionState.activePointerId === event.pointerId) {
|
|
51746
|
+
const cell2 = normalizeSelectionCell(positionToCell(event));
|
|
51747
|
+
event.preventDefault();
|
|
51748
|
+
selectionState.dragging = false;
|
|
51749
|
+
selectionState.focus = cell2;
|
|
51750
|
+
touchSelectionState.activePointerId = null;
|
|
51751
|
+
if (selectionState.anchor && selectionState.focus && selectionState.anchor.row === selectionState.focus.row && selectionState.anchor.col === selectionState.focus.col) {
|
|
51752
|
+
clearSelection();
|
|
51753
|
+
} else {
|
|
51754
|
+
updateCanvasCursor();
|
|
51755
|
+
needsRender = true;
|
|
51756
|
+
}
|
|
51757
|
+
return;
|
|
51758
|
+
}
|
|
51759
|
+
if (touchSelectionState.panPointerId === event.pointerId) {
|
|
51760
|
+
touchSelectionState.panPointerId = null;
|
|
51761
|
+
}
|
|
51762
|
+
return;
|
|
51763
|
+
}
|
|
51423
51764
|
const cell = normalizeSelectionCell(positionToCell(event));
|
|
51424
51765
|
if (selectionState.dragging) {
|
|
51425
51766
|
event.preventDefault();
|
|
@@ -51434,10 +51775,31 @@ function createResttyApp(options) {
|
|
|
51434
51775
|
} else {
|
|
51435
51776
|
updateLinkHover(cell);
|
|
51436
51777
|
}
|
|
51437
|
-
if (!selectionState.active && event.button === 0 &&
|
|
51778
|
+
if (!selectionState.active && event.button === 0 && linkState.hoverUri) {
|
|
51438
51779
|
openLink(linkState.hoverUri);
|
|
51439
51780
|
}
|
|
51440
51781
|
};
|
|
51782
|
+
const onPointerCancel = (event) => {
|
|
51783
|
+
if (scrollbarDragState.pointerId === event.pointerId) {
|
|
51784
|
+
scrollbarDragState.pointerId = null;
|
|
51785
|
+
}
|
|
51786
|
+
if (isTouchPointer(event)) {
|
|
51787
|
+
if (touchSelectionState.pendingPointerId === event.pointerId) {
|
|
51788
|
+
clearPendingTouchSelection();
|
|
51789
|
+
}
|
|
51790
|
+
if (touchSelectionState.panPointerId === event.pointerId) {
|
|
51791
|
+
touchSelectionState.panPointerId = null;
|
|
51792
|
+
}
|
|
51793
|
+
if (touchSelectionState.activePointerId === event.pointerId) {
|
|
51794
|
+
touchSelectionState.activePointerId = null;
|
|
51795
|
+
if (selectionState.dragging) {
|
|
51796
|
+
selectionState.dragging = false;
|
|
51797
|
+
updateCanvasCursor();
|
|
51798
|
+
needsRender = true;
|
|
51799
|
+
}
|
|
51800
|
+
}
|
|
51801
|
+
}
|
|
51802
|
+
};
|
|
51441
51803
|
const onWheel = (event) => {
|
|
51442
51804
|
const mouseActive = inputHandler.isMouseActive();
|
|
51443
51805
|
const altScreen = inputHandler.isAltScreen ? inputHandler.isAltScreen() : false;
|
|
@@ -51458,16 +51820,7 @@ function createResttyApp(options) {
|
|
|
51458
51820
|
} else {
|
|
51459
51821
|
lines = event.deltaY / gridState.cellH;
|
|
51460
51822
|
}
|
|
51461
|
-
lines
|
|
51462
|
-
scrollRemainder += lines;
|
|
51463
|
-
const delta = Math.trunc(scrollRemainder);
|
|
51464
|
-
scrollRemainder -= delta;
|
|
51465
|
-
if (!delta)
|
|
51466
|
-
return;
|
|
51467
|
-
wasm.scrollViewport(wasmHandle, delta);
|
|
51468
|
-
wasm.renderUpdate(wasmHandle);
|
|
51469
|
-
needsRender = true;
|
|
51470
|
-
noteScrollActivity();
|
|
51823
|
+
scrollViewportByLines(lines * speed);
|
|
51471
51824
|
event.preventDefault();
|
|
51472
51825
|
};
|
|
51473
51826
|
const onContextMenu = (event) => {
|
|
@@ -51480,6 +51833,7 @@ function createResttyApp(options) {
|
|
|
51480
51833
|
canvas.addEventListener("pointerdown", onPointerDown);
|
|
51481
51834
|
canvas.addEventListener("pointermove", onPointerMove);
|
|
51482
51835
|
canvas.addEventListener("pointerup", onPointerUp);
|
|
51836
|
+
canvas.addEventListener("pointercancel", onPointerCancel);
|
|
51483
51837
|
canvas.addEventListener("pointerleave", onPointerLeave);
|
|
51484
51838
|
canvas.addEventListener("wheel", onWheel, { passive: false });
|
|
51485
51839
|
canvas.addEventListener("contextmenu", onContextMenu);
|
|
@@ -51487,9 +51841,12 @@ function createResttyApp(options) {
|
|
|
51487
51841
|
canvas.removeEventListener("pointerdown", onPointerDown);
|
|
51488
51842
|
canvas.removeEventListener("pointermove", onPointerMove);
|
|
51489
51843
|
canvas.removeEventListener("pointerup", onPointerUp);
|
|
51844
|
+
canvas.removeEventListener("pointercancel", onPointerCancel);
|
|
51490
51845
|
canvas.removeEventListener("pointerleave", onPointerLeave);
|
|
51491
51846
|
canvas.removeEventListener("wheel", onWheel);
|
|
51492
51847
|
canvas.removeEventListener("contextmenu", onContextMenu);
|
|
51848
|
+
clearPendingTouchSelection();
|
|
51849
|
+
scrollbarDragState.pointerId = null;
|
|
51493
51850
|
});
|
|
51494
51851
|
if (imeInput) {
|
|
51495
51852
|
let suppressNextInput = false;
|
|
@@ -53632,7 +53989,9 @@ function createResttyApp(options) {
|
|
|
53632
53989
|
const startRow = forward ? a3.row : f.row;
|
|
53633
53990
|
const endRow = forward ? f.row : a3.row;
|
|
53634
53991
|
const lines = [];
|
|
53635
|
-
|
|
53992
|
+
const clampedStartRow = clamp(startRow, 0, rows - 1);
|
|
53993
|
+
const clampedEndRow = clamp(endRow, 0, rows - 1);
|
|
53994
|
+
for (let row = clampedStartRow;row <= clampedEndRow; row += 1) {
|
|
53636
53995
|
const range = selectionForRow(row, cols);
|
|
53637
53996
|
if (!range)
|
|
53638
53997
|
continue;
|
|
@@ -53759,8 +54118,6 @@ function createResttyApp(options) {
|
|
|
53759
54118
|
graphemeOffset,
|
|
53760
54119
|
graphemeLen,
|
|
53761
54120
|
graphemeBuffer,
|
|
53762
|
-
selectionStart,
|
|
53763
|
-
selectionEnd,
|
|
53764
54121
|
cursor
|
|
53765
54122
|
} = render;
|
|
53766
54123
|
if (!codepoints || !fgBytes)
|
|
@@ -53951,13 +54308,9 @@ function createResttyApp(options) {
|
|
|
53951
54308
|
for (let row = 0;row < rows; row += 1) {
|
|
53952
54309
|
const rowY = row * cellH;
|
|
53953
54310
|
const baseY = rowY + yPad + baselineOffset;
|
|
53954
|
-
let selStart = selectionStart ? selectionStart[row] : -1;
|
|
53955
|
-
let selEnd = selectionEnd ? selectionEnd[row] : -1;
|
|
53956
54311
|
const localSel = selectionState.active ? selectionForRow(row, cols) : null;
|
|
53957
|
-
|
|
53958
|
-
|
|
53959
|
-
selEnd = localSel.end;
|
|
53960
|
-
}
|
|
54312
|
+
const selStart = localSel?.start ?? -1;
|
|
54313
|
+
const selEnd = localSel?.end ?? -1;
|
|
53961
54314
|
if (selStart >= 0 && selEnd > selStart) {
|
|
53962
54315
|
const start = Math.max(0, selStart);
|
|
53963
54316
|
const end = Math.min(cols, selEnd);
|
|
@@ -54465,36 +54818,8 @@ function createResttyApp(options) {
|
|
|
54465
54818
|
scrollbarState.lastTotal = total;
|
|
54466
54819
|
scrollbarState.lastOffset = offset;
|
|
54467
54820
|
scrollbarState.lastLen = len;
|
|
54468
|
-
noteScrollActivity();
|
|
54469
|
-
}
|
|
54470
|
-
if (total > len && len > 0) {
|
|
54471
|
-
const now = performance.now();
|
|
54472
|
-
const since = now - scrollbarState.lastInputAt;
|
|
54473
|
-
const fadeDelay = 600;
|
|
54474
|
-
const fadeDuration = 700;
|
|
54475
|
-
let alpha = 0;
|
|
54476
|
-
if (offset > 0) {
|
|
54477
|
-
alpha = 0.65;
|
|
54478
|
-
} else if (since < fadeDelay) {
|
|
54479
|
-
alpha = 0.5;
|
|
54480
|
-
} else if (since < fadeDelay + fadeDuration) {
|
|
54481
|
-
alpha = 0.5 * (1 - (since - fadeDelay) / fadeDuration);
|
|
54482
|
-
}
|
|
54483
|
-
if (alpha > 0.01) {
|
|
54484
|
-
const trackH = rows * cellH;
|
|
54485
|
-
const scrollbarWidth = Math.max(2, Math.round(cellW * 0.12));
|
|
54486
|
-
const margin = Math.max(2, Math.round(cellW * 0.2));
|
|
54487
|
-
const trackX = canvas.width - margin - scrollbarWidth;
|
|
54488
|
-
const trackY = 0;
|
|
54489
|
-
const denom = Math.max(1, total - len);
|
|
54490
|
-
const thumbH = Math.max(cellH * 1.5, Math.round(trackH * (len / total)));
|
|
54491
|
-
const thumbY = Math.round(offset / denom * (trackH - thumbH));
|
|
54492
|
-
const thumbColor = [1, 1, 1, alpha * 0.35];
|
|
54493
|
-
const trackColor = [1, 1, 1, alpha * 0.08];
|
|
54494
|
-
pushRectBox(overlayData, trackX, trackY, scrollbarWidth, trackH, trackColor);
|
|
54495
|
-
pushRectBox(overlayData, trackX, thumbY, scrollbarWidth, thumbH, thumbColor);
|
|
54496
|
-
}
|
|
54497
54821
|
}
|
|
54822
|
+
appendOverlayScrollbar(overlayData, rows, cellW, cellH, total, offset, len);
|
|
54498
54823
|
}
|
|
54499
54824
|
webgpuUniforms[0] = canvas.width;
|
|
54500
54825
|
webgpuUniforms[1] = canvas.height;
|
|
@@ -54670,8 +54995,6 @@ function createResttyApp(options) {
|
|
|
54670
54995
|
graphemeOffset,
|
|
54671
54996
|
graphemeLen,
|
|
54672
54997
|
graphemeBuffer,
|
|
54673
|
-
selectionStart,
|
|
54674
|
-
selectionEnd,
|
|
54675
54998
|
cursor
|
|
54676
54999
|
} = render;
|
|
54677
55000
|
if (!codepoints || !fgBytes) {
|
|
@@ -54849,13 +55172,9 @@ function createResttyApp(options) {
|
|
|
54849
55172
|
for (let row = 0;row < rows; row += 1) {
|
|
54850
55173
|
const rowY = row * cellH;
|
|
54851
55174
|
const baseY = rowY + yPad + baselineOffset;
|
|
54852
|
-
let selStart = selectionStart ? selectionStart[row] : -1;
|
|
54853
|
-
let selEnd = selectionEnd ? selectionEnd[row] : -1;
|
|
54854
55175
|
const localSel = selectionState.active ? selectionForRow(row, cols) : null;
|
|
54855
|
-
|
|
54856
|
-
|
|
54857
|
-
selEnd = localSel.end;
|
|
54858
|
-
}
|
|
55176
|
+
const selStart = localSel?.start ?? -1;
|
|
55177
|
+
const selEnd = localSel?.end ?? -1;
|
|
54859
55178
|
if (selStart >= 0 && selEnd > selStart) {
|
|
54860
55179
|
const start = Math.max(0, selStart);
|
|
54861
55180
|
const end = Math.min(cols, selEnd);
|
|
@@ -55189,36 +55508,8 @@ function createResttyApp(options) {
|
|
|
55189
55508
|
scrollbarState.lastTotal = total;
|
|
55190
55509
|
scrollbarState.lastOffset = offset;
|
|
55191
55510
|
scrollbarState.lastLen = len;
|
|
55192
|
-
noteScrollActivity();
|
|
55193
|
-
}
|
|
55194
|
-
if (total > len && len > 0) {
|
|
55195
|
-
const now = performance.now();
|
|
55196
|
-
const since = now - scrollbarState.lastInputAt;
|
|
55197
|
-
const fadeDelay = 600;
|
|
55198
|
-
const fadeDuration = 700;
|
|
55199
|
-
let alpha = 0;
|
|
55200
|
-
if (offset > 0) {
|
|
55201
|
-
alpha = 0.65;
|
|
55202
|
-
} else if (since < fadeDelay) {
|
|
55203
|
-
alpha = 0.5;
|
|
55204
|
-
} else if (since < fadeDelay + fadeDuration) {
|
|
55205
|
-
alpha = 0.5 * (1 - (since - fadeDelay) / fadeDuration);
|
|
55206
|
-
}
|
|
55207
|
-
if (alpha > 0.01) {
|
|
55208
|
-
const trackH = rows * cellH;
|
|
55209
|
-
const scrollbarWidth = Math.max(2, Math.round(cellW * 0.12));
|
|
55210
|
-
const margin = Math.max(2, Math.round(cellW * 0.2));
|
|
55211
|
-
const trackX = canvas.width - margin - scrollbarWidth;
|
|
55212
|
-
const trackY = 0;
|
|
55213
|
-
const denom = Math.max(1, total - len);
|
|
55214
|
-
const thumbH = Math.max(cellH * 1.5, Math.round(trackH * (len / total)));
|
|
55215
|
-
const thumbY = Math.round(offset / denom * (trackH - thumbH));
|
|
55216
|
-
const thumbColor = [1, 1, 1, alpha * 0.35];
|
|
55217
|
-
const trackColor = [1, 1, 1, alpha * 0.08];
|
|
55218
|
-
pushRectBox(overlayData, trackX, trackY, scrollbarWidth, trackH, trackColor);
|
|
55219
|
-
pushRectBox(overlayData, trackX, thumbY, scrollbarWidth, thumbH, thumbColor);
|
|
55220
|
-
}
|
|
55221
55511
|
}
|
|
55512
|
+
appendOverlayScrollbar(overlayData, rows, cellW, cellH, total, offset, len);
|
|
55222
55513
|
}
|
|
55223
55514
|
for (const [fontIndex, neededIds] of neededGlyphIdsByFont.entries()) {
|
|
55224
55515
|
const fontEntry = fontState.fonts[fontIndex];
|
|
@@ -55594,9 +55885,11 @@ function createResttyApp(options) {
|
|
|
55594
55885
|
}
|
|
55595
55886
|
appendLog(`[key] ${JSON.stringify(normalized)}${before}`);
|
|
55596
55887
|
}
|
|
55597
|
-
if (source
|
|
55888
|
+
if (source !== "program" && (selectionState.active || selectionState.dragging)) {
|
|
55598
55889
|
clearSelection();
|
|
55599
55890
|
}
|
|
55891
|
+
if (source === "pty" && linkState.hoverId)
|
|
55892
|
+
updateLinkHover(null);
|
|
55600
55893
|
writeToWasm(wasmHandle, normalized);
|
|
55601
55894
|
flushWasmOutputToPty();
|
|
55602
55895
|
if (source === "pty" && inputHandler?.isSynchronizedOutput?.()) {
|
package/dist/app/types.d.ts
CHANGED
|
@@ -63,6 +63,7 @@ export type ResttyLocalFontSource = {
|
|
|
63
63
|
export type ResttyFontSource = ResttyUrlFontSource | ResttyBufferFontSource | ResttyLocalFontSource;
|
|
64
64
|
export type FontSource = ResttyFontSource;
|
|
65
65
|
export type ResttyFontPreset = "default-cdn" | "none";
|
|
66
|
+
export type ResttyTouchSelectionMode = "drag" | "long-press" | "off";
|
|
66
67
|
export type ResttyAppOptions = {
|
|
67
68
|
canvas: HTMLCanvasElement;
|
|
68
69
|
session?: ResttyAppSession;
|
|
@@ -83,6 +84,23 @@ export type ResttyAppOptions = {
|
|
|
83
84
|
autoResize?: boolean;
|
|
84
85
|
attachWindowEvents?: boolean;
|
|
85
86
|
attachCanvasEvents?: boolean;
|
|
87
|
+
/**
|
|
88
|
+
* Touch selection behavior on pointerType=touch:
|
|
89
|
+
* - drag: immediate drag-selection (legacy behavior)
|
|
90
|
+
* - long-press: selection starts after press timeout (default)
|
|
91
|
+
* - off: disable touch selection, keep touch scrolling
|
|
92
|
+
*/
|
|
93
|
+
touchSelectionMode?: ResttyTouchSelectionMode;
|
|
94
|
+
/**
|
|
95
|
+
* Long-press timeout in ms for touch selection intent.
|
|
96
|
+
* Only used when touchSelectionMode is "long-press".
|
|
97
|
+
*/
|
|
98
|
+
touchSelectionLongPressMs?: number;
|
|
99
|
+
/**
|
|
100
|
+
* Pointer move threshold in CSS pixels before long-press selection is
|
|
101
|
+
* canceled and touch pan-scroll takes priority.
|
|
102
|
+
*/
|
|
103
|
+
touchSelectionMoveThresholdPx?: number;
|
|
86
104
|
debugExpose?: boolean;
|
|
87
105
|
ptyTransport?: PtyTransport;
|
|
88
106
|
};
|