@powerhousedao/connect 6.2.2-dev.9 → 6.2.2-staging.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 (108) hide show
  1. package/assets/document-icon-192x192.png +0 -0
  2. package/assets/document-icon-512x512.png +0 -0
  3. package/assets/pwa-192x192.png +0 -0
  4. package/assets/pwa-512x512-maskable.png +0 -0
  5. package/assets/pwa-512x512.png +0 -0
  6. package/dist/{AddDriveModal-MMHlkJBJ.js → AddDriveModal-Q-Ffh5XA.js} +5 -5
  7. package/dist/AddDriveModal-Q-Ffh5XA.js.map +1 -0
  8. package/dist/{ClearStorageModal-BrqS8I-J.js → ClearStorageModal-DXV7eveG.js} +4 -4
  9. package/dist/{ClearStorageModal-BrqS8I-J.js.map → ClearStorageModal-DXV7eveG.js.map} +1 -1
  10. package/dist/ConfirmDocumentUpgradeModal-FUOhZsk7.js +114 -0
  11. package/dist/ConfirmDocumentUpgradeModal-FUOhZsk7.js.map +1 -0
  12. package/dist/{CookiesPolicyModal-CkcUKPLb.js → CookiesPolicyModal-BpyPPyxS.js} +3 -3
  13. package/dist/{CookiesPolicyModal-CkcUKPLb.js.map → CookiesPolicyModal-BpyPPyxS.js.map} +1 -1
  14. package/dist/{CreateDocumentModal-CvsTh0HM.js → CreateDocumentModal-C45W8J0V.js} +7 -5
  15. package/dist/CreateDocumentModal-C45W8J0V.js.map +1 -0
  16. package/dist/{DebugSettingsModal-CCZi5Gmx.js → DebugSettingsModal-D-8XfrLE.js} +6 -6
  17. package/dist/{DebugSettingsModal-CCZi5Gmx.js.map → DebugSettingsModal-D-8XfrLE.js.map} +1 -1
  18. package/dist/{DeleteDriveModal-DSTK8FYk.js → DeleteDriveModal-Dug84t1j.js} +4 -4
  19. package/dist/{DeleteDriveModal-DSTK8FYk.js.map → DeleteDriveModal-Dug84t1j.js.map} +1 -1
  20. package/dist/{DeleteItemModal-B2nJmjsP.js → DeleteItemModal-mMzy3V8o.js} +4 -4
  21. package/dist/{DeleteItemModal-B2nJmjsP.js.map → DeleteItemModal-mMzy3V8o.js.map} +1 -1
  22. package/dist/{DisclaimerModal-D5FO34u9.js → DisclaimerModal-DvpD5dPM.js} +3 -3
  23. package/dist/{DisclaimerModal-D5FO34u9.js.map → DisclaimerModal-DvpD5dPM.js.map} +1 -1
  24. package/dist/DocumentVersionUnsupportedModal-C3Q0V75l.js +32 -0
  25. package/dist/DocumentVersionUnsupportedModal-C3Q0V75l.js.map +1 -0
  26. package/dist/{DownloadDocumentWithErrorsModal-tEr3bbtN.js → DownloadDocumentWithErrorsModal-IKl0ljQb.js} +3 -3
  27. package/dist/{DownloadDocumentWithErrorsModal-tEr3bbtN.js.map → DownloadDocumentWithErrorsModal-IKl0ljQb.js.map} +1 -1
  28. package/dist/DriveAuthRequiredModal-Cw9eCAQ_.js +36 -0
  29. package/dist/DriveAuthRequiredModal-Cw9eCAQ_.js.map +1 -0
  30. package/dist/{DriveSettingsModal-kR3hQnh4.js → DriveSettingsModal-C6-mRQiv.js} +3 -3
  31. package/dist/{DriveSettingsModal-kR3hQnh4.js.map → DriveSettingsModal-C6-mRQiv.js.map} +1 -1
  32. package/dist/{InspectorModal-BfwRytsl.js → InspectorModal-Fnp57gG_.js} +6 -6
  33. package/dist/{InspectorModal-BfwRytsl.js.map → InspectorModal-Fnp57gG_.js.map} +1 -1
  34. package/dist/LoginModal-CybbOm3N.js +75 -0
  35. package/dist/LoginModal-CybbOm3N.js.map +1 -0
  36. package/dist/{MissingPackageModal-BQcCQbOn.js → MissingPackageModal-C7usWVHQ.js} +18 -6
  37. package/dist/MissingPackageModal-C7usWVHQ.js.map +1 -0
  38. package/dist/OpenFileDocumentsModal-Bp2SFfGY.js +311 -0
  39. package/dist/OpenFileDocumentsModal-Bp2SFfGY.js.map +1 -0
  40. package/dist/SettingsModal-BKyrRL4W.js +568 -0
  41. package/dist/SettingsModal-BKyrRL4W.js.map +1 -0
  42. package/dist/{UpgradeDriveModal-BZGSE9_r.js → UpgradeDriveModal-_8t9slxQ.js} +3 -3
  43. package/dist/{UpgradeDriveModal-BZGSE9_r.js.map → UpgradeDriveModal-_8t9slxQ.js.map} +1 -1
  44. package/dist/app-loader-1ACi7O7G.js +4 -0
  45. package/dist/{app-loader-BqH5S4gP.js → app-loader-9oxJxBSC.js} +839 -560
  46. package/dist/app-loader-9oxJxBSC.js.map +1 -0
  47. package/dist/build-info-Ck0vFY36.js +28 -0
  48. package/dist/build-info-Ck0vFY36.js.map +1 -0
  49. package/dist/components-C5E86_ow.js +4 -0
  50. package/dist/{connect.config-vgUeFoNp.js → connect.config-DaHDEUrw.js} +13 -7
  51. package/dist/connect.config-DaHDEUrw.js.map +1 -0
  52. package/dist/{connection-state-DO_2lAEJ.js → connection-state-3w4liC1X.js} +4 -4
  53. package/dist/{connection-state-DO_2lAEJ.js.map → connection-state-3w4liC1X.js.map} +1 -1
  54. package/dist/cookie-banner-BH6bIq24.js +70 -0
  55. package/dist/cookie-banner-BH6bIq24.js.map +1 -0
  56. package/dist/{reload-connect-toast-D1Iqw9E_.js → i18n-CWO0vYVd.js} +23 -30
  57. package/dist/i18n-CWO0vYVd.js.map +1 -0
  58. package/dist/load-CvwtlhbX.js +131 -0
  59. package/dist/load-CvwtlhbX.js.map +1 -0
  60. package/dist/main.js +1 -1
  61. package/dist/{pglite-seed-z3B1JA38.js → pglite-seed-BqAUvNFw.js} +4 -4
  62. package/dist/{pglite-seed-z3B1JA38.js.map → pglite-seed-BqAUvNFw.js.map} +1 -1
  63. package/dist/preferences-store-_5Cw4_YV.js +104 -0
  64. package/dist/preferences-store-_5Cw4_YV.js.map +1 -0
  65. package/dist/pwa-manifest-link-DIgOSo2Q.js +34 -0
  66. package/dist/pwa-manifest-link-DIgOSo2Q.js.map +1 -0
  67. package/dist/{reactor-ynguOpe7.js → reactor-CrKlyk9q.js} +240 -67
  68. package/dist/reactor-CrKlyk9q.js.map +1 -0
  69. package/dist/reactor.worker.js +11 -6
  70. package/dist/reactor.worker.js.map +1 -1
  71. package/dist/{registerServiceWorker-nJIc0yEY.js → registerServiceWorker-BSzbNfvz.js} +6 -4
  72. package/dist/registerServiceWorker-BSzbNfvz.js.map +1 -0
  73. package/dist/sentry-QXKgQmr2.js +15 -0
  74. package/dist/sentry-QXKgQmr2.js.map +1 -0
  75. package/dist/start-connect.js +1 -1
  76. package/dist/style.css +243 -7
  77. package/dist/{toast-4t-VimvD.js → toast-BXIGd4Eb.js} +8 -5
  78. package/dist/toast-BXIGd4Eb.js.map +1 -0
  79. package/dist/use-renown-login-Cf3va6u_.js +52 -0
  80. package/dist/use-renown-login-Cf3va6u_.js.map +1 -0
  81. package/dist/useRegistryPackages-DMhCvTWq.js +314 -0
  82. package/dist/useRegistryPackages-DMhCvTWq.js.map +1 -0
  83. package/package.json +19 -13
  84. package/public/powerhouse.config.json +3 -0
  85. package/style.css +1 -0
  86. package/dist/AddDriveModal-MMHlkJBJ.js.map +0 -1
  87. package/dist/CreateDocumentModal-CvsTh0HM.js.map +0 -1
  88. package/dist/DriveAuthRequiredModal-BA2vS3oF.js +0 -40
  89. package/dist/DriveAuthRequiredModal-BA2vS3oF.js.map +0 -1
  90. package/dist/MissingPackageModal-BQcCQbOn.js.map +0 -1
  91. package/dist/SettingsModal-CdoFxyJJ.js +0 -289
  92. package/dist/SettingsModal-CdoFxyJJ.js.map +0 -1
  93. package/dist/app-loader-BqH5S4gP.js.map +0 -1
  94. package/dist/app-loader-J8Z4QAD5.js +0 -4
  95. package/dist/build-info-AUWI7jOw.js +0 -18
  96. package/dist/build-info-AUWI7jOw.js.map +0 -1
  97. package/dist/components-B6-PFL8z.js +0 -5
  98. package/dist/connect.config-vgUeFoNp.js.map +0 -1
  99. package/dist/load-BucYuvxH.js +0 -100
  100. package/dist/load-BucYuvxH.js.map +0 -1
  101. package/dist/package-BcKQ9BGX.js +0 -127
  102. package/dist/package-BcKQ9BGX.js.map +0 -1
  103. package/dist/reactor-ynguOpe7.js.map +0 -1
  104. package/dist/registerServiceWorker-nJIc0yEY.js.map +0 -1
  105. package/dist/reload-connect-toast-D1Iqw9E_.js.map +0 -1
  106. package/dist/toast-4t-VimvD.js.map +0 -1
  107. package/dist/useRegistryPackages-BaYtekjQ.js +0 -179
  108. package/dist/useRegistryPackages-BaYtekjQ.js.map +0 -1
@@ -1,26 +1,34 @@
1
1
 
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="90216f1f-1ac5-5c02-806a-095895df569c")}catch(e){}}();
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="e2bf7b80-893e-5f57-b5a9-3f7fe300e8d6")}catch(e){}}();
3
3
  import { n as getRuntimeConfig } from "./runtime-config-DL3QjcfM.js";
4
4
  import { c as idbError } from "./pglite-major-B39aOEiE.js";
5
5
  import { n as cloneFileData, r as migrateIdb } from "./pglite-migrate-core-Boxqqx-c.js";
