@eclipse-lyra/core 0.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.
Files changed (281) hide show
  1. package/dist/api/base-classes.d.ts +7 -0
  2. package/dist/api/base-classes.d.ts.map +1 -0
  3. package/dist/api/constants.d.ts +2 -0
  4. package/dist/api/constants.d.ts.map +1 -0
  5. package/dist/api/index.d.ts +6 -0
  6. package/dist/api/index.d.ts.map +1 -0
  7. package/dist/api/index.js +84 -0
  8. package/dist/api/index.js.map +1 -0
  9. package/dist/api/services.d.ts +28 -0
  10. package/dist/api/services.d.ts.map +1 -0
  11. package/dist/api/types.d.ts +11 -0
  12. package/dist/api/types.d.ts.map +1 -0
  13. package/dist/commands/editor.d.ts +1 -0
  14. package/dist/commands/editor.d.ts.map +1 -0
  15. package/dist/commands/files.d.ts +2 -0
  16. package/dist/commands/files.d.ts.map +1 -0
  17. package/dist/commands/global.d.ts +1 -0
  18. package/dist/commands/global.d.ts.map +1 -0
  19. package/dist/commands/index.d.ts +1 -0
  20. package/dist/commands/index.d.ts.map +1 -0
  21. package/dist/commands/open-view-as-editor.d.ts +2 -0
  22. package/dist/commands/open-view-as-editor.d.ts.map +1 -0
  23. package/dist/commands/version-info.d.ts +2 -0
  24. package/dist/commands/version-info.d.ts.map +1 -0
  25. package/dist/components/app-selector.d.ts +17 -0
  26. package/dist/components/app-selector.d.ts.map +1 -0
  27. package/dist/components/app-switcher.d.ts +13 -0
  28. package/dist/components/app-switcher.d.ts.map +1 -0
  29. package/dist/components/command.d.ts +31 -0
  30. package/dist/components/command.d.ts.map +1 -0
  31. package/dist/components/extensions.d.ts +32 -0
  32. package/dist/components/extensions.d.ts.map +1 -0
  33. package/dist/components/fastviews.d.ts +34 -0
  34. package/dist/components/fastviews.d.ts.map +1 -0
  35. package/dist/components/filebrowser.d.ts +45 -0
  36. package/dist/components/filebrowser.d.ts.map +1 -0
  37. package/dist/components/index.d.ts +1 -0
  38. package/dist/components/index.d.ts.map +1 -0
  39. package/dist/components/language-selector.d.ts +12 -0
  40. package/dist/components/language-selector.d.ts.map +1 -0
  41. package/dist/components/log-terminal.d.ts +36 -0
  42. package/dist/components/log-terminal.d.ts.map +1 -0
  43. package/dist/components/part-name.d.ts +12 -0
  44. package/dist/components/part-name.d.ts.map +1 -0
  45. package/dist/components/tasks.d.ts +13 -0
  46. package/dist/components/tasks.d.ts.map +1 -0
  47. package/dist/contributions/default-ui-contributions.d.ts +2 -0
  48. package/dist/contributions/default-ui-contributions.d.ts.map +1 -0
  49. package/dist/contributions/index.d.ts +1 -0
  50. package/dist/contributions/index.d.ts.map +1 -0
  51. package/dist/contributions/marketplace-catalog-contributions.d.ts +2 -0
  52. package/dist/contributions/marketplace-catalog-contributions.d.ts.map +1 -0
  53. package/dist/core/app-host-config.d.ts +7 -0
  54. package/dist/core/app-host-config.d.ts.map +1 -0
  55. package/dist/core/apploader.d.ts +214 -0
  56. package/dist/core/apploader.d.ts.map +1 -0
  57. package/dist/core/appstate.d.ts +12 -0
  58. package/dist/core/appstate.d.ts.map +1 -0
  59. package/dist/core/commandregistry.d.ts +79 -0
  60. package/dist/core/commandregistry.d.ts.map +1 -0
  61. package/dist/core/config.d.ts +15 -0
  62. package/dist/core/config.d.ts.map +1 -0
  63. package/dist/core/constants.d.ts +22 -0
  64. package/dist/core/constants.d.ts.map +1 -0
  65. package/dist/core/contributionregistry.d.ts +53 -0
  66. package/dist/core/contributionregistry.d.ts.map +1 -0
  67. package/dist/core/di.d.ts +18 -0
  68. package/dist/core/di.d.ts.map +1 -0
  69. package/dist/core/dialogservice.d.ts +33 -0
  70. package/dist/core/dialogservice.d.ts.map +1 -0
  71. package/dist/core/editorregistry.d.ts +87 -0
  72. package/dist/core/editorregistry.d.ts.map +1 -0
  73. package/dist/core/esmsh-service.d.ts +40 -0
  74. package/dist/core/esmsh-service.d.ts.map +1 -0
  75. package/dist/core/events.d.ts +7 -0
  76. package/dist/core/events.d.ts.map +1 -0
  77. package/dist/core/events.js +63 -0
  78. package/dist/core/events.js.map +1 -0
  79. package/dist/core/extensionregistry.d.ts +98 -0
  80. package/dist/core/extensionregistry.d.ts.map +1 -0
  81. package/dist/core/filesys/common.d.ts +122 -0
  82. package/dist/core/filesys/common.d.ts.map +1 -0
  83. package/dist/core/filesys/fs-access.d.ts +31 -0
  84. package/dist/core/filesys/fs-access.d.ts.map +1 -0
  85. package/dist/core/filesys/index.d.ts +5 -0
  86. package/dist/core/filesys/index.d.ts.map +1 -0
  87. package/dist/core/filesys/indexeddb.d.ts +41 -0
  88. package/dist/core/filesys/indexeddb.d.ts.map +1 -0
  89. package/dist/core/filesys/opfs.d.ts +14 -0
  90. package/dist/core/filesys/opfs.d.ts.map +1 -0
  91. package/dist/core/i18n.d.ts +50 -0
  92. package/dist/core/i18n.d.ts.map +1 -0
  93. package/dist/core/index.d.ts +1 -0
  94. package/dist/core/index.d.ts.map +1 -0
  95. package/dist/core/keybindings.d.ts +67 -0
  96. package/dist/core/keybindings.d.ts.map +1 -0
  97. package/dist/core/logger.d.ts +44 -0
  98. package/dist/core/logger.d.ts.map +1 -0
  99. package/dist/core/marketplaceregistry.d.ts +25 -0
  100. package/dist/core/marketplaceregistry.d.ts.map +1 -0
  101. package/dist/core/packageinfoservice.d.ts +16 -0
  102. package/dist/core/packageinfoservice.d.ts.map +1 -0
  103. package/dist/core/persistenceservice.d.ts +6 -0
  104. package/dist/core/persistenceservice.d.ts.map +1 -0
  105. package/dist/core/settingsservice.d.ts +54 -0
  106. package/dist/core/settingsservice.d.ts.map +1 -0
  107. package/dist/core/signals.d.ts +3 -0
  108. package/dist/core/signals.d.ts.map +1 -0
  109. package/dist/core/taskservice.d.ts +20 -0
  110. package/dist/core/taskservice.d.ts.map +1 -0
  111. package/dist/core/toast.d.ts +4 -0
  112. package/dist/core/toast.d.ts.map +1 -0
  113. package/dist/core/tree-utils.d.ts +16 -0
  114. package/dist/core/tree-utils.d.ts.map +1 -0
  115. package/dist/dialogs/confirm-dialog.d.ts +14 -0
  116. package/dist/dialogs/confirm-dialog.d.ts.map +1 -0
  117. package/dist/dialogs/index.d.ts +5 -0
  118. package/dist/dialogs/index.d.ts.map +1 -0
  119. package/dist/dialogs/info-dialog.d.ts +13 -0
  120. package/dist/dialogs/info-dialog.d.ts.map +1 -0
  121. package/dist/dialogs/navigable-info-dialog.d.ts +33 -0
  122. package/dist/dialogs/navigable-info-dialog.d.ts.map +1 -0
  123. package/dist/dialogs/prompt-dialog.d.ts +21 -0
  124. package/dist/dialogs/prompt-dialog.d.ts.map +1 -0
  125. package/dist/externals/lit.d.ts +21 -0
  126. package/dist/externals/lit.d.ts.map +1 -0
  127. package/dist/externals/lit.js +24 -0
  128. package/dist/externals/lit.js.map +1 -0
  129. package/dist/externals/third-party.d.ts +7 -0
  130. package/dist/externals/third-party.d.ts.map +1 -0
  131. package/dist/externals/third-party.js +7 -0
  132. package/dist/externals/third-party.js.map +1 -0
  133. package/dist/externals/webawesome.d.ts +1 -0
  134. package/dist/externals/webawesome.d.ts.map +1 -0
  135. package/dist/externals/webawesome.js +75 -0
  136. package/dist/externals/webawesome.js.map +1 -0
  137. package/dist/i18n/extensions.json.d.ts +42 -0
  138. package/dist/i18n/fastviews.json.d.ts +13 -0
  139. package/dist/i18n/filebrowser.json.d.ts +29 -0
  140. package/dist/i18n/index.d.ts +2 -0
  141. package/dist/i18n/index.d.ts.map +1 -0
  142. package/dist/i18n/logterminal.json.d.ts +45 -0
  143. package/dist/i18n/partname.json.d.ts +15 -0
  144. package/dist/i18n/tasks.json.d.ts +15 -0
  145. package/dist/i18n/workspace.json.d.ts +15 -0
  146. package/dist/icon-DN6fp0dg.js +487 -0
  147. package/dist/icon-DN6fp0dg.js.map +1 -0
  148. package/dist/index.d.ts +2 -0
  149. package/dist/index.d.ts.map +1 -0
  150. package/dist/index.js +84 -0
  151. package/dist/index.js.map +1 -0
  152. package/dist/layouts/standard-layout.d.ts +16 -0
  153. package/dist/layouts/standard-layout.d.ts.map +1 -0
  154. package/dist/nocontent-BhrN6yxJ.js +48 -0
  155. package/dist/nocontent-BhrN6yxJ.js.map +1 -0
  156. package/dist/parts/container.d.ts +4 -0
  157. package/dist/parts/container.d.ts.map +1 -0
  158. package/dist/parts/contextmenu.d.ts +50 -0
  159. package/dist/parts/contextmenu.d.ts.map +1 -0
  160. package/dist/parts/dialog-content.d.ts +9 -0
  161. package/dist/parts/dialog-content.d.ts.map +1 -0
  162. package/dist/parts/element.d.ts +36 -0
  163. package/dist/parts/element.d.ts.map +1 -0
  164. package/dist/parts/index.d.ts +1 -0
  165. package/dist/parts/index.d.ts.map +1 -0
  166. package/dist/parts/index.js +3 -0
  167. package/dist/parts/index.js.map +1 -0
  168. package/dist/parts/part.d.ts +96 -0
  169. package/dist/parts/part.d.ts.map +1 -0
  170. package/dist/parts/resizable-grid.d.ts +31 -0
  171. package/dist/parts/resizable-grid.d.ts.map +1 -0
  172. package/dist/parts/tabs.d.ts +75 -0
  173. package/dist/parts/tabs.d.ts.map +1 -0
  174. package/dist/parts/toolbar.d.ts +35 -0
  175. package/dist/parts/toolbar.d.ts.map +1 -0
  176. package/dist/resizable-grid-BRH3MyZK.js +3813 -0
  177. package/dist/resizable-grid-BRH3MyZK.js.map +1 -0
  178. package/dist/standard-layout-BSGa06lP.js +4907 -0
  179. package/dist/standard-layout-BSGa06lP.js.map +1 -0
  180. package/dist/widgets/icon.d.ts +10 -0
  181. package/dist/widgets/icon.d.ts.map +1 -0
  182. package/dist/widgets/index.d.ts +1 -0
  183. package/dist/widgets/index.d.ts.map +1 -0
  184. package/dist/widgets/index.js +3 -0
  185. package/dist/widgets/index.js.map +1 -0
  186. package/dist/widgets/nocontent.d.ts +13 -0
  187. package/dist/widgets/nocontent.d.ts.map +1 -0
  188. package/dist/widgets/widget.d.ts +25 -0
  189. package/dist/widgets/widget.d.ts.map +1 -0
  190. package/package.json +81 -0
  191. package/src/api/base-classes.ts +10 -0
  192. package/src/api/constants.ts +3 -0
  193. package/src/api/index.ts +31 -0
  194. package/src/api/services.ts +58 -0
  195. package/src/api/types.ts +46 -0
  196. package/src/commands/editor.ts +1 -0
  197. package/src/commands/files.ts +425 -0
  198. package/src/commands/global.ts +198 -0
  199. package/src/commands/index.ts +6 -0
  200. package/src/commands/open-view-as-editor.ts +28 -0
  201. package/src/commands/version-info.ts +214 -0
  202. package/src/components/app-selector.ts +233 -0
  203. package/src/components/app-switcher.ts +126 -0
  204. package/src/components/command.ts +236 -0
  205. package/src/components/extensions.ts +615 -0
  206. package/src/components/fastviews.ts +314 -0
  207. package/src/components/filebrowser.ts +518 -0
  208. package/src/components/index.ts +9 -0
  209. package/src/components/language-selector.ts +166 -0
  210. package/src/components/log-terminal.ts +337 -0
  211. package/src/components/part-name.ts +54 -0
  212. package/src/components/tasks.ts +275 -0
  213. package/src/contributions/default-ui-contributions.ts +44 -0
  214. package/src/contributions/index.ts +3 -0
  215. package/src/contributions/marketplace-catalog-contributions.ts +6 -0
  216. package/src/core/app-host-config.ts +23 -0
  217. package/src/core/apploader.ts +630 -0
  218. package/src/core/appstate.ts +15 -0
  219. package/src/core/commandregistry.ts +210 -0
  220. package/src/core/config.ts +29 -0
  221. package/src/core/constants.ts +29 -0
  222. package/src/core/contributionregistry.ts +81 -0
  223. package/src/core/di.ts +54 -0
  224. package/src/core/dialogservice.ts +266 -0
  225. package/src/core/editorregistry.ts +347 -0
  226. package/src/core/esmsh-service.ts +404 -0
  227. package/src/core/events.ts +68 -0
  228. package/src/core/extensionregistry.ts +399 -0
  229. package/src/core/filesys/common.ts +474 -0
  230. package/src/core/filesys/fs-access.ts +339 -0
  231. package/src/core/filesys/index.ts +5 -0
  232. package/src/core/filesys/indexeddb.ts +596 -0
  233. package/src/core/filesys/opfs.ts +95 -0
  234. package/src/core/i18n.ts +221 -0
  235. package/src/core/index.ts +51 -0
  236. package/src/core/keybindings.ts +274 -0
  237. package/src/core/logger.ts +187 -0
  238. package/src/core/marketplaceregistry.ts +197 -0
  239. package/src/core/packageinfoservice.ts +56 -0
  240. package/src/core/persistenceservice.ts +46 -0
  241. package/src/core/settingsservice.ts +191 -0
  242. package/src/core/signals.ts +18 -0
  243. package/src/core/taskservice.ts +72 -0
  244. package/src/core/toast.ts +21 -0
  245. package/src/core/tree-utils.ts +24 -0
  246. package/src/dialogs/confirm-dialog.ts +72 -0
  247. package/src/dialogs/index.ts +4 -0
  248. package/src/dialogs/info-dialog.ts +67 -0
  249. package/src/dialogs/navigable-info-dialog.ts +256 -0
  250. package/src/dialogs/prompt-dialog.ts +123 -0
  251. package/src/externals/lit.ts +36 -0
  252. package/src/externals/third-party.ts +10 -0
  253. package/src/externals/webawesome.ts +76 -0
  254. package/src/i18n/extensions.json +39 -0
  255. package/src/i18n/fastviews.json +10 -0
  256. package/src/i18n/filebrowser.json +27 -0
  257. package/src/i18n/index.ts +25 -0
  258. package/src/i18n/logterminal.json +42 -0
  259. package/src/i18n/partname.json +12 -0
  260. package/src/i18n/tasks.json +12 -0
  261. package/src/i18n/workspace.json +12 -0
  262. package/src/icons/icons.txt +3 -0
  263. package/src/icons/js.svg +6 -0
  264. package/src/icons/jupyter.svg +18 -0
  265. package/src/icons/python.svg +15 -0
  266. package/src/index.ts +3 -0
  267. package/src/layouts/standard-layout.ts +174 -0
  268. package/src/parts/container.ts +4 -0
  269. package/src/parts/contextmenu.ts +266 -0
  270. package/src/parts/dialog-content.ts +31 -0
  271. package/src/parts/element.ts +100 -0
  272. package/src/parts/index.ts +5 -0
  273. package/src/parts/part.ts +158 -0
  274. package/src/parts/resizable-grid.ts +366 -0
  275. package/src/parts/tabs.ts +581 -0
  276. package/src/parts/toolbar.ts +260 -0
  277. package/src/vite-env.d.ts +16 -0
  278. package/src/widgets/icon.ts +38 -0
  279. package/src/widgets/index.ts +2 -0
  280. package/src/widgets/nocontent.ts +40 -0
  281. package/src/widgets/widget.ts +92 -0
