@solidxai/core-ui 0.1.20-beta.0 → 0.1.20

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 (252) hide show
  1. package/dist/components/common/Link.d.ts.map +1 -1
  2. package/dist/components/common/Link.js +2 -1
  3. package/dist/components/common/Link.js.map +1 -1
  4. package/dist/components/common/Link.tsx +2 -1
  5. package/dist/components/common/SolidSettings/LlmSettings/AiModelConfigTab.d.ts +1 -0
  6. package/dist/components/common/SolidSettings/LlmSettings/AiModelConfigTab.d.ts.map +1 -1
  7. package/dist/components/common/SolidSettings/LlmSettings/AiModelConfigTab.js +109 -23
  8. package/dist/components/common/SolidSettings/LlmSettings/AiModelConfigTab.js.map +1 -1
  9. package/dist/components/common/SolidSettings/LlmSettings/AiModelConfigTab.tsx +155 -34
  10. package/dist/components/core/chatter/SolidChatter.js +2 -2
  11. package/dist/components/core/chatter/SolidChatter.js.map +1 -1
  12. package/dist/components/core/chatter/SolidChatter.tsx +2 -2
  13. package/dist/components/core/common/SolidGenericImport/SolidImportTransaction.js +20 -19
  14. package/dist/components/core/common/SolidGenericImport/SolidImportTransaction.js.map +1 -1
  15. package/dist/components/core/common/SolidGenericImport/SolidImportTransaction.tsx +16 -16
  16. package/dist/components/core/extension/solid-core/moduleMetadata/list/ExportModulePackageRowAction.d.ts.map +1 -1
  17. package/dist/components/core/extension/solid-core/moduleMetadata/list/ExportModulePackageRowAction.js +36 -9
  18. package/dist/components/core/extension/solid-core/moduleMetadata/list/ExportModulePackageRowAction.js.map +1 -1
  19. package/dist/components/core/extension/solid-core/moduleMetadata/list/ExportModulePackageRowAction.tsx +78 -7
  20. package/dist/components/core/extension/solid-core/moduleMetadata/list/ModuleImportListHeaderAction.d.ts +1 -0
  21. package/dist/components/core/extension/solid-core/moduleMetadata/list/ModuleImportListHeaderAction.d.ts.map +1 -1
  22. package/dist/components/core/extension/solid-core/moduleMetadata/list/ModuleImportListHeaderAction.js +1 -1
  23. package/dist/components/core/extension/solid-core/moduleMetadata/list/ModuleImportListHeaderAction.js.map +1 -1
  24. package/dist/components/core/extension/solid-core/moduleMetadata/list/ModuleImportListHeaderAction.tsx +1 -1
  25. package/dist/components/core/extension/solid-core/moduleMetadata/list/RestoreModulePackageRowAction.d.ts +4 -0
  26. package/dist/components/core/extension/solid-core/moduleMetadata/list/RestoreModulePackageRowAction.d.ts.map +1 -0
  27. package/dist/components/core/extension/solid-core/moduleMetadata/list/RestoreModulePackageRowAction.js +131 -0
  28. package/dist/components/core/extension/solid-core/moduleMetadata/list/RestoreModulePackageRowAction.js.map +1 -0
  29. package/dist/components/core/extension/solid-core/moduleMetadata/list/RestoreModulePackageRowAction.tsx +151 -0
  30. package/dist/components/core/extension/solid-core/moduleMetadata/list/UpdateModulePackageRowAction.d.ts +4 -0
  31. package/dist/components/core/extension/solid-core/moduleMetadata/list/UpdateModulePackageRowAction.d.ts.map +1 -0
  32. package/dist/components/core/extension/solid-core/moduleMetadata/list/UpdateModulePackageRowAction.js +13 -0
  33. package/dist/components/core/extension/solid-core/moduleMetadata/list/UpdateModulePackageRowAction.js.map +1 -0
  34. package/dist/components/core/extension/solid-core/moduleMetadata/list/UpdateModulePackageRowAction.tsx +20 -0
  35. package/dist/components/core/form/SolidFormView.d.ts.map +1 -1
  36. package/dist/components/core/form/SolidFormView.js +4 -0
  37. package/dist/components/core/form/SolidFormView.js.map +1 -1
  38. package/dist/components/core/form/SolidFormView.tsx +4 -0
  39. package/dist/components/core/kanban/SolidKanbanView.d.ts.map +1 -1
  40. package/dist/components/core/kanban/SolidKanbanView.js +50 -15
  41. package/dist/components/core/kanban/SolidKanbanView.js.map +1 -1
  42. package/dist/components/core/kanban/SolidKanbanView.tsx +22 -3
  43. package/dist/components/core/list/SolidListView.d.ts.map +1 -1
  44. package/dist/components/core/list/SolidListView.js +16 -3
  45. package/dist/components/core/list/SolidListView.js.map +1 -1
  46. package/dist/components/core/list/SolidListView.tsx +16 -2
  47. package/dist/components/core/list/columns/SolidMediaMultipleColumn.d.ts.map +1 -1
  48. package/dist/components/core/list/columns/SolidMediaMultipleColumn.js +4 -1
  49. package/dist/components/core/list/columns/SolidMediaMultipleColumn.js.map +1 -1
  50. package/dist/components/core/list/columns/SolidMediaMultipleColumn.tsx +4 -1
  51. package/dist/components/core/module/ModulePackageDialog.css +102 -0
  52. package/dist/components/core/module/ModulePackageDialog.d.ts +2 -1
  53. package/dist/components/core/module/ModulePackageDialog.d.ts.map +1 -1
  54. package/dist/components/core/module/ModulePackageDialog.js +216 -68
  55. package/dist/components/core/module/ModulePackageDialog.js.map +1 -1
  56. package/dist/components/core/module/ModulePackageDialog.tsx +356 -76
  57. package/dist/components/core/solid-agent/SolidAgentChat.d.ts.map +1 -1
  58. package/dist/components/core/solid-agent/SolidAgentChat.js +9 -3
  59. package/dist/components/core/solid-agent/SolidAgentChat.js.map +1 -1
  60. package/dist/components/core/solid-agent/SolidAgentChat.tsx +9 -4
  61. package/dist/components/core/solid-agent/SolidAgentMarkdown.d.ts.map +1 -1
  62. package/dist/components/core/solid-agent/SolidAgentMarkdown.js +25 -6
  63. package/dist/components/core/solid-agent/SolidAgentMarkdown.js.map +1 -1
  64. package/dist/components/core/solid-agent/SolidAgentMarkdown.tsx +15 -1
  65. package/dist/components/core/solid-agent/client/agentRuntime.d.ts +5 -0
  66. package/dist/components/core/solid-agent/client/agentRuntime.d.ts.map +1 -1
  67. package/dist/components/core/solid-agent/client/agentRuntime.js +77 -20
  68. package/dist/components/core/solid-agent/client/agentRuntime.js.map +1 -1
  69. package/dist/components/core/solid-agent/client/agentRuntime.ts +29 -1
  70. package/dist/components/core/solid-agent/client/agentSocket.d.ts +5 -1
  71. package/dist/components/core/solid-agent/client/agentSocket.d.ts.map +1 -1
  72. package/dist/components/core/solid-agent/client/agentSocket.js +27 -4
  73. package/dist/components/core/solid-agent/client/agentSocket.js.map +1 -1
  74. package/dist/components/core/solid-agent/client/agentSocket.ts +14 -3
  75. package/dist/components/core/solid-agent/embed/AgentEmbeddingPanel.js +2 -2
  76. package/dist/components/core/solid-agent/embed/AgentEmbeddingPanel.js.map +1 -1
  77. package/dist/components/core/solid-agent/embed/AgentEmbeddingPanel.tsx +5 -5
  78. package/dist/components/core/solid-agent/embed/agentEmbedProtocol.d.ts +2 -0
  79. package/dist/components/core/solid-agent/embed/agentEmbedProtocol.d.ts.map +1 -1
  80. package/dist/components/core/solid-agent/embed/agentEmbedProtocol.js +2 -2
  81. package/dist/components/core/solid-agent/embed/agentEmbedProtocol.js.map +1 -1
  82. package/dist/components/core/solid-agent/embed/agentEmbedProtocol.ts +4 -4
  83. package/dist/components/core/solid-agent/thread/AgentThread.d.ts.map +1 -1
  84. package/dist/components/core/solid-agent/thread/AgentThread.js +61 -26
  85. package/dist/components/core/solid-agent/thread/AgentThread.js.map +1 -1
  86. package/dist/components/core/solid-agent/thread/AgentThread.tsx +74 -40
  87. package/dist/components/core/solid-agent/thread/SolidAgentChatItem.d.ts +2 -1
  88. package/dist/components/core/solid-agent/thread/SolidAgentChatItem.d.ts.map +1 -1
  89. package/dist/components/core/solid-agent/thread/SolidAgentChatItem.js +2 -1
  90. package/dist/components/core/solid-agent/thread/SolidAgentChatItem.js.map +1 -1
  91. package/dist/components/core/solid-agent/thread/SolidAgentChatItem.tsx +3 -1
  92. package/dist/components/core/solid-agent/types.d.ts +3 -0
  93. package/dist/components/core/solid-agent/types.d.ts.map +1 -1
  94. package/dist/components/core/solid-agent/types.js +0 -9
  95. package/dist/components/core/solid-agent/types.js.map +1 -1
  96. package/dist/components/core/solid-agent/types.ts +4 -2
  97. package/dist/components/core/solid-agent/widgets/DefaultAssistantMessageChatWidget.d.ts +1 -1
  98. package/dist/components/core/solid-agent/widgets/DefaultAssistantMessageChatWidget.d.ts.map +1 -1
  99. package/dist/components/core/solid-agent/widgets/DefaultAssistantMessageChatWidget.js +5 -2
  100. package/dist/components/core/solid-agent/widgets/DefaultAssistantMessageChatWidget.js.map +1 -1
  101. package/dist/components/core/solid-agent/widgets/DefaultAssistantMessageChatWidget.tsx +11 -5
  102. package/dist/components/core/solid-agent/widgets/DefaultErrorChatWidget.js +3 -0
  103. package/dist/components/core/solid-agent/widgets/DefaultErrorChatWidget.js.map +1 -1
  104. package/dist/components/core/solid-agent/widgets/DefaultErrorChatWidget.tsx +4 -0
  105. package/dist/components/core/solid-agent/widgets/DefaultNoticeChatWidget.js +3 -0
  106. package/dist/components/core/solid-agent/widgets/DefaultNoticeChatWidget.js.map +1 -1
  107. package/dist/components/core/solid-agent/widgets/DefaultNoticeChatWidget.tsx +4 -0
  108. package/dist/components/core/solid-agent/widgets/DefaultThinkingChatWidget.js +3 -0
  109. package/dist/components/core/solid-agent/widgets/DefaultThinkingChatWidget.js.map +1 -1
  110. package/dist/components/core/solid-agent/widgets/DefaultThinkingChatWidget.tsx +4 -0
  111. package/dist/components/core/solid-agent/widgets/DefaultToolCallChatWidget.js +3 -0
  112. package/dist/components/core/solid-agent/widgets/DefaultToolCallChatWidget.js.map +1 -1
  113. package/dist/components/core/solid-agent/widgets/DefaultToolCallChatWidget.tsx +4 -0
  114. package/dist/components/core/solid-agent/widgets/DefaultUnknownChatWidget.js +3 -0
  115. package/dist/components/core/solid-agent/widgets/DefaultUnknownChatWidget.js.map +1 -1
  116. package/dist/components/core/solid-agent/widgets/DefaultUnknownChatWidget.tsx +4 -0
  117. package/dist/components/core/solid-agent/widgets/DefaultUserMessageChatWidget.js +3 -0
  118. package/dist/components/core/solid-agent/widgets/DefaultUserMessageChatWidget.js.map +1 -1
  119. package/dist/components/core/solid-agent/widgets/DefaultUserMessageChatWidget.tsx +4 -0
  120. package/dist/components/core/solid-agent/widgets/SolidChecklistChatWidget.js +3 -0
  121. package/dist/components/core/solid-agent/widgets/SolidChecklistChatWidget.js.map +1 -1
  122. package/dist/components/core/solid-agent/widgets/SolidChecklistChatWidget.tsx +4 -0
  123. package/dist/components/core/solid-agent/widgets/SolidCodeChatWidget.js +3 -0
  124. package/dist/components/core/solid-agent/widgets/SolidCodeChatWidget.js.map +1 -1
  125. package/dist/components/core/solid-agent/widgets/SolidCodeChatWidget.tsx +4 -0
  126. package/dist/components/core/solid-agent/widgets/SolidDiffListChatWidget.js +3 -0
  127. package/dist/components/core/solid-agent/widgets/SolidDiffListChatWidget.js.map +1 -1
  128. package/dist/components/core/solid-agent/widgets/SolidDiffListChatWidget.tsx +4 -0
  129. package/dist/components/core/solid-agent/widgets/SolidGalleryChatWidget.js +19 -0
  130. package/dist/components/core/solid-agent/widgets/SolidGalleryChatWidget.js.map +1 -1
  131. package/dist/components/core/solid-agent/widgets/SolidGalleryChatWidget.tsx +20 -0
  132. package/dist/components/core/solid-agent/widgets/SolidJsonChatWidget.js +3 -0
  133. package/dist/components/core/solid-agent/widgets/SolidJsonChatWidget.js.map +1 -1
  134. package/dist/components/core/solid-agent/widgets/SolidJsonChatWidget.tsx +4 -0
  135. package/dist/components/core/solid-agent/widgets/SolidLinkCardChatWidget.js +3 -0
  136. package/dist/components/core/solid-agent/widgets/SolidLinkCardChatWidget.js.map +1 -1
  137. package/dist/components/core/solid-agent/widgets/SolidLinkCardChatWidget.tsx +4 -0
  138. package/dist/components/core/solid-agent/widgets/SolidMarkdownChatWidget.js +3 -0
  139. package/dist/components/core/solid-agent/widgets/SolidMarkdownChatWidget.js.map +1 -1
  140. package/dist/components/core/solid-agent/widgets/SolidMarkdownChatWidget.tsx +4 -0
  141. package/dist/components/core/solid-agent/widgets/SolidProgressChatWidget.js +3 -0
  142. package/dist/components/core/solid-agent/widgets/SolidProgressChatWidget.js.map +1 -1
  143. package/dist/components/core/solid-agent/widgets/SolidProgressChatWidget.tsx +4 -0
  144. package/dist/components/core/solid-agent/widgets/SolidQuestionChatWidget.js +3 -0
  145. package/dist/components/core/solid-agent/widgets/SolidQuestionChatWidget.js.map +1 -1
  146. package/dist/components/core/solid-agent/widgets/SolidQuestionChatWidget.tsx +4 -0
  147. package/dist/components/core/solid-agent/widgets/SolidStatusChatWidget.js +3 -0
  148. package/dist/components/core/solid-agent/widgets/SolidStatusChatWidget.js.map +1 -1
  149. package/dist/components/core/solid-agent/widgets/SolidStatusChatWidget.tsx +4 -0
  150. package/dist/components/core/solid-agent/widgets/SolidTableChatWidget.js +3 -0
  151. package/dist/components/core/solid-agent/widgets/SolidTableChatWidget.js.map +1 -1
  152. package/dist/components/core/solid-agent/widgets/SolidTableChatWidget.tsx +4 -0
  153. package/dist/components/core/solid-agent/widgets/SolidToolActivityChatWidget.js +3 -0
  154. package/dist/components/core/solid-agent/widgets/SolidToolActivityChatWidget.js.map +1 -1
  155. package/dist/components/core/solid-agent/widgets/SolidToolActivityChatWidget.tsx +4 -0
  156. package/dist/components/core/solid-agent/widgets/solidChatWidgets.module.css +9 -1
  157. package/dist/components/core/users/CreateUser.d.ts.map +1 -1
  158. package/dist/components/core/users/CreateUser.js +23 -8
  159. package/dist/components/core/users/CreateUser.js.map +1 -1
  160. package/dist/components/core/users/CreateUser.tsx +10 -6
  161. package/dist/components/layout/AdminTopHeader.js +2 -2
  162. package/dist/components/layout/AdminTopHeader.js.map +1 -1
  163. package/dist/components/layout/AdminTopHeader.tsx +2 -2
  164. package/dist/components/layout/AppSidebar.d.ts.map +1 -1
  165. package/dist/components/layout/AppSidebar.js +21 -5
  166. package/dist/components/layout/AppSidebar.js.map +1 -1
  167. package/dist/components/layout/AppSidebar.tsx +24 -6
  168. package/dist/components/layout/SolidAiStudioLayout.d.ts.map +1 -1
  169. package/dist/components/layout/SolidAiStudioLayout.js +3 -4
  170. package/dist/components/layout/SolidAiStudioLayout.js.map +1 -1
  171. package/dist/components/layout/SolidAiStudioLayout.tsx +3 -4
  172. package/dist/components/layout/user-profile-menu.js +10 -10
  173. package/dist/components/layout/user-profile-menu.js.map +1 -1
  174. package/dist/components/layout/user-profile-menu.tsx +3 -3
  175. package/dist/components/shad-cn-ui/SolidLightbox.d.ts +2 -1
  176. package/dist/components/shad-cn-ui/SolidLightbox.d.ts.map +1 -1
  177. package/dist/components/shad-cn-ui/SolidLightbox.js +11 -4
  178. package/dist/components/shad-cn-ui/SolidLightbox.js.map +1 -1
  179. package/dist/components/shad-cn-ui/SolidLightbox.tsx +14 -1
  180. package/dist/helpers/modelViewPersistence.d.ts +2 -0
  181. package/dist/helpers/modelViewPersistence.d.ts.map +1 -1
  182. package/dist/helpers/modelViewPersistence.js +72 -0
  183. package/dist/helpers/modelViewPersistence.js.map +1 -1
  184. package/dist/helpers/modelViewPersistence.ts +79 -0
  185. package/dist/helpers/registry.d.ts +6 -2
  186. package/dist/helpers/registry.d.ts.map +1 -1
  187. package/dist/helpers/registry.js +68 -3
  188. package/dist/helpers/registry.js.map +1 -1
  189. package/dist/helpers/registry.ts +62 -4
  190. package/dist/hooks/useModelsDevModels.d.ts +43 -0
  191. package/dist/hooks/useModelsDevModels.d.ts.map +1 -0
  192. package/dist/hooks/useModelsDevModels.js +81 -0
  193. package/dist/hooks/useModelsDevModels.js.map +1 -0
  194. package/dist/hooks/useModelsDevModels.ts +106 -0
  195. package/dist/hooks/useRouter.d.ts.map +1 -1
  196. package/dist/hooks/useRouter.js +3 -2
  197. package/dist/hooks/useRouter.js.map +1 -1
  198. package/dist/hooks/useRouter.ts +3 -2
  199. package/dist/index.d.ts +2 -2
  200. package/dist/index.d.ts.map +1 -1
  201. package/dist/index.js +1 -1
  202. package/dist/index.js.map +1 -1
  203. package/dist/index.ts +4 -1
  204. package/dist/modules/solidUiModule.d.ts +3 -2
  205. package/dist/modules/solidUiModule.d.ts.map +1 -1
  206. package/dist/modules/solidUiModule.js +1 -1
  207. package/dist/modules/solidUiModule.js.map +1 -1
  208. package/dist/modules/solidUiModule.ts +5 -1
  209. package/dist/redux/api/moduleApi.d.ts +12 -2
  210. package/dist/redux/api/moduleApi.d.ts.map +1 -1
  211. package/dist/redux/api/moduleApi.js +33 -3
  212. package/dist/redux/api/moduleApi.js.map +1 -1
  213. package/dist/redux/api/moduleApi.ts +17 -2
  214. package/dist/redux/api/solidChatterMessageApi.d.ts +3 -1
  215. package/dist/redux/api/solidChatterMessageApi.d.ts.map +1 -1
  216. package/dist/redux/api/solidChatterMessageApi.js +9 -1
  217. package/dist/redux/api/solidChatterMessageApi.js.map +1 -1
  218. package/dist/redux/api/solidChatterMessageApi.ts +9 -1
  219. package/dist/redux/store/defaultStoreConfig.d.ts +9 -1
  220. package/dist/redux/store/defaultStoreConfig.d.ts.map +1 -1
  221. package/dist/resources/globals.css +30 -0
  222. package/dist/resources/solid-custom.css +10 -6
  223. package/dist/resources/solid-tailwind-generated.css +1 -1
  224. package/dist/routes/AppEventListener.js +1 -1
  225. package/dist/routes/AppEventListener.js.map +1 -1
  226. package/dist/routes/AppEventListener.tsx +1 -1
  227. package/dist/routes/pages/admin/core/AgentPersonaHistory.css +130 -0
  228. package/dist/routes/pages/admin/core/AgentPersonaHistory.d.ts +7 -0
  229. package/dist/routes/pages/admin/core/AgentPersonaHistory.d.ts.map +1 -0
  230. package/dist/routes/pages/admin/core/AgentPersonaHistory.js +228 -0
  231. package/dist/routes/pages/admin/core/AgentPersonaHistory.js.map +1 -0
  232. package/dist/routes/pages/admin/core/AgentPersonaHistory.tsx +234 -0
  233. package/dist/routes/pages/admin/core/AgentRegistryEditorPage.css +6 -14
  234. package/dist/routes/pages/admin/core/AgentRegistryEditorPage.d.ts.map +1 -1
  235. package/dist/routes/pages/admin/core/AgentRegistryEditorPage.js +186 -146
  236. package/dist/routes/pages/admin/core/AgentRegistryEditorPage.js.map +1 -1
  237. package/dist/routes/pages/admin/core/AgentRegistryEditorPage.tsx +170 -118
  238. package/dist/routes/pages/admin/core/ArchivedChatterMessagesPage.d.ts +2 -0
  239. package/dist/routes/pages/admin/core/ArchivedChatterMessagesPage.d.ts.map +1 -0
  240. package/dist/routes/pages/admin/core/ArchivedChatterMessagesPage.js +108 -0
  241. package/dist/routes/pages/admin/core/ArchivedChatterMessagesPage.js.map +1 -0
  242. package/dist/routes/pages/admin/core/ArchivedChatterMessagesPage.tsx +96 -0
  243. package/dist/routes/solidRoutes.d.ts.map +1 -1
  244. package/dist/routes/solidRoutes.js +2 -0
  245. package/dist/routes/solidRoutes.js.map +1 -1
  246. package/dist/routes/solidRoutes.tsx +2 -0
  247. package/dist/types/extension-registry.d.ts +12 -0
  248. package/dist/types/extension-registry.d.ts.map +1 -1
  249. package/dist/types/extension-registry.js.map +1 -1
  250. package/dist/types/extension-registry.ts +15 -0
  251. package/dist/types/solid-core.d.ts +2 -0
  252. package/package.json +2 -1
