@qodalis/vue-cli 2.0.0-beta.1 → 2.0.0-beta.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs ADDED
@@ -0,0 +1,1264 @@
1
+ // src/Cli.ts
2
+ import {
3
+ defineComponent as defineComponent2,
4
+ ref,
5
+ onMounted,
6
+ onBeforeUnmount,
7
+ h,
8
+ inject
9
+ } from "vue";
10
+ import { CliEngine } from "@qodalis/cli";
11
+
12
+ // src/cliInjection.ts
13
+ var CliInjectionKey = /* @__PURE__ */ Symbol("qodalis-cli");
14
+
15
+ // src/CliConfigProvider.ts
16
+ import { defineComponent, provide } from "vue";
17
+ var CliConfigKey = /* @__PURE__ */ Symbol("qodalis-cli-config");
18
+ var CliConfigProvider = defineComponent({
19
+ name: "CliConfigProvider",
20
+ props: {
21
+ modules: {
22
+ type: Array,
23
+ default: void 0
24
+ },
25
+ processors: {
26
+ type: Array,
27
+ default: void 0
28
+ },
29
+ options: {
30
+ type: Object,
31
+ default: void 0
32
+ },
33
+ services: {
34
+ type: Object,
35
+ default: void 0
36
+ }
37
+ },
38
+ setup(props, { slots }) {
39
+ provide(CliConfigKey, {
40
+ modules: props.modules,
41
+ processors: props.processors,
42
+ options: props.options,
43
+ services: props.services
44
+ });
45
+ return () => slots.default?.();
46
+ }
47
+ });
48
+
49
+ // src/Cli.ts
50
+ var Cli = defineComponent2({
51
+ name: "Cli",
52
+ props: {
53
+ modules: {
54
+ type: Array,
55
+ default: void 0
56
+ },
57
+ processors: {
58
+ type: Array,
59
+ default: void 0
60
+ },
61
+ options: {
62
+ type: Object,
63
+ default: void 0
64
+ },
65
+ services: {
66
+ type: Object,
67
+ default: void 0
68
+ },
69
+ snapshot: {
70
+ type: Object,
71
+ default: void 0
72
+ },
73
+ style: {
74
+ type: Object,
75
+ default: void 0
76
+ },
77
+ class: {
78
+ type: String,
79
+ default: void 0
80
+ }
81
+ },
82
+ emits: ["ready"],
83
+ setup(props, { emit }) {
84
+ const containerRef = ref(null);
85
+ const ctx = inject(CliInjectionKey, null);
86
+ const config = inject(CliConfigKey, null);
87
+ let engine = null;
88
+ if (ctx) {
89
+ return () => null;
90
+ }
91
+ const modules = props.modules ?? config?.modules;
92
+ const processors = props.processors ?? config?.processors;
93
+ const options = props.options ?? config?.options;
94
+ const services = props.services ?? config?.services;
95
+ onMounted(async () => {
96
+ if (!containerRef.value) return;
97
+ const engineOptions = props.snapshot ? { ...options, snapshot: props.snapshot } : options;
98
+ engine = new CliEngine(containerRef.value, engineOptions);
99
+ engine.registerService("cli-framework", "Vue");
100
+ if (services) {
101
+ for (const [token, value] of Object.entries(services)) {
102
+ engine.registerService(token, value);
103
+ }
104
+ }
105
+ if (modules) {
106
+ engine.registerModules(modules);
107
+ }
108
+ if (processors) {
109
+ engine.registerProcessors(processors);
110
+ }
111
+ await engine.start();
112
+ emit("ready", engine);
113
+ });
114
+ onBeforeUnmount(() => {
115
+ engine?.destroy();
116
+ engine = null;
117
+ });
118
+ return () => h("div", {
119
+ ref: containerRef,
120
+ style: { height: "100%", ...props.style },
121
+ class: props.class
122
+ });
123
+ }
124
+ });
125
+
126
+ // src/CliPanel.ts
127
+ import {
128
+ defineComponent as defineComponent3,
129
+ ref as ref2,
130
+ computed,
131
+ inject as inject2,
132
+ h as h2,
133
+ onMounted as onMounted2,
134
+ onBeforeUnmount as onBeforeUnmount2
135
+ } from "vue";
136
+ import { derivePanelThemeStyles, loadPanelPosition, savePanelPosition } from "@qodalis/cli-core";
137
+ var MIN_PANE_WIDTH_PERCENT = 10;
138
+ function normalizePanes(panes) {
139
+ const total = panes.reduce((s, p) => s + p.widthPercent, 0);
140
+ if (total === 0) return;
141
+ const scale = 100 / total;
142
+ panes.forEach((p) => {
143
+ p.widthPercent *= scale;
144
+ });
145
+ }
146
+ var svgAttrs = {
147
+ width: "20",
148
+ height: "20",
149
+ viewBox: "0 0 24 24",
150
+ fill: "none",
151
+ stroke: "currentColor",
152
+ "stroke-width": "1.8",
153
+ "stroke-linecap": "round",
154
+ "stroke-linejoin": "round"
155
+ };
156
+ var terminalIcon = () => h2("svg", { ...svgAttrs, width: "22", height: "22", "stroke-width": "2" }, [
157
+ h2("polyline", { points: "4 17 10 11 4 5" }),
158
+ h2("line", { x1: "12", y1: "19", x2: "20", y2: "19" })
159
+ ]);
160
+ var maximizeIcon = () => h2("svg", svgAttrs, [
161
+ h2("polyline", { points: "15 3 21 3 21 9" }),
162
+ h2("polyline", { points: "9 21 3 21 3 15" }),
163
+ h2("line", { x1: "21", y1: "3", x2: "14", y2: "10" }),
164
+ h2("line", { x1: "3", y1: "21", x2: "10", y2: "14" })
165
+ ]);
166
+ var restoreIcon = () => h2("svg", svgAttrs, [
167
+ h2("polyline", { points: "4 14 10 14 10 20" }),
168
+ h2("polyline", { points: "20 10 14 10 14 4" }),
169
+ h2("line", { x1: "14", y1: "10", x2: "21", y2: "3" }),
170
+ h2("line", { x1: "3", y1: "21", x2: "10", y2: "14" })
171
+ ]);
172
+ var closeIcon = () => h2("svg", svgAttrs, [
173
+ h2("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
174
+ h2("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
175
+ ]);
176
+ var hideIcon = () => h2("svg", svgAttrs, [
177
+ h2("line", { x1: "5", y1: "18", x2: "19", y2: "18" }),
178
+ h2("polyline", { points: "9 14 12 17 15 14" })
179
+ ]);
180
+ function collapseChevron(position, isCollapsed) {
181
+ let points;
182
+ switch (position) {
183
+ case "top":
184
+ points = isCollapsed ? "6 9 12 15 18 9" : "18 15 12 9 6 15";
185
+ break;
186
+ case "left":
187
+ points = isCollapsed ? "9 18 15 12 9 6" : "15 6 9 12 15 18";
188
+ break;
189
+ case "right":
190
+ points = isCollapsed ? "15 6 9 12 15 18" : "9 18 15 12 9 6";
191
+ break;
192
+ default:
193
+ points = isCollapsed ? "18 15 12 9 6 15" : "6 9 12 15 18 9";
194
+ break;
195
+ }
196
+ return h2("svg", svgAttrs, [h2("polyline", { points })]);
197
+ }
198
+ function hideTabChevron(position) {
199
+ let points;
200
+ switch (position) {
201
+ case "top":
202
+ points = "6 9 12 15 18 9";
203
+ break;
204
+ case "left":
205
+ points = "9 18 15 12 9 6";
206
+ break;
207
+ case "right":
208
+ points = "15 6 9 12 15 18";
209
+ break;
210
+ default:
211
+ points = "18 15 12 9 6 15";
212
+ break;
213
+ }
214
+ return h2("svg", { class: "cli-panel-hide-tab-arrow", ...svgAttrs, width: "14", height: "14", "stroke-width": "2.5" }, [
215
+ h2("polyline", { points })
216
+ ]);
217
+ }
218
+ function positionIcon(pos, size = 20) {
219
+ let fillRect;
220
+ switch (pos) {
221
+ case "top":
222
+ fillRect = h2("rect", { x: "4", y: "4", width: "16", height: "5", rx: "1", fill: "currentColor", stroke: "none", opacity: "0.5" });
223
+ break;
224
+ case "left":
225
+ fillRect = h2("rect", { x: "4", y: "4", width: "5", height: "16", rx: "1", fill: "currentColor", stroke: "none", opacity: "0.5" });
226
+ break;
227
+ case "right":
228
+ fillRect = h2("rect", { x: "15", y: "4", width: "5", height: "16", rx: "1", fill: "currentColor", stroke: "none", opacity: "0.5" });
229
+ break;
230
+ default:
231
+ fillRect = h2("rect", { x: "4", y: "15", width: "16", height: "5", rx: "1", fill: "currentColor", stroke: "none", opacity: "0.5" });
232
+ break;
233
+ }
234
+ return h2("svg", { ...svgAttrs, width: String(size), height: String(size) }, [
235
+ h2("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }),
236
+ fillRect
237
+ ]);
238
+ }
239
+ var CliPanel = defineComponent3({
240
+ name: "CliPanel",
241
+ props: {
242
+ options: {
243
+ type: Object,
244
+ default: void 0
245
+ },
246
+ modules: { type: Array, default: void 0 },
247
+ processors: {
248
+ type: Array,
249
+ default: void 0
250
+ },
251
+ services: {
252
+ type: Object,
253
+ default: void 0
254
+ },
255
+ onClose: {
256
+ type: Function,
257
+ default: void 0
258
+ },
259
+ collapsed: { type: Boolean, default: void 0 },
260
+ hidden: { type: Boolean, default: void 0 },
261
+ maximized: { type: Boolean, default: void 0 },
262
+ activeTabId: { type: Number, default: void 0 },
263
+ position: { type: String, default: void 0 },
264
+ height: { type: Number, default: void 0 },
265
+ width: { type: Number, default: void 0 },
266
+ style: {
267
+ type: Object,
268
+ default: void 0
269
+ },
270
+ class: { type: String, default: void 0 }
271
+ },
272
+ emits: [
273
+ "update:collapsed",
274
+ "update:hidden",
275
+ "update:maximized",
276
+ "update:activeTabId",
277
+ "update:position",
278
+ "update:height",
279
+ "update:width",
280
+ "close",
281
+ "tab-added",
282
+ "tab-closed",
283
+ "pane-split",
284
+ "pane-closed"
285
+ ],
286
+ setup(props, { expose, emit }) {
287
+ const config = inject2(CliConfigKey, null);
288
+ const mergedOptions = computed(() => props.options ?? config?.options);
289
+ const mergedModules = computed(() => props.modules ?? config?.modules);
290
+ const mergedProcessors = computed(() => props.processors ?? config?.processors);
291
+ const mergedServices = computed(() => props.services ?? config?.services);
292
+ const visible = ref2(true);
293
+ const internalCollapsed = ref2(mergedOptions.value?.isCollapsed ?? true);
294
+ const internalMaximized = ref2(false);
295
+ const internalHeight = ref2(600);
296
+ const internalWidth = ref2(400);
297
+ const prevHeight = ref2(600);
298
+ const prevWidth = ref2(400);
299
+ const initialized = ref2(false);
300
+ const internalHidden = ref2(props.options?.isHidden ?? false);
301
+ let preHideCollapsed = true;
302
+ const internalActiveTabId = ref2(0);
303
+ const internalPosition = ref2(
304
+ loadPanelPosition() ?? mergedOptions.value?.position ?? "bottom"
305
+ );
306
+ const resolvedCollapsed = computed(() => props.collapsed !== void 0 ? props.collapsed : internalCollapsed.value);
307
+ const resolvedHidden = computed(() => props.hidden !== void 0 ? props.hidden : internalHidden.value);
308
+ const resolvedMaximized = computed(() => props.maximized !== void 0 ? props.maximized : internalMaximized.value);
309
+ const resolvedActiveTabId = computed(() => props.activeTabId !== void 0 ? props.activeTabId : internalActiveTabId.value);
310
+ const resolvedHeight = computed(() => props.height !== void 0 ? props.height : internalHeight.value);
311
+ const resolvedWidth = computed(() => props.width !== void 0 ? props.width : internalWidth.value);
312
+ const resolvedPosition = computed(() => props.position !== void 0 ? props.position : internalPosition.value);
313
+ function updateCollapsed(value) {
314
+ internalCollapsed.value = value;
315
+ emit("update:collapsed", value);
316
+ }
317
+ function updateHidden(value) {
318
+ internalHidden.value = value;
319
+ emit("update:hidden", value);
320
+ }
321
+ function updateMaximized(value) {
322
+ internalMaximized.value = value;
323
+ emit("update:maximized", value);
324
+ }
325
+ function updateActiveTabId(value) {
326
+ internalActiveTabId.value = value;
327
+ emit("update:activeTabId", value);
328
+ }
329
+ function updateHeight(value) {
330
+ internalHeight.value = value;
331
+ emit("update:height", value);
332
+ }
333
+ function updateWidth(value) {
334
+ internalWidth.value = value;
335
+ emit("update:width", value);
336
+ }
337
+ function updatePosition(value) {
338
+ internalPosition.value = value;
339
+ savePanelPosition(value);
340
+ emit("update:position", value);
341
+ }
342
+ const closable = computed(() => mergedOptions.value?.closable ?? true);
343
+ const resizable = computed(() => mergedOptions.value?.resizable ?? true);
344
+ const isHorizontal = computed(() => resolvedPosition.value === "left" || resolvedPosition.value === "right");
345
+ const hideable = computed(() => mergedOptions.value?.hideable ?? true);
346
+ const hideAlignment = computed(() => mergedOptions.value?.hideAlignment ?? "center");
347
+ const syncTheme = computed(() => mergedOptions.value?.syncTheme ?? false);
348
+ const themeStyles = ref2({});
349
+ const tabs = ref2([]);
350
+ const activePaneId = ref2(0);
351
+ let nextTabId = 1;
352
+ let nextPaneId = 1;
353
+ const contextMenu = ref2({
354
+ visible: false,
355
+ x: 0,
356
+ y: 0,
357
+ tabId: 0
358
+ });
359
+ const engineMap = /* @__PURE__ */ new Map();
360
+ const paneResizing = ref2(false);
361
+ let paneResizeState = {
362
+ tabId: 0,
363
+ dividerIndex: 0,
364
+ startX: 0,
365
+ startWidths: [],
366
+ containerWidth: 0
367
+ };
368
+ const panelResizing = ref2(false);
369
+ let panelResizeState = { startY: 0, startHeight: 0, startX: 0, startWidth: 0 };
370
+ function addTab(title) {
371
+ const tabId = nextTabId++;
372
+ const paneId = nextPaneId++;
373
+ tabs.value = [...tabs.value, {
374
+ id: tabId,
375
+ title: title ?? `Terminal ${tabId}`,
376
+ isEditing: false,
377
+ panes: [{ id: paneId, widthPercent: 100 }]
378
+ }];
379
+ updateActiveTabId(tabId);
380
+ activePaneId.value = paneId;
381
+ emit("tab-added", { tabId });
382
+ return tabId;
383
+ }
384
+ let themeObserver;
385
+ function syncThemeFromEngine() {
386
+ const engine = engineMap.values().next().value;
387
+ if (!engine) return;
388
+ const theme = engine.getTerminal().options.theme;
389
+ if (theme) themeStyles.value = derivePanelThemeStyles(theme);
390
+ }
391
+ function setupThemeSync() {
392
+ if (!syncTheme.value) return;
393
+ setTimeout(() => {
394
+ syncThemeFromEngine();
395
+ const container = document.querySelector(".cli-panel-wrapper .terminal-container");
396
+ if (!container) return;
397
+ themeObserver?.disconnect();
398
+ themeObserver = new MutationObserver(syncThemeFromEngine);
399
+ themeObserver.observe(container, { attributes: true, attributeFilter: ["style"] });
400
+ }, 200);
401
+ }
402
+ onMounted2(() => {
403
+ document.addEventListener("click", closePositionDropdown);
404
+ });
405
+ onBeforeUnmount2(() => {
406
+ themeObserver?.disconnect();
407
+ document.removeEventListener("click", closePositionDropdown);
408
+ });
409
+ function toggle() {
410
+ const newVal = !resolvedCollapsed.value;
411
+ updateCollapsed(newVal);
412
+ if (!newVal && !initialized.value) {
413
+ initialized.value = true;
414
+ addTab();
415
+ setupThemeSync();
416
+ }
417
+ }
418
+ function closeTab(id) {
419
+ const idx = tabs.value.findIndex((t) => t.id === id);
420
+ if (idx === -1) return;
421
+ tabs.value.splice(idx, 1);
422
+ emit("tab-closed", { tabId: id });
423
+ if (tabs.value.length === 0) {
424
+ initialized.value = false;
425
+ updateCollapsed(true);
426
+ return;
427
+ }
428
+ if (resolvedActiveTabId.value === id) {
429
+ const newIdx = Math.min(idx, tabs.value.length - 1);
430
+ selectTab(tabs.value[newIdx].id);
431
+ }
432
+ }
433
+ function selectTab(id) {
434
+ updateActiveTabId(id);
435
+ const tab = tabs.value.find((t) => t.id === id);
436
+ if (tab && tab.panes.length > 0) {
437
+ activePaneId.value = tab.panes[0].id;
438
+ }
439
+ }
440
+ function splitPane(tabId) {
441
+ const targetTabId = tabId ?? resolvedActiveTabId.value;
442
+ const paneId = nextPaneId++;
443
+ tabs.value = tabs.value.map((t) => {
444
+ if (t.id !== targetTabId) return t;
445
+ const newPanes = [...t.panes, { id: paneId, widthPercent: 0 }];
446
+ normalizePanes(newPanes);
447
+ return { ...t, panes: newPanes };
448
+ });
449
+ activePaneId.value = paneId;
450
+ emit("pane-split", { paneId, tabId: targetTabId });
451
+ return paneId;
452
+ }
453
+ function closePane(paneId) {
454
+ for (const tab of tabs.value) {
455
+ const idx = tab.panes.findIndex((p) => p.id === paneId);
456
+ if (idx === -1) continue;
457
+ if (tab.panes.length <= 1) {
458
+ closeTab(tab.id);
459
+ return;
460
+ }
461
+ engineMap.get(paneId)?.destroy();
462
+ engineMap.delete(paneId);
463
+ const newPanes = tab.panes.filter((p) => p.id !== paneId);
464
+ normalizePanes(newPanes);
465
+ if (activePaneId.value === paneId) {
466
+ activePaneId.value = newPanes[Math.min(idx, newPanes.length - 1)].id;
467
+ }
468
+ emit("pane-closed", { paneId });
469
+ tabs.value = tabs.value.map(
470
+ (t) => t.id === tab.id ? { ...t, panes: newPanes } : t
471
+ );
472
+ return;
473
+ }
474
+ }
475
+ function renameTab(tabId, title) {
476
+ tabs.value = tabs.value.map((t) => t.id === tabId ? { ...t, title } : t);
477
+ }
478
+ function startRename(tabId) {
479
+ tabs.value.forEach((t) => {
480
+ t.isEditing = t.id === tabId;
481
+ });
482
+ }
483
+ function commitRename(tabId, value) {
484
+ const trimmed = value.trim();
485
+ const tab = tabs.value.find((t) => t.id === tabId);
486
+ if (tab) {
487
+ if (trimmed) tab.title = trimmed;
488
+ tab.isEditing = false;
489
+ }
490
+ }
491
+ function closeContextMenu() {
492
+ contextMenu.value = { ...contextMenu.value, visible: false };
493
+ }
494
+ function onDocClick() {
495
+ if (contextMenu.value.visible) closeContextMenu();
496
+ }
497
+ onMounted2(() => document.addEventListener("click", onDocClick));
498
+ onBeforeUnmount2(
499
+ () => document.removeEventListener("click", onDocClick)
500
+ );
501
+ function onPanelResizeStart(e) {
502
+ if (!resizable.value) return;
503
+ e.preventDefault();
504
+ if (resolvedCollapsed.value) toggle();
505
+ panelResizing.value = true;
506
+ panelResizeState = {
507
+ startY: e.clientY,
508
+ startHeight: resolvedHeight.value,
509
+ startX: e.clientX,
510
+ startWidth: resolvedWidth.value
511
+ };
512
+ }
513
+ function onPanelResizeMove(e) {
514
+ if (!panelResizing.value) return;
515
+ if (isHorizontal.value) {
516
+ const deltaX = resolvedPosition.value === "left" ? e.clientX - panelResizeState.startX : panelResizeState.startX - e.clientX;
517
+ let next = Math.max(100, panelResizeState.startWidth + deltaX);
518
+ if (next > window.innerWidth) next = window.innerWidth;
519
+ updateWidth(next);
520
+ } else {
521
+ const deltaY = resolvedPosition.value === "top" ? e.clientY - panelResizeState.startY : panelResizeState.startY - e.clientY;
522
+ let next = Math.max(100, panelResizeState.startHeight + deltaY);
523
+ if (next > window.innerHeight) next = window.innerHeight;
524
+ updateHeight(next);
525
+ }
526
+ }
527
+ function onPanelResizeEnd() {
528
+ panelResizing.value = false;
529
+ }
530
+ onMounted2(() => {
531
+ document.addEventListener("mousemove", onPanelResizeMove);
532
+ document.addEventListener("mouseup", onPanelResizeEnd);
533
+ });
534
+ onBeforeUnmount2(() => {
535
+ document.removeEventListener("mousemove", onPanelResizeMove);
536
+ document.removeEventListener("mouseup", onPanelResizeEnd);
537
+ });
538
+ function onPaneResizeStart(e, tabId, dividerIndex) {
539
+ e.preventDefault();
540
+ const tab = tabs.value.find((t) => t.id === tabId);
541
+ if (!tab) return;
542
+ paneResizing.value = true;
543
+ const container = e.target.closest(
544
+ ".cli-panel-panes-container"
545
+ );
546
+ paneResizeState = {
547
+ tabId,
548
+ dividerIndex,
549
+ startX: e.clientX,
550
+ startWidths: tab.panes.map((p) => p.widthPercent),
551
+ containerWidth: container ? container.clientWidth : 1
552
+ };
553
+ document.body.classList.add("cli-pane-resizing");
554
+ }
555
+ function onPaneResizeMove(e) {
556
+ if (!paneResizing.value) return;
557
+ const s = paneResizeState;
558
+ const deltaPct = (e.clientX - s.startX) / s.containerWidth * 100;
559
+ const i = s.dividerIndex;
560
+ let leftW = s.startWidths[i] + deltaPct;
561
+ let rightW = s.startWidths[i + 1] - deltaPct;
562
+ if (leftW < MIN_PANE_WIDTH_PERCENT) {
563
+ leftW = MIN_PANE_WIDTH_PERCENT;
564
+ rightW = s.startWidths[i] + s.startWidths[i + 1] - MIN_PANE_WIDTH_PERCENT;
565
+ }
566
+ if (rightW < MIN_PANE_WIDTH_PERCENT) {
567
+ rightW = MIN_PANE_WIDTH_PERCENT;
568
+ leftW = s.startWidths[i] + s.startWidths[i + 1] - MIN_PANE_WIDTH_PERCENT;
569
+ }
570
+ const tab = tabs.value.find((t) => t.id === s.tabId);
571
+ if (tab) {
572
+ tab.panes[i].widthPercent = leftW;
573
+ tab.panes[i + 1].widthPercent = rightW;
574
+ }
575
+ }
576
+ function onPaneResizeEnd() {
577
+ if (!paneResizing.value) return;
578
+ paneResizing.value = false;
579
+ document.body.classList.remove("cli-pane-resizing");
580
+ }
581
+ onMounted2(() => {
582
+ document.addEventListener("mousemove", onPaneResizeMove);
583
+ document.addEventListener("mouseup", onPaneResizeEnd);
584
+ });
585
+ onBeforeUnmount2(() => {
586
+ document.removeEventListener("mousemove", onPaneResizeMove);
587
+ document.removeEventListener("mouseup", onPaneResizeEnd);
588
+ });
589
+ function toggleMaximize() {
590
+ if (!resolvedMaximized.value) {
591
+ if (isHorizontal.value) {
592
+ prevWidth.value = resolvedWidth.value;
593
+ updateWidth(window.innerWidth);
594
+ } else {
595
+ prevHeight.value = resolvedHeight.value;
596
+ updateHeight(window.innerHeight);
597
+ }
598
+ } else {
599
+ if (isHorizontal.value) {
600
+ updateWidth(prevWidth.value);
601
+ } else {
602
+ updateHeight(prevHeight.value);
603
+ }
604
+ }
605
+ updateMaximized(!resolvedMaximized.value);
606
+ }
607
+ function handleClose() {
608
+ visible.value = false;
609
+ emit("close");
610
+ props.onClose?.();
611
+ }
612
+ function handleHide() {
613
+ preHideCollapsed = resolvedCollapsed.value;
614
+ updateHidden(true);
615
+ }
616
+ function handleUnhide() {
617
+ updateHidden(false);
618
+ updateCollapsed(preHideCollapsed);
619
+ }
620
+ const positionDropdownOpen = ref2(false);
621
+ function closePositionDropdown() {
622
+ positionDropdownOpen.value = false;
623
+ }
624
+ function togglePositionDropdown(e) {
625
+ e.stopPropagation();
626
+ positionDropdownOpen.value = !positionDropdownOpen.value;
627
+ }
628
+ function selectPosition(pos) {
629
+ positionDropdownOpen.value = false;
630
+ updatePosition(pos);
631
+ }
632
+ function getEngine(paneId) {
633
+ const targetId = paneId ?? activePaneId.value;
634
+ return engineMap.get(targetId);
635
+ }
636
+ function getState() {
637
+ return {
638
+ collapsed: resolvedCollapsed.value,
639
+ hidden: resolvedHidden.value,
640
+ maximized: resolvedMaximized.value,
641
+ position: resolvedPosition.value,
642
+ height: resolvedHeight.value,
643
+ width: resolvedWidth.value,
644
+ activeTabId: resolvedActiveTabId.value,
645
+ activePaneId: activePaneId.value,
646
+ tabs: tabs.value.map((t) => ({
647
+ id: t.id,
648
+ title: t.title,
649
+ panes: t.panes.map((p) => ({ id: p.id, widthPercent: p.widthPercent }))
650
+ }))
651
+ };
652
+ }
653
+ expose({
654
+ open: () => {
655
+ if (!visible.value) {
656
+ visible.value = true;
657
+ }
658
+ if (resolvedHidden.value) {
659
+ updateHidden(false);
660
+ }
661
+ if (resolvedCollapsed.value) {
662
+ updateCollapsed(false);
663
+ if (!initialized.value) {
664
+ initialized.value = true;
665
+ addTab();
666
+ setupThemeSync();
667
+ }
668
+ }
669
+ },
670
+ collapse: () => {
671
+ if (!resolvedCollapsed.value) updateCollapsed(true);
672
+ },
673
+ toggleCollapse: () => {
674
+ if (resolvedCollapsed.value) {
675
+ updateCollapsed(false);
676
+ if (!initialized.value) {
677
+ initialized.value = true;
678
+ addTab();
679
+ setupThemeSync();
680
+ }
681
+ } else {
682
+ updateCollapsed(true);
683
+ }
684
+ },
685
+ hide: () => {
686
+ if (!resolvedHidden.value) updateHidden(true);
687
+ },
688
+ unhide: () => {
689
+ if (resolvedHidden.value) updateHidden(false);
690
+ },
691
+ toggleHide: () => {
692
+ updateHidden(!resolvedHidden.value);
693
+ },
694
+ close: handleClose,
695
+ maximize: () => {
696
+ if (!resolvedMaximized.value) updateMaximized(true);
697
+ },
698
+ restore: () => {
699
+ if (resolvedMaximized.value) updateMaximized(false);
700
+ },
701
+ toggleMaximize: () => {
702
+ updateMaximized(!resolvedMaximized.value);
703
+ },
704
+ resize: (dims) => {
705
+ if (dims.height !== void 0) updateHeight(dims.height);
706
+ if (dims.width !== void 0) updateWidth(dims.width);
707
+ },
708
+ setPosition: updatePosition,
709
+ addTab,
710
+ closeTab,
711
+ selectTab,
712
+ renameTab,
713
+ splitPane,
714
+ closePane,
715
+ getEngine,
716
+ getState
717
+ });
718
+ return () => {
719
+ if (!visible.value) return null;
720
+ const wrapperClass = [
721
+ "cli-panel-wrapper",
722
+ resolvedCollapsed.value && "collapsed",
723
+ resolvedMaximized.value && "maximized",
724
+ panelResizing.value && "resizing",
725
+ props.class
726
+ ].filter(Boolean).join(" ");
727
+ const wrapperStyle = {
728
+ ...isHorizontal.value ? { width: `${resolvedWidth.value}px` } : { height: `${resolvedHeight.value}px` },
729
+ ...resolvedHidden.value ? { display: "none" } : {},
730
+ ...themeStyles.value,
731
+ ...props.style
732
+ };
733
+ const headerEl = h2("div", { class: "cli-panel-header" }, [
734
+ h2(
735
+ "div",
736
+ {
737
+ class: "cli-panel-resize-bar",
738
+ onMousedown: onPanelResizeStart
739
+ },
740
+ [h2("div", { class: "cli-panel-resize-grip" })]
741
+ ),
742
+ h2("div", { class: "cli-panel-header-content" }, [
743
+ h2("p", { class: "cli-panel-title" }, [
744
+ h2("span", { class: "cli-panel-title-icon" }, [
745
+ terminalIcon()
746
+ ]),
747
+ "CLI"
748
+ ]),
749
+ h2("div", { class: "cli-panel-action-buttons" }, [
750
+ h2("div", {
751
+ class: "cli-panel-btn-position-wrapper",
752
+ onClick: (e) => e.stopPropagation()
753
+ }, [
754
+ h2("button", {
755
+ class: "cli-panel-btn cli-panel-btn-position",
756
+ title: "Move panel",
757
+ onClick: togglePositionDropdown
758
+ }, [positionIcon(resolvedPosition.value)]),
759
+ positionDropdownOpen.value ? h2(
760
+ "div",
761
+ { class: "cli-panel-position-dropdown" },
762
+ ["bottom", "top", "left", "right"].map(
763
+ (pos) => h2("button", {
764
+ class: ["cli-panel-position-dropdown-item", resolvedPosition.value === pos ? "active" : ""].filter(Boolean).join(" "),
765
+ onClick: () => selectPosition(pos)
766
+ }, [
767
+ positionIcon(pos, 16),
768
+ pos.charAt(0).toUpperCase() + pos.slice(1)
769
+ ])
770
+ )
771
+ ) : null
772
+ ]),
773
+ hideable.value ? h2("button", {
774
+ class: "cli-panel-btn cli-panel-btn-hide",
775
+ title: "Hide",
776
+ onClick: handleHide
777
+ }, [hideIcon()]) : null,
778
+ h2(
779
+ "button",
780
+ {
781
+ class: "cli-panel-btn",
782
+ title: resolvedMaximized.value ? "Restore" : "Maximize",
783
+ disabled: resolvedCollapsed.value,
784
+ onClick: toggleMaximize
785
+ },
786
+ [resolvedMaximized.value ? restoreIcon() : maximizeIcon()]
787
+ ),
788
+ h2(
789
+ "button",
790
+ {
791
+ class: "cli-panel-btn",
792
+ title: resolvedCollapsed.value ? "Expand" : "Collapse",
793
+ onClick: toggle
794
+ },
795
+ [collapseChevron(resolvedPosition.value, resolvedCollapsed.value)]
796
+ ),
797
+ closable.value ? h2(
798
+ "button",
799
+ {
800
+ class: "cli-panel-btn cli-panel-btn-close",
801
+ title: "Close",
802
+ onClick: handleClose
803
+ },
804
+ [closeIcon()]
805
+ ) : null
806
+ ])
807
+ ])
808
+ ]);
809
+ const contentEl = initialized.value ? h2("div", { class: "cli-panel-content", style: resolvedCollapsed.value ? { display: "none" } : void 0 }, [
810
+ // Tab bar
811
+ h2("div", { class: "cli-panel-tabs" }, [
812
+ h2(
813
+ "ul",
814
+ { class: "cli-panel-tab-list" },
815
+ tabs.value.map(
816
+ (tab) => h2(
817
+ "li",
818
+ {
819
+ key: tab.id,
820
+ class: [
821
+ "cli-panel-tab",
822
+ tab.id === resolvedActiveTabId.value && "active"
823
+ ].filter(Boolean).join(" "),
824
+ onClick: () => selectTab(tab.id),
825
+ onDblclick: () => startRename(tab.id),
826
+ onContextmenu: (e) => {
827
+ e.preventDefault();
828
+ e.stopPropagation();
829
+ contextMenu.value = {
830
+ visible: true,
831
+ x: e.clientX,
832
+ y: e.clientY,
833
+ tabId: tab.id
834
+ };
835
+ }
836
+ },
837
+ tab.isEditing ? [
838
+ h2("input", {
839
+ class: "cli-panel-tab-rename-input",
840
+ type: "text",
841
+ value: tab.title,
842
+ onKeydown: (e) => {
843
+ if (e.key === "Enter")
844
+ commitRename(
845
+ tab.id,
846
+ e.target.value
847
+ );
848
+ else if (e.key === "Escape") {
849
+ tab.isEditing = false;
850
+ }
851
+ },
852
+ onBlur: (e) => commitRename(
853
+ tab.id,
854
+ e.target.value
855
+ ),
856
+ onClick: (e) => e.stopPropagation(),
857
+ onDblclick: (e) => e.stopPropagation()
858
+ })
859
+ ] : [
860
+ h2(
861
+ "span",
862
+ {
863
+ class: "cli-panel-tab-title"
864
+ },
865
+ tab.title
866
+ ),
867
+ h2(
868
+ "button",
869
+ {
870
+ class: "cli-panel-tab-close-btn",
871
+ title: "Close tab",
872
+ onClick: (e) => {
873
+ e.stopPropagation();
874
+ closeTab(
875
+ tab.id
876
+ );
877
+ }
878
+ },
879
+ "\xD7"
880
+ )
881
+ ]
882
+ )
883
+ )
884
+ ),
885
+ h2(
886
+ "button",
887
+ {
888
+ class: "cli-panel-add-tab",
889
+ title: "New terminal",
890
+ onClick: addTab
891
+ },
892
+ "+"
893
+ )
894
+ ]),
895
+ // Terminal instances
896
+ h2(
897
+ "div",
898
+ { class: "cli-panel-instances" },
899
+ tabs.value.map(
900
+ (tab) => h2(
901
+ "div",
902
+ {
903
+ key: tab.id,
904
+ class: "cli-panel-instance",
905
+ style: {
906
+ display: tab.id === resolvedActiveTabId.value ? void 0 : "none"
907
+ }
908
+ },
909
+ [
910
+ h2(
911
+ "div",
912
+ {
913
+ class: [
914
+ "cli-panel-panes-container",
915
+ paneResizing.value && "resizing"
916
+ ].filter(Boolean).join(" ")
917
+ },
918
+ tab.panes.flatMap((pane, i) => {
919
+ const nodes = [];
920
+ if (i > 0) {
921
+ nodes.push(
922
+ h2(
923
+ "div",
924
+ {
925
+ class: "cli-panel-pane-divider",
926
+ onMousedown: (e) => onPaneResizeStart(
927
+ e,
928
+ tab.id,
929
+ i - 1
930
+ )
931
+ },
932
+ [
933
+ h2("div", {
934
+ class: "cli-panel-pane-divider-grip"
935
+ })
936
+ ]
937
+ )
938
+ );
939
+ }
940
+ nodes.push(
941
+ h2(
942
+ "div",
943
+ {
944
+ class: [
945
+ "cli-panel-pane",
946
+ pane.id === activePaneId.value && tab.id === resolvedActiveTabId.value && "active-pane"
947
+ ].filter(
948
+ Boolean
949
+ ).join(" "),
950
+ style: {
951
+ flex: `${pane.widthPercent} 1 0`
952
+ },
953
+ onClick: () => {
954
+ activePaneId.value = pane.id;
955
+ updateActiveTabId(tab.id);
956
+ }
957
+ },
958
+ [
959
+ tab.panes.length > 1 ? h2(
960
+ "button",
961
+ {
962
+ class: "cli-panel-pane-close-btn",
963
+ title: "Close pane",
964
+ onClick: (e) => {
965
+ e.stopPropagation();
966
+ closePane(
967
+ pane.id
968
+ );
969
+ }
970
+ },
971
+ "\xD7"
972
+ ) : null,
973
+ h2(Cli, {
974
+ options: mergedOptions.value,
975
+ modules: mergedModules.value,
976
+ processors: mergedProcessors.value,
977
+ services: mergedServices.value,
978
+ snapshot: pane.snapshot,
979
+ style: {
980
+ height: "100%"
981
+ },
982
+ onReady: (engine) => {
983
+ engineMap.set(pane.id, engine);
984
+ }
985
+ })
986
+ ]
987
+ )
988
+ );
989
+ return nodes;
990
+ })
991
+ )
992
+ ]
993
+ )
994
+ )
995
+ )
996
+ ]) : null;
997
+ const contextMenuEl = contextMenu.value.visible ? h2(
998
+ "div",
999
+ {
1000
+ class: "cli-panel-context-menu",
1001
+ style: {
1002
+ left: `${contextMenu.value.x}px`,
1003
+ top: `${contextMenu.value.y}px`
1004
+ },
1005
+ onClick: (e) => e.stopPropagation()
1006
+ },
1007
+ [
1008
+ h2(
1009
+ "button",
1010
+ {
1011
+ class: "cli-panel-context-menu-item",
1012
+ onClick: () => {
1013
+ closeContextMenu();
1014
+ startRename(contextMenu.value.tabId);
1015
+ }
1016
+ },
1017
+ "Rename"
1018
+ ),
1019
+ h2(
1020
+ "button",
1021
+ {
1022
+ class: "cli-panel-context-menu-item",
1023
+ onClick: () => {
1024
+ const tab = tabs.value.find(
1025
+ (t) => t.id === contextMenu.value.tabId
1026
+ );
1027
+ closeContextMenu();
1028
+ if (!tab) return;
1029
+ const sourceEngine = engineMap.get(tab.panes[0]?.id);
1030
+ const snapshot = sourceEngine?.snapshot();
1031
+ const paneId = nextPaneId++;
1032
+ const tabId = nextTabId++;
1033
+ const idx = tabs.value.indexOf(tab);
1034
+ tabs.value.splice(idx + 1, 0, {
1035
+ id: tabId,
1036
+ title: `${tab.title} (copy)`,
1037
+ isEditing: false,
1038
+ panes: [
1039
+ { id: paneId, widthPercent: 100, snapshot }
1040
+ ]
1041
+ });
1042
+ updateActiveTabId(tabId);
1043
+ activePaneId.value = paneId;
1044
+ }
1045
+ },
1046
+ "Duplicate"
1047
+ ),
1048
+ h2(
1049
+ "button",
1050
+ {
1051
+ class: "cli-panel-context-menu-item",
1052
+ onClick: () => {
1053
+ closeContextMenu();
1054
+ splitPane(contextMenu.value.tabId);
1055
+ }
1056
+ },
1057
+ "Split Right"
1058
+ ),
1059
+ h2("div", {
1060
+ class: "cli-panel-context-menu-separator"
1061
+ }),
1062
+ h2(
1063
+ "button",
1064
+ {
1065
+ class: "cli-panel-context-menu-item",
1066
+ onClick: () => {
1067
+ closeContextMenu();
1068
+ closeTab(contextMenu.value.tabId);
1069
+ }
1070
+ },
1071
+ "Close"
1072
+ ),
1073
+ h2(
1074
+ "button",
1075
+ {
1076
+ class: "cli-panel-context-menu-item",
1077
+ disabled: tabs.value.length <= 1,
1078
+ onClick: () => {
1079
+ const id = contextMenu.value.tabId;
1080
+ closeContextMenu();
1081
+ tabs.value = tabs.value.filter(
1082
+ (t) => t.id === id
1083
+ );
1084
+ updateActiveTabId(id);
1085
+ }
1086
+ },
1087
+ "Close Others"
1088
+ ),
1089
+ h2(
1090
+ "button",
1091
+ {
1092
+ class: "cli-panel-context-menu-item",
1093
+ onClick: () => {
1094
+ const id = contextMenu.value.tabId;
1095
+ closeContextMenu();
1096
+ const idx = tabs.value.findIndex(
1097
+ (t) => t.id === id
1098
+ );
1099
+ if (idx !== -1)
1100
+ tabs.value = tabs.value.slice(
1101
+ 0,
1102
+ idx + 1
1103
+ );
1104
+ }
1105
+ },
1106
+ "Close to the Right"
1107
+ ),
1108
+ h2("div", {
1109
+ class: "cli-panel-context-menu-separator"
1110
+ }),
1111
+ h2(
1112
+ "button",
1113
+ {
1114
+ class: "cli-panel-context-menu-item destructive",
1115
+ onClick: () => {
1116
+ closeContextMenu();
1117
+ tabs.value = [];
1118
+ initialized.value = false;
1119
+ updateCollapsed(true);
1120
+ }
1121
+ },
1122
+ "Close All"
1123
+ )
1124
+ ]
1125
+ ) : null;
1126
+ const hideTabEl = resolvedHidden.value ? h2("button", {
1127
+ class: "cli-panel-hide-tab",
1128
+ "data-position": resolvedPosition.value,
1129
+ "data-hide-align": hideAlignment.value,
1130
+ style: themeStyles.value,
1131
+ title: "Show CLI",
1132
+ onClick: handleUnhide
1133
+ }, [
1134
+ h2("svg", { class: "cli-panel-hide-tab-icon", ...svgAttrs, width: "16", height: "16", "stroke-width": "2" }, [
1135
+ h2("polyline", { points: "4 17 10 11 4 5" }),
1136
+ h2("line", { x1: "12", y1: "19", x2: "20", y2: "19" })
1137
+ ]),
1138
+ h2("span", { class: "cli-panel-hide-tab-label" }, "CLI"),
1139
+ hideTabChevron(resolvedPosition.value)
1140
+ ]) : null;
1141
+ return h2("div", {}, [
1142
+ hideTabEl,
1143
+ h2(
1144
+ "div",
1145
+ {
1146
+ class: wrapperClass,
1147
+ style: wrapperStyle,
1148
+ "data-position": resolvedPosition.value,
1149
+ "data-resizable": String(resizable.value),
1150
+ "data-closable": String(closable.value),
1151
+ "data-hideable": String(hideable.value)
1152
+ },
1153
+ [headerEl, contentEl]
1154
+ ),
1155
+ contextMenuEl
1156
+ ]);
1157
+ };
1158
+ }
1159
+ });
1160
+
1161
+ // src/CliProvider.ts
1162
+ import { defineComponent as defineComponent4, ref as ref4, h as h3, provide as provide2 } from "vue";
1163
+
1164
+ // src/useCliEngine.ts
1165
+ import { onMounted as onMounted3, onBeforeUnmount as onBeforeUnmount3, shallowRef } from "vue";
1166
+ import { CliEngine as CliEngine2 } from "@qodalis/cli";
1167
+ function useCliEngine(containerRef, config) {
1168
+ const engine = shallowRef(null);
1169
+ onMounted3(async () => {
1170
+ if (!containerRef.value) return;
1171
+ const e = new CliEngine2(containerRef.value, config?.options);
1172
+ e.registerService("cli-framework", "Vue");
1173
+ if (config?.services) {
1174
+ for (const [token, value] of Object.entries(config.services)) {
1175
+ e.registerService(token, value);
1176
+ }
1177
+ }
1178
+ if (config?.modules) {
1179
+ e.registerModules(config.modules);
1180
+ }
1181
+ if (config?.processors) {
1182
+ e.registerProcessors(config.processors);
1183
+ }
1184
+ await e.start();
1185
+ engine.value = e;
1186
+ });
1187
+ onBeforeUnmount3(() => {
1188
+ engine.value?.destroy();
1189
+ engine.value = null;
1190
+ });
1191
+ return engine;
1192
+ }
1193
+
1194
+ // src/CliProvider.ts
1195
+ var CliProvider = defineComponent4({
1196
+ name: "CliProvider",
1197
+ props: {
1198
+ modules: {
1199
+ type: Array,
1200
+ default: void 0
1201
+ },
1202
+ processors: {
1203
+ type: Array,
1204
+ default: void 0
1205
+ },
1206
+ options: {
1207
+ type: Object,
1208
+ default: void 0
1209
+ },
1210
+ services: {
1211
+ type: Object,
1212
+ default: void 0
1213
+ },
1214
+ style: {
1215
+ type: Object,
1216
+ default: void 0
1217
+ }
1218
+ },
1219
+ emits: ["ready"],
1220
+ setup(props, { slots, emit }) {
1221
+ const containerRef = ref4(null);
1222
+ const engine = useCliEngine(containerRef, {
1223
+ modules: props.modules,
1224
+ processors: props.processors,
1225
+ options: props.options,
1226
+ services: props.services
1227
+ });
1228
+ provide2(CliInjectionKey, { engine });
1229
+ return () => h3("div", { style: { height: "100%" } }, [
1230
+ h3("div", {
1231
+ ref: containerRef,
1232
+ style: { height: "100%", ...props.style }
1233
+ }),
1234
+ slots.default?.()
1235
+ ]);
1236
+ }
1237
+ });
1238
+
1239
+ // src/useCli.ts
1240
+ import { inject as inject3 } from "vue";
1241
+ function useCli() {
1242
+ const ctx = inject3(CliInjectionKey);
1243
+ if (!ctx) {
1244
+ throw new Error(
1245
+ "useCli() must be used inside a <CliProvider> component"
1246
+ );
1247
+ }
1248
+ return ctx;
1249
+ }
1250
+
1251
+ // src/index.ts
1252
+ import { CliEngine as CliEngine3 } from "@qodalis/cli";
1253
+ export {
1254
+ Cli,
1255
+ CliConfigKey,
1256
+ CliConfigProvider,
1257
+ CliEngine3 as CliEngine,
1258
+ CliInjectionKey,
1259
+ CliPanel,
1260
+ CliProvider,
1261
+ useCli,
1262
+ useCliEngine
1263
+ };
1264
+ //# sourceMappingURL=index.mjs.map