@qodalis/react-cli 2.0.0-beta.9 → 2.0.0-rc.1

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/dist/index.js CHANGED
@@ -33,7 +33,7 @@ __export(index_exports, {
33
33
  Cli: () => Cli,
34
34
  CliConfigProvider: () => CliConfigProvider,
35
35
  CliContext: () => CliContext,
36
- CliEngine: () => import_cli2.CliEngine,
36
+ CliEngine: () => import_cli5.CliEngine,
37
37
  CliPanel: () => CliPanel,
38
38
  CliProvider: () => CliProvider,
39
39
  useCli: () => useCli,
@@ -44,6 +44,7 @@ module.exports = __toCommonJS(index_exports);
44
44
 
45
45
  // src/Cli.tsx
46
46
  var import_react4 = require("react");
47
+ var import_cli2 = require("@qodalis/cli");
47
48
 
48
49
  // src/CliContext.tsx
49
50
  var import_react = require("react");
@@ -61,12 +62,11 @@ function useCliConfig() {
61
62
  }
62
63
  function CliConfigProvider({
63
64
  modules,
64
- processors,
65
65
  options,
66
66
  services,
67
67
  children
68
68
  }) {
69
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CliConfigCtx.Provider, { value: { modules, processors, options, services }, children });
69
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CliConfigCtx.Provider, { value: { modules, options, services }, children });
70
70
  }
71
71
 
72
72
  // src/useCliEngine.ts