6
- import { i as i18n, n as setSentryModule } from "./reload-connect-toast-D1Iqw9E_.js";
7
- import { a as defaultPHDocumentEditorConfig, i as defaultPHAppConfig, r as connectConfig } from "./connect.config-vgUeFoNp.js";
8
- import { f as PRIMARY_IDB_NAMES, r as subscribeWorkerConnection, s as getCachedMigrationMajor, t as getWorkerConnectionStatus, u as subscribeMigrationMajor } from "./connection-state-DO_2lAEJ.js";
9
- import { n as serviceWorkerManager, r as subscribeServiceWorkerUpdate, t as getServiceWorkerUpdateAvailable } from "./registerServiceWorker-nJIc0yEY.js";
10
- import { n as toast, t as ToastContainer } from "./toast-4t-VimvD.js";
6
+ import { a as defaultPHDocumentEditorConfig, i as defaultPHAppConfig, r as connectConfig } from "./connect.config-DaHDEUrw.js";
7
+ import { f as PRIMARY_IDB_NAMES, r as subscribeWorkerConnection, s as getCachedMigrationMajor, t as getWorkerConnectionStatus, u as subscribeMigrationMajor } from "./connection-state-3w4liC1X.js";
8
+ import { n as serviceWorkerManager, r as subscribeServiceWorkerUpdate, t as getServiceWorkerUpdateAvailable } from "./registerServiceWorker-BSzbNfvz.js";
9
+ import { t as setSentryModule } from "./sentry-QXKgQmr2.js";
10
+ import { i as useIsEmbedded, r as getIsEmbedded, t as useAcceptedCookies } from "./preferences-store-_5Cw4_YV.js";
11
+ import { n as dismissToast, r as toast, t as ToastContainer } from "./toast-BXIGd4Eb.js";
12
+ import { t as useOpenRenownLogin } from "./use-renown-login-Cf3va6u_.js";
11
13
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
12
- import { Trans, useTranslation } from "react-i18next";
13
- import React, { StrictMode, Suspense, lazy, useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
14
- import { DriveCollectionId, getRevisionFromDate, initTheme, logout, openRenown, setPHAppConfig, setPHDocumentEditorConfig, setRevisionHistoryVisible, setSelectedDrive, setSelectedNode, showPHModal, useAppModuleById, useConnectionStates, useDefaultAppModule, useDocumentById, useDocumentModelModuleById, useDocumentOperations, useDrives, useEditorModuleById, useEditorPreloader, useFallbackEditorModule, useIsAddDriveEnabled, useNodeParentFolderById, usePHModal, usePackageDiscoveryService, useReactorClientModule, useRevisionHistoryVisible, useSelectedDocument, useSelectedDocumentId, useSelectedDriveSafe, useSelectedFolder, useSelectedTimelineItem, useSyncList, useTheme, useUser, useVetraPackageManager, useVetraPackages } from "@powerhousedao/reactor-browser";
14
+ import { useTranslation } from "react-i18next";
15
+ import React, { StrictMode, Suspense, createElement, lazy, useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
16
+ import { DriveCollectionId, RenownWalletProvider, addFileWithProgress, getRevisionFromDate, initTheme, logout, setPHAppConfig, setPHDocumentEditorConfig, setRevisionHistoryVisible, setSelectedDrive, setSelectedNode, showPHModal, useAppModuleById, useConnectionStates, useDefaultAppModule, useDocumentById, useDocumentModelModuleById, useDocumentOperations, useDocumentVersionStatus, useDrives, useEditorModuleById, useEditorPreloader, useFallbackEditorModule, useIsAddDriveEnabled, useNodeParentFolderById, usePHModal, usePackageDiscoveryService, useReactorClientModule, useRevisionHistoryVisible, useSelectedDocument, useSelectedDocumentId, useSelectedDriveSafe, useSelectedFolder, useSelectedTimelineItem, useSyncList, useTheme, useUser, useVetraPackageManager, useVetraPackages } from "@powerhousedao/reactor-browser";
15
17
  import { childLogger } from "document-model";
16
- import { ConnectSidebar, ConnectTooltipProvider, CookieBanner, DefaultEditorLoader, DriveAuthGate, DropZoneWrapper, HomeScreen, HomeScreenAddDriveItem, HomeScreenItem, LogoAnimation, PackageInstallModal, RevisionHistory, SidebarAddDriveItem, SidebarItem, useEns } from "@powerhousedao/design-system/connect";
17
- import { z } from "zod";
18
+ import { ConnectSidebar, ConnectTooltipProvider, DefaultEditorLoader, DriveAuthGate, DropZoneWrapper, HomeScreen, HomeScreenAddDriveItem, HomeScreenItem, LogoAnimation, PackageInstallModal, RevisionHistory, SidebarAddDriveItem, SidebarItem, UploadFileListContainer, formatFileSize, mapProgressStageToStatus, useEns } from "@powerhousedao/design-system/connect";
18
19
  import { Outlet, RouterProvider, createBrowserRouter, createRoutesFromChildren, matchRoutes, useLocation, useNavigationType } from "react-router-dom";
19
20
  import { useHotkeys } from "react-hotkeys-hook";
20
21
  import { Icon, Modal, PowerhouseButton } from "@powerhousedao/design-system";
21
22
  import { ErrorBoundary } from "react-error-boundary";
23
+ import { z } from "zod";
22
24
  import { GenericDriveExplorer } from "@powerhousedao/powerhouse-vetra-packages/editors";
25
+ import { createReactorMcpProvider } from "@powerhousedao/reactor-mcp/tools";
26
+ import { ReactorChatFab, createSwitchboardSchemaTool } from "@powerhousedao/reactor-browser/ai";
23
27
  import { twMerge } from "tailwind-merge";
28
+ import { mockAdapter } from "@renown/sdk/wallet/mock";
29
+ import { privyAdapter } from "@renown/sdk/wallet/privy";
30
+ import { rainbowAdapter } from "@renown/sdk/wallet/rainbow";
31
+ import { arbitrum, base, mainnet, optimism, polygon, sepolia } from "wagmi/chains";
24
32
  import { redo, undo } from "@powerhousedao/shared/document-model";
25
33
  //#region src/utils/browser.ts
26
34
  function getBasePath() {
@@ -75,20 +83,198 @@ var DocumentEditorDebugTools = class {
75
83
  }
76
84
  };
77
85
  //#endregion
86
+ //#region src/utils/launch-queue.ts
87
+ let pendingImportFiles = [];
88
+ const pendingListeners = /* @__PURE__ */ new Set();
89
+ function notifyPendingListeners() {
90
+ for (const listener of pendingListeners) listener();
91
+ }
92
+ function getPendingImportFiles() {
93
+ return pendingImportFiles;
94
+ }
95
+ /** Stable identity of a pending file (also the picker's pre-check cache key). */
96
+ function pendingFileKey(file) {
97
+ return `${file.name}|${file.size}|${file.lastModified}`;
98
+ }
99
+ function addPendingImportFiles(files) {
100
+ const seen = new Set(pendingImportFiles.map(pendingFileKey));
101
+ const fresh = files.filter((file) => {
102
+ const key = pendingFileKey(file);
103
+ if (seen.has(key)) return false;
104
+ seen.add(key);
105
+ return true;
106
+ });
107
+ if (fresh.length === 0) return;
108
+ pendingImportFiles = [...pendingImportFiles, ...fresh];
109
+ notifyPendingListeners();
110
+ }
111
+ function clearPendingImportFiles() {
112
+ if (pendingImportFiles.length === 0) return;
113
+ pendingImportFiles = [];
114
+ notifyPendingListeners();
115
+ }
116
+ function subscribePendingImportFiles(cb) {
117
+ pendingListeners.add(cb);
118
+ return () => {
119
+ pendingListeners.delete(cb);
120
+ };
121
+ }
122
+ async function handleLaunchParams(params) {
123
+ const handles = params.files.filter((handle) => handle.kind === "file");
124
+ if (handles.length === 0) return;
125
+ const files = [];
126
+ for (const handle of handles) try {
127
+ files.push(await handle.getFile());
128
+ } catch (error) {
129
+ console.error("Failed to read launched file:", handle.name, error);
130
+ }
131
+ if (files.length === 0) return;
132
+ addPendingImportFiles(files);
133
+ showPHModal({ type: "openFileDocuments" });
134
+ }
135
+ function maybeReopenImportModal() {
136
+ if (getPendingImportFiles().length === 0) return;
137
+ if (window.ph?.modal !== void 0) return;
138
+ showPHModal({ type: "openFileDocuments" });
139
+ }
140
+ let initialized = false;
141
+ /**
142
+ * Register the launchQueue consumer that receives files opened with Connect
143
+ * through the OS file association. Idempotent (safe under StrictMode double
144
+ * effects) and a no-op where the File Handling API doesn't exist — Firefox,
145
+ * Safari, and any context without an installed PWA manifest simply never
146
+ * deliver launches.
147
+ */
148
+ function initLaunchQueueFileHandling() {
149
+ if (initialized) return;
150
+ initialized = true;
151
+ window.addEventListener("ph:modalUpdated", maybeReopenImportModal);
152
+ window.launchQueue?.setConsumer((params) => {
153
+ handleLaunchParams(params);
154
+ });
155
+ }
156
+ //#endregion
157
+ //#region src/utils/open-file-uploads.ts
158
+ let uploads = [];
159
+ const listeners$1 = /* @__PURE__ */ new Set();
160
+ function notifyUploadListeners() {
161
+ for (const listener of listeners$1) listener();
162
+ }
163
+ function getOpenFileUploads() {
164
+ return uploads;
165
+ }
166
+ function subscribeOpenFileUploads(cb) {
167
+ listeners$1.add(cb);
168
+ return () => {
169
+ listeners$1.delete(cb);
170
+ };
171
+ }
172
+ function removeOpenFileUpload(id) {
173
+ const next = uploads.filter((upload) => upload.id !== id);
174
+ if (next.length === uploads.length) return;
175
+ uploads = next;
176
+ notifyUploadListeners();
177
+ }
178
+ function clearOpenFileUploads() {
179
+ if (uploads.length === 0) return;
180
+ uploads = [];
181
+ notifyUploadListeners();
182
+ }
183
+ function addOpenFileUploads(rows) {
184
+ uploads = [...uploads, ...rows];
185
+ notifyUploadListeners();
186
+ }
187
+ function patchOpenFileUpload(id, patch) {
188
+ uploads = uploads.map((upload) => upload.id === id ? {
189
+ ...upload,
190
+ ...patch
191
+ } : upload);
192
+ notifyUploadListeners();
193
+ }
194
+ let uploadCounter = 0;
195
+ let importChain = Promise.resolve();
196
+ /**
197
+ * Import one file and resolve once the flow truly ends. Completion is
198
+ * tracked through the progress stages, NOT the returned promise — the
199
+ * package-discovery retry path resolves early and keeps reporting through
200
+ * the callback. `resolveConflict: "duplicate"` is always passed: duplicates
201
+ * were resolved in the picker (rename), so the conflict stage must never
202
+ * fire — this also covers a duplicate appearing between pre-check and import.
203
+ */
204
+ function importOne(row, driveId, documentTypes) {
205
+ return new Promise((resolve) => {
206
+ let settled = false;
207
+ const settle = (fileNode) => {
208
+ if (settled) return;
209
+ settled = true;
210
+ resolve(fileNode);
211
+ };
212
+ addFileWithProgress(row.file, driveId, row.name, void 0, (progress) => {
213
+ patchOpenFileUpload(row.id, {
214
+ status: mapProgressStageToStatus(progress.stage),
215
+ progress: progress.progress,
216
+ ...progress.error ? { errorDetails: progress.error } : {},
217
+ ...progress.documentType ? { documentType: progress.documentType } : {},
218
+ ...progress.fileNode ? { fileNode: progress.fileNode } : {}
219
+ });
220
+ switch (progress.stage) {
221
+ case "complete":
222
+ settle(progress.fileNode);
223
+ break;
224
+ case "failed":
225
+ case "unsupported-document-type":
226
+ settle(void 0);
227
+ break;
228
+ default: break;
229
+ }
230
+ }, documentTypes, "duplicate").catch(() => {
231
+ settle(void 0);
232
+ });
233
+ });
234
+ }
235
+ /**
236
+ * Run a confirmed batch of imports into a drive, feeding the floating upload
237
+ * panel. Rows appear immediately (queued), files import sequentially, and a
238
+ * single-file batch that succeeds opens its document in the editor.
239
+ */
240
+ function runOpenFileImports(args) {
241
+ const { driveId, imports, documentTypes } = args;
242
+ if (imports.length === 0) return importChain;
243
+ const rows = imports.map(({ file, name }) => ({
244
+ id: `open-file-${++uploadCounter}`,
245
+ file,
246
+ name
247
+ }));
248
+ addOpenFileUploads(rows.map((row) => ({
249
+ id: row.id,
250
+ fileName: row.name,
251
+ fileSize: formatFileSize(row.file.size),
252
+ status: "pending",
253
+ progress: 0
254
+ })));
255
+ const run = async () => {
256
+ const results = [];
257
+ for (const row of rows) results.push(await importOne(row, driveId, documentTypes));
258
+ if (rows.length === 1 && results[0]) setSelectedNode(results[0]);
259
+ };
260
+ importChain = importChain.then(run, run);
261
+ return importChain;
262
+ }
263
+ //#endregion
78
264
  //#region src/components/migration-status.ts
79
265
  let current = null;
80
- const listeners$2 = /* @__PURE__ */ new Set();
266
+ const listeners = /* @__PURE__ */ new Set();
81
267
  function setMigrationStatus(status) {
82
268
  current = status;
83
- for (const l of listeners$2) l();
269
+ for (const l of listeners) l();
84
270
  }
85
271
  function getMigrationStatus() {
86
272
  return current;
87
273
  }
88
274
  function subscribeMigrationStatus(cb) {
89
- listeners$2.add(cb);
275
+ listeners.add(cb);
90
276
  return () => {
91
- listeners$2.delete(cb);
277
+ listeners.delete(cb);
92
278
  };
93
279
  }
94
280
  //#endregion
@@ -193,325 +379,19 @@ async function migrateAllLocked() {
193
379
  phase
194
380
  }), backup);
