mulmoclaude 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (408) hide show
  1. package/README.md +44 -0
  2. package/bin/mulmoclaude.js +202 -0
  3. package/bin/prepare-dist.js +93 -0
  4. package/client/assets/chunk-vKJrgz-R-C_I3GbVV.js +1 -0
  5. package/client/assets/html2canvas-Cx501zZr-BF5dYYkY.js +5 -0
  6. package/client/assets/index-D8rhwXLq.js +4906 -0
  7. package/client/assets/index-KNLBjwuh.css +1 -0
  8. package/client/assets/index.es-D4YyL_Dg-BfRHLTZV.js +5 -0
  9. package/client/assets/material-icons-Dr0goTwe.woff +0 -0
  10. package/client/assets/material-icons-kAwBdRge.woff2 +0 -0
  11. package/client/assets/material-icons-outlined-BpWbwl2n.woff +0 -0
  12. package/client/assets/material-icons-outlined-DZhiGvEA.woff2 +0 -0
  13. package/client/assets/material-icons-round-BDlwx-sv.woff +0 -0
  14. package/client/assets/material-icons-round-DrirKXBx.woff2 +0 -0
  15. package/client/assets/material-icons-sharp-CH1KkVu7.woff +0 -0
  16. package/client/assets/material-icons-sharp-gidztirS.woff2 +0 -0
  17. package/client/assets/material-icons-two-tone-B7wz7mED.woff +0 -0
  18. package/client/assets/material-icons-two-tone-DuNIpaEj.woff2 +0 -0
  19. package/client/assets/mulmo_bw-ERmkSv0a.png +0 -0
  20. package/client/assets/purify.es-Fx1Nqyry-PeS5RUhs.js +2 -0
  21. package/client/assets/typeof-DBp4T-Ny-BC0P-2DM.js +1 -0
  22. package/client/index.html +28 -0
  23. package/package.json +66 -0
  24. package/server/agent/attachmentConverter.ts +270 -0
  25. package/server/agent/config.ts +414 -0
  26. package/server/agent/index.ts +260 -0
  27. package/server/agent/mcp-server.ts +412 -0
  28. package/server/agent/mcp-tools/index.ts +63 -0
  29. package/server/agent/mcp-tools/x.ts +188 -0
  30. package/server/agent/plugin-names.ts +75 -0
  31. package/server/agent/prompt.ts +349 -0
  32. package/server/agent/resumeFailover.ts +129 -0
  33. package/server/agent/sandboxMounts.ts +329 -0
  34. package/server/agent/stream.ts +194 -0
  35. package/server/api/auth/bearerAuth.ts +61 -0
  36. package/server/api/auth/token.ts +98 -0
  37. package/server/api/csrfGuard.ts +85 -0
  38. package/server/api/routes/agent.ts +478 -0
  39. package/server/api/routes/chart.ts +98 -0
  40. package/server/api/routes/chat-index.ts +46 -0
  41. package/server/api/routes/config.ts +258 -0
  42. package/server/api/routes/dispatchResponse.ts +79 -0
  43. package/server/api/routes/files.ts +812 -0
  44. package/server/api/routes/html.ts +101 -0
  45. package/server/api/routes/image.ts +169 -0
  46. package/server/api/routes/mulmo-script.ts +712 -0
  47. package/server/api/routes/mulmoScriptValidate.ts +101 -0
  48. package/server/api/routes/notifications.ts +69 -0
  49. package/server/api/routes/pdf.ts +163 -0
  50. package/server/api/routes/plugins.ts +276 -0
  51. package/server/api/routes/presentHtml.ts +48 -0
  52. package/server/api/routes/roles.ts +125 -0
  53. package/server/api/routes/scheduler.ts +153 -0
  54. package/server/api/routes/schedulerHandlers.ts +151 -0
  55. package/server/api/routes/schedulerTasks.ts +163 -0
  56. package/server/api/routes/sessions.ts +294 -0
  57. package/server/api/routes/sessionsCursor.ts +59 -0
  58. package/server/api/routes/skills.ts +195 -0
  59. package/server/api/routes/sources.ts +540 -0
  60. package/server/api/routes/todos.ts +263 -0
  61. package/server/api/routes/todosColumnsHandlers.ts +347 -0
  62. package/server/api/routes/todosHandlers.ts +274 -0
  63. package/server/api/routes/todosItemsHandlers.ts +386 -0
  64. package/server/api/routes/wiki/pageIndex.ts +53 -0
  65. package/server/api/routes/wiki.ts +363 -0
  66. package/server/api/sandboxStatus.ts +64 -0
  67. package/server/events/notifications.ts +160 -0
  68. package/server/events/pub-sub/index.ts +45 -0
  69. package/server/events/relay-client.ts +288 -0
  70. package/server/events/scheduler-adapter.ts +302 -0
  71. package/server/events/session-store/index.ts +492 -0
  72. package/server/events/task-manager/index.ts +181 -0
  73. package/server/index.ts +572 -0
  74. package/server/system/config.ts +243 -0
  75. package/server/system/credentials.ts +220 -0
  76. package/server/system/docker.ts +97 -0
  77. package/server/system/env.ts +109 -0
  78. package/server/system/logger/config.ts +112 -0
  79. package/server/system/logger/formatters.ts +40 -0
  80. package/server/system/logger/index.ts +53 -0
  81. package/server/system/logger/rotation.ts +37 -0
  82. package/server/system/logger/sinks.ts +101 -0
  83. package/server/system/logger/types.ts +29 -0
  84. package/server/utils/date.ts +57 -0
  85. package/server/utils/errors.ts +7 -0
  86. package/server/utils/fetch.ts +27 -0
  87. package/server/utils/files/atomic.ts +125 -0
  88. package/server/utils/files/html-io.ts +20 -0
  89. package/server/utils/files/image-store.ts +66 -0
  90. package/server/utils/files/index.ts +45 -0
  91. package/server/utils/files/journal-io.ts +213 -0
  92. package/server/utils/files/json.ts +69 -0
  93. package/server/utils/files/markdown-store.ts +33 -0
  94. package/server/utils/files/naming.ts +50 -0
  95. package/server/utils/files/reference-dirs-io.ts +45 -0
  96. package/server/utils/files/roles-io.ts +45 -0
  97. package/server/utils/files/safe.ts +106 -0
  98. package/server/utils/files/scheduler-io.ts +20 -0
  99. package/server/utils/files/scheduler-overrides-io.ts +64 -0
  100. package/server/utils/files/session-io.ts +136 -0
  101. package/server/utils/files/spreadsheet-store.ts +63 -0
  102. package/server/utils/files/todos-io.ts +29 -0
  103. package/server/utils/files/user-tasks-io.ts +25 -0
  104. package/server/utils/files/workspace-io.ts +221 -0
  105. package/server/utils/gemini.ts +59 -0
  106. package/server/utils/gitignore.ts +69 -0
  107. package/server/utils/http.ts +15 -0
  108. package/server/utils/httpError.ts +61 -0
  109. package/server/utils/id.ts +16 -0
  110. package/server/utils/json.ts +83 -0
  111. package/server/utils/logBackgroundError.ts +22 -0
  112. package/server/utils/markdown.ts +82 -0
  113. package/server/utils/request.ts +29 -0
  114. package/server/utils/slug.ts +50 -0
  115. package/server/utils/spawn.ts +62 -0
  116. package/server/utils/time.ts +34 -0
  117. package/server/utils/types.ts +47 -0
  118. package/server/workspace/chat-index/index.ts +153 -0
  119. package/server/workspace/chat-index/indexer.ts +209 -0
  120. package/server/workspace/chat-index/paths.ts +34 -0
  121. package/server/workspace/chat-index/summarizer.ts +247 -0
  122. package/server/workspace/chat-index/types.ts +38 -0
  123. package/server/workspace/custom-dirs.ts +220 -0
  124. package/server/workspace/helps/business.md +104 -0
  125. package/server/workspace/helps/github.md +23 -0
  126. package/server/workspace/helps/index.md +60 -0
  127. package/server/workspace/helps/mulmoscript.md +249 -0
  128. package/server/workspace/helps/sandbox.md +90 -0
  129. package/server/workspace/helps/spreadsheet.md +43 -0
  130. package/server/workspace/helps/telegram.md +135 -0
  131. package/server/workspace/helps/wiki.md +131 -0
  132. package/server/workspace/journal/archivist.ts +386 -0
  133. package/server/workspace/journal/dailyPass.ts +743 -0
  134. package/server/workspace/journal/diff.ts +71 -0
  135. package/server/workspace/journal/index.ts +185 -0
  136. package/server/workspace/journal/indexFile.ts +136 -0
  137. package/server/workspace/journal/linkRewrite.ts +4 -0
  138. package/server/workspace/journal/memoryExtractor.ts +130 -0
  139. package/server/workspace/journal/optimizationPass.ts +160 -0
  140. package/server/workspace/journal/paths.ts +76 -0
  141. package/server/workspace/journal/state.ts +125 -0
  142. package/server/workspace/paths.ts +158 -0
  143. package/server/workspace/reference-dirs.ts +252 -0
  144. package/server/workspace/roles.ts +37 -0
  145. package/server/workspace/skills/discovery.ts +125 -0
  146. package/server/workspace/skills/index.ts +10 -0
  147. package/server/workspace/skills/parser.ts +144 -0
  148. package/server/workspace/skills/paths.ts +41 -0
  149. package/server/workspace/skills/scheduler.ts +149 -0
  150. package/server/workspace/skills/types.ts +30 -0
  151. package/server/workspace/skills/user-tasks.ts +257 -0
  152. package/server/workspace/skills/writer.ts +189 -0
  153. package/server/workspace/sources/arxivDiscovery.ts +182 -0
  154. package/server/workspace/sources/classifier.ts +268 -0
  155. package/server/workspace/sources/fetchers/arxiv.ts +170 -0
  156. package/server/workspace/sources/fetchers/github.ts +106 -0
  157. package/server/workspace/sources/fetchers/githubIssues.ts +208 -0
  158. package/server/workspace/sources/fetchers/githubReleases.ts +186 -0
  159. package/server/workspace/sources/fetchers/index.ts +71 -0
  160. package/server/workspace/sources/fetchers/registerAll.ts +15 -0
  161. package/server/workspace/sources/fetchers/rss.ts +141 -0
  162. package/server/workspace/sources/fetchers/rssParser.ts +295 -0
  163. package/server/workspace/sources/httpFetcher.ts +230 -0
  164. package/server/workspace/sources/interests.ts +120 -0
  165. package/server/workspace/sources/paths.ts +110 -0
  166. package/server/workspace/sources/pipeline/dedup.ts +60 -0
  167. package/server/workspace/sources/pipeline/fetch.ts +136 -0
  168. package/server/workspace/sources/pipeline/index.ts +249 -0
  169. package/server/workspace/sources/pipeline/notify.ts +72 -0
  170. package/server/workspace/sources/pipeline/plan.ts +66 -0
  171. package/server/workspace/sources/pipeline/summarize.ts +189 -0
  172. package/server/workspace/sources/pipeline/write.ts +185 -0
  173. package/server/workspace/sources/rateLimiter.ts +148 -0
  174. package/server/workspace/sources/registry.ts +326 -0
  175. package/server/workspace/sources/robots.ts +271 -0
  176. package/server/workspace/sources/sourceState.ts +135 -0
  177. package/server/workspace/sources/taxonomy.ts +74 -0
  178. package/server/workspace/sources/types.ts +144 -0
  179. package/server/workspace/sources/urls.ts +112 -0
  180. package/server/workspace/tool-trace/classify.ts +114 -0
  181. package/server/workspace/tool-trace/index.ts +250 -0
  182. package/server/workspace/tool-trace/writeSearch.ts +98 -0
  183. package/server/workspace/wiki-backlinks/index.ts +107 -0
  184. package/server/workspace/wiki-backlinks/sessionBacklinks.ts +144 -0
  185. package/server/workspace/workspace.ts +66 -0
  186. package/src/App.vue +720 -0
  187. package/src/assets/mulmo_bw.png +0 -0
  188. package/src/components/CanvasViewToggle.vue +27 -0
  189. package/src/components/ChatAttachmentPreview.vue +45 -0
  190. package/src/components/ChatImagePreview.vue +17 -0
  191. package/src/components/ChatInput.vue +208 -0
  192. package/src/components/FileContentHeader.vue +49 -0
  193. package/src/components/FileContentRenderer.vue +162 -0
  194. package/src/components/FileTree.vue +115 -0
  195. package/src/components/FileTreePane.vue +85 -0
  196. package/src/components/FilesView.vue +206 -0
  197. package/src/components/LockStatusPopup.vue +111 -0
  198. package/src/components/NotificationBell.vue +131 -0
  199. package/src/components/NotificationToast.vue +72 -0
  200. package/src/components/PluginLauncher.vue +138 -0
  201. package/src/components/RightSidebar.vue +113 -0
  202. package/src/components/RoleSelector.vue +64 -0
  203. package/src/components/SessionHistoryPanel.vue +176 -0
  204. package/src/components/SessionTabBar.vue +81 -0
  205. package/src/components/SettingsMcpTab.vue +350 -0
  206. package/src/components/SettingsModal.vue +275 -0
  207. package/src/components/SettingsReferenceDirsTab.vue +173 -0
  208. package/src/components/SettingsWorkspaceDirsTab.vue +174 -0
  209. package/src/components/SidebarHeader.vue +69 -0
  210. package/src/components/StackView.vue +360 -0
  211. package/src/components/SuggestionsPanel.vue +65 -0
  212. package/src/components/TodoExplorer.vue +358 -0
  213. package/src/components/ToolResultsPanel.vue +77 -0
  214. package/src/components/todo/TodoAddDialog.vue +131 -0
  215. package/src/components/todo/TodoEditDialog.vue +47 -0
  216. package/src/components/todo/TodoEditPanel.vue +113 -0
  217. package/src/components/todo/TodoKanbanView.vue +249 -0
  218. package/src/components/todo/TodoListView.vue +79 -0
  219. package/src/components/todo/TodoTableView.vue +177 -0
  220. package/src/composables/useActiveSession.ts +40 -0
  221. package/src/composables/useAppApi.ts +45 -0
  222. package/src/composables/useCanvasViewMode.ts +121 -0
  223. package/src/composables/useChatScroll.ts +47 -0
  224. package/src/composables/useClickOutside.ts +26 -0
  225. package/src/composables/useClipboardCopy.ts +44 -0
  226. package/src/composables/useContentDisplay.ts +52 -0
  227. package/src/composables/useDebugBeat.ts +23 -0
  228. package/src/composables/useDynamicFavicon.ts +115 -0
  229. package/src/composables/useEventListeners.ts +42 -0
  230. package/src/composables/useExpandedDirs.ts +64 -0
  231. package/src/composables/useFaviconState.ts +30 -0
  232. package/src/composables/useFileSelection.ts +115 -0
  233. package/src/composables/useFileSortMode.ts +24 -0
  234. package/src/composables/useFileTree.ts +85 -0
  235. package/src/composables/useFreshPluginData.ts +89 -0
  236. package/src/composables/useHealth.ts +38 -0
  237. package/src/composables/useImeAwareEnter.ts +57 -0
  238. package/src/composables/useKeyNavigation.ts +60 -0
  239. package/src/composables/useMarkdownLinkHandler.ts +46 -0
  240. package/src/composables/useMarkdownMode.ts +17 -0
  241. package/src/composables/useMcpTools.ts +71 -0
  242. package/src/composables/useMergedSessions.ts +27 -0
  243. package/src/composables/useNotifications.ts +90 -0
  244. package/src/composables/usePdfDownload.ts +60 -0
  245. package/src/composables/usePendingCalls.ts +77 -0
  246. package/src/composables/usePubSub.ts +85 -0
  247. package/src/composables/useRightSidebar.ts +23 -0
  248. package/src/composables/useRoles.ts +34 -0
  249. package/src/composables/useSandboxStatus.ts +67 -0
  250. package/src/composables/useSelectedResult.ts +49 -0
  251. package/src/composables/useSessionDerived.ts +51 -0
  252. package/src/composables/useSessionHistory.ts +81 -0
  253. package/src/composables/useSessionSync.ts +57 -0
  254. package/src/composables/useViewLayout.ts +55 -0
  255. package/src/config/apiRoutes.ts +173 -0
  256. package/src/config/pubsubChannels.ts +45 -0
  257. package/src/config/roles.ts +335 -0
  258. package/src/config/schedulerActions.ts +25 -0
  259. package/src/config/toolNames.ts +71 -0
  260. package/src/config/workspacePaths.ts +24 -0
  261. package/src/index.css +107 -0
  262. package/src/main.ts +25 -0
  263. package/src/plugins/canvas/Preview.vue +13 -0
  264. package/src/plugins/canvas/View.vue +333 -0
  265. package/src/plugins/canvas/definition.ts +38 -0
  266. package/src/plugins/canvas/index.ts +36 -0
  267. package/src/plugins/chart/Preview.vue +49 -0
  268. package/src/plugins/chart/View.vue +143 -0
  269. package/src/plugins/chart/definition.ts +58 -0
  270. package/src/plugins/chart/index.ts +52 -0
  271. package/src/plugins/editImage/Preview.vue +13 -0
  272. package/src/plugins/editImage/View.vue +13 -0
  273. package/src/plugins/editImage/definition.ts +27 -0
  274. package/src/plugins/editImage/index.ts +36 -0
  275. package/src/plugins/generateImage/Preview.vue +13 -0
  276. package/src/plugins/generateImage/View.vue +33 -0
  277. package/src/plugins/generateImage/definition.ts +32 -0
  278. package/src/plugins/generateImage/index.ts +56 -0
  279. package/src/plugins/manageRoles/Preview.vue +49 -0
  280. package/src/plugins/manageRoles/View.vue +525 -0
  281. package/src/plugins/manageRoles/definition.ts +43 -0
  282. package/src/plugins/manageRoles/index.ts +47 -0
  283. package/src/plugins/manageSkills/Preview.vue +21 -0
  284. package/src/plugins/manageSkills/View.vue +321 -0
  285. package/src/plugins/manageSkills/definition.ts +49 -0
  286. package/src/plugins/manageSkills/index.ts +49 -0
  287. package/src/plugins/manageSource/Preview.vue +33 -0
  288. package/src/plugins/manageSource/View.vue +697 -0
  289. package/src/plugins/manageSource/definition.ts +63 -0
  290. package/src/plugins/manageSource/index.ts +66 -0
  291. package/src/plugins/markdown/Preview.vue +77 -0
  292. package/src/plugins/markdown/View.vue +476 -0
  293. package/src/plugins/markdown/definition.ts +50 -0
  294. package/src/plugins/markdown/index.ts +36 -0
  295. package/src/plugins/presentHtml/Preview.vue +25 -0
  296. package/src/plugins/presentHtml/View.vue +52 -0
  297. package/src/plugins/presentHtml/definition.ts +27 -0
  298. package/src/plugins/presentHtml/helpers.ts +72 -0
  299. package/src/plugins/presentHtml/index.ts +41 -0
  300. package/src/plugins/presentMulmoScript/Preview.vue +23 -0
  301. package/src/plugins/presentMulmoScript/View.vue +1166 -0
  302. package/src/plugins/presentMulmoScript/definition.ts +95 -0
  303. package/src/plugins/presentMulmoScript/helpers.ts +162 -0
  304. package/src/plugins/presentMulmoScript/index.ts +40 -0
  305. package/src/plugins/scheduler/Preview.vue +67 -0
  306. package/src/plugins/scheduler/TasksTab.vue +205 -0
  307. package/src/plugins/scheduler/View.vue +565 -0
  308. package/src/plugins/scheduler/definition.ts +57 -0
  309. package/src/plugins/scheduler/index.ts +45 -0
  310. package/src/plugins/scheduler/viewModes.ts +26 -0
  311. package/src/plugins/spreadsheet/Preview.vue +29 -0
  312. package/src/plugins/spreadsheet/View.vue +997 -0
  313. package/src/plugins/spreadsheet/cellHighlights.ts +79 -0
  314. package/src/plugins/spreadsheet/definition.ts +121 -0
  315. package/src/plugins/spreadsheet/engine/calculator.ts +459 -0
  316. package/src/plugins/spreadsheet/engine/cellBuilder.ts +81 -0
  317. package/src/plugins/spreadsheet/engine/date-parser.ts +220 -0
  318. package/src/plugins/spreadsheet/engine/date-utils.ts +56 -0
  319. package/src/plugins/spreadsheet/engine/engine.ts +176 -0
  320. package/src/plugins/spreadsheet/engine/evaluator.ts +390 -0
  321. package/src/plugins/spreadsheet/engine/formatter.ts +172 -0
  322. package/src/plugins/spreadsheet/engine/formulaRefs.ts +101 -0
  323. package/src/plugins/spreadsheet/engine/functions/date.ts +299 -0
  324. package/src/plugins/spreadsheet/engine/functions/financial.ts +387 -0
  325. package/src/plugins/spreadsheet/engine/functions/index.ts +16 -0
  326. package/src/plugins/spreadsheet/engine/functions/logical.ts +262 -0
  327. package/src/plugins/spreadsheet/engine/functions/lookup.ts +400 -0
  328. package/src/plugins/spreadsheet/engine/functions/mathematical.ts +297 -0
  329. package/src/plugins/spreadsheet/engine/functions/statistical.ts +338 -0
  330. package/src/plugins/spreadsheet/engine/functions/text.ts +389 -0
  331. package/src/plugins/spreadsheet/engine/index.ts +27 -0
  332. package/src/plugins/spreadsheet/engine/jsonCellLocator.ts +111 -0
  333. package/src/plugins/spreadsheet/engine/parser.ts +143 -0
  334. package/src/plugins/spreadsheet/engine/registry.ts +150 -0
  335. package/src/plugins/spreadsheet/engine/responseDecoder.ts +67 -0
  336. package/src/plugins/spreadsheet/engine/types.ts +64 -0
  337. package/src/plugins/spreadsheet/index.ts +36 -0
  338. package/src/plugins/textResponse/Preview.vue +94 -0
  339. package/src/plugins/textResponse/View.vue +503 -0
  340. package/src/plugins/textResponse/definition.ts +34 -0
  341. package/src/plugins/textResponse/index.ts +27 -0
  342. package/src/plugins/textResponse/plugin.ts +29 -0
  343. package/src/plugins/textResponse/samples.ts +97 -0
  344. package/src/plugins/textResponse/types.ts +11 -0
  345. package/src/plugins/todo/Preview.vue +63 -0
  346. package/src/plugins/todo/View.vue +364 -0
  347. package/src/plugins/todo/composables/useTodos.ts +177 -0
  348. package/src/plugins/todo/definition.ts +45 -0
  349. package/src/plugins/todo/index.ts +61 -0
  350. package/src/plugins/todo/labels.ts +163 -0
  351. package/src/plugins/todo/priority.ts +98 -0
  352. package/src/plugins/todo/viewModes.ts +19 -0
  353. package/src/plugins/ui-image/ImagePreview.vue +23 -0
  354. package/src/plugins/ui-image/ImageView.vue +34 -0
  355. package/src/plugins/ui-image/index.ts +3 -0
  356. package/src/plugins/ui-image/types.ts +4 -0
  357. package/src/plugins/wiki/Preview.vue +65 -0
  358. package/src/plugins/wiki/View.vue +342 -0
  359. package/src/plugins/wiki/definition.ts +25 -0
  360. package/src/plugins/wiki/helpers.ts +59 -0
  361. package/src/plugins/wiki/index.ts +52 -0
  362. package/src/router/guards.ts +61 -0
  363. package/src/router/index.ts +50 -0
  364. package/src/tools/index.ts +52 -0
  365. package/src/tools/types.ts +27 -0
  366. package/src/types/events.ts +16 -0
  367. package/src/types/fileTree.ts +13 -0
  368. package/src/types/notification.ts +67 -0
  369. package/src/types/session.ts +116 -0
  370. package/src/types/sse.ts +90 -0
  371. package/src/types/toolCallHistory.ts +13 -0
  372. package/src/utils/agent/eventDispatch.ts +74 -0
  373. package/src/utils/agent/request.ts +55 -0
  374. package/src/utils/agent/toolCalls.ts +62 -0
  375. package/src/utils/api.ts +218 -0
  376. package/src/utils/canvas/viewMode.ts +46 -0
  377. package/src/utils/dom/authTokenMeta.ts +20 -0
  378. package/src/utils/dom/clickOutside.ts +11 -0
  379. package/src/utils/dom/externalLink.ts +57 -0
  380. package/src/utils/dom/scrollable.ts +24 -0
  381. package/src/utils/errors.ts +11 -0
  382. package/src/utils/files/expandedDirs.ts +25 -0
  383. package/src/utils/files/filename.ts +12 -0
  384. package/src/utils/files/sortChildren.ts +20 -0
  385. package/src/utils/filesPreview/schedulerPreview.ts +38 -0
  386. package/src/utils/filesPreview/todoPreview.ts +40 -0
  387. package/src/utils/format/date.ts +85 -0
  388. package/src/utils/format/frontmatter.ts +80 -0
  389. package/src/utils/format/jsonSyntax.ts +109 -0
  390. package/src/utils/html/previewCsp.ts +65 -0
  391. package/src/utils/image/resolve.ts +8 -0
  392. package/src/utils/image/rewriteMarkdownImageRefs.ts +182 -0
  393. package/src/utils/markdown/extractFirstH1.ts +39 -0
  394. package/src/utils/notification/dispatch.ts +22 -0
  395. package/src/utils/path/relativeLink.ts +130 -0
  396. package/src/utils/role/icon.ts +20 -0
  397. package/src/utils/role/merge.ts +10 -0
  398. package/src/utils/role/plugins.ts +12 -0
  399. package/src/utils/session/mergeSessions.ts +103 -0
  400. package/src/utils/session/seedRoleDefault.ts +35 -0
  401. package/src/utils/session/sessionEntries.ts +121 -0
  402. package/src/utils/session/sessionFactory.ts +22 -0
  403. package/src/utils/session/sessionHelpers.ts +99 -0
  404. package/src/utils/tools/dedup.ts +17 -0
  405. package/src/utils/tools/mcp.ts +33 -0
  406. package/src/utils/tools/pendingCalls.ts +16 -0
  407. package/src/utils/tools/result.ts +40 -0
  408. package/src/utils/types.ts +44 -0
