@powerhousedao/connect 6.2.3-dev.0 → 6.2.3-dev.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/dist/{AddDriveModal-Q-Ffh5XA.js → AddDriveModal-xAw48kdl.js} +5 -20
  2. package/dist/AddDriveModal-xAw48kdl.js.map +1 -0
  3. package/dist/{ClearStorageModal-Dm2ztjgw.js → ClearStorageModal-BHLFpni1.js} +4 -4
  4. package/dist/{ClearStorageModal-Dm2ztjgw.js.map → ClearStorageModal-BHLFpni1.js.map} +1 -1
  5. package/dist/{DebugSettingsModal-BWOSNOw8.js → DebugSettingsModal-cFjT1zO6.js} +4 -4
  6. package/dist/{DebugSettingsModal-BWOSNOw8.js.map → DebugSettingsModal-cFjT1zO6.js.map} +1 -1
  7. package/dist/{DriveAuthRequiredModal-Cw9eCAQ_.js → DriveAuthRequiredModal-DVK7ZQzO.js} +17 -5
  8. package/dist/DriveAuthRequiredModal-DVK7ZQzO.js.map +1 -0
  9. package/dist/{InspectorModal-ChZE9QbX.js → InspectorModal-Z7YMEHRm.js} +4 -4
  10. package/dist/{InspectorModal-ChZE9QbX.js.map → InspectorModal-Z7YMEHRm.js.map} +1 -1
  11. package/dist/{MissingPackageModal-C7usWVHQ.js → MissingPackageModal-nBnExqO0.js} +4 -4
  12. package/dist/{MissingPackageModal-C7usWVHQ.js.map → MissingPackageModal-nBnExqO0.js.map} +1 -1
  13. package/dist/{OpenFileDocumentsModal-DOwweG6l.js → OpenFileDocumentsModal-JcGCx1ta.js} +4 -4
  14. package/dist/{OpenFileDocumentsModal-DOwweG6l.js.map → OpenFileDocumentsModal-JcGCx1ta.js.map} +1 -1
  15. package/dist/{SettingsModal-DzSC4sh8.js → SettingsModal-CgqnSXs5.js} +8 -7
  16. package/dist/{SettingsModal-DzSC4sh8.js.map → SettingsModal-CgqnSXs5.js.map} +1 -1
  17. package/dist/{app-loader-Dkgf816o.js → app-loader-GDy45Yza.js} +101 -27
  18. package/dist/{app-loader-Dkgf816o.js.map → app-loader-GDy45Yza.js.map} +1 -1
  19. package/dist/app-loader-WZecUfE8.js +4 -0
  20. package/dist/{build-info-BvyVA-pf.js → build-info-BPvkFr0E.js} +5 -5
  21. package/dist/{build-info-BvyVA-pf.js.map → build-info-BPvkFr0E.js.map} +1 -1
  22. package/dist/components-Df4NlQZz.js +4 -0
  23. package/dist/{load-uFhW7yeN.js → load-BJ8uRKR9.js} +5 -5
  24. package/dist/{load-uFhW7yeN.js.map → load-BJ8uRKR9.js.map} +1 -1
  25. package/dist/main.js +1 -1
  26. package/dist/{reactor-DB1Qy6Ib.js → reactor-CbgxQyev.js} +13 -4
  27. package/dist/reactor-CbgxQyev.js.map +1 -0
  28. package/dist/start-connect.js +1 -1
  29. package/dist/style.css +6 -0
  30. package/dist/{useRegistryPackages-DMhCvTWq.js → useRegistryPackages-BnUcL_nZ.js} +81 -5
  31. package/dist/useRegistryPackages-BnUcL_nZ.js.map +1 -0
  32. package/package.json +12 -12
  33. package/dist/AddDriveModal-Q-Ffh5XA.js.map +0 -1
  34. package/dist/DriveAuthRequiredModal-Cw9eCAQ_.js.map +0 -1
  35. package/dist/app-loader-BAbuTvzq.js +0 -4
  36. package/dist/components-VzbfpugJ.js +0 -4
  37. package/dist/reactor-DB1Qy6Ib.js.map +0 -1
  38. package/dist/useRegistryPackages-DMhCvTWq.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"OpenFileDocumentsModal-DOwweG6l.js","sources":["../src/utils/open-file-plan.ts","../src/components/modal/modals/OpenFileDocumentsModal.tsx"],"sourcesContent":["import { isDocumentTypeSupported } from \"@powerhousedao/reactor-browser\";\nimport type { Node } from \"@powerhousedao/shared/document-drive\";\nimport {\n getNextCopyNumber,\n isFileNode,\n} from \"@powerhousedao/shared/document-drive\";\n\n// Pure pre-flight planning for the open-file picker: given the pending files,\n// their parse results and the selected drive's nodes, decide per file whether\n// it can be imported, whether it collides with an existing document, and what\n// name it should be stored under. No reactor access — everything here derives\n// synchronously so the picker can recompute on every drive switch.\n\n/** What the one-time parse of a pending file yielded. */\nexport type ParsedFileInfo =\n | { state: \"parsed\"; id: string; documentType: string; headerName: string }\n // The document model isn't installed: the import path's package\n // auto-discovery may still resolve it, so the file stays importable —\n // but its header is unknown, so no duplicate check is possible.\n | { state: \"model-missing\" }\n // The document needs a newer document model version than any installed\n // module can replay.\n | {\n state: \"version-unsupported\";\n documentType: string;\n requiredVersion: number;\n }\n // Corrupt zip / not a Powerhouse document.\n | { state: \"invalid\" };\n\nexport type PlannedRow =\n | { kind: \"checking\" }\n | { kind: \"invalid\" }\n | {\n kind: \"version-unsupported\";\n documentType: string;\n requiredVersion: number;\n }\n | { kind: \"unsupported\" }\n | {\n kind: \"ready\";\n finalName: string;\n duplicate?: \"id\" | \"name\";\n renamed: boolean;\n };\n\nexport type PlanEntry = {\n /** Stable identity of the pending file (see pendingFileKey). */\n key: string;\n file: File;\n /** undefined while the parse is still in flight. */\n parsed: ParsedFileInfo | undefined;\n};\n\n/** Same name normalization the import stores (use-on-drop-file convention). */\nexport function storedFileName(file: File): string {\n return file.name.replace(/\\..+/gim, \"\");\n}\n\n/**\n * Duplicate semantics mirror the import path's isDocumentInLocation, scoped\n * to the drive ROOT: an id match, or a file node with the same name AND the\n * same document type. Renames follow the drive-tree copy convention\n * (\"name (copy) N\" via getNextCopyNumber). Names assigned within the batch\n * accumulate into the collision set, so two pending files that would store\n * the same name never end up identical either.\n */\nexport function planOpenFileImports(args: {\n entries: PlanEntry[];\n nodes: Node[];\n documentTypes: string[] | undefined;\n}): Map<string, PlannedRow> {\n const { entries, nodes, documentTypes } = args;\n const rootNodes = nodes.filter(\n (node) => (node.parentFolder ?? null) === null,\n );\n const rows = new Map<string, PlannedRow>();\n const assignedNames = new Set<string>();\n\n function uniqueName(base: string, driveNames: string[]): string {\n const collides = driveNames.includes(base) || assignedNames.has(base);\n if (!collides) return base;\n const candidates = [...driveNames, ...assignedNames];\n return `${base} (copy) ${getNextCopyNumber(candidates, base)}`;\n }\n\n for (const { key, file, parsed } of entries) {\n if (!parsed) {\n rows.set(key, { kind: \"checking\" });\n continue;\n }\n if (parsed.state === \"invalid\") {\n rows.set(key, { kind: \"invalid\" });\n continue;\n }\n\n if (parsed.state === \"version-unsupported\") {\n rows.set(key, {\n kind: \"version-unsupported\",\n documentType: parsed.documentType,\n requiredVersion: parsed.requiredVersion,\n });\n continue;\n }\n\n if (parsed.state === \"model-missing\") {\n const base = storedFileName(file) || file.name;\n const finalName = uniqueName(base, []);\n assignedNames.add(finalName);\n rows.set(key, {\n kind: \"ready\",\n finalName,\n renamed: finalName !== base,\n });\n continue;\n }\n\n if (!isDocumentTypeSupported(parsed.documentType, documentTypes)) {\n rows.set(key, { kind: \"unsupported\" });\n continue;\n }\n\n // The stored name falls back to the document's own header name when the\n // file name is extension-only (\".phd\" normalizes to \"\").\n const base = storedFileName(file) || parsed.headerName;\n const sameTypeNames = rootNodes\n .filter(\n (node) => isFileNode(node) && node.documentType === parsed.documentType,\n )\n .map((node) => node.name);\n const idDuplicate = rootNodes.some((node) => node.id === parsed.id);\n const nameDuplicate = sameTypeNames.includes(base);\n\n const finalName = uniqueName(base, sameTypeNames);\n assignedNames.add(finalName);\n rows.set(key, {\n kind: \"ready\",\n finalName,\n duplicate: nameDuplicate ? \"name\" : idDuplicate ? \"id\" : undefined,\n renamed: finalName !== base,\n });\n }\n\n return rows;\n}\n","import { DriveIcon } from \"@powerhousedao/connect/components\";\nimport {\n clearPendingImportFiles,\n getPendingImportFiles,\n pendingFileKey,\n planOpenFileImports,\n runOpenFileImports,\n subscribePendingImportFiles,\n type ParsedFileInfo,\n type PlanEntry,\n type PlannedOpenFileImport,\n} from \"@powerhousedao/connect/utils\";\nimport { Modal } from \"@powerhousedao/design-system\";\nimport {\n Combobox,\n formatFileSize,\n ModalButton,\n} from \"@powerhousedao/design-system/connect\";\nimport {\n closePHModal,\n DocumentModelNotFoundError,\n loadFile,\n setSelectedDrive,\n showPHModal,\n useDocumentTypes,\n useDrives,\n usePHModal,\n useUserPermissions,\n} from \"@powerhousedao/reactor-browser\";\nimport { UnsupportedDocumentModelVersionError } from \"@powerhousedao/shared/document-model\";\nimport { useEffect, useState, useSyncExternalStore } from \"react\";\nimport { useTranslation } from \"react-i18next\";\n\n// Drive picker for documents opened through the OS file association (PWA\n// File Handling). This is the flow's ONLY dialog state: validity and\n// duplicate checks run up-front against the selected drive (inline errors,\n// automatic \"(copy) N\" renames surfaced as \"Rename and import\"), and on\n// confirmation the dialog closes — progress is shown by the floating upload\n// panel (OpenFileUploadList) driven from utils/open-file-uploads.ts, the same\n// UI drag-and-drop uses.\n//\n// The component only mounts while the modal is shown (ModalsContainer keys on\n// the modal type), so picker state starts fresh on every open.\n\n// Parse results are cached at module level so they survive the create-drive\n// detour (which unmounts this component) and are computed once per pending\n// file — loadFile replays the file's full operation history. Only header\n// fields are retained, never the parsed document. In-flight promises are\n// cached too, deduplicating StrictMode double-effects.\nconst parseCache = new Map<string, ParsedFileInfo | Promise<ParsedFileInfo>>();\n\nasync function parsePendingFile(file: File): Promise<ParsedFileInfo> {\n try {\n const document = await loadFile(file);\n return {\n state: \"parsed\",\n id: document.header.id,\n documentType: document.header.documentType,\n headerName: document.header.name,\n };\n } catch (error) {\n if (UnsupportedDocumentModelVersionError.isError(error)) {\n return {\n state: \"version-unsupported\",\n documentType: error.documentType,\n requiredVersion: error.requiredVersion,\n };\n }\n // Missing document model ≠ invalid: the import path's package\n // auto-discovery may still resolve it.\n if (DocumentModelNotFoundError.isError(error)) {\n return { state: \"model-missing\" };\n }\n return { state: \"invalid\" };\n }\n}\n\nexport function OpenFileDocumentsModal() {\n const phModal = usePHModal();\n const open = phModal?.type === \"openFileDocuments\";\n const { t } = useTranslation();\n const drives = useDrives();\n const documentTypes = useDocumentTypes();\n const { isAllowedToCreateDocuments } = useUserPermissions();\n const pendingFiles = useSyncExternalStore(\n subscribePendingImportFiles,\n getPendingImportFiles,\n );\n\n const [pickedDriveId, setPickedDriveId] = useState<string>();\n const [, setParseVersion] = useState(0);\n\n // Kick off (and cache) the one-time parse of each pending file. Gated on\n // the reactor being ready — never cache \"reactor not initialized\" as a\n // parse result; the effect re-runs when files change and drives arriving\n // re-render the modal anyway.\n useEffect(() => {\n if (!window.ph?.reactorClient) return;\n let cancelled = false;\n const pendingKeys = new Set(pendingFiles.map(pendingFileKey));\n for (const key of parseCache.keys()) {\n if (!pendingKeys.has(key)) parseCache.delete(key);\n }\n for (const file of pendingFiles) {\n const key = pendingFileKey(file);\n if (parseCache.has(key)) continue;\n const promise = parsePendingFile(file).then((info) => {\n parseCache.set(key, info);\n return info;\n });\n parseCache.set(key, promise);\n void promise.then(() => {\n if (!cancelled) setParseVersion((version) => version + 1);\n });\n }\n return () => {\n cancelled = true;\n };\n }, [pendingFiles]);\n\n // The first drive is preselected so opening a file is a single\n // confirmation; the select lets the user switch destination.\n const selectedDriveId = pickedDriveId ?? drives?.[0]?.header.id;\n const selectedDrive = drives?.find(\n (drive) => drive.header.id === selectedDriveId,\n );\n\n const driveOptions = (drives ?? []).map((drive) => ({\n // New local drives carry their name only in the global state until the\n // header syncs.\n label: drive.header.name || drive.state.global.name,\n value: drive.header.id,\n drive,\n }));\n type DriveOption = (typeof driveOptions)[number];\n\n // Pure derivations — recomputed on drive switch, on files arriving, and on\n // parses settling.\n const entries: PlanEntry[] = pendingFiles.map((file) => {\n const cached = parseCache.get(pendingFileKey(file));\n return {\n key: pendingFileKey(file),\n file,\n parsed: cached instanceof Promise ? undefined : cached,\n };\n });\n const plan = selectedDrive\n ? planOpenFileImports({\n entries,\n nodes: selectedDrive.state.global.nodes,\n documentTypes,\n })\n : new Map<string, never>();\n\n const planRows = [...plan.values()];\n const readyCount = planRows.filter((row) => row.kind === \"ready\").length;\n const anyChecking = planRows.some((row) => row.kind === \"checking\");\n const anyRenamed = planRows.some(\n (row) => row.kind === \"ready\" && row.renamed,\n );\n\n function onImport() {\n const drive = selectedDrive;\n if (!drive) return;\n const imports: PlannedOpenFileImport[] = [];\n for (const { key, file } of entries) {\n const row = plan.get(key);\n if (row?.kind === \"ready\") imports.push({ file, name: row.finalName });\n }\n if (imports.length === 0) return;\n // Ordering matters: the pending store must be cleared BEFORE the modal\n // closes — the launch-queue listener reopens this picker whenever the\n // modal clears while files are still pending.\n clearPendingImportFiles();\n closePHModal();\n setSelectedDrive(drive);\n void runOpenFileImports({\n driveId: drive.header.id,\n imports,\n documentTypes,\n });\n }\n\n function onCancel() {\n clearPendingImportFiles();\n closePHModal();\n }\n\n if (!open) return null;\n\n const hasDrives = (drives?.length ?? 0) > 0;\n\n function rowAnnotation(key: string) {\n const row = plan.get(key);\n if (!row) return null;\n switch (row.kind) {\n case \"checking\":\n return (\n <span className=\"text-xs text-muted-foreground\">\n {t(\"modals.openFileDocuments.checking\")}\n </span>\n );\n case \"invalid\":\n return (\n <span className=\"text-xs text-destructive\">\n {t(\"modals.openFileDocuments.invalidFile\")}\n </span>\n );\n case \"version-unsupported\":\n return (\n <span className=\"text-xs text-destructive\">\n {`Requires version ${row.requiredVersion} of ${row.documentType} — update the package`}\n </span>\n );\n case \"unsupported\":\n return (\n <span className=\"text-xs text-destructive\">\n {t(\"modals.openFileDocuments.unsupportedType\")}\n </span>\n );\n case \"ready\":\n // \"name\": same name + type already in the drive, so the file is\n // renamed to a copy. \"id\": same document id, imported as a fresh copy\n // under its original name. A row that only got renamed to avoid\n // clashing with another file in THIS batch is NOT in the drive, so it\n // must not claim to be — it gets the neutral rename hint instead.\n if (row.duplicate === \"name\") {\n return (\n <span className=\"text-xs text-muted-foreground\">\n {t(\"modals.openFileDocuments.duplicateHint\", {\n name: row.finalName,\n })}\n </span>\n );\n }\n if (row.duplicate === \"id\") {\n return (\n <span className=\"text-xs text-muted-foreground\">\n {t(\"modals.openFileDocuments.duplicateCopyHint\")}\n </span>\n );\n }\n if (row.renamed) {\n return (\n <span className=\"text-xs text-muted-foreground\">\n {t(\"modals.openFileDocuments.renamedHint\", {\n name: row.finalName,\n })}\n </span>\n );\n }\n return null;\n }\n }\n\n return (\n <Modal\n open={open}\n onOpenChange={(status) => {\n if (!status) onCancel();\n }}\n // The drive select's dropdown must be able to spill past the dialog\n // bounds instead of being clipped (the menu cannot portal to <body>:\n // Radix's modal pointer-events lock would make it unclickable there).\n contentProps={{ className: \"overflow-visible\" }}\n >\n <div className=\"w-[520px] p-6\">\n <div className=\"pb-2 text-2xl font-bold text-foreground\">\n {t(\"modals.openFileDocuments.title\", {\n count: pendingFiles.length,\n })}\n </div>\n\n {!isAllowedToCreateDocuments ? (\n <>\n <div className=\"my-6 rounded-md bg-background p-4 text-center text-foreground\">\n {t(\"modals.openFileDocuments.notAllowed\")}\n </div>\n <div className=\"mt-8 flex\">\n <ModalButton variant=\"cancel\" onClick={onCancel}>\n {t(\"common.cancel\")}\n </ModalButton>\n </div>\n </>\n ) : (\n <>\n <div className=\"my-4 flex flex-col gap-1 text-sm text-foreground\">\n {pendingFiles.map((file, index) => {\n const key = pendingFileKey(file);\n const annotation = rowAnnotation(key);\n return (\n <div\n key={`${index}-${key}`}\n className=\"flex flex-col gap-0.5 rounded-md bg-background px-3 py-2\"\n >\n <div className=\"flex justify-between gap-2\">\n <span className=\"truncate\">{file.name}</span>\n <span className=\"shrink-0 text-muted-foreground\">\n {formatFileSize(file.size)}\n </span>\n </div>\n {annotation}\n </div>\n );\n })}\n </div>\n\n {hasDrives ? (\n <>\n <div className=\"mb-2 text-sm text-muted-foreground\">\n {t(\"modals.openFileDocuments.body\")}\n </div>\n <Combobox\n id=\"open-file-documents-drive\"\n isSearchable={false}\n options={driveOptions}\n value={\n driveOptions.find(\n (option) => option.value === selectedDriveId,\n ) ?? null\n }\n onChange={(option) =>\n setPickedDriveId((option as DriveOption).value)\n }\n formatOptionLabel={(option) => {\n const { label, drive } = option as DriveOption;\n return (\n <span className=\"flex items-center gap-2\">\n <DriveIcon drive={drive} />\n <span className=\"truncate\">{label}</span>\n </span>\n );\n }}\n maxMenuHeight={200}\n />\n </>\n ) : (\n <div className=\"my-6 rounded-md bg-background p-4 text-center text-foreground\">\n {t(\"modals.openFileDocuments.noDrives\")}\n </div>\n )}\n\n <div className=\"mt-8 flex justify-between gap-3\">\n <ModalButton variant=\"cancel\" onClick={onCancel}>\n {t(\"common.cancel\")}\n </ModalButton>\n {hasDrives ? (\n <ModalButton\n variant=\"confirm\"\n disabled={!selectedDriveId || readyCount === 0 || anyChecking}\n onClick={onImport}\n >\n {anyRenamed\n ? t(\"modals.openFileDocuments.renameAndImport\")\n : t(\"modals.openFileDocuments.import\")}\n </ModalButton>\n ) : (\n <ModalButton\n variant=\"confirm\"\n onClick={() => showPHModal({ type: \"addDrive\" })}\n >\n {t(\"modals.openFileDocuments.createDrive\")}\n </ModalButton>\n )}\n </div>\n </>\n )}\n </div>\n </Modal>\n );\n}\n"],"names":[],"mappings":"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","debug_id":"7c78e613-86c6-5626-ab32-025ccbbe991c"}
