@seedprotocol/react 0.7.8-next.1071.g2a141c1 → 0.7.8
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.
- package/dist/entityRows.d.ts +22 -0
- package/dist/entityRows.d.ts.map +1 -0
- package/dist/index.js +911 -872
- package/dist/index.js.map +1 -1
- package/dist/itemProperty.d.ts +2 -2
- package/dist/itemProperty.d.ts.map +1 -1
- package/dist/liveQuery.d.ts +8 -0
- package/dist/liveQuery.d.ts.map +1 -1
- package/package.json +3 -3
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["toError","ReactQueryClient","logger","logger","logger","logger","schemasTable","schemas","models","modelsTable","schemasTable","schemasTable","propertiesTable","_Fragment","_Fragment","_Fragment","_Fragment","_Fragment","_Fragment","_Fragment","_Fragment","_Fragment","React","debug","pluralize","subscribeToServiceChanges","getClient","onChange","getCurrentService","useClientService","useSyncExternalStore","useClientSelector","service","selectorRef","useRef","selector","subscribe","useCallback","subscription","unsubscribeService","getSnapshot","isClientReadySnapshot","snapshot","ClientManagerState","useIsClientReady","ADDRESSES_PERSISTED_EVENT","LOCAL_COPIES_REMOVED_EVENT","SeedAddressRevisionContext","createContext","useSeedAddressRevision","useContext","SeedAddressRevisionProvider","addressRevision","setAddressRevision","useState","onSessionInvalidation","queryClient","n","useEffect","eventEmitter","useLiveQuery","useLiveQueryState","query","queryKeyOf","sql","params","LIVE_QUERY_RETRY_DELAYS_MS","toError$1","err","state","setState","retryCount","setRetryCount","isClientReady","queryKey","queryRef","failuresRef","subscribedForRef","previous","prev","current","retryTimer","fail","delay","BaseDb","results","error","SEED_QUERY_DEFAULT_OPTIONS","getSeedQueryDefaultOptions","mergeSeedQueryDefaults","seed","userOptions","createSeedQueryClient","defaults","userDefaultOptions","restOverrides","overrides","QueryClient","fallbackQueryClient","warnedMissingProvider","isDevBuild","useSeedQueryClient","QueryClientContext","logger$4","getItemQueryKey","id","useItem","seedLocalId","seedUid","setEasHydrationTick","setPropertyValuesTick","isMachineBusy","setIsMachineBusy","machineError","setMachineError","hasSeenIdleRef","useMemo","queryKeyRef","useQuery","found","Item","modelName","item","onEasSynced","EAS_SEED_DATA_SYNCED_TO_DB_EVENT","itemService","propertySubscriptions","followProperties","propertyInstances","instance","ctx","propertyValue","renderValue","itemSubscription","getItemsQueryKey","deleted","includeEas","addressFilter","modelFileId","schemaName","useItems","useQueryClient","itemsRef","lastFetchedIdsRef","fetchesStartedRef","refetchRequestedAtRef","addressesForFilter","setAddressesForFilter","cancelled","getAddressesForItemsFilter","addrs","items","queryError","db","seedsTableData","conditions","placeholderUidSql","seeds","eq","easName","ownedSeedsSql","publisherInAddressListSql","or","isNull","versionData","getVersionData","and","gt","tableDataItemsSet","dbSeed","key","currentItemsSet","sameIds","isFetching","dataUpdatedAt","orderBy","isLoading","useCreateItem","setIsLoading","setError","inFlightRef","resetError","flushSync","promise","data","itemData","options","usePublishItem","publishingItem","setPublishingItem","subscriptionRef","publishItem","a","b","logger$3","propertiesLogger","getItemPropertyQueryKey","propertyName","useItemProperty","setVersion","obj","arg1","itemIdFromObj","propertyNameFromObj","itemId","arg2","lookupMode","resolvedSeedLocalId","resolvedSeedUid","ItemProperty","foundByUid","result","property","useHeldItemProperties","bump","initialContext","lastRenderValue","lastPropertyValue","wasIdle","lastVersionAt","trailing","now","useDebouncedItemProperty","latestValueRef","saveError","usePropertySaveError","debouncedPersist","debounce","debounceMs","flush","value","read","validationErrors","ItemPropertySaveValidationError","lastSource","update","source","held","properties","p","fetchItemPropertiesList","baseList","_itemProperties","propertiesWithMetadata","first","startCase","seedRecords","modelProperties","model","Model","modelProperty","itemProperty","createdAtPropertyName","createdAtProperty","useItemProperties","previousTableDataRef","canonicalItemKey","itemPropertiesQueryKey","rawPropertiesTableData","metadata","isNotNull","propertiesTableData","pickLatestMetadataRowPerProperty","tableDataString","useCreateItemProperty","create","props","useDestroyItemProperty","DB_READY_STATES","isDbReadyState","useDbsAreReady","logger$2","finalStrings","getServiceName","name","serviceAny","getServiceUniqueKey","getServiceValue","config","uniqueKey","context","isClientManagerService","MachineIds","getPercentComplete","percentForClientState","states","loadingStateNames","stateName","stateIndex","useService","timeElapsed","setTimeElapsed","getValue","updateTime","status","interval","startInterval","useIsDbReady","useGlobalServiceStatus","internalStatus","usePersistedSnapshots","useHasSavedSnapshots","useServices","clientService","percentComplete","toError","useCreateState","runIdRef","follow","apply","failureMessage","entity","start","run","runAsync","runId","logger$1","useSchema","schema","setSchema","loadSchema","schemaExists","identifier","load","schemaInstance","Schema","schemaIdentifier","SEED_PROTOCOL_SCHEMA_NAME","schemas","SEED_SCHEMAS_QUERY_KEY","useSchemas","previousSchemasTableDataRef","schemasRef","schemas$1","schemasTableData","desc","ch","onMessage","prevData","currentSchemasSet","schemaFileId","version","tableDataSchemasSet","dbSchema","setsAreEqual","tableHasNewRows","tableLostRows","useCreateSchema","useDestroySchema","useAllSchemaVersions","schemaInstances","setSchemaInstances","schemaInstancesRef","fetchSchemas","allSchemasData","loadAllSchemasFromDb","schemaNames","schemaData","currentInstances","existingInstance","useSeedProtocolSchema","getModelsQueryKey","schemaId","useModels","tableIdsRef","models$1","next","tableIds","displayModels","modelsRef","event","stableModelsQueryKeyRef","stableModelsQueryRef","buildModelsQuery","currentDb","models","modelSchemas","modelsTableData","prevKey","q","currentModelsSet","tableDataModelsSet","dbModel","useModel","setModel","isModelIdLookup","schemaIdOrModelId","foundModel","modelsList","modelsLoading","modelsError","m","activeModel","useCreateModel","useDestroyModel","resolveSchemaName","rows","getSchemaNameFromId","useModelProperties","dbModelId","useModelDbId","modelId","modelPropertiesQueryKey","ModelProperty","modelPropertiesRef","timers","ms","t","currentPropertiesSet","prop","propertyFileId","tableDataPropertiesSet","dbProperty","LOOKUP_RETRY_DELAYS_MS","readModelDbId","setDbModelId","getPropertySchemaByModelFileId","getPropertySchema","useModelProperty","args","arg3","argCount","initialLoadingState","setModelProperty","shouldLoad","updateModelProperty","propertyData","resolvedModelName","quiet","foundProperty","createdProperty","resolvedProperty","skipLookupRetryRef","shouldSubscribe","debouncedUpdate","useCreateModelProperty","useDestroyModelProperty","useDeleteItem","IMAGE_FILES_QUERY_KEY","useImageFiles","imageFiles","BaseFileManager","fileSavedHandler","filePath","downloadSuccessHandler","filesChangedHandler","filePaths","FILES_CHANGED_EVENT","FILES_QUERY_KEY_PREFIX","useFiles","dir","files","useAppDbWhenReady","useEasSyncProcessesQuery","easSyncProcesses","useEasSyncProcessesState","records","r","useEasSyncProcesses","useEasSyncProcessesNonActiveCount","useEasSyncProcessById","invalidateItemPropertiesRef","invalidateItemPropertiesForItem","p1","canonicalId","SeedProviderEventSubscriber","invalidate","handler","payload","onEasSyncedToDb","SeedClientInit","onInitErrorRef","onInitError","addressesRef","addresses","addressesKey","SeedProvider","queryClientProp","queryClientRef","w","QueryClientProvider","children","DefaultLoading","valueKey","SeedClientGate","initConfigRef","initConfig","schemaRef","initConfigKey","schemaKey","effectiveConfig","loadingContent","loadingComponent","wrapperStyle","wrapperClassName","overlayStyle","loadingClassName","splitPath","parts","path","resolveDirectoryHandle","handle","root","part","resolveParent","resolveFileHandle","getOPFSFile","deleteOPFSEntry","pruneEmptyDirectory","parent","child","empty","childName","removeEmptyOPFSDirectories","isOPFSSupported","SIGNATURES","ascii","bytes","end","sniffMimeType","s","detectMimeType","file","scanDirectory","foundFiles","dirHandle","currentPath","basePath","detectedType","subFiles","useOPFSFiles","setFiles","hasLoaded","setHasLoaded","errorName","setErrorName","allFiles","rootPath","SIZE_UNITS","formatFileSize","i","MINUTE","HOUR","DAY","formatRelativeTime","diff","timestamp","days","formatDateTime","truncateMiddle","max","head","tail","ICONS","Fragment","Icon","size","MAX_LISTED","ConfirmDialog","ref","dialog","className","onCancel","onSubmit","DeleteDialog","includeVariants","setIncludeVariants","variants","entries","variantsSize","v","count","prefix","action","deleteActionFor","heading","onConfirm","warning","FolderDeleteDialog","downloaded","setDownloaded","f","folder","fileCount","copies","busy","onDownload","hiddenCount","effectiveType","DATABASE_NAME","fileKind","type","formatLabel","ext","VARIANT_PATH","ORIGINAL_PATH","stripExtension","parseImageVariant","originalKey","toEntry","buildEntries","groupVariants","originals","variant","entry","original","joinPath","listDirectory","cwd","inside","all","sortEntries","folders","rest","slash","compare","sort","sizeByKind","totals","defaultDeleteWarning","THUMBNAIL_WIDTH","RESIZE_THRESHOLD_BYTES","MAX_CONCURRENT_DECODES","RELEASE_DELAY_MS","activeJobs","queue","schedule","job","resolve","reject","cache","acquire","created","blob","released","resizeImage","bitmap","canvas","createThumbnail","visibilityCallbacks","observer","observeOnce","onVisible","record","el","useThumbnail","isImage","element","setElement","visible","setVisible","url","setUrl","setStatus","lastModified","u","markError","KIND_META","kindStyle","kind","mayBeTransparent","FileThumbnail","failed","showImage","content","FileGrid","isSelected","selected","tileClassName","onActivate","index","e","onToggle","FileList","selectAllRef","selectedHere","allSelected","onToggleAll","deleteAction","rowClassName","onDelete","MAX_TEXT_PREVIEW_FILE","TEXT_PREVIEW_BYTES","usePreview","preview","setPreview","truncated","text","PreviewPanel","dimensions","setDimensions","imageFailed","setImageFailed","closeRef","headingId","previewContent","message","checker","onPrevious","onNext","onClose","onCopyPath","STYLE_ELEMENT_ID","STYLE_LAYER","LIGHT_TOKENS","DARK_TOKENS","OPFS_FILES_MANAGER_CSS","ensureStylesInjected","useStorageEstimate","estimate","setEstimate","usage","quota","revision","crcTable","getCrcTable","c","k","crc32","table","crc","dosDateTime","d","year","UTF8_FLAG","MAX_UINT32","createZip","inputs","encoder","central","offset","input","local","time","date","header","centralSize","DEFAULT_DESCRIPTION","TOAST_MS","errorMessage","plural","one","many","normalizeDir","triggerBrowserDownload","useToasts","toasts","setToasts","pending","timer","x","METER_KINDS","UsageMeter","byKind","filesTotal","otherSiteData","total","segments","StateMessage","tone","icon","title","OPFSFilesManager","theme","rootDir","rootLabel","cn","base","classNames","slot","rawFiles","filter","grouped","setGrouped","groupImageVariants","setCwd","setQuery","setSort","viewChoice","setViewChoice","defaultView","setSelected","anchorRef","panelPath","setPanelPath","panelOpenerRef","pendingDelete","setPendingDelete","pendingFolder","setPendingFolder","setBusy","notify","onNotify","notice","showToast","listing","imageShare","view","entriesByPath","selectedEntries","panelEntry","panelIndex","navigate","toggle","anchor","range","from","to","openPanel","closePanel","activate","modified","toggleAllVisible","relativeToRoot","saveBlob","downloadFiles","list","folderParent","zip","download","filesToDelete","resolveDeleteAction","targets","performDelete","skipped","folderRemoved","onBeforeDelete","refetch","reasons","onAfterDelete","requestDelete","confirmDelete","filesInFolder","requestFolderDelete","copyPath","done","onKeyDown","inField","hasVariants","inImagesFolder","crumbs","Heading","headingLevel","selectedSize","body","retry","pendingFiles","pendingFolderFiles","batchDeleteAction","description","deleteWarning","readStorageTransactionId","useEnsureLocalImage","setResult","refResolvedValue","imageProperty","rawValue","stringValue","storageTransactionId","transactionId","normalizeArweaveTxIdForEnsure","filename","fileName","gatewayHrefs","hrefs","getArweaveReadBaseUrls","href","configured","BaseArweaveClient","gatewayIndex","setGatewayIndex","gatewayHref","onGatewayError","enabled","ensureImageLocal","width","logger","contentUrlCache","getFileNameWithoutExtension","match","escapeRegExp","string","matchFileNameWithoutExtension","regex","targetName","nameWithoutExt","SeedImage","filenameOverride","sizedContentUrl","setSizedContentUrl","originalContentUrl","setOriginalContentUrl","propertyFromHook","resolvedFilename","ensureStatus","ensure","srcUrl","isFileOrBlob","blobPreviewUrl","setBlobPreviewUrl","blobPreviewRef","fs","baseDir","availableWidths","closestWidth","curr","filenameWithoutExtension","cacheKey","contentUrl","matchingFile","newPath","isBlobUrl","hasContentUrl","imgSrc","onErrorProp","imgProps","handleError","useResolvedMediaRef","tick","setTick","treatAs","blobUrl","resolveMediaRef","createdBlobUrl","isEmpty","SeedMediaImage","merged","alt","render","defaultLabelFromHref","isHttpOrHttps","SeedMediaFile","anchorProps","external","resolvedTarget","target","rel","SeedMediaAudio","audioProps","SeedMediaVideo","videoProps","hasRenderableHtml","html","SeedHtml","divProps","safe","sanitize","truncateString","looksLikeJsonObjectOrArray","limitDepth","maxStringLength","seen","depth","maxDepth","out","formatSeedJson","space","toSerialize","limited","SeedJson","preProps","format","formatOptions","useNormalizedFeedItemFields","normalizeFeedItemFields","manifest"],"sources":["../src/client.ts","../src/addressesPersistedEventName.ts","../src/localCopiesRemovedEventName.ts","../src/SeedSessionContext.tsx","../src/liveQuery.ts","../src/queryClient.ts","../src/item.ts","../src/itemProperty.ts","../src/db.ts","../src/services.ts","../src/useCreateState.ts","../src/schema.ts","../src/model.ts","../src/modelProperty.ts","../src/trash.ts","../src/useImageFiles.ts","../src/useFiles.ts","../src/useEasSyncProcesses.ts","../src/SeedProvider.tsx","../src/SeedClientGate.tsx","../src/opfsPaths.ts","../src/useOPFSFiles.ts","../src/OPFSFilesManager/format.ts","../src/OPFSFilesManager/icons.tsx","../src/OPFSFilesManager/DeleteDialog.tsx","../src/OPFSFilesManager/fileModel.ts","../src/OPFSFilesManager/useThumbnail.ts","../src/OPFSFilesManager/FileThumbnail.tsx","../src/OPFSFilesManager/FileGrid.tsx","../src/OPFSFilesManager/FileList.tsx","../src/OPFSFilesManager/PreviewPanel.tsx","../src/OPFSFilesManager/styles.ts","../src/OPFSFilesManager/useStorageEstimate.ts","../src/OPFSFilesManager/zip.ts","../src/OPFSFilesManager/OPFSFilesManager.tsx","../src/useEnsureLocalImage.ts","../src/SeedImage.tsx","../src/useResolvedMediaRef.ts","../src/SeedMediaImage.tsx","../src/SeedMediaFile.tsx","../src/SeedMediaAudio.tsx","../src/SeedMediaVideo.tsx","../src/SeedHtml.tsx","../src/formatSeedJson.ts","../src/SeedJson.tsx","../src/useNormalizedFeedItemFields.ts"],"sourcesContent":["import { useCallback, useRef, useSyncExternalStore } from 'react'\nimport { ClientManagerState, getClient } from '@seedprotocol/sdk'\n\ntype ClientService = ReturnType<ReturnType<typeof getClient>['getService']>\ntype ClientSnapshot = ReturnType<ClientService['getSnapshot']>\n\nconst subscribeToServiceChanges = (onChange: () => void) => getClient().subscribeService(onChange)\nconst getCurrentService = () => getClient().getService()\n\n/**\n * The current clientManager actor. Re-renders when `client.init()` after `client.stop()`\n * replaces it, so hooks built on it follow the new client instead of the stopped one.\n */\nexport const useClientService = (): ClientService =>\n useSyncExternalStore(subscribeToServiceChanges, getCurrentService, getCurrentService)\n\n/**\n * A value derived from the current clientManager snapshot. Follows actor swaps and `stop()`.\n * The selector must return a primitive or a stable reference: a new object on every call\n * re-renders forever.\n */\nexport function useClientSelector<T>(selector: (snapshot: ClientSnapshot) => T): T {\n const service = useClientService()\n const selectorRef = useRef(selector)\n selectorRef.current = selector\n\n const subscribe = useCallback(\n (onChange: () => void) => {\n const subscription = service.subscribe({ next: onChange, error: onChange, complete: onChange })\n // stop() may not emit a snapshot to subscribers; subscribeService reports it.\n const unsubscribeService = getClient().subscribeService(onChange)\n return () => {\n subscription.unsubscribe()\n unsubscribeService()\n }\n },\n [service],\n )\n const getSnapshot = () => selectorRef.current(service.getSnapshot())\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot)\n}\n\n/** Whether a clientManager snapshot is a running client that finished init. */\nexport const isClientReadySnapshot = (snapshot: ClientSnapshot): boolean =>\n snapshot.status === 'active' && snapshot.value === ClientManagerState.IDLE\n\n/**\n * Whether `client.init` has finished and the client is ready (idle). False after\n * `client.stop()` until the next init finishes. SeedClientGate uses it to hold back its\n * children; call it to wait for readiness in your own components.\n */\nexport const useIsClientReady = () => useClientSelector(isClientReadySnapshot)\n","/**\n * Must match `ADDRESSES_PERSISTED_EVENT` in `@seedprotocol/sdk` / `client/events.ts`\n * (emitted after `setAddresses` persists to `app_state`).\n */\nexport const ADDRESSES_PERSISTED_EVENT = 'addresses.persisted' as const\n","/**\n * Must match `LOCAL_COPIES_REMOVED_EVENT` in `@seedprotocol/sdk` / `client/events.ts`\n */\nexport const LOCAL_COPIES_REMOVED_EVENT = 'localCopies.removed' as const\n","import React, {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useState,\n type ReactNode,\n} from 'react'\nimport type { QueryClient } from '@tanstack/react-query'\nimport { eventEmitter } from '@seedprotocol/sdk'\nimport { ADDRESSES_PERSISTED_EVENT } from './addressesPersistedEventName'\nimport { LOCAL_COPIES_REMOVED_EVENT } from './localCopiesRemovedEventName'\n\nconst SeedAddressRevisionContext = createContext(0)\n\n/**\n * Bumps when persisted wallet/session addresses change (`setAddresses` completed).\n * Used by `useItems` / `useItem` so list and detail hooks refresh after connect/disconnect.\n * Outside {@link SeedAddressRevisionProvider} (e.g. without `SeedProvider`), this stays `0`.\n */\nexport function useSeedAddressRevision(): number {\n return useContext(SeedAddressRevisionContext)\n}\n\nexport function SeedAddressRevisionProvider({\n queryClient,\n children,\n}: {\n queryClient: QueryClient\n children: ReactNode\n}) {\n const [addressRevision, setAddressRevision] = useState(0)\n\n const onSessionInvalidation = useCallback(() => {\n void queryClient.invalidateQueries({ queryKey: ['seed', 'items'], exact: false })\n void queryClient.invalidateQueries({ queryKey: ['seed', 'item'], exact: false })\n void queryClient.invalidateQueries({ queryKey: ['seed', 'itemProperty'], exact: false })\n setAddressRevision((n) => n + 1)\n }, [queryClient])\n\n useEffect(() => {\n eventEmitter.on(ADDRESSES_PERSISTED_EVENT, onSessionInvalidation)\n eventEmitter.on(LOCAL_COPIES_REMOVED_EVENT, onSessionInvalidation)\n return () => {\n eventEmitter.off(ADDRESSES_PERSISTED_EVENT, onSessionInvalidation)\n eventEmitter.off(LOCAL_COPIES_REMOVED_EVENT, onSessionInvalidation)\n }\n }, [onSessionInvalidation])\n\n return (\n <SeedAddressRevisionContext.Provider value={addressRevision}>\n {children}\n </SeedAddressRevisionContext.Provider>\n )\n}\n","import { useEffect, useState, useRef } from 'react'\nimport { BaseDb } from '@seedprotocol/sdk'\nimport { useIsClientReady } from './client'\n\n/**\n * Hook to execute a reactive query that emits new results whenever the underlying data changes.\n * \n * Supports two usage patterns:\n * 1. SQL tag function: useLiveQuery((sql) => sql`SELECT * FROM models`)\n * 2. Drizzle query builder: useLiveQuery(db.select().from(models))\n * \n * @param query - SQL query function or Drizzle query builder, or null/undefined to disable the query\n * @returns Array of query results, or undefined if not yet loaded\n * \n * @example\n * ```typescript\n * // Using SQL tag function\n * const models = useLiveQuery<ModelRow>(\n * (sql) => sql`SELECT * FROM models WHERE schema_file_id = ${schemaId}`\n * )\n * \n * // Using Drizzle query builder\n * import { models } from '@seedprotocol/sdk'\n * import { eq } from 'drizzle-orm'\n * \n * const appDb = BaseDb.getAppDb()\n * const models = useLiveQuery<ModelRow>(\n * appDb.select().from(models).where(eq(models.schemaFileId, schemaId))\n * )\n * ```\n */\nexport function useLiveQuery<T>(\n query: ((sql: any) => any) | any | null | undefined\n): T[] | undefined {\n return useLiveQueryState<T>(query).data\n}\n\n/**\n * Identity of a query for subscription purposes. A Drizzle builder is identified by its compiled\n * SQL and parameters, so an inline (unmemoised) builder doesn't resubscribe on every render; that\n * re-render/resubscribe cycle looped. A SQL tag function can only be identified by reference:\n * memoise it (useCallback/useMemo).\n */\nfunction queryKeyOf(query: unknown): unknown {\n if (query && typeof query === 'object' && typeof (query as { toSQL?: unknown }).toSQL === 'function') {\n try {\n const { sql, params } = (query as { toSQL: () => { sql: string; params: unknown[] } }).toSQL()\n return `sql:${sql}|${JSON.stringify(params)}`\n } catch {\n return query\n }\n }\n return query\n}\n\n/** Delays before resubscribing after a live query fails, in ms; the last one repeats. */\nexport const LIVE_QUERY_RETRY_DELAYS_MS = [500, 1000, 2000, 5000, 10000, 30000]\n\nconst toError = (err: unknown): Error => (err instanceof Error ? err : new Error(String(err)))\n\n/**\n * useLiveQuery with its error: `{ data, error }`. `data` is undefined until the first result and\n * resets when the query changes or the client stops; `error` is the last error from creating or\n * running the live query, cleared by the next result. After an error the hook keeps the last rows\n * and resubscribes with backoff (LIVE_QUERY_RETRY_DELAYS_MS) until a result arrives.\n */\nexport function useLiveQueryState<T>(\n query: ((sql: any) => any) | any | null | undefined\n): { data: T[] | undefined; error: Error | null } {\n const [state, setState] = useState<{ data: T[] | undefined; error: Error | null }>({\n data: undefined,\n error: null,\n })\n // Bumped to resubscribe after an error.\n const [retryCount, setRetryCount] = useState(0)\n const isClientReady = useIsClientReady()\n\n const queryKey = queryKeyOf(query)\n const queryRef = useRef(query)\n queryRef.current = query\n const failuresRef = useRef(0)\n const subscribedForRef = useRef<{ queryKey: unknown; isClientReady: boolean } | null>(null)\n\n useEffect(() => {\n const previous = subscribedForRef.current\n subscribedForRef.current = { queryKey, isClientReady }\n if (!previous || previous.queryKey !== queryKey || previous.isClientReady !== isClientReady) {\n // A new (or no) query, or a client that started or stopped: drop the previous rows\n // (no re-render if already empty). A retry keeps them.\n failuresRef.current = 0\n setState((prev) => (prev.data === undefined && prev.error === null ? prev : { data: undefined, error: null }))\n }\n const current = queryRef.current\n if (!isClientReady || !current) {\n return\n }\n\n let retryTimer: ReturnType<typeof setTimeout> | undefined\n const fail = (err: unknown) => {\n // Keep the last rows (no flicker on transient errors) and report the error\n setState((prev) => ({ data: prev.data, error: toError(err) }))\n const delay =\n LIVE_QUERY_RETRY_DELAYS_MS[Math.min(failuresRef.current, LIVE_QUERY_RETRY_DELAYS_MS.length - 1)]\n failuresRef.current += 1\n retryTimer = setTimeout(() => setRetryCount((n) => n + 1), delay)\n }\n\n let subscription: { unsubscribe: () => void } | undefined\n try {\n subscription = BaseDb.liveQuery<T>(current).subscribe({\n next: (results) => {\n failuresRef.current = 0\n // New array reference so React re-renders when SQLocal mutates row objects in place\n // on the same backing array (Object.is would otherwise bail out of useState).\n setState({ data: results !== undefined ? [...results] : undefined, error: null })\n },\n error: fail,\n })\n } catch (error) {\n fail(error)\n }\n\n return () => {\n subscription?.unsubscribe()\n if (retryTimer !== undefined) clearTimeout(retryTimer)\n }\n }, [queryKey, isClientReady, retryCount])\n\n return state\n}\n","import { useContext } from 'react'\nimport {\n type QueryClient,\n type DefaultOptions,\n QueryClient as ReactQueryClient,\n QueryClientContext,\n type QueryClientConfig,\n NetworkMode,\n} from '@tanstack/react-query'\n\nconst SEED_QUERY_DEFAULT_OPTIONS: DefaultOptions = {\n queries: {\n networkMode: 'offlineFirst' as NetworkMode,\n gcTime: 1000 * 60 * 60 * 24, // 24 hours\n staleTime: 1000 * 60, // 1 minute - list data can be slightly stale\n },\n}\n\n/**\n * Returns the default options used by Seed for list-query caching.\n * Use this when building your own QueryClient so Seed hooks get consistent behavior.\n */\nexport function getSeedQueryDefaultOptions(): DefaultOptions {\n return { ...SEED_QUERY_DEFAULT_OPTIONS }\n}\n\n/**\n * Merges Seed's default query options with your existing default options.\n * Your options take precedence over Seed's. Use when constructing your own QueryClient:\n *\n * @example\n * ```ts\n * const client = new QueryClient({\n * defaultOptions: mergeSeedQueryDefaults({\n * queries: { gcTime: 1000 * 60 * 60 },\n * }),\n * })\n * ```\n */\nexport function mergeSeedQueryDefaults(\n userOptions?: Partial<DefaultOptions> | null\n): DefaultOptions {\n const seed = getSeedQueryDefaultOptions()\n if (!userOptions) return seed\n return {\n queries: {\n ...seed.queries,\n ...(userOptions.queries ?? {}),\n },\n mutations: {\n ...(seed.mutations ?? {}),\n ...(userOptions.mutations ?? {}),\n },\n }\n}\n\n/**\n * Creates a QueryClient configured with Seed's default options.\n * Use this when you want to provide your own QueryClientProvider but still use Seed's defaults.\n *\n * @param overrides - Optional config to merge with Seed defaults (e.g. defaultOptions, logger).\n */\nexport function createSeedQueryClient(overrides?: Partial<QueryClientConfig>): QueryClient {\n const defaults = getSeedQueryDefaultOptions()\n const { defaultOptions: userDefaultOptions, ...restOverrides } = overrides ?? {}\n return new ReactQueryClient({\n ...restOverrides,\n defaultOptions: userDefaultOptions\n ? mergeSeedQueryDefaults(userDefaultOptions as DefaultOptions)\n : defaults,\n })\n}\n\nlet fallbackQueryClient: QueryClient | undefined\nlet warnedMissingProvider = false\n\n/** False in production builds; bundlers compile `process.env.NODE_ENV` in. */\nconst isDevBuild = (): boolean => {\n try {\n return process.env.NODE_ENV !== 'production'\n } catch {\n return false // no `process` and no bundler replacement\n }\n}\n\n/**\n * The QueryClient from the nearest QueryClientProvider (SeedProvider sets one), or else one shared\n * client with Seed defaults, used by every Seed hook outside a provider. Development builds warn\n * once when the fallback is used. From 0.8 these hooks will throw without SeedProvider.\n */\nexport function useSeedQueryClient(): QueryClient {\n const fromProvider = useContext(QueryClientContext)\n if (fromProvider) return fromProvider\n if (!warnedMissingProvider && isDevBuild()) {\n warnedMissingProvider = true\n console.warn(\n '[@seedprotocol/react] A Seed hook ran outside SeedProvider (no QueryClientProvider found), so it ' +\n 'uses a shared fallback QueryClient. Wrap the app in <SeedProvider>: from 0.8 these hooks ' +\n 'will throw without it.',\n )\n }\n fallbackQueryClient ??= createSeedQueryClient()\n return fallbackQueryClient\n}\n\n/** @internal For tests: the fallback client `useSeedQueryClient` returns outside a provider. */\nexport function getFallbackSeedQueryClient(): QueryClient | undefined {\n return fallbackQueryClient\n}\n","import { useCallback, useEffect, useRef, useState, useMemo } from 'react'\nimport { flushSync } from 'react-dom'\nimport {\n getAddressesForItemsFilter,\n ownedSeedsSql,\n placeholderUidSql,\n publisherInAddressListSql,\n Item,\n eventEmitter,\n EAS_SEED_DATA_SYNCED_TO_DB_EVENT,\n} from '@seedprotocol/sdk'\nimport { orderBy } from 'lodash-es'\nimport debug from 'debug'\nimport type { ModelValues } from '@seedprotocol/sdk'\nimport { Subscription } from 'xstate'\nimport type { IItem, IItemProperty, ItemCreateOptions, ItemPublishOptions } from '@seedprotocol/sdk'\nimport { useIsClientReady } from './client'\nimport { useSeedAddressRevision } from './SeedSessionContext'\nimport { useLiveQuery } from './liveQuery'\nimport { useSeedQueryClient } from './queryClient'\nimport { BaseDb } from '@seedprotocol/sdk'\nimport { seeds } from '@seedprotocol/sdk'\nimport { and, eq, gt, isNull, or } from 'drizzle-orm'\nimport { easName } from '@seedprotocol/sdk'\nimport type { SeedType } from '@seedprotocol/sdk'\nimport { getVersionData } from '@seedprotocol/sdk'\nimport { useQuery, useQueryClient } from '@tanstack/react-query'\n\nconst logger = debug('seedSdk:react:item')\n\ntype UseItemReturn<T extends ModelValues<T>> = {\n item: IItem<T> | undefined\n isLoading: boolean\n error: Error | null\n}\n\ntype UseItemProps = {\n modelName: string\n seedLocalId?: string\n seedUid?: string\n}\n\ntype UseItem = <T extends ModelValues<T>>(props: UseItemProps) => UseItemReturn<T>\n\n/** Query key for one item: `useItem`'s query. Invalidated alongside `['seed', 'items']`. */\nexport const getItemQueryKey = (id: string | undefined) => ['seed', 'item', id ?? null] as const\n\n/**\n * Loads one Item by seedLocalId or seedUid.\n *\n * - An Item already in the instance cache and ready (`Item.peekReady`) comes back on the first\n * render with `isLoading: false`, then `Item.find()` refreshes it in the background.\n * - Changing the id never returns the previous id's item.\n * - The item updates in place (its state machine), so the hook also re-renders on its changes.\n */\nexport const useItem: UseItem = <T extends ModelValues<T>>({ modelName, seedLocalId, seedUid }: UseItemProps) => {\n const id = seedLocalId || seedUid\n /** Bumped when EAS sync updates SQLite so cached `Item` instances re-render after in-place hydration. */\n const [, setEasHydrationTick] = useState(0)\n /** Bumped when one of the item's property values changes. */\n const [, setPropertyValuesTick] = useState(0)\n const [isMachineBusy, setIsMachineBusy] = useState(false)\n const [machineError, setMachineError] = useState<Error | null>(null)\n const hasSeenIdleRef = useRef(false)\n\n const isClientReady = useIsClientReady()\n const queryClient = useSeedQueryClient()\n const queryKey = useMemo(() => getItemQueryKey(id), [id])\n const queryKeyRef = useRef(queryKey)\n queryKeyRef.current = queryKey\n\n const query = useQuery(\n {\n queryKey,\n queryFn: async (): Promise<Item<T> | null> => {\n // A ready cached item is what find() would return, minus the cache hold find() takes.\n const found = (Item.peekReady(id) ??\n (await Item.find({ modelName, seedLocalId, seedUid }))) as Item<T> | undefined\n if (found) return found\n logger('[useItem] no item found', modelName, id)\n // find() also returns undefined when the item doesn't settle in time; keep what we have,\n // unless it was unloaded (e.g. a local purge stops the instance and deletes its rows).\n const previous = queryClient.getQueryData<Item<T> | null>(queryKey)\n return previous && previous.getService().getSnapshot().status === 'active' ? previous : null\n },\n // The Item instance cache is the cache. A ready cached item is the first render's data and\n // staleTime 0 refreshes it in the background; gcTime 0 drops the query once unused, so it\n // never hands out an instance that was unloaded since.\n initialData: () => Item.peekReady(id) as Item<T> | undefined,\n staleTime: 0,\n gcTime: 0,\n // An Item is a live actor-backed object, not data to compare field by field.\n structuralSharing: false,\n retry: false,\n enabled: isClientReady && !!id,\n },\n queryClient,\n )\n const item = query.data ?? undefined\n\n useEffect(() => {\n const onEasSynced = () => {\n setEasHydrationTick((n) => n + 1)\n void queryClient.invalidateQueries({ queryKey: queryKeyRef.current })\n }\n eventEmitter.on(EAS_SEED_DATA_SYNCED_TO_DB_EVENT, onEasSynced)\n return () => {\n eventEmitter.off(EAS_SEED_DATA_SYNCED_TO_DB_EVENT, onEasSynced)\n }\n }, [queryClient])\n\n // Follow the item's machine: busy after a real transition out of idle (reload), or error.\n useEffect(() => {\n hasSeenIdleRef.current = false\n setIsMachineBusy(false)\n setMachineError(null)\n if (!item) return\n\n const subscription: Subscription = item.getService().subscribe((snapshot: any) => {\n if (!snapshot || typeof snapshot !== 'object' || !('value' in snapshot)) return\n if (snapshot.value === 'idle') {\n hasSeenIdleRef.current = true\n setIsMachineBusy(false)\n setMachineError(null)\n } else if (snapshot.value === 'error') {\n setMachineError(new Error('Item service error'))\n setIsMachineBusy(false)\n } else if (hasSeenIdleRef.current) {\n // Only after idle, so the transitions find() already waited out don't count as loading.\n setIsMachineBusy(true)\n }\n })\n return () => subscription.unsubscribe()\n }, [item])\n\n // Re-render when a property's value changes: the item reads them from its property instances,\n // which update without the item's own machine changing. The item adds instances as they load\n // (same Map, mutated), so recheck the set on each item snapshot.\n useEffect(() => {\n if (!item) return\n const itemService = item.getService()\n const propertySubscriptions = new Map<unknown, Subscription>()\n\n const followProperties = (propertyInstances: Map<PropertyKey, IItemProperty<any>> | undefined) => {\n const current = new Set(propertyInstances?.values() ?? [])\n for (const [instance, subscription] of propertySubscriptions) {\n if (!current.has(instance as IItemProperty<any>)) {\n subscription.unsubscribe()\n propertySubscriptions.delete(instance)\n }\n }\n for (const instance of current) {\n if (propertySubscriptions.has(instance)) continue\n const service = instance.getService()\n let { propertyValue, renderValue } = (service.getSnapshot() as any).context ?? {}\n propertySubscriptions.set(\n instance,\n service.subscribe((snapshot: any) => {\n const ctx = snapshot?.context\n if (!ctx) return\n if (Object.is(ctx.propertyValue, propertyValue) && Object.is(ctx.renderValue, renderValue)) return\n propertyValue = ctx.propertyValue\n renderValue = ctx.renderValue\n setPropertyValuesTick((n) => n + 1)\n }),\n )\n }\n }\n\n followProperties(itemService.getSnapshot().context.propertyInstances)\n const itemSubscription: Subscription = itemService.subscribe((snapshot: any) => {\n followProperties(snapshot?.context?.propertyInstances)\n })\n return () => {\n itemSubscription.unsubscribe()\n for (const subscription of propertySubscriptions.values()) subscription.unsubscribe()\n }\n }, [item])\n\n return {\n item,\n isLoading: (!!id && query.isPending) || isMachineBusy,\n error: machineError ?? (query.error as Error | null),\n }\n}\n\ntype UseItemsReturn = {\n items: IItem<any>[]\n isLoading: boolean\n error: Error | null\n}\n\ntype UseItemsProps = {\n modelName?: string\n /** Only items of one model, when several schemas define `modelName`: the schema that defines it ... */\n schemaName?: string\n /** ... or its Model.id. */\n modelFileId?: string\n deleted?: boolean\n includeEas?: boolean\n addressFilter?: 'owned' | 'watched' | 'all'\n}\n\ntype UseItems = (props: UseItemsProps) => UseItemsReturn\n\nconst getItemsQueryKey = (\n modelName?: string,\n deleted?: boolean,\n includeEas?: boolean,\n addressFilter?: 'owned' | 'watched' | 'all',\n addressRevision?: number,\n modelFileId?: string,\n schemaName?: string,\n) =>\n [\n 'seed',\n 'items',\n modelName ?? null,\n deleted ?? false,\n includeEas ?? false,\n addressFilter ?? null,\n addressRevision ?? 0,\n // Only scoped lists get the extra segment, so unscoped keys keep their existing shape.\n ...(modelFileId || schemaName ? [{ modelFileId: modelFileId ?? null, schemaName: schemaName ?? null }] : []),\n ] as const\n\n/**\n * Lists items for an optional model (and filters) with TanStack Query plus a SQLite live query.\n *\n * - This hook uses `staleTime: 0` on its query so the list does not inherit Seed’s default\n * long freshness window; local data is kept in sync via live-query invalidation, and remounts\n * can refetch when the cache is stale.\n * - Schema-backed fields on each `IItem` (e.g. list relation properties) read from the item\n * machine and may be `undefined` until that property is loaded; normalize for forms\n * (e.g. `Array.isArray(x) ? x : []`).\n */\nexport const useItems: UseItems = ({\n modelName,\n modelFileId,\n schemaName,\n deleted = false,\n includeEas = false,\n addressFilter,\n}) => {\n const isClientReady = useIsClientReady()\n const addressRevision = useSeedAddressRevision()\n const queryClient = useQueryClient()\n\n useEffect(() => {\n const onEasSynced = () => {\n queryClient.invalidateQueries({ queryKey: ['seed', 'items'] })\n }\n eventEmitter.on(EAS_SEED_DATA_SYNCED_TO_DB_EVENT, onEasSynced)\n return () => {\n eventEmitter.off(EAS_SEED_DATA_SYNCED_TO_DB_EVENT, onEasSynced)\n }\n }, [queryClient])\n\n const itemsRef = useRef<IItem<any>[]>([])\n // The live seed ids a refetch was last requested for.\n const lastFetchedIdsRef = useRef<Set<string>>(new Set())\n // Fetches started, and how many had started when the last refetch was requested. Only a fetch\n // started after the request can answer it: an invalidation while a fetch with no data yet is\n // running joins that fetch instead of starting one.\n const fetchesStartedRef = useRef(0)\n const refetchRequestedAtRef = useRef(0)\n const [addressesForFilter, setAddressesForFilter] = useState<string[] | null>(null)\n\n useEffect(() => {\n if (addressFilter !== 'owned' && addressFilter !== 'watched') {\n setAddressesForFilter(null)\n return\n }\n let cancelled = false\n getAddressesForItemsFilter(addressFilter).then((addrs) => {\n if (!cancelled) setAddressesForFilter(addrs)\n })\n return () => {\n cancelled = true\n }\n }, [addressFilter, addressRevision])\n\n const queryKey = useMemo(\n () => getItemsQueryKey(modelName, deleted, includeEas, addressFilter, addressRevision, modelFileId, schemaName),\n [modelName, deleted, includeEas, addressFilter, addressRevision, modelFileId, schemaName],\n )\n\n useEffect(() => {\n lastFetchedIdsRef.current = new Set()\n }, [queryKey])\n\n const {\n data: items = [],\n isLoading,\n isFetching,\n dataUpdatedAt,\n error: queryError,\n } = useQuery({\n queryKey,\n queryFn: async ({ signal }) => {\n fetchesStartedRef.current++\n const rows = await Item.all(modelName, deleted, {\n waitForReady: true,\n includeEas,\n addressFilter,\n modelFileId,\n schemaName,\n // A superseded fetch (new key, cancelled refetch, unmount) stops building items.\n signal,\n })\n return rows\n },\n enabled: isClientReady,\n // Local SQLite + live invalidation drive freshness; Seed’s default staleTime would keep a\n // mistaken initial [] “fresh” and block refetch when another subscriber mounts.\n staleTime: 0,\n })\n itemsRef.current = items\n\n // Watch the seeds table for changes\n const db = isClientReady ? BaseDb.getAppDb() : null\n const seedsQuery = useMemo(() => {\n if (!db) return null\n if (addressFilter === 'owned' || addressFilter === 'watched') {\n if (addressesForFilter === null) return null\n }\n const conditions: any[] = []\n // Same predicates as getItemsData, so the live query and the fetched list agree.\n if (!includeEas) {\n conditions.push(placeholderUidSql(seeds.uid) as any)\n }\n if (modelName) {\n conditions.push(eq(seeds.type, easName(modelName)))\n }\n if (addressFilter === 'owned') {\n conditions.push(ownedSeedsSql(addressesForFilter ?? []) as any)\n } else if (addressFilter === 'watched') {\n conditions.push(\n publisherInAddressListSql(seeds.publisher, addressesForFilter ?? []) as any,\n )\n }\n if (deleted) {\n conditions.push(eq(seeds._markedForDeletion, 1) as any)\n } else {\n conditions.push(\n or(\n isNull(seeds._markedForDeletion),\n eq(seeds._markedForDeletion, 0)\n ) as any\n )\n conditions.push(\n or(isNull(seeds.revokedAt), eq(seeds.revokedAt, 0)) as any\n )\n }\n const versionData = getVersionData()\n return db\n .with(versionData)\n .select({\n localId: seeds.localId,\n uid: seeds.uid,\n type: seeds.type,\n schemaUid: seeds.schemaUid,\n createdAt: seeds.createdAt,\n attestationCreatedAt: seeds.attestationCreatedAt,\n _markedForDeletion: seeds._markedForDeletion,\n })\n .from(seeds)\n .leftJoin(versionData, eq(seeds.localId, versionData.seedLocalId))\n .where(and(gt(versionData.versionsCount, 0), ...conditions))\n .groupBy(seeds.localId)\n }, [db, isClientReady, modelName, deleted, includeEas, addressFilter, addressesForFilter])\n const seedsTableData = useLiveQuery<SeedType>(seedsQuery)\n\n // Refetch when the live seed ids differ from the listed items.\n useEffect(() => {\n if (!isClientReady || !seedsTableData) return\n\n const tableDataItemsSet = new Set<string>()\n for (const dbSeed of seedsTableData) {\n const key = dbSeed.localId || dbSeed.uid\n if (key) tableDataItemsSet.add(key)\n }\n\n const currentItemsSet = new Set<string>()\n for (const item of itemsRef.current) {\n const key = item.seedLocalId || item.seedUid\n if (key) currentItemsSet.add(key)\n }\n\n if (tableDataItemsSet.size === 0 && currentItemsSet.size > 0) {\n return\n }\n\n if (sameIds(currentItemsSet, tableDataItemsSet)) {\n lastFetchedIdsRef.current = tableDataItemsSet\n return\n }\n\n const requestRefetch = () => {\n lastFetchedIdsRef.current = tableDataItemsSet\n refetchRequestedAtRef.current = fetchesStartedRef.current\n queryClient.invalidateQueries({ queryKey })\n }\n\n if (!sameIds(lastFetchedIdsRef.current, tableDataItemsSet)) {\n requestRefetch()\n return\n }\n\n // A refetch was already requested for these ids. If a fetch started after the request, it\n // answers it, or failed and retries on its own: Item.all and the live query can disagree\n // (an item still loading), so don't loop.\n if (fetchesStartedRef.current > refetchRequestedAtRef.current) return\n // The request joined a fetch that started before the change. Wait for it to settle (this\n // effect runs again then), and refetch: it listed the old ids.\n if (isFetching) return\n requestRefetch()\n }, [isClientReady, seedsTableData, queryClient, queryKey, dataUpdatedAt, isFetching])\n\n // Same array while the data is the same, so `items` can be an effect/memo dependency.\n const sortedItems = useMemo(\n () =>\n orderBy(\n items,\n [\n (item) =>\n item.lastVersionPublishedAt ||\n item.attestationCreatedAt ||\n item.createdAt,\n ],\n ['desc'],\n ),\n [items],\n )\n\n return {\n items: sortedItems,\n // Before the client is ready the query is disabled, which TanStack reports as not loading:\n // count it as loading so an empty list isn't shown as \"no items\" first.\n isLoading: isLoading || !isClientReady,\n error: queryError as Error | null,\n }\n}\n\nexport type UseCreateItemReturn = {\n /**\n * Calls `Item.create({ modelName, ...itemData }, options)`. A call made while another is in\n * flight returns that call's promise (and its item) instead of creating a second one.\n */\n createItem: (\n modelName: string,\n itemData?: Record<string, any>,\n options?: ItemCreateOptions,\n ) => Promise<Item<any> | undefined>\n isLoading: boolean\n error: Error | null\n resetError: () => void\n}\n\nexport const useCreateItem = (): UseCreateItemReturn => {\n const [isLoading, setIsLoading] = useState(false)\n const [error, setError] = useState<Error | null>(null)\n // A ref, not the isLoading state: two calls before a re-render both saw isLoading false.\n const inFlightRef = useRef<Promise<Item<any> | undefined> | null>(null)\n\n const resetError = useCallback(() => setError(null), [])\n\n const createItem = useCallback(\n (\n modelName: string,\n itemData?: Record<string, any>,\n options?: ItemCreateOptions,\n ): Promise<Item<any> | undefined> => {\n if (inFlightRef.current) {\n logger('[useCreateItem] [createItem] already creating item, returning the in-flight create')\n return inFlightRef.current\n }\n\n setError(null)\n // Flush loading=true synchronously so the UI (and tests) can observe it before async work runs.\n flushSync(() => setIsLoading(true))\n\n const run = async (): Promise<Item<any> | undefined> => {\n try {\n const data = itemData ?? {}\n // Item.create runs File/Image/Html values through their save pipeline; createNewItem stores them raw.\n const newItem = await Item.create({ modelName, ...data } as Parameters<typeof Item.create>[0], options)\n return (newItem ?? undefined) as Item<any> | undefined\n } catch (err) {\n logger('[useCreateItem] Error creating item:', err)\n setError(err instanceof Error ? err : new Error(String(err)))\n return undefined\n } finally {\n inFlightRef.current = null\n // Defer clearing loading so React can commit the loading=true render first.\n // Otherwise the test (or UI) may never observe isLoading true (same continuation batching).\n queueMicrotask(() => setIsLoading(false))\n }\n }\n const promise = run()\n inFlightRef.current = promise\n return promise\n },\n [],\n )\n\n return {\n createItem,\n isLoading,\n error,\n resetError,\n }\n}\n\ntype UsePublishItemReturn = {\n /**\n * Calls `item.publish(options)`, which needs @seedprotocol/publish: `initPublish()` or\n * `<PublishProvider config>` must have run, or `error` reports that publishing is not configured.\n */\n publishItem: (item: Item<any> | undefined, options?: ItemPublishOptions) => void\n isLoading: boolean\n error: Error | null\n resetError: () => void\n}\n\nexport const usePublishItem = (): UsePublishItemReturn => {\n const [publishingItem, setPublishingItem] = useState<Item<any> | null>(null)\n const [isLoading, setIsLoading] = useState(false)\n const [error, setError] = useState<Error | null>(null)\n const subscriptionRef = useRef<Subscription | undefined>(undefined)\n\n const resetError = useCallback(() => setError(null), [])\n\n const publishItem = useCallback((item: Item<any> | undefined, options?: ItemPublishOptions) => {\n if (!item) return\n setPublishingItem(item)\n setError(null)\n // The promise is the one source of errors: it also rejects before the item machine\n // starts (publishing not configured, item not owned, already publishing).\n item.publish(options).catch((err: unknown) => {\n setError(err instanceof Error ? err : new Error(String(err)))\n })\n }, [])\n\n useEffect(() => {\n if (!publishingItem) {\n subscriptionRef.current?.unsubscribe()\n subscriptionRef.current = undefined\n setIsLoading(false)\n return\n }\n\n subscriptionRef.current?.unsubscribe()\n const service = publishingItem.getService()\n const subscription = service.subscribe((snapshot: any) => {\n setIsLoading(snapshot?.value === 'publishing')\n })\n\n subscriptionRef.current = subscription\n setIsLoading(service.getSnapshot()?.value === 'publishing')\n\n return () => {\n subscriptionRef.current?.unsubscribe()\n subscriptionRef.current = undefined\n }\n }, [publishingItem])\n\n return {\n publishItem,\n isLoading,\n error,\n resetError,\n }\n}\n\nfunction sameIds(a: Set<string>, b: Set<string>): boolean {\n return a.size === b.size && [...a].every((id) => b.has(id))\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport { Subscription } from 'xstate'\nimport debug from 'debug'\nimport { ItemProperty, ItemPropertySaveValidationError } from '@seedprotocol/sdk'\nimport { useIsClientReady } from './client'\nimport type { IItemProperty } from '@seedprotocol/sdk'\nimport { useLiveQuery } from './liveQuery'\nimport { useSeedQueryClient } from './queryClient'\nimport { BaseDb } from '@seedprotocol/sdk'\nimport { metadata } from '@seedprotocol/sdk'\nimport { seeds } from '@seedprotocol/sdk'\nimport { and, eq, isNotNull } from 'drizzle-orm'\nimport { pickLatestMetadataRowPerProperty } from '@seedprotocol/sdk'\nimport { debounce, startCase } from 'lodash-es'\nimport { useQuery, useQueryClient } from '@tanstack/react-query'\n\nconst logger = debug('seedSdk:react:property')\nconst propertiesLogger = debug('seedSdk:react:itemProperties')\n\n\ntype UseItemPropertyReturn = {\n property: IItemProperty | undefined\n isLoading: boolean\n error: Error | null\n}\n\n/** Query key for one item property: `useItemProperty`'s query. */\nexport const getItemPropertyQueryKey = (\n seedLocalId: string | undefined,\n seedUid: string | undefined,\n propertyName: string,\n) => ['seed', 'itemProperty', seedLocalId ?? null, seedUid ?? null, propertyName] as const\n\n/**\n * Hook to get a specific ItemProperty instance\n * Can be called in multiple ways:\n * 1. With seedLocalId/seedUid and propertyName: useItemProperty({ seedLocalId, propertyName }) or useItemProperty({ seedUid, propertyName })\n * 2. With itemId and propertyName: useItemProperty(itemId, propertyName) or useItemProperty({ itemId, propertyName })\n *\n * A property already in the instance cache and ready (`ItemProperty.peekReady`) comes back on the\n * first render with `isLoading: false`. Changing the lookup never returns the previous property.\n *\n * @overload\n * @param props - Object with seedLocalId or seedUid, and propertyName\n * @returns Object with property, isLoading, and error\n *\n * @overload\n * @param props - Object with itemId and propertyName\n * @returns Object with property, isLoading, and error\n *\n * @overload\n * @param itemId - The item ID (seedLocalId or seedUid)\n * @param propertyName - The name of the property\n * @returns Object with property, isLoading, and error\n */\nexport function useItemProperty(props: {\n seedLocalId?: string\n seedUid?: string\n propertyName: string\n}): UseItemPropertyReturn\nexport function useItemProperty(props: {\n itemId?: string\n propertyName: string\n}): UseItemPropertyReturn\nexport function useItemProperty(\n itemId: string,\n propertyName: string\n): UseItemPropertyReturn\nexport function useItemProperty(\n arg1:\n | { seedLocalId?: string; seedUid?: string; propertyName: string }\n | { itemId: string; propertyName: string }\n | string,\n arg2?: string\n) {\n const isClientReady = useIsClientReady()\n const queryClient = useSeedQueryClient()\n const [, setVersion] = useState(0) // Version counter to force re-renders\n\n // Extract primitives so useMemo/useCallback deps are stable when caller passes inline objects\n // Support object form with itemId: useItemProperty({ itemId, propertyName })\n const arg1IsObject = typeof arg1 === 'object' && arg1 != null\n const obj = arg1IsObject ? (arg1 as Record<string, unknown>) : null\n const itemIdFromObj = obj != null ? (obj.itemId as string | undefined) : undefined\n const seedLocalId =\n obj != null ? (obj.seedLocalId as string | undefined) : undefined\n const seedUid =\n obj != null ? (obj.seedUid as string | undefined) : undefined\n const propertyNameFromObj =\n obj != null ? (obj.propertyName as string | undefined) : undefined\n const itemId =\n typeof arg1 === 'string' ? arg1 : (itemIdFromObj !== undefined && itemIdFromObj !== '' ? itemIdFromObj : undefined)\n const propertyNameFromArgs = typeof arg1 === 'string' ? arg2 : undefined\n const propertyName = propertyNameFromObj ?? propertyNameFromArgs\n\n // Determine which lookup mode we're in based on arguments (deps are primitives to avoid infinite loop)\n // Unify itemId and identifiers: when itemId is provided (string or object form), use it as seedLocalId so we hit the same code path\n const lookupMode = useMemo(() => {\n const resolvedSeedLocalId = (itemId !== undefined && itemId !== '') ? itemId : seedLocalId\n const resolvedSeedUid = (itemId !== undefined && itemId !== '') ? undefined : seedUid\n if ((resolvedSeedLocalId || resolvedSeedUid) && propertyName) {\n return {\n type: 'identifiers' as const,\n seedLocalId: resolvedSeedLocalId || undefined,\n seedUid: resolvedSeedUid || undefined,\n propertyName,\n // An itemId may be a seedLocalId or a seedUid: tried as the local id first\n itemId: itemId !== undefined && itemId !== '' ? itemId : undefined,\n }\n }\n return null\n }, [itemId, propertyName, seedLocalId, seedUid])\n\n const queryKey = useMemo(\n () => getItemPropertyQueryKey(lookupMode?.seedLocalId, lookupMode?.seedUid, lookupMode?.propertyName ?? ''),\n [lookupMode],\n )\n\n const query = useQuery(\n {\n queryKey,\n queryFn: async (): Promise<IItemProperty | null> => {\n if (!lookupMode) return null\n // A ready cached property is what find() would return. No hold here: the hook holds the\n // property it shows while mounted (useHeldItemProperties).\n const found =\n ItemProperty.peekReady(lookupMode) ??\n (await ItemProperty.find({\n propertyName: lookupMode.propertyName,\n seedLocalId: lookupMode.seedLocalId,\n seedUid: lookupMode.seedUid,\n hold: false,\n }))\n // itemId may be a seedUid (an item synced from EAS): it was only ever looked up as a local id\n const foundByUid =\n !found && lookupMode.itemId\n ? await ItemProperty.find({ propertyName: lookupMode.propertyName, seedUid: lookupMode.itemId, hold: false })\n : undefined\n const result = found ?? foundByUid\n if (!result) {\n logger(\n `[useItemProperty] no property found for Item.${lookupMode.seedLocalId || lookupMode.seedUid}.${lookupMode.propertyName}`,\n )\n }\n return (result as IItemProperty | undefined) ?? null\n },\n // The ItemProperty instance cache is the cache; see useItem.\n initialData: () =>\n (lookupMode ? ItemProperty.peekReady(lookupMode) : undefined) as IItemProperty | undefined,\n staleTime: 0,\n gcTime: 0,\n // An ItemProperty is a live actor-backed object, not data to compare field by field.\n structuralSharing: false,\n // find() throws when the property fails to load or times out; report that, as before.\n retry: false,\n enabled: isClientReady && !!lookupMode,\n },\n queryClient,\n )\n const property = query.data ?? undefined\n useHeldItemProperties(useMemo(() => (property ? [property] : []), [property]))\n\n // Re-render on the property's own changes (value updates after it loaded).\n useEffect(() => {\n if (!property) return\n\n const bump = () => setVersion((prev) => prev + 1)\n const initialContext = (property.getService().getSnapshot() as any)?.context\n let lastRenderValue: unknown = initialContext?.renderValue\n let lastPropertyValue: unknown = initialContext?.propertyValue\n let wasIdle = false\n let lastVersionAt = 0\n let trailing: ReturnType<typeof setTimeout> | undefined\n const THROTTLE_MS = 50\n const subscription: Subscription = property.getService().subscribe((snapshot: any) => {\n const ctx = snapshot?.context ?? {}\n // A value change re-renders at once: a controlled input bound to property.value that\n // skipped a render was reset to the old value, and the next keystroke built on it.\n if (!Object.is(ctx.renderValue, lastRenderValue) || !Object.is(ctx.propertyValue, lastPropertyValue)) {\n lastRenderValue = ctx.renderValue\n lastPropertyValue = ctx.propertyValue\n wasIdle = snapshot.value === 'idle'\n lastVersionAt = Date.now()\n clearTimeout(trailing)\n trailing = undefined\n bump()\n return\n }\n const isIdle = snapshot.value === 'idle'\n if (isIdle) {\n // Idle snapshots repeat often; re-render only on arriving there.\n if (!wasIdle) {\n wasIdle = true\n clearTimeout(trailing)\n trailing = undefined\n bump()\n }\n return\n }\n wasIdle = false\n // Other fields (saving flags, errors) change on many snapshots in a row: throttle those, with a\n // trailing render so the last one is never dropped.\n const now = Date.now()\n if (now - lastVersionAt >= THROTTLE_MS) {\n lastVersionAt = now\n bump()\n } else if (!trailing) {\n trailing = setTimeout(() => {\n trailing = undefined\n lastVersionAt = Date.now()\n bump()\n }, THROTTLE_MS - (now - lastVersionAt))\n }\n })\n\n return () => {\n clearTimeout(trailing)\n subscription.unsubscribe()\n }\n }, [property])\n\n return {\n property,\n isLoading: !!lookupMode && query.isPending,\n error: query.error as Error | null,\n }\n}\n\ntype UseDebouncedItemPropertyParams =\n | { seedLocalId?: string; seedUid?: string; propertyName: string }\n | { itemId: string; propertyName: string }\n\ntype UseDebouncedItemPropertyReturn = {\n property: IItemProperty | undefined\n setValue: (value: string) => void\n /** Save a pending edit now instead of after the debounce delay (e.g. before navigating). */\n flush: () => void\n isLoading: boolean\n error: Error | null\n}\n\n/**\n * Hook for real-time ItemProperty updates with debounced persistence.\n * Updates the display immediately on each change while debouncing writes to the database.\n * Use this for text inputs and other high-frequency updates.\n *\n * @param params - Same as useItemProperty: { seedLocalId, propertyName }, { seedUid, propertyName }, or { itemId, propertyName }\n * @param debounceMs - Debounce delay for persistence (default: 300)\n */\nexport function useDebouncedItemProperty(\n params: UseDebouncedItemPropertyParams,\n debounceMs = 300\n): UseDebouncedItemPropertyReturn {\n const itemId = 'itemId' in params ? params.itemId : undefined\n const seedLocalId = 'seedLocalId' in params ? params.seedLocalId : undefined\n const seedUid = 'seedUid' in params ? params.seedUid : undefined\n const propertyName = params.propertyName\n\n const normalizedParams = useMemo(() => {\n if (itemId) {\n return { itemId, propertyName }\n }\n return { seedLocalId, seedUid, propertyName }\n }, [itemId, seedLocalId, seedUid, propertyName])\n\n const { property, isLoading, error: queryError } = useItemProperty(normalizedParams)\n const latestValueRef = useRef<string>('')\n const saveError = usePropertySaveError(property)\n\n const debouncedPersist = useMemo(\n () =>\n debounce((prop: IItemProperty) => {\n // setValue looks up a missing propertyRecordSchema and, when the save is refused (item not\n // owned) or breaks a rule, rolls back to what showed before the first keystroke.\n prop.setValue(latestValueRef.current)\n }, debounceMs),\n [debounceMs]\n )\n\n // Save a pending edit when the component unmounts (or the delay changes); cancelling here lost\n // the last keystrokes typed within the debounce window.\n useEffect(() => {\n return () => debouncedPersist.flush()\n }, [debouncedPersist])\n\n const flush = useCallback(() => debouncedPersist.flush(), [debouncedPersist])\n\n const setValue = useCallback(\n (value: string) => {\n if (!property) return\n latestValueRef.current = value\n property.showValue(value)\n debouncedPersist(property)\n },\n [property, debouncedPersist]\n )\n\n return {\n property,\n setValue,\n flush,\n isLoading,\n error: queryError ?? saveError,\n }\n}\n\n/**\n * The property's last save failure: `_saveError` (an OwnershipError for an item you don't own,\n * say), or an ItemPropertySaveValidationError when the value broke a rule. Cleared by the next save.\n */\nfunction usePropertySaveError(property: IItemProperty | undefined): Error | null {\n const [error, setError] = useState<Error | null>(null)\n useEffect(() => {\n if (!property) {\n setError(null)\n return\n }\n const read = (ctx: any): Error | null => {\n if (ctx?._saveError) return ctx._saveError as Error\n const validationErrors = ctx?._saveValidationErrors\n if (validationErrors?.length) {\n return new ItemPropertySaveValidationError(\n `Invalid value for property ${ctx.propertyName}: ${validationErrors.map((e: { message: string }) => e.message).join('; ')}`,\n validationErrors,\n )\n }\n return null\n }\n let lastSource: unknown\n const update = (ctx: any) => {\n // Compare the context fields, not the built error, so an unchanged failure keeps its identity.\n const source = ctx?._saveError || (ctx?._saveValidationErrors?.length ? ctx._saveValidationErrors : null)\n if (source === lastSource) return\n lastSource = source\n setError(read(ctx))\n }\n update((property.getService().getSnapshot() as any)?.context)\n const subscription: Subscription = property.getService().subscribe((snapshot: any) => update(snapshot?.context))\n return () => subscription.unsubscribe()\n }, [property])\n return error\n}\n\ntype UseItemPropertiesReturn = {\n properties: IItemProperty[]\n isLoading: boolean\n error: Error | null\n}\n\n/**\n * Holds each property while the component shows it, so an item unloading elsewhere doesn't stop\n * it. The release is deferred a tick: a StrictMode remount or a refetch returning the same\n * instances takes its new hold before the old one is dropped, so the count never touches 0.\n */\nfunction useHeldItemProperties(properties: readonly IItemProperty[]): void {\n useEffect(() => {\n const held = properties.filter((p) => ItemProperty.retain(p))\n return () => {\n setTimeout(() => {\n for (const p of held) p.unload()\n }, 0)\n }\n }, [properties])\n}\n\n/** Fetches item properties list for useQuery (shared with useItemProperties). */\nasync function fetchItemPropertiesList(\n seedLocalId: string | undefined,\n seedUid: string | undefined\n): Promise<IItemProperty[]> {\n if (!seedLocalId && !seedUid) return []\n const db = BaseDb.getAppDb()\n if (!db) return []\n\n const baseList = await ItemProperty.all(\n { seedLocalId: seedLocalId ?? undefined, seedUid: seedUid ?? undefined },\n { waitForReady: true, hold: false },\n )\n const _itemProperties: IItemProperty[] = [...(baseList as IItemProperty[])]\n\n const propertiesWithMetadata = new Set<string>()\n for (const p of baseList) {\n if (p.propertyName) propertiesWithMetadata.add(p.propertyName)\n }\n\n let modelName: string | undefined\n if (baseList.length > 0) {\n const first = baseList[0]\n modelName = first.modelName ?? (first as any).modelType\n if (modelName && typeof modelName === 'string') modelName = startCase(modelName)\n }\n // The seed records which model it belongs to (model names are only unique per schema)\n const seedRecords = await db\n .select({ type: seeds.type, modelFileId: seeds.modelFileId })\n .from(seeds)\n .where(seedUid ? eq(seeds.uid, seedUid) : eq(seeds.localId, seedLocalId!))\n .limit(1)\n const modelFileId: string | undefined = seedRecords[0]?.modelFileId ?? undefined\n if (!modelName && seedRecords.length > 0 && seedRecords[0].type) {\n modelName = startCase(seedRecords[0].type)\n }\n\n const modelProperties: string[] = []\n if (modelName) {\n try {\n const { Model } = await import('@seedprotocol/sdk')\n const model = await Model.resolveAsync(modelName, { modelFileId })\n if (model?.properties) {\n for (const modelProperty of model.properties) {\n if (modelProperty.name) modelProperties.push(modelProperty.name)\n }\n }\n } catch (error) {\n propertiesLogger(`[useItemProperties] Error getting ModelProperties for ${modelName}:`, error)\n }\n }\n\n if (modelName && modelProperties.length > 0) {\n const resolvedSeedLocalId = baseList.length > 0 ? (baseList[0].seedLocalId ?? seedLocalId) : seedLocalId\n const resolvedSeedUid = baseList.length > 0 ? (baseList[0].seedUid ?? seedUid) : seedUid\n for (const propertyName of modelProperties) {\n if (propertiesWithMetadata.has(propertyName)) continue\n try {\n const itemProperty = ItemProperty.create(\n {\n propertyName,\n modelName,\n seedLocalId: resolvedSeedLocalId || undefined,\n seedUid: resolvedSeedUid || undefined,\n propertyValue: null,\n },\n { waitForReady: false, hold: false },\n )\n if (itemProperty) _itemProperties.push(itemProperty as any as IItemProperty)\n } catch (error) {\n logger(`[useItemProperties] Error creating ItemProperty for missing property ${propertyName}:`, error)\n }\n }\n }\n\n if (seedLocalId || seedUid) {\n const seedRecords = await db\n .select({ createdAt: seeds.createdAt })\n .from(seeds)\n .where(seedUid ? eq(seeds.uid, seedUid) : eq(seeds.localId, seedLocalId!))\n .limit(1)\n if (seedRecords.length > 0 && seedRecords[0].createdAt) {\n const createdAtPropertyName = 'createdAt'\n const hasCreatedAtProperty = _itemProperties.some((p) => p.propertyName === createdAtPropertyName)\n if (!hasCreatedAtProperty && modelName) {\n try {\n const resolvedSeedLocalId = baseList.length > 0 ? (baseList[0].seedLocalId ?? seedLocalId) : seedLocalId\n const resolvedSeedUid = baseList.length > 0 ? (baseList[0].seedUid ?? seedUid) : seedUid\n const createdAtProperty = ItemProperty.create(\n {\n propertyName: createdAtPropertyName,\n modelName,\n seedLocalId: resolvedSeedLocalId || undefined,\n seedUid: resolvedSeedUid || undefined,\n propertyValue: seedRecords[0].createdAt.toString(),\n },\n { waitForReady: false, hold: false },\n )\n if (createdAtProperty) _itemProperties.push(createdAtProperty as any as IItemProperty)\n } catch (error) {\n logger(`[useItemProperties] Error creating createdAt ItemProperty:`, error)\n }\n }\n }\n }\n\n return _itemProperties\n}\n\n/**\n * Hook to get all ItemProperty instances for a specific item\n * Can be called in multiple ways:\n * 1. With seedLocalId: useItemProperties({ seedLocalId })\n * 2. With seedUid: useItemProperties({ seedUid })\n * 3. With itemId: useItemProperties(itemId)\n * \n * Uses useLiveQuery to watch for changes in the metadata table and automatically\n * updates the returned ItemProperty instances when changes occur.\n *\n * The list has one entry per model property, including ones the item has no value for yet\n * (their `value` is null), plus a read-only `createdAt` entry built from the item's creation\n * time. Filter on `propertyName` or `value` if you only want stored values.\n * \n * @overload\n * @param props - Object with seedLocalId or seedUid\n * @returns Object with properties array, isLoading, and error\n * \n * @overload\n * @param itemId - The item ID (seedLocalId or seedUid)\n * @returns Object with properties array, isLoading, and error\n */\nexport function useItemProperties(props: {\n seedLocalId?: string\n seedUid?: string\n}): UseItemPropertiesReturn\nexport function useItemProperties(itemId: string): UseItemPropertiesReturn\nexport function useItemProperties(\n arg1: { seedLocalId?: string; seedUid?: string } | string\n): UseItemPropertiesReturn {\n\n const isClientReady = useIsClientReady()\n const queryClient = useQueryClient()\n const previousTableDataRef = useRef<string | undefined>(undefined)\n\n // Determine which lookup mode we're in based on arguments\n const lookupMode = useMemo(() => {\n if (typeof arg1 === 'string') {\n // String argument: itemId (assumed to be seedLocalId)\n return { type: 'itemId' as const, itemId: arg1 }\n } else if (typeof arg1 === 'object') {\n // Object argument: { seedLocalId/seedUid }\n return {\n type: 'identifiers' as const,\n seedLocalId: arg1.seedLocalId,\n seedUid: arg1.seedUid,\n }\n } else {\n return null\n }\n }, [arg1])\n\n // Determine seedLocalId and seedUid for query\n const seedLocalId = useMemo(() => {\n if (!lookupMode) return undefined\n if (lookupMode.type === 'itemId') {\n return lookupMode.itemId\n } else {\n return lookupMode.seedLocalId\n }\n }, [lookupMode])\n\n const seedUid = useMemo(() => {\n if (!lookupMode || lookupMode.type === 'itemId') return undefined\n return lookupMode.seedUid\n }, [lookupMode])\n\n const canonicalItemKey = seedLocalId ?? seedUid ?? ''\n const itemPropertiesQueryKey = useMemo(\n () => (['seed', 'itemProperties', canonicalItemKey] as const),\n [canonicalItemKey],\n )\n\n const {\n data: properties = [],\n isLoading,\n error: queryError,\n } = useQuery({\n queryKey: itemPropertiesQueryKey,\n queryFn: () => fetchItemPropertiesList(seedLocalId, seedUid),\n enabled: isClientReady && !!canonicalItemKey,\n // The instances are held only while a hook shows them: a list kept after unmount could hold\n // stopped ones.\n gcTime: 0,\n })\n useHeldItemProperties(properties)\n\n // Watch the metadata table for changes\n // Query metadata table directly and filter for latest records in JavaScript\n // This is simpler and works better with useLiveQuery than CTEs\n // Get db inside useMemo to avoid recreating query on each render\n // Drizzle query builders are new objects each time, but useMemo will only recreate\n // when dependencies change, which is what we want for reactive queries\n const propertiesQuery = useMemo(() => {\n if (!isClientReady || (!seedLocalId && !seedUid)) {\n propertiesLogger('[useItemProperties] Query: returning null (not ready or no identifiers)')\n return null\n }\n \n const db = BaseDb.getAppDb()\n if (!db) {\n propertiesLogger('[useItemProperties] Query: returning null (no db)')\n return null\n }\n \n propertiesLogger(`[useItemProperties] Query: creating query for seedLocalId=${seedLocalId}, seedUid=${seedUid}`)\n \n // Query metadata table directly - we'll filter for latest records in JavaScript\n const query = seedUid\n ? db\n .select({\n propertyName: metadata.propertyName,\n propertyValue: metadata.propertyValue,\n seedLocalId: metadata.seedLocalId,\n seedUid: metadata.seedUid,\n modelType: metadata.modelType,\n schemaUid: metadata.schemaUid,\n createdAt: metadata.createdAt,\n attestationCreatedAt: metadata.attestationCreatedAt,\n localId: metadata.localId,\n revokedAt: metadata.revokedAt,\n })\n .from(metadata)\n .where(\n and(\n eq(metadata.seedUid, seedUid),\n isNotNull(metadata.propertyName)\n )\n )\n : seedLocalId\n ? db\n .select({\n propertyName: metadata.propertyName,\n propertyValue: metadata.propertyValue,\n seedLocalId: metadata.seedLocalId,\n seedUid: metadata.seedUid,\n modelType: metadata.modelType,\n schemaUid: metadata.schemaUid,\n createdAt: metadata.createdAt,\n attestationCreatedAt: metadata.attestationCreatedAt,\n localId: metadata.localId,\n revokedAt: metadata.revokedAt,\n })\n .from(metadata)\n .where(\n and(\n eq(metadata.seedLocalId, seedLocalId),\n isNotNull(metadata.propertyName)\n )\n )\n : null\n \n propertiesLogger(`[useItemProperties] Query: created query object`, { queryType: seedUid ? 'seedUid' : 'seedLocalId' })\n return query\n }, [isClientReady, seedLocalId, seedUid])\n \n const rawPropertiesTableData = useLiveQuery<{\n propertyName: string | null\n propertyValue: string | null\n seedLocalId: string | null\n seedUid: string | null\n modelType: string | null\n schemaUid: string | null\n createdAt: number | null\n attestationCreatedAt: number | null\n localId: string | null\n revokedAt: number | null\n }>(propertiesQuery)\n \n // Filter for latest records (one per propertyName) in JavaScript\n const propertiesTableData = useMemo(() => {\n if (!rawPropertiesTableData || rawPropertiesTableData.length === 0) {\n return []\n }\n\n // One record per propertyName, in the SDK's reader order (live before revoked, then newest)\n return pickLatestMetadataRowPerProperty(rawPropertiesTableData)\n }, [rawPropertiesTableData])\n\n // Invalidate when metadata table data actually changes so useQuery refetches\n useEffect(() => {\n if (!isClientReady || (!seedLocalId && !seedUid) || propertiesTableData === undefined) return\n\n // Include propertyValue so value-only changes produce a different string and trigger invalidation\n const tableDataString = JSON.stringify(\n propertiesTableData\n .map((p) => ({\n propertyName: p.propertyName,\n propertyValue: p.propertyValue,\n seedLocalId: p.seedLocalId,\n seedUid: p.seedUid,\n }))\n .sort((a, b) => (a.propertyName || '').localeCompare(b.propertyName || ''))\n )\n\n if (previousTableDataRef.current === tableDataString) return\n previousTableDataRef.current = tableDataString\n\n // Invalidate when metadata table data changed (new/updated/removed props or value changes)\n // so useQuery refetches and UI shows latest values.\n if (propertiesTableData.length > 0) {\n queryClient.invalidateQueries({ queryKey: itemPropertiesQueryKey })\n }\n }, [isClientReady, propertiesTableData, properties, seedLocalId, seedUid, queryClient, itemPropertiesQueryKey])\n\n useEffect(() => {\n previousTableDataRef.current = undefined\n }, [seedLocalId, seedUid])\n\n return {\n properties,\n isLoading,\n error: queryError as Error | null,\n }\n}\n\nexport type UseCreateItemPropertyProps = {\n seedLocalId?: string\n seedUid?: string\n propertyName: string\n modelName: string\n propertyValue?: any\n versionLocalId?: string\n versionUid?: string\n [key: string]: any\n}\n\nexport type UseCreateItemPropertyReturn = {\n create: (props: UseCreateItemPropertyProps) => IItemProperty | undefined\n isLoading: boolean\n error: Error | null\n resetError: () => void\n}\n\n/**\n * Hook to create an ItemProperty with loading and error state.\n * create(props) creates a new property instance for an item; provide seedLocalId or seedUid, propertyName, and modelName.\n */\nexport const useCreateItemProperty = (): UseCreateItemPropertyReturn => {\n const subscriptionRef = useRef<Subscription | undefined>(undefined)\n const [isLoading, setIsLoading] = useState(false)\n const [error, setError] = useState<Error | null>(null)\n\n const resetError = useCallback(() => setError(null), [])\n\n const create = useCallback((props: UseCreateItemPropertyProps): IItemProperty | undefined => {\n if (!props.propertyName || (!props.seedLocalId && !props.seedUid) || !props.modelName) {\n const err = new Error('seedLocalId or seedUid, propertyName, and modelName are required')\n setError(err)\n return undefined\n }\n\n setError(null)\n setIsLoading(true)\n subscriptionRef.current?.unsubscribe()\n subscriptionRef.current = undefined\n\n const instance = ItemProperty.create(props, { waitForReady: false })\n if (!instance) {\n setError(new Error('Failed to create item property'))\n setIsLoading(false)\n return undefined\n }\n\n const subscription = instance.getService().subscribe((snapshot: any) => {\n if (snapshot?.value === 'error') {\n const err = (snapshot.context as any)?._loadingError?.error ?? new Error('Failed to create item property')\n setError(err instanceof Error ? err : new Error(String(err)))\n setIsLoading(false)\n }\n if (snapshot?.value === 'idle') {\n setError(null)\n setIsLoading(false)\n }\n })\n subscriptionRef.current = subscription\n return instance\n }, [])\n\n useEffect(() => {\n return () => {\n subscriptionRef.current?.unsubscribe()\n subscriptionRef.current = undefined\n }\n }, [])\n\n return {\n create,\n isLoading,\n error,\n resetError,\n }\n}\n\nexport type UseDestroyItemPropertyReturn = {\n destroy: (itemProperty: IItemProperty) => Promise<void>\n isLoading: boolean\n error: Error | null\n resetError: () => void\n}\n\nexport const useDestroyItemProperty = (): UseDestroyItemPropertyReturn => {\n const [isLoading, setIsLoading] = useState(false)\n const [error, setError] = useState<Error | null>(null)\n\n // Loading state is tracked here: the item property's service doesn't record destroy progress, and\n // destroy() stops the service, often within a few microtasks.\n const destroy = useCallback(async (itemProperty: IItemProperty) => {\n if (!itemProperty) return\n setError(null)\n setIsLoading(true)\n try {\n await itemProperty.destroy()\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)))\n throw err\n } finally {\n setIsLoading(false)\n }\n }, [])\n\n const resetError = useCallback(() => setError(null), [])\n\n return {\n destroy,\n isLoading,\n error,\n resetError,\n }\n}\n","import { ClientManagerState } from '@seedprotocol/sdk'\nimport { useClientSelector } from './client'\n\n/**\n * Client states in which the app database is open: DB_INIT finishes with dbReady and moves to\n * SAVE_CONFIG. DB_INIT itself (still opening, may fail) and INIT_FAILED are not ready.\n */\nconst DB_READY_STATES = new Set<string>([\n ClientManagerState.SAVE_CONFIG,\n ClientManagerState.PROCESS_SCHEMA_FILES,\n ClientManagerState.ADD_MODELS_TO_STORE,\n ClientManagerState.ADD_MODELS_TO_DB,\n ClientManagerState.IDLE,\n])\n\nexport const isDbReadyState = (state: unknown): boolean =>\n typeof state === 'string' && DB_READY_STATES.has(state)\n\n/**\n * Whether the app database is open. True once the client has opened it, false again if init\n * fails or restarts, or after `client.stop()`.\n */\nexport const useDbsAreReady = () => {\n const dbsAreReady = useClientSelector(\n (snapshot) => snapshot.status === 'active' && isDbReadyState(snapshot.value),\n )\n return {\n dbsAreReady,\n }\n}\n","import { useCallback, useEffect, useMemo, useState, useSyncExternalStore } from 'react'\nimport { ActorRef } from 'xstate'\nimport pluralize from 'pluralize'\nimport debug from 'debug'\nimport { ClientManagerState, MachineIds } from '@seedprotocol/sdk'\nimport { isDbReadyState } from './db'\nimport { useClientSelector, useClientService } from './client'\n\nconst logger = debug('seedSdk:react:services')\n\nconst finalStrings = ['idle', 'ready', 'done', 'success', 'initialized']\n\nexport const getServiceName = (service: ActorRef<any, any>) => {\n let name = 'actor'\n const serviceAny = service as any\n if (service && serviceAny.uniqueKey) {\n name = serviceAny.uniqueKey\n }\n if (service && !serviceAny.uniqueKey && serviceAny.logic && serviceAny.logic.config) {\n name = getServiceUniqueKey(service)\n }\n return name\n}\n\nexport const getServiceValue = (\n service: ActorRef<any, any>,\n): string | undefined => {\n let value\n if (service && service.getSnapshot() && service.getSnapshot().value) {\n value = service.getSnapshot().value\n }\n if (getServiceName(service) === 'global') {\n if (\n value &&\n typeof value === 'object' &&\n Object.keys(value).length > 0 &&\n Object.keys(value)[0] === 'initialized'\n ) {\n value = 'ready'\n }\n }\n if (value && typeof value === 'object') {\n value = JSON.stringify(value)\n }\n return value\n}\n\nexport const getServiceUniqueKey = (service: ActorRef<any, any>) => {\n const serviceAny = service as any\n if (!service || !serviceAny.logic || !serviceAny.logic.config || !serviceAny._snapshot) {\n return\n }\n const config = serviceAny.logic.config\n if (!config.id) {\n return\n }\n let uniqueKey = config.id\n if (config.id.includes('@seedSdk/')) {\n uniqueKey = config.id.match(/^.*@seedSdk\\/(\\w+)[\\.\\w]*/)[1]\n }\n let snapshot\n try {\n snapshot = service.getSnapshot()\n } catch (error) {\n logger('Error:', error)\n return uniqueKey\n }\n if (snapshot) {\n const context = snapshot.context\n if (context && context.dbName) {\n uniqueKey = context.dbName\n }\n if (context && context.modelNamePlural) {\n uniqueKey = context.modelNamePlural\n }\n if (context && context.modelName) {\n uniqueKey = pluralize(context.modelName.toLowerCase())\n }\n }\n return uniqueKey\n}\n\nconst isClientManagerService = (service: ActorRef<any, any>): boolean =>\n (service as any)?.logic?.config?.id === MachineIds.CLIENT_MANAGER\n\n/**\n * 100 once the service reaches a final state ('idle', 'ready', ...). During startup, the client\n * manager's progress through init, or the current state's position among the `loading`-tagged\n * states for other services. 0 when there's nothing to measure.\n */\nexport const getPercentComplete = (service: ActorRef<any, any>): number => {\n const snapshot = service?.getSnapshot()\n if (isClientManagerService(service)) {\n return snapshot?.status === 'active' ? percentForClientState(snapshot.value) : 0\n }\n const value = getServiceValue(service)\n if (value && finalStrings.includes(value)) {\n return 100\n }\n const states = (service as any)?.logic?.states\n if (!value || !states) {\n return 0\n }\n const loadingStateNames = Object.entries(states)\n .filter(([, state]) => (state as { tags?: string[] }).tags?.includes('loading'))\n .map(([stateName]) => stateName)\n const stateIndex = loadingStateNames.indexOf(value)\n if (stateIndex < 0) {\n return 0\n }\n return (stateIndex / loadingStateNames.length) * 100\n}\n\nexport const useService = (service: ActorRef<any, any>) => {\n const [timeElapsed, setTimeElapsed] = useState(0)\n\n // Re-render on state changes, not only on the timer below.\n const subscribe = useCallback(\n (onChange: () => void) => {\n const subscription = service.subscribe({ next: onChange, error: onChange, complete: onChange })\n return () => subscription.unsubscribe()\n },\n [service],\n )\n const getValue = () => getServiceValue(service)\n const value = useSyncExternalStore(subscribe, getValue, getValue)\n\n const updateTime = useCallback(\n (interval: ReturnType<typeof setInterval>) => {\n const status = service.getSnapshot().value\n if (\n status === 'done' ||\n status === 'success' ||\n status === 'idle' ||\n status === 'ready'\n ) {\n clearInterval(interval)\n return\n }\n setTimeElapsed((timeElapsed) => timeElapsed + 1)\n },\n [service],\n )\n\n const startInterval = useCallback(() => {\n const interval = setInterval(() => {\n updateTime(interval)\n }, 1000)\n return interval\n }, [updateTime, service])\n\n useEffect(() => {\n const interval = startInterval()\n return () => clearInterval(interval)\n }, [])\n\n return {\n name: getServiceName(service),\n timeElapsed,\n value,\n percentComplete: getPercentComplete(service),\n uniqueKey: getServiceUniqueKey(service),\n }\n}\n\n/** Whether the app database is open (see useDbsAreReady). */\nexport const useIsDbReady = () => {\n const { internalStatus } = useGlobalServiceStatus()\n return internalStatus === 'ready'\n}\n\n/**\n * @deprecated Does nothing: actor snapshots were never written, and loaded ones were discarded.\n * Will be removed.\n */\nexport const usePersistedSnapshots = () => {}\n\n/**\n * @deprecated Always false: nothing writes snapshot rows. Will be removed.\n */\nexport const useHasSavedSnapshots = () => false\n\nconst percentForClientState = (state: unknown): number => {\n switch (state) {\n case ClientManagerState.IDLE:\n return 100\n case ClientManagerState.ADD_MODELS_TO_DB:\n return 90\n case ClientManagerState.ADD_MODELS_TO_STORE:\n return 80\n case ClientManagerState.PROCESS_SCHEMA_FILES:\n return 70\n case ClientManagerState.SAVE_CONFIG:\n return 60\n case ClientManagerState.DB_INIT:\n return 50\n case ClientManagerState.FILE_SYSTEM_INIT:\n return 30\n case ClientManagerState.PLATFORM_CLASSES_INIT:\n return 10\n default:\n return 0\n }\n}\n\nexport const useServices = () => {\n const clientService = useClientService()\n const percentComplete = useClientSelector((snapshot) =>\n snapshot.status === 'active' ? percentForClientState(snapshot.value) : 0,\n )\n const services = useMemo(() => {\n ;(clientService as any).uniqueKey = 'clientManager'\n return [clientService as ActorRef<any, any>]\n }, [clientService])\n\n return {\n services,\n percentComplete,\n }\n}\n\nexport const useGlobalServiceStatus = () => {\n const status = useClientSelector((snapshot) => snapshot.value)\n\n // 'ready' while the app database is open (see isDbReadyState), otherwise the client state.\n const internalStatus = useClientSelector((snapshot) =>\n snapshot.status === 'active' && isDbReadyState(snapshot.value) ? 'ready' : snapshot.value,\n )\n\n return {\n status,\n internalStatus,\n }\n}\n","import { useCallback, useEffect, useRef, useState } from 'react'\n\ntype CreatedSnapshot = { value: unknown; context: unknown }\n\n/** An entity a create hook returns: Schema, Model or ModelProperty. */\ntype CreatedEntity = {\n getService(): {\n getSnapshot(): CreatedSnapshot\n subscribe(listener: (snapshot: CreatedSnapshot) => void): { unsubscribe(): void }\n }\n}\n\nconst toError = (err: unknown): Error => (err instanceof Error ? err : new Error(String(err)))\n\n/**\n * Loading and error state for a create hook. `run(create)` sets `isLoading`, calls `create`, then\n * follows the returned entity's service until it is idle or in error. The current snapshot is read\n * right after subscribing, so an entity that was already loaded (no further state change) clears\n * `isLoading` at once. A throw or rejection from `create` clears it, sets `error` and is rethrown.\n */\nexport function useCreateState(failureMessage: string) {\n const subscriptionRef = useRef<{ unsubscribe(): void } | null>(null)\n // Bumped by each run and on unmount, so a superseded async create doesn't take over the state.\n const runIdRef = useRef(0)\n const [isLoading, setIsLoading] = useState(false)\n const [error, setError] = useState<Error | null>(null)\n\n const resetError = useCallback(() => setError(null), [])\n\n const follow = useCallback(\n (entity: CreatedEntity) => {\n const apply = (snapshot: CreatedSnapshot) => {\n if (snapshot.value === 'error') {\n const err = (snapshot.context as { _loadingError?: { error?: unknown } })?._loadingError?.error\n setError(err instanceof Error ? err : new Error(failureMessage))\n setIsLoading(false)\n } else if (snapshot.value === 'idle') {\n setError(null)\n setIsLoading(false)\n }\n }\n const service = entity.getService()\n subscriptionRef.current = service.subscribe(apply)\n apply(service.getSnapshot())\n },\n [failureMessage],\n )\n\n const fail = useCallback((err: unknown): never => {\n setError(toError(err))\n setIsLoading(false)\n throw err\n }, [])\n\n const start = useCallback(() => {\n subscriptionRef.current?.unsubscribe()\n subscriptionRef.current = null\n setError(null)\n setIsLoading(true)\n return ++runIdRef.current\n }, [])\n\n /** For a synchronous SDK create. */\n const run = useCallback(\n <T extends CreatedEntity>(create: () => T): T => {\n start()\n let entity: T\n try {\n entity = create()\n } catch (err) {\n return fail(err)\n }\n follow(entity)\n return entity\n },\n [start, fail, follow],\n )\n\n /** For a create that has to await something first. */\n const runAsync = useCallback(\n async <T extends CreatedEntity>(create: () => Promise<T>): Promise<T> => {\n const runId = start()\n let entity: T\n try {\n entity = await create()\n } catch (err) {\n if (runId !== runIdRef.current) throw err\n return fail(err)\n }\n if (runId === runIdRef.current) follow(entity)\n return entity\n },\n [start, fail, follow],\n )\n\n useEffect(() => {\n return () => {\n runIdRef.current++\n subscriptionRef.current?.unsubscribe()\n subscriptionRef.current = null\n }\n }, [])\n\n return { run, runAsync, isLoading, error, resetError }\n}\n","import { loadAllSchemasFromDb } from '@seedprotocol/sdk'\nimport { useCallback, useEffect, useRef, useState, useMemo } from \"react\"\nimport { flushSync } from \"react-dom\"\nimport debug from \"debug\"\nimport { useIsClientReady } from \"./client\"\nimport { Schema } from '@seedprotocol/sdk'\nimport { SnapshotFrom, Subscription } from \"xstate\"\nimport { schemaMachine, SEED_PROTOCOL_SCHEMA_NAME } from '@seedprotocol/sdk'\nimport { useLiveQuery } from \"./liveQuery\"\nimport { BaseDb, schemas as schemasTable } from '@seedprotocol/sdk'\nimport { desc, eq, or } from \"drizzle-orm\"\nimport type { SchemaType as DbSchemaType } from '@seedprotocol/sdk'\nimport { useQuery } from \"@tanstack/react-query\"\nimport { useSeedQueryClient } from \"./queryClient\"\nimport { useCreateState } from \"./useCreateState\"\n\nconst logger = debug('seedSdk:react:schema')\n\n/**\n * Hook to get a Schema class instance (with setters) that is reactive\n * This allows you to edit schema properties directly like: schema.name = 'New name'\n * The schema instance uses a Proxy to ensure React re-renders when properties change\n * @param schemaIdentifier - The name of the schema or the schema file ID\n * - If a name is provided, retrieves the latest version with that name\n * - If an ID is provided, retrieves the specific schema by ID\n * @returns Object with schema instance\n */\nexport const useSchema = (schemaIdentifier: string | null | undefined) => {\n const [schema, setSchema] = useState<Schema | null>(null)\n const [isLoading, setIsLoading] = useState(schemaIdentifier ? true : false)\n const [error, setError] = useState<Error | null>(null)\n const isClientReady = useIsClientReady()\n\n // Each load takes a hold on the cached instance (Schema.create); the load's cleanup releases it.\n const loadSchema = useCallback(async (identifier: string, load: SchemaLoad) => {\n setIsLoading(true)\n setError(null)\n try {\n // Look the schema up only: Schema.create would add a draft schema for a name that doesn't\n // exist (e.g. a typo), so check the schemas table first.\n if (!(await schemaExists(identifier))) {\n if (!load.current) return\n setSchema(null)\n setIsLoading(false)\n return\n }\n if (!load.current) return\n const schemaInstance = Schema.create(identifier, {\n waitForReady: false,\n }) as Schema\n load.instance = schemaInstance\n setSchema(schemaInstance)\n\n const service = schemaInstance.getService()\n const apply = (snapshot: SchemaSnapshot) => {\n if (snapshot.value === 'idle') {\n flushSync(() => setIsLoading(false))\n setError(null)\n } else if (snapshot.value === 'error') {\n // A failed load used to report isLoading: true, error: null forever.\n const err = snapshot.context._loadingError?.error\n setIsLoading(false)\n setError(err instanceof Error ? err : new Error(`Failed to load schema \"${identifier}\"`))\n } else {\n setIsLoading(true)\n }\n }\n apply(service.getSnapshot())\n load.subscription = service.subscribe(apply)\n } catch (error) {\n logger('[useSchema] Error creating schema:', error)\n if (!load.current) return\n setError(error as Error)\n setSchema(null)\n setIsLoading(false)\n }\n }, [])\n\n useEffect(() => {\n if (!isClientReady) {\n setSchema(null)\n setError(null)\n setIsLoading(false)\n return\n }\n if (!schemaIdentifier) {\n setSchema(null)\n setError(null)\n setIsLoading(false)\n return\n }\n\n const load: SchemaLoad = { current: true, instance: null, subscription: null }\n void loadSchema(schemaIdentifier, load)\n\n // On unmount or identifier change: stop following the instance and release this load's hold.\n return () => {\n load.current = false\n load.subscription?.unsubscribe()\n load.subscription = null\n load.instance?.unload()\n load.instance = null\n }\n }, [schemaIdentifier, isClientReady, loadSchema])\n \n return {\n schema,\n isLoading,\n error,\n }\n\n}\n\ntype SchemaLoad = {\n current: boolean\n instance: Schema | null\n subscription: Subscription | null\n}\n\n/** Whether a schema with this name or schema file id is stored (or is the built-in one). */\nasync function schemaExists(identifier: string): Promise<boolean> {\n if (identifier === SEED_PROTOCOL_SCHEMA_NAME) return true\n const db = BaseDb.getAppDb()\n if (!db) return true // can't tell yet: let Schema.create resolve it\n const rows = await db\n .select({ id: schemasTable.id })\n .from(schemasTable)\n .where(or(eq(schemasTable.name, identifier), eq(schemasTable.schemaFileId, identifier)))\n .limit(1)\n return rows.length > 0\n}\n\nconst SEED_SCHEMAS_QUERY_KEY = ['seed', 'schemas'] as const\n\nexport const useSchemas = () => {\n const isClientReady = useIsClientReady()\n const queryClient = useSeedQueryClient()\n const previousSchemasTableDataRef = useRef<DbSchemaType[] | undefined>(undefined)\n const schemasRef = useRef<Schema[]>([])\n\n const {\n data: schemas = [],\n isLoading,\n error: queryError,\n } = useQuery({\n queryKey: SEED_SCHEMAS_QUERY_KEY,\n queryFn: () => Schema.all({ waitForReady: true }),\n enabled: isClientReady,\n }, queryClient)\n schemasRef.current = schemas\n\n // Watch the schemas table for changes and invalidate so useQuery refetches\n const db = isClientReady ? BaseDb.getAppDb() : null\n const schemasQuery = useMemo(() => {\n if (!db) return null\n return db.select().from(schemasTable).orderBy(schemasTable.name, desc(schemasTable.version))\n }, [db, isClientReady])\n const schemasTableData = useLiveQuery<DbSchemaType>(schemasQuery)\n\n // When a schema is created, addSchemaToDb posts to this channel; live query often doesn't re-run when schemas table is inserted.\n useEffect(() => {\n if (typeof BroadcastChannel === 'undefined') return\n const ch = new BroadcastChannel('seed-schemas-invalidate')\n const onMessage = () => {\n queryClient.invalidateQueries({ queryKey: SEED_SCHEMAS_QUERY_KEY })\n }\n ch.addEventListener('message', onMessage)\n return () => {\n ch.removeEventListener('message', onMessage)\n ch.close()\n }\n }, [queryClient])\n\n useEffect(() => {\n if (!isClientReady || !schemasTableData) {\n return\n }\n\n const prevData = previousSchemasTableDataRef.current\n const prevDataJson = prevData ? JSON.stringify(prevData) : 'undefined'\n const currDataJson = schemasTableData ? JSON.stringify(schemasTableData) : 'undefined'\n\n if (prevDataJson === currDataJson && prevData !== undefined) {\n return\n }\n\n previousSchemasTableDataRef.current = schemasTableData\n\n const currentSchemasSet = new Set<string>()\n for (const schema of schemasRef.current) {\n if (schema.metadata?.name === SEED_PROTOCOL_SCHEMA_NAME) continue\n const schemaFileId = schema.id || schema.schemaFileId\n if (schemaFileId) {\n currentSchemasSet.add(schemaFileId)\n } else {\n const name = schema.metadata?.name\n const version = schema.version\n if (name && version !== undefined) {\n currentSchemasSet.add(`${name}:${version}`)\n }\n }\n }\n\n const tableDataSchemasSet = new Set<string>()\n for (const dbSchema of schemasTableData) {\n if (dbSchema.name === 'Seed Protocol') continue\n if (dbSchema.schemaFileId) {\n tableDataSchemasSet.add(dbSchema.schemaFileId)\n } else if (dbSchema.name != null && dbSchema.version !== undefined) {\n tableDataSchemasSet.add(`${dbSchema.name}:${dbSchema.version}`)\n }\n }\n\n const setsAreEqual =\n currentSchemasSet.size === tableDataSchemasSet.size &&\n [...currentSchemasSet].every((id) => tableDataSchemasSet.has(id))\n\n // Only invalidate when we have data and the table has rows we might be missing (live query saw new data).\n // Skip during initial load (currentSchemasSet empty) to avoid disrupting loading state.\n // Do NOT invalidate when we have more than the table: the live query may not have updated\n // yet, and refetching could overwrite cache with stale/empty data.\n const tableHasNewRows =\n currentSchemasSet.size > 0 &&\n tableDataSchemasSet.size > 0 &&\n [...tableDataSchemasSet].some((id) => !currentSchemasSet.has(id))\n\n // A listed schema whose rows are gone was deleted (Schema.destroy): refetch so it leaves.\n const tableLostRows = [...currentSchemasSet].some((id) => !tableDataSchemasSet.has(id))\n\n if (!setsAreEqual && (tableHasNewRows || tableLostRows)) {\n queryClient.invalidateQueries({ queryKey: SEED_SCHEMAS_QUERY_KEY })\n }\n }, [isClientReady, schemasTableData, queryClient])\n\n return {\n schemas,\n isLoading,\n error: queryError as Error | null,\n }\n}\n\nexport const useCreateSchema = () => {\n const { run, isLoading, error, resetError } = useCreateState('Failed to create schema')\n\n const createSchema = useCallback(\n (schemaName: string) =>\n run(() => Schema.create(schemaName, { waitForReady: false }) as Schema),\n [run],\n )\n\n return {\n createSchema,\n isLoading,\n error,\n resetError,\n }\n}\n\nexport type UseDestroySchemaReturn = {\n destroy: (schema: Schema) => Promise<void>\n isLoading: boolean\n error: Error | null\n resetError: () => void\n}\n\nexport const useDestroySchema = (): UseDestroySchemaReturn => {\n const [isLoading, setIsLoading] = useState(false)\n const [error, setError] = useState<Error | null>(null)\n\n // Loading state is tracked here: the schema's service doesn't record destroy progress, and\n // destroy() stops the service, often within a few microtasks.\n const destroy = useCallback(async (schema: Schema) => {\n if (!schema) return\n setError(null)\n setIsLoading(true)\n try {\n await schema.destroy()\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)))\n throw err\n } finally {\n setIsLoading(false)\n }\n }, [])\n\n const resetError = useCallback(() => setError(null), [])\n\n return {\n destroy,\n isLoading,\n error,\n resetError,\n }\n}\n\n/**\n * One Schema instance per stored schema name, each at its latest version (despite the name, not\n * one per version). Instances are released on unmount; ones other hooks also use keep running.\n */\nexport const useAllSchemaVersions = () => {\n const [schemaInstances, setSchemaInstances] = useState<Schema[] | undefined | null>()\n const schemaInstancesRef = useRef<Map<string, Schema>>(new Map())\n const isClientReady = useIsClientReady()\n\n const fetchSchemas = useCallback(async () => {\n if (!isClientReady) {\n return\n }\n\n try {\n // Use DB-first approach: load all schemas from database\n // This will also import any missing schemas from files\n const allSchemasData = await loadAllSchemasFromDb()\n\n // Extract unique schema names\n const schemaNames = new Set<string>()\n for (const schemaData of allSchemasData) {\n const schemaName = schemaData.schema.metadata?.name\n if (schemaName) {\n schemaNames.add(schemaName)\n }\n }\n\n // Create/update Schema instances\n const currentInstances = new Map<string, Schema>()\n \n for (const schemaName of schemaNames) {\n // Reuse existing instance if it exists\n if (schemaInstancesRef.current.has(schemaName)) {\n const existingInstance = schemaInstancesRef.current.get(schemaName)!\n currentInstances.set(schemaName, existingInstance)\n } else {\n // Create new instance\n const schema = Schema.create(schemaName, {\n waitForReady: false,\n }) as Schema\n currentInstances.set(schemaName, schema)\n }\n }\n\n // Cleanup instances that are no longer needed\n for (const [name, instance] of schemaInstancesRef.current.entries()) {\n if (!schemaNames.has(name)) {\n instance.unload()\n }\n }\n\n // Update ref and state\n schemaInstancesRef.current = currentInstances\n setSchemaInstances(Array.from(currentInstances.values()))\n } catch (error) {\n logger('Error fetching all schema versions from database:', error)\n setSchemaInstances(null)\n }\n }, [isClientReady])\n\n useEffect(() => {\n if (!isClientReady) {\n return\n }\n fetchSchemas()\n }, [isClientReady, fetchSchemas])\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n schemaInstancesRef.current.forEach((instance) => {\n instance.unload()\n })\n schemaInstancesRef.current.clear()\n }\n }, [])\n\n return schemaInstances\n}\n\ntype SchemaSnapshot = SnapshotFrom<typeof schemaMachine>\n\n\n/**\n * Hook to get the internal Seed Protocol schema (SDK-only schema)\n * This schema is managed by the SDK and should not be edited by app developers\n * @returns Object with schema instance and schemaData (version, metadata, etc.)\n */\nexport const useSeedProtocolSchema = () => {\n return useSchema(SEED_PROTOCOL_SCHEMA_NAME)\n}","import { useMemo, useState, useEffect, useCallback, useRef } from 'react'\nimport { Model } from '@seedprotocol/sdk'\nimport { useIsClientReady } from './client'\nimport { useLiveQuery } from './liveQuery'\nimport { BaseDb } from '@seedprotocol/sdk'\nimport { models as modelsTable, modelSchemas, schemas as schemasTable } from '@seedprotocol/sdk'\nimport { eq, or } from 'drizzle-orm'\nimport { Subscription } from 'xstate'\nimport { useQuery } from '@tanstack/react-query'\nimport { useSeedQueryClient } from './queryClient'\nimport { useCreateState } from './useCreateState'\n\ntype UseModelsResult = {\n models: Model[]\n isLoading: boolean\n error: Error | null\n}\n\ntype UseModelsParams = string | null | undefined\n\ntype UseModels = (schemaId: UseModelsParams) => UseModelsResult\n\n/**\n * Hook to get all Model instances for a specific schema\n * Uses useLiveQuery to watch for changes in the models table and automatically\n * updates the returned Model instances when changes occur.\n * @param schemaId - The schema ID (schema file ID) or schema name to get models from\n * @returns Array of Model instances belonging to the schema\n */\nconst getModelsQueryKey = (schemaId: UseModelsParams) => ['seed', 'models', schemaId] as const\n\nexport const useModels: UseModels = (schemaId) => {\n const isClientReady = useIsClientReady()\n const queryClient = useSeedQueryClient()\n // Latest models-table rows for this schema (live query): the ground truth for what exists.\n const tableIdsRef = useRef<Set<string> | null>(null)\n\n const queryKey = useMemo(() => getModelsQueryKey(schemaId), [schemaId])\n\n const {\n data: models = [],\n isLoading,\n error: queryError,\n } = useQuery({\n queryKey,\n queryFn: async () => {\n // Use waitForReady: false so we return models as soon as they exist; waitForReady: true\n // filters to only idle models and can return [] while a model is in creatingProperties.\n const next = await Model.all(schemaId!, { waitForReady: false })\n // Model.all can briefly return [] while models load. Keep the previous list only while the\n // table still has rows; once it is empty (all deleted), [] is the answer.\n const prev = queryClient.getQueryData<Model[]>(queryKey)\n const tableIds = tableIdsRef.current\n if (next.length === 0 && prev && prev.length > 0 && (!tableIds || tableIds.size > 0)) {\n return prev\n }\n return next\n },\n enabled: isClientReady && !!schemaId,\n }, queryClient)\n const displayModels = schemaId ? models : []\n const modelsRef = useRef<Model[]>(displayModels)\n modelsRef.current = displayModels\n\n // When a model is created, writeModelToDb posts to this channel; live query over join often doesn't re-run\n useEffect(() => {\n if (!schemaId || typeof BroadcastChannel === 'undefined') return\n const ch = new BroadcastChannel('seed-models-invalidate')\n const onMessage = (event: MessageEvent<{ schemaName?: string; schemaFileId?: string }>) => {\n const { schemaName, schemaFileId } = event.data || {}\n if (schemaId === schemaName || schemaId === schemaFileId) {\n queryClient.invalidateQueries({ queryKey })\n queryClient.refetchQueries({ queryKey })\n }\n }\n ch.addEventListener('message', onMessage)\n return () => {\n ch.removeEventListener('message', onMessage)\n ch.close()\n }\n }, [schemaId, queryClient, queryKey])\n\n // Stabilize query reference: only recreate when (schemaId, isClientReady) change, not when db reference changes.\n // This keeps the same liveQuery observable/subscription alive so effects can deliver updates when a new model is added.\n const stableModelsQueryKeyRef = useRef<{ schemaId: string; ready: boolean } | null>(null)\n const stableModelsQueryRef = useRef<ReturnType<typeof buildModelsQuery> | null>(null)\n function buildModelsQuery() {\n const currentDb = BaseDb.getAppDb()\n if (!currentDb || !schemaId) return null\n return currentDb\n .select({\n modelFileId: modelsTable.schemaFileId,\n modelName: modelsTable.name,\n })\n .from(schemasTable)\n .innerJoin(modelSchemas, eq(schemasTable.id, modelSchemas.schemaId))\n .innerJoin(modelsTable, eq(modelSchemas.modelId, modelsTable.id))\n .where(\n or(\n eq(schemasTable.schemaFileId, schemaId),\n eq(schemasTable.name, schemaId)\n )\n )\n }\n const modelsQuery = useMemo(() => {\n if (!schemaId || !isClientReady) return null\n const key = { schemaId, ready: isClientReady }\n const prevKey = stableModelsQueryKeyRef.current\n if (\n prevKey &&\n prevKey.schemaId === key.schemaId &&\n prevKey.ready === key.ready &&\n stableModelsQueryRef.current !== null\n ) {\n return stableModelsQueryRef.current\n }\n const q = buildModelsQuery()\n if (!q) return null\n stableModelsQueryKeyRef.current = key\n stableModelsQueryRef.current = q\n return q\n }, [schemaId, isClientReady])\n\n const modelsTableData = useLiveQuery<{ modelFileId: string | null; modelName: string }>(modelsQuery)\n\n useEffect(() => {\n if (!isClientReady || !modelsTableData || !schemaId) return\n\n const currentModelsSet = new Set<string>()\n for (const model of modelsRef.current) {\n const modelFileId = model.id || (model as any).modelFileId\n if (modelFileId) currentModelsSet.add(modelFileId)\n else if (model.modelName) currentModelsSet.add(model.modelName)\n }\n\n const tableDataModelsSet = new Set<string>()\n for (const dbModel of modelsTableData) {\n if (dbModel.modelFileId) tableDataModelsSet.add(dbModel.modelFileId)\n else if (dbModel.modelName) tableDataModelsSet.add(dbModel.modelName)\n }\n tableIdsRef.current = tableDataModelsSet\n\n const setsAreEqual =\n currentModelsSet.size === tableDataModelsSet.size &&\n [...currentModelsSet].every((id) => tableDataModelsSet.has(id))\n\n // Refetch on any difference: new rows, and deleted ones (which used to leave the list stale).\n if (!setsAreEqual) {\n queryClient.invalidateQueries({ queryKey })\n }\n }, [isClientReady, modelsTableData, schemaId, queryClient, queryKey])\n\n return {\n models: displayModels,\n isLoading,\n error: queryError as Error | null,\n }\n}\n\ntype UseModelResult = {\n model: Model | undefined\n isLoading: boolean\n error: Error | null\n}\n\n/**\n * Hook to get a specific Model instance\n * Can be called in two ways:\n * 1. With schemaId and modelName: useModel(schemaId, modelName)\n * 2. With modelId: useModel(modelId)\n * \n * @param schemaIdOrModelId - The schema ID (schema file ID) OR the model ID (modelFileId)\n * @param modelName - The name of the model to retrieve (required if first param is schemaId)\n * @returns Object with model, isLoading, and error\n */\nexport const useModel = (\n schemaIdOrModelId: string | null | undefined,\n modelName?: string | null | undefined\n): UseModelResult => {\n const isClientReady = useIsClientReady()\n const [model, setModel] = useState<Model | undefined>(undefined)\n const [isLoading, setIsLoading] = useState(false)\n const [error, setError] = useState<Error | null>(null)\n const subscriptionRef = useRef<Subscription | undefined>(undefined)\n const [, setVersion] = useState(0) // Version counter to force re-renders\n\n // If modelName is provided, treat first param as schemaId\n // Otherwise, treat first param as modelId\n const isModelIdLookup = modelName === undefined || modelName === null\n\n\n // Lookup model by ID if needed\n useEffect(() => {\n if (!isClientReady || !isModelIdLookup || !schemaIdOrModelId) {\n setModel(undefined)\n setIsLoading(false)\n setError(null)\n return\n }\n\n const lookupModelById = async () => {\n try {\n setIsLoading(true)\n setError(null)\n // Use Model.createById which handles cache + DB lookup\n const foundModel = await Model.createById(schemaIdOrModelId, { hold: false })\n setModel(foundModel || undefined)\n setIsLoading(false)\n setError(null)\n } catch (error) {\n console.error('[useModel] Error looking up model by ID:', error)\n setModel(undefined)\n setIsLoading(false)\n setError(error as Error)\n }\n }\n\n lookupModelById()\n }, [isClientReady, isModelIdLookup, schemaIdOrModelId])\n\n // Always call useModels (with null for an id lookup): hooks must run in the same order on\n // every render, and returning early before it crashed when modelName switched null <-> string.\n const { models: modelsList, isLoading: modelsLoading, error: modelsError } = useModels(\n isModelIdLookup ? null : schemaIdOrModelId,\n )\n const foundModel = useMemo(() => {\n if (isModelIdLookup || !modelName) {\n return undefined\n }\n // Try both modelName property and name getter for compatibility\n return modelsList.find((m: Model) => {\n const mName = m.modelName ?? (m as any).name\n return mName === modelName\n })\n }, [isModelIdLookup, modelsList, modelName])\n\n const activeModel = isModelIdLookup ? model : foundModel\n\n // Re-render on the model's service changes\n useEffect(() => {\n subscriptionRef.current?.unsubscribe()\n subscriptionRef.current = undefined\n if (!activeModel) return\n\n subscriptionRef.current = activeModel.getService().subscribe(() => {\n setVersion((prev) => prev + 1)\n })\n\n return () => {\n subscriptionRef.current?.unsubscribe()\n subscriptionRef.current = undefined\n }\n }, [activeModel])\n\n if (isModelIdLookup) {\n return {\n model,\n isLoading,\n error,\n }\n }\n return {\n model: foundModel,\n isLoading: modelsLoading,\n error: modelsError,\n }\n}\n\nexport type UseCreateModelOptions = {\n modelFileId?: string\n properties?: { [propertyName: string]: any }\n}\n\nexport type UseCreateModelReturn = {\n create: (\n schemaName: string,\n modelName: string,\n options?: UseCreateModelOptions\n ) => Model\n isLoading: boolean\n error: Error | null\n resetError: () => void\n}\n\nexport const useCreateModel = (): UseCreateModelReturn => {\n const { run, isLoading, error, resetError } = useCreateState('Failed to create model')\n\n const create = useCallback(\n (schemaName: string, modelName: string, options?: UseCreateModelOptions): Model =>\n run(\n () =>\n Model.create(modelName, schemaName, {\n ...options,\n waitForReady: false,\n }) as Model,\n ),\n [run],\n )\n\n return {\n create,\n isLoading,\n error,\n resetError,\n }\n}\n\nexport type UseDestroyModelReturn = {\n destroy: (model: Model) => Promise<void>\n isLoading: boolean\n error: Error | null\n resetError: () => void\n}\n\nexport const useDestroyModel = (): UseDestroyModelReturn => {\n const [isLoading, setIsLoading] = useState(false)\n const [error, setError] = useState<Error | null>(null)\n\n // Loading state is tracked here: the model's service doesn't record destroy progress, and\n // destroy() stops the service, often within a few microtasks.\n const destroy = useCallback(async (model: Model) => {\n if (!model) return\n setError(null)\n setIsLoading(true)\n try {\n await model.destroy()\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)))\n throw err\n } finally {\n setIsLoading(false)\n }\n }, [])\n\n const resetError = useCallback(() => setError(null), [])\n\n return {\n destroy,\n isLoading,\n error,\n resetError,\n }\n}\n","import { useModel } from \"./model\"\nimport { useCallback, useEffect, useRef, useState, useMemo } from \"react\"\nimport { flushSync } from \"react-dom\"\nimport { debounce } from \"lodash-es\"\nimport { getPropertySchema, ModelProperty } from '@seedprotocol/sdk'\nimport { useIsClientReady } from \"./client\"\nimport { Subscription } from \"xstate\"\nimport { useLiveQuery } from \"./liveQuery\"\nimport { BaseDb } from '@seedprotocol/sdk'\nimport { properties as propertiesTable, schemas as schemasTable } from '@seedprotocol/sdk'\nimport { eq, or } from \"drizzle-orm\"\nimport { Model } from '@seedprotocol/sdk'\nimport { useQuery } from \"@tanstack/react-query\"\nimport { useSeedQueryClient } from \"./queryClient\"\nimport { useCreateState } from \"./useCreateState\"\nimport { getSchemaNameFromId } from '@seedprotocol/sdk'\n\n\ntype UseModelPropertiesResult = {\n modelProperties: ModelProperty[]\n isLoading: boolean\n error: Error | null\n}\n\n/**\n * Hook to get all ModelProperty instances for a specific model\n * Can be called in two ways:\n * 1. With schemaId and modelName: useModelProperties(schemaId, modelName)\n * 2. With modelId: useModelProperties(modelId)\n * \n * Uses useLiveQuery to watch for changes in the properties table and automatically\n * updates the returned ModelProperty instances when changes occur.\n * \n * @param schemaIdOrModelId - The schema ID (schema file ID) OR the model ID (modelFileId)\n * @param modelName - The name of the model to get properties from (required if first param is schemaId)\n * @returns Object with modelProperties array, isLoading, and error\n */\n/**\n * A schema name for a schemaId that may be a schema name, a schema file id, or \"name-version\".\n * An exact stored name or file id wins, so hyphenated names (\"my-app\") aren't cut.\n */\nasync function resolveSchemaName(schemaId: string): Promise<string> {\n const db = BaseDb.getAppDb()\n if (db) {\n const rows = await db\n .select({ name: schemasTable.name })\n .from(schemasTable)\n .where(or(eq(schemasTable.name, schemaId), eq(schemasTable.schemaFileId, schemaId)))\n .limit(1)\n if (rows[0]?.name) return rows[0].name\n }\n return getSchemaNameFromId(schemaId) ?? schemaId\n}\n\nexport const useModelProperties = (\n schemaIdOrModelId: string | null | undefined,\n modelName?: string | null | undefined\n): UseModelPropertiesResult => {\n // Use useModel to handle both lookup patterns (by ID or by schemaId + modelName)\n const { model } = useModel(schemaIdOrModelId, modelName)\n \n const isClientReady = useIsClientReady()\n const queryClient = useSeedQueryClient()\n\n // The model's database id. It can arrive after the model is first seen (the row is written later),\n // so follow the model's snapshot: memoizing it once left the properties live query unbuilt.\n const dbModelId = useModelDbId(model)\n\n const modelId = model?.id\n const modelPropertiesQueryKey = useMemo(\n () => ['seed', 'modelProperties', modelId ?? ''] as const,\n [modelId],\n )\n\n const {\n data: modelProperties = [],\n isLoading,\n error: queryError,\n } = useQuery({\n queryKey: modelPropertiesQueryKey,\n queryFn: () => ModelProperty.all(modelId!, { waitForReady: true }),\n enabled: isClientReady && !!modelId,\n }, queryClient)\n\n const db = isClientReady ? BaseDb.getAppDb() : null\n const propertiesQuery = useMemo(() => {\n if (!db || !dbModelId) return null\n return db\n .select({\n id: propertiesTable.id,\n name: propertiesTable.name,\n dataType: propertiesTable.dataType,\n schemaFileId: propertiesTable.schemaFileId,\n })\n .from(propertiesTable)\n .where(eq(propertiesTable.modelId, dbModelId))\n }, [db, isClientReady, dbModelId])\n const propertiesTableData = useLiveQuery<{ id: number; name: string; dataType: string; schemaFileId: string | null }>(propertiesQuery)\n\n const modelPropertiesRef = useRef<ModelProperty[]>([])\n modelPropertiesRef.current = modelProperties\n\n // Fallback: when we have modelId but query returned [] (e.g. properties not in DB yet or\n // propertiesTableData is undefined because model._dbId isn't set yet), schedule refetches\n // so we pick up properties after they're written.\n useEffect(() => {\n if (!modelId || modelProperties.length > 0 || !queryClient || !modelPropertiesQueryKey) return\n const delays = [400, 1200, 2500]\n const timers = delays.map((ms) =>\n setTimeout(() => {\n queryClient.invalidateQueries({ queryKey: modelPropertiesQueryKey })\n }, ms)\n )\n return () => timers.forEach((t) => clearTimeout(t))\n }, [modelId, modelProperties.length, queryClient, modelPropertiesQueryKey])\n\n useEffect(() => {\n if (!isClientReady || !model?.id || !propertiesTableData || !modelPropertiesQueryKey) return\n\n const currentPropertiesSet = new Set<string>()\n for (const prop of modelPropertiesRef.current) {\n const context = (prop as any)._getSnapshotContext()\n const propertyFileId = context?.id\n if (propertyFileId) currentPropertiesSet.add(propertyFileId)\n else if (prop.name) currentPropertiesSet.add(prop.name)\n }\n\n const tableDataPropertiesSet = new Set<string>()\n for (const dbProperty of propertiesTableData) {\n if (dbProperty.schemaFileId) tableDataPropertiesSet.add(dbProperty.schemaFileId)\n else if (dbProperty.name) tableDataPropertiesSet.add(dbProperty.name)\n }\n\n const setsAreEqual =\n currentPropertiesSet.size === tableDataPropertiesSet.size &&\n (currentPropertiesSet.size === 0 ||\n [...currentPropertiesSet].every((id) => tableDataPropertiesSet.has(id)))\n\n // Invalidate when cached list is out of sync with the table: either we have stale cached data\n // (currentPropertiesSet.size > 0) or the table has new rows we don't have yet (tableDataPropertiesSet.size > 0).\n // The latter handles the case where the initial query returned [] before properties were written.\n const shouldInvalidate =\n !setsAreEqual && (currentPropertiesSet.size > 0 || tableDataPropertiesSet.size > 0)\n\n if (shouldInvalidate) {\n queryClient.invalidateQueries({ queryKey: modelPropertiesQueryKey })\n }\n }, [isClientReady, propertiesTableData, model?.id, queryClient, modelPropertiesQueryKey])\n\n // When we have cached modelProperties, do not report isLoading: true even if the query\n // is refetching (e.g. after useModels refetch causes modelId to change briefly). UX expects\n // isLoading false once we have shown data.\n const effectiveIsLoading = isLoading && modelProperties.length === 0\n\n return {\n modelProperties,\n isLoading: effectiveIsLoading,\n error: queryError as Error | null,\n }\n}\n\nconst LOOKUP_RETRY_DELAYS_MS = [400, 1200, 2500]\n\nconst readModelDbId = (model: Model | undefined | null): number | undefined => {\n if (!model) return undefined\n try {\n return (model as any)._getSnapshotContext()._dbId as number | undefined\n } catch {\n return undefined\n }\n}\n\n/** The model's `_dbId`, updated when the model's actor sets it. */\nconst useModelDbId = (model: Model | undefined | null): number | undefined => {\n const [dbModelId, setDbModelId] = useState(() => readModelDbId(model))\n useEffect(() => {\n setDbModelId(readModelDbId(model))\n if (!model) return\n const subscription = model.getService().subscribe(() => setDbModelId(readModelDbId(model)))\n return () => subscription.unsubscribe()\n }, [model])\n return dbModelId\n}\n\n/**\n * Helper function to get property schema by modelFileId and propertyName\n */\nconst getPropertySchemaByModelFileId = async (\n modelFileId: string,\n propertyName: string\n): Promise<ReturnType<typeof getPropertySchema>> => {\n // Get model by modelFileId\n const model = await Model.createById(modelFileId, { hold: false })\n if (!model) {\n return undefined\n }\n\n // Get modelName from model\n const modelName = model.modelName ?? (model as any).name\n if (!modelName) {\n return undefined\n }\n\n // Use existing getPropertySchema function, pinned to this model (names are only unique per schema)\n return getPropertySchema(modelName, propertyName, { modelFileId, schemaName: model.schemaName })\n}\n\n/**\n * Hook to get a specific ModelProperty instance\n * Can be called in three ways:\n * 1. With propertyFileId: useModelProperty(propertyFileId)\n * 2. With modelFileId and propertyName: useModelProperty(modelFileId, propertyName)\n * 3. With schemaId, modelName, and propertyName: useModelProperty(schemaId, modelName, propertyName)\n * \n * @overload\n * @param propertyFileId - The property file ID (schemaFileId)\n * @returns Object with modelProperty, isLoading, and error\n * \n * @overload\n * @param modelFileId - The model file ID (modelFileId)\n * @param propertyName - The name of the property\n * @returns Object with modelProperty, isLoading, and error\n * \n * @overload\n * @param schemaId - The schema ID (schema file ID)\n * @param modelName - The name of the model\n * @param propertyName - The name of the property\n * @returns Object with modelProperty, isLoading, and error\n */\nexport function useModelProperty(propertyFileId: string | null | undefined): {\n modelProperty: ModelProperty | undefined\n isLoading: boolean\n error: Error | null\n}\nexport function useModelProperty(\n modelFileId: string | null | undefined,\n propertyName: string | null | undefined\n): {\n modelProperty: ModelProperty | undefined\n isLoading: boolean\n error: Error | null\n}\nexport function useModelProperty(\n schemaId: string | null | undefined,\n modelName: string | null | undefined,\n propertyName: string | null | undefined\n): {\n modelProperty: ModelProperty | undefined\n isLoading: boolean\n error: Error | null\n}\nexport function useModelProperty(...args: Array<string | null | undefined>) {\n const [arg1, arg2, arg3] = args\n // The lookup form follows the number of arguments, so a null or undefined argument (not ready\n // yet) waits instead of switching to a shorter form: (schemaId, null, null) used to look the\n // schema id up as a property file id.\n const argCount = Math.min(args.length, 3)\n\n // Determine initial loading state - start loading if we have valid parameters\n const initialLoadingState = useMemo(() => {\n if (argCount === 3) return !!(arg1 && arg2 && arg3)\n if (argCount === 2) return !!(arg1 && arg2)\n return !!arg1\n }, [argCount, arg1, arg2, arg3])\n\n const [modelProperty, setModelProperty] = useState<ModelProperty | undefined>(undefined)\n const [isLoading, setIsLoading] = useState(initialLoadingState)\n const [error, setError] = useState<Error | null>(null)\n const subscriptionRef = useRef<Subscription | undefined>(undefined)\n\n const isClientReady = useIsClientReady()\n\n // Determine which lookup mode we're in based on arguments\n const lookupMode = useMemo(() => {\n if (argCount === 3) {\n return { type: 'schemaId' as const, schemaId: arg1, modelName: arg2, propertyName: arg3 }\n }\n if (argCount === 2) {\n return { type: 'modelFileId' as const, modelFileId: arg1, propertyName: arg2 }\n }\n return { type: 'propertyFileId' as const, propertyFileId: arg1 }\n }, [argCount, arg1, arg2, arg3])\n\n // Determine if we should be loading based on parameters\n const shouldLoad = useMemo(() => {\n if (!isClientReady) return false\n if (lookupMode.type === 'propertyFileId') {\n return !!lookupMode.propertyFileId\n } else if (lookupMode.type === 'modelFileId') {\n return !!(lookupMode.modelFileId && lookupMode.propertyName)\n } else {\n return !!(lookupMode.schemaId && lookupMode.modelName && lookupMode.propertyName)\n }\n }, [isClientReady, lookupMode])\n\n /** `quiet`: a background retry; leaves isLoading alone. */\n const updateModelProperty = useCallback(async ({ quiet = false }: { quiet?: boolean } = {}) => {\n if (!isClientReady) {\n setModelProperty(undefined)\n setIsLoading(false)\n setError(null)\n return\n }\n\n let propertyData: Awaited<ReturnType<typeof getPropertySchema>> | undefined\n let resolvedModelName: string | undefined\n\n try {\n if (!quiet) {\n setIsLoading(true)\n setError(null)\n }\n\n if (lookupMode.type === 'propertyFileId') {\n if (!lookupMode.propertyFileId) {\n setModelProperty(undefined)\n setIsLoading(false)\n setError(null)\n return\n }\n\n // Use ModelProperty.createById for propertyFileId lookup\n const foundProperty = await ModelProperty.createById(lookupMode.propertyFileId)\n if (foundProperty) {\n setModelProperty(foundProperty)\n setIsLoading(false)\n setError(null)\n } else {\n setModelProperty(undefined)\n setIsLoading(false)\n setError(null)\n }\n return\n } else if (lookupMode.type === 'modelFileId') {\n if (!lookupMode.modelFileId || !lookupMode.propertyName) {\n setModelProperty(undefined)\n setIsLoading(false)\n setError(null)\n return\n }\n\n // Get property schema by modelFileId and propertyName\n // This function already gets the model and resolves modelName\n propertyData = await getPropertySchemaByModelFileId(lookupMode.modelFileId, lookupMode.propertyName)\n \n // Get modelName from model (needed for ModelProperty.create)\n const model = await Model.createById(lookupMode.modelFileId, { hold: false })\n resolvedModelName = model?.modelName ?? (model as any)?.name\n } else {\n // lookupMode.type === 'schemaId'\n if (!lookupMode.schemaId || !lookupMode.modelName || !lookupMode.propertyName) {\n setModelProperty(undefined)\n setIsLoading(false)\n setError(null)\n return\n }\n\n // Use existing getPropertySchema for schemaId + modelName + propertyName\n // schemaId may be a schema file id or a schema name; scope the lookup to that schema\n const schemaName = await resolveSchemaName(lookupMode.schemaId)\n propertyData = await getPropertySchema(lookupMode.modelName, lookupMode.propertyName, { schemaName })\n resolvedModelName = lookupMode.modelName\n }\n\n if (propertyData && resolvedModelName) {\n const createdProperty = ModelProperty.create(\n { ...propertyData, modelName: resolvedModelName },\n { waitForReady: false }\n )\n const resolvedProperty = createdProperty instanceof Promise ? await createdProperty : createdProperty\n flushSync(() => {\n setModelProperty(resolvedProperty)\n setIsLoading(false)\n setError(null)\n })\n } else {\n setModelProperty(undefined)\n setIsLoading(false)\n setError(null)\n }\n } catch (error) {\n console.error('[useModelProperty] Error updating model property:', error)\n setModelProperty(undefined)\n setIsLoading(false)\n setError(error as Error)\n }\n }, [isClientReady, lookupMode.type, lookupMode.propertyFileId, lookupMode.modelFileId, lookupMode.propertyName, lookupMode.schemaId, lookupMode.modelName])\n\n // Fetch/refetch when lookup parameters change or client becomes ready\n useEffect(() => {\n if (!shouldLoad) {\n setModelProperty(undefined)\n setIsLoading(false)\n setError(null)\n return\n }\n updateModelProperty()\n }, [shouldLoad, updateModelProperty])\n\n // A schemaId/modelFileId lookup reads the model's properties, which can still be loading (or the\n // property not yet written) when the hook mounts. Nothing would ask again, so retry a few times\n // while it finds nothing, as useModelProperties does (skipped while a lookup is running, and once\n // one found the property or failed).\n const skipLookupRetryRef = useRef(false)\n skipLookupRetryRef.current = isLoading || !!modelProperty || !!error\n useEffect(() => {\n if (!shouldLoad || lookupMode.type === 'propertyFileId') return\n const timers = LOOKUP_RETRY_DELAYS_MS.map((ms) =>\n setTimeout(() => {\n if (!skipLookupRetryRef.current) updateModelProperty({ quiet: true })\n }, ms),\n )\n return () => timers.forEach((t) => clearTimeout(t))\n }, [shouldLoad, lookupMode.type, updateModelProperty])\n\n // Subscribe to service changes when modelProperty is available.\n // Skip subscription for schemaId/modelFileId lookups where we created the instance locally—\n // refetching on every snapshot would set loading and can race with the initial render.\n // Debounce to reduce refetch frequency and avoid updateContext/refetch loops.\n const shouldSubscribe = lookupMode.type === 'propertyFileId'\n useEffect(() => {\n if (!modelProperty || !shouldSubscribe) {\n return\n }\n\n // Clean up previous subscription\n subscriptionRef.current?.unsubscribe()\n\n const debouncedUpdate = debounce(updateModelProperty, 100)\n\n // Subscribe to service changes\n const subscription = modelProperty.getService().subscribe(() => {\n debouncedUpdate()\n })\n\n subscriptionRef.current = subscription\n\n return () => {\n debouncedUpdate.cancel()\n subscriptionRef.current?.unsubscribe()\n subscriptionRef.current = undefined\n }\n }, [modelProperty, updateModelProperty, shouldSubscribe])\n\n return {\n modelProperty,\n isLoading,\n error,\n }\n}\n\nexport type UseCreateModelPropertyOptions = {\n name: string\n dataType: string\n [key: string]: any\n}\n\nexport type UseCreateModelPropertyReturn = {\n create: (\n schemaId: string,\n modelName: string,\n property: UseCreateModelPropertyOptions\n ) => Promise<ModelProperty>\n isLoading: boolean\n error: Error | null\n resetError: () => void\n}\n\n/**\n * Hook to create a ModelProperty with loading and error state.\n * create(schemaId, modelName, property) creates a new property on the model. schemaId is a schema\n * name, schema file id or \"name-version\"; it is resolved to a stored schema name first, so create\n * returns a promise.\n */\nexport const useCreateModelProperty = (): UseCreateModelPropertyReturn => {\n const { runAsync, isLoading, error, resetError } = useCreateState(\n 'Failed to create model property',\n )\n\n const create = useCallback(\n (\n schemaId: string,\n modelName: string,\n property: UseCreateModelPropertyOptions\n ): Promise<ModelProperty> =>\n runAsync(async () => {\n if (!modelName || !property.name || !property.dataType) {\n throw new Error('modelName, property name and dataType are required')\n }\n const schemaName = await resolveSchemaName(schemaId)\n return ModelProperty.create(\n { ...property, modelName } as Parameters<typeof ModelProperty.create>[0],\n { waitForReady: false, schemaName }\n ) as ModelProperty\n }),\n [runAsync]\n )\n\n return {\n create,\n isLoading,\n error,\n resetError,\n }\n}\n\nexport type UseDestroyModelPropertyReturn = {\n destroy: (modelProperty: ModelProperty) => Promise<void>\n isLoading: boolean\n error: Error | null\n resetError: () => void\n}\n\nexport const useDestroyModelProperty = (): UseDestroyModelPropertyReturn => {\n const [isLoading, setIsLoading] = useState(false)\n const [error, setError] = useState<Error | null>(null)\n\n // Loading state is tracked here: the model property's service doesn't record destroy progress, and\n // destroy() stops the service, often within a few microtasks.\n const destroy = useCallback(async (modelProperty: ModelProperty) => {\n if (!modelProperty) return\n setError(null)\n setIsLoading(true)\n try {\n await modelProperty.destroy()\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)))\n throw err\n } finally {\n setIsLoading(false)\n }\n }, [])\n\n const resetError = useCallback(() => setError(null), [])\n\n return {\n destroy,\n isLoading,\n error,\n resetError,\n }\n}\n","import { useCallback, useState } from 'react'\nimport { Item } from '@seedprotocol/sdk'\n\nexport type UseDeleteItemReturn = {\n deleteItem: (item: Item<any>) => Promise<void>\n isLoading: boolean\n error: Error | null\n resetError: () => void\n}\n\nexport const useDeleteItem = (): UseDeleteItemReturn => {\n const [isLoading, setIsLoading] = useState(false)\n const [error, setError] = useState<Error | null>(null)\n\n // Loading state is tracked here: the item's service doesn't record destroy progress, and\n // destroy() stops the service, often within a few microtasks.\n const destroy = useCallback(async (item: Item<any>) => {\n if (!item) return\n setError(null)\n setIsLoading(true)\n try {\n await item.destroy()\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)))\n throw err\n } finally {\n setIsLoading(false)\n }\n }, [])\n\n const resetError = useCallback(() => setError(null), [])\n\n return {\n deleteItem: destroy,\n isLoading,\n error,\n resetError,\n }\n}\n","import { useEffect } from 'react'\nimport { useQuery } from '@tanstack/react-query'\nimport { useSeedQueryClient } from './queryClient'\nimport { BaseFileManager } from '@seedprotocol/sdk'\nimport { useIsClientReady } from './client'\nimport { eventEmitter, FILES_CHANGED_EVENT } from '@seedprotocol/sdk'\n\nconst IMAGE_FILES_QUERY_KEY = ['seed', 'imageFiles'] as const\n\n/**\n * Returns an up-to-date list of image filenames stored in the file system (OPFS in browser).\n * Automatically refetches when images are saved (file-saved) or after bulk downloads (fs.downloadAll.success).\n *\n * Use within SeedProvider, after client.init().\n *\n * @example\n * ```tsx\n * const { imageFiles, isLoading, error, refetch } = useImageFiles()\n * // imageFiles: ['photo.jpg', 'cover.png']\n * ```\n */\nexport function useImageFiles() {\n const isClientReady = useIsClientReady()\n const queryClient = useSeedQueryClient()\n\n const {\n data: imageFiles = [],\n isLoading,\n error,\n refetch,\n } = useQuery({\n queryKey: IMAGE_FILES_QUERY_KEY,\n queryFn: () => BaseFileManager.listImageFiles(),\n enabled: isClientReady,\n }, queryClient)\n\n useEffect(() => {\n const fileSavedHandler = (filePath: string) => {\n if (filePath.includes('/images/')) {\n queryClient.invalidateQueries({ queryKey: IMAGE_FILES_QUERY_KEY })\n }\n }\n const downloadSuccessHandler = () => {\n queryClient.invalidateQueries({ queryKey: IMAGE_FILES_QUERY_KEY })\n }\n // Files written by workers or other tabs.\n const filesChangedHandler = (filePaths: string[]) => {\n if (filePaths.some((filePath) => filePath.includes('/images/'))) {\n queryClient.invalidateQueries({ queryKey: IMAGE_FILES_QUERY_KEY })\n }\n }\n eventEmitter.on('file-saved', fileSavedHandler)\n eventEmitter.on('fs.downloadAll.success', downloadSuccessHandler)\n eventEmitter.on(FILES_CHANGED_EVENT, filesChangedHandler)\n return () => {\n eventEmitter.off('file-saved', fileSavedHandler)\n eventEmitter.off('fs.downloadAll.success', downloadSuccessHandler)\n eventEmitter.off(FILES_CHANGED_EVENT, filesChangedHandler)\n }\n }, [queryClient])\n\n return {\n imageFiles,\n isLoading,\n error: error instanceof Error ? error : null,\n refetch,\n }\n}\n","import { useEffect, useMemo } from 'react'\nimport { useQuery } from '@tanstack/react-query'\nimport { useSeedQueryClient } from './queryClient'\nimport { BaseFileManager, eventEmitter, FILES_CHANGED_EVENT } from '@seedprotocol/sdk'\nimport { useIsClientReady } from './client'\n\nexport const FILES_QUERY_KEY_PREFIX = ['seed', 'files'] as const\n\n/**\n * Returns an up-to-date list of filenames stored in the given directory.\n * Automatically refetches when files are saved (file-saved) or after bulk downloads (fs.downloadAll.success).\n *\n * Use within SeedProvider, after client.init().\n *\n * @param dir - Directory name under the files root (e.g. 'files', 'images'). Default: 'files'.\n *\n * @example\n * ```tsx\n * const { files, isLoading, error, refetch } = useFiles('files')\n * // files: ['document.pdf', 'contract.docx']\n * ```\n */\nexport function useFiles(dir: string = 'files') {\n const isClientReady = useIsClientReady()\n const queryClient = useSeedQueryClient()\n\n const queryKey = useMemo(() => [...FILES_QUERY_KEY_PREFIX, dir] as const, [dir])\n\n const {\n data: files = [],\n isLoading,\n error,\n refetch,\n } = useQuery({\n queryKey,\n queryFn: () => BaseFileManager.listFiles(dir),\n enabled: isClientReady,\n }, queryClient)\n\n useEffect(() => {\n const fileSavedHandler = (filePath: string) => {\n if (filePath.includes(`/${dir}/`)) {\n queryClient.invalidateQueries({ queryKey })\n }\n }\n const downloadSuccessHandler = () => {\n queryClient.invalidateQueries({ queryKey })\n }\n // Files written by workers or other tabs.\n const filesChangedHandler = (filePaths: string[]) => {\n if (filePaths.some((filePath) => filePath.includes(`/${dir}/`))) {\n queryClient.invalidateQueries({ queryKey })\n }\n }\n eventEmitter.on('file-saved', fileSavedHandler)\n eventEmitter.on('fs.downloadAll.success', downloadSuccessHandler)\n eventEmitter.on(FILES_CHANGED_EVENT, filesChangedHandler)\n return () => {\n eventEmitter.off('file-saved', fileSavedHandler)\n eventEmitter.off('fs.downloadAll.success', downloadSuccessHandler)\n eventEmitter.off(FILES_CHANGED_EVENT, filesChangedHandler)\n }\n }, [queryClient, dir, queryKey])\n\n return {\n files,\n isLoading,\n error: error instanceof Error ? error : null,\n refetch,\n }\n}\n","import { useMemo } from 'react'\nimport { BaseDb, easSyncProcesses, type EasSyncProcessType } from '@seedprotocol/sdk'\nimport { desc, eq } from 'drizzle-orm'\nimport { useLiveQuery } from './liveQuery'\nimport { useIsClientReady } from './client'\n\nexport type EasSyncProcessRecord = EasSyncProcessType\n\n/** The app DB once the client is ready; before that BaseDb.getAppDb() throws \"Db not configured\". */\nfunction useAppDbWhenReady() {\n const isClientReady = useIsClientReady()\n return isClientReady ? BaseDb.getAppDb() : null\n}\n\nfunction useEasSyncProcessesQuery() {\n const db = useAppDbWhenReady()\n return useMemo(\n () =>\n db\n ? db.select().from(easSyncProcesses).orderBy(desc(easSyncProcesses.startedAt))\n : null,\n [db],\n )\n}\n\n/**\n * Single live subscription for `eas_sync_processes` ordered by `startedAt` descending plus a\n * derived count of rows whose status is not `in_progress`.\n */\nexport function useEasSyncProcessesState(): {\n records: EasSyncProcessRecord[] | undefined\n nonActiveCount: number | undefined\n} {\n const query = useEasSyncProcessesQuery()\n const records = useLiveQuery(query) as EasSyncProcessRecord[] | undefined\n const nonActiveCount = useMemo(\n () =>\n records === undefined ? undefined : records.filter((r) => r.status !== 'in_progress').length,\n [records],\n )\n return { records, nonActiveCount }\n}\n\nexport function useEasSyncProcesses(): EasSyncProcessRecord[] | undefined {\n const query = useEasSyncProcessesQuery()\n return useLiveQuery(query) as EasSyncProcessRecord[] | undefined\n}\n\nexport function useEasSyncProcessesNonActiveCount(): number | undefined {\n return useEasSyncProcessesState().nonActiveCount\n}\n\nexport function useEasSyncProcessById(\n id: number | undefined,\n): { record: EasSyncProcessRecord | null; isLoading: boolean } {\n const db = useAppDbWhenReady()\n const query = useMemo(\n () =>\n id != null && db\n ? db.select().from(easSyncProcesses).where(eq(easSyncProcesses.id, id)).limit(1)\n : null,\n [db, id],\n )\n const records = useLiveQuery(query)\n // Without an id there is nothing to load: not loading forever. With one, waiting for the client\n // counts as loading.\n const isLoading = id != null && (db == null || records === undefined)\n const record = records && records.length > 0 ? (records[0] as EasSyncProcessRecord) : null\n return { record, isLoading }\n}\n","import React, { type ReactNode, useMemo, useEffect, useRef } from 'react'\nimport { QueryClientProvider } from '@tanstack/react-query'\nimport type { QueryClient } from '@tanstack/react-query'\nimport { createSeedQueryClient } from './queryClient'\nimport { client, eventEmitter, EAS_SEED_DATA_SYNCED_TO_DB_EVENT } from '@seedprotocol/sdk'\nimport type { SeedConfig, SeedConstructorOptions } from '@seedprotocol/sdk'\nimport { SeedAddressRevisionProvider } from './SeedSessionContext'\n\nexport type SeedProviderProps = {\n children: ReactNode\n /**\n * Starts the Seed client with this config (`client.init`) when the provider mounts. Children\n * render straight away; the hooks report loading until the client is ready. Leave it out when you\n * call `client.init` yourself or start the client with `SeedClientGate`. Init runs again when\n * the config object changes, so define it outside the component.\n */\n config?: SeedConfig\n /** Addresses passed to `client.init` with `config`. See `SeedConstructorOptions.addresses`. */\n addresses?: SeedConstructorOptions['addresses']\n /**\n * Called when `client.init` fails, e.g. with `DatabaseNotPersistentError` (`DB_NOT_PERSISTENT`)\n * or `FileSystemLockedError` (`OPFS_LOCKED`). Without it the error is logged.\n */\n onInitError?: (err: unknown) => void\n /** Optional: use your own QueryClient. If not provided, a default client with Seed options is created. */\n queryClient?: QueryClient\n /** Optional: ref to receive the QueryClient instance (e.g. for tests). */\n queryClientRef?: React.MutableRefObject<QueryClient | null>\n}\n\ndeclare global {\n interface Window {\n __SEED_INVALIDATE_ITEM_PROPERTIES__?: ((canonicalId: string) => void) | null\n }\n}\n\n/** Module-level ref so invalidateItemPropertiesForItem works when test and app share the same bundle but not the same window (e.g. iframe). */\nlet invalidateItemPropertiesRef: ((canonicalId: string) => void | Promise<void>) | null = null\n\n/**\n * Invalidates and refetches the item-properties query for an item.\n * Call this after updating an ItemProperty (e.g. after save()) so useItemProperties\n * refetches and the UI updates. Returns a Promise that resolves when the refetch has completed (if available).\n */\nexport function invalidateItemPropertiesForItem(canonicalId: string): Promise<void> {\n const p1 = invalidateItemPropertiesRef?.(canonicalId)\n if (typeof window !== 'undefined' && window.__SEED_INVALIDATE_ITEM_PROPERTIES__) {\n window.__SEED_INVALIDATE_ITEM_PROPERTIES__(canonicalId)\n }\n return Promise.resolve(p1).then(() => {})\n}\n\nfunction SeedProviderEventSubscriber({ queryClient }: { queryClient: QueryClient }) {\n useEffect(() => {\n const invalidate = (canonicalId: string) => {\n const key: readonly [string, string, string] = ['seed', 'itemProperties', canonicalId]\n queryClient.invalidateQueries({ queryKey: key })\n return queryClient.refetchQueries({ queryKey: key })\n }\n invalidateItemPropertiesRef = invalidate\n if (typeof window !== 'undefined') {\n window.__SEED_INVALIDATE_ITEM_PROPERTIES__ = invalidate\n }\n const handler = (payload: { seedLocalId?: string; seedUid?: string }) => {\n const canonicalId = payload?.seedLocalId ?? payload?.seedUid\n if (canonicalId) {\n invalidate(canonicalId)\n }\n }\n const onEasSyncedToDb = () => {\n void queryClient.invalidateQueries({ queryKey: ['seed', 'items'], exact: false })\n }\n eventEmitter.on('itemProperty.saved', handler)\n eventEmitter.on(EAS_SEED_DATA_SYNCED_TO_DB_EVENT, onEasSyncedToDb)\n return () => {\n eventEmitter.off('itemProperty.saved', handler)\n eventEmitter.off(EAS_SEED_DATA_SYNCED_TO_DB_EVENT, onEasSyncedToDb)\n invalidateItemPropertiesRef = null\n if (typeof window !== 'undefined') {\n window.__SEED_INVALIDATE_ITEM_PROPERTIES__ = null\n }\n }\n }, [queryClient])\n return null\n}\n\nfunction SeedClientInit({ config, addresses, onInitError }: Pick<SeedProviderProps, 'addresses' | 'onInitError'> & { config: SeedConfig }) {\n // Refs, so inline callbacks and address arrays don't re-run init on every render.\n const onInitErrorRef = useRef(onInitError)\n onInitErrorRef.current = onInitError\n const addressesRef = useRef(addresses)\n addressesRef.current = addresses\n const addressesKey = JSON.stringify(addresses ?? null)\n\n useEffect(() => {\n const options: SeedConstructorOptions =\n addressesRef.current === undefined ? { config } : { config, addresses: addressesRef.current }\n client.init(options).catch((err: unknown) => {\n if (onInitErrorRef.current) {\n onInitErrorRef.current(err)\n } else {\n console.error('[SeedProvider] client init failed', err)\n }\n })\n }, [config, addressesKey])\n return null\n}\n\n/**\n * Provider that supplies a React Query client to Seed list hooks (useSchemas, useItems, useModels, etc.)\n * so results are cached and shared across components. Wrap your app (or the subtree that uses Seed hooks).\n *\n * - config prop: starts the client with `client.init({ config, addresses })`.\n * - No queryClient prop: uses an internal QueryClient with Seed defaults.\n * - queryClient prop: use your own client (e.g. merge getSeedQueryDefaultOptions when creating it).\n */\nexport function SeedProvider({\n children,\n config,\n addresses,\n onInitError,\n queryClient: queryClientProp,\n queryClientRef,\n}: SeedProviderProps) {\n const queryClient = useMemo(\n () => queryClientProp ?? createSeedQueryClient(),\n [queryClientProp]\n )\n if (queryClientRef) {\n queryClientRef.current = queryClient\n if (typeof window !== 'undefined') {\n const w = window as { __TEST_SEED_QUERY_CLIENT__?: QueryClient }\n w.__TEST_SEED_QUERY_CLIENT__ = queryClient\n try {\n if (window.parent && window.parent !== window) (window.parent as { __TEST_SEED_QUERY_CLIENT__?: QueryClient }).__TEST_SEED_QUERY_CLIENT__ = queryClient\n } catch {\n // cross-origin frame, ignore\n }\n }\n }\n return (\n <QueryClientProvider client={queryClient}>\n <SeedAddressRevisionProvider queryClient={queryClient}>\n <SeedProviderEventSubscriber queryClient={queryClient} />\n {config && <SeedClientInit config={config} addresses={addresses} onInitError={onInitError} />}\n {children}\n </SeedAddressRevisionProvider>\n </QueryClientProvider>\n )\n}\n","import React, { useEffect, useRef, type ReactNode } from 'react'\nimport { client } from '@seedprotocol/sdk'\nimport type { SeedConstructorOptions, SeedConfig, SchemaFileInput } from '@seedprotocol/sdk'\nimport { useIsClientReady } from './client'\n\n/** Schema input: path string or schema object. Merged into config.schema at init. */\ntype SchemaInput = string | SchemaFileInput\n\nfunction DefaultLoading() {\n return (\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n height: '100%',\n width: '100%',\n }}\n >\n Loading...\n </div>\n )\n}\n\nexport type SeedClientGateProps = {\n /** Seed client init options (config + addresses). Required. */\n initConfig: SeedConstructorOptions\n /** Optional canonical schema. When provided, merged into config.schema and loaded at init (no separate import). */\n schema?: SchemaInput\n /** Custom loading UI. Default: simple centered \"Loading...\" text. */\n loadingComponent?: ReactNode\n /** @deprecated Never called. Schema loads at init via config.schema, and a schema that fails to load fails init: handle it in `onInitError`. */\n onSchemaImportError?: (err: unknown) => void\n /**\n * Called when client init fails; children stay hidden behind the loading UI. A\n * `FileSystemLockedError` (`code === 'OPFS_LOCKED'`) means another tab is using the app's\n * storage: offer \"close other tabs / retry\", where retry calls `client.init` again.\n */\n onInitError?: (err: unknown) => void\n /** Class for the root wrapper. e.g. \"relative flex h-screen w-screen\" */\n wrapperClassName?: string\n /** Class for the loading overlay. e.g. \"absolute inset-0 z-50 flex items-center justify-center bg-zinc-950\" */\n loadingClassName?: string\n children: ReactNode\n}\n\n/**\n * A dependency that changes only when `value` holds different data, so an inline object doesn't\n * re-run an effect on every render. Falls back to identity for values JSON can't encode.\n */\nfunction valueKey(value: unknown): unknown {\n try {\n return JSON.stringify(value ?? null)\n } catch {\n return value\n }\n}\n\n/**\n * Gates children until the Seed client is initialized and optionally imports a schema.\n * Mounted inside RouterProvider so we never swap the router—only the route content.\n * This avoids removeChild DOM errors and ensures QueryClientProvider stays stable.\n * Both overlay and content stay in DOM; we only toggle visibility via classes.\n * After `client.stop()` it shows the loading state again until the next init finishes, without\n * a remount.\n */\nexport function SeedClientGate({\n initConfig,\n schema,\n loadingComponent,\n wrapperClassName,\n loadingClassName,\n onInitError,\n children,\n}: SeedClientGateProps) {\n const isClientReady = useIsClientReady()\n // Kept in a ref so an inline callback doesn't re-run init on every render.\n const onInitErrorRef = useRef(onInitError)\n onInitErrorRef.current = onInitError\n // Compared by value, so an inline initConfig or schema doesn't re-run init on every render.\n const initConfigRef = useRef(initConfig)\n initConfigRef.current = initConfig\n const schemaRef = useRef(schema)\n schemaRef.current = schema\n const initConfigKey = valueKey(initConfig)\n const schemaKey = valueKey(schema)\n\n useEffect(() => {\n const initConfig = initConfigRef.current\n const schema = schemaRef.current\n const effectiveConfig: SeedConstructorOptions = schema\n ? {\n ...initConfig,\n config: {\n ...initConfig.config,\n schema,\n } as SeedConfig,\n }\n : initConfig\n client.init(effectiveConfig).catch((err: unknown) => {\n if (onInitErrorRef.current) {\n onInitErrorRef.current(err)\n } else {\n console.error('[SeedClientGate] client init failed', err)\n }\n })\n }, [initConfigKey, schemaKey])\n\n const loadingContent = loadingComponent ?? <DefaultLoading />\n\n const wrapperStyle = !wrapperClassName\n ? { position: 'relative' as const, display: 'flex' as const, height: '100vh', width: '100vw' }\n : undefined\n\n const overlayStyle: React.CSSProperties = {\n display: isClientReady ? 'none' : 'flex',\n ...(!loadingClassName && {\n position: 'absolute',\n inset: 0,\n zIndex: 50,\n alignItems: 'center',\n justifyContent: 'center',\n }),\n }\n\n const contentStyle: React.CSSProperties = {\n flex: 1,\n display: isClientReady ? 'flex' : 'none',\n flexDirection: 'column',\n }\n\n return (\n <div className={wrapperClassName} style={wrapperStyle}>\n <div\n className={loadingClassName}\n style={overlayStyle}\n aria-hidden={isClientReady}\n >\n {loadingContent}\n </div>\n <div style={contentStyle}>\n {children}\n </div>\n </div>\n )\n}\n","/**\n * Shared OPFS path helpers. Paths are '/'-separated and relative to the OPFS root\n * (e.g. 'app-files/files/images/photo.jpg').\n */\n\nfunction splitPath(path: string): string[] {\n const parts = path.split('/').filter(Boolean)\n if (parts.length === 0) throw new Error(`Invalid OPFS path: \"${path}\"`)\n return parts\n}\n\n/** Walk to the directory handle at `path`. */\nexport async function resolveDirectoryHandle(\n path: string,\n root?: FileSystemDirectoryHandle,\n): Promise<FileSystemDirectoryHandle> {\n let handle = root ?? (await navigator.storage.getDirectory())\n for (const part of path.split('/').filter(Boolean)) {\n handle = await handle.getDirectoryHandle(part)\n }\n return handle\n}\n\n/** Resolve the parent directory handle and the entry name for `path`. */\nasync function resolveParent(\n path: string,\n root?: FileSystemDirectoryHandle,\n): Promise<{ dir: FileSystemDirectoryHandle; name: string }> {\n const parts = splitPath(path)\n const name = parts.pop()!\n const dir = await resolveDirectoryHandle(parts.join('/'), root)\n return { dir, name }\n}\n\nexport async function resolveFileHandle(\n path: string,\n root?: FileSystemDirectoryHandle,\n): Promise<FileSystemFileHandle> {\n const { dir, name } = await resolveParent(path, root)\n return dir.getFileHandle(name)\n}\n\n/** Read the file at `path` as a File (which is a Blob). */\nexport async function getOPFSFile(path: string, root?: FileSystemDirectoryHandle): Promise<File> {\n const handle = await resolveFileHandle(path, root)\n return handle.getFile()\n}\n\nexport async function deleteOPFSEntry(path: string, root?: FileSystemDirectoryHandle): Promise<void> {\n const { dir, name } = await resolveParent(path, root)\n await dir.removeEntry(name)\n}\n\nasync function pruneEmptyDirectory(parent: FileSystemDirectoryHandle, name: string): Promise<boolean> {\n const dir = await parent.getDirectoryHandle(name)\n const children: [string, FileSystemHandle][] = []\n for await (const child of dir.entries()) children.push(child)\n let empty = true\n for (const [childName, handle] of children) {\n if (handle.kind === 'file' || !(await pruneEmptyDirectory(dir, childName))) empty = false\n }\n if (empty) await parent.removeEntry(name)\n return empty\n}\n\n/**\n * Remove the folder at `path` and any folders inside it that hold no files. Folders that\n * still hold files stay. Resolves true when `path` itself was removed (or was already gone).\n */\nexport async function removeEmptyOPFSDirectories(path: string, root?: FileSystemDirectoryHandle): Promise<boolean> {\n try {\n const { dir, name } = await resolveParent(path, root)\n return await pruneEmptyDirectory(dir, name)\n } catch (err) {\n if (err instanceof DOMException && err.name === 'NotFoundError') return true\n throw err\n }\n}\n\n/** True when this browser exposes the Origin Private File System. */\nexport function isOPFSSupported(): boolean {\n return (\n typeof navigator !== 'undefined' &&\n !!navigator.storage &&\n typeof navigator.storage.getDirectory === 'function'\n )\n}\n\nconst SIGNATURES: Array<{ type: string; test: (b: Uint8Array) => boolean }> = [\n { type: 'image/jpeg', test: (b) => b[0] === 0xff && b[1] === 0xd8 && b[2] === 0xff },\n {\n type: 'image/png',\n test: (b) => b[0] === 0x89 && b[1] === 0x50 && b[2] === 0x4e && b[3] === 0x47,\n },\n { type: 'image/gif', test: (b) => ascii(b, 0, 4) === 'GIF8' },\n { type: 'image/webp', test: (b) => ascii(b, 0, 4) === 'RIFF' && ascii(b, 8, 12) === 'WEBP' },\n {\n type: 'image/avif',\n test: (b) => ascii(b, 4, 8) === 'ftyp' && ['avif', 'avis'].includes(ascii(b, 8, 12)),\n },\n { type: 'application/vnd.sqlite3', test: (b) => ascii(b, 0, 15) === 'SQLite format 3' },\n { type: 'application/pdf', test: (b) => ascii(b, 0, 5) === '%PDF-' },\n]\n\nfunction ascii(bytes: Uint8Array, start: number, end: number): string {\n return String.fromCharCode(...bytes.subarray(start, end))\n}\n\n/** Identify a file type from its leading bytes (at least 16 bytes recommended). */\nexport function sniffMimeType(bytes: Uint8Array): string | undefined {\n return SIGNATURES.find((s) => s.test(bytes))?.type\n}\n\n/**\n * OPFS doesn't store MIME types, so File.type comes from the extension and is empty\n * for files saved under an Arweave transaction ID. Sniff those from their contents.\n */\nexport async function detectMimeType(file: Blob): Promise<string | undefined> {\n if (file.size === 0) return undefined\n const head = new Uint8Array(await file.slice(0, 16).arrayBuffer())\n return sniffMimeType(head)\n}\n","import { useState, useCallback, useEffect } from 'react'\nimport { detectMimeType, resolveDirectoryHandle } from './opfsPaths'\n\nexport interface OPFSFile {\n name: string\n path: string\n size: number\n /** `File.type`, which OPFS derives from the extension. 'application/octet-stream' when unknown. */\n type: string\n /**\n * Type detected from the file's leading bytes, set only when `File.type` is empty\n * (e.g. images saved under an Arweave transaction ID with no extension).\n */\n detectedType?: string\n lastModified: number\n}\n\nasync function scanDirectory(\n dirHandle: FileSystemDirectoryHandle,\n basePath: string = ''\n): Promise<OPFSFile[]> {\n const foundFiles: OPFSFile[] = []\n\n try {\n for await (const [name, handle] of dirHandle.entries()) {\n const currentPath = basePath ? `${basePath}/${name}` : name\n\n if (handle.kind === 'file') {\n try {\n const file = await (handle as FileSystemFileHandle).getFile()\n const detectedType = file.type ? undefined : await detectMimeType(file).catch(() => undefined)\n foundFiles.push({\n name,\n path: currentPath,\n size: file.size,\n type: file.type || 'application/octet-stream',\n ...(detectedType && { detectedType }),\n lastModified: file.lastModified,\n })\n } catch (err) {\n console.warn(`Failed to read file ${currentPath}:`, err)\n }\n } else if (handle.kind === 'directory') {\n const subFiles = await scanDirectory(handle as FileSystemDirectoryHandle, currentPath)\n foundFiles.push(...subFiles)\n }\n }\n } catch (err) {\n console.warn(`Failed to scan directory ${basePath}:`, err)\n }\n\n return foundFiles\n}\n\nexport interface UseOPFSFilesOptions {\n /** Optional subdirectory to scan (e.g. 'app-files'). Default: root. */\n rootPath?: string\n}\n\n/**\n * Hook to scan and list all files in OPFS (Origin Private File System).\n * Uses navigator.storage.getDirectory() - works in browsers that support OPFS.\n *\n * `isLoading` is true during every scan, including refetches; `files` keeps the\n * previous result until the new scan finishes.\n *\n * @example\n * ```tsx\n * const { files, isLoading, error, refetch } = useOPFSFiles()\n * ```\n */\nexport function useOPFSFiles(options: UseOPFSFilesOptions = {}) {\n const { rootPath } = options\n const [files, setFiles] = useState<OPFSFile[]>([])\n const [isLoading, setIsLoading] = useState(true)\n const [hasLoaded, setHasLoaded] = useState(false)\n const [error, setError] = useState<string | null>(null)\n const [errorName, setErrorName] = useState<string | null>(null)\n\n const load = useCallback(async () => {\n setIsLoading(true)\n setError(null)\n setErrorName(null)\n\n try {\n const dirHandle = await resolveDirectoryHandle(rootPath ?? '')\n const allFiles = await scanDirectory(dirHandle, rootPath || '')\n setFiles(allFiles.sort((a, b) => a.path.localeCompare(b.path)))\n } catch (err) {\n setError(\n 'Failed to access OPFS: ' + (err instanceof Error ? err.message : String(err))\n )\n setErrorName(err instanceof Error ? err.name : null)\n console.error('OPFS access error:', err)\n } finally {\n setIsLoading(false)\n setHasLoaded(true)\n }\n }, [rootPath])\n\n useEffect(() => {\n load()\n }, [load])\n\n return {\n files,\n isLoading,\n /** False until the first scan finishes. */\n hasLoaded,\n error,\n /** The thrown error's name, e.g. 'NotFoundError' when `rootPath` doesn't exist. */\n errorName,\n refetch: load,\n }\n}\n","const SIZE_UNITS = ['B', 'KB', 'MB', 'GB', 'TB']\n\n/** 1024-based size: '0 B', '812 B', '4.2 KB', '38 MB'. One decimal below 10. */\nexport function formatFileSize(bytes: number): string {\n if (!Number.isFinite(bytes) || bytes <= 0) return '0 B'\n const i = Math.min(SIZE_UNITS.length - 1, Math.floor(Math.log(bytes) / Math.log(1024)))\n const value = bytes / Math.pow(1024, i)\n const shown = i === 0 ? value : value < 10 ? Math.round(value * 10) / 10 : Math.round(value)\n return `${shown} ${SIZE_UNITS[i]}`\n}\n\nconst MINUTE = 60_000\nconst HOUR = 60 * MINUTE\nconst DAY = 24 * HOUR\n\n/** Short relative time: 'just now', '5 min ago', '3 h ago', 'yesterday', '12 days ago', or a date. */\nexport function formatRelativeTime(timestamp: number, now: number = Date.now()): string {\n const diff = now - timestamp\n if (diff < MINUTE) return 'just now'\n if (diff < HOUR) return `${Math.round(diff / MINUTE)} min ago`\n if (diff < DAY) return `${Math.round(diff / HOUR)} h ago`\n const days = Math.round(diff / DAY)\n if (days === 1) return 'yesterday'\n if (days < 30) return `${days} days ago`\n return new Date(timestamp).toLocaleDateString(undefined, { dateStyle: 'medium' })\n}\n\nexport function formatDateTime(timestamp: number): string {\n return new Date(timestamp).toLocaleString(undefined, { dateStyle: 'medium', timeStyle: 'short' })\n}\n\n/** Shorten long names (e.g. 43-char Arweave transaction IDs) from the middle, keeping the extension visible. */\nexport function truncateMiddle(value: string, max: number): string {\n if (value.length <= max) return value\n const head = Math.ceil((max - 1) / 2)\n const tail = Math.floor((max - 1) / 2)\n return `${value.slice(0, head)}…${value.slice(value.length - tail)}`\n}\n","/**\n * Icons vendored from Lucide (https://lucide.dev), ISC License,\n * Copyright (c) Lucide Contributors. See docs/adr/0003-icons-vendored-from-lucide.md.\n *\n * To add an icon, copy the children of its <svg> from lucide.dev into ICONS.\n * Keep Lucide's 24×24 viewBox and stroke-width 2 so icons match hosts that use lucide-react.\n */\nimport React from 'react'\n\nconst ICONS = {\n check: <path d=\"M20 6 9 17l-5-5\" />,\n chevronLeft: <path d=\"m15 18-6-6 6-6\" />,\n chevronRight: <path d=\"m9 18 6-6-6-6\" />,\n copy: (\n <>\n <rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\" />\n <path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\" />\n </>\n ),\n database: (\n <>\n <ellipse cx=\"12\" cy=\"5\" rx=\"9\" ry=\"3\" />\n <path d=\"M3 5V19A9 3 0 0 0 21 19V5\" />\n <path d=\"M3 12A9 3 0 0 0 21 12\" />\n </>\n ),\n download: (\n <>\n <path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\" />\n <polyline points=\"7 10 12 15 17 10\" />\n <line x1=\"12\" x2=\"12\" y1=\"15\" y2=\"3\" />\n </>\n ),\n file: (\n <>\n <path d=\"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z\" />\n <path d=\"M14 2v4a2 2 0 0 0 2 2h4\" />\n </>\n ),\n fileCode: (\n <>\n <path d=\"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z\" />\n <path d=\"M14 2v4a2 2 0 0 0 2 2h4\" />\n <path d=\"m10 13-2 2 2 2\" />\n <path d=\"m14 17 2-2-2-2\" />\n </>\n ),\n fileJson: (\n <>\n <path d=\"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z\" />\n <path d=\"M14 2v4a2 2 0 0 0 2 2h4\" />\n <path d=\"M10 12a1 1 0 0 0-1 1v1a1 1 0 0 1-1 1 1 1 0 0 1 1 1v1a1 1 0 0 0 1 1\" />\n <path d=\"M14 18a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1 1 1 0 0 1-1-1v-1a1 1 0 0 0-1-1\" />\n </>\n ),\n fileText: (\n <>\n <path d=\"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z\" />\n <path d=\"M14 2v4a2 2 0 0 0 2 2h4\" />\n <path d=\"M10 9H8\" />\n <path d=\"M16 13H8\" />\n <path d=\"M16 17H8\" />\n </>\n ),\n folder: (\n <path d=\"M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z\" />\n ),\n folderOpen: (\n <path d=\"m6 14 1.5-2.9A2 2 0 0 1 9.24 10H20a2 2 0 0 1 1.94 2.5l-1.54 6a2 2 0 0 1-1.95 1.5H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H18a2 2 0 0 1 2 2v2\" />\n ),\n image: (\n <>\n <rect width=\"18\" height=\"18\" x=\"3\" y=\"3\" rx=\"2\" ry=\"2\" />\n <circle cx=\"9\" cy=\"9\" r=\"2\" />\n <path d=\"m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21\" />\n </>\n ),\n imageOff: (\n <>\n <line x1=\"2\" x2=\"22\" y1=\"2\" y2=\"22\" />\n <path d=\"M10.41 10.41a2 2 0 1 1-2.83-2.83\" />\n <line x1=\"13.5\" x2=\"6\" y1=\"13.5\" y2=\"21\" />\n <line x1=\"18\" x2=\"21\" y1=\"12\" y2=\"15\" />\n <path d=\"M3.59 3.59A1.99 1.99 0 0 0 3 5v14a2 2 0 0 0 2 2h14c.55 0 1.052-.22 1.41-.59\" />\n <path d=\"M21 15V5a2 2 0 0 0-2-2H9\" />\n </>\n ),\n layers: (\n <>\n <path d=\"m12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83Z\" />\n <path d=\"m22 17.65-9.17 4.16a2 2 0 0 1-1.66 0L2 17.65\" />\n <path d=\"m22 12.65-9.17 4.16a2 2 0 0 1-1.66 0L2 12.65\" />\n </>\n ),\n layoutGrid: (\n <>\n <rect width=\"7\" height=\"7\" x=\"3\" y=\"3\" rx=\"1\" />\n <rect width=\"7\" height=\"7\" x=\"14\" y=\"3\" rx=\"1\" />\n <rect width=\"7\" height=\"7\" x=\"14\" y=\"14\" rx=\"1\" />\n <rect width=\"7\" height=\"7\" x=\"3\" y=\"14\" rx=\"1\" />\n </>\n ),\n list: (\n <>\n <path d=\"M3 12h.01\" />\n <path d=\"M3 18h.01\" />\n <path d=\"M3 6h.01\" />\n <path d=\"M8 12h13\" />\n <path d=\"M8 18h13\" />\n <path d=\"M8 6h13\" />\n </>\n ),\n refresh: (\n <>\n <path d=\"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8\" />\n <path d=\"M21 3v5h-5\" />\n <path d=\"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16\" />\n <path d=\"M8 16H3v5\" />\n </>\n ),\n search: (\n <>\n <circle cx=\"11\" cy=\"11\" r=\"8\" />\n <path d=\"m21 21-4.3-4.3\" />\n </>\n ),\n trash: (\n <>\n <path d=\"M3 6h18\" />\n <path d=\"M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6\" />\n <path d=\"M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2\" />\n <line x1=\"10\" x2=\"10\" y1=\"11\" y2=\"17\" />\n <line x1=\"14\" x2=\"14\" y1=\"11\" y2=\"17\" />\n </>\n ),\n triangleAlert: (\n <>\n <path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\" />\n <path d=\"M12 9v4\" />\n <path d=\"M12 17h.01\" />\n </>\n ),\n x: (\n <>\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </>\n ),\n} satisfies Record<string, React.ReactNode>\n\nexport type IconName = keyof typeof ICONS\n\nexport function Icon({\n name,\n size = 16,\n strokeWidth = 2,\n className,\n}: {\n name: IconName\n size?: number\n strokeWidth?: number\n className?: string\n}) {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={strokeWidth}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n focusable=\"false\"\n className={className}\n >\n {ICONS[name]}\n </svg>\n )\n}\n","import React, { useEffect, useRef, useState } from 'react'\nimport type { OPFSFile } from '../useOPFSFiles'\nimport type { FileEntry, FolderSummary } from './fileModel'\nimport { formatFileSize, truncateMiddle } from './format'\nimport { Icon } from './icons'\nimport type { ResolvedDeleteAction } from './types'\n\nconst MAX_LISTED = 6\n\nexport interface DeleteDialogProps {\n entries: FileEntry[]\n /** Folder that listed paths are shown relative to. */\n rootPath: string\n warning: string | null\n /** Resolves the label and tone for the files the confirm button would remove. */\n deleteActionFor: (includeVariants: boolean) => ResolvedDeleteAction\n onCancel: () => void\n onConfirm: (includeVariants: boolean) => void\n className?: string\n}\n\n/** Native modal <dialog> (focus trap, Esc, top layer) wrapping a confirm form. */\nfunction ConfirmDialog({\n className,\n onCancel,\n onSubmit,\n children,\n}: {\n className?: string\n onCancel: () => void\n onSubmit: () => void\n children: React.ReactNode\n}) {\n const ref = useRef<HTMLDialogElement>(null)\n\n useEffect(() => {\n const dialog = ref.current\n if (dialog && !dialog.open) {\n dialog.showModal()\n // showModal() focuses the first control, which may be a checkbox or Download. Start on Cancel.\n dialog.querySelector<HTMLElement>('[data-initial-focus]')?.focus()\n }\n return () => {\n if (dialog?.open) dialog.close()\n }\n }, [])\n\n return (\n <dialog\n ref={ref}\n className={['seed-fm-dialog', className].filter(Boolean).join(' ')}\n aria-labelledby=\"seed-fm-delete-title\"\n onCancel={(e) => {\n e.preventDefault()\n onCancel()\n }}\n onClick={(e) => {\n // A click on the backdrop lands on the <dialog> element itself.\n if (e.target === e.currentTarget) onCancel()\n }}\n >\n <form\n method=\"dialog\"\n onSubmit={(e) => {\n e.preventDefault()\n onSubmit()\n }}\n >\n {children}\n </form>\n </dialog>\n )\n}\n\n/** Delete confirmation for one or more files. */\nexport function DeleteDialog({\n entries,\n rootPath,\n warning,\n deleteActionFor,\n onCancel,\n onConfirm,\n className,\n}: DeleteDialogProps) {\n const [includeVariants, setIncludeVariants] = useState(true)\n\n const variants = entries.flatMap((e) => e.variants)\n const variantsSize = variants.reduce((n, v) => n + v.file.size, 0)\n const count = entries.length + (includeVariants ? variants.length : 0)\n const prefix = rootPath ? `${rootPath}/` : ''\n const action = deleteActionFor(includeVariants)\n const heading = action.label\n ? `${action.label}?`\n : entries.length === 1\n ? `Delete ${truncateMiddle(entries[0].file.name, 32)}?`\n : `Delete ${entries.length} files?`\n\n return (\n <ConfirmDialog className={className} onCancel={onCancel} onSubmit={() => onConfirm(includeVariants)}>\n <h3 id=\"seed-fm-delete-title\">{heading}</h3>\n <p>\n This removes {entries.length === 1 ? 'it' : 'them'} from this browser’s private storage. Copies on Arweave or\n other devices aren’t affected. You can’t undo this.\n </p>\n <ul>\n {entries.slice(0, MAX_LISTED).map((e) => (\n <li key={e.file.path}>\n <code title={e.file.path}>{e.file.path.startsWith(prefix) ? e.file.path.slice(prefix.length) : e.file.path}</code>\n <span>{formatFileSize(e.file.size)}</span>\n </li>\n ))}\n {entries.length > MAX_LISTED && (\n <li>\n <span>and {entries.length - MAX_LISTED} more</span>\n </li>\n )}\n </ul>\n {variants.length > 0 && (\n <label>\n <input\n type=\"checkbox\"\n className=\"seed-fm-checkbox\"\n checked={includeVariants}\n onChange={(e) => setIncludeVariants(e.target.checked)}\n />\n Also delete {variants.length} resized {variants.length === 1 ? 'copy' : 'copies'} ({formatFileSize(variantsSize)})\n </label>\n )}\n {warning && (\n <div className=\"seed-fm-warning\" role=\"alert\">\n <Icon name=\"triangleAlert\" size={16} />\n <span>{warning}</span>\n </div>\n )}\n <div className=\"seed-fm-dialog-actions\">\n <button type=\"button\" className=\"seed-fm-btn\" onClick={onCancel} data-initial-focus>\n Cancel\n </button>\n <button\n type=\"submit\"\n className={`seed-fm-btn ${action.destructive ? 'seed-fm-btn--danger' : 'seed-fm-btn--primary'}`}\n >\n {action.label ?? `Delete ${count} ${count === 1 ? 'file' : 'files'}`}\n </button>\n </div>\n </ConfirmDialog>\n )\n}\n\nexport interface FolderDeleteDialogProps {\n folder: FolderSummary\n /** Every file under the folder that this manager shows, at any depth. */\n files: OPFSFile[]\n /** Files under the folder that `filter` hides. They're kept, so the folder stays. */\n hiddenCount: number\n warning: string | null\n action: ResolvedDeleteAction\n busy: boolean\n /** Saves the folder as a .zip. Resolves true once it's saved. */\n onDownload: () => Promise<boolean>\n onCancel: () => void\n onConfirm: () => void\n className?: string\n}\n\n/** Delete confirmation for a folder, with a chance to save a copy first. */\nexport function FolderDeleteDialog({\n folder,\n files,\n hiddenCount,\n warning,\n action,\n busy,\n onDownload,\n onCancel,\n onConfirm,\n className,\n}: FolderDeleteDialogProps) {\n const [downloaded, setDownloaded] = useState(false)\n const size = files.reduce((n, f) => n + f.size, 0)\n const prefix = `${folder.path}/`\n const fileCount = `${files.length} ${files.length === 1 ? 'file' : 'files'}`\n // The folder card counts grouped resized copies as part of their original.\n const copies = files.length - folder.fileCount\n\n return (\n <ConfirmDialog className={className} onCancel={onCancel} onSubmit={onConfirm}>\n <h3 id=\"seed-fm-delete-title\">\n {action.label ? `${action.label}?` : `Delete the “${truncateMiddle(folder.name, 32)}” folder?`}\n </h3>\n <p>\n This removes the folder and the {fileCount} in it ({formatFileSize(size)}\n {copies > 0 && `, including ${copies} resized ${copies === 1 ? 'copy' : 'copies'}`}) from this browser’s private\n storage. Copies on Arweave or other devices aren’t affected.\n </p>\n <div className=\"seed-fm-irreversible\">\n <Icon name=\"triangleAlert\" size={16} />\n <span>\n <strong>This can’t be undone.</strong> Once deleted, these files can’t be recovered from this browser.\n </span>\n </div>\n <ul>\n {files.slice(0, MAX_LISTED).map((f) => (\n <li key={f.path}>\n <code title={f.path}>{f.path.slice(prefix.length)}</code>\n <span>{formatFileSize(f.size)}</span>\n </li>\n ))}\n {files.length > MAX_LISTED && (\n <li>\n <span>and {files.length - MAX_LISTED} more</span>\n </li>\n )}\n </ul>\n <div className=\"seed-fm-keepcopy\">\n <span>{downloaded ? 'Saved a copy to your computer.' : 'Want a copy first? Save the folder to your computer.'}</span>\n <button\n type=\"button\"\n className=\"seed-fm-btn\"\n disabled={busy}\n onClick={async () => {\n if (await onDownload()) setDownloaded(true)\n }}\n >\n <Icon name={downloaded ? 'check' : 'download'} size={15} /> {downloaded ? 'Download again' : 'Download .zip'}\n </button>\n </div>\n {hiddenCount > 0 && (\n <p>\n {hiddenCount} other {hiddenCount === 1 ? 'file' : 'files'} in this folder {hiddenCount === 1 ? 'is' : 'are'}{' '}\n hidden here and won’t be deleted, so the folder itself will stay.\n </p>\n )}\n {warning && (\n <div className=\"seed-fm-warning\" role=\"alert\">\n <Icon name=\"triangleAlert\" size={16} />\n <span>{warning}</span>\n </div>\n )}\n <div className=\"seed-fm-dialog-actions\">\n <button type=\"button\" className=\"seed-fm-btn\" onClick={onCancel} data-initial-focus>\n Cancel\n </button>\n <button\n type=\"submit\"\n disabled={busy}\n className={`seed-fm-btn ${action.destructive ? 'seed-fm-btn--danger' : 'seed-fm-btn--primary'}`}\n >\n {action.label ?? `Delete folder (${fileCount})`}\n </button>\n </div>\n </ConfirmDialog>\n )\n}\n","import type { OPFSFile } from '../useOPFSFiles'\n\nexport type FileKind = 'image' | 'database' | 'json' | 'html' | 'text' | 'other'\n\nexport type SortKey = 'name' | 'modified' | 'size'\n\n/** Detected type when available, otherwise File.type. */\nexport function effectiveType(file: OPFSFile): string {\n return file.detectedType ?? file.type\n}\n\nconst DATABASE_NAME = /\\.(db|sqlite|sqlite3)(-journal|-wal|-shm)?$/i\n\nexport function fileKind(file: OPFSFile): FileKind {\n const type = effectiveType(file)\n if (type.startsWith('image/')) return 'image'\n if (type === 'application/vnd.sqlite3' || DATABASE_NAME.test(file.name)) return 'database'\n if (type === 'application/json' || /\\.json$/i.test(file.name)) return 'json'\n if (type === 'text/html' || /\\.html?$/i.test(file.name)) return 'html'\n if (type.startsWith('text/')) return 'text'\n return 'other'\n}\n\n/** Short label for a file's format: its extension, or the detected subtype when there is none. */\nexport function formatLabel(file: OPFSFile): string {\n const ext = /\\.([a-z0-9-]{1,12})$/i.exec(file.name)?.[1]\n if (ext) return ext.toLowerCase()\n const type = effectiveType(file)\n if (type === 'application/octet-stream') return 'bin'\n return type.split('/')[1]?.replace(/^vnd\\./, '') ?? 'bin'\n}\n\n/** A resized copy the SDK writes to `…/images/<width>/<base>.webp`. */\nexport interface ImageVariant {\n file: OPFSFile\n width: number\n}\n\n/** A row in the manager: a file plus, for originals, its resized copies. */\nexport interface FileEntry {\n file: OPFSFile\n /** Resized copies, narrowest first. Empty unless variants are grouped. */\n variants: ImageVariant[]\n /** Size of the file plus its variants. */\n totalSize: number\n}\n\nconst VARIANT_PATH = /^(.*\\/)?images\\/(\\d+)\\/([^/]+)\\.webp$/\nconst ORIGINAL_PATH = /^(.*\\/)?images\\/([^/]+)$/\n\nconst stripExtension = (name: string) => name.replace(/\\.[^/.]+$/, '')\n\nexport function parseImageVariant(path: string): { key: string; width: number } | null {\n const m = VARIANT_PATH.exec(path)\n if (!m) return null\n return { key: `${m[1] ?? ''}images/${m[3]}`, width: Number(m[2]) }\n}\n\nfunction originalKey(path: string): string | null {\n const m = ORIGINAL_PATH.exec(path)\n return m ? `${m[1] ?? ''}images/${stripExtension(m[2])}` : null\n}\n\nconst toEntry = (file: OPFSFile): FileEntry => ({ file, variants: [], totalSize: file.size })\n\n/**\n * Turn the flat file list into entries. With `groupVariants`, resized copies are\n * attached to their original image and removed from the list. Copies whose original\n * is missing stay in the list on their own.\n */\nexport function buildEntries(files: OPFSFile[], groupVariants: boolean): FileEntry[] {\n if (!groupVariants) return files.map(toEntry)\n\n const originals = new Map<string, FileEntry>()\n const entries: FileEntry[] = []\n const variants: Array<{ file: OPFSFile; key: string; width: number }> = []\n\n for (const file of files) {\n const variant = parseImageVariant(file.path)\n if (variant) {\n variants.push({ file, ...variant })\n continue\n }\n const entry = toEntry(file)\n entries.push(entry)\n const key = originalKey(file.path)\n if (key && fileKind(file) === 'image') originals.set(key, entry)\n }\n\n for (const v of variants) {\n const original = originals.get(v.key)\n if (original) {\n original.variants.push({ file: v.file, width: v.width })\n original.totalSize += v.file.size\n } else {\n entries.push(toEntry(v.file))\n }\n }\n for (const entry of originals.values()) entry.variants.sort((a, b) => a.width - b.width)\n return entries\n}\n\nexport interface FolderSummary {\n name: string\n path: string\n fileCount: number\n size: number\n}\n\nexport interface DirectoryListing {\n folders: FolderSummary[]\n entries: FileEntry[]\n}\n\nconst joinPath = (dir: string, name: string) => (dir ? `${dir}/${name}` : name)\n\n/**\n * List the folders and files directly inside `cwd`. With a search query, returns\n * every matching file at any depth below `cwd` and no folders.\n */\nexport function listDirectory(\n all: FileEntry[],\n cwd: string,\n options: { query?: string; sort?: SortKey } = {},\n): DirectoryListing {\n const prefix = cwd ? `${cwd}/` : ''\n const inside = all.filter((e) => e.file.path.startsWith(prefix))\n const query = options.query?.trim().toLowerCase()\n\n if (query) {\n const matches = inside.filter((e) => e.file.path.slice(prefix.length).toLowerCase().includes(query))\n return { folders: [], entries: sortEntries(matches, options.sort) }\n }\n\n const folders = new Map<string, FolderSummary>()\n const entries: FileEntry[] = []\n for (const entry of inside) {\n const rest = entry.file.path.slice(prefix.length)\n const slash = rest.indexOf('/')\n if (slash === -1) {\n entries.push(entry)\n continue\n }\n const name = rest.slice(0, slash)\n const folder = folders.get(name) ?? { name, path: joinPath(cwd, name), fileCount: 0, size: 0 }\n folder.fileCount += 1\n folder.size += entry.totalSize\n folders.set(name, folder)\n }\n\n return {\n folders: [...folders.values()].sort((a, b) => a.name.localeCompare(b.name)),\n entries: sortEntries(entries, options.sort),\n }\n}\n\nexport function sortEntries(entries: FileEntry[], sort: SortKey = 'name'): FileEntry[] {\n const compare: Record<SortKey, (a: FileEntry, b: FileEntry) => number> = {\n name: (a, b) => a.file.name.localeCompare(b.file.name),\n modified: (a, b) => b.file.lastModified - a.file.lastModified,\n size: (a, b) => b.totalSize - a.totalSize,\n }\n return [...entries].sort(compare[sort])\n}\n\n/** Bytes per kind across all files, for the storage meter. */\nexport function sizeByKind(files: OPFSFile[]): Record<FileKind, number> {\n const totals: Record<FileKind, number> = { image: 0, database: 0, json: 0, html: 0, text: 0, other: 0 }\n for (const f of files) totals[fileKind(f)] += f.size\n return totals\n}\n\n/** Default deleteWarning: flags the Seed client's SQLite database. */\nexport function defaultDeleteWarning(files: OPFSFile[]): string | null {\n const db = files.find((f) => /(^|\\/)db\\/seed\\.db$/.test(f.path))\n if (!db) return null\n return 'This includes the Seed database. The app loses its local items and drafts, and anything not yet published is gone. Reload the app afterwards.'\n}\n","import { useCallback, useEffect, useState } from 'react'\nimport { getOPFSFile } from '../opfsPaths'\nimport { fileKind, type FileEntry } from './fileModel'\n\n/** Thumbnails are drawn at about 2× the largest tile width. */\nconst THUMBNAIL_WIDTH = 400\n/** Originals smaller than this are shown as-is instead of being resized first. */\nconst RESIZE_THRESHOLD_BYTES = 256 * 1024\nconst MAX_CONCURRENT_DECODES = 4\n/** Keep an unused thumbnail's object URL around briefly so switching views doesn't reload it. */\nconst RELEASE_DELAY_MS = 30_000\n\nlet activeJobs = 0\nconst queue: Array<() => void> = []\n\nfunction schedule<T>(job: () => Promise<T>): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const run = () => {\n activeJobs++\n job()\n .then(resolve, reject)\n .finally(() => {\n activeJobs--\n queue.shift()?.()\n })\n }\n if (activeJobs < MAX_CONCURRENT_DECODES) run()\n else queue.push(run)\n })\n}\n\ninterface CacheEntry {\n promise: Promise<string>\n url?: string\n refs: number\n timer?: ReturnType<typeof setTimeout>\n}\n\nconst cache = new Map<string, CacheEntry>()\n\nfunction acquire(key: string, create: () => Promise<Blob>): { promise: Promise<string>; release: () => void } {\n let entry = cache.get(key)\n if (!entry) {\n const created: CacheEntry = { refs: 0, promise: Promise.resolve('') }\n created.promise = schedule(create).then((blob) => {\n created.url = URL.createObjectURL(blob)\n return created.url\n })\n cache.set(key, created)\n entry = created\n }\n if (entry.timer) {\n clearTimeout(entry.timer)\n entry.timer = undefined\n }\n entry.refs++\n const held = entry\n let released = false\n return {\n promise: held.promise,\n release: () => {\n if (released) return\n released = true\n held.refs--\n if (held.refs > 0) return\n held.timer = setTimeout(() => {\n if (held.url) URL.revokeObjectURL(held.url)\n if (cache.get(key) === held) cache.delete(key)\n }, RELEASE_DELAY_MS)\n },\n }\n}\n\nasync function resizeImage(blob: Blob): Promise<Blob> {\n if (blob.size < RESIZE_THRESHOLD_BYTES || typeof createImageBitmap !== 'function') return blob\n const bitmap = await createImageBitmap(blob, { resizeWidth: THUMBNAIL_WIDTH, resizeQuality: 'medium' })\n try {\n if (typeof OffscreenCanvas !== 'undefined') {\n const canvas = new OffscreenCanvas(bitmap.width, bitmap.height)\n canvas.getContext('2d')!.drawImage(bitmap, 0, 0)\n return await canvas.convertToBlob({ type: 'image/webp', quality: 0.82 })\n }\n const canvas = document.createElement('canvas')\n canvas.width = bitmap.width\n canvas.height = bitmap.height\n canvas.getContext('2d')!.drawImage(bitmap, 0, 0)\n return await new Promise<Blob>((resolve, reject) =>\n canvas.toBlob((b) => (b ? resolve(b) : reject(new Error('Canvas export failed'))), 'image/webp', 0.82),\n )\n } finally {\n bitmap.close()\n }\n}\n\n/**\n * Prefer the narrowest resized copy the SDK already wrote (480w WebP); it needs no\n * decoding on our side. Otherwise read the original and downscale it.\n */\nasync function createThumbnail(entry: FileEntry): Promise<Blob> {\n const variant = entry.variants[0]\n if (variant) return getOPFSFile(variant.file.path)\n return resizeImage(await getOPFSFile(entry.file.path))\n}\n\n// One IntersectionObserver for every tile.\ntype VisibilityCallback = () => void\nconst visibilityCallbacks = new WeakMap<Element, VisibilityCallback>()\nlet observer: IntersectionObserver | null = null\n\nfunction observeOnce(el: Element, onVisible: VisibilityCallback): () => void {\n if (typeof IntersectionObserver === 'undefined') {\n onVisible()\n return () => {}\n }\n observer ??= new IntersectionObserver(\n (records) => {\n for (const record of records) {\n if (!record.isIntersecting) continue\n observer!.unobserve(record.target)\n visibilityCallbacks.get(record.target)?.()\n visibilityCallbacks.delete(record.target)\n }\n },\n { rootMargin: '300px' },\n )\n visibilityCallbacks.set(el, onVisible)\n observer.observe(el)\n return () => {\n visibilityCallbacks.delete(el)\n observer?.unobserve(el)\n }\n}\n\nexport type ThumbnailStatus = 'idle' | 'loading' | 'ready' | 'error'\n\n/**\n * Lazily load a thumbnail object URL for an image entry once its element scrolls near\n * the viewport. Returns `status: 'idle'` for non-images.\n */\nexport function useThumbnail(entry: FileEntry) {\n const isImage = fileKind(entry.file) === 'image'\n const [element, setElement] = useState<Element | null>(null)\n const [visible, setVisible] = useState(false)\n const [url, setUrl] = useState<string | null>(null)\n const [status, setStatus] = useState<ThumbnailStatus>('idle')\n\n const ref = useCallback((el: Element | null) => setElement(el), [])\n\n useEffect(() => {\n if (!isImage || !element || visible) return\n return observeOnce(element, () => setVisible(true))\n }, [isImage, element, visible])\n\n const { path, lastModified, size } = entry.file\n const variantPath = entry.variants[0]?.file.path\n const key = `${path}:${lastModified}:${size}:${variantPath ?? ''}`\n\n useEffect(() => {\n if (!isImage || !visible) return\n let cancelled = false\n setStatus('loading')\n const handle = acquire(key, () => createThumbnail(entry))\n handle.promise.then(\n (u) => {\n if (cancelled) return\n setUrl(u)\n setStatus('ready')\n },\n () => {\n if (!cancelled) setStatus('error')\n },\n )\n return () => {\n cancelled = true\n handle.release()\n }\n // entry identity changes on every refetch; key captures what matters.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [isImage, visible, key])\n\n /** Call from <img onError> when the browser can't decode the thumbnail. */\n const markError = useCallback(() => setStatus('error'), [])\n\n return { ref, url: status === 'ready' ? url : null, status, markError }\n}\n","import React from 'react'\nimport { effectiveType, fileKind, formatLabel, type FileEntry, type FileKind } from './fileModel'\nimport { Icon, type IconName } from './icons'\nimport { useThumbnail } from './useThumbnail'\n\nexport const KIND_META: Record<FileKind, { icon: IconName; label: string; color: string }> = {\n image: { icon: 'image', label: 'Images', color: 'var(--seed-kind-image)' },\n database: { icon: 'database', label: 'Database', color: 'var(--seed-kind-database)' },\n json: { icon: 'fileJson', label: 'JSON', color: 'var(--seed-kind-json)' },\n html: { icon: 'fileCode', label: 'HTML', color: 'var(--seed-kind-html)' },\n text: { icon: 'fileText', label: 'Text', color: 'var(--seed-kind-text)' },\n other: { icon: 'file', label: 'Other', color: 'var(--seed-kind-other)' },\n}\n\nexport const kindStyle = (kind: FileKind) =>\n ({ '--seed-kind': KIND_META[kind].color }) as React.CSSProperties\n\n/**\n * Formats that can carry transparency get a checkerboard and are letterboxed instead of\n * cropped. Judged by the original: the SDK's WebP copies of a JPEG are still opaque.\n */\nconst mayBeTransparent = (entry: FileEntry) => /png|webp|gif|avif|svg/.test(effectiveType(entry.file))\n\nexport function FileThumbnail({\n entry,\n size,\n children,\n}: {\n entry: FileEntry\n size: 'tile' | 'mini'\n /** Overlays (checkbox, badges) rendered inside the frame. */\n children?: React.ReactNode\n}) {\n const kind = fileKind(entry.file)\n const { ref, url, status, markError } = useThumbnail(entry)\n const failed = kind === 'image' && status === 'error'\n const showImage = kind === 'image' && url && !failed\n\n const className = [\n size === 'tile' ? 'seed-fm-thumb' : 'seed-fm-mini',\n showImage && mayBeTransparent(entry) ? 'seed-fm-thumb--checker' : '',\n kind === 'image' && status === 'loading' ? 'seed-fm-skeleton' : '',\n ]\n .filter(Boolean)\n .join(' ')\n\n let content: React.ReactNode\n if (showImage) {\n content = <img src={url} alt=\"\" decoding=\"async\" draggable={false} onError={markError} />\n } else if (size === 'mini') {\n content = <Icon name={failed ? 'imageOff' : KIND_META[kind].icon} size={16} />\n } else if (kind === 'image' && !failed) {\n content = null\n } else {\n content = (\n <span className=\"seed-fm-kind\">\n <Icon name={failed ? 'imageOff' : KIND_META[kind].icon} size={28} strokeWidth={1.6} />\n {failed ? (\n <span className=\"seed-fm-kind-error\">Can’t decode image</span>\n ) : (\n <span className=\"seed-fm-ext\">{formatLabel(entry.file)}</span>\n )}\n </span>\n )\n }\n\n return (\n <span ref={ref} className={className} style={kindStyle(kind)}>\n {content}\n {children}\n </span>\n )\n}\n","import React from 'react'\nimport type { FileEntry } from './fileModel'\nimport { FileThumbnail } from './FileThumbnail'\nimport { formatDateTime, formatFileSize, formatRelativeTime, truncateMiddle } from './format'\nimport { Icon } from './icons'\n\nexport interface FileViewProps {\n entries: FileEntry[]\n selected: ReadonlySet<string>\n /** Click or Enter on an item. */\n onActivate: (entry: FileEntry, index: number, event: React.MouseEvent | React.KeyboardEvent) => void\n /** Checkbox, Space, or Shift-click. `range` extends from the last toggled item. */\n onToggle: (entry: FileEntry, index: number, range: boolean) => void\n tileClassName?: string\n className?: string\n}\n\nexport function FileGrid({ entries, selected, onActivate, onToggle, className, tileClassName }: FileViewProps) {\n return (\n <ul\n className={['seed-fm-grid', className].filter(Boolean).join(' ')}\n role=\"listbox\"\n aria-label=\"Files\"\n aria-multiselectable=\"true\"\n >\n {entries.map((entry, index) => {\n const { file, variants } = entry\n const isSelected = selected.has(file.path)\n return (\n <li\n key={file.path}\n role=\"option\"\n tabIndex={0}\n aria-selected={isSelected}\n aria-label={file.name}\n title={file.path}\n data-path={file.path}\n className={['seed-fm-tile', tileClassName].filter(Boolean).join(' ')}\n onClick={(e) => onActivate(entry, index, e)}\n onKeyDown={(e) => {\n if (e.target !== e.currentTarget) return\n if (e.key === 'Enter') {\n e.preventDefault()\n onActivate(entry, index, e)\n } else if (e.key === ' ') {\n e.preventDefault()\n onToggle(entry, index, e.shiftKey)\n }\n }}\n >\n <FileThumbnail entry={entry} size=\"tile\">\n <button\n type=\"button\"\n className=\"seed-fm-check\"\n tabIndex={-1}\n aria-label={`${isSelected ? 'Deselect' : 'Select'} ${file.name}`}\n onClick={(e) => {\n e.stopPropagation()\n onToggle(entry, index, e.shiftKey)\n }}\n >\n {isSelected && <Icon name=\"check\" size={14} strokeWidth={3} />}\n </button>\n {variants.length > 0 && (\n <span className=\"seed-fm-badge\" title=\"Resized copies are grouped with this image\">\n <Icon name=\"layers\" size={11} strokeWidth={2.2} />\n {variants.length} {variants.length === 1 ? 'size' : 'sizes'}\n </span>\n )}\n </FileThumbnail>\n <span className=\"seed-fm-tile-meta\">\n <span className=\"seed-fm-tile-name\">{truncateMiddle(file.name, 24)}</span>\n <span className=\"seed-fm-tile-sub\">\n {formatFileSize(file.size)} ·{' '}\n <time dateTime={new Date(file.lastModified).toISOString()} title={formatDateTime(file.lastModified)}>\n {formatRelativeTime(file.lastModified)}\n </time>\n </span>\n </span>\n </li>\n )\n })}\n </ul>\n )\n}\n","import React, { useEffect, useRef } from 'react'\nimport { effectiveType, fileKind, type FileEntry } from './fileModel'\nimport type { FileViewProps } from './FileGrid'\nimport { FileThumbnail, kindStyle } from './FileThumbnail'\nimport { formatDateTime, formatFileSize, formatRelativeTime } from './format'\nimport { Icon } from './icons'\nimport type { ResolvedDeleteAction } from './types'\n\nexport interface FileListProps extends Omit<FileViewProps, 'tileClassName'> {\n /** Paths are shown relative to this folder. */\n cwd: string\n onToggleAll: () => void\n onDownload: (entry: FileEntry) => void\n onDelete: (entry: FileEntry) => void\n deleteActionFor: (entry: FileEntry) => ResolvedDeleteAction\n rowClassName?: string\n}\n\nexport function FileList({\n entries,\n selected,\n cwd,\n onActivate,\n onToggle,\n onToggleAll,\n onDownload,\n onDelete,\n deleteActionFor,\n className,\n rowClassName,\n}: FileListProps) {\n const selectAllRef = useRef<HTMLInputElement>(null)\n const selectedHere = entries.filter((e) => selected.has(e.file.path)).length\n const allSelected = entries.length > 0 && selectedHere === entries.length\n\n useEffect(() => {\n if (selectAllRef.current) selectAllRef.current.indeterminate = selectedHere > 0 && !allSelected\n }, [selectedHere, allSelected])\n\n const prefix = cwd ? `${cwd}/` : ''\n\n return (\n <div className=\"seed-fm-list-wrap\">\n <table className={['seed-fm-list', className].filter(Boolean).join(' ')}>\n <thead>\n <tr>\n <th style={{ width: 28 }}>\n <input\n ref={selectAllRef}\n type=\"checkbox\"\n className=\"seed-fm-checkbox\"\n checked={allSelected}\n onChange={onToggleAll}\n aria-label=\"Select all\"\n />\n </th>\n <th>Name</th>\n <th className=\"seed-fm-col-type\">Type</th>\n <th className=\"seed-fm-num seed-fm-col-size\">Size</th>\n <th className=\"seed-fm-col-modified\">Modified</th>\n <th>\n <span className=\"seed-fm-visually-hidden\">Actions</span>\n </th>\n </tr>\n </thead>\n <tbody>\n {entries.map((entry, index) => {\n const { file } = entry\n const isSelected = selected.has(file.path)\n const deleteAction = deleteActionFor(entry)\n return (\n <tr\n key={file.path}\n data-path={file.path}\n aria-selected={isSelected}\n className={['seed-fm-row', rowClassName].filter(Boolean).join(' ')}\n onClick={(e) => onActivate(entry, index, e)}\n >\n <td onClick={(e) => e.stopPropagation()}>\n <input\n type=\"checkbox\"\n className=\"seed-fm-checkbox\"\n checked={isSelected}\n onChange={() => {}}\n onClick={(e) => onToggle(entry, index, e.shiftKey)}\n aria-label={`Select ${file.name}`}\n />\n </td>\n <td>\n <span className=\"seed-fm-namecell\">\n <FileThumbnail entry={entry} size=\"mini\" />\n <span>\n <button\n type=\"button\"\n className=\"seed-fm-row-name seed-fm-row-open\"\n onClick={(e) => {\n e.stopPropagation()\n onActivate(entry, index, e)\n }}\n >\n {file.name}\n </button>\n {/* Search results can come from subfolders; show where. */}\n {file.path.slice(prefix.length) !== file.name && (\n <span className=\"seed-fm-row-path\">{file.path.slice(prefix.length)}</span>\n )}\n </span>\n </span>\n </td>\n <td className=\"seed-fm-col-type\">\n <span className=\"seed-fm-type\" style={kindStyle(fileKind(file))}>\n {effectiveType(file)}\n {file.detectedType && <small>detected</small>}\n </span>\n </td>\n <td className=\"seed-fm-num seed-fm-col-size\" title={`${file.size.toLocaleString()} bytes`}>\n {formatFileSize(entry.totalSize)}\n </td>\n <td className=\"seed-fm-when seed-fm-col-modified\" title={formatDateTime(file.lastModified)}>\n {formatRelativeTime(file.lastModified)}\n </td>\n <td className=\"seed-fm-actions\" onClick={(e) => e.stopPropagation()}>\n <button\n type=\"button\"\n className=\"seed-fm-btn seed-fm-btn--ghost seed-fm-btn--icon\"\n aria-label={`Download ${file.name}`}\n title=\"Download\"\n onClick={() => onDownload(entry)}\n >\n <Icon name=\"download\" size={15} />\n </button>\n <button\n type=\"button\"\n className=\"seed-fm-btn seed-fm-btn--ghost seed-fm-btn--icon\"\n aria-label={deleteAction.label ? `${deleteAction.label}: ${file.name}` : `Delete ${file.name}`}\n title={deleteAction.label ?? 'Delete'}\n onClick={() => onDelete(entry)}\n >\n <Icon name=\"trash\" size={15} />\n </button>\n </td>\n </tr>\n )\n })}\n </tbody>\n </table>\n </div>\n )\n}\n","import React, { useEffect, useRef, useState } from 'react'\nimport { getOPFSFile } from '../opfsPaths'\nimport { effectiveType, fileKind, type FileEntry } from './fileModel'\nimport { KIND_META, kindStyle } from './FileThumbnail'\nimport { formatDateTime, formatFileSize, formatRelativeTime } from './format'\nimport { Icon } from './icons'\nimport type { ResolvedDeleteAction } from './types'\n\n/** Text files larger than this aren't previewed. */\nconst MAX_TEXT_PREVIEW_FILE = 1024 * 1024\n/** Only this many bytes of a text file are shown. */\nconst TEXT_PREVIEW_BYTES = 32 * 1024\n\ntype Preview =\n | { kind: 'loading' }\n | { kind: 'image'; url: string }\n | { kind: 'text'; text: string; truncated: boolean }\n | { kind: 'none'; message: string }\n\nfunction usePreview(entry: FileEntry): Preview {\n const [preview, setPreview] = useState<Preview>({ kind: 'loading' })\n const { path, lastModified } = entry.file\n const kind = fileKind(entry.file)\n const size = entry.file.size\n\n useEffect(() => {\n let cancelled = false\n let url: string | null = null\n setPreview({ kind: 'loading' })\n\n const load = async (): Promise<Preview> => {\n if (kind === 'image') {\n url = URL.createObjectURL(await getOPFSFile(path))\n return { kind: 'image', url }\n }\n if (kind === 'json' || kind === 'html' || kind === 'text') {\n if (size > MAX_TEXT_PREVIEW_FILE) return { kind: 'none', message: 'This file is too large to preview.' }\n const file = await getOPFSFile(path)\n const truncated = file.size > TEXT_PREVIEW_BYTES\n let text = await file.slice(0, TEXT_PREVIEW_BYTES).text()\n if (kind === 'json' && !truncated) {\n try {\n text = JSON.stringify(JSON.parse(text), null, 2)\n } catch {\n // Show the raw text when it isn't valid JSON.\n }\n }\n return { kind: 'text', text, truncated }\n }\n if (kind === 'database') {\n return { kind: 'none', message: 'SQLite database. Download it to open it in a database tool.' }\n }\n return { kind: 'none', message: 'No preview for this file type.' }\n }\n\n load().then(\n (p) => {\n if (!cancelled) setPreview(p)\n },\n (err) => {\n if (!cancelled) setPreview({ kind: 'none', message: `Couldn’t read the file: ${err instanceof Error ? err.message : String(err)}` })\n },\n )\n return () => {\n cancelled = true\n if (url) URL.revokeObjectURL(url)\n }\n }, [path, lastModified, size, kind])\n\n return preview\n}\n\nexport interface PreviewPanelProps {\n entry: FileEntry\n onClose: () => void\n onPrevious: (() => void) | null\n onNext: (() => void) | null\n onDownload: () => void\n onDelete: () => void\n deleteAction: ResolvedDeleteAction\n onCopyPath: () => void\n className?: string\n}\n\nexport function PreviewPanel({\n entry,\n onClose,\n onPrevious,\n onNext,\n onDownload,\n onDelete,\n deleteAction,\n onCopyPath,\n className,\n}: PreviewPanelProps) {\n const { file, variants } = entry\n const kind = fileKind(file)\n const preview = usePreview(entry)\n const [dimensions, setDimensions] = useState<{ width: number; height: number } | null>(null)\n const [imageFailed, setImageFailed] = useState(false)\n const closeRef = useRef<HTMLButtonElement>(null)\n\n useEffect(() => {\n setDimensions(null)\n setImageFailed(false)\n }, [file.path])\n\n useEffect(() => {\n closeRef.current?.focus()\n }, [])\n\n const headingId = `seed-fm-panel-${encodeURIComponent(file.path)}`\n const variantsSize = variants.reduce((n, v) => n + v.file.size, 0)\n\n let previewContent: React.ReactNode\n if (preview.kind === 'image' && !imageFailed) {\n previewContent = (\n <img\n src={preview.url}\n alt={`Preview of ${file.name}`}\n onLoad={(e) => setDimensions({ width: e.currentTarget.naturalWidth, height: e.currentTarget.naturalHeight })}\n onError={() => setImageFailed(true)}\n />\n )\n } else if (preview.kind === 'text') {\n previewContent = (\n <pre>\n {preview.text}\n {preview.truncated && '\\n…'}\n </pre>\n )\n } else if (preview.kind === 'loading') {\n previewContent = null\n } else {\n const message =\n preview.kind === 'none'\n ? preview.message\n : 'The browser can’t decode this image. The file may be corrupt or not an image.'\n previewContent = (\n <span className=\"seed-fm-preview-note\">\n <Icon name={imageFailed ? 'imageOff' : KIND_META[kind].icon} size={32} strokeWidth={1.5} />\n {message}\n </span>\n )\n }\n\n const checker = preview.kind === 'image' && !imageFailed && /png|webp|gif|avif|svg/.test(effectiveType(file))\n\n return (\n <aside\n className={['seed-fm-panel', className].filter(Boolean).join(' ')}\n role=\"dialog\"\n aria-modal=\"false\"\n aria-labelledby={headingId}\n >\n <div className=\"seed-fm-panel-head\">\n <h3 id={headingId} title={file.name}>\n {file.name}\n </h3>\n <button\n type=\"button\"\n className=\"seed-fm-btn seed-fm-btn--ghost seed-fm-btn--icon\"\n aria-label=\"Previous file\"\n disabled={!onPrevious}\n onClick={onPrevious ?? undefined}\n >\n <Icon name=\"chevronLeft\" />\n </button>\n <button\n type=\"button\"\n className=\"seed-fm-btn seed-fm-btn--ghost seed-fm-btn--icon\"\n aria-label=\"Next file\"\n disabled={!onNext}\n onClick={onNext ?? undefined}\n >\n <Icon name=\"chevronRight\" />\n </button>\n <button\n ref={closeRef}\n type=\"button\"\n className=\"seed-fm-btn seed-fm-btn--ghost seed-fm-btn--icon\"\n aria-label=\"Close details\"\n onClick={onClose}\n >\n <Icon name=\"x\" />\n </button>\n </div>\n\n <div className=\"seed-fm-panel-body\">\n <div\n className={['seed-fm-preview', checker ? 'seed-fm-thumb--checker' : '', preview.kind === 'loading' ? 'seed-fm-skeleton' : '']\n .filter(Boolean)\n .join(' ')}\n style={kindStyle(kind)}\n >\n {previewContent}\n </div>\n\n <dl className=\"seed-fm-details\">\n <dt>Path</dt>\n <dd>\n <span className=\"seed-fm-path\">\n <code>{file.path}</code>\n <button\n type=\"button\"\n className=\"seed-fm-btn seed-fm-btn--ghost seed-fm-btn--icon\"\n aria-label=\"Copy path\"\n title=\"Copy path\"\n onClick={onCopyPath}\n >\n <Icon name=\"copy\" size={13} />\n </button>\n </span>\n </dd>\n <dt>Size</dt>\n <dd>\n {formatFileSize(file.size)} <small>{file.size.toLocaleString()} bytes</small>\n </dd>\n <dt>Type</dt>\n <dd>\n {effectiveType(file)}\n {file.detectedType && <small> from file contents. File.type is “{file.type}”</small>}\n </dd>\n {dimensions && (\n <>\n <dt>Dimensions</dt>\n <dd>\n {dimensions.width.toLocaleString()} × {dimensions.height.toLocaleString()} px\n </dd>\n </>\n )}\n <dt>Modified</dt>\n <dd>\n {formatDateTime(file.lastModified)} <small>{formatRelativeTime(file.lastModified)}</small>\n </dd>\n </dl>\n\n {variants.length > 0 && (\n <div>\n <div className=\"seed-fm-section-label\">Resized copies · {formatFileSize(variantsSize)}</div>\n <ul className=\"seed-fm-variants\">\n {variants.map((v) => (\n <li key={v.file.path} title={v.file.path}>\n <span>{v.width}w · webp</span>\n <span>{formatFileSize(v.file.size)}</span>\n </li>\n ))}\n </ul>\n </div>\n )}\n </div>\n\n <div className=\"seed-fm-panel-foot\">\n <button type=\"button\" className=\"seed-fm-btn seed-fm-btn--primary\" onClick={onDownload}>\n <Icon name=\"download\" size={15} /> Download\n </button>\n <button\n type=\"button\"\n className={`seed-fm-btn${deleteAction.destructive ? ' seed-fm-btn--danger-text' : ''}`}\n onClick={onDelete}\n >\n <Icon name=\"trash\" size={15} /> {deleteAction.label ?? 'Delete'}\n </button>\n </div>\n </aside>\n )\n}\n","/**\n * Scoped stylesheet for OPFSFilesManager. Injected once into document.head (see\n * docs/adr/0002-component-styling-tokens-and-injected-css.md). Every color comes from a\n * `--seed-*` custom property, so hosts restyle by overriding variables on `.seed-fm`\n * or an ancestor.\n */\n\nexport const STYLE_ELEMENT_ID = 'seed-opfs-files-manager-styles'\nexport const STYLE_LAYER = 'seed-opfs-files-manager'\n\nconst LIGHT_TOKENS = `\n --seed-bg: #fbfcfb;\n --seed-surface: #ffffff;\n --seed-sunken: #f0f3f1;\n --seed-hover: #e9eeeb;\n --seed-fg: #18211d;\n --seed-fg-muted: #5a6861;\n --seed-fg-faint: #8a978f;\n --seed-border: #dfe5e1;\n --seed-border-strong: #c5cfc9;\n --seed-accent: #2c6b59;\n --seed-accent-fg: #ffffff;\n --seed-accent-soft: #e0eee8;\n --seed-danger: #b4332a;\n --seed-danger-fg: #ffffff;\n --seed-danger-soft: #fbe7e4;\n --seed-danger-inverse: #ff9a8f;\n --seed-warn: #8a5208;\n --seed-warn-soft: #fdf1dc;\n --seed-kind-image: #2f7d68;\n --seed-kind-database: #6a4fb3;\n --seed-kind-json: #a2620e;\n --seed-kind-html: #b0413a;\n --seed-kind-text: #3f6f9a;\n --seed-kind-other: #66736c;\n --seed-checker-a: #eef1ef;\n --seed-checker-b: #ffffff;\n --seed-shadow: 0 10px 30px rgb(16 24 20 / 0.14), 0 2px 6px rgb(16 24 20 / 0.08);\n --seed-backdrop: rgb(8 12 10 / 0.42);\n`\n\nconst DARK_TOKENS = `\n --seed-bg: #111816;\n --seed-surface: #171f1c;\n --seed-sunken: #1d2723;\n --seed-hover: #24302b;\n --seed-fg: #e4ebe7;\n --seed-fg-muted: #9daba3;\n --seed-fg-faint: #6f7d76;\n --seed-border: #29342f;\n --seed-border-strong: #3a4842;\n --seed-accent: #6cc0a3;\n --seed-accent-fg: #0c1a15;\n --seed-accent-soft: #1c3a30;\n --seed-danger: #f08277;\n --seed-danger-fg: #2a0d0a;\n --seed-danger-soft: #3b1c19;\n --seed-danger-inverse: #b4332a;\n --seed-warn: #e9b062;\n --seed-warn-soft: #382a14;\n --seed-kind-image: #6cc0a3;\n --seed-kind-database: #b29cf0;\n --seed-kind-json: #e3ad5c;\n --seed-kind-html: #ef8a80;\n --seed-kind-text: #8fb8de;\n --seed-kind-other: #9aa8a0;\n --seed-checker-a: #1d2723;\n --seed-checker-b: #24302b;\n --seed-shadow: 0 12px 34px rgb(0 0 0 / 0.5), 0 2px 6px rgb(0 0 0 / 0.4);\n --seed-backdrop: rgb(0 0 0 / 0.55);\n color-scheme: dark;\n`\n\nconst CHECKER = `background-color: var(--seed-checker-b);\n background-image: conic-gradient(var(--seed-checker-a) 25%, transparent 0 50%, var(--seed-checker-a) 0 75%, transparent 0);`\n\nconst CSS = `\n.seed-fm {\n ${LIGHT_TOKENS}\n --seed-radius-sm: 6px;\n --seed-radius-md: 8px;\n --seed-radius-lg: 12px;\n --seed-font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n color-scheme: light;\n}\n.seed-fm[data-seed-theme=\"dark\"] { ${DARK_TOKENS} }\n@media (prefers-color-scheme: dark) {\n .seed-fm[data-seed-theme=\"system\"] { ${DARK_TOKENS} }\n}\n\n.seed-fm { position: relative; color: var(--seed-fg); background: var(--seed-bg); font-family: inherit; font-size: 14px; line-height: 1.45; }\n.seed-fm *, .seed-fm *::before, .seed-fm *::after { box-sizing: border-box; }\n/* Element defaults use :where() so they have zero specificity and any component class beats them. */\n:where(.seed-fm) :where(button, input, select) { font: inherit; color: inherit; }\n.seed-fm :focus-visible { outline: 2px solid var(--seed-accent); outline-offset: 2px; }\n:where(.seed-fm) :where(svg) { flex: none; display: inline-block; vertical-align: middle; }\n.seed-fm-frame { container: seed-fm / inline-size; }\n\n.seed-fm-header { display: flex; flex-wrap: wrap; gap: 12px 20px; align-items: flex-start; padding: 18px 20px 14px; }\n.seed-fm-titles { flex: 1 1 240px; min-width: 0; }\n.seed-fm-title { font-size: 20px; line-height: 1.25; font-weight: 600; margin: 0; letter-spacing: -0.01em; }\n.seed-fm-description { margin: 2px 0 0; color: var(--seed-fg-muted); font-size: 13px; }\n.seed-fm-usage { flex: 0 1 280px; min-width: 200px; font-size: 12px; color: var(--seed-fg-muted); }\n.seed-fm-usage-row { display: flex; justify-content: space-between; gap: 8px; margin-bottom: 5px; font-variant-numeric: tabular-nums; }\n.seed-fm-usage-row strong { color: var(--seed-fg); font-weight: 600; }\n.seed-fm-meter { height: 6px; border-radius: 99px; background: var(--seed-sunken); overflow: hidden; display: flex; }\n.seed-fm-meter > span { display: block; height: 100%; }\n.seed-fm-legend { display: flex; flex-wrap: wrap; gap: 4px 10px; margin: 6px 0 0; padding: 0; list-style: none; }\n.seed-fm-legend li { display: inline-flex; align-items: center; gap: 5px; font-variant-numeric: tabular-nums; }\n.seed-fm-legend li::before { content: \"\"; width: 7px; height: 7px; border-radius: 2px; background: var(--seed-swatch); }\n\n.seed-fm-btn { display: inline-flex; align-items: center; justify-content: center; gap: 6px; height: 32px; padding: 0 12px; margin: 0; border-radius: var(--seed-radius-md); border: 1px solid var(--seed-border); background: var(--seed-surface); color: var(--seed-fg); cursor: pointer; font-size: 13px; font-weight: 500; white-space: nowrap; text-decoration: none; }\n.seed-fm-btn:hover:not(:disabled) { background: var(--seed-hover); }\n.seed-fm-btn:disabled { opacity: 0.45; cursor: default; }\n.seed-fm-btn--icon { width: 32px; padding: 0; }\n.seed-fm-btn--primary { background: var(--seed-accent); border-color: var(--seed-accent); color: var(--seed-accent-fg); }\n.seed-fm-btn--primary:hover:not(:disabled) { background: var(--seed-accent); filter: brightness(1.08); }\n.seed-fm-btn--danger { background: var(--seed-danger); border-color: var(--seed-danger); color: var(--seed-danger-fg); }\n.seed-fm-btn--danger:hover:not(:disabled) { background: var(--seed-danger); filter: brightness(1.08); }\n.seed-fm-btn--ghost { border-color: transparent; background: transparent; }\n.seed-fm-btn--danger-text { color: var(--seed-danger); }\n.seed-fm-btn[data-busy=\"true\"] svg { animation: seed-fm-spin 0.8s linear infinite; }\n@keyframes seed-fm-spin { to { transform: rotate(360deg); } }\n\n.seed-fm-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 10px; padding: 0 20px 12px; border-bottom: 1px solid var(--seed-border); }\n.seed-fm-crumbs { display: flex; align-items: center; gap: 2px; min-width: 0; flex: 1 1 auto; font-size: 13px; overflow: hidden; margin: 0; padding: 0; list-style: none; }\n.seed-fm-crumbs li { display: inline-flex; align-items: center; min-width: 0; }\n.seed-fm-crumbs button { border: 0; background: none; padding: 4px 6px; border-radius: var(--seed-radius-sm); cursor: pointer; color: var(--seed-fg-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 24ch; }\n.seed-fm-crumbs button:hover { background: var(--seed-hover); color: var(--seed-fg); }\n.seed-fm-crumbs button[aria-current=\"page\"] { color: var(--seed-fg); font-weight: 600; }\n.seed-fm-crumbs svg { color: var(--seed-fg-faint); }\n.seed-fm-search { position: relative; flex: 0 1 220px; min-width: 140px; display: block; }\n.seed-fm-search svg { position: absolute; left: 9px; top: 50%; transform: translateY(-50%); color: var(--seed-fg-faint); pointer-events: none; }\n.seed-fm-search input { width: 100%; height: 32px; margin: 0; padding: 0 10px 0 30px; border-radius: var(--seed-radius-md); border: 1px solid var(--seed-border); background: var(--seed-surface); color: var(--seed-fg); font-size: 13px; }\n.seed-fm-search input::placeholder { color: var(--seed-fg-faint); }\n.seed-fm-select { height: 32px; margin: 0; border-radius: var(--seed-radius-md); border: 1px solid var(--seed-border); background: var(--seed-surface); color: var(--seed-fg); font-size: 13px; padding: 0 8px; }\n.seed-fm-viewtoggle { display: inline-flex; border: 1px solid var(--seed-border); border-radius: var(--seed-radius-md); overflow: hidden; }\n.seed-fm-viewtoggle button { width: 32px; height: 30px; margin: 0; border: 0; background: var(--seed-surface); cursor: pointer; color: var(--seed-fg-muted); display: inline-flex; align-items: center; justify-content: center; }\n.seed-fm-viewtoggle button[aria-pressed=\"true\"] { background: var(--seed-sunken); color: var(--seed-fg); }\n.seed-fm-hint { width: 100%; font-size: 12px; color: var(--seed-fg-faint); margin: 0; }\n.seed-fm-hint svg { margin-right: 6px; vertical-align: -2px; }\n.seed-fm-hint .seed-fm-linkbtn { margin-left: 6px; }\n.seed-fm-linkbtn { border: 0; background: none; color: var(--seed-accent); cursor: pointer; padding: 0; font-size: inherit; text-decoration: underline; text-underline-offset: 2px; }\n\n.seed-fm-body { padding: 16px 20px 24px; }\n.seed-fm-section-label { font-size: 11.5px; font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase; color: var(--seed-fg-faint); margin: 4px 0 8px; }\n.seed-fm-folders { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 8px; margin: 0 0 20px; padding: 0; list-style: none; }\n.seed-fm-folder { width: 100%; display: flex; align-items: center; gap: 10px; padding: 10px 12px; margin: 0; border: 1px solid var(--seed-border); border-radius: var(--seed-radius-md); background: var(--seed-surface); cursor: pointer; text-align: left; min-width: 0; }\n.seed-fm-folder:hover { background: var(--seed-hover); }\n.seed-fm-folder > svg { color: var(--seed-fg-muted); }\n.seed-fm-folder > span { min-width: 0; display: flex; flex-direction: column; }\n.seed-fm-folder-name { font-weight: 600; font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.seed-fm-folder-meta { font-size: 11.5px; color: var(--seed-fg-muted); font-variant-numeric: tabular-nums; }\n.seed-fm-folders li { position: relative; }\n.seed-fm-folders li .seed-fm-folder { padding-right: 40px; }\n.seed-fm-folder-delete.seed-fm-btn { position: absolute; top: 50%; right: 6px; transform: translateY(-50%); width: 28px; height: 28px; color: var(--seed-fg-muted); opacity: 0; }\n.seed-fm-folders li:hover .seed-fm-folder-delete, .seed-fm-folder-delete.seed-fm-btn:focus-visible { opacity: 1; }\n.seed-fm-folder-delete.seed-fm-btn:hover:not(:disabled) { color: var(--seed-danger); }\n@media (hover: none) { .seed-fm-folder-delete.seed-fm-btn { opacity: 1; } }\n\n.seed-fm-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(156px, 1fr)); gap: 14px; margin: 0; padding: 0; list-style: none; }\n.seed-fm-tile { position: relative; border-radius: var(--seed-radius-lg); cursor: pointer; min-width: 0; outline: none; }\n.seed-fm-thumb { position: relative; aspect-ratio: 4 / 3; border-radius: var(--seed-radius-lg); overflow: hidden; background: var(--seed-sunken); border: 1px solid var(--seed-border); display: flex; align-items: center; justify-content: center; color: var(--seed-kind); }\n.seed-fm-thumb--checker { ${CHECKER} background-size: 14px 14px; }\n.seed-fm-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform 0.12s ease; }\n.seed-fm-thumb--checker img { object-fit: contain; }\n.seed-fm-tile:hover .seed-fm-thumb { border-color: var(--seed-border-strong); }\n.seed-fm-tile:focus-visible .seed-fm-thumb { outline: 2px solid var(--seed-accent); outline-offset: 2px; }\n.seed-fm-tile[aria-selected=\"true\"] .seed-fm-thumb { border-color: var(--seed-accent); box-shadow: 0 0 0 2px var(--seed-accent); }\n.seed-fm-tile[aria-selected=\"true\"] .seed-fm-thumb img { transform: scale(0.94); border-radius: 6px; }\n.seed-fm-kind { display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 8px; text-align: center; }\n.seed-fm-ext { font-family: var(--seed-font-mono); font-size: 10.5px; font-weight: 500; letter-spacing: 0.04em; text-transform: uppercase; padding: 1px 6px; border-radius: 4px; background: color-mix(in srgb, var(--seed-kind) 14%, transparent); }\n.seed-fm-kind-error { font-size: 11px; color: var(--seed-fg-faint); }\n.seed-fm-skeleton { background: linear-gradient(90deg, var(--seed-sunken), var(--seed-hover), var(--seed-sunken)); background-size: 200% 100%; animation: seed-fm-shimmer 1.3s ease-in-out infinite; border-color: transparent; }\n@keyframes seed-fm-shimmer { from { background-position: 100% 0; } to { background-position: -100% 0; } }\n.seed-fm-check { position: absolute; top: 8px; left: 8px; width: 22px; height: 22px; margin: 0; border-radius: 6px; border: 1.5px solid rgb(255 255 255 / 0.9); background: rgb(0 0 0 / 0.28); color: #fff; display: flex; align-items: center; justify-content: center; opacity: 0; cursor: pointer; padding: 0; transition: opacity 0.1s; z-index: 1; }\n.seed-fm-tile:hover .seed-fm-check, .seed-fm-tile:focus-visible .seed-fm-check, .seed-fm-check:focus-visible, .seed-fm[data-selecting=\"true\"] .seed-fm-check, .seed-fm-tile[aria-selected=\"true\"] .seed-fm-check { opacity: 1; }\n.seed-fm-tile[aria-selected=\"true\"] .seed-fm-check { background: var(--seed-accent); border-color: var(--seed-accent); color: var(--seed-accent-fg); }\n.seed-fm-badge { position: absolute; bottom: 7px; right: 7px; font-size: 10.5px; font-weight: 600; padding: 2px 6px 2px 5px; border-radius: 5px; background: rgb(10 16 13 / 0.66); color: #fff; display: inline-flex; gap: 4px; align-items: center; }\n.seed-fm-tile-meta { display: block; padding: 7px 2px 0; min-width: 0; }\n.seed-fm-tile-name { display: block; font-size: 13px; font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.seed-fm-tile-sub { display: block; font-size: 11.5px; color: var(--seed-fg-muted); font-variant-numeric: tabular-nums; }\n\n.seed-fm-list-wrap { overflow-x: auto; }\n.seed-fm-list { width: 100%; border-collapse: collapse; font-size: 13px; }\n.seed-fm-list th { text-align: left; font-size: 11.5px; font-weight: 600; color: var(--seed-fg-muted); padding: 6px 10px; border-bottom: 1px solid var(--seed-border); white-space: nowrap; background: none; }\n.seed-fm-list td { padding: 7px 10px; border-bottom: 1px solid var(--seed-border); vertical-align: middle; }\n.seed-fm-row { cursor: pointer; }\n.seed-fm-row:hover td { background: var(--seed-hover); }\n.seed-fm-row[aria-selected=\"true\"] td { background: var(--seed-accent-soft); }\n.seed-fm-num { text-align: right !important; font-variant-numeric: tabular-nums; white-space: nowrap; color: var(--seed-fg-muted); }\n.seed-fm-when { white-space: nowrap; color: var(--seed-fg-muted); }\n.seed-fm-namecell { display: flex; align-items: center; gap: 10px; min-width: 0; }\n.seed-fm-namecell > span { min-width: 0; display: flex; flex-direction: column; }\n.seed-fm-mini { width: 36px; height: 28px; border-radius: 5px; overflow: hidden; flex: none; border: 1px solid var(--seed-border); background: var(--seed-sunken); display: flex; align-items: center; justify-content: center; color: var(--seed-kind); }\n.seed-fm-mini img { width: 100%; height: 100%; object-fit: cover; display: block; }\n.seed-fm-mini.seed-fm-thumb--checker { background-size: 8px 8px; }\n.seed-fm-mini.seed-fm-thumb--checker img { object-fit: contain; }\n.seed-fm-row-name { font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 40ch; }\n.seed-fm-row-open { display: block; border: 0; padding: 0; margin: 0; background: none; color: inherit; cursor: pointer; text-align: left; }\n.seed-fm-visually-hidden { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }\n.seed-fm-row-path { display: block; font-family: var(--seed-font-mono); font-size: 11px; color: var(--seed-fg-faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 52ch; }\n.seed-fm-actions { white-space: nowrap; text-align: right; }\n.seed-fm-actions .seed-fm-btn { opacity: 0; height: 28px; width: 28px; }\n.seed-fm-row:hover .seed-fm-actions .seed-fm-btn, .seed-fm-actions .seed-fm-btn:focus-visible { opacity: 1; }\n.seed-fm-checkbox { width: 16px; height: 16px; margin: 0; accent-color: var(--seed-accent); cursor: pointer; vertical-align: middle; }\n.seed-fm-type { display: inline-flex; align-items: center; gap: 5px; font-size: 11.5px; color: var(--seed-fg-muted); white-space: nowrap; }\n.seed-fm-type::before { content: \"\"; width: 7px; height: 7px; border-radius: 2px; background: var(--seed-kind); }\n.seed-fm-type small { color: var(--seed-fg-faint); font-size: inherit; }\n@container seed-fm (max-width: 720px) {\n .seed-fm-col-type, .seed-fm-col-modified { display: none; }\n .seed-fm-row-path { max-width: 28ch; }\n}\n@container seed-fm (max-width: 520px) {\n .seed-fm-col-size { display: none; }\n .seed-fm-header { padding: 14px 14px 10px; }\n .seed-fm-toolbar { padding: 0 14px 10px; }\n .seed-fm-body { padding: 12px 14px 20px; }\n .seed-fm-grid { grid-template-columns: repeat(auto-fill, minmax(128px, 1fr)); gap: 10px; }\n .seed-fm-usage { flex-basis: 100%; order: 3; }\n .seed-fm-search { flex: 1 1 100%; }\n}\n\n.seed-fm-state { max-width: 400px; margin: 40px auto; text-align: center; display: flex; flex-direction: column; align-items: center; gap: 8px; }\n.seed-fm-state-icon { width: 48px; height: 48px; border-radius: 12px; background: var(--seed-sunken); display: flex; align-items: center; justify-content: center; color: var(--seed-fg-muted); margin-bottom: 4px; }\n.seed-fm-state--error .seed-fm-state-icon { background: var(--seed-danger-soft); color: var(--seed-danger); }\n.seed-fm-state h3, .seed-fm-state p { margin: 0; }\n.seed-fm-state h3 { font-size: 15px; font-weight: 600; }\n.seed-fm-state p { color: var(--seed-fg-muted); font-size: 13px; }\n.seed-fm-state .seed-fm-btn { margin-top: 8px; }\n:where(.seed-fm) :where(code) { font-family: var(--seed-font-mono); font-size: 0.92em; }\n.seed-fm-state code { background: var(--seed-sunken); padding: 1px 5px; border-radius: 4px; }\n\n.seed-fm-batch { position: sticky; bottom: 16px; z-index: 5; width: fit-content; max-width: calc(100% - 24px); margin: 0 auto 16px; display: flex; align-items: center; flex-wrap: wrap; justify-content: center; gap: 6px; padding: 6px 6px 6px 14px; background: var(--seed-fg); color: var(--seed-bg); border-radius: 12px; box-shadow: var(--seed-shadow); }\n.seed-fm-batch-count { font-size: 13px; font-weight: 600; margin-right: 6px; font-variant-numeric: tabular-nums; white-space: nowrap; }\n.seed-fm-batch-count span { font-weight: 400; opacity: 0.7; }\n.seed-fm-batch .seed-fm-btn { background: transparent; border-color: transparent; color: var(--seed-bg); height: 30px; }\n.seed-fm-batch .seed-fm-btn:hover:not(:disabled) { background: color-mix(in srgb, var(--seed-bg) 14%, transparent); }\n.seed-fm-batch .seed-fm-btn--danger-text { color: var(--seed-danger-inverse); }\n\n.seed-fm-panel { position: fixed; top: 0; right: 0; bottom: 0; z-index: 1000; width: min(420px, 100vw); background: var(--seed-surface); color: var(--seed-fg); border-left: 1px solid var(--seed-border); box-shadow: var(--seed-shadow); display: flex; flex-direction: column; }\n.seed-fm-panel-head { display: flex; align-items: center; gap: 4px; padding: 10px 10px 10px 16px; border-bottom: 1px solid var(--seed-border); }\n.seed-fm-panel-head h3 { flex: 1; min-width: 0; margin: 0; font-size: 14px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.seed-fm-panel-body { flex: 1; overflow: auto; padding: 16px; display: flex; flex-direction: column; gap: 16px; }\n.seed-fm-preview { border-radius: var(--seed-radius-md); overflow: hidden; border: 1px solid var(--seed-border); background: var(--seed-sunken); display: flex; align-items: center; justify-content: center; min-height: 180px; color: var(--seed-kind); }\n.seed-fm-preview.seed-fm-thumb--checker { background-size: 16px 16px; }\n.seed-fm-preview img { max-width: 100%; max-height: 320px; display: block; }\n.seed-fm-preview pre { margin: 0; padding: 12px 14px; align-self: stretch; width: 100%; font-family: var(--seed-font-mono); font-size: 11.5px; line-height: 1.55; color: var(--seed-fg); overflow: auto; max-height: 320px; white-space: pre; }\n.seed-fm-preview-note { padding: 28px; text-align: center; color: var(--seed-fg-muted); font-size: 12.5px; display: flex; flex-direction: column; align-items: center; gap: 8px; }\n.seed-fm-preview-note svg { color: var(--seed-kind); }\n.seed-fm-details { display: grid; grid-template-columns: 92px minmax(0, 1fr); gap: 8px 12px; margin: 0; font-size: 13px; }\n.seed-fm-details dt { color: var(--seed-fg-muted); }\n.seed-fm-details dd { margin: 0; min-width: 0; font-variant-numeric: tabular-nums; }\n.seed-fm-details small { color: var(--seed-fg-faint); font-size: 12px; }\n.seed-fm-path { display: flex; gap: 4px; align-items: flex-start; }\n.seed-fm-path code { font-size: 11.5px; word-break: break-all; flex: 1; padding-top: 4px; }\n.seed-fm-path .seed-fm-btn { width: 26px; height: 26px; }\n.seed-fm-variants { display: flex; flex-direction: column; margin: 0; padding: 0; list-style: none; border: 1px solid var(--seed-border); border-radius: var(--seed-radius-md); overflow: hidden; }\n.seed-fm-variants li { display: flex; justify-content: space-between; padding: 6px 10px; font-size: 12.5px; border-bottom: 1px solid var(--seed-border); font-variant-numeric: tabular-nums; }\n.seed-fm-variants li:last-child { border-bottom: 0; }\n.seed-fm-variants li span:last-child { color: var(--seed-fg-muted); }\n.seed-fm-panel-foot { display: flex; gap: 8px; padding: 12px 16px; border-top: 1px solid var(--seed-border); }\n.seed-fm-panel-foot .seed-fm-btn--primary { flex: 1; }\n\n/* margin: auto restates the UA default that centres a modal; Tailwind's preflight resets it to 0. */\n.seed-fm-dialog { margin: auto; border: 1px solid var(--seed-border); border-radius: var(--seed-radius-lg); background: var(--seed-surface); color: var(--seed-fg); box-shadow: var(--seed-shadow); width: min(460px, calc(100vw - 32px)); max-height: calc(100vh - 32px); padding: 0; }\n.seed-fm-dialog::backdrop { background: var(--seed-backdrop); }\n.seed-fm-dialog form { display: flex; flex-direction: column; gap: 12px; margin: 0; padding: 20px; }\n.seed-fm-dialog h3 { margin: 0; font-size: 16px; font-weight: 600; overflow-wrap: anywhere; }\n.seed-fm-dialog p { margin: 0; font-size: 13px; color: var(--seed-fg-muted); }\n.seed-fm-dialog ul { margin: 0; padding: 0; list-style: none; border: 1px solid var(--seed-border); border-radius: var(--seed-radius-md); max-height: 160px; overflow: auto; }\n.seed-fm-dialog li { display: flex; justify-content: space-between; gap: 12px; padding: 6px 10px; font-size: 12.5px; border-bottom: 1px solid var(--seed-border); }\n.seed-fm-dialog li:last-child { border-bottom: 0; }\n.seed-fm-dialog li code { font-size: 11.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }\n.seed-fm-dialog li span { color: var(--seed-fg-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }\n.seed-fm-dialog label { display: flex; gap: 8px; align-items: center; font-size: 13px; cursor: pointer; }\n.seed-fm-dialog-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 4px; }\n.seed-fm-warning { display: flex; gap: 10px; align-items: flex-start; padding: 10px 12px; border-radius: var(--seed-radius-md); background: var(--seed-warn-soft); color: var(--seed-warn); font-size: 12.5px; }\n.seed-fm-warning svg { margin-top: 1px; }\n.seed-fm-irreversible { display: flex; gap: 10px; align-items: flex-start; padding: 10px 12px; border-radius: var(--seed-radius-md); background: var(--seed-danger-soft); color: var(--seed-danger); font-size: 12.5px; }\n.seed-fm-irreversible svg { flex: none; margin-top: 1px; }\n.seed-fm-keepcopy { display: flex; flex-wrap: wrap; gap: 8px 12px; align-items: center; justify-content: space-between; padding: 10px 12px; border: 1px dashed var(--seed-border); border-radius: var(--seed-radius-md); font-size: 12.5px; color: var(--seed-fg-muted); }\n\n.seed-fm-toasts { position: fixed; left: 16px; bottom: 16px; z-index: 1001; display: flex; flex-direction: column; gap: 8px; pointer-events: none; margin: 0; padding: 0; list-style: none; }\n.seed-fm-toast { display: flex; align-items: center; gap: 8px; max-width: min(420px, calc(100vw - 32px)); padding: 9px 12px; background: var(--seed-surface); color: var(--seed-fg); border: 1px solid var(--seed-border); border-radius: var(--seed-radius-md); box-shadow: var(--seed-shadow); font-size: 13px; animation: seed-fm-toast-in 0.18s ease-out; }\n.seed-fm-toast svg { color: var(--seed-accent); }\n.seed-fm-toast--error svg { color: var(--seed-danger); }\n@keyframes seed-fm-toast-in { from { transform: translateY(6px); opacity: 0.4; } }\n\n@media (prefers-reduced-motion: reduce) {\n .seed-fm-skeleton, .seed-fm-btn[data-busy=\"true\"] svg, .seed-fm-toast { animation: none; }\n .seed-fm-thumb img { transition: none; }\n}\n`\n\nexport const OPFS_FILES_MANAGER_CSS = `@layer ${STYLE_LAYER} {\\n${CSS}\\n}`\n\n/** Inject the stylesheet once. No-op during SSR. */\nexport function ensureStylesInjected(): void {\n if (typeof document === 'undefined') return\n if (document.getElementById(STYLE_ELEMENT_ID)) return\n const el = document.createElement('style')\n el.id = STYLE_ELEMENT_ID\n el.textContent = OPFS_FILES_MANAGER_CSS\n document.head.appendChild(el)\n}\n","import { useEffect, useState } from 'react'\n\nexport interface StorageEstimate {\n /** Bytes this origin uses across OPFS, IndexedDB, Cache Storage, etc. */\n usage: number\n /** Bytes this origin may use. */\n quota: number\n}\n\n/**\n * Read `navigator.storage.estimate()`, re-reading whenever `revision` changes.\n * Returns null where the API is unavailable. Browsers round and pad these numbers.\n */\nexport function useStorageEstimate(revision: unknown): StorageEstimate | null {\n const [estimate, setEstimate] = useState<StorageEstimate | null>(null)\n\n useEffect(() => {\n if (typeof navigator === 'undefined' || typeof navigator.storage?.estimate !== 'function') return\n let cancelled = false\n navigator.storage.estimate().then(\n ({ usage, quota }) => {\n if (!cancelled && usage != null && quota != null) setEstimate({ usage, quota })\n },\n () => {},\n )\n return () => {\n cancelled = true\n }\n }, [revision])\n\n return estimate\n}\n","/**\n * Minimal store-only (uncompressed) ZIP writer. Used so \"download selected\" produces one\n * file instead of N downloads, which browsers block after the first. Most OPFS content\n * (JPEG, WebP, SQLite pages) doesn't compress well, so skipping deflate costs little.\n * No ZIP64: the archive must stay under 4 GB and 65,535 entries.\n */\n\nlet crcTable: Uint32Array | null = null\n\nfunction getCrcTable(): Uint32Array {\n if (crcTable) return crcTable\n crcTable = new Uint32Array(256)\n for (let n = 0; n < 256; n++) {\n let c = n\n for (let k = 0; k < 8; k++) c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1\n crcTable[n] = c >>> 0\n }\n return crcTable\n}\n\nexport function crc32(data: Uint8Array): number {\n const table = getCrcTable()\n let crc = 0xffffffff\n for (let i = 0; i < data.length; i++) crc = table[(crc ^ data[i]) & 0xff] ^ (crc >>> 8)\n return (crc ^ 0xffffffff) >>> 0\n}\n\nfunction dosDateTime(timestamp: number): { time: number; date: number } {\n const d = new Date(timestamp)\n const year = Math.max(1980, d.getFullYear())\n return {\n time: (d.getHours() << 11) | (d.getMinutes() << 5) | Math.floor(d.getSeconds() / 2),\n date: ((year - 1980) << 9) | ((d.getMonth() + 1) << 5) | d.getDate(),\n }\n}\n\nexport interface ZipInput {\n /** Path inside the archive, '/'-separated. */\n name: string\n data: Blob\n lastModified?: number\n}\n\nconst UTF8_FLAG = 0x0800\nconst MAX_UINT32 = 0xffffffff\n\nexport async function createZip(inputs: ZipInput[]): Promise<Blob> {\n if (inputs.length > 0xffff) throw new Error('Too many files for a ZIP archive (max 65,535).')\n const encoder = new TextEncoder()\n const parts: BlobPart[] = []\n const central: Uint8Array<ArrayBuffer>[] = []\n let offset = 0\n\n for (const input of inputs) {\n const name = encoder.encode(input.name)\n const bytes = new Uint8Array(await input.data.arrayBuffer())\n const crc = crc32(bytes)\n const { time, date } = dosDateTime(input.lastModified ?? Date.now())\n if (offset + bytes.length + 30 + name.length > MAX_UINT32) {\n throw new Error('Selection is too large for a ZIP archive (max 4 GB).')\n }\n\n const local = new DataView(new ArrayBuffer(30))\n local.setUint32(0, 0x04034b50, true)\n local.setUint16(4, 20, true)\n local.setUint16(6, UTF8_FLAG, true)\n local.setUint16(8, 0, true)\n local.setUint16(10, time, true)\n local.setUint16(12, date, true)\n local.setUint32(14, crc, true)\n local.setUint32(18, bytes.length, true)\n local.setUint32(22, bytes.length, true)\n local.setUint16(26, name.length, true)\n local.setUint16(28, 0, true)\n parts.push(local.buffer, name, bytes)\n\n const header = new DataView(new ArrayBuffer(46))\n header.setUint32(0, 0x02014b50, true)\n header.setUint16(4, 20, true)\n header.setUint16(6, 20, true)\n header.setUint16(8, UTF8_FLAG, true)\n header.setUint16(10, 0, true)\n header.setUint16(12, time, true)\n header.setUint16(14, date, true)\n header.setUint32(16, crc, true)\n header.setUint32(20, bytes.length, true)\n header.setUint32(24, bytes.length, true)\n header.setUint16(28, name.length, true)\n header.setUint32(42, offset, true)\n const entry = new Uint8Array(46 + name.length)\n entry.set(new Uint8Array(header.buffer), 0)\n entry.set(name, 46)\n central.push(entry)\n\n offset += 30 + name.length + bytes.length\n }\n\n const centralSize = central.reduce((n, c) => n + c.length, 0)\n const end = new DataView(new ArrayBuffer(22))\n end.setUint32(0, 0x06054b50, true)\n end.setUint16(8, inputs.length, true)\n end.setUint16(10, inputs.length, true)\n end.setUint32(12, centralSize, true)\n end.setUint32(16, offset, true)\n\n return new Blob([...parts, ...central, end.buffer], { type: 'application/zip' })\n}\n","import React, { useCallback, useEffect, useInsertionEffect, useMemo, useRef, useState } from 'react'\nimport { useOPFSFiles, type OPFSFile } from '../useOPFSFiles'\nimport { deleteOPFSEntry, getOPFSFile, isOPFSSupported, removeEmptyOPFSDirectories } from '../opfsPaths'\nimport { DeleteDialog, FolderDeleteDialog } from './DeleteDialog'\nimport { FileGrid } from './FileGrid'\nimport { FileList } from './FileList'\nimport { KIND_META } from './FileThumbnail'\nimport {\n buildEntries,\n defaultDeleteWarning,\n fileKind,\n listDirectory,\n parseImageVariant,\n sizeByKind,\n type FileEntry,\n type FileKind,\n type FolderSummary,\n type SortKey,\n} from './fileModel'\nimport { formatFileSize } from './format'\nimport { Icon } from './icons'\nimport { PreviewPanel } from './PreviewPanel'\nimport { ensureStylesInjected } from './styles'\nimport type {\n OPFSFilesManagerNotice,\n OPFSFilesManagerNotifyTone,\n OPFSFilesManagerProps,\n OPFSFilesManagerSlot,\n OPFSFilesManagerView,\n ResolvedDeleteAction,\n} from './types'\nimport { useStorageEstimate, type StorageEstimate } from './useStorageEstimate'\nimport { createZip } from './zip'\n\nconst DEFAULT_DESCRIPTION = 'Everything this app stores in the browser’s private file system (OPFS).'\nconst TOAST_MS = 4000\n\nconst errorMessage = (err: unknown) => (err instanceof Error ? err.message : String(err))\nconst plural = (n: number, one: string, many = `${one}s`) => `${n} ${n === 1 ? one : many}`\nconst normalizeDir = (path?: string) => (path ?? '').split('/').filter(Boolean).join('/')\n\nfunction triggerBrowserDownload(name: string, blob: Blob) {\n const url = URL.createObjectURL(blob)\n const a = document.createElement('a')\n a.href = url\n a.download = name\n document.body.appendChild(a)\n a.click()\n a.remove()\n setTimeout(() => URL.revokeObjectURL(url), 1000)\n}\n\ninterface Toast {\n id: number\n message: string\n tone: OPFSFilesManagerNotifyTone\n}\n\nfunction useToasts() {\n const [toasts, setToasts] = useState<Toast[]>([])\n const timers = useRef(new Set<ReturnType<typeof setTimeout>>())\n useEffect(() => {\n const pending = timers.current\n return () => pending.forEach(clearTimeout)\n }, [])\n const showToast = useCallback((message: string, tone: Toast['tone'] = 'success') => {\n const id = Date.now() + Math.random()\n setToasts((t) => [...t, { id, message, tone }])\n const timer = setTimeout(() => {\n timers.current.delete(timer)\n setToasts((t) => t.filter((x) => x.id !== id))\n }, TOAST_MS)\n timers.current.add(timer)\n }, [])\n return { toasts, showToast }\n}\n\nconst METER_KINDS: FileKind[] = ['image', 'database', 'json', 'html', 'text', 'other']\n\nfunction UsageMeter({ files, estimate }: { files: OPFSFile[]; estimate: StorageEstimate | null }) {\n const byKind = sizeByKind(files)\n const filesTotal = files.reduce((n, f) => n + f.size, 0)\n // estimate() covers the whole origin and is padded, so it can differ from the sum of file sizes.\n const otherSiteData = estimate ? Math.max(0, estimate.usage - filesTotal) : 0\n const total = filesTotal + otherSiteData\n const segments = [\n ...METER_KINDS.filter((k) => byKind[k] > 0).map((k) => ({\n label: KIND_META[k].label,\n size: byKind[k],\n color: KIND_META[k].color,\n })),\n ...(otherSiteData > 0\n ? [{ label: 'Other site data', size: otherSiteData, color: 'var(--seed-border-strong)' }]\n : []),\n ]\n\n return (\n <div className=\"seed-fm-usage\" title=\"From navigator.storage.estimate(). Browsers round these numbers.\">\n <div className=\"seed-fm-usage-row\">\n <span>\n <strong>{formatFileSize(estimate ? estimate.usage : filesTotal)}</strong>{' '}\n {estimate ? 'used by this site' : `in ${plural(files.length, 'file')}`}\n </span>\n {estimate && <span>{formatFileSize(estimate.quota)} available</span>}\n </div>\n {total > 0 && (\n <>\n <div className=\"seed-fm-meter\" role=\"img\" aria-label=\"Storage used, by file type\">\n {segments.map((s) => (\n <span key={s.label} style={{ width: `${(s.size / total) * 100}%`, background: s.color }} />\n ))}\n </div>\n <ul className=\"seed-fm-legend\">\n {segments.map((s) => (\n <li key={s.label} style={{ '--seed-swatch': s.color } as React.CSSProperties}>\n {s.label} {formatFileSize(s.size)}\n </li>\n ))}\n </ul>\n </>\n )}\n </div>\n )\n}\n\nfunction StateMessage({\n icon,\n title,\n children,\n tone,\n action,\n}: {\n icon: React.ComponentProps<typeof Icon>['name']\n title: string\n children: React.ReactNode\n tone?: 'error'\n action?: React.ReactNode\n}) {\n return (\n <div className={`seed-fm-state${tone === 'error' ? ' seed-fm-state--error' : ''}`} role={tone === 'error' ? 'alert' : undefined}>\n <span className=\"seed-fm-state-icon\">\n <Icon name={icon} size={22} />\n </span>\n <h3>{title}</h3>\n <p>{children}</p>\n {action}\n </div>\n )\n}\n\n/**\n * Browse, preview, download, and delete files stored in OPFS (the Origin Private File\n * System). Useful for debugging and for letting people manage what an app stores locally.\n *\n * @example\n * ```tsx\n * <OPFSFilesManager\n * rootPath=\"app-files\"\n * onAfterDelete={(paths) => {\n * if (paths.some(isSeedDbPath)) {\n * clearSeedDependentData()\n * window.location.reload()\n * }\n * }}\n * onDownload={async (file, blob) => {\n * if (window.electron?.downloadFile) {\n * await window.electron.downloadFile({ data: await blob.arrayBuffer(), filename: file.name })\n * }\n * }}\n * />\n * ```\n */\nexport function OPFSFilesManager({\n rootPath,\n filter,\n onBeforeDelete,\n onAfterDelete,\n onDownload,\n title = 'Files',\n description = DEFAULT_DESCRIPTION,\n headingLevel = 2,\n theme = 'system',\n className,\n classNames = {},\n defaultView,\n groupImageVariants = true,\n deleteWarning = defaultDeleteWarning,\n confirmDelete,\n deleteAction,\n onNotify,\n}: OPFSFilesManagerProps) {\n useInsertionEffect(() => {\n if (theme !== 'none') ensureStylesInjected()\n }, [theme])\n\n const rootDir = normalizeDir(rootPath)\n const rootLabel = rootDir ? rootDir.split('/').pop()! : 'OPFS'\n const cn = (slot: OPFSFilesManagerSlot, base: string) => [base, classNames[slot]].filter(Boolean).join(' ')\n\n const { files: rawFiles, isLoading, hasLoaded, error, errorName, refetch } = useOPFSFiles({ rootPath })\n const files = useMemo(() => (filter ? rawFiles.filter(filter) : rawFiles), [rawFiles, filter])\n const estimate = useStorageEstimate(rawFiles)\n\n const [grouped, setGrouped] = useState(groupImageVariants)\n useEffect(() => setGrouped(groupImageVariants), [groupImageVariants])\n const entries = useMemo(() => buildEntries(files, grouped), [files, grouped])\n\n const [cwd, setCwd] = useState(rootDir)\n useEffect(() => setCwd(rootDir), [rootDir])\n const [query, setQuery] = useState('')\n const [sort, setSort] = useState<SortKey>('name')\n const [viewChoice, setViewChoice] = useState<OPFSFilesManagerView | undefined>(defaultView)\n const [selected, setSelected] = useState<ReadonlySet<string>>(() => new Set())\n const anchorRef = useRef<number | null>(null)\n const [panelPath, setPanelPath] = useState<string | null>(null)\n const panelOpenerRef = useRef<HTMLElement | null>(null)\n const [pendingDelete, setPendingDelete] = useState<FileEntry[] | null>(null)\n const [pendingFolder, setPendingFolder] = useState<FolderSummary | null>(null)\n const [busy, setBusy] = useState(false)\n const { toasts, showToast } = useToasts()\n const notify = (message: string, tone: OPFSFilesManagerNotifyTone, notice: OPFSFilesManagerNotice) => {\n if (onNotify) onNotify(message, tone, notice)\n else showToast(message, tone)\n }\n\n const listing = useMemo(() => listDirectory(entries, cwd, { query, sort }), [entries, cwd, query, sort])\n const visible = listing.entries\n const imageShare = visible.length ? visible.filter((e) => fileKind(e.file) === 'image').length / visible.length : 0\n const view: OPFSFilesManagerView = viewChoice ?? (imageShare >= 0.5 ? 'grid' : 'list')\n\n const entriesByPath = useMemo(() => new Map(entries.map((e) => [e.file.path, e])), [entries])\n const selectedEntries = useMemo(\n () => [...selected].map((p) => entriesByPath.get(p)).filter((e): e is FileEntry => !!e),\n [selected, entriesByPath],\n )\n const panelEntry = panelPath ? entriesByPath.get(panelPath) ?? null : null\n const panelIndex = panelEntry ? visible.indexOf(panelEntry) : -1\n\n // Keep the selection across refreshes, minus anything that no longer exists.\n useEffect(() => {\n setSelected((prev) => {\n const next = new Set([...prev].filter((p) => entriesByPath.has(p)))\n return next.size === prev.size ? prev : next\n })\n }, [entriesByPath])\n\n const navigate = (dir: string) => {\n setCwd(dir)\n setQuery('')\n setSelected(new Set())\n anchorRef.current = null\n setPanelPath(null)\n }\n\n const toggle = (entry: FileEntry, index: number, range: boolean) => {\n const anchor = anchorRef.current\n setSelected((prev) => {\n const next = new Set(prev)\n if (range && anchor != null) {\n const [from, to] = anchor < index ? [anchor, index] : [index, anchor]\n for (const e of visible.slice(from, to + 1)) next.add(e.file.path)\n } else if (next.has(entry.file.path)) {\n next.delete(entry.file.path)\n } else {\n next.add(entry.file.path)\n }\n return next\n })\n if (!range || anchor == null) anchorRef.current = index\n }\n\n const openPanel = (path: string) => {\n if (!panelPath) panelOpenerRef.current = document.activeElement as HTMLElement | null\n setPanelPath(path)\n }\n\n const closePanel = () => {\n setPanelPath(null)\n panelOpenerRef.current?.focus?.()\n panelOpenerRef.current = null\n }\n\n const activate = (entry: FileEntry, index: number, event: React.MouseEvent | React.KeyboardEvent) => {\n const modified = event.shiftKey || event.metaKey || event.ctrlKey\n if (selected.size > 0 || modified) toggle(entry, index, event.shiftKey)\n else openPanel(entry.file.path)\n }\n\n const toggleAllVisible = () => {\n const allSelected = visible.every((e) => selected.has(e.file.path))\n setSelected((prev) => {\n const next = new Set(prev)\n for (const e of visible) {\n if (allSelected) next.delete(e.file.path)\n else next.add(e.file.path)\n }\n return next\n })\n }\n\n const relativeToRoot = (path: string) => (rootDir && path.startsWith(`${rootDir}/`) ? path.slice(rootDir.length + 1) : path)\n\n const saveBlob = async (file: OPFSFile, blob: Blob) => {\n if (onDownload) await onDownload(file, blob)\n else triggerBrowserDownload(file.name, blob)\n }\n\n /**\n * Save one file as-is, or several as a .zip. A folder is always zipped, with paths\n * starting at the folder's name. Resolves true once saved.\n */\n const downloadFiles = async (list: OPFSFile[], folder?: FolderSummary): Promise<boolean> => {\n if (list.length === 0) return false\n setBusy(true)\n try {\n if (list.length === 1 && !folder) {\n const [file] = list\n await saveBlob(file, await getOPFSFile(file.path))\n return true\n }\n const folderParent = folder ? folder.path.slice(0, folder.path.length - folder.name.length) : ''\n const inputs = await Promise.all(\n list.map(async (file) => ({\n name: folder ? file.path.slice(folderParent.length) : relativeToRoot(file.path),\n data: await getOPFSFile(file.path),\n lastModified: file.lastModified,\n })),\n )\n const zip = await createZip(inputs)\n const name = `${folder ? folder.name : rootLabel}-${new Date().toISOString().slice(0, 10)}.zip`\n await saveBlob({ name, path: name, size: zip.size, type: 'application/zip', lastModified: Date.now() }, zip)\n notify(`Saved ${name} with ${plural(list.length, 'file')}`, 'success', {\n kind: 'download',\n paths: list.map((f) => f.path),\n fileName: name,\n })\n return true\n } catch (err) {\n notify(`Couldn’t download: ${errorMessage(err)}`, 'error', {\n kind: 'download',\n paths: list.map((f) => f.path),\n error: errorMessage(err),\n })\n return false\n } finally {\n setBusy(false)\n }\n }\n const download = (list: FileEntry[]) => downloadFiles(list.map((e) => e.file))\n\n const filesToDelete = (list: FileEntry[], includeVariants: boolean) =>\n list.flatMap((e) => [e.file, ...(includeVariants ? e.variants.map((v) => v.file) : [])])\n\n const resolveDeleteAction = (targets: OPFSFile[]): ResolvedDeleteAction => {\n const action = deleteAction?.(targets)\n return { label: action?.label, destructive: action?.destructive !== false }\n }\n const deleteActionFor = (list: FileEntry[]) => resolveDeleteAction(filesToDelete(list, true))\n\n /**\n * Delete `targets` one by one so onBeforeDelete can keep any of them. With `folder`, then\n * removes the folders left empty; any that still hold kept, failed or hidden files stay.\n */\n const performDelete = async (targets: OPFSFile[], folder?: FolderSummary) => {\n setBusy(true)\n const deleted: string[] = []\n const failed: { path: string; error: string }[] = []\n const skipped: string[] = []\n let folderRemoved = false\n try {\n const root = await navigator.storage.getDirectory()\n for (const file of targets) {\n if (onBeforeDelete && !(await onBeforeDelete(file))) {\n skipped.push(file.path)\n continue\n }\n try {\n await deleteOPFSEntry(file.path, root)\n deleted.push(file.path)\n } catch (err) {\n failed.push({ path: file.path, error: errorMessage(err) })\n }\n }\n if (folder) {\n try {\n folderRemoved = await removeEmptyOPFSDirectories(folder.path, root)\n } catch (err) {\n failed.push({ path: folder.path, error: errorMessage(err) })\n }\n }\n } catch (err) {\n failed.push({ path: '', error: errorMessage(err) })\n }\n\n setSelected((prev) => {\n const next = new Set(prev)\n deleted.forEach((p) => next.delete(p))\n return next\n })\n if (panelPath && deleted.includes(panelPath)) setPanelPath(null)\n await refetch()\n setBusy(false)\n\n const parts = [\n folderRemoved\n ? `Deleted the ${folder!.name} folder (${plural(deleted.length, 'file')})`\n : deleted.length > 0\n ? `Deleted ${plural(deleted.length, 'file')}`\n : 'Nothing deleted',\n ]\n if (skipped.length) parts.push(`${skipped.length} kept by onBeforeDelete`)\n if (failed.length) {\n const reasons = failed.map((f) => (f.path ? `${f.path.split('/').pop()}: ${f.error}` : f.error))\n parts.push(`${failed.length} failed (${reasons.join('; ')})`)\n }\n if (folder && !folderRemoved && !failed.length) parts.push(`The ${folder.name} folder still has files, so it was kept`)\n notify(parts.join('. '), failed.length ? 'error' : 'success', {\n kind: 'delete',\n deleted,\n skipped,\n failed,\n ...(folder && { folder: folder.path, folderRemoved }),\n })\n\n if (deleted.length > 0) await onAfterDelete?.(deleted)\n }\n\n const requestDelete = async (list: FileEntry[]) => {\n if (list.length === 0) return\n if (!confirmDelete) {\n setPendingDelete(list)\n return\n }\n const targets = filesToDelete(list, true)\n if (await confirmDelete(targets)) await performDelete(targets)\n }\n\n // Raw files, not entries: grouped resized copies live in this folder too.\n const filesInFolder = (folder: FolderSummary) => files.filter((f) => f.path.startsWith(`${folder.path}/`))\n\n const requestFolderDelete = async (folder: FolderSummary) => {\n if (!confirmDelete) {\n setPendingFolder(folder)\n return\n }\n const targets = filesInFolder(folder)\n if (await confirmDelete(targets)) await performDelete(targets, folder)\n }\n\n const copyPath = (path: string) => {\n const done = () => notify('Path copied', 'success', { kind: 'copy-path', path })\n const fail = (err?: unknown) =>\n notify('Couldn’t copy. Select the path text instead.', 'error', {\n kind: 'copy-path',\n path,\n error: err === undefined ? 'Clipboard unavailable' : errorMessage(err),\n })\n try {\n navigator.clipboard.writeText(path).then(done, fail)\n } catch (err) {\n fail(err)\n }\n }\n\n const onKeyDown = (e: React.KeyboardEvent) => {\n if (pendingDelete || pendingFolder) return\n const inField = e.target instanceof HTMLInputElement || e.target instanceof HTMLSelectElement\n if (e.key === 'Escape') {\n if (panelPath) closePanel()\n else if (selected.size > 0) setSelected(new Set())\n else return\n e.preventDefault()\n return\n }\n if (panelEntry && !inField && (e.key === 'ArrowLeft' || e.key === 'ArrowRight')) {\n const next = visible[panelIndex + (e.key === 'ArrowRight' ? 1 : -1)]\n if (next) setPanelPath(next.file.path)\n e.preventDefault()\n }\n }\n\n const hasVariants = useMemo(() => files.some((f) => parseImageVariant(f.path)), [files])\n const inImagesFolder = /(^|\\/)images(\\/\\d+)?$/.test(cwd)\n const crumbs = [\n { label: rootLabel, path: rootDir },\n ...(cwd.length > rootDir.length ? cwd.slice(rootDir ? rootDir.length + 1 : 0).split('/') : []).map((name, i, all) => ({\n label: name,\n path: [rootDir, ...all.slice(0, i + 1)].filter(Boolean).join('/'),\n })),\n ]\n const Heading = `h${headingLevel}` as 'h2'\n const selectedSize = selectedEntries.reduce((n, e) => n + e.file.size, 0)\n\n let body: React.ReactNode\n if (!isOPFSSupported()) {\n body = (\n <StateMessage icon=\"triangleAlert\" tone=\"error\" title=\"This browser doesn’t support private file storage\">\n OPFS isn’t available here. Some private browsing windows and older browsers turn it off. Try a current version of\n Chrome, Edge, Firefox, or Safari in a normal window.\n </StateMessage>\n )\n } else if (error) {\n const retry = (\n <button type=\"button\" className=\"seed-fm-btn\" onClick={() => refetch()}>\n <Icon name=\"refresh\" size={15} /> Try again\n </button>\n )\n body =\n errorName === 'NotFoundError' && rootDir ? (\n <StateMessage icon=\"triangleAlert\" tone=\"error\" title={`Can’t open ${rootDir}`} action={retry}>\n The folder <code>{rootDir}</code> doesn’t exist in this browser’s private storage yet. Check the{' '}\n <code>rootPath</code> prop, or open the app once so the SDK creates it.\n </StateMessage>\n ) : (\n <StateMessage icon=\"triangleAlert\" tone=\"error\" title=\"Can’t read private storage\" action={retry}>\n {error}\n </StateMessage>\n )\n } else if (!hasLoaded) {\n body = (\n <ul className={cn('grid', 'seed-fm-grid')} aria-busy=\"true\" aria-label=\"Loading files\">\n {Array.from({ length: 8 }, (_, i) => (\n <li key={i} className=\"seed-fm-tile\">\n <span className=\"seed-fm-thumb seed-fm-skeleton\" />\n </li>\n ))}\n </ul>\n )\n } else if (entries.length === 0) {\n body = (\n <StateMessage icon=\"folderOpen\" title={`No files in ${rootLabel} yet`}>\n Files show up here when the app saves images, JSON, or HTML to this browser’s private storage, for example after\n you add an image to a post or sync content from Arweave.\n </StateMessage>\n )\n } else if (listing.folders.length === 0 && visible.length === 0) {\n body = query ? (\n <StateMessage\n icon=\"search\"\n title={`No matches for “${query}”`}\n action={\n <button type=\"button\" className=\"seed-fm-btn\" onClick={() => setQuery('')}>\n Clear search\n </button>\n }\n >\n Search covers file names and paths in {crumbs[crumbs.length - 1].label} and every folder inside it.\n </StateMessage>\n ) : (\n <StateMessage icon=\"folderOpen\" title=\"This folder is empty\">\n Everything in it has been deleted or filtered out.\n </StateMessage>\n )\n } else {\n body = (\n <>\n {listing.folders.length > 0 && (\n <>\n <div className=\"seed-fm-section-label\">Folders</div>\n <ul className=\"seed-fm-folders\">\n {listing.folders.map((folder) => (\n <li key={folder.path}>\n <button type=\"button\" className={cn('folder', 'seed-fm-folder')} onClick={() => navigate(folder.path)}>\n <Icon name=\"folder\" size={20} strokeWidth={1.75} />\n <span>\n <span className=\"seed-fm-folder-name\">{folder.name}</span>\n <span className=\"seed-fm-folder-meta\">\n {plural(folder.fileCount, 'file')} · {formatFileSize(folder.size)}\n </span>\n </span>\n </button>\n <button\n type=\"button\"\n className=\"seed-fm-btn seed-fm-btn--ghost seed-fm-btn--icon seed-fm-folder-delete\"\n aria-label={`Delete folder ${folder.name}`}\n title=\"Delete folder\"\n disabled={busy}\n onClick={() => requestFolderDelete(folder)}\n >\n <Icon name=\"trash\" size={15} />\n </button>\n </li>\n ))}\n </ul>\n </>\n )}\n {visible.length > 0 && (\n <>\n <div className=\"seed-fm-section-label\">{query ? plural(visible.length, 'result') : 'Files'}</div>\n {view === 'grid' ? (\n <FileGrid\n entries={visible}\n selected={selected}\n onActivate={activate}\n onToggle={toggle}\n className={classNames.grid}\n tileClassName={classNames.tile}\n />\n ) : (\n <FileList\n entries={visible}\n selected={selected}\n cwd={cwd}\n onActivate={activate}\n onToggle={toggle}\n onToggleAll={toggleAllVisible}\n onDownload={(e) => download([e])}\n onDelete={(e) => requestDelete([e])}\n deleteActionFor={(e) => deleteActionFor([e])}\n className={classNames.list}\n rowClassName={classNames.row}\n />\n )}\n </>\n )}\n </>\n )\n }\n\n const pendingFiles = pendingDelete ? filesToDelete(pendingDelete, true) : []\n const pendingFolderFiles = pendingFolder ? filesInFolder(pendingFolder) : []\n const batchDeleteAction = selected.size > 0 ? deleteActionFor(selectedEntries) : null\n\n return (\n <div\n className={[cn('root', 'seed-fm'), className].filter(Boolean).join(' ')}\n data-seed-theme={theme === 'none' ? undefined : theme}\n data-selecting={selected.size > 0 || undefined}\n onKeyDown={onKeyDown}\n >\n <div className=\"seed-fm-frame\">\n <header className={cn('header', 'seed-fm-header')}>\n <div className=\"seed-fm-titles\">\n <Heading className=\"seed-fm-title\">{title}</Heading>\n {description && <p className=\"seed-fm-description\">{description}</p>}\n </div>\n {hasLoaded && !error && <UsageMeter files={files} estimate={estimate} />}\n <button\n type=\"button\"\n className=\"seed-fm-btn seed-fm-btn--icon\"\n onClick={() => refetch()}\n aria-label=\"Refresh\"\n title=\"Refresh\"\n data-busy={isLoading && hasLoaded}\n >\n <Icon name=\"refresh\" size={15} />\n </button>\n </header>\n\n {hasLoaded && !error && entries.length > 0 && (\n <div className={cn('toolbar', 'seed-fm-toolbar')}>\n <nav aria-label=\"Folder\">\n <ol className=\"seed-fm-crumbs\">\n {crumbs.map((c, i) => (\n <li key={c.path || '/'}>\n {i > 0 && <Icon name=\"chevronRight\" size={14} />}\n <button\n type=\"button\"\n onClick={() => navigate(c.path)}\n aria-current={i === crumbs.length - 1 ? 'page' : undefined}\n >\n {c.label}\n </button>\n </li>\n ))}\n </ol>\n </nav>\n <label className=\"seed-fm-search\">\n <Icon name=\"search\" size={14} />\n <input\n type=\"search\"\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder=\"Search names and paths\"\n aria-label=\"Search files\"\n />\n </label>\n <select\n className=\"seed-fm-select\"\n value={sort}\n onChange={(e) => setSort(e.target.value as SortKey)}\n aria-label=\"Sort by\"\n >\n <option value=\"name\">Name</option>\n <option value=\"modified\">Recently modified</option>\n <option value=\"size\">Largest</option>\n </select>\n <div className=\"seed-fm-viewtoggle\" role=\"group\" aria-label=\"View\">\n <button type=\"button\" aria-pressed={view === 'grid'} aria-label=\"Grid view\" onClick={() => setViewChoice('grid')}>\n <Icon name=\"layoutGrid\" size={15} />\n </button>\n <button type=\"button\" aria-pressed={view === 'list'} aria-label=\"List view\" onClick={() => setViewChoice('list')}>\n <Icon name=\"list\" size={15} />\n </button>\n </div>\n {hasVariants && inImagesFolder && (\n <p className=\"seed-fm-hint\">\n <Icon name=\"layers\" size={13} />\n {grouped\n ? 'Resized copies are grouped with their original image.'\n : 'Resized copies are shown in their size folders.'}\n <button\n type=\"button\"\n className=\"seed-fm-linkbtn\"\n onClick={() => {\n setGrouped(!grouped)\n setSelected(new Set())\n }}\n >\n {grouped ? 'Show as folders' : 'Group with originals'}\n </button>\n </p>\n )}\n </div>\n )}\n\n <div className={cn('body', 'seed-fm-body')}>{body}</div>\n\n {batchDeleteAction && (\n <div className={cn('batchBar', 'seed-fm-batch')} role=\"toolbar\" aria-label=\"Selected files\">\n <span className=\"seed-fm-batch-count\" aria-live=\"polite\">\n {selected.size} selected <span>· {formatFileSize(selectedSize)}</span>\n </span>\n <button type=\"button\" className=\"seed-fm-btn\" disabled={busy} onClick={() => download(selectedEntries)}>\n <Icon name=\"download\" size={15} /> {selected.size > 1 ? 'Download .zip' : 'Download'}\n </button>\n <button\n type=\"button\"\n className={`seed-fm-btn${batchDeleteAction.destructive ? ' seed-fm-btn--danger-text' : ''}`}\n disabled={busy}\n onClick={() => requestDelete(selectedEntries)}\n >\n <Icon name=\"trash\" size={15} /> {batchDeleteAction.label ?? 'Delete'}\n </button>\n <button\n type=\"button\"\n className=\"seed-fm-btn seed-fm-btn--icon\"\n aria-label=\"Clear selection\"\n onClick={() => setSelected(new Set())}\n >\n <Icon name=\"x\" size={15} />\n </button>\n </div>\n )}\n </div>\n\n {panelEntry && (\n <PreviewPanel\n key=\"panel\"\n entry={panelEntry}\n className={classNames.panel}\n onClose={closePanel}\n onPrevious={panelIndex > 0 ? () => setPanelPath(visible[panelIndex - 1].file.path) : null}\n onNext={\n panelIndex >= 0 && panelIndex < visible.length - 1\n ? () => setPanelPath(visible[panelIndex + 1].file.path)\n : null\n }\n onDownload={() => download([panelEntry])}\n onDelete={() => requestDelete([panelEntry])}\n deleteAction={deleteActionFor([panelEntry])}\n onCopyPath={() => copyPath(panelEntry.file.path)}\n />\n )}\n\n {pendingDelete && (\n <DeleteDialog\n entries={pendingDelete}\n rootPath={rootDir}\n warning={deleteWarning(pendingFiles)}\n deleteActionFor={(includeVariants) => resolveDeleteAction(filesToDelete(pendingDelete, includeVariants))}\n className={classNames.dialog}\n onCancel={() => setPendingDelete(null)}\n onConfirm={(includeVariants) => {\n const targets = filesToDelete(pendingDelete, includeVariants)\n setPendingDelete(null)\n void performDelete(targets)\n }}\n />\n )}\n\n {pendingFolder && (\n <FolderDeleteDialog\n folder={pendingFolder}\n files={pendingFolderFiles}\n hiddenCount={rawFiles.filter((f) => f.path.startsWith(`${pendingFolder.path}/`)).length - pendingFolderFiles.length}\n warning={deleteWarning(pendingFolderFiles)}\n action={resolveDeleteAction(pendingFolderFiles)}\n busy={busy}\n className={classNames.dialog}\n onDownload={() => downloadFiles(pendingFolderFiles, pendingFolder)}\n onCancel={() => setPendingFolder(null)}\n onConfirm={() => {\n setPendingFolder(null)\n void performDelete(pendingFolderFiles, pendingFolder)\n }}\n />\n )}\n\n {!onNotify && (\n <ul className=\"seed-fm-toasts\" aria-live=\"polite\">\n {toasts.map((t) => (\n <li key={t.id} className={cn('toast', `seed-fm-toast${t.tone === 'error' ? ' seed-fm-toast--error' : ''}`)}>\n <Icon name={t.tone === 'error' ? 'triangleAlert' : 'check'} size={15} strokeWidth={2.5} />\n {t.message}\n </li>\n ))}\n </ul>\n )}\n </div>\n )\n}\n","import { useCallback, useEffect, useMemo, useState } from 'react'\nimport {\n BaseArweaveClient,\n getArweaveReadBaseUrls,\n ensureImageLocal,\n normalizeArweaveTxIdForEnsure,\n type EnsureImageLocalResult,\n type IItemProperty,\n} from '@seedprotocol/sdk'\n\nexport type UseEnsureLocalImageParams = {\n imageProperty: IItemProperty<any>\n filename?: string\n width?: number\n /** When false, skip ensure. Default true. */\n enabled?: boolean\n}\n\nexport type UseEnsureLocalImageReturn = {\n status: 'idle' | 'loading' | EnsureImageLocalResult['status']\n /** Current gateway URL for progressive display; advances through {@link gatewayHrefs} on {@link onGatewayError}. */\n gatewayHref: string | undefined\n /**\n * `/raw/{id}` on every read gateway, preferred ones first (`getArweaveReadBaseUrls`): a\n * just-published image is only on the gateway it was uploaded through until it propagates.\n */\n gatewayHrefs: string[]\n /** Call when `gatewayHref` failed to load (e.g. an `<img>` onError) to move to the next gateway. */\n onGatewayError: () => void\n result: EnsureImageLocalResult | null\n}\n\nconst readStorageTransactionId = (property: IItemProperty<any>): string | undefined => {\n try {\n const snapshot = property.getService?.()?.getSnapshot?.() as\n | { context?: { storageTransactionId?: string } }\n | undefined\n const ctx = snapshot?.context\n return ctx?.storageTransactionId || undefined\n } catch {\n return undefined\n }\n}\n\n/**\n * Ensure an ItemProperty image exists in OPFS (download + targeted resize).\n * Exposes a gateway URL for progressive display while ensuring.\n */\nexport function useEnsureLocalImage(\n params: UseEnsureLocalImageParams,\n): UseEnsureLocalImageReturn {\n const { imageProperty, filename, width, enabled = true } = params\n const [result, setResult] = useState<EnsureImageLocalResult | null>(null)\n const [status, setStatus] = useState<UseEnsureLocalImageReturn['status']>('idle')\n\n const refResolvedValue = imageProperty?.refResolvedValue\n const rawValue = imageProperty?.value\n const stringValue = typeof rawValue === 'string' ? rawValue : undefined\n const storageTransactionId = readStorageTransactionId(imageProperty)\n\n const transactionId = useMemo(() => {\n return (\n normalizeArweaveTxIdForEnsure(storageTransactionId) ??\n normalizeArweaveTxIdForEnsure(filename) ??\n normalizeArweaveTxIdForEnsure(refResolvedValue) ??\n normalizeArweaveTxIdForEnsure(stringValue)\n )\n }, [storageTransactionId, filename, refResolvedValue, stringValue])\n\n const fileName = useMemo(() => {\n const candidate =\n filename ??\n (typeof refResolvedValue === 'string' ? refResolvedValue : undefined) ??\n stringValue\n return candidate?.trim() || undefined\n }, [filename, refResolvedValue, stringValue])\n\n const gatewayHrefs = useMemo(() => {\n if (!transactionId) return []\n const hrefs: string[] = []\n try {\n for (const base of getArweaveReadBaseUrls()) {\n const href = `${base.replace(/\\/$/, '')}/raw/${transactionId}`\n if (!hrefs.includes(href)) hrefs.push(href)\n }\n } catch {\n /* gateway state not ready: fall back to the configured host below */\n }\n try {\n const configured = BaseArweaveClient.getRawUrl(transactionId)\n if (configured && !hrefs.includes(configured)) hrefs.push(configured)\n } catch {\n /* no configured host */\n }\n return hrefs\n }, [transactionId])\n\n const [gatewayIndex, setGatewayIndex] = useState(0)\n useEffect(() => {\n setGatewayIndex(0)\n }, [gatewayHrefs])\n const gatewayHref = gatewayHrefs[gatewayIndex]\n const onGatewayError = useCallback(() => {\n setGatewayIndex((i) => (i < gatewayHrefs.length ? i + 1 : i))\n }, [gatewayHrefs])\n\n useEffect(() => {\n let cancelled = false\n const go = async () => {\n if (!enabled) {\n if (!cancelled) {\n setResult(null)\n setStatus('idle')\n }\n return\n }\n if (!transactionId && !fileName) {\n if (!cancelled) {\n setResult(null)\n setStatus('idle')\n }\n return\n }\n if (!cancelled) {\n setStatus('loading')\n }\n try {\n const r = await ensureImageLocal({\n transactionId,\n fileName,\n widths: width != null ? [width] : undefined,\n })\n if (cancelled) return\n setResult(r)\n setStatus(r.status)\n } catch {\n if (cancelled) return\n setResult({ status: 'failed' })\n setStatus('failed')\n }\n }\n void go()\n return () => {\n cancelled = true\n }\n }, [enabled, transactionId, fileName, width])\n\n return { status, gatewayHref, gatewayHrefs, onGatewayError, result }\n}\n","import React, { useState, useEffect, DetailedHTMLProps, ImgHTMLAttributes } from 'react'\nimport debug from 'debug'\nimport { BaseFileManager } from '@seedprotocol/sdk'\nimport { useItemProperty } from \"./itemProperty\"\nimport type { IItemProperty } from '@seedprotocol/sdk'\nimport { useEnsureLocalImage } from './useEnsureLocalImage'\n\nconst logger = debug('seedSdk:react:SeedImage')\n\n// Cache for content URLs\nconst contentUrlCache = new Map<string, string>()\n\nconst getFileNameWithoutExtension = (filePath: string): string => {\n // This regex has 3 parts:\n // 1. (.*[\\/\\\\]) - Captures the directory path (if any) ending with / or \\\n // 2. ([^\\/\\\\]+) - Captures the filename (anything that's not a / or \\)\n // 3. (\\.[^.\\/\\\\]*)?$ - Captures the last extension (if any)\n const regex = /^(.*[\\/\\\\])?([^\\/\\\\]+?)(\\.[^.\\/\\\\]*)?$/;\n \n const match = filePath.match(regex);\n \n if (match && match[2]) {\n return match[2];\n }\n \n return filePath; // Return original if no match (unlikely)\n}\n\n// Helper function to escape special regex characters\nfunction escapeRegExp(string: string): string {\n return string.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'); // $& means the whole matched string\n}\n\nfunction matchFileNameWithoutExtension(fileName: string, targetName: string): boolean {\n // Create a regex pattern that matches the targetName\n // ^ - start of string\n // targetName is escaped to handle special regex characters\n // $ - end of string\n const regex = new RegExp(`^${escapeRegExp(targetName)}$`);\n \n // First get the filename without extension\n const nameWithoutExt = getFileNameWithoutExtension(fileName);\n \n // Then check if it matches the target name\n return regex.test(nameWithoutExt);\n}\n\nexport type SeedImageProps = DetailedHTMLProps<ImgHTMLAttributes<HTMLImageElement>, HTMLImageElement> & {\n /** The image property, e.g. from useItemProperty or item.properties. */\n imageProperty: IItemProperty<any>\n alt?: string\n width?: number\n height?: number\n /** Optional filename override when property hasn't resolved yet (e.g. in tests) */\n filename?: string\n /**\n * When true (default), ensure the image exists locally (download from Arweave +\n * create needed size variants) before / while rendering.\n */\n ensure?: boolean\n}\n\nconst SeedImageInner = ({\n imageProperty,\n width,\n filename: filenameOverride,\n ensure = true,\n ...props\n}: SeedImageProps, ref: React.ForwardedRef<HTMLImageElement>): React.ReactNode => {\n const [sizedContentUrl, setSizedContentUrl] = useState<string | undefined>()\n const [originalContentUrl, setOriginalContentUrl] = useState<string | undefined>()\n\n const { property: propertyFromHook } = useItemProperty({\n propertyName: imageProperty.propertyName,\n seedLocalId: imageProperty.seedLocalId,\n seedUid: imageProperty.seedUid\n })\n\n // Use passed imageProperty when parent provides it (e.g. direct pass from Item.properties); else use hook for reactive lookup\n const property = imageProperty ?? propertyFromHook\n\n // Resolved filename: explicit override, or from property\n const resolvedFilename = filenameOverride ?? (property?.refResolvedValue ?? property?.value) as string | undefined\n\n const { status: ensureStatus, gatewayHref, onGatewayError } = useEnsureLocalImage({\n imageProperty: property ?? imageProperty,\n filename: filenameOverride,\n width,\n enabled: ensure,\n })\n\n // Get display URL: value (blob URL or filename from ItemProperty.value getter), or resolvedFilename\n const rawValue = property?.value\n const srcUrl = (typeof rawValue === 'string' ? rawValue : resolvedFilename) as string | undefined\n const isFileOrBlob = rawValue != null && (rawValue instanceof File || rawValue instanceof Blob)\n const [blobPreviewUrl, setBlobPreviewUrl] = useState<string | null>(null)\n const blobPreviewRef = React.useRef<string | null>(null)\n useEffect(() => {\n if (isFileOrBlob && (rawValue instanceof File || rawValue instanceof Blob)) {\n if (!blobPreviewRef.current) {\n blobPreviewRef.current = URL.createObjectURL(rawValue)\n setBlobPreviewUrl(blobPreviewRef.current)\n }\n return () => {\n if (blobPreviewRef.current) {\n URL.revokeObjectURL(blobPreviewRef.current)\n blobPreviewRef.current = null\n }\n setBlobPreviewUrl(null)\n }\n }\n blobPreviewRef.current = null\n setBlobPreviewUrl(null)\n }, [isFileOrBlob, rawValue])\n\n // Fallback: when we have filename but no blob URL (e.g. after reload, or sized versions missing), load original file\n useEffect(() => {\n if (!resolvedFilename) return\n const isBlob = (s: unknown) => typeof s === 'string' && s.startsWith('blob:')\n if (rawValue && isBlob(rawValue)) return // Already have blob URL\n if (blobPreviewUrl) return // Have blob preview\n\n let cancelled = false\n const _getOriginalContentUrl = async () => {\n try {\n const filePath = property?.localStoragePath\n ? property.localStoragePath\n : `${BaseFileManager.getFilesPath('images')}/${resolvedFilename}`\n const exists = await BaseFileManager.pathExists(filePath)\n if (exists) {\n const url = await BaseFileManager.getContentUrlFromPath(filePath)\n if (!cancelled && url) setOriginalContentUrl(url)\n }\n } catch (err) {\n logger('_getOriginalContentUrl error', err)\n }\n }\n _getOriginalContentUrl()\n return () => {\n cancelled = true\n }\n }, [resolvedFilename, rawValue, blobPreviewUrl, property?.localStoragePath, ensureStatus])\n\n useEffect(() => {\n if (!width || !resolvedFilename) {\n return\n }\n\n const _getSizedContentUrl = async () => {\n try {\n const fs = await BaseFileManager.getFs()\n const baseDir = property?.localStoragePath\n ? property.localStoragePath.split('/').slice(0, -1).join('/')\n : BaseFileManager.getFilesPath('images')\n const itemsInDir = fs.readdirSync(baseDir, {withFileTypes: true})\n const widthDirs = itemsInDir.filter((item: { isDirectory: () => boolean }) => item.isDirectory())\n const availableWidths = widthDirs.map((dir: { name: string }) => parseInt(dir.name)).filter((n: number) => !Number.isNaN(n))\n if (availableWidths.length === 0) {\n return\n }\n const closestWidth = availableWidths.reduce((prev: number, curr: number) => {\n return (Math.abs(curr - width) < Math.abs(prev - width) ? curr : prev)\n }, availableWidths[0])\n const filenameWithoutExtension = getFileNameWithoutExtension(resolvedFilename)\n\n // Check cache first\n const cacheKey = `${filenameWithoutExtension}-${closestWidth}`\n if (contentUrlCache.has(cacheKey)) {\n try {\n const contentUrl = contentUrlCache.get(cacheKey)\n if (contentUrl) {\n const response = await fetch(contentUrl)\n if (response.ok) {\n setSizedContentUrl(contentUrl)\n return\n }\n }\n } catch (error) {\n logger('error', error)\n contentUrlCache.delete(cacheKey)\n }\n }\n\n const itemsInSizedDir = fs.readdirSync(`${baseDir}/${closestWidth}`, {withFileTypes: true})\n\n const matchingFile = itemsInSizedDir.find((item: { name?: string }) => {\n if (!item.name) {\n return false\n }\n return matchFileNameWithoutExtension(item.name, filenameWithoutExtension)\n })\n if (!matchingFile) {\n return\n }\n const newPath = `${baseDir}/${closestWidth}/${matchingFile?.name}`\n const exists = await BaseFileManager.pathExists(newPath)\n if (exists) {\n const contentUrl = await BaseFileManager.getContentUrlFromPath(newPath)\n if (contentUrl) {\n contentUrlCache.set(cacheKey, contentUrl)\n setSizedContentUrl(contentUrl)\n }\n }\n } catch (err) {\n logger('_getSizedContentUrl error', err)\n }\n }\n\n _getSizedContentUrl()\n }, [property, width, srcUrl, resolvedFilename, ensureStatus])\n\n // Render img when we have a content URL, filename, or progressive gateway href\n const isBlobUrl = (s: unknown) => typeof s === 'string' && s.startsWith('blob:')\n const hasContentUrl = !!sizedContentUrl || !!originalContentUrl || !!blobPreviewUrl || (!!srcUrl && isBlobUrl(srcUrl))\n if (!hasContentUrl && !resolvedFilename && !gatewayHref) {\n return null\n }\n\n // Prefer local sized/original/blob; while ensuring, fall back to gateway then transparent GIF\n const imgSrc =\n sizedContentUrl ||\n originalContentUrl ||\n blobPreviewUrl ||\n (isBlobUrl(srcUrl) ? srcUrl : undefined) ||\n (ensure && !hasContentUrl ? gatewayHref : undefined) ||\n 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'\n\n // A gateway that 404s (e.g. the image hasn't propagated there yet) moves on to the next one.\n const { onError: onErrorProp, ...imgProps } = props\n const handleError: React.ReactEventHandler<HTMLImageElement> = (event) => {\n if (gatewayHref && imgSrc === gatewayHref) onGatewayError()\n onErrorProp?.(event)\n }\n\n return (\n <img\n src={imgSrc}\n alt={props.alt || imageProperty.propertyName || 'Image'}\n width={width}\n {...imgProps}\n ref={ref}\n onError={handleError}\n />\n )\n}\n\n// Shallow comparison of all props: the old comparator ignored className, style, alt and handlers,\n// so changing them never re-rendered.\n// forwardRef: React 18 doesn't pass `ref` to a plain function component.\nexport const SeedImage = React.memo(React.forwardRef(SeedImageInner))\n","import { useCallback, useEffect, useState } from 'react'\nimport {\n resolveMediaRef,\n type ResolveMediaRefOptions,\n type ResolveMediaRefResult,\n} from '@seedprotocol/sdk'\n\nexport type UseResolvedMediaRefParams = {\n value: string | undefined | null\n enabled?: boolean\n} & ResolveMediaRefOptions\n\nexport type UseResolvedMediaRefReturn = {\n href: string | undefined\n status: 'idle' | 'loading' | 'ready' | 'empty' | 'unresolved' | 'error'\n source: 'direct' | 'gateway' | 'localBlob' | undefined\n error: Error | null\n result: ResolveMediaRefResult | null\n refetch: () => void\n}\n\ntype ResolvedState = {\n /** The request (value, options, refetch count) this state belongs to. */\n key: string\n status: UseResolvedMediaRefReturn['status']\n result: ResolveMediaRefResult | null\n error: Error | null\n}\n\n/**\n * Resolve a feed/XML media string (URL, Arweave tx id, seed UID, etc.) to a display URL.\n * Use `SeedImage` when you have a synced item property from the local database.\n */\nexport function useResolvedMediaRef(params: UseResolvedMediaRefParams): UseResolvedMediaRefReturn {\n const { value, enabled = true, treatAs } = params\n const [tick, setTick] = useState(0)\n const key = JSON.stringify([enabled, value ?? null, treatAs ?? null, tick])\n const [state, setState] = useState<ResolvedState | null>(null)\n\n useEffect(() => {\n let cancelled = false\n let blobUrl: string | undefined\n const go = async () => {\n if (!enabled || value == null || String(value).trim() === '') {\n setState({ key, status: 'idle', result: null, error: null })\n return\n }\n setState({ key, status: 'loading', result: null, error: null })\n try {\n const r = await resolveMediaRef(String(value), { treatAs })\n // A local blob URL is created for this result; nobody else revokes it.\n const createdBlobUrl = r.status === 'ready' && r.source === 'localBlob' ? r.href : undefined\n if (cancelled) {\n if (createdBlobUrl) URL.revokeObjectURL(createdBlobUrl)\n return\n }\n blobUrl = createdBlobUrl\n const status = r.status === 'empty' ? 'empty' : r.status === 'ready' ? 'ready' : 'unresolved'\n setState({ key, status, result: r, error: null })\n } catch (e) {\n if (cancelled) return\n setState({ key, status: 'error', result: null, error: e instanceof Error ? e : new Error(String(e)) })\n }\n }\n void go()\n return () => {\n cancelled = true\n // Release the blob URL this effect created (value changed, refetch, or unmount)\n if (blobUrl) URL.revokeObjectURL(blobUrl)\n }\n }, [key, enabled, value, treatAs])\n\n const refetch = useCallback(() => {\n setTick((t) => t + 1)\n }, [])\n\n // State from an earlier value (or before a refetch) is never returned: its blob URL may already\n // be revoked, and it isn't this value's media.\n const current = state?.key === key ? state : null\n const isEmpty = !enabled || value == null || String(value).trim() === ''\n const result = current?.result ?? null\n const href = result && result.status === 'ready' ? result.href : undefined\n const source = result && result.status === 'ready' ? result.source : undefined\n\n return {\n href,\n status: current?.status ?? (isEmpty ? 'idle' : 'loading'),\n source,\n error: current?.error ?? null,\n result,\n refetch,\n }\n}\n","import React from 'react'\nimport { useResolvedMediaRef, type UseResolvedMediaRefParams } from './useResolvedMediaRef'\n\ntype ImgProps = React.DetailedHTMLProps<\n React.ImgHTMLAttributes<HTMLImageElement>,\n HTMLImageElement\n>\n\nexport type SeedMediaImageProps = Omit<ImgProps, 'src'> & {\n value: string | undefined | null\n enabled?: boolean\n treatAs?: UseResolvedMediaRefParams['treatAs']\n /** Custom render; defaults to `<img />`. */\n render?: (props: ImgProps & { src: string }) => React.ReactNode\n}\n\n/**\n * Image from a raw feed/XML media string. For local `ItemProperty` images use `SeedImage`.\n */\nexport function SeedMediaImage({\n value,\n enabled,\n treatAs,\n render,\n alt,\n ...imgProps\n}: SeedMediaImageProps): React.ReactNode {\n const { href } = useResolvedMediaRef({ value, enabled, treatAs })\n if (!href) {\n return null\n }\n const merged = { ...imgProps, src: href, alt: alt ?? '' } as ImgProps & { src: string }\n if (render) {\n return <>{render(merged)}</>\n }\n return <img {...merged} />\n}\n","import React from 'react'\nimport { useResolvedMediaRef, type UseResolvedMediaRefParams } from './useResolvedMediaRef'\n\ntype AnchorProps = React.DetailedHTMLProps<\n React.AnchorHTMLAttributes<HTMLAnchorElement>,\n HTMLAnchorElement\n>\n\nfunction defaultLabelFromHref(href: string): string {\n try {\n const u = new URL(href)\n const seg = u.pathname.split('/').filter(Boolean).pop()\n return seg || 'Open'\n } catch {\n return 'Open'\n }\n}\n\nfunction isHttpOrHttps(href: string): boolean {\n try {\n const p = new URL(href).protocol\n return p === 'http:' || p === 'https:'\n } catch {\n return false\n }\n}\n\nexport type SeedMediaFileProps = Omit<AnchorProps, 'href'> & {\n value: string | undefined | null\n enabled?: boolean\n treatAs?: UseResolvedMediaRefParams['treatAs']\n download?: string | boolean\n /** Custom render; defaults to `<a />`. */\n render?: (props: AnchorProps & { href: string }) => React.ReactNode\n}\n\n/**\n * Link for a raw feed/XML file URL (or tx id / seed UID string resolved like images).\n * For local synced `File` item properties, build download UI with `useItemProperty` / file paths.\n */\nexport function SeedMediaFile({\n value,\n enabled,\n treatAs,\n download,\n render,\n children,\n target,\n rel,\n ...anchorProps\n}: SeedMediaFileProps): React.ReactNode {\n const { href } = useResolvedMediaRef({ value, enabled, treatAs })\n if (!href) {\n return null\n }\n const external = isHttpOrHttps(href)\n const resolvedTarget = target !== undefined ? target : external ? '_blank' : undefined\n const merged = {\n ...anchorProps,\n href,\n download,\n target: resolvedTarget,\n rel:\n rel ??\n (external && resolvedTarget === '_blank' ? 'noopener noreferrer' : undefined),\n children: children ?? defaultLabelFromHref(href),\n } as AnchorProps & { href: string }\n if (render) {\n return <>{render(merged)}</>\n }\n return <a {...merged} />\n}\n","import React from 'react'\nimport { useResolvedMediaRef, type UseResolvedMediaRefParams } from './useResolvedMediaRef'\n\ntype AudioProps = React.DetailedHTMLProps<\n React.AudioHTMLAttributes<HTMLAudioElement>,\n HTMLAudioElement\n>\n\nexport type SeedMediaAudioProps = Omit<AudioProps, 'src'> & {\n value: string | undefined | null\n enabled?: boolean\n treatAs?: UseResolvedMediaRefParams['treatAs']\n /** Custom render; defaults to `<audio controls />`. */\n render?: (props: AudioProps & { src: string }) => React.ReactNode\n}\n\n/**\n * Audio from a raw feed/XML media string. Storage remains File/Image schema types;\n * this helper only picks the browser element.\n */\nexport function SeedMediaAudio({\n value,\n enabled,\n treatAs,\n render,\n controls = true,\n ...audioProps\n}: SeedMediaAudioProps): React.ReactNode {\n const { href } = useResolvedMediaRef({ value, enabled, treatAs })\n if (!href) {\n return null\n }\n const merged = { ...audioProps, src: href, controls } as AudioProps & {\n src: string\n }\n if (render) {\n return <>{render(merged)}</>\n }\n return <audio {...merged} />\n}\n","import React from 'react'\nimport { useResolvedMediaRef, type UseResolvedMediaRefParams } from './useResolvedMediaRef'\n\ntype VideoProps = React.DetailedHTMLProps<\n React.VideoHTMLAttributes<HTMLVideoElement>,\n HTMLVideoElement\n>\n\nexport type SeedMediaVideoProps = Omit<VideoProps, 'src'> & {\n value: string | undefined | null\n enabled?: boolean\n treatAs?: UseResolvedMediaRefParams['treatAs']\n /** Custom render; defaults to `<video controls />`. */\n render?: (props: VideoProps & { src: string }) => React.ReactNode\n}\n\n/**\n * Video from a raw feed/XML media string. Storage remains File/Image schema types;\n * this helper only picks the browser element.\n */\nexport function SeedMediaVideo({\n value,\n enabled,\n treatAs,\n render,\n controls = true,\n ...videoProps\n}: SeedMediaVideoProps): React.ReactNode {\n const { href } = useResolvedMediaRef({ value, enabled, treatAs })\n if (!href) {\n return null\n }\n const merged = { ...videoProps, src: href, controls } as VideoProps & {\n src: string\n }\n if (render) {\n return <>{render(merged)}</>\n }\n return <video {...merged} />\n}\n","import React from 'react'\n\ntype DivProps = Omit<\n React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>,\n 'dangerouslySetInnerHTML' | 'children'\n>\n\nexport type SeedHtmlProps = DivProps & {\n html: string | null | undefined\n /** App-supplied sanitizer (e.g. DOMPurify). The SDK does not ship a default policy. */\n sanitize: (raw: string) => string\n /** If set, receive sanitized HTML and render your own container; no innerHTML is set by SeedHtml. */\n render?: (props: { html: string }) => React.ReactNode\n}\n\nfunction hasRenderableHtml(html: string | null | undefined): html is string {\n return typeof html === 'string' && html.trim().length > 0\n}\n\n/**\n * Renders HTML from an `Html` property, feed field, or other source using a required sanitizer.\n * For plain-text display of markup, use `{String(value)}` instead.\n */\nexport function SeedHtml({\n html,\n sanitize,\n render,\n ...divProps\n}: SeedHtmlProps): React.ReactNode {\n if (!hasRenderableHtml(html)) {\n return null\n }\n const safe = sanitize(html)\n if (render) {\n return <>{render({ html: safe })}</>\n }\n return <div {...divProps} dangerouslySetInnerHTML={{ __html: safe }} />\n}\n","export type FormatSeedJsonOptions = {\n /** Max object/array nesting; default 6 */\n maxDepth?: number\n /** Truncate long strings in the output; default 50_000 */\n maxStringLength?: number\n /** Passed to JSON.stringify; default 2 */\n space?: string | number\n}\n\nfunction truncateString(s: string, max: number): string {\n if (s.length <= max) return s\n return `${s.slice(0, max)}…`\n}\n\nfunction looksLikeJsonObjectOrArray(s: string): boolean {\n const t = s.trim()\n return (t.startsWith('{') && t.endsWith('}')) || (t.startsWith('[') && t.endsWith(']'))\n}\n\nfunction limitDepth(\n value: unknown,\n maxDepth: number,\n maxStringLength: number,\n depth: number,\n seen: WeakSet<object>,\n): unknown {\n if (typeof value === 'bigint') {\n return String(value)\n }\n if (typeof value === 'symbol') {\n return value.toString()\n }\n if (typeof value === 'function') {\n return '[Function]'\n }\n if (value instanceof Date) {\n return value.toISOString()\n }\n if (typeof value === 'string') {\n return truncateString(value, maxStringLength)\n }\n if (value === null || typeof value !== 'object') {\n return value\n }\n if (seen.has(value)) {\n return '[Circular]'\n }\n if (depth >= maxDepth) {\n return '[Max depth]'\n }\n // `seen` holds the current path only: a cycle is a value that is its own ancestor. Keeping\n // every visited object printed a shared (non-cyclic) object as [Circular] the second time.\n seen.add(value)\n try {\n if (Array.isArray(value)) {\n return value.map((item) => limitDepth(item, maxDepth, maxStringLength, depth + 1, seen))\n }\n const out: Record<string, unknown> = {}\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n out[k] = limitDepth(v, maxDepth, maxStringLength, depth + 1, seen)\n }\n return out\n } finally {\n seen.delete(value)\n }\n}\n\n/**\n * Format a value as indented JSON text for read-only display (no eval).\n * Handles JSON-looking strings (parse attempt), depth limits, long strings, BigInt, and circular refs.\n */\nexport function formatSeedJson(value: unknown, options?: FormatSeedJsonOptions): string {\n const maxDepth = options?.maxDepth ?? 6\n const maxStringLength = options?.maxStringLength ?? 50_000\n const space = options?.space ?? 2\n\n if (value === undefined) {\n return 'undefined'\n }\n if (value === null) {\n return 'null'\n }\n\n let toSerialize: unknown = value\n if (typeof value === 'string') {\n if (looksLikeJsonObjectOrArray(value)) {\n try {\n toSerialize = JSON.parse(value) as unknown\n } catch {\n return truncateString(value, maxStringLength)\n }\n } else {\n return truncateString(value, maxStringLength)\n }\n }\n\n const limited = limitDepth(toSerialize, maxDepth, maxStringLength, 0, new WeakSet())\n\n try {\n return JSON.stringify(limited, null, space)\n } catch {\n return '[Unserializable JSON]'\n }\n}\n","import React from 'react'\nimport { formatSeedJson, type FormatSeedJsonOptions } from './formatSeedJson'\n\ntype PreProps = Omit<\n React.DetailedHTMLProps<React.HTMLAttributes<HTMLPreElement>, HTMLPreElement>,\n 'children' | 'dangerouslySetInnerHTML'\n>\n\nexport type SeedJsonProps = PreProps & {\n value: unknown\n /** If set, replaces built-in formatting entirely */\n format?: (value: unknown) => string\n /** Options for `formatSeedJson` when `format` is not set */\n formatOptions?: FormatSeedJsonOptions\n render?: (props: { text: string }) => React.ReactNode\n}\n\n/**\n * Read-only JSON display for `Json` item properties or parsed objects. Does not execute code.\n */\nexport function SeedJson({\n value,\n format,\n formatOptions,\n render,\n ...preProps\n}: SeedJsonProps): React.ReactNode {\n const text = format ? format(value) : formatSeedJson(value, formatOptions)\n if (render) {\n return <>{render({ text })}</>\n }\n return <pre {...preProps}>{text}</pre>\n}\n","import { useMemo } from 'react'\nimport {\n normalizeFeedItemFields,\n type FeedFieldManifest,\n type NormalizedFeedFieldValue,\n} from '@seedprotocol/sdk'\n\n/**\n * Synchronously classify feed item fields from a manifest (no network).\n * Memoized; pass a stable `manifest` reference when possible.\n */\nexport function useNormalizedFeedItemFields(\n item: Record<string, unknown> | null | undefined,\n manifest: FeedFieldManifest,\n): Record<string, NormalizedFeedFieldValue | undefined> {\n return useMemo(() => {\n if (!item) {\n return {}\n }\n return normalizeFeedItemFields(item, manifest)\n }, [item, manifest])\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["toError","ReactQueryClient","logger","toError","logger","logger","logger","schemasTable","schemas","models","modelsTable","schemasTable","schemasTable","propertiesTable","_Fragment","_Fragment","_Fragment","_Fragment","_Fragment","_Fragment","_Fragment","_Fragment","_Fragment","React","debug","pluralize","subscribeToServiceChanges","getClient","onChange","getCurrentService","useClientService","useSyncExternalStore","useClientSelector","service","selectorRef","useRef","selector","subscribe","useCallback","subscription","unsubscribeService","getSnapshot","isClientReadySnapshot","snapshot","ClientManagerState","useIsClientReady","ADDRESSES_PERSISTED_EVENT","LOCAL_COPIES_REMOVED_EVENT","SeedAddressRevisionContext","createContext","useSeedAddressRevision","useContext","SeedAddressRevisionProvider","addressRevision","setAddressRevision","useState","onSessionInvalidation","queryClient","n","useEffect","eventEmitter","useLiveQuery","useLiveQueryState","query","queryKeyOf","sql","params","LIVE_QUERY_RETRY_DELAYS_MS","toError$1","err","state","setState","retryCount","setRetryCount","isClientReady","queryKey","queryRef","failuresRef","subscribedForRef","previous","prev","current","retryTimer","fail","delay","BaseDb","results","error","SEED_QUERY_DEFAULT_OPTIONS","getSeedQueryDefaultOptions","mergeSeedQueryDefaults","seed","userOptions","createSeedQueryClient","defaults","userDefaultOptions","restOverrides","overrides","QueryClient","fallbackQueryClient","warnedMissingProvider","isDevBuild","useSeedQueryClient","QueryClientContext","logger$4","getItemQueryKey","id","useItem","seedLocalId","seedUid","setEasHydrationTick","setPropertyValuesTick","isMachineBusy","setIsMachineBusy","machineError","setMachineError","hasSeenIdleRef","useMemo","queryKeyRef","useQuery","found","Item","modelName","item","onEasSynced","EAS_SEED_DATA_SYNCED_TO_DB_EVENT","itemService","propertySubscriptions","followProperties","propertyInstances","instance","ctx","propertyValue","renderValue","itemSubscription","getItemsQueryKey","deleted","includeEas","addressFilter","modelFileId","schemaName","useItems","useQueryClient","itemsRef","lastFetchedIdsRef","fetchesStartedRef","refetchRequestedAtRef","addressesForFilter","setAddressesForFilter","cancelled","getAddressesForItemsFilter","addrs","items","queryError","db","seedsTableData","conditions","placeholderUidSql","seeds","eq","easName","ownedSeedsSql","publisherInAddressListSql","or","isNull","versionData","getVersionData","and","gt","tableDataItemsSet","dbSeed","key","currentItemsSet","sameIds","isFetching","dataUpdatedAt","orderBy","isLoading","useCreateItem","setIsLoading","setError","inFlightRef","resetError","flushSync","promise","data","itemData","options","usePublishItem","publishingItem","setPublishingItem","subscriptionRef","publishItem","a","b","useEntityRowsState","onFeed","isChangeFeedRunning","fallback","watch","inputsRef","currentWatch","currentQuery","watchEntityRows","rows","logger$3","propertiesLogger","getItemPropertyQueryKey","propertyName","useItemProperty","setVersion","obj","arg1","itemIdFromObj","propertyNameFromObj","itemId","arg2","lookupMode","resolvedSeedLocalId","resolvedSeedUid","ItemProperty","foundByUid","result","property","useHeldItemProperties","bump","initialContext","lastRenderValue","lastPropertyValue","wasIdle","lastVersionAt","trailing","now","useDebouncedItemProperty","latestValueRef","saveError","usePropertySaveError","debouncedPersist","debounce","debounceMs","flush","value","read","validationErrors","ItemPropertySaveValidationError","lastSource","update","source","held","properties","p","fetchItemPropertiesList","baseList","_itemProperties","propertiesWithMetadata","first","startCase","seedRecords","modelProperties","model","Model","modelProperty","itemProperty","createdAtPropertyName","createdAtProperty","useItemProperties","previousTableDataRef","canonicalItemKey","itemPropertiesQueryKey","propertiesQuery","metadata","isNotNull","rawPropertiesTableData","propertiesTableData","pickLatestMetadataRowPerProperty","tableDataString","useCreateItemProperty","create","props","useDestroyItemProperty","DB_READY_STATES","isDbReadyState","useDbsAreReady","logger$2","finalStrings","getServiceName","name","serviceAny","getServiceUniqueKey","getServiceValue","config","uniqueKey","context","isClientManagerService","MachineIds","getPercentComplete","percentForClientState","states","loadingStateNames","stateName","stateIndex","useService","timeElapsed","setTimeElapsed","getValue","updateTime","status","interval","startInterval","useIsDbReady","useGlobalServiceStatus","internalStatus","usePersistedSnapshots","useHasSavedSnapshots","useServices","clientService","percentComplete","toError","useCreateState","runIdRef","follow","apply","failureMessage","entity","start","run","runAsync","runId","logger$1","useSchema","schema","setSchema","loadSchema","schemaExists","identifier","load","schemaInstance","Schema","schemaIdentifier","SEED_PROTOCOL_SCHEMA_NAME","schemas","SEED_SCHEMAS_QUERY_KEY","useSchemas","previousSchemasTableDataRef","schemasRef","schemas$1","schemasTableData","desc","ch","onMessage","prevData","currentSchemasSet","schemaFileId","version","tableDataSchemasSet","dbSchema","setsAreEqual","tableHasNewRows","tableLostRows","useCreateSchema","useDestroySchema","useAllSchemaVersions","schemaInstances","setSchemaInstances","schemaInstancesRef","fetchSchemas","allSchemasData","loadAllSchemasFromDb","schemaNames","schemaData","currentInstances","existingInstance","useSeedProtocolSchema","getModelsQueryKey","schemaId","useModels","tableIdsRef","models$1","next","tableIds","displayModels","modelsRef","event","stableModelsQueryKeyRef","stableModelsQueryRef","buildModelsQuery","currentDb","models","modelSchemas","modelsTableData","prevKey","q","currentModelsSet","tableDataModelsSet","dbModel","useModel","setModel","isModelIdLookup","schemaIdOrModelId","foundModel","modelsList","modelsLoading","modelsError","m","activeModel","useCreateModel","useDestroyModel","resolveSchemaName","getSchemaNameFromId","useModelProperties","dbModelId","useModelDbId","modelId","modelPropertiesQueryKey","ModelProperty","modelPropertiesRef","timers","ms","t","currentPropertiesSet","prop","propertyFileId","tableDataPropertiesSet","dbProperty","LOOKUP_RETRY_DELAYS_MS","readModelDbId","setDbModelId","getPropertySchemaByModelFileId","getPropertySchema","useModelProperty","args","arg3","argCount","initialLoadingState","setModelProperty","shouldLoad","updateModelProperty","propertyData","resolvedModelName","quiet","foundProperty","createdProperty","resolvedProperty","skipLookupRetryRef","shouldSubscribe","debouncedUpdate","useCreateModelProperty","useDestroyModelProperty","useDeleteItem","IMAGE_FILES_QUERY_KEY","useImageFiles","imageFiles","BaseFileManager","fileSavedHandler","filePath","downloadSuccessHandler","filesChangedHandler","filePaths","FILES_CHANGED_EVENT","FILES_QUERY_KEY_PREFIX","useFiles","dir","files","useAppDbWhenReady","useEasSyncProcessesQuery","easSyncProcesses","useEasSyncProcessesState","records","r","useEasSyncProcesses","useEasSyncProcessesNonActiveCount","useEasSyncProcessById","invalidateItemPropertiesRef","invalidateItemPropertiesForItem","p1","canonicalId","SeedProviderEventSubscriber","invalidate","handler","payload","onEasSyncedToDb","SeedClientInit","onInitErrorRef","onInitError","addressesRef","addresses","addressesKey","SeedProvider","queryClientProp","queryClientRef","w","QueryClientProvider","children","DefaultLoading","valueKey","SeedClientGate","initConfigRef","initConfig","schemaRef","initConfigKey","schemaKey","effectiveConfig","loadingContent","loadingComponent","wrapperStyle","wrapperClassName","overlayStyle","loadingClassName","splitPath","parts","path","resolveDirectoryHandle","handle","root","part","resolveParent","resolveFileHandle","getOPFSFile","deleteOPFSEntry","pruneEmptyDirectory","parent","child","empty","childName","removeEmptyOPFSDirectories","isOPFSSupported","SIGNATURES","ascii","bytes","end","sniffMimeType","s","detectMimeType","file","scanDirectory","foundFiles","dirHandle","currentPath","basePath","detectedType","subFiles","useOPFSFiles","setFiles","hasLoaded","setHasLoaded","errorName","setErrorName","allFiles","rootPath","SIZE_UNITS","formatFileSize","i","MINUTE","HOUR","DAY","formatRelativeTime","diff","timestamp","days","formatDateTime","truncateMiddle","max","head","tail","ICONS","Fragment","Icon","size","MAX_LISTED","ConfirmDialog","ref","dialog","className","onCancel","onSubmit","DeleteDialog","includeVariants","setIncludeVariants","variants","entries","variantsSize","v","count","prefix","action","deleteActionFor","heading","onConfirm","warning","FolderDeleteDialog","downloaded","setDownloaded","f","folder","fileCount","copies","busy","onDownload","hiddenCount","effectiveType","DATABASE_NAME","fileKind","type","formatLabel","ext","VARIANT_PATH","ORIGINAL_PATH","stripExtension","parseImageVariant","originalKey","toEntry","buildEntries","groupVariants","originals","variant","entry","original","joinPath","listDirectory","cwd","inside","all","sortEntries","folders","rest","slash","compare","sort","sizeByKind","totals","defaultDeleteWarning","THUMBNAIL_WIDTH","RESIZE_THRESHOLD_BYTES","MAX_CONCURRENT_DECODES","RELEASE_DELAY_MS","activeJobs","queue","schedule","job","resolve","reject","cache","acquire","created","blob","released","resizeImage","bitmap","canvas","createThumbnail","visibilityCallbacks","observer","observeOnce","onVisible","record","el","useThumbnail","isImage","element","setElement","visible","setVisible","url","setUrl","setStatus","lastModified","u","markError","KIND_META","kindStyle","kind","mayBeTransparent","FileThumbnail","failed","showImage","content","FileGrid","isSelected","selected","tileClassName","onActivate","index","e","onToggle","FileList","selectAllRef","selectedHere","allSelected","onToggleAll","deleteAction","rowClassName","onDelete","MAX_TEXT_PREVIEW_FILE","TEXT_PREVIEW_BYTES","usePreview","preview","setPreview","truncated","text","PreviewPanel","dimensions","setDimensions","imageFailed","setImageFailed","closeRef","headingId","previewContent","message","checker","onPrevious","onNext","onClose","onCopyPath","STYLE_ELEMENT_ID","STYLE_LAYER","LIGHT_TOKENS","DARK_TOKENS","OPFS_FILES_MANAGER_CSS","ensureStylesInjected","useStorageEstimate","estimate","setEstimate","usage","quota","revision","crcTable","getCrcTable","c","k","crc32","table","crc","dosDateTime","d","year","UTF8_FLAG","MAX_UINT32","createZip","inputs","encoder","central","offset","input","local","time","date","header","centralSize","DEFAULT_DESCRIPTION","TOAST_MS","errorMessage","plural","one","many","normalizeDir","triggerBrowserDownload","useToasts","toasts","setToasts","pending","timer","x","METER_KINDS","UsageMeter","byKind","filesTotal","otherSiteData","total","segments","StateMessage","tone","icon","title","OPFSFilesManager","theme","rootDir","rootLabel","cn","base","classNames","slot","rawFiles","filter","grouped","setGrouped","groupImageVariants","setCwd","setQuery","setSort","viewChoice","setViewChoice","defaultView","setSelected","anchorRef","panelPath","setPanelPath","panelOpenerRef","pendingDelete","setPendingDelete","pendingFolder","setPendingFolder","setBusy","notify","onNotify","notice","showToast","listing","imageShare","view","entriesByPath","selectedEntries","panelEntry","panelIndex","navigate","toggle","anchor","range","from","to","openPanel","closePanel","activate","modified","toggleAllVisible","relativeToRoot","saveBlob","downloadFiles","list","folderParent","zip","download","filesToDelete","resolveDeleteAction","targets","performDelete","skipped","folderRemoved","onBeforeDelete","refetch","reasons","onAfterDelete","requestDelete","confirmDelete","filesInFolder","requestFolderDelete","copyPath","done","onKeyDown","inField","hasVariants","inImagesFolder","crumbs","Heading","headingLevel","selectedSize","body","retry","pendingFiles","pendingFolderFiles","batchDeleteAction","description","deleteWarning","readStorageTransactionId","useEnsureLocalImage","setResult","refResolvedValue","imageProperty","rawValue","stringValue","storageTransactionId","transactionId","normalizeArweaveTxIdForEnsure","filename","fileName","gatewayHrefs","hrefs","getArweaveReadBaseUrls","href","configured","BaseArweaveClient","gatewayIndex","setGatewayIndex","gatewayHref","onGatewayError","enabled","ensureImageLocal","width","logger","contentUrlCache","getFileNameWithoutExtension","match","escapeRegExp","string","matchFileNameWithoutExtension","regex","targetName","nameWithoutExt","SeedImage","filenameOverride","sizedContentUrl","setSizedContentUrl","originalContentUrl","setOriginalContentUrl","propertyFromHook","resolvedFilename","ensureStatus","ensure","srcUrl","isFileOrBlob","blobPreviewUrl","setBlobPreviewUrl","blobPreviewRef","fs","baseDir","availableWidths","closestWidth","curr","filenameWithoutExtension","cacheKey","contentUrl","matchingFile","newPath","isBlobUrl","hasContentUrl","imgSrc","onErrorProp","imgProps","handleError","useResolvedMediaRef","tick","setTick","treatAs","blobUrl","resolveMediaRef","createdBlobUrl","isEmpty","SeedMediaImage","merged","alt","render","defaultLabelFromHref","isHttpOrHttps","SeedMediaFile","anchorProps","external","resolvedTarget","target","rel","SeedMediaAudio","audioProps","SeedMediaVideo","videoProps","hasRenderableHtml","html","SeedHtml","divProps","safe","sanitize","truncateString","looksLikeJsonObjectOrArray","limitDepth","maxStringLength","seen","depth","maxDepth","out","formatSeedJson","space","toSerialize","limited","SeedJson","preProps","format","formatOptions","useNormalizedFeedItemFields","normalizeFeedItemFields","manifest"],"sources":["../src/client.ts","../src/addressesPersistedEventName.ts","../src/localCopiesRemovedEventName.ts","../src/SeedSessionContext.tsx","../src/liveQuery.ts","../src/queryClient.ts","../src/item.ts","../src/entityRows.ts","../src/itemProperty.ts","../src/db.ts","../src/services.ts","../src/useCreateState.ts","../src/schema.ts","../src/model.ts","../src/modelProperty.ts","../src/trash.ts","../src/useImageFiles.ts","../src/useFiles.ts","../src/useEasSyncProcesses.ts","../src/SeedProvider.tsx","../src/SeedClientGate.tsx","../src/opfsPaths.ts","../src/useOPFSFiles.ts","../src/OPFSFilesManager/format.ts","../src/OPFSFilesManager/icons.tsx","../src/OPFSFilesManager/DeleteDialog.tsx","../src/OPFSFilesManager/fileModel.ts","../src/OPFSFilesManager/useThumbnail.ts","../src/OPFSFilesManager/FileThumbnail.tsx","../src/OPFSFilesManager/FileGrid.tsx","../src/OPFSFilesManager/FileList.tsx","../src/OPFSFilesManager/PreviewPanel.tsx","../src/OPFSFilesManager/styles.ts","../src/OPFSFilesManager/useStorageEstimate.ts","../src/OPFSFilesManager/zip.ts","../src/OPFSFilesManager/OPFSFilesManager.tsx","../src/useEnsureLocalImage.ts","../src/SeedImage.tsx","../src/useResolvedMediaRef.ts","../src/SeedMediaImage.tsx","../src/SeedMediaFile.tsx","../src/SeedMediaAudio.tsx","../src/SeedMediaVideo.tsx","../src/SeedHtml.tsx","../src/formatSeedJson.ts","../src/SeedJson.tsx","../src/useNormalizedFeedItemFields.ts"],"sourcesContent":["import { useCallback, useRef, useSyncExternalStore } from 'react'\nimport { ClientManagerState, getClient } from '@seedprotocol/sdk'\n\ntype ClientService = ReturnType<ReturnType<typeof getClient>['getService']>\ntype ClientSnapshot = ReturnType<ClientService['getSnapshot']>\n\nconst subscribeToServiceChanges = (onChange: () => void) => getClient().subscribeService(onChange)\nconst getCurrentService = () => getClient().getService()\n\n/**\n * The current clientManager actor. Re-renders when `client.init()` after `client.stop()`\n * replaces it, so hooks built on it follow the new client instead of the stopped one.\n */\nexport const useClientService = (): ClientService =>\n useSyncExternalStore(subscribeToServiceChanges, getCurrentService, getCurrentService)\n\n/**\n * A value derived from the current clientManager snapshot. Follows actor swaps and `stop()`.\n * The selector must return a primitive or a stable reference: a new object on every call\n * re-renders forever.\n */\nexport function useClientSelector<T>(selector: (snapshot: ClientSnapshot) => T): T {\n const service = useClientService()\n const selectorRef = useRef(selector)\n selectorRef.current = selector\n\n const subscribe = useCallback(\n (onChange: () => void) => {\n const subscription = service.subscribe({ next: onChange, error: onChange, complete: onChange })\n // stop() may not emit a snapshot to subscribers; subscribeService reports it.\n const unsubscribeService = getClient().subscribeService(onChange)\n return () => {\n subscription.unsubscribe()\n unsubscribeService()\n }\n },\n [service],\n )\n const getSnapshot = () => selectorRef.current(service.getSnapshot())\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot)\n}\n\n/** Whether a clientManager snapshot is a running client that finished init. */\nexport const isClientReadySnapshot = (snapshot: ClientSnapshot): boolean =>\n snapshot.status === 'active' && snapshot.value === ClientManagerState.IDLE\n\n/**\n * Whether `client.init` has finished and the client is ready (idle). False after\n * `client.stop()` until the next init finishes. SeedClientGate uses it to hold back its\n * children; call it to wait for readiness in your own components.\n */\nexport const useIsClientReady = () => useClientSelector(isClientReadySnapshot)\n","/**\n * Must match `ADDRESSES_PERSISTED_EVENT` in `@seedprotocol/sdk` / `client/events.ts`\n * (emitted after `setAddresses` persists to `app_state`).\n */\nexport const ADDRESSES_PERSISTED_EVENT = 'addresses.persisted' as const\n","/**\n * Must match `LOCAL_COPIES_REMOVED_EVENT` in `@seedprotocol/sdk` / `client/events.ts`\n */\nexport const LOCAL_COPIES_REMOVED_EVENT = 'localCopies.removed' as const\n","import React, {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useState,\n type ReactNode,\n} from 'react'\nimport type { QueryClient } from '@tanstack/react-query'\nimport { eventEmitter } from '@seedprotocol/sdk'\nimport { ADDRESSES_PERSISTED_EVENT } from './addressesPersistedEventName'\nimport { LOCAL_COPIES_REMOVED_EVENT } from './localCopiesRemovedEventName'\n\nconst SeedAddressRevisionContext = createContext(0)\n\n/**\n * Bumps when persisted wallet/session addresses change (`setAddresses` completed).\n * Used by `useItems` / `useItem` so list and detail hooks refresh after connect/disconnect.\n * Outside {@link SeedAddressRevisionProvider} (e.g. without `SeedProvider`), this stays `0`.\n */\nexport function useSeedAddressRevision(): number {\n return useContext(SeedAddressRevisionContext)\n}\n\nexport function SeedAddressRevisionProvider({\n queryClient,\n children,\n}: {\n queryClient: QueryClient\n children: ReactNode\n}) {\n const [addressRevision, setAddressRevision] = useState(0)\n\n const onSessionInvalidation = useCallback(() => {\n void queryClient.invalidateQueries({ queryKey: ['seed', 'items'], exact: false })\n void queryClient.invalidateQueries({ queryKey: ['seed', 'item'], exact: false })\n void queryClient.invalidateQueries({ queryKey: ['seed', 'itemProperty'], exact: false })\n setAddressRevision((n) => n + 1)\n }, [queryClient])\n\n useEffect(() => {\n eventEmitter.on(ADDRESSES_PERSISTED_EVENT, onSessionInvalidation)\n eventEmitter.on(LOCAL_COPIES_REMOVED_EVENT, onSessionInvalidation)\n return () => {\n eventEmitter.off(ADDRESSES_PERSISTED_EVENT, onSessionInvalidation)\n eventEmitter.off(LOCAL_COPIES_REMOVED_EVENT, onSessionInvalidation)\n }\n }, [onSessionInvalidation])\n\n return (\n <SeedAddressRevisionContext.Provider value={addressRevision}>\n {children}\n </SeedAddressRevisionContext.Provider>\n )\n}\n","import { useEffect, useState, useRef } from 'react'\nimport { BaseDb } from '@seedprotocol/sdk'\nimport { useIsClientReady } from './client'\n\n/**\n * Hook to execute a reactive query that emits new results whenever the underlying data changes.\n * \n * Supports two usage patterns:\n * 1. SQL tag function: useLiveQuery((sql) => sql`SELECT * FROM models`)\n * 2. Drizzle query builder: useLiveQuery(db.select().from(models))\n * \n * @param query - SQL query function or Drizzle query builder, or null/undefined to disable the query\n * @returns Array of query results, or undefined if not yet loaded\n * \n * @example\n * ```typescript\n * // Using SQL tag function\n * const models = useLiveQuery<ModelRow>(\n * (sql) => sql`SELECT * FROM models WHERE schema_file_id = ${schemaId}`\n * )\n * \n * // Using Drizzle query builder\n * import { models } from '@seedprotocol/sdk'\n * import { eq } from 'drizzle-orm'\n * \n * const appDb = BaseDb.getAppDb()\n * const models = useLiveQuery<ModelRow>(\n * appDb.select().from(models).where(eq(models.schemaFileId, schemaId))\n * )\n * ```\n */\nexport function useLiveQuery<T>(\n query: ((sql: any) => any) | any | null | undefined\n): T[] | undefined {\n return useLiveQueryState<T>(query).data\n}\n\n/**\n * Identity of a query for subscription purposes. A Drizzle builder is identified by its compiled\n * SQL and parameters, so an inline (unmemoised) builder doesn't resubscribe on every render; that\n * re-render/resubscribe cycle looped. A SQL tag function can only be identified by reference:\n * memoise it (useCallback/useMemo).\n */\nexport function queryKeyOf(query: unknown): unknown {\n if (query && typeof query === 'object' && typeof (query as { toSQL?: unknown }).toSQL === 'function') {\n try {\n const { sql, params } = (query as { toSQL: () => { sql: string; params: unknown[] } }).toSQL()\n return `sql:${sql}|${JSON.stringify(params)}`\n } catch {\n return query\n }\n }\n return query\n}\n\n/** Delays before resubscribing after a live query fails, in ms; the last one repeats. */\nexport const LIVE_QUERY_RETRY_DELAYS_MS = [500, 1000, 2000, 5000, 10000, 30000]\n\nexport const toError = (err: unknown): Error => (err instanceof Error ? err : new Error(String(err)))\n\n/**\n * useLiveQuery with its error: `{ data, error }`. `data` is undefined until the first result and\n * resets when the query changes or the client stops; `error` is the last error from creating or\n * running the live query, cleared by the next result. After an error the hook keeps the last rows\n * and resubscribes with backoff (LIVE_QUERY_RETRY_DELAYS_MS) until a result arrives.\n */\nexport function useLiveQueryState<T>(\n query: ((sql: any) => any) | any | null | undefined\n): { data: T[] | undefined; error: Error | null } {\n const [state, setState] = useState<{ data: T[] | undefined; error: Error | null }>({\n data: undefined,\n error: null,\n })\n // Bumped to resubscribe after an error.\n const [retryCount, setRetryCount] = useState(0)\n const isClientReady = useIsClientReady()\n\n const queryKey = queryKeyOf(query)\n const queryRef = useRef(query)\n queryRef.current = query\n const failuresRef = useRef(0)\n const subscribedForRef = useRef<{ queryKey: unknown; isClientReady: boolean } | null>(null)\n\n useEffect(() => {\n const previous = subscribedForRef.current\n subscribedForRef.current = { queryKey, isClientReady }\n if (!previous || previous.queryKey !== queryKey || previous.isClientReady !== isClientReady) {\n // A new (or no) query, or a client that started or stopped: drop the previous rows\n // (no re-render if already empty). A retry keeps them.\n failuresRef.current = 0\n setState((prev) => (prev.data === undefined && prev.error === null ? prev : { data: undefined, error: null }))\n }\n const current = queryRef.current\n if (!isClientReady || !current) {\n return\n }\n\n let retryTimer: ReturnType<typeof setTimeout> | undefined\n const fail = (err: unknown) => {\n // Keep the last rows (no flicker on transient errors) and report the error\n setState((prev) => ({ data: prev.data, error: toError(err) }))\n const delay =\n LIVE_QUERY_RETRY_DELAYS_MS[Math.min(failuresRef.current, LIVE_QUERY_RETRY_DELAYS_MS.length - 1)]\n failuresRef.current += 1\n retryTimer = setTimeout(() => setRetryCount((n) => n + 1), delay)\n }\n\n let subscription: { unsubscribe: () => void } | undefined\n try {\n subscription = BaseDb.liveQuery<T>(current).subscribe({\n next: (results) => {\n failuresRef.current = 0\n // New array reference so React re-renders when SQLocal mutates row objects in place\n // on the same backing array (Object.is would otherwise bail out of useState).\n setState({ data: results !== undefined ? [...results] : undefined, error: null })\n },\n error: fail,\n })\n } catch (error) {\n fail(error)\n }\n\n return () => {\n subscription?.unsubscribe()\n if (retryTimer !== undefined) clearTimeout(retryTimer)\n }\n }, [queryKey, isClientReady, retryCount])\n\n return state\n}\n","import { useContext } from 'react'\nimport {\n type QueryClient,\n type DefaultOptions,\n QueryClient as ReactQueryClient,\n QueryClientContext,\n type QueryClientConfig,\n NetworkMode,\n} from '@tanstack/react-query'\n\nconst SEED_QUERY_DEFAULT_OPTIONS: DefaultOptions = {\n queries: {\n networkMode: 'offlineFirst' as NetworkMode,\n gcTime: 1000 * 60 * 60 * 24, // 24 hours\n staleTime: 1000 * 60, // 1 minute - list data can be slightly stale\n },\n}\n\n/**\n * Returns the default options used by Seed for list-query caching.\n * Use this when building your own QueryClient so Seed hooks get consistent behavior.\n */\nexport function getSeedQueryDefaultOptions(): DefaultOptions {\n return { ...SEED_QUERY_DEFAULT_OPTIONS }\n}\n\n/**\n * Merges Seed's default query options with your existing default options.\n * Your options take precedence over Seed's. Use when constructing your own QueryClient:\n *\n * @example\n * ```ts\n * const client = new QueryClient({\n * defaultOptions: mergeSeedQueryDefaults({\n * queries: { gcTime: 1000 * 60 * 60 },\n * }),\n * })\n * ```\n */\nexport function mergeSeedQueryDefaults(\n userOptions?: Partial<DefaultOptions> | null\n): DefaultOptions {\n const seed = getSeedQueryDefaultOptions()\n if (!userOptions) return seed\n return {\n queries: {\n ...seed.queries,\n ...(userOptions.queries ?? {}),\n },\n mutations: {\n ...(seed.mutations ?? {}),\n ...(userOptions.mutations ?? {}),\n },\n }\n}\n\n/**\n * Creates a QueryClient configured with Seed's default options.\n * Use this when you want to provide your own QueryClientProvider but still use Seed's defaults.\n *\n * @param overrides - Optional config to merge with Seed defaults (e.g. defaultOptions, logger).\n */\nexport function createSeedQueryClient(overrides?: Partial<QueryClientConfig>): QueryClient {\n const defaults = getSeedQueryDefaultOptions()\n const { defaultOptions: userDefaultOptions, ...restOverrides } = overrides ?? {}\n return new ReactQueryClient({\n ...restOverrides,\n defaultOptions: userDefaultOptions\n ? mergeSeedQueryDefaults(userDefaultOptions as DefaultOptions)\n : defaults,\n })\n}\n\nlet fallbackQueryClient: QueryClient | undefined\nlet warnedMissingProvider = false\n\n/** False in production builds; bundlers compile `process.env.NODE_ENV` in. */\nconst isDevBuild = (): boolean => {\n try {\n return process.env.NODE_ENV !== 'production'\n } catch {\n return false // no `process` and no bundler replacement\n }\n}\n\n/**\n * The QueryClient from the nearest QueryClientProvider (SeedProvider sets one), or else one shared\n * client with Seed defaults, used by every Seed hook outside a provider. Development builds warn\n * once when the fallback is used. From 0.8 these hooks will throw without SeedProvider.\n */\nexport function useSeedQueryClient(): QueryClient {\n const fromProvider = useContext(QueryClientContext)\n if (fromProvider) return fromProvider\n if (!warnedMissingProvider && isDevBuild()) {\n warnedMissingProvider = true\n console.warn(\n '[@seedprotocol/react] A Seed hook ran outside SeedProvider (no QueryClientProvider found), so it ' +\n 'uses a shared fallback QueryClient. Wrap the app in <SeedProvider>: from 0.8 these hooks ' +\n 'will throw without it.',\n )\n }\n fallbackQueryClient ??= createSeedQueryClient()\n return fallbackQueryClient\n}\n\n/** @internal For tests: the fallback client `useSeedQueryClient` returns outside a provider. */\nexport function getFallbackSeedQueryClient(): QueryClient | undefined {\n return fallbackQueryClient\n}\n","import { useCallback, useEffect, useRef, useState, useMemo } from 'react'\nimport { flushSync } from 'react-dom'\nimport {\n getAddressesForItemsFilter,\n ownedSeedsSql,\n placeholderUidSql,\n publisherInAddressListSql,\n Item,\n eventEmitter,\n EAS_SEED_DATA_SYNCED_TO_DB_EVENT,\n} from '@seedprotocol/sdk'\nimport { orderBy } from 'lodash-es'\nimport debug from 'debug'\nimport type { ModelValues } from '@seedprotocol/sdk'\nimport { Subscription } from 'xstate'\nimport type { IItem, IItemProperty, ItemCreateOptions, ItemPublishOptions } from '@seedprotocol/sdk'\nimport { useIsClientReady } from './client'\nimport { useSeedAddressRevision } from './SeedSessionContext'\nimport { useLiveQuery } from './liveQuery'\nimport { useSeedQueryClient } from './queryClient'\nimport { BaseDb } from '@seedprotocol/sdk'\nimport { seeds } from '@seedprotocol/sdk'\nimport { and, eq, gt, isNull, or } from 'drizzle-orm'\nimport { easName } from '@seedprotocol/sdk'\nimport type { SeedType } from '@seedprotocol/sdk'\nimport { getVersionData } from '@seedprotocol/sdk'\nimport { useQuery, useQueryClient } from '@tanstack/react-query'\n\nconst logger = debug('seedSdk:react:item')\n\ntype UseItemReturn<T extends ModelValues<T>> = {\n item: IItem<T> | undefined\n isLoading: boolean\n error: Error | null\n}\n\ntype UseItemProps = {\n modelName: string\n seedLocalId?: string\n seedUid?: string\n}\n\ntype UseItem = <T extends ModelValues<T>>(props: UseItemProps) => UseItemReturn<T>\n\n/** Query key for one item: `useItem`'s query. Invalidated alongside `['seed', 'items']`. */\nexport const getItemQueryKey = (id: string | undefined) => ['seed', 'item', id ?? null] as const\n\n/**\n * Loads one Item by seedLocalId or seedUid.\n *\n * - An Item already in the instance cache and ready (`Item.peekReady`) comes back on the first\n * render with `isLoading: false`, then `Item.find()` refreshes it in the background.\n * - Changing the id never returns the previous id's item.\n * - The item updates in place (its state machine), so the hook also re-renders on its changes.\n */\nexport const useItem: UseItem = <T extends ModelValues<T>>({ modelName, seedLocalId, seedUid }: UseItemProps) => {\n const id = seedLocalId || seedUid\n /** Bumped when EAS sync updates SQLite so cached `Item` instances re-render after in-place hydration. */\n const [, setEasHydrationTick] = useState(0)\n /** Bumped when one of the item's property values changes. */\n const [, setPropertyValuesTick] = useState(0)\n const [isMachineBusy, setIsMachineBusy] = useState(false)\n const [machineError, setMachineError] = useState<Error | null>(null)\n const hasSeenIdleRef = useRef(false)\n\n const isClientReady = useIsClientReady()\n const queryClient = useSeedQueryClient()\n const queryKey = useMemo(() => getItemQueryKey(id), [id])\n const queryKeyRef = useRef(queryKey)\n queryKeyRef.current = queryKey\n\n const query = useQuery(\n {\n queryKey,\n queryFn: async (): Promise<Item<T> | null> => {\n // A ready cached item is what find() would return, minus the cache hold find() takes.\n const found = (Item.peekReady(id) ??\n (await Item.find({ modelName, seedLocalId, seedUid }))) as Item<T> | undefined\n if (found) return found\n logger('[useItem] no item found', modelName, id)\n // find() also returns undefined when the item doesn't settle in time; keep what we have,\n // unless it was unloaded (e.g. a local purge stops the instance and deletes its rows).\n const previous = queryClient.getQueryData<Item<T> | null>(queryKey)\n return previous && previous.getService().getSnapshot().status === 'active' ? previous : null\n },\n // The Item instance cache is the cache. A ready cached item is the first render's data and\n // staleTime 0 refreshes it in the background; gcTime 0 drops the query once unused, so it\n // never hands out an instance that was unloaded since.\n initialData: () => Item.peekReady(id) as Item<T> | undefined,\n staleTime: 0,\n gcTime: 0,\n // An Item is a live actor-backed object, not data to compare field by field.\n structuralSharing: false,\n retry: false,\n enabled: isClientReady && !!id,\n },\n queryClient,\n )\n const item = query.data ?? undefined\n\n useEffect(() => {\n const onEasSynced = () => {\n setEasHydrationTick((n) => n + 1)\n void queryClient.invalidateQueries({ queryKey: queryKeyRef.current })\n }\n eventEmitter.on(EAS_SEED_DATA_SYNCED_TO_DB_EVENT, onEasSynced)\n return () => {\n eventEmitter.off(EAS_SEED_DATA_SYNCED_TO_DB_EVENT, onEasSynced)\n }\n }, [queryClient])\n\n // Follow the item's machine: busy after a real transition out of idle (reload), or error.\n useEffect(() => {\n hasSeenIdleRef.current = false\n setIsMachineBusy(false)\n setMachineError(null)\n if (!item) return\n\n const subscription: Subscription = item.getService().subscribe((snapshot: any) => {\n if (!snapshot || typeof snapshot !== 'object' || !('value' in snapshot)) return\n if (snapshot.value === 'idle') {\n hasSeenIdleRef.current = true\n setIsMachineBusy(false)\n setMachineError(null)\n } else if (snapshot.value === 'error') {\n setMachineError(new Error('Item service error'))\n setIsMachineBusy(false)\n } else if (hasSeenIdleRef.current) {\n // Only after idle, so the transitions find() already waited out don't count as loading.\n setIsMachineBusy(true)\n }\n })\n return () => subscription.unsubscribe()\n }, [item])\n\n // Re-render when a property's value changes: the item reads them from its property instances,\n // which update without the item's own machine changing. The item adds instances as they load\n // (same Map, mutated), so recheck the set on each item snapshot.\n useEffect(() => {\n if (!item) return\n const itemService = item.getService()\n const propertySubscriptions = new Map<unknown, Subscription>()\n\n const followProperties = (propertyInstances: Map<PropertyKey, IItemProperty<any>> | undefined) => {\n const current = new Set(propertyInstances?.values() ?? [])\n for (const [instance, subscription] of propertySubscriptions) {\n if (!current.has(instance as IItemProperty<any>)) {\n subscription.unsubscribe()\n propertySubscriptions.delete(instance)\n }\n }\n for (const instance of current) {\n if (propertySubscriptions.has(instance)) continue\n const service = instance.getService()\n let { propertyValue, renderValue } = (service.getSnapshot() as any).context ?? {}\n propertySubscriptions.set(\n instance,\n service.subscribe((snapshot: any) => {\n const ctx = snapshot?.context\n if (!ctx) return\n if (Object.is(ctx.propertyValue, propertyValue) && Object.is(ctx.renderValue, renderValue)) return\n propertyValue = ctx.propertyValue\n renderValue = ctx.renderValue\n setPropertyValuesTick((n) => n + 1)\n }),\n )\n }\n }\n\n followProperties(itemService.getSnapshot().context.propertyInstances)\n const itemSubscription: Subscription = itemService.subscribe((snapshot: any) => {\n followProperties(snapshot?.context?.propertyInstances)\n })\n return () => {\n itemSubscription.unsubscribe()\n for (const subscription of propertySubscriptions.values()) subscription.unsubscribe()\n }\n }, [item])\n\n return {\n item,\n isLoading: (!!id && query.isPending) || isMachineBusy,\n error: machineError ?? (query.error as Error | null),\n }\n}\n\ntype UseItemsReturn = {\n items: IItem<any>[]\n isLoading: boolean\n error: Error | null\n}\n\ntype UseItemsProps = {\n modelName?: string\n /** Only items of one model, when several schemas define `modelName`: the schema that defines it ... */\n schemaName?: string\n /** ... or its Model.id. */\n modelFileId?: string\n deleted?: boolean\n includeEas?: boolean\n addressFilter?: 'owned' | 'watched' | 'all'\n}\n\ntype UseItems = (props: UseItemsProps) => UseItemsReturn\n\nconst getItemsQueryKey = (\n modelName?: string,\n deleted?: boolean,\n includeEas?: boolean,\n addressFilter?: 'owned' | 'watched' | 'all',\n addressRevision?: number,\n modelFileId?: string,\n schemaName?: string,\n) =>\n [\n 'seed',\n 'items',\n modelName ?? null,\n deleted ?? false,\n includeEas ?? false,\n addressFilter ?? null,\n addressRevision ?? 0,\n // Only scoped lists get the extra segment, so unscoped keys keep their existing shape.\n ...(modelFileId || schemaName ? [{ modelFileId: modelFileId ?? null, schemaName: schemaName ?? null }] : []),\n ] as const\n\n/**\n * Lists items for an optional model (and filters) with TanStack Query plus a SQLite live query.\n *\n * - This hook uses `staleTime: 0` on its query so the list does not inherit Seed’s default\n * long freshness window; local data is kept in sync via live-query invalidation, and remounts\n * can refetch when the cache is stale.\n * - Schema-backed fields on each `IItem` (e.g. list relation properties) read from the item\n * machine and may be `undefined` until that property is loaded; normalize for forms\n * (e.g. `Array.isArray(x) ? x : []`).\n */\nexport const useItems: UseItems = ({\n modelName,\n modelFileId,\n schemaName,\n deleted = false,\n includeEas = false,\n addressFilter,\n}) => {\n const isClientReady = useIsClientReady()\n const addressRevision = useSeedAddressRevision()\n const queryClient = useQueryClient()\n\n useEffect(() => {\n const onEasSynced = () => {\n queryClient.invalidateQueries({ queryKey: ['seed', 'items'] })\n }\n eventEmitter.on(EAS_SEED_DATA_SYNCED_TO_DB_EVENT, onEasSynced)\n return () => {\n eventEmitter.off(EAS_SEED_DATA_SYNCED_TO_DB_EVENT, onEasSynced)\n }\n }, [queryClient])\n\n const itemsRef = useRef<IItem<any>[]>([])\n // The live seed ids a refetch was last requested for.\n const lastFetchedIdsRef = useRef<Set<string>>(new Set())\n // Fetches started, and how many had started when the last refetch was requested. Only a fetch\n // started after the request can answer it: an invalidation while a fetch with no data yet is\n // running joins that fetch instead of starting one.\n const fetchesStartedRef = useRef(0)\n const refetchRequestedAtRef = useRef(0)\n const [addressesForFilter, setAddressesForFilter] = useState<string[] | null>(null)\n\n useEffect(() => {\n if (addressFilter !== 'owned' && addressFilter !== 'watched') {\n setAddressesForFilter(null)\n return\n }\n let cancelled = false\n getAddressesForItemsFilter(addressFilter).then((addrs) => {\n if (!cancelled) setAddressesForFilter(addrs)\n })\n return () => {\n cancelled = true\n }\n }, [addressFilter, addressRevision])\n\n const queryKey = useMemo(\n () => getItemsQueryKey(modelName, deleted, includeEas, addressFilter, addressRevision, modelFileId, schemaName),\n [modelName, deleted, includeEas, addressFilter, addressRevision, modelFileId, schemaName],\n )\n\n useEffect(() => {\n lastFetchedIdsRef.current = new Set()\n }, [queryKey])\n\n const {\n data: items = [],\n isLoading,\n isFetching,\n dataUpdatedAt,\n error: queryError,\n } = useQuery({\n queryKey,\n queryFn: async ({ signal }) => {\n fetchesStartedRef.current++\n const rows = await Item.all(modelName, deleted, {\n waitForReady: true,\n includeEas,\n addressFilter,\n modelFileId,\n schemaName,\n // A superseded fetch (new key, cancelled refetch, unmount) stops building items.\n signal,\n })\n return rows\n },\n enabled: isClientReady,\n // Local SQLite + live invalidation drive freshness; Seed’s default staleTime would keep a\n // mistaken initial [] “fresh” and block refetch when another subscriber mounts.\n staleTime: 0,\n })\n itemsRef.current = items\n\n // Watch the seeds table for changes\n const db = isClientReady ? BaseDb.getAppDb() : null\n const seedsQuery = useMemo(() => {\n if (!db) return null\n if (addressFilter === 'owned' || addressFilter === 'watched') {\n if (addressesForFilter === null) return null\n }\n const conditions: any[] = []\n // Same predicates as getItemsData, so the live query and the fetched list agree.\n if (!includeEas) {\n conditions.push(placeholderUidSql(seeds.uid) as any)\n }\n if (modelName) {\n conditions.push(eq(seeds.type, easName(modelName)))\n }\n if (addressFilter === 'owned') {\n conditions.push(ownedSeedsSql(addressesForFilter ?? []) as any)\n } else if (addressFilter === 'watched') {\n conditions.push(\n publisherInAddressListSql(seeds.publisher, addressesForFilter ?? []) as any,\n )\n }\n if (deleted) {\n conditions.push(eq(seeds._markedForDeletion, 1) as any)\n } else {\n conditions.push(\n or(\n isNull(seeds._markedForDeletion),\n eq(seeds._markedForDeletion, 0)\n ) as any\n )\n conditions.push(\n or(isNull(seeds.revokedAt), eq(seeds.revokedAt, 0)) as any\n )\n }\n const versionData = getVersionData()\n return db\n .with(versionData)\n .select({\n localId: seeds.localId,\n uid: seeds.uid,\n type: seeds.type,\n schemaUid: seeds.schemaUid,\n createdAt: seeds.createdAt,\n attestationCreatedAt: seeds.attestationCreatedAt,\n _markedForDeletion: seeds._markedForDeletion,\n })\n .from(seeds)\n .leftJoin(versionData, eq(seeds.localId, versionData.seedLocalId))\n .where(and(gt(versionData.versionsCount, 0), ...conditions))\n .groupBy(seeds.localId)\n }, [db, isClientReady, modelName, deleted, includeEas, addressFilter, addressesForFilter])\n const seedsTableData = useLiveQuery<SeedType>(seedsQuery)\n\n // Refetch when the live seed ids differ from the listed items.\n useEffect(() => {\n if (!isClientReady || !seedsTableData) return\n\n const tableDataItemsSet = new Set<string>()\n for (const dbSeed of seedsTableData) {\n const key = dbSeed.localId || dbSeed.uid\n if (key) tableDataItemsSet.add(key)\n }\n\n const currentItemsSet = new Set<string>()\n for (const item of itemsRef.current) {\n const key = item.seedLocalId || item.seedUid\n if (key) currentItemsSet.add(key)\n }\n\n if (tableDataItemsSet.size === 0 && currentItemsSet.size > 0) {\n return\n }\n\n if (sameIds(currentItemsSet, tableDataItemsSet)) {\n lastFetchedIdsRef.current = tableDataItemsSet\n return\n }\n\n const requestRefetch = () => {\n lastFetchedIdsRef.current = tableDataItemsSet\n refetchRequestedAtRef.current = fetchesStartedRef.current\n queryClient.invalidateQueries({ queryKey })\n }\n\n if (!sameIds(lastFetchedIdsRef.current, tableDataItemsSet)) {\n requestRefetch()\n return\n }\n\n // A refetch was already requested for these ids. If a fetch started after the request, it\n // answers it, or failed and retries on its own: Item.all and the live query can disagree\n // (an item still loading), so don't loop.\n if (fetchesStartedRef.current > refetchRequestedAtRef.current) return\n // The request joined a fetch that started before the change. Wait for it to settle (this\n // effect runs again then), and refetch: it listed the old ids.\n if (isFetching) return\n requestRefetch()\n }, [isClientReady, seedsTableData, queryClient, queryKey, dataUpdatedAt, isFetching])\n\n // Same array while the data is the same, so `items` can be an effect/memo dependency.\n const sortedItems = useMemo(\n () =>\n orderBy(\n items,\n [\n (item) =>\n item.lastVersionPublishedAt ||\n item.attestationCreatedAt ||\n item.createdAt,\n ],\n ['desc'],\n ),\n [items],\n )\n\n return {\n items: sortedItems,\n // Before the client is ready the query is disabled, which TanStack reports as not loading:\n // count it as loading so an empty list isn't shown as \"no items\" first.\n isLoading: isLoading || !isClientReady,\n error: queryError as Error | null,\n }\n}\n\nexport type UseCreateItemReturn = {\n /**\n * Calls `Item.create({ modelName, ...itemData }, options)`. A call made while another is in\n * flight returns that call's promise (and its item) instead of creating a second one.\n */\n createItem: (\n modelName: string,\n itemData?: Record<string, any>,\n options?: ItemCreateOptions,\n ) => Promise<Item<any> | undefined>\n isLoading: boolean\n error: Error | null\n resetError: () => void\n}\n\nexport const useCreateItem = (): UseCreateItemReturn => {\n const [isLoading, setIsLoading] = useState(false)\n const [error, setError] = useState<Error | null>(null)\n // A ref, not the isLoading state: two calls before a re-render both saw isLoading false.\n const inFlightRef = useRef<Promise<Item<any> | undefined> | null>(null)\n\n const resetError = useCallback(() => setError(null), [])\n\n const createItem = useCallback(\n (\n modelName: string,\n itemData?: Record<string, any>,\n options?: ItemCreateOptions,\n ): Promise<Item<any> | undefined> => {\n if (inFlightRef.current) {\n logger('[useCreateItem] [createItem] already creating item, returning the in-flight create')\n return inFlightRef.current\n }\n\n setError(null)\n // Flush loading=true synchronously so the UI (and tests) can observe it before async work runs.\n flushSync(() => setIsLoading(true))\n\n const run = async (): Promise<Item<any> | undefined> => {\n try {\n const data = itemData ?? {}\n // Item.create runs File/Image/Html values through their save pipeline; createNewItem stores them raw.\n const newItem = await Item.create({ modelName, ...data } as Parameters<typeof Item.create>[0], options)\n return (newItem ?? undefined) as Item<any> | undefined\n } catch (err) {\n logger('[useCreateItem] Error creating item:', err)\n setError(err instanceof Error ? err : new Error(String(err)))\n return undefined\n } finally {\n inFlightRef.current = null\n // Defer clearing loading so React can commit the loading=true render first.\n // Otherwise the test (or UI) may never observe isLoading true (same continuation batching).\n queueMicrotask(() => setIsLoading(false))\n }\n }\n const promise = run()\n inFlightRef.current = promise\n return promise\n },\n [],\n )\n\n return {\n createItem,\n isLoading,\n error,\n resetError,\n }\n}\n\ntype UsePublishItemReturn = {\n /**\n * Calls `item.publish(options)`, which needs @seedprotocol/publish: `initPublish()` or\n * `<PublishProvider config>` must have run, or `error` reports that publishing is not configured.\n */\n publishItem: (item: Item<any> | undefined, options?: ItemPublishOptions) => void\n isLoading: boolean\n error: Error | null\n resetError: () => void\n}\n\nexport const usePublishItem = (): UsePublishItemReturn => {\n const [publishingItem, setPublishingItem] = useState<Item<any> | null>(null)\n const [isLoading, setIsLoading] = useState(false)\n const [error, setError] = useState<Error | null>(null)\n const subscriptionRef = useRef<Subscription | undefined>(undefined)\n\n const resetError = useCallback(() => setError(null), [])\n\n const publishItem = useCallback((item: Item<any> | undefined, options?: ItemPublishOptions) => {\n if (!item) return\n setPublishingItem(item)\n setError(null)\n // The promise is the one source of errors: it also rejects before the item machine\n // starts (publishing not configured, item not owned, already publishing).\n item.publish(options).catch((err: unknown) => {\n setError(err instanceof Error ? err : new Error(String(err)))\n })\n }, [])\n\n useEffect(() => {\n if (!publishingItem) {\n subscriptionRef.current?.unsubscribe()\n subscriptionRef.current = undefined\n setIsLoading(false)\n return\n }\n\n subscriptionRef.current?.unsubscribe()\n const service = publishingItem.getService()\n const subscription = service.subscribe((snapshot: any) => {\n setIsLoading(snapshot?.value === 'publishing')\n })\n\n subscriptionRef.current = subscription\n setIsLoading(service.getSnapshot()?.value === 'publishing')\n\n return () => {\n subscriptionRef.current?.unsubscribe()\n subscriptionRef.current = undefined\n }\n }, [publishingItem])\n\n return {\n publishItem,\n isLoading,\n error,\n resetError,\n }\n}\n\nfunction sameIds(a: Set<string>, b: Set<string>): boolean {\n return a.size === b.size && [...a].every((id) => b.has(id))\n}\n","import { useEffect, useRef, useState } from 'react'\nimport { isChangeFeedRunning, watchEntityRows, type ChangeWatch } from '@seedprotocol/sdk'\nimport { useIsClientReady } from './client'\nimport { queryKeyOf, toError, useLiveQueryState } from './liveQuery'\n\ntype RowsQuery = { all: () => Promise<unknown[]>; toSQL: () => { sql: string; params: unknown[] } }\n\n/**\n * Rows of a Drizzle query on one seed's rows, re-read when the SDK's change feed reports a write\n * to them (ADR 0020), instead of a liveQuery that re-runs on every write to the tables it reads.\n * `watch` names the seed and tables the query reads; it must cover every row the query returns.\n * Same `{ data, error }` as useLiveQueryState: `data` is undefined until the first read and resets\n * when the query or watch changes. A failed read reports `error` and keeps the last rows; the next\n * change reads again. Where the feed doesn't run (Node), falls back to useLiveQueryState.\n */\nexport function useEntityRowsState<T>(\n watch: ChangeWatch | null | undefined,\n query: RowsQuery | null | undefined,\n): { data: T[] | undefined; error: Error | null } {\n const isClientReady = useIsClientReady()\n const onFeed = isClientReady && isChangeFeedRunning()\n const fallback = useLiveQueryState<T>(onFeed ? null : query)\n\n const [state, setState] = useState<{ data: T[] | undefined; error: Error | null }>({\n data: undefined,\n error: null,\n })\n const key = watch && query ? `${JSON.stringify(watch)}|${String(queryKeyOf(query))}` : null\n const inputsRef = useRef({ watch, query })\n inputsRef.current = { watch, query }\n\n useEffect(() => {\n setState((prev) => (prev.data === undefined && prev.error === null ? prev : { data: undefined, error: null }))\n const { watch: currentWatch, query: currentQuery } = inputsRef.current\n if (!onFeed || !key || !currentWatch || !currentQuery) return\n const subscription = watchEntityRows<T[]>(\n currentWatch,\n () => currentQuery.all() as Promise<T[]>,\n (rows) => setState({ data: [...rows], error: null }),\n {\n immediate: true,\n onError: (error) => setState((prev) => ({ data: prev.data, error: toError(error) })),\n },\n )\n return () => subscription.unsubscribe()\n }, [key, onFeed])\n\n return onFeed ? state : fallback\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport { Subscription } from 'xstate'\nimport debug from 'debug'\nimport { ItemProperty, ItemPropertySaveValidationError } from '@seedprotocol/sdk'\nimport { useIsClientReady } from './client'\nimport type { ChangeWatch, IItemProperty } from '@seedprotocol/sdk'\nimport { useEntityRowsState } from './entityRows'\nimport { useSeedQueryClient } from './queryClient'\nimport { BaseDb } from '@seedprotocol/sdk'\nimport { metadata } from '@seedprotocol/sdk'\nimport { seeds } from '@seedprotocol/sdk'\nimport { and, eq, isNotNull } from 'drizzle-orm'\nimport { pickLatestMetadataRowPerProperty } from '@seedprotocol/sdk'\nimport { debounce, startCase } from 'lodash-es'\nimport { useQuery, useQueryClient } from '@tanstack/react-query'\n\nconst logger = debug('seedSdk:react:property')\nconst propertiesLogger = debug('seedSdk:react:itemProperties')\n\n\ntype UseItemPropertyReturn = {\n property: IItemProperty | undefined\n isLoading: boolean\n error: Error | null\n}\n\n/** Query key for one item property: `useItemProperty`'s query. */\nexport const getItemPropertyQueryKey = (\n seedLocalId: string | undefined,\n seedUid: string | undefined,\n propertyName: string,\n) => ['seed', 'itemProperty', seedLocalId ?? null, seedUid ?? null, propertyName] as const\n\n/**\n * Hook to get a specific ItemProperty instance\n * Can be called in multiple ways:\n * 1. With seedLocalId/seedUid and propertyName: useItemProperty({ seedLocalId, propertyName }) or useItemProperty({ seedUid, propertyName })\n * 2. With itemId and propertyName: useItemProperty(itemId, propertyName) or useItemProperty({ itemId, propertyName })\n *\n * A property already in the instance cache and ready (`ItemProperty.peekReady`) comes back on the\n * first render with `isLoading: false`. Changing the lookup never returns the previous property.\n *\n * @overload\n * @param props - Object with seedLocalId or seedUid, and propertyName\n * @returns Object with property, isLoading, and error\n *\n * @overload\n * @param props - Object with itemId and propertyName\n * @returns Object with property, isLoading, and error\n *\n * @overload\n * @param itemId - The item ID (seedLocalId or seedUid)\n * @param propertyName - The name of the property\n * @returns Object with property, isLoading, and error\n */\nexport function useItemProperty(props: {\n seedLocalId?: string\n seedUid?: string\n propertyName: string\n}): UseItemPropertyReturn\nexport function useItemProperty(props: {\n itemId?: string\n propertyName: string\n}): UseItemPropertyReturn\nexport function useItemProperty(\n itemId: string,\n propertyName: string\n): UseItemPropertyReturn\nexport function useItemProperty(\n arg1:\n | { seedLocalId?: string; seedUid?: string; propertyName: string }\n | { itemId: string; propertyName: string }\n | string,\n arg2?: string\n) {\n const isClientReady = useIsClientReady()\n const queryClient = useSeedQueryClient()\n const [, setVersion] = useState(0) // Version counter to force re-renders\n\n // Extract primitives so useMemo/useCallback deps are stable when caller passes inline objects\n // Support object form with itemId: useItemProperty({ itemId, propertyName })\n const arg1IsObject = typeof arg1 === 'object' && arg1 != null\n const obj = arg1IsObject ? (arg1 as Record<string, unknown>) : null\n const itemIdFromObj = obj != null ? (obj.itemId as string | undefined) : undefined\n const seedLocalId =\n obj != null ? (obj.seedLocalId as string | undefined) : undefined\n const seedUid =\n obj != null ? (obj.seedUid as string | undefined) : undefined\n const propertyNameFromObj =\n obj != null ? (obj.propertyName as string | undefined) : undefined\n const itemId =\n typeof arg1 === 'string' ? arg1 : (itemIdFromObj !== undefined && itemIdFromObj !== '' ? itemIdFromObj : undefined)\n const propertyNameFromArgs = typeof arg1 === 'string' ? arg2 : undefined\n const propertyName = propertyNameFromObj ?? propertyNameFromArgs\n\n // Determine which lookup mode we're in based on arguments (deps are primitives to avoid infinite loop)\n // Unify itemId and identifiers: when itemId is provided (string or object form), use it as seedLocalId so we hit the same code path\n const lookupMode = useMemo(() => {\n const resolvedSeedLocalId = (itemId !== undefined && itemId !== '') ? itemId : seedLocalId\n const resolvedSeedUid = (itemId !== undefined && itemId !== '') ? undefined : seedUid\n if ((resolvedSeedLocalId || resolvedSeedUid) && propertyName) {\n return {\n type: 'identifiers' as const,\n seedLocalId: resolvedSeedLocalId || undefined,\n seedUid: resolvedSeedUid || undefined,\n propertyName,\n // An itemId may be a seedLocalId or a seedUid: tried as the local id first\n itemId: itemId !== undefined && itemId !== '' ? itemId : undefined,\n }\n }\n return null\n }, [itemId, propertyName, seedLocalId, seedUid])\n\n const queryKey = useMemo(\n () => getItemPropertyQueryKey(lookupMode?.seedLocalId, lookupMode?.seedUid, lookupMode?.propertyName ?? ''),\n [lookupMode],\n )\n\n const query = useQuery(\n {\n queryKey,\n queryFn: async (): Promise<IItemProperty | null> => {\n if (!lookupMode) return null\n // A ready cached property is what find() would return. No hold here: the hook holds the\n // property it shows while mounted (useHeldItemProperties).\n const found =\n ItemProperty.peekReady(lookupMode) ??\n (await ItemProperty.find({\n propertyName: lookupMode.propertyName,\n seedLocalId: lookupMode.seedLocalId,\n seedUid: lookupMode.seedUid,\n hold: false,\n }))\n // itemId may be a seedUid (an item synced from EAS): it was only ever looked up as a local id\n const foundByUid =\n !found && lookupMode.itemId\n ? await ItemProperty.find({ propertyName: lookupMode.propertyName, seedUid: lookupMode.itemId, hold: false })\n : undefined\n const result = found ?? foundByUid\n if (!result) {\n logger(\n `[useItemProperty] no property found for Item.${lookupMode.seedLocalId || lookupMode.seedUid}.${lookupMode.propertyName}`,\n )\n }\n return (result as IItemProperty | undefined) ?? null\n },\n // The ItemProperty instance cache is the cache; see useItem.\n initialData: () =>\n (lookupMode ? ItemProperty.peekReady(lookupMode) : undefined) as IItemProperty | undefined,\n staleTime: 0,\n gcTime: 0,\n // An ItemProperty is a live actor-backed object, not data to compare field by field.\n structuralSharing: false,\n // find() throws when the property fails to load or times out; report that, as before.\n retry: false,\n enabled: isClientReady && !!lookupMode,\n },\n queryClient,\n )\n const property = query.data ?? undefined\n useHeldItemProperties(useMemo(() => (property ? [property] : []), [property]))\n\n // Re-render on the property's own changes (value updates after it loaded).\n useEffect(() => {\n if (!property) return\n\n const bump = () => setVersion((prev) => prev + 1)\n const initialContext = (property.getService().getSnapshot() as any)?.context\n let lastRenderValue: unknown = initialContext?.renderValue\n let lastPropertyValue: unknown = initialContext?.propertyValue\n let wasIdle = false\n let lastVersionAt = 0\n let trailing: ReturnType<typeof setTimeout> | undefined\n const THROTTLE_MS = 50\n const subscription: Subscription = property.getService().subscribe((snapshot: any) => {\n const ctx = snapshot?.context ?? {}\n // A value change re-renders at once: a controlled input bound to property.value that\n // skipped a render was reset to the old value, and the next keystroke built on it.\n if (!Object.is(ctx.renderValue, lastRenderValue) || !Object.is(ctx.propertyValue, lastPropertyValue)) {\n lastRenderValue = ctx.renderValue\n lastPropertyValue = ctx.propertyValue\n wasIdle = snapshot.value === 'idle'\n lastVersionAt = Date.now()\n clearTimeout(trailing)\n trailing = undefined\n bump()\n return\n }\n const isIdle = snapshot.value === 'idle'\n if (isIdle) {\n // Idle snapshots repeat often; re-render only on arriving there.\n if (!wasIdle) {\n wasIdle = true\n clearTimeout(trailing)\n trailing = undefined\n bump()\n }\n return\n }\n wasIdle = false\n // Other fields (saving flags, errors) change on many snapshots in a row: throttle those, with a\n // trailing render so the last one is never dropped.\n const now = Date.now()\n if (now - lastVersionAt >= THROTTLE_MS) {\n lastVersionAt = now\n bump()\n } else if (!trailing) {\n trailing = setTimeout(() => {\n trailing = undefined\n lastVersionAt = Date.now()\n bump()\n }, THROTTLE_MS - (now - lastVersionAt))\n }\n })\n\n return () => {\n clearTimeout(trailing)\n subscription.unsubscribe()\n }\n }, [property])\n\n return {\n property,\n isLoading: !!lookupMode && query.isPending,\n error: query.error as Error | null,\n }\n}\n\ntype UseDebouncedItemPropertyParams =\n | { seedLocalId?: string; seedUid?: string; propertyName: string }\n | { itemId: string; propertyName: string }\n\ntype UseDebouncedItemPropertyReturn = {\n property: IItemProperty | undefined\n setValue: (value: string) => void\n /** Save a pending edit now instead of after the debounce delay (e.g. before navigating). */\n flush: () => void\n isLoading: boolean\n error: Error | null\n}\n\n/**\n * Hook for real-time ItemProperty updates with debounced persistence.\n * Updates the display immediately on each change while debouncing writes to the database.\n * Use this for text inputs and other high-frequency updates.\n *\n * @param params - Same as useItemProperty: { seedLocalId, propertyName }, { seedUid, propertyName }, or { itemId, propertyName }\n * @param debounceMs - Debounce delay for persistence (default: 300)\n */\nexport function useDebouncedItemProperty(\n params: UseDebouncedItemPropertyParams,\n debounceMs = 300\n): UseDebouncedItemPropertyReturn {\n const itemId = 'itemId' in params ? params.itemId : undefined\n const seedLocalId = 'seedLocalId' in params ? params.seedLocalId : undefined\n const seedUid = 'seedUid' in params ? params.seedUid : undefined\n const propertyName = params.propertyName\n\n const normalizedParams = useMemo(() => {\n if (itemId) {\n return { itemId, propertyName }\n }\n return { seedLocalId, seedUid, propertyName }\n }, [itemId, seedLocalId, seedUid, propertyName])\n\n const { property, isLoading, error: queryError } = useItemProperty(normalizedParams)\n const latestValueRef = useRef<string>('')\n const saveError = usePropertySaveError(property)\n\n const debouncedPersist = useMemo(\n () =>\n debounce((prop: IItemProperty) => {\n // setValue looks up a missing propertyRecordSchema and, when the save is refused (item not\n // owned) or breaks a rule, rolls back to what showed before the first keystroke.\n prop.setValue(latestValueRef.current)\n }, debounceMs),\n [debounceMs]\n )\n\n // Save a pending edit when the component unmounts (or the delay changes); cancelling here lost\n // the last keystrokes typed within the debounce window.\n useEffect(() => {\n return () => debouncedPersist.flush()\n }, [debouncedPersist])\n\n const flush = useCallback(() => debouncedPersist.flush(), [debouncedPersist])\n\n const setValue = useCallback(\n (value: string) => {\n if (!property) return\n latestValueRef.current = value\n property.showValue(value)\n debouncedPersist(property)\n },\n [property, debouncedPersist]\n )\n\n return {\n property,\n setValue,\n flush,\n isLoading,\n error: queryError ?? saveError,\n }\n}\n\n/**\n * The property's last save failure: `_saveError` (an OwnershipError for an item you don't own,\n * say), or an ItemPropertySaveValidationError when the value broke a rule. Cleared by the next save.\n */\nfunction usePropertySaveError(property: IItemProperty | undefined): Error | null {\n const [error, setError] = useState<Error | null>(null)\n useEffect(() => {\n if (!property) {\n setError(null)\n return\n }\n const read = (ctx: any): Error | null => {\n if (ctx?._saveError) return ctx._saveError as Error\n const validationErrors = ctx?._saveValidationErrors\n if (validationErrors?.length) {\n return new ItemPropertySaveValidationError(\n `Invalid value for property ${ctx.propertyName}: ${validationErrors.map((e: { message: string }) => e.message).join('; ')}`,\n validationErrors,\n )\n }\n return null\n }\n let lastSource: unknown\n const update = (ctx: any) => {\n // Compare the context fields, not the built error, so an unchanged failure keeps its identity.\n const source = ctx?._saveError || (ctx?._saveValidationErrors?.length ? ctx._saveValidationErrors : null)\n if (source === lastSource) return\n lastSource = source\n setError(read(ctx))\n }\n update((property.getService().getSnapshot() as any)?.context)\n const subscription: Subscription = property.getService().subscribe((snapshot: any) => update(snapshot?.context))\n return () => subscription.unsubscribe()\n }, [property])\n return error\n}\n\ntype UseItemPropertiesReturn = {\n properties: IItemProperty[]\n isLoading: boolean\n error: Error | null\n}\n\n/**\n * Holds each property while the component shows it, so an item unloading elsewhere doesn't stop\n * it. The release is deferred a tick: a StrictMode remount or a refetch returning the same\n * instances takes its new hold before the old one is dropped, so the count never touches 0.\n */\nfunction useHeldItemProperties(properties: readonly IItemProperty[]): void {\n useEffect(() => {\n const held = properties.filter((p) => ItemProperty.retain(p))\n return () => {\n setTimeout(() => {\n for (const p of held) p.unload()\n }, 0)\n }\n }, [properties])\n}\n\n/** Fetches item properties list for useQuery (shared with useItemProperties). */\nasync function fetchItemPropertiesList(\n seedLocalId: string | undefined,\n seedUid: string | undefined\n): Promise<IItemProperty[]> {\n if (!seedLocalId && !seedUid) return []\n const db = BaseDb.getAppDb()\n if (!db) return []\n\n const baseList = await ItemProperty.all(\n { seedLocalId: seedLocalId ?? undefined, seedUid: seedUid ?? undefined },\n { waitForReady: true, hold: false },\n )\n const _itemProperties: IItemProperty[] = [...(baseList as IItemProperty[])]\n\n const propertiesWithMetadata = new Set<string>()\n for (const p of baseList) {\n if (p.propertyName) propertiesWithMetadata.add(p.propertyName)\n }\n\n let modelName: string | undefined\n if (baseList.length > 0) {\n const first = baseList[0]\n modelName = first.modelName ?? (first as any).modelType\n if (modelName && typeof modelName === 'string') modelName = startCase(modelName)\n }\n // The seed records which model it belongs to (model names are only unique per schema)\n const seedRecords = await db\n .select({ type: seeds.type, modelFileId: seeds.modelFileId })\n .from(seeds)\n .where(seedUid ? eq(seeds.uid, seedUid) : eq(seeds.localId, seedLocalId!))\n .limit(1)\n const modelFileId: string | undefined = seedRecords[0]?.modelFileId ?? undefined\n if (!modelName && seedRecords.length > 0 && seedRecords[0].type) {\n modelName = startCase(seedRecords[0].type)\n }\n\n const modelProperties: string[] = []\n if (modelName) {\n try {\n const { Model } = await import('@seedprotocol/sdk')\n const model = await Model.resolveAsync(modelName, { modelFileId })\n if (model?.properties) {\n for (const modelProperty of model.properties) {\n if (modelProperty.name) modelProperties.push(modelProperty.name)\n }\n }\n } catch (error) {\n propertiesLogger(`[useItemProperties] Error getting ModelProperties for ${modelName}:`, error)\n }\n }\n\n if (modelName && modelProperties.length > 0) {\n const resolvedSeedLocalId = baseList.length > 0 ? (baseList[0].seedLocalId ?? seedLocalId) : seedLocalId\n const resolvedSeedUid = baseList.length > 0 ? (baseList[0].seedUid ?? seedUid) : seedUid\n for (const propertyName of modelProperties) {\n if (propertiesWithMetadata.has(propertyName)) continue\n try {\n const itemProperty = ItemProperty.create(\n {\n propertyName,\n modelName,\n seedLocalId: resolvedSeedLocalId || undefined,\n seedUid: resolvedSeedUid || undefined,\n propertyValue: null,\n },\n { waitForReady: false, hold: false },\n )\n if (itemProperty) _itemProperties.push(itemProperty as any as IItemProperty)\n } catch (error) {\n logger(`[useItemProperties] Error creating ItemProperty for missing property ${propertyName}:`, error)\n }\n }\n }\n\n if (seedLocalId || seedUid) {\n const seedRecords = await db\n .select({ createdAt: seeds.createdAt })\n .from(seeds)\n .where(seedUid ? eq(seeds.uid, seedUid) : eq(seeds.localId, seedLocalId!))\n .limit(1)\n if (seedRecords.length > 0 && seedRecords[0].createdAt) {\n const createdAtPropertyName = 'createdAt'\n const hasCreatedAtProperty = _itemProperties.some((p) => p.propertyName === createdAtPropertyName)\n if (!hasCreatedAtProperty && modelName) {\n try {\n const resolvedSeedLocalId = baseList.length > 0 ? (baseList[0].seedLocalId ?? seedLocalId) : seedLocalId\n const resolvedSeedUid = baseList.length > 0 ? (baseList[0].seedUid ?? seedUid) : seedUid\n const createdAtProperty = ItemProperty.create(\n {\n propertyName: createdAtPropertyName,\n modelName,\n seedLocalId: resolvedSeedLocalId || undefined,\n seedUid: resolvedSeedUid || undefined,\n propertyValue: seedRecords[0].createdAt.toString(),\n },\n { waitForReady: false, hold: false },\n )\n if (createdAtProperty) _itemProperties.push(createdAtProperty as any as IItemProperty)\n } catch (error) {\n logger(`[useItemProperties] Error creating createdAt ItemProperty:`, error)\n }\n }\n }\n }\n\n return _itemProperties\n}\n\n/**\n * Hook to get all ItemProperty instances for a specific item\n * Can be called in multiple ways:\n * 1. With seedLocalId: useItemProperties({ seedLocalId })\n * 2. With seedUid: useItemProperties({ seedUid })\n * 3. With itemId: useItemProperties(itemId)\n * \n * Re-reads the item's metadata rows when the SDK's change feed reports a write to them, and\n * updates the returned ItemProperty instances when they change.\n *\n * The list has one entry per model property, including ones the item has no value for yet\n * (their `value` is null), plus a read-only `createdAt` entry built from the item's creation\n * time. Filter on `propertyName` or `value` if you only want stored values.\n * \n * @overload\n * @param props - Object with seedLocalId or seedUid\n * @returns Object with properties array, isLoading, and error\n * \n * @overload\n * @param itemId - The item ID (seedLocalId or seedUid)\n * @returns Object with properties array, isLoading, and error\n */\nexport function useItemProperties(props: {\n seedLocalId?: string\n seedUid?: string\n}): UseItemPropertiesReturn\nexport function useItemProperties(itemId: string): UseItemPropertiesReturn\nexport function useItemProperties(\n arg1: { seedLocalId?: string; seedUid?: string } | string\n): UseItemPropertiesReturn {\n\n const isClientReady = useIsClientReady()\n const queryClient = useQueryClient()\n const previousTableDataRef = useRef<string | undefined>(undefined)\n\n // Determine which lookup mode we're in based on arguments\n const lookupMode = useMemo(() => {\n if (typeof arg1 === 'string') {\n // String argument: itemId (assumed to be seedLocalId)\n return { type: 'itemId' as const, itemId: arg1 }\n } else if (typeof arg1 === 'object') {\n // Object argument: { seedLocalId/seedUid }\n return {\n type: 'identifiers' as const,\n seedLocalId: arg1.seedLocalId,\n seedUid: arg1.seedUid,\n }\n } else {\n return null\n }\n }, [arg1])\n\n // Determine seedLocalId and seedUid for query\n const seedLocalId = useMemo(() => {\n if (!lookupMode) return undefined\n if (lookupMode.type === 'itemId') {\n return lookupMode.itemId\n } else {\n return lookupMode.seedLocalId\n }\n }, [lookupMode])\n\n const seedUid = useMemo(() => {\n if (!lookupMode || lookupMode.type === 'itemId') return undefined\n return lookupMode.seedUid\n }, [lookupMode])\n\n const canonicalItemKey = seedLocalId ?? seedUid ?? ''\n const itemPropertiesQueryKey = useMemo(\n () => (['seed', 'itemProperties', canonicalItemKey] as const),\n [canonicalItemKey],\n )\n\n const {\n data: properties = [],\n isLoading,\n error: queryError,\n } = useQuery({\n queryKey: itemPropertiesQueryKey,\n queryFn: () => fetchItemPropertiesList(seedLocalId, seedUid),\n enabled: isClientReady && !!canonicalItemKey,\n // The instances are held only while a hook shows them: a list kept after unmount could hold\n // stopped ones.\n gcTime: 0,\n })\n useHeldItemProperties(properties)\n\n // Query metadata table directly and filter for latest records in JavaScript\n // Get db inside useMemo to avoid recreating query on each render\n // Drizzle query builders are new objects each time, but useMemo will only recreate\n // when dependencies change, which is what we want for reactive queries\n const propertiesQuery = useMemo(() => {\n if (!isClientReady || (!seedLocalId && !seedUid)) {\n propertiesLogger('[useItemProperties] Query: returning null (not ready or no identifiers)')\n return null\n }\n \n const db = BaseDb.getAppDb()\n if (!db) {\n propertiesLogger('[useItemProperties] Query: returning null (no db)')\n return null\n }\n \n propertiesLogger(`[useItemProperties] Query: creating query for seedLocalId=${seedLocalId}, seedUid=${seedUid}`)\n \n // Query metadata table directly - we'll filter for latest records in JavaScript\n const query = seedUid\n ? db\n .select({\n propertyName: metadata.propertyName,\n propertyValue: metadata.propertyValue,\n seedLocalId: metadata.seedLocalId,\n seedUid: metadata.seedUid,\n modelType: metadata.modelType,\n schemaUid: metadata.schemaUid,\n createdAt: metadata.createdAt,\n attestationCreatedAt: metadata.attestationCreatedAt,\n localId: metadata.localId,\n revokedAt: metadata.revokedAt,\n })\n .from(metadata)\n .where(\n and(\n eq(metadata.seedUid, seedUid),\n isNotNull(metadata.propertyName)\n )\n )\n : seedLocalId\n ? db\n .select({\n propertyName: metadata.propertyName,\n propertyValue: metadata.propertyValue,\n seedLocalId: metadata.seedLocalId,\n seedUid: metadata.seedUid,\n modelType: metadata.modelType,\n schemaUid: metadata.schemaUid,\n createdAt: metadata.createdAt,\n attestationCreatedAt: metadata.attestationCreatedAt,\n localId: metadata.localId,\n revokedAt: metadata.revokedAt,\n })\n .from(metadata)\n .where(\n and(\n eq(metadata.seedLocalId, seedLocalId),\n isNotNull(metadata.propertyName)\n )\n )\n : null\n \n propertiesLogger(`[useItemProperties] Query: created query object`, { queryType: seedUid ? 'seedUid' : 'seedLocalId' })\n return query\n }, [isClientReady, seedLocalId, seedUid])\n \n // Re-read when the change feed reports a write to this item's metadata rows (ADR 0020): a page\n // mounting this hook per row no longer re-runs every mount's query on every metadata write.\n const propertiesWatch = useMemo<ChangeWatch | null>(() => {\n if (seedUid) return { seedUid, tables: ['metadata'] }\n if (seedLocalId) return { seedLocalId, tables: ['metadata'] }\n return null\n }, [seedLocalId, seedUid])\n const rawPropertiesTableData = useEntityRowsState<{\n propertyName: string | null\n propertyValue: string | null\n seedLocalId: string | null\n seedUid: string | null\n modelType: string | null\n schemaUid: string | null\n createdAt: number | null\n attestationCreatedAt: number | null\n localId: string | null\n revokedAt: number | null\n }>(propertiesWatch, propertiesQuery).data\n \n // Filter for latest records (one per propertyName) in JavaScript\n const propertiesTableData = useMemo(() => {\n if (!rawPropertiesTableData || rawPropertiesTableData.length === 0) {\n return []\n }\n\n // One record per propertyName, in the SDK's reader order (live before revoked, then newest)\n return pickLatestMetadataRowPerProperty(rawPropertiesTableData)\n }, [rawPropertiesTableData])\n\n // Invalidate when metadata table data actually changes so useQuery refetches\n useEffect(() => {\n if (!isClientReady || (!seedLocalId && !seedUid) || propertiesTableData === undefined) return\n\n // Include propertyValue so value-only changes produce a different string and trigger invalidation\n const tableDataString = JSON.stringify(\n propertiesTableData\n .map((p) => ({\n propertyName: p.propertyName,\n propertyValue: p.propertyValue,\n seedLocalId: p.seedLocalId,\n seedUid: p.seedUid,\n }))\n .sort((a, b) => (a.propertyName || '').localeCompare(b.propertyName || ''))\n )\n\n if (previousTableDataRef.current === tableDataString) return\n previousTableDataRef.current = tableDataString\n\n // Invalidate when metadata table data changed (new/updated/removed props or value changes)\n // so useQuery refetches and UI shows latest values.\n if (propertiesTableData.length > 0) {\n queryClient.invalidateQueries({ queryKey: itemPropertiesQueryKey })\n }\n }, [isClientReady, propertiesTableData, properties, seedLocalId, seedUid, queryClient, itemPropertiesQueryKey])\n\n useEffect(() => {\n previousTableDataRef.current = undefined\n }, [seedLocalId, seedUid])\n\n return {\n properties,\n isLoading,\n error: queryError as Error | null,\n }\n}\n\nexport type UseCreateItemPropertyProps = {\n seedLocalId?: string\n seedUid?: string\n propertyName: string\n modelName: string\n propertyValue?: any\n versionLocalId?: string\n versionUid?: string\n [key: string]: any\n}\n\nexport type UseCreateItemPropertyReturn = {\n create: (props: UseCreateItemPropertyProps) => IItemProperty | undefined\n isLoading: boolean\n error: Error | null\n resetError: () => void\n}\n\n/**\n * Hook to create an ItemProperty with loading and error state.\n * create(props) creates a new property instance for an item; provide seedLocalId or seedUid, propertyName, and modelName.\n */\nexport const useCreateItemProperty = (): UseCreateItemPropertyReturn => {\n const subscriptionRef = useRef<Subscription | undefined>(undefined)\n const [isLoading, setIsLoading] = useState(false)\n const [error, setError] = useState<Error | null>(null)\n\n const resetError = useCallback(() => setError(null), [])\n\n const create = useCallback((props: UseCreateItemPropertyProps): IItemProperty | undefined => {\n if (!props.propertyName || (!props.seedLocalId && !props.seedUid) || !props.modelName) {\n const err = new Error('seedLocalId or seedUid, propertyName, and modelName are required')\n setError(err)\n return undefined\n }\n\n setError(null)\n setIsLoading(true)\n subscriptionRef.current?.unsubscribe()\n subscriptionRef.current = undefined\n\n const instance = ItemProperty.create(props, { waitForReady: false })\n if (!instance) {\n setError(new Error('Failed to create item property'))\n setIsLoading(false)\n return undefined\n }\n\n const subscription = instance.getService().subscribe((snapshot: any) => {\n if (snapshot?.value === 'error') {\n const err = (snapshot.context as any)?._loadingError?.error ?? new Error('Failed to create item property')\n setError(err instanceof Error ? err : new Error(String(err)))\n setIsLoading(false)\n }\n if (snapshot?.value === 'idle') {\n setError(null)\n setIsLoading(false)\n }\n })\n subscriptionRef.current = subscription\n return instance\n }, [])\n\n useEffect(() => {\n return () => {\n subscriptionRef.current?.unsubscribe()\n subscriptionRef.current = undefined\n }\n }, [])\n\n return {\n create,\n isLoading,\n error,\n resetError,\n }\n}\n\nexport type UseDestroyItemPropertyReturn = {\n destroy: (itemProperty: IItemProperty) => Promise<void>\n isLoading: boolean\n error: Error | null\n resetError: () => void\n}\n\nexport const useDestroyItemProperty = (): UseDestroyItemPropertyReturn => {\n const [isLoading, setIsLoading] = useState(false)\n const [error, setError] = useState<Error | null>(null)\n\n // Loading state is tracked here: the item property's service doesn't record destroy progress, and\n // destroy() stops the service, often within a few microtasks.\n const destroy = useCallback(async (itemProperty: IItemProperty) => {\n if (!itemProperty) return\n setError(null)\n setIsLoading(true)\n try {\n await itemProperty.destroy()\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)))\n throw err\n } finally {\n setIsLoading(false)\n }\n }, [])\n\n const resetError = useCallback(() => setError(null), [])\n\n return {\n destroy,\n isLoading,\n error,\n resetError,\n }\n}\n","import { ClientManagerState } from '@seedprotocol/sdk'\nimport { useClientSelector } from './client'\n\n/**\n * Client states in which the app database is open: DB_INIT finishes with dbReady and moves to\n * SAVE_CONFIG. DB_INIT itself (still opening, may fail) and INIT_FAILED are not ready.\n */\nconst DB_READY_STATES = new Set<string>([\n ClientManagerState.SAVE_CONFIG,\n ClientManagerState.PROCESS_SCHEMA_FILES,\n ClientManagerState.ADD_MODELS_TO_STORE,\n ClientManagerState.ADD_MODELS_TO_DB,\n ClientManagerState.IDLE,\n])\n\nexport const isDbReadyState = (state: unknown): boolean =>\n typeof state === 'string' && DB_READY_STATES.has(state)\n\n/**\n * Whether the app database is open. True once the client has opened it, false again if init\n * fails or restarts, or after `client.stop()`.\n */\nexport const useDbsAreReady = () => {\n const dbsAreReady = useClientSelector(\n (snapshot) => snapshot.status === 'active' && isDbReadyState(snapshot.value),\n )\n return {\n dbsAreReady,\n }\n}\n","import { useCallback, useEffect, useMemo, useState, useSyncExternalStore } from 'react'\nimport { ActorRef } from 'xstate'\nimport pluralize from 'pluralize'\nimport debug from 'debug'\nimport { ClientManagerState, MachineIds } from '@seedprotocol/sdk'\nimport { isDbReadyState } from './db'\nimport { useClientSelector, useClientService } from './client'\n\nconst logger = debug('seedSdk:react:services')\n\nconst finalStrings = ['idle', 'ready', 'done', 'success', 'initialized']\n\nexport const getServiceName = (service: ActorRef<any, any>) => {\n let name = 'actor'\n const serviceAny = service as any\n if (service && serviceAny.uniqueKey) {\n name = serviceAny.uniqueKey\n }\n if (service && !serviceAny.uniqueKey && serviceAny.logic && serviceAny.logic.config) {\n name = getServiceUniqueKey(service)\n }\n return name\n}\n\nexport const getServiceValue = (\n service: ActorRef<any, any>,\n): string | undefined => {\n let value\n if (service && service.getSnapshot() && service.getSnapshot().value) {\n value = service.getSnapshot().value\n }\n if (getServiceName(service) === 'global') {\n if (\n value &&\n typeof value === 'object' &&\n Object.keys(value).length > 0 &&\n Object.keys(value)[0] === 'initialized'\n ) {\n value = 'ready'\n }\n }\n if (value && typeof value === 'object') {\n value = JSON.stringify(value)\n }\n return value\n}\n\nexport const getServiceUniqueKey = (service: ActorRef<any, any>) => {\n const serviceAny = service as any\n if (!service || !serviceAny.logic || !serviceAny.logic.config || !serviceAny._snapshot) {\n return\n }\n const config = serviceAny.logic.config\n if (!config.id) {\n return\n }\n let uniqueKey = config.id\n if (config.id.includes('@seedSdk/')) {\n uniqueKey = config.id.match(/^.*@seedSdk\\/(\\w+)[\\.\\w]*/)[1]\n }\n let snapshot\n try {\n snapshot = service.getSnapshot()\n } catch (error) {\n logger('Error:', error)\n return uniqueKey\n }\n if (snapshot) {\n const context = snapshot.context\n if (context && context.dbName) {\n uniqueKey = context.dbName\n }\n if (context && context.modelNamePlural) {\n uniqueKey = context.modelNamePlural\n }\n if (context && context.modelName) {\n uniqueKey = pluralize(context.modelName.toLowerCase())\n }\n }\n return uniqueKey\n}\n\nconst isClientManagerService = (service: ActorRef<any, any>): boolean =>\n (service as any)?.logic?.config?.id === MachineIds.CLIENT_MANAGER\n\n/**\n * 100 once the service reaches a final state ('idle', 'ready', ...). During startup, the client\n * manager's progress through init, or the current state's position among the `loading`-tagged\n * states for other services. 0 when there's nothing to measure.\n */\nexport const getPercentComplete = (service: ActorRef<any, any>): number => {\n const snapshot = service?.getSnapshot()\n if (isClientManagerService(service)) {\n return snapshot?.status === 'active' ? percentForClientState(snapshot.value) : 0\n }\n const value = getServiceValue(service)\n if (value && finalStrings.includes(value)) {\n return 100\n }\n const states = (service as any)?.logic?.states\n if (!value || !states) {\n return 0\n }\n const loadingStateNames = Object.entries(states)\n .filter(([, state]) => (state as { tags?: string[] }).tags?.includes('loading'))\n .map(([stateName]) => stateName)\n const stateIndex = loadingStateNames.indexOf(value)\n if (stateIndex < 0) {\n return 0\n }\n return (stateIndex / loadingStateNames.length) * 100\n}\n\nexport const useService = (service: ActorRef<any, any>) => {\n const [timeElapsed, setTimeElapsed] = useState(0)\n\n // Re-render on state changes, not only on the timer below.\n const subscribe = useCallback(\n (onChange: () => void) => {\n const subscription = service.subscribe({ next: onChange, error: onChange, complete: onChange })\n return () => subscription.unsubscribe()\n },\n [service],\n )\n const getValue = () => getServiceValue(service)\n const value = useSyncExternalStore(subscribe, getValue, getValue)\n\n const updateTime = useCallback(\n (interval: ReturnType<typeof setInterval>) => {\n const status = service.getSnapshot().value\n if (\n status === 'done' ||\n status === 'success' ||\n status === 'idle' ||\n status === 'ready'\n ) {\n clearInterval(interval)\n return\n }\n setTimeElapsed((timeElapsed) => timeElapsed + 1)\n },\n [service],\n )\n\n const startInterval = useCallback(() => {\n const interval = setInterval(() => {\n updateTime(interval)\n }, 1000)\n return interval\n }, [updateTime, service])\n\n useEffect(() => {\n const interval = startInterval()\n return () => clearInterval(interval)\n }, [])\n\n return {\n name: getServiceName(service),\n timeElapsed,\n value,\n percentComplete: getPercentComplete(service),\n uniqueKey: getServiceUniqueKey(service),\n }\n}\n\n/** Whether the app database is open (see useDbsAreReady). */\nexport const useIsDbReady = () => {\n const { internalStatus } = useGlobalServiceStatus()\n return internalStatus === 'ready'\n}\n\n/**\n * @deprecated Does nothing: actor snapshots were never written, and loaded ones were discarded.\n * Will be removed.\n */\nexport const usePersistedSnapshots = () => {}\n\n/**\n * @deprecated Always false: nothing writes snapshot rows. Will be removed.\n */\nexport const useHasSavedSnapshots = () => false\n\nconst percentForClientState = (state: unknown): number => {\n switch (state) {\n case ClientManagerState.IDLE:\n return 100\n case ClientManagerState.ADD_MODELS_TO_DB:\n return 90\n case ClientManagerState.ADD_MODELS_TO_STORE:\n return 80\n case ClientManagerState.PROCESS_SCHEMA_FILES:\n return 70\n case ClientManagerState.SAVE_CONFIG:\n return 60\n case ClientManagerState.DB_INIT:\n return 50\n case ClientManagerState.FILE_SYSTEM_INIT:\n return 30\n case ClientManagerState.PLATFORM_CLASSES_INIT:\n return 10\n default:\n return 0\n }\n}\n\nexport const useServices = () => {\n const clientService = useClientService()\n const percentComplete = useClientSelector((snapshot) =>\n snapshot.status === 'active' ? percentForClientState(snapshot.value) : 0,\n )\n const services = useMemo(() => {\n ;(clientService as any).uniqueKey = 'clientManager'\n return [clientService as ActorRef<any, any>]\n }, [clientService])\n\n return {\n services,\n percentComplete,\n }\n}\n\nexport const useGlobalServiceStatus = () => {\n const status = useClientSelector((snapshot) => snapshot.value)\n\n // 'ready' while the app database is open (see isDbReadyState), otherwise the client state.\n const internalStatus = useClientSelector((snapshot) =>\n snapshot.status === 'active' && isDbReadyState(snapshot.value) ? 'ready' : snapshot.value,\n )\n\n return {\n status,\n internalStatus,\n }\n}\n","import { useCallback, useEffect, useRef, useState } from 'react'\n\ntype CreatedSnapshot = { value: unknown; context: unknown }\n\n/** An entity a create hook returns: Schema, Model or ModelProperty. */\ntype CreatedEntity = {\n getService(): {\n getSnapshot(): CreatedSnapshot\n subscribe(listener: (snapshot: CreatedSnapshot) => void): { unsubscribe(): void }\n }\n}\n\nconst toError = (err: unknown): Error => (err instanceof Error ? err : new Error(String(err)))\n\n/**\n * Loading and error state for a create hook. `run(create)` sets `isLoading`, calls `create`, then\n * follows the returned entity's service until it is idle or in error. The current snapshot is read\n * right after subscribing, so an entity that was already loaded (no further state change) clears\n * `isLoading` at once. A throw or rejection from `create` clears it, sets `error` and is rethrown.\n */\nexport function useCreateState(failureMessage: string) {\n const subscriptionRef = useRef<{ unsubscribe(): void } | null>(null)\n // Bumped by each run and on unmount, so a superseded async create doesn't take over the state.\n const runIdRef = useRef(0)\n const [isLoading, setIsLoading] = useState(false)\n const [error, setError] = useState<Error | null>(null)\n\n const resetError = useCallback(() => setError(null), [])\n\n const follow = useCallback(\n (entity: CreatedEntity) => {\n const apply = (snapshot: CreatedSnapshot) => {\n if (snapshot.value === 'error') {\n const err = (snapshot.context as { _loadingError?: { error?: unknown } })?._loadingError?.error\n setError(err instanceof Error ? err : new Error(failureMessage))\n setIsLoading(false)\n } else if (snapshot.value === 'idle') {\n setError(null)\n setIsLoading(false)\n }\n }\n const service = entity.getService()\n subscriptionRef.current = service.subscribe(apply)\n apply(service.getSnapshot())\n },\n [failureMessage],\n )\n\n const fail = useCallback((err: unknown): never => {\n setError(toError(err))\n setIsLoading(false)\n throw err\n }, [])\n\n const start = useCallback(() => {\n subscriptionRef.current?.unsubscribe()\n subscriptionRef.current = null\n setError(null)\n setIsLoading(true)\n return ++runIdRef.current\n }, [])\n\n /** For a synchronous SDK create. */\n const run = useCallback(\n <T extends CreatedEntity>(create: () => T): T => {\n start()\n let entity: T\n try {\n entity = create()\n } catch (err) {\n return fail(err)\n }\n follow(entity)\n return entity\n },\n [start, fail, follow],\n )\n\n /** For a create that has to await something first. */\n const runAsync = useCallback(\n async <T extends CreatedEntity>(create: () => Promise<T>): Promise<T> => {\n const runId = start()\n let entity: T\n try {\n entity = await create()\n } catch (err) {\n if (runId !== runIdRef.current) throw err\n return fail(err)\n }\n if (runId === runIdRef.current) follow(entity)\n return entity\n },\n [start, fail, follow],\n )\n\n useEffect(() => {\n return () => {\n runIdRef.current++\n subscriptionRef.current?.unsubscribe()\n subscriptionRef.current = null\n }\n }, [])\n\n return { run, runAsync, isLoading, error, resetError }\n}\n","import { loadAllSchemasFromDb } from '@seedprotocol/sdk'\nimport { useCallback, useEffect, useRef, useState, useMemo } from \"react\"\nimport { flushSync } from \"react-dom\"\nimport debug from \"debug\"\nimport { useIsClientReady } from \"./client\"\nimport { Schema } from '@seedprotocol/sdk'\nimport { SnapshotFrom, Subscription } from \"xstate\"\nimport { schemaMachine, SEED_PROTOCOL_SCHEMA_NAME } from '@seedprotocol/sdk'\nimport { useLiveQuery } from \"./liveQuery\"\nimport { BaseDb, schemas as schemasTable } from '@seedprotocol/sdk'\nimport { desc, eq, or } from \"drizzle-orm\"\nimport type { SchemaType as DbSchemaType } from '@seedprotocol/sdk'\nimport { useQuery } from \"@tanstack/react-query\"\nimport { useSeedQueryClient } from \"./queryClient\"\nimport { useCreateState } from \"./useCreateState\"\n\nconst logger = debug('seedSdk:react:schema')\n\n/**\n * Hook to get a Schema class instance (with setters) that is reactive\n * This allows you to edit schema properties directly like: schema.name = 'New name'\n * The schema instance uses a Proxy to ensure React re-renders when properties change\n * @param schemaIdentifier - The name of the schema or the schema file ID\n * - If a name is provided, retrieves the latest version with that name\n * - If an ID is provided, retrieves the specific schema by ID\n * @returns Object with schema instance\n */\nexport const useSchema = (schemaIdentifier: string | null | undefined) => {\n const [schema, setSchema] = useState<Schema | null>(null)\n const [isLoading, setIsLoading] = useState(schemaIdentifier ? true : false)\n const [error, setError] = useState<Error | null>(null)\n const isClientReady = useIsClientReady()\n\n // Each load takes a hold on the cached instance (Schema.create); the load's cleanup releases it.\n const loadSchema = useCallback(async (identifier: string, load: SchemaLoad) => {\n setIsLoading(true)\n setError(null)\n try {\n // Look the schema up only: Schema.create would add a draft schema for a name that doesn't\n // exist (e.g. a typo), so check the schemas table first.\n if (!(await schemaExists(identifier))) {\n if (!load.current) return\n setSchema(null)\n setIsLoading(false)\n return\n }\n if (!load.current) return\n const schemaInstance = Schema.create(identifier, {\n waitForReady: false,\n }) as Schema\n load.instance = schemaInstance\n setSchema(schemaInstance)\n\n const service = schemaInstance.getService()\n const apply = (snapshot: SchemaSnapshot) => {\n if (snapshot.value === 'idle') {\n flushSync(() => setIsLoading(false))\n setError(null)\n } else if (snapshot.value === 'error') {\n // A failed load used to report isLoading: true, error: null forever.\n const err = snapshot.context._loadingError?.error\n setIsLoading(false)\n setError(err instanceof Error ? err : new Error(`Failed to load schema \"${identifier}\"`))\n } else {\n setIsLoading(true)\n }\n }\n apply(service.getSnapshot())\n load.subscription = service.subscribe(apply)\n } catch (error) {\n logger('[useSchema] Error creating schema:', error)\n if (!load.current) return\n setError(error as Error)\n setSchema(null)\n setIsLoading(false)\n }\n }, [])\n\n useEffect(() => {\n if (!isClientReady) {\n setSchema(null)\n setError(null)\n setIsLoading(false)\n return\n }\n if (!schemaIdentifier) {\n setSchema(null)\n setError(null)\n setIsLoading(false)\n return\n }\n\n const load: SchemaLoad = { current: true, instance: null, subscription: null }\n void loadSchema(schemaIdentifier, load)\n\n // On unmount or identifier change: stop following the instance and release this load's hold.\n return () => {\n load.current = false\n load.subscription?.unsubscribe()\n load.subscription = null\n load.instance?.unload()\n load.instance = null\n }\n }, [schemaIdentifier, isClientReady, loadSchema])\n \n return {\n schema,\n isLoading,\n error,\n }\n\n}\n\ntype SchemaLoad = {\n current: boolean\n instance: Schema | null\n subscription: Subscription | null\n}\n\n/** Whether a schema with this name or schema file id is stored (or is the built-in one). */\nasync function schemaExists(identifier: string): Promise<boolean> {\n if (identifier === SEED_PROTOCOL_SCHEMA_NAME) return true\n const db = BaseDb.getAppDb()\n if (!db) return true // can't tell yet: let Schema.create resolve it\n const rows = await db\n .select({ id: schemasTable.id })\n .from(schemasTable)\n .where(or(eq(schemasTable.name, identifier), eq(schemasTable.schemaFileId, identifier)))\n .limit(1)\n return rows.length > 0\n}\n\nconst SEED_SCHEMAS_QUERY_KEY = ['seed', 'schemas'] as const\n\nexport const useSchemas = () => {\n const isClientReady = useIsClientReady()\n const queryClient = useSeedQueryClient()\n const previousSchemasTableDataRef = useRef<DbSchemaType[] | undefined>(undefined)\n const schemasRef = useRef<Schema[]>([])\n\n const {\n data: schemas = [],\n isLoading,\n error: queryError,\n } = useQuery({\n queryKey: SEED_SCHEMAS_QUERY_KEY,\n queryFn: () => Schema.all({ waitForReady: true }),\n enabled: isClientReady,\n }, queryClient)\n schemasRef.current = schemas\n\n // Watch the schemas table for changes and invalidate so useQuery refetches\n const db = isClientReady ? BaseDb.getAppDb() : null\n const schemasQuery = useMemo(() => {\n if (!db) return null\n return db.select().from(schemasTable).orderBy(schemasTable.name, desc(schemasTable.version))\n }, [db, isClientReady])\n const schemasTableData = useLiveQuery<DbSchemaType>(schemasQuery)\n\n // When a schema is created, addSchemaToDb posts to this channel; live query often doesn't re-run when schemas table is inserted.\n useEffect(() => {\n if (typeof BroadcastChannel === 'undefined') return\n const ch = new BroadcastChannel('seed-schemas-invalidate')\n const onMessage = () => {\n queryClient.invalidateQueries({ queryKey: SEED_SCHEMAS_QUERY_KEY })\n }\n ch.addEventListener('message', onMessage)\n return () => {\n ch.removeEventListener('message', onMessage)\n ch.close()\n }\n }, [queryClient])\n\n useEffect(() => {\n if (!isClientReady || !schemasTableData) {\n return\n }\n\n const prevData = previousSchemasTableDataRef.current\n const prevDataJson = prevData ? JSON.stringify(prevData) : 'undefined'\n const currDataJson = schemasTableData ? JSON.stringify(schemasTableData) : 'undefined'\n\n if (prevDataJson === currDataJson && prevData !== undefined) {\n return\n }\n\n previousSchemasTableDataRef.current = schemasTableData\n\n const currentSchemasSet = new Set<string>()\n for (const schema of schemasRef.current) {\n if (schema.metadata?.name === SEED_PROTOCOL_SCHEMA_NAME) continue\n const schemaFileId = schema.id || schema.schemaFileId\n if (schemaFileId) {\n currentSchemasSet.add(schemaFileId)\n } else {\n const name = schema.metadata?.name\n const version = schema.version\n if (name && version !== undefined) {\n currentSchemasSet.add(`${name}:${version}`)\n }\n }\n }\n\n const tableDataSchemasSet = new Set<string>()\n for (const dbSchema of schemasTableData) {\n if (dbSchema.name === 'Seed Protocol') continue\n if (dbSchema.schemaFileId) {\n tableDataSchemasSet.add(dbSchema.schemaFileId)\n } else if (dbSchema.name != null && dbSchema.version !== undefined) {\n tableDataSchemasSet.add(`${dbSchema.name}:${dbSchema.version}`)\n }\n }\n\n const setsAreEqual =\n currentSchemasSet.size === tableDataSchemasSet.size &&\n [...currentSchemasSet].every((id) => tableDataSchemasSet.has(id))\n\n // Only invalidate when we have data and the table has rows we might be missing (live query saw new data).\n // Skip during initial load (currentSchemasSet empty) to avoid disrupting loading state.\n // Do NOT invalidate when we have more than the table: the live query may not have updated\n // yet, and refetching could overwrite cache with stale/empty data.\n const tableHasNewRows =\n currentSchemasSet.size > 0 &&\n tableDataSchemasSet.size > 0 &&\n [...tableDataSchemasSet].some((id) => !currentSchemasSet.has(id))\n\n // A listed schema whose rows are gone was deleted (Schema.destroy): refetch so it leaves.\n const tableLostRows = [...currentSchemasSet].some((id) => !tableDataSchemasSet.has(id))\n\n if (!setsAreEqual && (tableHasNewRows || tableLostRows)) {\n queryClient.invalidateQueries({ queryKey: SEED_SCHEMAS_QUERY_KEY })\n }\n }, [isClientReady, schemasTableData, queryClient])\n\n return {\n schemas,\n isLoading,\n error: queryError as Error | null,\n }\n}\n\nexport const useCreateSchema = () => {\n const { run, isLoading, error, resetError } = useCreateState('Failed to create schema')\n\n const createSchema = useCallback(\n (schemaName: string) =>\n run(() => Schema.create(schemaName, { waitForReady: false }) as Schema),\n [run],\n )\n\n return {\n createSchema,\n isLoading,\n error,\n resetError,\n }\n}\n\nexport type UseDestroySchemaReturn = {\n destroy: (schema: Schema) => Promise<void>\n isLoading: boolean\n error: Error | null\n resetError: () => void\n}\n\nexport const useDestroySchema = (): UseDestroySchemaReturn => {\n const [isLoading, setIsLoading] = useState(false)\n const [error, setError] = useState<Error | null>(null)\n\n // Loading state is tracked here: the schema's service doesn't record destroy progress, and\n // destroy() stops the service, often within a few microtasks.\n const destroy = useCallback(async (schema: Schema) => {\n if (!schema) return\n setError(null)\n setIsLoading(true)\n try {\n await schema.destroy()\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)))\n throw err\n } finally {\n setIsLoading(false)\n }\n }, [])\n\n const resetError = useCallback(() => setError(null), [])\n\n return {\n destroy,\n isLoading,\n error,\n resetError,\n }\n}\n\n/**\n * One Schema instance per stored schema name, each at its latest version (despite the name, not\n * one per version). Instances are released on unmount; ones other hooks also use keep running.\n */\nexport const useAllSchemaVersions = () => {\n const [schemaInstances, setSchemaInstances] = useState<Schema[] | undefined | null>()\n const schemaInstancesRef = useRef<Map<string, Schema>>(new Map())\n const isClientReady = useIsClientReady()\n\n const fetchSchemas = useCallback(async () => {\n if (!isClientReady) {\n return\n }\n\n try {\n // Use DB-first approach: load all schemas from database\n // This will also import any missing schemas from files\n const allSchemasData = await loadAllSchemasFromDb()\n\n // Extract unique schema names\n const schemaNames = new Set<string>()\n for (const schemaData of allSchemasData) {\n const schemaName = schemaData.schema.metadata?.name\n if (schemaName) {\n schemaNames.add(schemaName)\n }\n }\n\n // Create/update Schema instances\n const currentInstances = new Map<string, Schema>()\n \n for (const schemaName of schemaNames) {\n // Reuse existing instance if it exists\n if (schemaInstancesRef.current.has(schemaName)) {\n const existingInstance = schemaInstancesRef.current.get(schemaName)!\n currentInstances.set(schemaName, existingInstance)\n } else {\n // Create new instance\n const schema = Schema.create(schemaName, {\n waitForReady: false,\n }) as Schema\n currentInstances.set(schemaName, schema)\n }\n }\n\n // Cleanup instances that are no longer needed\n for (const [name, instance] of schemaInstancesRef.current.entries()) {\n if (!schemaNames.has(name)) {\n instance.unload()\n }\n }\n\n // Update ref and state\n schemaInstancesRef.current = currentInstances\n setSchemaInstances(Array.from(currentInstances.values()))\n } catch (error) {\n logger('Error fetching all schema versions from database:', error)\n setSchemaInstances(null)\n }\n }, [isClientReady])\n\n useEffect(() => {\n if (!isClientReady) {\n return\n }\n fetchSchemas()\n }, [isClientReady, fetchSchemas])\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n schemaInstancesRef.current.forEach((instance) => {\n instance.unload()\n })\n schemaInstancesRef.current.clear()\n }\n }, [])\n\n return schemaInstances\n}\n\ntype SchemaSnapshot = SnapshotFrom<typeof schemaMachine>\n\n\n/**\n * Hook to get the internal Seed Protocol schema (SDK-only schema)\n * This schema is managed by the SDK and should not be edited by app developers\n * @returns Object with schema instance and schemaData (version, metadata, etc.)\n */\nexport const useSeedProtocolSchema = () => {\n return useSchema(SEED_PROTOCOL_SCHEMA_NAME)\n}","import { useMemo, useState, useEffect, useCallback, useRef } from 'react'\nimport { Model } from '@seedprotocol/sdk'\nimport { useIsClientReady } from './client'\nimport { useLiveQuery } from './liveQuery'\nimport { BaseDb } from '@seedprotocol/sdk'\nimport { models as modelsTable, modelSchemas, schemas as schemasTable } from '@seedprotocol/sdk'\nimport { eq, or } from 'drizzle-orm'\nimport { Subscription } from 'xstate'\nimport { useQuery } from '@tanstack/react-query'\nimport { useSeedQueryClient } from './queryClient'\nimport { useCreateState } from './useCreateState'\n\ntype UseModelsResult = {\n models: Model[]\n isLoading: boolean\n error: Error | null\n}\n\ntype UseModelsParams = string | null | undefined\n\ntype UseModels = (schemaId: UseModelsParams) => UseModelsResult\n\n/**\n * Hook to get all Model instances for a specific schema\n * Uses useLiveQuery to watch for changes in the models table and automatically\n * updates the returned Model instances when changes occur.\n * @param schemaId - The schema ID (schema file ID) or schema name to get models from\n * @returns Array of Model instances belonging to the schema\n */\nconst getModelsQueryKey = (schemaId: UseModelsParams) => ['seed', 'models', schemaId] as const\n\nexport const useModels: UseModels = (schemaId) => {\n const isClientReady = useIsClientReady()\n const queryClient = useSeedQueryClient()\n // Latest models-table rows for this schema (live query): the ground truth for what exists.\n const tableIdsRef = useRef<Set<string> | null>(null)\n\n const queryKey = useMemo(() => getModelsQueryKey(schemaId), [schemaId])\n\n const {\n data: models = [],\n isLoading,\n error: queryError,\n } = useQuery({\n queryKey,\n queryFn: async () => {\n // Use waitForReady: false so we return models as soon as they exist; waitForReady: true\n // filters to only idle models and can return [] while a model is in creatingProperties.\n const next = await Model.all(schemaId!, { waitForReady: false })\n // Model.all can briefly return [] while models load. Keep the previous list only while the\n // table still has rows; once it is empty (all deleted), [] is the answer.\n const prev = queryClient.getQueryData<Model[]>(queryKey)\n const tableIds = tableIdsRef.current\n if (next.length === 0 && prev && prev.length > 0 && (!tableIds || tableIds.size > 0)) {\n return prev\n }\n return next\n },\n enabled: isClientReady && !!schemaId,\n }, queryClient)\n const displayModels = schemaId ? models : []\n const modelsRef = useRef<Model[]>(displayModels)\n modelsRef.current = displayModels\n\n // When a model is created, writeModelToDb posts to this channel; live query over join often doesn't re-run\n useEffect(() => {\n if (!schemaId || typeof BroadcastChannel === 'undefined') return\n const ch = new BroadcastChannel('seed-models-invalidate')\n const onMessage = (event: MessageEvent<{ schemaName?: string; schemaFileId?: string }>) => {\n const { schemaName, schemaFileId } = event.data || {}\n if (schemaId === schemaName || schemaId === schemaFileId) {\n queryClient.invalidateQueries({ queryKey })\n queryClient.refetchQueries({ queryKey })\n }\n }\n ch.addEventListener('message', onMessage)\n return () => {\n ch.removeEventListener('message', onMessage)\n ch.close()\n }\n }, [schemaId, queryClient, queryKey])\n\n // Stabilize query reference: only recreate when (schemaId, isClientReady) change, not when db reference changes.\n // This keeps the same liveQuery observable/subscription alive so effects can deliver updates when a new model is added.\n const stableModelsQueryKeyRef = useRef<{ schemaId: string; ready: boolean } | null>(null)\n const stableModelsQueryRef = useRef<ReturnType<typeof buildModelsQuery> | null>(null)\n function buildModelsQuery() {\n const currentDb = BaseDb.getAppDb()\n if (!currentDb || !schemaId) return null\n return currentDb\n .select({\n modelFileId: modelsTable.schemaFileId,\n modelName: modelsTable.name,\n })\n .from(schemasTable)\n .innerJoin(modelSchemas, eq(schemasTable.id, modelSchemas.schemaId))\n .innerJoin(modelsTable, eq(modelSchemas.modelId, modelsTable.id))\n .where(\n or(\n eq(schemasTable.schemaFileId, schemaId),\n eq(schemasTable.name, schemaId)\n )\n )\n }\n const modelsQuery = useMemo(() => {\n if (!schemaId || !isClientReady) return null\n const key = { schemaId, ready: isClientReady }\n const prevKey = stableModelsQueryKeyRef.current\n if (\n prevKey &&\n prevKey.schemaId === key.schemaId &&\n prevKey.ready === key.ready &&\n stableModelsQueryRef.current !== null\n ) {\n return stableModelsQueryRef.current\n }\n const q = buildModelsQuery()\n if (!q) return null\n stableModelsQueryKeyRef.current = key\n stableModelsQueryRef.current = q\n return q\n }, [schemaId, isClientReady])\n\n const modelsTableData = useLiveQuery<{ modelFileId: string | null; modelName: string }>(modelsQuery)\n\n useEffect(() => {\n if (!isClientReady || !modelsTableData || !schemaId) return\n\n const currentModelsSet = new Set<string>()\n for (const model of modelsRef.current) {\n const modelFileId = model.id || (model as any).modelFileId\n if (modelFileId) currentModelsSet.add(modelFileId)\n else if (model.modelName) currentModelsSet.add(model.modelName)\n }\n\n const tableDataModelsSet = new Set<string>()\n for (const dbModel of modelsTableData) {\n if (dbModel.modelFileId) tableDataModelsSet.add(dbModel.modelFileId)\n else if (dbModel.modelName) tableDataModelsSet.add(dbModel.modelName)\n }\n tableIdsRef.current = tableDataModelsSet\n\n const setsAreEqual =\n currentModelsSet.size === tableDataModelsSet.size &&\n [...currentModelsSet].every((id) => tableDataModelsSet.has(id))\n\n // Refetch on any difference: new rows, and deleted ones (which used to leave the list stale).\n if (!setsAreEqual) {\n queryClient.invalidateQueries({ queryKey })\n }\n }, [isClientReady, modelsTableData, schemaId, queryClient, queryKey])\n\n return {\n models: displayModels,\n isLoading,\n error: queryError as Error | null,\n }\n}\n\ntype UseModelResult = {\n model: Model | undefined\n isLoading: boolean\n error: Error | null\n}\n\n/**\n * Hook to get a specific Model instance\n * Can be called in two ways:\n * 1. With schemaId and modelName: useModel(schemaId, modelName)\n * 2. With modelId: useModel(modelId)\n * \n * @param schemaIdOrModelId - The schema ID (schema file ID) OR the model ID (modelFileId)\n * @param modelName - The name of the model to retrieve (required if first param is schemaId)\n * @returns Object with model, isLoading, and error\n */\nexport const useModel = (\n schemaIdOrModelId: string | null | undefined,\n modelName?: string | null | undefined\n): UseModelResult => {\n const isClientReady = useIsClientReady()\n const [model, setModel] = useState<Model | undefined>(undefined)\n const [isLoading, setIsLoading] = useState(false)\n const [error, setError] = useState<Error | null>(null)\n const subscriptionRef = useRef<Subscription | undefined>(undefined)\n const [, setVersion] = useState(0) // Version counter to force re-renders\n\n // If modelName is provided, treat first param as schemaId\n // Otherwise, treat first param as modelId\n const isModelIdLookup = modelName === undefined || modelName === null\n\n\n // Lookup model by ID if needed\n useEffect(() => {\n if (!isClientReady || !isModelIdLookup || !schemaIdOrModelId) {\n setModel(undefined)\n setIsLoading(false)\n setError(null)\n return\n }\n\n const lookupModelById = async () => {\n try {\n setIsLoading(true)\n setError(null)\n // Use Model.createById which handles cache + DB lookup\n const foundModel = await Model.createById(schemaIdOrModelId, { hold: false })\n setModel(foundModel || undefined)\n setIsLoading(false)\n setError(null)\n } catch (error) {\n console.error('[useModel] Error looking up model by ID:', error)\n setModel(undefined)\n setIsLoading(false)\n setError(error as Error)\n }\n }\n\n lookupModelById()\n }, [isClientReady, isModelIdLookup, schemaIdOrModelId])\n\n // Always call useModels (with null for an id lookup): hooks must run in the same order on\n // every render, and returning early before it crashed when modelName switched null <-> string.\n const { models: modelsList, isLoading: modelsLoading, error: modelsError } = useModels(\n isModelIdLookup ? null : schemaIdOrModelId,\n )\n const foundModel = useMemo(() => {\n if (isModelIdLookup || !modelName) {\n return undefined\n }\n // Try both modelName property and name getter for compatibility\n return modelsList.find((m: Model) => {\n const mName = m.modelName ?? (m as any).name\n return mName === modelName\n })\n }, [isModelIdLookup, modelsList, modelName])\n\n const activeModel = isModelIdLookup ? model : foundModel\n\n // Re-render on the model's service changes\n useEffect(() => {\n subscriptionRef.current?.unsubscribe()\n subscriptionRef.current = undefined\n if (!activeModel) return\n\n subscriptionRef.current = activeModel.getService().subscribe(() => {\n setVersion((prev) => prev + 1)\n })\n\n return () => {\n subscriptionRef.current?.unsubscribe()\n subscriptionRef.current = undefined\n }\n }, [activeModel])\n\n if (isModelIdLookup) {\n return {\n model,\n isLoading,\n error,\n }\n }\n return {\n model: foundModel,\n isLoading: modelsLoading,\n error: modelsError,\n }\n}\n\nexport type UseCreateModelOptions = {\n modelFileId?: string\n properties?: { [propertyName: string]: any }\n}\n\nexport type UseCreateModelReturn = {\n create: (\n schemaName: string,\n modelName: string,\n options?: UseCreateModelOptions\n ) => Model\n isLoading: boolean\n error: Error | null\n resetError: () => void\n}\n\nexport const useCreateModel = (): UseCreateModelReturn => {\n const { run, isLoading, error, resetError } = useCreateState('Failed to create model')\n\n const create = useCallback(\n (schemaName: string, modelName: string, options?: UseCreateModelOptions): Model =>\n run(\n () =>\n Model.create(modelName, schemaName, {\n ...options,\n waitForReady: false,\n }) as Model,\n ),\n [run],\n )\n\n return {\n create,\n isLoading,\n error,\n resetError,\n }\n}\n\nexport type UseDestroyModelReturn = {\n destroy: (model: Model) => Promise<void>\n isLoading: boolean\n error: Error | null\n resetError: () => void\n}\n\nexport const useDestroyModel = (): UseDestroyModelReturn => {\n const [isLoading, setIsLoading] = useState(false)\n const [error, setError] = useState<Error | null>(null)\n\n // Loading state is tracked here: the model's service doesn't record destroy progress, and\n // destroy() stops the service, often within a few microtasks.\n const destroy = useCallback(async (model: Model) => {\n if (!model) return\n setError(null)\n setIsLoading(true)\n try {\n await model.destroy()\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)))\n throw err\n } finally {\n setIsLoading(false)\n }\n }, [])\n\n const resetError = useCallback(() => setError(null), [])\n\n return {\n destroy,\n isLoading,\n error,\n resetError,\n }\n}\n","import { useModel } from \"./model\"\nimport { useCallback, useEffect, useRef, useState, useMemo } from \"react\"\nimport { flushSync } from \"react-dom\"\nimport { debounce } from \"lodash-es\"\nimport { getPropertySchema, ModelProperty } from '@seedprotocol/sdk'\nimport { useIsClientReady } from \"./client\"\nimport { Subscription } from \"xstate\"\nimport { useLiveQuery } from \"./liveQuery\"\nimport { BaseDb } from '@seedprotocol/sdk'\nimport { properties as propertiesTable, schemas as schemasTable } from '@seedprotocol/sdk'\nimport { eq, or } from \"drizzle-orm\"\nimport { Model } from '@seedprotocol/sdk'\nimport { useQuery } from \"@tanstack/react-query\"\nimport { useSeedQueryClient } from \"./queryClient\"\nimport { useCreateState } from \"./useCreateState\"\nimport { getSchemaNameFromId } from '@seedprotocol/sdk'\n\n\ntype UseModelPropertiesResult = {\n modelProperties: ModelProperty[]\n isLoading: boolean\n error: Error | null\n}\n\n/**\n * Hook to get all ModelProperty instances for a specific model\n * Can be called in two ways:\n * 1. With schemaId and modelName: useModelProperties(schemaId, modelName)\n * 2. With modelId: useModelProperties(modelId)\n * \n * Uses useLiveQuery to watch for changes in the properties table and automatically\n * updates the returned ModelProperty instances when changes occur.\n * \n * @param schemaIdOrModelId - The schema ID (schema file ID) OR the model ID (modelFileId)\n * @param modelName - The name of the model to get properties from (required if first param is schemaId)\n * @returns Object with modelProperties array, isLoading, and error\n */\n/**\n * A schema name for a schemaId that may be a schema name, a schema file id, or \"name-version\".\n * An exact stored name or file id wins, so hyphenated names (\"my-app\") aren't cut.\n */\nasync function resolveSchemaName(schemaId: string): Promise<string> {\n const db = BaseDb.getAppDb()\n if (db) {\n const rows = await db\n .select({ name: schemasTable.name })\n .from(schemasTable)\n .where(or(eq(schemasTable.name, schemaId), eq(schemasTable.schemaFileId, schemaId)))\n .limit(1)\n if (rows[0]?.name) return rows[0].name\n }\n return getSchemaNameFromId(schemaId) ?? schemaId\n}\n\nexport const useModelProperties = (\n schemaIdOrModelId: string | null | undefined,\n modelName?: string | null | undefined\n): UseModelPropertiesResult => {\n // Use useModel to handle both lookup patterns (by ID or by schemaId + modelName)\n const { model } = useModel(schemaIdOrModelId, modelName)\n \n const isClientReady = useIsClientReady()\n const queryClient = useSeedQueryClient()\n\n // The model's database id. It can arrive after the model is first seen (the row is written later),\n // so follow the model's snapshot: memoizing it once left the properties live query unbuilt.\n const dbModelId = useModelDbId(model)\n\n const modelId = model?.id\n const modelPropertiesQueryKey = useMemo(\n () => ['seed', 'modelProperties', modelId ?? ''] as const,\n [modelId],\n )\n\n const {\n data: modelProperties = [],\n isLoading,\n error: queryError,\n } = useQuery({\n queryKey: modelPropertiesQueryKey,\n queryFn: () => ModelProperty.all(modelId!, { waitForReady: true }),\n enabled: isClientReady && !!modelId,\n }, queryClient)\n\n const db = isClientReady ? BaseDb.getAppDb() : null\n const propertiesQuery = useMemo(() => {\n if (!db || !dbModelId) return null\n return db\n .select({\n id: propertiesTable.id,\n name: propertiesTable.name,\n dataType: propertiesTable.dataType,\n schemaFileId: propertiesTable.schemaFileId,\n })\n .from(propertiesTable)\n .where(eq(propertiesTable.modelId, dbModelId))\n }, [db, isClientReady, dbModelId])\n const propertiesTableData = useLiveQuery<{ id: number; name: string; dataType: string; schemaFileId: string | null }>(propertiesQuery)\n\n const modelPropertiesRef = useRef<ModelProperty[]>([])\n modelPropertiesRef.current = modelProperties\n\n // Fallback: when we have modelId but query returned [] (e.g. properties not in DB yet or\n // propertiesTableData is undefined because model._dbId isn't set yet), schedule refetches\n // so we pick up properties after they're written.\n useEffect(() => {\n if (!modelId || modelProperties.length > 0 || !queryClient || !modelPropertiesQueryKey) return\n const delays = [400, 1200, 2500]\n const timers = delays.map((ms) =>\n setTimeout(() => {\n queryClient.invalidateQueries({ queryKey: modelPropertiesQueryKey })\n }, ms)\n )\n return () => timers.forEach((t) => clearTimeout(t))\n }, [modelId, modelProperties.length, queryClient, modelPropertiesQueryKey])\n\n useEffect(() => {\n if (!isClientReady || !model?.id || !propertiesTableData || !modelPropertiesQueryKey) return\n\n const currentPropertiesSet = new Set<string>()\n for (const prop of modelPropertiesRef.current) {\n const context = (prop as any)._getSnapshotContext()\n const propertyFileId = context?.id\n if (propertyFileId) currentPropertiesSet.add(propertyFileId)\n else if (prop.name) currentPropertiesSet.add(prop.name)\n }\n\n const tableDataPropertiesSet = new Set<string>()\n for (const dbProperty of propertiesTableData) {\n if (dbProperty.schemaFileId) tableDataPropertiesSet.add(dbProperty.schemaFileId)\n else if (dbProperty.name) tableDataPropertiesSet.add(dbProperty.name)\n }\n\n const setsAreEqual =\n currentPropertiesSet.size === tableDataPropertiesSet.size &&\n (currentPropertiesSet.size === 0 ||\n [...currentPropertiesSet].every((id) => tableDataPropertiesSet.has(id)))\n\n // Invalidate when cached list is out of sync with the table: either we have stale cached data\n // (currentPropertiesSet.size > 0) or the table has new rows we don't have yet (tableDataPropertiesSet.size > 0).\n // The latter handles the case where the initial query returned [] before properties were written.\n const shouldInvalidate =\n !setsAreEqual && (currentPropertiesSet.size > 0 || tableDataPropertiesSet.size > 0)\n\n if (shouldInvalidate) {\n queryClient.invalidateQueries({ queryKey: modelPropertiesQueryKey })\n }\n }, [isClientReady, propertiesTableData, model?.id, queryClient, modelPropertiesQueryKey])\n\n // When we have cached modelProperties, do not report isLoading: true even if the query\n // is refetching (e.g. after useModels refetch causes modelId to change briefly). UX expects\n // isLoading false once we have shown data.\n const effectiveIsLoading = isLoading && modelProperties.length === 0\n\n return {\n modelProperties,\n isLoading: effectiveIsLoading,\n error: queryError as Error | null,\n }\n}\n\nconst LOOKUP_RETRY_DELAYS_MS = [400, 1200, 2500]\n\nconst readModelDbId = (model: Model | undefined | null): number | undefined => {\n if (!model) return undefined\n try {\n return (model as any)._getSnapshotContext()._dbId as number | undefined\n } catch {\n return undefined\n }\n}\n\n/** The model's `_dbId`, updated when the model's actor sets it. */\nconst useModelDbId = (model: Model | undefined | null): number | undefined => {\n const [dbModelId, setDbModelId] = useState(() => readModelDbId(model))\n useEffect(() => {\n setDbModelId(readModelDbId(model))\n if (!model) return\n const subscription = model.getService().subscribe(() => setDbModelId(readModelDbId(model)))\n return () => subscription.unsubscribe()\n }, [model])\n return dbModelId\n}\n\n/**\n * Helper function to get property schema by modelFileId and propertyName\n */\nconst getPropertySchemaByModelFileId = async (\n modelFileId: string,\n propertyName: string\n): Promise<ReturnType<typeof getPropertySchema>> => {\n // Get model by modelFileId\n const model = await Model.createById(modelFileId, { hold: false })\n if (!model) {\n return undefined\n }\n\n // Get modelName from model\n const modelName = model.modelName ?? (model as any).name\n if (!modelName) {\n return undefined\n }\n\n // Use existing getPropertySchema function, pinned to this model (names are only unique per schema)\n return getPropertySchema(modelName, propertyName, { modelFileId, schemaName: model.schemaName })\n}\n\n/**\n * Hook to get a specific ModelProperty instance\n * Can be called in three ways:\n * 1. With propertyFileId: useModelProperty(propertyFileId)\n * 2. With modelFileId and propertyName: useModelProperty(modelFileId, propertyName)\n * 3. With schemaId, modelName, and propertyName: useModelProperty(schemaId, modelName, propertyName)\n * \n * @overload\n * @param propertyFileId - The property file ID (schemaFileId)\n * @returns Object with modelProperty, isLoading, and error\n * \n * @overload\n * @param modelFileId - The model file ID (modelFileId)\n * @param propertyName - The name of the property\n * @returns Object with modelProperty, isLoading, and error\n * \n * @overload\n * @param schemaId - The schema ID (schema file ID)\n * @param modelName - The name of the model\n * @param propertyName - The name of the property\n * @returns Object with modelProperty, isLoading, and error\n */\nexport function useModelProperty(propertyFileId: string | null | undefined): {\n modelProperty: ModelProperty | undefined\n isLoading: boolean\n error: Error | null\n}\nexport function useModelProperty(\n modelFileId: string | null | undefined,\n propertyName: string | null | undefined\n): {\n modelProperty: ModelProperty | undefined\n isLoading: boolean\n error: Error | null\n}\nexport function useModelProperty(\n schemaId: string | null | undefined,\n modelName: string | null | undefined,\n propertyName: string | null | undefined\n): {\n modelProperty: ModelProperty | undefined\n isLoading: boolean\n error: Error | null\n}\nexport function useModelProperty(...args: Array<string | null | undefined>) {\n const [arg1, arg2, arg3] = args\n // The lookup form follows the number of arguments, so a null or undefined argument (not ready\n // yet) waits instead of switching to a shorter form: (schemaId, null, null) used to look the\n // schema id up as a property file id.\n const argCount = Math.min(args.length, 3)\n\n // Determine initial loading state - start loading if we have valid parameters\n const initialLoadingState = useMemo(() => {\n if (argCount === 3) return !!(arg1 && arg2 && arg3)\n if (argCount === 2) return !!(arg1 && arg2)\n return !!arg1\n }, [argCount, arg1, arg2, arg3])\n\n const [modelProperty, setModelProperty] = useState<ModelProperty | undefined>(undefined)\n const [isLoading, setIsLoading] = useState(initialLoadingState)\n const [error, setError] = useState<Error | null>(null)\n const subscriptionRef = useRef<Subscription | undefined>(undefined)\n\n const isClientReady = useIsClientReady()\n\n // Determine which lookup mode we're in based on arguments\n const lookupMode = useMemo(() => {\n if (argCount === 3) {\n return { type: 'schemaId' as const, schemaId: arg1, modelName: arg2, propertyName: arg3 }\n }\n if (argCount === 2) {\n return { type: 'modelFileId' as const, modelFileId: arg1, propertyName: arg2 }\n }\n return { type: 'propertyFileId' as const, propertyFileId: arg1 }\n }, [argCount, arg1, arg2, arg3])\n\n // Determine if we should be loading based on parameters\n const shouldLoad = useMemo(() => {\n if (!isClientReady) return false\n if (lookupMode.type === 'propertyFileId') {\n return !!lookupMode.propertyFileId\n } else if (lookupMode.type === 'modelFileId') {\n return !!(lookupMode.modelFileId && lookupMode.propertyName)\n } else {\n return !!(lookupMode.schemaId && lookupMode.modelName && lookupMode.propertyName)\n }\n }, [isClientReady, lookupMode])\n\n /** `quiet`: a background retry; leaves isLoading alone. */\n const updateModelProperty = useCallback(async ({ quiet = false }: { quiet?: boolean } = {}) => {\n if (!isClientReady) {\n setModelProperty(undefined)\n setIsLoading(false)\n setError(null)\n return\n }\n\n let propertyData: Awaited<ReturnType<typeof getPropertySchema>> | undefined\n let resolvedModelName: string | undefined\n\n try {\n if (!quiet) {\n setIsLoading(true)\n setError(null)\n }\n\n if (lookupMode.type === 'propertyFileId') {\n if (!lookupMode.propertyFileId) {\n setModelProperty(undefined)\n setIsLoading(false)\n setError(null)\n return\n }\n\n // Use ModelProperty.createById for propertyFileId lookup\n const foundProperty = await ModelProperty.createById(lookupMode.propertyFileId)\n if (foundProperty) {\n setModelProperty(foundProperty)\n setIsLoading(false)\n setError(null)\n } else {\n setModelProperty(undefined)\n setIsLoading(false)\n setError(null)\n }\n return\n } else if (lookupMode.type === 'modelFileId') {\n if (!lookupMode.modelFileId || !lookupMode.propertyName) {\n setModelProperty(undefined)\n setIsLoading(false)\n setError(null)\n return\n }\n\n // Get property schema by modelFileId and propertyName\n // This function already gets the model and resolves modelName\n propertyData = await getPropertySchemaByModelFileId(lookupMode.modelFileId, lookupMode.propertyName)\n \n // Get modelName from model (needed for ModelProperty.create)\n const model = await Model.createById(lookupMode.modelFileId, { hold: false })\n resolvedModelName = model?.modelName ?? (model as any)?.name\n } else {\n // lookupMode.type === 'schemaId'\n if (!lookupMode.schemaId || !lookupMode.modelName || !lookupMode.propertyName) {\n setModelProperty(undefined)\n setIsLoading(false)\n setError(null)\n return\n }\n\n // Use existing getPropertySchema for schemaId + modelName + propertyName\n // schemaId may be a schema file id or a schema name; scope the lookup to that schema\n const schemaName = await resolveSchemaName(lookupMode.schemaId)\n propertyData = await getPropertySchema(lookupMode.modelName, lookupMode.propertyName, { schemaName })\n resolvedModelName = lookupMode.modelName\n }\n\n if (propertyData && resolvedModelName) {\n const createdProperty = ModelProperty.create(\n { ...propertyData, modelName: resolvedModelName },\n { waitForReady: false }\n )\n const resolvedProperty = createdProperty instanceof Promise ? await createdProperty : createdProperty\n flushSync(() => {\n setModelProperty(resolvedProperty)\n setIsLoading(false)\n setError(null)\n })\n } else {\n setModelProperty(undefined)\n setIsLoading(false)\n setError(null)\n }\n } catch (error) {\n console.error('[useModelProperty] Error updating model property:', error)\n setModelProperty(undefined)\n setIsLoading(false)\n setError(error as Error)\n }\n }, [isClientReady, lookupMode.type, lookupMode.propertyFileId, lookupMode.modelFileId, lookupMode.propertyName, lookupMode.schemaId, lookupMode.modelName])\n\n // Fetch/refetch when lookup parameters change or client becomes ready\n useEffect(() => {\n if (!shouldLoad) {\n setModelProperty(undefined)\n setIsLoading(false)\n setError(null)\n return\n }\n updateModelProperty()\n }, [shouldLoad, updateModelProperty])\n\n // A schemaId/modelFileId lookup reads the model's properties, which can still be loading (or the\n // property not yet written) when the hook mounts. Nothing would ask again, so retry a few times\n // while it finds nothing, as useModelProperties does (skipped while a lookup is running, and once\n // one found the property or failed).\n const skipLookupRetryRef = useRef(false)\n skipLookupRetryRef.current = isLoading || !!modelProperty || !!error\n useEffect(() => {\n if (!shouldLoad || lookupMode.type === 'propertyFileId') return\n const timers = LOOKUP_RETRY_DELAYS_MS.map((ms) =>\n setTimeout(() => {\n if (!skipLookupRetryRef.current) updateModelProperty({ quiet: true })\n }, ms),\n )\n return () => timers.forEach((t) => clearTimeout(t))\n }, [shouldLoad, lookupMode.type, updateModelProperty])\n\n // Subscribe to service changes when modelProperty is available.\n // Skip subscription for schemaId/modelFileId lookups where we created the instance locally—\n // refetching on every snapshot would set loading and can race with the initial render.\n // Debounce to reduce refetch frequency and avoid updateContext/refetch loops.\n const shouldSubscribe = lookupMode.type === 'propertyFileId'\n useEffect(() => {\n if (!modelProperty || !shouldSubscribe) {\n return\n }\n\n // Clean up previous subscription\n subscriptionRef.current?.unsubscribe()\n\n const debouncedUpdate = debounce(updateModelProperty, 100)\n\n // Subscribe to service changes\n const subscription = modelProperty.getService().subscribe(() => {\n debouncedUpdate()\n })\n\n subscriptionRef.current = subscription\n\n return () => {\n debouncedUpdate.cancel()\n subscriptionRef.current?.unsubscribe()\n subscriptionRef.current = undefined\n }\n }, [modelProperty, updateModelProperty, shouldSubscribe])\n\n return {\n modelProperty,\n isLoading,\n error,\n }\n}\n\nexport type UseCreateModelPropertyOptions = {\n name: string\n dataType: string\n [key: string]: any\n}\n\nexport type UseCreateModelPropertyReturn = {\n create: (\n schemaId: string,\n modelName: string,\n property: UseCreateModelPropertyOptions\n ) => Promise<ModelProperty>\n isLoading: boolean\n error: Error | null\n resetError: () => void\n}\n\n/**\n * Hook to create a ModelProperty with loading and error state.\n * create(schemaId, modelName, property) creates a new property on the model. schemaId is a schema\n * name, schema file id or \"name-version\"; it is resolved to a stored schema name first, so create\n * returns a promise.\n */\nexport const useCreateModelProperty = (): UseCreateModelPropertyReturn => {\n const { runAsync, isLoading, error, resetError } = useCreateState(\n 'Failed to create model property',\n )\n\n const create = useCallback(\n (\n schemaId: string,\n modelName: string,\n property: UseCreateModelPropertyOptions\n ): Promise<ModelProperty> =>\n runAsync(async () => {\n if (!modelName || !property.name || !property.dataType) {\n throw new Error('modelName, property name and dataType are required')\n }\n const schemaName = await resolveSchemaName(schemaId)\n return ModelProperty.create(\n { ...property, modelName } as Parameters<typeof ModelProperty.create>[0],\n { waitForReady: false, schemaName }\n ) as ModelProperty\n }),\n [runAsync]\n )\n\n return {\n create,\n isLoading,\n error,\n resetError,\n }\n}\n\nexport type UseDestroyModelPropertyReturn = {\n destroy: (modelProperty: ModelProperty) => Promise<void>\n isLoading: boolean\n error: Error | null\n resetError: () => void\n}\n\nexport const useDestroyModelProperty = (): UseDestroyModelPropertyReturn => {\n const [isLoading, setIsLoading] = useState(false)\n const [error, setError] = useState<Error | null>(null)\n\n // Loading state is tracked here: the model property's service doesn't record destroy progress, and\n // destroy() stops the service, often within a few microtasks.\n const destroy = useCallback(async (modelProperty: ModelProperty) => {\n if (!modelProperty) return\n setError(null)\n setIsLoading(true)\n try {\n await modelProperty.destroy()\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)))\n throw err\n } finally {\n setIsLoading(false)\n }\n }, [])\n\n const resetError = useCallback(() => setError(null), [])\n\n return {\n destroy,\n isLoading,\n error,\n resetError,\n }\n}\n","import { useCallback, useState } from 'react'\nimport { Item } from '@seedprotocol/sdk'\n\nexport type UseDeleteItemReturn = {\n deleteItem: (item: Item<any>) => Promise<void>\n isLoading: boolean\n error: Error | null\n resetError: () => void\n}\n\nexport const useDeleteItem = (): UseDeleteItemReturn => {\n const [isLoading, setIsLoading] = useState(false)\n const [error, setError] = useState<Error | null>(null)\n\n // Loading state is tracked here: the item's service doesn't record destroy progress, and\n // destroy() stops the service, often within a few microtasks.\n const destroy = useCallback(async (item: Item<any>) => {\n if (!item) return\n setError(null)\n setIsLoading(true)\n try {\n await item.destroy()\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)))\n throw err\n } finally {\n setIsLoading(false)\n }\n }, [])\n\n const resetError = useCallback(() => setError(null), [])\n\n return {\n deleteItem: destroy,\n isLoading,\n error,\n resetError,\n }\n}\n","import { useEffect } from 'react'\nimport { useQuery } from '@tanstack/react-query'\nimport { useSeedQueryClient } from './queryClient'\nimport { BaseFileManager } from '@seedprotocol/sdk'\nimport { useIsClientReady } from './client'\nimport { eventEmitter, FILES_CHANGED_EVENT } from '@seedprotocol/sdk'\n\nconst IMAGE_FILES_QUERY_KEY = ['seed', 'imageFiles'] as const\n\n/**\n * Returns an up-to-date list of image filenames stored in the file system (OPFS in browser).\n * Automatically refetches when images are saved (file-saved) or after bulk downloads (fs.downloadAll.success).\n *\n * Use within SeedProvider, after client.init().\n *\n * @example\n * ```tsx\n * const { imageFiles, isLoading, error, refetch } = useImageFiles()\n * // imageFiles: ['photo.jpg', 'cover.png']\n * ```\n */\nexport function useImageFiles() {\n const isClientReady = useIsClientReady()\n const queryClient = useSeedQueryClient()\n\n const {\n data: imageFiles = [],\n isLoading,\n error,\n refetch,\n } = useQuery({\n queryKey: IMAGE_FILES_QUERY_KEY,\n queryFn: () => BaseFileManager.listImageFiles(),\n enabled: isClientReady,\n }, queryClient)\n\n useEffect(() => {\n const fileSavedHandler = (filePath: string) => {\n if (filePath.includes('/images/')) {\n queryClient.invalidateQueries({ queryKey: IMAGE_FILES_QUERY_KEY })\n }\n }\n const downloadSuccessHandler = () => {\n queryClient.invalidateQueries({ queryKey: IMAGE_FILES_QUERY_KEY })\n }\n // Files written by workers or other tabs.\n const filesChangedHandler = (filePaths: string[]) => {\n if (filePaths.some((filePath) => filePath.includes('/images/'))) {\n queryClient.invalidateQueries({ queryKey: IMAGE_FILES_QUERY_KEY })\n }\n }\n eventEmitter.on('file-saved', fileSavedHandler)\n eventEmitter.on('fs.downloadAll.success', downloadSuccessHandler)\n eventEmitter.on(FILES_CHANGED_EVENT, filesChangedHandler)\n return () => {\n eventEmitter.off('file-saved', fileSavedHandler)\n eventEmitter.off('fs.downloadAll.success', downloadSuccessHandler)\n eventEmitter.off(FILES_CHANGED_EVENT, filesChangedHandler)\n }\n }, [queryClient])\n\n return {\n imageFiles,\n isLoading,\n error: error instanceof Error ? error : null,\n refetch,\n }\n}\n","import { useEffect, useMemo } from 'react'\nimport { useQuery } from '@tanstack/react-query'\nimport { useSeedQueryClient } from './queryClient'\nimport { BaseFileManager, eventEmitter, FILES_CHANGED_EVENT } from '@seedprotocol/sdk'\nimport { useIsClientReady } from './client'\n\nexport const FILES_QUERY_KEY_PREFIX = ['seed', 'files'] as const\n\n/**\n * Returns an up-to-date list of filenames stored in the given directory.\n * Automatically refetches when files are saved (file-saved) or after bulk downloads (fs.downloadAll.success).\n *\n * Use within SeedProvider, after client.init().\n *\n * @param dir - Directory name under the files root (e.g. 'files', 'images'). Default: 'files'.\n *\n * @example\n * ```tsx\n * const { files, isLoading, error, refetch } = useFiles('files')\n * // files: ['document.pdf', 'contract.docx']\n * ```\n */\nexport function useFiles(dir: string = 'files') {\n const isClientReady = useIsClientReady()\n const queryClient = useSeedQueryClient()\n\n const queryKey = useMemo(() => [...FILES_QUERY_KEY_PREFIX, dir] as const, [dir])\n\n const {\n data: files = [],\n isLoading,\n error,\n refetch,\n } = useQuery({\n queryKey,\n queryFn: () => BaseFileManager.listFiles(dir),\n enabled: isClientReady,\n }, queryClient)\n\n useEffect(() => {\n const fileSavedHandler = (filePath: string) => {\n if (filePath.includes(`/${dir}/`)) {\n queryClient.invalidateQueries({ queryKey })\n }\n }\n const downloadSuccessHandler = () => {\n queryClient.invalidateQueries({ queryKey })\n }\n // Files written by workers or other tabs.\n const filesChangedHandler = (filePaths: string[]) => {\n if (filePaths.some((filePath) => filePath.includes(`/${dir}/`))) {\n queryClient.invalidateQueries({ queryKey })\n }\n }\n eventEmitter.on('file-saved', fileSavedHandler)\n eventEmitter.on('fs.downloadAll.success', downloadSuccessHandler)\n eventEmitter.on(FILES_CHANGED_EVENT, filesChangedHandler)\n return () => {\n eventEmitter.off('file-saved', fileSavedHandler)\n eventEmitter.off('fs.downloadAll.success', downloadSuccessHandler)\n eventEmitter.off(FILES_CHANGED_EVENT, filesChangedHandler)\n }\n }, [queryClient, dir, queryKey])\n\n return {\n files,\n isLoading,\n error: error instanceof Error ? error : null,\n refetch,\n }\n}\n","import { useMemo } from 'react'\nimport { BaseDb, easSyncProcesses, type EasSyncProcessType } from '@seedprotocol/sdk'\nimport { desc, eq } from 'drizzle-orm'\nimport { useLiveQuery } from './liveQuery'\nimport { useIsClientReady } from './client'\n\nexport type EasSyncProcessRecord = EasSyncProcessType\n\n/** The app DB once the client is ready; before that BaseDb.getAppDb() throws \"Db not configured\". */\nfunction useAppDbWhenReady() {\n const isClientReady = useIsClientReady()\n return isClientReady ? BaseDb.getAppDb() : null\n}\n\nfunction useEasSyncProcessesQuery() {\n const db = useAppDbWhenReady()\n return useMemo(\n () =>\n db\n ? db.select().from(easSyncProcesses).orderBy(desc(easSyncProcesses.startedAt))\n : null,\n [db],\n )\n}\n\n/**\n * Single live subscription for `eas_sync_processes` ordered by `startedAt` descending plus a\n * derived count of rows whose status is not `in_progress`.\n */\nexport function useEasSyncProcessesState(): {\n records: EasSyncProcessRecord[] | undefined\n nonActiveCount: number | undefined\n} {\n const query = useEasSyncProcessesQuery()\n const records = useLiveQuery(query) as EasSyncProcessRecord[] | undefined\n const nonActiveCount = useMemo(\n () =>\n records === undefined ? undefined : records.filter((r) => r.status !== 'in_progress').length,\n [records],\n )\n return { records, nonActiveCount }\n}\n\nexport function useEasSyncProcesses(): EasSyncProcessRecord[] | undefined {\n const query = useEasSyncProcessesQuery()\n return useLiveQuery(query) as EasSyncProcessRecord[] | undefined\n}\n\nexport function useEasSyncProcessesNonActiveCount(): number | undefined {\n return useEasSyncProcessesState().nonActiveCount\n}\n\nexport function useEasSyncProcessById(\n id: number | undefined,\n): { record: EasSyncProcessRecord | null; isLoading: boolean } {\n const db = useAppDbWhenReady()\n const query = useMemo(\n () =>\n id != null && db\n ? db.select().from(easSyncProcesses).where(eq(easSyncProcesses.id, id)).limit(1)\n : null,\n [db, id],\n )\n const records = useLiveQuery(query)\n // Without an id there is nothing to load: not loading forever. With one, waiting for the client\n // counts as loading.\n const isLoading = id != null && (db == null || records === undefined)\n const record = records && records.length > 0 ? (records[0] as EasSyncProcessRecord) : null\n return { record, isLoading }\n}\n","import React, { type ReactNode, useMemo, useEffect, useRef } from 'react'\nimport { QueryClientProvider } from '@tanstack/react-query'\nimport type { QueryClient } from '@tanstack/react-query'\nimport { createSeedQueryClient } from './queryClient'\nimport { client, eventEmitter, EAS_SEED_DATA_SYNCED_TO_DB_EVENT } from '@seedprotocol/sdk'\nimport type { SeedConfig, SeedConstructorOptions } from '@seedprotocol/sdk'\nimport { SeedAddressRevisionProvider } from './SeedSessionContext'\n\nexport type SeedProviderProps = {\n children: ReactNode\n /**\n * Starts the Seed client with this config (`client.init`) when the provider mounts. Children\n * render straight away; the hooks report loading until the client is ready. Leave it out when you\n * call `client.init` yourself or start the client with `SeedClientGate`. Init runs again when\n * the config object changes, so define it outside the component.\n */\n config?: SeedConfig\n /** Addresses passed to `client.init` with `config`. See `SeedConstructorOptions.addresses`. */\n addresses?: SeedConstructorOptions['addresses']\n /**\n * Called when `client.init` fails, e.g. with `DatabaseNotPersistentError` (`DB_NOT_PERSISTENT`)\n * or `FileSystemLockedError` (`OPFS_LOCKED`). Without it the error is logged.\n */\n onInitError?: (err: unknown) => void\n /** Optional: use your own QueryClient. If not provided, a default client with Seed options is created. */\n queryClient?: QueryClient\n /** Optional: ref to receive the QueryClient instance (e.g. for tests). */\n queryClientRef?: React.MutableRefObject<QueryClient | null>\n}\n\ndeclare global {\n interface Window {\n __SEED_INVALIDATE_ITEM_PROPERTIES__?: ((canonicalId: string) => void) | null\n }\n}\n\n/** Module-level ref so invalidateItemPropertiesForItem works when test and app share the same bundle but not the same window (e.g. iframe). */\nlet invalidateItemPropertiesRef: ((canonicalId: string) => void | Promise<void>) | null = null\n\n/**\n * Invalidates and refetches the item-properties query for an item.\n * Call this after updating an ItemProperty (e.g. after save()) so useItemProperties\n * refetches and the UI updates. Returns a Promise that resolves when the refetch has completed (if available).\n */\nexport function invalidateItemPropertiesForItem(canonicalId: string): Promise<void> {\n const p1 = invalidateItemPropertiesRef?.(canonicalId)\n if (typeof window !== 'undefined' && window.__SEED_INVALIDATE_ITEM_PROPERTIES__) {\n window.__SEED_INVALIDATE_ITEM_PROPERTIES__(canonicalId)\n }\n return Promise.resolve(p1).then(() => {})\n}\n\nfunction SeedProviderEventSubscriber({ queryClient }: { queryClient: QueryClient }) {\n useEffect(() => {\n const invalidate = (canonicalId: string) => {\n const key: readonly [string, string, string] = ['seed', 'itemProperties', canonicalId]\n queryClient.invalidateQueries({ queryKey: key })\n return queryClient.refetchQueries({ queryKey: key })\n }\n invalidateItemPropertiesRef = invalidate\n if (typeof window !== 'undefined') {\n window.__SEED_INVALIDATE_ITEM_PROPERTIES__ = invalidate\n }\n const handler = (payload: { seedLocalId?: string; seedUid?: string }) => {\n const canonicalId = payload?.seedLocalId ?? payload?.seedUid\n if (canonicalId) {\n invalidate(canonicalId)\n }\n }\n const onEasSyncedToDb = () => {\n void queryClient.invalidateQueries({ queryKey: ['seed', 'items'], exact: false })\n }\n eventEmitter.on('itemProperty.saved', handler)\n eventEmitter.on(EAS_SEED_DATA_SYNCED_TO_DB_EVENT, onEasSyncedToDb)\n return () => {\n eventEmitter.off('itemProperty.saved', handler)\n eventEmitter.off(EAS_SEED_DATA_SYNCED_TO_DB_EVENT, onEasSyncedToDb)\n invalidateItemPropertiesRef = null\n if (typeof window !== 'undefined') {\n window.__SEED_INVALIDATE_ITEM_PROPERTIES__ = null\n }\n }\n }, [queryClient])\n return null\n}\n\nfunction SeedClientInit({ config, addresses, onInitError }: Pick<SeedProviderProps, 'addresses' | 'onInitError'> & { config: SeedConfig }) {\n // Refs, so inline callbacks and address arrays don't re-run init on every render.\n const onInitErrorRef = useRef(onInitError)\n onInitErrorRef.current = onInitError\n const addressesRef = useRef(addresses)\n addressesRef.current = addresses\n const addressesKey = JSON.stringify(addresses ?? null)\n\n useEffect(() => {\n const options: SeedConstructorOptions =\n addressesRef.current === undefined ? { config } : { config, addresses: addressesRef.current }\n client.init(options).catch((err: unknown) => {\n if (onInitErrorRef.current) {\n onInitErrorRef.current(err)\n } else {\n console.error('[SeedProvider] client init failed', err)\n }\n })\n }, [config, addressesKey])\n return null\n}\n\n/**\n * Provider that supplies a React Query client to Seed list hooks (useSchemas, useItems, useModels, etc.)\n * so results are cached and shared across components. Wrap your app (or the subtree that uses Seed hooks).\n *\n * - config prop: starts the client with `client.init({ config, addresses })`.\n * - No queryClient prop: uses an internal QueryClient with Seed defaults.\n * - queryClient prop: use your own client (e.g. merge getSeedQueryDefaultOptions when creating it).\n */\nexport function SeedProvider({\n children,\n config,\n addresses,\n onInitError,\n queryClient: queryClientProp,\n queryClientRef,\n}: SeedProviderProps) {\n const queryClient = useMemo(\n () => queryClientProp ?? createSeedQueryClient(),\n [queryClientProp]\n )\n if (queryClientRef) {\n queryClientRef.current = queryClient\n if (typeof window !== 'undefined') {\n const w = window as { __TEST_SEED_QUERY_CLIENT__?: QueryClient }\n w.__TEST_SEED_QUERY_CLIENT__ = queryClient\n try {\n if (window.parent && window.parent !== window) (window.parent as { __TEST_SEED_QUERY_CLIENT__?: QueryClient }).__TEST_SEED_QUERY_CLIENT__ = queryClient\n } catch {\n // cross-origin frame, ignore\n }\n }\n }\n return (\n <QueryClientProvider client={queryClient}>\n <SeedAddressRevisionProvider queryClient={queryClient}>\n <SeedProviderEventSubscriber queryClient={queryClient} />\n {config && <SeedClientInit config={config} addresses={addresses} onInitError={onInitError} />}\n {children}\n </SeedAddressRevisionProvider>\n </QueryClientProvider>\n )\n}\n","import React, { useEffect, useRef, type ReactNode } from 'react'\nimport { client } from '@seedprotocol/sdk'\nimport type { SeedConstructorOptions, SeedConfig, SchemaFileInput } from '@seedprotocol/sdk'\nimport { useIsClientReady } from './client'\n\n/** Schema input: path string or schema object. Merged into config.schema at init. */\ntype SchemaInput = string | SchemaFileInput\n\nfunction DefaultLoading() {\n return (\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n height: '100%',\n width: '100%',\n }}\n >\n Loading...\n </div>\n )\n}\n\nexport type SeedClientGateProps = {\n /** Seed client init options (config + addresses). Required. */\n initConfig: SeedConstructorOptions\n /** Optional canonical schema. When provided, merged into config.schema and loaded at init (no separate import). */\n schema?: SchemaInput\n /** Custom loading UI. Default: simple centered \"Loading...\" text. */\n loadingComponent?: ReactNode\n /** @deprecated Never called. Schema loads at init via config.schema, and a schema that fails to load fails init: handle it in `onInitError`. */\n onSchemaImportError?: (err: unknown) => void\n /**\n * Called when client init fails; children stay hidden behind the loading UI. A\n * `FileSystemLockedError` (`code === 'OPFS_LOCKED'`) means another tab is using the app's\n * storage: offer \"close other tabs / retry\", where retry calls `client.init` again.\n */\n onInitError?: (err: unknown) => void\n /** Class for the root wrapper. e.g. \"relative flex h-screen w-screen\" */\n wrapperClassName?: string\n /** Class for the loading overlay. e.g. \"absolute inset-0 z-50 flex items-center justify-center bg-zinc-950\" */\n loadingClassName?: string\n children: ReactNode\n}\n\n/**\n * A dependency that changes only when `value` holds different data, so an inline object doesn't\n * re-run an effect on every render. Falls back to identity for values JSON can't encode.\n */\nfunction valueKey(value: unknown): unknown {\n try {\n return JSON.stringify(value ?? null)\n } catch {\n return value\n }\n}\n\n/**\n * Gates children until the Seed client is initialized and optionally imports a schema.\n * Mounted inside RouterProvider so we never swap the router—only the route content.\n * This avoids removeChild DOM errors and ensures QueryClientProvider stays stable.\n * Both overlay and content stay in DOM; we only toggle visibility via classes.\n * After `client.stop()` it shows the loading state again until the next init finishes, without\n * a remount.\n */\nexport function SeedClientGate({\n initConfig,\n schema,\n loadingComponent,\n wrapperClassName,\n loadingClassName,\n onInitError,\n children,\n}: SeedClientGateProps) {\n const isClientReady = useIsClientReady()\n // Kept in a ref so an inline callback doesn't re-run init on every render.\n const onInitErrorRef = useRef(onInitError)\n onInitErrorRef.current = onInitError\n // Compared by value, so an inline initConfig or schema doesn't re-run init on every render.\n const initConfigRef = useRef(initConfig)\n initConfigRef.current = initConfig\n const schemaRef = useRef(schema)\n schemaRef.current = schema\n const initConfigKey = valueKey(initConfig)\n const schemaKey = valueKey(schema)\n\n useEffect(() => {\n const initConfig = initConfigRef.current\n const schema = schemaRef.current\n const effectiveConfig: SeedConstructorOptions = schema\n ? {\n ...initConfig,\n config: {\n ...initConfig.config,\n schema,\n } as SeedConfig,\n }\n : initConfig\n client.init(effectiveConfig).catch((err: unknown) => {\n if (onInitErrorRef.current) {\n onInitErrorRef.current(err)\n } else {\n console.error('[SeedClientGate] client init failed', err)\n }\n })\n }, [initConfigKey, schemaKey])\n\n const loadingContent = loadingComponent ?? <DefaultLoading />\n\n const wrapperStyle = !wrapperClassName\n ? { position: 'relative' as const, display: 'flex' as const, height: '100vh', width: '100vw' }\n : undefined\n\n const overlayStyle: React.CSSProperties = {\n display: isClientReady ? 'none' : 'flex',\n ...(!loadingClassName && {\n position: 'absolute',\n inset: 0,\n zIndex: 50,\n alignItems: 'center',\n justifyContent: 'center',\n }),\n }\n\n const contentStyle: React.CSSProperties = {\n flex: 1,\n display: isClientReady ? 'flex' : 'none',\n flexDirection: 'column',\n }\n\n return (\n <div className={wrapperClassName} style={wrapperStyle}>\n <div\n className={loadingClassName}\n style={overlayStyle}\n aria-hidden={isClientReady}\n >\n {loadingContent}\n </div>\n <div style={contentStyle}>\n {children}\n </div>\n </div>\n )\n}\n","/**\n * Shared OPFS path helpers. Paths are '/'-separated and relative to the OPFS root\n * (e.g. 'app-files/files/images/photo.jpg').\n */\n\nfunction splitPath(path: string): string[] {\n const parts = path.split('/').filter(Boolean)\n if (parts.length === 0) throw new Error(`Invalid OPFS path: \"${path}\"`)\n return parts\n}\n\n/** Walk to the directory handle at `path`. */\nexport async function resolveDirectoryHandle(\n path: string,\n root?: FileSystemDirectoryHandle,\n): Promise<FileSystemDirectoryHandle> {\n let handle = root ?? (await navigator.storage.getDirectory())\n for (const part of path.split('/').filter(Boolean)) {\n handle = await handle.getDirectoryHandle(part)\n }\n return handle\n}\n\n/** Resolve the parent directory handle and the entry name for `path`. */\nasync function resolveParent(\n path: string,\n root?: FileSystemDirectoryHandle,\n): Promise<{ dir: FileSystemDirectoryHandle; name: string }> {\n const parts = splitPath(path)\n const name = parts.pop()!\n const dir = await resolveDirectoryHandle(parts.join('/'), root)\n return { dir, name }\n}\n\nexport async function resolveFileHandle(\n path: string,\n root?: FileSystemDirectoryHandle,\n): Promise<FileSystemFileHandle> {\n const { dir, name } = await resolveParent(path, root)\n return dir.getFileHandle(name)\n}\n\n/** Read the file at `path` as a File (which is a Blob). */\nexport async function getOPFSFile(path: string, root?: FileSystemDirectoryHandle): Promise<File> {\n const handle = await resolveFileHandle(path, root)\n return handle.getFile()\n}\n\nexport async function deleteOPFSEntry(path: string, root?: FileSystemDirectoryHandle): Promise<void> {\n const { dir, name } = await resolveParent(path, root)\n await dir.removeEntry(name)\n}\n\nasync function pruneEmptyDirectory(parent: FileSystemDirectoryHandle, name: string): Promise<boolean> {\n const dir = await parent.getDirectoryHandle(name)\n const children: [string, FileSystemHandle][] = []\n for await (const child of dir.entries()) children.push(child)\n let empty = true\n for (const [childName, handle] of children) {\n if (handle.kind === 'file' || !(await pruneEmptyDirectory(dir, childName))) empty = false\n }\n if (empty) await parent.removeEntry(name)\n return empty\n}\n\n/**\n * Remove the folder at `path` and any folders inside it that hold no files. Folders that\n * still hold files stay. Resolves true when `path` itself was removed (or was already gone).\n */\nexport async function removeEmptyOPFSDirectories(path: string, root?: FileSystemDirectoryHandle): Promise<boolean> {\n try {\n const { dir, name } = await resolveParent(path, root)\n return await pruneEmptyDirectory(dir, name)\n } catch (err) {\n if (err instanceof DOMException && err.name === 'NotFoundError') return true\n throw err\n }\n}\n\n/** True when this browser exposes the Origin Private File System. */\nexport function isOPFSSupported(): boolean {\n return (\n typeof navigator !== 'undefined' &&\n !!navigator.storage &&\n typeof navigator.storage.getDirectory === 'function'\n )\n}\n\nconst SIGNATURES: Array<{ type: string; test: (b: Uint8Array) => boolean }> = [\n { type: 'image/jpeg', test: (b) => b[0] === 0xff && b[1] === 0xd8 && b[2] === 0xff },\n {\n type: 'image/png',\n test: (b) => b[0] === 0x89 && b[1] === 0x50 && b[2] === 0x4e && b[3] === 0x47,\n },\n { type: 'image/gif', test: (b) => ascii(b, 0, 4) === 'GIF8' },\n { type: 'image/webp', test: (b) => ascii(b, 0, 4) === 'RIFF' && ascii(b, 8, 12) === 'WEBP' },\n {\n type: 'image/avif',\n test: (b) => ascii(b, 4, 8) === 'ftyp' && ['avif', 'avis'].includes(ascii(b, 8, 12)),\n },\n { type: 'application/vnd.sqlite3', test: (b) => ascii(b, 0, 15) === 'SQLite format 3' },\n { type: 'application/pdf', test: (b) => ascii(b, 0, 5) === '%PDF-' },\n]\n\nfunction ascii(bytes: Uint8Array, start: number, end: number): string {\n return String.fromCharCode(...bytes.subarray(start, end))\n}\n\n/** Identify a file type from its leading bytes (at least 16 bytes recommended). */\nexport function sniffMimeType(bytes: Uint8Array): string | undefined {\n return SIGNATURES.find((s) => s.test(bytes))?.type\n}\n\n/**\n * OPFS doesn't store MIME types, so File.type comes from the extension and is empty\n * for files saved under an Arweave transaction ID. Sniff those from their contents.\n */\nexport async function detectMimeType(file: Blob): Promise<string | undefined> {\n if (file.size === 0) return undefined\n const head = new Uint8Array(await file.slice(0, 16).arrayBuffer())\n return sniffMimeType(head)\n}\n","import { useState, useCallback, useEffect } from 'react'\nimport { detectMimeType, resolveDirectoryHandle } from './opfsPaths'\n\nexport interface OPFSFile {\n name: string\n path: string\n size: number\n /** `File.type`, which OPFS derives from the extension. 'application/octet-stream' when unknown. */\n type: string\n /**\n * Type detected from the file's leading bytes, set only when `File.type` is empty\n * (e.g. images saved under an Arweave transaction ID with no extension).\n */\n detectedType?: string\n lastModified: number\n}\n\nasync function scanDirectory(\n dirHandle: FileSystemDirectoryHandle,\n basePath: string = ''\n): Promise<OPFSFile[]> {\n const foundFiles: OPFSFile[] = []\n\n try {\n for await (const [name, handle] of dirHandle.entries()) {\n const currentPath = basePath ? `${basePath}/${name}` : name\n\n if (handle.kind === 'file') {\n try {\n const file = await (handle as FileSystemFileHandle).getFile()\n const detectedType = file.type ? undefined : await detectMimeType(file).catch(() => undefined)\n foundFiles.push({\n name,\n path: currentPath,\n size: file.size,\n type: file.type || 'application/octet-stream',\n ...(detectedType && { detectedType }),\n lastModified: file.lastModified,\n })\n } catch (err) {\n console.warn(`Failed to read file ${currentPath}:`, err)\n }\n } else if (handle.kind === 'directory') {\n const subFiles = await scanDirectory(handle as FileSystemDirectoryHandle, currentPath)\n foundFiles.push(...subFiles)\n }\n }\n } catch (err) {\n console.warn(`Failed to scan directory ${basePath}:`, err)\n }\n\n return foundFiles\n}\n\nexport interface UseOPFSFilesOptions {\n /** Optional subdirectory to scan (e.g. 'app-files'). Default: root. */\n rootPath?: string\n}\n\n/**\n * Hook to scan and list all files in OPFS (Origin Private File System).\n * Uses navigator.storage.getDirectory() - works in browsers that support OPFS.\n *\n * `isLoading` is true during every scan, including refetches; `files` keeps the\n * previous result until the new scan finishes.\n *\n * @example\n * ```tsx\n * const { files, isLoading, error, refetch } = useOPFSFiles()\n * ```\n */\nexport function useOPFSFiles(options: UseOPFSFilesOptions = {}) {\n const { rootPath } = options\n const [files, setFiles] = useState<OPFSFile[]>([])\n const [isLoading, setIsLoading] = useState(true)\n const [hasLoaded, setHasLoaded] = useState(false)\n const [error, setError] = useState<string | null>(null)\n const [errorName, setErrorName] = useState<string | null>(null)\n\n const load = useCallback(async () => {\n setIsLoading(true)\n setError(null)\n setErrorName(null)\n\n try {\n const dirHandle = await resolveDirectoryHandle(rootPath ?? '')\n const allFiles = await scanDirectory(dirHandle, rootPath || '')\n setFiles(allFiles.sort((a, b) => a.path.localeCompare(b.path)))\n } catch (err) {\n setError(\n 'Failed to access OPFS: ' + (err instanceof Error ? err.message : String(err))\n )\n setErrorName(err instanceof Error ? err.name : null)\n console.error('OPFS access error:', err)\n } finally {\n setIsLoading(false)\n setHasLoaded(true)\n }\n }, [rootPath])\n\n useEffect(() => {\n load()\n }, [load])\n\n return {\n files,\n isLoading,\n /** False until the first scan finishes. */\n hasLoaded,\n error,\n /** The thrown error's name, e.g. 'NotFoundError' when `rootPath` doesn't exist. */\n errorName,\n refetch: load,\n }\n}\n","const SIZE_UNITS = ['B', 'KB', 'MB', 'GB', 'TB']\n\n/** 1024-based size: '0 B', '812 B', '4.2 KB', '38 MB'. One decimal below 10. */\nexport function formatFileSize(bytes: number): string {\n if (!Number.isFinite(bytes) || bytes <= 0) return '0 B'\n const i = Math.min(SIZE_UNITS.length - 1, Math.floor(Math.log(bytes) / Math.log(1024)))\n const value = bytes / Math.pow(1024, i)\n const shown = i === 0 ? value : value < 10 ? Math.round(value * 10) / 10 : Math.round(value)\n return `${shown} ${SIZE_UNITS[i]}`\n}\n\nconst MINUTE = 60_000\nconst HOUR = 60 * MINUTE\nconst DAY = 24 * HOUR\n\n/** Short relative time: 'just now', '5 min ago', '3 h ago', 'yesterday', '12 days ago', or a date. */\nexport function formatRelativeTime(timestamp: number, now: number = Date.now()): string {\n const diff = now - timestamp\n if (diff < MINUTE) return 'just now'\n if (diff < HOUR) return `${Math.round(diff / MINUTE)} min ago`\n if (diff < DAY) return `${Math.round(diff / HOUR)} h ago`\n const days = Math.round(diff / DAY)\n if (days === 1) return 'yesterday'\n if (days < 30) return `${days} days ago`\n return new Date(timestamp).toLocaleDateString(undefined, { dateStyle: 'medium' })\n}\n\nexport function formatDateTime(timestamp: number): string {\n return new Date(timestamp).toLocaleString(undefined, { dateStyle: 'medium', timeStyle: 'short' })\n}\n\n/** Shorten long names (e.g. 43-char Arweave transaction IDs) from the middle, keeping the extension visible. */\nexport function truncateMiddle(value: string, max: number): string {\n if (value.length <= max) return value\n const head = Math.ceil((max - 1) / 2)\n const tail = Math.floor((max - 1) / 2)\n return `${value.slice(0, head)}…${value.slice(value.length - tail)}`\n}\n","/**\n * Icons vendored from Lucide (https://lucide.dev), ISC License,\n * Copyright (c) Lucide Contributors. See docs/adr/0003-icons-vendored-from-lucide.md.\n *\n * To add an icon, copy the children of its <svg> from lucide.dev into ICONS.\n * Keep Lucide's 24×24 viewBox and stroke-width 2 so icons match hosts that use lucide-react.\n */\nimport React from 'react'\n\nconst ICONS = {\n check: <path d=\"M20 6 9 17l-5-5\" />,\n chevronLeft: <path d=\"m15 18-6-6 6-6\" />,\n chevronRight: <path d=\"m9 18 6-6-6-6\" />,\n copy: (\n <>\n <rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\" />\n <path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\" />\n </>\n ),\n database: (\n <>\n <ellipse cx=\"12\" cy=\"5\" rx=\"9\" ry=\"3\" />\n <path d=\"M3 5V19A9 3 0 0 0 21 19V5\" />\n <path d=\"M3 12A9 3 0 0 0 21 12\" />\n </>\n ),\n download: (\n <>\n <path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\" />\n <polyline points=\"7 10 12 15 17 10\" />\n <line x1=\"12\" x2=\"12\" y1=\"15\" y2=\"3\" />\n </>\n ),\n file: (\n <>\n <path d=\"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z\" />\n <path d=\"M14 2v4a2 2 0 0 0 2 2h4\" />\n </>\n ),\n fileCode: (\n <>\n <path d=\"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z\" />\n <path d=\"M14 2v4a2 2 0 0 0 2 2h4\" />\n <path d=\"m10 13-2 2 2 2\" />\n <path d=\"m14 17 2-2-2-2\" />\n </>\n ),\n fileJson: (\n <>\n <path d=\"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z\" />\n <path d=\"M14 2v4a2 2 0 0 0 2 2h4\" />\n <path d=\"M10 12a1 1 0 0 0-1 1v1a1 1 0 0 1-1 1 1 1 0 0 1 1 1v1a1 1 0 0 0 1 1\" />\n <path d=\"M14 18a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1 1 1 0 0 1-1-1v-1a1 1 0 0 0-1-1\" />\n </>\n ),\n fileText: (\n <>\n <path d=\"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z\" />\n <path d=\"M14 2v4a2 2 0 0 0 2 2h4\" />\n <path d=\"M10 9H8\" />\n <path d=\"M16 13H8\" />\n <path d=\"M16 17H8\" />\n </>\n ),\n folder: (\n <path d=\"M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z\" />\n ),\n folderOpen: (\n <path d=\"m6 14 1.5-2.9A2 2 0 0 1 9.24 10H20a2 2 0 0 1 1.94 2.5l-1.54 6a2 2 0 0 1-1.95 1.5H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H18a2 2 0 0 1 2 2v2\" />\n ),\n image: (\n <>\n <rect width=\"18\" height=\"18\" x=\"3\" y=\"3\" rx=\"2\" ry=\"2\" />\n <circle cx=\"9\" cy=\"9\" r=\"2\" />\n <path d=\"m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21\" />\n </>\n ),\n imageOff: (\n <>\n <line x1=\"2\" x2=\"22\" y1=\"2\" y2=\"22\" />\n <path d=\"M10.41 10.41a2 2 0 1 1-2.83-2.83\" />\n <line x1=\"13.5\" x2=\"6\" y1=\"13.5\" y2=\"21\" />\n <line x1=\"18\" x2=\"21\" y1=\"12\" y2=\"15\" />\n <path d=\"M3.59 3.59A1.99 1.99 0 0 0 3 5v14a2 2 0 0 0 2 2h14c.55 0 1.052-.22 1.41-.59\" />\n <path d=\"M21 15V5a2 2 0 0 0-2-2H9\" />\n </>\n ),\n layers: (\n <>\n <path d=\"m12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83Z\" />\n <path d=\"m22 17.65-9.17 4.16a2 2 0 0 1-1.66 0L2 17.65\" />\n <path d=\"m22 12.65-9.17 4.16a2 2 0 0 1-1.66 0L2 12.65\" />\n </>\n ),\n layoutGrid: (\n <>\n <rect width=\"7\" height=\"7\" x=\"3\" y=\"3\" rx=\"1\" />\n <rect width=\"7\" height=\"7\" x=\"14\" y=\"3\" rx=\"1\" />\n <rect width=\"7\" height=\"7\" x=\"14\" y=\"14\" rx=\"1\" />\n <rect width=\"7\" height=\"7\" x=\"3\" y=\"14\" rx=\"1\" />\n </>\n ),\n list: (\n <>\n <path d=\"M3 12h.01\" />\n <path d=\"M3 18h.01\" />\n <path d=\"M3 6h.01\" />\n <path d=\"M8 12h13\" />\n <path d=\"M8 18h13\" />\n <path d=\"M8 6h13\" />\n </>\n ),\n refresh: (\n <>\n <path d=\"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8\" />\n <path d=\"M21 3v5h-5\" />\n <path d=\"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16\" />\n <path d=\"M8 16H3v5\" />\n </>\n ),\n search: (\n <>\n <circle cx=\"11\" cy=\"11\" r=\"8\" />\n <path d=\"m21 21-4.3-4.3\" />\n </>\n ),\n trash: (\n <>\n <path d=\"M3 6h18\" />\n <path d=\"M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6\" />\n <path d=\"M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2\" />\n <line x1=\"10\" x2=\"10\" y1=\"11\" y2=\"17\" />\n <line x1=\"14\" x2=\"14\" y1=\"11\" y2=\"17\" />\n </>\n ),\n triangleAlert: (\n <>\n <path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\" />\n <path d=\"M12 9v4\" />\n <path d=\"M12 17h.01\" />\n </>\n ),\n x: (\n <>\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </>\n ),\n} satisfies Record<string, React.ReactNode>\n\nexport type IconName = keyof typeof ICONS\n\nexport function Icon({\n name,\n size = 16,\n strokeWidth = 2,\n className,\n}: {\n name: IconName\n size?: number\n strokeWidth?: number\n className?: string\n}) {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={strokeWidth}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n focusable=\"false\"\n className={className}\n >\n {ICONS[name]}\n </svg>\n )\n}\n","import React, { useEffect, useRef, useState } from 'react'\nimport type { OPFSFile } from '../useOPFSFiles'\nimport type { FileEntry, FolderSummary } from './fileModel'\nimport { formatFileSize, truncateMiddle } from './format'\nimport { Icon } from './icons'\nimport type { ResolvedDeleteAction } from './types'\n\nconst MAX_LISTED = 6\n\nexport interface DeleteDialogProps {\n entries: FileEntry[]\n /** Folder that listed paths are shown relative to. */\n rootPath: string\n warning: string | null\n /** Resolves the label and tone for the files the confirm button would remove. */\n deleteActionFor: (includeVariants: boolean) => ResolvedDeleteAction\n onCancel: () => void\n onConfirm: (includeVariants: boolean) => void\n className?: string\n}\n\n/** Native modal <dialog> (focus trap, Esc, top layer) wrapping a confirm form. */\nfunction ConfirmDialog({\n className,\n onCancel,\n onSubmit,\n children,\n}: {\n className?: string\n onCancel: () => void\n onSubmit: () => void\n children: React.ReactNode\n}) {\n const ref = useRef<HTMLDialogElement>(null)\n\n useEffect(() => {\n const dialog = ref.current\n if (dialog && !dialog.open) {\n dialog.showModal()\n // showModal() focuses the first control, which may be a checkbox or Download. Start on Cancel.\n dialog.querySelector<HTMLElement>('[data-initial-focus]')?.focus()\n }\n return () => {\n if (dialog?.open) dialog.close()\n }\n }, [])\n\n return (\n <dialog\n ref={ref}\n className={['seed-fm-dialog', className].filter(Boolean).join(' ')}\n aria-labelledby=\"seed-fm-delete-title\"\n onCancel={(e) => {\n e.preventDefault()\n onCancel()\n }}\n onClick={(e) => {\n // A click on the backdrop lands on the <dialog> element itself.\n if (e.target === e.currentTarget) onCancel()\n }}\n >\n <form\n method=\"dialog\"\n onSubmit={(e) => {\n e.preventDefault()\n onSubmit()\n }}\n >\n {children}\n </form>\n </dialog>\n )\n}\n\n/** Delete confirmation for one or more files. */\nexport function DeleteDialog({\n entries,\n rootPath,\n warning,\n deleteActionFor,\n onCancel,\n onConfirm,\n className,\n}: DeleteDialogProps) {\n const [includeVariants, setIncludeVariants] = useState(true)\n\n const variants = entries.flatMap((e) => e.variants)\n const variantsSize = variants.reduce((n, v) => n + v.file.size, 0)\n const count = entries.length + (includeVariants ? variants.length : 0)\n const prefix = rootPath ? `${rootPath}/` : ''\n const action = deleteActionFor(includeVariants)\n const heading = action.label\n ? `${action.label}?`\n : entries.length === 1\n ? `Delete ${truncateMiddle(entries[0].file.name, 32)}?`\n : `Delete ${entries.length} files?`\n\n return (\n <ConfirmDialog className={className} onCancel={onCancel} onSubmit={() => onConfirm(includeVariants)}>\n <h3 id=\"seed-fm-delete-title\">{heading}</h3>\n <p>\n This removes {entries.length === 1 ? 'it' : 'them'} from this browser’s private storage. Copies on Arweave or\n other devices aren’t affected. You can’t undo this.\n </p>\n <ul>\n {entries.slice(0, MAX_LISTED).map((e) => (\n <li key={e.file.path}>\n <code title={e.file.path}>{e.file.path.startsWith(prefix) ? e.file.path.slice(prefix.length) : e.file.path}</code>\n <span>{formatFileSize(e.file.size)}</span>\n </li>\n ))}\n {entries.length > MAX_LISTED && (\n <li>\n <span>and {entries.length - MAX_LISTED} more</span>\n </li>\n )}\n </ul>\n {variants.length > 0 && (\n <label>\n <input\n type=\"checkbox\"\n className=\"seed-fm-checkbox\"\n checked={includeVariants}\n onChange={(e) => setIncludeVariants(e.target.checked)}\n />\n Also delete {variants.length} resized {variants.length === 1 ? 'copy' : 'copies'} ({formatFileSize(variantsSize)})\n </label>\n )}\n {warning && (\n <div className=\"seed-fm-warning\" role=\"alert\">\n <Icon name=\"triangleAlert\" size={16} />\n <span>{warning}</span>\n </div>\n )}\n <div className=\"seed-fm-dialog-actions\">\n <button type=\"button\" className=\"seed-fm-btn\" onClick={onCancel} data-initial-focus>\n Cancel\n </button>\n <button\n type=\"submit\"\n className={`seed-fm-btn ${action.destructive ? 'seed-fm-btn--danger' : 'seed-fm-btn--primary'}`}\n >\n {action.label ?? `Delete ${count} ${count === 1 ? 'file' : 'files'}`}\n </button>\n </div>\n </ConfirmDialog>\n )\n}\n\nexport interface FolderDeleteDialogProps {\n folder: FolderSummary\n /** Every file under the folder that this manager shows, at any depth. */\n files: OPFSFile[]\n /** Files under the folder that `filter` hides. They're kept, so the folder stays. */\n hiddenCount: number\n warning: string | null\n action: ResolvedDeleteAction\n busy: boolean\n /** Saves the folder as a .zip. Resolves true once it's saved. */\n onDownload: () => Promise<boolean>\n onCancel: () => void\n onConfirm: () => void\n className?: string\n}\n\n/** Delete confirmation for a folder, with a chance to save a copy first. */\nexport function FolderDeleteDialog({\n folder,\n files,\n hiddenCount,\n warning,\n action,\n busy,\n onDownload,\n onCancel,\n onConfirm,\n className,\n}: FolderDeleteDialogProps) {\n const [downloaded, setDownloaded] = useState(false)\n const size = files.reduce((n, f) => n + f.size, 0)\n const prefix = `${folder.path}/`\n const fileCount = `${files.length} ${files.length === 1 ? 'file' : 'files'}`\n // The folder card counts grouped resized copies as part of their original.\n const copies = files.length - folder.fileCount\n\n return (\n <ConfirmDialog className={className} onCancel={onCancel} onSubmit={onConfirm}>\n <h3 id=\"seed-fm-delete-title\">\n {action.label ? `${action.label}?` : `Delete the “${truncateMiddle(folder.name, 32)}” folder?`}\n </h3>\n <p>\n This removes the folder and the {fileCount} in it ({formatFileSize(size)}\n {copies > 0 && `, including ${copies} resized ${copies === 1 ? 'copy' : 'copies'}`}) from this browser’s private\n storage. Copies on Arweave or other devices aren’t affected.\n </p>\n <div className=\"seed-fm-irreversible\">\n <Icon name=\"triangleAlert\" size={16} />\n <span>\n <strong>This can’t be undone.</strong> Once deleted, these files can’t be recovered from this browser.\n </span>\n </div>\n <ul>\n {files.slice(0, MAX_LISTED).map((f) => (\n <li key={f.path}>\n <code title={f.path}>{f.path.slice(prefix.length)}</code>\n <span>{formatFileSize(f.size)}</span>\n </li>\n ))}\n {files.length > MAX_LISTED && (\n <li>\n <span>and {files.length - MAX_LISTED} more</span>\n </li>\n )}\n </ul>\n <div className=\"seed-fm-keepcopy\">\n <span>{downloaded ? 'Saved a copy to your computer.' : 'Want a copy first? Save the folder to your computer.'}</span>\n <button\n type=\"button\"\n className=\"seed-fm-btn\"\n disabled={busy}\n onClick={async () => {\n if (await onDownload()) setDownloaded(true)\n }}\n >\n <Icon name={downloaded ? 'check' : 'download'} size={15} /> {downloaded ? 'Download again' : 'Download .zip'}\n </button>\n </div>\n {hiddenCount > 0 && (\n <p>\n {hiddenCount} other {hiddenCount === 1 ? 'file' : 'files'} in this folder {hiddenCount === 1 ? 'is' : 'are'}{' '}\n hidden here and won’t be deleted, so the folder itself will stay.\n </p>\n )}\n {warning && (\n <div className=\"seed-fm-warning\" role=\"alert\">\n <Icon name=\"triangleAlert\" size={16} />\n <span>{warning}</span>\n </div>\n )}\n <div className=\"seed-fm-dialog-actions\">\n <button type=\"button\" className=\"seed-fm-btn\" onClick={onCancel} data-initial-focus>\n Cancel\n </button>\n <button\n type=\"submit\"\n disabled={busy}\n className={`seed-fm-btn ${action.destructive ? 'seed-fm-btn--danger' : 'seed-fm-btn--primary'}`}\n >\n {action.label ?? `Delete folder (${fileCount})`}\n </button>\n </div>\n </ConfirmDialog>\n )\n}\n","import type { OPFSFile } from '../useOPFSFiles'\n\nexport type FileKind = 'image' | 'database' | 'json' | 'html' | 'text' | 'other'\n\nexport type SortKey = 'name' | 'modified' | 'size'\n\n/** Detected type when available, otherwise File.type. */\nexport function effectiveType(file: OPFSFile): string {\n return file.detectedType ?? file.type\n}\n\nconst DATABASE_NAME = /\\.(db|sqlite|sqlite3)(-journal|-wal|-shm)?$/i\n\nexport function fileKind(file: OPFSFile): FileKind {\n const type = effectiveType(file)\n if (type.startsWith('image/')) return 'image'\n if (type === 'application/vnd.sqlite3' || DATABASE_NAME.test(file.name)) return 'database'\n if (type === 'application/json' || /\\.json$/i.test(file.name)) return 'json'\n if (type === 'text/html' || /\\.html?$/i.test(file.name)) return 'html'\n if (type.startsWith('text/')) return 'text'\n return 'other'\n}\n\n/** Short label for a file's format: its extension, or the detected subtype when there is none. */\nexport function formatLabel(file: OPFSFile): string {\n const ext = /\\.([a-z0-9-]{1,12})$/i.exec(file.name)?.[1]\n if (ext) return ext.toLowerCase()\n const type = effectiveType(file)\n if (type === 'application/octet-stream') return 'bin'\n return type.split('/')[1]?.replace(/^vnd\\./, '') ?? 'bin'\n}\n\n/** A resized copy the SDK writes to `…/images/<width>/<base>.webp`. */\nexport interface ImageVariant {\n file: OPFSFile\n width: number\n}\n\n/** A row in the manager: a file plus, for originals, its resized copies. */\nexport interface FileEntry {\n file: OPFSFile\n /** Resized copies, narrowest first. Empty unless variants are grouped. */\n variants: ImageVariant[]\n /** Size of the file plus its variants. */\n totalSize: number\n}\n\nconst VARIANT_PATH = /^(.*\\/)?images\\/(\\d+)\\/([^/]+)\\.webp$/\nconst ORIGINAL_PATH = /^(.*\\/)?images\\/([^/]+)$/\n\nconst stripExtension = (name: string) => name.replace(/\\.[^/.]+$/, '')\n\nexport function parseImageVariant(path: string): { key: string; width: number } | null {\n const m = VARIANT_PATH.exec(path)\n if (!m) return null\n return { key: `${m[1] ?? ''}images/${m[3]}`, width: Number(m[2]) }\n}\n\nfunction originalKey(path: string): string | null {\n const m = ORIGINAL_PATH.exec(path)\n return m ? `${m[1] ?? ''}images/${stripExtension(m[2])}` : null\n}\n\nconst toEntry = (file: OPFSFile): FileEntry => ({ file, variants: [], totalSize: file.size })\n\n/**\n * Turn the flat file list into entries. With `groupVariants`, resized copies are\n * attached to their original image and removed from the list. Copies whose original\n * is missing stay in the list on their own.\n */\nexport function buildEntries(files: OPFSFile[], groupVariants: boolean): FileEntry[] {\n if (!groupVariants) return files.map(toEntry)\n\n const originals = new Map<string, FileEntry>()\n const entries: FileEntry[] = []\n const variants: Array<{ file: OPFSFile; key: string; width: number }> = []\n\n for (const file of files) {\n const variant = parseImageVariant(file.path)\n if (variant) {\n variants.push({ file, ...variant })\n continue\n }\n const entry = toEntry(file)\n entries.push(entry)\n const key = originalKey(file.path)\n if (key && fileKind(file) === 'image') originals.set(key, entry)\n }\n\n for (const v of variants) {\n const original = originals.get(v.key)\n if (original) {\n original.variants.push({ file: v.file, width: v.width })\n original.totalSize += v.file.size\n } else {\n entries.push(toEntry(v.file))\n }\n }\n for (const entry of originals.values()) entry.variants.sort((a, b) => a.width - b.width)\n return entries\n}\n\nexport interface FolderSummary {\n name: string\n path: string\n fileCount: number\n size: number\n}\n\nexport interface DirectoryListing {\n folders: FolderSummary[]\n entries: FileEntry[]\n}\n\nconst joinPath = (dir: string, name: string) => (dir ? `${dir}/${name}` : name)\n\n/**\n * List the folders and files directly inside `cwd`. With a search query, returns\n * every matching file at any depth below `cwd` and no folders.\n */\nexport function listDirectory(\n all: FileEntry[],\n cwd: string,\n options: { query?: string; sort?: SortKey } = {},\n): DirectoryListing {\n const prefix = cwd ? `${cwd}/` : ''\n const inside = all.filter((e) => e.file.path.startsWith(prefix))\n const query = options.query?.trim().toLowerCase()\n\n if (query) {\n const matches = inside.filter((e) => e.file.path.slice(prefix.length).toLowerCase().includes(query))\n return { folders: [], entries: sortEntries(matches, options.sort) }\n }\n\n const folders = new Map<string, FolderSummary>()\n const entries: FileEntry[] = []\n for (const entry of inside) {\n const rest = entry.file.path.slice(prefix.length)\n const slash = rest.indexOf('/')\n if (slash === -1) {\n entries.push(entry)\n continue\n }\n const name = rest.slice(0, slash)\n const folder = folders.get(name) ?? { name, path: joinPath(cwd, name), fileCount: 0, size: 0 }\n folder.fileCount += 1\n folder.size += entry.totalSize\n folders.set(name, folder)\n }\n\n return {\n folders: [...folders.values()].sort((a, b) => a.name.localeCompare(b.name)),\n entries: sortEntries(entries, options.sort),\n }\n}\n\nexport function sortEntries(entries: FileEntry[], sort: SortKey = 'name'): FileEntry[] {\n const compare: Record<SortKey, (a: FileEntry, b: FileEntry) => number> = {\n name: (a, b) => a.file.name.localeCompare(b.file.name),\n modified: (a, b) => b.file.lastModified - a.file.lastModified,\n size: (a, b) => b.totalSize - a.totalSize,\n }\n return [...entries].sort(compare[sort])\n}\n\n/** Bytes per kind across all files, for the storage meter. */\nexport function sizeByKind(files: OPFSFile[]): Record<FileKind, number> {\n const totals: Record<FileKind, number> = { image: 0, database: 0, json: 0, html: 0, text: 0, other: 0 }\n for (const f of files) totals[fileKind(f)] += f.size\n return totals\n}\n\n/** Default deleteWarning: flags the Seed client's SQLite database. */\nexport function defaultDeleteWarning(files: OPFSFile[]): string | null {\n const db = files.find((f) => /(^|\\/)db\\/seed\\.db$/.test(f.path))\n if (!db) return null\n return 'This includes the Seed database. The app loses its local items and drafts, and anything not yet published is gone. Reload the app afterwards.'\n}\n","import { useCallback, useEffect, useState } from 'react'\nimport { getOPFSFile } from '../opfsPaths'\nimport { fileKind, type FileEntry } from './fileModel'\n\n/** Thumbnails are drawn at about 2× the largest tile width. */\nconst THUMBNAIL_WIDTH = 400\n/** Originals smaller than this are shown as-is instead of being resized first. */\nconst RESIZE_THRESHOLD_BYTES = 256 * 1024\nconst MAX_CONCURRENT_DECODES = 4\n/** Keep an unused thumbnail's object URL around briefly so switching views doesn't reload it. */\nconst RELEASE_DELAY_MS = 30_000\n\nlet activeJobs = 0\nconst queue: Array<() => void> = []\n\nfunction schedule<T>(job: () => Promise<T>): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const run = () => {\n activeJobs++\n job()\n .then(resolve, reject)\n .finally(() => {\n activeJobs--\n queue.shift()?.()\n })\n }\n if (activeJobs < MAX_CONCURRENT_DECODES) run()\n else queue.push(run)\n })\n}\n\ninterface CacheEntry {\n promise: Promise<string>\n url?: string\n refs: number\n timer?: ReturnType<typeof setTimeout>\n}\n\nconst cache = new Map<string, CacheEntry>()\n\nfunction acquire(key: string, create: () => Promise<Blob>): { promise: Promise<string>; release: () => void } {\n let entry = cache.get(key)\n if (!entry) {\n const created: CacheEntry = { refs: 0, promise: Promise.resolve('') }\n created.promise = schedule(create).then((blob) => {\n created.url = URL.createObjectURL(blob)\n return created.url\n })\n cache.set(key, created)\n entry = created\n }\n if (entry.timer) {\n clearTimeout(entry.timer)\n entry.timer = undefined\n }\n entry.refs++\n const held = entry\n let released = false\n return {\n promise: held.promise,\n release: () => {\n if (released) return\n released = true\n held.refs--\n if (held.refs > 0) return\n held.timer = setTimeout(() => {\n if (held.url) URL.revokeObjectURL(held.url)\n if (cache.get(key) === held) cache.delete(key)\n }, RELEASE_DELAY_MS)\n },\n }\n}\n\nasync function resizeImage(blob: Blob): Promise<Blob> {\n if (blob.size < RESIZE_THRESHOLD_BYTES || typeof createImageBitmap !== 'function') return blob\n const bitmap = await createImageBitmap(blob, { resizeWidth: THUMBNAIL_WIDTH, resizeQuality: 'medium' })\n try {\n if (typeof OffscreenCanvas !== 'undefined') {\n const canvas = new OffscreenCanvas(bitmap.width, bitmap.height)\n canvas.getContext('2d')!.drawImage(bitmap, 0, 0)\n return await canvas.convertToBlob({ type: 'image/webp', quality: 0.82 })\n }\n const canvas = document.createElement('canvas')\n canvas.width = bitmap.width\n canvas.height = bitmap.height\n canvas.getContext('2d')!.drawImage(bitmap, 0, 0)\n return await new Promise<Blob>((resolve, reject) =>\n canvas.toBlob((b) => (b ? resolve(b) : reject(new Error('Canvas export failed'))), 'image/webp', 0.82),\n )\n } finally {\n bitmap.close()\n }\n}\n\n/**\n * Prefer the narrowest resized copy the SDK already wrote (480w WebP); it needs no\n * decoding on our side. Otherwise read the original and downscale it.\n */\nasync function createThumbnail(entry: FileEntry): Promise<Blob> {\n const variant = entry.variants[0]\n if (variant) return getOPFSFile(variant.file.path)\n return resizeImage(await getOPFSFile(entry.file.path))\n}\n\n// One IntersectionObserver for every tile.\ntype VisibilityCallback = () => void\nconst visibilityCallbacks = new WeakMap<Element, VisibilityCallback>()\nlet observer: IntersectionObserver | null = null\n\nfunction observeOnce(el: Element, onVisible: VisibilityCallback): () => void {\n if (typeof IntersectionObserver === 'undefined') {\n onVisible()\n return () => {}\n }\n observer ??= new IntersectionObserver(\n (records) => {\n for (const record of records) {\n if (!record.isIntersecting) continue\n observer!.unobserve(record.target)\n visibilityCallbacks.get(record.target)?.()\n visibilityCallbacks.delete(record.target)\n }\n },\n { rootMargin: '300px' },\n )\n visibilityCallbacks.set(el, onVisible)\n observer.observe(el)\n return () => {\n visibilityCallbacks.delete(el)\n observer?.unobserve(el)\n }\n}\n\nexport type ThumbnailStatus = 'idle' | 'loading' | 'ready' | 'error'\n\n/**\n * Lazily load a thumbnail object URL for an image entry once its element scrolls near\n * the viewport. Returns `status: 'idle'` for non-images.\n */\nexport function useThumbnail(entry: FileEntry) {\n const isImage = fileKind(entry.file) === 'image'\n const [element, setElement] = useState<Element | null>(null)\n const [visible, setVisible] = useState(false)\n const [url, setUrl] = useState<string | null>(null)\n const [status, setStatus] = useState<ThumbnailStatus>('idle')\n\n const ref = useCallback((el: Element | null) => setElement(el), [])\n\n useEffect(() => {\n if (!isImage || !element || visible) return\n return observeOnce(element, () => setVisible(true))\n }, [isImage, element, visible])\n\n const { path, lastModified, size } = entry.file\n const variantPath = entry.variants[0]?.file.path\n const key = `${path}:${lastModified}:${size}:${variantPath ?? ''}`\n\n useEffect(() => {\n if (!isImage || !visible) return\n let cancelled = false\n setStatus('loading')\n const handle = acquire(key, () => createThumbnail(entry))\n handle.promise.then(\n (u) => {\n if (cancelled) return\n setUrl(u)\n setStatus('ready')\n },\n () => {\n if (!cancelled) setStatus('error')\n },\n )\n return () => {\n cancelled = true\n handle.release()\n }\n // entry identity changes on every refetch; key captures what matters.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [isImage, visible, key])\n\n /** Call from <img onError> when the browser can't decode the thumbnail. */\n const markError = useCallback(() => setStatus('error'), [])\n\n return { ref, url: status === 'ready' ? url : null, status, markError }\n}\n","import React from 'react'\nimport { effectiveType, fileKind, formatLabel, type FileEntry, type FileKind } from './fileModel'\nimport { Icon, type IconName } from './icons'\nimport { useThumbnail } from './useThumbnail'\n\nexport const KIND_META: Record<FileKind, { icon: IconName; label: string; color: string }> = {\n image: { icon: 'image', label: 'Images', color: 'var(--seed-kind-image)' },\n database: { icon: 'database', label: 'Database', color: 'var(--seed-kind-database)' },\n json: { icon: 'fileJson', label: 'JSON', color: 'var(--seed-kind-json)' },\n html: { icon: 'fileCode', label: 'HTML', color: 'var(--seed-kind-html)' },\n text: { icon: 'fileText', label: 'Text', color: 'var(--seed-kind-text)' },\n other: { icon: 'file', label: 'Other', color: 'var(--seed-kind-other)' },\n}\n\nexport const kindStyle = (kind: FileKind) =>\n ({ '--seed-kind': KIND_META[kind].color }) as React.CSSProperties\n\n/**\n * Formats that can carry transparency get a checkerboard and are letterboxed instead of\n * cropped. Judged by the original: the SDK's WebP copies of a JPEG are still opaque.\n */\nconst mayBeTransparent = (entry: FileEntry) => /png|webp|gif|avif|svg/.test(effectiveType(entry.file))\n\nexport function FileThumbnail({\n entry,\n size,\n children,\n}: {\n entry: FileEntry\n size: 'tile' | 'mini'\n /** Overlays (checkbox, badges) rendered inside the frame. */\n children?: React.ReactNode\n}) {\n const kind = fileKind(entry.file)\n const { ref, url, status, markError } = useThumbnail(entry)\n const failed = kind === 'image' && status === 'error'\n const showImage = kind === 'image' && url && !failed\n\n const className = [\n size === 'tile' ? 'seed-fm-thumb' : 'seed-fm-mini',\n showImage && mayBeTransparent(entry) ? 'seed-fm-thumb--checker' : '',\n kind === 'image' && status === 'loading' ? 'seed-fm-skeleton' : '',\n ]\n .filter(Boolean)\n .join(' ')\n\n let content: React.ReactNode\n if (showImage) {\n content = <img src={url} alt=\"\" decoding=\"async\" draggable={false} onError={markError} />\n } else if (size === 'mini') {\n content = <Icon name={failed ? 'imageOff' : KIND_META[kind].icon} size={16} />\n } else if (kind === 'image' && !failed) {\n content = null\n } else {\n content = (\n <span className=\"seed-fm-kind\">\n <Icon name={failed ? 'imageOff' : KIND_META[kind].icon} size={28} strokeWidth={1.6} />\n {failed ? (\n <span className=\"seed-fm-kind-error\">Can’t decode image</span>\n ) : (\n <span className=\"seed-fm-ext\">{formatLabel(entry.file)}</span>\n )}\n </span>\n )\n }\n\n return (\n <span ref={ref} className={className} style={kindStyle(kind)}>\n {content}\n {children}\n </span>\n )\n}\n","import React from 'react'\nimport type { FileEntry } from './fileModel'\nimport { FileThumbnail } from './FileThumbnail'\nimport { formatDateTime, formatFileSize, formatRelativeTime, truncateMiddle } from './format'\nimport { Icon } from './icons'\n\nexport interface FileViewProps {\n entries: FileEntry[]\n selected: ReadonlySet<string>\n /** Click or Enter on an item. */\n onActivate: (entry: FileEntry, index: number, event: React.MouseEvent | React.KeyboardEvent) => void\n /** Checkbox, Space, or Shift-click. `range` extends from the last toggled item. */\n onToggle: (entry: FileEntry, index: number, range: boolean) => void\n tileClassName?: string\n className?: string\n}\n\nexport function FileGrid({ entries, selected, onActivate, onToggle, className, tileClassName }: FileViewProps) {\n return (\n <ul\n className={['seed-fm-grid', className].filter(Boolean).join(' ')}\n role=\"listbox\"\n aria-label=\"Files\"\n aria-multiselectable=\"true\"\n >\n {entries.map((entry, index) => {\n const { file, variants } = entry\n const isSelected = selected.has(file.path)\n return (\n <li\n key={file.path}\n role=\"option\"\n tabIndex={0}\n aria-selected={isSelected}\n aria-label={file.name}\n title={file.path}\n data-path={file.path}\n className={['seed-fm-tile', tileClassName].filter(Boolean).join(' ')}\n onClick={(e) => onActivate(entry, index, e)}\n onKeyDown={(e) => {\n if (e.target !== e.currentTarget) return\n if (e.key === 'Enter') {\n e.preventDefault()\n onActivate(entry, index, e)\n } else if (e.key === ' ') {\n e.preventDefault()\n onToggle(entry, index, e.shiftKey)\n }\n }}\n >\n <FileThumbnail entry={entry} size=\"tile\">\n <button\n type=\"button\"\n className=\"seed-fm-check\"\n tabIndex={-1}\n aria-label={`${isSelected ? 'Deselect' : 'Select'} ${file.name}`}\n onClick={(e) => {\n e.stopPropagation()\n onToggle(entry, index, e.shiftKey)\n }}\n >\n {isSelected && <Icon name=\"check\" size={14} strokeWidth={3} />}\n </button>\n {variants.length > 0 && (\n <span className=\"seed-fm-badge\" title=\"Resized copies are grouped with this image\">\n <Icon name=\"layers\" size={11} strokeWidth={2.2} />\n {variants.length} {variants.length === 1 ? 'size' : 'sizes'}\n </span>\n )}\n </FileThumbnail>\n <span className=\"seed-fm-tile-meta\">\n <span className=\"seed-fm-tile-name\">{truncateMiddle(file.name, 24)}</span>\n <span className=\"seed-fm-tile-sub\">\n {formatFileSize(file.size)} ·{' '}\n <time dateTime={new Date(file.lastModified).toISOString()} title={formatDateTime(file.lastModified)}>\n {formatRelativeTime(file.lastModified)}\n </time>\n </span>\n </span>\n </li>\n )\n })}\n </ul>\n )\n}\n","import React, { useEffect, useRef } from 'react'\nimport { effectiveType, fileKind, type FileEntry } from './fileModel'\nimport type { FileViewProps } from './FileGrid'\nimport { FileThumbnail, kindStyle } from './FileThumbnail'\nimport { formatDateTime, formatFileSize, formatRelativeTime } from './format'\nimport { Icon } from './icons'\nimport type { ResolvedDeleteAction } from './types'\n\nexport interface FileListProps extends Omit<FileViewProps, 'tileClassName'> {\n /** Paths are shown relative to this folder. */\n cwd: string\n onToggleAll: () => void\n onDownload: (entry: FileEntry) => void\n onDelete: (entry: FileEntry) => void\n deleteActionFor: (entry: FileEntry) => ResolvedDeleteAction\n rowClassName?: string\n}\n\nexport function FileList({\n entries,\n selected,\n cwd,\n onActivate,\n onToggle,\n onToggleAll,\n onDownload,\n onDelete,\n deleteActionFor,\n className,\n rowClassName,\n}: FileListProps) {\n const selectAllRef = useRef<HTMLInputElement>(null)\n const selectedHere = entries.filter((e) => selected.has(e.file.path)).length\n const allSelected = entries.length > 0 && selectedHere === entries.length\n\n useEffect(() => {\n if (selectAllRef.current) selectAllRef.current.indeterminate = selectedHere > 0 && !allSelected\n }, [selectedHere, allSelected])\n\n const prefix = cwd ? `${cwd}/` : ''\n\n return (\n <div className=\"seed-fm-list-wrap\">\n <table className={['seed-fm-list', className].filter(Boolean).join(' ')}>\n <thead>\n <tr>\n <th style={{ width: 28 }}>\n <input\n ref={selectAllRef}\n type=\"checkbox\"\n className=\"seed-fm-checkbox\"\n checked={allSelected}\n onChange={onToggleAll}\n aria-label=\"Select all\"\n />\n </th>\n <th>Name</th>\n <th className=\"seed-fm-col-type\">Type</th>\n <th className=\"seed-fm-num seed-fm-col-size\">Size</th>\n <th className=\"seed-fm-col-modified\">Modified</th>\n <th>\n <span className=\"seed-fm-visually-hidden\">Actions</span>\n </th>\n </tr>\n </thead>\n <tbody>\n {entries.map((entry, index) => {\n const { file } = entry\n const isSelected = selected.has(file.path)\n const deleteAction = deleteActionFor(entry)\n return (\n <tr\n key={file.path}\n data-path={file.path}\n aria-selected={isSelected}\n className={['seed-fm-row', rowClassName].filter(Boolean).join(' ')}\n onClick={(e) => onActivate(entry, index, e)}\n >\n <td onClick={(e) => e.stopPropagation()}>\n <input\n type=\"checkbox\"\n className=\"seed-fm-checkbox\"\n checked={isSelected}\n onChange={() => {}}\n onClick={(e) => onToggle(entry, index, e.shiftKey)}\n aria-label={`Select ${file.name}`}\n />\n </td>\n <td>\n <span className=\"seed-fm-namecell\">\n <FileThumbnail entry={entry} size=\"mini\" />\n <span>\n <button\n type=\"button\"\n className=\"seed-fm-row-name seed-fm-row-open\"\n onClick={(e) => {\n e.stopPropagation()\n onActivate(entry, index, e)\n }}\n >\n {file.name}\n </button>\n {/* Search results can come from subfolders; show where. */}\n {file.path.slice(prefix.length) !== file.name && (\n <span className=\"seed-fm-row-path\">{file.path.slice(prefix.length)}</span>\n )}\n </span>\n </span>\n </td>\n <td className=\"seed-fm-col-type\">\n <span className=\"seed-fm-type\" style={kindStyle(fileKind(file))}>\n {effectiveType(file)}\n {file.detectedType && <small>detected</small>}\n </span>\n </td>\n <td className=\"seed-fm-num seed-fm-col-size\" title={`${file.size.toLocaleString()} bytes`}>\n {formatFileSize(entry.totalSize)}\n </td>\n <td className=\"seed-fm-when seed-fm-col-modified\" title={formatDateTime(file.lastModified)}>\n {formatRelativeTime(file.lastModified)}\n </td>\n <td className=\"seed-fm-actions\" onClick={(e) => e.stopPropagation()}>\n <button\n type=\"button\"\n className=\"seed-fm-btn seed-fm-btn--ghost seed-fm-btn--icon\"\n aria-label={`Download ${file.name}`}\n title=\"Download\"\n onClick={() => onDownload(entry)}\n >\n <Icon name=\"download\" size={15} />\n </button>\n <button\n type=\"button\"\n className=\"seed-fm-btn seed-fm-btn--ghost seed-fm-btn--icon\"\n aria-label={deleteAction.label ? `${deleteAction.label}: ${file.name}` : `Delete ${file.name}`}\n title={deleteAction.label ?? 'Delete'}\n onClick={() => onDelete(entry)}\n >\n <Icon name=\"trash\" size={15} />\n </button>\n </td>\n </tr>\n )\n })}\n </tbody>\n </table>\n </div>\n )\n}\n","import React, { useEffect, useRef, useState } from 'react'\nimport { getOPFSFile } from '../opfsPaths'\nimport { effectiveType, fileKind, type FileEntry } from './fileModel'\nimport { KIND_META, kindStyle } from './FileThumbnail'\nimport { formatDateTime, formatFileSize, formatRelativeTime } from './format'\nimport { Icon } from './icons'\nimport type { ResolvedDeleteAction } from './types'\n\n/** Text files larger than this aren't previewed. */\nconst MAX_TEXT_PREVIEW_FILE = 1024 * 1024\n/** Only this many bytes of a text file are shown. */\nconst TEXT_PREVIEW_BYTES = 32 * 1024\n\ntype Preview =\n | { kind: 'loading' }\n | { kind: 'image'; url: string }\n | { kind: 'text'; text: string; truncated: boolean }\n | { kind: 'none'; message: string }\n\nfunction usePreview(entry: FileEntry): Preview {\n const [preview, setPreview] = useState<Preview>({ kind: 'loading' })\n const { path, lastModified } = entry.file\n const kind = fileKind(entry.file)\n const size = entry.file.size\n\n useEffect(() => {\n let cancelled = false\n let url: string | null = null\n setPreview({ kind: 'loading' })\n\n const load = async (): Promise<Preview> => {\n if (kind === 'image') {\n url = URL.createObjectURL(await getOPFSFile(path))\n return { kind: 'image', url }\n }\n if (kind === 'json' || kind === 'html' || kind === 'text') {\n if (size > MAX_TEXT_PREVIEW_FILE) return { kind: 'none', message: 'This file is too large to preview.' }\n const file = await getOPFSFile(path)\n const truncated = file.size > TEXT_PREVIEW_BYTES\n let text = await file.slice(0, TEXT_PREVIEW_BYTES).text()\n if (kind === 'json' && !truncated) {\n try {\n text = JSON.stringify(JSON.parse(text), null, 2)\n } catch {\n // Show the raw text when it isn't valid JSON.\n }\n }\n return { kind: 'text', text, truncated }\n }\n if (kind === 'database') {\n return { kind: 'none', message: 'SQLite database. Download it to open it in a database tool.' }\n }\n return { kind: 'none', message: 'No preview for this file type.' }\n }\n\n load().then(\n (p) => {\n if (!cancelled) setPreview(p)\n },\n (err) => {\n if (!cancelled) setPreview({ kind: 'none', message: `Couldn’t read the file: ${err instanceof Error ? err.message : String(err)}` })\n },\n )\n return () => {\n cancelled = true\n if (url) URL.revokeObjectURL(url)\n }\n }, [path, lastModified, size, kind])\n\n return preview\n}\n\nexport interface PreviewPanelProps {\n entry: FileEntry\n onClose: () => void\n onPrevious: (() => void) | null\n onNext: (() => void) | null\n onDownload: () => void\n onDelete: () => void\n deleteAction: ResolvedDeleteAction\n onCopyPath: () => void\n className?: string\n}\n\nexport function PreviewPanel({\n entry,\n onClose,\n onPrevious,\n onNext,\n onDownload,\n onDelete,\n deleteAction,\n onCopyPath,\n className,\n}: PreviewPanelProps) {\n const { file, variants } = entry\n const kind = fileKind(file)\n const preview = usePreview(entry)\n const [dimensions, setDimensions] = useState<{ width: number; height: number } | null>(null)\n const [imageFailed, setImageFailed] = useState(false)\n const closeRef = useRef<HTMLButtonElement>(null)\n\n useEffect(() => {\n setDimensions(null)\n setImageFailed(false)\n }, [file.path])\n\n useEffect(() => {\n closeRef.current?.focus()\n }, [])\n\n const headingId = `seed-fm-panel-${encodeURIComponent(file.path)}`\n const variantsSize = variants.reduce((n, v) => n + v.file.size, 0)\n\n let previewContent: React.ReactNode\n if (preview.kind === 'image' && !imageFailed) {\n previewContent = (\n <img\n src={preview.url}\n alt={`Preview of ${file.name}`}\n onLoad={(e) => setDimensions({ width: e.currentTarget.naturalWidth, height: e.currentTarget.naturalHeight })}\n onError={() => setImageFailed(true)}\n />\n )\n } else if (preview.kind === 'text') {\n previewContent = (\n <pre>\n {preview.text}\n {preview.truncated && '\\n…'}\n </pre>\n )\n } else if (preview.kind === 'loading') {\n previewContent = null\n } else {\n const message =\n preview.kind === 'none'\n ? preview.message\n : 'The browser can’t decode this image. The file may be corrupt or not an image.'\n previewContent = (\n <span className=\"seed-fm-preview-note\">\n <Icon name={imageFailed ? 'imageOff' : KIND_META[kind].icon} size={32} strokeWidth={1.5} />\n {message}\n </span>\n )\n }\n\n const checker = preview.kind === 'image' && !imageFailed && /png|webp|gif|avif|svg/.test(effectiveType(file))\n\n return (\n <aside\n className={['seed-fm-panel', className].filter(Boolean).join(' ')}\n role=\"dialog\"\n aria-modal=\"false\"\n aria-labelledby={headingId}\n >\n <div className=\"seed-fm-panel-head\">\n <h3 id={headingId} title={file.name}>\n {file.name}\n </h3>\n <button\n type=\"button\"\n className=\"seed-fm-btn seed-fm-btn--ghost seed-fm-btn--icon\"\n aria-label=\"Previous file\"\n disabled={!onPrevious}\n onClick={onPrevious ?? undefined}\n >\n <Icon name=\"chevronLeft\" />\n </button>\n <button\n type=\"button\"\n className=\"seed-fm-btn seed-fm-btn--ghost seed-fm-btn--icon\"\n aria-label=\"Next file\"\n disabled={!onNext}\n onClick={onNext ?? undefined}\n >\n <Icon name=\"chevronRight\" />\n </button>\n <button\n ref={closeRef}\n type=\"button\"\n className=\"seed-fm-btn seed-fm-btn--ghost seed-fm-btn--icon\"\n aria-label=\"Close details\"\n onClick={onClose}\n >\n <Icon name=\"x\" />\n </button>\n </div>\n\n <div className=\"seed-fm-panel-body\">\n <div\n className={['seed-fm-preview', checker ? 'seed-fm-thumb--checker' : '', preview.kind === 'loading' ? 'seed-fm-skeleton' : '']\n .filter(Boolean)\n .join(' ')}\n style={kindStyle(kind)}\n >\n {previewContent}\n </div>\n\n <dl className=\"seed-fm-details\">\n <dt>Path</dt>\n <dd>\n <span className=\"seed-fm-path\">\n <code>{file.path}</code>\n <button\n type=\"button\"\n className=\"seed-fm-btn seed-fm-btn--ghost seed-fm-btn--icon\"\n aria-label=\"Copy path\"\n title=\"Copy path\"\n onClick={onCopyPath}\n >\n <Icon name=\"copy\" size={13} />\n </button>\n </span>\n </dd>\n <dt>Size</dt>\n <dd>\n {formatFileSize(file.size)} <small>{file.size.toLocaleString()} bytes</small>\n </dd>\n <dt>Type</dt>\n <dd>\n {effectiveType(file)}\n {file.detectedType && <small> from file contents. File.type is “{file.type}”</small>}\n </dd>\n {dimensions && (\n <>\n <dt>Dimensions</dt>\n <dd>\n {dimensions.width.toLocaleString()} × {dimensions.height.toLocaleString()} px\n </dd>\n </>\n )}\n <dt>Modified</dt>\n <dd>\n {formatDateTime(file.lastModified)} <small>{formatRelativeTime(file.lastModified)}</small>\n </dd>\n </dl>\n\n {variants.length > 0 && (\n <div>\n <div className=\"seed-fm-section-label\">Resized copies · {formatFileSize(variantsSize)}</div>\n <ul className=\"seed-fm-variants\">\n {variants.map((v) => (\n <li key={v.file.path} title={v.file.path}>\n <span>{v.width}w · webp</span>\n <span>{formatFileSize(v.file.size)}</span>\n </li>\n ))}\n </ul>\n </div>\n )}\n </div>\n\n <div className=\"seed-fm-panel-foot\">\n <button type=\"button\" className=\"seed-fm-btn seed-fm-btn--primary\" onClick={onDownload}>\n <Icon name=\"download\" size={15} /> Download\n </button>\n <button\n type=\"button\"\n className={`seed-fm-btn${deleteAction.destructive ? ' seed-fm-btn--danger-text' : ''}`}\n onClick={onDelete}\n >\n <Icon name=\"trash\" size={15} /> {deleteAction.label ?? 'Delete'}\n </button>\n </div>\n </aside>\n )\n}\n","/**\n * Scoped stylesheet for OPFSFilesManager. Injected once into document.head (see\n * docs/adr/0002-component-styling-tokens-and-injected-css.md). Every color comes from a\n * `--seed-*` custom property, so hosts restyle by overriding variables on `.seed-fm`\n * or an ancestor.\n */\n\nexport const STYLE_ELEMENT_ID = 'seed-opfs-files-manager-styles'\nexport const STYLE_LAYER = 'seed-opfs-files-manager'\n\nconst LIGHT_TOKENS = `\n --seed-bg: #fbfcfb;\n --seed-surface: #ffffff;\n --seed-sunken: #f0f3f1;\n --seed-hover: #e9eeeb;\n --seed-fg: #18211d;\n --seed-fg-muted: #5a6861;\n --seed-fg-faint: #8a978f;\n --seed-border: #dfe5e1;\n --seed-border-strong: #c5cfc9;\n --seed-accent: #2c6b59;\n --seed-accent-fg: #ffffff;\n --seed-accent-soft: #e0eee8;\n --seed-danger: #b4332a;\n --seed-danger-fg: #ffffff;\n --seed-danger-soft: #fbe7e4;\n --seed-danger-inverse: #ff9a8f;\n --seed-warn: #8a5208;\n --seed-warn-soft: #fdf1dc;\n --seed-kind-image: #2f7d68;\n --seed-kind-database: #6a4fb3;\n --seed-kind-json: #a2620e;\n --seed-kind-html: #b0413a;\n --seed-kind-text: #3f6f9a;\n --seed-kind-other: #66736c;\n --seed-checker-a: #eef1ef;\n --seed-checker-b: #ffffff;\n --seed-shadow: 0 10px 30px rgb(16 24 20 / 0.14), 0 2px 6px rgb(16 24 20 / 0.08);\n --seed-backdrop: rgb(8 12 10 / 0.42);\n`\n\nconst DARK_TOKENS = `\n --seed-bg: #111816;\n --seed-surface: #171f1c;\n --seed-sunken: #1d2723;\n --seed-hover: #24302b;\n --seed-fg: #e4ebe7;\n --seed-fg-muted: #9daba3;\n --seed-fg-faint: #6f7d76;\n --seed-border: #29342f;\n --seed-border-strong: #3a4842;\n --seed-accent: #6cc0a3;\n --seed-accent-fg: #0c1a15;\n --seed-accent-soft: #1c3a30;\n --seed-danger: #f08277;\n --seed-danger-fg: #2a0d0a;\n --seed-danger-soft: #3b1c19;\n --seed-danger-inverse: #b4332a;\n --seed-warn: #e9b062;\n --seed-warn-soft: #382a14;\n --seed-kind-image: #6cc0a3;\n --seed-kind-database: #b29cf0;\n --seed-kind-json: #e3ad5c;\n --seed-kind-html: #ef8a80;\n --seed-kind-text: #8fb8de;\n --seed-kind-other: #9aa8a0;\n --seed-checker-a: #1d2723;\n --seed-checker-b: #24302b;\n --seed-shadow: 0 12px 34px rgb(0 0 0 / 0.5), 0 2px 6px rgb(0 0 0 / 0.4);\n --seed-backdrop: rgb(0 0 0 / 0.55);\n color-scheme: dark;\n`\n\nconst CHECKER = `background-color: var(--seed-checker-b);\n background-image: conic-gradient(var(--seed-checker-a) 25%, transparent 0 50%, var(--seed-checker-a) 0 75%, transparent 0);`\n\nconst CSS = `\n.seed-fm {\n ${LIGHT_TOKENS}\n --seed-radius-sm: 6px;\n --seed-radius-md: 8px;\n --seed-radius-lg: 12px;\n --seed-font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n color-scheme: light;\n}\n.seed-fm[data-seed-theme=\"dark\"] { ${DARK_TOKENS} }\n@media (prefers-color-scheme: dark) {\n .seed-fm[data-seed-theme=\"system\"] { ${DARK_TOKENS} }\n}\n\n.seed-fm { position: relative; color: var(--seed-fg); background: var(--seed-bg); font-family: inherit; font-size: 14px; line-height: 1.45; }\n.seed-fm *, .seed-fm *::before, .seed-fm *::after { box-sizing: border-box; }\n/* Element defaults use :where() so they have zero specificity and any component class beats them. */\n:where(.seed-fm) :where(button, input, select) { font: inherit; color: inherit; }\n.seed-fm :focus-visible { outline: 2px solid var(--seed-accent); outline-offset: 2px; }\n:where(.seed-fm) :where(svg) { flex: none; display: inline-block; vertical-align: middle; }\n.seed-fm-frame { container: seed-fm / inline-size; }\n\n.seed-fm-header { display: flex; flex-wrap: wrap; gap: 12px 20px; align-items: flex-start; padding: 18px 20px 14px; }\n.seed-fm-titles { flex: 1 1 240px; min-width: 0; }\n.seed-fm-title { font-size: 20px; line-height: 1.25; font-weight: 600; margin: 0; letter-spacing: -0.01em; }\n.seed-fm-description { margin: 2px 0 0; color: var(--seed-fg-muted); font-size: 13px; }\n.seed-fm-usage { flex: 0 1 280px; min-width: 200px; font-size: 12px; color: var(--seed-fg-muted); }\n.seed-fm-usage-row { display: flex; justify-content: space-between; gap: 8px; margin-bottom: 5px; font-variant-numeric: tabular-nums; }\n.seed-fm-usage-row strong { color: var(--seed-fg); font-weight: 600; }\n.seed-fm-meter { height: 6px; border-radius: 99px; background: var(--seed-sunken); overflow: hidden; display: flex; }\n.seed-fm-meter > span { display: block; height: 100%; }\n.seed-fm-legend { display: flex; flex-wrap: wrap; gap: 4px 10px; margin: 6px 0 0; padding: 0; list-style: none; }\n.seed-fm-legend li { display: inline-flex; align-items: center; gap: 5px; font-variant-numeric: tabular-nums; }\n.seed-fm-legend li::before { content: \"\"; width: 7px; height: 7px; border-radius: 2px; background: var(--seed-swatch); }\n\n.seed-fm-btn { display: inline-flex; align-items: center; justify-content: center; gap: 6px; height: 32px; padding: 0 12px; margin: 0; border-radius: var(--seed-radius-md); border: 1px solid var(--seed-border); background: var(--seed-surface); color: var(--seed-fg); cursor: pointer; font-size: 13px; font-weight: 500; white-space: nowrap; text-decoration: none; }\n.seed-fm-btn:hover:not(:disabled) { background: var(--seed-hover); }\n.seed-fm-btn:disabled { opacity: 0.45; cursor: default; }\n.seed-fm-btn--icon { width: 32px; padding: 0; }\n.seed-fm-btn--primary { background: var(--seed-accent); border-color: var(--seed-accent); color: var(--seed-accent-fg); }\n.seed-fm-btn--primary:hover:not(:disabled) { background: var(--seed-accent); filter: brightness(1.08); }\n.seed-fm-btn--danger { background: var(--seed-danger); border-color: var(--seed-danger); color: var(--seed-danger-fg); }\n.seed-fm-btn--danger:hover:not(:disabled) { background: var(--seed-danger); filter: brightness(1.08); }\n.seed-fm-btn--ghost { border-color: transparent; background: transparent; }\n.seed-fm-btn--danger-text { color: var(--seed-danger); }\n.seed-fm-btn[data-busy=\"true\"] svg { animation: seed-fm-spin 0.8s linear infinite; }\n@keyframes seed-fm-spin { to { transform: rotate(360deg); } }\n\n.seed-fm-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 10px; padding: 0 20px 12px; border-bottom: 1px solid var(--seed-border); }\n.seed-fm-crumbs { display: flex; align-items: center; gap: 2px; min-width: 0; flex: 1 1 auto; font-size: 13px; overflow: hidden; margin: 0; padding: 0; list-style: none; }\n.seed-fm-crumbs li { display: inline-flex; align-items: center; min-width: 0; }\n.seed-fm-crumbs button { border: 0; background: none; padding: 4px 6px; border-radius: var(--seed-radius-sm); cursor: pointer; color: var(--seed-fg-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 24ch; }\n.seed-fm-crumbs button:hover { background: var(--seed-hover); color: var(--seed-fg); }\n.seed-fm-crumbs button[aria-current=\"page\"] { color: var(--seed-fg); font-weight: 600; }\n.seed-fm-crumbs svg { color: var(--seed-fg-faint); }\n.seed-fm-search { position: relative; flex: 0 1 220px; min-width: 140px; display: block; }\n.seed-fm-search svg { position: absolute; left: 9px; top: 50%; transform: translateY(-50%); color: var(--seed-fg-faint); pointer-events: none; }\n.seed-fm-search input { width: 100%; height: 32px; margin: 0; padding: 0 10px 0 30px; border-radius: var(--seed-radius-md); border: 1px solid var(--seed-border); background: var(--seed-surface); color: var(--seed-fg); font-size: 13px; }\n.seed-fm-search input::placeholder { color: var(--seed-fg-faint); }\n.seed-fm-select { height: 32px; margin: 0; border-radius: var(--seed-radius-md); border: 1px solid var(--seed-border); background: var(--seed-surface); color: var(--seed-fg); font-size: 13px; padding: 0 8px; }\n.seed-fm-viewtoggle { display: inline-flex; border: 1px solid var(--seed-border); border-radius: var(--seed-radius-md); overflow: hidden; }\n.seed-fm-viewtoggle button { width: 32px; height: 30px; margin: 0; border: 0; background: var(--seed-surface); cursor: pointer; color: var(--seed-fg-muted); display: inline-flex; align-items: center; justify-content: center; }\n.seed-fm-viewtoggle button[aria-pressed=\"true\"] { background: var(--seed-sunken); color: var(--seed-fg); }\n.seed-fm-hint { width: 100%; font-size: 12px; color: var(--seed-fg-faint); margin: 0; }\n.seed-fm-hint svg { margin-right: 6px; vertical-align: -2px; }\n.seed-fm-hint .seed-fm-linkbtn { margin-left: 6px; }\n.seed-fm-linkbtn { border: 0; background: none; color: var(--seed-accent); cursor: pointer; padding: 0; font-size: inherit; text-decoration: underline; text-underline-offset: 2px; }\n\n.seed-fm-body { padding: 16px 20px 24px; }\n.seed-fm-section-label { font-size: 11.5px; font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase; color: var(--seed-fg-faint); margin: 4px 0 8px; }\n.seed-fm-folders { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 8px; margin: 0 0 20px; padding: 0; list-style: none; }\n.seed-fm-folder { width: 100%; display: flex; align-items: center; gap: 10px; padding: 10px 12px; margin: 0; border: 1px solid var(--seed-border); border-radius: var(--seed-radius-md); background: var(--seed-surface); cursor: pointer; text-align: left; min-width: 0; }\n.seed-fm-folder:hover { background: var(--seed-hover); }\n.seed-fm-folder > svg { color: var(--seed-fg-muted); }\n.seed-fm-folder > span { min-width: 0; display: flex; flex-direction: column; }\n.seed-fm-folder-name { font-weight: 600; font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.seed-fm-folder-meta { font-size: 11.5px; color: var(--seed-fg-muted); font-variant-numeric: tabular-nums; }\n.seed-fm-folders li { position: relative; }\n.seed-fm-folders li .seed-fm-folder { padding-right: 40px; }\n.seed-fm-folder-delete.seed-fm-btn { position: absolute; top: 50%; right: 6px; transform: translateY(-50%); width: 28px; height: 28px; color: var(--seed-fg-muted); opacity: 0; }\n.seed-fm-folders li:hover .seed-fm-folder-delete, .seed-fm-folder-delete.seed-fm-btn:focus-visible { opacity: 1; }\n.seed-fm-folder-delete.seed-fm-btn:hover:not(:disabled) { color: var(--seed-danger); }\n@media (hover: none) { .seed-fm-folder-delete.seed-fm-btn { opacity: 1; } }\n\n.seed-fm-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(156px, 1fr)); gap: 14px; margin: 0; padding: 0; list-style: none; }\n.seed-fm-tile { position: relative; border-radius: var(--seed-radius-lg); cursor: pointer; min-width: 0; outline: none; }\n.seed-fm-thumb { position: relative; aspect-ratio: 4 / 3; border-radius: var(--seed-radius-lg); overflow: hidden; background: var(--seed-sunken); border: 1px solid var(--seed-border); display: flex; align-items: center; justify-content: center; color: var(--seed-kind); }\n.seed-fm-thumb--checker { ${CHECKER} background-size: 14px 14px; }\n.seed-fm-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform 0.12s ease; }\n.seed-fm-thumb--checker img { object-fit: contain; }\n.seed-fm-tile:hover .seed-fm-thumb { border-color: var(--seed-border-strong); }\n.seed-fm-tile:focus-visible .seed-fm-thumb { outline: 2px solid var(--seed-accent); outline-offset: 2px; }\n.seed-fm-tile[aria-selected=\"true\"] .seed-fm-thumb { border-color: var(--seed-accent); box-shadow: 0 0 0 2px var(--seed-accent); }\n.seed-fm-tile[aria-selected=\"true\"] .seed-fm-thumb img { transform: scale(0.94); border-radius: 6px; }\n.seed-fm-kind { display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 8px; text-align: center; }\n.seed-fm-ext { font-family: var(--seed-font-mono); font-size: 10.5px; font-weight: 500; letter-spacing: 0.04em; text-transform: uppercase; padding: 1px 6px; border-radius: 4px; background: color-mix(in srgb, var(--seed-kind) 14%, transparent); }\n.seed-fm-kind-error { font-size: 11px; color: var(--seed-fg-faint); }\n.seed-fm-skeleton { background: linear-gradient(90deg, var(--seed-sunken), var(--seed-hover), var(--seed-sunken)); background-size: 200% 100%; animation: seed-fm-shimmer 1.3s ease-in-out infinite; border-color: transparent; }\n@keyframes seed-fm-shimmer { from { background-position: 100% 0; } to { background-position: -100% 0; } }\n.seed-fm-check { position: absolute; top: 8px; left: 8px; width: 22px; height: 22px; margin: 0; border-radius: 6px; border: 1.5px solid rgb(255 255 255 / 0.9); background: rgb(0 0 0 / 0.28); color: #fff; display: flex; align-items: center; justify-content: center; opacity: 0; cursor: pointer; padding: 0; transition: opacity 0.1s; z-index: 1; }\n.seed-fm-tile:hover .seed-fm-check, .seed-fm-tile:focus-visible .seed-fm-check, .seed-fm-check:focus-visible, .seed-fm[data-selecting=\"true\"] .seed-fm-check, .seed-fm-tile[aria-selected=\"true\"] .seed-fm-check { opacity: 1; }\n.seed-fm-tile[aria-selected=\"true\"] .seed-fm-check { background: var(--seed-accent); border-color: var(--seed-accent); color: var(--seed-accent-fg); }\n.seed-fm-badge { position: absolute; bottom: 7px; right: 7px; font-size: 10.5px; font-weight: 600; padding: 2px 6px 2px 5px; border-radius: 5px; background: rgb(10 16 13 / 0.66); color: #fff; display: inline-flex; gap: 4px; align-items: center; }\n.seed-fm-tile-meta { display: block; padding: 7px 2px 0; min-width: 0; }\n.seed-fm-tile-name { display: block; font-size: 13px; font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.seed-fm-tile-sub { display: block; font-size: 11.5px; color: var(--seed-fg-muted); font-variant-numeric: tabular-nums; }\n\n.seed-fm-list-wrap { overflow-x: auto; }\n.seed-fm-list { width: 100%; border-collapse: collapse; font-size: 13px; }\n.seed-fm-list th { text-align: left; font-size: 11.5px; font-weight: 600; color: var(--seed-fg-muted); padding: 6px 10px; border-bottom: 1px solid var(--seed-border); white-space: nowrap; background: none; }\n.seed-fm-list td { padding: 7px 10px; border-bottom: 1px solid var(--seed-border); vertical-align: middle; }\n.seed-fm-row { cursor: pointer; }\n.seed-fm-row:hover td { background: var(--seed-hover); }\n.seed-fm-row[aria-selected=\"true\"] td { background: var(--seed-accent-soft); }\n.seed-fm-num { text-align: right !important; font-variant-numeric: tabular-nums; white-space: nowrap; color: var(--seed-fg-muted); }\n.seed-fm-when { white-space: nowrap; color: var(--seed-fg-muted); }\n.seed-fm-namecell { display: flex; align-items: center; gap: 10px; min-width: 0; }\n.seed-fm-namecell > span { min-width: 0; display: flex; flex-direction: column; }\n.seed-fm-mini { width: 36px; height: 28px; border-radius: 5px; overflow: hidden; flex: none; border: 1px solid var(--seed-border); background: var(--seed-sunken); display: flex; align-items: center; justify-content: center; color: var(--seed-kind); }\n.seed-fm-mini img { width: 100%; height: 100%; object-fit: cover; display: block; }\n.seed-fm-mini.seed-fm-thumb--checker { background-size: 8px 8px; }\n.seed-fm-mini.seed-fm-thumb--checker img { object-fit: contain; }\n.seed-fm-row-name { font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 40ch; }\n.seed-fm-row-open { display: block; border: 0; padding: 0; margin: 0; background: none; color: inherit; cursor: pointer; text-align: left; }\n.seed-fm-visually-hidden { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }\n.seed-fm-row-path { display: block; font-family: var(--seed-font-mono); font-size: 11px; color: var(--seed-fg-faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 52ch; }\n.seed-fm-actions { white-space: nowrap; text-align: right; }\n.seed-fm-actions .seed-fm-btn { opacity: 0; height: 28px; width: 28px; }\n.seed-fm-row:hover .seed-fm-actions .seed-fm-btn, .seed-fm-actions .seed-fm-btn:focus-visible { opacity: 1; }\n.seed-fm-checkbox { width: 16px; height: 16px; margin: 0; accent-color: var(--seed-accent); cursor: pointer; vertical-align: middle; }\n.seed-fm-type { display: inline-flex; align-items: center; gap: 5px; font-size: 11.5px; color: var(--seed-fg-muted); white-space: nowrap; }\n.seed-fm-type::before { content: \"\"; width: 7px; height: 7px; border-radius: 2px; background: var(--seed-kind); }\n.seed-fm-type small { color: var(--seed-fg-faint); font-size: inherit; }\n@container seed-fm (max-width: 720px) {\n .seed-fm-col-type, .seed-fm-col-modified { display: none; }\n .seed-fm-row-path { max-width: 28ch; }\n}\n@container seed-fm (max-width: 520px) {\n .seed-fm-col-size { display: none; }\n .seed-fm-header { padding: 14px 14px 10px; }\n .seed-fm-toolbar { padding: 0 14px 10px; }\n .seed-fm-body { padding: 12px 14px 20px; }\n .seed-fm-grid { grid-template-columns: repeat(auto-fill, minmax(128px, 1fr)); gap: 10px; }\n .seed-fm-usage { flex-basis: 100%; order: 3; }\n .seed-fm-search { flex: 1 1 100%; }\n}\n\n.seed-fm-state { max-width: 400px; margin: 40px auto; text-align: center; display: flex; flex-direction: column; align-items: center; gap: 8px; }\n.seed-fm-state-icon { width: 48px; height: 48px; border-radius: 12px; background: var(--seed-sunken); display: flex; align-items: center; justify-content: center; color: var(--seed-fg-muted); margin-bottom: 4px; }\n.seed-fm-state--error .seed-fm-state-icon { background: var(--seed-danger-soft); color: var(--seed-danger); }\n.seed-fm-state h3, .seed-fm-state p { margin: 0; }\n.seed-fm-state h3 { font-size: 15px; font-weight: 600; }\n.seed-fm-state p { color: var(--seed-fg-muted); font-size: 13px; }\n.seed-fm-state .seed-fm-btn { margin-top: 8px; }\n:where(.seed-fm) :where(code) { font-family: var(--seed-font-mono); font-size: 0.92em; }\n.seed-fm-state code { background: var(--seed-sunken); padding: 1px 5px; border-radius: 4px; }\n\n.seed-fm-batch { position: sticky; bottom: 16px; z-index: 5; width: fit-content; max-width: calc(100% - 24px); margin: 0 auto 16px; display: flex; align-items: center; flex-wrap: wrap; justify-content: center; gap: 6px; padding: 6px 6px 6px 14px; background: var(--seed-fg); color: var(--seed-bg); border-radius: 12px; box-shadow: var(--seed-shadow); }\n.seed-fm-batch-count { font-size: 13px; font-weight: 600; margin-right: 6px; font-variant-numeric: tabular-nums; white-space: nowrap; }\n.seed-fm-batch-count span { font-weight: 400; opacity: 0.7; }\n.seed-fm-batch .seed-fm-btn { background: transparent; border-color: transparent; color: var(--seed-bg); height: 30px; }\n.seed-fm-batch .seed-fm-btn:hover:not(:disabled) { background: color-mix(in srgb, var(--seed-bg) 14%, transparent); }\n.seed-fm-batch .seed-fm-btn--danger-text { color: var(--seed-danger-inverse); }\n\n.seed-fm-panel { position: fixed; top: 0; right: 0; bottom: 0; z-index: 1000; width: min(420px, 100vw); background: var(--seed-surface); color: var(--seed-fg); border-left: 1px solid var(--seed-border); box-shadow: var(--seed-shadow); display: flex; flex-direction: column; }\n.seed-fm-panel-head { display: flex; align-items: center; gap: 4px; padding: 10px 10px 10px 16px; border-bottom: 1px solid var(--seed-border); }\n.seed-fm-panel-head h3 { flex: 1; min-width: 0; margin: 0; font-size: 14px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.seed-fm-panel-body { flex: 1; overflow: auto; padding: 16px; display: flex; flex-direction: column; gap: 16px; }\n.seed-fm-preview { border-radius: var(--seed-radius-md); overflow: hidden; border: 1px solid var(--seed-border); background: var(--seed-sunken); display: flex; align-items: center; justify-content: center; min-height: 180px; color: var(--seed-kind); }\n.seed-fm-preview.seed-fm-thumb--checker { background-size: 16px 16px; }\n.seed-fm-preview img { max-width: 100%; max-height: 320px; display: block; }\n.seed-fm-preview pre { margin: 0; padding: 12px 14px; align-self: stretch; width: 100%; font-family: var(--seed-font-mono); font-size: 11.5px; line-height: 1.55; color: var(--seed-fg); overflow: auto; max-height: 320px; white-space: pre; }\n.seed-fm-preview-note { padding: 28px; text-align: center; color: var(--seed-fg-muted); font-size: 12.5px; display: flex; flex-direction: column; align-items: center; gap: 8px; }\n.seed-fm-preview-note svg { color: var(--seed-kind); }\n.seed-fm-details { display: grid; grid-template-columns: 92px minmax(0, 1fr); gap: 8px 12px; margin: 0; font-size: 13px; }\n.seed-fm-details dt { color: var(--seed-fg-muted); }\n.seed-fm-details dd { margin: 0; min-width: 0; font-variant-numeric: tabular-nums; }\n.seed-fm-details small { color: var(--seed-fg-faint); font-size: 12px; }\n.seed-fm-path { display: flex; gap: 4px; align-items: flex-start; }\n.seed-fm-path code { font-size: 11.5px; word-break: break-all; flex: 1; padding-top: 4px; }\n.seed-fm-path .seed-fm-btn { width: 26px; height: 26px; }\n.seed-fm-variants { display: flex; flex-direction: column; margin: 0; padding: 0; list-style: none; border: 1px solid var(--seed-border); border-radius: var(--seed-radius-md); overflow: hidden; }\n.seed-fm-variants li { display: flex; justify-content: space-between; padding: 6px 10px; font-size: 12.5px; border-bottom: 1px solid var(--seed-border); font-variant-numeric: tabular-nums; }\n.seed-fm-variants li:last-child { border-bottom: 0; }\n.seed-fm-variants li span:last-child { color: var(--seed-fg-muted); }\n.seed-fm-panel-foot { display: flex; gap: 8px; padding: 12px 16px; border-top: 1px solid var(--seed-border); }\n.seed-fm-panel-foot .seed-fm-btn--primary { flex: 1; }\n\n/* margin: auto restates the UA default that centres a modal; Tailwind's preflight resets it to 0. */\n.seed-fm-dialog { margin: auto; border: 1px solid var(--seed-border); border-radius: var(--seed-radius-lg); background: var(--seed-surface); color: var(--seed-fg); box-shadow: var(--seed-shadow); width: min(460px, calc(100vw - 32px)); max-height: calc(100vh - 32px); padding: 0; }\n.seed-fm-dialog::backdrop { background: var(--seed-backdrop); }\n.seed-fm-dialog form { display: flex; flex-direction: column; gap: 12px; margin: 0; padding: 20px; }\n.seed-fm-dialog h3 { margin: 0; font-size: 16px; font-weight: 600; overflow-wrap: anywhere; }\n.seed-fm-dialog p { margin: 0; font-size: 13px; color: var(--seed-fg-muted); }\n.seed-fm-dialog ul { margin: 0; padding: 0; list-style: none; border: 1px solid var(--seed-border); border-radius: var(--seed-radius-md); max-height: 160px; overflow: auto; }\n.seed-fm-dialog li { display: flex; justify-content: space-between; gap: 12px; padding: 6px 10px; font-size: 12.5px; border-bottom: 1px solid var(--seed-border); }\n.seed-fm-dialog li:last-child { border-bottom: 0; }\n.seed-fm-dialog li code { font-size: 11.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }\n.seed-fm-dialog li span { color: var(--seed-fg-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }\n.seed-fm-dialog label { display: flex; gap: 8px; align-items: center; font-size: 13px; cursor: pointer; }\n.seed-fm-dialog-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 4px; }\n.seed-fm-warning { display: flex; gap: 10px; align-items: flex-start; padding: 10px 12px; border-radius: var(--seed-radius-md); background: var(--seed-warn-soft); color: var(--seed-warn); font-size: 12.5px; }\n.seed-fm-warning svg { margin-top: 1px; }\n.seed-fm-irreversible { display: flex; gap: 10px; align-items: flex-start; padding: 10px 12px; border-radius: var(--seed-radius-md); background: var(--seed-danger-soft); color: var(--seed-danger); font-size: 12.5px; }\n.seed-fm-irreversible svg { flex: none; margin-top: 1px; }\n.seed-fm-keepcopy { display: flex; flex-wrap: wrap; gap: 8px 12px; align-items: center; justify-content: space-between; padding: 10px 12px; border: 1px dashed var(--seed-border); border-radius: var(--seed-radius-md); font-size: 12.5px; color: var(--seed-fg-muted); }\n\n.seed-fm-toasts { position: fixed; left: 16px; bottom: 16px; z-index: 1001; display: flex; flex-direction: column; gap: 8px; pointer-events: none; margin: 0; padding: 0; list-style: none; }\n.seed-fm-toast { display: flex; align-items: center; gap: 8px; max-width: min(420px, calc(100vw - 32px)); padding: 9px 12px; background: var(--seed-surface); color: var(--seed-fg); border: 1px solid var(--seed-border); border-radius: var(--seed-radius-md); box-shadow: var(--seed-shadow); font-size: 13px; animation: seed-fm-toast-in 0.18s ease-out; }\n.seed-fm-toast svg { color: var(--seed-accent); }\n.seed-fm-toast--error svg { color: var(--seed-danger); }\n@keyframes seed-fm-toast-in { from { transform: translateY(6px); opacity: 0.4; } }\n\n@media (prefers-reduced-motion: reduce) {\n .seed-fm-skeleton, .seed-fm-btn[data-busy=\"true\"] svg, .seed-fm-toast { animation: none; }\n .seed-fm-thumb img { transition: none; }\n}\n`\n\nexport const OPFS_FILES_MANAGER_CSS = `@layer ${STYLE_LAYER} {\\n${CSS}\\n}`\n\n/** Inject the stylesheet once. No-op during SSR. */\nexport function ensureStylesInjected(): void {\n if (typeof document === 'undefined') return\n if (document.getElementById(STYLE_ELEMENT_ID)) return\n const el = document.createElement('style')\n el.id = STYLE_ELEMENT_ID\n el.textContent = OPFS_FILES_MANAGER_CSS\n document.head.appendChild(el)\n}\n","import { useEffect, useState } from 'react'\n\nexport interface StorageEstimate {\n /** Bytes this origin uses across OPFS, IndexedDB, Cache Storage, etc. */\n usage: number\n /** Bytes this origin may use. */\n quota: number\n}\n\n/**\n * Read `navigator.storage.estimate()`, re-reading whenever `revision` changes.\n * Returns null where the API is unavailable. Browsers round and pad these numbers.\n */\nexport function useStorageEstimate(revision: unknown): StorageEstimate | null {\n const [estimate, setEstimate] = useState<StorageEstimate | null>(null)\n\n useEffect(() => {\n if (typeof navigator === 'undefined' || typeof navigator.storage?.estimate !== 'function') return\n let cancelled = false\n navigator.storage.estimate().then(\n ({ usage, quota }) => {\n if (!cancelled && usage != null && quota != null) setEstimate({ usage, quota })\n },\n () => {},\n )\n return () => {\n cancelled = true\n }\n }, [revision])\n\n return estimate\n}\n","/**\n * Minimal store-only (uncompressed) ZIP writer. Used so \"download selected\" produces one\n * file instead of N downloads, which browsers block after the first. Most OPFS content\n * (JPEG, WebP, SQLite pages) doesn't compress well, so skipping deflate costs little.\n * No ZIP64: the archive must stay under 4 GB and 65,535 entries.\n */\n\nlet crcTable: Uint32Array | null = null\n\nfunction getCrcTable(): Uint32Array {\n if (crcTable) return crcTable\n crcTable = new Uint32Array(256)\n for (let n = 0; n < 256; n++) {\n let c = n\n for (let k = 0; k < 8; k++) c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1\n crcTable[n] = c >>> 0\n }\n return crcTable\n}\n\nexport function crc32(data: Uint8Array): number {\n const table = getCrcTable()\n let crc = 0xffffffff\n for (let i = 0; i < data.length; i++) crc = table[(crc ^ data[i]) & 0xff] ^ (crc >>> 8)\n return (crc ^ 0xffffffff) >>> 0\n}\n\nfunction dosDateTime(timestamp: number): { time: number; date: number } {\n const d = new Date(timestamp)\n const year = Math.max(1980, d.getFullYear())\n return {\n time: (d.getHours() << 11) | (d.getMinutes() << 5) | Math.floor(d.getSeconds() / 2),\n date: ((year - 1980) << 9) | ((d.getMonth() + 1) << 5) | d.getDate(),\n }\n}\n\nexport interface ZipInput {\n /** Path inside the archive, '/'-separated. */\n name: string\n data: Blob\n lastModified?: number\n}\n\nconst UTF8_FLAG = 0x0800\nconst MAX_UINT32 = 0xffffffff\n\nexport async function createZip(inputs: ZipInput[]): Promise<Blob> {\n if (inputs.length > 0xffff) throw new Error('Too many files for a ZIP archive (max 65,535).')\n const encoder = new TextEncoder()\n const parts: BlobPart[] = []\n const central: Uint8Array<ArrayBuffer>[] = []\n let offset = 0\n\n for (const input of inputs) {\n const name = encoder.encode(input.name)\n const bytes = new Uint8Array(await input.data.arrayBuffer())\n const crc = crc32(bytes)\n const { time, date } = dosDateTime(input.lastModified ?? Date.now())\n if (offset + bytes.length + 30 + name.length > MAX_UINT32) {\n throw new Error('Selection is too large for a ZIP archive (max 4 GB).')\n }\n\n const local = new DataView(new ArrayBuffer(30))\n local.setUint32(0, 0x04034b50, true)\n local.setUint16(4, 20, true)\n local.setUint16(6, UTF8_FLAG, true)\n local.setUint16(8, 0, true)\n local.setUint16(10, time, true)\n local.setUint16(12, date, true)\n local.setUint32(14, crc, true)\n local.setUint32(18, bytes.length, true)\n local.setUint32(22, bytes.length, true)\n local.setUint16(26, name.length, true)\n local.setUint16(28, 0, true)\n parts.push(local.buffer, name, bytes)\n\n const header = new DataView(new ArrayBuffer(46))\n header.setUint32(0, 0x02014b50, true)\n header.setUint16(4, 20, true)\n header.setUint16(6, 20, true)\n header.setUint16(8, UTF8_FLAG, true)\n header.setUint16(10, 0, true)\n header.setUint16(12, time, true)\n header.setUint16(14, date, true)\n header.setUint32(16, crc, true)\n header.setUint32(20, bytes.length, true)\n header.setUint32(24, bytes.length, true)\n header.setUint16(28, name.length, true)\n header.setUint32(42, offset, true)\n const entry = new Uint8Array(46 + name.length)\n entry.set(new Uint8Array(header.buffer), 0)\n entry.set(name, 46)\n central.push(entry)\n\n offset += 30 + name.length + bytes.length\n }\n\n const centralSize = central.reduce((n, c) => n + c.length, 0)\n const end = new DataView(new ArrayBuffer(22))\n end.setUint32(0, 0x06054b50, true)\n end.setUint16(8, inputs.length, true)\n end.setUint16(10, inputs.length, true)\n end.setUint32(12, centralSize, true)\n end.setUint32(16, offset, true)\n\n return new Blob([...parts, ...central, end.buffer], { type: 'application/zip' })\n}\n","import React, { useCallback, useEffect, useInsertionEffect, useMemo, useRef, useState } from 'react'\nimport { useOPFSFiles, type OPFSFile } from '../useOPFSFiles'\nimport { deleteOPFSEntry, getOPFSFile, isOPFSSupported, removeEmptyOPFSDirectories } from '../opfsPaths'\nimport { DeleteDialog, FolderDeleteDialog } from './DeleteDialog'\nimport { FileGrid } from './FileGrid'\nimport { FileList } from './FileList'\nimport { KIND_META } from './FileThumbnail'\nimport {\n buildEntries,\n defaultDeleteWarning,\n fileKind,\n listDirectory,\n parseImageVariant,\n sizeByKind,\n type FileEntry,\n type FileKind,\n type FolderSummary,\n type SortKey,\n} from './fileModel'\nimport { formatFileSize } from './format'\nimport { Icon } from './icons'\nimport { PreviewPanel } from './PreviewPanel'\nimport { ensureStylesInjected } from './styles'\nimport type {\n OPFSFilesManagerNotice,\n OPFSFilesManagerNotifyTone,\n OPFSFilesManagerProps,\n OPFSFilesManagerSlot,\n OPFSFilesManagerView,\n ResolvedDeleteAction,\n} from './types'\nimport { useStorageEstimate, type StorageEstimate } from './useStorageEstimate'\nimport { createZip } from './zip'\n\nconst DEFAULT_DESCRIPTION = 'Everything this app stores in the browser’s private file system (OPFS).'\nconst TOAST_MS = 4000\n\nconst errorMessage = (err: unknown) => (err instanceof Error ? err.message : String(err))\nconst plural = (n: number, one: string, many = `${one}s`) => `${n} ${n === 1 ? one : many}`\nconst normalizeDir = (path?: string) => (path ?? '').split('/').filter(Boolean).join('/')\n\nfunction triggerBrowserDownload(name: string, blob: Blob) {\n const url = URL.createObjectURL(blob)\n const a = document.createElement('a')\n a.href = url\n a.download = name\n document.body.appendChild(a)\n a.click()\n a.remove()\n setTimeout(() => URL.revokeObjectURL(url), 1000)\n}\n\ninterface Toast {\n id: number\n message: string\n tone: OPFSFilesManagerNotifyTone\n}\n\nfunction useToasts() {\n const [toasts, setToasts] = useState<Toast[]>([])\n const timers = useRef(new Set<ReturnType<typeof setTimeout>>())\n useEffect(() => {\n const pending = timers.current\n return () => pending.forEach(clearTimeout)\n }, [])\n const showToast = useCallback((message: string, tone: Toast['tone'] = 'success') => {\n const id = Date.now() + Math.random()\n setToasts((t) => [...t, { id, message, tone }])\n const timer = setTimeout(() => {\n timers.current.delete(timer)\n setToasts((t) => t.filter((x) => x.id !== id))\n }, TOAST_MS)\n timers.current.add(timer)\n }, [])\n return { toasts, showToast }\n}\n\nconst METER_KINDS: FileKind[] = ['image', 'database', 'json', 'html', 'text', 'other']\n\nfunction UsageMeter({ files, estimate }: { files: OPFSFile[]; estimate: StorageEstimate | null }) {\n const byKind = sizeByKind(files)\n const filesTotal = files.reduce((n, f) => n + f.size, 0)\n // estimate() covers the whole origin and is padded, so it can differ from the sum of file sizes.\n const otherSiteData = estimate ? Math.max(0, estimate.usage - filesTotal) : 0\n const total = filesTotal + otherSiteData\n const segments = [\n ...METER_KINDS.filter((k) => byKind[k] > 0).map((k) => ({\n label: KIND_META[k].label,\n size: byKind[k],\n color: KIND_META[k].color,\n })),\n ...(otherSiteData > 0\n ? [{ label: 'Other site data', size: otherSiteData, color: 'var(--seed-border-strong)' }]\n : []),\n ]\n\n return (\n <div className=\"seed-fm-usage\" title=\"From navigator.storage.estimate(). Browsers round these numbers.\">\n <div className=\"seed-fm-usage-row\">\n <span>\n <strong>{formatFileSize(estimate ? estimate.usage : filesTotal)}</strong>{' '}\n {estimate ? 'used by this site' : `in ${plural(files.length, 'file')}`}\n </span>\n {estimate && <span>{formatFileSize(estimate.quota)} available</span>}\n </div>\n {total > 0 && (\n <>\n <div className=\"seed-fm-meter\" role=\"img\" aria-label=\"Storage used, by file type\">\n {segments.map((s) => (\n <span key={s.label} style={{ width: `${(s.size / total) * 100}%`, background: s.color }} />\n ))}\n </div>\n <ul className=\"seed-fm-legend\">\n {segments.map((s) => (\n <li key={s.label} style={{ '--seed-swatch': s.color } as React.CSSProperties}>\n {s.label} {formatFileSize(s.size)}\n </li>\n ))}\n </ul>\n </>\n )}\n </div>\n )\n}\n\nfunction StateMessage({\n icon,\n title,\n children,\n tone,\n action,\n}: {\n icon: React.ComponentProps<typeof Icon>['name']\n title: string\n children: React.ReactNode\n tone?: 'error'\n action?: React.ReactNode\n}) {\n return (\n <div className={`seed-fm-state${tone === 'error' ? ' seed-fm-state--error' : ''}`} role={tone === 'error' ? 'alert' : undefined}>\n <span className=\"seed-fm-state-icon\">\n <Icon name={icon} size={22} />\n </span>\n <h3>{title}</h3>\n <p>{children}</p>\n {action}\n </div>\n )\n}\n\n/**\n * Browse, preview, download, and delete files stored in OPFS (the Origin Private File\n * System). Useful for debugging and for letting people manage what an app stores locally.\n *\n * @example\n * ```tsx\n * <OPFSFilesManager\n * rootPath=\"app-files\"\n * onAfterDelete={(paths) => {\n * if (paths.some(isSeedDbPath)) {\n * clearSeedDependentData()\n * window.location.reload()\n * }\n * }}\n * onDownload={async (file, blob) => {\n * if (window.electron?.downloadFile) {\n * await window.electron.downloadFile({ data: await blob.arrayBuffer(), filename: file.name })\n * }\n * }}\n * />\n * ```\n */\nexport function OPFSFilesManager({\n rootPath,\n filter,\n onBeforeDelete,\n onAfterDelete,\n onDownload,\n title = 'Files',\n description = DEFAULT_DESCRIPTION,\n headingLevel = 2,\n theme = 'system',\n className,\n classNames = {},\n defaultView,\n groupImageVariants = true,\n deleteWarning = defaultDeleteWarning,\n confirmDelete,\n deleteAction,\n onNotify,\n}: OPFSFilesManagerProps) {\n useInsertionEffect(() => {\n if (theme !== 'none') ensureStylesInjected()\n }, [theme])\n\n const rootDir = normalizeDir(rootPath)\n const rootLabel = rootDir ? rootDir.split('/').pop()! : 'OPFS'\n const cn = (slot: OPFSFilesManagerSlot, base: string) => [base, classNames[slot]].filter(Boolean).join(' ')\n\n const { files: rawFiles, isLoading, hasLoaded, error, errorName, refetch } = useOPFSFiles({ rootPath })\n const files = useMemo(() => (filter ? rawFiles.filter(filter) : rawFiles), [rawFiles, filter])\n const estimate = useStorageEstimate(rawFiles)\n\n const [grouped, setGrouped] = useState(groupImageVariants)\n useEffect(() => setGrouped(groupImageVariants), [groupImageVariants])\n const entries = useMemo(() => buildEntries(files, grouped), [files, grouped])\n\n const [cwd, setCwd] = useState(rootDir)\n useEffect(() => setCwd(rootDir), [rootDir])\n const [query, setQuery] = useState('')\n const [sort, setSort] = useState<SortKey>('name')\n const [viewChoice, setViewChoice] = useState<OPFSFilesManagerView | undefined>(defaultView)\n const [selected, setSelected] = useState<ReadonlySet<string>>(() => new Set())\n const anchorRef = useRef<number | null>(null)\n const [panelPath, setPanelPath] = useState<string | null>(null)\n const panelOpenerRef = useRef<HTMLElement | null>(null)\n const [pendingDelete, setPendingDelete] = useState<FileEntry[] | null>(null)\n const [pendingFolder, setPendingFolder] = useState<FolderSummary | null>(null)\n const [busy, setBusy] = useState(false)\n const { toasts, showToast } = useToasts()\n const notify = (message: string, tone: OPFSFilesManagerNotifyTone, notice: OPFSFilesManagerNotice) => {\n if (onNotify) onNotify(message, tone, notice)\n else showToast(message, tone)\n }\n\n const listing = useMemo(() => listDirectory(entries, cwd, { query, sort }), [entries, cwd, query, sort])\n const visible = listing.entries\n const imageShare = visible.length ? visible.filter((e) => fileKind(e.file) === 'image').length / visible.length : 0\n const view: OPFSFilesManagerView = viewChoice ?? (imageShare >= 0.5 ? 'grid' : 'list')\n\n const entriesByPath = useMemo(() => new Map(entries.map((e) => [e.file.path, e])), [entries])\n const selectedEntries = useMemo(\n () => [...selected].map((p) => entriesByPath.get(p)).filter((e): e is FileEntry => !!e),\n [selected, entriesByPath],\n )\n const panelEntry = panelPath ? entriesByPath.get(panelPath) ?? null : null\n const panelIndex = panelEntry ? visible.indexOf(panelEntry) : -1\n\n // Keep the selection across refreshes, minus anything that no longer exists.\n useEffect(() => {\n setSelected((prev) => {\n const next = new Set([...prev].filter((p) => entriesByPath.has(p)))\n return next.size === prev.size ? prev : next\n })\n }, [entriesByPath])\n\n const navigate = (dir: string) => {\n setCwd(dir)\n setQuery('')\n setSelected(new Set())\n anchorRef.current = null\n setPanelPath(null)\n }\n\n const toggle = (entry: FileEntry, index: number, range: boolean) => {\n const anchor = anchorRef.current\n setSelected((prev) => {\n const next = new Set(prev)\n if (range && anchor != null) {\n const [from, to] = anchor < index ? [anchor, index] : [index, anchor]\n for (const e of visible.slice(from, to + 1)) next.add(e.file.path)\n } else if (next.has(entry.file.path)) {\n next.delete(entry.file.path)\n } else {\n next.add(entry.file.path)\n }\n return next\n })\n if (!range || anchor == null) anchorRef.current = index\n }\n\n const openPanel = (path: string) => {\n if (!panelPath) panelOpenerRef.current = document.activeElement as HTMLElement | null\n setPanelPath(path)\n }\n\n const closePanel = () => {\n setPanelPath(null)\n panelOpenerRef.current?.focus?.()\n panelOpenerRef.current = null\n }\n\n const activate = (entry: FileEntry, index: number, event: React.MouseEvent | React.KeyboardEvent) => {\n const modified = event.shiftKey || event.metaKey || event.ctrlKey\n if (selected.size > 0 || modified) toggle(entry, index, event.shiftKey)\n else openPanel(entry.file.path)\n }\n\n const toggleAllVisible = () => {\n const allSelected = visible.every((e) => selected.has(e.file.path))\n setSelected((prev) => {\n const next = new Set(prev)\n for (const e of visible) {\n if (allSelected) next.delete(e.file.path)\n else next.add(e.file.path)\n }\n return next\n })\n }\n\n const relativeToRoot = (path: string) => (rootDir && path.startsWith(`${rootDir}/`) ? path.slice(rootDir.length + 1) : path)\n\n const saveBlob = async (file: OPFSFile, blob: Blob) => {\n if (onDownload) await onDownload(file, blob)\n else triggerBrowserDownload(file.name, blob)\n }\n\n /**\n * Save one file as-is, or several as a .zip. A folder is always zipped, with paths\n * starting at the folder's name. Resolves true once saved.\n */\n const downloadFiles = async (list: OPFSFile[], folder?: FolderSummary): Promise<boolean> => {\n if (list.length === 0) return false\n setBusy(true)\n try {\n if (list.length === 1 && !folder) {\n const [file] = list\n await saveBlob(file, await getOPFSFile(file.path))\n return true\n }\n const folderParent = folder ? folder.path.slice(0, folder.path.length - folder.name.length) : ''\n const inputs = await Promise.all(\n list.map(async (file) => ({\n name: folder ? file.path.slice(folderParent.length) : relativeToRoot(file.path),\n data: await getOPFSFile(file.path),\n lastModified: file.lastModified,\n })),\n )\n const zip = await createZip(inputs)\n const name = `${folder ? folder.name : rootLabel}-${new Date().toISOString().slice(0, 10)}.zip`\n await saveBlob({ name, path: name, size: zip.size, type: 'application/zip', lastModified: Date.now() }, zip)\n notify(`Saved ${name} with ${plural(list.length, 'file')}`, 'success', {\n kind: 'download',\n paths: list.map((f) => f.path),\n fileName: name,\n })\n return true\n } catch (err) {\n notify(`Couldn’t download: ${errorMessage(err)}`, 'error', {\n kind: 'download',\n paths: list.map((f) => f.path),\n error: errorMessage(err),\n })\n return false\n } finally {\n setBusy(false)\n }\n }\n const download = (list: FileEntry[]) => downloadFiles(list.map((e) => e.file))\n\n const filesToDelete = (list: FileEntry[], includeVariants: boolean) =>\n list.flatMap((e) => [e.file, ...(includeVariants ? e.variants.map((v) => v.file) : [])])\n\n const resolveDeleteAction = (targets: OPFSFile[]): ResolvedDeleteAction => {\n const action = deleteAction?.(targets)\n return { label: action?.label, destructive: action?.destructive !== false }\n }\n const deleteActionFor = (list: FileEntry[]) => resolveDeleteAction(filesToDelete(list, true))\n\n /**\n * Delete `targets` one by one so onBeforeDelete can keep any of them. With `folder`, then\n * removes the folders left empty; any that still hold kept, failed or hidden files stay.\n */\n const performDelete = async (targets: OPFSFile[], folder?: FolderSummary) => {\n setBusy(true)\n const deleted: string[] = []\n const failed: { path: string; error: string }[] = []\n const skipped: string[] = []\n let folderRemoved = false\n try {\n const root = await navigator.storage.getDirectory()\n for (const file of targets) {\n if (onBeforeDelete && !(await onBeforeDelete(file))) {\n skipped.push(file.path)\n continue\n }\n try {\n await deleteOPFSEntry(file.path, root)\n deleted.push(file.path)\n } catch (err) {\n failed.push({ path: file.path, error: errorMessage(err) })\n }\n }\n if (folder) {\n try {\n folderRemoved = await removeEmptyOPFSDirectories(folder.path, root)\n } catch (err) {\n failed.push({ path: folder.path, error: errorMessage(err) })\n }\n }\n } catch (err) {\n failed.push({ path: '', error: errorMessage(err) })\n }\n\n setSelected((prev) => {\n const next = new Set(prev)\n deleted.forEach((p) => next.delete(p))\n return next\n })\n if (panelPath && deleted.includes(panelPath)) setPanelPath(null)\n await refetch()\n setBusy(false)\n\n const parts = [\n folderRemoved\n ? `Deleted the ${folder!.name} folder (${plural(deleted.length, 'file')})`\n : deleted.length > 0\n ? `Deleted ${plural(deleted.length, 'file')}`\n : 'Nothing deleted',\n ]\n if (skipped.length) parts.push(`${skipped.length} kept by onBeforeDelete`)\n if (failed.length) {\n const reasons = failed.map((f) => (f.path ? `${f.path.split('/').pop()}: ${f.error}` : f.error))\n parts.push(`${failed.length} failed (${reasons.join('; ')})`)\n }\n if (folder && !folderRemoved && !failed.length) parts.push(`The ${folder.name} folder still has files, so it was kept`)\n notify(parts.join('. '), failed.length ? 'error' : 'success', {\n kind: 'delete',\n deleted,\n skipped,\n failed,\n ...(folder && { folder: folder.path, folderRemoved }),\n })\n\n if (deleted.length > 0) await onAfterDelete?.(deleted)\n }\n\n const requestDelete = async (list: FileEntry[]) => {\n if (list.length === 0) return\n if (!confirmDelete) {\n setPendingDelete(list)\n return\n }\n const targets = filesToDelete(list, true)\n if (await confirmDelete(targets)) await performDelete(targets)\n }\n\n // Raw files, not entries: grouped resized copies live in this folder too.\n const filesInFolder = (folder: FolderSummary) => files.filter((f) => f.path.startsWith(`${folder.path}/`))\n\n const requestFolderDelete = async (folder: FolderSummary) => {\n if (!confirmDelete) {\n setPendingFolder(folder)\n return\n }\n const targets = filesInFolder(folder)\n if (await confirmDelete(targets)) await performDelete(targets, folder)\n }\n\n const copyPath = (path: string) => {\n const done = () => notify('Path copied', 'success', { kind: 'copy-path', path })\n const fail = (err?: unknown) =>\n notify('Couldn’t copy. Select the path text instead.', 'error', {\n kind: 'copy-path',\n path,\n error: err === undefined ? 'Clipboard unavailable' : errorMessage(err),\n })\n try {\n navigator.clipboard.writeText(path).then(done, fail)\n } catch (err) {\n fail(err)\n }\n }\n\n const onKeyDown = (e: React.KeyboardEvent) => {\n if (pendingDelete || pendingFolder) return\n const inField = e.target instanceof HTMLInputElement || e.target instanceof HTMLSelectElement\n if (e.key === 'Escape') {\n if (panelPath) closePanel()\n else if (selected.size > 0) setSelected(new Set())\n else return\n e.preventDefault()\n return\n }\n if (panelEntry && !inField && (e.key === 'ArrowLeft' || e.key === 'ArrowRight')) {\n const next = visible[panelIndex + (e.key === 'ArrowRight' ? 1 : -1)]\n if (next) setPanelPath(next.file.path)\n e.preventDefault()\n }\n }\n\n const hasVariants = useMemo(() => files.some((f) => parseImageVariant(f.path)), [files])\n const inImagesFolder = /(^|\\/)images(\\/\\d+)?$/.test(cwd)\n const crumbs = [\n { label: rootLabel, path: rootDir },\n ...(cwd.length > rootDir.length ? cwd.slice(rootDir ? rootDir.length + 1 : 0).split('/') : []).map((name, i, all) => ({\n label: name,\n path: [rootDir, ...all.slice(0, i + 1)].filter(Boolean).join('/'),\n })),\n ]\n const Heading = `h${headingLevel}` as 'h2'\n const selectedSize = selectedEntries.reduce((n, e) => n + e.file.size, 0)\n\n let body: React.ReactNode\n if (!isOPFSSupported()) {\n body = (\n <StateMessage icon=\"triangleAlert\" tone=\"error\" title=\"This browser doesn’t support private file storage\">\n OPFS isn’t available here. Some private browsing windows and older browsers turn it off. Try a current version of\n Chrome, Edge, Firefox, or Safari in a normal window.\n </StateMessage>\n )\n } else if (error) {\n const retry = (\n <button type=\"button\" className=\"seed-fm-btn\" onClick={() => refetch()}>\n <Icon name=\"refresh\" size={15} /> Try again\n </button>\n )\n body =\n errorName === 'NotFoundError' && rootDir ? (\n <StateMessage icon=\"triangleAlert\" tone=\"error\" title={`Can’t open ${rootDir}`} action={retry}>\n The folder <code>{rootDir}</code> doesn’t exist in this browser’s private storage yet. Check the{' '}\n <code>rootPath</code> prop, or open the app once so the SDK creates it.\n </StateMessage>\n ) : (\n <StateMessage icon=\"triangleAlert\" tone=\"error\" title=\"Can’t read private storage\" action={retry}>\n {error}\n </StateMessage>\n )\n } else if (!hasLoaded) {\n body = (\n <ul className={cn('grid', 'seed-fm-grid')} aria-busy=\"true\" aria-label=\"Loading files\">\n {Array.from({ length: 8 }, (_, i) => (\n <li key={i} className=\"seed-fm-tile\">\n <span className=\"seed-fm-thumb seed-fm-skeleton\" />\n </li>\n ))}\n </ul>\n )\n } else if (entries.length === 0) {\n body = (\n <StateMessage icon=\"folderOpen\" title={`No files in ${rootLabel} yet`}>\n Files show up here when the app saves images, JSON, or HTML to this browser’s private storage, for example after\n you add an image to a post or sync content from Arweave.\n </StateMessage>\n )\n } else if (listing.folders.length === 0 && visible.length === 0) {\n body = query ? (\n <StateMessage\n icon=\"search\"\n title={`No matches for “${query}”`}\n action={\n <button type=\"button\" className=\"seed-fm-btn\" onClick={() => setQuery('')}>\n Clear search\n </button>\n }\n >\n Search covers file names and paths in {crumbs[crumbs.length - 1].label} and every folder inside it.\n </StateMessage>\n ) : (\n <StateMessage icon=\"folderOpen\" title=\"This folder is empty\">\n Everything in it has been deleted or filtered out.\n </StateMessage>\n )\n } else {\n body = (\n <>\n {listing.folders.length > 0 && (\n <>\n <div className=\"seed-fm-section-label\">Folders</div>\n <ul className=\"seed-fm-folders\">\n {listing.folders.map((folder) => (\n <li key={folder.path}>\n <button type=\"button\" className={cn('folder', 'seed-fm-folder')} onClick={() => navigate(folder.path)}>\n <Icon name=\"folder\" size={20} strokeWidth={1.75} />\n <span>\n <span className=\"seed-fm-folder-name\">{folder.name}</span>\n <span className=\"seed-fm-folder-meta\">\n {plural(folder.fileCount, 'file')} · {formatFileSize(folder.size)}\n </span>\n </span>\n </button>\n <button\n type=\"button\"\n className=\"seed-fm-btn seed-fm-btn--ghost seed-fm-btn--icon seed-fm-folder-delete\"\n aria-label={`Delete folder ${folder.name}`}\n title=\"Delete folder\"\n disabled={busy}\n onClick={() => requestFolderDelete(folder)}\n >\n <Icon name=\"trash\" size={15} />\n </button>\n </li>\n ))}\n </ul>\n </>\n )}\n {visible.length > 0 && (\n <>\n <div className=\"seed-fm-section-label\">{query ? plural(visible.length, 'result') : 'Files'}</div>\n {view === 'grid' ? (\n <FileGrid\n entries={visible}\n selected={selected}\n onActivate={activate}\n onToggle={toggle}\n className={classNames.grid}\n tileClassName={classNames.tile}\n />\n ) : (\n <FileList\n entries={visible}\n selected={selected}\n cwd={cwd}\n onActivate={activate}\n onToggle={toggle}\n onToggleAll={toggleAllVisible}\n onDownload={(e) => download([e])}\n onDelete={(e) => requestDelete([e])}\n deleteActionFor={(e) => deleteActionFor([e])}\n className={classNames.list}\n rowClassName={classNames.row}\n />\n )}\n </>\n )}\n </>\n )\n }\n\n const pendingFiles = pendingDelete ? filesToDelete(pendingDelete, true) : []\n const pendingFolderFiles = pendingFolder ? filesInFolder(pendingFolder) : []\n const batchDeleteAction = selected.size > 0 ? deleteActionFor(selectedEntries) : null\n\n return (\n <div\n className={[cn('root', 'seed-fm'), className].filter(Boolean).join(' ')}\n data-seed-theme={theme === 'none' ? undefined : theme}\n data-selecting={selected.size > 0 || undefined}\n onKeyDown={onKeyDown}\n >\n <div className=\"seed-fm-frame\">\n <header className={cn('header', 'seed-fm-header')}>\n <div className=\"seed-fm-titles\">\n <Heading className=\"seed-fm-title\">{title}</Heading>\n {description && <p className=\"seed-fm-description\">{description}</p>}\n </div>\n {hasLoaded && !error && <UsageMeter files={files} estimate={estimate} />}\n <button\n type=\"button\"\n className=\"seed-fm-btn seed-fm-btn--icon\"\n onClick={() => refetch()}\n aria-label=\"Refresh\"\n title=\"Refresh\"\n data-busy={isLoading && hasLoaded}\n >\n <Icon name=\"refresh\" size={15} />\n </button>\n </header>\n\n {hasLoaded && !error && entries.length > 0 && (\n <div className={cn('toolbar', 'seed-fm-toolbar')}>\n <nav aria-label=\"Folder\">\n <ol className=\"seed-fm-crumbs\">\n {crumbs.map((c, i) => (\n <li key={c.path || '/'}>\n {i > 0 && <Icon name=\"chevronRight\" size={14} />}\n <button\n type=\"button\"\n onClick={() => navigate(c.path)}\n aria-current={i === crumbs.length - 1 ? 'page' : undefined}\n >\n {c.label}\n </button>\n </li>\n ))}\n </ol>\n </nav>\n <label className=\"seed-fm-search\">\n <Icon name=\"search\" size={14} />\n <input\n type=\"search\"\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder=\"Search names and paths\"\n aria-label=\"Search files\"\n />\n </label>\n <select\n className=\"seed-fm-select\"\n value={sort}\n onChange={(e) => setSort(e.target.value as SortKey)}\n aria-label=\"Sort by\"\n >\n <option value=\"name\">Name</option>\n <option value=\"modified\">Recently modified</option>\n <option value=\"size\">Largest</option>\n </select>\n <div className=\"seed-fm-viewtoggle\" role=\"group\" aria-label=\"View\">\n <button type=\"button\" aria-pressed={view === 'grid'} aria-label=\"Grid view\" onClick={() => setViewChoice('grid')}>\n <Icon name=\"layoutGrid\" size={15} />\n </button>\n <button type=\"button\" aria-pressed={view === 'list'} aria-label=\"List view\" onClick={() => setViewChoice('list')}>\n <Icon name=\"list\" size={15} />\n </button>\n </div>\n {hasVariants && inImagesFolder && (\n <p className=\"seed-fm-hint\">\n <Icon name=\"layers\" size={13} />\n {grouped\n ? 'Resized copies are grouped with their original image.'\n : 'Resized copies are shown in their size folders.'}\n <button\n type=\"button\"\n className=\"seed-fm-linkbtn\"\n onClick={() => {\n setGrouped(!grouped)\n setSelected(new Set())\n }}\n >\n {grouped ? 'Show as folders' : 'Group with originals'}\n </button>\n </p>\n )}\n </div>\n )}\n\n <div className={cn('body', 'seed-fm-body')}>{body}</div>\n\n {batchDeleteAction && (\n <div className={cn('batchBar', 'seed-fm-batch')} role=\"toolbar\" aria-label=\"Selected files\">\n <span className=\"seed-fm-batch-count\" aria-live=\"polite\">\n {selected.size} selected <span>· {formatFileSize(selectedSize)}</span>\n </span>\n <button type=\"button\" className=\"seed-fm-btn\" disabled={busy} onClick={() => download(selectedEntries)}>\n <Icon name=\"download\" size={15} /> {selected.size > 1 ? 'Download .zip' : 'Download'}\n </button>\n <button\n type=\"button\"\n className={`seed-fm-btn${batchDeleteAction.destructive ? ' seed-fm-btn--danger-text' : ''}`}\n disabled={busy}\n onClick={() => requestDelete(selectedEntries)}\n >\n <Icon name=\"trash\" size={15} /> {batchDeleteAction.label ?? 'Delete'}\n </button>\n <button\n type=\"button\"\n className=\"seed-fm-btn seed-fm-btn--icon\"\n aria-label=\"Clear selection\"\n onClick={() => setSelected(new Set())}\n >\n <Icon name=\"x\" size={15} />\n </button>\n </div>\n )}\n </div>\n\n {panelEntry && (\n <PreviewPanel\n key=\"panel\"\n entry={panelEntry}\n className={classNames.panel}\n onClose={closePanel}\n onPrevious={panelIndex > 0 ? () => setPanelPath(visible[panelIndex - 1].file.path) : null}\n onNext={\n panelIndex >= 0 && panelIndex < visible.length - 1\n ? () => setPanelPath(visible[panelIndex + 1].file.path)\n : null\n }\n onDownload={() => download([panelEntry])}\n onDelete={() => requestDelete([panelEntry])}\n deleteAction={deleteActionFor([panelEntry])}\n onCopyPath={() => copyPath(panelEntry.file.path)}\n />\n )}\n\n {pendingDelete && (\n <DeleteDialog\n entries={pendingDelete}\n rootPath={rootDir}\n warning={deleteWarning(pendingFiles)}\n deleteActionFor={(includeVariants) => resolveDeleteAction(filesToDelete(pendingDelete, includeVariants))}\n className={classNames.dialog}\n onCancel={() => setPendingDelete(null)}\n onConfirm={(includeVariants) => {\n const targets = filesToDelete(pendingDelete, includeVariants)\n setPendingDelete(null)\n void performDelete(targets)\n }}\n />\n )}\n\n {pendingFolder && (\n <FolderDeleteDialog\n folder={pendingFolder}\n files={pendingFolderFiles}\n hiddenCount={rawFiles.filter((f) => f.path.startsWith(`${pendingFolder.path}/`)).length - pendingFolderFiles.length}\n warning={deleteWarning(pendingFolderFiles)}\n action={resolveDeleteAction(pendingFolderFiles)}\n busy={busy}\n className={classNames.dialog}\n onDownload={() => downloadFiles(pendingFolderFiles, pendingFolder)}\n onCancel={() => setPendingFolder(null)}\n onConfirm={() => {\n setPendingFolder(null)\n void performDelete(pendingFolderFiles, pendingFolder)\n }}\n />\n )}\n\n {!onNotify && (\n <ul className=\"seed-fm-toasts\" aria-live=\"polite\">\n {toasts.map((t) => (\n <li key={t.id} className={cn('toast', `seed-fm-toast${t.tone === 'error' ? ' seed-fm-toast--error' : ''}`)}>\n <Icon name={t.tone === 'error' ? 'triangleAlert' : 'check'} size={15} strokeWidth={2.5} />\n {t.message}\n </li>\n ))}\n </ul>\n )}\n </div>\n )\n}\n","import { useCallback, useEffect, useMemo, useState } from 'react'\nimport {\n BaseArweaveClient,\n getArweaveReadBaseUrls,\n ensureImageLocal,\n normalizeArweaveTxIdForEnsure,\n type EnsureImageLocalResult,\n type IItemProperty,\n} from '@seedprotocol/sdk'\n\nexport type UseEnsureLocalImageParams = {\n imageProperty: IItemProperty<any>\n filename?: string\n width?: number\n /** When false, skip ensure. Default true. */\n enabled?: boolean\n}\n\nexport type UseEnsureLocalImageReturn = {\n status: 'idle' | 'loading' | EnsureImageLocalResult['status']\n /** Current gateway URL for progressive display; advances through {@link gatewayHrefs} on {@link onGatewayError}. */\n gatewayHref: string | undefined\n /**\n * `/raw/{id}` on every read gateway, preferred ones first (`getArweaveReadBaseUrls`): a\n * just-published image is only on the gateway it was uploaded through until it propagates.\n */\n gatewayHrefs: string[]\n /** Call when `gatewayHref` failed to load (e.g. an `<img>` onError) to move to the next gateway. */\n onGatewayError: () => void\n result: EnsureImageLocalResult | null\n}\n\nconst readStorageTransactionId = (property: IItemProperty<any>): string | undefined => {\n try {\n const snapshot = property.getService?.()?.getSnapshot?.() as\n | { context?: { storageTransactionId?: string } }\n | undefined\n const ctx = snapshot?.context\n return ctx?.storageTransactionId || undefined\n } catch {\n return undefined\n }\n}\n\n/**\n * Ensure an ItemProperty image exists in OPFS (download + targeted resize).\n * Exposes a gateway URL for progressive display while ensuring.\n */\nexport function useEnsureLocalImage(\n params: UseEnsureLocalImageParams,\n): UseEnsureLocalImageReturn {\n const { imageProperty, filename, width, enabled = true } = params\n const [result, setResult] = useState<EnsureImageLocalResult | null>(null)\n const [status, setStatus] = useState<UseEnsureLocalImageReturn['status']>('idle')\n\n const refResolvedValue = imageProperty?.refResolvedValue\n const rawValue = imageProperty?.value\n const stringValue = typeof rawValue === 'string' ? rawValue : undefined\n const storageTransactionId = readStorageTransactionId(imageProperty)\n\n const transactionId = useMemo(() => {\n return (\n normalizeArweaveTxIdForEnsure(storageTransactionId) ??\n normalizeArweaveTxIdForEnsure(filename) ??\n normalizeArweaveTxIdForEnsure(refResolvedValue) ??\n normalizeArweaveTxIdForEnsure(stringValue)\n )\n }, [storageTransactionId, filename, refResolvedValue, stringValue])\n\n const fileName = useMemo(() => {\n const candidate =\n filename ??\n (typeof refResolvedValue === 'string' ? refResolvedValue : undefined) ??\n stringValue\n return candidate?.trim() || undefined\n }, [filename, refResolvedValue, stringValue])\n\n const gatewayHrefs = useMemo(() => {\n if (!transactionId) return []\n const hrefs: string[] = []\n try {\n for (const base of getArweaveReadBaseUrls()) {\n const href = `${base.replace(/\\/$/, '')}/raw/${transactionId}`\n if (!hrefs.includes(href)) hrefs.push(href)\n }\n } catch {\n /* gateway state not ready: fall back to the configured host below */\n }\n try {\n const configured = BaseArweaveClient.getRawUrl(transactionId)\n if (configured && !hrefs.includes(configured)) hrefs.push(configured)\n } catch {\n /* no configured host */\n }\n return hrefs\n }, [transactionId])\n\n const [gatewayIndex, setGatewayIndex] = useState(0)\n useEffect(() => {\n setGatewayIndex(0)\n }, [gatewayHrefs])\n const gatewayHref = gatewayHrefs[gatewayIndex]\n const onGatewayError = useCallback(() => {\n setGatewayIndex((i) => (i < gatewayHrefs.length ? i + 1 : i))\n }, [gatewayHrefs])\n\n useEffect(() => {\n let cancelled = false\n const go = async () => {\n if (!enabled) {\n if (!cancelled) {\n setResult(null)\n setStatus('idle')\n }\n return\n }\n if (!transactionId && !fileName) {\n if (!cancelled) {\n setResult(null)\n setStatus('idle')\n }\n return\n }\n if (!cancelled) {\n setStatus('loading')\n }\n try {\n const r = await ensureImageLocal({\n transactionId,\n fileName,\n widths: width != null ? [width] : undefined,\n })\n if (cancelled) return\n setResult(r)\n setStatus(r.status)\n } catch {\n if (cancelled) return\n setResult({ status: 'failed' })\n setStatus('failed')\n }\n }\n void go()\n return () => {\n cancelled = true\n }\n }, [enabled, transactionId, fileName, width])\n\n return { status, gatewayHref, gatewayHrefs, onGatewayError, result }\n}\n","import React, { useState, useEffect, DetailedHTMLProps, ImgHTMLAttributes } from 'react'\nimport debug from 'debug'\nimport { BaseFileManager } from '@seedprotocol/sdk'\nimport { useItemProperty } from \"./itemProperty\"\nimport type { IItemProperty } from '@seedprotocol/sdk'\nimport { useEnsureLocalImage } from './useEnsureLocalImage'\n\nconst logger = debug('seedSdk:react:SeedImage')\n\n// Cache for content URLs\nconst contentUrlCache = new Map<string, string>()\n\nconst getFileNameWithoutExtension = (filePath: string): string => {\n // This regex has 3 parts:\n // 1. (.*[\\/\\\\]) - Captures the directory path (if any) ending with / or \\\n // 2. ([^\\/\\\\]+) - Captures the filename (anything that's not a / or \\)\n // 3. (\\.[^.\\/\\\\]*)?$ - Captures the last extension (if any)\n const regex = /^(.*[\\/\\\\])?([^\\/\\\\]+?)(\\.[^.\\/\\\\]*)?$/;\n \n const match = filePath.match(regex);\n \n if (match && match[2]) {\n return match[2];\n }\n \n return filePath; // Return original if no match (unlikely)\n}\n\n// Helper function to escape special regex characters\nfunction escapeRegExp(string: string): string {\n return string.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'); // $& means the whole matched string\n}\n\nfunction matchFileNameWithoutExtension(fileName: string, targetName: string): boolean {\n // Create a regex pattern that matches the targetName\n // ^ - start of string\n // targetName is escaped to handle special regex characters\n // $ - end of string\n const regex = new RegExp(`^${escapeRegExp(targetName)}$`);\n \n // First get the filename without extension\n const nameWithoutExt = getFileNameWithoutExtension(fileName);\n \n // Then check if it matches the target name\n return regex.test(nameWithoutExt);\n}\n\nexport type SeedImageProps = DetailedHTMLProps<ImgHTMLAttributes<HTMLImageElement>, HTMLImageElement> & {\n /** The image property, e.g. from useItemProperty or item.properties. */\n imageProperty: IItemProperty<any>\n alt?: string\n width?: number\n height?: number\n /** Optional filename override when property hasn't resolved yet (e.g. in tests) */\n filename?: string\n /**\n * When true (default), ensure the image exists locally (download from Arweave +\n * create needed size variants) before / while rendering.\n */\n ensure?: boolean\n}\n\nconst SeedImageInner = ({\n imageProperty,\n width,\n filename: filenameOverride,\n ensure = true,\n ...props\n}: SeedImageProps, ref: React.ForwardedRef<HTMLImageElement>): React.ReactNode => {\n const [sizedContentUrl, setSizedContentUrl] = useState<string | undefined>()\n const [originalContentUrl, setOriginalContentUrl] = useState<string | undefined>()\n\n const { property: propertyFromHook } = useItemProperty({\n propertyName: imageProperty.propertyName,\n seedLocalId: imageProperty.seedLocalId,\n seedUid: imageProperty.seedUid\n })\n\n // Use passed imageProperty when parent provides it (e.g. direct pass from Item.properties); else use hook for reactive lookup\n const property = imageProperty ?? propertyFromHook\n\n // Resolved filename: explicit override, or from property\n const resolvedFilename = filenameOverride ?? (property?.refResolvedValue ?? property?.value) as string | undefined\n\n const { status: ensureStatus, gatewayHref, onGatewayError } = useEnsureLocalImage({\n imageProperty: property ?? imageProperty,\n filename: filenameOverride,\n width,\n enabled: ensure,\n })\n\n // Get display URL: value (blob URL or filename from ItemProperty.value getter), or resolvedFilename\n const rawValue = property?.value\n const srcUrl = (typeof rawValue === 'string' ? rawValue : resolvedFilename) as string | undefined\n const isFileOrBlob = rawValue != null && (rawValue instanceof File || rawValue instanceof Blob)\n const [blobPreviewUrl, setBlobPreviewUrl] = useState<string | null>(null)\n const blobPreviewRef = React.useRef<string | null>(null)\n useEffect(() => {\n if (isFileOrBlob && (rawValue instanceof File || rawValue instanceof Blob)) {\n if (!blobPreviewRef.current) {\n blobPreviewRef.current = URL.createObjectURL(rawValue)\n setBlobPreviewUrl(blobPreviewRef.current)\n }\n return () => {\n if (blobPreviewRef.current) {\n URL.revokeObjectURL(blobPreviewRef.current)\n blobPreviewRef.current = null\n }\n setBlobPreviewUrl(null)\n }\n }\n blobPreviewRef.current = null\n setBlobPreviewUrl(null)\n }, [isFileOrBlob, rawValue])\n\n // Fallback: when we have filename but no blob URL (e.g. after reload, or sized versions missing), load original file\n useEffect(() => {\n if (!resolvedFilename) return\n const isBlob = (s: unknown) => typeof s === 'string' && s.startsWith('blob:')\n if (rawValue && isBlob(rawValue)) return // Already have blob URL\n if (blobPreviewUrl) return // Have blob preview\n\n let cancelled = false\n const _getOriginalContentUrl = async () => {\n try {\n const filePath = property?.localStoragePath\n ? property.localStoragePath\n : `${BaseFileManager.getFilesPath('images')}/${resolvedFilename}`\n const exists = await BaseFileManager.pathExists(filePath)\n if (exists) {\n const url = await BaseFileManager.getContentUrlFromPath(filePath)\n if (!cancelled && url) setOriginalContentUrl(url)\n }\n } catch (err) {\n logger('_getOriginalContentUrl error', err)\n }\n }\n _getOriginalContentUrl()\n return () => {\n cancelled = true\n }\n }, [resolvedFilename, rawValue, blobPreviewUrl, property?.localStoragePath, ensureStatus])\n\n useEffect(() => {\n if (!width || !resolvedFilename) {\n return\n }\n\n const _getSizedContentUrl = async () => {\n try {\n const fs = await BaseFileManager.getFs()\n const baseDir = property?.localStoragePath\n ? property.localStoragePath.split('/').slice(0, -1).join('/')\n : BaseFileManager.getFilesPath('images')\n const itemsInDir = fs.readdirSync(baseDir, {withFileTypes: true})\n const widthDirs = itemsInDir.filter((item: { isDirectory: () => boolean }) => item.isDirectory())\n const availableWidths = widthDirs.map((dir: { name: string }) => parseInt(dir.name)).filter((n: number) => !Number.isNaN(n))\n if (availableWidths.length === 0) {\n return\n }\n const closestWidth = availableWidths.reduce((prev: number, curr: number) => {\n return (Math.abs(curr - width) < Math.abs(prev - width) ? curr : prev)\n }, availableWidths[0])\n const filenameWithoutExtension = getFileNameWithoutExtension(resolvedFilename)\n\n // Check cache first\n const cacheKey = `${filenameWithoutExtension}-${closestWidth}`\n if (contentUrlCache.has(cacheKey)) {\n try {\n const contentUrl = contentUrlCache.get(cacheKey)\n if (contentUrl) {\n const response = await fetch(contentUrl)\n if (response.ok) {\n setSizedContentUrl(contentUrl)\n return\n }\n }\n } catch (error) {\n logger('error', error)\n contentUrlCache.delete(cacheKey)\n }\n }\n\n const itemsInSizedDir = fs.readdirSync(`${baseDir}/${closestWidth}`, {withFileTypes: true})\n\n const matchingFile = itemsInSizedDir.find((item: { name?: string }) => {\n if (!item.name) {\n return false\n }\n return matchFileNameWithoutExtension(item.name, filenameWithoutExtension)\n })\n if (!matchingFile) {\n return\n }\n const newPath = `${baseDir}/${closestWidth}/${matchingFile?.name}`\n const exists = await BaseFileManager.pathExists(newPath)\n if (exists) {\n const contentUrl = await BaseFileManager.getContentUrlFromPath(newPath)\n if (contentUrl) {\n contentUrlCache.set(cacheKey, contentUrl)\n setSizedContentUrl(contentUrl)\n }\n }\n } catch (err) {\n logger('_getSizedContentUrl error', err)\n }\n }\n\n _getSizedContentUrl()\n }, [property, width, srcUrl, resolvedFilename, ensureStatus])\n\n // Render img when we have a content URL, filename, or progressive gateway href\n const isBlobUrl = (s: unknown) => typeof s === 'string' && s.startsWith('blob:')\n const hasContentUrl = !!sizedContentUrl || !!originalContentUrl || !!blobPreviewUrl || (!!srcUrl && isBlobUrl(srcUrl))\n if (!hasContentUrl && !resolvedFilename && !gatewayHref) {\n return null\n }\n\n // Prefer local sized/original/blob; while ensuring, fall back to gateway then transparent GIF\n const imgSrc =\n sizedContentUrl ||\n originalContentUrl ||\n blobPreviewUrl ||\n (isBlobUrl(srcUrl) ? srcUrl : undefined) ||\n (ensure && !hasContentUrl ? gatewayHref : undefined) ||\n 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'\n\n // A gateway that 404s (e.g. the image hasn't propagated there yet) moves on to the next one.\n const { onError: onErrorProp, ...imgProps } = props\n const handleError: React.ReactEventHandler<HTMLImageElement> = (event) => {\n if (gatewayHref && imgSrc === gatewayHref) onGatewayError()\n onErrorProp?.(event)\n }\n\n return (\n <img\n src={imgSrc}\n alt={props.alt || imageProperty.propertyName || 'Image'}\n width={width}\n {...imgProps}\n ref={ref}\n onError={handleError}\n />\n )\n}\n\n// Shallow comparison of all props: the old comparator ignored className, style, alt and handlers,\n// so changing them never re-rendered.\n// forwardRef: React 18 doesn't pass `ref` to a plain function component.\nexport const SeedImage = React.memo(React.forwardRef(SeedImageInner))\n","import { useCallback, useEffect, useState } from 'react'\nimport {\n resolveMediaRef,\n type ResolveMediaRefOptions,\n type ResolveMediaRefResult,\n} from '@seedprotocol/sdk'\n\nexport type UseResolvedMediaRefParams = {\n value: string | undefined | null\n enabled?: boolean\n} & ResolveMediaRefOptions\n\nexport type UseResolvedMediaRefReturn = {\n href: string | undefined\n status: 'idle' | 'loading' | 'ready' | 'empty' | 'unresolved' | 'error'\n source: 'direct' | 'gateway' | 'localBlob' | undefined\n error: Error | null\n result: ResolveMediaRefResult | null\n refetch: () => void\n}\n\ntype ResolvedState = {\n /** The request (value, options, refetch count) this state belongs to. */\n key: string\n status: UseResolvedMediaRefReturn['status']\n result: ResolveMediaRefResult | null\n error: Error | null\n}\n\n/**\n * Resolve a feed/XML media string (URL, Arweave tx id, seed UID, etc.) to a display URL.\n * Use `SeedImage` when you have a synced item property from the local database.\n */\nexport function useResolvedMediaRef(params: UseResolvedMediaRefParams): UseResolvedMediaRefReturn {\n const { value, enabled = true, treatAs } = params\n const [tick, setTick] = useState(0)\n const key = JSON.stringify([enabled, value ?? null, treatAs ?? null, tick])\n const [state, setState] = useState<ResolvedState | null>(null)\n\n useEffect(() => {\n let cancelled = false\n let blobUrl: string | undefined\n const go = async () => {\n if (!enabled || value == null || String(value).trim() === '') {\n setState({ key, status: 'idle', result: null, error: null })\n return\n }\n setState({ key, status: 'loading', result: null, error: null })\n try {\n const r = await resolveMediaRef(String(value), { treatAs })\n // A local blob URL is created for this result; nobody else revokes it.\n const createdBlobUrl = r.status === 'ready' && r.source === 'localBlob' ? r.href : undefined\n if (cancelled) {\n if (createdBlobUrl) URL.revokeObjectURL(createdBlobUrl)\n return\n }\n blobUrl = createdBlobUrl\n const status = r.status === 'empty' ? 'empty' : r.status === 'ready' ? 'ready' : 'unresolved'\n setState({ key, status, result: r, error: null })\n } catch (e) {\n if (cancelled) return\n setState({ key, status: 'error', result: null, error: e instanceof Error ? e : new Error(String(e)) })\n }\n }\n void go()\n return () => {\n cancelled = true\n // Release the blob URL this effect created (value changed, refetch, or unmount)\n if (blobUrl) URL.revokeObjectURL(blobUrl)\n }\n }, [key, enabled, value, treatAs])\n\n const refetch = useCallback(() => {\n setTick((t) => t + 1)\n }, [])\n\n // State from an earlier value (or before a refetch) is never returned: its blob URL may already\n // be revoked, and it isn't this value's media.\n const current = state?.key === key ? state : null\n const isEmpty = !enabled || value == null || String(value).trim() === ''\n const result = current?.result ?? null\n const href = result && result.status === 'ready' ? result.href : undefined\n const source = result && result.status === 'ready' ? result.source : undefined\n\n return {\n href,\n status: current?.status ?? (isEmpty ? 'idle' : 'loading'),\n source,\n error: current?.error ?? null,\n result,\n refetch,\n }\n}\n","import React from 'react'\nimport { useResolvedMediaRef, type UseResolvedMediaRefParams } from './useResolvedMediaRef'\n\ntype ImgProps = React.DetailedHTMLProps<\n React.ImgHTMLAttributes<HTMLImageElement>,\n HTMLImageElement\n>\n\nexport type SeedMediaImageProps = Omit<ImgProps, 'src'> & {\n value: string | undefined | null\n enabled?: boolean\n treatAs?: UseResolvedMediaRefParams['treatAs']\n /** Custom render; defaults to `<img />`. */\n render?: (props: ImgProps & { src: string }) => React.ReactNode\n}\n\n/**\n * Image from a raw feed/XML media string. For local `ItemProperty` images use `SeedImage`.\n */\nexport function SeedMediaImage({\n value,\n enabled,\n treatAs,\n render,\n alt,\n ...imgProps\n}: SeedMediaImageProps): React.ReactNode {\n const { href } = useResolvedMediaRef({ value, enabled, treatAs })\n if (!href) {\n return null\n }\n const merged = { ...imgProps, src: href, alt: alt ?? '' } as ImgProps & { src: string }\n if (render) {\n return <>{render(merged)}</>\n }\n return <img {...merged} />\n}\n","import React from 'react'\nimport { useResolvedMediaRef, type UseResolvedMediaRefParams } from './useResolvedMediaRef'\n\ntype AnchorProps = React.DetailedHTMLProps<\n React.AnchorHTMLAttributes<HTMLAnchorElement>,\n HTMLAnchorElement\n>\n\nfunction defaultLabelFromHref(href: string): string {\n try {\n const u = new URL(href)\n const seg = u.pathname.split('/').filter(Boolean).pop()\n return seg || 'Open'\n } catch {\n return 'Open'\n }\n}\n\nfunction isHttpOrHttps(href: string): boolean {\n try {\n const p = new URL(href).protocol\n return p === 'http:' || p === 'https:'\n } catch {\n return false\n }\n}\n\nexport type SeedMediaFileProps = Omit<AnchorProps, 'href'> & {\n value: string | undefined | null\n enabled?: boolean\n treatAs?: UseResolvedMediaRefParams['treatAs']\n download?: string | boolean\n /** Custom render; defaults to `<a />`. */\n render?: (props: AnchorProps & { href: string }) => React.ReactNode\n}\n\n/**\n * Link for a raw feed/XML file URL (or tx id / seed UID string resolved like images).\n * For local synced `File` item properties, build download UI with `useItemProperty` / file paths.\n */\nexport function SeedMediaFile({\n value,\n enabled,\n treatAs,\n download,\n render,\n children,\n target,\n rel,\n ...anchorProps\n}: SeedMediaFileProps): React.ReactNode {\n const { href } = useResolvedMediaRef({ value, enabled, treatAs })\n if (!href) {\n return null\n }\n const external = isHttpOrHttps(href)\n const resolvedTarget = target !== undefined ? target : external ? '_blank' : undefined\n const merged = {\n ...anchorProps,\n href,\n download,\n target: resolvedTarget,\n rel:\n rel ??\n (external && resolvedTarget === '_blank' ? 'noopener noreferrer' : undefined),\n children: children ?? defaultLabelFromHref(href),\n } as AnchorProps & { href: string }\n if (render) {\n return <>{render(merged)}</>\n }\n return <a {...merged} />\n}\n","import React from 'react'\nimport { useResolvedMediaRef, type UseResolvedMediaRefParams } from './useResolvedMediaRef'\n\ntype AudioProps = React.DetailedHTMLProps<\n React.AudioHTMLAttributes<HTMLAudioElement>,\n HTMLAudioElement\n>\n\nexport type SeedMediaAudioProps = Omit<AudioProps, 'src'> & {\n value: string | undefined | null\n enabled?: boolean\n treatAs?: UseResolvedMediaRefParams['treatAs']\n /** Custom render; defaults to `<audio controls />`. */\n render?: (props: AudioProps & { src: string }) => React.ReactNode\n}\n\n/**\n * Audio from a raw feed/XML media string. Storage remains File/Image schema types;\n * this helper only picks the browser element.\n */\nexport function SeedMediaAudio({\n value,\n enabled,\n treatAs,\n render,\n controls = true,\n ...audioProps\n}: SeedMediaAudioProps): React.ReactNode {\n const { href } = useResolvedMediaRef({ value, enabled, treatAs })\n if (!href) {\n return null\n }\n const merged = { ...audioProps, src: href, controls } as AudioProps & {\n src: string\n }\n if (render) {\n return <>{render(merged)}</>\n }\n return <audio {...merged} />\n}\n","import React from 'react'\nimport { useResolvedMediaRef, type UseResolvedMediaRefParams } from './useResolvedMediaRef'\n\ntype VideoProps = React.DetailedHTMLProps<\n React.VideoHTMLAttributes<HTMLVideoElement>,\n HTMLVideoElement\n>\n\nexport type SeedMediaVideoProps = Omit<VideoProps, 'src'> & {\n value: string | undefined | null\n enabled?: boolean\n treatAs?: UseResolvedMediaRefParams['treatAs']\n /** Custom render; defaults to `<video controls />`. */\n render?: (props: VideoProps & { src: string }) => React.ReactNode\n}\n\n/**\n * Video from a raw feed/XML media string. Storage remains File/Image schema types;\n * this helper only picks the browser element.\n */\nexport function SeedMediaVideo({\n value,\n enabled,\n treatAs,\n render,\n controls = true,\n ...videoProps\n}: SeedMediaVideoProps): React.ReactNode {\n const { href } = useResolvedMediaRef({ value, enabled, treatAs })\n if (!href) {\n return null\n }\n const merged = { ...videoProps, src: href, controls } as VideoProps & {\n src: string\n }\n if (render) {\n return <>{render(merged)}</>\n }\n return <video {...merged} />\n}\n","import React from 'react'\n\ntype DivProps = Omit<\n React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>,\n 'dangerouslySetInnerHTML' | 'children'\n>\n\nexport type SeedHtmlProps = DivProps & {\n html: string | null | undefined\n /** App-supplied sanitizer (e.g. DOMPurify). The SDK does not ship a default policy. */\n sanitize: (raw: string) => string\n /** If set, receive sanitized HTML and render your own container; no innerHTML is set by SeedHtml. */\n render?: (props: { html: string }) => React.ReactNode\n}\n\nfunction hasRenderableHtml(html: string | null | undefined): html is string {\n return typeof html === 'string' && html.trim().length > 0\n}\n\n/**\n * Renders HTML from an `Html` property, feed field, or other source using a required sanitizer.\n * For plain-text display of markup, use `{String(value)}` instead.\n */\nexport function SeedHtml({\n html,\n sanitize,\n render,\n ...divProps\n}: SeedHtmlProps): React.ReactNode {\n if (!hasRenderableHtml(html)) {\n return null\n }\n const safe = sanitize(html)\n if (render) {\n return <>{render({ html: safe })}</>\n }\n return <div {...divProps} dangerouslySetInnerHTML={{ __html: safe }} />\n}\n","export type FormatSeedJsonOptions = {\n /** Max object/array nesting; default 6 */\n maxDepth?: number\n /** Truncate long strings in the output; default 50_000 */\n maxStringLength?: number\n /** Passed to JSON.stringify; default 2 */\n space?: string | number\n}\n\nfunction truncateString(s: string, max: number): string {\n if (s.length <= max) return s\n return `${s.slice(0, max)}…`\n}\n\nfunction looksLikeJsonObjectOrArray(s: string): boolean {\n const t = s.trim()\n return (t.startsWith('{') && t.endsWith('}')) || (t.startsWith('[') && t.endsWith(']'))\n}\n\nfunction limitDepth(\n value: unknown,\n maxDepth: number,\n maxStringLength: number,\n depth: number,\n seen: WeakSet<object>,\n): unknown {\n if (typeof value === 'bigint') {\n return String(value)\n }\n if (typeof value === 'symbol') {\n return value.toString()\n }\n if (typeof value === 'function') {\n return '[Function]'\n }\n if (value instanceof Date) {\n return value.toISOString()\n }\n if (typeof value === 'string') {\n return truncateString(value, maxStringLength)\n }\n if (value === null || typeof value !== 'object') {\n return value\n }\n if (seen.has(value)) {\n return '[Circular]'\n }\n if (depth >= maxDepth) {\n return '[Max depth]'\n }\n // `seen` holds the current path only: a cycle is a value that is its own ancestor. Keeping\n // every visited object printed a shared (non-cyclic) object as [Circular] the second time.\n seen.add(value)\n try {\n if (Array.isArray(value)) {\n return value.map((item) => limitDepth(item, maxDepth, maxStringLength, depth + 1, seen))\n }\n const out: Record<string, unknown> = {}\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n out[k] = limitDepth(v, maxDepth, maxStringLength, depth + 1, seen)\n }\n return out\n } finally {\n seen.delete(value)\n }\n}\n\n/**\n * Format a value as indented JSON text for read-only display (no eval).\n * Handles JSON-looking strings (parse attempt), depth limits, long strings, BigInt, and circular refs.\n */\nexport function formatSeedJson(value: unknown, options?: FormatSeedJsonOptions): string {\n const maxDepth = options?.maxDepth ?? 6\n const maxStringLength = options?.maxStringLength ?? 50_000\n const space = options?.space ?? 2\n\n if (value === undefined) {\n return 'undefined'\n }\n if (value === null) {\n return 'null'\n }\n\n let toSerialize: unknown = value\n if (typeof value === 'string') {\n if (looksLikeJsonObjectOrArray(value)) {\n try {\n toSerialize = JSON.parse(value) as unknown\n } catch {\n return truncateString(value, maxStringLength)\n }\n } else {\n return truncateString(value, maxStringLength)\n }\n }\n\n const limited = limitDepth(toSerialize, maxDepth, maxStringLength, 0, new WeakSet())\n\n try {\n return JSON.stringify(limited, null, space)\n } catch {\n return '[Unserializable JSON]'\n }\n}\n","import React from 'react'\nimport { formatSeedJson, type FormatSeedJsonOptions } from './formatSeedJson'\n\ntype PreProps = Omit<\n React.DetailedHTMLProps<React.HTMLAttributes<HTMLPreElement>, HTMLPreElement>,\n 'children' | 'dangerouslySetInnerHTML'\n>\n\nexport type SeedJsonProps = PreProps & {\n value: unknown\n /** If set, replaces built-in formatting entirely */\n format?: (value: unknown) => string\n /** Options for `formatSeedJson` when `format` is not set */\n formatOptions?: FormatSeedJsonOptions\n render?: (props: { text: string }) => React.ReactNode\n}\n\n/**\n * Read-only JSON display for `Json` item properties or parsed objects. Does not execute code.\n */\nexport function SeedJson({\n value,\n format,\n formatOptions,\n render,\n ...preProps\n}: SeedJsonProps): React.ReactNode {\n const text = format ? format(value) : formatSeedJson(value, formatOptions)\n if (render) {\n return <>{render({ text })}</>\n }\n return <pre {...preProps}>{text}</pre>\n}\n","import { useMemo } from 'react'\nimport {\n normalizeFeedItemFields,\n type FeedFieldManifest,\n type NormalizedFeedFieldValue,\n} from '@seedprotocol/sdk'\n\n/**\n * Synchronously classify feed item fields from a manifest (no network).\n * Memoized; pass a stable `manifest` reference when possible.\n */\nexport function useNormalizedFeedItemFields(\n item: Record<string, unknown> | null | undefined,\n manifest: FeedFieldManifest,\n): Record<string, NormalizedFeedFieldValue | undefined> {\n return useMemo(() => {\n if (!item) {\n return {}\n }\n return normalizeFeedItemFields(item, manifest)\n }, [item, manifest])\n}\n"],"mappings":"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"}
|