@drawnagency/primitives 0.1.100 → 0.1.102

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/{chunk-GOCFTXR3.js → chunk-6SLGKRLW.js} +1 -1
  2. package/dist/{chunk-72FMQQIA.js → chunk-P3NNI3QI.js} +99 -18
  3. package/dist/components/docfile/DocFilePane.d.ts.map +1 -1
  4. package/dist/components/docfile/doc-file-state.d.ts +5 -0
  5. package/dist/components/docfile/doc-file-state.d.ts.map +1 -1
  6. package/dist/components/primitives/DocumentDropZone.d.ts +18 -0
  7. package/dist/components/primitives/DocumentDropZone.d.ts.map +1 -0
  8. package/dist/components/sections/Document/index.d.ts +7 -1
  9. package/dist/components/sections/Document/index.d.ts.map +1 -1
  10. package/dist/components/sections/brand-guide.js +1 -1
  11. package/dist/components/sections/register-schemas.js +2 -2
  12. package/dist/components/sections/register.js +2 -2
  13. package/dist/components/shell/EditorShell.d.ts.map +1 -1
  14. package/dist/components/shell/MediaLibraryContext.d.ts +6 -0
  15. package/dist/components/shell/MediaLibraryContext.d.ts.map +1 -1
  16. package/dist/hooks/useDocumentUploads.d.ts.map +1 -1
  17. package/dist/hooks/useMediaPipeline.d.ts.map +1 -1
  18. package/dist/lib/dexie.d.ts +3 -1
  19. package/dist/lib/dexie.d.ts.map +1 -1
  20. package/dist/lib/dexie.js +7 -2
  21. package/dist/storage/types.d.ts +14 -5
  22. package/dist/storage/types.d.ts.map +1 -1
  23. package/package.json +1 -1
  24. package/src/components/docfile/DocFilePane.tsx +8 -1
  25. package/src/components/docfile/doc-file-state.ts +31 -3
  26. package/src/components/primitives/DocumentDropZone.tsx +79 -0
  27. package/src/components/sections/Document/index.tsx +32 -1
  28. package/src/components/shell/EditorShell.tsx +21 -1
  29. package/src/components/shell/MediaLibraryContext.tsx +6 -0
  30. package/src/hooks/useDocumentUploads.ts +7 -1
  31. package/src/hooks/useMediaPipeline.ts +6 -1
  32. package/src/lib/dexie.ts +13 -2
  33. package/src/storage/types.ts +14 -6