1
+ {"version":3,"file":"OpenFileDocumentsModal-JcGCx1ta.js","sources":["../src/utils/open-file-plan.ts","../src/components/modal/modals/OpenFileDocumentsModal.tsx"],"sourcesContent":["import { isDocumentTypeSupported } from \"@powerhousedao/reactor-browser\";\nimport type { Node } from \"@powerhousedao/shared/document-drive\";\nimport {\n getNextCopyNumber,\n isFileNode,\n} from \"@powerhousedao/shared/document-drive\";\n\n// Pure pre-flight planning for the open-file picker: given the pending files,\n// their parse results and the selected drive's nodes, decide per file whether\n// it can be imported, whether it collides with an existing document, and what\n// name it should be stored under. No reactor access — everything here derives\n// synchronously so the picker can recompute on every drive switch.\n\n/** What the one-time parse of a pending file yielded. */\nexport type ParsedFileInfo =\n | { state: \"parsed\"; id: string; documentType: string; headerName: string }\n // The document model isn't installed: the import path's package\n // auto-discovery may still resolve it, so the file stays importable —\n // but its header is unknown, so no duplicate check is possible.\n | { state: \"model-missing\" }\n // The document needs a newer document model version than any installed\n // module can replay.\n | {\n state: \"version-unsupported\";\n documentType: string;\n requiredVersion: number;\n }\n // Corrupt zip / not a Powerhouse document.\n | { state: \"invalid\" };\n\nexport type PlannedRow =\n | { kind: \"checking\" }\n | { kind: \"invalid\" }\n | {\n kind: \"version-unsupported\";\n documentType: string;\n requiredVersion: number;\n }\n | { kind: \"unsupported\" }\n | {\n kind: \"ready\";\n finalName: string;\n duplicate?: \"id\" | \"name\";\n renamed: boolean;\n };\n\nexport type PlanEntry = {\n /** Stable identity of the pending file (see pendingFileKey). */\n key: string;\n file: File;\n /** undefined while the parse is still in flight. */\n parsed: ParsedFileInfo | undefined;\n};\n\n/** Same name normalization the import stores (use-on-drop-file convention). */\nexport function storedFileName(file: File): string {\n return file.name.replace(/\\..+/gim, \"\");\n}\n\n/**\n * Duplicate semantics mirror the import path's isDocumentInLocation, scoped\n * to the drive ROOT: an id match, or a file node with the same name AND the\n * same document type. Renames follow the drive-tree copy convention\n * (\"name (copy) N\" via getNextCopyNumber). Names assigned within the batch\n * accumulate into the collision set, so two pending files that would store\n * the same name never end up identical either.\n */\nexport function planOpenFileImports(args: {\n entries: PlanEntry[];\n nodes: Node[];\n documentTypes: string[] | undefined;\n}): Map<string, PlannedRow> {\n const { entries, nodes, documentTypes } = args;\n const rootNodes = nodes.filter(\n (node) => (node.parentFolder ?? null) === null,\n );\n const rows = new Map<string, PlannedRow>();\n const assignedNames = new Set<string>();\n\n function uniqueName(base: string, driveNames: string[]): string {\n const collides = driveNames.includes(base) || assignedNames.has(base);\n if (!collides) return base;\n const candidates = [...driveNames, ...assignedNames];\n return `${base} (copy) ${getNextCopyNumber(candidates, base)}`;\n }\n\n for (const { key, file, parsed } of entries) {\n if (!parsed) {\n rows.set(key, { kind: \"checking\" });\n continue;\n }\n if (parsed.state === \"invalid\") {\n rows.set(key, { kind: \"invalid\" });\n continue;\n }\n\n if (parsed.state === \"version-unsupported\") {\n rows.set(key, {\n kind: \"version-unsupported\",\n documentType: parsed.documentType,\n requiredVersion: parsed.requiredVersion,\n });\n continue;\n }\n\n if (parsed.state === \"model-missing\") {\n const base = storedFileName(file) || file.name;\n const finalName = uniqueName(base, []);\n assignedNames.add(finalName);\n rows.set(key, {\n kind: \"ready\",\n finalName,\n renamed: finalName !== base,\n });\n continue;\n }\n\n if (!isDocumentTypeSupported(parsed.documentType, documentTypes)) {\n rows.set(key, { kind: \"unsupported\" });\n continue;\n }\n\n // The stored name falls back to the document's own header name when the\n // file name is extension-only (\".phd\" normalizes to \"\").\n const base = storedFileName(file) || parsed.headerName;\n const sameTypeNames = rootNodes\n .filter(\n (node) => isFileNode(node) && node.documentType === parsed.documentType,\n )\n .map((node) => node.name);\n const idDuplicate = rootNodes.some((node) => node.id === parsed.id);\n const nameDuplicate = sameTypeNames.includes(base);\n\n const finalName = uniqueName(base, sameTypeNames);\n assignedNames.add(finalName);\n rows.set(key, {\n kind: \"ready\",\n finalName,\n duplicate: nameDuplicate ? \"name\" : idDuplicate ? \"id\" : undefined,\n renamed: finalName !== base,\n });\n }\n\n return rows;\n}\n","import { DriveIcon } from \"@powerhousedao/connect/components\";\nimport {\n clearPendingImportFiles,\n getPendingImportFiles,\n pendingFileKey,\n planOpenFileImports,\n runOpenFileImports,\n subscribePendingImportFiles,\n type ParsedFileInfo,\n type PlanEntry,\n type PlannedOpenFileImport,\n} from \"@powerhousedao/connect/utils\";\nimport { Modal } from \"@powerhousedao/design-system\";\nimport {\n Combobox,\n formatFileSize,\n ModalButton,\n} from \"@powerhousedao/design-system/connect\";\nimport {\n closePHModal,\n DocumentModelNotFoundError,\n loadFile,\n setSelectedDrive,\n showPHModal,\n useDocumentTypes,\n useDrives,\n usePHModal,\n useUserPermissions,\n} from \"@powerhousedao/reactor-browser\";\nimport { UnsupportedDocumentModelVersionError } from \"@powerhousedao/shared/document-model\";\nimport { useEffect, useState, useSyncExternalStore } from \"react\";\nimport { useTranslation } from \"react-i18next\";\n\n// Drive picker for documents opened through the OS file association (PWA\n// File Handling). This is the flow's ONLY dialog state: validity and\n// duplicate checks run up-front against the selected drive (inline errors,\n// automatic \"(copy) N\" renames surfaced as \"Rename and import\"), and on\n// confirmation the dialog closes — progress is shown by the floating upload\n// panel (OpenFileUploadList) driven from utils/open-file-uploads.ts, the same\n// UI drag-and-drop uses.\n//\n// The component only mounts while the modal is shown (ModalsContainer keys on\n// the modal type), so picker state starts fresh on every open.\n\n// Parse results are cached at module level so they survive the create-drive\n// detour (which unmounts this component) and are computed once per pending\n// file — loadFile replays the file's full operation history. Only header\n// fields are retained, never the parsed document. In-flight promises are\n// cached too, deduplicating StrictMode double-effects.\nconst parseCache = new Map<string, ParsedFileInfo | Promise<ParsedFileInfo>>();\n\nasync function parsePendingFile(file: File): Promise<ParsedFileInfo> {\n try {\n const document = await loadFile(file);\n return {\n state: \"parsed\",\n id: document.header.id,\n documentType: document.header.documentType,\n headerName: document.header.name,\n };\n } catch (error) {\n if (UnsupportedDocumentModelVersionError.isError(error)) {\n return {\n state: \"version-unsupported\",\n documentType: error.documentType,\n requiredVersion: error.requiredVersion,\n };\n }\n // Missing document model ≠ invalid: the import path's package\n // auto-discovery may still resolve it.\n if (DocumentModelNotFoundError.isError(error)) {\n return { state: \"model-missing\" };\n }\n return { state: \"invalid\" };\n }\n}\n\nexport function OpenFileDocumentsModal() {\n const phModal = usePHModal();\n const open = phModal?.type === \"openFileDocuments\";\n const { t } = useTranslation();\n const drives = useDrives();\n const documentTypes = useDocumentTypes();\n const { isAllowedToCreateDocuments } = useUserPermissions();\n const pendingFiles = useSyncExternalStore(\n subscribePendingImportFiles,\n getPendingImportFiles,\n );\n\n const [pickedDriveId, setPickedDriveId] = useState<string>();\n const [, setParseVersion] = useState(0);\n\n // Kick off (and cache) the one-time parse of each pending file. Gated on\n // the reactor being ready — never cache \"reactor not initialized\" as a\n // parse result; the effect re-runs when files change and drives arriving\n // re-render the modal anyway.\n useEffect(() => {\n if (!window.ph?.reactorClient) return;\n let cancelled = false;\n const pendingKeys = new Set(pendingFiles.map(pendingFileKey));\n for (const key of parseCache.keys()) {\n if (!pendingKeys.has(key)) parseCache.delete(key);\n }\n for (const file of pendingFiles) {\n const key = pendingFileKey(file);\n if (parseCache.has(key)) continue;\n const promise = parsePendingFile(file).then((info) => {\n parseCache.set(key, info);\n return info;\n });\n parseCache.set(key, promise);\n void promise.then(() => {\n if (!cancelled) setParseVersion((version) => version + 1);\n });\n }\n return () => {\n cancelled = true;\n };\n }, [pendingFiles]);\n\n // The first drive is preselected so opening a file is a single\n // confirmation; the select lets the user switch destination.\n const selectedDriveId = pickedDriveId ?? drives?.[0]?.header.id;\n const selectedDrive = drives?.find(\n (drive) => drive.header.id === selectedDriveId,\n );\n\n const driveOptions = (drives ?? []).map((drive) => ({\n // New local drives carry their name only in the global state until the\n // header syncs.\n label: drive.header.name || drive.state.global.name,\n value: drive.header.id,\n drive,\n }));\n type DriveOption = (typeof driveOptions)[number];\n\n // Pure derivations — recomputed on drive switch, on files arriving, and on\n // parses settling.\n const entries: PlanEntry[] = pendingFiles.map((file) => {\n const cached = parseCache.get(pendingFileKey(file));\n return {\n key: pendingFileKey(file),\n file,\n parsed: cached instanceof Promise ? undefined : cached,\n };\n });\n const plan = selectedDrive\n ? planOpenFileImports({\n entries,\n nodes: selectedDrive.state.global.nodes,\n documentTypes,\n })\n : new Map<string, never>();\n\n const planRows = [...plan.values()];\n const readyCount = planRows.filter((row) => row.kind === \"ready\").length;\n const anyChecking = planRows.some((row) => row.kind === \"checking\");\n const anyRenamed = planRows.some(\n (row) => row.kind === \"ready\" && row.renamed,\n );\n\n function onImport() {\n const drive = selectedDrive;\n if (!drive) return;\n const imports: PlannedOpenFileImport[] = [];\n for (const { key, file } of entries) {\n const row = plan.get(key);\n if (row?.kind === \"ready\") imports.push({ file, name: row.finalName });\n }\n if (imports.length === 0) return;\n // Ordering matters: the pending store must be cleared BEFORE the modal\n // closes — the launch-queue listener reopens this picker whenever the\n // modal clears while files are still pending.\n clearPendingImportFiles();\n closePHModal();\n setSelectedDrive(drive);\n void runOpenFileImports({\n driveId: drive.header.id,\n imports,\n documentTypes,\n });\n }\n\n function onCancel() {\n clearPendingImportFiles();\n closePHModal();\n }\n\n if (!open) return null;\n\n const hasDrives = (drives?.length ?? 0) > 0;\n\n function rowAnnotation(key: string) {\n const row = plan.get(key);\n if (!row) return null;\n switch (row.kind) {\n case \"checking\":\n return (\n <span className=\"text-xs text-muted-foreground\">\n {t(\"modals.openFileDocuments.checking\")}\n </span>\n );\n case \"invalid\":\n return (\n <span className=\"text-xs text-destructive\">\n {t(\"modals.openFileDocuments.invalidFile\")}\n </span>\n );\n case \"version-unsupported\":\n return (\n <span className=\"text-xs text-destructive\">\n {`Requires version ${row.requiredVersion} of ${row.documentType} — update the package`}\n </span>\n );\n case \"unsupported\":\n return (\n <span className=\"text-xs text-destructive\">\n {t(\"modals.openFileDocuments.unsupportedType\")}\n </span>\n );\n case \"ready\":\n // \"name\": same name + type already in the drive, so the file is\n // renamed to a copy. \"id\": same document id, imported as a fresh copy\n // under its original name. A row that only got renamed to avoid\n // clashing with another file in THIS batch is NOT in the drive, so it\n // must not claim to be — it gets the neutral rename hint instead.\n if (row.duplicate === \"name\") {\n return (\n <span className=\"text-xs text-muted-foreground\">\n {t(\"modals.openFileDocuments.duplicateHint\", {\n name: row.finalName,\n })}\n </span>\n );\n }\n if (row.duplicate === \"id\") {\n return (\n <span className=\"text-xs text-muted-foreground\">\n {t(\"modals.openFileDocuments.duplicateCopyHint\")}\n </span>\n );\n }\n if (row.renamed) {\n return (\n <span className=\"text-xs text-muted-foreground\">\n {t(\"modals.openFileDocuments.renamedHint\", {\n name: row.finalName,\n })}\n </span>\n );\n }\n return null;\n }\n }\n\n return (\n <Modal\n open={open}\n onOpenChange={(status) => {\n if (!status) onCancel();\n }}\n // The drive select's dropdown must be able to spill past the dialog\n // bounds instead of being clipped (the menu cannot portal to <body>:\n // Radix's modal pointer-events lock would make it unclickable there).\n contentProps={{ className: \"overflow-visible\" }}\n >\n <div className=\"w-[520px] p-6\">\n <div className=\"pb-2 text-2xl font-bold text-foreground\">\n {t(\"modals.openFileDocuments.title\", {\n count: pendingFiles.length,\n })}\n </div>\n\n {!isAllowedToCreateDocuments ? (\n <>\n <div className=\"my-6 rounded-md bg-background p-4 text-center text-foreground\">\n {t(\"modals.openFileDocuments.notAllowed\")}\n </div>\n <div className=\"mt-8 flex\">\n <ModalButton variant=\"cancel\" onClick={onCancel}>\n {t(\"common.cancel\")}\n </ModalButton>\n </div>\n </>\n ) : (\n <>\n <div className=\"my-4 flex flex-col gap-1 text-sm text-foreground\">\n {pendingFiles.map((file, index) => {\n const key = pendingFileKey(file);\n const annotation = rowAnnotation(key);\n return (\n <div\n key={`${index}-${key}`}\n className=\"flex flex-col gap-0.5 rounded-md bg-background px-3 py-2\"\n >\n <div className=\"flex justify-between gap-2\">\n <span className=\"truncate\">{file.name}</span>\n <span className=\"shrink-0 text-muted-foreground\">\n {formatFileSize(file.size)}\n </span>\n </div>\n {annotation}\n </div>\n );\n })}\n </div>\n\n {hasDrives ? (\n <>\n <div className=\"mb-2 text-sm text-muted-foreground\">\n {t(\"modals.openFileDocuments.body\")}\n </div>\n <Combobox\n id=\"open-file-documents-drive\"\n isSearchable={false}\n options={driveOptions}\n value={\n driveOptions.find(\n (option) => option.value === selectedDriveId,\n ) ?? null\n }\n onChange={(option) =>\n setPickedDriveId((option as DriveOption).value)\n }\n formatOptionLabel={(option) => {\n const { label, drive } = option as DriveOption;\n return (\n <span className=\"flex items-center gap-2\">\n <DriveIcon drive={drive} />\n <span className=\"truncate\">{label}</span>\n </span>\n );\n }}\n maxMenuHeight={200}\n />\n </>\n ) : (\n <div className=\"my-6 rounded-md bg-background p-4 text-center text-foreground\">\n {t(\"modals.openFileDocuments.noDrives\")}\n </div>\n )}\n\n <div className=\"mt-8 flex justify-between gap-3\">\n <ModalButton variant=\"cancel\" onClick={onCancel}>\n {t(\"common.cancel\")}\n </ModalButton>\n {hasDrives ? (\n <ModalButton\n variant=\"confirm\"\n disabled={!selectedDriveId || readyCount === 0 || anyChecking}\n onClick={onImport}\n >\n {anyRenamed\n ? t(\"modals.openFileDocuments.renameAndImport\")\n : t(\"modals.openFileDocuments.import\")}\n </ModalButton>\n ) : (\n <ModalButton\n variant=\"confirm\"\n onClick={() => showPHModal({ type: \"addDrive\" })}\n >\n {t(\"modals.openFileDocuments.createDrive\")}\n </ModalButton>\n )}\n </div>\n </>\n )}\n </div>\n </Modal>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;AAuDA,SAAgB,eAAe,MAAoB;AACjD,QAAO,KAAK,KAAK,QAAQ,WAAW,GAAG;;;;;;;;;;AAWzC,SAAgB,oBAAoB,MAIR;CAC1B,MAAM,EAAE,SAAS,OAAO,kBAAkB;CAC1C,MAAM,YAAY,MAAM,QACrB,UAAU,KAAK,gBAAgB,UAAU,KAC3C;CACD,MAAM,uBAAO,IAAI,KAAyB;CAC1C,MAAM,gCAAgB,IAAI,KAAa;CAEvC,SAAS,WAAW,MAAc,YAA8B;AAE9D,MAAI,EADa,WAAW,SAAS,KAAK,IAAI,cAAc,IAAI,KAAK,EACtD,QAAO;AAEtB,SAAO,GAAG,KAAK,UAAU,kBADN,CAAC,GAAG,YAAY,GAAG,cAAc,EACG,KAAK;;AAG9D,MAAK,MAAM,EAAE,KAAK,MAAM,YAAY,SAAS;AAC3C,MAAI,CAAC,QAAQ;AACX,QAAK,IAAI,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC;;AAEF,MAAI,OAAO,UAAU,WAAW;AAC9B,QAAK,IAAI,KAAK,EAAE,MAAM,WAAW,CAAC;AAClC;;AAGF,MAAI,OAAO,UAAU,uBAAuB;AAC1C,QAAK,IAAI,KAAK;IACZ,MAAM;IACN,cAAc,OAAO;IACrB,iBAAiB,OAAO;IACzB,CAAC;AACF;;AAGF,MAAI,OAAO,UAAU,iBAAiB;GACpC,MAAM,OAAO,eAAe,KAAK,IAAI,KAAK;GAC1C,MAAM,YAAY,WAAW,MAAM,EAAE,CAAC;AACtC,iBAAc,IAAI,UAAU;AAC5B,QAAK,IAAI,KAAK;IACZ,MAAM;IACN;IACA,SAAS,cAAc;IACxB,CAAC;AACF;;AAGF,MAAI,CAAC,wBAAwB,OAAO,cAAc,cAAc,EAAE;AAChE,QAAK,IAAI,KAAK,EAAE,MAAM,eAAe,CAAC;AACtC;;EAKF,MAAM,OAAO,eAAe,KAAK,IAAI,OAAO;EAC5C,MAAM,gBAAgB,UACnB,QACE,SAAS,WAAW,KAAK,IAAI,KAAK,iBAAiB,OAAO,aAC5D,CACA,KAAK,SAAS,KAAK,KAAK;EAC3B,MAAM,cAAc,UAAU,MAAM,SAAS,KAAK,OAAO,OAAO,GAAG;EACnE,MAAM,gBAAgB,cAAc,SAAS,KAAK;EAElD,MAAM,YAAY,WAAW,MAAM,cAAc;AACjD,gBAAc,IAAI,UAAU;AAC5B,OAAK,IAAI,KAAK;GACZ,MAAM;GACN;GACA,WAAW,gBAAgB,SAAS,cAAc,OAAO,KAAA;GACzD,SAAS,cAAc;GACxB,CAAC;;AAGJ,QAAO;;;;AC9FT,MAAM,6BAAa,IAAI,KAAuD;AAE9E,eAAe,iBAAiB,MAAqC;AACnE,KAAI;EACF,MAAM,WAAW,MAAM,SAAS,KAAK;AACrC,SAAO;GACL,OAAO;GACP,IAAI,SAAS,OAAO;GACpB,cAAc,SAAS,OAAO;GAC9B,YAAY,SAAS,OAAO;GAC7B;UACM,OAAO;AACd,MAAI,qCAAqC,QAAQ,MAAM,CACrD,QAAO;GACL,OAAO;GACP,cAAc,MAAM;GACpB,iBAAiB,MAAM;GACxB;AAIH,MAAI,2BAA2B,QAAQ,MAAM,CAC3C,QAAO,EAAE,OAAO,iBAAiB;AAEnC,SAAO,EAAE,OAAO,WAAW;;;AAI/B,SAAgB,yBAAyB;CAEvC,MAAM,OADU,YAAY,EACN,SAAS;CAC/B,MAAM,EAAE,MAAM,gBAAgB;CAC9B,MAAM,SAAS,WAAW;CAC1B,MAAM,gBAAgB,kBAAkB;CACxC,MAAM,EAAE,+BAA+B,oBAAoB;CAC3D,MAAM,eAAe,qBACnB,6BACA,sBACD;CAED,MAAM,CAAC,eAAe,oBAAoB,UAAkB;CAC5D,MAAM,GAAG,mBAAmB,SAAS,EAAE;AAMvC,iBAAgB;AACd,MAAI,CAAC,OAAO,IAAI,cAAe;EAC/B,IAAI,YAAY;EAChB,MAAM,cAAc,IAAI,IAAI,aAAa,IAAI,eAAe,CAAC;AAC7D,OAAK,MAAM,OAAO,WAAW,MAAM,CACjC,KAAI,CAAC,YAAY,IAAI,IAAI,CAAE,YAAW,OAAO,IAAI;AAEnD,OAAK,MAAM,QAAQ,cAAc;GAC/B,MAAM,MAAM,eAAe,KAAK;AAChC,OAAI,WAAW,IAAI,IAAI,CAAE;GACzB,MAAM,UAAU,iBAAiB,KAAK,CAAC,MAAM,SAAS;AACpD,eAAW,IAAI,KAAK,KAAK;AACzB,WAAO;KACP;AACF,cAAW,IAAI,KAAK,QAAQ;AACvB,WAAQ,WAAW;AACtB,QAAI,CAAC,UAAW,kBAAiB,YAAY,UAAU,EAAE;KACzD;;AAEJ,eAAa;AACX,eAAY;;IAEb,CAAC,aAAa,CAAC;CAIlB,MAAM,kBAAkB,iBAAiB,SAAS,IAAI,OAAO;CAC7D,MAAM,gBAAgB,QAAQ,MAC3B,UAAU,MAAM,OAAO,OAAO,gBAChC;CAED,MAAM,gBAAgB,UAAU,EAAE,EAAE,KAAK,WAAW;EAGlD,OAAO,MAAM,OAAO,QAAQ,MAAM,MAAM,OAAO;EAC/C,OAAO,MAAM,OAAO;EACpB;EACD,EAAE;CAKH,MAAM,UAAuB,aAAa,KAAK,SAAS;EACtD,MAAM,SAAS,WAAW,IAAI,eAAe,KAAK,CAAC;AACnD,SAAO;GACL,KAAK,eAAe,KAAK;GACzB;GACA,QAAQ,kBAAkB,UAAU,KAAA,IAAY;GACjD;GACD;CACF,MAAM,OAAO,gBACT,oBAAoB;EAClB;EACA,OAAO,cAAc,MAAM,OAAO;EAClC;EACD,CAAC,mBACF,IAAI,KAAoB;CAE5B,MAAM,WAAW,CAAC,GAAG,KAAK,QAAQ,CAAC;CACnC,MAAM,aAAa,SAAS,QAAQ,QAAQ,IAAI,SAAS,QAAQ,CAAC;CAClE,MAAM,cAAc,SAAS,MAAM,QAAQ,IAAI,SAAS,WAAW;CACnE,MAAM,aAAa,SAAS,MACzB,QAAQ,IAAI,SAAS,WAAW,IAAI,QACtC;CAED,SAAS,WAAW;EAClB,MAAM,QAAQ;AACd,MAAI,CAAC,MAAO;EACZ,MAAM,UAAmC,EAAE;AAC3C,OAAK,MAAM,EAAE,KAAK,UAAU,SAAS;GACnC,MAAM,MAAM,KAAK,IAAI,IAAI;AACzB,OAAI,KAAK,SAAS,QAAS,SAAQ,KAAK;IAAE;IAAM,MAAM,IAAI;IAAW,CAAC;;AAExE,MAAI,QAAQ,WAAW,EAAG;AAI1B,2BAAyB;AACzB,gBAAc;AACd,mBAAiB,MAAM;AAClB,qBAAmB;GACtB,SAAS,MAAM,OAAO;GACtB;GACA;GACD,CAAC;;CAGJ,SAAS,WAAW;AAClB,2BAAyB;AACzB,gBAAc;;AAGhB,KAAI,CAAC,KAAM,QAAO;CAElB,MAAM,aAAa,QAAQ,UAAU,KAAK;CAE1C,SAAS,cAAc,KAAa;EAClC,MAAM,MAAM,KAAK,IAAI,IAAI;AACzB,MAAI,CAAC,IAAK,QAAO;AACjB,UAAQ,IAAI,MAAZ;GACE,KAAK,WACH,QACE,oBAAC,QAAD;IAAM,WAAU;cACb,EAAE,oCAAoC;IAClC,CAAA;GAEX,KAAK,UACH,QACE,oBAAC,QAAD;IAAM,WAAU;cACb,EAAE,uCAAuC;IACrC,CAAA;GAEX,KAAK,sBACH,QACE,oBAAC,QAAD;IAAM,WAAU;cACb,oBAAoB,IAAI,gBAAgB,MAAM,IAAI,aAAa;IAC3D,CAAA;GAEX,KAAK,cACH,QACE,oBAAC,QAAD;IAAM,WAAU;cACb,EAAE,2CAA2C;IACzC,CAAA;GAEX,KAAK;AAMH,QAAI,IAAI,cAAc,OACpB,QACE,oBAAC,QAAD;KAAM,WAAU;eACb,EAAE,0CAA0C,EAC3C,MAAM,IAAI,WACX,CAAC;KACG,CAAA;AAGX,QAAI,IAAI,cAAc,KACpB,QACE,oBAAC,QAAD;KAAM,WAAU;eACb,EAAE,6CAA6C;KAC3C,CAAA;AAGX,QAAI,IAAI,QACN,QACE,oBAAC,QAAD;KAAM,WAAU;eACb,EAAE,wCAAwC,EACzC,MAAM,IAAI,WACX,CAAC;KACG,CAAA;AAGX,WAAO;;;AAIb,QACE,oBAAC,OAAD;EACQ;EACN,eAAe,WAAW;AACxB,OAAI,CAAC,OAAQ,WAAU;;EAKzB,cAAc,EAAE,WAAW,oBAAoB;YAE/C,qBAAC,OAAD;GAAK,WAAU;aAAf,CACE,oBAAC,OAAD;IAAK,WAAU;cACZ,EAAE,kCAAkC,EACnC,OAAO,aAAa,QACrB,CAAC;IACE,CAAA,EAEL,CAAC,6BACA,qBAAA,UAAA,EAAA,UAAA,CACE,oBAAC,OAAD;IAAK,WAAU;cACZ,EAAE,sCAAsC;IACrC,CAAA,EACN,oBAAC,OAAD;IAAK,WAAU;cACb,oBAAC,aAAD;KAAa,SAAQ;KAAS,SAAS;eACpC,EAAE,gBAAgB;KACP,CAAA;IACV,CAAA,CACL,EAAA,CAAA,GAEH,qBAAA,UAAA,EAAA,UAAA;IACE,oBAAC,OAAD;KAAK,WAAU;eACZ,aAAa,KAAK,MAAM,UAAU;MACjC,MAAM,MAAM,eAAe,KAAK;MAChC,MAAM,aAAa,cAAc,IAAI;AACrC,aACE,qBAAC,OAAD;OAEE,WAAU;iBAFZ,CAIE,qBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,oBAAC,QAAD;SAAM,WAAU;mBAAY,KAAK;SAAY,CAAA,EAC7C,oBAAC,QAAD;SAAM,WAAU;mBACb,eAAe,KAAK,KAAK;SACrB,CAAA,CACH;WACL,WACG;SAVC,GAAG,MAAM,GAAG,MAUb;OAER;KACE,CAAA;IAEL,YACC,qBAAA,UAAA,EAAA,UAAA,CACE,oBAAC,OAAD;KAAK,WAAU;eACZ,EAAE,gCAAgC;KAC/B,CAAA,EACN,oBAAC,UAAD;KACE,IAAG;KACH,cAAc;KACd,SAAS;KACT,OACE,aAAa,MACV,WAAW,OAAO,UAAU,gBAC9B,IAAI;KAEP,WAAW,WACT,iBAAkB,OAAuB,MAAM;KAEjD,oBAAoB,WAAW;MAC7B,MAAM,EAAE,OAAO,UAAU;AACzB,aACE,qBAAC,QAAD;OAAM,WAAU;iBAAhB,CACE,oBAAC,WAAD,EAAkB,OAAS,CAAA,EAC3B,oBAAC,QAAD;QAAM,WAAU;kBAAY;QAAa,CAAA,CACpC;;;KAGX,eAAe;KACf,CAAA,CACD,EAAA,CAAA,GAEH,oBAAC,OAAD;KAAK,WAAU;eACZ,EAAE,oCAAoC;KACnC,CAAA;IAGR,qBAAC,OAAD;KAAK,WAAU;eAAf,CACE,oBAAC,aAAD;MAAa,SAAQ;MAAS,SAAS;gBACpC,EAAE,gBAAgB;MACP,CAAA,EACb,YACC,oBAAC,aAAD;MACE,SAAQ;MACR,UAAU,CAAC,mBAAmB,eAAe,KAAK;MAClD,SAAS;gBAER,aACG,EAAE,2CAA2C,GAC7C,EAAE,kCAAkC;MAC5B,CAAA,GAEd,oBAAC,aAAD;MACE,SAAQ;MACR,eAAe,YAAY,EAAE,MAAM,YAAY,CAAC;gBAE/C,EAAE,uCAAuC;MAC9B,CAAA,CAEZ;;IACL,EAAA,CAAA,CAED;;EACA,CAAA","debug_id":"bbca2005-43bd-5d38-b8ac-878c78a31a8f"}
@@ -1,8 +1,8 @@
1
1
 
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="5852ca79-deda-5cec-b6ee-8b9d57223aaf")}catch(e){}}();
3
- import { a as shortGitSha, n as getGitSha, r as getGitUrl } from "./build-info-BvyVA-pf.js";
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="2611d2b5-5e71-5305-a0df-eecf8618a3bc")}catch(e){}}();
3
+ import { a as shortGitSha, n as getGitSha, r as getGitUrl } from "./build-info-BPvkFr0E.js";
4
4
  import { r as connectConfig } from "./connect.config-DaHDEUrw.js";
