@powerhousedao/connect 6.2.2-dev.4 → 6.2.2-dev.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/assets/document-icon-192x192.png +0 -0
  2. package/assets/document-icon-512x512.png +0 -0
  3. package/assets/pwa-192x192.png +0 -0
  4. package/assets/pwa-512x512-maskable.png +0 -0
  5. package/assets/pwa-512x512.png +0 -0
  6. package/dist/{AddDriveModal-MMHlkJBJ.js → AddDriveModal-mTJUAV2Z.js} +5 -5
  7. package/dist/AddDriveModal-mTJUAV2Z.js.map +1 -0
  8. package/dist/{ClearStorageModal-B2RYDgLq.js → ClearStorageModal-DzVJ-bz1.js} +4 -4
  9. package/dist/{ClearStorageModal-B2RYDgLq.js.map → ClearStorageModal-DzVJ-bz1.js.map} +1 -1
  10. package/dist/{CookiesPolicyModal-CkcUKPLb.js → CookiesPolicyModal-Dpx-2TgP.js} +3 -3
  11. package/dist/{CookiesPolicyModal-CkcUKPLb.js.map → CookiesPolicyModal-Dpx-2TgP.js.map} +1 -1
  12. package/dist/{CreateDocumentModal-CvsTh0HM.js → CreateDocumentModal-u-IaJUQ0.js} +3 -3
  13. package/dist/{CreateDocumentModal-CvsTh0HM.js.map → CreateDocumentModal-u-IaJUQ0.js.map} +1 -1
  14. package/dist/{DebugSettingsModal-DcoIFbGH.js → DebugSettingsModal-DEIZdxIJ.js} +6 -6
  15. package/dist/{DebugSettingsModal-DcoIFbGH.js.map → DebugSettingsModal-DEIZdxIJ.js.map} +1 -1
  16. package/dist/{DeleteDriveModal-DSTK8FYk.js → DeleteDriveModal-CJKhWtli.js} +4 -4
  17. package/dist/{DeleteDriveModal-DSTK8FYk.js.map → DeleteDriveModal-CJKhWtli.js.map} +1 -1
  18. package/dist/{DeleteItemModal-B2nJmjsP.js → DeleteItemModal-DiieJw_o.js} +4 -4
  19. package/dist/{DeleteItemModal-B2nJmjsP.js.map → DeleteItemModal-DiieJw_o.js.map} +1 -1
  20. package/dist/{DisclaimerModal-D5FO34u9.js → DisclaimerModal-DAvYIF9-.js} +3 -3
  21. package/dist/{DisclaimerModal-D5FO34u9.js.map → DisclaimerModal-DAvYIF9-.js.map} +1 -1
  22. package/dist/{DownloadDocumentWithErrorsModal-tEr3bbtN.js → DownloadDocumentWithErrorsModal-CE4fa1k5.js} +3 -3
  23. package/dist/{DownloadDocumentWithErrorsModal-tEr3bbtN.js.map → DownloadDocumentWithErrorsModal-CE4fa1k5.js.map} +1 -1
  24. package/dist/DriveAuthRequiredModal-BQaAWHTe.js +36 -0
  25. package/dist/DriveAuthRequiredModal-BQaAWHTe.js.map +1 -0
  26. package/dist/{DriveSettingsModal-kR3hQnh4.js → DriveSettingsModal-CqFeWu4r.js} +3 -3
  27. package/dist/{DriveSettingsModal-kR3hQnh4.js.map → DriveSettingsModal-CqFeWu4r.js.map} +1 -1
  28. package/dist/{InspectorModal-DqFcidAW.js → InspectorModal-CiFzmU35.js} +6 -6
  29. package/dist/{InspectorModal-DqFcidAW.js.map → InspectorModal-CiFzmU35.js.map} +1 -1
  30. package/dist/LoginModal-CX0RlUEa.js +75 -0
  31. package/dist/LoginModal-CX0RlUEa.js.map +1 -0
  32. package/dist/{MissingPackageModal-BQcCQbOn.js → MissingPackageModal-C3acJ9r0.js} +18 -6
  33. package/dist/MissingPackageModal-C3acJ9r0.js.map +1 -0
  34. package/dist/OpenFileDocumentsModal-DN9a0rV4.js +293 -0
  35. package/dist/OpenFileDocumentsModal-DN9a0rV4.js.map +1 -0
  36. package/dist/{SettingsModal-DVxrOHRl.js → SettingsModal-DIrHdgma.js} +26 -13
  37. package/dist/SettingsModal-DIrHdgma.js.map +1 -0
  38. package/dist/{UpgradeDriveModal-BZGSE9_r.js → UpgradeDriveModal-rUHyJTc7.js} +3 -3
  39. package/dist/{UpgradeDriveModal-BZGSE9_r.js.map → UpgradeDriveModal-rUHyJTc7.js.map} +1 -1
  40. package/dist/{app-loader-D5VE_BWh.js → app-loader-CFsW8a09.js} +398 -88
  41. package/dist/app-loader-CFsW8a09.js.map +1 -0
  42. package/dist/app-loader-D3ZxJGT1.js +4 -0
  43. package/dist/{build-info-CM8FiHg5.js → build-info-BdM4dnSV.js} +5 -5
  44. package/dist/{build-info-CM8FiHg5.js.map → build-info-BdM4dnSV.js.map} +1 -1
  45. package/dist/components-CJao9PpY.js +5 -0
  46. package/dist/{connect.config-vgUeFoNp.js → connect.config-C-b_Yhd6.js} +9 -4
  47. package/dist/connect.config-C-b_Yhd6.js.map +1 -0
  48. package/dist/{connection-state-DO_2lAEJ.js → connection-state-KCa1yljA.js} +4 -4
  49. package/dist/{connection-state-DO_2lAEJ.js.map → connection-state-KCa1yljA.js.map} +1 -1
  50. package/dist/{load-Dwg68U_6.js → load-DFOEsqzK.js} +10 -10
  51. package/dist/{load-Dwg68U_6.js.map → load-DFOEsqzK.js.map} +1 -1
  52. package/dist/main.js +1 -1
  53. package/dist/{package-D_aVpf8t.js → package-QgF7pRWL.js} +8 -4
  54. package/dist/package-QgF7pRWL.js.map +1 -0
  55. package/dist/{pglite-seed-z3B1JA38.js → pglite-seed-DRXPe7p-.js} +4 -4
  56. package/dist/{pglite-seed-z3B1JA38.js.map → pglite-seed-DRXPe7p-.js.map} +1 -1
  57. package/dist/pwa-manifest-link-CG8UajMz.js +34 -0
  58. package/dist/pwa-manifest-link-CG8UajMz.js.map +1 -0
  59. package/dist/{reactor-BQyfLv_t.js → reactor-CLGTL6eH.js} +153 -16
  60. package/dist/reactor-CLGTL6eH.js.map +1 -0
  61. package/dist/reactor.worker.js +7 -5
  62. package/dist/reactor.worker.js.map +1 -1
  63. package/dist/{registerServiceWorker-nJIc0yEY.js → registerServiceWorker-Bn1KWmk0.js} +6 -4
  64. package/dist/registerServiceWorker-Bn1KWmk0.js.map +1 -0
  65. package/dist/{reload-connect-toast-D1Iqw9E_.js → reload-connect-toast-BkgP9i6_.js} +19 -3
  66. package/dist/{reload-connect-toast-D1Iqw9E_.js.map → reload-connect-toast-BkgP9i6_.js.map} +1 -1
  67. package/dist/start-connect.js +1 -1
  68. package/dist/style.css +76 -7
  69. package/dist/{toast-4t-VimvD.js → toast-BLv8rjmy.js} +3 -3
  70. package/dist/{toast-4t-VimvD.js.map → toast-BLv8rjmy.js.map} +1 -1
  71. package/dist/use-renown-login-BtGljnB4.js +52 -0
  72. package/dist/use-renown-login-BtGljnB4.js.map +1 -0
  73. package/dist/useRegistryPackages-DMhCvTWq.js +314 -0
  74. package/dist/useRegistryPackages-DMhCvTWq.js.map +1 -0
  75. package/package.json +15 -11
  76. package/dist/AddDriveModal-MMHlkJBJ.js.map +0 -1
  77. package/dist/DriveAuthRequiredModal-BA2vS3oF.js +0 -40
  78. package/dist/DriveAuthRequiredModal-BA2vS3oF.js.map +0 -1
  79. package/dist/MissingPackageModal-BQcCQbOn.js.map +0 -1
  80. package/dist/SettingsModal-DVxrOHRl.js.map +0 -1
  81. package/dist/app-loader-Bl5S9rhZ.js +0 -4
  82. package/dist/app-loader-D5VE_BWh.js.map +0 -1
  83. package/dist/components-B80uXxs-.js +0 -5
  84. package/dist/connect.config-vgUeFoNp.js.map +0 -1
  85. package/dist/package-D_aVpf8t.js.map +0 -1
  86. package/dist/reactor-BQyfLv_t.js.map +0 -1
  87. package/dist/registerServiceWorker-nJIc0yEY.js.map +0 -1
  88. package/dist/useRegistryPackages-BaYtekjQ.js +0 -179
  89. package/dist/useRegistryPackages-BaYtekjQ.js.map +0 -1
