@kispace-io/core 0.7.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 (272) 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 +80 -0
  8. package/dist/api/index.js.map +1 -0
  9. package/dist/api/services.d.ts +27 -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/files.d.ts +2 -0
  14. package/dist/commands/files.d.ts.map +1 -0
  15. package/dist/commands/global.d.ts +1 -0
  16. package/dist/commands/global.d.ts.map +1 -0
  17. package/dist/commands/index.d.ts +1 -0
  18. package/dist/commands/index.d.ts.map +1 -0
  19. package/dist/commands/version-info.d.ts +2 -0
  20. package/dist/commands/version-info.d.ts.map +1 -0
  21. package/dist/components/index.d.ts +1 -0
  22. package/dist/components/index.d.ts.map +1 -0
  23. package/dist/components/k-app-selector.d.ts +17 -0
  24. package/dist/components/k-app-selector.d.ts.map +1 -0
  25. package/dist/components/k-app-switcher.d.ts +13 -0
  26. package/dist/components/k-app-switcher.d.ts.map +1 -0
  27. package/dist/components/k-command.d.ts +31 -0
  28. package/dist/components/k-command.d.ts.map +1 -0
  29. package/dist/components/k-extensions.d.ts +32 -0
  30. package/dist/components/k-extensions.d.ts.map +1 -0
  31. package/dist/components/k-fastviews.d.ts +34 -0
  32. package/dist/components/k-fastviews.d.ts.map +1 -0
  33. package/dist/components/k-filebrowser.d.ts +40 -0
  34. package/dist/components/k-filebrowser.d.ts.map +1 -0
  35. package/dist/components/k-language-selector.d.ts +12 -0
  36. package/dist/components/k-language-selector.d.ts.map +1 -0
  37. package/dist/components/k-log-terminal.d.ts +36 -0
  38. package/dist/components/k-log-terminal.d.ts.map +1 -0
  39. package/dist/components/k-part-name.d.ts +12 -0
  40. package/dist/components/k-part-name.d.ts.map +1 -0
  41. package/dist/components/k-tasks.d.ts +13 -0
  42. package/dist/components/k-tasks.d.ts.map +1 -0
  43. package/dist/components/k-workspace-name.d.ts +14 -0
  44. package/dist/components/k-workspace-name.d.ts.map +1 -0
  45. package/dist/contributions/default-ui-contributions.d.ts +2 -0
  46. package/dist/contributions/default-ui-contributions.d.ts.map +1 -0
  47. package/dist/contributions/index.d.ts +1 -0
  48. package/dist/contributions/index.d.ts.map +1 -0
  49. package/dist/contributions/marketplace-catalog-contributions.d.ts +2 -0
  50. package/dist/contributions/marketplace-catalog-contributions.d.ts.map +1 -0
  51. package/dist/core/app-host-config.d.ts +7 -0
  52. package/dist/core/app-host-config.d.ts.map +1 -0
  53. package/dist/core/apploader.d.ts +214 -0
  54. package/dist/core/apploader.d.ts.map +1 -0
  55. package/dist/core/appstate.d.ts +12 -0
  56. package/dist/core/appstate.d.ts.map +1 -0
  57. package/dist/core/commandregistry.d.ts +79 -0
  58. package/dist/core/commandregistry.d.ts.map +1 -0
  59. package/dist/core/config.d.ts +15 -0
  60. package/dist/core/config.d.ts.map +1 -0
  61. package/dist/core/constants.d.ts +21 -0
  62. package/dist/core/constants.d.ts.map +1 -0
  63. package/dist/core/contributionregistry.d.ts +49 -0
  64. package/dist/core/contributionregistry.d.ts.map +1 -0
  65. package/dist/core/di.d.ts +18 -0
  66. package/dist/core/di.d.ts.map +1 -0
  67. package/dist/core/dialogservice.d.ts +33 -0
  68. package/dist/core/dialogservice.d.ts.map +1 -0
  69. package/dist/core/editorregistry.d.ts +73 -0
  70. package/dist/core/editorregistry.d.ts.map +1 -0
  71. package/dist/core/esmsh-service.d.ts +40 -0
  72. package/dist/core/esmsh-service.d.ts.map +1 -0
  73. package/dist/core/events.d.ts +7 -0
  74. package/dist/core/events.d.ts.map +1 -0
  75. package/dist/core/events.js +63 -0
  76. package/dist/core/events.js.map +1 -0
  77. package/dist/core/extensionregistry.d.ts +98 -0
  78. package/dist/core/extensionregistry.d.ts.map +1 -0
  79. package/dist/core/filesys.d.ts +139 -0
  80. package/dist/core/filesys.d.ts.map +1 -0
  81. package/dist/core/i18n.d.ts +50 -0
  82. package/dist/core/i18n.d.ts.map +1 -0
  83. package/dist/core/index.d.ts +1 -0
  84. package/dist/core/index.d.ts.map +1 -0
  85. package/dist/core/k-utils.d.ts +2 -0
  86. package/dist/core/k-utils.d.ts.map +1 -0
  87. package/dist/core/keybindings.d.ts +67 -0
  88. package/dist/core/keybindings.d.ts.map +1 -0
  89. package/dist/core/logger.d.ts +44 -0
  90. package/dist/core/logger.d.ts.map +1 -0
  91. package/dist/core/marketplaceregistry.d.ts +25 -0
  92. package/dist/core/marketplaceregistry.d.ts.map +1 -0
  93. package/dist/core/packageinfoservice.d.ts +16 -0
  94. package/dist/core/packageinfoservice.d.ts.map +1 -0
  95. package/dist/core/persistenceservice.d.ts +6 -0
  96. package/dist/core/persistenceservice.d.ts.map +1 -0
  97. package/dist/core/settingsservice.d.ts +19 -0
  98. package/dist/core/settingsservice.d.ts.map +1 -0
  99. package/dist/core/signals.d.ts +3 -0
  100. package/dist/core/signals.d.ts.map +1 -0
  101. package/dist/core/taskservice.d.ts +20 -0
  102. package/dist/core/taskservice.d.ts.map +1 -0
  103. package/dist/core/toast.d.ts +4 -0
  104. package/dist/core/toast.d.ts.map +1 -0
  105. package/dist/core/tree-utils.d.ts +16 -0
  106. package/dist/core/tree-utils.d.ts.map +1 -0
  107. package/dist/dialogs/confirm-dialog.d.ts +14 -0
  108. package/dist/dialogs/confirm-dialog.d.ts.map +1 -0
  109. package/dist/dialogs/index.d.ts +5 -0
  110. package/dist/dialogs/index.d.ts.map +1 -0
  111. package/dist/dialogs/info-dialog.d.ts +13 -0
  112. package/dist/dialogs/info-dialog.d.ts.map +1 -0
  113. package/dist/dialogs/navigable-info-dialog.d.ts +33 -0
  114. package/dist/dialogs/navigable-info-dialog.d.ts.map +1 -0
  115. package/dist/dialogs/prompt-dialog.d.ts +21 -0
  116. package/dist/dialogs/prompt-dialog.d.ts.map +1 -0
  117. package/dist/externals/lit.d.ts +20 -0
  118. package/dist/externals/lit.d.ts.map +1 -0
  119. package/dist/externals/lit.js +15 -0
  120. package/dist/externals/lit.js.map +1 -0
  121. package/dist/externals/third-party.d.ts +7 -0
  122. package/dist/externals/third-party.d.ts.map +1 -0
  123. package/dist/externals/third-party.js +2 -0
  124. package/dist/externals/third-party.js.map +1 -0
  125. package/dist/externals/webawesome.d.ts +1 -0
  126. package/dist/externals/webawesome.d.ts.map +1 -0
  127. package/dist/externals/webawesome.js +52 -0
  128. package/dist/externals/webawesome.js.map +1 -0
  129. package/dist/i18n/extensions.json.d.ts +42 -0
  130. package/dist/i18n/fastviews.json.d.ts +13 -0
  131. package/dist/i18n/filebrowser.json.d.ts +35 -0
  132. package/dist/i18n/index.d.ts +2 -0
  133. package/dist/i18n/index.d.ts.map +1 -0
  134. package/dist/i18n/logterminal.json.d.ts +45 -0
  135. package/dist/i18n/partname.json.d.ts +15 -0
  136. package/dist/i18n/tasks.json.d.ts +15 -0
  137. package/dist/i18n/workspace.json.d.ts +15 -0
  138. package/dist/index.d.ts +2 -0
  139. package/dist/index.d.ts.map +1 -0
  140. package/dist/index.js +80 -0
  141. package/dist/index.js.map +1 -0
  142. package/dist/k-icon-BZC7dQV0.js +492 -0
  143. package/dist/k-icon-BZC7dQV0.js.map +1 -0
  144. package/dist/k-nocontent-Bh_yToGh.js +48 -0
  145. package/dist/k-nocontent-Bh_yToGh.js.map +1 -0
  146. package/dist/k-resizable-grid-Ch3iWZaL.js +3157 -0
  147. package/dist/k-resizable-grid-Ch3iWZaL.js.map +1 -0
  148. package/dist/k-standard-layout-CQ1VZoxa.js +5011 -0
  149. package/dist/k-standard-layout-CQ1VZoxa.js.map +1 -0
  150. package/dist/layouts/k-standard-layout.d.ts +16 -0
  151. package/dist/layouts/k-standard-layout.d.ts.map +1 -0
  152. package/dist/parts/index.d.ts +1 -0
  153. package/dist/parts/index.d.ts.map +1 -0
  154. package/dist/parts/index.js +53 -0
  155. package/dist/parts/index.js.map +1 -0
  156. package/dist/parts/k-app.d.ts +11 -0
  157. package/dist/parts/k-app.d.ts.map +1 -0
  158. package/dist/parts/k-container.d.ts +4 -0
  159. package/dist/parts/k-container.d.ts.map +1 -0
  160. package/dist/parts/k-contextmenu.d.ts +38 -0
  161. package/dist/parts/k-contextmenu.d.ts.map +1 -0
  162. package/dist/parts/k-dialog-content.d.ts +9 -0
  163. package/dist/parts/k-dialog-content.d.ts.map +1 -0
  164. package/dist/parts/k-element.d.ts +36 -0
  165. package/dist/parts/k-element.d.ts.map +1 -0
  166. package/dist/parts/k-part.d.ts +96 -0
  167. package/dist/parts/k-part.d.ts.map +1 -0
  168. package/dist/parts/k-resizable-grid.d.ts +31 -0
  169. package/dist/parts/k-resizable-grid.d.ts.map +1 -0
  170. package/dist/parts/k-tabs.d.ts +74 -0
  171. package/dist/parts/k-tabs.d.ts.map +1 -0
  172. package/dist/parts/k-toolbar.d.ts +21 -0
  173. package/dist/parts/k-toolbar.d.ts.map +1 -0
  174. package/dist/widgets/index.d.ts +1 -0
  175. package/dist/widgets/index.d.ts.map +1 -0
  176. package/dist/widgets/index.js +3 -0
  177. package/dist/widgets/index.js.map +1 -0
  178. package/dist/widgets/k-icon.d.ts +10 -0
  179. package/dist/widgets/k-icon.d.ts.map +1 -0
  180. package/dist/widgets/k-nocontent.d.ts +13 -0
  181. package/dist/widgets/k-nocontent.d.ts.map +1 -0
  182. package/dist/widgets/k-widget.d.ts +25 -0
  183. package/dist/widgets/k-widget.d.ts.map +1 -0
  184. package/package.json +81 -0
  185. package/src/api/base-classes.ts +10 -0
  186. package/src/api/constants.ts +3 -0
  187. package/src/api/index.ts +31 -0
  188. package/src/api/services.ts +52 -0
  189. package/src/api/types.ts +46 -0
  190. package/src/commands/files.ts +829 -0
  191. package/src/commands/global.ts +225 -0
  192. package/src/commands/index.ts +4 -0
  193. package/src/commands/version-info.ts +214 -0
  194. package/src/components/index.ts +10 -0
  195. package/src/components/k-app-selector.ts +233 -0
  196. package/src/components/k-app-switcher.ts +126 -0
  197. package/src/components/k-command.ts +236 -0
  198. package/src/components/k-extensions.ts +615 -0
  199. package/src/components/k-fastviews.ts +314 -0
  200. package/src/components/k-filebrowser.ts +442 -0
  201. package/src/components/k-language-selector.ts +166 -0
  202. package/src/components/k-log-terminal.ts +337 -0
  203. package/src/components/k-part-name.ts +54 -0
  204. package/src/components/k-tasks.ts +267 -0
  205. package/src/components/k-workspace-name.ts +56 -0
  206. package/src/contributions/default-ui-contributions.ts +51 -0
  207. package/src/contributions/index.ts +3 -0
  208. package/src/contributions/marketplace-catalog-contributions.ts +6 -0
  209. package/src/core/app-host-config.ts +23 -0
  210. package/src/core/apploader.ts +630 -0
  211. package/src/core/appstate.ts +15 -0
  212. package/src/core/commandregistry.ts +210 -0
  213. package/src/core/config.ts +29 -0
  214. package/src/core/constants.ts +27 -0
  215. package/src/core/contributionregistry.ts +77 -0
  216. package/src/core/di.ts +54 -0
  217. package/src/core/dialogservice.ts +266 -0
  218. package/src/core/editorregistry.ts +303 -0
  219. package/src/core/esmsh-service.ts +404 -0
  220. package/src/core/events.ts +68 -0
  221. package/src/core/extensionregistry.ts +399 -0
  222. package/src/core/filesys.ts +618 -0
  223. package/src/core/i18n.ts +221 -0
  224. package/src/core/index.ts +51 -0
  225. package/src/core/k-utils.ts +11 -0
  226. package/src/core/keybindings.ts +274 -0
  227. package/src/core/logger.ts +187 -0
  228. package/src/core/marketplaceregistry.ts +197 -0
  229. package/src/core/packageinfoservice.ts +56 -0
  230. package/src/core/persistenceservice.ts +15 -0
  231. package/src/core/settingsservice.ts +70 -0
  232. package/src/core/signals.ts +18 -0
  233. package/src/core/taskservice.ts +72 -0
  234. package/src/core/toast.ts +11 -0
  235. package/src/core/tree-utils.ts +24 -0
  236. package/src/dialogs/confirm-dialog.ts +72 -0
  237. package/src/dialogs/index.ts +4 -0
  238. package/src/dialogs/info-dialog.ts +67 -0
  239. package/src/dialogs/navigable-info-dialog.ts +256 -0
  240. package/src/dialogs/prompt-dialog.ts +123 -0
  241. package/src/externals/lit.ts +26 -0
  242. package/src/externals/third-party.ts +9 -0
  243. package/src/externals/webawesome.ts +54 -0
  244. package/src/i18n/extensions.json +39 -0
  245. package/src/i18n/fastviews.json +10 -0
  246. package/src/i18n/filebrowser.json +33 -0
  247. package/src/i18n/index.ts +25 -0
  248. package/src/i18n/logterminal.json +42 -0
  249. package/src/i18n/partname.json +12 -0
  250. package/src/i18n/tasks.json +12 -0
  251. package/src/i18n/workspace.json +12 -0
  252. package/src/icons/icons.txt +3 -0
  253. package/src/icons/js.svg +6 -0
  254. package/src/icons/jupyter.svg +18 -0
  255. package/src/icons/python.svg +15 -0
  256. package/src/index.ts +3 -0
  257. package/src/layouts/k-standard-layout.ts +174 -0
  258. package/src/parts/index.ts +6 -0
  259. package/src/parts/k-app.ts +29 -0
  260. package/src/parts/k-container.ts +4 -0
  261. package/src/parts/k-contextmenu.ts +245 -0
  262. package/src/parts/k-dialog-content.ts +31 -0
  263. package/src/parts/k-element.ts +100 -0
  264. package/src/parts/k-part.ts +158 -0
  265. package/src/parts/k-resizable-grid.ts +366 -0
  266. package/src/parts/k-tabs.ts +574 -0
  267. package/src/parts/k-toolbar.ts +158 -0
  268. package/src/vite-env.d.ts +2 -0
  269. package/src/widgets/index.ts +2 -0
  270. package/src/widgets/k-icon.ts +39 -0
  271. package/src/widgets/k-nocontent.ts +40 -0
  272. package/src/widgets/k-widget.ts +90 -0