5
- import { t as useRegistryPackages } from "./useRegistryPackages-DMhCvTWq.js";
5
+ import { t as useRegistryPackages } from "./useRegistryPackages-BnUcL_nZ.js";
6
6
  import { r as toast } from "./toast-BXIGd4Eb.js";
7
7
  import { jsx, jsxs } from "react/jsx-runtime";
8
8
  import { t } from "i18next";
@@ -16,7 +16,7 @@ import { Bot, CircleHelp, Package, PenLine, TriangleAlert } from "lucide-react";
16
16
  var package_default = {
17
17
  name: "@powerhousedao/connect",
18
18
  productName: "Powerhouse-Connect",
19
- version: "6.2.3-dev.0",
19
+ version: "6.2.3-dev.10",
20
20
  description: "Powerhouse Connect",
21
21
  main: "dist/index.html",
22
22
  type: "module",
@@ -460,7 +460,7 @@ function parseBareName(spec) {
460
460
  }
461
461
  const ConnectPackageManager = () => {
462
462
  const packageManager = useVetraPackageManager();
463
- const { registryPackageList, installedPackages, availablePackages, availableHasMore, isLoadingAvailable, isLoadingMoreAvailable, availableError, ensureAvailableLoaded, fetchAvailablePage, loadMoreAvailable, setAvailableSearch, updateRegistryPackageStatus, registerFallbackRegistryPackage } = useRegistryPackages();
463
+ const { registryPackageList, installedPackages, availablePackages, availableHasMore, isLoadingAvailable, isLoadingMoreAvailable, availableError, ensureAvailableLoaded, ensureInstalledMetadataLoaded, fetchAvailablePage, loadMoreAvailable, setAvailableSearch, updateRegistryPackageStatus, registerFallbackRegistryPackage } = useRegistryPackages();
464
464
  async function handleInstall(packageSpec) {
465
465
  if (!packageManager) return;
466
466
  const bareName = parseBareName(packageSpec);
@@ -503,6 +503,7 @@ const ConnectPackageManager = () => {
503
503
  },
504
504
  onAvailableSearchChange: setAvailableSearch,
505
505
  onAvailableTabOpen: ensureAvailableLoaded,
506
+ onInstalledTabOpen: ensureInstalledMetadataLoaded,
506
507
  onInstall: handleInstall,
507
508
  onUninstall: handleUninstall
508
509
  })
@@ -564,5 +565,5 @@ const SettingsModal = () => {
564
565
  //#endregion
565
566
  export { SettingsModal };
566
567
 
567
- //# sourceMappingURL=SettingsModal-DzSC4sh8.js.map
568
- //# debugId=5852ca79-deda-5cec-b6ee-8b9d57223aaf
568
+ //# sourceMappingURL=SettingsModal-CgqnSXs5.js.map
569
+ //# debugId=2611d2b5-5e71-5305-a0df-eecf8618a3bc
@@ -1 +1 @@
1
- {"version":3,"file":"SettingsModal-DzSC4sh8.js","sources":["../package.json","../src/components/modal/modals/settings/about.tsx","../src/components/modal/modals/settings/ai-assistant.tsx","../src/components/modal/modals/settings/danger-zone.tsx","../src/components/modal/modals/settings/default-editor.tsx","../src/components/modal/modals/settings/package-manager.tsx","../src/components/modal/modals/SettingsModal.tsx"],"sourcesContent":["","import { connectConfig } from \"@powerhousedao/connect/config\";\nimport {\n getGitSha,\n getGitUrl,\n packageJson,\n shortGitSha,\n} from \"@powerhousedao/connect/utils\";\nimport { Icon } from \"@powerhousedao/design-system\";\nimport { About as BaseAbout } from \"@powerhousedao/design-system/connect\";\nimport {\n closePHModal,\n DriveCollectionId,\n showPHModal,\n useDrives,\n useDriveSystemInfo,\n useSyncList,\n} from \"@powerhousedao/reactor-browser\";\nimport type { DocumentDriveDocument } from \"@powerhousedao/shared/document-drive\";\nimport { useMemo } from \"react\";\n\nexport const About: React.FC = () => {\n const onOpenInspector = () => {\n closePHModal();\n showPHModal({ type: \"inspector\" });\n };\n\n return (\n <div>\n <BaseAbout\n packageJson={packageJson}\n phCliVersion={\n typeof connectConfig.phCliVersion === \"string\"\n ? connectConfig.phCliVersion\n : undefined\n }\n />\n <AppGitHash />\n <ConnectedDrives />\n <div className=\"bg-background p-3\">\n <h2 className=\"mb-2 font-semibold text-foreground\">Inspector</h2>\n <p className=\"mb-3 text-sm font-normal text-foreground\">\n Explore the local database and sync state for debugging.\n </p>\n <button\n className=\"flex items-center gap-x-2 rounded-md border border-border bg-transparent px-3 py-1 text-sm font-medium text-foreground transition-colors hover:hover-effect\"\n onClick={onOpenInspector}\n type=\"button\"\n >\n Open Inspector <Icon name=\"CircleInfo\" size={16} />\n </button>\n </div>\n </div>\n );\n};\n\nfunction AppGitHash() {\n const sha = getGitSha();\n if (sha === \"unknown\") return null;\n const url = getGitUrl();\n const label = shortGitSha(sha);\n return (\n <div className=\"bg-background p-3 text-sm\">\n <span className=\"font-semibold\">Git hash: </span>\n {url ? (\n <a\n className=\"font-mono underline\"\n href={url}\n target=\"_blank\"\n rel=\"noreferrer noopener\"\n title={sha}\n >\n {label}\n </a>\n ) : (\n <span className=\"font-mono\" title={sha}>\n {label}\n </span>\n )}\n </div>\n );\n}\n\nfunction ConnectedDrives() {\n const drives = useDrives() ?? [];\n const remotes = useSyncList();\n\n const remoteDrives = useMemo(\n () =>\n drives.filter((d) =>\n remotes.some((r) =>\n r.meta.collectionId.equals(DriveCollectionId.forDrive(d.header.id)),\n ),\n ),\n [drives, remotes],\n );\n\n return (\n <div className=\"my-4 bg-background p-3\">\n <h2 className=\"mb-2 font-semibold text-foreground\">Connected drives</h2>\n {remoteDrives.length === 0 ? (\n <p className=\"text-sm font-normal text-foreground\">\n No connected remote drives.\n </p>\n ) : (\n <ul className=\"flex flex-col gap-3\">\n {remoteDrives.map((drive) => (\n <DriveAboutEntry key={drive.header.id} drive={drive} />\n ))}\n </ul>\n )}\n </div>\n );\n}\n\nfunction DriveAboutEntry({ drive }: { drive: DocumentDriveDocument }) {\n const info = useDriveSystemInfo(drive);\n const name = drive.state.global.name || drive.header.name;\n\n return (\n <li className=\"text-sm text-foreground\">\n <div className=\"flex items-baseline gap-2\">\n <span className=\"font-medium\">{name}</span>\n {info.status === \"ready\" && (\n <span className=\"text-xs text-muted-foreground\">{info.host}</span>\n )}\n </div>\n {info.status === \"loading\" && (\n <div className=\"mt-1 text-xs text-muted-foreground\">Loading…</div>\n )}\n {info.status === \"error\" && (\n <div className=\"mt-1 text-xs text-destructive\">\n Could not load system info\n </div>\n )}\n {info.status === \"ready\" && (\n <div className=\"mt-1 text-xs text-foreground\">\n <div>\n <span className=\"font-medium\">Version:</span> {info.version}\n </div>\n <div>\n <span className=\"font-medium\">Git hash: </span>\n {info.gitUrl ? (\n <a\n className=\"font-mono hover:underline\"\n href={info.gitUrl}\n target=\"_blank\"\n rel=\"noreferrer noopener\"\n title={info.gitHash}\n >\n {shortGitSha(info.gitHash)}\n </a>\n ) : (\n <span className=\"font-mono\" title={info.gitHash}>\n {shortGitSha(info.gitHash)}\n </span>\n )}\n </div>\n </div>\n )}\n {info.status === \"local\" && (\n <div className=\"mt-1 text-xs text-muted-foreground\">\n Local drive — N/A\n </div>\n )}\n </li>\n );\n}\n","import {\n getAiSettings,\n isAiConfigured,\n updateAiSettings,\n type AiSettings,\n} from \"@powerhousedao/reactor-browser/ai\";\nimport { Bot } from \"lucide-react\";\nimport { useState } from \"react\";\n\ntype TestState =\n | { status: \"idle\" }\n | { status: \"testing\" }\n | { status: \"ok\"; message: string }\n | { status: \"error\"; message: string };\n\nconst FIELD_CLASS =\n \"w-full rounded-md border border-border bg-background p-2.5 text-sm text-foreground outline-none placeholder:text-muted-foreground\";\n\n/**\n * Settings-modal section for the in-browser AI assistant: the user master\n * toggle, the OpenAI-compatible endpoint configuration, and a\n * save-and-test-connection action. Only mounted when the deployment\n * enables the feature (`connect.ai.assistantEnabled`).\n */\nexport const AiAssistant: React.FC = () => {\n const [draft, setDraft] = useState<AiSettings>(() => getAiSettings());\n const [test, setTest] = useState<TestState>({ status: \"idle\" });\n\n const patch = (value: Partial<AiSettings>) =>\n setDraft((d) => ({ ...d, ...value }));\n\n const saveAndTest = async () => {\n updateAiSettings(draft);\n if (!isAiConfigured(draft)) {\n setTest({\n status: \"error\",\n message: \"Base URL, API key and model are all required.\",\n });\n return;\n }\n setTest({ status: \"testing\" });\n try {\n const base = draft.baseUrl.trim().replace(/\\/+$/, \"\");\n const res = await fetch(`${base}/models`, {\n headers: draft.apiKey.trim()\n ? { Authorization: `Bearer ${draft.apiKey.trim()}` }\n : undefined,\n });\n if (!res.ok) {\n throw new Error(\n `HTTP ${res.status}${res.statusText ? ` ${res.statusText}` : \"\"}`,\n );\n }\n const data: unknown = await res.json();\n const list = (data as { data?: unknown[] } | null)?.data;\n const count = Array.isArray(list) ? list.length : 0;\n setTest({\n status: \"ok\",\n message: count\n ? `Connected — ${count} model(s) available.`\n : \"Connected.\",\n });\n } catch (error) {\n setTest({\n status: \"error\",\n message: `Connection failed: ${\n error instanceof Error ? error.message : String(error)\n }`,\n });\n }\n };\n\n return (\n <div className=\"space-y-4 p-1\">\n <div>\n <h3 className=\"flex items-center gap-2 text-sm font-semibold text-foreground\">\n <Bot size={16} /> AI Assistant\n </h3>\n <p className=\"mt-1 text-xs text-muted-foreground\">\n An in-page assistant that creates and changes documents, manages\n drives, and inspects read models through an OpenAI-compatible\n endpoint.\n </p>\n </div>\n\n <label className=\"flex items-center gap-2 text-sm text-foreground\">\n <input\n type=\"checkbox\"\n checked={draft.enabled}\n onChange={(event) => {\n patch({ enabled: event.target.checked });\n // The switch acts immediately: the FAB mounts/unmounts at once.\n updateAiSettings({ enabled: event.target.checked });\n }}\n />\n Enable AI assistant\n </label>\n\n {draft.enabled && (\n <div className=\"space-y-2 rounded-lg border border-border p-3\">\n <div className=\"mb-1 text-xs font-semibold tracking-wide text-muted-foreground uppercase\">\n Endpoint\n </div>\n <input\n type=\"url\"\n placeholder=\"Base URL (e.g. https://api.openai.com/v1)\"\n value={draft.baseUrl}\n onChange={(e) => patch({ baseUrl: e.target.value })}\n className={FIELD_CLASS}\n spellCheck={false}\n />\n <input\n type=\"password\"\n placeholder=\"API key\"\n value={draft.apiKey}\n onChange={(e) => patch({ apiKey: e.target.value })}\n className={FIELD_CLASS}\n autoComplete=\"off\"\n spellCheck={false}\n />\n <input\n type=\"text\"\n placeholder=\"Model (e.g. gpt-4o-mini)\"\n value={draft.model}\n onChange={(e) => patch({ model: e.target.value })}\n className={FIELD_CLASS}\n spellCheck={false}\n />\n <label className=\"flex items-center gap-2 pt-1 text-sm text-foreground\">\n <input\n type=\"checkbox\"\n checked={draft.autoApproveWrites}\n onChange={(event) =>\n patch({ autoApproveWrites: event.target.checked })\n }\n />\n Auto-approve write actions\n </label>\n <p className=\"text-xs text-muted-foreground\">\n Requests go directly from this browser to the endpoint; the API key\n is never sent to Powerhouse servers. The endpoint must allow\n cross-origin (CORS) requests from this site.\n </p>\n <div className=\"flex items-center gap-3 pt-1\">\n <button\n type=\"button\"\n onClick={() => void saveAndTest()}\n disabled={test.status === \"testing\"}\n className=\"rounded-md bg-primary px-3 py-1.5 text-sm font-semibold text-primary-foreground hover:bg-primary/90 disabled:disabled-effect\"\n >\n {test.status === \"testing\"\n ? \"Testing…\"\n : \"Save & test connection\"}\n </button>\n {test.status === \"ok\" && (\n <span className=\"text-xs text-foreground\">{test.message}</span>\n )}\n {test.status === \"error\" && (\n <span className=\"text-xs text-destructive\">{test.message}</span>\n )}\n </div>\n </div>\n )}\n </div>\n );\n};\n","import { DangerZone as BaseDangerZone } from \"@powerhousedao/design-system/connect\";\nimport {\n deleteDrive,\n setSelectedDrive,\n showPHModal,\n useDrives,\n} from \"@powerhousedao/reactor-browser\";\nimport type { DocumentDriveDocument } from \"@powerhousedao/shared/document-drive\";\n\nexport const DangerZone: React.FC = () => {\n const drives = useDrives();\n\n const handleDeleteDrive = async (drive: DocumentDriveDocument) => {\n await deleteDrive(drive.header.id);\n setSelectedDrive(undefined);\n };\n\n const handleClearStorage = () => {\n showPHModal({ type: \"clearStorage\" });\n };\n\n return (\n <BaseDangerZone\n drives={drives ?? []}\n onDeleteDrive={(drive) => {\n void handleDeleteDrive(drive);\n }}\n onClearStorage={handleClearStorage}\n />\n );\n};\n","import { DefaultEditor as BaseDefaultEditor } from \"@powerhousedao/design-system/connect\";\nimport { useCallback, useState } from \"react\";\n\nconst documentModelEditorOptions = [\n { label: \"V1\", value: \"document-model-editor\" },\n { label: \"V2\", value: \"document-model-editor-v2\" },\n] as const;\n\nexport const DefaultEditor: React.FC = () => {\n const [documentModelEditor, setDocumentModelEditor] = useState<\n (typeof documentModelEditorOptions)[number]\n >(documentModelEditorOptions[1]);\n\n const handleSetDocumentEditor = useCallback((value: string) => {\n const option = documentModelEditorOptions.find((dm) => dm.value == value);\n if (option) {\n setDocumentModelEditor(option);\n }\n }, []);\n\n return (\n <BaseDefaultEditor\n documentModelEditor={documentModelEditor.value}\n setDocumentModelEditor={handleSetDocumentEditor}\n documentModelEditorOptions={\n documentModelEditorOptions as unknown as {\n value: string;\n label: string;\n }[]\n }\n />\n );\n};\n","import { useRegistryPackages } from \"@powerhousedao/connect/hooks\";\nimport { toast } from \"@powerhousedao/connect/services\";\nimport { PackageManager } from \"@powerhousedao/design-system/connect\";\nimport { useVetraPackageManager } from \"@powerhousedao/reactor-browser\";\nimport React from \"react\";\n\n/**\n * Split a `name@tag` spec into its bare package name. Mirrors the design-system's\n * `parsePackageSpec` — duplicated here to avoid reaching into a deep subpath\n * export. Scoped names split on the LAST `@`, unscoped on the first.\n */\nfunction parseBareName(spec: string): string {\n const trimmed = spec.trim();\n const at = trimmed.startsWith(\"@\")\n ? trimmed.lastIndexOf(\"@\")\n : trimmed.indexOf(\"@\");\n return at > 0 ? trimmed.slice(0, at) : trimmed;\n}\n\nexport const ConnectPackageManager: React.FC = () => {\n const packageManager = useVetraPackageManager();\n const {\n registryPackageList,\n installedPackages,\n availablePackages,\n availableHasMore,\n isLoadingAvailable,\n isLoadingMoreAvailable,\n availableError,\n ensureAvailableLoaded,\n fetchAvailablePage,\n loadMoreAvailable,\n setAvailableSearch,\n updateRegistryPackageStatus,\n registerFallbackRegistryPackage,\n } = useRegistryPackages();\n\n async function handleInstall(packageSpec: string) {\n if (!packageManager) return;\n\n // The spec may include an explicit `@tag` / `@version` suffix from the\n // search input. Pass it through to the package manager as-is (the CDN\n // route already handles `name@tag` specs), but track status under the\n // bare name so the Settings list doesn't fragment.\n const bareName = parseBareName(packageSpec);\n // Packages that were in `/packages` when the user clicked Install go\n // through `updateRegistryPackageStatus` and hit an existing entry.\n // Packages that landed via the npm-uplink fallback weren't in the list,\n // so we register them explicitly rather than silently upserting a\n // placeholder — keeping the \"does not exist\" error as a signal for any\n // other code path that reaches update without a matching entry.\n const wasKnownToRegistry = registryPackageList.some(\n (p) => p.name === bareName,\n );\n const result = await packageManager.addPackage(packageSpec);\n if (result.type === \"success\") {\n if (wasKnownToRegistry) {\n updateRegistryPackageStatus(bareName, \"registry-install\");\n } else {\n registerFallbackRegistryPackage(\n bareName,\n result.package,\n packageManager.getPackageVersion(bareName) ??\n packageManager.getPackageVersion(packageSpec),\n \"registry-install\",\n );\n }\n toast(`Package \"${packageSpec}\" installed successfully`, {\n type: \"connect-success\",\n });\n } else {\n const message = result.error.message;\n console.error(\n `[Connect][PackageManager] Install failed for \"${packageSpec}\":`,\n result.error,\n );\n // `BrowserPackageManager` raises a generic \"Failed to fetch dynamically\n // imported module\" when the registry CDN returns an error. That covers\n // both \"the name exists nowhere (not on this registry AND not on the\n // npmjs uplink)\" and \"the tarball is there but doesn't look like a\n // Powerhouse package\". Map to plain-language copy — never dump the raw\n // API / loader message into the toast.\n const isLikelyNotFound = /failed to fetch|404|not found/i.test(message);\n const userMessage = isLikelyNotFound\n ? `Couldn't install \"${packageSpec}\". It isn't available on this registry or via npm.`\n : `Couldn't install \"${packageSpec}\". Please try again.`;\n toast(userMessage, { type: \"error\" });\n }\n }\n\n function handleUninstall(packageName: string) {\n if (!packageManager) return;\n try {\n packageManager.removePackage(packageName);\n updateRegistryPackageStatus(packageName, \"available\");\n\n toast(`Package \"${packageName}\" uninstalled successfully`, {\n type: \"connect-success\",\n });\n } catch (error) {\n console.error(\n `[Connect][PackageManager] Uninstall failed for \"${packageName}\":`,\n error,\n );\n toast(`Couldn't uninstall \"${packageName}\". Please try again.`, {\n type: \"error\",\n });\n }\n }\n\n return (\n <div className=\"flex min-h-0 flex-1 flex-col\">\n <PackageManager\n mutable={true}\n installedPackages={installedPackages}\n availablePackages={availablePackages}\n isAvailableLoading={isLoadingAvailable}\n isLoadingMoreAvailable={isLoadingMoreAvailable}\n hasMoreAvailable={availableHasMore}\n onLoadMoreAvailable={loadMoreAvailable}\n availableError={availableError}\n onAvailableRetry={() => {\n void fetchAvailablePage({ reset: true });\n }}\n onAvailableSearchChange={setAvailableSearch}\n onAvailableTabOpen={ensureAvailableLoaded}\n onInstall={handleInstall}\n onUninstall={handleUninstall}\n />\n </div>\n );\n};\n","import {\n SettingsModal as SettingsModalV2,\n ThemeSwitch,\n} from \"@powerhousedao/design-system/connect\";\nimport { connectConfig } from \"@powerhousedao/connect/config\";\nimport { closePHModal, usePHModal } from \"@powerhousedao/reactor-browser\";\nimport { t } from \"i18next\";\nimport { Bot, CircleHelp, Package, PenLine, TriangleAlert } from \"lucide-react\";\nimport React, { useMemo } from \"react\";\nimport { About } from \"./settings/about.js\";\nimport { AiAssistant } from \"./settings/ai-assistant.js\";\nimport { DangerZone } from \"./settings/danger-zone.js\";\nimport { DefaultEditor } from \"./settings/default-editor.js\";\nimport { ConnectPackageManager } from \"./settings/package-manager.js\";\n\nconst iconProps = { size: 16 } as const;\n\nexport const SettingsModal: React.FC = () => {\n const phModal = usePHModal();\n const open = phModal?.type === \"settings\";\n\n const tabs = useMemo(\n () => [\n ...(connectConfig.aiAssistantEnabled\n ? [\n {\n id: \"ai-assistant\",\n icon: <Bot {...iconProps} />,\n label: \"AI Assistant\",\n content: AiAssistant,\n },\n ]\n : []),\n {\n id: \"package-manager\",\n icon: <Package {...iconProps} />,\n label: \"Package Manager\",\n content: ConnectPackageManager,\n },\n {\n id: \"default-editors\",\n icon: <PenLine {...iconProps} />,\n label: \"Default Editors\",\n content: DefaultEditor,\n },\n {\n id: \"danger-zone\",\n icon: <TriangleAlert {...iconProps} className=\"text-destructive\" />,\n label: <span className=\"text-destructive\">Danger Zone</span>,\n content: () => <DangerZone />,\n },\n {\n id: \"about\",\n icon: <CircleHelp {...iconProps} />,\n label: \"About\",\n content: About,\n },\n ],\n [],\n );\n\n return (\n <SettingsModalV2\n open={open}\n title={t(\"modals.connectSettings.title\")}\n onOpenChange={(status: boolean) => {\n if (!status) return closePHModal();\n }}\n tabs={tabs}\n navFooter={<ThemeSwitch horizontal />}\n />\n );\n};\n"],"names":["About","BaseAbout","packageJson","DangerZone","BaseDangerZone","DefaultEditor","BaseDefaultEditor","DefaultEditor","DangerZone","About","SettingsModalV2"],"mappings":"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","debug_id":"5852ca79-deda-5cec-b6ee-8b9d57223aaf"}
1
+ {"version":3,"file":"SettingsModal-CgqnSXs5.js","sources":["../package.json","../src/components/modal/modals/settings/about.tsx","../src/components/modal/modals/settings/ai-assistant.tsx","../src/components/modal/modals/settings/danger-zone.tsx","../src/components/modal/modals/settings/default-editor.tsx","../src/components/modal/modals/settings/package-manager.tsx","../src/components/modal/modals/SettingsModal.tsx"],"sourcesContent":["","import { connectConfig } from \"@powerhousedao/connect/config\";\nimport {\n getGitSha,\n getGitUrl,\n packageJson,\n shortGitSha,\n} from \"@powerhousedao/connect/utils\";\nimport { Icon } from \"@powerhousedao/design-system\";\nimport { About as BaseAbout } from \"@powerhousedao/design-system/connect\";\nimport {\n closePHModal,\n DriveCollectionId,\n showPHModal,\n useDrives,\n useDriveSystemInfo,\n useSyncList,\n} from \"@powerhousedao/reactor-browser\";\nimport type { DocumentDriveDocument } from \"@powerhousedao/shared/document-drive\";\nimport { useMemo } from \"react\";\n\nexport const About: React.FC = () => {\n const onOpenInspector = () => {\n closePHModal();\n showPHModal({ type: \"inspector\" });\n };\n\n return (\n <div>\n <BaseAbout\n packageJson={packageJson}\n phCliVersion={\n typeof connectConfig.phCliVersion === \"string\"\n ? connectConfig.phCliVersion\n : undefined\n }\n />\n <AppGitHash />\n <ConnectedDrives />\n <div className=\"bg-background p-3\">\n <h2 className=\"mb-2 font-semibold text-foreground\">Inspector</h2>\n <p className=\"mb-3 text-sm font-normal text-foreground\">\n Explore the local database and sync state for debugging.\n </p>\n <button\n className=\"flex items-center gap-x-2 rounded-md border border-border bg-transparent px-3 py-1 text-sm font-medium text-foreground transition-colors hover:hover-effect\"\n onClick={onOpenInspector}\n type=\"button\"\n >\n Open Inspector <Icon name=\"CircleInfo\" size={16} />\n </button>\n </div>\n </div>\n );\n};\n\nfunction AppGitHash() {\n const sha = getGitSha();\n if (sha === \"unknown\") return null;\n const url = getGitUrl();\n const label = shortGitSha(sha);\n return (\n <div className=\"bg-background p-3 text-sm\">\n <span className=\"font-semibold\">Git hash: </span>\n {url ? (\n <a\n className=\"font-mono underline\"\n href={url}\n target=\"_blank\"\n rel=\"noreferrer noopener\"\n title={sha}\n >\n {label}\n </a>\n ) : (\n <span className=\"font-mono\" title={sha}>\n {label}\n </span>\n )}\n </div>\n );\n}\n\nfunction ConnectedDrives() {\n const drives = useDrives() ?? [];\n const remotes = useSyncList();\n\n const remoteDrives = useMemo(\n () =>\n drives.filter((d) =>\n remotes.some((r) =>\n r.meta.collectionId.equals(DriveCollectionId.forDrive(d.header.id)),\n ),\n ),\n [drives, remotes],\n );\n\n return (\n <div className=\"my-4 bg-background p-3\">\n <h2 className=\"mb-2 font-semibold text-foreground\">Connected drives</h2>\n {remoteDrives.length === 0 ? (\n <p className=\"text-sm font-normal text-foreground\">\n No connected remote drives.\n </p>\n ) : (\n <ul className=\"flex flex-col gap-3\">\n {remoteDrives.map((drive) => (\n <DriveAboutEntry key={drive.header.id} drive={drive} />\n ))}\n </ul>\n )}\n </div>\n );\n}\n\nfunction DriveAboutEntry({ drive }: { drive: DocumentDriveDocument }) {\n const info = useDriveSystemInfo(drive);\n const name = drive.state.global.name || drive.header.name;\n\n return (\n <li className=\"text-sm text-foreground\">\n <div className=\"flex items-baseline gap-2\">\n <span className=\"font-medium\">{name}</span>\n {info.status === \"ready\" && (\n <span className=\"text-xs text-muted-foreground\">{info.host}</span>\n )}\n </div>\n {info.status === \"loading\" && (\n <div className=\"mt-1 text-xs text-muted-foreground\">Loading…</div>\n )}\n {info.status === \"error\" && (\n <div className=\"mt-1 text-xs text-destructive\">\n Could not load system info\n </div>\n )}\n {info.status === \"ready\" && (\n <div className=\"mt-1 text-xs text-foreground\">\n <div>\n <span className=\"font-medium\">Version:</span> {info.version}\n </div>\n <div>\n <span className=\"font-medium\">Git hash: </span>\n {info.gitUrl ? (\n <a\n className=\"font-mono hover:underline\"\n href={info.gitUrl}\n target=\"_blank\"\n rel=\"noreferrer noopener\"\n title={info.gitHash}\n >\n {shortGitSha(info.gitHash)}\n </a>\n ) : (\n <span className=\"font-mono\" title={info.gitHash}>\n {shortGitSha(info.gitHash)}\n </span>\n )}\n </div>\n </div>\n )}\n {info.status === \"local\" && (\n <div className=\"mt-1 text-xs text-muted-foreground\">\n Local drive — N/A\n </div>\n )}\n </li>\n );\n}\n","import {\n getAiSettings,\n isAiConfigured,\n updateAiSettings,\n type AiSettings,\n} from \"@powerhousedao/reactor-browser/ai\";\nimport { Bot } from \"lucide-react\";\nimport { useState } from \"react\";\n\ntype TestState =\n | { status: \"idle\" }\n | { status: \"testing\" }\n | { status: \"ok\"; message: string }\n | { status: \"error\"; message: string };\n\nconst FIELD_CLASS =\n \"w-full rounded-md border border-border bg-background p-2.5 text-sm text-foreground outline-none placeholder:text-muted-foreground\";\n\n/**\n * Settings-modal section for the in-browser AI assistant: the user master\n * toggle, the OpenAI-compatible endpoint configuration, and a\n * save-and-test-connection action. Only mounted when the deployment\n * enables the feature (`connect.ai.assistantEnabled`).\n */\nexport const AiAssistant: React.FC = () => {\n const [draft, setDraft] = useState<AiSettings>(() => getAiSettings());\n const [test, setTest] = useState<TestState>({ status: \"idle\" });\n\n const patch = (value: Partial<AiSettings>) =>\n setDraft((d) => ({ ...d, ...value }));\n\n const saveAndTest = async () => {\n updateAiSettings(draft);\n if (!isAiConfigured(draft)) {\n setTest({\n status: \"error\",\n message: \"Base URL, API key and model are all required.\",\n });\n return;\n }\n setTest({ status: \"testing\" });\n try {\n const base = draft.baseUrl.trim().replace(/\\/+$/, \"\");\n const res = await fetch(`${base}/models`, {\n headers: draft.apiKey.trim()\n ? { Authorization: `Bearer ${draft.apiKey.trim()}` }\n : undefined,\n });\n if (!res.ok) {\n throw new Error(\n `HTTP ${res.status}${res.statusText ? ` ${res.statusText}` : \"\"}`,\n );\n }\n const data: unknown = await res.json();\n const list = (data as { data?: unknown[] } | null)?.data;\n const count = Array.isArray(list) ? list.length : 0;\n setTest({\n status: \"ok\",\n message: count\n ? `Connected — ${count} model(s) available.`\n : \"Connected.\",\n });\n } catch (error) {\n setTest({\n status: \"error\",\n message: `Connection failed: ${\n error instanceof Error ? error.message : String(error)\n }`,\n });\n }\n };\n\n return (\n <div className=\"space-y-4 p-1\">\n <div>\n <h3 className=\"flex items-center gap-2 text-sm font-semibold text-foreground\">\n <Bot size={16} /> AI Assistant\n </h3>\n <p className=\"mt-1 text-xs text-muted-foreground\">\n An in-page assistant that creates and changes documents, manages\n drives, and inspects read models through an OpenAI-compatible\n endpoint.\n </p>\n </div>\n\n <label className=\"flex items-center gap-2 text-sm text-foreground\">\n <input\n type=\"checkbox\"\n checked={draft.enabled}\n onChange={(event) => {\n patch({ enabled: event.target.checked });\n // The switch acts immediately: the FAB mounts/unmounts at once.\n updateAiSettings({ enabled: event.target.checked });\n }}\n />\n Enable AI assistant\n </label>\n\n {draft.enabled && (\n <div className=\"space-y-2 rounded-lg border border-border p-3\">\n <div className=\"mb-1 text-xs font-semibold tracking-wide text-muted-foreground uppercase\">\n Endpoint\n </div>\n <input\n type=\"url\"\n placeholder=\"Base URL (e.g. https://api.openai.com/v1)\"\n value={draft.baseUrl}\n onChange={(e) => patch({ baseUrl: e.target.value })}\n className={FIELD_CLASS}\n spellCheck={false}\n />\n <input\n type=\"password\"\n placeholder=\"API key\"\n value={draft.apiKey}\n onChange={(e) => patch({ apiKey: e.target.value })}\n className={FIELD_CLASS}\n autoComplete=\"off\"\n spellCheck={false}\n />\n <input\n type=\"text\"\n placeholder=\"Model (e.g. gpt-4o-mini)\"\n value={draft.model}\n onChange={(e) => patch({ model: e.target.value })}\n className={FIELD_CLASS}\n spellCheck={false}\n />\n <label className=\"flex items-center gap-2 pt-1 text-sm text-foreground\">\n <input\n type=\"checkbox\"\n checked={draft.autoApproveWrites}\n onChange={(event) =>\n patch({ autoApproveWrites: event.target.checked })\n }\n />\n Auto-approve write actions\n </label>\n <p className=\"text-xs text-muted-foreground\">\n Requests go directly from this browser to the endpoint; the API key\n is never sent to Powerhouse servers. The endpoint must allow\n cross-origin (CORS) requests from this site.\n </p>\n <div className=\"flex items-center gap-3 pt-1\">\n <button\n type=\"button\"\n onClick={() => void saveAndTest()}\n disabled={test.status === \"testing\"}\n className=\"rounded-md bg-primary px-3 py-1.5 text-sm font-semibold text-primary-foreground hover:bg-primary/90 disabled:disabled-effect\"\n >\n {test.status === \"testing\"\n ? \"Testing…\"\n : \"Save & test connection\"}\n </button>\n {test.status === \"ok\" && (\n <span className=\"text-xs text-foreground\">{test.message}</span>\n )}\n {test.status === \"error\" && (\n <span className=\"text-xs text-destructive\">{test.message}</span>\n )}\n </div>\n </div>\n )}\n </div>\n );\n};\n","import { DangerZone as BaseDangerZone } from \"@powerhousedao/design-system/connect\";\nimport {\n deleteDrive,\n setSelectedDrive,\n showPHModal,\n useDrives,\n} from \"@powerhousedao/reactor-browser\";\nimport type { DocumentDriveDocument } from \"@powerhousedao/shared/document-drive\";\n\nexport const DangerZone: React.FC = () => {\n const drives = useDrives();\n\n const handleDeleteDrive = async (drive: DocumentDriveDocument) => {\n await deleteDrive(drive.header.id);\n setSelectedDrive(undefined);\n };\n\n const handleClearStorage = () => {\n showPHModal({ type: \"clearStorage\" });\n };\n\n return (\n <BaseDangerZone\n drives={drives ?? []}\n onDeleteDrive={(drive) => {\n void handleDeleteDrive(drive);\n }}\n onClearStorage={handleClearStorage}\n />\n );\n};\n","import { DefaultEditor as BaseDefaultEditor } from \"@powerhousedao/design-system/connect\";\nimport { useCallback, useState } from \"react\";\n\nconst documentModelEditorOptions = [\n { label: \"V1\", value: \"document-model-editor\" },\n { label: \"V2\", value: \"document-model-editor-v2\" },\n] as const;\n\nexport const DefaultEditor: React.FC = () => {\n const [documentModelEditor, setDocumentModelEditor] = useState<\n (typeof documentModelEditorOptions)[number]\n >(documentModelEditorOptions[1]);\n\n const handleSetDocumentEditor = useCallback((value: string) => {\n const option = documentModelEditorOptions.find((dm) => dm.value == value);\n if (option) {\n setDocumentModelEditor(option);\n }\n }, []);\n\n return (\n <BaseDefaultEditor\n documentModelEditor={documentModelEditor.value}\n setDocumentModelEditor={handleSetDocumentEditor}\n documentModelEditorOptions={\n documentModelEditorOptions as unknown as {\n value: string;\n label: string;\n }[]\n }\n />\n );\n};\n","import { useRegistryPackages } from \"@powerhousedao/connect/hooks\";\nimport { toast } from \"@powerhousedao/connect/services\";\nimport { PackageManager } from \"@powerhousedao/design-system/connect\";\nimport { useVetraPackageManager } from \"@powerhousedao/reactor-browser\";\nimport React from \"react\";\n\n/**\n * Split a `name@tag` spec into its bare package name. Mirrors the design-system's\n * `parsePackageSpec` — duplicated here to avoid reaching into a deep subpath\n * export. Scoped names split on the LAST `@`, unscoped on the first.\n */\nfunction parseBareName(spec: string): string {\n const trimmed = spec.trim();\n const at = trimmed.startsWith(\"@\")\n ? trimmed.lastIndexOf(\"@\")\n : trimmed.indexOf(\"@\");\n return at > 0 ? trimmed.slice(0, at) : trimmed;\n}\n\nexport const ConnectPackageManager: React.FC = () => {\n const packageManager = useVetraPackageManager();\n const {\n registryPackageList,\n installedPackages,\n availablePackages,\n availableHasMore,\n isLoadingAvailable,\n isLoadingMoreAvailable,\n availableError,\n ensureAvailableLoaded,\n ensureInstalledMetadataLoaded,\n fetchAvailablePage,\n loadMoreAvailable,\n setAvailableSearch,\n updateRegistryPackageStatus,\n registerFallbackRegistryPackage,\n } = useRegistryPackages();\n\n async function handleInstall(packageSpec: string) {\n if (!packageManager) return;\n\n // The spec may include an explicit `@tag` / `@version` suffix from the\n // search input. Pass it through to the package manager as-is (the CDN\n // route already handles `name@tag` specs), but track status under the\n // bare name so the Settings list doesn't fragment.\n const bareName = parseBareName(packageSpec);\n // Packages that were in `/packages` when the user clicked Install go\n // through `updateRegistryPackageStatus` and hit an existing entry.\n // Packages that landed via the npm-uplink fallback weren't in the list,\n // so we register them explicitly rather than silently upserting a\n // placeholder — keeping the \"does not exist\" error as a signal for any\n // other code path that reaches update without a matching entry.\n const wasKnownToRegistry = registryPackageList.some(\n (p) => p.name === bareName,\n );\n const result = await packageManager.addPackage(packageSpec);\n if (result.type === \"success\") {\n if (wasKnownToRegistry) {\n updateRegistryPackageStatus(bareName, \"registry-install\");\n } else {\n registerFallbackRegistryPackage(\n bareName,\n result.package,\n packageManager.getPackageVersion(bareName) ??\n packageManager.getPackageVersion(packageSpec),\n \"registry-install\",\n );\n }\n toast(`Package \"${packageSpec}\" installed successfully`, {\n type: \"connect-success\",\n });\n } else {\n const message = result.error.message;\n console.error(\n `[Connect][PackageManager] Install failed for \"${packageSpec}\":`,\n result.error,\n );\n // `BrowserPackageManager` raises a generic \"Failed to fetch dynamically\n // imported module\" when the registry CDN returns an error. That covers\n // both \"the name exists nowhere (not on this registry AND not on the\n // npmjs uplink)\" and \"the tarball is there but doesn't look like a\n // Powerhouse package\". Map to plain-language copy — never dump the raw\n // API / loader message into the toast.\n const isLikelyNotFound = /failed to fetch|404|not found/i.test(message);\n const userMessage = isLikelyNotFound\n ? `Couldn't install \"${packageSpec}\". It isn't available on this registry or via npm.`\n : `Couldn't install \"${packageSpec}\". Please try again.`;\n toast(userMessage, { type: \"error\" });\n }\n }\n\n function handleUninstall(packageName: string) {\n if (!packageManager) return;\n try {\n packageManager.removePackage(packageName);\n updateRegistryPackageStatus(packageName, \"available\");\n\n toast(`Package \"${packageName}\" uninstalled successfully`, {\n type: \"connect-success\",\n });\n } catch (error) {\n console.error(\n `[Connect][PackageManager] Uninstall failed for \"${packageName}\":`,\n error,\n );\n toast(`Couldn't uninstall \"${packageName}\". Please try again.`, {\n type: \"error\",\n });\n }\n }\n\n return (\n <div className=\"flex min-h-0 flex-1 flex-col\">\n <PackageManager\n mutable={true}\n installedPackages={installedPackages}\n availablePackages={availablePackages}\n isAvailableLoading={isLoadingAvailable}\n isLoadingMoreAvailable={isLoadingMoreAvailable}\n hasMoreAvailable={availableHasMore}\n onLoadMoreAvailable={loadMoreAvailable}\n availableError={availableError}\n onAvailableRetry={() => {\n void fetchAvailablePage({ reset: true });\n }}\n onAvailableSearchChange={setAvailableSearch}\n onAvailableTabOpen={ensureAvailableLoaded}\n onInstalledTabOpen={ensureInstalledMetadataLoaded}\n onInstall={handleInstall}\n onUninstall={handleUninstall}\n />\n </div>\n );\n};\n","import {\n SettingsModal as SettingsModalV2,\n ThemeSwitch,\n} from \"@powerhousedao/design-system/connect\";\nimport { connectConfig } from \"@powerhousedao/connect/config\";\nimport { closePHModal, usePHModal } from \"@powerhousedao/reactor-browser\";\nimport { t } from \"i18next\";\nimport { Bot, CircleHelp, Package, PenLine, TriangleAlert } from \"lucide-react\";\nimport React, { useMemo } from \"react\";\nimport { About } from \"./settings/about.js\";\nimport { AiAssistant } from \"./settings/ai-assistant.js\";\nimport { DangerZone } from \"./settings/danger-zone.js\";\nimport { DefaultEditor } from \"./settings/default-editor.js\";\nimport { ConnectPackageManager } from \"./settings/package-manager.js\";\n\nconst iconProps = { size: 16 } as const;\n\nexport const SettingsModal: React.FC = () => {\n const phModal = usePHModal();\n const open = phModal?.type === \"settings\";\n\n const tabs = useMemo(\n () => [\n ...(connectConfig.aiAssistantEnabled\n ? [\n {\n id: \"ai-assistant\",\n icon: <Bot {...iconProps} />,\n label: \"AI Assistant\",\n content: AiAssistant,\n },\n ]\n : []),\n {\n id: \"package-manager\",\n icon: <Package {...iconProps} />,\n label: \"Package Manager\",\n content: ConnectPackageManager,\n },\n {\n id: \"default-editors\",\n icon: <PenLine {...iconProps} />,\n label: \"Default Editors\",\n content: DefaultEditor,\n },\n {\n id: \"danger-zone\",\n icon: <TriangleAlert {...iconProps} className=\"text-destructive\" />,\n label: <span className=\"text-destructive\">Danger Zone</span>,\n content: () => <DangerZone />,\n },\n {\n id: \"about\",\n icon: <CircleHelp {...iconProps} />,\n label: \"About\",\n content: About,\n },\n ],\n [],\n );\n\n return (\n <SettingsModalV2\n open={open}\n title={t(\"modals.connectSettings.title\")}\n onOpenChange={(status: boolean) => {\n if (!status) return closePHModal();\n }}\n tabs={tabs}\n navFooter={<ThemeSwitch horizontal />}\n />\n );\n};\n"],"names":["About","BaseAbout","packageJson","DangerZone","BaseDangerZone","DefaultEditor","BaseDefaultEditor","DefaultEditor","DangerZone","About","SettingsModalV2"],"mappings":"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","debug_id":"2611d2b5-5e71-5305-a0df-eecf8618a3bc"}
@@ -1,5 +1,5 @@
1
1
 
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="8ddd55a3-f7ce-5c8f-900a-961313c74af8")}catch(e){}}();
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="b3199364-42fc-5e8f-a94a-473c7a7b966a")}catch(e){}}();
3
3
  import { n as getRuntimeConfig } from "./runtime-config-DL3QjcfM.js";
