@powerhousedao/connect-core 6.2.3-dev.76 → 6.2.3-dev.78
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/connect-router-11uRIT3z.d.ts.map +1 -1
- package/dist/index-DQ0nbYne.d.ts.map +1 -1
- package/dist/{index-DaI9iBEb.d.ts → index-q4bHCPoJ.d.ts} +1 -6
- package/dist/{index-DaI9iBEb.d.ts.map → index-q4bHCPoJ.d.ts.map} +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +7 -7
- package/dist/{multi-reactor-D79-dEpt.js → multi-reactor-CCMzwoMi.js} +18 -16
- package/dist/multi-reactor-CCMzwoMi.js.map +1 -0
- package/dist/{plugin-host-BSFfhXpz.js → plugin-host-HhfxwUZN.js} +2 -2
- package/dist/{plugin-host-BSFfhXpz.js.map → plugin-host-HhfxwUZN.js.map} +1 -1
- package/dist/{plugin-ui-sO7AJLHj.js → plugin-ui-W0ma6oKJ.js} +5 -5
- package/dist/plugin-ui-W0ma6oKJ.js.map +1 -0
- package/dist/plugins.js +2 -2
- package/dist/{router-8pekwJsk.js → router-DHL9GT3P.js} +4 -3
- package/dist/{router-8pekwJsk.js.map → router-DHL9GT3P.js.map} +1 -1
- package/dist/router.js +3 -3
- package/dist/{runtime-Y9B9UJK1.js → runtime-D5hpzfgT.js} +3 -3
- package/dist/{runtime-Y9B9UJK1.js.map → runtime-D5hpzfgT.js.map} +1 -1
- package/dist/runtime-config.js +4 -4
- package/dist/{runtime-flag-CJWngtxE.js → runtime-flag-U8y14Nth.js} +2 -2
- package/dist/{runtime-flag-CJWngtxE.js.map → runtime-flag-U8y14Nth.js.map} +1 -1
- package/dist/runtime.d.ts +1 -1
- package/dist/runtime.js +2 -2
- package/dist/{sidebar-DVQCwBYR.js → sidebar-CRv_DvBV.js} +4 -4
- package/dist/sidebar-CRv_DvBV.js.map +1 -0
- package/dist/{views-CAbBIFzO.js → views-anOyTI7u.js} +10 -4
- package/dist/views-anOyTI7u.js.map +1 -0
- package/dist/views.js +1 -1
- package/package.json +8 -8
- package/src/router/connect-router.ts +1 -0
- package/src/router/drive-route-loader.ts +3 -1
- package/src/router/route-data.ts +2 -4
- package/src/router/titles.ts +2 -2
- package/src/runtime/graphql-routable-backend.ts +9 -0
- package/src/runtime/multi-reactor.ts +27 -25
- package/src/views/directory-listing.tsx +12 -1
- package/dist/multi-reactor-D79-dEpt.js.map +0 -1
- package/dist/plugin-ui-sO7AJLHj.js.map +0 -1
- package/dist/sidebar-DVQCwBYR.js.map +0 -1
- package/dist/views-CAbBIFzO.js.map +0 -1
|
@@ -107,11 +107,20 @@ export function createGraphQLRoutableBackend(
|
|
|
107
107
|
});
|
|
108
108
|
const backend: IRoutableBackend = {
|
|
109
109
|
supports: { find: findIsServableOverGraphQL, pointInTimeViews: false },
|
|
110
|
+
drives: gql.read.drives,
|
|
110
111
|
get: async <TDocument extends PHDocument>(
|
|
111
112
|
identifier: string,
|
|
112
113
|
view?: ViewFilter,
|
|
113
114
|
signal?: AbortSignal,
|
|
114
115
|
) => drives.learn(await gql.get<TDocument>(identifier, view, signal)),
|
|
116
|
+
getMany: async <TDocument extends PHDocument>(
|
|
117
|
+
ids: string[],
|
|
118
|
+
view?: ViewFilter,
|
|
119
|
+
signal?: AbortSignal,
|
|
120
|
+
) =>
|
|
121
|
+
(await gql.getMany<TDocument>(ids, view, signal)).map((document) =>
|
|
122
|
+
drives.learn(document),
|
|
123
|
+
),
|
|
115
124
|
getOperations: (identifier, view, filter, paging, signal) =>
|
|
116
125
|
gql.getOperations(identifier, view, filter, paging, signal),
|
|
117
126
|
find: async (search, view, paging, signal) =>
|
|
@@ -107,10 +107,30 @@ export type MultiReactorParams = {
|
|
|
107
107
|
* requirement must NOT apply, or no backend would be eligible.) */
|
|
108
108
|
export const PLACE_ON_REMOTE: CollectionRequirementsInput = Object.freeze({});
|
|
109
109
|
|
|
110
|
+
/**
|
|
111
|
+
* The local answer stands when only the Switchboard could not answer -- but
|
|
112
|
+
* only when there IS a local backend. Remote-only has nothing to fall back to,
|
|
113
|
+
* so its errors surface. Installed as the router's `answerFailedIdCheck`, so
|
|
114
|
+
* the facade and `write.isDocumentIdTaken` both apply it.
|
|
115
|
+
*/
|
|
116
|
+
function localIdCheckAnswer(
|
|
117
|
+
documentId: string,
|
|
118
|
+
error: unknown,
|
|
119
|
+
): boolean | undefined {
|
|
120
|
+
if (!failedOnlyOnRemote(error)) {
|
|
121
|
+
return undefined;
|
|
122
|
+
}
|
|
123
|
+
logger.warn(
|
|
124
|
+
"The Switchboard could not say whether @id is taken; using the local reactor's answer: @error",
|
|
125
|
+
documentId,
|
|
126
|
+
error,
|
|
127
|
+
);
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
|
|
110
131
|
/** The router Connect installs, with Connect's handling of credentials and id checks. */
|
|
111
132
|
export class ConnectRoutingClient extends RoutingReactorClient {
|
|
112
133
|
private readonly remote: GraphQLRoutableBackend;
|
|
113
|
-
private readonly hasLocal: boolean;
|
|
114
134
|
|
|
115
135
|
constructor(
|
|
116
136
|
backends: readonly RoutableBackendConfig[],
|
|
@@ -118,37 +138,19 @@ export class ConnectRoutingClient extends RoutingReactorClient {
|
|
|
118
138
|
remote: GraphQLRoutableBackend,
|
|
119
139
|
hasLocal: boolean,
|
|
120
140
|
) {
|
|
121
|
-
super(backends,
|
|
141
|
+
super(backends, {
|
|
142
|
+
...options,
|
|
143
|
+
answerFailedIdCheck: hasLocal
|
|
144
|
+
? localIdCheckAnswer
|
|
145
|
+
: options.answerFailedIdCheck,
|
|
146
|
+
});
|
|
122
147
|
this.remote = remote;
|
|
123
|
-
this.hasLocal = hasLocal;
|
|
124
148
|
}
|
|
125
149
|
|
|
126
150
|
/** Renown's sign-in and sign-out call this on whatever client is installed. */
|
|
127
151
|
notifyCredentialsChanged(): void {
|
|
128
152
|
this.remote.notifyCredentialsChanged();
|
|
129
153
|
}
|
|
130
|
-
|
|
131
|
-
/** The local answer stands when only the Switchboard could not answer — but
|
|
132
|
-
* only when there IS a local backend. Remote-only has nothing to fall back
|
|
133
|
-
* to, so its errors surface. */
|
|
134
|
-
override async isDocumentIdTaken(
|
|
135
|
-
documentId: string,
|
|
136
|
-
signal?: AbortSignal,
|
|
137
|
-
): Promise<boolean> {
|
|
138
|
-
try {
|
|
139
|
-
return await super.isDocumentIdTaken(documentId, signal);
|
|
140
|
-
} catch (error) {
|
|
141
|
-
if (!this.hasLocal || !failedOnlyOnRemote(error)) {
|
|
142
|
-
throw error;
|
|
143
|
-
}
|
|
144
|
-
logger.warn(
|
|
145
|
-
"The Switchboard could not say whether @id is taken; using the local reactor's answer: @error",
|
|
146
|
-
documentId,
|
|
147
|
-
error,
|
|
148
|
-
);
|
|
149
|
-
return false;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
154
|
}
|
|
153
155
|
|
|
154
156
|
function failedOnlyOnRemote(error: unknown): boolean {
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
makeDriveSlug,
|
|
4
4
|
makeNodeSlug,
|
|
5
5
|
useDocumentById,
|
|
6
|
+
useDriveNodes,
|
|
6
7
|
useLinkProps,
|
|
7
8
|
} from "@powerhousedao/reactor-browser";
|
|
8
9
|
import type {
|
|
@@ -83,7 +84,17 @@ const byKindThenName = (a: Node, b: Node) =>
|
|
|
83
84
|
/** Core's drive fallback: a listing of the open folder; an open document shows instead. */
|
|
84
85
|
export function DirectoryListing({ drive, folder, children }: DriveViewProps) {
|
|
85
86
|
if (children) return children;
|
|
86
|
-
|
|
87
|
+
return <FolderListing drive={drive} folder={folder} />;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function FolderListing({
|
|
91
|
+
drive,
|
|
92
|
+
folder,
|
|
93
|
+
}: {
|
|
94
|
+
drive: DocumentDriveDocument;
|
|
95
|
+
folder: FolderNode | undefined;
|
|
96
|
+
}) {
|
|
97
|
+
const nodes = useDriveNodes(drive.header.id) ?? [];
|
|
87
98
|
const path = folderPath(nodes, folder);
|
|
88
99
|
const parent = path.at(-2);
|
|
89
100
|
const entries = nodes
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"multi-reactor-D79-dEpt.js","names":[],"sources":["../src/runtime/graphql-routable-backend.ts","../src/runtime/multi-reactor.ts"],"sourcesContent":["import {\n JOB_NOT_FOUND_ERROR_NAME,\n JobStatus,\n type PagedResults,\n type ReactorInfo,\n type ViewFilter,\n} from \"@powerhousedao/reactor\";\nimport {\n DRIVE_DOCUMENT_TYPES,\n findIsServableOverGraphQL,\n GraphQLReactorClient,\n type DriveIdCall,\n type GraphQLReactorClientOptions,\n} from \"@powerhousedao/reactor-browser\";\nimport type { IRoutableBackend } from \"@powerhousedao/reactor-router\";\nimport type { Action, PHDocument } from \"@powerhousedao/shared/document-model\";\n\nconst REMOTE_INFO_QUERY = /* GraphQL */ `\n query RemoteReactorInfo {\n inspection {\n info {\n storage {\n engine\n persistence\n durable\n selfHeal\n }\n workflows\n syncChannels\n }\n }\n }\n`;\n\ntype RemoteReactorInfo = {\n inspection: { info: Omit<ReactorInfo, \"access\"> };\n};\n\n/** Ids and slugs of documents this backend answered as drives, by drive id. */\nclass KnownDrives {\n private readonly ids = new Map<string, string>();\n\n learn<T extends PHDocument>(document: T): T {\n const { id, slug, documentType } = document.header;\n if ((DRIVE_DOCUMENT_TYPES as readonly string[]).includes(documentType)) {\n this.ids.set(id, id);\n if (slug) {\n this.ids.set(slug, id);\n }\n }\n return document;\n }\n\n learnPage<T extends PHDocument>(page: PagedResults<T>): PagedResults<T> {\n for (const document of page.results) {\n this.learn(document);\n }\n return page;\n }\n\n driveOf(identifier: string | undefined): string | undefined {\n return identifier === undefined ? undefined : this.ids.get(identifier);\n }\n\n /** Names a drive only when this backend proved it is one. */\n driveIdFor = (call: DriveIdCall): string | undefined => {\n switch (call.method) {\n case \"create\":\n case \"find\":\n return this.driveOf(call.parentId);\n case \"execute\":\n case \"deleteDocument\":\n return this.driveOf(call.documentId);\n case \"executeBatch\":\n for (const job of call.jobs ?? []) {\n const drive = this.driveOf(job.documentId);\n if (drive !== undefined) {\n return drive;\n }\n }\n return undefined;\n }\n };\n}\n\nexport type GraphQLRoutableBackendOptions = Omit<\n GraphQLReactorClientOptions,\n \"driveIdFor\"\n>;\n\nexport type GraphQLRoutableBackend = {\n readonly backend: IRoutableBackend;\n /** The Switchboard's own facts, from its inspection subgraph. */\n info(): Promise<ReactorInfo>;\n /** Reopens the realtime socket on the new credentials. */\n notifyCredentialsChanged(): void;\n};\n\n/** A Switchboard as a router backend; members GraphQL cannot serve are absent. */\nexport function createGraphQLRoutableBackend(\n options: GraphQLRoutableBackendOptions,\n): GraphQLRoutableBackend {\n const drives = new KnownDrives();\n const gql = new GraphQLReactorClient({\n ...options,\n driveIdFor: drives.driveIdFor,\n });\n const backend: IRoutableBackend = {\n supports: { find: findIsServableOverGraphQL, pointInTimeViews: false },\n get: async <TDocument extends PHDocument>(\n identifier: string,\n view?: ViewFilter,\n signal?: AbortSignal,\n ) => drives.learn(await gql.get<TDocument>(identifier, view, signal)),\n getOperations: (identifier, view, filter, paging, signal) =>\n gql.getOperations(identifier, view, filter, paging, signal),\n find: async (search, view, paging, signal) =>\n drives.learnPage(await gql.find(search, view, paging, signal)),\n isServed: (identifier, view, signal) =>\n gql.isServed(identifier, view, signal),\n getOutgoingRelationships: (source, type, view, paging, signal) =>\n gql.getOutgoingRelationships(source, type, view, paging, signal),\n getIncomingRelationships: (target, type, view, paging, signal) =>\n gql.getIncomingRelationships(target, type, view, paging, signal),\n getOutgoingRelationshipEdges: (source, type, view, paging, signal) =>\n gql.getOutgoingRelationshipEdges(source, type, view, paging, signal),\n getIncomingRelationshipEdges: (target, type, view, paging, signal) =>\n gql.getIncomingRelationshipEdges(target, type, view, paging, signal),\n subscribe: (search, callback, view) =>\n gql.subscribe(search, callback, view),\n getCreateSignaturePolicy: () => gql.getCreateSignaturePolicy(),\n getCreateProtocolVersions: (parent, signal) =>\n gql.getCreateProtocolVersions(parent, signal),\n create: async <TDocument extends PHDocument = PHDocument>(\n document: PHDocument,\n parent?: string,\n signal?: AbortSignal,\n ) => drives.learn(await gql.create<TDocument>(document, parent, signal)),\n execute: async <TDocument extends PHDocument>(\n identifier: string,\n branch: string,\n actions: Action[],\n signal?: AbortSignal,\n ) =>\n drives.learn(\n await gql.execute<TDocument>(identifier, branch, actions, signal),\n ),\n executeBatch: (request, signal) => gql.executeBatch(request, signal),\n deleteDocument: (identifier, propagate, signal) =>\n gql.deleteDocument(identifier, propagate, signal),\n setPreferredEditor: (identifier, editor, branch, signal) =>\n gql.setPreferredEditor(identifier, editor, branch, signal),\n getJob: async (jobId, signal) => {\n const job = await gql.getJob(jobId, signal);\n return job?.status === JobStatus.FAILED &&\n job.error?.name === JOB_NOT_FOUND_ERROR_NAME\n ? undefined\n : job;\n },\n waitForJob: (job, signal) => gql.waitForJob(job, signal),\n };\n return {\n backend,\n info: async () => {\n const result = await gql.request<RemoteReactorInfo>(REMOTE_INFO_QUERY);\n return {\n ...result.inspection.info,\n access: { admin: false, sql: false },\n };\n },\n notifyCredentialsChanged: () => gql.notifyCredentialsChanged(),\n };\n}\n","import {\n createReactorInspector,\n type ReactorInfo,\n} from \"@powerhousedao/reactor\";\nimport type {\n BrowserReactorClientModule,\n WorkerReactorClientModule,\n} from \"@powerhousedao/reactor-browser\";\nimport {\n FanInPartialFailureError,\n fromReactorClient,\n RoutingReactorClient,\n type CollectionRequirementsInput,\n type ReactorReach,\n type RoutableBackendConfig,\n type RouterDiagnostic,\n type RoutingClientOptions,\n} from \"@powerhousedao/reactor-router\";\nimport type {\n DocumentModelModule,\n ISigner,\n} from \"@powerhousedao/shared/document-model\";\nimport { logger } from \"document-model\";\nimport {\n createGraphQLRoutableBackend,\n type GraphQLRoutableBackend,\n} from \"./graphql-routable-backend.js\";\n\nexport { createGraphQLRoutableBackend } from \"./graphql-routable-backend.js\";\nexport type {\n GraphQLRoutableBackend,\n GraphQLRoutableBackendOptions,\n} from \"./graphql-routable-backend.js\";\n\nexport const LOCAL_BACKEND_NAME = \"connect-local\";\nexport const REMOTE_BACKEND_NAME = \"switchboard-remote\";\n\nexport type LocalReactorModule =\n | BrowserReactorClientModule\n | WorkerReactorClientModule;\n\nconst REMOTE_REACH: ReactorReach = Object.freeze({\n hosting: \"remote\",\n inspection: \"none\",\n});\n\n/** Only the tab's reactor is inspectable here, so new collections land on it. */\nexport const PLACE_ON_LOCAL: CollectionRequirementsInput = Object.freeze({\n inspectable: true,\n});\n\nexport function localReach(kind: LocalReactorModule[\"kind\"]): ReactorReach {\n return kind === \"worker\"\n ? { hosting: \"worker\", inspection: \"rpc\" }\n : { hosting: \"in-process\", inspection: \"direct\" };\n}\n\n/** Read at every refresh, never kept: a rebuilt reactor reports its own. */\nexport function localFacts(\n module: LocalReactorModule,\n): () => Promise<ReactorInfo> {\n if (module.kind === \"worker\") {\n return () => module.inspector.info();\n }\n return async () => {\n const reactorModule = module.reactorModule;\n if (!reactorModule) {\n throw new Error(\"The in-tab reactor exposes no module to inspect\");\n }\n return createReactorInspector(reactorModule).info();\n };\n}\n\n/** `<origin>[/<prefix>]/d/<slug>` -> `<origin>[/<prefix>]/graphql`. */\nexport function deriveSwitchboardGraphqlUrl(\n remoteDriveUrl: string,\n): string | undefined {\n let parsed: URL;\n try {\n parsed = new URL(remoteDriveUrl);\n } catch {\n return undefined;\n }\n const prefix = /^(.*)\\/d\\/[^/]+\\/?$/.exec(parsed.pathname)?.[1];\n return prefix === undefined ? undefined : `${parsed.origin}${prefix}/graphql`;\n}\n\n/** The loaded document models, or a getter that reads the current ones. */\nexport type LoadedDocumentModels =\n | readonly DocumentModelModule[]\n | (() => readonly DocumentModelModule[]);\n\nexport type MultiReactorParams = {\n /** The local module, built and ready. Undefined builds a REMOTE-ONLY router\n * (no local backend → no local reactor, no PGlite): the `remote` client. */\n module: LocalReactorModule | undefined;\n remoteGraphqlUrl: string;\n /** Signs routed writes; unsigned (anonymous) without one. */\n signer?: ISigner;\n /** Signing models when the local module exposes no registry; the router asks the local reactor (or, remote-only, the loaded set). */\n documentModelModules: LoadedDocumentModels;\n onDiagnostic?: RouterDiagnostic;\n};\n\n/** Remote-only placement: the Switchboard serves every collection. (It offers\n * no inspection surface over GraphQL, so PLACE_ON_LOCAL's `inspectable`\n * requirement must NOT apply, or no backend would be eligible.) */\nexport const PLACE_ON_REMOTE: CollectionRequirementsInput = Object.freeze({});\n\n/** The router Connect installs, with Connect's handling of credentials and id checks. */\nexport class ConnectRoutingClient extends RoutingReactorClient {\n private readonly remote: GraphQLRoutableBackend;\n private readonly hasLocal: boolean;\n\n constructor(\n backends: readonly RoutableBackendConfig[],\n options: RoutingClientOptions,\n remote: GraphQLRoutableBackend,\n hasLocal: boolean,\n ) {\n super(backends, options);\n this.remote = remote;\n this.hasLocal = hasLocal;\n }\n\n /** Renown's sign-in and sign-out call this on whatever client is installed. */\n notifyCredentialsChanged(): void {\n this.remote.notifyCredentialsChanged();\n }\n\n /** The local answer stands when only the Switchboard could not answer — but\n * only when there IS a local backend. Remote-only has nothing to fall back\n * to, so its errors surface. */\n override async isDocumentIdTaken(\n documentId: string,\n signal?: AbortSignal,\n ): Promise<boolean> {\n try {\n return await super.isDocumentIdTaken(documentId, signal);\n } catch (error) {\n if (!this.hasLocal || !failedOnlyOnRemote(error)) {\n throw error;\n }\n logger.warn(\n \"The Switchboard could not say whether @id is taken; using the local reactor's answer: @error\",\n documentId,\n error,\n );\n return false;\n }\n }\n}\n\nfunction failedOnlyOnRemote(error: unknown): boolean {\n return (\n error instanceof FanInPartialFailureError &&\n error.failures.every((failure) => failure.backend === REMOTE_BACKEND_NAME)\n );\n}\n\n/**\n * Build the Connect routing client. With a local `module` it is the router\n * over the tab's reactor (primary) and the Switchboard; WITHOUT one it is the\n * `remote` client — a remote-only router whose single backend is the\n * Switchboard, so no local reactor (and no PGlite) is built. Either way the\n * result is a full IReactorClient the app graph consumes unchanged.\n */\nexport async function buildMultiReactorClient(\n params: MultiReactorParams,\n): Promise<ConnectRoutingClient> {\n const module = params.module;\n const registry = module?.reactorModule?.documentModelRegistry;\n const loaded = params.documentModelModules;\n const loadedModules = typeof loaded === \"function\" ? loaded : () => loaded;\n const remote = createGraphQLRoutableBackend({\n url: params.remoteGraphqlUrl,\n documentModels: () => registry?.getAllModules() ?? loadedModules(),\n });\n const remoteBackend: RoutableBackendConfig = {\n name: REMOTE_BACKEND_NAME,\n backend: remote.backend,\n facts: () => remote.info(),\n reach: REMOTE_REACH,\n refusesMisroutes: false,\n };\n const backends: RoutableBackendConfig[] = module\n ? [\n {\n name: LOCAL_BACKEND_NAME,\n backend: fromReactorClient(module.client),\n facts: localFacts(module),\n reach: localReach(module.kind),\n refusesMisroutes: false,\n },\n remoteBackend,\n ]\n : [remoteBackend];\n const router = new ConnectRoutingClient(\n backends,\n {\n // Remote-only: the Switchboard is primary and serves every collection;\n // with a local reactor, new collections land on it (inspectable).\n primaryBackend: module ? LOCAL_BACKEND_NAME : REMOTE_BACKEND_NAME,\n defaultRequirements: module ? PLACE_ON_LOCAL : PLACE_ON_REMOTE,\n signer: params.signer,\n onDiagnostic: params.onDiagnostic,\n // The Switchboard serves no models over GraphQL; remote-only uses the loaded set.\n documentModelModules: module ? undefined : loadedModules,\n },\n remote,\n Boolean(module),\n );\n await router.refreshFacts();\n return router;\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"plugin-ui-sO7AJLHj.js","names":[],"sources":["../src/router/drive-route-loader.ts","../src/router/core-views.tsx","../src/plugins/plugin-ui.ts"],"sourcesContent":["// The drive routes' loaders. Server-safe: they read everything from router context.\nimport type { DriveArrivals, PHStore } from \"@powerhousedao/reactor-browser\";\nimport {\n findDriveBySlugOrId,\n makeDriveSlug,\n makeNodeSlug,\n resolveDriveRoute,\n} from \"@powerhousedao/reactor-browser\";\nimport { notFound, redirect } from \"@tanstack/react-router\";\n\nexport type ConnectRouterContext = {\n store: PHStore;\n /** Without it the drive route never waits for drives to arrive. */\n arrivals?: DriveArrivals;\n /** Ends every document title; \"Connect\" without one. */\n appName?: string;\n /** Rendering the loading shell: a layout shows its pendingComponent in place of its page. */\n shell?: boolean;\n};\n\n/** A missing drive's not-found data. */\nexport type DriveNotFoundData = { driveSlug: string };\n\nexport function isDriveNotFound(data: unknown): data is DriveNotFoundData {\n return (\n typeof data === \"object\" &&\n data !== null &&\n typeof (data as { driveSlug?: unknown }).driveSlug === \"string\"\n );\n}\n\n/** What the drive routes resolved their params to. */\nexport type DriveRouteData = { driveId: string; nodeId?: string };\n\nexport type DriveRouteParams = { driveSlug: string; nodeSlug?: string };\n\nexport type DriveLoaderOptions = {\n params: DriveRouteParams;\n context: ConnectRouterContext;\n abortController: AbortController;\n preload: boolean;\n};\n\nfunction resolve(options: DriveLoaderOptions, params: DriveRouteParams) {\n return resolveDriveRoute({\n store: options.context.store,\n arrivals: options.context.arrivals,\n driveSlug: params.driveSlug,\n nodeSlug: params.nodeSlug,\n signal: options.abortController.signal,\n preload: options.preload,\n });\n}\n\n// At the drive layout, so the 404 takes the drive's place in the layout.\nfunction driveNotFound(driveSlug: string) {\n return notFound({\n routeId: \"/_app/d/$driveSlug\" as never,\n data: { driveSlug } satisfies DriveNotFoundData,\n });\n}\n\n/** The drive layout's loader: the drive, or not found once arrivals settle. */\nexport async function loadDrive(\n options: DriveLoaderOptions,\n): Promise<{ driveId: string }> {\n const { driveSlug } = options.params;\n const result = await resolve(options, { driveSlug });\n if (result.kind === \"drive-not-found\") {\n // oxlint-disable-next-line typescript/only-throw-error -- router control flow\n throw driveNotFound(driveSlug);\n }\n return { driveId: result.driveId };\n}\n\n/** The node route's loader: the drive and the open node. An id or an old name\n * in the URL becomes the current slugs; a missing node leads to its drive. */\nexport async function loadNode(\n options: DriveLoaderOptions,\n): Promise<DriveRouteData> {\n const { params, context, preload } = options;\n const result = await resolve(options, params);\n switch (result.kind) {\n case \"drive\": {\n const data: DriveRouteData = result.nodeId\n ? { driveId: result.driveId, nodeId: result.nodeId }\n : { driveId: result.driveId };\n const canonical = canonicalParams(context.store, data);\n // An id, an old name or an old drive id in the URL becomes the current slugs.\n if (\n !preload &&\n canonical &&\n (canonical.driveSlug !== params.driveSlug ||\n canonical.nodeSlug !== (params.nodeSlug || undefined))\n ) {\n // oxlint-disable-next-line typescript/only-throw-error -- router control flow\n throw redirectTo(canonical);\n }\n return data;\n }\n case \"drive-not-found\":\n // oxlint-disable-next-line typescript/only-throw-error -- router control flow\n throw driveNotFound(params.driveSlug);\n case \"node-not-found\":\n // oxlint-disable-next-line typescript/only-throw-error -- router control flow\n throw redirectTo({\n driveSlug:\n canonicalParams(context.store, { driveId: result.driveId })\n ?.driveSlug ?? params.driveSlug,\n nodeSlug: undefined,\n });\n }\n}\n\n// The slugs links write for `data`; unknown while the drive is unlisted.\nfunction canonicalParams(\n store: PHStore,\n data: DriveRouteData,\n): DriveRouteParams | undefined {\n const drive = findDriveBySlugOrId(store, data.driveId);\n if (!drive) return undefined;\n if (!data.nodeId) return { driveSlug: makeDriveSlug(drive) };\n const node = drive.state.global.nodes.find((n) => n.id === data.nodeId);\n if (!node) return undefined;\n return { driveSlug: makeDriveSlug(drive), nodeSlug: makeNodeSlug(node) };\n}\n\n// In place, with the URL's search params and history state (the opened-from index).\nfunction redirectTo(params: DriveRouteParams) {\n return redirect({\n to: \"/d/$driveSlug/{-$nodeSlug}\",\n params,\n search: true,\n state: true,\n replace: true,\n });\n}\n","// The router's one edge to core's views: views never import this module, so\n// loading them lazily makes no import cycle.\nimport type {\n ErrorRouteComponent,\n NotFoundRouteProps,\n RouteComponent,\n} from \"@tanstack/react-router\";\nimport { lazyRouteComponent } from \"@tanstack/react-router\";\nimport type { ComponentType } from \"react\";\nimport { isDriveNotFound } from \"./drive-route-loader.js\";\nimport type { ConnectModals } from \"./modals.js\";\nimport { CORE_MODAL_VIEWS } from \"./modals.js\";\n\n// Core's views load only when a page shows one.\nconst views = () => import(\"@powerhousedao/connect-core/views\");\n\nconst CORE_VIEW = Symbol.for(\"@powerhousedao/connect-core/core-view\");\n\n/** One of core's views, loaded with the route that shows it. */\nexport function coreView(name: keyof Awaited<ReturnType<typeof views>>) {\n return Object.assign(lazyRouteComponent(views, name), {\n [CORE_VIEW]: true,\n }) as RouteComponent;\n}\n\n/** Whether a route's component is one of core's views, which a plugin's UI replaces. */\nexport function isCoreView(component: unknown) {\n return (\n typeof component === \"function\" &&\n (component as { [CORE_VIEW]?: true })[CORE_VIEW] === true\n );\n}\n\nconst NotFoundPage = coreView(\"NotFoundPage\");\nconst DriveNotFoundPage = coreView(\"DriveNotFoundPage\") as ComponentType<{\n driveSlug: string;\n}>;\n\n/** Core's not-found page; a missing drive's names the drive. */\nexport function CoreNotFound({ data }: Partial<NotFoundRouteProps>) {\n return isDriveNotFound(data) ? (\n <DriveNotFoundPage driveSlug={data.driveSlug} />\n ) : (\n <NotFoundPage />\n );\n}\n\n/** Core's view of a route's loader or render error. */\nexport const CoreError = coreView(\"CoreRouteError\") as ErrorRouteComponent;\n\n/** Core's modals, loaded when one opens. */\nexport const coreModals: ConnectModals = Object.fromEntries(\n Object.entries(CORE_MODAL_VIEWS).map(([type, name]) => [\n type,\n coreView(name),\n ]),\n);\n","import type { AnyRoute, ErrorRouteComponent } from \"@tanstack/react-router\";\nimport type { ComponentType } from \"react\";\nimport type { ConnectRouteRegistration } from \"../registry.js\";\nimport { getConnectRegistry } from \"../registry.js\";\nimport type { ConnectModals } from \"../router/modals.js\";\nimport { isCoreView } from \"../router/core-views.js\";\nimport {\n addConnectRoutes,\n removeConnectRoutes,\n resolveConnectRoutes,\n} from \"../router/route-tree.js\";\nimport { knownPlugins } from \"./plugin-host.js\";\nimport type { ConnectPluginUi, ConnectRouteUi } from \"./types.js\";\n\n/** The active plugins' UI; a later plugin's routes and modals win. */\nexport type ConnectPluginUiSet = {\n routes: Record<string, ConnectRouteUi>;\n addRoutes: ConnectRouteRegistration[];\n modals: ConnectModals;\n roots: ComponentType[];\n errorComponent?: ErrorRouteComponent;\n commonEditors: boolean;\n consent: boolean;\n};\n\nconst NO_UI: ConnectPluginUiSet = {\n routes: {},\n addRoutes: [],\n modals: {},\n roots: [],\n commonEditors: false,\n consent: false,\n};\n\nexport function mergeConnectPluginUi(\n uis: readonly ConnectPluginUi[],\n): ConnectPluginUiSet {\n const set: ConnectPluginUiSet = {\n routes: {},\n addRoutes: [],\n modals: {},\n roots: [],\n commonEditors: false,\n consent: false,\n };\n for (const ui of uis) {\n for (const [id, route] of Object.entries(ui.routes ?? {})) {\n set.routes[id] = { ...set.routes[id], ...route };\n }\n if (ui.addRoutes) set.addRoutes.push(ui.addRoutes);\n Object.assign(set.modals, ui.modals);\n if (ui.Root) set.roots.push(ui.Root);\n set.errorComponent = ui.errorComponent ?? set.errorComponent;\n set.commonEditors ||= ui.commonEditors ?? false;\n set.consent ||= ui.consent ?? false;\n }\n return set;\n}\n\n/** Loads the UI of the plugins `composition` names, or of every known plugin\n * without one (classic Connect), and keeps it for `getConnectPluginUi`. */\nexport async function loadConnectPluginUi(\n composition: readonly { module: string }[] | undefined,\n): Promise<ConnectPluginUiSet> {\n const definitions = await knownPlugins();\n const modules = composition\n ? composition.map((entry) => entry.module)\n : Object.keys(definitions);\n const uis = await Promise.all(\n modules.map(\n (module) => definitions[module]?.ui?.() ?? Promise.resolve(undefined),\n ),\n );\n const set = mergeConnectPluginUi(\n uis.filter((ui): ui is ConnectPluginUi => !!ui),\n );\n getConnectRegistry().ui = set;\n return set;\n}\n\n/** The active plugins' UI; none until `loadRuntimeConfig` has loaded it. */\nexport function getConnectPluginUi(): ConnectPluginUiSet {\n return getConnectRegistry().ui ?? NO_UI;\n}\n\ntype RouteOptions = Record<string, unknown> & { id?: string; path?: string };\n\nconst ROOT_ID = \"__root__\";\n\nfunction forEachRoute(\n route: AnyRoute,\n visit: (route: AnyRoute, id: string) => void,\n parentId?: string,\n) {\n const options = route.options as unknown as RouteOptions;\n const segment = options.id ?? options.path;\n const own =\n parentId === undefined || segment === undefined\n ? ROOT_ID\n : `${parentId === ROOT_ID ? \"\" : parentId}/${segment.replace(/^\\//, \"\")}`;\n visit(route, own);\n for (const child of (route.children ?? []) as AnyRoute[]) {\n forEachRoute(child, visit, own);\n }\n}\n\ntype Applied = { originals: Map<AnyRoute, RouteOptions>; added: AnyRoute[] };\nconst appliedTo = new WeakMap<AnyRoute, Applied>();\n\n/** Sets `ui`'s route options on the tree's routes that still show core's view,\n * and adds its routes; `resetConnectPluginUi` undoes both. */\nexport function applyConnectPluginUi(\n routeTree: AnyRoute,\n ui: ConnectPluginUiSet = getConnectPluginUi(),\n) {\n resetConnectPluginUi(routeTree);\n const applied: Applied = { originals: new Map(), added: [] };\n forEachRoute(routeTree, (route, id) => {\n const routeUi = ui.routes[id];\n const options = route.options as unknown as RouteOptions;\n if (!routeUi || !isCoreView(options.component)) return;\n const { staticData, ...views } = routeUi;\n const keys = [...Object.keys(views), ...(staticData ? [\"staticData\"] : [])];\n applied.originals.set(\n route,\n Object.fromEntries(keys.map((key) => [key, options[key]])),\n );\n Object.assign(options, views);\n if (staticData) {\n options.staticData = {\n ...(options.staticData as object | undefined),\n ...staticData,\n };\n }\n });\n for (const registration of ui.addRoutes) {\n const routes = resolveConnectRoutes(registration, routeTree);\n addConnectRoutes(routes);\n applied.added.push(...routes);\n }\n appliedTo.set(routeTree, applied);\n}\n\n/** Restores the routes `applyConnectPluginUi` changed and removes the ones it added. */\nexport function resetConnectPluginUi(routeTree: AnyRoute) {\n const applied = appliedTo.get(routeTree);\n if (!applied) return;\n for (const [route, originals] of applied.originals) {\n Object.assign(route.options, originals);\n }\n removeConnectRoutes(applied.added);\n appliedTo.delete(routeTree);\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sidebar-DVQCwBYR.js","names":[],"sources":["../src/router/cold-start.ts","../src/router/route-components.tsx","../src/router/titles.ts","../src/router/settings.ts","../src/router/route-data.ts","../src/router/create-target.ts","../src/router/sidebar.ts"],"sourcesContent":["import { useRouterState } from \"@tanstack/react-router\";\n\n/** True until the router has resolved its first location. */\nexport function useIsColdStart() {\n return useRouterState({ select: (state) => !state.resolvedLocation });\n}\n","import type {\n AnyRoute,\n AnyRouter,\n RouteComponent,\n} from \"@tanstack/react-router\";\nimport { Outlet, useMatch, useRouter } from \"@tanstack/react-router\";\nimport type { ReactNode } from \"react\";\nimport type {\n DocumentDriveDocument,\n FolderNode,\n} from \"@powerhousedao/shared/document-drive\";\nimport type { PHDocument } from \"@powerhousedao/shared/document-model\";\nimport { useIsColdStart } from \"./cold-start.js\";\n\n/** A drive view's props: drive apps and core's directory listing get the same. */\nexport type DriveViewProps = {\n drive: DocumentDriveDocument;\n /** The open folder; undefined at the drive's root. */\n folder?: FolderNode;\n /** The open document's view, when one is open. */\n children?: ReactNode;\n};\n\n/** A document view's props, e.g. core's JSON view or revision history. */\nexport type DocumentViewProps = { document: PHDocument };\n\n// Open views stay while a navigation loads; the pending view only after this.\nexport const DRIVE_PENDING_MS = 200;\n\n/** The scroll container of the content view. */\nexport const CONTENT_VIEW_ID = \"content-view\";\n\n/** A layout's content area: the matched route, in the container whose scroll\n * Connect resets on new views and restores on back and forward. */\nexport function ContentOutlet(props: { className?: string }) {\n return (\n <div\n className={props.className ?? \"flex h-full flex-col overflow-auto\"}\n id={CONTENT_VIEW_ID}\n data-scroll-restoration-id={CONTENT_VIEW_ID}\n >\n {useRouter().options.context.shell ? <LayoutPending /> : <Outlet />}\n </div>\n );\n}\n\n// The loading shell: the layout's own pendingComponent stands in for its page.\nfunction LayoutPending() {\n const router = useRouter<AnyRouter>();\n const routeId = useMatch({ strict: false, select: (match) => match.routeId });\n const routesById = router.routesById as Partial<Record<string, AnyRoute>>;\n const route = routeId ? routesById[routeId] : undefined;\n const Pending = route?.options.pendingComponent as RouteComponent | undefined;\n return Pending ? <Pending /> : null;\n}\n\n/** A note on a cold start; otherwise the open view stays while loading. */\nexport function DrivePending() {\n const isColdStart = useIsColdStart();\n if (!isColdStart) return null;\n return (\n <p role=\"status\" className=\"p-6 text-core-muted\">\n Loading drive…\n </p>\n );\n}\n","// Document titles for Connect's routes; route heads set them through <HeadContent />.\nimport type { PHStore } from \"@powerhousedao/reactor-browser\";\nimport { getDriveName } from \"@powerhousedao/reactor-browser\";\nimport type { DriveRouteData } from \"./drive-route-loader.js\";\n\nconst DEFAULT_APP_NAME = \"Connect\";\n\n/** The configured app name, else \"Connect\". */\nexport function appTitle(appName?: string) {\n return appName || DEFAULT_APP_NAME;\n}\n\n/** `Not found · <app>`. */\nexport function notFoundTitle(appName?: string) {\n return `Not found · ${appTitle(appName)}`;\n}\n\n/** `<node> · <drive> · <app>`, from the store so renames show. */\nexport function driveTitle(\n store: PHStore,\n data: DriveRouteData | undefined,\n appName?: string,\n) {\n const drive = data && store.drives?.find((d) => d.header.id === data.driveId);\n if (!drive) return appTitle(appName);\n const node = data.nodeId\n ? drive.state.global.nodes.find((n) => n.id === data.nodeId)\n : undefined;\n return [node?.name, getDriveName(drive)]\n .filter(Boolean)\n .concat(appTitle(appName))\n .join(\" · \");\n}\n","// The settings page: its tabs are the /settings route's children with `staticData.settings`.\nimport {\n OPENED_FROM_STATE_KEY,\n SETTINGS_PATH,\n} from \"@powerhousedao/reactor-browser\";\nimport type {\n AnyRoute,\n AnyRouter,\n HistoryState,\n StaticDataRouteOption,\n} from \"@tanstack/react-router\";\nimport { notFound, redirect, useRouter } from \"@tanstack/react-router\";\nimport type { ComponentType } from \"react\";\nimport { useMemo } from \"react\";\nimport { useRoutesById } from \"./route-tree.js\";\nimport type { ConnectRouterContext } from \"./drive-route-loader.js\";\nimport { appTitle } from \"./titles.js\";\n\n/** A settings tab, as a child route of /settings declares it in `staticData`. */\nexport type ConnectSettingsTab = {\n label: string;\n /** Ascending; Connect's own tabs use multiples of 10. */\n order: number;\n icon?: ComponentType<{ size?: number; className?: string }>;\n /** Marks the tab as destructive, e.g. the danger zone. */\n destructive?: boolean;\n /** Hidden, and redirected away from, when this returns false. */\n enabled?: (context: ConnectRouterContext) => boolean;\n};\n\ndeclare module \"@tanstack/react-router\" {\n interface StaticDataRouteOption {\n settings?: ConnectSettingsTab;\n }\n}\n\n/** The settings layout route's id. */\nexport const SETTINGS_ROUTE_ID = `/_app${SETTINGS_PATH}`;\n\n// Structural, not the router's match union: route options typed with it are circular.\ntype SettingsMatch = { routeId: string; staticData: StaticDataRouteOption };\n\n/** A listed tab: its id is its path below /settings. */\nexport type SettingsTabEntry = ConnectSettingsTab & {\n id: string;\n routeId: string;\n to: string;\n};\n\n/** The enabled tabs below `settingsRoute`, in order. */\nexport function settingsTabsOf(\n settingsRoute: AnyRoute,\n context: ConnectRouterContext,\n): SettingsTabEntry[] {\n const children = (settingsRoute.children ?? []) as AnyRoute[];\n const prefix = `${settingsRoute.fullPath}/`;\n return children\n .flatMap((route) => {\n const tab = (\n route.options as { staticData?: { settings?: ConnectSettingsTab } }\n ).staticData?.settings;\n if (!tab || tab.enabled?.(context) === false) return [];\n const { id: routeId, fullPath } = route as {\n id: string;\n fullPath: string;\n };\n const to = fullPath.replace(/\\/$/, \"\");\n return [{ ...tab, id: to.slice(prefix.length), routeId, to }];\n })\n .sort((a, b) => a.order - b.order || a.label.localeCompare(b.label));\n}\n\n/** The settings tabs of `router`'s tree; empty without a settings route. */\nexport function settingsTabs(router: AnyRouter): SettingsTabEntry[] {\n const settingsRoute = router.routesById[SETTINGS_ROUTE_ID] as\n | AnyRoute\n | undefined;\n if (!settingsRoute) return [];\n return settingsTabsOf(\n settingsRoute,\n router.options.context as ConnectRouterContext,\n );\n}\n\nexport function useSettingsTabs() {\n const router = useRouter();\n const routesById = useRoutesById();\n return useMemo(() => settingsTabs(router), [router, routesById]);\n}\n\n/** Whether `matches` show a tab that may stay: not bare /settings, not a disabled tab. */\nexport function showsSettingsTab(\n matches: readonly SettingsMatch[],\n context: ConnectRouterContext,\n) {\n const leaf = matches.at(-1);\n if (!leaf || leaf.routeId === SETTINGS_ROUTE_ID) return false;\n return leaf.staticData.settings?.enabled?.(context) !== false;\n}\n\nlet routerSettingsRoute: AnyRoute | undefined;\n\n/** The router's settings route: core's, or a project's override of it. */\nexport function setSettingsRoute(route?: AnyRoute) {\n routerSettingsRoute = route;\n}\n\n/** Sends bare /settings and a disabled tab to the first tab. */\nexport function redirectToSettingsTab({\n matches,\n context,\n}: {\n matches: readonly SettingsMatch[];\n context: ConnectRouterContext;\n}): void {\n if (showsSettingsTab(matches, context)) return;\n const first =\n routerSettingsRoute && settingsTabsOf(routerSettingsRoute, context).at(0);\n // oxlint-disable-next-line typescript/only-throw-error -- router control flow\n if (!first) throw notFound();\n // oxlint-disable-next-line typescript/only-throw-error -- router control flow\n throw redirect<AnyRouter>({ to: first.to, replace: true, state: true });\n}\n\n/** `Settings · <tab> · <app>`. */\nexport function settingsTitle(tabLabel: string | undefined, appName?: string) {\n return [\"Settings\", tabLabel, appTitle(appName)].filter(Boolean).join(\" · \");\n}\n\n/** Makes closing settings go home, not back to an entry that may be gone,\n * e.g. a deleted drive. */\nexport function useForgetSettingsOpenedFrom() {\n const router = useRouter();\n return () =>\n router.navigate({\n to: \".\",\n search: true,\n replace: true,\n resetScroll: false,\n state: (prev) =>\n ({ ...prev, [OPENED_FROM_STATE_KEY]: undefined }) as HistoryState,\n });\n}\n\n/** History state that keeps where the settings page was opened from. */\nexport function keepOpenedFrom(state: object) {\n const from: unknown = Reflect.get(state, OPENED_FROM_STATE_KEY);\n return { [OPENED_FROM_STATE_KEY]: from } as unknown as HistoryState;\n}\n","import { useDrives } from \"@powerhousedao/reactor-browser\";\nimport type {\n DocumentDriveDocument,\n FolderNode,\n} from \"@powerhousedao/shared/document-drive\";\nimport { useRouterState } from \"@tanstack/react-router\";\nimport type { DriveRouteData } from \"./drive-route-loader.js\";\n\n/** The drive layout route's id. */\nexport const DRIVE_ROUTE_ID = \"/_app/d/$driveSlug\";\n/** The node route's id: below the drive layout, with an optional node. */\nexport const NODE_ROUTE_ID = \"/_app/d/$driveSlug/{-$nodeSlug}\";\n\n/** The open drive, folder and document, from the node route's loader data.\n * Live: a drive's edits re-render the caller. */\nexport type DriveRouteView = {\n drive: DocumentDriveDocument | undefined;\n /** The open folder; undefined at the drive's root or on a document. */\n folder: FolderNode | undefined;\n /** The open document's id; undefined on a folder or the drive's root. */\n documentId: string | undefined;\n};\n\n/** The ids the node route resolved, or undefined outside a drive. */\nexport function useDriveRouteIds(): DriveRouteData | undefined {\n return useRouterState({\n select: (state) =>\n state.matches.find((match) => match.routeId === NODE_ROUTE_ID)\n ?.loaderData as DriveRouteData | undefined,\n });\n}\n\n/** What the drive routes show: the drive, and the folder or document open in it. */\nexport function useDriveRouteData(): DriveRouteView {\n const ids = useDriveRouteIds();\n const drives = useDrives();\n const drive = ids && drives?.find((d) => d.header.id === ids.driveId);\n const node = ids?.nodeId\n ? drive?.state.global.nodes.find((n) => n.id === ids.nodeId)\n : undefined;\n const folder = node?.kind === \"folder\" ? node : undefined;\n // A node the listed drive doesn't have yet was a document the loader fetched.\n return { drive, folder, documentId: folder ? undefined : ids?.nodeId };\n}\n","import { useDrives } from \"@powerhousedao/reactor-browser\";\nimport type { DocumentDriveDocument } from \"@powerhousedao/shared/document-drive\";\nimport { useState } from \"react\";\nimport { useDriveRouteData } from \"./route-data.js\";\n\nexport type CreateDocumentTarget = {\n /** The drive the document goes in; undefined until one is picked. */\n drive: DocumentDriveDocument | undefined;\n /** The drives to pick from, or undefined when the drive is given. */\n choices: DocumentDriveDocument[] | undefined;\n pick: (driveId: string) => void;\n /** Whether it is the open drive, whose open folder the document goes in. */\n isOpenDrive: boolean;\n};\n\n/** Where a new document goes: the modal's `driveId`, else the open drive, else\n * one the user picks, the only drive preselected. */\nexport function useCreateDocumentTarget(\n driveId: string | undefined,\n): CreateDocumentTarget {\n const { drive: openDrive } = useDriveRouteData();\n const drives = useDrives();\n const [picked, setPicked] = useState<string>();\n const given = driveId ?? openDrive?.header.id;\n const only = drives?.length === 1 ? drives[0].header.id : undefined;\n const id = given ?? picked ?? only;\n return {\n drive: drives?.find((d) => d.header.id === id),\n choices: given ? undefined : (drives ?? []),\n pick: setPicked,\n isOpenDrive: !!openDrive && openDrive.header.id === id,\n };\n}\n","// What routes tell the sidebar layout: nav items and whether to show the sidebar.\nimport type { AnyRoute, AnyRouter } from \"@tanstack/react-router\";\nimport { useMatches, useRouter } from \"@tanstack/react-router\";\nimport type { ComponentType } from \"react\";\nimport { useMemo } from \"react\";\nimport { useRoutesById } from \"./route-tree.js\";\n\n/** A sidebar link to the route that declares it in `staticData.nav`. */\nexport type ConnectNavItem = {\n label: string;\n icon?: ComponentType<{ size?: number; className?: string }>;\n /** Ascending, after the drives. */\n order: number;\n};\n\n/** \"hidden\" drops the sidebar; \"collapsed\" keeps only its header and footer. */\nexport type SidebarMode = \"hidden\" | \"collapsed\";\n\ndeclare module \"@tanstack/react-router\" {\n interface StaticDataRouteOption {\n nav?: ConnectNavItem;\n sidebar?: SidebarMode;\n }\n}\n\nexport type NavRouteEntry = ConnectNavItem & { routeId: string; to: string };\n\n/** The routes with `staticData.nav`, in order; routes with params can't be listed. */\nexport function navRoutes(router: AnyRouter): NavRouteEntry[] {\n return Object.values(router.routesById as Record<string, AnyRoute>)\n .flatMap((route) => {\n const nav = (route.options as { staticData?: { nav?: ConnectNavItem } })\n .staticData?.nav;\n const { id: routeId, fullPath } = route as {\n id: string;\n fullPath: string;\n };\n if (!nav || fullPath.includes(\"$\")) return [];\n return [{ ...nav, routeId, to: fullPath.replace(/(.)\\/$/, \"$1\") }];\n })\n .sort((a, b) => a.order - b.order || a.label.localeCompare(b.label));\n}\n\nexport function useNavRoutes() {\n const router = useRouter();\n const routesById = useRoutesById();\n return useMemo(() => navRoutes(router), [router, routesById]);\n}\n\n/** The deepest match's `staticData.sidebar`, if any declares one. */\nexport function useSidebarMode(): SidebarMode | undefined {\n return useMatches({\n select: (matches) =>\n matches.findLast((match) => match.staticData.sidebar !== undefined)\n ?.staticData.sidebar,\n });\n}\n"],"mappings":";;;;;;;AAGA,SAAgB,iBAAiB;CAC/B,OAAO,eAAe,EAAE,SAAS,UAAU,CAAC,MAAM,iBAAiB,CAAC;AACtE;;;ACsBA,MAAa,mBAAmB;;AAGhC,MAAa,kBAAkB;;;AAI/B,SAAgB,cAAc,OAA+B;CAC3D,OACE,oBAAC,OAAD;EACE,WAAW,MAAM,aAAa;EAC9B,IAAI;EACJ,8BAA4B;EAE3B,UAAA,UAAU,CAAC,CAAC,QAAQ,QAAQ,QAAQ,oBAAC,eAAD,CAAgB,CAAA,IAAI,oBAAC,QAAD,CAAS,CAAA;CAC/D,CAAA;AAET;AAGA,SAAS,gBAAgB;CACvB,MAAM,SAAS,UAAqB;CACpC,MAAM,UAAU,SAAS;EAAE,QAAQ;EAAO,SAAS,UAAU,MAAM;CAAQ,CAAC;CAC5E,MAAM,aAAa,OAAO;CAE1B,MAAM,WADQ,UAAU,WAAW,WAAW,KAAA,EAAA,EACvB,QAAQ;CAC/B,OAAO,UAAU,oBAAC,SAAD,CAAU,CAAA,IAAI;AACjC;;AAGA,SAAgB,eAAe;CAE7B,IAAI,CADgB,eACL,GAAG,OAAO;CACzB,OACE,oBAAC,KAAD;EAAG,MAAK;EAAS,WAAU;EAAsB,UAAA;CAE9C,CAAA;AAEP;;;AC5DA,MAAM,mBAAmB;;AAGzB,SAAgB,SAAS,SAAkB;CACzC,OAAO,WAAW;AACpB;;AAGA,SAAgB,cAAc,SAAkB;CAC9C,OAAO,eAAe,SAAS,OAAO;AACxC;;AAGA,SAAgB,WACd,OACA,MACA,SACA;CACA,MAAM,QAAQ,QAAQ,MAAM,QAAQ,MAAM,MAAM,EAAE,OAAO,OAAO,KAAK,OAAO;CAC5E,IAAI,CAAC,OAAO,OAAO,SAAS,OAAO;CAInC,OAAO,EAHM,KAAK,SACd,MAAM,MAAM,OAAO,MAAM,MAAM,MAAM,EAAE,OAAO,KAAK,MAAM,IACzD,KAAA,EAAA,EACU,MAAM,aAAa,KAAK,CAAC,CAAC,CACrC,OAAO,OAAO,CAAC,CACf,OAAO,SAAS,OAAO,CAAC,CAAC,CACzB,KAAK,KAAK;AACf;;;;ACKA,MAAa,oBAAoB,QAAQ;;AAazC,SAAgB,eACd,eACA,SACoB;CACpB,MAAM,WAAY,cAAc,YAAY,CAAC;CAC7C,MAAM,SAAS,GAAG,cAAc,SAAS;CACzC,OAAO,SACJ,SAAS,UAAU;EAClB,MAAM,MACJ,MAAM,QACN,YAAY;EACd,IAAI,CAAC,OAAO,IAAI,UAAU,OAAO,MAAM,OAAO,OAAO,CAAC;EACtD,MAAM,EAAE,IAAI,SAAS,aAAa;EAIlC,MAAM,KAAK,SAAS,QAAQ,OAAO,EAAE;EACrC,OAAO,CAAC;GAAE,GAAG;GAAK,IAAI,GAAG,MAAM,OAAO,MAAM;GAAG;GAAS;EAAG,CAAC;CAC9D,CAAC,CAAC,CACD,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,cAAc,EAAE,KAAK,CAAC;AACvE;;AAGA,SAAgB,aAAa,QAAuC;CAClE,MAAM,gBAAgB,OAAO,WAAW;CAGxC,IAAI,CAAC,eAAe,OAAO,CAAC;CAC5B,OAAO,eACL,eACA,OAAO,QAAQ,OACjB;AACF;AAEA,SAAgB,kBAAkB;CAChC,MAAM,SAAS,UAAU;CACzB,MAAM,aAAa,cAAc;CACjC,OAAO,cAAc,aAAa,MAAM,GAAG,CAAC,QAAQ,UAAU,CAAC;AACjE;;AAGA,SAAgB,iBACd,SACA,SACA;CACA,MAAM,OAAO,QAAQ,GAAG,EAAE;CAC1B,IAAI,CAAC,QAAQ,KAAK,YAAY,mBAAmB,OAAO;CACxD,OAAO,KAAK,WAAW,UAAU,UAAU,OAAO,MAAM;AAC1D;AAEA,IAAI;;AAGJ,SAAgB,iBAAiB,OAAkB;CACjD,sBAAsB;AACxB;;AAGA,SAAgB,sBAAsB,EACpC,SACA,WAIO;CACP,IAAI,iBAAiB,SAAS,OAAO,GAAG;CACxC,MAAM,QACJ,uBAAuB,eAAe,qBAAqB,OAAO,CAAC,CAAC,GAAG,CAAC;CAE1E,IAAI,CAAC,OAAO,MAAM,SAAS;CAE3B,MAAM,SAAoB;EAAE,IAAI,MAAM;EAAI,SAAS;EAAM,OAAO;CAAK,CAAC;AACxE;;AAGA,SAAgB,cAAc,UAA8B,SAAkB;CAC5E,OAAO;EAAC;EAAY;EAAU,SAAS,OAAO;CAAC,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,KAAK;AAC7E;;;AAIA,SAAgB,8BAA8B;CAC5C,MAAM,SAAS,UAAU;CACzB,aACE,OAAO,SAAS;EACd,IAAI;EACJ,QAAQ;EACR,SAAS;EACT,aAAa;EACb,QAAQ,UACL;GAAE,GAAG;IAAO,wBAAwB,KAAA;EAAU;CACnD,CAAC;AACL;;AAGA,SAAgB,eAAe,OAAe;CAC5C,MAAM,OAAgB,QAAQ,IAAI,OAAO,qBAAqB;CAC9D,OAAO,GAAG,wBAAwB,KAAK;AACzC;;;;AC3IA,MAAa,iBAAiB;;AAE9B,MAAa,gBAAgB;;AAa7B,SAAgB,mBAA+C;CAC7D,OAAO,eAAe,EACpB,SAAS,UACP,MAAM,QAAQ,MAAM,UAAU,MAAM,YAAY,aAAa,CAAC,EAC1D,WACR,CAAC;AACH;;AAGA,SAAgB,oBAAoC;CAClD,MAAM,MAAM,iBAAiB;CAC7B,MAAM,SAAS,UAAU;CACzB,MAAM,QAAQ,OAAO,QAAQ,MAAM,MAAM,EAAE,OAAO,OAAO,IAAI,OAAO;CACpE,MAAM,OAAO,KAAK,SACd,OAAO,MAAM,OAAO,MAAM,MAAM,MAAM,EAAE,OAAO,IAAI,MAAM,IACzD,KAAA;CACJ,MAAM,SAAS,MAAM,SAAS,WAAW,OAAO,KAAA;CAEhD,OAAO;EAAE;EAAO;EAAQ,YAAY,SAAS,KAAA,IAAY,KAAK;CAAO;AACvE;;;;;AC1BA,SAAgB,wBACd,SACsB;CACtB,MAAM,EAAE,OAAO,cAAc,kBAAkB;CAC/C,MAAM,SAAS,UAAU;CACzB,MAAM,CAAC,QAAQ,aAAa,SAAiB;CAC7C,MAAM,QAAQ,WAAW,WAAW,OAAO;CAC3C,MAAM,OAAO,QAAQ,WAAW,IAAI,OAAO,EAAE,CAAC,OAAO,KAAK,KAAA;CAC1D,MAAM,KAAK,SAAS,UAAU;CAC9B,OAAO;EACL,OAAO,QAAQ,MAAM,MAAM,EAAE,OAAO,OAAO,EAAE;EAC7C,SAAS,QAAQ,KAAA,IAAa,UAAU,CAAC;EACzC,MAAM;EACN,aAAa,CAAC,CAAC,aAAa,UAAU,OAAO,OAAO;CACtD;AACF;;;;ACJA,SAAgB,UAAU,QAAoC;CAC5D,OAAO,OAAO,OAAO,OAAO,UAAsC,CAAC,CAChE,SAAS,UAAU;EAClB,MAAM,MAAO,MAAM,QAChB,YAAY;EACf,MAAM,EAAE,IAAI,SAAS,aAAa;EAIlC,IAAI,CAAC,OAAO,SAAS,SAAS,GAAG,GAAG,OAAO,CAAC;EAC5C,OAAO,CAAC;GAAE,GAAG;GAAK;GAAS,IAAI,SAAS,QAAQ,UAAU,IAAI;EAAE,CAAC;CACnE,CAAC,CAAC,CACD,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,cAAc,EAAE,KAAK,CAAC;AACvE;AAEA,SAAgB,eAAe;CAC7B,MAAM,SAAS,UAAU;CACzB,MAAM,aAAa,cAAc;CACjC,OAAO,cAAc,UAAU,MAAM,GAAG,CAAC,QAAQ,UAAU,CAAC;AAC9D;;AAGA,SAAgB,iBAA0C;CACxD,OAAO,WAAW,EAChB,SAAS,YACP,QAAQ,UAAU,UAAU,MAAM,WAAW,YAAY,KAAA,CAAS,CAAC,EAC/D,WAAW,QACnB,CAAC;AACH"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"views-CAbBIFzO.js","names":["modalViews"],"sources":["../src/views/index-table.tsx","../src/views/directory-listing.tsx","../src/views/document-history.tsx","../src/views/document-json.tsx","../src/views/editor-host.tsx","../src/views/content.tsx","../src/views/home.tsx","../src/views/theme-toggle.tsx","../src/views/modals.tsx","../src/views/layout.tsx","../src/views/not-found-page.tsx","../src/views/route-views.tsx","../src/views/settings.tsx","../src/views/settings-packages.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\n\n/** `YYYY-MM-DD HH:mm` in UTC, as a server index lists it; \"-\" when unknown. */\nexport function formatModified(iso: string | undefined) {\n if (!iso) return \"-\";\n const date = new Date(iso);\n if (Number.isNaN(date.getTime())) return \"-\";\n return date.toISOString().slice(0, 16).replace(\"T\", \" \");\n}\n\n/** An \"Index of\" page: a title above a name / type / last modified table. */\nexport function IndexTable(props: {\n title: ReactNode;\n label: string;\n children: ReactNode;\n}) {\n return (\n <div className=\"p-6 font-mono text-sm text-core-fg\">\n <h1 className=\"mb-4 text-xl font-semibold\">{props.title}</h1>\n <table aria-label={props.label} className=\"w-full border-collapse\">\n <thead>\n <tr className=\"border-b border-core-border text-left text-core-muted\">\n <th className=\"py-1 pr-6 font-normal\">Name</th>\n <th className=\"py-1 pr-6 font-normal\">Type</th>\n <th className=\"py-1 font-normal\">Last modified</th>\n </tr>\n </thead>\n <tbody>{props.children}</tbody>\n </table>\n </div>\n );\n}\n\nexport function IndexRow(props: {\n name: ReactNode;\n type: ReactNode;\n modified: ReactNode;\n}) {\n return (\n <tr className=\"border-b border-core-border/50 hover:bg-core-hover\">\n <td className=\"py-1 pr-6\">{props.name}</td>\n <td className=\"py-1 pr-6 text-core-muted\">{props.type}</td>\n <td className=\"py-1 whitespace-nowrap text-core-muted\">\n {props.modified}\n </td>\n </tr>\n );\n}\n\nexport const linkClass = \"text-core-link underline-offset-2 hover:underline\";\n","import {\n getDriveName,\n makeDriveSlug,\n makeNodeSlug,\n useDocumentById,\n useLinkProps,\n} from \"@powerhousedao/reactor-browser\";\nimport type {\n DocumentDriveDocument,\n FolderNode,\n Node,\n} from \"@powerhousedao/shared/document-drive\";\nimport { isFileNode } from \"@powerhousedao/shared/document-drive\";\nimport type { ReactNode } from \"react\";\nimport { Component, Fragment, Suspense } from \"react\";\nimport type { DriveViewProps } from \"../router/route-components.js\";\nimport {\n formatModified,\n IndexRow,\n IndexTable,\n linkClass,\n} from \"./index-table.js\";\n\nfunction NodeLink(props: {\n drive: DocumentDriveDocument;\n node: Node | undefined;\n children: ReactNode;\n}) {\n const link = useLinkProps({\n driveSlug: makeDriveSlug(props.drive),\n nodeSlug: props.node ? makeNodeSlug(props.node) : undefined,\n });\n return (\n <a {...link} className={linkClass}>\n {props.children}\n </a>\n );\n}\n\nfunction HomeLink(props: { children: ReactNode }) {\n const link = useLinkProps(undefined);\n return (\n <a {...link} className={linkClass}>\n {props.children}\n </a>\n );\n}\n\n// A row's date is a nicety: it shows \"-\" when the document can't load.\nclass Quiet extends Component<{ children: ReactNode }, { failed: boolean }> {\n state = { failed: false };\n static getDerivedStateFromError() {\n return { failed: true };\n }\n render() {\n return this.state.failed ? \"-\" : this.props.children;\n }\n}\n\nfunction DocumentModified({ id }: { id: string }) {\n const [document] = useDocumentById(id);\n return formatModified(document?.header.lastModifiedAtUtcIso);\n}\n\n/** The folders from the drive's root down to `folder`. */\nfunction folderPath(nodes: readonly Node[], folder: FolderNode | undefined) {\n const path: Node[] = [];\n for (let node: Node | undefined = folder; node;) {\n path.unshift(node);\n const parentId: string | null | undefined = node.parentFolder;\n node = parentId ? nodes.find((n) => n.id === parentId) : undefined;\n }\n return path;\n}\n\nconst byKindThenName = (a: Node, b: Node) =>\n a.kind === b.kind\n ? a.name.localeCompare(b.name)\n : a.kind === \"folder\"\n ? -1\n : 1;\n\n/** Core's drive fallback: a listing of the open folder; an open document shows instead. */\nexport function DirectoryListing({ drive, folder, children }: DriveViewProps) {\n if (children) return children;\n const nodes = drive.state.global.nodes;\n const path = folderPath(nodes, folder);\n const parent = path.at(-2);\n const entries = nodes\n .filter((node) => (node.parentFolder ?? null) === (folder?.id ?? null))\n .sort(byKindThenName);\n const title = (\n <>\n Index of /\n <NodeLink drive={drive} node={undefined}>\n {getDriveName(drive)}\n </NodeLink>\n /\n {path.map((node) => (\n <Fragment key={node.id}>\n <NodeLink drive={drive} node={node}>\n {node.name}\n </NodeLink>\n /\n </Fragment>\n ))}\n </>\n );\n return (\n <IndexTable title={title} label=\"Directory listing\">\n <IndexRow\n name={\n folder ? (\n <NodeLink drive={drive} node={parent}>\n Parent Directory\n </NodeLink>\n ) : (\n <HomeLink>Parent Directory</HomeLink>\n )\n }\n type=\"-\"\n modified=\"-\"\n />\n {entries.map((node) => (\n <IndexRow\n key={node.id}\n name={\n <NodeLink drive={drive} node={node}>\n {node.kind === \"folder\" ? `${node.name}/` : node.name}\n </NodeLink>\n }\n type={isFileNode(node) ? node.documentType : \"Folder\"}\n modified={\n node.kind === \"folder\" ? (\n \"-\"\n ) : (\n <Quiet>\n <Suspense fallback=\"…\">\n <DocumentModified id={node.id} />\n </Suspense>\n </Quiet>\n )\n }\n />\n ))}\n </IndexTable>\n );\n}\n","import {\n hideRevisionHistory,\n setRevisionHistoryPage,\n useReactorClient,\n useRevisionHistoryPage,\n} from \"@powerhousedao/reactor-browser\";\nimport type { Operation } from \"@powerhousedao/shared/document-model\";\nimport { useEffect, useState } from \"react\";\nimport type { DocumentViewProps } from \"../router/route-components.js\";\nimport { formatModified, linkClass } from \"./index-table.js\";\n\nconst pagerClass = `${linkClass} disabled:text-core-muted disabled:no-underline`;\n\n/** Operations per scope on a history page. */\nexport const HISTORY_PAGE_SIZE = 50;\n\n/** Page `page`'s index range of a scope with `count` operations, newest first. */\nexport function historyPageRange(count: number, page: number) {\n const end = Math.max(count - (page - 1) * HISTORY_PAGE_SIZE, 0);\n return { start: Math.max(end - HISTORY_PAGE_SIZE, 0), end };\n}\n\ntype PageState =\n | { status: \"loading\" }\n | { status: \"ready\"; operations: Operation[] }\n | { status: \"error\"; error: Error };\n\n// Fetches only the page's range: `sinceRevision` and a limit of the page size.\nfunction useScopePage(\n documentId: string,\n scope: string,\n start: number,\n end: number,\n) {\n const client = useReactorClient();\n // Each result is kept with its request; a newer request reads as loading.\n const key = `${documentId}:${scope}:${start}:${end}`;\n const [result, setResult] = useState<{ key: string; state: PageState }>();\n const empty = !client || end <= start;\n useEffect(() => {\n if (empty) return;\n const controller = new AbortController();\n const setState = (state: PageState) => setResult({ key, state });\n client\n .getOperations(\n documentId,\n { scopes: [scope] },\n { sinceRevision: start },\n { cursor: \"\", limit: end - start },\n controller.signal,\n )\n .then(\n (page) => {\n const operations = page.results\n .filter((operation) => operation.index < end)\n .sort((a, b) => b.index - a.index);\n setState({ status: \"ready\", operations });\n },\n (reason: unknown) => {\n if (controller.signal.aborted) return;\n setState({\n status: \"error\",\n error: reason instanceof Error ? reason : new Error(String(reason)),\n });\n },\n );\n return () => controller.abort();\n }, [client, empty, key, documentId, scope, start, end]);\n if (empty) return { status: \"ready\", operations: [] } as const;\n return result?.key === key ? result.state : ({ status: \"loading\" } as const);\n}\n\nfunction ScopeOperations(props: {\n documentId: string;\n scope: string;\n count: number;\n page: number;\n}) {\n const { start, end } = historyPageRange(props.count, props.page);\n const state = useScopePage(props.documentId, props.scope, start, end);\n return (\n <section className=\"mb-6\">\n <h2 className=\"mb-2 font-semibold\">\n {props.scope}{\" \"}\n <span className=\"font-normal text-core-muted\">\n {end > start\n ? `${end - start} of ${props.count}, ${start}-${end - 1}`\n : `0 of ${props.count}`}\n </span>\n </h2>\n {state.status === \"error\" ? (\n <p className=\"text-core-danger\">{state.error.message}</p>\n ) : state.status === \"loading\" ? (\n <p className=\"text-core-muted\">Loading…</p>\n ) : state.operations.length === 0 ? (\n <p className=\"text-core-muted\">No operations on this page.</p>\n ) : (\n <table\n aria-label={`${props.scope} operations`}\n className=\"w-full border-collapse\"\n >\n <thead>\n <tr className=\"border-b border-core-border text-left text-core-muted\">\n <th className=\"py-1 pr-6 font-normal\">Index</th>\n <th className=\"py-1 pr-6 font-normal\">Action</th>\n <th className=\"py-1 font-normal\">Timestamp</th>\n </tr>\n </thead>\n <tbody>\n {state.operations.map((operation) => (\n <tr\n key={operation.index}\n className=\"border-b border-core-border/50 hover:bg-core-hover\"\n >\n <td className=\"py-1 pr-6\">{operation.index}</td>\n <td className=\"py-1 pr-6\">{operation.action.type}</td>\n <td className=\"py-1\">\n {formatModified(operation.timestampUtcMs)}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n )}\n </section>\n );\n}\n\n/** Core's revision history: each scope's operations, newest first, a page at a time. */\nexport function DocumentHistory({ document }: DocumentViewProps) {\n const page = useRevisionHistoryPage();\n const { header } = document;\n const scopes = Object.entries(header.revision);\n const pageCount = Math.max(\n 1,\n ...scopes.map(([, count]) => Math.ceil(count / HISTORY_PAGE_SIZE)),\n );\n const pager = (\n <nav aria-label=\"History pages\" className=\"flex items-center gap-4\">\n <button\n type=\"button\"\n className={pagerClass}\n disabled={page <= 1}\n onClick={() => setRevisionHistoryPage(page - 1)}\n >\n Newer\n </button>\n <span className=\"text-core-muted\">\n Page {page} of {pageCount}\n </span>\n <button\n type=\"button\"\n className={pagerClass}\n disabled={page >= pageCount}\n onClick={() => setRevisionHistoryPage(page + 1)}\n >\n Older\n </button>\n </nav>\n );\n return (\n <div className=\"p-6 font-mono text-sm text-core-fg\">\n <div className=\"mb-4 flex items-baseline justify-between\">\n <h1 className=\"text-xl font-semibold\">\n History of {header.name || header.id}\n </h1>\n <button\n type=\"button\"\n className={linkClass}\n onClick={hideRevisionHistory}\n >\n Close\n </button>\n </div>\n <div className=\"mb-4\">{pager}</div>\n {scopes.length === 0 ? (\n <p className=\"text-core-muted\">No operations yet.</p>\n ) : (\n scopes.map(([scope, count]) => (\n <ScopeOperations\n key={scope}\n documentId={header.id}\n scope={scope}\n count={count}\n page={page}\n />\n ))\n )}\n </div>\n );\n}\n","import type { ReactNode } from \"react\";\nimport type { DocumentViewProps } from \"../router/route-components.js\";\nimport { formatModified } from \"./index-table.js\";\n\nfunction Leaf({ value }: { value: unknown }) {\n if (value === null) return <span className=\"text-core-muted\">null</span>;\n if (typeof value === \"string\") {\n return <span className=\"text-core-string\">{JSON.stringify(value)}</span>;\n }\n return <span className=\"text-core-number\">{JSON.stringify(value)}</span>;\n}\n\nfunction JsonNode(props: { name: ReactNode; value: unknown; open?: boolean }) {\n const { name, value } = props;\n if (value === null || typeof value !== \"object\") {\n return (\n <div className=\"pl-4\">\n <span className=\"text-core-muted\">{name}: </span>\n <Leaf value={value} />\n </div>\n );\n }\n const entries = Object.entries(value as Record<string, unknown>);\n const isArray = Array.isArray(value);\n const summary = isArray ? `[${entries.length}]` : `{${entries.length}}`;\n return (\n <details open={props.open} className=\"pl-4\">\n <summary className=\"cursor-pointer select-none\">\n <span className=\"text-core-muted\">{name}: </span>\n <span className=\"text-core-muted\">{summary}</span>\n </summary>\n {entries.map(([key, child]) => (\n <JsonNode key={key} name={key} value={child} />\n ))}\n </details>\n );\n}\n\n/** Core's document fallback: the document's state, read-only, as a JSON tree. */\nexport function DocumentJson({ document }: DocumentViewProps) {\n const { header } = document;\n const revisions = Object.entries(header.revision);\n const operations = revisions.reduce((sum, [, count]) => sum + count, 0);\n const state = document.state as Record<string, unknown>;\n const scopes = [\"global\", \"local\"].filter((scope) => scope in state);\n return (\n <div className=\"p-6 font-mono text-sm text-core-fg\">\n <h1 className=\"mb-1 text-xl font-semibold\">{header.name || header.id}</h1>\n <dl className=\"mb-4 grid grid-cols-[max-content_1fr] gap-x-4 text-core-muted\">\n <dt>Type</dt>\n <dd>{header.documentType}</dd>\n <dt>Id</dt>\n <dd>{header.id}</dd>\n <dt>Last modified</dt>\n <dd>{formatModified(header.lastModifiedAtUtcIso)}</dd>\n <dt>Operations</dt>\n <dd data-testid=\"operation-count\">\n {operations}\n {revisions.length > 0 &&\n ` (${revisions.map(([scope, count]) => `${scope} ${count}`).join(\", \")})`}\n </dd>\n </dl>\n <p className=\"mb-2 text-core-muted\">\n No editor is installed for this document type; its state is shown\n read-only.\n </p>\n <div aria-label=\"Document state\" className=\"-ml-4\">\n {scopes.map((scope) => (\n <JsonNode key={scope} name={scope} value={state[scope]} open />\n ))}\n </div>\n </div>\n );\n}\n","import type { EditorProps } from \"@powerhousedao/shared/document-model\";\nimport type { ErrorInfo, ReactNode } from \"react\";\nimport { Component, Suspense } from \"react\";\n\n/** What core passes a document editor; drive apps get `DriveViewProps`. */\nexport type DocumentEditorProps = EditorProps & {\n documentId: string;\n context?: { readMode?: boolean; selectedTimelineRevision?: number };\n};\n\ntype BoundaryProps = {\n resetKeys: readonly unknown[];\n label: string;\n children: ReactNode;\n};\ntype BoundaryState = { error?: unknown; keys: readonly unknown[] };\n\nclass EditorErrorBoundary extends Component<BoundaryProps, BoundaryState> {\n state: BoundaryState = { keys: this.props.resetKeys };\n\n static getDerivedStateFromError(error: unknown): Partial<BoundaryState> {\n return { error };\n }\n\n // A new document or drive gets a fresh try.\n static getDerivedStateFromProps(\n props: BoundaryProps,\n state: BoundaryState,\n ): Partial<BoundaryState> | null {\n const changed =\n props.resetKeys.length !== state.keys.length ||\n props.resetKeys.some((key, i) => !Object.is(key, state.keys[i]));\n return changed ? { error: undefined, keys: props.resetKeys } : null;\n }\n\n componentDidCatch(error: unknown, info: ErrorInfo) {\n console.error(`[connect] ${this.props.label} failed:`, error, info);\n }\n\n render() {\n const { error } = this.state;\n if (error === undefined) return this.props.children;\n return (\n <div role=\"alert\" className=\"p-6 text-core-fg\">\n <h2 className=\"font-semibold\">The {this.props.label} failed</h2>\n <pre className=\"mt-2 overflow-x-auto text-sm text-core-muted\">\n {error instanceof Error ? error.message : JSON.stringify(error)}\n </pre>\n </div>\n );\n }\n}\n\nexport type EditorHostProps = {\n /** A change shows the editor again after an error. */\n resetKeys?: readonly unknown[];\n /** Names what failed, e.g. \"editor\" or \"drive app\". */\n label?: string;\n children: ReactNode;\n};\n\n/** Hosts a drive app or an editor: it loads in Suspense, and an error stays inside. */\nexport function EditorHost({\n resetKeys = [],\n label = \"editor\",\n children,\n}: EditorHostProps) {\n return (\n <EditorErrorBoundary resetKeys={resetKeys} label={label}>\n <Suspense\n name=\"EditorHost\"\n fallback={\n <p role=\"status\" className=\"p-6 text-core-muted\">\n Loading {label}…\n </p>\n }\n >\n {children}\n </Suspense>\n </EditorErrorBoundary>\n );\n}\n","import {\n DriveProvider,\n EditorDocumentProvider,\n useAppModuleById,\n useDispatch,\n useDocument,\n useEditorModuleFor,\n useRevisionHistoryVisible,\n} from \"@powerhousedao/reactor-browser\";\nimport type { PHDocument } from \"@powerhousedao/shared/document-model\";\nimport { Outlet } from \"@tanstack/react-router\";\nimport { useMemo } from \"react\";\nimport { useDriveRouteData } from \"../router/route-data.js\";\nimport { DirectoryListing } from \"./directory-listing.js\";\nimport { DocumentHistory } from \"./document-history.js\";\nimport { DocumentJson } from \"./document-json.js\";\nimport { EditorHost } from \"./editor-host.js\";\n\n/** `document`: its revision history while `?history` is set, else its\n * editor (given `document` and `dispatch`), else its JSON. */\nexport function DocumentView({ document }: { document: PHDocument }) {\n const [, dispatch] = useDispatch(document);\n const editor = useEditorModuleFor(document);\n const historyVisible = useRevisionHistoryVisible();\n if (historyVisible) return <DocumentHistory document={document} />;\n if (!editor) return <DocumentJson document={document} />;\n const Editor = editor.Component;\n return (\n <div\n className=\"relative h-full\"\n data-editor={editor.config.id}\n data-document-type={document.header.documentType}\n >\n <EditorDocumentProvider document={document} dispatch={dispatch}>\n <Editor\n documentId={document.header.id}\n document={document}\n dispatch={dispatch}\n context={{ readMode: false }}\n />\n </EditorDocumentProvider>\n </div>\n );\n}\n\nfunction OpenDocument({ documentId }: { documentId: string }) {\n const document = useDocument(documentId);\n return document ? <DocumentView document={document} /> : null;\n}\n\n/** The drive layout: the drive app its `preferredEditor` names (given the\n * drive as `document`), else the directory listing, around the node route. */\nexport function DriveLayout() {\n const { drive, folder, documentId } = useDriveRouteData();\n const app = useAppModuleById(drive?.header.meta?.preferredEditor);\n const [, dispatch] = useDispatch(drive);\n const nodeId = documentId ?? folder?.id;\n const context = useMemo(\n () => drive && { drive, dispatch, folder, nodeId },\n [drive, dispatch, folder, nodeId],\n );\n if (!drive || !context) return null;\n const App = app?.Component;\n // Drive apps show their own view until they get a document to show.\n const document = documentId ? <Outlet /> : null;\n return (\n <DriveProvider value={context}>\n <EditorHost\n resetKeys={[drive.header.id]}\n label={App ? \"drive app\" : \"drive view\"}\n >\n {App ? (\n <App document={drive} dispatch={dispatch}>\n {document}\n </App>\n ) : (\n <DirectoryListing drive={drive} folder={folder}>\n {document}\n </DirectoryListing>\n )}\n </EditorHost>\n </DriveProvider>\n );\n}\n\n/** The node route: the open document, or nothing on a folder or the drive's root. */\nexport function NodeView() {\n const { documentId } = useDriveRouteData();\n if (!documentId) return null;\n return (\n <EditorHost resetKeys={[documentId]} label=\"editor\">\n <OpenDocument documentId={documentId} />\n </EditorHost>\n );\n}\n","import {\n getDriveName,\n makeDriveSlug,\n preloadDrive,\n useAppModuleById,\n useDrives,\n useLinkProps,\n} from \"@powerhousedao/reactor-browser\";\nimport type { DocumentDriveDocument } from \"@powerhousedao/shared/document-drive\";\nimport {\n formatModified,\n IndexRow,\n IndexTable,\n linkClass,\n} from \"./index-table.js\";\n\nfunction DriveRow({ drive }: { drive: DocumentDriveDocument }) {\n const app = useAppModuleById(drive.header.meta?.preferredEditor);\n const link = useLinkProps({ driveSlug: makeDriveSlug(drive) });\n return (\n <IndexRow\n name={\n <a\n {...link}\n className={linkClass}\n onMouseEnter={() => {\n link.onMouseEnter();\n preloadDrive(drive);\n }}\n >\n {getDriveName(drive)}/\n </a>\n }\n type={app?.config.name ?? \"Drive\"}\n modified={formatModified(drive.header.lastModifiedAtUtcIso)}\n />\n );\n}\n\n/** Core's home: an index of the drives. */\nexport function DriveIndex() {\n const drives = useDrives();\n // Empty until the reactor lists its drives, e.g. in the prerendered shell.\n if (!drives) return null;\n return (\n <IndexTable title=\"Index of /\" label=\"Drives\">\n {drives.map((drive) => (\n <DriveRow key={drive.header.id} drive={drive} />\n ))}\n {!drives.length && (\n <tr>\n <td colSpan={3} className=\"py-2 text-core-muted\">\n No drives yet.\n </td>\n </tr>\n )}\n </IndexTable>\n );\n}\n","import { useTheme } from \"@powerhousedao/reactor-browser\";\n\n/** Flips between light and dark; the first flip pins the system theme's opposite. */\nexport function ThemeToggle({ className }: { className?: string }) {\n const { theme, setTheme } = useTheme();\n const next = theme === \"dark\" ? \"light\" : \"dark\";\n return (\n <button\n type=\"button\"\n aria-label={`Switch to ${next} theme`}\n title={`Switch to ${next} theme`}\n className={\n className ??\n \"rounded px-2 py-1 text-core-muted hover:bg-core-hover hover:text-core-fg\"\n }\n onClick={() => setTheme(next)}\n >\n {theme === \"dark\" ? \"☾\" : \"☀\"}\n </button>\n );\n}\n","// Core's modals: the ones editors, drive apps and the reactor open, in core's plain style.\nimport {\n addDocument,\n closePHModal,\n deleteNode,\n exportFile,\n getDocumentUpgradePreview,\n getDriveName,\n getNodeTarget,\n logout,\n openRenown,\n openTarget,\n setSelectedNode,\n showPHModal,\n upgradeDocument,\n useDocumentById,\n useDocumentModelModuleById,\n useModelRegistry,\n useNodeById,\n useNodeParentFolderById,\n usePHModal,\n useRenownAuth,\n useRenownLoginMethods,\n useUser,\n validateDocument,\n} from \"@powerhousedao/reactor-browser\";\nimport type { PHModal } from \"@powerhousedao/reactor-browser\";\nimport type { ReactNode } from \"react\";\nimport { createElement, useEffect, useId, useState } from \"react\";\nimport type { ModalFrameProps } from \"../router/modal-frame.js\";\nimport { ModalDialog, useModalFrame } from \"../router/modal-frame.js\";\nimport { useCreateDocumentTarget } from \"../router/create-target.js\";\nimport { useDriveRouteData } from \"../router/route-data.js\";\n\nconst buttonClass =\n \"rounded border border-core-border px-3 py-1 text-sm hover:bg-core-hover disabled:opacity-50\";\nconst primaryClass = `${buttonClass} font-semibold`;\n\n/** Core's modal frame: title, content, and the buttons on the right. */\nfunction CoreModalFrame({\n title,\n titleId,\n actions,\n children,\n}: ModalFrameProps) {\n return (\n <ModalDialog\n labelledBy={titleId}\n className=\"m-auto w-96 max-w-[calc(100vw-2rem)] rounded border border-core-border bg-core-bg p-5 text-core-fg backdrop:bg-black/30\"\n >\n <h2 id={titleId} className=\"mb-3 text-lg font-semibold\">\n {title}\n </h2>\n <div className=\"flex flex-col gap-2 text-sm\">{children}</div>\n <div className=\"mt-4 flex justify-end gap-2\">{actions}</div>\n </ModalDialog>\n );\n}\n\n/** A modal in the routes' frame, else core's. */\nfunction Dialog(props: {\n title: string;\n children: ReactNode;\n actions?: ReactNode;\n}) {\n const Frame = useModalFrame() ?? CoreModalFrame;\n const titleId = useId();\n const actions = props.actions ?? (\n <button type=\"button\" className={buttonClass} onClick={closePHModal}>\n Close\n </button>\n );\n // A registered frame, not one made here.\n return createElement(Frame, {\n title: props.title,\n titleId,\n actions,\n children: props.children,\n });\n}\n\nconst Cancel = () => (\n <button type=\"button\" className={buttonClass} onClick={closePHModal}>\n Cancel\n </button>\n);\n\nfunction useModal<T extends PHModal[\"type\"]>(type: T) {\n const modal = usePHModal();\n return modal?.type === type\n ? (modal as Extract<PHModal, { type: T }>)\n : undefined;\n}\n\n/** A new document of the modal's type: in its `driveId`, else the open drive\n * and folder, else a drive picked here. */\nexport function CreateDocumentModal() {\n const modal = useModal(\"createDocument\");\n const module = useDocumentModelModuleById(\n modal?.documentType,\n modal?.documentModelVersion,\n );\n const { folder } = useDriveRouteData();\n const target = useCreateDocumentTarget(modal?.driveId);\n const { drive } = target;\n const [name, setName] = useState(\"\");\n const typeName = module?.documentModel.global.name ?? modal?.documentType;\n const create = async () => {\n if (!drive || !module || !modal) return;\n closePHModal();\n const node = await addDocument(\n drive.header.id,\n name.trim() || `New ${typeName}`,\n module.documentModel.global.id,\n target.isOpenDrive ? folder?.id : undefined,\n undefined,\n undefined,\n undefined,\n modal.documentModelVersion,\n );\n // Opens it in its drive, wherever the modal was opened.\n openTarget(getNodeTarget(node, drive));\n };\n return (\n <Dialog\n title={`New ${typeName ?? \"document\"}`}\n actions={\n <>\n <Cancel />\n <button\n type=\"submit\"\n form=\"create-document\"\n className={primaryClass}\n disabled={!drive || !module}\n >\n Create\n </button>\n </>\n }\n >\n <form\n id=\"create-document\"\n className=\"flex flex-col gap-2\"\n onSubmit={(event) => {\n event.preventDefault();\n void create();\n }}\n >\n <label className=\"flex flex-col gap-1\">\n Name\n <input\n autoFocus\n value={name}\n onChange={(event) => setName(event.target.value)}\n placeholder={`New ${typeName}`}\n className=\"rounded border border-core-border bg-core-bg px-2 py-1\"\n />\n </label>\n {!!target.choices?.length && (\n <label className=\"flex flex-col gap-1\">\n Drive\n <select\n value={drive?.header.id ?? \"\"}\n onChange={(event) => target.pick(event.target.value)}\n className=\"rounded border border-core-border bg-core-bg px-2 py-1\"\n >\n {!drive && <option value=\"\">Choose a drive</option>}\n {target.choices.map((choice) => (\n <option key={choice.header.id} value={choice.header.id}>\n {getDriveName(choice)}\n </option>\n ))}\n </select>\n </label>\n )}\n </form>\n {target.choices?.length === 0 && (\n <p className=\"text-core-muted\">\n There's no drive to create it in.{\" \"}\n <button\n type=\"button\"\n className=\"underline\"\n onClick={() => showPHModal({ type: \"addDrive\" })}\n >\n Add a drive\n </button>\n </p>\n )}\n </Dialog>\n );\n}\n\n/** Signs in with Renown: the configured methods, else its portal. */\nexport function LoginModal() {\n const user = useUser();\n const methods = useRenownLoginMethods();\n const { login, pending, error } = useRenownAuth();\n useEffect(() => {\n if (user) closePHModal();\n }, [user]);\n return (\n <Dialog title=\"Log in\">\n {methods.length ? (\n methods.map((method) => (\n <button\n key={method.id}\n type=\"button\"\n className={buttonClass}\n disabled={pending}\n onClick={() => {\n closePHModal();\n login(undefined, method.id);\n }}\n >\n {method.label}\n </button>\n ))\n ) : (\n <button\n type=\"button\"\n className={primaryClass}\n onClick={() => openRenown()}\n >\n Continue with Renown\n </button>\n )}\n {error && <p className=\"text-core-muted\">{error.message}</p>}\n </Dialog>\n );\n}\n\n/** Deletes a file or folder, then opens its parent. */\nexport function DeleteItemModal() {\n const modal = useModal(\"deleteItem\");\n const node = useNodeById(modal?.id);\n const parent = useNodeParentFolderById(modal?.id);\n const { drive } = useDriveRouteData();\n const remove = async () => {\n closePHModal();\n if (!drive || !modal) return;\n await deleteNode(drive.header.id, modal.id);\n setSelectedNode(parent);\n };\n return (\n <Dialog\n title={`Delete ${node?.name ?? \"item\"}?`}\n actions={\n <>\n <Cancel />\n <button\n type=\"button\"\n className={primaryClass}\n onClick={() => void remove()}\n >\n Delete\n </button>\n </>\n }\n >\n <p>\n {node?.kind === \"folder\"\n ? \"The folder and everything in it will be deleted.\"\n : \"The document will be deleted.\"}\n </p>\n </Dialog>\n );\n}\n\n/** Upgrades a document to its model's latest version. */\nexport function ConfirmDocumentUpgradeModal() {\n const modal = useModal(\"confirmDocumentUpgrade\");\n const [document] = useDocumentById(modal?.documentId);\n const registry = useModelRegistry();\n const preview = document\n ? getDocumentUpgradePreview(document, registry)\n : undefined;\n if (!document || !preview) return null;\n const upgrade = () => {\n closePHModal();\n upgradeDocument(document.header.id).catch((error: unknown) => {\n console.error(\"[connect] the document upgrade failed:\", error);\n });\n };\n return (\n <Dialog\n title=\"Update document\"\n actions={\n <>\n <Cancel />\n <button type=\"button\" className={primaryClass} onClick={upgrade}>\n Update document\n </button>\n </>\n }\n >\n <p>\n From version {preview.fromVersion} to {preview.toVersion}. Existing\n content is migrated; this can't be undone.\n </p>\n {preview.steps\n .filter((step) => step.description)\n .map((step) => (\n <p key={step.toVersion}>\n v{step.toVersion}: {step.description}\n </p>\n ))}\n </Dialog>\n );\n}\n\n/** Exports a document that has validation errors, after listing them. */\nexport function DownloadDocumentWithErrorsModal() {\n const modal = useModal(\"downloadDocumentWithErrors\");\n const [document] = useDocumentById(modal?.documentId);\n if (!document) return null;\n const errors = validateDocument(document);\n const download = () => {\n closePHModal();\n exportFile(document).catch((error: unknown) => {\n console.error(\"[connect] the export failed:\", error);\n });\n };\n return (\n <Dialog\n title=\"The document has errors\"\n actions={\n <>\n <Cancel />\n <button type=\"button\" className={primaryClass} onClick={download}>\n Export anyway\n </button>\n </>\n }\n >\n <ul className=\"list-disc pl-5 text-core-muted\">\n {errors.map((error, index) => (\n <li key={index}>{error.message}</li>\n ))}\n </ul>\n </Dialog>\n );\n}\n\n/** A document whose model version no installed package provides. */\nexport function DocumentVersionUnsupportedModal() {\n const modal = useModal(\"documentVersionUnsupported\");\n if (!modal) return null;\n const { documentType, requiredVersion, availableVersions } = modal;\n return (\n <Dialog\n title=\"Document version not supported\"\n actions={\n <>\n <Cancel />\n <button\n type=\"button\"\n className={primaryClass}\n onClick={() => {\n closePHModal();\n openTarget({ settings: \"packages\" });\n }}\n >\n Show packages\n </button>\n </>\n }\n >\n <p>\n This document needs version {requiredVersion} of \"{documentType}\"; the\n installed packages provide {availableVersions.join(\", \")}.\n </p>\n </Dialog>\n );\n}\n\n/** A protected drive that couldn't be added: sign in, or as someone else. */\nexport function DriveAuthRequiredModal() {\n const user = useUser();\n return (\n <Dialog\n title={user ? \"No access to this drive\" : \"Log in to access this drive\"}\n actions={\n <>\n <Cancel />\n {user ? (\n <button\n type=\"button\"\n className={primaryClass}\n onClick={() => {\n closePHModal();\n void logout();\n }}\n >\n Log out\n </button>\n ) : (\n <button\n type=\"button\"\n className={primaryClass}\n onClick={() => showPHModal({ type: \"login\" })}\n >\n Log in\n </button>\n )}\n </>\n }\n >\n <p>\n {user\n ? \"The account you're signed in with can't open this drive.\"\n : \"This drive is only open to signed-in users.\"}\n </p>\n </Dialog>\n );\n}\n","import {\n initTheme,\n useLinkProps,\n useTheme,\n} from \"@powerhousedao/reactor-browser\";\nimport { HeadContent, Link, Outlet, useRouter } from \"@tanstack/react-router\";\nimport { Suspense } from \"react\";\nimport type { ConnectModals } from \"../router/modals.js\";\nimport { CORE_MODAL_VIEWS, ModalOutlet } from \"../router/modals.js\";\nimport { ContentOutlet } from \"../router/route-components.js\";\nimport { useNavRoutes, useSidebarMode } from \"../router/sidebar.js\";\nimport { appTitle } from \"../router/titles.js\";\nimport { useIsEmbedded } from \"../runtime/embedded.js\";\nimport { ThemeToggle } from \"./theme-toggle.js\";\nimport { linkClass } from \"./index-table.js\";\nimport * as modalViews from \"./modals.js\";\n\n// The layout shares the modals' chunk, so it renders them directly.\nconst coreModals: ConnectModals = Object.fromEntries(\n Object.entries(CORE_MODAL_VIEWS).map(([type, name]) => [\n type,\n modalViews[name],\n ]),\n);\n\n/** Core's root: the page, in the theme. */\nexport function CoreRoot() {\n initTheme();\n useTheme();\n return (\n <div className=\"h-screen overflow-auto bg-core-bg text-core-fg\">\n {/* The matched routes' head: the document title. */}\n <HeadContent />\n <Suspense name=\"CoreRoot\">\n <Outlet />\n </Suspense>\n <ModalOutlet modals={coreModals} />\n </div>\n );\n}\n\n/** Core's header: home, the `staticData.nav` routes, settings and the theme. */\nexport function CoreHeader() {\n const appName = useRouter().options.context.appName;\n const home = useLinkProps(undefined);\n const settings = useLinkProps({ settings: true });\n const navRoutes = useNavRoutes();\n return (\n <header className=\"flex items-center gap-4 border-b border-core-border px-4 py-2 text-sm\">\n <a {...home} className=\"font-semibold text-core-fg\">\n {appTitle(appName)}\n </a>\n <nav aria-label=\"Pages\" className=\"flex gap-3\">\n {navRoutes.map((item) => (\n // `to` is a plain string: the routes are only known at runtime.\n <Link key={item.routeId} to={item.to as never} className={linkClass}>\n {item.label}\n </Link>\n ))}\n </nav>\n <div className=\"ml-auto flex items-center gap-2\">\n <a {...settings} className={linkClass}>\n Settings\n </a>\n <ThemeToggle />\n </div>\n </header>\n );\n}\n\n/** Core's `_app` layout: the header above the matched route. Embedded mode\n * and `staticData.sidebar: \"hidden\"` drop the header. */\nexport function CoreLayout() {\n const isEmbedded = useIsEmbedded();\n const hidden = useSidebarMode() === \"hidden\";\n return (\n <div className=\"flex h-full flex-col\">\n {!isEmbedded && !hidden && <CoreHeader />}\n <main className=\"relative min-h-0 flex-1\">\n <ContentOutlet />\n </main>\n </div>\n );\n}\n","import { useLinkProps } from \"@powerhousedao/reactor-browser\";\nimport { useRouterState } from \"@tanstack/react-router\";\n\n/** The 404 page: what was asked for, and a way home. */\nexport function NotFoundPage({ driveSlug }: { driveSlug?: string }) {\n const pathname = useRouterState({ select: (s) => s.location.pathname });\n const home = useLinkProps(undefined);\n return (\n <div\n role=\"alert\"\n className=\"flex size-full flex-col items-center justify-center gap-3 p-6 text-center\"\n >\n <h1 className=\"text-2xl font-semibold text-core-fg\">\n {driveSlug ? `Drive ${driveSlug} not found` : \"Page not found\"}\n </h1>\n <p className=\"text-core-muted\">\n Nothing is at{\" \"}\n <code data-testid=\"not-found-path\" className=\"text-core-fg\">\n {pathname}\n </code>\n .\n </p>\n <a {...home} className=\"text-core-fg underline\">\n Go to the home page\n </a>\n </div>\n );\n}\n\nexport function DriveNotFoundPage({ driveSlug }: { driveSlug: string }) {\n return <NotFoundPage driveSlug={driveSlug} />;\n}\n","import type { ErrorComponentProps } from \"@tanstack/react-router\";\n\n/** A route's loader or render error. */\nexport function CoreRouteError({ error, reset }: ErrorComponentProps) {\n return (\n <div role=\"alert\" className=\"flex flex-col gap-2 p-6 text-core-fg\">\n <h1 className=\"text-lg font-semibold\">Something went wrong</h1>\n <pre className=\"overflow-x-auto text-sm text-core-muted\">\n {error instanceof Error ? error.message : JSON.stringify(error)}\n </pre>\n <button\n type=\"button\"\n className=\"self-start rounded border border-core-border px-3 py-1 hover:bg-core-hover\"\n onClick={reset}\n >\n Try again\n </button>\n </div>\n );\n}\n","import {\n useLinkProps,\n usePHNavigation,\n useTheme,\n} from \"@powerhousedao/reactor-browser\";\nimport { Outlet } from \"@tanstack/react-router\";\nimport { Suspense } from \"react\";\nimport type { SettingsTabEntry } from \"../router/settings.js\";\nimport { useSettingsTabs } from \"../router/settings.js\";\n\nfunction TabLink({ tab }: { tab: SettingsTabEntry }) {\n const link = useLinkProps({ settings: tab.id });\n const current = link[\"aria-current\"] === \"page\";\n const Icon = tab.icon;\n return (\n <a\n {...link}\n className={[\n \"flex items-center gap-2 rounded px-3 py-1.5\",\n current ? \"bg-core-hover font-semibold\" : \"hover:bg-core-hover\",\n tab.destructive ? \"text-core-danger\" : \"text-core-fg\",\n ].join(\" \")}\n >\n {Icon && <Icon size={16} />}\n {tab.label}\n </a>\n );\n}\n\n/** Core's settings page: the tabs from `staticData.settings` beside the open one. */\nexport function CoreSettingsPage() {\n const tabs = useSettingsTabs();\n const navigation = usePHNavigation();\n // Back to where settings was opened from, else home.\n const close = () => {\n if (navigation.closeOpened?.()) return;\n navigation.open(undefined, { replace: true });\n };\n return (\n <div className=\"flex h-full flex-col text-core-fg\">\n <div className=\"flex items-center justify-between border-b border-core-border px-4 py-3\">\n <h1 className=\"text-lg font-semibold\">Settings</h1>\n <button\n type=\"button\"\n className=\"rounded px-2 text-core-muted hover:bg-core-hover\"\n onClick={close}\n >\n Close\n </button>\n </div>\n <div className=\"flex min-h-0 flex-1\">\n <nav aria-label=\"Settings\" className=\"w-48 shrink-0 p-3\">\n <ul className=\"flex flex-col gap-1\">\n {tabs.map((tab) => (\n <li key={tab.id}>\n <TabLink tab={tab} />\n </li>\n ))}\n </ul>\n </nav>\n <section className=\"min-w-0 flex-1 overflow-auto p-6\">\n <Suspense name=\"SettingsTab\">\n <Outlet />\n </Suspense>\n </section>\n </div>\n </div>\n );\n}\n\nconst THEMES = [\n { value: \"light\", label: \"Light\" },\n { value: \"dark\", label: \"Dark\" },\n { value: \"system\", label: \"System\" },\n] as const;\n\n/** Core's Appearance tab: light, dark, or the system's. */\nexport function AppearanceTab() {\n const { theme, isSystem, setTheme } = useTheme();\n const selected = isSystem ? \"system\" : theme;\n return (\n <fieldset className=\"flex flex-col gap-2\">\n <legend className=\"mb-2 font-semibold\">Theme</legend>\n {THEMES.map((option) => (\n <label key={option.value} className=\"flex items-center gap-2\">\n <input\n type=\"radio\"\n name=\"theme\"\n value={option.value}\n checked={selected === option.value}\n onChange={() => setTheme(option.value)}\n />\n {option.label}\n </label>\n ))}\n </fieldset>\n );\n}\n","import {\n DRIVE_DOCUMENT_TYPES,\n useVetraPackageManager,\n useVetraPackages,\n} from \"@powerhousedao/reactor-browser\";\nimport type {\n DocumentModelLib,\n EditorModule,\n} from \"@powerhousedao/shared/document-model\";\nimport type { ReactNode } from \"react\";\n\nconst SOURCE_LABELS: Record<string, string> = {\n common: \"built-in\",\n project: \"project\",\n \"local-install\": \"local\",\n \"registry-install\": \"registry\",\n};\n\nconst isDriveApp = (editor: EditorModule) =>\n editor.documentTypes.some((type) =>\n (DRIVE_DOCUMENT_TYPES as readonly string[]).includes(type),\n );\n\n/** A package's document models by type, with every version it ships. */\nfunction modelsOf(pkg: DocumentModelLib) {\n const byType = new Map<string, { name: string; versions: number[] }>();\n for (const model of pkg.documentModels) {\n const { id, name } = model.documentModel.global;\n const entry = byType.get(id) ?? { name, versions: [] };\n entry.versions.push(model.version ?? 1);\n byType.set(id, entry);\n }\n return [...byType].map(([type, { name, versions }]) => ({\n type,\n name,\n versions: versions.sort((a, b) => a - b),\n }));\n}\n\nfunction Section(props: { title: string; children: ReactNode }) {\n return (\n <div className=\"mt-3\">\n <h3 className=\"mb-1 text-xs font-semibold tracking-wide text-core-muted uppercase\">\n {props.title}\n </h3>\n {props.children}\n </div>\n );\n}\n\nconst cell = \"py-1 pr-4 align-top break-words\";\n\nfunction EditorTable(props: { label: string; editors: EditorModule[] }) {\n return (\n <table\n aria-label={props.label}\n className=\"w-full table-fixed border-collapse text-sm\"\n >\n <tbody>\n {props.editors.map((editor) => (\n <tr key={editor.config.id} className=\"border-t border-core-border/50\">\n <td className={`${cell} w-1/3`}>{editor.config.name}</td>\n <td className={`${cell} w-1/3 font-mono text-core-muted`}>\n {editor.config.id}\n </td>\n <td className={`${cell} font-mono`}>\n {editor.documentTypes.join(\", \")}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n );\n}\n\nfunction PackageEntry({ pkg }: { pkg: DocumentModelLib }) {\n const manager = useVetraPackageManager();\n const name = pkg.manifest.name;\n const version = manager?.getPackageVersion(name);\n const source = manager?.getPackageSource(name);\n const models = modelsOf(pkg);\n const apps = pkg.editors.filter(isDriveApp);\n const editors = pkg.editors.filter((editor) => !isDriveApp(editor));\n return (\n <li\n aria-label={name}\n className=\"rounded border border-core-border px-4 py-3\"\n >\n <div className=\"flex flex-wrap items-baseline gap-x-3\">\n <h2 className=\"font-semibold\">{name}</h2>\n {version && (\n <span className=\"font-mono text-sm text-core-muted\">{version}</span>\n )}\n {source && (\n <span className=\"rounded border border-core-border px-1.5 text-xs text-core-muted\">\n {SOURCE_LABELS[source] ?? source}\n </span>\n )}\n </div>\n {models.length > 0 && (\n <Section title=\"Document models\">\n <table\n aria-label={`${name} document models`}\n className=\"w-full table-fixed border-collapse text-sm\"\n >\n <tbody>\n {models.map((model) => (\n <tr key={model.type} className=\"border-t border-core-border/50\">\n <td className={`${cell} w-1/3`}>{model.name}</td>\n <td className={`${cell} w-1/3 font-mono`}>{model.type}</td>\n <td className={`${cell} text-core-muted`}>\n {model.versions.map((v) => `v${v}`).join(\", \")}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </Section>\n )}\n {editors.length > 0 && (\n <Section title=\"Editors\">\n <EditorTable label={`${name} editors`} editors={editors} />\n </Section>\n )}\n {apps.length > 0 && (\n <Section title=\"Drive apps\">\n <EditorTable label={`${name} drive apps`} editors={apps} />\n </Section>\n )}\n </li>\n );\n}\n\n/** Core's Packages tab: the loaded packages, their document models, editors and drive apps. */\nexport function PackagesTab() {\n const packages = useVetraPackages();\n return (\n <div className=\"w-full max-w-3xl\">\n {packages.length === 0 ? (\n <p className=\"text-core-muted\">No packages are loaded.</p>\n ) : (\n <ul aria-label=\"Packages\" className=\"flex flex-col gap-3\">\n {packages.map((pkg, index) => (\n <PackageEntry key={`${pkg.manifest.name}:${index}`} pkg={pkg} />\n ))}\n </ul>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|