@@ -0,0 +1,262 @@
1
+ /**
2
+ * Logical Functions
3
+ */
4
+
5
+ import { functionRegistry, type FunctionHandler } from "../registry";
6
+
7
+ const ifHandler: FunctionHandler = (args, context) => {
8
+ if (args.length !== 3) throw new Error("IF requires 3 arguments");
9
+
10
+ const condition = args[0];
11
+ const trueValue = args[1];
12
+ const falseValue = args[2];
13
+
14
+ // Evaluate condition - use evaluateFormula to handle nested functions like MONTH()
15
+ const conditionValue = context.evaluateFormula(condition);
16
+
17
+ // Convert to boolean
18
+ let conditionResult = false;
19
+ if (typeof conditionValue === "boolean") {
20
+ conditionResult = conditionValue;
21
+ } else if (typeof conditionValue === "number") {
22
+ conditionResult = conditionValue !== 0;
23
+ } else if (typeof conditionValue === "string") {
24
+ conditionResult = conditionValue.toLowerCase() === "true" || conditionValue !== "";
25
+ } else {
26
+ conditionResult = !!conditionValue;
27
+ }
28
+
29
+ // Return the appropriate value based on condition
30
+ const resultValue = conditionResult ? trueValue : falseValue;
31
+
32
+ // If result is a quoted string, return the string without quotes
33
+ if (/^["'](.*)["']$/.test(resultValue)) {
34
+ return resultValue.slice(1, -1);
35
+ }
36
+
37
+ // If result is a nested formula, evaluate it recursively
38
+ if (/^(SUM|AVERAGE|MAX|MIN|COUNT|IF|AND|OR|NOT)\(/i.test(resultValue)) {
39
+ return context.evaluateFormula(resultValue);
40
+ }
41
+
42
+ // Otherwise evaluate as expression
43
+ let expr = resultValue;
44
+
45
+ const refs = resultValue.match(/(?:'[^']+'|[^'!\s]+)![A-Z]+\d+|\$?[A-Z]+\$?\d+/g);
46
+ if (refs) {
47
+ for (const ref of refs) {
48
+ const value = context.getCellValue(ref);
49
+ const escapedRef = ref.replace(/\$/g, "\\$").replace(/'/g, "\\'");
50
+ expr = expr.replace(new RegExp(escapedRef.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g"), String(value));
51
+ }
52
+ }
53
+
54
+ const numResult = parseFloat(expr);
55
+ return isNaN(numResult) ? expr : numResult;
56
+ };
57
+
58
+ const andHandler: FunctionHandler = (args, context) => {
59
+ if (args.length === 0) throw new Error("AND requires at least 1 argument");
60
+
61
+ for (const arg of args) {
62
+ const value = context.evaluateFormula(arg.trim());
63
+ // Check if value is falsy (0, false, empty string, etc.)
64
+ // Note: !value already covers false, so we check for 0 and "0" explicitly
65
+ if (!value || value === 0 || value === "0") {
66
+ return false;
67
+ }
68
+ }
69
+ return true;
70
+ };
71
+
72
+ const orHandler: FunctionHandler = (args, context) => {
73
+ if (args.length === 0) throw new Error("OR requires at least 1 argument");
74
+
75
+ for (const arg of args) {
76
+ const value = context.evaluateFormula(arg.trim());
77
+ // Check if value is truthy (non-zero, non-empty)
78
+ if (value && value !== 0 && value !== "0") {
79
+ return true;
80
+ }
81
+ }
82
+ return false;
83
+ };
84
+
85
+ const notHandler: FunctionHandler = (args, context) => {
86
+ if (args.length !== 1) throw new Error("NOT requires 1 argument");
87
+
88
+ const value = context.evaluateFormula(args[0]);
89
+ // Note: !value already covers false
90
+ return !value || value === 0 || value === "0";
91
+ };
92
+
93
+ const iferrorHandler: FunctionHandler = (args, context) => {
94
+ if (args.length !== 2) throw new Error("IFERROR requires 2 arguments");
95
+
96
+ try {
97
+ const result = context.evaluateFormula(args[0]);
98
+ // Check if result is an error (NaN, Infinity, etc.)
99
+ if (result === null || result === undefined || (typeof result === "number" && (isNaN(result) || !isFinite(result)))) {
100
+ return context.evaluateFormula(args[1]);
101
+ }
102
+ return result;
103
+ } catch {
104
+ // If evaluation throws an error, return the fallback value
105
+ return context.evaluateFormula(args[1]);
106
+ }
107
+ };
108
+
109
+ const ifnaHandler: FunctionHandler = (args, context) => {
110
+ if (args.length !== 2) throw new Error("IFNA requires 2 arguments");
111
+
112
+ const result = context.evaluateFormula(args[0]);
113
+ // Check if result is N/A (could be represented as specific error value)
114
+ if (result === null || result === undefined || result === "#N/A") {
115
+ return context.evaluateFormula(args[1]);
116
+ }
117
+ return result;
118
+ };
119
+
120
+ const ifsHandler: FunctionHandler = (args, context) => {
121
+ if (args.length < 2 || args.length % 2 !== 0) {
122
+ throw new Error("IFS requires an even number of arguments (condition-value pairs)");
123
+ }
124
+
125
+ // Iterate through condition-value pairs
126
+ for (let i = 0; i < args.length; i += 2) {
127
+ const condition = args[i];
128
+ const value = args[i + 1];
129
+
130
+ // Evaluate condition
131
+ let condExpr = condition;
132
+
133
+ const cellRefs = condition.match(/(?:'[^']+'|[^'!\s]+)![A-Z]+\d+|\$?[A-Z]+\$?\d+/g);
134
+ if (cellRefs) {
135
+ for (const ref of cellRefs) {
136
+ const cellValue = context.getCellValue(ref);
137
+ const escapedRef = ref.replace(/\$/g, "\\$").replace(/'/g, "\\'");
138
+ condExpr = condExpr.replace(new RegExp(escapedRef.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g"), String(cellValue));
139
+ }
140
+ }
141
+
142
+ // Evaluate the condition
143
+ let conditionResult = false;
144
+
145
+ if (/>=|<=|>|<|==|!=/.test(condExpr)) {
146
+ conditionResult = eval(condExpr);
147
+ } else {
148
+ conditionResult = !!eval(condExpr);
149
+ }
150
+
151
+ if (conditionResult) {
152
+ // If result is a quoted string, return without quotes
153
+
154
+ if (/^["'](.*)["']$/.test(value)) {
155
+ return value.slice(1, -1);
156
+ }
157
+ // Otherwise evaluate as formula or expression
158
+ return context.evaluateFormula(value);
159
+ }
160
+ }
161
+
162
+ // If no conditions match, return error
163
+ return "#N/A";
164
+ };
165
+
166
+ const trueHandler: FunctionHandler = (args) => {
167
+ if (args.length !== 0) throw new Error("TRUE requires 0 arguments");
168
+ return true;
169
+ };
170
+
171
+ const falseHandler: FunctionHandler = (args) => {
172
+ if (args.length !== 0) throw new Error("FALSE requires 0 arguments");
173
+ return false;
174
+ };
175
+
176
+ // Register all logical functions
177
+ functionRegistry.register({
178
+ name: "IF",
179
+ handler: ifHandler,
180
+ minArgs: 3,
181
+ maxArgs: 3,
182
+ description: "Returns one value if a condition is true and another if false",
183
+ examples: ['IF(A1>10, "High", "Low")', "IF(B2>=5, SUM(C1:C10), 0)"],
184
+ category: "Logical",
185
+ });
186
+
187
+ functionRegistry.register({
188
+ name: "AND",
189
+ handler: andHandler,
190
+ minArgs: 1,
191
+ description: "Returns TRUE if all arguments are true",
192
+ examples: ["AND(A1>5, B1<10)", "AND(A1>0, B1>0, C1>0)"],
193
+ category: "Logical",
194
+ });
195
+
196
+ functionRegistry.register({
197
+ name: "OR",
198
+ handler: orHandler,
199
+ minArgs: 1,
200
+ description: "Returns TRUE if any argument is true",
201
+ examples: ["OR(A1>5, B1<10)", "OR(A1>0, B1>0)"],
202
+ category: "Logical",
203
+ });
204
+
205
+ functionRegistry.register({
206
+ name: "NOT",
207
+ handler: notHandler,
208
+ minArgs: 1,
209
+ maxArgs: 1,
210
+ description: "Reverses the logical value of its argument",
211
+ examples: ["NOT(A1>5)", "NOT(B1)"],
212
+ category: "Logical",
213
+ });
214
+
215
+ functionRegistry.register({
216
+ name: "IFERROR",
217
+ handler: iferrorHandler,
218
+ minArgs: 2,
219
+ maxArgs: 2,
220
+ description: "Returns a value if expression is an error, otherwise returns the expression",
221
+ examples: ["IFERROR(A1/B1, 0)", 'IFERROR(VLOOKUP(A1, B1:C10, 2), "Not found")'],
222
+ category: "Logical",
223
+ });
224
+
225
+ functionRegistry.register({
226
+ name: "IFNA",
227
+ handler: ifnaHandler,
228
+ minArgs: 2,
229
+ maxArgs: 2,
230
+ description: "Returns a value if expression is #N/A, otherwise returns the expression",
231
+ examples: ['IFNA(A1, "N/A")', "IFNA(MATCH(A1, B1:B10), 0)"],
232
+ category: "Logical",
233
+ });
234
+
235
+ functionRegistry.register({
236
+ name: "IFS",
237
+ handler: ifsHandler,
238
+ minArgs: 2,
239
+ description: "Checks multiple conditions and returns the first true result",
240
+ examples: ['IFS(A1>90, "A", A1>80, "B", A1>70, "C")', 'IFS(B1="Yes", 1, B1="No", 0)'],
241
+ category: "Logical",
242
+ });
243
+
244
+ functionRegistry.register({
245
+ name: "TRUE",
246
+ handler: trueHandler,
247
+ minArgs: 0,
248
+ maxArgs: 0,
249
+ description: "Returns the logical value TRUE",
250
+ examples: ["TRUE()", "IF(A1>0, TRUE(), FALSE())"],
251
+ category: "Logical",
252
+ });
253
+
254
+ functionRegistry.register({
255
+ name: "FALSE",
256
+ handler: falseHandler,
257
+ minArgs: 0,
258
+ maxArgs: 0,
259
+ description: "Returns the logical value FALSE",
260
+ examples: ["FALSE()", "IF(A1>0, TRUE(), FALSE())"],
261
+ category: "Logical",
262
+ });
@@ -0,0 +1,400 @@
1
+ /**
2
+ * Lookup and Reference Functions
3
+ */
4
+
5
+ import { functionRegistry, toNumber, parseCriteria, type FunctionHandler } from "../registry";
6
+ import type { CellValue } from "../types";
7
+
8
+ // Helper to convert Excel column letters to 0-based index (A=0, Z=25, AA=26, etc.)
9
+ const colToIndex = (col: string): number => {
10
+ let result = 0;
11
+ for (let i = 0; i < col.length; i++) {
12
+ result = result * 26 + (col.charCodeAt(i) - 64);
13
+ }
14
+ return result - 1;
15
+ };
16
+
17
+ // Helper to convert 0-based index to Excel column letters (0=A, 25=Z, 26=AA, etc.)
18
+ const indexToCol = (index: number): string => {
19
+ let col = "";
20
+ let num = index + 1;
21
+ while (num > 0) {
22
+ const remainder = (num - 1) % 26;
23
+ col = String.fromCharCode(65 + remainder) + col;
24
+ num = Math.floor((num - 1) / 26);
25
+ }
26
+ return col;
27
+ };
28
+
29
+ // Helper to find match index
30
+ const findMatchIndex = (
31
+ lookupValue: CellValue,
32
+ lookupArray: CellValue[],
33
+ matchType: number = 1, // 1 = less than (sorted asc), 0 = exact, -1 = greater than (sorted desc)
34
+ searchMode: number = 1, // 1 = first to last, -1 = last to first (for XLOOKUP)
35
+ ): number => {
36
+ const compare = (a: CellValue, b: CellValue) => {
37
+ if (typeof a === "number" && typeof b === "number") return a - b;
38
+ return String(a).localeCompare(String(b));
39
+ };
40
+
41
+ // Exact match
42
+ if (matchType === 0) {
43
+ // Handle wildcards for strings if it's an exact match request
44
+ if (typeof lookupValue === "string" && (lookupValue.includes("*") || lookupValue.includes("?"))) {
45
+ const criteriaFn = parseCriteria(lookupValue);
46
+
47
+ if (searchMode === 1) {
48
+ return lookupArray.findIndex((item) => criteriaFn(item));
49
+ } else {
50
+ for (let i = lookupArray.length - 1; i >= 0; i--) {
51
+ if (criteriaFn(lookupArray[i])) return i;
52
+ }
53
+ return -1;
54
+ }
55
+ }
56
+
57
+ if (searchMode === 1) {
58
+ return lookupArray.findIndex((item) => item == lookupValue); // Loose equality for "10" == 10
59
+ } else {
60
+ for (let i = lookupArray.length - 1; i >= 0; i--) {
61
+ if (lookupArray[i] == lookupValue) return i;
62
+ }
63
+ return -1;
64
+ }
65
+ }
66
+
67
+ // Approximate match (requires sorted array)
68
+ // We'll assume the user knows what they are doing regarding sorting, as per Excel behavior
69
+
70
+ if (matchType === 1) {
71
+ // Less than or equal to
72
+ // Array must be sorted ascending
73
+ let bestIdx = -1;
74
+ for (let i = 0; i < lookupArray.length; i++) {
75
+ const item = lookupArray[i];
76
+ if (compare(item, lookupValue) <= 0) {
77
+ bestIdx = i;
78
+ } else {
79
+ // Since it's sorted ascending, once we exceed, we can stop
80
+ break;
81
+ }
82
+ }
83
+ return bestIdx;
84
+ }
85
+
86
+ if (matchType === -1) {
87
+ // Greater than or equal to
88
+ // Array must be sorted descending
89
+ let bestIdx = -1;
90
+ for (let i = 0; i < lookupArray.length; i++) {
91
+ const item = lookupArray[i];
92
+ if (compare(item, lookupValue) >= 0) {
93
+ bestIdx = i;
94
+ } else {
95
+ break;
96
+ }
97
+ }
98
+ return bestIdx;
99
+ }
100
+
101
+ return -1;
102
+ };
103
+
104
+ const vlookupHandler: FunctionHandler = (args, context) => {
105
+ if (args.length < 3 || args.length > 4) {
106
+ throw new Error("VLOOKUP requires 3 or 4 arguments");
107
+ }
108
+
109
+ const lookupValue = context.evaluateFormula(args[0]);
110
+ const tableArrayRange = args[1];
111
+ const colIndexNum = toNumber(context.evaluateFormula(args[2]));
112
+ const rangeLookup = args.length === 4 ? context.evaluateFormula(args[3]) : true;
113
+
114
+ // Convert rangeLookup to boolean/number logic
115
+ // TRUE/1/omitted = approximate match (default)
116
+ // FALSE/0 = exact match
117
+ const isApprox = rangeLookup === true || rangeLookup === 1 || rangeLookup === "1";
118
+ const matchType = isApprox ? 1 : 0;
119
+
120
+ // Get the full table data
121
+ // We need to parse the range string to get dimensions
122
+ const match = tableArrayRange.match(/^([A-Z]+)(\d+):([A-Z]+)(\d+)$/);
123
+ if (!match) throw new Error("Invalid table array range");
124
+
125
+ // We need to get the first column for looking up
126
+ // And the specific column for the result
127
+ // This is a bit tricky with the current getRangeValues which flattens everything
128
+ // We need to manually reconstruct the table structure or request specific cells
129
+
130
+ // Let's parse the range to get start/end col/row
131
+ // Note: This relies on the context.getCellValue implementation details or we need to implement
132
+ // a smarter way to get 2D data.
133
+ // For now, we will iterate row by row.
134
+
135
+ // Parse range manually to get boundaries
136
+ // We can't easily use context.getRangeValues because it flattens 2D arrays to 1D
137
+ // So we will iterate through the rows of the first column
138
+
139
+ // Extract sheet name if present
140
+ let sheetName = "";
141
+ let rangePart = tableArrayRange;
142
+ if (tableArrayRange.includes("!")) {
143
+ const parts = tableArrayRange.split("!");
144
+ sheetName = parts[0] + "!";
145
+ rangePart = parts[1];
146
+ }
147
+
148
+ const rangeMatch = rangePart.match(/^([A-Z]+)(\d+):([A-Z]+)(\d+)$/);
149
+ if (!rangeMatch) throw new Error("Invalid range format");
150
+
151
+ const startColStr = rangeMatch[1];
152
+ const startRow = parseInt(rangeMatch[2]);
153
+ const endRow = parseInt(rangeMatch[4]);
154
+
155
+ const startColIdx = colToIndex(startColStr);
156
+ const resultColIdx = startColIdx + colIndexNum - 1;
157
+ const resultColStr = indexToCol(resultColIdx);
158
+
159
+ // Build lookup array (first column)
160
+ const lookupArray: CellValue[] = [];
161
+ for (let r = startRow; r <= endRow; r++) {
162
+ const cellRef = `${sheetName}${startColStr}${r}`;
163
+ lookupArray.push(context.getCellValue(cellRef));
164
+ }
165
+
166
+ const matchIdx = findMatchIndex(lookupValue, lookupArray, matchType);
167
+
168
+ if (matchIdx === -1) return "#N/A";
169
+
170
+ // Get result
171
+ const resultRow = startRow + matchIdx;
172
+ const resultRef = `${sheetName}${resultColStr}${resultRow}`;
173
+
174
+ return context.getCellValue(resultRef);
175
+ };
176
+
177
+ const hlookupHandler: FunctionHandler = (args, context) => {
178
+ if (args.length < 3 || args.length > 4) {
179
+ throw new Error("HLOOKUP requires 3 or 4 arguments");
180
+ }
181
+
182
+ const lookupValue = context.evaluateFormula(args[0]);
183
+ const tableArrayRange = args[1];
184
+ const rowIndexNum = toNumber(context.evaluateFormula(args[2]));
185
+ const rangeLookup = args.length === 4 ? context.evaluateFormula(args[3]) : true;
186
+
187
+ const isApprox = rangeLookup === true || rangeLookup === 1 || rangeLookup === "1";
188
+ const matchType = isApprox ? 1 : 0;
189
+
190
+ // Extract sheet name if present
191
+ let sheetName = "";
192
+ let rangePart = tableArrayRange;
193
+ if (tableArrayRange.includes("!")) {
194
+ const parts = tableArrayRange.split("!");
195
+ sheetName = parts[0] + "!";
196
+ rangePart = parts[1];
197
+ }
198
+
199
+ const rangeMatch = rangePart.match(/^([A-Z]+)(\d+):([A-Z]+)(\d+)$/);
200
+ if (!rangeMatch) throw new Error("Invalid range format");
201
+
202
+ const startColStr = rangeMatch[1];
203
+ const endColStr = rangeMatch[3];
204
+ const startRow = parseInt(rangeMatch[2]);
205
+
206
+ const startColIdx = colToIndex(startColStr);
207
+ const endColIdx = colToIndex(endColStr);
208
+
209
+ // Build lookup array (first row)
210
+ const lookupArray: CellValue[] = [];
211
+ for (let c = startColIdx; c <= endColIdx; c++) {
212
+ const colStr = indexToCol(c);
213
+ const cellRef = `${sheetName}${colStr}${startRow}`;
214
+ lookupArray.push(context.getCellValue(cellRef));
215
+ }
216
+
217
+ const matchIdx = findMatchIndex(lookupValue, lookupArray, matchType);
218
+
219
+ if (matchIdx === -1) return "#N/A";
220
+
221
+ // Get result
222
+ const resultColIdx = startColIdx + matchIdx;
223
+ const resultColStr = indexToCol(resultColIdx);
224
+ const resultRow = startRow + rowIndexNum - 1;
225
+ const resultRef = `${sheetName}${resultColStr}${resultRow}`;
226
+
227
+ return context.getCellValue(resultRef);
228
+ };
229
+
230
+ const matchHandler: FunctionHandler = (args, context) => {
231
+ if (args.length < 2 || args.length > 3) {
232
+ throw new Error("MATCH requires 2 or 3 arguments");
233
+ }
234
+
235
+ const lookupValue = context.evaluateFormula(args[0]);
236
+ const lookupArrayRange = args[1];
237
+ const matchType = args.length === 3 ? toNumber(context.evaluateFormula(args[2])) : 1;
238
+
239
+ const lookupArray = context.getRangeValues(lookupArrayRange);
240
+
241
+ const index = findMatchIndex(lookupValue, lookupArray, matchType);
242
+
243
+ return index === -1 ? "#N/A" : index + 1; // 1-based index
244
+ };
245
+
246
+ const indexHandler: FunctionHandler = (args, context) => {
247
+ if (args.length < 2 || args.length > 4) {
248
+ throw new Error("INDEX requires 2 to 4 arguments");
249
+ }
250
+
251
+ const arrayRange = args[0];
252
+ const rowNum = toNumber(context.evaluateFormula(args[1]));
253
+ const colNum = args.length >= 3 ? toNumber(context.evaluateFormula(args[2])) : 1; // Default to 1 if omitted (for 1D arrays)
254
+
255
+ // Parse range to find the specific cell
256
+ let sheetName = "";
257
+ let rangePart = arrayRange;
258
+ if (arrayRange.includes("!")) {
259
+ const parts = arrayRange.split("!");
260
+ sheetName = parts[0] + "!";
261
+ rangePart = parts[1];
262
+ }
263
+
264
+ const rangeMatch = rangePart.match(/^([A-Z]+)(\d+):([A-Z]+)(\d+)$/);
265
+ if (!rangeMatch) throw new Error("Invalid range format");
266
+
267
+ const startColStr = rangeMatch[1];
268
+ const startRow = parseInt(rangeMatch[2]);
269
+
270
+ const startColIdx = colToIndex(startColStr);
271
+
272
+ // Calculate target cell
273
+ // rowNum and colNum are 1-based relative to the range
274
+ const targetRow = startRow + rowNum - 1;
275
+ const targetColIdx = startColIdx + colNum - 1;
276
+ const targetColStr = indexToCol(targetColIdx);
277
+
278
+ const ref = `${sheetName}${targetColStr}${targetRow}`;
279
+ return context.getCellValue(ref);
280
+ };
281
+
282
+ const xlookupHandler: FunctionHandler = (args, context) => {
283
+ if (args.length < 3 || args.length > 6) {
284
+ throw new Error("XLOOKUP requires 3 to 6 arguments");
285
+ }
286
+
287
+ const lookupValue = context.evaluateFormula(args[0]);
288
+ const lookupArrayRange = args[1];
289
+ const returnArrayRange = args[2];
290
+ const ifNotFound = args.length >= 4 ? context.evaluateFormula(args[3]) : "#N/A";
291
+ const matchMode = args.length >= 5 ? toNumber(context.evaluateFormula(args[4])) : 0;
292
+ const searchMode = args.length >= 6 ? toNumber(context.evaluateFormula(args[5])) : 1;
293
+
294
+ const lookupArray = context.getRangeValues(lookupArrayRange);
295
+ const returnArray = context.getRangeValues(returnArrayRange);
296
+
297
+ // XLOOKUP match modes:
298
+ // 0 = Exact match (default)
299
+ // -1 = Exact match or next smaller
300
+ // 1 = Exact match or next larger
301
+ // 2 = Wildcard match
302
+
303
+ // Map XLOOKUP modes to our internal findMatchIndex modes
304
+ // Our internal: 0=exact, 1=less than (sorted), -1=greater than (sorted)
305
+ // XLOOKUP is more complex because it doesn't require sorted arrays for next smaller/larger
306
+ // For now, we'll implement exact (0) and wildcard (2 -> handled by exact with wildcard logic in findMatchIndex)
307
+ // For -1 and 1, we'll do a linear search for best match if not sorted
308
+
309
+ let matchIdx = -1;
310
+
311
+ if (matchMode === 0 || matchMode === 2) {
312
+ matchIdx = findMatchIndex(lookupValue, lookupArray, 0, searchMode);
313
+ } else {
314
+ // Implement exact or next smaller/larger for unsorted arrays
315
+ // This is O(N)
316
+ let bestDiff = Infinity;
317
+
318
+ for (let i = 0; i < lookupArray.length; i++) {
319
+ const idx = searchMode === 1 ? i : lookupArray.length - 1 - i;
320
+ const item = lookupArray[idx];
321
+
322
+ if (item == lookupValue) {
323
+ matchIdx = idx;
324
+ break;
325
+ }
326
+
327
+ if (typeof item === "number" && typeof lookupValue === "number") {
328
+ const diff = item - lookupValue;
329
+ if (matchMode === -1 && diff < 0 && Math.abs(diff) < bestDiff) {
330
+ // Next smaller (closest negative difference)
331
+ bestDiff = Math.abs(diff);
332
+ matchIdx = idx;
333
+ } else if (matchMode === 1 && diff > 0 && diff < bestDiff) {
334
+ // Next larger (closest positive difference)
335
+ bestDiff = diff;
336
+ matchIdx = idx;
337
+ }
338
+ }
339
+ }
340
+ }
341
+
342
+ if (matchIdx === -1) return ifNotFound;
343
+
344
+ if (matchIdx >= 0 && matchIdx < returnArray.length) {
345
+ return returnArray[matchIdx];
346
+ }
347
+
348
+ return "#N/A";
349
+ };
350
+
351
+ // Register functions
352
+ functionRegistry.register({
353
+ name: "VLOOKUP",
354
+ handler: vlookupHandler,
355
+ minArgs: 3,
356
+ maxArgs: 4,
357
+ description: "Looks for a value in the leftmost column of a table, and then returns a value in the same row from a column you specify",
358
+ examples: ["VLOOKUP(105, A2:C10, 2)", 'VLOOKUP("Smith", A2:E10, 5, FALSE)'],
359
+ category: "Lookup & Reference",
360
+ });
361
+
362
+ functionRegistry.register({
363
+ name: "HLOOKUP",
364
+ handler: hlookupHandler,
365
+ minArgs: 3,
366
+ maxArgs: 4,
367
+ description: "Looks for a value in the top row of a table, and then returns a value in the same column from a row you specify",
368
+ examples: ['HLOOKUP("Axles", A1:C10, 2, TRUE)'],
369
+ category: "Lookup & Reference",
370
+ });
371
+
372
+ functionRegistry.register({
373
+ name: "MATCH",
374
+ handler: matchHandler,
375
+ minArgs: 2,
376
+ maxArgs: 3,
377
+ description: "Returns the relative position of an item in an array that matches a specified value",
378
+ examples: ["MATCH(25, A1:A10, 0)", 'MATCH("b", A1:A5, 0)'],
379
+ category: "Lookup & Reference",
380
+ });
381
+
382
+ functionRegistry.register({
383
+ name: "INDEX",
384
+ handler: indexHandler,
385
+ minArgs: 2,
386
+ maxArgs: 4,
387
+ description: "Returns the value of an element in a table or an array, selected by the row and column number indexes",
388
+ examples: ["INDEX(A1:B5, 2, 2)", "INDEX(A1:A10, 5)"],
389
+ category: "Lookup & Reference",
390
+ });
391
+
392
+ functionRegistry.register({
393
+ name: "XLOOKUP",
394
+ handler: xlookupHandler,
395
+ minArgs: 3,
396
+ maxArgs: 6,
397
+ description: "Searches a range or an array, and returns an item corresponding to the first match it finds",
398
+ examples: ["XLOOKUP(A1, B1:B10, C1:C10)", 'XLOOKUP("USA", Countries, Populations)'],
399
+ category: "Lookup & Reference",
400
+ });