@openvcs/sdk 0.2.17-nightly.20260406.2 → 0.2.18-beta.8

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/README.md CHANGED
@@ -1,8 +1,7 @@
1
1
  # @openvcs/sdk
2
-
3
- [![Nightly](https://github.com/Open-VCS/OpenVCS-SDK/actions/workflows/nightly.yml/badge.svg?branch=Dev)](https://github.com/Open-VCS/OpenVCS-SDK/actions/workflows/nightly.yml)
2
+ [![Nightly](https://github.com/Open-VCS/OpenVCS-SDK/actions/workflows/publish.yml/badge.svg?branch=Dev)](https://github.com/Open-VCS/OpenVCS-SDK/actions/workflows/publish.yml)
4
3
  [![Dev](https://github.com/Open-VCS/OpenVCS-SDK/actions/workflows/ci.yml/badge.svg?branch=Dev)](https://github.com/Open-VCS/OpenVCS-SDK/actions/workflows/ci.yml)
5
- [![Stable](https://github.com/Open-VCS/OpenVCS-SDK/actions/workflows/release.yml/badge.svg?branch=Stable)](https://github.com/Open-VCS/OpenVCS-SDK/actions/workflows/release.yml)
4
+ [![Publish](https://github.com/Open-VCS/OpenVCS-SDK/actions/workflows/publish.yml/badge.svg?branch=Stable)](https://github.com/Open-VCS/OpenVCS-SDK/actions/workflows/publish.yml)
6
5
 
7
6
  OpenVCS SDK for npm-based plugin development.
8
7
 
package/lib/build.js CHANGED
@@ -1,6 +1,39 @@
1
1
  "use strict";
2
2
  // Copyright © 2025-2026 OpenVCS Contributors
3
3
  // SPDX-License-Identifier: GPL-3.0-or-later
4
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
5
+ if (k2 === undefined) k2 = k;
6
+ var desc = Object.getOwnPropertyDescriptor(m, k);
7
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
8
+ desc = { enumerable: true, get: function() { return m[k]; } };
9
+ }
10
+ Object.defineProperty(o, k2, desc);
11
+ }) : (function(o, m, k, k2) {
12
+ if (k2 === undefined) k2 = k;
13
+ o[k2] = m[k];
14
+ }));
15
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
16
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
17
+ }) : function(o, v) {
18
+ o["default"] = v;
19
+ });
20
+ var __importStar = (this && this.__importStar) || (function () {
21
+ var ownKeys = function(o) {
22
+ ownKeys = Object.getOwnPropertyNames || function (o) {
23
+ var ar = [];
24
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
25
+ return ar;
26
+ };
27
+ return ownKeys(o);
28
+ };
29
+ return function (mod) {
30
+ if (mod && mod.__esModule) return mod;
31
+ var result = {};
32
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
33
+ __setModuleDefault(result, mod);
34
+ return result;
35
+ };
36
+ })();
4
37
  Object.defineProperty(exports, "__esModule", { value: true });
5
38
  exports.npmExecutable = npmExecutable;
6
39
  exports.shouldUseWindowsShell = shouldUseWindowsShell;
@@ -15,8 +48,8 @@ exports.generateModuleBootstrap = generateModuleBootstrap;
15
48
  exports.hasPackageJson = hasPackageJson;
16
49
  exports.runCommand = runCommand;
17
50
  exports.buildPluginAssets = buildPluginAssets;
18
- const fs = require("node:fs");
19
- const path = require("node:path");
51
+ const fs = __importStar(require("node:fs"));
52
+ const path = __importStar(require("node:path"));
20
53
  const node_child_process_1 = require("node:child_process");
21
54
  const fs_utils_1 = require("./fs-utils");
22
55
  const AUTHORED_PLUGIN_MODULE_BASENAME = "plugin.js";
package/lib/fs-utils.js CHANGED
@@ -1,12 +1,45 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  Object.defineProperty(exports, "__esModule", { value: true });
3
36
  exports.isPathInside = isPathInside;
4
37
  exports.rejectSymlinksRecursive = rejectSymlinksRecursive;
5
38
  exports.ensureDirectory = ensureDirectory;
6
39
  exports.copyFileStrict = copyFileStrict;
7
40
  exports.copyDirectoryRecursiveStrict = copyDirectoryRecursiveStrict;
8
- const fs = require("node:fs");
9
- const path = require("node:path");
41
+ const fs = __importStar(require("node:fs"));
42
+ const path = __importStar(require("node:path"));
10
43
  function isPathInside(rootPath, candidatePath) {
11
44
  const relative = path.relative(rootPath, candidatePath);
12
45
  return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
package/lib/init.js CHANGED
@@ -1,12 +1,45 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  Object.defineProperty(exports, "__esModule", { value: true });
3
36
  exports.__private = void 0;
4
37
  exports.initUsage = initUsage;
5
38
  exports.runInitCommand = runInitCommand;
6
39
  exports.isUsageError = isUsageError;
7
- const fs = require("node:fs");
8
- const path = require("node:path");
9
- const readline = require("node:readline/promises");
40
+ const fs = __importStar(require("node:fs"));
41
+ const path = __importStar(require("node:path"));
42
+ const readline = __importStar(require("node:readline/promises"));
10
43
  const node_process_1 = require("node:process");
11
44
  const node_child_process_1 = require("node:child_process");
12
45
  const packageJson = require("../package.json");
@@ -1,4 +1,4 @@
1
- import type { ModalButtonVariant, ModalContentAlign, ModalInputKind, ModalListRowDefinition, ModalSelectOptionDefinition, PluginModalDefinition } from '../types/modal.js';
1
+ import type { ModalButtonVariant, ModalContentAlign, ModalInputKind, ModalListRowDefinition, ModalSelectOptionDefinition, PluginModalContentItem, PluginModalDefinition } from '../types/modal.js';
2
2
  /** Describes the options accepted by `ModalBuilder.button()`. */
3
3
  export interface ModalBuilderButtonOptions {
4
4
  /** Stores the optional tooltip text. */
@@ -50,6 +50,27 @@ export interface ModalBuilderListOptions {
50
50
  /** Stores the list rows. */
51
51
  items: ModalListRowDefinition[];
52
52
  }
53
+ /** Describes the options accepted by `ModalBuilder.horizontalBox()`. */
54
+ export interface ModalBuilderHorizontalBoxOptions {
55
+ /** Stores the spacing between children. */
56
+ gap?: string;
57
+ /** Stores the alignment hint for the main axis. */
58
+ align?: ModalContentAlign;
59
+ /** Stores whether children may wrap. */
60
+ wrap?: boolean;
61
+ }
62
+ /** Describes the options accepted by `ModalBuilder.verticalBox()`. */
63
+ export interface ModalBuilderVerticalBoxOptions {
64
+ /** Stores the spacing between children. */
65
+ gap?: string;
66
+ }
67
+ /** Describes the options accepted by `ModalBuilder.grid()`. */
68
+ export interface ModalBuilderGridOptions {
69
+ /** Stores the CSS grid column template. */
70
+ columns: string;
71
+ /** Stores the spacing between cells. */
72
+ gap?: string;
73
+ }
53
74
  /** Builds a structured modal definition with a fluent class API. */
54
75
  export declare class ModalBuilder {
55
76
  private readonly definition;
@@ -59,6 +80,12 @@ export declare class ModalBuilder {
59
80
  text(content: string, options?: ModalBuilderTextOptions): this;
60
81
  /** Adds a separator to the modal body. */
61
82
  separator(): this;
83
+ /** Adds a horizontal box to the modal body. */
84
+ horizontalBox(content: PluginModalContentItem[], options?: ModalBuilderHorizontalBoxOptions): this;
85
+ /** Adds a vertical box to the modal body. */
86
+ verticalBox(content: PluginModalContentItem[], options?: ModalBuilderVerticalBoxOptions): this;
87
+ /** Adds a grid container to the modal body. */
88
+ grid(content: PluginModalContentItem[], options: ModalBuilderGridOptions): this;
62
89
  /** Adds a button to the modal body. */
63
90
  button(id: string, content: string, options?: ModalBuilderButtonOptions): this;
64
91
  /** Adds a text input to the modal body. */
@@ -28,6 +28,36 @@ class ModalBuilder {
28
28
  this.definition.content.push({ type: 'separator' });
29
29
  return this;
30
30
  }
31
+ /** Adds a horizontal box to the modal body. */
32
+ horizontalBox(content, options = {}) {
33
+ this.definition.content.push({
34
+ type: 'horizontal-box',
35
+ content: cloneContent(content),
36
+ ...(options.gap ? { gap: options.gap } : {}),
37
+ ...(options.align ? { align: options.align } : {}),
38
+ ...(options.wrap !== undefined ? { wrap: options.wrap } : {}),
39
+ });
40
+ return this;
41
+ }
42
+ /** Adds a vertical box to the modal body. */
43
+ verticalBox(content, options = {}) {
44
+ this.definition.content.push({
45
+ type: 'vertical-box',
46
+ content: cloneContent(content),
47
+ ...(options.gap ? { gap: options.gap } : {}),
48
+ });
49
+ return this;
50
+ }
51
+ /** Adds a grid container to the modal body. */
52
+ grid(content, options) {
53
+ this.definition.content.push({
54
+ type: 'grid',
55
+ content: cloneContent(content),
56
+ columns: String(options.columns || '').trim(),
57
+ ...(options.gap ? { gap: options.gap } : {}),
58
+ });
59
+ return this;
60
+ }
31
61
  /** Adds a button to the modal body. */
32
62
  button(id, content, options = {}) {
33
63
  this.definition.content.push({
@@ -83,7 +113,7 @@ class ModalBuilder {
83
113
  build() {
84
114
  return {
85
115
  title: this.definition.title,
86
- content: this.definition.content.map((item) => ({ ...item })),
116
+ content: cloneContent(this.definition.content),
87
117
  };
88
118
  }
89
119
  /** Returns the serialized modal payload for a host request. */
@@ -92,3 +122,31 @@ class ModalBuilder {
92
122
  }
93
123
  }
94
124
  exports.ModalBuilder = ModalBuilder;
125
+ /** Clones modal content recursively so nested containers stay isolated. */
126
+ function cloneContent(content) {
127
+ return Array.isArray(content) ? content.map((item) => cloneItem(item)) : [];
128
+ }
129
+ /** Clones one modal content item recursively. */
130
+ function cloneItem(item) {
131
+ if (!item || typeof item !== 'object')
132
+ return item;
133
+ if (item.type === 'horizontal-box') {
134
+ return {
135
+ ...item,
136
+ content: cloneContent(item.content),
137
+ };
138
+ }
139
+ if (item.type === 'vertical-box') {
140
+ return {
141
+ ...item,
142
+ content: cloneContent(item.content),
143
+ };
144
+ }
145
+ if (item.type === 'grid') {
146
+ return {
147
+ ...item,
148
+ content: cloneContent(item.content),
149
+ };
150
+ }
151
+ return { ...item };
152
+ }
@@ -35,6 +35,39 @@ export interface ModalSeparatorDefinition {
35
35
  /** Always stores `separator`. */
36
36
  type: 'separator';
37
37
  }
38
+ /** Describes one horizontal box rendered inside a modal. */
39
+ export interface ModalHorizontalBoxDefinition {
40
+ /** Always stores `horizontal-box`. */
41
+ type: 'horizontal-box';
42
+ /** Stores the ordered child content. */
43
+ content: PluginModalContentItem[];
44
+ /** Stores the optional spacing between children. */
45
+ gap?: string;
46
+ /** Stores the alignment hint along the main axis. */
47
+ align?: ModalContentAlign;
48
+ /** Stores whether children may wrap onto multiple rows. */
49
+ wrap?: boolean;
50
+ }
51
+ /** Describes one vertical box rendered inside a modal. */
52
+ export interface ModalVerticalBoxDefinition {
53
+ /** Always stores `vertical-box`. */
54
+ type: 'vertical-box';
55
+ /** Stores the ordered child content. */
56
+ content: PluginModalContentItem[];
57
+ /** Stores the optional spacing between children. */
58
+ gap?: string;
59
+ }
60
+ /** Describes one grid rendered inside a modal. */
61
+ export interface ModalGridDefinition {
62
+ /** Always stores `grid`. */
63
+ type: 'grid';
64
+ /** Stores the ordered child content. */
65
+ content: PluginModalContentItem[];
66
+ /** Stores the CSS grid column template. */
67
+ columns: string;
68
+ /** Stores the optional spacing between cells. */
69
+ gap?: string;
70
+ }
38
71
  /** Describes one button rendered inside a modal body. */
39
72
  export interface ModalButtonItemDefinition extends ModalButtonDefinition {
40
73
  /** Always stores `button`. */
@@ -119,7 +152,7 @@ export interface ModalListDefinition {
119
152
  items: ModalListRowDefinition[];
120
153
  }
121
154
  /** Describes one plugin modal content item. */
122
- export type PluginModalContentItem = ModalTextDefinition | ModalSeparatorDefinition | ModalButtonItemDefinition | ModalInputDefinition | ModalSelectDefinition | ModalListDefinition;
155
+ export type PluginModalContentItem = ModalTextDefinition | ModalSeparatorDefinition | ModalHorizontalBoxDefinition | ModalVerticalBoxDefinition | ModalGridDefinition | ModalButtonItemDefinition | ModalInputDefinition | ModalSelectDefinition | ModalListDefinition;
123
156
  /** Describes the structured payload used to render a plugin modal. */
124
157
  export interface PluginModalDefinition {
125
158
  /** Stores the modal title. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openvcs/sdk",
3
- "version": "0.2.17-nightly.20260406.2",
3
+ "version": "0.2.18-beta.8",
4
4
  "description": "OpenVCS SDK CLI for plugin scaffolding and runtime asset builds",
5
5
  "license": "GPL-3.0-or-later",
6
6
  "homepage": "https://openvcs.app/",
@@ -40,7 +40,7 @@
40
40
  },
41
41
  "devDependencies": {
42
42
  "@types/node": "^25.3.3",
43
- "typescript": "^5.8.2"
43
+ "typescript": "^6.0.2"
44
44
  },
45
45
  "files": [
46
46
  "src/",
@@ -2,14 +2,13 @@
2
2
  // SPDX-License-Identifier: GPL-3.0-or-later
3
3
 
4
4
  import type {
5
- ModalButtonDefinition,
6
5
  ModalButtonVariant,
7
6
  ModalContentAlign,
8
- ModalInputDefinition,
7
+ ModalGridDefinition,
9
8
  ModalInputKind,
10
- ModalListDefinition,
9
+ ModalHorizontalBoxDefinition,
11
10
  ModalListRowDefinition,
12
- ModalSelectDefinition,
11
+ ModalVerticalBoxDefinition,
13
12
  ModalSelectOptionDefinition,
14
13
  PluginModalContentItem,
15
14
  PluginModalDefinition,
@@ -71,6 +70,30 @@ export interface ModalBuilderListOptions {
71
70
  items: ModalListRowDefinition[];
72
71
  }
73
72
 
73
+ /** Describes the options accepted by `ModalBuilder.horizontalBox()`. */
74
+ export interface ModalBuilderHorizontalBoxOptions {
75
+ /** Stores the spacing between children. */
76
+ gap?: string;
77
+ /** Stores the alignment hint for the main axis. */
78
+ align?: ModalContentAlign;
79
+ /** Stores whether children may wrap. */
80
+ wrap?: boolean;
81
+ }
82
+
83
+ /** Describes the options accepted by `ModalBuilder.verticalBox()`. */
84
+ export interface ModalBuilderVerticalBoxOptions {
85
+ /** Stores the spacing between children. */
86
+ gap?: string;
87
+ }
88
+
89
+ /** Describes the options accepted by `ModalBuilder.grid()`. */
90
+ export interface ModalBuilderGridOptions {
91
+ /** Stores the CSS grid column template. */
92
+ columns: string;
93
+ /** Stores the spacing between cells. */
94
+ gap?: string;
95
+ }
96
+
74
97
  /** Builds a structured modal definition with a fluent class API. */
75
98
  export class ModalBuilder {
76
99
  private readonly definition: PluginModalDefinition;
@@ -100,6 +123,39 @@ export class ModalBuilder {
100
123
  return this;
101
124
  }
102
125
 
126
+ /** Adds a horizontal box to the modal body. */
127
+ horizontalBox(content: PluginModalContentItem[], options: ModalBuilderHorizontalBoxOptions = {}): this {
128
+ this.definition.content.push({
129
+ type: 'horizontal-box',
130
+ content: cloneContent(content),
131
+ ...(options.gap ? { gap: options.gap } : {}),
132
+ ...(options.align ? { align: options.align } : {}),
133
+ ...(options.wrap !== undefined ? { wrap: options.wrap } : {}),
134
+ } as ModalHorizontalBoxDefinition);
135
+ return this;
136
+ }
137
+
138
+ /** Adds a vertical box to the modal body. */
139
+ verticalBox(content: PluginModalContentItem[], options: ModalBuilderVerticalBoxOptions = {}): this {
140
+ this.definition.content.push({
141
+ type: 'vertical-box',
142
+ content: cloneContent(content),
143
+ ...(options.gap ? { gap: options.gap } : {}),
144
+ } as ModalVerticalBoxDefinition);
145
+ return this;
146
+ }
147
+
148
+ /** Adds a grid container to the modal body. */
149
+ grid(content: PluginModalContentItem[], options: ModalBuilderGridOptions): this {
150
+ this.definition.content.push({
151
+ type: 'grid',
152
+ content: cloneContent(content),
153
+ columns: String(options.columns || '').trim(),
154
+ ...(options.gap ? { gap: options.gap } : {}),
155
+ } as ModalGridDefinition);
156
+ return this;
157
+ }
158
+
103
159
  /** Adds a button to the modal body. */
104
160
  button(id: string, content: string, options: ModalBuilderButtonOptions = {}): this {
105
161
  this.definition.content.push({
@@ -159,7 +215,7 @@ export class ModalBuilder {
159
215
  build(): PluginModalDefinition {
160
216
  return {
161
217
  title: this.definition.title,
162
- content: this.definition.content.map((item) => ({ ...item })) as PluginModalContentItem[],
218
+ content: cloneContent(this.definition.content),
163
219
  };
164
220
  }
165
221
 
@@ -168,3 +224,36 @@ export class ModalBuilder {
168
224
  return this.build();
169
225
  }
170
226
  }
227
+
228
+ /** Clones modal content recursively so nested containers stay isolated. */
229
+ function cloneContent(content: PluginModalContentItem[]): PluginModalContentItem[] {
230
+ return Array.isArray(content) ? content.map((item) => cloneItem(item)) : [];
231
+ }
232
+
233
+ /** Clones one modal content item recursively. */
234
+ function cloneItem(item: PluginModalContentItem): PluginModalContentItem {
235
+ if (!item || typeof item !== 'object') return item;
236
+
237
+ if (item.type === 'horizontal-box') {
238
+ return {
239
+ ...item,
240
+ content: cloneContent(item.content),
241
+ } as ModalHorizontalBoxDefinition;
242
+ }
243
+
244
+ if (item.type === 'vertical-box') {
245
+ return {
246
+ ...item,
247
+ content: cloneContent(item.content),
248
+ } as ModalVerticalBoxDefinition;
249
+ }
250
+
251
+ if (item.type === 'grid') {
252
+ return {
253
+ ...item,
254
+ content: cloneContent(item.content),
255
+ } as ModalGridDefinition;
256
+ }
257
+
258
+ return { ...item };
259
+ }
@@ -44,6 +44,42 @@ export interface ModalSeparatorDefinition {
44
44
  type: 'separator';
45
45
  }
46
46
 
47
+ /** Describes one horizontal box rendered inside a modal. */
48
+ export interface ModalHorizontalBoxDefinition {
49
+ /** Always stores `horizontal-box`. */
50
+ type: 'horizontal-box';
51
+ /** Stores the ordered child content. */
52
+ content: PluginModalContentItem[];
53
+ /** Stores the optional spacing between children. */
54
+ gap?: string;
55
+ /** Stores the alignment hint along the main axis. */
56
+ align?: ModalContentAlign;
57
+ /** Stores whether children may wrap onto multiple rows. */
58
+ wrap?: boolean;
59
+ }
60
+
61
+ /** Describes one vertical box rendered inside a modal. */
62
+ export interface ModalVerticalBoxDefinition {
63
+ /** Always stores `vertical-box`. */
64
+ type: 'vertical-box';
65
+ /** Stores the ordered child content. */
66
+ content: PluginModalContentItem[];
67
+ /** Stores the optional spacing between children. */
68
+ gap?: string;
69
+ }
70
+
71
+ /** Describes one grid rendered inside a modal. */
72
+ export interface ModalGridDefinition {
73
+ /** Always stores `grid`. */
74
+ type: 'grid';
75
+ /** Stores the ordered child content. */
76
+ content: PluginModalContentItem[];
77
+ /** Stores the CSS grid column template. */
78
+ columns: string;
79
+ /** Stores the optional spacing between cells. */
80
+ gap?: string;
81
+ }
82
+
47
83
  /** Describes one button rendered inside a modal body. */
48
84
  export interface ModalButtonItemDefinition extends ModalButtonDefinition {
49
85
  /** Always stores `button`. */
@@ -138,6 +174,9 @@ export interface ModalListDefinition {
138
174
  export type PluginModalContentItem =
139
175
  | ModalTextDefinition
140
176
  | ModalSeparatorDefinition
177
+ | ModalHorizontalBoxDefinition
178
+ | ModalVerticalBoxDefinition
179
+ | ModalGridDefinition
141
180
  | ModalButtonItemDefinition
142
181
  | ModalInputDefinition
143
182
  | ModalSelectDefinition
@@ -10,14 +10,83 @@ describe('ModalBuilder', () => {
10
10
  it('builds a structured modal definition', () => {
11
11
  const modal = new ModalBuilder('Manage Submodules')
12
12
  .text('Hello, World!')
13
- .button('new-button', 'Test button, push me!', { align: 'centered' })
13
+ .verticalBox([
14
+ {
15
+ type: 'input',
16
+ id: 'path',
17
+ label: 'Submodule Path',
18
+ placeholder: 'libs/example',
19
+ },
20
+ {
21
+ type: 'grid',
22
+ columns: 'minmax(0, 1fr) minmax(0, 1fr)',
23
+ content: [
24
+ {
25
+ type: 'input',
26
+ id: 'name',
27
+ label: 'Submodule Name',
28
+ placeholder: 'example',
29
+ },
30
+ {
31
+ type: 'input',
32
+ id: 'branch',
33
+ label: 'Branch (optional)',
34
+ placeholder: 'main',
35
+ },
36
+ ],
37
+ },
38
+ ])
39
+ .horizontalBox(
40
+ [
41
+ { type: 'button', id: 'new-button', content: 'Test button, push me!', align: 'centered' },
42
+ { type: 'button', id: 'secondary-button', content: 'Refresh' },
43
+ ],
44
+ { align: 'centered', wrap: true },
45
+ )
14
46
  .build();
15
47
 
16
48
  assert.deepStrictEqual(modal, {
17
49
  title: 'Manage Submodules',
18
50
  content: [
19
51
  { type: 'text', content: 'Hello, World!' },
20
- { type: 'button', id: 'new-button', content: 'Test button, push me!', align: 'centered' },
52
+ {
53
+ type: 'vertical-box',
54
+ content: [
55
+ {
56
+ type: 'input',
57
+ id: 'path',
58
+ label: 'Submodule Path',
59
+ placeholder: 'libs/example',
60
+ },
61
+ {
62
+ type: 'grid',
63
+ columns: 'minmax(0, 1fr) minmax(0, 1fr)',
64
+ content: [
65
+ {
66
+ type: 'input',
67
+ id: 'name',
68
+ label: 'Submodule Name',
69
+ placeholder: 'example',
70
+ },
71
+ {
72
+ type: 'input',
73
+ id: 'branch',
74
+ label: 'Branch (optional)',
75
+ placeholder: 'main',
76
+ },
77
+ ],
78
+ },
79
+ ],
80
+ },
81
+ {
82
+ type: 'horizontal-box',
83
+ align: 'centered',
84
+ wrap: true,
85
+ content: [
86
+ { type: 'button', id: 'new-button', content: 'Test button, push me!', align: 'centered' },
87
+ { type: 'button', id: 'secondary-button', content: 'Refresh' },
88
+ ],
89
+ },
21
90
  ],
22
91
  });
23
92
  });