restty 0.1.8 → 0.1.9

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
@@ -109,6 +109,7 @@ await restty.setFontSources([
109
109
 
110
110
  `restty` ships a pane manager and a default terminal context menu so you can add split panes quickly.
111
111
  It auto-creates pane DOM, canvas, and hidden IME inputs for you.
112
+ Pane/split/divider styles are injected by the library (no separate CSS file required).
112
113
 
113
114
  ```ts
114
115
  import {
@@ -131,6 +132,10 @@ const restty = new Restty({
131
132
  defaultContextMenu: {
132
133
  getPtyUrl: () => "ws://localhost:8787/pty",
133
134
  },
135
+ paneStyles: {
136
+ inactivePaneOpacity: 0.82,
137
+ dividerThicknessPx: 1,
138
+ },
134
139
  shortcuts: true,
135
140
  });
136
141
 
@@ -150,6 +155,7 @@ Main methods:
150
155
  - `destroy()`
151
156
  - `getPanes()` / `getActivePane()` / `getFocusedPane()`
152
157
  - `splitActivePane("vertical" | "horizontal")` / `splitPane(id, direction)` / `closePane(id)`
158
+ - `getPaneStyleOptions()` / `setPaneStyleOptions({...})`
153
159
  - `connectPty(url)` / `disconnectPty()` / `isPtyConnected()`
154
160
  - `setRenderer("auto" | "webgpu" | "webgl2")`
155
161
  - `setFontSize(number)`
@@ -3,6 +3,6 @@ export { createResttyAppSession, getDefaultResttyAppSession } from "./session";
3
3
  export { createResttyPaneManager, createDefaultResttyPaneContextMenuItems, getResttyShortcutModifierLabel, } from "./panes";
4
4
  export { Restty } from "./restty";
5
5
  export type { ResttyAppElements, ResttyAppCallbacks, FontSource, ResttyFontSource, ResttyUrlFontSource, ResttyBufferFontSource, ResttyLocalFontSource, ResttyWasmLogListener, ResttyAppSession, ResttyAppOptions, ResttyApp, } from "./types";
6
- export type { ResttyPaneSplitDirection, ResttyPaneContextMenuItem, ResttyPaneDefinition, ResttyPaneShortcutsOptions, ResttyPaneContextMenuOptions, CreateResttyPaneManagerOptions, ResttyPaneManager, ResttyPaneWithApp, CreateDefaultResttyPaneContextMenuItemsOptions, } from "./panes";
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;
@@ -1,4 +1,4 @@
1
- import { type ResttyPaneContextMenuOptions, type ResttyPaneManager, type ResttyPaneShortcutsOptions, type ResttyPaneWithApp } from "./panes";
1
+ import { type ResttyPaneStyleOptions, type ResttyPaneStylesOptions, type ResttyPaneContextMenuOptions, type ResttyPaneManager, type ResttyPaneShortcutsOptions, type ResttyPaneWithApp } from "./panes";
2
2
  import type { ResttyAppOptions, ResttyAppSession } from "./types";
3
3
  export type ResttyManagedAppPane = ResttyPaneWithApp & {
4
4
  canvas: HTMLCanvasElement;
@@ -11,6 +11,8 @@ export type ResttyPaneDomDefaults = {
11
11
  imeInputClassName?: string;
12
12
  termDebugClassName?: string;
13
13
  };
14
+ export type ResttyManagedPaneStyleOptions = ResttyPaneStyleOptions;
15
+ export type ResttyManagedPaneStylesOptions = ResttyPaneStylesOptions;
14
16
  export type ResttyPaneAppOptionsInput = Omit<ResttyAppOptions, "canvas" | "imeInput" | "session">;
15
17
  export type ResttyDefaultPaneContextMenuOptions = {
16
18
  enabled?: boolean;
@@ -31,6 +33,7 @@ export type CreateResttyAppPaneManagerOptions = {
31
33
  paneDom?: ResttyPaneDomDefaults;
32
34
  autoInit?: boolean;
33
35
  minPaneSize?: number;
36
+ paneStyles?: boolean | ResttyManagedPaneStylesOptions;
34
37
  shortcuts?: boolean | ResttyPaneShortcutsOptions;
35
38
  contextMenu?: ResttyPaneContextMenuOptions<ResttyManagedAppPane> | null;
36
39
  defaultContextMenu?: boolean | ResttyDefaultPaneContextMenuOptions;
@@ -21,6 +21,17 @@ export type ResttyPaneContextMenuOptions<TPane extends ResttyPaneDefinition> = {
21
21
  canOpen?: (event: MouseEvent, pane: TPane) => boolean;
22
22
  getItems: (pane: TPane, manager: ResttyPaneManager<TPane>) => Array<ResttyPaneContextMenuItem | "separator">;
23
23
  };
24
+ export type ResttyPaneStyleOptions = {
25
+ splitBackground?: string;
26
+ paneBackground?: string;
27
+ inactivePaneOpacity?: number;
28
+ activePaneOpacity?: number;
29
+ opacityTransitionMs?: number;
30
+ dividerThicknessPx?: number;
31
+ };
32
+ export type ResttyPaneStylesOptions = ResttyPaneStyleOptions & {
33
+ enabled?: boolean;
34
+ };
24
35
  export type CreateResttyPaneManagerOptions<TPane extends ResttyPaneDefinition> = {
25
36
  root: HTMLElement;
26
37
  createPane: (context: {
@@ -37,6 +48,7 @@ export type CreateResttyPaneManagerOptions<TPane extends ResttyPaneDefinition> =
37
48
  minPaneSize?: number;
38
49
  contextMenu?: ResttyPaneContextMenuOptions<TPane> | null;
39
50
  shortcuts?: boolean | ResttyPaneShortcutsOptions;
51
+ styles?: boolean | ResttyPaneStylesOptions;
40
52
  };
41
53
  export type ResttyPaneManager<TPane extends ResttyPaneDefinition> = {
42
54
  getPanes: () => TPane[];
@@ -55,6 +67,8 @@ export type ResttyPaneManager<TPane extends ResttyPaneDefinition> = {
55
67
  splitPane: (id: number, direction: ResttyPaneSplitDirection) => TPane | null;
56
68
  splitActivePane: (direction: ResttyPaneSplitDirection) => TPane | null;
57
69
  closePane: (id: number) => boolean;
70
+ getStyleOptions: () => Readonly<Required<ResttyPaneStyleOptions>>;
71
+ setStyleOptions: (options: ResttyPaneStyleOptions) => void;
58
72
  requestLayoutSync: () => void;
59
73
  hideContextMenu: () => void;
60
74
  destroy: () => void;
@@ -1,6 +1,6 @@
1
1
  import type { GhosttyTheme } from "../theme";
2
2
  import type { InputHandler } from "../input";
3
- import { type CreateResttyAppPaneManagerOptions, type ResttyPaneAppOptionsInput, type ResttyManagedAppPane } from "./pane-app-manager";
3
+ import { type CreateResttyAppPaneManagerOptions, type ResttyManagedPaneStyleOptions, type ResttyPaneAppOptionsInput, type ResttyManagedAppPane } from "./pane-app-manager";
4
4
  import type { ResttyPaneManager, ResttyPaneSplitDirection } from "./panes";
5
5
  import type { ResttyFontSource } from "./types";
6
6
  export type ResttyOptions = Omit<CreateResttyAppPaneManagerOptions, "appOptions"> & {
@@ -24,6 +24,8 @@ export declare class Restty {
24
24
  splitActivePane(direction: ResttyPaneSplitDirection): ResttyManagedAppPane | null;
25
25
  splitPane(id: number, direction: ResttyPaneSplitDirection): ResttyManagedAppPane | null;
26
26
  closePane(id: number): boolean;
27
+ getPaneStyleOptions(): Readonly<Required<ResttyManagedPaneStyleOptions>>;
28
+ setPaneStyleOptions(options: ResttyManagedPaneStyleOptions): void;
27
29
  setActivePane(id: number, options?: {
28
30
  focus?: boolean;
29
31
  }): void;
package/dist/index.d.ts CHANGED
@@ -14,5 +14,5 @@ export { parseGhosttyTheme, parseGhosttyColor, colorToFloats, colorToRgbU32, lis
14
14
  export type { GhosttyTheme, ThemeColor, ResttyBuiltinThemeName } from "./theme";
15
15
  export { Restty } from "./app/restty";
16
16
  export type { ResttyOptions } from "./app/restty";
17
- export type { ResttyManagedAppPane, ResttyPaneDomDefaults, ResttyPaneAppOptionsInput, } from "./app/pane-app-manager";
17
+ export type { ResttyManagedAppPane, ResttyPaneDomDefaults, ResttyManagedPaneStyleOptions, ResttyManagedPaneStylesOptions, ResttyPaneAppOptionsInput, } from "./app/pane-app-manager";
18
18
  export type { ResttyFontSource, ResttyUrlFontSource, ResttyBufferFontSource, ResttyLocalFontSource, } from "./app/types";
package/dist/index.js CHANGED
@@ -25464,6 +25464,230 @@ function getBuiltinTheme(name) {
25464
25464
  return parsed;
25465
25465
  }
25466
25466
  // src/app/panes.ts
25467
+ var RESTTY_PANE_ROOT_CLASS = "restty-pane-root";
25468
+ var RESTTY_PANE_STYLE_MARKER = "data-restty-pane-styles";
25469
+ var RESTTY_PANE_STYLE_TEXT = `
25470
+ .${RESTTY_PANE_ROOT_CLASS} {
25471
+ display: flex;
25472
+ width: 100%;
25473
+ height: 100%;
25474
+ min-width: 0;
25475
+ min-height: 0;
25476
+ }
25477
+
25478
+ .${RESTTY_PANE_ROOT_CLASS} .pane-split {
25479
+ display: flex;
25480
+ flex: 1 1 auto;
25481
+ min-width: 0;
25482
+ min-height: 0;
25483
+ gap: 0;
25484
+ padding: 0;
25485
+ background: var(--restty-pane-split-background, #111);
25486
+ }
25487
+
25488
+ .${RESTTY_PANE_ROOT_CLASS} .pane-split.is-vertical {
25489
+ flex-direction: row;
25490
+ }
25491
+
25492
+ .${RESTTY_PANE_ROOT_CLASS} .pane-split.is-horizontal {
25493
+ flex-direction: column;
25494
+ }
25495
+
25496
+ .${RESTTY_PANE_ROOT_CLASS} .pane {
25497
+ position: relative;
25498
+ flex: 1 1 0;
25499
+ min-width: 0;
25500
+ min-height: 0;
25501
+ background: var(--restty-pane-background, #000);
25502
+ border: 0;
25503
+ overflow: hidden;
25504
+ opacity: var(--restty-pane-inactive-opacity, 0.82);
25505
+ transition: opacity var(--restty-pane-opacity-transition, 140ms) ease-out;
25506
+ }
25507
+
25508
+ .${RESTTY_PANE_ROOT_CLASS} .pane.is-active {
25509
+ opacity: var(--restty-pane-active-opacity, 1);
25510
+ }
25511
+
25512
+ .${RESTTY_PANE_ROOT_CLASS} .pane-divider {
25513
+ position: relative;
25514
+ z-index: 2;
25515
+ flex: 0 0 var(--restty-pane-divider-thickness, 1px);
25516
+ touch-action: none;
25517
+ }
25518
+
25519
+ .${RESTTY_PANE_ROOT_CLASS} .pane-divider.is-vertical {
25520
+ cursor: col-resize;
25521
+ background: transparent;
25522
+ }
25523
+
25524
+ .${RESTTY_PANE_ROOT_CLASS} .pane-divider.is-horizontal {
25525
+ cursor: row-resize;
25526
+ background: transparent;
25527
+ }
25528
+
25529
+ .${RESTTY_PANE_ROOT_CLASS} .pane-divider.is-vertical:hover,
25530
+ .${RESTTY_PANE_ROOT_CLASS} .pane-divider.is-vertical.is-dragging {
25531
+ background:
25532
+ radial-gradient(
25533
+ 100px 46% at 50% 50%,
25534
+ rgba(235, 235, 235, 0.92) 0%,
25535
+ rgba(200, 200, 200, 0.48) 46%,
25536
+ rgba(155, 155, 155, 0.12) 68%,
25537
+ rgba(120, 120, 120, 0) 100%
25538
+ ),
25539
+ rgba(185, 185, 185, 0.24);
25540
+ }
25541
+
25542
+ .${RESTTY_PANE_ROOT_CLASS} .pane-divider.is-horizontal:hover,
25543
+ .${RESTTY_PANE_ROOT_CLASS} .pane-divider.is-horizontal.is-dragging {
25544
+ background:
25545
+ radial-gradient(
25546
+ 46% 100px at 50% 50%,
25547
+ rgba(235, 235, 235, 0.92) 0%,
25548
+ rgba(200, 200, 200, 0.48) 46%,
25549
+ rgba(155, 155, 155, 0.12) 68%,
25550
+ rgba(120, 120, 120, 0) 100%
25551
+ ),
25552
+ rgba(185, 185, 185, 0.24);
25553
+ }
25554
+
25555
+ body.is-resizing-split {
25556
+ user-select: none;
25557
+ }
25558
+
25559
+ .${RESTTY_PANE_ROOT_CLASS} .pane-canvas {
25560
+ width: 100%;
25561
+ height: 100%;
25562
+ display: block;
25563
+ outline: none;
25564
+ }
25565
+
25566
+ .${RESTTY_PANE_ROOT_CLASS} .pane-ime-input {
25567
+ position: fixed;
25568
+ left: 0;
25569
+ top: 0;
25570
+ width: 1px;
25571
+ height: 1px;
25572
+ opacity: 0;
25573
+ pointer-events: none;
25574
+ }
25575
+
25576
+ .${RESTTY_PANE_ROOT_CLASS} .pane-term-debug {
25577
+ display: none;
25578
+ }
25579
+
25580
+ .pane-context-menu {
25581
+ position: fixed;
25582
+ z-index: 9999;
25583
+ min-width: 200px;
25584
+ padding: 6px;
25585
+ border: 1px solid #2a2a2a;
25586
+ border-radius: 8px;
25587
+ background: #161616;
25588
+ box-shadow: 0 14px 40px rgba(0, 0, 0, 0.45);
25589
+ }
25590
+
25591
+ .pane-context-menu-item {
25592
+ width: 100%;
25593
+ display: flex;
25594
+ align-items: center;
25595
+ justify-content: space-between;
25596
+ gap: 12px;
25597
+ padding: 7px 9px;
25598
+ border: 0;
25599
+ border-radius: 6px;
25600
+ background: transparent;
25601
+ color: #d6d6d6;
25602
+ text-align: left;
25603
+ cursor: pointer;
25604
+ }
25605
+
25606
+ .pane-context-menu-item:hover {
25607
+ background: #252525;
25608
+ }
25609
+
25610
+ .pane-context-menu-item:disabled {
25611
+ opacity: 0.4;
25612
+ cursor: default;
25613
+ }
25614
+
25615
+ .pane-context-menu-item.is-danger {
25616
+ color: #f1a1a1;
25617
+ }
25618
+
25619
+ .pane-context-menu-label {
25620
+ font-size: 12px;
25621
+ }
25622
+
25623
+ .pane-context-menu-shortcut {
25624
+ font-size: 10px;
25625
+ color: #868686;
25626
+ }
25627
+
25628
+ .pane-context-menu-separator {
25629
+ height: 1px;
25630
+ margin: 6px 4px;
25631
+ background: #2a2a2a;
25632
+ }
25633
+ `;
25634
+ var DEFAULT_RESTTY_PANE_STYLE_OPTIONS = {
25635
+ splitBackground: "#111",
25636
+ paneBackground: "#000",
25637
+ inactivePaneOpacity: 0.82,
25638
+ activePaneOpacity: 1,
25639
+ opacityTransitionMs: 140,
25640
+ dividerThicknessPx: 1
25641
+ };
25642
+ function clampNumber(value, min, max) {
25643
+ return Math.min(max, Math.max(min, value));
25644
+ }
25645
+ function normalizeColor(value, fallback) {
25646
+ if (typeof value !== "string")
25647
+ return fallback;
25648
+ const trimmed = value.trim();
25649
+ return trimmed ? trimmed : fallback;
25650
+ }
25651
+ function normalizePaneStyleOptions(options) {
25652
+ const inactivePaneOpacity = Number.isFinite(options.inactivePaneOpacity) ? clampNumber(Number(options.inactivePaneOpacity), 0, 1) : DEFAULT_RESTTY_PANE_STYLE_OPTIONS.inactivePaneOpacity;
25653
+ const activePaneOpacity = Number.isFinite(options.activePaneOpacity) ? clampNumber(Number(options.activePaneOpacity), 0, 1) : DEFAULT_RESTTY_PANE_STYLE_OPTIONS.activePaneOpacity;
25654
+ const opacityTransitionMs = Number.isFinite(options.opacityTransitionMs) ? clampNumber(Number(options.opacityTransitionMs), 0, 5000) : DEFAULT_RESTTY_PANE_STYLE_OPTIONS.opacityTransitionMs;
25655
+ const dividerThicknessPx = Number.isFinite(options.dividerThicknessPx) ? clampNumber(Number(options.dividerThicknessPx), 1, 32) : DEFAULT_RESTTY_PANE_STYLE_OPTIONS.dividerThicknessPx;
25656
+ return {
25657
+ splitBackground: normalizeColor(options.splitBackground, DEFAULT_RESTTY_PANE_STYLE_OPTIONS.splitBackground),
25658
+ paneBackground: normalizeColor(options.paneBackground, DEFAULT_RESTTY_PANE_STYLE_OPTIONS.paneBackground),
25659
+ inactivePaneOpacity,
25660
+ activePaneOpacity,
25661
+ opacityTransitionMs,
25662
+ dividerThicknessPx
25663
+ };
25664
+ }
25665
+ function ensureResttyPaneStylesDocument(doc) {
25666
+ if (doc.querySelector(`style[${RESTTY_PANE_STYLE_MARKER}="1"]`))
25667
+ return;
25668
+ const style = doc.createElement("style");
25669
+ style.setAttribute(RESTTY_PANE_STYLE_MARKER, "1");
25670
+ style.textContent = RESTTY_PANE_STYLE_TEXT;
25671
+ doc.head.appendChild(style);
25672
+ }
25673
+ function applyPaneStyleOptionsToRoot(root, options) {
25674
+ root.classList.add(RESTTY_PANE_ROOT_CLASS);
25675
+ root.style.setProperty("--restty-pane-split-background", options.splitBackground);
25676
+ root.style.setProperty("--restty-pane-background", options.paneBackground);
25677
+ root.style.setProperty("--restty-pane-inactive-opacity", options.inactivePaneOpacity.toFixed(3));
25678
+ root.style.setProperty("--restty-pane-active-opacity", options.activePaneOpacity.toFixed(3));
25679
+ root.style.setProperty("--restty-pane-opacity-transition", `${options.opacityTransitionMs}ms`);
25680
+ root.style.setProperty("--restty-pane-divider-thickness", `${options.dividerThicknessPx}px`);
25681
+ }
25682
+ function clearPaneStyleOptionsFromRoot(root) {
25683
+ root.classList.remove(RESTTY_PANE_ROOT_CLASS);
25684
+ root.style.removeProperty("--restty-pane-split-background");
25685
+ root.style.removeProperty("--restty-pane-background");
25686
+ root.style.removeProperty("--restty-pane-inactive-opacity");
25687
+ root.style.removeProperty("--restty-pane-active-opacity");
25688
+ root.style.removeProperty("--restty-pane-opacity-transition");
25689
+ root.style.removeProperty("--restty-pane-divider-thickness");
25690
+ }
25467
25691
  function getResttyShortcutModifierLabel() {
25468
25692
  const isMac = typeof navigator !== "undefined" && /mac/i.test(navigator.platform);
25469
25693
  return isMac ? "Cmd" : "Ctrl";
@@ -25550,6 +25774,17 @@ function createResttyPaneManager(options) {
25550
25774
  const paneCleanupFns = new Map;
25551
25775
  const minPaneSize = Number.isFinite(options.minPaneSize) ? Math.max(24, Number(options.minPaneSize)) : 96;
25552
25776
  const shortcutOptions = typeof options.shortcuts === "object" ? options.shortcuts : { enabled: options.shortcuts !== false };
25777
+ const stylesInput = typeof options.styles === "object" && options.styles ? options.styles : undefined;
25778
+ const stylesEnabled = options.styles === false ? false : stylesInput?.enabled ?? true;
25779
+ let styleOptions = normalizePaneStyleOptions({
25780
+ ...DEFAULT_RESTTY_PANE_STYLE_OPTIONS,
25781
+ ...stylesInput
25782
+ });
25783
+ if (stylesEnabled) {
25784
+ const doc = root.ownerDocument ?? document;
25785
+ ensureResttyPaneStylesDocument(doc);
25786
+ applyPaneStyleOptionsToRoot(root, styleOptions);
25787
+ }
25553
25788
  let nextPaneId = 1;
25554
25789
  let activePaneId = null;
25555
25790
  let focusedPaneId = null;
@@ -25569,6 +25804,18 @@ function createResttyPaneManager(options) {
25569
25804
  options.onLayoutChanged?.();
25570
25805
  });
25571
25806
  };
25807
+ const getStyleOptions = () => ({
25808
+ ...styleOptions
25809
+ });
25810
+ const setStyleOptions = (next) => {
25811
+ styleOptions = normalizePaneStyleOptions({
25812
+ ...styleOptions,
25813
+ ...next
25814
+ });
25815
+ if (!stylesEnabled)
25816
+ return;
25817
+ applyPaneStyleOptionsToRoot(root, styleOptions);
25818
+ };
25572
25819
  const getPanes = () => Array.from(panes.values());
25573
25820
  const getPaneById = (id) => {
25574
25821
  return panes.get(id) ?? null;
@@ -26004,6 +26251,9 @@ function createResttyPaneManager(options) {
26004
26251
  root.replaceChildren();
26005
26252
  hideContextMenu();
26006
26253
  contextMenuEl?.remove();
26254
+ if (stylesEnabled) {
26255
+ clearPaneStyleOptionsFromRoot(root);
26256
+ }
26007
26257
  };
26008
26258
  const api = {
26009
26259
  getPanes,
@@ -26016,6 +26266,8 @@ function createResttyPaneManager(options) {
26016
26266
  splitPane,
26017
26267
  splitActivePane,
26018
26268
  closePane,
26269
+ getStyleOptions,
26270
+ setStyleOptions,
26019
26271
  requestLayoutSync,
26020
26272
  hideContextMenu,
26021
26273
  destroy
@@ -55127,6 +55379,7 @@ function createResttyAppPaneManager(options) {
55127
55379
  const manager2 = createResttyPaneManager({
55128
55380
  root: options.root,
55129
55381
  minPaneSize: options.minPaneSize,
55382
+ styles: options.paneStyles,
55130
55383
  shortcuts,
55131
55384
  contextMenu,
55132
55385
  createPane: ({ id, sourcePane }) => {
@@ -55228,6 +55481,12 @@ class Restty {
55228
55481
  closePane(id) {
55229
55482
  return this.paneManager.closePane(id);
55230
55483
  }
55484
+ getPaneStyleOptions() {
55485
+ return this.paneManager.getStyleOptions();
55486
+ }
55487
+ setPaneStyleOptions(options) {
55488
+ this.paneManager.setStyleOptions(options);
55489
+ }
55231
55490
  setActivePane(id, options) {
55232
55491
  this.paneManager.setActivePane(id, options);
55233
55492
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "restty",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "Browser terminal rendering library powered by WASM, WebGPU/WebGL2, and TypeScript text shaping.",
5
5
  "keywords": [
6
6
  "terminal",