4
4
  import { c as idbError } from "./pglite-major-B39aOEiE.js";
5
5
  import { n as cloneFileData, r as migrateIdb } from "./pglite-migrate-core-Boxqqx-c.js";
@@ -13,7 +13,7 @@ import { t as useOpenRenownLogin } from "./use-renown-login-Cf3va6u_.js";
13
13
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
14
14
  import { useTranslation } from "react-i18next";
15
15
  import React, { StrictMode, Suspense, createElement, lazy, useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
16
- import { DriveCollectionId, RenownWalletProvider, addFileWithProgress, getRevisionFromDate, initTheme, logout, setPHAppConfig, setPHDocumentEditorConfig, setRevisionHistoryVisible, setSelectedDrive, setSelectedNode, showPHModal, useAppModuleById, useConnectionStates, useDefaultAppModule, useDocumentById, useDocumentModelModuleById, useDocumentOperations, useDocumentVersionStatus, useDrives, useEditorModuleById, useEditorPreloader, useFallbackEditorModule, useIsAddDriveEnabled, useNodeParentFolderById, usePHModal, usePackageDiscoveryService, useReactorClientModule, useRevisionHistoryVisible, useSelectedDocument, useSelectedDocumentId, useSelectedDriveSafe, useSelectedFolder, useSelectedTimelineItem, useSyncList, useTheme, useUser, useVetraPackageManager, useVetraPackages } from "@powerhousedao/reactor-browser";
16
+ import { DriveCollectionId, RenownWalletProvider, addFileWithProgress, getRevisionFromDate, initTheme, logout, setPHAppConfig, setPHDocumentEditorConfig, setRevisionHistoryVisible, setSelectedDrive, setSelectedNode, showPHModal, useAppModuleById, useConnectionStates, useDefaultAppModule, useDocumentById, useDocumentModelModuleById, useDocumentOperations, useDocumentVersionStatus, useDrives, useEditorModuleById, useEditorPreloader, useFallbackEditorModule, useIsAddDriveEnabled, useNodeParentFolderById, usePHModal, usePackageDiscoveryService, useReactorClientModule, useRevisionHistoryVisible, useSelectedDocument, useSelectedDocumentId, useSelectedDriveId, useSelectedDriveSafe, useSelectedFolder, useSelectedTimelineItem, useSync, useSyncList, useTheme, useUser, useVetraPackageManager, useVetraPackages } from "@powerhousedao/reactor-browser";
17
17
  import { childLogger } from "document-model";
18
18
  import { ConnectSidebar, ConnectTooltipProvider, DefaultEditorLoader, DriveAuthGate, DropZoneWrapper, HomeScreen, HomeScreenAddDriveItem, HomeScreenItem, LogoAnimation, PackageInstallModal, RevisionHistory, SidebarAddDriveItem, SidebarItem, UploadFileListContainer, formatFileSize, mapProgressStageToStatus, useEns } from "@powerhousedao/design-system/connect";
19
19
  import { Outlet, RouterProvider, createBrowserRouter, createRoutesFromChildren, matchRoutes, useLocation, useNavigationType } from "react-router-dom";
@@ -1778,25 +1778,98 @@ function Root() {
1778
1778
  }
1779
1779
  //#endregion
1780
1780
  //#region src/components/use-drive-auth-gate.ts
1781
- function computeAuthGate(isAuthenticated, connectionStates) {
1782
- let sawAuthError = false;
1783
- for (const snap of connectionStates.values()) if (snap.state === "error" && snap.requiresAuth) {
1784
- sawAuthError = true;
1785
- break;
1781
+ /**
1782
+ * Decides the gate for the drive the user selected, and only that drive. A
1783
+ * refusal on any other drive must not hide the rest of Connect.
1784
+ *
1785
+ * Connection states are keyed by remote name, which is a random uuid, so the
1786
+ * caller supplies the remote -> drive mapping.
1787
+ */
1788
+ function computeAuthGate(isAuthenticated, connectionStates, selection) {
1789
+ const { selectedDriveId, remotes } = selection;
1790
+ if (!selectedDriveId) return null;
1791
+ let refused = false;
1792
+ for (const remote of remotes) {
1793
+ if (remote.driveId !== selectedDriveId) continue;
1794
+ const snapshot = connectionStates.get(remote.name);
1795
+ if (snapshot?.state === "error" && snapshot.requiresAuth) {
1796
+ refused = true;
1797
+ break;
1798
+ }
1786
1799
  }
1787
- if (!sawAuthError) return null;
1800
+ if (!refused) return null;
1788
1801
  return isAuthenticated ? "unauthorized" : "login";
1789
1802
  }
1790
- /** Live wrapper over {@link computeAuthGate}. */
1803
+ const hiddenDriveIds = /* @__PURE__ */ new Set();
1804
+ let hiddenDriveIdsSnapshot = [];
1805
+ const hiddenDriveListeners = /* @__PURE__ */ new Set();
1806
+ function publishHiddenDrives() {
1807
+ hiddenDriveIdsSnapshot = [...hiddenDriveIds];
1808
+ for (const listener of [...hiddenDriveListeners]) listener();
1809
+ }
1810
+ function subscribeHiddenDrives(listener) {
1811
+ hiddenDriveListeners.add(listener);
1812
+ return () => {
1813
+ hiddenDriveListeners.delete(listener);
1814
+ };
1815
+ }
1816
+ function getHiddenDriveIds() {
1817
+ return hiddenDriveIdsSnapshot;
1818
+ }
1819
+ /** Hides a drive from Connect's drive lists until the page reloads. */
1820
+ function hideDriveForSession(driveId) {
1821
+ if (hiddenDriveIds.has(driveId)) return;
1822
+ hiddenDriveIds.add(driveId);
1823
+ publishHiddenDrives();
1824
+ }
1825
+ /** Restores every drive hidden this session. */
1826
+ function clearHiddenDrives() {
1827
+ if (hiddenDriveIds.size === 0) return;
1828
+ hiddenDriveIds.clear();
1829
+ publishHiddenDrives();
1830
+ }
1831
+ function useHiddenDriveIds() {
1832
+ return useSyncExternalStore(subscribeHiddenDrives, getHiddenDriveIds, getHiddenDriveIds);
1833
+ }
1834
+ /** `useDrives()` minus the drives dismissed from the auth gate this session. */
1835
+ function useVisibleDrives() {
1836
+ const drives = useDrives();
1837
+ const hidden = useHiddenDriveIds();
1838
+ return useMemo(() => {
1839
+ if (!drives || hidden.length === 0) return drives;
1840
+ return drives.filter((drive) => !hidden.includes(drive.header.id));
1841
+ }, [drives, hidden]);
1842
+ }
1843
+ /** Live wrapper over {@link computeAuthGate}, plus the dismiss action. */
1791
1844
  function useDriveAuthGate() {
1792
1845
  const user = useUser();
1793
1846
  const connectionStates = useConnectionStates();
1794
- return { gate: computeAuthGate(Boolean(user), connectionStates) };
1847
+ const syncManager = useSync();
1848
+ const selectedDriveId = useSelectedDriveId();
1849
+ const remotes = useMemo(() => (syncManager?.list() ?? []).map((remote) => ({
1850
+ name: remote.meta.name,
1851
+ driveId: remote.meta.collectionId.driveId
1852
+ })), [syncManager, connectionStates]);
1853
+ const address = user?.address;
1854
+ useEffect(() => {
1855
+ clearHiddenDrives();
1856
+ }, [address]);
1857
+ const dismiss = useCallback(() => {
1858
+ if (selectedDriveId) hideDriveForSession(selectedDriveId);
1859
+ setSelectedDrive(void 0);
1860
+ }, [selectedDriveId]);
1861
+ return {
1862
+ gate: computeAuthGate(Boolean(user), connectionStates, {
1863
+ selectedDriveId,
1864
+ remotes
1865
+ }),
1866
+ dismiss
1867
+ };
1795
1868
  }
1796
1869
  //#endregion
1797
1870
  //#region src/pages/content.tsx
1798
1871
  function Content() {
1799
- const { gate } = useDriveAuthGate();
1872
+ const { gate, dismiss } = useDriveAuthGate();
1800
1873
  const openLogin = useOpenRenownLogin();
1801
1874
  const [selectedDrive] = useSelectedDriveSafe();
1802
1875
  const selectedFolder = useSelectedFolder();
@@ -1812,7 +1885,8 @@ function Content() {
1812
1885
  children: /* @__PURE__ */ jsx(DriveAuthGate, {
1813
1886
  mode: gate,
1814
1887
  onLogin: openLogin,
1815
- onLogout: () => void logout()
1888
+ onLogout: () => void logout(),
1889
+ onClose: dismiss
1816
1890
  })
1817
1891
  }) : !selectedDocumentId && !selectedDrive && !selectedFolder ? /* @__PURE__ */ jsx(HomeScreenContainer, {}) : /* @__PURE__ */ jsx(AppContainer, {}) });
1818
1892
  }
