@powerhousedao/connect 1.1.1 → 2.5.0-test.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/dist/.env +2 -1
  2. package/dist/assets/{app-W_mH4m8_.css → app-Djvyy0EQ.css} +145 -2
  3. package/dist/assets/{app-CZSKsD-w.js → app-DnlszXoG.js} +50862 -43671
  4. package/dist/assets/app-DnlszXoG.js.map +1 -0
  5. package/dist/assets/{app-loader-4GyeoNTc.css → app-loader-CDW9U8zV.css} +179 -7
  6. package/dist/assets/{app-loader-CyUyHxMC.js → app-loader-LF_aPRFS.js} +3441 -3983
  7. package/dist/assets/app-loader-LF_aPRFS.js.map +1 -0
  8. package/dist/assets/browser-Bl-r87ld.js +27234 -0
  9. package/dist/assets/browser-Bl-r87ld.js.map +1 -0
  10. package/dist/assets/{ccip-BPO0EeBI.js → ccip-5fOGBE-2.js} +4 -3
  11. package/dist/assets/ccip-5fOGBE-2.js.map +1 -0
  12. package/dist/assets/{content-fzZoyU8t.js → content-ChtzAUHq.js} +1762 -1049
  13. package/dist/assets/content-ChtzAUHq.js.map +1 -0
  14. package/dist/assets/{index-C3PlmBGF.js → index-B8Ch0VYb.js} +4 -3
  15. package/dist/assets/index-B8Ch0VYb.js.map +1 -0
  16. package/dist/assets/{index-DFiZ2Eug.js → index-C9SvsNTs.js} +23 -171
  17. package/dist/assets/index-C9SvsNTs.js.map +1 -0
  18. package/dist/assets/index-ttu2Jrkl.js +208 -0
  19. package/dist/assets/index-ttu2Jrkl.js.map +1 -0
  20. package/dist/assets/{index-D_CfC9G5.js → index-wgNrNkzN.js} +469 -312
  21. package/dist/assets/index-wgNrNkzN.js.map +1 -0
  22. package/dist/assets/{main.8RNzWcZO.js → main.B-rBx6n2.js} +2 -1
  23. package/dist/assets/main.B-rBx6n2.js.map +1 -0
  24. package/dist/assets/reactor-analytics-C42Sftjd.js +42 -0
  25. package/dist/assets/reactor-analytics-C42Sftjd.js.map +1 -0
  26. package/dist/assets/router-DocwpreM.js +1791 -0
  27. package/dist/assets/router-DocwpreM.js.map +1 -0
  28. package/dist/assets/{style-U7Kx3_hE.css → style-C0GVbPTL.css} +6 -3
  29. package/dist/external-packages.js +1 -0
  30. package/dist/external-packages.js.map +1 -0
  31. package/dist/hmr.js +1 -0
  32. package/dist/hmr.js.map +1 -0
  33. package/dist/index.html +2 -1
  34. package/dist/service-worker.js +1 -0
  35. package/dist/service-worker.js.map +1 -0
  36. package/dist/swEnv.js +1 -0
  37. package/dist/vite-envs.sh +10 -1
  38. package/package.json +21 -16
@@ -1,10 +1,11 @@
1
1
  import { jsx, jsxs, Fragment as Fragment$1 } from "react/jsx-runtime";
2
- import { t as twMerge, B as Button, m as mergeClassNameProps, E as ERROR, M as MISSING, C as CONFLICT, S as SUCCESS, k as SYNCING, I as INITIAL_SYNC, l as ConnectDropdownMenu, u as useOnClickOutside, n as useEventListener, o as useCopyToClipboard, p as Select, q as ENSAvatar, P as Provider, v as Root3, T as Trigger, w as Portal, x as Content2, y as validateInitialState, z as validateStateSchemaName, A as validateModules, D as useDocumentDrives, F as useUiNodesContext, G as useDriveContext, H as FILE$1, J as useUnwrappedReactor, K as useConnectDid, L as useConnectCrypto, N as useTranslation, O as useModal, Q as useAtomValue, U as themeAtom, V as useUser, W as useUserPermissions$1, X as useUiNodes, Y as exportFile, Z as useGetDocumentModelModule, _ as addActionContext$1, $ as signOperation$1, a0 as useDocumentDriveServer, a1 as useHotkeys, a2 as useGetEditor, a3 as isSameDocument, a4 as useNavigate, a5 as ErrorBoundary, a6 as DriveLayout, a7 as DriveContextProvider, a8 as SearchBar, a9 as useAsyncReactor, aa as useFilteredDocumentModels, ab as useDriveEditor, ac as useDocumentDriveById, ad as useParams, ae as useDocumentDrives$1, af as toast } from "./app-CZSKsD-w.js";
3
2
  import * as React from "react";
4
3
  import React__default, { useState, useCallback, useMemo, useEffect, Fragment, useRef, useLayoutEffect, memo as memo$1, createElement, useSyncExternalStore, Suspense } from "react";
5
- import { _ as Icon, aX as getDimensions, aY as READ, aZ as nodeOptionsMap, a_ as defaultFileOptions, a$ as DELETE, b0 as RENAME, b1 as WRITE, b2 as DUPLICATE, b3 as defaultFolderOptions, b4 as garbageCollect, b5 as sortOperations, b6 as UI_NODE, aP as DRIVE, az as FILE, ae as buildSignedOperation, b7 as generateId$1, aw as isFolderNode, aB as generateNodesCopy, aC as copyNode, aA as moveNode, ay as updateNode, ax as deleteNode, ar as generateAddNodeAction, as as isFileNode, av as addFolder, b8 as undo, b9 as redo, ac as logger, ba as useDocumentDispatch$1, aQ as FOLDER, al as driveDocumentModelModule } from "./app-loader-CyUyHxMC.js";
4
+ import { t as twMerge, B as Button, aU as mergeClassNameProps, I as Icon, aV as getDimensions, aW as useDocumentDrives, h as useUiNodesContext, aX as useDriveContext, aY as FILE, H as ERROR, aZ as MISSING, G as CONFLICT, S as SUCCESS, a_ as SYNCING, a$ as INITIAL_SYNC, b0 as READ, b1 as nodeOptionsMap, b2 as defaultFileOptions, b3 as ConnectDropdownMenu, b4 as DELETE, b5 as RENAME, b6 as WRITE, b7 as DUPLICATE, b8 as defaultFolderOptions, b9 as useOnClickOutside, ba as useEventListener, bb as useCopyToClipboard, bc as Select, bd as Provider, be as Root3, bf as Trigger, bg as Portal, bh as Content2, bi as UI_NODE, D as DRIVE, F as FILE$1, bj as validateInitialState, bk as validateStateSchemaName, bl as validateModules, e as useDocumentDriveServer, k as useUnwrappedReactor, bm as useConnectDid, bn as useConnectCrypto, x as useTranslation, f as useModal, bo as useAtomValue, bp as themeAtom, bq as useUser, br as useUserPermissions$1, bs as useFileNodeDocument, bt as useGetDocumentModelModule, bu as exportFile, bv as addActionContext$1, bw as signOperation$1, bx as useHotkeys, by as FOLDER, bz as DriveLayout, bA as DriveContextProvider, bB as SearchBar, V as useAnalyticsStore, bC as useQueryClient, u as useQuery, a8 as AnalyticsPath, bD as useAnalyticsEngine, a7 as DateTime, bE as AnalyticsGranularity, bF as useGetEditor, bG as isSameDocument, w as useNavigate, A as useAsyncReactor, bH as useFilteredDocumentModels, bI as useDriveEditor, K as useParams, y as useDocumentDrives$1 } from "./app-DnlszXoG.js";
5
+ import { ab as buildSignedOperation, am as generateId$1, aw as isFolderNode, aA as generateNodesCopy, aB as copyNode, az as moveNode, ay as updateNode, ax as deleteNode, ar as generateAddNodeAction, as as isFileNode, av as addFolder, bU as garbageCollect, bV as sortOperations, bW as undo, bX as redo, _ as logger, bY as openUrl, bZ as useDocumentDispatch$1, aj as module, af as connectConfig } from "./app-loader-LF_aPRFS.js";
6
+ import { E as ENSAvatar, u as useSwitchboard, a as ErrorBoundary, t as toast } from "./router-DocwpreM.js";
6
7
  import { flushSync } from "react-dom";
