flexdesk 0.1.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.
Files changed (124) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +195 -0
  3. package/THIRD_PARTY_LICENSES.md +53 -0
  4. package/css/base.css +3439 -0
  5. package/css/flexdesk.css +5797 -0
  6. package/css/overrides.css +2349 -0
  7. package/css/reset.css +46 -0
  8. package/css/tokens.css +137 -0
  9. package/dist/charts.js +780 -0
  10. package/dist/charts.js.map +7 -0
  11. package/dist/chunk-3PHCPZHT.js +222 -0
  12. package/dist/chunk-3PHCPZHT.js.map +7 -0
  13. package/dist/chunk-CT4YXXLP.js +1770 -0
  14. package/dist/chunk-CT4YXXLP.js.map +7 -0
  15. package/dist/chunk-DRYCDMEG.js +2217 -0
  16. package/dist/chunk-DRYCDMEG.js.map +7 -0
  17. package/dist/chunk-DVU44T77.js +855 -0
  18. package/dist/chunk-DVU44T77.js.map +7 -0
  19. package/dist/chunk-FL5KFNQH.js +640 -0
  20. package/dist/chunk-FL5KFNQH.js.map +7 -0
  21. package/dist/chunk-FOOS3T5L.js +117 -0
  22. package/dist/chunk-FOOS3T5L.js.map +7 -0
  23. package/dist/chunk-JYWURG5T.js +32 -0
  24. package/dist/chunk-JYWURG5T.js.map +7 -0
  25. package/dist/chunk-TLZUUFOE.js +276 -0
  26. package/dist/chunk-TLZUUFOE.js.map +7 -0
  27. package/dist/chunk-UCJ2WD4D.js +625 -0
  28. package/dist/chunk-UCJ2WD4D.js.map +7 -0
  29. package/dist/chunk-WVFGV5FT.js +205 -0
  30. package/dist/chunk-WVFGV5FT.js.map +7 -0
  31. package/dist/core.js +1098 -0
  32. package/dist/core.js.map +7 -0
  33. package/dist/editor.js +1661 -0
  34. package/dist/editor.js.map +7 -0
  35. package/dist/flexdesk.css +5797 -0
  36. package/dist/host.js +132 -0
  37. package/dist/host.js.map +7 -0
  38. package/dist/reset.css +46 -0
  39. package/dist/tile_registry-6WZPTUZV.js +28 -0
  40. package/dist/tile_registry-6WZPTUZV.js.map +7 -0
  41. package/dist/tiles.js +2068 -0
  42. package/dist/tiles.js.map +7 -0
  43. package/dist/tokens.css +137 -0
  44. package/dist/widgets.js +5438 -0
  45. package/dist/widgets.js.map +7 -0
  46. package/dist/wm.js +4830 -0
  47. package/dist/wm.js.map +7 -0
  48. package/package.json +80 -0
  49. package/src/charts/chart_types.js +699 -0
  50. package/src/charts/data_series_plot_window.js +761 -0
  51. package/src/charts/downsample.js +119 -0
  52. package/src/charts/plot_config.js +176 -0
  53. package/src/charts/plot_popout_window.js +1560 -0
  54. package/src/charts/plotly_wrapper.js +432 -0
  55. package/src/core/event_bus.js +230 -0
  56. package/src/core/logging.js +300 -0
  57. package/src/core/settings.js +813 -0
  58. package/src/core/state_guard.js +382 -0
  59. package/src/core/state_guard_config.js +309 -0
  60. package/src/core/state_machine.js +258 -0
  61. package/src/editor/editor_pane.js +264 -0
  62. package/src/editor/monaco_editor_factory.js +449 -0
  63. package/src/editor/monaco_loader.js +74 -0
  64. package/src/editor/notebook_search.js +460 -0
  65. package/src/editor/notebook_tab_bar.js +456 -0
  66. package/src/editor/notebook_undo_manager.js +108 -0
  67. package/src/editor/split_pane_container.js +197 -0
  68. package/src/help/help_modal.js +409 -0
  69. package/src/help/help_registry.js +65 -0
  70. package/src/host/host.js +69 -0
  71. package/src/host/pywebview_host.js +133 -0
  72. package/src/tiles/config_schema.js +256 -0
  73. package/src/tiles/layout_persistence.js +662 -0
  74. package/src/tiles/tile_base.js +785 -0
  75. package/src/tiles/tile_grid.js +905 -0
  76. package/src/tiles/tile_registry.js +136 -0
  77. package/src/tiling/command_palette.js +328 -0
  78. package/src/tiling/content_registry.js +110 -0
  79. package/src/tiling/desktops.js +133 -0
  80. package/src/tiling/entity_sources.js +179 -0
  81. package/src/tiling/keymap.js +156 -0
  82. package/src/tiling/kind_taxonomy.js +172 -0
  83. package/src/tiling/landing_table.js +717 -0
  84. package/src/tiling/loading_overlay.js +33 -0
  85. package/src/tiling/nav_panel.js +142 -0
  86. package/src/tiling/page_factory.js +215 -0
  87. package/src/tiling/panel_keys.js +145 -0
  88. package/src/tiling/shell.js +437 -0
  89. package/src/tiling/tile_breadcrumb.js +222 -0
  90. package/src/tiling/tile_renderer.js +459 -0
  91. package/src/tiling/tile_tab_menu.js +340 -0
  92. package/src/tiling/tile_tree.js +880 -0
  93. package/src/tiling/wm.js +1352 -0
  94. package/src/ui/base/component_base.js +30 -0
  95. package/src/ui/base/controller_base.js +48 -0
  96. package/src/ui/base/page_base.js +102 -0
  97. package/src/ui/components/about_dialog.js +224 -0
  98. package/src/ui/components/action_dropdown.js +389 -0
  99. package/src/ui/components/attribute_list_editor.js +652 -0
  100. package/src/ui/components/autocomplete_field.js +732 -0
  101. package/src/ui/components/computing_status_window.js +81 -0
  102. package/src/ui/components/confirm_dialog.js +307 -0
  103. package/src/ui/components/context_menu.js +87 -0
  104. package/src/ui/components/data_table.js +2228 -0
  105. package/src/ui/components/detail_header.js +177 -0
  106. package/src/ui/components/drag_reorder.js +0 -0
  107. package/src/ui/components/gallery_picker.js +147 -0
  108. package/src/ui/components/inline_renamer.js +129 -0
  109. package/src/ui/components/managed_window.js +804 -0
  110. package/src/ui/components/modal.js +512 -0
  111. package/src/ui/components/notification_history.js +264 -0
  112. package/src/ui/components/slide_out_panel.js +185 -0
  113. package/src/ui/components/slider_field.js +122 -0
  114. package/src/ui/components/sortable_list.js +309 -0
  115. package/src/ui/components/table_state_store.js +81 -0
  116. package/src/ui/components/toast.js +59 -0
  117. package/src/ui/components/tree_view.js +460 -0
  118. package/src/ui/components/window_taskbar.js +143 -0
  119. package/src/ui/controllers/panel_state_machine.js +634 -0
  120. package/src/ui/controllers/window_chrome_controller.js +659 -0
  121. package/src/ui/notification_center.js +576 -0
  122. package/src/ui/utils/overlay_scrollbar.js +492 -0
  123. package/src/ui/utils/raf_resize_observer.js +31 -0
  124. package/src/ui/utils/tooltip_service.js +406 -0
