@powerhousedao/connect 1.0.17-dev.2 → 1.0.17-dev.3

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 (34) hide show
  1. package/dist/assets/{app-CayLrjAy.js → app-BdUxze73.js} +735 -117
  2. package/dist/assets/{app-CayLrjAy.js.map → app-BdUxze73.js.map} +1 -1
  3. package/dist/assets/{app-CDOWlDp2.css → app-b1jkE5Vo.css} +115 -0
  4. package/dist/assets/{app-loader-DiF8OhX3.css → app-loader-B74nopv_.css} +130 -0
  5. package/dist/assets/{app-loader-Ej0QwX6c.js → app-loader-BVN3aSFC.js} +9 -5
  6. package/dist/assets/app-loader-BVN3aSFC.js.map +1 -0
  7. package/dist/assets/{browser-BfB4Waw5.js → browser-CzAjKVis.js} +4 -5
  8. package/dist/assets/{browser-BfB4Waw5.js.map → browser-CzAjKVis.js.map} +1 -1
  9. package/dist/assets/{ccip-DBZNMhmz.js → ccip-CbFWJKu1.js} +4 -4
  10. package/dist/assets/{ccip-DBZNMhmz.js.map → ccip-CbFWJKu1.js.map} +1 -1
  11. package/dist/assets/{content-DnXO71l4.js → content-C1JKApwb.js} +1010 -374
  12. package/dist/assets/content-C1JKApwb.js.map +1 -0
  13. package/dist/assets/{index-CCSrpy8s.js → index-D1myKexe.js} +4 -4
  14. package/dist/assets/{index-CCSrpy8s.js.map → index-D1myKexe.js.map} +1 -1
  15. package/dist/assets/{index-BbMglwKh.js → index-DtwSBKZB.js} +14 -14
  16. package/dist/assets/index-DtwSBKZB.js.map +1 -0
  17. package/dist/assets/{index-CU3_V46J.js → index-qIrGoURI.js} +4 -4
  18. package/dist/assets/{index-CU3_V46J.js.map → index-qIrGoURI.js.map} +1 -1
  19. package/dist/assets/{main.D0KZAQpb.js → main.DCtGjfg0.js} +2 -2
  20. package/dist/assets/{main.D0KZAQpb.js.map → main.DCtGjfg0.js.map} +1 -1
  21. package/dist/assets/reactor-analytics-BUR_Njds.js +15 -0
  22. package/dist/assets/reactor-analytics-BUR_Njds.js.map +1 -0
  23. package/dist/assets/{router-BF9pgdRU.js → router-LLmzUc4B.js} +8 -7
  24. package/dist/assets/{router-BF9pgdRU.js.map → router-LLmzUc4B.js.map} +1 -1
  25. package/dist/index.html +1 -1
  26. package/dist/vite-envs.sh +1 -1
  27. package/package.json +8 -8
  28. package/dist/assets/AnalyticsProfiler-DD1sT8NE.js +0 -22
  29. package/dist/assets/AnalyticsProfiler-DD1sT8NE.js.map +0 -1
  30. package/dist/assets/app-loader-Ej0QwX6c.js.map +0 -1
  31. package/dist/assets/content-DnXO71l4.js.map +0 -1
  32. package/dist/assets/index-BbMglwKh.js.map +0 -1
  33. package/dist/assets/reactor-analytics-BioFquA0.js +0 -605
  34. package/dist/assets/reactor-analytics-BioFquA0.js.map +0 -1
@@ -1,11 +1,11 @@
1
1
  import { jsx, jsxs, Fragment as Fragment$1 } from "react/jsx-runtime";
2
- import { t as twMerge, B as Button, b3 as mergeClassNameProps, O as ERROR, b4 as MISSING, N as CONFLICT, L as SUCCESS, b5 as SYNCING, b6 as INITIAL_SYNC, b7 as ConnectDropdownMenu, b8 as useOnClickOutside, b9 as useEventListener, ba as useCopyToClipboard, bb as Select, bc as Provider, bd as Root3, be as Trigger, bf as Portal, bg as Content2, bh as validateInitialState, bi as validateStateSchemaName, bj as validateModules, bk as useDocumentDrives, U as useUiNodesContext, bl as useDriveContext, bm as FILE$1, V as useUnwrappedReactor, bn as useConnectDid, bo as useConnectCrypto, F as useTranslation, X as useModal, bp as useAtomValue, bq as themeAtom, br as useUser, bs as useUserPermissions$1, G as useUiNodes, bt as exportFile, bu as useGetDocumentModelModule, bv as addActionContext$1, bw as signOperation$1, o as useDocumentDriveServer, bx as useHotkeys, by as useGetEditor, bz as isSameDocument, C as useNavigate, bA as DriveLayout, bB as DriveContextProvider, bC as SearchBar, J as useAsyncReactor, bD as useFilteredDocumentModels, bE as useDriveEditor, bF as useDocumentDriveById, T as useParams, H as useDocumentDrives$1, M as toast } from "./app-CayLrjAy.js";
2
+ import { t as twMerge, B as Button, b0 as mergeClassNameProps, O as ERROR, b1 as MISSING, N as CONFLICT, L as SUCCESS, b2 as SYNCING, b3 as INITIAL_SYNC, b4 as ConnectDropdownMenu, b5 as useOnClickOutside, b6 as useEventListener, b7 as useCopyToClipboard, b8 as Select, b9 as Provider, ba as Root3, bb as Trigger, bc as Portal, bd as Content2, be as validateInitialState, bf as validateStateSchemaName, bg as validateModules, bh as useDocumentDrives, U as useUiNodesContext, bi as useDriveContext, bj as FILE$1, V as useUnwrappedReactor, bk as useConnectDid, bl as useConnectCrypto, F as useTranslation, X as useModal, bm as useAtomValue, bn as themeAtom, bo as useUser, bp as useUserPermissions$1, G as useUiNodes, bq as exportFile, br as useGetDocumentModelModule, bs as addActionContext$1, bt as signOperation$1, o as useDocumentDriveServer, bu as useHotkeys, bv as DriveLayout, bw as DriveContextProvider, bx as SearchBar, by as useAnalyticsEngine, bz as useAnalyticsStore, u as useQueryClient, aQ as AnalyticsPath, aP as DateTime, bA as AnalyticsGranularity, bB as useGetEditor, bC as isSameDocument, C as useNavigate, J as useAsyncReactor, bD as useFilteredDocumentModels, bE as useDriveEditor, bF as useDocumentDriveById, T as useParams, H as useDocumentDrives$1, M as toast } from "./app-BdUxze73.js";
3
3
  import * as React from "react";
4
4
  import React__default, { useState, useCallback, useMemo, useEffect, Fragment, useRef, useLayoutEffect, memo as memo$1, createElement, useSyncExternalStore, Suspense } from "react";
