restty 0.1.35 → 0.2.0

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.
Files changed (186) hide show
  1. package/README.md +49 -343
  2. package/dist/chunk-30px21q2.js +0 -0
  3. package/dist/{chunk-zqscavsh.js → chunk-db9dt7pr.js} +48146 -63236
  4. package/dist/chunk-s26trws6.js +9136 -0
  5. package/dist/chunk-wdme6j9c.js +1776 -0
  6. package/dist/chunk-xykzfd81.js +0 -0
  7. package/dist/chunk-y6hfk43b.js +1690 -0
  8. package/dist/fonts/local-font-access.types.d.ts +19 -0
  9. package/dist/fonts/manager/entries.d.ts +7 -0
  10. package/dist/headless.d.ts +102 -0
  11. package/dist/headless.js +350 -0
  12. package/dist/index.d.ts +8 -4
  13. package/dist/input/keymap/default-mapping.d.ts +1 -0
  14. package/dist/input/mouse.d.ts +13 -0
  15. package/dist/input/types.d.ts +10 -1
  16. package/dist/internal/runtime.d.ts +4 -0
  17. package/dist/internal/runtime.js +12 -0
  18. package/dist/internal/surface.d.ts +6 -0
  19. package/dist/internal/surface.js +15 -0
  20. package/dist/internal.d.ts +2 -5
  21. package/dist/internal.js +23 -5
  22. package/dist/restty.esm.js +32 -11368
  23. package/dist/restty.js +8 -2
  24. package/dist/runtime/core/api.d.ts +112 -0
  25. package/dist/runtime/core/config.d.ts +133 -0
  26. package/dist/runtime/core/lifecycle.d.ts +1 -0
  27. package/dist/runtime/core/models.d.ts +124 -0
  28. package/dist/runtime/core/resources.d.ts +50 -0
  29. package/dist/runtime/core/runtime-events.d.ts +28 -0
  30. package/dist/runtime/core/session.d.ts +9 -0
  31. package/dist/runtime/{codepoint-utils.d.ts → create-runtime/codepoint-utils.d.ts} +1 -1
  32. package/dist/runtime/create-runtime/{color-glyph-atlas.d.ts → font-runtime/color-glyph-atlas.d.ts} +2 -2
  33. package/dist/runtime/create-runtime/font-runtime/grid.d.ts +6 -0
  34. package/dist/runtime/create-runtime/font-runtime/grid.types.d.ts +22 -0
  35. package/dist/runtime/create-runtime/font-runtime/index.d.ts +15 -0
  36. package/dist/runtime/create-runtime/font-runtime/text.d.ts +12 -0
  37. package/dist/runtime/create-runtime/font-runtime/text.types.d.ts +10 -0
  38. package/dist/runtime/create-runtime/{font-runtime-helpers.types.d.ts → font-runtime/types.d.ts} +5 -8
  39. package/dist/runtime/create-runtime/font-runtime/webgpu-atlas.d.ts +7 -0
  40. package/dist/runtime/create-runtime/font-runtime/webgpu-atlas.types.d.ts +23 -0
  41. package/dist/runtime/create-runtime/highlight-terminal-color-utils.d.ts +1 -8
  42. package/dist/runtime/create-runtime/highlight-terminal-color-utils.types.d.ts +9 -0
  43. package/dist/runtime/create-runtime/input-hooks.d.ts +2 -12
  44. package/dist/runtime/create-runtime/input-hooks.types.d.ts +10 -0
  45. package/dist/runtime/create-runtime/interaction-runtime/bind-ime-events.d.ts +2 -1
  46. package/dist/runtime/create-runtime/interaction-runtime/bind-pointer-aux-handlers.d.ts +1 -1
  47. package/dist/runtime/create-runtime/interaction-runtime/bind-pointer-events.d.ts +2 -1
  48. package/dist/runtime/create-runtime/interaction-runtime/bind-pointer-up-handler.d.ts +1 -1
  49. package/dist/runtime/create-runtime/{interaction-runtime.d.ts → interaction-runtime/index.d.ts} +3 -2
  50. package/dist/runtime/create-runtime/interaction-runtime/kitty-image-cache.d.ts +2 -17
  51. package/dist/runtime/create-runtime/interaction-runtime/kitty-image-cache.types.d.ts +17 -0
  52. package/dist/runtime/create-runtime/interaction-runtime/{types.d.ts → runtime.types.d.ts} +1 -47
  53. package/dist/runtime/create-runtime/interaction-runtime/scrollbar-runtime.d.ts +1 -23
  54. package/dist/runtime/create-runtime/interaction-runtime/scrollbar-runtime.types.d.ts +23 -0
  55. package/dist/runtime/create-runtime/interaction-runtime/state.types.d.ts +47 -0
  56. package/dist/runtime/create-runtime/kitty-render-runtime.d.ts +2 -36
  57. package/dist/runtime/create-runtime/kitty-render-runtime.types.d.ts +33 -0
  58. package/dist/runtime/create-runtime/{lifecycle-theme-size-canvas.d.ts → lifecycle-theme-size/canvas.d.ts} +1 -1
  59. package/dist/runtime/create-runtime/{lifecycle-theme-size.d.ts → lifecycle-theme-size/index.d.ts} +3 -3
  60. package/dist/runtime/create-runtime/lifecycle-theme-size/theme.d.ts +6 -0
  61. package/dist/runtime/create-runtime/{lifecycle-theme-size.types.d.ts → lifecycle-theme-size/types.d.ts} +1 -6
  62. package/dist/runtime/{max-scrollback.d.ts → create-runtime/max-scrollback.d.ts} +2 -5
  63. package/dist/runtime/create-runtime/max-scrollback.types.d.ts +4 -0
  64. package/dist/runtime/create-runtime/native-scrollbar-host.d.ts +2 -15
  65. package/dist/runtime/create-runtime/native-scrollbar-host.types.d.ts +13 -0
  66. package/dist/runtime/{overlay-scrollbar.d.ts → create-runtime/overlay-scrollbar.d.ts} +1 -12
  67. package/dist/runtime/create-runtime/overlay-scrollbar.types.d.ts +12 -0
  68. package/dist/runtime/create-runtime/pty-input-runtime.d.ts +2 -46
  69. package/dist/runtime/create-runtime/pty-input-runtime.types.d.ts +41 -0
  70. package/dist/runtime/{render-color-utils.d.ts → create-runtime/render-color-utils.d.ts} +1 -1
  71. package/dist/runtime/{render-stage-runtime.d.ts → create-runtime/render-stage-runtime.d.ts} +2 -2
  72. package/dist/runtime/{create-app-types.d.ts → create-runtime/render-stage-runtime.types.d.ts} +1 -29
  73. package/dist/runtime/create-runtime/render-tick-webgl-overlays.d.ts +1 -1
  74. package/dist/runtime/create-runtime/render-tick-webgl-scene.d.ts +1 -1
  75. package/dist/runtime/create-runtime/render-tick-webgl.types.d.ts +2 -5
  76. package/dist/runtime/create-runtime/render-tick-webgpu-cell-pass.d.ts +1 -1
  77. package/dist/runtime/create-runtime/render-tick-webgpu.types.d.ts +5 -9
  78. package/dist/runtime/create-runtime/runtime-controller.api.types.d.ts +96 -0
  79. package/dist/runtime/create-runtime/runtime-controller.clipboard.d.ts +10 -0
  80. package/dist/runtime/create-runtime/runtime-controller.d.ts +3 -0
  81. package/dist/runtime/create-runtime/runtime-controller.input.d.ts +24 -0
  82. package/dist/runtime/create-runtime/runtime-controller.keyboard.d.ts +19 -0
  83. package/dist/runtime/create-runtime/runtime-controller.lifecycle.d.ts +50 -0
  84. package/dist/runtime/create-runtime/runtime-controller.public-api.capabilities.d.ts +57 -0
  85. package/dist/runtime/create-runtime/runtime-controller.public-api.d.ts +28 -0
  86. package/dist/runtime/create-runtime/runtime-controller.render-loop.d.ts +20 -0
  87. package/dist/runtime/create-runtime/runtime-controller.state.types.d.ts +24 -0
  88. package/dist/runtime/create-runtime/runtime-reporting.d.ts +3 -21
  89. package/dist/runtime/create-runtime/runtime-reporting.types.d.ts +18 -0
  90. package/dist/runtime/{create-app-symbols.d.ts → create-runtime/runtime-symbols.d.ts} +2 -2
  91. package/dist/runtime/create-runtime/search-highlight-utils.d.ts +1 -1
  92. package/dist/runtime/create-runtime/search-runtime/index.d.ts +2 -0
  93. package/dist/runtime/create-runtime/{search-runtime.d.ts → search-runtime/types.d.ts} +9 -6
  94. package/dist/runtime/create-runtime/shader-stage-runtime.d.ts +2 -29
  95. package/dist/runtime/create-runtime/shader-stage-runtime.types.d.ts +27 -0
  96. package/dist/runtime/{text-decoration.d.ts → create-runtime/text-decoration.d.ts} +1 -1
  97. package/dist/runtime/create-runtime.d.ts +7 -7
  98. package/dist/runtime/font-atlas-utils/glyph-atlas-builder.d.ts +1 -1
  99. package/dist/runtime/font-atlas-utils/nerd-metrics-utils.d.ts +1 -1
  100. package/dist/runtime/{atlas-builder.d.ts → fonts/atlas-builder.d.ts} +1 -1
  101. package/dist/runtime/{font-resource-store.d.ts → fonts/font-resource-store.d.ts} +3 -2
  102. package/dist/runtime/fonts/font-sources.d.ts +4 -0
  103. package/dist/runtime/shader-stages.d.ts +1 -1
  104. package/dist/runtime/types.d.ts +6 -393
  105. package/dist/surface/{panes-context-menu.d.ts → panes/context-menu.d.ts} +1 -1
  106. package/dist/surface/panes/default-context-menu-items.d.ts +2 -2
  107. package/dist/surface/panes/layout.d.ts +1 -1
  108. package/dist/surface/panes/managed-pane-create.d.ts +3 -0
  109. package/dist/surface/panes/managed-pane-create.types.d.ts +17 -0
  110. package/dist/surface/panes/managed-pane-dom.d.ts +13 -0
  111. package/dist/surface/panes/managed-pane-manager.d.ts +6 -0
  112. package/dist/surface/panes/managed-pane-options.d.ts +6 -0
  113. package/dist/surface/panes/managed-pane-options.types.d.ts +7 -0
  114. package/dist/surface/panes/managed-pane-runtime-config.d.ts +3 -0
  115. package/dist/surface/panes/managed-pane-runtime-config.types.d.ts +9 -0
  116. package/dist/surface/panes/managed-pane-runtime.d.ts +3 -0
  117. package/dist/surface/panes/managed-pane-runtime.types.d.ts +10 -0
  118. package/dist/surface/panes/managed-pane-search-ui.d.ts +9 -0
  119. package/dist/surface/{pane-app-manager.d.ts → panes/managed-pane-types.d.ts} +34 -36
  120. package/dist/surface/panes/manager.d.ts +1 -1
  121. package/dist/surface/panes/pane-interactions.d.ts +2 -2
  122. package/dist/surface/{panes-styles.d.ts → panes/styles.d.ts} +1 -1
  123. package/dist/surface/{panes-types.d.ts → panes/types.d.ts} +48 -7
  124. package/dist/surface/panes/window-events.d.ts +1 -1
  125. package/dist/surface/{restty-plugin-types.d.ts → plugins/context.types.d.ts} +32 -88
  126. package/dist/surface/{restty/plugin-dispatcher.d.ts → plugins/dispatcher.d.ts} +4 -13
  127. package/dist/surface/plugins/dispatcher.types.d.ts +6 -0
  128. package/dist/surface/{restty/plugin-ops.d.ts → plugins/host.d.ts} +4 -3
  129. package/dist/surface/plugins/runtime.d.ts +18 -0
  130. package/dist/surface/plugins/runtime.types.d.ts +47 -0
  131. package/dist/surface/plugins/types.d.ts +83 -0
  132. package/dist/surface/restty/active-pane-api.d.ts +33 -23
  133. package/dist/surface/restty/assembly.d.ts +23 -0
  134. package/dist/surface/restty/bootstrap.d.ts +21 -0
  135. package/dist/surface/restty/config.d.ts +46 -0
  136. package/dist/surface/restty/controller.d.ts +45 -0
  137. package/dist/surface/restty/events.d.ts +14 -0
  138. package/dist/surface/restty/manager-options.d.ts +4 -23
  139. package/dist/surface/restty/manager-options.types.d.ts +32 -0
  140. package/dist/surface/restty/pane-command-ops.d.ts +37 -0
  141. package/dist/surface/restty/pane-handle-ops.d.ts +24 -0
  142. package/dist/surface/{restty-pane-handle.d.ts → restty/pane-handle.d.ts} +42 -40
  143. package/dist/surface/restty/pane-lookup.d.ts +22 -0
  144. package/dist/surface/restty/pane-manager-assembly.d.ts +18 -0
  145. package/dist/surface/restty/pane-ops.d.ts +3 -51
  146. package/dist/surface/restty/pane-style-ops.d.ts +6 -0
  147. package/dist/surface/restty/plugin-surface.d.ts +5 -0
  148. package/dist/surface/restty/shader-ops.d.ts +6 -5
  149. package/dist/surface/restty.d.ts +28 -49
  150. package/dist/surface/search-ui/controller.d.ts +2 -0
  151. package/dist/surface/search-ui/index.d.ts +2 -0
  152. package/dist/surface/search-ui/styles.d.ts +5 -0
  153. package/dist/surface/{pane-search-ui.d.ts → search-ui/types.d.ts} +22 -13
  154. package/dist/wasm/embedded.d.ts +1 -1
  155. package/dist/wasm/runtime/types.d.ts +0 -6
  156. package/dist/xterm/compat-services.d.ts +2 -0
  157. package/dist/xterm.d.ts +4 -3
  158. package/dist/xterm.js +14 -8
  159. package/package.json +52 -33
  160. package/dist/internal.esm.js +0 -22285
  161. package/dist/runtime/create-runtime/debug-tools/create-dump-glyph-render.d.ts +0 -2
  162. package/dist/runtime/create-runtime/debug-tools/diagnose-codepoint.d.ts +0 -2
  163. package/dist/runtime/create-runtime/debug-tools/dump-atlas-for-codepoint.d.ts +0 -2
  164. package/dist/runtime/create-runtime/debug-tools/read-texture-to-image-data.d.ts +0 -1
  165. package/dist/runtime/create-runtime/debug-tools/setup-debug-expose.d.ts +0 -2
  166. package/dist/runtime/create-runtime/debug-tools/types.d.ts +0 -63
  167. package/dist/runtime/create-runtime/debug-tools.d.ts +0 -6
  168. package/dist/runtime/create-runtime/font-runtime-grid-helpers.d.ts +0 -31
  169. package/dist/runtime/create-runtime/font-runtime-helpers.d.ts +0 -15
  170. package/dist/runtime/create-runtime/font-runtime-text-helpers.d.ts +0 -21
  171. package/dist/runtime/create-runtime/font-runtime-webgpu-atlas.d.ts +0 -29
  172. package/dist/runtime/create-runtime/lifecycle-theme-size-theme.d.ts +0 -6
  173. package/dist/runtime/create-runtime/runtime-app-api.d.ts +0 -113
  174. package/dist/runtime/create-runtime/runtime-logger.d.ts +0 -17
  175. package/dist/runtime/font-sources.d.ts +0 -5
  176. package/dist/runtime/pty-output-buffer.d.ts +0 -12
  177. package/dist/runtime/session.d.ts +0 -9
  178. package/dist/surface/app-factory.d.ts +0 -3
  179. package/dist/surface/restty-plugin-runtime.d.ts +0 -57
  180. package/dist/xterm/app-options.d.ts +0 -2
  181. package/dist/xterm.esm.js +0 -12210
  182. /package/dist/runtime/create-runtime/{atlas-debug-utils.d.ts → atlas-bitmap-utils.d.ts} +0 -0
  183. /package/dist/runtime/{clipboard-paste.d.ts → create-runtime/clipboard-paste.d.ts} +0 -0
  184. /package/dist/runtime/{render-stage-shaders.d.ts → create-runtime/render-stage-shaders.d.ts} +0 -0
  185. /package/dist/runtime/{create-app-io-utils.d.ts → create-runtime/runtime-io-utils.d.ts} +0 -0
  186. /package/dist/surface/{restty-plugin-utils.d.ts → plugins/utils.d.ts} +0 -0