195
381
  } finally {
196
- setMigrationStatus(null);
197
- }
198
- }
199
- async function migrateAllIfNeeded() {
200
- janitor().catch((err) => console.warn("PGlite backup janitor failed", err));
201
- if (!(typeof navigator !== "undefined" && "locks" in navigator && typeof navigator.locks.request === "function")) {
202
- await migrateAllLocked();
203
- return;
204
- }
205
- await navigator.locks.request("ph-pglite-migration", { mode: "exclusive" }, async () => {
206
- await migrateAllLocked();
207
- });
208
- }
209
- //#endregion
210
- //#region src/hooks/useAcceptedCookies.ts
211
- const ACCEPTED_COOKIES_KEY_STORAGE = `${connectConfig.routerBasename}:acceptedCookies`;
212
- const listeners$1 = /* @__PURE__ */ new Set();
213
- let cookies = getInitial$1();
214
- function getInitial$1() {
215
- try {
216
- const value = localStorage.getItem(ACCEPTED_COOKIES_KEY_STORAGE);
217
- return value ? JSON.parse(value) : {
218
- analytics: false,
219
- marketing: false,
220
- functional: false
221
- };
222
- } catch {
223
- return {
224
- analytics: false,
225
- marketing: false,
226
- functional: false
227
- };
228
- }
229
- }
230
- function getCookies() {
231
- return cookies;
232
- }
233
- function setCookies(setter) {
234
- cookies = setter(cookies);
235
- localStorage.setItem(ACCEPTED_COOKIES_KEY_STORAGE, JSON.stringify(cookies));
236
- listeners$1.forEach((fn) => fn());
237
- }
238
- function subscribe$1(fn) {
239
- listeners$1.add(fn);
240
- return () => listeners$1.delete(fn);
241
- }
242
- const useAcceptedCookies = () => {
243
- return [useSyncExternalStore(subscribe$1, getCookies, getCookies), setCookies];
244
- };
245
- //#endregion
246
- //#region src/services/openpanel/buffer.ts
247
- const buffer = [];
248
- let errorHandler;
249
- function defaultOnError(err) {
250
- console.warn("[useOpenPanelAnalytics] track failed:", err);
251
- }
252
- /**
253
- * Forward a single event to the active client, swallowing any throw.
254
- *
255
- * The outer try/catch handles synchronous throws from `client.track`; the
256
- * inner `.catch()` handles rejected promises. Both are needed because
257
- * `Promise.resolve(x).catch()` cannot catch a throw from evaluating `x`.
258
- */
259
- function forward(client, name, props) {
260
- try {
261
- Promise.resolve(client.track(name, props)).catch((err) => (errorHandler ?? defaultOnError)(err));
262
- } catch (err) {
263
- (errorHandler ?? defaultOnError)(err);
264
- }
265
- }
266
- /**
267
- * Drain the pre-init buffer in FIFO order through `client.track`, then forward
268
- * subsequent events directly. Called once from `getOpenPanelClient()`.
269
- */
270
- function drainOpenPanelBuffer(client, onError) {
271
- errorHandler = onError;
272
- const pending = buffer.splice(0, buffer.length);
273
- for (const [name, props] of pending) forward(client, name, props);
274
- }
275
- /**
276
- * Clear the buffer and reset the active client. Buffered pre-consent events
277
- * are discarded so they are not replayed if the user later re-accepts.
278
- */
279
- function clearOpenPanelBuffer() {
280
- buffer.length = 0;
281
- errorHandler = void 0;
282
- }
283
- //#endregion
284
- //#region src/services/openpanel/client.ts
285
- let client;
286
- /**
287
- * Returns the cached `OpenPanel` singleton, building it on first call.
288
- *
289
- * When `config.clientId` is empty it returns `undefined` without importing
290
- * `@openpanel/web`, so the bundler can tree-shake the SDK out of production.
291
- */
292
- async function getOpenPanelClient(config) {
293
- if (!config.clientId) return;
294
- if (client) return client;
295
- const { OpenPanel: OpenPanelClass } = await import("@openpanel/web");
296
- client = new OpenPanelClass({
297
- clientId: config.clientId,
298
- ...config.apiUrl ? { apiUrl: config.apiUrl } : {},
299
- trackScreenViews: true,
300
- trackOutgoingLinks: true
301
- });
302
- client.setGlobalProperties({ app: "connect" });
303
- drainOpenPanelBuffer(client);
304
- return client;
305
- }
306
- /**
307
- * Clears the cached singleton and the pre-init buffer. Call on consent
308
- * revocation or during tests; buffered events are discarded, not replayed.
309
- */
310
- function resetOpenPanelClient() {
311
- client = void 0;
312
- clearOpenPanelBuffer();
313
- }
314
- //#endregion
315
- //#region src/services/openpanel/events.json
316
- var events_default = [
317
- {
318
- "documentType": "powerhouse/document-drive",
319
- "actionTypes": [
320
- "ADD_FOLDER",
321
- "DELETE_NODE",
322
- "ADD_FILE",
323
- "UPDATE_FILE",
324
- "COPY_NODE",
325
- "MOVE_NODE",
326
- "SET_DRIVE_NAME",
327
- "SET_DRIVE_ICON"
328
- ]
329
- },
330
- {
331
- "documentType": "powerhouse/document-model",
332
- "actionTypes": [
333
- "SET_MODEL_NAME",
334
- "SET_MODEL_ID",
335
- "ADD_MODULE",
336
- "DELETE_MODULE",
337
- "ADD_OPERATION",
338
- "DELETE_OPERATION"
339
- ]
340
- },
341
- {
342
- "documentType": "powerhouse/reactor-drive",
343
- "actionTypes": [
344
- "ADD_FOLDER",
345
- "REMOVE_FOLDER",
346
- "UPDATE_FOLDER"
347
- ]
348
- }
349
- ];
350
- //#endregion
351
- //#region src/services/openpanel/events.ts
352
- const EventMappingSchema = z.object({
353
- documentType: z.string().min(1),
354
- actionTypes: z.array(z.string().min(1)).min(1),
355
- alias: z.string().optional()
356
- }).strict();
357
- const EventMappingsSchema = z.array(EventMappingSchema).readonly();
358
- /**
359
- * Parses and validates the event mapping table, returning the validated array
360
- * and an O(1) lookup map. Accepts a `raw` override for tests. Throws on zod
361
- * failures and on duplicate `(documentType, actionType)` pairs.
362
- */
363
- function loadEvents(raw = events_default) {
364
- const result = EventMappingsSchema.safeParse(raw);
365
- if (!result.success) throw new Error(`Invalid OpenPanel events.json: ${result.error.message}`);
366
- const mappings = result.data;
367
- const lookupMap = /* @__PURE__ */ new Map();
368
- for (const mapping of mappings) {
369
- let inner = lookupMap.get(mapping.documentType);
370
- if (!inner) {
371
- inner = /* @__PURE__ */ new Map();
372
- lookupMap.set(mapping.documentType, inner);
373
- }
374
- for (const actionType of mapping.actionTypes) {
375
- if (inner.has(actionType)) throw new Error(`Duplicate OpenPanel event mapping for ${mapping.documentType}/${actionType}`);
376
- inner.set(actionType, mapping);
377
- }
378
- }
379
- return {
380
- mappings,
381
- lookupMap
382
- };
383
- }
384
- const { mappings: defaultMappings, lookupMap: defaultLookupMap } = loadEvents();
385
- /** Validated mapping array from the bundled events.json. */
386
- const eventMappings = defaultMappings;
387
- /** O(1) lookup map: documentType → actionType → mapping. */
388
- const eventLookupMap = defaultLookupMap;
389
- /**
390
- * Normalizes a document type into a safe event-name segment: lowercased, `/`
391
- * replaced with `.`, and any character outside `[a-z0-9._-]` stripped.
392
- * e.g. `powerhouse/document-drive` → `powerhouse.document-drive`.
393
- */
394
- function normalize(documentType) {
395
- return documentType.toLowerCase().replace(/\//g, ".").replace(/[^a-z0-9._-]/g, "");
396
- }
397
- /**
398
- * Derives the OpenPanel event name: `mapping.alias` when present, otherwise
399
- * `${normalize(documentType)}.${actionType.toLowerCase()}`.
400
- */
401
- function deriveEventName(mapping, op) {
402
- if (mapping.alias) return mapping.alias;
403
- return `${normalize(op.context.documentType)}.${op.operation.action.type.toLowerCase()}`;
404
- }
405
- /**
406
- * Builds the default properties attached to every OpenPanel event. The `app`
407
- * property is stamped globally by the client, not here.
408
- */
409
- function buildDefaultProperties(op) {
410
- return {
411
- documentType: op.context.documentType,
412
- actionType: op.operation.action.type,
413
- documentId: op.context.documentId,
414
- scope: op.context.scope,
415
- branch: op.context.branch
416
- };
417
- }
418
- //#endregion
419
- //#region src/services/openpanel/processor.ts
420
- /**
421
- * Read-only reactor `IProcessor` that translates matching document operations
422
- * into OpenPanel analytics events. Operations whose `(documentType, actionType)`
423
- * pair is absent from the lookup map are skipped; per-operation errors are
424
- * routed to `onError` and never thrown back to the manager.
425
- */
426
- var OpenPanelProcessor = class {
427
- constructor(client, lookupMap, onError = (err) => console.warn("[OpenPanelProcessor] Error tracking event:", err)) {
428
- this.client = client;
429
- this.lookupMap = lookupMap;
430
- this.onError = onError;
431
- }
432
- async onOperations(operations) {
433
- for (const op of operations) {
434
- const inner = this.lookupMap.get(op.context.documentType);
435
- if (!inner) continue;
436
- const mapping = inner.get(op.operation.action.type);
437
- if (!mapping) continue;
438
- const name = deriveEventName(mapping, op);
439
- const payload = buildDefaultProperties(op);
440
- try {
441
- await this.client.track(name, payload);
442
- } catch (err) {
443
- this.onError(err, op);
444
- }
445
- }
446
- }
447
- onDisconnect() {
448
- this.client.flush?.();
449
- return Promise.resolve();
450
- }
451
- };
452
- //#endregion
453
- //#region src/services/openpanel/factory.ts
454
- /**
455
- * Returns a `ProcessorFactory` that, for each drive, creates a single
456
- * `OpenPanelProcessor` record whose `filter.documentType` is the union of all
457
- * document types present in the event mapping table.
458
- */
459
- function createOpenPanelProcessorFactory(config) {
460
- return function openPanelProcessorFactory(_driveHeader) {
461
- const documentType = Array.from(config.events.lookupMap.keys());
462
- return [{
463
- processor: new OpenPanelProcessor(config.client, config.events.lookupMap, config.onError),
464
- filter: { documentType },
465
- startFrom: config.startFrom ?? "current"
466
- }];
467
- };
468
- }
469
- //#endregion
470
- //#region src/hooks/useIsEmbedded.ts
471
- /**
472
- * Detects whether Connect is being rendered inside another app (e.g. inside
473
- * an iframe in the vetra-cli drive editor). When `?embed=1` is present in the
474
- * URL, chrome that doesn't make sense in the embedded context — the cookie
475
- * banner, the outer sidebar — is suppressed.
476
- */
477
- function getIsEmbedded() {
478
- if (typeof window === "undefined") return false;
479
- try {
480
- const value = new URLSearchParams(window.location.search).get("embed");
481
- return value !== null && value !== "0" && value !== "false";
482
- } catch {
483
- return false;
382
+ setMigrationStatus(null);
484
383
  }
485
384
  }
486
- function useIsEmbedded() {
487
- return getIsEmbedded();
488
- }
489
- const COOKIE_BANNER_KEY_STORAGE = `${connectConfig.routerBasename}:display-cookie-banner`;
490
- const listeners = /* @__PURE__ */ new Set();
491
- let bannerShown = getInitial();
492
- function getInitial() {
493
- if (getIsEmbedded()) return false;
494
- try {
495
- return localStorage.getItem(COOKIE_BANNER_KEY_STORAGE) !== "false";
496
- } catch (error) {
497
- console.error(error);
498
- return true;
385
+ async function migrateAllIfNeeded() {
386
+ janitor().catch((err) => console.warn("PGlite backup janitor failed", err));
387
+ if (!(typeof navigator !== "undefined" && "locks" in navigator && typeof navigator.locks.request === "function")) {
388
+ await migrateAllLocked();
389
+ return;
499
390
  }
391
+ await navigator.locks.request("ph-pglite-migration", { mode: "exclusive" }, async () => {
392
+ await migrateAllLocked();
393
+ });
500
394
  }
501
- function getCookieBannerState() {
502
- return bannerShown;
503
- }
504
- function setCookieBannerState(state) {
505
- bannerShown = state;
506
- localStorage.setItem(COOKIE_BANNER_KEY_STORAGE, JSON.stringify(state));
507
- }
508
- function subscribe(fn) {
509
- listeners.add(fn);
510
- return () => listeners.delete(fn);
511
- }
512
- const useCookieBanner = () => {
513
- return [useSyncExternalStore(subscribe, getCookieBannerState, getCookieBannerState), setCookieBannerState];
514
- };
515
395
  //#endregion
516
396
  //#region src/hooks/useFailedInstallations.ts
517
397
  const emptyArray$1 = [];
@@ -583,6 +463,44 @@ function useInitSentry() {
583
463
  }, [analytics]);
584
464
  }
585
465
  //#endregion
466
+ //#region src/services/openpanel/buffer.ts
467
+ const buffer = [];
468
+ let errorHandler;
469
+ function defaultOnError(err) {
470
+ console.warn("[useOpenPanelAnalytics] track failed:", err);
471
+ }
472
+ /**
473
+ * Forward a single event to the active client, swallowing any throw.
474
+ *
475
+ * The outer try/catch handles synchronous throws from `client.track`; the
476
+ * inner `.catch()` handles rejected promises. Both are needed because
477
+ * `Promise.resolve(x).catch()` cannot catch a throw from evaluating `x`.
478
+ */
479
+ function forward(client, name, props) {
480
+ try {
481
+ Promise.resolve(client.track(name, props)).catch((err) => (errorHandler ?? defaultOnError)(err));
482
+ } catch (err) {
483
+ (errorHandler ?? defaultOnError)(err);
484
+ }
485
+ }
486
+ /**
487
+ * Drain the pre-init buffer in FIFO order through `client.track`, then forward
488
+ * subsequent events directly. Called once from `getOpenPanelClient()`.
489
+ */
490
+ function drainOpenPanelBuffer(client, onError) {
491
+ errorHandler = onError;
492
+ const pending = buffer.splice(0, buffer.length);
493
+ for (const [name, props] of pending) forward(client, name, props);
494
+ }
495
+ /**
496
+ * Clear the buffer and reset the active client. Buffered pre-consent events
497
+ * are discarded so they are not replayed if the user later re-accepts.
498
+ */
499
+ function clearOpenPanelBuffer() {
500
+ buffer.length = 0;
501
+ errorHandler = void 0;
502
+ }
503
+ //#endregion
586
504
  //#region src/hooks/usePendingInstallations.ts
587
505
  const emptyArray = [];
588
506
  function usePendingInstallations() {
@@ -708,7 +626,7 @@ function SilentFallback() {
708
626
  */
709
627
  function TextFallback({ message = "Something went wrong" }) {
710
628
  return /* @__PURE__ */ jsx("div", {
711
- className: "text-center text-foreground",
629
+ className: "text-center wrap-break-word text-foreground",
712
630
  children: message
713
631
  });
714
632
  }
@@ -736,7 +654,7 @@ function DetailedFallback({ error, resetErrorBoundary }) {
736
654
  })]
737
655
  }),