5
- import { $ as Icon, bY as getDimensions, bZ as READ, b_ as nodeOptionsMap, b$ as defaultFileOptions, c0 as DELETE, c1 as RENAME, c2 as WRITE, c3 as DUPLICATE, c4 as defaultFolderOptions, c5 as garbageCollect, c6 as sortOperations, c7 as UI_NODE, aQ as DRIVE, aA as FILE, ae as buildSignedOperation, aW as generateId$1, ax as isFolderNode, aC as generateNodesCopy, aD as copyNode, aB as moveNode, az as updateNode, ay as deleteNode, as as generateAddNodeAction, at as isFileNode, aw as addFolder, c8 as undo, c9 as redo, _ as logger, ca as useDocumentDispatch$1, aR as FOLDER, am as driveDocumentModelModule } from "./app-loader-Ej0QwX6c.js";
6
- import { E as ENSAvatar, a as ErrorBoundary } from "./router-BF9pgdRU.js";
5
+ import { $ as Icon, bY as getDimensions, bZ as READ, b_ as nodeOptionsMap, b$ as defaultFileOptions, c0 as DELETE, c1 as RENAME, c2 as WRITE, c3 as DUPLICATE, c4 as defaultFolderOptions, c5 as garbageCollect, c6 as sortOperations, c7 as UI_NODE, aQ as DRIVE, aA as FILE, ae as buildSignedOperation, aW as generateId$1, ax as isFolderNode, aC as generateNodesCopy, aD as copyNode, aB as moveNode, az as updateNode, ay as deleteNode, as as generateAddNodeAction, at as isFileNode, aw as addFolder, c8 as undo, c9 as redo, _ as logger, aR as FOLDER, ca as useDocumentDispatch$1, am as driveDocumentModelModule } from "./app-loader-BVN3aSFC.js";
6
+ import { E as ENSAvatar, u as useQuery, a as ErrorBoundary } from "./router-LLmzUc4B.js";
7
7
  import { flushSync } from "react-dom";
8
- import "./main.D0KZAQpb.js";
8
+ import "./main.DCtGjfg0.js";
9
9
  const PaginationButton = ({ active = false, ...props }) => {
10
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");
11
11
  return jsx(Button, { color: "light", size: "small", ...mergeClassNameProps(props, className), children: props.children });
@@ -152,131 +152,12 @@ function DefaultEditorLoader(props) {
152
152
  const { message: message2 = "Loading editor", ...divProps } = props;
153
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, {})] }) });
154
154
  }
155
- const DocumentToolbar = (props) => {
156
- const { undo: undo2, canUndo, redo: redo2, canRedo, title, onClose, onExport, className, onShowRevisionHistory, onSwitchboardLinkClick, onShowTimeline } = props;
157
- const isUndoDisabled = !canUndo || !undo2;
158
- const isRedoDisabled = !canRedo || !redo2;
159
- const isExportDisabled = !onExport;
160
- const isSwitchboardLinkDisabled = !onSwitchboardLinkClick;
161
- const isRevisionHistoryDisabled = !onShowRevisionHistory;
162
- const isTimelineDisabled = !onShowTimeline;
163
- 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" }) })] })] });
164
- };
165
- const syncIcons = {
166
- SYNCING: "Syncing",
167
- SUCCESS: "Synced",
168
- CONFLICT: "Error",
169
- MISSING: "Circle",
170
- ERROR: "Error",
171
- INITIAL_SYNC: "Syncing"
172
- };
173
- function SyncStatusIcon(props) {
174
- const { syncStatus, className, overrideSyncIcons = {}, ...iconProps } = props;
175
- const icons = { ...syncIcons, ...overrideSyncIcons };
176
- const syncStatusIcons = {
177
- [INITIAL_SYNC]: jsx(Icon, { size: 16, ...iconProps, className: twMerge("text-blue-900", className), name: icons[INITIAL_SYNC] }),
178
- [SYNCING]: jsx(Icon, { size: 16, ...iconProps, className: twMerge("text-blue-900", className), name: icons[SYNCING] }),
179
- [SUCCESS]: jsx(Icon, { size: 16, ...iconProps, className: twMerge("text-green-900", className), name: icons[SUCCESS] }),
180
- [CONFLICT]: jsx(Icon, { size: 16, ...iconProps, className: twMerge("text-orange-900", className), name: icons[CONFLICT] }),
181
- [MISSING]: jsx(Icon, { size: 16, ...iconProps, className: twMerge("text-red-900", className), name: icons[MISSING] }),
182
- [ERROR]: jsx(Icon, { size: 16, ...iconProps, className: twMerge("text-red-900", className), name: icons[ERROR] })
183
- };
184
- return syncStatusIcons[syncStatus];
185
- }
186
- function FileItem(props) {
187
- const { uiNode, className, customDocumentIconSrc, onSelectNode, onRenameNode, onDuplicateNode, onDeleteNode, isAllowedToCreateDocuments } = props;
188
- const [mode, setMode] = useState(READ);
189
- const [isDropdownMenuOpen, setIsDropdownMenuOpen] = useState(false);
190
- const { dragProps } = useDrag({ uiNode });
191
- const isReadMode = mode === READ;
192
- const dropdownMenuHandlers = {
193
- [DUPLICATE]: () => onDuplicateNode(uiNode),
194
- [RENAME]: () => setMode(WRITE),
195
- [DELETE]: () => onDeleteNode(uiNode)
196
- };
197
- const dropdownMenuOptions = Object.entries(nodeOptionsMap).map(([id, option]) => ({
198
- ...option,
199
- id
200
- })).filter((option) => defaultFileOptions.includes(option.id));
201
- function onSubmit(name) {
202
- onRenameNode(name, uiNode);
203
- setMode(READ);
204
- }
205
- function onCancel() {
206
- setMode(READ);
207
- }
208
- function onClick() {
209
- onSelectNode(uiNode);
210
- }
211
- function onDropdownMenuOptionClick(itemId) {
212
- const handler = dropdownMenuHandlers[itemId];
213
- if (!handler) {
214
- console.error(`No handler found for dropdown menu item: ${itemId}`);
215
- return;
216
- }
217
- handler();
218
- setIsDropdownMenuOpen(false);
219
- }
220
- const iconSrc = getDocumentIconSrc(uiNode.documentType, customDocumentIconSrc);
221
- 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 }) }) })] });
222
- 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);
223
- 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) => {
224
- e.stopPropagation();
225
- setIsDropdownMenuOpen(true);
226
- }, children: jsx(Icon, { className: "text-gray-600", name: "VerticalDots" }) }) }) : null] }) : jsx(NodeInput, { className: "ml-3 flex-1 font-medium", defaultValue: uiNode.name, onCancel, onSubmit });
227
- 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] }) }) });
228
- }
229
- function FolderItem(props) {
230
- const { uiNode, isAllowedToCreateDocuments, className, onRenameNode, onDuplicateNode, onDeleteNode, onSelectNode, onAddFile, onCopyNode, onMoveNode } = props;
231
- const [mode, setMode] = useState(READ);
232
- const [isDropdownMenuOpen, setIsDropdownMenuOpen] = useState(false);
233
- const { dragProps } = useDrag({ ...props, uiNode });
234
- const { isDropTarget, dropProps } = useDrop({
235
- uiNode,
236
- onAddFile,
237
- onCopyNode,
238
- onMoveNode
239
- });
240
- const isReadMode = mode === READ;
241
- function onCancel() {
242
- setMode(READ);
243
- }
244
- function onSubmit(name) {
245
- onRenameNode(name, uiNode);
246
- setMode(READ);
247
- }
248
- function onClick() {
249
- onSelectNode(uiNode);
250
- }
251
- const dropdownMenuHandlers = {
252
- [DUPLICATE]: () => onDuplicateNode(uiNode),
253
- [RENAME]: () => setMode(WRITE),
254
- [DELETE]: () => onDeleteNode(uiNode)
255
- };
256
- const dropdownMenuOptions = Object.entries(nodeOptionsMap).map(([id, option]) => ({
257
- ...option,
258
- id
259
- })).filter((option) => defaultFolderOptions.includes(option.id));
260
- function onDropdownMenuOptionClick(itemId) {
261
- const handler = dropdownMenuHandlers[itemId];
262
- if (!handler) {
263
- console.error(`No handler found for dropdown menu item: ${itemId}`);
264
- return;
265
- }
266
- handler();
267
- setIsDropdownMenuOpen(false);
268
- }
269
- 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 });
270
- 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");
271
- 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: {
272
- SUCCESS: "CheckCircleFill"
273
- }, 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) => {
274
- e.stopPropagation();
275
- setIsDropdownMenuOpen(true);
276
- }, children: jsx(Icon, { className: "text-gray-600", name: "VerticalDots" }) }) }) : null] }) });
277
- }
278
155
  const millisecondsInWeek = 6048e5;
