@powerhousedao/design-system 6.2.2-dev.9 → 6.2.2-staging.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,10 +1,9 @@
1
- import { _ as DropdownMenuTrigger, c as SidebarHeader, d as PackageAnimation, f as Modal, h as DropdownMenuItem, l as SidebarPanel, m as DropdownMenuContent, n as usePagination, o as Sidebar, p as DropdownMenu, s as SidebarFooter, u as Pagination, v as PowerhouseButton } from "../src-DpUTb0dJ.js";
1
+ import { _ as DropdownMenuTrigger, c as SidebarHeader, d as PackageAnimation, f as Modal, h as DropdownMenuItem, l as SidebarPanel, m as DropdownMenuContent, n as usePagination, o as Sidebar, p as DropdownMenu, s as SidebarFooter, u as Pagination, v as PowerhouseButton } from "../src-CVV6HzQZ.js";
2
2
  import { n as getDimensions, t as Icon } from "../icon-C4QOpsdI.js";
3
3
  import { Popover, PopoverContent, PopoverTrigger } from "../ui/components/popover/popover.js";
4
4
  import { SelectFieldRaw } from "../ui/components/select-field/select-field.js";
5
5
  import { Input } from "../ui/components/input/input.js";
6
6
  import { JsonViewer } from "../ui/components/json-viewer/json-viewer.js";
7
- import { SearchAutocomplete } from "../ui/components/search-autocomplete/search-autocomplete.js";
8
7
  import React, { Fragment, createElement, forwardRef, memo, useCallback, useEffect, useLayoutEffect, useMemo, useReducer, useRef, useState } from "react";
9
8
  import { twJoin, twMerge } from "tailwind-merge";
10
9
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
@@ -13,7 +12,8 @@ import { Controller, useForm } from "react-hook-form";
13
12
  import { Anchor, Content as Content$1, Portal as Portal$1, Root as Root$1, Trigger as Trigger$1 } from "@radix-ui/react-popover";
14
13
  import { format, formatDistanceToNow, parseISO } from "date-fns";
15
14
  import { useCopyToClipboard, useDebounceValue, useEventListener, useOnClickOutside } from "usehooks-ts";
16
- import { SyncOperationStatus, addFolder, getSyncStatusSync, preloadEditorModule, setSelectedDrive, setSelectedNode, showDeleteNodeModal, showPHModal, showRevisionHistory, useDocumentById, useDownloadDocument, useDragNode, useDropFile, useDropNode, useEditorModulesForDocumentType, useGetSwitchboardLink, useIsDragAndDropEnabled, useNodeActions, useNodeById, useNodeParentFolderById, useOnDropFile, useSelectedDocumentSafe, useSelectedDriveId, useSelectedDriveSafe, useSelectedNodePath, useTheme, useUserPermissions } from "@powerhousedao/reactor-browser";
15
+ import { Loader2, Mail, Monitor, Moon, Settings, Smartphone, Sun, Wallet } from "lucide-react";
16
+ import { SyncOperationStatus, addFolder, getSyncStatusSync, preloadEditorModule, setSelectedDrive, setSelectedNode, showDeleteNodeModal, showPHModal, showRevisionHistory, useDocumentById, useDocumentVersionStatus, useDownloadDocument, useDownloadFolder, useDragNode, useDropFile, useDropNode, useEditorModulesForDocumentType, useGetSwitchboardLink, useIsDragAndDropEnabled, useNodeActions, useNodeById, useNodeParentFolderById, useOnDropFile, useSelectedDocumentSafe, useSelectedDriveId, useSelectedDriveSafe, useSelectedNodePath, useTheme, useUserPermissions } from "@powerhousedao/reactor-browser";
17
17
  import Select, { components } from "react-select";
18
18
  import { Content as Content$2, List, Root as Root$2, Trigger as Trigger$2 } from "@radix-ui/react-tabs";
19
19
  import { addProp, defaultTo, entries, filter, funnel, hasAtLeast, isArray, isDefined, isIncludedIn, isStrictEqual, isString, isTruthy, keys, map, pipe, prop, values } from "remeda";
@@ -25,22 +25,152 @@ import { lightTheme } from "@uiw/react-json-view/light";
25
25
  import { isValidName } from "@powerhousedao/shared/document-drive";
26
26
  import { capitalCase } from "change-case";
27
27
  import { useVirtualizer } from "@tanstack/react-virtual";
28
- import { Monitor, Moon, Settings, Sun } from "lucide-react";
29
28
  //#region src/connect/components/account-popover/account-popover.tsx
