@powerhousedao/connect 6.1.0 → 6.2.0-dev.1

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 (84) hide show
  1. package/.env +11 -49
  2. package/README.md +4 -0
  3. package/dist/{AddDriveModal-Cg2qcYp6.js → AddDriveModal-kzL41qdg.js} +17 -7
  4. package/dist/AddDriveModal-kzL41qdg.js.map +1 -0
  5. package/dist/{ClearStorageModal-D2K3LkHV.js → ClearStorageModal-DIKwqiia.js} +7 -7
  6. package/dist/{ClearStorageModal-D2K3LkHV.js.map → ClearStorageModal-DIKwqiia.js.map} +1 -1
  7. package/dist/{CookiesPolicyModal-4QzcllDR.js → CookiesPolicyModal-1nOh6oN3.js} +5 -5
  8. package/dist/{CookiesPolicyModal-4QzcllDR.js.map → CookiesPolicyModal-1nOh6oN3.js.map} +1 -1
  9. package/dist/{CreateDocumentModal-7vgZ8nbM.js → CreateDocumentModal-nQbLVDq3.js} +4 -4
  10. package/dist/{CreateDocumentModal-7vgZ8nbM.js.map → CreateDocumentModal-nQbLVDq3.js.map} +1 -1
  11. package/dist/{DebugSettingsModal-B1FRL4rp.js → DebugSettingsModal-CJunSkZB.js} +8 -8
  12. package/dist/{DebugSettingsModal-B1FRL4rp.js.map → DebugSettingsModal-CJunSkZB.js.map} +1 -1
  13. package/dist/{DeleteDriveModal-ABue_rOY.js → DeleteDriveModal-6P0T1QgD.js} +6 -6
  14. package/dist/{DeleteDriveModal-ABue_rOY.js.map → DeleteDriveModal-6P0T1QgD.js.map} +1 -1
  15. package/dist/{DeleteItemModal-B6aHnYPD.js → DeleteItemModal-BmV1aQyZ.js} +7 -7
  16. package/dist/{DeleteItemModal-B6aHnYPD.js.map → DeleteItemModal-BmV1aQyZ.js.map} +1 -1
  17. package/dist/{DisclaimerModal-CfDeiBz8.js → DisclaimerModal-CVTFfN0_.js} +6 -6
  18. package/dist/{DisclaimerModal-CfDeiBz8.js.map → DisclaimerModal-CVTFfN0_.js.map} +1 -1
  19. package/dist/{DownloadDocumentWithErrorsModal-CaAPGg04.js → DownloadDocumentWithErrorsModal-CXeFUtaz.js} +5 -5
  20. package/dist/{DownloadDocumentWithErrorsModal-CaAPGg04.js.map → DownloadDocumentWithErrorsModal-CXeFUtaz.js.map} +1 -1
  21. package/dist/{DriveSettingsModal-CNtCO4-y.js → DriveSettingsModal-L5sM-rIh.js} +5 -5
  22. package/dist/{DriveSettingsModal-CNtCO4-y.js.map → DriveSettingsModal-L5sM-rIh.js.map} +1 -1
  23. package/dist/{InspectorModal-ywEL5GKA.js → InspectorModal-C7pIf6OO.js} +8 -8
  24. package/dist/{InspectorModal-ywEL5GKA.js.map → InspectorModal-C7pIf6OO.js.map} +1 -1
  25. package/dist/{MissingPackageModal-CUdcDjSO.js → MissingPackageModal-D_N96M6a.js} +5 -5
  26. package/dist/{MissingPackageModal-CUdcDjSO.js.map → MissingPackageModal-D_N96M6a.js.map} +1 -1
  27. package/dist/{SettingsModal-5dMD1lq9.js → SettingsModal-Dz3u5m_G.js} +11 -11
  28. package/dist/{SettingsModal-5dMD1lq9.js.map → SettingsModal-Dz3u5m_G.js.map} +1 -1
  29. package/dist/{UpgradeDriveModal-BwI5E5k3.js → UpgradeDriveModal-CyAUDCU7.js} +6 -6
  30. package/dist/{UpgradeDriveModal-BwI5E5k3.js.map → UpgradeDriveModal-CyAUDCU7.js.map} +1 -1
  31. package/dist/app-loader-C2ua5JsF.js +4 -0
  32. package/dist/{sidebar-CO3JjI3G.js → app-loader-CCPp_d0K.js} +894 -854
  33. package/dist/app-loader-CCPp_d0K.js.map +1 -0
  34. package/dist/{build-info-B3CWTvMu.js → build-info-DaWwuuKb.js} +4 -4
  35. package/dist/{build-info-B3CWTvMu.js.map → build-info-DaWwuuKb.js.map} +1 -1
  36. package/dist/components-CILkbSlg.js +5 -0
  37. package/dist/connect.config-aPx3rHZQ.js +161 -0
  38. package/dist/connect.config-aPx3rHZQ.js.map +1 -0
  39. package/dist/{load-D-7n7rPU.js → load-DalJBk1C.js} +13 -25
  40. package/dist/load-DalJBk1C.js.map +1 -0
  41. package/dist/main.js +5 -3
  42. package/dist/main.js.map +1 -1
  43. package/dist/{package-Dk3L0oWJ.js → package-BPfA1bwo.js} +6 -5
  44. package/dist/package-BPfA1bwo.js.map +1 -0
  45. package/dist/{pglite-runtime-Btp8ZXVH.js → pglite-runtime-toc1-TWa.js} +12 -6
  46. package/dist/pglite-runtime-toc1-TWa.js.map +1 -0
  47. package/dist/{pglite-seed-b1mJUaip.js → pglite-seed-D8oSKkep.js} +7 -7
  48. package/dist/pglite-seed-D8oSKkep.js.map +1 -0
  49. package/dist/pglite.worker.js +5 -5
  50. package/dist/pglite.worker.js.map +1 -1
  51. package/dist/pglite.worker.legacy.js +4 -4
  52. package/dist/pglite.worker.legacy.js.map +1 -1
  53. package/dist/{reactor-BixVsAMj.js → reactor-BlxTyHZ5.js} +92 -45
  54. package/dist/reactor-BlxTyHZ5.js.map +1 -0
  55. package/dist/{registerServiceWorker-BkUnxLqB.js → registerServiceWorker-B7uKpkci.js} +4 -4
  56. package/dist/{registerServiceWorker-BkUnxLqB.js.map → registerServiceWorker-B7uKpkci.js.map} +1 -1
  57. package/dist/{i18n-BuVT7wFQ.js → reload-connect-toast-CwvSc6F-.js} +19 -5
  58. package/dist/reload-connect-toast-CwvSc6F-.js.map +1 -0
  59. package/dist/runtime-config-DL3QjcfM.js +24 -0
  60. package/dist/runtime-config-DL3QjcfM.js.map +1 -0
  61. package/dist/start-connect.d.ts.map +1 -1
  62. package/dist/start-connect.js +5 -3
  63. package/dist/start-connect.js.map +1 -1
  64. package/dist/style.css +7 -0
  65. package/dist/{toast-DnODOv28.js → toast-BEy42aYx.js} +3 -3
  66. package/dist/{toast-DnODOv28.js.map → toast-BEy42aYx.js.map} +1 -1
  67. package/dist/{useRegistryPackages-PUiMxd73.js → useRegistryPackages-Bh0luCvV.js} +6 -6
  68. package/dist/useRegistryPackages-Bh0luCvV.js.map +1 -0
  69. package/package.json +11 -10
  70. package/public/powerhouse.config.json +33 -0
  71. package/dist/AddDriveModal-Cg2qcYp6.js.map +0 -1
  72. package/dist/connect.config-B2xP5Iez.js +0 -148
  73. package/dist/connect.config-B2xP5Iez.js.map +0 -1
  74. package/dist/i18n-BuVT7wFQ.js.map +0 -1
  75. package/dist/load-D-7n7rPU.js.map +0 -1
  76. package/dist/package-Dk3L0oWJ.js.map +0 -1
  77. package/dist/packages.config-CiH8KJVN.js +0 -32
  78. package/dist/packages.config-CiH8KJVN.js.map +0 -1
  79. package/dist/pglite-runtime-Btp8ZXVH.js.map +0 -1
  80. package/dist/pglite-seed-b1mJUaip.js.map +0 -1
  81. package/dist/reactor-BixVsAMj.js.map +0 -1
  82. package/dist/sidebar-CO3JjI3G.js.map +0 -1
  83. package/dist/useRegistryPackages-PUiMxd73.js.map +0 -1
  84. package/public/ph-packages.json +0 -4
