@orchestra-mcp/settings 1.0.0

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.cjs ADDED
@@ -0,0 +1,935 @@
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
+ SettingField: () => SettingField,
24
+ SettingGroupShell: () => SettingGroupShell,
25
+ SettingsForm: () => SettingsForm,
26
+ SettingsGroup: () => SettingsGroup,
27
+ SettingsNav: () => SettingsNav,
28
+ validateSetting: () => validateSetting
29
+ });
30
+ module.exports = __toCommonJS(index_exports);
31
+
32
+ // src/SettingsForm/SettingField.tsx
33
+ var import_react = require("react");
34
+
35
+ // src/SettingsForm/timezones.ts
36
+ var TIMEZONE_OPTIONS = [
37
+ { label: "UTC", value: "UTC", description: "Coordinated Universal Time" },
38
+ { label: "US/Eastern (ET)", value: "America/New_York", description: "UTC-5 / UTC-4 DST" },
39
+ { label: "US/Central (CT)", value: "America/Chicago", description: "UTC-6 / UTC-5 DST" },
40
+ { label: "US/Mountain (MT)", value: "America/Denver", description: "UTC-7 / UTC-6 DST" },
41
+ { label: "US/Pacific (PT)", value: "America/Los_Angeles", description: "UTC-8 / UTC-7 DST" },
42
+ { label: "US/Alaska", value: "America/Anchorage", description: "UTC-9 / UTC-8 DST" },
43
+ { label: "US/Hawaii", value: "Pacific/Honolulu", description: "UTC-10" },
44
+ { label: "Canada/Atlantic", value: "America/Halifax", description: "UTC-4 / UTC-3 DST" },
45
+ { label: "Canada/Newfoundland", value: "America/St_Johns", description: "UTC-3:30" },
46
+ { label: "Mexico City", value: "America/Mexico_City", description: "UTC-6 / UTC-5 DST" },
47
+ { label: "Sao Paulo", value: "America/Sao_Paulo", description: "UTC-3" },
48
+ { label: "Buenos Aires", value: "America/Argentina/Buenos_Aires", description: "UTC-3" },
49
+ { label: "London (GMT/BST)", value: "Europe/London", description: "UTC+0 / UTC+1 DST" },
50
+ { label: "Paris (CET)", value: "Europe/Paris", description: "UTC+1 / UTC+2 DST" },
51
+ { label: "Berlin (CET)", value: "Europe/Berlin", description: "UTC+1 / UTC+2 DST" },
52
+ { label: "Amsterdam (CET)", value: "Europe/Amsterdam", description: "UTC+1 / UTC+2 DST" },
53
+ { label: "Rome (CET)", value: "Europe/Rome", description: "UTC+1 / UTC+2 DST" },
54
+ { label: "Madrid (CET)", value: "Europe/Madrid", description: "UTC+1 / UTC+2 DST" },
55
+ { label: "Athens (EET)", value: "Europe/Athens", description: "UTC+2 / UTC+3 DST" },
56
+ { label: "Istanbul", value: "Europe/Istanbul", description: "UTC+3" },
57
+ { label: "Moscow", value: "Europe/Moscow", description: "UTC+3" },
58
+ { label: "Dubai", value: "Asia/Dubai", description: "UTC+4" },
59
+ { label: "Kolkata (IST)", value: "Asia/Kolkata", description: "UTC+5:30" },
60
+ { label: "Bangkok (ICT)", value: "Asia/Bangkok", description: "UTC+7" },
61
+ { label: "Singapore (SGT)", value: "Asia/Singapore", description: "UTC+8" },
62
+ { label: "Hong Kong (HKT)", value: "Asia/Hong_Kong", description: "UTC+8" },
63
+ { label: "Shanghai (CST)", value: "Asia/Shanghai", description: "UTC+8" },
64
+ { label: "Taipei (CST)", value: "Asia/Taipei", description: "UTC+8" },
65
+ { label: "Seoul (KST)", value: "Asia/Seoul", description: "UTC+9" },
66
+ { label: "Tokyo (JST)", value: "Asia/Tokyo", description: "UTC+9" },
67
+ { label: "Sydney (AEST)", value: "Australia/Sydney", description: "UTC+10 / UTC+11 DST" },
68
+ { label: "Melbourne (AEST)", value: "Australia/Melbourne", description: "UTC+10 / UTC+11 DST" },
69
+ { label: "Auckland (NZST)", value: "Pacific/Auckland", description: "UTC+12 / UTC+13 DST" },
70
+ { label: "Cairo (EET)", value: "Africa/Cairo", description: "UTC+2" },
71
+ { label: "Lagos (WAT)", value: "Africa/Lagos", description: "UTC+1" },
72
+ { label: "Nairobi (EAT)", value: "Africa/Nairobi", description: "UTC+3" },
73
+ { label: "Johannesburg (SAST)", value: "Africa/Johannesburg", description: "UTC+2" }
74
+ ];
75
+
76
+ // src/SettingsForm/SettingField.tsx
77
+ var import_jsx_runtime = require("react/jsx-runtime");
78
+ var SettingField = ({
79
+ setting,
80
+ value,
81
+ onChange,
82
+ disabled
83
+ }) => {
84
+ const isDisabled = disabled || setting.disabled;
85
+ const handleChange = (0, import_react.useCallback)(
86
+ (newValue) => {
87
+ onChange(setting.key, newValue);
88
+ },
89
+ [onChange, setting.key]
90
+ );
91
+ if (setting.hidden) {
92
+ return null;
93
+ }
94
+ if (setting.type === "boolean") {
95
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
96
+ BooleanField,
97
+ {
98
+ setting,
99
+ value,
100
+ onChange: handleChange,
101
+ disabled: isDisabled
102
+ }
103
+ );
104
+ }
105
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "setting-field", children: [
106
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "setting-field__label", children: [
107
+ setting.label,
108
+ setting.validation?.required && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "setting-field__required", children: "*" })
109
+ ] }),
110
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
111
+ FieldInput,
112
+ {
113
+ setting,
114
+ value,
115
+ onChange: handleChange,
116
+ disabled: isDisabled
117
+ }
118
+ ),
119
+ setting.description && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "setting-field__description", children: setting.description })
120
+ ] });
121
+ };
122
+ var FieldInput = ({ setting, value, onChange, disabled }) => {
123
+ switch (setting.type) {
124
+ case "string":
125
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
126
+ StringField,
127
+ {
128
+ setting,
129
+ value,
130
+ onChange,
131
+ disabled
132
+ }
133
+ );
134
+ case "number":
135
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
136
+ NumberField,
137
+ {
138
+ setting,
139
+ value,
140
+ onChange,
141
+ disabled
142
+ }
143
+ );
144
+ case "select":
145
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
146
+ SelectField,
147
+ {
148
+ setting,
149
+ value,
150
+ onChange,
151
+ disabled
152
+ }
153
+ );
154
+ case "multi-select":
155
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
156
+ MultiSelectField,
157
+ {
158
+ setting,
159
+ value,
160
+ onChange,
161
+ disabled
162
+ }
163
+ );
164
+ case "color":
165
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
166
+ ColorField,
167
+ {
168
+ setting,
169
+ value,
170
+ onChange,
171
+ disabled
172
+ }
173
+ );
174
+ case "range":
175
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
176
+ RangeField,
177
+ {
178
+ setting,
179
+ value,
180
+ onChange,
181
+ disabled
182
+ }
183
+ );
184
+ case "date":
185
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
186
+ DateField,
187
+ {
188
+ setting,
189
+ value,
190
+ onChange,
191
+ disabled
192
+ }
193
+ );
194
+ case "timezone":
195
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
196
+ TimezoneField,
197
+ {
198
+ setting,
199
+ value,
200
+ onChange,
201
+ disabled
202
+ }
203
+ );
204
+ case "key-value":
205
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
206
+ KeyValueField,
207
+ {
208
+ setting,
209
+ value,
210
+ onChange,
211
+ disabled
212
+ }
213
+ );
214
+ case "repeater":
215
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
216
+ RepeaterField,
217
+ {
218
+ setting,
219
+ value,
220
+ onChange,
221
+ disabled
222
+ }
223
+ );
224
+ case "code":
225
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
226
+ CodeField,
227
+ {
228
+ setting,
229
+ value,
230
+ onChange,
231
+ disabled
232
+ }
233
+ );
234
+ case "markdown":
235
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
236
+ MarkdownSettingField,
237
+ {
238
+ setting,
239
+ value,
240
+ onChange,
241
+ disabled
242
+ }
243
+ );
244
+ default:
245
+ return null;
246
+ }
247
+ };
248
+ var StringField = ({ setting, value, onChange, disabled }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
249
+ "input",
250
+ {
251
+ type: "text",
252
+ className: "setting-field__input",
253
+ value: value ?? "",
254
+ placeholder: setting.placeholder,
255
+ disabled,
256
+ minLength: setting.validation?.minLength,
257
+ maxLength: setting.validation?.maxLength,
258
+ pattern: setting.validation?.pattern,
259
+ onChange: (e) => onChange(e.target.value)
260
+ }
261
+ );
262
+ var NumberField = ({ setting, value, onChange, disabled }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
263
+ "input",
264
+ {
265
+ type: "number",
266
+ className: "setting-field__input",
267
+ value: value ?? "",
268
+ placeholder: setting.placeholder,
269
+ disabled,
270
+ min: setting.validation?.min,
271
+ max: setting.validation?.max,
272
+ step: setting.validation?.step,
273
+ onChange: (e) => onChange(Number(e.target.value))
274
+ }
275
+ );
276
+ var BooleanField = ({ setting, value, onChange, disabled }) => {
277
+ const isOn = Boolean(value);
278
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "setting-field", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "setting-field__toggle-row", children: [
279
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "setting-field__toggle-text", children: [
280
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "setting-field__toggle-label", children: [
281
+ setting.label,
282
+ setting.validation?.required && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "setting-field__required", children: "*" })
283
+ ] }),
284
+ setting.description && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "setting-field__toggle-desc", children: setting.description })
285
+ ] }),
286
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
287
+ "button",
288
+ {
289
+ type: "button",
290
+ className: `setting-field__toggle ${isOn ? "setting-field__toggle--on" : "setting-field__toggle--off"}`,
291
+ disabled,
292
+ onClick: () => onChange(!isOn),
293
+ "aria-label": `Toggle ${setting.label}`,
294
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
295
+ "span",
296
+ {
297
+ className: `setting-field__toggle-knob ${isOn ? "setting-field__toggle-knob--on" : "setting-field__toggle-knob--off"}`
298
+ }
299
+ )
300
+ }
301
+ )
302
+ ] }) });
303
+ };
304
+ var SelectField = ({ setting, value, onChange, disabled }) => {
305
+ const [open, setOpen] = (0, import_react.useState)(false);
306
+ const [search, setSearch] = (0, import_react.useState)("");
307
+ const wrapperRef = (0, import_react.useRef)(null);
308
+ const inputRef = (0, import_react.useRef)(null);
309
+ const options = setting.options ?? [];
310
+ const selected = options.find((o) => String(o.value) === String(value));
311
+ const filtered = search ? options.filter((o) => o.label.toLowerCase().includes(search.toLowerCase())) : options;
312
+ (0, import_react.useEffect)(() => {
313
+ if (!open) return;
314
+ const handler = (e) => {
315
+ if (wrapperRef.current && !wrapperRef.current.contains(e.target)) {
316
+ setOpen(false);
317
+ setSearch("");
318
+ }
319
+ };
320
+ document.addEventListener("mousedown", handler);
321
+ return () => document.removeEventListener("mousedown", handler);
322
+ }, [open]);
323
+ const handleSelect = (optValue) => {
324
+ onChange(optValue);
325
+ setOpen(false);
326
+ setSearch("");
327
+ };
328
+ const handleKeyDown = (e) => {
329
+ if (e.key === "Escape") {
330
+ setOpen(false);
331
+ setSearch("");
332
+ }
333
+ };
334
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "setting-field__select-wrapper", ref: wrapperRef, children: [
335
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
336
+ "button",
337
+ {
338
+ type: "button",
339
+ className: `setting-field__select-trigger ${open ? "setting-field__select-trigger--open" : ""}`,
340
+ disabled,
341
+ onClick: () => {
342
+ setOpen(!open);
343
+ if (!open) setTimeout(() => inputRef.current?.focus(), 0);
344
+ },
345
+ "aria-haspopup": "listbox",
346
+ "aria-expanded": open,
347
+ children: [
348
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: selected ? "" : "setting-field__select-placeholder", children: selected?.label || setting.placeholder || "Select..." }),
349
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectChevron, {})
350
+ ]
351
+ }
352
+ ),
353
+ open && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "setting-field__select-dropdown", role: "listbox", onKeyDown: handleKeyDown, children: [
354
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "setting-field__select-search-box", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
355
+ "input",
356
+ {
357
+ ref: inputRef,
358
+ type: "text",
359
+ className: "setting-field__select-search",
360
+ placeholder: "Search...",
361
+ value: search,
362
+ onChange: (e) => setSearch(e.target.value)
363
+ }
364
+ ) }),
365
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "setting-field__select-options", children: [
366
+ filtered.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "setting-field__select-empty", children: "No results" }),
367
+ filtered.map((opt) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
368
+ "button",
369
+ {
370
+ type: "button",
371
+ className: `setting-field__select-option ${String(opt.value) === String(value) ? "setting-field__select-option--active" : ""}`,
372
+ onClick: () => handleSelect(opt.value),
373
+ role: "option",
374
+ "aria-selected": String(opt.value) === String(value),
375
+ children: [
376
+ opt.label,
377
+ opt.description && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "setting-field__select-option-desc", children: opt.description })
378
+ ]
379
+ },
380
+ String(opt.value)
381
+ ))
382
+ ] })
383
+ ] })
384
+ ] });
385
+ };
386
+ var SelectChevron = () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { className: "setting-field__select-chevron", width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "m6 9 6 6 6-6" }) });
387
+ var MultiSelectField = ({
388
+ setting,
389
+ value,
390
+ onChange,
391
+ disabled
392
+ }) => {
393
+ const selected = value ?? [];
394
+ const toggle = (optValue) => {
395
+ const next = selected.includes(optValue) ? selected.filter((v) => v !== optValue) : [...selected, optValue];
396
+ onChange(next);
397
+ };
398
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "setting-field__checkbox-group", children: setting.options?.map((opt) => {
399
+ const optStr = String(opt.value);
400
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
401
+ "label",
402
+ {
403
+ className: `setting-field__checkbox-card${disabled ? " setting-field__checkbox-card--disabled" : ""}${selected.includes(optStr) ? " setting-field__checkbox-card--checked" : ""}`,
404
+ children: [
405
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
406
+ "input",
407
+ {
408
+ type: "checkbox",
409
+ className: "setting-field__checkbox",
410
+ checked: selected.includes(optStr),
411
+ disabled,
412
+ onChange: () => toggle(optStr)
413
+ }
414
+ ),
415
+ opt.icon && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "setting-field__checkbox-icon", children: opt.icon }),
416
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "setting-field__checkbox-text", children: [
417
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "setting-field__checkbox-title", children: opt.label }),
418
+ opt.description && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "setting-field__checkbox-desc", children: opt.description })
419
+ ] })
420
+ ]
421
+ },
422
+ optStr
423
+ );
424
+ }) });
425
+ };
426
+ var ColorField = ({ setting, value, onChange, disabled }) => {
427
+ const swatches = setting.options ?? [];
428
+ const isGradient = (v) => v.includes("gradient") || v.includes(",");
429
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "setting-field__color-wrapper", children: [
430
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "setting-field__color-row", children: [
431
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
432
+ "input",
433
+ {
434
+ type: "color",
435
+ className: "setting-field__color-picker",
436
+ value: isGradient(value || "") ? "#000000" : value || "#000000",
437
+ disabled,
438
+ onChange: (e) => onChange(e.target.value)
439
+ }
440
+ ),
441
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
442
+ "input",
443
+ {
444
+ type: "text",
445
+ className: "setting-field__input setting-field__color-text",
446
+ value: value ?? "",
447
+ placeholder: setting.placeholder || "#000000",
448
+ disabled,
449
+ onChange: (e) => onChange(e.target.value)
450
+ }
451
+ )
452
+ ] }),
453
+ swatches.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "setting-field__color-swatches", children: swatches.map((swatch) => {
454
+ const swatchVal = String(swatch.value);
455
+ const active = swatchVal === value;
456
+ const gradient = isGradient(swatchVal);
457
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
458
+ "button",
459
+ {
460
+ type: "button",
461
+ className: `setting-field__color-swatch${active ? " setting-field__color-swatch--active" : ""}`,
462
+ style: {
463
+ background: gradient ? swatchVal : swatchVal
464
+ },
465
+ disabled,
466
+ onClick: () => onChange(swatchVal),
467
+ "aria-label": swatch.label,
468
+ title: swatch.label
469
+ },
470
+ swatchVal
471
+ );
472
+ }) })
473
+ ] });
474
+ };
475
+ var RangeField = ({ setting, value, onChange, disabled }) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "setting-field__range-row", children: [
476
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
477
+ "input",
478
+ {
479
+ type: "range",
480
+ className: "setting-field__range",
481
+ value: value ?? setting.validation?.min ?? 0,
482
+ min: setting.validation?.min ?? 0,
483
+ max: setting.validation?.max ?? 100,
484
+ step: setting.validation?.step ?? 1,
485
+ disabled,
486
+ onChange: (e) => onChange(Number(e.target.value))
487
+ }
488
+ ),
489
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "setting-field__range-value", children: value ?? setting.validation?.min ?? 0 })
490
+ ] });
491
+ var DateField = ({ setting, value, onChange, disabled }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
492
+ "input",
493
+ {
494
+ type: "date",
495
+ className: "setting-field__input setting-field__date",
496
+ value: value ?? "",
497
+ disabled,
498
+ onChange: (e) => onChange(e.target.value)
499
+ }
500
+ );
501
+ var TimezoneField = ({ setting, value, onChange, disabled }) => {
502
+ const tzSetting = {
503
+ ...setting,
504
+ type: "select",
505
+ options: TIMEZONE_OPTIONS,
506
+ placeholder: setting.placeholder || "Select timezone..."
507
+ };
508
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
509
+ SelectField,
510
+ {
511
+ setting: tzSetting,
512
+ value,
513
+ onChange: (v) => onChange(String(v)),
514
+ disabled
515
+ }
516
+ );
517
+ };
518
+ var KeyValueField = ({ setting, value, onChange, disabled }) => {
519
+ const pairs = value ?? {};
520
+ const entries = Object.entries(pairs);
521
+ const updateKey = (oldKey, newKey) => {
522
+ const next = {};
523
+ for (const [k, v] of entries) {
524
+ next[k === oldKey ? newKey : k] = v;
525
+ }
526
+ onChange(next);
527
+ };
528
+ const updateValue = (key, newVal) => {
529
+ onChange({ ...pairs, [key]: newVal });
530
+ };
531
+ const addPair = () => {
532
+ onChange({ ...pairs, "": "" });
533
+ };
534
+ const removePair = (key) => {
535
+ const next = { ...pairs };
536
+ delete next[key];
537
+ onChange(next);
538
+ };
539
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "setting-field__kv", children: [
540
+ entries.map(([k, v], i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "setting-field__kv-row", children: [
541
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
542
+ "input",
543
+ {
544
+ type: "text",
545
+ className: "setting-field__input setting-field__kv-key",
546
+ value: k,
547
+ placeholder: "Key",
548
+ disabled,
549
+ onChange: (e) => updateKey(k, e.target.value)
550
+ }
551
+ ),
552
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
553
+ "input",
554
+ {
555
+ type: "text",
556
+ className: "setting-field__input setting-field__kv-value",
557
+ value: v,
558
+ placeholder: "Value",
559
+ disabled,
560
+ onChange: (e) => updateValue(k, e.target.value)
561
+ }
562
+ ),
563
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
564
+ "button",
565
+ {
566
+ type: "button",
567
+ className: "setting-field__kv-remove",
568
+ disabled,
569
+ onClick: () => removePair(k),
570
+ "aria-label": "Remove pair",
571
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RemoveIcon, {})
572
+ }
573
+ )
574
+ ] }, i)),
575
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
576
+ "button",
577
+ {
578
+ type: "button",
579
+ className: "setting-field__kv-add",
580
+ disabled,
581
+ onClick: addPair,
582
+ children: [
583
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AddIcon, {}),
584
+ " Add pair"
585
+ ]
586
+ }
587
+ )
588
+ ] });
589
+ };
590
+ var RepeaterField = ({ setting, value, onChange, disabled }) => {
591
+ const rows = value ?? [];
592
+ const columns = setting.repeaterColumns ?? [{ key: "value", label: "Value" }];
593
+ const addRow = () => {
594
+ const newRow = { id: crypto.randomUUID() };
595
+ for (const col of columns) newRow[col.key] = "";
596
+ onChange([...rows, newRow]);
597
+ };
598
+ const removeRow = (id) => {
599
+ onChange(rows.filter((r) => r.id !== id));
600
+ };
601
+ const updateCell = (id, colKey, val) => {
602
+ onChange(rows.map((r) => r.id === id ? { ...r, [colKey]: val } : r));
603
+ };
604
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "setting-field__repeater", children: [
605
+ columns.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "setting-field__repeater-header", children: [
606
+ columns.map((col) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "setting-field__repeater-col-label", children: col.label }, col.key)),
607
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "setting-field__repeater-col-action" })
608
+ ] }),
609
+ rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "setting-field__repeater-row", children: [
610
+ columns.map((col) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
611
+ "input",
612
+ {
613
+ type: "text",
614
+ className: "setting-field__input setting-field__repeater-cell",
615
+ value: row[col.key] ?? "",
616
+ placeholder: col.placeholder || col.label,
617
+ disabled,
618
+ onChange: (e) => updateCell(row.id, col.key, e.target.value)
619
+ },
620
+ col.key
621
+ )),
622
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
623
+ "button",
624
+ {
625
+ type: "button",
626
+ className: "setting-field__repeater-remove",
627
+ disabled,
628
+ onClick: () => removeRow(row.id),
629
+ "aria-label": "Remove row",
630
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RemoveIcon, {})
631
+ }
632
+ )
633
+ ] }, row.id)),
634
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
635
+ "button",
636
+ {
637
+ type: "button",
638
+ className: "setting-field__repeater-add",
639
+ disabled,
640
+ onClick: addRow,
641
+ children: [
642
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AddIcon, {}),
643
+ " Add row"
644
+ ]
645
+ }
646
+ )
647
+ ] });
648
+ };
649
+ var CodeField = ({ setting, value, onChange, disabled }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
650
+ "textarea",
651
+ {
652
+ className: "setting-field__input setting-field__code",
653
+ value: value ?? "",
654
+ placeholder: setting.placeholder,
655
+ disabled,
656
+ rows: 8,
657
+ spellCheck: false,
658
+ onChange: (e) => onChange(e.target.value)
659
+ }
660
+ );
661
+ var MarkdownSettingField = ({ setting, value, onChange, disabled }) => {
662
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
663
+ "textarea",
664
+ {
665
+ className: "setting-field__input setting-field__code",
666
+ value: value ?? "",
667
+ placeholder: setting.placeholder || "Write markdown...",
668
+ disabled,
669
+ rows: 8,
670
+ onChange: (e) => onChange(e.target.value)
671
+ }
672
+ );
673
+ };
674
+ var AddIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M12 5v14M5 12h14" }) });
675
+ var RemoveIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M18 6 6 18M6 6l12 12" }) });
676
+
677
+ // src/SettingsForm/SettingsGroup.tsx
678
+ var import_react2 = require("react");
679
+ var import_jsx_runtime2 = require("react/jsx-runtime");
680
+ var SettingsGroup = ({
681
+ group,
682
+ settings,
683
+ values,
684
+ onChange,
685
+ disabled
686
+ }) => {
687
+ const [collapsed, setCollapsed] = (0, import_react2.useState)(group.collapsed ?? false);
688
+ const sortedSettings = (0, import_react2.useMemo)(
689
+ () => [...settings].sort((a, b) => a.order - b.order),
690
+ [settings]
691
+ );
692
+ const groupClasses = [
693
+ "settings-group",
694
+ collapsed ? "settings-group--collapsed" : ""
695
+ ].filter(Boolean).join(" ");
696
+ const headerClasses = [
697
+ "settings-group__header",
698
+ group.collapsible ? "settings-group__header--collapsible" : ""
699
+ ].filter(Boolean).join(" ");
700
+ const handleHeaderClick = () => {
701
+ if (group.collapsible) {
702
+ setCollapsed((prev) => !prev);
703
+ }
704
+ };
705
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: groupClasses, children: [
706
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
707
+ "div",
708
+ {
709
+ className: headerClasses,
710
+ onClick: handleHeaderClick,
711
+ role: group.collapsible ? "button" : void 0,
712
+ tabIndex: group.collapsible ? 0 : void 0,
713
+ onKeyDown: group.collapsible ? (e) => {
714
+ if (e.key === "Enter" || e.key === " ") {
715
+ e.preventDefault();
716
+ handleHeaderClick();
717
+ }
718
+ } : void 0,
719
+ children: [
720
+ group.icon && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "settings-group__icon", children: group.icon }),
721
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "settings-group__header-text", children: [
722
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { className: "settings-group__title", children: group.label }),
723
+ group.description && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "settings-group__description", children: group.description })
724
+ ] }),
725
+ group.collapsible && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ChevronIcon, { collapsed })
726
+ ]
727
+ }
728
+ ),
729
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "settings-group__body", children: sortedSettings.map((setting) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
730
+ SettingField,
731
+ {
732
+ setting,
733
+ value: values[setting.key] ?? setting.default,
734
+ onChange,
735
+ disabled
736
+ },
737
+ setting.key
738
+ )) })
739
+ ] });
740
+ };
741
+ function ChevronIcon({ collapsed }) {
742
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
743
+ "svg",
744
+ {
745
+ className: `settings-group__chevron ${collapsed ? "settings-group__chevron--collapsed" : ""}`,
746
+ width: "16",
747
+ height: "16",
748
+ viewBox: "0 0 24 24",
749
+ fill: "none",
750
+ stroke: "currentColor",
751
+ strokeWidth: 2,
752
+ strokeLinecap: "round",
753
+ strokeLinejoin: "round",
754
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "m6 9 6 6 6-6" })
755
+ }
756
+ );
757
+ }
758
+
759
+ // src/SettingsForm/SettingGroupShell.tsx
760
+ var import_jsx_runtime3 = require("react/jsx-runtime");
761
+ var SettingGroupShell = ({ group, children }) => {
762
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "settings-group", children: [
763
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "settings-group__header", children: [
764
+ group.icon && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "settings-group__icon", children: group.icon }),
765
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "settings-group__header-text", children: [
766
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h3", { className: "settings-group__title", children: group.label }),
767
+ group.description && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "settings-group__description", children: group.description })
768
+ ] })
769
+ ] }),
770
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "settings-group__body", children })
771
+ ] });
772
+ };
773
+
774
+ // src/SettingsForm/SettingsForm.tsx
775
+ var import_react3 = require("react");
776
+ var import_jsx_runtime4 = require("react/jsx-runtime");
777
+ var SettingsForm = ({
778
+ state,
779
+ values,
780
+ onChange,
781
+ activeGroup,
782
+ disabled,
783
+ className,
784
+ emptyState
785
+ }) => {
786
+ const visibleGroups = (0, import_react3.useMemo)(() => {
787
+ const sorted = [...state.groups].sort((a, b) => a.order - b.order);
788
+ if (activeGroup) {
789
+ return sorted.filter((g) => g.id === activeGroup);
790
+ }
791
+ return sorted;
792
+ }, [state.groups, activeGroup]);
793
+ const settingsByGroup = (0, import_react3.useMemo)(() => {
794
+ const map = {};
795
+ for (const setting of state.settings) {
796
+ if (setting.hidden) continue;
797
+ if (!map[setting.group]) {
798
+ map[setting.group] = [];
799
+ }
800
+ map[setting.group].push(setting);
801
+ }
802
+ return map;
803
+ }, [state.settings]);
804
+ const formClasses = ["settings-form", className].filter(Boolean).join(" ");
805
+ const hasVisibleSettings = visibleGroups.some(
806
+ (g) => settingsByGroup[g.id] && settingsByGroup[g.id].length > 0
807
+ );
808
+ if (!hasVisibleSettings) {
809
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: formClasses, children: emptyState ?? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "settings-form__empty", children: [
810
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { className: "settings-form__empty-icon", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
811
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z" }),
812
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "12", cy: "12", r: "3" })
813
+ ] }),
814
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "settings-form__empty-title", children: "No settings available" }),
815
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "settings-form__empty-desc", children: "There are no configurable settings for this section." })
816
+ ] }) });
817
+ }
818
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: formClasses, children: visibleGroups.map((group) => {
819
+ const groupSettings = settingsByGroup[group.id];
820
+ if (!groupSettings || groupSettings.length === 0) return null;
821
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
822
+ SettingsGroup,
823
+ {
824
+ group,
825
+ settings: groupSettings,
826
+ values,
827
+ onChange,
828
+ disabled
829
+ },
830
+ group.id
831
+ );
832
+ }) });
833
+ };
834
+
835
+ // src/SettingsNav/SettingsNav.tsx
836
+ var import_jsx_runtime5 = require("react/jsx-runtime");
837
+ var SettingsNav = ({
838
+ title = "Settings",
839
+ subtitle = "Customize your workspace",
840
+ items,
841
+ activeId,
842
+ searchQuery = "",
843
+ onSearchChange,
844
+ onSelect,
845
+ showSearch = true
846
+ }) => {
847
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "settings-nav", children: [
848
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "settings-nav__header", children: [
849
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h2", { className: "settings-nav__title", children: title }),
850
+ subtitle && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "settings-nav__subtitle", children: subtitle })
851
+ ] }),
852
+ showSearch && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "settings-nav__search", children: [
853
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SearchIcon, {}),
854
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
855
+ "input",
856
+ {
857
+ type: "text",
858
+ placeholder: "Search settings...",
859
+ value: searchQuery,
860
+ onChange: (e) => onSearchChange?.(e.target.value),
861
+ className: "settings-nav__search-input"
862
+ }
863
+ )
864
+ ] }),
865
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("nav", { className: "settings-nav__list", children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
866
+ "button",
867
+ {
868
+ onClick: () => onSelect(item.id),
869
+ className: `settings-nav__item ${activeId === item.id ? "settings-nav__item--active" : ""}`,
870
+ children: [
871
+ item.icon && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "settings-nav__icon", children: item.icon }),
872
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: item.label })
873
+ ]
874
+ },
875
+ item.id
876
+ )) })
877
+ ] });
878
+ };
879
+ function SearchIcon() {
880
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
881
+ "svg",
882
+ {
883
+ className: "settings-nav__search-icon",
884
+ width: "16",
885
+ height: "16",
886
+ viewBox: "0 0 24 24",
887
+ fill: "none",
888
+ stroke: "currentColor",
889
+ strokeWidth: 2,
890
+ strokeLinecap: "round",
891
+ strokeLinejoin: "round",
892
+ children: [
893
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("circle", { cx: "11", cy: "11", r: "8" }),
894
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "m21 21-4.3-4.3" })
895
+ ]
896
+ }
897
+ );
898
+ }
899
+
900
+ // src/validate.ts
901
+ function validateSetting(value, validation) {
902
+ if (!validation) return null;
903
+ if (validation.required && (value === void 0 || value === null || value === "")) {
904
+ return validation.message ?? "This field is required";
905
+ }
906
+ if (typeof value === "string") {
907
+ if (validation.min !== void 0 && value.length < validation.min) {
908
+ return validation.message ?? `Must be at least ${validation.min} characters`;
909
+ }
910
+ if (validation.max !== void 0 && value.length > validation.max) {
911
+ return validation.message ?? `Must be at most ${validation.max} characters`;
912
+ }
913
+ if (validation.pattern && !new RegExp(validation.pattern).test(value)) {
914
+ return validation.message ?? "Invalid format";
915
+ }
916
+ }
917
+ if (typeof value === "number") {
918
+ if (validation.min !== void 0 && value < validation.min) {
919
+ return validation.message ?? `Must be at least ${validation.min}`;
920
+ }
921
+ if (validation.max !== void 0 && value > validation.max) {
922
+ return validation.message ?? `Must be at most ${validation.max}`;
923
+ }
924
+ }
925
+ return null;
926
+ }
927
+ // Annotate the CommonJS export names for ESM import in node:
928
+ 0 && (module.exports = {
929
+ SettingField,
930
+ SettingGroupShell,
931
+ SettingsForm,
932
+ SettingsGroup,
933
+ SettingsNav,
934
+ validateSetting
935
+ });