30
- const AccountPopover = ({ children, content }) => {
31
- return /* @__PURE__ */ jsxs(Popover, { children: [/* @__PURE__ */ jsx(PopoverTrigger, {
32
- asChild: true,
33
- children: /* @__PURE__ */ jsx("button", {
29
+ const AccountPopover = ({ children, content, open, onOpenChange }) => {
30
+ return /* @__PURE__ */ jsxs(Popover, {
31
+ open,
32
+ onOpenChange,
33
+ children: [/* @__PURE__ */ jsx(PopoverTrigger, {
34
+ asChild: true,
35
+ children: /* @__PURE__ */ jsx("button", {
36
+ type: "button",
37
+ "aria-label": "Open Account",
38
+ className: "cursor-pointer",
39
+ children
40
+ })
41
+ }), /* @__PURE__ */ jsx(PopoverContent, {
42
+ className: "w-52 p-0",
43
+ align: "start",
44
+ children: content
45
+ })]
46
+ });
47
+ };
48
+ //#endregion
49
+ //#region src/connect/components/account-popover/account-popover-login.tsx
50
+ const AccountPopoverLogin = ({ onLogin }) => {
51
+ const allowLogin = !!onLogin;
52
+ return /* @__PURE__ */ jsxs("div", {
53
+ className: "p-4",
54
+ children: [/* @__PURE__ */ jsx("div", {
55
+ className: "mb-4 flex justify-center",
56
+ children: /* @__PURE__ */ jsx("div", {
57
+ className: "flex h-5.5 w-20.75 items-center justify-center overflow-hidden text-foreground",
58
+ children: /* @__PURE__ */ jsx(Icon, {
59
+ name: "RenownLight",
60
+ size: 83
61
+ })
62
+ })
63
+ }), /* @__PURE__ */ jsx("button", {
64
+ onClick: allowLogin ? onLogin : void 0,
65
+ className: allowLogin ? "mt-4 flex h-7 w-full cursor-pointer items-center justify-center rounded-lg border border-border bg-transparent text-sm text-foreground active:active-effect" : "mt-4 flex h-7 w-full cursor-wait items-center justify-center rounded-lg border border-border bg-transparent text-sm text-foreground",
34
66
  type: "button",
35
- "aria-label": "Open Account",
36
- className: "cursor-pointer",
37
- children
67
+ children: /* @__PURE__ */ jsx("span", { children: "Connect" })
68
+ })]
69
+ });
70
+ };
71
+ //#endregion
72
+ //#region src/connect/components/renown-login/renown-login-methods.tsx
73
+ const GoogleMark = () => /* @__PURE__ */ jsxs("svg", {
74
+ width: "18",
75
+ height: "18",
76
+ viewBox: "0 0 18 18",
77
+ "aria-hidden": "true",
78
+ children: [
79
+ /* @__PURE__ */ jsx("path", {
80
+ fill: "#4285F4",
81
+ d: "M17.64 9.205c0-.639-.057-1.252-.164-1.841H9v3.481h4.844a4.14 4.14 0 0 1-1.796 2.716v2.259h2.908c1.702-1.567 2.684-3.875 2.684-6.615z"
82
+ }),
83
+ /* @__PURE__ */ jsx("path", {
84
+ fill: "#34A853",
85
+ d: "M9 18c2.43 0 4.467-.806 5.956-2.18l-2.908-2.259c-.806.54-1.837.859-3.048.859-2.344 0-4.328-1.583-5.036-3.71H.957v2.332A8.997 8.997 0 0 0 9 18z"
86
+ }),
87
+ /* @__PURE__ */ jsx("path", {
88
+ fill: "#FBBC05",
89
+ d: "M3.964 10.71A5.41 5.41 0 0 1 3.682 9c0-.593.102-1.17.282-1.71V4.958H.957A8.997 8.997 0 0 0 0 9c0 1.452.348 2.827.957 4.042l3.007-2.332z"
90
+ }),
91
+ /* @__PURE__ */ jsx("path", {
92
+ fill: "#EA4335",
93
+ d: "M9 3.58c1.321 0 2.508.454 3.44 1.346l2.582-2.581C13.463.891 11.426 0 9 0A8.997 8.997 0 0 0 .957 4.958L3.964 7.29C4.672 5.163 6.656 3.58 9 3.58z"
38
94
  })
39
- }), /* @__PURE__ */ jsx(PopoverContent, {
40
- className: "w-52 p-0",
41
- align: "start",
42
- children: content
43
- })] });
95
+ ]
96
+ });
97
+ const AppleMark = () => /* @__PURE__ */ jsx("svg", {
98
+ width: "18",
99
+ height: "18",
100
+ viewBox: "0 0 24 24",
101
+ fill: "currentColor",
102
+ "aria-hidden": "true",
103
+ children: /* @__PURE__ */ jsx("path", { d: "M17.05 20.28c-.98.95-2.05.8-3.08.35-1.09-.46-2.09-.48-3.24 0-1.44.62-2.2.44-3.06-.35C2.79 15.25 3.51 7.59 9.05 7.31c1.35.07 2.29.74 3.08.8 1.18-.24 2.31-.93 3.57-.84 1.51.12 2.65.72 3.4 1.8-3.12 1.87-2.38 5.98.48 7.13-.57 1.5-1.31 2.99-2.51 4.09zM12.03 7.25c-.15-2.23 1.66-4.07 3.74-4.25.29 2.58-2.34 4.5-3.74 4.25z" })
104
+ });
105
+ const METHOD_ICONS = {
106
+ wallet: /* @__PURE__ */ jsx(Wallet, {
107
+ size: 18,
108
+ "aria-hidden": "true"
109
+ }),
110
+ email: /* @__PURE__ */ jsx(Mail, {
111
+ size: 18,
112
+ "aria-hidden": "true"
113
+ }),
114
+ sms: /* @__PURE__ */ jsx(Smartphone, {
115
+ size: 18,
116
+ "aria-hidden": "true"
117
+ }),
118
+ google: /* @__PURE__ */ jsx(GoogleMark, {}),
119
+ apple: /* @__PURE__ */ jsx(AppleMark, {})
120
+ };
121
+ const baseButton = "relative flex h-10 w-full items-center justify-center gap-2 rounded-lg px-3 text-sm font-semibold transition-colors disabled:pointer-events-none disabled:opacity-60";
122
+ const primaryButton = "bg-primary text-primary-foreground hover:bg-primary/85";
123
+ const secondaryButton = "border border-border bg-card text-foreground hover:bg-muted";
124
+ const RenownLoginMethods = ({ methods, loading = false, error, className }) => {
125
+ const [pendingId, setPendingId] = useState(null);
126
+ const wallet = methods.find((method) => method.id === "wallet");
127
+ const others = methods.filter((method) => method.id !== "wallet");
128
+ const renderButton = (method, primary) => {
129
+ const isPending = loading && pendingId === method.id;
130
+ return /* @__PURE__ */ jsxs("button", {
131
+ type: "button",
132
+ disabled: loading,
133
+ onClick: () => {
134
+ setPendingId(method.id);
135
+ method.onSelect();
136
+ },
137
+ className: twMerge(baseButton, primary ? primaryButton : secondaryButton, isPending && "animate-pulse"),
138
+ children: [
139
+ isPending ? /* @__PURE__ */ jsx(Loader2, {
140
+ size: 18,
141
+ className: "animate-spin",
142
+ "aria-hidden": "true"
143
+ }) : METHOD_ICONS[method.id],
144
+ /* @__PURE__ */ jsx("span", { children: method.label }),
145
+ method.lastUsed ? /* @__PURE__ */ jsx("span", {
146
+ className: "absolute -top-2 right-2 rounded-full bg-foreground px-1.5 py-0.5 text-[10px] leading-none font-semibold text-background shadow-sm",
147
+ children: "Last used"
148
+ }) : null
149
+ ]
150
+ }, method.id);
151
+ };
152
+ return /* @__PURE__ */ jsxs("div", {
153
+ className: twMerge("flex w-full flex-col gap-3", className),
154
+ children: [
155
+ wallet ? renderButton(wallet, true) : null,
156
+ wallet && others.length > 0 ? /* @__PURE__ */ jsxs("div", {
157
+ className: "my-1 flex items-center gap-3",
158
+ children: [
159
+ /* @__PURE__ */ jsx("div", { className: "h-px flex-1 bg-border" }),
160
+ /* @__PURE__ */ jsx("span", {
161
+ className: "text-xs tracking-wider text-muted-foreground uppercase",
162
+ children: "or"
163
+ }),
164
+ /* @__PURE__ */ jsx("div", { className: "h-px flex-1 bg-border" })
165
+ ]
166
+ }) : null,
167
+ others.map((method) => renderButton(method, false)),
168
+ error ? /* @__PURE__ */ jsx("p", {
169
+ className: "text-center text-xs text-destructive",
170
+ children: error
171
+ }) : null
172
+ ]
173
+ });
44
174
  };
45
175
  //#endregion
46
176
  //#region assets/connect-loader.mp4
@@ -334,7 +464,7 @@ function Combobox(props) {
334
464
  });
335
465
  }
336
466
  //#endregion
337
- //#region src/connect/components/cookie-banner/cookie-banner.tsx
467
+ //#region src/connect/components/consent-banner/consent-banner.tsx
338
468
  const CookieBanner = (props) => {
339
469
  const { children, cookies, submitLabel, rejectLabel, onSubmit = () => {}, onReject = () => {}, className, ...divProps } = props;
340
470
  const [cookiesValue, setCookiesValue] = useState(cookies);
@@ -574,8 +704,17 @@ function fixedForwardRef(render) {
574
704
  return /* @__PURE__ */ forwardRef(render);
575
705
  }
576
706
  const ConnectSelect = /* @__PURE__ */ fixedForwardRef(function Select(props, ref) {
577
- const { items, value, id, onChange, containerClassName, menuClassName, itemClassName, absolutePositionMenu = false, borderRadius = "6px" } = props;
707
+ const { items, value, id, onChange, containerClassName, menuClassName, itemClassName, absolutePositionMenu = false, borderRadius = "6px", listClassName } = props;
578
708
  const [showItems, setShowItems] = useState(false);
709
+ const containerRef = useRef(null);
710
+ useEffect(() => {
711
+ if (!showItems) return;
712
+ function onPointerDown(event) {
713
+ if (!containerRef.current?.contains(event.target)) setShowItems(false);
714
+ }
715
+ window.addEventListener("pointerdown", onPointerDown);
716
+ return () => window.removeEventListener("pointerdown", onPointerDown);
717
+ }, [showItems]);
579
718
  const selectedItem = getItemByValue(value) ?? items[0];
580
719
  function onItemClick(item) {
581
720
  if (item.disabled) return;
@@ -589,7 +728,11 @@ const ConnectSelect = /* @__PURE__ */ fixedForwardRef(function Select(props, ref
589
728
  return /* @__PURE__ */ jsxs("div", {
590
729
  className: twMerge("border border-border bg-background text-foreground transition-[border-radius]", absolutePositionMenu && "relative", containerClassName),
591
730
  "data-open": showItems,
592
- ref,
731
+ ref: (node) => {
732
+ containerRef.current = node;
733
+ if (typeof ref === "function") ref(node);
734
+ else if (ref) ref.current = node;
735
+ },
593
736
  style: { borderRadius },
594
737
  children: [/* @__PURE__ */ jsxs("div", {
595
738
  className: twMerge("flex min-w-[360px] cursor-pointer items-center justify-between pr-3 text-foreground outline-none", menuClassName),
@@ -603,7 +746,7 @@ const ConnectSelect = /* @__PURE__ */ fixedForwardRef(function Select(props, ref
603
746
  name: "ChevronDown"
604
747
  })]
605
748
  }), /* @__PURE__ */ jsx("div", {
606
- className: twMerge("max-h-0 w-full overflow-hidden bg-inherit transition-[max-height] ease-in-out", showItems && "max-h-screen", absolutePositionMenu && "absolute"),
749
+ className: twMerge("max-h-0 w-full overflow-hidden bg-inherit transition-[max-height] ease-in-out", showItems && "max-h-screen", absolutePositionMenu && "absolute", showItems && listClassName),
607
750
  style: { borderRadius: `0 0 ${borderRadius} ${borderRadius}` },
608
751
  children: itemsToShow.map((item) => /* @__PURE__ */ jsx(ItemContainer, {
609
752
  ...item,
@@ -616,7 +759,7 @@ const ConnectSelect = /* @__PURE__ */ fixedForwardRef(function Select(props, ref
616
759
  function ItemContainer(props) {
617
760
  const { className, disabled, onItemClick, icon, displayValue, value, description } = props;
618
761
  return /* @__PURE__ */ jsxs("div", {
619
- className: twMerge(disabled ? "cursor-not-allowed text-muted-foreground" : "text-foreground", "flex size-full cursor-pointer items-center gap-2 bg-inherit py-3 pl-3 text-start outline-none", className),
762
+ className: twMerge(disabled ? "cursor-not-allowed text-muted-foreground" : "text-foreground", "flex size-full cursor-pointer items-center gap-2 bg-inherit py-3 pl-3 text-start outline-none", onItemClick && !disabled && "hover:bg-muted", className),
620
763
  onClick: onItemClick,
621
764
  children: [icon, /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("p", {
622
765
  className: "text-inherit capitalize",
@@ -1643,33 +1786,35 @@ function TabContent(props) {
1643
1786
  //#endregion
1644
1787
  //#region src/connect/components/tabs/tabs.tsx
1645
1788
  const toTabValue = (label) => label.trim().replace(/\s+/g, "-");
1646
- function Tabs({ children, defaultValue }) {
1789
+ const resolveTabValue = (props) => props.value ?? toTabValue(props.label);
1790
+ function Tabs({ children, defaultValue, onValueChange, listClassName }) {
1647
1791
  return /* @__PURE__ */ jsxs(Root$2, {
1648
1792
  defaultValue: toTabValue(defaultValue),
1793
+ onValueChange,
1649
1794
  className: "flex min-h-0 flex-1 flex-col gap-2",
1650
1795
  children: [/* @__PURE__ */ jsx("div", {
1651
- className: "flex w-full shrink-0 justify-between",
1796
+ className: twMerge("flex w-full shrink-0 justify-between", listClassName),
1652
1797
  children: /* @__PURE__ */ jsx(List, {
1653
1798
  className: "flex w-full gap-x-2 rounded-xl p-1 text-sm font-semibold text-foreground outline-none",
1654
1799
  children: React.Children.map(children, (child, _i) => {
1655
1800
  if (!/* @__PURE__ */ React.isValidElement(child)) return;
1656
- const { label, disabled } = child.props;
1801
+ const props = child.props;
1802
+ const value = resolveTabValue(props);
1657
1803
  return /* @__PURE__ */ jsx(Trigger$2, {
1658
1804
  className: "flex min-h-7 flex-1 items-center justify-center rounded-lg border border-border bg-background py-2 text-foreground transition duration-300 data-disabled:disabled-effect data-[state=active]:bg-accent data-[state=active]:text-foreground",
1659
- value: toTabValue(label),
1660
- disabled: disabled ?? false,
1661
- children: label
1662
- }, label);
1805
+ value,
1806
+ disabled: props.disabled ?? false,
1807
+ children: props.label
1808
+ }, value);
1663
1809
  })
1664
1810
  })
1665
1811
  }), /* @__PURE__ */ jsx("div", {
1666
1812
  className: "mt-3 min-h-0 flex-1 rounded-md bg-background",
1667
1813
  children: React.Children.map(children, (child, i) => {
1668
1814
  if (!/* @__PURE__ */ React.isValidElement(child)) return;
1669
- const { label } = child.props;
1670
1815
  return /* @__PURE__ */ jsx(Content$2, {
1671
1816
  className: "h-full",
1672
- value: toTabValue(label),
1817
+ value: resolveTabValue(child.props),
1673
1818
  children: child
1674
1819
  }, i);
1675
1820
  })
@@ -2189,6 +2334,33 @@ function ToolbarCloseButton(props) {
2189
2334
  });
2190
2335
  }
2191
2336
  /**
2337
+ * Toolbar control for upgrading the document to the latest installed
2338
+ * document model version. Renders nothing when the document is already
2339
+ * current or no upgrade path is registered.
2340
+ */
2341
+ function ToolbarUpgradeButton(props) {
2342
+ const { className, onClick: onClickOverride, document, children } = props;
2343
+ const versionStatus = useDocumentVersionStatus(document);
2344
+ const runUpgrade = () => {
2345
+ if (document) showPHModal({
2346
+ type: "confirmDocumentUpgrade",
2347
+ documentId: document.header.id
2348
+ });
2349
+ };
2350
+ const onClick = makeOnClick(document, onClickOverride, runUpgrade);
2351
+ if (!versionStatus || versionStatus.kind !== "upgrade-available" || !versionStatus.canUpgrade) return null;
2352
+ return /* @__PURE__ */ jsx(ToolbarButton, {
2353
+ "data-testid": "toolbar-upgrade-button",
2354
+ "aria-label": "Update document",
2355
+ className,
2356
+ onClick,
2357
+ children: children ?? /* @__PURE__ */ jsx("span", {
2358
+ className: "px-1 text-xs",
2359
+ children: "Update available"
2360
+ })
2361
+ });
2362
+ }
2363
+ /**
2192
2364
  * Creates a toolbar button click handler.
2193
2365
  *
2194
2366
  * If an override is provided, it is called with the current document. Otherwise,
@@ -2273,6 +2445,7 @@ const defaultControlSlots = {
2273
2445
  ],
2274
2446
  second: ["name"],
2275
2447
  third: [
2448
+ "update",
2276
2449
  "history",
2277
2450
  "switchboard",
2278
2451
  "close"
@@ -2303,6 +2476,7 @@ const defaultControlComponents = {
2303
2476
  redo: ToolbarRedoButton,
2304
2477
  download: ToolbarDownloadButton,
2305
2478
  name: ToolbarName,
2479
+ update: ToolbarUpgradeButton,
2306
2480
  switchboard: ToolbarSwitchboardButton,
2307
2481
  history: ToolbarHistoryButton,
2308
2482
  close: ToolbarCloseButton
@@ -2441,11 +2615,6 @@ function ControlsContainerSlot(props) {
2441
2615
  }
2442
2616
  //#endregion
2443
2617
  //#region src/connect/components/drive-auth-gate/drive-auth-gate.tsx
2444
- /**
2445
- * Reusable "log in to access this drive" card. Used both as the content of the
2446
- * drive-add auth modal and as the full-page gate shown when the user logs out
2447
- * while inside a protected drive — so the two stay visually identical.
2448
- */
2449
2618
  function DriveAuthGate(props) {
2450
2619
  const { mode = "login", onLogin, onLogout, className } = props;
2451
2620
  return /* @__PURE__ */ jsx("div", {
@@ -3129,7 +3298,7 @@ function DropZoneWrapper({ children, ...props }) {
3129
3298
  const selectedDriveId = useSelectedDriveId();
3130
3299
  const onDropFile = useOnDropFile();
3131
3300
  const onAddFile = async (file, parent, onProgress, resolveConflict) => {
3132
- return await onDropFile(file, onProgress, resolveConflict);
3301
+ return await onDropFile(file, parent, onProgress, resolveConflict);
3133
3302
  };
3134
3303
  if (!isDragAndDropEnabled || !selectedDriveId) return /* @__PURE__ */ jsx(Fragment$1, { children });
3135
3304
  return /* @__PURE__ */ jsx(DropZone, {
@@ -3258,6 +3427,7 @@ function ENSAvatar(props) {
3258
3427
  //#endregion
3259
3428
  //#region src/connect/constants/options.tsx
3260
3429
  const defaultDriveOptions = [
3430
+ "DOWNLOAD",
3261
3431
  "NEW_FOLDER",
3262
3432
  "RENAME",
3263
3433
  "SETTINGS"
@@ -3359,6 +3529,13 @@ const debugNodeOptionsMap = {
3359
3529
  }
3360
3530
  };
3361
3531
  const folderNodeDropdownOptions = {
3532
+ DOWNLOAD: {
3533
+ label: "Download as zip",
3534
+ icon: /* @__PURE__ */ jsx(Icon, {
3535
+ name: "DownloadFile",
3536
+ size: 16
3537
+ })
3538
+ },
3362
3539
  DUPLICATE: {
3363
3540
  label: "Duplicate",
3364
3541
  icon: /* @__PURE__ */ jsx(Icon, {
@@ -3383,14 +3560,14 @@ const folderNodeDropdownOptions = {
3383
3560
  }
3384
3561
  };
3385
3562
  const fileNodeDropdownOptions = {
3563
+ ...folderNodeDropdownOptions,
3386
3564
  DOWNLOAD: {
3387
3565
  label: "Download",
3388
3566
  icon: /* @__PURE__ */ jsx(Icon, {
3389
3567
  name: "DownloadFile",
3390
3568
  size: 16
3391
3569
  })
3392
- },
3393
- ...folderNodeDropdownOptions
3570
+ }
3394
3571
  };
3395
3572
  //#endregion
3396
3573
  //#region src/connect/constants/syncing.ts
@@ -3714,6 +3891,7 @@ function FolderItem(props) {
3714
3891
  const { isDropTarget, ...dropProps } = useDropNode(folderNode.id);
3715
3892
  const { onRenameNode, onRenameDriveNodes, onDuplicateNode } = useNodeActions();
3716
3893
  const isReadMode = mode === "READ";
3894
+ const downloadFolder = useDownloadFolder(folderNode);
3717
3895
  function onCancel() {
3718
3896
  setMode("READ");
3719
3897
  }
@@ -3725,6 +3903,7 @@ function FolderItem(props) {
3725
3903
  });
3726
3904
  }
3727
3905
  const dropdownMenuHandlers = {
3906
+ DOWNLOAD: downloadFolder,
3728
3907
  DUPLICATE: () => onDuplicateNode(folderNode),
3729
3908
  RENAME: () => setMode("WRITE"),
3730
3909
  DELETE: () => showDeleteNodeModal(folderNode)
@@ -4662,19 +4841,19 @@ function AddDriveModal(props) {
4662
4841
  }
4663
4842
  //#endregion
4664
4843
  //#region src/connect/components/modal/create-document-modal.tsx
4665
- const CLOSE_ANIMATION_DURATION = 300;
4844
+ const CLOSE_ANIMATION_DURATION$1 = 300;
4666
4845
  function CreateDocumentModal(props) {
4667
4846
  const { onOpenChange, onContinue, overlayProps, contentProps, ...restProps } = props;
4668
4847
  const [nodeName, setNodeName] = useState("");
4669
4848
  const [isValid, setIsValid] = useState(false);
4670
4849
  const handleCancel = () => {
4671
4850
  onOpenChange?.(false);
4672
- setTimeout(() => setNodeName(""), CLOSE_ANIMATION_DURATION);
4851
+ setTimeout(() => setNodeName(""), CLOSE_ANIMATION_DURATION$1);
4673
4852
  };
4674
4853
  const handleCreate = useCallback(() => {
4675
4854
  if (!isValid) return;
4676
4855
  onContinue(nodeName);
4677
- setTimeout(() => setNodeName(""), CLOSE_ANIMATION_DURATION);
4856
+ setTimeout(() => setNodeName(""), CLOSE_ANIMATION_DURATION$1);
4678
4857
  }, [
4679
4858
  isValid,
4680
4859
  nodeName,
@@ -4737,6 +4916,135 @@ function CreateDocumentModal(props) {
4737
4916
  });
4738
4917
  }
4739
4918
  //#endregion
4919
+ //#region src/connect/components/modal/create-document-with-type-modal.tsx
4920
+ const CLOSE_ANIMATION_DURATION = 300;
4921
+ const PLACEHOLDER_KEY = "";
4922
+ function optionKey(option) {
4923
+ return `${option.documentType}::${option.version ?? "latest"}`;
4924
+ }
4925
+ function optionDisplayName(option) {
4926
+ return option.version ? `${option.name} v${option.version}` : option.name;
4927
+ }
4928
+ function CreateDocumentWithTypeModal(props) {
4929
+ const { documentTypes, onCreate, onTypeSelected, onOpenChange, overlayProps, contentProps, ...restProps } = props;
4930
+ const [documentName, setDocumentName] = useState("");
4931
+ const [isNameValid, setIsNameValid] = useState(false);
4932
+ const [selectedKey, setSelectedKey] = useState(PLACEHOLDER_KEY);
4933
+ const selectedOption = documentTypes.find((option) => optionKey(option) === selectedKey);
4934
+ const canCreate = isNameValid && selectedOption !== void 0;
4935
+ const typeItems = documentTypes.map((option) => ({
4936
+ value: optionKey(option),
4937
+ displayValue: optionDisplayName(option)
4938
+ }));
4939
+ const items = selectedKey === PLACEHOLDER_KEY ? [{
4940
+ value: PLACEHOLDER_KEY,
4941
+ displayValue: "Select document type…"
4942
+ }, ...typeItems] : typeItems;
4943
+ const resetAfterClose = useCallback(() => {
4944
+ setTimeout(() => {
4945
+ setDocumentName("");
4946
+ setIsNameValid(false);
4947
+ setSelectedKey(PLACEHOLDER_KEY);
4948
+ }, CLOSE_ANIMATION_DURATION);
4949
+ }, []);
4950
+ const handleOpenChange = (open) => {
4951
+ onOpenChange?.(open);
4952
+ if (!open) resetAfterClose();
4953
+ };
4954
+ const handleCancel = () => {
4955
+ handleOpenChange(false);
4956
+ };
4957
+ const handleTypeChange = (value) => {
4958
+ if (value === PLACEHOLDER_KEY) return;
4959
+ setSelectedKey(value);
4960
+ const option = documentTypes.find((o) => optionKey(o) === value);
4961
+ if (option) onTypeSelected?.(option.documentType);
4962
+ };
4963
+ const handleCreate = useCallback(() => {
4964
+ if (!canCreate) return;
4965
+ onCreate({
4966
+ name: documentName,
4967
+ documentType: selectedOption.documentType,
4968
+ version: selectedOption.version
4969
+ });
4970
+ resetAfterClose();
4971
+ }, [
4972
+ canCreate,
4973
+ documentName,
4974
+ onCreate,
4975
+ resetAfterClose,
4976
+ selectedOption
4977
+ ]);
4978
+ const handleSubmit = useCallback((e) => {
4979
+ e.preventDefault();
4980
+ handleCreate();
4981
+ }, [handleCreate]);
4982
+ return /* @__PURE__ */ jsx(Modal, {
4983
+ contentProps,
4984
+ onOpenChange: handleOpenChange,
4985
+ overlayProps,
4986
+ title: "Create a new document",
4987
+ ...restProps,
4988
+ children: /* @__PURE__ */ jsxs("form", {
4989
+ name: "create-document-with-type",
4990
+ className: "max-h-[85vh] w-100 overflow-y-auto rounded-xl bg-background p-6 text-foreground",
4991
+ onSubmit: handleSubmit,
4992
+ children: [
4993
+ /* @__PURE__ */ jsx("div", {
4994
+ className: "pb-2 text-2xl font-bold text-foreground",
4995
+ children: "Create a new document"
4996
+ }),
4997
+ /* @__PURE__ */ jsxs("div", {
4998
+ className: "my-6",
4999
+ children: [!isNameValid && documentName ? /* @__PURE__ */ jsx("div", {
5000
+ className: "mb-2 text-destructive",
5001
+ children: "Document name must not be empty or contain control characters."
5002
+ }) : null, /* @__PURE__ */ jsx(FormInput, {
5003
+ icon: /* @__PURE__ */ jsx(Icon, { name: "BrickGlobe" }),
5004
+ onChange: (e) => {
5005
+ const name = e.target.value;
5006
+ setDocumentName(name);
5007
+ setIsNameValid(isValidName(name));
5008
+ },
5009
+ placeholder: "Document name",
5010
+ required: true,
5011
+ value: documentName
5012
+ })]
5013
+ }),
5014
+ /* @__PURE__ */ jsxs("div", {
5015
+ className: "my-6",
5016
+ children: [/* @__PURE__ */ jsx(Label, {
5017
+ className: "mb-2 text-sm font-medium text-foreground",
5018
+ htmlFor: "document-type",
5019
+ children: "Document type"
5020
+ }), /* @__PURE__ */ jsx(ConnectSelect, {
5021
+ id: "document-type",
5022
+ items,
5023
+ listClassName: "max-h-80 overflow-y-auto overscroll-contain",
5024
+ menuClassName: "min-w-0",
5025
+ onChange: handleTypeChange,
5026
+ value: selectedKey
5027
+ })]
5028
+ }),
5029
+ /* @__PURE__ */ jsxs("div", {
5030
+ className: "mt-8 flex justify-between gap-3",
5031
+ children: [/* @__PURE__ */ jsx(ModalButton, {
5032
+ onClick: handleCancel,
5033
+ type: "button",
5034
+ variant: "cancel",
5035
+ children: "Cancel"
5036
+ }), /* @__PURE__ */ jsx(ModalButton, {
5037
+ disabled: !canCreate,
5038
+ type: "submit",
5039
+ variant: "confirm",
5040
+ children: "Create"
5041
+ })]
5042
+ })
5043
+ ]
5044
+ })
5045
+ });
5046
+ }
5047
+ //#endregion
4740
5048
  //#region src/connect/components/modal/delete-drive-modal.tsx
4741
5049
  const ConnectDeleteDriveModal = (props) => {
4742
5050
  const { inputPlaceholder, body, driveName, ...confirmationModalProps } = props;
@@ -6814,6 +7122,10 @@ function WorkerInspector({ getInfo, onRestart }) {
6814
7122
  /* @__PURE__ */ jsx(Field, {
6815
7123
  label: "RPC protocol",
6816
7124
  value: String(info.rpcProtocolVersion)
7125
+ }),
7126
+ /* @__PURE__ */ jsx(Field, {
7127
+ label: "Enforcement flags",
7128
+ value: info.featureFlags === void 0 || info.featureFlags === "" ? "none" : info.featureFlags
6817
7129
  })
6818
7130
  ]
6819
7131
  })]
@@ -7373,6 +7685,76 @@ function buildPackageSpec(name, tag) {
7373
7685
  return tag ? `${name}@${tag}` : name;
7374
7686
  }
7375
7687
  //#endregion
7688
+ //#region src/connect/components/modal/settings-modal-v2/package-manager/filter-packages.ts
7689
+ /**
7690
+ * Client-side package filter used by both Package Manager tabs. Matches the
7691
+ * query's name part (any `@tag` suffix is ignored for matching — it drives
7692
+ * version preselection instead) against the package name only.
7693
+ *
7694
+ * This is the seam a future registry search endpoint replaces: when the
7695
+ * Available tab searches server-side, its panel skips this function and
7696
+ * renders the API results as-is.
7697
+ */
7698
+ function filterPackages(packages, query) {
7699
+ const { name } = parsePackageSpec(query);
7700
+ const needle = name.toLowerCase();
7701
+ if (!needle) return packages;
7702
+ return packages.filter((pkg) => pkg.name.toLowerCase().includes(needle));
7703
+ }
7704
+ //#endregion
7705
+ //#region src/connect/components/modal/settings-modal-v2/package-manager/npm-fallback-row.tsx
7706
+ /**
7707
+ * Shown in the Available tab when the search query matches nothing in the
7708
+ * registry but looks like a valid npm package name: the registry's verdaccio
7709
+ * backend proxies npmjs.org, so the install can still succeed. Replaces the
7710
+ * old SearchAutocomplete fallback option. The button label is "Install from
7711
+ * npm" (not "Install") so it doesn't collide with the row-kebab menu item.
7712
+ */
7713
+ const NpmFallbackRow = (props) => {
7714
+ const { query, onInstall, disabled } = props;
7715
+ const [isInstalling, setIsInstalling] = useState(false);
7716
+ const { name, tag } = parsePackageSpec(query);
7717
+ const spec = buildPackageSpec(name, tag);
7718
+ function handleInstall() {
7719
+ setIsInstalling(true);
7720
+ onInstall(spec).catch(console.error).finally(() => setIsInstalling(false));
7721
+ }
7722
+ return /* @__PURE__ */ jsxs("div", {
7723
+ className: "flex items-start justify-between gap-3 rounded-md border border-dashed border-border bg-background p-3 text-sm/5",
7724
+ children: [/* @__PURE__ */ jsxs("div", {
7725
+ className: "min-w-0 flex-1",
7726
+ children: [
7727
+ /* @__PURE__ */ jsx("p", {
7728
+ className: "truncate font-semibold text-foreground",
7729
+ children: tag ? `${name} @ ${tag}` : name
7730
+ }),
7731
+ /* @__PURE__ */ jsx("p", {
7732
+ className: "text-xs text-foreground",
7733
+ children: "Not published to this registry. Install via the npmjs.org uplink."
7734
+ }),
7735
+ /* @__PURE__ */ jsx("p", {
7736
+ className: "text-xs text-muted-foreground",
7737
+ children: "npm fallback"
7738
+ })
7739
+ ]
7740
+ }), /* @__PURE__ */ jsx("div", {
7741
+ className: "shrink-0 self-center",
7742
+ children: /* @__PURE__ */ jsx("button", {
7743
+ onClick: handleInstall,
7744
+ disabled: disabled || isInstalling,
7745
+ className: "rounded-md bg-primary px-3 py-1 text-xs font-medium text-primary-foreground transition-colors hover:hover-effect disabled:disabled-effect",
7746
+ children: isInstalling ? "Installing..." : "Install from npm"
7747
+ })
7748
+ })]
7749
+ });
7750
+ };
7751
+ //#endregion
7752
+ //#region src/connect/components/modal/settings-modal-v2/package-manager/npm-name.ts
7753
+ const NPM_NAME_RE = /^@[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._-]*$|^[a-z0-9][a-z0-9._-]*$/i;
7754
+ function isPlausiblePackageName(name) {
7755
+ return name.length >= 2 && NPM_NAME_RE.test(name);
7756
+ }
7757
+ //#endregion
7376
7758
  //#region src/connect/components/modal/settings-modal-v2/package-manager/version-picker.tsx
7377
7759
  function resolveDefaultVersionSelection(options) {
7378
7760
  const { distTags, versions, version, preferredTag } = options;
@@ -7516,147 +7898,6 @@ const VersionPicker = (props) => {
7516
7898
  });
7517
7899
  };
7518
7900
  //#endregion
7519
- //#region src/connect/components/modal/settings-modal-v2/package-manager/package-manager-input.tsx
7520
- const NPM_NAME_RE = /^@[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._-]*$|^[a-z0-9][a-z0-9._-]*$/i;
7521
- function isPlausiblePackageName(name) {
7522
- return name.length >= 2 && NPM_NAME_RE.test(name);
7523
- }
7524
- function PackageResultCard(props) {
7525
- const { option, ctx, typedTag } = props;
7526
- const { selectingValue, selectLabel, selectingContent, handleSelect } = ctx;
7527
- const baseName = option.label.split(" @ ")[0] ?? option.value;
7528
- const hasVersionMetadata = option.distTags && Object.keys(option.distTags).length > 0 || (option.versions?.length ?? 0) > 0;
7529
- const [selected, setSelected] = useState(() => resolveDefaultVersionSelection({
7530
- distTags: option.distTags,
7531
- versions: option.versions,
7532
- version: option.version,
7533
- preferredTag: typedTag
7534
- }));
7535
- useEffect(() => {
7536
- if (!typedTag) return;
7537
- if (option.distTags && typedTag in option.distTags) setSelected({
7538
- kind: "tag",
7539
- value: typedTag
7540
- });
7541
- else if (option.versions?.includes(typedTag)) setSelected({
7542
- kind: "version",
7543
- value: typedTag
7544
- });
7545
- }, [
7546
- typedTag,
7547
- option.distTags,
7548
- option.versions
7549
- ]);
7550
- const installSpec = hasVersionMetadata ? buildPackageSpec(baseName, selected.value) : option.value;
7551
- const isSelecting = selectingValue === installSpec;
7552
- const isDisabled = option.disabled === true;
7553
- return /* @__PURE__ */ jsxs("div", {
7554
- className: "flex items-start justify-between gap-3 rounded-md p-2 hover:hover-effect",
7555
- children: [/* @__PURE__ */ jsxs("div", {
7556
- className: "min-w-0 flex-1",
7557
- children: [
7558
- /* @__PURE__ */ jsx("p", {
7559
- className: "truncate text-sm font-medium text-foreground",
7560
- children: baseName
7561
- }),
7562
- option.description && /* @__PURE__ */ jsx("p", {
7563
- className: "truncate text-xs text-foreground",
7564
- children: option.description
7565
- }),
7566
- option.meta && /* @__PURE__ */ jsx("p", {
7567
- className: "truncate text-xs text-foreground",
7568
- children: option.meta
7569
- }),
7570
- hasVersionMetadata && /* @__PURE__ */ jsx("div", {
7571
- className: "mt-2",
7572
- children: /* @__PURE__ */ jsx(VersionPicker, {
7573
- distTags: option.distTags,
7574
- versions: option.versions,
7575
- selected,
7576
- onChange: setSelected,
7577
- disabled: isDisabled
7578
- })
7579
- })
7580
- ]
7581
- }), /* @__PURE__ */ jsx("div", {
7582
- className: "shrink-0 self-center",
7583
- children: isSelecting && selectingContent ? /* @__PURE__ */ jsx("div", {
7584
- className: "flex items-center justify-center",
7585
- children: selectingContent
7586
- }) : isDisabled ? /* @__PURE__ */ jsx("span", {
7587
- className: "rounded-md bg-muted px-3 py-1 text-xs font-medium text-muted-foreground",
7588
- children: option.disabledLabel ?? "Unavailable"
7589
- }) : /* @__PURE__ */ jsx("button", {
7590
- onClick: () => handleSelect(installSpec),
7591
- disabled: isSelecting,
7592
- className: "rounded-md bg-primary px-3 py-1 text-xs font-medium text-primary-foreground transition-colors hover:hover-effect disabled:disabled-effect",
7593
- children: isSelecting ? "..." : selectLabel
7594
- })
7595
- })]
7596
- });
7597
- }
7598
- const PackageManagerInput = (props) => {
7599
- const { registryPackageList, onInstall, disabled, className } = props;
7600
- const [typedTag, setTypedTag] = useState(void 0);
7601
- const fetchOptions = async (query) => {
7602
- const { name: namePart, tag } = parsePackageSpec(query);
7603
- setTypedTag(tag);
7604
- const needle = namePart.toLowerCase();
7605
- const localOptions = registryPackageList.filter((pkg) => pkg.name.toLowerCase().includes(needle) || pkg.manifest?.description?.toLowerCase().includes(needle)).map((pkg) => {
7606
- const isInstalled = pkg.status === "local-install" || pkg.status === "registry-install";
7607
- return {
7608
- value: pkg.name,
7609
- label: pkg.name,
7610
- version: pkg.version,
7611
- description: pkg.manifest?.description,
7612
- meta: pkg.manifest?.publisher?.name,
7613
- disabled: isInstalled,
7614
- disabledLabel: isInstalled ? "Installed" : void 0,
7615
- distTags: pkg.distTags,
7616
- versions: pkg.versions
7617
- };
7618
- });
7619
- if (!isPlausiblePackageName(namePart) || localOptions.length > 0) return Promise.resolve(localOptions);
7620
- const fallbackOption = {
7621
- value: buildPackageSpec(namePart, tag),
7622
- label: tag ? `${namePart} @ ${tag}` : namePart,
7623
- version: tag,
7624
- description: "Not published to this registry. Install via the npmjs.org uplink.",
7625
- meta: "npm fallback"
7626
- };
7627
- return Promise.resolve([fallbackOption]);
7628
- };
7629
- const handleSelect = useCallback((value) => {
7630
- return onInstall(value);
7631
- }, [onInstall]);
7632
- const renderRow = useCallback((option, ctx) => /* @__PURE__ */ jsx(PackageResultCard, {
7633
- option,
7634
- ctx,
7635
- typedTag
7636
- }), [typedTag]);
7637
- return /* @__PURE__ */ jsxs("div", {
7638
- className,
7639
- children: [/* @__PURE__ */ jsx("h3", {
7640
- className: "mb-4 font-semibold text-foreground",
7641
- children: "Install Package"
7642
- }), /* @__PURE__ */ jsx(SearchAutocomplete, {
7643
- fetchOptions,
7644
- onSelect: handleSelect,
7645
- selectLabel: "Install",
7646
- selectingContent: /* @__PURE__ */ jsx(PackageAnimation, {
7647
- animate: true,
7648
- loop: true,
7649
- color: "#6b7280",
7650
- size: 48
7651
- }),
7652
- placeholder: "Search packages (e.g. my-pkg, my-pkg@dev, my-pkg@1.2.3)...",
7653
- disabled,
7654
- renderRow,
7655
- keepOpenSelector: "[data-version-picker],[data-version-picker-trigger]"
7656
- })]
7657
- });
7658
- };
7659
- //#endregion
7660
7901
  //#region src/connect/components/modal/settings-modal-v2/package-manager/package-manager-list.tsx
7661
7902
  const PackageDetail = ({ label, value }) => {
7662
7903
  return /* @__PURE__ */ jsxs("div", {
@@ -7671,15 +7912,31 @@ const PackageDetail = ({ label, value }) => {
7671
7912
  });
7672
7913
  };
7673
7914
  const PackageManagerListItem = (props) => {
7674
- const { registryPackage, onInstall, onUninstall, className } = props;
7915
+ const { registryPackage, onInstall, onUninstall, preferredTag, className } = props;
7675
7916
  const [isDropdownMenuOpen, setIsDropdownMenuOpen] = useState(false);
7676
7917
  const canPickVersion = registryPackage.status === "available" || registryPackage.status === "dismissed";
7677
7918
  const hasVersionMetadata = registryPackage.distTags && Object.keys(registryPackage.distTags).length > 0 || (registryPackage.versions?.length ?? 0) > 0;
7678
7919
  const [selected, setSelected] = useState(() => resolveDefaultVersionSelection({
7679
7920
  distTags: registryPackage.distTags,
7680
7921
  versions: registryPackage.versions,
7681
- version: registryPackage.version
7922
+ version: registryPackage.version,
7923
+ preferredTag
7682
7924
  }));
7925
+ useEffect(() => {
7926
+ if (!preferredTag) return;
7927
+ if (registryPackage.distTags && preferredTag in registryPackage.distTags) setSelected({
7928
+ kind: "tag",
7929
+ value: preferredTag
7930
+ });
7931
+ else if (registryPackage.versions?.includes(preferredTag)) setSelected({
7932
+ kind: "version",
7933
+ value: preferredTag
7934
+ });
7935
+ }, [
7936
+ preferredTag,
7937
+ registryPackage.distTags,
7938
+ registryPackage.versions
7939
+ ]);
7683
7940
  const installDropdownItem = {
7684
7941
  id: "install",
7685
7942
  label: "Install",
@@ -7716,31 +7973,38 @@ const PackageManagerListItem = (props) => {
7716
7973
  }),
7717
7974
  registryPackage.manifest !== null && (() => {
7718
7975
  const { description, category, publisher } = registryPackage.manifest;
7719
- if (!(description != null || category != null || publisher?.name != null || publisher?.url != null)) return null;
7976
+ const publisherName = publisher?.name;
7977
+ const publisherUrl = publisher?.url;
7978
+ const hasText = (value) => typeof value === "string" && value.trim() !== "";
7979
+ const showDescription = hasText(description);
7980
+ const showCategory = hasText(category);
7981
+ const showPublisher = hasText(publisherName);
7982
+ const showPublisherUrl = hasText(publisherUrl);
7983
+ if (!showDescription && !showCategory && !showPublisher && !showPublisherUrl) return null;
7720
7984
  return /* @__PURE__ */ jsxs(Fragment$1, { children: [
7721
- description != null && /* @__PURE__ */ jsx(PackageDetail, {
7985
+ showDescription && /* @__PURE__ */ jsx(PackageDetail, {
7722
7986
  label: "Description",
7723
7987
  value: description
7724
7988
  }),
7725
- category != null && /* @__PURE__ */ jsx(PackageDetail, {
7989
+ showCategory && /* @__PURE__ */ jsx(PackageDetail, {
7726
7990
  label: "Category",
7727
7991
  value: category
7728
7992
  }),
7729
- publisher?.name != null && /* @__PURE__ */ jsx(PackageDetail, {
7993
+ showPublisher && /* @__PURE__ */ jsx(PackageDetail, {
7730
7994
  label: "Publisher",
7731
- value: publisher.name
7995
+ value: publisherName
7732
7996
  }),
7733
- publisher?.url != null && /* @__PURE__ */ jsx(PackageDetail, {
7997
+ showPublisherUrl && /* @__PURE__ */ jsx(PackageDetail, {
7734
7998
  label: "Publisher URL",
7735
7999
  value: /* @__PURE__ */ jsx("a", {
7736
8000
  className: "underline",
7737
- href: publisher.url,
7738
- children: publisher.url
8001
+ href: publisherUrl,
8002
+ children: publisherUrl
7739
8003
  })
7740
8004
  })
7741
8005
  ] });
7742
8006
  })(),
7743
- /* @__PURE__ */ jsx(ConnectDropdownMenu, {
8007
+ dropdownItems.length > 0 && /* @__PURE__ */ jsx(ConnectDropdownMenu, {
7744
8008
  items: dropdownItems,
7745
8009
  onItemClick: (id) => {
7746
8010
  if (id === "install") {
@@ -7766,141 +8030,46 @@ const PackageManagerListItem = (props) => {
7766
8030
  ]
7767
8031
  });
7768
8032
  };
7769
- const PackageManagerList = (props) => {
7770
- const { className, registryPackageList, onInstall, onUninstall } = props;
7771
- const [maxHeight, setMaxHeight] = useState();
7772
- const locallyInstalledPackages = registryPackageList.filter((p) => p.status === "local-install");
7773
- const registryInstalledPackages = registryPackageList.filter((p) => p.status === "registry-install");
7774
- const availablePackages = registryPackageList.filter((p) => p.status === "available");
7775
- const dismissedPackages = registryPackageList.filter((p) => p.status === "dismissed");
7776
- useLayoutEffect(() => {
7777
- const calculateMaxHeight = () => {
7778
- const availableHeight = window.innerHeight - 516;
7779
- setMaxHeight(Math.max(200, availableHeight));
7780
- };
7781
- calculateMaxHeight();
7782
- window.addEventListener("resize", calculateMaxHeight);
7783
- return () => window.removeEventListener("resize", calculateMaxHeight);
7784
- }, []);
7785
- const hasLocallyInstalled = locallyInstalledPackages.length > 0;
7786
- const hasRegistryInstalled = registryInstalledPackages.length > 0;
7787
- const hasAnyInstalled = hasLocallyInstalled || hasRegistryInstalled;
7788
- const hasAvailable = availablePackages.length > 0;
7789
- const hasDismissed = dismissedPackages.length > 0;
7790
- const installedCount = locallyInstalledPackages.length + registryInstalledPackages.length;
7791
- return /* @__PURE__ */ jsx("div", {
7792
- className: twMerge("flex flex-col items-stretch overflow-hidden", className),
7793
- style: { maxHeight },
7794
- children: /* @__PURE__ */ jsxs("div", {
7795
- className: "flex-1 overflow-y-auto pr-2",
7796
- children: [
7797
- /* @__PURE__ */ jsxs(PackageSection, {
7798
- sectionId: "installed",
7799
- title: "Installed Packages",
7800
- count: installedCount,
7801
- isEmpty: !hasAnyInstalled,
7802
- emptyText: "No packages installed.",
7803
- children: [hasLocallyInstalled && /* @__PURE__ */ jsx(PackageSubSection, {
7804
- title: "Locally installed",
7805
- count: locallyInstalledPackages.length,
7806
- children: /* @__PURE__ */ jsx(PackageList, {
7807
- packages: locallyInstalledPackages,
7808
- onInstall,
7809
- onUninstall
7810
- })
7811
- }), hasRegistryInstalled && /* @__PURE__ */ jsx(PackageSubSection, {
7812
- title: "Installed from registry",
7813
- count: registryInstalledPackages.length,
7814
- children: /* @__PURE__ */ jsx(PackageList, {
7815
- packages: registryInstalledPackages,
7816
- onInstall,
7817
- onUninstall
7818
- })
7819
- })]
7820
- }),
7821
- /* @__PURE__ */ jsx(PackageSection, {
7822
- sectionId: "available",
7823
- title: "Available Packages",
7824
- count: availablePackages.length,
7825
- isEmpty: !hasAvailable,
7826
- emptyText: "No packages available to install.",
7827
- children: /* @__PURE__ */ jsx(PackageList, {
7828
- packages: availablePackages,
7829
- onInstall,
7830
- onUninstall
7831
- })
7832
- }),
7833
- hasDismissed && /* @__PURE__ */ jsx(PackageSection, {
7834
- sectionId: "dismissed",
7835
- title: "Dismissed Packages",
7836
- count: dismissedPackages.length,
7837
- children: /* @__PURE__ */ jsx(PackageList, {
7838
- packages: dismissedPackages,
7839
- onInstall,
7840
- onUninstall
7841
- })
7842
- })
7843
- ]
7844
- })
8033
+ /**
8034
+ * Scroll region shared by both Package Manager tab panels. Fills the space
8035
+ * the panel gives it and scrolls its overflow. This works because the tab
8036
+ * panel sits inside the settings content card, which has a bounded height
8037
+ * (`m-6 ... h-full flex-1 overflow-hidden`); the flex chain down to here is
8038
+ * unbroken (`min-h-0` + `flex-1`/`h-full` at every level), so no
8039
+ * viewport-measuring hack is needed.
8040
+ */
8041
+ const PackagePanelScrollArea = ({ children, className, onReachEnd }) => {
8042
+ const scrollRef = useRef(null);
8043
+ const sentinelRef = useRef(null);
8044
+ const onReachEndRef = useRef(onReachEnd);
8045
+ useEffect(() => {
8046
+ onReachEndRef.current = onReachEnd;
7845
8047
  });
7846
- };
7847
- const STORAGE_KEY = "ph:package-manager:collapsed-sections";
7848
- function loadCollapsedSections() {
7849
- if (typeof window === "undefined") return {};
7850
- try {
7851
- const raw = window.localStorage.getItem(STORAGE_KEY);
7852
- return raw ? JSON.parse(raw) : {};
7853
- } catch {
7854
- return {};
7855
- }
7856
- }
7857
- function useCollapsedSection(sectionId) {
7858
- const [collapsed, setCollapsed] = useState(() => loadCollapsedSections()[sectionId] ?? true);
8048
+ const hasHandler = onReachEnd !== void 0;
7859
8049
  useEffect(() => {
7860
- if (typeof window === "undefined") return;
7861
- try {
7862
- const current = loadCollapsedSections();
7863
- current[sectionId] = collapsed;
7864
- window.localStorage.setItem(STORAGE_KEY, JSON.stringify(current));
7865
- } catch {}
7866
- }, [sectionId, collapsed]);
7867
- return [collapsed, useCallback(() => setCollapsed((prev) => !prev), [])];
7868
- }
7869
- const PackageSection = ({ sectionId, title, count, isEmpty, emptyText, children }) => {
7870
- const [collapsed, toggle] = useCollapsedSection(sectionId);
7871
- const contentId = `package-section-${sectionId}`;
7872
- return /* @__PURE__ */ jsxs("section", {
7873
- className: "mb-6",
7874
- children: [/* @__PURE__ */ jsx("h3", {
7875
- className: "sticky top-0 z-10 mb-3 border-b border-border bg-background text-foreground",
7876
- children: /* @__PURE__ */ jsxs("button", {
7877
- type: "button",
7878
- onClick: toggle,
7879
- "aria-expanded": !collapsed,
7880
- "aria-controls": contentId,
7881
- className: "flex w-full items-center gap-2 pb-2 text-left text-base font-semibold text-foreground hover:hover-effect",
7882
- children: [
7883
- /* @__PURE__ */ jsx(Icon, {
7884
- name: "ChevronDown",
7885
- size: 16,
7886
- className: twMerge("shrink-0 text-foreground transition-transform", collapsed && "-rotate-90")
7887
- }),
7888
- /* @__PURE__ */ jsx("span", { children: title }),
7889
- /* @__PURE__ */ jsx("span", {
7890
- className: "text-xs font-medium text-foreground",
7891
- children: count
7892
- })
7893
- ]
7894
- })
7895
- }), !collapsed && /* @__PURE__ */ jsx("div", {
7896
- id: contentId,
7897
- children: isEmpty ? /* @__PURE__ */ jsx("p", {
7898
- className: "text-sm text-foreground",
7899
- children: emptyText
7900
- }) : /* @__PURE__ */ jsx("div", {
7901
- className: "flex flex-col gap-4",
7902
- children
7903
- })
8050
+ if (!hasHandler) return;
8051
+ const root = scrollRef.current;
8052
+ const sentinel = sentinelRef.current;
8053
+ if (!root || !sentinel) return;
8054
+ const observer = new IntersectionObserver((entries) => {
8055
+ if (entries.some((e) => e.isIntersecting)) onReachEndRef.current?.();
8056
+ }, {
8057
+ root,
8058
+ rootMargin: "200px"
8059
+ });
8060
+ observer.observe(sentinel);
8061
+ return () => observer.disconnect();
8062
+ }, [hasHandler]);
8063
+ return /* @__PURE__ */ jsxs("div", {
8064
+ ref: scrollRef,
8065
+ className: twMerge("min-h-0 flex-1 overflow-y-auto", className),
8066
+ children: [/* @__PURE__ */ jsx("div", {
8067
+ className: "pr-3 pb-3",
8068
+ children
8069
+ }), hasHandler && /* @__PURE__ */ jsx("div", {
8070
+ ref: sentinelRef,
8071
+ "aria-hidden": true,
8072
+ className: "h-px w-full"
7904
8073
  })]
7905
8074
  });
7906
8075
  };
@@ -7917,31 +8086,286 @@ const PackageSubSection = ({ title, count, children }) => {
7917
8086
  })]
7918
8087
  }), children] });
7919
8088
  };
7920
- const PackageList = ({ packages, onInstall, onUninstall }) => {
8089
+ const PackageList = ({ packages, onInstall, onUninstall, preferredTag }) => {
7921
8090
  return /* @__PURE__ */ jsx("ul", {
7922
- className: "flex flex-col items-stretch gap-4 pr-2",
8091
+ className: "flex flex-col items-stretch gap-4",
7923
8092
  children: packages.map((pkg) => /* @__PURE__ */ jsx(PackageManagerListItem, {
7924
8093
  registryPackage: pkg,
7925
8094
  onInstall,
7926
- onUninstall
8095
+ onUninstall,
8096
+ preferredTag
7927
8097
  }, pkg.name))
7928
8098
  });
7929
8099
  };
7930
8100
  //#endregion
8101
+ //#region src/connect/components/modal/settings-modal-v2/package-manager/package-registry-error.tsx
8102
+ /**
8103
+ * Friendly registry-error banner. Never surfaces raw API / network messages —
8104
+ * those stay in the console for debugging.
8105
+ */
8106
+ const PackageRegistryError = ({ variant, onRetry }) => {
8107
+ const isEmpty = variant === "empty";
8108
+ return /* @__PURE__ */ jsxs("div", {
8109
+ className: isEmpty ? "flex items-start gap-3 rounded-lg border border-destructive bg-destructive/10 p-4" : "flex items-start gap-3 rounded-lg border border-destructive bg-destructive/10 px-3 py-2.5",
8110
+ role: "alert",
8111
+ children: [
8112
+ /* @__PURE__ */ jsx(Icon, {
8113
+ name: "Error",
8114
+ size: 16,
8115
+ className: "mt-0.5 shrink-0 text-destructive"
8116
+ }),
8117
+ /* @__PURE__ */ jsxs("div", {
8118
+ className: "min-w-0 flex-1",
8119
+ children: [
8120
+ /* @__PURE__ */ jsx("p", {
8121
+ className: "text-sm font-medium text-destructive",
8122
+ children: isEmpty ? "Couldn't load packages" : "Couldn't refresh packages"
8123
+ }),
8124
+ /* @__PURE__ */ jsx("p", {
8125
+ className: "mt-0.5 text-xs text-muted-foreground",
8126
+ children: isEmpty ? "We couldn't reach the package registry. Check your connection and try again." : "Showing previously loaded packages. Check your connection and try again."
8127
+ }),
8128
+ isEmpty && onRetry && /* @__PURE__ */ jsx("button", {
8129
+ type: "button",
8130
+ onClick: onRetry,
8131
+ className: "mt-3 rounded-md bg-primary px-3 py-1.5 text-xs font-medium text-primary-foreground transition-colors hover:hover-effect",
8132
+ children: "Try again"
8133
+ })
8134
+ ]
8135
+ }),
8136
+ !isEmpty && onRetry && /* @__PURE__ */ jsx("button", {
8137
+ type: "button",
8138
+ onClick: onRetry,
8139
+ className: "shrink-0 self-center rounded-md border border-border bg-background px-2.5 py-1 text-xs font-medium text-foreground transition-colors hover:hover-effect",
8140
+ children: "Try again"
8141
+ })
8142
+ ]
8143
+ });
8144
+ };
8145
+ //#endregion
8146
+ //#region src/connect/components/modal/settings-modal-v2/package-manager/package-search-input.tsx
8147
+ /**
8148
+ * Plain controlled search box shared by the Installed/Available tabs. It only
8149
+ * reports the query — filtering happens in the panels (client-side today, a
8150
+ * registry search endpoint later).
8151
+ */
8152
+ const PackageSearchInput = (props) => {
8153
+ const { value, onChange, placeholder, disabled, className } = props;
8154
+ return /* @__PURE__ */ jsxs("div", {
8155
+ className: twMerge("relative", className),
8156
+ children: [/* @__PURE__ */ jsx(Icon, {
8157
+ name: "Search",
8158
+ size: 14,
8159
+ className: "absolute top-1/2 left-2 -translate-y-1/2 text-muted-foreground"
8160
+ }), /* @__PURE__ */ jsx(Input, {
8161
+ value,
8162
+ onChange: (e) => onChange(e.target.value),
8163
+ placeholder,
8164
+ disabled,
8165
+ className: "pl-7"
8166
+ })]
8167
+ });
8168
+ };
8169
+ //#endregion
8170
+ //#region src/connect/components/modal/settings-modal-v2/package-manager/available-packages-panel.tsx
8171
+ const LoadingRow = ({ label, center }) => /* @__PURE__ */ jsxs("div", {
8172
+ className: `flex items-center gap-2 text-sm text-muted-foreground ${center ? "justify-center" : ""}`,
8173
+ children: [/* @__PURE__ */ jsx(PackageAnimation, {
8174
+ animate: true,
8175
+ loop: true,
8176
+ color: "#6b7280",
8177
+ size: 24
8178
+ }), /* @__PURE__ */ jsx("span", { children: label })]
8179
+ });
8180
+ const AvailablePackagesPanel = (props) => {
8181
+ const { packages, installedNames, query, onQueryChange, serverSearch, isLoading, isLoadingMore, hasMore, onLoadMore, error, onRetry, mutable, onInstall, onUninstall, disabled } = props;
8182
+ const filtered = serverSearch ? packages : filterPackages(packages, query);
8183
+ const available = filtered.filter((p) => p.status === "available");
8184
+ const dismissed = filtered.filter((p) => p.status === "dismissed");
8185
+ const { name: queryName, tag: queryTag } = parsePackageSpec(query);
8186
+ const showNpmFallback = mutable && filtered.length === 0 && isPlausiblePackageName(queryName) && !installedNames.includes(queryName);
8187
+ const isEmpty = filtered.length === 0;
8188
+ const paginated = onLoadMore !== void 0;
8189
+ function renderContent() {
8190
+ if (isEmpty && isLoading) return /* @__PURE__ */ jsx(LoadingRow, {
8191
+ label: "Loading packages…",
8192
+ center: true
8193
+ });
8194
+ if (isEmpty && error) return /* @__PURE__ */ jsx(PackageRegistryError, {
8195
+ variant: "empty",
8196
+ onRetry
8197
+ });
8198
+ if (isEmpty) {
8199
+ if (showNpmFallback) return /* @__PURE__ */ jsx(NpmFallbackRow, {
8200
+ query,
8201
+ onInstall,
8202
+ disabled
8203
+ });
8204
+ return /* @__PURE__ */ jsx("p", {
8205
+ className: "text-sm text-foreground",
8206
+ children: query ? "No packages match your search." : "No packages available to install."
8207
+ });
8208
+ }
8209
+ return /* @__PURE__ */ jsxs("div", {
8210
+ className: "flex flex-col gap-4",
8211
+ children: [
8212
+ available.length > 0 && /* @__PURE__ */ jsx(PackageList, {
8213
+ packages: available,
8214
+ onInstall,
8215
+ onUninstall,
8216
+ preferredTag: queryTag
8217
+ }),
8218
+ dismissed.length > 0 && /* @__PURE__ */ jsx(PackageSubSection, {
8219
+ title: "Dismissed",
8220
+ count: dismissed.length,
8221
+ children: /* @__PURE__ */ jsx(PackageList, {
8222
+ packages: dismissed,
8223
+ onInstall,
8224
+ onUninstall,
8225
+ preferredTag: queryTag
8226
+ })
8227
+ }),
8228
+ isLoadingMore && /* @__PURE__ */ jsx(LoadingRow, {
8229
+ label: "Loading more…",
8230
+ center: true
8231
+ }),
8232
+ paginated && !hasMore && !isLoadingMore && /* @__PURE__ */ jsx("p", {
8233
+ className: "py-2 text-center text-sm text-muted-foreground",
8234
+ children: "You’ve reached the end of the list."
8235
+ })
8236
+ ]
8237
+ });
8238
+ }
8239
+ return /* @__PURE__ */ jsxs("div", {
8240
+ className: "flex h-full flex-col gap-3",
8241
+ children: [/* @__PURE__ */ jsxs("div", {
8242
+ className: "pr-3",
8243
+ children: [
8244
+ /* @__PURE__ */ jsx(PackageSearchInput, {
8245
+ value: query,
8246
+ onChange: onQueryChange,
8247
+ placeholder: "Search available packages",
8248
+ disabled
8249
+ }),
8250
+ !isEmpty && isLoading && /* @__PURE__ */ jsx("div", {
8251
+ className: "mt-3",
8252
+ children: /* @__PURE__ */ jsx(LoadingRow, { label: "Refreshing packages…" })
8253
+ }),
8254
+ !isEmpty && error && /* @__PURE__ */ jsx("div", {
8255
+ className: "mt-3",
8256
+ children: /* @__PURE__ */ jsx(PackageRegistryError, {
8257
+ variant: "refresh",
8258
+ onRetry
8259
+ })
8260
+ })
8261
+ ]
8262
+ }), /* @__PURE__ */ jsx(PackagePanelScrollArea, {
8263
+ onReachEnd: hasMore && onLoadMore ? onLoadMore : void 0,
8264
+ children: renderContent()
8265
+ })]
8266
+ });
8267
+ };
8268
+ //#endregion
8269
+ //#region src/connect/components/modal/settings-modal-v2/package-manager/installed-packages-panel.tsx
8270
+ const InstalledPackagesPanel = (props) => {
8271
+ const { packages, query, onQueryChange, onInstall, onUninstall, disabled } = props;
8272
+ const filtered = filterPackages(packages, query);
8273
+ const locallyInstalled = filtered.filter((p) => p.status === "local-install");
8274
+ const registryInstalled = filtered.filter((p) => p.status === "registry-install");
8275
+ return /* @__PURE__ */ jsxs("div", {
8276
+ className: "flex h-full flex-col gap-3",
8277
+ children: [/* @__PURE__ */ jsx("div", {
8278
+ className: "pr-3",
8279
+ children: /* @__PURE__ */ jsx(PackageSearchInput, {
8280
+ value: query,
8281
+ onChange: onQueryChange,
8282
+ placeholder: "Search installed packages",
8283
+ disabled
8284
+ })
8285
+ }), /* @__PURE__ */ jsx(PackagePanelScrollArea, { children: packages.length === 0 ? /* @__PURE__ */ jsx("p", {
8286
+ className: "text-sm text-foreground",
8287
+ children: "No packages installed."
8288
+ }) : filtered.length === 0 ? /* @__PURE__ */ jsx("p", {
8289
+ className: "text-sm text-foreground",
8290
+ children: "No installed packages match your search."
8291
+ }) : /* @__PURE__ */ jsxs("div", {
8292
+ className: "flex flex-col gap-4",
8293
+ children: [locallyInstalled.length > 0 && /* @__PURE__ */ jsx(PackageSubSection, {
8294
+ title: "Locally installed",
8295
+ count: locallyInstalled.length,
8296
+ children: /* @__PURE__ */ jsx(PackageList, {
8297
+ packages: locallyInstalled,
8298
+ onInstall,
8299
+ onUninstall
8300
+ })
8301
+ }), registryInstalled.length > 0 && /* @__PURE__ */ jsx(PackageSubSection, {
8302
+ title: "Installed from registry",
8303
+ count: registryInstalled.length,
8304
+ children: /* @__PURE__ */ jsx(PackageList, {
8305
+ packages: registryInstalled,
8306
+ onInstall,
8307
+ onUninstall
8308
+ })
8309
+ })]
8310
+ }) })]
8311
+ });
8312
+ };
8313
+ //#endregion
7931
8314
  //#region src/connect/components/modal/settings-modal-v2/package-manager/package-manager.tsx
7932
8315
  const PackageManager = (props) => {
7933
- const { registryPackageList, onInstall, onUninstall, mutable, disabled, className } = props;
8316
+ const { installedPackages, availablePackages, isAvailableLoading, isLoadingMoreAvailable, hasMoreAvailable, onLoadMoreAvailable, availableError, onAvailableRetry, onAvailableSearchChange, onInstall, onUninstall, onAvailableTabOpen, initialTab = "installed", mutable, disabled, className } = props;
8317
+ const [installedQuery, setInstalledQuery] = useState("");
8318
+ const [availableQuery, setAvailableQuery] = useState("");
8319
+ useEffect(() => {
8320
+ if (initialTab === "available") onAvailableTabOpen?.();
8321
+ }, []);
8322
+ const installedNames = installedPackages.map((p) => p.name);
7934
8323
  return /* @__PURE__ */ jsxs("div", {
7935
- className: twMerge("flex h-full flex-1 flex-col rounded-lg bg-background p-3 text-foreground", className),
7936
- children: [mutable && /* @__PURE__ */ jsx(PackageManagerInput, {
7937
- className: "mb-4",
7938
- registryPackageList,
7939
- onInstall,
7940
- disabled
7941
- }), /* @__PURE__ */ jsx(PackageManagerList, {
7942
- registryPackageList,
7943
- onUninstall,
7944
- onInstall
8324
+ className: twMerge("flex min-h-0 flex-1 flex-col rounded-lg bg-background pt-3 pl-3 text-foreground", className),
8325
+ children: [/* @__PURE__ */ jsx("h2", {
8326
+ className: "mb-4 shrink-0 pr-3 font-semibold text-foreground",
8327
+ children: "Packages"
8328
+ }), /* @__PURE__ */ jsxs(Tabs, {
8329
+ defaultValue: initialTab,
8330
+ listClassName: "pr-3",
8331
+ onValueChange: (value) => {
8332
+ if (value === "available") onAvailableTabOpen?.();
8333
+ },
8334
+ children: [/* @__PURE__ */ jsx(TabContent, {
8335
+ value: "installed",
8336
+ label: `Installed (${installedPackages.length})`,
8337
+ children: /* @__PURE__ */ jsx(InstalledPackagesPanel, {
8338
+ packages: installedPackages,
8339
+ query: installedQuery,
8340
+ onQueryChange: setInstalledQuery,
8341
+ onInstall,
8342
+ onUninstall,
8343
+ disabled
8344
+ })
8345
+ }), /* @__PURE__ */ jsx(TabContent, {
8346
+ value: "available",
8347
+ label: "Available",
8348
+ children: /* @__PURE__ */ jsx(AvailablePackagesPanel, {
8349
+ packages: availablePackages,
8350
+ installedNames,
8351
+ query: availableQuery,
8352
+ onQueryChange: (q) => {
8353
+ setAvailableQuery(q);
8354
+ onAvailableSearchChange?.(q);
8355
+ },
8356
+ serverSearch: onAvailableSearchChange !== void 0,
8357
+ isLoading: isAvailableLoading,
8358
+ isLoadingMore: isLoadingMoreAvailable,
8359
+ hasMore: hasMoreAvailable,
8360
+ onLoadMore: onLoadMoreAvailable,
8361
+ error: availableError,
8362
+ onRetry: onAvailableRetry,
8363
+ mutable,
8364
+ onInstall,
8365
+ onUninstall,
8366
+ disabled
8367
+ })
8368
+ })]
7945
8369
  })]
7946
8370
  });
7947
8371
  };
@@ -7950,19 +8374,21 @@ const PackageManager = (props) => {
7950
8374
  function SettingsModal(props) {
7951
8375
  const { title, overlayProps, contentProps, onOpenChange, tabs, defaultTab, navFooter, ...restProps } = props;
7952
8376
  const [selectedTab, setSelectedTab] = useState(defaultTab ?? tabs.at(0)?.id);
7953
- const tabsContent = tabs.map((tab) => /* @__PURE__ */ jsx("button", {
7954
- type: "button",
7955
- onClick: () => setSelectedTab(tab.id),
7956
- children: /* @__PURE__ */ jsxs("div", {
7957
- className: twMerge("flex h-9 w-48 cursor-pointer items-center gap-x-2 rounded-md pl-3 text-foreground hover:hover-effect", selectedTab === tab.id ? "bg-background" : "bg-transparent"),
8377
+ const tabsContent = tabs.map((tab) => {
8378
+ const isActive = selectedTab === tab.id;
8379
+ return /* @__PURE__ */ jsxs("button", {
8380
+ type: "button",
8381
+ "aria-current": isActive ? "page" : void 0,
8382
+ onClick: isActive ? void 0 : () => setSelectedTab(tab.id),
8383
+ className: twMerge("flex h-9 w-48 items-center gap-x-2 rounded-md pl-3 text-left text-foreground", isActive ? "cursor-default bg-accent" : "cursor-pointer bg-transparent hover:bg-accent"),
7958
8384
  children: [tab.icon, /* @__PURE__ */ jsx("span", { children: tab.label })]
7959
- })
7960
- }, tab.id));
8385
+ }, tab.id);
8386
+ });
7961
8387
  const SelectedTabComponent = tabs.find((tab) => tab.id === selectedTab)?.content;
7962
8388
  return /* @__PURE__ */ jsxs(Modal, {
7963
8389
  contentProps: {
7964
8390
  ...contentProps,
7965
- className: twMerge("min-h-full w-full max-w-4xl rounded-xl", contentProps?.className)
8391
+ className: twMerge("flex max-h-[calc(100dvh-14rem)] min-h-full w-full max-w-4xl flex-col rounded-xl", contentProps?.className)
7966
8392
  },
7967
8393
  onOpenChange,
7968
8394
  overlayProps: {
@@ -7986,7 +8412,7 @@ function SettingsModal(props) {
7986
8412
  })
7987
8413
  })]
7988
8414
  }), /* @__PURE__ */ jsxs("div", {
7989
- className: "flex flex-1",
8415
+ className: "flex min-h-0 flex-1",
7990
8416
  children: [/* @__PURE__ */ jsxs("div", {
7991
8417
  className: "flex flex-col p-3 pt-6",
7992
8418
  children: [/* @__PURE__ */ jsx("div", {
@@ -7997,7 +8423,7 @@ function SettingsModal(props) {
7997
8423
  children: navFooter
7998
8424
  })]
7999
8425
  }), /* @__PURE__ */ jsx("div", {
8000
- className: "m-6 flex h-full flex-1 flex-col overflow-hidden rounded-lg border border-border bg-background",
8426
+ className: "m-6 flex min-h-0 flex-1 flex-col overflow-hidden rounded-lg border border-border bg-background",
8001
8427
  children: typeof SelectedTabComponent === "function" ? /* @__PURE__ */ jsx(SelectedTabComponent, {}) : SelectedTabComponent
8002
8428
  })]
8003
8429
  })]
@@ -8772,50 +9198,20 @@ var renown_short_hover_default = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA
8772
9198
  //#region assets/renown-short.png
8773
9199
  var renown_short_default = "data:image/png;base64,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";
8774
9200
  //#endregion
8775
- //#region src/connect/components/account-popover/account-popover-login.tsx
8776
- const AccountPopoverLogin = ({ onLogin }) => {
8777
- const [loading, setLoading] = useState(false);
8778
- const allowLogin = !loading && !!onLogin;
8779
- const content = loading ? /* @__PURE__ */ jsx(Icon, {
8780
- name: "Reload",
8781
- size: 14,
8782
- className: "animate-spin"
8783
- }) : /* @__PURE__ */ jsx("span", { children: "Connect" });
8784
- const handleLogin = () => {
8785
- if (onLogin) {
8786
- setLoading(true);
8787
- onLogin();
8788
- }
8789
- };
8790
- return /* @__PURE__ */ jsxs("div", {
8791
- className: "p-4",
8792
- children: [/* @__PURE__ */ jsx("div", {
8793
- className: "mb-4 flex justify-center",
8794
- children: /* @__PURE__ */ jsx("div", {
8795
- className: "flex h-5.5 w-20.75 items-center justify-center overflow-hidden text-foreground",
8796
- children: /* @__PURE__ */ jsx(Icon, {
8797
- name: "RenownLight",
8798
- size: 83
8799
- })
8800
- })
8801
- }), /* @__PURE__ */ jsx("button", {
8802
- onClick: allowLogin ? handleLogin : void 0,
8803
- className: twMerge("mt-4 flex h-7 w-full cursor-pointer items-center justify-center rounded-lg border border-border bg-transparent text-sm text-foreground active:active-effect", allowLogin ? "cursor-pointer" : "cursor-wait"),
8804
- type: "button",
8805
- children: content
8806
- })]
8807
- });
8808
- };
8809
- //#endregion
8810
9201
  //#region src/connect/components/sidebar/sidebar-login.tsx
8811
9202
  const SidebarLogin = ({ onLogin }) => {
8812
- const content = /* @__PURE__ */ jsx(AccountPopoverLogin, { onLogin });
9203
+ const [open, setOpen] = useState(false);
8813
9204
  const { theme } = useTheme();
8814
9205
  const isDark = theme === "dark";
8815
9206
  const src = isDark ? renown_short_dark_default : renown_short_default;
8816
9207
  const hoverSrc = isDark ? renown_short_hover_dark_default : renown_short_hover_default;
8817
9208
  return /* @__PURE__ */ jsx(AccountPopover, {
8818
- content,
9209
+ open,
9210
+ onOpenChange: setOpen,
9211
+ content: /* @__PURE__ */ jsx(AccountPopoverLogin, { onLogin: onLogin ? () => {
9212
+ setOpen(false);
9213
+ onLogin();
9214
+ } : void 0 }),
8819
9215
  children: /* @__PURE__ */ jsxs("div", {
8820
9216
  className: twMerge("group/sidebar-footer flex w-full items-baseline justify-start text-sm/10 font-semibold text-foreground", onLogin ? "cursor-pointer" : "cursor-wait"),
8821
9217
  children: [/* @__PURE__ */ jsx("img", {
@@ -9139,6 +9535,6 @@ const removeSuccessFiles = (files) => {
9139
9535
  return files.filter((file) => file.status !== "SUCCESS");
9140
9536
  };
9141
9537
  //#endregion
9142
- export { About, AccountPopover, AddDriveModal, AddLocalDriveForm, AddRemoteDriveForm, AnimatedLoader, Breadcrumb, Breadcrumbs, CONFLICT, ChannelInspector, Combobox, ConnectConfirmationModal, ConnectDeleteDriveModal, ConnectDeleteItemModal, ConnectDropdownMenu, ConnectReplaceDuplicateModal, ConnectSearchBar, ConnectSelect, ConnectSidebar, ConnectSidebarFooter, ConnectSidebarHeader, ConnectTooltip, ConnectTooltipProvider, ConnectUpgradeDriveModal, ConnectionStateBadge, CookieBanner, CreateDocumentModal, DBExplorer, DangerZone, DebugInspector, DefaultEditor, DefaultEditorLoader, DefaultEditorSelect, DependencyVersions, Disclosure, Divider, DocumentStateViewer, DocumentTimeline, DocumentToolbar, DriveAuthGate, DriveSettingsModal, DropZone, DropZoneWrapper, ENSAvatar, ERROR, EditorActionButtons, EditorUndoRedoButtons, FileItem, FolderItem, Footer, FooterLink, FormInput, FormattedJsonViewer, HomeBackgroundImage, HomeScreen, HomeScreenAddDriveItem, HomeScreenItem, INITIAL_SYNC, InspectorModal, IntegrityInspector, LoadingScreen, LogoAnimation, MISSING, NodeInput, ObjectInspectorModal, PackageInstallModal, PackageManager, PackageManagerInput, PackageManagerList, PackageManagerListItem, ProcessorsInspector, QueueInspector, ReadRequiredModal, RemotesInspector, RevisionHistory, SUCCESS, SYNCING, SettingsModal, SidebarAddDriveItem, SidebarItem, SidebarLogin, SidebarUser, SyncStatusIcon, TabContent, Tabs, ThemeSwitch, Toggle, ToolbarButton, ToolbarCloseButton, ToolbarContainer, ToolbarControlsContainer, ToolbarDownloadButton, ToolbarHistoryButton, ToolbarInput, ToolbarName, ToolbarRedoButton, ToolbarSwitchboardButton, ToolbarUndoButton, UploadFileItem, UploadFileList, UploadFileListContainer, WorkerInspector, debugNodeOptions, debugNodeOptionsMap, defaultDriveOptions, defaultNodeOptions, fileNodeDropdownOptions, folderNodeDropdownOptions, formatEthAddress, getFolderStatus, locationInfoByLocation, nodeOptions, normalNodeOptions, removeSuccessFiles, sharingTypeOptions, sortFilesByStatus, syncStatuses, useEns, verifyPackageJsonFields };
9538
+ export { About, AccountPopover, AccountPopoverLogin, AddDriveModal, AddLocalDriveForm, AddRemoteDriveForm, AnimatedLoader, AvailablePackagesPanel, Breadcrumb, Breadcrumbs, CONFLICT, ChannelInspector, Combobox, ConnectConfirmationModal, ConnectDeleteDriveModal, ConnectDeleteItemModal, ConnectDropdownMenu, ConnectReplaceDuplicateModal, ConnectSearchBar, ConnectSelect, ConnectSidebar, ConnectSidebarFooter, ConnectSidebarHeader, ConnectTooltip, ConnectTooltipProvider, ConnectUpgradeDriveModal, ConnectionStateBadge, CookieBanner, CreateDocumentModal, CreateDocumentWithTypeModal, DBExplorer, DangerZone, DebugInspector, DefaultEditor, DefaultEditorLoader, DefaultEditorSelect, DependencyVersions, Disclosure, Divider, DocumentStateViewer, DocumentTimeline, DocumentToolbar, DriveAuthGate, DriveSettingsModal, DropZone, DropZoneWrapper, ENSAvatar, ERROR, EditorActionButtons, EditorUndoRedoButtons, FileItem, FolderItem, Footer, FooterLink, FormInput, FormattedJsonViewer, HomeBackgroundImage, HomeScreen, HomeScreenAddDriveItem, HomeScreenItem, INITIAL_SYNC, InspectorModal, InstalledPackagesPanel, IntegrityInspector, LoadingScreen, LogoAnimation, MISSING, ModalButton, NodeInput, ObjectInspectorModal, PackageInstallModal, PackageList, PackageManager, PackageManagerListItem, PackagePanelScrollArea, PackageRegistryError, PackageSubSection, ProcessorsInspector, QueueInspector, ReadRequiredModal, RemotesInspector, RenownLoginMethods, RevisionHistory, SUCCESS, SYNCING, SettingsModal, SidebarAddDriveItem, SidebarItem, SidebarLogin, SidebarUser, SyncStatusIcon, TabContent, Tabs, ThemeSwitch, Toggle, ToolbarButton, ToolbarCloseButton, ToolbarContainer, ToolbarControlsContainer, ToolbarDownloadButton, ToolbarHistoryButton, ToolbarInput, ToolbarName, ToolbarRedoButton, ToolbarSwitchboardButton, ToolbarUndoButton, UploadFileItem, UploadFileList, UploadFileListContainer, WorkerInspector, debugNodeOptions, debugNodeOptionsMap, defaultDriveOptions, defaultNodeOptions, fileNodeDropdownOptions, folderNodeDropdownOptions, formatEthAddress, formatFileSize, generateId, getFolderStatus, locationInfoByLocation, mapProgressStageToStatus, mapUploadsToFileItems, nodeOptions, normalNodeOptions, removeSuccessFiles, sharingTypeOptions, sortFilesByStatus, syncStatuses, useEns, verifyPackageJsonFields };
9143
9539
 
9144
9540
  //# sourceMappingURL=index.js.map