restty 0.1.25 → 0.1.26

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 CHANGED
@@ -173,10 +173,23 @@ const metricsPlugin: ResttyPlugin = {
173
173
  const lifecycle = ctx.addLifecycleHook(({ phase, action }) => {
174
174
  console.log("lifecycle", phase, action);
175
175
  });
176
+ const stage = ctx.addRenderStage({
177
+ id: "metrics/tint",
178
+ mode: "after-main",
179
+ uniforms: [0.12],
180
+ shader: {
181
+ wgsl: `
182
+ fn resttyStage(color: vec4f, uv: vec2f, time: f32, params0: vec4f, params1: vec4f) -> vec4f {
183
+ return vec4f(min(vec3f(1.0), color.rgb + vec3f(params0.x, 0.0, 0.0)), color.a);
184
+ }
185
+ `,
186
+ },
187
+ });
176
188
  return () => {
177
189
  paneCreated.dispose();
178
190
  outgoing.dispose();
179
191
  lifecycle.dispose();
192
+ stage.dispose();
180
193
  };
181
194
  },
182
195
  };
@@ -199,6 +212,50 @@ await restty.loadPlugins(
199
212
 
200
213
  See `docs/plugins.md` for full plugin authoring details.
201
214
 
215
+ ### Shader stages
216
+
217
+ Shader stages let you extend the final frame pipeline with WGSL/GLSL passes.
218
+
219
+ Global stages:
220
+
221
+ ```ts
222
+ restty.setShaderStages([
223
+ {
224
+ id: "app/crt-lite",
225
+ mode: "after-main",
226
+ backend: "both",
227
+ uniforms: [0.24, 0.12],
228
+ shader: {
229
+ wgsl: `
230
+ fn resttyStage(color: vec4f, uv: vec2f, time: f32, params0: vec4f, params1: vec4f) -> vec4f {
231
+ let v = clamp(params0.x, 0.0, 0.8);
232
+ let centered = (uv - vec2f(0.5, 0.5)) * 2.0;
233
+ let vignette = max(0.0, 1.0 - v * dot(centered, centered));
234
+ return vec4f(color.rgb * vignette, color.a);
235
+ }
236
+ `,
237
+ },
238
+ },
239
+ ]);
240
+
241
+ const stage = restty.addShaderStage({
242
+ id: "app/mono",
243
+ mode: "after-main",
244
+ uniforms: [1.0],
245
+ shader: {
246
+ wgsl: `
247
+ fn resttyStage(color: vec4f, uv: vec2f, time: f32, params0: vec4f, params1: vec4f) -> vec4f {
248
+ let l = dot(color.rgb, vec3f(0.2126, 0.7152, 0.0722));
249
+ return vec4f(l * 0.12, l * 0.95, l * 0.35, color.a);
250
+ }
251
+ `,
252
+ },
253
+ });
254
+
255
+ stage.setEnabled(false);
256
+ restty.removeShaderStage("app/mono");
257
+ ```
258
+
202
259
  ### xterm compatibility layer
203
260
 
204
261
  For migration from xterm.js-style app code, use `restty/xterm`:
@@ -260,7 +317,12 @@ Active-pane convenience:
260
317
  Plugin host:
261
318
 
262
319
  - `use(plugin, options?)` / `loadPlugins(manifest, registry)` / `unuse(pluginId)` / `plugins()` / `pluginInfo(pluginId?)`
263
- - plugin context supports `on(...)`, `addInputInterceptor(...)`, `addOutputInterceptor(...)`, `addLifecycleHook(...)`, `addRenderHook(...)`
320
+ - plugin context supports `on(...)`, `addInputInterceptor(...)`, `addOutputInterceptor(...)`, `addLifecycleHook(...)`, `addRenderHook(...)`, `addRenderStage(...)`
321
+
322
+ Shader stages:
323
+
324
+ - `setShaderStages(stages)` / `getShaderStages()`
325
+ - `addShaderStage(stage)` / `removeShaderStage(id)`
264
326
 
265
327
  ## Advanced / Internal Modules
266
328
 
@@ -281,6 +343,13 @@ bun run playground
281
343
 
282
344
  Open `http://localhost:5173`.
283
345
 
346
+ ## Code Layout
347
+
348
+ - `src/surface/`: public API (`Restty`), pane manager orchestration, plugin host, xterm shim.
349
+ - `src/runtime/`: terminal runtime/render loop implementation.
350
+ - `src/renderer/`, `src/input/`, `src/pty/`, `src/fonts/`, `src/theme/`, `src/wasm/`, `src/selection/`: subsystem modules.
351
+ - `src/app/`: compatibility re-export layer while internals are refactored.
352
+
284
353
  ## Repository Commands
285
354
 
286
355
  ```bash
@@ -7922,12 +7922,14 @@ function encodeBeforeInputEvent(event) {
7922
7922
  const type = event.inputType;
7923
7923
  if (type === "insertText")
7924
7924
  return event.data || "";
7925
- if (type === "insertLineBreak")
7925
+ if (type === "insertLineBreak" || type === "insertParagraph")
7926
7926
  return sequences.enter;
7927
- if (type === "deleteContentBackward")
7927
+ if (type === "deleteContentBackward" || type === "deleteWordBackward" || type === "deleteSoftLineBackward" || type === "deleteHardLineBackward" || type === "deleteEntireSoftLine") {
7928
7928
  return sequences.backspace;
7929
- if (type === "deleteContentForward")
7929
+ }
7930
+ if (type === "deleteContentForward" || type === "deleteWordForward" || type === "deleteSoftLineForward" || type === "deleteHardLineForward") {
7930
7931
  return sequences.delete;
7932
+ }
7931
7933
  if (type === "insertFromPaste") {
7932
7934
  return event.dataTransfer?.getData("text/plain") || "";
7933
7935
  }
@@ -8303,10 +8305,20 @@ function encodeKittyKeyEvent(event, kittyFlags) {
8303
8305
  }
8304
8306
 
8305
8307
  // src/input/keymap/pty-map.ts
8308
+ function parseKittyEventType(body) {
8309
+ const [, modifiersPart = ""] = body.split(";");
8310
+ if (!modifiersPart)
8311
+ return 0;
8312
+ const [, eventTypePart = ""] = modifiersPart.split(":");
8313
+ const parsed = Number(eventTypePart);
8314
+ return Number.isFinite(parsed) ? parsed : 0;
8315
+ }
8306
8316
  function mapKeySequenceForPty(seq) {
8307
8317
  const csi = "\x1B[";
8308
8318
  if (seq.startsWith(csi) && seq.endsWith("u")) {
8309
8319
  const body = seq.slice(csi.length, -1);
8320
+ if (parseKittyEventType(body) === 3)
8321
+ return seq;
8310
8322
  const [codeText] = body.split(";");
8311
8323
  if (codeText && /^[0-9]+$/.test(codeText)) {
8312
8324
  const code = Number(codeText);
@@ -8320,6 +8332,8 @@ function mapKeySequenceForPty(seq) {
8320
8332
  }
8321
8333
  if (seq.startsWith(csi) && seq.endsWith("~")) {
8322
8334
  const body = seq.slice(csi.length, -1);
8335
+ if (parseKittyEventType(body) === 3)
8336
+ return seq;
8323
8337
  if (body === "3" || body.startsWith("3;"))
8324
8338
  return "\x1B[3~";
8325
8339
  }
@@ -52765,24 +52779,7 @@ function createDumpGlyphRender(options) {
52765
52779
  });
52766
52780
  const uniforms = new Float32Array([outW, outH, 0, 0, 0, 0, 0, 0]);
52767
52781
  state2.device.queue.writeBuffer(uniformBuffer, 0, uniforms);
52768
- const instance = new Float32Array([
52769
- 0,
52770
- 0,
52771
- outW,
52772
- outH,
52773
- u02,
52774
- v02,
52775
- u12,
52776
- v12,
52777
- 1,
52778
- 1,
52779
- 1,
52780
- 1,
52781
- 0,
52782
- 0,
52783
- 0,
52784
- 1
52785
- ]);
52782
+ const instance = new Float32Array([0, 0, outW, outH, u02, v02, u12, v12, 1, 1, 1, 1, 0, 0, 0, 1]);
52786
52783
  const instanceBuffer = state2.device.createBuffer({
52787
52784
  size: instance.byteLength,
52788
52785
  usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
@@ -52889,7 +52886,10 @@ function createSetupDebugExpose(options, diagnoseCodepoint) {
52889
52886
  debugWindow.dumpAtlasRegion = createDumpAtlasRegion(options);
52890
52887
  debugWindow.dumpGlyphRender = createDumpGlyphRender(options);
52891
52888
  };
52892
- function createDumpGlyphMetrics({ pickFontIndexForText: pickFontIndexForText2, fontState }) {
52889
+ function createDumpGlyphMetrics({
52890
+ pickFontIndexForText: pickFontIndexForText2,
52891
+ fontState
52892
+ }) {
52893
52893
  return (cp) => {
52894
52894
  const text2 = String.fromCodePoint(cp);
52895
52895
  const fontIndex = pickFontIndexForText2(text2, 1);
@@ -52937,9 +52937,7 @@ function createSetupDebugExpose(options, diagnoseCodepoint) {
52937
52937
  return { fontIndex, glyphId };
52938
52938
  };
52939
52939
  }
52940
- function createDumpAtlasRegion({
52941
- getActiveState
52942
- }) {
52940
+ function createDumpAtlasRegion({ getActiveState }) {
52943
52941
  return async (fontIndex, x3, y, width, height) => {
52944
52942
  const state2 = getActiveState();
52945
52943
  if (!state2 || !("device" in state2)) {
@@ -52951,7 +52949,10 @@ function createSetupDebugExpose(options, diagnoseCodepoint) {
52951
52949
  console.warn("atlas not ready");
52952
52950
  return null;
52953
52951
  }
52954
- const image = await readTextureToImageData(state2.device, atlasState.texture, width, height, { x: x3, y });
52952
+ const image = await readTextureToImageData(state2.device, atlasState.texture, width, height, {
52953
+ x: x3,
52954
+ y
52955
+ });
52955
52956
  const canvas = document.createElement("canvas");
52956
52957
  canvas.width = width;
52957
52958
  canvas.height = height;
@@ -53222,6 +53223,21 @@ function bindImeEvents(options) {
53222
53223
  keydownBeforeinputDedupeMs
53223
53224
  } = bindOptions;
53224
53225
  let suppressNextInput = false;
53226
+ let lastNormalizedKeydownSeq = "";
53227
+ let lastNormalizedKeydownSeqSource = "";
53228
+ const getNormalizedLastKeydownSeq = () => {
53229
+ const source = getLastKeydownSeq();
53230
+ if (!source) {
53231
+ lastNormalizedKeydownSeqSource = "";
53232
+ lastNormalizedKeydownSeq = "";
53233
+ return "";
53234
+ }
53235
+ if (source === lastNormalizedKeydownSeqSource)
53236
+ return lastNormalizedKeydownSeq;
53237
+ lastNormalizedKeydownSeqSource = source;
53238
+ lastNormalizedKeydownSeq = inputHandler.mapKeyForPty(source);
53239
+ return lastNormalizedKeydownSeq;
53240
+ };
53225
53241
  const onCompositionStart = (event) => {
53226
53242
  imeState.composing = true;
53227
53243
  setPreedit2(event.data || imeInput.value || "");
@@ -53263,8 +53279,10 @@ function bindImeEvents(options) {
53263
53279
  }
53264
53280
  const text2 = inputHandler.encodeBeforeInput(event);
53265
53281
  if (text2) {
53282
+ const normalizedText = inputHandler.mapKeyForPty(text2);
53283
+ const normalizedLastKeydownSeq = getNormalizedLastKeydownSeq();
53266
53284
  const now = performance.now();
53267
- if (getLastKeydownSeq() && text2 === getLastKeydownSeq() && now - getLastKeydownSeqAt() <= keydownBeforeinputDedupeMs) {
53285
+ if (normalizedLastKeydownSeq && normalizedText === normalizedLastKeydownSeq && now - getLastKeydownSeqAt() <= keydownBeforeinputDedupeMs) {
53268
53286
  event.preventDefault();
53269
53287
  suppressNextInput = true;
53270
53288
  imeInput.value = "";
@@ -57883,10 +57901,7 @@ function createFontRuntimeTextHelpers(options) {
57883
57901
  (entry) => hasBoldHint(entry) && hasItalicHint(entry),
57884
57902
  (entry) => hasBoldHint(entry),
57885
57903
  (entry) => hasItalicHint(entry)
57886
- ] : stylePreference === "bold" ? [
57887
- (entry) => hasBoldHint(entry) && !hasItalicHint(entry),
57888
- (entry) => hasBoldHint(entry)
57889
- ] : stylePreference === "italic" ? [
57904
+ ] : stylePreference === "bold" ? [(entry) => hasBoldHint(entry) && !hasItalicHint(entry), (entry) => hasBoldHint(entry)] : stylePreference === "italic" ? [
57890
57905
  (entry) => hasItalicHint(entry) && !hasBoldHint(entry),
57891
57906
  (entry) => hasItalicHint(entry)
57892
57907
  ] : [];
@@ -59845,7 +59860,19 @@ function createResttyApp(options) {
59845
59860
  lastRenderState = value;
59846
59861
  }
59847
59862
  });
59848
- const readRuntimeAppApiState = () => ({ wasm, wasmExports, wasmHandle, wasmReady, activeState, needsRender, lastRenderTime, currentContextType, isFocused, lastKeydownSeq, lastKeydownSeqAt });
59863
+ const readRuntimeAppApiState = () => ({
59864
+ wasm,
59865
+ wasmExports,
59866
+ wasmHandle,
59867
+ wasmReady,
59868
+ activeState,
59869
+ needsRender,
59870
+ lastRenderTime,
59871
+ currentContextType,
59872
+ isFocused,
59873
+ lastKeydownSeq,
59874
+ lastKeydownSeqAt
59875
+ });
59849
59876
  const writeRuntimeAppApiState = (patch) => {
59850
59877
  ({
59851
59878
  wasm = wasm,
package/dist/internal.js CHANGED
@@ -100,7 +100,7 @@ import {
100
100
  updateGridState,
101
101
  updateImePosition,
102
102
  updateSelection
103
- } from "./chunk-pab3ge3d.js";
103
+ } from "./chunk-y290zh3e.js";
104
104
  export {
105
105
  updateSelection,
106
106
  updateImePosition,
package/dist/restty.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  isBuiltinThemeName,
9
9
  listBuiltinThemeNames,
10
10
  parseGhosttyTheme
11
- } from "./chunk-pab3ge3d.js";
11
+ } from "./chunk-y290zh3e.js";
12
12
  export {
13
13
  parseGhosttyTheme,
14
14
  listBuiltinThemeNames,
@@ -1,6 +1,6 @@
1
1
  import type { ResttyApp, ResttyAppOptions } from "./types";
2
2
  export { createResttyAppSession, getDefaultResttyAppSession } from "./session";
3
- export { createResttyPaneManager, } from "../surface/panes/manager";
3
+ export { createResttyPaneManager } from "../surface/panes/manager";
4
4
  export { createDefaultResttyPaneContextMenuItems, getResttyShortcutModifierLabel, } from "../surface/panes/default-context-menu-items";
5
5
  export type { ResttyAppElements, ResttyAppCallbacks, FontSource, ResttyFontSource, ResttyTouchSelectionMode, ResttyUrlFontSource, ResttyBufferFontSource, ResttyLocalFontSource, ResttyWasmLogListener, ResttyAppSession, ResttyAppInputPayload, ResttyShaderStage, ResttyShaderStageMode, ResttyShaderStageBackend, ResttyShaderStageSource, ResttyAppOptions, ResttyApp, } from "./types";
6
6
  export type { ResttyPaneSplitDirection, ResttyPaneContextMenuItem, ResttyPaneDefinition, ResttyPaneStyleOptions, ResttyPaneStylesOptions, ResttyPaneShortcutsOptions, ResttyPaneContextMenuOptions, CreateResttyPaneManagerOptions, ResttyPaneManager, ResttyPaneWithApp, CreateDefaultResttyPaneContextMenuItemsOptions, } from "../surface/panes-types";
package/dist/xterm.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createRestty
3
- } from "./chunk-pab3ge3d.js";
3
+ } from "./chunk-y290zh3e.js";
4
4
 
5
5
  // src/xterm/app-options.ts
6
6
  function createCompatAppOptions(userAppOptions, emitData) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "restty",
3
- "version": "0.1.25",
3
+ "version": "0.1.26",
4
4
  "description": "Browser terminal rendering library powered by WASM, WebGPU/WebGL2, and TypeScript text shaping.",
5
5
  "keywords": [
6
6
  "terminal",