@qodalis/vue-cli 2.0.0-beta.3 → 2.0.0-beta.4

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 ADDED
@@ -0,0 +1,1136 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ Cli: () => Cli,
24
+ CliConfigKey: () => CliConfigKey,
25
+ CliConfigProvider: () => CliConfigProvider,
26
+ CliEngine: () => import_cli3.CliEngine,
27
+ CliInjectionKey: () => CliInjectionKey,
28
+ CliPanel: () => CliPanel,
29
+ CliProvider: () => CliProvider,
30
+ useCli: () => useCli,
31
+ useCliEngine: () => useCliEngine
32
+ });
33
+ module.exports = __toCommonJS(index_exports);
34
+
35
+ // src/Cli.ts
36
+ var import_vue2 = require("vue");
37
+ var import_cli = require("@qodalis/cli");
38
+
39
+ // src/cliInjection.ts
40
+ var CliInjectionKey = /* @__PURE__ */ Symbol("qodalis-cli");
41
+
42
+ // src/CliConfigProvider.ts
43
+ var import_vue = require("vue");
44
+ var CliConfigKey = /* @__PURE__ */ Symbol("qodalis-cli-config");
45
+ var CliConfigProvider = (0, import_vue.defineComponent)({
46
+ name: "CliConfigProvider",
47
+ props: {
48
+ modules: {
49
+ type: Array,
50
+ default: void 0
51
+ },
52
+ processors: {
53
+ type: Array,
54
+ default: void 0
55
+ },
56
+ options: {
57
+ type: Object,
58
+ default: void 0
59
+ },
60
+ services: {
61
+ type: Object,
62
+ default: void 0
63
+ }
64
+ },
65
+ setup(props, { slots }) {
66
+ (0, import_vue.provide)(CliConfigKey, {
67
+ modules: props.modules,
68
+ processors: props.processors,
69
+ options: props.options,
70
+ services: props.services
71
+ });
72
+ return () => slots.default?.();
73
+ }
74
+ });
75
+
76
+ // src/Cli.ts
77
+ var Cli = (0, import_vue2.defineComponent)({
78
+ name: "Cli",
79
+ props: {
80
+ modules: {
81
+ type: Array,
82
+ default: void 0
83
+ },
84
+ processors: {
85
+ type: Array,
86
+ default: void 0
87
+ },
88
+ options: {
89
+ type: Object,
90
+ default: void 0
91
+ },
92
+ services: {
93
+ type: Object,
94
+ default: void 0
95
+ },
96
+ snapshot: {
97
+ type: Object,
98
+ default: void 0
99
+ },
100
+ style: {
101
+ type: Object,
102
+ default: void 0
103
+ },
104
+ class: {
105
+ type: String,
106
+ default: void 0
107
+ }
108
+ },
109
+ emits: ["ready"],
110
+ setup(props, { emit }) {
111
+ const containerRef = (0, import_vue2.ref)(null);
112
+ const ctx = (0, import_vue2.inject)(CliInjectionKey, null);
113
+ const config = (0, import_vue2.inject)(CliConfigKey, null);
114
+ let engine = null;
115
+ if (ctx) {
116
+ return () => null;
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;
122
+ (0, import_vue2.onMounted)(async () => {
123
+ 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
+ }
138
+ await engine.start();
139
+ emit("ready", engine);
140
+ });
141
+ (0, import_vue2.onBeforeUnmount)(() => {
142
+ engine?.destroy();
143
+ engine = null;
144
+ });
145
+ return () => (0, import_vue2.h)("div", {
146
+ ref: containerRef,
147
+ style: { height: "100%", ...props.style },
148
+ class: props.class
149
+ });
150
+ }
151
+ });
152
+
153
+ // src/CliPanel.ts
154
+ 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
+ ]);
257
+ }
258
+ var CliPanel = (0, import_vue3.defineComponent)({
259
+ name: "CliPanel",
260
+ props: {
261
+ options: {
262
+ type: Object,
263
+ default: void 0
264
+ },
265
+ 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
+ },
278
+ style: {
279
+ type: Object,
280
+ default: void 0
281
+ },
282
+ class: { type: String, default: void 0 }
283
+ },
284
+ setup(props) {
285
+ const config = (0, import_vue3.inject)(CliConfigKey, null);
286
+ const mergedOptions = (0, import_vue3.computed)(() => props.options ?? config?.options);
287
+ const mergedModules = (0, import_vue3.computed)(() => props.modules ?? config?.modules);
288
+ const mergedProcessors = (0, import_vue3.computed)(() => props.processors ?? config?.processors);
289
+ const mergedServices = (0, import_vue3.computed)(() => props.services ?? config?.services);
290
+ const visible = (0, import_vue3.ref)(true);
291
+ const collapsed = (0, import_vue3.ref)(mergedOptions.value?.isCollapsed ?? true);
292
+ const maximized = (0, import_vue3.ref)(false);
293
+ const panelHeight = (0, import_vue3.ref)(600);
294
+ const panelWidth = (0, import_vue3.ref)(400);
295
+ const prevHeight = (0, import_vue3.ref)(600);
296
+ const prevWidth = (0, import_vue3.ref)(400);
297
+ const initialized = (0, import_vue3.ref)(false);
298
+ const hidden = (0, import_vue3.ref)(props.options?.isHidden ?? false);
299
+ let preHideCollapsed = true;
300
+ const position = (0, import_vue3.ref)((0, import_cli_core.loadPanelPosition)() ?? mergedOptions.value?.position ?? "bottom");
301
+ const closable = (0, import_vue3.computed)(() => mergedOptions.value?.closable ?? true);
302
+ const resizable = (0, import_vue3.computed)(() => mergedOptions.value?.resizable ?? true);
303
+ const isHorizontal = (0, import_vue3.computed)(() => position.value === "left" || position.value === "right");
304
+ const hideable = (0, import_vue3.computed)(() => mergedOptions.value?.hideable ?? true);
305
+ const hideAlignment = (0, import_vue3.computed)(() => mergedOptions.value?.hideAlignment ?? "center");
306
+ const syncTheme = (0, import_vue3.computed)(() => mergedOptions.value?.syncTheme ?? false);
307
+ const themeStyles = (0, import_vue3.ref)({});
308
+ const tabs = (0, import_vue3.ref)([]);
309
+ const activeTabId = (0, import_vue3.ref)(0);
310
+ const activePaneId = (0, import_vue3.ref)(0);
311
+ let nextTabId = 1;
312
+ let nextPaneId = 1;
313
+ const contextMenu = (0, import_vue3.ref)({
314
+ visible: false,
315
+ x: 0,
316
+ y: 0,
317
+ tabId: 0
318
+ });
319
+ const engineMap = /* @__PURE__ */ new Map();
320
+ const paneResizing = (0, import_vue3.ref)(false);
321
+ let paneResizeState = {
322
+ tabId: 0,
323
+ dividerIndex: 0,
324
+ startX: 0,
325
+ startWidths: [],
326
+ containerWidth: 0
327
+ };
328
+ const panelResizing = (0, import_vue3.ref)(false);
329
+ let panelResizeState = { startY: 0, startHeight: 0, startX: 0, startWidth: 0 };
330
+ function addTab() {
331
+ const paneId = nextPaneId++;
332
+ const tabId = nextTabId++;
333
+ const pane = { id: paneId, widthPercent: 100 };
334
+ const tab = {
335
+ id: tabId,
336
+ title: `Terminal ${tabId}`,
337
+ isEditing: false,
338
+ panes: [pane]
339
+ };
340
+ tabs.value.push(tab);
341
+ activeTabId.value = tabId;
342
+ activePaneId.value = paneId;
343
+ }
344
+ let themeObserver;
345
+ function syncThemeFromEngine() {
346
+ const engine = engineMap.values().next().value;
347
+ if (!engine) return;
348
+ const theme = engine.getTerminal().options.theme;
349
+ if (theme) themeStyles.value = (0, import_cli_core.derivePanelThemeStyles)(theme);
350
+ }
351
+ function setupThemeSync() {
352
+ if (!syncTheme.value) return;
353
+ setTimeout(() => {
354
+ syncThemeFromEngine();
355
+ const container = document.querySelector(".cli-panel-wrapper .terminal-container");
356
+ if (!container) return;
357
+ themeObserver?.disconnect();
358
+ themeObserver = new MutationObserver(syncThemeFromEngine);
359
+ themeObserver.observe(container, { attributes: true, attributeFilter: ["style"] });
360
+ }, 200);
361
+ }
362
+ (0, import_vue3.onMounted)(() => {
363
+ document.addEventListener("click", closePositionDropdown);
364
+ });
365
+ (0, import_vue3.onBeforeUnmount)(() => {
366
+ themeObserver?.disconnect();
367
+ document.removeEventListener("click", closePositionDropdown);
368
+ });
369
+ function toggle() {
370
+ collapsed.value = !collapsed.value;
371
+ if (!collapsed.value && !initialized.value) {
372
+ initialized.value = true;
373
+ addTab();
374
+ setupThemeSync();
375
+ }
376
+ }
377
+ function closeTab(id) {
378
+ const idx = tabs.value.findIndex((t) => t.id === id);
379
+ if (idx === -1) return;
380
+ tabs.value.splice(idx, 1);
381
+ if (tabs.value.length === 0) {
382
+ initialized.value = false;
383
+ collapsed.value = true;
384
+ return;
385
+ }
386
+ if (activeTabId.value === id) {
387
+ const newIdx = Math.min(idx, tabs.value.length - 1);
388
+ selectTab(tabs.value[newIdx].id);
389
+ }
390
+ }
391
+ function selectTab(id) {
392
+ activeTabId.value = id;
393
+ const tab = tabs.value.find((t) => t.id === id);
394
+ if (tab && tab.panes.length > 0) {
395
+ activePaneId.value = tab.panes[0].id;
396
+ }
397
+ }
398
+ function splitRight(tabId) {
399
+ const tab = tabs.value.find(
400
+ (t) => t.id === (tabId ?? activeTabId.value)
401
+ );
402
+ if (!tab) return;
403
+ const idx = tab.panes.findIndex((p) => p.id === activePaneId.value);
404
+ const insertIdx = idx === -1 ? tab.panes.length : idx + 1;
405
+ const newPaneId = nextPaneId++;
406
+ tab.panes.splice(insertIdx, 0, { id: newPaneId, widthPercent: 0 });
407
+ const evenWidth = 100 / tab.panes.length;
408
+ tab.panes.forEach((p) => {
409
+ p.widthPercent = evenWidth;
410
+ });
411
+ normalizePanes(tab.panes);
412
+ activePaneId.value = newPaneId;
413
+ }
414
+ function closePane(tabId, paneId) {
415
+ const tab = tabs.value.find((t) => t.id === tabId);
416
+ if (!tab) return;
417
+ if (tab.panes.length <= 1) {
418
+ closeTab(tabId);
419
+ return;
420
+ }
421
+ const idx = tab.panes.findIndex((p) => p.id === paneId);
422
+ if (idx === -1) return;
423
+ tab.panes.splice(idx, 1);
424
+ const total = tab.panes.reduce((s, p) => s + p.widthPercent, 0);
425
+ if (total > 0)
426
+ tab.panes.forEach((p) => {
427
+ p.widthPercent = p.widthPercent / total * 100;
428
+ });
429
+ normalizePanes(tab.panes);
430
+ if (activePaneId.value === paneId) {
431
+ activePaneId.value = tab.panes[Math.min(idx, tab.panes.length - 1)].id;
432
+ }
433
+ }
434
+ function startRename(tabId) {
435
+ tabs.value.forEach((t) => {
436
+ t.isEditing = t.id === tabId;
437
+ });
438
+ }
439
+ function commitRename(tabId, value) {
440
+ const trimmed = value.trim();
441
+ const tab = tabs.value.find((t) => t.id === tabId);
442
+ if (tab) {
443
+ if (trimmed) tab.title = trimmed;
444
+ tab.isEditing = false;
445
+ }
446
+ }
447
+ function closeContextMenu() {
448
+ contextMenu.value = { ...contextMenu.value, visible: false };
449
+ }
450
+ function onDocClick() {
451
+ if (contextMenu.value.visible) closeContextMenu();
452
+ }
453
+ (0, import_vue3.onMounted)(() => document.addEventListener("click", onDocClick));
454
+ (0, import_vue3.onBeforeUnmount)(
455
+ () => document.removeEventListener("click", onDocClick)
456
+ );
457
+ function onPanelResizeStart(e) {
458
+ if (!resizable.value) return;
459
+ e.preventDefault();
460
+ if (collapsed.value) toggle();
461
+ panelResizing.value = true;
462
+ panelResizeState = {
463
+ startY: e.clientY,
464
+ startHeight: panelHeight.value,
465
+ startX: e.clientX,
466
+ startWidth: panelWidth.value
467
+ };
468
+ }
469
+ function onPanelResizeMove(e) {
470
+ if (!panelResizing.value) return;
471
+ if (isHorizontal.value) {
472
+ const deltaX = position.value === "left" ? e.clientX - panelResizeState.startX : panelResizeState.startX - e.clientX;
473
+ let next = Math.max(100, panelResizeState.startWidth + deltaX);
474
+ if (next > window.innerWidth) next = window.innerWidth;
475
+ panelWidth.value = next;
476
+ } else {
477
+ const deltaY = position.value === "top" ? e.clientY - panelResizeState.startY : panelResizeState.startY - e.clientY;
478
+ let next = Math.max(100, panelResizeState.startHeight + deltaY);
479
+ if (next > window.innerHeight) next = window.innerHeight;
480
+ panelHeight.value = next;
481
+ }
482
+ }
483
+ function onPanelResizeEnd() {
484
+ panelResizing.value = false;
485
+ }
486
+ (0, import_vue3.onMounted)(() => {
487
+ document.addEventListener("mousemove", onPanelResizeMove);
488
+ document.addEventListener("mouseup", onPanelResizeEnd);
489
+ });
490
+ (0, import_vue3.onBeforeUnmount)(() => {
491
+ document.removeEventListener("mousemove", onPanelResizeMove);
492
+ document.removeEventListener("mouseup", onPanelResizeEnd);
493
+ });
494
+ function onPaneResizeStart(e, tabId, dividerIndex) {
495
+ e.preventDefault();
496
+ const tab = tabs.value.find((t) => t.id === tabId);
497
+ if (!tab) return;
498
+ paneResizing.value = true;
499
+ const container = e.target.closest(
500
+ ".cli-panel-panes-container"
501
+ );
502
+ paneResizeState = {
503
+ tabId,
504
+ dividerIndex,
505
+ startX: e.clientX,
506
+ startWidths: tab.panes.map((p) => p.widthPercent),
507
+ containerWidth: container ? container.clientWidth : 1
508
+ };
509
+ document.body.classList.add("cli-pane-resizing");
510
+ }
511
+ function onPaneResizeMove(e) {
512
+ if (!paneResizing.value) return;
513
+ const s = paneResizeState;
514
+ const deltaPct = (e.clientX - s.startX) / s.containerWidth * 100;
515
+ const i = s.dividerIndex;
516
+ let leftW = s.startWidths[i] + deltaPct;
517
+ let rightW = s.startWidths[i + 1] - deltaPct;
518
+ if (leftW < MIN_PANE_WIDTH_PERCENT) {
519
+ leftW = MIN_PANE_WIDTH_PERCENT;
520
+ rightW = s.startWidths[i] + s.startWidths[i + 1] - MIN_PANE_WIDTH_PERCENT;
521
+ }
522
+ if (rightW < MIN_PANE_WIDTH_PERCENT) {
523
+ rightW = MIN_PANE_WIDTH_PERCENT;
524
+ leftW = s.startWidths[i] + s.startWidths[i + 1] - MIN_PANE_WIDTH_PERCENT;
525
+ }
526
+ const tab = tabs.value.find((t) => t.id === s.tabId);
527
+ if (tab) {
528
+ tab.panes[i].widthPercent = leftW;
529
+ tab.panes[i + 1].widthPercent = rightW;
530
+ }
531
+ }
532
+ function onPaneResizeEnd() {
533
+ if (!paneResizing.value) return;
534
+ paneResizing.value = false;
535
+ document.body.classList.remove("cli-pane-resizing");
536
+ }
537
+ (0, import_vue3.onMounted)(() => {
538
+ document.addEventListener("mousemove", onPaneResizeMove);
539
+ document.addEventListener("mouseup", onPaneResizeEnd);
540
+ });
541
+ (0, import_vue3.onBeforeUnmount)(() => {
542
+ document.removeEventListener("mousemove", onPaneResizeMove);
543
+ document.removeEventListener("mouseup", onPaneResizeEnd);
544
+ });
545
+ function toggleMaximize() {
546
+ if (!maximized.value) {
547
+ if (isHorizontal.value) {
548
+ prevWidth.value = panelWidth.value;
549
+ panelWidth.value = window.innerWidth;
550
+ } else {
551
+ prevHeight.value = panelHeight.value;
552
+ panelHeight.value = window.innerHeight;
553
+ }
554
+ } else {
555
+ if (isHorizontal.value) {
556
+ panelWidth.value = prevWidth.value;
557
+ } else {
558
+ panelHeight.value = prevHeight.value;
559
+ }
560
+ }
561
+ maximized.value = !maximized.value;
562
+ }
563
+ function handleClose() {
564
+ visible.value = false;
565
+ props.onClose?.();
566
+ }
567
+ function handleHide() {
568
+ preHideCollapsed = collapsed.value;
569
+ hidden.value = true;
570
+ }
571
+ function handleUnhide() {
572
+ hidden.value = false;
573
+ collapsed.value = preHideCollapsed;
574
+ }
575
+ const positionDropdownOpen = (0, import_vue3.ref)(false);
576
+ function closePositionDropdown() {
577
+ positionDropdownOpen.value = false;
578
+ }
579
+ function togglePositionDropdown(e) {
580
+ e.stopPropagation();
581
+ positionDropdownOpen.value = !positionDropdownOpen.value;
582
+ }
583
+ function selectPosition(pos) {
584
+ positionDropdownOpen.value = false;
585
+ position.value = pos;
586
+ (0, import_cli_core.savePanelPosition)(pos);
587
+ }
588
+ return () => {
589
+ if (!visible.value) return null;
590
+ const wrapperClass = [
591
+ "cli-panel-wrapper",
592
+ collapsed.value && "collapsed",
593
+ maximized.value && "maximized",
594
+ panelResizing.value && "resizing",
595
+ props.class
596
+ ].filter(Boolean).join(" ");
597
+ const wrapperStyle = {
598
+ ...isHorizontal.value ? { width: `${panelWidth.value}px` } : { height: `${panelHeight.value}px` },
599
+ ...hidden.value ? { display: "none" } : {},
600
+ ...themeStyles.value,
601
+ ...props.style
602
+ };
603
+ const headerEl = (0, import_vue3.h)("div", { class: "cli-panel-header" }, [
604
+ (0, import_vue3.h)(
605
+ "div",
606
+ {
607
+ class: "cli-panel-resize-bar",
608
+ onMousedown: onPanelResizeStart
609
+ },
610
+ [(0, import_vue3.h)("div", { class: "cli-panel-resize-grip" })]
611
+ ),
612
+ (0, import_vue3.h)("div", { class: "cli-panel-header-content" }, [
613
+ (0, import_vue3.h)("p", { class: "cli-panel-title" }, [
614
+ (0, import_vue3.h)("span", { class: "cli-panel-title-icon" }, [
615
+ terminalIcon()
616
+ ]),
617
+ "CLI"
618
+ ]),
619
+ (0, import_vue3.h)("div", { class: "cli-panel-action-buttons" }, [
620
+ (0, import_vue3.h)("div", {
621
+ class: "cli-panel-btn-position-wrapper",
622
+ onClick: (e) => e.stopPropagation()
623
+ }, [
624
+ (0, import_vue3.h)("button", {
625
+ class: "cli-panel-btn cli-panel-btn-position",
626
+ title: "Move panel",
627
+ onClick: togglePositionDropdown
628
+ }, [positionIcon(position.value)]),
629
+ positionDropdownOpen.value ? (0, import_vue3.h)(
630
+ "div",
631
+ { class: "cli-panel-position-dropdown" },
632
+ ["bottom", "top", "left", "right"].map(
633
+ (pos) => (0, import_vue3.h)("button", {
634
+ class: ["cli-panel-position-dropdown-item", position.value === pos ? "active" : ""].filter(Boolean).join(" "),
635
+ onClick: () => selectPosition(pos)
636
+ }, [
637
+ positionIcon(pos, 16),
638
+ pos.charAt(0).toUpperCase() + pos.slice(1)
639
+ ])
640
+ )
641
+ ) : null
642
+ ]),
643
+ hideable.value ? (0, import_vue3.h)("button", {
644
+ class: "cli-panel-btn cli-panel-btn-hide",
645
+ title: "Hide",
646
+ onClick: handleHide
647
+ }, [hideIcon()]) : null,
648
+ (0, import_vue3.h)(
649
+ "button",
650
+ {
651
+ class: "cli-panel-btn",
652
+ title: maximized.value ? "Restore" : "Maximize",
653
+ disabled: collapsed.value,
654
+ onClick: toggleMaximize
655
+ },
656
+ [maximized.value ? restoreIcon() : maximizeIcon()]
657
+ ),
658
+ (0, import_vue3.h)(
659
+ "button",
660
+ {
661
+ class: "cli-panel-btn",
662
+ title: collapsed.value ? "Expand" : "Collapse",
663
+ onClick: toggle
664
+ },
665
+ [collapseChevron(position.value, collapsed.value)]
666
+ ),
667
+ closable.value ? (0, import_vue3.h)(
668
+ "button",
669
+ {
670
+ class: "cli-panel-btn cli-panel-btn-close",
671
+ title: "Close",
672
+ onClick: handleClose
673
+ },
674
+ [closeIcon()]
675
+ ) : null
676
+ ])
677
+ ])
678
+ ]);
679
+ const contentEl = initialized.value ? (0, import_vue3.h)("div", { class: "cli-panel-content", style: collapsed.value ? { display: "none" } : void 0 }, [
680
+ // Tab bar
681
+ (0, import_vue3.h)("div", { class: "cli-panel-tabs" }, [
682
+ (0, import_vue3.h)(
683
+ "ul",
684
+ { class: "cli-panel-tab-list" },
685
+ tabs.value.map(
686
+ (tab) => (0, import_vue3.h)(
687
+ "li",
688
+ {
689
+ key: tab.id,
690
+ class: [
691
+ "cli-panel-tab",
692
+ tab.id === activeTabId.value && "active"
693
+ ].filter(Boolean).join(" "),
694
+ onClick: () => selectTab(tab.id),
695
+ onDblclick: () => startRename(tab.id),
696
+ onContextmenu: (e) => {
697
+ e.preventDefault();
698
+ e.stopPropagation();
699
+ contextMenu.value = {
700
+ visible: true,
701
+ x: e.clientX,
702
+ y: e.clientY,
703
+ tabId: tab.id
704
+ };
705
+ }
706
+ },
707
+ tab.isEditing ? [
708
+ (0, import_vue3.h)("input", {
709
+ class: "cli-panel-tab-rename-input",
710
+ type: "text",
711
+ value: tab.title,
712
+ onKeydown: (e) => {
713
+ if (e.key === "Enter")
714
+ commitRename(
715
+ tab.id,
716
+ e.target.value
717
+ );
718
+ else if (e.key === "Escape") {
719
+ tab.isEditing = false;
720
+ }
721
+ },
722
+ onBlur: (e) => commitRename(
723
+ tab.id,
724
+ e.target.value
725
+ ),
726
+ onClick: (e) => e.stopPropagation(),
727
+ onDblclick: (e) => e.stopPropagation()
728
+ })
729
+ ] : [
730
+ (0, import_vue3.h)(
731
+ "span",
732
+ {
733
+ class: "cli-panel-tab-title"
734
+ },
735
+ tab.title
736
+ ),
737
+ (0, import_vue3.h)(
738
+ "button",
739
+ {
740
+ class: "cli-panel-tab-close-btn",
741
+ title: "Close tab",
742
+ onClick: (e) => {
743
+ e.stopPropagation();
744
+ closeTab(
745
+ tab.id
746
+ );
747
+ }
748
+ },
749
+ "\xD7"
750
+ )
751
+ ]
752
+ )
753
+ )
754
+ ),
755
+ (0, import_vue3.h)(
756
+ "button",
757
+ {
758
+ class: "cli-panel-add-tab",
759
+ title: "New terminal",
760
+ onClick: addTab
761
+ },
762
+ "+"
763
+ )
764
+ ]),
765
+ // Terminal instances
766
+ (0, import_vue3.h)(
767
+ "div",
768
+ { class: "cli-panel-instances" },
769
+ tabs.value.map(
770
+ (tab) => (0, import_vue3.h)(
771
+ "div",
772
+ {
773
+ key: tab.id,
774
+ class: "cli-panel-instance",
775
+ style: {
776
+ display: tab.id === activeTabId.value ? void 0 : "none"
777
+ }
778
+ },
779
+ [
780
+ (0, import_vue3.h)(
781
+ "div",
782
+ {
783
+ class: [
784
+ "cli-panel-panes-container",
785
+ paneResizing.value && "resizing"
786
+ ].filter(Boolean).join(" ")
787
+ },
788
+ tab.panes.flatMap((pane, i) => {
789
+ const nodes = [];
790
+ if (i > 0) {
791
+ nodes.push(
792
+ (0, import_vue3.h)(
793
+ "div",
794
+ {
795
+ class: "cli-panel-pane-divider",
796
+ onMousedown: (e) => onPaneResizeStart(
797
+ e,
798
+ tab.id,
799
+ i - 1
800
+ )
801
+ },
802
+ [
803
+ (0, import_vue3.h)("div", {
804
+ class: "cli-panel-pane-divider-grip"
805
+ })
806
+ ]
807
+ )
808
+ );
809
+ }
810
+ nodes.push(
811
+ (0, import_vue3.h)(
812
+ "div",
813
+ {
814
+ class: [
815
+ "cli-panel-pane",
816
+ pane.id === activePaneId.value && tab.id === activeTabId.value && "active-pane"
817
+ ].filter(
818
+ Boolean
819
+ ).join(" "),
820
+ style: {
821
+ flex: `${pane.widthPercent} 1 0`
822
+ },
823
+ onClick: () => {
824
+ activePaneId.value = pane.id;
825
+ activeTabId.value = tab.id;
826
+ }
827
+ },
828
+ [
829
+ tab.panes.length > 1 ? (0, import_vue3.h)(
830
+ "button",
831
+ {
832
+ class: "cli-panel-pane-close-btn",
833
+ title: "Close pane",
834
+ onClick: (e) => {
835
+ e.stopPropagation();
836
+ closePane(
837
+ tab.id,
838
+ pane.id
839
+ );
840
+ }
841
+ },
842
+ "\xD7"
843
+ ) : null,
844
+ (0, import_vue3.h)(Cli, {
845
+ options: mergedOptions.value,
846
+ modules: mergedModules.value,
847
+ processors: mergedProcessors.value,
848
+ services: mergedServices.value,
849
+ snapshot: pane.snapshot,
850
+ style: {
851
+ height: "100%"
852
+ },
853
+ onReady: (engine) => {
854
+ engineMap.set(pane.id, engine);
855
+ }
856
+ })
857
+ ]
858
+ )
859
+ );
860
+ return nodes;
861
+ })
862
+ )
863
+ ]
864
+ )
865
+ )
866
+ )
867
+ ]) : null;
868
+ const contextMenuEl = contextMenu.value.visible ? (0, import_vue3.h)(
869
+ "div",
870
+ {
871
+ class: "cli-panel-context-menu",
872
+ style: {
873
+ left: `${contextMenu.value.x}px`,
874
+ top: `${contextMenu.value.y}px`
875
+ },
876
+ onClick: (e) => e.stopPropagation()
877
+ },
878
+ [
879
+ (0, import_vue3.h)(
880
+ "button",
881
+ {
882
+ class: "cli-panel-context-menu-item",
883
+ onClick: () => {
884
+ closeContextMenu();
885
+ startRename(contextMenu.value.tabId);
886
+ }
887
+ },
888
+ "Rename"
889
+ ),
890
+ (0, import_vue3.h)(
891
+ "button",
892
+ {
893
+ class: "cli-panel-context-menu-item",
894
+ onClick: () => {
895
+ const tab = tabs.value.find(
896
+ (t) => t.id === contextMenu.value.tabId
897
+ );
898
+ closeContextMenu();
899
+ if (!tab) return;
900
+ const sourceEngine = engineMap.get(tab.panes[0]?.id);
901
+ const snapshot = sourceEngine?.snapshot();
902
+ const paneId = nextPaneId++;
903
+ const tabId = nextTabId++;
904
+ const idx = tabs.value.indexOf(tab);
905
+ tabs.value.splice(idx + 1, 0, {
906
+ id: tabId,
907
+ title: `${tab.title} (copy)`,
908
+ isEditing: false,
909
+ panes: [
910
+ { id: paneId, widthPercent: 100, snapshot }
911
+ ]
912
+ });
913
+ activeTabId.value = tabId;
914
+ activePaneId.value = paneId;
915
+ }
916
+ },
917
+ "Duplicate"
918
+ ),
919
+ (0, import_vue3.h)(
920
+ "button",
921
+ {
922
+ class: "cli-panel-context-menu-item",
923
+ onClick: () => {
924
+ closeContextMenu();
925
+ splitRight(contextMenu.value.tabId);
926
+ }
927
+ },
928
+ "Split Right"
929
+ ),
930
+ (0, import_vue3.h)("div", {
931
+ class: "cli-panel-context-menu-separator"
932
+ }),
933
+ (0, import_vue3.h)(
934
+ "button",
935
+ {
936
+ class: "cli-panel-context-menu-item",
937
+ onClick: () => {
938
+ closeContextMenu();
939
+ closeTab(contextMenu.value.tabId);
940
+ }
941
+ },
942
+ "Close"
943
+ ),
944
+ (0, import_vue3.h)(
945
+ "button",
946
+ {
947
+ class: "cli-panel-context-menu-item",
948
+ disabled: tabs.value.length <= 1,
949
+ onClick: () => {
950
+ const id = contextMenu.value.tabId;
951
+ closeContextMenu();
952
+ tabs.value = tabs.value.filter(
953
+ (t) => t.id === id
954
+ );
955
+ activeTabId.value = id;
956
+ }
957
+ },
958
+ "Close Others"
959
+ ),
960
+ (0, import_vue3.h)(
961
+ "button",
962
+ {
963
+ class: "cli-panel-context-menu-item",
964
+ onClick: () => {
965
+ const id = contextMenu.value.tabId;
966
+ closeContextMenu();
967
+ const idx = tabs.value.findIndex(
968
+ (t) => t.id === id
969
+ );
970
+ if (idx !== -1)
971
+ tabs.value = tabs.value.slice(
972
+ 0,
973
+ idx + 1
974
+ );
975
+ }
976
+ },
977
+ "Close to the Right"
978
+ ),
979
+ (0, import_vue3.h)("div", {
980
+ class: "cli-panel-context-menu-separator"
981
+ }),
982
+ (0, import_vue3.h)(
983
+ "button",
984
+ {
985
+ class: "cli-panel-context-menu-item destructive",
986
+ onClick: () => {
987
+ closeContextMenu();
988
+ tabs.value = [];
989
+ initialized.value = false;
990
+ collapsed.value = true;
991
+ }
992
+ },
993
+ "Close All"
994
+ )
995
+ ]
996
+ ) : null;
997
+ const hideTabEl = hidden.value ? (0, import_vue3.h)("button", {
998
+ class: "cli-panel-hide-tab",
999
+ "data-position": position.value,
1000
+ "data-hide-align": hideAlignment.value,
1001
+ style: themeStyles.value,
1002
+ title: "Show CLI",
1003
+ onClick: handleUnhide
1004
+ }, [
1005
+ (0, import_vue3.h)("svg", { class: "cli-panel-hide-tab-icon", ...svgAttrs, width: "16", height: "16", "stroke-width": "2" }, [
1006
+ (0, import_vue3.h)("polyline", { points: "4 17 10 11 4 5" }),
1007
+ (0, import_vue3.h)("line", { x1: "12", y1: "19", x2: "20", y2: "19" })
1008
+ ]),
1009
+ (0, import_vue3.h)("span", { class: "cli-panel-hide-tab-label" }, "CLI"),
1010
+ hideTabChevron(position.value)
1011
+ ]) : null;
1012
+ return (0, import_vue3.h)("div", {}, [
1013
+ hideTabEl,
1014
+ (0, import_vue3.h)(
1015
+ "div",
1016
+ {
1017
+ class: wrapperClass,
1018
+ style: wrapperStyle,
1019
+ "data-position": position.value,
1020
+ "data-resizable": String(resizable.value),
1021
+ "data-closable": String(closable.value),
1022
+ "data-hideable": String(hideable.value)
1023
+ },
1024
+ [headerEl, contentEl]
1025
+ ),
1026
+ contextMenuEl
1027
+ ]);
1028
+ };
1029
+ }
1030
+ });
1031
+
1032
+ // src/CliProvider.ts
1033
+ var import_vue5 = require("vue");
1034
+
1035
+ // src/useCliEngine.ts
1036
+ var import_vue4 = require("vue");
1037
+ var import_cli2 = require("@qodalis/cli");
1038
+ function useCliEngine(containerRef, config) {
1039
+ const engine = (0, import_vue4.shallowRef)(null);
1040
+ (0, import_vue4.onMounted)(async () => {
1041
+ if (!containerRef.value) return;
1042
+ const e = new import_cli2.CliEngine(containerRef.value, config?.options);
1043
+ e.registerService("cli-framework", "Vue");
1044
+ if (config?.services) {
1045
+ for (const [token, value] of Object.entries(config.services)) {
1046
+ e.registerService(token, value);
1047
+ }
1048
+ }
1049
+ if (config?.modules) {
1050
+ e.registerModules(config.modules);
1051
+ }
1052
+ if (config?.processors) {
1053
+ e.registerProcessors(config.processors);
1054
+ }
1055
+ await e.start();
1056
+ engine.value = e;
1057
+ });
1058
+ (0, import_vue4.onBeforeUnmount)(() => {
1059
+ engine.value?.destroy();
1060
+ engine.value = null;
1061
+ });
1062
+ return engine;
1063
+ }
1064
+
1065
+ // src/CliProvider.ts
1066
+ var CliProvider = (0, import_vue5.defineComponent)({
1067
+ name: "CliProvider",
1068
+ props: {
1069
+ modules: {
1070
+ type: Array,
1071
+ default: void 0
1072
+ },
1073
+ processors: {
1074
+ type: Array,
1075
+ default: void 0
1076
+ },
1077
+ options: {
1078
+ type: Object,
1079
+ default: void 0
1080
+ },
1081
+ services: {
1082
+ type: Object,
1083
+ default: void 0
1084
+ },
1085
+ style: {
1086
+ type: Object,
1087
+ default: void 0
1088
+ }
1089
+ },
1090
+ emits: ["ready"],
1091
+ setup(props, { slots, emit }) {
1092
+ const containerRef = (0, import_vue5.ref)(null);
1093
+ const engine = useCliEngine(containerRef, {
1094
+ modules: props.modules,
1095
+ processors: props.processors,
1096
+ options: props.options,
1097
+ services: props.services
1098
+ });
1099
+ (0, import_vue5.provide)(CliInjectionKey, { engine });
1100
+ return () => (0, import_vue5.h)("div", { style: { height: "100%" } }, [
1101
+ (0, import_vue5.h)("div", {
1102
+ ref: containerRef,
1103
+ style: { height: "100%", ...props.style }
1104
+ }),
1105
+ slots.default?.()
1106
+ ]);
1107
+ }
1108
+ });
1109
+
1110
+ // src/useCli.ts
1111
+ var import_vue6 = require("vue");
1112
+ function useCli() {
1113
+ const ctx = (0, import_vue6.inject)(CliInjectionKey);
1114
+ if (!ctx) {
1115
+ throw new Error(
1116
+ "useCli() must be used inside a <CliProvider> component"
1117
+ );
1118
+ }
1119
+ return ctx;
1120
+ }
1121
+
1122
+ // src/index.ts
1123
+ var import_cli3 = require("@qodalis/cli");
1124
+ // Annotate the CommonJS export names for ESM import in node:
1125
+ 0 && (module.exports = {
1126
+ Cli,
1127
+ CliConfigKey,
1128
+ CliConfigProvider,
1129
+ CliEngine,
1130
+ CliInjectionKey,
1131
+ CliPanel,
1132
+ CliProvider,
1133
+ useCli,
1134
+ useCliEngine
1135
+ });
1136
+ //# sourceMappingURL=index.js.map