@qodalis/react-cli 2.0.0-beta.10 → 2.0.0-beta.11

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
@@ -247,562 +247,690 @@ function PositionIcon({ position, size = 20 }) {
247
247
  fillRect
248
248
  ] });
249
249
  }
250
- function CliPanel({ options: optionsProp, modules: modulesProp, processors: processorsProp, services: servicesProp, onClose, style, className }) {
251
- 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;
250
+ var CliPanel = import_react5.default.forwardRef(
251
+ function CliPanel2(props, ref) {
252
+ const { options: optionsProp, modules: modulesProp, processors: processorsProp, services: servicesProp, onClose, style, className } = props;
253
+ const config = useCliConfig();
254
+ const options = optionsProp ?? config.options;
255
+ const modules = modulesProp ?? config.modules;
256
+ const processors = processorsProp ?? config.processors;
257
+ const services = servicesProp ?? config.services;
258
+ const [internalPosition, setInternalPosition] = (0, import_react5.useState)(
259
+ () => (0, import_cli_core.loadPanelPosition)() ?? options?.position ?? "bottom"
260
+ );
261
+ const [internalCollapsed, setInternalCollapsed] = (0, import_react5.useState)(options?.isCollapsed ?? true);
262
+ const [internalHidden, setInternalHidden] = (0, import_react5.useState)(options?.isHidden ?? false);
263
+ const [internalMaximized, setInternalMaximized] = (0, import_react5.useState)(false);
264
+ const [internalHeight, setInternalHeight] = (0, import_react5.useState)(600);
265
+ const [internalWidth, setInternalWidth] = (0, import_react5.useState)(400);
266
+ const [internalActiveTabId, setInternalActiveTabId] = (0, import_react5.useState)(0);
267
+ const position = props.position !== void 0 ? props.position : internalPosition;
268
+ const collapsed = props.collapsed !== void 0 ? props.collapsed : internalCollapsed;
269
+ const hidden = props.hidden !== void 0 ? props.hidden : internalHidden;
270
+ const maximized = props.maximized !== void 0 ? props.maximized : internalMaximized;
271
+ const panelHeight = props.height !== void 0 ? props.height : internalHeight;
272
+ const panelWidth = props.width !== void 0 ? props.width : internalWidth;
273
+ const activeTabId = props.activeTabId !== void 0 ? props.activeTabId : internalActiveTabId;
274
+ const updateCollapsed = (0, import_react5.useCallback)((value) => {
275
+ setInternalCollapsed(value);
276
+ props.onCollapsedChange?.(value);
277
+ }, [props.onCollapsedChange]);
278
+ const updateHidden = (0, import_react5.useCallback)((value) => {
279
+ setInternalHidden(value);
280
+ props.onHiddenChange?.(value);
281
+ }, [props.onHiddenChange]);
282
+ const updateMaximized = (0, import_react5.useCallback)((value) => {
283
+ setInternalMaximized(value);
284
+ props.onMaximizedChange?.(value);
285
+ }, [props.onMaximizedChange]);
286
+ const updateHeight = (0, import_react5.useCallback)((value) => {
287
+ setInternalHeight(value);
288
+ props.onHeightChange?.(value);
289
+ }, [props.onHeightChange]);
290
+ const updateWidth = (0, import_react5.useCallback)((value) => {
291
+ setInternalWidth(value);
292
+ props.onWidthChange?.(value);
293
+ }, [props.onWidthChange]);
294
+ const updateActiveTabId = (0, import_react5.useCallback)((value) => {
295
+ setInternalActiveTabId(value);
296
+ props.onActiveTabIdChange?.(value);
297
+ }, [props.onActiveTabIdChange]);
298
+ const updatePosition = (0, import_react5.useCallback)((value) => {
299
+ setInternalPosition(value);
300
+ (0, import_cli_core.savePanelPosition)(value);
301
+ props.onPositionChange?.(value);
302
+ }, [props.onPositionChange]);
303
+ const closable = options?.closable ?? true;
304
+ const resizable = options?.resizable ?? true;
305
+ const isHorizontal = position === "left" || position === "right";
306
+ const hideable = options?.hideable ?? true;
307
+ const hideAlignment = options?.hideAlignment ?? "center";
308
+ const syncTheme = options?.syncTheme ?? false;
309
+ const [visible, setVisible] = (0, import_react5.useState)(true);
310
+ const [initialized, setInitialized] = (0, import_react5.useState)(false);
311
+ const preHideCollapsedRef = (0, import_react5.useRef)(true);
312
+ const [themeStyles, setThemeStyles] = (0, import_react5.useState)({});
313
+ const wrapperRef = (0, import_react5.useRef)(null);
314
+ const [tabs, setTabs] = (0, import_react5.useState)([]);
315
+ const [activePaneId, setActivePaneId] = (0, import_react5.useState)(0);
316
+ const nextIdRef = (0, import_react5.useRef)({ tab: 1, pane: 1 });
317
+ const [contextMenu, setContextMenu] = (0, import_react5.useState)({ visible: false, x: 0, y: 0, tabId: 0 });
318
+ const engineMapRef = (0, import_react5.useRef)(/* @__PURE__ */ new Map());
319
+ const activeTabIdRef = (0, import_react5.useRef)(activeTabId);
320
+ activeTabIdRef.current = activeTabId;
321
+ const activePaneIdRef = (0, import_react5.useRef)(activePaneId);
322
+ activePaneIdRef.current = activePaneId;
323
+ const [paneResizing, setPaneResizing] = (0, import_react5.useState)(false);
324
+ const paneResizeRef = (0, import_react5.useRef)({ tabId: 0, dividerIndex: 0, startX: 0, startWidths: [], containerWidth: 0 });
325
+ const [panelResizing, setPanelResizing] = (0, import_react5.useState)(false);
326
+ const panelResizeRef = (0, import_react5.useRef)({ startY: 0, startHeight: 0, startX: 0, startWidth: 0 });
327
+ const prevHeightRef = (0, import_react5.useRef)(600);
328
+ const prevWidthRef = (0, import_react5.useRef)(400);
329
+ const normalizePanes = (0, import_react5.useCallback)((panes) => {
330
+ const total = panes.reduce((s, p) => s + p.widthPercent, 0);
331
+ if (total === 0) return;
332
+ const scale = 100 / total;
333
+ panes.forEach((p) => {
334
+ p.widthPercent *= scale;
331
335
  });
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);
336
+ }, []);
337
+ const addTab = (0, import_react5.useCallback)((title) => {
338
+ const tabId = nextIdRef.current.tab++;
339
+ const paneId = nextIdRef.current.pane++;
340
+ const pane = { id: paneId, widthPercent: 100 };
341
+ const tab = { id: tabId, title: title ?? `Terminal ${tabId}`, isEditing: false, panes: [pane] };
342
+ setTabs((prev) => [...prev, tab]);
343
+ updateActiveTabId(tabId);
344
+ setActivePaneId(paneId);
345
+ props.onTabAdded?.({ tabId });
346
+ return tabId;
347
+ }, [updateActiveTabId, props.onTabAdded]);
348
+ const toggle = (0, import_react5.useCallback)(() => {
349
+ if (collapsed) {
350
+ updateCollapsed(false);
351
+ if (!initialized) {
352
+ setInitialized(true);
353
+ addTab();
354
+ }
355
+ } else {
356
+ updateCollapsed(true);
341
357
  }
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) {
358
+ }, [collapsed, initialized, updateCollapsed, addTab]);
359
+ const closeTab = (0, import_react5.useCallback)((id) => {
360
+ setTabs((prev) => {
361
+ const tab = prev.find((t) => t.id === id);
362
+ if (tab) {
363
+ tab.panes.forEach((p) => {
364
+ engineMapRef.current.get(p.id)?.destroy();
365
+ engineMapRef.current.delete(p.id);
366
+ });
367
+ }
368
+ const next = prev.filter((t) => t.id !== id);
369
+ if (next.length === 0) {
380
370
  setInitialized(false);
381
- setCollapsed(true);
371
+ updateCollapsed(true);
382
372
  return [];
383
373
  }
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
- });
396
- }
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;
374
+ if (activeTabIdRef.current === id) {
375
+ const oldIdx = prev.findIndex((t) => t.id === id);
376
+ const newIdx = Math.min(oldIdx, next.length - 1);
377
+ updateActiveTabId(next[newIdx].id);
402
378
  }
403
- return curr;
379
+ return next;
404
380
  });
405
- return next;
406
- });
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
- }
381
+ props.onTabClosed?.({ tabId: id });
382
+ }, [updateCollapsed, updateActiveTabId, props.onTabClosed]);
383
+ const selectTab = (0, import_react5.useCallback)((id) => {
384
+ updateActiveTabId(id);
494
385
  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;
386
+ const tab = prev.find((t) => t.id === id);
387
+ if (tab && tab.panes.length > 0) {
388
+ setActivePaneId(tab.panes[0].id);
500
389
  }
501
- return next;
390
+ return prev;
502
391
  });
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) {
392
+ }, [updateActiveTabId]);
393
+ const renameTab = (0, import_react5.useCallback)((tabId, title) => {
394
+ setTabs((prev) => prev.map((t) => t.id === tabId ? { ...t, title } : t));
395
+ }, []);
396
+ const startRename = (0, import_react5.useCallback)((tabId) => {
397
+ setTabs((prev) => prev.map((t) => ({ ...t, isEditing: t.id === tabId })));
398
+ }, []);
399
+ const commitRename = (0, import_react5.useCallback)((tabId, value) => {
400
+ const trimmed = value.trim();
401
+ setTabs((prev) => prev.map(
402
+ (t) => t.id === tabId ? { ...t, title: trimmed || t.title, isEditing: false } : t
403
+ ));
404
+ }, []);
405
+ const splitPane = (0, import_react5.useCallback)((tabId) => {
406
+ const targetTabId = tabId ?? activeTabIdRef.current;
407
+ const paneId = nextIdRef.current.pane++;
408
+ setTabs((prev) => prev.map((t) => {
409
+ if (t.id !== targetTabId) return t;
410
+ const newPanes = [...t.panes.map((p) => ({ ...p })), { id: paneId, widthPercent: 0 }];
411
+ normalizePanes(newPanes);
412
+ return { ...t, panes: newPanes };
413
+ }));
414
+ setActivePaneId(paneId);
415
+ props.onPaneSplit?.({ paneId, tabId: targetTabId });
416
+ return paneId;
417
+ }, [normalizePanes, props.onPaneSplit]);
418
+ const closePane = (0, import_react5.useCallback)((paneId) => {
419
+ setTabs((prev) => {
420
+ for (let i = 0; i < prev.length; i++) {
421
+ const tab = prev[i];
422
+ const idx = tab.panes.findIndex((p) => p.id === paneId);
423
+ if (idx === -1) continue;
424
+ if (tab.panes.length <= 1) {
425
+ setTimeout(() => closeTab(tab.id), 0);
426
+ return prev;
427
+ }
428
+ const newPanes = tab.panes.filter((p) => p.id !== paneId);
429
+ normalizePanes(newPanes);
430
+ const newTabs = [...prev];
431
+ newTabs[i] = { ...tab, panes: newPanes };
432
+ engineMapRef.current.get(paneId)?.destroy();
433
+ engineMapRef.current.delete(paneId);
434
+ if (activePaneIdRef.current === paneId) {
435
+ setActivePaneId(newPanes[Math.min(idx, newPanes.length - 1)].id);
436
+ }
437
+ props.onPaneClosed?.({ paneId });
438
+ return newTabs;
439
+ }
440
+ return prev;
441
+ });
442
+ }, [closeTab, normalizePanes, props.onPaneClosed]);
443
+ const closeContextMenu = (0, import_react5.useCallback)(() => {
444
+ setContextMenu((prev) => ({ ...prev, visible: false }));
445
+ }, []);
446
+ (0, import_react5.useEffect)(() => {
447
+ if (!contextMenu.visible) return;
448
+ const handler = () => closeContextMenu();
449
+ document.addEventListener("click", handler);
450
+ return () => document.removeEventListener("click", handler);
451
+ }, [contextMenu.visible, closeContextMenu]);
452
+ const onPanelResizeStart = (0, import_react5.useCallback)((e) => {
453
+ if (!resizable) return;
454
+ e.preventDefault();
455
+ if (collapsed) {
456
+ toggle();
457
+ }
458
+ setPanelResizing(true);
459
+ panelResizeRef.current = {
460
+ startY: e.clientY,
461
+ startHeight: panelHeight,
462
+ startX: e.clientX,
463
+ startWidth: panelWidth
464
+ };
465
+ }, [collapsed, toggle, panelHeight, panelWidth, resizable]);
466
+ (0, import_react5.useEffect)(() => {
467
+ if (!panelResizing) return;
468
+ const onMove = (e) => {
469
+ if (isHorizontal) {
470
+ const deltaX = position === "left" ? e.clientX - panelResizeRef.current.startX : panelResizeRef.current.startX - e.clientX;
471
+ let next = Math.max(100, panelResizeRef.current.startWidth + deltaX);
472
+ if (next > window.innerWidth) next = window.innerWidth;
473
+ updateWidth(next);
474
+ } else {
475
+ const deltaY = position === "top" ? e.clientY - panelResizeRef.current.startY : panelResizeRef.current.startY - e.clientY;
476
+ let next = Math.max(100, panelResizeRef.current.startHeight + deltaY);
477
+ if (next > window.innerHeight) next = window.innerHeight;
478
+ updateHeight(next);
479
+ }
480
+ };
481
+ const onUp = () => setPanelResizing(false);
482
+ document.addEventListener("mousemove", onMove);
483
+ document.addEventListener("mouseup", onUp);
484
+ return () => {
485
+ document.removeEventListener("mousemove", onMove);
486
+ document.removeEventListener("mouseup", onUp);
487
+ };
488
+ }, [panelResizing, isHorizontal, position, updateWidth, updateHeight]);
489
+ const onPaneResizeStart = (0, import_react5.useCallback)((e, tabId, dividerIndex) => {
490
+ e.preventDefault();
491
+ const tab = tabs.find((t) => t.id === tabId);
492
+ if (!tab) return;
493
+ setPaneResizing(true);
494
+ const container = e.target.closest(".cli-panel-panes-container");
495
+ paneResizeRef.current = {
496
+ tabId,
497
+ dividerIndex,
498
+ startX: e.clientX,
499
+ startWidths: tab.panes.map((p) => p.widthPercent),
500
+ containerWidth: container ? container.clientWidth : 1
501
+ };
502
+ }, [tabs]);
503
+ (0, import_react5.useEffect)(() => {
504
+ if (!paneResizing) return;
505
+ const onMove = (e) => {
506
+ const ref2 = paneResizeRef.current;
507
+ const deltaX = e.clientX - ref2.startX;
508
+ const deltaPct = deltaX / ref2.containerWidth * 100;
509
+ const i = ref2.dividerIndex;
510
+ let leftWidth = ref2.startWidths[i] + deltaPct;
511
+ let rightWidth = ref2.startWidths[i + 1] - deltaPct;
512
+ if (leftWidth < MIN_PANE_WIDTH_PERCENT) {
513
+ leftWidth = MIN_PANE_WIDTH_PERCENT;
514
+ rightWidth = ref2.startWidths[i] + ref2.startWidths[i + 1] - MIN_PANE_WIDTH_PERCENT;
515
+ }
516
+ if (rightWidth < MIN_PANE_WIDTH_PERCENT) {
517
+ rightWidth = MIN_PANE_WIDTH_PERCENT;
518
+ leftWidth = ref2.startWidths[i] + ref2.startWidths[i + 1] - MIN_PANE_WIDTH_PERCENT;
519
+ }
520
+ setTabs((prev) => {
521
+ const next = prev.map((t) => ({ ...t, panes: t.panes.map((p) => ({ ...p })) }));
522
+ const tab = next.find((t) => t.id === ref2.tabId);
523
+ if (tab) {
524
+ tab.panes[i].widthPercent = leftWidth;
525
+ tab.panes[i + 1].widthPercent = rightWidth;
526
+ }
527
+ return next;
528
+ });
529
+ };
530
+ const onUp = () => {
531
+ setPaneResizing(false);
532
+ document.body.classList.remove("cli-pane-resizing");
533
+ };
534
+ document.body.classList.add("cli-pane-resizing");
535
+ document.addEventListener("mousemove", onMove);
536
+ document.addEventListener("mouseup", onUp);
537
+ return () => {
538
+ document.removeEventListener("mousemove", onMove);
539
+ document.removeEventListener("mouseup", onUp);
540
+ };
541
+ }, [paneResizing]);
542
+ (0, import_react5.useEffect)(() => {
543
+ if (!syncTheme || !initialized) return;
544
+ const syncFromEngine = () => {
545
+ const engine = engineMapRef.current.values().next().value;
546
+ if (!engine) return;
547
+ const theme = engine.getTerminal().options.theme;
548
+ if (theme) setThemeStyles((0, import_cli_core.derivePanelThemeStyles)(theme));
549
+ };
550
+ const timer = setTimeout(() => {
551
+ syncFromEngine();
552
+ const container = wrapperRef.current?.querySelector(".terminal-container");
553
+ if (!container) return;
554
+ const observer = new MutationObserver(syncFromEngine);
555
+ observer.observe(container, { attributes: true, attributeFilter: ["style"] });
556
+ return () => observer.disconnect();
557
+ }, 200);
558
+ return () => clearTimeout(timer);
559
+ }, [syncTheme, initialized]);
560
+ const toggleMaximize = (0, import_react5.useCallback)(() => {
561
+ const nextMaximized = !maximized;
562
+ if (nextMaximized) {
537
563
  if (isHorizontal) {
538
564
  prevWidthRef.current = panelWidth;
539
- setPanelWidth(window.innerWidth);
565
+ updateWidth(window.innerWidth);
540
566
  } else {
541
567
  prevHeightRef.current = panelHeight;
542
- setPanelHeight(window.innerHeight);
568
+ updateHeight(window.innerHeight);
543
569
  }
544
570
  } else {
545
571
  if (isHorizontal) {
546
- setPanelWidth(prevWidthRef.current);
572
+ updateWidth(prevWidthRef.current);
547
573
  } else {
548
- setPanelHeight(prevHeightRef.current);
574
+ updateHeight(prevHeightRef.current);
549
575
  }
550
576
  }
551
- return !prev;
552
- });
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
- };
589
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
590
- hidden && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
591
- "button",
592
- {
593
- className: "cli-panel-hide-tab",
594
- "data-position": position,
595
- "data-hide-align": hideAlignment,
596
- style: themeStyles,
597
- title: "Show CLI",
598
- onClick: handleUnhide,
599
- 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 })
606
- ]
607
- }
608
- ),
609
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
610
- "div",
611
- {
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),
619
- 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
- )) })
577
+ updateMaximized(nextMaximized);
578
+ }, [maximized, panelHeight, panelWidth, isHorizontal, updateMaximized, updateWidth, updateHeight]);
579
+ const handleClose = (0, import_react5.useCallback)(() => {
580
+ setVisible(false);
581
+ onClose?.();
582
+ }, [onClose]);
583
+ const handleHide = (0, import_react5.useCallback)(() => {
584
+ preHideCollapsedRef.current = collapsed;
585
+ updateHidden(true);
586
+ }, [collapsed, updateHidden]);
587
+ const handleUnhide = (0, import_react5.useCallback)(() => {
588
+ updateHidden(false);
589
+ updateCollapsed(preHideCollapsedRef.current);
590
+ }, [updateHidden, updateCollapsed]);
591
+ const [positionDropdownOpen, setPositionDropdownOpen] = (0, import_react5.useState)(false);
592
+ const closePositionDropdown = (0, import_react5.useCallback)(() => setPositionDropdownOpen(false), []);
593
+ (0, import_react5.useEffect)(() => {
594
+ if (!positionDropdownOpen) return;
595
+ document.addEventListener("click", closePositionDropdown);
596
+ return () => document.removeEventListener("click", closePositionDropdown);
597
+ }, [positionDropdownOpen, closePositionDropdown]);
598
+ const handlePositionButtonClick = (0, import_react5.useCallback)((e) => {
599
+ e.stopPropagation();
600
+ setPositionDropdownOpen((prev) => !prev);
601
+ }, []);
602
+ const selectPosition = (0, import_react5.useCallback)((pos) => {
603
+ setPositionDropdownOpen(false);
604
+ updatePosition(pos);
605
+ }, [updatePosition]);
606
+ const getEngine = (0, import_react5.useCallback)((paneId) => {
607
+ const targetId = paneId ?? activePaneIdRef.current;
608
+ return engineMapRef.current.get(targetId);
609
+ }, []);
610
+ const getState = (0, import_react5.useCallback)(() => ({
611
+ collapsed,
612
+ hidden,
613
+ maximized,
614
+ position,
615
+ height: panelHeight,
616
+ width: panelWidth,
617
+ activeTabId,
618
+ activePaneId,
619
+ tabs: tabs.map((t) => ({
620
+ id: t.id,
621
+ title: t.title,
622
+ panes: t.panes.map((p) => ({ id: p.id, widthPercent: p.widthPercent }))
623
+ }))
624
+ }), [collapsed, hidden, maximized, position, panelHeight, panelWidth, activeTabId, activePaneId, tabs]);
625
+ import_react5.default.useImperativeHandle(ref, () => ({
626
+ open: () => {
627
+ if (!visible) {
628
+ setVisible(true);
629
+ }
630
+ if (hidden) {
631
+ updateHidden(false);
632
+ }
633
+ if (collapsed) {
634
+ updateCollapsed(false);
635
+ if (!initialized) {
636
+ setInitialized(true);
637
+ addTab();
638
+ }
639
+ }
640
+ },
641
+ collapse: () => {
642
+ if (!collapsed) updateCollapsed(true);
643
+ },
644
+ toggleCollapse: () => {
645
+ if (collapsed) {
646
+ updateCollapsed(false);
647
+ if (!initialized) {
648
+ setInitialized(true);
649
+ addTab();
650
+ }
651
+ } else {
652
+ updateCollapsed(true);
653
+ }
654
+ },
655
+ hide: () => {
656
+ if (!hidden) updateHidden(true);
657
+ },
658
+ unhide: () => {
659
+ if (hidden) updateHidden(false);
660
+ },
661
+ toggleHide: () => {
662
+ updateHidden(!hidden);
663
+ },
664
+ close: handleClose,
665
+ maximize: () => {
666
+ if (!maximized) updateMaximized(true);
667
+ },
668
+ restore: () => {
669
+ if (maximized) updateMaximized(false);
670
+ },
671
+ toggleMaximize: () => {
672
+ updateMaximized(!maximized);
673
+ },
674
+ resize: (dims) => {
675
+ if (dims.height !== void 0) updateHeight(dims.height);
676
+ if (dims.width !== void 0) updateWidth(dims.width);
677
+ },
678
+ setPosition: updatePosition,
679
+ addTab,
680
+ closeTab,
681
+ selectTab,
682
+ renameTab,
683
+ splitPane,
684
+ closePane,
685
+ getEngine,
686
+ getState
687
+ }), [
688
+ collapsed,
689
+ hidden,
690
+ maximized,
691
+ initialized,
692
+ updateCollapsed,
693
+ updateHidden,
694
+ updateMaximized,
695
+ updateHeight,
696
+ updateWidth,
697
+ updatePosition,
698
+ handleClose,
699
+ addTab,
700
+ closeTab,
701
+ selectTab,
702
+ renameTab,
703
+ splitPane,
704
+ closePane,
705
+ getEngine,
706
+ getState
707
+ ]);
708
+ if (!visible) return null;
709
+ const wrapperStyle = {
710
+ ...isHorizontal ? { width: `${panelWidth}px` } : { height: `${panelHeight}px` },
711
+ ...hidden ? { display: "none" } : {},
712
+ ...themeStyles,
713
+ ...style
714
+ };
715
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
716
+ hidden && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
717
+ "button",
718
+ {
719
+ className: "cli-panel-hide-tab",
720
+ "data-position": position,
721
+ "data-hide-align": hideAlignment,
722
+ style: themeStyles,
723
+ title: "Show CLI",
724
+ onClick: handleUnhide,
725
+ children: [
726
+ /* @__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: [
727
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polyline", { points: "4 17 10 11 4 5" }),
728
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "12", y1: "19", x2: "20", y2: "19" })
729
+ ] }),
730
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-hide-tab-label", children: "CLI" }),
731
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(HideTabChevron, { position })
732
+ ]
733
+ }
734
+ ),
735
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
736
+ "div",
737
+ {
738
+ ref: wrapperRef,
739
+ className: `cli-panel-wrapper ${collapsed ? "collapsed" : ""} ${maximized ? "maximized" : ""} ${panelResizing ? "resizing" : ""} ${className ?? ""}`,
740
+ style: wrapperStyle,
741
+ "data-position": position,
742
+ "data-resizable": String(resizable),
743
+ "data-closable": String(closable),
744
+ "data-hideable": String(hideable),
745
+ children: [
746
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-header", children: [
747
+ /* @__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" }) }),
748
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-header-content", children: [
749
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("p", { className: "cli-panel-title", children: [
750
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-title-icon", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(TerminalIcon, {}) }),
751
+ "CLI"
642
752
  ] }),
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: [
651
- /* @__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)(
653
- "li",
654
- {
655
- className: `cli-panel-tab ${tab.id === activeTabId ? "active" : ""}`,
656
- onClick: () => selectTab(tab.id),
657
- onDoubleClick: () => startRename(tab.id),
658
- onContextMenu: (e) => {
659
- e.preventDefault();
660
- e.stopPropagation();
661
- setContextMenu({ visible: true, x: e.clientX, y: e.clientY, tabId: tab.id });
662
- },
663
- children: tab.isEditing ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
664
- "input",
665
- {
666
- className: "cli-panel-tab-rename-input",
667
- type: "text",
668
- 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),
675
- onClick: (e) => e.stopPropagation(),
676
- onDoubleClick: (e) => e.stopPropagation()
677
- }
678
- ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
679
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-tab-title", children: tab.title }),
680
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
753
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-action-buttons", children: [
754
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-btn-position-wrapper", onClick: (e) => e.stopPropagation(), children: [
755
+ /* @__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 }) }),
756
+ 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)(
681
757
  "button",
682
758
  {
683
- className: "cli-panel-tab-close-btn",
684
- title: "Close tab",
685
- onClick: (e) => {
686
- e.stopPropagation();
687
- closeTab(tab.id);
688
- },
689
- children: "\xD7"
690
- }
691
- )
692
- ] })
693
- },
694
- tab.id
695
- )) }),
696
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-add-tab", title: "New terminal", onClick: addTab, children: "+" })
759
+ className: `cli-panel-position-dropdown-item${position === pos ? " active" : ""}`,
760
+ onClick: () => selectPosition(pos),
761
+ children: [
762
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PositionIcon, { position: pos, size: 16 }),
763
+ pos.charAt(0).toUpperCase() + pos.slice(1)
764
+ ]
765
+ },
766
+ pos
767
+ )) })
768
+ ] }),
769
+ 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, {}) }),
770
+ /* @__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, {}) }),
771
+ /* @__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 }) }),
772
+ 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, {}) })
773
+ ] })
774
+ ] })
697
775
  ] }),
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)(
701
- "div",
702
- {
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);
708
- },
709
- children: [
710
- tab.panes.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
711
- "button",
776
+ initialized && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-content", style: collapsed ? { display: "none" } : void 0, children: [
777
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "cli-panel-tabs", children: [
778
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ul", { className: "cli-panel-tab-list", children: tabs.map((tab) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
779
+ "li",
780
+ {
781
+ className: `cli-panel-tab ${tab.id === activeTabId ? "active" : ""}`,
782
+ onClick: () => selectTab(tab.id),
783
+ onDoubleClick: () => startRename(tab.id),
784
+ onContextMenu: (e) => {
785
+ e.preventDefault();
786
+ e.stopPropagation();
787
+ setContextMenu({ visible: true, x: e.clientX, y: e.clientY, tabId: tab.id });
788
+ },
789
+ children: tab.isEditing ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
790
+ "input",
712
791
  {
713
- className: "cli-panel-pane-close-btn",
714
- onClick: (e) => {
715
- e.stopPropagation();
716
- closePane(tab.id, pane.id);
792
+ className: "cli-panel-tab-rename-input",
793
+ type: "text",
794
+ defaultValue: tab.title,
795
+ autoFocus: true,
796
+ onKeyDown: (e) => {
797
+ if (e.key === "Enter") commitRename(tab.id, e.target.value);
798
+ else if (e.key === "Escape") setTabs((prev) => prev.map((t) => ({ ...t, isEditing: false })));
717
799
  },
718
- title: "Close pane",
719
- children: "\xD7"
720
- }
721
- ),
722
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CliContext.Provider, { value: { engine: null }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
723
- Cli,
724
- {
725
- options,
726
- modules,
727
- processors,
728
- services,
729
- snapshot: pane.snapshot,
730
- onReady: (engine) => engineMapRef.current.set(pane.id, engine)
800
+ onBlur: (e) => commitRename(tab.id, e.target.value),
801
+ onClick: (e) => e.stopPropagation(),
802
+ onDoubleClick: (e) => e.stopPropagation()
731
803
  }
732
- ) })
733
- ]
734
- }
735
- )
736
- ] }, pane.id)) }) }, tab.id)) })
737
- ] })
738
- ]
739
- }
740
- ),
741
- contextMenu.visible && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
742
- "div",
743
- {
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
- ]
802
- }
803
- )
804
- ] });
805
- }
804
+ ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
805
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "cli-panel-tab-title", children: tab.title }),
806
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
807
+ "button",
808
+ {
809
+ className: "cli-panel-tab-close-btn",
810
+ title: "Close tab",
811
+ onClick: (e) => {
812
+ e.stopPropagation();
813
+ closeTab(tab.id);
814
+ },
815
+ children: "\xD7"
816
+ }
817
+ )
818
+ ] })
819
+ },
820
+ tab.id
821
+ )) }),
822
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-add-tab", title: "New terminal", onClick: () => addTab(), children: "+" })
823
+ ] }),
824
+ /* @__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: [
825
+ 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" }) }),
826
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
827
+ "div",
828
+ {
829
+ className: `cli-panel-pane ${pane.id === activePaneId && tab.id === activeTabId ? "active-pane" : ""}`,
830
+ style: { flex: `${pane.widthPercent} 1 0` },
831
+ onClick: () => {
832
+ setActivePaneId(pane.id);
833
+ updateActiveTabId(tab.id);
834
+ },
835
+ children: [
836
+ tab.panes.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
837
+ "button",
838
+ {
839
+ className: "cli-panel-pane-close-btn",
840
+ onClick: (e) => {
841
+ e.stopPropagation();
842
+ closePane(pane.id);
843
+ },
844
+ title: "Close pane",
845
+ children: "\xD7"
846
+ }
847
+ ),
848
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CliContext.Provider, { value: { engine: null }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
849
+ Cli,
850
+ {
851
+ options,
852
+ modules,
853
+ processors,
854
+ services,
855
+ snapshot: pane.snapshot,
856
+ onReady: (engine) => engineMapRef.current.set(pane.id, engine)
857
+ }
858
+ ) })
859
+ ]
860
+ }
861
+ )
862
+ ] }, pane.id)) }) }, tab.id)) })
863
+ ] })
864
+ ]
865
+ }
866
+ ),
867
+ contextMenu.visible && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
868
+ "div",
869
+ {
870
+ className: "cli-panel-context-menu",
871
+ style: { left: contextMenu.x, top: contextMenu.y },
872
+ onClick: (e) => e.stopPropagation(),
873
+ children: [
874
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-context-menu-item", onClick: () => {
875
+ closeContextMenu();
876
+ startRename(contextMenu.tabId);
877
+ }, children: "Rename" }),
878
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-context-menu-item", onClick: () => {
879
+ closeContextMenu();
880
+ const tab = tabs.find((t) => t.id === contextMenu.tabId);
881
+ if (!tab) return;
882
+ const sourceEngine = engineMapRef.current.get(tab.panes[0]?.id);
883
+ const snapshot = sourceEngine?.snapshot();
884
+ const paneId = nextIdRef.current.pane++;
885
+ const tabId = nextIdRef.current.tab++;
886
+ const pane = { id: paneId, widthPercent: 100, snapshot };
887
+ const newTab = { id: tabId, title: `${tab.title} (copy)`, isEditing: false, panes: [pane] };
888
+ const idx = tabs.indexOf(tab);
889
+ setTabs((prev) => {
890
+ const next = [...prev];
891
+ next.splice(idx + 1, 0, newTab);
892
+ return next;
893
+ });
894
+ updateActiveTabId(tabId);
895
+ setActivePaneId(paneId);
896
+ props.onTabAdded?.({ tabId });
897
+ }, children: "Duplicate" }),
898
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-context-menu-item", onClick: () => {
899
+ closeContextMenu();
900
+ splitPane(contextMenu.tabId);
901
+ }, children: "Split Right" }),
902
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cli-panel-context-menu-separator" }),
903
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-context-menu-item", onClick: () => {
904
+ closeContextMenu();
905
+ closeTab(contextMenu.tabId);
906
+ }, children: "Close" }),
907
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-context-menu-item", disabled: tabs.length <= 1, onClick: () => {
908
+ closeContextMenu();
909
+ setTabs((prev) => prev.filter((t) => t.id === contextMenu.tabId));
910
+ updateActiveTabId(contextMenu.tabId);
911
+ }, children: "Close Others" }),
912
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-context-menu-item", onClick: () => {
913
+ const id = contextMenu.tabId;
914
+ closeContextMenu();
915
+ setTabs((prev) => {
916
+ const idx = prev.findIndex((t) => t.id === id);
917
+ if (idx === -1) return prev;
918
+ return prev.slice(0, idx + 1);
919
+ });
920
+ }, children: "Close to the Right" }),
921
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "cli-panel-context-menu-separator" }),
922
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "cli-panel-context-menu-item destructive", onClick: () => {
923
+ closeContextMenu();
924
+ setTabs([]);
925
+ setInitialized(false);
926
+ updateCollapsed(true);
927
+ }, children: "Close All" })
928
+ ]
929
+ }
930
+ )
931
+ ] });
932
+ }
933
+ );
806
934
 
807
935
  // src/CliProvider.tsx
808
936
  var import_react6 = require("react");