738
656
  /* @__PURE__ */ jsx("p", {
739
- className: "mb-4 text-sm text-foreground",
657
+ className: "mb-4 text-sm wrap-break-word text-foreground",
740
658
  children: errorMessage
741
659
  }),
742
660
  errorDetails !== "{}" && /* @__PURE__ */ jsxs("details", {
@@ -760,108 +678,294 @@ function DetailedFallback({ error, resetErrorBoundary }) {
760
678
  });
761
679
  }
762
680
  /**
763
- * Centered error message fallback component.
764
- * Displays the error message in a centered, full-size container.
765
- * Suitable for use as a custom fallbackRender when you need simple error display.
681
+ * Centered error message fallback component.
682
+ * Displays the error message in a centered, full-size container.
683
+ * Suitable for use as a custom fallbackRender when you need simple error display.
684
+ */
685
+ function CenteredErrorMessage({ error }) {
686
+ return /* @__PURE__ */ jsx("div", {
687
+ className: "flex size-full items-center justify-center p-6",
688
+ children: /* @__PURE__ */ jsx("h3", {
689
+ className: "max-w-lg min-w-0 text-center text-lg font-semibold wrap-break-word text-foreground",
690
+ children: error instanceof Error ? error.message : String(error)
691
+ })
692
+ });
693
+ }
694
+ function getFallbackComponent(variant, fallbackMessage) {
695
+ switch (variant) {
696
+ case "silent": return SilentFallback;
697
+ case "text": return () => /* @__PURE__ */ jsx(TextFallback, { message: fallbackMessage });
698
+ case "detailed": return DetailedFallback;
699
+ default: return CenteredErrorMessage;
700
+ }
701
+ }
702
+ /**
703
+ * Unified error boundary component for catching and handling React errors.
704
+ *
705
+ * Built on top of `react-error-boundary`, this component provides:
706
+ * - Automatic error logging with configurable context
707
+ * - Multiple fallback UI variants (silent, text, detailed)
708
+ * - Support for custom fallback render functions
709
+ * - Reset capability via `resetKeys`
710
+ *
711
+ * @example
712
+ * // Using a predefined variant
713
+ * <ErrorBoundary variant="detailed" loggerContext={["Connect", "Editor"]}>
714
+ * <EditorComponent />
715
+ * </ErrorBoundary>
716
+ *
717
+ * @example
718
+ * // Using a custom message with the text variant
719
+ * <ErrorBoundary variant="text" fallbackMessage="Failed to load drives">
720
+ * <DrivesList />
721
+ * </ErrorBoundary>
722
+ *
723
+ * @example
724
+ * // Using a custom fallback render
725
+ * <ErrorBoundary
726
+ * fallbackRender={CenteredErrorMessage}
727
+ * onError={(error) => trackError(error)}
728
+ * >
729
+ * <MyComponent />
730
+ * </ErrorBoundary>
731
+ *
732
+ * @example
733
+ * // Silent mode for modals (renders null on error)
734
+ * <ErrorBoundary variant="silent" loggerContext={["Connect", "Modals"]}>
735
+ * <ModalContent />
736
+ * </ErrorBoundary>
737
+ */
738
+ function ErrorBoundary$1(props) {
739
+ const { children, onError, resetKeys, loggerContext } = props;
740
+ const handleError = (error, info) => {
741
+ if (error.message.includes("Failed to fetch dynamically imported module") && error.message.includes("node_modules/.vite")) {
742
+ console.error(error);
743
+ console.log("Outdated chunk detected, reloading page...");
744
+ window.location.reload();
745
+ return;
746
+ }
747
+ (loggerContext ? childLogger(loggerContext) : logger$1).error("@error @info", error, info);
748
+ onError?.(error, info);
749
+ };
750
+ return /* @__PURE__ */ jsx(ErrorBoundary, {
751
+ fallbackRender: hasCustomFallback(props) ? props.fallbackRender : getFallbackComponent(props.variant, props.fallbackMessage),
752
+ onError: handleError,
753
+ resetKeys,
754
+ children
755
+ });
756
+ }
757
+ //#endregion
758
+ //#region src/components/analytics.tsx
759
+ function gtag(...args) {
760
+ window.dataLayer?.push(...args);
761
+ }
762
+ const Analytics = () => {
763
+ const gaTrackingId = connectConfig.gaTrackingId;
764
+ const [{ analytics }] = useAcceptedCookies();
765
+ const useAnalytics = gaTrackingId && analytics;
766
+ useEffect(() => {
767
+ if (useAnalytics) {
768
+ const script = document.createElement("script");
769
+ script.src = `https://www.googletagmanager.com/gtag/js?id=${gaTrackingId}`;
770
+ script.async = true;
771
+ document.head.appendChild(script);
772
+ window.dataLayer = window.dataLayer || [];
773
+ gtag("js", /* @__PURE__ */ new Date());
774
+ gtag("config", gaTrackingId);
775
+ return () => {
776
+ document.head.removeChild(script);
777
+ };
778
+ }
779
+ }, [useAnalytics, gaTrackingId]);
780
+ return null;
781
+ };
782
+ //#endregion
783
+ //#region src/services/openpanel/client.ts
784
+ let client;
785
+ /**
786
+ * Returns the cached `OpenPanel` singleton, building it on first call.
787
+ *
788
+ * When `config.clientId` is empty it returns `undefined` without importing
789
+ * `@openpanel/web`, so the bundler can tree-shake the SDK out of production.
790
+ */
791
+ async function getOpenPanelClient(config) {
792
+ if (!config.clientId) return;
793
+ if (client) return client;
794
+ const { OpenPanel: OpenPanelClass } = await import("@openpanel/web");
795
+ client = new OpenPanelClass({
796
+ clientId: config.clientId,
797
+ ...config.apiUrl ? { apiUrl: config.apiUrl } : {},
798
+ trackScreenViews: true,
799
+ trackOutgoingLinks: true
800
+ });
801
+ client.setGlobalProperties({ app: "connect" });
802
+ drainOpenPanelBuffer(client);
803
+ return client;
804
+ }
805
+ /**
806
+ * Clears the cached singleton and the pre-init buffer. Call on consent
807
+ * revocation or during tests; buffered events are discarded, not replayed.
808
+ */
809
+ function resetOpenPanelClient() {
810
+ client = void 0;
811
+ clearOpenPanelBuffer();
812
+ }
813
+ //#endregion
814
+ //#region src/services/openpanel/events.json
815
+ var events_default = [
816
+ {
817
+ "documentType": "powerhouse/document-drive",
818
+ "actionTypes": [
819
+ "ADD_FOLDER",
820
+ "DELETE_NODE",
821
+ "ADD_FILE",
822
+ "UPDATE_FILE",
823
+ "COPY_NODE",
824
+ "MOVE_NODE",
825
+ "SET_DRIVE_NAME",
826
+ "SET_DRIVE_ICON"
827
+ ]
828
+ },
829
+ {
830
+ "documentType": "powerhouse/document-model",
831
+ "actionTypes": [
832
+ "SET_MODEL_NAME",
833
+ "SET_MODEL_ID",
834
+ "ADD_MODULE",
835
+ "DELETE_MODULE",
836
+ "ADD_OPERATION",
837
+ "DELETE_OPERATION"
838
+ ]
839
+ },
840
+ {
841
+ "documentType": "powerhouse/reactor-drive",
842
+ "actionTypes": [
843
+ "ADD_FOLDER",
844
+ "REMOVE_FOLDER",
845
+ "UPDATE_FOLDER"
846
+ ]
847
+ }
848
+ ];
849
+ //#endregion
850
+ //#region src/services/openpanel/events.ts
851
+ const EventMappingSchema = z.object({
852
+ documentType: z.string().min(1),
853
+ actionTypes: z.array(z.string().min(1)).min(1),
854
+ alias: z.string().optional()
855
+ }).strict();
856
+ const EventMappingsSchema = z.array(EventMappingSchema).readonly();
857
+ /**
858
+ * Parses and validates the event mapping table, returning the validated array
859
+ * and an O(1) lookup map. Accepts a `raw` override for tests. Throws on zod
860
+ * failures and on duplicate `(documentType, actionType)` pairs.
861
+ */
862
+ function loadEvents(raw = events_default) {
863
+ const result = EventMappingsSchema.safeParse(raw);
864
+ if (!result.success) throw new Error(`Invalid OpenPanel events.json: ${result.error.message}`);
865
+ const mappings = result.data;
866
+ const lookupMap = /* @__PURE__ */ new Map();
867
+ for (const mapping of mappings) {
868
+ let inner = lookupMap.get(mapping.documentType);
869
+ if (!inner) {
870
+ inner = /* @__PURE__ */ new Map();
871
+ lookupMap.set(mapping.documentType, inner);
872
+ }
873
+ for (const actionType of mapping.actionTypes) {
874
+ if (inner.has(actionType)) throw new Error(`Duplicate OpenPanel event mapping for ${mapping.documentType}/${actionType}`);
875
+ inner.set(actionType, mapping);
876
+ }
877
+ }
878
+ return {
879
+ mappings,
880
+ lookupMap
881
+ };
882
+ }
883
+ const { mappings: defaultMappings, lookupMap: defaultLookupMap } = loadEvents();
884
+ /** Validated mapping array from the bundled events.json. */
885
+ const eventMappings = defaultMappings;
886
+ /** O(1) lookup map: documentType → actionType → mapping. */
887
+ const eventLookupMap = defaultLookupMap;
888
+ /**
889
+ * Normalizes a document type into a safe event-name segment: lowercased, `/`
890
+ * replaced with `.`, and any character outside `[a-z0-9._-]` stripped.
891
+ * e.g. `powerhouse/document-drive` → `powerhouse.document-drive`.
766
892
  */