@@ -0,0 +1,187 @@
1
+ import { rootContext } from './di';
2
+
3
+ export type LogLevel = 'info' | 'warning' | 'error' | 'debug';
4
+
5
+ const LogLevelPriority: Record<LogLevel, number> = {
6
+ 'debug': 0,
7
+ 'info': 1,
8
+ 'warning': 2,
9
+ 'error': 3,
10
+ };
11
+
12
+ // Global log level filter
13
+ let globalLogLevel: LogLevel = 'debug';
14
+
15
+ // Store original console methods before any interception
16
+ const originalConsole = {
17
+ log: console.log.bind(console),
18
+ info: console.info.bind(console),
19
+ warn: console.warn.bind(console),
20
+ error: console.error.bind(console),
21
+ debug: console.debug.bind(console)
22
+ };
23
+
24
+ // Log handler type
25
+ type LogHandler = (source: string, message: string, level: LogLevel) => void;
26
+
27
+ // Registered log handler (set by UI components like k-log-terminal)
28
+ let logHandler: LogHandler | null = null;
29
+
30
+ // Message buffer for logs that occur before handler is registered
31
+ const messageBuffer: Array<{source: string, message: string, level: LogLevel}> = [];
32
+
33
+ // Format console arguments
34
+ function formatArg(arg: any): string {
35
+ if (arg === null) return 'null';
36
+ if (arg === undefined) return 'undefined';
37
+ if (typeof arg === 'string') return arg;
38
+ if (typeof arg === 'number' || typeof arg === 'boolean') return String(arg);
39
+ if (arg instanceof Error) return `${arg.name}: ${arg.message}`;
40
+
41
+ try {
42
+ return JSON.stringify(arg);
43
+ } catch {
44
+ return String(arg);
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Global logger utility for logging messages
50
+ */
51
+ export class Logger {
52
+ constructor(private source: string) {}
53
+
54
+ info(message: string) {
55
+ this.log(message, 'info');
56
+ }
57
+
58
+ warning(message: string) {
59
+ this.log(message, 'warning');
60
+ }
61
+
62
+ warn(message: string) {
63
+ this.log(message, 'warning');
64
+ }
65
+
66
+ error(message: string) {
67
+ this.log(message, 'error');
68
+ }
69
+
70
+ debug(message: string) {
71
+ this.log(message, 'debug');
72
+ }
73
+
74
+ private log(message: string, level: LogLevel) {
75
+ dispatch(this.source, message, level);
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Check if a log level should be displayed based on current filter
81
+ */
82
+ function shouldLog(level: LogLevel): boolean {
83
+ return LogLevelPriority[level] >= LogLevelPriority[globalLogLevel];
84
+ }
85
+
86
+ /**
87
+ * Dispatch a log message to the registered handler (or buffer it if no handler registered)
88
+ */
89
+ function dispatch(source: string, message: string, level: LogLevel) {
90
+ if (!shouldLog(level)) {
91
+ return;
92
+ }
93
+
94
+ if (logHandler) {
95
+ logHandler(source, message, level);
96
+ } else {
97
+ // Buffer the message until handler is registered
98
+ messageBuffer.push({ source, message, level });
99
+ // Also log to console as fallback
100
+ originalConsole[level === 'error' ? 'error' : level === 'warning' ? 'warn' : level === 'debug' ? 'debug' : 'log'](
101
+ `[${source}] ${message}`
102
+ );
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Initialize console interception globally
108
+ * Note: This is called automatically when the logger module is imported
109
+ */
110
+ export function initializeConsoleInterception() {
111
+ console.log = function(...args: any[]) {
112
+ originalConsole.log.apply(console, args);
113
+ dispatch('Console', args.map(a => formatArg(a)).join(' '), 'info');
114
+ };
115
+
116
+ console.info = function(...args: any[]) {
117
+ originalConsole.info.apply(console, args);
118
+ dispatch('Console', args.map(a => formatArg(a)).join(' '), 'info');
119
+ };
120
+
121
+ console.warn = function(...args: any[]) {
122
+ originalConsole.warn.apply(console, args);
123
+ dispatch('Console', args.map(a => formatArg(a)).join(' '), 'warning');
124
+ };
125
+
126
+ console.error = function(...args: any[]) {
127
+ originalConsole.error.apply(console, args);
128
+ dispatch('Console', args.map(a => formatArg(a)).join(' '), 'error');
129
+ };
130
+
131
+ console.debug = function(...args: any[]) {
132
+ originalConsole.debug.apply(console, args);
133
+ dispatch('Console', args.map(a => formatArg(a)).join(' '), 'debug');
134
+ };
135
+ }
136
+
137
+ // Auto-initialize console interception when this module is imported
138
+ initializeConsoleInterception();
139
+
140
+ /**
141
+ * Register a handler to receive log messages
142
+ * Also flushes any buffered messages to the new handler
143
+ */
144
+ export function registerLogHandler(handler: LogHandler) {
145
+ logHandler = handler;
146
+
147
+ // Flush buffered messages
148
+ while (messageBuffer.length > 0) {
149
+ const msg = messageBuffer.shift();
150
+ if (msg) {
151
+ handler(msg.source, msg.message, msg.level);
152
+ }
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Unregister the current log handler
158
+ */
159
+ export function unregisterLogHandler() {
160
+ logHandler = null;
161
+ }
162
+
163
+ /**
164
+ * Create a logger instance for a specific source
165
+ */
166
+ export function createLogger(source: string): Logger {
167
+ return new Logger(source);
168
+ }
169
+
170
+ /**
171
+ * Set the global log level filter
172
+ */
173
+ export function setLogLevel(level: LogLevel) {
174
+ globalLogLevel = level;
175
+ }
176
+
177
+ /**
178
+ * Default logger instance for backward compatibility
179
+ * This provides a simple logger similar to the old logging.ts
180
+ */
181
+ const defaultLogger = createLogger('System');
182
+
183
+ // Register default logger in DI container for backward compatibility
184
+ rootContext.put('logger', defaultLogger);
185
+
186
+ export default defaultLogger;
187
+
@@ -0,0 +1,197 @@
1
+ import {appSettings} from "./settingsservice";
2
+ import {publish} from "./events";
3
+ import {createLogger} from "./logger";
4
+ import {extensionRegistry, Extension} from "./extensionregistry";
5
+ import {rootContext} from "./di";
6
+
7
+ const logger = createLogger('MarketplaceRegistry');
8
+
9
+ export const TOPIC_MARKETPLACE_CHANGED = "events/marketplaceregistry/changed";
10
+
11
+ export interface MarketplaceCatalog {
12
+ name?: string;
13
+ description?: string;
14
+ extensions?: Extension[];
15
+ }
16
+
17
+ const KEY_CATALOG_URLS = "marketplace.catalogUrls";
18
+
19
+ class MarketplaceRegistry {
20
+ private catalogUrls: string[] = [];
21
+ private loadingPromises: Map<string, Promise<MarketplaceCatalog>> = new Map();
22
+
23
+ constructor() {
24
+ // Load catalog URLs and refresh catalogs
25
+ this.loadCatalogUrls().then(() => {
26
+ this.refreshCatalogs().catch(err => {
27
+ logger.error(`Failed to refresh catalogs on init: ${err.message}`);
28
+ });
29
+ });
30
+ }
31
+
32
+ private async loadCatalogUrls(): Promise<void> {
33
+ try {
34
+ const urls = await appSettings.get(KEY_CATALOG_URLS);
35
+ this.catalogUrls = Array.isArray(urls) ? urls : [];
36
+ logger.debug(`Loaded ${this.catalogUrls.length} catalog URLs`);
37
+ } catch (error) {
38
+ logger.error(`Failed to load catalog URLs: ${error}`);
39
+ this.catalogUrls = [];
40
+ }
41
+ }
42
+
43
+ private async saveCatalogUrls(): Promise<void> {
44
+ await appSettings.set(KEY_CATALOG_URLS, this.catalogUrls);
45
+ publish(TOPIC_MARKETPLACE_CHANGED, {type: 'catalogs', urls: this.catalogUrls});
46
+ }
47
+
48
+ async addCatalogUrl(url: string): Promise<void> {
49
+ if (!this.isValidUrl(url)) {
50
+ throw new Error(`Invalid catalog URL: ${url}`);
51
+ }
52
+
53
+ if (this.catalogUrls.includes(url)) {
54
+ logger.debug(`Catalog URL already exists: ${url}`);
55
+ return;
56
+ }
57
+
58
+ this.catalogUrls.push(url);
59
+ await this.saveCatalogUrls();
60
+ logger.info(`Added catalog URL: ${url}`);
61
+
62
+ try {
63
+ await this.refreshCatalogs();
64
+ } catch (error) {
65
+ logger.warn(`Failed to refresh catalogs immediately after adding: ${error}`);
66
+ }
67
+ }
68
+
69
+ async removeCatalogUrl(url: string): Promise<void> {
70
+ const index = this.catalogUrls.indexOf(url);
71
+ if (index === -1) {
72
+ return;
73
+ }
74
+
75
+ this.catalogUrls.splice(index, 1);
76
+ await this.saveCatalogUrls();
77
+ logger.info(`Removed catalog URL: ${url}`);
78
+ }
79
+
80
+ getCatalogUrls(): string[] {
81
+ return [...this.catalogUrls];
82
+ }
83
+
84
+ private isValidUrl(url: string): boolean {
85
+ try {
86
+ const parsed = new URL(url);
87
+ return parsed.protocol === 'http:' || parsed.protocol === 'https:';
88
+ } catch {
89
+ return false;
90
+ }
91
+ }
92
+
93
+ private async fetchCatalog(url: string): Promise<MarketplaceCatalog> {
94
+ const existingPromise = this.loadingPromises.get(url);
95
+ if (existingPromise) {
96
+ return existingPromise;
97
+ }
98
+
99
+ const fetchPromise = (async () => {
100
+ try {
101
+ logger.debug(`Fetching catalog from: ${url}`);
102
+ const response = await fetch(url, {
103
+ method: 'GET',
104
+ headers: {
105
+ 'Accept': 'application/json',
106
+ },
107
+ });
108
+
109
+ if (!response.ok) {
110
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
111
+ }
112
+
113
+ const data = await response.json() as MarketplaceCatalog;
114
+
115
+ if (!data.extensions || !Array.isArray(data.extensions)) {
116
+ throw new Error('Invalid catalog format: extensions array is required');
117
+ }
118
+
119
+ const catalog: MarketplaceCatalog = {
120
+ name: data.name,
121
+ description: data.description,
122
+ extensions: data.extensions || [],
123
+ };
124
+
125
+ const extCount = catalog.extensions?.length || 0;
126
+ logger.debug(`Successfully fetched catalog from ${url}: ${extCount} extensions`);
127
+ return catalog;
128
+ } catch (error) {
129
+ logger.error(`Failed to fetch catalog from ${url}: ${error}`);
130
+ throw error;
131
+ } finally {
132
+ this.loadingPromises.delete(url);
133
+ }
134
+ })();
135
+
136
+ this.loadingPromises.set(url, fetchPromise);
137
+ return fetchPromise;
138
+ }
139
+
140
+ async refreshCatalogs(): Promise<void> {
141
+ logger.info(`Refreshing ${this.catalogUrls.length} catalogs...`);
142
+
143
+ const promises = this.catalogUrls.map(url =>
144
+ this.fetchCatalog(url).catch(error => {
145
+ logger.warn(`Failed to refresh catalog ${url}: ${error.message}`);
146
+ return null;
147
+ })
148
+ );
149
+
150
+ const catalogs = await Promise.allSettled(promises);
151
+
152
+ // Register all marketplace extensions from successfully fetched catalogs
153
+ // Extensions will register apps themselves when loaded
154
+ catalogs.forEach((result, index) => {
155
+ if (result.status === 'fulfilled' && result.value) {
156
+ const catalog = result.value;
157
+
158
+ // Register extensions
159
+ if (catalog.extensions) {
160
+ catalog.extensions.forEach(marketplaceExt => {
161
+ // Only register if not already registered
162
+ if (!extensionRegistry.getExtensions().find(e => e.id === marketplaceExt.id)) {
163
+ const extension: Extension = {
164
+ ...marketplaceExt,
165
+ external: true
166
+ };
167
+ extensionRegistry.registerExtension(extension);
168
+ logger.debug(`Registered marketplace extension: ${marketplaceExt.id}`);
169
+ }
170
+ });
171
+ }
172
+ }
173
+ });
174
+
175
+ // Publish event after registration
176
+ publish(TOPIC_MARKETPLACE_CHANGED, {type: 'refreshed'});
177
+ logger.info('Catalog refresh completed');
178
+ }
179
+
180
+ getMarketplaceExtension(extensionId: string): Extension | undefined {
181
+ // Check if extension is registered in extensionRegistry and is external
182
+ const extension = extensionRegistry.getExtensions().find(e => e.id === extensionId);
183
+ if (extension && extension.external) {
184
+ return extension;
185
+ }
186
+ return undefined;
187
+ }
188
+
189
+ isMarketplaceExtension(extensionId: string): boolean {
190
+ const extension = extensionRegistry.getExtensions().find(e => e.id === extensionId);
191
+ return extension !== undefined && extension.external === true;
192
+ }
193
+ }
194
+
195
+ export const marketplaceRegistry = new MarketplaceRegistry();
196
+ rootContext.put("marketplaceRegistry", marketplaceRegistry);
197
+
@@ -0,0 +1,56 @@
1
+ import { html, TemplateResult } from "lit";
2
+ import { rootContext } from "./di";
3
+
4
+ export interface PackageInfo {
5
+ name: string;
6
+ version: string;
7
+ dependencies?: Record<string, string>;
8
+ devDependencies?: Record<string, string>;
9
+ }
10
+
11
+ class PackageInfoService {
12
+ private packages: PackageInfo[] = [];
13
+
14
+ addPackage(packageInfo: PackageInfo): void {
15
+ this.packages.push(packageInfo);
16
+ }
17
+
18
+ hasPackages(): boolean {
19
+ return this.packages.length > 0 && this.packages.some(pkg =>
20
+ pkg.dependencies && Object.keys(pkg.dependencies).length > 0
21
+ );
22
+ }
23
+
24
+ renderTree(): TemplateResult {
25
+ if (this.packages.length === 0) {
26
+ return html``;
27
+ }
28
+
29
+ return html`
30
+ <wa-tree style="--indent-guide-width: 1px;">
31
+ ${this.packages.map(pkg => {
32
+ const deps = pkg.dependencies || {};
33
+ const depEntries = Object.entries(deps);
34
+
35
+ if (depEntries.length === 0) {
36
+ return html``;
37
+ }
38
+
39
+ return html`
40
+ <wa-tree-item expanded>
41
+ <span>${pkg.name}</span>
42
+ ${depEntries.map(([name, version]) => html`
43
+ <wa-tree-item>
44
+ <span>${name} <small>${version}</small></span>
45
+ </wa-tree-item>
46
+ `)}
47
+ </wa-tree-item>
48
+ `;
49
+ })}
50
+ </wa-tree>
51
+ `;
52
+ }
53
+ }
54
+
55
+ export const packageInfoService = new PackageInfoService();
56
+ rootContext.put("packageInfoService", packageInfoService);
@@ -0,0 +1,15 @@
1
+ import {get, set} from 'idb-keyval';
2
+ import {rootContext} from "./di";
3
+
4
+ export class PersistenceService {
5
+ async persistObject(key: string, value: any) {
6
+ return set(key, value);
7
+ }
8
+
9
+ async getObject(key: string): Promise<any> {
10
+ return get(key)
11
+ }
12
+ }
13
+
14
+ export const persistenceService = new PersistenceService()
15
+ rootContext.put("persistenceService", persistenceService)
@@ -0,0 +1,70 @@
1
+ import {publish} from "./events";
2
+ import {persistenceService} from "./persistenceservice";
3
+ import {rootContext} from "./di";
4
+
5
+ export const SETTINGS_FILE_PATH = ".geospace/settings.json"
6
+
7
+ export const DIALOG_SETTINGS_KEY = "dialogSettings"
8
+
9
+ export interface AppSettings {
10
+ [key: string]: any;
11
+ }
12
+
13
+ export const TOPIC_SETTINGS_CHANGED = "events/settings/changed"
14
+
15
+ class SettingsService {
16
+
17
+ private appSettings?: AppSettings;
18
+
19
+ private async checkSettings() {
20
+ if (!this.appSettings) {
21
+ this.appSettings = await persistenceService.getObject(SETTINGS_FILE_PATH)
22
+ if (!this.appSettings) {
23
+ this.appSettings = {}
24
+ await persistenceService.persistObject(SETTINGS_FILE_PATH, this.appSettings)
25
+ }
26
+ publish(TOPIC_SETTINGS_CHANGED, this.appSettings)
27
+ }
28
+ }
29
+
30
+ public async get(key: string) {
31
+ await this.checkSettings()
32
+ return this.appSettings![key]
33
+ }
34
+
35
+ public async set(key: string, value: any) {
36
+ await this.checkSettings()
37
+ this.appSettings![key] = value
38
+ await persistenceService.persistObject(SETTINGS_FILE_PATH, this.appSettings)
39
+ publish(TOPIC_SETTINGS_CHANGED, this.appSettings)
40
+ }
41
+
42
+ public async getAll() {
43
+ await this.checkSettings()
44
+ return this.appSettings!;
45
+ }
46
+
47
+ public async setAll(settings: AppSettings) {
48
+ this.appSettings = settings
49
+ await persistenceService.persistObject(SETTINGS_FILE_PATH, this.appSettings)
50
+ publish(TOPIC_SETTINGS_CHANGED, this.appSettings)
51
+ }
52
+
53
+ public async getDialogSetting(key: string) {
54
+ await this.checkSettings()
55
+ const dialogSettings = this.appSettings![DIALOG_SETTINGS_KEY] || {}
56
+ return dialogSettings[key]
57
+ }
58
+
59
+ public async setDialogSetting(key: string, value: any) {
60
+ await this.checkSettings()
61
+ const dialogSettings = this.appSettings![DIALOG_SETTINGS_KEY] || {}
62
+ dialogSettings[key] = value
63
+ this.appSettings![DIALOG_SETTINGS_KEY] = dialogSettings
64
+ await persistenceService.persistObject(SETTINGS_FILE_PATH, this.appSettings)
65
+ publish(TOPIC_SETTINGS_CHANGED, this.appSettings)
66
+ }
67
+ }
68
+
69
+ export const appSettings = new SettingsService();
70
+ rootContext.put("appSettings", appSettings)
@@ -0,0 +1,18 @@
1
+ import {Signal} from "@lit-labs/signals";
2
+
3
+ export const watchSignal = (signal: Signal.State<any> | Signal.Computed<any>, callback: (value: any) => void): (() => void) => {
4
+ const watcher = new Signal.subtle.Watcher(async () => {
5
+ try {
6
+ await 0;
7
+ callback(signal.get());
8
+ } finally {
9
+ watcher.watch(signal);
10
+ }
11
+ });
12
+ watcher.watch(signal);
13
+ signal.get();
14
+
15
+ return () => {
16
+ watcher.unwatch(signal);
17
+ };
18
+ }
@@ -0,0 +1,72 @@
1
+ import {activeTasksSignal} from "./appstate";
2
+ import {rootContext} from "./di";
3
+
4
+ export interface ProgressMonitor {
5
+ name: string
6
+ message: string
7
+ currentStep: number
8
+ totalSteps: number
9
+ progress: number // Manual progress percentage (0-100), overrides step-based calculation if >= 0
10
+ }
11
+
12
+ export type Task = (progressMonitor: ProgressMonitor) => any
13
+ export type AsyncTask = (progressMonitor: ProgressMonitor) => Promise<any>
14
+
15
+ export class TaskService {
16
+ private tasks: ProgressMonitor[] = []
17
+ private updateCounter = 0
18
+
19
+ private notifyUpdate() {
20
+ // Always increment counter to ensure signal value changes and triggers re-render
21
+ this.updateCounter++
22
+ activeTasksSignal.set(this.updateCounter)
23
+ }
24
+
25
+ public run(name: string, task: Task) {
26
+ const progressMonitor = this.createProgressMonitor(name)
27
+ try {
28
+ this.tasks.push(progressMonitor)
29
+ this.notifyUpdate()
30
+ task(progressMonitor)
31
+ } finally {
32
+ this.tasks.splice(this.tasks.indexOf(progressMonitor), 1)
33
+ this.notifyUpdate()
34
+ }
35
+ }
36
+
37
+ public async runAsync(name: string, task: AsyncTask) {
38
+ const progressMonitor = this.createProgressMonitor(name)
39
+ this.tasks.push(progressMonitor)
40
+ this.notifyUpdate()
41
+ return task(progressMonitor).finally(() => {
42
+ this.tasks.splice(this.tasks.indexOf(progressMonitor), 1)
43
+ this.notifyUpdate()
44
+ })
45
+ }
46
+
47
+ private createProgressMonitor(name: string): ProgressMonitor {
48
+ const monitor = {
49
+ name: name,
50
+ message: "",
51
+ currentStep: 0,
52
+ totalSteps: -1, // -1 indicates indefinite progress
53
+ progress: -1 // -1 means use step-based calculation
54
+ } as ProgressMonitor
55
+
56
+ // Create a proxy to detect property changes and trigger UI updates
57
+ return new Proxy(monitor, {
58
+ set: (target, prop, value) => {
59
+ (target as any)[prop] = value
60
+ this.notifyUpdate()
61
+ return true
62
+ }
63
+ })
64
+ }
65
+
66
+ getActiveTasks() {
67
+ return this.tasks
68
+ }
69
+ }
70
+
71
+ export const taskService = new TaskService()
72
+ rootContext.put("taskService", taskService)
@@ -0,0 +1,11 @@
1
+ export const toastInfo = (msg: string) => {
2
+ console.info("[TOAST] INFO:", msg);
3
+ }
4
+
5
+ export const toastError = (msg: string) => {
6
+ console.error("[TOAST] ERROR:", msg);
7
+ }
8
+
9
+ export const toastWarning = (msg: string) => {
10
+ console.warn("[TOAST] WARNING:", msg);
11
+ }
@@ -0,0 +1,24 @@
1
+ import {Contribution} from "./contributionregistry";
2
+
3
+ export interface TreeNode {
4
+ data: any;
5
+ label: string;
6
+ icon?: string;
7
+ leaf: boolean;
8
+ children: TreeNode[];
9
+ }
10
+
11
+ export const treeNodeComparator = (c1: TreeNode, c2: TreeNode) => {
12
+ if (!c1.leaf && c2.leaf) {
13
+ return -1
14
+ }
15
+ if (c1.leaf && !c2.leaf) {
16
+ return 1
17
+ }
18
+ return c1.label.localeCompare(c2.label)
19
+ }
20
+
21
+ export interface TreeContribution extends Contribution {
22
+ contributionId?: string;
23
+ state?: { [key: string]: any }
24
+ }