@@ -1,129 +1,434 @@
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]="239c52db-b664-5e9c-ac37-c27d54ef59b8")}catch(e){}}();
3
- import { n as defaultPHAppConfig, r as defaultPHDocumentEditorConfig, t as connectConfig } from "./connect.config-B2xP5Iez.js";
4
- import { n as toast, t as ToastContainer } from "./toast-DnODOv28.js";
5
- import { t as i18n } from "./i18n-BuVT7wFQ.js";
6
- import { c as subscribeReactorPgMajor, d as createFileDataStore, f as idbError, l as IDB_STORE_NAME, m as readPgVersionFile, p as openIdb, r as getCachedReactorPgMajor, u as PRIMARY_IDB_NAMES } from "./pglite-runtime-Btp8ZXVH.js";
7
- import { t as serviceWorkerManager } from "./registerServiceWorker-BkUnxLqB.js";
8
- import { driveCollectionId, getRevisionFromDate, initTheme, logout, openRenown, setPHAppConfig, setPHDocumentEditorConfig, setRevisionHistoryVisible, setSelectedDrive, setSelectedNode, showPHModal, useAppModuleById, useDefaultAppModule, useDocumentById, useDocumentModelModuleById, useDocumentOperations, useDrives, useEditorModuleById, useFallbackEditorModule, useNodeParentFolderById, usePHModal, usePackageDiscoveryService, useReactorClientModule, useRevisionHistoryVisible, useSelectedDocument, useSelectedDocumentId, useSelectedDriveSafe, useSelectedFolder, useSelectedTimelineItem, useSyncList, useTheme, useUser, useVetraPackageManager, useVetraPackages } from "@powerhousedao/reactor-browser";
9
- import { childLogger } from "document-model";
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]="ce32a32d-843f-5968-a8c8-3c3a319dc387")}catch(e){}}();
3
+ import { n as getRuntimeConfig } from "./runtime-config-DL3QjcfM.js";
4
+ import { n as i18n } from "./reload-connect-toast-CwvSc6F-.js";
5
+ import { a as defaultPHDocumentEditorConfig, i as defaultPHAppConfig, r as connectConfig } from "./connect.config-aPx3rHZQ.js";
6
+ import { c as subscribeReactorPgMajor, d as createFileDataStore, f as idbError, l as IDB_STORE_NAME, m as readPgVersionFile, p as openIdb, r as getCachedReactorPgMajor, u as PRIMARY_IDB_NAMES } from "./pglite-runtime-toc1-TWa.js";
7
+ import { t as serviceWorkerManager } from "./registerServiceWorker-B7uKpkci.js";
8
+ import { n as toast, t as ToastContainer } from "./toast-BEy42aYx.js";
9
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
10
+ import { PGlite } from "@electric-sql/pglite";
11
+ import { Trans, useTranslation } from "react-i18next";
10
12
  import React, { StrictMode, Suspense, lazy, useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
13
+ import { driveCollectionId, getRevisionFromDate, initTheme, logout, openRenown, setPHAppConfig, setPHDocumentEditorConfig, setRevisionHistoryVisible, setSelectedDrive, setSelectedNode, showPHModal, useAppModuleById, useDefaultAppModule, useDocumentById, useDocumentModelModuleById, useDocumentOperations, useDrives, useEditorModuleById, useFallbackEditorModule, useIsAddDriveEnabled, useNodeParentFolderById, usePHModal, usePackageDiscoveryService, useReactorClientModule, useRevisionHistoryVisible, useSelectedDocument, useSelectedDocumentId, useSelectedDriveSafe, useSelectedFolder, useSelectedTimelineItem, useSyncList, useTheme, useUser, useVetraPackageManager, useVetraPackages } from "@powerhousedao/reactor-browser";
14
+ import { childLogger } from "document-model";
15
+ import { ConnectSidebar, ConnectTooltipProvider, CookieBanner, DefaultEditorLoader, DropZoneWrapper, HomeScreen, HomeScreenAddDriveItem, HomeScreenItem, LogoAnimation, PackageInstallModal, RevisionHistory, SidebarAddDriveItem, SidebarItem, useEns } from "@powerhousedao/design-system/connect";
11
16
  import { z } from "zod";
12
- import { Trans, useTranslation } from "react-i18next";
13
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
14
17
  import { Outlet, RouterProvider, createBrowserRouter, createRoutesFromChildren, matchRoutes, useLocation, useNavigationType } from "react-router-dom";
15
18
  import { useHotkeys } from "react-hotkeys-hook";
16
- import { ConnectSidebar, ConnectTooltipProvider, CookieBanner, DefaultEditorLoader, DropZoneWrapper, HomeScreen, HomeScreenAddDriveItem, HomeScreenItem, LogoAnimation, PackageInstallModal, RevisionHistory, SidebarAddDriveItem, SidebarItem, useEns } from "@powerhousedao/design-system/connect";
17
- import { GenericDriveExplorer } from "@powerhousedao/powerhouse-vetra-packages/editors";
18
19
  import { Icon, Modal, PowerhouseButton } from "@powerhousedao/design-system";
19
20
  import { ErrorBoundary } from "react-error-boundary";
20
- import { PGlite } from "@electric-sql/pglite";
21
+ import { GenericDriveExplorer } from "@powerhousedao/powerhouse-vetra-packages/editors";
21
22
  import { twMerge } from "tailwind-merge";
22
23
  import { redo, undo } from "@powerhousedao/shared/document-model";
23
- //#region src/hooks/useAcceptedCookies.ts
24
- const ACCEPTED_COOKIES_KEY_STORAGE = `${connectConfig.routerBasename}:acceptedCookies`;
25
- const listeners$3 = /* @__PURE__ */ new Set();
26
- let cookies = getInitial$1();
27
- function getInitial$1() {
28
- try {
29
- const value = localStorage.getItem(ACCEPTED_COOKIES_KEY_STORAGE);
30
- return value ? JSON.parse(value) : {
31
- analytics: false,
32
- marketing: false,
33
- functional: false
34
- };
35
- } catch {
36
- return {
37
- analytics: false,
38
- marketing: false,
39
- functional: false
40
- };
41
- }
24
+ //#region src/utils/browser.ts
25
+ function getBasePath() {
26
+ if (typeof document === "undefined") return "/";
27
+ return document.querySelector("base")?.getAttribute("href") || "/";
42
28
  }
43
- function getCookies() {
44
- return cookies;
29
+ //#endregion
30
+ //#region src/utils/document-editor-debug-tools.ts
31
+ var DocumentEditorDebugTools = class {
32
+ document;
33
+ operations = [];
34
+ constructor(document) {
35
+ if (document) this.document = document;
36
+ }
37
+ operationsToTableObject(operations) {
38
+ return operations.map((op) => ({
39
+ ...op,
40
+ input: JSON.stringify(op.action.input)
41
+ }));
42
+ }
43
+ setDocument(document) {
44
+ this.document = document;
45
+ }
46
+ getDocument() {
47
+ return this.document;
48
+ }
49
+ getOperations() {
50
+ return this.operations;
51
+ }
52
+ pushOperation(operation) {
53
+ this.operations.push(operation);
54
+ }
55
+ operationsTable() {
56
+ if (!this.document) console.warn("No document");
57
+ const ops = Object.values(this.document?.operations || {}).filter((array) => array !== void 0).flatMap((array) => array).sort((a, b) => a.index - b.index);
58
+ console.table(this.operationsToTableObject(ops));
59
+ }
60
+ scopeOperationsTable(scope) {
61
+ if (!this.document) console.warn("No document");
62
+ const ops = this.document?.operations[scope] || [];
63
+ console.table(this.operationsToTableObject(ops));
64
+ }
65
+ operationsLog() {
66
+ console.log(this.operations);
67
+ }
68
+ operationsLogTable() {
69
+ console.table(this.operationsToTableObject(this.operations));
70
+ }
71
+ clear() {
72
+ this.operations = [];
73
+ this.document = void 0;
74
+ }
75
+ };
76
+ //#endregion
77
+ //#region src/components/migration-status.ts
78
+ let current = null;
79
+ const listeners$3 = /* @__PURE__ */ new Set();
80
+ function setMigrationStatus(status) {
81
+ current = status;
82
+ for (const l of listeners$3) l();
45
83
  }
46
- function setCookies(setter) {
47
- cookies = setter(cookies);
48
- localStorage.setItem(ACCEPTED_COOKIES_KEY_STORAGE, JSON.stringify(cookies));
49
- listeners$3.forEach((fn) => fn());
84
+ function getMigrationStatus() {
85
+ return current;
50
86
  }
51
- function subscribe$1(fn) {
52
- listeners$3.add(fn);
53
- return () => listeners$3.delete(fn);
87
+ function subscribeMigrationStatus(cb) {
88
+ listeners$3.add(cb);
89
+ return () => {
90
+ listeners$3.delete(cb);
91
+ };
54
92
  }
55
- const useAcceptedCookies = () => {
56
- return [useSyncExternalStore(subscribe$1, getCookies, getCookies), setCookies];
57
- };
58
93
  //#endregion
59
- //#region src/services/openpanel/buffer.ts
60
- const buffer = [];
61
- let errorHandler;
62
- function defaultOnError(err) {
63
- console.warn("[useOpenPanel] track failed:", err);
94
+ //#region src/utils/pglite-migration.ts
95
+ const BACKUP_PREFIX = "ph-pglite-backup::";
96
+ const BACKUP_INDEX_KEY = "ph:pglite-backups";
97
+ const BACKUP_RETENTION_MS = 720 * 60 * 60 * 1e3;
98
+ const LEGACY_DUMPERS = { 16: async () => {
99
+ const [{ PGlite: LegacyPGlite }, { pgDump }] = await Promise.all([import("pglite-legacy-02"), import("pglite-tools-legacy-02/pg_dump")]);
100
+ return async (idbName) => {
101
+ const pg = new LegacyPGlite(`idb://${idbName.replace(/^\/pglite\//, "")}`);
102
+ try {
103
+ await pg.waitReady;
104
+ return await (await pgDump({ pg })).text();
105
+ } finally {
106
+ await pg.close();
107
+ }
108
+ };
109
+ } };
110
+ function deleteIdb(name, timeoutMs = 15e3) {
111
+ return new Promise((resolve, reject) => {
112
+ const req = indexedDB.deleteDatabase(name);
113
+ let settled = false;
114
+ const done = (fn) => {
115
+ if (settled) return;
116
+ settled = true;
117
+ fn();
118
+ };
119
+ const timer = setTimeout(() => done(() => reject(/* @__PURE__ */ new Error(`IDB delete timed out for ${name} after ${timeoutMs}ms`))), timeoutMs);
120
+ req.onsuccess = () => done(() => {
121
+ clearTimeout(timer);
122
+ resolve();
123
+ });
124
+ req.onerror = () => done(() => {
125
+ clearTimeout(timer);
126
+ reject(idbError(req, `IDB delete failed for ${name}`));
127
+ });
128
+ });
64
129
  }
65
- /**
66
- * Forward a single event to the active client, swallowing any throw.
67
- *
68
- * The outer try/catch handles synchronous throws from `client.track`; the
69
- * inner `.catch()` handles rejected promises. Both are needed because
70
- * `Promise.resolve(x).catch()` cannot catch a throw from evaluating `x`.
71
- */
72
- function forward(client, name, props) {
130
+ async function clearFileData(name) {
131
+ const db = await openIdb(name, 21, createFileDataStore);
73
132
  try {
74
- Promise.resolve(client.track(name, props)).catch((err) => (errorHandler ?? defaultOnError)(err));
75
- } catch (err) {
76
- (errorHandler ?? defaultOnError)(err);
133
+ await new Promise((resolve, reject) => {
134
+ const tx = db.transaction(IDB_STORE_NAME, "readwrite");
135
+ tx.oncomplete = () => resolve();
136
+ tx.onerror = () => reject(idbError(tx, `FILE_DATA clear failed for ${name}`));
137
+ tx.onabort = () => reject(idbError(tx, `FILE_DATA clear aborted for ${name}`));
138
+ tx.objectStore(IDB_STORE_NAME).clear();
139
+ });
140
+ } finally {
141
+ db.close();
77
142
  }
78
143
  }
79
- /**
80
- * Drain the pre-init buffer in FIFO order through `client.track`, then forward
81
- * subsequent events directly. Called once from `getOpenPanelClient()`.
82
- */
83
- function drainOpenPanelBuffer(client, onError) {
84
- errorHandler = onError;
85
- const pending = buffer.splice(0, buffer.length);
86
- for (const [name, props] of pending) forward(client, name, props);
144
+ async function cloneFileData(sourceName, destName) {
145
+ const source = await openIdb(sourceName, 21, createFileDataStore);
146
+ try {
147
+ const dest = await openIdb(destName, 21, createFileDataStore);
148
+ try {
149
+ const readStore = source.transaction(IDB_STORE_NAME, "readonly").objectStore(IDB_STORE_NAME);
150
+ const entries = await new Promise((resolve, reject) => {
151
+ const out = [];
152
+ const req = readStore.openCursor();
153
+ req.onerror = () => reject(idbError(req, `FILE_DATA read cursor failed for ${sourceName}`));
154
+ req.onsuccess = () => {
155
+ const cursor = req.result;
156
+ if (!cursor) return resolve(out);
157
+ out.push({
158
+ key: cursor.key,
159
+ value: cursor.value
160
+ });
161
+ cursor.continue();
162
+ };
163
+ });
164
+ await new Promise((resolve, reject) => {
165
+ const writeTx = dest.transaction(IDB_STORE_NAME, "readwrite");
166
+ writeTx.oncomplete = () => resolve();
167
+ writeTx.onerror = () => reject(idbError(writeTx, `FILE_DATA write failed for ${destName}`));
168
+ writeTx.onabort = () => reject(idbError(writeTx, `FILE_DATA write aborted for ${destName}`));
169
+ const writeStore = writeTx.objectStore(IDB_STORE_NAME);
170
+ writeStore.clear();
171
+ for (const { key, value } of entries) writeStore.put(value, key);
172
+ });
173
+ } finally {
174
+ dest.close();
175
+ }
176
+ } finally {
177
+ source.close();
178
+ }
87
179
  }
88
- /**
89
- * Clear the buffer and reset the active client. Buffered pre-consent events
90
- * are discarded so they are not replayed if the user later re-accepts.
91
- */
92
- function clearOpenPanelBuffer() {
93
- buffer.length = 0;
94
- errorHandler = void 0;
180
+ function readBackupIndex() {
181
+ try {
182
+ const raw = localStorage.getItem(BACKUP_INDEX_KEY);
183
+ if (!raw) return [];
184
+ const parsed = JSON.parse(raw);
185
+ return Array.isArray(parsed) ? parsed : [];
186
+ } catch {
187
+ return [];
188
+ }
95
189
  }
96
- //#endregion
97
- //#region src/services/openpanel/client.ts
98
- let client;
99
- /**
100
- * Returns the cached `OpenPanel` singleton, building it on first call.
101
- *
102
- * When `config.clientId` is empty it returns `undefined` without importing
103
- * `@openpanel/web`, so the bundler can tree-shake the SDK out of production.
104
- */
105
- async function getOpenPanelClient(config) {
106
- if (!config.clientId) return;
107
- if (client) return client;
108
- const { OpenPanel: OpenPanelClass } = await import("@openpanel/web");
109
- client = new OpenPanelClass({
110
- clientId: config.clientId,
111
- ...config.apiUrl ? { apiUrl: config.apiUrl } : {}
112
- });
113
- drainOpenPanelBuffer(client);
114
- return client;
190
+ function writeBackupIndex(records) {
191
+ try {
192
+ localStorage.setItem(BACKUP_INDEX_KEY, JSON.stringify(records));
193
+ } catch {}
115
194
  }
116
- /**
117
- * Clears the cached singleton and the pre-init buffer. Call on consent
118
- * revocation or during tests; buffered events are discarded, not replayed.
119
- */
120
- function resetOpenPanelClient() {
121
- client = void 0;
122
- clearOpenPanelBuffer();
195
+ function registerBackup(rec) {
196
+ writeBackupIndex([...readBackupIndex(), rec]);
123
197
  }
124
- //#endregion
125
- //#region src/services/openpanel/events.json
126
- var events_default = [
198
+ function forgetBackup(name) {
199
+ writeBackupIndex(readBackupIndex().filter((r) => r.name !== name));
200
+ }
201
+ async function janitor() {
202
+ const cutoff = Date.now() - BACKUP_RETENTION_MS;
203
+ const keep = [];
204
+ for (const rec of readBackupIndex()) {
205
+ if (rec.createdAt >= cutoff) {
206
+ keep.push(rec);
207
+ continue;
208
+ }
209
+ try {
210
+ await deleteIdb(rec.name);
211
+ } catch (err) {
212
+ console.warn(`Failed to delete expired pglite backup ${rec.name}`, err);
213
+ keep.push(rec);
214
+ }
215
+ }
216
+ writeBackupIndex(keep);
217
+ }
218
+ function publishPhase(idbName, phase) {
219
+ setMigrationStatus({
220
+ idbName,
221
+ phase
222
+ });
223
+ }
224
+ async function migrateIdbIfNeeded(idbName) {
225
+ const major = await readPgVersionFile(idbName);
226
+ if (major === null || major === 17) return;
227
+ const loader = LEGACY_DUMPERS[major];
228
+ if (!loader) throw new Error(`Unsupported legacy PGlite data dir: PG_VERSION=${major} for ${idbName}`);
229
+ const backupName = `${BACKUP_PREFIX}${idbName}::${(/* @__PURE__ */ new Date()).toISOString()}`;
230
+ const backupRec = {
231
+ name: backupName,
232
+ idbName,
233
+ createdAt: Date.now()
234
+ };
235
+ publishPhase(idbName, "clone");
236
+ await cloneFileData(idbName, backupName);
237
+ registerBackup(backupRec);
238
+ let sql;
239
+ try {
240
+ publishPhase(idbName, "dump");
241
+ sql = await (await loader())(idbName);
242
+ } catch (err) {
243
+ await deleteIdb(backupName).catch(() => {});
244
+ forgetBackup(backupName);
245
+ throw err;
246
+ }
247
+ try {
248
+ await clearFileData(idbName);
249
+ publishPhase(idbName, "restore");
250
+ const pg = new PGlite(`idb://${idbName.replace(/^\/pglite\//, "")}`, { relaxedDurability: false });
251
+ try {
252
+ await pg.waitReady;
253
+ console.info(`[pglite-migration] Dump preamble (first 500 chars):\n${sql.slice(0, 500)}`);
254
+ try {
255
+ await pg.exec("SET standard_conforming_strings = off;");
256
+ } catch (gucErr) {
257
+ console.warn("[pglite-migration] Could not force standard_conforming_strings=off", gucErr);
258
+ }
259
+ try {
260
+ await pg.exec(sql);
261
+ } catch (execErr) {
262
+ logRestoreFailure(idbName, sql, execErr);
263
+ throw execErr;
264
+ }
265
+ } finally {
266
+ await pg.close();
267
+ }
268
+ } catch (err) {
269
+ await rollbackFromBackup(idbName, backupName, err);
270
+ throw err;
271
+ }
272
+ }
273
+ function logRestoreFailure(idbName, sql, err) {
274
+ const errObj = err;
275
+ const position = typeof errObj.position === "string" ? parseInt(errObj.position, 10) : typeof errObj.position === "number" ? errObj.position : NaN;
276
+ console.error(`[pglite-migration] Restore failed for ${idbName}`, {
277
+ message: errObj.message,
278
+ code: errObj.code,
279
+ severity: errObj.severity,
280
+ detail: errObj.detail,
281
+ where: errObj.where,
282
+ position: errObj.position,
283
+ sqlLength: sql.length
284
+ });
285
+ if (Number.isFinite(position) && position > 0) {
286
+ const zeroBased = position - 1;
287
+ const start = Math.max(0, zeroBased - 200);
288
+ const end = Math.min(sql.length, zeroBased + 200);
289
+ const before = sql.slice(start, zeroBased);
290
+ const at = sql.slice(zeroBased, zeroBased + 1);
291
+ const after = sql.slice(zeroBased + 1, end);
292
+ console.error(`[pglite-migration] SQL context around position ${position}:\n${before}»${at}«${after}`);
293
+ const lineStart = sql.lastIndexOf("\n", zeroBased) + 1;
294
+ const lineEnd = sql.indexOf("\n", zeroBased);
295
+ const line = sql.slice(lineStart, lineEnd === -1 ? sql.length : lineEnd);
296
+ console.error(`[pglite-migration] Failing line (truncated to 500 chars):\n${line.slice(0, 500)}`);
297
+ } else console.error(`[pglite-migration] No position info. First 2000 chars of dump:\n${sql.slice(0, 2e3)}`);
298
+ }
299
+ async function rollbackFromBackup(idbName, backupName, originalError) {
300
+ try {
301
+ await clearFileData(idbName).catch(() => {});
302
+ await cloneFileData(backupName, idbName);
303
+ } catch (rollbackErr) {
304
+ console.error(`PGlite migration failed AND rollback failed for ${idbName}. Primary error:`, originalError, "Rollback error:", rollbackErr);
305
+ return;
306
+ }
307
+ await deleteIdb(backupName).catch(() => {});
308
+ forgetBackup(backupName);
309
+ }
310
+ async function migrateAllLocked() {
311
+ for (const idbName of PRIMARY_IDB_NAMES) await migrateIdbIfNeeded(idbName);
312
+ setMigrationStatus(null);
313
+ }
314
+ async function migrateAllIfNeeded() {
315
+ janitor().catch((err) => console.warn("PGlite backup janitor failed", err));
316
+ if (!(typeof navigator !== "undefined" && "locks" in navigator && typeof navigator.locks.request === "function")) {
317
+ await migrateAllLocked();
318
+ return;
319
+ }
320
+ await navigator.locks.request("ph-pglite-migration", { mode: "exclusive" }, async () => {
321
+ await migrateAllLocked();
322
+ });
323
+ }
324
+ //#endregion
325
+ //#region src/hooks/useAcceptedCookies.ts
326
+ const ACCEPTED_COOKIES_KEY_STORAGE = `${connectConfig.routerBasename}:acceptedCookies`;
327
+ const listeners$2 = /* @__PURE__ */ new Set();
328
+ let cookies = getInitial$1();
329
+ function getInitial$1() {
330
+ try {
331
+ const value = localStorage.getItem(ACCEPTED_COOKIES_KEY_STORAGE);
332
+ return value ? JSON.parse(value) : {
333
+ analytics: false,
334
+ marketing: false,
335
+ functional: false
336
+ };
337
+ } catch {
338
+ return {
339
+ analytics: false,
340
+ marketing: false,
341
+ functional: false
342
+ };
343
+ }
344
+ }
345
+ function getCookies() {
346
+ return cookies;
347
+ }
348
+ function setCookies(setter) {
349
+ cookies = setter(cookies);
350
+ localStorage.setItem(ACCEPTED_COOKIES_KEY_STORAGE, JSON.stringify(cookies));
351
+ listeners$2.forEach((fn) => fn());
352
+ }
353
+ function subscribe$1(fn) {
354
+ listeners$2.add(fn);
355
+ return () => listeners$2.delete(fn);
356
+ }
357
+ const useAcceptedCookies = () => {
358
+ return [useSyncExternalStore(subscribe$1, getCookies, getCookies), setCookies];
359
+ };
360
+ //#endregion
361
+ //#region src/services/openpanel/buffer.ts
362
+ const buffer = [];
363
+ let errorHandler;
364
+ function defaultOnError(err) {
365
+ console.warn("[useOpenPanelAnalytics] track failed:", err);
366
+ }
367
+ /**
368
+ * Forward a single event to the active client, swallowing any throw.
369
+ *
370
+ * The outer try/catch handles synchronous throws from `client.track`; the
371
+ * inner `.catch()` handles rejected promises. Both are needed because
372
+ * `Promise.resolve(x).catch()` cannot catch a throw from evaluating `x`.
373
+ */
374
+ function forward(client, name, props) {
375
+ try {
376
+ Promise.resolve(client.track(name, props)).catch((err) => (errorHandler ?? defaultOnError)(err));
377
+ } catch (err) {
378
+ (errorHandler ?? defaultOnError)(err);
379
+ }
380
+ }
381
+ /**
382
+ * Drain the pre-init buffer in FIFO order through `client.track`, then forward
383
+ * subsequent events directly. Called once from `getOpenPanelClient()`.
384
+ */
385
+ function drainOpenPanelBuffer(client, onError) {
386
+ errorHandler = onError;
387
+ const pending = buffer.splice(0, buffer.length);
388
+ for (const [name, props] of pending) forward(client, name, props);
389
+ }
390
+ /**
391
+ * Clear the buffer and reset the active client. Buffered pre-consent events
392
+ * are discarded so they are not replayed if the user later re-accepts.
393
+ */
394
+ function clearOpenPanelBuffer() {
395
+ buffer.length = 0;
396
+ errorHandler = void 0;
397
+ }
398
+ //#endregion
399
+ //#region src/services/openpanel/client.ts
400
+ let client;
401
+ /**
402
+ * Returns the cached `OpenPanel` singleton, building it on first call.
403
+ *
404
+ * When `config.clientId` is empty it returns `undefined` without importing
405
+ * `@openpanel/web`, so the bundler can tree-shake the SDK out of production.
406
+ */
407
+ async function getOpenPanelClient(config) {
408
+ if (!config.clientId) return;
409
+ if (client) return client;
410
+ const { OpenPanel: OpenPanelClass } = await import("@openpanel/web");
411
+ client = new OpenPanelClass({
412
+ clientId: config.clientId,
413
+ ...config.apiUrl ? { apiUrl: config.apiUrl } : {},
414
+ trackScreenViews: true,
415
+ trackOutgoingLinks: true
416
+ });
417
+ client.setGlobalProperties({ app: "connect" });
418
+ drainOpenPanelBuffer(client);
419
+ return client;
420
+ }
421
+ /**
422
+ * Clears the cached singleton and the pre-init buffer. Call on consent
423
+ * revocation or during tests; buffered events are discarded, not replayed.
424
+ */
425
+ function resetOpenPanelClient() {
426
+ client = void 0;
427
+ clearOpenPanelBuffer();
428
+ }
429
+ //#endregion
430
+ //#region src/services/openpanel/events.json
431
+ var events_default = [
127
432
  {
128
433
  "documentType": "powerhouse/document-drive",
129
434
  "actionTypes": [
@@ -212,15 +517,17 @@ function deriveEventName(mapping, op) {
212
517
  if (mapping.alias) return mapping.alias;
213
518
  return `${normalize(op.context.documentType)}.${op.operation.action.type.toLowerCase()}`;
214
519
  }
215
- /** Builds the default properties attached to every OpenPanel event. */
520
+ /**
521
+ * Builds the default properties attached to every OpenPanel event. The `app`
522
+ * property is stamped globally by the client, not here.
523
+ */
216
524
  function buildDefaultProperties(op) {
217
525
  return {
218
526
  documentType: op.context.documentType,
219
527
  actionType: op.operation.action.type,
220
528
  documentId: op.context.documentId,
221
529
  scope: op.context.scope,
222
- branch: op.context.branch,
223
- app: "connect"
530
+ branch: op.context.branch
224
531
  };
225
532
  }
226
533
  //#endregion
@@ -295,7 +602,7 @@ function useIsEmbedded() {
295
602
  return getIsEmbedded();
296
603
  }
297
604
  const COOKIE_BANNER_KEY_STORAGE = `${connectConfig.routerBasename}:display-cookie-banner`;
298
- const listeners$2 = /* @__PURE__ */ new Set();
605
+ const listeners$1 = /* @__PURE__ */ new Set();
299
606
  let bannerShown = getInitial();
300
607
  function getInitial() {
301
608
  if (getIsEmbedded()) return false;
@@ -314,8 +621,8 @@ function setCookieBannerState(state) {
314
621
  localStorage.setItem(COOKIE_BANNER_KEY_STORAGE, JSON.stringify(state));
315
622
  }
316
623
  function subscribe(fn) {
317
- listeners$2.add(fn);
318
- return () => listeners$2.delete(fn);
624
+ listeners$1.add(fn);
625
+ return () => listeners$1.delete(fn);
319
626
  }
320
627
  const useCookieBanner = () => {
321
628
  return [useSyncExternalStore(subscribe, getCookieBannerState, getCookieBannerState), setCookieBannerState];
@@ -335,7 +642,6 @@ async function getSentry() {
335
642
  return await import("@sentry/react");
336
643
  }
337
644
  async function initSentry() {
338
- connectConfig.sentry;
339
645
  const Sentry = await getSentry();
340
646
  const integrations = [
341
647
  Sentry.httpClientIntegration(),
@@ -352,7 +658,7 @@ async function initSentry() {
352
658
  }));
353
659
  Sentry.init({
354
660
  release: connectConfig.sentry.release,
355
- dsn: connectConfig.sentry.dsn,
661
+ dsn: connectConfig.sentry.dsn ?? void 0,
356
662
  environment: connectConfig.sentry.env,
357
663
  integrations,
358
664
  ignoreErrors: [
@@ -384,7 +690,7 @@ function useInitSentry() {
384
690
  closeClient().catch((error) => logger$2.error("@error", error));
385
691
  return;
386
692
  }
387
- if (clientStarted || !connectConfig.sentry.dsn || connectConfig.sentry.dsn === "") return;
693
+ if (clientStarted || connectConfig.sentry.dsn === null || connectConfig.sentry.dsn === void 0 || connectConfig.sentry.dsn === "") return;
388
694
  initSentry().catch((error) => logger$2.error("@error", error));
389
695
  }, [analytics]);
390
696
  }
@@ -424,200 +730,81 @@ const useUndoRedoShortcuts = (props) => {
424
730
  }, {}, [canRedo, redo]);
425
731
  };
426
732
  //#endregion
427
- //#region src/components/analytics.tsx
428
- function gtag(...args) {
429
- window.dataLayer?.push(...args);
430
- }
431
- const Analytics = () => {
432
- const gaTrackingId = connectConfig.gaTrackingId;
433
- const [{ analytics }] = useAcceptedCookies();
434
- const useAnalytics = gaTrackingId && analytics;
435
- useEffect(() => {
436
- if (useAnalytics) {
437
- const script = document.createElement("script");
438
- script.src = `https://www.googletagmanager.com/gtag/js?id=${gaTrackingId}`;
439
- script.async = true;
440
- document.head.appendChild(script);
441
- window.dataLayer = window.dataLayer || [];
442
- gtag("js", /* @__PURE__ */ new Date());
443
- gtag("config", gaTrackingId);
444
- return () => {
445
- document.head.removeChild(script);
446
- };
447
- }
448
- }, [useAnalytics, gaTrackingId]);
449
- return null;
733
+ //#region src/components/app-skeleton.tsx
734
+ const LOADER_DELAY = 250;
735
+ const PHASE_LABEL = {
736
+ clone: "Backing up local database…",
737
+ dump: "Exporting data from previous version…",
738
+ restore: "Restoring data into the new database…"
450
739
  };
451
- //#endregion
452
- //#region src/components/openpanel-traits.ts
453
- /**
454
- * Builds the OpenPanel identity traits from a renown User.
455
- *
456
- * Rules:
457
- * - `credential` (contains a JWT) is **never** forwarded.
458
- * - `did` / `profileId` is sent as the top-level `profileId` key in the
459
- * `client.identify()` call — it is **not** duplicated inside traits.
460
- * - Optional fields (`ens`, `profile`) are only included when their value is
461
- * non-nullish (guards against `null` from `RenownProfile` fields as well
462
- * as plain `undefined`).
463
- *
464
- * Mirror the Sentry pattern in `store/user.ts`: destructure off `credential`
465
- * first, then assemble the trait payload from the remainder.
466
- */
467
- function buildTraits(user) {
468
- const { credential: _credential, ...rest } = user;
469
- const traits = {
470
- address: rest.address,
471
- networkId: rest.networkId,
472
- chainId: rest.chainId
473
- };
474
- if (rest.ens?.name != null) traits.ensName = rest.ens.name;
475
- if (rest.ens?.avatarUrl != null) traits.ensAvatar = rest.ens.avatarUrl;
476
- if (rest.profile?.username != null) traits.username = rest.profile.username;
477
- if (rest.profile?.userImage != null) traits.userImage = rest.profile.userImage;
478
- if (rest.profile?.documentId != null) traits.profileDocumentId = rest.profile.documentId;
479
- if (rest.profile?.createdAt != null) traits.profileCreatedAt = rest.profile.createdAt;
480
- return traits;
481
- }
482
- //#endregion
483
- //#region src/components/openpanel.tsx
484
- /**
485
- * `OpenPanel` is a `null`-rendering component that manages the full lifecycle
486
- * of the OpenPanel analytics subsystem.
487
- *
488
- * Gates:
489
- * - `useAcceptedCookies().analytics === true` — the existing analytics cookie.
490
- * - `connectConfig.openPanel.clientId` non-empty — no client ID → no-op.
491
- *
492
- * Behaviour when both gates pass:
493
- * 1. Lazy-imports `@openpanel/web` and builds the singleton via
494
- * `getOpenPanelClient(connectConfig.openPanel)`.
495
- * 2. Exposes the client on `window.openPanel` for the card-6 `useOpenPanel()`
496
- * hook to consume.
497
- * 3. If `connectConfig.openPanel.trackOperations`, registers the processor
498
- * factory against `processorManager` so document operations are forwarded.
499
- * 4. Calls `client.identify()` when the renown user transitions from
500
- * `undefined` → defined (login).
501
- * 5. Calls `client.clear()` when the user transitions from defined →
502
- * `undefined` (logout).
503
- *
504
- * Teardown (unmount **or** consent revocation):
505
- * - `unregisterFactory("openpanel")` to remove the processor.
506
- * - `resetOpenPanelClient()` to clear the singleton.
507
- * - `window.openPanel` is cleared.
508
- *
509
- * Mirrors the Sentry pattern in `store/user.ts` for login/logout detection.
510
- * Analytics failures are caught and forwarded to `console.warn` — they must
511
- * never throw into the application.
512
- *
513
- * Mounted next to `<Analytics />` in `app.tsx`.
514
- */
515
- function OpenPanel() {
516
- const [{ analytics }] = useAcceptedCookies();
517
- const user = useUser();
518
- const reactorClientModule = useReactorClientModule();
519
- const [client, setClient] = useState();
520
- /**
521
- * Tracks the previous `user` value so we can detect the two transitions:
522
- * undefined → defined (login)
523
- * defined → undefined (logout)
524
- */
525
- const prevUserRef = useRef(void 0);
526
- const enabled = analytics && !!connectConfig.openPanel.clientId;
527
- const processorManager = reactorClientModule?.reactorModule?.processorManager;
528
- useEffect(() => {
529
- if (!enabled) return;
530
- let cancelled = false;
531
- (async () => {
532
- let builtClient;
533
- try {
534
- builtClient = await getOpenPanelClient(connectConfig.openPanel);
535
- } catch (err) {
536
- console.warn("[OpenPanel] Failed to build client:", err);
537
- return;
538
- }
539
- if (cancelled || !builtClient) return;
540
- setClient(builtClient);
541
- window.openPanel = builtClient;
542
- if (connectConfig.openPanel.trackOperations && processorManager) try {
543
- await processorManager.registerFactory("openpanel", createOpenPanelProcessorFactory({
544
- client: builtClient,
545
- events: {
546
- mappings: eventMappings,
547
- lookupMap: eventLookupMap
548
- },
549
- startFrom: "current"
550
- }));
551
- if (cancelled) await processorManager.unregisterFactory("openpanel").catch((err) => console.warn("[OpenPanel] Failed to unregister factory after cancellation:", err));
552
- } catch (err) {
553
- console.warn("[OpenPanel] Failed to register processor factory:", err);
554
- }
555
- })();
556
- return () => {
557
- cancelled = true;
558
- prevUserRef.current = void 0;
559
- if (connectConfig.openPanel.trackOperations && processorManager) processorManager.unregisterFactory("openpanel").catch((err) => console.warn("[OpenPanel] Failed to unregister processor factory:", err));
560
- resetOpenPanelClient();
561
- window.openPanel = void 0;
562
- setClient(void 0);
563
- };
564
- }, [enabled, processorManager]);
565
- useEffect(() => {
566
- if (!enabled || !client) return;
567
- const prev = prevUserRef.current;
568
- prevUserRef.current = user;
569
- if (!prev && user) try {
570
- client.identify({
571
- profileId: user.did,
572
- properties: buildTraits(user)
573
- });
574
- } catch (err) {
575
- console.warn("[OpenPanel] Failed to identify user:", err);
576
- }
577
- else if (prev && !user) try {
578
- client.clear();
579
- } catch (err) {
580
- console.warn("[OpenPanel] Failed to clear user:", err);
581
- }
582
- }, [
583
- client,
584
- user,
585
- enabled
586
- ]);
587
- return null;
588
- }
589
- //#endregion
590
- //#region src/components/document-editor-container.tsx
591
- function DocumentEditorContainer() {
592
- const [selectedDocument] = useSelectedDocument();
593
- const parentFolder = useNodeParentFolderById(selectedDocument.header.id);
594
- useMemo(() => {
595
- return async () => {};
596
- }, []);
597
- useCallback(() => {
598
- setSelectedNode(parentFolder);
599
- }, [parentFolder]);
740
+ const MigrationOverlay = () => {
741
+ const status = useSyncExternalStore(subscribeMigrationStatus, getMigrationStatus, () => null);
742
+ if (!status) return null;
600
743
  return /* @__PURE__ */ jsx("div", {
601
- id: "document-editor-container",
602
- className: "flex-1",
603
- "data-document-type": selectedDocument.header.documentType,
604
- children: /* @__PURE__ */ jsx(DocumentEditor, { document: selectedDocument })
744
+ className: "absolute inset-0 z-20 flex items-center justify-center",
745
+ children: /* @__PURE__ */ jsxs("div", {
746
+ className: "rounded-lg bg-white/90 px-6 py-4 text-sm text-gray-900 shadow-lg dark:bg-slate-900/90 dark:text-slate-50",
747
+ children: [/* @__PURE__ */ jsx("div", {
748
+ className: "font-medium",
749
+ children: "Upgrading local database…"
750
+ }), /* @__PURE__ */ jsx("div", {
751
+ className: "text-gray-700 dark:text-slate-200",
752
+ children: PHASE_LABEL[status.phase]
753
+ })]
754
+ })
605
755
  });
606
- }
607
- //#endregion
608
- //#region src/components/editor-loader.tsx
609
- function EditorLoader(props) {
610
- const [showLoading, setShowLoading] = useState(false);
756
+ };
757
+ const Loader = ({ delay = LOADER_DELAY }) => {
758
+ const isSSR = typeof window === "undefined";
759
+ const showInitialLoader = typeof document !== "undefined" && document.body.getAttribute("data-show-loader") === "true";
760
+ if (!isSSR) initTheme();
761
+ const [showLoading, setShowLoading] = useState(!delay || showInitialLoader);
611
762
  useEffect(() => {
612
- setTimeout(() => {
763
+ const id = setTimeout(() => {
613
764
  setShowLoading(true);
614
- }, props.loadingTimeout ?? 200);
615
- }, [props]);
616
- if (!showLoading) return null;
617
- const { customEditorLoader, ...defaultProps } = props;
618
- if (customEditorLoader) return /* @__PURE__ */ jsx(Fragment, { children: customEditorLoader });
619
- return /* @__PURE__ */ jsx(DefaultEditorLoader, { ...defaultProps });
620
- }
765
+ }, delay);
766
+ return () => clearTimeout(id);
767
+ }, []);
768
+ return /* @__PURE__ */ jsxs("div", {
769
+ className: `skeleton-loader absolute inset-0 z-10 flex items-center justify-center ${showLoading ? "" : "hidden"}`,
770
+ children: [/* @__PURE__ */ jsx("div", {
771
+ className: "animate-pulse overflow-hidden rounded-full shadow-lg",
772
+ children: /* @__PURE__ */ jsx(LogoAnimation, {})
773
+ }), isSSR ? /* @__PURE__ */ jsx("script", { dangerouslySetInnerHTML: { __html: `setTimeout(() => {
774
+ document.querySelector('.skeleton-loader')?.classList.remove('hidden');
775
+ document.body.setAttribute('data-show-loader', 'true');
776
+ }, ${delay})` } }) : null]
777
+ });
778
+ };
779
+ const AppSkeleton = (props) => {
780
+ const isSSR = typeof window === "undefined";
781
+ const isHomeScreen = !isSSR && window.location.pathname === getBasePath();
782
+ return /* @__PURE__ */ jsxs("div", {
783
+ className: "flex h-screen overflow-hidden bg-gray-50 dark:bg-slate-700",
784
+ children: [
785
+ !(!isSSR && getIsEmbedded()) && /* @__PURE__ */ jsx(ConnectSidebar, {
786
+ className: "animate-pulse",
787
+ onLogin: void 0,
788
+ onDisconnect: void 0,
789
+ onClickSettings: void 0,
790
+ address: void 0
791
+ }),
792
+ /* @__PURE__ */ jsx(HomeScreen, {
793
+ containerClassName: isSSR || !isHomeScreen ? "hidden home-screen" : void 0,
794
+ children: props.children ?? null
795
+ }),
796
+ isSSR ? /* @__PURE__ */ jsx("script", { dangerouslySetInnerHTML: { __html: `
797
+ const baseEl = document.querySelector('base');
798
+ const href = baseEl?.getAttribute('href');
799
+ const basePath = href || '/';
800
+ if (window.location.pathname === basePath) {
801
+ document.querySelector('.home-screen')?.classList.remove('hidden')
802
+ }` } }) : null,
803
+ !props.children ? /* @__PURE__ */ jsx(Loader, {}) : null,
804
+ /* @__PURE__ */ jsx(MigrationOverlay, {})
805
+ ]
806
+ });
807
+ };
621
808
  //#endregion
622
809
  //#region src/components/error-boundary.tsx
623
810
  const logger$1 = childLogger(["Connect"]);
@@ -762,564 +949,253 @@ function ErrorBoundary$1(props) {
762
949
  });
763
950
  }
764
951
  //#endregion
765
- //#region src/components/app-container.tsx
766
- function AppContainer() {
767
- const [selectedDrive] = useSelectedDriveSafe();
768
- const selectedDocumentId = useSelectedDocumentId();
769
- const app = useAppModuleById(selectedDrive?.header.meta?.preferredEditor);
770
- const defaultApp = useDefaultAppModule();
771
- if (!selectedDrive) return /* @__PURE__ */ jsx(EditorLoader, {});
772
- const AppComponent = app?.Component ?? defaultApp?.Component ?? GenericDriveExplorer.Component;
773
- if (!AppComponent) throw new Error("No app component found");
774
- return /* @__PURE__ */ jsx(ErrorBoundary$1, {
775
- variant: "detailed",
776
- resetKeys: [selectedDrive.header.id],
777
- loggerContext: ["Connect", "App"],
778
- children: /* @__PURE__ */ jsx(Suspense, {
779
- fallback: /* @__PURE__ */ jsx(EditorLoader, {}),
780
- children: /* @__PURE__ */ jsx(DropZoneWrapper, {
781
- className: "flex h-full flex-col overflow-auto",
782
- children: /* @__PURE__ */ jsx(AppComponent, { children: selectedDocumentId ? /* @__PURE__ */ jsx(DocumentEditorContainer, {}) : null })
783
- })
784
- })
785
- });
786
- }
787
- //#endregion
788
- //#region src/utils/browser.ts
789
- function getBasePath() {
790
- if (typeof document === "undefined") return "/";
791
- return document.querySelector("base")?.getAttribute("href") || "/";
952
+ //#region src/components/analytics.tsx
953
+ function gtag(...args) {
954
+ window.dataLayer?.push(...args);
792
955
  }
793
- //#endregion
794
- //#region src/utils/document-editor-debug-tools.ts
795
- var DocumentEditorDebugTools = class {
796
- document;
797
- operations = [];
798
- constructor(document) {
799
- if (document) this.document = document;
800
- }
801
- operationsToTableObject(operations) {
802
- return operations.map((op) => ({
803
- ...op,
804
- input: JSON.stringify(op.action.input)
805
- }));
806
- }
807
- setDocument(document) {
808
- this.document = document;
809
- }
810
- getDocument() {
811
- return this.document;
812
- }
813
- getOperations() {
814
- return this.operations;
815
- }
816
- pushOperation(operation) {
817
- this.operations.push(operation);
818
- }
819
- operationsTable() {
820
- if (!this.document) console.warn("No document");
821
- const ops = Object.values(this.document?.operations || {}).filter((array) => array !== void 0).flatMap((array) => array).sort((a, b) => a.index - b.index);
822
- console.table(this.operationsToTableObject(ops));
823
- }
824
- scopeOperationsTable(scope) {
825
- if (!this.document) console.warn("No document");
826
- const ops = this.document?.operations[scope] || [];
827
- console.table(this.operationsToTableObject(ops));
828
- }
829
- operationsLog() {
830
- console.log(this.operations);
831
- }
832
- operationsLogTable() {
833
- console.table(this.operationsToTableObject(this.operations));
834
- }
835
- clear() {
836
- this.operations = [];
837
- this.document = void 0;
838
- }
956
+ const Analytics = () => {
957
+ const gaTrackingId = connectConfig.gaTrackingId;
958
+ const [{ analytics }] = useAcceptedCookies();
959
+ const useAnalytics = gaTrackingId && analytics;
960
+ useEffect(() => {
961
+ if (useAnalytics) {
962
+ const script = document.createElement("script");
963
+ script.src = `https://www.googletagmanager.com/gtag/js?id=${gaTrackingId}`;
964
+ script.async = true;
965
+ document.head.appendChild(script);
966
+ window.dataLayer = window.dataLayer || [];
967
+ gtag("js", /* @__PURE__ */ new Date());
968
+ gtag("config", gaTrackingId);
969
+ return () => {
970
+ document.head.removeChild(script);
971
+ };
972
+ }
973
+ }, [useAnalytics, gaTrackingId]);
974
+ return null;
839
975
  };
840
976
  //#endregion
841
- //#region src/components/migration-status.ts
842
- let current = null;
843
- const listeners$1 = /* @__PURE__ */ new Set();
844
- function setMigrationStatus(status) {
845
- current = status;
846
- for (const l of listeners$1) l();
847
- }
848
- function getMigrationStatus() {
849
- return current;
977
+ //#region src/components/openpanel-traits.ts
978
+ /**
979
+ * Builds the OpenPanel identity traits from a renown User.
980
+ *
981
+ * Canonical trait schema (shared with Renown and Vetra — each app sends every
982
+ * field it has data for, under these names):
983
+ * - `address`, `did`, `networkId`, `chainId`, `caip2` — wallet/chain facts.
984
+ * - `ensName`, `ensAvatar` — raw ENS facts.
985
+ * - `username`, `userImage`, `profileDocumentId`, `profileCreatedAt` — raw
986
+ * Renown profile facts.
987
+ * - `accountType` — wallet-adapter concept; only Renown has it.
988
+ *
989
+ * Rules:
990
+ * - `credential` (contains a JWT) is **never** forwarded.
991
+ * - Traits carry raw facts only; resolved presentation values (avatar,
992
+ * display name) go in OpenPanel's native identify fields — see
993
+ * {@link buildIdentifyPayload}.
994
+ * - Optional fields (`ens`, `profile`) are only included when their value is
995
+ * non-nullish (guards against `null` from `RenownProfile` fields as well
996
+ * as plain `undefined`).
997
+ *
998
+ * Mirror the Sentry pattern in `store/user.ts`: destructure off `credential`
999
+ * first, then assemble the trait payload from the remainder.
1000
+ */
1001
+ function buildTraits(user) {
1002
+ const { credential: _credential, ...rest } = user;
1003
+ const traits = {
1004
+ address: rest.address,
1005
+ did: rest.did,
1006
+ networkId: rest.networkId,
1007
+ chainId: rest.chainId,
1008
+ caip2: `${rest.networkId}:${rest.chainId}`
1009
+ };
1010
+ if (rest.ens?.name != null) traits.ensName = rest.ens.name;
1011
+ if (rest.ens?.avatarUrl != null) traits.ensAvatar = rest.ens.avatarUrl;
1012
+ if (rest.profile?.username != null) traits.username = rest.profile.username;
1013
+ if (rest.profile?.userImage != null) traits.userImage = rest.profile.userImage;
1014
+ if (rest.profile?.documentId != null) traits.profileDocumentId = rest.profile.documentId;
1015
+ if (rest.profile?.createdAt != null) traits.profileCreatedAt = rest.profile.createdAt;
1016
+ return traits;
850
1017
  }
851
- function subscribeMigrationStatus(cb) {
852
- listeners$1.add(cb);
853
- return () => {
854
- listeners$1.delete(cb);
1018
+ /**
1019
+ * Builds the full `identify()` payload:
1020
+ * - `profileId` is the wallet address — the cross-app profile key shared with
1021
+ * Renown and Vetra.
1022
+ * - `avatar` / `firstName` are OpenPanel's native profile fields (they drive
1023
+ * the picture and name in the dashboard UI), resolved as
1024
+ * `userImage ?? ensAvatar` and `ensName ?? username`.
1025
+ * - `properties` are the raw traits from {@link buildTraits}.
1026
+ */
1027
+ function buildIdentifyPayload(user) {
1028
+ const avatar = user.profile?.userImage ?? user.ens?.avatarUrl;
1029
+ const firstName = user.ens?.name ?? user.profile?.username;
1030
+ return {
1031
+ profileId: user.address,
1032
+ ...avatar != null ? { avatar } : {},
1033
+ ...firstName != null ? { firstName } : {},
1034
+ properties: buildTraits(user)
855
1035
  };
856
1036
  }
857
1037
  //#endregion
858
- //#region src/utils/pglite-migration.ts
859
- const BACKUP_PREFIX = "ph-pglite-backup::";
860
- const BACKUP_INDEX_KEY = "ph:pglite-backups";
861
- const BACKUP_RETENTION_MS = 720 * 60 * 60 * 1e3;
862
- const LEGACY_DUMPERS = { 16: async () => {
863
- const [{ PGlite: LegacyPGlite }, { pgDump }] = await Promise.all([import("pglite-legacy-02"), import("pglite-tools-legacy-02/pg_dump")]);
864
- return async (idbName) => {
865
- const pg = new LegacyPGlite(`idb://${idbName.replace(/^\/pglite\//, "")}`);
866
- try {
867
- await pg.waitReady;
868
- return await (await pgDump({ pg })).text();
869
- } finally {
870
- await pg.close();
871
- }
872
- };
873
- } };
874
- function deleteIdb(name, timeoutMs = 15e3) {
875
- return new Promise((resolve, reject) => {
876
- const req = indexedDB.deleteDatabase(name);
877
- let settled = false;
878
- const done = (fn) => {
879
- if (settled) return;
880
- settled = true;
881
- fn();
1038
+ //#region src/components/openpanel.tsx
1039
+ /**
1040
+ * `OpenPanel` is a `null`-rendering component that manages the full lifecycle
1041
+ * of the OpenPanel analytics subsystem.
1042
+ *
1043
+ * Gates:
1044
+ * - `useAcceptedCookies().analytics === true` — the existing analytics cookie.
1045
+ * - `connectConfig.openPanel.clientId` non-empty — no client ID → no-op.
1046
+ *
1047
+ * Behaviour when both gates pass:
1048
+ * 1. Lazy-imports `@openpanel/web` and builds the singleton via
1049
+ * `getOpenPanelClient(connectConfig.openPanel)`.
1050
+ * 2. Exposes the client on `window.openPanel` for the card-6
1051
+ * `useOpenPanelAnalytics()` hook to consume.
1052
+ * 3. If `connectConfig.openPanel.trackOperations`, registers the processor
1053
+ * factory against `processorManager` so document operations are forwarded.
1054
+ * 4. Calls `client.identify()` when the renown user transitions from
1055
+ * `undefined` defined (login). The profile ID is the wallet address —
1056
+ * the cross-app key shared with Renown and Vetra.
1057
+ * 5. Calls `client.clear()` when the user transitions from defined →
1058
+ * `undefined` (logout).
1059
+ *
1060
+ * Teardown (unmount **or** consent revocation):
1061
+ * - `unregisterFactory("openpanel")` to remove the processor.
1062
+ * - `resetOpenPanelClient()` to clear the singleton.
1063
+ * - `window.openPanel` is cleared.
1064
+ *
1065
+ * Mirrors the Sentry pattern in `store/user.ts` for login/logout detection.
1066
+ * Analytics failures are caught and forwarded to `console.warn` — they must
1067
+ * never throw into the application.
1068
+ *
1069
+ * Mounted next to `<Analytics />` in `app.tsx`.
1070
+ */
1071
+ function OpenPanel() {
1072
+ const [{ analytics }] = useAcceptedCookies();
1073
+ const user = useUser();
1074
+ const reactorClientModule = useReactorClientModule();
1075
+ const [client, setClient] = useState();
1076
+ /**
1077
+ * Tracks the previous `user` value so we can detect the two transitions:
1078
+ * undefined → defined (login)
1079
+ * defined → undefined (logout)
1080
+ */
1081
+ const prevUserRef = useRef(void 0);
1082
+ const enabled = analytics && !!connectConfig.openPanel.clientId;
1083
+ const processorManager = reactorClientModule?.reactorModule?.processorManager;
1084
+ useEffect(() => {
1085
+ if (!enabled) return;
1086
+ let cancelled = false;
1087
+ const isCancelled = () => cancelled;
1088
+ const setup = async () => {
1089
+ let builtClient;
1090
+ try {
1091
+ builtClient = await getOpenPanelClient(connectConfig.openPanel);
1092
+ } catch (err) {
1093
+ console.warn("[OpenPanel] Failed to build client:", err);
1094
+ return;
1095
+ }
1096
+ if (isCancelled() || !builtClient) return;
1097
+ setClient(builtClient);
1098
+ window.openPanel = builtClient;
1099
+ if (connectConfig.openPanel.trackOperations && processorManager) try {
1100
+ await processorManager.registerFactory("openpanel", createOpenPanelProcessorFactory({
1101
+ client: builtClient,
1102
+ events: {
1103
+ mappings: eventMappings,
1104
+ lookupMap: eventLookupMap
1105
+ },
1106
+ startFrom: "current"
1107
+ }));
1108
+ if (isCancelled()) await processorManager.unregisterFactory("openpanel").catch((err) => console.warn("[OpenPanel] Failed to unregister factory after cancellation:", err));
1109
+ } catch (err) {
1110
+ console.warn("[OpenPanel] Failed to register processor factory:", err);
1111
+ }
882
1112
  };
883
- const timer = setTimeout(() => done(() => reject(/* @__PURE__ */ new Error(`IDB delete timed out for ${name} after ${timeoutMs}ms`))), timeoutMs);
884
- req.onsuccess = () => done(() => {
885
- clearTimeout(timer);
886
- resolve();
887
- });
888
- req.onerror = () => done(() => {
889
- clearTimeout(timer);
890
- reject(idbError(req, `IDB delete failed for ${name}`));
891
- });
892
- });
893
- }
894
- async function clearFileData(name) {
895
- const db = await openIdb(name, 21, createFileDataStore);
896
- try {
897
- await new Promise((resolve, reject) => {
898
- const tx = db.transaction(IDB_STORE_NAME, "readwrite");
899
- tx.oncomplete = () => resolve();
900
- tx.onerror = () => reject(idbError(tx, `FILE_DATA clear failed for ${name}`));
901
- tx.onabort = () => reject(idbError(tx, `FILE_DATA clear aborted for ${name}`));
902
- tx.objectStore(IDB_STORE_NAME).clear();
903
- });
904
- } finally {
905
- db.close();
906
- }
907
- }
908
- async function cloneFileData(sourceName, destName) {
909
- const source = await openIdb(sourceName, 21, createFileDataStore);
910
- try {
911
- const dest = await openIdb(destName, 21, createFileDataStore);
912
- try {
913
- const readStore = source.transaction(IDB_STORE_NAME, "readonly").objectStore(IDB_STORE_NAME);
914
- const entries = await new Promise((resolve, reject) => {
915
- const out = [];
916
- const req = readStore.openCursor();
917
- req.onerror = () => reject(idbError(req, `FILE_DATA read cursor failed for ${sourceName}`));
918
- req.onsuccess = () => {
919
- const cursor = req.result;
920
- if (!cursor) return resolve(out);
921
- out.push({
922
- key: cursor.key,
923
- value: cursor.value
924
- });
925
- cursor.continue();
926
- };
927
- });
928
- await new Promise((resolve, reject) => {
929
- const writeTx = dest.transaction(IDB_STORE_NAME, "readwrite");
930
- writeTx.oncomplete = () => resolve();
931
- writeTx.onerror = () => reject(idbError(writeTx, `FILE_DATA write failed for ${destName}`));
932
- writeTx.onabort = () => reject(idbError(writeTx, `FILE_DATA write aborted for ${destName}`));
933
- const writeStore = writeTx.objectStore(IDB_STORE_NAME);
934
- writeStore.clear();
935
- for (const { key, value } of entries) writeStore.put(value, key);
936
- });
937
- } finally {
938
- dest.close();
939
- }
940
- } finally {
941
- source.close();
942
- }
943
- }
944
- function readBackupIndex() {
945
- try {
946
- const raw = localStorage.getItem(BACKUP_INDEX_KEY);
947
- if (!raw) return [];
948
- const parsed = JSON.parse(raw);
949
- return Array.isArray(parsed) ? parsed : [];
950
- } catch {
951
- return [];
952
- }
953
- }
954
- function writeBackupIndex(records) {
955
- try {
956
- localStorage.setItem(BACKUP_INDEX_KEY, JSON.stringify(records));
957
- } catch {}
958
- }
959
- function registerBackup(rec) {
960
- writeBackupIndex([...readBackupIndex(), rec]);
961
- }
962
- function forgetBackup(name) {
963
- writeBackupIndex(readBackupIndex().filter((r) => r.name !== name));
964
- }
965
- async function janitor() {
966
- const cutoff = Date.now() - BACKUP_RETENTION_MS;
967
- const keep = [];
968
- for (const rec of readBackupIndex()) {
969
- if (rec.createdAt >= cutoff) {
970
- keep.push(rec);
971
- continue;
972
- }
973
- try {
974
- await deleteIdb(rec.name);
1113
+ setup();
1114
+ return () => {
1115
+ cancelled = true;
1116
+ prevUserRef.current = void 0;
1117
+ if (connectConfig.openPanel.trackOperations && processorManager) processorManager.unregisterFactory("openpanel").catch((err) => console.warn("[OpenPanel] Failed to unregister processor factory:", err));
1118
+ resetOpenPanelClient();
1119
+ window.openPanel = void 0;
1120
+ setClient(void 0);
1121
+ };
1122
+ }, [enabled, processorManager]);
1123
+ useEffect(() => {
1124
+ if (!enabled || !client) return;
1125
+ const prev = prevUserRef.current;
1126
+ prevUserRef.current = user;
1127
+ if (!prev && user) try {
1128
+ client.identify(buildIdentifyPayload(user));
975
1129
  } catch (err) {
976
- console.warn(`Failed to delete expired pglite backup ${rec.name}`, err);
977
- keep.push(rec);
1130
+ console.warn("[OpenPanel] Failed to identify user:", err);
978
1131
  }
979
- }
980
- writeBackupIndex(keep);
981
- }
982
- function publishPhase(idbName, phase) {
983
- setMigrationStatus({
984
- idbName,
985
- phase
986
- });
987
- }
988
- async function migrateIdbIfNeeded(idbName) {
989
- const major = await readPgVersionFile(idbName);
990
- if (major === null || major === 17) return;
991
- const loader = LEGACY_DUMPERS[major];
992
- if (!loader) throw new Error(`Unsupported legacy PGlite data dir: PG_VERSION=${major} for ${idbName}`);
993
- const backupName = `${BACKUP_PREFIX}${idbName}::${(/* @__PURE__ */ new Date()).toISOString()}`;
994
- const backupRec = {
995
- name: backupName,
996
- idbName,
997
- createdAt: Date.now()
998
- };
999
- publishPhase(idbName, "clone");
1000
- await cloneFileData(idbName, backupName);
1001
- registerBackup(backupRec);
1002
- let sql;
1003
- try {
1004
- publishPhase(idbName, "dump");
1005
- sql = await (await loader())(idbName);
1006
- } catch (err) {
1007
- await deleteIdb(backupName).catch(() => {});
1008
- forgetBackup(backupName);
1009
- throw err;
1010
- }
1011
- try {
1012
- await clearFileData(idbName);
1013
- publishPhase(idbName, "restore");
1014
- const pg = new PGlite(`idb://${idbName.replace(/^\/pglite\//, "")}`, { relaxedDurability: false });
1015
- try {
1016
- await pg.waitReady;
1017
- console.info(`[pglite-migration] Dump preamble (first 500 chars):\n${sql.slice(0, 500)}`);
1018
- try {
1019
- await pg.exec("SET standard_conforming_strings = off;");
1020
- } catch (gucErr) {
1021
- console.warn("[pglite-migration] Could not force standard_conforming_strings=off", gucErr);
1022
- }
1023
- try {
1024
- await pg.exec(sql);
1025
- } catch (execErr) {
1026
- logRestoreFailure(idbName, sql, execErr);
1027
- throw execErr;
1028
- }
1029
- } finally {
1030
- await pg.close();
1132
+ else if (prev && !user) try {
1133
+ client.clear();
1134
+ } catch (err) {
1135
+ console.warn("[OpenPanel] Failed to clear user:", err);
1031
1136
  }
1032
- } catch (err) {
1033
- await rollbackFromBackup(idbName, backupName, err);
1034
- throw err;
1035
- }
1036
- }
1037
- function logRestoreFailure(idbName, sql, err) {
1038
- const errObj = err;
1039
- const position = typeof errObj.position === "string" ? parseInt(errObj.position, 10) : typeof errObj.position === "number" ? errObj.position : NaN;
1040
- console.error(`[pglite-migration] Restore failed for ${idbName}`, {
1041
- message: errObj.message,
1042
- code: errObj.code,
1043
- severity: errObj.severity,
1044
- detail: errObj.detail,
1045
- where: errObj.where,
1046
- position: errObj.position,
1047
- sqlLength: sql.length
1048
- });
1049
- if (Number.isFinite(position) && position > 0) {
1050
- const zeroBased = position - 1;
1051
- const start = Math.max(0, zeroBased - 200);
1052
- const end = Math.min(sql.length, zeroBased + 200);
1053
- const before = sql.slice(start, zeroBased);
1054
- const at = sql.slice(zeroBased, zeroBased + 1);
1055
- const after = sql.slice(zeroBased + 1, end);
1056
- console.error(`[pglite-migration] SQL context around position ${position}:\n${before}»${at}«${after}`);
1057
- const lineStart = sql.lastIndexOf("\n", zeroBased) + 1;
1058
- const lineEnd = sql.indexOf("\n", zeroBased);
1059
- const line = sql.slice(lineStart, lineEnd === -1 ? sql.length : lineEnd);
1060
- console.error(`[pglite-migration] Failing line (truncated to 500 chars):\n${line.slice(0, 500)}`);
1061
- } else console.error(`[pglite-migration] No position info. First 2000 chars of dump:\n${sql.slice(0, 2e3)}`);
1062
- }
1063
- async function rollbackFromBackup(idbName, backupName, originalError) {
1064
- try {
1065
- await clearFileData(idbName).catch(() => {});
1066
- await cloneFileData(backupName, idbName);
1067
- } catch (rollbackErr) {
1068
- console.error(`PGlite migration failed AND rollback failed for ${idbName}. Primary error:`, originalError, "Rollback error:", rollbackErr);
1069
- return;
1070
- }
1071
- await deleteIdb(backupName).catch(() => {});
1072
- forgetBackup(backupName);
1073
- }
1074
- async function migrateAllLocked() {
1075
- for (const idbName of PRIMARY_IDB_NAMES) await migrateIdbIfNeeded(idbName);
1076
- setMigrationStatus(null);
1077
- }
1078
- async function migrateAllIfNeeded() {
1079
- janitor().catch((err) => console.warn("PGlite backup janitor failed", err));
1080
- if (!(typeof navigator !== "undefined" && "locks" in navigator && typeof navigator.locks.request === "function")) {
1081
- await migrateAllLocked();
1082
- return;
1083
- }
1084
- await navigator.locks.request("ph-pglite-migration", { mode: "exclusive" }, async () => {
1085
- await migrateAllLocked();
1086
- });
1087
- }
1088
- //#endregion
1089
- //#region src/components/app-skeleton.tsx
1090
- const LOADER_DELAY = 250;
1091
- const PHASE_LABEL = {
1092
- clone: "Backing up local database…",
1093
- dump: "Exporting data from previous version…",
1094
- restore: "Restoring data into the new database…"
1095
- };
1096
- const MigrationOverlay = () => {
1097
- const status = useSyncExternalStore(subscribeMigrationStatus, getMigrationStatus, () => null);
1098
- if (!status) return null;
1099
- return /* @__PURE__ */ jsx("div", {
1100
- className: "absolute inset-0 z-20 flex items-center justify-center",
1101
- children: /* @__PURE__ */ jsxs("div", {
1102
- className: "rounded-lg bg-white/90 px-6 py-4 text-sm text-gray-900 shadow-lg dark:bg-slate-900/90 dark:text-slate-50",
1103
- children: [/* @__PURE__ */ jsx("div", {
1104
- className: "font-medium",
1105
- children: "Upgrading local database…"
1106
- }), /* @__PURE__ */ jsx("div", {
1107
- className: "text-gray-700 dark:text-slate-200",
1108
- children: PHASE_LABEL[status.phase]
1109
- })]
1110
- })
1111
- });
1112
- };
1113
- const Loader = ({ delay = LOADER_DELAY }) => {
1114
- const isSSR = typeof window === "undefined";
1115
- const showInitialLoader = typeof document !== "undefined" && document.body.getAttribute("data-show-loader") === "true";
1116
- if (!isSSR) initTheme();
1117
- const [showLoading, setShowLoading] = useState(!delay || showInitialLoader);
1118
- useEffect(() => {
1119
- const id = setTimeout(() => {
1120
- setShowLoading(true);
1121
- }, delay);
1122
- return () => clearTimeout(id);
1123
- }, []);
1124
- return /* @__PURE__ */ jsxs("div", {
1125
- className: `skeleton-loader absolute inset-0 z-10 flex items-center justify-center ${showLoading ? "" : "hidden"}`,
1126
- children: [/* @__PURE__ */ jsx("div", {
1127
- className: "animate-pulse overflow-hidden rounded-full shadow-lg",
1128
- children: /* @__PURE__ */ jsx(LogoAnimation, {})
1129
- }), isSSR ? /* @__PURE__ */ jsx("script", { dangerouslySetInnerHTML: { __html: `setTimeout(() => {
1130
- document.querySelector('.skeleton-loader')?.classList.remove('hidden');
1131
- document.body.setAttribute('data-show-loader', 'true');
1132
- }, ${delay})` } }) : null]
1133
- });
1134
- };
1135
- const AppSkeleton = (props) => {
1136
- const isSSR = typeof window === "undefined";
1137
- const isHomeScreen = !isSSR && window.location.pathname === getBasePath();
1138
- return /* @__PURE__ */ jsxs("div", {
1139
- className: "flex h-screen overflow-hidden bg-gray-50 dark:bg-slate-700",
1140
- children: [
1141
- !(!isSSR && getIsEmbedded()) && /* @__PURE__ */ jsx(ConnectSidebar, {
1142
- className: "animate-pulse",
1143
- onLogin: void 0,
1144
- onDisconnect: void 0,
1145
- onClickSettings: void 0,
1146
- address: void 0
1147
- }),
1148
- /* @__PURE__ */ jsx(HomeScreen, {
1149
- containerClassName: isSSR || !isHomeScreen ? "hidden home-screen" : void 0,
1150
- children: props.children ?? null
1151
- }),
1152
- isSSR ? /* @__PURE__ */ jsx("script", { dangerouslySetInnerHTML: { __html: `
1153
- const baseEl = document.querySelector('base');
1154
- const href = baseEl?.getAttribute('href');
1155
- const basePath = href || '/';
1156
- if (window.location.pathname === basePath) {
1157
- document.querySelector('.home-screen')?.classList.remove('hidden')
1158
- }` } }) : null,
1159
- !props.children ? /* @__PURE__ */ jsx(Loader, {}) : null,
1160
- /* @__PURE__ */ jsx(MigrationOverlay, {})
1161
- ]
1162
- });
1163
- };
1164
- //#endregion
1165
- //#region src/components/migration-banner.tsx
1166
- let dismissed = false;
1167
- const listeners = /* @__PURE__ */ new Set();
1168
- function notify() {
1169
- for (const l of listeners) l();
1170
- }
1171
- function dismiss() {
1172
- dismissed = true;
1173
- notify();
1174
- }
1175
- function subscribeDismissed(cb) {
1176
- listeners.add(cb);
1177
- return () => {
1178
- listeners.delete(cb);
1179
- };
1180
- }
1181
- function getDismissed() {
1182
- return dismissed;
1183
- }
1184
- const MigrationBanner = () => {
1185
- const isDismissed = useSyncExternalStore(subscribeDismissed, getDismissed, () => false);
1186
- const major = useSyncExternalStore(subscribeReactorPgMajor, getCachedReactorPgMajor, () => void 0);
1187
- const [migrating, setMigrating] = useState(false);
1188
- const [error, setError] = useState(null);
1189
- if (major === void 0 || major === null || major === 17) return null;
1190
- if (isDismissed) return null;
1191
- const handleMigrate = () => {
1192
- setError(null);
1193
- setMigrating(true);
1194
- migrateAllIfNeeded().then(() => {
1195
- window.location.reload();
1196
- }).catch((err) => {
1197
- setError(err instanceof Error ? err.message : String(err));
1198
- setMigrating(false);
1199
- });
1200
- };
1201
- return /* @__PURE__ */ jsx("div", {
1202
- className: "absolute inset-x-0 top-0 z-30 flex justify-center p-3",
1203
- children: /* @__PURE__ */ jsxs("div", {
1204
- className: "flex max-w-3xl items-center gap-3 rounded-lg border border-yellow-500 bg-yellow-400 px-4 py-3 text-sm text-gray-900 shadow-lg dark:border-yellow-400 dark:bg-yellow-500 dark:text-slate-50",
1205
- children: [
1206
- /* @__PURE__ */ jsxs("div", {
1207
- className: "flex-1",
1208
- children: [
1209
- /* @__PURE__ */ jsxs("div", {
1210
- className: "font-semibold",
1211
- children: [
1212
- "Your local database uses Postgres ",
1213
- major,
1214
- "."
1215
- ]
1216
- }),
1217
- /* @__PURE__ */ jsxs("div", {
1218
- className: "text-gray-900 dark:text-slate-100",
1219
- children: [
1220
- "Migrate to Postgres ",
1221
- 17,
1222
- " to pick up the latest features. Your data will be backed up first."
1223
- ]
1224
- }),
1225
- error && /* @__PURE__ */ jsxs("div", {
1226
- className: "mt-2 font-medium text-red-900 dark:text-red-400",
1227
- children: ["Migration failed: ", error]
1228
- })
1229
- ]
1230
- }),
1231
- /* @__PURE__ */ jsx("button", {
1232
- type: "button",
1233
- disabled: migrating,
1234
- onClick: handleMigrate,
1235
- className: "rounded-sm bg-gray-900 px-3 py-1 text-sm font-medium text-white hover:bg-gray-800 disabled:opacity-50 dark:bg-slate-50 dark:text-slate-900 dark:hover:bg-slate-100",
1236
- children: migrating ? "Migrating…" : "Migrate now"
1237
- }),
1238
- /* @__PURE__ */ jsx("button", {
1239
- type: "button",
1240
- disabled: migrating,
1241
- onClick: dismiss,
1242
- className: "rounded-sm border border-gray-900 bg-transparent px-3 py-1 text-sm font-medium text-gray-900 hover:bg-gray-900/10 disabled:opacity-50 dark:border-slate-50 dark:text-slate-50",
1243
- children: "Dismiss"
1244
- })
1245
- ]
1246
- })
1137
+ }, [
1138
+ client,
1139
+ user,
1140
+ enabled
1141
+ ]);
1142
+ return null;
1143
+ }
1144
+ //#endregion
1145
+ //#region src/components/document-editor-container.tsx
1146
+ function DocumentEditorContainer() {
1147
+ const [selectedDocument] = useSelectedDocument();
1148
+ const parentFolder = useNodeParentFolderById(selectedDocument.header.id);
1149
+ useMemo(() => {
1150
+ return async () => {};
1151
+ }, []);
1152
+ useCallback(() => {
1153
+ setSelectedNode(parentFolder);
1154
+ }, [parentFolder]);
1155
+ return /* @__PURE__ */ jsx("div", {
1156
+ id: "document-editor-container",
1157
+ className: "flex-1",
1158
+ "data-document-type": selectedDocument.header.documentType,
1159
+ children: /* @__PURE__ */ jsx(DocumentEditor, { document: selectedDocument })
1247
1160
  });
1248
- };
1161
+ }
1249
1162
  //#endregion
1250
- //#region src/components/modal/modals-container.tsx
1251
- const AddDriveModal$1 = lazy(() => import("./AddDriveModal-Cg2qcYp6.js").then((m) => ({ default: m.AddDriveModal })));
1252
- const ClearStorageModal = lazy(() => import("./ClearStorageModal-D2K3LkHV.js").then((m) => ({ default: m.ClearStorageModal })));
1253
- const CookiesPolicyModal = lazy(() => import("./CookiesPolicyModal-4QzcllDR.js").then((m) => ({ default: m.CookiesPolicyModal })));
1254
- const CreateDocumentModal$1 = lazy(() => import("./CreateDocumentModal-7vgZ8nbM.js").then((m) => ({ default: m.CreateDocumentModal })));
1255
- const DebugSettingsModal = lazy(() => import("./DebugSettingsModal-B1FRL4rp.js").then((m) => ({ default: m.DebugSettingsModal })));
1256
- const DeleteDriveModal = lazy(() => import("./DeleteDriveModal-ABue_rOY.js").then((m) => ({ default: m.DeleteDriveModal })));
1257
- const DeleteItemModal = lazy(() => import("./DeleteItemModal-B6aHnYPD.js").then((m) => ({ default: m.DeleteItemModal })));
1258
- const DisclaimerModal = lazy(() => import("./DisclaimerModal-CfDeiBz8.js").then((m) => ({ default: m.DisclaimerModal })));
1259
- const DriveSettingsModal$1 = lazy(() => import("./DriveSettingsModal-CNtCO4-y.js").then((m) => ({ default: m.DriveSettingsModal })));
1260
- const DownloadDocumentWithErrorsModal = lazy(() => import("./DownloadDocumentWithErrorsModal-CaAPGg04.js").then((m) => ({ default: m.DownloadDocumentWithErrorsModal })));
1261
- const SettingsModal$1 = lazy(() => import("./SettingsModal-5dMD1lq9.js").then((m) => ({ default: m.SettingsModal })));
1262
- const UpgradeDriveModal = lazy(() => import("./UpgradeDriveModal-BwI5E5k3.js").then((m) => ({ default: m.UpgradeDriveModal })));
1263
- const modalComponents = {
1264
- addDrive: AddDriveModal$1,
1265
- clearStorage: ClearStorageModal,
1266
- cookiesPolicy: CookiesPolicyModal,
1267
- createDocument: CreateDocumentModal$1,
1268
- debugSettings: DebugSettingsModal,
1269
- deleteDrive: DeleteDriveModal,
1270
- deleteItem: DeleteItemModal,
1271
- disclaimer: DisclaimerModal,
1272
- driveSettings: DriveSettingsModal$1,
1273
- downloadDocumentWithErrors: DownloadDocumentWithErrorsModal,
1274
- inspector: lazy(() => import("./InspectorModal-ywEL5GKA.js").then((m) => ({ default: m.InspectorModal }))),
1275
- settings: SettingsModal$1,
1276
- upgradeDrive: UpgradeDriveModal,
1277
- missingPackage: lazy(() => import("./MissingPackageModal-CUdcDjSO.js").then((m) => ({ default: m.ConnectMissingPackageModal })))
1278
- };
1279
- const ModalsContainer = lazy(() => {
1280
- return Promise.resolve({ default: () => {
1281
- const phModal = usePHModal();
1282
- if (!phModal?.type) return null;
1283
- const ModalComponent = modalComponents[phModal.type];
1284
- return ModalComponent ? /* @__PURE__ */ jsx(ErrorBoundary$1, {
1285
- variant: "silent",
1286
- loggerContext: ["Connect", "Modals"],
1287
- children: /* @__PURE__ */ jsx(Suspense, {
1288
- fallback: null,
1289
- children: /* @__PURE__ */ jsx(ModalComponent, {})
1290
- })
1291
- }) : null;
1292
- } });
1293
- });
1163
+ //#region src/components/editor-loader.tsx
1164
+ function EditorLoader(props) {
1165
+ const [showLoading, setShowLoading] = useState(false);
1166
+ useEffect(() => {
1167
+ setTimeout(() => {
1168
+ setShowLoading(true);
1169
+ }, props.loadingTimeout ?? 200);
1170
+ }, [props]);
1171
+ if (!showLoading) return null;
1172
+ const { customEditorLoader, ...defaultProps } = props;
1173
+ if (customEditorLoader) return /* @__PURE__ */ jsx(Fragment, { children: customEditorLoader });
1174
+ return /* @__PURE__ */ jsx(DefaultEditorLoader, { ...defaultProps });
1175
+ }
1294
1176
  //#endregion
1295
- //#region src/components/app-loader.tsx
1296
- const AppLoader = (props) => {
1297
- const Load = lazy(() => import("./load-D-7n7rPU.js").then((m) => m.loadComponent(props.localPackage)));
1298
- return /* @__PURE__ */ jsx(StrictMode, { children: /* @__PURE__ */ jsxs(ErrorBoundary$1, {
1299
- fallbackRender: (props) => /* @__PURE__ */ jsx(AppSkeleton, { children: /* @__PURE__ */ jsx(DetailedFallback, { ...props }) }),
1300
- resetKeys: [props.localPackage],
1301
- loggerContext: ["Connect"],
1302
- children: [
1303
- /* @__PURE__ */ jsx(Suspense, {
1304
- fallback: /* @__PURE__ */ jsx(AppSkeleton, {}),
1305
- name: "AppLoader",
1306
- children: /* @__PURE__ */ jsx(Load, {
1307
- ...props,
1308
- children: /* @__PURE__ */ jsx(App, {})
1309
- })
1310
- }),
1311
- /* @__PURE__ */ jsx(Suspense, {
1312
- name: "CookieBanner",
1313
- children: /* @__PURE__ */ jsx(CookieBanner$1, {})
1314
- }),
1315
- /* @__PURE__ */ jsx(Suspense, {
1316
- name: "ModalsContainer",
1317
- children: /* @__PURE__ */ jsx(ModalsContainer, {})
1318
- }),
1319
- /* @__PURE__ */ jsx(MigrationBanner, {})
1320
- ]
1321
- }) });
1322
- };
1177
+ //#region src/components/app-container.tsx
1178
+ function AppContainer() {
1179
+ const [selectedDrive] = useSelectedDriveSafe();
1180
+ const selectedDocumentId = useSelectedDocumentId();
1181
+ const app = useAppModuleById(selectedDrive?.header.meta?.preferredEditor);
1182
+ const defaultApp = useDefaultAppModule();
1183
+ if (!selectedDrive) return /* @__PURE__ */ jsx(EditorLoader, {});
1184
+ const AppComponent = app?.Component ?? defaultApp?.Component ?? GenericDriveExplorer.Component;
1185
+ if (!AppComponent) throw new Error("No app component found");
1186
+ return /* @__PURE__ */ jsx(ErrorBoundary$1, {
1187
+ variant: "detailed",
1188
+ resetKeys: [selectedDrive.header.id],
1189
+ loggerContext: ["Connect", "App"],
1190
+ children: /* @__PURE__ */ jsx(Suspense, {
1191
+ fallback: /* @__PURE__ */ jsx(EditorLoader, {}),
1192
+ children: /* @__PURE__ */ jsx(DropZoneWrapper, {
1193
+ className: "flex h-full flex-col overflow-auto",
1194
+ children: /* @__PURE__ */ jsx(AppComponent, { children: selectedDocumentId ? /* @__PURE__ */ jsx(DocumentEditorContainer, {}) : null })
1195
+ })
1196
+ })
1197
+ });
1198
+ }
1323
1199
  //#endregion
1324
1200
  //#region src/context/sentry-provider.tsx
1325
1201
  const SentryProvider = ({ children }) => {
@@ -1775,10 +1651,14 @@ function DriveItem({ drive }) {
1775
1651
  }
1776
1652
  function HomeScreenContainer() {
1777
1653
  const drives = useDrives();
1778
- const config = connectConfig;
1779
- return /* @__PURE__ */ jsxs(HomeScreen, { children: [drives?.map((drive) => {
1780
- return /* @__PURE__ */ jsx(DriveItem, { drive }, drive.header.id);
1781
- }), config.drives.addDriveEnabled && /* @__PURE__ */ jsx(HomeScreenAddDriveItem, {})] });
1654
+ const isAddDriveEnabled = useIsAddDriveEnabled();
1655
+ const homeBackground = getRuntimeConfig().connect?.branding?.homeBackground;
1656
+ return /* @__PURE__ */ jsxs(HomeScreen, {
1657
+ homeBackground,
1658
+ children: [drives?.map((drive) => {
1659
+ return /* @__PURE__ */ jsx(DriveItem, { drive }, drive.header.id);
1660
+ }), isAddDriveEnabled && /* @__PURE__ */ jsx(HomeScreenAddDriveItem, {})]
1661
+ });
1782
1662
  }
1783
1663
  //#endregion
1784
1664
  //#region src/components/router.tsx
@@ -1817,6 +1697,7 @@ const Router = () => {
1817
1697
  function Sidebar() {
1818
1698
  const user = useUser();
1819
1699
  const drives = useDrives();
1700
+ const isAddDriveEnabled = useIsAddDriveEnabled();
1820
1701
  const [selectedDrive] = useSelectedDriveSafe();
1821
1702
  const connectDebug = localStorage.getItem("CONNECT_DEBUG") === "true";
1822
1703
  const ensName = user?.ens?.name || user?.profile?.username || void 0;
@@ -1852,12 +1733,171 @@ function Sidebar() {
1852
1733
  onClick: () => setSelectedDrive(drive),
1853
1734
  active: selectedDrive?.header.id === drive.header.id,
1854
1735
  icon: /* @__PURE__ */ jsx(DriveIcon, { drive })
1855
- }, index)), connectConfig.drives.addDriveEnabled && /* @__PURE__ */ jsx(SidebarAddDriveItem, { onClick: onAddDriveClick })]
1736
+ }, index)), isAddDriveEnabled && /* @__PURE__ */ jsx(SidebarAddDriveItem, { onClick: onAddDriveClick })]
1856
1737
  })
1857
1738
  }) });
1858
1739
  }
1859
1740
  //#endregion
1860
- export { AppLoader as t };
1741
+ //#region src/components/migration-banner.tsx
1742
+ let dismissed = false;
1743
+ const listeners = /* @__PURE__ */ new Set();
1744
+ function notify() {
1745
+ for (const l of listeners) l();
1746
+ }
1747
+ function dismiss() {
1748
+ dismissed = true;
1749
+ notify();
1750
+ }
1751
+ function subscribeDismissed(cb) {
1752
+ listeners.add(cb);
1753
+ return () => {
1754
+ listeners.delete(cb);
1755
+ };
1756
+ }
1757
+ function getDismissed() {
1758
+ return dismissed;
1759
+ }
1760
+ const MigrationBanner = () => {
1761
+ const isDismissed = useSyncExternalStore(subscribeDismissed, getDismissed, () => false);
1762
+ const major = useSyncExternalStore(subscribeReactorPgMajor, getCachedReactorPgMajor, () => void 0);
1763
+ const [migrating, setMigrating] = useState(false);
1764
+ const [error, setError] = useState(null);
1765
+ if (major === void 0 || major === null || major === 17) return null;
1766
+ if (isDismissed) return null;
1767
+ const handleMigrate = () => {
1768
+ setError(null);
1769
+ setMigrating(true);
1770
+ migrateAllIfNeeded().then(() => {
1771
+ window.location.reload();
1772
+ }).catch((err) => {
1773
+ setError(err instanceof Error ? err.message : String(err));
1774
+ setMigrating(false);
1775
+ });
1776
+ };
1777
+ return /* @__PURE__ */ jsx("div", {
1778
+ className: "absolute inset-x-0 top-0 z-30 flex justify-center p-3",
1779
+ children: /* @__PURE__ */ jsxs("div", {
1780
+ className: "flex max-w-3xl items-center gap-3 rounded-lg border border-yellow-500 bg-yellow-400 px-4 py-3 text-sm text-gray-900 shadow-lg dark:border-yellow-400 dark:bg-yellow-500 dark:text-slate-50",
1781
+ children: [
1782
+ /* @__PURE__ */ jsxs("div", {
1783
+ className: "flex-1",
1784
+ children: [
1785
+ /* @__PURE__ */ jsxs("div", {
1786
+ className: "font-semibold",
1787
+ children: [
1788
+ "Your local database uses Postgres ",
1789
+ major,
1790
+ "."
1791
+ ]
1792
+ }),
1793
+ /* @__PURE__ */ jsxs("div", {
1794
+ className: "text-gray-900 dark:text-slate-100",
1795
+ children: [
1796
+ "Migrate to Postgres ",
1797
+ 17,
1798
+ " to pick up the latest features. Your data will be backed up first."
1799
+ ]
1800
+ }),
1801
+ error && /* @__PURE__ */ jsxs("div", {
1802
+ className: "mt-2 font-medium text-red-900 dark:text-red-400",
1803
+ children: ["Migration failed: ", error]
1804
+ })
1805
+ ]
1806
+ }),
1807
+ /* @__PURE__ */ jsx("button", {
1808
+ type: "button",
1809
+ disabled: migrating,
1810
+ onClick: handleMigrate,
1811
+ className: "rounded-sm bg-gray-900 px-3 py-1 text-sm font-medium text-white hover:bg-gray-800 disabled:opacity-50 dark:bg-slate-50 dark:text-slate-900 dark:hover:bg-slate-100",
1812
+ children: migrating ? "Migrating…" : "Migrate now"
1813
+ }),
1814
+ /* @__PURE__ */ jsx("button", {
1815
+ type: "button",
1816
+ disabled: migrating,
1817
+ onClick: dismiss,
1818
+ className: "rounded-sm border border-gray-900 bg-transparent px-3 py-1 text-sm font-medium text-gray-900 hover:bg-gray-900/10 disabled:opacity-50 dark:border-slate-50 dark:text-slate-50",
1819
+ children: "Dismiss"
1820
+ })
1821
+ ]
1822
+ })
1823
+ });
1824
+ };
1825
+ //#endregion
1826
+ //#region src/components/modal/modals-container.tsx
1827
+ const AddDriveModal$1 = lazy(() => import("./AddDriveModal-kzL41qdg.js").then((m) => ({ default: m.AddDriveModal })));
1828
+ const ClearStorageModal = lazy(() => import("./ClearStorageModal-DIKwqiia.js").then((m) => ({ default: m.ClearStorageModal })));
1829
+ const CookiesPolicyModal = lazy(() => import("./CookiesPolicyModal-1nOh6oN3.js").then((m) => ({ default: m.CookiesPolicyModal })));
1830
+ const CreateDocumentModal$1 = lazy(() => import("./CreateDocumentModal-nQbLVDq3.js").then((m) => ({ default: m.CreateDocumentModal })));
1831
+ const DebugSettingsModal = lazy(() => import("./DebugSettingsModal-CJunSkZB.js").then((m) => ({ default: m.DebugSettingsModal })));
1832
+ const DeleteDriveModal = lazy(() => import("./DeleteDriveModal-6P0T1QgD.js").then((m) => ({ default: m.DeleteDriveModal })));
1833
+ const DeleteItemModal = lazy(() => import("./DeleteItemModal-BmV1aQyZ.js").then((m) => ({ default: m.DeleteItemModal })));
1834
+ const DisclaimerModal = lazy(() => import("./DisclaimerModal-CVTFfN0_.js").then((m) => ({ default: m.DisclaimerModal })));
1835
+ const DriveSettingsModal$1 = lazy(() => import("./DriveSettingsModal-L5sM-rIh.js").then((m) => ({ default: m.DriveSettingsModal })));
1836
+ const DownloadDocumentWithErrorsModal = lazy(() => import("./DownloadDocumentWithErrorsModal-CXeFUtaz.js").then((m) => ({ default: m.DownloadDocumentWithErrorsModal })));
1837
+ const SettingsModal$1 = lazy(() => import("./SettingsModal-Dz3u5m_G.js").then((m) => ({ default: m.SettingsModal })));
1838
+ const UpgradeDriveModal = lazy(() => import("./UpgradeDriveModal-CyAUDCU7.js").then((m) => ({ default: m.UpgradeDriveModal })));
1839
+ const modalComponents = {
1840
+ addDrive: AddDriveModal$1,
1841
+ clearStorage: ClearStorageModal,
1842
+ cookiesPolicy: CookiesPolicyModal,
1843
+ createDocument: CreateDocumentModal$1,
1844
+ debugSettings: DebugSettingsModal,
1845
+ deleteDrive: DeleteDriveModal,
1846
+ deleteItem: DeleteItemModal,
1847
+ disclaimer: DisclaimerModal,
1848
+ driveSettings: DriveSettingsModal$1,
1849
+ downloadDocumentWithErrors: DownloadDocumentWithErrorsModal,
1850
+ inspector: lazy(() => import("./InspectorModal-C7pIf6OO.js").then((m) => ({ default: m.InspectorModal }))),
1851
+ settings: SettingsModal$1,
1852
+ upgradeDrive: UpgradeDriveModal,
1853
+ missingPackage: lazy(() => import("./MissingPackageModal-D_N96M6a.js").then((m) => ({ default: m.ConnectMissingPackageModal })))
1854
+ };
1855
+ const ModalsContainer = lazy(() => {
1856
+ return Promise.resolve({ default: () => {
1857
+ const phModal = usePHModal();
1858
+ if (!phModal?.type) return null;
1859
+ const ModalComponent = modalComponents[phModal.type];
1860
+ return ModalComponent ? /* @__PURE__ */ jsx(ErrorBoundary$1, {
1861
+ variant: "silent",
1862
+ loggerContext: ["Connect", "Modals"],
1863
+ children: /* @__PURE__ */ jsx(Suspense, {
1864
+ fallback: null,
1865
+ children: /* @__PURE__ */ jsx(ModalComponent, {})
1866
+ })
1867
+ }) : null;
1868
+ } });
1869
+ });
1870
+ //#endregion
1871
+ //#region src/components/app-loader.tsx
1872
+ const AppLoader = (props) => {
1873
+ const Load = lazy(() => import("./load-DalJBk1C.js").then((m) => m.loadComponent(props.localPackage)));
1874
+ return /* @__PURE__ */ jsx(StrictMode, { children: /* @__PURE__ */ jsxs(ErrorBoundary$1, {
1875
+ fallbackRender: (props) => /* @__PURE__ */ jsx(AppSkeleton, { children: /* @__PURE__ */ jsx(DetailedFallback, { ...props }) }),
1876
+ resetKeys: [props.localPackage],
1877
+ loggerContext: ["Connect"],
1878
+ children: [
1879
+ /* @__PURE__ */ jsx(Suspense, {
1880
+ fallback: /* @__PURE__ */ jsx(AppSkeleton, {}),
1881
+ name: "AppLoader",
1882
+ children: /* @__PURE__ */ jsx(Load, {
1883
+ ...props,
1884
+ children: /* @__PURE__ */ jsx(App, {})
1885
+ })
1886
+ }),
1887
+ /* @__PURE__ */ jsx(Suspense, {
1888
+ name: "CookieBanner",
1889
+ children: /* @__PURE__ */ jsx(CookieBanner$1, {})
1890
+ }),
1891
+ /* @__PURE__ */ jsx(Suspense, {
1892
+ name: "ModalsContainer",
1893
+ children: /* @__PURE__ */ jsx(ModalsContainer, {})
1894
+ }),
1895
+ /* @__PURE__ */ jsx(MigrationBanner, {})
1896
+ ]
1897
+ }) });
1898
+ };
1899
+ //#endregion
1900
+ 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 };
1861
1901
 
1862
- //# sourceMappingURL=sidebar-CO3JjI3G.js.map
1863
- //# debugId=239c52db-b664-5e9c-ac37-c27d54ef59b8
1902
+ //# sourceMappingURL=app-loader-CCPp_d0K.js.map
1903
+ //# debugId=ce32a32d-843f-5968-a8c8-3c3a319dc387