279
156
  const millisecondsInDay = 864e5;
157
+ const millisecondsInMinute = 6e4;
158
+ const millisecondsInHour = 36e5;
159
+ const minutesInMonth = 43200;
160
+ const minutesInDay = 1440;
280
161
  const constructFromSymbol = Symbol.for("constructDateFrom");
281
162
  function constructFrom(date, value) {
282
163
  if (typeof date === "function") return date(value);
@@ -344,7 +225,7 @@ function getTimezoneOffsetInMilliseconds(date) {
344
225
  function normalizeDates(context, ...dates) {
345
226
  const normalize = constructFrom.bind(
346
227
  null,
347
- dates.find((date) => typeof date === "object")
228
+ context || dates.find((date) => typeof date === "object")
348
229
  );
349
230
  return dates.map(normalize);
350
231
  }
@@ -372,12 +253,83 @@ function startOfISOWeekYear(date, options) {
372
253
  fourthOfJanuary.setHours(0, 0, 0, 0);
373
254
  return startOfISOWeek(fourthOfJanuary);
374
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
+ }
375
265
  function isDate(value) {
376
266
  return value instanceof Date || typeof value === "object" && Object.prototype.toString.call(value) === "[object Date]";
377
267
  }
378
268
  function isValid(date) {
379
269
  return !(!isDate(date) && typeof date !== "number" || isNaN(+toDate(date)));
380
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
+ }
381
333
  function startOfYear(date, options) {
382
334
  const date_ = toDate(date, options == null ? void 0 : options.in);
383
335
  date_.setFullYear(date_.getFullYear(), 0, 1);
@@ -447,7 +399,7 @@ const formatDistanceLocale = {
447
399
  other: "almost {{count}} years"
448
400
  }
449
401
  };
450
- const formatDistance = (token, count, options) => {
402
+ const formatDistance$1 = (token, count, options) => {
451
403
  let result;
452
404
  const tokenValue = formatDistanceLocale[token];
453
405
  if (typeof tokenValue === "string") {
@@ -863,7 +815,7 @@ const match = {
863
815
  };
864
816
  const enUS = {
865
817
  code: "en-US",
866
- formatDistance,
818
+ formatDistance: formatDistance$1,
867
819
  formatLong,
868
820
  formatRelative,
869
821
  localize,
@@ -1765,75 +1717,574 @@ function cleanEscapedString(input) {
1765
1717
  }
1766
1718
  return matched[1].replace(doubleQuoteRegExp, "'");
1767
1719
  }
1768
- function NodeInput(props) {
1769
- const { onSubmit, onCancel, defaultValue, className, minLength = 1, ...inputProps } = props;
1770
- const [value, setValue] = useState(defaultValue ?? "");
1771
- const ref = useRef(null);
1772
- useOnClickOutside(ref, handleSubmit);
1773
- useEventListener("keyup", (e) => {
1774
- if (e.key === "Enter") {
1775
- 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
+ }
1776
1759
  }
1777
- if (e.key === "Escape") {
1778
- 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);
1779
1789
  }
1780
- });
1781
- useLayoutEffect(() => {
1782
- setTimeout(() => {
1783
- var _a, _b, _c;
1784
- (_a = ref.current) == null ? void 0 : _a.focus();
1785
- (_b = ref.current) == null ? void 0 : _b.select();
1786
- (_c = ref.current) == null ? void 0 : _c.scroll({ left: 9999 });
1787
- }, 100);
1788
- }, []);
1789
- function handleSubmit() {
1790
- if (value.length >= minLength) {
1791
- 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
+ );
1792
1859
  }
1793
1860
  }
1794
- 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;
1795
1871
  }
1796
- function Branch(props) {
1797
- const { branch = "main" } = props;
1798
- 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
+ };
1799
1884
  }
1800
- function DocId(props) {
1801
- const { docId } = props;
1802
- const [, copy] = useCopyToClipboard();
1803
- function handleCopy(text) {
1804
- return () => {
1805
- copy(text).catch((error) => {
1806
- console.error("Failed to copy!", error);
1807
- });
1808
- };
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;
1809
1907
  }
1810
- 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 })] });
1811
1908
  }
1812
- function Scope(props) {
1813
- const { value, onChange } = props;
1814
- const items = [
1815
- { displayValue: "Global scope", value: "global" },
1816
- { displayValue: "Local scope", value: "local" }
1817
- ];
1818
- 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;
1819
1911
  }
1820
- function Header(props) {
1821
- const { title, docId, scope, onChangeScope, onClose, className, ...divProps } = props;
1822
- 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;
1823
1922
  }
