@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,829 @@
1
+ import {registerAll, type ExecutionContext} from "../core/commandregistry";
2
+ import {File, workspaceService, Directory} from "../core/filesys";
3
+ import {toastError, toastInfo} from "../core/toast";
4
+ import {promptDialog, confirmDialog} from "../dialogs";
5
+ import {activeSelectionSignal} from "../core/appstate";
6
+ import {editorRegistry, EditorContentProvider} from "../core/editorregistry";
7
+ import logger from "../core/logger";
8
+
9
+ async function getWorkspaceAndPath(params: any, requirePath: boolean = true): Promise<{workspace: Directory, path: string} | null> {
10
+ const workspace = await workspaceService.getWorkspace()
11
+ if (!workspace) {
12
+ return null
13
+ }
14
+
15
+ const path = params?.path
16
+ if (requirePath && !path) {
17
+ return null
18
+ }
19
+
20
+ return { workspace, path: path || '' }
21
+ }
22
+
23
+ /**
24
+ * Type guard to check if an editor implements EditorContentProvider
25
+ */
26
+ function isEditorContentProvider(editor: any): editor is EditorContentProvider {
27
+ return editor &&
28
+ typeof editor.getContent === 'function' &&
29
+ typeof editor.getSelection === 'function' &&
30
+ typeof editor.getSnippet === 'function' &&
31
+ typeof editor.getLanguage === 'function' &&
32
+ typeof editor.getFilePath === 'function';
33
+ }
34
+
35
+ /**
36
+ * Helper to get a resource from path or active selection
37
+ */
38
+ async function getResourceFromContext(context: ExecutionContext, params?: any): Promise<any> {
39
+ let resource = undefined
40
+ const path = params?.path || context.params?.["path"]
41
+
42
+ if (path) {
43
+ const workspaceDir = await workspaceService.getWorkspace()
44
+ if (workspaceDir) {
45
+ resource = await workspaceDir.getResource(path)
46
+ }
47
+ }
48
+
49
+ if (!resource) {
50
+ resource = activeSelectionSignal.get()
51
+ }
52
+
53
+ return resource
54
+ }
55
+
56
+ /**
57
+ * Helper to read text file contents with error handling
58
+ */
59
+ async function readTextFile(file: File): Promise<string | null> {
60
+ try {
61
+ const contents = await file.getContents()
62
+ if (typeof contents !== 'string') {
63
+ toastError("File is not a text file")
64
+ return null
65
+ }
66
+ return contents
67
+ } catch (err: any) {
68
+ toastError(`Failed to read file: ${err.message}`)
69
+ return null
70
+ }
71
+ }
72
+
73
+ /**
74
+ * Helper to create null editor response
75
+ */
76
+ function createNullEditorResponse(includeCursorLine: boolean = false) {
77
+ const base = {
78
+ filePath: null,
79
+ language: null
80
+ }
81
+ if (includeCursorLine) {
82
+ return { ...base, snippet: null, cursorLine: null }
83
+ }
84
+ return base
85
+ }
86
+
87
+ async function getWorkspaceAndFile(params: any, requirePath: boolean = true): Promise<{workspace: Directory, path: string, file: File} | null> {
88
+ const result = await getWorkspaceAndPath(params, requirePath)
89
+ if (!result) {
90
+ return null
91
+ }
92
+
93
+ const { workspace, path } = result
94
+
95
+ if (!path) {
96
+ return null
97
+ }
98
+
99
+ try {
100
+ const resource = await workspace.getResource(path)
101
+ if (!resource || !(resource instanceof File)) {
102
+ return null
103
+ }
104
+
105
+ return { workspace, path, file: resource }
106
+ } catch (err: any) {
107
+ return null
108
+ }
109
+ }
110
+
111
+ registerAll({
112
+ command: {
113
+ "id": "create_file",
114
+ "name": "Create new file",
115
+ "description": "Creates a new file within the workspace. For .geospace map files, use create_map_file instead.",
116
+ "keyBinding": "CTRL+N",
117
+ "parameters": [
118
+ {
119
+ "name": "path",
120
+ "description": "the path including name of the file to be created, must be relative to the workspace",
121
+ "required": false
122
+ },
123
+ {
124
+ "name": "contents",
125
+ "description": "the textual contents of the file",
126
+ "required": false
127
+ },
128
+ {
129
+ "name": "ask",
130
+ "description": "whether to prompt the user for the file path",
131
+ "required": false
132
+ },
133
+ {
134
+ "name": "extension",
135
+ "description": "required file extension (e.g., '.geospace'), will be appended if missing",
136
+ "required": false
137
+ }
138
+ ],
139
+ "output": [
140
+ {
141
+ "name": "path",
142
+ "description": "the path of the created file"
143
+ }
144
+ ]
145
+ },
146
+ handler: {
147
+ execute: async ({params}: any) => {
148
+ let path = params?.path
149
+ const contents = params?.contents
150
+ const ask = params?.ask
151
+ const extension = params?.extension
152
+
153
+ if (ask || !path) {
154
+ path = await promptDialog("Enter path to new file (directories will be created if not exist):", path || "")
155
+ if (!path) {
156
+ return
157
+ }
158
+ }
159
+
160
+ if (extension && !path.endsWith(extension)) {
161
+ path += extension
162
+ }
163
+
164
+ const result = await getWorkspaceAndPath({path}, true)
165
+ if (!result) {
166
+ return
167
+ }
168
+ const { workspace: workspaceDir } = result
169
+
170
+ const existingResource = await workspaceDir.getResource(path)
171
+ if (existingResource) {
172
+ const overwrite = await confirmDialog(`File "${path}" already exists. Do you want to overwrite it?`)
173
+ if (!overwrite) {
174
+ return
175
+ }
176
+ }
177
+
178
+ const createdResource = await workspaceDir.getResource(path, {create: true})
179
+ if (!createdResource) {
180
+ toastError("Could not create file: " + path)
181
+ } else {
182
+ if (contents) {
183
+ await (createdResource as File).saveContents(contents)
184
+ }
185
+ logger.info("File created: " + path)
186
+ }
187
+
188
+ return path
189
+ }
190
+ }
191
+ })
192
+
193
+ registerAll({
194
+ command: {
195
+ "id": "rename_resource",
196
+ "name": "Rename a resource (file or directory)",
197
+ "description": "Renames a resource (file or directory)",
198
+ "keyBinding": "F2",
199
+ "parameters": [
200
+ {
201
+ "name": "path",
202
+ "description": "the path of the resource within the workspace to rename or the currently active selection",
203
+ "required": false
204
+ },
205
+ {
206
+ "name": "newName",
207
+ "description": "the new name for the resource",
208
+ "required": false
209
+ }
210
+ ]
211
+ },
212
+ handler: {
213
+ execute: async context => {
214
+ const resource = await getResourceFromContext(context)
215
+ if (!resource) {
216
+ toastError("No resource to rename provided!")
217
+ return
218
+ }
219
+
220
+ const currentName = resource.getName()
221
+ const newName = context.params?.newName ||
222
+ await promptDialog(`Enter new name for "${currentName}":`, currentName)
223
+
224
+ if (!newName || newName === currentName) {
225
+ return
226
+ }
227
+
228
+ try {
229
+ await resource.rename(newName)
230
+ toastInfo(`Resource renamed to: ${newName}`)
231
+ } catch (err: any) {
232
+ toastError(`Failed to rename ${currentName}: ${err.message}`)
233
+ }
234
+ }
235
+ }
236
+ })
237
+
238
+ registerAll({
239
+ command: {
240
+ "id": "delete_resource",
241
+ "name": "Delete a resource (file or directory)",
242
+ "description": "Deletes a resource (file or directory)",
243
+ "parameters": [
244
+ {
245
+ "name": "path",
246
+ "description": "the path of the resource within the workspace to delete or the currently active selection",
247
+ "required": false
248
+ },
249
+ {
250
+ "name": "confirm",
251
+ "description": "whether to ask the user to confirm the deletion, true by default",
252
+ "required": false
253
+ }
254
+ ]
255
+ },
256
+ handler: {
257
+ execute: async context => {
258
+ const resource = await getResourceFromContext(context)
259
+ if (!resource) {
260
+ toastError("No resource to delete provided!")
261
+ return
262
+ }
263
+ const path = resource.getWorkspacePath()
264
+ const confirmParam = context.params && context.params["confirm"]
265
+ let yes = true
266
+ if (confirmParam === undefined || confirmParam === true) {
267
+ yes = await confirmDialog(`Are you sure you want to delete ${path}?`)
268
+ }
269
+
270
+ if (yes) {
271
+ try {
272
+ await resource.delete()
273
+ toastInfo("Resource deleted: " + path)
274
+ } catch (err: any) {
275
+ toastError(`Resource ${path} could not be deleted: ${err.message || err}`)
276
+ }
277
+ }
278
+ }
279
+ }
280
+ })
281
+
282
+ // Register local filesystem workspace contribution
283
+ registerAll({
284
+ command: {
285
+ "id": "load_workspace",
286
+ "name": "Local Folder",
287
+ "description": "Connect to a local folder using File System Access API",
288
+ "parameters": []
289
+ },
290
+ handler: {
291
+ execute: async _context => {
292
+ // @ts-ignore
293
+ await window.showDirectoryPicker({
294
+ mode: "readwrite"
295
+ }).then((dirHandle: FileSystemDirectoryHandle) => {
296
+ return workspaceService.connectWorkspace(dirHandle);
297
+ }).catch((err: Error) => {
298
+ toastError(err.message)
299
+ });
300
+ }
301
+ },
302
+ contribution: {
303
+ target: "filebrowser.connections",
304
+ label: "Local Folder",
305
+ icon: "folder-open"
306
+ }
307
+ })
308
+
309
+ registerAll({
310
+ command: {
311
+ "id": "reload_workspace",
312
+ "name": "Reload workspace folder",
313
+ "description": "Reloads the active workspace folder",
314
+ "parameters": []
315
+ },
316
+ handler: {
317
+ execute: async _context => {
318
+ const workspace = await workspaceService.getWorkspace()
319
+ if (!workspace) {
320
+ toastError("No workspace selected.")
321
+ }
322
+ workspace!.touch()
323
+ }
324
+ }
325
+ })
326
+
327
+ registerAll({
328
+ command: {
329
+ "id": "open_editor",
330
+ "name": "Open editor",
331
+ "description": "Opens a file in an editor",
332
+ "parameters": [
333
+ {
334
+ "name": "path",
335
+ "description": "tha path of the file to open within the workspace",
336
+ "required": true
337
+ }
338
+ ]
339
+ },
340
+ handler: {
341
+ execute: async context => {
342
+ const result = await getWorkspaceAndFile({path: context.params?.["path"]})
343
+ if (!result) {
344
+ return
345
+ }
346
+
347
+ const { file } = result
348
+ await editorRegistry.loadEditor(file)
349
+ }
350
+ }
351
+ })
352
+
353
+ registerAll({
354
+ command: {
355
+ "id": "head_file",
356
+ "name": "Head - Show first lines",
357
+ "description": "Shows the first N lines of a file",
358
+ "parameters": [
359
+ {
360
+ "name": "path",
361
+ "description": "the path of the file to read",
362
+ "required": true
363
+ },
364
+ {
365
+ "name": "lines",
366
+ "description": "number of lines to show from the beginning (default: 10)",
367
+ "type": "number",
368
+ "required": false
369
+ }
370
+ ],
371
+ "output": [
372
+ {
373
+ "name": "content",
374
+ "description": "the first N lines of the file"
375
+ }
376
+ ]
377
+ },
378
+ handler: {
379
+ execute: async ({params}: any) => {
380
+ const result = await getWorkspaceAndFile(params)
381
+ if (!result) {
382
+ return
383
+ }
384
+
385
+ const { file } = result
386
+ const numLines = params?.lines ? parseInt(params.lines, 10) : 10
387
+ if (isNaN(numLines) || numLines < 1) {
388
+ toastError("Number of lines must be a positive integer")
389
+ return
390
+ }
391
+
392
+ const contents = await readTextFile(file)
393
+ if (!contents) {
394
+ return
395
+ }
396
+
397
+ const lines = contents.split('\n')
398
+ const headLines = lines.slice(0, numLines).join('\n')
399
+ return headLines
400
+ }
401
+ }
402
+ })
403
+
404
+ registerAll({
405
+ command: {
406
+ "id": "tail_file",
407
+ "name": "Tail - Show last lines",
408
+ "description": "Shows the last N lines of a file",
409
+ "parameters": [
410
+ {
411
+ "name": "path",
412
+ "description": "the path of the file to read",
413
+ "required": true
414
+ },
415
+ {
416
+ "name": "lines",
417
+ "description": "number of lines to show from the end (default: 10)",
418
+ "type": "number",
419
+ "required": false
420
+ }
421
+ ],
422
+ "output": [
423
+ {
424
+ "name": "content",
425
+ "description": "the last N lines of the file"
426
+ }
427
+ ]
428
+ },
429
+ handler: {
430
+ execute: async ({params}: any) => {
431
+ const result = await getWorkspaceAndFile(params)
432
+ if (!result) {
433
+ return
434
+ }
435
+
436
+ const { file } = result
437
+ const numLines = params?.lines ? parseInt(params.lines, 10) : 10
438
+ if (isNaN(numLines) || numLines < 1) {
439
+ toastError("Number of lines must be a positive integer")
440
+ return
441
+ }
442
+
443
+ const contents = await readTextFile(file)
444
+ if (!contents) {
445
+ return
446
+ }
447
+
448
+ const lines = contents.split('\n')
449
+ const tailLines = lines.slice(-numLines).join('\n')
450
+ return tailLines
451
+ }
452
+ }
453
+ })
454
+
455
+ registerAll({
456
+ command: {
457
+ "id": "cat_file",
458
+ "name": "Cat - Show file contents",
459
+ "description": "Shows the complete contents of a file",
460
+ "parameters": [
461
+ {
462
+ "name": "path",
463
+ "description": "the path of the file to read",
464
+ "required": true
465
+ }
466
+ ],
467
+ "output": [
468
+ {
469
+ "name": "content",
470
+ "description": "the complete contents of the file"
471
+ }
472
+ ]
473
+ },
474
+ handler: {
475
+ execute: async ({params}: any) => {
476
+ const result = await getWorkspaceAndFile(params)
477
+ if (!result) {
478
+ return
479
+ }
480
+
481
+ const { file } = result
482
+
483
+ return await readTextFile(file)
484
+ }
485
+ }
486
+ })
487
+
488
+ registerAll({
489
+ command: {
490
+ "id": "wc_file",
491
+ "name": "Word count",
492
+ "description": "Counts lines, words, and characters in a file",
493
+ "parameters": [
494
+ {
495
+ "name": "path",
496
+ "description": "the path of the file to analyze",
497
+ "required": true
498
+ }
499
+ ],
500
+ "output": [
501
+ {
502
+ "name": "lines",
503
+ "description": "number of lines in the file"
504
+ },
505
+ {
506
+ "name": "words",
507
+ "description": "number of words in the file"
508
+ },
509
+ {
510
+ "name": "characters",
511
+ "description": "number of characters in the file"
512
+ }
513
+ ]
514
+ },
515
+ handler: {
516
+ execute: async ({params}: any) => {
517
+ const result = await getWorkspaceAndFile(params)
518
+ if (!result) {
519
+ return
520
+ }
521
+
522
+ const { file } = result
523
+
524
+ const contents = await readTextFile(file)
525
+ if (!contents) {
526
+ return
527
+ }
528
+
529
+ const lines = contents.split('\n')
530
+ const lineCount = lines.length
531
+ const wordCount = contents.trim() === '' ? 0 : contents.trim().split(/\s+/).filter(w => w.length > 0).length
532
+ const charCount = contents.length
533
+
534
+ return {
535
+ lines: lineCount,
536
+ words: wordCount,
537
+ characters: charCount
538
+ }
539
+ }
540
+ }
541
+ })
542
+
543
+ registerAll({
544
+ command: {
545
+ "id": "file_exists",
546
+ "name": "Check if file exists",
547
+ "description": "Checks if a file exists at the given path",
548
+ "parameters": [
549
+ {
550
+ "name": "path",
551
+ "description": "the path of the file to check, relative to the workspace",
552
+ "required": true
553
+ }
554
+ ],
555
+ "output": [
556
+ {
557
+ "name": "exists",
558
+ "description": "true if the file exists, false otherwise"
559
+ }
560
+ ]
561
+ },
562
+ handler: {
563
+ execute: async ({params}: any) => {
564
+ const result = await getWorkspaceAndPath(params)
565
+ if (!result) {
566
+ return false
567
+ }
568
+
569
+ const { workspace, path } = result
570
+
571
+ try {
572
+ const resource = await workspace.getResource(path)
573
+ return resource instanceof File
574
+ } catch (err: any) {
575
+ return false
576
+ }
577
+ }
578
+ }
579
+ })
580
+
581
+ async function collectFilesRecursive(directory: Directory): Promise<string[]> {
582
+ const files: string[] = []
583
+ const children = await directory.listChildren(true)
584
+
585
+ for (const child of children) {
586
+ if (child instanceof File) {
587
+ files.push(child.getWorkspacePath())
588
+ } else if (child instanceof Directory) {
589
+ const subFiles = await collectFilesRecursive(child)
590
+ files.push(...subFiles)
591
+ }
592
+ }
593
+
594
+ return files
595
+ }
596
+
597
+ registerAll({
598
+ command: {
599
+ "id": "ls",
600
+ "name": "List files",
601
+ "description": "Lists files from a directory. If recursive is provided, it traverses from the provided directory down to all leaves. If no directory is provided, it will traverse from the workspace root.",
602
+ "parameters": [
603
+ {
604
+ "name": "path",
605
+ "description": "the path of the directory to list, relative to the workspace. If not provided, uses workspace root",
606
+ "required": false
607
+ },
608
+ {
609
+ "name": "recursive",
610
+ "description": "whether to recursively traverse all subdirectories",
611
+ "type": "boolean",
612
+ "required": false
613
+ }
614
+ ],
615
+ "output": [
616
+ {
617
+ "name": "files",
618
+ "description": "array of file objects with path and size information"
619
+ }
620
+ ]
621
+ },
622
+ handler: {
623
+ execute: async ({params}: any) => {
624
+ const result = await getWorkspaceAndPath(params, false)
625
+ if (!result) {
626
+ toastError("No workspace available")
627
+ return []
628
+ }
629
+
630
+ const { workspace, path } = result
631
+ const recursive = params?.recursive === true || params?.recursive === "true"
632
+
633
+ try {
634
+ let targetDir: Directory = workspace
635
+
636
+ if (path) {
637
+ const resource = await workspace.getResource(path)
638
+ if (!resource) {
639
+ toastError(`Path not found: ${path}`)
640
+ return []
641
+ }
642
+ if (!(resource instanceof Directory)) {
643
+ toastError(`Path is not a directory: ${path}`)
644
+ return []
645
+ }
646
+ targetDir = resource
647
+ }
648
+
649
+ if (recursive) {
650
+ const files = await collectFilesRecursive(targetDir)
651
+ const result = []
652
+ for (const filePath of files) {
653
+ const file = await workspace.getResource(filePath)
654
+ if (file instanceof File) {
655
+ const size = await file.size()
656
+ result.push({
657
+ path: filePath,
658
+ size: size
659
+ })
660
+ }
661
+ }
662
+ return result
663
+ } else {
664
+ const children = await targetDir.listChildren(true)
665
+ const result = []
666
+ for (const child of children) {
667
+ if (child instanceof File) {
668
+ const size = await child.size()
669
+ result.push({
670
+ path: child.getWorkspacePath(),
671
+ size: size
672
+ })
673
+ }
674
+ }
675
+ return result
676
+ }
677
+ } catch (err: any) {
678
+ toastError(`Failed to list files: ${err.message}`)
679
+ return []
680
+ }
681
+ }
682
+ }
683
+ })
684
+
685
+ registerAll({
686
+ command: {
687
+ "id": "get_active_editor_content",
688
+ "name": "Get active editor content",
689
+ "description": "Gets the complete contents of the currently active editor. Returns null if no editor is active or if the editor is not a code editor.",
690
+ "parameters": [],
691
+ "output": [
692
+ {
693
+ "name": "content",
694
+ "description": "the complete contents of the active editor, or null if no editor is active"
695
+ },
696
+ {
697
+ "name": "filePath",
698
+ "description": "the workspace path of the file in the active editor, or null if no editor is active"
699
+ },
700
+ {
701
+ "name": "language",
702
+ "description": "the programming language of the active editor, or null if no editor is active"
703
+ }
704
+ ]
705
+ },
706
+ handler: {
707
+ execute: async (context: ExecutionContext) => {
708
+ const activeEditor = context.activeEditor as any
709
+ if (!isEditorContentProvider(activeEditor)) {
710
+ return { ...createNullEditorResponse(), content: null }
711
+ }
712
+
713
+ try {
714
+ return {
715
+ content: activeEditor.getContent(),
716
+ filePath: activeEditor.getFilePath(),
717
+ language: activeEditor.getLanguage()
718
+ }
719
+ } catch (err: any) {
720
+ return { ...createNullEditorResponse(), content: null }
721
+ }
722
+ }
723
+ }
724
+ })
725
+
726
+ registerAll({
727
+ command: {
728
+ "id": "get_active_editor_selection",
729
+ "name": "Get active editor selection",
730
+ "description": "Gets the currently selected text in the active editor. Returns null if no editor is active, no selection exists, or if the editor is not a code editor.",
731
+ "parameters": [],
732
+ "output": [
733
+ {
734
+ "name": "selection",
735
+ "description": "the selected text in the active editor, or null if no selection exists or no editor is active"
736
+ },
737
+ {
738
+ "name": "filePath",
739
+ "description": "the workspace path of the file in the active editor, or null if no editor is active"
740
+ },
741
+ {
742
+ "name": "language",
743
+ "description": "the programming language of the active editor, or null if no editor is active"
744
+ }
745
+ ]
746
+ },
747
+ handler: {
748
+ execute: async (context: ExecutionContext) => {
749
+ const activeEditor = context.activeEditor as any
750
+ if (!isEditorContentProvider(activeEditor)) {
751
+ return { ...createNullEditorResponse(), selection: null }
752
+ }
753
+
754
+ try {
755
+ return {
756
+ selection: activeEditor.getSelection(),
757
+ filePath: activeEditor.getFilePath(),
758
+ language: activeEditor.getLanguage()
759
+ }
760
+ } catch (err: any) {
761
+ return { ...createNullEditorResponse(), selection: null }
762
+ }
763
+ }
764
+ }
765
+ })
766
+
767
+ registerAll({
768
+ command: {
769
+ "id": "get_active_editor_snippet",
770
+ "name": "Get active editor snippet around cursor",
771
+ "description": "Gets a code snippet from the active editor with n lines before and n lines after the cursor position. Useful for getting context around the cursor without loading the entire file.",
772
+ "parameters": [
773
+ {
774
+ "name": "lines",
775
+ "description": "number of lines to include before and after the cursor position (default: 5)",
776
+ "type": "number",
777
+ "required": false
778
+ }
779
+ ],
780
+ "output": [
781
+ {
782
+ "name": "snippet",
783
+ "description": "the code snippet with n lines before and after the cursor, or null if no editor is active"
784
+ },
785
+ {
786
+ "name": "filePath",
787
+ "description": "the workspace path of the file in the active editor, or null if no editor is active"
788
+ },
789
+ {
790
+ "name": "language",
791
+ "description": "the programming language of the active editor, or null if no editor is active"
792
+ },
793
+ {
794
+ "name": "cursorLine",
795
+ "description": "the line number where the cursor is positioned (1-based), or null if no editor is active"
796
+ }
797
+ ]
798
+ },
799
+ handler: {
800
+ execute: async (context: ExecutionContext) => {
801
+ const activeEditor = context.activeEditor as any
802
+ if (!isEditorContentProvider(activeEditor)) {
803
+ return createNullEditorResponse(true)
804
+ }
805
+
806
+ try {
807
+ const numLines = context.params?.lines ? parseInt(context.params.lines, 10) : 5
808
+ if (isNaN(numLines) || numLines < 0) {
809
+ return createNullEditorResponse(true)
810
+ }
811
+
812
+ const snippetResult = activeEditor.getSnippet(numLines)
813
+ if (!snippetResult) {
814
+ return createNullEditorResponse(true)
815
+ }
816
+
817
+ return {
818
+ snippet: snippetResult.snippet,
819
+ filePath: activeEditor.getFilePath(),
820
+ language: activeEditor.getLanguage(),
821
+ cursorLine: snippetResult.cursorLine
822
+ }
823
+ } catch (err: any) {
824
+ return createNullEditorResponse(true)
825
+ }
826
+ }
827
+ }
828
+ })
829
+