@@ -0,0 +1,1690 @@
1
+ import {
2
+ createResttyRuntime,
3
+ getDefaultResttyRuntimeSession
4
+ } from "./chunk-db9dt7pr.js";
5
+
6
+ // src/surface/panes/context-menu.ts
7
+ function createPaneContextMenuController(options) {
8
+ const contextMenuEl = options.doc.createElement("div");
9
+ contextMenuEl.className = "pane-context-menu";
10
+ contextMenuEl.hidden = true;
11
+ options.doc.body.appendChild(contextMenuEl);
12
+ const hide = () => {
13
+ contextMenuEl.hidden = true;
14
+ contextMenuEl.innerHTML = "";
15
+ };
16
+ const addSeparator = () => {
17
+ const separator = options.doc.createElement("div");
18
+ separator.className = "pane-context-menu-separator";
19
+ contextMenuEl.appendChild(separator);
20
+ };
21
+ const render = (items) => {
22
+ contextMenuEl.innerHTML = "";
23
+ for (const item of items) {
24
+ if (item === "separator") {
25
+ addSeparator();
26
+ continue;
27
+ }
28
+ const button = options.doc.createElement("button");
29
+ button.type = "button";
30
+ button.className = "pane-context-menu-item";
31
+ if (item.danger)
32
+ button.classList.add("is-danger");
33
+ if (item.enabled === false)
34
+ button.disabled = true;
35
+ const label = options.doc.createElement("span");
36
+ label.className = "pane-context-menu-label";
37
+ label.textContent = item.label;
38
+ button.appendChild(label);
39
+ if (item.shortcut) {
40
+ const shortcut = options.doc.createElement("span");
41
+ shortcut.className = "pane-context-menu-shortcut";
42
+ shortcut.textContent = item.shortcut;
43
+ button.appendChild(shortcut);
44
+ }
45
+ button.addEventListener("click", () => {
46
+ hide();
47
+ item.action();
48
+ });
49
+ contextMenuEl.appendChild(button);
50
+ }
51
+ };
52
+ const show = (pane, clientX, clientY, manager) => {
53
+ const items = options.contextMenu.getItems(pane, manager);
54
+ render(items);
55
+ contextMenuEl.hidden = false;
56
+ const margin = 8;
57
+ const rect = contextMenuEl.getBoundingClientRect();
58
+ const maxX = Math.max(margin, options.win.innerWidth - rect.width - margin);
59
+ const maxY = Math.max(margin, options.win.innerHeight - rect.height - margin);
60
+ const left = Math.min(Math.max(clientX, margin), maxX);
61
+ const top = Math.min(Math.max(clientY, margin), maxY);
62
+ contextMenuEl.style.left = `${left}px`;
63
+ contextMenuEl.style.top = `${top}px`;
64
+ };
65
+ const destroy = () => {
66
+ hide();
67
+ contextMenuEl.remove();
68
+ };
69
+ return {
70
+ element: contextMenuEl,
71
+ isOpen: () => !contextMenuEl.hidden,
72
+ containsTarget: (target) => target instanceof Node && contextMenuEl.contains(target),
73
+ show,
74
+ hide,
75
+ destroy
76
+ };
77
+ }
78
+
79
+ // src/surface/panes/styles.ts
80
+ var RESTTY_PANE_ROOT_CLASS = "restty-pane-root";
81
+ var RESTTY_PANE_STYLE_MARKER = "data-restty-pane-styles";
82
+ var RESTTY_PANE_STYLE_TEXT = `
83
+ .${RESTTY_PANE_ROOT_CLASS} {
84
+ display: flex;
85
+ width: 100%;
86
+ height: 100%;
87
+ min-width: 0;
88
+ min-height: 0;
89
+ }
90
+
91
+ .${RESTTY_PANE_ROOT_CLASS} .pane-split {
92
+ display: flex;
93
+ flex: 1 1 auto;
94
+ min-width: 0;
95
+ min-height: 0;
96
+ gap: 0;
97
+ padding: 0;
98
+ background: var(--restty-pane-split-background, #000);
99
+ }
100
+
101
+ .${RESTTY_PANE_ROOT_CLASS} .pane-split.is-vertical {
102
+ flex-direction: row;
103
+ }
104
+
105
+ .${RESTTY_PANE_ROOT_CLASS} .pane-split.is-horizontal {
106
+ flex-direction: column;
107
+ }
108
+
109
+ .${RESTTY_PANE_ROOT_CLASS} .pane {
110
+ position: relative;
111
+ flex: 1 1 0;
112
+ min-width: 0;
113
+ min-height: 0;
114
+ background: var(--restty-pane-background, #000);
115
+ border: 0;
116
+ overflow: hidden;
117
+ opacity: var(--restty-pane-inactive-opacity, 0.9);
118
+ transition: opacity var(--restty-pane-opacity-transition, 140ms) ease-out;
119
+ }
120
+
121
+ .${RESTTY_PANE_ROOT_CLASS} .pane.is-active {
122
+ opacity: var(--restty-pane-active-opacity, 1);
123
+ }
124
+
125
+ .${RESTTY_PANE_ROOT_CLASS} .pane-divider {
126
+ position: relative;
127
+ z-index: 2;
128
+ flex: 0 0 var(--restty-pane-divider-thickness, 1px);
129
+ background: var(--restty-pane-divider-color, #242424);
130
+ touch-action: none;
131
+ }
132
+
133
+ .${RESTTY_PANE_ROOT_CLASS} .pane-divider.is-vertical {
134
+ cursor: col-resize;
135
+ }
136
+
137
+ .${RESTTY_PANE_ROOT_CLASS} .pane-divider.is-horizontal {
138
+ cursor: row-resize;
139
+ }
140
+
141
+ .${RESTTY_PANE_ROOT_CLASS} .pane-divider.is-vertical:hover,
142
+ .${RESTTY_PANE_ROOT_CLASS} .pane-divider.is-vertical.is-dragging {
143
+ background:
144
+ radial-gradient(
145
+ 100px 46% at 50% 50%,
146
+ rgba(235, 235, 235, 0.92) 0%,
147
+ rgba(200, 200, 200, 0.48) 46%,
148
+ rgba(155, 155, 155, 0.12) 68%,
149
+ rgba(120, 120, 120, 0) 100%
150
+ ),
151
+ rgba(185, 185, 185, 0.24);
152
+ }
153
+
154
+ .${RESTTY_PANE_ROOT_CLASS} .pane-divider.is-horizontal:hover,
155
+ .${RESTTY_PANE_ROOT_CLASS} .pane-divider.is-horizontal.is-dragging {
156
+ background:
157
+ radial-gradient(
158
+ 46% 100px at 50% 50%,
159
+ rgba(235, 235, 235, 0.92) 0%,
160
+ rgba(200, 200, 200, 0.48) 46%,
161
+ rgba(155, 155, 155, 0.12) 68%,
162
+ rgba(120, 120, 120, 0) 100%
163
+ ),
164
+ rgba(185, 185, 185, 0.24);
165
+ }
166
+
167
+ body.is-resizing-split {
168
+ user-select: none;
169
+ }
170
+
171
+ .${RESTTY_PANE_ROOT_CLASS} .pane-canvas {
172
+ width: 100%;
173
+ height: 100%;
174
+ display: block;
175
+ outline: none;
176
+ }
177
+
178
+ .${RESTTY_PANE_ROOT_CLASS} .pane-ime-input {
179
+ position: fixed;
180
+ left: 0;
181
+ top: 0;
182
+ width: 1em;
183
+ height: 1em;
184
+ padding: 0;
185
+ margin: 0;
186
+ border: 0;
187
+ outline: none;
188
+ background: transparent;
189
+ color: transparent;
190
+ caret-color: transparent;
191
+ overflow: hidden;
192
+ resize: none;
193
+ opacity: 0;
194
+ pointer-events: none;
195
+ }
196
+
197
+ .pane-context-menu {
198
+ position: fixed;
199
+ z-index: 9999;
200
+ min-width: 200px;
201
+ padding: 6px;
202
+ border: 1px solid #2a2a2a;
203
+ border-radius: 8px;
204
+ background: #161616;
205
+ box-shadow: 0 14px 40px rgba(0, 0, 0, 0.45);
206
+ }
207
+
208
+ .pane-context-menu-item {
209
+ width: 100%;
210
+ display: flex;
211
+ align-items: center;
212
+ justify-content: space-between;
213
+ gap: 12px;
214
+ padding: 7px 9px;
215
+ border: 0;
216
+ border-radius: 6px;
217
+ background: transparent;
218
+ color: #d6d6d6;
219
+ text-align: left;
220
+ cursor: pointer;
221
+ }
222
+
223
+ .pane-context-menu-item:hover {
224
+ background: #252525;
225
+ }
226
+
227
+ .pane-context-menu-item:disabled {
228
+ opacity: 0.4;
229
+ cursor: default;
230
+ }
231
+
232
+ .pane-context-menu-item.is-danger {
233
+ color: #f1a1a1;
234
+ }
235
+
236
+ .pane-context-menu-label {
237
+ font-size: 12px;
238
+ }
239
+
240
+ .pane-context-menu-shortcut {
241
+ font-size: 10px;
242
+ color: #868686;
243
+ }
244
+
245
+ .pane-context-menu-separator {
246
+ height: 1px;
247
+ margin: 6px 4px;
248
+ background: #2a2a2a;
249
+ }
250
+ `;
251
+ var DEFAULT_RESTTY_PANE_STYLE_OPTIONS = {
252
+ splitBackground: "#000",
253
+ paneBackground: "#000",
254
+ inactivePaneOpacity: 0.9,
255
+ activePaneOpacity: 1,
256
+ opacityTransitionMs: 140,
257
+ dividerColor: "#242424",
258
+ dividerThicknessPx: 1
259
+ };
260
+ function clampNumber(value, min, max) {
261
+ return Math.min(max, Math.max(min, value));
262
+ }
263
+ function normalizeColor(value, fallback) {
264
+ if (typeof value !== "string")
265
+ return fallback;
266
+ const trimmed = value.trim();
267
+ return trimmed ? trimmed : fallback;
268
+ }
269
+ function normalizePaneStyleOptions(options) {
270
+ const inactivePaneOpacity = Number.isFinite(options.inactivePaneOpacity) ? clampNumber(Number(options.inactivePaneOpacity), 0, 1) : DEFAULT_RESTTY_PANE_STYLE_OPTIONS.inactivePaneOpacity;
271
+ const activePaneOpacity = Number.isFinite(options.activePaneOpacity) ? clampNumber(Number(options.activePaneOpacity), 0, 1) : DEFAULT_RESTTY_PANE_STYLE_OPTIONS.activePaneOpacity;
272
+ const opacityTransitionMs = Number.isFinite(options.opacityTransitionMs) ? clampNumber(Number(options.opacityTransitionMs), 0, 5000) : DEFAULT_RESTTY_PANE_STYLE_OPTIONS.opacityTransitionMs;
273
+ const dividerThicknessPx = Number.isFinite(options.dividerThicknessPx) ? clampNumber(Number(options.dividerThicknessPx), 1, 32) : DEFAULT_RESTTY_PANE_STYLE_OPTIONS.dividerThicknessPx;
274
+ return {
275
+ splitBackground: normalizeColor(options.splitBackground, DEFAULT_RESTTY_PANE_STYLE_OPTIONS.splitBackground),
276
+ paneBackground: normalizeColor(options.paneBackground, DEFAULT_RESTTY_PANE_STYLE_OPTIONS.paneBackground),
277
+ inactivePaneOpacity,
278
+ activePaneOpacity,
279
+ opacityTransitionMs,
280
+ dividerColor: normalizeColor(options.dividerColor, DEFAULT_RESTTY_PANE_STYLE_OPTIONS.dividerColor),
281
+ dividerThicknessPx
282
+ };
283
+ }
284
+ function ensureResttyPaneStylesDocument(doc) {
285
+ if (doc.querySelector(`style[${RESTTY_PANE_STYLE_MARKER}="1"]`))
286
+ return;
287
+ const style = doc.createElement("style");
288
+ style.setAttribute(RESTTY_PANE_STYLE_MARKER, "1");
289
+ style.textContent = RESTTY_PANE_STYLE_TEXT;
290
+ doc.head.appendChild(style);
291
+ }
292
+ function applyPaneStyleOptionsToRoot(root, options) {
293
+ root.classList.add(RESTTY_PANE_ROOT_CLASS);
294
+ root.style.setProperty("--restty-pane-split-background", options.splitBackground);
295
+ root.style.setProperty("--restty-pane-background", options.paneBackground);
296
+ root.style.setProperty("--restty-pane-inactive-opacity", options.inactivePaneOpacity.toFixed(3));
297
+ root.style.setProperty("--restty-pane-active-opacity", options.activePaneOpacity.toFixed(3));
298
+ root.style.setProperty("--restty-pane-opacity-transition", `${options.opacityTransitionMs}ms`);
299
+ root.style.setProperty("--restty-pane-divider-color", options.dividerColor);
300
+ root.style.setProperty("--restty-pane-divider-thickness", `${options.dividerThicknessPx}px`);
301
+ }
302
+ function clearPaneStyleOptionsFromRoot(root) {
303
+ root.classList.remove(RESTTY_PANE_ROOT_CLASS);
304
+ root.style.removeProperty("--restty-pane-split-background");
305
+ root.style.removeProperty("--restty-pane-background");
306
+ root.style.removeProperty("--restty-pane-inactive-opacity");
307
+ root.style.removeProperty("--restty-pane-active-opacity");
308
+ root.style.removeProperty("--restty-pane-opacity-transition");
309
+ root.style.removeProperty("--restty-pane-divider-color");
310
+ root.style.removeProperty("--restty-pane-divider-thickness");
311
+ }
312
+
313
+ // src/surface/panes/layout.ts
314
+ var getSplitBranches = (split) => {
315
+ const branches = [];
316
+ for (const child of Array.from(split.children)) {
317
+ if (!(child instanceof HTMLElement))
318
+ continue;
319
+ if (child.classList.contains("pane-divider"))
320
+ continue;
321
+ branches.push(child);
322
+ }
323
+ return branches;
324
+ };
325
+ var getRectEdgeDistanceSquared = (sourceRect, targetRect) => {
326
+ const dx = Math.max(targetRect.left - sourceRect.right, sourceRect.left - targetRect.right, 0);
327
+ const dy = Math.max(targetRect.top - sourceRect.bottom, sourceRect.top - targetRect.bottom, 0);
328
+ return dx ** 2 + dy ** 2;
329
+ };
330
+ var getRectCenterDistanceSquared = (sourceRect, targetRect) => {
331
+ const sourceCenterX = sourceRect.left + sourceRect.width * 0.5;
332
+ const sourceCenterY = sourceRect.top + sourceRect.height * 0.5;
333
+ const targetCenterX = targetRect.left + targetRect.width * 0.5;
334
+ const targetCenterY = targetRect.top + targetRect.height * 0.5;
335
+ const dx = targetCenterX - sourceCenterX;
336
+ const dy = targetCenterY - sourceCenterY;
337
+ return dx ** 2 + dy ** 2;
338
+ };
339
+ function findClosestPaneToRect(sourceRect, panes) {
340
+ if (!sourceRect)
341
+ return null;
342
+ let closestPane = null;
343
+ let closestEdgeDistance = Number.POSITIVE_INFINITY;
344
+ let closestCenterDistance = Number.POSITIVE_INFINITY;
345
+ for (const candidate of panes) {
346
+ const targetRect = candidate.container.getBoundingClientRect();
347
+ const edgeDistance = getRectEdgeDistanceSquared(sourceRect, targetRect);
348
+ const centerDistance = getRectCenterDistanceSquared(sourceRect, targetRect);
349
+ if (edgeDistance < closestEdgeDistance || edgeDistance === closestEdgeDistance && centerDistance < closestCenterDistance) {
350
+ closestPane = candidate;
351
+ closestEdgeDistance = edgeDistance;
352
+ closestCenterDistance = centerDistance;
353
+ }
354
+ }
355
+ return closestPane;
356
+ }
357
+ function collapseSplitAncestors(start) {
358
+ let current = start;
359
+ while (current && current.classList.contains("pane-split")) {
360
+ const branches = getSplitBranches(current);
361
+ if (branches.length > 1)
362
+ return;
363
+ const onlyChild = branches[0];
364
+ const parent = current.parentElement;
365
+ if (!parent || !onlyChild)
366
+ return;
367
+ const inheritedFlex = current.style.flex;
368
+ if (inheritedFlex) {
369
+ onlyChild.style.flex = inheritedFlex;
370
+ } else {
371
+ onlyChild.style.flex = "";
372
+ }
373
+ parent.replaceChild(onlyChild, current);
374
+ current = parent;
375
+ }
376
+ }
377
+ function createSplitDividerFactory(options) {
378
+ let splitResizeState = null;
379
+ const createSplitDivider = (direction) => {
380
+ const divider = document.createElement("div");
381
+ divider.className = `pane-divider ${direction === "vertical" ? "is-vertical" : "is-horizontal"}`;
382
+ divider.setAttribute("role", "separator");
383
+ divider.setAttribute("aria-orientation", direction === "vertical" ? "vertical" : "horizontal");
384
+ const onPointerMove = (event) => {
385
+ const state = splitResizeState;
386
+ if (!state || event.pointerId !== state.pointerId)
387
+ return;
388
+ event.preventDefault();
389
+ const coord = state.axis === "x" ? event.clientX : event.clientY;
390
+ const delta = coord - state.startCoord;
391
+ const maxFirst = Math.max(options.minPaneSize, state.total - options.minPaneSize);
392
+ const nextFirst = Math.min(maxFirst, Math.max(options.minPaneSize, state.startFirst + delta));
393
+ const nextSecond = Math.max(options.minPaneSize, state.total - nextFirst);
394
+ const firstPercent = nextFirst / (nextFirst + nextSecond) * 100;
395
+ const secondPercent = 100 - firstPercent;
396
+ state.first.style.flex = `0 0 ${firstPercent.toFixed(5)}%`;
397
+ state.second.style.flex = `0 0 ${secondPercent.toFixed(5)}%`;
398
+ options.requestLayoutSync();
399
+ };
400
+ const endResize = () => {
401
+ if (!splitResizeState)
402
+ return;
403
+ splitResizeState.divider.classList.remove("is-dragging");
404
+ document.body.classList.remove("is-resizing-split");
405
+ splitResizeState = null;
406
+ };
407
+ const onPointerEnd = (event) => {
408
+ if (!splitResizeState || event.pointerId !== splitResizeState.pointerId)
409
+ return;
410
+ try {
411
+ divider.releasePointerCapture(splitResizeState.pointerId);
412
+ } catch {}
413
+ divider.removeEventListener("pointermove", onPointerMove);
414
+ divider.removeEventListener("pointerup", onPointerEnd);
415
+ divider.removeEventListener("pointercancel", onPointerEnd);
416
+ endResize();
417
+ };
418
+ divider.addEventListener("pointerdown", (event) => {
419
+ if (event.button !== 0)
420
+ return;
421
+ const first = divider.previousElementSibling;
422
+ const second = divider.nextElementSibling;
423
+ const split = divider.parentElement;
424
+ if (!first || !second || !split)
425
+ return;
426
+ const splitRect = split.getBoundingClientRect();
427
+ const firstRect = first.getBoundingClientRect();
428
+ const axis = direction === "vertical" ? "x" : "y";
429
+ const total = axis === "x" ? splitRect.width : splitRect.height;
430
+ if (total <= 0)
431
+ return;
432
+ endResize();
433
+ event.preventDefault();
434
+ event.stopPropagation();
435
+ splitResizeState = {
436
+ pointerId: event.pointerId,
437
+ axis,
438
+ divider,
439
+ first,
440
+ second,
441
+ startCoord: axis === "x" ? event.clientX : event.clientY,
442
+ startFirst: axis === "x" ? firstRect.width : firstRect.height,
443
+ total
444
+ };
445
+ divider.classList.add("is-dragging");
446
+ document.body.classList.add("is-resizing-split");
447
+ divider.setPointerCapture(event.pointerId);
448
+ divider.addEventListener("pointermove", onPointerMove);
449
+ divider.addEventListener("pointerup", onPointerEnd);
450
+ divider.addEventListener("pointercancel", onPointerEnd);
451
+ });
452
+ return divider;
453
+ };
454
+ return { createSplitDivider };
455
+ }
456
+
457
+ // src/surface/panes/pane-interactions.ts
458
+ function createPaneInteractions(options) {
459
+ const paneCleanupFns = new Map;
460
+ const bindPaneInteractions = (pane) => {
461
+ const cleanupFns = [];
462
+ const { id, container } = pane;
463
+ const onPointerDown = () => {
464
+ options.markPaneFocused(id);
465
+ };
466
+ container.addEventListener("pointerdown", onPointerDown);
467
+ cleanupFns.push(() => {
468
+ container.removeEventListener("pointerdown", onPointerDown);
469
+ });
470
+ const focusTarget = pane.focusTarget;
471
+ if (focusTarget) {
472
+ const onFocus = () => {
473
+ options.markPaneFocused(id);
474
+ };
475
+ focusTarget.addEventListener("focus", onFocus);
476
+ cleanupFns.push(() => {
477
+ focusTarget.removeEventListener("focus", onFocus);
478
+ });
479
+ }
480
+ if (options.contextMenu) {
481
+ const onContextMenu = (event) => {
482
+ if (event.defaultPrevented)
483
+ return;
484
+ if (options.contextMenu?.canOpen && !options.contextMenu.canOpen(event, pane)) {
485
+ return;
486
+ }
487
+ event.preventDefault();
488
+ event.stopPropagation();
489
+ options.markPaneFocused(id);
490
+ options.contextMenuController?.show(pane, event.clientX, event.clientY, options.getManager());
491
+ };
492
+ container.addEventListener("contextmenu", onContextMenu);
493
+ cleanupFns.push(() => {
494
+ container.removeEventListener("contextmenu", onContextMenu);
495
+ });
496
+ }
497
+ paneCleanupFns.set(id, cleanupFns);
498
+ };
499
+ const cleanupPaneInteractions = (id) => {
500
+ const cleanupFns = paneCleanupFns.get(id) ?? [];
501
+ paneCleanupFns.delete(id);
502
+ for (const cleanup of cleanupFns) {
503
+ cleanup();
504
+ }
505
+ };
506
+ return {
507
+ bindPaneInteractions,
508
+ cleanupPaneInteractions
509
+ };
510
+ }
511
+
512
+ // src/surface/panes/window-events.ts
513
+ function attachPaneManagerWindowEvents(options) {
514
+ const onWindowPointerDown = (event) => {
515
+ if (!options.contextMenuController?.isOpen())
516
+ return;
517
+ if (options.contextMenuController.containsTarget(event.target))
518
+ return;
519
+ options.hideContextMenu();
520
+ };
521
+ const onWindowBlur = () => {
522
+ options.hideContextMenu();
523
+ };
524
+ const onWindowKeyDown = (event) => {
525
+ if (options.contextMenuController?.isOpen() && event.key === "Escape") {
526
+ options.hideContextMenu();
527
+ return;
528
+ }
529
+ if (options.shortcutOptions.enabled === false)
530
+ return;
531
+ if (options.shortcutOptions.canHandleEvent && !options.shortcutOptions.canHandleEvent(event)) {
532
+ return;
533
+ }
534
+ const target = event.target;
535
+ if (target && ["INPUT", "TEXTAREA", "SELECT", "BUTTON"].includes(target.tagName)) {
536
+ const allowed = options.shortcutOptions.isAllowedInputTarget?.(target) ?? false;
537
+ if (!allowed)
538
+ return;
539
+ }
540
+ const isMac = typeof navigator !== "undefined" && /mac/i.test(navigator.platform);
541
+ const hasCommandModifier = isMac ? event.metaKey : event.ctrlKey;
542
+ if (!hasCommandModifier || event.altKey || event.code !== "KeyD" || event.repeat) {
543
+ return;
544
+ }
545
+ event.preventDefault();
546
+ event.stopPropagation();
547
+ options.splitActivePane(event.shiftKey ? "horizontal" : "vertical");
548
+ };
549
+ window.addEventListener("pointerdown", onWindowPointerDown);
550
+ window.addEventListener("blur", onWindowBlur);
551
+ window.addEventListener("keydown", onWindowKeyDown, { capture: true });
552
+ return () => {
553
+ window.removeEventListener("pointerdown", onWindowPointerDown);
554
+ window.removeEventListener("blur", onWindowBlur);
555
+ window.removeEventListener("keydown", onWindowKeyDown, { capture: true });
556
+ };
557
+ }
558
+
559
+ // src/surface/panes/manager.ts
560
+ function createResttyPaneManager(options) {
561
+ const { root, createPane } = options;
562
+ const isElementLike = typeof HTMLElement !== "undefined" ? root instanceof HTMLElement : !!root && typeof root.ownerDocument !== "undefined";
563
+ if (!isElementLike) {
564
+ throw new Error("createResttyPaneManager requires a root HTMLElement");
565
+ }
566
+ const panes = new Map;
567
+ const minPaneSize = Number.isFinite(options.minPaneSize) ? Math.max(24, Number(options.minPaneSize)) : 96;
568
+ const shortcutOptions = typeof options.shortcuts === "object" ? options.shortcuts : { enabled: options.shortcuts !== false };
569
+ const stylesInput = typeof options.styles === "object" && options.styles ? options.styles : undefined;
570
+ const stylesEnabled = options.styles === false ? false : stylesInput?.enabled ?? true;
571
+ let styleOptions = normalizePaneStyleOptions({
572
+ ...DEFAULT_RESTTY_PANE_STYLE_OPTIONS,
573
+ ...stylesInput
574
+ });
575
+ if (stylesEnabled) {
576
+ const doc = root.ownerDocument ?? document;
577
+ ensureResttyPaneStylesDocument(doc);
578
+ applyPaneStyleOptionsToRoot(root, styleOptions);
579
+ }
580
+ let nextPaneId = 1;
581
+ let activePaneId = null;
582
+ let focusedPaneId = null;
583
+ let resizeRaf = 0;
584
+ const ownerDoc = root.ownerDocument ?? document;
585
+ const ownerWin = ownerDoc.defaultView ?? window;
586
+ const contextMenuController = options.contextMenu ? createPaneContextMenuController({
587
+ contextMenu: options.contextMenu,
588
+ doc: ownerDoc,
589
+ win: ownerWin
590
+ }) : null;
591
+ const requestLayoutSync = () => {
592
+ if (resizeRaf)
593
+ return;
594
+ resizeRaf = requestAnimationFrame(() => {
595
+ resizeRaf = 0;
596
+ options.onLayoutChanged?.();
597
+ });
598
+ };
599
+ const { createSplitDivider } = createSplitDividerFactory({ minPaneSize, requestLayoutSync });
600
+ const getStyleOptions = () => ({
601
+ ...styleOptions
602
+ });
603
+ const setStyleOptions = (next) => {
604
+ styleOptions = normalizePaneStyleOptions({
605
+ ...styleOptions,
606
+ ...next
607
+ });
608
+ if (!stylesEnabled)
609
+ return;
610
+ applyPaneStyleOptionsToRoot(root, styleOptions);
611
+ };
612
+ const getPanes = () => Array.from(panes.values());
613
+ const getPaneById = (id) => panes.get(id) ?? null;
614
+ const findPaneByElement = (element) => {
615
+ if (!(element instanceof HTMLElement))
616
+ return null;
617
+ const host = element.closest(".pane");
618
+ if (!host)
619
+ return null;
620
+ const id = Number(host.dataset.paneId ?? "");
621
+ if (!Number.isFinite(id))
622
+ return null;
623
+ return panes.get(id) ?? null;
624
+ };
625
+ const getActivePane = () => activePaneId === null ? null : panes.get(activePaneId) ?? null;
626
+ const getFocusedPane = () => {
627
+ if (focusedPaneId !== null) {
628
+ const focused = panes.get(focusedPaneId);
629
+ if (focused)
630
+ return focused;
631
+ }
632
+ if (typeof document === "undefined")
633
+ return null;
634
+ return findPaneByElement(document.activeElement);
635
+ };
636
+ const setActivePane = (id, config) => {
637
+ const pane = panes.get(id);
638
+ if (!pane)
639
+ return;
640
+ activePaneId = id;
641
+ for (const current of panes.values()) {
642
+ current.container.classList.toggle("is-active", current.id === id);
643
+ }
644
+ options.onActivePaneChange?.(pane);
645
+ if (config?.focus) {
646
+ const target = pane.focusTarget ?? pane.container;
647
+ if (target instanceof HTMLElement) {
648
+ target.focus({ preventScroll: true });
649
+ }
650
+ }
651
+ };
652
+ const markPaneFocused = (id, config) => {
653
+ focusedPaneId = id;
654
+ setActivePane(id, config);
655
+ };
656
+ const hideContextMenu = () => contextMenuController?.hide();
657
+ let api;
658
+ const paneInteractions = createPaneInteractions({
659
+ contextMenu: options.contextMenu,
660
+ contextMenuController,
661
+ getManager: () => api,
662
+ markPaneFocused
663
+ });
664
+ const createPaneInternal = (sourcePane) => {
665
+ const id = nextPaneId;
666
+ nextPaneId += 1;
667
+ const pane = createPane({ id, sourcePane, manager: api });
668
+ if (pane.id !== id) {
669
+ throw new Error(`createResttyPaneManager expected pane.id=${id}, received ${pane.id}`);
670
+ }
671
+ if (!(pane.container instanceof HTMLDivElement)) {
672
+ throw new Error("createResttyPaneManager createPane() must return { container: HTMLDivElement }");
673
+ }
674
+ pane.container.classList.add("pane");
675
+ pane.container.dataset.paneId = `${id}`;
676
+ panes.set(id, pane);
677
+ paneInteractions.bindPaneInteractions(pane);
678
+ options.onPaneCreated?.(pane);
679
+ return pane;
680
+ };
681
+ const splitPane = (id, direction) => {
682
+ const target = panes.get(id);
683
+ if (!target)
684
+ return null;
685
+ const parent = target.container.parentElement;
686
+ if (!parent)
687
+ return null;
688
+ const split = document.createElement("div");
689
+ split.className = `pane-split ${direction === "vertical" ? "is-vertical" : "is-horizontal"}`;
690
+ const inheritedFlex = target.container.style.flex;
691
+ if (inheritedFlex) {
692
+ split.style.flex = inheritedFlex;
693
+ }
694
+ parent.replaceChild(split, target.container);
695
+ target.container.style.flex = "0 0 50%";
696
+ split.appendChild(target.container);
697
+ split.appendChild(createSplitDivider(direction));
698
+ const created = createPaneInternal(target);
699
+ created.container.style.flex = "0 0 50%";
700
+ split.appendChild(created.container);
701
+ markPaneFocused(created.id, { focus: true });
702
+ requestLayoutSync();
703
+ options.onPaneSplit?.(target, created, direction);
704
+ return created;
705
+ };
706
+ const splitActivePane = (direction) => {
707
+ const target = getFocusedPane() ?? getActivePane();
708
+ if (!target)
709
+ return null;
710
+ return splitPane(target.id, direction);
711
+ };
712
+ const closePane = (id) => {
713
+ if (panes.size <= 1)
714
+ return false;
715
+ const pane = panes.get(id);
716
+ if (!pane)
717
+ return false;
718
+ const closingRect = pane.container.getBoundingClientRect();
719
+ paneInteractions.cleanupPaneInteractions(id);
720
+ options.destroyPane?.(pane);
721
+ panes.delete(id);
722
+ if (activePaneId === id)
723
+ activePaneId = null;
724
+ if (focusedPaneId === id)
725
+ focusedPaneId = null;
726
+ const parent = pane.container.parentElement;
727
+ pane.container.remove();
728
+ collapseSplitAncestors(parent);
729
+ const fallback = getActivePane() ?? findClosestPaneToRect(closingRect, panes.values()) ?? getPanes()[0] ?? null;
730
+ if (fallback) {
731
+ markPaneFocused(fallback.id, { focus: true });
732
+ } else {
733
+ options.onActivePaneChange?.(null);
734
+ }
735
+ options.onPaneClosed?.(pane);
736
+ requestLayoutSync();
737
+ return true;
738
+ };
739
+ const createInitialPane = (config) => {
740
+ if (panes.size) {
741
+ return getPanes()[0];
742
+ }
743
+ const first = createPaneInternal(null);
744
+ root.appendChild(first.container);
745
+ markPaneFocused(first.id, { focus: config?.focus !== false });
746
+ requestLayoutSync();
747
+ return first;
748
+ };
749
+ const removeWindowEvents = attachPaneManagerWindowEvents({
750
+ contextMenuController,
751
+ hideContextMenu,
752
+ shortcutOptions,
753
+ splitActivePane
754
+ });
755
+ const destroy = () => {
756
+ removeWindowEvents();
757
+ if (resizeRaf) {
758
+ cancelAnimationFrame(resizeRaf);
759
+ resizeRaf = 0;
760
+ }
761
+ for (const pane of getPanes()) {
762
+ paneInteractions.cleanupPaneInteractions(pane.id);
763
+ options.destroyPane?.(pane);
764
+ }
765
+ panes.clear();
766
+ activePaneId = null;
767
+ focusedPaneId = null;
768
+ root.replaceChildren();
769
+ hideContextMenu();
770
+ contextMenuController?.destroy();
771
+ if (stylesEnabled) {
772
+ clearPaneStyleOptionsFromRoot(root);
773
+ }
774
+ };
775
+ api = {
776
+ getPanes,
777
+ getPaneById,
778
+ getActivePane,
779
+ getFocusedPane,
780
+ createInitialPane,
781
+ setActivePane,
782
+ markPaneFocused,
783
+ splitPane,
784
+ splitActivePane,
785
+ closePane,
786
+ getStyleOptions,
787
+ setStyleOptions,
788
+ requestLayoutSync,
789
+ hideContextMenu,
790
+ destroy
791
+ };
792
+ return api;
793
+ }
794
+
795
+ // src/surface/panes/default-context-menu-items.ts
796
+ function getResttyShortcutModifierLabel() {
797
+ const isMac = typeof navigator !== "undefined" && /mac/i.test(navigator.platform);
798
+ return isMac ? "Cmd" : "Ctrl";
799
+ }
800
+ function createDefaultResttyPaneContextMenuItems(options) {
801
+ const { pane, manager, getPtyUrl } = options;
802
+ const mod = options.modKeyLabel ?? getResttyShortcutModifierLabel();
803
+ const closeEnabled = manager.getPanes().length > 1;
804
+ const pauseLabel = typeof pane.paused === "boolean" ? pane.paused ? "Resume Renderer" : "Pause Renderer" : "Toggle Renderer Pause";
805
+ return [
806
+ {
807
+ label: "Copy",
808
+ shortcut: `${mod}+C`,
809
+ action: async () => {
810
+ await pane.copySelectionToClipboard();
811
+ }
812
+ },
813
+ {
814
+ label: "Paste",
815
+ shortcut: `${mod}+V`,
816
+ action: async () => {
817
+ await pane.pasteFromClipboard();
818
+ }
819
+ },
820
+ "separator",
821
+ {
822
+ label: "Split Right",
823
+ shortcut: `${mod}+D`,
824
+ action: () => {
825
+ manager.splitPane(pane.id, "vertical");
826
+ }
827
+ },
828
+ {
829
+ label: "Split Down",
830
+ shortcut: `${mod}+Shift+D`,
831
+ action: () => {
832
+ manager.splitPane(pane.id, "horizontal");
833
+ }
834
+ },
835
+ {
836
+ label: "Close Pane",
837
+ enabled: closeEnabled,
838
+ danger: true,
839
+ action: () => {
840
+ manager.closePane(pane.id);
841
+ }
842
+ },
843
+ "separator",
844
+ {
845
+ label: "Clear Screen",
846
+ action: () => {
847
+ pane.clearScreen();
848
+ }
849
+ },
850
+ {
851
+ label: pane.isPtyConnected() ? "Disconnect PTY" : "Connect PTY",
852
+ action: () => {
853
+ if (pane.isPtyConnected()) {
854
+ pane.disconnectPty();
855
+ return;
856
+ }
857
+ const url = (getPtyUrl?.() ?? "").trim();
858
+ pane.connectPty(url);
859
+ }
860
+ },
861
+ {
862
+ label: pauseLabel,
863
+ action: () => {
864
+ if (typeof pane.setPaused === "function") {
865
+ pane.setPaused(!(pane.paused ?? false));
866
+ return;
867
+ }
868
+ pane.togglePause();
869
+ }
870
+ }
871
+ ];
872
+ }
873
+
874
+ // src/surface/panes/managed-pane-dom.ts
875
+ function createImeInput(className, doc = document) {
876
+ const imeInput = doc.createElement("textarea");
877
+ imeInput.className = className;
878
+ imeInput.tabIndex = -1;
879
+ imeInput.autocapitalize = "off";
880
+ imeInput.autocomplete = "off";
881
+ imeInput.autocorrect = "off";
882
+ imeInput.spellcheck = false;
883
+ imeInput.style.position = "fixed";
884
+ imeInput.style.left = "0";
885
+ imeInput.style.top = "0";
886
+ imeInput.style.width = "1em";
887
+ imeInput.style.height = "1em";
888
+ imeInput.style.padding = "0";
889
+ imeInput.style.margin = "0";
890
+ imeInput.style.border = "0";
891
+ imeInput.style.outline = "none";
892
+ imeInput.style.background = "transparent";
893
+ imeInput.style.color = "transparent";
894
+ imeInput.style.caretColor = "transparent";
895
+ imeInput.style.overflow = "hidden";
896
+ imeInput.style.resize = "none";
897
+ imeInput.style.opacity = "0";
898
+ imeInput.style.pointerEvents = "none";
899
+ return imeInput;
900
+ }
901
+ function createManagedPaneDom(options) {
902
+ const doc = options.doc ?? document;
903
+ const container = doc.createElement("div");
904
+ container.className = options.paneClassName;
905
+ const canvas = doc.createElement("canvas");
906
+ canvas.className = options.canvasClassName;
907
+ canvas.tabIndex = 0;
908
+ const imeInput = createImeInput(options.imeInputClassName, doc);
909
+ container.append(canvas, imeInput);
910
+ return {
911
+ container,
912
+ canvas,
913
+ imeInput
914
+ };
915
+ }
916
+
917
+ // src/surface/panes/managed-pane-runtime-config.ts
918
+ function createManagedPaneRuntimeConfig(options) {
919
+ const { context, session, onSearchState } = options;
920
+ const baseTerminal = typeof options.terminal === "function" ? options.terminal(context) : options.terminal ?? {};
921
+ const baseServices = typeof options.services === "function" ? options.services(context) : options.services ?? {};
922
+ const mergedCallbacks = {
923
+ ...baseServices.callbacks,
924
+ onSearchState: (state) => {
925
+ baseServices.callbacks?.onSearchState?.(state);
926
+ onSearchState?.(state);
927
+ }
928
+ };
929
+ return {
930
+ mount: {
931
+ canvas: context.canvas,
932
+ imeInput: context.imeInput,
933
+ session
934
+ },
935
+ terminal: baseTerminal,
936
+ services: {
937
+ ...baseServices,
938
+ callbacks: mergedCallbacks
939
+ }
940
+ };
941
+ }
942
+
943
+ // src/surface/panes/managed-pane-runtime.ts
944
+ function createManagedPaneRuntime(options) {
945
+ const { autoInit } = options;
946
+ const runtime = createResttyRuntime(createManagedPaneRuntimeConfig(options));
947
+ if (autoInit) {
948
+ runtime.lifecycle.init();
949
+ }
950
+ return runtime;
951
+ }
952
+
953
+ // src/surface/panes/managed-pane-create.ts
954
+ function createManagedPane(options) {
955
+ const { container, canvas, imeInput } = createManagedPaneDom(options.dom);
956
+ const context = {
957
+ id: options.id,
958
+ sourcePane: options.sourcePane,
959
+ canvas,
960
+ imeInput
961
+ };
962
+ const runtime = createManagedPaneRuntime({
963
+ context,
964
+ terminal: options.terminal,
965
+ services: options.services,
966
+ session: options.session,
967
+ autoInit: options.autoInit,
968
+ onSearchState: options.onSearchState
969
+ });
970
+ return {
971
+ id: options.id,
972
+ container,
973
+ focusTarget: canvas,
974
+ runtime,
975
+ setRenderer: (value) => runtime.terminal.setRenderer(value),
976
+ setPaused: (value) => runtime.terminal.setPaused(value),
977
+ setFontSize: (value) => runtime.terminal.setFontSize(value),
978
+ setLigatures: (value) => runtime.terminal.setLigatures(value),
979
+ setFontHinting: (value) => runtime.terminal.setFontHinting(value),
980
+ setFontHintTarget: (value) => runtime.terminal.setFontHintTarget(value),
981
+ setFonts: (fonts) => runtime.terminal.setFonts(fonts),
982
+ applyTheme: (theme, sourceLabel) => runtime.terminal.applyTheme(theme, sourceLabel),
983
+ resetTheme: () => runtime.terminal.resetTheme(),
984
+ sendInput: (text, source) => runtime.io.sendInput(text, source),
985
+ sendKeyInput: (text, source) => runtime.io.sendKeyInput(text, source),
986
+ copySelectionToClipboard: () => runtime.interaction.copySelectionToClipboard(),
987
+ pasteFromClipboard: () => runtime.interaction.pasteFromClipboard(),
988
+ clearScreen: () => runtime.terminal.clearScreen(),
989
+ connectPty: (url = "") => runtime.io.connectPty(url),
990
+ disconnectPty: () => runtime.io.disconnectPty(),
991
+ isPtyConnected: () => runtime.io.isPtyConnected(),
992
+ togglePause: () => runtime.terminal.togglePause(),
993
+ setMouseMode: (value) => runtime.interaction.setMouseMode(value),
994
+ getMouseStatus: () => runtime.interaction.getMouseStatus(),
995
+ selectWordAtClientPoint: (clientX, clientY) => runtime.interaction.selectWordAtClientPoint(clientX, clientY),
996
+ initRuntime: () => runtime.lifecycle.init(),
997
+ destroyRuntime: () => runtime.lifecycle.destroy(),
998
+ setSearchQuery: (query) => runtime.search.setQuery(query),
999
+ clearSearch: () => runtime.search.clear(),
1000
+ searchNext: () => runtime.search.next(),
1001
+ searchPrevious: () => runtime.search.previous(),
1002
+ getSearchState: () => runtime.search.getState(),
1003
+ resize: (cols, rows) => runtime.interaction.resize(cols, rows),
1004
+ focus: () => runtime.interaction.focus(),
1005
+ blur: () => runtime.interaction.blur(),
1006
+ updateSize: (force) => runtime.interaction.updateSize(force),
1007
+ getBackend: () => runtime.render.getBackend(),
1008
+ setShaderStages: (stages) => runtime.render.setShaderStages(stages),
1009
+ getShaderStages: () => runtime.render.getShaderStages(),
1010
+ canvas,
1011
+ imeInput
1012
+ };
1013
+ }
1014
+
1015
+ // src/surface/panes/managed-pane-options.ts
1016
+ function defaultManagedPaneInputTargetPredicate(target) {
1017
+ return target.classList.contains("pane-ime-input") || target.classList.contains("restty-pane-ime-input");
1018
+ }
1019
+ function resolveManagedPaneContextMenu(options) {
1020
+ let contextMenu = options.contextMenu ?? null;
1021
+ if (!contextMenu) {
1022
+ const defaultMenuConfig = options.defaultContextMenu;
1023
+ const enabled = defaultMenuConfig === undefined ? true : typeof defaultMenuConfig === "boolean" ? defaultMenuConfig : defaultMenuConfig.enabled ?? true;
1024
+ if (enabled) {
1025
+ const config = typeof defaultMenuConfig === "object" && defaultMenuConfig ? defaultMenuConfig : undefined;
1026
+ contextMenu = {
1027
+ canOpen: config?.canOpen,
1028
+ getItems: (pane, manager) => createDefaultResttyPaneContextMenuItems({
1029
+ pane,
1030
+ manager,
1031
+ modKeyLabel: config?.modKeyLabel,
1032
+ getPtyUrl: config?.getPtyUrl
1033
+ })
1034
+ };
1035
+ }
1036
+ }
1037
+ return contextMenu;
1038
+ }
1039
+ function resolveManagedPaneShortcuts(shortcuts) {
1040
+ if (shortcuts === undefined || shortcuts === true) {
1041
+ return {
1042
+ enabled: true,
1043
+ isAllowedInputTarget: defaultManagedPaneInputTargetPredicate
1044
+ };
1045
+ }
1046
+ if (typeof shortcuts === "object" && !shortcuts.isAllowedInputTarget) {
1047
+ return {
1048
+ ...shortcuts,
1049
+ isAllowedInputTarget: defaultManagedPaneInputTargetPredicate
1050
+ };
1051
+ }
1052
+ return shortcuts;
1053
+ }
1054
+
1055
+ // src/surface/search-ui/styles.ts
1056
+ var ROOT_CLASS = "restty-search-ui-root";
1057
+ var STYLE_MARKER = "data-restty-pane-search-ui-styles";
1058
+ var STYLE_TEXT = `
1059
+ .${ROOT_CLASS} .restty-pane-search {
1060
+ position: absolute;
1061
+ top: var(--restty-search-ui-top, 10px);
1062
+ right: var(--restty-search-ui-right, 10px);
1063
+ z-index: var(--restty-search-ui-z-index, 8);
1064
+ width: min(var(--restty-search-ui-max-width, 332px), calc(100% - 20px));
1065
+ min-width: var(--restty-search-ui-min-width, 232px);
1066
+ display: none;
1067
+ align-items: center;
1068
+ padding: 6px;
1069
+ border: 1px solid var(--restty-search-ui-border, #2a2a2a);
1070
+ border-radius: var(--restty-search-ui-radius, 8px);
1071
+ background: var(--restty-search-ui-background, #161616);
1072
+ color: var(--restty-search-ui-text, #d6d6d6);
1073
+ backdrop-filter: blur(var(--restty-search-ui-blur, 8px));
1074
+ box-shadow: var(--restty-search-ui-shadow, 0 14px 40px rgba(0, 0, 0, 0.45));
1075
+ }
1076
+
1077
+ .${ROOT_CLASS} .restty-pane-search[data-open="1"] {
1078
+ display: flex;
1079
+ }
1080
+
1081
+ .${ROOT_CLASS} .restty-pane-search-row {
1082
+ display: grid;
1083
+ width: 100%;
1084
+ grid-template-columns: minmax(0, 1fr) auto auto auto auto auto;
1085
+ gap: 4px;
1086
+ align-items: center;
1087
+ }
1088
+
1089
+ .${ROOT_CLASS} .restty-pane-search-input,
1090
+ .${ROOT_CLASS} .restty-pane-search-button {
1091
+ min-width: 0;
1092
+ height: 28px;
1093
+ border: 1px solid transparent;
1094
+ border-radius: 6px;
1095
+ transition:
1096
+ background-color 120ms ease-out,
1097
+ border-color 120ms ease-out,
1098
+ color 120ms ease-out,
1099
+ transform 120ms ease-out;
1100
+ }
1101
+
1102
+ .${ROOT_CLASS} .restty-pane-search-input {
1103
+ padding: 0 10px;
1104
+ background: var(--restty-search-ui-input-background, #252525);
1105
+ color: var(--restty-search-ui-input-text, #d6d6d6);
1106
+ outline: none;
1107
+ font-size: 11px;
1108
+ letter-spacing: 0.01em;
1109
+ }
1110
+
1111
+ .${ROOT_CLASS} .restty-pane-search-input::placeholder {
1112
+ color: var(--restty-search-ui-input-placeholder, #868686);
1113
+ }
1114
+
1115
+ .${ROOT_CLASS} .restty-pane-search-input:focus {
1116
+ border-color: #3a3a3a;
1117
+ }
1118
+
1119
+ .${ROOT_CLASS} .restty-pane-search-button {
1120
+ padding: 0 8px;
1121
+ background: var(--restty-search-ui-button-background, transparent);
1122
+ color: var(--restty-search-ui-button-text, #d6d6d6);
1123
+ cursor: pointer;
1124
+ font-size: 10px;
1125
+ font-weight: 500;
1126
+ letter-spacing: 0.01em;
1127
+ }
1128
+
1129
+ .${ROOT_CLASS} .restty-pane-search-button:hover:not(:disabled) {
1130
+ background: var(--restty-search-ui-button-hover, #252525);
1131
+ }
1132
+
1133
+ .${ROOT_CLASS} .restty-pane-search-button:disabled {
1134
+ cursor: default;
1135
+ opacity: var(--restty-search-ui-button-disabled-opacity, 0.42);
1136
+ }
1137
+
1138
+ .${ROOT_CLASS} .restty-pane-search-button:focus-visible {
1139
+ outline: none;
1140
+ border-color: #3a3a3a;
1141
+ }
1142
+
1143
+ .${ROOT_CLASS} .restty-pane-search-status {
1144
+ min-width: 0;
1145
+ height: 28px;
1146
+ display: inline-flex;
1147
+ align-items: center;
1148
+ justify-content: center;
1149
+ padding: 0 8px;
1150
+ border-radius: 6px;
1151
+ background: #252525;
1152
+ border: 1px solid #2a2a2a;
1153
+ font-size: 10px;
1154
+ line-height: 1;
1155
+ color: var(--restty-search-ui-status, #868686);
1156
+ letter-spacing: 0.01em;
1157
+ white-space: nowrap;
1158
+ }
1159
+
1160
+ .${ROOT_CLASS} .restty-pane-search-status[data-empty="1"] {
1161
+ display: none;
1162
+ }
1163
+
1164
+ .${ROOT_CLASS} .restty-pane-search-status[data-active="1"] {
1165
+ color: var(--restty-search-ui-status-active, #d6d6d6);
1166
+ }
1167
+
1168
+ .${ROOT_CLASS} .restty-pane-search-status[data-complete="1"] {
1169
+ color: var(--restty-search-ui-status-complete, #868686);
1170
+ }
1171
+ `;
1172
+ var DEFAULT_STYLE_OPTIONS = {
1173
+ offsetTopPx: 10,
1174
+ offsetRightPx: 10,
1175
+ minWidthPx: 232,
1176
+ maxWidthPx: 332,
1177
+ zIndex: 8,
1178
+ borderRadiusPx: 8,
1179
+ backdropBlurPx: 8,
1180
+ panelBackground: "#161616",
1181
+ panelBorderColor: "#2a2a2a",
1182
+ panelTextColor: "#d6d6d6",
1183
+ panelShadow: "0 14px 40px rgba(0, 0, 0, 0.45)",
1184
+ inputBackground: "#252525",
1185
+ inputTextColor: "#d6d6d6",
1186
+ inputPlaceholderColor: "#868686",
1187
+ buttonBackground: "transparent",
1188
+ buttonTextColor: "#d6d6d6",
1189
+ buttonHoverBackground: "#252525",
1190
+ buttonDisabledOpacity: 0.42,
1191
+ statusTextColor: "#868686",
1192
+ statusActiveTextColor: "#d6d6d6",
1193
+ statusCompleteTextColor: "#868686"
1194
+ };
1195
+ function clampNumber2(value, min, max) {
1196
+ return Math.min(max, Math.max(min, value));
1197
+ }
1198
+ function normalizeColor2(value, fallback) {
1199
+ if (typeof value !== "string")
1200
+ return fallback;
1201
+ const trimmed = value.trim();
1202
+ return trimmed ? trimmed : fallback;
1203
+ }
1204
+ function normalizeSearchUiStyleOptions(options) {
1205
+ return {
1206
+ offsetTopPx: Number.isFinite(options?.offsetTopPx) ? clampNumber2(Number(options?.offsetTopPx), 0, 256) : DEFAULT_STYLE_OPTIONS.offsetTopPx,
1207
+ offsetRightPx: Number.isFinite(options?.offsetRightPx) ? clampNumber2(Number(options?.offsetRightPx), 0, 256) : DEFAULT_STYLE_OPTIONS.offsetRightPx,
1208
+ minWidthPx: Number.isFinite(options?.minWidthPx) ? clampNumber2(Number(options?.minWidthPx), 160, 800) : DEFAULT_STYLE_OPTIONS.minWidthPx,
1209
+ maxWidthPx: Number.isFinite(options?.maxWidthPx) ? clampNumber2(Number(options?.maxWidthPx), 180, 960) : DEFAULT_STYLE_OPTIONS.maxWidthPx,
1210
+ zIndex: Number.isFinite(options?.zIndex) ? clampNumber2(Number(options?.zIndex), 1, 9999) : DEFAULT_STYLE_OPTIONS.zIndex,
1211
+ borderRadiusPx: Number.isFinite(options?.borderRadiusPx) ? clampNumber2(Number(options?.borderRadiusPx), 0, 48) : DEFAULT_STYLE_OPTIONS.borderRadiusPx,
1212
+ backdropBlurPx: Number.isFinite(options?.backdropBlurPx) ? clampNumber2(Number(options?.backdropBlurPx), 0, 48) : DEFAULT_STYLE_OPTIONS.backdropBlurPx,
1213
+ panelBackground: normalizeColor2(options?.panelBackground, DEFAULT_STYLE_OPTIONS.panelBackground),
1214
+ panelBorderColor: normalizeColor2(options?.panelBorderColor, DEFAULT_STYLE_OPTIONS.panelBorderColor),
1215
+ panelTextColor: normalizeColor2(options?.panelTextColor, DEFAULT_STYLE_OPTIONS.panelTextColor),
1216
+ panelShadow: normalizeColor2(options?.panelShadow, DEFAULT_STYLE_OPTIONS.panelShadow),
1217
+ inputBackground: normalizeColor2(options?.inputBackground, DEFAULT_STYLE_OPTIONS.inputBackground),
1218
+ inputTextColor: normalizeColor2(options?.inputTextColor, DEFAULT_STYLE_OPTIONS.inputTextColor),
1219
+ inputPlaceholderColor: normalizeColor2(options?.inputPlaceholderColor, DEFAULT_STYLE_OPTIONS.inputPlaceholderColor),
1220
+ buttonBackground: normalizeColor2(options?.buttonBackground, DEFAULT_STYLE_OPTIONS.buttonBackground),
1221
+ buttonTextColor: normalizeColor2(options?.buttonTextColor, DEFAULT_STYLE_OPTIONS.buttonTextColor),
1222
+ buttonHoverBackground: normalizeColor2(options?.buttonHoverBackground, DEFAULT_STYLE_OPTIONS.buttonHoverBackground),
1223
+ buttonDisabledOpacity: Number.isFinite(options?.buttonDisabledOpacity) ? clampNumber2(Number(options?.buttonDisabledOpacity), 0, 1) : DEFAULT_STYLE_OPTIONS.buttonDisabledOpacity,
1224
+ statusTextColor: normalizeColor2(options?.statusTextColor, DEFAULT_STYLE_OPTIONS.statusTextColor),
1225
+ statusActiveTextColor: normalizeColor2(options?.statusActiveTextColor, DEFAULT_STYLE_OPTIONS.statusActiveTextColor),
1226
+ statusCompleteTextColor: normalizeColor2(options?.statusCompleteTextColor, DEFAULT_STYLE_OPTIONS.statusCompleteTextColor)
1227
+ };
1228
+ }
1229
+ function ensurePaneSearchUiStyles(doc) {
1230
+ if (doc.querySelector(`style[${STYLE_MARKER}="1"]`))
1231
+ return;
1232
+ const style = doc.createElement("style");
1233
+ style.setAttribute(STYLE_MARKER, "1");
1234
+ style.textContent = STYLE_TEXT;
1235
+ doc.head.appendChild(style);
1236
+ }
1237
+ function applySearchUiStyleOptions(root, options) {
1238
+ root.classList.add(ROOT_CLASS);
1239
+ root.style.setProperty("--restty-search-ui-top", `${options.offsetTopPx}px`);
1240
+ root.style.setProperty("--restty-search-ui-right", `${options.offsetRightPx}px`);
1241
+ root.style.setProperty("--restty-search-ui-min-width", `${options.minWidthPx}px`);
1242
+ root.style.setProperty("--restty-search-ui-max-width", `${options.maxWidthPx}px`);
1243
+ root.style.setProperty("--restty-search-ui-z-index", `${options.zIndex}`);
1244
+ root.style.setProperty("--restty-search-ui-radius", `${options.borderRadiusPx}px`);
1245
+ root.style.setProperty("--restty-search-ui-blur", `${options.backdropBlurPx}px`);
1246
+ root.style.setProperty("--restty-search-ui-background", options.panelBackground);
1247
+ root.style.setProperty("--restty-search-ui-border", options.panelBorderColor);
1248
+ root.style.setProperty("--restty-search-ui-text", options.panelTextColor);
1249
+ root.style.setProperty("--restty-search-ui-shadow", options.panelShadow);
1250
+ root.style.setProperty("--restty-search-ui-input-background", options.inputBackground);
1251
+ root.style.setProperty("--restty-search-ui-input-text", options.inputTextColor);
1252
+ root.style.setProperty("--restty-search-ui-input-placeholder", options.inputPlaceholderColor);
1253
+ root.style.setProperty("--restty-search-ui-button-background", options.buttonBackground);
1254
+ root.style.setProperty("--restty-search-ui-button-text", options.buttonTextColor);
1255
+ root.style.setProperty("--restty-search-ui-button-hover", options.buttonHoverBackground);
1256
+ root.style.setProperty("--restty-search-ui-button-disabled-opacity", options.buttonDisabledOpacity.toFixed(3));
1257
+ root.style.setProperty("--restty-search-ui-status", options.statusTextColor);
1258
+ root.style.setProperty("--restty-search-ui-status-active", options.statusActiveTextColor);
1259
+ root.style.setProperty("--restty-search-ui-status-complete", options.statusCompleteTextColor);
1260
+ }
1261
+ function clearSearchUiStyleOptions(root) {
1262
+ root.classList.remove(ROOT_CLASS);
1263
+ root.style.removeProperty("--restty-search-ui-top");
1264
+ root.style.removeProperty("--restty-search-ui-right");
1265
+ root.style.removeProperty("--restty-search-ui-min-width");
1266
+ root.style.removeProperty("--restty-search-ui-max-width");
1267
+ root.style.removeProperty("--restty-search-ui-z-index");
1268
+ root.style.removeProperty("--restty-search-ui-radius");
1269
+ root.style.removeProperty("--restty-search-ui-blur");
1270
+ root.style.removeProperty("--restty-search-ui-background");
1271
+ root.style.removeProperty("--restty-search-ui-border");
1272
+ root.style.removeProperty("--restty-search-ui-text");
1273
+ root.style.removeProperty("--restty-search-ui-shadow");
1274
+ root.style.removeProperty("--restty-search-ui-input-background");
1275
+ root.style.removeProperty("--restty-search-ui-input-text");
1276
+ root.style.removeProperty("--restty-search-ui-input-placeholder");
1277
+ root.style.removeProperty("--restty-search-ui-button-background");
1278
+ root.style.removeProperty("--restty-search-ui-button-text");
1279
+ root.style.removeProperty("--restty-search-ui-button-hover");
1280
+ root.style.removeProperty("--restty-search-ui-button-disabled-opacity");
1281
+ root.style.removeProperty("--restty-search-ui-status");
1282
+ root.style.removeProperty("--restty-search-ui-status-active");
1283
+ root.style.removeProperty("--restty-search-ui-status-complete");
1284
+ }
1285
+
1286
+ // src/surface/search-ui/controller.ts
1287
+ function defaultStatusFormatter(state) {
1288
+ if (!state.query)
1289
+ return "";
1290
+ if (state.pending)
1291
+ return "…";
1292
+ if (state.total <= 0)
1293
+ return "0";
1294
+ if (state.selectedIndex !== null) {
1295
+ return `${state.selectedIndex + 1}/${state.total}`;
1296
+ }
1297
+ return `${state.total}`;
1298
+ }
1299
+ function isNodeWithinRoot(root, target) {
1300
+ return target instanceof Node && root.contains(target);
1301
+ }
1302
+ function createPaneSearchUiController(options) {
1303
+ const enabled = options.enabled ?? true;
1304
+ const paneStates = new Map;
1305
+ const ownerDoc = options.root.ownerDocument ?? document;
1306
+ const ownerWin = ownerDoc.defaultView ?? window;
1307
+ let styleOptions = normalizeSearchUiStyleOptions(options.styles);
1308
+ const shortcutOptions = typeof options.shortcut === "object" ? options.shortcut : { enabled: options.shortcut !== false };
1309
+ const statusFormatter = options.statusFormatter ?? defaultStatusFormatter;
1310
+ const placeholder = options.placeholder ?? "Find in scrollback";
1311
+ const previousButtonText = options.previousButtonText ?? "↑";
1312
+ const nextButtonText = options.nextButtonText ?? "↓";
1313
+ const clearButtonText = options.clearButtonText ?? "Clear";
1314
+ const closeButtonText = options.closeButtonText ?? "×";
1315
+ if (enabled) {
1316
+ ensurePaneSearchUiStyles(ownerDoc);
1317
+ applySearchUiStyleOptions(options.root, styleOptions);
1318
+ }
1319
+ function getOpenPaneId() {
1320
+ for (const [paneId, state] of paneStates) {
1321
+ if (state.open)
1322
+ return paneId;
1323
+ }
1324
+ return null;
1325
+ }
1326
+ function syncPaneUi(paneState) {
1327
+ const { input, prevButton, nextButton, clearButton, root, status, state } = paneState;
1328
+ if (ownerDoc.activeElement !== input) {
1329
+ input.value = state.query;
1330
+ }
1331
+ const hasMatches = state.active && state.total > 0;
1332
+ prevButton.disabled = !hasMatches;
1333
+ nextButton.disabled = !hasMatches;
1334
+ clearButton.disabled = !state.query;
1335
+ root.dataset.open = paneState.open ? "1" : "0";
1336
+ status.textContent = statusFormatter(state);
1337
+ status.dataset.active = state.active ? "1" : "0";
1338
+ status.dataset.complete = state.complete ? "1" : "0";
1339
+ status.dataset.empty = status.textContent ? "0" : "1";
1340
+ }
1341
+ function restoreFocus(paneState) {
1342
+ const target = paneState?.pane.focusTarget ?? paneState?.pane.container ?? null;
1343
+ if (target instanceof HTMLElement) {
1344
+ target.focus({ preventScroll: true });
1345
+ }
1346
+ }
1347
+ function closeAllExcept(paneId) {
1348
+ for (const [id, paneState] of paneStates) {
1349
+ if (id === paneId || !paneState.open)
1350
+ continue;
1351
+ paneState.open = false;
1352
+ syncPaneUi(paneState);
1353
+ }
1354
+ }
1355
+ function focusSearchInput(paneState, selectAll = false) {
1356
+ paneState.input.focus({ preventScroll: true });
1357
+ if (selectAll)
1358
+ paneState.input.select();
1359
+ }
1360
+ function open(paneId, config = {}) {
1361
+ if (!enabled)
1362
+ return;
1363
+ const paneState = paneStates.get(paneId);
1364
+ if (!paneState)
1365
+ return;
1366
+ closeAllExcept(paneId);
1367
+ paneState.open = true;
1368
+ paneState.state = { ...paneState.pane.getSearchState() };
1369
+ syncPaneUi(paneState);
1370
+ focusSearchInput(paneState, config.selectAll ?? true);
1371
+ }
1372
+ function close(paneId, config = {}) {
1373
+ const paneState = paneStates.get(paneId);
1374
+ if (!paneState || !paneState.open)
1375
+ return;
1376
+ paneState.open = false;
1377
+ syncPaneUi(paneState);
1378
+ if (config.restoreFocus !== false) {
1379
+ restoreFocus(paneState);
1380
+ }
1381
+ }
1382
+ function toggle(paneId, config = {}) {
1383
+ if (paneStates.get(paneId)?.open) {
1384
+ close(paneId, config);
1385
+ return;
1386
+ }
1387
+ open(paneId, config);
1388
+ }
1389
+ function registerPane(pane) {
1390
+ if (!enabled)
1391
+ return;
1392
+ const root = ownerDoc.createElement("div");
1393
+ root.className = "restty-pane-search";
1394
+ root.dataset.open = "0";
1395
+ root.setAttribute("role", "search");
1396
+ root.setAttribute("aria-label", "Search terminal scrollback");
1397
+ const row = ownerDoc.createElement("div");
1398
+ row.className = "restty-pane-search-row";
1399
+ const input = ownerDoc.createElement("input");
1400
+ input.className = "restty-pane-search-input";
1401
+ input.type = "text";
1402
+ input.placeholder = placeholder;
1403
+ input.spellcheck = false;
1404
+ input.autocapitalize = "off";
1405
+ input.autocomplete = "off";
1406
+ input.autocorrect = "off";
1407
+ const prevButton = ownerDoc.createElement("button");
1408
+ prevButton.className = "restty-pane-search-button";
1409
+ prevButton.type = "button";
1410
+ prevButton.textContent = previousButtonText;
1411
+ prevButton.title = "Match above";
1412
+ const nextButton = ownerDoc.createElement("button");
1413
+ nextButton.className = "restty-pane-search-button";
1414
+ nextButton.type = "button";
1415
+ nextButton.textContent = nextButtonText;
1416
+ nextButton.title = "Match below";
1417
+ const clearButton = ownerDoc.createElement("button");
1418
+ clearButton.className = "restty-pane-search-button";
1419
+ clearButton.type = "button";
1420
+ clearButton.textContent = clearButtonText;
1421
+ const closeButton = ownerDoc.createElement("button");
1422
+ closeButton.className = "restty-pane-search-button";
1423
+ closeButton.type = "button";
1424
+ closeButton.textContent = closeButtonText;
1425
+ closeButton.title = "Close search";
1426
+ closeButton.setAttribute("aria-label", "Close search");
1427
+ const status = ownerDoc.createElement("div");
1428
+ status.className = "restty-pane-search-status";
1429
+ row.append(input, status, prevButton, nextButton, clearButton, closeButton);
1430
+ root.append(row);
1431
+ if (!pane.container.style.position) {
1432
+ pane.container.style.position = "relative";
1433
+ }
1434
+ pane.container.appendChild(root);
1435
+ const paneState = {
1436
+ pane,
1437
+ root,
1438
+ input,
1439
+ prevButton,
1440
+ nextButton,
1441
+ clearButton,
1442
+ closeButton,
1443
+ status,
1444
+ cleanupFns: [],
1445
+ state: { ...pane.getSearchState() },
1446
+ open: false
1447
+ };
1448
+ const onInput = () => {
1449
+ pane.setSearchQuery(input.value);
1450
+ };
1451
+ input.addEventListener("input", onInput);
1452
+ paneState.cleanupFns.push(() => {
1453
+ input.removeEventListener("input", onInput);
1454
+ });
1455
+ const onInputKeyDown = (event) => {
1456
+ if (event.key === "Enter") {
1457
+ event.preventDefault();
1458
+ if (event.shiftKey) {
1459
+ pane.searchNext();
1460
+ } else {
1461
+ pane.searchPrevious();
1462
+ }
1463
+ return;
1464
+ }
1465
+ if (event.key === "Escape") {
1466
+ event.preventDefault();
1467
+ close(pane.id);
1468
+ }
1469
+ };
1470
+ input.addEventListener("keydown", onInputKeyDown);
1471
+ paneState.cleanupFns.push(() => {
1472
+ input.removeEventListener("keydown", onInputKeyDown);
1473
+ });
1474
+ const onPrev = () => {
1475
+ pane.searchNext();
1476
+ focusSearchInput(paneState);
1477
+ };
1478
+ prevButton.addEventListener("click", onPrev);
1479
+ paneState.cleanupFns.push(() => {
1480
+ prevButton.removeEventListener("click", onPrev);
1481
+ });
1482
+ const onNext = () => {
1483
+ pane.searchPrevious();
1484
+ focusSearchInput(paneState);
1485
+ };
1486
+ nextButton.addEventListener("click", onNext);
1487
+ paneState.cleanupFns.push(() => {
1488
+ nextButton.removeEventListener("click", onNext);
1489
+ });
1490
+ const onClear = () => {
1491
+ pane.clearSearch();
1492
+ paneState.state = { ...pane.getSearchState() };
1493
+ syncPaneUi(paneState);
1494
+ focusSearchInput(paneState);
1495
+ };
1496
+ clearButton.addEventListener("click", onClear);
1497
+ paneState.cleanupFns.push(() => {
1498
+ clearButton.removeEventListener("click", onClear);
1499
+ });
1500
+ const onClose = () => {
1501
+ close(pane.id);
1502
+ };
1503
+ closeButton.addEventListener("click", onClose);
1504
+ paneState.cleanupFns.push(() => {
1505
+ closeButton.removeEventListener("click", onClose);
1506
+ });
1507
+ paneStates.set(pane.id, paneState);
1508
+ syncPaneUi(paneState);
1509
+ }
1510
+ function unregisterPane(paneId) {
1511
+ const paneState = paneStates.get(paneId);
1512
+ if (!paneState)
1513
+ return;
1514
+ for (const cleanup of paneState.cleanupFns) {
1515
+ cleanup();
1516
+ }
1517
+ paneState.root.remove();
1518
+ paneStates.delete(paneId);
1519
+ }
1520
+ function handleSearchState(paneId, state) {
1521
+ const paneState = paneStates.get(paneId);
1522
+ if (!paneState)
1523
+ return;
1524
+ paneState.state = { ...state };
1525
+ syncPaneUi(paneState);
1526
+ }
1527
+ function handleActivePaneChange(paneId) {
1528
+ const openPaneId = getOpenPaneId();
1529
+ if (openPaneId !== null && openPaneId !== paneId) {
1530
+ close(openPaneId, { restoreFocus: false });
1531
+ }
1532
+ }
1533
+ function isOpen(paneId) {
1534
+ return paneStates.get(paneId)?.open ?? false;
1535
+ }
1536
+ const onWindowKeyDown = (event) => {
1537
+ if (!enabled)
1538
+ return;
1539
+ if (shortcutOptions.enabled === false)
1540
+ return;
1541
+ const isMac = typeof navigator !== "undefined" && /mac/i.test(navigator.platform);
1542
+ const primaryModifier = isMac ? event.metaKey : event.ctrlKey;
1543
+ if (!primaryModifier || event.altKey || event.repeat || event.key.toLowerCase() !== "f") {
1544
+ return;
1545
+ }
1546
+ if (!isNodeWithinRoot(options.root, event.target) && !isNodeWithinRoot(options.root, ownerDoc.activeElement)) {
1547
+ return;
1548
+ }
1549
+ const pane = options.getFocusedPane() ?? options.getActivePane();
1550
+ if (!pane)
1551
+ return;
1552
+ if (shortcutOptions.canOpen && !shortcutOptions.canOpen(event, pane.id)) {
1553
+ return;
1554
+ }
1555
+ event.preventDefault();
1556
+ event.stopPropagation();
1557
+ open(pane.id, { selectAll: true });
1558
+ };
1559
+ ownerWin.addEventListener("keydown", onWindowKeyDown, { capture: true });
1560
+ return {
1561
+ registerPane,
1562
+ unregisterPane,
1563
+ handleSearchState,
1564
+ handleActivePaneChange,
1565
+ open,
1566
+ close,
1567
+ toggle,
1568
+ isOpen,
1569
+ getStyleOptions: () => ({ ...styleOptions }),
1570
+ setStyleOptions: (next) => {
1571
+ styleOptions = normalizeSearchUiStyleOptions({
1572
+ ...styleOptions,
1573
+ ...next
1574
+ });
1575
+ if (enabled) {
1576
+ applySearchUiStyleOptions(options.root, styleOptions);
1577
+ }
1578
+ },
1579
+ destroy: () => {
1580
+ ownerWin.removeEventListener("keydown", onWindowKeyDown, { capture: true });
1581
+ for (const paneId of Array.from(paneStates.keys())) {
1582
+ unregisterPane(paneId);
1583
+ }
1584
+ if (enabled) {
1585
+ clearSearchUiStyleOptions(options.root);
1586
+ }
1587
+ }
1588
+ };
1589
+ }
1590
+ // src/surface/panes/managed-pane-search-ui.ts
1591
+ function createManagedPaneSearchUiController(options) {
1592
+ const searchUiConfig = typeof options.searchUi === "object" && options.searchUi ? options.searchUi : undefined;
1593
+ return createPaneSearchUiController({
1594
+ root: options.root,
1595
+ enabled: options.searchUi === false ? false : searchUiConfig?.enabled ?? true,
1596
+ placeholder: searchUiConfig?.placeholder,
1597
+ previousButtonText: searchUiConfig?.previousButtonText,
1598
+ nextButtonText: searchUiConfig?.nextButtonText,
1599
+ clearButtonText: searchUiConfig?.clearButtonText,
1600
+ closeButtonText: searchUiConfig?.closeButtonText,
1601
+ statusFormatter: searchUiConfig?.statusFormatter,
1602
+ shortcut: searchUiConfig?.shortcut,
1603
+ styles: searchUiConfig?.styles,
1604
+ getActivePane: options.getActivePane,
1605
+ getFocusedPane: options.getFocusedPane
1606
+ });
1607
+ }
1608
+
1609
+ // src/surface/panes/managed-pane-manager.ts
1610
+ function createResttyManagedPaneManager(options) {
1611
+ const session = options.session ?? getDefaultResttyRuntimeSession();
1612
+ const autoInit = options.autoInit ?? true;
1613
+ const paneClassName = options.paneDom?.paneClassName ?? "pane";
1614
+ const canvasClassName = options.paneDom?.canvasClassName ?? "pane-canvas";
1615
+ const imeInputClassName = options.paneDom?.imeInputClassName ?? "pane-ime-input restty-pane-ime-input";
1616
+ const contextMenu = resolveManagedPaneContextMenu(options);
1617
+ const shortcuts = resolveManagedPaneShortcuts(options.shortcuts);
1618
+ let manager;
1619
+ const searchUiController = createManagedPaneSearchUiController({
1620
+ root: options.root,
1621
+ searchUi: options.searchUi,
1622
+ getActivePane: () => manager.getActivePane(),
1623
+ getFocusedPane: () => manager.getFocusedPane()
1624
+ });
1625
+ manager = createResttyPaneManager({
1626
+ root: options.root,
1627
+ minPaneSize: options.minPaneSize,
1628
+ styles: options.paneStyles,
1629
+ shortcuts,
1630
+ contextMenu,
1631
+ createPane: ({ id, sourcePane }) => {
1632
+ const pane = createManagedPane({
1633
+ id,
1634
+ sourcePane,
1635
+ dom: {
1636
+ paneClassName,
1637
+ canvasClassName,
1638
+ imeInputClassName
1639
+ },
1640
+ terminal: options.terminal,
1641
+ services: options.services,
1642
+ session,
1643
+ autoInit,
1644
+ onSearchState: (state) => {
1645
+ searchUiController.handleSearchState(id, state);
1646
+ }
1647
+ });
1648
+ searchUiController.registerPane(pane);
1649
+ return pane;
1650
+ },
1651
+ destroyPane: (pane) => {
1652
+ searchUiController.unregisterPane(pane.id);
1653
+ pane.destroyRuntime();
1654
+ },
1655
+ onPaneCreated: options.onPaneCreated,
1656
+ onPaneClosed: options.onPaneClosed,
1657
+ onPaneSplit: options.onPaneSplit,
1658
+ onActivePaneChange: (pane) => {
1659
+ searchUiController.handleActivePaneChange(pane?.id ?? null);
1660
+ options.onActivePaneChange?.(pane);
1661
+ },
1662
+ onLayoutChanged: () => {
1663
+ options.onLayoutChanged?.();
1664
+ }
1665
+ });
1666
+ const destroy = () => {
1667
+ searchUiController.destroy();
1668
+ manager.destroy();
1669
+ };
1670
+ return {
1671
+ ...manager,
1672
+ openPaneSearch: (id, config) => {
1673
+ searchUiController.open(id, config);
1674
+ },
1675
+ closePaneSearch: (id, config) => {
1676
+ searchUiController.close(id, config);
1677
+ },
1678
+ togglePaneSearch: (id, config) => {
1679
+ searchUiController.toggle(id, config);
1680
+ },
1681
+ isPaneSearchOpen: (id) => searchUiController.isOpen(id),
1682
+ getSearchUiStyleOptions: () => searchUiController.getStyleOptions(),
1683
+ setSearchUiStyleOptions: (next) => {
1684
+ searchUiController.setStyleOptions(next);
1685
+ },
1686
+ destroy
1687
+ };
1688
+ }
1689
+
1690
+ export { createResttyPaneManager, getResttyShortcutModifierLabel, createDefaultResttyPaneContextMenuItems, createResttyManagedPaneManager };