@@ -1833,7 +1907,7 @@ function DriveItem({ drive }) {
1833
1907
  }, drive.header.id);
1834
1908
  }
1835
1909
  function HomeScreenContainer() {
1836
- const drives = useDrives();
1910
+ const drives = useVisibleDrives();
1837
1911
  const isAddDriveEnabled = useIsAddDriveEnabled();
1838
1912
  const homeBackground = getRuntimeConfig().connect?.branding?.homeBackground;
1839
1913
  return /* @__PURE__ */ jsxs(HomeScreen, {
@@ -1880,7 +1954,7 @@ const Router = () => {
1880
1954
  function Sidebar() {
1881
1955
  const user = useUser();
1882
1956
  const openLogin = useOpenRenownLogin();
1883
- const drives = useDrives();
1957
+ const drives = useVisibleDrives();
1884
1958
  const isAddDriveEnabled = useIsAddDriveEnabled();
1885
1959
  const [selectedDrive] = useSelectedDriveSafe();
1886
1960
  const connectDebug = localStorage.getItem("CONNECT_DEBUG") === "true";
@@ -1912,12 +1986,12 @@ function Sidebar() {
1912
1986
  variant: "text",
1913
1987
  fallbackMessage: "There was an error loading drives",
1914
1988
  loggerContext: ["Connect", "Sidebar"],
1915
- children: [drives?.map((drive, index) => /* @__PURE__ */ jsx(SidebarItem, {
1989
+ children: [drives?.map((drive) => /* @__PURE__ */ jsx(SidebarItem, {
1916
1990
  title: drive.header.name,
1917
1991
  onClick: () => setSelectedDrive(drive),
1918
1992
  active: selectedDrive?.header.id === drive.header.id,
1919
1993
  icon: /* @__PURE__ */ jsx(DriveIcon, { drive })
1920
- }, index)), isAddDriveEnabled && /* @__PURE__ */ jsx(SidebarAddDriveItem, { onClick: onAddDriveClick })]
1994
+ }, drive.header.id)), isAddDriveEnabled && /* @__PURE__ */ jsx(SidebarAddDriveItem, { onClick: onAddDriveClick })]
1921
1995
  })
1922
1996
  }) });
