@openparachute/vault 0.5.3-rc.1 → 0.5.3-rc.2

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.
@@ -2067,6 +2067,41 @@ describe("MCP tools", async () => {
2067
2067
  expect(result[1].tags).toContain("doc");
2068
2068
  });
2069
2069
 
2070
+ // vault#316 — the create-note tool re-reads each note AFTER
2071
+ // `applySchemaDefaults` runs, so the response reflects the post-defaults
2072
+ // on-disk state (matching the update-note path). Before the fix the
2073
+ // response mapped over the pre-defaults in-memory objects, so a
2074
+ // schema-default-filled field was missing from the returned note even
2075
+ // though it had just been written to disk.
2076
+ it("create-note response reflects post-applySchemaDefaults state (vault#316)", async () => {
2077
+ await store.upsertTagSchema("task", {
2078
+ fields: { priority: { type: "string", enum: ["high", "low"] } },
2079
+ });
2080
+ const tools = generateMcpTools(store);
2081
+ const createNote = tools.find((t) => t.name === "create-note")!;
2082
+
2083
+ // Single: default lands in the returned metadata.
2084
+ const single = await createNote.execute({
2085
+ content: "do the thing",
2086
+ path: "Inbox/task-1",
2087
+ tags: ["task"],
2088
+ }) as any;
2089
+ expect(single.metadata?.priority).toBe("high"); // first enum value
2090
+ // Disk and response agree.
2091
+ const onDisk = await store.getNoteByPath("Inbox/task-1");
2092
+ expect((onDisk!.metadata as any)?.priority).toBe("high");
2093
+
2094
+ // Batch: each entry is re-read post-defaults too.
2095
+ const batch = await createNote.execute({
2096
+ notes: [
2097
+ { content: "a", path: "Inbox/task-2", tags: ["task"] },
2098
+ { content: "b", path: "Inbox/task-3", tags: ["task"] },
2099
+ ],
2100
+ }) as any[];
2101
+ expect(batch[0].metadata?.priority).toBe("high");
2102
+ expect(batch[1].metadata?.priority).toBe("high");
2103
+ });
2104
+
2070
2105
  it("create-note accepts extension field (vault#328)", async () => {
2071
2106
  const tools = generateMcpTools(store);
2072
2107
  const createNote = tools.find((t) => t.name === "create-note")!;
package/core/src/mcp.ts CHANGED
@@ -131,7 +131,7 @@ export interface GenerateMcpToolsOpts {
131
131
  * delete-tag, find-path, vault-info, prune-schema (admin).
132
132
  */
133
133
  export function generateMcpTools(store: Store, opts?: GenerateMcpToolsOpts): McpToolDef[] {
134
- const db: Database = (store as any).db;
134
+ const db: Database = store.db;
135
135
  const expandVisibility = opts?.expandVisibility;
136
136
  const nearTraversable = opts?.nearTraversable;
137
137
 
@@ -591,17 +591,35 @@ Link expansion: pass \`expand_links: true\` to inline [[wikilinks]] from returne
591
591
  throw e;
592
592
  }
593
593
 
594
- // Apply tag schema effects
594
+ // Apply tag schema effects, then re-read the notes whose metadata was
595
+ // actually default-filled so the response reflects the final on-disk
596
+ // state (the `created` entries were read before `applySchemaDefaults`
597
+ // ran, so default-filled metadata isn't on them yet). This mirrors the
598
+ // update-note path, which already re-reads post-defaults. The re-read
599
+ // is batched (`getNotes` = one `WHERE id IN (...)`) and skipped
600
+ // entirely when no defaults were applied, so the common no-defaults
601
+ // path adds zero extra reads.
602
+ const mutatedIds = new Set<string>();
595
603
  for (const note of created) {
596
604
  if (note.tags && note.tags.length > 0) {
597
- await applySchemaDefaults(store, db, [note.id], note.tags);
605
+ for (const id of await applySchemaDefaults(store, db, [note.id], note.tags)) {
606
+ mutatedIds.add(id);
607
+ }
598
608
  }
599
609
  }
600
-
601
- // Re-read after schema-default population so the response reflects the
602
- // final on-disk state, then attach `validation_status` from any
603
- // tag's `fields` declaration that applies to this note.
604
- const final = created.map((n) => attachValidationStatus(store, db, n));
610
+ const refreshed =
611
+ mutatedIds.size === 0
612
+ ? created
613
+ : (() => {
614
+ const byId = new Map(
615
+ noteOps.getNotes(db, [...mutatedIds]).map((n) => [n.id, n]),
616
+ );
617
+ return created.map((n) => byId.get(n.id) ?? n);
618
+ })();
619
+
620
+ // Attach `validation_status` from any tag's `fields` declaration that
621
+ // applies to this note, against the post-defaults state.
622
+ const final = refreshed.map((n) => attachValidationStatus(store, db, n));
605
623
  return batch ? final : final[0];
606
624
  },
607
625
  },
@@ -1406,9 +1424,16 @@ Link expansion: pass \`expand_links: true\` to inline [[wikilinks]] from returne
1406
1424
  // Tag schema effects — auto-populate defaults when tags are applied
1407
1425
  // ---------------------------------------------------------------------------
1408
1426
 
1409
- async function applySchemaDefaults(store: Store, db: Database, noteIds: string[], tags: string[]): Promise<void> {
1427
+ /**
1428
+ * Fill schema-declared default values into the metadata of the given notes
1429
+ * for any field they omitted. Returns the IDs of the notes whose metadata was
1430
+ * actually written — callers use this to re-read ONLY the mutated notes (and
1431
+ * to skip the re-read entirely when nothing changed). The common no-schema /
1432
+ * no-defaults path returns an empty array.
1433
+ */
1434
+ async function applySchemaDefaults(store: Store, db: Database, noteIds: string[], tags: string[]): Promise<string[]> {
1410
1435
  const schemas = tagSchemaOps.getTagSchemaMap(db);
1411
- if (Object.keys(schemas).length === 0) return;
1436
+ if (Object.keys(schemas).length === 0) return [];
1412
1437
 
1413
1438
  const defaults: Record<string, unknown> = {};
1414
1439
  for (const tag of tags) {
@@ -1420,8 +1445,9 @@ async function applySchemaDefaults(store: Store, db: Database, noteIds: string[]
1420
1445
  }
1421
1446
  }
1422
1447
  }
1423
- if (Object.keys(defaults).length === 0) return;
1448
+ if (Object.keys(defaults).length === 0) return [];
1424
1449
 
1450
+ const mutated: string[] = [];
1425
1451
  for (const noteId of noteIds) {
1426
1452
  const note = noteOps.getNote(db, noteId);
1427
1453
  if (!note) continue;
@@ -1437,7 +1463,9 @@ async function applySchemaDefaults(store: Store, db: Database, noteIds: string[]
1437
1463
  metadata: { ...existing, ...missing },
1438
1464
  skipUpdatedAt: true,
1439
1465
  });
1466
+ mutated.push(noteId);
1440
1467
  }
1468
+ return mutated;
1441
1469
  }
1442
1470
 
1443
1471
  function defaultForField(field: { type: string; enum?: string[] }): unknown {
package/core/src/types.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { Database } from "bun:sqlite";
1
2
  import type { TagFieldSchema, TagRelationship, TagRelationshipMap, TagRecord } from "./tag-schemas.js";
2
3
  import type { PrunedField } from "./indexed-fields.js";
3
4
 
@@ -208,6 +209,15 @@ export interface HydratedLink extends Link {
208
209
  // ---- Store Interface ----
209
210
 
210
211
  export interface Store {
212
+ /**
213
+ * The underlying `bun:sqlite` handle. Exposed (read-only) so callers that
214
+ * need to run a raw query the Store interface doesn't surface — e.g. the
215
+ * token-table reverse-lookups in routes.ts and MCP tool generation in
216
+ * mcp.ts — can reach it without an `(store as any).db` cast. The concrete
217
+ * `Store` class declares this as `public readonly db: Database`. See vault#242.
218
+ */
219
+ readonly db: Database;
220
+
211
221
  // Notes
212
222
  createNote(content: string, opts?: { id?: string; path?: string; tags?: string[]; metadata?: Record<string, unknown>; created_at?: string; extension?: string }): Promise<Note>;
213
223
  getNote(id: string): Promise<Note | null>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openparachute/vault",
3
- "version": "0.5.3-rc.1",
3
+ "version": "0.5.3-rc.2",
4
4
  "description": "Agent-native knowledge graph. Notes, tags, links over MCP.",
5
5
  "module": "src/cli.ts",
6
6
  "type": "module",
package/src/routes.ts CHANGED
@@ -13,7 +13,7 @@
13
13
 
14
14
  import type { Store, Note } from "../core/src/types.ts";
15
15
  import { listUnresolvedWikilinks } from "../core/src/wikilinks.ts";
16
- import { getNote, getNoteTags, toNoteIndex, filterMetadata, MAX_BATCH_SIZE, validateExtension, ExtensionValidationError } from "../core/src/notes.ts";
16
+ import { getNote, getNotes, getNoteTags, toNoteIndex, filterMetadata, MAX_BATCH_SIZE, validateExtension, ExtensionValidationError } from "../core/src/notes.ts";
17
17
  import { attachValidationStatus } from "../core/src/mcp.ts";
18
18
  import * as linkOps from "../core/src/links.ts";
19
19
  import * as tagSchemaOps from "../core/src/tag-schemas.ts";
@@ -567,7 +567,7 @@ async function handleNotesInner(
567
567
  ): Promise<Response> {
568
568
  const url = new URL(req.url);
569
569
  const method = req.method;
570
- const db = (store as any).db;
570
+ const db = store.db;
571
571
 
572
572
  // ---- Collection routes (no ID in path) ----
573
573
  if (subpath === "") {
@@ -1013,19 +1013,33 @@ async function handleNotesInner(
1013
1013
  throw e;
1014
1014
  }
1015
1015
 
1016
- // Apply tag schema defaults
1016
+ // Apply tag schema defaults, then re-read the notes whose metadata was
1017
+ // actually default-filled so the response reflects the final on-disk
1018
+ // state (the `created` entries were read before `applySchemaDefaults`
1019
+ // ran). Mirrors the MCP create-note path (vault#316). Batched re-read
1020
+ // (`getNotes` = one `WHERE id IN (...)`), skipped when no defaults
1021
+ // applied so the common no-defaults path adds zero extra reads.
1022
+ const mutatedIds = new Set<string>();
1017
1023
  for (const note of created) {
1018
1024
  if (note.tags?.length) {
1019
- await applySchemaDefaults(store, db, [note.id], note.tags);
1025
+ for (const id of await applySchemaDefaults(store, db, [note.id], note.tags)) {
1026
+ mutatedIds.add(id);
1027
+ }
1020
1028
  }
1021
1029
  }
1030
+ const refreshed =
1031
+ mutatedIds.size === 0
1032
+ ? created
1033
+ : (() => {
1034
+ const byId = new Map(getNotes(db, [...mutatedIds]).map((n) => [n.id, n]));
1035
+ return created.map((n) => byId.get(n.id) ?? n);
1036
+ })();
1022
1037
 
1023
1038
  // Attach `validation_status` so HTTP create-note matches the MCP
1024
- // surface (vault#287). Mirrors the MCP create-note attach site at
1025
- // `core/src/mcp.ts:451`. `attachValidationStatus` returns the note
1039
+ // surface (vault#287). `attachValidationStatus` returns the note
1026
1040
  // unchanged when no tag declares fields, so vaults without any tag
1027
1041
  // schemas see no behavior change.
1028
- const final = created.map((n) => attachValidationStatus(store, db, n));
1042
+ const final = refreshed.map((n) => attachValidationStatus(store, db, n));
1029
1043
  return json(body.notes ? final : final[0], 201);
1030
1044
  }
1031
1045
 
@@ -1636,7 +1650,7 @@ export async function handleTags(
1636
1650
  // that token's allowlist. Aggregate matches across sources for a single
1637
1651
  // 409 envelope.
1638
1652
  const referenced: { source: string; tokens: { id: string; label: string }[] }[] = [];
1639
- const db = (store as any).db;
1653
+ const db = store.db;
1640
1654
  for (const src of sources) {
1641
1655
  const tokens = findTokensReferencingTag(db, src as string);
1642
1656
  if (tokens.length > 0) referenced.push({ source: src as string, tokens });
@@ -1800,7 +1814,7 @@ export async function handleTags(
1800
1814
  // tag would silently orphan the token's allowlist. Fail closed (409)
1801
1815
  // and name the offending tokens so the operator can revoke or re-mint
1802
1816
  // before retrying. patterns/tag-scoped-tokens.md §Dependencies.
1803
- const referenced_by = findTokensReferencingTag((store as any).db, tagName);
1817
+ const referenced_by = findTokensReferencingTag(store.db, tagName);
1804
1818
  if (referenced_by.length > 0) {
1805
1819
  return json(
1806
1820
  {
@@ -1835,7 +1849,7 @@ export async function handleFindPath(
1835
1849
  const target = parseQuery(url, "target");
1836
1850
  if (!source || !target) return json({ error: "source and target parameters are required" }, 400);
1837
1851
 
1838
- const db = (store as any).db;
1852
+ const db = store.db;
1839
1853
  try {
1840
1854
  const sourceNote = await resolveNote(store, source);
1841
1855
  if (!sourceNote) return json({ error: `Note not found: "${source}"` }, 404);
@@ -1957,7 +1971,7 @@ export function handleUnresolvedWikilinks(
1957
1971
  const url = new URL(req.url);
1958
1972
  const limitStr = url.searchParams.get("limit");
1959
1973
  const limit = limitStr ? parseInt(limitStr, 10) : 50;
1960
- const db = (store as any).db;
1974
+ const db = store.db;
1961
1975
  const result = listUnresolvedWikilinks(db, limit);
1962
1976
 
1963
1977
  // Unscoped token → return as-is (unchanged behavior).
@@ -2619,9 +2633,12 @@ export async function handleStorage(
2619
2633
  // Tag schema defaults — same logic as core/src/mcp.ts applySchemaDefaults
2620
2634
  // ---------------------------------------------------------------------------
2621
2635
 
2622
- async function applySchemaDefaults(store: Store, db: any, noteIds: string[], tags: string[]): Promise<void> {
2636
+ // Returns the IDs of notes whose metadata was actually default-filled, so
2637
+ // the caller can re-read ONLY the mutated notes (and skip the re-read when
2638
+ // nothing changed). Mirrors the core/src/mcp.ts contract.
2639
+ async function applySchemaDefaults(store: Store, db: any, noteIds: string[], tags: string[]): Promise<string[]> {
2623
2640
  const schemas = tagSchemaOps.getTagSchemaMap(db);
2624
- if (Object.keys(schemas).length === 0) return;
2641
+ if (Object.keys(schemas).length === 0) return [];
2625
2642
 
2626
2643
  const defaults: Record<string, unknown> = {};
2627
2644
  for (const tag of tags) {
@@ -2633,8 +2650,9 @@ async function applySchemaDefaults(store: Store, db: any, noteIds: string[], tag
2633
2650
  }
2634
2651
  }
2635
2652
  }
2636
- if (Object.keys(defaults).length === 0) return;
2653
+ if (Object.keys(defaults).length === 0) return [];
2637
2654
 
2655
+ const mutated: string[] = [];
2638
2656
  for (const noteId of noteIds) {
2639
2657
  const note = await store.getNote(noteId);
2640
2658
  if (!note) continue;
@@ -2648,7 +2666,9 @@ async function applySchemaDefaults(store: Store, db: any, noteIds: string[], tag
2648
2666
  metadata: { ...existing, ...missing },
2649
2667
  skipUpdatedAt: true,
2650
2668
  });
2669
+ mutated.push(noteId);
2651
2670
  }
2671
+ return mutated;
2652
2672
  }
2653
2673
 
2654
2674
  function defaultForField(field: { type: string; enum?: string[] }): unknown {
package/src/vault.test.ts CHANGED
@@ -2023,6 +2023,46 @@ describe("HTTP /notes", async () => {
2023
2023
  expect(body.createdAt).toBe("2025-01-01T00:00:00.000Z");
2024
2024
  });
2025
2025
 
2026
+ // vault#316 — the HTTP POST path re-reads each note AFTER
2027
+ // `applySchemaDefaults`, so the response metadata carries the just-written
2028
+ // defaults (mirrors the MCP create-note path). Before the fix the response
2029
+ // mapped over the pre-defaults in-memory objects, so default-filled
2030
+ // metadata was missing from `POST /api/notes` responses.
2031
+ test("POST /notes response reflects post-applySchemaDefaults state (vault#316)", async () => {
2032
+ await store.upsertTagSchema("task", {
2033
+ fields: { priority: { type: "string", enum: ["high", "low"] } },
2034
+ });
2035
+
2036
+ // Single: default lands in the returned metadata and agrees with disk.
2037
+ const single = await handleNotes(
2038
+ mkReq("POST", "/notes", { content: "do the thing", path: "Inbox/task-1", tags: ["task"] }),
2039
+ store,
2040
+ "",
2041
+ );
2042
+ expect(single.status).toBe(201);
2043
+ const singleBody = await single.json() as any;
2044
+ expect(singleBody.metadata?.priority).toBe("high"); // first enum value
2045
+ const onDisk = await store.getNoteByPath("Inbox/task-1");
2046
+ expect((onDisk!.metadata as any)?.priority).toBe("high");
2047
+
2048
+ // Batch: each entry is re-read post-defaults too, in input order.
2049
+ const batch = await handleNotes(
2050
+ mkReq("POST", "/notes", {
2051
+ notes: [
2052
+ { content: "a", path: "Inbox/task-2", tags: ["task"] },
2053
+ { content: "b", path: "Inbox/task-3", tags: ["task"] },
2054
+ ],
2055
+ }),
2056
+ store,
2057
+ "",
2058
+ );
2059
+ expect(batch.status).toBe(201);
2060
+ const batchBody = await batch.json() as any[];
2061
+ expect(batchBody.map((n) => n.path)).toEqual(["Inbox/task-2", "Inbox/task-3"]);
2062
+ expect(batchBody[0].metadata?.priority).toBe("high");
2063
+ expect(batchBody[1].metadata?.priority).toBe("high");
2064
+ });
2065
+
2026
2066
  // ---- Extension field (vault#328) ----
2027
2067
 
2028
2068
  test("POST /notes accepts extension and persists it", async () => {
@@ -57,4 +57,4 @@ Error generating stack: `+a.message+`
57
57
  * @license MIT
58
58
  */var Ah="popstate";function Nh(i){return typeof i=="object"&&i!=null&&"pathname"in i&&"search"in i&&"hash"in i&&"state"in i&&"key"in i}function Wy(i={}){function s(r,h){var v;let m=(v=h.state)==null?void 0:v.masked,{pathname:y,search:E,hash:b}=m||r.location;return Fs("",{pathname:y,search:E,hash:b},h.state&&h.state.usr||null,h.state&&h.state.key||"default",m?{pathname:r.location.pathname,search:r.location.search,hash:r.location.hash}:void 0)}function f(r,h){return typeof h=="string"?h:Qn(h)}return Py(s,f,null,i)}function ze(i,s){if(i===!1||i===null||typeof i>"u")throw new Error(s)}function Xt(i,s){if(!i){typeof console<"u"&&console.warn(s);try{throw new Error(s)}catch{}}}function Iy(){return Math.random().toString(36).substring(2,10)}function Rh(i,s){return{usr:i.state,key:i.key,idx:s,masked:i.unstable_mask?{pathname:i.pathname,search:i.search,hash:i.hash}:void 0}}function Fs(i,s,f=null,r,h){return{pathname:typeof i=="string"?i:i.pathname,search:"",hash:"",...typeof s=="string"?Xa(s):s,state:f,key:s&&s.key||r||Iy(),unstable_mask:h}}function Qn({pathname:i="/",search:s="",hash:f=""}){return s&&s!=="?"&&(i+=s.charAt(0)==="?"?s:"?"+s),f&&f!=="#"&&(i+=f.charAt(0)==="#"?f:"#"+f),i}function Xa(i){let s={};if(i){let f=i.indexOf("#");f>=0&&(s.hash=i.substring(f),i=i.substring(0,f));let r=i.indexOf("?");r>=0&&(s.search=i.substring(r),i=i.substring(0,r)),i&&(s.pathname=i)}return s}function Py(i,s,f,r={}){let{window:h=document.defaultView,v5Compat:m=!1}=r,y=h.history,E="POP",b=null,v=_();v==null&&(v=0,y.replaceState({...y.state,idx:v},""));function _(){return(y.state||{idx:null}).idx}function g(){E="POP";let q=_(),L=q==null?null:q-v;v=q,b&&b({action:E,location:B.location,delta:L})}function R(q,L){E="PUSH";let X=Nh(q)?q:Fs(B.location,q,L);v=_()+1;let J=Rh(X,v),V=B.createHref(X.unstable_mask||X);try{y.pushState(J,"",V)}catch(Y){if(Y instanceof DOMException&&Y.name==="DataCloneError")throw Y;h.location.assign(V)}m&&b&&b({action:E,location:B.location,delta:1})}function k(q,L){E="REPLACE";let X=Nh(q)?q:Fs(B.location,q,L);v=_();let J=Rh(X,v),V=B.createHref(X.unstable_mask||X);y.replaceState(J,"",V),m&&b&&b({action:E,location:B.location,delta:0})}function D(q){return ep(q)}let B={get action(){return E},get location(){return i(h,y)},listen(q){if(b)throw new Error("A history only accepts one active listener");return h.addEventListener(Ah,g),b=q,()=>{h.removeEventListener(Ah,g),b=null}},createHref(q){return s(h,q)},createURL:D,encodeLocation(q){let L=D(q);return{pathname:L.pathname,search:L.search,hash:L.hash}},push:R,replace:k,go(q){return y.go(q)}};return B}function ep(i,s=!1){let f="http://localhost";typeof window<"u"&&(f=window.location.origin!=="null"?window.location.origin:window.location.href),ze(f,"No window.location.(origin|href) available to create URL");let r=typeof i=="string"?i:Qn(i);return r=r.replace(/ $/,"%20"),!s&&r.startsWith("//")&&(r=f+r),new URL(r,f)}function Qh(i,s,f="/"){return tp(i,s,f,!1)}function tp(i,s,f,r){let h=typeof s=="string"?Xa(s):s,m=ol(h.pathname||"/",f);if(m==null)return null;let y=Vh(i);lp(y);let E=null;for(let b=0;E==null&&b<y.length;++b){let v=hp(m);E=fp(y[b],v,r)}return E}function Vh(i,s=[],f=[],r="",h=!1){let m=(y,E,b=h,v)=>{let _={relativePath:v===void 0?y.path||"":v,caseSensitive:y.caseSensitive===!0,childrenIndex:E,route:y};if(_.relativePath.startsWith("/")){if(!_.relativePath.startsWith(r)&&b)return;ze(_.relativePath.startsWith(r),`Absolute route path "${_.relativePath}" nested under path "${r}" is not valid. An absolute child route path must start with the combined path of all its parent routes.`),_.relativePath=_.relativePath.slice(r.length)}let g=Ut([r,_.relativePath]),R=f.concat(_);y.children&&y.children.length>0&&(ze(y.index!==!0,`Index routes must not have child routes. Please remove all child routes from route path "${g}".`),Vh(y.children,s,R,g,b)),!(y.path==null&&!y.index)&&s.push({path:g,score:rp(g,y.index),routesMeta:R})};return i.forEach((y,E)=>{var b;if(y.path===""||!((b=y.path)!=null&&b.includes("?")))m(y,E);else for(let v of Zh(y.path))m(y,E,!0,v)}),s}function Zh(i){let s=i.split("/");if(s.length===0)return[];let[f,...r]=s,h=f.endsWith("?"),m=f.replace(/\?$/,"");if(r.length===0)return h?[m,""]:[m];let y=Zh(r.join("/")),E=[];return E.push(...y.map(b=>b===""?m:[m,b].join("/"))),h&&E.push(...y),E.map(b=>i.startsWith("/")&&b===""?"/":b)}function lp(i){i.sort((s,f)=>s.score!==f.score?f.score-s.score:op(s.routesMeta.map(r=>r.childrenIndex),f.routesMeta.map(r=>r.childrenIndex)))}var ap=/^:[\w-]+$/,np=3,up=2,ip=1,cp=10,sp=-2,zh=i=>i==="*";function rp(i,s){let f=i.split("/"),r=f.length;return f.some(zh)&&(r+=sp),s&&(r+=up),f.filter(h=>!zh(h)).reduce((h,m)=>h+(ap.test(m)?np:m===""?ip:cp),r)}function op(i,s){return i.length===s.length&&i.slice(0,-1).every((r,h)=>r===s[h])?i[i.length-1]-s[s.length-1]:0}function fp(i,s,f=!1){let{routesMeta:r}=i,h={},m="/",y=[];for(let E=0;E<r.length;++E){let b=r[E],v=E===r.length-1,_=m==="/"?s:s.slice(m.length)||"/",g=yi({path:b.relativePath,caseSensitive:b.caseSensitive,end:v},_),R=b.route;if(!g&&v&&f&&!r[r.length-1].route.index&&(g=yi({path:b.relativePath,caseSensitive:b.caseSensitive,end:!1},_)),!g)return null;Object.assign(h,g.params),y.push({params:h,pathname:Ut([m,g.pathname]),pathnameBase:pp(Ut([m,g.pathnameBase])),route:R}),g.pathnameBase!=="/"&&(m=Ut([m,g.pathnameBase]))}return y}function yi(i,s){typeof i=="string"&&(i={path:i,caseSensitive:!1,end:!0});let[f,r]=dp(i.path,i.caseSensitive,i.end),h=s.match(f);if(!h)return null;let m=h[0],y=m.replace(/(.)\/+$/,"$1"),E=h.slice(1);return{params:r.reduce((v,{paramName:_,isOptional:g},R)=>{if(_==="*"){let D=E[R]||"";y=m.slice(0,m.length-D.length).replace(/(.)\/+$/,"$1")}const k=E[R];return g&&!k?v[_]=void 0:v[_]=(k||"").replace(/%2F/g,"/"),v},{}),pathname:m,pathnameBase:y,pattern:i}}function dp(i,s=!1,f=!0){Xt(i==="*"||!i.endsWith("*")||i.endsWith("/*"),`Route path "${i}" will be treated as if it were "${i.replace(/\*$/,"/*")}" because the \`*\` character must always follow a \`/\` in the pattern. To get rid of this warning, please change the route path to "${i.replace(/\*$/,"/*")}".`);let r=[],h="^"+i.replace(/\/*\*?$/,"").replace(/^\/*/,"/").replace(/[\\.*+^${}|()[\]]/g,"\\$&").replace(/\/:([\w-]+)(\?)?/g,(y,E,b,v,_)=>{if(r.push({paramName:E,isOptional:b!=null}),b){let g=_.charAt(v+y.length);return g&&g!=="/"?"/([^\\/]*)":"(?:/([^\\/]*))?"}return"/([^\\/]+)"}).replace(/\/([\w-]+)\?(\/|$)/g,"(/$1)?$2");return i.endsWith("*")?(r.push({paramName:"*"}),h+=i==="*"||i==="/*"?"(.*)$":"(?:\\/(.+)|\\/*)$"):f?h+="\\/*$":i!==""&&i!=="/"&&(h+="(?:(?=\\/|$))"),[new RegExp(h,s?void 0:"i"),r]}function hp(i){try{return i.split("/").map(s=>decodeURIComponent(s).replace(/\//g,"%2F")).join("/")}catch(s){return Xt(!1,`The URL path "${i}" could not be decoded because it is a malformed URL segment. This is probably due to a bad percent encoding (${s}).`),i}}function ol(i,s){if(s==="/")return i;if(!i.toLowerCase().startsWith(s.toLowerCase()))return null;let f=s.endsWith("/")?s.length-1:s.length,r=i.charAt(f);return r&&r!=="/"?null:i.slice(f)||"/"}var mp=/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i;function vp(i,s="/"){let{pathname:f,search:r="",hash:h=""}=typeof i=="string"?Xa(i):i,m;return f?(f=Jh(f),f.startsWith("/")?m=Ch(f.substring(1),"/"):m=Ch(f,s)):m=s,{pathname:m,search:gp(r),hash:bp(h)}}function Ch(i,s){let f=pi(s).split("/");return i.split("/").forEach(h=>{h===".."?f.length>1&&f.pop():h!=="."&&f.push(h)}),f.length>1?f.join("/"):"/"}function Zs(i,s,f,r){return`Cannot include a '${i}' character in a manually specified \`to.${s}\` field [${JSON.stringify(r)}]. Please separate it out to the \`to.${f}\` field. Alternatively you may provide the full path as a string in <Link to="..."> and the router will parse it for you.`}function yp(i){return i.filter((s,f)=>f===0||s.route.path&&s.route.path.length>0)}function Kh(i){let s=yp(i);return s.map((f,r)=>r===s.length-1?f.pathname:f.pathnameBase)}function er(i,s,f,r=!1){let h;typeof i=="string"?h=Xa(i):(h={...i},ze(!h.pathname||!h.pathname.includes("?"),Zs("?","pathname","search",h)),ze(!h.pathname||!h.pathname.includes("#"),Zs("#","pathname","hash",h)),ze(!h.search||!h.search.includes("#"),Zs("#","search","hash",h)));let m=i===""||h.pathname==="",y=m?"/":h.pathname,E;if(y==null)E=f;else{let g=s.length-1;if(!r&&y.startsWith("..")){let R=y.split("/");for(;R[0]==="..";)R.shift(),g-=1;h.pathname=R.join("/")}E=g>=0?s[g]:"/"}let b=vp(h,E),v=y&&y!=="/"&&y.endsWith("/"),_=(m||y===".")&&f.endsWith("/");return!b.pathname.endsWith("/")&&(v||_)&&(b.pathname+="/"),b}var Jh=i=>i.replace(/\/\/+/g,"/"),Ut=i=>Jh(i.join("/")),pi=i=>i.replace(/\/+$/,""),pp=i=>pi(i).replace(/^\/*/,"/"),gp=i=>!i||i==="?"?"":i.startsWith("?")?i:"?"+i,bp=i=>!i||i==="#"?"":i.startsWith("#")?i:"#"+i,Sp=class{constructor(i,s,f,r=!1){this.status=i,this.statusText=s||"",this.internal=r,f instanceof Error?(this.data=f.toString(),this.error=f):this.data=f}};function xp(i){return i!=null&&typeof i.status=="number"&&typeof i.statusText=="string"&&typeof i.internal=="boolean"&&"data"in i}function jp(i){let s=i.map(f=>f.route.path).filter(Boolean);return Ut(s)||"/"}var $h=typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u";function Fh(i,s){let f=i;if(typeof f!="string"||!mp.test(f))return{absoluteURL:void 0,isExternal:!1,to:f};let r=f,h=!1;if($h)try{let m=new URL(window.location.href),y=f.startsWith("//")?new URL(m.protocol+f):new URL(f),E=ol(y.pathname,s);y.origin===m.origin&&E!=null?f=E+y.search+y.hash:h=!0}catch{Xt(!1,`<Link to="${f}"> contains an invalid URL which will probably break when clicked - please update to a valid URL path.`)}return{absoluteURL:r,isExternal:h,to:f}}Object.getOwnPropertyNames(Object.prototype).sort().join("\0");var Wh=["POST","PUT","PATCH","DELETE"];new Set(Wh);var Ep=["GET",...Wh];new Set(Ep);var Qa=S.createContext(null);Qa.displayName="DataRouter";var gi=S.createContext(null);gi.displayName="DataRouterState";var Ih=S.createContext(!1);function Tp(){return S.useContext(Ih)}var Ph=S.createContext({isTransitioning:!1});Ph.displayName="ViewTransition";var _p=S.createContext(new Map);_p.displayName="Fetchers";var Ap=S.createContext(null);Ap.displayName="Await";var wt=S.createContext(null);wt.displayName="Navigation";var Zn=S.createContext(null);Zn.displayName="Location";var Qt=S.createContext({outlet:null,matches:[],isDataRoute:!1});Qt.displayName="Route";var tr=S.createContext(null);tr.displayName="RouteError";var em="REACT_ROUTER_ERROR",Np="REDIRECT",Rp="ROUTE_ERROR_RESPONSE";function zp(i){if(i.startsWith(`${em}:${Np}:{`))try{let s=JSON.parse(i.slice(28));if(typeof s=="object"&&s&&typeof s.status=="number"&&typeof s.statusText=="string"&&typeof s.location=="string"&&typeof s.reloadDocument=="boolean"&&typeof s.replace=="boolean")return s}catch{}}function Cp(i){if(i.startsWith(`${em}:${Rp}:{`))try{let s=JSON.parse(i.slice(40));if(typeof s=="object"&&s&&typeof s.status=="number"&&typeof s.statusText=="string")return new Sp(s.status,s.statusText,s.data)}catch{}}function wp(i,{relative:s}={}){ze(Kn(),"useHref() may be used only in the context of a <Router> component.");let{basename:f,navigator:r}=S.useContext(wt),{hash:h,pathname:m,search:y}=Jn(i,{relative:s}),E=m;return f!=="/"&&(E=m==="/"?f:Ut([f,m])),r.createHref({pathname:E,search:y,hash:h})}function Kn(){return S.useContext(Zn)!=null}function fl(){return ze(Kn(),"useLocation() may be used only in the context of a <Router> component."),S.useContext(Zn).location}var tm="You should call navigate() in a React.useEffect(), not when your component is first rendered.";function lm(i){S.useContext(wt).static||S.useLayoutEffect(i)}function Mp(){let{isDataRoute:i}=S.useContext(Qt);return i?Vp():Op()}function Op(){ze(Kn(),"useNavigate() may be used only in the context of a <Router> component.");let i=S.useContext(Qa),{basename:s,navigator:f}=S.useContext(wt),{matches:r}=S.useContext(Qt),{pathname:h}=fl(),m=JSON.stringify(Kh(r)),y=S.useRef(!1);return lm(()=>{y.current=!0}),S.useCallback((b,v={})=>{if(Xt(y.current,tm),!y.current)return;if(typeof b=="number"){f.go(b);return}let _=er(b,JSON.parse(m),h,v.relative==="path");i==null&&s!=="/"&&(_.pathname=_.pathname==="/"?s:Ut([s,_.pathname])),(v.replace?f.replace:f.push)(_,v.state,v)},[s,f,m,h,i])}S.createContext(null);function lr(){let{matches:i}=S.useContext(Qt),s=i[i.length-1];return(s==null?void 0:s.params)??{}}function Jn(i,{relative:s}={}){let{matches:f}=S.useContext(Qt),{pathname:r}=fl(),h=JSON.stringify(Kh(f));return S.useMemo(()=>er(i,JSON.parse(h),r,s==="path"),[i,h,r,s])}function Dp(i,s){return am(i,s)}function am(i,s,f){var q;ze(Kn(),"useRoutes() may be used only in the context of a <Router> component.");let{navigator:r}=S.useContext(wt),{matches:h}=S.useContext(Qt),m=h[h.length-1],y=m?m.params:{},E=m?m.pathname:"/",b=m?m.pathnameBase:"/",v=m&&m.route;{let L=v&&v.path||"";um(E,!v||L.endsWith("*")||L.endsWith("*?"),`You rendered descendant <Routes> (or called \`useRoutes()\`) at "${E}" (under <Route path="${L}">) but the parent route path has no trailing "*". This means if you navigate deeper, the parent won't match anymore and therefore the child routes will never render.
59
59
 
60
- Please change the parent <Route path="${L}"> to <Route path="${L==="/"?"*":`${L}/*`}">.`)}let _=fl(),g;if(s){let L=typeof s=="string"?Xa(s):s;ze(b==="/"||((q=L.pathname)==null?void 0:q.startsWith(b)),`When overriding the location using \`<Routes location>\` or \`useRoutes(routes, location)\`, the location pathname must begin with the portion of the URL pathname that was matched by all parent routes. The current pathname base is "${b}" but pathname "${L.pathname}" was given in the \`location\` prop.`),g=L}else g=_;let R=g.pathname||"/",k=R;if(b!=="/"){let L=b.replace(/^\//,"").split("/");k="/"+R.replace(/^\//,"").split("/").slice(L.length).join("/")}let D=Qh(i,{pathname:k});Xt(v||D!=null,`No routes matched location "${g.pathname}${g.search}${g.hash}" `),Xt(D==null||D[D.length-1].route.element!==void 0||D[D.length-1].route.Component!==void 0||D[D.length-1].route.lazy!==void 0,`Matched leaf route at location "${g.pathname}${g.search}${g.hash}" does not have an element or Component. This means it will render an <Outlet /> with a null value by default resulting in an "empty" page.`);let B=kp(D&&D.map(L=>Object.assign({},L,{params:Object.assign({},y,L.params),pathname:Ut([b,r.encodeLocation?r.encodeLocation(L.pathname.replace(/%/g,"%25").replace(/\?/g,"%3F").replace(/#/g,"%23")).pathname:L.pathname]),pathnameBase:L.pathnameBase==="/"?b:Ut([b,r.encodeLocation?r.encodeLocation(L.pathnameBase.replace(/%/g,"%25").replace(/\?/g,"%3F").replace(/#/g,"%23")).pathname:L.pathnameBase])})),h,f);return s&&B?S.createElement(Zn.Provider,{value:{location:{pathname:"/",search:"",hash:"",state:null,key:"default",unstable_mask:void 0,...g},navigationType:"POP"}},B):B}function Up(){let i=Qp(),s=xp(i)?`${i.status} ${i.statusText}`:i instanceof Error?i.message:JSON.stringify(i),f=i instanceof Error?i.stack:null,r="rgba(200,200,200, 0.5)",h={padding:"0.5rem",backgroundColor:r},m={padding:"2px 4px",backgroundColor:r},y=null;return console.error("Error handled by React Router default ErrorBoundary:",i),y=S.createElement(S.Fragment,null,S.createElement("p",null,"💿 Hey developer 👋"),S.createElement("p",null,"You can provide a way better UX than this when your app throws errors by providing your own ",S.createElement("code",{style:m},"ErrorBoundary")," or"," ",S.createElement("code",{style:m},"errorElement")," prop on your route.")),S.createElement(S.Fragment,null,S.createElement("h2",null,"Unexpected Application Error!"),S.createElement("h3",{style:{fontStyle:"italic"}},s),f?S.createElement("pre",{style:h},f):null,y)}var Hp=S.createElement(Up,null),nm=class extends S.Component{constructor(i){super(i),this.state={location:i.location,revalidation:i.revalidation,error:i.error}}static getDerivedStateFromError(i){return{error:i}}static getDerivedStateFromProps(i,s){return s.location!==i.location||s.revalidation!=="idle"&&i.revalidation==="idle"?{error:i.error,location:i.location,revalidation:i.revalidation}:{error:i.error!==void 0?i.error:s.error,location:s.location,revalidation:i.revalidation||s.revalidation}}componentDidCatch(i,s){this.props.onError?this.props.onError(i,s):console.error("React Router caught the following error during render",i)}render(){let i=this.state.error;if(this.context&&typeof i=="object"&&i&&"digest"in i&&typeof i.digest=="string"){const f=Cp(i.digest);f&&(i=f)}let s=i!==void 0?S.createElement(Qt.Provider,{value:this.props.routeContext},S.createElement(tr.Provider,{value:i,children:this.props.component})):this.props.children;return this.context?S.createElement(Bp,{error:i},s):s}};nm.contextType=Ih;var Ks=new WeakMap;function Bp({children:i,error:s}){let{basename:f}=S.useContext(wt);if(typeof s=="object"&&s&&"digest"in s&&typeof s.digest=="string"){let r=zp(s.digest);if(r){let h=Ks.get(s);if(h)throw h;let m=Fh(r.location,f);if($h&&!Ks.get(s))if(m.isExternal||r.reloadDocument)window.location.href=m.absoluteURL||m.to;else{const y=Promise.resolve().then(()=>window.__reactRouterDataRouter.navigate(m.to,{replace:r.replace}));throw Ks.set(s,y),y}return S.createElement("meta",{httpEquiv:"refresh",content:`0;url=${m.absoluteURL||m.to}`})}}return i}function Lp({routeContext:i,match:s,children:f}){let r=S.useContext(Qa);return r&&r.static&&r.staticContext&&(s.route.errorElement||s.route.ErrorBoundary)&&(r.staticContext._deepestRenderedBoundaryId=s.route.id),S.createElement(Qt.Provider,{value:i},f)}function kp(i,s=[],f){let r=f==null?void 0:f.state;if(i==null){if(!r)return null;if(r.errors)i=r.matches;else if(s.length===0&&!r.initialized&&r.matches.length>0)i=r.matches;else return null}let h=i,m=r==null?void 0:r.errors;if(m!=null){let _=h.findIndex(g=>g.route.id&&(m==null?void 0:m[g.route.id])!==void 0);ze(_>=0,`Could not find a matching route for errors on route IDs: ${Object.keys(m).join(",")}`),h=h.slice(0,Math.min(h.length,_+1))}let y=!1,E=-1;if(f&&r){y=r.renderFallback;for(let _=0;_<h.length;_++){let g=h[_];if((g.route.HydrateFallback||g.route.hydrateFallbackElement)&&(E=_),g.route.id){let{loaderData:R,errors:k}=r,D=g.route.loader&&!R.hasOwnProperty(g.route.id)&&(!k||k[g.route.id]===void 0);if(g.route.lazy||D){f.isStatic&&(y=!0),E>=0?h=h.slice(0,E+1):h=[h[0]];break}}}}let b=f==null?void 0:f.onError,v=r&&b?(_,g)=>{var R,k;b(_,{location:r.location,params:((k=(R=r.matches)==null?void 0:R[0])==null?void 0:k.params)??{},unstable_pattern:jp(r.matches),errorInfo:g})}:void 0;return h.reduceRight((_,g,R)=>{let k,D=!1,B=null,q=null;r&&(k=m&&g.route.id?m[g.route.id]:void 0,B=g.route.errorElement||Hp,y&&(E<0&&R===0?(um("route-fallback",!1,"No `HydrateFallback` element provided to render during initial hydration"),D=!0,q=null):E===R&&(D=!0,q=g.route.hydrateFallbackElement||null)));let L=s.concat(h.slice(0,R+1)),X=()=>{let J;return k?J=B:D?J=q:g.route.Component?J=S.createElement(g.route.Component,null):g.route.element?J=g.route.element:J=_,S.createElement(Lp,{match:g,routeContext:{outlet:_,matches:L,isDataRoute:r!=null},children:J})};return r&&(g.route.ErrorBoundary||g.route.errorElement||R===0)?S.createElement(nm,{location:r.location,revalidation:r.revalidation,component:B,error:k,children:X(),routeContext:{outlet:null,matches:L,isDataRoute:!0},onError:v}):X()},null)}function ar(i){return`${i} must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.`}function qp(i){let s=S.useContext(Qa);return ze(s,ar(i)),s}function Yp(i){let s=S.useContext(gi);return ze(s,ar(i)),s}function Gp(i){let s=S.useContext(Qt);return ze(s,ar(i)),s}function nr(i){let s=Gp(i),f=s.matches[s.matches.length-1];return ze(f.route.id,`${i} can only be used on routes that contain a unique "id"`),f.route.id}function Xp(){return nr("useRouteId")}function Qp(){var r;let i=S.useContext(tr),s=Yp("useRouteError"),f=nr("useRouteError");return i!==void 0?i:(r=s.errors)==null?void 0:r[f]}function Vp(){let{router:i}=qp("useNavigate"),s=nr("useNavigate"),f=S.useRef(!1);return lm(()=>{f.current=!0}),S.useCallback(async(h,m={})=>{Xt(f.current,tm),f.current&&(typeof h=="number"?await i.navigate(h):await i.navigate(h,{fromRouteId:s,...m}))},[i,s])}var wh={};function um(i,s,f){!s&&!wh[i]&&(wh[i]=!0,Xt(!1,f))}S.memo(Zp);function Zp({routes:i,future:s,state:f,isStatic:r,onError:h}){return am(i,void 0,{state:f,isStatic:r,onError:h})}function Yt(i){ze(!1,"A <Route> is only ever to be used as the child of <Routes> element, never rendered directly. Please wrap your <Route> in a <Routes>.")}function Kp({basename:i="/",children:s=null,location:f,navigationType:r="POP",navigator:h,static:m=!1,unstable_useTransitions:y}){ze(!Kn(),"You cannot render a <Router> inside another <Router>. You should never have more than one in your app.");let E=i.replace(/^\/*/,"/"),b=S.useMemo(()=>({basename:E,navigator:h,static:m,unstable_useTransitions:y,future:{}}),[E,h,m,y]);typeof f=="string"&&(f=Xa(f));let{pathname:v="/",search:_="",hash:g="",state:R=null,key:k="default",unstable_mask:D}=f,B=S.useMemo(()=>{let q=ol(v,E);return q==null?null:{location:{pathname:q,search:_,hash:g,state:R,key:k,unstable_mask:D},navigationType:r}},[E,v,_,g,R,k,r,D]);return Xt(B!=null,`<Router basename="${E}"> is not able to match the URL "${v}${_}${g}" because it does not start with the basename, so the <Router> won't render anything.`),B==null?null:S.createElement(wt.Provider,{value:b},S.createElement(Zn.Provider,{children:s,value:B}))}function Mh({children:i,location:s}){return Dp(Ws(i),s)}function Ws(i,s=[]){let f=[];return S.Children.forEach(i,(r,h)=>{if(!S.isValidElement(r))return;let m=[...s,h];if(r.type===S.Fragment){f.push.apply(f,Ws(r.props.children,m));return}ze(r.type===Yt,`[${typeof r.type=="string"?r.type:r.type.name}] is not a <Route> component. All component children of <Routes> must be a <Route> or <React.Fragment>`),ze(!r.props.index||!r.props.children,"An index route cannot have child routes.");let y={id:r.props.id||m.join("-"),caseSensitive:r.props.caseSensitive,element:r.props.element,Component:r.props.Component,index:r.props.index,path:r.props.path,middleware:r.props.middleware,loader:r.props.loader,action:r.props.action,hydrateFallbackElement:r.props.hydrateFallbackElement,HydrateFallback:r.props.HydrateFallback,errorElement:r.props.errorElement,ErrorBoundary:r.props.ErrorBoundary,hasErrorBoundary:r.props.hasErrorBoundary===!0||r.props.ErrorBoundary!=null||r.props.errorElement!=null,shouldRevalidate:r.props.shouldRevalidate,handle:r.props.handle,lazy:r.props.lazy};r.props.children&&(y.children=Ws(r.props.children,m)),f.push(y)}),f}var mi="get",vi="application/x-www-form-urlencoded";function bi(i){return typeof HTMLElement<"u"&&i instanceof HTMLElement}function Jp(i){return bi(i)&&i.tagName.toLowerCase()==="button"}function $p(i){return bi(i)&&i.tagName.toLowerCase()==="form"}function Fp(i){return bi(i)&&i.tagName.toLowerCase()==="input"}function Wp(i){return!!(i.metaKey||i.altKey||i.ctrlKey||i.shiftKey)}function Ip(i,s){return i.button===0&&(!s||s==="_self")&&!Wp(i)}var hi=null;function Pp(){if(hi===null)try{new FormData(document.createElement("form"),0),hi=!1}catch{hi=!0}return hi}var eg=new Set(["application/x-www-form-urlencoded","multipart/form-data","text/plain"]);function Js(i){return i!=null&&!eg.has(i)?(Xt(!1,`"${i}" is not a valid \`encType\` for \`<Form>\`/\`<fetcher.Form>\` and will default to "${vi}"`),null):i}function tg(i,s){let f,r,h,m,y;if($p(i)){let E=i.getAttribute("action");r=E?ol(E,s):null,f=i.getAttribute("method")||mi,h=Js(i.getAttribute("enctype"))||vi,m=new FormData(i)}else if(Jp(i)||Fp(i)&&(i.type==="submit"||i.type==="image")){let E=i.form;if(E==null)throw new Error('Cannot submit a <button> or <input type="submit"> without a <form>');let b=i.getAttribute("formaction")||E.getAttribute("action");if(r=b?ol(b,s):null,f=i.getAttribute("formmethod")||E.getAttribute("method")||mi,h=Js(i.getAttribute("formenctype"))||Js(E.getAttribute("enctype"))||vi,m=new FormData(E,i),!Pp()){let{name:v,type:_,value:g}=i;if(_==="image"){let R=v?`${v}.`:"";m.append(`${R}x`,"0"),m.append(`${R}y`,"0")}else v&&m.append(v,g)}}else{if(bi(i))throw new Error('Cannot submit element that is not <form>, <button>, or <input type="submit|image">');f=mi,r=null,h=vi,y=i}return m&&h==="text/plain"&&(y=m,m=void 0),{action:r,method:f.toLowerCase(),encType:h,formData:m,body:y}}Object.getOwnPropertyNames(Object.prototype).sort().join("\0");function ur(i,s){if(i===!1||i===null||typeof i>"u")throw new Error(s)}function im(i,s,f,r){let h=typeof i=="string"?new URL(i,typeof window>"u"?"server://singlefetch/":window.location.origin):i;return f?h.pathname.endsWith("/")?h.pathname=`${h.pathname}_.${r}`:h.pathname=`${h.pathname}.${r}`:h.pathname==="/"?h.pathname=`_root.${r}`:s&&ol(h.pathname,s)==="/"?h.pathname=`${pi(s)}/_root.${r}`:h.pathname=`${pi(h.pathname)}.${r}`,h}async function lg(i,s){if(i.id in s)return s[i.id];try{let f=await import(i.module);return s[i.id]=f,f}catch(f){return console.error(`Error loading route module \`${i.module}\`, reloading page...`),console.error(f),window.__reactRouterContext&&window.__reactRouterContext.isSpaMode,window.location.reload(),new Promise(()=>{})}}function ag(i){return i==null?!1:i.href==null?i.rel==="preload"&&typeof i.imageSrcSet=="string"&&typeof i.imageSizes=="string":typeof i.rel=="string"&&typeof i.href=="string"}async function ng(i,s,f){let r=await Promise.all(i.map(async h=>{let m=s.routes[h.route.id];if(m){let y=await lg(m,f);return y.links?y.links():[]}return[]}));return sg(r.flat(1).filter(ag).filter(h=>h.rel==="stylesheet"||h.rel==="preload").map(h=>h.rel==="stylesheet"?{...h,rel:"prefetch",as:"style"}:{...h,rel:"prefetch"}))}function Oh(i,s,f,r,h,m){let y=(b,v)=>f[v]?b.route.id!==f[v].route.id:!0,E=(b,v)=>{var _;return f[v].pathname!==b.pathname||((_=f[v].route.path)==null?void 0:_.endsWith("*"))&&f[v].params["*"]!==b.params["*"]};return m==="assets"?s.filter((b,v)=>y(b,v)||E(b,v)):m==="data"?s.filter((b,v)=>{var g;let _=r.routes[b.route.id];if(!_||!_.hasLoader)return!1;if(y(b,v)||E(b,v))return!0;if(b.route.shouldRevalidate){let R=b.route.shouldRevalidate({currentUrl:new URL(h.pathname+h.search+h.hash,window.origin),currentParams:((g=f[0])==null?void 0:g.params)||{},nextUrl:new URL(i,window.origin),nextParams:b.params,defaultShouldRevalidate:!0});if(typeof R=="boolean")return R}return!0}):[]}function ug(i,s,{includeHydrateFallback:f}={}){return ig(i.map(r=>{let h=s.routes[r.route.id];if(!h)return[];let m=[h.module];return h.clientActionModule&&(m=m.concat(h.clientActionModule)),h.clientLoaderModule&&(m=m.concat(h.clientLoaderModule)),f&&h.hydrateFallbackModule&&(m=m.concat(h.hydrateFallbackModule)),h.imports&&(m=m.concat(h.imports)),m}).flat(1))}function ig(i){return[...new Set(i)]}function cg(i){let s={},f=Object.keys(i).sort();for(let r of f)s[r]=i[r];return s}function sg(i,s){let f=new Set;return new Set(s),i.reduce((r,h)=>{let m=JSON.stringify(cg(h));return f.has(m)||(f.add(m),r.push({key:m,link:h})),r},[])}function ir(){let i=S.useContext(Qa);return ur(i,"You must render this element inside a <DataRouterContext.Provider> element"),i}function rg(){let i=S.useContext(gi);return ur(i,"You must render this element inside a <DataRouterStateContext.Provider> element"),i}var cr=S.createContext(void 0);cr.displayName="FrameworkContext";function sr(){let i=S.useContext(cr);return ur(i,"You must render this element inside a <HydratedRouter> element"),i}function og(i,s){let f=S.useContext(cr),[r,h]=S.useState(!1),[m,y]=S.useState(!1),{onFocus:E,onBlur:b,onMouseEnter:v,onMouseLeave:_,onTouchStart:g}=s,R=S.useRef(null);S.useEffect(()=>{if(i==="render"&&y(!0),i==="viewport"){let B=L=>{L.forEach(X=>{y(X.isIntersecting)})},q=new IntersectionObserver(B,{threshold:.5});return R.current&&q.observe(R.current),()=>{q.disconnect()}}},[i]),S.useEffect(()=>{if(r){let B=setTimeout(()=>{y(!0)},100);return()=>{clearTimeout(B)}}},[r]);let k=()=>{h(!0)},D=()=>{h(!1),y(!1)};return f?i!=="intent"?[m,R,{}]:[m,R,{onFocus:Yn(E,k),onBlur:Yn(b,D),onMouseEnter:Yn(v,k),onMouseLeave:Yn(_,D),onTouchStart:Yn(g,k)}]:[!1,R,{}]}function Yn(i,s){return f=>{i&&i(f),f.defaultPrevented||s(f)}}function fg({page:i,...s}){let f=Tp(),{router:r}=ir(),h=S.useMemo(()=>Qh(r.routes,i,r.basename),[r.routes,i,r.basename]);return h?f?S.createElement(hg,{page:i,matches:h,...s}):S.createElement(mg,{page:i,matches:h,...s}):null}function dg(i){let{manifest:s,routeModules:f}=sr(),[r,h]=S.useState([]);return S.useEffect(()=>{let m=!1;return ng(i,s,f).then(y=>{m||h(y)}),()=>{m=!0}},[i,s,f]),r}function hg({page:i,matches:s,...f}){let r=fl(),{future:h}=sr(),{basename:m}=ir(),y=S.useMemo(()=>{if(i===r.pathname+r.search+r.hash)return[];let E=im(i,m,h.unstable_trailingSlashAwareDataRequests,"rsc"),b=!1,v=[];for(let _ of s)typeof _.route.shouldRevalidate=="function"?b=!0:v.push(_.route.id);return b&&v.length>0&&E.searchParams.set("_routes",v.join(",")),[E.pathname+E.search]},[m,h.unstable_trailingSlashAwareDataRequests,i,r,s]);return S.createElement(S.Fragment,null,y.map(E=>S.createElement("link",{key:E,rel:"prefetch",as:"fetch",href:E,...f})))}function mg({page:i,matches:s,...f}){let r=fl(),{future:h,manifest:m,routeModules:y}=sr(),{basename:E}=ir(),{loaderData:b,matches:v}=rg(),_=S.useMemo(()=>Oh(i,s,v,m,r,"data"),[i,s,v,m,r]),g=S.useMemo(()=>Oh(i,s,v,m,r,"assets"),[i,s,v,m,r]),R=S.useMemo(()=>{if(i===r.pathname+r.search+r.hash)return[];let B=new Set,q=!1;if(s.forEach(X=>{var V;let J=m.routes[X.route.id];!J||!J.hasLoader||(!_.some(Y=>Y.route.id===X.route.id)&&X.route.id in b&&((V=y[X.route.id])!=null&&V.shouldRevalidate)||J.hasClientLoader?q=!0:B.add(X.route.id))}),B.size===0)return[];let L=im(i,E,h.unstable_trailingSlashAwareDataRequests,"data");return q&&B.size>0&&L.searchParams.set("_routes",s.filter(X=>B.has(X.route.id)).map(X=>X.route.id).join(",")),[L.pathname+L.search]},[E,h.unstable_trailingSlashAwareDataRequests,b,r,m,_,s,i,y]),k=S.useMemo(()=>ug(g,m),[g,m]),D=dg(g);return S.createElement(S.Fragment,null,R.map(B=>S.createElement("link",{key:B,rel:"prefetch",as:"fetch",href:B,...f})),k.map(B=>S.createElement("link",{key:B,rel:"modulepreload",href:B,...f})),D.map(({key:B,link:q})=>S.createElement("link",{key:B,nonce:f.nonce,...q,crossOrigin:q.crossOrigin??f.crossOrigin})))}function vg(...i){return s=>{i.forEach(f=>{typeof f=="function"?f(s):f!=null&&(f.current=s)})}}var yg=typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u";try{yg&&(window.__reactRouterVersion="7.14.2")}catch{}function pg({basename:i,children:s,unstable_useTransitions:f,window:r}){let h=S.useRef();h.current==null&&(h.current=Wy({window:r,v5Compat:!0}));let m=h.current,[y,E]=S.useState({action:m.action,location:m.location}),b=S.useCallback(v=>{f===!1?E(v):S.startTransition(()=>E(v))},[f]);return S.useLayoutEffect(()=>m.listen(b),[m,b]),S.createElement(Kp,{basename:i,children:s,location:y.location,navigationType:y.action,navigator:m,unstable_useTransitions:f})}var cm=/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i,Je=S.forwardRef(function({onClick:s,discover:f="render",prefetch:r="none",relative:h,reloadDocument:m,replace:y,unstable_mask:E,state:b,target:v,to:_,preventScrollReset:g,viewTransition:R,unstable_defaultShouldRevalidate:k,...D},B){let{basename:q,navigator:L,unstable_useTransitions:X}=S.useContext(wt),J=typeof _=="string"&&cm.test(_),V=Fh(_,q);_=V.to;let Y=wp(_,{relative:h}),K=fl(),G=null;if(E){let Ue=er(E,[],K.unstable_mask?K.unstable_mask.pathname:"/",!0);q!=="/"&&(Ue.pathname=Ue.pathname==="/"?q:Ut([q,Ue.pathname])),G=L.createHref(Ue)}let[te,ye,tt]=og(r,D),We=xg(_,{replace:y,unstable_mask:E,state:b,target:v,preventScrollReset:g,relative:h,viewTransition:R,unstable_defaultShouldRevalidate:k,unstable_useTransitions:X});function $e(Ue){s&&s(Ue),Ue.defaultPrevented||We(Ue)}let Ht=!(V.isExternal||m),St=S.createElement("a",{...D,...tt,href:(Ht?G:void 0)||V.absoluteURL||Y,onClick:Ht?$e:s,ref:vg(B,ye),target:v,"data-discover":!J&&f==="render"?"true":void 0});return te&&!J?S.createElement(S.Fragment,null,St,S.createElement(fg,{page:Y})):St});Je.displayName="Link";var gg=S.forwardRef(function({"aria-current":s="page",caseSensitive:f=!1,className:r="",end:h=!1,style:m,to:y,viewTransition:E,children:b,...v},_){let g=Jn(y,{relative:v.relative}),R=fl(),k=S.useContext(gi),{navigator:D,basename:B}=S.useContext(wt),q=k!=null&&Ag(g)&&E===!0,L=D.encodeLocation?D.encodeLocation(g).pathname:g.pathname,X=R.pathname,J=k&&k.navigation&&k.navigation.location?k.navigation.location.pathname:null;f||(X=X.toLowerCase(),J=J?J.toLowerCase():null,L=L.toLowerCase()),J&&B&&(J=ol(J,B)||J);const V=L!=="/"&&L.endsWith("/")?L.length-1:L.length;let Y=X===L||!h&&X.startsWith(L)&&X.charAt(V)==="/",K=J!=null&&(J===L||!h&&J.startsWith(L)&&J.charAt(L.length)==="/"),G={isActive:Y,isPending:K,isTransitioning:q},te=Y?s:void 0,ye;typeof r=="function"?ye=r(G):ye=[r,Y?"active":null,K?"pending":null,q?"transitioning":null].filter(Boolean).join(" ");let tt=typeof m=="function"?m(G):m;return S.createElement(Je,{...v,"aria-current":te,className:ye,ref:_,style:tt,to:y,viewTransition:E},typeof b=="function"?b(G):b)});gg.displayName="NavLink";var bg=S.forwardRef(({discover:i="render",fetcherKey:s,navigate:f,reloadDocument:r,replace:h,state:m,method:y=mi,action:E,onSubmit:b,relative:v,preventScrollReset:_,viewTransition:g,unstable_defaultShouldRevalidate:R,...k},D)=>{let{unstable_useTransitions:B}=S.useContext(wt),q=Tg(),L=_g(E,{relative:v}),X=y.toLowerCase()==="get"?"get":"post",J=typeof E=="string"&&cm.test(E),V=Y=>{if(b&&b(Y),Y.defaultPrevented)return;Y.preventDefault();let K=Y.nativeEvent.submitter,G=(K==null?void 0:K.getAttribute("formmethod"))||y,te=()=>q(K||Y.currentTarget,{fetcherKey:s,method:G,navigate:f,replace:h,state:m,relative:v,preventScrollReset:_,viewTransition:g,unstable_defaultShouldRevalidate:R});B&&f!==!1?S.startTransition(()=>te()):te()};return S.createElement("form",{ref:D,method:X,action:L,onSubmit:r?b:V,...k,"data-discover":!J&&i==="render"?"true":void 0})});bg.displayName="Form";function Sg(i){return`${i} must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.`}function sm(i){let s=S.useContext(Qa);return ze(s,Sg(i)),s}function xg(i,{target:s,replace:f,unstable_mask:r,state:h,preventScrollReset:m,relative:y,viewTransition:E,unstable_defaultShouldRevalidate:b,unstable_useTransitions:v}={}){let _=Mp(),g=fl(),R=Jn(i,{relative:y});return S.useCallback(k=>{if(Ip(k,s)){k.preventDefault();let D=f!==void 0?f:Qn(g)===Qn(R),B=()=>_(i,{replace:D,unstable_mask:r,state:h,preventScrollReset:m,relative:y,viewTransition:E,unstable_defaultShouldRevalidate:b});v?S.startTransition(()=>B()):B()}},[g,_,R,f,r,h,s,i,m,y,E,b,v])}var jg=0,Eg=()=>`__${String(++jg)}__`;function Tg(){let{router:i}=sm("useSubmit"),{basename:s}=S.useContext(wt),f=Xp(),r=i.fetch,h=i.navigate;return S.useCallback(async(m,y={})=>{let{action:E,method:b,encType:v,formData:_,body:g}=tg(m,s);if(y.navigate===!1){let R=y.fetcherKey||Eg();await r(R,f,y.action||E,{unstable_defaultShouldRevalidate:y.unstable_defaultShouldRevalidate,preventScrollReset:y.preventScrollReset,formData:_,body:g,formMethod:y.method||b,formEncType:y.encType||v,flushSync:y.flushSync})}else await h(y.action||E,{unstable_defaultShouldRevalidate:y.unstable_defaultShouldRevalidate,preventScrollReset:y.preventScrollReset,formData:_,body:g,formMethod:y.method||b,formEncType:y.encType||v,replace:y.replace,state:y.state,fromRouteId:f,flushSync:y.flushSync,viewTransition:y.viewTransition})},[r,h,s,f])}function _g(i,{relative:s}={}){let{basename:f}=S.useContext(wt),r=S.useContext(Qt);ze(r,"useFormAction must be used inside a RouteContext");let[h]=r.matches.slice(-1),m={...Jn(i||".",{relative:s})},y=fl();if(i==null){m.search=y.search;let E=new URLSearchParams(m.search),b=E.getAll("index");if(b.some(_=>_==="")){E.delete("index"),b.filter(g=>g).forEach(g=>E.append("index",g));let _=E.toString();m.search=_?`?${_}`:""}}return(!i||i===".")&&h.route.index&&(m.search=m.search?m.search.replace(/^\?/,"?index&"):"?index"),f!=="/"&&(m.pathname=m.pathname==="/"?f:Ut([f,m.pathname])),Qn(m)}function Ag(i,{relative:s}={}){let f=S.useContext(Ph);ze(f!=null,"`useViewTransitionState` must be used within `react-router-dom`'s `RouterProvider`. Did you accidentally import `RouterProvider` from `react-router`?");let{basename:r}=sm("useViewTransitionState"),h=Jn(i,{relative:s});if(!f.isTransitioning)return!1;let m=ol(f.currentLocation.pathname,r)||f.currentLocation.pathname,y=ol(f.nextLocation.pathname,r)||f.nextLocation.pathname;return yi(h.pathname,y)!=null||yi(h.pathname,m)!=null}const rm=/^\/vault\/([^/]+)\/admin(?=\/|$)/;function om(){if(typeof window>"u")return null;const i=window.location.pathname.match(rm);if(!i)return null;try{return decodeURIComponent(i[1])}catch{return null}}function Ng(){if(typeof window>"u")return"";const i=window.location.pathname,s=i.match(rm);return s?`/vault/${s[1]}/admin`:i==="/admin"||i.startsWith("/admin/")?"/admin":""}let Bl=null,Gt=null,aa=null,rl=null,Dh=!1;const Rg=.8,zg=1e3,Cg=3e4;function wg(){if(typeof window>"u")return;const i=window.location.hash;if(!i||i.length<2)return;const s=new URLSearchParams(i.slice(1)),f=s.get("token");if(!f)return;Bl=f,Gt=null,s.delete("token");const r=s.toString(),h=r.length>0?`#${r}`:"",m=`${window.location.pathname}${window.location.search}${h}`;window.history.replaceState(null,"",m)}function rr(){return Bl}function Mg(){Bl=null,Gt=null,rl=null,Si()}async function Vn(i){if(typeof window>"u")return{kind:"network-error",message:"no window"};let s;try{s=await fetch(`/admin/vault-admin-token/${encodeURIComponent(i)}`,{method:"GET",headers:{accept:"application/json"},credentials:"same-origin"})}catch(r){return{kind:"network-error",message:r instanceof Error?r.message:String(r)}}if(s.status===401||s.status===403||s.status===404)return{kind:"auth-required",status:s.status};if(!s.ok)return{kind:"network-error",message:`hub returned ${s.status}`};let f;try{f=await s.json()}catch(r){return{kind:"network-error",message:r instanceof Error?r.message:"could not parse mint response"}}return typeof f.token!="string"||f.token.length===0?{kind:"network-error",message:"mint response missing token"}:(Bl=f.token,Gt=f.expires_at?Og(f.expires_at):null,rl=i,fm(),Hg(),{kind:"ok",token:f.token})}function Og(i){const s=Date.parse(i);return Number.isNaN(s)?null:s}async function Is(i){if(Bl){if(Gt===null||Gt>Date.now())return{kind:"ok",token:Bl};Bl=null,Gt=null,Si()}return Vn(i)}async function Dg(i){typeof window>"u"||Bl||await Vn(i).catch(()=>{})}function fm(){if(Si(),Gt===null||rl===null||typeof document<"u"&&document.visibilityState==="hidden")return;const i=Date.now(),s=Gt-i;if(s<=0)return;const f=Math.max(Math.floor(s*Rg),zg);aa=setTimeout(()=>{Ug()},f)}function Si(){aa!==null&&(clearTimeout(aa),aa=null)}async function Ug(){if(aa=null,rl===null)return;const i=await Vn(rl);i.kind!=="ok"&&(typeof console<"u"&&console.warn("[vault-admin-spa] proactive token refresh failed; will retry once",i),aa=setTimeout(()=>{aa=null,rl!==null&&Vn(rl).catch(()=>{})},Cg))}function Hg(){Dh||typeof document>"u"||(Dh=!0,document.addEventListener("visibilitychange",()=>{if(document.visibilityState==="hidden"){Si();return}if(rl!==null){if(Gt!==null&&Gt<=Date.now()){Vn(rl).catch(()=>{});return}fm()}}))}class _e extends Error{constructor(s,f){super(f),this.status=s,this.name="HttpError"}}async function et(i,s,f={}){let r=rr();if(!r){const v=await Is(i);if(v.kind!=="ok")throw new _e(401,"no admin token — sign in to the hub to refresh");r=v.token}const{headers:h,...m}=f,y=v=>({accept:"application/json",...h??{},authorization:`Bearer ${v}`}),E=await fetch(s,{...m,headers:y(r)});if(E.status!==401)return E;Mg();const b=await Is(i);return b.kind!=="ok"?E:fetch(s,{...m,headers:y(b.token)})}async function Bg(){const i=await fetch("/vaults/list",{headers:{accept:"application/json"}});if(i.status===404)return[];if(!i.ok)throw new _e(i.status,`vaults/list fetch failed: ${i.status}`);return(await i.json()).vaults??[]}async function Lg(i){const s=await et(i,`/vault/${encodeURIComponent(i)}/`);if(!s.ok)throw new _e(s.status,await rt(s));return await s.json()}async function kg(i){const s=await et(i,`/vault/${encodeURIComponent(i)}/.parachute/mirror`);if(!s.ok)throw new _e(s.status,await rt(s));return await s.json()}async function qg(i,s){const f=await et(i,`/vault/${encodeURIComponent(i)}/.parachute/mirror`,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify(s)});if(!f.ok)throw new _e(f.status,await rt(f));return await f.json()}async function Yg(i){const s=await et(i,`/vault/${encodeURIComponent(i)}/.parachute/mirror/run-now`,{method:"POST"});if(!s.ok)throw new _e(s.status,await rt(s));return await s.json()}async function Gg(i){const s=await et(i,`/vault/${encodeURIComponent(i)}/.parachute/mirror/push-now`,{method:"POST"});if(!s.ok)throw new _e(s.status,await rt(s));return await s.json()}async function dm(i){const s=await et(i,`/vault/${encodeURIComponent(i)}/.parachute/mirror/auth`);if(!s.ok)throw new _e(s.status,await rt(s));return await s.json()}async function Xg(i){const s=await et(i,`/vault/${encodeURIComponent(i)}/.parachute/mirror/auth`,{method:"DELETE"});if(!s.ok)throw new _e(s.status,await rt(s));return await s.json()}async function Qg(i){const s=await et(i,`/vault/${encodeURIComponent(i)}/.parachute/mirror/auth/github/device-code`,{method:"POST"});if(!s.ok)throw new _e(s.status,await rt(s));return await s.json()}async function Vg(i,s){const f=await et(i,`/vault/${encodeURIComponent(i)}/.parachute/mirror/auth/github/poll`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({polling_id:s})});if(!f.ok&&f.status!==404)throw new _e(f.status,await rt(f));return await f.json()}async function Zg(i,s){const f=await et(i,`/vault/${encodeURIComponent(i)}/.parachute/mirror/auth/pat`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(s)});if(!f.ok)throw new _e(f.status,await rt(f));return await f.json()}async function hm(i){const s=await et(i,`/vault/${encodeURIComponent(i)}/.parachute/mirror/auth/github/repos`);if(!s.ok)throw new _e(s.status,await rt(s));return await s.json()}async function Kg(i,s){const f=await et(i,`/vault/${encodeURIComponent(i)}/.parachute/mirror/auth/github/create-repo`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(s)});if(!f.ok)throw new _e(f.status,await rt(f));return await f.json()}async function Jg(i,s){const f=await et(i,`/vault/${encodeURIComponent(i)}/.parachute/mirror/auth/github/select-repo`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(s)});if(!f.ok)throw new _e(f.status,await rt(f));return await f.json()}async function $g(i,s){const f=await et(i,`/vault/${encodeURIComponent(i)}/.parachute/mirror/import`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(s)});if(!f.ok)throw new _e(f.status,await rt(f));return await f.json()}async function rt(i){try{const s=await i.text(),f=JSON.parse(s);if(f.error_description)return f.error_description;if(f.message)return f.message;if(f.error)return f.error;if(s)return s}catch{}return`${i.status} ${i.statusText}`}function mm(i){const s=i.split(".");if(s.length!==3)return null;try{const f=s[1].replace(/-/g,"+").replace(/_/g,"/"),r=f.length%4===0?"":"=".repeat(4-f.length%4),h=atob(f+r),m=JSON.parse(h);return m===null||typeof m!="object"?null:m}catch{return null}}function Fg(i){if(!i)return[];const s=mm(i);if(!s)return[];const f=s.scope;if(typeof f=="string"&&f.length>0)return f.split(/\s+/).filter(h=>h.length>0);const r=s.scopes;return Array.isArray(r)?r.filter(h=>typeof h=="string"&&h.length>0):[]}function vm(i){return Fg(rr()).includes(`vault:${i}:admin`)}function ym(){const i=rr();if(!i)return null;const s=mm(i);if(!s)return null;const f=s.iss;return typeof f!="string"||f.length===0?null:f.replace(/\/$/,"")}const Uh=5e3;function or({vaultName:i,status:s,onRecovered:f}){const r=s===403;if(S.useEffect(()=>{if(r)return;let E=!1,b=null;const v=async()=>{if(E||typeof document<"u"&&document.visibilityState!=="visible")return;const g=await Is(i);if(!E){if(g.kind==="ok"){f();return}b=setTimeout(()=>void v(),Uh)}},_=()=>{typeof document>"u"||document.visibilityState==="visible"&&(b!==null&&(clearTimeout(b),b=null),v())};return b=setTimeout(()=>void v(),Uh),typeof document<"u"&&document.addEventListener("visibilitychange",_),()=>{E=!0,b!==null&&clearTimeout(b),typeof document<"u"&&document.removeEventListener("visibilitychange",_)}},[i,f,r]),r)return c.jsxs("div",{className:"warn-banner",role:"status",children:[c.jsxs("p",{style:{margin:"0 0 0.5rem"},children:["You're signed in, but vault management is restricted to the hub admin."," ",c.jsx("a",{href:"/account/",children:"Go to your account →"})]}),c.jsx("p",{className:"dim",style:{margin:0,fontSize:"0.85rem"},children:"Ask the hub admin if you need access to manage this vault."})]});const h=Wg(),m=`/login?next=${encodeURIComponent(h)}`,y=s===404?"This hub doesn't host a vault by that name.":"You're not signed in to the hub.";return c.jsxs("div",{className:"warn-banner",role:"status",children:[c.jsxs("p",{style:{margin:"0 0 0.5rem"},children:[y," ",c.jsx("a",{href:m,children:"Sign in to the hub →"})]}),c.jsx("p",{className:"dim",style:{margin:0,fontSize:"0.85rem"},children:"After signing in, this page will refresh automatically."})]})}function Wg(){return typeof window>"u"?"/":`${window.location.pathname}${window.location.search}${window.location.hash}`}function Hh({vaultName:i}={}){const s=lr(),f=i??s.name,r=i!==void 0,h=r?"/tokens":`/vault/${encodeURIComponent(f??"")}/tokens`,m=r?"/mirror":`/vault/${encodeURIComponent(f??"")}/mirror`,[y,E]=S.useState({kind:"loading"}),[b,v]=S.useState(0),_=S.useCallback(()=>v(k=>k+1),[]);if(S.useEffect(()=>{let k=!1;if(!f){E({kind:"missing"});return}return E({kind:"loading"}),Lg(f).then(D=>{k||E({kind:"ok",vault:D})}).catch(D=>{if(k)return;if(D instanceof _e){if(D.status===401||D.status===403){E({kind:"auth-required",status:D.status});return}if(D.status===404){E({kind:"missing"});return}E({kind:"error",message:`${D.status}: ${D.message}`});return}const B=D instanceof Error?D.message:String(D);E({kind:"error",message:B})}),()=>{k=!0}},[f,b]),y.kind==="loading")return c.jsxs("div",{children:[c.jsx("h2",{children:"Vault"}),c.jsx("p",{className:"muted",children:"Loading…"})]});if(y.kind==="auth-required")return c.jsxs("div",{children:[c.jsxs("h2",{children:["Vault ",c.jsx("code",{children:f})]}),c.jsx(or,{vaultName:f??"",status:y.status,onRecovered:_}),r?null:c.jsx(Je,{to:"/",children:"← Back to vaults"})]});if(y.kind==="missing")return c.jsxs("div",{children:[c.jsx("h2",{children:"Vault not found"}),c.jsxs("p",{className:"muted",children:["No vault named ",c.jsx("code",{children:f})," is registered on this server."]}),r?null:c.jsx(Je,{to:"/",children:"← Back to vaults"})]});if(y.kind==="error")return c.jsxs("div",{children:[c.jsxs("h2",{children:["Vault ",c.jsx("code",{children:f})]}),c.jsx("div",{className:"error-banner",children:c.jsx("code",{children:y.message})}),r?null:c.jsx(Je,{to:"/",children:"← Back to vaults"})]});const{vault:g}=y,R=`/vault/${g.name}`;return c.jsxs("div",{children:[c.jsxs("div",{className:"list-header",children:[c.jsxs("h2",{children:["Vault ",c.jsx("code",{children:g.name})]}),r?null:c.jsx(Je,{to:"/",className:"muted",children:"← All vaults"})]}),c.jsxs("div",{className:"kv section",children:[c.jsx("div",{children:"Name"}),c.jsx("div",{children:c.jsx("code",{children:g.name})}),g.description?c.jsxs(c.Fragment,{children:[c.jsx("div",{children:"Description"}),c.jsx("div",{children:g.description})]}):null,c.jsx("div",{children:"Mount"}),c.jsx("div",{children:c.jsx("code",{children:R})}),g.createdAt?c.jsxs(c.Fragment,{children:[c.jsx("div",{children:"Created"}),c.jsx("div",{children:c.jsx("code",{children:g.createdAt})})]}):null]}),c.jsxs("div",{className:"section",children:[c.jsx("h3",{style:{margin:"0 0 0.85rem",fontSize:"1rem",fontWeight:500},children:"Stats"}),c.jsxs("div",{className:"stats",children:[c.jsxs("div",{className:"stat",children:[c.jsx("div",{className:"label",children:"Notes"}),c.jsx("div",{className:"value",children:g.stats.totalNotes})]}),c.jsxs("div",{className:"stat",children:[c.jsx("div",{className:"label",children:"Tags"}),c.jsx("div",{className:"value",children:g.stats.tagCount})]}),c.jsxs("div",{className:"stat",children:[c.jsx("div",{className:"label",children:"Attachments"}),c.jsx("div",{className:"value",children:g.stats.attachmentCount})]}),c.jsxs("div",{className:"stat",children:[c.jsx("div",{className:"label",children:"Links"}),c.jsx("div",{className:"value",children:g.stats.linkCount})]})]})]}),c.jsxs("div",{className:"section",children:[c.jsx("h3",{style:{margin:"0 0 0.85rem",fontSize:"1rem",fontWeight:500},children:"Manage"}),c.jsxs("ul",{className:"manage-list",children:[c.jsxs("li",{children:[c.jsx(Je,{to:h,children:"Tokens →"}),c.jsxs("span",{className:"dim",children:[" mint, list, and revoke ",c.jsx("code",{children:"pvt_*"})," tokens"]})]}),c.jsxs("li",{children:[c.jsx(Je,{to:m,children:"Git backup →"}),c.jsx("span",{className:"dim",children:" mirror this vault to a git repository on a schedule, or on demand"})]}),c.jsx(Ig,{vaultName:g.name})]})]})]})}function Ig({vaultName:i}){const s=ym();if(!s)return c.jsxs("li",{children:[c.jsx("span",{children:"Permissions →"}),c.jsxs("span",{className:"dim",children:[" ","grants are managed on hub (the OAuth issuer); link will be live when hub ships its permissions UI."]})]});const f=`${s}/hub/permissions?vault=${encodeURIComponent(i)}`;return c.jsxs("li",{children:[c.jsx("a",{href:f,children:"Permissions →"}),c.jsxs("span",{className:"dim",children:[" ","grants are managed on hub (the OAuth issuer); link will be live when hub ships its permissions UI."]})]})}const Pg=[{value:"events",label:"On change (default)"},{value:"manual",label:"Manual only"}],e0=[{id:"history",label:"History",subtext:"Local audit trail. Hidden under vault data. Events-driven.",apply:i=>({...i,enabled:!0,location:"internal",sync_mode:"events",auto_commit:!0,auto_push:!1})},{id:"live",label:"External folder mirror",subtext:"Visible folder. Open in Obsidian, push to GitHub. Events-driven.",apply:i=>({...i,enabled:!0,location:"external",sync_mode:"events",auto_commit:!0})},{id:"manual",label:"Manual Export",subtext:"Snapshot on demand. No auto-fire.",apply:i=>({...i,enabled:!0,location:"external",sync_mode:"manual",auto_commit:!0,auto_push:!1})}];function Bh({vaultName:i}={}){const s=lr(),f=i??s.name,r=i!==void 0,h=r?"/":`/vault/${encodeURIComponent(f??"")}`,[m,y]=S.useState({kind:"loading"}),[E,b]=S.useState(0);S.useEffect(()=>{let _=!1;if(f)return y({kind:"loading"}),kg(f).then(g=>{_||y({kind:"ok",snapshot:g})}).catch(g=>{if(_)return;if(g instanceof _e&&(g.status===401||g.status===403)){y({kind:"auth-required",status:g.status});return}const R=g instanceof Error?g.message:String(g);y({kind:"error",message:R})}),()=>{_=!0}},[f,E]);const v=S.useCallback(()=>b(_=>_+1),[]);return f?c.jsxs("div",{children:[c.jsxs("div",{className:"list-header",children:[c.jsxs("h2",{children:["Backup for ",c.jsx("code",{children:f})]}),c.jsx(Je,{to:h,className:"muted",children:"← Vault detail"})]}),m.kind==="loading"?c.jsx("p",{className:"muted",children:"Loading…"}):null,m.kind==="auth-required"?c.jsx(or,{vaultName:f,status:m.status,onRecovered:v}):null,m.kind==="error"?c.jsx("div",{className:"error-banner",children:c.jsx("code",{children:m.message})}):null,m.kind==="ok"?c.jsx(t0,{vaultName:f,snapshot:m.snapshot,onRefresh:()=>b(_=>_+1),onSnapshot:_=>y({kind:"ok",snapshot:_})}):null]}):c.jsxs("div",{children:[c.jsx("h2",{children:"Mirror"}),c.jsx("p",{className:"muted",children:"Missing vault name."}),r?null:c.jsx(Je,{to:"/",children:"← Back to vaults"})]})}function t0({vaultName:i,snapshot:s,onRefresh:f,onSnapshot:r}){const h=vm(i),[m,y]=S.useState(null),[E,b]=S.useState(null);S.useEffect(()=>{if(!h)return;let g=!1;return dm(i).then(R=>{g||y(R)}).catch(R=>{g||b(R instanceof Error?R.message:String(R))}),()=>{g=!0}},[i,h]);const[v,_]=S.useState(!1);return c.jsxs(c.Fragment,{children:[c.jsx(l0,{status:s.status,config:s.config,creds:m}),h?null:c.jsxs("div",{className:"warn-banner",children:["You're viewing this page with a read-only token. Saving config + manual run require ",c.jsxs("code",{children:["vault:",i,":admin"]}),`. Re-enter from the hub directory's "Manage" link with an admin-scoped session to make changes.`]}),h?c.jsx(u0,{vaultName:i,creds:m,credsError:E,onCredsChanged:y,onCredsSaved:()=>{f()},locationIsExternal:s.config.location==="external"}):null,c.jsxs("div",{className:"section",children:[c.jsx("button",{type:"button",className:"secondary",onClick:()=>_(g=>!g),"aria-expanded":v,children:v?"Hide advanced settings":"Advanced settings"}),v?null:c.jsx("p",{className:"dim",style:{margin:"0.6rem 0 0"},children:"Manual exports, run-now, an external (Obsidian-visible) mirror folder, commit settings, and import-from-a-repo. Most owners never need these."})]}),v?c.jsxs(c.Fragment,{children:[c.jsx(a0,{status:s.status,config:s.config,creds:m,canRun:h&&s.status.enabled,vaultName:i,onSnapshot:r}),c.jsx(n0,{vaultName:i,initial:s.config,readOnly:!h,creds:m,onSaved:g=>{r(g),f()}}),h?c.jsx(o0,{vaultName:i,creds:m}):null]}):null]})}function l0({status:i,config:s,creds:f}){var y;if(!s.enabled)return c.jsxs("div",{className:"warn-banner",role:"status",children:[c.jsx("strong",{children:"Version history is off."})," This vault isn't saving a local history of changes right now. Open ",c.jsx("strong",{children:"Advanced settings"})," ","below to turn it back on."]});const r=!!(f!=null&&f.active_method),h=s.auto_push&&r,m=(f==null?void 0:f.active_method)==="github_oauth"?(y=f.github_oauth)==null?void 0:y.user_login:void 0;return c.jsxs("div",{className:"mint-banner",role:"status",style:{marginBottom:"1rem"},children:[h?c.jsxs(c.Fragment,{children:[c.jsx("strong",{children:"✓ Version history + backed up off this machine."})," ",m?c.jsxs(c.Fragment,{children:["Every change is saved locally and pushed to GitHub as"," ",c.jsxs("code",{children:["@",m]}),"."]}):c.jsx(c.Fragment,{children:"Every change is saved locally and pushed to your git remote automatically."})]}):c.jsxs(c.Fragment,{children:[c.jsx("strong",{children:"✓ Version history — on."})," Your vault automatically saves a full local history of every change. Want an off-machine copy too? Use ",c.jsx("strong",{children:"Back up to GitHub"})," below."]}),i.last_error?c.jsxs("p",{className:"dim",style:{margin:"0.5rem 0 0"},children:["Heads up — the last backup pass reported an error. See"," ",c.jsx("strong",{children:"Advanced settings"})," below for details."]}):null]})}function a0({status:i,config:s,creds:f,canRun:r,vaultName:h,onSnapshot:m}){const y=s.enabled,[E,b]=S.useState(!1),[v,_]=S.useState(!1),[g,R]=S.useState(null),k=async()=>{b(!0),R(null);try{const L=await Yg(h);m(L)}catch(L){R(L instanceof Error?L.message:String(L))}finally{b(!1)}},D=async()=>{_(!0),R(null);try{const L=await Gg(h);m(L)}catch(L){R(L instanceof Error?L.message:String(L))}finally{_(!1)}},B=!!(f!=null&&f.active_method)||s.auto_push,q=!r||v||!B;return c.jsxs("div",{className:"section",children:[c.jsx("h3",{style:{margin:"0 0 0.85rem",fontSize:"1rem",fontWeight:500},children:"Status"}),c.jsxs("div",{className:"kv",children:[c.jsx("div",{children:"Enabled"}),c.jsx("div",{children:y?c.jsx("code",{children:"yes"}):c.jsx("code",{children:"no"})}),c.jsx("div",{children:"Watch loop"}),c.jsx("div",{children:i.watch_running?c.jsx("code",{children:"running"}):c.jsx("code",{children:"stopped"})}),c.jsx("div",{children:"Path"}),c.jsx("div",{children:i.mirror_path?c.jsx("code",{children:i.mirror_path}):c.jsx("span",{className:"dim",children:"—"})}),c.jsx("div",{children:"Last export"}),c.jsx("div",{children:i.last_export_at?c.jsxs(c.Fragment,{children:[c.jsx("code",{children:i.last_export_at}),i.last_export_notes_count!==null?c.jsxs("span",{className:"dim",children:[" ","· ",i.last_export_notes_count," note",i.last_export_notes_count===1?"":"s"]}):null]}):c.jsx("span",{className:"dim",children:"never"})}),c.jsx("div",{children:"Last commit"}),c.jsx("div",{children:i.last_commit_sha?c.jsx("code",{children:i.last_commit_sha.slice(0,10)}):c.jsx("span",{className:"dim",children:"—"})}),c.jsx("div",{children:"Last push"}),c.jsx("div",{children:i.last_push_at?c.jsxs(c.Fragment,{children:[c.jsx("code",{children:i.last_push_at}),i.last_push_sha?c.jsxs("span",{className:"dim",children:[" ","· ",c.jsx("code",{children:i.last_push_sha.slice(0,10)})]}):null]}):c.jsx("span",{className:"dim",children:"never"})}),i.commits_unpushed!==null&&i.commits_unpushed>0?c.jsxs(c.Fragment,{children:[c.jsx("div",{}),c.jsxs("div",{className:"dim",children:[i.commits_unpushed," commit",i.commits_unpushed===1?"":"s"," ready to push"]})]}):null]}),i.last_push_error?c.jsxs("div",{className:"error-banner",style:{marginTop:"1rem"},children:[c.jsx("strong",{children:"Last push failed:"})," ",c.jsx("code",{children:i.last_push_error})]}):null,i.last_error?c.jsxs("div",{className:"error-banner",style:{marginTop:"1rem"},children:[c.jsx("strong",{children:"Last error:"})," ",c.jsx("code",{children:i.last_error})]}):null,g?c.jsx("div",{className:"error-banner",style:{marginTop:"1rem"},children:c.jsx("code",{children:g})}):null,c.jsxs("div",{className:"actions",children:[c.jsx("button",{type:"button",onClick:k,disabled:!r||E,title:y?r?void 0:"Admin scope required to trigger a manual export.":"Enable the mirror first, then trigger a manual export.",children:E?"Running…":"Run export now"}),c.jsx("button",{type:"button",className:"secondary",onClick:D,disabled:q,title:y?r?B?void 0:"Wire credentials or turn on auto-push to push to a remote.":"Admin scope required to push.":"Enable the mirror first, then push.",children:v?"Pushing…":"Push now"})]})]})}function n0({vaultName:i,initial:s,readOnly:f,creds:r,onSaved:h}){const[m,y]=S.useState(s),[E,b]=S.useState(!1),[v,_]=S.useState(!1),[g,R]=S.useState(null),[k,D]=S.useState(null),[B,q]=S.useState(0);S.useEffect(()=>{y(s)},[s]);const L=V=>{const Y=V.apply(m);y(Y)},X=V=>{y(Y=>({...Y,sync_mode:V}))},J=async V=>{if(V.preventDefault(),!f){_(!0),R(null),D(null);try{const Y=await qg(i,m);h(Y),q(K=>K+1),setTimeout(()=>q(0),3e3)}catch(Y){if(Y instanceof _e){const K=Y.message;R(K);const G=K.toLowerCase();G.includes("external_path")?D("external_path"):G.includes("commit_template")?D("commit_template"):G.includes("safety_net_seconds")?D("safety_net_seconds"):G.includes("sync_mode")?D("sync_mode"):G.includes("auto_push")?D("auto_push"):G.includes("location")?D("location"):D(null)}else R(Y instanceof Error?Y.message:String(Y))}finally{_(!1)}}};return c.jsxs("form",{className:"section",onSubmit:J,children:[c.jsx("h3",{style:{margin:"0 0 0.85rem",fontSize:"1rem",fontWeight:500},children:"Configuration"}),c.jsxs("div",{className:"form-row",children:[c.jsxs("label",{children:[c.jsx("input",{type:"checkbox",checked:m.enabled,disabled:f,onChange:V=>y(Y=>({...Y,enabled:V.target.checked})),style:{width:"auto",marginRight:"0.5rem"}}),"Enable mirror"]}),c.jsx("p",{className:"dim",style:{margin:"0.35rem 0 0"},children:"Master switch. When off, no export / commit / push runs."})]}),c.jsxs("div",{className:"form-row",children:[c.jsx("label",{children:"Presets"}),c.jsxs("p",{className:"dim",style:{marginTop:0,marginBottom:"0.5rem",fontSize:"0.9em"},children:[c.jsx("strong",{children:"History"})," is the default every vault ships with — vault manages the history folder under its own data dir, no path to pick, no remote to configure. ",c.jsx("strong",{children:"External folder mirror"})," +"," ",c.jsx("strong",{children:"Manual Export"})," are for operators who want to point at a visible folder (Obsidian, GitHub)."]}),c.jsx("div",{className:"preset-grid",children:e0.map(V=>c.jsxs("button",{type:"button",className:"preset-card",disabled:f,onClick:()=>L(V),"aria-label":`Apply ${V.label} preset`,children:[c.jsx("strong",{children:V.label}),c.jsx("span",{className:"dim",children:V.subtext})]},V.id))})]}),c.jsxs("div",{className:"form-row",children:[c.jsx("label",{children:"Location"}),c.jsxs("div",{children:[c.jsxs("label",{className:"radio-row",children:[c.jsx("input",{type:"radio",name:"location",value:"internal",checked:m.location==="internal",disabled:f,onChange:()=>y(V=>({...V,location:"internal"}))}),c.jsxs("span",{children:["Internal ",c.jsx("span",{className:"dim",children:"— hidden under vault data dir"})]})]}),c.jsxs("label",{className:"radio-row",children:[c.jsx("input",{type:"radio",name:"location",value:"external",checked:m.location==="external",disabled:f,onChange:()=>y(V=>({...V,location:"external"}))}),c.jsxs("span",{children:["External ",c.jsx("span",{className:"dim",children:"— operator-picked path"})]})]})]})]}),m.location==="external"?c.jsxs("div",{className:"form-row",children:[c.jsx("label",{htmlFor:"external-path",children:"External path"}),c.jsx("input",{id:"external-path",type:"text",value:m.external_path??"",disabled:f,onChange:V=>y(Y=>({...Y,external_path:V.target.value.length>0?V.target.value:null})),placeholder:"/Users/you/Documents/vault-mirror","aria-invalid":k==="external_path"}),c.jsxs("div",{className:"warn-banner",style:{marginTop:"0.5rem"},role:"alert",children:["Path must exist AND be a git repo (run ",c.jsx("code",{children:"git init"})," first if needed)."]})]}):null,c.jsxs("div",{className:"form-row",children:[c.jsx("label",{htmlFor:"sync-mode-select",children:"Sync mode"}),c.jsx("select",{id:"sync-mode-select",value:m.sync_mode,disabled:f,onChange:V=>X(V.target.value),children:Pg.map(V=>c.jsx("option",{value:V.value,children:V.label},V.value))}),c.jsx("p",{className:"dim",style:{margin:"0.35rem 0 0"},children:m.sync_mode==="manual"?'No auto-fire. Exports only run when you click "Run export now" (or run `parachute-vault export` from the CLI).':"Every change to a note, tag, or attachment triggers an export within ~500ms. A background safety check runs hourly to catch anything missed."})]}),c.jsxs("div",{className:"form-row",children:[c.jsxs("label",{children:[c.jsx("input",{type:"checkbox",checked:m.auto_commit,disabled:f,onChange:V=>y(Y=>({...Y,auto_commit:V.target.checked})),style:{width:"auto",marginRight:"0.5rem"}}),"Commit after each export"]}),m.auto_commit?null:c.jsx("p",{className:"hint",style:{marginTop:"0.25rem",fontSize:"0.85em"},children:"Note: the export cursor still advances after each pass. Subsequent runs only re-export notes written since the last pass — even when triggered manually."})]}),m.location==="external"||r!=null&&r.active_method?c.jsxs("div",{className:"form-row",children:[c.jsxs("label",{children:[c.jsx("input",{type:"checkbox",checked:m.auto_push,disabled:f,onChange:V=>y(Y=>({...Y,auto_push:V.target.checked})),style:{width:"auto",marginRight:"0.5rem"}}),"Push after each commit"]}),c.jsx("p",{className:"dim",style:{margin:"0.35rem 0 0",fontSize:"0.9em"},children:"When credentials are configured, vault can push the mirror's commits to your remote regardless of whether the mirror folder lives under vault's data dir or somewhere visible."}),m.auto_push?r!=null&&r.active_method?c.jsxs("div",{className:"info-banner",style:{marginTop:"0.5rem"},role:"status",children:[r.active_method==="github_oauth"&&r.github_oauth?c.jsxs(c.Fragment,{children:["Will push to ",c.jsxs("code",{children:["@",r.github_oauth.user_login]})," on GitHub."]}):r.active_method==="pat"&&r.pat?c.jsxs(c.Fragment,{children:["Will push using saved credential: ",c.jsx("code",{children:r.pat.label}),"."]}):c.jsx(c.Fragment,{children:"Will push using saved credential."})," ","Failed pushes are logged but won't crash the export."]}):c.jsxs("div",{className:"warn-banner",style:{marginTop:"0.5rem"},role:"alert",children:["Auto-push needs git credentials. Either connect GitHub in the"," ",c.jsx("strong",{children:"Back up to GitHub"})," section above, or paste a Personal Access Token + remote URL there. Failed pushes are logged but won't crash the export."]}):null]}):null,c.jsx("div",{className:"form-row",children:c.jsxs("button",{type:"button",className:"secondary",onClick:()=>b(V=>!V),children:[E?"Hide":"Show"," advanced"]})}),E?c.jsxs("div",{className:"form-row",children:[c.jsx("label",{htmlFor:"commit-template",children:"Commit template"}),c.jsx("input",{id:"commit-template",type:"text",value:m.commit_template,disabled:f,onChange:V=>y(Y=>({...Y,commit_template:V.target.value})),"aria-invalid":k==="commit_template"}),c.jsxs("p",{className:"dim",style:{margin:"0.35rem 0 0"},children:["Supports ",c.jsx("code",{children:"{{date}}"}),", ",c.jsx("code",{children:"{{notes_changed}}"}),","," ",c.jsx("code",{children:"{{plural}}"}),", ",c.jsx("code",{children:"{{first_note_title}}"}),","," ",c.jsx("code",{children:"{{vault_name}}"}),"."]})]}):null,g?c.jsx("div",{className:"error-banner",role:"alert",children:c.jsx("code",{children:g})}):null,B>0?c.jsx("div",{className:"mint-banner",style:{padding:"0.75rem 1rem",marginBottom:"1rem"},role:"status",children:"Saved."}):null,c.jsx("div",{className:"actions",children:c.jsx("button",{type:"submit",disabled:f||v,children:v?"Saving…":"Save"})})]})}function u0({vaultName:i,creds:s,credsError:f,onCredsChanged:r,onCredsSaved:h,locationIsExternal:m}){const[y,E]=S.useState(!1),[b,v]=S.useState(!1),[_,g]=S.useState(!1),[R,k]=S.useState(null),[D,B]=S.useState(null),q=(s==null?void 0:s.active_method)!==null&&(s==null?void 0:s.active_method)!==void 0,L=async()=>{if(confirm("Disconnect git remote credentials? Auto-push will stop working until you reconnect.")){g(!0),k(null);try{const X=await Xg(i);r(X)}catch(X){k(X instanceof Error?X.message:String(X))}finally{g(!1)}}};return c.jsxs("div",{className:"section",id:"git-remote-section",children:[c.jsx("h3",{style:{margin:"0 0 0.85rem",fontSize:"1rem",fontWeight:500},children:q?"Backed up to a git remote":"Back up to GitHub"}),c.jsx("p",{className:"dim",style:{marginTop:0},children:q?c.jsxs(c.Fragment,{children:["Your vault pushes its history to an off-machine git remote. Credentials are stored on this server with ",c.jsx("code",{children:"0600"})," file permissions — never sent to GitHub or any third party."]}):c.jsxs(c.Fragment,{children:["Keep an off-machine copy: push your vault's history to GitHub (or any HTTPS git host). Credentials are stored on this server with"," ",c.jsx("code",{children:"0600"})," file permissions, never sent to a third party."]})}),!m&&!(s!=null&&s.active_method)?c.jsx("div",{className:"info-banner",style:{marginBottom:"0.75rem"},role:"status",children:"Internal mirrors live under the vault's data directory. To push them to a remote, paste a Personal Access Token + remote URL below — that wires the remote and turns on auto-push automatically."}):null,f?c.jsxs("div",{className:"error-banner",role:"alert",children:["Could not load credential status: ",c.jsx("code",{children:f})]}):null,q?c.jsxs("div",{className:"kv",style:{marginBottom:"0.75rem"},children:[c.jsx("div",{children:"Status"}),c.jsx("div",{children:(s==null?void 0:s.active_method)==="github_oauth"&&s.github_oauth?c.jsxs(c.Fragment,{children:["Connected to ",c.jsxs("code",{children:["@",s.github_oauth.user_login]})," on GitHub"]}):(s==null?void 0:s.active_method)==="pat"&&s.pat?c.jsxs(c.Fragment,{children:["Custom credential: ",c.jsx("code",{children:s.pat.label})]}):null}),(s==null?void 0:s.active_method)==="github_oauth"&&s.github_oauth?c.jsxs(c.Fragment,{children:[c.jsx("div",{children:"Token"}),c.jsxs("div",{children:[c.jsx("code",{children:s.github_oauth.token_preview})," ",c.jsxs("span",{className:"dim",children:["· scope ",s.github_oauth.scope||"—"," · authorized"," ",s.github_oauth.authorized_at.slice(0,10)]})]})]}):null,(s==null?void 0:s.active_method)==="pat"&&s.pat?c.jsxs(c.Fragment,{children:[c.jsx("div",{children:"Remote"}),c.jsx("div",{children:c.jsx("code",{children:s.pat.remote_url})}),c.jsx("div",{children:"Token"}),c.jsx("div",{children:c.jsx("code",{children:s.pat.token_preview})})]}):null]}):c.jsx("p",{className:"dim",children:"Not connected. Auto-push won't work until you connect. Personal Access Token is the universal path (works with GitHub, GitLab, Gitea, Bitbucket, anything that takes an HTTPS token); the GitHub shortcut just saves a step for GitHub users."}),R?c.jsx("div",{className:"error-banner",role:"alert",children:c.jsx("code",{children:R})}):null,D?c.jsxs("div",{className:"mint-banner",style:{marginBottom:"0.75rem"},role:"status",children:[c.jsx("strong",{children:"Credentials saved."})," ",(()=>{const X=D,J=X.initial_push.fired&&X.initial_push.pushed?X.initial_push.sha:void 0,V=X.initial_push.fired&&!X.initial_push.pushed?X.initial_push.error:void 0;return X.auto_push_was_already_enabled?J?c.jsxs(c.Fragment,{children:["Auto-push was already on; just pushed"," ",c.jsx("code",{children:J.slice(0,10)}),"."]}):V?c.jsx(c.Fragment,{children:"Auto-push was already on; the push attempt failed — see Advanced settings → Status for details."}):c.jsx(c.Fragment,{children:"Auto-push was already on; nothing to push right now."}):X.auto_push_enabled?J?c.jsxs(c.Fragment,{children:["Auto-push enabled and the first push landed"," ",c.jsx("code",{children:J.slice(0,10)}),". Your next commit will push to the remote too."]}):V?c.jsx(c.Fragment,{children:"Auto-push enabled. The initial push attempt failed — see Advanced settings → Status for the error."}):c.jsx(c.Fragment,{children:"Auto-push enabled. Your next commit will push to the remote."}):c.jsx(c.Fragment,{children:"Credentials wired. Auto-push remains off; flip it on in Advanced settings → Configuration to push commits automatically."})})()," ",c.jsx("button",{type:"button",className:"secondary",onClick:()=>B(null),style:{marginLeft:"0.5rem"},"aria-label":"Dismiss",children:"Dismiss"})]}):null,c.jsx("div",{className:"actions",children:q?c.jsx("button",{type:"button",className:"secondary",onClick:L,disabled:_,children:_?"Disconnecting…":"Disconnect"}):c.jsxs(c.Fragment,{children:[c.jsx("button",{type:"button",onClick:()=>v(!0),children:"Use Personal Access Token"}),c.jsx("button",{type:"button",className:"secondary",onClick:()=>E(!0),children:"Connect GitHub (one-click for GitHub users)"})]})}),y?c.jsx(i0,{vaultName:i,onClose:()=>E(!1),onConnected:(X,J)=>{r(X),J&&(B(J),h(J)),E(!1)}}):null,b?c.jsx(r0,{vaultName:i,onClose:()=>v(!1),onSaved:X=>{r(X),B(X),h(X),v(!1)}}):null]})}function i0({vaultName:i,onClose:s,onConnected:f}){const[r,h]=S.useState({kind:"starting"}),[m,y]=S.useState(Date.now()),E=S.useRef(!1);S.useEffect(()=>{const v=setInterval(()=>y(Date.now()),1e3);return()=>clearInterval(v)},[]),S.useEffect(()=>{let v=!1;return E.current=!1,Qg(i).then(_=>{v||h({kind:"polling",code:_,pollIntervalMs:Math.max(_.interval,1)*1e3,startedAt:Date.now()})}).catch(_=>{v||h({kind:"error",message:_ instanceof Error?_.message:String(_)})}),()=>{v=!0,E.current=!0}},[i]),S.useEffect(()=>{if(r.kind!=="polling")return;const v=r.code;let _=!1,g=null,R=r.pollIntervalMs;const k=async()=>{if(!(_||E.current))try{const D=await Vg(i,v.polling_id);if(_)return;if(D.state==="granted"){h({kind:"granted",user:{login:D.user.login}});return}if(D.state==="denied"){h({kind:"error",message:"Authorization denied."});return}if(D.state==="expired"){h({kind:"error",message:"The device code expired. Try again."});return}D.state==="slow_down"&&(R=D.interval*1e3),g=setTimeout(k,R)}catch(D){if(_)return;h({kind:"error",message:D instanceof Error?D.message:String(D)})}};return g=setTimeout(k,R),()=>{_=!0,g&&clearTimeout(g)}},[r,i]);const b=async v=>{try{await navigator.clipboard.writeText(v)}catch{}};return c.jsx("div",{className:"modal-backdrop",role:"dialog","aria-modal":"true",children:c.jsxs("div",{className:"modal",children:[c.jsxs("div",{className:"list-header",children:[c.jsx("h3",{style:{margin:0},children:"Connect GitHub"}),c.jsx("button",{type:"button",className:"secondary",onClick:s,children:"Close"})]}),r.kind==="starting"?c.jsx("p",{className:"muted",children:"Requesting device code from GitHub…"}):null,r.kind==="polling"?c.jsxs(c.Fragment,{children:[c.jsxs("p",{children:[c.jsx("strong",{children:"Step 1."})," Open"," ",c.jsx("a",{href:r.code.verification_uri,target:"_blank",rel:"noreferrer",children:r.code.verification_uri})," ","in your browser."]}),c.jsxs("p",{children:[c.jsx("strong",{children:"Step 2."})," Enter this code:"]}),c.jsxs("div",{className:"device-code-row",children:[c.jsx("code",{className:"device-code",children:r.code.user_code}),c.jsx("button",{type:"button",className:"secondary",onClick:()=>b(r.code.user_code),children:"Copy"})]}),c.jsxs("p",{className:"dim",children:["Waiting for authorization…"," ",c0(r.code.expires_in,r.startedAt,m)]})]}):null,r.kind==="granted"?c.jsx(s0,{vaultName:i,user:r.user,onPicked:async(v,_)=>{const g=await Jg(i,{owner:v,name:_}),R=await dm(i);f(R,g)},onError:v=>h({kind:"error",message:v})}):null,r.kind==="error"?c.jsxs(c.Fragment,{children:[c.jsx("div",{className:"error-banner",role:"alert",children:c.jsx("code",{children:r.message})}),c.jsxs("div",{className:"actions",children:[c.jsx("button",{type:"button",onClick:()=>h({kind:"starting"}),children:"Try again"}),c.jsx("button",{type:"button",className:"secondary",onClick:s,children:"Close"})]})]}):null]})})}function c0(i,s,f){const r=Math.floor((f-s)/1e3),h=Math.max(0,i-r),m=Math.floor(h/60),y=h%60;return`${m}:${String(y).padStart(2,"0")} remaining`}function s0({vaultName:i,user:s,onPicked:f,onError:r}){const[h,m]=S.useState(null),[y,E]=S.useState(!1),[b,v]=S.useState(""),[_,g]=S.useState(null),[R,k]=S.useState(!1),[D,B]=S.useState(""),[q,L]=S.useState(!1);S.useEffect(()=>{let Y=!1;return hm(i).then(({repos:K,truncated:G})=>{Y||(m(K),E(!!G))}).catch(K=>{Y||r(K instanceof Error?K.message:String(K))}),()=>{Y=!0}},[i,r]);const X=(h??[]).filter(Y=>b.length===0?!0:Y.full_name.toLowerCase().includes(b.toLowerCase())),J=async(Y,K)=>{g(`${Y}/${K}`);try{await f(Y,K)}catch(G){r(G instanceof Error?G.message:String(G))}finally{g(null)}},V=async()=>{if(D.trim().length!==0){k(!0);try{const Y=await Kg(i,{name:D.trim(),description:"Parachute Vault mirror",private:!0});await f(Y.owner,Y.name)}catch(Y){r(Y instanceof Error?Y.message:String(Y))}finally{k(!1)}}};return c.jsxs(c.Fragment,{children:[c.jsxs("p",{children:["Authorized as ",c.jsxs("code",{children:["@",s.login]}),". Pick a repository to push the mirror to:"]}),c.jsx("div",{className:"form-row",children:c.jsx("input",{type:"search",placeholder:"Filter by name…",value:b,onChange:Y=>v(Y.target.value)})}),y?c.jsx("p",{className:"muted",style:{fontSize:"0.85em"},children:"Showing the first 300 repos. Use the filter above to narrow down — or paste the clone URL directly via Personal Access Token below if your repo isn't here."}):null,h===null?c.jsx("p",{className:"muted",children:"Loading repos…"}):null,h!==null?c.jsxs("div",{className:"repo-list",children:[X.length===0&&b.length>0?c.jsxs("p",{className:"dim",children:['No repos match "',b,'".']}):null,X.length===0&&b.length===0&&h.length===0?c.jsx("p",{className:"dim",children:"You don't own any repos on this account yet."}):null,X.map(Y=>c.jsxs("button",{type:"button",className:"repo-row",onClick:()=>J(Y.owner,Y.name),disabled:_!==null,children:[c.jsxs("span",{children:[c.jsx("strong",{children:Y.name}),Y.private?c.jsx("span",{className:"dim",children:" · private"}):null]}),c.jsx("span",{className:"dim",children:Y.updated_at.slice(0,10)})]},Y.full_name))]}):null,q?c.jsxs("div",{className:"form-row",style:{marginTop:"0.75rem"},children:[c.jsx("label",{htmlFor:"new-repo-name",children:"New repo name"}),c.jsx("input",{id:"new-repo-name",type:"text",value:D,placeholder:"my-vault-backup",onChange:Y=>B(Y.target.value)}),c.jsxs("div",{className:"actions",children:[c.jsx("button",{type:"button",onClick:V,disabled:R||D.trim().length===0,children:R?"Creating…":"Create"}),c.jsx("button",{type:"button",className:"secondary",onClick:()=>L(!1),disabled:R,children:"Cancel"})]})]}):c.jsx("div",{className:"actions",style:{marginTop:"0.75rem"},children:c.jsx("button",{type:"button",className:"secondary",onClick:()=>L(!0),children:"+ Create new private repo"})})]})}function r0({vaultName:i,onClose:s,onSaved:f}){const[r,h]=S.useState(""),[m,y]=S.useState(""),[E,b]=S.useState(""),[v,_]=S.useState(!1),[g,R]=S.useState(null),k=async D=>{D.preventDefault(),_(!0),R(null);try{const B=await Zg(i,{token:r.trim(),remote_url:m.trim(),label:E.trim()||void 0});f(B)}catch(B){R(B instanceof Error?B.message:String(B))}finally{_(!1)}};return c.jsx("div",{className:"modal-backdrop",role:"dialog","aria-modal":"true",children:c.jsxs("div",{className:"modal",children:[c.jsxs("div",{className:"list-header",children:[c.jsx("h3",{style:{margin:0},children:"Use Personal Access Token"}),c.jsx("button",{type:"button",className:"secondary",onClick:s,disabled:v,children:"Close"})]}),c.jsxs("p",{className:"dim",children:["Works for any provider that supports HTTPS push with a token in the URL (GitHub, GitLab, Codeberg, Gitea, …). The token is stored with"," ",c.jsx("code",{children:"0600"})," file perms on this server."]}),c.jsxs("form",{onSubmit:k,children:[c.jsxs("div",{className:"form-row",children:[c.jsx("label",{htmlFor:"pat-token",children:"Token"}),c.jsx("input",{id:"pat-token",type:"password",value:r,autoComplete:"off",onChange:D=>h(D.target.value),placeholder:"ghp_… or glpat-… or similar",required:!0})]}),c.jsxs("div",{className:"form-row",children:[c.jsx("label",{htmlFor:"pat-url",children:"Remote URL"}),c.jsx("input",{id:"pat-url",type:"url",value:m,onChange:D=>y(D.target.value),placeholder:"https://github.com/owner/repo.git",required:!0}),c.jsxs("p",{className:"dim",style:{margin:"0.35rem 0 0"},children:["We'll embed your token in the URL before saving (using GitHub's"," ",c.jsx("code",{children:"x-access-token"})," convention). The URL you see on disk will carry the token; ensure the file isn't shared."]})]}),c.jsxs("div",{className:"form-row",children:[c.jsx("label",{htmlFor:"pat-label",children:"Label (optional)"}),c.jsx("input",{id:"pat-label",type:"text",value:E,onChange:D=>b(D.target.value),placeholder:"GitHub PAT for backup"})]}),g?c.jsx("div",{className:"error-banner",role:"alert",children:c.jsx("code",{children:g})}):null,c.jsxs("div",{className:"actions",children:[c.jsx("button",{type:"submit",disabled:v,children:v?"Validating…":"Validate & save"}),c.jsx("button",{type:"button",className:"secondary",onClick:s,disabled:v,children:"Cancel"})]})]})]})})}function o0({vaultName:i,creds:s}){const[f,r]=S.useState(""),[h,m]=S.useState("merge"),[y,E]=S.useState(!1),[b,v]=S.useState(""),[_,g]=S.useState(!0),[R,k]=S.useState({kind:"idle"}),[D,B]=S.useState(""),[q,L]=S.useState(!1),X=(s==null?void 0:s.active_method)==="github_oauth"&&s.github_oauth!==null,J=(s==null?void 0:s.active_method)!==null&&(s==null?void 0:s.active_method)!==void 0,V=h!=="replace"||D.trim()===i,Y=R.kind!=="running"&&f.trim().length>0&&V&&(!y||b.trim().length>0),K=async()=>{k({kind:"running",stage:"cloning"});let te;y?te={kind:"pat",token:b.trim()}:J?te=null:te={kind:"none"};try{const ye=window.setTimeout(()=>k(We=>We.kind==="running"?{kind:"running",stage:"importing"}:We),1500),tt=await $g(i,{remote_url:f.trim(),mode:h,credentials:te,enable_sync:_});window.clearTimeout(ye),k({kind:"success",result:tt,remoteUrl:f.trim(),mode:h})}catch(ye){const tt=ye instanceof _e?`${ye.status===409?"Already running. ":""}${ye.message}`:ye instanceof Error?ye.message:String(ye);k({kind:"error",message:tt})}},G=()=>{k({kind:"idle"}),B("")};return c.jsxs("div",{className:"section",children:[c.jsx("h3",{style:{margin:"0 0 0.85rem",fontSize:"1rem",fontWeight:500},children:"Import from a git repo"}),c.jsxs("p",{className:"dim",style:{marginTop:0},children:["Pull a vault state from a remote git repo into ",c.jsx("strong",{children:"this"})," vault. Use this to load a vault someone has been mirroring, or to sync a vault between machines you control. The remote must be a Parachute vault export (created by ",c.jsx("code",{children:"parachute-vault export"})," or the mirror's export flow)."]}),c.jsxs("div",{className:"warn-banner",style:{marginBottom:"1rem"},role:"note",children:[c.jsx("strong",{children:"One vault per remote."})," Multiple vaults pushing to the same git repo isn't a supported shape — the last push wins, and vaults that diverge silently overwrite each other. Today's working pattern is one vault per remote. Active two-way sync is a future direction; for now, do exports from one place and imports as snapshots elsewhere."]}),R.kind==="success"?c.jsx(f0,{vaultName:i,result:R.result,remoteUrl:R.remoteUrl,mode:R.mode,onReset:G}):c.jsxs(c.Fragment,{children:[c.jsxs("div",{className:"form-row",children:[c.jsx("label",{htmlFor:"import-remote-url",children:"Remote URL"}),c.jsx("input",{id:"import-remote-url",type:"text",value:f,placeholder:"https://github.com/aaron/my-vault.git",onChange:te=>r(te.target.value),disabled:R.kind==="running"}),X?c.jsxs("p",{className:"dim",style:{margin:"0.35rem 0 0",fontSize:"0.9em"},children:["Or"," ",c.jsx("button",{type:"button",onClick:()=>L(!0),disabled:R.kind==="running",style:{background:"none",border:"none",padding:0,color:"var(--accent)",textDecoration:"underline",cursor:"pointer",fontSize:"inherit"},children:"pick from your GitHub repos"}),"."]}):null,!J&&!y?c.jsx("p",{className:"hint",style:{marginTop:"0.35rem",fontSize:"0.85em"},children:"No saved git credentials. The import will be unauthenticated — works for public repos; private repos will fail with a clear error. Use the one-time credential toggle below if needed."}):null]}),c.jsxs("div",{className:"form-row",children:[c.jsx("label",{children:"Mode"}),c.jsxs("label",{className:"radio-row",children:[c.jsx("input",{type:"radio",name:"import-mode",value:"merge",checked:h==="merge",onChange:()=>{m("merge"),B("")},disabled:R.kind==="running"}),c.jsxs("span",{children:[c.jsx("strong",{children:"Merge"})," ",c.jsxs("span",{className:"dim",children:["— upsert by id. Notes in the remote get created/updated; any notes that exist only locally ",c.jsx("strong",{children:"survive"}),"."]})]})]}),c.jsxs("label",{className:"radio-row",children:[c.jsx("input",{type:"radio",name:"import-mode",value:"replace",checked:h==="replace",onChange:()=>m("replace"),disabled:R.kind==="running"}),c.jsxs("span",{children:[c.jsx("strong",{children:"Replace"})," ",c.jsxs("span",{className:"dim",children:["— wipe this vault first, then import. The remote becomes the new source of truth. ",c.jsx("strong",{children:"Destructive."})]})]})]})]}),h==="replace"?c.jsxs("div",{className:"form-row",children:[c.jsxs("div",{className:"error-banner",role:"alert",style:{marginBottom:"0.5rem"},children:[c.jsxs("strong",{children:['Replace will delete every note in vault "',i,'" before importing.']})," To confirm, type the vault name below:"]}),c.jsx("input",{id:"import-confirm-name",type:"text",value:D,placeholder:i,onChange:te=>B(te.target.value),disabled:R.kind==="running","aria-label":"Type vault name to confirm"})]}):null,c.jsxs("div",{className:"form-row",children:[c.jsxs("label",{children:[c.jsx("input",{type:"checkbox",checked:y,onChange:te=>E(te.target.checked),disabled:R.kind==="running",style:{width:"auto",marginRight:"0.5rem"}}),"One-time credential for this import only"]}),c.jsx("p",{className:"dim",style:{margin:"0.35rem 0 0",fontSize:"0.85em"},children:"Use a different Personal Access Token just for this clone, without changing your saved credentials."}),y?c.jsx("input",{id:"import-per-call-pat",type:"password",value:b,placeholder:"ghp_… or similar",onChange:te=>v(te.target.value),disabled:R.kind==="running",autoComplete:"off",style:{marginTop:"0.5rem"}}):null]}),c.jsxs("div",{className:"form-row",children:[c.jsxs("label",{children:[c.jsx("input",{type:"checkbox",checked:_,onChange:te=>g(te.target.checked),disabled:R.kind==="running",style:{width:"auto",marginRight:"0.5rem"}}),"Also sync changes back to this repo"]}),c.jsx("p",{className:"dim",style:{margin:"0.35rem 0 0",fontSize:"0.85em"},children:"Pushes future changes to this repo automatically. Uses the access you provide above."})]}),R.kind==="error"?c.jsx("div",{className:"error-banner",role:"alert",children:c.jsx("code",{children:R.message})}):null,c.jsx("div",{className:"actions",children:c.jsx("button",{type:"button",onClick:K,disabled:!Y,title:V?f.trim().length===0?"Provide a remote URL.":y&&b.trim().length===0?"Provide a token for one-time credential.":void 0:"Type the vault name to confirm Replace.",children:R.kind==="running"?R.stage==="cloning"?"Cloning…":"Importing…":"Start import"})})]}),q?c.jsx(d0,{vaultName:i,onClose:()=>L(!1),onPicked:te=>{r(te),L(!1)}}):null]})}function f0({vaultName:i,result:s,remoteUrl:f,mode:r,onReset:h}){return c.jsxs(c.Fragment,{children:[c.jsxs("div",{className:"mint-banner",role:"status",style:{marginBottom:"0.75rem"},children:[c.jsx("strong",{children:"Import succeeded."})," Imported ",s.notes_imported," note",s.notes_imported===1?"":"s",", ",s.tags_imported," tag schema",s.tags_imported===1?"":"s",", ",s.attachments_imported," ","attachment",s.attachments_imported===1?"":"s",r==="replace"&&s.notes_deleted!==void 0?`, wiped ${s.notes_deleted} pre-existing note${s.notes_deleted===1?"":"s"}`:"","."]}),s.warnings.length>0?c.jsxs("details",{style:{marginBottom:"0.75rem"},children:[c.jsxs("summary",{children:[s.warnings.length," warning",s.warnings.length===1?"":"s"," ","(see details)"]}),c.jsx("ul",{style:{marginTop:"0.5rem"},children:s.warnings.map((m,y)=>c.jsx("li",{children:c.jsx("code",{style:{fontSize:"0.85em"},children:m})},y))})]}):null,s.sync_enabled?c.jsxs("div",{className:"mint-banner",style:{marginBottom:"0.75rem"},role:"status",children:[c.jsx("strong",{children:"Sync enabled."})," Changes to vault ",c.jsx("code",{children:i})," ","now push back to ",c.jsx("code",{children:f})," automatically."]}):s.sync_warning?c.jsxs("div",{className:"info-banner",style:{marginBottom:"0.75rem"},role:"status",children:[c.jsxs("p",{style:{marginTop:0},children:[c.jsx("strong",{children:"Sync not enabled."})," ",s.sync_warning]}),c.jsxs("p",{className:"dim",style:{marginBottom:0,fontSize:"0.9em"},children:["You can still set up Sync from the"," ",c.jsx("button",{type:"button",onClick:()=>{var m;return(m=document.getElementById("git-remote-section"))==null?void 0:m.scrollIntoView({behavior:"smooth",block:"start"})},style:{background:"none",border:"none",padding:0,color:"var(--accent)",textDecoration:"underline",cursor:"pointer",fontSize:"inherit"},children:"Git remote section"})," ","above."]})]}):null,c.jsx("div",{className:"actions",children:c.jsx("button",{type:"button",className:"secondary",onClick:h,children:"Run another import"})})]})}function d0({vaultName:i,onClose:s,onPicked:f}){const[r,h]=S.useState(null),[m,y]=S.useState(!1),[E,b]=S.useState(""),[v,_]=S.useState(null);S.useEffect(()=>{let R=!1;return hm(i).then(({repos:k,truncated:D})=>{R||(h(k),y(!!D))}).catch(k=>{R||_(k instanceof Error?k.message:String(k))}),()=>{R=!0}},[i]);const g=(r??[]).filter(R=>E.length===0?!0:R.full_name.toLowerCase().includes(E.toLowerCase()));return c.jsx("div",{className:"modal-backdrop",role:"dialog","aria-modal":"true",children:c.jsxs("div",{className:"modal",children:[c.jsxs("div",{className:"list-header",children:[c.jsx("h3",{style:{margin:0},children:"Pick a repo to import from"}),c.jsx("button",{type:"button",className:"secondary",onClick:s,children:"Close"})]}),v?c.jsx("div",{className:"error-banner",role:"alert",children:c.jsx("code",{children:v})}):null,c.jsx("div",{className:"form-row",children:c.jsx("input",{type:"search",placeholder:"Filter by name…",value:E,onChange:R=>b(R.target.value)})}),m?c.jsx("p",{className:"muted",style:{fontSize:"0.85em"},children:"Showing the first 300 repos. Use the filter above to narrow down."}):null,r===null&&!v?c.jsx("p",{className:"muted",children:"Loading repos…"}):null,r!==null?c.jsxs("div",{className:"repo-list",children:[g.length===0&&E.length>0?c.jsxs("p",{className:"dim",children:['No repos match "',E,'".']}):null,g.length===0&&E.length===0&&r.length===0?c.jsx("p",{className:"dim",children:"You don't own any repos on this account."}):null,g.map(R=>c.jsxs("button",{type:"button",className:"repo-row",onClick:()=>f(R.clone_url),children:[c.jsxs("span",{children:[c.jsx("strong",{children:R.name}),R.private?c.jsx("span",{className:"dim",children:" · private"}):null]}),c.jsx("span",{className:"dim",children:R.updated_at.slice(0,10)})]},R.full_name))]}):null]})})}let Gn=null,Xn=null;function h0(i){const s=Date.parse(i);return Number.isNaN(s)?null:s}async function m0(){if(typeof window>"u")return{kind:"network-error",message:"no window"};let i;try{i=await fetch("/admin/host-admin-token",{method:"GET",headers:{accept:"application/json"},credentials:"same-origin"})}catch(f){return{kind:"network-error",message:f instanceof Error?f.message:String(f)}}if(i.status===403)return{kind:"forbidden"};if(i.status===401||i.status===404)return{kind:"auth-required",status:i.status};if(!i.ok)return{kind:"network-error",message:`hub returned ${i.status}`};let s;try{s=await i.json()}catch(f){return{kind:"network-error",message:f instanceof Error?f.message:"could not parse host-admin mint response"}}return typeof s.token!="string"||s.token.length===0?{kind:"network-error",message:"host-admin mint response missing token"}:(Gn=s.token,Xn=s.expires_at?h0(s.expires_at):null,{kind:"ok",token:s.token})}async function Lh(){if(Gn){if(Xn===null||Xn>Date.now())return{kind:"ok",token:Gn};Gn=null,Xn=null}return m0()}function v0(){Gn=null,Xn=null}class Ga extends Error{constructor(s,f,r){super(f),this.status=s,this.disposition=r,this.name="HostAdminError"}}async function fr(i,s={}){let f;const r=await Lh();if(r.kind!=="ok")throw kh(r);f=r.token;const{headers:h,...m}=s,y=v=>({accept:"application/json",...h??{},authorization:`Bearer ${v}`}),E=await fetch(i,{...m,headers:y(f)});if(E.status!==401)return E;v0();const b=await Lh();if(b.kind!=="ok")throw kh(b);return fetch(i,{...m,headers:y(b.token)})}function kh(i){return i.kind==="forbidden"?new Ga(403,"host-admin token mint forbidden — not the hub admin","forbidden"):i.kind==="auth-required"?new Ga(i.status,"no host-admin session — sign in to the hub to manage tokens","auth-required"):new Ga(0,i.message,"network-error")}function dr(){if(typeof window>"u")return;const i=ym();window.location.href=i?`${i}/account/`:"/account/"}const y0=[30,90,180,365],qh=90,p0=1440*60;function g0(i){if(!i)return null;const s=i.scoped_tags;if(!Array.isArray(s))return null;const f=s.filter(r=>typeof r=="string"&&r.length>0);return f.length>0?f:null}function b0(i){return{jti:i.jti,label:i.subject,scopes:Array.isArray(i.scopes)?i.scopes:[],scoped_tags:g0(i.permissions),expires_at:i.expires_at??null,revoked_at:i.revoked_at??null,created_at:i.created_at}}async function S0(i,s){const f={scope:`vault:${i}:${s.verb}`,expires_in:s.ttlDays*p0,subject:s.label};s.scopedTags.length>0&&(f.permissions={scoped_tags:s.scopedTags});const r=await fr("/api/auth/mint-token",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(f)});if(!r.ok)throw new _e(r.status,await xi(r));const h=await r.json();return{jti:h.jti,token:h.token,scope:h.scope,expires_at:h.expires_at??null}}async function x0(i){const s=`vault:${i}:`,f=[];let r=null;for(let h=0;h<1e3;h++){const m=new URLSearchParams({revoked:"all"});r&&m.set("cursor",r);const y=await fr(`/api/auth/tokens?${m.toString()}`);if(!y.ok)throw new _e(y.status,await xi(y));const E=await y.json(),b=Array.isArray(E.tokens)?E.tokens:[];if(f.push(...b),r=E.next_cursor??null,!r)break}return f.filter(h=>Array.isArray(h.scopes)&&h.scopes.some(m=>m.startsWith(s))).map(b0)}async function j0(i){const s=await fr("/api/auth/revoke-token",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({jti:i})});if(!s.ok)throw new _e(s.status,await xi(s))}async function E0(i){const s=await et(i,`/vault/${encodeURIComponent(i)}/api/tags`);if(!s.ok)throw new _e(s.status,await xi(s));const f=await s.json();return Array.isArray(f)?f:[]}async function xi(i){try{const s=await i.text(),f=JSON.parse(s);if(f.error_description)return f.error_description;if(f.message)return f.message;if(f.error)return f.error;if(s)return s}catch{}return`${i.status} ${i.statusText}`}const T0=["read","write","admin"],_0={30:"30 days",90:"90 days",180:"180 days",365:"1 year (max)"};function A0(i){return i instanceof Ga?i.disposition==="forbidden"?(dr(),{kind:"auth-required",status:403}):i.disposition==="auth-required"?{kind:"auth-required",status:i.status}:{kind:"error",message:i.message}:i instanceof _e&&(i.status===401||i.status===403)?{kind:"auth-required",status:i.status}:{kind:"error",message:i instanceof Error?i.message:String(i)}}function Yh({vaultName:i}={}){const s=lr(),f=i??s.name,r=i!==void 0,h=r?"/":`/vault/${encodeURIComponent(f??"")}`,[m,y]=S.useState({kind:"loading"}),[E,b]=S.useState(null),[v,_]=S.useState(null),[g,R]=S.useState(0);S.useEffect(()=>{let B=!1;if(f)return y({kind:"loading"}),x0(f).then(q=>{B||y({kind:"ok",tokens:q})}).catch(q=>{B||y(A0(q))}),()=>{B=!0}},[f,g]);const k=S.useCallback(()=>R(B=>B+1),[]);if(S.useEffect(()=>{if(!E)return;const B=q=>{q.preventDefault(),q.returnValue=""};return window.addEventListener("beforeunload",B),()=>window.removeEventListener("beforeunload",B)},[E]),!f)return c.jsxs("div",{children:[c.jsx("h2",{children:"Tokens"}),c.jsx("p",{className:"muted",children:"Missing vault name."}),r?null:c.jsx(Je,{to:"/",children:"← Back to vaults"})]});const D=vm(f);return c.jsxs("div",{children:[c.jsxs("div",{className:"list-header",children:[c.jsxs("h2",{children:["Tokens for ",c.jsx("code",{children:f})]}),c.jsx(Je,{to:h,className:"muted",children:"← Vault detail"})]}),D?null:c.jsxs("div",{className:"warn-banner",children:["You're viewing this page with a read-only token. Mint and revoke require"," ",c.jsxs("code",{children:["vault:",f,":admin"]}),`. Re-enter from the hub directory's "Manage" link with an admin-scoped session to manage tokens.`]}),E?c.jsx(N0,{result:E,onDismiss:()=>b(null)}):null,m.kind==="ok"&&D&&!E?c.jsx(R0,{vaultName:f,onMinted:B=>{b(B),R(q=>q+1)}}):null,c.jsxs("div",{className:"section",children:[c.jsx("h3",{style:{margin:"0 0 0.85rem",fontSize:"1rem",fontWeight:500},children:"Existing tokens"}),m.kind==="loading"?c.jsx("p",{className:"muted",children:"Loading…"}):null,m.kind==="auth-required"?c.jsx(or,{vaultName:f,status:m.status,onRecovered:k}):null,m.kind==="error"?c.jsx("div",{className:"error-banner",children:c.jsx("code",{children:m.message})}):null,m.kind==="ok"?c.jsx(z0,{tokens:m.tokens,allowRevoke:D,confirmingJti:v,onAskConfirm:_,onRevoked:()=>{_(null),R(B=>B+1)}}):null]})]})}function N0({result:i,onDismiss:s}){const[f,r]=S.useState(!1),h=async()=>{try{await navigator.clipboard.writeText(i.token),r(!0),setTimeout(()=>r(!1),2e3)}catch{}};return c.jsxs("div",{className:"mint-banner",children:[c.jsx("h3",{children:"New token (shown once)"}),c.jsx("p",{className:"muted",children:"This is the only time the vault will show this token. Copy it now and store it somewhere safe — a password manager, the operator's notes, paraclaw's secrets store. If you lose it, revoke it and mint a new one."}),c.jsxs("div",{className:"token-box",children:[c.jsx("code",{children:i.token}),c.jsx("button",{type:"button",onClick:h,className:"secondary",children:f?"Copied":"Copy"})]}),c.jsx("p",{className:"warn",children:"⚠ Don't dismiss this banner until you've saved the token."}),c.jsx("div",{className:"actions",children:c.jsx("button",{type:"button",onClick:s,children:"Done — I've saved the token"})})]})}function R0({vaultName:i,onMinted:s}){const[f,r]=S.useState(""),[h,m]=S.useState("admin"),[y,E]=S.useState(qh),[b,v]=S.useState(!1),[_,g]=S.useState(null),[R,k]=S.useState([]),[D,B]=S.useState(null),[q,L]=S.useState(new Set);S.useEffect(()=>{let K=!1;return E0(i).then(G=>{if(K)return;const te=G.map(ye=>ye.name).filter(ye=>!ye.includes("/")).sort();k(te)}).catch(G=>{K||B(G instanceof Error?G.message:String(G))}),()=>{K=!0}},[i]);const X=f.trim(),J=X.length===0,V=K=>{L(G=>{const te=new Set(G);return te.has(K)?te.delete(K):te.add(K),te})},Y=async K=>{if(K.preventDefault(),J){g("label required — give the token a recognizable name");return}v(!0),g(null);try{const G=await S0(i,{verb:h,label:X,scopedTags:[...q],ttlDays:y});s(G),r(""),m("admin"),E(qh),L(new Set)}catch(G){if(G instanceof Ga&&G.disposition==="forbidden"){dr();return}g(G instanceof Error?G.message:String(G))}finally{v(!1)}};return c.jsxs("form",{onSubmit:Y,className:"section",children:[c.jsx("h3",{style:{margin:"0 0 0.85rem",fontSize:"1rem",fontWeight:500},children:"Mint a token"}),c.jsx("p",{className:"dim",style:{margin:"0 0 0.85rem"},children:"Issues a hub-signed token (JWT) scoped to this vault. Store it like a password — it's shown once and can't be re-displayed."}),_?c.jsx("div",{className:"error-banner",children:c.jsx("code",{children:_})}):null,c.jsxs("div",{className:"form-row",children:[c.jsxs("label",{htmlFor:"mint-label",children:["Label ",c.jsx("span",{className:"dim",children:"(required)"})]}),c.jsx("input",{id:"mint-label",type:"text",value:f,onChange:K=>r(K.target.value),placeholder:"e.g. ci, paraclaw-prod, my-laptop",maxLength:120,required:!0})]}),c.jsxs("div",{className:"form-row",children:[c.jsx("label",{htmlFor:"mint-scope",children:"Scope"}),c.jsx("select",{id:"mint-scope",value:h,onChange:K=>m(K.target.value),children:T0.map(K=>c.jsx("option",{value:K,children:K==="read"?"read — query notes only":K==="write"?"write — query + create/update notes":"admin — full control (incl. token mgmt)"},K))}),c.jsxs("p",{className:"dim",style:{margin:"0.35rem 0 0"},children:["Issued as ",c.jsxs("code",{children:["vault:",i,":",h]}),". Lower scopes inherit narrower powers."]})]}),c.jsxs("div",{className:"form-row",children:[c.jsx("label",{htmlFor:"mint-ttl",children:"Expires after"}),c.jsx("select",{id:"mint-ttl",value:y,onChange:K=>E(Number(K.target.value)),children:y0.map(K=>c.jsx("option",{value:K,children:_0[K]??`${K} days`},K))}),c.jsx("p",{className:"dim",style:{margin:"0.35rem 0 0"},children:"The token stops working after this. Rotate before it lapses."})]}),c.jsxs("div",{className:"form-row",children:[c.jsxs("label",{children:["Tag scope ",c.jsx("span",{className:"dim",children:"(optional)"})]}),D?c.jsxs("p",{className:"dim",style:{margin:"0.35rem 0 0"},children:["Couldn't load tags: ",c.jsx("code",{children:D}),". Token will be unscoped."]}):R.length===0?c.jsx("p",{className:"dim",style:{margin:"0.35rem 0 0"},children:"No root tags in this vault yet — token will see the full vault."}):c.jsxs(c.Fragment,{children:[c.jsx("div",{className:"tag-picker",children:R.map(K=>c.jsxs("label",{className:"tag-checkbox",children:[c.jsx("input",{type:"checkbox",checked:q.has(K),onChange:()=>V(K)}),c.jsx("code",{children:K})]},K))}),c.jsx("p",{className:"dim",style:{margin:"0.35rem 0 0"},children:q.size===0?"Nothing selected — token sees the full vault (unscoped).":`Token limited to: ${[...q].join(", ")}. Sub-tags (e.g. health/food) are reachable when their root is selected.`})]})]}),c.jsx("div",{className:"actions",children:c.jsx("button",{type:"submit",disabled:b||J,children:b?"Minting…":"Mint token"})})]})}function z0({tokens:i,allowRevoke:s,confirmingJti:f,onAskConfirm:r,onRevoked:h}){return i.length===0?c.jsx("p",{className:"muted",children:"No tokens yet. Mint one above to get started."}):c.jsx("div",{className:"token-list",children:i.map(m=>c.jsx(C0,{token:m,allowRevoke:s,confirming:f===m.jti,onAskConfirm:r,onRevoked:h},m.jti))})}function C0({token:i,allowRevoke:s,confirming:f,onAskConfirm:r,onRevoked:h}){const[m,y]=S.useState(!1),[E,b]=S.useState(null),v=i.revoked_at!==null,_=async()=>{y(!0),b(null);try{await j0(i.jti),h()}catch(g){if(g instanceof Ga&&g.disposition==="forbidden"){dr();return}b(g instanceof Error?g.message:String(g)),y(!1)}};return c.jsxs("div",{className:"token-row",children:[c.jsxs("div",{className:"body",children:[c.jsxs("div",{className:"name",children:[c.jsx("strong",{children:i.label??"(unlabeled)"}),c.jsx("code",{className:"dim",children:i.jti}),v?c.jsx("code",{className:"scope-tag",title:"This token has been revoked and no longer works.",children:"revoked"}):null]}),c.jsxs("div",{className:"meta",children:[c.jsx("span",{className:"dim",children:"scopes:"})," ",i.scopes.length>0?i.scopes.map(g=>c.jsx("code",{className:"scope-tag",children:g},g)):c.jsx("span",{className:"dim",children:"(none)"})]}),i.scoped_tags&&i.scoped_tags.length>0?c.jsxs("div",{className:"meta",children:[c.jsx("span",{className:"dim",children:"tags:"})," ",i.scoped_tags.map(g=>c.jsxs("code",{className:"scope-tag tag-pill",children:["#",g]},g))]}):null,c.jsxs("div",{className:"meta dim",children:["created ",i.created_at,i.expires_at?` · expires ${i.expires_at}`:"",v&&i.revoked_at?` · revoked ${i.revoked_at}`:""]}),E?c.jsx("div",{className:"error-banner",style:{marginTop:"0.5rem"},children:c.jsx("code",{children:E})}):null]}),s&&!v?f?c.jsxs("div",{className:"actions",children:[c.jsx("button",{type:"button",onClick:_,disabled:m,children:m?"Revoking…":"Confirm revoke"}),c.jsx("button",{type:"button",className:"secondary",onClick:()=>r(null),disabled:m,children:"Cancel"})]}):c.jsx("button",{type:"button",className:"secondary",onClick:()=>r(i.jti),children:"Revoke"}):null]})}function w0(){const[i,s]=S.useState({kind:"loading"});return S.useEffect(()=>{let f=!1;return Bg().then(r=>{f||s({kind:"ok",vaults:r})}).catch(r=>{if(f)return;const h=r instanceof _e?`${r.status}: ${r.message}`:r instanceof Error?r.message:String(r);s({kind:"error",message:h})}),()=>{f=!0}},[]),i.kind==="loading"?c.jsxs("div",{children:[c.jsx("h2",{children:"Vaults"}),c.jsx("p",{className:"muted",children:"Loading…"})]}):i.kind==="error"?c.jsxs("div",{children:[c.jsx("h2",{children:"Vaults"}),c.jsx("div",{className:"error-banner",children:c.jsx("code",{children:i.message})})]}):i.vaults.length===0?c.jsxs("div",{children:[c.jsx("h2",{children:"Vaults"}),c.jsxs("div",{className:"empty",children:["No vaults yet. Run ",c.jsx("code",{children:"parachute-vault create <name>"})," on the host to create one."]})]}):c.jsxs("div",{children:[c.jsx("div",{className:"list-header",children:c.jsx("h2",{children:"Vaults"})}),i.vaults.map(f=>c.jsxs(Je,{to:`/vault/${f}`,className:"vault-row",children:[c.jsx("div",{className:"body",children:c.jsx("div",{className:"name",children:c.jsx("code",{children:f})})}),c.jsx("div",{className:"chev",children:"→"})]},f))]})}function M0(){const i=om();return c.jsxs("div",{className:"page",children:[c.jsxs("nav",{className:"nav",children:[c.jsxs(Je,{to:"/",className:"brand",children:["Parachute Vault ",c.jsx("span",{className:"sub",children:"admin"})]}),i?c.jsx(Je,{to:"/",children:c.jsx("code",{children:i})}):c.jsx(Je,{to:"/",children:"Vaults"})]}),i?c.jsxs(Mh,{children:[c.jsx(Yt,{path:"/",element:c.jsx(Hh,{vaultName:i})}),c.jsx(Yt,{path:"/tokens",element:c.jsx(Yh,{vaultName:i})}),c.jsx(Yt,{path:"/mirror",element:c.jsx(Bh,{vaultName:i})}),c.jsx(Yt,{path:"*",element:c.jsx(Gh,{})})]}):c.jsxs(Mh,{children:[c.jsx(Yt,{path:"/",element:c.jsx(w0,{})}),c.jsx(Yt,{path:"/vault/:name",element:c.jsx(Hh,{})}),c.jsx(Yt,{path:"/vault/:name/tokens",element:c.jsx(Yh,{})}),c.jsx(Yt,{path:"/vault/:name/mirror",element:c.jsx(Bh,{})}),c.jsx(Yt,{path:"*",element:c.jsx(Gh,{})})]})]})}function Gh(){return c.jsxs("div",{className:"empty",children:["404 — back to ",c.jsx(Je,{to:"/",children:"vault"}),"."]})}wg();const pm=document.getElementById("root");if(!pm)throw new Error("#root not found");function $s(){Fy.createRoot(pm).render(c.jsx(S.StrictMode,{children:c.jsx(pg,{basename:Ng(),children:c.jsx(M0,{})})}))}const Xh=om();Xh?Dg(Xh).then($s,$s):$s();
60
+ Please change the parent <Route path="${L}"> to <Route path="${L==="/"?"*":`${L}/*`}">.`)}let _=fl(),g;if(s){let L=typeof s=="string"?Xa(s):s;ze(b==="/"||((q=L.pathname)==null?void 0:q.startsWith(b)),`When overriding the location using \`<Routes location>\` or \`useRoutes(routes, location)\`, the location pathname must begin with the portion of the URL pathname that was matched by all parent routes. The current pathname base is "${b}" but pathname "${L.pathname}" was given in the \`location\` prop.`),g=L}else g=_;let R=g.pathname||"/",k=R;if(b!=="/"){let L=b.replace(/^\//,"").split("/");k="/"+R.replace(/^\//,"").split("/").slice(L.length).join("/")}let D=Qh(i,{pathname:k});Xt(v||D!=null,`No routes matched location "${g.pathname}${g.search}${g.hash}" `),Xt(D==null||D[D.length-1].route.element!==void 0||D[D.length-1].route.Component!==void 0||D[D.length-1].route.lazy!==void 0,`Matched leaf route at location "${g.pathname}${g.search}${g.hash}" does not have an element or Component. This means it will render an <Outlet /> with a null value by default resulting in an "empty" page.`);let B=kp(D&&D.map(L=>Object.assign({},L,{params:Object.assign({},y,L.params),pathname:Ut([b,r.encodeLocation?r.encodeLocation(L.pathname.replace(/%/g,"%25").replace(/\?/g,"%3F").replace(/#/g,"%23")).pathname:L.pathname]),pathnameBase:L.pathnameBase==="/"?b:Ut([b,r.encodeLocation?r.encodeLocation(L.pathnameBase.replace(/%/g,"%25").replace(/\?/g,"%3F").replace(/#/g,"%23")).pathname:L.pathnameBase])})),h,f);return s&&B?S.createElement(Zn.Provider,{value:{location:{pathname:"/",search:"",hash:"",state:null,key:"default",unstable_mask:void 0,...g},navigationType:"POP"}},B):B}function Up(){let i=Qp(),s=xp(i)?`${i.status} ${i.statusText}`:i instanceof Error?i.message:JSON.stringify(i),f=i instanceof Error?i.stack:null,r="rgba(200,200,200, 0.5)",h={padding:"0.5rem",backgroundColor:r},m={padding:"2px 4px",backgroundColor:r},y=null;return console.error("Error handled by React Router default ErrorBoundary:",i),y=S.createElement(S.Fragment,null,S.createElement("p",null,"💿 Hey developer 👋"),S.createElement("p",null,"You can provide a way better UX than this when your app throws errors by providing your own ",S.createElement("code",{style:m},"ErrorBoundary")," or"," ",S.createElement("code",{style:m},"errorElement")," prop on your route.")),S.createElement(S.Fragment,null,S.createElement("h2",null,"Unexpected Application Error!"),S.createElement("h3",{style:{fontStyle:"italic"}},s),f?S.createElement("pre",{style:h},f):null,y)}var Hp=S.createElement(Up,null),nm=class extends S.Component{constructor(i){super(i),this.state={location:i.location,revalidation:i.revalidation,error:i.error}}static getDerivedStateFromError(i){return{error:i}}static getDerivedStateFromProps(i,s){return s.location!==i.location||s.revalidation!=="idle"&&i.revalidation==="idle"?{error:i.error,location:i.location,revalidation:i.revalidation}:{error:i.error!==void 0?i.error:s.error,location:s.location,revalidation:i.revalidation||s.revalidation}}componentDidCatch(i,s){this.props.onError?this.props.onError(i,s):console.error("React Router caught the following error during render",i)}render(){let i=this.state.error;if(this.context&&typeof i=="object"&&i&&"digest"in i&&typeof i.digest=="string"){const f=Cp(i.digest);f&&(i=f)}let s=i!==void 0?S.createElement(Qt.Provider,{value:this.props.routeContext},S.createElement(tr.Provider,{value:i,children:this.props.component})):this.props.children;return this.context?S.createElement(Bp,{error:i},s):s}};nm.contextType=Ih;var Ks=new WeakMap;function Bp({children:i,error:s}){let{basename:f}=S.useContext(wt);if(typeof s=="object"&&s&&"digest"in s&&typeof s.digest=="string"){let r=zp(s.digest);if(r){let h=Ks.get(s);if(h)throw h;let m=Fh(r.location,f);if($h&&!Ks.get(s))if(m.isExternal||r.reloadDocument)window.location.href=m.absoluteURL||m.to;else{const y=Promise.resolve().then(()=>window.__reactRouterDataRouter.navigate(m.to,{replace:r.replace}));throw Ks.set(s,y),y}return S.createElement("meta",{httpEquiv:"refresh",content:`0;url=${m.absoluteURL||m.to}`})}}return i}function Lp({routeContext:i,match:s,children:f}){let r=S.useContext(Qa);return r&&r.static&&r.staticContext&&(s.route.errorElement||s.route.ErrorBoundary)&&(r.staticContext._deepestRenderedBoundaryId=s.route.id),S.createElement(Qt.Provider,{value:i},f)}function kp(i,s=[],f){let r=f==null?void 0:f.state;if(i==null){if(!r)return null;if(r.errors)i=r.matches;else if(s.length===0&&!r.initialized&&r.matches.length>0)i=r.matches;else return null}let h=i,m=r==null?void 0:r.errors;if(m!=null){let _=h.findIndex(g=>g.route.id&&(m==null?void 0:m[g.route.id])!==void 0);ze(_>=0,`Could not find a matching route for errors on route IDs: ${Object.keys(m).join(",")}`),h=h.slice(0,Math.min(h.length,_+1))}let y=!1,E=-1;if(f&&r){y=r.renderFallback;for(let _=0;_<h.length;_++){let g=h[_];if((g.route.HydrateFallback||g.route.hydrateFallbackElement)&&(E=_),g.route.id){let{loaderData:R,errors:k}=r,D=g.route.loader&&!R.hasOwnProperty(g.route.id)&&(!k||k[g.route.id]===void 0);if(g.route.lazy||D){f.isStatic&&(y=!0),E>=0?h=h.slice(0,E+1):h=[h[0]];break}}}}let b=f==null?void 0:f.onError,v=r&&b?(_,g)=>{var R,k;b(_,{location:r.location,params:((k=(R=r.matches)==null?void 0:R[0])==null?void 0:k.params)??{},unstable_pattern:jp(r.matches),errorInfo:g})}:void 0;return h.reduceRight((_,g,R)=>{let k,D=!1,B=null,q=null;r&&(k=m&&g.route.id?m[g.route.id]:void 0,B=g.route.errorElement||Hp,y&&(E<0&&R===0?(um("route-fallback",!1,"No `HydrateFallback` element provided to render during initial hydration"),D=!0,q=null):E===R&&(D=!0,q=g.route.hydrateFallbackElement||null)));let L=s.concat(h.slice(0,R+1)),X=()=>{let J;return k?J=B:D?J=q:g.route.Component?J=S.createElement(g.route.Component,null):g.route.element?J=g.route.element:J=_,S.createElement(Lp,{match:g,routeContext:{outlet:_,matches:L,isDataRoute:r!=null},children:J})};return r&&(g.route.ErrorBoundary||g.route.errorElement||R===0)?S.createElement(nm,{location:r.location,revalidation:r.revalidation,component:B,error:k,children:X(),routeContext:{outlet:null,matches:L,isDataRoute:!0},onError:v}):X()},null)}function ar(i){return`${i} must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.`}function qp(i){let s=S.useContext(Qa);return ze(s,ar(i)),s}function Yp(i){let s=S.useContext(gi);return ze(s,ar(i)),s}function Gp(i){let s=S.useContext(Qt);return ze(s,ar(i)),s}function nr(i){let s=Gp(i),f=s.matches[s.matches.length-1];return ze(f.route.id,`${i} can only be used on routes that contain a unique "id"`),f.route.id}function Xp(){return nr("useRouteId")}function Qp(){var r;let i=S.useContext(tr),s=Yp("useRouteError"),f=nr("useRouteError");return i!==void 0?i:(r=s.errors)==null?void 0:r[f]}function Vp(){let{router:i}=qp("useNavigate"),s=nr("useNavigate"),f=S.useRef(!1);return lm(()=>{f.current=!0}),S.useCallback(async(h,m={})=>{Xt(f.current,tm),f.current&&(typeof h=="number"?await i.navigate(h):await i.navigate(h,{fromRouteId:s,...m}))},[i,s])}var wh={};function um(i,s,f){!s&&!wh[i]&&(wh[i]=!0,Xt(!1,f))}S.memo(Zp);function Zp({routes:i,future:s,state:f,isStatic:r,onError:h}){return am(i,void 0,{state:f,isStatic:r,onError:h})}function Yt(i){ze(!1,"A <Route> is only ever to be used as the child of <Routes> element, never rendered directly. Please wrap your <Route> in a <Routes>.")}function Kp({basename:i="/",children:s=null,location:f,navigationType:r="POP",navigator:h,static:m=!1,unstable_useTransitions:y}){ze(!Kn(),"You cannot render a <Router> inside another <Router>. You should never have more than one in your app.");let E=i.replace(/^\/*/,"/"),b=S.useMemo(()=>({basename:E,navigator:h,static:m,unstable_useTransitions:y,future:{}}),[E,h,m,y]);typeof f=="string"&&(f=Xa(f));let{pathname:v="/",search:_="",hash:g="",state:R=null,key:k="default",unstable_mask:D}=f,B=S.useMemo(()=>{let q=ol(v,E);return q==null?null:{location:{pathname:q,search:_,hash:g,state:R,key:k,unstable_mask:D},navigationType:r}},[E,v,_,g,R,k,r,D]);return Xt(B!=null,`<Router basename="${E}"> is not able to match the URL "${v}${_}${g}" because it does not start with the basename, so the <Router> won't render anything.`),B==null?null:S.createElement(wt.Provider,{value:b},S.createElement(Zn.Provider,{children:s,value:B}))}function Mh({children:i,location:s}){return Dp(Ws(i),s)}function Ws(i,s=[]){let f=[];return S.Children.forEach(i,(r,h)=>{if(!S.isValidElement(r))return;let m=[...s,h];if(r.type===S.Fragment){f.push.apply(f,Ws(r.props.children,m));return}ze(r.type===Yt,`[${typeof r.type=="string"?r.type:r.type.name}] is not a <Route> component. All component children of <Routes> must be a <Route> or <React.Fragment>`),ze(!r.props.index||!r.props.children,"An index route cannot have child routes.");let y={id:r.props.id||m.join("-"),caseSensitive:r.props.caseSensitive,element:r.props.element,Component:r.props.Component,index:r.props.index,path:r.props.path,middleware:r.props.middleware,loader:r.props.loader,action:r.props.action,hydrateFallbackElement:r.props.hydrateFallbackElement,HydrateFallback:r.props.HydrateFallback,errorElement:r.props.errorElement,ErrorBoundary:r.props.ErrorBoundary,hasErrorBoundary:r.props.hasErrorBoundary===!0||r.props.ErrorBoundary!=null||r.props.errorElement!=null,shouldRevalidate:r.props.shouldRevalidate,handle:r.props.handle,lazy:r.props.lazy};r.props.children&&(y.children=Ws(r.props.children,m)),f.push(y)}),f}var mi="get",vi="application/x-www-form-urlencoded";function bi(i){return typeof HTMLElement<"u"&&i instanceof HTMLElement}function Jp(i){return bi(i)&&i.tagName.toLowerCase()==="button"}function $p(i){return bi(i)&&i.tagName.toLowerCase()==="form"}function Fp(i){return bi(i)&&i.tagName.toLowerCase()==="input"}function Wp(i){return!!(i.metaKey||i.altKey||i.ctrlKey||i.shiftKey)}function Ip(i,s){return i.button===0&&(!s||s==="_self")&&!Wp(i)}var hi=null;function Pp(){if(hi===null)try{new FormData(document.createElement("form"),0),hi=!1}catch{hi=!0}return hi}var eg=new Set(["application/x-www-form-urlencoded","multipart/form-data","text/plain"]);function Js(i){return i!=null&&!eg.has(i)?(Xt(!1,`"${i}" is not a valid \`encType\` for \`<Form>\`/\`<fetcher.Form>\` and will default to "${vi}"`),null):i}function tg(i,s){let f,r,h,m,y;if($p(i)){let E=i.getAttribute("action");r=E?ol(E,s):null,f=i.getAttribute("method")||mi,h=Js(i.getAttribute("enctype"))||vi,m=new FormData(i)}else if(Jp(i)||Fp(i)&&(i.type==="submit"||i.type==="image")){let E=i.form;if(E==null)throw new Error('Cannot submit a <button> or <input type="submit"> without a <form>');let b=i.getAttribute("formaction")||E.getAttribute("action");if(r=b?ol(b,s):null,f=i.getAttribute("formmethod")||E.getAttribute("method")||mi,h=Js(i.getAttribute("formenctype"))||Js(E.getAttribute("enctype"))||vi,m=new FormData(E,i),!Pp()){let{name:v,type:_,value:g}=i;if(_==="image"){let R=v?`${v}.`:"";m.append(`${R}x`,"0"),m.append(`${R}y`,"0")}else v&&m.append(v,g)}}else{if(bi(i))throw new Error('Cannot submit element that is not <form>, <button>, or <input type="submit|image">');f=mi,r=null,h=vi,y=i}return m&&h==="text/plain"&&(y=m,m=void 0),{action:r,method:f.toLowerCase(),encType:h,formData:m,body:y}}Object.getOwnPropertyNames(Object.prototype).sort().join("\0");function ur(i,s){if(i===!1||i===null||typeof i>"u")throw new Error(s)}function im(i,s,f,r){let h=typeof i=="string"?new URL(i,typeof window>"u"?"server://singlefetch/":window.location.origin):i;return f?h.pathname.endsWith("/")?h.pathname=`${h.pathname}_.${r}`:h.pathname=`${h.pathname}.${r}`:h.pathname==="/"?h.pathname=`_root.${r}`:s&&ol(h.pathname,s)==="/"?h.pathname=`${pi(s)}/_root.${r}`:h.pathname=`${pi(h.pathname)}.${r}`,h}async function lg(i,s){if(i.id in s)return s[i.id];try{let f=await import(i.module);return s[i.id]=f,f}catch(f){return console.error(`Error loading route module \`${i.module}\`, reloading page...`),console.error(f),window.__reactRouterContext&&window.__reactRouterContext.isSpaMode,window.location.reload(),new Promise(()=>{})}}function ag(i){return i==null?!1:i.href==null?i.rel==="preload"&&typeof i.imageSrcSet=="string"&&typeof i.imageSizes=="string":typeof i.rel=="string"&&typeof i.href=="string"}async function ng(i,s,f){let r=await Promise.all(i.map(async h=>{let m=s.routes[h.route.id];if(m){let y=await lg(m,f);return y.links?y.links():[]}return[]}));return sg(r.flat(1).filter(ag).filter(h=>h.rel==="stylesheet"||h.rel==="preload").map(h=>h.rel==="stylesheet"?{...h,rel:"prefetch",as:"style"}:{...h,rel:"prefetch"}))}function Oh(i,s,f,r,h,m){let y=(b,v)=>f[v]?b.route.id!==f[v].route.id:!0,E=(b,v)=>{var _;return f[v].pathname!==b.pathname||((_=f[v].route.path)==null?void 0:_.endsWith("*"))&&f[v].params["*"]!==b.params["*"]};return m==="assets"?s.filter((b,v)=>y(b,v)||E(b,v)):m==="data"?s.filter((b,v)=>{var g;let _=r.routes[b.route.id];if(!_||!_.hasLoader)return!1;if(y(b,v)||E(b,v))return!0;if(b.route.shouldRevalidate){let R=b.route.shouldRevalidate({currentUrl:new URL(h.pathname+h.search+h.hash,window.origin),currentParams:((g=f[0])==null?void 0:g.params)||{},nextUrl:new URL(i,window.origin),nextParams:b.params,defaultShouldRevalidate:!0});if(typeof R=="boolean")return R}return!0}):[]}function ug(i,s,{includeHydrateFallback:f}={}){return ig(i.map(r=>{let h=s.routes[r.route.id];if(!h)return[];let m=[h.module];return h.clientActionModule&&(m=m.concat(h.clientActionModule)),h.clientLoaderModule&&(m=m.concat(h.clientLoaderModule)),f&&h.hydrateFallbackModule&&(m=m.concat(h.hydrateFallbackModule)),h.imports&&(m=m.concat(h.imports)),m}).flat(1))}function ig(i){return[...new Set(i)]}function cg(i){let s={},f=Object.keys(i).sort();for(let r of f)s[r]=i[r];return s}function sg(i,s){let f=new Set;return new Set(s),i.reduce((r,h)=>{let m=JSON.stringify(cg(h));return f.has(m)||(f.add(m),r.push({key:m,link:h})),r},[])}function ir(){let i=S.useContext(Qa);return ur(i,"You must render this element inside a <DataRouterContext.Provider> element"),i}function rg(){let i=S.useContext(gi);return ur(i,"You must render this element inside a <DataRouterStateContext.Provider> element"),i}var cr=S.createContext(void 0);cr.displayName="FrameworkContext";function sr(){let i=S.useContext(cr);return ur(i,"You must render this element inside a <HydratedRouter> element"),i}function og(i,s){let f=S.useContext(cr),[r,h]=S.useState(!1),[m,y]=S.useState(!1),{onFocus:E,onBlur:b,onMouseEnter:v,onMouseLeave:_,onTouchStart:g}=s,R=S.useRef(null);S.useEffect(()=>{if(i==="render"&&y(!0),i==="viewport"){let B=L=>{L.forEach(X=>{y(X.isIntersecting)})},q=new IntersectionObserver(B,{threshold:.5});return R.current&&q.observe(R.current),()=>{q.disconnect()}}},[i]),S.useEffect(()=>{if(r){let B=setTimeout(()=>{y(!0)},100);return()=>{clearTimeout(B)}}},[r]);let k=()=>{h(!0)},D=()=>{h(!1),y(!1)};return f?i!=="intent"?[m,R,{}]:[m,R,{onFocus:Yn(E,k),onBlur:Yn(b,D),onMouseEnter:Yn(v,k),onMouseLeave:Yn(_,D),onTouchStart:Yn(g,k)}]:[!1,R,{}]}function Yn(i,s){return f=>{i&&i(f),f.defaultPrevented||s(f)}}function fg({page:i,...s}){let f=Tp(),{router:r}=ir(),h=S.useMemo(()=>Qh(r.routes,i,r.basename),[r.routes,i,r.basename]);return h?f?S.createElement(hg,{page:i,matches:h,...s}):S.createElement(mg,{page:i,matches:h,...s}):null}function dg(i){let{manifest:s,routeModules:f}=sr(),[r,h]=S.useState([]);return S.useEffect(()=>{let m=!1;return ng(i,s,f).then(y=>{m||h(y)}),()=>{m=!0}},[i,s,f]),r}function hg({page:i,matches:s,...f}){let r=fl(),{future:h}=sr(),{basename:m}=ir(),y=S.useMemo(()=>{if(i===r.pathname+r.search+r.hash)return[];let E=im(i,m,h.unstable_trailingSlashAwareDataRequests,"rsc"),b=!1,v=[];for(let _ of s)typeof _.route.shouldRevalidate=="function"?b=!0:v.push(_.route.id);return b&&v.length>0&&E.searchParams.set("_routes",v.join(",")),[E.pathname+E.search]},[m,h.unstable_trailingSlashAwareDataRequests,i,r,s]);return S.createElement(S.Fragment,null,y.map(E=>S.createElement("link",{key:E,rel:"prefetch",as:"fetch",href:E,...f})))}function mg({page:i,matches:s,...f}){let r=fl(),{future:h,manifest:m,routeModules:y}=sr(),{basename:E}=ir(),{loaderData:b,matches:v}=rg(),_=S.useMemo(()=>Oh(i,s,v,m,r,"data"),[i,s,v,m,r]),g=S.useMemo(()=>Oh(i,s,v,m,r,"assets"),[i,s,v,m,r]),R=S.useMemo(()=>{if(i===r.pathname+r.search+r.hash)return[];let B=new Set,q=!1;if(s.forEach(X=>{var V;let J=m.routes[X.route.id];!J||!J.hasLoader||(!_.some(Y=>Y.route.id===X.route.id)&&X.route.id in b&&((V=y[X.route.id])!=null&&V.shouldRevalidate)||J.hasClientLoader?q=!0:B.add(X.route.id))}),B.size===0)return[];let L=im(i,E,h.unstable_trailingSlashAwareDataRequests,"data");return q&&B.size>0&&L.searchParams.set("_routes",s.filter(X=>B.has(X.route.id)).map(X=>X.route.id).join(",")),[L.pathname+L.search]},[E,h.unstable_trailingSlashAwareDataRequests,b,r,m,_,s,i,y]),k=S.useMemo(()=>ug(g,m),[g,m]),D=dg(g);return S.createElement(S.Fragment,null,R.map(B=>S.createElement("link",{key:B,rel:"prefetch",as:"fetch",href:B,...f})),k.map(B=>S.createElement("link",{key:B,rel:"modulepreload",href:B,...f})),D.map(({key:B,link:q})=>S.createElement("link",{key:B,nonce:f.nonce,...q,crossOrigin:q.crossOrigin??f.crossOrigin})))}function vg(...i){return s=>{i.forEach(f=>{typeof f=="function"?f(s):f!=null&&(f.current=s)})}}var yg=typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u";try{yg&&(window.__reactRouterVersion="7.14.2")}catch{}function pg({basename:i,children:s,unstable_useTransitions:f,window:r}){let h=S.useRef();h.current==null&&(h.current=Wy({window:r,v5Compat:!0}));let m=h.current,[y,E]=S.useState({action:m.action,location:m.location}),b=S.useCallback(v=>{f===!1?E(v):S.startTransition(()=>E(v))},[f]);return S.useLayoutEffect(()=>m.listen(b),[m,b]),S.createElement(Kp,{basename:i,children:s,location:y.location,navigationType:y.action,navigator:m,unstable_useTransitions:f})}var cm=/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i,Je=S.forwardRef(function({onClick:s,discover:f="render",prefetch:r="none",relative:h,reloadDocument:m,replace:y,unstable_mask:E,state:b,target:v,to:_,preventScrollReset:g,viewTransition:R,unstable_defaultShouldRevalidate:k,...D},B){let{basename:q,navigator:L,unstable_useTransitions:X}=S.useContext(wt),J=typeof _=="string"&&cm.test(_),V=Fh(_,q);_=V.to;let Y=wp(_,{relative:h}),K=fl(),G=null;if(E){let Ue=er(E,[],K.unstable_mask?K.unstable_mask.pathname:"/",!0);q!=="/"&&(Ue.pathname=Ue.pathname==="/"?q:Ut([q,Ue.pathname])),G=L.createHref(Ue)}let[te,ye,tt]=og(r,D),We=xg(_,{replace:y,unstable_mask:E,state:b,target:v,preventScrollReset:g,relative:h,viewTransition:R,unstable_defaultShouldRevalidate:k,unstable_useTransitions:X});function $e(Ue){s&&s(Ue),Ue.defaultPrevented||We(Ue)}let Ht=!(V.isExternal||m),St=S.createElement("a",{...D,...tt,href:(Ht?G:void 0)||V.absoluteURL||Y,onClick:Ht?$e:s,ref:vg(B,ye),target:v,"data-discover":!J&&f==="render"?"true":void 0});return te&&!J?S.createElement(S.Fragment,null,St,S.createElement(fg,{page:Y})):St});Je.displayName="Link";var gg=S.forwardRef(function({"aria-current":s="page",caseSensitive:f=!1,className:r="",end:h=!1,style:m,to:y,viewTransition:E,children:b,...v},_){let g=Jn(y,{relative:v.relative}),R=fl(),k=S.useContext(gi),{navigator:D,basename:B}=S.useContext(wt),q=k!=null&&Ag(g)&&E===!0,L=D.encodeLocation?D.encodeLocation(g).pathname:g.pathname,X=R.pathname,J=k&&k.navigation&&k.navigation.location?k.navigation.location.pathname:null;f||(X=X.toLowerCase(),J=J?J.toLowerCase():null,L=L.toLowerCase()),J&&B&&(J=ol(J,B)||J);const V=L!=="/"&&L.endsWith("/")?L.length-1:L.length;let Y=X===L||!h&&X.startsWith(L)&&X.charAt(V)==="/",K=J!=null&&(J===L||!h&&J.startsWith(L)&&J.charAt(L.length)==="/"),G={isActive:Y,isPending:K,isTransitioning:q},te=Y?s:void 0,ye;typeof r=="function"?ye=r(G):ye=[r,Y?"active":null,K?"pending":null,q?"transitioning":null].filter(Boolean).join(" ");let tt=typeof m=="function"?m(G):m;return S.createElement(Je,{...v,"aria-current":te,className:ye,ref:_,style:tt,to:y,viewTransition:E},typeof b=="function"?b(G):b)});gg.displayName="NavLink";var bg=S.forwardRef(({discover:i="render",fetcherKey:s,navigate:f,reloadDocument:r,replace:h,state:m,method:y=mi,action:E,onSubmit:b,relative:v,preventScrollReset:_,viewTransition:g,unstable_defaultShouldRevalidate:R,...k},D)=>{let{unstable_useTransitions:B}=S.useContext(wt),q=Tg(),L=_g(E,{relative:v}),X=y.toLowerCase()==="get"?"get":"post",J=typeof E=="string"&&cm.test(E),V=Y=>{if(b&&b(Y),Y.defaultPrevented)return;Y.preventDefault();let K=Y.nativeEvent.submitter,G=(K==null?void 0:K.getAttribute("formmethod"))||y,te=()=>q(K||Y.currentTarget,{fetcherKey:s,method:G,navigate:f,replace:h,state:m,relative:v,preventScrollReset:_,viewTransition:g,unstable_defaultShouldRevalidate:R});B&&f!==!1?S.startTransition(()=>te()):te()};return S.createElement("form",{ref:D,method:X,action:L,onSubmit:r?b:V,...k,"data-discover":!J&&i==="render"?"true":void 0})});bg.displayName="Form";function Sg(i){return`${i} must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.`}function sm(i){let s=S.useContext(Qa);return ze(s,Sg(i)),s}function xg(i,{target:s,replace:f,unstable_mask:r,state:h,preventScrollReset:m,relative:y,viewTransition:E,unstable_defaultShouldRevalidate:b,unstable_useTransitions:v}={}){let _=Mp(),g=fl(),R=Jn(i,{relative:y});return S.useCallback(k=>{if(Ip(k,s)){k.preventDefault();let D=f!==void 0?f:Qn(g)===Qn(R),B=()=>_(i,{replace:D,unstable_mask:r,state:h,preventScrollReset:m,relative:y,viewTransition:E,unstable_defaultShouldRevalidate:b});v?S.startTransition(()=>B()):B()}},[g,_,R,f,r,h,s,i,m,y,E,b,v])}var jg=0,Eg=()=>`__${String(++jg)}__`;function Tg(){let{router:i}=sm("useSubmit"),{basename:s}=S.useContext(wt),f=Xp(),r=i.fetch,h=i.navigate;return S.useCallback(async(m,y={})=>{let{action:E,method:b,encType:v,formData:_,body:g}=tg(m,s);if(y.navigate===!1){let R=y.fetcherKey||Eg();await r(R,f,y.action||E,{unstable_defaultShouldRevalidate:y.unstable_defaultShouldRevalidate,preventScrollReset:y.preventScrollReset,formData:_,body:g,formMethod:y.method||b,formEncType:y.encType||v,flushSync:y.flushSync})}else await h(y.action||E,{unstable_defaultShouldRevalidate:y.unstable_defaultShouldRevalidate,preventScrollReset:y.preventScrollReset,formData:_,body:g,formMethod:y.method||b,formEncType:y.encType||v,replace:y.replace,state:y.state,fromRouteId:f,flushSync:y.flushSync,viewTransition:y.viewTransition})},[r,h,s,f])}function _g(i,{relative:s}={}){let{basename:f}=S.useContext(wt),r=S.useContext(Qt);ze(r,"useFormAction must be used inside a RouteContext");let[h]=r.matches.slice(-1),m={...Jn(i||".",{relative:s})},y=fl();if(i==null){m.search=y.search;let E=new URLSearchParams(m.search),b=E.getAll("index");if(b.some(_=>_==="")){E.delete("index"),b.filter(g=>g).forEach(g=>E.append("index",g));let _=E.toString();m.search=_?`?${_}`:""}}return(!i||i===".")&&h.route.index&&(m.search=m.search?m.search.replace(/^\?/,"?index&"):"?index"),f!=="/"&&(m.pathname=m.pathname==="/"?f:Ut([f,m.pathname])),Qn(m)}function Ag(i,{relative:s}={}){let f=S.useContext(Ph);ze(f!=null,"`useViewTransitionState` must be used within `react-router-dom`'s `RouterProvider`. Did you accidentally import `RouterProvider` from `react-router`?");let{basename:r}=sm("useViewTransitionState"),h=Jn(i,{relative:s});if(!f.isTransitioning)return!1;let m=ol(f.currentLocation.pathname,r)||f.currentLocation.pathname,y=ol(f.nextLocation.pathname,r)||f.nextLocation.pathname;return yi(h.pathname,y)!=null||yi(h.pathname,m)!=null}const rm=/^\/vault\/([^/]+)\/admin(?=\/|$)/;function om(){if(typeof window>"u")return null;const i=window.location.pathname.match(rm);if(!i)return null;try{return decodeURIComponent(i[1])}catch{return null}}function Ng(){if(typeof window>"u")return"";const i=window.location.pathname,s=i.match(rm);return s?`/vault/${s[1]}/admin`:i==="/admin"||i.startsWith("/admin/")?"/admin":""}let Bl=null,Gt=null,aa=null,rl=null,Dh=!1;const Rg=.8,zg=1e3,Cg=3e4;function wg(){if(typeof window>"u")return;const i=window.location.hash;if(!i||i.length<2)return;const s=new URLSearchParams(i.slice(1)),f=s.get("token");if(!f)return;Bl=f,Gt=null,s.delete("token");const r=s.toString(),h=r.length>0?`#${r}`:"",m=`${window.location.pathname}${window.location.search}${h}`;window.history.replaceState(null,"",m)}function rr(){return Bl}function Mg(){Bl=null,Gt=null,rl=null,Si()}async function Vn(i){if(typeof window>"u")return{kind:"network-error",message:"no window"};let s;try{s=await fetch(`/admin/vault-admin-token/${encodeURIComponent(i)}`,{method:"GET",headers:{accept:"application/json"},credentials:"same-origin"})}catch(r){return{kind:"network-error",message:r instanceof Error?r.message:String(r)}}if(s.status===401||s.status===403||s.status===404)return{kind:"auth-required",status:s.status};if(!s.ok)return{kind:"network-error",message:`hub returned ${s.status}`};let f;try{f=await s.json()}catch(r){return{kind:"network-error",message:r instanceof Error?r.message:"could not parse mint response"}}return typeof f.token!="string"||f.token.length===0?{kind:"network-error",message:"mint response missing token"}:(Bl=f.token,Gt=f.expires_at?Og(f.expires_at):null,rl=i,fm(),Hg(),{kind:"ok",token:f.token})}function Og(i){const s=Date.parse(i);return Number.isNaN(s)?null:s}async function Is(i){if(Bl){if(Gt===null||Gt>Date.now())return{kind:"ok",token:Bl};Bl=null,Gt=null,Si()}return Vn(i)}async function Dg(i){typeof window>"u"||Bl||await Vn(i).catch(()=>{})}function fm(){if(Si(),Gt===null||rl===null||typeof document<"u"&&document.visibilityState==="hidden")return;const i=Date.now(),s=Gt-i;if(s<=0)return;const f=Math.max(Math.floor(s*Rg),zg);aa=setTimeout(()=>{Ug()},f)}function Si(){aa!==null&&(clearTimeout(aa),aa=null)}async function Ug(){if(aa=null,rl===null)return;const i=await Vn(rl);i.kind!=="ok"&&(typeof console<"u"&&console.warn("[vault-admin-spa] proactive token refresh failed; will retry once",i),aa=setTimeout(()=>{aa=null,rl!==null&&Vn(rl).catch(()=>{})},Cg))}function Hg(){Dh||typeof document>"u"||(Dh=!0,document.addEventListener("visibilitychange",()=>{if(document.visibilityState==="hidden"){Si();return}if(rl!==null){if(Gt!==null&&Gt<=Date.now()){Vn(rl).catch(()=>{});return}fm()}}))}class _e extends Error{constructor(s,f){super(f),this.status=s,this.name="HttpError"}}async function et(i,s,f={}){let r=rr();if(!r){const v=await Is(i);if(v.kind!=="ok")throw new _e(401,"no admin token — sign in to the hub to refresh");r=v.token}const{headers:h,...m}=f,y=v=>({accept:"application/json",...h??{},authorization:`Bearer ${v}`}),E=await fetch(s,{...m,headers:y(r)});if(E.status!==401)return E;Mg();const b=await Is(i);return b.kind!=="ok"?E:fetch(s,{...m,headers:y(b.token)})}async function Bg(){const i=await fetch("/vaults/list",{headers:{accept:"application/json"}});if(i.status===404)return[];if(!i.ok)throw new _e(i.status,`vaults/list fetch failed: ${i.status}`);return(await i.json()).vaults??[]}async function Lg(i){const s=await et(i,`/vault/${encodeURIComponent(i)}/`);if(!s.ok)throw new _e(s.status,await rt(s));return await s.json()}async function kg(i){const s=await et(i,`/vault/${encodeURIComponent(i)}/.parachute/mirror`);if(!s.ok)throw new _e(s.status,await rt(s));return await s.json()}async function qg(i,s){const f=await et(i,`/vault/${encodeURIComponent(i)}/.parachute/mirror`,{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify(s)});if(!f.ok)throw new _e(f.status,await rt(f));return await f.json()}async function Yg(i){const s=await et(i,`/vault/${encodeURIComponent(i)}/.parachute/mirror/run-now`,{method:"POST"});if(!s.ok)throw new _e(s.status,await rt(s));return await s.json()}async function Gg(i){const s=await et(i,`/vault/${encodeURIComponent(i)}/.parachute/mirror/push-now`,{method:"POST"});if(!s.ok)throw new _e(s.status,await rt(s));return await s.json()}async function dm(i){const s=await et(i,`/vault/${encodeURIComponent(i)}/.parachute/mirror/auth`);if(!s.ok)throw new _e(s.status,await rt(s));return await s.json()}async function Xg(i){const s=await et(i,`/vault/${encodeURIComponent(i)}/.parachute/mirror/auth`,{method:"DELETE"});if(!s.ok)throw new _e(s.status,await rt(s));return await s.json()}async function Qg(i){const s=await et(i,`/vault/${encodeURIComponent(i)}/.parachute/mirror/auth/github/device-code`,{method:"POST"});if(!s.ok)throw new _e(s.status,await rt(s));return await s.json()}async function Vg(i,s){const f=await et(i,`/vault/${encodeURIComponent(i)}/.parachute/mirror/auth/github/poll`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({polling_id:s})});if(!f.ok&&f.status!==404)throw new _e(f.status,await rt(f));return await f.json()}async function Zg(i,s){const f=await et(i,`/vault/${encodeURIComponent(i)}/.parachute/mirror/auth/pat`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(s)});if(!f.ok)throw new _e(f.status,await rt(f));return await f.json()}async function hm(i){const s=await et(i,`/vault/${encodeURIComponent(i)}/.parachute/mirror/auth/github/repos`);if(!s.ok)throw new _e(s.status,await rt(s));return await s.json()}async function Kg(i,s){const f=await et(i,`/vault/${encodeURIComponent(i)}/.parachute/mirror/auth/github/create-repo`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(s)});if(!f.ok)throw new _e(f.status,await rt(f));return await f.json()}async function Jg(i,s){const f=await et(i,`/vault/${encodeURIComponent(i)}/.parachute/mirror/auth/github/select-repo`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(s)});if(!f.ok)throw new _e(f.status,await rt(f));return await f.json()}async function $g(i,s){const f=await et(i,`/vault/${encodeURIComponent(i)}/.parachute/mirror/import`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(s)});if(!f.ok)throw new _e(f.status,await rt(f));return await f.json()}async function rt(i){try{const s=await i.text(),f=JSON.parse(s);if(f.error_description)return f.error_description;if(f.message)return f.message;if(f.error)return f.error;if(s)return s}catch{}return`${i.status} ${i.statusText}`}function mm(i){const s=i.split(".");if(s.length!==3)return null;try{const f=s[1].replace(/-/g,"+").replace(/_/g,"/"),r=f.length%4===0?"":"=".repeat(4-f.length%4),h=atob(f+r),m=JSON.parse(h);return m===null||typeof m!="object"?null:m}catch{return null}}function Fg(i){if(!i)return[];const s=mm(i);if(!s)return[];const f=s.scope;if(typeof f=="string"&&f.length>0)return f.split(/\s+/).filter(h=>h.length>0);const r=s.scopes;return Array.isArray(r)?r.filter(h=>typeof h=="string"&&h.length>0):[]}function vm(i){return Fg(rr()).includes(`vault:${i}:admin`)}function ym(){const i=rr();if(!i)return null;const s=mm(i);if(!s)return null;const f=s.iss;return typeof f!="string"||f.length===0?null:f.replace(/\/$/,"")}const Uh=5e3;function or({vaultName:i,status:s,onRecovered:f}){const r=s===403;if(S.useEffect(()=>{if(r)return;let E=!1,b=null;const v=async()=>{if(E||typeof document<"u"&&document.visibilityState!=="visible")return;const g=await Is(i);if(!E){if(g.kind==="ok"){f();return}b=setTimeout(()=>void v(),Uh)}},_=()=>{typeof document>"u"||document.visibilityState==="visible"&&(b!==null&&(clearTimeout(b),b=null),v())};return b=setTimeout(()=>void v(),Uh),typeof document<"u"&&document.addEventListener("visibilitychange",_),()=>{E=!0,b!==null&&clearTimeout(b),typeof document<"u"&&document.removeEventListener("visibilitychange",_)}},[i,f,r]),r)return c.jsxs("div",{className:"warn-banner",role:"status",children:[c.jsxs("p",{style:{margin:"0 0 0.5rem"},children:["You're signed in, but vault management is restricted to the hub admin."," ",c.jsx("a",{href:"/account/",children:"Go to your account →"})]}),c.jsx("p",{className:"dim",style:{margin:0,fontSize:"0.85rem"},children:"Ask the hub admin if you need access to manage this vault."})]});const h=Wg(),m=`/login?next=${encodeURIComponent(h)}`,y=s===404?"This hub doesn't host a vault by that name.":"You're not signed in to the hub.";return c.jsxs("div",{className:"warn-banner",role:"status",children:[c.jsxs("p",{style:{margin:"0 0 0.5rem"},children:[y," ",c.jsx("a",{href:m,children:"Sign in to the hub →"})]}),c.jsx("p",{className:"dim",style:{margin:0,fontSize:"0.85rem"},children:"After signing in, this page will refresh automatically."})]})}function Wg(){return typeof window>"u"?"/":`${window.location.pathname}${window.location.search}${window.location.hash}`}function Hh({vaultName:i}={}){const s=lr(),f=i??s.name,r=i!==void 0,h=r?"/tokens":`/vault/${encodeURIComponent(f??"")}/tokens`,m=r?"/mirror":`/vault/${encodeURIComponent(f??"")}/mirror`,[y,E]=S.useState({kind:"loading"}),[b,v]=S.useState(0),_=S.useCallback(()=>v(k=>k+1),[]);if(S.useEffect(()=>{let k=!1;if(!f){E({kind:"missing"});return}return E({kind:"loading"}),Lg(f).then(D=>{k||E({kind:"ok",vault:D})}).catch(D=>{if(k)return;if(D instanceof _e){if(D.status===401||D.status===403){E({kind:"auth-required",status:D.status});return}if(D.status===404){E({kind:"missing"});return}E({kind:"error",message:`${D.status}: ${D.message}`});return}const B=D instanceof Error?D.message:String(D);E({kind:"error",message:B})}),()=>{k=!0}},[f,b]),y.kind==="loading")return c.jsxs("div",{children:[c.jsx("h2",{children:"Vault"}),c.jsx("p",{className:"muted",children:"Loading…"})]});if(y.kind==="auth-required")return c.jsxs("div",{children:[c.jsxs("h2",{children:["Vault ",c.jsx("code",{children:f})]}),c.jsx(or,{vaultName:f??"",status:y.status,onRecovered:_}),r?null:c.jsx(Je,{to:"/",children:"← Back to vaults"})]});if(y.kind==="missing")return c.jsxs("div",{children:[c.jsx("h2",{children:"Vault not found"}),c.jsxs("p",{className:"muted",children:["No vault named ",c.jsx("code",{children:f})," is registered on this server."]}),r?null:c.jsx(Je,{to:"/",children:"← Back to vaults"})]});if(y.kind==="error")return c.jsxs("div",{children:[c.jsxs("h2",{children:["Vault ",c.jsx("code",{children:f})]}),c.jsx("div",{className:"error-banner",children:c.jsx("code",{children:y.message})}),r?null:c.jsx(Je,{to:"/",children:"← Back to vaults"})]});const{vault:g}=y,R=`/vault/${g.name}`;return c.jsxs("div",{children:[c.jsxs("div",{className:"list-header",children:[c.jsxs("h2",{children:["Vault ",c.jsx("code",{children:g.name})]}),r?null:c.jsx(Je,{to:"/",className:"muted",children:"← All vaults"})]}),c.jsxs("div",{className:"kv section",children:[c.jsx("div",{children:"Name"}),c.jsx("div",{children:c.jsx("code",{children:g.name})}),g.description?c.jsxs(c.Fragment,{children:[c.jsx("div",{children:"Description"}),c.jsx("div",{children:g.description})]}):null,c.jsx("div",{children:"Mount"}),c.jsx("div",{children:c.jsx("code",{children:R})}),g.createdAt?c.jsxs(c.Fragment,{children:[c.jsx("div",{children:"Created"}),c.jsx("div",{children:c.jsx("code",{children:g.createdAt})})]}):null]}),c.jsxs("div",{className:"section",children:[c.jsx("h3",{style:{margin:"0 0 0.85rem",fontSize:"1rem",fontWeight:500},children:"Stats"}),c.jsxs("div",{className:"stats",children:[c.jsxs("div",{className:"stat",children:[c.jsx("div",{className:"label",children:"Notes"}),c.jsx("div",{className:"value",children:g.stats.totalNotes})]}),c.jsxs("div",{className:"stat",children:[c.jsx("div",{className:"label",children:"Tags"}),c.jsx("div",{className:"value",children:g.stats.tagCount})]}),c.jsxs("div",{className:"stat",children:[c.jsx("div",{className:"label",children:"Attachments"}),c.jsx("div",{className:"value",children:g.stats.attachmentCount})]}),c.jsxs("div",{className:"stat",children:[c.jsx("div",{className:"label",children:"Links"}),c.jsx("div",{className:"value",children:g.stats.linkCount})]})]})]}),c.jsxs("div",{className:"section",children:[c.jsx("h3",{style:{margin:"0 0 0.85rem",fontSize:"1rem",fontWeight:500},children:"Manage"}),c.jsxs("ul",{className:"manage-list",children:[c.jsxs("li",{children:[c.jsx(Je,{to:h,children:"Tokens →"}),c.jsx("span",{className:"dim",children:" mint, list, and revoke hub access tokens (JWTs)"})]}),c.jsxs("li",{children:[c.jsx(Je,{to:m,children:"Git backup →"}),c.jsx("span",{className:"dim",children:" mirror this vault to a git repository on a schedule, or on demand"})]}),c.jsx(Ig,{vaultName:g.name})]})]})]})}function Ig({vaultName:i}){const s=ym();if(!s)return c.jsxs("li",{children:[c.jsx("span",{children:"Permissions →"}),c.jsxs("span",{className:"dim",children:[" ","grants are managed on hub (the OAuth issuer); link will be live when hub ships its permissions UI."]})]});const f=`${s}/hub/permissions?vault=${encodeURIComponent(i)}`;return c.jsxs("li",{children:[c.jsx("a",{href:f,children:"Permissions →"}),c.jsxs("span",{className:"dim",children:[" ","grants are managed on hub (the OAuth issuer); link will be live when hub ships its permissions UI."]})]})}const Pg=[{value:"events",label:"On change (default)"},{value:"manual",label:"Manual only"}],e0=[{id:"history",label:"History",subtext:"Local audit trail. Hidden under vault data. Events-driven.",apply:i=>({...i,enabled:!0,location:"internal",sync_mode:"events",auto_commit:!0,auto_push:!1})},{id:"live",label:"External folder mirror",subtext:"Visible folder. Open in Obsidian, push to GitHub. Events-driven.",apply:i=>({...i,enabled:!0,location:"external",sync_mode:"events",auto_commit:!0})},{id:"manual",label:"Manual Export",subtext:"Snapshot on demand. No auto-fire.",apply:i=>({...i,enabled:!0,location:"external",sync_mode:"manual",auto_commit:!0,auto_push:!1})}];function Bh({vaultName:i}={}){const s=lr(),f=i??s.name,r=i!==void 0,h=r?"/":`/vault/${encodeURIComponent(f??"")}`,[m,y]=S.useState({kind:"loading"}),[E,b]=S.useState(0);S.useEffect(()=>{let _=!1;if(f)return y({kind:"loading"}),kg(f).then(g=>{_||y({kind:"ok",snapshot:g})}).catch(g=>{if(_)return;if(g instanceof _e&&(g.status===401||g.status===403)){y({kind:"auth-required",status:g.status});return}const R=g instanceof Error?g.message:String(g);y({kind:"error",message:R})}),()=>{_=!0}},[f,E]);const v=S.useCallback(()=>b(_=>_+1),[]);return f?c.jsxs("div",{children:[c.jsxs("div",{className:"list-header",children:[c.jsxs("h2",{children:["Backup for ",c.jsx("code",{children:f})]}),c.jsx(Je,{to:h,className:"muted",children:"← Vault detail"})]}),m.kind==="loading"?c.jsx("p",{className:"muted",children:"Loading…"}):null,m.kind==="auth-required"?c.jsx(or,{vaultName:f,status:m.status,onRecovered:v}):null,m.kind==="error"?c.jsx("div",{className:"error-banner",children:c.jsx("code",{children:m.message})}):null,m.kind==="ok"?c.jsx(t0,{vaultName:f,snapshot:m.snapshot,onRefresh:()=>b(_=>_+1),onSnapshot:_=>y({kind:"ok",snapshot:_})}):null]}):c.jsxs("div",{children:[c.jsx("h2",{children:"Mirror"}),c.jsx("p",{className:"muted",children:"Missing vault name."}),r?null:c.jsx(Je,{to:"/",children:"← Back to vaults"})]})}function t0({vaultName:i,snapshot:s,onRefresh:f,onSnapshot:r}){const h=vm(i),[m,y]=S.useState(null),[E,b]=S.useState(null);S.useEffect(()=>{if(!h)return;let g=!1;return dm(i).then(R=>{g||y(R)}).catch(R=>{g||b(R instanceof Error?R.message:String(R))}),()=>{g=!0}},[i,h]);const[v,_]=S.useState(!1);return c.jsxs(c.Fragment,{children:[c.jsx(l0,{status:s.status,config:s.config,creds:m}),h?null:c.jsxs("div",{className:"warn-banner",children:["You're viewing this page with a read-only token. Saving config + manual run require ",c.jsxs("code",{children:["vault:",i,":admin"]}),`. Re-enter from the hub directory's "Manage" link with an admin-scoped session to make changes.`]}),h?c.jsx(u0,{vaultName:i,creds:m,credsError:E,onCredsChanged:y,onCredsSaved:()=>{f()},locationIsExternal:s.config.location==="external"}):null,c.jsxs("div",{className:"section",children:[c.jsx("button",{type:"button",className:"secondary",onClick:()=>_(g=>!g),"aria-expanded":v,children:v?"Hide advanced settings":"Advanced settings"}),v?null:c.jsx("p",{className:"dim",style:{margin:"0.6rem 0 0"},children:"Manual exports, run-now, an external (Obsidian-visible) mirror folder, commit settings, and import-from-a-repo. Most owners never need these."})]}),v?c.jsxs(c.Fragment,{children:[c.jsx(a0,{status:s.status,config:s.config,creds:m,canRun:h&&s.status.enabled,vaultName:i,onSnapshot:r}),c.jsx(n0,{vaultName:i,initial:s.config,readOnly:!h,creds:m,onSaved:g=>{r(g),f()}}),h?c.jsx(o0,{vaultName:i,creds:m}):null]}):null]})}function l0({status:i,config:s,creds:f}){var y;if(!s.enabled)return c.jsxs("div",{className:"warn-banner",role:"status",children:[c.jsx("strong",{children:"Version history is off."})," This vault isn't saving a local history of changes right now. Open ",c.jsx("strong",{children:"Advanced settings"})," ","below to turn it back on."]});const r=!!(f!=null&&f.active_method),h=s.auto_push&&r,m=(f==null?void 0:f.active_method)==="github_oauth"?(y=f.github_oauth)==null?void 0:y.user_login:void 0;return c.jsxs("div",{className:"mint-banner",role:"status",style:{marginBottom:"1rem"},children:[h?c.jsxs(c.Fragment,{children:[c.jsx("strong",{children:"✓ Version history + backed up off this machine."})," ",m?c.jsxs(c.Fragment,{children:["Every change is saved locally and pushed to GitHub as"," ",c.jsxs("code",{children:["@",m]}),"."]}):c.jsx(c.Fragment,{children:"Every change is saved locally and pushed to your git remote automatically."})]}):c.jsxs(c.Fragment,{children:[c.jsx("strong",{children:"✓ Version history — on."})," Your vault automatically saves a full local history of every change. Want an off-machine copy too? Use ",c.jsx("strong",{children:"Back up to GitHub"})," below."]}),i.last_error?c.jsxs("p",{className:"dim",style:{margin:"0.5rem 0 0"},children:["Heads up — the last backup pass reported an error. See"," ",c.jsx("strong",{children:"Advanced settings"})," below for details."]}):null]})}function a0({status:i,config:s,creds:f,canRun:r,vaultName:h,onSnapshot:m}){const y=s.enabled,[E,b]=S.useState(!1),[v,_]=S.useState(!1),[g,R]=S.useState(null),k=async()=>{b(!0),R(null);try{const L=await Yg(h);m(L)}catch(L){R(L instanceof Error?L.message:String(L))}finally{b(!1)}},D=async()=>{_(!0),R(null);try{const L=await Gg(h);m(L)}catch(L){R(L instanceof Error?L.message:String(L))}finally{_(!1)}},B=!!(f!=null&&f.active_method)||s.auto_push,q=!r||v||!B;return c.jsxs("div",{className:"section",children:[c.jsx("h3",{style:{margin:"0 0 0.85rem",fontSize:"1rem",fontWeight:500},children:"Status"}),c.jsxs("div",{className:"kv",children:[c.jsx("div",{children:"Enabled"}),c.jsx("div",{children:y?c.jsx("code",{children:"yes"}):c.jsx("code",{children:"no"})}),c.jsx("div",{children:"Watch loop"}),c.jsx("div",{children:i.watch_running?c.jsx("code",{children:"running"}):c.jsx("code",{children:"stopped"})}),c.jsx("div",{children:"Path"}),c.jsx("div",{children:i.mirror_path?c.jsx("code",{children:i.mirror_path}):c.jsx("span",{className:"dim",children:"—"})}),c.jsx("div",{children:"Last export"}),c.jsx("div",{children:i.last_export_at?c.jsxs(c.Fragment,{children:[c.jsx("code",{children:i.last_export_at}),i.last_export_notes_count!==null?c.jsxs("span",{className:"dim",children:[" ","· ",i.last_export_notes_count," note",i.last_export_notes_count===1?"":"s"]}):null]}):c.jsx("span",{className:"dim",children:"never"})}),c.jsx("div",{children:"Last commit"}),c.jsx("div",{children:i.last_commit_sha?c.jsx("code",{children:i.last_commit_sha.slice(0,10)}):c.jsx("span",{className:"dim",children:"—"})}),c.jsx("div",{children:"Last push"}),c.jsx("div",{children:i.last_push_at?c.jsxs(c.Fragment,{children:[c.jsx("code",{children:i.last_push_at}),i.last_push_sha?c.jsxs("span",{className:"dim",children:[" ","· ",c.jsx("code",{children:i.last_push_sha.slice(0,10)})]}):null]}):c.jsx("span",{className:"dim",children:"never"})}),i.commits_unpushed!==null&&i.commits_unpushed>0?c.jsxs(c.Fragment,{children:[c.jsx("div",{}),c.jsxs("div",{className:"dim",children:[i.commits_unpushed," commit",i.commits_unpushed===1?"":"s"," ready to push"]})]}):null]}),i.last_push_error?c.jsxs("div",{className:"error-banner",style:{marginTop:"1rem"},children:[c.jsx("strong",{children:"Last push failed:"})," ",c.jsx("code",{children:i.last_push_error})]}):null,i.last_error?c.jsxs("div",{className:"error-banner",style:{marginTop:"1rem"},children:[c.jsx("strong",{children:"Last error:"})," ",c.jsx("code",{children:i.last_error})]}):null,g?c.jsx("div",{className:"error-banner",style:{marginTop:"1rem"},children:c.jsx("code",{children:g})}):null,c.jsxs("div",{className:"actions",children:[c.jsx("button",{type:"button",onClick:k,disabled:!r||E,title:y?r?void 0:"Admin scope required to trigger a manual export.":"Enable the mirror first, then trigger a manual export.",children:E?"Running…":"Run export now"}),c.jsx("button",{type:"button",className:"secondary",onClick:D,disabled:q,title:y?r?B?void 0:"Wire credentials or turn on auto-push to push to a remote.":"Admin scope required to push.":"Enable the mirror first, then push.",children:v?"Pushing…":"Push now"})]})]})}function n0({vaultName:i,initial:s,readOnly:f,creds:r,onSaved:h}){const[m,y]=S.useState(s),[E,b]=S.useState(!1),[v,_]=S.useState(!1),[g,R]=S.useState(null),[k,D]=S.useState(null),[B,q]=S.useState(0);S.useEffect(()=>{y(s)},[s]);const L=V=>{const Y=V.apply(m);y(Y)},X=V=>{y(Y=>({...Y,sync_mode:V}))},J=async V=>{if(V.preventDefault(),!f){_(!0),R(null),D(null);try{const Y=await qg(i,m);h(Y),q(K=>K+1),setTimeout(()=>q(0),3e3)}catch(Y){if(Y instanceof _e){const K=Y.message;R(K);const G=K.toLowerCase();G.includes("external_path")?D("external_path"):G.includes("commit_template")?D("commit_template"):G.includes("safety_net_seconds")?D("safety_net_seconds"):G.includes("sync_mode")?D("sync_mode"):G.includes("auto_push")?D("auto_push"):G.includes("location")?D("location"):D(null)}else R(Y instanceof Error?Y.message:String(Y))}finally{_(!1)}}};return c.jsxs("form",{className:"section",onSubmit:J,children:[c.jsx("h3",{style:{margin:"0 0 0.85rem",fontSize:"1rem",fontWeight:500},children:"Configuration"}),c.jsxs("div",{className:"form-row",children:[c.jsxs("label",{children:[c.jsx("input",{type:"checkbox",checked:m.enabled,disabled:f,onChange:V=>y(Y=>({...Y,enabled:V.target.checked})),style:{width:"auto",marginRight:"0.5rem"}}),"Enable mirror"]}),c.jsx("p",{className:"dim",style:{margin:"0.35rem 0 0"},children:"Master switch. When off, no export / commit / push runs."})]}),c.jsxs("div",{className:"form-row",children:[c.jsx("label",{children:"Presets"}),c.jsxs("p",{className:"dim",style:{marginTop:0,marginBottom:"0.5rem",fontSize:"0.9em"},children:[c.jsx("strong",{children:"History"})," is the default every vault ships with — vault manages the history folder under its own data dir, no path to pick, no remote to configure. ",c.jsx("strong",{children:"External folder mirror"})," +"," ",c.jsx("strong",{children:"Manual Export"})," are for operators who want to point at a visible folder (Obsidian, GitHub)."]}),c.jsx("div",{className:"preset-grid",children:e0.map(V=>c.jsxs("button",{type:"button",className:"preset-card",disabled:f,onClick:()=>L(V),"aria-label":`Apply ${V.label} preset`,children:[c.jsx("strong",{children:V.label}),c.jsx("span",{className:"dim",children:V.subtext})]},V.id))})]}),c.jsxs("div",{className:"form-row",children:[c.jsx("label",{children:"Location"}),c.jsxs("div",{children:[c.jsxs("label",{className:"radio-row",children:[c.jsx("input",{type:"radio",name:"location",value:"internal",checked:m.location==="internal",disabled:f,onChange:()=>y(V=>({...V,location:"internal"}))}),c.jsxs("span",{children:["Internal ",c.jsx("span",{className:"dim",children:"— hidden under vault data dir"})]})]}),c.jsxs("label",{className:"radio-row",children:[c.jsx("input",{type:"radio",name:"location",value:"external",checked:m.location==="external",disabled:f,onChange:()=>y(V=>({...V,location:"external"}))}),c.jsxs("span",{children:["External ",c.jsx("span",{className:"dim",children:"— operator-picked path"})]})]})]})]}),m.location==="external"?c.jsxs("div",{className:"form-row",children:[c.jsx("label",{htmlFor:"external-path",children:"External path"}),c.jsx("input",{id:"external-path",type:"text",value:m.external_path??"",disabled:f,onChange:V=>y(Y=>({...Y,external_path:V.target.value.length>0?V.target.value:null})),placeholder:"/Users/you/Documents/vault-mirror","aria-invalid":k==="external_path"}),c.jsxs("div",{className:"warn-banner",style:{marginTop:"0.5rem"},role:"alert",children:["Path must exist AND be a git repo (run ",c.jsx("code",{children:"git init"})," first if needed)."]})]}):null,c.jsxs("div",{className:"form-row",children:[c.jsx("label",{htmlFor:"sync-mode-select",children:"Sync mode"}),c.jsx("select",{id:"sync-mode-select",value:m.sync_mode,disabled:f,onChange:V=>X(V.target.value),children:Pg.map(V=>c.jsx("option",{value:V.value,children:V.label},V.value))}),c.jsx("p",{className:"dim",style:{margin:"0.35rem 0 0"},children:m.sync_mode==="manual"?'No auto-fire. Exports only run when you click "Run export now" (or run `parachute-vault export` from the CLI).':"Every change to a note, tag, or attachment triggers an export within ~500ms. A background safety check runs hourly to catch anything missed."})]}),c.jsxs("div",{className:"form-row",children:[c.jsxs("label",{children:[c.jsx("input",{type:"checkbox",checked:m.auto_commit,disabled:f,onChange:V=>y(Y=>({...Y,auto_commit:V.target.checked})),style:{width:"auto",marginRight:"0.5rem"}}),"Commit after each export"]}),m.auto_commit?null:c.jsx("p",{className:"hint",style:{marginTop:"0.25rem",fontSize:"0.85em"},children:"Note: the export cursor still advances after each pass. Subsequent runs only re-export notes written since the last pass — even when triggered manually."})]}),m.location==="external"||r!=null&&r.active_method?c.jsxs("div",{className:"form-row",children:[c.jsxs("label",{children:[c.jsx("input",{type:"checkbox",checked:m.auto_push,disabled:f,onChange:V=>y(Y=>({...Y,auto_push:V.target.checked})),style:{width:"auto",marginRight:"0.5rem"}}),"Push after each commit"]}),c.jsx("p",{className:"dim",style:{margin:"0.35rem 0 0",fontSize:"0.9em"},children:"When credentials are configured, vault can push the mirror's commits to your remote regardless of whether the mirror folder lives under vault's data dir or somewhere visible."}),m.auto_push?r!=null&&r.active_method?c.jsxs("div",{className:"info-banner",style:{marginTop:"0.5rem"},role:"status",children:[r.active_method==="github_oauth"&&r.github_oauth?c.jsxs(c.Fragment,{children:["Will push to ",c.jsxs("code",{children:["@",r.github_oauth.user_login]})," on GitHub."]}):r.active_method==="pat"&&r.pat?c.jsxs(c.Fragment,{children:["Will push using saved credential: ",c.jsx("code",{children:r.pat.label}),"."]}):c.jsx(c.Fragment,{children:"Will push using saved credential."})," ","Failed pushes are logged but won't crash the export."]}):c.jsxs("div",{className:"warn-banner",style:{marginTop:"0.5rem"},role:"alert",children:["Auto-push needs git credentials. Either connect GitHub in the"," ",c.jsx("strong",{children:"Back up to GitHub"})," section above, or paste a Personal Access Token + remote URL there. Failed pushes are logged but won't crash the export."]}):null]}):null,c.jsx("div",{className:"form-row",children:c.jsxs("button",{type:"button",className:"secondary",onClick:()=>b(V=>!V),children:[E?"Hide":"Show"," advanced"]})}),E?c.jsxs("div",{className:"form-row",children:[c.jsx("label",{htmlFor:"commit-template",children:"Commit template"}),c.jsx("input",{id:"commit-template",type:"text",value:m.commit_template,disabled:f,onChange:V=>y(Y=>({...Y,commit_template:V.target.value})),"aria-invalid":k==="commit_template"}),c.jsxs("p",{className:"dim",style:{margin:"0.35rem 0 0"},children:["Supports ",c.jsx("code",{children:"{{date}}"}),", ",c.jsx("code",{children:"{{notes_changed}}"}),","," ",c.jsx("code",{children:"{{plural}}"}),", ",c.jsx("code",{children:"{{first_note_title}}"}),","," ",c.jsx("code",{children:"{{vault_name}}"}),"."]})]}):null,g?c.jsx("div",{className:"error-banner",role:"alert",children:c.jsx("code",{children:g})}):null,B>0?c.jsx("div",{className:"mint-banner",style:{padding:"0.75rem 1rem",marginBottom:"1rem"},role:"status",children:"Saved."}):null,c.jsx("div",{className:"actions",children:c.jsx("button",{type:"submit",disabled:f||v,children:v?"Saving…":"Save"})})]})}function u0({vaultName:i,creds:s,credsError:f,onCredsChanged:r,onCredsSaved:h,locationIsExternal:m}){const[y,E]=S.useState(!1),[b,v]=S.useState(!1),[_,g]=S.useState(!1),[R,k]=S.useState(null),[D,B]=S.useState(null),q=(s==null?void 0:s.active_method)!==null&&(s==null?void 0:s.active_method)!==void 0,L=async()=>{if(confirm("Disconnect git remote credentials? Auto-push will stop working until you reconnect.")){g(!0),k(null);try{const X=await Xg(i);r(X)}catch(X){k(X instanceof Error?X.message:String(X))}finally{g(!1)}}};return c.jsxs("div",{className:"section",id:"git-remote-section",children:[c.jsx("h3",{style:{margin:"0 0 0.85rem",fontSize:"1rem",fontWeight:500},children:q?"Backed up to a git remote":"Back up to GitHub"}),c.jsx("p",{className:"dim",style:{marginTop:0},children:q?c.jsxs(c.Fragment,{children:["Your vault pushes its history to an off-machine git remote. Credentials are stored on this server with ",c.jsx("code",{children:"0600"})," file permissions — never sent to GitHub or any third party."]}):c.jsxs(c.Fragment,{children:["Keep an off-machine copy: push your vault's history to GitHub (or any HTTPS git host). Credentials are stored on this server with"," ",c.jsx("code",{children:"0600"})," file permissions, never sent to a third party."]})}),!m&&!(s!=null&&s.active_method)?c.jsx("div",{className:"info-banner",style:{marginBottom:"0.75rem"},role:"status",children:"Internal mirrors live under the vault's data directory. To push them to a remote, paste a Personal Access Token + remote URL below — that wires the remote and turns on auto-push automatically."}):null,f?c.jsxs("div",{className:"error-banner",role:"alert",children:["Could not load credential status: ",c.jsx("code",{children:f})]}):null,q?c.jsxs("div",{className:"kv",style:{marginBottom:"0.75rem"},children:[c.jsx("div",{children:"Status"}),c.jsx("div",{children:(s==null?void 0:s.active_method)==="github_oauth"&&s.github_oauth?c.jsxs(c.Fragment,{children:["Connected to ",c.jsxs("code",{children:["@",s.github_oauth.user_login]})," on GitHub"]}):(s==null?void 0:s.active_method)==="pat"&&s.pat?c.jsxs(c.Fragment,{children:["Custom credential: ",c.jsx("code",{children:s.pat.label})]}):null}),(s==null?void 0:s.active_method)==="github_oauth"&&s.github_oauth?c.jsxs(c.Fragment,{children:[c.jsx("div",{children:"Token"}),c.jsxs("div",{children:[c.jsx("code",{children:s.github_oauth.token_preview})," ",c.jsxs("span",{className:"dim",children:["· scope ",s.github_oauth.scope||"—"," · authorized"," ",s.github_oauth.authorized_at.slice(0,10)]})]})]}):null,(s==null?void 0:s.active_method)==="pat"&&s.pat?c.jsxs(c.Fragment,{children:[c.jsx("div",{children:"Remote"}),c.jsx("div",{children:c.jsx("code",{children:s.pat.remote_url})}),c.jsx("div",{children:"Token"}),c.jsx("div",{children:c.jsx("code",{children:s.pat.token_preview})})]}):null]}):c.jsx("p",{className:"dim",children:"Not connected. Auto-push won't work until you connect. Personal Access Token is the universal path (works with GitHub, GitLab, Gitea, Bitbucket, anything that takes an HTTPS token); the GitHub shortcut just saves a step for GitHub users."}),R?c.jsx("div",{className:"error-banner",role:"alert",children:c.jsx("code",{children:R})}):null,D?c.jsxs("div",{className:"mint-banner",style:{marginBottom:"0.75rem"},role:"status",children:[c.jsx("strong",{children:"Credentials saved."})," ",(()=>{const X=D,J=X.initial_push.fired&&X.initial_push.pushed?X.initial_push.sha:void 0,V=X.initial_push.fired&&!X.initial_push.pushed?X.initial_push.error:void 0;return X.auto_push_was_already_enabled?J?c.jsxs(c.Fragment,{children:["Auto-push was already on; just pushed"," ",c.jsx("code",{children:J.slice(0,10)}),"."]}):V?c.jsx(c.Fragment,{children:"Auto-push was already on; the push attempt failed — see Advanced settings → Status for details."}):c.jsx(c.Fragment,{children:"Auto-push was already on; nothing to push right now."}):X.auto_push_enabled?J?c.jsxs(c.Fragment,{children:["Auto-push enabled and the first push landed"," ",c.jsx("code",{children:J.slice(0,10)}),". Your next commit will push to the remote too."]}):V?c.jsx(c.Fragment,{children:"Auto-push enabled. The initial push attempt failed — see Advanced settings → Status for the error."}):c.jsx(c.Fragment,{children:"Auto-push enabled. Your next commit will push to the remote."}):c.jsx(c.Fragment,{children:"Credentials wired. Auto-push remains off; flip it on in Advanced settings → Configuration to push commits automatically."})})()," ",c.jsx("button",{type:"button",className:"secondary",onClick:()=>B(null),style:{marginLeft:"0.5rem"},"aria-label":"Dismiss",children:"Dismiss"})]}):null,c.jsx("div",{className:"actions",children:q?c.jsx("button",{type:"button",className:"secondary",onClick:L,disabled:_,children:_?"Disconnecting…":"Disconnect"}):c.jsxs(c.Fragment,{children:[c.jsx("button",{type:"button",onClick:()=>v(!0),children:"Use Personal Access Token"}),c.jsx("button",{type:"button",className:"secondary",onClick:()=>E(!0),children:"Connect GitHub (one-click for GitHub users)"})]})}),y?c.jsx(i0,{vaultName:i,onClose:()=>E(!1),onConnected:(X,J)=>{r(X),J&&(B(J),h(J)),E(!1)}}):null,b?c.jsx(r0,{vaultName:i,onClose:()=>v(!1),onSaved:X=>{r(X),B(X),h(X),v(!1)}}):null]})}function i0({vaultName:i,onClose:s,onConnected:f}){const[r,h]=S.useState({kind:"starting"}),[m,y]=S.useState(Date.now()),E=S.useRef(!1);S.useEffect(()=>{const v=setInterval(()=>y(Date.now()),1e3);return()=>clearInterval(v)},[]),S.useEffect(()=>{let v=!1;return E.current=!1,Qg(i).then(_=>{v||h({kind:"polling",code:_,pollIntervalMs:Math.max(_.interval,1)*1e3,startedAt:Date.now()})}).catch(_=>{v||h({kind:"error",message:_ instanceof Error?_.message:String(_)})}),()=>{v=!0,E.current=!0}},[i]),S.useEffect(()=>{if(r.kind!=="polling")return;const v=r.code;let _=!1,g=null,R=r.pollIntervalMs;const k=async()=>{if(!(_||E.current))try{const D=await Vg(i,v.polling_id);if(_)return;if(D.state==="granted"){h({kind:"granted",user:{login:D.user.login}});return}if(D.state==="denied"){h({kind:"error",message:"Authorization denied."});return}if(D.state==="expired"){h({kind:"error",message:"The device code expired. Try again."});return}D.state==="slow_down"&&(R=D.interval*1e3),g=setTimeout(k,R)}catch(D){if(_)return;h({kind:"error",message:D instanceof Error?D.message:String(D)})}};return g=setTimeout(k,R),()=>{_=!0,g&&clearTimeout(g)}},[r,i]);const b=async v=>{try{await navigator.clipboard.writeText(v)}catch{}};return c.jsx("div",{className:"modal-backdrop",role:"dialog","aria-modal":"true",children:c.jsxs("div",{className:"modal",children:[c.jsxs("div",{className:"list-header",children:[c.jsx("h3",{style:{margin:0},children:"Connect GitHub"}),c.jsx("button",{type:"button",className:"secondary",onClick:s,children:"Close"})]}),r.kind==="starting"?c.jsx("p",{className:"muted",children:"Requesting device code from GitHub…"}):null,r.kind==="polling"?c.jsxs(c.Fragment,{children:[c.jsxs("p",{children:[c.jsx("strong",{children:"Step 1."})," Open"," ",c.jsx("a",{href:r.code.verification_uri,target:"_blank",rel:"noreferrer",children:r.code.verification_uri})," ","in your browser."]}),c.jsxs("p",{children:[c.jsx("strong",{children:"Step 2."})," Enter this code:"]}),c.jsxs("div",{className:"device-code-row",children:[c.jsx("code",{className:"device-code",children:r.code.user_code}),c.jsx("button",{type:"button",className:"secondary",onClick:()=>b(r.code.user_code),children:"Copy"})]}),c.jsxs("p",{className:"dim",children:["Waiting for authorization…"," ",c0(r.code.expires_in,r.startedAt,m)]})]}):null,r.kind==="granted"?c.jsx(s0,{vaultName:i,user:r.user,onPicked:async(v,_)=>{const g=await Jg(i,{owner:v,name:_}),R=await dm(i);f(R,g)},onError:v=>h({kind:"error",message:v})}):null,r.kind==="error"?c.jsxs(c.Fragment,{children:[c.jsx("div",{className:"error-banner",role:"alert",children:c.jsx("code",{children:r.message})}),c.jsxs("div",{className:"actions",children:[c.jsx("button",{type:"button",onClick:()=>h({kind:"starting"}),children:"Try again"}),c.jsx("button",{type:"button",className:"secondary",onClick:s,children:"Close"})]})]}):null]})})}function c0(i,s,f){const r=Math.floor((f-s)/1e3),h=Math.max(0,i-r),m=Math.floor(h/60),y=h%60;return`${m}:${String(y).padStart(2,"0")} remaining`}function s0({vaultName:i,user:s,onPicked:f,onError:r}){const[h,m]=S.useState(null),[y,E]=S.useState(!1),[b,v]=S.useState(""),[_,g]=S.useState(null),[R,k]=S.useState(!1),[D,B]=S.useState(""),[q,L]=S.useState(!1);S.useEffect(()=>{let Y=!1;return hm(i).then(({repos:K,truncated:G})=>{Y||(m(K),E(!!G))}).catch(K=>{Y||r(K instanceof Error?K.message:String(K))}),()=>{Y=!0}},[i,r]);const X=(h??[]).filter(Y=>b.length===0?!0:Y.full_name.toLowerCase().includes(b.toLowerCase())),J=async(Y,K)=>{g(`${Y}/${K}`);try{await f(Y,K)}catch(G){r(G instanceof Error?G.message:String(G))}finally{g(null)}},V=async()=>{if(D.trim().length!==0){k(!0);try{const Y=await Kg(i,{name:D.trim(),description:"Parachute Vault mirror",private:!0});await f(Y.owner,Y.name)}catch(Y){r(Y instanceof Error?Y.message:String(Y))}finally{k(!1)}}};return c.jsxs(c.Fragment,{children:[c.jsxs("p",{children:["Authorized as ",c.jsxs("code",{children:["@",s.login]}),". Pick a repository to push the mirror to:"]}),c.jsx("div",{className:"form-row",children:c.jsx("input",{type:"search",placeholder:"Filter by name…",value:b,onChange:Y=>v(Y.target.value)})}),y?c.jsx("p",{className:"muted",style:{fontSize:"0.85em"},children:"Showing the first 300 repos. Use the filter above to narrow down — or paste the clone URL directly via Personal Access Token below if your repo isn't here."}):null,h===null?c.jsx("p",{className:"muted",children:"Loading repos…"}):null,h!==null?c.jsxs("div",{className:"repo-list",children:[X.length===0&&b.length>0?c.jsxs("p",{className:"dim",children:['No repos match "',b,'".']}):null,X.length===0&&b.length===0&&h.length===0?c.jsx("p",{className:"dim",children:"You don't own any repos on this account yet."}):null,X.map(Y=>c.jsxs("button",{type:"button",className:"repo-row",onClick:()=>J(Y.owner,Y.name),disabled:_!==null,children:[c.jsxs("span",{children:[c.jsx("strong",{children:Y.name}),Y.private?c.jsx("span",{className:"dim",children:" · private"}):null]}),c.jsx("span",{className:"dim",children:Y.updated_at.slice(0,10)})]},Y.full_name))]}):null,q?c.jsxs("div",{className:"form-row",style:{marginTop:"0.75rem"},children:[c.jsx("label",{htmlFor:"new-repo-name",children:"New repo name"}),c.jsx("input",{id:"new-repo-name",type:"text",value:D,placeholder:"my-vault-backup",onChange:Y=>B(Y.target.value)}),c.jsxs("div",{className:"actions",children:[c.jsx("button",{type:"button",onClick:V,disabled:R||D.trim().length===0,children:R?"Creating…":"Create"}),c.jsx("button",{type:"button",className:"secondary",onClick:()=>L(!1),disabled:R,children:"Cancel"})]})]}):c.jsx("div",{className:"actions",style:{marginTop:"0.75rem"},children:c.jsx("button",{type:"button",className:"secondary",onClick:()=>L(!0),children:"+ Create new private repo"})})]})}function r0({vaultName:i,onClose:s,onSaved:f}){const[r,h]=S.useState(""),[m,y]=S.useState(""),[E,b]=S.useState(""),[v,_]=S.useState(!1),[g,R]=S.useState(null),k=async D=>{D.preventDefault(),_(!0),R(null);try{const B=await Zg(i,{token:r.trim(),remote_url:m.trim(),label:E.trim()||void 0});f(B)}catch(B){R(B instanceof Error?B.message:String(B))}finally{_(!1)}};return c.jsx("div",{className:"modal-backdrop",role:"dialog","aria-modal":"true",children:c.jsxs("div",{className:"modal",children:[c.jsxs("div",{className:"list-header",children:[c.jsx("h3",{style:{margin:0},children:"Use Personal Access Token"}),c.jsx("button",{type:"button",className:"secondary",onClick:s,disabled:v,children:"Close"})]}),c.jsxs("p",{className:"dim",children:["Works for any provider that supports HTTPS push with a token in the URL (GitHub, GitLab, Codeberg, Gitea, …). The token is stored with"," ",c.jsx("code",{children:"0600"})," file perms on this server."]}),c.jsxs("form",{onSubmit:k,children:[c.jsxs("div",{className:"form-row",children:[c.jsx("label",{htmlFor:"pat-token",children:"Token"}),c.jsx("input",{id:"pat-token",type:"password",value:r,autoComplete:"off",onChange:D=>h(D.target.value),placeholder:"ghp_… or glpat-… or similar",required:!0})]}),c.jsxs("div",{className:"form-row",children:[c.jsx("label",{htmlFor:"pat-url",children:"Remote URL"}),c.jsx("input",{id:"pat-url",type:"url",value:m,onChange:D=>y(D.target.value),placeholder:"https://github.com/owner/repo.git",required:!0}),c.jsxs("p",{className:"dim",style:{margin:"0.35rem 0 0"},children:["We'll embed your token in the URL before saving (using GitHub's"," ",c.jsx("code",{children:"x-access-token"})," convention). The URL you see on disk will carry the token; ensure the file isn't shared."]})]}),c.jsxs("div",{className:"form-row",children:[c.jsx("label",{htmlFor:"pat-label",children:"Label (optional)"}),c.jsx("input",{id:"pat-label",type:"text",value:E,onChange:D=>b(D.target.value),placeholder:"GitHub PAT for backup"})]}),g?c.jsx("div",{className:"error-banner",role:"alert",children:c.jsx("code",{children:g})}):null,c.jsxs("div",{className:"actions",children:[c.jsx("button",{type:"submit",disabled:v,children:v?"Validating…":"Validate & save"}),c.jsx("button",{type:"button",className:"secondary",onClick:s,disabled:v,children:"Cancel"})]})]})]})})}function o0({vaultName:i,creds:s}){const[f,r]=S.useState(""),[h,m]=S.useState("merge"),[y,E]=S.useState(!1),[b,v]=S.useState(""),[_,g]=S.useState(!0),[R,k]=S.useState({kind:"idle"}),[D,B]=S.useState(""),[q,L]=S.useState(!1),X=(s==null?void 0:s.active_method)==="github_oauth"&&s.github_oauth!==null,J=(s==null?void 0:s.active_method)!==null&&(s==null?void 0:s.active_method)!==void 0,V=h!=="replace"||D.trim()===i,Y=R.kind!=="running"&&f.trim().length>0&&V&&(!y||b.trim().length>0),K=async()=>{k({kind:"running",stage:"cloning"});let te;y?te={kind:"pat",token:b.trim()}:J?te=null:te={kind:"none"};try{const ye=window.setTimeout(()=>k(We=>We.kind==="running"?{kind:"running",stage:"importing"}:We),1500),tt=await $g(i,{remote_url:f.trim(),mode:h,credentials:te,enable_sync:_});window.clearTimeout(ye),k({kind:"success",result:tt,remoteUrl:f.trim(),mode:h})}catch(ye){const tt=ye instanceof _e?`${ye.status===409?"Already running. ":""}${ye.message}`:ye instanceof Error?ye.message:String(ye);k({kind:"error",message:tt})}},G=()=>{k({kind:"idle"}),B("")};return c.jsxs("div",{className:"section",children:[c.jsx("h3",{style:{margin:"0 0 0.85rem",fontSize:"1rem",fontWeight:500},children:"Import from a git repo"}),c.jsxs("p",{className:"dim",style:{marginTop:0},children:["Pull a vault state from a remote git repo into ",c.jsx("strong",{children:"this"})," vault. Use this to load a vault someone has been mirroring, or to sync a vault between machines you control. The remote must be a Parachute vault export (created by ",c.jsx("code",{children:"parachute-vault export"})," or the mirror's export flow)."]}),c.jsxs("div",{className:"warn-banner",style:{marginBottom:"1rem"},role:"note",children:[c.jsx("strong",{children:"One vault per remote."})," Multiple vaults pushing to the same git repo isn't a supported shape — the last push wins, and vaults that diverge silently overwrite each other. Today's working pattern is one vault per remote. Active two-way sync is a future direction; for now, do exports from one place and imports as snapshots elsewhere."]}),R.kind==="success"?c.jsx(f0,{vaultName:i,result:R.result,remoteUrl:R.remoteUrl,mode:R.mode,onReset:G}):c.jsxs(c.Fragment,{children:[c.jsxs("div",{className:"form-row",children:[c.jsx("label",{htmlFor:"import-remote-url",children:"Remote URL"}),c.jsx("input",{id:"import-remote-url",type:"text",value:f,placeholder:"https://github.com/aaron/my-vault.git",onChange:te=>r(te.target.value),disabled:R.kind==="running"}),X?c.jsxs("p",{className:"dim",style:{margin:"0.35rem 0 0",fontSize:"0.9em"},children:["Or"," ",c.jsx("button",{type:"button",onClick:()=>L(!0),disabled:R.kind==="running",style:{background:"none",border:"none",padding:0,color:"var(--accent)",textDecoration:"underline",cursor:"pointer",fontSize:"inherit"},children:"pick from your GitHub repos"}),"."]}):null,!J&&!y?c.jsx("p",{className:"hint",style:{marginTop:"0.35rem",fontSize:"0.85em"},children:"No saved git credentials. The import will be unauthenticated — works for public repos; private repos will fail with a clear error. Use the one-time credential toggle below if needed."}):null]}),c.jsxs("div",{className:"form-row",children:[c.jsx("label",{children:"Mode"}),c.jsxs("label",{className:"radio-row",children:[c.jsx("input",{type:"radio",name:"import-mode",value:"merge",checked:h==="merge",onChange:()=>{m("merge"),B("")},disabled:R.kind==="running"}),c.jsxs("span",{children:[c.jsx("strong",{children:"Merge"})," ",c.jsxs("span",{className:"dim",children:["— upsert by id. Notes in the remote get created/updated; any notes that exist only locally ",c.jsx("strong",{children:"survive"}),"."]})]})]}),c.jsxs("label",{className:"radio-row",children:[c.jsx("input",{type:"radio",name:"import-mode",value:"replace",checked:h==="replace",onChange:()=>m("replace"),disabled:R.kind==="running"}),c.jsxs("span",{children:[c.jsx("strong",{children:"Replace"})," ",c.jsxs("span",{className:"dim",children:["— wipe this vault first, then import. The remote becomes the new source of truth. ",c.jsx("strong",{children:"Destructive."})]})]})]})]}),h==="replace"?c.jsxs("div",{className:"form-row",children:[c.jsxs("div",{className:"error-banner",role:"alert",style:{marginBottom:"0.5rem"},children:[c.jsxs("strong",{children:['Replace will delete every note in vault "',i,'" before importing.']})," To confirm, type the vault name below:"]}),c.jsx("input",{id:"import-confirm-name",type:"text",value:D,placeholder:i,onChange:te=>B(te.target.value),disabled:R.kind==="running","aria-label":"Type vault name to confirm"})]}):null,c.jsxs("div",{className:"form-row",children:[c.jsxs("label",{children:[c.jsx("input",{type:"checkbox",checked:y,onChange:te=>E(te.target.checked),disabled:R.kind==="running",style:{width:"auto",marginRight:"0.5rem"}}),"One-time credential for this import only"]}),c.jsx("p",{className:"dim",style:{margin:"0.35rem 0 0",fontSize:"0.85em"},children:"Use a different Personal Access Token just for this clone, without changing your saved credentials."}),y?c.jsx("input",{id:"import-per-call-pat",type:"password",value:b,placeholder:"ghp_… or similar",onChange:te=>v(te.target.value),disabled:R.kind==="running",autoComplete:"off",style:{marginTop:"0.5rem"}}):null]}),c.jsxs("div",{className:"form-row",children:[c.jsxs("label",{children:[c.jsx("input",{type:"checkbox",checked:_,onChange:te=>g(te.target.checked),disabled:R.kind==="running",style:{width:"auto",marginRight:"0.5rem"}}),"Also sync changes back to this repo"]}),c.jsx("p",{className:"dim",style:{margin:"0.35rem 0 0",fontSize:"0.85em"},children:"Pushes future changes to this repo automatically. Uses the access you provide above."})]}),R.kind==="error"?c.jsx("div",{className:"error-banner",role:"alert",children:c.jsx("code",{children:R.message})}):null,c.jsx("div",{className:"actions",children:c.jsx("button",{type:"button",onClick:K,disabled:!Y,title:V?f.trim().length===0?"Provide a remote URL.":y&&b.trim().length===0?"Provide a token for one-time credential.":void 0:"Type the vault name to confirm Replace.",children:R.kind==="running"?R.stage==="cloning"?"Cloning…":"Importing…":"Start import"})})]}),q?c.jsx(d0,{vaultName:i,onClose:()=>L(!1),onPicked:te=>{r(te),L(!1)}}):null]})}function f0({vaultName:i,result:s,remoteUrl:f,mode:r,onReset:h}){return c.jsxs(c.Fragment,{children:[c.jsxs("div",{className:"mint-banner",role:"status",style:{marginBottom:"0.75rem"},children:[c.jsx("strong",{children:"Import succeeded."})," Imported ",s.notes_imported," note",s.notes_imported===1?"":"s",", ",s.tags_imported," tag schema",s.tags_imported===1?"":"s",", ",s.attachments_imported," ","attachment",s.attachments_imported===1?"":"s",r==="replace"&&s.notes_deleted!==void 0?`, wiped ${s.notes_deleted} pre-existing note${s.notes_deleted===1?"":"s"}`:"","."]}),s.warnings.length>0?c.jsxs("details",{style:{marginBottom:"0.75rem"},children:[c.jsxs("summary",{children:[s.warnings.length," warning",s.warnings.length===1?"":"s"," ","(see details)"]}),c.jsx("ul",{style:{marginTop:"0.5rem"},children:s.warnings.map((m,y)=>c.jsx("li",{children:c.jsx("code",{style:{fontSize:"0.85em"},children:m})},y))})]}):null,s.sync_enabled?c.jsxs("div",{className:"mint-banner",style:{marginBottom:"0.75rem"},role:"status",children:[c.jsx("strong",{children:"Sync enabled."})," Changes to vault ",c.jsx("code",{children:i})," ","now push back to ",c.jsx("code",{children:f})," automatically."]}):s.sync_warning?c.jsxs("div",{className:"info-banner",style:{marginBottom:"0.75rem"},role:"status",children:[c.jsxs("p",{style:{marginTop:0},children:[c.jsx("strong",{children:"Sync not enabled."})," ",s.sync_warning]}),c.jsxs("p",{className:"dim",style:{marginBottom:0,fontSize:"0.9em"},children:["You can still set up Sync from the"," ",c.jsx("button",{type:"button",onClick:()=>{var m;return(m=document.getElementById("git-remote-section"))==null?void 0:m.scrollIntoView({behavior:"smooth",block:"start"})},style:{background:"none",border:"none",padding:0,color:"var(--accent)",textDecoration:"underline",cursor:"pointer",fontSize:"inherit"},children:"Git remote section"})," ","above."]})]}):null,c.jsx("div",{className:"actions",children:c.jsx("button",{type:"button",className:"secondary",onClick:h,children:"Run another import"})})]})}function d0({vaultName:i,onClose:s,onPicked:f}){const[r,h]=S.useState(null),[m,y]=S.useState(!1),[E,b]=S.useState(""),[v,_]=S.useState(null);S.useEffect(()=>{let R=!1;return hm(i).then(({repos:k,truncated:D})=>{R||(h(k),y(!!D))}).catch(k=>{R||_(k instanceof Error?k.message:String(k))}),()=>{R=!0}},[i]);const g=(r??[]).filter(R=>E.length===0?!0:R.full_name.toLowerCase().includes(E.toLowerCase()));return c.jsx("div",{className:"modal-backdrop",role:"dialog","aria-modal":"true",children:c.jsxs("div",{className:"modal",children:[c.jsxs("div",{className:"list-header",children:[c.jsx("h3",{style:{margin:0},children:"Pick a repo to import from"}),c.jsx("button",{type:"button",className:"secondary",onClick:s,children:"Close"})]}),v?c.jsx("div",{className:"error-banner",role:"alert",children:c.jsx("code",{children:v})}):null,c.jsx("div",{className:"form-row",children:c.jsx("input",{type:"search",placeholder:"Filter by name…",value:E,onChange:R=>b(R.target.value)})}),m?c.jsx("p",{className:"muted",style:{fontSize:"0.85em"},children:"Showing the first 300 repos. Use the filter above to narrow down."}):null,r===null&&!v?c.jsx("p",{className:"muted",children:"Loading repos…"}):null,r!==null?c.jsxs("div",{className:"repo-list",children:[g.length===0&&E.length>0?c.jsxs("p",{className:"dim",children:['No repos match "',E,'".']}):null,g.length===0&&E.length===0&&r.length===0?c.jsx("p",{className:"dim",children:"You don't own any repos on this account."}):null,g.map(R=>c.jsxs("button",{type:"button",className:"repo-row",onClick:()=>f(R.clone_url),children:[c.jsxs("span",{children:[c.jsx("strong",{children:R.name}),R.private?c.jsx("span",{className:"dim",children:" · private"}):null]}),c.jsx("span",{className:"dim",children:R.updated_at.slice(0,10)})]},R.full_name))]}):null]})})}let Gn=null,Xn=null;function h0(i){const s=Date.parse(i);return Number.isNaN(s)?null:s}async function m0(){if(typeof window>"u")return{kind:"network-error",message:"no window"};let i;try{i=await fetch("/admin/host-admin-token",{method:"GET",headers:{accept:"application/json"},credentials:"same-origin"})}catch(f){return{kind:"network-error",message:f instanceof Error?f.message:String(f)}}if(i.status===403)return{kind:"forbidden"};if(i.status===401||i.status===404)return{kind:"auth-required",status:i.status};if(!i.ok)return{kind:"network-error",message:`hub returned ${i.status}`};let s;try{s=await i.json()}catch(f){return{kind:"network-error",message:f instanceof Error?f.message:"could not parse host-admin mint response"}}return typeof s.token!="string"||s.token.length===0?{kind:"network-error",message:"host-admin mint response missing token"}:(Gn=s.token,Xn=s.expires_at?h0(s.expires_at):null,{kind:"ok",token:s.token})}async function Lh(){if(Gn){if(Xn===null||Xn>Date.now())return{kind:"ok",token:Gn};Gn=null,Xn=null}return m0()}function v0(){Gn=null,Xn=null}class Ga extends Error{constructor(s,f,r){super(f),this.status=s,this.disposition=r,this.name="HostAdminError"}}async function fr(i,s={}){let f;const r=await Lh();if(r.kind!=="ok")throw kh(r);f=r.token;const{headers:h,...m}=s,y=v=>({accept:"application/json",...h??{},authorization:`Bearer ${v}`}),E=await fetch(i,{...m,headers:y(f)});if(E.status!==401)return E;v0();const b=await Lh();if(b.kind!=="ok")throw kh(b);return fetch(i,{...m,headers:y(b.token)})}function kh(i){return i.kind==="forbidden"?new Ga(403,"host-admin token mint forbidden — not the hub admin","forbidden"):i.kind==="auth-required"?new Ga(i.status,"no host-admin session — sign in to the hub to manage tokens","auth-required"):new Ga(0,i.message,"network-error")}function dr(){if(typeof window>"u")return;const i=ym();window.location.href=i?`${i}/account/`:"/account/"}const y0=[30,90,180,365],qh=90,p0=1440*60;function g0(i){if(!i)return null;const s=i.scoped_tags;if(!Array.isArray(s))return null;const f=s.filter(r=>typeof r=="string"&&r.length>0);return f.length>0?f:null}function b0(i){return{jti:i.jti,label:i.subject,scopes:Array.isArray(i.scopes)?i.scopes:[],scoped_tags:g0(i.permissions),expires_at:i.expires_at??null,revoked_at:i.revoked_at??null,created_at:i.created_at}}async function S0(i,s){const f={scope:`vault:${i}:${s.verb}`,expires_in:s.ttlDays*p0,subject:s.label};s.scopedTags.length>0&&(f.permissions={scoped_tags:s.scopedTags});const r=await fr("/api/auth/mint-token",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(f)});if(!r.ok)throw new _e(r.status,await xi(r));const h=await r.json();return{jti:h.jti,token:h.token,scope:h.scope,expires_at:h.expires_at??null}}async function x0(i){const s=`vault:${i}:`,f=[];let r=null;for(let h=0;h<1e3;h++){const m=new URLSearchParams({revoked:"all"});r&&m.set("cursor",r);const y=await fr(`/api/auth/tokens?${m.toString()}`);if(!y.ok)throw new _e(y.status,await xi(y));const E=await y.json(),b=Array.isArray(E.tokens)?E.tokens:[];if(f.push(...b),r=E.next_cursor??null,!r)break}return f.filter(h=>Array.isArray(h.scopes)&&h.scopes.some(m=>m.startsWith(s))).map(b0)}async function j0(i){const s=await fr("/api/auth/revoke-token",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({jti:i})});if(!s.ok)throw new _e(s.status,await xi(s))}async function E0(i){const s=await et(i,`/vault/${encodeURIComponent(i)}/api/tags`);if(!s.ok)throw new _e(s.status,await xi(s));const f=await s.json();return Array.isArray(f)?f:[]}async function xi(i){try{const s=await i.text(),f=JSON.parse(s);if(f.error_description)return f.error_description;if(f.message)return f.message;if(f.error)return f.error;if(s)return s}catch{}return`${i.status} ${i.statusText}`}const T0=["read","write","admin"],_0={30:"30 days",90:"90 days",180:"180 days",365:"1 year (max)"};function A0(i){return i instanceof Ga?i.disposition==="forbidden"?(dr(),{kind:"auth-required",status:403}):i.disposition==="auth-required"?{kind:"auth-required",status:i.status}:{kind:"error",message:i.message}:i instanceof _e&&(i.status===401||i.status===403)?{kind:"auth-required",status:i.status}:{kind:"error",message:i instanceof Error?i.message:String(i)}}function Yh({vaultName:i}={}){const s=lr(),f=i??s.name,r=i!==void 0,h=r?"/":`/vault/${encodeURIComponent(f??"")}`,[m,y]=S.useState({kind:"loading"}),[E,b]=S.useState(null),[v,_]=S.useState(null),[g,R]=S.useState(0);S.useEffect(()=>{let B=!1;if(f)return y({kind:"loading"}),x0(f).then(q=>{B||y({kind:"ok",tokens:q})}).catch(q=>{B||y(A0(q))}),()=>{B=!0}},[f,g]);const k=S.useCallback(()=>R(B=>B+1),[]);if(S.useEffect(()=>{if(!E)return;const B=q=>{q.preventDefault(),q.returnValue=""};return window.addEventListener("beforeunload",B),()=>window.removeEventListener("beforeunload",B)},[E]),!f)return c.jsxs("div",{children:[c.jsx("h2",{children:"Tokens"}),c.jsx("p",{className:"muted",children:"Missing vault name."}),r?null:c.jsx(Je,{to:"/",children:"← Back to vaults"})]});const D=vm(f);return c.jsxs("div",{children:[c.jsxs("div",{className:"list-header",children:[c.jsxs("h2",{children:["Tokens for ",c.jsx("code",{children:f})]}),c.jsx(Je,{to:h,className:"muted",children:"← Vault detail"})]}),D?null:c.jsxs("div",{className:"warn-banner",children:["You're viewing this page with a read-only token. Mint and revoke require"," ",c.jsxs("code",{children:["vault:",f,":admin"]}),`. Re-enter from the hub directory's "Manage" link with an admin-scoped session to manage tokens.`]}),E?c.jsx(N0,{result:E,onDismiss:()=>b(null)}):null,m.kind==="ok"&&D&&!E?c.jsx(R0,{vaultName:f,onMinted:B=>{b(B),R(q=>q+1)}}):null,c.jsxs("div",{className:"section",children:[c.jsx("h3",{style:{margin:"0 0 0.85rem",fontSize:"1rem",fontWeight:500},children:"Existing tokens"}),m.kind==="loading"?c.jsx("p",{className:"muted",children:"Loading…"}):null,m.kind==="auth-required"?c.jsx(or,{vaultName:f,status:m.status,onRecovered:k}):null,m.kind==="error"?c.jsx("div",{className:"error-banner",children:c.jsx("code",{children:m.message})}):null,m.kind==="ok"?c.jsx(z0,{tokens:m.tokens,allowRevoke:D,confirmingJti:v,onAskConfirm:_,onRevoked:()=>{_(null),R(B=>B+1)}}):null]})]})}function N0({result:i,onDismiss:s}){const[f,r]=S.useState(!1),h=async()=>{try{await navigator.clipboard.writeText(i.token),r(!0),setTimeout(()=>r(!1),2e3)}catch{}};return c.jsxs("div",{className:"mint-banner",children:[c.jsx("h3",{children:"New token (shown once)"}),c.jsx("p",{className:"muted",children:"This is the only time the vault will show this token. Copy it now and store it somewhere safe — a password manager, the operator's notes, paraclaw's secrets store. If you lose it, revoke it and mint a new one."}),c.jsxs("div",{className:"token-box",children:[c.jsx("code",{children:i.token}),c.jsx("button",{type:"button",onClick:h,className:"secondary",children:f?"Copied":"Copy"})]}),c.jsx("p",{className:"warn",children:"⚠ Don't dismiss this banner until you've saved the token."}),c.jsx("div",{className:"actions",children:c.jsx("button",{type:"button",onClick:s,children:"Done — I've saved the token"})})]})}function R0({vaultName:i,onMinted:s}){const[f,r]=S.useState(""),[h,m]=S.useState("admin"),[y,E]=S.useState(qh),[b,v]=S.useState(!1),[_,g]=S.useState(null),[R,k]=S.useState([]),[D,B]=S.useState(null),[q,L]=S.useState(new Set);S.useEffect(()=>{let K=!1;return E0(i).then(G=>{if(K)return;const te=G.map(ye=>ye.name).filter(ye=>!ye.includes("/")).sort();k(te)}).catch(G=>{K||B(G instanceof Error?G.message:String(G))}),()=>{K=!0}},[i]);const X=f.trim(),J=X.length===0,V=K=>{L(G=>{const te=new Set(G);return te.has(K)?te.delete(K):te.add(K),te})},Y=async K=>{if(K.preventDefault(),J){g("label required — give the token a recognizable name");return}v(!0),g(null);try{const G=await S0(i,{verb:h,label:X,scopedTags:[...q],ttlDays:y});s(G),r(""),m("admin"),E(qh),L(new Set)}catch(G){if(G instanceof Ga&&G.disposition==="forbidden"){dr();return}g(G instanceof Error?G.message:String(G))}finally{v(!1)}};return c.jsxs("form",{onSubmit:Y,className:"section",children:[c.jsx("h3",{style:{margin:"0 0 0.85rem",fontSize:"1rem",fontWeight:500},children:"Mint a token"}),c.jsx("p",{className:"dim",style:{margin:"0 0 0.85rem"},children:"Issues a hub-signed token (JWT) scoped to this vault. Store it like a password — it's shown once and can't be re-displayed."}),_?c.jsx("div",{className:"error-banner",children:c.jsx("code",{children:_})}):null,c.jsxs("div",{className:"form-row",children:[c.jsxs("label",{htmlFor:"mint-label",children:["Label ",c.jsx("span",{className:"dim",children:"(required)"})]}),c.jsx("input",{id:"mint-label",type:"text",value:f,onChange:K=>r(K.target.value),placeholder:"e.g. ci, paraclaw-prod, my-laptop",maxLength:120,required:!0})]}),c.jsxs("div",{className:"form-row",children:[c.jsx("label",{htmlFor:"mint-scope",children:"Scope"}),c.jsx("select",{id:"mint-scope",value:h,onChange:K=>m(K.target.value),children:T0.map(K=>c.jsx("option",{value:K,children:K==="read"?"read — query notes only":K==="write"?"write — query + create/update notes":"admin — full control (incl. token mgmt)"},K))}),c.jsxs("p",{className:"dim",style:{margin:"0.35rem 0 0"},children:["Issued as ",c.jsxs("code",{children:["vault:",i,":",h]}),". Lower scopes inherit narrower powers."]})]}),c.jsxs("div",{className:"form-row",children:[c.jsx("label",{htmlFor:"mint-ttl",children:"Expires after"}),c.jsx("select",{id:"mint-ttl",value:y,onChange:K=>E(Number(K.target.value)),children:y0.map(K=>c.jsx("option",{value:K,children:_0[K]??`${K} days`},K))}),c.jsx("p",{className:"dim",style:{margin:"0.35rem 0 0"},children:"The token stops working after this. Rotate before it lapses."})]}),c.jsxs("div",{className:"form-row",children:[c.jsxs("label",{children:["Tag scope ",c.jsx("span",{className:"dim",children:"(optional)"})]}),D?c.jsxs("p",{className:"dim",style:{margin:"0.35rem 0 0"},children:["Couldn't load tags: ",c.jsx("code",{children:D}),". Token will be unscoped."]}):R.length===0?c.jsx("p",{className:"dim",style:{margin:"0.35rem 0 0"},children:"No root tags in this vault yet — token will see the full vault."}):c.jsxs(c.Fragment,{children:[c.jsx("div",{className:"tag-picker",children:R.map(K=>c.jsxs("label",{className:"tag-checkbox",children:[c.jsx("input",{type:"checkbox",checked:q.has(K),onChange:()=>V(K)}),c.jsx("code",{children:K})]},K))}),c.jsx("p",{className:"dim",style:{margin:"0.35rem 0 0"},children:q.size===0?"Nothing selected — token sees the full vault (unscoped).":`Token limited to: ${[...q].join(", ")}. Sub-tags (e.g. health/food) are reachable when their root is selected.`})]})]}),c.jsx("div",{className:"actions",children:c.jsx("button",{type:"submit",disabled:b||J,children:b?"Minting…":"Mint token"})})]})}function z0({tokens:i,allowRevoke:s,confirmingJti:f,onAskConfirm:r,onRevoked:h}){return i.length===0?c.jsx("p",{className:"muted",children:"No tokens yet. Mint one above to get started."}):c.jsx("div",{className:"token-list",children:i.map(m=>c.jsx(C0,{token:m,allowRevoke:s,confirming:f===m.jti,onAskConfirm:r,onRevoked:h},m.jti))})}function C0({token:i,allowRevoke:s,confirming:f,onAskConfirm:r,onRevoked:h}){const[m,y]=S.useState(!1),[E,b]=S.useState(null),v=i.revoked_at!==null,_=async()=>{y(!0),b(null);try{await j0(i.jti),h()}catch(g){if(g instanceof Ga&&g.disposition==="forbidden"){dr();return}b(g instanceof Error?g.message:String(g)),y(!1)}};return c.jsxs("div",{className:"token-row",children:[c.jsxs("div",{className:"body",children:[c.jsxs("div",{className:"name",children:[c.jsx("strong",{children:i.label??"(unlabeled)"}),c.jsx("code",{className:"dim",children:i.jti}),v?c.jsx("code",{className:"scope-tag",title:"This token has been revoked and no longer works.",children:"revoked"}):null]}),c.jsxs("div",{className:"meta",children:[c.jsx("span",{className:"dim",children:"scopes:"})," ",i.scopes.length>0?i.scopes.map(g=>c.jsx("code",{className:"scope-tag",children:g},g)):c.jsx("span",{className:"dim",children:"(none)"})]}),i.scoped_tags&&i.scoped_tags.length>0?c.jsxs("div",{className:"meta",children:[c.jsx("span",{className:"dim",children:"tags:"})," ",i.scoped_tags.map(g=>c.jsxs("code",{className:"scope-tag tag-pill",children:["#",g]},g))]}):null,c.jsxs("div",{className:"meta dim",children:["created ",i.created_at,i.expires_at?` · expires ${i.expires_at}`:"",v&&i.revoked_at?` · revoked ${i.revoked_at}`:""]}),E?c.jsx("div",{className:"error-banner",style:{marginTop:"0.5rem"},children:c.jsx("code",{children:E})}):null]}),s&&!v?f?c.jsxs("div",{className:"actions",children:[c.jsx("button",{type:"button",onClick:_,disabled:m,children:m?"Revoking…":"Confirm revoke"}),c.jsx("button",{type:"button",className:"secondary",onClick:()=>r(null),disabled:m,children:"Cancel"})]}):c.jsx("button",{type:"button",className:"secondary",onClick:()=>r(i.jti),children:"Revoke"}):null]})}function w0(){const[i,s]=S.useState({kind:"loading"});return S.useEffect(()=>{let f=!1;return Bg().then(r=>{f||s({kind:"ok",vaults:r})}).catch(r=>{if(f)return;const h=r instanceof _e?`${r.status}: ${r.message}`:r instanceof Error?r.message:String(r);s({kind:"error",message:h})}),()=>{f=!0}},[]),i.kind==="loading"?c.jsxs("div",{children:[c.jsx("h2",{children:"Vaults"}),c.jsx("p",{className:"muted",children:"Loading…"})]}):i.kind==="error"?c.jsxs("div",{children:[c.jsx("h2",{children:"Vaults"}),c.jsx("div",{className:"error-banner",children:c.jsx("code",{children:i.message})})]}):i.vaults.length===0?c.jsxs("div",{children:[c.jsx("h2",{children:"Vaults"}),c.jsxs("div",{className:"empty",children:["No vaults yet. Run ",c.jsx("code",{children:"parachute-vault create <name>"})," on the host to create one."]})]}):c.jsxs("div",{children:[c.jsx("div",{className:"list-header",children:c.jsx("h2",{children:"Vaults"})}),i.vaults.map(f=>c.jsxs(Je,{to:`/vault/${f}`,className:"vault-row",children:[c.jsx("div",{className:"body",children:c.jsx("div",{className:"name",children:c.jsx("code",{children:f})})}),c.jsx("div",{className:"chev",children:"→"})]},f))]})}function M0(){const i=om();return c.jsxs("div",{className:"page",children:[c.jsxs("nav",{className:"nav",children:[c.jsxs(Je,{to:"/",className:"brand",children:["Parachute Vault ",c.jsx("span",{className:"sub",children:"admin"})]}),i?c.jsx(Je,{to:"/",children:c.jsx("code",{children:i})}):c.jsx(Je,{to:"/",children:"Vaults"})]}),i?c.jsxs(Mh,{children:[c.jsx(Yt,{path:"/",element:c.jsx(Hh,{vaultName:i})}),c.jsx(Yt,{path:"/tokens",element:c.jsx(Yh,{vaultName:i})}),c.jsx(Yt,{path:"/mirror",element:c.jsx(Bh,{vaultName:i})}),c.jsx(Yt,{path:"*",element:c.jsx(Gh,{})})]}):c.jsxs(Mh,{children:[c.jsx(Yt,{path:"/",element:c.jsx(w0,{})}),c.jsx(Yt,{path:"/vault/:name",element:c.jsx(Hh,{})}),c.jsx(Yt,{path:"/vault/:name/tokens",element:c.jsx(Yh,{})}),c.jsx(Yt,{path:"/vault/:name/mirror",element:c.jsx(Bh,{})}),c.jsx(Yt,{path:"*",element:c.jsx(Gh,{})})]})]})}function Gh(){return c.jsxs("div",{className:"empty",children:["404 — back to ",c.jsx(Je,{to:"/",children:"vault"}),"."]})}wg();const pm=document.getElementById("root");if(!pm)throw new Error("#root not found");function $s(){Fy.createRoot(pm).render(c.jsx(S.StrictMode,{children:c.jsx(pg,{basename:Ng(),children:c.jsx(M0,{})})}))}const Xh=om();Xh?Dg(Xh).then($s,$s):$s();
@@ -5,7 +5,7 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>Parachute Vault — Admin</title>
7
7
  <meta name="description" content="Manage vaults hosted by this Parachute Vault server." />
8
- <script type="module" crossorigin src="./assets/index-D8nCVT1e.js"></script>
8
+ <script type="module" crossorigin src="./assets/index-DJL6Az--.js"></script>
9
9
  <link rel="stylesheet" crossorigin href="./assets/index-DBe8Xiah.css">
10
10
  </head>
11
11
  <body>