restty 0.1.21 → 0.1.22

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.
@@ -26723,6 +26723,17 @@ function buildFontAtlasIfNeeded(params) {
26723
26723
  };
26724
26724
  }
26725
26725
 
26726
+ // src/app/clipboard-paste.ts
26727
+ function readPastePayloadFromDataTransfer(dataTransfer) {
26728
+ if (!dataTransfer)
26729
+ return null;
26730
+ const text = dataTransfer.getData("text/plain") || "";
26731
+ return text ? {
26732
+ kind: "text",
26733
+ text
26734
+ } : null;
26735
+ }
26736
+
26726
26737
  // src/app/font-sources.ts
26727
26738
  var DEFAULT_FONT_SOURCES = [
26728
26739
  {
@@ -50315,6 +50326,8 @@ var DEFAULT_EMOJI_CONSTRAINT = {
50315
50326
  };
50316
50327
  function createResttyApp(options) {
50317
50328
  const { canvas: canvasInput, imeInput: imeInputInput, elements, callbacks } = options;
50329
+ const beforeInputHook = options.beforeInput;
50330
+ const beforeRenderOutputHook = options.beforeRenderOutput;
50318
50331
  const session = options.session ?? getDefaultResttyAppSession();
50319
50332
  const textShaper = exports_dist;
50320
50333
  if (!canvasInput) {
@@ -51593,15 +51606,18 @@ function createResttyApp(options) {
51593
51606
  function sendKeyInput(text, source = "key") {
51594
51607
  if (!text)
51595
51608
  return;
51609
+ const intercepted = runBeforeInputHook(text, source);
51610
+ if (!intercepted)
51611
+ return;
51596
51612
  if (source !== "program" && (selectionState.active || selectionState.dragging)) {
51597
51613
  clearSelection();
51598
51614
  }
51599
51615
  if (ptyTransport.isConnected()) {
51600
- const payload = inputHandler.mapKeyForPty(text);
51616
+ const payload = inputHandler.mapKeyForPty(intercepted);
51601
51617
  ptyTransport.sendInput(payload);
51602
51618
  return;
51603
51619
  }
51604
- sendInput(text, source);
51620
+ sendInput(intercepted, source, { skipHooks: true });
51605
51621
  }
51606
51622
  function formatPasteText(text) {
51607
51623
  if (!inputHandler?.isBracketedPaste?.())
@@ -51613,6 +51629,13 @@ function createResttyApp(options) {
51613
51629
  return;
51614
51630
  sendKeyInput(formatPasteText(text));
51615
51631
  }
51632
+ function sendPastePayloadFromDataTransfer(dataTransfer) {
51633
+ const payload = readPastePayloadFromDataTransfer(dataTransfer);
51634
+ if (!payload)
51635
+ return false;
51636
+ sendPasteText(payload.text);
51637
+ return true;
51638
+ }
51616
51639
  function openLink(uri) {
51617
51640
  if (!uri || typeof window === "undefined")
51618
51641
  return;
@@ -51911,13 +51934,16 @@ function createResttyApp(options) {
51911
51934
  if (imeState.composing)
51912
51935
  return;
51913
51936
  if (event.inputType === "insertFromPaste") {
51937
+ event.preventDefault();
51938
+ suppressNextInput = true;
51914
51939
  const pasteText = event.dataTransfer?.getData("text/plain") || event.data || "";
51915
51940
  if (pasteText) {
51916
- event.preventDefault();
51917
- suppressNextInput = true;
51918
51941
  sendPasteText(pasteText);
51919
51942
  imeInput.value = "";
51943
+ return;
51920
51944
  }
51945
+ sendPastePayloadFromDataTransfer(event.dataTransfer);
51946
+ imeInput.value = "";
51921
51947
  return;
51922
51948
  }
51923
51949
  const text = inputHandler.encodeBeforeInput(event);
@@ -51954,13 +51980,16 @@ function createResttyApp(options) {
51954
51980
  const onPaste = (event) => {
51955
51981
  if (!wasmReady || !wasmHandle)
51956
51982
  return;
51983
+ event.preventDefault();
51984
+ suppressNextInput = true;
51957
51985
  const text = event.clipboardData?.getData("text/plain") || "";
51958
51986
  if (text) {
51959
- event.preventDefault();
51960
- suppressNextInput = true;
51961
51987
  sendPasteText(text);
51962
51988
  imeInput.value = "";
51989
+ return;
51963
51990
  }
51991
+ sendPastePayloadFromDataTransfer(event.clipboardData);
51992
+ imeInput.value = "";
51964
51993
  };
51965
51994
  imeInput.addEventListener("compositionstart", onCompositionStart);
51966
51995
  imeInput.addEventListener("compositionupdate", onCompositionUpdate);
@@ -53235,6 +53264,36 @@ function createResttyApp(options) {
53235
53264
  needsRender = true;
53236
53265
  lastRenderTime = 0;
53237
53266
  }
53267
+ function resize(cols, rows) {
53268
+ const nextCols = Math.max(1, Math.floor(Number(cols)));
53269
+ const nextRows = Math.max(1, Math.floor(Number(rows)));
53270
+ if (!Number.isFinite(nextCols) || !Number.isFinite(nextRows))
53271
+ return;
53272
+ const dpr = window.devicePixelRatio || 1;
53273
+ if (dpr !== currentDpr) {
53274
+ currentDpr = dpr;
53275
+ if (dprEl)
53276
+ dprEl.textContent = dpr.toFixed(2);
53277
+ callbacks?.onDpr?.(dpr);
53278
+ }
53279
+ const metrics = computeCellMetrics2();
53280
+ if (!metrics)
53281
+ return;
53282
+ canvas.width = Math.max(1, nextCols * metrics.cellW);
53283
+ canvas.height = Math.max(1, nextRows * metrics.cellH);
53284
+ if (sizeEl)
53285
+ sizeEl.textContent = `${canvas.width}x${canvas.height}`;
53286
+ callbacks?.onCanvasSize?.(canvas.width, canvas.height);
53287
+ resizeState.dpr = currentDpr;
53288
+ resizeState.active = true;
53289
+ resizeState.lastAt = performance.now();
53290
+ resizeState.cols = nextCols;
53291
+ resizeState.rows = nextRows;
53292
+ updateGrid();
53293
+ scheduleTerminalResizeCommit(nextCols, nextRows, { immediate: true });
53294
+ needsRender = true;
53295
+ lastRenderTime = 0;
53296
+ }
53238
53297
  function scheduleSizeUpdate() {
53239
53298
  updateSize();
53240
53299
  if (sizeRaf)
@@ -53244,22 +53303,35 @@ function createResttyApp(options) {
53244
53303
  updateSize();
53245
53304
  });
53246
53305
  }
53306
+ function focusTypingInput() {
53307
+ canvas.focus({ preventScroll: true });
53308
+ if (!imeInput)
53309
+ return;
53310
+ imeInput.focus({ preventScroll: true });
53311
+ if (typeof document !== "undefined" && document.activeElement !== imeInput) {
53312
+ requestAnimationFrame(() => {
53313
+ if (document.activeElement === canvas)
53314
+ imeInput.focus({ preventScroll: true });
53315
+ });
53316
+ }
53317
+ }
53318
+ function focus() {
53319
+ focusTypingInput();
53320
+ isFocused = typeof document !== "undefined" && imeInput ? document.activeElement === canvas || document.activeElement === imeInput : true;
53321
+ }
53322
+ function blur() {
53323
+ if (imeInput && document.activeElement === imeInput) {
53324
+ imeInput.blur();
53325
+ }
53326
+ if (document.activeElement === canvas) {
53327
+ canvas.blur();
53328
+ }
53329
+ isFocused = false;
53330
+ }
53247
53331
  function bindFocusEvents() {
53248
53332
  if (!attachCanvasEvents)
53249
53333
  return;
53250
53334
  canvas.tabIndex = 0;
53251
- const focusTypingInput = () => {
53252
- canvas.focus({ preventScroll: true });
53253
- if (!imeInput)
53254
- return;
53255
- imeInput.focus({ preventScroll: true });
53256
- if (typeof document !== "undefined" && document.activeElement !== imeInput) {
53257
- requestAnimationFrame(() => {
53258
- if (document.activeElement === canvas)
53259
- imeInput.focus({ preventScroll: true });
53260
- });
53261
- }
53262
- };
53263
53335
  const handleFocus = () => {
53264
53336
  isFocused = true;
53265
53337
  focusTypingInput();
@@ -56063,12 +56135,48 @@ function createResttyApp(options) {
56063
56135
  return text.replace(/\r?\n/g, `\r
56064
56136
  `);
56065
56137
  }
56066
- function sendInput(text, source = "program") {
56138
+ function runBeforeInputHook(text, source) {
56139
+ if (!beforeInputHook)
56140
+ return text;
56141
+ try {
56142
+ const next = beforeInputHook({ text, source });
56143
+ if (next === null)
56144
+ return null;
56145
+ if (typeof next === "string")
56146
+ return next;
56147
+ return text;
56148
+ } catch (error) {
56149
+ console.error("[restty] beforeInput hook error:", error);
56150
+ return text;
56151
+ }
56152
+ }
56153
+ function runBeforeRenderOutputHook(text, source) {
56154
+ if (!beforeRenderOutputHook)
56155
+ return text;
56156
+ try {
56157
+ const next = beforeRenderOutputHook({ text, source });
56158
+ if (next === null)
56159
+ return null;
56160
+ if (typeof next === "string")
56161
+ return next;
56162
+ return text;
56163
+ } catch (error) {
56164
+ console.error("[restty] beforeRenderOutput hook error:", error);
56165
+ return text;
56166
+ }
56167
+ }
56168
+ function sendInput(text, source = "program", options2 = {}) {
56067
56169
  if (!wasmReady || !wasm || !wasmHandle)
56068
56170
  return;
56069
56171
  if (!text)
56070
56172
  return;
56071
- const normalized = source === "pty" ? text : normalizeNewlines(text);
56173
+ let intercepted = text;
56174
+ if (!options2.skipHooks) {
56175
+ intercepted = source === "pty" ? runBeforeRenderOutputHook(text, source) : runBeforeInputHook(text, source);
56176
+ }
56177
+ if (!intercepted)
56178
+ return;
56179
+ const normalized = source === "pty" ? intercepted : normalizeNewlines(intercepted);
56072
56180
  if (source === "key") {
56073
56181
  const bytes = textEncoder3.encode(normalized);
56074
56182
  const hex = Array.from(bytes, (b3) => b3.toString(16).padStart(2, "0")).join(" ");
@@ -56134,6 +56242,8 @@ function createResttyApp(options) {
56134
56242
  return false;
56135
56243
  }
56136
56244
  async function pasteFromClipboard() {
56245
+ if (typeof navigator === "undefined" || !navigator.clipboard)
56246
+ return false;
56137
56247
  try {
56138
56248
  const text = await navigator.clipboard.readText();
56139
56249
  if (text) {
@@ -56145,6 +56255,15 @@ function createResttyApp(options) {
56145
56255
  }
56146
56256
  return false;
56147
56257
  }
56258
+ async function handlePasteShortcut(event) {
56259
+ const pasted = await pasteFromClipboard();
56260
+ if (pasted)
56261
+ return;
56262
+ const seq = inputHandler.encodeKeyEvent(event);
56263
+ if (!seq)
56264
+ return;
56265
+ sendKeyInput(seq);
56266
+ }
56148
56267
  function clearScreen() {
56149
56268
  sendInput("\x1B[2J\x1B[H");
56150
56269
  }
@@ -56193,7 +56312,9 @@ function createResttyApp(options) {
56193
56312
  }
56194
56313
  if (wantsPaste) {
56195
56314
  event.preventDefault();
56196
- pasteFromClipboard();
56315
+ if (imeInput)
56316
+ imeInput.focus({ preventScroll: true });
56317
+ handlePasteShortcut(event);
56197
56318
  return;
56198
56319
  }
56199
56320
  const seq = inputHandler.encodeKeyEvent(event);
@@ -56395,6 +56516,9 @@ function createResttyApp(options) {
56395
56516
  copySelectionToClipboard,
56396
56517
  pasteFromClipboard,
56397
56518
  dumpAtlasForCodepoint,
56519
+ resize,
56520
+ focus,
56521
+ blur,
56398
56522
  updateSize,
56399
56523
  getBackend: () => backend
56400
56524
  };
@@ -56514,6 +56638,8 @@ function createResttyAppPaneManager(options) {
56514
56638
  }
56515
56639
 
56516
56640
  // src/app/restty.ts
56641
+ var RESTTY_PLUGIN_API_VERSION = 1;
56642
+
56517
56643
  class ResttyPaneHandle {
56518
56644
  resolvePane;
56519
56645
  constructor(resolvePane) {
@@ -56573,6 +56699,15 @@ class ResttyPaneHandle {
56573
56699
  dumpAtlasForCodepoint(cp) {
56574
56700
  this.resolvePane().app.dumpAtlasForCodepoint(cp);
56575
56701
  }
56702
+ resize(cols, rows) {
56703
+ this.resolvePane().app.resize(cols, rows);
56704
+ }
56705
+ focus() {
56706
+ this.resolvePane().app.focus();
56707
+ }
56708
+ blur() {
56709
+ this.resolvePane().app.blur();
56710
+ }
56576
56711
  updateSize(force) {
56577
56712
  this.resolvePane().app.updateSize(force);
56578
56713
  }
@@ -56587,25 +56722,105 @@ class ResttyPaneHandle {
56587
56722
  class Restty {
56588
56723
  paneManager;
56589
56724
  fontSources;
56725
+ pluginListeners = new Map;
56726
+ pluginRuntimes = new Map;
56727
+ pluginDiagnostics = new Map;
56728
+ inputInterceptors = [];
56729
+ outputInterceptors = [];
56730
+ lifecycleHooks = [];
56731
+ renderHooks = [];
56732
+ nextInterceptorId = 1;
56733
+ nextInterceptorOrder = 1;
56590
56734
  constructor(options) {
56591
- const { createInitialPane = true, appOptions, fontSources, ...paneManagerOptions } = options;
56735
+ const {
56736
+ createInitialPane = true,
56737
+ appOptions,
56738
+ fontSources,
56739
+ onPaneCreated,
56740
+ onPaneClosed,
56741
+ onPaneSplit,
56742
+ onActivePaneChange,
56743
+ onLayoutChanged,
56744
+ ...paneManagerOptions
56745
+ } = options;
56592
56746
  this.fontSources = fontSources ? [...fontSources] : undefined;
56593
56747
  const mergedAppOptions = (context) => {
56594
56748
  const resolved = typeof appOptions === "function" ? appOptions(context) : appOptions ?? {};
56595
- if (!this.fontSources)
56596
- return resolved;
56749
+ const resolvedBeforeInput = resolved.beforeInput;
56750
+ const resolvedBeforeRenderOutput = resolved.beforeRenderOutput;
56597
56751
  return {
56598
56752
  ...resolved,
56599
- fontSources: this.fontSources
56753
+ ...this.fontSources ? { fontSources: this.fontSources } : {},
56754
+ beforeInput: ({ text, source }) => {
56755
+ const maybeUserText = resolvedBeforeInput?.({ text, source });
56756
+ if (maybeUserText === null)
56757
+ return null;
56758
+ const current = maybeUserText === undefined ? text : maybeUserText;
56759
+ return this.applyInputInterceptors(context.id, current, source);
56760
+ },
56761
+ beforeRenderOutput: ({ text, source }) => {
56762
+ this.runRenderHooks({
56763
+ phase: "before",
56764
+ paneId: context.id,
56765
+ text,
56766
+ source,
56767
+ dropped: false
56768
+ });
56769
+ const maybeUserText = resolvedBeforeRenderOutput?.({ text, source });
56770
+ if (maybeUserText === null) {
56771
+ this.runRenderHooks({
56772
+ phase: "after",
56773
+ paneId: context.id,
56774
+ text,
56775
+ source,
56776
+ dropped: true
56777
+ });
56778
+ return null;
56779
+ }
56780
+ const current = maybeUserText === undefined ? text : maybeUserText;
56781
+ const next = this.applyOutputInterceptors(context.id, current, source);
56782
+ this.runRenderHooks({
56783
+ phase: "after",
56784
+ paneId: context.id,
56785
+ text: next === null ? current : next,
56786
+ source,
56787
+ dropped: next === null
56788
+ });
56789
+ return next;
56790
+ }
56600
56791
  };
56601
56792
  };
56602
56793
  this.paneManager = createResttyAppPaneManager({
56603
56794
  ...paneManagerOptions,
56604
- appOptions: mergedAppOptions
56795
+ appOptions: mergedAppOptions,
56796
+ onPaneCreated: (pane) => {
56797
+ this.emitPluginEvent("pane:created", { paneId: pane.id });
56798
+ onPaneCreated?.(pane);
56799
+ },
56800
+ onPaneClosed: (pane) => {
56801
+ this.emitPluginEvent("pane:closed", { paneId: pane.id });
56802
+ onPaneClosed?.(pane);
56803
+ },
56804
+ onPaneSplit: (sourcePane, createdPane, direction) => {
56805
+ this.emitPluginEvent("pane:split", {
56806
+ sourcePaneId: sourcePane.id,
56807
+ createdPaneId: createdPane.id,
56808
+ direction
56809
+ });
56810
+ onPaneSplit?.(sourcePane, createdPane, direction);
56811
+ },
56812
+ onActivePaneChange: (pane) => {
56813
+ this.emitPluginEvent("pane:active-changed", { paneId: pane?.id ?? null });
56814
+ onActivePaneChange?.(pane);
56815
+ },
56816
+ onLayoutChanged: () => {
56817
+ this.emitPluginEvent("layout:changed", {});
56818
+ onLayoutChanged?.();
56819
+ }
56605
56820
  });
56606
56821
  if (createInitialPane) {
56607
56822
  const focus = typeof createInitialPane === "object" ? createInitialPane.focus ?? true : true;
56608
- this.paneManager.createInitialPane({ focus });
56823
+ this.createInitialPane({ focus });
56609
56824
  }
56610
56825
  }
56611
56826
  getPanes() {
@@ -56656,16 +56871,63 @@ class Restty {
56656
56871
  await Promise.all(updates);
56657
56872
  }
56658
56873
  createInitialPane(options) {
56659
- return this.paneManager.createInitialPane(options);
56874
+ this.runLifecycleHooks({ phase: "before", action: "create-initial-pane" });
56875
+ const pane = this.paneManager.createInitialPane(options);
56876
+ this.runLifecycleHooks({
56877
+ phase: "after",
56878
+ action: "create-initial-pane",
56879
+ paneId: pane.id,
56880
+ ok: true
56881
+ });
56882
+ return pane;
56660
56883
  }
56661
56884
  splitActivePane(direction) {
56662
- return this.paneManager.splitActivePane(direction);
56885
+ const sourcePaneId = this.getActivePane()?.id ?? null;
56886
+ this.runLifecycleHooks({
56887
+ phase: "before",
56888
+ action: "split-active-pane",
56889
+ paneId: sourcePaneId,
56890
+ direction
56891
+ });
56892
+ const pane = this.paneManager.splitActivePane(direction);
56893
+ this.runLifecycleHooks({
56894
+ phase: "after",
56895
+ action: "split-active-pane",
56896
+ sourcePaneId: sourcePaneId ?? undefined,
56897
+ createdPaneId: pane?.id ?? null,
56898
+ direction,
56899
+ ok: !!pane
56900
+ });
56901
+ return pane;
56663
56902
  }
56664
56903
  splitPane(id, direction) {
56665
- return this.paneManager.splitPane(id, direction);
56904
+ this.runLifecycleHooks({
56905
+ phase: "before",
56906
+ action: "split-pane",
56907
+ paneId: id,
56908
+ direction
56909
+ });
56910
+ const pane = this.paneManager.splitPane(id, direction);
56911
+ this.runLifecycleHooks({
56912
+ phase: "after",
56913
+ action: "split-pane",
56914
+ sourcePaneId: id,
56915
+ createdPaneId: pane?.id ?? null,
56916
+ direction,
56917
+ ok: !!pane
56918
+ });
56919
+ return pane;
56666
56920
  }
56667
56921
  closePane(id) {
56668
- return this.paneManager.closePane(id);
56922
+ this.runLifecycleHooks({ phase: "before", action: "close-pane", paneId: id });
56923
+ const ok = this.paneManager.closePane(id);
56924
+ this.runLifecycleHooks({
56925
+ phase: "after",
56926
+ action: "close-pane",
56927
+ paneId: id,
56928
+ ok
56929
+ });
56930
+ return ok;
56669
56931
  }
56670
56932
  getPaneStyleOptions() {
56671
56933
  return this.paneManager.getStyleOptions();
@@ -56674,10 +56936,34 @@ class Restty {
56674
56936
  this.paneManager.setStyleOptions(options);
56675
56937
  }
56676
56938
  setActivePane(id, options) {
56939
+ this.runLifecycleHooks({
56940
+ phase: "before",
56941
+ action: "set-active-pane",
56942
+ paneId: id
56943
+ });
56677
56944
  this.paneManager.setActivePane(id, options);
56945
+ const activePaneId = this.getActivePane()?.id ?? null;
56946
+ this.runLifecycleHooks({
56947
+ phase: "after",
56948
+ action: "set-active-pane",
56949
+ paneId: activePaneId,
56950
+ ok: activePaneId === id
56951
+ });
56678
56952
  }
56679
56953
  markPaneFocused(id, options) {
56954
+ this.runLifecycleHooks({
56955
+ phase: "before",
56956
+ action: "mark-pane-focused",
56957
+ paneId: id
56958
+ });
56680
56959
  this.paneManager.markPaneFocused(id, options);
56960
+ const focusedPaneId = this.getFocusedPane()?.id ?? null;
56961
+ this.runLifecycleHooks({
56962
+ phase: "after",
56963
+ action: "mark-pane-focused",
56964
+ paneId: focusedPaneId,
56965
+ ok: focusedPaneId === id
56966
+ });
56681
56967
  }
56682
56968
  requestLayoutSync() {
56683
56969
  this.paneManager.requestLayoutSync();
@@ -56685,14 +56971,193 @@ class Restty {
56685
56971
  hideContextMenu() {
56686
56972
  this.paneManager.hideContextMenu();
56687
56973
  }
56974
+ async use(plugin, options) {
56975
+ if (!plugin || typeof plugin !== "object") {
56976
+ throw new Error("Restty plugin must be an object");
56977
+ }
56978
+ const pluginId = plugin.id?.trim?.() ?? "";
56979
+ if (!pluginId) {
56980
+ throw new Error("Restty plugin id is required");
56981
+ }
56982
+ if (typeof plugin.activate !== "function") {
56983
+ throw new Error(`Restty plugin ${pluginId} must define activate(context)`);
56984
+ }
56985
+ if (this.pluginRuntimes.has(pluginId))
56986
+ return;
56987
+ try {
56988
+ this.assertPluginCompatibility(pluginId, plugin);
56989
+ } catch (error) {
56990
+ this.pluginDiagnostics.set(pluginId, {
56991
+ id: pluginId,
56992
+ version: plugin.version?.trim?.() || null,
56993
+ apiVersion: Number.isFinite(plugin.apiVersion) ? Number(plugin.apiVersion) : null,
56994
+ requires: plugin.requires ?? null,
56995
+ active: false,
56996
+ activatedAt: null,
56997
+ lastError: this.errorToMessage(error)
56998
+ });
56999
+ throw error;
57000
+ }
57001
+ const runtime2 = {
57002
+ plugin: this.normalizePluginMetadata(plugin, pluginId),
57003
+ cleanup: null,
57004
+ activatedAt: Date.now(),
57005
+ options,
57006
+ disposers: []
57007
+ };
57008
+ this.pluginDiagnostics.set(pluginId, {
57009
+ id: pluginId,
57010
+ version: runtime2.plugin.version?.trim?.() || null,
57011
+ apiVersion: Number.isFinite(runtime2.plugin.apiVersion) ? Number(runtime2.plugin.apiVersion) : null,
57012
+ requires: runtime2.plugin.requires ?? null,
57013
+ active: false,
57014
+ activatedAt: null,
57015
+ lastError: null
57016
+ });
57017
+ this.pluginRuntimes.set(pluginId, runtime2);
57018
+ try {
57019
+ const cleanup = await runtime2.plugin.activate(this.createPluginContext(runtime2), runtime2.options);
57020
+ runtime2.cleanup = this.normalizePluginCleanup(cleanup);
57021
+ runtime2.activatedAt = Date.now();
57022
+ this.updatePluginDiagnostic(pluginId, {
57023
+ active: true,
57024
+ activatedAt: runtime2.activatedAt,
57025
+ lastError: null
57026
+ });
57027
+ this.emitPluginEvent("plugin:activated", { pluginId });
57028
+ } catch (error) {
57029
+ this.teardownPluginRuntime(runtime2);
57030
+ this.pluginRuntimes.delete(pluginId);
57031
+ this.updatePluginDiagnostic(pluginId, {
57032
+ active: false,
57033
+ activatedAt: null,
57034
+ lastError: this.errorToMessage(error)
57035
+ });
57036
+ throw error;
57037
+ }
57038
+ }
57039
+ async loadPlugins(manifest, registry) {
57040
+ const results = [];
57041
+ for (let i3 = 0;i3 < manifest.length; i3 += 1) {
57042
+ const item = manifest[i3];
57043
+ const pluginId = item.id?.trim?.() ?? "";
57044
+ if (!pluginId) {
57045
+ results.push({
57046
+ id: "",
57047
+ status: "failed",
57048
+ error: "Restty plugin manifest entry is missing id"
57049
+ });
57050
+ continue;
57051
+ }
57052
+ if (item.enabled === false) {
57053
+ results.push({ id: pluginId, status: "skipped", error: null });
57054
+ continue;
57055
+ }
57056
+ const entry = this.lookupPluginRegistryEntry(registry, pluginId);
57057
+ if (!entry) {
57058
+ const message = `Restty plugin ${pluginId} was not found in registry`;
57059
+ this.setPluginLoadError(pluginId, message);
57060
+ results.push({ id: pluginId, status: "missing", error: message });
57061
+ continue;
57062
+ }
57063
+ let plugin;
57064
+ try {
57065
+ plugin = await this.resolvePluginRegistryEntry(entry);
57066
+ } catch (error) {
57067
+ const message = this.errorToMessage(error);
57068
+ this.setPluginLoadError(pluginId, message);
57069
+ results.push({ id: pluginId, status: "failed", error: message });
57070
+ continue;
57071
+ }
57072
+ const resolvedId = plugin.id?.trim?.() ?? "";
57073
+ if (resolvedId !== pluginId) {
57074
+ const message = `Restty plugin registry entry ${pluginId} resolved to id ${resolvedId || "(empty)"}`;
57075
+ this.setPluginLoadError(pluginId, message);
57076
+ results.push({ id: pluginId, status: "failed", error: message });
57077
+ continue;
57078
+ }
57079
+ try {
57080
+ await this.use(plugin, item.options);
57081
+ results.push({ id: pluginId, status: "loaded", error: null });
57082
+ } catch (error) {
57083
+ results.push({
57084
+ id: pluginId,
57085
+ status: "failed",
57086
+ error: this.errorToMessage(error)
57087
+ });
57088
+ }
57089
+ }
57090
+ return results;
57091
+ }
57092
+ unuse(pluginId) {
57093
+ const key = pluginId?.trim?.() ?? "";
57094
+ if (!key)
57095
+ return false;
57096
+ const runtime2 = this.pluginRuntimes.get(key);
57097
+ if (!runtime2)
57098
+ return false;
57099
+ this.pluginRuntimes.delete(key);
57100
+ this.teardownPluginRuntime(runtime2);
57101
+ this.updatePluginDiagnostic(key, {
57102
+ active: false,
57103
+ activatedAt: null
57104
+ });
57105
+ this.emitPluginEvent("plugin:deactivated", { pluginId: key });
57106
+ return true;
57107
+ }
57108
+ plugins() {
57109
+ return Array.from(this.pluginRuntimes.keys());
57110
+ }
57111
+ pluginInfo(pluginId) {
57112
+ if (typeof pluginId === "string") {
57113
+ const key = pluginId.trim();
57114
+ if (!key)
57115
+ return null;
57116
+ return this.buildPluginInfo(key);
57117
+ }
57118
+ const keys = new Set;
57119
+ for (const key of this.pluginDiagnostics.keys())
57120
+ keys.add(key);
57121
+ for (const key of this.pluginRuntimes.keys())
57122
+ keys.add(key);
57123
+ return Array.from(keys).sort((a3, b3) => a3.localeCompare(b3)).map((key) => this.buildPluginInfo(key)).filter((entry) => entry !== null);
57124
+ }
56688
57125
  destroy() {
57126
+ const pluginIds = this.plugins();
57127
+ for (let i3 = 0;i3 < pluginIds.length; i3 += 1) {
57128
+ this.unuse(pluginIds[i3]);
57129
+ }
56689
57130
  this.paneManager.destroy();
56690
57131
  }
56691
57132
  connectPty(url = "") {
56692
- this.requireActivePaneHandle().connectPty(url);
57133
+ const pane = this.requireActivePaneHandle();
57134
+ this.runLifecycleHooks({
57135
+ phase: "before",
57136
+ action: "connect-pty",
57137
+ paneId: pane.id
57138
+ });
57139
+ pane.connectPty(url);
57140
+ this.runLifecycleHooks({
57141
+ phase: "after",
57142
+ action: "connect-pty",
57143
+ paneId: pane.id,
57144
+ ok: true
57145
+ });
56693
57146
  }
56694
57147
  disconnectPty() {
56695
- this.requireActivePaneHandle().disconnectPty();
57148
+ const pane = this.requireActivePaneHandle();
57149
+ this.runLifecycleHooks({
57150
+ phase: "before",
57151
+ action: "disconnect-pty",
57152
+ paneId: pane.id
57153
+ });
57154
+ pane.disconnectPty();
57155
+ this.runLifecycleHooks({
57156
+ phase: "after",
57157
+ action: "disconnect-pty",
57158
+ paneId: pane.id,
57159
+ ok: true
57160
+ });
56696
57161
  }
56697
57162
  isPtyConnected() {
56698
57163
  return this.requireActivePaneHandle().isPtyConnected();
@@ -56739,6 +57204,58 @@ class Restty {
56739
57204
  dumpAtlasForCodepoint(cp) {
56740
57205
  this.requireActivePaneHandle().dumpAtlasForCodepoint(cp);
56741
57206
  }
57207
+ resize(cols, rows) {
57208
+ const pane = this.requireActivePaneHandle();
57209
+ this.runLifecycleHooks({
57210
+ phase: "before",
57211
+ action: "resize",
57212
+ paneId: pane.id,
57213
+ cols,
57214
+ rows
57215
+ });
57216
+ pane.resize(cols, rows);
57217
+ this.runLifecycleHooks({
57218
+ phase: "after",
57219
+ action: "resize",
57220
+ paneId: pane.id,
57221
+ cols,
57222
+ rows,
57223
+ ok: true
57224
+ });
57225
+ this.emitPluginEvent("pane:resized", { paneId: pane.id, cols, rows });
57226
+ }
57227
+ focus() {
57228
+ const pane = this.requireActivePaneHandle();
57229
+ this.runLifecycleHooks({
57230
+ phase: "before",
57231
+ action: "focus",
57232
+ paneId: pane.id
57233
+ });
57234
+ pane.focus();
57235
+ this.runLifecycleHooks({
57236
+ phase: "after",
57237
+ action: "focus",
57238
+ paneId: pane.id,
57239
+ ok: true
57240
+ });
57241
+ this.emitPluginEvent("pane:focused", { paneId: pane.id });
57242
+ }
57243
+ blur() {
57244
+ const pane = this.requireActivePaneHandle();
57245
+ this.runLifecycleHooks({
57246
+ phase: "before",
57247
+ action: "blur",
57248
+ paneId: pane.id
57249
+ });
57250
+ pane.blur();
57251
+ this.runLifecycleHooks({
57252
+ phase: "after",
57253
+ action: "blur",
57254
+ paneId: pane.id,
57255
+ ok: true
57256
+ });
57257
+ this.emitPluginEvent("pane:blurred", { paneId: pane.id });
57258
+ }
56742
57259
  updateSize(force) {
56743
57260
  this.requireActivePaneHandle().updateSize(force);
56744
57261
  }
@@ -56761,9 +57278,300 @@ class Restty {
56761
57278
  }
56762
57279
  return this.makePaneHandle(pane.id);
56763
57280
  }
57281
+ createPluginContext(runtime2) {
57282
+ return {
57283
+ restty: this,
57284
+ options: runtime2.options,
57285
+ panes: () => this.panes(),
57286
+ pane: (id) => this.pane(id),
57287
+ activePane: () => this.activePane(),
57288
+ focusedPane: () => this.focusedPane(),
57289
+ on: (event, listener) => {
57290
+ return {
57291
+ dispose: this.attachRuntimeDisposer(runtime2, "event", this.onPluginEvent(event, listener))
57292
+ };
57293
+ },
57294
+ addInputInterceptor: (interceptor, options) => {
57295
+ return {
57296
+ dispose: this.attachRuntimeDisposer(runtime2, "input-interceptor", this.addInputInterceptor(runtime2.plugin.id, interceptor, options))
57297
+ };
57298
+ },
57299
+ addOutputInterceptor: (interceptor, options) => {
57300
+ return {
57301
+ dispose: this.attachRuntimeDisposer(runtime2, "output-interceptor", this.addOutputInterceptor(runtime2.plugin.id, interceptor, options))
57302
+ };
57303
+ },
57304
+ addLifecycleHook: (hook, options) => {
57305
+ return {
57306
+ dispose: this.attachRuntimeDisposer(runtime2, "lifecycle-hook", this.addLifecycleHook(runtime2.plugin.id, hook, options))
57307
+ };
57308
+ },
57309
+ addRenderHook: (hook, options) => {
57310
+ return {
57311
+ dispose: this.attachRuntimeDisposer(runtime2, "render-hook", this.addRenderHook(runtime2.plugin.id, hook, options))
57312
+ };
57313
+ }
57314
+ };
57315
+ }
57316
+ attachRuntimeDisposer(runtime2, kind, dispose) {
57317
+ const entry = {
57318
+ kind,
57319
+ active: true,
57320
+ dispose: () => {
57321
+ if (!entry.active)
57322
+ return;
57323
+ entry.active = false;
57324
+ dispose();
57325
+ }
57326
+ };
57327
+ runtime2.disposers.push(entry);
57328
+ return entry.dispose;
57329
+ }
57330
+ addInputInterceptor(pluginId, interceptor, options) {
57331
+ return this.registerInterceptor(this.inputInterceptors, pluginId, interceptor, options);
57332
+ }
57333
+ addOutputInterceptor(pluginId, interceptor, options) {
57334
+ return this.registerInterceptor(this.outputInterceptors, pluginId, interceptor, options);
57335
+ }
57336
+ addLifecycleHook(pluginId, hook, options) {
57337
+ return this.registerInterceptor(this.lifecycleHooks, pluginId, hook, options);
57338
+ }
57339
+ addRenderHook(pluginId, hook, options) {
57340
+ return this.registerInterceptor(this.renderHooks, pluginId, hook, options);
57341
+ }
57342
+ registerInterceptor(bucket, pluginId, interceptor, options) {
57343
+ const entry = {
57344
+ id: this.nextInterceptorId++,
57345
+ pluginId,
57346
+ priority: Number.isFinite(options?.priority) ? Number(options?.priority) : 0,
57347
+ order: this.nextInterceptorOrder++,
57348
+ interceptor
57349
+ };
57350
+ bucket.push(entry);
57351
+ bucket.sort((a3, b3) => {
57352
+ if (a3.priority !== b3.priority)
57353
+ return a3.priority - b3.priority;
57354
+ return a3.order - b3.order;
57355
+ });
57356
+ return () => {
57357
+ const index = bucket.findIndex((current) => current.id === entry.id);
57358
+ if (index >= 0) {
57359
+ bucket.splice(index, 1);
57360
+ }
57361
+ };
57362
+ }
57363
+ applyInputInterceptors(paneId, text, source) {
57364
+ return this.applyInterceptors(this.inputInterceptors, "input", { paneId, text, source });
57365
+ }
57366
+ applyOutputInterceptors(paneId, text, source) {
57367
+ return this.applyInterceptors(this.outputInterceptors, "output", { paneId, text, source });
57368
+ }
57369
+ runLifecycleHooks(payload) {
57370
+ this.runHooks(this.lifecycleHooks, "lifecycle", payload);
57371
+ }
57372
+ runRenderHooks(payload) {
57373
+ this.runHooks(this.renderHooks, "render", payload);
57374
+ }
57375
+ applyInterceptors(bucket, kind, payload) {
57376
+ let currentText = payload.text;
57377
+ for (let i3 = 0;i3 < bucket.length; i3 += 1) {
57378
+ const entry = bucket[i3];
57379
+ try {
57380
+ const result = entry.interceptor({ ...payload, text: currentText });
57381
+ if (result === null)
57382
+ return null;
57383
+ if (typeof result === "string")
57384
+ currentText = result;
57385
+ } catch (error) {
57386
+ console.error(`[restty plugin] ${kind} interceptor error (${entry.pluginId}):`, error);
57387
+ }
57388
+ }
57389
+ return currentText;
57390
+ }
57391
+ runHooks(bucket, kind, payload) {
57392
+ for (let i3 = 0;i3 < bucket.length; i3 += 1) {
57393
+ const entry = bucket[i3];
57394
+ try {
57395
+ entry.interceptor(payload);
57396
+ } catch (error) {
57397
+ console.error(`[restty plugin] ${kind} hook error (${entry.pluginId}):`, error);
57398
+ }
57399
+ }
57400
+ }
57401
+ normalizePluginMetadata(plugin, pluginId) {
57402
+ return {
57403
+ ...plugin,
57404
+ id: pluginId,
57405
+ version: plugin.version?.trim?.() || undefined,
57406
+ apiVersion: Number.isFinite(plugin.apiVersion) ? Math.trunc(Number(plugin.apiVersion)) : undefined,
57407
+ requires: plugin.requires ?? undefined
57408
+ };
57409
+ }
57410
+ assertPluginCompatibility(pluginId, plugin) {
57411
+ const version = plugin.version?.trim?.();
57412
+ if (version !== undefined && !version) {
57413
+ throw new Error(`Restty plugin ${pluginId} has an empty version`);
57414
+ }
57415
+ if (plugin.apiVersion !== undefined) {
57416
+ if (!Number.isInteger(plugin.apiVersion) || plugin.apiVersion < 1) {
57417
+ throw new Error(`Restty plugin ${pluginId} has invalid apiVersion ${String(plugin.apiVersion)}`);
57418
+ }
57419
+ if (plugin.apiVersion !== RESTTY_PLUGIN_API_VERSION) {
57420
+ throw new Error(`Restty plugin ${pluginId} requires apiVersion ${plugin.apiVersion}, current is ${RESTTY_PLUGIN_API_VERSION}`);
57421
+ }
57422
+ }
57423
+ const requirement = plugin.requires?.pluginApi;
57424
+ if (requirement === undefined)
57425
+ return;
57426
+ if (typeof requirement === "number") {
57427
+ if (!Number.isInteger(requirement) || requirement < 1) {
57428
+ throw new Error(`Restty plugin ${pluginId} has invalid requires.pluginApi value`);
57429
+ }
57430
+ if (requirement !== RESTTY_PLUGIN_API_VERSION) {
57431
+ throw new Error(`Restty plugin ${pluginId} requires pluginApi ${requirement}, current is ${RESTTY_PLUGIN_API_VERSION}`);
57432
+ }
57433
+ return;
57434
+ }
57435
+ const min = requirement.min;
57436
+ const max = requirement.max;
57437
+ if (!Number.isInteger(min) || min < 1) {
57438
+ throw new Error(`Restty plugin ${pluginId} has invalid requires.pluginApi.min`);
57439
+ }
57440
+ if (max !== undefined && (!Number.isInteger(max) || max < min)) {
57441
+ throw new Error(`Restty plugin ${pluginId} has invalid requires.pluginApi.max`);
57442
+ }
57443
+ if (RESTTY_PLUGIN_API_VERSION < min || max !== undefined && RESTTY_PLUGIN_API_VERSION > max) {
57444
+ const range = max === undefined ? `>=${min}` : `${min}-${max}`;
57445
+ throw new Error(`Restty plugin ${pluginId} requires pluginApi range ${range}, current is ${RESTTY_PLUGIN_API_VERSION}`);
57446
+ }
57447
+ }
57448
+ lookupPluginRegistryEntry(registry, pluginId) {
57449
+ if (registry instanceof Map) {
57450
+ return registry.get(pluginId) ?? null;
57451
+ }
57452
+ if (Object.prototype.hasOwnProperty.call(registry, pluginId)) {
57453
+ return registry[pluginId];
57454
+ }
57455
+ return null;
57456
+ }
57457
+ async resolvePluginRegistryEntry(entry) {
57458
+ if (typeof entry === "function") {
57459
+ return await entry();
57460
+ }
57461
+ return entry;
57462
+ }
57463
+ setPluginLoadError(pluginId, message) {
57464
+ this.pluginDiagnostics.set(pluginId, {
57465
+ id: pluginId,
57466
+ version: null,
57467
+ apiVersion: null,
57468
+ requires: null,
57469
+ active: false,
57470
+ activatedAt: null,
57471
+ lastError: message
57472
+ });
57473
+ }
57474
+ updatePluginDiagnostic(pluginId, patch) {
57475
+ const current = this.pluginDiagnostics.get(pluginId);
57476
+ if (!current)
57477
+ return;
57478
+ this.pluginDiagnostics.set(pluginId, {
57479
+ ...current,
57480
+ ...patch
57481
+ });
57482
+ }
57483
+ buildPluginInfo(pluginId) {
57484
+ const diagnostic = this.pluginDiagnostics.get(pluginId) ?? null;
57485
+ const runtime2 = this.pluginRuntimes.get(pluginId) ?? null;
57486
+ if (!diagnostic && !runtime2)
57487
+ return null;
57488
+ const plugin = runtime2?.plugin;
57489
+ const listeners = runtime2 ? runtime2.disposers.filter((entry) => entry.active && entry.kind === "event").length : 0;
57490
+ const inputInterceptors = runtime2 ? runtime2.disposers.filter((entry) => entry.active && entry.kind === "input-interceptor").length : 0;
57491
+ const outputInterceptors = runtime2 ? runtime2.disposers.filter((entry) => entry.active && entry.kind === "output-interceptor").length : 0;
57492
+ const lifecycleHooks = runtime2 ? runtime2.disposers.filter((entry) => entry.active && entry.kind === "lifecycle-hook").length : 0;
57493
+ const renderHooks = runtime2 ? runtime2.disposers.filter((entry) => entry.active && entry.kind === "render-hook").length : 0;
57494
+ return {
57495
+ id: pluginId,
57496
+ version: plugin?.version?.trim?.() || diagnostic?.version || null,
57497
+ apiVersion: plugin?.apiVersion ?? (Number.isFinite(diagnostic?.apiVersion) ? diagnostic?.apiVersion : null),
57498
+ requires: plugin?.requires ?? diagnostic?.requires ?? null,
57499
+ active: runtime2 ? true : diagnostic?.active ?? false,
57500
+ activatedAt: runtime2?.activatedAt ?? diagnostic?.activatedAt ?? null,
57501
+ lastError: diagnostic?.lastError ?? null,
57502
+ listeners,
57503
+ inputInterceptors,
57504
+ outputInterceptors,
57505
+ lifecycleHooks,
57506
+ renderHooks
57507
+ };
57508
+ }
57509
+ errorToMessage(error) {
57510
+ if (error instanceof Error)
57511
+ return error.message || error.name || "Unknown error";
57512
+ return String(error);
57513
+ }
57514
+ onPluginEvent(event, listener) {
57515
+ let listeners = this.pluginListeners.get(event);
57516
+ if (!listeners) {
57517
+ listeners = new Set;
57518
+ this.pluginListeners.set(event, listeners);
57519
+ }
57520
+ const wrapped = listener;
57521
+ listeners.add(wrapped);
57522
+ return () => {
57523
+ const current = this.pluginListeners.get(event);
57524
+ if (!current)
57525
+ return;
57526
+ current.delete(wrapped);
57527
+ if (current.size === 0) {
57528
+ this.pluginListeners.delete(event);
57529
+ }
57530
+ };
57531
+ }
57532
+ emitPluginEvent(event, payload) {
57533
+ const listeners = this.pluginListeners.get(event);
57534
+ if (!listeners || listeners.size === 0)
57535
+ return;
57536
+ const snapshot = Array.from(listeners);
57537
+ for (let i3 = 0;i3 < snapshot.length; i3 += 1) {
57538
+ try {
57539
+ snapshot[i3](payload);
57540
+ } catch (error) {
57541
+ console.error(`[restty plugin] listener error (${String(event)}):`, error);
57542
+ }
57543
+ }
57544
+ }
57545
+ teardownPluginRuntime(runtime2) {
57546
+ for (let i3 = 0;i3 < runtime2.disposers.length; i3 += 1) {
57547
+ try {
57548
+ runtime2.disposers[i3].dispose();
57549
+ } catch {}
57550
+ }
57551
+ runtime2.disposers.length = 0;
57552
+ const cleanup = runtime2.cleanup;
57553
+ runtime2.cleanup = null;
57554
+ if (!cleanup)
57555
+ return;
57556
+ try {
57557
+ cleanup();
57558
+ } catch (error) {
57559
+ console.error(`[restty plugin] cleanup error (${runtime2.plugin.id}):`, error);
57560
+ }
57561
+ }
57562
+ normalizePluginCleanup(cleanup) {
57563
+ if (!cleanup)
57564
+ return null;
57565
+ if (typeof cleanup === "function")
57566
+ return cleanup;
57567
+ if (typeof cleanup === "object" && typeof cleanup.dispose === "function") {
57568
+ return () => cleanup.dispose();
57569
+ }
57570
+ return null;
57571
+ }
56764
57572
  }
56765
57573
  function createRestty(options) {
56766
57574
  return new Restty(options);
56767
57575
  }
56768
57576
 
56769
- export { BOX_LINE_MAP, BOX_STYLE_NONE, BOX_STYLE_LIGHT, BOX_STYLE_HEAVY, BOX_STYLE_DOUBLE, isPrivateUse, isSpaceCp, isBoxDrawing, isBlockElement, isLegacyComputing, isPowerline, isBraille, isGraphicsElement, isSymbolCp, applyAlpha, pushRect, pushRectSnapped, pushRectBox, drawBlockElement, drawBoxDrawing, drawBraille, drawPowerline, constrainGlyphBox, RECT_SHADER, GLYPH_SHADER, initWebGPUCore, initWebGPU, initWebGL, ensureInstanceBuffer, configureContext, ensureGLInstanceBuffer, createResizeState, createScrollbarState, ResttyWasm, loadResttyWasm, createInputHandler, NERD_SYMBOL_RANGES, isNerdSymbolCodepoint, fontHeightUnits, computeCellMetrics, createGridState, updateGridState, clamp, isSymbolFont, isNerdSymbolFont, fontMaxCellSpan, fontScaleOverride, fontRasterScale, createFontEntry, resetFontEntry, createFontManagerState, fontHasGlyph, fontAdvanceUnits, glyphWidthUnits, pickFontIndexForText, tryFetchFontBuffer, tryLocalFontBuffer, loadPrimaryFontBuffer, loadFallbackFontBuffers, NERD_CONSTRAINTS, getNerdConstraint, createPtyConnection, connectPty, disconnectPty, sendPtyInput, sendPtyResize, isPtyConnected, createWebSocketPtyTransport, parseGhosttyColor, colorToFloats, colorToRgbU32, parseGhosttyTheme, listBuiltinThemeNames, isBuiltinThemeName, getBuiltinThemeSource, getBuiltinTheme, createImeState, setPreedit, clearPreedit, startComposition, updateComposition, endComposition, syncImeSelection, updateImePosition, PREEDIT_BG, PREEDIT_ACTIVE_BG, PREEDIT_FG, PREEDIT_UL, PREEDIT_CARET, ResttyPaneHandle, Restty, createRestty };
57577
+ export { BOX_LINE_MAP, BOX_STYLE_NONE, BOX_STYLE_LIGHT, BOX_STYLE_HEAVY, BOX_STYLE_DOUBLE, isPrivateUse, isSpaceCp, isBoxDrawing, isBlockElement, isLegacyComputing, isPowerline, isBraille, isGraphicsElement, isSymbolCp, applyAlpha, pushRect, pushRectSnapped, pushRectBox, drawBlockElement, drawBoxDrawing, drawBraille, drawPowerline, constrainGlyphBox, RECT_SHADER, GLYPH_SHADER, initWebGPUCore, initWebGPU, initWebGL, ensureInstanceBuffer, configureContext, ensureGLInstanceBuffer, createResizeState, createScrollbarState, ResttyWasm, loadResttyWasm, createInputHandler, NERD_SYMBOL_RANGES, isNerdSymbolCodepoint, fontHeightUnits, computeCellMetrics, createGridState, updateGridState, clamp, isSymbolFont, isNerdSymbolFont, fontMaxCellSpan, fontScaleOverride, fontRasterScale, createFontEntry, resetFontEntry, createFontManagerState, fontHasGlyph, fontAdvanceUnits, glyphWidthUnits, pickFontIndexForText, tryFetchFontBuffer, tryLocalFontBuffer, loadPrimaryFontBuffer, loadFallbackFontBuffers, NERD_CONSTRAINTS, getNerdConstraint, createPtyConnection, connectPty, disconnectPty, sendPtyInput, sendPtyResize, isPtyConnected, createWebSocketPtyTransport, parseGhosttyColor, colorToFloats, colorToRgbU32, parseGhosttyTheme, listBuiltinThemeNames, isBuiltinThemeName, getBuiltinThemeSource, getBuiltinTheme, createImeState, setPreedit, clearPreedit, startComposition, updateComposition, endComposition, syncImeSelection, updateImePosition, PREEDIT_BG, PREEDIT_ACTIVE_BG, PREEDIT_FG, PREEDIT_UL, PREEDIT_CARET, RESTTY_PLUGIN_API_VERSION, ResttyPaneHandle, Restty, createRestty };