@@ -1,10 +1,11 @@
1
1
  import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
2
2
  import { useDropzone } from "react-dropzone";
3
- import { FileArchive, FolderTree, RefreshCw, Sparkles, UploadCloud, X } from "lucide-react";
3
+ import { ArrowRight, FileArchive, FolderTree, RefreshCw, RotateCcw, Sparkles, UploadCloud, X } from "lucide-react";
4
4
  import {
5
5
  useConfirmModulePackageImportMutation,
6
6
  useDismissModulePackageImportMutation,
7
7
  useLazyGetModulePackageImportStatusQuery,
8
+ useRestoreModulePackageImportMutation,
8
9
  useRunModulePackageBuildMutation,
9
10
  useRunModulePackageSeedMutation,
10
11
  useValidateModulePackageImportMutation,
@@ -36,8 +37,12 @@ type ModulePackageImportContentProps = {
36
37
  onImported?: () => void;
37
38
  initialTransactionKey?: string | null;
38
39
  autoResume?: boolean;
40
+ // Update this installed module: the uploaded package must be for it.
41
+ targetModuleName?: string | null;
39
42
  };
40
43
 
44
+ type PackageMode = "install" | "update";
45
+
41
46
  type StepStatus = "pending" | "running" | "success" | "warning" | "error";
42
47
  type StepKey = "preview" | "import" | "build" | "seed";
43
48
 
@@ -52,16 +57,45 @@ type StepMap = Record<StepKey, StepState>;
52
57
 
53
58
  const STEP_ORDER: StepKey[] = ["preview", "import", "build", "seed"];
54
59
 
55
- function createInitialSteps(): StepMap {
60
+ const VERSION_COMPARISONS: Record<string, { label: string; tone: "neutral" | "warning" | "danger" }> = {
61
+ new: { label: "New install", tone: "neutral" },
62
+ upgrade: { label: "Upgrade", tone: "neutral" },
63
+ first_version: { label: "First versioned release", tone: "neutral" },
64
+ same: { label: "Reinstall", tone: "warning" },
65
+ downgrade: { label: "Downgrade", tone: "danger" },
66
+ legacy_package: { label: "Unversioned package", tone: "danger" },
67
+ };
68
+
69
+ function formatVersionLabel(version?: string | null) {
70
+ return version ? `v${version}` : "unversioned";
71
+ }
72
+
73
+ function getPackageMode(transaction: any, fallbackMode: PackageMode = "install"): PackageMode {
74
+ return transaction?.mode ?? transaction?.preview?.mode ?? fallbackMode;
75
+ }
76
+
77
+ function getWorkflowLabels(transaction: any, fallbackMode: PackageMode = "install") {
78
+ if (transaction?.restoredFromBackup) return { noun: "restore", past: "restored" };
79
+ return getPackageMode(transaction, fallbackMode) === "update"
80
+ ? { noun: "update", past: "updated" }
81
+ : { noun: "import", past: "imported" };
82
+ }
83
+
84
+ function createInitialSteps(mode: PackageMode = "install"): StepMap {
85
+ const isUpdate = mode === "update";
56
86
  return {
57
87
  preview: {
58
88
  title: "Preview",
59
- description: "Upload a `.sldx` archive to validate it and preview its contents automatically.",
89
+ description: isUpdate
90
+ ? "Upload a `.sldx` archive of the new version to compare it with the installed module."
91
+ : "Upload a `.sldx` archive to validate it and preview its contents automatically.",
60
92
  status: "pending",
61
93
  },
62
94
  import: {
63
- title: "Import",
64
- description: "Import the files locally. This may restart the UI and API, dismiss the popup, and require a page refresh before you continue.",
95
+ title: isUpdate ? "Update" : "Import",
96
+ description: isUpdate
97
+ ? "Replace the module folders with the new version. The current folders are backed up first. This may restart the UI and API, dismiss the popup, and require a page refresh before you continue."
98
+ : "Import the files locally. This may restart the UI and API, dismiss the popup, and require a page refresh before you continue.",
65
99
  status: "pending",
66
100
  },
67
101
  build: {
@@ -71,7 +105,9 @@ function createInitialSteps(): StepMap {
71
105
  },
72
106
  seed: {
73
107
  title: "Seed",
74
- description: "Seed the imported module after the build step completes.",
108
+ description: isUpdate
109
+ ? "Seed the updated module after the build step completes."
110
+ : "Seed the imported module after the build step completes.",
75
111
  status: "pending",
76
112
  },
77
113
  };
@@ -104,6 +140,7 @@ function isResumableModulePackageStatus(status?: string | null) {
104
140
  "build_succeeded",
105
141
  "seed_running",
106
142
  "seed_failed",
143
+ "restore_running",
107
144
  ].includes(String(status ?? ""));
108
145
  }
109
146
 
@@ -115,9 +152,11 @@ function getBuildStepStatusFromTransaction(transaction: any): StepStatus {
115
152
  return "pending";
116
153
  }
117
154
 
118
- function hydrateStepsFromTransaction(transaction: any): StepMap {
119
- const nextSteps = createInitialSteps();
155
+ function hydrateStepsFromTransaction(transaction: any, fallbackMode: PackageMode = "install"): StepMap {
156
+ const mode = getPackageMode(transaction, fallbackMode);
157
+ const nextSteps = createInitialSteps(mode);
120
158
  const status = String(transaction?.status ?? "");
159
+ const { noun } = getWorkflowLabels(transaction, fallbackMode);
121
160
 
122
161
  if (transaction?.preview || transaction?.validation) {
123
162
  nextSteps.preview = {
@@ -130,13 +169,23 @@ function hydrateStepsFromTransaction(transaction: any): StepMap {
130
169
  }
131
170
 
132
171
  if (
133
- transaction?.outputs?.import ||
134
- ["awaiting_restart", "build_running", "build_failed", "build_succeeded", "seed_running", "seed_failed", "completed"].includes(status)
172
+ status !== "restore_running" &&
173
+ (transaction?.outputs?.import ||
174
+ ["awaiting_restart", "build_running", "build_failed", "build_succeeded", "seed_running", "seed_failed", "completed"].includes(status))
135
175
  ) {
136
176
  nextSteps.import = {
137
177
  ...nextSteps.import,
138
178
  status: "success",
139
- description: "Archive extracted. Wait for the UI and API to finish restarting, then refresh this page to continue.",
179
+ description: transaction?.restoredFromBackup
180
+ ? "Previous version restored. Wait for the UI and API to finish restarting, then refresh this page to continue."
181
+ : "Archive extracted. Wait for the UI and API to finish restarting, then refresh this page to continue.",
182
+ output: [transaction?.outputs?.import, transaction?.outputs?.restore].filter(Boolean).join("\n\n") || undefined,
183
+ };
184
+ } else if (status === "restore_running") {
185
+ nextSteps.import = {
186
+ ...nextSteps.import,
187
+ status: "warning",
188
+ description: "Restoring the previous version did not finish. Use Restore again to complete it.",
140
189
  output: transaction?.outputs?.import ?? undefined,
141
190
  };
142
191
  }
@@ -158,7 +207,7 @@ function hydrateStepsFromTransaction(transaction: any): StepMap {
158
207
  nextSteps.build = {
159
208
  ...nextSteps.build,
160
209
  status: "pending",
161
- description: "Import completed. Once the UI and API are back, refresh this module list page and continue with Build Now.",
210
+ description: `${noun.charAt(0).toUpperCase()}${noun.slice(1)} completed. Once the UI and API are back, refresh this module list page and continue with Build Now.`,
162
211
  };
163
212
  }
164
213
 
@@ -230,13 +279,24 @@ export function ModulePackageDialog({ open, onOpenChange, onImported }: ModulePa
230
279
  );
231
280
  }
232
281
 
233
- export function ModulePackageImportContent({ onClose, onImported, initialTransactionKey = null, autoResume = false }: ModulePackageImportContentProps) {
282
+ export function ModulePackageImportContent({
283
+ onClose,
284
+ onImported,
285
+ initialTransactionKey = null,
286
+ autoResume = false,
287
+ targetModuleName = null,
288
+ }: ModulePackageImportContentProps) {
234
289
  const dispatch = useDispatch();
290
+ const fallbackMode: PackageMode = targetModuleName ? "update" : "install";
235
291
  const [selectedFile, setSelectedFile] = useState<File | null>(null);
236
292
  const [previewResponse, setPreviewResponse] = useState<any | null>(null);
237
293
  const [transactionKey, setTransactionKey] = useState<string | null>(null);
238
294
  const [overwriteExisting, setOverwriteExisting] = useState(false);
239
- const [steps, setSteps] = useState<StepMap>(() => createInitialSteps());
295
+ const [acknowledgeVersionChange, setAcknowledgeVersionChange] = useState(false);
296
+ const [pruneOnSeed, setPruneOnSeed] = useState(false);
297
+ const [showAllFiles, setShowAllFiles] = useState(false);
298
+ const [isRestoreConfirmOpen, setRestoreConfirmOpen] = useState(false);
299
+ const [steps, setSteps] = useState<StepMap>(() => createInitialSteps(fallbackMode));
240
300
  const [selectedStep, setSelectedStep] = useState<StepKey>("preview");
241
301
  const [finalSummary, setFinalSummary] = useState<string | null>(null);
242
302
  const [isPreviewing, setIsPreviewing] = useState(false);
@@ -248,6 +308,7 @@ export function ModulePackageImportContent({ onClose, onImported, initialTransac
248
308
  const [fetchModulePackageImportStatus] = useLazyGetModulePackageImportStatusQuery();
249
309
  const [runModulePackageBuild] = useRunModulePackageBuildMutation();
250
310
  const [runModulePackageSeed] = useRunModulePackageSeedMutation();
311
+ const [restoreModulePackageImport] = useRestoreModulePackageImportMutation();
251
312
 
252
313
  const hasResumedTransactionRef = useRef(false);
253
314
  const lastAutoPreviewFileRef = useRef<string | null>(null);
@@ -282,7 +343,21 @@ export function ModulePackageImportContent({ onClose, onImported, initialTransac
282
343
  const hasConflicts = (previewResponse?.conflicts?.length ?? 0) > 0;
283
344
  const suggestedStep = getSuggestedStepFromSteps(steps, Boolean(previewResponse), isArchiveValid);
284
345
 
285
- const canImport = steps.preview.status !== "pending" && isArchiveValid && steps.import.status !== "success" && !isProcessing && !isPreviewing;
346
+ const packageMode = getPackageMode(previewResponse, fallbackMode);
347
+ const isUpdate = packageMode === "update";
348
+ const workflowLabels = getWorkflowLabels(previewResponse, fallbackMode);
349
+ const packageModuleName = previewResponse?.preview?.module?.name || previewResponse?.moduleName || targetModuleName || "this module";
350
+ const installedVersion = previewResponse?.installedVersion ?? previewResponse?.preview?.installedVersion ?? null;
351
+ const incomingVersion = previewResponse?.incomingVersion ?? previewResponse?.preview?.incomingVersion ?? null;
352
+ const versionComparison = previewResponse?.versionComparison ?? previewResponse?.preview?.versionComparison ?? null;
353
+ const requiresVersionAcknowledgement = Boolean(previewResponse?.preview?.requiresVersionAcknowledgement);
354
+ // Clicking the Update button approves the shown version; risky updates also need an explicit acknowledgement.
355
+ const isUpdateApproved = !isUpdate || !requiresVersionAcknowledgement || acknowledgeVersionChange;
356
+ const backup = previewResponse?.backup ?? null;
357
+
358
+ const canImport = steps.preview.status !== "pending" && isArchiveValid && isUpdateApproved && steps.import.status !== "success" && !isProcessing && !isPreviewing;
359
+ const canRestore = Boolean(backup && transactionKey) && steps.import.status !== "pending" && !isProcessing;
360
+ const isRestoreInterrupted = previewResponse?.status === "restore_running";
286
361
  const canRunBuild = steps.import.status === "success" && !isProcessing;
287
362
  const canRunSeed = (steps.build.status === "success" || steps.build.status === "warning") && !isProcessing;
288
363
  const canContinueFromPreview = Boolean(previewResponse) && isArchiveValid && !isPreviewing;
@@ -325,6 +400,9 @@ export function ModulePackageImportContent({ onClose, onImported, initialTransac
325
400
 
326
401
  const formData = new FormData();
327
402
  formData.append("modulePackage", selectedFile);
403
+ if (targetModuleName) {
404
+ formData.append("expectedModuleName", targetModuleName);
405
+ }
328
406
 
329
407
  try {
330
408
  setIsPreviewing(true);
@@ -332,16 +410,18 @@ export function ModulePackageImportContent({ onClose, onImported, initialTransac
332
410
  setPreviewResponse(response);
333
411
  setTransactionKey(response?.transactionKey ?? null);
334
412
  setOverwriteExisting(false);
413
+ setAcknowledgeVersionChange(false);
414
+ setShowAllFiles(false);
335
415
  setFinalSummary(null);
336
- setSteps(hydrateStepsFromTransaction(response));
416
+ setSteps(hydrateStepsFromTransaction(response, fallbackMode));
337
417
  setSelectedStep("preview");
338
418
  dispatch(
339
419
  showToast({
340
420
  severity: response?.validation?.valid ? "success" : "warn",
341
421
  summary: response?.validation?.valid ? "Archive validated" : "Archive needs attention",
342
422
  detail: response?.validation?.valid
343
- ? "Review the preview and continue with the import when you are ready."
344
- : "The uploaded archive is missing one or more required SolidX validation markers.",
423
+ ? `Review the preview and continue with the ${getWorkflowLabels(response, fallbackMode).noun} when you are ready.`
424
+ : "The uploaded archive has validation issues. Review them below.",
345
425
  }),
346
426
  );
347
427
  } catch (error: any) {
@@ -349,7 +429,7 @@ export function ModulePackageImportContent({ onClose, onImported, initialTransac
349
429
  } finally {
350
430
  setIsPreviewing(false);
351
431
  }
352
- }, [dispatch, selectedFile, validateModulePackageImport]);
432
+ }, [dispatch, fallbackMode, selectedFile, targetModuleName, validateModulePackageImport]);
353
433
 
354
434
  useEffect(() => {
355
435
  if (!selectedFile) {
@@ -393,12 +473,13 @@ export function ModulePackageImportContent({ onClose, onImported, initialTransac
393
473
  const executeSeedStep = useCallback(async (activeTransactionKey: string) => {
394
474
  updateStep("seed", {
395
475
  status: "running",
396
- description: "Running `solidctl seed --modules-to-seed` for the imported module.",
476
+ description: `Running \`solidctl seed --modules-to-seed${pruneOnSeed ? " --prune" : ""}\` for the ${workflowLabels.past} module.`,
397
477
  });
398
478
 
399
479
  const seedResponse = await runModulePackageSeed({
400
480
  transactionKey: activeTransactionKey,
401
481
  seedGlobalMetadata: false,
482
+ prune: pruneOnSeed,
402
483
  }).unwrap();
403
484
 
404
485
  const seedFailed = seedResponse?.status === "seed_failed";
@@ -411,7 +492,7 @@ export function ModulePackageImportContent({ onClose, onImported, initialTransac
411
492
  });
412
493
 
413
494
  return seedResponse;
414
- }, [runModulePackageSeed, updateStep]);
495
+ }, [pruneOnSeed, runModulePackageSeed, updateStep, workflowLabels.past]);
415
496
 
416
497
  const handleRunBuild = useCallback(async () => {
417
498
  if (!transactionKey) {
@@ -458,16 +539,16 @@ export function ModulePackageImportContent({ onClose, onImported, initialTransac
458
539
  }
459
540
 
460
541
  if (buildFailed && !seedFailed) {
461
- setFinalSummary("Module imported and seeded successfully, but the build step reported errors.");
542
+ setFinalSummary(`Module ${workflowLabels.past} and seeded successfully, but the build step reported errors.`);
462
543
  return;
463
544
  }
464
545
 
465
546
  if (seedFailed) {
466
- setFinalSummary("Module imported, but the seed step needs attention.");
547
+ setFinalSummary(`Module ${workflowLabels.past}, but the seed step needs attention.`);
467
548
  return;
468
549
  }
469
550
 
470
- setFinalSummary("Module import completed successfully.");
551
+ setFinalSummary(`Module ${workflowLabels.noun} completed successfully.`);
471
552
  } catch (error: any) {
472
553
  updateStep("seed", {
473
554
  status: "error",
@@ -476,7 +557,7 @@ export function ModulePackageImportContent({ onClose, onImported, initialTransac
476
557
  setFinalSummary("The seed step needs attention before the workflow can be closed.");
477
558
  setIsProcessing(false);
478
559
  }
479
- }, [dispatch, executeSeedStep, onImported, previewResponse?.outputs?.build, steps.build.status, transactionKey, updateStep]);
560
+ }, [dispatch, executeSeedStep, onImported, previewResponse?.outputs?.build, steps.build.status, transactionKey, updateStep, workflowLabels.noun, workflowLabels.past]);
480
561
 
481
562
  useEffect(() => {
482
563
  if (!initialTransactionKey || hasResumedTransactionRef.current) {
@@ -488,19 +569,21 @@ export function ModulePackageImportContent({ onClose, onImported, initialTransac
488
569
  const loadTransaction = async () => {
489
570
  try {
490
571
  const transaction = await fetchModulePackageImportStatus({ transactionKey: initialTransactionKey }).unwrap();
491
- const hydratedSteps = hydrateStepsFromTransaction(transaction);
572
+ const hydratedSteps = hydrateStepsFromTransaction(transaction, fallbackMode);
573
+ const labels = getWorkflowLabels(transaction, fallbackMode);
492
574
  setPreviewResponse(transaction);
493
575
  setTransactionKey(transaction?.transactionKey ?? null);
494
576
  setSteps(hydratedSteps);
577
+ setPruneOnSeed(Boolean(transaction?.restoredFromBackup));
495
578
  setSelectedStep(getSuggestedStepFromSteps(hydratedSteps, Boolean(transaction?.preview || transaction?.validation), Boolean(transaction?.validation?.valid)));
496
579
 
497
580
  if (transaction?.status === "completed") {
498
- setFinalSummary("Module import completed successfully.");
581
+ setFinalSummary(`Module ${labels.noun} completed successfully.`);
499
582
  return;
500
583
  }
501
584
 
502
585
  if (transaction?.status === "seed_failed") {
503
- setFinalSummary("Module imported, but the seed step needs attention.");
586
+ setFinalSummary(`Module ${labels.past}, but the seed step needs attention.`);
504
587
  return;
505
588
  }
506
589
 
@@ -513,7 +596,7 @@ export function ModulePackageImportContent({ onClose, onImported, initialTransac
513
596
  };
514
597
 
515
598
  void loadTransaction();
516
- }, [autoResume, dispatch, fetchModulePackageImportStatus, initialTransactionKey]);
599
+ }, [autoResume, dispatch, fallbackMode, fetchModulePackageImportStatus, initialTransactionKey]);
517
600
 
518
601
  const handleStartImport = async () => {
519
602
  if (!transactionKey) {
@@ -526,13 +609,16 @@ export function ModulePackageImportContent({ onClose, onImported, initialTransac
526
609
  setFinalSummary(null);
527
610
  updateStep("import", {
528
611
  status: "running",
529
- description: "Extracting the archive and placing files into `solid-api` and `solid-ui`.",
612
+ description: isUpdate
613
+ ? "Backing up the current module folders and placing the new version into `solid-api` and `solid-ui`."
614
+ : "Extracting the archive and placing files into `solid-api` and `solid-ui`.",
530
615
  });
531
616
 
532
- const response = await confirmModulePackageImport({
533
- transactionKey,
534
- overwriteExisting,
535
- }).unwrap();
617
+ const response = await confirmModulePackageImport(
618
+ isUpdate
619
+ ? { transactionKey, confirmedVersion: incomingVersion, acknowledgeVersionChange }
620
+ : { transactionKey, overwriteExisting },
621
+ ).unwrap();
536
622
 
537
623
  updateStep("import", {
538
624
  status: "success",
@@ -544,8 +630,10 @@ export function ModulePackageImportContent({ onClose, onImported, initialTransac
544
630
  dispatch(
545
631
  showToast({
546
632
  severity: "success",
547
- summary: "Module imported",
548
- detail: "The module files were placed locally. Wait for the UI and API to restart, then refresh this module list page to continue with build and seed.",
633
+ summary: isUpdate ? "Module updated" : "Module imported",
634
+ detail: isUpdate
635
+ ? `${packageModuleName} was updated to ${formatVersionLabel(incomingVersion)}. Wait for the UI and API to restart, then refresh this module list page to continue with build and seed.`
636
+ : "The module files were placed locally. Wait for the UI and API to restart, then refresh this module list page to continue with build and seed.",
549
637
  }),
550
638
  );
551
639
  onClose();
@@ -554,11 +642,39 @@ export function ModulePackageImportContent({ onClose, onImported, initialTransac
554
642
  status: "error",
555
643
  description: getErrorMessage(error),
556
644
  });
557
- setFinalSummary("The module import could not be completed.");
645
+ setFinalSummary(`The module ${workflowLabels.noun} could not be completed.`);
558
646
  setIsProcessing(false);
559
647
  }
560
648
  };
561
649
 
650
+ const handleRestore = async () => {
651
+ if (!transactionKey || !backup) {
652
+ return;
653
+ }
654
+
655
+ try {
656
+ setIsProcessing(true);
657
+ setFinalSummary(null);
658
+ const response = await restoreModulePackageImport({ transactionKey }).unwrap();
659
+ setPreviewResponse(response);
660
+ setSteps(hydrateStepsFromTransaction(response, fallbackMode));
661
+ setPruneOnSeed(true);
662
+ setRestoreConfirmOpen(false);
663
+ setIsProcessing(false);
664
+ dispatch(
665
+ showToast({
666
+ severity: "success",
667
+ summary: "Previous version restored",
668
+ detail: `${packageModuleName} ${formatVersionLabel(backup.version)} was put back. Wait for the UI and API to restart, then refresh this module list page to continue with build and seed.`,
669
+ }),
670
+ );
671
+ onClose();
672
+ } catch (error: any) {
673
+ setIsProcessing(false);
674
+ dispatch(showToast({ severity: "error", summary: "Restore failed", detail: getErrorMessage(error) }));
675
+ }
676
+ };
677
+
562
678
  const handleClose = () => {
563
679
  if (isProcessing) return;
564
680
  if (finalSummary && transactionKey) {
@@ -567,6 +683,75 @@ export function ModulePackageImportContent({ onClose, onImported, initialTransac
567
683
  onClose();
568
684
  };
569
685
 
686
+ const renderVersionApproval = () => {
687
+ const comparison = VERSION_COMPARISONS[versionComparison ?? ""] ?? { label: "Update", tone: "neutral" };
688
+ const identicalContent = Boolean(previewResponse?.preview?.identicalContent);
689
+ const acknowledgementLabel = identicalContent
690
+ ? "Reinstall anyway, even though the content is identical"
691
+ : versionComparison === "same"
692
+ ? `I understand this reinstalls ${formatVersionLabel(installedVersion)}`
693
+ : versionComparison === "downgrade"
694
+ ? `I understand this downgrades ${packageModuleName} to ${formatVersionLabel(incomingVersion)}`
695
+ : "I understand this package has no version";
696
+
697
+ return (
698
+ <div className={`solid-module-package-notice${comparison.tone === "danger" ? " is-danger" : comparison.tone === "warning" ? " is-warning" : ""}`}>
699
+ <div className="solid-module-package-notice__title">Version change</div>
700
+ <div className="solid-module-package-version">
701
+ <span className="solid-module-package-version__value">{formatVersionLabel(installedVersion)}</span>
702
+ <ArrowRight size={14} aria-label="to" />
703
+ <span className="solid-module-package-version__value">{formatVersionLabel(incomingVersion)}</span>
704
+ <span className={`solid-module-package-version__badge is-${comparison.tone}`}>{comparison.label}</span>
705
+ </div>
706
+ {isArchiveValid && requiresVersionAcknowledgement ? (
707
+ <SolidCheckbox
708
+ checked={acknowledgeVersionChange}
709
+ onChange={(event) => setAcknowledgeVersionChange(event.currentTarget.checked)}
710
+ label={acknowledgementLabel}
711
+ />
712
+ ) : null}
713
+ </div>
714
+ );
715
+ };
716
+
717
+ const renderChangeSummary = () => {
718
+ const changeSet = previewResponse?.preview?.changeSet;
719
+ if (!changeSet) return null;
720
+
721
+ const sides: Array<[string, any]> = [["solid-api", changeSet.solidApi], ["solid-ui", changeSet.solidUi]];
722
+ return (
723
+ <div className="solid-module-package-checklist__section">
724
+ <div className="solid-module-package-checklist__title">Files that will change</div>
725
+ {previewResponse?.preview?.identicalContent ? (
726
+ <p className="solid-module-package-diff__empty">No file changes. The package content matches the installed module.</p>
727
+ ) : null}
728
+ {sides.map(([label, diff]) => {
729
+ const added: string[] = diff?.added ?? [];
730
+ const modified: string[] = diff?.modified ?? [];
731
+ const removed: string[] = diff?.removed ?? [];
732
+ const hasChanges = added.length + modified.length + removed.length > 0;
733
+ return (
734
+ <details key={label} className="solid-module-package-diff">
735
+ <summary>
736
+ <span className="solid-module-package-diff__side">{label}</span>
737
+ <span className="is-added">+{added.length}</span>
738
+ <span className="is-modified">~{modified.length}</span>
739
+ <span className="is-removed">−{removed.length}</span>
740
+ <span className="solid-module-package-diff__unchanged">{diff?.unchangedCount ?? 0} unchanged</span>
741
+ </summary>
742
+ <ul className="solid-module-package-diff__list">
743
+ {added.map((file) => <li key={`a:${file}`} className="is-added">+ {file}</li>)}
744
+ {modified.map((file) => <li key={`m:${file}`} className="is-modified">~ {file}</li>)}
745
+ {removed.map((file) => <li key={`r:${file}`} className="is-removed">− {file}</li>)}
746
+ {!hasChanges ? <li>No changes</li> : null}
747
+ </ul>
748
+ </details>
749
+ );
750
+ })}
751
+ </div>
752
+ );
753
+ };
754
+
570
755
  const renderPreviewContent = () => {
571
756
  return (
572
757
  <div className="solid-module-package-preview">
@@ -621,9 +806,10 @@ export function ModulePackageImportContent({ onClose, onImported, initialTransac
621
806
  setPreviewResponse(null);
622
807
  setTransactionKey(null);
623
808
  setOverwriteExisting(false);
809
+ setAcknowledgeVersionChange(false);
624
810
  setFinalSummary(null);
625
811
  setSelectedStep("preview");
626
- setSteps(createInitialSteps());
812
+ setSteps(createInitialSteps(fallbackMode));
627
813
  }}
628
814
  disabled={isProcessing}
629
815
  >
@@ -659,6 +845,9 @@ export function ModulePackageImportContent({ onClose, onImported, initialTransac
659
845
  </div>
660
846
 
661
847
  <div className="solid-module-package-checklist">
848
+ {isUpdate ? renderVersionApproval() : null}
849
+ {isUpdate ? renderChangeSummary() : null}
850
+
662
851
  <div className="solid-module-package-checklist__section">
663
852
  <div className="solid-module-package-checklist__title">Required markers</div>
664
853
  <ul className="solid-module-package-inline-list">
@@ -690,7 +879,7 @@ export function ModulePackageImportContent({ onClose, onImported, initialTransac
690
879
  </div>
691
880
  ) : null}
692
881
 
693
- {hasConflicts ? (
882
+ {hasConflicts && !isUpdate ? (
694
883
  <div className="solid-module-package-notice is-warning">
695
884
  <div className="solid-module-package-notice__title">Existing files detected</div>
696
885
  <ul className="solid-module-package-inline-list">
@@ -707,13 +896,23 @@ export function ModulePackageImportContent({ onClose, onImported, initialTransac
707
896
  ) : null}
708
897
  </div>
709
898
 
710
- <div className="solid-module-package-tree">
711
- {(previewResponse?.preview?.fileTree ?? []).map((entry: string) => (
712
- <div key={entry} className="solid-module-package-tree__row">
713
- {entry}
714
- </div>
715
- ))}
716
- </div>
899
+ {isUpdate ? (
900
+ <div>
901
+ <SolidButton type="button" size="small" variant="ghost" onClick={() => setShowAllFiles((current) => !current)}>
902
+ {showAllFiles ? "Hide package files" : `Show all package files (${previewResponse?.preview?.fileTree?.length ?? 0})`}
903
+ </SolidButton>
904
+ </div>
905
+ ) : null}
906
+
907
+ {!isUpdate || showAllFiles ? (
908
+ <div className="solid-module-package-tree">
909
+ {(previewResponse?.preview?.fileTree ?? []).map((entry: string) => (
910
+ <div key={entry} className="solid-module-package-tree__row">
911
+ {entry}
912
+ </div>
913
+ ))}
914
+ </div>
915
+ ) : null}
717
916
  </>
718
917
  )}
719
918
  </div>
@@ -727,14 +926,31 @@ export function ModulePackageImportContent({ onClose, onImported, initialTransac
727
926
  type="button"
728
927
  size="small"
729
928
  onClick={() => setSelectedStep("import")}
730
- disabled={!canContinueFromPreview}
929
+ disabled={!canContinueFromPreview || !isUpdateApproved}
731
930
  rightIcon={<Sparkles size={14} />}
732
931
  >
733
- Continue to import
932
+ {isUpdate ? "Continue to update" : "Continue to import"}
734
933
  </SolidButton>
735
934
  );
736
935
  }
737
936
 
937
+ const restoreButton = backup && !isRestoreConfirmOpen ? (
938
+ <SolidButton
939
+ type="button"
940
+ size="small"
941
+ variant="ghost"
942
+ onClick={() => setRestoreConfirmOpen(true)}
943
+ disabled={!canRestore}
944
+ leftIcon={<RotateCcw size={14} />}
945
+ >
946
+ Restore {formatVersionLabel(backup.version)}
947
+ </SolidButton>
948
+ ) : null;
949
+
950
+ if (activeStep === "import" && isRestoreInterrupted) {
951
+ return restoreButton;
952
+ }
953
+
738
954
  if (activeStep === "import") {
739
955
  return (
740
956
  <SolidButton
@@ -745,38 +961,44 @@ export function ModulePackageImportContent({ onClose, onImported, initialTransac
745
961
  loading={isProcessing && steps.import.status === "running"}
746
962
  leftIcon={<Sparkles size={14} />}
747
963
  >
748
- Import Module
964
+ {isUpdate ? `Update to ${formatVersionLabel(incomingVersion)}` : "Import Module"}
749
965
  </SolidButton>
750
966
  );
751
967
  }
752
968
 
753
969
  if (activeStep === "build") {
754
970
  return (
755
- <SolidButton
756
- type="button"
757
- size="small"
758
- variant="outline"
759
- onClick={handleRunBuild}
760
- disabled={!canRunBuild}
761
- loading={isProcessing && steps.build.status === "running"}
762
- >
763
- {steps.build.status === "success" || steps.build.status === "warning" ? "Build Again" : "Build Now"}
764
- </SolidButton>
971
+ <>
972
+ {restoreButton}
973
+ <SolidButton
974
+ type="button"
975
+ size="small"
976
+ variant="outline"
977
+ onClick={handleRunBuild}
978
+ disabled={!canRunBuild}
979
+ loading={isProcessing && steps.build.status === "running"}
980
+ >
981
+ {steps.build.status === "success" || steps.build.status === "warning" ? "Build Again" : "Build Now"}
982
+ </SolidButton>
983
+ </>
765
984
  );
766
985
  }
767
986
 
768
987
  if (activeStep === "seed") {
769
988
  return (
770
- <SolidButton
771
- type="button"
772
- size="small"
773
- variant="outline"
774
- onClick={handleRunSeed}
775
- disabled={!canRunSeed}
776
- loading={isProcessing && steps.seed.status === "running"}
777
- >
778
- {steps.seed.status === "success" || steps.seed.status === "warning" ? "Seed Again" : "Seed"}
779
- </SolidButton>
989
+ <>
990
+ {restoreButton}
991
+ <SolidButton
992
+ type="button"
993
+ size="small"
994
+ variant="outline"
995
+ onClick={handleRunSeed}
996
+ disabled={!canRunSeed}
997
+ loading={isProcessing && steps.seed.status === "running"}
998
+ >
999
+ {steps.seed.status === "success" || steps.seed.status === "warning" ? "Seed Again" : "Seed"}
1000
+ </SolidButton>
1001
+ </>
780
1002
  );
781
1003
  }
782
1004
 
@@ -788,28 +1010,82 @@ export function ModulePackageImportContent({ onClose, onImported, initialTransac
788
1010
  return renderPreviewContent();
789
1011
  }
790
1012
 
1013
+ const restoreConfirm = isRestoreConfirmOpen && backup ? (
1014
+ <div className="solid-module-package-notice is-danger">
1015
+ <div className="solid-module-package-notice__title">Restore {formatVersionLabel(backup.version)}?</div>
1016
+ <ul className="solid-module-package-inline-list">
1017
+ <li>This puts back the {packageModuleName} folders from before this update ({formatVersionLabel(backup.version)}).</li>
1018
+ <li>When the API restarts, the database schema is synced to that code: columns added by the newer version are dropped along with their data, and tables it added are left in place.</li>
1019
+ <li>Back up the database first if this module has data you care about.</li>
1020
+ <li>After the restart, refresh this page and run build and seed again. Prune is turned on for that seed.</li>
1021
+ </ul>
1022
+ <div className="solid-module-package-notice__actions">
1023
+ <SolidButton type="button" size="small" variant="ghost" onClick={() => setRestoreConfirmOpen(false)} disabled={isProcessing}>
1024
+ Cancel
1025
+ </SolidButton>
1026
+ <SolidButton
1027
+ type="button"
1028
+ size="small"
1029
+ variant="destructive"
1030
+ onClick={handleRestore}
1031
+ disabled={!canRestore}
1032
+ loading={isProcessing}
1033
+ leftIcon={<RotateCcw size={14} />}
1034
+ >
1035
+ Restore {formatVersionLabel(backup.version)}
1036
+ </SolidButton>
1037
+ </div>
1038
+ </div>
1039
+ ) : null;
1040
+
791
1041
  if (activeStep === "import") {
792
1042
  return (
793
1043
  <div className="solid-module-package-step-stack">
794
1044
  <div className="solid-module-package-notice is-warning">
795
- <div className="solid-module-package-notice__title">What happens after import</div>
1045
+ <div className="solid-module-package-notice__title">{isUpdate ? "What happens during the update" : "What happens after import"}</div>
796
1046
  <ul className="solid-module-package-inline-list">
1047
+ {isUpdate ? (
1048
+ <>
1049
+ <li>The current {packageModuleName} folders in solid-api and solid-ui are backed up, then replaced with {formatVersionLabel(incomingVersion)}. Files the new version no longer has are removed.</li>
1050
+ <li>Local changes made inside these folders are kept only in the backup.</li>
1051
+ </>
1052
+ ) : null}
797
1053
  <li>Depending on the files in this archive, both the UI and API services may restart immediately.</li>
798
1054
  <li>This popup may get dismissed as part of that restart.</li>
799
1055
  <li>Wait for both services to come back, then refresh this module list page to continue with build and seed.</li>
800
1056
  </ul>
801
1057
  </div>
1058
+ {restoreConfirm}
802
1059
  {steps.import.output ? <pre className="solid-module-package-step__output">{steps.import.output}</pre> : null}
803
1060
  </div>
804
1061
  );
805
1062
  }
806
1063
 
807
- if (activeStep === "build" && steps.build.output) {
808
- return <pre className="solid-module-package-step__output">{steps.build.output}</pre>;
1064
+ if (activeStep === "build") {
1065
+ if (!restoreConfirm && !steps.build.output) return null;
1066
+ return (
1067
+ <div className="solid-module-package-step-stack">
1068
+ {restoreConfirm}
1069
+ {steps.build.output ? <pre className="solid-module-package-step__output">{steps.build.output}</pre> : null}
1070
+ </div>
1071
+ );
809
1072
  }
810
1073
 
811
- if (activeStep === "seed" && steps.seed.output) {
812
- return <pre className="solid-module-package-step__output">{steps.seed.output}</pre>;
1074
+ if (activeStep === "seed") {
1075
+ return (
1076
+ <div className="solid-module-package-step-stack">
1077
+ {restoreConfirm}
1078
+ {isUpdate ? (
1079
+ <SolidCheckbox
1080
+ checked={pruneOnSeed}
1081
+ onChange={(event) => setPruneOnSeed(event.currentTarget.checked)}
1082
+ disabled={isProcessing}
1083
+ label="Remove metadata this module no longer defines (prune). This also removes permissions for endpoints that no longer exist."
1084
+ />
1085
+ ) : null}
1086
+ {steps.seed.output ? <pre className="solid-module-package-step__output">{steps.seed.output}</pre> : null}
1087
+ </div>
1088
+ );
813
1089
  }
814
1090
 
815
1091
  return null;
@@ -819,9 +1095,13 @@ export function ModulePackageImportContent({ onClose, onImported, initialTransac
819
1095
  <>
820
1096
  <SolidDialogHeader className="solid-filter-dialog-head solid-module-package-dialog__header">
821
1097
  <div>
822
- <SolidDialogTitle className="solid-filter-dialog-title m-0">Import Module Package</SolidDialogTitle>
1098
+ <SolidDialogTitle className="solid-filter-dialog-title m-0">
1099
+ {isUpdate ? `Update ${targetModuleName || packageModuleName}` : "Import Module Package"}
1100
+ </SolidDialogTitle>
823
1101
  <SolidDialogDescription className="solid-filter-dialog-subtitle m-0">
824
- Upload a `.sldx` archive, let SolidX preview it automatically, then move through import, build, and seed.
1102
+ {isUpdate
1103
+ ? "Upload a `.sldx` archive of the new version, review the version change and the files that will change, then move through update, build, and seed."
1104
+ : "Upload a `.sldx` archive, let SolidX preview it automatically, then move through import, build, and seed."}
825
1105
  </SolidDialogDescription>
826
1106
  </div>
827
1107
  {!isProcessing ? (