@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,214 @@
1
+ import { registerAll } from "../core/commandregistry";
2
+ import { html, render } from "lit";
3
+ import { unsafeHTML } from "lit/directives/unsafe-html.js";
4
+ import { marked } from "marked";
5
+ import { toastError } from "../core/toast";
6
+ import { appLoaderService, type ReleaseEntry } from "../core/apploader";
7
+ import { packageInfoService } from "../core/packageinfoservice";
8
+
9
+ registerAll({
10
+ command: {
11
+ "id": "show_version_info",
12
+ "name": "Show Version Info",
13
+ "description": "Shows application version information",
14
+ "parameters": []
15
+ },
16
+ handler: {
17
+ execute: async _context => {
18
+ const app = appLoaderService.getCurrentApp();
19
+ if (!app) {
20
+ toastError("No app loaded");
21
+ return;
22
+ }
23
+
24
+ const hasPackages = packageInfoService.hasPackages();
25
+ const packagesTree = packageInfoService.renderTree();
26
+
27
+ let dialogContainer: HTMLElement | null = null;
28
+ const getDialogContainer = (): HTMLElement => {
29
+ if (!dialogContainer) {
30
+ dialogContainer = document.getElementById('global-dialog-container') || document.createElement('div');
31
+ if (!dialogContainer.id) {
32
+ dialogContainer.id = 'global-dialog-container';
33
+ document.body.appendChild(dialogContainer);
34
+ }
35
+ }
36
+ return dialogContainer;
37
+ };
38
+
39
+ const cleanup = () => {
40
+ if (dialogContainer) {
41
+ render(html``, dialogContainer);
42
+ }
43
+ };
44
+
45
+ const renderReleaseContent = (releaseContent: string) => {
46
+ const htmlContent = marked.parse(releaseContent, { async: false }) as string;
47
+ return html`${unsafeHTML(htmlContent)}`;
48
+ };
49
+
50
+ let releases: ReleaseEntry[] = [];
51
+ if (app.releaseHistory) {
52
+ if (typeof app.releaseHistory === 'function') {
53
+ try {
54
+ releases = await app.releaseHistory();
55
+ } catch (error) {
56
+ console.error('Failed to load release history from app:', error);
57
+ releases = [];
58
+ }
59
+ } else {
60
+ releases = app.releaseHistory;
61
+ }
62
+ }
63
+
64
+ const isDev = app.version === '0.0.0';
65
+ const currentIndex = releases.length > 0 ? releases.findIndex(r => r.tag_name === app.version) : -1;
66
+ const startIndex = currentIndex >= 0 ? currentIndex : 0;
67
+ let currentReleaseIndex = startIndex;
68
+
69
+ const buildReleaseContent = (index: number) => {
70
+ if (releases.length === 0) {
71
+ return "";
72
+ }
73
+
74
+ const release = releases[index];
75
+ const isCurrentVersion = release.tag_name === app.version;
76
+
77
+ let message = `**Version:** ${release.tag_name}`;
78
+ if (isCurrentVersion) {
79
+ message += ` (Current)`;
80
+ }
81
+ message += `\n\n`;
82
+
83
+ const publishDate = new Date(release.published_at).toLocaleDateString();
84
+ message += `**Released:** ${publishDate}\n\n`;
85
+
86
+ if (!isCurrentVersion) {
87
+ const cleanCurrent = app.version.replace(/^v/, '');
88
+ const cleanRelease = release.tag_name.replace(/^v/, '');
89
+ const currentParts = cleanCurrent.split('.').map(Number);
90
+ const releaseParts = cleanRelease.split('.').map(Number);
91
+ let isNewer = false;
92
+ for (let i = 0; i < Math.max(currentParts.length, releaseParts.length); i++) {
93
+ const current = currentParts[i] || 0;
94
+ const releasePart = releaseParts[i] || 0;
95
+ if (releasePart > current) {
96
+ isNewer = true;
97
+ break;
98
+ }
99
+ if (releasePart < current) {
100
+ break;
101
+ }
102
+ }
103
+ if (isNewer) {
104
+ message += `⚠️ **Update available - reload page to update**\n\n`;
105
+ }
106
+ }
107
+
108
+ if (release.body) {
109
+ message += `---\n\n${release.body}`;
110
+ }
111
+
112
+ return message;
113
+ };
114
+
115
+ const handleClose = () => {
116
+ cleanup();
117
+ };
118
+
119
+ const handleAfterHide = () => {
120
+ cleanup();
121
+ };
122
+
123
+ const updateDialog = (index: number) => {
124
+ const releaseContent = buildReleaseContent(index);
125
+ const hasNavigation = releases.length > 0;
126
+
127
+ const template = html`
128
+ <wa-dialog
129
+ label="About ${app.name} - ${app.version}"
130
+ open
131
+ light-dismiss
132
+ style="--width: 600px;"
133
+ @wa-request-close=${handleClose}
134
+ @wa-after-hide=${handleAfterHide}
135
+ >
136
+ <style>
137
+ .dialog-content {
138
+ height: 600px;
139
+ }
140
+
141
+ wa-tree-item > span small {
142
+ color: var(--wa-color-neutral-60);
143
+ font-size: 0.875em;
144
+ margin-left: 0.5rem;
145
+ }
146
+ </style>
147
+ <small>${app.description}</small>
148
+ <div class="dialog-content">
149
+ <wa-tab-group>
150
+ ${releases.length > 0 ? html`
151
+ <wa-tab slot="nav" panel="release">Release History</wa-tab>
152
+ <wa-tab-panel name="release">
153
+ ${renderReleaseContent(releaseContent)}
154
+ </wa-tab-panel>
155
+ ` : ''}
156
+
157
+ ${hasPackages ? html`
158
+ <wa-tab slot="nav" panel="packages">NPM Packages</wa-tab>
159
+ <wa-tab-panel name="packages">
160
+ ${packagesTree}
161
+ </wa-tab-panel>
162
+ ` : ''}
163
+ </wa-tab-group>
164
+ </div>
165
+ <div slot="footer">
166
+ ${hasNavigation ? html`
167
+ <wa-button
168
+ variant="default"
169
+ ?disabled=${index === releases.length - 1}
170
+ @click=${() => {
171
+ if (index < releases.length - 1) {
172
+ currentReleaseIndex = index + 1;
173
+ updateDialog(currentReleaseIndex);
174
+ }
175
+ }}
176
+ >
177
+ ← Previous
178
+ </wa-button>
179
+ <wa-button
180
+ variant="default"
181
+ ?disabled=${index === 0}
182
+ @click=${() => {
183
+ if (index > 0) {
184
+ currentReleaseIndex = index - 1;
185
+ updateDialog(currentReleaseIndex);
186
+ }
187
+ }}
188
+ >
189
+ Next →
190
+ </wa-button>
191
+ ` : ''}
192
+ <wa-button variant="primary" data-dialog="close">Close</wa-button>
193
+ </div>
194
+ </wa-dialog>
195
+ `;
196
+ render(template, getDialogContainer());
197
+ };
198
+
199
+ updateDialog(startIndex);
200
+
201
+ return new Promise<void>((resolve) => {
202
+ const checkClosed = () => {
203
+ if (!dialogContainer?.querySelector('wa-dialog[open]')) {
204
+ resolve();
205
+ } else {
206
+ setTimeout(checkClosed, 100);
207
+ }
208
+ };
209
+ checkClosed();
210
+ });
211
+ }
212
+ }
213
+ })
214
+
@@ -0,0 +1,233 @@
1
+ import { html, css } from "lit";
2
+ import { customElement, state } from "lit/decorators.js";
3
+ import { LyraElement } from "../parts/element";
4
+ import { appLoaderService, type AppDefinition } from "../core/apploader";
5
+
6
+ @customElement('lyra-app-selector')
7
+ export class LyraAppSelector extends LyraElement {
8
+ @state()
9
+ private apps: AppDefinition[] = [];
10
+
11
+ @state()
12
+ private loading = true;
13
+
14
+ @state()
15
+ private error: string | null = null;
16
+
17
+ protected async doBeforeUI() {
18
+ await this.loadApps();
19
+ }
20
+
21
+ private async loadApps() {
22
+ try {
23
+ this.loading = true;
24
+ this.apps = appLoaderService.getRegisteredApps();
25
+ this.loading = false;
26
+ } catch (err) {
27
+ this.error = err instanceof Error ? err.message : 'Failed to load apps';
28
+ this.loading = false;
29
+ }
30
+ }
31
+
32
+ private async selectApp(app: AppDefinition) {
33
+ try {
34
+ await appLoaderService.setPreferredAppId(app.id);
35
+ await appLoaderService.loadApp(app.id, document.body);
36
+ } catch (err) {
37
+ this.error = err instanceof Error ? err.message : 'Failed to load app';
38
+ }
39
+ }
40
+
41
+ protected render() {
42
+ if (this.loading) {
43
+ return html`
44
+ <div class="selector-container">
45
+ <wa-spinner></wa-spinner>
46
+ <p>Loading available apps...</p>
47
+ </div>
48
+ `;
49
+ }
50
+
51
+ if (this.error) {
52
+ return html`
53
+ <div class="selector-container">
54
+ <wa-icon name="triangle-exclamation" class="error-icon"></wa-icon>
55
+ <p class="error-message">${this.error}</p>
56
+ <wa-button @click=${() => this.loadApps()}>Retry</wa-button>
57
+ </div>
58
+ `;
59
+ }
60
+
61
+ if (this.apps.length === 0) {
62
+ return html`
63
+ <div class="selector-container">
64
+ <wa-icon name="folder-open" class="empty-icon"></wa-icon>
65
+ <h2>No Apps Available</h2>
66
+ <p>No applications are currently registered.</p>
67
+ </div>
68
+ `;
69
+ }
70
+
71
+ return html`
72
+ <div class="selector-container">
73
+ <div class="header">
74
+ <h1>Select an Application</h1>
75
+ <p>Choose an application to load</p>
76
+ </div>
77
+
78
+ <div class="app-list">
79
+ ${this.apps.map(app => html`
80
+ <div
81
+ class="app-card"
82
+ @click=${() => this.selectApp(app)}
83
+ @keydown=${(e: KeyboardEvent) => {
84
+ if (e.key === 'Enter' || e.key === ' ') {
85
+ e.preventDefault();
86
+ this.selectApp(app);
87
+ }
88
+ }}
89
+ tabindex="0"
90
+ role="button">
91
+ <div class="app-header">
92
+ <h3 class="app-name">${app.name}</h3>
93
+ ${app.version ? html`<span class="app-version">v${app.version}</span>` : ''}
94
+ </div>
95
+ ${app.description ? html`<p class="app-description">${app.description}</p>` : ''}
96
+ <div class="app-id">ID: ${app.id}</div>
97
+ </div>
98
+ `)}
99
+ </div>
100
+ </div>
101
+ `;
102
+ }
103
+
104
+ static styles = css`
105
+ :host {
106
+ display: flex;
107
+ flex-direction: column;
108
+ width: 100%;
109
+ height: 100vh;
110
+ align-items: center;
111
+ justify-content: center;
112
+ background: var(--wa-color-neutral-background-base);
113
+ }
114
+
115
+ .selector-container {
116
+ display: flex;
117
+ flex-direction: column;
118
+ align-items: center;
119
+ justify-content: center;
120
+ max-width: 800px;
121
+ width: 100%;
122
+ padding: 2rem;
123
+ box-sizing: border-box;
124
+ }
125
+
126
+ .header {
127
+ text-align: center;
128
+ margin-bottom: 2rem;
129
+ }
130
+
131
+ .header h1 {
132
+ margin: 0 0 0.5rem 0;
133
+ font-size: 2rem;
134
+ font-weight: 600;
135
+ color: var(--wa-color-neutral-foreground-loud);
136
+ }
137
+
138
+ .header p {
139
+ margin: 0;
140
+ color: var(--wa-color-neutral-foreground-quiet);
141
+ }
142
+
143
+ .app-list {
144
+ display: grid;
145
+ grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
146
+ gap: 1rem;
147
+ width: 100%;
148
+ }
149
+
150
+ .app-card {
151
+ display: flex;
152
+ flex-direction: column;
153
+ padding: 1.5rem;
154
+ border: 1px solid var(--wa-color-neutral-border-loud);
155
+ border-radius: var(--wa-border-radius-medium);
156
+ background: var(--wa-color-neutral-background-base);
157
+ cursor: pointer;
158
+ transition: all 0.2s ease;
159
+ box-sizing: border-box;
160
+ }
161
+
162
+ .app-card:hover {
163
+ border-color: var(--wa-color-brand-border-loud);
164
+ background: var(--wa-color-brand-fill-quiet);
165
+ transform: translateY(-2px);
166
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
167
+ }
168
+
169
+ .app-card:focus {
170
+ outline: 2px solid var(--wa-color-brand-border-loud);
171
+ outline-offset: 2px;
172
+ }
173
+
174
+ .app-header {
175
+ display: flex;
176
+ justify-content: space-between;
177
+ align-items: flex-start;
178
+ margin-bottom: 0.75rem;
179
+ }
180
+
181
+ .app-name {
182
+ margin: 0;
183
+ font-size: 1.25rem;
184
+ font-weight: 600;
185
+ color: var(--wa-color-neutral-foreground-loud);
186
+ flex: 1;
187
+ }
188
+
189
+ .app-version {
190
+ font-size: 0.875rem;
191
+ color: var(--wa-color-neutral-foreground-quiet);
192
+ padding: 0.25rem 0.5rem;
193
+ background: var(--wa-color-neutral-fill-quiet);
194
+ border-radius: var(--wa-border-radius-small);
195
+ }
196
+
197
+ .app-description {
198
+ margin: 0 0 0.75rem 0;
199
+ color: var(--wa-color-neutral-foreground-base);
200
+ line-height: 1.5;
201
+ }
202
+
203
+ .app-id {
204
+ font-size: 0.75rem;
205
+ color: var(--wa-color-neutral-foreground-quiet);
206
+ font-family: monospace;
207
+ margin-top: auto;
208
+ }
209
+
210
+ .empty-icon,
211
+ .error-icon {
212
+ font-size: 4rem;
213
+ color: var(--wa-color-neutral-foreground-quiet);
214
+ margin-bottom: 1rem;
215
+ }
216
+
217
+ .error-message {
218
+ color: var(--wa-color-danger-foreground-loud);
219
+ margin-bottom: 1rem;
220
+ }
221
+
222
+ wa-spinner {
223
+ margin-bottom: 1rem;
224
+ }
225
+ `;
226
+ }
227
+
228
+ declare global {
229
+ interface HTMLElementTagNameMap {
230
+ 'lyra-app-selector': LyraAppSelector;
231
+ }
232
+ }
233
+
@@ -0,0 +1,126 @@
1
+ import { html, css } from "lit";
2
+ import { customElement, state } from "lit/decorators.js";
3
+ import { LyraElement } from "../parts/element";
4
+ import { appLoaderService, type AppDefinition } from "../core/apploader";
5
+ import { contributionRegistry } from "../core/contributionregistry";
6
+ import { CLOSE_BUTTON, DIALOG_CONTRIBUTION_TARGET, dialogService } from "../core/dialogservice";
7
+
8
+ const APP_SWITCHER_DIALOG_ID = 'app-switcher';
9
+
10
+ contributionRegistry.registerContribution(DIALOG_CONTRIBUTION_TARGET, {
11
+ id: APP_SWITCHER_DIALOG_ID,
12
+ label: "Switch Application",
13
+ buttons: [CLOSE_BUTTON],
14
+ component: (state?: any) => {
15
+ const apps: AppDefinition[] = state?.apps || [];
16
+ const currentAppId: string | undefined = state?.currentAppId;
17
+ const selectApp = state?.selectApp as (app: AppDefinition) => void;
18
+
19
+ return html`
20
+ <wa-scroller orientation="vertical" style="min-width: 300px; max-height: 400px; padding: var(--wa-space-m);">
21
+ <div style="display: flex; flex-direction: column; gap: var(--wa-space-s);">
22
+ ${apps.map(app => html`
23
+ <wa-card
24
+ style="cursor: pointer;"
25
+ variant=${app.id === currentAppId ? 'brand' : 'neutral'}
26
+ @click=${() => selectApp(app)}>
27
+ <div style="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: var(--wa-space-xs);">
28
+ <span style="font-weight: 600;">${app.name}</span>
29
+ ${app.version ? html`<wa-badge variant="neutral">v${app.version}</wa-badge>` : ''}
30
+ </div>
31
+ ${app.description ? html`<p style="margin: 0; font-size: 0.875rem; line-height: 1.4;">${app.description}</p>` : ''}
32
+ <div style="font-size: 0.75rem; color: var(--wa-color-neutral-foreground-quiet); font-family: monospace; margin-top: var(--wa-space-xs);">ID: ${app.id}</div>
33
+ </wa-card>
34
+ `)}
35
+ </div>
36
+ </wa-scroller>
37
+ `;
38
+ },
39
+ onButton: async () => true,
40
+ });
41
+
42
+ const showAppSwitcherDialog = async (): Promise<void> => {
43
+ const apps = appLoaderService.getRegisteredApps();
44
+ const currentApp = appLoaderService.getCurrentApp();
45
+
46
+ if (apps.length === 0) {
47
+ return;
48
+ }
49
+
50
+ const state = {
51
+ apps,
52
+ currentAppId: currentApp?.id,
53
+ selectApp: async (app: AppDefinition) => {
54
+ if (app.id === currentApp?.id) {
55
+ state.close?.();
56
+ return;
57
+ }
58
+
59
+ try {
60
+ await appLoaderService.setPreferredAppId(app.id);
61
+ await appLoaderService.loadApp(app.id, document.body);
62
+ } catch (error) {
63
+ console.error('Failed to switch app:', error);
64
+ } finally {
65
+ state.close?.();
66
+ }
67
+ },
68
+ close: undefined as (() => void) | undefined,
69
+ };
70
+
71
+ await dialogService.open(APP_SWITCHER_DIALOG_ID, state);
72
+ };
73
+
74
+ @customElement('lyra-app-switcher')
75
+ export class LyraAppSwitcher extends LyraElement {
76
+ @state()
77
+ private currentApp: AppDefinition | undefined;
78
+
79
+ protected doBeforeUI() {
80
+ this.currentApp = appLoaderService.getCurrentApp();
81
+
82
+ const updateCurrentApp = () => {
83
+ this.currentApp = appLoaderService.getCurrentApp();
84
+ this.requestUpdate();
85
+ };
86
+
87
+ window.addEventListener('app-loaded', updateCurrentApp);
88
+
89
+ return () => {
90
+ window.removeEventListener('app-loaded', updateCurrentApp);
91
+ };
92
+ }
93
+
94
+ protected render() {
95
+ const apps = appLoaderService.getRegisteredApps();
96
+ const appName = this.currentApp?.name || 'No App';
97
+
98
+ if (apps.length <= 1) {
99
+ return html``;
100
+ }
101
+
102
+ return html`
103
+ <wa-button
104
+ appearance="plain"
105
+ size="small"
106
+ title="Current app: ${appName}. Click to switch applications."
107
+ @click=${() => showAppSwitcherDialog()}>
108
+ <wa-icon name="grip" style="margin-right: 0.5rem;"></wa-icon>
109
+ ${appName}
110
+ </wa-button>
111
+ `;
112
+ }
113
+
114
+ static styles = css`
115
+ :host {
116
+ display: inline-block;
117
+ }
118
+ `;
119
+ }
120
+
121
+ declare global {
122
+ interface HTMLElementTagNameMap {
123
+ 'lyra-app-switcher': LyraAppSwitcher;
124
+ }
125
+ }
126
+