restty 0.1.13 → 0.1.15
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 +219 -10
- package/dist/app/types.d.ts +18 -0
- package/dist/index.js +219 -10
- package/dist/input/index.js +7 -0
- package/dist/input/output.d.ts +2 -0
- package/dist/input/types.d.ts +1 -0
- package/dist/internal.js +219 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,6 +5,7 @@ Powerful, lightweight browser terminal. Batteries included.
|
|
|
5
5
|
Live demo: `https://restty.pages.dev/`
|
|
6
6
|
|
|
7
7
|
Powered by:
|
|
8
|
+
|
|
8
9
|
- `libghostty-vt` (WASM terminal core)
|
|
9
10
|
- `WebGPU` (with WebGL2 fallback)
|
|
10
11
|
- `text-shaper` (shaping + raster)
|
|
@@ -124,17 +125,37 @@ await restty.setFontSources([
|
|
|
124
125
|
]);
|
|
125
126
|
```
|
|
126
127
|
|
|
128
|
+
### Touch behavior (pan-first by default)
|
|
129
|
+
|
|
130
|
+
On touch devices, restty defaults to pan-first scrolling with long-press selection.
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
const restty = new Restty({
|
|
134
|
+
root: document.getElementById("terminal") as HTMLElement,
|
|
135
|
+
appOptions: {
|
|
136
|
+
// "long-press" (default) | "drag" | "off"
|
|
137
|
+
touchSelectionMode: "long-press",
|
|
138
|
+
// Optional tuning knobs:
|
|
139
|
+
touchSelectionLongPressMs: 450,
|
|
140
|
+
touchSelectionMoveThresholdPx: 10,
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
```
|
|
144
|
+
|
|
127
145
|
## API Snapshot
|
|
128
146
|
|
|
129
147
|
Primary class:
|
|
148
|
+
|
|
130
149
|
- `new Restty({ root, ...options })`
|
|
131
150
|
- `createRestty(options)`
|
|
132
151
|
|
|
133
152
|
Pane access:
|
|
153
|
+
|
|
134
154
|
- `panes()` / `pane(id)` / `activePane()` / `focusedPane()` / `forEachPane(visitor)`
|
|
135
155
|
- `splitActivePane("vertical" | "horizontal")` / `splitPane(id, direction)` / `closePane(id)`
|
|
136
156
|
|
|
137
157
|
Active-pane convenience:
|
|
158
|
+
|
|
138
159
|
- `connectPty(url)` / `disconnectPty()` / `isPtyConnected()`
|
|
139
160
|
- `setRenderer("auto" | "webgpu" | "webgl2")`
|
|
140
161
|
- `setFontSize(number)` / `setFontSources([...])`
|
|
@@ -148,6 +169,7 @@ Active-pane convenience:
|
|
|
148
169
|
## Advanced / Internal Modules
|
|
149
170
|
|
|
150
171
|
Use these only when you need lower-level control:
|
|
172
|
+
|
|
151
173
|
- `restty/wasm`: low-level WASM ABI wrapper (`loadResttyWasm`, `ResttyWasm`)
|
|
152
174
|
- `restty/input`: key/mouse/input encoding utilities
|
|
153
175
|
- `restty/pty`: PTY transport helpers
|
package/dist/app/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { ResttyApp, ResttyAppOptions } from "./types";
|
|
|
2
2
|
export { createResttyAppSession, getDefaultResttyAppSession } from "./session";
|
|
3
3
|
export { createResttyPaneManager, createDefaultResttyPaneContextMenuItems, getResttyShortcutModifierLabel, } from "./panes";
|
|
4
4
|
export { Restty } from "./restty";
|
|
5
|
-
export type { ResttyAppElements, ResttyAppCallbacks, FontSource, ResttyFontSource, ResttyUrlFontSource, ResttyBufferFontSource, ResttyLocalFontSource, ResttyWasmLogListener, ResttyAppSession, ResttyAppOptions, ResttyApp, } from "./types";
|
|
5
|
+
export type { ResttyAppElements, ResttyAppCallbacks, FontSource, ResttyFontSource, ResttyTouchSelectionMode, ResttyUrlFontSource, ResttyBufferFontSource, ResttyLocalFontSource, ResttyWasmLogListener, ResttyAppSession, ResttyAppOptions, ResttyApp, } from "./types";
|
|
6
6
|
export type { ResttyPaneSplitDirection, ResttyPaneContextMenuItem, ResttyPaneDefinition, ResttyPaneStyleOptions, ResttyPaneStylesOptions, ResttyPaneShortcutsOptions, ResttyPaneContextMenuOptions, CreateResttyPaneManagerOptions, ResttyPaneManager, ResttyPaneWithApp, CreateDefaultResttyPaneContextMenuItemsOptions, } from "./panes";
|
|
7
7
|
export type { ResttyOptions } from "./restty";
|
|
8
8
|
export declare function createResttyApp(options: ResttyAppOptions): ResttyApp;
|
package/dist/app/index.js
CHANGED
|
@@ -8300,6 +8300,7 @@ class OutputFilter {
|
|
|
8300
8300
|
altScreen = false;
|
|
8301
8301
|
bracketedPaste = false;
|
|
8302
8302
|
focusReporting = false;
|
|
8303
|
+
synchronizedOutput = false;
|
|
8303
8304
|
windowOpHandler;
|
|
8304
8305
|
getWindowMetrics;
|
|
8305
8306
|
clipboardWrite;
|
|
@@ -8333,6 +8334,9 @@ class OutputFilter {
|
|
|
8333
8334
|
isFocusReporting() {
|
|
8334
8335
|
return this.focusReporting;
|
|
8335
8336
|
}
|
|
8337
|
+
isSynchronizedOutput() {
|
|
8338
|
+
return this.synchronizedOutput;
|
|
8339
|
+
}
|
|
8336
8340
|
replyOscColor(code, rgb) {
|
|
8337
8341
|
const toHex4 = (value) => Math.round(Math.max(0, Math.min(255, value)) * 257).toString(16).padStart(4, "0");
|
|
8338
8342
|
const r = toHex4(rgb[0]);
|
|
@@ -8398,6 +8402,8 @@ class OutputFilter {
|
|
|
8398
8402
|
} else if (code === 1004) {
|
|
8399
8403
|
this.focusReporting = enabled;
|
|
8400
8404
|
handled = true;
|
|
8405
|
+
} else if (code === 2026) {
|
|
8406
|
+
this.synchronizedOutput = enabled;
|
|
8401
8407
|
}
|
|
8402
8408
|
}
|
|
8403
8409
|
return handled;
|
|
@@ -8581,6 +8587,7 @@ function createInputHandler(options = {}) {
|
|
|
8581
8587
|
isBracketedPaste: () => filter.isBracketedPaste(),
|
|
8582
8588
|
isFocusReporting: () => filter.isFocusReporting(),
|
|
8583
8589
|
isAltScreen: () => filter.isAltScreen(),
|
|
8590
|
+
isSynchronizedOutput: () => filter.isSynchronizedOutput(),
|
|
8584
8591
|
sendMouseEvent: (kind, event) => mouse.sendMouseEvent(kind, event)
|
|
8585
8592
|
};
|
|
8586
8593
|
}
|
|
@@ -50316,6 +50323,17 @@ function createRestty(options) {
|
|
|
50316
50323
|
}
|
|
50317
50324
|
|
|
50318
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
|
+
}
|
|
50319
50337
|
function createResttyApp(options) {
|
|
50320
50338
|
const { canvas: canvasInput, imeInput: imeInputInput, elements, callbacks } = options;
|
|
50321
50339
|
const session = options.session ?? getDefaultResttyAppSession();
|
|
@@ -50338,6 +50356,9 @@ function createResttyApp(options) {
|
|
|
50338
50356
|
const attachCanvasEvents = options.attachCanvasEvents ?? true;
|
|
50339
50357
|
const autoResize = options.autoResize ?? true;
|
|
50340
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);
|
|
50341
50362
|
const nerdIconScale = Number.isFinite(options.nerdIconScale) ? Number(options.nerdIconScale) : 1;
|
|
50342
50363
|
const alphaBlending = options.alphaBlending ?? "linear-corrected";
|
|
50343
50364
|
const srgbChannelToLinear = (c3) => c3 <= 0.04045 ? c3 / 12.92 : Math.pow((c3 + 0.055) / 1.055, 2.4);
|
|
@@ -50443,6 +50464,14 @@ function createResttyApp(options) {
|
|
|
50443
50464
|
let lastKeydownSeqAt = 0;
|
|
50444
50465
|
let nextBlinkTime = performance.now() + CURSOR_BLINK_MS;
|
|
50445
50466
|
const ptyTransport = options.ptyTransport ?? createWebSocketPtyTransport();
|
|
50467
|
+
const PTY_OUTPUT_IDLE_MS = 10;
|
|
50468
|
+
const PTY_OUTPUT_MAX_MS = 40;
|
|
50469
|
+
const SYNC_OUTPUT_RESET_MS = 1000;
|
|
50470
|
+
const SYNC_OUTPUT_RESET_SEQ = "\x1B[?2026l";
|
|
50471
|
+
let ptyOutputBuffer = "";
|
|
50472
|
+
let ptyOutputIdleTimer = 0;
|
|
50473
|
+
let ptyOutputMaxTimer = 0;
|
|
50474
|
+
let syncOutputResetTimer = 0;
|
|
50446
50475
|
let lastCursorForCpr = { row: 1, col: 1 };
|
|
50447
50476
|
let inputHandler = null;
|
|
50448
50477
|
let activeTheme = null;
|
|
@@ -50531,6 +50560,14 @@ function createResttyApp(options) {
|
|
|
50531
50560
|
anchor: null,
|
|
50532
50561
|
focus: null
|
|
50533
50562
|
};
|
|
50563
|
+
const touchSelectionState = {
|
|
50564
|
+
pendingPointerId: null,
|
|
50565
|
+
activePointerId: null,
|
|
50566
|
+
pendingCell: null,
|
|
50567
|
+
pendingStartX: 0,
|
|
50568
|
+
pendingStartY: 0,
|
|
50569
|
+
pendingTimer: 0
|
|
50570
|
+
};
|
|
50534
50571
|
const linkState = {
|
|
50535
50572
|
hoverId: 0,
|
|
50536
50573
|
hoverUri: ""
|
|
@@ -50552,6 +50589,27 @@ function createResttyApp(options) {
|
|
|
50552
50589
|
}
|
|
50553
50590
|
canvas.style.cursor = linkState.hoverId ? "pointer" : "default";
|
|
50554
50591
|
}
|
|
50592
|
+
function isTouchPointer(event) {
|
|
50593
|
+
return event.pointerType === "touch";
|
|
50594
|
+
}
|
|
50595
|
+
function clearPendingTouchSelection() {
|
|
50596
|
+
if (touchSelectionState.pendingTimer) {
|
|
50597
|
+
clearTimeout(touchSelectionState.pendingTimer);
|
|
50598
|
+
touchSelectionState.pendingTimer = 0;
|
|
50599
|
+
}
|
|
50600
|
+
touchSelectionState.pendingPointerId = null;
|
|
50601
|
+
touchSelectionState.pendingCell = null;
|
|
50602
|
+
}
|
|
50603
|
+
function beginSelectionDrag(cell, pointerId) {
|
|
50604
|
+
selectionState.active = true;
|
|
50605
|
+
selectionState.dragging = true;
|
|
50606
|
+
selectionState.anchor = cell;
|
|
50607
|
+
selectionState.focus = cell;
|
|
50608
|
+
touchSelectionState.activePointerId = pointerId;
|
|
50609
|
+
canvas.setPointerCapture?.(pointerId);
|
|
50610
|
+
updateCanvasCursor();
|
|
50611
|
+
needsRender = true;
|
|
50612
|
+
}
|
|
50555
50613
|
function noteScrollActivity() {
|
|
50556
50614
|
scrollbarState.lastInputAt = performance.now();
|
|
50557
50615
|
}
|
|
@@ -51152,6 +51210,7 @@ function createResttyApp(options) {
|
|
|
51152
51210
|
selectionState.dragging = false;
|
|
51153
51211
|
selectionState.anchor = null;
|
|
51154
51212
|
selectionState.focus = null;
|
|
51213
|
+
touchSelectionState.activePointerId = null;
|
|
51155
51214
|
updateCanvasCursor();
|
|
51156
51215
|
needsRender = true;
|
|
51157
51216
|
}
|
|
@@ -51202,7 +51261,71 @@ function createResttyApp(options) {
|
|
|
51202
51261
|
const label = status.active ? `${status.mode} (${status.detail})` : status.mode;
|
|
51203
51262
|
setMouseStatus(label);
|
|
51204
51263
|
}
|
|
51264
|
+
function cancelPtyOutputFlush() {
|
|
51265
|
+
if (ptyOutputIdleTimer) {
|
|
51266
|
+
clearTimeout(ptyOutputIdleTimer);
|
|
51267
|
+
ptyOutputIdleTimer = 0;
|
|
51268
|
+
}
|
|
51269
|
+
if (ptyOutputMaxTimer) {
|
|
51270
|
+
clearTimeout(ptyOutputMaxTimer);
|
|
51271
|
+
ptyOutputMaxTimer = 0;
|
|
51272
|
+
}
|
|
51273
|
+
}
|
|
51274
|
+
function cancelSyncOutputReset() {
|
|
51275
|
+
if (syncOutputResetTimer) {
|
|
51276
|
+
clearTimeout(syncOutputResetTimer);
|
|
51277
|
+
syncOutputResetTimer = 0;
|
|
51278
|
+
}
|
|
51279
|
+
}
|
|
51280
|
+
function scheduleSyncOutputReset() {
|
|
51281
|
+
if (syncOutputResetTimer)
|
|
51282
|
+
return;
|
|
51283
|
+
syncOutputResetTimer = setTimeout(() => {
|
|
51284
|
+
syncOutputResetTimer = 0;
|
|
51285
|
+
if (!inputHandler?.isSynchronizedOutput?.())
|
|
51286
|
+
return;
|
|
51287
|
+
const sanitized = inputHandler.filterOutput(SYNC_OUTPUT_RESET_SEQ) || SYNC_OUTPUT_RESET_SEQ;
|
|
51288
|
+
sendInput(sanitized, "pty");
|
|
51289
|
+
}, SYNC_OUTPUT_RESET_MS);
|
|
51290
|
+
}
|
|
51291
|
+
function flushPtyOutputBuffer() {
|
|
51292
|
+
const output = ptyOutputBuffer;
|
|
51293
|
+
ptyOutputBuffer = "";
|
|
51294
|
+
if (!output)
|
|
51295
|
+
return;
|
|
51296
|
+
sendInput(output, "pty");
|
|
51297
|
+
}
|
|
51298
|
+
function queuePtyOutput(text) {
|
|
51299
|
+
if (!text)
|
|
51300
|
+
return;
|
|
51301
|
+
ptyOutputBuffer += text;
|
|
51302
|
+
if (ptyOutputIdleTimer) {
|
|
51303
|
+
clearTimeout(ptyOutputIdleTimer);
|
|
51304
|
+
}
|
|
51305
|
+
ptyOutputIdleTimer = setTimeout(() => {
|
|
51306
|
+
ptyOutputIdleTimer = 0;
|
|
51307
|
+
if (ptyOutputMaxTimer) {
|
|
51308
|
+
clearTimeout(ptyOutputMaxTimer);
|
|
51309
|
+
ptyOutputMaxTimer = 0;
|
|
51310
|
+
}
|
|
51311
|
+
flushPtyOutputBuffer();
|
|
51312
|
+
}, PTY_OUTPUT_IDLE_MS);
|
|
51313
|
+
if (!ptyOutputMaxTimer) {
|
|
51314
|
+
ptyOutputMaxTimer = setTimeout(() => {
|
|
51315
|
+
ptyOutputMaxTimer = 0;
|
|
51316
|
+
if (ptyOutputIdleTimer) {
|
|
51317
|
+
clearTimeout(ptyOutputIdleTimer);
|
|
51318
|
+
ptyOutputIdleTimer = 0;
|
|
51319
|
+
}
|
|
51320
|
+
flushPtyOutputBuffer();
|
|
51321
|
+
}, PTY_OUTPUT_MAX_MS);
|
|
51322
|
+
}
|
|
51323
|
+
}
|
|
51205
51324
|
function disconnectPty2() {
|
|
51325
|
+
flushPtyOutputBuffer();
|
|
51326
|
+
cancelPtyOutputFlush();
|
|
51327
|
+
cancelSyncOutputReset();
|
|
51328
|
+
ptyOutputBuffer = "";
|
|
51206
51329
|
ptyTransport.disconnect();
|
|
51207
51330
|
updateMouseStatus();
|
|
51208
51331
|
setPtyStatus("disconnected");
|
|
@@ -51249,7 +51372,7 @@ function createResttyApp(options) {
|
|
|
51249
51372
|
const sanitized = inputHandler ? inputHandler.filterOutput(text) : text;
|
|
51250
51373
|
updateMouseStatus();
|
|
51251
51374
|
if (sanitized)
|
|
51252
|
-
|
|
51375
|
+
queuePtyOutput(sanitized);
|
|
51253
51376
|
}
|
|
51254
51377
|
}
|
|
51255
51378
|
});
|
|
@@ -51301,30 +51424,71 @@ function createResttyApp(options) {
|
|
|
51301
51424
|
function bindCanvasEvents() {
|
|
51302
51425
|
if (!attachCanvasEvents)
|
|
51303
51426
|
return;
|
|
51427
|
+
canvas.style.touchAction = touchSelectionMode === "drag" ? "none" : "pan-y pinch-zoom";
|
|
51304
51428
|
const onPointerDown = (event) => {
|
|
51305
51429
|
if (inputHandler.sendMouseEvent("down", event)) {
|
|
51306
51430
|
event.preventDefault();
|
|
51307
51431
|
canvas.setPointerCapture?.(event.pointerId);
|
|
51308
51432
|
return;
|
|
51309
51433
|
}
|
|
51434
|
+
if (isTouchPointer(event)) {
|
|
51435
|
+
if (event.button !== 0)
|
|
51436
|
+
return;
|
|
51437
|
+
const cell2 = normalizeSelectionCell(positionToCell(event));
|
|
51438
|
+
touchSelectionState.activePointerId = null;
|
|
51439
|
+
if (touchSelectionMode === "off")
|
|
51440
|
+
return;
|
|
51441
|
+
if (touchSelectionMode === "drag") {
|
|
51442
|
+
event.preventDefault();
|
|
51443
|
+
beginSelectionDrag(cell2, event.pointerId);
|
|
51444
|
+
return;
|
|
51445
|
+
}
|
|
51446
|
+
clearPendingTouchSelection();
|
|
51447
|
+
touchSelectionState.pendingPointerId = event.pointerId;
|
|
51448
|
+
touchSelectionState.pendingCell = cell2;
|
|
51449
|
+
touchSelectionState.pendingStartX = event.clientX;
|
|
51450
|
+
touchSelectionState.pendingStartY = event.clientY;
|
|
51451
|
+
touchSelectionState.pendingTimer = setTimeout(() => {
|
|
51452
|
+
if (touchSelectionState.pendingPointerId !== event.pointerId || !touchSelectionState.pendingCell) {
|
|
51453
|
+
return;
|
|
51454
|
+
}
|
|
51455
|
+
const pendingCell = touchSelectionState.pendingCell;
|
|
51456
|
+
clearPendingTouchSelection();
|
|
51457
|
+
beginSelectionDrag(pendingCell, event.pointerId);
|
|
51458
|
+
}, touchSelectionLongPressMs);
|
|
51459
|
+
return;
|
|
51460
|
+
}
|
|
51310
51461
|
if (event.button !== 0)
|
|
51311
51462
|
return;
|
|
51312
51463
|
event.preventDefault();
|
|
51313
51464
|
const cell = normalizeSelectionCell(positionToCell(event));
|
|
51314
51465
|
updateLinkHover(cell);
|
|
51315
|
-
|
|
51316
|
-
selectionState.dragging = true;
|
|
51317
|
-
selectionState.anchor = cell;
|
|
51318
|
-
selectionState.focus = cell;
|
|
51319
|
-
canvas.setPointerCapture?.(event.pointerId);
|
|
51320
|
-
updateCanvasCursor();
|
|
51321
|
-
needsRender = true;
|
|
51466
|
+
beginSelectionDrag(cell, event.pointerId);
|
|
51322
51467
|
};
|
|
51323
51468
|
const onPointerMove = (event) => {
|
|
51324
51469
|
if (inputHandler.sendMouseEvent("move", event)) {
|
|
51325
51470
|
event.preventDefault();
|
|
51326
51471
|
return;
|
|
51327
51472
|
}
|
|
51473
|
+
if (isTouchPointer(event)) {
|
|
51474
|
+
if (touchSelectionState.pendingPointerId === event.pointerId) {
|
|
51475
|
+
const dx = event.clientX - touchSelectionState.pendingStartX;
|
|
51476
|
+
const dy = event.clientY - touchSelectionState.pendingStartY;
|
|
51477
|
+
if (dx * dx + dy * dy >= touchSelectionMoveThresholdPx * touchSelectionMoveThresholdPx) {
|
|
51478
|
+
clearPendingTouchSelection();
|
|
51479
|
+
}
|
|
51480
|
+
return;
|
|
51481
|
+
}
|
|
51482
|
+
if (selectionState.dragging && touchSelectionState.activePointerId === event.pointerId) {
|
|
51483
|
+
const cell2 = normalizeSelectionCell(positionToCell(event));
|
|
51484
|
+
event.preventDefault();
|
|
51485
|
+
selectionState.focus = cell2;
|
|
51486
|
+
updateLinkHover(null);
|
|
51487
|
+
updateCanvasCursor();
|
|
51488
|
+
needsRender = true;
|
|
51489
|
+
}
|
|
51490
|
+
return;
|
|
51491
|
+
}
|
|
51328
51492
|
const cell = normalizeSelectionCell(positionToCell(event));
|
|
51329
51493
|
if (!selectionState.dragging) {
|
|
51330
51494
|
updateLinkHover(cell);
|
|
@@ -51341,6 +51505,27 @@ function createResttyApp(options) {
|
|
|
51341
51505
|
event.preventDefault();
|
|
51342
51506
|
return;
|
|
51343
51507
|
}
|
|
51508
|
+
if (isTouchPointer(event)) {
|
|
51509
|
+
if (touchSelectionState.pendingPointerId === event.pointerId) {
|
|
51510
|
+
clearPendingTouchSelection();
|
|
51511
|
+
touchSelectionState.activePointerId = null;
|
|
51512
|
+
return;
|
|
51513
|
+
}
|
|
51514
|
+
if (selectionState.dragging && touchSelectionState.activePointerId === event.pointerId) {
|
|
51515
|
+
const cell2 = normalizeSelectionCell(positionToCell(event));
|
|
51516
|
+
event.preventDefault();
|
|
51517
|
+
selectionState.dragging = false;
|
|
51518
|
+
selectionState.focus = cell2;
|
|
51519
|
+
touchSelectionState.activePointerId = null;
|
|
51520
|
+
if (selectionState.anchor && selectionState.focus && selectionState.anchor.row === selectionState.focus.row && selectionState.anchor.col === selectionState.focus.col) {
|
|
51521
|
+
clearSelection();
|
|
51522
|
+
} else {
|
|
51523
|
+
updateCanvasCursor();
|
|
51524
|
+
needsRender = true;
|
|
51525
|
+
}
|
|
51526
|
+
}
|
|
51527
|
+
return;
|
|
51528
|
+
}
|
|
51344
51529
|
const cell = normalizeSelectionCell(positionToCell(event));
|
|
51345
51530
|
if (selectionState.dragging) {
|
|
51346
51531
|
event.preventDefault();
|
|
@@ -51359,6 +51544,21 @@ function createResttyApp(options) {
|
|
|
51359
51544
|
openLink(linkState.hoverUri);
|
|
51360
51545
|
}
|
|
51361
51546
|
};
|
|
51547
|
+
const onPointerCancel = (event) => {
|
|
51548
|
+
if (isTouchPointer(event)) {
|
|
51549
|
+
if (touchSelectionState.pendingPointerId === event.pointerId) {
|
|
51550
|
+
clearPendingTouchSelection();
|
|
51551
|
+
}
|
|
51552
|
+
if (touchSelectionState.activePointerId === event.pointerId) {
|
|
51553
|
+
touchSelectionState.activePointerId = null;
|
|
51554
|
+
if (selectionState.dragging) {
|
|
51555
|
+
selectionState.dragging = false;
|
|
51556
|
+
updateCanvasCursor();
|
|
51557
|
+
needsRender = true;
|
|
51558
|
+
}
|
|
51559
|
+
}
|
|
51560
|
+
}
|
|
51561
|
+
};
|
|
51362
51562
|
const onWheel = (event) => {
|
|
51363
51563
|
const mouseActive = inputHandler.isMouseActive();
|
|
51364
51564
|
const altScreen = inputHandler.isAltScreen ? inputHandler.isAltScreen() : false;
|
|
@@ -51401,6 +51601,7 @@ function createResttyApp(options) {
|
|
|
51401
51601
|
canvas.addEventListener("pointerdown", onPointerDown);
|
|
51402
51602
|
canvas.addEventListener("pointermove", onPointerMove);
|
|
51403
51603
|
canvas.addEventListener("pointerup", onPointerUp);
|
|
51604
|
+
canvas.addEventListener("pointercancel", onPointerCancel);
|
|
51404
51605
|
canvas.addEventListener("pointerleave", onPointerLeave);
|
|
51405
51606
|
canvas.addEventListener("wheel", onWheel, { passive: false });
|
|
51406
51607
|
canvas.addEventListener("contextmenu", onContextMenu);
|
|
@@ -51408,9 +51609,11 @@ function createResttyApp(options) {
|
|
|
51408
51609
|
canvas.removeEventListener("pointerdown", onPointerDown);
|
|
51409
51610
|
canvas.removeEventListener("pointermove", onPointerMove);
|
|
51410
51611
|
canvas.removeEventListener("pointerup", onPointerUp);
|
|
51612
|
+
canvas.removeEventListener("pointercancel", onPointerCancel);
|
|
51411
51613
|
canvas.removeEventListener("pointerleave", onPointerLeave);
|
|
51412
51614
|
canvas.removeEventListener("wheel", onWheel);
|
|
51413
51615
|
canvas.removeEventListener("contextmenu", onContextMenu);
|
|
51616
|
+
clearPendingTouchSelection();
|
|
51414
51617
|
});
|
|
51415
51618
|
if (imeInput) {
|
|
51416
51619
|
let suppressNextInput = false;
|
|
@@ -55497,7 +55700,7 @@ function createResttyApp(options) {
|
|
|
55497
55700
|
return;
|
|
55498
55701
|
if (!text)
|
|
55499
55702
|
return;
|
|
55500
|
-
const normalized = normalizeNewlines(text);
|
|
55703
|
+
const normalized = source === "pty" ? text : normalizeNewlines(text);
|
|
55501
55704
|
if (source === "key") {
|
|
55502
55705
|
const bytes = textEncoder3.encode(normalized);
|
|
55503
55706
|
const hex = Array.from(bytes, (b3) => b3.toString(16).padStart(2, "0")).join(" ");
|
|
@@ -55519,8 +55722,13 @@ function createResttyApp(options) {
|
|
|
55519
55722
|
clearSelection();
|
|
55520
55723
|
}
|
|
55521
55724
|
writeToWasm(wasmHandle, normalized);
|
|
55522
|
-
wasm.renderUpdate(wasmHandle);
|
|
55523
55725
|
flushWasmOutputToPty();
|
|
55726
|
+
if (source === "pty" && inputHandler?.isSynchronizedOutput?.()) {
|
|
55727
|
+
scheduleSyncOutputReset();
|
|
55728
|
+
return;
|
|
55729
|
+
}
|
|
55730
|
+
cancelSyncOutputReset();
|
|
55731
|
+
wasm.renderUpdate(wasmHandle);
|
|
55524
55732
|
if (source === "key" && wasmExports?.restty_debug_cursor_x && wasmExports?.restty_debug_cursor_y) {
|
|
55525
55733
|
const ax = wasmExports.restty_debug_cursor_x(wasmHandle);
|
|
55526
55734
|
const ay = wasmExports.restty_debug_cursor_y(wasmHandle);
|
|
@@ -55749,6 +55957,7 @@ function createResttyApp(options) {
|
|
|
55749
55957
|
clearTimeout(terminalResizeTimer);
|
|
55750
55958
|
terminalResizeTimer = 0;
|
|
55751
55959
|
}
|
|
55960
|
+
cancelSyncOutputReset();
|
|
55752
55961
|
pendingTerminalResize = null;
|
|
55753
55962
|
disconnectPty2();
|
|
55754
55963
|
ptyTransport.destroy?.();
|
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
|
};
|
package/dist/index.js
CHANGED
|
@@ -8300,6 +8300,7 @@ class OutputFilter {
|
|
|
8300
8300
|
altScreen = false;
|
|
8301
8301
|
bracketedPaste = false;
|
|
8302
8302
|
focusReporting = false;
|
|
8303
|
+
synchronizedOutput = false;
|
|
8303
8304
|
windowOpHandler;
|
|
8304
8305
|
getWindowMetrics;
|
|
8305
8306
|
clipboardWrite;
|
|
@@ -8333,6 +8334,9 @@ class OutputFilter {
|
|
|
8333
8334
|
isFocusReporting() {
|
|
8334
8335
|
return this.focusReporting;
|
|
8335
8336
|
}
|
|
8337
|
+
isSynchronizedOutput() {
|
|
8338
|
+
return this.synchronizedOutput;
|
|
8339
|
+
}
|
|
8336
8340
|
replyOscColor(code, rgb) {
|
|
8337
8341
|
const toHex4 = (value) => Math.round(Math.max(0, Math.min(255, value)) * 257).toString(16).padStart(4, "0");
|
|
8338
8342
|
const r = toHex4(rgb[0]);
|
|
@@ -8398,6 +8402,8 @@ class OutputFilter {
|
|
|
8398
8402
|
} else if (code === 1004) {
|
|
8399
8403
|
this.focusReporting = enabled;
|
|
8400
8404
|
handled = true;
|
|
8405
|
+
} else if (code === 2026) {
|
|
8406
|
+
this.synchronizedOutput = enabled;
|
|
8401
8407
|
}
|
|
8402
8408
|
}
|
|
8403
8409
|
return handled;
|
|
@@ -8581,6 +8587,7 @@ function createInputHandler(options = {}) {
|
|
|
8581
8587
|
isBracketedPaste: () => filter.isBracketedPaste(),
|
|
8582
8588
|
isFocusReporting: () => filter.isFocusReporting(),
|
|
8583
8589
|
isAltScreen: () => filter.isAltScreen(),
|
|
8590
|
+
isSynchronizedOutput: () => filter.isSynchronizedOutput(),
|
|
8584
8591
|
sendMouseEvent: (kind, event) => mouse.sendMouseEvent(kind, event)
|
|
8585
8592
|
};
|
|
8586
8593
|
}
|
|
@@ -50316,6 +50323,17 @@ function createRestty(options) {
|
|
|
50316
50323
|
}
|
|
50317
50324
|
|
|
50318
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
|
+
}
|
|
50319
50337
|
function createResttyApp(options) {
|
|
50320
50338
|
const { canvas: canvasInput, imeInput: imeInputInput, elements, callbacks } = options;
|
|
50321
50339
|
const session = options.session ?? getDefaultResttyAppSession();
|
|
@@ -50338,6 +50356,9 @@ function createResttyApp(options) {
|
|
|
50338
50356
|
const attachCanvasEvents = options.attachCanvasEvents ?? true;
|
|
50339
50357
|
const autoResize = options.autoResize ?? true;
|
|
50340
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);
|
|
50341
50362
|
const nerdIconScale = Number.isFinite(options.nerdIconScale) ? Number(options.nerdIconScale) : 1;
|
|
50342
50363
|
const alphaBlending = options.alphaBlending ?? "linear-corrected";
|
|
50343
50364
|
const srgbChannelToLinear = (c3) => c3 <= 0.04045 ? c3 / 12.92 : Math.pow((c3 + 0.055) / 1.055, 2.4);
|
|
@@ -50443,6 +50464,14 @@ function createResttyApp(options) {
|
|
|
50443
50464
|
let lastKeydownSeqAt = 0;
|
|
50444
50465
|
let nextBlinkTime = performance.now() + CURSOR_BLINK_MS;
|
|
50445
50466
|
const ptyTransport = options.ptyTransport ?? createWebSocketPtyTransport();
|
|
50467
|
+
const PTY_OUTPUT_IDLE_MS = 10;
|
|
50468
|
+
const PTY_OUTPUT_MAX_MS = 40;
|
|
50469
|
+
const SYNC_OUTPUT_RESET_MS = 1000;
|
|
50470
|
+
const SYNC_OUTPUT_RESET_SEQ = "\x1B[?2026l";
|
|
50471
|
+
let ptyOutputBuffer = "";
|
|
50472
|
+
let ptyOutputIdleTimer = 0;
|
|
50473
|
+
let ptyOutputMaxTimer = 0;
|
|
50474
|
+
let syncOutputResetTimer = 0;
|
|
50446
50475
|
let lastCursorForCpr = { row: 1, col: 1 };
|
|
50447
50476
|
let inputHandler = null;
|
|
50448
50477
|
let activeTheme = null;
|
|
@@ -50531,6 +50560,14 @@ function createResttyApp(options) {
|
|
|
50531
50560
|
anchor: null,
|
|
50532
50561
|
focus: null
|
|
50533
50562
|
};
|
|
50563
|
+
const touchSelectionState = {
|
|
50564
|
+
pendingPointerId: null,
|
|
50565
|
+
activePointerId: null,
|
|
50566
|
+
pendingCell: null,
|
|
50567
|
+
pendingStartX: 0,
|
|
50568
|
+
pendingStartY: 0,
|
|
50569
|
+
pendingTimer: 0
|
|
50570
|
+
};
|
|
50534
50571
|
const linkState = {
|
|
50535
50572
|
hoverId: 0,
|
|
50536
50573
|
hoverUri: ""
|
|
@@ -50552,6 +50589,27 @@ function createResttyApp(options) {
|
|
|
50552
50589
|
}
|
|
50553
50590
|
canvas.style.cursor = linkState.hoverId ? "pointer" : "default";
|
|
50554
50591
|
}
|
|
50592
|
+
function isTouchPointer(event) {
|
|
50593
|
+
return event.pointerType === "touch";
|
|
50594
|
+
}
|
|
50595
|
+
function clearPendingTouchSelection() {
|
|
50596
|
+
if (touchSelectionState.pendingTimer) {
|
|
50597
|
+
clearTimeout(touchSelectionState.pendingTimer);
|
|
50598
|
+
touchSelectionState.pendingTimer = 0;
|
|
50599
|
+
}
|
|
50600
|
+
touchSelectionState.pendingPointerId = null;
|
|
50601
|
+
touchSelectionState.pendingCell = null;
|
|
50602
|
+
}
|
|
50603
|
+
function beginSelectionDrag(cell, pointerId) {
|
|
50604
|
+
selectionState.active = true;
|
|
50605
|
+
selectionState.dragging = true;
|
|
50606
|
+
selectionState.anchor = cell;
|
|
50607
|
+
selectionState.focus = cell;
|
|
50608
|
+
touchSelectionState.activePointerId = pointerId;
|
|
50609
|
+
canvas.setPointerCapture?.(pointerId);
|
|
50610
|
+
updateCanvasCursor();
|
|
50611
|
+
needsRender = true;
|
|
50612
|
+
}
|
|
50555
50613
|
function noteScrollActivity() {
|
|
50556
50614
|
scrollbarState.lastInputAt = performance.now();
|
|
50557
50615
|
}
|
|
@@ -51152,6 +51210,7 @@ function createResttyApp(options) {
|
|
|
51152
51210
|
selectionState.dragging = false;
|
|
51153
51211
|
selectionState.anchor = null;
|
|
51154
51212
|
selectionState.focus = null;
|
|
51213
|
+
touchSelectionState.activePointerId = null;
|
|
51155
51214
|
updateCanvasCursor();
|
|
51156
51215
|
needsRender = true;
|
|
51157
51216
|
}
|
|
@@ -51202,7 +51261,71 @@ function createResttyApp(options) {
|
|
|
51202
51261
|
const label = status.active ? `${status.mode} (${status.detail})` : status.mode;
|
|
51203
51262
|
setMouseStatus(label);
|
|
51204
51263
|
}
|
|
51264
|
+
function cancelPtyOutputFlush() {
|
|
51265
|
+
if (ptyOutputIdleTimer) {
|
|
51266
|
+
clearTimeout(ptyOutputIdleTimer);
|
|
51267
|
+
ptyOutputIdleTimer = 0;
|
|
51268
|
+
}
|
|
51269
|
+
if (ptyOutputMaxTimer) {
|
|
51270
|
+
clearTimeout(ptyOutputMaxTimer);
|
|
51271
|
+
ptyOutputMaxTimer = 0;
|
|
51272
|
+
}
|
|
51273
|
+
}
|
|
51274
|
+
function cancelSyncOutputReset() {
|
|
51275
|
+
if (syncOutputResetTimer) {
|
|
51276
|
+
clearTimeout(syncOutputResetTimer);
|
|
51277
|
+
syncOutputResetTimer = 0;
|
|
51278
|
+
}
|
|
51279
|
+
}
|
|
51280
|
+
function scheduleSyncOutputReset() {
|
|
51281
|
+
if (syncOutputResetTimer)
|
|
51282
|
+
return;
|
|
51283
|
+
syncOutputResetTimer = setTimeout(() => {
|
|
51284
|
+
syncOutputResetTimer = 0;
|
|
51285
|
+
if (!inputHandler?.isSynchronizedOutput?.())
|
|
51286
|
+
return;
|
|
51287
|
+
const sanitized = inputHandler.filterOutput(SYNC_OUTPUT_RESET_SEQ) || SYNC_OUTPUT_RESET_SEQ;
|
|
51288
|
+
sendInput(sanitized, "pty");
|
|
51289
|
+
}, SYNC_OUTPUT_RESET_MS);
|
|
51290
|
+
}
|
|
51291
|
+
function flushPtyOutputBuffer() {
|
|
51292
|
+
const output = ptyOutputBuffer;
|
|
51293
|
+
ptyOutputBuffer = "";
|
|
51294
|
+
if (!output)
|
|
51295
|
+
return;
|
|
51296
|
+
sendInput(output, "pty");
|
|
51297
|
+
}
|
|
51298
|
+
function queuePtyOutput(text) {
|
|
51299
|
+
if (!text)
|
|
51300
|
+
return;
|
|
51301
|
+
ptyOutputBuffer += text;
|
|
51302
|
+
if (ptyOutputIdleTimer) {
|
|
51303
|
+
clearTimeout(ptyOutputIdleTimer);
|
|
51304
|
+
}
|
|
51305
|
+
ptyOutputIdleTimer = setTimeout(() => {
|
|
51306
|
+
ptyOutputIdleTimer = 0;
|
|
51307
|
+
if (ptyOutputMaxTimer) {
|
|
51308
|
+
clearTimeout(ptyOutputMaxTimer);
|
|
51309
|
+
ptyOutputMaxTimer = 0;
|
|
51310
|
+
}
|
|
51311
|
+
flushPtyOutputBuffer();
|
|
51312
|
+
}, PTY_OUTPUT_IDLE_MS);
|
|
51313
|
+
if (!ptyOutputMaxTimer) {
|
|
51314
|
+
ptyOutputMaxTimer = setTimeout(() => {
|
|
51315
|
+
ptyOutputMaxTimer = 0;
|
|
51316
|
+
if (ptyOutputIdleTimer) {
|
|
51317
|
+
clearTimeout(ptyOutputIdleTimer);
|
|
51318
|
+
ptyOutputIdleTimer = 0;
|
|
51319
|
+
}
|
|
51320
|
+
flushPtyOutputBuffer();
|
|
51321
|
+
}, PTY_OUTPUT_MAX_MS);
|
|
51322
|
+
}
|
|
51323
|
+
}
|
|
51205
51324
|
function disconnectPty2() {
|
|
51325
|
+
flushPtyOutputBuffer();
|
|
51326
|
+
cancelPtyOutputFlush();
|
|
51327
|
+
cancelSyncOutputReset();
|
|
51328
|
+
ptyOutputBuffer = "";
|
|
51206
51329
|
ptyTransport.disconnect();
|
|
51207
51330
|
updateMouseStatus();
|
|
51208
51331
|
setPtyStatus("disconnected");
|
|
@@ -51249,7 +51372,7 @@ function createResttyApp(options) {
|
|
|
51249
51372
|
const sanitized = inputHandler ? inputHandler.filterOutput(text) : text;
|
|
51250
51373
|
updateMouseStatus();
|
|
51251
51374
|
if (sanitized)
|
|
51252
|
-
|
|
51375
|
+
queuePtyOutput(sanitized);
|
|
51253
51376
|
}
|
|
51254
51377
|
}
|
|
51255
51378
|
});
|
|
@@ -51301,30 +51424,71 @@ function createResttyApp(options) {
|
|
|
51301
51424
|
function bindCanvasEvents() {
|
|
51302
51425
|
if (!attachCanvasEvents)
|
|
51303
51426
|
return;
|
|
51427
|
+
canvas.style.touchAction = touchSelectionMode === "drag" ? "none" : "pan-y pinch-zoom";
|
|
51304
51428
|
const onPointerDown = (event) => {
|
|
51305
51429
|
if (inputHandler.sendMouseEvent("down", event)) {
|
|
51306
51430
|
event.preventDefault();
|
|
51307
51431
|
canvas.setPointerCapture?.(event.pointerId);
|
|
51308
51432
|
return;
|
|
51309
51433
|
}
|
|
51434
|
+
if (isTouchPointer(event)) {
|
|
51435
|
+
if (event.button !== 0)
|
|
51436
|
+
return;
|
|
51437
|
+
const cell2 = normalizeSelectionCell(positionToCell(event));
|
|
51438
|
+
touchSelectionState.activePointerId = null;
|
|
51439
|
+
if (touchSelectionMode === "off")
|
|
51440
|
+
return;
|
|
51441
|
+
if (touchSelectionMode === "drag") {
|
|
51442
|
+
event.preventDefault();
|
|
51443
|
+
beginSelectionDrag(cell2, event.pointerId);
|
|
51444
|
+
return;
|
|
51445
|
+
}
|
|
51446
|
+
clearPendingTouchSelection();
|
|
51447
|
+
touchSelectionState.pendingPointerId = event.pointerId;
|
|
51448
|
+
touchSelectionState.pendingCell = cell2;
|
|
51449
|
+
touchSelectionState.pendingStartX = event.clientX;
|
|
51450
|
+
touchSelectionState.pendingStartY = event.clientY;
|
|
51451
|
+
touchSelectionState.pendingTimer = setTimeout(() => {
|
|
51452
|
+
if (touchSelectionState.pendingPointerId !== event.pointerId || !touchSelectionState.pendingCell) {
|
|
51453
|
+
return;
|
|
51454
|
+
}
|
|
51455
|
+
const pendingCell = touchSelectionState.pendingCell;
|
|
51456
|
+
clearPendingTouchSelection();
|
|
51457
|
+
beginSelectionDrag(pendingCell, event.pointerId);
|
|
51458
|
+
}, touchSelectionLongPressMs);
|
|
51459
|
+
return;
|
|
51460
|
+
}
|
|
51310
51461
|
if (event.button !== 0)
|
|
51311
51462
|
return;
|
|
51312
51463
|
event.preventDefault();
|
|
51313
51464
|
const cell = normalizeSelectionCell(positionToCell(event));
|
|
51314
51465
|
updateLinkHover(cell);
|
|
51315
|
-
|
|
51316
|
-
selectionState.dragging = true;
|
|
51317
|
-
selectionState.anchor = cell;
|
|
51318
|
-
selectionState.focus = cell;
|
|
51319
|
-
canvas.setPointerCapture?.(event.pointerId);
|
|
51320
|
-
updateCanvasCursor();
|
|
51321
|
-
needsRender = true;
|
|
51466
|
+
beginSelectionDrag(cell, event.pointerId);
|
|
51322
51467
|
};
|
|
51323
51468
|
const onPointerMove = (event) => {
|
|
51324
51469
|
if (inputHandler.sendMouseEvent("move", event)) {
|
|
51325
51470
|
event.preventDefault();
|
|
51326
51471
|
return;
|
|
51327
51472
|
}
|
|
51473
|
+
if (isTouchPointer(event)) {
|
|
51474
|
+
if (touchSelectionState.pendingPointerId === event.pointerId) {
|
|
51475
|
+
const dx = event.clientX - touchSelectionState.pendingStartX;
|
|
51476
|
+
const dy = event.clientY - touchSelectionState.pendingStartY;
|
|
51477
|
+
if (dx * dx + dy * dy >= touchSelectionMoveThresholdPx * touchSelectionMoveThresholdPx) {
|
|
51478
|
+
clearPendingTouchSelection();
|
|
51479
|
+
}
|
|
51480
|
+
return;
|
|
51481
|
+
}
|
|
51482
|
+
if (selectionState.dragging && touchSelectionState.activePointerId === event.pointerId) {
|
|
51483
|
+
const cell2 = normalizeSelectionCell(positionToCell(event));
|
|
51484
|
+
event.preventDefault();
|
|
51485
|
+
selectionState.focus = cell2;
|
|
51486
|
+
updateLinkHover(null);
|
|
51487
|
+
updateCanvasCursor();
|
|
51488
|
+
needsRender = true;
|
|
51489
|
+
}
|
|
51490
|
+
return;
|
|
51491
|
+
}
|
|
51328
51492
|
const cell = normalizeSelectionCell(positionToCell(event));
|
|
51329
51493
|
if (!selectionState.dragging) {
|
|
51330
51494
|
updateLinkHover(cell);
|
|
@@ -51341,6 +51505,27 @@ function createResttyApp(options) {
|
|
|
51341
51505
|
event.preventDefault();
|
|
51342
51506
|
return;
|
|
51343
51507
|
}
|
|
51508
|
+
if (isTouchPointer(event)) {
|
|
51509
|
+
if (touchSelectionState.pendingPointerId === event.pointerId) {
|
|
51510
|
+
clearPendingTouchSelection();
|
|
51511
|
+
touchSelectionState.activePointerId = null;
|
|
51512
|
+
return;
|
|
51513
|
+
}
|
|
51514
|
+
if (selectionState.dragging && touchSelectionState.activePointerId === event.pointerId) {
|
|
51515
|
+
const cell2 = normalizeSelectionCell(positionToCell(event));
|
|
51516
|
+
event.preventDefault();
|
|
51517
|
+
selectionState.dragging = false;
|
|
51518
|
+
selectionState.focus = cell2;
|
|
51519
|
+
touchSelectionState.activePointerId = null;
|
|
51520
|
+
if (selectionState.anchor && selectionState.focus && selectionState.anchor.row === selectionState.focus.row && selectionState.anchor.col === selectionState.focus.col) {
|
|
51521
|
+
clearSelection();
|
|
51522
|
+
} else {
|
|
51523
|
+
updateCanvasCursor();
|
|
51524
|
+
needsRender = true;
|
|
51525
|
+
}
|
|
51526
|
+
}
|
|
51527
|
+
return;
|
|
51528
|
+
}
|
|
51344
51529
|
const cell = normalizeSelectionCell(positionToCell(event));
|
|
51345
51530
|
if (selectionState.dragging) {
|
|
51346
51531
|
event.preventDefault();
|
|
@@ -51359,6 +51544,21 @@ function createResttyApp(options) {
|
|
|
51359
51544
|
openLink(linkState.hoverUri);
|
|
51360
51545
|
}
|
|
51361
51546
|
};
|
|
51547
|
+
const onPointerCancel = (event) => {
|
|
51548
|
+
if (isTouchPointer(event)) {
|
|
51549
|
+
if (touchSelectionState.pendingPointerId === event.pointerId) {
|
|
51550
|
+
clearPendingTouchSelection();
|
|
51551
|
+
}
|
|
51552
|
+
if (touchSelectionState.activePointerId === event.pointerId) {
|
|
51553
|
+
touchSelectionState.activePointerId = null;
|
|
51554
|
+
if (selectionState.dragging) {
|
|
51555
|
+
selectionState.dragging = false;
|
|
51556
|
+
updateCanvasCursor();
|
|
51557
|
+
needsRender = true;
|
|
51558
|
+
}
|
|
51559
|
+
}
|
|
51560
|
+
}
|
|
51561
|
+
};
|
|
51362
51562
|
const onWheel = (event) => {
|
|
51363
51563
|
const mouseActive = inputHandler.isMouseActive();
|
|
51364
51564
|
const altScreen = inputHandler.isAltScreen ? inputHandler.isAltScreen() : false;
|
|
@@ -51401,6 +51601,7 @@ function createResttyApp(options) {
|
|
|
51401
51601
|
canvas.addEventListener("pointerdown", onPointerDown);
|
|
51402
51602
|
canvas.addEventListener("pointermove", onPointerMove);
|
|
51403
51603
|
canvas.addEventListener("pointerup", onPointerUp);
|
|
51604
|
+
canvas.addEventListener("pointercancel", onPointerCancel);
|
|
51404
51605
|
canvas.addEventListener("pointerleave", onPointerLeave);
|
|
51405
51606
|
canvas.addEventListener("wheel", onWheel, { passive: false });
|
|
51406
51607
|
canvas.addEventListener("contextmenu", onContextMenu);
|
|
@@ -51408,9 +51609,11 @@ function createResttyApp(options) {
|
|
|
51408
51609
|
canvas.removeEventListener("pointerdown", onPointerDown);
|
|
51409
51610
|
canvas.removeEventListener("pointermove", onPointerMove);
|
|
51410
51611
|
canvas.removeEventListener("pointerup", onPointerUp);
|
|
51612
|
+
canvas.removeEventListener("pointercancel", onPointerCancel);
|
|
51411
51613
|
canvas.removeEventListener("pointerleave", onPointerLeave);
|
|
51412
51614
|
canvas.removeEventListener("wheel", onWheel);
|
|
51413
51615
|
canvas.removeEventListener("contextmenu", onContextMenu);
|
|
51616
|
+
clearPendingTouchSelection();
|
|
51414
51617
|
});
|
|
51415
51618
|
if (imeInput) {
|
|
51416
51619
|
let suppressNextInput = false;
|
|
@@ -55497,7 +55700,7 @@ function createResttyApp(options) {
|
|
|
55497
55700
|
return;
|
|
55498
55701
|
if (!text)
|
|
55499
55702
|
return;
|
|
55500
|
-
const normalized = normalizeNewlines(text);
|
|
55703
|
+
const normalized = source === "pty" ? text : normalizeNewlines(text);
|
|
55501
55704
|
if (source === "key") {
|
|
55502
55705
|
const bytes = textEncoder3.encode(normalized);
|
|
55503
55706
|
const hex = Array.from(bytes, (b3) => b3.toString(16).padStart(2, "0")).join(" ");
|
|
@@ -55519,8 +55722,13 @@ function createResttyApp(options) {
|
|
|
55519
55722
|
clearSelection();
|
|
55520
55723
|
}
|
|
55521
55724
|
writeToWasm(wasmHandle, normalized);
|
|
55522
|
-
wasm.renderUpdate(wasmHandle);
|
|
55523
55725
|
flushWasmOutputToPty();
|
|
55726
|
+
if (source === "pty" && inputHandler?.isSynchronizedOutput?.()) {
|
|
55727
|
+
scheduleSyncOutputReset();
|
|
55728
|
+
return;
|
|
55729
|
+
}
|
|
55730
|
+
cancelSyncOutputReset();
|
|
55731
|
+
wasm.renderUpdate(wasmHandle);
|
|
55524
55732
|
if (source === "key" && wasmExports?.restty_debug_cursor_x && wasmExports?.restty_debug_cursor_y) {
|
|
55525
55733
|
const ax = wasmExports.restty_debug_cursor_x(wasmHandle);
|
|
55526
55734
|
const ay = wasmExports.restty_debug_cursor_y(wasmHandle);
|
|
@@ -55749,6 +55957,7 @@ function createResttyApp(options) {
|
|
|
55749
55957
|
clearTimeout(terminalResizeTimer);
|
|
55750
55958
|
terminalResizeTimer = 0;
|
|
55751
55959
|
}
|
|
55960
|
+
cancelSyncOutputReset();
|
|
55752
55961
|
pendingTerminalResize = null;
|
|
55753
55962
|
disconnectPty2();
|
|
55754
55963
|
ptyTransport.destroy?.();
|
package/dist/input/index.js
CHANGED
|
@@ -751,6 +751,7 @@ class OutputFilter {
|
|
|
751
751
|
altScreen = false;
|
|
752
752
|
bracketedPaste = false;
|
|
753
753
|
focusReporting = false;
|
|
754
|
+
synchronizedOutput = false;
|
|
754
755
|
windowOpHandler;
|
|
755
756
|
getWindowMetrics;
|
|
756
757
|
clipboardWrite;
|
|
@@ -784,6 +785,9 @@ class OutputFilter {
|
|
|
784
785
|
isFocusReporting() {
|
|
785
786
|
return this.focusReporting;
|
|
786
787
|
}
|
|
788
|
+
isSynchronizedOutput() {
|
|
789
|
+
return this.synchronizedOutput;
|
|
790
|
+
}
|
|
787
791
|
replyOscColor(code, rgb) {
|
|
788
792
|
const toHex4 = (value) => Math.round(Math.max(0, Math.min(255, value)) * 257).toString(16).padStart(4, "0");
|
|
789
793
|
const r = toHex4(rgb[0]);
|
|
@@ -849,6 +853,8 @@ class OutputFilter {
|
|
|
849
853
|
} else if (code === 1004) {
|
|
850
854
|
this.focusReporting = enabled;
|
|
851
855
|
handled = true;
|
|
856
|
+
} else if (code === 2026) {
|
|
857
|
+
this.synchronizedOutput = enabled;
|
|
852
858
|
}
|
|
853
859
|
}
|
|
854
860
|
return handled;
|
|
@@ -1032,6 +1038,7 @@ function createInputHandler(options = {}) {
|
|
|
1032
1038
|
isBracketedPaste: () => filter.isBracketedPaste(),
|
|
1033
1039
|
isFocusReporting: () => filter.isFocusReporting(),
|
|
1034
1040
|
isAltScreen: () => filter.isAltScreen(),
|
|
1041
|
+
isSynchronizedOutput: () => filter.isSynchronizedOutput(),
|
|
1035
1042
|
sendMouseEvent: (kind, event) => mouse.sendMouseEvent(kind, event)
|
|
1036
1043
|
};
|
|
1037
1044
|
}
|
package/dist/input/output.d.ts
CHANGED
|
@@ -33,6 +33,7 @@ export declare class OutputFilter {
|
|
|
33
33
|
private altScreen;
|
|
34
34
|
private bracketedPaste;
|
|
35
35
|
private focusReporting;
|
|
36
|
+
private synchronizedOutput;
|
|
36
37
|
private windowOpHandler?;
|
|
37
38
|
private getWindowMetrics?;
|
|
38
39
|
private clipboardWrite?;
|
|
@@ -45,6 +46,7 @@ export declare class OutputFilter {
|
|
|
45
46
|
isAltScreen(): boolean;
|
|
46
47
|
isBracketedPaste(): boolean;
|
|
47
48
|
isFocusReporting(): boolean;
|
|
49
|
+
isSynchronizedOutput(): boolean;
|
|
48
50
|
private replyOscColor;
|
|
49
51
|
private handleOsc;
|
|
50
52
|
private handleModeSeq;
|
package/dist/input/types.d.ts
CHANGED
package/dist/internal.js
CHANGED
|
@@ -8300,6 +8300,7 @@ class OutputFilter {
|
|
|
8300
8300
|
altScreen = false;
|
|
8301
8301
|
bracketedPaste = false;
|
|
8302
8302
|
focusReporting = false;
|
|
8303
|
+
synchronizedOutput = false;
|
|
8303
8304
|
windowOpHandler;
|
|
8304
8305
|
getWindowMetrics;
|
|
8305
8306
|
clipboardWrite;
|
|
@@ -8333,6 +8334,9 @@ class OutputFilter {
|
|
|
8333
8334
|
isFocusReporting() {
|
|
8334
8335
|
return this.focusReporting;
|
|
8335
8336
|
}
|
|
8337
|
+
isSynchronizedOutput() {
|
|
8338
|
+
return this.synchronizedOutput;
|
|
8339
|
+
}
|
|
8336
8340
|
replyOscColor(code, rgb) {
|
|
8337
8341
|
const toHex4 = (value) => Math.round(Math.max(0, Math.min(255, value)) * 257).toString(16).padStart(4, "0");
|
|
8338
8342
|
const r = toHex4(rgb[0]);
|
|
@@ -8398,6 +8402,8 @@ class OutputFilter {
|
|
|
8398
8402
|
} else if (code === 1004) {
|
|
8399
8403
|
this.focusReporting = enabled;
|
|
8400
8404
|
handled = true;
|
|
8405
|
+
} else if (code === 2026) {
|
|
8406
|
+
this.synchronizedOutput = enabled;
|
|
8401
8407
|
}
|
|
8402
8408
|
}
|
|
8403
8409
|
return handled;
|
|
@@ -8581,6 +8587,7 @@ function createInputHandler(options = {}) {
|
|
|
8581
8587
|
isBracketedPaste: () => filter.isBracketedPaste(),
|
|
8582
8588
|
isFocusReporting: () => filter.isFocusReporting(),
|
|
8583
8589
|
isAltScreen: () => filter.isAltScreen(),
|
|
8590
|
+
isSynchronizedOutput: () => filter.isSynchronizedOutput(),
|
|
8584
8591
|
sendMouseEvent: (kind, event) => mouse.sendMouseEvent(kind, event)
|
|
8585
8592
|
};
|
|
8586
8593
|
}
|
|
@@ -50316,6 +50323,17 @@ function createRestty(options) {
|
|
|
50316
50323
|
}
|
|
50317
50324
|
|
|
50318
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
|
+
}
|
|
50319
50337
|
function createResttyApp(options) {
|
|
50320
50338
|
const { canvas: canvasInput, imeInput: imeInputInput, elements, callbacks } = options;
|
|
50321
50339
|
const session = options.session ?? getDefaultResttyAppSession();
|
|
@@ -50338,6 +50356,9 @@ function createResttyApp(options) {
|
|
|
50338
50356
|
const attachCanvasEvents = options.attachCanvasEvents ?? true;
|
|
50339
50357
|
const autoResize = options.autoResize ?? true;
|
|
50340
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);
|
|
50341
50362
|
const nerdIconScale = Number.isFinite(options.nerdIconScale) ? Number(options.nerdIconScale) : 1;
|
|
50342
50363
|
const alphaBlending = options.alphaBlending ?? "linear-corrected";
|
|
50343
50364
|
const srgbChannelToLinear = (c3) => c3 <= 0.04045 ? c3 / 12.92 : Math.pow((c3 + 0.055) / 1.055, 2.4);
|
|
@@ -50443,6 +50464,14 @@ function createResttyApp(options) {
|
|
|
50443
50464
|
let lastKeydownSeqAt = 0;
|
|
50444
50465
|
let nextBlinkTime = performance.now() + CURSOR_BLINK_MS;
|
|
50445
50466
|
const ptyTransport = options.ptyTransport ?? createWebSocketPtyTransport();
|
|
50467
|
+
const PTY_OUTPUT_IDLE_MS = 10;
|
|
50468
|
+
const PTY_OUTPUT_MAX_MS = 40;
|
|
50469
|
+
const SYNC_OUTPUT_RESET_MS = 1000;
|
|
50470
|
+
const SYNC_OUTPUT_RESET_SEQ = "\x1B[?2026l";
|
|
50471
|
+
let ptyOutputBuffer = "";
|
|
50472
|
+
let ptyOutputIdleTimer = 0;
|
|
50473
|
+
let ptyOutputMaxTimer = 0;
|
|
50474
|
+
let syncOutputResetTimer = 0;
|
|
50446
50475
|
let lastCursorForCpr = { row: 1, col: 1 };
|
|
50447
50476
|
let inputHandler = null;
|
|
50448
50477
|
let activeTheme = null;
|
|
@@ -50531,6 +50560,14 @@ function createResttyApp(options) {
|
|
|
50531
50560
|
anchor: null,
|
|
50532
50561
|
focus: null
|
|
50533
50562
|
};
|
|
50563
|
+
const touchSelectionState = {
|
|
50564
|
+
pendingPointerId: null,
|
|
50565
|
+
activePointerId: null,
|
|
50566
|
+
pendingCell: null,
|
|
50567
|
+
pendingStartX: 0,
|
|
50568
|
+
pendingStartY: 0,
|
|
50569
|
+
pendingTimer: 0
|
|
50570
|
+
};
|
|
50534
50571
|
const linkState = {
|
|
50535
50572
|
hoverId: 0,
|
|
50536
50573
|
hoverUri: ""
|
|
@@ -50552,6 +50589,27 @@ function createResttyApp(options) {
|
|
|
50552
50589
|
}
|
|
50553
50590
|
canvas.style.cursor = linkState.hoverId ? "pointer" : "default";
|
|
50554
50591
|
}
|
|
50592
|
+
function isTouchPointer(event) {
|
|
50593
|
+
return event.pointerType === "touch";
|
|
50594
|
+
}
|
|
50595
|
+
function clearPendingTouchSelection() {
|
|
50596
|
+
if (touchSelectionState.pendingTimer) {
|
|
50597
|
+
clearTimeout(touchSelectionState.pendingTimer);
|
|
50598
|
+
touchSelectionState.pendingTimer = 0;
|
|
50599
|
+
}
|
|
50600
|
+
touchSelectionState.pendingPointerId = null;
|
|
50601
|
+
touchSelectionState.pendingCell = null;
|
|
50602
|
+
}
|
|
50603
|
+
function beginSelectionDrag(cell, pointerId) {
|
|
50604
|
+
selectionState.active = true;
|
|
50605
|
+
selectionState.dragging = true;
|
|
50606
|
+
selectionState.anchor = cell;
|
|
50607
|
+
selectionState.focus = cell;
|
|
50608
|
+
touchSelectionState.activePointerId = pointerId;
|
|
50609
|
+
canvas.setPointerCapture?.(pointerId);
|
|
50610
|
+
updateCanvasCursor();
|
|
50611
|
+
needsRender = true;
|
|
50612
|
+
}
|
|
50555
50613
|
function noteScrollActivity() {
|
|
50556
50614
|
scrollbarState.lastInputAt = performance.now();
|
|
50557
50615
|
}
|
|
@@ -51152,6 +51210,7 @@ function createResttyApp(options) {
|
|
|
51152
51210
|
selectionState.dragging = false;
|
|
51153
51211
|
selectionState.anchor = null;
|
|
51154
51212
|
selectionState.focus = null;
|
|
51213
|
+
touchSelectionState.activePointerId = null;
|
|
51155
51214
|
updateCanvasCursor();
|
|
51156
51215
|
needsRender = true;
|
|
51157
51216
|
}
|
|
@@ -51202,7 +51261,71 @@ function createResttyApp(options) {
|
|
|
51202
51261
|
const label = status.active ? `${status.mode} (${status.detail})` : status.mode;
|
|
51203
51262
|
setMouseStatus(label);
|
|
51204
51263
|
}
|
|
51264
|
+
function cancelPtyOutputFlush() {
|
|
51265
|
+
if (ptyOutputIdleTimer) {
|
|
51266
|
+
clearTimeout(ptyOutputIdleTimer);
|
|
51267
|
+
ptyOutputIdleTimer = 0;
|
|
51268
|
+
}
|
|
51269
|
+
if (ptyOutputMaxTimer) {
|
|
51270
|
+
clearTimeout(ptyOutputMaxTimer);
|
|
51271
|
+
ptyOutputMaxTimer = 0;
|
|
51272
|
+
}
|
|
51273
|
+
}
|
|
51274
|
+
function cancelSyncOutputReset() {
|
|
51275
|
+
if (syncOutputResetTimer) {
|
|
51276
|
+
clearTimeout(syncOutputResetTimer);
|
|
51277
|
+
syncOutputResetTimer = 0;
|
|
51278
|
+
}
|
|
51279
|
+
}
|
|
51280
|
+
function scheduleSyncOutputReset() {
|
|
51281
|
+
if (syncOutputResetTimer)
|
|
51282
|
+
return;
|
|
51283
|
+
syncOutputResetTimer = setTimeout(() => {
|
|
51284
|
+
syncOutputResetTimer = 0;
|
|
51285
|
+
if (!inputHandler?.isSynchronizedOutput?.())
|
|
51286
|
+
return;
|
|
51287
|
+
const sanitized = inputHandler.filterOutput(SYNC_OUTPUT_RESET_SEQ) || SYNC_OUTPUT_RESET_SEQ;
|
|
51288
|
+
sendInput(sanitized, "pty");
|
|
51289
|
+
}, SYNC_OUTPUT_RESET_MS);
|
|
51290
|
+
}
|
|
51291
|
+
function flushPtyOutputBuffer() {
|
|
51292
|
+
const output = ptyOutputBuffer;
|
|
51293
|
+
ptyOutputBuffer = "";
|
|
51294
|
+
if (!output)
|
|
51295
|
+
return;
|
|
51296
|
+
sendInput(output, "pty");
|
|
51297
|
+
}
|
|
51298
|
+
function queuePtyOutput(text) {
|
|
51299
|
+
if (!text)
|
|
51300
|
+
return;
|
|
51301
|
+
ptyOutputBuffer += text;
|
|
51302
|
+
if (ptyOutputIdleTimer) {
|
|
51303
|
+
clearTimeout(ptyOutputIdleTimer);
|
|
51304
|
+
}
|
|
51305
|
+
ptyOutputIdleTimer = setTimeout(() => {
|
|
51306
|
+
ptyOutputIdleTimer = 0;
|
|
51307
|
+
if (ptyOutputMaxTimer) {
|
|
51308
|
+
clearTimeout(ptyOutputMaxTimer);
|
|
51309
|
+
ptyOutputMaxTimer = 0;
|
|
51310
|
+
}
|
|
51311
|
+
flushPtyOutputBuffer();
|
|
51312
|
+
}, PTY_OUTPUT_IDLE_MS);
|
|
51313
|
+
if (!ptyOutputMaxTimer) {
|
|
51314
|
+
ptyOutputMaxTimer = setTimeout(() => {
|
|
51315
|
+
ptyOutputMaxTimer = 0;
|
|
51316
|
+
if (ptyOutputIdleTimer) {
|
|
51317
|
+
clearTimeout(ptyOutputIdleTimer);
|
|
51318
|
+
ptyOutputIdleTimer = 0;
|
|
51319
|
+
}
|
|
51320
|
+
flushPtyOutputBuffer();
|
|
51321
|
+
}, PTY_OUTPUT_MAX_MS);
|
|
51322
|
+
}
|
|
51323
|
+
}
|
|
51205
51324
|
function disconnectPty2() {
|
|
51325
|
+
flushPtyOutputBuffer();
|
|
51326
|
+
cancelPtyOutputFlush();
|
|
51327
|
+
cancelSyncOutputReset();
|
|
51328
|
+
ptyOutputBuffer = "";
|
|
51206
51329
|
ptyTransport.disconnect();
|
|
51207
51330
|
updateMouseStatus();
|
|
51208
51331
|
setPtyStatus("disconnected");
|
|
@@ -51249,7 +51372,7 @@ function createResttyApp(options) {
|
|
|
51249
51372
|
const sanitized = inputHandler ? inputHandler.filterOutput(text) : text;
|
|
51250
51373
|
updateMouseStatus();
|
|
51251
51374
|
if (sanitized)
|
|
51252
|
-
|
|
51375
|
+
queuePtyOutput(sanitized);
|
|
51253
51376
|
}
|
|
51254
51377
|
}
|
|
51255
51378
|
});
|
|
@@ -51301,30 +51424,71 @@ function createResttyApp(options) {
|
|
|
51301
51424
|
function bindCanvasEvents() {
|
|
51302
51425
|
if (!attachCanvasEvents)
|
|
51303
51426
|
return;
|
|
51427
|
+
canvas.style.touchAction = touchSelectionMode === "drag" ? "none" : "pan-y pinch-zoom";
|
|
51304
51428
|
const onPointerDown = (event) => {
|
|
51305
51429
|
if (inputHandler.sendMouseEvent("down", event)) {
|
|
51306
51430
|
event.preventDefault();
|
|
51307
51431
|
canvas.setPointerCapture?.(event.pointerId);
|
|
51308
51432
|
return;
|
|
51309
51433
|
}
|
|
51434
|
+
if (isTouchPointer(event)) {
|
|
51435
|
+
if (event.button !== 0)
|
|
51436
|
+
return;
|
|
51437
|
+
const cell2 = normalizeSelectionCell(positionToCell(event));
|
|
51438
|
+
touchSelectionState.activePointerId = null;
|
|
51439
|
+
if (touchSelectionMode === "off")
|
|
51440
|
+
return;
|
|
51441
|
+
if (touchSelectionMode === "drag") {
|
|
51442
|
+
event.preventDefault();
|
|
51443
|
+
beginSelectionDrag(cell2, event.pointerId);
|
|
51444
|
+
return;
|
|
51445
|
+
}
|
|
51446
|
+
clearPendingTouchSelection();
|
|
51447
|
+
touchSelectionState.pendingPointerId = event.pointerId;
|
|
51448
|
+
touchSelectionState.pendingCell = cell2;
|
|
51449
|
+
touchSelectionState.pendingStartX = event.clientX;
|
|
51450
|
+
touchSelectionState.pendingStartY = event.clientY;
|
|
51451
|
+
touchSelectionState.pendingTimer = setTimeout(() => {
|
|
51452
|
+
if (touchSelectionState.pendingPointerId !== event.pointerId || !touchSelectionState.pendingCell) {
|
|
51453
|
+
return;
|
|
51454
|
+
}
|
|
51455
|
+
const pendingCell = touchSelectionState.pendingCell;
|
|
51456
|
+
clearPendingTouchSelection();
|
|
51457
|
+
beginSelectionDrag(pendingCell, event.pointerId);
|
|
51458
|
+
}, touchSelectionLongPressMs);
|
|
51459
|
+
return;
|
|
51460
|
+
}
|
|
51310
51461
|
if (event.button !== 0)
|
|
51311
51462
|
return;
|
|
51312
51463
|
event.preventDefault();
|
|
51313
51464
|
const cell = normalizeSelectionCell(positionToCell(event));
|
|
51314
51465
|
updateLinkHover(cell);
|
|
51315
|
-
|
|
51316
|
-
selectionState.dragging = true;
|
|
51317
|
-
selectionState.anchor = cell;
|
|
51318
|
-
selectionState.focus = cell;
|
|
51319
|
-
canvas.setPointerCapture?.(event.pointerId);
|
|
51320
|
-
updateCanvasCursor();
|
|
51321
|
-
needsRender = true;
|
|
51466
|
+
beginSelectionDrag(cell, event.pointerId);
|
|
51322
51467
|
};
|
|
51323
51468
|
const onPointerMove = (event) => {
|
|
51324
51469
|
if (inputHandler.sendMouseEvent("move", event)) {
|
|
51325
51470
|
event.preventDefault();
|
|
51326
51471
|
return;
|
|
51327
51472
|
}
|
|
51473
|
+
if (isTouchPointer(event)) {
|
|
51474
|
+
if (touchSelectionState.pendingPointerId === event.pointerId) {
|
|
51475
|
+
const dx = event.clientX - touchSelectionState.pendingStartX;
|
|
51476
|
+
const dy = event.clientY - touchSelectionState.pendingStartY;
|
|
51477
|
+
if (dx * dx + dy * dy >= touchSelectionMoveThresholdPx * touchSelectionMoveThresholdPx) {
|
|
51478
|
+
clearPendingTouchSelection();
|
|
51479
|
+
}
|
|
51480
|
+
return;
|
|
51481
|
+
}
|
|
51482
|
+
if (selectionState.dragging && touchSelectionState.activePointerId === event.pointerId) {
|
|
51483
|
+
const cell2 = normalizeSelectionCell(positionToCell(event));
|
|
51484
|
+
event.preventDefault();
|
|
51485
|
+
selectionState.focus = cell2;
|
|
51486
|
+
updateLinkHover(null);
|
|
51487
|
+
updateCanvasCursor();
|
|
51488
|
+
needsRender = true;
|
|
51489
|
+
}
|
|
51490
|
+
return;
|
|
51491
|
+
}
|
|
51328
51492
|
const cell = normalizeSelectionCell(positionToCell(event));
|
|
51329
51493
|
if (!selectionState.dragging) {
|
|
51330
51494
|
updateLinkHover(cell);
|
|
@@ -51341,6 +51505,27 @@ function createResttyApp(options) {
|
|
|
51341
51505
|
event.preventDefault();
|
|
51342
51506
|
return;
|
|
51343
51507
|
}
|
|
51508
|
+
if (isTouchPointer(event)) {
|
|
51509
|
+
if (touchSelectionState.pendingPointerId === event.pointerId) {
|
|
51510
|
+
clearPendingTouchSelection();
|
|
51511
|
+
touchSelectionState.activePointerId = null;
|
|
51512
|
+
return;
|
|
51513
|
+
}
|
|
51514
|
+
if (selectionState.dragging && touchSelectionState.activePointerId === event.pointerId) {
|
|
51515
|
+
const cell2 = normalizeSelectionCell(positionToCell(event));
|
|
51516
|
+
event.preventDefault();
|
|
51517
|
+
selectionState.dragging = false;
|
|
51518
|
+
selectionState.focus = cell2;
|
|
51519
|
+
touchSelectionState.activePointerId = null;
|
|
51520
|
+
if (selectionState.anchor && selectionState.focus && selectionState.anchor.row === selectionState.focus.row && selectionState.anchor.col === selectionState.focus.col) {
|
|
51521
|
+
clearSelection();
|
|
51522
|
+
} else {
|
|
51523
|
+
updateCanvasCursor();
|
|
51524
|
+
needsRender = true;
|
|
51525
|
+
}
|
|
51526
|
+
}
|
|
51527
|
+
return;
|
|
51528
|
+
}
|
|
51344
51529
|
const cell = normalizeSelectionCell(positionToCell(event));
|
|
51345
51530
|
if (selectionState.dragging) {
|
|
51346
51531
|
event.preventDefault();
|
|
@@ -51359,6 +51544,21 @@ function createResttyApp(options) {
|
|
|
51359
51544
|
openLink(linkState.hoverUri);
|
|
51360
51545
|
}
|
|
51361
51546
|
};
|
|
51547
|
+
const onPointerCancel = (event) => {
|
|
51548
|
+
if (isTouchPointer(event)) {
|
|
51549
|
+
if (touchSelectionState.pendingPointerId === event.pointerId) {
|
|
51550
|
+
clearPendingTouchSelection();
|
|
51551
|
+
}
|
|
51552
|
+
if (touchSelectionState.activePointerId === event.pointerId) {
|
|
51553
|
+
touchSelectionState.activePointerId = null;
|
|
51554
|
+
if (selectionState.dragging) {
|
|
51555
|
+
selectionState.dragging = false;
|
|
51556
|
+
updateCanvasCursor();
|
|
51557
|
+
needsRender = true;
|
|
51558
|
+
}
|
|
51559
|
+
}
|
|
51560
|
+
}
|
|
51561
|
+
};
|
|
51362
51562
|
const onWheel = (event) => {
|
|
51363
51563
|
const mouseActive = inputHandler.isMouseActive();
|
|
51364
51564
|
const altScreen = inputHandler.isAltScreen ? inputHandler.isAltScreen() : false;
|
|
@@ -51401,6 +51601,7 @@ function createResttyApp(options) {
|
|
|
51401
51601
|
canvas.addEventListener("pointerdown", onPointerDown);
|
|
51402
51602
|
canvas.addEventListener("pointermove", onPointerMove);
|
|
51403
51603
|
canvas.addEventListener("pointerup", onPointerUp);
|
|
51604
|
+
canvas.addEventListener("pointercancel", onPointerCancel);
|
|
51404
51605
|
canvas.addEventListener("pointerleave", onPointerLeave);
|
|
51405
51606
|
canvas.addEventListener("wheel", onWheel, { passive: false });
|
|
51406
51607
|
canvas.addEventListener("contextmenu", onContextMenu);
|
|
@@ -51408,9 +51609,11 @@ function createResttyApp(options) {
|
|
|
51408
51609
|
canvas.removeEventListener("pointerdown", onPointerDown);
|
|
51409
51610
|
canvas.removeEventListener("pointermove", onPointerMove);
|
|
51410
51611
|
canvas.removeEventListener("pointerup", onPointerUp);
|
|
51612
|
+
canvas.removeEventListener("pointercancel", onPointerCancel);
|
|
51411
51613
|
canvas.removeEventListener("pointerleave", onPointerLeave);
|
|
51412
51614
|
canvas.removeEventListener("wheel", onWheel);
|
|
51413
51615
|
canvas.removeEventListener("contextmenu", onContextMenu);
|
|
51616
|
+
clearPendingTouchSelection();
|
|
51414
51617
|
});
|
|
51415
51618
|
if (imeInput) {
|
|
51416
51619
|
let suppressNextInput = false;
|
|
@@ -55497,7 +55700,7 @@ function createResttyApp(options) {
|
|
|
55497
55700
|
return;
|
|
55498
55701
|
if (!text)
|
|
55499
55702
|
return;
|
|
55500
|
-
const normalized = normalizeNewlines(text);
|
|
55703
|
+
const normalized = source === "pty" ? text : normalizeNewlines(text);
|
|
55501
55704
|
if (source === "key") {
|
|
55502
55705
|
const bytes = textEncoder3.encode(normalized);
|
|
55503
55706
|
const hex = Array.from(bytes, (b3) => b3.toString(16).padStart(2, "0")).join(" ");
|
|
@@ -55519,8 +55722,13 @@ function createResttyApp(options) {
|
|
|
55519
55722
|
clearSelection();
|
|
55520
55723
|
}
|
|
55521
55724
|
writeToWasm(wasmHandle, normalized);
|
|
55522
|
-
wasm.renderUpdate(wasmHandle);
|
|
55523
55725
|
flushWasmOutputToPty();
|
|
55726
|
+
if (source === "pty" && inputHandler?.isSynchronizedOutput?.()) {
|
|
55727
|
+
scheduleSyncOutputReset();
|
|
55728
|
+
return;
|
|
55729
|
+
}
|
|
55730
|
+
cancelSyncOutputReset();
|
|
55731
|
+
wasm.renderUpdate(wasmHandle);
|
|
55524
55732
|
if (source === "key" && wasmExports?.restty_debug_cursor_x && wasmExports?.restty_debug_cursor_y) {
|
|
55525
55733
|
const ax = wasmExports.restty_debug_cursor_x(wasmHandle);
|
|
55526
55734
|
const ay = wasmExports.restty_debug_cursor_y(wasmHandle);
|
|
@@ -55749,6 +55957,7 @@ function createResttyApp(options) {
|
|
|
55749
55957
|
clearTimeout(terminalResizeTimer);
|
|
55750
55958
|
terminalResizeTimer = 0;
|
|
55751
55959
|
}
|
|
55960
|
+
cancelSyncOutputReset();
|
|
55752
55961
|
pendingTerminalResize = null;
|
|
55753
55962
|
disconnectPty2();
|
|
55754
55963
|
ptyTransport.destroy?.();
|