@qodalis/vue-cli 2.0.0-beta.8 → 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.mjs CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  h,
8
8
  inject
9
9
  } from "vue";
10
- import { CliEngine } from "@qodalis/cli";
10
+ import { createCliEngine, resolveCliHostConfig } from "@qodalis/cli";
11
11
 
12
12
  // src/cliInjection.ts
13
13
  var CliInjectionKey = /* @__PURE__ */ Symbol("qodalis-cli");
@@ -22,10 +22,6 @@ var CliConfigProvider = defineComponent({
22
22
  type: Array,
23
23
  default: void 0
24
24
  },
25
- processors: {
26
- type: Array,
27
- default: void 0
28
- },
29
25
  options: {
30
26
  type: Object,
31
27
  default: void 0
@@ -38,7 +34,6 @@ var CliConfigProvider = defineComponent({
38
34
  setup(props, { slots }) {
39
35
  provide(CliConfigKey, {
40
36
  modules: props.modules,
41
- processors: props.processors,
42
37
  options: props.options,
43
38
  services: props.services
44
39
  });
@@ -54,10 +49,6 @@ var Cli = defineComponent2({
54
49
  type: Array,
55
50
  default: void 0
56
51
  },
57
- processors: {
58
- type: Array,
59
- default: void 0
60
- },
61
52
  options: {
62
53
  type: Object,
63
54
  default: void 0
@@ -88,26 +79,10 @@ var Cli = defineComponent2({
88
79
  if (ctx) {
89
80
  return () => null;
90
81
  }
91
- const modules = props.modules ?? config?.modules;
92
- const processors = props.processors ?? config?.processors;
93
- const options = props.options ?? config?.options;
94
- const services = props.services ?? config?.services;
82
+ const hostConfig = resolveCliHostConfig(props, config);
95
83
  onMounted(async () => {
96
84
  if (!containerRef.value) return;
97
- const engineOptions = props.snapshot ? { ...options, snapshot: props.snapshot } : options;
98
- engine = new CliEngine(containerRef.value, engineOptions);
99
- engine.registerService("cli-framework", "Vue");
100
- if (services) {
101
- for (const [token, value] of Object.entries(services)) {
102
- engine.registerService(token, value);
103
- }
104
- }
105
- if (modules) {
106
- engine.registerModules(modules);
107
- }
108
- if (processors) {
109
- engine.registerProcessors(processors);
110
- }
85
+ engine = createCliEngine(containerRef.value, { ...hostConfig, snapshot: props.snapshot, framework: "Vue" });
111
86
  await engine.start();
112
87
  emit("ready", engine);
113
88
  });
@@ -124,915 +99,471 @@ var Cli = defineComponent2({
124
99
  });
125
100
 
126
101
  // src/CliPanel.ts
102
+ import { defineComponent as defineComponent3, computed, inject as inject2, watch, h as h2, onBeforeUnmount as onBeforeUnmount2, onMounted as onMounted2, shallowRef, Fragment } from "vue";
127
103
  import {
128
- defineComponent as defineComponent3,
129
- ref as ref2,
130
- computed,
131
- inject as inject2,
132
- h as h2,
133
- onMounted as onMounted2,
134
- onBeforeUnmount as onBeforeUnmount2
135
- } from "vue";
136
- import { derivePanelThemeStyles, loadPanelPosition, savePanelPosition } from "@qodalis/cli-core";
137
- var MIN_PANE_WIDTH_PERCENT = 10;
138
- function normalizePanes(panes) {
139
- const total = panes.reduce((s, p) => s + p.widthPercent, 0);
140
- if (total === 0) return;
141
- const scale = 100 / total;
142
- panes.forEach((p) => {
143
- p.widthPercent *= scale;
144
- });
145
- }
146
- var svgAttrs = {
147
- width: "20",
148
- height: "20",
149
- viewBox: "0 0 24 24",
150
- fill: "none",
151
- stroke: "currentColor",
152
- "stroke-width": "1.8",
153
- "stroke-linecap": "round",
154
- "stroke-linejoin": "round"
155
- };
156
- var terminalIcon = () => h2("svg", { ...svgAttrs, width: "22", height: "22", "stroke-width": "2" }, [
157
- h2("polyline", { points: "4 17 10 11 4 5" }),
158
- h2("line", { x1: "12", y1: "19", x2: "20", y2: "19" })
159
- ]);
160
- var maximizeIcon = () => h2("svg", svgAttrs, [
161
- h2("polyline", { points: "15 3 21 3 21 9" }),
162
- h2("polyline", { points: "9 21 3 21 3 15" }),
163
- h2("line", { x1: "21", y1: "3", x2: "14", y2: "10" }),
164
- h2("line", { x1: "3", y1: "21", x2: "10", y2: "14" })
165
- ]);
166
- var restoreIcon = () => h2("svg", svgAttrs, [
167
- h2("polyline", { points: "4 14 10 14 10 20" }),
168
- h2("polyline", { points: "20 10 14 10 14 4" }),
169
- h2("line", { x1: "14", y1: "10", x2: "21", y2: "3" }),
170
- h2("line", { x1: "3", y1: "21", x2: "10", y2: "14" })
171
- ]);
172
- var closeIcon = () => h2("svg", svgAttrs, [
173
- h2("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
174
- h2("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
175
- ]);
176
- var hideIcon = () => h2("svg", svgAttrs, [
177
- h2("line", { x1: "5", y1: "18", x2: "19", y2: "18" }),
178
- h2("polyline", { points: "9 14 12 17 15 14" })
179
- ]);
180
- function collapseChevron(position, isCollapsed) {
181
- let points;
182
- switch (position) {
183
- case "top":
184
- points = isCollapsed ? "6 9 12 15 18 9" : "18 15 12 9 6 15";
185
- break;
186
- case "left":
187
- points = isCollapsed ? "9 18 15 12 9 6" : "15 6 9 12 15 18";
188
- break;
189
- case "right":
190
- points = isCollapsed ? "15 6 9 12 15 18" : "9 18 15 12 9 6";
191
- break;
192
- default:
193
- points = isCollapsed ? "18 15 12 9 6 15" : "6 9 12 15 18 9";
194
- break;
195
- }
196
- return h2("svg", svgAttrs, [h2("polyline", { points })]);
197
- }
198
- function hideTabChevron(position) {
199
- let points;
200
- switch (position) {
201
- case "top":
202
- points = "6 9 12 15 18 9";
203
- break;
204
- case "left":
205
- points = "9 18 15 12 9 6";
206
- break;
207
- case "right":
208
- points = "15 6 9 12 15 18";
209
- break;
210
- default:
211
- points = "18 15 12 9 6 15";
212
- break;
213
- }
214
- return h2("svg", { class: "cli-panel-hide-tab-arrow", ...svgAttrs, width: "14", height: "14", "stroke-width": "2.5" }, [
215
- h2("polyline", { points })
216
- ]);
217
- }
218
- function positionIcon(pos, size = 20) {
219
- let fillRect;
220
- switch (pos) {
221
- case "top":
222
- fillRect = h2("rect", { x: "4", y: "4", width: "16", height: "5", rx: "1", fill: "currentColor", stroke: "none", opacity: "0.5" });
223
- break;
224
- case "left":
225
- fillRect = h2("rect", { x: "4", y: "4", width: "5", height: "16", rx: "1", fill: "currentColor", stroke: "none", opacity: "0.5" });
226
- break;
227
- case "right":
228
- fillRect = h2("rect", { x: "15", y: "4", width: "5", height: "16", rx: "1", fill: "currentColor", stroke: "none", opacity: "0.5" });
229
- break;
230
- default:
231
- fillRect = h2("rect", { x: "4", y: "15", width: "16", height: "5", rx: "1", fill: "currentColor", stroke: "none", opacity: "0.5" });
232
- break;
233
- }
234
- return h2("svg", { ...svgAttrs, width: String(size), height: String(size) }, [
235
- h2("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }),
236
- fillRect
237
- ]);
104
+ CliPanelController,
105
+ CLOSE_GLYPH,
106
+ CLOSE_ICON,
107
+ HIDE_ICON,
108
+ HIDE_TAB_TERMINAL_ICON,
109
+ MAXIMIZE_ICON,
110
+ PANEL_LABELS,
111
+ RESTORE_ICON,
112
+ TERMINAL_ICON,
113
+ collapseChevronIcon,
114
+ contextMenuItems,
115
+ contextMenuStyle,
116
+ glowLineClass,
117
+ handleRenameKey,
118
+ hideTabChevronIcon,
119
+ joinClasses,
120
+ mergeCliOptions,
121
+ openStatusDropdown,
122
+ paneClass,
123
+ paneStyle,
124
+ panelTabClass,
125
+ panelTabDotClass,
126
+ panelWrapperClass,
127
+ panelWrapperStyle,
128
+ panesContainerClass,
129
+ panesContainerWidth,
130
+ positionIcon,
131
+ positionOptions,
132
+ resolveCliHostConfig as resolveCliHostConfig2,
133
+ runContextMenuAction,
134
+ statusIndicators,
135
+ svgAttrs
136
+ } from "@qodalis/cli";
137
+ var PANEL_REF_METHODS = [
138
+ "open",
139
+ "collapse",
140
+ "toggleCollapse",
141
+ "hide",
142
+ "unhide",
143
+ "toggleHide",
144
+ "close",
145
+ "maximize",
146
+ "restore",
147
+ "toggleMaximize",
148
+ "resize",
149
+ "setPosition",
150
+ "addTab",
151
+ "closeTab",
152
+ "selectTab",
153
+ "renameTab",
154
+ "splitPane",
155
+ "closePane",
156
+ "getEngine",
157
+ "getState"
158
+ ];
159
+ function icon(svg, className) {
160
+ return h2("svg", { class: className, ...svgAttrs(svg) }, svg.shapes.map((shape) => h2(shape.tag, shape.attrs)));
238
161
  }
162
+ var stop = (e) => e.stopPropagation();
239
163
  var CliPanel = defineComponent3({
240
164
  name: "CliPanel",
241
165
  props: {
242
- options: {
243
- type: Object,
244
- default: void 0
245
- },
166
+ options: { type: Object, default: void 0 },
246
167
  modules: { type: Array, default: void 0 },
247
- processors: {
248
- type: Array,
249
- default: void 0
250
- },
251
- services: {
252
- type: Object,
253
- default: void 0
254
- },
255
- onClose: {
256
- type: Function,
257
- default: void 0
258
- },
259
- style: {
260
- type: Object,
261
- default: void 0
262
- },
168
+ services: { type: Object, default: void 0 },
169
+ onClose: { type: Function, default: void 0 },
170
+ collapsed: { type: Boolean, default: void 0 },
171
+ hidden: { type: Boolean, default: void 0 },
172
+ maximized: { type: Boolean, default: void 0 },
173
+ activeTabId: { type: Number, default: void 0 },
174
+ position: { type: String, default: void 0 },
175
+ height: { type: Number, default: void 0 },
176
+ width: { type: Number, default: void 0 },
177
+ style: { type: Object, default: void 0 },
263
178
  class: { type: String, default: void 0 }
264
179
  },
265
- setup(props) {
180
+ emits: [
181
+ "update:collapsed",
182
+ "update:hidden",
183
+ "update:maximized",
184
+ "update:activeTabId",
185
+ "update:position",
186
+ "update:height",
187
+ "update:width",
188
+ "close",
189
+ "tab-added",
190
+ "tab-closed",
191
+ "pane-split",
192
+ "pane-closed"
193
+ ],
194
+ setup(props, { expose, emit }) {
266
195
  const config = inject2(CliConfigKey, null);
267
- const mergedOptions = computed(() => props.options ?? config?.options);
268
- const mergedModules = computed(() => props.modules ?? config?.modules);
269
- const mergedProcessors = computed(() => props.processors ?? config?.processors);
270
- const mergedServices = computed(() => props.services ?? config?.services);
271
- const visible = ref2(true);
272
- const collapsed = ref2(mergedOptions.value?.isCollapsed ?? true);
273
- const maximized = ref2(false);
274
- const panelHeight = ref2(600);
275
- const panelWidth = ref2(400);
276
- const prevHeight = ref2(600);
277
- const prevWidth = ref2(400);
278
- const initialized = ref2(false);
279
- const hidden = ref2(props.options?.isHidden ?? false);
280
- let preHideCollapsed = true;
281
- const position = ref2(loadPanelPosition() ?? mergedOptions.value?.position ?? "bottom");
282
- const closable = computed(() => mergedOptions.value?.closable ?? true);
283
- const resizable = computed(() => mergedOptions.value?.resizable ?? true);
284
- const isHorizontal = computed(() => position.value === "left" || position.value === "right");
285
- const hideable = computed(() => mergedOptions.value?.hideable ?? true);
286
- const hideAlignment = computed(() => mergedOptions.value?.hideAlignment ?? "center");
287
- const syncTheme = computed(() => mergedOptions.value?.syncTheme ?? false);
288
- const themeStyles = ref2({});
289
- const tabs = ref2([]);
290
- const activeTabId = ref2(0);
291
- const activePaneId = ref2(0);
292
- let nextTabId = 1;
293
- let nextPaneId = 1;
294
- const contextMenu = ref2({
295
- visible: false,
296
- x: 0,
297
- y: 0,
298
- tabId: 0
299
- });
300
- const engineMap = /* @__PURE__ */ new Map();
301
- const paneResizing = ref2(false);
302
- let paneResizeState = {
303
- tabId: 0,
304
- dividerIndex: 0,
305
- startX: 0,
306
- startWidths: [],
307
- containerWidth: 0
308
- };
309
- const panelResizing = ref2(false);
310
- let panelResizeState = { startY: 0, startHeight: 0, startX: 0, startWidth: 0 };
311
- function addTab() {
312
- const paneId = nextPaneId++;
313
- const tabId = nextTabId++;
314
- const pane = { id: paneId, widthPercent: 100 };
315
- const tab = {
316
- id: tabId,
317
- title: `Terminal ${tabId}`,
318
- isEditing: false,
319
- panes: [pane]
320
- };
321
- tabs.value.push(tab);
322
- activeTabId.value = tabId;
323
- activePaneId.value = paneId;
324
- }
325
- let themeObserver;
326
- function syncThemeFromEngine() {
327
- const engine = engineMap.values().next().value;
328
- if (!engine) return;
329
- const theme = engine.getTerminal().options.theme;
330
- if (theme) themeStyles.value = derivePanelThemeStyles(theme);
331
- }
332
- function setupThemeSync() {
333
- if (!syncTheme.value) return;
334
- setTimeout(() => {
335
- syncThemeFromEngine();
336
- const container = document.querySelector(".cli-panel-wrapper .terminal-container");
337
- if (!container) return;
338
- themeObserver?.disconnect();
339
- themeObserver = new MutationObserver(syncThemeFromEngine);
340
- themeObserver.observe(container, { attributes: true, attributeFilter: ["style"] });
341
- }, 200);
342
- }
343
- onMounted2(() => {
344
- document.addEventListener("click", closePositionDropdown);
196
+ const mergedOptions = computed(() => mergeCliOptions(props.options, config?.options));
197
+ const hostConfig = computed(() => resolveCliHostConfig2(props, config));
198
+ const controlled = () => ({
199
+ collapsed: props.collapsed,
200
+ hidden: props.hidden,
201
+ maximized: props.maximized,
202
+ activeTabId: props.activeTabId,
203
+ position: props.position,
204
+ height: props.height,
205
+ width: props.width
345
206
  });
346
- onBeforeUnmount2(() => {
347
- themeObserver?.disconnect();
348
- document.removeEventListener("click", closePositionDropdown);
349
- });
350
- function toggle() {
351
- collapsed.value = !collapsed.value;
352
- if (!collapsed.value && !initialized.value) {
353
- initialized.value = true;
354
- addTab();
355
- setupThemeSync();
356
- }
357
- }
358
- function closeTab(id) {
359
- const idx = tabs.value.findIndex((t) => t.id === id);
360
- if (idx === -1) return;
361
- tabs.value.splice(idx, 1);
362
- if (tabs.value.length === 0) {
363
- initialized.value = false;
364
- collapsed.value = true;
365
- return;
366
- }
367
- if (activeTabId.value === id) {
368
- const newIdx = Math.min(idx, tabs.value.length - 1);
369
- selectTab(tabs.value[newIdx].id);
370
- }
371
- }
372
- function selectTab(id) {
373
- activeTabId.value = id;
374
- const tab = tabs.value.find((t) => t.id === id);
375
- if (tab && tab.panes.length > 0) {
376
- activePaneId.value = tab.panes[0].id;
377
- }
378
- }
379
- function splitRight(tabId) {
380
- const tab = tabs.value.find(
381
- (t) => t.id === (tabId ?? activeTabId.value)
382
- );
383
- if (!tab) return;
384
- const idx = tab.panes.findIndex((p) => p.id === activePaneId.value);
385
- const insertIdx = idx === -1 ? tab.panes.length : idx + 1;
386
- const newPaneId = nextPaneId++;
387
- tab.panes.splice(insertIdx, 0, { id: newPaneId, widthPercent: 0 });
388
- const evenWidth = 100 / tab.panes.length;
389
- tab.panes.forEach((p) => {
390
- p.widthPercent = evenWidth;
391
- });
392
- normalizePanes(tab.panes);
393
- activePaneId.value = newPaneId;
394
- }
395
- function closePane(tabId, paneId) {
396
- const tab = tabs.value.find((t) => t.id === tabId);
397
- if (!tab) return;
398
- if (tab.panes.length <= 1) {
399
- closeTab(tabId);
400
- return;
401
- }
402
- const idx = tab.panes.findIndex((p) => p.id === paneId);
403
- if (idx === -1) return;
404
- tab.panes.splice(idx, 1);
405
- const total = tab.panes.reduce((s, p) => s + p.widthPercent, 0);
406
- if (total > 0)
407
- tab.panes.forEach((p) => {
408
- p.widthPercent = p.widthPercent / total * 100;
409
- });
410
- normalizePanes(tab.panes);
411
- if (activePaneId.value === paneId) {
412
- activePaneId.value = tab.panes[Math.min(idx, tab.panes.length - 1)].id;
413
- }
414
- }
415
- function startRename(tabId) {
416
- tabs.value.forEach((t) => {
417
- t.isEditing = t.id === tabId;
418
- });
419
- }
420
- function commitRename(tabId, value) {
421
- const trimmed = value.trim();
422
- const tab = tabs.value.find((t) => t.id === tabId);
423
- if (tab) {
424
- if (trimmed) tab.title = trimmed;
425
- tab.isEditing = false;
426
- }
427
- }
428
- function closeContextMenu() {
429
- contextMenu.value = { ...contextMenu.value, visible: false };
430
- }
431
- function onDocClick() {
432
- if (contextMenu.value.visible) closeContextMenu();
433
- }
434
- onMounted2(() => document.addEventListener("click", onDocClick));
435
- onBeforeUnmount2(
436
- () => document.removeEventListener("click", onDocClick)
437
- );
438
- function onPanelResizeStart(e) {
439
- if (!resizable.value) return;
440
- e.preventDefault();
441
- if (collapsed.value) toggle();
442
- panelResizing.value = true;
443
- panelResizeState = {
444
- startY: e.clientY,
445
- startHeight: panelHeight.value,
446
- startX: e.clientX,
447
- startWidth: panelWidth.value
448
- };
449
- }
450
- function onPanelResizeMove(e) {
451
- if (!panelResizing.value) return;
452
- if (isHorizontal.value) {
453
- const deltaX = position.value === "left" ? e.clientX - panelResizeState.startX : panelResizeState.startX - e.clientX;
454
- let next = Math.max(100, panelResizeState.startWidth + deltaX);
455
- if (next > window.innerWidth) next = window.innerWidth;
456
- panelWidth.value = next;
457
- } else {
458
- const deltaY = position.value === "top" ? e.clientY - panelResizeState.startY : panelResizeState.startY - e.clientY;
459
- let next = Math.max(100, panelResizeState.startHeight + deltaY);
460
- if (next > window.innerHeight) next = window.innerHeight;
461
- panelHeight.value = next;
207
+ const controller = new CliPanelController({
208
+ config: mergedOptions.value,
209
+ controlled: controlled(),
210
+ callbacks: {
211
+ onCollapsedChange: (v) => emit("update:collapsed", v),
212
+ onHiddenChange: (v) => emit("update:hidden", v),
213
+ onMaximizedChange: (v) => emit("update:maximized", v),
214
+ onActiveTabIdChange: (v) => emit("update:activeTabId", v),
215
+ onPositionChange: (v) => emit("update:position", v),
216
+ onHeightChange: (v) => emit("update:height", v),
217
+ onWidthChange: (v) => emit("update:width", v),
218
+ onClose: () => {
219
+ emit("close");
220
+ props.onClose?.();
221
+ },
222
+ onTabAdded: (e) => emit("tab-added", e),
223
+ onTabClosed: (e) => emit("tab-closed", e),
224
+ onPaneSplit: (e) => emit("pane-split", e),
225
+ onPaneClosed: (e) => emit("pane-closed", e)
462
226
  }
463
- }
464
- function onPanelResizeEnd() {
465
- panelResizing.value = false;
466
- }
467
- onMounted2(() => {
468
- document.addEventListener("mousemove", onPanelResizeMove);
469
- document.addEventListener("mouseup", onPanelResizeEnd);
470
- });
471
- onBeforeUnmount2(() => {
472
- document.removeEventListener("mousemove", onPanelResizeMove);
473
- document.removeEventListener("mouseup", onPanelResizeEnd);
474
227
  });
475
- function onPaneResizeStart(e, tabId, dividerIndex) {
476
- e.preventDefault();
477
- const tab = tabs.value.find((t) => t.id === tabId);
478
- if (!tab) return;
479
- paneResizing.value = true;
480
- const container = e.target.closest(
481
- ".cli-panel-panes-container"
482
- );
483
- paneResizeState = {
484
- tabId,
485
- dividerIndex,
486
- startX: e.clientX,
487
- startWidths: tab.panes.map((p) => p.widthPercent),
488
- containerWidth: container ? container.clientWidth : 1
489
- };
490
- document.body.classList.add("cli-pane-resizing");
491
- }
492
- function onPaneResizeMove(e) {
493
- if (!paneResizing.value) return;
494
- const s = paneResizeState;
495
- const deltaPct = (e.clientX - s.startX) / s.containerWidth * 100;
496
- const i = s.dividerIndex;
497
- let leftW = s.startWidths[i] + deltaPct;
498
- let rightW = s.startWidths[i + 1] - deltaPct;
499
- if (leftW < MIN_PANE_WIDTH_PERCENT) {
500
- leftW = MIN_PANE_WIDTH_PERCENT;
501
- rightW = s.startWidths[i] + s.startWidths[i + 1] - MIN_PANE_WIDTH_PERCENT;
502
- }
503
- if (rightW < MIN_PANE_WIDTH_PERCENT) {
504
- rightW = MIN_PANE_WIDTH_PERCENT;
505
- leftW = s.startWidths[i] + s.startWidths[i + 1] - MIN_PANE_WIDTH_PERCENT;
506
- }
507
- const tab = tabs.value.find((t) => t.id === s.tabId);
508
- if (tab) {
509
- tab.panes[i].widthPercent = leftW;
510
- tab.panes[i + 1].widthPercent = rightW;
511
- }
512
- }
513
- function onPaneResizeEnd() {
514
- if (!paneResizing.value) return;
515
- paneResizing.value = false;
516
- document.body.classList.remove("cli-pane-resizing");
517
- }
518
- onMounted2(() => {
519
- document.addEventListener("mousemove", onPaneResizeMove);
520
- document.addEventListener("mouseup", onPaneResizeEnd);
228
+ const state = shallowRef(controller.getSnapshot());
229
+ const unsubscribe = controller.subscribe(() => {
230
+ state.value = controller.getSnapshot();
521
231
  });
232
+ watch(controlled, (values) => controller.setControlled(values));
233
+ watch(mergedOptions, (options) => controller.setOptions(options));
234
+ const root = shallowRef(null);
235
+ onMounted2(() => controller.setRootElement(root.value));
522
236
  onBeforeUnmount2(() => {
523
- document.removeEventListener("mousemove", onPaneResizeMove);
524
- document.removeEventListener("mouseup", onPaneResizeEnd);
237
+ unsubscribe();
238
+ controller.destroy();
525
239
  });
526
- function toggleMaximize() {
527
- if (!maximized.value) {
528
- if (isHorizontal.value) {
529
- prevWidth.value = panelWidth.value;
530
- panelWidth.value = window.innerWidth;
531
- } else {
532
- prevHeight.value = panelHeight.value;
533
- panelHeight.value = window.innerHeight;
534
- }
535
- } else {
536
- if (isHorizontal.value) {
537
- panelWidth.value = prevWidth.value;
538
- } else {
539
- panelHeight.value = prevHeight.value;
540
- }
541
- }
542
- maximized.value = !maximized.value;
543
- }
544
- function handleClose() {
545
- visible.value = false;
546
- props.onClose?.();
547
- }
548
- function handleHide() {
549
- preHideCollapsed = collapsed.value;
550
- hidden.value = true;
240
+ const exposed = {};
241
+ for (const method of PANEL_REF_METHODS) {
242
+ exposed[method] = controller[method].bind(controller);
551
243
  }
552
- function handleUnhide() {
553
- hidden.value = false;
554
- collapsed.value = preHideCollapsed;
555
- }
556
- const positionDropdownOpen = ref2(false);
557
- function closePositionDropdown() {
558
- positionDropdownOpen.value = false;
559
- }
560
- function togglePositionDropdown(e) {
244
+ expose(exposed);
245
+ const openDropdown = (e, dropdown) => {
246
+ if (!dropdown) return;
561
247
  e.stopPropagation();
562
- positionDropdownOpen.value = !positionDropdownOpen.value;
563
- }
564
- function selectPosition(pos) {
565
- positionDropdownOpen.value = false;
566
- position.value = pos;
567
- savePanelPosition(pos);
568
- }
569
- return () => {
570
- if (!visible.value) return null;
571
- const wrapperClass = [
572
- "cli-panel-wrapper",
573
- collapsed.value && "collapsed",
574
- maximized.value && "maximized",
575
- panelResizing.value && "resizing",
576
- props.class
577
- ].filter(Boolean).join(" ");
578
- const wrapperStyle = {
579
- ...isHorizontal.value ? { width: `${panelWidth.value}px` } : { height: `${panelHeight.value}px` },
580
- ...hidden.value ? { display: "none" } : {},
581
- ...themeStyles.value,
582
- ...props.style
583
- };
584
- const headerEl = h2("div", { class: "cli-panel-header" }, [
248
+ const anchor = e.currentTarget;
249
+ if (dropdown === "services") controller.toggleServicesDropdown(anchor);
250
+ else controller.toggleServersDropdown(anchor);
251
+ };
252
+ const renderHeader = (s) => {
253
+ const indicators = statusIndicators(s);
254
+ return h2("div", { class: "cli-panel-header" }, [
585
255
  h2(
586
256
  "div",
587
257
  {
588
258
  class: "cli-panel-resize-bar",
589
- onMousedown: onPanelResizeStart
259
+ onMousedown: (e) => {
260
+ if (!s.resizable) return;
261
+ e.preventDefault();
262
+ controller.startPanelResize(e.clientX, e.clientY);
263
+ }
590
264
  },
591
265
  [h2("div", { class: "cli-panel-resize-grip" })]
592
266
  ),
593
267
  h2("div", { class: "cli-panel-header-content" }, [
594
- h2("p", { class: "cli-panel-title" }, [
595
- h2("span", { class: "cli-panel-title-icon" }, [
596
- terminalIcon()
597
- ]),
598
- "CLI"
599
- ]),
268
+ h2("p", { class: "cli-panel-title" }, [h2("span", { class: "cli-panel-title-icon" }, [icon(TERMINAL_ICON)]), PANEL_LABELS.title]),
269
+ s.showStatusIndicators ? h2(
270
+ "div",
271
+ { class: "cli-panel-status-indicators" },
272
+ indicators.map(
273
+ (item) => h2(
274
+ "span",
275
+ {
276
+ key: item.key,
277
+ class: joinClasses("cli-panel-status-item", item.itemClass),
278
+ onClick: item.dropdown ? (e) => openDropdown(e, item.dropdown) : void 0
279
+ },
280
+ [
281
+ item.dotClass ? h2("span", { class: `cli-panel-status-dot ${item.dotClass}` }) : null,
282
+ item.icon ? h2("span", { class: joinClasses("cli-panel-status-icon", item.iconClass) }, item.icon) : null,
283
+ h2("span", { class: "cli-panel-status-label" }, item.label)
284
+ ]
285
+ )
286
+ )
287
+ ) : null,
288
+ // Compact indicators for left/right positions (CSS controls visibility).
289
+ h2(
290
+ "div",
291
+ { class: "cli-panel-status-indicators-compact" },
292
+ indicators.map(
293
+ (item) => h2(
294
+ "span",
295
+ {
296
+ key: item.key,
297
+ class: joinClasses("cli-panel-status-compact-item", item.compactClass),
298
+ title: item.title,
299
+ onClick: item.dropdown ? (e) => openDropdown(e, item.dropdown) : void 0
300
+ },
301
+ [
302
+ item.compactDotClass ? h2("span", { class: item.compactDotClass }) : null,
303
+ item.dotClass ? h2("span", { class: `cli-panel-status-dot ${item.dotClass}` }) : null,
304
+ item.icon ? h2("span", { class: joinClasses("cli-panel-status-icon", item.iconClass) }, item.icon) : null
305
+ ]
306
+ )
307
+ )
308
+ ),
600
309
  h2("div", { class: "cli-panel-action-buttons" }, [
601
- h2("div", {
602
- class: "cli-panel-btn-position-wrapper",
603
- onClick: (e) => e.stopPropagation()
604
- }, [
605
- h2("button", {
606
- class: "cli-panel-btn cli-panel-btn-position",
607
- title: "Move panel",
608
- onClick: togglePositionDropdown
609
- }, [positionIcon(position.value)]),
610
- positionDropdownOpen.value ? h2(
310
+ h2("div", { class: "cli-panel-btn-position-wrapper", onClick: stop }, [
311
+ h2(
312
+ "button",
313
+ {
314
+ class: "cli-panel-btn cli-panel-btn-position",
315
+ title: PANEL_LABELS.movePanel,
316
+ onClick: () => controller.togglePositionDropdown()
317
+ },
318
+ [icon(positionIcon(s.position))]
319
+ ),
320
+ s.positionDropdownOpen ? h2(
611
321
  "div",
612
322
  { class: "cli-panel-position-dropdown" },
613
- ["bottom", "top", "left", "right"].map(
614
- (pos) => h2("button", {
615
- class: ["cli-panel-position-dropdown-item", position.value === pos ? "active" : ""].filter(Boolean).join(" "),
616
- onClick: () => selectPosition(pos)
617
- }, [
618
- positionIcon(pos, 16),
619
- pos.charAt(0).toUpperCase() + pos.slice(1)
620
- ])
323
+ positionOptions(s).map(
324
+ (opt) => h2(
325
+ "button",
326
+ { key: opt.position, class: opt.className, onClick: () => controller.setPosition(opt.position) },
327
+ [icon(positionIcon(opt.position, 16)), opt.label]
328
+ )
621
329
  )
622
330
  ) : null
623
331
  ]),
624
- hideable.value ? h2("button", {
625
- class: "cli-panel-btn cli-panel-btn-hide",
626
- title: "Hide",
627
- onClick: handleHide
628
- }, [hideIcon()]) : null,
332
+ s.hideable ? h2("button", { class: "cli-panel-btn cli-panel-btn-hide", title: PANEL_LABELS.hide, onClick: () => controller.hide() }, [icon(HIDE_ICON)]) : null,
629
333
  h2(
630
334
  "button",
631
335
  {
632
336
  class: "cli-panel-btn",
633
- title: maximized.value ? "Restore" : "Maximize",
634
- disabled: collapsed.value,
635
- onClick: toggleMaximize
337
+ title: s.maximized ? PANEL_LABELS.restore : PANEL_LABELS.maximize,
338
+ disabled: s.collapsed,
339
+ onClick: () => controller.toggleMaximize()
636
340
  },
637
- [maximized.value ? restoreIcon() : maximizeIcon()]
341
+ [icon(s.maximized ? RESTORE_ICON : MAXIMIZE_ICON)]
638
342
  ),
639
343
  h2(
640
344
  "button",
641
345
  {
642
346
  class: "cli-panel-btn",
643
- title: collapsed.value ? "Expand" : "Collapse",
644
- onClick: toggle
347
+ title: s.collapsed ? PANEL_LABELS.expand : PANEL_LABELS.collapse,
348
+ onClick: () => controller.toggle()
645
349
  },
646
- [collapseChevron(position.value, collapsed.value)]
350
+ [icon(collapseChevronIcon(s.position, s.collapsed))]
647
351
  ),
648
- closable.value ? h2(
649
- "button",
650
- {
651
- class: "cli-panel-btn cli-panel-btn-close",
652
- title: "Close",
653
- onClick: handleClose
654
- },
655
- [closeIcon()]
656
- ) : null
352
+ s.closable ? h2("button", { class: "cli-panel-btn cli-panel-btn-close", title: PANEL_LABELS.close, onClick: () => controller.close() }, [icon(CLOSE_ICON)]) : null
657
353
  ])
658
- ])
354
+ ]),
355
+ h2("div", { class: glowLineClass(s) })
659
356
  ]);
660
- const contentEl = initialized.value ? h2("div", { class: "cli-panel-content", style: collapsed.value ? { display: "none" } : void 0 }, [
661
- // Tab bar
662
- h2("div", { class: "cli-panel-tabs" }, [
357
+ };
358
+ const renderTabs = (s) => h2("div", { class: "cli-panel-tabs" }, [
359
+ h2(
360
+ "ul",
361
+ { class: "cli-panel-tab-list" },
362
+ s.tabs.map(
363
+ (tab) => h2(
364
+ "li",
365
+ {
366
+ key: tab.id,
367
+ class: panelTabClass(s, tab),
368
+ onClick: () => controller.selectTab(tab.id),
369
+ onDblclick: () => controller.startRename(tab.id),
370
+ onContextmenu: (e) => {
371
+ e.preventDefault();
372
+ e.stopPropagation();
373
+ controller.openContextMenu(tab.id, e.clientX, e.clientY);
374
+ }
375
+ },
376
+ tab.isEditing ? [
377
+ h2("input", {
378
+ class: "cli-panel-tab-rename-input",
379
+ type: "text",
380
+ value: tab.title,
381
+ onVnodeMounted: (vnode) => {
382
+ const input = vnode.el;
383
+ input.focus();
384
+ input.select();
385
+ },
386
+ onKeydown: (e) => handleRenameKey(controller, tab.id, e.key, e.target.value),
387
+ onBlur: (e) => controller.commitRename(tab.id, e.target.value),
388
+ onClick: stop,
389
+ onDblclick: stop
390
+ })
391
+ ] : [
392
+ h2("span", { class: panelTabDotClass(s, tab) }),
393
+ h2("span", { class: "cli-panel-tab-title" }, tab.title),
394
+ h2(
395
+ "button",
396
+ {
397
+ class: "cli-panel-tab-close-btn",
398
+ title: PANEL_LABELS.closeTab,
399
+ onClick: (e) => {
400
+ e.stopPropagation();
401
+ controller.closeTab(tab.id);
402
+ }
403
+ },
404
+ CLOSE_GLYPH
405
+ )
406
+ ]
407
+ )
408
+ )
409
+ ),
410
+ h2("button", { class: "cli-panel-add-tab", title: PANEL_LABELS.newTerminal, onClick: () => controller.addTab() }, "+")
411
+ ]);
412
+ const renderInstances = (s) => h2(
413
+ "div",
414
+ { class: "cli-panel-instances" },
415
+ s.tabs.map(
416
+ (tab) => h2("div", { key: tab.id, class: "cli-panel-instance", style: { display: tab.id === s.activeTabId ? void 0 : "none" } }, [
663
417
  h2(
664
- "ul",
665
- { class: "cli-panel-tab-list" },
666
- tabs.value.map(
667
- (tab) => h2(
668
- "li",
418
+ "div",
419
+ { class: panesContainerClass(s) },
420
+ tab.panes.flatMap((pane, i) => [
421
+ i > 0 ? h2(
422
+ "div",
669
423
  {
670
- key: tab.id,
671
- class: [
672
- "cli-panel-tab",
673
- tab.id === activeTabId.value && "active"
674
- ].filter(Boolean).join(" "),
675
- onClick: () => selectTab(tab.id),
676
- onDblclick: () => startRename(tab.id),
677
- onContextmenu: (e) => {
424
+ key: `divider-${pane.id}`,
425
+ class: "cli-panel-pane-divider",
426
+ onMousedown: (e) => {
678
427
  e.preventDefault();
679
- e.stopPropagation();
680
- contextMenu.value = {
681
- visible: true,
682
- x: e.clientX,
683
- y: e.clientY,
684
- tabId: tab.id
685
- };
428
+ controller.startPaneResize(tab.id, i - 1, e.clientX, panesContainerWidth(e.target));
686
429
  }
687
430
  },
688
- tab.isEditing ? [
689
- h2("input", {
690
- class: "cli-panel-tab-rename-input",
691
- type: "text",
692
- value: tab.title,
693
- onKeydown: (e) => {
694
- if (e.key === "Enter")
695
- commitRename(
696
- tab.id,
697
- e.target.value
698
- );
699
- else if (e.key === "Escape") {
700
- tab.isEditing = false;
701
- }
702
- },
703
- onBlur: (e) => commitRename(
704
- tab.id,
705
- e.target.value
706
- ),
707
- onClick: (e) => e.stopPropagation(),
708
- onDblclick: (e) => e.stopPropagation()
709
- })
710
- ] : [
711
- h2(
712
- "span",
713
- {
714
- class: "cli-panel-tab-title"
715
- },
716
- tab.title
717
- ),
718
- h2(
431
+ [h2("div", { class: "cli-panel-pane-divider-grip" })]
432
+ ) : null,
433
+ h2(
434
+ "div",
435
+ {
436
+ key: pane.id,
437
+ class: paneClass(s, tab.id, pane.id),
438
+ style: paneStyle(pane.widthPercent),
439
+ onClick: () => controller.activatePane(tab.id, pane.id)
440
+ },
441
+ [
442
+ tab.panes.length > 1 ? h2(
719
443
  "button",
720
444
  {
721
- class: "cli-panel-tab-close-btn",
722
- title: "Close tab",
445
+ class: "cli-panel-pane-close-btn",
446
+ title: PANEL_LABELS.closePane,
723
447
  onClick: (e) => {
724
448
  e.stopPropagation();
725
- closeTab(
726
- tab.id
727
- );
449
+ controller.closePane(pane.id);
728
450
  }
729
451
  },
730
- "\xD7"
731
- )
452
+ CLOSE_GLYPH
453
+ ) : null,
454
+ h2(Cli, {
455
+ options: mergedOptions.value,
456
+ modules: hostConfig.value.modules,
457
+ services: hostConfig.value.services,
458
+ snapshot: pane.snapshot,
459
+ style: { height: "100%" },
460
+ onReady: (engine) => controller.registerEngine(tab.id, pane.id, engine)
461
+ })
732
462
  ]
733
463
  )
734
- )
735
- ),
736
- h2(
737
- "button",
738
- {
739
- class: "cli-panel-add-tab",
740
- title: "New terminal",
741
- onClick: addTab
742
- },
743
- "+"
464
+ ])
744
465
  )
745
- ]),
746
- // Terminal instances
466
+ ])
467
+ )
468
+ );
469
+ const renderContextMenu = (s) => h2(
470
+ "div",
471
+ { class: "cli-panel-context-menu", style: contextMenuStyle(s), onClick: stop },
472
+ contextMenuItems(s).flatMap((item) => [
473
+ item.separatorBefore ? h2("div", { class: "cli-panel-context-menu-separator" }) : null,
474
+ h2(
475
+ "button",
476
+ {
477
+ class: joinClasses("cli-panel-context-menu-item", item.destructive && "destructive"),
478
+ disabled: item.disabled,
479
+ onClick: () => runContextMenuAction(controller, item.action)
480
+ },
481
+ item.label
482
+ )
483
+ ])
484
+ );
485
+ const renderStatusDropdown = (s) => {
486
+ const dropdown = openStatusDropdown(s);
487
+ if (!dropdown) return null;
488
+ return h2("div", { class: "cli-panel-services-dropdown", style: dropdown.style, onClick: stop }, [
489
+ h2("div", { class: "cli-panel-services-dropdown-header" }, dropdown.header),
747
490
  h2(
748
491
  "div",
749
- { class: "cli-panel-instances" },
750
- tabs.value.map(
751
- (tab) => h2(
752
- "div",
753
- {
754
- key: tab.id,
755
- class: "cli-panel-instance",
756
- style: {
757
- display: tab.id === activeTabId.value ? void 0 : "none"
758
- }
759
- },
760
- [
761
- h2(
762
- "div",
763
- {
764
- class: [
765
- "cli-panel-panes-container",
766
- paneResizing.value && "resizing"
767
- ].filter(Boolean).join(" ")
768
- },
769
- tab.panes.flatMap((pane, i) => {
770
- const nodes = [];
771
- if (i > 0) {
772
- nodes.push(
773
- h2(
774
- "div",
775
- {
776
- class: "cli-panel-pane-divider",
777
- onMousedown: (e) => onPaneResizeStart(
778
- e,
779
- tab.id,
780
- i - 1
781
- )
782
- },
783
- [
784
- h2("div", {
785
- class: "cli-panel-pane-divider-grip"
786
- })
787
- ]
788
- )
789
- );
790
- }
791
- nodes.push(
792
- h2(
793
- "div",
794
- {
795
- class: [
796
- "cli-panel-pane",
797
- pane.id === activePaneId.value && tab.id === activeTabId.value && "active-pane"
798
- ].filter(
799
- Boolean
800
- ).join(" "),
801
- style: {
802
- flex: `${pane.widthPercent} 1 0`
803
- },
804
- onClick: () => {
805
- activePaneId.value = pane.id;
806
- activeTabId.value = tab.id;
807
- }
808
- },
809
- [
810
- tab.panes.length > 1 ? h2(
811
- "button",
812
- {
813
- class: "cli-panel-pane-close-btn",
814
- title: "Close pane",
815
- onClick: (e) => {
816
- e.stopPropagation();
817
- closePane(
818
- tab.id,
819
- pane.id
820
- );
821
- }
822
- },
823
- "\xD7"
824
- ) : null,
825
- h2(Cli, {
826
- options: mergedOptions.value,
827
- modules: mergedModules.value,
828
- processors: mergedProcessors.value,
829
- services: mergedServices.value,
830
- snapshot: pane.snapshot,
831
- style: {
832
- height: "100%"
833
- },
834
- onReady: (engine) => {
835
- engineMap.set(pane.id, engine);
836
- }
837
- })
838
- ]
839
- )
840
- );
841
- return nodes;
842
- })
843
- )
844
- ]
845
- )
492
+ { class: "cli-panel-services-dropdown-list" },
493
+ dropdown.rows.map(
494
+ (row) => h2("div", { key: row.key, class: "cli-panel-services-dropdown-item" }, [
495
+ h2("span", { class: row.dotClass }),
496
+ h2("div", { class: "cli-panel-svc-info" }, [
497
+ h2("span", { class: "cli-panel-svc-name" }, row.name),
498
+ row.description ? h2("span", { class: "cli-panel-svc-desc" }, row.description) : null
499
+ ]),
500
+ dropdown.kind === "servers" ? h2("div", { class: "cli-panel-srv-meta" }, [
501
+ h2("span", { class: "cli-panel-svc-status" }, row.status),
502
+ ...row.meta.map((m) => h2("span", { key: m, class: "cli-panel-svc-desc" }, m))
503
+ ]) : h2("span", { class: "cli-panel-svc-status" }, row.status)
504
+ ])
846
505
  )
847
- )
848
- ]) : null;
849
- const contextMenuEl = contextMenu.value.visible ? h2(
850
- "div",
851
- {
852
- class: "cli-panel-context-menu",
853
- style: {
854
- left: `${contextMenu.value.x}px`,
855
- top: `${contextMenu.value.y}px`
506
+ ),
507
+ dropdown.rows.length === 0 ? h2("div", { class: "cli-panel-services-dropdown-empty" }, dropdown.emptyText) : null
508
+ ]);
509
+ };
510
+ return () => {
511
+ const s = state.value;
512
+ if (!s.visible) return null;
513
+ return h2(Fragment, [
514
+ s.hidden ? h2(
515
+ "button",
516
+ {
517
+ class: "cli-panel-hide-tab",
518
+ "data-position": s.position,
519
+ "data-hide-align": s.hideAlignment,
520
+ style: s.themeStyles,
521
+ title: PANEL_LABELS.showCli,
522
+ onClick: () => controller.unhide()
856
523
  },
857
- onClick: (e) => e.stopPropagation()
858
- },
859
- [
860
- h2(
861
- "button",
862
- {
863
- class: "cli-panel-context-menu-item",
864
- onClick: () => {
865
- closeContextMenu();
866
- startRename(contextMenu.value.tabId);
867
- }
868
- },
869
- "Rename"
870
- ),
871
- h2(
872
- "button",
873
- {
874
- class: "cli-panel-context-menu-item",
875
- onClick: () => {
876
- const tab = tabs.value.find(
877
- (t) => t.id === contextMenu.value.tabId
878
- );
879
- closeContextMenu();
880
- if (!tab) return;
881
- const sourceEngine = engineMap.get(tab.panes[0]?.id);
882
- const snapshot = sourceEngine?.snapshot();
883
- const paneId = nextPaneId++;
884
- const tabId = nextTabId++;
885
- const idx = tabs.value.indexOf(tab);
886
- tabs.value.splice(idx + 1, 0, {
887
- id: tabId,
888
- title: `${tab.title} (copy)`,
889
- isEditing: false,
890
- panes: [
891
- { id: paneId, widthPercent: 100, snapshot }
892
- ]
893
- });
894
- activeTabId.value = tabId;
895
- activePaneId.value = paneId;
896
- }
897
- },
898
- "Duplicate"
899
- ),
900
- h2(
901
- "button",
902
- {
903
- class: "cli-panel-context-menu-item",
904
- onClick: () => {
905
- closeContextMenu();
906
- splitRight(contextMenu.value.tabId);
907
- }
908
- },
909
- "Split Right"
910
- ),
911
- h2("div", {
912
- class: "cli-panel-context-menu-separator"
913
- }),
914
- h2(
915
- "button",
916
- {
917
- class: "cli-panel-context-menu-item",
918
- onClick: () => {
919
- closeContextMenu();
920
- closeTab(contextMenu.value.tabId);
921
- }
922
- },
923
- "Close"
924
- ),
925
- h2(
926
- "button",
927
- {
928
- class: "cli-panel-context-menu-item",
929
- disabled: tabs.value.length <= 1,
930
- onClick: () => {
931
- const id = contextMenu.value.tabId;
932
- closeContextMenu();
933
- tabs.value = tabs.value.filter(
934
- (t) => t.id === id
935
- );
936
- activeTabId.value = id;
937
- }
938
- },
939
- "Close Others"
940
- ),
941
- h2(
942
- "button",
943
- {
944
- class: "cli-panel-context-menu-item",
945
- onClick: () => {
946
- const id = contextMenu.value.tabId;
947
- closeContextMenu();
948
- const idx = tabs.value.findIndex(
949
- (t) => t.id === id
950
- );
951
- if (idx !== -1)
952
- tabs.value = tabs.value.slice(
953
- 0,
954
- idx + 1
955
- );
956
- }
957
- },
958
- "Close to the Right"
959
- ),
960
- h2("div", {
961
- class: "cli-panel-context-menu-separator"
962
- }),
963
- h2(
964
- "button",
965
- {
966
- class: "cli-panel-context-menu-item destructive",
967
- onClick: () => {
968
- closeContextMenu();
969
- tabs.value = [];
970
- initialized.value = false;
971
- collapsed.value = true;
972
- }
973
- },
974
- "Close All"
975
- )
976
- ]
977
- ) : null;
978
- const hideTabEl = hidden.value ? h2("button", {
979
- class: "cli-panel-hide-tab",
980
- "data-position": position.value,
981
- "data-hide-align": hideAlignment.value,
982
- style: themeStyles.value,
983
- title: "Show CLI",
984
- onClick: handleUnhide
985
- }, [
986
- h2("svg", { class: "cli-panel-hide-tab-icon", ...svgAttrs, width: "16", height: "16", "stroke-width": "2" }, [
987
- h2("polyline", { points: "4 17 10 11 4 5" }),
988
- h2("line", { x1: "12", y1: "19", x2: "20", y2: "19" })
989
- ]),
990
- h2("span", { class: "cli-panel-hide-tab-label" }, "CLI"),
991
- hideTabChevron(position.value)
992
- ]) : null;
993
- return h2("div", {}, [
994
- hideTabEl,
524
+ [
525
+ icon(HIDE_TAB_TERMINAL_ICON, "cli-panel-hide-tab-icon"),
526
+ h2("span", { class: "cli-panel-hide-tab-label" }, PANEL_LABELS.title),
527
+ icon(hideTabChevronIcon(s.position), "cli-panel-hide-tab-arrow")
528
+ ]
529
+ ) : null,
995
530
  h2(
996
531
  "div",
997
532
  {
998
- class: wrapperClass,
999
- style: wrapperStyle,
1000
- "data-position": position.value,
1001
- "data-resizable": String(resizable.value),
1002
- "data-closable": String(closable.value),
1003
- "data-hideable": String(hideable.value)
533
+ ref: root,
534
+ class: panelWrapperClass(s, props.class),
535
+ style: { ...panelWrapperStyle(s), ...props.style },
536
+ "data-position": s.position,
537
+ "data-resizable": String(s.resizable),
538
+ "data-closable": String(s.closable),
539
+ "data-hideable": String(s.hideable)
1004
540
  },
1005
- [headerEl, contentEl]
541
+ [
542
+ renderHeader(s),
543
+ s.initialized ? h2("div", { class: "cli-panel-content", style: s.collapsed ? { display: "none" } : void 0 }, [
544
+ renderTabs(s),
545
+ renderInstances(s)
546
+ ]) : null
547
+ ]
1006
548
  ),
1007
- contextMenuEl
549
+ s.contextMenu.visible ? renderContextMenu(s) : null,
550
+ renderStatusDropdown(s)
1008
551
  ]);
1009
552
  };
1010
553
  }
1011
554
  });
1012
555
 
1013
556
  // src/CliProvider.ts
1014
- import { defineComponent as defineComponent4, ref as ref4, h as h3, provide as provide2 } from "vue";
557
+ import { defineComponent as defineComponent4, ref as ref2, h as h3, provide as provide2 } from "vue";
1015
558
 
1016
559
  // src/useCliEngine.ts
1017
- import { onMounted as onMounted3, onBeforeUnmount as onBeforeUnmount3, shallowRef } from "vue";
1018
- import { CliEngine as CliEngine2 } from "@qodalis/cli";
560
+ import { onMounted as onMounted3, onBeforeUnmount as onBeforeUnmount3, shallowRef as shallowRef2 } from "vue";
561
+ import { createCliEngine as createCliEngine2 } from "@qodalis/cli";
1019
562
  function useCliEngine(containerRef, config) {
1020
- const engine = shallowRef(null);
563
+ const engine = shallowRef2(null);
1021
564
  onMounted3(async () => {
1022
565
  if (!containerRef.value) return;
1023
- const e = new CliEngine2(containerRef.value, config?.options);
1024
- e.registerService("cli-framework", "Vue");
1025
- if (config?.services) {
1026
- for (const [token, value] of Object.entries(config.services)) {
1027
- e.registerService(token, value);
1028
- }
1029
- }
1030
- if (config?.modules) {
1031
- e.registerModules(config.modules);
1032
- }
1033
- if (config?.processors) {
1034
- e.registerProcessors(config.processors);
1035
- }
566
+ const e = createCliEngine2(containerRef.value, { ...config, framework: "Vue" });
1036
567
  await e.start();
1037
568
  engine.value = e;
1038
569
  });
@@ -1051,10 +582,6 @@ var CliProvider = defineComponent4({
1051
582
  type: Array,
1052
583
  default: void 0
1053
584
  },
1054
- processors: {
1055
- type: Array,
1056
- default: void 0
1057
- },
1058
585
  options: {
1059
586
  type: Object,
1060
587
  default: void 0
@@ -1070,10 +597,9 @@ var CliProvider = defineComponent4({
1070
597
  },
1071
598
  emits: ["ready"],
1072
599
  setup(props, { slots, emit }) {
1073
- const containerRef = ref4(null);
600
+ const containerRef = ref2(null);
1074
601
  const engine = useCliEngine(containerRef, {
1075
602
  modules: props.modules,
1076
- processors: props.processors,
1077
603
  options: props.options,
1078
604
  services: props.services
1079
605
  });
@@ -1101,12 +627,12 @@ function useCli() {
1101
627
  }
1102
628
 
1103
629
  // src/index.ts
1104
- import { CliEngine as CliEngine3 } from "@qodalis/cli";
630
+ import { CliEngine as CliEngine4 } from "@qodalis/cli";
1105
631
  export {
1106
632
  Cli,
1107
633
  CliConfigKey,
1108
634
  CliConfigProvider,
1109
- CliEngine3 as CliEngine,
635
+ CliEngine4 as CliEngine,
1110
636
  CliInjectionKey,
1111
637
  CliPanel,
1112
638
  CliProvider,