@@ -82,19 +82,7 @@ function useCliEngine(containerRef, config) {
82
82
  initTimerRef.current = setTimeout(() => {
83
83
  initTimerRef.current = null;
84
84
  if (!container) return;
85
- const e = new import_cli.CliEngine(container, config?.options);
86
- e.registerService("cli-framework", "React");
87
- if (config?.services) {
88
- for (const [token, value] of Object.entries(config.services)) {
89
- e.registerService(token, value);
90
- }
91
- }
92
- if (config?.modules) {
93
- e.registerModules(config.modules);
94
- }
95
- if (config?.processors) {
96
- e.registerProcessors(config.processors);
97
- }
85
+ const e = (0, import_cli.createCliEngine)(container, { ...config, framework: "React" });
98
86
  engineRef.current = e;
99
87
  e.start().then(() => setEngine(e));
100
88
  }, 0);
@@ -118,7 +106,6 @@ function useCliEngine(containerRef, config) {
118
106
  var import_jsx_runtime2 = require("react/jsx-runtime");
119
107
  function Cli({
120
108
  modules,
121
- processors,
122
109
  options,
123
110
  services,
124
111
  snapshot,
@@ -130,16 +117,9 @@ function Cli({
130
117
  const config = useCliConfig();
131
118
  const containerRef = (0, import_react4.useRef)(null);
132
119
  const hasProvider = !!ctx.engine;
133
- const mergedModules = modules ?? config.modules;
134
- const mergedProcessors = processors ?? config.processors;
135
- const mergedOptions = options ?? config.options;
136
- const mergedServices = services ?? config.services;
137
- const optionsWithSnapshot = snapshot ? { ...mergedOptions, snapshot } : mergedOptions;
138
120
  const standaloneEngine = useCliEngine(containerRef, {
139
- modules: mergedModules,
140
- processors: mergedProcessors,
141
- options: optionsWithSnapshot,
142
- services: mergedServices,
121
+ ...(0, import_cli2.resolveCliHostConfig)({ modules, options, services }, config),
122
+ snapshot,
143
123
  disabled: hasProvider
144
124
  });
145
125
  const engine = ctx.engine ?? standaloneEngine;
@@ -163,502 +143,116 @@ function Cli({
163
143
 
164
144
  // src/CliPanel.tsx
165
145
  var import_react5 = __toESM(require("react"));
166
- var import_cli_core = require("@qodalis/cli-core");
146
+ var import_cli3 = require("@qodalis/cli");
167
147
  var import_jsx_runtime3 = require("react/jsx-runtime");
168
- var MIN_PANE_WIDTH_PERCENT = 10;
169
- var TerminalIcon = () => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
170
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polyline", { points: "4 17 10 11 4 5" }),
171
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "12", y1: "19", x2: "20", y2: "19" })
172
- ] });
173
- var MaximizeIcon = () => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", children: [
174
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polyline", { points: "15 3 21 3 21 9" }),
175
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polyline", { points: "9 21 3 21 3 15" }),
176
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "21", y1: "3", x2: "14", y2: "10" }),
177
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "3", y1: "21", x2: "10", y2: "14" })
178
- ] });
179
- var RestoreIcon = () => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", children: [
180
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polyline", { points: "4 14 10 14 10 20" }),
181
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polyline", { points: "20 10 14 10 14 4" }),
182
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "14", y1: "10", x2: "21", y2: "3" }),
183
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "3", y1: "21", x2: "10", y2: "14" })
184
- ] });
185
- var CloseIcon = () => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", children: [
186
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
187
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
188
- ] });
189
- var HideIcon = () => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", children: [
190
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "5", y1: "18", x2: "19", y2: "18" }),
191
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polyline", { points: "9 14 12 17 15 14" })
192
- ] });
193
- function CollapseChevron({ position, isCollapsed }) {
194
- let points;
195
- switch (position) {
196
- case "top":
197
- points = isCollapsed ? "6 9 12 15 18 9" : "18 15 12 9 6 15";
198
- break;
199
- case "left":
200
- points = isCollapsed ? "9 18 15 12 9 6" : "15 6 9 12 15 18";
201
- break;
202
- case "right":
203
- points = isCollapsed ? "15 6 9 12 15 18" : "9 18 15 12 9 6";
204
- break;
205
- default:
206
- points = isCollapsed ? "18 15 12 9 6 15" : "6 9 12 15 18 9";
207
- break;
148
+ function reactSvgProps(attrs) {
149
+ const props = {};
150
+ for (const [key, value] of Object.entries(attrs)) {
151
+ props[key.replace(/-([a-z])/g, (_, c) => c.toUpperCase())] = value;
208
152
  }
209
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polyline", { points }) });
153
+ return props;
210
154
  }
211
- function HideTabChevron({ position }) {
212
- let points;
213
- switch (position) {
214
- case "top":
215
- points = "6 9 12 15 18 9";
216
- break;
217
- case "left":
218
- points = "9 18 15 12 9 6";
219
- break;
220
- case "right":
221
- points = "15 6 9 12 15 18";
222
- break;
223
- default:
224
- points = "18 15 12 9 6 15";
225
- break;
226
- }
227
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { className: "cli-panel-hide-tab-arrow", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polyline", { points }) });
155
+ function Icon({ icon, className }) {
156
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { className, ...reactSvgProps((0, import_cli3.svgAttrs)(icon)), children: icon.shapes.map((shape, i) => import_react5.default.createElement(shape.tag, { key: i, ...reactSvgProps(shape.attrs) })) });
228
157
  }
229
- function PositionIcon({ position, size = 20 }) {
230
- let fillRect;
231
- switch (position) {
232
- case "top":
233
- fillRect = /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("rect", { x: "4", y: "4", width: "16", height: "5", rx: "1", fill: "currentColor", stroke: "none", opacity: "0.5" });
234
- break;
235
- case "left":
236
- fillRect = /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("rect", { x: "4", y: "4", width: "5", height: "16", rx: "1", fill: "currentColor", stroke: "none", opacity: "0.5" });
237
- break;
238
- case "right":
239
- fillRect = /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("rect", { x: "15", y: "4", width: "5", height: "16", rx: "1", fill: "currentColor", stroke: "none", opacity: "0.5" });
240
- break;
241
- default:
242
- fillRect = /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("rect", { x: "4", y: "15", width: "16", height: "5", rx: "1", fill: "currentColor", stroke: "none", opacity: "0.5" });
243
- break;
158
+ var focusAndSelect = (input) => {
159
+ if (input && document.activeElement !== input) {
160
+ input.focus();
161
+ input.select();
244
162
  }
245
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", children: [
246
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }),
247
- fillRect
248
- ] });
249
- }
250
- function CliPanel({ options: optionsProp, modules: modulesProp, processors: processorsProp, services: servicesProp, onClose, style, className }) {
163
+ };
164
+ var asStyle = (style) => style;
165
+ var CliPanel = import_react5.default.forwardRef(function CliPanel2(props, ref) {
251
166
  const config = useCliConfig();
252
- const options = optionsProp ?? config.options;
253
- const modules = modulesProp ?? config.modules;
254
- const processors = processorsProp ?? config.processors;
255
- const services = servicesProp ?? config.services;
256
- const [position, setPosition] = (0, import_react5.useState)(
257
- () => (0, import_cli_core.loadPanelPosition)() ?? options?.position ?? "bottom"
258
- );
259
- const closable = options?.closable ?? true;
260
- const resizable = options?.resizable ?? true;
261
- const isHorizontal = position === "left" || position === "right";
262
- const hideable = options?.hideable ?? true;
263
- const hideAlignment = options?.hideAlignment ?? "center";
264
- const syncTheme = options?.syncTheme ?? false;
265
- const [visible, setVisible] = (0, import_react5.useState)(true);
266
- const [collapsed, setCollapsed] = (0, import_react5.useState)(options?.isCollapsed ?? true);
267
- const [maximized, setMaximized] = (0, import_react5.useState)(false);
268
- const [panelHeight, setPanelHeight] = (0, import_react5.useState)(600);
269
- const [panelWidth, setPanelWidth] = (0, import_react5.useState)(400);
270
- const [initialized, setInitialized] = (0, import_react5.useState)(false);
271
- const [hidden, setHidden] = (0, import_react5.useState)(options?.isHidden ?? false);
272
- const preHideCollapsedRef = (0, import_react5.useRef)(true);
273
- const [themeStyles, setThemeStyles] = (0, import_react5.useState)({});
274
- const wrapperRef = (0, import_react5.useRef)(null);
275
- const [tabs, setTabs] = (0, import_react5.useState)([]);
276
- const [activeTabId, setActiveTabId] = (0, import_react5.useState)(0);
277
- const [activePaneId, setActivePaneId] = (0, import_react5.useState)(0);
278
- const nextIdRef = (0, import_react5.useRef)({ tab: 1, pane: 1 });
279
- const [contextMenu, setContextMenu] = (0, import_react5.useState)({ visible: false, x: 0, y: 0, tabId: 0 });
280
- const engineMapRef = (0, import_react5.useRef)(/* @__PURE__ */ new Map());
281
- const [paneResizing, setPaneResizing] = (0, import_react5.useState)(false);
282
- const paneResizeRef = (0, import_react5.useRef)({ tabId: 0, dividerIndex: 0, startX: 0, startWidths: [], containerWidth: 0 });
283
- const [panelResizing, setPanelResizing] = (0, import_react5.useState)(false);
284
- const panelResizeRef = (0, import_react5.useRef)({ startY: 0, startHeight: 0, startX: 0, startWidth: 0 });
285
- const prevHeightRef = (0, import_react5.useRef)(600);
286
- const prevWidthRef = (0, import_react5.useRef)(400);
287
- const addTab = (0, import_react5.useCallback)(() => {
288
- const paneId = nextIdRef.current.pane++;
289
- const tabId = nextIdRef.current.tab++;
290
- const pane = { id: paneId, widthPercent: 100 };
291
- const tab = { id: tabId, title: `Terminal ${tabId}`, isEditing: false, panes: [pane] };
292
- setTabs((prev) => [...prev, tab]);
293
- setActiveTabId(tabId);
294
- setActivePaneId(paneId);
295
- }, []);
296
- const toggle = (0, import_react5.useCallback)(() => {
297
- setCollapsed((prev) => {
298
- const next = !prev;
299
- if (!next && !initialized) {
300
- setInitialized(true);
301
- const paneId = nextIdRef.current.pane++;
302
- const tabId = nextIdRef.current.tab++;
303
- const pane = { id: paneId, widthPercent: 100 };
304
- const tab = { id: tabId, title: `Terminal ${tabId}`, isEditing: false, panes: [pane] };
305
- setTabs([tab]);
306
- setActiveTabId(tabId);
307
- setActivePaneId(paneId);
308
- }
309
- return next;
310
- });
311
- }, [initialized]);
312
- const closeTab = (0, import_react5.useCallback)((id) => {
313
- setTabs((prev) => {
314
- const tab = prev.find((t) => t.id === id);
315
- if (tab) {
316
- tab.panes.forEach((p) => engineMapRef.current.delete(p.id));
317
- }
318
- const next = prev.filter((t) => t.id !== id);
319
- if (next.length === 0) {
320
- setInitialized(false);
321
- setCollapsed(true);
322
- return [];
323
- }
324
- setActiveTabId((current) => {
325
- if (current === id) {
326
- const oldIdx = prev.findIndex((t) => t.id === id);
327
- const newIdx = Math.min(oldIdx, next.length - 1);
328
- return next[newIdx].id;
329
- }
330
- return current;
331
- });
332
- return next;
333
- });
334
- }, []);
335
- const selectTab = (0, import_react5.useCallback)((id) => {
336
- setActiveTabId(id);
337
- setTabs((prev) => {
338
- const tab = prev.find((t) => t.id === id);
339
- if (tab && tab.panes.length > 0) {
340
- setActivePaneId(tab.panes[0].id);
341
- }
342
- return prev;
343
- });
344
- }, []);
345
- const normalizePanes = (panes) => {
346
- const total = panes.reduce((s, p) => s + p.widthPercent, 0);
347
- if (total === 0) return;
348
- const scale = 100 / total;
349
- panes.forEach((p) => {
350
- p.widthPercent *= scale;
351
- });
352
- };
353
- const splitRight = (0, import_react5.useCallback)((tabId, afterPaneId) => {
354
- setTabs((prev) => {
355
- const next = prev.map((t) => ({ ...t, panes: t.panes.map((p) => ({ ...p })) }));
356
- const tab = next.find((t) => t.id === (tabId ?? activeTabId));
357
- if (!tab) return prev;
358
- const targetPaneId = afterPaneId ?? activePaneId;
359
- const idx = tab.panes.findIndex((p) => p.id === targetPaneId);
360
- const insertIdx = idx === -1 ? tab.panes.length : idx + 1;
361
- const newPaneId = nextIdRef.current.pane++;
362
- const newPane = { id: newPaneId, widthPercent: 0 };
363
- tab.panes.splice(insertIdx, 0, newPane);
364
- const evenWidth = 100 / tab.panes.length;
365
- tab.panes.forEach((p) => {
366
- p.widthPercent = evenWidth;
367
- });
368
- normalizePanes(tab.panes);
369
- setActivePaneId(newPaneId);
370
- return next;
371
- });
372
- }, [activeTabId, activePaneId]);
373
- const closePane = (0, import_react5.useCallback)((tabId, paneId) => {
374
- setTabs((prev) => {
375
- const tab = prev.find((t) => t.id === tabId);
376
- if (!tab) return prev;
377
- if (tab.panes.length <= 1) {
378
- const next2 = prev.filter((t) => t.id !== tabId);
379
- if (next2.length === 0) {
380
- setInitialized(false);
381
- setCollapsed(true);
382
- return [];
383
- }
384
- return next2;
385
- }
386
- const next = prev.map((t) => ({ ...t, panes: t.panes.map((p) => ({ ...p })) }));
387
- const updatedTab = next.find((t) => t.id === tabId);
388
- const idx = updatedTab.panes.findIndex((p) => p.id === paneId);
389
- if (idx === -1) return prev;
390
- updatedTab.panes.splice(idx, 1);
391
- const totalRemaining = updatedTab.panes.reduce((s, p) => s + p.widthPercent, 0);
392
- if (totalRemaining > 0) {
393
- updatedTab.panes.forEach((p) => {
394
- p.widthPercent = p.widthPercent / totalRemaining * 100;
395
- });
167
+ const options = (0, import_cli3.mergeCliOptions)(props.options, config.options);
168
+ const { modules, services } = (0, import_cli3.resolveCliHostConfig)(props, config);
169
+ const [controller] = (0, import_react5.useState)(
170
+ () => new import_cli3.CliPanelController({
171
+ config: options,
172
+ controlled: {
173
+ collapsed: props.collapsed,
174
+ hidden: props.hidden,
175
+ maximized: props.maximized,
176
+ activeTabId: props.activeTabId,
177
+ position: props.position,
178
+ height: props.height,
179
+ width: props.width
396
180
  }
397
- normalizePanes(updatedTab.panes);
398
- setActivePaneId((curr) => {
399
- if (curr === paneId) {
400
- const newIdx = Math.min(idx, updatedTab.panes.length - 1);
401
- return updatedTab.panes[newIdx].id;
402
- }
403
- return curr;
404
- });
405
- return next;
181
+ })
182
+ );
183
+ const s = (0, import_react5.useSyncExternalStore)(controller.subscribe, controller.getSnapshot, controller.getSnapshot);
184
+ const [rootElement, setRootElement] = (0, import_react5.useState)(null);
185
+ (0, import_react5.useLayoutEffect)(() => {
186
+ controller.setCallbacks({
187
+ onCollapsedChange: props.onCollapsedChange,
188
+ onHiddenChange: props.onHiddenChange,
189
+ onMaximizedChange: props.onMaximizedChange,
190
+ onActiveTabIdChange: props.onActiveTabIdChange,
191
+ onPositionChange: props.onPositionChange,
192
+ onHeightChange: props.onHeightChange,
193
+ onWidthChange: props.onWidthChange,
194
+ onClose: props.onClose,
195
+ onTabAdded: props.onTabAdded,
196
+ onTabClosed: props.onTabClosed,
197
+ onPaneSplit: props.onPaneSplit,
198
+ onPaneClosed: props.onPaneClosed
406
199
  });
407
- }, []);
408
- const startRename = (0, import_react5.useCallback)((tabId) => {
409
- setTabs((prev) => prev.map((t) => ({ ...t, isEditing: t.id === tabId })));
410
- }, []);
411
- const commitRename = (0, import_react5.useCallback)((tabId, value) => {
412
- const trimmed = value.trim();
413
- setTabs((prev) => prev.map(
414
- (t) => t.id === tabId ? { ...t, title: trimmed || t.title, isEditing: false } : t
415
- ));
416
- }, []);
417
- const closeContextMenu = (0, import_react5.useCallback)(() => {
418
- setContextMenu((prev) => ({ ...prev, visible: false }));
419
- }, []);
420
- (0, import_react5.useEffect)(() => {
421
- if (!contextMenu.visible) return;
422
- const handler = () => closeContextMenu();
423
- document.addEventListener("click", handler);
424
- return () => document.removeEventListener("click", handler);
425
- }, [contextMenu.visible, closeContextMenu]);
426
- const onPanelResizeStart = (0, import_react5.useCallback)((e) => {
427
- if (!resizable) return;
428
- e.preventDefault();
429
- if (collapsed) {
430
- toggle();
431
- }
432
- setPanelResizing(true);
433
- panelResizeRef.current = {
434
- startY: e.clientY,
435
- startHeight: panelHeight,
436
- startX: e.clientX,
437
- startWidth: panelWidth
438
- };
439
- }, [collapsed, toggle, panelHeight, panelWidth, resizable]);
440
- (0, import_react5.useEffect)(() => {
441
- if (!panelResizing) return;
442
- const onMove = (e) => {
443
- if (isHorizontal) {
444
- const deltaX = position === "left" ? e.clientX - panelResizeRef.current.startX : panelResizeRef.current.startX - e.clientX;
445
- let next = Math.max(100, panelResizeRef.current.startWidth + deltaX);
446
- if (next > window.innerWidth) next = window.innerWidth;
447
- setPanelWidth(next);
448
- } else {
449
- const deltaY = position === "top" ? e.clientY - panelResizeRef.current.startY : panelResizeRef.current.startY - e.clientY;
450
- let next = Math.max(100, panelResizeRef.current.startHeight + deltaY);
451
- if (next > window.innerHeight) next = window.innerHeight;
452
- setPanelHeight(next);
453
- }
454
- };
455
- const onUp = () => setPanelResizing(false);
456
- document.addEventListener("mousemove", onMove);
457
- document.addEventListener("mouseup", onUp);
458
- return () => {
459
- document.removeEventListener("mousemove", onMove);
460
- document.removeEventListener("mouseup", onUp);
461
- };
462
- }, [panelResizing, isHorizontal, position]);
463
- const onPaneResizeStart = (0, import_react5.useCallback)((e, tabId, dividerIndex) => {
464
- e.preventDefault();
465
- const tab = tabs.find((t) => t.id === tabId);
466
- if (!tab) return;
467
- setPaneResizing(true);
468
- const container = e.target.closest(".cli-panel-panes-container");
469
- paneResizeRef.current = {
470
- tabId,
471
- dividerIndex,
472
- startX: e.clientX,
473
- startWidths: tab.panes.map((p) => p.widthPercent),
474
- containerWidth: container ? container.clientWidth : 1
475
- };
476
- }, [tabs]);
477
- (0, import_react5.useEffect)(() => {
478
- if (!paneResizing) return;
479
- const onMove = (e) => {
480
- const ref = paneResizeRef.current;
481
- const deltaX = e.clientX - ref.startX;
482
- const deltaPct = deltaX / ref.containerWidth * 100;
483
- const i = ref.dividerIndex;
484
- let leftWidth = ref.startWidths[i] + deltaPct;
485
- let rightWidth = ref.startWidths[i + 1] - deltaPct;
486
- if (leftWidth < MIN_PANE_WIDTH_PERCENT) {
487
- leftWidth = MIN_PANE_WIDTH_PERCENT;
488
- rightWidth = ref.startWidths[i] + ref.startWidths[i + 1] - MIN_PANE_WIDTH_PERCENT;
489
- }
490
- if (rightWidth < MIN_PANE_WIDTH_PERCENT) {
491
- rightWidth = MIN_PANE_WIDTH_PERCENT;
492
- leftWidth = ref.startWidths[i] + ref.startWidths[i + 1] - MIN_PANE_WIDTH_PERCENT;
493
- }
494
- setTabs((prev) => {
495
- const next = prev.map((t) => ({ ...t, panes: t.panes.map((p) => ({ ...p })) }));
496
- const tab = next.find((t) => t.id === ref.tabId);
497
- if (tab) {
498
- tab.panes[i].widthPercent = leftWidth;
499
- tab.panes[i + 1].widthPercent = rightWidth;
500
- }
501
- return next;
502
- });
503
- };
504
- const onUp = () => {
505
- setPaneResizing(false);
506
- document.body.classList.remove("cli-pane-resizing");
507
- };
508
- document.body.classList.add("cli-pane-resizing");
509
- document.addEventListener("mousemove", onMove);
510
- document.addEventListener("mouseup", onUp);
511
- return () => {
512
- document.removeEventListener("mousemove", onMove);
513
- document.removeEventListener("mouseup", onUp);
514
- };
515
- }, [paneResizing]);
516
- (0, import_react5.useEffect)(() => {
517
- if (!syncTheme || !initialized) return;
518
- const syncFromEngine = () => {
519
- const engine = engineMapRef.current.values().next().value;
520
- if (!engine) return;
521
- const theme = engine.getTerminal().options.theme;
522
- if (theme) setThemeStyles((0, import_cli_core.derivePanelThemeStyles)(theme));
523
- };
524
- const timer = setTimeout(() => {
525
- syncFromEngine();
526
- const container = wrapperRef.current?.querySelector(".terminal-container");
527
- if (!container) return;
528
- const observer = new MutationObserver(syncFromEngine);
529
- observer.observe(container, { attributes: true, attributeFilter: ["style"] });
530
- return () => observer.disconnect();
531
- }, 200);
532
- return () => clearTimeout(timer);
533
- }, [syncTheme, initialized]);
534
- const toggleMaximize = (0, import_react5.useCallback)(() => {
535
- setMaximized((prev) => {
536
- if (!prev) {
537
- if (isHorizontal) {
538
- prevWidthRef.current = panelWidth;
539
- setPanelWidth(window.innerWidth);
540
- } else {
541
- prevHeightRef.current = panelHeight;
542
- setPanelHeight(window.innerHeight);
543
- }
544
- } else {
545
- if (isHorizontal) {
546
- setPanelWidth(prevWidthRef.current);
547
- } else {
548
- setPanelHeight(prevHeightRef.current);
549
- }
550
- }
551
- return !prev;
200
+ controller.setControlled({
201
+ collapsed: props.collapsed,
202
+ hidden: props.hidden,
203
+ maximized: props.maximized,
204
+ activeTabId: props.activeTabId,
205
+ position: props.position,
206
+ height: props.height,
207
+ width: props.width
552
208
  });
553
- }, [panelHeight, panelWidth, isHorizontal]);
554
- const handleClose = (0, import_react5.useCallback)(() => {
555
- setVisible(false);
556
- onClose?.();
557
- }, [onClose]);
558
- const handleHide = (0, import_react5.useCallback)(() => {
559
- preHideCollapsedRef.current = collapsed;
560
- setHidden(true);
561
- }, [collapsed]);
562
- const handleUnhide = (0, import_react5.useCallback)(() => {
563
- setHidden(false);
564
- setCollapsed(preHideCollapsedRef.current);
565
- }, []);
566
- const [positionDropdownOpen, setPositionDropdownOpen] = (0, import_react5.useState)(false);
567
- const closePositionDropdown = (0, import_react5.useCallback)(() => setPositionDropdownOpen(false), []);
568
- (0, import_react5.useEffect)(() => {
569
- if (!positionDropdownOpen) return;
570
- document.addEventListener("click", closePositionDropdown);
571
- return () => document.removeEventListener("click", closePositionDropdown);
572
- }, [positionDropdownOpen, closePositionDropdown]);
573
- const handlePositionButtonClick = (0, import_react5.useCallback)((e) => {
574
- e.stopPropagation();
575
- setPositionDropdownOpen((prev) => !prev);
576
- }, []);
577
- const selectPosition = (0, import_react5.useCallback)((pos) => {
578
- setPositionDropdownOpen(false);
579
- setPosition(pos);
580
- (0, import_cli_core.savePanelPosition)(pos);
581
- }, []);
582
- if (!visible) return null;
583
- const wrapperStyle = {
584
- ...isHorizontal ? { width: `${panelWidth}px` } : { height: `${panelHeight}px` },
585
- ...hidden ? { display: "none" } : {},
586
- ...themeStyles,
587
- ...style
588
- };
209
+ controller.setOptions(options);
210
+ });
211
+ (0, import_react5.useEffect)(() => controller.setRootElement(rootElement), [controller, rootElement]);
212
+ (0, import_react5.useEffect)(() => () => controller.destroy(), [controller]);
213
+ import_react5.default.useImperativeHandle(ref, () => controller, [controller]);
214
+ if (!s.visible) return null;
589
215
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
590
- hidden && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
216
+ s.hidden && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
591
217
  "button",
592
218
  {
593
219
  className: "cli-panel-hide-tab",
594
- "data-position": position,
595
- "data-hide-align": hideAlignment,
596
- style: themeStyles,
597
- title: "Show CLI",
598
- onClick: handleUnhide,
220
+ "data-position": s.position,
221
+ "data-hide-align": s.hideAlignment,
222
+ style: asStyle(s.themeStyles),
223
+ title: import_cli3.PANEL_LABELS.showCli,
224
+ onClick: () => controller.unhide(),
599
225
  children: [
600
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { className: "cli-panel-hide-tab-icon", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
601
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polyline", { points: "4 17 10 11 4 5" }),
602
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "12", y1: "19", x2: "20", y2: "19" })
603
- ] }),
604
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-hide-tab-label", children: "CLI" }),
605
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(HideTabChevron, { position })
226
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Icon, { icon: import_cli3.HIDE_TAB_TERMINAL_ICON, className: "cli-panel-hide-tab-icon" }),
227
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-hide-tab-label", children: import_cli3.PANEL_LABELS.title }),
228
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Icon, { icon: (0, import_cli3.hideTabChevronIcon)(s.position), className: "cli-panel-hide-tab-arrow" })
606
229
  ]
607
230
  }
608
231
  ),