@@ -1,19 +1,20 @@
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]="4530617e-df9b-597a-a2d8-dfb8bb42d166")}catch(e){}}();
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="b538cac1-1d6d-5f23-97fc-691b3cf37e28")}catch(e){}}();
3
3
  import { n as getRuntimeConfig } from "./runtime-config-DL3QjcfM.js";
4
4
  import { c as idbError } from "./pglite-major-B39aOEiE.js";
5
5
  import { n as cloneFileData, r as migrateIdb } from "./pglite-migrate-core-Boxqqx-c.js";
6
- import { i as i18n, n as setSentryModule } from "./reload-connect-toast-D1Iqw9E_.js";
7
- import { a as defaultPHDocumentEditorConfig, i as defaultPHAppConfig, r as connectConfig } from "./connect.config-vgUeFoNp.js";
8
- import { f as PRIMARY_IDB_NAMES, r as subscribeWorkerConnection, s as getCachedMigrationMajor, t as getWorkerConnectionStatus, u as subscribeMigrationMajor } from "./connection-state-DO_2lAEJ.js";
9
- import { n as serviceWorkerManager, r as subscribeServiceWorkerUpdate, t as getServiceWorkerUpdateAvailable } from "./registerServiceWorker-nJIc0yEY.js";
10
- import { n as toast, t as ToastContainer } from "./toast-4t-VimvD.js";
6
+ import { i as i18n, n as setSentryModule } from "./reload-connect-toast-BkgP9i6_.js";
7
+ import { a as defaultPHDocumentEditorConfig, i as defaultPHAppConfig, r as connectConfig } from "./connect.config-C-b_Yhd6.js";
8
+ import { f as PRIMARY_IDB_NAMES, r as subscribeWorkerConnection, s as getCachedMigrationMajor, t as getWorkerConnectionStatus, u as subscribeMigrationMajor } from "./connection-state-KCa1yljA.js";
9
+ import { n as serviceWorkerManager, r as subscribeServiceWorkerUpdate, t as getServiceWorkerUpdateAvailable } from "./registerServiceWorker-Bn1KWmk0.js";
10
+ import { n as toast, t as ToastContainer } from "./toast-BLv8rjmy.js";
11
+ import { t as useOpenRenownLogin } from "./use-renown-login-BtGljnB4.js";
11
12
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
12
13
  import { Trans, useTranslation } from "react-i18next";