1824
- function memo(getDeps, fn, opts) {
1825
- let deps = opts.initialDeps ?? [];
1826
- let result;
1827
- function memoizedFunction() {
1828
- var _a, _b, _c, _d;
1829
- let depTime;
1830
- if (opts.key && ((_a = opts.debug) == null ? void 0 : _a.call(opts))) depTime = Date.now();
1831
- const newDeps = getDeps();
1832
- const depsChanged = newDeps.length !== deps.length || newDeps.some((dep, index) => deps[index] !== dep);
1833
- if (!depsChanged) {
1834
- 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);
1835
1976
  }
1836
- deps = newDeps;
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;
1985
+ }
1986
+ };
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
+ }
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" }) }) })] });
1997
+ };
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]";
2012
+ }
2013
+ };
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;
2022
+ }
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
+ }
2034
+ };
2035
+ const handleMouseLeave = () => {
2036
+ setOpen(false);
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) }) })] }) })] });
2039
+ };
2040
+ const defaultTimeLineItem = {
2041
+ id: "default",
2042
+ type: "bar",
2043
+ addSize: 0,
2044
+ delSize: 0
2045
+ };
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
+ }
2058
+ };
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);
2097
+ }
2098
+ }, [initialTimelineVisible]);
2099
+ const handleTimelineToggle = () => {
2100
+ if (isTimelineDisabled)
2101
+ return;
2102
+ setShowTimeline(!showTimeline);
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 }) })] });
2105
+ };
2106
+ const syncIcons = {
2107
+ SYNCING: "Syncing",
2108
+ SUCCESS: "Synced",
2109
+ CONFLICT: "Error",
2110
+ MISSING: "Circle",
2111
+ ERROR: "Error",
2112
+ INITIAL_SYNC: "Syncing"
2113
+ };
2114
+ function SyncStatusIcon(props) {
2115
+ const { syncStatus, className, overrideSyncIcons = {}, ...iconProps } = props;
2116
+ const icons = { ...syncIcons, ...overrideSyncIcons };
2117
+ const syncStatusIcons = {
2118
+ [INITIAL_SYNC]: jsx(Icon, { size: 16, ...iconProps, className: twMerge("text-blue-900", className), name: icons[INITIAL_SYNC] }),
2119
+ [SYNCING]: jsx(Icon, { size: 16, ...iconProps, className: twMerge("text-blue-900", className), name: icons[SYNCING] }),
2120
+ [SUCCESS]: jsx(Icon, { size: 16, ...iconProps, className: twMerge("text-green-900", className), name: icons[SUCCESS] }),
2121
+ [CONFLICT]: jsx(Icon, { size: 16, ...iconProps, className: twMerge("text-orange-900", className), name: icons[CONFLICT] }),
2122
+ [MISSING]: jsx(Icon, { size: 16, ...iconProps, className: twMerge("text-red-900", className), name: icons[MISSING] }),
2123
+ [ERROR]: jsx(Icon, { size: 16, ...iconProps, className: twMerge("text-red-900", className), name: icons[ERROR] })
2124
+ };
2125
+ return syncStatusIcons[syncStatus];
2126
+ }
2127
+ function FileItem(props) {
2128
+ const { uiNode, className, customDocumentIconSrc, onSelectNode, onRenameNode, onDuplicateNode, onDeleteNode, isAllowedToCreateDocuments } = props;
2129
+ const [mode, setMode] = useState(READ);
2130
+ const [isDropdownMenuOpen, setIsDropdownMenuOpen] = useState(false);
2131
+ const { dragProps } = useDrag({ uiNode });
2132
+ const isReadMode = mode === READ;
2133
+ const dropdownMenuHandlers = {
2134
+ [DUPLICATE]: () => onDuplicateNode(uiNode),
2135
+ [RENAME]: () => setMode(WRITE),
2136
+ [DELETE]: () => onDeleteNode(uiNode)
2137
+ };
2138
+ const dropdownMenuOptions = Object.entries(nodeOptionsMap).map(([id, option]) => ({
2139
+ ...option,
2140
+ id
2141
+ })).filter((option) => defaultFileOptions.includes(option.id));
2142
+ function onSubmit(name) {
2143
+ onRenameNode(name, uiNode);
2144
+ setMode(READ);
2145
+ }
2146
+ function onCancel() {
2147
+ setMode(READ);
2148
+ }
2149
+ function onClick() {
2150
+ onSelectNode(uiNode);
2151
+ }
2152
+ function onDropdownMenuOptionClick(itemId) {
2153
+ const handler = dropdownMenuHandlers[itemId];
2154
+ if (!handler) {
2155
+ console.error(`No handler found for dropdown menu item: ${itemId}`);
2156
+ return;
2157
+ }
2158
+ handler();
2159
+ setIsDropdownMenuOpen(false);
2160
+ }
2161
+ const iconSrc = getDocumentIconSrc(uiNode.documentType, customDocumentIconSrc);
2162
+ 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 }) }) })] });
2163
+ 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);
2164
+ 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) => {
2165
+ e.stopPropagation();
2166
+ setIsDropdownMenuOpen(true);
2167
+ }, children: jsx(Icon, { className: "text-gray-600", name: "VerticalDots" }) }) }) : null] }) : jsx(NodeInput, { className: "ml-3 flex-1 font-medium", defaultValue: uiNode.name, onCancel, onSubmit });
2168
+ 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] }) }) });
2169
+ }
2170
+ function FolderItem(props) {
2171
+ const { uiNode, isAllowedToCreateDocuments, className, onRenameNode, onDuplicateNode, onDeleteNode, onSelectNode, onAddFile, onCopyNode, onMoveNode } = props;
2172
+ const [mode, setMode] = useState(READ);
2173
+ const [isDropdownMenuOpen, setIsDropdownMenuOpen] = useState(false);
2174
+ const { dragProps } = useDrag({ ...props, uiNode });
2175
+ const { isDropTarget, dropProps } = useDrop({
2176
+ uiNode,
2177
+ onAddFile,
2178
+ onCopyNode,
2179
+ onMoveNode
2180
+ });
2181
+ const isReadMode = mode === READ;
2182
+ function onCancel() {
2183
+ setMode(READ);
2184
+ }
2185
+ function onSubmit(name) {
2186
+ onRenameNode(name, uiNode);
2187
+ setMode(READ);
2188
+ }
2189
+ function onClick() {
2190
+ onSelectNode(uiNode);
2191
+ }
2192
+ const dropdownMenuHandlers = {
2193
+ [DUPLICATE]: () => onDuplicateNode(uiNode),
2194
+ [RENAME]: () => setMode(WRITE),
2195
+ [DELETE]: () => onDeleteNode(uiNode)
2196
+ };
2197
+ const dropdownMenuOptions = Object.entries(nodeOptionsMap).map(([id, option]) => ({
2198
+ ...option,
2199
+ id
2200
+ })).filter((option) => defaultFolderOptions.includes(option.id));
2201
+ function onDropdownMenuOptionClick(itemId) {
2202
+ const handler = dropdownMenuHandlers[itemId];
2203
+ if (!handler) {
2204
+ console.error(`No handler found for dropdown menu item: ${itemId}`);
2205
+ return;
2206
+ }
2207
+ handler();
2208
+ setIsDropdownMenuOpen(false);
2209
+ }
2210
+ 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 });
2211
+ 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");
2212
+ 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: {
2213
+ SUCCESS: "CheckCircleFill"
2214
+ }, 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) => {
2215
+ e.stopPropagation();
2216
+ setIsDropdownMenuOpen(true);
2217
+ }, children: jsx(Icon, { className: "text-gray-600", name: "VerticalDots" }) }) }) : null] }) });
2218
+ }
2219
+ function NodeInput(props) {
2220
+ const { onSubmit, onCancel, defaultValue, className, minLength = 1, ...inputProps } = props;
2221
+ const [value, setValue] = useState(defaultValue ?? "");
2222
+ const ref = useRef(null);
2223
+ useOnClickOutside(ref, handleSubmit);
2224
+ useEventListener("keyup", (e) => {
2225
+ if (e.key === "Enter") {
2226
+ handleSubmit();
2227
+ }
2228
+ if (e.key === "Escape") {
2229
+ onCancel();
2230
+ }
2231
+ });
2232
+ useLayoutEffect(() => {
2233
+ setTimeout(() => {
2234
+ var _a, _b, _c;
2235
+ (_a = ref.current) == null ? void 0 : _a.focus();
2236
+ (_b = ref.current) == null ? void 0 : _b.select();
2237
+ (_c = ref.current) == null ? void 0 : _c.scroll({ left: 9999 });
2238
+ }, 100);
2239
+ }, []);
2240
+ function handleSubmit() {
2241
+ if (value.length >= minLength) {
2242
+ onSubmit(value);
2243
+ }
2244
+ }
2245
+ 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 });
2246
+ }
2247
+ function Branch(props) {
2248
+ const { branch = "main" } = props;
2249
+ 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 })] });
2250
+ }
2251
+ function DocId(props) {
2252
+ const { docId } = props;
2253
+ const [, copy] = useCopyToClipboard();
2254
+ function handleCopy(text) {
2255
+ return () => {
2256
+ copy(text).catch((error) => {
2257
+ console.error("Failed to copy!", error);
2258
+ });
2259
+ };
2260
+ }
2261
+ 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 })] });
2262
+ }
2263
+ function Scope(props) {
2264
+ const { value, onChange } = props;
2265
+ const items = [
2266
+ { displayValue: "Global scope", value: "global" },
2267
+ { displayValue: "Local scope", value: "local" }
2268
+ ];
2269
+ 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 });
2270
+ }
2271
+ function Header(props) {
2272
+ const { title, docId, scope, onChangeScope, onClose, className, ...divProps } = props;
2273
+ 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 })] })] });
2274
+ }
2275
+ function memo(getDeps, fn, opts) {
2276
+ let deps = opts.initialDeps ?? [];
2277
+ let result;
2278
+ function memoizedFunction() {
2279
+ var _a, _b, _c, _d;
2280
+ let depTime;
2281
+ if (opts.key && ((_a = opts.debug) == null ? void 0 : _a.call(opts))) depTime = Date.now();
2282
+ const newDeps = getDeps();
2283
+ const depsChanged = newDeps.length !== deps.length || newDeps.some((dep, index) => deps[index] !== dep);
2284
+ if (!depsChanged) {
2285
+ return result;
2286
+ }
2287
+ deps = newDeps;
1837
2288
  let resultTime;
1838
2289
  if (opts.key && ((_b = opts.debug) == null ? void 0 : _b.call(opts))) resultTime = Date.now();
1839
2290
  result = fn(...newDeps);
@@ -2858,8 +3309,8 @@ function RevisionHistory(props) {
2858
3309
  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
3310
  }
2860
3311
  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 }) })] });