609
232
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
610
233
  "div",
611
234
  {
612
- ref: wrapperRef,
613
- className: `cli-panel-wrapper ${collapsed ? "collapsed" : ""} ${maximized ? "maximized" : ""} ${panelResizing ? "resizing" : ""} ${className ?? ""}`,
614
- style: wrapperStyle,
615
- "data-position": position,
616
- "data-resizable": String(resizable),
617
- "data-closable": String(closable),
618
- "data-hideable": String(hideable),
235
+ ref: setRootElement,
236
+ className: (0, import_cli3.panelWrapperClass)(s, props.className),
237
+ style: { ...asStyle((0, import_cli3.panelWrapperStyle)(s)), ...props.style },
238
+ "data-position": s.position,
239
+ "data-resizable": String(s.resizable),
240
+ "data-closable": String(s.closable),
241
+ "data-hideable": String(s.hideable),
619
242
  children: [
620
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-header", children: [
621
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cli-panel-resize-bar", onMouseDown: onPanelResizeStart, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cli-panel-resize-grip" }) }),
622
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-header-content", children: [
623
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("p", { className: "cli-panel-title", children: [
624
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-title-icon", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(TerminalIcon, {}) }),
625
- "CLI"
626
- ] }),
627
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-action-buttons", children: [
628
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-btn-position-wrapper", onClick: (e) => e.stopPropagation(), children: [
629
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-btn cli-panel-btn-position", title: "Move panel", onClick: handlePositionButtonClick, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PositionIcon, { position }) }),
630
- positionDropdownOpen && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cli-panel-position-dropdown", children: ["bottom", "top", "left", "right"].map((pos) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
631
- "button",
632
- {
633
- className: `cli-panel-position-dropdown-item${position === pos ? " active" : ""}`,
634
- onClick: () => selectPosition(pos),
635
- children: [
636
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PositionIcon, { position: pos, size: 16 }),
637
- pos.charAt(0).toUpperCase() + pos.slice(1)
638
- ]
639
- },
640
- pos
641
- )) })
642
- ] }),
643
- hideable && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-btn cli-panel-btn-hide", title: "Hide", onClick: handleHide, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(HideIcon, {}) }),
644
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-btn", title: maximized ? "Restore" : "Maximize", disabled: collapsed, onClick: toggleMaximize, children: maximized ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(RestoreIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(MaximizeIcon, {}) }),
645
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-btn", title: collapsed ? "Expand" : "Collapse", onClick: toggle, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CollapseChevron, { position, isCollapsed: collapsed }) }),
646
- closable && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-btn cli-panel-btn-close", title: "Close", onClick: handleClose, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CloseIcon, {}) })
647
- ] })
648
- ] })
649
- ] }),
650
- initialized && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-content", style: collapsed ? { display: "none" } : void 0, children: [
243
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PanelHeader, { controller, s }),
244
+ s.initialized && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-content", style: s.collapsed ? { display: "none" } : void 0, children: [
651
245
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-tabs", children: [
652
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ul", { className: "cli-panel-tab-list", children: tabs.map((tab) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
246
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ul", { className: "cli-panel-tab-list", children: s.tabs.map((tab) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
653
247
  "li",