@@ -0,0 +1,640 @@
1
+ // src/core/settings.js
2
+ var DEFAULTS = Object.freeze({
3
+ workspace: Object.freeze({
4
+ save: Object.freeze({
5
+ showToast: false,
6
+ showErrorToast: true
7
+ }),
8
+ import: Object.freeze({
9
+ showToast: false,
10
+ showErrorToast: true
11
+ }),
12
+ autosave: Object.freeze({
13
+ enabled: true,
14
+ intervalSeconds: 60
15
+ }),
16
+ undoHistoryLimit: 200
17
+ }),
18
+ host: Object.freeze({
19
+ showConnectedToast: false
20
+ }),
21
+ window: Object.freeze({
22
+ animateMinimize: true,
23
+ macShadows: true
24
+ }),
25
+ notifications: Object.freeze({
26
+ durationMs: 3500
27
+ }),
28
+ data: Object.freeze({
29
+ tablePageSize: 100,
30
+ defaultResampleMethod: "mean"
31
+ }),
32
+ etl: Object.freeze({
33
+ parallelWorkers: 1
34
+ }),
35
+ logging: Object.freeze({
36
+ minLevel: "info",
37
+ historyLimit: 500,
38
+ enableConsole: false
39
+ }),
40
+ editor: Object.freeze({
41
+ autosaveDelayMs: 5e3
42
+ }),
43
+ notebook: Object.freeze({
44
+ cellWidthMode: "fixed",
45
+ codeCellMaxHeight: false,
46
+ defaultCellView: "config"
47
+ }),
48
+ debug: Object.freeze({
49
+ logSettingsAccess: false
50
+ }),
51
+ projects: Object.freeze({
52
+ directory: null
53
+ }),
54
+ // Only auto-create-defaults survives; the modules directory / addon /
55
+ // ETL-plugin loaders were the node-graph plugin system.
56
+ ai: Object.freeze({
57
+ provider: "",
58
+ cloud: Object.freeze({
59
+ providerId: "anthropic",
60
+ providers: Object.freeze({})
61
+ }),
62
+ local: Object.freeze({
63
+ serverType: "llamacpp",
64
+ modelPath: "",
65
+ baseUrl: "",
66
+ model: "",
67
+ port: 8080,
68
+ gpuLayers: -1,
69
+ contextLength: 32768,
70
+ flashAttention: true,
71
+ evalBatchSize: 512,
72
+ kvCacheOnGpu: true
73
+ }),
74
+ defaultMode: "ask",
75
+ maxToolCalls: 25
76
+ })
77
+ });
78
+ var SCHEMA = Object.freeze({
79
+ // ── General ──────────────────────────────────────────────────────────────
80
+ "workspace.save.showToast": {
81
+ type: "boolean",
82
+ category: "general",
83
+ group: "Workspace Notifications",
84
+ label: "Save success notification",
85
+ description: "Show a toast notification when a workspace is saved successfully.",
86
+ defaultValue: false
87
+ },
88
+ "workspace.save.showErrorToast": {
89
+ type: "boolean",
90
+ category: "general",
91
+ group: "Workspace Notifications",
92
+ label: "Save error notification",
93
+ description: "Show a toast notification when a workspace save fails.",
94
+ defaultValue: true
95
+ },
96
+ "workspace.import.showToast": {
97
+ type: "boolean",
98
+ category: "general",
99
+ group: "Workspace Notifications",
100
+ label: "Import success notification",
101
+ description: "Show a toast notification when a workspace is imported successfully.",
102
+ defaultValue: false
103
+ },
104
+ "workspace.import.showErrorToast": {
105
+ type: "boolean",
106
+ category: "general",
107
+ group: "Workspace Notifications",
108
+ label: "Import error notification",
109
+ description: "Show a toast notification when a workspace import fails.",
110
+ defaultValue: true
111
+ },
112
+ "host.showConnectedToast": {
113
+ type: "boolean",
114
+ category: "general",
115
+ group: "Host Bridge",
116
+ label: "Host connected notification",
117
+ description: "Show a toast notification when the desktop host bridge connects.",
118
+ defaultValue: false
119
+ },
120
+ "notifications.durationMs": {
121
+ type: "number",
122
+ category: "general",
123
+ group: "Notifications",
124
+ label: "Toast notification duration",
125
+ description: "How long toast notifications stay visible (milliseconds).",
126
+ defaultValue: 3500,
127
+ min: 1e3,
128
+ max: 15e3,
129
+ step: 500
130
+ },
131
+ "workspace.autosave.enabled": {
132
+ type: "boolean",
133
+ category: "general",
134
+ group: "Auto-Save",
135
+ label: "Enable auto-save",
136
+ description: "Automatically save the workspace at regular intervals.",
137
+ defaultValue: true
138
+ },
139
+ "workspace.autosave.intervalSeconds": {
140
+ type: "number",
141
+ category: "general",
142
+ group: "Auto-Save",
143
+ label: "Auto-save interval (seconds)",
144
+ description: "Time between automatic saves.",
145
+ defaultValue: 60,
146
+ min: 10,
147
+ max: 600,
148
+ step: 10
149
+ },
150
+ "workspace.undoHistoryLimit": {
151
+ type: "number",
152
+ category: "general",
153
+ group: "History",
154
+ label: "Undo/redo history depth",
155
+ description: "Maximum number of undo/redo steps retained in memory.",
156
+ defaultValue: 200,
157
+ min: 10,
158
+ max: 1e3,
159
+ step: 10
160
+ },
161
+ // ── Windows ──────────────────────────────────────────────────────────────
162
+ "window.animateMinimize": {
163
+ type: "boolean",
164
+ category: "window",
165
+ group: "Appearance",
166
+ label: "Animate minimize/restore",
167
+ description: "Animate managed windows toward/from the taskbar when minimizing and restoring.",
168
+ defaultValue: true
169
+ },
170
+ "window.macShadows": {
171
+ type: "boolean",
172
+ category: "window",
173
+ group: "Appearance",
174
+ label: "macOS-style shadows",
175
+ description: "Use multi-layered soft shadows on managed windows.",
176
+ defaultValue: true
177
+ },
178
+ // ── Data ─────────────────────────────────────────────────────────────────
179
+ "data.tablePageSize": {
180
+ type: "number",
181
+ category: "data",
182
+ group: "Tables",
183
+ label: "Table page size",
184
+ description: "Number of rows displayed per page in data tables.",
185
+ defaultValue: 100,
186
+ min: 25,
187
+ max: 1e3,
188
+ step: 25
189
+ },
190
+ "data.defaultResampleMethod": {
191
+ type: "select",
192
+ category: "data",
193
+ group: "Import",
194
+ label: "Default resample method",
195
+ description: "Aggregation method used when resampling imported time series.",
196
+ defaultValue: "mean",
197
+ options: [
198
+ { value: "mean", label: "Mean" },
199
+ { value: "sum", label: "Sum" },
200
+ { value: "last", label: "Last" },
201
+ { value: "first", label: "First" },
202
+ { value: "linear", label: "Linear interpolation" }
203
+ ]
204
+ },
205
+ "etl.parallelWorkers": {
206
+ type: "number",
207
+ category: "data",
208
+ group: "ETL Pipelines",
209
+ label: "Parallel pipeline workers",
210
+ description: "Number of pipelines to execute simultaneously in orchestrations. 1 = sequential.",
211
+ defaultValue: 1,
212
+ min: 1,
213
+ max: 8,
214
+ step: 1
215
+ },
216
+ // ── Logging ──────────────────────────────────────────────────────────────
217
+ "logging.minLevel": {
218
+ type: "select",
219
+ category: "logging",
220
+ group: "Output",
221
+ label: "Minimum log level",
222
+ description: "Only messages at this level or above are recorded.",
223
+ defaultValue: "info",
224
+ options: [
225
+ { value: "trace", label: "Trace" },
226
+ { value: "debug", label: "Debug" },
227
+ { value: "info", label: "Info" },
228
+ { value: "warn", label: "Warning" },
229
+ { value: "error", label: "Error" },
230
+ { value: "fatal", label: "Fatal" }
231
+ ]
232
+ },
233
+ "logging.enableConsole": {
234
+ type: "boolean",
235
+ category: "logging",
236
+ group: "Output",
237
+ label: "Enable console output",
238
+ description: "Mirror log messages to the browser console.",
239
+ defaultValue: false
240
+ },
241
+ "logging.historyLimit": {
242
+ type: "number",
243
+ category: "logging",
244
+ group: "History",
245
+ label: "Log history limit",
246
+ description: "Maximum number of log entries retained in memory.",
247
+ defaultValue: 500,
248
+ min: 50,
249
+ max: 1e4,
250
+ step: 50
251
+ },
252
+ // ── Projects ─────────────────────────────────────────────────────────────
253
+ "projects.directory": {
254
+ type: "text",
255
+ category: "general",
256
+ group: "Projects",
257
+ label: "Projects directory",
258
+ description: "Default directory for new projects and bundled demos. Leave empty for OS default.",
259
+ defaultValue: null,
260
+ placeholder: "OS default (%APPDATA%/EcoSim/projects)"
261
+ },
262
+ // ── AI Assistant ────────────────────────────────────────────────────────
263
+ // Provider, auth, model, and server settings are managed from the chat UI
264
+ // modals. Only behavior settings appear here.
265
+ "ai.defaultMode": {
266
+ type: "select",
267
+ category: "ai",
268
+ group: "Behavior",
269
+ label: "Default mode",
270
+ description: "Default interaction mode for the AI assistant.",
271
+ defaultValue: "ask",
272
+ options: [
273
+ { value: "ask", label: "Ask (preview before applying)" },
274
+ { value: "edit", label: "Edit (apply immediately)" }
275
+ ]
276
+ },
277
+ "ai.maxToolCalls": {
278
+ type: "number",
279
+ category: "ai",
280
+ group: "Behavior",
281
+ label: "Max tool calls",
282
+ description: "Maximum number of tool calls per AI turn.",
283
+ defaultValue: 25,
284
+ min: 1,
285
+ max: 100,
286
+ step: 1
287
+ },
288
+ // ── Notebook ─────────────────────────────────────────────────────────────
289
+ "notebook.cellWidthMode": {
290
+ type: "select",
291
+ category: "notebook",
292
+ group: "Layout",
293
+ label: "Cell width mode",
294
+ description: 'Controls how wide all cells appear. "Fixed" constrains cells to a max-width; "Full" stretches all cells.',
295
+ defaultValue: "fixed",
296
+ options: [
297
+ { value: "fixed", label: "Fixed width (900px)" },
298
+ { value: "full", label: "Full width" }
299
+ ]
300
+ },
301
+ "notebook.codeCellMaxHeight": {
302
+ type: "boolean",
303
+ category: "notebook",
304
+ group: "Layout",
305
+ label: "Limit code cell height",
306
+ description: "When enabled, code cells have a maximum height and scroll internally instead of expanding to show all content.",
307
+ defaultValue: false
308
+ },
309
+ "notebook.paramCellMaxHeight": {
310
+ type: "boolean",
311
+ category: "notebook",
312
+ group: "Layout",
313
+ label: "Limit parameter cell height",
314
+ description: "When enabled, parameter cells have a maximum height and scroll internally instead of expanding to show all content.",
315
+ defaultValue: false
316
+ },
317
+ "notebook.defaultCellView": {
318
+ type: "select",
319
+ category: "notebook",
320
+ group: "Layout",
321
+ label: "Default cell view",
322
+ description: "Which tab to show by default on all cells: Config (edit) or EcoLang (generated code).",
323
+ defaultValue: "config",
324
+ options: [
325
+ { value: "config", label: "Config" },
326
+ { value: "dsl", label: "EcoLang" }
327
+ ]
328
+ },
329
+ // ── Advanced ─────────────────────────────────────────────────────────────
330
+ "editor.autosaveDelayMs": {
331
+ type: "number",
332
+ category: "advanced",
333
+ group: "Performance",
334
+ label: "Editor autosave delay (ms)",
335
+ description: "Delay before the function editor auto-saves changes.",
336
+ defaultValue: 5e3,
337
+ min: 1e3,
338
+ max: 3e4,
339
+ step: 1e3
340
+ },
341
+ "debug.logSettingsAccess": {
342
+ type: "boolean",
343
+ category: "advanced",
344
+ group: "Debug",
345
+ label: "Log settings access",
346
+ description: "Log all getSetting/setSetting calls to the console for debugging.",
347
+ defaultValue: false
348
+ }
349
+ });
350
+ var CATEGORIES = Object.freeze([
351
+ { id: "general", label: "General", icon: "tune", description: "Workspace behavior and notifications", order: 10 },
352
+ { id: "ai", label: "AI Assistant", icon: "smart_toy", description: "AI assistant behavior", order: 30 },
353
+ { id: "window", label: "Windows", icon: "web_asset", description: "Managed window animation and appearance", order: 40 },
354
+ { id: "data", label: "Data", icon: "database", description: "Table display, import, and pipeline defaults", order: 50 },
355
+ { id: "logging", label: "Logging", icon: "terminal", description: "Log level, console output, and history", order: 60 },
356
+ { id: "notebook", label: "Notebook", icon: "menu_book", description: "Notebook editor layout and behavior", order: 70 },
357
+ { id: "advanced", label: "Advanced", icon: "code", description: "Debug and developer settings", order: 80 }
358
+ ]);
359
+ var STORAGE_KEY = "ecosim.settings.v1";
360
+ function _getNestedValue(obj, parts) {
361
+ let current = obj;
362
+ for (const part of parts) {
363
+ if (current === null || current === void 0 || typeof current !== "object") {
364
+ return void 0;
365
+ }
366
+ current = current[part];
367
+ }
368
+ return current;
369
+ }
370
+ function _setNestedValue(obj, parts, value) {
371
+ let current = obj;
372
+ for (let i = 0; i < parts.length - 1; i++) {
373
+ if (current[parts[i]] === void 0 || current[parts[i]] === null || typeof current[parts[i]] !== "object") {
374
+ current[parts[i]] = {};
375
+ }
376
+ current = current[parts[i]];
377
+ }
378
+ current[parts[parts.length - 1]] = value;
379
+ }
380
+ function _deepEqual(a, b) {
381
+ if (a === b) return true;
382
+ if (a == null || b == null) return a === b;
383
+ if (typeof a !== typeof b) return false;
384
+ if (Array.isArray(a)) {
385
+ if (!Array.isArray(b) || a.length !== b.length) return false;
386
+ return a.every((v, i) => _deepEqual(v, b[i]));
387
+ }
388
+ if (typeof a === "object") {
389
+ const keysA = Object.keys(a);
390
+ const keysB = Object.keys(b);
391
+ if (keysA.length !== keysB.length) return false;
392
+ return keysA.every((k) => _deepEqual(a[k], b[k]));
393
+ }
394
+ return false;
395
+ }
396
+ function _clone(value) {
397
+ return value === void 0 ? void 0 : JSON.parse(JSON.stringify(value));
398
+ }
399
+ function _deepMerge(target, src) {
400
+ for (const [key, value] of Object.entries(src || {})) {
401
+ const isPlain = value !== null && typeof value === "object" && !Array.isArray(value);
402
+ if (isPlain) {
403
+ if (target[key] === null || typeof target[key] !== "object" || Array.isArray(target[key])) {
404
+ target[key] = {};
405
+ }
406
+ _deepMerge(target[key], value);
407
+ } else {
408
+ target[key] = _clone(value);
409
+ }
410
+ }
411
+ return target;
412
+ }
413
+ function createSettingsStore({
414
+ defaults = {},
415
+ schema = {},
416
+ categories = [],
417
+ storageKey = null,
418
+ eventBus = null
419
+ } = {}) {
420
+ const _defaults = _clone(defaults) ?? {};
421
+ const _schema = { ...schema };
422
+ let _categories = categories.map((c) => ({ ...c }));
423
+ const _values = _clone(defaults) ?? {};
424
+ let _bus = eventBus;
425
+ const _sortCategories = () => {
426
+ _categories.sort((a, b) => (a.order ?? 1e3) - (b.order ?? 1e3));
427
+ };
428
+ _sortCategories();
429
+ const _debug = () => _values.debug?.logSettingsAccess;
430
+ const _load = () => {
431
+ try {
432
+ if (!storageKey || typeof localStorage === "undefined") return;
433
+ const raw = localStorage.getItem(storageKey);
434
+ if (!raw) return;
435
+ const saved = JSON.parse(raw);
436
+ for (const path of Object.keys(_schema)) {
437
+ const parts = path.split(".");
438
+ const value = _getNestedValue(saved, parts);
439
+ if (value !== void 0) {
440
+ _setNestedValue(_values, parts, value);
441
+ }
442
+ }
443
+ } catch (err) {
444
+ console.warn("[Settings] Failed to load persisted settings", err);
445
+ }
446
+ };
447
+ const _save = () => {
448
+ try {
449
+ if (!storageKey || typeof localStorage === "undefined") return;
450
+ const sparse = {};
451
+ for (const path of Object.keys(_schema)) {
452
+ const parts = path.split(".");
453
+ const current = _getNestedValue(_values, parts);
454
+ const def = _schema[path].defaultValue;
455
+ if (!_deepEqual(current, def)) {
456
+ _setNestedValue(sparse, parts, current);
457
+ }
458
+ }
459
+ if (Object.keys(sparse).length === 0) {
460
+ localStorage.removeItem(storageKey);
461
+ } else {
462
+ localStorage.setItem(storageKey, JSON.stringify(sparse));
463
+ }
464
+ } catch (err) {
465
+ console.warn("[Settings] Failed to persist settings", err);
466
+ }
467
+ };
468
+ const _emit = (path, value) => {
469
+ if (!_bus) return;
470
+ _bus.emit(`settings:${path}:changed`, { path, value });
471
+ _bus.emit("settings:changed", { path, value });
472
+ };
473
+ _load();
474
+ const extend = ({ defaults: d = {}, schema: s = {}, categories: c = [] } = {}) => {
475
+ _deepMerge(_defaults, d);
476
+ _deepMerge(_values, d);
477
+ Object.assign(_schema, s);
478
+ for (const cat of c) {
479
+ if (!cat?.id) continue;
480
+ if (_categories.some((x) => x.id === cat.id)) continue;
481
+ _categories.push({ ...cat });
482
+ }
483
+ _sortCategories();
484
+ _load();
485
+ };
486
+ const get = (path, defaultValue = void 0) => {
487
+ const parts = path.split(".");
488
+ let current = _values;
489
+ for (const part of parts) {
490
+ if (current === null || current === void 0 || typeof current !== "object") {
491
+ if (_debug()) console.warn(`[Settings] Path not found: ${path}`);
492
+ return defaultValue;
493
+ }
494
+ current = current[part];
495
+ }
496
+ if (current === void 0) return defaultValue;
497
+ if (_debug()) console.log(`[Settings] getSetting('${path}') =>`, current);
498
+ return current;
499
+ };
500
+ const set = (path, value) => {
501
+ const parts = path.split(".");
502
+ const lastPart = parts.pop();
503
+ let current = _values;
504
+ for (const part of parts) {
505
+ if (current[part] === void 0 || current[part] === null) {
506
+ current[part] = {};
507
+ }
508
+ if (typeof current[part] !== "object") {
509
+ console.warn(`[Settings] Cannot set '${path}': intermediate path is not an object`);
510
+ return false;
511
+ }
512
+ current = current[part];
513
+ }
514
+ current[lastPart] = value;
515
+ if (_debug()) console.log(`[Settings] setSetting('${path}', ${JSON.stringify(value)})`);
516
+ _save();
517
+ _emit(path, value);
518
+ return true;
519
+ };
520
+ const getDefault = (path) => {
521
+ const def = _schema[path];
522
+ if (!def) return _clone(_getNestedValue(_defaults, path.split(".")));
523
+ return _clone(def.defaultValue);
524
+ };
525
+ const resetOne = (path) => {
526
+ const defaultVal = getDefault(path);
527
+ if (defaultVal === void 0) {
528
+ console.warn(`[Settings] No default found for '${path}'`);
529
+ return false;
530
+ }
531
+ return set(path, defaultVal);
532
+ };
533
+ const resetCategoryFn = (categoryId) => {
534
+ for (const [path, def] of Object.entries(_schema)) {
535
+ if (def.category !== categoryId) continue;
536
+ _setNestedValue(_values, path.split("."), _clone(def.defaultValue));
537
+ }
538
+ _save();
539
+ if (_bus) {
540
+ _bus.emit("settings:category:reset", { category: categoryId });
541
+ _bus.emit("settings:changed", { path: null, category: categoryId });
542
+ }
543
+ };
544
+ const resetAll = () => {
545
+ for (const [path, def] of Object.entries(_schema)) {
546
+ _setNestedValue(_values, path.split("."), _clone(def.defaultValue));
547
+ }
548
+ _save();
549
+ if (_bus) {
550
+ _bus.emit("settings:reset", {});
551
+ _bus.emit("settings:changed", { path: null });
552
+ }
553
+ };
554
+ const getByCategory = (categoryId) => {
555
+ const result = [];
556
+ for (const [path, def] of Object.entries(_schema)) {
557
+ if (def.category === categoryId) result.push({ path, ...def });
558
+ }
559
+ return result;
560
+ };
561
+ return Object.freeze({
562
+ get,
563
+ set,
564
+ extend,
565
+ getDefault,
566
+ resetOne,
567
+ resetCategory: resetCategoryFn,
568
+ resetAll,
569
+ getAll: () => _clone(_values),
570
+ getSchema: () => _schema,
571
+ // Frozen snapshot: the old module-level CATEGORIES was a frozen const, and
572
+ // a caller that sorted or spliced the live array in place would silently
573
+ // reorder the Settings sidebar. Same objects, same order — just not ours
574
+ // to wreck.
575
+ getCategories: () => Object.freeze(_categories.slice()),
576
+ getByCategory,
577
+ setEventBus: (bus) => {
578
+ _bus = bus;
579
+ }
580
+ });
581
+ }
582
+ var _store = createSettingsStore({
583
+ defaults: DEFAULTS,
584
+ schema: SCHEMA,
585
+ categories: CATEGORIES,
586
+ storageKey: STORAGE_KEY
587
+ });
588
+ function registerSettings(slice) {
589
+ _store.extend(slice);
590
+ }
591
+ function registerSettingsEventBus(eventBus) {
592
+ _store.setEventBus(eventBus);
593
+ }
594
+ function getSetting(path, defaultValue = void 0) {
595
+ return _store.get(path, defaultValue);
596
+ }
597
+ function setSetting(path, value) {
598
+ return _store.set(path, value);
599
+ }
600
+ function getDefaultValue(path) {
601
+ return _store.getDefault(path);
602
+ }
603
+ function resetSetting(path) {
604
+ return _store.resetOne(path);
605
+ }
606
+ function resetCategory(categoryId) {
607
+ _store.resetCategory(categoryId);
608
+ }
609
+ function resetAllSettings() {
610
+ _store.resetAll();
611
+ }
612
+ function getAllSettings() {
613
+ return _store.getAll();
614
+ }
615
+ function getSchema() {
616
+ return _store.getSchema();
617
+ }
618
+ function getCategories() {
619
+ return _store.getCategories();
620
+ }
621
+ function getSettingsByCategory(categoryId) {
622
+ return _store.getByCategory(categoryId);
623
+ }
624
+
625
+ export {
626
+ createSettingsStore,
627
+ registerSettings,
628
+ registerSettingsEventBus,
629
+ getSetting,
630
+ setSetting,
631
+ getDefaultValue,
632
+ resetSetting,
633
+ resetCategory,
634
+ resetAllSettings,
635
+ getAllSettings,
636
+ getSchema,
637
+ getCategories,
638
+ getSettingsByCategory
639
+ };
640
+ //# sourceMappingURL=chunk-FL5KFNQH.js.map