3312
+ const { children, content, open, defaultOpen, onOpenChange, className, side = "top", sideOffset = 5, ...rest } = props;
3313
+ 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
3314
  }
2864
3315
  const TooltipProvider = Provider;
2865
3316
  const BUDGET = "powerhouse/budget-statement";
@@ -3489,6 +3940,17 @@ function useEditorProps(document2, node, documentDispatch, onAddOperation) {
3489
3940
  isAllowedToEditDocuments: (userPermissions == null ? void 0 : userPermissions.isAllowedToEditDocuments) ?? false
3490
3941
  };
3491
3942
  }
3943
+ function useGetDocument() {
3944
+ const { openFile } = useDocumentDriveServer();
3945
+ const getDocument = useCallback(
3946
+ async (driveId, documentId, options) => {
3947
+ const document2 = await openFile(driveId, documentId, options);
3948
+ return document2;
3949
+ },
3950
+ [openFile]
3951
+ );
3952
+ return getDocument;
3953
+ }
3492
3954
  function useSyncStatus(driveId, documentId) {
3493
3955
  const { getSyncStatusSync, onSyncStatus, documentDrives } = useDocumentDriveServer();
3494
3956
  const syncStatus = useSyncExternalStore(
@@ -3559,36 +4021,340 @@ const useUndoRedoShortcuts = (props) => {
3559
4021
  [canRedo, redo2]
3560
4022
  );
3561
4023
  };
3562
- function EditorLoader(props) {
3563
- const [showLoading, setShowLoading] = useState(false);
3564
- useEffect(() => {
3565
- setTimeout(() => {
3566
- setShowLoading(true);
3567
- }, props.loadingTimeout ?? 200);
3568
- }, [props]);
3569
- if (!showLoading) return null;
3570
- const { customEditorLoader, ...defaultProps } = props;
3571
- if (customEditorLoader) return /* @__PURE__ */ jsx(Fragment$1, { children: customEditorLoader });
3572
- return /* @__PURE__ */ jsx(DefaultEditorLoader, { ...defaultProps });
3573
- }
3574
- function EditorError({ message: message2 }) {
3575
- return /* @__PURE__ */ jsx("div", { className: "flex size-full items-center justify-center", children: /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold", children: message2 }) });
4024
+ function getDocumentSpec(doc) {
4025
+ if ("documentModelState" in doc) {
4026
+ return doc.documentModelState;
4027
+ }
4028
+ return doc.documentModel;
3576
4029
  }
3577
- function FallbackEditorError(props) {
3578
- const message2 = props.error instanceof Error ? props.error.message : props.error;
3579
- return /* @__PURE__ */ jsx(EditorError, { message: message2 });
4030
+ const CreateDocument = ({ documentModels, createDocument }) => {
4031
+ 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) => {
4032
+ const spec = getDocumentSpec(doc);
4033
+ return jsx(Button, { color: "light", "aria-details": spec.description, onClick: () => createDocument(doc), children: jsx("span", { className: "text-sm", children: spec.name }) }, spec.id);
4034
+ }) })] });
4035
+ };
4036
+ function sortUiNodesByName(a, b) {
4037
+ return a.name.localeCompare(b.name);
3580
4038
  }