13
14
  import React, { StrictMode, Suspense, lazy, useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
14
- import { DriveCollectionId, getRevisionFromDate, initTheme, logout, openRenown, setPHAppConfig, setPHDocumentEditorConfig, setRevisionHistoryVisible, setSelectedDrive, setSelectedNode, showPHModal, useAppModuleById, useConnectionStates, useDefaultAppModule, useDocumentById, useDocumentModelModuleById, useDocumentOperations, useDrives, useEditorModuleById, useEditorPreloader, useFallbackEditorModule, useIsAddDriveEnabled, useNodeParentFolderById, usePHModal, usePackageDiscoveryService, useReactorClientModule, useRevisionHistoryVisible, useSelectedDocument, useSelectedDocumentId, useSelectedDriveSafe, useSelectedFolder, useSelectedTimelineItem, useSyncList, useTheme, useUser, useVetraPackageManager, useVetraPackages } from "@powerhousedao/reactor-browser";
15
+ import { DriveCollectionId, RenownWalletProvider, addFileWithProgress, getRevisionFromDate, initTheme, logout, setPHAppConfig, setPHDocumentEditorConfig, setRevisionHistoryVisible, setSelectedDrive, setSelectedNode, showPHModal, useAppModuleById, useConnectionStates, useDefaultAppModule, useDocumentById, useDocumentModelModuleById, useDocumentOperations, useDrives, useEditorModuleById, useEditorPreloader, useFallbackEditorModule, useIsAddDriveEnabled, useNodeParentFolderById, usePHModal, usePackageDiscoveryService, useReactorClientModule, useRevisionHistoryVisible, useSelectedDocument, useSelectedDocumentId, useSelectedDriveSafe, useSelectedFolder, useSelectedTimelineItem, useSyncList, useTheme, useUser, useVetraPackageManager, useVetraPackages } from "@powerhousedao/reactor-browser";
15
16
  import { childLogger } from "document-model";
16
- import { ConnectSidebar, ConnectTooltipProvider, CookieBanner, DefaultEditorLoader, DriveAuthGate, DropZoneWrapper, HomeScreen, HomeScreenAddDriveItem, HomeScreenItem, LogoAnimation, PackageInstallModal, RevisionHistory, SidebarAddDriveItem, SidebarItem, useEns } from "@powerhousedao/design-system/connect";
17
+ import { ConnectSidebar, ConnectTooltipProvider, CookieBanner, DefaultEditorLoader, DriveAuthGate, DropZoneWrapper, HomeScreen, HomeScreenAddDriveItem, HomeScreenItem, LogoAnimation, PackageInstallModal, RevisionHistory, SidebarAddDriveItem, SidebarItem, UploadFileListContainer, formatFileSize, mapProgressStageToStatus, useEns } from "@powerhousedao/design-system/connect";
17
18
  import { z } from "zod";
18
19
  import { Outlet, RouterProvider, createBrowserRouter, createRoutesFromChildren, matchRoutes, useLocation, useNavigationType } from "react-router-dom";
19
20
  import { useHotkeys } from "react-hotkeys-hook";
@@ -21,6 +22,10 @@ import { Icon, Modal, PowerhouseButton } from "@powerhousedao/design-system";
21
22
  import { ErrorBoundary } from "react-error-boundary";
22
23
  import { GenericDriveExplorer } from "@powerhousedao/powerhouse-vetra-packages/editors";
23
24
  import { twMerge } from "tailwind-merge";
25
+ import { mockAdapter } from "@renown/sdk/wallet/mock";
26
+ import { privyAdapter } from "@renown/sdk/wallet/privy";
27
+ import { rainbowAdapter } from "@renown/sdk/wallet/rainbow";
28
+ import { arbitrum, base, mainnet, optimism, polygon, sepolia } from "wagmi/chains";
24
29
  import { redo, undo } from "@powerhousedao/shared/document-model";
25
30
  //#region src/utils/browser.ts
26
31
  function getBasePath() {
@@ -75,6 +80,184 @@ var DocumentEditorDebugTools = class {
75
80
  }
76
81
  };
77
82
  //#endregion
83
+ //#region src/utils/launch-queue.ts
84
+ let pendingImportFiles = [];
85
+ const pendingListeners = /* @__PURE__ */ new Set();
86
+ function notifyPendingListeners() {
87
+ for (const listener of pendingListeners) listener();
88
+ }
89
+ function getPendingImportFiles() {
90
+ return pendingImportFiles;
91
+ }
92
+ /** Stable identity of a pending file (also the picker's pre-check cache key). */
93
+ function pendingFileKey(file) {
94
+ return `${file.name}|${file.size}|${file.lastModified}`;
95
+ }
96
+ function addPendingImportFiles(files) {
97
+ const seen = new Set(pendingImportFiles.map(pendingFileKey));
98
+ const fresh = files.filter((file) => {
99
+ const key = pendingFileKey(file);
100
+ if (seen.has(key)) return false;
101
+ seen.add(key);
102
+ return true;
103
+ });
104
+ if (fresh.length === 0) return;
105
+ pendingImportFiles = [...pendingImportFiles, ...fresh];
106
+ notifyPendingListeners();
107
+ }
108
+ function clearPendingImportFiles() {
109
+ if (pendingImportFiles.length === 0) return;
110
+ pendingImportFiles = [];
111
+ notifyPendingListeners();
112
+ }
113
+ function subscribePendingImportFiles(cb) {
114
+ pendingListeners.add(cb);
115
+ return () => {
116
+ pendingListeners.delete(cb);
117
+ };
118
+ }
119
+ async function handleLaunchParams(params) {
120
+ const handles = params.files.filter((handle) => handle.kind === "file");
121
+ if (handles.length === 0) return;
122
+ const files = [];
123
+ for (const handle of handles) try {
124
+ files.push(await handle.getFile());
125
+ } catch (error) {
126
+ console.error("Failed to read launched file:", handle.name, error);
127
+ }
128
+ if (files.length === 0) return;
129
+ addPendingImportFiles(files);
130
+ showPHModal({ type: "openFileDocuments" });
131
+ }
132
+ function maybeReopenImportModal() {
133
+ if (getPendingImportFiles().length === 0) return;
134
+ if (window.ph?.modal !== void 0) return;
135
+ showPHModal({ type: "openFileDocuments" });
136
+ }
137
+ let initialized = false;
138
+ /**
139
+ * Register the launchQueue consumer that receives files opened with Connect
140
+ * through the OS file association. Idempotent (safe under StrictMode double
141
+ * effects) and a no-op where the File Handling API doesn't exist — Firefox,
142
+ * Safari, and any context without an installed PWA manifest simply never
143
+ * deliver launches.
144
+ */
145
+ function initLaunchQueueFileHandling() {
146
+ if (initialized) return;
147
+ initialized = true;
148
+ window.addEventListener("ph:modalUpdated", maybeReopenImportModal);
149
+ window.launchQueue?.setConsumer((params) => {
150
+ handleLaunchParams(params);
151
+ });
152
+ }
153
+ //#endregion
154
+ //#region src/utils/open-file-uploads.ts
155
+ let uploads = [];
156
+ const listeners$3 = /* @__PURE__ */ new Set();
157
+ function notifyUploadListeners() {
158
+ for (const listener of listeners$3) listener();
159
+ }
160
+ function getOpenFileUploads() {
161
+ return uploads;
162
+ }
163
+ function subscribeOpenFileUploads(cb) {
164
+ listeners$3.add(cb);
165
+ return () => {
166
+ listeners$3.delete(cb);
167
+ };
168
+ }
169
+ function removeOpenFileUpload(id) {
170
+ const next = uploads.filter((upload) => upload.id !== id);
171
+ if (next.length === uploads.length) return;
172
+ uploads = next;
173
+ notifyUploadListeners();
174
+ }
175
+ function clearOpenFileUploads() {
176
+ if (uploads.length === 0) return;
177
+ uploads = [];
178
+ notifyUploadListeners();
179
+ }
180
+ function addOpenFileUploads(rows) {
181
+ uploads = [...uploads, ...rows];
182
+ notifyUploadListeners();
183
+ }
184
+ function patchOpenFileUpload(id, patch) {
185
+ uploads = uploads.map((upload) => upload.id === id ? {
186
+ ...upload,
187
+ ...patch
188
+ } : upload);
189
+ notifyUploadListeners();
190
+ }
191
+ let uploadCounter = 0;
192
+ let importChain = Promise.resolve();
193
+ /**
194
+ * Import one file and resolve once the flow truly ends. Completion is
195
+ * tracked through the progress stages, NOT the returned promise — the
196
+ * package-discovery retry path resolves early and keeps reporting through
197
+ * the callback. `resolveConflict: "duplicate"` is always passed: duplicates
198
+ * were resolved in the picker (rename), so the conflict stage must never
199
+ * fire — this also covers a duplicate appearing between pre-check and import.
200
+ */
201
+ function importOne(row, driveId, documentTypes) {
202
+ return new Promise((resolve) => {
203
+ let settled = false;
204
+ const settle = (fileNode) => {
205
+ if (settled) return;
206
+ settled = true;
207
+ resolve(fileNode);
208
+ };
209
+ addFileWithProgress(row.file, driveId, row.name, void 0, (progress) => {
210
+ patchOpenFileUpload(row.id, {
211
+ status: mapProgressStageToStatus(progress.stage),
212
+ progress: progress.progress,
213
+ ...progress.error ? { errorDetails: progress.error } : {},
214
+ ...progress.documentType ? { documentType: progress.documentType } : {},
215
+ ...progress.fileNode ? { fileNode: progress.fileNode } : {}
216
+ });
217
+ switch (progress.stage) {
218
+ case "complete":
219
+ settle(progress.fileNode);
220
+ break;
221
+ case "failed":
222
+ case "unsupported-document-type":
223
+ settle(void 0);
224
+ break;
225
+ default: break;
226
+ }
227
+ }, documentTypes, "duplicate").catch(() => {
228
+ settle(void 0);
229
+ });
230
+ });
231
+ }
232
+ /**
233
+ * Run a confirmed batch of imports into a drive, feeding the floating upload
234
+ * panel. Rows appear immediately (queued), files import sequentially, and a
235
+ * single-file batch that succeeds opens its document in the editor.
236
+ */
237
+ function runOpenFileImports(args) {
238
+ const { driveId, imports, documentTypes } = args;
239
+ if (imports.length === 0) return importChain;
240
+ const rows = imports.map(({ file, name }) => ({
241
+ id: `open-file-${++uploadCounter}`,
242
+ file,
243
+ name
244
+ }));
245
+ addOpenFileUploads(rows.map((row) => ({
246
+ id: row.id,
247
+ fileName: row.name,
248
+ fileSize: formatFileSize(row.file.size),
249
+ status: "pending",
250
+ progress: 0
251
+ })));
252
+ const run = async () => {
253
+ const results = [];
254
+ for (const row of rows) results.push(await importOne(row, driveId, documentTypes));
255
+ if (rows.length === 1 && results[0]) setSelectedNode(results[0]);
256
+ };
257
+ importChain = importChain.then(run, run);
258
+ return importChain;
259
+ }
260
+ //#endregion
78
261
  //#region src/components/migration-status.ts
79
262
  let current = null;
80
263
  const listeners$2 = /* @__PURE__ */ new Set();
@@ -708,7 +891,7 @@ function SilentFallback() {
708
891
  */
709
892
  function TextFallback({ message = "Something went wrong" }) {
710
893
  return /* @__PURE__ */ jsx("div", {
711
- className: "text-center text-foreground",
894
+ className: "text-center wrap-break-word text-foreground",
712
895
  children: message
713
896
  });
714
897
  }
@@ -736,7 +919,7 @@ function DetailedFallback({ error, resetErrorBoundary }) {
736
919
  })]