767
- function CenteredErrorMessage({ error }) {
768
- return /* @__PURE__ */ jsx("div", {
769
- className: "flex size-full items-center justify-center",
770
- children: /* @__PURE__ */ jsx("h3", {
771
- className: "text-lg font-semibold text-foreground",
772
- children: error instanceof Error ? error.message : String(error)
773
- })
774
- });
893
+ function normalize(documentType) {
894
+ return documentType.toLowerCase().replace(/\//g, ".").replace(/[^a-z0-9._-]/g, "");
775
895
  }
776
- function getFallbackComponent(variant, fallbackMessage) {
777
- switch (variant) {
778
- case "silent": return SilentFallback;
779
- case "text": return () => /* @__PURE__ */ jsx(TextFallback, { message: fallbackMessage });
780
- case "detailed": return DetailedFallback;
781
- default: return CenteredErrorMessage;
782
- }
896
+ /**
897
+ * Derives the OpenPanel event name: `mapping.alias` when present, otherwise
898
+ * `${normalize(documentType)}.${actionType.toLowerCase()}`.
899
+ */
900
+ function deriveEventName(mapping, op) {
901
+ if (mapping.alias) return mapping.alias;
902
+ return `${normalize(op.context.documentType)}.${op.operation.action.type.toLowerCase()}`;
783
903
  }
784
904
  /**
785
- * Unified error boundary component for catching and handling React errors.
786
- *
787
- * Built on top of `react-error-boundary`, this component provides:
788
- * - Automatic error logging with configurable context
789
- * - Multiple fallback UI variants (silent, text, detailed)
790
- * - Support for custom fallback render functions
791
- * - Reset capability via `resetKeys`
792
- *
793
- * @example
794
- * // Using a predefined variant
795
- * <ErrorBoundary variant="detailed" loggerContext={["Connect", "Editor"]}>
796
- * <EditorComponent />
797
- * </ErrorBoundary>
798
- *
799
- * @example
800
- * // Using a custom message with the text variant
801
- * <ErrorBoundary variant="text" fallbackMessage="Failed to load drives">
802
- * <DrivesList />
803
- * </ErrorBoundary>
804
- *
805
- * @example
806
- * // Using a custom fallback render
807
- * <ErrorBoundary
808
- * fallbackRender={CenteredErrorMessage}
809
- * onError={(error) => trackError(error)}
810
- * >
811
- * <MyComponent />
812
- * </ErrorBoundary>
813
- *
814
- * @example
815
- * // Silent mode for modals (renders null on error)
816
- * <ErrorBoundary variant="silent" loggerContext={["Connect", "Modals"]}>
817
- * <ModalContent />
818
- * </ErrorBoundary>
905
+ * Builds the default properties attached to every OpenPanel event. The `app`
906
+ * property is stamped globally by the client, not here.
819
907
  */
820
- function ErrorBoundary$1(props) {
821
- const { children, onError, resetKeys, loggerContext } = props;
822
- const handleError = (error, info) => {
823
- if (error.message.includes("Failed to fetch dynamically imported module") && error.message.includes("node_modules/.vite")) {
824
- console.error(error);
825
- console.log("Outdated chunk detected, reloading page...");
826
- window.location.reload();
827
- return;
828
- }
829
- (loggerContext ? childLogger(loggerContext) : logger$1).error("@error @info", error, info);
830
- onError?.(error, info);
908
+ function buildDefaultProperties(op) {
909
+ return {
910
+ documentType: op.context.documentType,
911
+ actionType: op.operation.action.type,
912
+ documentId: op.context.documentId,
913
+ scope: op.context.scope,
914
+ branch: op.context.branch
831
915
  };
832
- return /* @__PURE__ */ jsx(ErrorBoundary, {
833
- fallbackRender: hasCustomFallback(props) ? props.fallbackRender : getFallbackComponent(props.variant, props.fallbackMessage),
834
- onError: handleError,
835
- resetKeys,
836
- children
837
- });
838
916
  }
839
917
  //#endregion
840
- //#region src/components/analytics.tsx
841
- function gtag(...args) {
842
- window.dataLayer?.push(...args);
843
- }
844
- const Analytics = () => {
845
- const gaTrackingId = connectConfig.gaTrackingId;
846
- const [{ analytics }] = useAcceptedCookies();
847
- const useAnalytics = gaTrackingId && analytics;
848
- useEffect(() => {
849
- if (useAnalytics) {
850
- const script = document.createElement("script");
851
- script.src = `https://www.googletagmanager.com/gtag/js?id=${gaTrackingId}`;
852
- script.async = true;
853
- document.head.appendChild(script);
854
- window.dataLayer = window.dataLayer || [];
855
- gtag("js", /* @__PURE__ */ new Date());
856
- gtag("config", gaTrackingId);
857
- return () => {
858
- document.head.removeChild(script);
859
- };
918
+ //#region src/services/openpanel/processor.ts
919
+ /**
920
+ * Read-only reactor `IProcessor` that translates matching document operations
921
+ * into OpenPanel analytics events. Operations whose `(documentType, actionType)`
922
+ * pair is absent from the lookup map are skipped; per-operation errors are
923
+ * routed to `onError` and never thrown back to the manager.
924
+ */
925
+ var OpenPanelProcessor = class {
926
+ constructor(client, lookupMap, onError = (err) => console.warn("[OpenPanelProcessor] Error tracking event:", err)) {
927
+ this.client = client;
928
+ this.lookupMap = lookupMap;
929
+ this.onError = onError;
930
+ }
931
+ async onOperations(operations) {
932
+ for (const op of operations) {
933
+ const inner = this.lookupMap.get(op.context.documentType);
934
+ if (!inner) continue;
935
+ const mapping = inner.get(op.operation.action.type);
936
+ if (!mapping) continue;
937
+ const name = deriveEventName(mapping, op);
938
+ const payload = buildDefaultProperties(op);
939
+ try {
940
+ await this.client.track(name, payload);
941
+ } catch (err) {
942
+ this.onError(err, op);
943
+ }
860
944
  }
861
- }, [useAnalytics, gaTrackingId]);
862
- return null;
945
+ }
946
+ onDisconnect() {
947
+ this.client.flush?.();
948
+ return Promise.resolve();
949
+ }
863
950
  };
864
951
  //#endregion
952
+ //#region src/services/openpanel/factory.ts
953
+ /**
954
+ * Returns a `ProcessorFactory` that, for each drive, creates a single
955
+ * `OpenPanelProcessor` record whose `filter.documentType` is the union of all
956
+ * document types present in the event mapping table.
957
+ */
958
+ function createOpenPanelProcessorFactory(config) {
959
+ return function openPanelProcessorFactory(_driveHeader) {
960
+ const documentType = Array.from(config.events.lookupMap.keys());
961
+ return [{
962
+ processor: new OpenPanelProcessor(config.client, config.events.lookupMap, config.onError),
963
+ filter: { documentType },
964
+ startFrom: config.startFrom ?? "current"
965
+ }];
966
+ };
967
+ }
968
+ //#endregion
865
969
  //#region src/components/openpanel-traits.ts
866
970
  /**
867
971
  * Builds the OpenPanel identity traits from a renown User.
@@ -1092,6 +1196,46 @@ const SentryProvider = ({ children }) => {
1092
1196
  return children;
1093
1197
  };
1094
1198
  //#endregion
1199
+ //#region src/components/reactor-chat.tsx
1200
+ /**
1201
+ * Resolves the reactor tool descriptors for the chat agent from the
1202
+ * browser's live reactor client. Built lazily at send time so the reactor
1203
+ * (and its SharedWorker) do not need to be ready at render time.
1204
+ */
1205
+ function getReactorTools() {
1206
+ const ph = window.ph;
1207
+ const client = ph?.reactorClient;
1208
+ if (!client) return Promise.reject(/* @__PURE__ */ new Error("The reactor client is not available yet. Try again shortly."));
1209
+ const syncManager = ph?.reactorClientModule?.reactorModule?.syncModule?.syncManager;
1210
+ return createReactorMcpProvider({
1211
+ client,
1212
+ syncManager
1213
+ }).then((provider) => {
1214
+ const builtInTools = Object.values(provider.tools).map((tool) => ({
1215
+ ...tool,
1216
+ inputSchema: tool.inputSchema ?? {}
1217
+ }));
1218
+ const taken = new Set(builtInTools.map((tool) => tool.name));
1219
+ const packageTools = (ph?.vetraPackageManager?.packages ?? []).flatMap((pkg) => (pkg.aiTools ?? []).filter((tool) => {
1220
+ if (taken.has(tool.name)) {
1221
+ console.warn(`[Connect][AI] Package tool "${tool.name}" shadows an existing tool and was ignored.`);
1222
+ return false;
1223
+ }
1224
+ taken.add(tool.name);
1225
+ return true;
1226
+ }));
1227
+ return [
1228
+ ...builtInTools,
1229
+ createSwitchboardSchemaTool(),
1230
+ ...packageTools
1231
+ ];
1232
+ });
1233
+ }
1234
+ /** Connect's bottom-right AI chat FAB, bound to the browser reactor. */
1235
+ function ConnectReactorChatFab() {
1236
+ return /* @__PURE__ */ jsx(ReactorChatFab, { getTools: getReactorTools });
1237
+ }
1238
+ //#endregion
1095
1239
  //#region src/components/missing-model-banner.tsx
1096
1240
  const reasonLabels = {
1097
1241
  "no-registry": "No package registry is configured.",
@@ -1106,16 +1250,38 @@ function canRetry(reason) {
1106
1250
  }
1107
1251
  function MissingModelBanner() {
1108
1252
  const failed = useFailedInstallations();
1253
+ const discoveryService = usePackageDiscoveryService();
1109
1254
  const [open, setOpen] = useState(false);
1255
+ const [dismissed, setDismissed] = useState(false);
1256
+ useEffect(() => {
1257
+ return discoveryService?.subscribeEvents((event) => {
1258
+ if (event.type === "load-failed") setDismissed(false);
1259
+ });
1260
+ }, [discoveryService]);
1110
1261
  if (failed.length === 0) return null;
1111
- return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("div", {
1112
- className: "flex items-center justify-between gap-3 bg-warning/10 px-4 py-2 text-sm text-warning",
1113
- children: [/* @__PURE__ */ jsx("span", { children: failed.length === 1 ? "1 document type couldn't load (missing model)." : `${failed.length} document types couldn't load (missing models).` }), /* @__PURE__ */ jsx("button", {
1114
- type: "button",
1115
- onClick: () => setOpen(true),
1116
- className: "rounded-md border border-warning bg-background px-3 py-1 text-warning hover:hover-effect",
1117
- children: "View"
1118
- })]
1262
+ return /* @__PURE__ */ jsxs(Fragment, { children: [dismissed ? null : /* @__PURE__ */ jsx("div", {
1263
+ className: "absolute inset-x-0 top-0 z-200 bg-background",
1264
+ children: /* @__PURE__ */ jsxs("div", {
1265
+ className: "flex items-center justify-between gap-3 bg-warning/10 px-4 py-2 text-sm text-warning",
1266
+ children: [/* @__PURE__ */ jsx("span", { children: failed.length === 1 ? "1 document type couldn't load (missing model)." : `${failed.length} document types couldn't load (missing models).` }), /* @__PURE__ */ jsxs("div", {
1267
+ className: "flex items-center gap-2",
1268
+ children: [/* @__PURE__ */ jsx("button", {
1269
+ type: "button",
1270
+ onClick: () => setOpen(true),
1271
+ className: "rounded-md border border-warning bg-background px-3 py-1 text-warning hover:hover-effect",
1272
+ children: "View"
1273
+ }), /* @__PURE__ */ jsx("button", {
1274
+ type: "button",
1275
+ onClick: () => setDismissed(true),
1276
+ "aria-label": "Dismiss",
1277
+ className: "flex items-center text-warning hover:hover-effect",
1278
+ children: /* @__PURE__ */ jsx(Icon, {
1279
+ name: "XmarkLight",
1280
+ size: 16
1281
+ })
1282
+ })]
1283
+ })]
1284
+ })
1119
1285
  }), /* @__PURE__ */ jsx(MissingModelDetailsModal, {
1120
1286
  open,
1121
1287
  failed,
@@ -1218,6 +1384,18 @@ function MissingModelDetailsModal(props) {
1218
1384
  });
1219
1385
  }
1220
1386
  //#endregion
1387
+ //#region src/components/open-file-upload-list.tsx
1388
+ function OpenFileUploadList() {
1389
+ const uploads = useSyncExternalStore(subscribeOpenFileUploads, getOpenFileUploads);
1390
+ return /* @__PURE__ */ jsx(UploadFileListContainer, {
1391
+ uploadsArray: uploads,
1392
+ uploadsCount: uploads.length,
1393
+ removeUpload: removeOpenFileUpload,
1394
+ clearAllUploads: clearOpenFileUploads,
1395
+ setSelectedNode
1396
+ });
1397
+ }
1398
+ //#endregion
1221
1399
  //#region src/components/package-install-prompt.tsx
1222
1400
  function PackageInstallPrompt() {
1223
1401
  const pendingInstallations = usePendingInstallations();
@@ -1233,6 +1411,88 @@ function PackageInstallPrompt() {
1233
1411
  });
1234
1412
  }
1235
1413
  //#endregion
1414
+ //#region src/components/renown-adapter-descriptors.ts
1415
+ function describe(id, build) {
1416
+ try {
1417
+ return [build()];
1418
+ } catch (error) {
1419
+ console.error(`[connect] Ignoring the "${id}" wallet adapter: connect.renown.adapters.${id} was rejected.`, error);
1420
+ return [];
1421
+ }
1422
+ }
1423
+ const CHAINS_BY_ID = new Map([
1424
+ mainnet,
1425
+ sepolia,
1426
+ polygon,
1427
+ optimism,
1428
+ arbitrum,
1429
+ base
1430
+ ].map((chain) => [chain.id, chain]));
1431
+ function chainsFor(chainId) {
1432
+ if (chainId === void 0) return void 0;
1433
+ const chain = CHAINS_BY_ID.get(chainId);
1434
+ if (!chain) {
1435
+ console.error(`[connect] connect.renown.chainId ${chainId} is not a chain Connect can offer, so in-page sign-in will be rejected. Supported: ${[...CHAINS_BY_ID.keys()].join(", ")}.`);
1436
+ return;
1437
+ }
1438
+ return [chain];
1439
+ }
1440
+ /** Map Connect's declarative `connect.renown.adapters` config to the descriptors `RenownWalletProvider` takes. Array order sets the order of the login buttons. A rejected adapter config is dropped (logged), leaving the others working. `chainId` is `connect.renown.chainId`, the chain Renown issues credentials on. */
1441
+ function configToDescriptors(config, chainId) {
1442
+ if (!config) return [];
1443
+ const { rainbow, privy, mock } = config;
1444
+ const descriptors = [];
1445
+ const chains = chainsFor(chainId);
1446
+ if (rainbow) descriptors.push(...describe("rainbow", () => rainbowAdapter({
1447
+ ...rainbow,
1448
+ ...chains ? { chains } : {}
1449
+ })));
1450
+ if (privy) {
1451
+ const { appId, clientId, methods } = privy;
1452
+ if (appId) descriptors.push(...describe("privy", () => privyAdapter({
1453
+ appId,
1454
+ clientId,
1455
+ methods,
1456
+ chain: chains?.[0]
1457
+ })));
1458
+ }
1459
+ if (mock) descriptors.push(...describe("mock", () => mockAdapter({
1460
+ ...mock,
1461
+ methods: mock.methods
1462
+ })));
1463
+ return descriptors;
1464
+ }
1465
+ //#endregion
1466
+ //#region src/components/renown-wallet-providers.tsx
1467
+ function resolveColor(expr, fallback) {
1468
+ if (typeof document === "undefined") return fallback;
1469
+ const probe = document.createElement("span");
1470
+ probe.style.color = fallback;
1471
+ probe.style.color = expr;
1472
+ probe.style.display = "none";
1473
+ document.body.appendChild(probe);
1474
+ const resolved = getComputedStyle(probe).color;
1475
+ probe.remove();
1476
+ return resolved || fallback;
1477
+ }
1478
+ function RenownWalletProviders({ children }) {
1479
+ const config = getRuntimeConfig().connect.renown?.adapters;
1480
+ const chainId = getRuntimeConfig().connect.renown?.chainId;
1481
+ const { theme } = useTheme();
1482
+ const adapters = useMemo(() => configToDescriptors(config, chainId), [config, chainId]);
1483
+ const walletTheme = useMemo(() => adapters.length > 0 ? {
1484
+ mode: theme,
1485
+ accentColor: resolveColor("var(--primary)", "#0084ff"),
1486
+ accentColorForeground: resolveColor("var(--primary-foreground)", "#ffffff")
1487
+ } : void 0, [theme, adapters]);
1488
+ if (adapters.length === 0) return /* @__PURE__ */ jsx(Fragment, { children });
1489
+ return /* @__PURE__ */ jsx(RenownWalletProvider, {
1490
+ adapters,
1491
+ theme: walletTheme,
1492
+ children
1493
+ });
1494
+ }
1495
+ //#endregion
1236
1496
  //#region src/components/app.tsx
1237
1497
  const App = () => {
1238
1498
  useTheme();
@@ -1258,72 +1518,19 @@ const App = () => {
1258
1518
  else if (connectConfig.offline) serviceWorkerManager.registerServiceWorker(false);
1259
1519
  else serviceWorkerManager.unregisterAll();
1260
1520
  }, []);
1261
- return /* @__PURE__ */ jsxs(SentryProvider, { children: [
1521
+ useEffect(() => {
1522
+ initLaunchQueueFileHandling();
1523
+ }, []);
1524
+ return /* @__PURE__ */ jsx(SentryProvider, { children: /* @__PURE__ */ jsxs(RenownWalletProviders, { children: [
1262
1525
  /* @__PURE__ */ jsx(ToastContainer, { position: "bottom-right" }),
1526
+ connectConfig.aiAssistantEnabled && /* @__PURE__ */ jsx(ConnectReactorChatFab, {}),
1263
1527
  /* @__PURE__ */ jsx(MissingModelBanner, {}),
1264
1528
  /* @__PURE__ */ jsx(Router, {}),
1529
+ /* @__PURE__ */ jsx(OpenFileUploadList, {}),
1265
1530
  /* @__PURE__ */ jsx(PackageInstallPrompt, {}),
1266
1531
  /* @__PURE__ */ jsx(Analytics, {}),
1267
1532
  /* @__PURE__ */ jsx(OpenPanel, {})
1268
- ] });
1269
- };
1270
- //#endregion
1271
- //#region src/components/cookie-banner.tsx
1272
- const isCookieAccepted = (cookies, id) => {
1273
- return cookies.some((cookie) => cookie.id === id && cookie.value);
1274
- };
1275
- const CookieBanner$1 = () => {
1276
- const { t } = useTranslation(void 0, {
1277
- useSuspense: true,
1278
- i18n
1279
- });
1280
- const [showBanner, setShowBanner] = useCookieBanner();
1281
- const [, setAcceptedCookies] = useAcceptedCookies();
1282
- const cookiesInput = [{
1283
- id: "analytics-cookie",
1284
- label: t("cookieBanner.cookies.analytics"),
1285
- value: true
1286
- }];
1287
- const handleAccept = (cookies) => {
1288
- setShowBanner(false);
1289
- if (isCookieAccepted(cookies, "analytics-cookie")) setAcceptedCookies((acceptedCookies) => ({
1290
- ...acceptedCookies,
1291
- analytics: true
1292
- }));
1293
- };
1294
- const handleReject = () => {
1295
- setShowBanner(false);
1296
- setAcceptedCookies(() => ({
1297
- analytics: false,
1298
- functional: false,
1299
- marketing: false
1300
- }));
1301
- };
1302
- if (!showBanner) return null;
1303
- return /* @__PURE__ */ jsxs("div", {
1304
- className: "absolute inset-0 z-10000 backdrop-blur-sm",
1305
- children: [/* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-primary opacity-15" }), /* @__PURE__ */ jsx("div", {
1306
- className: "absolute inset-x-0 bottom-0 flex justify-center bg-background px-10 pt-10 pb-16 shadow-lg",
1307
- children: /* @__PURE__ */ jsx(CookieBanner, {
1308
- className: "max-w-[1024px]",
1309
- cookies: cookiesInput,
1310
- onSubmit: handleAccept,
1311
- onReject: handleReject,
1312
- submitLabel: t("cookieBanner.accept"),
1313
- rejectLabel: t("cookieBanner.reject"),
1314
- children: /* @__PURE__ */ jsx("p", {
1315
- className: "font-semibold text-muted-foreground",
1316
- children: /* @__PURE__ */ jsx(Trans, {
1317
- i18nKey: "cookieBanner.message",
1318
- components: { a: /* @__PURE__ */ jsx("a", {
1319
- onClick: () => showPHModal({ type: "cookiesPolicy" }),
1320
- className: "cursor-pointer text-foreground hover:underline"
1321
- }, "cookieBanner.message-link") }
1322
- }, "cookieBanner.message")
1323
- })
1324
- })
1325
- })]
1326
- });
1533
+ ] }) });
1327
1534
  };
1328
1535
  //#endregion
1329
1536
  //#region src/components/drive-icon.tsx
@@ -1350,16 +1557,56 @@ function DriveIcon({ drive }) {
1350
1557
  });
1351
1558
  }
1352
1559
  //#endregion
1560
+ //#region src/components/document-upgrade-toast.tsx
1561
+ const DocumentUpgradeToast = ({ documentId }) => {
1562
+ const { t } = useTranslation();
1563
+ return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("p", {
1564
+ className: "font-medium",
1565
+ children: t("notifications.documentUpgradeAvailable")
1566
+ }), /* @__PURE__ */ jsx("button", {
1567
+ onClick: () => {
1568
+ showPHModal({
1569
+ type: "confirmDocumentUpgrade",
1570
+ documentId
1571
+ });
1572
+ },
1573
+ className: "underline decoration-solid underline-offset-2",
1574
+ children: t("common.updateDocument")
1575
+ })] });
1576
+ };
1577
+ //#endregion
1353
1578
  //#region src/components/editors.tsx
1354
- function EditorError({ message }) {
1579
+ function EditorError({ title, children }) {
1355
1580
  return /* @__PURE__ */ jsx("div", {
1356
- className: "flex size-full items-center justify-center",
1357
- children: /* @__PURE__ */ jsx("h3", {
1358
- className: "text-lg font-semibold",
1359
- children: message
1581
+ className: "flex size-full items-center justify-center p-6",
1582
+ children: /* @__PURE__ */ jsxs("div", {
1583
+ className: "w-full max-w-lg rounded-lg border border-border bg-card p-6 text-foreground shadow-sm",
1584
+ children: [/* @__PURE__ */ jsxs("div", {
1585
+ className: "mb-3 flex items-center gap-2",
1586
+ children: [/* @__PURE__ */ jsx(Icon, {
1587
+ name: "Error",
1588
+ className: "size-5 shrink-0 text-destructive"
1589
+ }), /* @__PURE__ */ jsx("h3", {
1590
+ className: "min-w-0 text-lg font-semibold wrap-break-word text-foreground",
1591
+ children: title
1592
+ })]
1593
+ }), /* @__PURE__ */ jsx("div", {
1594
+ className: "wrap-break-word",
1595
+ children
1596
+ })]
1360
1597
  })
1361
1598
  });
1362
1599
  }
1600
+ function OpenPackageManagerButton() {
1601
+ return /* @__PURE__ */ jsx(PowerhouseButton, {
1602
+ className: "px-3 py-1.5 text-base font-medium",
1603
+ onClick: () => {
1604
+ showPHModal({ type: "settings" });
1605
+ },
1606
+ type: "button",
1607
+ children: "Open package manager"
1608
+ });
1609
+ }
1363
1610
  const DocumentEditor = (props) => {
1364
1611
  const { document: initialDocument } = props;
1365
1612
  const selectedTimelineItem = useSelectedTimelineItem();
@@ -1379,6 +1626,21 @@ const DocumentEditor = (props) => {
1379
1626
  const preferredEditorModule = useEditorModuleById(preferredEditor);
1380
1627
  const fallbackEditorModule = useFallbackEditorModule(documentType);
1381
1628
  const editorModule = preferredEditorModule ?? fallbackEditorModule;
1629
+ const versionStatus = useDocumentVersionStatus(document ?? void 0);
1630
+ const versionStatusKind = versionStatus?.kind;
1631
+ const upgradeAvailable = versionStatus?.kind === "upgrade-available" && versionStatus.canUpgrade;
1632
+ useEffect(() => {
1633
+ if (upgradeAvailable && documentId) toast(createElement(DocumentUpgradeToast, { documentId }), {
1634
+ type: "connect-warning",
1635
+ toastId: `outdated-document-${documentId}`,
1636
+ autoClose: false
1637
+ });
1638
+ else if (versionStatusKind && versionStatusKind !== "upgrade-available" && documentId) dismissToast(`outdated-document-${documentId}`);
1639
+ }, [
1640
+ upgradeAvailable,
1641
+ versionStatusKind,
1642
+ documentId
1643
+ ]);
1382
1644
  const vetraPackages = useVetraPackages();
1383
1645
  const packageManager = useVetraPackageManager();
1384
1646
  const owningPackageName = editorModule ? vetraPackages.find((pkg) => pkg.editors.includes(editorModule))?.manifest.name : void 0;
@@ -1414,46 +1676,49 @@ const DocumentEditor = (props) => {
1414
1676
  };
1415
1677
  if (isLoadingEditor) return /* @__PURE__ */ jsx(EditorLoader, { message: "Loading editor" });
1416
1678
  if (isLoadingDocument) return /* @__PURE__ */ jsx(EditorLoader, { message: "Loading document" });
1417
- if (!documentModelModule) return /* @__PURE__ */ jsx(EditorError, { message: /* @__PURE__ */ jsxs("div", {
1418
- className: "text-center leading-10",
1419
- children: [/* @__PURE__ */ jsxs("p", { children: [
1420
- "Unable to open the document because the document model \"",
1421
- documentType,
1422
- "\" is not supported."
1423
- ] }), /* @__PURE__ */ jsxs("p", { children: [
1424
- "Go to the",
1425
- " ",
1426
- /* @__PURE__ */ jsx("button", {
1427
- type: "button",
1428
- className: "cursor-pointer underline",
1429
- onClick: () => {
1430
- showPHModal({ type: "settings" });
1431
- },
1432
- children: "package manager"
1433
- }),
1434
- " ",
1435
- "to install this document model"
1436
- ] })]
1437
- }) });
1438
- if (!editorModule) return /* @__PURE__ */ jsx(EditorError, { message: /* @__PURE__ */ jsxs("div", {
1439
- className: "text-center leading-10",
1440
- children: [/* @__PURE__ */ jsx("p", { children: "Unable to open the document because no editor has been found" }), /* @__PURE__ */ jsxs("p", { children: [
1441
- "Go to the",
1442
- " ",
1443
- /* @__PURE__ */ jsx("button", {
1444
- type: "button",
1445
- className: "cursor-pointer underline",
1446
- onClick: () => {
1447
- showPHModal({ type: "settings" });
1448
- },
1449
- children: "package manager"
1450
- }),
1451
- " ",
1452
- "an editor for the \"$",
1453
- documentType,
1454
- "\" document type"
1455
- ] })]
1456
- }) });
1679
+ if (!documentModelModule) return /* @__PURE__ */ jsxs(EditorError, {
1680
+ title: "Document type not supported",
1681
+ children: [/* @__PURE__ */ jsxs("p", {
1682
+ className: "mb-4 text-sm text-foreground",
1683
+ children: [
1684
+ "This document can't be opened because the \"",
1685
+ documentType,
1686
+ "\" document model isn't installed. Install the package that provides it to open the document."
1687
+ ]
1688
+ }), /* @__PURE__ */ jsx(OpenPackageManagerButton, {})]
1689
+ });
1690
+ if (versionStatus?.kind === "unsupported") return /* @__PURE__ */ jsxs(EditorError, {
1691
+ title: "Document version not supported",
1692
+ children: [/* @__PURE__ */ jsxs("p", {
1693
+ className: "mb-4 text-sm text-foreground",
1694
+ children: [
1695
+ "This document requires version ",
1696
+ versionStatus.documentVersion,
1697
+ " of the \"",
1698
+ documentType,
1699
+ "\" document model, but only version",
1700
+ versionStatus.availableVersions.length > 1 ? "s" : "",
1701
+ " ",
1702
+ versionStatus.availableVersions.join(", "),
1703
+ " ",
1704
+ versionStatus.availableVersions.length > 1 ? "are" : "is",
1705
+ " installed. Update the package that provides \"",
1706
+ documentType,
1707
+ "\" to open it."
1708
+ ]
1709
+ }), /* @__PURE__ */ jsx(OpenPackageManagerButton, {})]
1710
+ });
1711
+ if (!editorModule) return /* @__PURE__ */ jsxs(EditorError, {
1712
+ title: "No editor available",
1713
+ children: [/* @__PURE__ */ jsxs("p", {
1714
+ className: "mb-4 text-sm text-foreground",
1715
+ children: [
1716
+ "The \"",
1717
+ documentType,
1718
+ "\" document model is installed, but no editor for it was found. Install a package that provides an editor for this document type."
1719
+ ]
1720
+ }), /* @__PURE__ */ jsx(OpenPackageManagerButton, {})]
1721
+ });
1457
1722
  const EditorComponent = editorModule.Component;
1458
1723
  return /* @__PURE__ */ jsx("div", {
1459
1724
  className: "relative h-full",
@@ -1532,6 +1797,7 @@ function useDriveAuthGate() {
1532
1797
  //#region src/pages/content.tsx
1533
1798
  function Content() {
1534
1799
  const { gate } = useDriveAuthGate();
1800
+ const openLogin = useOpenRenownLogin();
1535
1801
  const [selectedDrive] = useSelectedDriveSafe();
1536
1802
  const selectedFolder = useSelectedFolder();
1537
1803
  const selectedDocumentId = useSelectedDocumentId();
@@ -1545,7 +1811,7 @@ function Content() {
1545
1811
  className: "flex h-full items-center justify-center p-4",
1546
1812
  children: /* @__PURE__ */ jsx(DriveAuthGate, {
1547
1813
  mode: gate,
1548
- onLogin: openRenown,
1814
+ onLogin: openLogin,
1549
1815
  onLogout: () => void logout()
1550
1816
  })
1551
1817
  }) : !selectedDocumentId && !selectedDrive && !selectedFolder ? /* @__PURE__ */ jsx(HomeScreenContainer, {}) : /* @__PURE__ */ jsx(AppContainer, {}) });
@@ -1613,6 +1879,7 @@ const Router = () => {
1613
1879
  //#region src/components/sidebar.tsx
1614
1880
  function Sidebar() {
1615
1881
  const user = useUser();
1882
+ const openLogin = useOpenRenownLogin();
1616
1883
  const drives = useDrives();
1617
1884
  const isAddDriveEnabled = useIsAddDriveEnabled();
1618
1885
  const [selectedDrive] = useSelectedDriveSafe();
@@ -1632,7 +1899,7 @@ function Sidebar() {
1632
1899
  onClick: () => setSelectedDrive(void 0),
1633
1900
  onClickSettings,
1634
1901
  address: user?.address,
1635
- onLogin: openRenown,
1902
+ onLogin: openLogin,
1636
1903
  onDisconnect: () => {
1637
1904
  logout();
1638
1905
  },
@@ -1789,18 +2056,20 @@ const MigrationBanner = () => {
1789
2056
  };
1790
2057
  //#endregion
1791
2058
  //#region src/components/modal/modals-container.tsx
1792
- const AddDriveModal$1 = lazy(() => import("./AddDriveModal-MMHlkJBJ.js").then((m) => ({ default: m.AddDriveModal })));
1793
- const ClearStorageModal = lazy(() => import("./ClearStorageModal-BrqS8I-J.js").then((m) => ({ default: m.ClearStorageModal })));
1794
- const CookiesPolicyModal = lazy(() => import("./CookiesPolicyModal-CkcUKPLb.js").then((m) => ({ default: m.CookiesPolicyModal })));
1795
- const CreateDocumentModal$1 = lazy(() => import("./CreateDocumentModal-CvsTh0HM.js").then((m) => ({ default: m.CreateDocumentModal })));
1796
- const DebugSettingsModal = lazy(() => import("./DebugSettingsModal-CCZi5Gmx.js").then((m) => ({ default: m.DebugSettingsModal })));
1797
- const DeleteDriveModal = lazy(() => import("./DeleteDriveModal-DSTK8FYk.js").then((m) => ({ default: m.DeleteDriveModal })));
1798
- const DeleteItemModal = lazy(() => import("./DeleteItemModal-B2nJmjsP.js").then((m) => ({ default: m.DeleteItemModal })));
1799
- const DisclaimerModal = lazy(() => import("./DisclaimerModal-D5FO34u9.js").then((m) => ({ default: m.DisclaimerModal })));
1800
- const DriveSettingsModal$1 = lazy(() => import("./DriveSettingsModal-kR3hQnh4.js").then((m) => ({ default: m.DriveSettingsModal })));
1801
- const DownloadDocumentWithErrorsModal = lazy(() => import("./DownloadDocumentWithErrorsModal-tEr3bbtN.js").then((m) => ({ default: m.DownloadDocumentWithErrorsModal })));
1802
- const SettingsModal$1 = lazy(() => import("./SettingsModal-CdoFxyJJ.js").then((m) => ({ default: m.SettingsModal })));
1803
- const UpgradeDriveModal = lazy(() => import("./UpgradeDriveModal-BZGSE9_r.js").then((m) => ({ default: m.UpgradeDriveModal })));
2059
+ const AddDriveModal$1 = lazy(() => import("./AddDriveModal-Q-Ffh5XA.js").then((m) => ({ default: m.AddDriveModal })));
2060
+ const ClearStorageModal = lazy(() => import("./ClearStorageModal-DXV7eveG.js").then((m) => ({ default: m.ClearStorageModal })));
2061
+ const CookiesPolicyModal = lazy(() => import("./CookiesPolicyModal-BpyPPyxS.js").then((m) => ({ default: m.CookiesPolicyModal })));
2062
+ const CreateDocumentModal$1 = lazy(() => import("./CreateDocumentModal-C45W8J0V.js").then((m) => ({ default: m.CreateDocumentModal })));
2063
+ const DebugSettingsModal = lazy(() => import("./DebugSettingsModal-D-8XfrLE.js").then((m) => ({ default: m.DebugSettingsModal })));
2064
+ const DeleteDriveModal = lazy(() => import("./DeleteDriveModal-Dug84t1j.js").then((m) => ({ default: m.DeleteDriveModal })));
2065
+ const DeleteItemModal = lazy(() => import("./DeleteItemModal-mMzy3V8o.js").then((m) => ({ default: m.DeleteItemModal })));
2066
+ const DisclaimerModal = lazy(() => import("./DisclaimerModal-DvpD5dPM.js").then((m) => ({ default: m.DisclaimerModal })));
2067
+ const DriveSettingsModal$1 = lazy(() => import("./DriveSettingsModal-C6-mRQiv.js").then((m) => ({ default: m.DriveSettingsModal })));
2068
+ const DownloadDocumentWithErrorsModal = lazy(() => import("./DownloadDocumentWithErrorsModal-IKl0ljQb.js").then((m) => ({ default: m.DownloadDocumentWithErrorsModal })));
2069
+ const DocumentVersionUnsupportedModal = lazy(() => import("./DocumentVersionUnsupportedModal-C3Q0V75l.js").then((m) => ({ default: m.DocumentVersionUnsupportedModal })));
2070
+ const ConfirmDocumentUpgradeModal = lazy(() => import("./ConfirmDocumentUpgradeModal-FUOhZsk7.js").then((m) => ({ default: m.ConfirmDocumentUpgradeModal })));
2071
+ const SettingsModal$1 = lazy(() => import("./SettingsModal-BKyrRL4W.js").then((m) => ({ default: m.SettingsModal })));
2072
+ const UpgradeDriveModal = lazy(() => import("./UpgradeDriveModal-_8t9slxQ.js").then((m) => ({ default: m.UpgradeDriveModal })));
1804
2073
  const modalComponents = {
1805
2074
  addDrive: AddDriveModal$1,
1806
2075
  clearStorage: ClearStorageModal,
@@ -1812,11 +2081,15 @@ const modalComponents = {
1812
2081
  disclaimer: DisclaimerModal,
1813
2082
  driveSettings: DriveSettingsModal$1,
1814
2083
  downloadDocumentWithErrors: DownloadDocumentWithErrorsModal,
1815
- inspector: lazy(() => import("./InspectorModal-BfwRytsl.js").then((m) => ({ default: m.InspectorModal }))),
2084
+ documentVersionUnsupported: DocumentVersionUnsupportedModal,
2085
+ confirmDocumentUpgrade: ConfirmDocumentUpgradeModal,
2086
+ inspector: lazy(() => import("./InspectorModal-Fnp57gG_.js").then((m) => ({ default: m.InspectorModal }))),
1816
2087
  settings: SettingsModal$1,
1817
2088
  upgradeDrive: UpgradeDriveModal,
1818
- missingPackage: lazy(() => import("./MissingPackageModal-BQcCQbOn.js").then((m) => ({ default: m.ConnectMissingPackageModal }))),
1819
- driveAuthRequired: lazy(() => import("./DriveAuthRequiredModal-BA2vS3oF.js").then((m) => ({ default: m.DriveAuthRequiredModal })))
2089
+ missingPackage: lazy(() => import("./MissingPackageModal-C7usWVHQ.js").then((m) => ({ default: m.ConnectMissingPackageModal }))),
2090
+ driveAuthRequired: lazy(() => import("./DriveAuthRequiredModal-Cw9eCAQ_.js").then((m) => ({ default: m.DriveAuthRequiredModal }))),
2091
+ openFileDocuments: lazy(() => import("./OpenFileDocumentsModal-Bp2SFfGY.js").then((m) => ({ default: m.OpenFileDocumentsModal }))),
2092
+ login: lazy(() => import("./LoginModal-CybbOm3N.js").then((m) => ({ default: m.LoginModal })))
1820
2093
  };
1821
2094
  const ModalsContainer = lazy(() => {
1822
2095
  return Promise.resolve({ default: () => {
@@ -1855,8 +2128,9 @@ const ServiceWorkerUpdatePrompt = () => {
1855
2128
  };
1856
2129
  //#endregion
1857
2130
  //#region src/components/app-loader.tsx
2131
+ const CookieBanner$1 = lazy(() => import("./cookie-banner-BH6bIq24.js").then((m) => ({ default: m.CookieBanner })));
1858
2132
  const AppLoader = (props) => {
1859
- const Load = lazy(() => import("./load-BucYuvxH.js").then((m) => m.loadComponent(props.localPackage)));
2133
+ const Load = lazy(() => import("./load-CvwtlhbX.js").then((m) => m.loadComponent(props.localPackage)));
1860
2134
  return /* @__PURE__ */ jsx(StrictMode, { children: /* @__PURE__ */ jsxs(ErrorBoundary$1, {
1861
2135
  fallbackRender: (props) => /* @__PURE__ */ jsx(AppSkeleton, { children: /* @__PURE__ */ jsx(DetailedFallback, { ...props }) }),
1862
2136
  resetKeys: [props.localPackage],
@@ -1870,9 +2144,14 @@ const AppLoader = (props) => {
1870
2144
  children: /* @__PURE__ */ jsx(App, {})
1871
2145
  })
1872
2146
  }),
1873
- /* @__PURE__ */ jsx(Suspense, {
1874
- name: "CookieBanner",
1875
- children: /* @__PURE__ */ jsx(CookieBanner$1, {})
2147
+ /* @__PURE__ */ jsx(ErrorBoundary$1, {
2148
+ variant: "silent",
2149
+ loggerContext: ["Connect", "CookieBanner"],
2150
+ children: /* @__PURE__ */ jsx(Suspense, {
2151
+ fallback: null,
2152
+ name: "CookieBanner",
2153
+ children: /* @__PURE__ */ jsx(CookieBanner$1, {})
2154
+ })
1876
2155
  }),
1877
2156
  /* @__PURE__ */ jsx(Suspense, {
1878
2157
  name: "ModalsContainer",
@@ -1885,7 +2164,7 @@ const AppLoader = (props) => {
1885
2164
  }) });
1886
2165
  };
1887
2166
  //#endregion
1888
- export { DocumentEditor as a, App as c, DocumentEditorContainer as d, OpenPanel as f, Root as i, AppContainer as l, AppSkeleton as m, Sidebar as n, DriveIcon as o, Analytics as p, Router as r, CookieBanner$1 as s, AppLoader as t, EditorLoader as u };
2167
+ export { pendingFileKey as _, DocumentEditor as a, AppContainer as c, OpenPanel as d, Analytics as f, getPendingImportFiles as g, clearPendingImportFiles as h, Root as i, EditorLoader as l, runOpenFileImports as m, Sidebar as n, DriveIcon as o, AppSkeleton as p, Router as r, App as s, AppLoader as t, DocumentEditorContainer as u, subscribePendingImportFiles as v };
1889
2168
 
1890
- //# sourceMappingURL=app-loader-BqH5S4gP.js.map
1891
- //# debugId=90216f1f-1ac5-5c02-806a-095895df569c
2169
+ //# sourceMappingURL=app-loader-9oxJxBSC.js.map
2170
+ //# debugId=e2bf7b80-893e-5f57-b5a9-3f7fe300e8d6