3581
- const DocumentEditor = (props) => {
3582
- const {
3583
- fileNodeDocument,
3584
- document: initialDocument,
4039
+ const GAP = 8;
4040
+ const ITEM_WIDTH = 256;
4041
+ const ITEM_HEIGHT = 48;
4042
+ const USED_SPACE = 420;
4043
+ function FileContentView(props) {
4044
+ const parentRef = useRef(null);
4045
+ const { t } = useTranslation();
4046
+ const windowSize = useWindowSize();
4047
+ const { fileNodes, ...fileProps } = props;
4048
+ const availableWidth = windowSize.innerWidth - USED_SPACE;
4049
+ const columnCount = Math.floor(availableWidth / (ITEM_WIDTH + GAP)) || 1;
4050
+ const rowCount = Math.ceil(fileNodes.length / columnCount);
4051
+ const rowVirtualizer = useVirtualizer({
4052
+ count: rowCount,
4053
+ getScrollElement: () => parentRef.current,
4054
+ estimateSize: (index) => {
4055
+ if (index > 0) {
4056
+ return ITEM_HEIGHT + GAP;
4057
+ }
4058
+ return ITEM_HEIGHT;
4059
+ },
4060
+ overscan: 5
4061
+ });
4062
+ const columnVirtualizer = useVirtualizer({
4063
+ horizontal: true,
4064
+ count: columnCount,
4065
+ getScrollElement: () => parentRef.current,
4066
+ estimateSize: (index) => {
4067
+ if (index > 0) {
4068
+ return ITEM_WIDTH + GAP;
4069
+ }
4070
+ return ITEM_WIDTH;
4071
+ },
4072
+ overscan: 5
4073
+ });
4074
+ const getItemIndex = (rowIndex, columnIndex) => rowIndex * columnCount + columnIndex;
4075
+ const getItem = (rowIndex, columnIndex) => {
4076
+ const index = getItemIndex(rowIndex, columnIndex);
4077
+ return fileNodes[index] || null;
4078
+ };
4079
+ if (fileNodes.length === 0) {
4080
+ return jsx("div", { className: "mb-8 text-sm text-gray-400", children: t("folderView.sections.documents.empty", {
4081
+ defaultValue: "No documents or files 📄"
4082
+ }) });
4083
+ }
4084
+ const renderItem = (rowIndex, columnIndex) => {
4085
+ const fileNode = getItem(rowIndex, columnIndex);
4086
+ if (!fileNode) {
4087
+ return null;
4088
+ }
4089
+ return jsx("div", { style: {
4090
+ marginLeft: columnIndex === 0 ? 0 : GAP
4091
+ }, children: jsx(FileItem, { uiNode: fileNode, ...fileProps }, fileNode.id) });
4092
+ };
4093
+ return jsx("div", { ref: parentRef, style: {
4094
+ height: `400px`,
4095
+ width: `100%`,
4096
+ overflow: "auto"
4097
+ }, children: jsx("div", { style: {
4098
+ height: `${rowVirtualizer.getTotalSize()}px`,
4099
+ width: `${columnVirtualizer.getTotalSize()}px`,
4100
+ position: "relative"
4101
+ }, children: rowVirtualizer.getVirtualItems().map((virtualRow) => jsx(React__default.Fragment, { children: columnVirtualizer.getVirtualItems().map((virtualColumn) => jsx("div", { style: {
4102
+ position: "absolute",
4103
+ top: 0,
4104
+ left: 0,
4105
+ marginTop: virtualRow.index === 0 ? 0 : GAP,
4106
+ width: `${virtualColumn.size}px`,
4107
+ height: `${virtualRow.size}px`,
4108
+ transform: `translateX(${virtualColumn.start}px) translateY(${virtualRow.start}px)`
4109
+ }, children: renderItem(virtualRow.index, virtualColumn.index) }, virtualColumn.key)) }, virtualRow.key)) }) });
4110
+ }
4111
+ function FolderView(props) {
4112
+ const { node, className, isDropTarget, containerProps, ...nodeProps } = props;
4113
+ const { t } = useTranslation();
4114
+ const folderNodes = node.children.filter((node2) => node2.kind === FOLDER).sort(sortUiNodesByName);
4115
+ const fileNodes = node.children.filter((node2) => node2.kind === FILE).sort(sortUiNodesByName);
4116
+ const folderCallbacks = {
4117
+ onSelectNode: (node2) => nodeProps.onSelectNode(node2),
4118
+ onRenameNode: (name, node2) => nodeProps.onRenameNode(name, node2),
4119
+ onDuplicateNode: (node2) => nodeProps.onDuplicateNode(node2),
4120
+ onDeleteNode: (node2) => nodeProps.onDeleteNode(node2)
4121
+ };
4122
+ const fileCallbacks = {
4123
+ onSelectNode: (node2) => nodeProps.onSelectNode(node2),
4124
+ onRenameNode: (name, node2) => nodeProps.onRenameNode(name, node2),
4125
+ onDuplicateNode: (node2) => nodeProps.onDuplicateNode(node2),
4126
+ onDeleteNode: (node2) => nodeProps.onDeleteNode(node2)
4127
+ };
4128
+ const baseNodeCallbacks = {
4129
+ onAddFile: async (file, parentNode) => {
4130
+ await nodeProps.onAddFile(file, parentNode);
4131
+ },
4132
+ onCopyNode: async (uiNode, targetNode) => {
4133
+ await nodeProps.onCopyNode(uiNode, targetNode);
4134
+ },
4135
+ onMoveNode: async (uiNode, targetNode) => {
4136
+ await nodeProps.onMoveNode(uiNode, targetNode);
4137
+ }
4138
+ };
4139
+ 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", {
4140
+ defaultValue: "Folders"
4141
+ }), 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", {
4142
+ defaultValue: "No documents or files 📄"
4143
+ }) }) }), jsx(DriveLayout.ContentSection, { title: t("folderView.sections.documents.title", {
4144
+ defaultValue: "Documents and files"
4145
+ }), children: jsx("div", { className: twMerge("w-full", fileNodes.length > 0 ? "min-h-[400px]" : "min-h-14"), children: jsx(FileContentView, { fileNodes, ...fileCallbacks, isAllowedToCreateDocuments: nodeProps.isAllowedToCreateDocuments }) }) })] });
4146
+ }
4147
+ function BaseEditor(props) {
4148
+ const { document: document2, dispatch, className, children } = props;
4149
+ const { state: { global: { id: driveId } } } = document2;
4150
+ const { showSearchBar, isAllowedToCreateDocuments, documentModels, showCreateDocumentModal } = useDriveContext();
4151
+ const { driveNodes, selectedNode, selectedNodePath, getNodeById, setSelectedNode } = useUiNodesContext();
4152
+ const driveNode = useMemo(() => driveNodes.find((n) => n.id === driveId), [driveNodes, driveId]);
4153
+ const { addDocument, addFile, addFolder: addFolder2, renameNode, deleteNode: deleteNode2, moveNode: moveNode2, copyNode: copyNode2, duplicateNode } = useDriveActionsWithUiNodes(document2, dispatch);
4154
+ const onCreateDocument = useCallback(async (documentModel) => {
4155
+ const { name } = await showCreateDocumentModal(documentModel);
4156
+ const document3 = documentModel.utils.createDocument();
4157
+ await addDocument(name, documentModel.documentModel.name, document3, selectedNode == null ? void 0 : selectedNode.id);
4158
+ }, [addDocument, showCreateDocumentModal, selectedNode == null ? void 0 : selectedNode.id]);
4159
+ const { isDropTarget, dropProps } = useDrop({
4160
+ uiNode: selectedNode,
4161
+ onAddFile: addFile,
4162
+ onCopyNode: copyNode2,
4163
+ onMoveNode: moveNode2
4164
+ });
4165
+ const { breadcrumbs, onBreadcrumbSelected } = useBreadcrumbs({
4166
+ selectedNodePath,
4167
+ getNodeById,
4168
+ setSelectedNode
4169
+ });
4170
+ if (!driveNode) {
4171
+ return jsx("div", { children: "Drive not found" });
4172
+ } else if ((selectedNode == null ? void 0 : selectedNode.kind) === FILE$1) {
4173
+ return jsx(Fragment$1, {});
4174
+ }
4175
+ 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 }) })] });
4176
+ }
4177
+ function Editor(props) {
4178
+ return jsx(DriveContextProvider, { value: props.context, children: jsx(BaseEditor, { ...props }) });
4179
+ }
4180
+ const GenericDriveExplorer = {
4181
+ Component: Editor
4182
+ };
4183
+ function useAnalyticsQuery(query, options) {
4184
+ const engine = useAnalyticsEngine();
4185
+ const store = useAnalyticsStore();
4186
+ const { data: querySources } = useQuerySources(query);
4187
+ const queryClient = useQueryClient();
4188
+ const subscriptions = useRef([]);
4189
+ const result = useQuery({
4190
+ queryKey: ["analytics", "query", query],
4191
+ queryFn: () => engine.execute(query),
4192
+ ...options
4193
+ });
4194
+ useEffect(() => {
4195
+ if (!(querySources == null ? void 0 : querySources.length)) {
4196
+ return;
4197
+ }
4198
+ querySources.forEach((source) => {
4199
+ const unsub = store.subscribeToSource(source, () => {
4200
+ return queryClient.invalidateQueries({
4201
+ queryKey: ["analytics", "query", query]
4202
+ });
4203
+ });
4204
+ subscriptions.current.push(unsub);
4205
+ });
4206
+ return () => {
4207
+ subscriptions.current.forEach((unsub) => unsub());
4208
+ subscriptions.current = [];
4209
+ };
4210
+ }, [querySources]);
4211
+ return result;
4212
+ }
4213
+ function useMatchingSeries(query, options) {
4214
+ const store = useAnalyticsStore();
4215
+ const result = useQuery({
4216
+ queryKey: ["analytics", "matchingSeries", query],
4217
+ queryFn: () => store.getMatchingSeries(query),
4218
+ ...options
4219
+ });
4220
+ return result;
4221
+ }
4222
+ function useQuerySources(query, options) {
4223
+ const { data: matchingSeries } = useMatchingSeries(query);
4224
+ return useQuery({
4225
+ queryKey: ["analytics", "sources", query],
4226
+ queryFn: () => {
4227
+ if (!(matchingSeries == null ? void 0 : matchingSeries.length)) {
4228
+ return [];
4229
+ }
4230
+ const uniqueSources = [
4231
+ ...new Set(matchingSeries.map((s) => s.source.toString()))
4232
+ ];
4233
+ return uniqueSources.map((source) => AnalyticsPath.fromString(source));
4234
+ },
4235
+ enabled: !!matchingSeries,
4236
+ ...options
4237
+ });
4238
+ }
4239
+ const getBarSize = (value) => {
4240
+ if (value <= 0)
4241
+ return 0;
4242
+ if (value > 0 && value <= 50)
4243
+ return 1;
4244
+ if (value > 50 && value <= 100)
4245
+ return 2;
4246
+ if (value > 100 && value <= 250)
4247
+ return 3;
4248
+ return 4;
4249
+ };
4250
+ const useTimelineItems = (documentId, startTimestamp) => {
4251
+ const start = startTimestamp ? DateTime.fromISO(startTimestamp) : DateTime.now().startOf("day");
4252
+ const { data: diffResult, isLoading } = useAnalyticsQuery({
4253
+ start,
4254
+ end: DateTime.now().endOf("day"),
4255
+ granularity: AnalyticsGranularity.Daily,
4256
+ metrics: ["Count"],
4257
+ select: {
4258
+ changes: [AnalyticsPath.fromString(`changes`)],
4259
+ document: [AnalyticsPath.fromString(`document/${documentId}`)]
4260
+ },
4261
+ lod: {
4262
+ changes: 2
4263
+ },
4264
+ currency: AnalyticsPath.fromString("")
4265
+ });
4266
+ const mappedResult = useMemo(() => {
4267
+ if (!diffResult)
4268
+ return [];
4269
+ return diffResult.sort((a, b) => {
4270
+ const aDate = new Date(a.start);
4271
+ const bDate = new Date(b.start);
4272
+ return aDate.getTime() - bDate.getTime();
4273
+ }).map((result) => {
4274
+ const { additions, deletions } = result.rows.reduce((acc, row) => {
4275
+ if (row.dimensions.changes.path === "changes/add") {
4276
+ acc.additions += row.sum;
4277
+ } else if (row.dimensions.changes.path === "changes/remove") {
4278
+ acc.deletions += row.sum;
4279
+ }
4280
+ return acc;
4281
+ }, { additions: 0, deletions: 0 });
4282
+ const startDate = new Date(result.start);
4283
+ return {
4284
+ id: startDate.toISOString(),
4285
+ type: "bar",
4286
+ addSize: getBarSize(additions),
4287
+ delSize: getBarSize(deletions),
4288
+ additions,
4289
+ deletions,
4290
+ timestamp: startDate.toISOString(),
4291
+ date: startDate,
4292
+ endDate: new Date(result.end),
4293
+ revision: 0
4294
+ };
4295
+ });
4296
+ }, [diffResult]);
4297
+ const resultWithDividers = useMemo(() => {
4298
+ if (!mappedResult.length)
4299
+ return [];
4300
+ const result = [];
4301
+ mappedResult.forEach((item, index) => {
4302
+ result.push(item);
4303
+ if (index < mappedResult.length - 1) {
4304
+ const currentDate = new Date(item.date);
4305
+ const nextDate = new Date(mappedResult[index + 1].date);
4306
+ const currentHour = currentDate.getHours();
4307
+ const nextHour = nextDate.getHours();
4308
+ const currentDay = currentDate.toDateString();
4309
+ const nextDay = nextDate.toDateString();
4310
+ if (currentDay !== nextDay || currentDay === nextDay && Math.abs(nextHour - currentHour) > 1) {
4311
+ result.push({
4312
+ id: `divider-${item.id}-${mappedResult[index + 1].id}`,
4313
+ type: "divider",
4314
+ revision: 0
4315
+ });
4316
+ }
4317
+ }
4318
+ });
4319
+ return result;
4320
+ }, [mappedResult]);
4321
+ return {
4322
+ isLoading,
4323
+ data: resultWithDividers
4324
+ };
4325
+ };
4326
+ function EditorLoader(props) {
4327
+ const [showLoading, setShowLoading] = useState(false);
4328
+ useEffect(() => {
4329
+ setTimeout(() => {
4330
+ setShowLoading(true);
4331
+ }, props.loadingTimeout ?? 200);
4332
+ }, [props]);
4333
+ if (!showLoading) return null;
4334
+ const { customEditorLoader, ...defaultProps } = props;
4335
+ if (customEditorLoader) return /* @__PURE__ */ jsx(Fragment$1, { children: customEditorLoader });
4336
+ return /* @__PURE__ */ jsx(DefaultEditorLoader, { ...defaultProps });
4337
+ }
4338
+ function EditorError({ message: message2 }) {
4339
+ return /* @__PURE__ */ jsx("div", { className: "flex size-full items-center justify-center", children: /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold", children: message2 }) });
4340
+ }
4341
+ function FallbackEditorError(props) {
4342
+ const message2 = props.error instanceof Error ? props.error.message : props.error;
4343
+ return /* @__PURE__ */ jsx(EditorError, { message: message2 });
4344
+ }
4345
+ const DocumentEditor = (props) => {
4346
+ const {
4347
+ fileNodeDocument,
4348
+ document: initialDocument,
3585
4349
  onClose,
3586
4350
  onChange,
3587
4351
  onExport,
3588
4352
  onAddOperation,
4353
+ onGetDocumentRevision,
3589
4354
  onOpenSwitchboardLink
3590
4355
  } = props;
3591
4356
  const documentId = fileNodeDocument == null ? void 0 : fileNodeDocument.documentId;
4357
+ const [selectedTimelineItem, setSelectedTimelineItem] = useState(null);
3592
4358
  const [revisionHistoryVisible, setRevisionHistoryVisible] = useState(false);
3593
4359
  const theme = useAtomValue(themeAtom);
3594
4360
  const user = useUser() || void 0;
@@ -3614,6 +4380,10 @@ const DocumentEditor = (props) => {
3614
4380
  [theme, user]
3615
4381
  );
3616
4382
  const userPermissions = useUserPermissions$1();
4383
+ const timelineItems = useTimelineItems(
4384
+ documentId,
4385
+ initialDocument == null ? void 0 : initialDocument.created
4386
+ );
3617
4387
  const currentDocument = useRef({ ...fileNodeDocument, document: document2 });
3618
4388
  useEffect(() => {
3619
4389
  var _a;
@@ -3790,7 +4560,8 @@ const DocumentEditor = (props) => {
3790
4560
  const {
3791
4561
  disableExternalControls,
3792
4562
  documentToolbarEnabled,
3793
- showSwitchboardLink
4563
+ showSwitchboardLink,
4564
+ timelineEnabled
3794
4565
  } = editor.config || {};
3795
4566
  const handleSwitchboardLinkClick = showSwitchboardLink !== false ? onOpenSwitchboardLink : void 0;
3796
4567
  return /* @__PURE__ */ jsxs("div", { className: "relative h-full", id: "document-editor-context", children: [
@@ -3801,7 +4572,10 @@ const DocumentEditor = (props) => {
3801
4572
  onExport,
3802
4573
  onShowRevisionHistory: showRevisionHistory,
3803
4574
  title: fileNodeDocument.name || document2.name,
3804
- onSwitchboardLinkClick: handleSwitchboardLinkClick
4575
+ onSwitchboardLinkClick: handleSwitchboardLinkClick,
4576
+ timelineButtonVisible: timelineEnabled,
4577
+ timelineItems: timelineItems.data,
4578
+ onTimelineItemClick: setSelectedTimelineItem
3805
4579
  }
3806
4580
  ),
3807
4581
  !disableExternalControls && /* @__PURE__ */ jsxs("div", { className: "mb-4 flex justify-end gap-10", children: [
@@ -3831,7 +4605,12 @@ const DocumentEditor = (props) => {
3831
4605
  EditorComponent,
3832
4606
  {
3833
4607
  error,
3834
- context,
4608
+ context: {
4609
+ ...context,
4610
+ getDocumentRevision: onGetDocumentRevision,
4611
+ readMode: !!selectedTimelineItem,
4612
+ selectedTimelineRevision: selectedTimelineItem == null ? void 0 : selectedTimelineItem.revision
4613
+ },
3835
4614
  document: document2,
3836
4615
  documentNodeName: fileNodeDocument.name,
3837
4616
  dispatch,
@@ -3869,6 +4648,7 @@ function DocumentEditorContainer() {
3869
4648
  renameNode,
3870
4649
  getDocumentModelModule
3871
4650
  } = useUiNodes();
4651
+ const getDocument = useGetDocument();
3872
4652
  const handleAddOperationToSelectedDocument = useCallback(
3873
4653
  async (operation) => {
3874
4654
  if (!selectedDocument) {
@@ -3931,6 +4711,20 @@ function DocumentEditorContainer() {
3931
4711
  },
3932
4712
  [getDocumentModelModule, showModal, t]
3933
4713
  );
4714
+ const onGetDocumentRevision = useCallback(
4715
+ (options) => {
4716
+ if (!selectedNode) {
4717
+ console.error("No selected node");
4718
+ return Promise.reject(new Error("No selected node"));
4719
+ }
4720
+ return getDocument(
4721
+ selectedNode.driveId,
4722
+ selectedNode.id,
4723
+ options
4724
+ );
4725
+ },
4726
+ [getDocument, selectedNode]
4727
+ );
3934
4728
  const onExport = useCallback(() => {
3935
4729
  if (selectedDocument) {
3936
4730
  return exportDocument(selectedDocument);
@@ -3952,6 +4746,7 @@ function DocumentEditorContainer() {
3952
4746
  onChange: onDocumentChangeHandler,
3953
4747
  onClose,
3954
4748
  onExport,
4749
+ onGetDocumentRevision,
3955
4750
  onAddOperation: handleAddOperationToSelectedDocument,
3956
4751
  onOpenSwitchboardLink
3957
4752
  }
@@ -3960,165 +4755,6 @@ function DocumentEditorContainer() {
3960
4755
  fileNodeDocument.documentId
3961
4756
  );
3962
4757
  }
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
4758
  function useDocumentsState(args) {
4123
4759
  const { reactor, driveId, documentIds, options } = args;
4124
4760
  const [statesByDocumentId, setStatesByDocumentId] = useState({});
@@ -4396,4 +5032,4 @@ function Content() {
4396
5032
  export {
4397
5033
  Content as default
4398
5034
  };
4399
- //# sourceMappingURL=content-DnXO71l4.js.map
5035
+ //# sourceMappingURL=content-C1JKApwb.js.map