737
920
  }),
738
921
  /* @__PURE__ */ jsx("p", {
739
- className: "mb-4 text-sm text-foreground",
922
+ className: "mb-4 text-sm wrap-break-word text-foreground",
740
923
  children: errorMessage
741
924
  }),
742
925
  errorDetails !== "{}" && /* @__PURE__ */ jsxs("details", {
@@ -766,9 +949,9 @@ function DetailedFallback({ error, resetErrorBoundary }) {
766
949
  */
767
950
  function CenteredErrorMessage({ error }) {
768
951
  return /* @__PURE__ */ jsx("div", {
769
- className: "flex size-full items-center justify-center",
952
+ className: "flex size-full items-center justify-center p-6",
770
953
  children: /* @__PURE__ */ jsx("h3", {
771
- className: "text-lg font-semibold text-foreground",
954
+ className: "max-w-lg min-w-0 text-center text-lg font-semibold wrap-break-word text-foreground",
772
955
  children: error instanceof Error ? error.message : String(error)
773
956
  })
774
957
  });
@@ -1106,16 +1289,38 @@ function canRetry(reason) {
1106
1289
  }
1107
1290
  function MissingModelBanner() {
1108
1291
  const failed = useFailedInstallations();
1292
+ const discoveryService = usePackageDiscoveryService();
1109
1293
  const [open, setOpen] = useState(false);
1294
+ const [dismissed, setDismissed] = useState(false);
1295
+ useEffect(() => {
1296
+ return discoveryService?.subscribeEvents((event) => {
1297
+ if (event.type === "load-failed") setDismissed(false);
1298
+ });
1299
+ }, [discoveryService]);
1110
1300
  if (failed.length === 0) return null;
1111
- return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("div", {
1112
- className: "flex items-center justify-between gap-3 bg-warning/10 px-4 py-2 text-sm text-warning",
1113
- children: [/* @__PURE__ */ jsx("span", { children: failed.length === 1 ? "1 document type couldn't load (missing model)." : `${failed.length} document types couldn't load (missing models).` }), /* @__PURE__ */ jsx("button", {
1114
- type: "button",
1115
- onClick: () => setOpen(true),
1116
- className: "rounded-md border border-warning bg-background px-3 py-1 text-warning hover:hover-effect",
1117
- children: "View"
1118
- })]
1301
+ return /* @__PURE__ */ jsxs(Fragment, { children: [dismissed ? null : /* @__PURE__ */ jsx("div", {
1302
+ className: "absolute inset-x-0 top-0 z-200 bg-background",
1303
+ children: /* @__PURE__ */ jsxs("div", {
1304
+ className: "flex items-center justify-between gap-3 bg-warning/10 px-4 py-2 text-sm text-warning",
1305
+ children: [/* @__PURE__ */ jsx("span", { children: failed.length === 1 ? "1 document type couldn't load (missing model)." : `${failed.length} document types couldn't load (missing models).` }), /* @__PURE__ */ jsxs("div", {
1306
+ className: "flex items-center gap-2",
1307
+ children: [/* @__PURE__ */ jsx("button", {
1308
+ type: "button",
1309
+ onClick: () => setOpen(true),
1310
+ className: "rounded-md border border-warning bg-background px-3 py-1 text-warning hover:hover-effect",
1311
+ children: "View"
1312
+ }), /* @__PURE__ */ jsx("button", {
1313
+ type: "button",
1314
+ onClick: () => setDismissed(true),
1315
+ "aria-label": "Dismiss",
1316
+ className: "flex items-center text-warning hover:hover-effect",
1317
+ children: /* @__PURE__ */ jsx(Icon, {
1318
+ name: "XmarkLight",
1319
+ size: 16
1320
+ })
1321
+ })]
1322
+ })]
1323
+ })
1119
1324
  }), /* @__PURE__ */ jsx(MissingModelDetailsModal, {
1120
1325
  open,
1121
1326
  failed,
@@ -1218,6 +1423,18 @@ function MissingModelDetailsModal(props) {
1218
1423
  });
1219
1424
  }
1220
1425
  //#endregion
1426
+ //#region src/components/open-file-upload-list.tsx
1427
+ function OpenFileUploadList() {
1428
+ const uploads = useSyncExternalStore(subscribeOpenFileUploads, getOpenFileUploads);
1429
+ return /* @__PURE__ */ jsx(UploadFileListContainer, {
1430
+ uploadsArray: uploads,
1431
+ uploadsCount: uploads.length,
1432
+ removeUpload: removeOpenFileUpload,
1433
+ clearAllUploads: clearOpenFileUploads,
1434
+ setSelectedNode
1435
+ });
1436
+ }
1437
+ //#endregion
1221
1438
  //#region src/components/package-install-prompt.tsx
1222
1439
  function PackageInstallPrompt() {
1223
1440
  const pendingInstallations = usePendingInstallations();
@@ -1233,6 +1450,87 @@ function PackageInstallPrompt() {
1233
1450
  });
1234
1451
  }
1235
1452
  //#endregion
1453
+ //#region src/components/renown-adapter-descriptors.ts
1454
+ function describe(id, build) {
1455
+ try {
1456
+ return [build()];
1457
+ } catch (error) {
1458
+ console.error(`[connect] Ignoring the "${id}" wallet adapter: connect.renown.adapters.${id} was rejected.`, error);
1459
+ return [];
1460
+ }
1461
+ }
1462
+ const CHAINS_BY_ID = new Map([
1463
+ mainnet,
1464
+ sepolia,
1465
+ polygon,
1466
+ optimism,
1467
+ arbitrum,
1468
+ base
1469
+ ].map((chain) => [chain.id, chain]));
1470
+ function chainsFor(chainId) {
1471
+ if (chainId === void 0) return void 0;
1472
+ const chain = CHAINS_BY_ID.get(chainId);
1473
+ if (!chain) {
1474
+ console.error(`[connect] connect.renown.chainId ${chainId} is not a chain Connect can offer, so in-page sign-in will be rejected. Supported: ${[...CHAINS_BY_ID.keys()].join(", ")}.`);
1475
+ return;
1476
+ }
1477
+ return [chain];
1478
+ }
1479
+ /** Map Connect's declarative `connect.renown.adapters` config to the descriptors `RenownWalletProvider` takes. Array order sets the order of the login buttons. A rejected adapter config is dropped (logged), leaving the others working. `chainId` is `connect.renown.chainId`, the chain Renown issues credentials on. */
1480
+ function configToDescriptors(config, chainId) {
1481
+ if (!config) return [];
1482
+ const { rainbow, privy, mock } = config;
1483
+ const descriptors = [];
1484
+ const chains = chainsFor(chainId);
1485
+ if (rainbow) descriptors.push(...describe("rainbow", () => rainbowAdapter({
1486
+ ...rainbow,
1487
+ ...chains ? { chains } : {}
1488
+ })));
1489
+ if (privy) {
1490
+ const { appId, clientId, methods } = privy;
1491
+ if (appId) descriptors.push(...describe("privy", () => privyAdapter({
1492
+ appId,
1493
+ clientId,
1494
+ methods
1495
+ })));
1496
+ }
1497
+ if (mock) descriptors.push(...describe("mock", () => mockAdapter({
1498
+ ...mock,
1499
+ methods: mock.methods
1500
+ })));
1501
+ return descriptors;
1502
+ }
1503
+ //#endregion
1504
+ //#region src/components/renown-wallet-providers.tsx
1505
+ function resolveColor(expr, fallback) {
1506
+ if (typeof document === "undefined") return fallback;
1507
+ const probe = document.createElement("span");
1508
+ probe.style.color = fallback;
1509
+ probe.style.color = expr;
1510
+ probe.style.display = "none";
1511
+ document.body.appendChild(probe);
1512
+ const resolved = getComputedStyle(probe).color;
1513
+ probe.remove();
1514
+ return resolved || fallback;
1515
+ }
1516
+ function RenownWalletProviders({ children }) {
1517
+ const config = getRuntimeConfig().connect.renown?.adapters;
1518
+ const chainId = getRuntimeConfig().connect.renown?.chainId;
1519
+ const { theme } = useTheme();
1520
+ const adapters = useMemo(() => configToDescriptors(config, chainId), [config, chainId]);
1521
+ const walletTheme = useMemo(() => adapters.length > 0 ? {
1522
+ mode: theme,
1523
+ accentColor: resolveColor("var(--primary)", "#0084ff"),
1524
+ accentColorForeground: resolveColor("var(--primary-foreground)", "#ffffff")
1525
+ } : void 0, [theme, adapters]);
1526
+ if (adapters.length === 0) return /* @__PURE__ */ jsx(Fragment, { children });
1527
+ return /* @__PURE__ */ jsx(RenownWalletProvider, {
1528
+ adapters,
1529
+ theme: walletTheme,
1530
+ children
1531
+ });
1532
+ }
1533
+ //#endregion
1236
1534
  //#region src/components/app.tsx
1237
1535
  const App = () => {
1238
1536
  useTheme();
@@ -1258,14 +1556,18 @@ const App = () => {
1258
1556
  else if (connectConfig.offline) serviceWorkerManager.registerServiceWorker(false);
1259
1557
  else serviceWorkerManager.unregisterAll();
1260
1558
  }, []);
1261
- return /* @__PURE__ */ jsxs(SentryProvider, { children: [
1559
+ useEffect(() => {
1560
+ initLaunchQueueFileHandling();
1561
+ }, []);
1562
+ return /* @__PURE__ */ jsx(SentryProvider, { children: /* @__PURE__ */ jsxs(RenownWalletProviders, { children: [
1262
1563
  /* @__PURE__ */ jsx(ToastContainer, { position: "bottom-right" }),
1263
1564
  /* @__PURE__ */ jsx(MissingModelBanner, {}),
1264
1565
  /* @__PURE__ */ jsx(Router, {}),
1566
+ /* @__PURE__ */ jsx(OpenFileUploadList, {}),
1265
1567
  /* @__PURE__ */ jsx(PackageInstallPrompt, {}),
1266
1568
  /* @__PURE__ */ jsx(Analytics, {}),
1267
1569
  /* @__PURE__ */ jsx(OpenPanel, {})
1268
- ] });
1570
+ ] }) });
1269
1571
  };
1270
1572
  //#endregion
1271
1573
  //#region src/components/cookie-banner.tsx
@@ -1351,15 +1653,37 @@ function DriveIcon({ drive }) {
1351
1653
  }
1352
1654
  //#endregion
1353
1655
  //#region src/components/editors.tsx
1354
- function EditorError({ message }) {
1656
+ function EditorError({ title, children }) {
1355
1657
  return /* @__PURE__ */ jsx("div", {
1356
- className: "flex size-full items-center justify-center",
1357
- children: /* @__PURE__ */ jsx("h3", {
1358
- className: "text-lg font-semibold",
1359
- children: message
1658
+ className: "flex size-full items-center justify-center p-6",
1659
+ children: /* @__PURE__ */ jsxs("div", {
1660
+ className: "w-full max-w-lg rounded-lg border border-border bg-card p-6 text-foreground shadow-sm",
1661
+ children: [/* @__PURE__ */ jsxs("div", {
1662
+ className: "mb-3 flex items-center gap-2",
1663
+ children: [/* @__PURE__ */ jsx(Icon, {
1664
+ name: "Error",
1665
+ className: "size-5 shrink-0 text-destructive"
1666
+ }), /* @__PURE__ */ jsx("h3", {
1667
+ className: "min-w-0 text-lg font-semibold wrap-break-word text-foreground",
1668
+ children: title
1669
+ })]
1670
+ }), /* @__PURE__ */ jsx("div", {
1671
+ className: "wrap-break-word",
1672
+ children
1673
+ })]
1360
1674
  })
1361
1675
  });
1362
1676
  }
1677
+ function OpenPackageManagerButton() {
1678
+ return /* @__PURE__ */ jsx(PowerhouseButton, {
1679
+ className: "px-3 py-1.5 text-base font-medium",
1680
+ onClick: () => {
1681
+ showPHModal({ type: "settings" });
1682
+ },
1683
+ type: "button",
1684
+ children: "Open package manager"
1685
+ });
1686
+ }
1363
1687
  const DocumentEditor = (props) => {
1364
1688
  const { document: initialDocument } = props;
1365
1689
  const selectedTimelineItem = useSelectedTimelineItem();
@@ -1414,46 +1738,28 @@ const DocumentEditor = (props) => {
1414
1738
  };
1415
1739
  if (isLoadingEditor) return /* @__PURE__ */ jsx(EditorLoader, { message: "Loading editor" });
1416
1740
  if (isLoadingDocument) return /* @__PURE__ */ jsx(EditorLoader, { message: "Loading document" });
1417
- if (!documentModelModule) return /* @__PURE__ */ jsx(EditorError, { message: /* @__PURE__ */ jsxs("div", {
1418
- className: "text-center leading-10",
1419
- children: [/* @__PURE__ */ jsxs("p", { children: [
1420
- "Unable to open the document because the document model \"",
1421
- documentType,
1422
- "\" is not supported."
1423
- ] }), /* @__PURE__ */ jsxs("p", { children: [
1424
- "Go to the",
1425
- " ",
1426
- /* @__PURE__ */ jsx("button", {
1427
- type: "button",
1428
- className: "cursor-pointer underline",
1429
- onClick: () => {
1430
- showPHModal({ type: "settings" });
1431
- },
1432
- children: "package manager"
1433
- }),
1434
- " ",
1435
- "to install this document model"
1436
- ] })]
1437
- }) });
1438
- if (!editorModule) return /* @__PURE__ */ jsx(EditorError, { message: /* @__PURE__ */ jsxs("div", {
1439
- className: "text-center leading-10",
1440
- children: [/* @__PURE__ */ jsx("p", { children: "Unable to open the document because no editor has been found" }), /* @__PURE__ */ jsxs("p", { children: [
1441
- "Go to the",
1442
- " ",
1443
- /* @__PURE__ */ jsx("button", {
1444
- type: "button",
1445
- className: "cursor-pointer underline",
1446
- onClick: () => {
1447
- showPHModal({ type: "settings" });
1448
- },
1449
- children: "package manager"
1450
- }),
1451
- " ",
1452
- "an editor for the \"$",
1453
- documentType,
1454
- "\" document type"
1455
- ] })]
1456
- }) });
1741
+ if (!documentModelModule) return /* @__PURE__ */ jsxs(EditorError, {
1742
+ title: "Document type not supported",
1743
+ children: [/* @__PURE__ */ jsxs("p", {
1744
+ className: "mb-4 text-sm text-foreground",
1745
+ children: [
1746
+ "This document can't be opened because the \"",
1747
+ documentType,
1748
+ "\" document model isn't installed. Install the package that provides it to open the document."
1749
+ ]
1750
+ }), /* @__PURE__ */ jsx(OpenPackageManagerButton, {})]
1751
+ });
1752
+ if (!editorModule) return /* @__PURE__ */ jsxs(EditorError, {
1753
+ title: "No editor available",
1754
+ children: [/* @__PURE__ */ jsxs("p", {
1755
+ className: "mb-4 text-sm text-foreground",
1756
+ children: [
1757
+ "The \"",
1758
+ documentType,
1759
+ "\" document model is installed, but no editor for it was found. Install a package that provides an editor for this document type."
1760
+ ]
1761
+ }), /* @__PURE__ */ jsx(OpenPackageManagerButton, {})]
1762
+ });
1457
1763
  const EditorComponent = editorModule.Component;
1458
1764
  return /* @__PURE__ */ jsx("div", {
1459
1765
  className: "relative h-full",
@@ -1532,6 +1838,7 @@ function useDriveAuthGate() {
1532
1838
  //#region src/pages/content.tsx
1533
1839
  function Content() {
1534
1840
  const { gate } = useDriveAuthGate();
1841
+ const openLogin = useOpenRenownLogin();
1535
1842
  const [selectedDrive] = useSelectedDriveSafe();
1536
1843
  const selectedFolder = useSelectedFolder();
1537
1844
  const selectedDocumentId = useSelectedDocumentId();
@@ -1545,7 +1852,7 @@ function Content() {
1545
1852
  className: "flex h-full items-center justify-center p-4",
1546
1853
  children: /* @__PURE__ */ jsx(DriveAuthGate, {
1547
1854
  mode: gate,
1548
- onLogin: openRenown,
1855
+ onLogin: openLogin,
1549
1856
  onLogout: () => void logout()
1550
1857
  })
1551
1858
  }) : !selectedDocumentId && !selectedDrive && !selectedFolder ? /* @__PURE__ */ jsx(HomeScreenContainer, {}) : /* @__PURE__ */ jsx(AppContainer, {}) });
@@ -1613,6 +1920,7 @@ const Router = () => {
1613
1920
  //#region src/components/sidebar.tsx
1614
1921
  function Sidebar() {
1615
1922
  const user = useUser();
1923
+ const openLogin = useOpenRenownLogin();
1616
1924
  const drives = useDrives();
1617
1925
  const isAddDriveEnabled = useIsAddDriveEnabled();
1618
1926
  const [selectedDrive] = useSelectedDriveSafe();
@@ -1632,7 +1940,7 @@ function Sidebar() {
1632
1940
  onClick: () => setSelectedDrive(void 0),
1633
1941
  onClickSettings,
1634
1942
  address: user?.address,
1635
- onLogin: openRenown,
1943
+ onLogin: openLogin,
1636
1944
  onDisconnect: () => {
1637
1945
  logout();
1638
1946
  },
@@ -1789,18 +2097,18 @@ const MigrationBanner = () => {
1789
2097
  };
1790
2098
  //#endregion
1791
2099
  //#region src/components/modal/modals-container.tsx
1792
- const AddDriveModal$1 = lazy(() => import("./AddDriveModal-MMHlkJBJ.js").then((m) => ({ default: m.AddDriveModal })));
1793
- const ClearStorageModal = lazy(() => import("./ClearStorageModal-B2RYDgLq.js").then((m) => ({ default: m.ClearStorageModal })));
1794
- const CookiesPolicyModal = lazy(() => import("./CookiesPolicyModal-CkcUKPLb.js").then((m) => ({ default: m.CookiesPolicyModal })));
1795
- const CreateDocumentModal$1 = lazy(() => import("./CreateDocumentModal-CvsTh0HM.js").then((m) => ({ default: m.CreateDocumentModal })));
1796
- const DebugSettingsModal = lazy(() => import("./DebugSettingsModal-DcoIFbGH.js").then((m) => ({ default: m.DebugSettingsModal })));
1797
- const DeleteDriveModal = lazy(() => import("./DeleteDriveModal-DSTK8FYk.js").then((m) => ({ default: m.DeleteDriveModal })));
1798
- const DeleteItemModal = lazy(() => import("./DeleteItemModal-B2nJmjsP.js").then((m) => ({ default: m.DeleteItemModal })));
1799
- const DisclaimerModal = lazy(() => import("./DisclaimerModal-D5FO34u9.js").then((m) => ({ default: m.DisclaimerModal })));
1800
- const DriveSettingsModal$1 = lazy(() => import("./DriveSettingsModal-kR3hQnh4.js").then((m) => ({ default: m.DriveSettingsModal })));
1801
- const DownloadDocumentWithErrorsModal = lazy(() => import("./DownloadDocumentWithErrorsModal-tEr3bbtN.js").then((m) => ({ default: m.DownloadDocumentWithErrorsModal })));
1802
- const SettingsModal$1 = lazy(() => import("./SettingsModal-DVxrOHRl.js").then((m) => ({ default: m.SettingsModal })));
1803
- const UpgradeDriveModal = lazy(() => import("./UpgradeDriveModal-BZGSE9_r.js").then((m) => ({ default: m.UpgradeDriveModal })));
2100
+ const AddDriveModal$1 = lazy(() => import("./AddDriveModal-mTJUAV2Z.js").then((m) => ({ default: m.AddDriveModal })));
2101
+ const ClearStorageModal = lazy(() => import("./ClearStorageModal-DzVJ-bz1.js").then((m) => ({ default: m.ClearStorageModal })));
2102
+ const CookiesPolicyModal = lazy(() => import("./CookiesPolicyModal-Dpx-2TgP.js").then((m) => ({ default: m.CookiesPolicyModal })));
2103
+ const CreateDocumentModal$1 = lazy(() => import("./CreateDocumentModal-u-IaJUQ0.js").then((m) => ({ default: m.CreateDocumentModal })));
2104
+ const DebugSettingsModal = lazy(() => import("./DebugSettingsModal-DEIZdxIJ.js").then((m) => ({ default: m.DebugSettingsModal })));
2105
+ const DeleteDriveModal = lazy(() => import("./DeleteDriveModal-CJKhWtli.js").then((m) => ({ default: m.DeleteDriveModal })));
2106
+ const DeleteItemModal = lazy(() => import("./DeleteItemModal-DiieJw_o.js").then((m) => ({ default: m.DeleteItemModal })));
2107
+ const DisclaimerModal = lazy(() => import("./DisclaimerModal-DAvYIF9-.js").then((m) => ({ default: m.DisclaimerModal })));
2108
+ const DriveSettingsModal$1 = lazy(() => import("./DriveSettingsModal-CqFeWu4r.js").then((m) => ({ default: m.DriveSettingsModal })));
2109
+ const DownloadDocumentWithErrorsModal = lazy(() => import("./DownloadDocumentWithErrorsModal-CE4fa1k5.js").then((m) => ({ default: m.DownloadDocumentWithErrorsModal })));
2110
+ const SettingsModal$1 = lazy(() => import("./SettingsModal-DIrHdgma.js").then((m) => ({ default: m.SettingsModal })));
2111
+ const UpgradeDriveModal = lazy(() => import("./UpgradeDriveModal-rUHyJTc7.js").then((m) => ({ default: m.UpgradeDriveModal })));
1804
2112
  const modalComponents = {
1805
2113
  addDrive: AddDriveModal$1,
1806
2114
  clearStorage: ClearStorageModal,
@@ -1812,11 +2120,13 @@ const modalComponents = {
1812
2120
  disclaimer: DisclaimerModal,
1813
2121
  driveSettings: DriveSettingsModal$1,
1814
2122
  downloadDocumentWithErrors: DownloadDocumentWithErrorsModal,
1815
- inspector: lazy(() => import("./InspectorModal-DqFcidAW.js").then((m) => ({ default: m.InspectorModal }))),
2123
+ inspector: lazy(() => import("./InspectorModal-CiFzmU35.js").then((m) => ({ default: m.InspectorModal }))),
1816
2124
  settings: SettingsModal$1,
1817
2125
  upgradeDrive: UpgradeDriveModal,
1818
- missingPackage: lazy(() => import("./MissingPackageModal-BQcCQbOn.js").then((m) => ({ default: m.ConnectMissingPackageModal }))),
1819
- driveAuthRequired: lazy(() => import("./DriveAuthRequiredModal-BA2vS3oF.js").then((m) => ({ default: m.DriveAuthRequiredModal })))
2126
+ missingPackage: lazy(() => import("./MissingPackageModal-C3acJ9r0.js").then((m) => ({ default: m.ConnectMissingPackageModal }))),
2127
+ driveAuthRequired: lazy(() => import("./DriveAuthRequiredModal-BQaAWHTe.js").then((m) => ({ default: m.DriveAuthRequiredModal }))),
2128
+ openFileDocuments: lazy(() => import("./OpenFileDocumentsModal-DN9a0rV4.js").then((m) => ({ default: m.OpenFileDocumentsModal }))),
2129
+ login: lazy(() => import("./LoginModal-CX0RlUEa.js").then((m) => ({ default: m.LoginModal })))
1820
2130
  };
1821
2131
  const ModalsContainer = lazy(() => {
1822
2132
  return Promise.resolve({ default: () => {
@@ -1856,7 +2166,7 @@ const ServiceWorkerUpdatePrompt = () => {
1856
2166
  //#endregion
1857
2167
  //#region src/components/app-loader.tsx
1858
2168
  const AppLoader = (props) => {
1859
- const Load = lazy(() => import("./load-Dwg68U_6.js").then((m) => m.loadComponent(props.localPackage)));
2169
+ const Load = lazy(() => import("./load-DFOEsqzK.js").then((m) => m.loadComponent(props.localPackage)));
1860
2170
  return /* @__PURE__ */ jsx(StrictMode, { children: /* @__PURE__ */ jsxs(ErrorBoundary$1, {
1861
2171
  fallbackRender: (props) => /* @__PURE__ */ jsx(AppSkeleton, { children: /* @__PURE__ */ jsx(DetailedFallback, { ...props }) }),
1862
2172
  resetKeys: [props.localPackage],
@@ -1885,7 +2195,7 @@ const AppLoader = (props) => {
1885
2195
  }) });
1886
2196
  };
1887
2197
  //#endregion
1888
- export { DocumentEditor as a, App as c, DocumentEditorContainer as d, OpenPanel as f, Root as i, AppContainer as l, AppSkeleton as m, Sidebar as n, DriveIcon as o, Analytics as p, Router as r, CookieBanner$1 as s, AppLoader as t, EditorLoader as u };
2198
+ export { getPendingImportFiles as _, DocumentEditor as a, App as c, DocumentEditorContainer as d, OpenPanel as f, clearPendingImportFiles as g, runOpenFileImports as h, Root as i, AppContainer as l, AppSkeleton as m, Sidebar as n, DriveIcon as o, Analytics as p, Router as r, CookieBanner$1 as s, AppLoader as t, EditorLoader as u, pendingFileKey as v, subscribePendingImportFiles as y };
1889
2199
 
1890
- //# sourceMappingURL=app-loader-D5VE_BWh.js.map
1891
- //# debugId=4530617e-df9b-597a-a2d8-dfb8bb42d166
2200
+ //# sourceMappingURL=app-loader-CFsW8a09.js.map
2201
+ //# debugId=b538cac1-1d6d-5f23-97fc-691b3cf37e28