@@ -9,7 +9,7 @@ import {
9
9
  stripHtmlToPlainText,
10
10
  trimTrailingSpacers,
11
11
  truncate
12
- } from "./chunk-72FMQQIA.js";
12
+ } from "./chunk-P3NNI3QI.js";
13
13
  import {
14
14
  DeleteButton,
15
15
  DragHandle,
@@ -14,12 +14,16 @@ import {
14
14
  AddIcon,
15
15
  DeleteIcon,
16
16
  DragHandle,
17
- IconButton
17
+ IconButton,
18
+ useMediaLibrary
18
19
  } from "./chunk-26ALJO4K.js";
19
20
  import {
20
21
  DEFAULT_LINK,
21
22
  LinkValueSchema
22
23
  } from "./chunk-PZJL2S3Z.js";
24
+ import {
25
+ DOCUMENT_MIME_TO_EXT
26
+ } from "./chunk-BS5AQHZG.js";
23
27
  import {
24
28
  curatedIcons,
25
29
  getIcon
@@ -1299,10 +1303,61 @@ var DoDontMedia_default = defineSection({
1299
1303
  });
1300
1304
 
1301
1305
  // src/components/sections/Document/index.tsx
1302
- import { useEffect as useEffect5, useState as useState6 } from "react";
1306
+ import { useEffect as useEffect5, useState as useState7 } from "react";
1303
1307
  import { z as z5 } from "zod";
1304
1308
  import { ExternalLink, FileText } from "lucide-react";
1305
1309
 
1310
+ // src/components/primitives/DocumentDropZone.tsx
1311
+ import { useState as useState6 } from "react";
1312
+ import { FileUp } from "lucide-react";
1313
+ import { jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
1314
+ var isDocumentFile = (file) => file.type in DOCUMENT_MIME_TO_EXT;
1315
+ function DocumentDropZone({ onDocumentSelected, className }) {
1316
+ const mediaLibrary = useMediaLibrary();
1317
+ const [dragging, setDragging] = useState6(false);
1318
+ const hasFiles = (e) => e.dataTransfer.types.includes("Files");
1319
+ const handleDragOver = (e) => {
1320
+ if (!hasFiles(e)) return;
1321
+ e.preventDefault();
1322
+ e.dataTransfer.dropEffect = "copy";
1323
+ setDragging(true);
1324
+ };
1325
+ const handleDragLeave = (e) => {
1326
+ if (!hasFiles(e)) return;
1327
+ const related = e.relatedTarget;
1328
+ if (related && e.currentTarget.contains(related)) return;
1329
+ setDragging(false);
1330
+ };
1331
+ const handleDrop = (e) => {
1332
+ if (!hasFiles(e)) return;
1333
+ e.preventDefault();
1334
+ e.stopPropagation();
1335
+ setDragging(false);
1336
+ const files = Array.from(e.dataTransfer.files);
1337
+ if (files.length === 0) return;
1338
+ void mediaLibrary?.uploadDocument?.(files.find(isDocumentFile) ?? files[0], onDocumentSelected);
1339
+ };
1340
+ return /* @__PURE__ */ jsxs9(
1341
+ "button",
1342
+ {
1343
+ type: "button",
1344
+ onClick: () => mediaLibrary?.openSelectModal(onDocumentSelected, { kinds: ["document"] }),
1345
+ onDragOver: handleDragOver,
1346
+ onDragLeave: handleDragLeave,
1347
+ onDrop: handleDrop,
1348
+ className: cn(
1349
+ "flex w-full cursor-pointer items-center justify-center gap-2 rounded-outer border-2 border-dashed border-base-200 bg-base px-4 py-6 text-sm text-base-contrast-light transition-colors hover:border-base-300 hover:bg-base-accent hover:text-base-contrast",
1350
+ dragging && "border-primary bg-primary/5 text-base-contrast",
1351
+ className
1352
+ ),
1353
+ children: [
1354
+ /* @__PURE__ */ jsx13(FileUp, { size: 18, "aria-hidden": "true", className: "shrink-0" }),
1355
+ /* @__PURE__ */ jsx13("span", { children: "Drop a PDF or HTML file, or choose from the library" })
1356
+ ]
1357
+ }
1358
+ );
1359
+ }
1360
+
1306
1361
  // src/lib/container-ops.ts
1307
1362
  function getContainerChildren(block) {
1308
1363
  const content = block.content;
@@ -1377,6 +1432,8 @@ function formatDocSize(bytes) {
1377
1432
 
1378
1433
  // src/components/docfile/doc-file-state.ts
1379
1434
  var DOC_OPEN_CLASS = "bp-doc-open";
1435
+ var DOC_CLOSING_CLASS = "bp-doc-closing";
1436
+ var DOC_CLOSING_MS = 350;
1380
1437
  var DOC_DESKTOP_QUERY = "(min-width: 1024px)";
1381
1438
  var docFileOpenEvent = createEvent("bp-doc-file-open");
1382
1439
  var STORE_KEY = /* @__PURE__ */ Symbol.for("@drawnagency/primitives/doc-file-state");
@@ -1384,7 +1441,7 @@ var g = globalThis;
1384
1441
  function store() {
1385
1442
  let s = g[STORE_KEY];
1386
1443
  if (!s) {
1387
- s = { open: null, docs: null, openListeners: /* @__PURE__ */ new Set(), docsListeners: /* @__PURE__ */ new Set(), teardown: null };
1444
+ s = { open: null, docs: null, openListeners: /* @__PURE__ */ new Set(), docsListeners: /* @__PURE__ */ new Set(), teardown: null, closingTimer: null };
1388
1445
  g[STORE_KEY] = s;
1389
1446
  }
1390
1447
  return s;
@@ -1396,9 +1453,23 @@ function isDocDesktop() {
1396
1453
  function apply(sectionId) {
1397
1454
  const s = store();
1398
1455
  if (s.open === sectionId) return;
1456
+ const wasOpen = s.open !== null;
1399
1457
  s.open = sectionId;
1400
1458
  if (typeof document !== "undefined") {
1401
- document.documentElement.classList.toggle(DOC_OPEN_CLASS, sectionId !== null);
1459
+ const root = document.documentElement;
1460
+ root.classList.toggle(DOC_OPEN_CLASS, sectionId !== null);
1461
+ if (s.closingTimer !== null) {
1462
+ clearTimeout(s.closingTimer);
1463
+ s.closingTimer = null;
1464
+ root.classList.remove(DOC_CLOSING_CLASS);
1465
+ }
1466
+ if (sectionId === null && wasOpen) {
1467
+ root.classList.add(DOC_CLOSING_CLASS);
1468
+ s.closingTimer = setTimeout(() => {
1469
+ root.classList.remove(DOC_CLOSING_CLASS);
1470
+ s.closingTimer = null;
1471
+ }, DOC_CLOSING_MS);
1472
+ }
1402
1473
  }
1403
1474
  for (const listener of [...s.openListeners]) listener(sectionId);
1404
1475
  }
@@ -1424,7 +1495,7 @@ function subscribeSiteDocuments(cb) {
1424
1495
  }
1425
1496
 
1426
1497
  // src/components/sections/Document/index.tsx
1427
- import { jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
1498
+ import { jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
1428
1499
  var schema4 = z5.object({
1429
1500
  type: z5.literal("document"),
1430
1501
  content: z5.object({
@@ -1441,7 +1512,7 @@ var schema4 = z5.object({
1441
1512
  options: z5.object({}).default({})
1442
1513
  });
1443
1514
  function useSiteDocument(sectionId) {
1444
- const [docs, setDocs] = useState6(null);
1515
+ const [docs, setDocs] = useState7(null);
1445
1516
  useEffect5(() => {
1446
1517
  setDocs(getSiteDocuments());
1447
1518
  return subscribeSiteDocuments(setDocs);
@@ -1454,10 +1525,15 @@ function useSiteDocument(sectionId) {
1454
1525
  function DocumentRow({
1455
1526
  sectionId,
1456
1527
  title,
1457
- isEditMode
1528
+ documentId = "",
1529
+ isEditMode,
1530
+ onDocumentSelected
1458
1531
  }) {
1459
1532
  const { doc, resolved } = useSiteDocument(sectionId);
1460
1533
  if (resolved && !doc && !isEditMode) return null;
1534
+ if (isEditMode && !documentId && onDocumentSelected) {
1535
+ return /* @__PURE__ */ jsx14(DocumentDropZone, { onDocumentSelected });
1536
+ }
1461
1537
  const label = doc?.title ?? title;
1462
1538
  function handleClick() {
1463
1539
  if (!doc) return;
@@ -1467,7 +1543,7 @@ function DocumentRow({
1467
1543
  }
1468
1544
  openDocFile(sectionId);
1469
1545
  }
1470
- return /* @__PURE__ */ jsxs9(
1546
+ return /* @__PURE__ */ jsxs10(
1471
1547
  "button",
1472
1548
  {
1473
1549
  type: "button",
@@ -1479,15 +1555,15 @@ function DocumentRow({
1479
1555
  doc ? "cursor-pointer hover:bg-base-accent" : "cursor-default opacity-60"
1480
1556
  ),
1481
1557
  children: [
1482
- /* @__PURE__ */ jsx13(FileText, { size: 18, "aria-hidden": "true", className: "shrink-0 text-base-contrast/60" }),
1483
- /* @__PURE__ */ jsxs9("div", { className: "min-w-0 flex-1", children: [
1484
- /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-2", children: [
1485
- /* @__PURE__ */ jsx13("span", { className: "truncate text-sm font-medium text-base-contrast", children: label }),
1486
- doc && /* @__PURE__ */ jsx13("span", { className: "shrink-0 rounded bg-base-100 px-1.5 py-0.5 text-xs font-medium text-base-contrast", children: doc.kind === "pdf" ? "PDF" : "HTML" })
1558
+ /* @__PURE__ */ jsx14(FileText, { size: 18, "aria-hidden": "true", className: "shrink-0 text-base-contrast/60" }),
1559
+ /* @__PURE__ */ jsxs10("div", { className: "min-w-0 flex-1", children: [
1560
+ /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-2", children: [
1561
+ /* @__PURE__ */ jsx14("span", { className: "truncate text-sm font-medium text-base-contrast", children: label }),
1562
+ doc && /* @__PURE__ */ jsx14("span", { className: "shrink-0 rounded bg-base-100 px-1.5 py-0.5 text-xs font-medium text-base-contrast", children: doc.kind === "pdf" ? "PDF" : "HTML" })
1487
1563
  ] }),
1488
- /* @__PURE__ */ jsx13("div", { className: "truncate text-xs text-base-contrast/70", children: doc ? `${doc.filename} \xB7 ${formatDocSize(doc.size)}` : resolved ? "File missing" : "\xA0" })
1564
+ /* @__PURE__ */ jsx14("div", { className: "truncate text-xs text-base-contrast/70", children: doc ? `${doc.filename} \xB7 ${formatDocSize(doc.size)}` : resolved ? "File missing" : "\xA0" })
1489
1565
  ] }),
1490
- doc && /* @__PURE__ */ jsx13(ExternalLink, { size: 16, "aria-hidden": "true", className: "shrink-0 text-base-contrast/50" })
1566
+ doc && /* @__PURE__ */ jsx14(ExternalLink, { size: 16, "aria-hidden": "true", className: "shrink-0 text-base-contrast/50" })
1491
1567
  ]
1492
1568
  }
1493
1569
  );
@@ -1496,18 +1572,23 @@ var Document_default = defineSection({
1496
1572
  type: "document",
1497
1573
  category: "brand-guide",
1498
1574
  label: "Document",
1499
- icon: /* @__PURE__ */ jsx13(FileText, { size: 18 }),
1575
+ icon: /* @__PURE__ */ jsx14(FileText, { size: 18 }),
1500
1576
  schema: schema4,
1501
1577
  // The row reads the client-side document registry and opens the overlay, so
1502
1578
  // the viewer hydrates it as an island rather than emitting static HTML.
1503
1579
  interactive: true,
1504
1580
  authoringNote: "One downloadable/viewable file \u2014 a PDF or a single self-contained HTML file \u2014 presented as a clickable row. Clicking opens it in a full-viewport preview overlay without leaving the page; on phones a PDF opens in the OS viewer instead. Pick the file from the media library (documents tab); leave the title blank to show the file name without its extension, or type one to override it. Leave 'Show in Documents menu' on for anything a viewer should be able to reach from any page \u2014 those entries collect into a derived Documents group at the bottom of the left nav. Use one section per file; for a set of downloads, place several document sections in a container.",
1505
- component: ({ content, isEditMode }) => /* @__PURE__ */ jsx13(
1581
+ component: ({ content, isEditMode, onChange }) => /* @__PURE__ */ jsx14(
1506
1582
  DocumentRow,
1507
1583
  {
1508
1584
  sectionId: content.id ?? "",
1509
1585
  title: content.content.title,
1510
- isEditMode
1586
+ documentId: content.content.doc?.documentId ?? "",
1587
+ isEditMode,
1588
+ onDocumentSelected: onChange ? (documentId) => onChange({
1589
+ ...content,
1590
+ content: { ...content.content, doc: { ...content.content.doc, documentId } }
1591
+ }) : void 0
1511
1592
  }
1512
1593
  ),
1513
1594
  defaults: () => ({
@@ -1 +1 @@
1
- {"version":3,"file":"DocFilePane.d.ts","sourceRoot":"","sources":["../../../src/components/docfile/DocFilePane.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAOxD;;;;;;;;;GASG;AACH,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,EAClC,GAAG,EACH,OAAO,EACP,MAAc,GACf,EAAE;IAAE,GAAG,EAAE,YAAY,CAAC;IAAC,OAAO,EAAE,MAAM,IAAI,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,2CAyG9D"}
1
+ {"version":3,"file":"DocFilePane.d.ts","sourceRoot":"","sources":["../../../src/components/docfile/DocFilePane.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAOxD;;;;;;;;;GASG;AACH,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,EAClC,GAAG,EACH,OAAO,EACP,MAAc,GACf,EAAE;IAAE,GAAG,EAAE,YAAY,CAAC;IAAC,OAAO,EAAE,MAAM,IAAI,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,2CAgH9D"}
@@ -1,6 +1,11 @@
1
1
  import type { SiteDocument } from "../../lib/documents";
2
2
  /** Toggled on <html> while a document is open (the chat's class mechanism). */
3
3
  export declare const DOC_OPEN_CLASS = "bp-doc-open";
4
+ /** Held on <html> for a beat after close. bp-doc-open's `transition: left`
5
+ * rule leaves WITH the class, so without this the nav sidebar snaps home
6
+ * instead of gliding back the way it glided out — this class carries the
7
+ * same transition while the sidebar makes the return trip. */
8
+ export declare const DOC_CLOSING_CLASS = "bp-doc-closing";
4
9
  /** The one breakpoint the doc feature keys off: at or above it the overlay
5
10
  * gets the desktop stage; below it a PDF goes to the OS viewer instead (iOS
6
11
  * Safari cannot scroll an iframed PDF, Android Chrome forces a download).
@@ -1 +1 @@
1
- {"version":3,"file":"doc-file-state.d.ts","sourceRoot":"","sources":["../../../src/components/docfile/doc-file-state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGxD,+EAA+E;AAC/E,eAAO,MAAM,cAAc,gBAAgB,CAAC;AAE5C;;;;;;;mBAOmB;AACnB,eAAO,MAAM,iBAAiB,wBAAwB,CAAC;AAEvD;;4CAE4C;AAC5C,eAAO,MAAM,gBAAgB;eAA4B,MAAM,GAAG,IAAI;EAAuB,CAAC;AA2B9F,wBAAgB,YAAY,IAAI,OAAO,CAGtC;AAmCD,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAWnD;AAED,wBAAgB,YAAY,IAAI,IAAI,CAUnC;AAED,wBAAgB,cAAc,IAAI,MAAM,GAAG,IAAI,CAE9C;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,GAAG,MAAM,IAAI,CAkCnF;AAED,4DAA4D;AAC5D,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,YAAY,EAAE,GAAG,IAAI,CAI3D;AAED;2EAC2E;AAC3E,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,YAAY,EAAE,GAAG,IAAI,CAG5D;AAED,wBAAgB,gBAAgB,IAAI,YAAY,EAAE,GAAG,IAAI,CAExD;AAED,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,YAAY,EAAE,GAAG,IAAI,KAAK,IAAI,GAAG,MAAM,IAAI,CAI5F;AAED,+EAA+E;AAC/E,wBAAgB,iBAAiB,IAAI,IAAI,CAKxC"}
1
+ {"version":3,"file":"doc-file-state.d.ts","sourceRoot":"","sources":["../../../src/components/docfile/doc-file-state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGxD,+EAA+E;AAC/E,eAAO,MAAM,cAAc,gBAAgB,CAAC;AAE5C;;;+DAG+D;AAC/D,eAAO,MAAM,iBAAiB,mBAAmB,CAAC;AAKlD;;;;;;;mBAOmB;AACnB,eAAO,MAAM,iBAAiB,wBAAwB,CAAC;AAEvD;;4CAE4C;AAC5C,eAAO,MAAM,gBAAgB;eAA4B,MAAM,GAAG,IAAI;EAAuB,CAAC;AA4B9F,wBAAgB,YAAY,IAAI,OAAO,CAGtC;AAkDD,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAWnD;AAED,wBAAgB,YAAY,IAAI,IAAI,CAUnC;AAED,wBAAgB,cAAc,IAAI,MAAM,GAAG,IAAI,CAE9C;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,GAAG,MAAM,IAAI,CAkCnF;AAED,4DAA4D;AAC5D,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,YAAY,EAAE,GAAG,IAAI,CAI3D;AAED;2EAC2E;AAC3E,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,YAAY,EAAE,GAAG,IAAI,CAG5D;AAED,wBAAgB,gBAAgB,IAAI,YAAY,EAAE,GAAG,IAAI,CAExD;AAED,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,YAAY,EAAE,GAAG,IAAI,KAAK,IAAI,GAAG,MAAM,IAAI,CAI5F;AAED,+EAA+E;AAC/E,wBAAgB,iBAAiB,IAAI,IAAI,CAQxC"}
@@ -0,0 +1,18 @@
1
+ interface DocumentDropZoneProps {
2
+ /** Fired with the staged/committed document id once the upload resolves. */
3
+ onDocumentSelected: (documentId: string) => void;
4
+ className?: string;
5
+ }
6
+ /**
7
+ * The empty state of a `document` section in edit mode: drop a PDF/HTML file to
8
+ * upload it, or click to pick one from the library. Editor-only — the viewer
9
+ * never renders an unconfigured document section at all.
10
+ *
11
+ * Deliberately thin on validation: every rejection (wrong type, too large, no
12
+ * documents store) belongs to `uploadDocument`, which already routes typed copy
13
+ * through the pipeline's `onUploadError` → `UploadRejectionAlert` channel. The
14
+ * one judgement made here is WHICH file of a multi-file drop to hand over.
15
+ */
16
+ export declare function DocumentDropZone({ onDocumentSelected, className }: DocumentDropZoneProps): import("react/jsx-runtime").JSX.Element;
17
+ export {};
18
+ //# sourceMappingURL=DocumentDropZone.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DocumentDropZone.d.ts","sourceRoot":"","sources":["../../../src/components/primitives/DocumentDropZone.tsx"],"names":[],"mappings":"AAMA,UAAU,qBAAqB;IAC7B,4EAA4E;IAC5E,kBAAkB,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IACjD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAID;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,kBAAkB,EAAE,SAAS,EAAE,EAAE,qBAAqB,2CAsDxF"}
@@ -1,7 +1,13 @@
1
- export declare function DocumentRow({ sectionId, title, isEditMode, }: {
1
+ export declare function DocumentRow({ sectionId, title, documentId, isEditMode, onDocumentSelected, }: {
2
2
  sectionId: string;
3
3
  title: string;
4
+ /** Raw `content.doc.documentId`. Empty means UNCONFIGURED, which is a
5
+ * different state from an id that no longer resolves (see below). */
6
+ documentId?: string;
4
7
  isEditMode: boolean;
8
+ /** Present only when the section is editable — wires a picked/uploaded id
9
+ * back into content. Absent for viewers and for a locked/read-only render. */
10
+ onDocumentSelected?: (documentId: string) => void;
5
11
  }): import("react/jsx-runtime").JSX.Element | null;
6
12
  declare const _default: import("../../..").SectionDefinition<{
7
13
  type: "document";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/sections/Document/index.tsx"],"names":[],"mappings":"AAqDA,wBAAgB,WAAW,CAAC,EAC1B,SAAS,EACT,KAAK,EACL,UAAU,GACX,EAAE;IACD,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,OAAO,CAAC;CACrB,kDAsDA;;;;;;;;;;;;AAED,wBA6CG"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/sections/Document/index.tsx"],"names":[],"mappings":"AAsDA,wBAAgB,WAAW,CAAC,EAC1B,SAAS,EACT,KAAK,EACL,UAAe,EACf,UAAU,EACV,kBAAkB,GACnB,EAAE;IACD,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd;0EACsE;IACtE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB;mFAC+E;IAC/E,kBAAkB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;CACnD,kDA8DA;;;;;;;;;;;;AAED,wBA2DG"}
@@ -2,7 +2,7 @@ import {
2
2
  ColorItemSchema,
3
3
  ColorSpaceSchema,
4
4
  brandGuideSectionDefs
5
- } from "../../chunk-72FMQQIA.js";
5
+ } from "../../chunk-P3NNI3QI.js";
6
6
  import "../../chunk-5THS4K5D.js";
7
7
  import "../../chunk-MUTHF2GT.js";
8
8
  import "../../chunk-26ALJO4K.js";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  allSectionDefs
3
- } from "../../chunk-GOCFTXR3.js";
4
- import "../../chunk-72FMQQIA.js";
3
+ } from "../../chunk-6SLGKRLW.js";
4
+ import "../../chunk-P3NNI3QI.js";
5
5
  import "../../chunk-LBHQWIVL.js";
6
6
  import "../../chunk-5THS4K5D.js";
7
7
  import "../../chunk-EWNDBKYK.js";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  allSectionDefs
3
- } from "../../chunk-GOCFTXR3.js";
4
- import "../../chunk-72FMQQIA.js";
3
+ } from "../../chunk-6SLGKRLW.js";
4
+ import "../../chunk-P3NNI3QI.js";
5
5
  import "../../chunk-LBHQWIVL.js";
6
6
  import "../../chunk-5THS4K5D.js";
7
7
  import "../../chunk-EWNDBKYK.js";
@@ -1 +1 @@
1
- {"version":3,"file":"EditorShell.d.ts","sourceRoot":"","sources":["../../../src/components/shell/EditorShell.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAejD,OAAO,EAAoC,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAyDtF,OAAO,KAAK,EAAE,eAAe,EAA2D,MAAM,oBAAoB,CAAC;AA2CnH,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AA2BxD,UAAU,KAAK;IACb,OAAO,EAAE,MAAM,CAAC;IAIhB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,YAAY,EAAE;QACZ,KAAK,EAAE,OAAO,CAAC;QACf,aAAa,EAAE,OAAO,CAAC;QACvB,YAAY,EAAE,OAAO,CAAC;QACtB,cAAc,EAAE,OAAO,CAAC;QACxB,kBAAkB,EAAE,OAAO,CAAC;QAC5B,cAAc,EAAE,OAAO,CAAC;KACzB,CAAC;IACF,WAAW,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,GAAG,QAAQ,CAAA;KAAE,GAAG,IAAI,CAAC;IAIhE,sBAAsB,CAAC,EAAE,OAAO,CAAC;IAKjC,eAAe,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;CAC1C;AAED,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,EAClC,OAAO,EACP,YAAY,EACZ,MAAM,EACN,SAAS,EAAE,gBAAgB,EAC3B,YAAY,EACZ,WAAW,EACX,sBAA8B,EAC9B,eAAe,GAChB,EAAE,KAAK,2CA01DP;AAwED;6EAC6E;AAC7E,wBAAgB,kBAAkB,CAChC,YAAY,EAAE,WAAW,CAAC,YAAY,CAAC,EACvC,aAAa,EAAE,MAAM,GAAG,IAAI,EAC5B,eAAe,EAAE,MAAM,GAAG,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAK/B"}
1
+ {"version":3,"file":"EditorShell.d.ts","sourceRoot":"","sources":["../../../src/components/shell/EditorShell.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAejD,OAAO,EAAoC,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAyDtF,OAAO,KAAK,EAAE,eAAe,EAA2D,MAAM,oBAAoB,CAAC;AA2CnH,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AA2BxD,UAAU,KAAK;IACb,OAAO,EAAE,MAAM,CAAC;IAIhB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,YAAY,EAAE;QACZ,KAAK,EAAE,OAAO,CAAC;QACf,aAAa,EAAE,OAAO,CAAC;QACvB,YAAY,EAAE,OAAO,CAAC;QACtB,cAAc,EAAE,OAAO,CAAC;QACxB,kBAAkB,EAAE,OAAO,CAAC;QAC5B,cAAc,EAAE,OAAO,CAAC;KACzB,CAAC;IACF,WAAW,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,GAAG,QAAQ,CAAA;KAAE,GAAG,IAAI,CAAC;IAIhE,sBAAsB,CAAC,EAAE,OAAO,CAAC;IAKjC,eAAe,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;CAC1C;AAED,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,EAClC,OAAO,EACP,YAAY,EACZ,MAAM,EACN,SAAS,EAAE,gBAAgB,EAC3B,YAAY,EACZ,WAAW,EACX,sBAA8B,EAC9B,eAAe,GAChB,EAAE,KAAK,2CA82DP;AAwED;6EAC6E;AAC7E,wBAAgB,kBAAkB,CAChC,YAAY,EAAE,WAAW,CAAC,YAAY,CAAC,EACvC,aAAa,EAAE,MAAM,GAAG,IAAI,EAC5B,eAAe,EAAE,MAAM,GAAG,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAK/B"}
@@ -31,6 +31,12 @@ export interface MediaLibraryContextValue {
31
31
  * predate documents and must keep compiling. */
32
32
  documentManifest?: DocumentManifest;
33
33
  pendingDocuments?: PendingDocument[];
34
+ /** Upload a PDF/HTML file through the document pipeline, calling back with the
35
+ * document id once it is staged. Optional for the same reason as the two
36
+ * fields above. Owns every rejection itself (unsupported type, too large, no
37
+ * documents store) — they surface on the pipeline's `onUploadError` channel,
38
+ * so a caller never needs to pre-validate or report. */
39
+ uploadDocument?: (file: File, onComplete?: (documentId: string) => void) => void | Promise<void>;
34
40
  }
35
41
  export declare const MediaLibraryContext: import("react").Context<MediaLibraryContextValue | null>;
36
42
  export declare function useMediaLibrary(): MediaLibraryContextValue | null;
@@ -1 +1 @@
1
- {"version":3,"file":"MediaLibraryContext.d.ts","sourceRoot":"","sources":["../../../src/components/shell/MediaLibraryContext.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAClE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,KAAK,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAEvF,0EAA0E;AAC1E,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,UAAU,GAAG,OAAO,GAAG,UAAU,CAAC;AAEtE;wEACwE;AACxE,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,qBAAqB,CAAC;CAC9B;AAED,MAAM,WAAW,wBAAwB;IACvC;0EACsE;IACtE,eAAe,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,EAAE,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,aAAa,CAAC,WAAW,CAAC,CAAA;KAAE,KAAK,IAAI,CAAC;IAC9G,UAAU,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IACjC,sBAAsB,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,EAAE,IAAI,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,MAAM,CAAC;IACnH,qBAAqB,EAAE,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,EAAE,KAAK,IAAI,CAAC;IACpF,QAAQ,EAAE,aAAa,CAAC;IACxB,YAAY,EAAE,SAAS,EAAE,CAAC;IAC1B,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B;qDACiD;IACjD,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,gBAAgB,CAAC,EAAE,eAAe,EAAE,CAAC;CACtC;AAED,eAAO,MAAM,mBAAmB,0DAAuD,CAAC;AAExF,wBAAgB,eAAe,IAAI,wBAAwB,GAAG,IAAI,CAEjE"}
1
+ {"version":3,"file":"MediaLibraryContext.d.ts","sourceRoot":"","sources":["../../../src/components/shell/MediaLibraryContext.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAClE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,KAAK,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAEvF,0EAA0E;AAC1E,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,UAAU,GAAG,OAAO,GAAG,UAAU,CAAC;AAEtE;wEACwE;AACxE,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,qBAAqB,CAAC;CAC9B;AAED,MAAM,WAAW,wBAAwB;IACvC;0EACsE;IACtE,eAAe,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,EAAE,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,aAAa,CAAC,WAAW,CAAC,CAAA;KAAE,KAAK,IAAI,CAAC;IAC9G,UAAU,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IACjC,sBAAsB,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,EAAE,IAAI,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,MAAM,CAAC;IACnH,qBAAqB,EAAE,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,EAAE,KAAK,IAAI,CAAC;IACpF,QAAQ,EAAE,aAAa,CAAC;IACxB,YAAY,EAAE,SAAS,EAAE,CAAC;IAC1B,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B;qDACiD;IACjD,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,gBAAgB,CAAC,EAAE,eAAe,EAAE,CAAC;IACrC;;;;6DAIyD;IACzD,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,UAAU,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAClG;AAED,eAAO,MAAM,mBAAmB,0DAAuD,CAAC;AAExF,wBAAgB,eAAe,IAAI,wBAAwB,GAAG,IAAI,CAEjE"}
@@ -1 +1 @@
1
- {"version":3,"file":"useDocumentUploads.d.ts","sourceRoot":"","sources":["../../src/hooks/useDocumentUploads.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAyB,MAAM,sBAAsB,CAAC;AAapF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yCAAyC,CAAC;AAS/E,UAAU,uBAAuB;IAC/B,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,mBAAmB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,gBAAgB,CAAC,CAAC,CAAC;IAC5E,oBAAoB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IACpE;;;yEAGqE;IACrE,gBAAgB,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,+EAA+E;IAC/E,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAC3C;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,gBAAgB,EAChB,mBAAmB,EACnB,oBAAoB,EACpB,gBAAgB,EAChB,aAAa,GACd,EAAE,uBAAuB;;;;2BAiET,IAAI,eAAe,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,KAAG,OAAO,CAAC,IAAI,CAAC;4BA4IhE,MAAM,EAAE;4BAqC2B,MAAM,EAAE;;EAqC1D"}
1
+ {"version":3,"file":"useDocumentUploads.d.ts","sourceRoot":"","sources":["../../src/hooks/useDocumentUploads.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAyB,MAAM,sBAAsB,CAAC;AAapF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yCAAyC,CAAC;AAS/E,UAAU,uBAAuB;IAC/B,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,mBAAmB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,gBAAgB,CAAC,CAAC,CAAC;IAC5E,oBAAoB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IACpE;;;yEAGqE;IACrE,gBAAgB,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,+EAA+E;IAC/E,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAC3C;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,gBAAgB,EAChB,mBAAmB,EACnB,oBAAoB,EACpB,gBAAgB,EAChB,aAAa,GACd,EAAE,uBAAuB;;;;2BAiET,IAAI,eAAe,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,KAAG,OAAO,CAAC,IAAI,CAAC;4BAiJhE,MAAM,EAAE;4BAqC2B,MAAM,EAAE;;EAqC1D"}
@@ -1 +1 @@
1
- {"version":3,"file":"useMediaPipeline.d.ts","sourceRoot":"","sources":["../../src/hooks/useMediaPipeline.ts"],"names":[],"mappings":"AAEA,OAAO,EAAmB,KAAK,SAAS,EAAmB,MAAM,gBAAgB,CAAC;AAClF,OAAO,KAAK,EAAE,aAAa,EAAiC,MAAM,gBAAgB,CAAC;AACnF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,yCAAyC,CAAC;AAiBxF,OAAO,EAAwB,KAAK,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAEnF,UAAU,qBAAqB;IAC7B,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B,aAAa,EAAE,aAAa,CAAC;IAC7B,gBAAgB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC,CAAC;IACtE,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,mBAAmB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,gBAAgB,CAAC,CAAC,CAAC;IAC5E,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC;IACnE,oBAAoB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IACpE,kBAAkB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IACtE,gBAAgB,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;IAIvE,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAG1C,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAI3C,kBAAkB,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,UAAU,KAAK,IAAI,CAAC;CAC1E;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,UAAU,EACV,aAAa,EACb,gBAAgB,EAChB,gBAAgB,EAChB,mBAAmB,EACnB,QAAQ,EACR,WAAW,EACX,oBAAoB,EACpB,kBAAkB,EAClB,gBAAgB,EAChB,aAAa,EACb,cAAc,EACd,kBAAkB,GACnB,EAAE,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+BAoV8B,IAAI,EAAE,mBAAkB,MAAM,GAAG,IAAI;6BAiEvC,MAAM,EAAE;gCA0DL,MAAM,EAAE;;;+BA+Bf,MAAM,OAAO,MAAM;+BAiBnB,MAAM,YAAY,MAAM,GAAG,IAAI,KAAG,MAAM;6BAW1C,MAAM,QAAQ,MAAM;2BAWtB,MAAM,YAAY,MAAM,GAAG,IAAI;6BA4BvB,MAAM,YAAY,MAAM,GAAG,QAAQ;2BAd3C,MAAM,EAAE,YAAY,MAAM,GAAG,IAAI;kCA4DhE;QAAE,IAAI,EAAE,IAAI,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,EAAE,mBAC9B,MAAM,GAAG,IAAI;;;;wBA4CO,IAAI,eAAe,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,SAAS;QAAE,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE;;;;;;;EAgHjH"}
1
+ {"version":3,"file":"useMediaPipeline.d.ts","sourceRoot":"","sources":["../../src/hooks/useMediaPipeline.ts"],"names":[],"mappings":"AAEA,OAAO,EAAmB,KAAK,SAAS,EAAmB,MAAM,gBAAgB,CAAC;AAClF,OAAO,KAAK,EAAE,aAAa,EAAiC,MAAM,gBAAgB,CAAC;AACnF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,yCAAyC,CAAC;AAiBxF,OAAO,EAAwB,KAAK,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAEnF,UAAU,qBAAqB;IAC7B,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B,aAAa,EAAE,aAAa,CAAC;IAC7B,gBAAgB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC,CAAC;IACtE,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,mBAAmB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,gBAAgB,CAAC,CAAC,CAAC;IAC5E,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC;IACnE,oBAAoB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IACpE,kBAAkB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IACtE,gBAAgB,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;IAIvE,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAG1C,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAI3C,kBAAkB,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,UAAU,KAAK,IAAI,CAAC;CAC1E;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,UAAU,EACV,aAAa,EACb,gBAAgB,EAChB,gBAAgB,EAChB,mBAAmB,EACnB,QAAQ,EACR,WAAW,EACX,oBAAoB,EACpB,kBAAkB,EAClB,gBAAgB,EAChB,aAAa,EACb,cAAc,EACd,kBAAkB,GACnB,EAAE,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+BAoV8B,IAAI,EAAE,mBAAkB,MAAM,GAAG,IAAI;6BAiEvC,MAAM,EAAE;gCA0DL,MAAM,EAAE;;;+BA+Bf,MAAM,OAAO,MAAM;+BAiBnB,MAAM,YAAY,MAAM,GAAG,IAAI,KAAG,MAAM;6BAW1C,MAAM,QAAQ,MAAM;2BAWtB,MAAM,YAAY,MAAM,GAAG,IAAI;6BA4BvB,MAAM,YAAY,MAAM,GAAG,QAAQ;2BAd3C,MAAM,EAAE,YAAY,MAAM,GAAG,IAAI;kCA4DhE;QAAE,IAAI,EAAE,IAAI,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,EAAE,mBAC9B,MAAM,GAAG,IAAI;;;;wBA4CO,IAAI,eAAe,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,SAAS;QAAE,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE;;;;;;;EAqHjH"}
@@ -2,7 +2,7 @@ import type { SectionContent } from "../schemas/sections";
2
2
  import type { SiteIndex, SiteConfig } from "../schemas/site-config";
3
3
  import type { LoadedSection } from "./loader";
4
4
  import type { MediaManifest, MediaItem } from "../media/types";
5
- import type { DocumentManifestEntry } from "../schemas/documents";
5
+ import type { DocumentManifest, DocumentManifestEntry } from "../schemas/documents";
6
6
  export interface PreservedEditRow {
7
7
  id: string;
8
8
  sectionId: string;
@@ -69,6 +69,7 @@ export declare function cacheContent(sha: string, sections: LoadedSection[], ind
69
69
  savedBranchSha: string | null;
70
70
  changedSectionIds: string[];
71
71
  mainIndex: SiteIndex | null;
72
+ documentManifest?: DocumentManifest;
72
73
  }): Promise<void>;
73
74
  export declare function getCachedContent(): Promise<{
74
75
  sha: string;
@@ -78,6 +79,7 @@ export declare function getCachedContent(): Promise<{
78
79
  savedBranchSha: string | null;
79
80
  changedSectionIds: string[];
80
81
  mainIndex: SiteIndex | null;
82
+ documentManifest: DocumentManifest;
81
83
  } | null>;
82
84
  /** Invalidate only the cached content snapshot (e.g. after a publish removes the
83
85
  * draft), forcing the next editor load to refetch rather than serve stale diff
@@ -1 +1 @@
1
- {"version":3,"file":"dexie.d.ts","sourceRoot":"","sources":["../../src/lib/dexie.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dexie.d.ts","sourceRoot":"","sources":["../../src/lib/dexie.ts"],"names":[],"mappings":"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"}
package/dist/lib/dexie.js CHANGED
@@ -321,7 +321,8 @@ async function cacheContent(sha, sections, index, siteConfig, diff) {
321
321
  updatedAt: now,
322
322
  savedBranchSha: diff?.savedBranchSha ?? null,
323
323
  changedSectionIds: diff?.changedSectionIds ?? [],
324
- mainIndex: diff?.mainIndex ?? null
324
+ mainIndex: diff?.mainIndex ?? null,
325
+ documentManifest: diff?.documentManifest ?? null
325
326
  });
326
327
  }
327
328
  async function getCachedContent() {
@@ -336,7 +337,11 @@ async function getCachedContent() {
336
337
  changedSectionIds: row.changedSectionIds ?? [],
337
338
  // No stored mainIndex (e.g. legacy or post-publish cache) means "no draft",
338
339
  // so the published index equals the current index.
339
- mainIndex: row.mainIndex ?? row.index
340
+ mainIndex: row.mainIndex ?? row.index,
341
+ // Legacy row (pre-documents package): {} matches the fetch path's posture.
342
+ // The narrow stale edge — an old row at a sha that DOES hold a manifest —
343
+ // heals on the next fresh load or save.
344
+ documentManifest: row.documentManifest ?? {}
340
345
  };
341
346
  }
342
347
  async function clearContentCache() {
@@ -111,9 +111,18 @@ export declare const ASSET_CACHE_CONTROL = "max-age=31536000, immutable";
111
111
  * can slot in. All paths are SITE-RELATIVE ("media/{hash}/original.mp4") —
112
112
  * implementations prepend the tenant prefix ("sites/{siteId}/") themselves so
113
113
  * a caller can never address another tenant's objects.
114
+ *
115
+ * This bucket is PUBLIC: an object's URL is statically derivable from the
116
+ * bucket's public base, so `createUploadUrl` returns it once at upload time
117
+ * (built locally in standalone mode, brokered from the admin app's env in
118
+ * platform mode) and the media manifest stores that absolute URL. Rendering
119
+ * reads the manifest — nothing on the read path re-derives a URL.
114
120
  */
115
121
  export interface AssetStore {
116
- /** Mint a signed direct-upload URL for the browser (PUT). */
122
+ /**
123
+ * Mint a signed direct-upload URL for the browser (PUT), along with the
124
+ * object's permanent public URL for the caller to persist.
125
+ */
117
126
  createUploadUrl(path: string, opts: {
118
127
  contentType: string;
119
128
  }): Promise<{
@@ -121,8 +130,6 @@ export interface AssetStore {
121
130
  path: string;
122
131
  publicUrl: string;
123
132
  }>;
124
- /** Deterministic public CDN URL for an object (no network call). */
125
- getPublicUrl(path: string): string;
126
133
  delete(paths: string[]): Promise<void>;
127
134
  /** Recursive listing under a site-relative prefix. */
128
135
  list(prefix: string): Promise<{
@@ -145,8 +152,10 @@ export declare const DOCUMENT_CACHE_CONTROL = "private, max-age=31536000, immuta
145
152
  * Deliberately not an AssetStore: the assets bucket is public by configuration
146
153
  * at R2_PUBLIC_BASE (videos depend on it), so a signed GET there would protect
147
154
  * nothing and hosted HTML would render unsandboxed on a sibling origin. Hence
148
- * no `getPublicUrl` here at all — every read is a short-lived signed GET,
149
- * minted only after the gated route has authorized the requester.
155
+ * the asymmetry with AssetStore — an asset has a permanent public URL handed
156
+ * back by `createUploadUrl`, while a document has no durable URL at all: every
157
+ * read is a short-lived signed GET from `createDownloadUrl`, minted only after
158
+ * the gated route has authorized the requester.
150
159
  *
151
160
  * Paths are SITE-RELATIVE ("documents/{hash}/original.pdf"); implementations
152
161
  * prepend the tenant prefix themselves so a caller can never address another
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/storage/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/storage/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,qBAAa,oBAAqB,SAAQ,KAAK;aACjB,cAAc,EAAE,MAAM,GAAG,IAAI;gBAA7B,cAAc,EAAE,MAAM,GAAG,IAAI;CAI1D;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,CACR,KAAK,EAAE,SAAS,EAAE,EAClB,OAAO,EAAE,MAAM,EAIf,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GACxC,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAEhC,QAAQ,CACN,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,GAC5B,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;IAE9B,YAAY,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAE7C,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAE9B,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAE9E;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACH,0BAA0B,CAAC,CACzB,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAC3B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CAC3B;AAED;;;;;GAKG;AACH,qBAAa,iCAAkC,SAAQ,KAAK;aAC5B,KAAK,CAAC,EAAE,OAAO;gBAAf,KAAK,CAAC,EAAE,OAAO,YAAA;CAI9C;AAED;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,oBAAoB,CAAC;AAErD;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,QAA6B,CAAC;AAE7D;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAM5E;AAED,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAAC;IACpC,QAAQ,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;CAC/B,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,mBAAmB,gCAAgC,CAAC;AAEjE;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,UAAU;IACzB;;;OAGG;IACH,eAAe,CACb,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,GAC5B,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAEnE,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvC,sDAAsD;IACtD,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC,CAAC;CACpF;AAED;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,yCAAyC,CAAC;AAE7E;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,aAAa;IAC5B,6DAA6D;IAC7D,eAAe,CACb,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,GAC5B,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAEhD;;;;;OAKG;IACH,iBAAiB,CACf,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE;QAAE,gBAAgB,EAAE,MAAM,CAAC;QAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;KAAE,GAC5D,OAAO,CAAC,MAAM,CAAC,CAAC;IAEnB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvC,sDAAsD;IACtD,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;CACzF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drawnagency/primitives",
3
- "version": "0.1.100",
3
+ "version": "0.1.102",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  "./package.json": "./package.json",
@@ -112,7 +112,14 @@ export default function DocFilePane({
112
112
  <iframe
113
113
  key={attempt}
114
114
  ref={frameRef}
115
- src={doc.url}
115
+ // PDF open-parameters hide the native viewer's toolbar/side panel in
116
+ // Chromium so the pane's own top bar is the only chrome; Firefox and
117
+ // Safari ignore them harmlessly. view=FitH fits the page to the pane
118
+ // width in Chromium (default is fit-page, which wastes ~half the
119
+ // pane); zoom=page-width is the Firefox/pdf.js spelling of the same
120
+ // thing, verified harmless in Chromium. A client-side fragment: it
121
+ // survives the gated route's 302 and works on pre-save blob: URLs too.
122
+ src={doc.kind === "pdf" ? `${doc.url}#toolbar=0&navpanes=0&view=FitH&zoom=page-width` : doc.url}
116
123
  title={doc.title}
117
124
  // Opaque origin for authored HTML: no portal cookies, storage or
118
125
  // parent DOM. The ATTRIBUTE applies regardless of origin, so
@@ -4,6 +4,15 @@ import { createEvent } from "../../lib/events";
4
4
  /** Toggled on <html> while a document is open (the chat's class mechanism). */
5
5
  export const DOC_OPEN_CLASS = "bp-doc-open";
6
6
 
7
+ /** Held on <html> for a beat after close. bp-doc-open's `transition: left`
8
+ * rule leaves WITH the class, so without this the nav sidebar snaps home
9
+ * instead of gliding back the way it glided out — this class carries the
10
+ * same transition while the sidebar makes the return trip. */
11
+ export const DOC_CLOSING_CLASS = "bp-doc-closing";
12
+
13
+ /** Slightly longer than the 300ms CSS ease so the class outlives the glide. */
14
+ const DOC_CLOSING_MS = 350;
15
+
7
16
  /** The one breakpoint the doc feature keys off: at or above it the overlay
8
17
  * gets the desktop stage; below it a PDF goes to the OS viewer instead (iOS
9
18
  * Safari cannot scroll an iframed PDF, Android Chrome forces a download).
@@ -27,6 +36,7 @@ interface DocFileStore {
27
36
  openListeners: Set<(id: string | null) => void>;
28
37
  docsListeners: Set<(docs: SiteDocument[] | null) => void>;
29
38
  teardown: (() => void) | null;
39
+ closingTimer: ReturnType<typeof setTimeout> | null;
30
40
  }
31
41
 
32
42
  // Symbol-keyed on globalThis for the same reason the section registry is: the
@@ -38,7 +48,7 @@ const g = globalThis as unknown as Record<symbol, DocFileStore | undefined>;
38
48
  function store(): DocFileStore {
39
49
  let s = g[STORE_KEY];
40
50
  if (!s) {
41
- s = { open: null, docs: null, openListeners: new Set(), docsListeners: new Set(), teardown: null };
51
+ s = { open: null, docs: null, openListeners: new Set(), docsListeners: new Set(), teardown: null, closingTimer: null };
42
52
  g[STORE_KEY] = s;
43
53
  }
44
54
  return s;
@@ -63,9 +73,24 @@ function readHash(): string | null {
63
73
  function apply(sectionId: string | null): void {
64
74
  const s = store();
65
75
  if (s.open === sectionId) return;
76
+ const wasOpen = s.open !== null;
66
77
  s.open = sectionId;
67
78
  if (typeof document !== "undefined") {
68
- document.documentElement.classList.toggle(DOC_OPEN_CLASS, sectionId !== null);
79
+ const root = document.documentElement;
80
+ root.classList.toggle(DOC_OPEN_CLASS, sectionId !== null);
81
+ // A reopen during the return glide cancels the closing state outright.
82
+ if (s.closingTimer !== null) {
83
+ clearTimeout(s.closingTimer);
84
+ s.closingTimer = null;
85
+ root.classList.remove(DOC_CLOSING_CLASS);
86
+ }
87
+ if (sectionId === null && wasOpen) {
88
+ root.classList.add(DOC_CLOSING_CLASS);
89
+ s.closingTimer = setTimeout(() => {
90
+ root.classList.remove(DOC_CLOSING_CLASS);
91
+ s.closingTimer = null;
92
+ }, DOC_CLOSING_MS);
93
+ }
69
94
  }
70
95
  for (const listener of [...s.openListeners]) listener(sectionId);
71
96
  }
@@ -175,6 +200,9 @@ export function subscribeSiteDocuments(cb: (docs: SiteDocument[] | null) => void
175
200
  export function resetDocFileState(): void {
176
201
  const s = g[STORE_KEY];
177
202
  if (s?.teardown) s.teardown();
178
- if (typeof document !== "undefined") document.documentElement.classList.remove(DOC_OPEN_CLASS);
203
+ if (s?.closingTimer) clearTimeout(s.closingTimer);
204
+ if (typeof document !== "undefined") {
205
+ document.documentElement.classList.remove(DOC_OPEN_CLASS, DOC_CLOSING_CLASS);
206
+ }
179
207
  g[STORE_KEY] = undefined;
180
208
  }
@@ -0,0 +1,79 @@
1
+ import { useState, type DragEvent } from "react";
2
+ import { FileUp } from "lucide-react";
3
+ import { cn } from "../../lib/cn";
4
+ import { DOCUMENT_MIME_TO_EXT } from "../../schemas/documents";
5
+ import { useMediaLibrary } from "../shell/MediaLibraryContext";
6
+
7
+ interface DocumentDropZoneProps {
8
+ /** Fired with the staged/committed document id once the upload resolves. */
9
+ onDocumentSelected: (documentId: string) => void;
10
+ className?: string;
11
+ }
12
+
13
+ const isDocumentFile = (file: File) => file.type in DOCUMENT_MIME_TO_EXT;
14
+
15
+ /**
16
+ * The empty state of a `document` section in edit mode: drop a PDF/HTML file to
17
+ * upload it, or click to pick one from the library. Editor-only — the viewer
18
+ * never renders an unconfigured document section at all.
19
+ *
20
+ * Deliberately thin on validation: every rejection (wrong type, too large, no
21
+ * documents store) belongs to `uploadDocument`, which already routes typed copy
22
+ * through the pipeline's `onUploadError` → `UploadRejectionAlert` channel. The
23
+ * one judgement made here is WHICH file of a multi-file drop to hand over.
24
+ */
25
+ export function DocumentDropZone({ onDocumentSelected, className }: DocumentDropZoneProps) {
26
+ const mediaLibrary = useMediaLibrary();
27
+ const [dragging, setDragging] = useState(false);
28
+
29
+ // Only react to drags carrying files, so the editor's block reorder (which
30
+ // carries no `Files`) passes straight through. Mirrors ImageDropZone.
31
+ const hasFiles = (e: DragEvent) => e.dataTransfer.types.includes("Files");
32
+
33
+ const handleDragOver = (e: DragEvent) => {
34
+ if (!hasFiles(e)) return;
35
+ e.preventDefault();
36
+ e.dataTransfer.dropEffect = "copy";
37
+ setDragging(true);
38
+ };
39
+
40
+ const handleDragLeave = (e: DragEvent) => {
41
+ if (!hasFiles(e)) return;
42
+ const related = e.relatedTarget as Node | null;
43
+ if (related && e.currentTarget.contains(related)) return;
44
+ setDragging(false);
45
+ };
46
+
47
+ const handleDrop = (e: DragEvent) => {
48
+ if (!hasFiles(e)) return;
49
+ e.preventDefault();
50
+ e.stopPropagation();
51
+ setDragging(false);
52
+ const files = Array.from(e.dataTransfer.files);
53
+ if (files.length === 0) return;
54
+ // First document-typed file wins. With none, the first file still goes to
55
+ // the pipeline so its rejection names the real constraint ("only PDF and
56
+ // HTML documents can be uploaded") instead of a hand-rolled message here.
57
+ void mediaLibrary?.uploadDocument?.(files.find(isDocumentFile) ?? files[0], onDocumentSelected);
58
+ };
59
+
60
+ return (
61
+ <button
62
+ type="button"
63
+ onClick={() =>
64
+ mediaLibrary?.openSelectModal(onDocumentSelected, { kinds: ["document"] })
65
+ }
66
+ onDragOver={handleDragOver}
67
+ onDragLeave={handleDragLeave}
68
+ onDrop={handleDrop}
69
+ className={cn(
70
+ "flex w-full cursor-pointer items-center justify-center gap-2 rounded-outer border-2 border-dashed border-base-200 bg-base px-4 py-6 text-sm text-base-contrast-light transition-colors hover:border-base-300 hover:bg-base-accent hover:text-base-contrast",
71
+ dragging && "border-primary bg-primary/5 text-base-contrast",
72
+ className,
73
+ )}
74
+ >
75
+ <FileUp size={18} aria-hidden="true" className="shrink-0" />
76
+ <span>Drop a PDF or HTML file, or choose from the library</span>
77
+ </button>
78
+ );
79
+ }
@@ -3,6 +3,7 @@ import { z } from "zod";
3
3
  import { ExternalLink, FileText } from "lucide-react";
4
4
  import { defineSection } from "../../../lib/registry";
5
5
  import { cn } from "../../../lib/cn";
6
+ import { DocumentDropZone } from "../../primitives/DocumentDropZone";
6
7
  import { formatDocSize, type SiteDocument } from "../../../lib/documents";
7
8
  import {
8
9
  getSiteDocuments,
@@ -54,11 +55,19 @@ function useSiteDocument(sectionId: string): { doc: SiteDocument | null; resolve
54
55
  export function DocumentRow({
55
56
  sectionId,
56
57
  title,
58
+ documentId = "",
57
59
  isEditMode,
60
+ onDocumentSelected,
58
61
  }: {
59
62
  sectionId: string;
60
63
  title: string;
64
+ /** Raw `content.doc.documentId`. Empty means UNCONFIGURED, which is a
65
+ * different state from an id that no longer resolves (see below). */
66
+ documentId?: string;
61
67
  isEditMode: boolean;
68
+ /** Present only when the section is editable — wires a picked/uploaded id
69
+ * back into content. Absent for viewers and for a locked/read-only render. */
70
+ onDocumentSelected?: (documentId: string) => void;
62
71
  }) {
63
72
  const { doc, resolved } = useSiteDocument(sectionId);
64
73
 
@@ -67,6 +76,14 @@ export function DocumentRow({
67
76
  // the section stays selectable and repairable.
68
77
  if (resolved && !doc && !isEditMode) return null;
69
78
 
79
+ // An empty id is an unconfigured section: give editors somewhere to drop a
80
+ // file instead of a dead, permanently-disabled row. A NON-empty id that does
81
+ // not resolve is a different problem — it keeps the "File missing" row so the
82
+ // broken reference stays visible rather than looking like a fresh section.
83
+ if (isEditMode && !documentId && onDocumentSelected) {
84
+ return <DocumentDropZone onDocumentSelected={onDocumentSelected} />;
85
+ }
86
+
70
87
  // `doc.title` is already `content.title || filenameStem(entry.filename)` —
71
88
  // deriveSiteDocuments applies the fallback once so the row, the Documents nav
72
89
  // entry and the overlay top bar can never disagree. Picking a file NEVER
@@ -130,14 +147,28 @@ export default defineSection({
130
147
  "Pick the file from the media library (documents tab); leave the title blank to show the file name without its extension, or type one to override it. " +
131
148
  "Leave 'Show in Documents menu' on for anything a viewer should be able to reach from any page — those entries collect into a derived Documents group at the bottom of the left nav. " +
132
149
  "Use one section per file; for a set of downloads, place several document sections in a container.",
133
- component: ({ content, isEditMode }) => (
150
+ component: ({ content, isEditMode, onChange }) => (
134
151
  <DocumentRow
135
152
  // The section id is added by the outer Section schema (id + layout
136
153
  // intersected with the per-type schema), so it is present at render time
137
154
  // but absent from this type's inferred content shape.
138
155
  sectionId={(content as unknown as { id?: string }).id ?? ""}
139
156
  title={content.content.title}
157
+ documentId={content.content.doc?.documentId ?? ""}
140
158
  isEditMode={isEditMode}
159
+ // Writes `doc` only: `title` stays the editor's (deriveSiteDocuments
160
+ // applies the filename-stem fallback for display) and showInDocumentsNav
161
+ // is untouched. Spreading `content` preserves the id and layout envelope,
162
+ // which the container path (Container.tsx updateChild) also re-attaches.
163
+ onDocumentSelected={
164
+ onChange
165
+ ? (documentId) =>
166
+ onChange({
167
+ ...content,
168
+ content: { ...content.content, doc: { ...content.content.doc, documentId } },
169
+ })
170
+ : undefined
171
+ }
141
172
  />
142
173
  ),
143
174
  defaults: () => ({
@@ -247,7 +247,8 @@ export default function EditorShell({
247
247
  const [rejectedUploads, setRejectedUploads] = useState<{ name: string; reason: "size" | "type" }[]>([]);
248
248
  // Surfaces useMediaPipeline's onUploadError (oversized/disallowed files,
249
249
  // upload-intent failures, direct-to-bucket PUT failures) in the media
250
- // library modal, alongside its existing client-side rejection banner.
250
+ // library modal, alongside its existing client-side rejection banner — and at
251
+ // shell level while that modal is closed, for uploads started from a section.
251
252
  const [uploadErrorMessage, setUploadErrorMessage] = useState<string | null>(null);
252
253
  // Surfaces useMediaPipeline's onUploadNotice (e.g. "N files already exist
253
254
  // and were left where they are" on dedup skip) in the media library modal.
@@ -1089,6 +1090,10 @@ export default function EditorShell({
1089
1090
  setMainSha(headSha);
1090
1091
  setChangedSectionIds(new Set(cached.changedSectionIds ?? []));
1091
1092
  setMainIndex(cached.mainIndex ?? loadedIndex);
1093
+ // Without this, a cache-hit load left documentManifest null forever —
1094
+ // the siteDocuments memo's null-guard then never lifted and every
1095
+ // document row (committed or staged) read "File missing".
1096
+ setDocumentManifest(cached.documentManifest ?? {});
1092
1097
  } else {
1093
1098
  const data = await fetchSavedContent();
1094
1099
  loadedSections = data.sections;
@@ -1098,6 +1103,7 @@ export default function EditorShell({
1098
1103
  savedBranchSha: data.savedBranchSha,
1099
1104
  changedSectionIds: data.changedSectionIds,
1100
1105
  mainIndex: data.mainIndex,
1106
+ documentManifest: data.documentManifest ?? {},
1101
1107
  });
1102
1108
  if (data.mediaManifest) {
1103
1109
  loadedManifest = data.mediaManifest;
@@ -1824,6 +1830,20 @@ export default function EditorShell({
1824
1830
  </div>
1825
1831
  )}
1826
1832
 
1833
+ {/* Same onUploadError message the media modal shows in its own banner,
1834
+ surfaced at shell level when the modal is closed — an upload can now
1835
+ start from a document section's drop zone, where nothing else would
1836
+ ever render the rejection. Gated on the modal being shut so the two
1837
+ never show the same message twice. */}
1838
+ {uploadErrorMessage && !showMediaLibrary && (
1839
+ <div className="sticky top-2 z-30 mx-auto w-full max-w-3xl px-4">
1840
+ <UploadRejectionAlert
1841
+ message={uploadErrorMessage}
1842
+ onDismiss={() => setUploadErrorMessage(null)}
1843
+ />
1844
+ </div>
1845
+ )}
1846
+
1827
1847
  {unparseableSectionIds.length > 0 && (
1828
1848
  <div className="sticky top-2 z-30 mx-auto w-full max-w-3xl px-4">
1829
1849
  <UploadRejectionAlert
@@ -28,6 +28,12 @@ export interface MediaLibraryContextValue {
28
28
  * predate documents and must keep compiling. */
29
29
  documentManifest?: DocumentManifest;
30
30
  pendingDocuments?: PendingDocument[];
31
+ /** Upload a PDF/HTML file through the document pipeline, calling back with the
32
+ * document id once it is staged. Optional for the same reason as the two
33
+ * fields above. Owns every rejection itself (unsupported type, too large, no
34
+ * documents store) — they surface on the pipeline's `onUploadError` channel,
35
+ * so a caller never needs to pre-validate or report. */
36
+ uploadDocument?: (file: File, onComplete?: (documentId: string) => void) => void | Promise<void>;
31
37
  }
32
38
 
33
39
  export const MediaLibraryContext = createContext<MediaLibraryContextValue | null>(null);
@@ -1,4 +1,5 @@
1
1
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
2
+ import { DOCUMENT_CACHE_CONTROL } from "../storage/types";
2
3
  import { hashFileBuffer } from "../media/utils";
3
4
  import type { DocumentManifest, DocumentManifestEntry } from "../schemas/documents";
4
5
  import {
@@ -209,6 +210,11 @@ export function useDocumentUploads({
209
210
  const xhr = new XMLHttpRequest();
210
211
  xhr.open("PUT", uploadUrl);
211
212
  xhr.setRequestHeader("Content-Type", file.type);
213
+ // The presign signs cache-control;content-type;host — BOTH headers
214
+ // must be sent byte-exact or R2 403s the PUT, and R2 omits CORS
215
+ // headers on errors, so the browser misreports it as a CORS block.
216
+ // (The video path sets ASSET_CACHE_CONTROL the same way.)
217
+ xhr.setRequestHeader("Cache-Control", DOCUMENT_CACHE_CONTROL);
212
218
  xhr.upload.onprogress = (e) => {
213
219
  if (e.lengthComputable) {
214
220
  setDocumentUploadProgress((prev) => ({ ...prev, [documentId]: e.loaded / e.total }));
@@ -216,7 +222,7 @@ export function useDocumentUploads({
216
222
  };
217
223
  xhr.onload = () =>
218
224
  xhr.status >= 200 && xhr.status < 300 ? resolvePut() : rejectPut(new Error(`upload ${xhr.status}`));
219
- xhr.onerror = () => rejectPut(new Error("upload failed"));
225
+ xhr.onerror = () => rejectPut(new Error("the storage request was blocked or interrupted"));
220
226
  xhr.send(file);
221
227
  });
222
228
  } catch (err) {
@@ -790,7 +790,12 @@ export function useMediaPipeline({
790
790
  // pipeline's pendingDocumentUrls — a context copy would be dead surface.
791
791
  documentManifest,
792
792
  pendingDocuments: documents.pendingDocuments,
793
- }), [handleMediaUpload, enqueueFile, mediaManifest, pendingMediaItems, pendingLocalUrls, siteConfig, documentManifest, documents.pendingDocuments]);
793
+ // Direct document upload, for the `document` section's empty-state drop
794
+ // zone. Deliberately not routed through enqueueFile: that gate keys off the
795
+ // MIME type, so a file the drop zone means as a document but the browser
796
+ // typed as something else would silently become an image.
797
+ uploadDocument: documents.uploadDocument,
798
+ }), [handleMediaUpload, enqueueFile, mediaManifest, pendingMediaItems, pendingLocalUrls, siteConfig, documentManifest, documents.pendingDocuments, documents.uploadDocument]);
794
799
 
795
800
  return {
796
801
  processingItems,
package/src/lib/dexie.ts CHANGED
@@ -4,7 +4,7 @@ import type { SiteIndex, SectionMeta, SiteConfig, Page } from "../schemas/site-c
4
4
  import type { NavEntry } from "../schemas/nav";
5
5
  import type { LoadedSection } from "./loader";
6
6
  import type { MediaManifest, MediaItem } from "../media/types";
7
- import type { DocumentManifestEntry } from "../schemas/documents";
7
+ import type { DocumentManifest, DocumentManifestEntry } from "../schemas/documents";
8
8
 
9
9
  interface SectionRow {
10
10
  sectionId: string;
@@ -50,6 +50,11 @@ interface ContentCacheRow {
50
50
  savedBranchSha?: string | null;
51
51
  changedSectionIds?: string[];
52
52
  mainIndex?: SiteIndex | null;
53
+ // The document sidecar at this sha. Optional for rows written by older
54
+ // packages; readers fall back to {} (same posture as the fetch path's
55
+ // `data.documentManifest ?? {}`). Without this, a cache-hit editor load left
56
+ // documentManifest null and the whole documents feature dead for the session.
57
+ documentManifest?: DocumentManifest | null;
53
58
  }
54
59
 
55
60
  interface MediaManifestRow {
@@ -478,7 +483,7 @@ export async function cacheContent(
478
483
  sections: LoadedSection[],
479
484
  index: SiteIndex,
480
485
  siteConfig: Record<string, unknown>,
481
- diff?: { savedBranchSha: string | null; changedSectionIds: string[]; mainIndex: SiteIndex | null },
486
+ diff?: { savedBranchSha: string | null; changedSectionIds: string[]; mainIndex: SiteIndex | null; documentManifest?: DocumentManifest },
482
487
  ): Promise<void> {
483
488
  const now = new Date().toISOString();
484
489
  await getDb().contentCache.put({
@@ -491,6 +496,7 @@ export async function cacheContent(
491
496
  savedBranchSha: diff?.savedBranchSha ?? null,
492
497
  changedSectionIds: diff?.changedSectionIds ?? [],
493
498
  mainIndex: diff?.mainIndex ?? null,
499
+ documentManifest: diff?.documentManifest ?? null,
494
500
  });
495
501
  }
496
502
 
@@ -502,6 +508,7 @@ export async function getCachedContent(): Promise<{
502
508
  savedBranchSha: string | null;
503
509
  changedSectionIds: string[];
504
510
  mainIndex: SiteIndex | null;
511
+ documentManifest: DocumentManifest;
505
512
  } | null> {
506
513
  const row = await getDb().contentCache.get("current");
507
514
  if (!row) return null;
@@ -515,6 +522,10 @@ export async function getCachedContent(): Promise<{
515
522
  // No stored mainIndex (e.g. legacy or post-publish cache) means "no draft",
516
523
  // so the published index equals the current index.
517
524
  mainIndex: row.mainIndex ?? row.index,
525
+ // Legacy row (pre-documents package): {} matches the fetch path's posture.
526
+ // The narrow stale edge — an old row at a sha that DOES hold a manifest —
527
+ // heals on the next fresh load or save.
528
+ documentManifest: row.documentManifest ?? {},
518
529
  };
519
530
  }
520
531
 
@@ -135,17 +135,23 @@ export const ASSET_CACHE_CONTROL = "max-age=31536000, immutable";
135
135
  * can slot in. All paths are SITE-RELATIVE ("media/{hash}/original.mp4") —
136
136
  * implementations prepend the tenant prefix ("sites/{siteId}/") themselves so
137
137
  * a caller can never address another tenant's objects.
138
+ *
139
+ * This bucket is PUBLIC: an object's URL is statically derivable from the
140
+ * bucket's public base, so `createUploadUrl` returns it once at upload time
141
+ * (built locally in standalone mode, brokered from the admin app's env in
142
+ * platform mode) and the media manifest stores that absolute URL. Rendering
143
+ * reads the manifest — nothing on the read path re-derives a URL.
138
144
  */
139
145
  export interface AssetStore {
140
- /** Mint a signed direct-upload URL for the browser (PUT). */
146
+ /**
147
+ * Mint a signed direct-upload URL for the browser (PUT), along with the
148
+ * object's permanent public URL for the caller to persist.
149
+ */
141
150
  createUploadUrl(
142
151
  path: string,
143
152
  opts: { contentType: string },
144
153
  ): Promise<{ uploadUrl: string; path: string; publicUrl: string }>;
145
154
 
146
- /** Deterministic public CDN URL for an object (no network call). */
147
- getPublicUrl(path: string): string;
148
-
149
155
  delete(paths: string[]): Promise<void>;
150
156
 
151
157
  /** Recursive listing under a site-relative prefix. */
@@ -167,8 +173,10 @@ export const DOCUMENT_CACHE_CONTROL = "private, max-age=31536000, immutable";
167
173
  * Deliberately not an AssetStore: the assets bucket is public by configuration
168
174
  * at R2_PUBLIC_BASE (videos depend on it), so a signed GET there would protect
169
175
  * nothing and hosted HTML would render unsandboxed on a sibling origin. Hence
170
- * no `getPublicUrl` here at all — every read is a short-lived signed GET,
171
- * minted only after the gated route has authorized the requester.
176
+ * the asymmetry with AssetStore — an asset has a permanent public URL handed
177
+ * back by `createUploadUrl`, while a document has no durable URL at all: every
178
+ * read is a short-lived signed GET from `createDownloadUrl`, minted only after
179
+ * the gated route has authorized the requester.
172
180
  *
173
181
  * Paths are SITE-RELATIVE ("documents/{hash}/original.pdf"); implementations
174
182
  * prepend the tenant prefix themselves so a caller can never address another