1923
1997
  }
@@ -2056,11 +2130,11 @@ const MigrationBanner = () => {
2056
2130
  };
2057
2131
  //#endregion
2058
2132
  //#region src/components/modal/modals-container.tsx
2059
- const AddDriveModal$1 = lazy(() => import("./AddDriveModal-Q-Ffh5XA.js").then((m) => ({ default: m.AddDriveModal })));
2060
- const ClearStorageModal = lazy(() => import("./ClearStorageModal-Dm2ztjgw.js").then((m) => ({ default: m.ClearStorageModal })));
2133
+ const AddDriveModal$1 = lazy(() => import("./AddDriveModal-xAw48kdl.js").then((m) => ({ default: m.AddDriveModal })));
2134
+ const ClearStorageModal = lazy(() => import("./ClearStorageModal-BHLFpni1.js").then((m) => ({ default: m.ClearStorageModal })));
2061
2135
  const CookiesPolicyModal = lazy(() => import("./CookiesPolicyModal-BpyPPyxS.js").then((m) => ({ default: m.CookiesPolicyModal })));
2062
2136
  const CreateDocumentModal$1 = lazy(() => import("./CreateDocumentModal-C45W8J0V.js").then((m) => ({ default: m.CreateDocumentModal })));
2063
- const DebugSettingsModal = lazy(() => import("./DebugSettingsModal-BWOSNOw8.js").then((m) => ({ default: m.DebugSettingsModal })));
2137
+ const DebugSettingsModal = lazy(() => import("./DebugSettingsModal-cFjT1zO6.js").then((m) => ({ default: m.DebugSettingsModal })));
2064
2138
  const DeleteDriveModal = lazy(() => import("./DeleteDriveModal-Dug84t1j.js").then((m) => ({ default: m.DeleteDriveModal })));