654
248
  {
655
- className: `cli-panel-tab ${tab.id === activeTabId ? "active" : ""}`,
656
- onClick: () => selectTab(tab.id),
657
- onDoubleClick: () => startRename(tab.id),
249
+ className: (0, import_cli3.panelTabClass)(s, tab),
250
+ onClick: () => controller.selectTab(tab.id),
251
+ onDoubleClick: () => controller.startRename(tab.id),
658
252
  onContextMenu: (e) => {
659
253
  e.preventDefault();
660
254
  e.stopPropagation();
661
- setContextMenu({ visible: true, x: e.clientX, y: e.clientY, tabId: tab.id });
255
+ controller.openContextMenu(tab.id, e.clientX, e.clientY);
662
256
  },
663
257
  children: tab.isEditing ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
664
258
  "input",
@@ -666,57 +260,62 @@ function CliPanel({ options: optionsProp, modules: modulesProp, processors: proc
666
260
  className: "cli-panel-tab-rename-input",
667
261
  type: "text",
668
262
  defaultValue: tab.title,
669
- autoFocus: true,
670
- onKeyDown: (e) => {
671
- if (e.key === "Enter") commitRename(tab.id, e.target.value);
672
- else if (e.key === "Escape") setTabs((prev) => prev.map((t) => ({ ...t, isEditing: false })));
673
- },
674
- onBlur: (e) => commitRename(tab.id, e.target.value),
263
+ ref: focusAndSelect,
264
+ onKeyDown: (e) => (0, import_cli3.handleRenameKey)(controller, tab.id, e.key, e.currentTarget.value),
265
+ onBlur: (e) => controller.commitRename(tab.id, e.target.value),
675
266
  onClick: (e) => e.stopPropagation(),
676
267
  onDoubleClick: (e) => e.stopPropagation()
677
268
  }
678
269
  ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
270
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: (0, import_cli3.panelTabDotClass)(s, tab) }),
679
271
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-tab-title", children: tab.title }),
680
272
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
681
273
  "button",