7
- import "./main.8RNzWcZO.js";
8
+ import "./main.B-rBx6n2.js";
8
9
  const PaginationButton = ({ active = false, ...props }) => {
9
10
  const className = twMerge("h-8 min-w-8 border border-solid border-gray-300 bg-white px-3 py-1 text-xs text-gray-900 hover:bg-gray-100", !active && "border-0");
10
11
  return jsx(Button, { color: "light", size: "small", ...mergeClassNameProps(props, className), children: props.children });
@@ -151,131 +152,12 @@ function DefaultEditorLoader(props) {
151
152
  const { message: message2 = "Loading editor", ...divProps } = props;
152
153
  return jsx("div", { className: "grid h-full place-items-center", ...divProps, children: jsxs("div", { className: "-mt-20 grid place-items-center", children: [jsx("h3", { className: "mb-4 text-xl", children: message2 }), jsx(AnimatedLoader, {})] }) });
153
154
  }
154
- const DocumentToolbar = (props) => {
155
- const { undo: undo2, canUndo, redo: redo2, canRedo, title, onClose, onExport, className, onShowRevisionHistory, onSwitchboardLinkClick, onShowTimeline } = props;
156
- const isUndoDisabled = !canUndo || !undo2;
157
- const isRedoDisabled = !canRedo || !redo2;
158
- const isExportDisabled = !onExport;
159
- const isSwitchboardLinkDisabled = !onSwitchboardLinkClick;
160
- const isRevisionHistoryDisabled = !onShowRevisionHistory;
161
- const isTimelineDisabled = !onShowTimeline;
162
- return jsxs("div", { className: twMerge("flex h-12 w-full items-center justify-between rounded-xl border border-gray-200 bg-slate-50 px-4", className), children: [jsxs("div", { className: "flex items-center gap-x-2", children: [jsx("button", { className: twMerge("grid size-8 place-items-center rounded-lg border border-gray-200 bg-white", isUndoDisabled ? "cursor-not-allowed" : "cursor-pointer active:opacity-70"), onClick: undo2, disabled: isUndoDisabled, children: jsx(Icon, { name: "ArrowCouterclockwise", size: 16, className: isUndoDisabled ? "text-gray-500" : "text-gray-900" }) }), jsx("button", { className: twMerge("grid size-8 place-items-center rounded-lg border border-gray-200 bg-white", isRedoDisabled ? "cursor-not-allowed" : "cursor-pointer active:opacity-70"), onClick: redo2, disabled: isRedoDisabled, children: jsx("div", { className: "-scale-x-100", children: jsx(Icon, { name: "ArrowCouterclockwise", size: 16, className: isRedoDisabled ? "text-gray-500" : "text-gray-900" }) }) }), jsx("button", { className: twMerge("flex h-8 items-center rounded-lg border border-gray-200 bg-white px-3 text-sm", isExportDisabled ? "cursor-not-allowed" : "cursor-pointer active:opacity-70"), onClick: onExport, disabled: isExportDisabled, children: jsx("span", { className: isExportDisabled ? "text-gray-500" : "text-gray-900", children: "Export" }) })] }), jsx("div", { className: "flex items-center", children: jsx("h1", { className: "text-sm font-medium text-gray-500", children: title }) }), jsxs("div", { className: "flex items-center gap-x-2", children: [jsx("button", { className: twMerge("grid size-8 place-items-center rounded-lg border border-gray-200 bg-white", isSwitchboardLinkDisabled ? "cursor-not-allowed" : "cursor-pointer active:opacity-70"), onClick: onSwitchboardLinkClick, disabled: isSwitchboardLinkDisabled, children: jsx(Icon, { name: "Drive", size: 16, className: isSwitchboardLinkDisabled ? "text-gray-500" : "text-gray-900" }) }), jsx("button", { className: twMerge("grid size-8 place-items-center rounded-lg border border-gray-200 bg-white", isRevisionHistoryDisabled ? "cursor-not-allowed" : "cursor-pointer active:opacity-70"), onClick: onShowRevisionHistory, disabled: isRevisionHistoryDisabled, children: jsx(Icon, { name: "History", size: 16, className: isRevisionHistoryDisabled ? "text-gray-500" : "text-gray-900" }) }), jsx("button", { className: twMerge("grid size-8 place-items-center rounded-lg border border-gray-200 bg-white", isTimelineDisabled ? "cursor-not-allowed" : "cursor-pointer active:opacity-70"), onClick: onShowTimeline, disabled: isTimelineDisabled, children: jsx(Icon, { name: "Timeline", size: 16, className: twMerge("text-gray-900", isTimelineDisabled && "opacity-50") }) }), jsx("button", { className: "grid size-8 cursor-pointer place-items-center rounded-lg border border-gray-200 bg-white active:opacity-70", onClick: onClose, children: jsx(Icon, { name: "XmarkLight", size: 16, className: "text-gray-900" }) })] })] });
163
- };
164
- const syncIcons = {
165
- SYNCING: "Syncing",
166
- SUCCESS: "Synced",
167
- CONFLICT: "Error",
168
- MISSING: "Circle",
169
- ERROR: "Error",
170
- INITIAL_SYNC: "Syncing"
171
- };
172
- function SyncStatusIcon(props) {
173
- const { syncStatus, className, overrideSyncIcons = {}, ...iconProps } = props;
174
- const icons = { ...syncIcons, ...overrideSyncIcons };
175
- const syncStatusIcons = {
176
- [INITIAL_SYNC]: jsx(Icon, { size: 16, ...iconProps, className: twMerge("text-blue-900", className), name: icons[INITIAL_SYNC] }),
177
- [SYNCING]: jsx(Icon, { size: 16, ...iconProps, className: twMerge("text-blue-900", className), name: icons[SYNCING] }),
178
- [SUCCESS]: jsx(Icon, { size: 16, ...iconProps, className: twMerge("text-green-900", className), name: icons[SUCCESS] }),
179
- [CONFLICT]: jsx(Icon, { size: 16, ...iconProps, className: twMerge("text-orange-900", className), name: icons[CONFLICT] }),
180
- [MISSING]: jsx(Icon, { size: 16, ...iconProps, className: twMerge("text-red-900", className), name: icons[MISSING] }),
181
- [ERROR]: jsx(Icon, { size: 16, ...iconProps, className: twMerge("text-red-900", className), name: icons[ERROR] })
182
- };
183
- return syncStatusIcons[syncStatus];
184
- }
185
- function FileItem(props) {
186
- const { uiNode, className, customDocumentIconSrc, onSelectNode, onRenameNode, onDuplicateNode, onDeleteNode, isAllowedToCreateDocuments } = props;
187
- const [mode, setMode] = useState(READ);
188
- const [isDropdownMenuOpen, setIsDropdownMenuOpen] = useState(false);
189
- const { dragProps } = useDrag({ uiNode });
190
- const isReadMode = mode === READ;
191
- const dropdownMenuHandlers = {
192
- [DUPLICATE]: () => onDuplicateNode(uiNode),
193
- [RENAME]: () => setMode(WRITE),
194
- [DELETE]: () => onDeleteNode(uiNode)
195
- };
196
- const dropdownMenuOptions = Object.entries(nodeOptionsMap).map(([id, option]) => ({
197
- ...option,
198
- id
199
- })).filter((option) => defaultFileOptions.includes(option.id));
200
- function onSubmit(name) {
201
- onRenameNode(name, uiNode);
202
- setMode(READ);
203
- }
204
- function onCancel() {
205
- setMode(READ);
206
- }
207
- function onClick() {
208
- onSelectNode(uiNode);
209
- }
210
- function onDropdownMenuOptionClick(itemId) {
211
- const handler = dropdownMenuHandlers[itemId];
212
- if (!handler) {
213
- console.error(`No handler found for dropdown menu item: ${itemId}`);
214
- return;
215
- }
216
- handler();
217
- setIsDropdownMenuOpen(false);
218
- }
219
- const iconSrc = getDocumentIconSrc(uiNode.documentType, customDocumentIconSrc);
220
- const iconNode = jsxs("div", { className: "relative", children: [jsx("img", { alt: "file icon", className: "max-w-none", height: 34, src: iconSrc, width: 32 }), isReadMode && uiNode.syncStatus && jsx("div", { className: "absolute bottom-[-2px] right-0 size-3 rounded-full bg-white", children: jsx("div", { className: "absolute left-[-2px] top-[-2px]", children: jsx(SyncStatusIcon, { overrideSyncIcons: { SUCCESS: "CheckCircleFill" }, syncStatus: uiNode.syncStatus }) }) })] });
221
- const containerStyles = twMerge("group flex h-12 cursor-pointer select-none items-center rounded-lg bg-gray-200 px-2 text-gray-600 hover:text-gray-800", className);
222
- const content = isReadMode ? jsxs("div", { className: "flex w-52 items-center justify-between", children: [jsxs("div", { className: "mr-2 truncate group-hover:mr-0", children: [jsx("div", { className: "max-h-6 truncate text-sm font-medium group-hover:text-gray-800", children: uiNode.name }), jsx("div", { className: "max-h-6 truncate text-xs font-medium text-gray-600 group-hover:text-gray-800", children: uiNode.documentType })] }), isAllowedToCreateDocuments ? jsx(ConnectDropdownMenu, { items: dropdownMenuOptions, onItemClick: onDropdownMenuOptionClick, onOpenChange: setIsDropdownMenuOpen, open: isDropdownMenuOpen, children: jsx("button", { className: twMerge("hidden group-hover:block", isDropdownMenuOpen && "block"), onClick: (e) => {
223
- e.stopPropagation();
224
- setIsDropdownMenuOpen(true);
225
- }, children: jsx(Icon, { className: "text-gray-600", name: "VerticalDots" }) }) }) : null] }) : jsx(NodeInput, { className: "ml-3 flex-1 font-medium", defaultValue: uiNode.name, onCancel, onSubmit });
226
- return jsx("div", { className: "relative w-64", onClick, children: jsx("div", { ...dragProps, className: containerStyles, children: jsxs("div", { className: "flex items-center", children: [jsx("div", { className: "mr-1.5", children: iconNode }), content] }) }) });
227
- }
228
- function FolderItem(props) {
229
- const { uiNode, isAllowedToCreateDocuments, className, onRenameNode, onDuplicateNode, onDeleteNode, onSelectNode, onAddFile, onCopyNode, onMoveNode } = props;
230
- const [mode, setMode] = useState(READ);
231
- const [isDropdownMenuOpen, setIsDropdownMenuOpen] = useState(false);
232
- const { dragProps } = useDrag({ ...props, uiNode });
233
- const { isDropTarget, dropProps } = useDrop({
234
- uiNode,
235
- onAddFile,
236
- onCopyNode,
237
- onMoveNode
238
- });
239
- const isReadMode = mode === READ;
240
- function onCancel() {
241
- setMode(READ);
242
- }
243
- function onSubmit(name) {
244
- onRenameNode(name, uiNode);
245
- setMode(READ);
246
- }
247
- function onClick() {
248
- onSelectNode(uiNode);
249
- }
250
- const dropdownMenuHandlers = {
251
- [DUPLICATE]: () => onDuplicateNode(uiNode),
252
- [RENAME]: () => setMode(WRITE),
253
- [DELETE]: () => onDeleteNode(uiNode)
254
- };
255
- const dropdownMenuOptions = Object.entries(nodeOptionsMap).map(([id, option]) => ({
256
- ...option,
257
- id
258
- })).filter((option) => defaultFolderOptions.includes(option.id));
259
- function onDropdownMenuOptionClick(itemId) {
260
- const handler = dropdownMenuHandlers[itemId];
261
- if (!handler) {
262
- console.error(`No handler found for dropdown menu item: ${itemId}`);
263
- return;
264
- }
265
- handler();
266
- setIsDropdownMenuOpen(false);
267
- }
268
- const content = isReadMode || !isAllowedToCreateDocuments ? jsx("div", { className: "ml-3 max-h-6 truncate font-medium text-gray-600 group-hover:text-gray-800", children: uiNode.name }) : jsx(NodeInput, { className: "ml-3 font-medium", defaultValue: uiNode.name, onCancel, onSubmit });
269
- const containerStyles = twMerge("group flex h-12 cursor-pointer select-none items-center rounded-lg bg-gray-200 px-2", className, isDropTarget && "bg-blue-100");
270
- return jsx("div", { className: "relative w-64", onClick, children: jsxs("div", { ...dragProps, ...dropProps, className: containerStyles, children: [jsxs("div", { className: "flex items-center overflow-hidden", children: [jsx("div", { className: "p-1", children: jsxs("div", { className: "relative", children: [jsx(Icon, { name: "FolderClose", size: 24 }), isReadMode && uiNode.syncStatus ? jsx("div", { className: "absolute bottom-[-3px] right-[-2px] size-3 rounded-full bg-white", children: jsx("div", { className: "absolute left-[-2px] top-[-2px]", children: jsx(SyncStatusIcon, { overrideSyncIcons: {
271
- SUCCESS: "CheckCircleFill"
272
- }, syncStatus: uiNode.syncStatus }) }) }) : null] }) }), content] }), isReadMode && isAllowedToCreateDocuments ? jsx(ConnectDropdownMenu, { items: dropdownMenuOptions, onItemClick: onDropdownMenuOptionClick, onOpenChange: setIsDropdownMenuOpen, open: isDropdownMenuOpen, children: jsx("button", { className: twMerge("ml-auto hidden group-hover:block", isDropdownMenuOpen && "block"), onClick: (e) => {
273
- e.stopPropagation();
274
- setIsDropdownMenuOpen(true);
275
- }, children: jsx(Icon, { className: "text-gray-600", name: "VerticalDots" }) }) }) : null] }) });
276
- }
277
155
  const millisecondsInWeek = 6048e5;
278
156
  const millisecondsInDay = 864e5;
157
+ const millisecondsInMinute = 6e4;
158
+ const millisecondsInHour = 36e5;
159
+ const minutesInMonth = 43200;
160
+ const minutesInDay = 1440;
279
161
  const constructFromSymbol = Symbol.for("constructDateFrom");
280
162
  function constructFrom(date, value) {
281
163
  if (typeof date === "function") return date(value);
@@ -343,7 +225,7 @@ function getTimezoneOffsetInMilliseconds(date) {
343
225
  function normalizeDates(context, ...dates) {
344
226
  const normalize = constructFrom.bind(
345
227
  null,
346
- dates.find((date) => typeof date === "object")
228
+ context || dates.find((date) => typeof date === "object")
347
229
  );
348
230
  return dates.map(normalize);
349
231
  }
@@ -371,12 +253,83 @@ function startOfISOWeekYear(date, options) {
371
253
  fourthOfJanuary.setHours(0, 0, 0, 0);
372
254
  return startOfISOWeek(fourthOfJanuary);
373
255
  }
256
+ function compareAsc(dateLeft, dateRight) {
257
+ const diff = +toDate(dateLeft) - +toDate(dateRight);
258
+ if (diff < 0) return -1;
259
+ else if (diff > 0) return 1;
260
+ return diff;
261
+ }
262
+ function constructNow(date) {
263
+ return constructFrom(date, Date.now());
264
+ }
374
265
  function isDate(value) {
375
266
  return value instanceof Date || typeof value === "object" && Object.prototype.toString.call(value) === "[object Date]";
376
267
  }
377
268
  function isValid(date) {
378
269
  return !(!isDate(date) && typeof date !== "number" || isNaN(+toDate(date)));
379
270
  }
271
+ function differenceInCalendarMonths(laterDate, earlierDate, options) {
272
+ const [laterDate_, earlierDate_] = normalizeDates(
273
+ options == null ? void 0 : options.in,
274
+ laterDate,
275
+ earlierDate
276
+ );
277
+ const yearsDiff = laterDate_.getFullYear() - earlierDate_.getFullYear();
278
+ const monthsDiff = laterDate_.getMonth() - earlierDate_.getMonth();
279
+ return yearsDiff * 12 + monthsDiff;
280
+ }
281
+ function getRoundingMethod(method) {
282
+ return (number) => {
283
+ const round = method ? Math[method] : Math.trunc;
284
+ const result = round(number);
285
+ return result === 0 ? 0 : result;
286
+ };
287
+ }
288
+ function differenceInMilliseconds(laterDate, earlierDate) {
289
+ return +toDate(laterDate) - +toDate(earlierDate);
290
+ }
291
+ function endOfDay(date, options) {
292
+ const _date = toDate(date, options == null ? void 0 : options.in);
293
+ _date.setHours(23, 59, 59, 999);
294
+ return _date;
295
+ }
296
+ function endOfMonth(date, options) {
297
+ const _date = toDate(date, options == null ? void 0 : options.in);
298
+ const month = _date.getMonth();
299
+ _date.setFullYear(_date.getFullYear(), month + 1, 0);
300
+ _date.setHours(23, 59, 59, 999);
301
+ return _date;
302
+ }
303
+ function isLastDayOfMonth(date, options) {
304
+ const _date = toDate(date, options == null ? void 0 : options.in);
305
+ return +endOfDay(_date, options) === +endOfMonth(_date, options);
306
+ }
307
+ function differenceInMonths(laterDate, earlierDate, options) {
308
+ const [laterDate_, workingLaterDate, earlierDate_] = normalizeDates(
309
+ options == null ? void 0 : options.in,
310
+ laterDate,
311
+ laterDate,
312
+ earlierDate
313
+ );
314
+ const sign = compareAsc(workingLaterDate, earlierDate_);
315
+ const difference = Math.abs(
316
+ differenceInCalendarMonths(workingLaterDate, earlierDate_)
317
+ );
318
+ if (difference < 1) return 0;
319
+ if (workingLaterDate.getMonth() === 1 && workingLaterDate.getDate() > 27)
320
+ workingLaterDate.setDate(30);
321
+ workingLaterDate.setMonth(workingLaterDate.getMonth() - sign * difference);
322
+ let isLastMonthNotFull = compareAsc(workingLaterDate, earlierDate_) === -sign;
323
+ if (isLastDayOfMonth(laterDate_) && difference === 1 && compareAsc(laterDate_, earlierDate_) === 1) {
324
+ isLastMonthNotFull = false;
325
+ }
326
+ const result = sign * (difference - +isLastMonthNotFull);
327
+ return result === 0 ? 0 : result;
328
+ }
329
+ function differenceInSeconds(laterDate, earlierDate, options) {
330
+ const diff = differenceInMilliseconds(laterDate, earlierDate) / 1e3;
331
+ return getRoundingMethod(options == null ? void 0 : options.roundingMethod)(diff);
332
+ }
380
333
  function startOfYear(date, options) {
381
334
  const date_ = toDate(date, options == null ? void 0 : options.in);
382
335
  date_.setFullYear(date_.getFullYear(), 0, 1);
@@ -446,7 +399,7 @@ const formatDistanceLocale = {
446
399
  other: "almost {{count}} years"
447
400
  }
448
401
  };
449
- const formatDistance = (token, count, options) => {
402
+ const formatDistance$1 = (token, count, options) => {
450
403
  let result;
451
404
  const tokenValue = formatDistanceLocale[token];
452
405
  if (typeof tokenValue === "string") {
@@ -862,7 +815,7 @@ const match = {
862
815
  };
863
816
  const enUS = {
864
817
  code: "en-US",
865
- formatDistance,
818
+ formatDistance: formatDistance$1,
866
819
  formatLong,
867
820
  formatRelative,
868
821
  localize,
@@ -1764,281 +1717,1123 @@ function cleanEscapedString(input) {
1764
1717
  }
1765
1718
  return matched[1].replace(doubleQuoteRegExp, "'");
1766
1719
  }
1767
- function NodeInput(props) {
1768
- const { onSubmit, onCancel, defaultValue, className, minLength = 1, ...inputProps } = props;
1769
- const [value, setValue] = useState(defaultValue ?? "");
1770
- const ref = useRef(null);
1771
- useOnClickOutside(ref, handleSubmit);
1772
- useEventListener("keyup", (e) => {
1773
- if (e.key === "Enter") {
1774
- handleSubmit();
1720
+ function formatDistance(laterDate, earlierDate, options) {
1721
+ const defaultOptions2 = getDefaultOptions();
1722
+ const locale = (options == null ? void 0 : options.locale) ?? defaultOptions2.locale ?? enUS;
1723
+ const minutesInAlmostTwoDays = 2520;
1724
+ const comparison = compareAsc(laterDate, earlierDate);
1725
+ if (isNaN(comparison)) throw new RangeError("Invalid time value");
1726
+ const localizeOptions = Object.assign({}, options, {
1727
+ addSuffix: options == null ? void 0 : options.addSuffix,
1728
+ comparison
1729
+ });
1730
+ const [laterDate_, earlierDate_] = normalizeDates(
1731
+ options == null ? void 0 : options.in,
1732
+ ...comparison > 0 ? [earlierDate, laterDate] : [laterDate, earlierDate]
1733
+ );
1734
+ const seconds = differenceInSeconds(earlierDate_, laterDate_);
1735
+ const offsetInSeconds = (getTimezoneOffsetInMilliseconds(earlierDate_) - getTimezoneOffsetInMilliseconds(laterDate_)) / 1e3;
1736
+ const minutes = Math.round((seconds - offsetInSeconds) / 60);
1737
+ let months;
1738
+ if (minutes < 2) {
1739
+ if (options == null ? void 0 : options.includeSeconds) {
1740
+ if (seconds < 5) {
1741
+ return locale.formatDistance("lessThanXSeconds", 5, localizeOptions);
1742
+ } else if (seconds < 10) {
1743
+ return locale.formatDistance("lessThanXSeconds", 10, localizeOptions);
1744
+ } else if (seconds < 20) {
1745
+ return locale.formatDistance("lessThanXSeconds", 20, localizeOptions);
1746
+ } else if (seconds < 40) {
1747
+ return locale.formatDistance("halfAMinute", 0, localizeOptions);
1748
+ } else if (seconds < 60) {
1749
+ return locale.formatDistance("lessThanXMinutes", 1, localizeOptions);
1750
+ } else {
1751
+ return locale.formatDistance("xMinutes", 1, localizeOptions);
1752
+ }
1753
+ } else {
1754
+ if (minutes === 0) {
1755
+ return locale.formatDistance("lessThanXMinutes", 1, localizeOptions);
1756
+ } else {
1757
+ return locale.formatDistance("xMinutes", minutes, localizeOptions);
1758
+ }
1775
1759
  }
1776
- if (e.key === "Escape") {
1777
- onCancel();
1760
+ } else if (minutes < 45) {
1761
+ return locale.formatDistance("xMinutes", minutes, localizeOptions);
1762
+ } else if (minutes < 90) {
1763
+ return locale.formatDistance("aboutXHours", 1, localizeOptions);
1764
+ } else if (minutes < minutesInDay) {
1765
+ const hours = Math.round(minutes / 60);
1766
+ return locale.formatDistance("aboutXHours", hours, localizeOptions);
1767
+ } else if (minutes < minutesInAlmostTwoDays) {
1768
+ return locale.formatDistance("xDays", 1, localizeOptions);
1769
+ } else if (minutes < minutesInMonth) {
1770
+ const days = Math.round(minutes / minutesInDay);
1771
+ return locale.formatDistance("xDays", days, localizeOptions);
1772
+ } else if (minutes < minutesInMonth * 2) {
1773
+ months = Math.round(minutes / minutesInMonth);
1774
+ return locale.formatDistance("aboutXMonths", months, localizeOptions);
1775
+ }
1776
+ months = differenceInMonths(earlierDate_, laterDate_);
1777
+ if (months < 12) {
1778
+ const nearestMonth = Math.round(minutes / minutesInMonth);
1779
+ return locale.formatDistance("xMonths", nearestMonth, localizeOptions);
1780
+ } else {
1781
+ const monthsSinceStartOfYear = months % 12;
1782
+ const years = Math.trunc(months / 12);
1783
+ if (monthsSinceStartOfYear < 3) {
1784
+ return locale.formatDistance("aboutXYears", years, localizeOptions);
1785
+ } else if (monthsSinceStartOfYear < 9) {
1786
+ return locale.formatDistance("overXYears", years, localizeOptions);
1787
+ } else {
1788
+ return locale.formatDistance("almostXYears", years + 1, localizeOptions);
1778
1789
  }
1779
- });
1780
- useLayoutEffect(() => {
1781
- setTimeout(() => {
1782
- var _a, _b, _c;
1783
- (_a = ref.current) == null ? void 0 : _a.focus();
1784
- (_b = ref.current) == null ? void 0 : _b.select();
1785
- (_c = ref.current) == null ? void 0 : _c.scroll({ left: 9999 });
1786
- }, 100);
1787
- }, []);
1788
- function handleSubmit() {
1789
- if (value.length >= minLength) {
1790
- onSubmit(value);
1790
+ }
1791
+ }
1792
+ function formatDistanceToNow(date, options) {
1793
+ return formatDistance(date, constructNow(date), options);
1794
+ }
1795
+ function parseISO(argument, options) {
1796
+ const invalidDate = () => constructFrom(options == null ? void 0 : options.in, NaN);
1797
+ const additionalDigits = 2;
1798
+ const dateStrings = splitDateString(argument);
1799
+ let date;
1800
+ if (dateStrings.date) {
1801
+ const parseYearResult = parseYear(dateStrings.date, additionalDigits);
1802
+ date = parseDate(parseYearResult.restDateString, parseYearResult.year);
1803
+ }
1804
+ if (!date || isNaN(+date)) return invalidDate();
1805
+ const timestamp = +date;
1806
+ let time = 0;
1807
+ let offset;
1808
+ if (dateStrings.time) {
1809
+ time = parseTime(dateStrings.time);
1810
+ if (isNaN(time)) return invalidDate();
1811
+ }
1812
+ if (dateStrings.timezone) {
1813
+ offset = parseTimezone(dateStrings.timezone);
1814
+ if (isNaN(offset)) return invalidDate();
1815
+ } else {
1816
+ const tmpDate = new Date(timestamp + time);
1817
+ const result = toDate(0, options == null ? void 0 : options.in);
1818
+ result.setFullYear(
1819
+ tmpDate.getUTCFullYear(),
1820
+ tmpDate.getUTCMonth(),
1821
+ tmpDate.getUTCDate()
1822
+ );
1823
+ result.setHours(
1824
+ tmpDate.getUTCHours(),
1825
+ tmpDate.getUTCMinutes(),
1826
+ tmpDate.getUTCSeconds(),
1827
+ tmpDate.getUTCMilliseconds()
1828
+ );
1829
+ return result;
1830
+ }
1831
+ return toDate(timestamp + time + offset, options == null ? void 0 : options.in);
1832
+ }
1833
+ const patterns = {
1834
+ dateTimeDelimiter: /[T ]/,
1835
+ timeZoneDelimiter: /[Z ]/i,
1836
+ timezone: /([Z+-].*)$/
1837
+ };
1838
+ const dateRegex = /^-?(?:(\d{3})|(\d{2})(?:-?(\d{2}))?|W(\d{2})(?:-?(\d{1}))?|)$/;
1839
+ const timeRegex = /^(\d{2}(?:[.,]\d*)?)(?::?(\d{2}(?:[.,]\d*)?))?(?::?(\d{2}(?:[.,]\d*)?))?$/;
1840
+ const timezoneRegex = /^([+-])(\d{2})(?::?(\d{2}))?$/;
1841
+ function splitDateString(dateString) {
1842
+ const dateStrings = {};
1843
+ const array = dateString.split(patterns.dateTimeDelimiter);
1844
+ let timeString;
1845
+ if (array.length > 2) {
1846
+ return dateStrings;
1847
+ }
1848
+ if (/:/.test(array[0])) {
1849
+ timeString = array[0];
1850
+ } else {
1851
+ dateStrings.date = array[0];
1852
+ timeString = array[1];
1853
+ if (patterns.timeZoneDelimiter.test(dateStrings.date)) {
1854
+ dateStrings.date = dateString.split(patterns.timeZoneDelimiter)[0];
1855
+ timeString = dateString.substr(
1856
+ dateStrings.date.length,
1857
+ dateString.length
1858
+ );
1791
1859
  }
1792
1860
  }
1793
- return jsx("input", { ...inputProps, autoFocus: true, className: twMerge("bg-inherit text-inherit outline-none", className), minLength, onChange: (e) => setValue(e.target.value), ref, required: true, type: "text", value });
1861
+ if (timeString) {
1862
+ const token = patterns.timezone.exec(timeString);
1863
+ if (token) {
1864
+ dateStrings.time = timeString.replace(token[1], "");
1865
+ dateStrings.timezone = token[1];
1866
+ } else {
1867
+ dateStrings.time = timeString;
1868
+ }
1869
+ }
1870
+ return dateStrings;
1794
1871
  }
1795
- function Branch(props) {
1796
- const { branch = "main" } = props;
1797
- return jsxs("button", { className: "flex h-8 items-center gap-1 rounded-lg bg-slate-50 pl-1 pr-2 text-xs text-slate-100", children: [jsx(Icon, { name: "Branch" }), jsx("span", { children: "BRANCH" }), jsx("span", { className: "text-gray-900", children: branch })] });
1872
+ function parseYear(dateString, additionalDigits) {
1873
+ const regex = new RegExp(
1874
+ "^(?:(\\d{4}|[+-]\\d{" + (4 + additionalDigits) + "})|(\\d{2}|[+-]\\d{" + (2 + additionalDigits) + "})$)"
1875
+ );
1876
+ const captures = dateString.match(regex);
1877
+ if (!captures) return { year: NaN, restDateString: "" };
1878
+ const year = captures[1] ? parseInt(captures[1]) : null;
1879
+ const century = captures[2] ? parseInt(captures[2]) : null;
1880
+ return {
1881
+ year: century === null ? year : century * 100,
1882
+ restDateString: dateString.slice((captures[1] || captures[2]).length)
1883
+ };
1798
1884
  }
1799
- function DocId(props) {
1800
- const { docId } = props;
1801
- const [, copy] = useCopyToClipboard();
1802
- function handleCopy(text) {
1803
- return () => {
1804
- copy(text).catch((error) => {
1805
- console.error("Failed to copy!", error);
1806
- });
1807
- };
1885
+ function parseDate(dateString, year) {
1886
+ if (year === null) return /* @__PURE__ */ new Date(NaN);
1887
+ const captures = dateString.match(dateRegex);
1888
+ if (!captures) return /* @__PURE__ */ new Date(NaN);
1889
+ const isWeekDate = !!captures[4];
1890
+ const dayOfYear = parseDateUnit(captures[1]);
1891
+ const month = parseDateUnit(captures[2]) - 1;
1892
+ const day = parseDateUnit(captures[3]);
1893
+ const week = parseDateUnit(captures[4]);
1894
+ const dayOfWeek = parseDateUnit(captures[5]) - 1;
1895
+ if (isWeekDate) {
1896
+ if (!validateWeekDate(year, week, dayOfWeek)) {
1897
+ return /* @__PURE__ */ new Date(NaN);
1898
+ }
1899
+ return dayOfISOWeekYear(year, week, dayOfWeek);
1900
+ } else {
1901
+ const date = /* @__PURE__ */ new Date(0);
1902
+ if (!validateDate(year, month, day) || !validateDayOfYearDate(year, dayOfYear)) {
1903
+ return /* @__PURE__ */ new Date(NaN);
1904
+ }
1905
+ date.setUTCFullYear(year, month, Math.max(dayOfYear, day));
1906
+ return date;
1808
1907
  }
1809
- return jsxs("button", { className: "flex h-8 items-center gap-1 rounded-lg bg-slate-50 pl-1 pr-2 text-xs text-slate-100", onClick: handleCopy(docId), children: [jsx(Icon, { name: "Link" }), "DOC ID", jsx("span", { className: "text-gray-900", children: docId })] });
1810
1908
  }
1811
- function Scope(props) {
1812
- const { value, onChange } = props;
1813
- const items = [
1814
- { displayValue: "Global scope", value: "global" },
1815
- { displayValue: "Local scope", value: "local" }
1816
- ];
1817
- return jsx(Select, { absolutePositionMenu: true, containerClassName: "bg-slate-50 text-gray-500 rounded-lg w-fit text-xs z-10", id: "scope select", itemClassName: "py-2 text-gray-500 grid grid-cols-[auto,auto] gap-1", items, menuClassName: "min-w-0 text-gray-500", onChange, value });
1909
+ function parseDateUnit(value) {
1910
+ return value ? parseInt(value) : 1;
1818
1911
  }
1819
- function Header(props) {
1820
- const { title, docId, scope, onChangeScope, onClose, className, ...divProps } = props;
1821
- return jsxs("header", { className: twMerge("flex items-center justify-between bg-transparent", className), ...divProps, children: [jsxs("div", { className: "flex items-center gap-3", children: [jsx("button", { className: "shadow-button rounded-lg bg-gray-50 p-1 text-slate-100", onClick: onClose, children: jsx(Icon, { name: "VariantArrowLeft" }) }), jsx("h1", { className: "text-xs", children: title })] }), jsxs("div", { className: "flex items-center gap-2", children: [jsx(DocId, { docId }), jsx(Branch, {}), jsx(Scope, { onChange: onChangeScope, value: scope })] })] });
1912
+ function parseTime(timeString) {
1913
+ const captures = timeString.match(timeRegex);
1914
+ if (!captures) return NaN;
1915
+ const hours = parseTimeUnit(captures[1]);
1916
+ const minutes = parseTimeUnit(captures[2]);
1917
+ const seconds = parseTimeUnit(captures[3]);
1918
+ if (!validateTime(hours, minutes, seconds)) {
1919
+ return NaN;
1920
+ }
1921
+ return hours * millisecondsInHour + minutes * millisecondsInMinute + seconds * 1e3;
1822
1922
  }
1823
- function memo(getDeps, fn, opts) {
1824
- let deps = opts.initialDeps ?? [];
1825
- let result;
1826
- function memoizedFunction() {
1827
- var _a, _b, _c, _d;
1828
- let depTime;
1829
- if (opts.key && ((_a = opts.debug) == null ? void 0 : _a.call(opts))) depTime = Date.now();
1830
- const newDeps = getDeps();
1831
- const depsChanged = newDeps.length !== deps.length || newDeps.some((dep, index) => deps[index] !== dep);
1832
- if (!depsChanged) {
1833
- return result;
1923
+ function parseTimeUnit(value) {
1924
+ return value && parseFloat(value.replace(",", ".")) || 0;
1925
+ }
1926
+ function parseTimezone(timezoneString) {
1927
+ if (timezoneString === "Z") return 0;
1928
+ const captures = timezoneString.match(timezoneRegex);
1929
+ if (!captures) return 0;
1930
+ const sign = captures[1] === "+" ? -1 : 1;
1931
+ const hours = parseInt(captures[2]);
1932
+ const minutes = captures[3] && parseInt(captures[3]) || 0;
1933
+ if (!validateTimezone(hours, minutes)) {
1934
+ return NaN;
1935
+ }
1936
+ return sign * (hours * millisecondsInHour + minutes * millisecondsInMinute);
1937
+ }
1938
+ function dayOfISOWeekYear(isoWeekYear, week, day) {
1939
+ const date = /* @__PURE__ */ new Date(0);
1940
+ date.setUTCFullYear(isoWeekYear, 0, 4);
1941
+ const fourthOfJanuaryDay = date.getUTCDay() || 7;
1942
+ const diff = (week - 1) * 7 + day + 1 - fourthOfJanuaryDay;
1943
+ date.setUTCDate(date.getUTCDate() + diff);
1944
+ return date;
1945
+ }
1946
+ const daysInMonths = [31, null, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
1947
+ function isLeapYearIndex(year) {
1948
+ return year % 400 === 0 || year % 4 === 0 && year % 100 !== 0;
1949
+ }
1950
+ function validateDate(year, month, date) {
1951
+ return month >= 0 && month <= 11 && date >= 1 && date <= (daysInMonths[month] || (isLeapYearIndex(year) ? 29 : 28));
1952
+ }
1953
+ function validateDayOfYearDate(year, dayOfYear) {
1954
+ return dayOfYear >= 1 && dayOfYear <= (isLeapYearIndex(year) ? 366 : 365);
1955
+ }
1956
+ function validateWeekDate(_year, week, day) {
1957
+ return week >= 1 && week <= 53 && day >= 0 && day <= 6;
1958
+ }
1959
+ function validateTime(hours, minutes, seconds) {
1960
+ if (hours === 24) {
1961
+ return minutes === 0 && seconds === 0;
1962
+ }
1963
+ return seconds >= 0 && seconds < 60 && minutes >= 0 && minutes < 60 && hours >= 0 && hours < 25;
1964
+ }
1965
+ function validateTimezone(_hours, minutes) {
1966
+ return minutes >= 0 && minutes <= 59;
1967
+ }
1968
+ const HDivider = (props) => {
1969
+ const { className, timestamp, title, subtitle, onClick, isSelected = false } = props;
1970
+ const [open, setOpen] = useState(false);
1971
+ const hasContent = !!title || !!subtitle || !!timestamp;
1972
+ useEffect(() => {
1973
+ if (open) {
1974
+ setOpen(false);
1975
+ setTimeout(() => hasContent && setOpen(true), 50);
1834
1976
  }
1835
- deps = newDeps;
1836
- let resultTime;
1837
- if (opts.key && ((_b = opts.debug) == null ? void 0 : _b.call(opts))) resultTime = Date.now();
1838
- result = fn(...newDeps);
1839
- if (opts.key && ((_c = opts.debug) == null ? void 0 : _c.call(opts))) {
1840
- const depEndTime = Math.round((Date.now() - depTime) * 100) / 100;
1841
- const resultEndTime = Math.round((Date.now() - resultTime) * 100) / 100;
1842
- const resultFpsPercentage = resultEndTime / 16;
1843
- const pad = (str, num) => {
1844
- str = String(str);
1845
- while (str.length < num) {
1846
- str = " " + str;
1847
- }
1848
- return str;
1849
- };
1850
- console.info(
1851
- `%c⏱ ${pad(resultEndTime, 5)} /${pad(depEndTime, 5)} ms`,
1852
- `
1853
- font-size: .6rem;
1854
- font-weight: bold;
1855
- color: hsl(${Math.max(
1856
- 0,
1857
- Math.min(120 - 120 * resultFpsPercentage, 120)
1858
- )}deg 100% 31%);`,
1859
- opts == null ? void 0 : opts.key
1860
- );
1977
+ }, [title, subtitle, timestamp, hasContent]);
1978
+ const formatTimestamp2 = (isoString) => {
1979
+ if (!isoString)
1980
+ return "";
1981
+ try {
1982
+ return formatDistanceToNow(new Date(isoString), { addSuffix: true });
1983
+ } catch {
1984
+ return isoString;
1861
1985
  }
1862
- (_d = opts == null ? void 0 : opts.onChange) == null ? void 0 : _d.call(opts, result);
1863
- return result;
1864
- }
1865
- memoizedFunction.updateDeps = (newDeps) => {
1866
- deps = newDeps;
1867
1986
  };
1868
- return memoizedFunction;
1869
- }
1870
- function notUndefined(value, msg) {
1871
- if (value === void 0) {
1872
- throw new Error(`Unexpected undefined${""}`);
1873
- } else {
1874
- return value;
1875
- }
1876
- }
1877
- const approxEqual = (a, b) => Math.abs(a - b) <= 1;
1878
- const debounce = (targetWindow, fn, ms) => {
1879
- let timeoutId;
1880
- return function(...args) {
1881
- targetWindow.clearTimeout(timeoutId);
1882
- timeoutId = targetWindow.setTimeout(() => fn.apply(this, args), ms);
1987
+ const tooltipContent = jsxs("div", { className: "flex flex-col text-xs", children: [!!title && jsx("div", { children: title }), !!subtitle && jsx("div", { className: "text-gray-300", children: subtitle }), !!timestamp && jsx("div", { children: formatTimestamp2(timestamp) })] });
1988
+ const handleMouseEnter = () => {
1989
+ if (hasContent) {
1990
+ setOpen(true);
1991
+ }
1883
1992
  };
1993
+ const handleMouseLeave = () => {
1994
+ setOpen(false);
1995
+ };
1996
+ return jsxs("div", { className: "relative", onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, children: [isSelected && jsx(Icon, { name: "TimelineCaret", color: "#4EA9FF", size: 10, className: "absolute top-[-11px] z-40" }), jsx(Tooltip, { className: "rounded-md bg-gray-900 text-white", content: tooltipContent, open: open && hasContent, onOpenChange: setOpen, delayDuration: 0, side: "bottom", sideOffset: 5, children: jsx("div", { className: twMerge("mx-0.5 flex h-[25px] w-1.5 cursor-pointer flex-col items-center justify-center rounded-[2px] hover:bg-blue-300", isSelected && "bg-blue-300", className), onClick, "data-title": title, "data-subtitle": subtitle, "data-timestamp": timestamp, children: jsx("div", { className: "h-0.5 w-1 rounded-full bg-gray-500" }) }) })] });
1884
1997
  };
1885
- const getRect = (element) => {
1886
- const { offsetWidth, offsetHeight } = element;
1887
- return { width: offsetWidth, height: offsetHeight };
1888
- };
1889
- const defaultKeyExtractor = (index) => index;
1890
- const defaultRangeExtractor = (range) => {
1891
- const start = Math.max(range.startIndex - range.overscan, 0);
1892
- const end = Math.min(range.endIndex + range.overscan, range.count - 1);
1893
- const arr = [];
1894
- for (let i = start; i <= end; i++) {
1895
- arr.push(i);
1998
+ const getBarHeight = (size = 0) => {
1999
+ switch (true) {
2000
+ case size <= 0:
2001
+ return "h-[1px]";
2002
+ case size === 1:
2003
+ return "h-[3px]";
2004
+ case size === 2:
2005
+ return "h-[6px]";
2006
+ case size === 3:
2007
+ return "h-[9px]";
2008
+ case size >= 4:
2009
+ return "h-[12px]";
2010
+ default:
2011
+ return "h-[1px]";
1896
2012
  }
1897
- return arr;
1898
2013
  };
1899
- const observeElementRect = (instance, cb) => {
1900
- const element = instance.scrollElement;
1901
- if (!element) {
1902
- return;
2014
+ const formatTimestamp = (isoString) => {
2015
+ if (!isoString)
2016
+ return "";
2017
+ try {
2018
+ const date = parseISO(isoString);
2019
+ return format(date, "HH:mm, dd, MMMM");
2020
+ } catch {
2021
+ return isoString;
1903
2022
  }
1904
- const targetWindow = instance.targetWindow;
1905
- if (!targetWindow) {
1906
- return;
1907
- }
1908
- const handler = (rect) => {
1909
- const { width, height } = rect;
1910
- cb({ width: Math.round(width), height: Math.round(height) });
2023
+ };
2024
+ const TimelineBar = ({ onClick, className, timestamp, additions, deletions, addSize = 0, delSize = 0, isSelected = false }) => {
2025
+ const [open, setOpen] = useState(false);
2026
+ const noChanges = addSize === 0 && delSize === 0;
2027
+ const addBarHeight = getBarHeight(addSize);
2028
+ const delBarHeight = getBarHeight(delSize);
2029
+ const tooltipContent = jsxs("div", { className: "flex flex-col text-xs", children: [jsx("div", { children: formatTimestamp(timestamp) }), jsx("div", { className: "text-green-900", children: `${additions} additions +` }), jsx("div", { className: "text-red-700", children: `${deletions} deletions -` })] });
2030
+ const handleMouseEnter = () => {
2031
+ if (!noChanges) {
2032
+ setOpen(true);
2033
+ }
1911
2034
  };
1912
- handler(getRect(element));
1913
- if (!targetWindow.ResizeObserver) {
1914
- return () => {
1915
- };
1916
- }
1917
- const observer = new targetWindow.ResizeObserver((entries) => {
1918
- const run = () => {
1919
- const entry = entries[0];
1920
- if (entry == null ? void 0 : entry.borderBoxSize) {
1921
- const box = entry.borderBoxSize[0];
1922
- if (box) {
1923
- handler({ width: box.inlineSize, height: box.blockSize });
1924
- return;
1925
- }
1926
- }
1927
- handler(getRect(element));
1928
- };
1929
- instance.options.useAnimationFrameWithResizeObserver ? requestAnimationFrame(run) : run();
1930
- });
1931
- observer.observe(element, { box: "border-box" });
1932
- return () => {
1933
- observer.unobserve(element);
2035
+ const handleMouseLeave = () => {
2036
+ setOpen(false);
1934
2037
  };
2038
+ return jsxs("div", { className: "relative", onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, children: [isSelected && jsx(Icon, { name: "TimelineCaret", color: "#4EA9FF", size: 10, className: "absolute left-[-2px] top-[-11px] z-40" }), noChanges ? jsx("div", { className: twMerge("flex h-[25px] w-1.5 cursor-pointer flex-col items-center justify-center rounded-[2px] hover:bg-blue-300", className), "data-timestamp": timestamp, onClick, children: jsx("div", { className: "size-[3px] rounded-full bg-gray-500" }) }) : jsx(Tooltip, { className: "rounded-md bg-gray-900 text-white", content: tooltipContent, open, onOpenChange: setOpen, delayDuration: 0, side: "bottom", sideOffset: 5, children: jsxs("div", { className: twMerge("flex h-[25px] w-1.5 cursor-pointer flex-col items-center justify-center rounded-[2px] hover:bg-blue-300", className, isSelected && "bg-blue-300"), "data-timestamp": timestamp, onClick, children: [jsx("div", { className: "flex h-3 w-0.5 items-end", children: jsx("div", { className: twMerge("h-3 w-0.5 rounded-t-full bg-green-600", addBarHeight) }) }), jsx("div", { className: "flex h-3 w-0.5 items-start", children: jsx("div", { className: twMerge("h-3 w-0.5 rounded-b-full bg-red-600", delBarHeight) }) })] }) })] });
1935
2039
  };
1936
- const addEventListenerOptions = {
1937
- passive: true
2040
+ const defaultTimeLineItem = {
2041
+ id: "default",
2042
+ type: "bar",
2043
+ addSize: 0,
2044
+ delSize: 0
1938
2045
  };
1939
- const supportsScrollend = typeof window == "undefined" ? true : "onscrollend" in window;
1940
- const observeElementOffset = (instance, cb) => {
1941
- const element = instance.scrollElement;
1942
- if (!element) {
1943
- return;
1944
- }
1945
- const targetWindow = instance.targetWindow;
1946
- if (!targetWindow) {
1947
- return;
1948
- }
1949
- let offset = 0;
1950
- const fallback = instance.options.useScrollendEvent && supportsScrollend ? () => void 0 : debounce(
1951
- targetWindow,
1952
- () => {
1953
- cb(offset, false);
1954
- },
1955
- instance.options.isScrollingResetDelay
1956
- );
1957
- const createHandler = (isScrolling) => () => {
1958
- const { horizontal, isRtl } = instance.options;
1959
- offset = horizontal ? element["scrollLeft"] * (isRtl && -1 || 1) : element["scrollTop"];
1960
- fallback();
1961
- cb(offset, isScrolling);
2046
+ const DocumentTimeline = (props) => {
2047
+ const { timeline = [], onItemClick } = props;
2048
+ const [selectedItem, setSelectedItem] = useState(null);
2049
+ const scrollContainerRef = useRef(null);
2050
+ const handleClick = (item) => {
2051
+ if (item.id === selectedItem || item.id === defaultTimeLineItem.id) {
2052
+ onItemClick == null ? void 0 : onItemClick(null);
2053
+ setSelectedItem(null);
2054
+ } else {
2055
+ onItemClick == null ? void 0 : onItemClick(item);
2056
+ setSelectedItem(item.id);
2057
+ }
1962
2058
  };
1963
- const handler = createHandler(true);
1964
- const endHandler = createHandler(false);
1965
- endHandler();
1966
- element.addEventListener("scroll", handler, addEventListenerOptions);
1967
- const registerScrollendEvent = instance.options.useScrollendEvent && supportsScrollend;
1968
- if (registerScrollendEvent) {
1969
- element.addEventListener("scrollend", endHandler, addEventListenerOptions);
1970
- }
1971
- return () => {
1972
- element.removeEventListener("scroll", handler);
1973
- if (registerScrollendEvent) {
1974
- element.removeEventListener("scrollend", endHandler);
2059
+ const mergedTimelineItems = [...timeline, defaultTimeLineItem];
2060
+ const [unselectedItems, selectedItems] = useMemo(() => {
2061
+ const indexSelected = mergedTimelineItems.findIndex((item) => item.id === selectedItem);
2062
+ return indexSelected === -1 ? [mergedTimelineItems, []] : [
2063
+ mergedTimelineItems.slice(0, indexSelected),
2064
+ mergedTimelineItems.slice(indexSelected)
2065
+ ];
2066
+ }, [mergedTimelineItems, selectedItem]);
2067
+ const renderTimelineItems = useCallback((items) => {
2068
+ return items.map((item) => {
2069
+ if (item.type === "divider") {
2070
+ const { timestamp, title, subtitle } = item;
2071
+ return jsx(HDivider, { timestamp, title, subtitle, onClick: () => handleClick(item), isSelected: item.id === selectedItem }, item.id);
2072
+ }
2073
+ return jsx(TimelineBar, { timestamp: item.timestamp, addSize: item.addSize, delSize: item.delSize, additions: item.additions, deletions: item.deletions, isSelected: item.id === selectedItem, onClick: () => handleClick(item) }, item.id);
2074
+ });
2075
+ }, [handleClick, selectedItem]);
2076
+ const unselectedContent = useMemo(() => renderTimelineItems(unselectedItems), [unselectedItems, renderTimelineItems]);
2077
+ const selectedContent = useMemo(() => renderTimelineItems(selectedItems), [selectedItems, renderTimelineItems]);
2078
+ useEffect(() => {
2079
+ if (scrollContainerRef.current) {
2080
+ scrollContainerRef.current.scrollLeft = scrollContainerRef.current.scrollWidth;
2081
+ }
2082
+ }, []);
2083
+ return jsx(TooltipProvider, { delayDuration: 0, skipDelayDuration: 0, children: jsxs("div", { className: "relative h-[36px] w-full", children: [jsx("div", { className: "absolute left-[0px] z-[20] h-[17px] w-[6px] bg-white", children: jsx("div", { className: "mt-[11px] h-[6px] w-[6px] rounded-tl-md bg-slate-50" }) }), jsx("div", { className: "absolute right-[0px] top-[11px] z-[20] h-[6px] w-[6px] bg-white", children: jsx("div", { className: "h-[6px] w-[6px] rounded-tr-md bg-slate-50" }) }), jsx("div", { className: "absolute inset-x-0 bottom-0 h-[25px] rounded-md bg-slate-50" }), jsx("div", { className: "absolute inset-x-0 bottom-0 h-[36px]", children: jsx("div", { ref: scrollContainerRef, className: "h-full overflow-x-auto rounded-md", children: jsxs("div", { className: "ml-auto flex h-[36px] w-max items-end px-2 pb-0", children: [jsx("div", { className: "flex", children: unselectedContent }), jsx("div", { className: "flex rounded-sm bg-blue-200", children: selectedContent })] }) }) }), jsx("div", { className: "pointer-events-none absolute bottom-0 left-0 z-10 h-[25px] w-2 rounded-l-md bg-slate-50" }), jsx("div", { className: "pointer-events-none absolute bottom-0 right-0 z-10 h-[25px] w-2 rounded-r-md bg-slate-50" })] }) });
2084
+ };
2085
+ const DocumentToolbar = (props) => {
2086
+ const { undo: undo2, canUndo, redo: redo2, canRedo, title, onClose, onExport, className, onShowRevisionHistory, onSwitchboardLinkClick, timelineItems = [], onTimelineItemClick, initialTimelineVisible = false, timelineButtonVisible = false } = props;
2087
+ const [showTimeline, setShowTimeline] = useState(initialTimelineVisible);
2088
+ const isUndoDisabled = !canUndo || !undo2;
2089
+ const isRedoDisabled = !canRedo || !redo2;
2090
+ const isExportDisabled = !onExport;
2091
+ const isSwitchboardLinkDisabled = !onSwitchboardLinkClick;
2092
+ const isRevisionHistoryDisabled = !onShowRevisionHistory;
2093
+ const isTimelineDisabled = timelineItems.length === 0;
2094
+ useEffect(() => {
2095
+ if (initialTimelineVisible) {
2096
+ setShowTimeline(true);
1975
2097
  }
2098
+ }, [initialTimelineVisible]);
2099
+ const handleTimelineToggle = () => {
2100
+ if (isTimelineDisabled)
2101
+ return;
2102
+ setShowTimeline(!showTimeline);
1976
2103
  };
2104
+ return jsxs("div", { className: "flex w-full flex-col", children: [jsxs("div", { className: twMerge("flex h-12 w-full items-center justify-between rounded-xl border border-gray-200 bg-slate-50 px-4", className), children: [jsxs("div", { className: "flex items-center gap-x-2", children: [jsx("button", { className: twMerge("grid size-8 place-items-center rounded-lg border border-gray-200 bg-white", isUndoDisabled ? "cursor-not-allowed" : "cursor-pointer active:opacity-70"), onClick: undo2, disabled: isUndoDisabled, children: jsx(Icon, { name: "ArrowCouterclockwise", size: 16, className: isUndoDisabled ? "text-gray-500" : "text-gray-900" }) }), jsx("button", { className: twMerge("grid size-8 place-items-center rounded-lg border border-gray-200 bg-white", isRedoDisabled ? "cursor-not-allowed" : "cursor-pointer active:opacity-70"), onClick: redo2, disabled: isRedoDisabled, children: jsx("div", { className: "-scale-x-100", children: jsx(Icon, { name: "ArrowCouterclockwise", size: 16, className: isRedoDisabled ? "text-gray-500" : "text-gray-900" }) }) }), jsx("button", { className: twMerge("flex h-8 items-center rounded-lg border border-gray-200 bg-white px-3 text-sm", isExportDisabled ? "cursor-not-allowed" : "cursor-pointer active:opacity-70"), onClick: onExport, disabled: isExportDisabled, children: jsx("span", { className: isExportDisabled ? "text-gray-500" : "text-gray-900", children: "Export" }) })] }), jsx("div", { className: "flex items-center", children: jsx("h1", { className: "text-sm font-medium text-gray-500", children: title }) }), jsxs("div", { className: "flex items-center gap-x-2", children: [jsx("button", { className: twMerge("grid size-8 place-items-center rounded-lg border border-gray-200 bg-white", isSwitchboardLinkDisabled ? "cursor-not-allowed" : "cursor-pointer active:opacity-70"), onClick: onSwitchboardLinkClick, disabled: isSwitchboardLinkDisabled, children: jsx(Icon, { name: "Drive", size: 16, className: isSwitchboardLinkDisabled ? "text-gray-500" : "text-gray-900" }) }), jsx("button", { className: twMerge("grid size-8 place-items-center rounded-lg border border-gray-200 bg-white", isRevisionHistoryDisabled ? "cursor-not-allowed" : "cursor-pointer active:opacity-70"), onClick: onShowRevisionHistory, disabled: isRevisionHistoryDisabled, children: jsx(Icon, { name: "History", size: 16, className: isRevisionHistoryDisabled ? "text-gray-500" : "text-gray-900" }) }), timelineButtonVisible && jsx("button", { className: twMerge("grid size-8 place-items-center rounded-lg border border-gray-200 bg-white", isTimelineDisabled ? "cursor-not-allowed" : "cursor-pointer active:opacity-70"), onClick: handleTimelineToggle, disabled: isTimelineDisabled, "aria-pressed": showTimeline, children: jsx(Icon, { name: "Timeline", size: 16, className: twMerge("text-gray-900", isTimelineDisabled && "opacity-50", showTimeline && "text-blue-600") }) }), jsx("button", { className: "grid size-8 cursor-pointer place-items-center rounded-lg border border-gray-200 bg-white active:opacity-70", onClick: onClose, children: jsx(Icon, { name: "XmarkLight", size: 16, className: "text-gray-900" }) })] })] }), showTimeline && jsx("div", { className: "mt-2 w-full", children: jsx(DocumentTimeline, { timeline: timelineItems, onItemClick: onTimelineItemClick }) })] });
1977
2105
  };
1978
- const measureElement = (element, entry, instance) => {
1979
- if (entry == null ? void 0 : entry.borderBoxSize) {
1980
- const box = entry.borderBoxSize[0];
1981
- if (box) {
1982
- const size = Math.round(
1983
- box[instance.options.horizontal ? "inlineSize" : "blockSize"]
1984
- );
1985
- return size;
2106
+ function useUserPermissions() {
2107
+ return {
2108
+ isAllowedToCreateDocuments: true,
2109
+ isAllowedToEditDocuments: true
2110
+ };
2111
+ }
2112
+ function useDocument(reactor, documentMeta = {}) {
2113
+ const { documentId, documentType, driveId } = documentMeta;
2114
+ const [document2, setDocument] = useState();
2115
+ const onStrandUpdate = useCallback((cb) => {
2116
+ if (!reactor) {
2117
+ throw new Error("Reactor is not loaded");
1986
2118
  }
2119
+ return reactor.on("strandUpdate", cb);
2120
+ }, [reactor]);
2121
+ useEffect(() => {
2122
+ if (!reactor)
2123
+ return;
2124
+ if (!driveId || !documentId || !documentType)
2125
+ return;
2126
+ reactor.getDocument(driveId, documentId).then(setDocument).catch(console.error);
2127
+ }, [driveId, documentId, documentType, reactor]);
2128
+ useEffect(() => {
2129
+ if (!reactor)
2130
+ return;
2131
+ if (!driveId || !documentId || !documentType)
2132
+ return;
2133
+ const removeListener = onStrandUpdate((strand) => {
2134
+ if (strand.driveId === driveId && strand.documentId === documentId) {
2135
+ reactor.getDocument(driveId, documentId).then(setDocument).catch(console.error);
2136
+ }
2137
+ });
2138
+ return removeListener;
2139
+ }, [onStrandUpdate, driveId, documentId, documentType]);
2140
+ return document2;
2141
+ }
2142
+ async function signOperation(operation, sign, documentId, document2, reducer, user) {
2143
+ if (!user)
2144
+ return operation;
2145
+ if (!operation.context)
2146
+ return operation;
2147
+ if (!operation.context.signer)
2148
+ return operation;
2149
+ if (!reducer) {
2150
+ console.error(`Document model '${document2.documentType}' does not have a reducer`);
2151
+ return operation;
1987
2152
  }
1988
- return element[instance.options.horizontal ? "offsetWidth" : "offsetHeight"];
1989
- };
1990
- const elementScroll = (offset, {
1991
- adjustments = 0,
1992
- behavior
1993
- }, instance) => {
1994
- var _a, _b;
1995
- const toOffset = offset + adjustments;
1996
- (_b = (_a = instance.scrollElement) == null ? void 0 : _a.scrollTo) == null ? void 0 : _b.call(_a, {
1997
- [instance.options.horizontal ? "left" : "top"]: toOffset,
1998
- behavior
1999
- });
2000
- };
2001
- class Virtualizer {
2002
- constructor(opts) {
2003
- this.unsubs = [];
2004
- this.scrollElement = null;
2005
- this.targetWindow = null;
2006
- this.isScrolling = false;
2007
- this.scrollToIndexTimeoutId = null;
2008
- this.measurementsCache = [];
2009
- this.itemSizeCache = /* @__PURE__ */ new Map();
2010
- this.pendingMeasuredCacheIndexes = [];
2011
- this.scrollRect = null;
2012
- this.scrollOffset = null;
2013
- this.scrollDirection = null;
2014
- this.scrollAdjustments = 0;
2015
- this.elementsCache = /* @__PURE__ */ new Map();
2016
- this.observer = /* @__PURE__ */ (() => {
2017
- let _ro = null;
2018
- const get = () => {
2019
- if (_ro) {
2020
- return _ro;
2021
- }
2022
- if (!this.targetWindow || !this.targetWindow.ResizeObserver) {
2023
- return null;
2024
- }
2025
- return _ro = new this.targetWindow.ResizeObserver((entries) => {
2026
- entries.forEach((entry) => {
2027
- const run = () => {
2028
- this._measureElement(entry.target, entry);
2029
- };
2030
- this.options.useAnimationFrameWithResizeObserver ? requestAnimationFrame(run) : run();
2031
- });
2032
- });
2033
- };
2034
- return {
2035
- disconnect: () => {
2036
- var _a;
2037
- (_a = get()) == null ? void 0 : _a.disconnect();
2038
- _ro = null;
2039
- },
2040
- observe: (target) => {
2041
- var _a;
2153
+ const context = {
2154
+ documentId,
2155
+ signer: operation.context.signer
2156
+ };
2157
+ const signedOperation = await buildSignedOperation(operation, reducer, document2, context, sign);
2158
+ return signedOperation;
2159
+ }
2160
+ function addActionContext(action, connectDid, user) {
2161
+ if (!user)
2162
+ return action;
2163
+ const signer = {
2164
+ app: {
2165
+ name: "Connect",
2166
+ key: connectDid || ""
2167
+ },
2168
+ user: {
2169
+ address: user.address,
2170
+ networkId: user.networkId,
2171
+ chainId: user.chainId
2172
+ },
2173
+ signatures: []
2174
+ };
2175
+ return {
2176
+ context: { signer },
2177
+ ...action
2178
+ };
2179
+ }
2180
+ function debounceOperations(callback, timeout = 50) {
2181
+ let timer;
2182
+ const operations = [];
2183
+ return (operation) => {
2184
+ if (timer) {
2185
+ clearTimeout(timer);
2186
+ }
2187
+ const index = operations.findIndex((op) => op.scope === operation.scope && op.index === operation.index);
2188
+ if (index > -1) {
2189
+ const oldOperation = operations[index];
2190
+ if (!(oldOperation.type === operation.type && JSON.stringify(operation.input) === JSON.stringify(oldOperation.input))) {
2191
+ console.warn("Two conflicting operations were dispatched:", oldOperation, operation);
2192
+ }
2193
+ operations[index] = operation;
2194
+ } else {
2195
+ operations.push(operation);
2196
+ }
2197
+ return new Promise((resolve, reject) => {
2198
+ timer = setTimeout(() => {
2199
+ callback(operations).then(resolve).catch(reject);
2200
+ }, timeout);
2201
+ });
2202
+ };
2203
+ }
2204
+ function useAddDebouncedOperations(reactor, props) {
2205
+ const { driveId, documentId } = props;
2206
+ const [documentDrives] = useDocumentDrives(reactor);
2207
+ const documentDrivesRef = useRef(documentDrives);
2208
+ const { isAllowedToEditDocuments } = useUserPermissions() || {
2209
+ isAllowedToEditDocuments: false
2210
+ };
2211
+ useEffect(() => {
2212
+ documentDrivesRef.current = documentDrives;
2213
+ }, [documentDrives]);
2214
+ const addOperations = useCallback(async (driveId2, id, operations) => {
2215
+ if (!isAllowedToEditDocuments) {
2216
+ throw new Error("User is not allowed to edit documents");
2217
+ }
2218
+ if (!reactor) {
2219
+ throw new Error("Reactor is not loaded");
2220
+ }
2221
+ const drive = documentDrivesRef.current.find((drive2) => drive2.id === driveId2);
2222
+ if (!drive) {
2223
+ throw new Error(`Drive with id ${driveId2} not found`);
2224
+ }
2225
+ const newDocument = await reactor.queueOperations(driveId2, id, operations);
2226
+ return newDocument.document;
2227
+ }, [isAllowedToEditDocuments, reactor]);
2228
+ const addDebouncedOperations = useMemo(() => {
2229
+ return debounceOperations((operations) => addOperations(driveId, documentId, operations));
2230
+ }, [addOperations, driveId, documentId]);
2231
+ return addDebouncedOperations;
2232
+ }
2233
+ function useDocumentDispatch(documentReducer, initialState, onError = console.error) {
2234
+ const [state, setState] = useState(initialState);
2235
+ const [error, setError] = useState();
2236
+ const onErrorHandler = (error2) => {
2237
+ setError(error2);
2238
+ onError(error2);
2239
+ };
2240
+ useEffect(() => {
2241
+ setState(initialState);
2242
+ }, [initialState]);
2243
+ const dispatch = (action, callback, onErrorCallback) => {
2244
+ setError(void 0);
2245
+ setState((_state) => {
2246
+ if (!documentReducer || !_state)
2247
+ return _state;
2248
+ try {
2249
+ const newState = documentReducer(_state, action);
2250
+ const scope = action.scope ?? "global";
2251
+ const operations = newState.operations[scope];
2252
+ const operation = operations[operations.length - 1];
2253
+ if (operation.error) {
2254
+ const error2 = new Error(operation.error);
2255
+ onErrorHandler(error2);
2256
+ onErrorCallback == null ? void 0 : onErrorCallback(error2);
2257
+ }
2258
+ callback == null ? void 0 : callback(operation, {
2259
+ prevState: { ..._state },
2260
+ newState: { ...newState }
2261
+ });
2262
+ return newState;
2263
+ } catch (error2) {
2264
+ onErrorHandler(error2);
2265
+ onErrorCallback == null ? void 0 : onErrorCallback(error2);
2266
+ return _state;
2267
+ }
2268
+ });
2269
+ };
2270
+ return [state, dispatch, error];
2271
+ }
2272
+ function useDocumentEditorProps(reactor, props) {
2273
+ const { nodeId, driveId, documentModelModule, document: initialDocument, user, connectDid, sign } = props;
2274
+ const addDebouncedOprations = useAddDebouncedOperations(reactor, {
2275
+ driveId,
2276
+ documentId: nodeId
2277
+ });
2278
+ const [document2, _dispatch, error] = useDocumentDispatch(documentModelModule.reducer, initialDocument);
2279
+ function dispatch(action, onErrorCallback) {
2280
+ const callback = (operation, state) => {
2281
+ const { prevState } = state;
2282
+ signOperation(operation, sign, nodeId, prevState, documentModelModule.reducer, user).then((op) => {
2283
+ return addDebouncedOprations(op);
2284
+ }).catch(console.error);
2285
+ };
2286
+ _dispatch(addActionContext(action, connectDid, user), callback, onErrorCallback);
2287
+ }
2288
+ return {
2289
+ dispatch,
2290
+ document: document2,
2291
+ error
2292
+ };
2293
+ }
2294
+ const generateId = () => generateId$1().toString();
2295
+ function getNode(id, drive) {
2296
+ return drive.state.global.nodes.find((node) => node.id === id);
2297
+ }
2298
+ function createDriveActions(document2, dispatch, context) {
2299
+ const drive = document2;
2300
+ const driveId = drive.id;
2301
+ const { selectedNode } = context;
2302
+ const handleAddFolder = async (name, parentFolder, id = generateId()) => {
2303
+ dispatch(addFolder({
2304
+ id,
2305
+ name,
2306
+ parentFolder: parentFolder ?? null
2307
+ }));
2308
+ };
2309
+ const addDocument = async (name, documentType, document3, parentFolder, id = generateId()) => {
2310
+ const action = generateAddNodeAction(drive.state.global, {
2311
+ id,
2312
+ name,
2313
+ parentFolder: parentFolder ?? null,
2314
+ documentType,
2315
+ document: document3
2316
+ }, ["global"]);
2317
+ dispatch(action);
2318
+ };
2319
+ const addFile = async (file, parentFolder = selectedNode && isFileNode(selectedNode) ? void 0 : selectedNode == null ? void 0 : selectedNode.id, name = file.name.replace(/\.zip$/gim, "")) => {
2320
+ const folder = parentFolder ? getNode(parentFolder, drive) : void 0;
2321
+ if (parentFolder && !folder) {
2322
+ throw new Error(`Parent folder with id "${parentFolder}" not found`);
2323
+ }
2324
+ if (folder && !isFolderNode(folder)) {
2325
+ throw new Error(`Parent folder with id "${parentFolder}" is not a folder`);
2326
+ }
2327
+ await context.addFile(file, driveId, name, parentFolder);
2328
+ };
2329
+ const handleDeleteNode = async (id) => {
2330
+ dispatch(deleteNode({ id }));
2331
+ };
2332
+ const renameNode = async (id, name) => {
2333
+ dispatch(updateNode({ id, name }));
2334
+ };
2335
+ const handleMoveNode = async (sourceId, targetId) => {
2336
+ dispatch(moveNode({
2337
+ srcFolder: sourceId,
2338
+ targetParentFolder: targetId
2339
+ }));
2340
+ };
2341
+ const handleCopyNode = async (sourceId, targetFolderId) => {
2342
+ const target = targetFolderId ? getNode(targetFolderId, drive) : void 0;
2343
+ if (targetFolderId && !target && targetFolderId !== driveId) {
2344
+ throw new Error(`Target node with id "${targetFolderId}" not found`);
2345
+ }
2346
+ if (target && !isFolderNode(target)) {
2347
+ throw new Error(`Target node with id "${targetFolderId}" is not a folder`);
2348
+ }
2349
+ const source = getNode(sourceId, drive);
2350
+ if (!source) {
2351
+ throw new Error(`Source node with id "${sourceId}" not found`);
2352
+ }
2353
+ const copyNodesInput = generateNodesCopy({
2354
+ srcId: sourceId,
2355
+ targetParentFolder: target == null ? void 0 : target.id,
2356
+ targetName: source.name
2357
+ }, generateId, drive.state.global.nodes);
2358
+ const copyActions = copyNodesInput.map((copyNodeInput) => copyNode(copyNodeInput));
2359
+ for (const copyAction of copyActions) {
2360
+ dispatch(copyAction);
2361
+ }
2362
+ };
2363
+ const duplicateNode = async (sourceId) => {
2364
+ const node = getNode(sourceId, drive);
2365
+ if (!node) {
2366
+ throw new Error(`Node with id "${sourceId}" not found`);
2367
+ }
2368
+ await handleCopyNode(node.id, node.parentFolder || void 0);
2369
+ };
2370
+ return {
2371
+ context,
2372
+ selectNode: context.selectNode,
2373
+ addFolder: handleAddFolder,
2374
+ addFile,
2375
+ addDocument,
2376
+ deleteNode: handleDeleteNode,
2377
+ renameNode,
2378
+ moveNode: handleMoveNode,
2379
+ copyNode: handleCopyNode,
2380
+ duplicateNode
2381
+ };
2382
+ }
2383
+ function useDriveActions(document2, dispatch, context) {
2384
+ return useMemo(() => createDriveActions(document2, dispatch, context), [document2, dispatch, context]);
2385
+ }
2386
+ function toNode(uiNode) {
2387
+ if (uiNode.kind === "DRIVE") {
2388
+ throw new Error("Cannot convert drive node to regular node");
2389
+ }
2390
+ const { id, name, parentFolder, kind } = uiNode;
2391
+ if (kind === "FOLDER") {
2392
+ return { id, name, parentFolder, kind: "folder" };
2393
+ } else {
2394
+ const fileNode = uiNode;
2395
+ return {
2396
+ id,
2397
+ name,
2398
+ parentFolder,
2399
+ kind: "file",
2400
+ documentType: fileNode.documentType,
2401
+ synchronizationUnits: fileNode.synchronizationUnits
2402
+ };
2403
+ }
2404
+ }
2405
+ function createUiNodeAdapter(driveActions) {
2406
+ return {
2407
+ ...driveActions,
2408
+ addFile: (file, parentNode) => driveActions.addFile(file, parentNode == null ? void 0 : parentNode.id),
2409
+ addFolder: (name, parentFolder = void 0) => driveActions.addFolder(name, parentFolder),
2410
+ renameNode: (name, node) => {
2411
+ const converted = toNode(node);
2412
+ return driveActions.renameNode(converted.id, name);
2413
+ },
2414
+ deleteNode: (node) => {
2415
+ const converted = toNode(node);
2416
+ return driveActions.deleteNode(converted.id);
2417
+ },
2418
+ moveNode: async (src, target) => {
2419
+ if (target.kind === FILE || src.parentFolder === target.id)
2420
+ return;
2421
+ const srcNode = toNode(src);
2422
+ const targetNode = toNode(target);
2423
+ return driveActions.moveNode(srcNode.id, targetNode.id);
2424
+ },
2425
+ copyNode: (src, target) => {
2426
+ return driveActions.copyNode(src.id, target.id);
2427
+ },
2428
+ duplicateNode: (node) => {
2429
+ const converted = toNode(node);
2430
+ return driveActions.duplicateNode(converted.id);
2431
+ }
2432
+ };
2433
+ }
2434
+ function useDriveActionsWithUiNodes(document2, dispatch) {
2435
+ const { selectedNode, selectedDriveNode, setSelectedNode, getNodeById } = useUiNodesContext();
2436
+ const _driveContext = useDriveContext();
2437
+ const driveContext = useMemo(() => ({
2438
+ ..._driveContext,
2439
+ selectedNode,
2440
+ onSelectNode: (node) => {
2441
+ _driveContext.selectNode(node);
2442
+ setSelectedNode(getNodeById(node.id));
2443
+ }
2444
+ }), [selectedNode, selectedDriveNode == null ? void 0 : selectedDriveNode.driveId, setSelectedNode, getNodeById]);
2445
+ const driveActions = useDriveActions(document2, dispatch, driveContext);
2446
+ const uiNodeActions = useMemo(() => createUiNodeAdapter(driveActions), [driveActions]);
2447
+ return uiNodeActions;
2448
+ }
2449
+ const syncIcons = {
2450
+ SYNCING: "Syncing",
2451
+ SUCCESS: "Synced",
2452
+ CONFLICT: "Error",
2453
+ MISSING: "Circle",
2454
+ ERROR: "Error",
2455
+ INITIAL_SYNC: "Syncing"
2456
+ };
2457
+ function SyncStatusIcon(props) {
2458
+ const { syncStatus, className, overrideSyncIcons = {}, ...iconProps } = props;
2459
+ const icons = { ...syncIcons, ...overrideSyncIcons };
2460
+ const syncStatusIcons = {
2461
+ [INITIAL_SYNC]: jsx(Icon, { size: 16, ...iconProps, className: twMerge("text-blue-900", className), name: icons[INITIAL_SYNC] }),
2462
+ [SYNCING]: jsx(Icon, { size: 16, ...iconProps, className: twMerge("text-blue-900", className), name: icons[SYNCING] }),
2463
+ [SUCCESS]: jsx(Icon, { size: 16, ...iconProps, className: twMerge("text-green-900", className), name: icons[SUCCESS] }),
2464
+ [CONFLICT]: jsx(Icon, { size: 16, ...iconProps, className: twMerge("text-orange-900", className), name: icons[CONFLICT] }),
2465
+ [MISSING]: jsx(Icon, { size: 16, ...iconProps, className: twMerge("text-red-900", className), name: icons[MISSING] }),
2466
+ [ERROR]: jsx(Icon, { size: 16, ...iconProps, className: twMerge("text-red-900", className), name: icons[ERROR] })
2467
+ };
2468
+ return syncStatusIcons[syncStatus];
2469
+ }
2470
+ function FileItem(props) {
2471
+ const { uiNode, className, customDocumentIconSrc, onSelectNode, onRenameNode, onDuplicateNode, onDeleteNode, isAllowedToCreateDocuments } = props;
2472
+ const [mode, setMode] = useState(READ);
2473
+ const [isDropdownMenuOpen, setIsDropdownMenuOpen] = useState(false);
2474
+ const { dragProps } = useDrag({ uiNode });
2475
+ const isReadMode = mode === READ;
2476
+ const dropdownMenuHandlers = {
2477
+ [DUPLICATE]: () => onDuplicateNode(uiNode),
2478
+ [RENAME]: () => setMode(WRITE),
2479
+ [DELETE]: () => onDeleteNode(uiNode)
2480
+ };
2481
+ const dropdownMenuOptions = Object.entries(nodeOptionsMap).map(([id, option]) => ({
2482
+ ...option,
2483
+ id
2484
+ })).filter((option) => defaultFileOptions.includes(option.id));
2485
+ function onSubmit(name) {
2486
+ onRenameNode(name, uiNode);
2487
+ setMode(READ);
2488
+ }
2489
+ function onCancel() {
2490
+ setMode(READ);
2491
+ }
2492
+ function onClick() {
2493
+ onSelectNode(uiNode);
2494
+ }
2495
+ function onDropdownMenuOptionClick(itemId) {
2496
+ const handler = dropdownMenuHandlers[itemId];
2497
+ if (!handler) {
2498
+ console.error(`No handler found for dropdown menu item: ${itemId}`);
2499
+ return;
2500
+ }
2501
+ handler();
2502
+ setIsDropdownMenuOpen(false);
2503
+ }
2504
+ const iconSrc = getDocumentIconSrc(uiNode.documentType, customDocumentIconSrc);
2505
+ const iconNode = jsxs("div", { className: "relative", children: [jsx("img", { alt: "file icon", className: "max-w-none", height: 34, src: iconSrc, width: 32 }), isReadMode && uiNode.syncStatus && jsx("div", { className: "absolute bottom-[-2px] right-0 size-3 rounded-full bg-white", children: jsx("div", { className: "absolute left-[-2px] top-[-2px]", children: jsx(SyncStatusIcon, { overrideSyncIcons: { SUCCESS: "CheckCircleFill" }, syncStatus: uiNode.syncStatus }) }) })] });
2506
+ const containerStyles = twMerge("group flex h-12 cursor-pointer select-none items-center rounded-lg bg-gray-200 px-2 text-gray-600 hover:text-gray-800", className);
2507
+ const content = isReadMode ? jsxs("div", { className: "flex w-52 items-center justify-between", children: [jsxs("div", { className: "mr-2 truncate group-hover:mr-0", children: [jsx("div", { className: "max-h-6 truncate text-sm font-medium group-hover:text-gray-800", children: uiNode.name }), jsx("div", { className: "max-h-6 truncate text-xs font-medium text-gray-600 group-hover:text-gray-800", children: uiNode.documentType })] }), isAllowedToCreateDocuments ? jsx(ConnectDropdownMenu, { items: dropdownMenuOptions, onItemClick: onDropdownMenuOptionClick, onOpenChange: setIsDropdownMenuOpen, open: isDropdownMenuOpen, children: jsx("button", { className: twMerge("hidden group-hover:block", isDropdownMenuOpen && "block"), onClick: (e) => {
2508
+ e.stopPropagation();
2509
+ setIsDropdownMenuOpen(true);
2510
+ }, children: jsx(Icon, { className: "text-gray-600", name: "VerticalDots" }) }) }) : null] }) : jsx(NodeInput, { className: "ml-3 flex-1 font-medium", defaultValue: uiNode.name, onCancel, onSubmit });
2511
+ return jsx("div", { className: "relative w-64", onClick, children: jsx("div", { ...dragProps, className: containerStyles, children: jsxs("div", { className: "flex items-center", children: [jsx("div", { className: "mr-1.5", children: iconNode }), content] }) }) });
2512
+ }
2513
+ function FolderItem(props) {
2514
+ const { uiNode, isAllowedToCreateDocuments, className, onRenameNode, onDuplicateNode, onDeleteNode, onSelectNode, onAddFile, onCopyNode, onMoveNode } = props;
2515
+ const [mode, setMode] = useState(READ);
2516
+ const [isDropdownMenuOpen, setIsDropdownMenuOpen] = useState(false);
2517
+ const { dragProps } = useDrag({ ...props, uiNode });
2518
+ const { isDropTarget, dropProps } = useDrop({
2519
+ uiNode,
2520
+ onAddFile,
2521
+ onCopyNode,
2522
+ onMoveNode
2523
+ });
2524
+ const isReadMode = mode === READ;
2525
+ function onCancel() {
2526
+ setMode(READ);
2527
+ }
2528
+ function onSubmit(name) {
2529
+ onRenameNode(name, uiNode);
2530
+ setMode(READ);
2531
+ }
2532
+ function onClick() {
2533
+ onSelectNode(uiNode);
2534
+ }
2535
+ const dropdownMenuHandlers = {
2536
+ [DUPLICATE]: () => onDuplicateNode(uiNode),
2537
+ [RENAME]: () => setMode(WRITE),
2538
+ [DELETE]: () => onDeleteNode(uiNode)
2539
+ };
2540
+ const dropdownMenuOptions = Object.entries(nodeOptionsMap).map(([id, option]) => ({
2541
+ ...option,
2542
+ id
2543
+ })).filter((option) => defaultFolderOptions.includes(option.id));
2544
+ function onDropdownMenuOptionClick(itemId) {
2545
+ const handler = dropdownMenuHandlers[itemId];
2546
+ if (!handler) {
2547
+ console.error(`No handler found for dropdown menu item: ${itemId}`);
2548
+ return;
2549
+ }
2550
+ handler();
2551
+ setIsDropdownMenuOpen(false);
2552
+ }
2553
+ const content = isReadMode || !isAllowedToCreateDocuments ? jsx("div", { className: "ml-3 max-h-6 truncate font-medium text-gray-600 group-hover:text-gray-800", children: uiNode.name }) : jsx(NodeInput, { className: "ml-3 font-medium", defaultValue: uiNode.name, onCancel, onSubmit });
2554
+ const containerStyles = twMerge("group flex h-12 cursor-pointer select-none items-center rounded-lg bg-gray-200 px-2", className, isDropTarget && "bg-blue-100");
2555
+ return jsx("div", { className: "relative w-64", onClick, children: jsxs("div", { ...dragProps, ...dropProps, className: containerStyles, children: [jsxs("div", { className: "flex items-center overflow-hidden", children: [jsx("div", { className: "p-1", children: jsxs("div", { className: "relative", children: [jsx(Icon, { name: "FolderClose", size: 24 }), isReadMode && uiNode.syncStatus ? jsx("div", { className: "absolute bottom-[-3px] right-[-2px] size-3 rounded-full bg-white", children: jsx("div", { className: "absolute left-[-2px] top-[-2px]", children: jsx(SyncStatusIcon, { overrideSyncIcons: {
2556
+ SUCCESS: "CheckCircleFill"
2557
+ }, syncStatus: uiNode.syncStatus }) }) }) : null] }) }), content] }), isReadMode && isAllowedToCreateDocuments ? jsx(ConnectDropdownMenu, { items: dropdownMenuOptions, onItemClick: onDropdownMenuOptionClick, onOpenChange: setIsDropdownMenuOpen, open: isDropdownMenuOpen, children: jsx("button", { className: twMerge("ml-auto hidden group-hover:block", isDropdownMenuOpen && "block"), onClick: (e) => {
2558
+ e.stopPropagation();
2559
+ setIsDropdownMenuOpen(true);
2560
+ }, children: jsx(Icon, { className: "text-gray-600", name: "VerticalDots" }) }) }) : null] }) });
2561
+ }
2562
+ function NodeInput(props) {
2563
+ const { onSubmit, onCancel, defaultValue, className, minLength = 1, ...inputProps } = props;
2564
+ const [value, setValue] = useState(defaultValue ?? "");
2565
+ const ref = useRef(null);
2566
+ useOnClickOutside(ref, handleSubmit);
2567
+ useEventListener("keyup", (e) => {
2568
+ if (e.key === "Enter") {
2569
+ handleSubmit();
2570
+ }
2571
+ if (e.key === "Escape") {
2572
+ onCancel();
2573
+ }
2574
+ });
2575
+ useLayoutEffect(() => {
2576
+ setTimeout(() => {
2577
+ var _a, _b, _c;
2578
+ (_a = ref.current) == null ? void 0 : _a.focus();
2579
+ (_b = ref.current) == null ? void 0 : _b.select();
2580
+ (_c = ref.current) == null ? void 0 : _c.scroll({ left: 9999 });
2581
+ }, 100);
2582
+ }, []);
2583
+ function handleSubmit() {
2584
+ if (value.length >= minLength) {
2585
+ onSubmit(value);
2586
+ }
2587
+ }
2588
+ return jsx("input", { ...inputProps, autoFocus: true, className: twMerge("bg-inherit text-inherit outline-none", className), minLength, onChange: (e) => setValue(e.target.value), ref, required: true, type: "text", value });
2589
+ }
2590
+ function Branch(props) {
2591
+ const { branch = "main" } = props;
2592
+ return jsxs("button", { className: "flex h-8 items-center gap-1 rounded-lg bg-slate-50 pl-1 pr-2 text-xs text-slate-100", children: [jsx(Icon, { name: "Branch" }), jsx("span", { children: "BRANCH" }), jsx("span", { className: "text-gray-900", children: branch })] });
2593
+ }
2594
+ function DocId(props) {
2595
+ const { docId } = props;
2596
+ const [, copy] = useCopyToClipboard();
2597
+ function handleCopy(text) {
2598
+ return () => {
2599
+ copy(text).catch((error) => {
2600
+ console.error("Failed to copy!", error);
2601
+ });
2602
+ };
2603
+ }
2604
+ return jsxs("button", { className: "flex h-8 items-center gap-1 rounded-lg bg-slate-50 pl-1 pr-2 text-xs text-slate-100", onClick: handleCopy(docId), children: [jsx(Icon, { name: "Link" }), "DOC ID", jsx("span", { className: "text-gray-900", children: docId })] });
2605
+ }
2606
+ function Scope(props) {
2607
+ const { value, onChange } = props;
2608
+ const items = [
2609
+ { displayValue: "Global scope", value: "global" },
2610
+ { displayValue: "Local scope", value: "local" }
2611
+ ];
2612
+ return jsx(Select, { absolutePositionMenu: true, containerClassName: "bg-slate-50 text-gray-500 rounded-lg w-fit text-xs z-10", id: "scope select", itemClassName: "py-2 text-gray-500 grid grid-cols-[auto,auto] gap-1", items, menuClassName: "min-w-0 text-gray-500", onChange, value });
2613
+ }
2614
+ function Header(props) {
2615
+ const { title, docId, scope, onChangeScope, onClose, className, ...divProps } = props;
2616
+ return jsxs("header", { className: twMerge("flex items-center justify-between bg-transparent", className), ...divProps, children: [jsxs("div", { className: "flex items-center gap-3", children: [jsx("button", { className: "shadow-button rounded-lg bg-gray-50 p-1 text-slate-100", onClick: onClose, children: jsx(Icon, { name: "VariantArrowLeft" }) }), jsx("h1", { className: "text-xs", children: title })] }), jsxs("div", { className: "flex items-center gap-2", children: [jsx(DocId, { docId }), jsx(Branch, {}), jsx(Scope, { onChange: onChangeScope, value: scope })] })] });
2617
+ }
2618
+ function memo(getDeps, fn, opts) {
2619
+ let deps = opts.initialDeps ?? [];
2620
+ let result;
2621
+ function memoizedFunction() {
2622
+ var _a, _b, _c, _d;
2623
+ let depTime;
2624
+ if (opts.key && ((_a = opts.debug) == null ? void 0 : _a.call(opts))) depTime = Date.now();
2625
+ const newDeps = getDeps();
2626
+ const depsChanged = newDeps.length !== deps.length || newDeps.some((dep, index) => deps[index] !== dep);
2627
+ if (!depsChanged) {
2628
+ return result;
2629
+ }
2630
+ deps = newDeps;
2631
+ let resultTime;
2632
+ if (opts.key && ((_b = opts.debug) == null ? void 0 : _b.call(opts))) resultTime = Date.now();
2633
+ result = fn(...newDeps);
2634
+ if (opts.key && ((_c = opts.debug) == null ? void 0 : _c.call(opts))) {
2635
+ const depEndTime = Math.round((Date.now() - depTime) * 100) / 100;
2636
+ const resultEndTime = Math.round((Date.now() - resultTime) * 100) / 100;
2637
+ const resultFpsPercentage = resultEndTime / 16;
2638
+ const pad = (str, num) => {
2639
+ str = String(str);
2640
+ while (str.length < num) {
2641
+ str = " " + str;
2642
+ }
2643
+ return str;
2644
+ };
2645
+ console.info(
2646
+ `%c⏱ ${pad(resultEndTime, 5)} /${pad(depEndTime, 5)} ms`,
2647
+ `
2648
+ font-size: .6rem;
2649
+ font-weight: bold;
2650
+ color: hsl(${Math.max(
2651
+ 0,
2652
+ Math.min(120 - 120 * resultFpsPercentage, 120)
2653
+ )}deg 100% 31%);`,
2654
+ opts == null ? void 0 : opts.key
2655
+ );
2656
+ }
2657
+ (_d = opts == null ? void 0 : opts.onChange) == null ? void 0 : _d.call(opts, result);
2658
+ return result;
2659
+ }
2660
+ memoizedFunction.updateDeps = (newDeps) => {
2661
+ deps = newDeps;
2662
+ };
2663
+ return memoizedFunction;
2664
+ }
2665
+ function notUndefined(value, msg) {
2666
+ if (value === void 0) {
2667
+ throw new Error(`Unexpected undefined${""}`);
2668
+ } else {
2669
+ return value;
2670
+ }
2671
+ }
2672
+ const approxEqual = (a, b) => Math.abs(a - b) <= 1;
2673
+ const debounce = (targetWindow, fn, ms) => {
2674
+ let timeoutId;
2675
+ return function(...args) {
2676
+ targetWindow.clearTimeout(timeoutId);
2677
+ timeoutId = targetWindow.setTimeout(() => fn.apply(this, args), ms);
2678
+ };
2679
+ };
2680
+ const getRect = (element) => {
2681
+ const { offsetWidth, offsetHeight } = element;
2682
+ return { width: offsetWidth, height: offsetHeight };
2683
+ };
2684
+ const defaultKeyExtractor = (index) => index;
2685
+ const defaultRangeExtractor = (range) => {
2686
+ const start = Math.max(range.startIndex - range.overscan, 0);
2687
+ const end = Math.min(range.endIndex + range.overscan, range.count - 1);
2688
+ const arr = [];
2689
+ for (let i = start; i <= end; i++) {
2690
+ arr.push(i);
2691
+ }
2692
+ return arr;
2693
+ };
2694
+ const observeElementRect = (instance, cb) => {
2695
+ const element = instance.scrollElement;
2696
+ if (!element) {
2697
+ return;
2698
+ }
2699
+ const targetWindow = instance.targetWindow;
2700
+ if (!targetWindow) {
2701
+ return;
2702
+ }
2703
+ const handler = (rect) => {
2704
+ const { width, height } = rect;
2705
+ cb({ width: Math.round(width), height: Math.round(height) });
2706
+ };
2707
+ handler(getRect(element));
2708
+ if (!targetWindow.ResizeObserver) {
2709
+ return () => {
2710
+ };
2711
+ }
2712
+ const observer = new targetWindow.ResizeObserver((entries) => {
2713
+ const run = () => {
2714
+ const entry = entries[0];
2715
+ if (entry == null ? void 0 : entry.borderBoxSize) {
2716
+ const box = entry.borderBoxSize[0];
2717
+ if (box) {
2718
+ handler({ width: box.inlineSize, height: box.blockSize });
2719
+ return;
2720
+ }
2721
+ }
2722
+ handler(getRect(element));
2723
+ };
2724
+ instance.options.useAnimationFrameWithResizeObserver ? requestAnimationFrame(run) : run();
2725
+ });
2726
+ observer.observe(element, { box: "border-box" });
2727
+ return () => {
2728
+ observer.unobserve(element);
2729
+ };
2730
+ };
2731
+ const addEventListenerOptions = {
2732
+ passive: true
2733
+ };
2734
+ const supportsScrollend = typeof window == "undefined" ? true : "onscrollend" in window;
2735
+ const observeElementOffset = (instance, cb) => {
2736
+ const element = instance.scrollElement;
2737
+ if (!element) {
2738
+ return;
2739
+ }
2740
+ const targetWindow = instance.targetWindow;
2741
+ if (!targetWindow) {
2742
+ return;
2743
+ }
2744
+ let offset = 0;
2745
+ const fallback = instance.options.useScrollendEvent && supportsScrollend ? () => void 0 : debounce(
2746
+ targetWindow,
2747
+ () => {
2748
+ cb(offset, false);
2749
+ },
2750
+ instance.options.isScrollingResetDelay
2751
+ );
2752
+ const createHandler = (isScrolling) => () => {
2753
+ const { horizontal, isRtl } = instance.options;
2754
+ offset = horizontal ? element["scrollLeft"] * (isRtl && -1 || 1) : element["scrollTop"];
2755
+ fallback();
2756
+ cb(offset, isScrolling);
2757
+ };
2758
+ const handler = createHandler(true);
2759
+ const endHandler = createHandler(false);
2760
+ endHandler();
2761
+ element.addEventListener("scroll", handler, addEventListenerOptions);
2762
+ const registerScrollendEvent = instance.options.useScrollendEvent && supportsScrollend;
2763
+ if (registerScrollendEvent) {
2764
+ element.addEventListener("scrollend", endHandler, addEventListenerOptions);
2765
+ }
2766
+ return () => {
2767
+ element.removeEventListener("scroll", handler);
2768
+ if (registerScrollendEvent) {
2769
+ element.removeEventListener("scrollend", endHandler);
2770
+ }
2771
+ };
2772
+ };
2773
+ const measureElement = (element, entry, instance) => {
2774
+ if (entry == null ? void 0 : entry.borderBoxSize) {
2775
+ const box = entry.borderBoxSize[0];
2776
+ if (box) {
2777
+ const size = Math.round(
2778
+ box[instance.options.horizontal ? "inlineSize" : "blockSize"]
2779
+ );
2780
+ return size;
2781
+ }
2782
+ }
2783
+ return element[instance.options.horizontal ? "offsetWidth" : "offsetHeight"];
2784
+ };
2785
+ const elementScroll = (offset, {
2786
+ adjustments = 0,
2787
+ behavior
2788
+ }, instance) => {
2789
+ var _a, _b;
2790
+ const toOffset = offset + adjustments;
2791
+ (_b = (_a = instance.scrollElement) == null ? void 0 : _a.scrollTo) == null ? void 0 : _b.call(_a, {
2792
+ [instance.options.horizontal ? "left" : "top"]: toOffset,
2793
+ behavior
2794
+ });
2795
+ };
2796
+ class Virtualizer {
2797
+ constructor(opts) {
2798
+ this.unsubs = [];
2799
+ this.scrollElement = null;
2800
+ this.targetWindow = null;
2801
+ this.isScrolling = false;
2802
+ this.scrollToIndexTimeoutId = null;
2803
+ this.measurementsCache = [];
2804
+ this.itemSizeCache = /* @__PURE__ */ new Map();
2805
+ this.pendingMeasuredCacheIndexes = [];
2806
+ this.scrollRect = null;
2807
+ this.scrollOffset = null;
2808
+ this.scrollDirection = null;
2809
+ this.scrollAdjustments = 0;
2810
+ this.elementsCache = /* @__PURE__ */ new Map();
2811
+ this.observer = /* @__PURE__ */ (() => {
2812
+ let _ro = null;
2813
+ const get = () => {
2814
+ if (_ro) {
2815
+ return _ro;
2816
+ }
2817
+ if (!this.targetWindow || !this.targetWindow.ResizeObserver) {
2818
+ return null;
2819
+ }
2820
+ return _ro = new this.targetWindow.ResizeObserver((entries) => {
2821
+ entries.forEach((entry) => {
2822
+ const run = () => {
2823
+ this._measureElement(entry.target, entry);
2824
+ };
2825
+ this.options.useAnimationFrameWithResizeObserver ? requestAnimationFrame(run) : run();
2826
+ });
2827
+ });
2828
+ };
2829
+ return {
2830
+ disconnect: () => {
2831
+ var _a;
2832
+ (_a = get()) == null ? void 0 : _a.disconnect();
2833
+ _ro = null;
2834
+ },
2835
+ observe: (target) => {
2836
+ var _a;
2042
2837
  return (_a = get()) == null ? void 0 : _a.observe(target, { box: "border-box" });
2043
2838
  },
2044
2839
  unobserve: (target) => {
@@ -2481,9 +3276,9 @@ class Virtualizer {
2481
3276
  this.options.getItemKey(index)
2482
3277
  );
2483
3278
  if (elementInDOM) {
2484
- const [latestOffset] = notUndefined(
2485
- this.getOffsetForIndex(index, align)
2486
- );
3279
+ const result = this.getOffsetForIndex(index, align);
3280
+ if (!result) return;
3281
+ const [latestOffset] = result;
2487
3282
  const currentScrollOffset = this.getScrollOffset();
2488
3283
  if (!approxEqual(latestOffset, currentScrollOffset)) {
2489
3284
  this.scrollToIndex(index, { align, behavior });
@@ -2858,8 +3653,8 @@ function RevisionHistory(props) {
2858
3653
  return jsxs(TooltipProvider, { children: [jsx(Header, { docId: documentId, onChangeScope, onClose, scope, title: documentTitle }), PaginationComponent, jsx("div", { className: "mt-4 flex justify-center rounded-md bg-slate-50 p-4", children: visibleOperations.length > 0 ? jsx("div", { className: "grid grid-cols-[minmax(min-content,1018px)]", children: jsx(Timeline, { globalOperations: scope === "global" ? pageItems : [], localOperations: scope === "local" ? pageItems : [], scope }) }) : jsx("h3", { className: "my-40 text-gray-600", children: "This document has no recorded operations yet." }) }), PaginationComponent] });
2859
3654
  }
2860
3655
  function Tooltip(props) {
2861
- const { children, content, open, defaultOpen, onOpenChange, className, ...rest } = props;
2862
- return jsxs(Root3, { defaultOpen, delayDuration: 0, onOpenChange, open, children: [jsx(Trigger, { asChild: true, children }), jsx(Portal, { children: jsx(Content2, { ...rest, className: twMerge("shadow-tooltip rounded-lg border border-gray-200 bg-white p-2 text-xs", className), children: content }) })] });
3656
+ const { children, content, open, defaultOpen, onOpenChange, className, side = "top", sideOffset = 5, ...rest } = props;
3657
+ return jsxs(Root3, { defaultOpen, delayDuration: 0, onOpenChange, open, children: [jsx(Trigger, { asChild: true, children }), jsx(Portal, { children: jsx(Content2, { ...rest, side, sideOffset, className: twMerge("shadow-tooltip rounded-lg border border-gray-200 bg-white p-2 text-xs", className), children: content }) })] });
2863
3658
  }
2864
3659
  const TooltipProvider = Provider;
2865
3660
  const BUDGET = "powerhouse/budget-statement";
@@ -2883,471 +3678,150 @@ function useDrag(props) {
2883
3678
  const onDragEnd = useCallback(() => {
2884
3679
  setIsDragging(false);
2885
3680
  }, []);
2886
- const allowedToDragNode = !!uiNode && uiNode.kind !== DRIVE;
2887
- return useMemo(() => {
2888
- const dragProps = allowedToDragNode ? {
2889
- draggable: true,
2890
- onDragStart,
2891
- onDragEnd
2892
- } : {
2893
- draggable: false,
2894
- onDragStart: void 0,
2895
- onDragEnd: void 0
2896
- };
2897
- return {
2898
- isDragging,
2899
- dragProps
2900
- };
2901
- }, [allowedToDragNode, isDragging, onDragEnd, onDragStart]);
2902
- }
2903
- function useDrop(props) {
2904
- const { uiNode, onAddFile, onCopyNode, onMoveNode } = props;
2905
- const [isDropTarget, setIsDropTarget] = useState(false);
2906
- const allowedToBeDropTarget = !!uiNode && uiNode.kind !== FILE;
2907
- const onDragOver = useCallback((event) => {
2908
- event.preventDefault();
2909
- setIsDropTarget(true);
2910
- }, []);
2911
- const onDragLeave = useCallback(() => {
2912
- setIsDropTarget(false);
2913
- }, []);
2914
- const onDrop = useCallback(async (event) => {
2915
- event.preventDefault();
2916
- event.stopPropagation();
2917
- if (!uiNode)
2918
- return;
2919
- const droppedFiles = getDroppedFiles(event.dataTransfer.items).filter(Boolean);
2920
- if (droppedFiles.length) {
2921
- for (const file of droppedFiles) {
2922
- if (file) {
2923
- await onAddFile(file, uiNode);
2924
- }
2925
- }
2926
- } else {
2927
- const altOrOptionKeyPressed = event.getModifierState("Alt");
2928
- const data = event.dataTransfer.getData(UI_NODE);
2929
- const droppedNode = JSON.parse(data);
2930
- if (altOrOptionKeyPressed) {
2931
- await onCopyNode(droppedNode, uiNode);
2932
- } else {
2933
- await onMoveNode(droppedNode, uiNode);
2934
- }
2935
- }
2936
- setIsDropTarget(false);
2937
- }, [onAddFile, onCopyNode, onMoveNode, uiNode]);
2938
- return useMemo(() => {
2939
- const dropProps = allowedToBeDropTarget ? { onDragOver, onDragLeave, onDrop } : {
2940
- onDragOver: void 0,
2941
- onDragLeave: void 0,
2942
- onDrop: void 0
2943
- };
2944
- return {
2945
- isDropTarget,
2946
- dropProps
2947
- };
2948
- }, [allowedToBeDropTarget, isDropTarget, onDragLeave, onDragOver, onDrop]);
2949
- }
2950
- function getDroppedFiles(items) {
2951
- const droppedFiles = Array.from(items).map((item) => item.kind === "file" ? item.getAsFile() : null).filter(Boolean);
2952
- return droppedFiles;
2953
- }
2954
- function formatEthAddress(address) {
2955
- return `${address.slice(0, 7)}...${address.slice(-5)}`;
2956
- }
2957
- function getDocumentIconSrc(documentType, customDocumentIconSrc) {
2958
- if (customDocumentIconSrc) {
2959
- return customDocumentIconSrc;
2960
- }
2961
- if (documentTypes.includes(documentType)) {
2962
- return iconMap[documentType];
2963
- }
2964
- return iconMap[DEFAULT];
2965
- }
2966
- const validateDocument = (document2) => {
2967
- const errors = [];
2968
- if (document2.documentType !== "powerhouse/document-model") {
2969
- return errors;
2970
- }
2971
- const doc = document2;
2972
- const specs = doc.state.global.specifications[0];
2973
- const initialStateErrors = Object.keys(specs.state).reduce((acc, scopeKey) => {
2974
- const scope = scopeKey;
2975
- return [
2976
- ...acc,
2977
- ...validateInitialState(
2978
- specs.state[scope].initialValue,
2979
- scope !== "global"
2980
- ).map((err) => ({
2981
- ...err,
2982
- message: `${err.message}. Scope: ${scope}`,
2983
- details: { ...err.details, scope }
2984
- }))
2985
- ];
2986
- }, []);
2987
- const schemaStateErrors = Object.keys(specs.state).reduce((acc, scopeKey) => {
2988
- var _a;
2989
- const scope = scopeKey;
2990
- const isGlobalScope = scope === "global";
2991
- return [
2992
- ...acc,
2993
- ...validateStateSchemaName(
2994
- specs.state[scope].schema,
2995
- doc.name || ((_a = doc.state.global) == null ? void 0 : _a.name) || "",
2996
- !isGlobalScope ? scope : "",
2997
- !isGlobalScope
2998
- ).map((err) => ({
2999
- ...err,
3000
- message: `${err.message}. Scope: ${scope}`,
3001
- details: { ...err.details, scope }
3002
- }))
3003
- ];
3004
- }, []);
3005
- const modulesErrors = validateModules(specs.modules);
3006
- return [...initialStateErrors, ...schemaStateErrors, ...modulesErrors];
3007
- };
3008
- function useUserPermissions() {
3009
- return {
3010
- isAllowedToCreateDocuments: true,
3011
- isAllowedToEditDocuments: true
3012
- };
3013
- }
3014
- function useDocument(reactor, documentMeta = {}) {
3015
- const { documentId, documentType, driveId } = documentMeta;
3016
- const [document2, setDocument] = useState();
3017
- const onStrandUpdate = useCallback((cb) => {
3018
- if (!reactor) {
3019
- throw new Error("Reactor is not loaded");
3020
- }
3021
- return reactor.on("strandUpdate", cb);
3022
- }, [reactor]);
3023
- useEffect(() => {
3024
- if (!reactor)
3025
- return;
3026
- if (!driveId || !documentId || !documentType)
3027
- return;
3028
- reactor.getDocument(driveId, documentId).then(setDocument).catch(console.error);
3029
- }, [driveId, documentId, documentType, reactor]);
3030
- useEffect(() => {
3031
- if (!reactor)
3032
- return;
3033
- if (!driveId || !documentId || !documentType)
3034
- return;
3035
- const removeListener = onStrandUpdate((strand) => {
3036
- if (strand.driveId === driveId && strand.documentId === documentId) {
3037
- reactor.getDocument(driveId, documentId).then(setDocument).catch(console.error);
3038
- }
3039
- });
3040
- return removeListener;
3041
- }, [onStrandUpdate, driveId, documentId, documentType]);
3042
- return document2;
3043
- }
3044
- async function signOperation(operation, sign, documentId, document2, reducer, user) {
3045
- if (!user)
3046
- return operation;
3047
- if (!operation.context)
3048
- return operation;
3049
- if (!operation.context.signer)
3050
- return operation;
3051
- if (!reducer) {
3052
- console.error(`Document model '${document2.documentType}' does not have a reducer`);
3053
- return operation;
3054
- }
3055
- const context = {
3056
- documentId,
3057
- signer: operation.context.signer
3058
- };
3059
- const signedOperation = await buildSignedOperation(operation, reducer, document2, context, sign);
3060
- return signedOperation;
3061
- }
3062
- function addActionContext(action, connectDid, user) {
3063
- if (!user)
3064
- return action;
3065
- const signer = {
3066
- app: {
3067
- name: "Connect",
3068
- key: connectDid || ""
3069
- },
3070
- user: {
3071
- address: user.address,
3072
- networkId: user.networkId,
3073
- chainId: user.chainId
3074
- },
3075
- signatures: []
3076
- };
3077
- return {
3078
- context: { signer },
3079
- ...action
3080
- };
3081
- }
3082
- function debounceOperations(callback, timeout = 50) {
3083
- let timer;
3084
- const operations = [];
3085
- return (operation) => {
3086
- if (timer) {
3087
- clearTimeout(timer);
3088
- }
3089
- const index = operations.findIndex((op) => op.scope === operation.scope && op.index === operation.index);
3090
- if (index > -1) {
3091
- const oldOperation = operations[index];
3092
- if (!(oldOperation.type === operation.type && JSON.stringify(operation.input) === JSON.stringify(oldOperation.input))) {
3093
- console.warn("Two conflicting operations were dispatched:", oldOperation, operation);
3094
- }
3095
- operations[index] = operation;
3096
- } else {
3097
- operations.push(operation);
3098
- }
3099
- return new Promise((resolve, reject) => {
3100
- timer = setTimeout(() => {
3101
- callback(operations).then(resolve).catch(reject);
3102
- }, timeout);
3103
- });
3104
- };
3105
- }
3106
- function useAddDebouncedOperations(reactor, props) {
3107
- const { driveId, documentId } = props;
3108
- const [documentDrives] = useDocumentDrives(reactor);
3109
- const documentDrivesRef = useRef(documentDrives);
3110
- const { isAllowedToEditDocuments } = useUserPermissions() || {
3111
- isAllowedToEditDocuments: false
3112
- };
3113
- useEffect(() => {
3114
- documentDrivesRef.current = documentDrives;
3115
- }, [documentDrives]);
3116
- const addOperations = useCallback(async (driveId2, id, operations) => {
3117
- if (!isAllowedToEditDocuments) {
3118
- throw new Error("User is not allowed to edit documents");
3119
- }
3120
- if (!reactor) {
3121
- throw new Error("Reactor is not loaded");
3122
- }
3123
- const drive = documentDrivesRef.current.find((drive2) => drive2.state.global.id === driveId2);
3124
- if (!drive) {
3125
- throw new Error(`Drive with id ${driveId2} not found`);
3126
- }
3127
- const newDocument = await reactor.queueOperations(driveId2, id, operations);
3128
- return newDocument.document;
3129
- }, [isAllowedToEditDocuments, reactor]);
3130
- const addDebouncedOperations = useMemo(() => {
3131
- return debounceOperations((operations) => addOperations(driveId, documentId, operations));
3132
- }, [addOperations, driveId, documentId]);
3133
- return addDebouncedOperations;
3681
+ const allowedToDragNode = !!uiNode && uiNode.kind !== DRIVE;
3682
+ return useMemo(() => {
3683
+ const dragProps = allowedToDragNode ? {
3684
+ draggable: true,
3685
+ onDragStart,
3686
+ onDragEnd
3687
+ } : {
3688
+ draggable: false,
3689
+ onDragStart: void 0,
3690
+ onDragEnd: void 0
3691
+ };
3692
+ return {
3693
+ isDragging,
3694
+ dragProps
3695
+ };
3696
+ }, [allowedToDragNode, isDragging, onDragEnd, onDragStart]);
3134
3697
  }
3135
- function useDocumentDispatch(documentReducer, initialState, onError = console.error) {
3136
- const [state, setState] = useState(initialState);
3137
- const [error, setError] = useState();
3138
- const onErrorHandler = (error2) => {
3139
- setError(error2);
3140
- onError(error2);
3141
- };
3142
- useEffect(() => {
3143
- setState(initialState);
3144
- }, [initialState]);
3145
- const dispatch = (action, callback, onErrorCallback) => {
3146
- setError(void 0);
3147
- setState((_state) => {
3148
- if (!documentReducer || !_state)
3149
- return _state;
3150
- try {
3151
- const newState = documentReducer(_state, action);
3152
- const scope = action.scope ?? "global";
3153
- const operations = newState.operations[scope];
3154
- const operation = operations[operations.length - 1];
3155
- if (operation.error) {
3156
- const error2 = new Error(operation.error);
3157
- onErrorHandler(error2);
3158
- onErrorCallback == null ? void 0 : onErrorCallback(error2);
3698
+ function useDrop(props) {
3699
+ const { uiNode, onAddFile, onCopyNode, onMoveNode } = props;
3700
+ const [isDropTarget, setIsDropTarget] = useState(false);
3701
+ const allowedToBeDropTarget = !!uiNode && uiNode.kind !== FILE$1;
3702
+ const onDragOver = useCallback((event) => {
3703
+ event.preventDefault();
3704
+ setIsDropTarget(true);
3705
+ }, []);
3706
+ const onDragLeave = useCallback(() => {
3707
+ setIsDropTarget(false);
3708
+ }, []);
3709
+ const onDrop = useCallback(async (event) => {
3710
+ event.preventDefault();
3711
+ event.stopPropagation();
3712
+ if (!uiNode)
3713
+ return;
3714
+ const droppedFiles = getDroppedFiles(event.dataTransfer.items).filter(Boolean);
3715
+ if (droppedFiles.length) {
3716
+ for (const file of droppedFiles) {
3717
+ if (file) {
3718
+ await onAddFile(file, uiNode);
3159
3719
  }
3160
- callback == null ? void 0 : callback(operation, {
3161
- prevState: { ..._state },
3162
- newState: { ...newState }
3163
- });
3164
- return newState;
3165
- } catch (error2) {
3166
- onErrorHandler(error2);
3167
- onErrorCallback == null ? void 0 : onErrorCallback(error2);
3168
- return _state;
3169
3720
  }
3170
- });
3171
- };
3172
- return [state, dispatch, error];
3173
- }
3174
- function useDocumentEditorProps(reactor, props) {
3175
- const { nodeId, driveId, documentModelModule, document: initialDocument, user, connectDid, sign } = props;
3176
- const addDebouncedOprations = useAddDebouncedOperations(reactor, {
3177
- driveId,
3178
- documentId: nodeId
3179
- });
3180
- const [document2, _dispatch, error] = useDocumentDispatch(documentModelModule.reducer, initialDocument);
3181
- function dispatch(action, onErrorCallback) {
3182
- const callback = (operation, state) => {
3183
- const { prevState } = state;
3184
- signOperation(operation, sign, nodeId, prevState, documentModelModule.reducer, user).then((op) => {
3185
- return addDebouncedOprations(op);
3186
- }).catch(console.error);
3721
+ } else {
3722
+ const altOrOptionKeyPressed = event.getModifierState("Alt");
3723
+ const data = event.dataTransfer.getData(UI_NODE);
3724
+ const droppedNode = JSON.parse(data);
3725
+ if (altOrOptionKeyPressed) {
3726
+ await onCopyNode(droppedNode, uiNode);
3727
+ } else {
3728
+ await onMoveNode(droppedNode, uiNode);
3729
+ }
3730
+ }
3731
+ setIsDropTarget(false);
3732
+ }, [onAddFile, onCopyNode, onMoveNode, uiNode]);
3733
+ return useMemo(() => {
3734
+ const dropProps = allowedToBeDropTarget ? { onDragOver, onDragLeave, onDrop } : {
3735
+ onDragOver: void 0,
3736
+ onDragLeave: void 0,
3737
+ onDrop: void 0
3187
3738
  };
3188
- _dispatch(addActionContext(action, connectDid, user), callback, onErrorCallback);
3189
- }
3190
- return {
3191
- dispatch,
3192
- document: document2,
3193
- error
3194
- };
3739
+ return {
3740
+ isDropTarget,
3741
+ dropProps
3742
+ };
3743
+ }, [allowedToBeDropTarget, isDropTarget, onDragLeave, onDragOver, onDrop]);
3195
3744
  }
3196
- const generateId = () => generateId$1().toString();
3197
- function getNode(id, drive) {
3198
- return drive.state.global.nodes.find((node) => node.id === id);
3745
+ function getDroppedFiles(items) {
3746
+ const droppedFiles = Array.from(items).map((item) => item.kind === "file" ? item.getAsFile() : null).filter(Boolean);
3747
+ return droppedFiles;
3199
3748
  }
3200
- function createDriveActions(document2, dispatch, context) {
3201
- const drive = document2;
3202
- const { id: driveId } = drive.state.global;
3203
- const { selectedNode } = context;
3204
- const handleAddFolder = async (name, parentFolder, id = generateId()) => {
3205
- dispatch(addFolder({
3206
- id,
3207
- name,
3208
- parentFolder: parentFolder ?? null
3209
- }));
3210
- };
3211
- const addDocument = async (name, documentType, document3, parentFolder, id = generateId()) => {
3212
- const action = generateAddNodeAction(drive.state.global, {
3213
- id,
3214
- name,
3215
- parentFolder: parentFolder ?? null,
3216
- documentType,
3217
- document: document3
3218
- }, ["global"]);
3219
- dispatch(action);
3220
- };
3221
- const addFile = async (file, parentFolder = selectedNode && isFileNode(selectedNode) ? void 0 : selectedNode == null ? void 0 : selectedNode.id, name = file.name.replace(/\.zip$/gim, "")) => {
3222
- const folder = parentFolder ? getNode(parentFolder, drive) : void 0;
3223
- if (parentFolder && !folder) {
3224
- throw new Error(`Parent folder with id "${parentFolder}" not found`);
3225
- }
3226
- if (folder && !isFolderNode(folder)) {
3227
- throw new Error(`Parent folder with id "${parentFolder}" is not a folder`);
3228
- }
3229
- await context.addFile(file, driveId, name, parentFolder);
3230
- };
3231
- const handleDeleteNode = async (id) => {
3232
- dispatch(deleteNode({ id }));
3233
- };
3234
- const renameNode = async (id, name) => {
3235
- dispatch(updateNode({ id, name }));
3236
- };
3237
- const handleMoveNode = async (sourceId, targetId) => {
3238
- dispatch(moveNode({
3239
- srcFolder: sourceId,
3240
- targetParentFolder: targetId
3241
- }));
3242
- };
3243
- const handleCopyNode = async (sourceId, targetFolderId) => {
3244
- const target = targetFolderId ? getNode(targetFolderId, drive) : void 0;
3245
- if (targetFolderId && !target && targetFolderId !== driveId) {
3246
- throw new Error(`Target node with id "${targetFolderId}" not found`);
3247
- }
3248
- if (target && !isFolderNode(target)) {
3249
- throw new Error(`Target node with id "${targetFolderId}" is not a folder`);
3250
- }
3251
- const source = getNode(sourceId, drive);
3252
- if (!source) {
3253
- throw new Error(`Source node with id "${sourceId}" not found`);
3254
- }
3255
- const copyNodesInput = generateNodesCopy({
3256
- srcId: sourceId,
3257
- targetParentFolder: target == null ? void 0 : target.id,
3258
- targetName: source.name
3259
- }, generateId, drive.state.global.nodes);
3260
- const copyActions = copyNodesInput.map((copyNodeInput) => copyNode(copyNodeInput));
3261
- for (const copyAction of copyActions) {
3262
- dispatch(copyAction);
3263
- }
3264
- };
3265
- const duplicateNode = async (sourceId) => {
3266
- const node = getNode(sourceId, drive);
3267
- if (!node) {
3268
- throw new Error(`Node with id "${sourceId}" not found`);
3269
- }
3270
- await handleCopyNode(node.id, node.parentFolder || void 0);
3271
- };
3272
- return {
3273
- context,
3274
- selectNode: context.selectNode,
3275
- addFolder: handleAddFolder,
3276
- addFile,
3277
- addDocument,
3278
- deleteNode: handleDeleteNode,
3279
- renameNode,
3280
- moveNode: handleMoveNode,
3281
- copyNode: handleCopyNode,
3282
- duplicateNode
3283
- };
3749
+ function formatEthAddress(address) {
3750
+ return `${address.slice(0, 7)}...${address.slice(-5)}`;
3284
3751
  }
3285
- function useDriveActions(document2, dispatch, context) {
3286
- return useMemo(() => createDriveActions(document2, dispatch, context), [document2, dispatch, context]);
3752
+ function getDocumentIconSrc(documentType, customDocumentIconSrc) {
3753
+ if (customDocumentIconSrc) {
3754
+ return customDocumentIconSrc;
3755
+ }
3756
+ if (documentTypes.includes(documentType)) {
3757
+ return iconMap[documentType];
3758
+ }
3759
+ return iconMap[DEFAULT];
3287
3760
  }
3288
- function toNode(uiNode) {
3289
- if (uiNode.kind === "DRIVE") {
3290
- throw new Error("Cannot convert drive node to regular node");
3761
+ const validateDocument = (document2) => {
3762
+ const errors = [];
3763
+ if (document2.documentType !== "powerhouse/document-model") {
3764
+ return errors;
3291
3765
  }
3292
- const { id, name, parentFolder, kind } = uiNode;
3293
- if (kind === "FOLDER") {
3294
- return { id, name, parentFolder, kind: "folder" };
3295
- } else {
3296
- const fileNode = uiNode;
3766
+ const doc = document2;
3767
+ const specs = doc.state.global.specifications[0];
3768
+ const initialStateErrors = Object.keys(specs.state).reduce((acc, scopeKey) => {
3769
+ const scope = scopeKey;
3770
+ return [
3771
+ ...acc,
3772
+ ...validateInitialState(
3773
+ specs.state[scope].initialValue,
3774
+ scope !== "global"
3775
+ ).map((err) => ({
3776
+ ...err,
3777
+ message: `${err.message}. Scope: ${scope}`,
3778
+ details: { ...err.details, scope }
3779
+ }))
3780
+ ];
3781
+ }, []);
3782
+ const schemaStateErrors = Object.keys(specs.state).reduce((acc, scopeKey) => {
3783
+ var _a;
3784
+ const scope = scopeKey;
3785
+ const isGlobalScope = scope === "global";
3786
+ return [
3787
+ ...acc,
3788
+ ...validateStateSchemaName(
3789
+ specs.state[scope].schema,
3790
+ doc.name || ((_a = doc.state.global) == null ? void 0 : _a.name) || "",
3791
+ !isGlobalScope ? scope : "",
3792
+ !isGlobalScope
3793
+ ).map((err) => ({
3794
+ ...err,
3795
+ message: `${err.message}. Scope: ${scope}`,
3796
+ details: { ...err.details, scope }
3797
+ }))
3798
+ ];
3799
+ }, []);
3800
+ const modulesErrors = validateModules(specs.modules);
3801
+ return [...initialStateErrors, ...schemaStateErrors, ...modulesErrors];
3802
+ };
3803
+ function useDocumentDriveById(driveId) {
3804
+ var _a;
3805
+ const { documentDrives } = useDocumentDriveServer();
3806
+ if (!driveId)
3297
3807
  return {
3298
- id,
3299
- name,
3300
- parentFolder,
3301
- kind: "file",
3302
- documentType: fileNode.documentType,
3303
- synchronizationUnits: fileNode.synchronizationUnits
3808
+ drive: null,
3809
+ remoteUrl: null,
3810
+ isRemoteDrive: false
3304
3811
  };
3305
- }
3306
- }
3307
- function createUiNodeAdapter(driveActions) {
3812
+ const drive = documentDrives.find((drive2) => drive2.id === driveId);
3813
+ const pullResponder = drive == null ? void 0 : drive.state.local.triggers.find(
3814
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
3815
+ (trigger) => trigger.type === "PullResponder"
3816
+ );
3817
+ const { remoteUrl } = (drive == null ? void 0 : drive.state.global) || {};
3818
+ const isRemoteDrive = !!remoteUrl || !!pullResponder;
3308
3819
  return {
3309
- ...driveActions,
3310
- addFile: (file, parentNode) => driveActions.addFile(file, parentNode == null ? void 0 : parentNode.id),
3311
- addFolder: (name, parentFolder = void 0) => driveActions.addFolder(name, parentFolder),
3312
- renameNode: (name, node) => {
3313
- const converted = toNode(node);
3314
- return driveActions.renameNode(converted.id, name);
3315
- },
3316
- deleteNode: (node) => {
3317
- const converted = toNode(node);
3318
- return driveActions.deleteNode(converted.id);
3319
- },
3320
- moveNode: async (src, target) => {
3321
- if (target.kind === FILE$1 || src.parentFolder === target.id)
3322
- return;
3323
- const srcNode = toNode(src);
3324
- const targetNode = toNode(target);
3325
- return driveActions.moveNode(srcNode.id, targetNode.id);
3326
- },
3327
- copyNode: (src, target) => {
3328
- return driveActions.copyNode(src.id, target.id);
3329
- },
3330
- duplicateNode: (node) => {
3331
- const converted = toNode(node);
3332
- return driveActions.duplicateNode(converted.id);
3333
- }
3820
+ drive,
3821
+ remoteUrl: remoteUrl || ((_a = pullResponder == null ? void 0 : pullResponder.data) == null ? void 0 : _a.url),
3822
+ isRemoteDrive
3334
3823
  };
3335
3824
  }
3336
- function useDriveActionsWithUiNodes(document2, dispatch) {
3337
- const { selectedNode, selectedDriveNode, setSelectedNode, getNodeById } = useUiNodesContext();
3338
- const _driveContext = useDriveContext();
3339
- const driveContext = useMemo(() => ({
3340
- ..._driveContext,
3341
- selectedNode,
3342
- onSelectNode: (node) => {
3343
- _driveContext.selectNode(node);
3344
- setSelectedNode(getNodeById(node.id));
3345
- }
3346
- }), [selectedNode, selectedDriveNode == null ? void 0 : selectedDriveNode.driveId, setSelectedNode, getNodeById]);
3347
- const driveActions = useDriveActions(document2, dispatch, driveContext);
3348
- const uiNodeActions = useMemo(() => createUiNodeAdapter(driveActions), [driveActions]);
3349
- return uiNodeActions;
3350
- }
3351
3825
  function useDocumentEditor(props) {
3352
3826
  const { driveId, documentId, documentType, documentModelModule, user } = props;
3353
3827
  const reactor = useUnwrappedReactor();
@@ -3420,12 +3894,9 @@ function useEditorProps(document2, node, documentDispatch, onAddOperation) {
3420
3894
  const user = useUser() || void 0;
3421
3895
  const userPermissions = useUserPermissions$1();
3422
3896
  const context = useMemo(() => ({ theme, user }), [theme, user]);
3423
- const {
3424
- selectedParentNode,
3425
- selectedDocument,
3426
- setSelectedNode,
3427
- getDocumentModelModule
3428
- } = useUiNodes();
3897
+ const { selectedDocument } = useFileNodeDocument();
3898
+ const { selectedParentNode, setSelectedNode } = useUiNodesContext();
3899
+ const getDocumentModelModule = useGetDocumentModelModule();
3429
3900
  const canUndo = !!document2 && (document2.revision.global > 0 || document2.revision.local > 0);
3430
3901
  const canRedo = !!(document2 == null ? void 0 : document2.clipboard.length);
3431
3902
  const dispatch = useEditorDispatch(node, documentDispatch, onAddOperation);
@@ -3489,6 +3960,31 @@ function useEditorProps(document2, node, documentDispatch, onAddOperation) {
3489
3960
  isAllowedToEditDocuments: (userPermissions == null ? void 0 : userPermissions.isAllowedToEditDocuments) ?? false
3490
3961
  };
3491
3962
  }
3963
+ function useGetDocument() {
3964
+ const { openFile } = useDocumentDriveServer();
3965
+ const getDocument = useCallback(
3966
+ async (driveId, documentId, options) => {
3967
+ const document2 = await openFile(driveId, documentId, options);
3968
+ return document2;
3969
+ },
3970
+ [openFile]
3971
+ );
3972
+ return getDocument;
3973
+ }
3974
+ const useOpenSwitchboardLink = (driveId) => {
3975
+ const { isRemoteDrive, remoteUrl } = useDocumentDriveById(driveId);
3976
+ const reactor = useUnwrappedReactor();
3977
+ const { getDocumentGraphqlQuery, getSwitchboardGatewayUrl } = useSwitchboard(reactor);
3978
+ return async (uiNode) => {
3979
+ if ((uiNode == null ? void 0 : uiNode.kind) !== FILE$1 || !remoteUrl || !isRemoteDrive) return;
3980
+ const url = new URL(remoteUrl);
3981
+ url.origin;
3982
+ const switchboardUrl = getSwitchboardGatewayUrl(remoteUrl);
3983
+ const query = await getDocumentGraphqlQuery(driveId, uiNode.id);
3984
+ const encodedQuery = encodeURIComponent(query);
3985
+ await openUrl(`${switchboardUrl}?query=${encodedQuery}`);
3986
+ };
3987
+ };
3492
3988
  function useSyncStatus(driveId, documentId) {
3493
3989
  const { getSyncStatusSync, onSyncStatus, documentDrives } = useDocumentDriveServer();
3494
3990
  const syncStatus = useSyncExternalStore(
@@ -3498,9 +3994,7 @@ function useSyncStatus(driveId, documentId) {
3498
3994
  },
3499
3995
  () => {
3500
3996
  var _a;
3501
- const drive = documentDrives.find(
3502
- (_drive) => _drive.state.global.id === driveId
3503
- );
3997
+ const drive = documentDrives.find((_drive) => _drive.id === driveId);
3504
3998
  if (!drive) return;
3505
3999
  const isReadDrive = "readContext" in drive;
3506
4000
  const _sharingType = !isReadDrive ? (_a = drive.state.local.sharingType) == null ? void 0 : _a.toUpperCase() : "PUBLIC";
@@ -3523,41 +4017,366 @@ function useSyncStatus(driveId, documentId) {
3523
4017
  );
3524
4018
  return status;
3525
4019
  }
3526
- );
3527
- return syncStatus;
4020
+ );
4021
+ return syncStatus;
4022
+ }
4023
+ const useUndoRedoShortcuts = (props) => {
4024
+ var _a;
4025
+ const { undo: undo2, redo: redo2, canRedo, canUndo } = props;
4026
+ const { isMac } = ((_a = window.electronAPI) == null ? void 0 : _a.platformInfo) || {};
4027
+ let undoShortcut = "ctrl+z";
4028
+ let redoShortcut = "ctrl+y";
4029
+ if (isMac) {
4030
+ undoShortcut = "mod+z";
4031
+ redoShortcut = "mod+shift+z";
4032
+ }
4033
+ useHotkeys(
4034
+ undoShortcut,
4035
+ (event) => {
4036
+ event.preventDefault();
4037
+ if (canUndo) {
4038
+ undo2();
4039
+ }
4040
+ },
4041
+ {},
4042
+ [canUndo, undo2]
4043
+ );
4044
+ useHotkeys(
4045
+ redoShortcut,
4046
+ (event) => {
4047
+ event.preventDefault();
4048
+ if (canRedo) {
4049
+ redo2();
4050
+ }
4051
+ },
4052
+ {},
4053
+ [canRedo, redo2]
4054
+ );
4055
+ };
4056
+ function getDocumentSpec(doc) {
4057
+ if ("documentModelState" in doc) {
4058
+ return doc.documentModelState;
4059
+ }
4060
+ return doc.documentModel;
4061
+ }
4062
+ const CreateDocument = ({ documentModels, createDocument }) => {
4063
+ return jsxs("div", { className: "px-6", children: [jsx("h3", { className: "mb-3 mt-4 text-xl font-bold text-gray-600", children: "New document" }), jsx("div", { className: "flex w-full flex-wrap gap-4", children: documentModels == null ? void 0 : documentModels.map((doc) => {
4064
+ const spec = getDocumentSpec(doc);
4065
+ return jsx(Button, { color: "light", "aria-details": spec.description, onClick: () => createDocument(doc), children: jsx("span", { className: "text-sm", children: spec.name }) }, spec.id);
4066
+ }) })] });
4067
+ };
4068
+ function sortUiNodesByName(a, b) {
4069
+ return a.name.localeCompare(b.name);
4070
+ }
4071
+ const GAP = 8;
4072
+ const ITEM_WIDTH = 256;
4073
+ const ITEM_HEIGHT = 48;
4074
+ const USED_SPACE = 420;
4075
+ function FileContentView(props) {
4076
+ const parentRef = useRef(null);
4077
+ const { t } = useTranslation();
4078
+ const windowSize = useWindowSize();
4079
+ const { fileNodes, ...fileProps } = props;
4080
+ const availableWidth = windowSize.innerWidth - USED_SPACE;
4081
+ const columnCount = Math.floor(availableWidth / (ITEM_WIDTH + GAP)) || 1;
4082
+ const rowCount = Math.ceil(fileNodes.length / columnCount);
4083
+ const rowVirtualizer = useVirtualizer({
4084
+ count: rowCount,
4085
+ getScrollElement: () => parentRef.current,
4086
+ estimateSize: (index) => {
4087
+ if (index > 0) {
4088
+ return ITEM_HEIGHT + GAP;
4089
+ }
4090
+ return ITEM_HEIGHT;
4091
+ },
4092
+ overscan: 5
4093
+ });
4094
+ const columnVirtualizer = useVirtualizer({
4095
+ horizontal: true,
4096
+ count: columnCount,
4097
+ getScrollElement: () => parentRef.current,
4098
+ estimateSize: (index) => {
4099
+ if (index > 0) {
4100
+ return ITEM_WIDTH + GAP;
4101
+ }
4102
+ return ITEM_WIDTH;
4103
+ },
4104
+ overscan: 5
4105
+ });
4106
+ const getItemIndex = (rowIndex, columnIndex) => rowIndex * columnCount + columnIndex;
4107
+ const getItem = (rowIndex, columnIndex) => {
4108
+ const index = getItemIndex(rowIndex, columnIndex);
4109
+ return fileNodes[index] || null;
4110
+ };
4111
+ if (fileNodes.length === 0) {
4112
+ return jsx("div", { className: "mb-8 text-sm text-gray-400", children: t("folderView.sections.documents.empty", {
4113
+ defaultValue: "No documents or files 📄"
4114
+ }) });
4115
+ }
4116
+ const renderItem = (rowIndex, columnIndex) => {
4117
+ const fileNode = getItem(rowIndex, columnIndex);
4118
+ if (!fileNode) {
4119
+ return null;
4120
+ }
4121
+ return jsx("div", { style: {
4122
+ marginLeft: columnIndex === 0 ? 0 : GAP
4123
+ }, children: jsx(FileItem, { uiNode: fileNode, ...fileProps }, fileNode.id) });
4124
+ };
4125
+ return jsx("div", { ref: parentRef, style: {
4126
+ height: `400px`,
4127
+ width: `100%`,
4128
+ overflow: "auto"
4129
+ }, children: jsx("div", { style: {
4130
+ height: `${rowVirtualizer.getTotalSize()}px`,
4131
+ width: `${columnVirtualizer.getTotalSize()}px`,
4132
+ position: "relative"
4133
+ }, children: rowVirtualizer.getVirtualItems().map((virtualRow) => jsx(React__default.Fragment, { children: columnVirtualizer.getVirtualItems().map((virtualColumn) => jsx("div", { style: {
4134
+ position: "absolute",
4135
+ top: 0,
4136
+ left: 0,
4137
+ marginTop: virtualRow.index === 0 ? 0 : GAP,
4138
+ width: `${virtualColumn.size}px`,
4139
+ height: `${virtualRow.size}px`,
4140
+ transform: `translateX(${virtualColumn.start}px) translateY(${virtualRow.start}px)`
4141
+ }, children: renderItem(virtualRow.index, virtualColumn.index) }, virtualColumn.key)) }, virtualRow.key)) }) });
4142
+ }
4143
+ function FolderView(props) {
4144
+ const { node, className, isDropTarget, containerProps, ...nodeProps } = props;
4145
+ const { t } = useTranslation();
4146
+ const folderNodes = node.children.filter((node2) => node2.kind === FOLDER).sort(sortUiNodesByName);
4147
+ const fileNodes = node.children.filter((node2) => node2.kind === FILE$1).sort(sortUiNodesByName);
4148
+ const folderCallbacks = {
4149
+ onSelectNode: (node2) => nodeProps.onSelectNode(node2),
4150
+ onRenameNode: (name, node2) => nodeProps.onRenameNode(name, node2),
4151
+ onDuplicateNode: (node2) => nodeProps.onDuplicateNode(node2),
4152
+ onDeleteNode: (node2) => nodeProps.onDeleteNode(node2)
4153
+ };
4154
+ const fileCallbacks = {
4155
+ onSelectNode: (node2) => nodeProps.onSelectNode(node2),
4156
+ onRenameNode: (name, node2) => nodeProps.onRenameNode(name, node2),
4157
+ onDuplicateNode: (node2) => nodeProps.onDuplicateNode(node2),
4158
+ onDeleteNode: (node2) => nodeProps.onDeleteNode(node2)
4159
+ };
4160
+ const baseNodeCallbacks = {
4161
+ onAddFile: async (file, parentNode) => {
4162
+ await nodeProps.onAddFile(file, parentNode);
4163
+ },
4164
+ onCopyNode: async (uiNode, targetNode) => {
4165
+ await nodeProps.onCopyNode(uiNode, targetNode);
4166
+ },
4167
+ onMoveNode: async (uiNode, targetNode) => {
4168
+ await nodeProps.onMoveNode(uiNode, targetNode);
4169
+ }
4170
+ };
4171
+ return jsxs("div", { className: twMerge("rounded-md border-2 border-transparent p-2", isDropTarget && "border-dashed border-blue-100", className), ...containerProps, children: [jsx(DriveLayout.ContentSection, { title: t("folderView.sections.folders.title", {
4172
+ defaultValue: "Folders"
4173
+ }), className: "mb-4", children: folderNodes.length > 0 ? folderNodes.map((folderNode) => jsx(FolderItem, { uiNode: folderNode, ...baseNodeCallbacks, ...folderCallbacks, isAllowedToCreateDocuments: nodeProps.isAllowedToCreateDocuments }, folderNode.id)) : jsx("div", { className: "mb-8 text-sm text-gray-400", children: t("folderView.sections.folders.empty", {
4174
+ defaultValue: "No documents or files 📄"
4175
+ }) }) }), jsx(DriveLayout.ContentSection, { title: t("folderView.sections.documents.title", {
4176
+ defaultValue: "Documents and files"
4177
+ }), children: jsx("div", { className: twMerge("w-full", fileNodes.length > 0 ? "min-h-[400px]" : "min-h-14"), children: jsx(FileContentView, { fileNodes, ...fileCallbacks, isAllowedToCreateDocuments: nodeProps.isAllowedToCreateDocuments }) }) })] });
4178
+ }
4179
+ function BaseEditor(props) {
4180
+ const { document: document2, dispatch, className, children } = props;
4181
+ const { id: driveId } = document2;
4182
+ const { showSearchBar, isAllowedToCreateDocuments, documentModels, showCreateDocumentModal } = useDriveContext();
4183
+ const { driveNodes, selectedNode, selectedNodePath, getNodeById, setSelectedNode } = useUiNodesContext();
4184
+ const driveNode = useMemo(() => driveNodes.find((n) => n.id === driveId), [driveNodes, driveId]);
4185
+ const { addDocument, addFile, addFolder: addFolder2, renameNode, deleteNode: deleteNode2, moveNode: moveNode2, copyNode: copyNode2, duplicateNode } = useDriveActionsWithUiNodes(document2, dispatch);
4186
+ const onCreateDocument = useCallback(async (documentModel) => {
4187
+ const { name } = await showCreateDocumentModal(documentModel);
4188
+ const document3 = documentModel.utils.createDocument();
4189
+ await addDocument(name, documentModel.documentModel.name, document3, selectedNode == null ? void 0 : selectedNode.id);
4190
+ }, [addDocument, showCreateDocumentModal, selectedNode == null ? void 0 : selectedNode.id]);
4191
+ const { isDropTarget, dropProps } = useDrop({
4192
+ uiNode: selectedNode,
4193
+ onAddFile: addFile,
4194
+ onCopyNode: copyNode2,
4195
+ onMoveNode: moveNode2
4196
+ });
4197
+ const { breadcrumbs, onBreadcrumbSelected } = useBreadcrumbs({
4198
+ selectedNodePath,
4199
+ getNodeById,
4200
+ setSelectedNode
4201
+ });
4202
+ if (!driveNode) {
4203
+ return jsx("div", { children: "Drive not found" });
4204
+ } else if ((selectedNode == null ? void 0 : selectedNode.kind) === FILE) {
4205
+ return jsx(Fragment$1, {});
4206
+ }
4207
+ return jsxs(DriveLayout, { className, children: [children, jsxs(DriveLayout.Header, { children: [jsx(Breadcrumbs, { breadcrumbs, createEnabled: isAllowedToCreateDocuments, onCreate: addFolder2, onBreadcrumbSelected }), showSearchBar && jsx(SearchBar, {})] }), jsx(DriveLayout.Content, { children: jsx(FolderView, { node: selectedNode || driveNode, onSelectNode: setSelectedNode, onRenameNode: renameNode, onDuplicateNode: duplicateNode, onDeleteNode: deleteNode2, onAddFile: addFile, onCopyNode: copyNode2, onMoveNode: moveNode2, isDropTarget, isAllowedToCreateDocuments }) }), jsx(DriveLayout.Footer, { children: isAllowedToCreateDocuments && jsx(CreateDocument, { documentModels, createDocument: onCreateDocument }) })] });
4208
+ }
4209
+ function Editor(props) {
4210
+ return jsx(DriveContextProvider, { value: props.context, children: jsx(BaseEditor, { ...props }) });
4211
+ }
4212
+ const GenericDriveExplorer = {
4213
+ Component: Editor
4214
+ };
4215
+ function useAnalyticsQueryWrapper(options) {
4216
+ const { queryFn, ...queryOptions } = options;
4217
+ const store = useAnalyticsStore();
4218
+ const engine = useAnalyticsEngine();
4219
+ return useQuery({
4220
+ ...queryOptions,
4221
+ queryFn: () => {
4222
+ if (!store || !engine) {
4223
+ throw new Error("No analytics store available. Use within an AnalyticsProvider.");
4224
+ }
4225
+ return queryFn({ store, engine });
4226
+ }
4227
+ });
3528
4228
  }
3529
- const useUndoRedoShortcuts = (props) => {
3530
- var _a;
3531
- const { undo: undo2, redo: redo2, canRedo, canUndo } = props;
3532
- const { isMac } = ((_a = window.electronAPI) == null ? void 0 : _a.platformInfo) || {};
3533
- let undoShortcut = "ctrl+z";
3534
- let redoShortcut = "ctrl+y";
3535
- if (isMac) {
3536
- undoShortcut = "mod+z";
3537
- redoShortcut = "mod+shift+z";
3538
- }
3539
- useHotkeys(
3540
- undoShortcut,
3541
- (event) => {
3542
- event.preventDefault();
3543
- if (canUndo) {
3544
- undo2();
4229
+ function useAnalyticsQuery(query, options) {
4230
+ const store = useAnalyticsStore();
4231
+ const { data: querySources } = useQuerySources(query);
4232
+ const queryClient = useQueryClient();
4233
+ const subscriptions = useRef([]);
4234
+ const result = useAnalyticsQueryWrapper({
4235
+ queryKey: ["analytics", "query", query],
4236
+ queryFn: ({ engine }) => engine.execute(query),
4237
+ ...options
4238
+ });
4239
+ useEffect(() => {
4240
+ if (!(querySources == null ? void 0 : querySources.length) || !store) {
4241
+ return;
4242
+ }
4243
+ querySources.forEach((source) => {
4244
+ const unsub = store.subscribeToSource(source, () => {
4245
+ return queryClient.invalidateQueries({
4246
+ queryKey: ["analytics", "query", query]
4247
+ });
4248
+ });
4249
+ subscriptions.current.push(unsub);
4250
+ });
4251
+ return () => {
4252
+ subscriptions.current.forEach((unsub) => unsub());
4253
+ subscriptions.current = [];
4254
+ };
4255
+ }, [querySources]);
4256
+ return result;
4257
+ }
4258
+ function useMatchingSeries(query, options) {
4259
+ const result = useAnalyticsQueryWrapper({
4260
+ queryKey: ["analytics", "matchingSeries", query],
4261
+ queryFn: ({ store }) => store.getMatchingSeries(query),
4262
+ ...options
4263
+ });
4264
+ return result;
4265
+ }
4266
+ function useQuerySources(query, options) {
4267
+ const { data: matchingSeries } = useMatchingSeries(query);
4268
+ return useQuery({
4269
+ queryKey: ["analytics", "sources", query],
4270
+ queryFn: () => {
4271
+ if (!(matchingSeries == null ? void 0 : matchingSeries.length)) {
4272
+ return [];
3545
4273
  }
4274
+ const uniqueSources = [
4275
+ ...new Set(matchingSeries.map((s) => s.source.toString()))
4276
+ ];
4277
+ return uniqueSources.map((source) => AnalyticsPath.fromString(source));
3546
4278
  },
3547
- {},
3548
- [canUndo, undo2]
3549
- );
3550
- useHotkeys(
3551
- redoShortcut,
3552
- (event) => {
3553
- event.preventDefault();
3554
- if (canRedo) {
3555
- redo2();
3556
- }
4279
+ enabled: !!matchingSeries,
4280
+ ...options
4281
+ });
4282
+ }
4283
+ const getBarSize = (value) => {
4284
+ if (value <= 0)
4285
+ return 0;
4286
+ if (value > 0 && value <= 50)
4287
+ return 1;
4288
+ if (value > 50 && value <= 100)
4289
+ return 2;
4290
+ if (value > 100 && value <= 250)
4291
+ return 3;
4292
+ return 4;
4293
+ };
4294
+ const useTimelineItems = (documentId, startTimestamp) => {
4295
+ const start = startTimestamp ? DateTime.fromISO(startTimestamp) : DateTime.now().startOf("day");
4296
+ const { data: diffResult, isLoading } = useAnalyticsQuery({
4297
+ start,
4298
+ end: DateTime.now().endOf("day"),
4299
+ granularity: AnalyticsGranularity.Hourly,
4300
+ metrics: ["Count"],
4301
+ select: {
4302
+ changes: [AnalyticsPath.fromString(`changes`)],
4303
+ document: [AnalyticsPath.fromString(`document/${documentId}`)]
3557
4304
  },
3558
- {},
3559
- [canRedo, redo2]
3560
- );
4305
+ lod: {
4306
+ changes: 2
4307
+ },
4308
+ currency: AnalyticsPath.fromString("")
4309
+ });
4310
+ const mappedResult = useMemo(() => {
4311
+ if (!diffResult)
4312
+ return [];
4313
+ return diffResult.sort((a, b) => {
4314
+ const aDate = new Date(a.start);
4315
+ const bDate = new Date(b.start);
4316
+ return aDate.getTime() - bDate.getTime();
4317
+ }).filter((result) => {
4318
+ return result.rows.every((row) => row.value > 0);
4319
+ }).map((result) => {
4320
+ const { additions, deletions } = result.rows.reduce((acc, row) => {
4321
+ if (row.dimensions.changes.path === "changes/add") {
4322
+ acc.additions += row.value;
4323
+ } else if (row.dimensions.changes.path === "changes/remove") {
4324
+ acc.deletions += row.value;
4325
+ }
4326
+ return acc;
4327
+ }, { additions: 0, deletions: 0 });
4328
+ const startDate = new Date(result.start);
4329
+ return {
4330
+ id: startDate.toISOString(),
4331
+ type: "bar",
4332
+ addSize: getBarSize(additions),
4333
+ delSize: getBarSize(deletions),
4334
+ additions,
4335
+ deletions,
4336
+ timestamp: startDate.toISOString(),
4337
+ startDate,
4338
+ endDate: new Date(result.end),
4339
+ revision: 0
4340
+ };
4341
+ });
4342
+ }, [diffResult]);
4343
+ const resultWithDividers = useMemo(() => {
4344
+ if (!mappedResult.length)
4345
+ return [];
4346
+ const result = [];
4347
+ mappedResult.forEach((item, index) => {
4348
+ result.push(item);
4349
+ if (index < mappedResult.length - 1) {
4350
+ const currentDate = new Date(item.startDate);
4351
+ const nextDate = new Date(mappedResult[index + 1].startDate);
4352
+ const currentHour = currentDate.getHours();
4353
+ const nextHour = nextDate.getHours();
4354
+ const currentDay = currentDate.toDateString();
4355
+ const nextDay = nextDate.toDateString();
4356
+ if (currentDay !== nextDay || currentDay === nextDay && Math.abs(nextHour - currentHour) > 1) {
4357
+ result.push({
4358
+ id: `divider-${item.id}-${mappedResult[index + 1].id}`,
4359
+ type: "divider",
4360
+ revision: 0
4361
+ });
4362
+ }
4363
+ }
4364
+ });
4365
+ return result;
4366
+ }, [mappedResult]);
4367
+ return {
4368
+ isLoading,
4369
+ data: resultWithDividers
4370
+ };
4371
+ };
4372
+ const getRevisionFromDate = (startDate, endDate, operations = []) => {
4373
+ if (!startDate || !endDate)
4374
+ return 0;
4375
+ const operation = operations.find((operation2) => {
4376
+ const operationDate = new Date(operation2.timestamp);
4377
+ return operationDate >= startDate && operationDate <= endDate;
4378
+ });
4379
+ return operation ? operation.index : 0;
3561
4380
  };
3562
4381
  function EditorLoader(props) {
3563
4382
  const [showLoading, setShowLoading] = useState(false);
@@ -3586,9 +4405,11 @@ const DocumentEditor = (props) => {
3586
4405
  onChange,
3587
4406
  onExport,
3588
4407
  onAddOperation,
4408
+ onGetDocumentRevision,
3589
4409
  onOpenSwitchboardLink
3590
4410
  } = props;
3591
4411
  const documentId = fileNodeDocument == null ? void 0 : fileNodeDocument.documentId;
4412
+ const [selectedTimelineItem, setSelectedTimelineItem] = useState(null);
3592
4413
  const [revisionHistoryVisible, setRevisionHistoryVisible] = useState(false);
3593
4414
  const theme = useAtomValue(themeAtom);
3594
4415
  const user = useUser() || void 0;
@@ -3614,6 +4435,10 @@ const DocumentEditor = (props) => {
3614
4435
  [theme, user]
3615
4436
  );
3616
4437
  const userPermissions = useUserPermissions$1();
4438
+ const timelineItems = useTimelineItems(
4439
+ documentId,
4440
+ initialDocument == null ? void 0 : initialDocument.created
4441
+ );
3617
4442
  const currentDocument = useRef({ ...fileNodeDocument, document: document2 });
3618
4443
  useEffect(() => {
3619
4444
  var _a;
@@ -3790,7 +4615,8 @@ const DocumentEditor = (props) => {
3790
4615
  const {
3791
4616
  disableExternalControls,
3792
4617
  documentToolbarEnabled,
3793
- showSwitchboardLink
4618
+ showSwitchboardLink,
4619
+ timelineEnabled
3794
4620
  } = editor.config || {};
3795
4621
  const handleSwitchboardLinkClick = showSwitchboardLink !== false ? onOpenSwitchboardLink : void 0;
3796
4622
  return /* @__PURE__ */ jsxs("div", { className: "relative h-full", id: "document-editor-context", children: [
@@ -3801,7 +4627,10 @@ const DocumentEditor = (props) => {
3801
4627
  onExport,
3802
4628
  onShowRevisionHistory: showRevisionHistory,
3803
4629
  title: fileNodeDocument.name || document2.name,
3804
- onSwitchboardLinkClick: handleSwitchboardLinkClick
4630
+ onSwitchboardLinkClick: handleSwitchboardLinkClick,
4631
+ timelineButtonVisible: timelineEnabled,
4632
+ timelineItems: timelineItems.data,
4633
+ onTimelineItemClick: setSelectedTimelineItem
3805
4634
  }
3806
4635
  ),
3807
4636
  !disableExternalControls && /* @__PURE__ */ jsxs("div", { className: "mb-4 flex justify-end gap-10", children: [
@@ -3831,7 +4660,16 @@ const DocumentEditor = (props) => {
3831
4660
  EditorComponent,
3832
4661
  {
3833
4662
  error,
3834
- context,
4663
+ context: {
4664
+ ...context,
4665
+ getDocumentRevision: onGetDocumentRevision,
4666
+ readMode: !!selectedTimelineItem,
4667
+ selectedTimelineRevision: getRevisionFromDate(
4668
+ selectedTimelineItem == null ? void 0 : selectedTimelineItem.startDate,
4669
+ selectedTimelineItem == null ? void 0 : selectedTimelineItem.endDate,
4670
+ document2.operations.global
4671
+ )
4672
+ },
3835
4673
  document: document2,
3836
4674
  documentNodeName: fileNodeDocument.name,
3837
4675
  dispatch,
@@ -3857,18 +4695,22 @@ function DocumentEditorContainer() {
3857
4695
  const { t } = useTranslation();
3858
4696
  const { showModal } = useModal();
3859
4697
  const {
3860
- selectedNode,
3861
- selectedParentNode,
3862
- isRemoteDrive,
3863
4698
  selectedDocument,
3864
4699
  fileNodeDocument,
3865
- setSelectedNode,
3866
4700
  setSelectedDocument,
3867
- openSwitchboardLink,
3868
- addOperationToSelectedDocument,
3869
- renameNode,
3870
- getDocumentModelModule
3871
- } = useUiNodes();
4701
+ addOperationToSelectedDocument
4702
+ } = useFileNodeDocument();
4703
+ const { renameNode } = useDocumentDriveServer();
4704
+ const {
4705
+ selectedNode,
4706
+ selectedDriveNode,
4707
+ selectedParentNode,
4708
+ setSelectedNode
4709
+ } = useUiNodesContext();
4710
+ const { isRemoteDrive } = useDocumentDriveById(selectedDriveNode == null ? void 0 : selectedDriveNode.id);
4711
+ const openSwitchboardLink = useOpenSwitchboardLink(selectedDriveNode == null ? void 0 : selectedDriveNode.id);
4712
+ const getDocumentModelModule = useGetDocumentModelModule();
4713
+ const getDocument = useGetDocument();
3872
4714
  const handleAddOperationToSelectedDocument = useCallback(
3873
4715
  async (operation) => {
3874
4716
  if (!selectedDocument) {
@@ -3931,6 +4773,20 @@ function DocumentEditorContainer() {
3931
4773
  },
3932
4774
  [getDocumentModelModule, showModal, t]
3933
4775
  );
4776
+ const onGetDocumentRevision = useCallback(
4777
+ (options) => {
4778
+ if (!selectedNode) {
4779
+ console.error("No selected node");
4780
+ return Promise.reject(new Error("No selected node"));
4781
+ }
4782
+ return getDocument(
4783
+ selectedNode.driveId,
4784
+ selectedNode.id,
4785
+ options
4786
+ );
4787
+ },
4788
+ [getDocument, selectedNode]
4789
+ );
3934
4790
  const onExport = useCallback(() => {
3935
4791
  if (selectedDocument) {
3936
4792
  return exportDocument(selectedDocument);
@@ -3952,6 +4808,7 @@ function DocumentEditorContainer() {
3952
4808
  onChange: onDocumentChangeHandler,
3953
4809
  onClose,
3954
4810
  onExport,
4811
+ onGetDocumentRevision,
3955
4812
  onAddOperation: handleAddOperationToSelectedDocument,
3956
4813
  onOpenSwitchboardLink
3957
4814
  }
@@ -3960,165 +4817,6 @@ function DocumentEditorContainer() {
3960
4817
  fileNodeDocument.documentId
3961
4818
  );
3962
4819
  }
3963
- function getDocumentSpec(doc) {
3964
- if ("documentModelState" in doc) {
3965
- return doc.documentModelState;
3966
- }
3967
- return doc.documentModel;
3968
- }
3969
- const CreateDocument = ({ documentModels, createDocument }) => {
3970
- return jsxs("div", { className: "px-6", children: [jsx("h3", { className: "mb-3 mt-4 text-xl font-bold text-gray-600", children: "New document" }), jsx("div", { className: "flex w-full flex-wrap gap-4", children: documentModels == null ? void 0 : documentModels.map((doc) => {
3971
- const spec = getDocumentSpec(doc);
3972
- return jsx(Button, { color: "light", "aria-details": spec.description, onClick: () => createDocument(doc), children: jsx("span", { className: "text-sm", children: spec.name }) }, spec.id);
3973
- }) })] });
3974
- };
3975
- function sortUiNodesByName(a, b) {
3976
- return a.name.localeCompare(b.name);
3977
- }
3978
- const GAP = 8;
3979
- const ITEM_WIDTH = 256;
3980
- const ITEM_HEIGHT = 48;
3981
- const USED_SPACE = 420;
3982
- function FileContentView(props) {
3983
- const parentRef = useRef(null);
3984
- const { t } = useTranslation();
3985
- const windowSize = useWindowSize();
3986
- const { fileNodes, ...fileProps } = props;
3987
- const availableWidth = windowSize.innerWidth - USED_SPACE;
3988
- const columnCount = Math.floor(availableWidth / (ITEM_WIDTH + GAP)) || 1;
3989
- const rowCount = Math.ceil(fileNodes.length / columnCount);
3990
- const rowVirtualizer = useVirtualizer({
3991
- count: rowCount,
3992
- getScrollElement: () => parentRef.current,
3993
- estimateSize: (index) => {
3994
- if (index > 0) {
3995
- return ITEM_HEIGHT + GAP;
3996
- }
3997
- return ITEM_HEIGHT;
3998
- },
3999
- overscan: 5
4000
- });
4001
- const columnVirtualizer = useVirtualizer({
4002
- horizontal: true,
4003
- count: columnCount,
4004
- getScrollElement: () => parentRef.current,
4005
- estimateSize: (index) => {
4006
- if (index > 0) {
4007
- return ITEM_WIDTH + GAP;
4008
- }
4009
- return ITEM_WIDTH;
4010
- },
4011
- overscan: 5
4012
- });
4013
- const getItemIndex = (rowIndex, columnIndex) => rowIndex * columnCount + columnIndex;
4014
- const getItem = (rowIndex, columnIndex) => {
4015
- const index = getItemIndex(rowIndex, columnIndex);
4016
- return fileNodes[index] || null;
4017
- };
4018
- if (fileNodes.length === 0) {
4019
- return jsx("div", { className: "mb-8 text-sm text-gray-400", children: t("folderView.sections.documents.empty", {
4020
- defaultValue: "No documents or files 📄"
4021
- }) });
4022
- }
4023
- const renderItem = (rowIndex, columnIndex) => {
4024
- const fileNode = getItem(rowIndex, columnIndex);
4025
- if (!fileNode) {
4026
- return null;
4027
- }
4028
- return jsx("div", { style: {
4029
- marginLeft: columnIndex === 0 ? 0 : GAP
4030
- }, children: jsx(FileItem, { uiNode: fileNode, ...fileProps }, fileNode.id) });
4031
- };
4032
- return jsx("div", { ref: parentRef, style: {
4033
- height: `400px`,
4034
- width: `100%`,
4035
- overflow: "auto"
4036
- }, children: jsx("div", { style: {
4037
- height: `${rowVirtualizer.getTotalSize()}px`,
4038
- width: `${columnVirtualizer.getTotalSize()}px`,
4039
- position: "relative"
4040
- }, children: rowVirtualizer.getVirtualItems().map((virtualRow) => jsx(React__default.Fragment, { children: columnVirtualizer.getVirtualItems().map((virtualColumn) => jsx("div", { style: {
4041
- position: "absolute",
4042
- top: 0,
4043
- left: 0,
4044
- marginTop: virtualRow.index === 0 ? 0 : GAP,
4045
- width: `${virtualColumn.size}px`,
4046
- height: `${virtualRow.size}px`,
4047
- transform: `translateX(${virtualColumn.start}px) translateY(${virtualRow.start}px)`
4048
- }, children: renderItem(virtualRow.index, virtualColumn.index) }, virtualColumn.key)) }, virtualRow.key)) }) });
4049
- }
4050
- function FolderView(props) {
4051
- const { node, className, isDropTarget, containerProps, ...nodeProps } = props;
4052
- const { t } = useTranslation();
4053
- const folderNodes = node.children.filter((node2) => node2.kind === FOLDER).sort(sortUiNodesByName);
4054
- const fileNodes = node.children.filter((node2) => node2.kind === FILE).sort(sortUiNodesByName);
4055
- const folderCallbacks = {
4056
- onSelectNode: (node2) => nodeProps.onSelectNode(node2),
4057
- onRenameNode: (name, node2) => nodeProps.onRenameNode(name, node2),
4058
- onDuplicateNode: (node2) => nodeProps.onDuplicateNode(node2),
4059
- onDeleteNode: (node2) => nodeProps.onDeleteNode(node2)
4060
- };
4061
- const fileCallbacks = {
4062
- onSelectNode: (node2) => nodeProps.onSelectNode(node2),
4063
- onRenameNode: (name, node2) => nodeProps.onRenameNode(name, node2),
4064
- onDuplicateNode: (node2) => nodeProps.onDuplicateNode(node2),
4065
- onDeleteNode: (node2) => nodeProps.onDeleteNode(node2)
4066
- };
4067
- const baseNodeCallbacks = {
4068
- onAddFile: async (file, parentNode) => {
4069
- await nodeProps.onAddFile(file, parentNode);
4070
- },
4071
- onCopyNode: async (uiNode, targetNode) => {
4072
- await nodeProps.onCopyNode(uiNode, targetNode);
4073
- },
4074
- onMoveNode: async (uiNode, targetNode) => {
4075
- await nodeProps.onMoveNode(uiNode, targetNode);
4076
- }
4077
- };
4078
- return jsxs("div", { className: twMerge("rounded-md border-2 border-transparent p-2", isDropTarget && "border-dashed border-blue-100", className), ...containerProps, children: [jsx(DriveLayout.ContentSection, { title: t("folderView.sections.folders.title", {
4079
- defaultValue: "Folders"
4080
- }), className: "mb-4", children: folderNodes.length > 0 ? folderNodes.map((folderNode) => jsx(FolderItem, { uiNode: folderNode, ...baseNodeCallbacks, ...folderCallbacks, isAllowedToCreateDocuments: nodeProps.isAllowedToCreateDocuments }, folderNode.id)) : jsx("div", { className: "mb-8 text-sm text-gray-400", children: t("folderView.sections.folders.empty", {
4081
- defaultValue: "No documents or files 📄"
4082
- }) }) }), jsx(DriveLayout.ContentSection, { title: t("folderView.sections.documents.title", {
4083
- defaultValue: "Documents and files"
4084
- }), children: jsx("div", { className: twMerge("w-full", fileNodes.length > 0 ? "min-h-[400px]" : "min-h-14"), children: jsx(FileContentView, { fileNodes, ...fileCallbacks, isAllowedToCreateDocuments: nodeProps.isAllowedToCreateDocuments }) }) })] });
4085
- }
4086
- function BaseEditor(props) {
4087
- const { document: document2, dispatch, className, children } = props;
4088
- const { state: { global: { id: driveId } } } = document2;
4089
- const { showSearchBar, isAllowedToCreateDocuments, documentModels, showCreateDocumentModal } = useDriveContext();
4090
- const { driveNodes, selectedNode, selectedNodePath, getNodeById, setSelectedNode } = useUiNodesContext();
4091
- const driveNode = useMemo(() => driveNodes.find((n) => n.id === driveId), [driveNodes, driveId]);
4092
- const { addDocument, addFile, addFolder: addFolder2, renameNode, deleteNode: deleteNode2, moveNode: moveNode2, copyNode: copyNode2, duplicateNode } = useDriveActionsWithUiNodes(document2, dispatch);
4093
- const onCreateDocument = useCallback(async (documentModel) => {
4094
- const { name } = await showCreateDocumentModal(documentModel);
4095
- const document3 = documentModel.utils.createDocument();
4096
- await addDocument(name, documentModel.documentModel.name, document3, selectedNode == null ? void 0 : selectedNode.id);
4097
- }, [addDocument, showCreateDocumentModal, selectedNode == null ? void 0 : selectedNode.id]);
4098
- const { isDropTarget, dropProps } = useDrop({
4099
- uiNode: selectedNode,
4100
- onAddFile: addFile,
4101
- onCopyNode: copyNode2,
4102
- onMoveNode: moveNode2
4103
- });
4104
- const { breadcrumbs, onBreadcrumbSelected } = useBreadcrumbs({
4105
- selectedNodePath,
4106
- getNodeById,
4107
- setSelectedNode
4108
- });
4109
- if (!driveNode) {
4110
- return jsx("div", { children: "Drive not found" });
4111
- } else if ((selectedNode == null ? void 0 : selectedNode.kind) === FILE$1) {
4112
- return jsx(Fragment$1, {});
4113
- }
4114
- return jsxs(DriveLayout, { className, children: [children, jsxs(DriveLayout.Header, { children: [jsx(Breadcrumbs, { breadcrumbs, createEnabled: isAllowedToCreateDocuments, onCreate: addFolder2, onBreadcrumbSelected }), showSearchBar && jsx(SearchBar, {})] }), jsx(DriveLayout.Content, { children: jsx(FolderView, { node: selectedNode || driveNode, onSelectNode: setSelectedNode, onRenameNode: renameNode, onDuplicateNode: duplicateNode, onDeleteNode: deleteNode2, onAddFile: addFile, onCopyNode: copyNode2, onMoveNode: moveNode2, isDropTarget, isAllowedToCreateDocuments }) }), jsx(DriveLayout.Footer, { children: isAllowedToCreateDocuments && jsx(CreateDocument, { documentModels, createDocument: onCreateDocument }) })] });
4115
- }
4116
- function Editor(props) {
4117
- return jsx(DriveContextProvider, { value: props.context, children: jsx(BaseEditor, { ...props }) });
4118
- }
4119
- const GenericDriveExplorer = {
4120
- Component: Editor
4121
- };
4122
4820
  function useDocumentsState(args) {
4123
4821
  const { reactor, driveId, documentIds, options } = args;
4124
4822
  const [statesByDocumentId, setStatesByDocumentId] = useState({});
@@ -4268,10 +4966,10 @@ function DriveEditorContainer() {
4268
4966
  selectedNode,
4269
4967
  selectedParentNode
4270
4968
  } = useUiNodesContext();
4271
- const { addOperationToSelectedDrive } = useUiNodes();
4969
+ const { addOperationToSelectedDrive } = useFileNodeDocument();
4272
4970
  const documentDrive = useSelectedDocumentDrive();
4273
4971
  const [document2, _dispatch, error] = useDocumentDispatch$1(
4274
- driveDocumentModelModule.reducer,
4972
+ module.reducer,
4275
4973
  documentDrive
4276
4974
  );
4277
4975
  const reactor = useAsyncReactor();
@@ -4305,8 +5003,19 @@ function DriveEditorContainer() {
4305
5003
  const { addFile, addDocument } = useDocumentDriveServer();
4306
5004
  const documentModels = useFilteredDocumentModels();
4307
5005
  const useDriveDocumentState = makeDriveDocumentStateHook(reactor);
5006
+ const getDocument = useGetDocument();
4308
5007
  const getDocumentModelModule = useGetDocumentModelModule();
4309
5008
  const getEditor = useGetEditor();
5009
+ const onGetDocumentRevision = useCallback(
5010
+ (documentId, options) => {
5011
+ if (!selectedNode) {
5012
+ console.error("No selected node");
5013
+ return Promise.reject(new Error("No selected node"));
5014
+ }
5015
+ return getDocument(selectedNode.driveId, documentId, options);
5016
+ },
5017
+ [getDocument, selectedNode]
5018
+ );
4310
5019
  const driveContext = useMemo(
4311
5020
  () => ({
4312
5021
  showSearchBar: false,
@@ -4350,6 +5059,8 @@ function DriveEditorContainer() {
4350
5059
  context: {
4351
5060
  ...editorProps.context,
4352
5061
  ...driveContext,
5062
+ analyticsDatabaseName: connectConfig.analyticsDatabaseName,
5063
+ getDocumentRevision: onGetDocumentRevision,
4353
5064
  getDocumentModelModule,
4354
5065
  getEditor
4355
5066
  },
@@ -4367,12 +5078,13 @@ function Content() {
4367
5078
  const navigate = useNavigate();
4368
5079
  const { driveId } = useParams();
4369
5080
  const [documentDrives, , , status] = useDocumentDrives$1();
4370
- const uiNodes = useUiNodes();
4371
- const { fileNodeDocument, selectedDriveNode, selectedNode, addFile } = uiNodes;
5081
+ const { selectedDriveNode, selectedNode } = useUiNodesContext();
5082
+ const { addFile } = useDocumentDriveServer();
5083
+ const { fileNodeDocument } = useFileNodeDocument();
4372
5084
  useEffect(() => {
4373
5085
  var _a;
4374
5086
  return (_a = window.electronAPI) == null ? void 0 : _a.handleFileOpen(async (file) => {
4375
- if (!selectedDriveNode || (selectedNode == null ? void 0 : selectedNode.kind) !== FILE) {
5087
+ if (!selectedDriveNode || (selectedNode == null ? void 0 : selectedNode.kind) !== FILE$1) {
4376
5088
  return;
4377
5089
  }
4378
5090
  await addFile(
@@ -4385,7 +5097,7 @@ function Content() {
4385
5097
  }, [selectedDriveNode, selectedNode, addFile]);
4386
5098
  useEffect(() => {
4387
5099
  if ((status === "LOADED" || status === "ERROR") && !documentDrives.find(
4388
- (d) => d.state.global.id === driveId || d.state.global.slug === driveId || d.state.global.name === driveId
5100
+ (d) => d.id === driveId || d.slug === driveId || d.state.global.name === driveId
4389
5101
  )) {
4390
5102
  toast(/* @__PURE__ */ jsxs("p", { children: [
4391
5103
  "Drive ",
@@ -4400,3 +5112,4 @@ function Content() {
4400
5112
  export {
4401
5113
  Content as default
4402
5114
  };
5115
+ //# sourceMappingURL=content-ChtzAUHq.js.map