2065
2139
  const DeleteItemModal = lazy(() => import("./DeleteItemModal-mMzy3V8o.js").then((m) => ({ default: m.DeleteItemModal })));
2066
2140
  const DisclaimerModal = lazy(() => import("./DisclaimerModal-DvpD5dPM.js").then((m) => ({ default: m.DisclaimerModal })));
@@ -2068,7 +2142,7 @@ const DriveSettingsModal$1 = lazy(() => import("./DriveSettingsModal-C6-mRQiv.js
2068
2142
  const DownloadDocumentWithErrorsModal = lazy(() => import("./DownloadDocumentWithErrorsModal-IKl0ljQb.js").then((m) => ({ default: m.DownloadDocumentWithErrorsModal })));
2069
2143
  const DocumentVersionUnsupportedModal = lazy(() => import("./DocumentVersionUnsupportedModal-C3Q0V75l.js").then((m) => ({ default: m.DocumentVersionUnsupportedModal })));
2070
2144
  const ConfirmDocumentUpgradeModal = lazy(() => import("./ConfirmDocumentUpgradeModal-FUOhZsk7.js").then((m) => ({ default: m.ConfirmDocumentUpgradeModal })));
2071
- const SettingsModal$1 = lazy(() => import("./SettingsModal-DzSC4sh8.js").then((m) => ({ default: m.SettingsModal })));
2145
+ const SettingsModal$1 = lazy(() => import("./SettingsModal-CgqnSXs5.js").then((m) => ({ default: m.SettingsModal })));
2072
2146
  const UpgradeDriveModal = lazy(() => import("./UpgradeDriveModal-_8t9slxQ.js").then((m) => ({ default: m.UpgradeDriveModal })));
2073
2147
  const modalComponents = {
2074
2148
  addDrive: AddDriveModal$1,
@@ -2083,12 +2157,12 @@ const modalComponents = {
2083
2157
  downloadDocumentWithErrors: DownloadDocumentWithErrorsModal,
2084
2158
  documentVersionUnsupported: DocumentVersionUnsupportedModal,
2085
2159
  confirmDocumentUpgrade: ConfirmDocumentUpgradeModal,
2086
- inspector: lazy(() => import("./InspectorModal-ChZE9QbX.js").then((m) => ({ default: m.InspectorModal }))),
2160
+ inspector: lazy(() => import("./InspectorModal-Z7YMEHRm.js").then((m) => ({ default: m.InspectorModal }))),
2087
2161
  settings: SettingsModal$1,
2088
2162
  upgradeDrive: UpgradeDriveModal,
2089
- missingPackage: lazy(() => import("./MissingPackageModal-C7usWVHQ.js").then((m) => ({ default: m.ConnectMissingPackageModal }))),
2090
- driveAuthRequired: lazy(() => import("./DriveAuthRequiredModal-Cw9eCAQ_.js").then((m) => ({ default: m.DriveAuthRequiredModal }))),
2091
- openFileDocuments: lazy(() => import("./OpenFileDocumentsModal-DOwweG6l.js").then((m) => ({ default: m.OpenFileDocumentsModal }))),
2163
+ missingPackage: lazy(() => import("./MissingPackageModal-nBnExqO0.js").then((m) => ({ default: m.ConnectMissingPackageModal }))),
2164
+ driveAuthRequired: lazy(() => import("./DriveAuthRequiredModal-DVK7ZQzO.js").then((m) => ({ default: m.DriveAuthRequiredModal }))),
2165
+ openFileDocuments: lazy(() => import("./OpenFileDocumentsModal-JcGCx1ta.js").then((m) => ({ default: m.OpenFileDocumentsModal }))),
2092
2166
  login: lazy(() => import("./LoginModal-CybbOm3N.js").then((m) => ({ default: m.LoginModal })))
2093
2167
  };
2094
2168
  const ModalsContainer = lazy(() => {
@@ -2130,7 +2204,7 @@ const ServiceWorkerUpdatePrompt = () => {
2130
2204
  //#region src/components/app-loader.tsx
2131
2205
  const CookieBanner$1 = lazy(() => import("./cookie-banner-BH6bIq24.js").then((m) => ({ default: m.CookieBanner })));
2132
2206
  const AppLoader = (props) => {
2133
- const Load = lazy(() => import("./load-uFhW7yeN.js").then((m) => m.loadComponent(props.localPackage)));
2207
+ const Load = lazy(() => import("./load-BJ8uRKR9.js").then((m) => m.loadComponent(props.localPackage)));
2134
2208
  return /* @__PURE__ */ jsx(StrictMode, { children: /* @__PURE__ */ jsxs(ErrorBoundary$1, {
2135
2209
  fallbackRender: (props) => /* @__PURE__ */ jsx(AppSkeleton, { children: /* @__PURE__ */ jsx(DetailedFallback, { ...props }) }),
2136
2210
  resetKeys: [props.localPackage],
@@ -2166,5 +2240,5 @@ const AppLoader = (props) => {
2166
2240
  //#endregion
2167
2241
  export { pendingFileKey as _, DocumentEditor as a, AppContainer as c, OpenPanel as d, Analytics as f, getPendingImportFiles as g, clearPendingImportFiles as h, Root as i, EditorLoader as l, runOpenFileImports as m, Sidebar as n, DriveIcon as o, AppSkeleton as p, Router as r, App as s, AppLoader as t, DocumentEditorContainer as u, subscribePendingImportFiles as v };
2168
2242
 
2169
- //# sourceMappingURL=app-loader-Dkgf816o.js.map
2170
- //# debugId=8ddd55a3-f7ce-5c8f-900a-961313c74af8
2243
+ //# sourceMappingURL=app-loader-GDy45Yza.js.map
2244
+ //# debugId=b3199364-42fc-5e8f-a94a-473c7a7b966a