682
274
  {
683
275
  className: "cli-panel-tab-close-btn",
684
- title: "Close tab",
276
+ title: import_cli3.PANEL_LABELS.closeTab,
685
277
  onClick: (e) => {
686
278
  e.stopPropagation();
687
- closeTab(tab.id);
279
+ controller.closeTab(tab.id);
688
280
  },
689
- children: "\xD7"
281
+ children: import_cli3.CLOSE_GLYPH
690
282
  }
691
283
  )
692
284
  ] })
693
285
  },
694
286
  tab.id
695
287
  )) }),
696
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-add-tab", title: "New terminal", onClick: addTab, children: "+" })
288
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-add-tab", title: import_cli3.PANEL_LABELS.newTerminal, onClick: () => controller.addTab(), children: "+" })
697
289
  ] }),
698
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cli-panel-instances", children: tabs.map((tab) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cli-panel-instance", style: { display: tab.id === activeTabId ? void 0 : "none" }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: `cli-panel-panes-container ${paneResizing ? "resizing" : ""}`, children: tab.panes.map((pane, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react5.default.Fragment, { children: [
699
- i > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cli-panel-pane-divider", onMouseDown: (e) => onPaneResizeStart(e, tab.id, i - 1), children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cli-panel-pane-divider-grip" }) }),
700
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
290
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cli-panel-instances", children: s.tabs.map((tab) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cli-panel-instance", style: { display: tab.id === s.activeTabId ? void 0 : "none" }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: (0, import_cli3.panesContainerClass)(s), children: tab.panes.map((pane, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react5.default.Fragment, { children: [
291
+ i > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
701
292
  "div",
702
293
  {
703
- className: `cli-panel-pane ${pane.id === activePaneId && tab.id === activeTabId ? "active-pane" : ""}`,
704
- style: { flex: `${pane.widthPercent} 1 0` },
705
- onClick: () => {
706
- setActivePaneId(pane.id);
707
- setActiveTabId(tab.id);
294
+ className: "cli-panel-pane-divider",
295
+ onMouseDown: (e) => {
296
+ e.preventDefault();
297
+ controller.startPaneResize(tab.id, i - 1, e.clientX, (0, import_cli3.panesContainerWidth)(e.target));
708
298
  },
299
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cli-panel-pane-divider-grip" })
300
+ }
301
+ ),
302
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
303
+ "div",
304
+ {
305
+ className: (0, import_cli3.paneClass)(s, tab.id, pane.id),
306
+ style: (0, import_cli3.paneStyle)(pane.widthPercent),
307
+ onClick: () => controller.activatePane(tab.id, pane.id),
709
308
  children: [
710
309
  tab.panes.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
711
310
  "button",
712
311
  {
713
312
  className: "cli-panel-pane-close-btn",
313
+ title: import_cli3.PANEL_LABELS.closePane,
714
314
  onClick: (e) => {
715
315
  e.stopPropagation();
716
- closePane(tab.id, pane.id);
316
+ controller.closePane(pane.id);
717
317
  },
718
- title: "Close pane",
719
- children: "\xD7"
318
+ children: import_cli3.CLOSE_GLYPH
720
319
  }
721
320
  ),
722
321
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CliContext.Provider, { value: { engine: null }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
@@ -724,10 +323,9 @@ function CliPanel({ options: optionsProp, modules: modulesProp, processors: proc
724
323
  {
725
324
  options,
726
325
  modules,
727
- processors,
728
326
  services,
729
327
  snapshot: pane.snapshot,
730
- onReady: (engine) => engineMapRef.current.set(pane.id, engine)
328
+ onReady: (engine) => controller.registerEngine(tab.id, pane.id, engine)
731
329
  }
732
330
  ) })
733
331
  ]
@@ -738,78 +336,126 @@ function CliPanel({ options: optionsProp, modules: modulesProp, processors: proc
738
336
  ]
739
337
  }
740
338
  ),
741
- contextMenu.visible && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
339
+ s.contextMenu.visible && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cli-panel-context-menu", style: asStyle((0, import_cli3.contextMenuStyle)(s)), onClick: (e) => e.stopPropagation(), children: (0, import_cli3.contextMenuItems)(s).map((item) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react5.default.Fragment, { children: [
340
+ item.separatorBefore && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cli-panel-context-menu-separator" }),
341
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
342
+ "button",
343
+ {
344
+ className: (0, import_cli3.joinClasses)("cli-panel-context-menu-item", item.destructive && "destructive"),
345
+ disabled: item.disabled,
346
+ onClick: () => (0, import_cli3.runContextMenuAction)(controller, item.action),
347
+ children: item.label
348
+ }
349
+ )
350
+ ] }, item.action)) }),
351
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(StatusDropdown, { s })
352
+ ] });
353
+ });
354
+ function StatusDropdown({ s }) {
355
+ const dropdown = (0, import_cli3.openStatusDropdown)(s);
356
+ if (!dropdown) return null;
357
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-services-dropdown", style: asStyle(dropdown.style), onClick: (e) => e.stopPropagation(), children: [
358
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cli-panel-services-dropdown-header", children: dropdown.header }),
359
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cli-panel-services-dropdown-list", children: dropdown.rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-services-dropdown-item", children: [
360
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: row.dotClass }),
361
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-svc-info", children: [
362
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-svc-name", children: row.name }),
363
+ row.description && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-svc-desc", children: row.description })
364
+ ] }),
365
+ dropdown.kind === "servers" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-srv-meta", children: [
366
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-svc-status", children: row.status }),
367
+ row.meta.map((m) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-svc-desc", children: m }, m))
368
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-svc-status", children: row.status })
369
+ ] }, row.key)) }),
370
+ dropdown.rows.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cli-panel-services-dropdown-empty", children: dropdown.emptyText })
371
+ ] });
372
+ }
373
+ function PanelHeader({ controller, s }) {
374
+ const indicators = (0, import_cli3.statusIndicators)(s);
375
+ const openDropdown = (e, dropdown) => {
376
+ if (!dropdown) return;
377
+ e.stopPropagation();
378
+ if (dropdown === "services") controller.toggleServicesDropdown(e.currentTarget);
379
+ else controller.toggleServersDropdown(e.currentTarget);
380
+ };
381
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-header", children: [
382
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
742
383
  "div",
743
384
  {
744
- className: "cli-panel-context-menu",
745
- style: { left: contextMenu.x, top: contextMenu.y },
746
- onClick: (e) => e.stopPropagation(),
747
- children: [
748
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-context-menu-item", onClick: () => {
749
- closeContextMenu();
750
- startRename(contextMenu.tabId);
751
- }, children: "Rename" }),
752
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-context-menu-item", onClick: () => {
753
- closeContextMenu();
754
- const tab = tabs.find((t) => t.id === contextMenu.tabId);
755
- if (!tab) return;
756
- const sourceEngine = engineMapRef.current.get(tab.panes[0]?.id);
757
- const snapshot = sourceEngine?.snapshot();
758
- const paneId = nextIdRef.current.pane++;
759
- const tabId = nextIdRef.current.tab++;
760
- const pane = { id: paneId, widthPercent: 100, snapshot };
761
- const newTab = { id: tabId, title: `${tab.title} (copy)`, isEditing: false, panes: [pane] };
762
- const idx = tabs.indexOf(tab);
763
- setTabs((prev) => {
764
- const next = [...prev];
765
- next.splice(idx + 1, 0, newTab);
766
- return next;
767
- });
768
- setActiveTabId(tabId);
769
- setActivePaneId(paneId);
770
- }, children: "Duplicate" }),
771
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-context-menu-item", onClick: () => {
772
- closeContextMenu();
773
- splitRight(contextMenu.tabId);
774
- }, children: "Split Right" }),
775
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cli-panel-context-menu-separator" }),
776
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-context-menu-item", onClick: () => {
777
- closeContextMenu();
778
- closeTab(contextMenu.tabId);
779
- }, children: "Close" }),
780
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-context-menu-item", disabled: tabs.length <= 1, onClick: () => {
781
- closeContextMenu();
782
- setTabs((prev) => prev.filter((t) => t.id === contextMenu.tabId));
783
- setActiveTabId(contextMenu.tabId);
784
- }, children: "Close Others" }),
785
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-context-menu-item", onClick: () => {
786
- const id = contextMenu.tabId;
787
- closeContextMenu();
788
- setTabs((prev) => {
789
- const idx = prev.findIndex((t) => t.id === id);
790
- if (idx === -1) return prev;
791
- return prev.slice(0, idx + 1);
792
- });
793
- }, children: "Close to the Right" }),
794
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cli-panel-context-menu-separator" }),
795
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-context-menu-item destructive", onClick: () => {
796
- closeContextMenu();
797
- setTabs([]);
798
- setInitialized(false);
799
- setCollapsed(true);
800
- }, children: "Close All" })
801
- ]
385
+ className: "cli-panel-resize-bar",
386
+ onMouseDown: (e) => {
387
+ if (!s.resizable) return;
388
+ e.preventDefault();
389
+ controller.startPanelResize(e.clientX, e.clientY);
390
+ },
391
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cli-panel-resize-grip" })
802
392
  }
