@eclipse-lyra/extension-settings-tree 0.7.57 → 0.7.59

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.
@@ -0,0 +1,792 @@
1
+ import { LyraPart, TOOLBAR_MAIN_RIGHT, TOPIC_SETTINGS_CHANGED, appSettings, commandRegistry, contributionRegistry, editorRegistry, promptDialog, subscribe } from "@eclipse-lyra/core";
2
+ import { css, html } from "lit";
3
+ import { customElement, property, state } from "lit/decorators.js";
4
+ import { createRef, ref } from "lit/directives/ref.js";
5
+ import { repeat } from "lit/directives/repeat.js";
6
+ import _decorate from "@oxc-project/runtime/helpers/decorate";
7
+ //#region src/settings-tree.ts
8
+ var _LyraSettingsTree;
9
+ var LyraSettingsTree = class LyraSettingsTree extends LyraPart {
10
+ static {
11
+ _LyraSettingsTree = this;
12
+ }
13
+ constructor(..._args) {
14
+ super(..._args);
15
+ this.settings = {};
16
+ this.categoryTree = [];
17
+ this.selectedPath = null;
18
+ this.detailRows = [];
19
+ this.detailTitle = "";
20
+ this.searchQuery = "";
21
+ this.detailForPath = null;
22
+ this.treeRef = createRef();
23
+ }
24
+ async doInitUI() {
25
+ await this.loadSettings();
26
+ subscribe(TOPIC_SETTINGS_CHANGED, () => this.loadSettings());
27
+ }
28
+ async loadSettings() {
29
+ if (this.isDirty()) return;
30
+ this.settings = await appSettings.getAll() ?? {};
31
+ this.buildCategoryTree();
32
+ if (this.selectedPath) this.updateDetailForPath(this.selectedPath);
33
+ }
34
+ buildCategoryTree() {
35
+ const categories = appSettings.getCategories();
36
+ const schemaIds = new Set(categories.map((c) => c.id));
37
+ const nodes = [];
38
+ for (const cat of categories) {
39
+ const childSchema = cat.schema?.properties;
40
+ const value = this.settings[cat.id];
41
+ const children = this.buildChildNodes(`${cat.id}`, childSchema, value);
42
+ const label = cat.schema?.title ?? cat.label;
43
+ if (this.matchesSearch(label, cat.id)) nodes.push({
44
+ path: cat.id,
45
+ label,
46
+ children,
47
+ expanded: true
48
+ });
49
+ }
50
+ for (const key of Object.keys(this.settings)) {
51
+ if (schemaIds.has(key)) continue;
52
+ const value = this.settings[key];
53
+ const children = this.buildChildNodes(key, void 0, value);
54
+ if (this.matchesSearch(key, key)) nodes.push({
55
+ path: key,
56
+ label: key,
57
+ children,
58
+ expanded: true
59
+ });
60
+ }
61
+ this.categoryTree = nodes;
62
+ }
63
+ buildChildNodes(parentPath, schemaProps, value) {
64
+ const children = [];
65
+ if (schemaProps) for (const [k, s] of Object.entries(schemaProps)) {
66
+ const path = `${parentPath}.${k}`;
67
+ const label = s?.title ?? k;
68
+ const subValue = _LyraSettingsTree.isRecord(value) ? value[k] : void 0;
69
+ const subChildren = this.buildChildNodes(path, s?.properties, subValue);
70
+ if (this.matchesSearch(label, path)) children.push({
71
+ path,
72
+ label,
73
+ children: subChildren,
74
+ expanded: false
75
+ });
76
+ }
77
+ if (_LyraSettingsTree.isRecord(value) && !schemaProps) for (const [k, v] of Object.entries(value)) {
78
+ const path = `${parentPath}.${k}`;
79
+ const subChildren = this.buildChildNodes(path, void 0, v);
80
+ if (this.matchesSearch(k, path)) children.push({
81
+ path,
82
+ label: k,
83
+ children: subChildren,
84
+ expanded: false
85
+ });
86
+ }
87
+ return children;
88
+ }
89
+ matchesSearch(label, path) {
90
+ if (!this.searchQuery.trim()) return true;
91
+ const q = this.searchQuery.toLowerCase();
92
+ return label.toLowerCase().includes(q) || path.toLowerCase().includes(q);
93
+ }
94
+ static isRecord(v) {
95
+ return v !== null && typeof v === "object" && !Array.isArray(v);
96
+ }
97
+ refreshAfterEdit() {
98
+ this.markDirty(true);
99
+ if (this.selectedPath) this.updateDetailForPath(this.selectedPath);
100
+ }
101
+ getValueAtPath(path) {
102
+ const parts = path.split(".").filter(Boolean);
103
+ if (parts.length === 0) return this.settings;
104
+ let current = this.settings;
105
+ for (const part of parts) {
106
+ if (current === null || current === void 0 || typeof current !== "object") return void 0;
107
+ current = current[part];
108
+ }
109
+ return current;
110
+ }
111
+ setAtInMemory(path, value) {
112
+ const parts = path.split(".").filter(Boolean);
113
+ if (parts.length === 0) return;
114
+ this.settings = this.deepSet(this.settings, parts, value);
115
+ }
116
+ deepSet(obj, pathParts, value) {
117
+ if (pathParts.length === 1) {
118
+ const key = pathParts[0];
119
+ if (Array.isArray(obj)) {
120
+ const idx = parseInt(key, 10);
121
+ if (isNaN(idx) || idx < 0) return obj;
122
+ const arr = [...obj];
123
+ arr[idx] = value;
124
+ return arr;
125
+ }
126
+ return {
127
+ ...obj,
128
+ [key]: value
129
+ };
130
+ }
131
+ const [head, ...rest] = pathParts;
132
+ if (Array.isArray(obj)) {
133
+ const idx = parseInt(head, 10);
134
+ if (isNaN(idx) || idx < 0) return obj;
135
+ const arr = [...obj];
136
+ const current = arr[idx];
137
+ const next = current !== null && typeof current === "object" ? current : {};
138
+ arr[idx] = this.deepSet(next, rest, value);
139
+ return arr;
140
+ }
141
+ const o = obj;
142
+ const current = o[head];
143
+ const next = current !== null && typeof current === "object" ? current : {};
144
+ return {
145
+ ...o,
146
+ [head]: this.deepSet(next, rest, value)
147
+ };
148
+ }
149
+ getArrayItemLabel(item, index) {
150
+ if (_LyraSettingsTree.isRecord(item)) {
151
+ if (typeof item.id === "string") return item.id;
152
+ if (typeof item.name === "string") return item.name;
153
+ }
154
+ return `Item ${index}`;
155
+ }
156
+ async updateDetailForPath(path) {
157
+ const containerSchema = appSettings.getSchemaForSettingKey(path);
158
+ const containerValue = this.getValueAtPath(path);
159
+ const valueObj = _LyraSettingsTree.isRecord(containerValue) ? containerValue : null;
160
+ const isArray = Array.isArray(containerValue);
161
+ let rows;
162
+ if (isArray) {
163
+ const arr = containerValue;
164
+ const flat = [];
165
+ arr.forEach((item, index) => {
166
+ const subPath = `${path}.${index}`;
167
+ const groupLabel = this.getArrayItemLabel(item, index);
168
+ if (_LyraSettingsTree.isRecord(item)) Object.keys(item).forEach((k) => {
169
+ flat.push({
170
+ key: k,
171
+ path: `${subPath}.${k}`,
172
+ title: k,
173
+ description: "",
174
+ schema: void 0,
175
+ value: item[k],
176
+ parentPath: subPath,
177
+ groupLabel
178
+ });
179
+ });
180
+ else flat.push({
181
+ key: String(index),
182
+ path: subPath,
183
+ title: groupLabel,
184
+ description: `Array element ${index}.`,
185
+ schema: void 0,
186
+ value: item,
187
+ parentPath: path,
188
+ groupLabel
189
+ });
190
+ });
191
+ rows = flat;
192
+ } else if (valueObj !== null) {
193
+ const schemaProps = containerSchema?.properties;
194
+ rows = (schemaProps ? Object.keys(schemaProps) : Object.keys(valueObj)).map((key) => {
195
+ const subPath = `${path}.${key}`;
196
+ const propSchema = schemaProps?.[key];
197
+ const value = valueObj[key];
198
+ return {
199
+ key,
200
+ path: subPath,
201
+ title: propSchema?.title ?? key,
202
+ description: propSchema?.description ?? "",
203
+ schema: propSchema,
204
+ value,
205
+ parentPath: path
206
+ };
207
+ });
208
+ } else if (containerValue !== void 0 && containerValue !== null && typeof containerValue !== "object") {
209
+ const segment = path.split(".").pop() ?? path;
210
+ rows = [{
211
+ key: segment,
212
+ path,
213
+ title: segment,
214
+ description: "Edit value (no schema).",
215
+ schema: void 0,
216
+ value: containerValue
217
+ }];
218
+ } else rows = [];
219
+ rows.forEach((r) => {
220
+ if (r.title === void 0) r.title = r.key;
221
+ });
222
+ if (this.selectedPath !== path) return;
223
+ const category = appSettings.getCategories().find((c) => c.id === path);
224
+ this.detailForPath = path;
225
+ this.detailTitle = category?.label ?? containerSchema?.title ?? path;
226
+ this.detailRows = rows;
227
+ }
228
+ selectPath(path) {
229
+ this.selectedPath = path;
230
+ this.updateDetailForPath(path);
231
+ }
232
+ updated(changedProperties) {
233
+ super.updated?.(changedProperties);
234
+ if (changedProperties.has("selectedPath") && this.selectedPath !== this.detailForPath) if (this.selectedPath) this.updateDetailForPath(this.selectedPath);
235
+ else {
236
+ this.detailForPath = null;
237
+ this.detailRows = [];
238
+ this.detailTitle = "";
239
+ }
240
+ }
241
+ setRowValue(row, value) {
242
+ this.setAtInMemory(row.path, value);
243
+ this.refreshAfterEdit();
244
+ }
245
+ setRowValueFromJson(row, raw) {
246
+ try {
247
+ this.setRowValue(row, JSON.parse(raw));
248
+ } catch {
249
+ this.refreshAfterEdit();
250
+ }
251
+ }
252
+ renameKey(parentPath, oldKey, newKey) {
253
+ if (newKey.trim() === "" || newKey === oldKey) return;
254
+ const parent = this.getValueAtPath(parentPath);
255
+ if (!_LyraSettingsTree.isRecord(parent)) return;
256
+ const value = parent[oldKey];
257
+ const updated = { ...parent };
258
+ delete updated[oldKey];
259
+ updated[newKey.trim()] = value;
260
+ this.setAtInMemory(parentPath, updated);
261
+ this.refreshAfterEdit();
262
+ }
263
+ deleteKey(parentPath, key) {
264
+ const parent = this.getValueAtPath(parentPath);
265
+ if (!_LyraSettingsTree.isRecord(parent)) return;
266
+ const updated = { ...parent };
267
+ delete updated[key];
268
+ this.setAtInMemory(parentPath, updated);
269
+ this.refreshAfterEdit();
270
+ }
271
+ async addKeyToGroup(groupParentPath) {
272
+ const key = await promptDialog("New key name:", "");
273
+ if (!key?.trim()) return;
274
+ this.setAtInMemory(`${groupParentPath}.${key.trim()}`, null);
275
+ this.refreshAfterEdit();
276
+ }
277
+ deleteTopLevelKey(key) {
278
+ if (!(key in this.settings)) return;
279
+ const updated = { ...this.settings };
280
+ delete updated[key];
281
+ this.settings = updated;
282
+ this.markDirty(true);
283
+ if (this.selectedPath === key) {
284
+ this.selectedPath = null;
285
+ this.detailForPath = null;
286
+ this.detailRows = [];
287
+ this.detailTitle = "";
288
+ }
289
+ this.buildCategoryTree();
290
+ if (this.selectedPath) this.updateDetailForPath(this.selectedPath);
291
+ }
292
+ renderDetailControl(row) {
293
+ const schema = row.schema;
294
+ const type = schema?.type;
295
+ const enumValues = schema?.enum;
296
+ const current = row.value;
297
+ if (enumValues && Array.isArray(enumValues) && enumValues.length > 0) {
298
+ const options = enumValues.map((v) => String(v));
299
+ return html`
300
+ <wa-select
301
+ value=${current !== void 0 && current !== null ? String(current) : schema?.default !== void 0 ? String(schema.default) : options[0]}
302
+ @change=${(e) => this.setRowValue(row, e.target.value)}
303
+ size="small">
304
+ ${options.map((opt) => html`<wa-option value=${opt}>${opt}</wa-option>`)}
305
+ </wa-select>
306
+ `;
307
+ }
308
+ if (type === "boolean") return html`
309
+ <wa-switch
310
+ ?checked=${current === true || current === void 0 && schema?.default === true}
311
+ @change=${(e) => this.setRowValue(row, e.target.checked)}
312
+ size="small">
313
+ </wa-switch>
314
+ `;
315
+ if (type === "number") {
316
+ const num = typeof current === "number" ? current : typeof schema?.default === "number" ? schema.default : 0;
317
+ return html`
318
+ <wa-number-input
319
+ .value=${String(num)}
320
+ @change=${(e) => this.setRowValue(row, Number(e.target.value))}
321
+ size="small">
322
+ </wa-number-input>
323
+ `;
324
+ }
325
+ const isObjectOrArray = current !== null && typeof current === "object";
326
+ if (isObjectOrArray && !schema && _LyraSettingsTree.isRecord(current)) return this.renderObjectFields(row.path, current, 0);
327
+ if (isObjectOrArray) return html`
328
+ <wa-textarea
329
+ .value=${JSON.stringify(current, null, 2)}
330
+ @blur=${(e) => this.setRowValueFromJson(row, e.target.value)}
331
+ rows="3">
332
+ </wa-textarea>
333
+ `;
334
+ return this.renderPrimitiveControl(row, current);
335
+ }
336
+ renderPrimitiveControl(row, current) {
337
+ const str = current !== void 0 && current !== null ? String(current) : "";
338
+ if (typeof current === "boolean") return html`
339
+ <wa-switch
340
+ ?checked=${current}
341
+ @change=${(e) => this.setRowValue(row, e.target.checked)}
342
+ size="small">
343
+ </wa-switch>
344
+ `;
345
+ if (typeof current === "number") return html`
346
+ <wa-number-input
347
+ .value=${String(current)}
348
+ @change=${(e) => this.setRowValue(row, Number(e.target.value))}
349
+ size="small">
350
+ </wa-number-input>
351
+ `;
352
+ return html`
353
+ <wa-input
354
+ .value=${str}
355
+ @input=${(e) => this.setRowValue(row, e.target.value)}
356
+ size="small">
357
+ </wa-input>
358
+ `;
359
+ }
360
+ renderObjectFields(parentPath, obj, depth) {
361
+ if (depth >= 2) {
362
+ const str = JSON.stringify(obj, null, 2);
363
+ const row = {
364
+ key: "",
365
+ path: parentPath,
366
+ title: "",
367
+ description: "",
368
+ value: obj
369
+ };
370
+ return html`
371
+ <wa-textarea
372
+ .value=${str}
373
+ @blur=${(e) => this.setRowValueFromJson(row, e.target.value)}
374
+ rows="3">
375
+ </wa-textarea>
376
+ `;
377
+ }
378
+ return html`
379
+ <div class="nested-object" >
380
+ ${Object.entries(obj).map(([k, v]) => {
381
+ const subPath = `${parentPath}.${k}`;
382
+ const subRow = {
383
+ key: k,
384
+ path: subPath,
385
+ title: k,
386
+ description: "",
387
+ value: v,
388
+ parentPath
389
+ };
390
+ return html`
391
+ <div class="nested-row">
392
+ <wa-input
393
+ .value=${k}
394
+ size="small"
395
+ style="width: 140px;"
396
+ @blur=${(e) => {
397
+ const newKey = e.target.value.trim();
398
+ if (newKey && newKey !== k) this.renameKey(parentPath, k, newKey);
399
+ }}>
400
+ </wa-input>
401
+ <span class="nested-sep">:</span>
402
+ ${_LyraSettingsTree.isRecord(v) ? this.renderObjectFields(subPath, v, depth + 1) : Array.isArray(v) ? html`
403
+ <wa-textarea
404
+ .value=${JSON.stringify(v, null, 2)}
405
+ rows="2"
406
+ style="min-width: 200px;"
407
+ @blur=${(e) => this.setRowValueFromJson(subRow, e.target.value)}>
408
+ </wa-textarea>
409
+ ` : this.renderPrimitiveControl(subRow, v)}
410
+ </div>
411
+ `;
412
+ })}
413
+ </div>
414
+ `;
415
+ }
416
+ renderDetailPanel() {
417
+ if (!this.selectedPath) return html`
418
+ <div class="detail-placeholder">
419
+ <wa-icon name="gear"></wa-icon>
420
+ <p>Select a category from the tree</p>
421
+ </div>
422
+ `;
423
+ if (this.detailForPath !== this.selectedPath) return html`
424
+ <div class="detail-panel">
425
+ <div class="detail-heading-row">
426
+ <h2 class="detail-heading">${this.selectedPath}</h2>
427
+ </div>
428
+ <div class="detail-placeholder">
429
+ <p>Loading…</p>
430
+ </div>
431
+ </div>
432
+ `;
433
+ const filteredRows = this.detailRows.filter((row) => {
434
+ if (!this.searchQuery.trim()) return true;
435
+ const q = this.searchQuery.toLowerCase();
436
+ return row.title.toLowerCase().includes(q) || row.description.toLowerCase().includes(q);
437
+ });
438
+ const containerValue = this.selectedPath ? this.getValueAtPath(this.selectedPath) : void 0;
439
+ const isObjectContainer = _LyraSettingsTree.isRecord(containerValue);
440
+ const isTopLevel = this.selectedPath !== null && !this.selectedPath.includes(".");
441
+ const canAddToSelection = isObjectContainer || isTopLevel;
442
+ return html`
443
+ <div class="detail-panel">
444
+ <div class="detail-heading-row">
445
+ <h2 class="detail-heading">${this.detailTitle}</h2>
446
+ ${canAddToSelection ? html`
447
+ <lyra-command size="small" icon="plus" title="Add setting" .action=${() => this.addKeyToGroup(this.selectedPath)}>Add</lyra-command>
448
+ ` : ""}
449
+ ${isTopLevel ? html`
450
+ <lyra-command size="small" icon="trash" title="Delete category" .action=${() => this.deleteTopLevelKey(this.selectedPath)}></lyra-command>
451
+ ` : ""}
452
+ </div>
453
+ <wa-scroller class="detail-scroller" orientation="vertical">
454
+ ${filteredRows.length === 0 ? html`
455
+ <div class="detail-empty">
456
+ ${this.detailRows.length === 0 ? html`<p>No settings in this category. Add keys in the JSON file or register a JSON Schema for this path.</p>` : html`<p>No matching settings for the current search.</p>`}
457
+ </div>
458
+ ` : repeat(filteredRows, (row) => row.path, (row, index) => {
459
+ const prev = index > 0 ? filteredRows[index - 1] : null;
460
+ const showGroup = row.groupLabel && prev?.groupLabel !== row.groupLabel;
461
+ const inGroup = !!row.groupLabel;
462
+ return html`
463
+ ${showGroup ? html`
464
+ <div class="detail-group-header">
465
+ <span>${row.groupLabel}</span>
466
+ <lyra-command size="small" icon="plus" title="Add setting" .action=${() => this.addKeyToGroup(row.parentPath)}>Add</lyra-command>
467
+ </div>
468
+ ` : ""}
469
+ <div class="setting-row ${inGroup ? "setting-row-in-group" : ""}">
470
+ <div class="setting-meta">
471
+ ${row.parentPath != null ? html`
472
+ <wa-input
473
+ .value=${row.key}
474
+ size="small"
475
+ class="editable-key setting-control-key"
476
+ @blur=${(e) => {
477
+ const newKey = e.target.value.trim();
478
+ if (newKey && newKey !== row.key) this.renameKey(row.parentPath, row.key, newKey);
479
+ }}>
480
+ </wa-input>
481
+ ` : html`<span class="setting-title">${row.title}</span>`}
482
+ ${row.description ? html`<p class="setting-description">${row.description}</p>` : ""}
483
+ </div>
484
+ <div class="setting-control">${this.renderDetailControl(row)}</div>
485
+ ${row.parentPath != null ? html`
486
+ <lyra-command size="small" icon="trash" title="Delete setting" .action=${() => this.deleteKey(row.parentPath, row.key)}></lyra-command>
487
+ ` : ""}
488
+ </div>
489
+ `;
490
+ })}
491
+ </wa-scroller>
492
+ </div>
493
+ `;
494
+ }
495
+ renderCategoryNode(node) {
496
+ const hasChildren = node.children.length > 0;
497
+ const isSelected = this.selectedPath === node.path;
498
+ return html`
499
+ <wa-tree-item ?expanded=${node.expanded}>
500
+ <div
501
+ class="tree-node-content ${isSelected ? "selected" : ""}"
502
+ @click=${() => this.selectPath(node.path)}
503
+ role="button"
504
+ tabindex="0"
505
+ @keydown=${(e) => e.key === "Enter" && this.selectPath(node.path)}>
506
+ <span class="tree-node-label">${node.label}</span>
507
+ </div>
508
+ ${hasChildren ? node.children.map((child) => this.renderCategoryNode(child)) : ""}
509
+ </wa-tree-item>
510
+ `;
511
+ }
512
+ setAllExpanded(expanded) {
513
+ const tree = this.treeRef.value;
514
+ if (tree) tree.querySelectorAll("wa-tree-item").forEach((item) => {
515
+ item.expanded = expanded;
516
+ });
517
+ this.categoryTree = this.categoryTree.map((n) => this.setExpanded(n, expanded));
518
+ this.requestUpdate();
519
+ }
520
+ setExpanded(node, expanded) {
521
+ return {
522
+ ...node,
523
+ expanded,
524
+ children: node.children.map((c) => this.setExpanded(c, expanded))
525
+ };
526
+ }
527
+ async addKey() {
528
+ const key = await promptDialog("Enter new top-level key name:", "");
529
+ if (!key?.trim()) return;
530
+ this.setAtInMemory(key.trim(), null);
531
+ this.markDirty(true);
532
+ this.buildCategoryTree();
533
+ }
534
+ save() {
535
+ appSettings.setAll(this.settings).then(() => {
536
+ this.markDirty(false);
537
+ }).catch((err) => {
538
+ console.error("Failed to save settings:", err);
539
+ });
540
+ }
541
+ renderToolbar() {
542
+ return html`
543
+ <wa-input
544
+ placeholder="Search settings"
545
+ .value=${this.searchQuery}
546
+ @input=${(e) => {
547
+ this.searchQuery = e.target.value;
548
+ this.buildCategoryTree();
549
+ }}
550
+ size="small"
551
+ class="toolbar-search">
552
+ </wa-input>
553
+ <lyra-command size="small" icon="plus" title="Add Key" .action=${() => this.addKey()}>Add Key</lyra-command>
554
+ <lyra-command size="small" icon="chevron-down" title="Expand All" .action=${() => this.setAllExpanded(true)}>Expand All</lyra-command>
555
+ <lyra-command size="small" icon="chevron-right" title="Collapse All" .action=${() => this.setAllExpanded(false)}>Collapse All</lyra-command>
556
+ `;
557
+ }
558
+ renderContent() {
559
+ return html`
560
+ <div class="settings-editor-container">
561
+ <wa-split-panel position="25" class="split-panel-fill">
562
+ <div slot="start" class="left-panel">
563
+ <wa-scroller class="tree-scroller" orientation="vertical">
564
+ ${this.categoryTree.length === 0 ? html`
565
+ <div class="empty-state">
566
+ <wa-icon name="gear"></wa-icon>
567
+ <p>No settings.</p>
568
+ </div>
569
+ ` : html`
570
+ <wa-tree ${ref(this.treeRef)}>
571
+ ${this.categoryTree.map((node) => this.renderCategoryNode(node))}
572
+ </wa-tree>
573
+ `}
574
+ </wa-scroller>
575
+ </div>
576
+ <div slot="end" class="right-panel">
577
+ ${this.renderDetailPanel()}
578
+ </div>
579
+ </wa-split-panel>
580
+ </div>
581
+ `;
582
+ }
583
+ static {
584
+ this.styles = css`
585
+ :host {
586
+ display: flex;
587
+ flex-direction: column;
588
+ height: 100%;
589
+ }
590
+
591
+ .settings-editor-container {
592
+ flex: 1;
593
+ min-height: 0;
594
+ display: flex;
595
+ flex-direction: column;
596
+ }
597
+
598
+ .left-panel, .right-panel {
599
+ display: flex;
600
+ flex-direction: column;
601
+ min-height: 0;
602
+ overflow: hidden;
603
+ }
604
+
605
+ .tree-scroller, .detail-scroller {
606
+ flex: 1;
607
+ min-height: 0;
608
+ }
609
+
610
+ .tree-node-content {
611
+ cursor: pointer;
612
+ user-select: none;
613
+ }
614
+
615
+ .tree-node-content.selected {
616
+ font-weight: 600;
617
+ }
618
+
619
+ .detail-placeholder {
620
+ display: flex;
621
+ flex-direction: column;
622
+ align-items: center;
623
+ justify-content: center;
624
+ height: 100%;
625
+ text-align: center;
626
+ padding: 32px;
627
+ }
628
+
629
+ .detail-panel {
630
+ display: flex;
631
+ flex-direction: column;
632
+ height: 100%;
633
+ overflow: hidden;
634
+ }
635
+
636
+ .detail-heading-row {
637
+ display: flex;
638
+ align-items: center;
639
+ gap: 8px;
640
+ margin-bottom: 16px;
641
+ padding: 0 16px;
642
+ }
643
+
644
+ .detail-heading {
645
+ margin: 0;
646
+ flex: 1;
647
+ }
648
+
649
+ .detail-scroller {
650
+ padding: 0 16px 16px;
651
+ }
652
+
653
+ .detail-empty {
654
+ padding: 24px 0;
655
+ }
656
+
657
+ .detail-group-header {
658
+ display: flex;
659
+ align-items: center;
660
+ gap: 8px;
661
+ margin-top: 12px;
662
+ margin-bottom: 4px;
663
+ }
664
+
665
+ .detail-group-header:first-child {
666
+ margin-top: 0;
667
+ }
668
+
669
+ .nested-row {
670
+ display: flex;
671
+ align-items: center;
672
+ gap: 8px;
673
+ margin-bottom: 6px;
674
+ }
675
+
676
+ .setting-row {
677
+ display: flex;
678
+ flex-wrap: wrap;
679
+ gap: 12px;
680
+ align-items: flex-start;
681
+ padding: 12px 0;
682
+ }
683
+
684
+ .setting-row-in-group {
685
+ padding-left: 20px;
686
+ }
687
+
688
+ .setting-meta {
689
+ flex: 0 0 auto;
690
+ min-width: 140px;
691
+ }
692
+
693
+ .setting-title {
694
+ font-weight: 600;
695
+ display: block;
696
+ margin-bottom: 4px;
697
+ }
698
+
699
+ .setting-control {
700
+ flex: 1;
701
+ min-width: 0;
702
+ display: flex;
703
+ }
704
+
705
+ .setting-row command {
706
+ flex-shrink: 0;
707
+ }
708
+
709
+ .setting-control wa-input,
710
+ .setting-control wa-textarea,
711
+ .setting-control wa-number-input,
712
+ .setting-control wa-select {
713
+ flex: 1;
714
+ min-width: 0;
715
+ width: 100%;
716
+ }
717
+
718
+ .setting-meta .setting-control-key {
719
+ width: 160px;
720
+ }
721
+
722
+ .empty-state {
723
+ display: flex;
724
+ flex-direction: column;
725
+ align-items: center;
726
+ justify-content: center;
727
+ padding: 32px;
728
+ text-align: center;
729
+ }
730
+
731
+ .nested-object {
732
+ margin-left: 8px;
733
+ padding-left: 8px;
734
+ border-left: 2px solid var(--wa-color-neutral-85);
735
+ }
736
+
737
+ .toolbar-search {
738
+ width: 200px;
739
+ margin-right: 8px;
740
+ }
741
+
742
+ .split-panel-fill {
743
+ height: 100%;
744
+ }
745
+ `;
746
+ }
747
+ };
748
+ _decorate([property({ attribute: false })], LyraSettingsTree.prototype, "input", void 0);
749
+ _decorate([state()], LyraSettingsTree.prototype, "settings", void 0);
750
+ _decorate([state()], LyraSettingsTree.prototype, "categoryTree", void 0);
751
+ _decorate([state()], LyraSettingsTree.prototype, "selectedPath", void 0);
752
+ _decorate([state()], LyraSettingsTree.prototype, "detailRows", void 0);
753
+ _decorate([state()], LyraSettingsTree.prototype, "detailTitle", void 0);
754
+ _decorate([state()], LyraSettingsTree.prototype, "searchQuery", void 0);
755
+ LyraSettingsTree = _LyraSettingsTree = _decorate([customElement("lyra-settings-tree")], LyraSettingsTree);
756
+ //#endregion
757
+ //#region src/settings-tree-extension.ts
758
+ var settings_tree_extension_default = (_uiContext) => {
759
+ editorRegistry.registerEditorInputHandler({
760
+ editorId: "system.settings-tree",
761
+ label: "Settings",
762
+ ranking: 1e3,
763
+ canHandle: (input) => input.key === ".system.settings",
764
+ handle: async (input) => {
765
+ input.component = (id) => html`
766
+ <lyra-settings-tree id="${id}" .input=${input}></lyra-settings-tree>
767
+ `;
768
+ return input;
769
+ }
770
+ });
771
+ commandRegistry.registerHandler("open_settings", {
772
+ ranking: 100,
773
+ execute: () => {
774
+ editorRegistry.loadEditor({
775
+ title: "Settings",
776
+ data: {},
777
+ key: ".system.settings",
778
+ icon: "gear",
779
+ state: {}
780
+ });
781
+ }
782
+ });
783
+ contributionRegistry.registerContribution(TOOLBAR_MAIN_RIGHT, {
784
+ command: "open_settings",
785
+ icon: "gear",
786
+ label: "Settings"
787
+ });
788
+ };
789
+ //#endregion
790
+ export { settings_tree_extension_default as default };
791
+
792
+ //# sourceMappingURL=settings-tree-extension-xvuT1JDl.js.map