@qodalis/vue-cli 2.0.0-beta.16 → 2.0.0-beta.18

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
@@ -23,7 +23,7 @@ __export(index_exports, {
23
23
  Cli: () => Cli,
24
24
  CliConfigKey: () => CliConfigKey,
25
25
  CliConfigProvider: () => CliConfigProvider,
26
- CliEngine: () => import_cli3.CliEngine,
26
+ CliEngine: () => import_cli4.CliEngine,
27
27
  CliInjectionKey: () => CliInjectionKey,
28
28
  CliPanel: () => CliPanel,
29
29
  CliProvider: () => CliProvider,
@@ -115,26 +115,10 @@ var Cli = (0, import_vue2.defineComponent)({
115
115
  if (ctx) {
116
116
  return () => null;
117
117
  }
118
- const modules = props.modules ?? config?.modules;
119
- const processors = props.processors ?? config?.processors;
120
- const options = props.options ?? config?.options;
121
- const services = props.services ?? config?.services;
118
+ const hostConfig = (0, import_cli.resolveCliHostConfig)(props, config);
122
119
  (0, import_vue2.onMounted)(async () => {
123
120
  if (!containerRef.value) return;
124
- const engineOptions = props.snapshot ? { ...options, snapshot: props.snapshot } : options;
125
- engine = new import_cli.CliEngine(containerRef.value, engineOptions);
126
- engine.registerService("cli-framework", "Vue");
127
- if (services) {
128
- for (const [token, value] of Object.entries(services)) {
129
- engine.registerService(token, value);
130
- }
131
- }
132
- if (modules) {
133
- engine.registerModules(modules);
134
- }
135
- if (processors) {
136
- engine.registerProcessors(processors);
137
- }
121
+ engine = (0, import_cli.createCliEngine)(containerRef.value, { ...hostConfig, snapshot: props.snapshot, framework: "Vue" });
138
122
  await engine.start();
139
123
  emit("ready", engine);
140
124
  });
@@ -152,129 +136,41 @@ var Cli = (0, import_vue2.defineComponent)({
152
136
 
153
137
  // src/CliPanel.ts
154
138
  var import_vue3 = require("vue");
155
- var import_cli_core = require("@qodalis/cli-core");
156
- var MIN_PANE_WIDTH_PERCENT = 10;
157
- function normalizePanes(panes) {
158
- const total = panes.reduce((s, p) => s + p.widthPercent, 0);
159
- if (total === 0) return;
160
- const scale = 100 / total;
161
- panes.forEach((p) => {
162
- p.widthPercent *= scale;
163
- });
164
- }
165
- var svgAttrs = {
166
- width: "20",
167
- height: "20",
168
- viewBox: "0 0 24 24",
169
- fill: "none",
170
- stroke: "currentColor",
171
- "stroke-width": "1.8",
172
- "stroke-linecap": "round",
173
- "stroke-linejoin": "round"
174
- };
175
- var terminalIcon = () => (0, import_vue3.h)("svg", { ...svgAttrs, width: "22", height: "22", "stroke-width": "2" }, [
176
- (0, import_vue3.h)("polyline", { points: "4 17 10 11 4 5" }),
177
- (0, import_vue3.h)("line", { x1: "12", y1: "19", x2: "20", y2: "19" })
178
- ]);
179
- var maximizeIcon = () => (0, import_vue3.h)("svg", svgAttrs, [
180
- (0, import_vue3.h)("polyline", { points: "15 3 21 3 21 9" }),
181
- (0, import_vue3.h)("polyline", { points: "9 21 3 21 3 15" }),
182
- (0, import_vue3.h)("line", { x1: "21", y1: "3", x2: "14", y2: "10" }),
183
- (0, import_vue3.h)("line", { x1: "3", y1: "21", x2: "10", y2: "14" })
184
- ]);
185
- var restoreIcon = () => (0, import_vue3.h)("svg", svgAttrs, [
186
- (0, import_vue3.h)("polyline", { points: "4 14 10 14 10 20" }),
187
- (0, import_vue3.h)("polyline", { points: "20 10 14 10 14 4" }),
188
- (0, import_vue3.h)("line", { x1: "14", y1: "10", x2: "21", y2: "3" }),
189
- (0, import_vue3.h)("line", { x1: "3", y1: "21", x2: "10", y2: "14" })
190
- ]);
191
- var closeIcon = () => (0, import_vue3.h)("svg", svgAttrs, [
192
- (0, import_vue3.h)("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
193
- (0, import_vue3.h)("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
194
- ]);
195
- var hideIcon = () => (0, import_vue3.h)("svg", svgAttrs, [
196
- (0, import_vue3.h)("line", { x1: "5", y1: "18", x2: "19", y2: "18" }),
197
- (0, import_vue3.h)("polyline", { points: "9 14 12 17 15 14" })
198
- ]);
199
- function collapseChevron(position, isCollapsed) {
200
- let points;
201
- switch (position) {
202
- case "top":
203
- points = isCollapsed ? "6 9 12 15 18 9" : "18 15 12 9 6 15";
204
- break;
205
- case "left":
206
- points = isCollapsed ? "9 18 15 12 9 6" : "15 6 9 12 15 18";
207
- break;
208
- case "right":
209
- points = isCollapsed ? "15 6 9 12 15 18" : "9 18 15 12 9 6";
210
- break;
211
- default:
212
- points = isCollapsed ? "18 15 12 9 6 15" : "6 9 12 15 18 9";
213
- break;
214
- }
215
- return (0, import_vue3.h)("svg", svgAttrs, [(0, import_vue3.h)("polyline", { points })]);
216
- }
217
- function hideTabChevron(position) {
218
- let points;
219
- switch (position) {
220
- case "top":
221
- points = "6 9 12 15 18 9";
222
- break;
223
- case "left":
224
- points = "9 18 15 12 9 6";
225
- break;
226
- case "right":
227
- points = "15 6 9 12 15 18";
228
- break;
229
- default:
230
- points = "18 15 12 9 6 15";
231
- break;
232
- }
233
- return (0, import_vue3.h)("svg", { class: "cli-panel-hide-tab-arrow", ...svgAttrs, width: "14", height: "14", "stroke-width": "2.5" }, [
234
- (0, import_vue3.h)("polyline", { points })
235
- ]);
236
- }
237
- function positionIcon(pos, size = 20) {
238
- let fillRect;
239
- switch (pos) {
240
- case "top":
241
- fillRect = (0, import_vue3.h)("rect", { x: "4", y: "4", width: "16", height: "5", rx: "1", fill: "currentColor", stroke: "none", opacity: "0.5" });
242
- break;
243
- case "left":
244
- fillRect = (0, import_vue3.h)("rect", { x: "4", y: "4", width: "5", height: "16", rx: "1", fill: "currentColor", stroke: "none", opacity: "0.5" });
245
- break;
246
- case "right":
247
- fillRect = (0, import_vue3.h)("rect", { x: "15", y: "4", width: "5", height: "16", rx: "1", fill: "currentColor", stroke: "none", opacity: "0.5" });
248
- break;
249
- default:
250
- fillRect = (0, import_vue3.h)("rect", { x: "4", y: "15", width: "16", height: "5", rx: "1", fill: "currentColor", stroke: "none", opacity: "0.5" });
251
- break;
252
- }
253
- return (0, import_vue3.h)("svg", { ...svgAttrs, width: String(size), height: String(size) }, [
254
- (0, import_vue3.h)("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }),
255
- fillRect
256
- ]);
139
+ var import_cli2 = require("@qodalis/cli");
140
+ var PANEL_REF_METHODS = [
141
+ "open",
142
+ "collapse",
143
+ "toggleCollapse",
144
+ "hide",
145
+ "unhide",
146
+ "toggleHide",
147
+ "close",
148
+ "maximize",
149
+ "restore",
150
+ "toggleMaximize",
151
+ "resize",
152
+ "setPosition",
153
+ "addTab",
154
+ "closeTab",
155
+ "selectTab",
156
+ "renameTab",
157
+ "splitPane",
158
+ "closePane",
159
+ "getEngine",
160
+ "getState"
161
+ ];
162
+ function icon(svg, className) {
163
+ return (0, import_vue3.h)("svg", { class: className, ...(0, import_cli2.svgAttrs)(svg) }, svg.shapes.map((shape) => (0, import_vue3.h)(shape.tag, shape.attrs)));
257
164
  }
165
+ var stop = (e) => e.stopPropagation();
258
166
  var CliPanel = (0, import_vue3.defineComponent)({
259
167
  name: "CliPanel",
260
168
  props: {
261
- options: {
262
- type: Object,
263
- default: void 0
264
- },
169
+ options: { type: Object, default: void 0 },
265
170
  modules: { type: Array, default: void 0 },
266
- processors: {
267
- type: Array,
268
- default: void 0
269
- },
270
- services: {
271
- type: Object,
272
- default: void 0
273
- },
274
- onClose: {
275
- type: Function,
276
- default: void 0
277
- },
171
+ processors: { type: Array, default: void 0 },
172
+ services: { type: Object, default: void 0 },
173
+ onClose: { type: Function, default: void 0 },
278
174
  collapsed: { type: Boolean, default: void 0 },
279
175
  hidden: { type: Boolean, default: void 0 },
280
176
  maximized: { type: Boolean, default: void 0 },
@@ -282,10 +178,7 @@ var CliPanel = (0, import_vue3.defineComponent)({
282
178
  position: { type: String, default: void 0 },
283
179
  height: { type: Number, default: void 0 },
284
180
  width: { type: Number, default: void 0 },
285
- style: {
286
- type: Object,
287
- default: void 0
288
- },
181
+ style: { type: Object, default: void 0 },
289
182
  class: { type: String, default: void 0 }
290
183
  },
291
184
  emits: [
@@ -304,1166 +197,362 @@ var CliPanel = (0, import_vue3.defineComponent)({
304
197
  ],
305
198
  setup(props, { expose, emit }) {
306
199
  const config = (0, import_vue3.inject)(CliConfigKey, null);
307
- const mergedOptions = (0, import_vue3.computed)(() => props.options ? { ...config?.options, ...props.options } : config?.options);
308
- const mergedModules = (0, import_vue3.computed)(() => props.modules ?? config?.modules);
309
- const mergedProcessors = (0, import_vue3.computed)(() => props.processors ?? config?.processors);
310
- const mergedServices = (0, import_vue3.computed)(() => props.services ?? config?.services);
311
- const visible = (0, import_vue3.ref)(true);
312
- const internalCollapsed = (0, import_vue3.ref)(mergedOptions.value?.isCollapsed ?? true);
313
- const internalMaximized = (0, import_vue3.ref)(false);
314
- const internalHeight = (0, import_vue3.ref)(600);
315
- const internalWidth = (0, import_vue3.ref)(400);
316
- const prevHeight = (0, import_vue3.ref)(600);
317
- const prevWidth = (0, import_vue3.ref)(400);
318
- const initialized = (0, import_vue3.ref)(false);
319
- const internalHidden = (0, import_vue3.ref)(props.options?.isHidden ?? false);
320
- let preHideCollapsed = true;
321
- const internalActiveTabId = (0, import_vue3.ref)(0);
322
- const internalPosition = (0, import_vue3.ref)(
323
- (0, import_cli_core.loadPanelPosition)() ?? mergedOptions.value?.position ?? "bottom"
324
- );
325
- const resolvedCollapsed = (0, import_vue3.computed)(() => props.collapsed !== void 0 ? props.collapsed : internalCollapsed.value);
326
- const resolvedHidden = (0, import_vue3.computed)(() => props.hidden !== void 0 ? props.hidden : internalHidden.value);
327
- const resolvedMaximized = (0, import_vue3.computed)(() => props.maximized !== void 0 ? props.maximized : internalMaximized.value);
328
- const resolvedActiveTabId = (0, import_vue3.computed)(() => props.activeTabId !== void 0 ? props.activeTabId : internalActiveTabId.value);
329
- const resolvedHeight = (0, import_vue3.computed)(() => props.height !== void 0 ? props.height : internalHeight.value);
330
- const resolvedWidth = (0, import_vue3.computed)(() => props.width !== void 0 ? props.width : internalWidth.value);
331
- const resolvedPosition = (0, import_vue3.computed)(() => props.position !== void 0 ? props.position : internalPosition.value);
332
- function updateCollapsed(value) {
333
- internalCollapsed.value = value;
334
- emit("update:collapsed", value);
335
- }
336
- function updateHidden(value) {
337
- internalHidden.value = value;
338
- emit("update:hidden", value);
339
- }
340
- function updateMaximized(value) {
341
- internalMaximized.value = value;
342
- emit("update:maximized", value);
343
- }
344
- function updateActiveTabId(value) {
345
- internalActiveTabId.value = value;
346
- emit("update:activeTabId", value);
347
- }
348
- function updateHeight(value) {
349
- internalHeight.value = value;
350
- emit("update:height", value);
351
- }
352
- function updateWidth(value) {
353
- internalWidth.value = value;
354
- emit("update:width", value);
355
- }
356
- function updatePosition(value) {
357
- internalPosition.value = value;
358
- (0, import_cli_core.savePanelPosition)(value);
359
- emit("update:position", value);
360
- }
361
- const closable = (0, import_vue3.computed)(() => mergedOptions.value?.closable ?? true);
362
- const resizable = (0, import_vue3.computed)(() => mergedOptions.value?.resizable ?? true);
363
- const isHorizontal = (0, import_vue3.computed)(() => resolvedPosition.value === "left" || resolvedPosition.value === "right");
364
- const hideable = (0, import_vue3.computed)(() => mergedOptions.value?.hideable ?? true);
365
- const hideAlignment = (0, import_vue3.computed)(() => mergedOptions.value?.hideAlignment ?? "center");
366
- const syncTheme = (0, import_vue3.computed)(() => mergedOptions.value?.syncTheme ?? false);
367
- const themeStyles = (0, import_vue3.ref)({});
368
- const tabs = (0, import_vue3.ref)([]);
369
- const activePaneId = (0, import_vue3.ref)(0);
370
- let nextTabId = 1;
371
- let nextPaneId = 1;
372
- const contextMenu = (0, import_vue3.ref)({
373
- visible: false,
374
- x: 0,
375
- y: 0,
376
- tabId: 0
377
- });
378
- const engineMap = /* @__PURE__ */ new Map();
379
- const paneResizing = (0, import_vue3.ref)(false);
380
- let paneResizeState = {
381
- tabId: 0,
382
- dividerIndex: 0,
383
- startX: 0,
384
- startWidths: [],
385
- containerWidth: 0
386
- };
387
- const panelResizing = (0, import_vue3.ref)(false);
388
- let panelResizeState = { startY: 0, startHeight: 0, startX: 0, startWidth: 0 };
389
- const statusExecutionState = (0, import_vue3.ref)("idle");
390
- const statusLastCommand = (0, import_vue3.ref)(null);
391
- const statusServiceCount = (0, import_vue3.ref)({ running: 0, total: 0 });
392
- const statusServiceDetails = (0, import_vue3.ref)([]);
393
- const statusServerState = (0, import_vue3.ref)("none");
394
- const statusServerDetails = (0, import_vue3.ref)([]);
395
- const statusUptime = (0, import_vue3.ref)(0);
396
- const notification = (0, import_vue3.ref)(null);
397
- const servicesDropdownOpen = (0, import_vue3.ref)(false);
398
- const serversDropdownOpen = (0, import_vue3.ref)(false);
399
- const servicesDropdownTriggerRect = (0, import_vue3.ref)(null);
400
- const serversDropdownTriggerRect = (0, import_vue3.ref)(null);
401
- const formattedUptime = (0, import_vue3.computed)(() => {
402
- const mins = Math.floor(statusUptime.value / 6e4);
403
- if (mins < 60) return `${mins}m`;
404
- const hrs = Math.floor(mins / 60);
405
- return `${hrs}h${mins % 60}m`;
200
+ const mergedOptions = (0, import_vue3.computed)(() => (0, import_cli2.mergeCliOptions)(props.options, config?.options));
201
+ const hostConfig = (0, import_vue3.computed)(() => (0, import_cli2.resolveCliHostConfig)(props, config));
202
+ const controlled = () => ({
203
+ collapsed: props.collapsed,
204
+ hidden: props.hidden,
205
+ maximized: props.maximized,
206
+ activeTabId: props.activeTabId,
207
+ position: props.position,
208
+ height: props.height,
209
+ width: props.width
406
210
  });
407
- let execPollTimer;
408
- let globalPollTimer;
409
- function startStatusPolling() {
410
- stopStatusPolling();
411
- execPollTimer = setInterval(() => {
412
- const tab = tabs.value.find((t) => t.id === resolvedActiveTabId.value);
413
- if (!tab) return;
414
- const engine = engineMap.get(tab.panes[0]?.id);
415
- if (!engine) return;
416
- const context = engine.getContext();
417
- if (!context) return;
418
- const running = !!context.isExecuting || !!context.contextProcessor;
419
- statusExecutionState.value = running ? "running" : "idle";
420
- const result = context.lastCommandResult;
421
- statusLastCommand.value = result ? { name: result.command, success: result.success } : null;
422
- const notif = context.notifier?.current;
423
- notification.value = notif || null;
424
- }, 300);
425
- globalPollTimer = setInterval(() => {
426
- const tab = tabs.value.find((t) => t.id === resolvedActiveTabId.value);
427
- if (!tab) return;
428
- const engine = engineMap.get(tab.panes[0]?.id);
429
- if (!engine) return;
430
- const context = engine.getContext();
431
- if (!context) return;
432
- try {
433
- const services = context.backgroundServices?.list() ?? [];
434
- statusServiceCount.value = { running: services.filter((s) => s.status === "running").length, total: services.length };
435
- statusServiceDetails.value = services.map((s) => ({ name: s.name, status: s.status, description: s.description }));
436
- } catch {
437
- }
438
- try {
439
- const serverManager = context.services?.get?.("cli-server-manager");
440
- const details = [];
441
- if (serverManager?.connections?.size > 0) {
442
- let anyConnected = false;
443
- for (const [name, conn] of serverManager.connections) {
444
- if (conn.connected) anyConnected = true;
445
- details.push({
446
- name,
447
- url: conn.config?.url ?? "",
448
- connected: !!conn.connected,
449
- apiVersion: conn.connected ? conn.apiVersion : void 0,
450
- commandCount: conn.connected ? conn.commands?.length : void 0
451
- });
452
- }
453
- statusServerState.value = anyConnected ? "connected" : "disconnected";
454
- } else {
455
- const configuredServers = engine.options?.servers;
456
- if (Array.isArray(configuredServers) && configuredServers.length > 0) {
457
- for (const srv of configuredServers) {
458
- if (srv.enabled === false) continue;
459
- details.push({
460
- name: srv.name,
461
- url: srv.url ?? "",
462
- connected: false
463
- });
464
- }
465
- statusServerState.value = "disconnected";
466
- } else {
467
- statusServerState.value = "none";
468
- }
469
- }
470
- statusServerDetails.value = details;
471
- } catch {
472
- }
473
- statusUptime.value = engine.startedAt ? Date.now() - engine.startedAt : 0;
474
- }, 2e3);
475
- }
476
- function stopStatusPolling() {
477
- if (execPollTimer) {
478
- clearInterval(execPollTimer);
479
- execPollTimer = void 0;
480
- }
481
- if (globalPollTimer) {
482
- clearInterval(globalPollTimer);
483
- globalPollTimer = void 0;
484
- }
485
- }
486
- (0, import_vue3.watch)(initialized, (val) => {
487
- if (val) startStatusPolling();
488
- else stopStatusPolling();
489
- });
490
- function addTab(title) {
491
- const tabId = nextTabId++;
492
- const paneId = nextPaneId++;
493
- tabs.value = [...tabs.value, {
494
- id: tabId,
495
- title: title ?? `Terminal ${tabId}`,
496
- isEditing: false,
497
- panes: [{ id: paneId, widthPercent: 100 }]
498
- }];
499
- updateActiveTabId(tabId);
500
- activePaneId.value = paneId;
501
- emit("tab-added", { tabId });
502
- return tabId;
503
- }
504
- let themeObserver;
505
- function syncThemeFromEngine() {
506
- const engine = engineMap.values().next().value;
507
- if (!engine) return;
508
- const theme = engine.getTerminal().options.theme;
509
- if (theme) themeStyles.value = (0, import_cli_core.derivePanelThemeStyles)(theme);
510
- }
511
- function setupThemeSync() {
512
- if (!syncTheme.value) return;
513
- setTimeout(() => {
514
- syncThemeFromEngine();
515
- const container = document.querySelector(".cli-panel-wrapper .terminal-container");
516
- if (!container) return;
517
- themeObserver?.disconnect();
518
- themeObserver = new MutationObserver(syncThemeFromEngine);
519
- themeObserver.observe(container, { attributes: true, attributeFilter: ["style"] });
520
- }, 200);
521
- }
522
- (0, import_vue3.onMounted)(() => {
523
- document.addEventListener("click", closePositionDropdown);
524
- });
525
- (0, import_vue3.onBeforeUnmount)(() => {
526
- themeObserver?.disconnect();
527
- document.removeEventListener("click", closePositionDropdown);
528
- });
529
- function toggle() {
530
- const newVal = !resolvedCollapsed.value;
531
- updateCollapsed(newVal);
532
- if (!newVal && !initialized.value) {
533
- initialized.value = true;
534
- addTab();
535
- setupThemeSync();
536
- }
537
- }
538
- function closeTab(id) {
539
- const idx = tabs.value.findIndex((t) => t.id === id);
540
- if (idx === -1) return;
541
- tabs.value.splice(idx, 1);
542
- emit("tab-closed", { tabId: id });
543
- if (tabs.value.length === 0) {
544
- initialized.value = false;
545
- updateCollapsed(true);
546
- return;
547
- }
548
- if (resolvedActiveTabId.value === id) {
549
- const newIdx = Math.min(idx, tabs.value.length - 1);
550
- selectTab(tabs.value[newIdx].id);
551
- }
552
- }
553
- function selectTab(id) {
554
- updateActiveTabId(id);
555
- const tab = tabs.value.find((t) => t.id === id);
556
- if (tab && tab.panes.length > 0) {
557
- activePaneId.value = tab.panes[0].id;
558
- }
559
- }
560
- function splitPane(tabId) {
561
- const targetTabId = tabId ?? resolvedActiveTabId.value;
562
- const paneId = nextPaneId++;
563
- tabs.value = tabs.value.map((t) => {
564
- if (t.id !== targetTabId) return t;
565
- const newPanes = [...t.panes, { id: paneId, widthPercent: 0 }];
566
- const evenWidth = 100 / newPanes.length;
567
- newPanes.forEach((p) => {
568
- p.widthPercent = evenWidth;
569
- });
570
- normalizePanes(newPanes);
571
- return { ...t, panes: newPanes };
572
- });
573
- activePaneId.value = paneId;
574
- emit("pane-split", { paneId, tabId: targetTabId });
575
- return paneId;
576
- }
577
- function closePane(paneId) {
578
- for (const tab of tabs.value) {
579
- const idx = tab.panes.findIndex((p) => p.id === paneId);
580
- if (idx === -1) continue;
581
- if (tab.panes.length <= 1) {
582
- closeTab(tab.id);
583
- return;
584
- }
585
- engineMap.get(paneId)?.destroy();
586
- engineMap.delete(paneId);
587
- const newPanes = tab.panes.filter((p) => p.id !== paneId);
588
- normalizePanes(newPanes);
589
- if (activePaneId.value === paneId) {
590
- activePaneId.value = newPanes[Math.min(idx, newPanes.length - 1)].id;
591
- }
592
- emit("pane-closed", { paneId });
593
- tabs.value = tabs.value.map(
594
- (t) => t.id === tab.id ? { ...t, panes: newPanes } : t
595
- );
596
- return;
597
- }
598
- }
599
- function renameTab(tabId, title) {
600
- tabs.value = tabs.value.map((t) => t.id === tabId ? { ...t, title } : t);
601
- }
602
- function startRename(tabId) {
603
- tabs.value.forEach((t) => {
604
- t.isEditing = t.id === tabId;
605
- });
606
- }
607
- function commitRename(tabId, value) {
608
- const trimmed = value.trim();
609
- const tab = tabs.value.find((t) => t.id === tabId);
610
- if (tab) {
611
- if (trimmed) tab.title = trimmed;
612
- tab.isEditing = false;
613
- }
614
- }
615
- function closeContextMenu() {
616
- contextMenu.value = { ...contextMenu.value, visible: false };
617
- }
618
- function onDocClick() {
619
- if (contextMenu.value.visible) closeContextMenu();
620
- }
621
- (0, import_vue3.onMounted)(() => document.addEventListener("click", onDocClick));
622
- (0, import_vue3.onBeforeUnmount)(
623
- () => document.removeEventListener("click", onDocClick)
624
- );
625
- function onPanelResizeStart(e) {
626
- if (!resizable.value) return;
627
- e.preventDefault();
628
- if (resolvedCollapsed.value) toggle();
629
- panelResizing.value = true;
630
- panelResizeState = {
631
- startY: e.clientY,
632
- startHeight: resolvedHeight.value,
633
- startX: e.clientX,
634
- startWidth: resolvedWidth.value
635
- };
636
- }
637
- function onPanelResizeMove(e) {
638
- if (!panelResizing.value) return;
639
- if (isHorizontal.value) {
640
- const deltaX = resolvedPosition.value === "left" ? e.clientX - panelResizeState.startX : panelResizeState.startX - e.clientX;
641
- let next = Math.max(100, panelResizeState.startWidth + deltaX);
642
- if (next > window.innerWidth) next = window.innerWidth;
643
- updateWidth(next);
644
- } else {
645
- const deltaY = resolvedPosition.value === "top" ? e.clientY - panelResizeState.startY : panelResizeState.startY - e.clientY;
646
- let next = Math.max(100, panelResizeState.startHeight + deltaY);
647
- if (next > window.innerHeight) next = window.innerHeight;
648
- updateHeight(next);
211
+ const controller = new import_cli2.CliPanelController({
212
+ config: mergedOptions.value,
213
+ controlled: controlled(),
214
+ callbacks: {
215
+ onCollapsedChange: (v) => emit("update:collapsed", v),
216
+ onHiddenChange: (v) => emit("update:hidden", v),
217
+ onMaximizedChange: (v) => emit("update:maximized", v),
218
+ onActiveTabIdChange: (v) => emit("update:activeTabId", v),
219
+ onPositionChange: (v) => emit("update:position", v),
220
+ onHeightChange: (v) => emit("update:height", v),
221
+ onWidthChange: (v) => emit("update:width", v),
222
+ onClose: () => {
223
+ emit("close");
224
+ props.onClose?.();
225
+ },
226
+ onTabAdded: (e) => emit("tab-added", e),
227
+ onTabClosed: (e) => emit("tab-closed", e),
228
+ onPaneSplit: (e) => emit("pane-split", e),
229
+ onPaneClosed: (e) => emit("pane-closed", e)
649
230
  }
650
- }
651
- function onPanelResizeEnd() {
652
- panelResizing.value = false;
653
- }
654
- (0, import_vue3.onMounted)(() => {
655
- document.addEventListener("mousemove", onPanelResizeMove);
656
- document.addEventListener("mouseup", onPanelResizeEnd);
657
- });
658
- (0, import_vue3.onBeforeUnmount)(() => {
659
- document.removeEventListener("mousemove", onPanelResizeMove);
660
- document.removeEventListener("mouseup", onPanelResizeEnd);
661
231
  });
662
- function onPaneResizeStart(e, tabId, dividerIndex) {
663
- e.preventDefault();
664
- const tab = tabs.value.find((t) => t.id === tabId);
665
- if (!tab) return;
666
- paneResizing.value = true;
667
- const container = e.target.closest(
668
- ".cli-panel-panes-container"
669
- );
670
- paneResizeState = {
671
- tabId,
672
- dividerIndex,
673
- startX: e.clientX,
674
- startWidths: tab.panes.map((p) => p.widthPercent),
675
- containerWidth: container ? container.clientWidth : 1
676
- };
677
- document.body.classList.add("cli-pane-resizing");
678
- }
679
- function onPaneResizeMove(e) {
680
- if (!paneResizing.value) return;
681
- const s = paneResizeState;
682
- const deltaPct = (e.clientX - s.startX) / s.containerWidth * 100;
683
- const i = s.dividerIndex;
684
- let leftW = s.startWidths[i] + deltaPct;
685
- let rightW = s.startWidths[i + 1] - deltaPct;
686
- if (leftW < MIN_PANE_WIDTH_PERCENT) {
687
- leftW = MIN_PANE_WIDTH_PERCENT;
688
- rightW = s.startWidths[i] + s.startWidths[i + 1] - MIN_PANE_WIDTH_PERCENT;
689
- }
690
- if (rightW < MIN_PANE_WIDTH_PERCENT) {
691
- rightW = MIN_PANE_WIDTH_PERCENT;
692
- leftW = s.startWidths[i] + s.startWidths[i + 1] - MIN_PANE_WIDTH_PERCENT;
693
- }
694
- const tab = tabs.value.find((t) => t.id === s.tabId);
695
- if (tab) {
696
- tab.panes[i].widthPercent = leftW;
697
- tab.panes[i + 1].widthPercent = rightW;
698
- }
699
- }
700
- function onPaneResizeEnd() {
701
- if (!paneResizing.value) return;
702
- paneResizing.value = false;
703
- document.body.classList.remove("cli-pane-resizing");
704
- }
705
- (0, import_vue3.onMounted)(() => {
706
- document.addEventListener("mousemove", onPaneResizeMove);
707
- document.addEventListener("mouseup", onPaneResizeEnd);
232
+ const state = (0, import_vue3.shallowRef)(controller.getSnapshot());
233
+ const unsubscribe = controller.subscribe(() => {
234
+ state.value = controller.getSnapshot();
708
235
  });
236
+ (0, import_vue3.watch)(controlled, (values) => controller.setControlled(values));
237
+ (0, import_vue3.watch)(mergedOptions, (options) => controller.setOptions(options));
238
+ const root = (0, import_vue3.shallowRef)(null);
239
+ (0, import_vue3.onMounted)(() => controller.setRootElement(root.value));
709
240
  (0, import_vue3.onBeforeUnmount)(() => {
710
- document.removeEventListener("mousemove", onPaneResizeMove);
711
- document.removeEventListener("mouseup", onPaneResizeEnd);
712
- stopStatusPolling();
241
+ unsubscribe();
242
+ controller.destroy();
713
243
  });
714
- function toggleMaximize() {
715
- if (!resolvedMaximized.value) {
716
- if (isHorizontal.value) {
717
- prevWidth.value = resolvedWidth.value;
718
- updateWidth(window.innerWidth);
719
- } else {
720
- prevHeight.value = resolvedHeight.value;
721
- updateHeight(window.innerHeight);
722
- }
723
- } else {
724
- if (isHorizontal.value) {
725
- updateWidth(prevWidth.value);
726
- } else {
727
- updateHeight(prevHeight.value);
728
- }
729
- }
730
- updateMaximized(!resolvedMaximized.value);
731
- }
732
- function handleClose() {
733
- visible.value = false;
734
- emit("close");
735
- props.onClose?.();
736
- }
737
- function handleHide() {
738
- preHideCollapsed = resolvedCollapsed.value;
739
- updateHidden(true);
740
- }
741
- function handleUnhide() {
742
- updateHidden(false);
743
- updateCollapsed(preHideCollapsed);
244
+ const exposed = {};
245
+ for (const method of PANEL_REF_METHODS) {
246
+ exposed[method] = controller[method].bind(controller);
744
247
  }
745
- const positionDropdownOpen = (0, import_vue3.ref)(false);
746
- function closePositionDropdown() {
747
- positionDropdownOpen.value = false;
748
- servicesDropdownOpen.value = false;
749
- serversDropdownOpen.value = false;
750
- }
751
- function togglePositionDropdown(e) {
248
+ expose(exposed);
249
+ const openDropdown = (e, dropdown) => {
250
+ if (!dropdown) return;
752
251
  e.stopPropagation();
753
- positionDropdownOpen.value = !positionDropdownOpen.value;
754
- }
755
- function selectPosition(pos) {
756
- positionDropdownOpen.value = false;
757
- updatePosition(pos);
758
- }
759
- function getEngine(paneId) {
760
- const targetId = paneId ?? activePaneId.value;
761
- return engineMap.get(targetId);
762
- }
763
- function getState() {
764
- return {
765
- collapsed: resolvedCollapsed.value,
766
- hidden: resolvedHidden.value,
767
- maximized: resolvedMaximized.value,
768
- position: resolvedPosition.value,
769
- height: resolvedHeight.value,
770
- width: resolvedWidth.value,
771
- activeTabId: resolvedActiveTabId.value,
772
- activePaneId: activePaneId.value,
773
- tabs: tabs.value.map((t) => ({
774
- id: t.id,
775
- title: t.title,
776
- panes: t.panes.map((p) => ({ id: p.id, widthPercent: p.widthPercent }))
777
- }))
778
- };
779
- }
780
- expose({
781
- open: () => {
782
- if (!visible.value) {
783
- visible.value = true;
784
- }
785
- if (resolvedHidden.value) {
786
- updateHidden(false);
787
- }
788
- if (resolvedCollapsed.value) {
789
- updateCollapsed(false);
790
- if (!initialized.value) {
791
- initialized.value = true;
792
- addTab();
793
- setupThemeSync();
794
- }
795
- }
796
- },
797
- collapse: () => {
798
- if (!resolvedCollapsed.value) updateCollapsed(true);
799
- },
800
- toggleCollapse: () => {
801
- if (resolvedCollapsed.value) {
802
- updateCollapsed(false);
803
- if (!initialized.value) {
804
- initialized.value = true;
805
- addTab();
806
- setupThemeSync();
807
- }
808
- } else {
809
- updateCollapsed(true);
810
- }
811
- },
812
- hide: () => {
813
- if (!resolvedHidden.value) updateHidden(true);
814
- },
815
- unhide: () => {
816
- if (resolvedHidden.value) updateHidden(false);
817
- },
818
- toggleHide: () => {
819
- updateHidden(!resolvedHidden.value);
820
- },
821
- close: handleClose,
822
- maximize: () => {
823
- if (!resolvedMaximized.value) updateMaximized(true);
824
- },
825
- restore: () => {
826
- if (resolvedMaximized.value) updateMaximized(false);
827
- },
828
- toggleMaximize: () => {
829
- updateMaximized(!resolvedMaximized.value);
830
- },
831
- resize: (dims) => {
832
- if (dims.height !== void 0) updateHeight(dims.height);
833
- if (dims.width !== void 0) updateWidth(dims.width);
834
- },
835
- setPosition: updatePosition,
836
- addTab,
837
- closeTab,
838
- selectTab,
839
- renameTab,
840
- splitPane,
841
- closePane,
842
- getEngine,
843
- getState
844
- });
845
- return () => {
846
- if (!visible.value) return null;
847
- const wrapperClass = [
848
- "cli-panel-wrapper",
849
- resolvedCollapsed.value && "collapsed",
850
- resolvedMaximized.value && "maximized",
851
- panelResizing.value && "resizing",
852
- props.class
853
- ].filter(Boolean).join(" ");
854
- const wrapperStyle = {
855
- ...isHorizontal.value ? { width: `${resolvedWidth.value}px` } : { height: `${resolvedHeight.value}px` },
856
- ...resolvedHidden.value ? { display: "none" } : {},
857
- ...themeStyles.value,
858
- ...props.style
859
- };
860
- const headerEl = (0, import_vue3.h)("div", { class: "cli-panel-header" }, [
252
+ const anchor = e.currentTarget;
253
+ if (dropdown === "services") controller.toggleServicesDropdown(anchor);
254
+ else controller.toggleServersDropdown(anchor);
255
+ };
256
+ const renderHeader = (s) => {
257
+ const indicators = (0, import_cli2.statusIndicators)(s);
258
+ return (0, import_vue3.h)("div", { class: "cli-panel-header" }, [
861
259
  (0, import_vue3.h)(
862
260
  "div",
863
261
  {
864
262
  class: "cli-panel-resize-bar",
865
- onMousedown: onPanelResizeStart
263
+ onMousedown: (e) => {
264
+ if (!s.resizable) return;
265
+ e.preventDefault();
266
+ controller.startPanelResize(e.clientX, e.clientY);
267
+ }
866
268
  },
867
269
  [(0, import_vue3.h)("div", { class: "cli-panel-resize-grip" })]
868
270
  ),
869
271
  (0, import_vue3.h)("div", { class: "cli-panel-header-content" }, [
870
- (0, import_vue3.h)("p", { class: "cli-panel-title" }, [
871
- (0, import_vue3.h)("span", { class: "cli-panel-title-icon" }, [
872
- terminalIcon()
873
- ]),
874
- "CLI"
875
- ]),
876
- // Status indicators (bottom/top only)
877
- resolvedPosition.value === "bottom" || resolvedPosition.value === "top" ? (0, import_vue3.h)("div", { class: "cli-panel-status-indicators" }, [
878
- // Execution state
879
- (0, import_vue3.h)("span", { class: ["cli-panel-status-item", statusExecutionState.value === "running" ? "status-running" : ""].filter(Boolean).join(" ") }, [
880
- (0, import_vue3.h)("span", { class: ["cli-panel-status-dot", statusExecutionState.value === "running" ? "dot-running" : "dot-idle"].join(" ") }),
881
- (0, import_vue3.h)("span", { class: "cli-panel-status-label" }, statusExecutionState.value)
882
- ]),
883
- // Background services
884
- statusServiceCount.value.total > 0 ? (0, import_vue3.h)("span", {
885
- class: "cli-panel-status-item status-clickable",
886
- onClick: (e) => {
887
- e.stopPropagation();
888
- servicesDropdownTriggerRect.value = e.currentTarget.getBoundingClientRect();
889
- servicesDropdownOpen.value = !servicesDropdownOpen.value;
890
- serversDropdownOpen.value = false;
891
- }
892
- }, [
893
- (0, import_vue3.h)("span", { class: "cli-panel-status-icon", innerHTML: "&#9881;" }),
894
- (0, import_vue3.h)("span", { class: "cli-panel-status-label" }, `${statusServiceCount.value.running}/${statusServiceCount.value.total} services`)
895
- ]) : null,
896
- // Last command
897
- statusLastCommand.value ? (0, import_vue3.h)("span", { class: "cli-panel-status-item" }, [
898
- (0, import_vue3.h)("span", { class: ["cli-panel-status-icon", statusLastCommand.value.success ? "status-success" : "status-error"].join(" "), innerHTML: statusLastCommand.value.success ? "&#10003;" : "&#10005;" }),
899
- (0, import_vue3.h)("span", { class: "cli-panel-status-label" }, statusLastCommand.value.name)
900
- ]) : null,
901
- // Server connection
902
- statusServerState.value !== "none" ? (0, import_vue3.h)("span", {
903
- class: "cli-panel-status-item status-clickable",
904
- onClick: (e) => {
905
- e.stopPropagation();
906
- serversDropdownTriggerRect.value = e.currentTarget.getBoundingClientRect();
907
- serversDropdownOpen.value = !serversDropdownOpen.value;
908
- servicesDropdownOpen.value = false;
909
- }
910
- }, [
911
- (0, import_vue3.h)("span", { class: ["cli-panel-status-dot", statusServerState.value === "connected" ? "dot-idle" : "dot-error"].join(" ") }),
912
- (0, import_vue3.h)("span", { class: "cli-panel-status-label" }, `${statusServerDetails.value.filter((s) => s.connected).length}/${statusServerDetails.value.length} servers`)
913
- ]) : null,
914
- // Uptime
915
- statusUptime.value > 0 ? (0, import_vue3.h)("span", { class: "cli-panel-status-item status-muted" }, [
916
- (0, import_vue3.h)("span", { class: "cli-panel-status-icon", innerHTML: "&uarr;" }),
917
- (0, import_vue3.h)("span", { class: "cli-panel-status-label" }, formattedUptime.value)
918
- ]) : null,
919
- // Custom notification
920
- notification.value ? (0, import_vue3.h)("span", { class: `cli-panel-status-item status-text level-${notification.value.level}` }, [
921
- (0, import_vue3.h)("span", { class: "cli-panel-status-label" }, notification.value.message)
922
- ]) : null
923
- ].filter(Boolean)) : null,
924
- // Compact status indicators (left/right positions — CSS controls visibility)
925
- (0, import_vue3.h)("div", { class: "cli-panel-status-indicators-compact" }, [
926
- // Execution state
927
- (0, import_vue3.h)("span", { class: "cli-panel-status-compact-item", title: statusExecutionState.value }, [
928
- (0, import_vue3.h)("span", { class: ["cli-panel-status-dot", statusExecutionState.value === "running" ? "dot-running" : "dot-idle"].join(" ") })
929
- ]),
930
- // Background services
931
- statusServiceCount.value.total > 0 ? (0, import_vue3.h)("span", {
932
- class: "cli-panel-status-compact-item status-clickable",
933
- title: `${statusServiceCount.value.running}/${statusServiceCount.value.total} services`,
934
- onClick: (e) => {
935
- e.stopPropagation();
936
- servicesDropdownTriggerRect.value = e.currentTarget.getBoundingClientRect();
937
- servicesDropdownOpen.value = !servicesDropdownOpen.value;
938
- serversDropdownOpen.value = false;
939
- }
940
- }, [
941
- (0, import_vue3.h)("span", { class: "cli-panel-status-icon", innerHTML: "&#9881;" })
942
- ]) : null,
943
- // Last command
944
- statusLastCommand.value ? (0, import_vue3.h)("span", {
945
- class: "cli-panel-status-compact-item",
946
- title: `${statusLastCommand.value.success ? "\u2713" : "\u2717"} ${statusLastCommand.value.name}`
947
- }, [
948
- (0, import_vue3.h)("span", {
949
- class: ["cli-panel-status-icon", statusLastCommand.value.success ? "status-success" : "status-error"].join(" "),
950
- innerHTML: statusLastCommand.value.success ? "&#10003;" : "&#10005;"
951
- })
952
- ]) : null,
953
- // Server connection
954
- statusServerState.value !== "none" ? (0, import_vue3.h)("span", {
955
- class: "cli-panel-status-compact-item status-clickable",
956
- title: `${statusServerDetails.value.filter((s) => s.connected).length}/${statusServerDetails.value.length} servers`,
957
- onClick: (e) => {
958
- e.stopPropagation();
959
- serversDropdownTriggerRect.value = e.currentTarget.getBoundingClientRect();
960
- serversDropdownOpen.value = !serversDropdownOpen.value;
961
- servicesDropdownOpen.value = false;
962
- }
963
- }, [
964
- (0, import_vue3.h)("span", { class: ["cli-panel-status-dot", statusServerState.value === "connected" ? "dot-idle" : "dot-error"].join(" ") })
965
- ]) : null,
966
- // Uptime
967
- statusUptime.value > 0 ? (0, import_vue3.h)("span", { class: "cli-panel-status-compact-item status-muted", title: formattedUptime.value }, [
968
- (0, import_vue3.h)("span", { class: "cli-panel-status-icon", innerHTML: "&uarr;" })
969
- ]) : null,
970
- // Notification
971
- notification.value ? (0, import_vue3.h)("span", { class: "cli-panel-status-compact-item", title: notification.value.message }, [
972
- (0, import_vue3.h)("span", { class: `cli-panel-status-compact-dot level-${notification.value.level}` })
973
- ]) : null
974
- ].filter(Boolean)),
272
+ (0, import_vue3.h)("p", { class: "cli-panel-title" }, [(0, import_vue3.h)("span", { class: "cli-panel-title-icon" }, [icon(import_cli2.TERMINAL_ICON)]), import_cli2.PANEL_LABELS.title]),
273
+ s.showStatusIndicators ? (0, import_vue3.h)(
274
+ "div",
275
+ { class: "cli-panel-status-indicators" },
276
+ indicators.map(
277
+ (item) => (0, import_vue3.h)(
278
+ "span",
279
+ {
280
+ key: item.key,
281
+ class: (0, import_cli2.joinClasses)("cli-panel-status-item", item.itemClass),
282
+ onClick: item.dropdown ? (e) => openDropdown(e, item.dropdown) : void 0
283
+ },
284
+ [
285
+ item.dotClass ? (0, import_vue3.h)("span", { class: `cli-panel-status-dot ${item.dotClass}` }) : null,
286
+ item.icon ? (0, import_vue3.h)("span", { class: (0, import_cli2.joinClasses)("cli-panel-status-icon", item.iconClass) }, item.icon) : null,
287
+ (0, import_vue3.h)("span", { class: "cli-panel-status-label" }, item.label)
288
+ ]
289
+ )
290
+ )
291
+ ) : null,
292
+ // Compact indicators for left/right positions (CSS controls visibility).
293
+ (0, import_vue3.h)(
294
+ "div",
295
+ { class: "cli-panel-status-indicators-compact" },
296
+ indicators.map(
297
+ (item) => (0, import_vue3.h)(
298
+ "span",
299
+ {
300
+ key: item.key,
301
+ class: (0, import_cli2.joinClasses)("cli-panel-status-compact-item", item.compactClass),
302
+ title: item.title,
303
+ onClick: item.dropdown ? (e) => openDropdown(e, item.dropdown) : void 0
304
+ },
305
+ [
306
+ item.compactDotClass ? (0, import_vue3.h)("span", { class: item.compactDotClass }) : null,
307
+ item.dotClass ? (0, import_vue3.h)("span", { class: `cli-panel-status-dot ${item.dotClass}` }) : null,
308
+ item.icon ? (0, import_vue3.h)("span", { class: (0, import_cli2.joinClasses)("cli-panel-status-icon", item.iconClass) }, item.icon) : null
309
+ ]
310
+ )
311
+ )
312
+ ),
975
313
  (0, import_vue3.h)("div", { class: "cli-panel-action-buttons" }, [
976
- (0, import_vue3.h)("div", {
977
- class: "cli-panel-btn-position-wrapper",
978
- onClick: (e) => e.stopPropagation()
979
- }, [
980
- (0, import_vue3.h)("button", {
981
- class: "cli-panel-btn cli-panel-btn-position",
982
- title: "Move panel",
983
- onClick: togglePositionDropdown
984
- }, [positionIcon(resolvedPosition.value)]),
985
- positionDropdownOpen.value ? (0, import_vue3.h)(
314
+ (0, import_vue3.h)("div", { class: "cli-panel-btn-position-wrapper", onClick: stop }, [
315
+ (0, import_vue3.h)(
316
+ "button",
317
+ {
318
+ class: "cli-panel-btn cli-panel-btn-position",
319
+ title: import_cli2.PANEL_LABELS.movePanel,
320
+ onClick: () => controller.togglePositionDropdown()
321
+ },
322
+ [icon((0, import_cli2.positionIcon)(s.position))]
323
+ ),
324
+ s.positionDropdownOpen ? (0, import_vue3.h)(
986
325
  "div",
987
326
  { class: "cli-panel-position-dropdown" },
988
- ["bottom", "top", "left", "right"].map(
989
- (pos) => (0, import_vue3.h)("button", {
990
- class: ["cli-panel-position-dropdown-item", resolvedPosition.value === pos ? "active" : ""].filter(Boolean).join(" "),
991
- onClick: () => selectPosition(pos)
992
- }, [
993
- positionIcon(pos, 16),
994
- pos.charAt(0).toUpperCase() + pos.slice(1)
995
- ])
327
+ (0, import_cli2.positionOptions)(s).map(
328
+ (opt) => (0, import_vue3.h)(
329
+ "button",
330
+ { key: opt.position, class: opt.className, onClick: () => controller.setPosition(opt.position) },
331
+ [icon((0, import_cli2.positionIcon)(opt.position, 16)), opt.label]
332
+ )
996
333
  )
997
334
  ) : null
998
335
  ]),
999
- hideable.value ? (0, import_vue3.h)("button", {
1000
- class: "cli-panel-btn cli-panel-btn-hide",
1001
- title: "Hide",
1002
- onClick: handleHide
1003
- }, [hideIcon()]) : null,
336
+ s.hideable ? (0, import_vue3.h)("button", { class: "cli-panel-btn cli-panel-btn-hide", title: import_cli2.PANEL_LABELS.hide, onClick: () => controller.hide() }, [icon(import_cli2.HIDE_ICON)]) : null,
1004
337
  (0, import_vue3.h)(
1005
338
  "button",
1006
339
  {
1007
340
  class: "cli-panel-btn",
1008
- title: resolvedMaximized.value ? "Restore" : "Maximize",
1009
- disabled: resolvedCollapsed.value,
1010
- onClick: toggleMaximize
341
+ title: s.maximized ? import_cli2.PANEL_LABELS.restore : import_cli2.PANEL_LABELS.maximize,
342
+ disabled: s.collapsed,
343
+ onClick: () => controller.toggleMaximize()
1011
344
  },
1012
- [resolvedMaximized.value ? restoreIcon() : maximizeIcon()]
345
+ [icon(s.maximized ? import_cli2.RESTORE_ICON : import_cli2.MAXIMIZE_ICON)]
1013
346
  ),
1014
347
  (0, import_vue3.h)(
1015
348
  "button",
1016
349
  {
1017
350
  class: "cli-panel-btn",
1018
- title: resolvedCollapsed.value ? "Expand" : "Collapse",
1019
- onClick: toggle
351
+ title: s.collapsed ? import_cli2.PANEL_LABELS.expand : import_cli2.PANEL_LABELS.collapse,
352
+ onClick: () => controller.toggle()
1020
353
  },
1021
- [collapseChevron(resolvedPosition.value, resolvedCollapsed.value)]
354
+ [icon((0, import_cli2.collapseChevronIcon)(s.position, s.collapsed))]
1022
355
  ),
1023
- closable.value ? (0, import_vue3.h)(
1024
- "button",
1025
- {
1026
- class: "cli-panel-btn cli-panel-btn-close",
1027
- title: "Close",
1028
- onClick: handleClose
1029
- },
1030
- [closeIcon()]
1031
- ) : null
356
+ s.closable ? (0, import_vue3.h)("button", { class: "cli-panel-btn cli-panel-btn-close", title: import_cli2.PANEL_LABELS.close, onClick: () => controller.close() }, [icon(import_cli2.CLOSE_ICON)]) : null
1032
357
  ])
1033
358
  ]),
1034
- (0, import_vue3.h)("div", { class: ["cli-panel-glow-line", statusExecutionState.value === "running" ? "glow-active" : ""].filter(Boolean).join(" ") })
359
+ (0, import_vue3.h)("div", { class: (0, import_cli2.glowLineClass)(s) })
1035
360
  ]);
1036
- const contentEl = initialized.value ? (0, import_vue3.h)("div", { class: "cli-panel-content", style: resolvedCollapsed.value ? { display: "none" } : void 0 }, [
1037
- // Tab bar
1038
- (0, import_vue3.h)("div", { class: "cli-panel-tabs" }, [
361
+ };
362
+ const renderTabs = (s) => (0, import_vue3.h)("div", { class: "cli-panel-tabs" }, [
363
+ (0, import_vue3.h)(
364
+ "ul",
365
+ { class: "cli-panel-tab-list" },
366
+ s.tabs.map(
367
+ (tab) => (0, import_vue3.h)(
368
+ "li",
369
+ {
370
+ key: tab.id,
371
+ class: (0, import_cli2.panelTabClass)(s, tab),
372
+ onClick: () => controller.selectTab(tab.id),
373
+ onDblclick: () => controller.startRename(tab.id),
374
+ onContextmenu: (e) => {
375
+ e.preventDefault();
376
+ e.stopPropagation();
377
+ controller.openContextMenu(tab.id, e.clientX, e.clientY);
378
+ }
379
+ },
380
+ tab.isEditing ? [
381
+ (0, import_vue3.h)("input", {
382
+ class: "cli-panel-tab-rename-input",
383
+ type: "text",
384
+ value: tab.title,
385
+ onVnodeMounted: (vnode) => {
386
+ const input = vnode.el;
387
+ input.focus();
388
+ input.select();
389
+ },
390
+ onKeydown: (e) => (0, import_cli2.handleRenameKey)(controller, tab.id, e.key, e.target.value),
391
+ onBlur: (e) => controller.commitRename(tab.id, e.target.value),
392
+ onClick: stop,
393
+ onDblclick: stop
394
+ })
395
+ ] : [
396
+ (0, import_vue3.h)("span", { class: (0, import_cli2.panelTabDotClass)(s, tab) }),
397
+ (0, import_vue3.h)("span", { class: "cli-panel-tab-title" }, tab.title),
398
+ (0, import_vue3.h)(
399
+ "button",
400
+ {
401
+ class: "cli-panel-tab-close-btn",
402
+ title: import_cli2.PANEL_LABELS.closeTab,
403
+ onClick: (e) => {
404
+ e.stopPropagation();
405
+ controller.closeTab(tab.id);
406
+ }
407
+ },
408
+ import_cli2.CLOSE_GLYPH
409
+ )
410
+ ]
411
+ )
412
+ )
413
+ ),
414
+ (0, import_vue3.h)("button", { class: "cli-panel-add-tab", title: import_cli2.PANEL_LABELS.newTerminal, onClick: () => controller.addTab() }, "+")
415
+ ]);
416
+ const renderInstances = (s) => (0, import_vue3.h)(
417
+ "div",
418
+ { class: "cli-panel-instances" },
419
+ s.tabs.map(
420
+ (tab) => (0, import_vue3.h)("div", { key: tab.id, class: "cli-panel-instance", style: { display: tab.id === s.activeTabId ? void 0 : "none" } }, [
1039
421
  (0, import_vue3.h)(
1040
- "ul",
1041
- { class: "cli-panel-tab-list" },
1042
- tabs.value.map(
1043
- (tab) => (0, import_vue3.h)(
1044
- "li",
422
+ "div",
423
+ { class: (0, import_cli2.panesContainerClass)(s) },
424
+ tab.panes.flatMap((pane, i) => [
425
+ i > 0 ? (0, import_vue3.h)(
426
+ "div",
1045
427
  {
1046
- key: tab.id,
1047
- class: [
1048
- "cli-panel-tab",
1049
- tab.id === resolvedActiveTabId.value && "active"
1050
- ].filter(Boolean).join(" "),
1051
- onClick: () => selectTab(tab.id),
1052
- onDblclick: () => startRename(tab.id),
1053
- onContextmenu: (e) => {
428
+ key: `divider-${pane.id}`,
429
+ class: "cli-panel-pane-divider",
430
+ onMousedown: (e) => {
1054
431
  e.preventDefault();
1055
- e.stopPropagation();
1056
- contextMenu.value = {
1057
- visible: true,
1058
- x: e.clientX,
1059
- y: e.clientY,
1060
- tabId: tab.id
1061
- };
432
+ controller.startPaneResize(tab.id, i - 1, e.clientX, (0, import_cli2.panesContainerWidth)(e.target));
1062
433
  }
1063
434
  },
1064
- tab.isEditing ? [
1065
- (0, import_vue3.h)("input", {
1066
- class: "cli-panel-tab-rename-input",
1067
- type: "text",
1068
- value: tab.title,
1069
- onKeydown: (e) => {
1070
- if (e.key === "Enter")
1071
- commitRename(
1072
- tab.id,
1073
- e.target.value
1074
- );
1075
- else if (e.key === "Escape") {
1076
- tab.isEditing = false;
1077
- }
1078
- },
1079
- onBlur: (e) => commitRename(
1080
- tab.id,
1081
- e.target.value
1082
- ),
1083
- onClick: (e) => e.stopPropagation(),
1084
- onDblclick: (e) => e.stopPropagation()
1085
- })
1086
- ] : [
1087
- (0, import_vue3.h)("span", { class: ["cli-panel-tab-dot", (() => {
1088
- if (tab.id !== resolvedActiveTabId.value) return "dot-idle";
1089
- if (statusExecutionState.value === "running") return "dot-running";
1090
- if (statusLastCommand.value && !statusLastCommand.value.success) return "dot-error";
1091
- return "dot-idle";
1092
- })()].join(" ") }),
1093
- (0, import_vue3.h)(
1094
- "span",
1095
- {
1096
- class: "cli-panel-tab-title"
1097
- },
1098
- tab.title
1099
- ),
1100
- (0, import_vue3.h)(
435
+ [(0, import_vue3.h)("div", { class: "cli-panel-pane-divider-grip" })]
436
+ ) : null,
437
+ (0, import_vue3.h)(
438
+ "div",
439
+ {
440
+ key: pane.id,
441
+ class: (0, import_cli2.paneClass)(s, tab.id, pane.id),
442
+ style: (0, import_cli2.paneStyle)(pane.widthPercent),
443
+ onClick: () => controller.activatePane(tab.id, pane.id)
444
+ },
445
+ [
446
+ tab.panes.length > 1 ? (0, import_vue3.h)(
1101
447
  "button",
1102
448
  {
1103
- class: "cli-panel-tab-close-btn",
1104
- title: "Close tab",
449
+ class: "cli-panel-pane-close-btn",
450
+ title: import_cli2.PANEL_LABELS.closePane,
1105
451
  onClick: (e) => {
1106
452
  e.stopPropagation();
1107
- closeTab(
1108
- tab.id
1109
- );
453
+ controller.closePane(pane.id);
1110
454
  }
1111
455
  },
1112
- "\xD7"
1113
- )
456
+ import_cli2.CLOSE_GLYPH
457
+ ) : null,
458
+ (0, import_vue3.h)(Cli, {
459
+ options: mergedOptions.value,
460
+ modules: hostConfig.value.modules,
461
+ processors: hostConfig.value.processors,
462
+ services: hostConfig.value.services,
463
+ snapshot: pane.snapshot,
464
+ style: { height: "100%" },
465
+ onReady: (engine) => controller.registerEngine(tab.id, pane.id, engine)
466
+ })
1114
467
  ]
1115
468
  )
1116
- )
1117
- ),
1118
- (0, import_vue3.h)(
1119
- "button",
1120
- {
1121
- class: "cli-panel-add-tab",
1122
- title: "New terminal",
1123
- onClick: () => addTab()
1124
- },
1125
- "+"
469
+ ])
1126
470
  )
1127
- ]),
1128
- // Terminal instances
471
+ ])
472
+ )
473
+ );
474
+ const renderContextMenu = (s) => (0, import_vue3.h)(
475
+ "div",
476
+ { class: "cli-panel-context-menu", style: (0, import_cli2.contextMenuStyle)(s), onClick: stop },
477
+ (0, import_cli2.contextMenuItems)(s).flatMap((item) => [
478
+ item.separatorBefore ? (0, import_vue3.h)("div", { class: "cli-panel-context-menu-separator" }) : null,
1129
479
  (0, import_vue3.h)(
1130
- "div",
1131
- { class: "cli-panel-instances" },
1132
- tabs.value.map(
1133
- (tab) => (0, import_vue3.h)(
1134
- "div",
1135
- {
1136
- key: tab.id,
1137
- class: "cli-panel-instance",
1138
- style: {
1139
- display: tab.id === resolvedActiveTabId.value ? void 0 : "none"
1140
- }
1141
- },
1142
- [
1143
- (0, import_vue3.h)(
1144
- "div",
1145
- {
1146
- class: [
1147
- "cli-panel-panes-container",
1148
- paneResizing.value && "resizing"
1149
- ].filter(Boolean).join(" ")
1150
- },
1151
- tab.panes.flatMap((pane, i) => {
1152
- const nodes = [];
1153
- if (i > 0) {
1154
- nodes.push(
1155
- (0, import_vue3.h)(
1156
- "div",
1157
- {
1158
- class: "cli-panel-pane-divider",
1159
- onMousedown: (e) => onPaneResizeStart(
1160
- e,
1161
- tab.id,
1162
- i - 1
1163
- )
1164
- },
1165
- [
1166
- (0, import_vue3.h)("div", {
1167
- class: "cli-panel-pane-divider-grip"
1168
- })
1169
- ]
1170
- )
1171
- );
1172
- }
1173
- nodes.push(
1174
- (0, import_vue3.h)(
1175
- "div",
1176
- {
1177
- class: [
1178
- "cli-panel-pane",
1179
- pane.id === activePaneId.value && tab.id === resolvedActiveTabId.value && "active-pane"
1180
- ].filter(
1181
- Boolean
1182
- ).join(" "),
1183
- style: {
1184
- flex: `${pane.widthPercent} 1 0`
1185
- },
1186
- onClick: () => {
1187
- activePaneId.value = pane.id;
1188
- updateActiveTabId(tab.id);
1189
- }
1190
- },
1191
- [
1192
- tab.panes.length > 1 ? (0, import_vue3.h)(
1193
- "button",
1194
- {
1195
- class: "cli-panel-pane-close-btn",
1196
- title: "Close pane",
1197
- onClick: (e) => {
1198
- e.stopPropagation();
1199
- closePane(
1200
- pane.id
1201
- );
1202
- }
1203
- },
1204
- "\xD7"
1205
- ) : null,
1206
- (0, import_vue3.h)(Cli, {
1207
- options: mergedOptions.value,
1208
- modules: mergedModules.value,
1209
- processors: mergedProcessors.value,
1210
- services: mergedServices.value,
1211
- snapshot: pane.snapshot,
1212
- style: {
1213
- height: "100%"
1214
- },
1215
- onReady: (engine) => {
1216
- engineMap.set(pane.id, engine);
1217
- }
1218
- })
1219
- ]
1220
- )
1221
- );
1222
- return nodes;
1223
- })
1224
- )
1225
- ]
1226
- )
1227
- )
1228
- )
1229
- ]) : null;
1230
- const contextMenuEl = contextMenu.value.visible ? (0, import_vue3.h)(
1231
- "div",
1232
- {
1233
- class: "cli-panel-context-menu",
1234
- style: {
1235
- left: `${contextMenu.value.x}px`,
1236
- top: `${contextMenu.value.y}px`
480
+ "button",
481
+ {
482
+ class: (0, import_cli2.joinClasses)("cli-panel-context-menu-item", item.destructive && "destructive"),
483
+ disabled: item.disabled,
484
+ onClick: () => (0, import_cli2.runContextMenuAction)(controller, item.action)
1237
485
  },
1238
- onClick: (e) => e.stopPropagation()
1239
- },
1240
- [
1241
- (0, import_vue3.h)(
1242
- "button",
1243
- {
1244
- class: "cli-panel-context-menu-item",
1245
- onClick: () => {
1246
- closeContextMenu();
1247
- startRename(contextMenu.value.tabId);
1248
- }
1249
- },
1250
- "Rename"
1251
- ),
1252
- (0, import_vue3.h)(
1253
- "button",
1254
- {
1255
- class: "cli-panel-context-menu-item",
1256
- onClick: () => {
1257
- const tab = tabs.value.find(
1258
- (t) => t.id === contextMenu.value.tabId
1259
- );
1260
- closeContextMenu();
1261
- if (!tab) return;
1262
- const sourceEngine = engineMap.get(tab.panes[0]?.id);
1263
- const snapshot = sourceEngine?.snapshot();
1264
- const paneId = nextPaneId++;
1265
- const tabId = nextTabId++;
1266
- const idx = tabs.value.indexOf(tab);
1267
- tabs.value.splice(idx + 1, 0, {
1268
- id: tabId,
1269
- title: `${tab.title} (copy)`,
1270
- isEditing: false,
1271
- panes: [
1272
- { id: paneId, widthPercent: 100, snapshot }
1273
- ]
1274
- });
1275
- updateActiveTabId(tabId);
1276
- activePaneId.value = paneId;
1277
- }
1278
- },
1279
- "Duplicate"
1280
- ),
1281
- (0, import_vue3.h)(
1282
- "button",
1283
- {
1284
- class: "cli-panel-context-menu-item",
1285
- onClick: () => {
1286
- closeContextMenu();
1287
- splitPane(contextMenu.value.tabId);
1288
- }
1289
- },
1290
- "Split Right"
1291
- ),
1292
- (0, import_vue3.h)("div", {
1293
- class: "cli-panel-context-menu-separator"
1294
- }),
1295
- (0, import_vue3.h)(
1296
- "button",
1297
- {
1298
- class: "cli-panel-context-menu-item",
1299
- onClick: () => {
1300
- closeContextMenu();
1301
- closeTab(contextMenu.value.tabId);
1302
- }
1303
- },
1304
- "Close"
1305
- ),
1306
- (0, import_vue3.h)(
1307
- "button",
1308
- {
1309
- class: "cli-panel-context-menu-item",
1310
- disabled: tabs.value.length <= 1,
1311
- onClick: () => {
1312
- const id = contextMenu.value.tabId;
1313
- closeContextMenu();
1314
- tabs.value = tabs.value.filter(
1315
- (t) => t.id === id
1316
- );
1317
- updateActiveTabId(id);
1318
- }
1319
- },
1320
- "Close Others"
1321
- ),
1322
- (0, import_vue3.h)(
1323
- "button",
1324
- {
1325
- class: "cli-panel-context-menu-item",
1326
- onClick: () => {
1327
- const id = contextMenu.value.tabId;
1328
- closeContextMenu();
1329
- const idx = tabs.value.findIndex(
1330
- (t) => t.id === id
1331
- );
1332
- if (idx !== -1)
1333
- tabs.value = tabs.value.slice(
1334
- 0,
1335
- idx + 1
1336
- );
1337
- }
1338
- },
1339
- "Close to the Right"
1340
- ),
1341
- (0, import_vue3.h)("div", {
1342
- class: "cli-panel-context-menu-separator"
1343
- }),
1344
- (0, import_vue3.h)(
1345
- "button",
1346
- {
1347
- class: "cli-panel-context-menu-item destructive",
1348
- onClick: () => {
1349
- closeContextMenu();
1350
- tabs.value = [];
1351
- initialized.value = false;
1352
- updateCollapsed(true);
1353
- }
1354
- },
1355
- "Close All"
1356
- )
1357
- ]
1358
- ) : null;
1359
- const hideTabEl = resolvedHidden.value ? (0, import_vue3.h)("button", {
1360
- class: "cli-panel-hide-tab",
1361
- "data-position": resolvedPosition.value,
1362
- "data-hide-align": hideAlignment.value,
1363
- style: themeStyles.value,
1364
- title: "Show CLI",
1365
- onClick: handleUnhide
1366
- }, [
1367
- (0, import_vue3.h)("svg", { class: "cli-panel-hide-tab-icon", ...svgAttrs, width: "16", height: "16", "stroke-width": "2" }, [
1368
- (0, import_vue3.h)("polyline", { points: "4 17 10 11 4 5" }),
1369
- (0, import_vue3.h)("line", { x1: "12", y1: "19", x2: "20", y2: "19" })
1370
- ]),
1371
- (0, import_vue3.h)("span", { class: "cli-panel-hide-tab-label" }, "CLI"),
1372
- hideTabChevron(resolvedPosition.value)
1373
- ]) : null;
1374
- const servicesDropdownEl = servicesDropdownOpen.value && statusServiceCount.value.total > 0 ? (0, import_vue3.h)("div", {
1375
- class: "cli-panel-services-dropdown",
1376
- style: (() => {
1377
- const rect = servicesDropdownTriggerRect.value;
1378
- if (!rect) return {};
1379
- const base = { position: "fixed", zIndex: 1100 };
1380
- switch (resolvedPosition.value) {
1381
- case "top":
1382
- return { ...base, top: `${rect.bottom + 4}px`, left: `${rect.left}px` };
1383
- case "left":
1384
- return { ...base, top: `${rect.top}px`, left: `${rect.right + 4}px` };
1385
- case "right":
1386
- return { ...base, top: `${rect.top}px`, right: `${window.innerWidth - rect.left + 4}px` };
1387
- default:
1388
- return { ...base, bottom: `${window.innerHeight - rect.top + 4}px`, left: `${rect.left}px` };
1389
- }
1390
- })(),
1391
- onClick: (e) => e.stopPropagation()
1392
- }, [
1393
- (0, import_vue3.h)("div", { class: "cli-panel-services-dropdown-header" }, "Background Services"),
1394
- (0, import_vue3.h)(
1395
- "div",
1396
- { class: "cli-panel-services-dropdown-list" },
1397
- statusServiceDetails.value.map(
1398
- (svc, i) => (0, import_vue3.h)("div", { key: i, class: "cli-panel-services-dropdown-item" }, [
1399
- (0, import_vue3.h)("span", { class: ["cli-panel-svc-dot", svc.status === "running" ? "svc-running" : "svc-stopped"].join(" ") }),
1400
- (0, import_vue3.h)("div", { class: "cli-panel-svc-info" }, [
1401
- (0, import_vue3.h)("span", { class: "cli-panel-svc-name" }, svc.name),
1402
- svc.description ? (0, import_vue3.h)("span", { class: "cli-panel-svc-desc" }, svc.description) : null
1403
- ].filter(Boolean)),
1404
- (0, import_vue3.h)("span", { class: "cli-panel-svc-status" }, svc.status)
1405
- ])
1406
- )
1407
- ),
1408
- statusServiceDetails.value.length === 0 ? (0, import_vue3.h)("div", { class: "cli-panel-services-dropdown-empty" }, "No services registered") : null
1409
- ].filter(Boolean)) : null;
1410
- const serversDropdownEl = serversDropdownOpen.value && statusServerState.value !== "none" ? (0, import_vue3.h)("div", {
1411
- class: "cli-panel-services-dropdown",
1412
- style: (() => {
1413
- const rect = serversDropdownTriggerRect.value;
1414
- if (!rect) return {};
1415
- const base = { position: "fixed", zIndex: 1100 };
1416
- switch (resolvedPosition.value) {
1417
- case "top":
1418
- return { ...base, top: `${rect.bottom + 4}px`, left: `${rect.left}px` };
1419
- case "left":
1420
- return { ...base, top: `${rect.top}px`, left: `${rect.right + 4}px` };
1421
- case "right":
1422
- return { ...base, top: `${rect.top}px`, right: `${window.innerWidth - rect.left + 4}px` };
1423
- default:
1424
- return { ...base, bottom: `${window.innerHeight - rect.top + 4}px`, left: `${rect.left}px` };
1425
- }
1426
- })(),
1427
- onClick: (e) => e.stopPropagation()
1428
- }, [
1429
- (0, import_vue3.h)("div", { class: "cli-panel-services-dropdown-header" }, "Server Connections"),
486
+ item.label
487
+ )
488
+ ])
489
+ );
490
+ const renderStatusDropdown = (s) => {
491
+ const dropdown = (0, import_cli2.openStatusDropdown)(s);
492
+ if (!dropdown) return null;
493
+ return (0, import_vue3.h)("div", { class: "cli-panel-services-dropdown", style: dropdown.style, onClick: stop }, [
494
+ (0, import_vue3.h)("div", { class: "cli-panel-services-dropdown-header" }, dropdown.header),
1430
495
  (0, import_vue3.h)(
1431
496
  "div",
1432
497
  { class: "cli-panel-services-dropdown-list" },
1433
- statusServerDetails.value.map(
1434
- (srv, i) => (0, import_vue3.h)("div", { key: i, class: "cli-panel-services-dropdown-item" }, [
1435
- (0, import_vue3.h)("span", { class: ["cli-panel-svc-dot", srv.connected ? "svc-running" : "svc-stopped"].join(" ") }),
498
+ dropdown.rows.map(
499
+ (row) => (0, import_vue3.h)("div", { key: row.key, class: "cli-panel-services-dropdown-item" }, [
500
+ (0, import_vue3.h)("span", { class: row.dotClass }),
1436
501
  (0, import_vue3.h)("div", { class: "cli-panel-svc-info" }, [
1437
- (0, import_vue3.h)("span", { class: "cli-panel-svc-name" }, srv.name),
1438
- srv.url ? (0, import_vue3.h)("span", { class: "cli-panel-svc-desc" }, srv.url) : null
1439
- ].filter(Boolean)),
1440
- (0, import_vue3.h)("div", { class: "cli-panel-srv-meta" }, [
1441
- (0, import_vue3.h)("span", { class: "cli-panel-svc-status" }, srv.connected ? "connected" : "disconnected"),
1442
- srv.apiVersion ? (0, import_vue3.h)("span", { class: "cli-panel-svc-desc" }, `v${srv.apiVersion}`) : null,
1443
- srv.commandCount ? (0, import_vue3.h)("span", { class: "cli-panel-svc-desc" }, `${srv.commandCount} cmds`) : null
1444
- ].filter(Boolean))
502
+ (0, import_vue3.h)("span", { class: "cli-panel-svc-name" }, row.name),
503
+ row.description ? (0, import_vue3.h)("span", { class: "cli-panel-svc-desc" }, row.description) : null
504
+ ]),
505
+ dropdown.kind === "servers" ? (0, import_vue3.h)("div", { class: "cli-panel-srv-meta" }, [
506
+ (0, import_vue3.h)("span", { class: "cli-panel-svc-status" }, row.status),
507
+ ...row.meta.map((m) => (0, import_vue3.h)("span", { key: m, class: "cli-panel-svc-desc" }, m))
508
+ ]) : (0, import_vue3.h)("span", { class: "cli-panel-svc-status" }, row.status)
1445
509
  ])
1446
510
  )
1447
511
  ),
1448
- statusServerDetails.value.length === 0 ? (0, import_vue3.h)("div", { class: "cli-panel-services-dropdown-empty" }, "No servers configured") : null
1449
- ].filter(Boolean)) : null;
1450
- return (0, import_vue3.h)("div", {}, [
1451
- hideTabEl,
512
+ dropdown.rows.length === 0 ? (0, import_vue3.h)("div", { class: "cli-panel-services-dropdown-empty" }, dropdown.emptyText) : null
513
+ ]);
514
+ };
515
+ return () => {
516
+ const s = state.value;
517
+ if (!s.visible) return null;
518
+ return (0, import_vue3.h)(import_vue3.Fragment, [
519
+ s.hidden ? (0, import_vue3.h)(
520
+ "button",
521
+ {
522
+ class: "cli-panel-hide-tab",
523
+ "data-position": s.position,
524
+ "data-hide-align": s.hideAlignment,
525
+ style: s.themeStyles,
526
+ title: import_cli2.PANEL_LABELS.showCli,
527
+ onClick: () => controller.unhide()
528
+ },
529
+ [
530
+ icon(import_cli2.HIDE_TAB_TERMINAL_ICON, "cli-panel-hide-tab-icon"),
531
+ (0, import_vue3.h)("span", { class: "cli-panel-hide-tab-label" }, import_cli2.PANEL_LABELS.title),
532
+ icon((0, import_cli2.hideTabChevronIcon)(s.position), "cli-panel-hide-tab-arrow")
533
+ ]
534
+ ) : null,
1452
535
  (0, import_vue3.h)(
1453
536
  "div",
1454
537
  {
1455
- class: wrapperClass,
1456
- style: wrapperStyle,
1457
- "data-position": resolvedPosition.value,
1458
- "data-resizable": String(resizable.value),
1459
- "data-closable": String(closable.value),
1460
- "data-hideable": String(hideable.value)
538
+ ref: root,
539
+ class: (0, import_cli2.panelWrapperClass)(s, props.class),
540
+ style: { ...(0, import_cli2.panelWrapperStyle)(s), ...props.style },
541
+ "data-position": s.position,
542
+ "data-resizable": String(s.resizable),
543
+ "data-closable": String(s.closable),
544
+ "data-hideable": String(s.hideable)
1461
545
  },
1462
- [headerEl, contentEl]
546
+ [
547
+ renderHeader(s),
548
+ s.initialized ? (0, import_vue3.h)("div", { class: "cli-panel-content", style: s.collapsed ? { display: "none" } : void 0 }, [
549
+ renderTabs(s),
550
+ renderInstances(s)
551
+ ]) : null
552
+ ]
1463
553
  ),
1464
- contextMenuEl,
1465
- servicesDropdownEl,
1466
- serversDropdownEl
554
+ s.contextMenu.visible ? renderContextMenu(s) : null,
555
+ renderStatusDropdown(s)
1467
556
  ]);
1468
557
  };
1469
558
  }
@@ -1474,24 +563,12 @@ var import_vue5 = require("vue");
1474
563
 
1475
564
  // src/useCliEngine.ts
1476
565
  var import_vue4 = require("vue");
1477
- var import_cli2 = require("@qodalis/cli");
566
+ var import_cli3 = require("@qodalis/cli");
1478
567
  function useCliEngine(containerRef, config) {
1479
568
  const engine = (0, import_vue4.shallowRef)(null);
1480
569
  (0, import_vue4.onMounted)(async () => {
1481
570
  if (!containerRef.value) return;
1482
- const e = new import_cli2.CliEngine(containerRef.value, config?.options);
1483
- e.registerService("cli-framework", "Vue");
1484
- if (config?.services) {
1485
- for (const [token, value] of Object.entries(config.services)) {
1486
- e.registerService(token, value);
1487
- }
1488
- }
1489
- if (config?.modules) {
1490
- e.registerModules(config.modules);
1491
- }
1492
- if (config?.processors) {
1493
- e.registerProcessors(config.processors);
1494
- }
571
+ const e = (0, import_cli3.createCliEngine)(containerRef.value, { ...config, framework: "Vue" });
1495
572
  await e.start();
1496
573
  engine.value = e;
1497
574
  });
@@ -1560,7 +637,7 @@ function useCli() {
1560
637
  }
1561
638
 
1562
639
  // src/index.ts
1563
- var import_cli3 = require("@qodalis/cli");
640
+ var import_cli4 = require("@qodalis/cli");
1564
641
  // Annotate the CommonJS export names for ESM import in node:
1565
642
  0 && (module.exports = {
1566
643
  Cli,