803
- )
393
+ ),
394
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-header-content", children: [
395
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("p", { className: "cli-panel-title", children: [
396
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-title-icon", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Icon, { icon: import_cli3.TERMINAL_ICON }) }),
397
+ import_cli3.PANEL_LABELS.title
398
+ ] }),
399
+ s.showStatusIndicators && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cli-panel-status-indicators", children: indicators.map((item) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
400
+ "span",
401
+ {
402
+ className: (0, import_cli3.joinClasses)("cli-panel-status-item", item.itemClass),
403
+ onClick: item.dropdown ? (e) => openDropdown(e, item.dropdown) : void 0,
404
+ children: [
405
+ item.dotClass && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: `cli-panel-status-dot ${item.dotClass}` }),
406
+ item.icon && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: (0, import_cli3.joinClasses)("cli-panel-status-icon", item.iconClass), children: item.icon }),
407
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-status-label", children: item.label })
408
+ ]
409
+ },
410
+ item.key
411
+ )) }),
412
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cli-panel-status-indicators-compact", children: indicators.map((item) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
413
+ "span",
414
+ {
415
+ className: (0, import_cli3.joinClasses)("cli-panel-status-compact-item", item.compactClass),
416
+ title: item.title,
417
+ onClick: item.dropdown ? (e) => openDropdown(e, item.dropdown) : void 0,
418
+ children: [
419
+ item.compactDotClass && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: item.compactDotClass }),
420
+ item.dotClass && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: `cli-panel-status-dot ${item.dotClass}` }),
421
+ item.icon && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: (0, import_cli3.joinClasses)("cli-panel-status-icon", item.iconClass), children: item.icon })
422
+ ]
423
+ },
424
+ item.key
425
+ )) }),
426
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-action-buttons", children: [
427
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-btn-position-wrapper", onClick: (e) => e.stopPropagation(), children: [
428
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-btn cli-panel-btn-position", title: import_cli3.PANEL_LABELS.movePanel, onClick: () => controller.togglePositionDropdown(), children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Icon, { icon: (0, import_cli3.positionIcon)(s.position) }) }),
429
+ s.positionDropdownOpen && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cli-panel-position-dropdown", children: (0, import_cli3.positionOptions)(s).map((opt) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("button", { className: opt.className, onClick: () => controller.setPosition(opt.position), children: [
430
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Icon, { icon: (0, import_cli3.positionIcon)(opt.position, 16) }),
431
+ opt.label
432
+ ] }, opt.position)) })
433
+ ] }),
434
+ s.hideable && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-btn cli-panel-btn-hide", title: import_cli3.PANEL_LABELS.hide, onClick: () => controller.hide(), children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Icon, { icon: import_cli3.HIDE_ICON }) }),
435
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
436
+ "button",
437
+ {
438
+ className: "cli-panel-btn",
439
+ title: s.maximized ? import_cli3.PANEL_LABELS.restore : import_cli3.PANEL_LABELS.maximize,
440
+ disabled: s.collapsed,
441
+ onClick: () => controller.toggleMaximize(),
442
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Icon, { icon: s.maximized ? import_cli3.RESTORE_ICON : import_cli3.MAXIMIZE_ICON })
443
+ }
444
+ ),
445
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-btn", title: s.collapsed ? import_cli3.PANEL_LABELS.expand : import_cli3.PANEL_LABELS.collapse, onClick: () => controller.toggle(), children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Icon, { icon: (0, import_cli3.collapseChevronIcon)(s.position, s.collapsed) }) }),
446
+ s.closable && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-btn cli-panel-btn-close", title: import_cli3.PANEL_LABELS.close, onClick: () => controller.close(), children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Icon, { icon: import_cli3.CLOSE_ICON }) })
447
+ ] })
448
+ ] }),
449
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: (0, import_cli3.glowLineClass)(s) })
804
450
  ] });
