@powerhousedao/connect 6.2.3-dev.10 → 6.2.3-dev.12
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.
- package/dist/{AddDriveModal-xAw48kdl.js → AddDriveModal-CW-ZSWkf.js} +5 -5
- package/dist/{AddDriveModal-xAw48kdl.js.map → AddDriveModal-CW-ZSWkf.js.map} +1 -1
- package/dist/{ClearStorageModal-BHLFpni1.js → ClearStorageModal-DPeEp0Uy.js} +5 -5
- package/dist/{ClearStorageModal-BHLFpni1.js.map → ClearStorageModal-DPeEp0Uy.js.map} +1 -1
- package/dist/{ConfirmDocumentUpgradeModal-FUOhZsk7.js → ConfirmDocumentUpgradeModal-BBrFlHp_.js} +6 -6
- package/dist/{ConfirmDocumentUpgradeModal-FUOhZsk7.js.map → ConfirmDocumentUpgradeModal-BBrFlHp_.js.map} +1 -1
- package/dist/{CookiesPolicyModal-BpyPPyxS.js → CookiesPolicyModal-lKdOuTNm.js} +4 -4
- package/dist/{CookiesPolicyModal-BpyPPyxS.js.map → CookiesPolicyModal-lKdOuTNm.js.map} +1 -1
- package/dist/{CreateDocumentModal-C45W8J0V.js → CreateDocumentModal-HmK-78sB.js} +4 -4
- package/dist/{CreateDocumentModal-C45W8J0V.js.map → CreateDocumentModal-HmK-78sB.js.map} +1 -1
- package/dist/{DebugSettingsModal-cFjT1zO6.js → DebugSettingsModal-ViAF8fXV.js} +7 -7
- package/dist/{DebugSettingsModal-cFjT1zO6.js.map → DebugSettingsModal-ViAF8fXV.js.map} +1 -1
- package/dist/{DeleteDriveModal-Dug84t1j.js → DeleteDriveModal-Cyw6hwc2.js} +5 -5
- package/dist/{DeleteDriveModal-Dug84t1j.js.map → DeleteDriveModal-Cyw6hwc2.js.map} +1 -1
- package/dist/{DeleteItemModal-mMzy3V8o.js → DeleteItemModal-CiFwstZx.js} +5 -5
- package/dist/{DeleteItemModal-mMzy3V8o.js.map → DeleteItemModal-CiFwstZx.js.map} +1 -1
- package/dist/{DisclaimerModal-DvpD5dPM.js → DisclaimerModal-DIRsjUvu.js} +4 -4
- package/dist/{DisclaimerModal-DvpD5dPM.js.map → DisclaimerModal-DIRsjUvu.js.map} +1 -1
- package/dist/{DocumentVersionUnsupportedModal-C3Q0V75l.js → DocumentVersionUnsupportedModal-KftC3-jr.js} +4 -4
- package/dist/{DocumentVersionUnsupportedModal-C3Q0V75l.js.map → DocumentVersionUnsupportedModal-KftC3-jr.js.map} +1 -1
- package/dist/{DownloadDocumentWithErrorsModal-IKl0ljQb.js → DownloadDocumentWithErrorsModal-CxsR74d-.js} +4 -4
- package/dist/{DownloadDocumentWithErrorsModal-IKl0ljQb.js.map → DownloadDocumentWithErrorsModal-CxsR74d-.js.map} +1 -1
- package/dist/{DriveAuthRequiredModal-DVK7ZQzO.js → DriveAuthRequiredModal-BoqnnNKT.js} +5 -5
- package/dist/{DriveAuthRequiredModal-DVK7ZQzO.js.map → DriveAuthRequiredModal-BoqnnNKT.js.map} +1 -1
- package/dist/{DriveSettingsModal-C6-mRQiv.js → DriveSettingsModal-DRarHRIb.js} +4 -4
- package/dist/{DriveSettingsModal-C6-mRQiv.js.map → DriveSettingsModal-DRarHRIb.js.map} +1 -1
- package/dist/{InspectorModal-Z7YMEHRm.js → InspectorModal-3Bs1PQPO.js} +8 -8
- package/dist/{InspectorModal-Z7YMEHRm.js.map → InspectorModal-3Bs1PQPO.js.map} +1 -1
- package/dist/{LoginModal-CybbOm3N.js → LoginModal-DhQTxsoG.js} +5 -5
- package/dist/{LoginModal-CybbOm3N.js.map → LoginModal-DhQTxsoG.js.map} +1 -1
- package/dist/{MissingPackageModal-nBnExqO0.js → MissingPackageModal-Bar7ErIs.js} +5 -5
- package/dist/{MissingPackageModal-nBnExqO0.js.map → MissingPackageModal-Bar7ErIs.js.map} +1 -1
- package/dist/{OpenFileDocumentsModal-JcGCx1ta.js → OpenFileDocumentsModal-C8xf6Yiu.js} +6 -6
- package/dist/{OpenFileDocumentsModal-JcGCx1ta.js.map → OpenFileDocumentsModal-C8xf6Yiu.js.map} +1 -1
- package/dist/{SettingsModal-CgqnSXs5.js → SettingsModal-DEjFnBbg.js} +10 -9
- package/dist/{SettingsModal-CgqnSXs5.js.map → SettingsModal-DEjFnBbg.js.map} +1 -1
- package/dist/{UpgradeDriveModal-_8t9slxQ.js → UpgradeDriveModal-C8scFkuq.js} +4 -4
- package/dist/{UpgradeDriveModal-_8t9slxQ.js.map → UpgradeDriveModal-C8scFkuq.js.map} +1 -1
- package/dist/{app-loader-GDy45Yza.js → app-loader-BOmz0_wJ.js} +1102 -1105
- package/dist/app-loader-BOmz0_wJ.js.map +1 -0
- package/dist/app-loader-DNF0HVVm.js +4 -0
- package/dist/app-skeleton-BQts0IHn.js +4 -0
- package/dist/app-skeleton-C6RjKQuy.js +111 -0
- package/dist/app-skeleton-C6RjKQuy.js.map +1 -0
- package/dist/boot-uobbAyWT.js +210 -0
- package/dist/boot-uobbAyWT.js.map +1 -0
- package/dist/buffer-Bu1eWySI.js +66 -0
- package/dist/buffer-Bu1eWySI.js.map +1 -0
- package/dist/{build-info-BPvkFr0E.js → build-info-D0HdsjsO.js} +5 -5
- package/dist/{build-info-BPvkFr0E.js.map → build-info-D0HdsjsO.js.map} +1 -1
- package/dist/components-C00AmqoA.js +5 -0
- package/dist/{connect.config-DaHDEUrw.js → connect.config-BofwIsWJ.js} +4 -3
- package/dist/{connect.config-DaHDEUrw.js.map → connect.config-BofwIsWJ.js.map} +1 -1
- package/dist/{connection-state-3w4liC1X.js → connection-state-DAqW06pk.js} +5 -5
- package/dist/{connection-state-3w4liC1X.js.map → connection-state-DAqW06pk.js.map} +1 -1
- package/dist/{cookie-banner-BH6bIq24.js → cookie-banner-BQescTYk.js} +6 -6
- package/dist/{cookie-banner-BH6bIq24.js.map → cookie-banner-BQescTYk.js.map} +1 -1
- package/dist/{i18n-CWO0vYVd.js → i18n-90Q8hAPL.js} +3 -3
- package/dist/{i18n-CWO0vYVd.js.map → i18n-90Q8hAPL.js.map} +1 -1
- package/dist/{load-BJ8uRKR9.js → load-BCywb9js.js} +8 -8
- package/dist/{load-BJ8uRKR9.js.map → load-BCywb9js.js.map} +1 -1
- package/dist/main.js +8 -8
- package/dist/main.js.map +1 -1
- package/dist/package-manager-CKEdIJGv.js +475 -0
- package/dist/package-manager-CKEdIJGv.js.map +1 -0
- package/dist/{pglite-major-B39aOEiE.js → pglite-major-D3sdYMs2.js} +3 -3
- package/dist/{pglite-major-B39aOEiE.js.map → pglite-major-D3sdYMs2.js.map} +1 -1
- package/dist/{pglite-migrate-core-Boxqqx-c.js → pglite-migrate-core-BIWwtl5e.js} +4 -4
- package/dist/{pglite-migrate-core-Boxqqx-c.js.map → pglite-migrate-core-BIWwtl5e.js.map} +1 -1
- package/dist/{pglite-seed-BqAUvNFw.js → pglite-seed-ChNTfFkc.js} +4 -4
- package/dist/{pglite-seed-BqAUvNFw.js.map → pglite-seed-ChNTfFkc.js.map} +1 -1
- package/dist/{preferences-store-_5Cw4_YV.js → preferences-store-Coa1SHvi.js} +6 -25
- package/dist/preferences-store-Coa1SHvi.js.map +1 -0
- package/dist/{pwa-manifest-link-DIgOSo2Q.js → pwa-manifest-link-B38lBmEu.js} +3 -3
- package/dist/{pwa-manifest-link-DIgOSo2Q.js.map → pwa-manifest-link-B38lBmEu.js.map} +1 -1
- package/dist/{reactor-CbgxQyev.js → reactor-DR2eIP5w.js} +26 -421
- package/dist/reactor-DR2eIP5w.js.map +1 -0
- package/dist/reactor.worker.js +29 -13
- package/dist/reactor.worker.js.map +1 -1
- package/dist/{registerServiceWorker-BSzbNfvz.js → registerServiceWorker-BAbPbSW4.js} +5 -5
- package/dist/{registerServiceWorker-BSzbNfvz.js.map → registerServiceWorker-BAbPbSW4.js.map} +1 -1
- package/dist/start-connect.d.ts.map +1 -1
- package/dist/start-connect.js +6 -8
- package/dist/start-connect.js.map +1 -1
- package/dist/style.css +2057 -0
- package/dist/{toast-BXIGd4Eb.js → toast-CO6ChlzE.js} +3 -3
- package/dist/{toast-BXIGd4Eb.js.map → toast-CO6ChlzE.js.map} +1 -1
- package/dist/{use-renown-login-Cf3va6u_.js → use-renown-login-rS9wfcuG.js} +3 -3
- package/dist/{use-renown-login-Cf3va6u_.js.map → use-renown-login-rS9wfcuG.js.map} +1 -1
- package/dist/useIsEmbedded-B_AIFqBu.js +26 -0
- package/dist/useIsEmbedded-B_AIFqBu.js.map +1 -0
- package/dist/{useRegistryPackages-BnUcL_nZ.js → useRegistryPackages-C2WHmqn4.js} +60 -3
- package/dist/useRegistryPackages-C2WHmqn4.js.map +1 -0
- package/package.json +13 -12
- package/style.css +4 -0
- package/dist/app-loader-GDy45Yza.js.map +0 -1
- package/dist/app-loader-WZecUfE8.js +0 -4
- package/dist/components-Df4NlQZz.js +0 -4
- package/dist/preferences-store-_5Cw4_YV.js.map +0 -1
- package/dist/reactor-CbgxQyev.js.map +0 -1
- package/dist/useRegistryPackages-BnUcL_nZ.js.map +0 -1
|
@@ -1,27 +1,31 @@
|
|
|
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]="
|
|
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]="07080b59-d0d0-5a15-b224-26b85d0fea75")}catch(e){}}();
|
|
3
3
|
import { n as getRuntimeConfig } from "./runtime-config-DL3QjcfM.js";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { n as
|
|
4
|
+
import { n as drainOpenPanelBuffer, t as clearOpenPanelBuffer } from "./buffer-Bu1eWySI.js";
|
|
5
|
+
import { c as idbError } from "./pglite-major-D3sdYMs2.js";
|
|
6
|
+
import { n as cloneFileData, r as migrateIdb } from "./pglite-migrate-core-BIWwtl5e.js";
|
|
7
|
+
import { n as setMigrationStatus, t as AppSkeleton } from "./app-skeleton-C6RjKQuy.js";
|
|
8
|
+
import { n as useIsEmbedded } from "./useIsEmbedded-B_AIFqBu.js";
|
|
9
|
+
import { a as defaultPHDocumentEditorConfig, i as defaultPHAppConfig, r as connectConfig } from "./connect.config-BofwIsWJ.js";
|
|
10
|
+
import { n as useAcceptedCookies, t as setBannerShown } from "./preferences-store-Coa1SHvi.js";
|
|
9
11
|
import { t as setSentryModule } from "./sentry-QXKgQmr2.js";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import { t as
|
|
12
|
+
import { n as dismissToast, r as toast, t as ToastContainer } from "./toast-CO6ChlzE.js";
|
|
13
|
+
import { f as PRIMARY_IDB_NAMES, r as subscribeWorkerConnection, s as getCachedMigrationMajor, t as getWorkerConnectionStatus, u as subscribeMigrationMajor } from "./connection-state-DAqW06pk.js";
|
|
14
|
+
import { n as serviceWorkerManager, r as subscribeServiceWorkerUpdate, t as getServiceWorkerUpdateAvailable } from "./registerServiceWorker-BAbPbSW4.js";
|
|
15
|
+
import { t as useOpenRenownLogin } from "./use-renown-login-rS9wfcuG.js";
|
|
13
16
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
14
|
-
import { useTranslation } from "react-i18next";
|
|
17
|
+
import { Trans, useTranslation } from "react-i18next";
|
|
15
18
|
import React, { StrictMode, Suspense, createElement, lazy, useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import { ConnectSidebar, ConnectTooltipProvider, DefaultEditorLoader, DriveAuthGate, DropZoneWrapper, HomeScreen, HomeScreenAddDriveItem, HomeScreenItem, LogoAnimation, PackageInstallModal, RevisionHistory, SidebarAddDriveItem, SidebarItem, UploadFileListContainer, formatFileSize, mapProgressStageToStatus, useEns } from "@powerhousedao/design-system/connect";
|
|
19
|
-
import { Outlet, RouterProvider, createBrowserRouter, createRoutesFromChildren, matchRoutes, useLocation, useNavigationType } from "react-router-dom";
|
|
20
|
-
import { useHotkeys } from "react-hotkeys-hook";
|
|
19
|
+
import { ConnectSearchBar, ConnectSidebar, ConnectTooltipProvider, DefaultEditorLoader, DriveAuthGate, DropZoneWrapper, Footer, FooterLink, HomeScreen, HomeScreenAddDriveItem, HomeScreenItem, PackageInstallModal, RevisionHistory, SidebarAddDriveItem, SidebarItem, UploadFileListContainer, formatFileSize, mapProgressStageToStatus, useEns } from "@powerhousedao/design-system/connect";
|
|
20
|
+
import { DriveCollectionId, RenownWalletProvider, addFileWithProgress, getRevisionFromDate, 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, useSelectedDriveId, useSelectedDriveSafe, useSelectedFolder, useSelectedTimelineItem, useSync, useSyncList, useTheme, useUser, useVetraPackageManager, useVetraPackages } from "@powerhousedao/reactor-browser";
|
|
21
21
|
import { Icon, Modal, PowerhouseButton } from "@powerhousedao/design-system";
|
|
22
|
+
import { childLogger } from "document-model";
|
|
22
23
|
import { ErrorBoundary } from "react-error-boundary";
|
|
23
24
|
import { z } from "zod";
|
|
24
25
|
import { GenericDriveExplorer } from "@powerhousedao/powerhouse-vetra-packages/editors";
|
|
26
|
+
import { Outlet, RouterProvider, createBrowserRouter, createRoutesFromChildren, matchRoutes, useLocation, useNavigationType } from "react-router-dom";
|
|
27
|
+
import { redo, undo } from "@powerhousedao/shared/document-model";
|
|
28
|
+
import { useHotkeys } from "react-hotkeys-hook";
|
|
25
29
|
import { createReactorMcpProvider } from "@powerhousedao/reactor-mcp/tools";
|
|
26
30
|
import { ReactorChatFab, createSwitchboardSchemaTool } from "@powerhousedao/reactor-browser/ai";
|
|
27
31
|
import { twMerge } from "tailwind-merge";
|
|
@@ -29,487 +33,663 @@ import { mockAdapter } from "@renown/sdk/wallet/mock";
|
|
|
29
33
|
import { privyAdapter } from "@renown/sdk/wallet/privy";
|
|
30
34
|
import { rainbowAdapter } from "@renown/sdk/wallet/rainbow";
|
|
31
35
|
import { arbitrum, base, mainnet, optimism, polygon, sepolia } from "wagmi/chains";
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
function
|
|
35
|
-
|
|
36
|
-
return document.querySelector("base")?.getAttribute("href") || "/";
|
|
37
|
-
}
|
|
38
|
-
//#endregion
|
|
39
|
-
//#region src/utils/document-editor-debug-tools.ts
|
|
40
|
-
var DocumentEditorDebugTools = class {
|
|
41
|
-
document;
|
|
42
|
-
operations = [];
|
|
43
|
-
constructor(document) {
|
|
44
|
-
if (document) this.document = document;
|
|
45
|
-
}
|
|
46
|
-
operationsToTableObject(operations) {
|
|
47
|
-
return operations.map((op) => ({
|
|
48
|
-
...op,
|
|
49
|
-
input: JSON.stringify(op.action.input)
|
|
50
|
-
}));
|
|
51
|
-
}
|
|
52
|
-
setDocument(document) {
|
|
53
|
-
this.document = document;
|
|
54
|
-
}
|
|
55
|
-
getDocument() {
|
|
56
|
-
return this.document;
|
|
57
|
-
}
|
|
58
|
-
getOperations() {
|
|
59
|
-
return this.operations;
|
|
60
|
-
}
|
|
61
|
-
pushOperation(operation) {
|
|
62
|
-
this.operations.push(operation);
|
|
63
|
-
}
|
|
64
|
-
operationsTable() {
|
|
65
|
-
if (!this.document) console.warn("No document");
|
|
66
|
-
const ops = Object.values(this.document?.operations || {}).filter((array) => array !== void 0).flatMap((array) => array).sort((a, b) => a.index - b.index);
|
|
67
|
-
console.table(this.operationsToTableObject(ops));
|
|
68
|
-
}
|
|
69
|
-
scopeOperationsTable(scope) {
|
|
70
|
-
if (!this.document) console.warn("No document");
|
|
71
|
-
const ops = this.document?.operations[scope] || [];
|
|
72
|
-
console.table(this.operationsToTableObject(ops));
|
|
73
|
-
}
|
|
74
|
-
operationsLog() {
|
|
75
|
-
console.log(this.operations);
|
|
76
|
-
}
|
|
77
|
-
operationsLogTable() {
|
|
78
|
-
console.table(this.operationsToTableObject(this.operations));
|
|
79
|
-
}
|
|
80
|
-
clear() {
|
|
81
|
-
this.operations = [];
|
|
82
|
-
this.document = void 0;
|
|
83
|
-
}
|
|
84
|
-
};
|
|
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;
|
|
36
|
+
//#region src/components/error-boundary.tsx
|
|
37
|
+
const logger$2 = childLogger(["Connect"]);
|
|
38
|
+
function hasCustomFallback(props) {
|
|
39
|
+
return "fallbackRender" in props;
|
|
94
40
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
return `${file.name}|${file.size}|${file.lastModified}`;
|
|
41
|
+
function SilentFallback() {
|
|
42
|
+
return null;
|
|
98
43
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
44
|
+
/**
|
|
45
|
+
* Simple text-based fallback component.
|
|
46
|
+
* Displays a centered message when an error occurs.
|
|
47
|
+
*/
|
|
48
|
+
function TextFallback({ message = "Something went wrong" }) {
|
|
49
|
+
return /* @__PURE__ */ jsx("div", {
|
|
50
|
+
className: "text-center wrap-break-word text-foreground",
|
|
51
|
+
children: message
|
|
106
52
|
});
|
|
107
|
-
if (fresh.length === 0) return;
|
|
108
|
-
pendingImportFiles = [...pendingImportFiles, ...fresh];
|
|
109
|
-
notifyPendingListeners();
|
|
110
53
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
54
|
+
/**
|
|
55
|
+
* Detailed fallback component showing full error information.
|
|
56
|
+
* Displays error message with a collapsible details section.
|
|
57
|
+
* Useful for development and debugging purposes.
|
|
58
|
+
*/
|
|
59
|
+
function DetailedFallback({ error, resetErrorBoundary }) {
|
|
60
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
61
|
+
const errorDetails = error instanceof Error ? error.stack ?? String(error.cause) : JSON.stringify(error, null, 2);
|
|
62
|
+
return /* @__PURE__ */ jsx("div", {
|
|
63
|
+
className: "z-10 mx-auto flex max-w-[80%] flex-1 items-center justify-center p-6",
|
|
64
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
65
|
+
className: "w-full max-w-lg rounded-lg border border-border bg-card p-6 text-foreground shadow-sm",
|
|
66
|
+
children: [
|
|
67
|
+
/* @__PURE__ */ jsxs("div", {
|
|
68
|
+
className: "mb-3 flex items-center gap-2",
|
|
69
|
+
children: [/* @__PURE__ */ jsx(Icon, {
|
|
70
|
+
name: "Error",
|
|
71
|
+
className: "size-5 shrink-0 text-destructive"
|
|
72
|
+
}), /* @__PURE__ */ jsx("h1", {
|
|
73
|
+
className: "text-lg font-semibold text-foreground",
|
|
74
|
+
children: "Something went wrong"
|
|
75
|
+
})]
|
|
76
|
+
}),
|
|
77
|
+
/* @__PURE__ */ jsx("p", {
|
|
78
|
+
className: "mb-4 text-sm wrap-break-word text-foreground",
|
|
79
|
+
children: errorMessage
|
|
80
|
+
}),
|
|
81
|
+
errorDetails !== "{}" && /* @__PURE__ */ jsxs("details", {
|
|
82
|
+
className: "group",
|
|
83
|
+
children: [/* @__PURE__ */ jsx("summary", {
|
|
84
|
+
className: "cursor-pointer text-sm font-medium text-foreground underline select-none hover:hover-effect",
|
|
85
|
+
children: "Show details"
|
|
86
|
+
}), /* @__PURE__ */ jsx("pre", {
|
|
87
|
+
className: "mt-2 max-h-48 overflow-auto rounded-sm bg-muted p-3 text-xs text-foreground",
|
|
88
|
+
children: errorDetails
|
|
89
|
+
})]
|
|
90
|
+
}),
|
|
91
|
+
/* @__PURE__ */ jsx(PowerhouseButton, {
|
|
92
|
+
type: "button",
|
|
93
|
+
onClick: resetErrorBoundary,
|
|
94
|
+
className: "mt-4 px-3 py-1.5 text-base font-medium",
|
|
95
|
+
children: "Try again"
|
|
96
|
+
})
|
|
97
|
+
]
|
|
98
|
+
})
|
|
99
|
+
});
|
|
115
100
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
101
|
+
/**
|
|
102
|
+
* Centered error message fallback component.
|
|
103
|
+
* Displays the error message in a centered, full-size container.
|
|
104
|
+
* Suitable for use as a custom fallbackRender when you need simple error display.
|
|
105
|
+
*/
|
|
106
|
+
function CenteredErrorMessage({ error }) {
|
|
107
|
+
return /* @__PURE__ */ jsx("div", {
|
|
108
|
+
className: "flex size-full items-center justify-center p-6",
|
|
109
|
+
children: /* @__PURE__ */ jsx("h3", {
|
|
110
|
+
className: "max-w-lg min-w-0 text-center text-lg font-semibold wrap-break-word text-foreground",
|
|
111
|
+
children: error instanceof Error ? error.message : String(error)
|
|
112
|
+
})
|
|
113
|
+
});
|
|
121
114
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
} catch (error) {
|
|
129
|
-
console.error("Failed to read launched file:", handle.name, error);
|
|
115
|
+
function getFallbackComponent(variant, fallbackMessage) {
|
|
116
|
+
switch (variant) {
|
|
117
|
+
case "silent": return SilentFallback;
|
|
118
|
+
case "text": return () => /* @__PURE__ */ jsx(TextFallback, { message: fallbackMessage });
|
|
119
|
+
case "detailed": return DetailedFallback;
|
|
120
|
+
default: return CenteredErrorMessage;
|
|
130
121
|
}
|
|
131
|
-
if (files.length === 0) return;
|
|
132
|
-
addPendingImportFiles(files);
|
|
133
|
-
showPHModal({ type: "openFileDocuments" });
|
|
134
122
|
}
|
|
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
123
|
/**
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
124
|
+
* Unified error boundary component for catching and handling React errors.
|
|
125
|
+
*
|
|
126
|
+
* Built on top of `react-error-boundary`, this component provides:
|
|
127
|
+
* - Automatic error logging with configurable context
|
|
128
|
+
* - Multiple fallback UI variants (silent, text, detailed)
|
|
129
|
+
* - Support for custom fallback render functions
|
|
130
|
+
* - Reset capability via `resetKeys`
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* // Using a predefined variant
|
|
134
|
+
* <ErrorBoundary variant="detailed" loggerContext={["Connect", "Editor"]}>
|
|
135
|
+
* <EditorComponent />
|
|
136
|
+
* </ErrorBoundary>
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* // Using a custom message with the text variant
|
|
140
|
+
* <ErrorBoundary variant="text" fallbackMessage="Failed to load drives">
|
|
141
|
+
* <DrivesList />
|
|
142
|
+
* </ErrorBoundary>
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* // Using a custom fallback render
|
|
146
|
+
* <ErrorBoundary
|
|
147
|
+
* fallbackRender={CenteredErrorMessage}
|
|
148
|
+
* onError={(error) => trackError(error)}
|
|
149
|
+
* >
|
|
150
|
+
* <MyComponent />
|
|
151
|
+
* </ErrorBoundary>
|
|
152
|
+
*
|
|
153
|
+
* @example
|
|
154
|
+
* // Silent mode for modals (renders null on error)
|
|
155
|
+
* <ErrorBoundary variant="silent" loggerContext={["Connect", "Modals"]}>
|
|
156
|
+
* <ModalContent />
|
|
157
|
+
* </ErrorBoundary>
|
|
147
158
|
*/
|
|
148
|
-
function
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
159
|
+
function ErrorBoundary$1(props) {
|
|
160
|
+
const { children, onError, resetKeys, loggerContext } = props;
|
|
161
|
+
const handleError = (error, info) => {
|
|
162
|
+
if (error.message.includes("Failed to fetch dynamically imported module") && error.message.includes("node_modules/.vite")) {
|
|
163
|
+
console.error(error);
|
|
164
|
+
console.log("Outdated chunk detected, reloading page...");
|
|
165
|
+
window.location.reload();
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
(loggerContext ? childLogger(loggerContext) : logger$2).error("@error @info", error, info);
|
|
169
|
+
onError?.(error, info);
|
|
170
|
+
};
|
|
171
|
+
return /* @__PURE__ */ jsx(ErrorBoundary, {
|
|
172
|
+
fallbackRender: hasCustomFallback(props) ? props.fallbackRender : getFallbackComponent(props.variant, props.fallbackMessage),
|
|
173
|
+
onError: handleError,
|
|
174
|
+
resetKeys,
|
|
175
|
+
children
|
|
154
176
|
});
|
|
155
177
|
}
|
|
156
178
|
//#endregion
|
|
157
|
-
//#region src/
|
|
158
|
-
|
|
159
|
-
|
|
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
|
-
};
|
|
179
|
+
//#region src/components/analytics.tsx
|
|
180
|
+
function gtag(...args) {
|
|
181
|
+
window.dataLayer?.push(...args);
|
|
171
182
|
}
|
|
172
|
-
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
let
|
|
195
|
-
let importChain = Promise.resolve();
|
|
183
|
+
const Analytics = () => {
|
|
184
|
+
const gaTrackingId = connectConfig.gaTrackingId;
|
|
185
|
+
const [{ analytics }] = useAcceptedCookies();
|
|
186
|
+
const useAnalytics = gaTrackingId && analytics;
|
|
187
|
+
useEffect(() => {
|
|
188
|
+
if (useAnalytics) {
|
|
189
|
+
const script = document.createElement("script");
|
|
190
|
+
script.src = `https://www.googletagmanager.com/gtag/js?id=${gaTrackingId}`;
|
|
191
|
+
script.async = true;
|
|
192
|
+
document.head.appendChild(script);
|
|
193
|
+
window.dataLayer = window.dataLayer || [];
|
|
194
|
+
gtag("js", /* @__PURE__ */ new Date());
|
|
195
|
+
gtag("config", gaTrackingId);
|
|
196
|
+
return () => {
|
|
197
|
+
document.head.removeChild(script);
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
}, [useAnalytics, gaTrackingId]);
|
|
201
|
+
return null;
|
|
202
|
+
};
|
|
203
|
+
//#endregion
|
|
204
|
+
//#region src/services/openpanel/client.ts
|
|
205
|
+
let client;
|
|
196
206
|
/**
|
|
197
|
-
*
|
|
198
|
-
*
|
|
199
|
-
*
|
|
200
|
-
* the
|
|
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.
|
|
207
|
+
* Returns the cached `OpenPanel` singleton, building it on first call.
|
|
208
|
+
*
|
|
209
|
+
* When `config.clientId` is empty it returns `undefined` without importing
|
|
210
|
+
* `@openpanel/web`, so the bundler can tree-shake the SDK out of production.
|
|
203
211
|
*/
|
|
204
|
-
function
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
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
|
-
});
|
|
212
|
+
async function getOpenPanelClient(config) {
|
|
213
|
+
if (!config.clientId) return;
|
|
214
|
+
if (client) return client;
|
|
215
|
+
const { OpenPanel: OpenPanelClass } = await import("@openpanel/web");
|
|
216
|
+
client = new OpenPanelClass({
|
|
217
|
+
clientId: config.clientId,
|
|
218
|
+
...config.apiUrl ? { apiUrl: config.apiUrl } : {},
|
|
219
|
+
trackScreenViews: true,
|
|
220
|
+
trackOutgoingLinks: true
|
|
233
221
|
});
|
|
222
|
+
client.setGlobalProperties({ app: "connect" });
|
|
223
|
+
drainOpenPanelBuffer(client);
|
|
224
|
+
return client;
|
|
234
225
|
}
|
|
235
226
|
/**
|
|
236
|
-
*
|
|
237
|
-
*
|
|
238
|
-
* single-file batch that succeeds opens its document in the editor.
|
|
227
|
+
* Clears the cached singleton and the pre-init buffer. Call on consent
|
|
228
|
+
* revocation or during tests; buffered events are discarded, not replayed.
|
|
239
229
|
*/
|
|
240
|
-
function
|
|
241
|
-
|
|
242
|
-
|
|
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
|
|
264
|
-
//#region src/components/migration-status.ts
|
|
265
|
-
let current = null;
|
|
266
|
-
const listeners = /* @__PURE__ */ new Set();
|
|
267
|
-
function setMigrationStatus(status) {
|
|
268
|
-
current = status;
|
|
269
|
-
for (const l of listeners) l();
|
|
270
|
-
}
|
|
271
|
-
function getMigrationStatus() {
|
|
272
|
-
return current;
|
|
273
|
-
}
|
|
274
|
-
function subscribeMigrationStatus(cb) {
|
|
275
|
-
listeners.add(cb);
|
|
276
|
-
return () => {
|
|
277
|
-
listeners.delete(cb);
|
|
278
|
-
};
|
|
230
|
+
function resetOpenPanelClient() {
|
|
231
|
+
client = void 0;
|
|
232
|
+
clearOpenPanelBuffer();
|
|
279
233
|
}
|
|
280
234
|
//#endregion
|
|
281
|
-
//#region src/
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
235
|
+
//#region src/services/openpanel/events.json
|
|
236
|
+
var events_default = [
|
|
237
|
+
{
|
|
238
|
+
"documentType": "powerhouse/document-drive",
|
|
239
|
+
"actionTypes": [
|
|
240
|
+
"ADD_FOLDER",
|
|
241
|
+
"DELETE_NODE",
|
|
242
|
+
"ADD_FILE",
|
|
243
|
+
"UPDATE_FILE",
|
|
244
|
+
"COPY_NODE",
|
|
245
|
+
"MOVE_NODE",
|
|
246
|
+
"SET_DRIVE_NAME",
|
|
247
|
+
"SET_DRIVE_ICON"
|
|
248
|
+
]
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
"documentType": "powerhouse/document-model",
|
|
252
|
+
"actionTypes": [
|
|
253
|
+
"SET_MODEL_NAME",
|
|
254
|
+
"SET_MODEL_ID",
|
|
255
|
+
"ADD_MODULE",
|
|
256
|
+
"DELETE_MODULE",
|
|
257
|
+
"ADD_OPERATION",
|
|
258
|
+
"DELETE_OPERATION"
|
|
259
|
+
]
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
"documentType": "powerhouse/reactor-drive",
|
|
263
|
+
"actionTypes": [
|
|
264
|
+
"ADD_FOLDER",
|
|
265
|
+
"REMOVE_FOLDER",
|
|
266
|
+
"UPDATE_FOLDER"
|
|
267
|
+
]
|
|
313
268
|
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
269
|
+
];
|
|
270
|
+
//#endregion
|
|
271
|
+
//#region src/services/openpanel/events.ts
|
|
272
|
+
const EventMappingSchema = z.object({
|
|
273
|
+
documentType: z.string().min(1),
|
|
274
|
+
actionTypes: z.array(z.string().min(1)).min(1),
|
|
275
|
+
alias: z.string().optional()
|
|
276
|
+
}).strict();
|
|
277
|
+
const EventMappingsSchema = z.array(EventMappingSchema).readonly();
|
|
278
|
+
/**
|
|
279
|
+
* Parses and validates the event mapping table, returning the validated array
|
|
280
|
+
* and an O(1) lookup map. Accepts a `raw` override for tests. Throws on zod
|
|
281
|
+
* failures and on duplicate `(documentType, actionType)` pairs.
|
|
282
|
+
*/
|
|
283
|
+
function loadEvents(raw = events_default) {
|
|
284
|
+
const result = EventMappingsSchema.safeParse(raw);
|
|
285
|
+
if (!result.success) throw new Error(`Invalid OpenPanel events.json: ${result.error.message}`);
|
|
286
|
+
const mappings = result.data;
|
|
287
|
+
const lookupMap = /* @__PURE__ */ new Map();
|
|
288
|
+
for (const mapping of mappings) {
|
|
289
|
+
let inner = lookupMap.get(mapping.documentType);
|
|
290
|
+
if (!inner) {
|
|
291
|
+
inner = /* @__PURE__ */ new Map();
|
|
292
|
+
lookupMap.set(mapping.documentType, inner);
|
|
333
293
|
}
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
console.warn(`Failed to delete expired pglite backup ${rec.name}`, err);
|
|
338
|
-
keep.push(rec);
|
|
294
|
+
for (const actionType of mapping.actionTypes) {
|
|
295
|
+
if (inner.has(actionType)) throw new Error(`Duplicate OpenPanel event mapping for ${mapping.documentType}/${actionType}`);
|
|
296
|
+
inner.set(actionType, mapping);
|
|
339
297
|
}
|
|
340
298
|
}
|
|
341
|
-
writeBackupIndex(keep);
|
|
342
|
-
}
|
|
343
|
-
function createPersistentBackup() {
|
|
344
299
|
return {
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
await cloneFileData(idbName, backupName);
|
|
348
|
-
registerBackup({
|
|
349
|
-
name: backupName,
|
|
350
|
-
idbName,
|
|
351
|
-
createdAt: Date.now()
|
|
352
|
-
});
|
|
353
|
-
return backupName;
|
|
354
|
-
},
|
|
355
|
-
rollback: async (handle, idbName, cause) => {
|
|
356
|
-
const backupName = handle;
|
|
357
|
-
try {
|
|
358
|
-
await cloneFileData(backupName, idbName);
|
|
359
|
-
} catch (rollbackErr) {
|
|
360
|
-
console.error(`PGlite migration rollback failed for ${idbName}; backup ${backupName} retained. Primary migration error:`, cause, "Rollback error:", rollbackErr);
|
|
361
|
-
return;
|
|
362
|
-
}
|
|
363
|
-
await deleteIdb(backupName).catch(() => void 0);
|
|
364
|
-
forgetBackup(backupName);
|
|
365
|
-
},
|
|
366
|
-
discard: async (handle) => {
|
|
367
|
-
const backupName = handle;
|
|
368
|
-
await deleteIdb(backupName).catch(() => void 0);
|
|
369
|
-
forgetBackup(backupName);
|
|
370
|
-
},
|
|
371
|
-
commit: () => Promise.resolve()
|
|
300
|
+
mappings,
|
|
301
|
+
lookupMap
|
|
372
302
|
};
|
|
373
303
|
}
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
304
|
+
const { mappings: defaultMappings, lookupMap: defaultLookupMap } = loadEvents();
|
|
305
|
+
/** Validated mapping array from the bundled events.json. */
|
|
306
|
+
const eventMappings = defaultMappings;
|
|
307
|
+
/** O(1) lookup map: documentType → actionType → mapping. */
|
|
308
|
+
const eventLookupMap = defaultLookupMap;
|
|
309
|
+
/**
|
|
310
|
+
* Normalizes a document type into a safe event-name segment: lowercased, `/`
|
|
311
|
+
* replaced with `.`, and any character outside `[a-z0-9._-]` stripped.
|
|
312
|
+
* e.g. `powerhouse/document-drive` → `powerhouse.document-drive`.
|
|
313
|
+
*/
|
|
314
|
+
function normalize(documentType) {
|
|
315
|
+
return documentType.toLowerCase().replace(/\//g, ".").replace(/[^a-z0-9._-]/g, "");
|
|
384
316
|
}
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
await migrateAllLocked();
|
|
393
|
-
});
|
|
317
|
+
/**
|
|
318
|
+
* Derives the OpenPanel event name: `mapping.alias` when present, otherwise
|
|
319
|
+
* `${normalize(documentType)}.${actionType.toLowerCase()}`.
|
|
320
|
+
*/
|
|
321
|
+
function deriveEventName(mapping, op) {
|
|
322
|
+
if (mapping.alias) return mapping.alias;
|
|
323
|
+
return `${normalize(op.context.documentType)}.${op.operation.action.type.toLowerCase()}`;
|
|
394
324
|
}
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
return
|
|
325
|
+
/**
|
|
326
|
+
* Builds the default properties attached to every OpenPanel event. The `app`
|
|
327
|
+
* property is stamped globally by the client, not here.
|
|
328
|
+
*/
|
|
329
|
+
function buildDefaultProperties(op) {
|
|
330
|
+
return {
|
|
331
|
+
documentType: op.context.documentType,
|
|
332
|
+
actionType: op.operation.action.type,
|
|
333
|
+
documentId: op.context.documentId,
|
|
334
|
+
scope: op.context.scope,
|
|
335
|
+
branch: op.context.branch
|
|
336
|
+
};
|
|
401
337
|
}
|
|
402
338
|
//#endregion
|
|
403
|
-
//#region src/
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
ignoreErrors: [
|
|
430
|
-
"User is not allowed to create files",
|
|
431
|
-
"User is not allowed to move documents",
|
|
432
|
-
"The user aborted a request."
|
|
433
|
-
],
|
|
434
|
-
sendDefaultPii: true,
|
|
435
|
-
tracesSampleRate: 1,
|
|
436
|
-
replaysSessionSampleRate: .1,
|
|
437
|
-
replaysOnErrorSampleRate: 1,
|
|
438
|
-
beforeSend(event, hint) {
|
|
439
|
-
const error = hint.originalException;
|
|
440
|
-
if (error instanceof TypeError && error.message.includes("Failed to fetch") && !navigator.onLine) return null;
|
|
441
|
-
return event;
|
|
442
|
-
}
|
|
443
|
-
});
|
|
444
|
-
setSentryModule(Sentry);
|
|
445
|
-
clientStarted = true;
|
|
446
|
-
}
|
|
447
|
-
async function closeClient() {
|
|
448
|
-
if (!clientStarted) return;
|
|
449
|
-
const sentry = await getSentry();
|
|
450
|
-
setSentryModule(void 0);
|
|
451
|
-
return sentry.getClient()?.close();
|
|
452
|
-
}
|
|
453
|
-
function useInitSentry() {
|
|
454
|
-
const [acceptedCookies] = useAcceptedCookies();
|
|
455
|
-
const { analytics } = acceptedCookies;
|
|
456
|
-
useEffect(() => {
|
|
457
|
-
if (!analytics) {
|
|
458
|
-
closeClient().catch((error) => logger$2.error("@error", error));
|
|
459
|
-
return;
|
|
339
|
+
//#region src/services/openpanel/processor.ts
|
|
340
|
+
/**
|
|
341
|
+
* Read-only reactor `IProcessor` that translates matching document operations
|
|
342
|
+
* into OpenPanel analytics events. Operations whose `(documentType, actionType)`
|
|
343
|
+
* pair is absent from the lookup map are skipped; per-operation errors are
|
|
344
|
+
* routed to `onError` and never thrown back to the manager.
|
|
345
|
+
*/
|
|
346
|
+
var OpenPanelProcessor = class {
|
|
347
|
+
constructor(client, lookupMap, onError = (err) => console.warn("[OpenPanelProcessor] Error tracking event:", err)) {
|
|
348
|
+
this.client = client;
|
|
349
|
+
this.lookupMap = lookupMap;
|
|
350
|
+
this.onError = onError;
|
|
351
|
+
}
|
|
352
|
+
async onOperations(operations) {
|
|
353
|
+
for (const op of operations) {
|
|
354
|
+
const inner = this.lookupMap.get(op.context.documentType);
|
|
355
|
+
if (!inner) continue;
|
|
356
|
+
const mapping = inner.get(op.operation.action.type);
|
|
357
|
+
if (!mapping) continue;
|
|
358
|
+
const name = deriveEventName(mapping, op);
|
|
359
|
+
const payload = buildDefaultProperties(op);
|
|
360
|
+
try {
|
|
361
|
+
await this.client.track(name, payload);
|
|
362
|
+
} catch (err) {
|
|
363
|
+
this.onError(err, op);
|
|
364
|
+
}
|
|
460
365
|
}
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
366
|
+
}
|
|
367
|
+
onDisconnect() {
|
|
368
|
+
this.client.flush?.();
|
|
369
|
+
return Promise.resolve();
|
|
370
|
+
}
|
|
371
|
+
};
|
|
465
372
|
//#endregion
|
|
466
|
-
//#region src/services/openpanel/
|
|
467
|
-
const buffer = [];
|
|
468
|
-
let errorHandler;
|
|
469
|
-
function defaultOnError(err) {
|
|
470
|
-
console.warn("[useOpenPanelAnalytics] track failed:", err);
|
|
471
|
-
}
|
|
373
|
+
//#region src/services/openpanel/factory.ts
|
|
472
374
|
/**
|
|
473
|
-
*
|
|
474
|
-
*
|
|
475
|
-
*
|
|
476
|
-
* inner `.catch()` handles rejected promises. Both are needed because
|
|
477
|
-
* `Promise.resolve(x).catch()` cannot catch a throw from evaluating `x`.
|
|
375
|
+
* Returns a `ProcessorFactory` that, for each drive, creates a single
|
|
376
|
+
* `OpenPanelProcessor` record whose `filter.documentType` is the union of all
|
|
377
|
+
* document types present in the event mapping table.
|
|
478
378
|
*/
|
|
479
|
-
function
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
379
|
+
function createOpenPanelProcessorFactory(config) {
|
|
380
|
+
return function openPanelProcessorFactory(_driveHeader) {
|
|
381
|
+
const documentType = Array.from(config.events.lookupMap.keys());
|
|
382
|
+
return [{
|
|
383
|
+
processor: new OpenPanelProcessor(config.client, config.events.lookupMap, config.onError),
|
|
384
|
+
filter: { documentType },
|
|
385
|
+
startFrom: config.startFrom ?? "current"
|
|
386
|
+
}];
|
|
387
|
+
};
|
|
485
388
|
}
|
|
389
|
+
//#endregion
|
|
390
|
+
//#region src/components/openpanel-traits.ts
|
|
486
391
|
/**
|
|
487
|
-
*
|
|
488
|
-
*
|
|
392
|
+
* Builds the OpenPanel identity traits from a renown User.
|
|
393
|
+
*
|
|
394
|
+
* Canonical trait schema (shared with Renown and Vetra — each app sends every
|
|
395
|
+
* field it has data for, under these names):
|
|
396
|
+
* - `address`, `did`, `networkId`, `chainId`, `caip2` — wallet/chain facts.
|
|
397
|
+
* - `ensName`, `ensAvatar` — raw ENS facts.
|
|
398
|
+
* - `username`, `userImage`, `profileDocumentId`, `profileCreatedAt` — raw
|
|
399
|
+
* Renown profile facts.
|
|
400
|
+
* - `accountType` — wallet-adapter concept; only Renown has it.
|
|
401
|
+
*
|
|
402
|
+
* Rules:
|
|
403
|
+
* - `credential` (contains a JWT) is **never** forwarded.
|
|
404
|
+
* - Traits carry raw facts only; resolved presentation values (avatar,
|
|
405
|
+
* display name) go in OpenPanel's native identify fields — see
|
|
406
|
+
* {@link buildIdentifyPayload}.
|
|
407
|
+
* - Optional fields (`ens`, `profile`) are only included when their value is
|
|
408
|
+
* non-nullish (guards against `null` from `RenownProfile` fields as well
|
|
409
|
+
* as plain `undefined`).
|
|
410
|
+
*
|
|
411
|
+
* Mirror the Sentry pattern in `store/user.ts`: destructure off `credential`
|
|
412
|
+
* first, then assemble the trait payload from the remainder.
|
|
489
413
|
*/
|
|
490
|
-
function
|
|
491
|
-
|
|
492
|
-
const
|
|
493
|
-
|
|
414
|
+
function buildTraits(user) {
|
|
415
|
+
const { credential: _credential, ...rest } = user;
|
|
416
|
+
const traits = {
|
|
417
|
+
address: rest.address,
|
|
418
|
+
did: rest.did,
|
|
419
|
+
networkId: rest.networkId,
|
|
420
|
+
chainId: rest.chainId,
|
|
421
|
+
caip2: `${rest.networkId}:${rest.chainId}`
|
|
422
|
+
};
|
|
423
|
+
if (rest.ens?.name != null) traits.ensName = rest.ens.name;
|
|
424
|
+
if (rest.ens?.avatarUrl != null) traits.ensAvatar = rest.ens.avatarUrl;
|
|
425
|
+
if (rest.profile?.username != null) traits.username = rest.profile.username;
|
|
426
|
+
if (rest.profile?.userImage != null) traits.userImage = rest.profile.userImage;
|
|
427
|
+
if (rest.profile?.documentId != null) traits.profileDocumentId = rest.profile.documentId;
|
|
428
|
+
if (rest.profile?.createdAt != null) traits.profileCreatedAt = rest.profile.createdAt;
|
|
429
|
+
return traits;
|
|
494
430
|
}
|
|
495
431
|
/**
|
|
496
|
-
*
|
|
497
|
-
*
|
|
432
|
+
* Builds the full `identify()` payload:
|
|
433
|
+
* - `profileId` is the wallet address — the cross-app profile key shared with
|
|
434
|
+
* Renown and Vetra.
|
|
435
|
+
* - `avatar` / `firstName` are OpenPanel's native profile fields (they drive
|
|
436
|
+
* the picture and name in the dashboard UI), resolved as
|
|
437
|
+
* `userImage ?? ensAvatar` and `ensName ?? username`.
|
|
438
|
+
* - `properties` are the raw traits from {@link buildTraits}.
|
|
498
439
|
*/
|
|
499
|
-
function
|
|
500
|
-
|
|
501
|
-
|
|
440
|
+
function buildIdentifyPayload(user) {
|
|
441
|
+
const avatar = user.profile?.userImage ?? user.ens?.avatarUrl;
|
|
442
|
+
const firstName = user.ens?.name ?? user.profile?.username;
|
|
443
|
+
return {
|
|
444
|
+
profileId: user.address,
|
|
445
|
+
...avatar != null ? { avatar } : {},
|
|
446
|
+
...firstName != null ? { firstName } : {},
|
|
447
|
+
properties: buildTraits(user)
|
|
448
|
+
};
|
|
502
449
|
}
|
|
503
450
|
//#endregion
|
|
504
|
-
//#region src/
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
451
|
+
//#region src/components/openpanel.tsx
|
|
452
|
+
/**
|
|
453
|
+
* `OpenPanel` is a `null`-rendering component that manages the full lifecycle
|
|
454
|
+
* of the OpenPanel analytics subsystem.
|
|
455
|
+
*
|
|
456
|
+
* Gates:
|
|
457
|
+
* - `useAcceptedCookies().analytics === true` — the existing analytics cookie.
|
|
458
|
+
* - `connectConfig.openPanel.clientId` non-empty — no client ID → no-op.
|
|
459
|
+
*
|
|
460
|
+
* Behaviour when both gates pass:
|
|
461
|
+
* 1. Lazy-imports `@openpanel/web` and builds the singleton via
|
|
462
|
+
* `getOpenPanelClient(connectConfig.openPanel)`.
|
|
463
|
+
* 2. Exposes the client on `window.openPanel` for the card-6
|
|
464
|
+
* `useOpenPanelAnalytics()` hook to consume.
|
|
465
|
+
* 3. If `connectConfig.openPanel.trackOperations`, registers the processor
|
|
466
|
+
* factory against `processorManager` so document operations are forwarded.
|
|
467
|
+
* 4. Calls `client.identify()` when the renown user transitions from
|
|
468
|
+
* `undefined` → defined (login). The profile ID is the wallet address —
|
|
469
|
+
* the cross-app key shared with Renown and Vetra.
|
|
470
|
+
* 5. Calls `client.clear()` when the user transitions from defined →
|
|
471
|
+
* `undefined` (logout).
|
|
472
|
+
*
|
|
473
|
+
* Teardown (unmount **or** consent revocation):
|
|
474
|
+
* - `unregisterFactory("openpanel")` to remove the processor.
|
|
475
|
+
* - `resetOpenPanelClient()` to clear the singleton.
|
|
476
|
+
* - `window.openPanel` is cleared.
|
|
477
|
+
*
|
|
478
|
+
* Mirrors the Sentry pattern in `store/user.ts` for login/logout detection.
|
|
479
|
+
* Analytics failures are caught and forwarded to `console.warn` — they must
|
|
480
|
+
* never throw into the application.
|
|
481
|
+
*
|
|
482
|
+
* Mounted next to `<Analytics />` in `app.tsx`.
|
|
483
|
+
*/
|
|
484
|
+
function OpenPanel() {
|
|
485
|
+
const [{ analytics }] = useAcceptedCookies();
|
|
486
|
+
const user = useUser();
|
|
487
|
+
const module = useReactorClientModule();
|
|
488
|
+
const reactorClientModule = module?.kind === "browser" ? module : void 0;
|
|
489
|
+
const [client, setClient] = useState();
|
|
490
|
+
/**
|
|
491
|
+
* Tracks the previous `user` value so we can detect the two transitions:
|
|
492
|
+
* undefined → defined (login)
|
|
493
|
+
* defined → undefined (logout)
|
|
494
|
+
*/
|
|
495
|
+
const prevUserRef = useRef(void 0);
|
|
496
|
+
const enabled = analytics && !!connectConfig.openPanel.clientId;
|
|
497
|
+
const processorManager = reactorClientModule?.reactorModule?.processorManager;
|
|
498
|
+
useEffect(() => {
|
|
499
|
+
if (!enabled) return;
|
|
500
|
+
let cancelled = false;
|
|
501
|
+
const isCancelled = () => cancelled;
|
|
502
|
+
const setup = async () => {
|
|
503
|
+
let builtClient;
|
|
504
|
+
try {
|
|
505
|
+
builtClient = await getOpenPanelClient(connectConfig.openPanel);
|
|
506
|
+
} catch (err) {
|
|
507
|
+
console.warn("[OpenPanel] Failed to build client:", err);
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
if (isCancelled() || !builtClient) return;
|
|
511
|
+
setClient(builtClient);
|
|
512
|
+
window.openPanel = builtClient;
|
|
513
|
+
if (connectConfig.openPanel.trackOperations && processorManager) try {
|
|
514
|
+
await processorManager.registerFactory("openpanel", createOpenPanelProcessorFactory({
|
|
515
|
+
client: builtClient,
|
|
516
|
+
events: {
|
|
517
|
+
mappings: eventMappings,
|
|
518
|
+
lookupMap: eventLookupMap
|
|
519
|
+
},
|
|
520
|
+
startFrom: "current"
|
|
521
|
+
}));
|
|
522
|
+
if (isCancelled()) await processorManager.unregisterFactory("openpanel").catch((err) => console.warn("[OpenPanel] Failed to unregister factory after cancellation:", err));
|
|
523
|
+
} catch (err) {
|
|
524
|
+
console.warn("[OpenPanel] Failed to register processor factory:", err);
|
|
525
|
+
}
|
|
526
|
+
};
|
|
527
|
+
setup();
|
|
528
|
+
return () => {
|
|
529
|
+
cancelled = true;
|
|
530
|
+
prevUserRef.current = void 0;
|
|
531
|
+
if (connectConfig.openPanel.trackOperations && processorManager) processorManager.unregisterFactory("openpanel").catch((err) => console.warn("[OpenPanel] Failed to unregister processor factory:", err));
|
|
532
|
+
resetOpenPanelClient();
|
|
533
|
+
window.openPanel = void 0;
|
|
534
|
+
setClient(void 0);
|
|
535
|
+
};
|
|
536
|
+
}, [enabled, processorManager]);
|
|
537
|
+
useEffect(() => {
|
|
538
|
+
if (!enabled || !client) return;
|
|
539
|
+
const prev = prevUserRef.current;
|
|
540
|
+
prevUserRef.current = user;
|
|
541
|
+
if (!prev && user) try {
|
|
542
|
+
client.identify(buildIdentifyPayload(user));
|
|
543
|
+
} catch (err) {
|
|
544
|
+
console.warn("[OpenPanel] Failed to identify user:", err);
|
|
545
|
+
}
|
|
546
|
+
else if (prev && !user) try {
|
|
547
|
+
client.clear();
|
|
548
|
+
} catch (err) {
|
|
549
|
+
console.warn("[OpenPanel] Failed to clear user:", err);
|
|
550
|
+
}
|
|
551
|
+
}, [
|
|
552
|
+
client,
|
|
553
|
+
user,
|
|
554
|
+
enabled
|
|
555
|
+
]);
|
|
556
|
+
return null;
|
|
557
|
+
}
|
|
558
|
+
//#endregion
|
|
559
|
+
//#region src/components/document-editor-container.tsx
|
|
560
|
+
function DocumentEditorContainer() {
|
|
561
|
+
const [selectedDocument] = useSelectedDocument();
|
|
562
|
+
const parentFolder = useNodeParentFolderById(selectedDocument.header.id);
|
|
563
|
+
useMemo(() => {
|
|
564
|
+
return async () => {};
|
|
565
|
+
}, []);
|
|
566
|
+
useCallback(() => {
|
|
567
|
+
setSelectedNode(parentFolder);
|
|
568
|
+
}, [parentFolder]);
|
|
569
|
+
return /* @__PURE__ */ jsx("div", {
|
|
570
|
+
id: "document-editor-container",
|
|
571
|
+
className: "flex-1",
|
|
572
|
+
"data-document-type": selectedDocument.header.documentType,
|
|
573
|
+
children: /* @__PURE__ */ jsx(DocumentEditor, { document: selectedDocument })
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
//#endregion
|
|
577
|
+
//#region src/components/editor-loader.tsx
|
|
578
|
+
function EditorLoader(props) {
|
|
579
|
+
const [showLoading, setShowLoading] = useState(false);
|
|
580
|
+
useEffect(() => {
|
|
581
|
+
setTimeout(() => {
|
|
582
|
+
setShowLoading(true);
|
|
583
|
+
}, props.loadingTimeout ?? 200);
|
|
584
|
+
}, [props]);
|
|
585
|
+
if (!showLoading) return null;
|
|
586
|
+
const { customEditorLoader, ...defaultProps } = props;
|
|
587
|
+
if (customEditorLoader) return /* @__PURE__ */ jsx(Fragment, { children: customEditorLoader });
|
|
588
|
+
return /* @__PURE__ */ jsx(DefaultEditorLoader, { ...defaultProps });
|
|
589
|
+
}
|
|
590
|
+
//#endregion
|
|
591
|
+
//#region src/components/app-container.tsx
|
|
592
|
+
function AppContainer() {
|
|
593
|
+
const [selectedDrive] = useSelectedDriveSafe();
|
|
594
|
+
const selectedDocumentId = useSelectedDocumentId();
|
|
595
|
+
const app = useAppModuleById(selectedDrive?.header.meta?.preferredEditor);
|
|
596
|
+
const defaultApp = useDefaultAppModule();
|
|
597
|
+
if (!selectedDrive) return /* @__PURE__ */ jsx(EditorLoader, {});
|
|
598
|
+
const AppComponent = app?.Component ?? defaultApp?.Component ?? GenericDriveExplorer.Component;
|
|
599
|
+
if (!AppComponent) throw new Error("No app component found");
|
|
600
|
+
return /* @__PURE__ */ jsx(ErrorBoundary$1, {
|
|
601
|
+
variant: "detailed",
|
|
602
|
+
resetKeys: [selectedDrive.header.id],
|
|
603
|
+
loggerContext: ["Connect", "App"],
|
|
604
|
+
children: /* @__PURE__ */ jsx(Suspense, {
|
|
605
|
+
fallback: /* @__PURE__ */ jsx(EditorLoader, {}),
|
|
606
|
+
children: /* @__PURE__ */ jsx(DropZoneWrapper, {
|
|
607
|
+
className: "flex h-full flex-col overflow-auto",
|
|
608
|
+
children: /* @__PURE__ */ jsx(AppComponent, { children: selectedDocumentId ? /* @__PURE__ */ jsx(DocumentEditorContainer, {}) : null })
|
|
609
|
+
})
|
|
610
|
+
})
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
//#endregion
|
|
614
|
+
//#region src/hooks/useFailedInstallations.ts
|
|
615
|
+
const emptyArray$1 = [];
|
|
616
|
+
function useFailedInstallations() {
|
|
617
|
+
const discoveryService = usePackageDiscoveryService();
|
|
618
|
+
return useSyncExternalStore((cb) => discoveryService?.subscribeFailed(cb) ?? (() => {}), () => discoveryService?.getFailedInstallations() ?? emptyArray$1);
|
|
619
|
+
}
|
|
620
|
+
//#endregion
|
|
621
|
+
//#region src/hooks/useInitSentry.ts
|
|
622
|
+
const logger$1 = childLogger(["sentry"]);
|
|
623
|
+
let clientStarted = false;
|
|
624
|
+
async function getSentry() {
|
|
625
|
+
return await import("@sentry/react");
|
|
626
|
+
}
|
|
627
|
+
async function initSentry() {
|
|
628
|
+
const Sentry = await getSentry();
|
|
629
|
+
const integrations = [
|
|
630
|
+
Sentry.httpClientIntegration(),
|
|
631
|
+
Sentry.extraErrorDataIntegration({ depth: 5 }),
|
|
632
|
+
Sentry.replayIntegration(),
|
|
633
|
+
Sentry.captureConsoleIntegration({ levels: ["error"] })
|
|
634
|
+
];
|
|
635
|
+
if (connectConfig.sentry.tracing) integrations.push(Sentry.reactRouterV6BrowserTracingIntegration({
|
|
636
|
+
useEffect: React.useEffect,
|
|
637
|
+
useLocation,
|
|
638
|
+
useNavigationType,
|
|
639
|
+
createRoutesFromChildren,
|
|
640
|
+
matchRoutes
|
|
641
|
+
}));
|
|
642
|
+
Sentry.init({
|
|
643
|
+
release: connectConfig.sentry.release,
|
|
644
|
+
dsn: connectConfig.sentry.dsn ?? void 0,
|
|
645
|
+
environment: connectConfig.sentry.env,
|
|
646
|
+
integrations,
|
|
647
|
+
ignoreErrors: [
|
|
648
|
+
"User is not allowed to create files",
|
|
649
|
+
"User is not allowed to move documents",
|
|
650
|
+
"The user aborted a request."
|
|
651
|
+
],
|
|
652
|
+
sendDefaultPii: true,
|
|
653
|
+
tracesSampleRate: 1,
|
|
654
|
+
replaysSessionSampleRate: .1,
|
|
655
|
+
replaysOnErrorSampleRate: 1,
|
|
656
|
+
beforeSend(event, hint) {
|
|
657
|
+
const error = hint.originalException;
|
|
658
|
+
if (error instanceof TypeError && error.message.includes("Failed to fetch") && !navigator.onLine) return null;
|
|
659
|
+
return event;
|
|
660
|
+
}
|
|
661
|
+
});
|
|
662
|
+
setSentryModule(Sentry);
|
|
663
|
+
clientStarted = true;
|
|
664
|
+
}
|
|
665
|
+
async function closeClient() {
|
|
666
|
+
if (!clientStarted) return;
|
|
667
|
+
const sentry = await getSentry();
|
|
668
|
+
setSentryModule(void 0);
|
|
669
|
+
return sentry.getClient()?.close();
|
|
670
|
+
}
|
|
671
|
+
function useInitSentry() {
|
|
672
|
+
const [acceptedCookies] = useAcceptedCookies();
|
|
673
|
+
const { analytics } = acceptedCookies;
|
|
674
|
+
useEffect(() => {
|
|
675
|
+
if (!analytics) {
|
|
676
|
+
closeClient().catch((error) => logger$1.error("@error", error));
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
if (clientStarted || connectConfig.sentry.dsn === null || connectConfig.sentry.dsn === void 0 || connectConfig.sentry.dsn === "") return;
|
|
680
|
+
initSentry().catch((error) => logger$1.error("@error", error));
|
|
681
|
+
}, [analytics]);
|
|
682
|
+
}
|
|
683
|
+
//#endregion
|
|
684
|
+
//#region src/hooks/usePendingInstallations.ts
|
|
685
|
+
const emptyArray = [];
|
|
686
|
+
function usePendingInstallations() {
|
|
687
|
+
const discoveryService = usePackageDiscoveryService();
|
|
688
|
+
const cacheRef = useRef(emptyArray);
|
|
689
|
+
return useSyncExternalStore((cb) => discoveryService?.subscribePending(cb) ?? (() => {}), useCallback(() => {
|
|
690
|
+
if (!discoveryService) return emptyArray;
|
|
691
|
+
const next = discoveryService.getPendingInstallations() ?? emptyArray;
|
|
692
|
+
const prev = cacheRef.current;
|
|
513
693
|
if (next === prev || next.length === prev.length && next.every((p, i) => p === prev[i])) return prev;
|
|
514
694
|
cacheRef.current = next;
|
|
515
695
|
return next;
|
|
@@ -536,666 +716,356 @@ const useUndoRedoShortcuts = (props) => {
|
|
|
536
716
|
}, {}, [canRedo, redo]);
|
|
537
717
|
};
|
|
538
718
|
//#endregion
|
|
539
|
-
//#region src/
|
|
540
|
-
const
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
dump: "Exporting data from previous version…",
|
|
544
|
-
restore: "Restoring data into the new database…"
|
|
545
|
-
};
|
|
546
|
-
const MigrationOverlay = () => {
|
|
547
|
-
const status = useSyncExternalStore(subscribeMigrationStatus, getMigrationStatus, () => null);
|
|
548
|
-
if (!status) return null;
|
|
549
|
-
return /* @__PURE__ */ jsx("div", {
|
|
550
|
-
className: "absolute inset-0 z-20 flex items-center justify-center",
|
|
551
|
-
children: /* @__PURE__ */ jsxs("div", {
|
|
552
|
-
className: "rounded-lg bg-card/90 px-6 py-4 text-sm text-foreground shadow-lg",
|
|
553
|
-
children: [/* @__PURE__ */ jsx("div", {
|
|
554
|
-
className: "font-medium",
|
|
555
|
-
children: "Upgrading local database…"
|
|
556
|
-
}), /* @__PURE__ */ jsx("div", {
|
|
557
|
-
className: "text-foreground",
|
|
558
|
-
children: PHASE_LABEL[status.phase]
|
|
559
|
-
})]
|
|
560
|
-
})
|
|
561
|
-
});
|
|
562
|
-
};
|
|
563
|
-
const Loader = ({ delay = LOADER_DELAY }) => {
|
|
564
|
-
const isSSR = typeof window === "undefined";
|
|
565
|
-
const showInitialLoader = typeof document !== "undefined" && document.body.getAttribute("data-show-loader") === "true";
|
|
566
|
-
if (!isSSR) initTheme();
|
|
567
|
-
const [showLoading, setShowLoading] = useState(!delay || showInitialLoader);
|
|
568
|
-
useEffect(() => {
|
|
569
|
-
const id = setTimeout(() => {
|
|
570
|
-
setShowLoading(true);
|
|
571
|
-
}, delay);
|
|
572
|
-
return () => clearTimeout(id);
|
|
573
|
-
}, []);
|
|
574
|
-
return /* @__PURE__ */ jsxs("div", {
|
|
575
|
-
className: `skeleton-loader absolute inset-0 z-10 flex items-center justify-center ${showLoading ? "" : "hidden"}`,
|
|
576
|
-
children: [/* @__PURE__ */ jsx("div", {
|
|
577
|
-
className: "animate-pulse overflow-hidden rounded-full shadow-lg",
|
|
578
|
-
children: /* @__PURE__ */ jsx(LogoAnimation, {})
|
|
579
|
-
}), isSSR ? /* @__PURE__ */ jsx("script", { dangerouslySetInnerHTML: { __html: `setTimeout(() => {
|
|
580
|
-
document.querySelector('.skeleton-loader')?.classList.remove('hidden');
|
|
581
|
-
document.body.setAttribute('data-show-loader', 'true');
|
|
582
|
-
}, ${delay})` } }) : null]
|
|
583
|
-
});
|
|
719
|
+
//#region src/context/sentry-provider.tsx
|
|
720
|
+
const SentryProvider = ({ children }) => {
|
|
721
|
+
useInitSentry();
|
|
722
|
+
return children;
|
|
584
723
|
};
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
724
|
+
//#endregion
|
|
725
|
+
//#region src/utils/document-editor-debug-tools.ts
|
|
726
|
+
var DocumentEditorDebugTools = class {
|
|
727
|
+
document;
|
|
728
|
+
operations = [];
|
|
729
|
+
constructor(document) {
|
|
730
|
+
if (document) this.document = document;
|
|
731
|
+
}
|
|
732
|
+
operationsToTableObject(operations) {
|
|
733
|
+
return operations.map((op) => ({
|
|
734
|
+
...op,
|
|
735
|
+
input: JSON.stringify(op.action.input)
|
|
736
|
+
}));
|
|
737
|
+
}
|
|
738
|
+
setDocument(document) {
|
|
739
|
+
this.document = document;
|
|
740
|
+
}
|
|
741
|
+
getDocument() {
|
|
742
|
+
return this.document;
|
|
743
|
+
}
|
|
744
|
+
getOperations() {
|
|
745
|
+
return this.operations;
|
|
746
|
+
}
|
|
747
|
+
pushOperation(operation) {
|
|
748
|
+
this.operations.push(operation);
|
|
749
|
+
}
|
|
750
|
+
operationsTable() {
|
|
751
|
+
if (!this.document) console.warn("No document");
|
|
752
|
+
const ops = Object.values(this.document?.operations || {}).filter((array) => array !== void 0).flatMap((array) => array).sort((a, b) => a.index - b.index);
|
|
753
|
+
console.table(this.operationsToTableObject(ops));
|
|
754
|
+
}
|
|
755
|
+
scopeOperationsTable(scope) {
|
|
756
|
+
if (!this.document) console.warn("No document");
|
|
757
|
+
const ops = this.document?.operations[scope] || [];
|
|
758
|
+
console.table(this.operationsToTableObject(ops));
|
|
759
|
+
}
|
|
760
|
+
operationsLog() {
|
|
761
|
+
console.log(this.operations);
|
|
762
|
+
}
|
|
763
|
+
operationsLogTable() {
|
|
764
|
+
console.table(this.operationsToTableObject(this.operations));
|
|
765
|
+
}
|
|
766
|
+
clear() {
|
|
767
|
+
this.operations = [];
|
|
768
|
+
this.document = void 0;
|
|
769
|
+
}
|
|
613
770
|
};
|
|
614
771
|
//#endregion
|
|
615
|
-
//#region src/
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
772
|
+
//#region src/utils/launch-queue.ts
|
|
773
|
+
let pendingImportFiles = [];
|
|
774
|
+
const pendingListeners = /* @__PURE__ */ new Set();
|
|
775
|
+
function notifyPendingListeners() {
|
|
776
|
+
for (const listener of pendingListeners) listener();
|
|
619
777
|
}
|
|
620
|
-
function
|
|
621
|
-
return
|
|
778
|
+
function getPendingImportFiles() {
|
|
779
|
+
return pendingImportFiles;
|
|
622
780
|
}
|
|
623
|
-
/**
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
*/
|
|
627
|
-
function TextFallback({ message = "Something went wrong" }) {
|
|
628
|
-
return /* @__PURE__ */ jsx("div", {
|
|
629
|
-
className: "text-center wrap-break-word text-foreground",
|
|
630
|
-
children: message
|
|
631
|
-
});
|
|
781
|
+
/** Stable identity of a pending file (also the picker's pre-check cache key). */
|
|
782
|
+
function pendingFileKey(file) {
|
|
783
|
+
return `${file.name}|${file.size}|${file.lastModified}`;
|
|
632
784
|
}
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
const errorDetails = error instanceof Error ? error.stack ?? String(error.cause) : JSON.stringify(error, null, 2);
|
|
641
|
-
return /* @__PURE__ */ jsx("div", {
|
|
642
|
-
className: "z-10 mx-auto flex max-w-[80%] flex-1 items-center justify-center p-6",
|
|
643
|
-
children: /* @__PURE__ */ jsxs("div", {
|
|
644
|
-
className: "w-full max-w-lg rounded-lg border border-border bg-card p-6 text-foreground shadow-sm",
|
|
645
|
-
children: [
|
|
646
|
-
/* @__PURE__ */ jsxs("div", {
|
|
647
|
-
className: "mb-3 flex items-center gap-2",
|
|
648
|
-
children: [/* @__PURE__ */ jsx(Icon, {
|
|
649
|
-
name: "Error",
|
|
650
|
-
className: "size-5 shrink-0 text-destructive"
|
|
651
|
-
}), /* @__PURE__ */ jsx("h1", {
|
|
652
|
-
className: "text-lg font-semibold text-foreground",
|
|
653
|
-
children: "Something went wrong"
|
|
654
|
-
})]
|
|
655
|
-
}),
|
|
656
|
-
/* @__PURE__ */ jsx("p", {
|
|
657
|
-
className: "mb-4 text-sm wrap-break-word text-foreground",
|
|
658
|
-
children: errorMessage
|
|
659
|
-
}),
|
|
660
|
-
errorDetails !== "{}" && /* @__PURE__ */ jsxs("details", {
|
|
661
|
-
className: "group",
|
|
662
|
-
children: [/* @__PURE__ */ jsx("summary", {
|
|
663
|
-
className: "cursor-pointer text-sm font-medium text-foreground underline select-none hover:hover-effect",
|
|
664
|
-
children: "Show details"
|
|
665
|
-
}), /* @__PURE__ */ jsx("pre", {
|
|
666
|
-
className: "mt-2 max-h-48 overflow-auto rounded-sm bg-muted p-3 text-xs text-foreground",
|
|
667
|
-
children: errorDetails
|
|
668
|
-
})]
|
|
669
|
-
}),
|
|
670
|
-
/* @__PURE__ */ jsx(PowerhouseButton, {
|
|
671
|
-
type: "button",
|
|
672
|
-
onClick: resetErrorBoundary,
|
|
673
|
-
className: "mt-4 px-3 py-1.5 text-base font-medium",
|
|
674
|
-
children: "Try again"
|
|
675
|
-
})
|
|
676
|
-
]
|
|
677
|
-
})
|
|
785
|
+
function addPendingImportFiles(files) {
|
|
786
|
+
const seen = new Set(pendingImportFiles.map(pendingFileKey));
|
|
787
|
+
const fresh = files.filter((file) => {
|
|
788
|
+
const key = pendingFileKey(file);
|
|
789
|
+
if (seen.has(key)) return false;
|
|
790
|
+
seen.add(key);
|
|
791
|
+
return true;
|
|
678
792
|
});
|
|
793
|
+
if (fresh.length === 0) return;
|
|
794
|
+
pendingImportFiles = [...pendingImportFiles, ...fresh];
|
|
795
|
+
notifyPendingListeners();
|
|
679
796
|
}
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
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
|
-
});
|
|
797
|
+
function clearPendingImportFiles() {
|
|
798
|
+
if (pendingImportFiles.length === 0) return;
|
|
799
|
+
pendingImportFiles = [];
|
|
800
|
+
notifyPendingListeners();
|
|
693
801
|
}
|
|
694
|
-
function
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
802
|
+
function subscribePendingImportFiles(cb) {
|
|
803
|
+
pendingListeners.add(cb);
|
|
804
|
+
return () => {
|
|
805
|
+
pendingListeners.delete(cb);
|
|
806
|
+
};
|
|
807
|
+
}
|
|
808
|
+
async function handleLaunchParams(params) {
|
|
809
|
+
const handles = params.files.filter((handle) => handle.kind === "file");
|
|
810
|
+
if (handles.length === 0) return;
|
|
811
|
+
const files = [];
|
|
812
|
+
for (const handle of handles) try {
|
|
813
|
+
files.push(await handle.getFile());
|
|
814
|
+
} catch (error) {
|
|
815
|
+
console.error("Failed to read launched file:", handle.name, error);
|
|
700
816
|
}
|
|
817
|
+
if (files.length === 0) return;
|
|
818
|
+
addPendingImportFiles(files);
|
|
819
|
+
showPHModal({ type: "openFileDocuments" });
|
|
820
|
+
}
|
|
821
|
+
function maybeReopenImportModal() {
|
|
822
|
+
if (getPendingImportFiles().length === 0) return;
|
|
823
|
+
if (window.ph?.modal !== void 0) return;
|
|
824
|
+
showPHModal({ type: "openFileDocuments" });
|
|
701
825
|
}
|
|
826
|
+
let initialized = false;
|
|
702
827
|
/**
|
|
703
|
-
*
|
|
704
|
-
*
|
|
705
|
-
*
|
|
706
|
-
*
|
|
707
|
-
*
|
|
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>
|
|
828
|
+
* Register the launchQueue consumer that receives files opened with Connect
|
|
829
|
+
* through the OS file association. Idempotent (safe under StrictMode double
|
|
830
|
+
* effects) and a no-op where the File Handling API doesn't exist — Firefox,
|
|
831
|
+
* Safari, and any context without an installed PWA manifest simply never
|
|
832
|
+
* deliver launches.
|
|
737
833
|
*/
|
|
738
|
-
function
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
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
|
|
834
|
+
function initLaunchQueueFileHandling() {
|
|
835
|
+
if (initialized) return;
|
|
836
|
+
initialized = true;
|
|
837
|
+
window.addEventListener("ph:modalUpdated", maybeReopenImportModal);
|
|
838
|
+
window.launchQueue?.setConsumer((params) => {
|
|
839
|
+
handleLaunchParams(params);
|
|
755
840
|
});
|
|
756
841
|
}
|
|
757
842
|
//#endregion
|
|
758
|
-
//#region src/
|
|
759
|
-
|
|
760
|
-
|
|
843
|
+
//#region src/utils/open-file-uploads.ts
|
|
844
|
+
let uploads = [];
|
|
845
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
846
|
+
function notifyUploadListeners() {
|
|
847
|
+
for (const listener of listeners) listener();
|
|
761
848
|
}
|
|
762
|
-
|
|
763
|
-
|
|
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();
|
|
849
|
+
function getOpenFileUploads() {
|
|
850
|
+
return uploads;
|
|
812
851
|
}
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
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
|
|
852
|
+
function subscribeOpenFileUploads(cb) {
|
|
853
|
+
listeners.add(cb);
|
|
854
|
+
return () => {
|
|
855
|
+
listeners.delete(cb);
|
|
881
856
|
};
|
|
882
857
|
}
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
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`.
|
|
892
|
-
*/
|
|
893
|
-
function normalize(documentType) {
|
|
894
|
-
return documentType.toLowerCase().replace(/\//g, ".").replace(/[^a-z0-9._-]/g, "");
|
|
895
|
-
}
|
|
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()}`;
|
|
903
|
-
}
|
|
904
|
-
/**
|
|
905
|
-
* Builds the default properties attached to every OpenPanel event. The `app`
|
|
906
|
-
* property is stamped globally by the client, not here.
|
|
907
|
-
*/
|
|
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
|
|
915
|
-
};
|
|
858
|
+
function removeOpenFileUpload(id) {
|
|
859
|
+
const next = uploads.filter((upload) => upload.id !== id);
|
|
860
|
+
if (next.length === uploads.length) return;
|
|
861
|
+
uploads = next;
|
|
862
|
+
notifyUploadListeners();
|
|
916
863
|
}
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
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
|
-
}
|
|
944
|
-
}
|
|
945
|
-
}
|
|
946
|
-
onDisconnect() {
|
|
947
|
-
this.client.flush?.();
|
|
948
|
-
return Promise.resolve();
|
|
949
|
-
}
|
|
950
|
-
};
|
|
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
|
-
};
|
|
864
|
+
function clearOpenFileUploads() {
|
|
865
|
+
if (uploads.length === 0) return;
|
|
866
|
+
uploads = [];
|
|
867
|
+
notifyUploadListeners();
|
|
967
868
|
}
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
* Builds the OpenPanel identity traits from a renown User.
|
|
972
|
-
*
|
|
973
|
-
* Canonical trait schema (shared with Renown and Vetra — each app sends every
|
|
974
|
-
* field it has data for, under these names):
|
|
975
|
-
* - `address`, `did`, `networkId`, `chainId`, `caip2` — wallet/chain facts.
|
|
976
|
-
* - `ensName`, `ensAvatar` — raw ENS facts.
|
|
977
|
-
* - `username`, `userImage`, `profileDocumentId`, `profileCreatedAt` — raw
|
|
978
|
-
* Renown profile facts.
|
|
979
|
-
* - `accountType` — wallet-adapter concept; only Renown has it.
|
|
980
|
-
*
|
|
981
|
-
* Rules:
|
|
982
|
-
* - `credential` (contains a JWT) is **never** forwarded.
|
|
983
|
-
* - Traits carry raw facts only; resolved presentation values (avatar,
|
|
984
|
-
* display name) go in OpenPanel's native identify fields — see
|
|
985
|
-
* {@link buildIdentifyPayload}.
|
|
986
|
-
* - Optional fields (`ens`, `profile`) are only included when their value is
|
|
987
|
-
* non-nullish (guards against `null` from `RenownProfile` fields as well
|
|
988
|
-
* as plain `undefined`).
|
|
989
|
-
*
|
|
990
|
-
* Mirror the Sentry pattern in `store/user.ts`: destructure off `credential`
|
|
991
|
-
* first, then assemble the trait payload from the remainder.
|
|
992
|
-
*/
|
|
993
|
-
function buildTraits(user) {
|
|
994
|
-
const { credential: _credential, ...rest } = user;
|
|
995
|
-
const traits = {
|
|
996
|
-
address: rest.address,
|
|
997
|
-
did: rest.did,
|
|
998
|
-
networkId: rest.networkId,
|
|
999
|
-
chainId: rest.chainId,
|
|
1000
|
-
caip2: `${rest.networkId}:${rest.chainId}`
|
|
1001
|
-
};
|
|
1002
|
-
if (rest.ens?.name != null) traits.ensName = rest.ens.name;
|
|
1003
|
-
if (rest.ens?.avatarUrl != null) traits.ensAvatar = rest.ens.avatarUrl;
|
|
1004
|
-
if (rest.profile?.username != null) traits.username = rest.profile.username;
|
|
1005
|
-
if (rest.profile?.userImage != null) traits.userImage = rest.profile.userImage;
|
|
1006
|
-
if (rest.profile?.documentId != null) traits.profileDocumentId = rest.profile.documentId;
|
|
1007
|
-
if (rest.profile?.createdAt != null) traits.profileCreatedAt = rest.profile.createdAt;
|
|
1008
|
-
return traits;
|
|
869
|
+
function addOpenFileUploads(rows) {
|
|
870
|
+
uploads = [...uploads, ...rows];
|
|
871
|
+
notifyUploadListeners();
|
|
1009
872
|
}
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
* `userImage ?? ensAvatar` and `ensName ?? username`.
|
|
1017
|
-
* - `properties` are the raw traits from {@link buildTraits}.
|
|
1018
|
-
*/
|
|
1019
|
-
function buildIdentifyPayload(user) {
|
|
1020
|
-
const avatar = user.profile?.userImage ?? user.ens?.avatarUrl;
|
|
1021
|
-
const firstName = user.ens?.name ?? user.profile?.username;
|
|
1022
|
-
return {
|
|
1023
|
-
profileId: user.address,
|
|
1024
|
-
...avatar != null ? { avatar } : {},
|
|
1025
|
-
...firstName != null ? { firstName } : {},
|
|
1026
|
-
properties: buildTraits(user)
|
|
1027
|
-
};
|
|
873
|
+
function patchOpenFileUpload(id, patch) {
|
|
874
|
+
uploads = uploads.map((upload) => upload.id === id ? {
|
|
875
|
+
...upload,
|
|
876
|
+
...patch
|
|
877
|
+
} : upload);
|
|
878
|
+
notifyUploadListeners();
|
|
1028
879
|
}
|
|
1029
|
-
|
|
1030
|
-
|
|
880
|
+
let uploadCounter = 0;
|
|
881
|
+
let importChain = Promise.resolve();
|
|
1031
882
|
/**
|
|
1032
|
-
*
|
|
1033
|
-
*
|
|
1034
|
-
*
|
|
1035
|
-
*
|
|
1036
|
-
*
|
|
1037
|
-
*
|
|
1038
|
-
*
|
|
1039
|
-
* Behaviour when both gates pass:
|
|
1040
|
-
* 1. Lazy-imports `@openpanel/web` and builds the singleton via
|
|
1041
|
-
* `getOpenPanelClient(connectConfig.openPanel)`.
|
|
1042
|
-
* 2. Exposes the client on `window.openPanel` for the card-6
|
|
1043
|
-
* `useOpenPanelAnalytics()` hook to consume.
|
|
1044
|
-
* 3. If `connectConfig.openPanel.trackOperations`, registers the processor
|
|
1045
|
-
* factory against `processorManager` so document operations are forwarded.
|
|
1046
|
-
* 4. Calls `client.identify()` when the renown user transitions from
|
|
1047
|
-
* `undefined` → defined (login). The profile ID is the wallet address —
|
|
1048
|
-
* the cross-app key shared with Renown and Vetra.
|
|
1049
|
-
* 5. Calls `client.clear()` when the user transitions from defined →
|
|
1050
|
-
* `undefined` (logout).
|
|
1051
|
-
*
|
|
1052
|
-
* Teardown (unmount **or** consent revocation):
|
|
1053
|
-
* - `unregisterFactory("openpanel")` to remove the processor.
|
|
1054
|
-
* - `resetOpenPanelClient()` to clear the singleton.
|
|
1055
|
-
* - `window.openPanel` is cleared.
|
|
1056
|
-
*
|
|
1057
|
-
* Mirrors the Sentry pattern in `store/user.ts` for login/logout detection.
|
|
1058
|
-
* Analytics failures are caught and forwarded to `console.warn` — they must
|
|
1059
|
-
* never throw into the application.
|
|
1060
|
-
*
|
|
1061
|
-
* Mounted next to `<Analytics />` in `app.tsx`.
|
|
883
|
+
* Import one file and resolve once the flow truly ends. Completion is
|
|
884
|
+
* tracked through the progress stages, NOT the returned promise — the
|
|
885
|
+
* package-discovery retry path resolves early and keeps reporting through
|
|
886
|
+
* the callback. `resolveConflict: "duplicate"` is always passed: duplicates
|
|
887
|
+
* were resolved in the picker (rename), so the conflict stage must never
|
|
888
|
+
* fire — this also covers a duplicate appearing between pre-check and import.
|
|
1062
889
|
*/
|
|
1063
|
-
function
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
* Tracks the previous `user` value so we can detect the two transitions:
|
|
1071
|
-
* undefined → defined (login)
|
|
1072
|
-
* defined → undefined (logout)
|
|
1073
|
-
*/
|
|
1074
|
-
const prevUserRef = useRef(void 0);
|
|
1075
|
-
const enabled = analytics && !!connectConfig.openPanel.clientId;
|
|
1076
|
-
const processorManager = reactorClientModule?.reactorModule?.processorManager;
|
|
1077
|
-
useEffect(() => {
|
|
1078
|
-
if (!enabled) return;
|
|
1079
|
-
let cancelled = false;
|
|
1080
|
-
const isCancelled = () => cancelled;
|
|
1081
|
-
const setup = async () => {
|
|
1082
|
-
let builtClient;
|
|
1083
|
-
try {
|
|
1084
|
-
builtClient = await getOpenPanelClient(connectConfig.openPanel);
|
|
1085
|
-
} catch (err) {
|
|
1086
|
-
console.warn("[OpenPanel] Failed to build client:", err);
|
|
1087
|
-
return;
|
|
1088
|
-
}
|
|
1089
|
-
if (isCancelled() || !builtClient) return;
|
|
1090
|
-
setClient(builtClient);
|
|
1091
|
-
window.openPanel = builtClient;
|
|
1092
|
-
if (connectConfig.openPanel.trackOperations && processorManager) try {
|
|
1093
|
-
await processorManager.registerFactory("openpanel", createOpenPanelProcessorFactory({
|
|
1094
|
-
client: builtClient,
|
|
1095
|
-
events: {
|
|
1096
|
-
mappings: eventMappings,
|
|
1097
|
-
lookupMap: eventLookupMap
|
|
1098
|
-
},
|
|
1099
|
-
startFrom: "current"
|
|
1100
|
-
}));
|
|
1101
|
-
if (isCancelled()) await processorManager.unregisterFactory("openpanel").catch((err) => console.warn("[OpenPanel] Failed to unregister factory after cancellation:", err));
|
|
1102
|
-
} catch (err) {
|
|
1103
|
-
console.warn("[OpenPanel] Failed to register processor factory:", err);
|
|
1104
|
-
}
|
|
890
|
+
function importOne(row, driveId, documentTypes) {
|
|
891
|
+
return new Promise((resolve) => {
|
|
892
|
+
let settled = false;
|
|
893
|
+
const settle = (fileNode) => {
|
|
894
|
+
if (settled) return;
|
|
895
|
+
settled = true;
|
|
896
|
+
resolve(fileNode);
|
|
1105
897
|
};
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
898
|
+
addFileWithProgress(row.file, driveId, row.name, void 0, (progress) => {
|
|
899
|
+
patchOpenFileUpload(row.id, {
|
|
900
|
+
status: mapProgressStageToStatus(progress.stage),
|
|
901
|
+
progress: progress.progress,
|
|
902
|
+
...progress.error ? { errorDetails: progress.error } : {},
|
|
903
|
+
...progress.documentType ? { documentType: progress.documentType } : {},
|
|
904
|
+
...progress.fileNode ? { fileNode: progress.fileNode } : {}
|
|
905
|
+
});
|
|
906
|
+
switch (progress.stage) {
|
|
907
|
+
case "complete":
|
|
908
|
+
settle(progress.fileNode);
|
|
909
|
+
break;
|
|
910
|
+
case "failed":
|
|
911
|
+
case "unsupported-document-type":
|
|
912
|
+
settle(void 0);
|
|
913
|
+
break;
|
|
914
|
+
default: break;
|
|
915
|
+
}
|
|
916
|
+
}, documentTypes, "duplicate").catch(() => {
|
|
917
|
+
settle(void 0);
|
|
918
|
+
});
|
|
919
|
+
});
|
|
920
|
+
}
|
|
921
|
+
/**
|
|
922
|
+
* Run a confirmed batch of imports into a drive, feeding the floating upload
|
|
923
|
+
* panel. Rows appear immediately (queued), files import sequentially, and a
|
|
924
|
+
* single-file batch that succeeds opens its document in the editor.
|
|
925
|
+
*/
|
|
926
|
+
function runOpenFileImports(args) {
|
|
927
|
+
const { driveId, imports, documentTypes } = args;
|
|
928
|
+
if (imports.length === 0) return importChain;
|
|
929
|
+
const rows = imports.map(({ file, name }) => ({
|
|
930
|
+
id: `open-file-${++uploadCounter}`,
|
|
931
|
+
file,
|
|
932
|
+
name
|
|
933
|
+
}));
|
|
934
|
+
addOpenFileUploads(rows.map((row) => ({
|
|
935
|
+
id: row.id,
|
|
936
|
+
fileName: row.name,
|
|
937
|
+
fileSize: formatFileSize(row.file.size),
|
|
938
|
+
status: "pending",
|
|
939
|
+
progress: 0
|
|
940
|
+
})));
|
|
941
|
+
const run = async () => {
|
|
942
|
+
const results = [];
|
|
943
|
+
for (const row of rows) results.push(await importOne(row, driveId, documentTypes));
|
|
944
|
+
if (rows.length === 1 && results[0]) setSelectedNode(results[0]);
|
|
945
|
+
};
|
|
946
|
+
importChain = importChain.then(run, run);
|
|
947
|
+
return importChain;
|
|
948
|
+
}
|
|
949
|
+
//#endregion
|
|
950
|
+
//#region src/utils/openUrl.ts
|
|
951
|
+
const openBrowserUrl = (url) => window.open(url, "_blank");
|
|
952
|
+
const openUrl = openBrowserUrl;
|
|
953
|
+
//#endregion
|
|
954
|
+
//#region src/utils/pglite-migration.ts
|
|
955
|
+
const BACKUP_PREFIX = "ph-pglite-backup::";
|
|
956
|
+
const BACKUP_INDEX_KEY = "ph:pglite-backups";
|
|
957
|
+
const BACKUP_RETENTION_MS = 720 * 60 * 60 * 1e3;
|
|
958
|
+
function deleteIdb(name, timeoutMs = 15e3) {
|
|
959
|
+
return new Promise((resolve, reject) => {
|
|
960
|
+
const req = indexedDB.deleteDatabase(name);
|
|
961
|
+
let settled = false;
|
|
962
|
+
const done = (fn) => {
|
|
963
|
+
if (settled) return;
|
|
964
|
+
settled = true;
|
|
965
|
+
fn();
|
|
1114
966
|
};
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
967
|
+
const timer = setTimeout(() => done(() => reject(/* @__PURE__ */ new Error(`IDB delete timed out for ${name} after ${timeoutMs}ms`))), timeoutMs);
|
|
968
|
+
req.onsuccess = () => done(() => {
|
|
969
|
+
clearTimeout(timer);
|
|
970
|
+
resolve();
|
|
971
|
+
});
|
|
972
|
+
req.onerror = () => done(() => {
|
|
973
|
+
clearTimeout(timer);
|
|
974
|
+
reject(idbError(req, `IDB delete failed for ${name}`));
|
|
975
|
+
});
|
|
976
|
+
});
|
|
977
|
+
}
|
|
978
|
+
function readBackupIndex() {
|
|
979
|
+
try {
|
|
980
|
+
const raw = localStorage.getItem(BACKUP_INDEX_KEY);
|
|
981
|
+
if (!raw) return [];
|
|
982
|
+
const parsed = JSON.parse(raw);
|
|
983
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
984
|
+
} catch {
|
|
985
|
+
return [];
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
function writeBackupIndex(records) {
|
|
989
|
+
try {
|
|
990
|
+
localStorage.setItem(BACKUP_INDEX_KEY, JSON.stringify(records));
|
|
991
|
+
} catch {}
|
|
992
|
+
}
|
|
993
|
+
function registerBackup(rec) {
|
|
994
|
+
writeBackupIndex([...readBackupIndex(), rec]);
|
|
995
|
+
}
|
|
996
|
+
function forgetBackup(name) {
|
|
997
|
+
writeBackupIndex(readBackupIndex().filter((r) => r.name !== name));
|
|
998
|
+
}
|
|
999
|
+
async function janitor() {
|
|
1000
|
+
const cutoff = Date.now() - BACKUP_RETENTION_MS;
|
|
1001
|
+
const keep = [];
|
|
1002
|
+
for (const rec of readBackupIndex()) {
|
|
1003
|
+
if (rec.createdAt >= cutoff) {
|
|
1004
|
+
keep.push(rec);
|
|
1005
|
+
continue;
|
|
1124
1006
|
}
|
|
1125
|
-
|
|
1126
|
-
|
|
1007
|
+
try {
|
|
1008
|
+
await deleteIdb(rec.name);
|
|
1127
1009
|
} catch (err) {
|
|
1128
|
-
console.warn(
|
|
1010
|
+
console.warn(`Failed to delete expired pglite backup ${rec.name}`, err);
|
|
1011
|
+
keep.push(rec);
|
|
1129
1012
|
}
|
|
1130
|
-
}
|
|
1131
|
-
|
|
1132
|
-
user,
|
|
1133
|
-
enabled
|
|
1134
|
-
]);
|
|
1135
|
-
return null;
|
|
1013
|
+
}
|
|
1014
|
+
writeBackupIndex(keep);
|
|
1136
1015
|
}
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1016
|
+
function createPersistentBackup() {
|
|
1017
|
+
return {
|
|
1018
|
+
snapshot: async (idbName) => {
|
|
1019
|
+
const backupName = `${BACKUP_PREFIX}${idbName}::${(/* @__PURE__ */ new Date()).toISOString()}`;
|
|
1020
|
+
await cloneFileData(idbName, backupName);
|
|
1021
|
+
registerBackup({
|
|
1022
|
+
name: backupName,
|
|
1023
|
+
idbName,
|
|
1024
|
+
createdAt: Date.now()
|
|
1025
|
+
});
|
|
1026
|
+
return backupName;
|
|
1027
|
+
},
|
|
1028
|
+
rollback: async (handle, idbName, cause) => {
|
|
1029
|
+
const backupName = handle;
|
|
1030
|
+
try {
|
|
1031
|
+
await cloneFileData(backupName, idbName);
|
|
1032
|
+
} catch (rollbackErr) {
|
|
1033
|
+
console.error(`PGlite migration rollback failed for ${idbName}; backup ${backupName} retained. Primary migration error:`, cause, "Rollback error:", rollbackErr);
|
|
1034
|
+
return;
|
|
1035
|
+
}
|
|
1036
|
+
await deleteIdb(backupName).catch(() => void 0);
|
|
1037
|
+
forgetBackup(backupName);
|
|
1038
|
+
},
|
|
1039
|
+
discard: async (handle) => {
|
|
1040
|
+
const backupName = handle;
|
|
1041
|
+
await deleteIdb(backupName).catch(() => void 0);
|
|
1042
|
+
forgetBackup(backupName);
|
|
1043
|
+
},
|
|
1044
|
+
commit: () => Promise.resolve()
|
|
1045
|
+
};
|
|
1154
1046
|
}
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
const { customEditorLoader, ...defaultProps } = props;
|
|
1166
|
-
if (customEditorLoader) return /* @__PURE__ */ jsx(Fragment, { children: customEditorLoader });
|
|
1167
|
-
return /* @__PURE__ */ jsx(DefaultEditorLoader, { ...defaultProps });
|
|
1047
|
+
async function migrateAllLocked() {
|
|
1048
|
+
const backup = createPersistentBackup();
|
|
1049
|
+
try {
|
|
1050
|
+
for (const idbName of PRIMARY_IDB_NAMES) await migrateIdb(idbName, (phase) => setMigrationStatus({
|
|
1051
|
+
idbName,
|
|
1052
|
+
phase
|
|
1053
|
+
}), backup);
|
|
1054
|
+
} finally {
|
|
1055
|
+
setMigrationStatus(null);
|
|
1056
|
+
}
|
|
1168
1057
|
}
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
const AppComponent = app?.Component ?? defaultApp?.Component ?? GenericDriveExplorer.Component;
|
|
1178
|
-
if (!AppComponent) throw new Error("No app component found");
|
|
1179
|
-
return /* @__PURE__ */ jsx(ErrorBoundary$1, {
|
|
1180
|
-
variant: "detailed",
|
|
1181
|
-
resetKeys: [selectedDrive.header.id],
|
|
1182
|
-
loggerContext: ["Connect", "App"],
|
|
1183
|
-
children: /* @__PURE__ */ jsx(Suspense, {
|
|
1184
|
-
fallback: /* @__PURE__ */ jsx(EditorLoader, {}),
|
|
1185
|
-
children: /* @__PURE__ */ jsx(DropZoneWrapper, {
|
|
1186
|
-
className: "flex h-full flex-col overflow-auto",
|
|
1187
|
-
children: /* @__PURE__ */ jsx(AppComponent, { children: selectedDocumentId ? /* @__PURE__ */ jsx(DocumentEditorContainer, {}) : null })
|
|
1188
|
-
})
|
|
1189
|
-
})
|
|
1058
|
+
async function migrateAllIfNeeded() {
|
|
1059
|
+
janitor().catch((err) => console.warn("PGlite backup janitor failed", err));
|
|
1060
|
+
if (!(typeof navigator !== "undefined" && "locks" in navigator && typeof navigator.locks.request === "function")) {
|
|
1061
|
+
await migrateAllLocked();
|
|
1062
|
+
return;
|
|
1063
|
+
}
|
|
1064
|
+
await navigator.locks.request("ph-pglite-migration", { mode: "exclusive" }, async () => {
|
|
1065
|
+
await migrateAllLocked();
|
|
1190
1066
|
});
|
|
1191
1067
|
}
|
|
1192
1068
|
//#endregion
|
|
1193
|
-
//#region src/context/sentry-provider.tsx
|
|
1194
|
-
const SentryProvider = ({ children }) => {
|
|
1195
|
-
useInitSentry();
|
|
1196
|
-
return children;
|
|
1197
|
-
};
|
|
1198
|
-
//#endregion
|
|
1199
1069
|
//#region src/components/reactor-chat.tsx
|
|
1200
1070
|
/**
|
|
1201
1071
|
* Resolves the reactor tool descriptors for the chat agent from the
|
|
@@ -1533,6 +1403,20 @@ const App = () => {
|
|
|
1533
1403
|
] }) });
|
|
1534
1404
|
};
|
|
1535
1405
|
//#endregion
|
|
1406
|
+
//#region src/components/dots-loader.tsx
|
|
1407
|
+
const DotsLoader = () => {
|
|
1408
|
+
const [dots, setDots] = useState(0);
|
|
1409
|
+
useEffect(() => {
|
|
1410
|
+
const id = setInterval(() => {
|
|
1411
|
+
setDots((dots) => dots === 3 ? 0 : dots + 1);
|
|
1412
|
+
}, 200);
|
|
1413
|
+
return () => {
|
|
1414
|
+
clearInterval(id);
|
|
1415
|
+
};
|
|
1416
|
+
}, []);
|
|
1417
|
+
return /* @__PURE__ */ jsx("span", { children: new Array(dots).fill(".").join("") });
|
|
1418
|
+
};
|
|
1419
|
+
//#endregion
|
|
1536
1420
|
//#region src/components/drive-icon.tsx
|
|
1537
1421
|
function DriveIcon({ drive }) {
|
|
1538
1422
|
const remotes = useSyncList();
|
|
@@ -1597,6 +1481,9 @@ function EditorError({ title, children }) {
|
|
|
1597
1481
|
})
|
|
1598
1482
|
});
|
|
1599
1483
|
}
|
|
1484
|
+
function defaultScope(scopes) {
|
|
1485
|
+
return scopes.includes("global") ? "global" : scopes[0];
|
|
1486
|
+
}
|
|
1600
1487
|
function OpenPackageManagerButton() {
|
|
1601
1488
|
return /* @__PURE__ */ jsx(PowerhouseButton, {
|
|
1602
1489
|
className: "px-3 py-1.5 text-base font-medium",
|
|
@@ -1616,10 +1503,30 @@ const DocumentEditor = (props) => {
|
|
|
1616
1503
|
const documentName = document?.header.name ?? void 0;
|
|
1617
1504
|
const documentType = document?.header.documentType ?? void 0;
|
|
1618
1505
|
const preferredEditor = document?.header.meta?.preferredEditor ?? void 0;
|
|
1619
|
-
const
|
|
1506
|
+
const scopes = useMemo(() => {
|
|
1507
|
+
const keys = Object.keys(document?.header.revision ?? {});
|
|
1508
|
+
return keys.length > 0 ? keys : ["global"];
|
|
1509
|
+
}, [document?.header.revision]);
|
|
1510
|
+
const [operationScope, setOperationScope] = useState(() => defaultScope(scopes));
|
|
1511
|
+
const selectedScope = scopes.includes(operationScope) ? operationScope : defaultScope(scopes);
|
|
1512
|
+
const { operations: historyOperations, isLoading: isLoadingHistory, hasNextPage: historyHasNextPage, fetchNextPage: fetchNextHistoryPage, error: historyError } = useDocumentOperations(documentId, selectedScope, {
|
|
1513
|
+
enabled: revisionHistoryVisible,
|
|
1514
|
+
limit: 500
|
|
1515
|
+
});
|
|
1516
|
+
const { operations: globalOperations, hasNextPage: globalHasNextPage, isLoading: isLoadingGlobal, fetchNextPage: fetchNextGlobalPage, error: globalError } = useDocumentOperations(documentId, "global", {
|
|
1517
|
+
enabled: !!selectedTimelineItem,
|
|
1518
|
+
limit: 500
|
|
1519
|
+
});
|
|
1620
1520
|
useEffect(() => {
|
|
1621
|
-
if (
|
|
1622
|
-
|
|
1521
|
+
if (!globalHasNextPage || isLoadingGlobal || globalError) return;
|
|
1522
|
+
const handle = window.setTimeout(fetchNextGlobalPage, 0);
|
|
1523
|
+
return () => window.clearTimeout(handle);
|
|
1524
|
+
}, [
|
|
1525
|
+
globalHasNextPage,
|
|
1526
|
+
isLoadingGlobal,
|
|
1527
|
+
fetchNextGlobalPage,
|
|
1528
|
+
globalError
|
|
1529
|
+
]);
|
|
1623
1530
|
const globalRevisionNumber = document?.header.revision.global ?? 0;
|
|
1624
1531
|
const localRevisionNumber = document?.header.revision.local ?? 0;
|
|
1625
1532
|
const documentModelModule = useDocumentModelModuleById(documentType);
|
|
@@ -1725,11 +1632,17 @@ const DocumentEditor = (props) => {
|
|
|
1725
1632
|
id: "document-editor-context",
|
|
1726
1633
|
"data-editor": editorModule.config.id,
|
|
1727
1634
|
"data-document-type": documentType,
|
|
1728
|
-
children: revisionHistoryVisible ?
|
|
1635
|
+
children: revisionHistoryVisible ? /* @__PURE__ */ jsx(RevisionHistory, {
|
|
1729
1636
|
documentTitle: documentName ?? "",
|
|
1730
1637
|
documentId: documentId ?? "",
|
|
1731
|
-
|
|
1732
|
-
|
|
1638
|
+
operations: historyOperations,
|
|
1639
|
+
isLoading: isLoadingHistory,
|
|
1640
|
+
hasNextPage: historyHasNextPage,
|
|
1641
|
+
onLoadNextPage: fetchNextHistoryPage,
|
|
1642
|
+
error: historyError,
|
|
1643
|
+
scopes,
|
|
1644
|
+
scope: selectedScope,
|
|
1645
|
+
onScopeChange: setOperationScope,
|
|
1733
1646
|
onClose: () => setRevisionHistoryVisible(false),
|
|
1734
1647
|
documentState: document.state,
|
|
1735
1648
|
onCopyState: () => {
|
|
@@ -1749,7 +1662,7 @@ const DocumentEditor = (props) => {
|
|
|
1749
1662
|
children: !editorError?.error && /* @__PURE__ */ jsx(EditorComponent, {
|
|
1750
1663
|
context: {
|
|
1751
1664
|
readMode: !!selectedTimelineItem,
|
|
1752
|
-
selectedTimelineRevision: getRevisionFromDate(selectedTimelineItem?.startDate, selectedTimelineItem?.endDate, globalOperations)
|
|
1665
|
+
selectedTimelineRevision: (isLoadingGlobal || globalHasNextPage) && !globalError ? void 0 : getRevisionFromDate(selectedTimelineItem?.startDate, selectedTimelineItem?.endDate, globalOperations)
|
|
1753
1666
|
},
|
|
1754
1667
|
documentId: document.header.id
|
|
1755
1668
|
}, `${editorBundleKey}:${documentId}`)
|
|
@@ -1758,6 +1671,32 @@ const DocumentEditor = (props) => {
|
|
|
1758
1671
|
});
|
|
1759
1672
|
};
|
|
1760
1673
|
//#endregion
|
|
1674
|
+
//#region src/components/footer.tsx
|
|
1675
|
+
const Footer$1 = () => {
|
|
1676
|
+
return /* @__PURE__ */ jsxs(Footer, { children: [
|
|
1677
|
+
/* @__PURE__ */ jsx(FooterLink, {
|
|
1678
|
+
onClick: () => setBannerShown(true),
|
|
1679
|
+
children: /* @__PURE__ */ jsx(Trans, { i18nKey: "footer.cookiePolicy" })
|
|
1680
|
+
}),
|
|
1681
|
+
/* @__PURE__ */ jsx(FooterLink, {
|
|
1682
|
+
onClick: () => showPHModal({ type: "disclaimer" }),
|
|
1683
|
+
children: /* @__PURE__ */ jsx(Trans, { i18nKey: "footer.disclaimer" })
|
|
1684
|
+
}),
|
|
1685
|
+
/* @__PURE__ */ jsx(FooterLink, {
|
|
1686
|
+
id: "ph-logo-link",
|
|
1687
|
+
onClick: () => openUrl("https://www.powerhouse.inc/"),
|
|
1688
|
+
children: /* @__PURE__ */ jsx(Trans, {
|
|
1689
|
+
i18nKey: "footer.builtWith",
|
|
1690
|
+
components: { icon: /* @__PURE__ */ jsx(Icon, {
|
|
1691
|
+
name: "PowerhouseLogoSmall",
|
|
1692
|
+
size: 16,
|
|
1693
|
+
className: "mx-1"
|
|
1694
|
+
}) }
|
|
1695
|
+
})
|
|
1696
|
+
})
|
|
1697
|
+
] });
|
|
1698
|
+
};
|
|
1699
|
+
//#endregion
|
|
1761
1700
|
//#region src/components/root.tsx
|
|
1762
1701
|
function Root() {
|
|
1763
1702
|
return /* @__PURE__ */ jsx("div", {
|
|
@@ -1950,6 +1889,64 @@ const Router = () => {
|
|
|
1950
1889
|
return /* @__PURE__ */ jsx(RouterProvider, { router });
|
|
1951
1890
|
};
|
|
1952
1891
|
//#endregion
|
|
1892
|
+
//#region src/components/search-bar.tsx
|
|
1893
|
+
const defaultFilterItems = [
|
|
1894
|
+
{
|
|
1895
|
+
id: "project",
|
|
1896
|
+
label: ".project",
|
|
1897
|
+
icon: /* @__PURE__ */ jsx(Icon, {
|
|
1898
|
+
name: "Project",
|
|
1899
|
+
color: "#FF6A55",
|
|
1900
|
+
size: 16
|
|
1901
|
+
})
|
|
1902
|
+
},
|
|
1903
|
+
{
|
|
1904
|
+
id: "budget",
|
|
1905
|
+
label: ".budget",
|
|
1906
|
+
icon: /* @__PURE__ */ jsx(Icon, {
|
|
1907
|
+
name: "BarChart",
|
|
1908
|
+
color: "#8E55EA",
|
|
1909
|
+
size: 16
|
|
1910
|
+
})
|
|
1911
|
+
},
|
|
1912
|
+
{
|
|
1913
|
+
id: "profile",
|
|
1914
|
+
label: ".profile",
|
|
1915
|
+
icon: /* @__PURE__ */ jsx(Icon, {
|
|
1916
|
+
name: "Person",
|
|
1917
|
+
color: "#3E90F0",
|
|
1918
|
+
size: 16
|
|
1919
|
+
})
|
|
1920
|
+
},
|
|
1921
|
+
{
|
|
1922
|
+
id: "legal",
|
|
1923
|
+
label: ".legal",
|
|
1924
|
+
icon: /* @__PURE__ */ jsx(Icon, {
|
|
1925
|
+
name: "Briefcase",
|
|
1926
|
+
color: "#4BAB71",
|
|
1927
|
+
size: 16
|
|
1928
|
+
})
|
|
1929
|
+
},
|
|
1930
|
+
{
|
|
1931
|
+
id: "atlas",
|
|
1932
|
+
label: ".Atlas",
|
|
1933
|
+
icon: /* @__PURE__ */ jsx(Icon, {
|
|
1934
|
+
name: "Globe",
|
|
1935
|
+
color: "#FF8A00",
|
|
1936
|
+
size: 16
|
|
1937
|
+
})
|
|
1938
|
+
}
|
|
1939
|
+
];
|
|
1940
|
+
const SearchBar = () => {
|
|
1941
|
+
const { t } = useTranslation();
|
|
1942
|
+
return /* @__PURE__ */ jsx(ConnectSearchBar, {
|
|
1943
|
+
className: "m-4 shrink-0 bg-muted",
|
|
1944
|
+
placeholder: t("searchbar.placeholder"),
|
|
1945
|
+
filterLabel: t("searchbar.filterLabel"),
|
|
1946
|
+
filterItems: defaultFilterItems
|
|
1947
|
+
});
|
|
1948
|
+
};
|
|
1949
|
+
//#endregion
|
|
1953
1950
|
//#region src/components/sidebar.tsx
|
|
1954
1951
|
function Sidebar() {
|
|
1955
1952
|
const user = useUser();
|
|
@@ -2130,20 +2127,20 @@ const MigrationBanner = () => {
|
|
|
2130
2127
|
};
|
|
2131
2128
|
//#endregion
|
|
2132
2129
|
//#region src/components/modal/modals-container.tsx
|
|
2133
|
-
const AddDriveModal$1 = lazy(() => import("./AddDriveModal-
|
|
2134
|
-
const ClearStorageModal = lazy(() => import("./ClearStorageModal-
|
|
2135
|
-
const CookiesPolicyModal = lazy(() => import("./CookiesPolicyModal-
|
|
2136
|
-
const CreateDocumentModal$1 = lazy(() => import("./CreateDocumentModal-
|
|
2137
|
-
const DebugSettingsModal = lazy(() => import("./DebugSettingsModal-
|
|
2138
|
-
const DeleteDriveModal = lazy(() => import("./DeleteDriveModal-
|
|
2139
|
-
const DeleteItemModal = lazy(() => import("./DeleteItemModal-
|
|
2140
|
-
const DisclaimerModal = lazy(() => import("./DisclaimerModal-
|
|
2141
|
-
const DriveSettingsModal$1 = lazy(() => import("./DriveSettingsModal-
|
|
2142
|
-
const DownloadDocumentWithErrorsModal = lazy(() => import("./DownloadDocumentWithErrorsModal-
|
|
2143
|
-
const DocumentVersionUnsupportedModal = lazy(() => import("./DocumentVersionUnsupportedModal-
|
|
2144
|
-
const ConfirmDocumentUpgradeModal = lazy(() => import("./ConfirmDocumentUpgradeModal-
|
|
2145
|
-
const SettingsModal$1 = lazy(() => import("./SettingsModal-
|
|
2146
|
-
const UpgradeDriveModal = lazy(() => import("./UpgradeDriveModal-
|
|
2130
|
+
const AddDriveModal$1 = lazy(() => import("./AddDriveModal-CW-ZSWkf.js").then((m) => ({ default: m.AddDriveModal })));
|
|
2131
|
+
const ClearStorageModal = lazy(() => import("./ClearStorageModal-DPeEp0Uy.js").then((m) => ({ default: m.ClearStorageModal })));
|
|
2132
|
+
const CookiesPolicyModal = lazy(() => import("./CookiesPolicyModal-lKdOuTNm.js").then((m) => ({ default: m.CookiesPolicyModal })));
|
|
2133
|
+
const CreateDocumentModal$1 = lazy(() => import("./CreateDocumentModal-HmK-78sB.js").then((m) => ({ default: m.CreateDocumentModal })));
|
|
2134
|
+
const DebugSettingsModal = lazy(() => import("./DebugSettingsModal-ViAF8fXV.js").then((m) => ({ default: m.DebugSettingsModal })));
|
|
2135
|
+
const DeleteDriveModal = lazy(() => import("./DeleteDriveModal-Cyw6hwc2.js").then((m) => ({ default: m.DeleteDriveModal })));
|
|
2136
|
+
const DeleteItemModal = lazy(() => import("./DeleteItemModal-CiFwstZx.js").then((m) => ({ default: m.DeleteItemModal })));
|
|
2137
|
+
const DisclaimerModal = lazy(() => import("./DisclaimerModal-DIRsjUvu.js").then((m) => ({ default: m.DisclaimerModal })));
|
|
2138
|
+
const DriveSettingsModal$1 = lazy(() => import("./DriveSettingsModal-DRarHRIb.js").then((m) => ({ default: m.DriveSettingsModal })));
|
|
2139
|
+
const DownloadDocumentWithErrorsModal = lazy(() => import("./DownloadDocumentWithErrorsModal-CxsR74d-.js").then((m) => ({ default: m.DownloadDocumentWithErrorsModal })));
|
|
2140
|
+
const DocumentVersionUnsupportedModal = lazy(() => import("./DocumentVersionUnsupportedModal-KftC3-jr.js").then((m) => ({ default: m.DocumentVersionUnsupportedModal })));
|
|
2141
|
+
const ConfirmDocumentUpgradeModal = lazy(() => import("./ConfirmDocumentUpgradeModal-BBrFlHp_.js").then((m) => ({ default: m.ConfirmDocumentUpgradeModal })));
|
|
2142
|
+
const SettingsModal$1 = lazy(() => import("./SettingsModal-DEjFnBbg.js").then((m) => ({ default: m.SettingsModal })));
|
|
2143
|
+
const UpgradeDriveModal = lazy(() => import("./UpgradeDriveModal-C8scFkuq.js").then((m) => ({ default: m.UpgradeDriveModal })));
|
|
2147
2144
|
const modalComponents = {
|
|
2148
2145
|
addDrive: AddDriveModal$1,
|
|
2149
2146
|
clearStorage: ClearStorageModal,
|
|
@@ -2157,13 +2154,13 @@ const modalComponents = {
|
|
|
2157
2154
|
downloadDocumentWithErrors: DownloadDocumentWithErrorsModal,
|
|
2158
2155
|
documentVersionUnsupported: DocumentVersionUnsupportedModal,
|
|
2159
2156
|
confirmDocumentUpgrade: ConfirmDocumentUpgradeModal,
|
|
2160
|
-
inspector: lazy(() => import("./InspectorModal-
|
|
2157
|
+
inspector: lazy(() => import("./InspectorModal-3Bs1PQPO.js").then((m) => ({ default: m.InspectorModal }))),
|
|
2161
2158
|
settings: SettingsModal$1,
|
|
2162
2159
|
upgradeDrive: UpgradeDriveModal,
|
|
2163
|
-
missingPackage: lazy(() => import("./MissingPackageModal-
|
|
2164
|
-
driveAuthRequired: lazy(() => import("./DriveAuthRequiredModal-
|
|
2165
|
-
openFileDocuments: lazy(() => import("./OpenFileDocumentsModal-
|
|
2166
|
-
login: lazy(() => import("./LoginModal-
|
|
2160
|
+
missingPackage: lazy(() => import("./MissingPackageModal-Bar7ErIs.js").then((m) => ({ default: m.ConnectMissingPackageModal }))),
|
|
2161
|
+
driveAuthRequired: lazy(() => import("./DriveAuthRequiredModal-BoqnnNKT.js").then((m) => ({ default: m.DriveAuthRequiredModal }))),
|
|
2162
|
+
openFileDocuments: lazy(() => import("./OpenFileDocumentsModal-C8xf6Yiu.js").then((m) => ({ default: m.OpenFileDocumentsModal }))),
|
|
2163
|
+
login: lazy(() => import("./LoginModal-DhQTxsoG.js").then((m) => ({ default: m.LoginModal })))
|
|
2167
2164
|
};
|
|
2168
2165
|
const ModalsContainer = lazy(() => {
|
|
2169
2166
|
return Promise.resolve({ default: () => {
|
|
@@ -2202,9 +2199,9 @@ const ServiceWorkerUpdatePrompt = () => {
|
|
|
2202
2199
|
};
|
|
2203
2200
|
//#endregion
|
|
2204
2201
|
//#region src/components/app-loader.tsx
|
|
2205
|
-
const CookieBanner$1 = lazy(() => import("./cookie-banner-
|
|
2202
|
+
const CookieBanner$1 = lazy(() => import("./cookie-banner-BQescTYk.js").then((m) => ({ default: m.CookieBanner })));
|
|
2206
2203
|
const AppLoader = (props) => {
|
|
2207
|
-
const Load = lazy(() => import("./load-
|
|
2204
|
+
const Load = lazy(() => import("./load-BCywb9js.js").then((m) => m.loadComponent(props.localPackage)));
|
|
2208
2205
|
return /* @__PURE__ */ jsx(StrictMode, { children: /* @__PURE__ */ jsxs(ErrorBoundary$1, {
|
|
2209
2206
|
fallbackRender: (props) => /* @__PURE__ */ jsx(AppSkeleton, { children: /* @__PURE__ */ jsx(DetailedFallback, { ...props }) }),
|
|
2210
2207
|
resetKeys: [props.localPackage],
|
|
@@ -2238,7 +2235,7 @@ const AppLoader = (props) => {
|
|
|
2238
2235
|
}) });
|
|
2239
2236
|
};
|
|
2240
2237
|
//#endregion
|
|
2241
|
-
export {
|
|
2238
|
+
export { EditorLoader as _, Root as a, Analytics as b, DriveIcon as c, runOpenFileImports as d, clearPendingImportFiles as f, AppContainer as g, subscribePendingImportFiles as h, Router as i, DotsLoader as l, pendingFileKey as m, Sidebar as n, Footer$1 as o, getPendingImportFiles as p, SearchBar as r, DocumentEditor as s, AppLoader as t, App as u, DocumentEditorContainer as v, OpenPanel as y };
|
|
2242
2239
|
|
|
2243
|
-
//# sourceMappingURL=app-loader-
|
|
2244
|
-
//# debugId=
|
|
2240
|
+
//# sourceMappingURL=app-loader-BOmz0_wJ.js.map
|
|
2241
|
+
//# debugId=07080b59-d0d0-5a15-b224-26b85d0fea75
|