@@ -0,0 +1,3813 @@
1
+ import { css, html, render, nothing } from "lit";
2
+ import { property, state, customElement } from "lit/decorators.js";
3
+ import { styleMap } from "lit/directives/style-map.js";
4
+ import { l as rootContext, f as activeTasksSignal, i as createLogger, j as defaultLogger, t as toastError, u as uiContext, m as toastInfo, T as TOPIC_COMMAND_REGISTERED, g as commandRegistry, L as LyraWidget, b as TOPIC_CONTRIBUTEIONS_CHANGED, h as contributionRegistry, p as partDirtySignal, d as activePartSignal } from "./icon-DN6fp0dg.js";
5
+ import { unsafeHTML } from "lit/directives/unsafe-html.js";
6
+ import { publish, subscribe } from "./core/events.js";
7
+ import { createRef, ref } from "lit/directives/ref.js";
8
+ import { when } from "lit/directives/when.js";
9
+ import { repeat } from "lit/directives/repeat.js";
10
+ import { marked } from "marked";
11
+ const TOOLBAR_MAIN = "app-toolbars-main";
12
+ const TOOLBAR_MAIN_RIGHT = "app-toolbars-main-right";
13
+ const TOOLBAR_MAIN_CENTER = "app-toolbars-main-center";
14
+ const TOOLBAR_BOTTOM = "app-toolbars-bottom";
15
+ const TOOLBAR_BOTTOM_CENTER = "app-toolbars-bottom-center";
16
+ const TOOLBAR_BOTTOM_END = "app-toolbars-bottom-end";
17
+ const SYSTEM_VIEWS = "system-views";
18
+ const EDITOR_AREA_MAIN = "editor-area-main";
19
+ const SIDEBAR_MAIN = "sidebar-main";
20
+ const SIDEBAR_MAIN_BOTTOM = "sidebar-main-bottom";
21
+ const SIDEBAR_AUXILIARY = "sidebar-auxiliary";
22
+ const PANEL_BOTTOM = "panel-bottom";
23
+ const COMMAND_SAVE = "command-save";
24
+ const HIDE_DOT_RESOURCE = false;
25
+ var MouseButton = /* @__PURE__ */ ((MouseButton2) => {
26
+ MouseButton2[MouseButton2["LEFT"] = 0] = "LEFT";
27
+ MouseButton2[MouseButton2["MIDDLE"] = 1] = "MIDDLE";
28
+ MouseButton2[MouseButton2["RIGHT"] = 2] = "RIGHT";
29
+ MouseButton2[MouseButton2["BACK"] = 3] = "BACK";
30
+ MouseButton2[MouseButton2["FORWARD"] = 4] = "FORWARD";
31
+ return MouseButton2;
32
+ })(MouseButton || {});
33
+ const constants = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
34
+ __proto__: null,
35
+ COMMAND_SAVE,
36
+ EDITOR_AREA_MAIN,
37
+ HIDE_DOT_RESOURCE,
38
+ MouseButton,
39
+ PANEL_BOTTOM,
40
+ SIDEBAR_AUXILIARY,
41
+ SIDEBAR_MAIN,
42
+ SIDEBAR_MAIN_BOTTOM,
43
+ SYSTEM_VIEWS,
44
+ TOOLBAR_BOTTOM,
45
+ TOOLBAR_BOTTOM_CENTER,
46
+ TOOLBAR_BOTTOM_END,
47
+ TOOLBAR_MAIN,
48
+ TOOLBAR_MAIN_CENTER,
49
+ TOOLBAR_MAIN_RIGHT
50
+ }, Symbol.toStringTag, { value: "Module" }));
51
+ const DB_NAME = "eclipse-lyra-persistence";
52
+ const STORE_NAME = "keyval";
53
+ const DB_VERSION = 1;
54
+ let dbPromise = null;
55
+ function getDB() {
56
+ if (!dbPromise) {
57
+ dbPromise = new Promise((resolve, reject) => {
58
+ const request = indexedDB.open(DB_NAME, DB_VERSION);
59
+ request.onerror = () => reject(request.error);
60
+ request.onsuccess = () => resolve(request.result);
61
+ request.onupgradeneeded = (e) => {
62
+ e.target.result.createObjectStore(STORE_NAME);
63
+ };
64
+ });
65
+ }
66
+ return dbPromise;
67
+ }
68
+ class PersistenceService {
69
+ async persistObject(key, value) {
70
+ const db = await getDB();
71
+ return new Promise((resolve, reject) => {
72
+ const tx = db.transaction(STORE_NAME, "readwrite");
73
+ const req = tx.objectStore(STORE_NAME).put(value, key);
74
+ req.onsuccess = () => resolve();
75
+ req.onerror = () => reject(req.error);
76
+ });
77
+ }
78
+ async getObject(key) {
79
+ const db = await getDB();
80
+ return new Promise((resolve, reject) => {
81
+ const tx = db.transaction(STORE_NAME, "readonly");
82
+ const req = tx.objectStore(STORE_NAME).get(key);
83
+ req.onsuccess = () => resolve(req.result);
84
+ req.onerror = () => reject(req.error);
85
+ });
86
+ }
87
+ }
88
+ const persistenceService = new PersistenceService();
89
+ rootContext.put("persistenceService", persistenceService);
90
+ const SETTINGS_FILE_PATH = ".geospace/settings.json";
91
+ const DIALOG_SETTINGS_KEY = "dialogSettings";
92
+ const TOPIC_SETTINGS_CHANGED = "events/settings/changed";
93
+ function mergeSchemaProperties(target, source) {
94
+ if (!source) return;
95
+ for (const [key, value] of Object.entries(source)) {
96
+ if (value && typeof value === "object") {
97
+ const existing = target[key];
98
+ if (existing?.properties && value.properties) {
99
+ mergeSchemaProperties(existing.properties, value.properties);
100
+ } else {
101
+ target[key] = { ...value, properties: value.properties ? { ...value.properties } : void 0 };
102
+ }
103
+ }
104
+ }
105
+ }
106
+ class SettingsService {
107
+ constructor() {
108
+ this.mergedSchema = { type: "object", properties: {} };
109
+ }
110
+ async checkSettings() {
111
+ if (!this.appSettings) {
112
+ this.appSettings = await persistenceService.getObject(SETTINGS_FILE_PATH);
113
+ if (!this.appSettings) {
114
+ this.appSettings = {};
115
+ await persistenceService.persistObject(SETTINGS_FILE_PATH, this.appSettings);
116
+ }
117
+ publish(TOPIC_SETTINGS_CHANGED, this.appSettings);
118
+ }
119
+ }
120
+ /**
121
+ * Register a JSON Schema for settings. Top-level keys of `schema.properties` become categories.
122
+ * Consumers can pass schemas produced by Zod (e.g. zod-to-json-schema) or hand-written JSON Schema.
123
+ */
124
+ registerSchema(schema) {
125
+ const props = schema.properties ?? (schema.type === "object" ? {} : void 0);
126
+ if (props) {
127
+ if (!this.mergedSchema.properties) this.mergedSchema.properties = {};
128
+ mergeSchemaProperties(this.mergedSchema.properties, props);
129
+ }
130
+ }
131
+ getCategories() {
132
+ const props = this.mergedSchema.properties;
133
+ if (!props) return [];
134
+ return Object.entries(props).filter(([, s]) => s && typeof s === "object").map(([id, schema]) => ({
135
+ id,
136
+ label: schema.title ?? id,
137
+ order: typeof schema.order === "number" ? schema.order : 0,
138
+ schema
139
+ })).sort((a, b) => a.order - b.order);
140
+ }
141
+ getSchemaForCategory(categoryId) {
142
+ return this.mergedSchema.properties?.[categoryId];
143
+ }
144
+ /**
145
+ * Resolve a dotted key (e.g. "editor.fontSize") to the JSON Schema fragment for that path.
146
+ */
147
+ getSchemaForSettingKey(dottedKey) {
148
+ const parts = dottedKey.split(".").filter(Boolean);
149
+ if (parts.length === 0) return this.mergedSchema;
150
+ let current = this.mergedSchema;
151
+ for (const part of parts) {
152
+ current = current?.properties?.[part];
153
+ if (!current) return void 0;
154
+ }
155
+ return current;
156
+ }
157
+ traversePath(obj, pathParts, createIfMissing) {
158
+ if (pathParts.length === 0) return null;
159
+ let current = obj;
160
+ const lastIndex = pathParts.length - 1;
161
+ for (let i = 0; i < lastIndex; i++) {
162
+ const part = pathParts[i];
163
+ if (current[part] === void 0) {
164
+ if (!createIfMissing) return null;
165
+ current[part] = {};
166
+ }
167
+ if (current[part] === null || typeof current[part] !== "object") return null;
168
+ current = current[part];
169
+ }
170
+ return { parent: current, key: pathParts[lastIndex] };
171
+ }
172
+ async getAt(path) {
173
+ await this.checkSettings();
174
+ const parts = path.split(".").filter(Boolean);
175
+ if (parts.length === 0) return this.appSettings;
176
+ const result = this.traversePath(this.appSettings, parts, false);
177
+ if (!result) return void 0;
178
+ return result.parent[result.key];
179
+ }
180
+ async setAt(path, value) {
181
+ await this.checkSettings();
182
+ const parts = path.split(".").filter(Boolean);
183
+ if (parts.length === 0) return;
184
+ const result = this.traversePath(this.appSettings, parts, true);
185
+ if (!result) return;
186
+ result.parent[result.key] = value;
187
+ await persistenceService.persistObject(SETTINGS_FILE_PATH, this.appSettings);
188
+ publish(TOPIC_SETTINGS_CHANGED, this.appSettings);
189
+ }
190
+ async get(key) {
191
+ await this.checkSettings();
192
+ return this.appSettings[key];
193
+ }
194
+ async set(key, value) {
195
+ await this.checkSettings();
196
+ this.appSettings[key] = value;
197
+ await persistenceService.persistObject(SETTINGS_FILE_PATH, this.appSettings);
198
+ publish(TOPIC_SETTINGS_CHANGED, this.appSettings);
199
+ }
200
+ async getAll() {
201
+ await this.checkSettings();
202
+ return this.appSettings;
203
+ }
204
+ async setAll(settings) {
205
+ this.appSettings = settings;
206
+ await persistenceService.persistObject(SETTINGS_FILE_PATH, this.appSettings);
207
+ publish(TOPIC_SETTINGS_CHANGED, this.appSettings);
208
+ }
209
+ async getDialogSetting(key) {
210
+ await this.checkSettings();
211
+ const dialogSettings = this.appSettings[DIALOG_SETTINGS_KEY] || {};
212
+ return dialogSettings[key];
213
+ }
214
+ async setDialogSetting(key, value) {
215
+ await this.checkSettings();
216
+ const dialogSettings = this.appSettings[DIALOG_SETTINGS_KEY] || {};
217
+ dialogSettings[key] = value;
218
+ this.appSettings[DIALOG_SETTINGS_KEY] = dialogSettings;
219
+ await persistenceService.persistObject(SETTINGS_FILE_PATH, this.appSettings);
220
+ publish(TOPIC_SETTINGS_CHANGED, this.appSettings);
221
+ }
222
+ }
223
+ const appSettings = new SettingsService();
224
+ rootContext.put("appSettings", appSettings);
225
+ class TaskService {
226
+ constructor() {
227
+ this.tasks = [];
228
+ this.updateCounter = 0;
229
+ }
230
+ notifyUpdate() {
231
+ this.updateCounter++;
232
+ activeTasksSignal.set(this.updateCounter);
233
+ }
234
+ run(name, task) {
235
+ const progressMonitor = this.createProgressMonitor(name);
236
+ try {
237
+ this.tasks.push(progressMonitor);
238
+ this.notifyUpdate();
239
+ task(progressMonitor);
240
+ } finally {
241
+ this.tasks.splice(this.tasks.indexOf(progressMonitor), 1);
242
+ this.notifyUpdate();
243
+ }
244
+ }
245
+ async runAsync(name, task) {
246
+ const progressMonitor = this.createProgressMonitor(name);
247
+ this.tasks.push(progressMonitor);
248
+ this.notifyUpdate();
249
+ return task(progressMonitor).finally(() => {
250
+ this.tasks.splice(this.tasks.indexOf(progressMonitor), 1);
251
+ this.notifyUpdate();
252
+ });
253
+ }
254
+ createProgressMonitor(name) {
255
+ const monitor = {
256
+ name,
257
+ message: "",
258
+ currentStep: 0,
259
+ totalSteps: -1,
260
+ // -1 indicates indefinite progress
261
+ progress: -1
262
+ // -1 means use step-based calculation
263
+ };
264
+ return new Proxy(monitor, {
265
+ set: (target, prop, value) => {
266
+ target[prop] = value;
267
+ this.notifyUpdate();
268
+ return true;
269
+ }
270
+ });
271
+ }
272
+ getActiveTasks() {
273
+ return this.tasks;
274
+ }
275
+ }
276
+ const taskService = new TaskService();
277
+ rootContext.put("taskService", taskService);
278
+ const logger$2 = createLogger("EsmShService");
279
+ const _EsmShService = class _EsmShService {
280
+ isEsmShUrl(url) {
281
+ try {
282
+ const urlObj = new URL(url);
283
+ return urlObj.hostname === "esm.sh" || urlObj.hostname === "raw.esm.sh";
284
+ } catch {
285
+ return false;
286
+ }
287
+ }
288
+ isSourceIdentifier(source) {
289
+ if (this.isEsmShUrl(source)) {
290
+ return false;
291
+ }
292
+ if (this.isHttpUrl(source)) {
293
+ return false;
294
+ }
295
+ return this.parseSource(source) !== null;
296
+ }
297
+ isHttpUrl(url) {
298
+ try {
299
+ const parsed = new URL(url);
300
+ return parsed.protocol === "http:" || parsed.protocol === "https:";
301
+ } catch {
302
+ return false;
303
+ }
304
+ }
305
+ parseSource(source) {
306
+ if (!source || typeof source !== "string") {
307
+ return null;
308
+ }
309
+ source = source.trim();
310
+ if (this.isHttpUrl(source)) {
311
+ return null;
312
+ }
313
+ if (source.startsWith(_EsmShService.GITHUB_PREFIX)) {
314
+ return this.parseGitHubSource(source);
315
+ }
316
+ if (source.startsWith(_EsmShService.JSR_PREFIX)) {
317
+ return this.parseJsrSource(source);
318
+ }
319
+ if (source.startsWith(_EsmShService.PR_PREFIX)) {
320
+ return this.parsePrSource(source);
321
+ }
322
+ return this.parseNpmSource(source);
323
+ }
324
+ parseGitHubSource(source) {
325
+ const withoutPrefix = source.substring(_EsmShService.GITHUB_PREFIX.length);
326
+ const parts = withoutPrefix.split("/");
327
+ if (parts.length < 2) {
328
+ return null;
329
+ }
330
+ const owner = parts[0];
331
+ const repoWithRef = parts[1];
332
+ let repo;
333
+ let version;
334
+ let path;
335
+ const refMatch = repoWithRef.match(/^(.+?)(@(.+))?$/);
336
+ if (!refMatch) {
337
+ return null;
338
+ }
339
+ repo = refMatch[1];
340
+ version = refMatch[3];
341
+ if (parts.length > 2) {
342
+ path = parts.slice(2).join("/");
343
+ }
344
+ return {
345
+ type: "github",
346
+ owner,
347
+ repo,
348
+ version,
349
+ path
350
+ };
351
+ }
352
+ parseJsrSource(source) {
353
+ const withoutPrefix = source.substring(_EsmShService.JSR_PREFIX.length);
354
+ if (!withoutPrefix.startsWith("@")) {
355
+ return null;
356
+ }
357
+ const parts = withoutPrefix.split("/");
358
+ if (parts.length < 2) {
359
+ return null;
360
+ }
361
+ const scope = parts[0];
362
+ const packageWithVersion = parts[1];
363
+ let packageName;
364
+ let version;
365
+ let path;
366
+ const versionMatch = packageWithVersion.match(/^(.+?)(@(.+))?$/);
367
+ if (!versionMatch) {
368
+ return null;
369
+ }
370
+ packageName = `${scope}/${versionMatch[1]}`;
371
+ version = versionMatch[3];
372
+ if (parts.length > 2) {
373
+ path = parts.slice(2).join("/");
374
+ }
375
+ return {
376
+ type: "jsr",
377
+ package: packageName,
378
+ version,
379
+ path
380
+ };
381
+ }
382
+ parsePrSource(source) {
383
+ const withoutPrefix = source.substring(_EsmShService.PR_PREFIX.length);
384
+ const parts = withoutPrefix.split("/");
385
+ if (parts.length < 2) {
386
+ return null;
387
+ }
388
+ const owner = parts[0];
389
+ const repoWithCommit = parts[1];
390
+ let repo;
391
+ let commit;
392
+ const commitMatch = repoWithCommit.match(/^(.+?)@(.+)$/);
393
+ if (commitMatch) {
394
+ repo = commitMatch[1];
395
+ commit = commitMatch[2];
396
+ } else {
397
+ repo = repoWithCommit;
398
+ }
399
+ return {
400
+ type: "pr",
401
+ owner,
402
+ repo,
403
+ commit
404
+ };
405
+ }
406
+ parseNpmSource(source) {
407
+ const parts = source.split("/");
408
+ const firstPart = parts[0];
409
+ let packageName;
410
+ let version;
411
+ let path;
412
+ const versionMatch = firstPart.match(/^(.+?)(@(.+))?$/);
413
+ if (!versionMatch) {
414
+ return null;
415
+ }
416
+ packageName = versionMatch[1];
417
+ version = versionMatch[3];
418
+ if (parts.length > 1) {
419
+ path = parts.slice(1).join("/");
420
+ }
421
+ return {
422
+ type: "npm",
423
+ package: packageName,
424
+ version,
425
+ path
426
+ };
427
+ }
428
+ buildEsmShUrl(source, options) {
429
+ let url = _EsmShService.ESM_SH_BASE;
430
+ switch (source.type) {
431
+ case "npm":
432
+ url += `/${source.package}`;
433
+ if (source.version) {
434
+ url += `@${source.version}`;
435
+ }
436
+ if (source.path) {
437
+ url += `/${source.path}`;
438
+ }
439
+ break;
440
+ case "github":
441
+ url += `/${_EsmShService.GITHUB_PREFIX}${source.owner}/${source.repo}`;
442
+ if (source.version) {
443
+ url += `@${source.version}`;
444
+ }
445
+ if (source.path) {
446
+ url += `/${source.path}`;
447
+ }
448
+ break;
449
+ case "jsr":
450
+ url += `/${_EsmShService.JSR_PREFIX}${source.package}`;
451
+ if (source.version) {
452
+ url += `@${source.version}`;
453
+ }
454
+ if (source.path) {
455
+ url += `/${source.path}`;
456
+ }
457
+ break;
458
+ case "pr":
459
+ url += `/${_EsmShService.PR_PREFIX}${source.owner}/${source.repo}`;
460
+ if (source.commit) {
461
+ url += `@${source.commit}`;
462
+ }
463
+ break;
464
+ }
465
+ const queryParams = [];
466
+ if (options?.deps) {
467
+ const depsString = Object.entries(options.deps).map(([pkg, version]) => `${pkg}@${version}`).join(",");
468
+ queryParams.push(`deps=${encodeURIComponent(depsString)}`);
469
+ }
470
+ if (options?.target) {
471
+ queryParams.push(`target=${encodeURIComponent(options.target)}`);
472
+ }
473
+ if (options?.dev) {
474
+ queryParams.push("dev");
475
+ }
476
+ if (options?.bundle === false) {
477
+ queryParams.push("bundle=false");
478
+ } else if (options?.bundle === true) {
479
+ queryParams.push("bundle");
480
+ }
481
+ if (queryParams.length > 0) {
482
+ url += `?${queryParams.join("&")}`;
483
+ }
484
+ return url;
485
+ }
486
+ normalizeToEsmSh(source, options) {
487
+ if (this.isEsmShUrl(source)) {
488
+ return source;
489
+ }
490
+ if (this.isHttpUrl(source)) {
491
+ return source;
492
+ }
493
+ const parsed = this.parseSource(source);
494
+ if (!parsed) {
495
+ logger$2.warn(`Could not parse source identifier: ${source}`);
496
+ return source;
497
+ }
498
+ return this.buildEsmShUrl(parsed, options);
499
+ }
500
+ extractPackageName(source) {
501
+ const parsed = this.parseSource(source);
502
+ if (!parsed) {
503
+ return null;
504
+ }
505
+ switch (parsed.type) {
506
+ case "npm":
507
+ return parsed.package || null;
508
+ case "github":
509
+ return `${parsed.owner}/${parsed.repo}`;
510
+ case "jsr":
511
+ return parsed.package || null;
512
+ case "pr":
513
+ return `${parsed.owner}/${parsed.repo}`;
514
+ }
515
+ }
516
+ isGitHubUrl(url) {
517
+ try {
518
+ const urlObj = new URL(url);
519
+ return urlObj.hostname === "github.com" || urlObj.hostname === "www.github.com";
520
+ } catch {
521
+ return url.startsWith("https://github.com/") || url.startsWith("http://github.com/");
522
+ }
523
+ }
524
+ convertGitHubUrlToSource(githubUrl) {
525
+ try {
526
+ const urlObj = new URL(githubUrl);
527
+ const pathParts = urlObj.pathname.split("/").filter((p) => p);
528
+ if (pathParts.length < 2) {
529
+ throw new Error("Invalid GitHub URL format");
530
+ }
531
+ const owner = pathParts[0];
532
+ let repo = pathParts[1].replace(/\.git$/, "");
533
+ let ref2;
534
+ let filePath;
535
+ if (pathParts.length > 2) {
536
+ if (pathParts[2] === "blob" || pathParts[2] === "tree") {
537
+ ref2 = pathParts[3] || "main";
538
+ if (pathParts[2] === "blob" && pathParts.length > 4) {
539
+ filePath = pathParts.slice(4).join("/");
540
+ }
541
+ } else if (pathParts[2] === "commit") {
542
+ ref2 = pathParts[3];
543
+ } else {
544
+ filePath = pathParts.slice(2).join("/");
545
+ }
546
+ }
547
+ let ghUrl = `${_EsmShService.GITHUB_PREFIX}${owner}/${repo}`;
548
+ if (ref2) {
549
+ ghUrl += `@${ref2}`;
550
+ }
551
+ if (filePath) {
552
+ ghUrl += `/${filePath}`;
553
+ }
554
+ return ghUrl;
555
+ } catch {
556
+ const urlMatch = githubUrl.match(/github\.com\/([^\/]+)\/([^\/]+)/);
557
+ if (urlMatch) {
558
+ return `${_EsmShService.GITHUB_PREFIX}${urlMatch[1]}/${urlMatch[2].replace(/\.git$/, "")}`;
559
+ }
560
+ return githubUrl;
561
+ }
562
+ }
563
+ async fetchGitHubPackageJson(source) {
564
+ if (source.type !== "github") {
565
+ throw new Error("Source must be a GitHub source");
566
+ }
567
+ const owner = source.owner;
568
+ const repo = source.repo;
569
+ const ref2 = source.version || "main";
570
+ const packageJsonUrl = `https://raw.githubusercontent.com/${owner}/${repo}/${ref2}/package.json`;
571
+ const response = await fetch(packageJsonUrl);
572
+ if (!response.ok) {
573
+ throw new Error(`Failed to fetch package.json: ${response.statusText}`);
574
+ }
575
+ return await response.json();
576
+ }
577
+ };
578
+ _EsmShService.ESM_SH_BASE = "https://esm.sh";
579
+ _EsmShService.GITHUB_PREFIX = "gh/";
580
+ _EsmShService.JSR_PREFIX = "jsr/";
581
+ _EsmShService.PR_PREFIX = "pr/";
582
+ let EsmShService = _EsmShService;
583
+ const esmShService = new EsmShService();
584
+ rootContext.put("esmShService", esmShService);
585
+ const TOPIC_EXTENSIONS_CHANGED = "events/extensionsregistry/extensionsConfigChanged";
586
+ const KEY_EXTENSIONS_CONFIG = "extensions";
587
+ const KEY_EXTERNAL_EXTENSIONS = "extensions.external";
588
+ class ExtensionRegistry {
589
+ constructor() {
590
+ this.extensions = {};
591
+ this.loadedExtensions = /* @__PURE__ */ new Set();
592
+ this.loadingPromises = /* @__PURE__ */ new Map();
593
+ subscribe(TOPIC_SETTINGS_CHANGED, () => {
594
+ this.extensionsSettings = void 0;
595
+ this.checkExtensionsConfig().then();
596
+ });
597
+ this.loadPersistedExternalExtensions().then(() => {
598
+ this.checkExtensionsConfig().then(async () => {
599
+ const loadPromises = this.extensionsSettings?.filter((setting) => this.isEnabled(setting.id)).map(
600
+ (setting) => this.load(setting.id).catch((e) => {
601
+ toastError("Extension could not be loaded: " + e.message);
602
+ })
603
+ ) || [];
604
+ await Promise.all(loadPromises);
605
+ });
606
+ });
607
+ }
608
+ async loadPersistedExternalExtensions() {
609
+ try {
610
+ const persisted = await appSettings.get(KEY_EXTERNAL_EXTENSIONS);
611
+ if (persisted && Array.isArray(persisted)) {
612
+ persisted.forEach((ext) => {
613
+ this.extensions[ext.id] = ext;
614
+ });
615
+ defaultLogger.debug(`Loaded ${persisted.length} persisted external extensions`);
616
+ }
617
+ } catch (error) {
618
+ defaultLogger.error(`Failed to load persisted external extensions: ${error}`);
619
+ }
620
+ }
621
+ async savePersistedExternalExtensions() {
622
+ try {
623
+ const externalExtensions = Object.values(this.extensions).filter((ext) => ext.external);
624
+ await appSettings.set(KEY_EXTERNAL_EXTENSIONS, externalExtensions);
625
+ } catch (error) {
626
+ defaultLogger.error(`Failed to save persisted external extensions: ${error}`);
627
+ }
628
+ }
629
+ async checkExtensionsConfig() {
630
+ if (!this.extensionsSettings) {
631
+ this.extensionsSettings = await appSettings.get(KEY_EXTENSIONS_CONFIG);
632
+ if (!this.extensionsSettings) {
633
+ await appSettings.set(KEY_EXTENSIONS_CONFIG, []);
634
+ this.extensionsSettings = await appSettings.get(KEY_EXTENSIONS_CONFIG);
635
+ }
636
+ publish(TOPIC_EXTENSIONS_CHANGED, this.extensionsSettings);
637
+ }
638
+ }
639
+ registerExtension(extension) {
640
+ this.extensions[extension.id] = extension;
641
+ if (extension.external) {
642
+ this.savePersistedExternalExtensions().catch((err) => {
643
+ defaultLogger.error(`Failed to persist external extension: ${err}`);
644
+ });
645
+ }
646
+ publish(TOPIC_EXTENSIONS_CHANGED, this.extensionsSettings);
647
+ }
648
+ /**
649
+ * Load an extension from a URL and register it.
650
+ * The module at the URL must export a default function that receives uiContext.
651
+ * The extension will register its contributions when loaded.
652
+ *
653
+ * Supports:
654
+ * - Direct URLs (http/https)
655
+ * - esm.sh URLs
656
+ * - Source identifiers (npm packages, GitHub repos, JSR packages, PR packages)
657
+ * Examples: 'react@18', 'gh/user/repo', 'jsr/@std/encoding@1.0.0', 'pr/owner/repo@commit'
658
+ *
659
+ * @param url - URL or source identifier to the extension module
660
+ * @param extensionId - Optional extension ID. If not provided, generates one from the URL.
661
+ * @returns Promise that resolves to the extension ID when the extension is loaded
662
+ */
663
+ async loadExtensionFromUrl(url, extensionId) {
664
+ defaultLogger.info(`Loading extension from URL: ${url}...`);
665
+ try {
666
+ let finalUrl = url;
667
+ let extensionName = `Extension from ${url}`;
668
+ if (esmShService.isSourceIdentifier(url)) {
669
+ const packageName = esmShService.extractPackageName(url);
670
+ if (packageName) {
671
+ extensionName = `Extension: ${packageName}`;
672
+ }
673
+ finalUrl = esmShService.normalizeToEsmSh(url);
674
+ defaultLogger.debug(`Converted source identifier to esm.sh URL: ${url} -> ${finalUrl}`);
675
+ }
676
+ const id = extensionId || `url:${finalUrl}`;
677
+ if (this.isEnabled(id)) {
678
+ defaultLogger.info(`Extension from URL ${finalUrl} is already enabled`);
679
+ return id;
680
+ }
681
+ if (!this.extensions[id]) {
682
+ const extension = {
683
+ id,
684
+ name: extensionName,
685
+ description: `Extension loaded from: ${url}`,
686
+ url: finalUrl
687
+ };
688
+ this.registerExtension(extension);
689
+ defaultLogger.info(`Registered extension from URL: ${id}`);
690
+ }
691
+ this.enable(id, false);
692
+ defaultLogger.info(`Successfully enabled extension from URL: ${finalUrl}`);
693
+ return id;
694
+ } catch (error) {
695
+ defaultLogger.error(`Failed to load extension from URL ${url}: ${error}`);
696
+ throw error;
697
+ }
698
+ }
699
+ getExtensions() {
700
+ return Object.values(this.extensions);
701
+ }
702
+ isEnabled(extensionId) {
703
+ this.checkExtensionsConfig();
704
+ return !!this.extensionsSettings?.find((setting) => setting.id === extensionId && setting.enabled);
705
+ }
706
+ isLoaded(extensionId) {
707
+ return this.loadedExtensions.has(extensionId);
708
+ }
709
+ enable(extensionId, informUser = false) {
710
+ if (this.isEnabled(extensionId)) {
711
+ return;
712
+ }
713
+ defaultLogger.debug(`Loading extension: ${extensionId}`);
714
+ this.load(extensionId).then(() => {
715
+ this.updateEnablement(extensionId, true, informUser);
716
+ }).catch((_e) => {
717
+ defaultLogger.error(`Could not load extension: ${extensionId}: ${_e}`);
718
+ });
719
+ }
720
+ /**
721
+ * Loads an extension and all its dependencies.
722
+ *
723
+ * Features:
724
+ * - Automatically loads all dependencies recursively before loading the extension
725
+ * - Ensures each extension is loaded only once (idempotent)
726
+ * - Dependencies are loaded in the order they are declared
727
+ * - If an extension is already being loaded, waits for that load to complete
728
+ * - Detects circular dependencies in the dependency chain
729
+ *
730
+ * @param extensionId - The ID of the extension to load
731
+ * @param loadingChain - Internal parameter to track the dependency chain for circular detection
732
+ * @throws Error if the extension is not found or if a circular dependency is detected
733
+ *
734
+ * @example
735
+ * ```typescript
736
+ * // This will automatically load @eclipse-lyra/extension-python-runtime first
737
+ * await extensionRegistry.load('@eclipse-lyra/extension-notebook')
738
+ * ```
739
+ */
740
+ async load(extensionId, loadingChain = []) {
741
+ if (this.loadedExtensions.has(extensionId)) {
742
+ return;
743
+ }
744
+ const existingPromise = this.loadingPromises.get(extensionId);
745
+ if (existingPromise) {
746
+ return existingPromise;
747
+ }
748
+ if (loadingChain.includes(extensionId)) {
749
+ const chain = [...loadingChain, extensionId].join(" → ");
750
+ throw new Error(`Circular dependency detected: ${chain}`);
751
+ }
752
+ const extension = this.extensions[extensionId];
753
+ if (!extension) {
754
+ throw new Error("Extension not found: " + extensionId);
755
+ }
756
+ const loadingPromise = (async () => {
757
+ try {
758
+ if (extension.dependencies && extension.dependencies.length > 0) {
759
+ defaultLogger.debug(`Loading dependencies for ${extensionId}: ${extension.dependencies.join(", ")}`);
760
+ const newChain = [...loadingChain, extensionId];
761
+ for (const depId of extension.dependencies) {
762
+ await this.load(depId, newChain);
763
+ if (!this.isEnabled(depId)) {
764
+ await this.updateEnablementAsync(depId, true, false);
765
+ defaultLogger.debug(`Auto-enabled dependency: ${depId}`);
766
+ }
767
+ }
768
+ }
769
+ const module = await taskService.runAsync("Loading extension: " + extension.name, async () => {
770
+ if (extension.loader) {
771
+ return extension.loader();
772
+ } else if (extension.url) {
773
+ let finalUrl = extension.url;
774
+ if (esmShService.isSourceIdentifier(extension.url)) {
775
+ finalUrl = esmShService.normalizeToEsmSh(extension.url);
776
+ defaultLogger.debug(`Normalized extension URL: ${extension.url} -> ${finalUrl}`);
777
+ }
778
+ return import(
779
+ /* @vite-ignore */
780
+ finalUrl
781
+ );
782
+ }
783
+ });
784
+ this.loadedExtensions.add(extensionId);
785
+ if (module?.default instanceof Function) {
786
+ defaultLogger.debug(`Executing extension function for: ${extensionId}`);
787
+ try {
788
+ module?.default(uiContext.getProxy());
789
+ defaultLogger.debug(`Extension function executed successfully: ${extensionId}`);
790
+ } catch (error) {
791
+ defaultLogger.error(`Error executing extension function for ${extensionId}: ${error}`);
792
+ throw error;
793
+ }
794
+ } else {
795
+ defaultLogger.warn(`Extension ${extensionId} does not export a default function`);
796
+ }
797
+ defaultLogger.debug(`Extension loaded: ${extensionId}`);
798
+ } catch (error) {
799
+ this.loadedExtensions.delete(extensionId);
800
+ throw error;
801
+ } finally {
802
+ this.loadingPromises.delete(extensionId);
803
+ }
804
+ })();
805
+ this.loadingPromises.set(extensionId, loadingPromise);
806
+ return loadingPromise;
807
+ }
808
+ disable(extensionId, informUser = false) {
809
+ if (!this.isEnabled(extensionId)) {
810
+ return;
811
+ }
812
+ this.updateEnablement(extensionId, false, informUser);
813
+ }
814
+ updateEnablement(extensionId, enabled, informUser) {
815
+ this.checkExtensionsConfig().then(() => {
816
+ const extension = this.extensionsSettings?.find((e) => e.id == extensionId);
817
+ if (extension) {
818
+ extension.enabled = enabled;
819
+ } else {
820
+ this.extensionsSettings?.push({ id: extensionId, enabled });
821
+ }
822
+ appSettings.set(KEY_EXTENSIONS_CONFIG, this.extensionsSettings).then(() => {
823
+ if (informUser) {
824
+ const extObj = this.extensions[extensionId];
825
+ if (enabled) {
826
+ toastInfo(extObj.name + " enabled.");
827
+ } else {
828
+ toastInfo(extObj.name + " disabled - Please restart to take effect");
829
+ }
830
+ }
831
+ publish(TOPIC_EXTENSIONS_CHANGED, this.extensionsSettings);
832
+ });
833
+ });
834
+ }
835
+ async updateEnablementAsync(extensionId, enabled, informUser) {
836
+ await this.checkExtensionsConfig();
837
+ const extension = this.extensionsSettings?.find((e) => e.id == extensionId);
838
+ if (extension) {
839
+ extension.enabled = enabled;
840
+ } else {
841
+ this.extensionsSettings?.push({ id: extensionId, enabled });
842
+ }
843
+ await appSettings.set(KEY_EXTENSIONS_CONFIG, this.extensionsSettings);
844
+ if (informUser) {
845
+ const extObj = this.extensions[extensionId];
846
+ if (enabled) {
847
+ toastInfo(extObj.name + " enabled.");
848
+ } else {
849
+ toastInfo(extObj.name + " disabled - Please restart to take effect");
850
+ }
851
+ }
852
+ publish(TOPIC_EXTENSIONS_CHANGED, this.extensionsSettings);
853
+ }
854
+ }
855
+ defaultLogger.debug("ExtensionRegistry initializing...");
856
+ const extensionRegistry = new ExtensionRegistry();
857
+ rootContext.put("extensionRegistry", extensionRegistry);
858
+ defaultLogger.debug("ExtensionRegistry initialized");
859
+ const MODIFIER_FIELDS = ["alt", "ctrl", "meta", "shift"];
860
+ const MODIFIER_ALIASES = {
861
+ CTRL: "ctrl",
862
+ CONTROL: "ctrl",
863
+ ALT: "alt",
864
+ OPTION: "alt",
865
+ SHIFT: "shift",
866
+ META: "meta",
867
+ CMD: "meta",
868
+ COMMAND: "meta",
869
+ WIN: "meta",
870
+ WINDOWS: "meta"
871
+ };
872
+ const MODIFIER_DISPLAY = {
873
+ ctrl: "Ctrl",
874
+ alt: "Alt",
875
+ shift: "Shift",
876
+ meta: "Cmd"
877
+ };
878
+ const KEY_NORMALIZE = {
879
+ SPACE: " ",
880
+ ESC: "ESCAPE",
881
+ RETURN: "ENTER",
882
+ LEFT: "ARROWLEFT",
883
+ RIGHT: "ARROWRIGHT",
884
+ UP: "ARROWUP",
885
+ DOWN: "ARROWDOWN",
886
+ DEL: "DELETE",
887
+ INS: "INSERT",
888
+ PAGEUP: "PAGEUP",
889
+ PAGEDOWN: "PAGEDOWN"
890
+ };
891
+ const KNOWN_MODIFIERS = new Set(Object.keys(MODIFIER_ALIASES));
892
+ function normalizeKey(key) {
893
+ return KEY_NORMALIZE[key] ?? key;
894
+ }
895
+ class KeyBindingManager {
896
+ constructor() {
897
+ this.bindings = /* @__PURE__ */ new Map();
898
+ this.enabled = true;
899
+ document.addEventListener("keydown", this.handleKeyDown.bind(this), true);
900
+ this.registerExistingCommandBindings();
901
+ subscribe(TOPIC_COMMAND_REGISTERED, (command) => {
902
+ if (command.keyBinding) {
903
+ this.registerKeyBinding(command.id, command.keyBinding);
904
+ }
905
+ });
906
+ }
907
+ registerExistingCommandBindings() {
908
+ const commands = commandRegistry.listCommands();
909
+ Object.values(commands).forEach((command) => {
910
+ if (command.keyBinding) {
911
+ this.registerKeyBinding(command.id, command.keyBinding);
912
+ }
913
+ });
914
+ }
915
+ /**
916
+ * Parse a key binding string like "CTRL+SHIFT+P" or "ALT+S"
917
+ */
918
+ parseKeyBinding(keyBindingString) {
919
+ if (!keyBindingString || keyBindingString.trim() === "") {
920
+ return null;
921
+ }
922
+ const parts = keyBindingString.toUpperCase().split("+").map((p) => p.trim());
923
+ if (parts.length === 0) {
924
+ return null;
925
+ }
926
+ const key = parts[parts.length - 1];
927
+ const modifiers = parts.slice(0, -1);
928
+ if (parts.length === 1 && KNOWN_MODIFIERS.has(key)) {
929
+ return null;
930
+ }
931
+ const binding = { ctrl: false, alt: false, shift: false, meta: false };
932
+ for (const modifier of modifiers) {
933
+ const field = MODIFIER_ALIASES[modifier];
934
+ if (field === void 0) return null;
935
+ binding[field] = true;
936
+ }
937
+ binding.key = normalizeKey(key);
938
+ return binding;
939
+ }
940
+ /**
941
+ * Create a unique key for a binding
942
+ */
943
+ getBindingKey(binding) {
944
+ const parts = [...MODIFIER_FIELDS.filter((f) => binding[f]), binding.key.toUpperCase()];
945
+ return parts.join("+");
946
+ }
947
+ /**
948
+ * Register a key binding for a command
949
+ */
950
+ registerKeyBinding(commandId, keyBindingString) {
951
+ const binding = this.parseKeyBinding(keyBindingString);
952
+ if (!binding) {
953
+ defaultLogger.error(`Invalid key binding: ${keyBindingString}`);
954
+ return false;
955
+ }
956
+ binding.commandId = commandId;
957
+ const bindingKey = this.getBindingKey(binding);
958
+ if (!this.bindings.has(bindingKey)) {
959
+ this.bindings.set(bindingKey, []);
960
+ }
961
+ const existing = this.bindings.get(bindingKey);
962
+ const sameCommand = existing.find((b) => b.commandId === commandId);
963
+ if (sameCommand) {
964
+ defaultLogger.error(`Key binding ${keyBindingString} already registered for command ${commandId}`);
965
+ return false;
966
+ }
967
+ const otherCommand = existing.find((b) => b.commandId !== commandId);
968
+ if (otherCommand) {
969
+ defaultLogger.warn(`Key binding ${keyBindingString} already used by command ${otherCommand.commandId}; refusing for ${commandId}`);
970
+ return false;
971
+ }
972
+ existing.push(binding);
973
+ defaultLogger.debug(`Key binding registered: ${keyBindingString} -> ${commandId}`);
974
+ return true;
975
+ }
976
+ /**
977
+ * Unregister a key binding
978
+ */
979
+ unregisterKeyBinding(commandId, keyBindingString) {
980
+ if (keyBindingString) {
981
+ const binding = this.parseKeyBinding(keyBindingString);
982
+ if (binding) {
983
+ const bindingKey = this.getBindingKey(binding);
984
+ const bindings = this.bindings.get(bindingKey);
985
+ if (bindings) {
986
+ const filtered = bindings.filter((b) => b.commandId !== commandId);
987
+ if (filtered.length === 0) {
988
+ this.bindings.delete(bindingKey);
989
+ } else {
990
+ this.bindings.set(bindingKey, filtered);
991
+ }
992
+ }
993
+ }
994
+ } else {
995
+ for (const [key, bindings] of this.bindings.entries()) {
996
+ const filtered = bindings.filter((b) => b.commandId !== commandId);
997
+ if (filtered.length === 0) {
998
+ this.bindings.delete(key);
999
+ } else {
1000
+ this.bindings.set(key, filtered);
1001
+ }
1002
+ }
1003
+ }
1004
+ }
1005
+ /**
1006
+ * Get all key bindings for a command
1007
+ */
1008
+ getKeyBindingsForCommand(commandId) {
1009
+ const result = [];
1010
+ for (const bindings of this.bindings.values()) {
1011
+ for (const binding of bindings) {
1012
+ if (binding.commandId === commandId) {
1013
+ result.push(this.formatKeyBinding(binding));
1014
+ }
1015
+ }
1016
+ }
1017
+ return result.sort();
1018
+ }
1019
+ /**
1020
+ * Format a key binding for display
1021
+ */
1022
+ formatKeyBinding(binding) {
1023
+ const parts = MODIFIER_FIELDS.filter((f) => binding[f]).map((f) => MODIFIER_DISPLAY[f]);
1024
+ const key = binding.key.length === 1 ? binding.key.toUpperCase() : binding.key.charAt(0).toUpperCase() + binding.key.slice(1).toLowerCase();
1025
+ parts.push(key);
1026
+ return parts.join("+");
1027
+ }
1028
+ /**
1029
+ * Handle keyboard events
1030
+ */
1031
+ handleKeyDown(event) {
1032
+ if (!this.enabled) {
1033
+ return;
1034
+ }
1035
+ const eventBinding = {
1036
+ commandId: "",
1037
+ key: normalizeKey(event.key.toUpperCase()),
1038
+ ctrl: event.ctrlKey,
1039
+ alt: event.altKey,
1040
+ shift: event.shiftKey,
1041
+ meta: event.metaKey
1042
+ };
1043
+ const bindingKey = this.getBindingKey(eventBinding);
1044
+ const bindings = this.bindings.get(bindingKey);
1045
+ if (bindings && bindings.length > 0) {
1046
+ const binding = bindings[0];
1047
+ try {
1048
+ event.preventDefault();
1049
+ event.stopPropagation();
1050
+ const context = commandRegistry.createExecutionContext({});
1051
+ commandRegistry.execute(binding.commandId, context);
1052
+ defaultLogger.debug(`Executed command via key binding: ${binding.commandId}`);
1053
+ } catch (error) {
1054
+ defaultLogger.error(`Failed to execute command ${binding.commandId}: ${error.message}`);
1055
+ }
1056
+ }
1057
+ }
1058
+ /**
1059
+ * Enable or disable key binding handling
1060
+ */
1061
+ setEnabled(enabled) {
1062
+ this.enabled = enabled;
1063
+ }
1064
+ /**
1065
+ * Check if key binding handling is enabled
1066
+ */
1067
+ isEnabled() {
1068
+ return this.enabled;
1069
+ }
1070
+ /**
1071
+ * Get all registered key bindings
1072
+ */
1073
+ getAllBindings() {
1074
+ const copy = /* @__PURE__ */ new Map();
1075
+ for (const [key, bindings] of this.bindings) {
1076
+ copy.set(key, [...bindings]);
1077
+ }
1078
+ return copy;
1079
+ }
1080
+ /**
1081
+ * Clear all key bindings
1082
+ */
1083
+ clearAll() {
1084
+ this.bindings.clear();
1085
+ }
1086
+ }
1087
+ const keyBindingManager = new KeyBindingManager();
1088
+ rootContext.put("keyBindingManager", keyBindingManager);
1089
+ class LyraElement extends LyraWidget {
1090
+ constructor() {
1091
+ super(...arguments);
1092
+ this.settingsKey = null;
1093
+ }
1094
+ /**
1095
+ * Builds a unique DOM tree path for this element.
1096
+ * Uses id attribute if available, otherwise builds a path based on tag names and indices.
1097
+ * Useful for generating unique settings keys.
1098
+ *
1099
+ * @returns A string representing the DOM path, or null if path cannot be determined
1100
+ */
1101
+ buildDOMTreePath() {
1102
+ const pathParts = [];
1103
+ let current = this;
1104
+ while (current && current !== document.body && current !== document.documentElement) {
1105
+ const id = current.getAttribute("id");
1106
+ if (id) {
1107
+ pathParts.unshift(`#${id}`);
1108
+ break;
1109
+ }
1110
+ const tagName = current.tagName.toLowerCase();
1111
+ const parent = current.parentElement;
1112
+ if (!parent) {
1113
+ break;
1114
+ }
1115
+ const siblings = Array.from(parent.children).filter(
1116
+ (child) => child.tagName.toLowerCase() === tagName
1117
+ );
1118
+ const index = siblings.indexOf(current);
1119
+ if (index >= 0) {
1120
+ pathParts.unshift(`${tagName}:${index}`);
1121
+ } else {
1122
+ pathParts.unshift(tagName);
1123
+ }
1124
+ current = parent;
1125
+ }
1126
+ return pathParts.length > 0 ? pathParts.join(" > ") : null;
1127
+ }
1128
+ /**
1129
+ * Initializes the settings key for this element using the element's tag name.
1130
+ * Called automatically on first access via getDialogSetting() or setDialogSetting().
1131
+ */
1132
+ initializeSettingsKey() {
1133
+ if (!this.settingsKey) {
1134
+ const prefix = this.tagName.toLowerCase();
1135
+ const id = this.getAttribute("id");
1136
+ if (id) {
1137
+ this.settingsKey = `${prefix}:${id}`;
1138
+ return;
1139
+ }
1140
+ const path = this.buildDOMTreePath();
1141
+ if (path) {
1142
+ this.settingsKey = `${prefix}:${path}`;
1143
+ }
1144
+ }
1145
+ }
1146
+ /**
1147
+ * Gets a dialog setting value for this element.
1148
+ * Automatically initializes the settings key on first access if not already set.
1149
+ *
1150
+ * @returns The persisted setting value, or undefined if not found
1151
+ */
1152
+ async getDialogSetting() {
1153
+ this.initializeSettingsKey();
1154
+ if (!this.settingsKey) {
1155
+ return void 0;
1156
+ }
1157
+ return await appSettings.getDialogSetting(this.settingsKey);
1158
+ }
1159
+ /**
1160
+ * Saves a dialog setting value for this element.
1161
+ * Automatically initializes the settings key on first access if not already set.
1162
+ *
1163
+ * @param value - The value to persist
1164
+ */
1165
+ async setDialogSetting(value) {
1166
+ this.initializeSettingsKey();
1167
+ if (!this.settingsKey) {
1168
+ return;
1169
+ }
1170
+ await appSettings.setDialogSetting(this.settingsKey, value);
1171
+ }
1172
+ }
1173
+ const _LyraDialogContent = class _LyraDialogContent extends LyraElement {
1174
+ dispose() {
1175
+ }
1176
+ getResult() {
1177
+ return void 0;
1178
+ }
1179
+ renderMessage(message, markdown = false) {
1180
+ if (markdown) {
1181
+ const htmlContent = marked.parse(message, { async: false });
1182
+ return html`<div class="dialog-message" style="white-space: normal;">${unsafeHTML(htmlContent)}</div>`;
1183
+ }
1184
+ return html`<div class="dialog-message" style="white-space: pre-line;">${message}</div>`;
1185
+ }
1186
+ };
1187
+ _LyraDialogContent.styles = [
1188
+ css`
1189
+ .dialog-message {
1190
+ margin-bottom: 0.5rem;
1191
+ color: var(--wa-color-text-normal);
1192
+ }
1193
+ `
1194
+ ];
1195
+ let LyraDialogContent = _LyraDialogContent;
1196
+ const logger$1 = createLogger("DialogService");
1197
+ const DIALOG_CONTRIBUTION_TARGET = "dialogs";
1198
+ const OK_BUTTON = {
1199
+ id: "ok",
1200
+ label: "OK",
1201
+ variant: "primary"
1202
+ };
1203
+ const CANCEL_BUTTON = {
1204
+ id: "cancel",
1205
+ label: "Cancel",
1206
+ variant: "default"
1207
+ };
1208
+ const CLOSE_BUTTON = {
1209
+ id: "close",
1210
+ label: "Close",
1211
+ variant: "default"
1212
+ };
1213
+ let dialogContainer = null;
1214
+ function getDialogContainer() {
1215
+ if (!dialogContainer || !document.body.contains(dialogContainer)) {
1216
+ dialogContainer = document.createElement("div");
1217
+ dialogContainer.id = "global-dialog-container";
1218
+ document.body.appendChild(dialogContainer);
1219
+ }
1220
+ return dialogContainer;
1221
+ }
1222
+ class DialogService {
1223
+ constructor() {
1224
+ this.contributions = /* @__PURE__ */ new Map();
1225
+ this.loadContributions();
1226
+ subscribe(TOPIC_CONTRIBUTEIONS_CHANGED, (event) => {
1227
+ if (event.target === DIALOG_CONTRIBUTION_TARGET) {
1228
+ this.loadContributions();
1229
+ }
1230
+ });
1231
+ }
1232
+ loadContributions() {
1233
+ const contributions = contributionRegistry.getContributions(DIALOG_CONTRIBUTION_TARGET);
1234
+ this.contributions.clear();
1235
+ for (const contribution of contributions) {
1236
+ if (!contribution.id) {
1237
+ logger$1.warn("Dialog contribution missing id, skipping");
1238
+ continue;
1239
+ }
1240
+ if (!contribution.component) {
1241
+ logger$1.warn(`Dialog contribution "${contribution.id}" has no component function, skipping`);
1242
+ continue;
1243
+ }
1244
+ if (!contribution.onButton) {
1245
+ logger$1.warn(`Dialog contribution "${contribution.id}" has no onButton callback, skipping`);
1246
+ continue;
1247
+ }
1248
+ this.contributions.set(contribution.id, contribution);
1249
+ logger$1.debug(`Loaded dialog contribution: ${contribution.id}`);
1250
+ }
1251
+ logger$1.info(`Loaded ${this.contributions.size} dialog contributions`);
1252
+ }
1253
+ async open(dialogId, state2) {
1254
+ const contribution = this.contributions.get(dialogId);
1255
+ if (!contribution) {
1256
+ logger$1.error(`Dialog "${dialogId}" not found`);
1257
+ throw new Error(`Dialog "${dialogId}" not found`);
1258
+ }
1259
+ return new Promise((resolve) => {
1260
+ const container = getDialogContainer();
1261
+ let isOpen = true;
1262
+ let dialogContentElement = null;
1263
+ const cleanup = async () => {
1264
+ if (!isOpen) return;
1265
+ isOpen = false;
1266
+ if (dialogContentElement) {
1267
+ try {
1268
+ await dialogContentElement.dispose();
1269
+ } catch (error) {
1270
+ const errorMessage = error instanceof Error ? error.message : String(error);
1271
+ logger$1.error(`Error disposing dialog content for "${dialogId}": ${errorMessage}`);
1272
+ }
1273
+ }
1274
+ try {
1275
+ const result = dialogContentElement ? dialogContentElement.getResult() : void 0;
1276
+ await contribution.onButton("close", result, stateWithClose);
1277
+ } catch (error) {
1278
+ const errorMessage = error instanceof Error ? error.message : String(error);
1279
+ logger$1.error(`Error executing close callback for dialog "${dialogId}": ${errorMessage}`);
1280
+ }
1281
+ render(html``, container);
1282
+ resolve();
1283
+ };
1284
+ const handleButtonClick = async (buttonId) => {
1285
+ try {
1286
+ const result = dialogContentElement ? dialogContentElement.getResult() : void 0;
1287
+ const shouldClose = await contribution.onButton(buttonId, result, stateWithClose);
1288
+ if (shouldClose !== false) {
1289
+ cleanup();
1290
+ }
1291
+ } catch (error) {
1292
+ const errorMessage = error instanceof Error ? error.message : String(error);
1293
+ logger$1.error(`Error executing button callback for dialog "${dialogId}": ${errorMessage}`);
1294
+ cleanup();
1295
+ }
1296
+ };
1297
+ const buttons = contribution.buttons && contribution.buttons.length > 0 ? contribution.buttons : [OK_BUTTON];
1298
+ if (state2 && typeof state2 === "object") {
1299
+ state2.close = cleanup;
1300
+ }
1301
+ const stateWithClose = { ...state2, close: cleanup };
1302
+ const template = html`
1303
+ <wa-dialog label="${contribution.label || dialogId}" open @wa-request-close=${cleanup}>
1304
+ <style>
1305
+ .dialog-service-content {
1306
+ display: flex;
1307
+ flex-direction: column;
1308
+ gap: 1rem;
1309
+ padding: 1rem;
1310
+ min-width: 400px;
1311
+ }
1312
+
1313
+ .dialog-service-footer {
1314
+ display: flex;
1315
+ gap: 0.5rem;
1316
+ justify-content: flex-end;
1317
+ margin-top: 1rem;
1318
+ padding-top: 1rem;
1319
+ border-top: 1px solid var(--wa-color-neutral-20);
1320
+ }
1321
+
1322
+ :host-context(.wa-light) .dialog-service-footer {
1323
+ border-top-color: var(--wa-color-neutral-80);
1324
+ }
1325
+ </style>
1326
+
1327
+ <div class="dialog-service-content"
1328
+ @dialog-ok=${() => {
1329
+ const okButton = buttons.find((b) => b.id === "ok");
1330
+ if (okButton) {
1331
+ handleButtonClick(okButton.id);
1332
+ }
1333
+ }}
1334
+ @dialog-cancel=${() => {
1335
+ const cancelButton = buttons.find((b) => b.id === "cancel");
1336
+ if (cancelButton) {
1337
+ handleButtonClick(cancelButton.id);
1338
+ } else {
1339
+ cleanup();
1340
+ }
1341
+ }}>
1342
+ ${contribution.component(state2)}
1343
+
1344
+ <div class="dialog-service-footer">
1345
+ ${buttons.map((button) => html`
1346
+ <wa-button
1347
+ variant="${button.variant || "default"}"
1348
+ ?disabled=${button.disabled}
1349
+ @click=${() => handleButtonClick(button.id)}
1350
+ >
1351
+ ${button.label}
1352
+ </wa-button>
1353
+ `)}
1354
+ </div>
1355
+ </div>
1356
+ </wa-dialog>
1357
+ `;
1358
+ render(template, container);
1359
+ (async () => {
1360
+ const allElements = Array.from(container.querySelectorAll("*"));
1361
+ for (const element of allElements) {
1362
+ if (element instanceof LyraDialogContent) {
1363
+ await element.updateComplete;
1364
+ dialogContentElement = element;
1365
+ break;
1366
+ }
1367
+ }
1368
+ })();
1369
+ });
1370
+ }
1371
+ getDialogIds() {
1372
+ return Array.from(this.contributions.keys());
1373
+ }
1374
+ hasDialog(dialogId) {
1375
+ return this.contributions.has(dialogId);
1376
+ }
1377
+ }
1378
+ const dialogService = new DialogService();
1379
+ rootContext.put("dialogService", dialogService);
1380
+ class LyraContainer extends LyraElement {
1381
+ }
1382
+ var __defProp$9 = Object.defineProperty;
1383
+ var __decorateClass$9 = (decorators, target, key, kind) => {
1384
+ var result = void 0;
1385
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
1386
+ if (decorator = decorators[i])
1387
+ result = decorator(target, key, result) || result;
1388
+ if (result) __defProp$9(target, key, result);
1389
+ return result;
1390
+ };
1391
+ class LyraPart extends LyraContainer {
1392
+ constructor() {
1393
+ super(...arguments);
1394
+ this.dirty = false;
1395
+ this.isEditor = false;
1396
+ }
1397
+ getCommandStack() {
1398
+ return this.commandStack;
1399
+ }
1400
+ /**
1401
+ * Override this method to provide toolbar content for this part.
1402
+ * This is a lightweight alternative to registering toolbar contributions
1403
+ * for actions that are scoped to this part instance.
1404
+ *
1405
+ * IMPORTANT: Event handlers MUST use arrow functions to preserve the component's 'this' context.
1406
+ * The toolbar template is rendered in a different component (lyra-toolbar), so direct method
1407
+ * references lose their binding.
1408
+ *
1409
+ * ✅ Correct:
1410
+ * @click=${() => this.myMethod()}
1411
+ * @click=${(e) => this.handleClick(e)}
1412
+ *
1413
+ * ❌ Wrong (this will be bound to the toolbar, not your component):
1414
+ * @click=${this.myMethod}
1415
+ *
1416
+ * Example:
1417
+ * ```typescript
1418
+ * protected renderToolbar() {
1419
+ * return html`
1420
+ * <wa-button @click=${() => this.save()} title="Save">
1421
+ * <wa-icon name="save"></wa-icon>
1422
+ * </wa-button>
1423
+ * `;
1424
+ * }
1425
+ * ```
1426
+ *
1427
+ * @returns TemplateResult with toolbar items, or nothing if no toolbar needed
1428
+ */
1429
+ renderToolbar() {
1430
+ return nothing;
1431
+ }
1432
+ /**
1433
+ * Call this method to update the toolbar when the component's state changes.
1434
+ * This triggers a re-render of the toolbar with the latest content from renderToolbar().
1435
+ */
1436
+ updateToolbar() {
1437
+ this.dispatchEvent(new CustomEvent("part-toolbar-changed", {
1438
+ bubbles: true,
1439
+ composed: true
1440
+ }));
1441
+ }
1442
+ /**
1443
+ * Override this method to provide context menu content for this part.
1444
+ * This is a lightweight alternative to registering context menu contributions
1445
+ * for actions that are scoped to this part instance.
1446
+ *
1447
+ * IMPORTANT: Event handlers MUST use arrow functions to preserve the component's 'this' context.
1448
+ * The context menu is rendered in a different component (contextmenu), so direct method
1449
+ * references lose their binding.
1450
+ *
1451
+ * ✅ Correct:
1452
+ * @click=${() => this.myMethod()}
1453
+ * @click=${(e) => this.handleClick(e)}
1454
+ *
1455
+ * ❌ Wrong (this will be bound to the context menu, not your component):
1456
+ * @click=${this.myMethod}
1457
+ *
1458
+ * Example:
1459
+ * ```typescript
1460
+ * protected renderContextMenu() {
1461
+ * return html`
1462
+ * <wa-dropdown-item @click=${() => this.open()}>
1463
+ * <wa-icon name="folder-open"></wa-icon>
1464
+ * Open
1465
+ * </wa-dropdown-item>
1466
+ * <wa-divider></wa-divider>
1467
+ * <wa-dropdown-item @click=${() => this.delete()}>
1468
+ * <wa-icon name="trash"></wa-icon>
1469
+ * Delete
1470
+ * </wa-dropdown-item>
1471
+ * `;
1472
+ * }
1473
+ * ```
1474
+ *
1475
+ * @returns TemplateResult with context menu items, or nothing if no context menu needed
1476
+ */
1477
+ renderContextMenu() {
1478
+ return nothing;
1479
+ }
1480
+ /**
1481
+ * Call this method to update the context menu when the component's state changes.
1482
+ * This triggers a re-render of the context menu with the latest content from renderContextMenu().
1483
+ */
1484
+ updateContextMenu() {
1485
+ this.dispatchEvent(new CustomEvent("part-contextmenu-changed", {
1486
+ bubbles: true,
1487
+ composed: true
1488
+ }));
1489
+ }
1490
+ updated(_changedProperties) {
1491
+ super.updated(_changedProperties);
1492
+ if (_changedProperties.has("dirty")) {
1493
+ const dirty = _changedProperties.get("dirty");
1494
+ if (dirty !== void 0) {
1495
+ this.dispatchEvent(new CustomEvent("dirty", { detail: this.dirty, bubbles: true }));
1496
+ }
1497
+ }
1498
+ }
1499
+ doClose() {
1500
+ }
1501
+ disconnectedCallback() {
1502
+ super.disconnectedCallback();
1503
+ }
1504
+ close() {
1505
+ this.doClose();
1506
+ }
1507
+ connectedCallback() {
1508
+ super.connectedCallback();
1509
+ }
1510
+ save() {
1511
+ }
1512
+ isDirty() {
1513
+ return this.dirty;
1514
+ }
1515
+ markDirty(dirty) {
1516
+ this.dirty = dirty;
1517
+ partDirtySignal.set(null);
1518
+ partDirtySignal.set(this);
1519
+ activePartSignal.set(null);
1520
+ activePartSignal.set(this);
1521
+ }
1522
+ }
1523
+ __decorateClass$9([
1524
+ property()
1525
+ ], LyraPart.prototype, "dirty");
1526
+ const logger = createLogger("AppLoader");
1527
+ function getErrorMessage(error) {
1528
+ return error instanceof Error ? error.message : String(error);
1529
+ }
1530
+ function extractLastPathSegment(urlString) {
1531
+ try {
1532
+ const url = new URL(urlString);
1533
+ const pathSegments = url.pathname.split("/").filter(Boolean);
1534
+ return pathSegments.length > 0 ? pathSegments[pathSegments.length - 1] : void 0;
1535
+ } catch {
1536
+ const pathSegments = urlString.split("/").filter(Boolean);
1537
+ return pathSegments.length > 0 ? pathSegments[pathSegments.length - 1] : void 0;
1538
+ }
1539
+ }
1540
+ function extractAppIdFromPath() {
1541
+ const pathname = window.location.pathname;
1542
+ const pathSegments = pathname.split("/").filter(Boolean);
1543
+ if (pathSegments.length === 0) {
1544
+ return void 0;
1545
+ }
1546
+ const firstSegment = pathSegments[0];
1547
+ if (!firstSegment || firstSegment === "index.html" || firstSegment.endsWith(".html")) {
1548
+ return void 0;
1549
+ }
1550
+ return firstSegment;
1551
+ }
1552
+ const _AppLoaderService = class _AppLoaderService {
1553
+ constructor() {
1554
+ this.apps = /* @__PURE__ */ new Map();
1555
+ this.started = false;
1556
+ this.container = document.body;
1557
+ this.systemRequiredExtensions = /* @__PURE__ */ new Set();
1558
+ }
1559
+ /**
1560
+ * Register an application with the framework.
1561
+ * Optionally starts the apploader automatically after registration.
1562
+ *
1563
+ * @param app - Application definition
1564
+ * @param options - Optional configuration for registration and auto-starting
1565
+ */
1566
+ registerApp(app, options) {
1567
+ if (this.apps.has(app.id)) {
1568
+ logger.warn(`App '${app.id}' is already registered. Overwriting.`);
1569
+ }
1570
+ this.apps.set(app.id, app);
1571
+ logger.info(`Registered app: ${app.name} (${app.id}) v${app.version}`);
1572
+ if (options?.defaultAppId) {
1573
+ this.defaultAppId = options.defaultAppId;
1574
+ }
1575
+ if (options?.container) {
1576
+ this.container = options.container;
1577
+ }
1578
+ if (options?.autoStart && !this.started) {
1579
+ this.start();
1580
+ }
1581
+ }
1582
+ registerSystemRequiredExtension(extensionId) {
1583
+ this.systemRequiredExtensions.add(extensionId);
1584
+ }
1585
+ /**
1586
+ * Load an application definition from a URL.
1587
+ * The module at the URL must export an AppDefinition as the default export.
1588
+ *
1589
+ * @param url - URL to the app definition module
1590
+ * @returns Promise that resolves to the loaded AppDefinition
1591
+ */
1592
+ async loadAppFromUrl(url) {
1593
+ logger.info(`Loading app from URL: ${url}...`);
1594
+ try {
1595
+ const module = await import(
1596
+ /* @vite-ignore */
1597
+ url
1598
+ );
1599
+ if (!module.default) {
1600
+ throw new Error(`Module at ${url} does not have a default export`);
1601
+ }
1602
+ const app = module.default;
1603
+ if (!app.id || !app.name || !app.version) {
1604
+ throw new Error(`Module at ${url} does not export a valid AppDefinition`);
1605
+ }
1606
+ logger.info(`Successfully loaded app definition from URL: ${app.name} (${app.id})`);
1607
+ return app;
1608
+ } catch (error) {
1609
+ logger.error(`Failed to load app from URL ${url}: ${getErrorMessage(error)}`);
1610
+ throw error;
1611
+ }
1612
+ }
1613
+ /**
1614
+ * Start the application loader.
1615
+ * Checks URL parameters for app=URL, loads that extension or app if found.
1616
+ * URL parameter has higher precedence than defaultAppId.
1617
+ * Then loads the default app or first registered app.
1618
+ * This method is idempotent - calling it multiple times only starts once.
1619
+ */
1620
+ async start() {
1621
+ if (this.started) {
1622
+ logger.debug("AppLoader already started");
1623
+ return;
1624
+ }
1625
+ this.started = true;
1626
+ logger.info("Starting AppLoader...");
1627
+ const urlParams = new URLSearchParams(window.location.search);
1628
+ const appUrl = urlParams.get("app");
1629
+ const appIdFromUrl = urlParams.get("appId");
1630
+ const appIdFromPath = extractAppIdFromPath();
1631
+ const appsBeforeExtension = this.apps.size;
1632
+ let appIdFromAppUrl;
1633
+ if (appUrl) {
1634
+ appIdFromAppUrl = extractLastPathSegment(appUrl);
1635
+ if (appIdFromAppUrl) {
1636
+ logger.info(`Extracted app ID from URL path: ${appIdFromAppUrl}`);
1637
+ }
1638
+ }
1639
+ if (appIdFromPath) {
1640
+ logger.info(`Extracted app ID from current page path: ${appIdFromPath}`);
1641
+ }
1642
+ if (appUrl) {
1643
+ try {
1644
+ logger.info(`URL parameter 'app' found: ${appUrl}, attempting to load extension or app`);
1645
+ try {
1646
+ await extensionRegistry.loadExtensionFromUrl(appUrl);
1647
+ logger.info(`Successfully loaded extension from URL: ${appUrl}`);
1648
+ } catch (extensionError) {
1649
+ logger.info(`Failed to load as extension, trying as app definition: ${getErrorMessage(extensionError)}`);
1650
+ try {
1651
+ const app = await this.loadAppFromUrl(appUrl);
1652
+ this.registerApp(app);
1653
+ await this.loadApp(app.id, this.container);
1654
+ logger.info(`Successfully loaded app from URL: ${appUrl}`);
1655
+ return;
1656
+ } catch (appError) {
1657
+ logger.error(`Failed to load from URL as both extension and app: ${getErrorMessage(appError)}`);
1658
+ throw appError;
1659
+ }
1660
+ }
1661
+ } catch (error) {
1662
+ logger.error(`Failed to load from URL parameter, falling back to default app: ${getErrorMessage(error)}`);
1663
+ }
1664
+ }
1665
+ const appToLoad = await this.selectAppToLoad({
1666
+ appIdFromUrl,
1667
+ appIdFromPath,
1668
+ appIdFromAppUrl,
1669
+ appsBeforeExtension
1670
+ });
1671
+ if (!appToLoad) {
1672
+ throw new Error("No apps registered");
1673
+ }
1674
+ await this.loadApp(appToLoad, this.container);
1675
+ }
1676
+ /**
1677
+ * Load and initialize an application.
1678
+ *
1679
+ * @param appId - Application identifier (must be already registered)
1680
+ * @param container - Optional DOM element to render into (if provided, auto-renders after loading)
1681
+ * @returns Promise that resolves when app is initialized and rendered
1682
+ */
1683
+ async loadApp(appId, container) {
1684
+ const app = this.apps.get(appId);
1685
+ if (!app) {
1686
+ throw new Error(`App '${appId}' not found. Make sure it's registered.`);
1687
+ }
1688
+ logger.info(`Loading app: ${app.name}...`);
1689
+ if (this.currentApp) {
1690
+ logger.info(`Disposing current app: ${this.currentApp.name}`);
1691
+ if (this.currentApp.dispose) {
1692
+ await this.currentApp.dispose();
1693
+ }
1694
+ if (this.currentApp.extensions && this.currentApp.extensions.length > 0) {
1695
+ logger.info(`Disabling ${this.currentApp.extensions.length} extensions...`);
1696
+ this.currentApp.extensions.forEach((extId) => {
1697
+ extensionRegistry.disable(extId);
1698
+ });
1699
+ }
1700
+ }
1701
+ if (app.contributions) {
1702
+ logger.info("Registering app contributions...");
1703
+ if (app.contributions.ui) {
1704
+ app.contributions.ui.forEach((contribution) => {
1705
+ const target = contribution.target;
1706
+ if (target) {
1707
+ contributionRegistry.registerContribution(target, contribution);
1708
+ }
1709
+ });
1710
+ logger.info(`Registered ${app.contributions.ui.length} UI contributions`);
1711
+ }
1712
+ if (app.contributions.extensions) {
1713
+ app.contributions.extensions.forEach((extension) => {
1714
+ extensionRegistry.registerExtension(extension);
1715
+ });
1716
+ logger.info(`Registered ${app.contributions.extensions.length} app extensions`);
1717
+ }
1718
+ }
1719
+ const extensionsSet = new Set(app.extensions || []);
1720
+ this.systemRequiredExtensions.forEach((extId) => extensionsSet.add(extId));
1721
+ app.extensions = Array.from(extensionsSet);
1722
+ if (app.extensions.length > 0) {
1723
+ logger.info(`Enabling ${app.extensions.length} extensions...`);
1724
+ app.extensions.forEach((extId) => {
1725
+ extensionRegistry.enable(extId);
1726
+ });
1727
+ }
1728
+ if (app.initialize) {
1729
+ logger.info(`Initializing ${app.name}...`);
1730
+ await app.initialize();
1731
+ }
1732
+ this.currentApp = app;
1733
+ logger.info(`App ${app.name} loaded successfully`);
1734
+ this.updateDocumentMetadata(app);
1735
+ if (container) {
1736
+ this.renderApp(container);
1737
+ }
1738
+ window.dispatchEvent(new CustomEvent("app-loaded", { detail: { appId: app.id } }));
1739
+ }
1740
+ /**
1741
+ * Updates document title and favicon from app metadata
1742
+ */
1743
+ updateDocumentMetadata(app) {
1744
+ document.title = app.name;
1745
+ if (app.metadata?.favicon) {
1746
+ const faviconPath = app.metadata.favicon;
1747
+ let link = document.querySelector("link[rel*='icon']");
1748
+ if (!link) {
1749
+ link = document.createElement("link");
1750
+ link.rel = "icon";
1751
+ document.head.appendChild(link);
1752
+ }
1753
+ link.type = "image/svg+xml";
1754
+ link.href = faviconPath;
1755
+ }
1756
+ }
1757
+ /**
1758
+ * Render the current application to the DOM.
1759
+ *
1760
+ * @param container - DOM element to render into
1761
+ */
1762
+ renderApp(container) {
1763
+ if (!this.currentApp) {
1764
+ throw new Error("No app loaded. Call loadApp() first.");
1765
+ }
1766
+ const r = this.currentApp.render;
1767
+ if (typeof r === "string") {
1768
+ const el = document.createElement(r);
1769
+ container.innerHTML = "";
1770
+ container.appendChild(el);
1771
+ } else if (r && typeof r === "object" && "tag" in r) {
1772
+ const el = document.createElement(r.tag);
1773
+ for (const [key, value] of Object.entries(r.attributes ?? {})) {
1774
+ el.setAttribute(key, value);
1775
+ }
1776
+ container.innerHTML = "";
1777
+ container.appendChild(el);
1778
+ } else if (typeof r === "function") {
1779
+ const template = r();
1780
+ render(template, container);
1781
+ } else {
1782
+ render(html`<lyra-standard-layout></lyra-standard-layout>`, container);
1783
+ }
1784
+ logger.info(`Rendered ${this.currentApp.name}`);
1785
+ }
1786
+ /**
1787
+ * Get the currently loaded application.
1788
+ */
1789
+ getCurrentApp() {
1790
+ return this.currentApp;
1791
+ }
1792
+ /**
1793
+ * Get all registered applications.
1794
+ */
1795
+ getRegisteredApps() {
1796
+ return Array.from(this.apps.values());
1797
+ }
1798
+ /**
1799
+ * Get the preferred app ID from settings.
1800
+ */
1801
+ async getPreferredAppId() {
1802
+ try {
1803
+ return await appSettings.get(_AppLoaderService.PREFERRED_APP_KEY);
1804
+ } catch (error) {
1805
+ logger.debug(`Failed to get preferred app ID from settings: ${getErrorMessage(error)}`);
1806
+ return void 0;
1807
+ }
1808
+ }
1809
+ /**
1810
+ * Set the preferred app ID and persist it to settings.
1811
+ */
1812
+ async setPreferredAppId(appId) {
1813
+ if (!this.apps.has(appId)) {
1814
+ throw new Error(`App '${appId}' not found. Make sure it's registered.`);
1815
+ }
1816
+ try {
1817
+ await appSettings.set(_AppLoaderService.PREFERRED_APP_KEY, appId);
1818
+ this.defaultAppId = appId;
1819
+ logger.info(`Set preferred app to: ${appId}`);
1820
+ } catch (error) {
1821
+ logger.error(`Failed to persist preferred app ID: ${getErrorMessage(error)}`);
1822
+ throw error;
1823
+ }
1824
+ }
1825
+ /**
1826
+ * Select which app to load based on priority:
1827
+ * 1. appId URL parameter (?appId=...)
1828
+ * 2. App ID from current page URL path (/geospace)
1829
+ * 3. App ID extracted from app URL parameter (?app=...)
1830
+ * 4. App registered by extension
1831
+ * 5. Preferred app ID from settings
1832
+ * 6. Default app ID
1833
+ * 7. First registered app
1834
+ */
1835
+ async selectAppToLoad(options) {
1836
+ const { appIdFromUrl, appIdFromPath, appIdFromAppUrl, appsBeforeExtension } = options;
1837
+ if (appIdFromUrl) {
1838
+ if (this.apps.has(appIdFromUrl)) {
1839
+ logger.info(`Loading app specified by URL parameter 'appId': ${appIdFromUrl}`);
1840
+ return appIdFromUrl;
1841
+ }
1842
+ logger.warn(`App ID '${appIdFromUrl}' from URL parameter not found`);
1843
+ }
1844
+ if (appIdFromPath) {
1845
+ if (this.apps.has(appIdFromPath)) {
1846
+ logger.info(`Loading app from URL path: ${appIdFromPath}`);
1847
+ return appIdFromPath;
1848
+ }
1849
+ logger.debug(`App ID '${appIdFromPath}' from URL path not found, continuing search`);
1850
+ }
1851
+ if (appIdFromAppUrl) {
1852
+ if (this.apps.has(appIdFromAppUrl)) {
1853
+ logger.info(`Loading app using ID extracted from app URL path: ${appIdFromAppUrl}`);
1854
+ return appIdFromAppUrl;
1855
+ }
1856
+ }
1857
+ if (this.apps.size > appsBeforeExtension) {
1858
+ const newlyRegisteredApps = Array.from(this.apps.values()).slice(appsBeforeExtension);
1859
+ if (newlyRegisteredApps.length > 0) {
1860
+ const app = newlyRegisteredApps[0];
1861
+ logger.info(`Loading app registered by extension: ${app.name} (${app.id})`);
1862
+ return app.id;
1863
+ }
1864
+ }
1865
+ const preferredAppId = await this.getPreferredAppId();
1866
+ if (preferredAppId && this.apps.has(preferredAppId)) {
1867
+ logger.info(`Loading preferred app from settings: ${preferredAppId}`);
1868
+ return preferredAppId;
1869
+ }
1870
+ if (this.defaultAppId) {
1871
+ if (this.apps.has(this.defaultAppId)) {
1872
+ return this.defaultAppId;
1873
+ }
1874
+ logger.warn(`Default app '${this.defaultAppId}' not found`);
1875
+ }
1876
+ const registeredApps = this.getRegisteredApps();
1877
+ if (registeredApps.length > 0) {
1878
+ const app = registeredApps[0];
1879
+ logger.info(`Loading first registered app: ${app.name} (${app.id})`);
1880
+ return app.id;
1881
+ }
1882
+ return void 0;
1883
+ }
1884
+ };
1885
+ _AppLoaderService.PREFERRED_APP_KEY = "preferredAppId";
1886
+ let AppLoaderService = _AppLoaderService;
1887
+ const appLoaderService = new AppLoaderService();
1888
+ rootContext.put("appLoaderService", appLoaderService);
1889
+ var __defProp$8 = Object.defineProperty;
1890
+ var __getOwnPropDesc$8 = Object.getOwnPropertyDescriptor;
1891
+ var __decorateClass$8 = (decorators, target, key, kind) => {
1892
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$8(target, key) : target;
1893
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
1894
+ if (decorator = decorators[i])
1895
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
1896
+ if (kind && result) __defProp$8(target, key, result);
1897
+ return result;
1898
+ };
1899
+ let LyraPromptDialogContent = class extends LyraDialogContent {
1900
+ constructor() {
1901
+ super(...arguments);
1902
+ this.message = "";
1903
+ this.defaultValue = "";
1904
+ this.markdown = false;
1905
+ this.inputValue = "";
1906
+ }
1907
+ async firstUpdated(changedProperties) {
1908
+ super.firstUpdated(changedProperties);
1909
+ this.inputValue = this.defaultValue;
1910
+ await this.updateComplete;
1911
+ const input = this.shadowRoot?.querySelector("wa-input");
1912
+ if (input) {
1913
+ const inputEl = input.shadowRoot?.querySelector("input");
1914
+ if (inputEl) {
1915
+ inputEl.focus();
1916
+ inputEl.select();
1917
+ }
1918
+ }
1919
+ }
1920
+ getResult() {
1921
+ return this.inputValue;
1922
+ }
1923
+ handleInput(e) {
1924
+ this.inputValue = e.target.value;
1925
+ }
1926
+ handleKeyDown(e) {
1927
+ if (e.key === "Enter") {
1928
+ e.preventDefault();
1929
+ this.dispatchEvent(new CustomEvent("dialog-ok", { bubbles: true, composed: true }));
1930
+ } else if (e.key === "Escape") {
1931
+ e.preventDefault();
1932
+ this.dispatchEvent(new CustomEvent("dialog-cancel", { bubbles: true, composed: true }));
1933
+ }
1934
+ }
1935
+ render() {
1936
+ return html`
1937
+ ${this.renderMessage(this.message, this.markdown)}
1938
+ <wa-input
1939
+ value="${this.inputValue}"
1940
+ @input=${this.handleInput}
1941
+ @keydown=${this.handleKeyDown}
1942
+ autofocus
1943
+ ></wa-input>
1944
+ `;
1945
+ }
1946
+ };
1947
+ LyraPromptDialogContent.styles = [
1948
+ ...LyraDialogContent.styles,
1949
+ css`
1950
+ wa-input {
1951
+ width: 100%;
1952
+ }
1953
+ `
1954
+ ];
1955
+ __decorateClass$8([
1956
+ property({ type: String })
1957
+ ], LyraPromptDialogContent.prototype, "message", 2);
1958
+ __decorateClass$8([
1959
+ property({ type: String, attribute: "default-value" })
1960
+ ], LyraPromptDialogContent.prototype, "defaultValue", 2);
1961
+ __decorateClass$8([
1962
+ property({ type: Boolean })
1963
+ ], LyraPromptDialogContent.prototype, "markdown", 2);
1964
+ __decorateClass$8([
1965
+ state()
1966
+ ], LyraPromptDialogContent.prototype, "inputValue", 2);
1967
+ LyraPromptDialogContent = __decorateClass$8([
1968
+ customElement("lyra-prompt-dialog-content")
1969
+ ], LyraPromptDialogContent);
1970
+ contributionRegistry.registerContribution(DIALOG_CONTRIBUTION_TARGET, {
1971
+ id: "prompt",
1972
+ label: "Input",
1973
+ buttons: [OK_BUTTON, CANCEL_BUTTON],
1974
+ component: (state2) => {
1975
+ if (!state2) {
1976
+ return html`<div>Error: No prompt dialog state</div>`;
1977
+ }
1978
+ return html`
1979
+ <lyra-prompt-dialog-content
1980
+ .message="${state2.message}"
1981
+ .defaultValue="${state2.defaultValue}"
1982
+ .markdown="${state2.markdown}"
1983
+ ></lyra-prompt-dialog-content>
1984
+ `;
1985
+ },
1986
+ onButton: async (id, result, state2) => {
1987
+ if (!state2) {
1988
+ return true;
1989
+ }
1990
+ if (id === "ok") {
1991
+ state2.resolve(result || "");
1992
+ } else {
1993
+ state2.resolve(null);
1994
+ }
1995
+ return true;
1996
+ }
1997
+ });
1998
+ async function promptDialog(message, defaultValue = "", markdown = false) {
1999
+ return new Promise((resolve) => {
2000
+ dialogService.open("prompt", {
2001
+ message,
2002
+ defaultValue,
2003
+ markdown,
2004
+ resolve
2005
+ });
2006
+ });
2007
+ }
2008
+ var __defProp$7 = Object.defineProperty;
2009
+ var __getOwnPropDesc$7 = Object.getOwnPropertyDescriptor;
2010
+ var __decorateClass$7 = (decorators, target, key, kind) => {
2011
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$7(target, key) : target;
2012
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
2013
+ if (decorator = decorators[i])
2014
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
2015
+ if (kind && result) __defProp$7(target, key, result);
2016
+ return result;
2017
+ };
2018
+ let LyraInfoDialogContent = class extends LyraDialogContent {
2019
+ constructor() {
2020
+ super(...arguments);
2021
+ this.message = "";
2022
+ this.markdown = false;
2023
+ }
2024
+ render() {
2025
+ return html`
2026
+ ${this.renderMessage(this.message, this.markdown)}
2027
+ `;
2028
+ }
2029
+ };
2030
+ __decorateClass$7([
2031
+ property({ type: String })
2032
+ ], LyraInfoDialogContent.prototype, "message", 2);
2033
+ __decorateClass$7([
2034
+ property({ type: Boolean })
2035
+ ], LyraInfoDialogContent.prototype, "markdown", 2);
2036
+ LyraInfoDialogContent = __decorateClass$7([
2037
+ customElement("lyra-info-dialog-content")
2038
+ ], LyraInfoDialogContent);
2039
+ contributionRegistry.registerContribution(DIALOG_CONTRIBUTION_TARGET, {
2040
+ id: "info",
2041
+ label: "Information",
2042
+ buttons: [OK_BUTTON],
2043
+ component: (state2) => {
2044
+ if (!state2) {
2045
+ return html`<div>Error: No info dialog state</div>`;
2046
+ }
2047
+ return html`
2048
+ <lyra-info-dialog-content
2049
+ .message="${state2.message}"
2050
+ .markdown="${state2.markdown}"
2051
+ ></lyra-info-dialog-content>
2052
+ `;
2053
+ },
2054
+ onButton: async (id, result, state2) => {
2055
+ if (!state2) {
2056
+ return true;
2057
+ }
2058
+ if (state2.resolve) {
2059
+ state2.resolve();
2060
+ }
2061
+ return true;
2062
+ }
2063
+ });
2064
+ async function infoDialog(title, message, markdown = false) {
2065
+ return new Promise((resolve) => {
2066
+ dialogService.open("info", {
2067
+ title,
2068
+ message,
2069
+ markdown,
2070
+ resolve
2071
+ });
2072
+ });
2073
+ }
2074
+ var __defProp$6 = Object.defineProperty;
2075
+ var __getOwnPropDesc$6 = Object.getOwnPropertyDescriptor;
2076
+ var __decorateClass$6 = (decorators, target, key, kind) => {
2077
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$6(target, key) : target;
2078
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
2079
+ if (decorator = decorators[i])
2080
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
2081
+ if (kind && result) __defProp$6(target, key, result);
2082
+ return result;
2083
+ };
2084
+ let LyraConfirmDialogContent = class extends LyraDialogContent {
2085
+ constructor() {
2086
+ super(...arguments);
2087
+ this.message = "";
2088
+ this.markdown = false;
2089
+ }
2090
+ getResult() {
2091
+ return false;
2092
+ }
2093
+ render() {
2094
+ return html`
2095
+ ${this.renderMessage(this.message, this.markdown)}
2096
+ `;
2097
+ }
2098
+ };
2099
+ __decorateClass$6([
2100
+ property({ type: String })
2101
+ ], LyraConfirmDialogContent.prototype, "message", 2);
2102
+ __decorateClass$6([
2103
+ property({ type: Boolean })
2104
+ ], LyraConfirmDialogContent.prototype, "markdown", 2);
2105
+ LyraConfirmDialogContent = __decorateClass$6([
2106
+ customElement("lyra-confirm-dialog-content")
2107
+ ], LyraConfirmDialogContent);
2108
+ contributionRegistry.registerContribution(DIALOG_CONTRIBUTION_TARGET, {
2109
+ id: "confirm",
2110
+ label: "Confirm",
2111
+ buttons: [OK_BUTTON, CANCEL_BUTTON],
2112
+ component: (state2) => {
2113
+ if (!state2) {
2114
+ return html`<div>Error: No confirm dialog state</div>`;
2115
+ }
2116
+ return html`
2117
+ <lyra-confirm-dialog-content
2118
+ .message="${state2.message}"
2119
+ .markdown="${state2.markdown}"
2120
+ ></lyra-confirm-dialog-content>
2121
+ `;
2122
+ },
2123
+ onButton: async (id, result, state2) => {
2124
+ if (!state2) {
2125
+ return true;
2126
+ }
2127
+ if (id === "ok") {
2128
+ state2.resolve(true);
2129
+ } else {
2130
+ state2.resolve(false);
2131
+ }
2132
+ return true;
2133
+ }
2134
+ });
2135
+ async function confirmDialog(message, markdown = false) {
2136
+ return new Promise((resolve) => {
2137
+ dialogService.open("confirm", {
2138
+ message,
2139
+ markdown,
2140
+ resolve
2141
+ });
2142
+ });
2143
+ }
2144
+ var __defProp$5 = Object.defineProperty;
2145
+ var __getOwnPropDesc$5 = Object.getOwnPropertyDescriptor;
2146
+ var __decorateClass$5 = (decorators, target, key, kind) => {
2147
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$5(target, key) : target;
2148
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
2149
+ if (decorator = decorators[i])
2150
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
2151
+ if (kind && result) __defProp$5(target, key, result);
2152
+ return result;
2153
+ };
2154
+ let LyraNavigableInfoDialogContent = class extends LyraDialogContent {
2155
+ constructor() {
2156
+ super(...arguments);
2157
+ this.title = "";
2158
+ this.message = "";
2159
+ this.markdown = false;
2160
+ this.actions = [];
2161
+ this.currentTitle = "";
2162
+ this.currentMessage = "";
2163
+ this.dialogElement = null;
2164
+ }
2165
+ async firstUpdated(changedProperties) {
2166
+ super.firstUpdated(changedProperties);
2167
+ this.currentTitle = this.title;
2168
+ this.currentMessage = this.message;
2169
+ await this.updateComplete;
2170
+ const dialog = this.closest("wa-dialog");
2171
+ if (dialog) {
2172
+ this.dialogElement = dialog;
2173
+ this.updateDialogLabel();
2174
+ }
2175
+ const contentContainer = this.closest(".dialog-service-content");
2176
+ if (contentContainer) {
2177
+ const footer = contentContainer.parentElement?.querySelector(".dialog-service-footer");
2178
+ if (footer) {
2179
+ footer.style.display = "none";
2180
+ }
2181
+ }
2182
+ }
2183
+ updated(changedProperties) {
2184
+ super.updated(changedProperties);
2185
+ if (changedProperties.has("title")) {
2186
+ this.currentTitle = this.title;
2187
+ this.updateDialogLabel();
2188
+ }
2189
+ if (changedProperties.has("message")) {
2190
+ this.currentMessage = this.message;
2191
+ }
2192
+ }
2193
+ updateDialogLabel() {
2194
+ if (this.dialogElement) {
2195
+ this.dialogElement.setAttribute("label", this.currentTitle);
2196
+ }
2197
+ }
2198
+ updateDialog(newTitle, newMessage, newActions) {
2199
+ this.currentTitle = newTitle;
2200
+ this.currentMessage = newMessage;
2201
+ this.actions = [...newActions];
2202
+ this.updateDialogLabel();
2203
+ this.requestUpdate();
2204
+ }
2205
+ handleActionClick(action) {
2206
+ action.callback();
2207
+ }
2208
+ handleClose() {
2209
+ const dialog = this.closest("wa-dialog");
2210
+ if (dialog && this.resolveCallback) {
2211
+ this.resolveCallback();
2212
+ }
2213
+ }
2214
+ render() {
2215
+ const leftActions = this.actions.filter((a) => a.label !== "Close");
2216
+ const rightActions = this.actions.filter((a) => a.label === "Close");
2217
+ return html`
2218
+ <div class="dialog-content">
2219
+ <wa-scroller class="dialog-scroller">
2220
+ ${this.renderMessage(this.currentMessage, this.markdown)}
2221
+ </wa-scroller>
2222
+
2223
+ <div class="dialog-actions">
2224
+ <div class="dialog-actions-left">
2225
+ ${leftActions.map((action) => html`
2226
+ <wa-button
2227
+ variant="${action.variant || "default"}"
2228
+ ?disabled=${action.disabled}
2229
+ @click=${() => this.handleActionClick(action)}
2230
+ >
2231
+ ${action.label}
2232
+ </wa-button>
2233
+ `)}
2234
+ </div>
2235
+ <div class="dialog-actions-right">
2236
+ ${rightActions.map((action) => html`
2237
+ <wa-button
2238
+ variant="${action.variant || "primary"}"
2239
+ @click=${() => {
2240
+ this.handleActionClick(action);
2241
+ this.handleClose();
2242
+ }}
2243
+ >
2244
+ ${action.label}
2245
+ </wa-button>
2246
+ `)}
2247
+ </div>
2248
+ </div>
2249
+ </div>
2250
+ `;
2251
+ }
2252
+ };
2253
+ LyraNavigableInfoDialogContent.styles = [
2254
+ ...LyraDialogContent.styles,
2255
+ css`
2256
+ :host {
2257
+ display: block;
2258
+ }
2259
+
2260
+ :host-context(.dialog-service-content) {
2261
+ padding: 0;
2262
+ }
2263
+
2264
+ .dialog-content {
2265
+ display: flex;
2266
+ flex-direction: column;
2267
+ gap: 1rem;
2268
+ min-width: 400px;
2269
+ max-width: 600px;
2270
+ height: 500px;
2271
+ padding: 1rem;
2272
+ }
2273
+
2274
+ .dialog-scroller {
2275
+ flex: 1;
2276
+ overflow-y: auto;
2277
+ }
2278
+
2279
+ .dialog-actions {
2280
+ display: flex;
2281
+ gap: 0.5rem;
2282
+ justify-content: space-between;
2283
+ margin-top: 0.5rem;
2284
+ }
2285
+
2286
+ .dialog-actions-left,
2287
+ .dialog-actions-right {
2288
+ display: flex;
2289
+ gap: 0.5rem;
2290
+ }
2291
+ `
2292
+ ];
2293
+ __decorateClass$5([
2294
+ property({ type: String })
2295
+ ], LyraNavigableInfoDialogContent.prototype, "title", 2);
2296
+ __decorateClass$5([
2297
+ property({ type: String })
2298
+ ], LyraNavigableInfoDialogContent.prototype, "message", 2);
2299
+ __decorateClass$5([
2300
+ property({ type: Boolean })
2301
+ ], LyraNavigableInfoDialogContent.prototype, "markdown", 2);
2302
+ __decorateClass$5([
2303
+ state()
2304
+ ], LyraNavigableInfoDialogContent.prototype, "actions", 2);
2305
+ __decorateClass$5([
2306
+ state()
2307
+ ], LyraNavigableInfoDialogContent.prototype, "currentTitle", 2);
2308
+ __decorateClass$5([
2309
+ state()
2310
+ ], LyraNavigableInfoDialogContent.prototype, "currentMessage", 2);
2311
+ LyraNavigableInfoDialogContent = __decorateClass$5([
2312
+ customElement("lyra-navigable-info-dialog-content")
2313
+ ], LyraNavigableInfoDialogContent);
2314
+ contributionRegistry.registerContribution(DIALOG_CONTRIBUTION_TARGET, {
2315
+ id: "navigable-info",
2316
+ label: "Information",
2317
+ buttons: [CLOSE_BUTTON],
2318
+ component: (state2) => {
2319
+ if (!state2) {
2320
+ return html`<div>Error: No navigable info dialog state</div>`;
2321
+ }
2322
+ const componentHtml = html`
2323
+ <lyra-navigable-info-dialog-content
2324
+ .title="${state2.title}"
2325
+ .message="${state2.message}"
2326
+ .markdown="${state2.markdown}"
2327
+ ></lyra-navigable-info-dialog-content>
2328
+ `;
2329
+ (async () => {
2330
+ const element = document.querySelector("lyra-navigable-info-dialog-content");
2331
+ if (element) {
2332
+ await element.updateComplete;
2333
+ element.actions = state2.actions || [];
2334
+ element.resolveCallback = state2.resolve;
2335
+ if (state2.updateDialogRef) {
2336
+ state2.updateDialogRef.current = (newTitle, newMessage, newActions) => {
2337
+ element.updateDialog(newTitle, newMessage, newActions);
2338
+ };
2339
+ }
2340
+ }
2341
+ })();
2342
+ return componentHtml;
2343
+ },
2344
+ onButton: async (id, result, state2) => {
2345
+ if (!state2) {
2346
+ return false;
2347
+ }
2348
+ if (id === "close" && state2.resolve) {
2349
+ state2.resolve();
2350
+ return true;
2351
+ }
2352
+ return false;
2353
+ }
2354
+ });
2355
+ async function navigableInfoDialog(title, message, actions, markdown = false) {
2356
+ return new Promise((resolve) => {
2357
+ const updateDialogRef = {};
2358
+ dialogService.open("navigable-info", {
2359
+ title,
2360
+ message,
2361
+ actions,
2362
+ markdown,
2363
+ resolve,
2364
+ updateDialogRef
2365
+ });
2366
+ const updateDialog = (newTitle, newMessage, newActions) => {
2367
+ if (updateDialogRef.current) {
2368
+ updateDialogRef.current(newTitle, newMessage, newActions);
2369
+ }
2370
+ };
2371
+ actions.updateDialog = updateDialog;
2372
+ });
2373
+ }
2374
+ var __defProp$4 = Object.defineProperty;
2375
+ var __getOwnPropDesc$4 = Object.getOwnPropertyDescriptor;
2376
+ var __decorateClass$4 = (decorators, target, key, kind) => {
2377
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$4(target, key) : target;
2378
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
2379
+ if (decorator = decorators[i])
2380
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
2381
+ if (kind && result) __defProp$4(target, key, result);
2382
+ return result;
2383
+ };
2384
+ const RESIZE_DEBOUNCE_MS = 150;
2385
+ function renderButtonGroup(slotName, orientation, contributions, isToolbarItem, contributionCreator) {
2386
+ const slot = slotName ?? "default";
2387
+ const label = `Toolbar ${slot}`;
2388
+ const items = contributions.filter((c) => c.slot === slotName && isToolbarItem(c));
2389
+ const slotHtml = slotName === "start" ? html`<slot name="start"></slot>` : slotName === "end" ? html`<slot name="end"></slot>` : html`<slot></slot>`;
2390
+ return html`
2391
+ <wa-button-group orientation=${orientation} label=${label}>
2392
+ ${slotHtml}
2393
+ ${items.map(contributionCreator)}
2394
+ </wa-button-group>
2395
+ `;
2396
+ }
2397
+ let LyraToolbar = class extends LyraElement {
2398
+ constructor() {
2399
+ super(...arguments);
2400
+ this.position = "start";
2401
+ this.orientation = "horizontal";
2402
+ this.align = "start";
2403
+ this.size = "small";
2404
+ this.isEditor = false;
2405
+ this.partToolbarContent = void 0;
2406
+ this.partToolbarRenderer = void 0;
2407
+ this.contributions = [];
2408
+ this.compact = false;
2409
+ this.resizeObserver = null;
2410
+ this.resizeDebounceTimer = null;
2411
+ this.overflowCheckScheduled = false;
2412
+ this.onResize = () => {
2413
+ if (this.resizeDebounceTimer !== null) clearTimeout(this.resizeDebounceTimer);
2414
+ this.resizeDebounceTimer = setTimeout(() => {
2415
+ this.resizeDebounceTimer = null;
2416
+ this.updateCompactFromSpace();
2417
+ }, RESIZE_DEBOUNCE_MS);
2418
+ };
2419
+ }
2420
+ updateCompactFromSpace() {
2421
+ const toolbarItems = this.shadowRoot?.querySelector(".toolbar-items");
2422
+ if (!toolbarItems) return;
2423
+ const trimmed = toolbarItems.scrollWidth > toolbarItems.clientWidth;
2424
+ if (this.compact !== trimmed) {
2425
+ this.compact = trimmed;
2426
+ this.requestUpdate();
2427
+ }
2428
+ }
2429
+ scheduleOverflowCheck() {
2430
+ if (this.overflowCheckScheduled) return;
2431
+ this.overflowCheckScheduled = true;
2432
+ requestAnimationFrame(() => {
2433
+ this.overflowCheckScheduled = false;
2434
+ this.updateCompactFromSpace();
2435
+ });
2436
+ }
2437
+ connectedCallback() {
2438
+ super.connectedCallback();
2439
+ this.resizeObserver = new ResizeObserver(this.onResize);
2440
+ this.resizeObserver.observe(this);
2441
+ }
2442
+ disconnectedCallback() {
2443
+ this.resizeObserver?.disconnect();
2444
+ this.resizeObserver = null;
2445
+ if (this.resizeDebounceTimer !== null) {
2446
+ clearTimeout(this.resizeDebounceTimer);
2447
+ this.resizeDebounceTimer = null;
2448
+ }
2449
+ super.disconnectedCallback();
2450
+ }
2451
+ updated(changedProperties) {
2452
+ super.updated?.(changedProperties);
2453
+ if (!this.compact) this.scheduleOverflowCheck();
2454
+ }
2455
+ doBeforeUI() {
2456
+ const id = this.getAttribute("id");
2457
+ if (id) {
2458
+ this.loadContributions(id);
2459
+ }
2460
+ subscribe(TOPIC_CONTRIBUTEIONS_CHANGED, (event) => {
2461
+ if (!id) return;
2462
+ const shouldReload = this.matchesTarget(id, event.target);
2463
+ if (shouldReload) {
2464
+ this.loadContributions(id);
2465
+ this.requestUpdate();
2466
+ }
2467
+ });
2468
+ }
2469
+ matchesTarget(id, target) {
2470
+ if (target === id) return true;
2471
+ if (!id.includes(":")) return false;
2472
+ const [prefix] = id.split(":");
2473
+ if (target === `${prefix}:*`) return true;
2474
+ const targetParts = target.split(":");
2475
+ if (targetParts.length === 2) {
2476
+ const categoryToken = targetParts[1];
2477
+ if (categoryToken === "system.editors" || categoryToken === ".system.editors") {
2478
+ return this.isEditor && id.startsWith(`${prefix}:`);
2479
+ }
2480
+ }
2481
+ return false;
2482
+ }
2483
+ loadContributions(id) {
2484
+ const specific = contributionRegistry.getContributions(id);
2485
+ if (!id.includes(":")) {
2486
+ this.contributions = specific;
2487
+ return;
2488
+ }
2489
+ const [prefix] = id.split(":");
2490
+ const wildcardId = `${prefix}:*`;
2491
+ const wildcard = contributionRegistry.getContributions(wildcardId);
2492
+ const categoryMatches = [];
2493
+ if (this.isEditor) {
2494
+ const allCategories = ["system.editors", ".system.editors"];
2495
+ for (const category of allCategories) {
2496
+ const categoryId = `${prefix}:${category}`;
2497
+ const matches = contributionRegistry.getContributions(categoryId);
2498
+ categoryMatches.push(...matches);
2499
+ }
2500
+ }
2501
+ this.contributions = [...wildcard, ...categoryMatches, ...specific];
2502
+ }
2503
+ isToolbarItem(contribution) {
2504
+ return "command" in contribution || "html" in contribution;
2505
+ }
2506
+ contributionCreator(contribution) {
2507
+ if ("command" in contribution) {
2508
+ const commandContribution = contribution;
2509
+ const showLabel = !this.compact && !!commandContribution.showLabel;
2510
+ return html`
2511
+ <wa-button @click=${() => this.executeCommand(commandContribution.command, commandContribution.params || {})}
2512
+ title=${commandContribution.label}
2513
+ ?disabled="${commandContribution.disabled?.get()}"
2514
+ appearance="plain" size=${this.size}>
2515
+ <wa-icon name=${commandContribution.icon} label="${commandContribution.label}"></wa-icon>
2516
+ ${showLabel ? commandContribution.label : ""}
2517
+ </wa-button>
2518
+ `;
2519
+ }
2520
+ if ("html" in contribution) {
2521
+ const contents = contribution.html;
2522
+ if (contents instanceof Function) {
2523
+ return contents();
2524
+ }
2525
+ return unsafeHTML(contents);
2526
+ }
2527
+ return html`<span>unknown contribution type: ${typeof contribution}</span>`;
2528
+ }
2529
+ render() {
2530
+ const partContent = this.partToolbarRenderer ? this.partToolbarRenderer() : this.partToolbarContent ? this.partToolbarContent : "";
2531
+ const flexDir = this.orientation === "vertical" ? "column" : "row";
2532
+ const alignMap = { start: "flex-start", center: "center", end: "flex-end" };
2533
+ const bindCreator = this.contributionCreator.bind(this);
2534
+ const bindIsItem = this.isToolbarItem.bind(this);
2535
+ return html`
2536
+ <div class="toolbar-items" style=${styleMap({
2537
+ "flex-direction": flexDir,
2538
+ "align-items": alignMap[this.align],
2539
+ "justify-content": this.position
2540
+ })}>
2541
+ ${renderButtonGroup("start", this.orientation, this.contributions, bindIsItem, bindCreator)}
2542
+ ${partContent}
2543
+ ${renderButtonGroup(void 0, this.orientation, this.contributions, bindIsItem, bindCreator)}
2544
+ ${renderButtonGroup("end", this.orientation, this.contributions, bindIsItem, bindCreator)}
2545
+ </div>
2546
+ `;
2547
+ }
2548
+ };
2549
+ LyraToolbar.styles = css`
2550
+ :host {
2551
+ display: flex;
2552
+ flex-direction: row;
2553
+ --wa-form-control-padding-inline: var(--wa-space-2xs);
2554
+ }
2555
+
2556
+ :host([orientation="vertical"]) {
2557
+ flex-direction: column;
2558
+ }
2559
+
2560
+ .toolbar-items {
2561
+ display: flex;
2562
+ flex: 1;
2563
+ gap: var(--wa-space-2xs);
2564
+ }
2565
+ `;
2566
+ __decorateClass$4([
2567
+ property()
2568
+ ], LyraToolbar.prototype, "position", 2);
2569
+ __decorateClass$4([
2570
+ property({ reflect: true })
2571
+ ], LyraToolbar.prototype, "orientation", 2);
2572
+ __decorateClass$4([
2573
+ property({ reflect: true })
2574
+ ], LyraToolbar.prototype, "align", 2);
2575
+ __decorateClass$4([
2576
+ property({ reflect: true })
2577
+ ], LyraToolbar.prototype, "size", 2);
2578
+ __decorateClass$4([
2579
+ property({ type: Boolean, attribute: "is-editor" })
2580
+ ], LyraToolbar.prototype, "isEditor", 2);
2581
+ __decorateClass$4([
2582
+ property({ attribute: false })
2583
+ ], LyraToolbar.prototype, "partToolbarContent", 2);
2584
+ __decorateClass$4([
2585
+ property({ attribute: false })
2586
+ ], LyraToolbar.prototype, "partToolbarRenderer", 2);
2587
+ __decorateClass$4([
2588
+ state()
2589
+ ], LyraToolbar.prototype, "contributions", 2);
2590
+ __decorateClass$4([
2591
+ state()
2592
+ ], LyraToolbar.prototype, "compact", 2);
2593
+ LyraToolbar = __decorateClass$4([
2594
+ customElement("lyra-toolbar")
2595
+ ], LyraToolbar);
2596
+ var __defProp$3 = Object.defineProperty;
2597
+ var __getOwnPropDesc$3 = Object.getOwnPropertyDescriptor;
2598
+ var __decorateClass$3 = (decorators, target, key, kind) => {
2599
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$3(target, key) : target;
2600
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
2601
+ if (decorator = decorators[i])
2602
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
2603
+ if (kind && result) __defProp$3(target, key, result);
2604
+ return result;
2605
+ };
2606
+ let LyraCommand = class extends LyraWidget {
2607
+ constructor() {
2608
+ super(...arguments);
2609
+ this.cmd = "";
2610
+ this.title = "";
2611
+ this.label = false;
2612
+ this.disabled = false;
2613
+ this.appearance = "plain";
2614
+ this.variant = "neutral";
2615
+ this.size = "small";
2616
+ this.params = {};
2617
+ this.placement = "bottom-start";
2618
+ this.dropdownContributions = [];
2619
+ }
2620
+ handleClick(event) {
2621
+ if (this.disabled) return;
2622
+ if (event) {
2623
+ event.stopPropagation();
2624
+ }
2625
+ if (this.action) {
2626
+ this.action(event);
2627
+ return;
2628
+ }
2629
+ if (this.cmd) {
2630
+ const dropdown = this.closest("wa-dropdown");
2631
+ if (dropdown && dropdown.open !== void 0) {
2632
+ dropdown.open = false;
2633
+ }
2634
+ this.executeCommand(this.cmd, this.params);
2635
+ }
2636
+ }
2637
+ handleSelect(event) {
2638
+ const dropdown = event.target;
2639
+ if (dropdown && dropdown.open !== void 0) {
2640
+ dropdown.open = false;
2641
+ }
2642
+ }
2643
+ isInDropdown() {
2644
+ return !!this.closest("wa-dropdown, wa-dropdown-menu");
2645
+ }
2646
+ getKeybinding() {
2647
+ if (!this.cmd || this.action) return null;
2648
+ const keybindings = keyBindingManager.getKeyBindingsForCommand(this.cmd);
2649
+ return keybindings.length > 0 ? keybindings[0] : null;
2650
+ }
2651
+ doBeforeUI() {
2652
+ if (this.dropdown) {
2653
+ this.loadDropdownContributions();
2654
+ subscribe(TOPIC_CONTRIBUTEIONS_CHANGED, (event) => {
2655
+ if (this.dropdown && event.target === this.dropdown) {
2656
+ this.dropdownContributions = event.contributions;
2657
+ this.requestUpdate();
2658
+ }
2659
+ });
2660
+ }
2661
+ }
2662
+ loadDropdownContributions() {
2663
+ if (!this.dropdown) return;
2664
+ this.dropdownContributions = contributionRegistry.getContributions(this.dropdown);
2665
+ this.requestUpdate();
2666
+ }
2667
+ renderContribution(contribution) {
2668
+ if ("command" in contribution) {
2669
+ const commandContribution = contribution;
2670
+ const disabled = commandContribution.disabled?.get();
2671
+ return html`
2672
+ <lyra-command
2673
+ cmd="${commandContribution.command}"
2674
+ icon="${commandContribution.icon || ""}"
2675
+ .params=${commandContribution.params || {}}
2676
+ ?disabled="${disabled}">
2677
+ ${commandContribution.label}
2678
+ </lyra-command>
2679
+ `;
2680
+ }
2681
+ if ("html" in contribution) {
2682
+ const htmlContribution = contribution;
2683
+ const contents = htmlContribution.html;
2684
+ if (contents instanceof Function) {
2685
+ return contents();
2686
+ }
2687
+ return unsafeHTML(contents);
2688
+ }
2689
+ return nothing;
2690
+ }
2691
+ render() {
2692
+ const keybinding = this.getKeybinding();
2693
+ if (this.isInDropdown()) {
2694
+ return html`
2695
+ <wa-dropdown-item
2696
+ ?disabled=${this.disabled}
2697
+ @click=${(e) => this.handleClick(e)}>
2698
+ <lyra-icon name="${this.icon}" label="${this.title}" slot="icon"></lyra-icon>
2699
+ <slot></slot>
2700
+ ${keybinding ? html`<span class="keybinding">${keybinding}</span>` : ""}
2701
+ </wa-dropdown-item>
2702
+ `;
2703
+ }
2704
+ if (this.dropdown) {
2705
+ return html`
2706
+ <wa-dropdown
2707
+ placement=${this.placement}
2708
+ @wa-select=${(e) => this.handleSelect(e)}>
2709
+ <wa-button
2710
+ slot="trigger"
2711
+ appearance=${this.appearance}
2712
+ variant=${this.variant}
2713
+ size=${this.size}
2714
+ ?disabled=${this.disabled}
2715
+ with-caret
2716
+ title=${keybinding ? `${this.title} (${keybinding})` : this.title}>
2717
+ <lyra-icon slot="start" name="${this.icon}" label="${this.title}"></lyra-icon>
2718
+ <slot></slot>
2719
+ ${this.label ? this.title : nothing}
2720
+ </wa-button>
2721
+
2722
+ ${this.title ? html`
2723
+ <h6 style="padding: var(--wa-space-xs) var(--wa-space-s); margin: 0; color: var(--wa-color-neutral-50); font-size: 0.75rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em;">
2724
+ ${this.title}
2725
+ </h6>
2726
+ ` : nothing}
2727
+
2728
+ ${this.dropdownContributions.map((c) => this.renderContribution(c))}
2729
+
2730
+ ${this.cmd ? html`
2731
+ <wa-divider></wa-divider>
2732
+ <lyra-command
2733
+ cmd="${this.cmd}"
2734
+ icon="${this.icon || ""}"
2735
+ .params=${this.params}
2736
+ ?disabled=${this.disabled}>
2737
+ <slot></slot>
2738
+ ${this.title}
2739
+ </lyra-command>
2740
+ ` : nothing}
2741
+ </wa-dropdown>
2742
+ `;
2743
+ }
2744
+ return html`
2745
+ <wa-button
2746
+ appearance=${this.appearance}
2747
+ variant=${this.variant}
2748
+ size=${this.size}
2749
+ ?disabled=${this.disabled}
2750
+ title=${keybinding ? `${this.title} (${keybinding})` : this.title}
2751
+ @click=${(e) => this.handleClick(e)}>
2752
+ <lyra-icon slot="start" name="${this.icon}" label="${this.title}"></lyra-icon>
2753
+ <slot></slot>
2754
+ </wa-button>
2755
+ `;
2756
+ }
2757
+ };
2758
+ LyraCommand.styles = css`
2759
+ :host {
2760
+ display: inline-block;
2761
+ }
2762
+
2763
+ .keybinding {
2764
+ margin-left: auto;
2765
+ padding: 2px 6px;
2766
+ background: var(--wa-color-neutral-15);
2767
+ border: 1px solid var(--wa-color-neutral-25);
2768
+ border-radius: 3px;
2769
+ font-size: 10px;
2770
+ font-family: monospace;
2771
+ opacity: 0.7;
2772
+ }
2773
+
2774
+ :host-context(.wa-light) .keybinding {
2775
+ background: var(--wa-color-neutral-85);
2776
+ border: 1px solid var(--wa-color-neutral-75);
2777
+ }
2778
+ `;
2779
+ __decorateClass$3([
2780
+ property()
2781
+ ], LyraCommand.prototype, "cmd", 2);
2782
+ __decorateClass$3([
2783
+ property({ type: Object, attribute: false })
2784
+ ], LyraCommand.prototype, "action", 2);
2785
+ __decorateClass$3([
2786
+ property()
2787
+ ], LyraCommand.prototype, "title", 2);
2788
+ __decorateClass$3([
2789
+ property()
2790
+ ], LyraCommand.prototype, "label", 2);
2791
+ __decorateClass$3([
2792
+ property()
2793
+ ], LyraCommand.prototype, "icon", 2);
2794
+ __decorateClass$3([
2795
+ property({ type: Boolean })
2796
+ ], LyraCommand.prototype, "disabled", 2);
2797
+ __decorateClass$3([
2798
+ property()
2799
+ ], LyraCommand.prototype, "appearance", 2);
2800
+ __decorateClass$3([
2801
+ property()
2802
+ ], LyraCommand.prototype, "variant", 2);
2803
+ __decorateClass$3([
2804
+ property()
2805
+ ], LyraCommand.prototype, "size", 2);
2806
+ __decorateClass$3([
2807
+ property({ type: Object, attribute: false })
2808
+ ], LyraCommand.prototype, "params", 2);
2809
+ __decorateClass$3([
2810
+ property()
2811
+ ], LyraCommand.prototype, "dropdown", 2);
2812
+ __decorateClass$3([
2813
+ property()
2814
+ ], LyraCommand.prototype, "placement", 2);
2815
+ __decorateClass$3([
2816
+ state()
2817
+ ], LyraCommand.prototype, "dropdownContributions", 2);
2818
+ LyraCommand = __decorateClass$3([
2819
+ customElement("lyra-command")
2820
+ ], LyraCommand);
2821
+ var __defProp$2 = Object.defineProperty;
2822
+ var __getOwnPropDesc$2 = Object.getOwnPropertyDescriptor;
2823
+ var __decorateClass$2 = (decorators, target, key, kind) => {
2824
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$2(target, key) : target;
2825
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
2826
+ if (decorator = decorators[i])
2827
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
2828
+ if (kind && result) __defProp$2(target, key, result);
2829
+ return result;
2830
+ };
2831
+ let LyraContextMenu = class extends LyraElement {
2832
+ constructor() {
2833
+ super(...arguments);
2834
+ this.isEditor = false;
2835
+ this.partContextMenuRenderer = void 0;
2836
+ this.contributions = [];
2837
+ this.isOpen = false;
2838
+ this.position = { x: 0, y: 0 };
2839
+ this.anchorRef = createRef();
2840
+ this.dropdownRef = createRef();
2841
+ this.boundHandleDocumentPointerDown = this.handleDocumentPointerDown.bind(this);
2842
+ }
2843
+ /**
2844
+ * "Click outside to close" runs in capture phase before the target's click.
2845
+ * We use composedPath() so hits inside the menu still count as inside:
2846
+ * - Clicks on items (e.g. command / wa-dropdown-item) or their icon/label
2847
+ * are inside shadow roots; contains(target) can miss those.
2848
+ * - composedPath() is the path from target to root crossing shadow boundaries,
2849
+ * so if the dropdown is in the path, the click was inside the menu and we
2850
+ * do not close (so the item's click can run). We only close when the click
2851
+ * is truly outside (dropdown not in path). Submenus: same idea, skip close
2852
+ * when any node in the path has part="submenu".
2853
+ */
2854
+ handleDocumentPointerDown(e) {
2855
+ if (!this.isOpen) return;
2856
+ const path = e.composedPath();
2857
+ if (this.dropdownRef.value && path.includes(this.dropdownRef.value)) return;
2858
+ if (path.some((el) => el.getAttribute?.("part") === "submenu")) return;
2859
+ this.onClose();
2860
+ }
2861
+ doBeforeUI() {
2862
+ const id = this.getAttribute("id");
2863
+ if (id) {
2864
+ this.loadContributions(id);
2865
+ }
2866
+ subscribe(TOPIC_CONTRIBUTEIONS_CHANGED, (event) => {
2867
+ if (!id) return;
2868
+ const shouldReload = this.matchesTarget(id, event.target);
2869
+ if (shouldReload) {
2870
+ this.loadContributions(id);
2871
+ this.requestUpdate();
2872
+ }
2873
+ });
2874
+ }
2875
+ matchesTarget(id, target) {
2876
+ if (target === id) return true;
2877
+ if (!id.includes(":")) return false;
2878
+ const [prefix] = id.split(":");
2879
+ if (target === `${prefix}:*`) return true;
2880
+ const targetParts = target.split(":");
2881
+ if (targetParts.length === 2) {
2882
+ const categoryToken = targetParts[1];
2883
+ if (categoryToken === "system.editors" || categoryToken === ".system.editors") {
2884
+ return this.isEditor && id.startsWith(`${prefix}:`);
2885
+ }
2886
+ }
2887
+ return false;
2888
+ }
2889
+ loadContributions(id) {
2890
+ const specific = contributionRegistry.getContributions(id);
2891
+ if (!id.includes(":")) {
2892
+ this.contributions = specific;
2893
+ return;
2894
+ }
2895
+ const [prefix] = id.split(":");
2896
+ const wildcardId = `${prefix}:*`;
2897
+ const wildcard = contributionRegistry.getContributions(wildcardId);
2898
+ const categoryMatches = [];
2899
+ if (this.isEditor) {
2900
+ const allCategories = ["system.editors", ".system.editors"];
2901
+ for (const category of allCategories) {
2902
+ const categoryId = `${prefix}:${category}`;
2903
+ const matches = contributionRegistry.getContributions(categoryId);
2904
+ categoryMatches.push(...matches);
2905
+ }
2906
+ }
2907
+ this.contributions = [...wildcard, ...categoryMatches, ...specific];
2908
+ }
2909
+ /**
2910
+ * Gets the element at the given point, traversing shadow DOM boundaries recursively.
2911
+ * This is necessary because elementFromPoint() doesn't penetrate shadow roots.
2912
+ */
2913
+ getElementFromPoint(x, y) {
2914
+ let element = document.elementFromPoint(x, y);
2915
+ if (!element) return null;
2916
+ while (element) {
2917
+ const shadowRoot = element.shadowRoot;
2918
+ if (shadowRoot) {
2919
+ const shadowElement = shadowRoot.elementFromPoint(x, y);
2920
+ if (shadowElement && shadowElement !== element) {
2921
+ element = shadowElement;
2922
+ continue;
2923
+ }
2924
+ }
2925
+ break;
2926
+ }
2927
+ return element;
2928
+ }
2929
+ /**
2930
+ * Triggers a click on the element under the cursor to update selection before showing context menu.
2931
+ */
2932
+ triggerClickUnderCursor(mouseEvent) {
2933
+ const elementUnderCursor = this.getElementFromPoint(mouseEvent.clientX, mouseEvent.clientY);
2934
+ if (elementUnderCursor) {
2935
+ const clickEvent = new MouseEvent("click", {
2936
+ bubbles: true,
2937
+ cancelable: true,
2938
+ view: window,
2939
+ clientX: mouseEvent.clientX,
2940
+ clientY: mouseEvent.clientY,
2941
+ screenX: mouseEvent.screenX,
2942
+ screenY: mouseEvent.screenY,
2943
+ button: 0,
2944
+ buttons: 0,
2945
+ detail: 1,
2946
+ which: 1
2947
+ });
2948
+ elementUnderCursor.dispatchEvent(clickEvent);
2949
+ }
2950
+ }
2951
+ show(position, mouseEvent) {
2952
+ if (mouseEvent) {
2953
+ this.triggerClickUnderCursor(mouseEvent);
2954
+ }
2955
+ this.position = position;
2956
+ this.isOpen = true;
2957
+ this.updateComplete.then(() => {
2958
+ document.addEventListener("pointerdown", this.boundHandleDocumentPointerDown, { capture: true });
2959
+ });
2960
+ }
2961
+ onClose() {
2962
+ this.isOpen = false;
2963
+ document.removeEventListener("pointerdown", this.boundHandleDocumentPointerDown, { capture: true });
2964
+ }
2965
+ renderContribution(contribution) {
2966
+ if ("command" in contribution) {
2967
+ const commandContribution = contribution;
2968
+ const disabled = commandContribution.disabled?.get();
2969
+ return html`
2970
+ <lyra-command
2971
+ cmd="${commandContribution.command}"
2972
+ icon="${commandContribution.icon ?? ""}"
2973
+ .params=${commandContribution.params ?? {}}
2974
+ ?disabled="${disabled}">
2975
+ ${commandContribution.label}
2976
+ </lyra-command>
2977
+ `;
2978
+ } else if ("html" in contribution) {
2979
+ const contents = contribution.html;
2980
+ if (contents instanceof Function) {
2981
+ return contents();
2982
+ }
2983
+ return unsafeHTML(contents);
2984
+ }
2985
+ return nothing;
2986
+ }
2987
+ render() {
2988
+ if (!this.isOpen) return nothing;
2989
+ const partContent = this.partContextMenuRenderer ? this.partContextMenuRenderer() : nothing;
2990
+ return html`
2991
+ <wa-dropdown
2992
+ ${ref(this.dropdownRef)}
2993
+ ?open=${this.isOpen}
2994
+ @wa-after-hide=${this.onClose}>
2995
+
2996
+ <div
2997
+ slot="trigger"
2998
+ ${ref(this.anchorRef)}
2999
+ style="position: fixed;
3000
+ left: ${this.position.x}px;
3001
+ top: ${this.position.y}px;
3002
+ width: 1px;
3003
+ height: 1px;
3004
+ pointer-events: none;">
3005
+ </div>
3006
+
3007
+ ${partContent}
3008
+ ${this.contributions.map((c) => this.renderContribution(c))}
3009
+ </wa-dropdown>
3010
+ `;
3011
+ }
3012
+ };
3013
+ LyraContextMenu.styles = css`
3014
+ :host {
3015
+ position: fixed;
3016
+ top: 0;
3017
+ left: 0;
3018
+ width: 0;
3019
+ height: 0;
3020
+ pointer-events: none;
3021
+ z-index: 10000;
3022
+ }
3023
+
3024
+ wa-dropdown {
3025
+ pointer-events: auto;
3026
+ min-width: 200px;
3027
+ }
3028
+
3029
+ wa-dropdown::part(menu) {
3030
+ min-width: 200px;
3031
+ }
3032
+ `;
3033
+ __decorateClass$2([
3034
+ property({ type: Boolean, attribute: "is-editor" })
3035
+ ], LyraContextMenu.prototype, "isEditor", 2);
3036
+ __decorateClass$2([
3037
+ property({ attribute: false })
3038
+ ], LyraContextMenu.prototype, "partContextMenuRenderer", 2);
3039
+ __decorateClass$2([
3040
+ state()
3041
+ ], LyraContextMenu.prototype, "contributions", 2);
3042
+ __decorateClass$2([
3043
+ state()
3044
+ ], LyraContextMenu.prototype, "isOpen", 2);
3045
+ __decorateClass$2([
3046
+ state()
3047
+ ], LyraContextMenu.prototype, "position", 2);
3048
+ LyraContextMenu = __decorateClass$2([
3049
+ customElement("lyra-contextmenu")
3050
+ ], LyraContextMenu);
3051
+ var __defProp$1 = Object.defineProperty;
3052
+ var __getOwnPropDesc$1 = Object.getOwnPropertyDescriptor;
3053
+ var __decorateClass$1 = (decorators, target, key, kind) => {
3054
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$1(target, key) : target;
3055
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
3056
+ if (decorator = decorators[i])
3057
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
3058
+ if (kind && result) __defProp$1(target, key, result);
3059
+ return result;
3060
+ };
3061
+ let LyraTabs = class extends LyraContainer {
3062
+ constructor() {
3063
+ super(...arguments);
3064
+ this.placement = "top";
3065
+ this.contributions = [];
3066
+ this.tabGroup = createRef();
3067
+ this.containerId = null;
3068
+ this.resizeObservers = /* @__PURE__ */ new WeakMap();
3069
+ }
3070
+ // ============= Lifecycle Methods =============
3071
+ doBeforeUI() {
3072
+ this.containerId = this.getAttribute("id");
3073
+ if (!this.containerId) {
3074
+ throw new Error("lyra-tabs requires an 'id' attribute to function");
3075
+ }
3076
+ this.loadAndResolveContributions();
3077
+ }
3078
+ doInitUI() {
3079
+ this.updateComplete.then(() => {
3080
+ this.activateNextAvailableTab();
3081
+ if (!this.tabGroup.value) return;
3082
+ this.tabGroup.value.addEventListener("wa-tab-show", (event) => {
3083
+ const tabPanel = this.getTabPanel(event.detail.name);
3084
+ if (tabPanel) {
3085
+ this.updateToolbarFromComponent(tabPanel);
3086
+ requestAnimationFrame(() => {
3087
+ this.updateToolbarHeightVariable(tabPanel);
3088
+ this.setupToolbarResizeObserver(tabPanel);
3089
+ });
3090
+ this.dispatchEvent(new CustomEvent("tab-shown", { detail: tabPanel }));
3091
+ }
3092
+ });
3093
+ this.tabGroup.value.addEventListener("part-toolbar-changed", (event) => {
3094
+ const component = event.target;
3095
+ const tabPanel = component.closest("wa-tab-panel");
3096
+ if (tabPanel) {
3097
+ this.updateToolbarFromComponent(tabPanel);
3098
+ requestAnimationFrame(() => this.updateToolbarHeightVariable(tabPanel));
3099
+ }
3100
+ });
3101
+ this.tabGroup.value.addEventListener("part-contextmenu-changed", (event) => {
3102
+ const component = event.target;
3103
+ const tabPanel = component.closest("wa-tab-panel");
3104
+ if (tabPanel) {
3105
+ this.updateContextMenuFromComponent(tabPanel);
3106
+ }
3107
+ });
3108
+ this.tabGroup.value.addEventListener("click", (event) => {
3109
+ const target = event.target;
3110
+ const tab = target.closest("wa-tab");
3111
+ if (tab) {
3112
+ const panelName = tab.getAttribute("panel");
3113
+ if (panelName) {
3114
+ const tabPanel2 = this.getTabPanel(panelName);
3115
+ if (tabPanel2) {
3116
+ const contentDiv2 = tabPanel2.querySelector(".tab-content");
3117
+ if (contentDiv2 && contentDiv2.firstElementChild) {
3118
+ const part = contentDiv2.firstElementChild;
3119
+ if (part instanceof LyraPart) {
3120
+ activePartSignal.set(part);
3121
+ }
3122
+ }
3123
+ }
3124
+ }
3125
+ return;
3126
+ }
3127
+ const scroller = target.closest("wa-scroller.tab-content");
3128
+ if (!scroller) return;
3129
+ const tabPanel = scroller.closest("wa-tab-panel");
3130
+ if (!tabPanel) return;
3131
+ const contentDiv = tabPanel.querySelector(".tab-content");
3132
+ if (contentDiv && contentDiv.firstElementChild) {
3133
+ const part = contentDiv.firstElementChild;
3134
+ if (part instanceof LyraPart) {
3135
+ activePartSignal.set(part);
3136
+ }
3137
+ }
3138
+ });
3139
+ this.tabGroup.value.addEventListener("contextmenu", (event) => {
3140
+ const mouseEvent = event;
3141
+ const scroller = mouseEvent.target.closest("wa-scroller.tab-content");
3142
+ if (!scroller) return;
3143
+ mouseEvent.preventDefault();
3144
+ const tabPanel = scroller.closest("wa-tab-panel");
3145
+ if (!tabPanel) return;
3146
+ requestAnimationFrame(() => {
3147
+ this.updateContextMenuFromComponent(tabPanel);
3148
+ const contextMenu = tabPanel.querySelector("lyra-contextmenu");
3149
+ if (contextMenu) {
3150
+ contextMenu.show({ x: mouseEvent.clientX, y: mouseEvent.clientY }, mouseEvent);
3151
+ }
3152
+ });
3153
+ });
3154
+ });
3155
+ subscribe(TOPIC_CONTRIBUTEIONS_CHANGED, (event) => {
3156
+ if (!this.containerId || event.target !== this.containerId) return;
3157
+ this.loadAndResolveContributions();
3158
+ this.requestUpdate();
3159
+ this.updateComplete.then(() => {
3160
+ this.activateNextAvailableTab();
3161
+ });
3162
+ });
3163
+ }
3164
+ updated(changedProperties) {
3165
+ super.updated(changedProperties);
3166
+ if (changedProperties.has("contributions")) {
3167
+ const isEditorArea = this.containerId === EDITOR_AREA_MAIN;
3168
+ this.contributions.forEach((contribution) => {
3169
+ const tabPanel = this.getTabPanel(contribution.name);
3170
+ if (!tabPanel) return;
3171
+ const contentDiv = tabPanel.querySelector(".tab-content");
3172
+ if (contentDiv && contentDiv.firstElementChild) {
3173
+ const part = contentDiv.firstElementChild;
3174
+ if (part instanceof LyraPart) {
3175
+ part.tabContribution = contribution;
3176
+ part.isEditor = isEditorArea;
3177
+ }
3178
+ }
3179
+ requestAnimationFrame(() => this.updateToolbarHeightVariable(tabPanel));
3180
+ });
3181
+ }
3182
+ }
3183
+ // ============= Public API Methods =============
3184
+ has(key) {
3185
+ if (!this.tabGroup.value) return false;
3186
+ return !!this.getTabPanel(key);
3187
+ }
3188
+ activate(key) {
3189
+ if (!this.tabGroup.value) return;
3190
+ this.tabGroup.value.setAttribute("active", key);
3191
+ }
3192
+ getActiveEditor() {
3193
+ if (!this.tabGroup.value) return null;
3194
+ return this.tabGroup.value.getAttribute("active");
3195
+ }
3196
+ open(contribution) {
3197
+ const existing = this.contributions.find((c) => c.name === contribution.name);
3198
+ if (existing) {
3199
+ this.activate(contribution.name);
3200
+ return;
3201
+ }
3202
+ this.contributions.push(contribution);
3203
+ this.requestUpdate();
3204
+ this.updateComplete.then(() => {
3205
+ this.activate(contribution.name);
3206
+ const tabPanel = this.getTabPanel(contribution.name);
3207
+ if (tabPanel) {
3208
+ const contentDiv = tabPanel.querySelector(".tab-content");
3209
+ if (contentDiv && contentDiv.firstElementChild) {
3210
+ const part = contentDiv.firstElementChild;
3211
+ if (part instanceof LyraPart) {
3212
+ part.tabContribution = contribution;
3213
+ part.isEditor = this.containerId === EDITOR_AREA_MAIN;
3214
+ }
3215
+ }
3216
+ requestAnimationFrame(() => {
3217
+ this.updateToolbarFromComponent(tabPanel);
3218
+ this.updateToolbarHeightVariable(tabPanel);
3219
+ this.setupToolbarResizeObserver(tabPanel);
3220
+ });
3221
+ }
3222
+ });
3223
+ }
3224
+ handleTabAuxClick(event, contribution) {
3225
+ if (event.button === MouseButton.MIDDLE && contribution.closable) {
3226
+ this.closeTab(event, contribution.name);
3227
+ }
3228
+ }
3229
+ async closeTab(event, tabName) {
3230
+ event.stopPropagation();
3231
+ if (this.isDirty(tabName) && !await confirmDialog("Unsaved changes will be lost: Do you really want to close?")) {
3232
+ return;
3233
+ }
3234
+ const tabPanel = this.getTabPanel(tabName);
3235
+ if (!tabPanel) return;
3236
+ const contribution = this.contributions.find((c) => c.name === tabName);
3237
+ if (!contribution) return;
3238
+ this.cleanupTabInstance(tabPanel);
3239
+ const index = this.contributions.indexOf(contribution);
3240
+ if (index > -1) {
3241
+ this.contributions.splice(index, 1);
3242
+ }
3243
+ this.dispatchEvent(new CustomEvent("tab-closed", { detail: tabPanel }));
3244
+ this.requestUpdate();
3245
+ this.updateComplete.then(() => {
3246
+ this.activateNextAvailableTab();
3247
+ });
3248
+ }
3249
+ markDirty(name, dirty) {
3250
+ const tab = this.getTab(name);
3251
+ tab.classList.toggle("part-dirty", dirty);
3252
+ }
3253
+ isDirty(name) {
3254
+ const tab = this.getTab(name);
3255
+ return tab.classList.contains("part-dirty");
3256
+ }
3257
+ // ============= Private Helper Methods =============
3258
+ /**
3259
+ * Loads tab contributions from the registry.
3260
+ */
3261
+ loadAndResolveContributions() {
3262
+ if (!this.containerId) return;
3263
+ this.contributions = contributionRegistry.getContributions(this.containerId);
3264
+ this.requestUpdate();
3265
+ }
3266
+ /**
3267
+ * Cleans up a tab instance when the tab is closed.
3268
+ *
3269
+ * Cleanup Process:
3270
+ * 1. Disconnect ResizeObserver if one exists
3271
+ * 2. Call component's close() method if available (disposes resources)
3272
+ * 3. DOM element is removed by caller (closeTab method)
3273
+ */
3274
+ cleanupTabInstance(tabPanel) {
3275
+ const observer = this.resizeObservers.get(tabPanel);
3276
+ if (observer) {
3277
+ observer.disconnect();
3278
+ this.resizeObservers.delete(tabPanel);
3279
+ }
3280
+ const contentDiv = tabPanel.querySelector(".tab-content");
3281
+ if (contentDiv && contentDiv.firstElementChild) {
3282
+ const component = contentDiv.firstElementChild;
3283
+ if ("close" in component && typeof component.close === "function") {
3284
+ component.close();
3285
+ }
3286
+ }
3287
+ }
3288
+ activateNextAvailableTab() {
3289
+ if (!this.tabGroup.value) return;
3290
+ const allRemainingTabs = this.tabGroup.value.querySelectorAll("wa-tab");
3291
+ if (allRemainingTabs.length > 0) {
3292
+ const newActive = allRemainingTabs.item(0).getAttribute("panel");
3293
+ if (newActive) {
3294
+ this.tabGroup.value.setAttribute("active", newActive);
3295
+ }
3296
+ } else {
3297
+ this.tabGroup.value.removeAttribute("active");
3298
+ }
3299
+ }
3300
+ getTabPanel(name) {
3301
+ if (!this.tabGroup.value) return null;
3302
+ return this.tabGroup.value.querySelector(`wa-tab-panel[name='${name}']`);
3303
+ }
3304
+ getTab(name) {
3305
+ if (!this.tabGroup.value) return null;
3306
+ return this.tabGroup.value.querySelector(`wa-tab[panel='${name}']`);
3307
+ }
3308
+ /**
3309
+ * Updates the toolbar for a tab panel by querying the component for its toolbar content.
3310
+ * This allows LyraPart components to provide their own toolbar items directly.
3311
+ */
3312
+ updateToolbarFromComponent(tabPanel) {
3313
+ const contentDiv = tabPanel.querySelector(".tab-content");
3314
+ if (!contentDiv || !contentDiv.firstElementChild) return;
3315
+ const component = contentDiv.firstElementChild;
3316
+ if (!(component instanceof LyraPart)) return;
3317
+ if (!component["renderToolbar"]) return;
3318
+ const toolbar = tabPanel.querySelector("lyra-toolbar");
3319
+ if (toolbar) {
3320
+ toolbar.partToolbarRenderer = () => component["renderToolbar"]();
3321
+ toolbar.requestUpdate();
3322
+ }
3323
+ }
3324
+ /**
3325
+ * Updates the context menu for a tab panel by querying the component for its context menu content.
3326
+ * This allows LyraPart components to provide their own context menu items directly.
3327
+ */
3328
+ updateContextMenuFromComponent(tabPanel) {
3329
+ const contentDiv = tabPanel.querySelector(".tab-content");
3330
+ if (!contentDiv || !contentDiv.firstElementChild) return;
3331
+ const component = contentDiv.firstElementChild;
3332
+ if (!(component instanceof LyraPart)) return;
3333
+ if (!component["renderContextMenu"]) return;
3334
+ const contextMenu = tabPanel.querySelector("lyra-contextmenu");
3335
+ if (contextMenu) {
3336
+ contextMenu.partContextMenuRenderer = () => component["renderContextMenu"]();
3337
+ contextMenu.requestUpdate();
3338
+ }
3339
+ }
3340
+ /**
3341
+ * Updates the toolbar height CSS variable for calc() positioning.
3342
+ */
3343
+ updateToolbarHeightVariable(tabPanel) {
3344
+ const toolbar = tabPanel.querySelector(".tab-toolbar");
3345
+ if (!toolbar) return;
3346
+ const toolbarHeight = toolbar.offsetHeight;
3347
+ tabPanel.style.setProperty("--toolbar-height", `${toolbarHeight}px`);
3348
+ }
3349
+ /**
3350
+ * Sets up a ResizeObserver to update toolbar height variable when toolbar size changes.
3351
+ * Reuses existing observer if one already exists for this tab panel.
3352
+ */
3353
+ setupToolbarResizeObserver(tabPanel) {
3354
+ if (this.resizeObservers.has(tabPanel)) return;
3355
+ const toolbar = tabPanel.querySelector(".tab-toolbar");
3356
+ if (!toolbar) return;
3357
+ const observer = new ResizeObserver(() => {
3358
+ this.updateToolbarHeightVariable(tabPanel);
3359
+ });
3360
+ observer.observe(toolbar);
3361
+ this.resizeObservers.set(tabPanel, observer);
3362
+ }
3363
+ // ============= Render Method =============
3364
+ render() {
3365
+ const currentApp = appLoaderService.getCurrentApp();
3366
+ return html`
3367
+ <wa-tab-group ${ref(this.tabGroup)} placement=${this.placement}>
3368
+ ${when(
3369
+ this.contributions.length === 0,
3370
+ () => html`
3371
+ <div class="empty-state">
3372
+ ${when(
3373
+ currentApp,
3374
+ () => html`
3375
+ <div class="empty-content">
3376
+ <h2 class="empty-title">${currentApp.name}</h2>
3377
+ ${when(
3378
+ currentApp.description,
3379
+ () => html`<p class="empty-description">${currentApp.description}</p>`
3380
+ )}
3381
+ </div>
3382
+ `,
3383
+ () => html`
3384
+ <wa-icon name="folder-open" class="empty-icon"></wa-icon>
3385
+ `
3386
+ )}
3387
+ </div>
3388
+ `,
3389
+ () => repeat(
3390
+ this.contributions,
3391
+ (c) => c.name,
3392
+ (c) => html`
3393
+ <wa-tab panel="${c.name}"
3394
+ @auxclick="${(e) => this.handleTabAuxClick(e, c)}">
3395
+ <lyra-icon name="${c.icon}"></lyra-icon>
3396
+ ${c.label}
3397
+ ${when(c.closable, () => html`
3398
+ <wa-icon name="xmark" label="Close" @click="${(e) => this.closeTab(e, c.name)}"></wa-icon>
3399
+ `)}
3400
+ </wa-tab>
3401
+ <wa-tab-panel name="${c.name}">
3402
+ ${when(c.toolbar !== false, () => html`
3403
+ <lyra-toolbar id="toolbar:${c.editorId ?? c.name}"
3404
+ class="tab-toolbar"
3405
+ ?is-editor="${this.containerId === EDITOR_AREA_MAIN}"></lyra-toolbar>
3406
+ `)}
3407
+ <wa-scroller class="tab-content" orientation="vertical">
3408
+ ${c.component ? c.component(c.name) : nothing}
3409
+ </wa-scroller>
3410
+ ${when(c.contextMenu !== false, () => html`
3411
+ <lyra-contextmenu id="contextmenu:${c.name}"
3412
+ ?is-editor="${this.containerId === EDITOR_AREA_MAIN}"></lyra-contextmenu>
3413
+ `)}
3414
+ </wa-tab-panel>
3415
+ `
3416
+ )
3417
+ )}
3418
+ </wa-tab-group>
3419
+ `;
3420
+ }
3421
+ };
3422
+ LyraTabs.styles = css`
3423
+ :host {
3424
+ height: 100%;
3425
+ width: 100%;
3426
+ }
3427
+
3428
+ wa-tab-group {
3429
+ height: 100%;
3430
+ width: 100%;
3431
+ }
3432
+
3433
+ wa-tab-group::part(base) {
3434
+ display: grid;
3435
+ grid-template-rows: auto minmax(0, 1fr);
3436
+ height: 100%;
3437
+ width: 100%;
3438
+ }
3439
+
3440
+ wa-tab-panel[active] {
3441
+ display: grid;
3442
+ grid-template-rows: minmax(0, 1fr);
3443
+ height: 100%;
3444
+ width: 100%;
3445
+ overflow: hidden;
3446
+ position: relative;
3447
+ }
3448
+
3449
+ .tab-content {
3450
+ position: absolute;
3451
+ top: calc(var(--toolbar-height, 0px));
3452
+ right: 0;
3453
+ left: 0;
3454
+ height: calc(100% - var(--toolbar-height, 0px));
3455
+ }
3456
+
3457
+ wa-tab::part(base) {
3458
+ padding: 3px 0.5rem;
3459
+ }
3460
+
3461
+ wa-tab-panel {
3462
+ --padding: 0px;
3463
+ }
3464
+
3465
+ .part-dirty::part(base) {
3466
+ font-style: italic;
3467
+ color: var(--wa-color-danger-fill-loud)
3468
+ }
3469
+
3470
+ .empty-state {
3471
+ display: flex;
3472
+ align-items: center;
3473
+ justify-content: center;
3474
+ width: 100%;
3475
+ height: 100%;
3476
+ grid-row: 2;
3477
+ }
3478
+
3479
+ .empty-content {
3480
+ display: flex;
3481
+ flex-direction: column;
3482
+ align-items: center;
3483
+ justify-content: center;
3484
+ text-align: center;
3485
+ padding: 2rem;
3486
+ gap: 0.75rem;
3487
+ opacity: 0.3;
3488
+ }
3489
+
3490
+ .empty-title {
3491
+ margin: 0;
3492
+ font-size: 1.5rem;
3493
+ font-weight: 500;
3494
+ color: var(--wa-color-text-quiet);
3495
+ }
3496
+
3497
+ .empty-description {
3498
+ margin: 0;
3499
+ font-size: 1rem;
3500
+ color: var(--wa-color-text-quiet);
3501
+ max-width: 500px;
3502
+ }
3503
+
3504
+ .empty-icon {
3505
+ font-size: 6rem;
3506
+ opacity: 0.2;
3507
+ color: var(--wa-color-text-quiet);
3508
+ }
3509
+ `;
3510
+ __decorateClass$1([
3511
+ property({ reflect: true })
3512
+ ], LyraTabs.prototype, "placement", 2);
3513
+ __decorateClass$1([
3514
+ state()
3515
+ ], LyraTabs.prototype, "contributions", 2);
3516
+ LyraTabs = __decorateClass$1([
3517
+ customElement("lyra-tabs")
3518
+ ], LyraTabs);
3519
+ var __defProp = Object.defineProperty;
3520
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3521
+ var __decorateClass = (decorators, target, key, kind) => {
3522
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
3523
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
3524
+ if (decorator = decorators[i])
3525
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
3526
+ if (kind && result) __defProp(target, key, result);
3527
+ return result;
3528
+ };
3529
+ let LyraResizableGrid = class extends LyraElement {
3530
+ constructor() {
3531
+ super(...arguments);
3532
+ this.orientation = "horizontal";
3533
+ this.gridSizes = [];
3534
+ this.gridChildren = [];
3535
+ this.resizing = null;
3536
+ this.resizeOverlay = null;
3537
+ this.childrenLoaded = false;
3538
+ this.childStylesApplied = false;
3539
+ this.settingsLoaded = false;
3540
+ this.handleResize = (e) => {
3541
+ if (!this.resizing) return;
3542
+ const currentPos = this.orientation === "horizontal" ? e.clientX : e.clientY;
3543
+ const delta = currentPos - this.resizing.startPos;
3544
+ const newSizes = [...this.resizing.startSizes];
3545
+ newSizes[this.resizing.handleIndex] += delta;
3546
+ newSizes[this.resizing.handleIndex + 1] -= delta;
3547
+ const containerSize = this.orientation === "horizontal" ? this.offsetWidth : this.offsetHeight;
3548
+ const minSize = containerSize * 0.05;
3549
+ if (newSizes[this.resizing.handleIndex] >= minSize && newSizes[this.resizing.handleIndex + 1] >= minSize) {
3550
+ this.resizing.currentSizes = newSizes;
3551
+ const gridTemplate = newSizes.map((size, index) => {
3552
+ const percent = size / containerSize * 100;
3553
+ const sizeStr = `${percent.toFixed(2)}%`;
3554
+ if (index === newSizes.length - 1) {
3555
+ return sizeStr;
3556
+ }
3557
+ return `${sizeStr} 4px`;
3558
+ }).join(" ");
3559
+ if (this.orientation === "horizontal") {
3560
+ this.style.gridTemplateColumns = gridTemplate;
3561
+ } else {
3562
+ this.style.gridTemplateRows = gridTemplate;
3563
+ }
3564
+ }
3565
+ };
3566
+ this.stopResize = async () => {
3567
+ if (this.resizing?.currentSizes) {
3568
+ const containerSize = this.orientation === "horizontal" ? this.offsetWidth : this.offsetHeight;
3569
+ this.gridSizes = this.resizing.currentSizes.map((size) => {
3570
+ const percent = size / containerSize * 100;
3571
+ return `${percent.toFixed(2)}%`;
3572
+ });
3573
+ await this.saveSizes();
3574
+ this.requestUpdate();
3575
+ }
3576
+ if (this.resizeOverlay) {
3577
+ document.body.removeChild(this.resizeOverlay);
3578
+ this.resizeOverlay = null;
3579
+ }
3580
+ this.resizing = null;
3581
+ document.removeEventListener("mousemove", this.handleResize);
3582
+ document.removeEventListener("mouseup", this.stopResize);
3583
+ document.body.style.cursor = "";
3584
+ document.body.style.userSelect = "";
3585
+ };
3586
+ }
3587
+ createRenderRoot() {
3588
+ return this;
3589
+ }
3590
+ // ============= Lifecycle Methods =============
3591
+ doBeforeUI() {
3592
+ if (!this.childrenLoaded) {
3593
+ this.mutationObserver = new MutationObserver(() => {
3594
+ if (!this.childrenLoaded) {
3595
+ this.loadChildren();
3596
+ }
3597
+ });
3598
+ this.mutationObserver.observe(this, { childList: true, subtree: false });
3599
+ this.loadChildren();
3600
+ }
3601
+ }
3602
+ async loadChildren() {
3603
+ const potentialChildren = Array.from(this.children).filter(
3604
+ (child) => child.tagName !== "STYLE" && child.tagName !== "SCRIPT" && !child.classList.contains("resize-handle")
3605
+ );
3606
+ if (potentialChildren.length === 0) {
3607
+ return;
3608
+ }
3609
+ this.childrenLoaded = true;
3610
+ if (this.mutationObserver) {
3611
+ this.mutationObserver.disconnect();
3612
+ this.mutationObserver = void 0;
3613
+ }
3614
+ this.gridChildren = potentialChildren;
3615
+ if (!this.settingsLoaded) {
3616
+ this.settingsLoaded = true;
3617
+ const persisted = await this.getDialogSetting();
3618
+ if (persisted && Array.isArray(persisted.sizes) && persisted.sizes.length === this.gridChildren.length) {
3619
+ this.gridSizes = persisted.sizes;
3620
+ this.requestUpdate();
3621
+ return;
3622
+ }
3623
+ }
3624
+ if (this.sizes) {
3625
+ this.gridSizes = this.sizes.split(",").map((s) => s.trim());
3626
+ } else {
3627
+ const equalSize = `${100 / this.gridChildren.length}%`;
3628
+ this.gridSizes = this.gridChildren.map(() => equalSize);
3629
+ }
3630
+ this.requestUpdate();
3631
+ }
3632
+ async saveSizes() {
3633
+ if (this.gridSizes.length === 0) {
3634
+ return;
3635
+ }
3636
+ await this.setDialogSetting({
3637
+ sizes: this.gridSizes,
3638
+ orientation: this.orientation
3639
+ });
3640
+ }
3641
+ updated(changedProperties) {
3642
+ super.updated(changedProperties);
3643
+ if (changedProperties.has("gridChildren") && !this.childStylesApplied && this.gridChildren.length > 0) {
3644
+ this.childStylesApplied = true;
3645
+ this.gridChildren.forEach((child, index) => {
3646
+ child.style.overflow = "hidden";
3647
+ child.style.height = "100%";
3648
+ child.style.width = "100%";
3649
+ child.style.gridColumn = this.orientation === "horizontal" ? `${index * 2 + 1}` : "1";
3650
+ child.style.gridRow = this.orientation === "vertical" ? `${index * 2 + 1}` : "1";
3651
+ child.style.display = "flex";
3652
+ child.style.flexDirection = "column";
3653
+ });
3654
+ }
3655
+ }
3656
+ // ============= Resize Handling Methods =============
3657
+ startResize(e, handleIndex) {
3658
+ e.preventDefault();
3659
+ if (handleIndex >= this.gridChildren.length - 1) return;
3660
+ const startPos = this.orientation === "horizontal" ? e.clientX : e.clientY;
3661
+ const containerSize = this.orientation === "horizontal" ? this.offsetWidth : this.offsetHeight;
3662
+ const startSizes = this.gridSizes.map((size) => {
3663
+ if (size.endsWith("%")) {
3664
+ return parseFloat(size) / 100 * containerSize;
3665
+ } else if (size.endsWith("px")) {
3666
+ return parseFloat(size);
3667
+ } else {
3668
+ return parseFloat(size);
3669
+ }
3670
+ });
3671
+ this.resizing = {
3672
+ handleIndex,
3673
+ startPos,
3674
+ startSizes
3675
+ };
3676
+ this.resizeOverlay = document.createElement("div");
3677
+ this.resizeOverlay.style.position = "fixed";
3678
+ this.resizeOverlay.style.top = "0";
3679
+ this.resizeOverlay.style.left = "0";
3680
+ this.resizeOverlay.style.width = "100%";
3681
+ this.resizeOverlay.style.height = "100%";
3682
+ this.resizeOverlay.style.zIndex = "9999";
3683
+ this.resizeOverlay.style.cursor = this.orientation === "horizontal" ? "col-resize" : "row-resize";
3684
+ document.body.appendChild(this.resizeOverlay);
3685
+ document.addEventListener("mousemove", this.handleResize);
3686
+ document.addEventListener("mouseup", this.stopResize);
3687
+ document.body.style.cursor = this.orientation === "horizontal" ? "col-resize" : "row-resize";
3688
+ document.body.style.userSelect = "none";
3689
+ }
3690
+ // ============= Render Methods =============
3691
+ render() {
3692
+ if (this.gridChildren.length === 0 || this.gridSizes.length === 0) {
3693
+ return nothing;
3694
+ }
3695
+ const gridTemplate = this.gridSizes.flatMap((size, index) => {
3696
+ if (index === this.gridSizes.length - 1) {
3697
+ return [size];
3698
+ }
3699
+ return [size, "1px"];
3700
+ }).join(" ");
3701
+ this.style.display = "grid";
3702
+ if (this.orientation === "horizontal") {
3703
+ this.style.gridTemplateColumns = gridTemplate;
3704
+ this.style.gridTemplateRows = "100%";
3705
+ } else {
3706
+ this.style.gridTemplateColumns = "100%";
3707
+ this.style.gridTemplateRows = gridTemplate;
3708
+ }
3709
+ this.style.overflow = "hidden";
3710
+ return html`
3711
+ <style>
3712
+ .resize-handle {
3713
+ position: relative;
3714
+ z-index: 10;
3715
+ background-color: var(--wa-color-neutral-border-quiet);
3716
+ transition: background-color var(--wa-transition-fast);
3717
+ }
3718
+
3719
+ .resize-handle:hover {
3720
+ background-color: var(--wa-color-brand-fill-normal);
3721
+ }
3722
+ </style>
3723
+
3724
+ ${this.gridChildren.map((_, index) => {
3725
+ if (index < this.gridChildren.length - 1) {
3726
+ const gridCol = this.orientation === "horizontal" ? `${index * 2 + 2}` : "1";
3727
+ const gridRow = this.orientation === "vertical" ? `${index * 2 + 2}` : "1";
3728
+ return html`
3729
+ <div
3730
+ class="resize-handle"
3731
+ style="
3732
+ cursor: ${this.orientation === "horizontal" ? "col-resize" : "row-resize"};
3733
+ grid-column: ${gridCol};
3734
+ grid-row: ${gridRow};
3735
+ "
3736
+ @mousedown=${(e) => this.startResize(e, index)}
3737
+ ></div>
3738
+ `;
3739
+ }
3740
+ return nothing;
3741
+ })}
3742
+ `;
3743
+ }
3744
+ // ============= Cleanup Methods =============
3745
+ disconnectedCallback() {
3746
+ super.disconnectedCallback();
3747
+ if (this.resizing) {
3748
+ this.stopResize();
3749
+ }
3750
+ if (this.mutationObserver) {
3751
+ this.mutationObserver.disconnect();
3752
+ this.mutationObserver = void 0;
3753
+ }
3754
+ }
3755
+ connectedCallback() {
3756
+ super.connectedCallback();
3757
+ this.style.height = "100%";
3758
+ this.style.width = "100%";
3759
+ }
3760
+ };
3761
+ __decorateClass([
3762
+ property()
3763
+ ], LyraResizableGrid.prototype, "orientation", 2);
3764
+ __decorateClass([
3765
+ property()
3766
+ ], LyraResizableGrid.prototype, "sizes", 2);
3767
+ __decorateClass([
3768
+ state()
3769
+ ], LyraResizableGrid.prototype, "gridSizes", 2);
3770
+ __decorateClass([
3771
+ state()
3772
+ ], LyraResizableGrid.prototype, "gridChildren", 2);
3773
+ LyraResizableGrid = __decorateClass([
3774
+ customElement("lyra-resizable-grid")
3775
+ ], LyraResizableGrid);
3776
+ export {
3777
+ COMMAND_SAVE as C,
3778
+ DIALOG_CONTRIBUTION_TARGET as D,
3779
+ EDITOR_AREA_MAIN as E,
3780
+ HIDE_DOT_RESOURCE as H,
3781
+ LyraContainer as L,
3782
+ MouseButton as M,
3783
+ PANEL_BOTTOM as P,
3784
+ SIDEBAR_AUXILIARY as S,
3785
+ TOOLBAR_BOTTOM as T,
3786
+ LyraDialogContent as a,
3787
+ LyraElement as b,
3788
+ LyraPart as c,
3789
+ SIDEBAR_MAIN as d,
3790
+ SIDEBAR_MAIN_BOTTOM as e,
3791
+ SYSTEM_VIEWS as f,
3792
+ TOOLBAR_BOTTOM_CENTER as g,
3793
+ TOOLBAR_BOTTOM_END as h,
3794
+ TOOLBAR_MAIN as i,
3795
+ TOOLBAR_MAIN_CENTER as j,
3796
+ TOOLBAR_MAIN_RIGHT as k,
3797
+ TOPIC_SETTINGS_CHANGED as l,
3798
+ appLoaderService as m,
3799
+ appSettings as n,
3800
+ confirmDialog as o,
3801
+ esmShService as p,
3802
+ extensionRegistry as q,
3803
+ infoDialog as r,
3804
+ navigableInfoDialog as s,
3805
+ persistenceService as t,
3806
+ promptDialog as u,
3807
+ taskService as v,
3808
+ TOPIC_EXTENSIONS_CHANGED as w,
3809
+ CLOSE_BUTTON as x,
3810
+ dialogService as y,
3811
+ constants as z
3812
+ };
3813
+ //# sourceMappingURL=resizable-grid-BRH3MyZK.js.map