805
451
  }
806
452
 
807
453
  // src/CliProvider.tsx
808
454
  var import_react6 = require("react");
455
+ var import_cli4 = require("@qodalis/cli");
809
456
  var import_jsx_runtime4 = require("react/jsx-runtime");
810
457
  function CliProvider({
811
458
  modules,
812
- processors,
813
459
  options,
814
460
  services,
815
461
  children,
@@ -817,12 +463,7 @@ function CliProvider({
817
463
  }) {
818
464
  const config = useCliConfig();
819
465
  const containerRef = (0, import_react6.useRef)(null);
820
- const engine = useCliEngine(containerRef, {
821
- modules: modules ?? config.modules,
822
- processors: processors ?? config.processors,
823
- options: options ?? config.options,
824
- services: services ?? config.services
825
- });
466
+ const engine = useCliEngine(containerRef, (0, import_cli4.resolveCliHostConfig)({ modules, options, services }, config));
826
467
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(CliContext.Provider, { value: { engine }, children: [
827
468
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { ref: containerRef, style: { height: "100%", ...style } }),
828
469
  children
@@ -830,7 +471,7 @@ function CliProvider({
830
471
  }
831
472
 
832
473
  // src/index.tsx
833
- var import_cli2 = require("@qodalis/cli");
474
+ var import_cli5 = require("@qodalis/cli");
834
475
  // Annotate the CommonJS export names for ESM import in node:
835
476
  0 && (module.exports = {
836
477
  Cli,