@pipelex/mthds-form 0.35.1 → 0.36.0

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.
@@ -237,6 +237,9 @@ var EN_FIELD_STRINGS = {
237
237
  previewUnavailablePdf: "Preview unavailable - open to view.",
238
238
  pdfViewerUnavailable: "This browser cannot show the PDF here.",
239
239
  openInNewTab: "Open in a new tab",
240
+ documentPdfPreparing: "Preparing the preview\u2026",
241
+ documentPdfFailed: "Couldn\u2019t prepare a preview.",
242
+ documentPdfUnavailable: "No preview for this file.",
240
243
  // U+002D HYPHEN-MINUS, deliberately: not an en dash, not an em dash.
241
244
  resultAbsent: "-",
242
245
  resultAbsentDescription: "Not provided",
@@ -423,6 +426,9 @@ var FR_FIELD_STRINGS = {
423
426
  previewUnavailablePdf: "Aper\xE7u indisponible - ouvrez le fichier pour le consulter.",
424
427
  pdfViewerUnavailable: "Ce navigateur ne peut pas afficher le PDF ici.",
425
428
  openInNewTab: "Ouvrir dans un nouvel onglet",
429
+ documentPdfPreparing: "Pr\xE9paration de l\u2019aper\xE7u\u2026",
430
+ documentPdfFailed: "Impossible de pr\xE9parer un aper\xE7u.",
431
+ documentPdfUnavailable: "Aucun aper\xE7u pour ce fichier.",
426
432
  // U+002D HYPHEN-MINUS, as in the English defaults.
427
433
  resultAbsent: "-",
428
434
  resultAbsentDescription: "Non renseign\xE9",
@@ -4441,6 +4447,7 @@ function ResultEnvProvider({
4441
4447
  resolveUrl,
4442
4448
  resolveUrls,
4443
4449
  resolveRendition,
4450
+ resolveDocumentPdf,
4444
4451
  resolveShareUrl,
4445
4452
  proseImages,
4446
4453
  tableColumns,
@@ -4456,6 +4463,7 @@ function ResultEnvProvider({
4456
4463
  resolveUrl,
4457
4464
  resolveUrls,
4458
4465
  resolveRendition,
4466
+ resolveDocumentPdf,
4459
4467
  resolveShareUrl,
4460
4468
  proseImages,
4461
4469
  tableColumns,
@@ -4469,6 +4477,7 @@ function ResultEnvProvider({
4469
4477
  resolveUrl,
4470
4478
  resolveUrls,
4471
4479
  resolveRendition,
4480
+ resolveDocumentPdf,
4472
4481
  resolveShareUrl,
4473
4482
  proseImages,
4474
4483
  tableColumns,
@@ -4524,6 +4533,9 @@ function useMarkupResolvers() {
4524
4533
  function useResolveRendition() {
4525
4534
  return use(ResultEnvContext).resolveRendition;
4526
4535
  }
4536
+ function useResolveDocumentPdf() {
4537
+ return use(ResultEnvContext).resolveDocumentPdf;
4538
+ }
4527
4539
  function useResolveShareUrl() {
4528
4540
  return use(ResultEnvContext).resolveShareUrl;
4529
4541
  }
@@ -4631,6 +4643,74 @@ function ResultAt({ segment, children }) {
4631
4643
  );
4632
4644
  return /* @__PURE__ */ jsx(ResultLocationContext, { value: location, children });
4633
4645
  }
4646
+ var DOCUMENT_PDF_ASK_TIMEOUT_MS = 3e4;
4647
+ var DOCUMENT_PDF_PREPARING_LIMIT_MS = 18e4;
4648
+ var FIRST_WAIT_MS = 1500;
4649
+ var WAIT_GROWTH = 1.5;
4650
+ var LONGEST_WAIT_MS = 1e4;
4651
+ var PREPARING = { state: "preparing" };
4652
+ var FAILED = { state: "failed" };
4653
+ var isNonEmptyString = (value) => typeof value === "string" && value !== "";
4654
+ async function askOnce(ask) {
4655
+ let timer;
4656
+ const timeout = new Promise((resolve) => {
4657
+ timer = setTimeout(() => resolve(FAILED), DOCUMENT_PDF_ASK_TIMEOUT_MS);
4658
+ });
4659
+ try {
4660
+ const answer = await Promise.race([ask(), timeout]);
4661
+ switch (answer?.state) {
4662
+ case "ready":
4663
+ return isNonEmptyString(answer.url) ? answer : FAILED;
4664
+ case "failed":
4665
+ return isNonEmptyString(answer.message) ? answer : FAILED;
4666
+ case "preparing":
4667
+ case "unavailable":
4668
+ return answer;
4669
+ default:
4670
+ return FAILED;
4671
+ }
4672
+ } catch {
4673
+ return FAILED;
4674
+ } finally {
4675
+ clearTimeout(timer);
4676
+ }
4677
+ }
4678
+ function useDocumentPdf(ask) {
4679
+ const [answer, setAnswer] = useState(PREPARING);
4680
+ const [attempt, setAttempt] = useState(0);
4681
+ const latestAsk = useRef(ask);
4682
+ useEffect(() => {
4683
+ latestAsk.current = ask;
4684
+ });
4685
+ useEffect(() => {
4686
+ let stale = false;
4687
+ let timer;
4688
+ const started = Date.now();
4689
+ const askAgain = async (wait) => {
4690
+ const asked = await askOnce(latestAsk.current);
4691
+ if (stale) return;
4692
+ if (asked.state !== "preparing") {
4693
+ setAnswer(asked);
4694
+ return;
4695
+ }
4696
+ if (Date.now() + wait - started > DOCUMENT_PDF_PREPARING_LIMIT_MS) {
4697
+ setAnswer(FAILED);
4698
+ return;
4699
+ }
4700
+ timer = setTimeout(() => void askAgain(Math.min(wait * WAIT_GROWTH, LONGEST_WAIT_MS)), wait);
4701
+ };
4702
+ void askAgain(FIRST_WAIT_MS);
4703
+ return () => {
4704
+ stale = true;
4705
+ clearTimeout(timer);
4706
+ };
4707
+ }, [attempt]);
4708
+ const retry = useCallback(() => {
4709
+ setAnswer(PREPARING);
4710
+ setAttempt((current) => current + 1);
4711
+ }, []);
4712
+ return { answer, retry };
4713
+ }
4634
4714
  function FocusFrame({ ref, className, ...props }) {
4635
4715
  const [frame, setFrame] = useState(null);
4636
4716
  const focused = useFrameFocus(frame);
@@ -7221,12 +7301,12 @@ function frameableUrl(candidate) {
7221
7301
  if (/^https?:/.test(candidate.url)) return candidate.url;
7222
7302
  return candidate.fromResolver && candidate.url.startsWith("/") ? candidate.url : void 0;
7223
7303
  }
7304
+ function browserRenders(content, url) {
7305
+ return content.mimeType ? PREVIEWABLE_MIME_RE.test(content.mimeType) : PREVIEWABLE_EXT_RE.test(content.filename ?? url);
7306
+ }
7224
7307
  function previewableUrl(content, resolve) {
7225
7308
  const url = frameableUrl(paintable(content, resolve));
7226
- if (!url) return void 0;
7227
- const named = content.filename ?? url;
7228
- const renderable = content.mimeType ? PREVIEWABLE_MIME_RE.test(content.mimeType) : PREVIEWABLE_EXT_RE.test(named);
7229
- return renderable ? url : void 0;
7309
+ return url && browserRenders(content, url) ? url : void 0;
7230
7310
  }
7231
7311
  function isPdfDocument(content, url) {
7232
7312
  return content.mimeType ? /^application\/pdf\s*(;|$)/i.test(content.mimeType) : /\.pdf(\?|#|$)/i.test(content.filename ?? url);
@@ -7299,9 +7379,81 @@ function PdfUnavailable({
7299
7379
  ] })
7300
7380
  ] });
7301
7381
  }
7382
+ function ConvertedDocumentPreview({
7383
+ ask,
7384
+ name,
7385
+ file
7386
+ }) {
7387
+ const s = useFieldStrings();
7388
+ const { answer, retry } = useDocumentPdf(ask);
7389
+ const preparing = useRef(null);
7390
+ const retried = useRef(false);
7391
+ useEffect(() => {
7392
+ if (!retried.current) return;
7393
+ retried.current = false;
7394
+ preparing.current?.focus();
7395
+ });
7396
+ if (answer.state === "preparing") {
7397
+ return /* @__PURE__ */ jsxs(
7398
+ "div",
7399
+ {
7400
+ ref: preparing,
7401
+ role: "status",
7402
+ tabIndex: -1,
7403
+ className: cn(
7404
+ NESTED_FRAME,
7405
+ "flex items-center justify-center gap-2 rounded-lg border border-border bg-card/40 text-[12px] text-muted-foreground outline-none"
7406
+ ),
7407
+ children: [
7408
+ /* @__PURE__ */ jsx(Loader2, { "aria-hidden": true, className: "size-4 animate-spin" }),
7409
+ /* @__PURE__ */ jsx("span", { children: s.documentPdfPreparing })
7410
+ ]
7411
+ }
7412
+ );
7413
+ }
7414
+ if (answer.state === "failed") {
7415
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 rounded-lg border border-border bg-card/40 px-3.5 py-3 sm:flex-row sm:items-center", children: [
7416
+ /* @__PURE__ */ jsxs(
7417
+ "p",
7418
+ {
7419
+ role: "alert",
7420
+ className: "flex min-w-0 flex-1 items-start gap-2 text-[12.5px] leading-relaxed text-muted-foreground",
7421
+ children: [
7422
+ /* @__PURE__ */ jsx(TriangleAlert, { "aria-hidden": true, className: "mt-0.5 size-3.5 shrink-0" }),
7423
+ /* @__PURE__ */ jsx("span", { children: answer.message ?? s.documentPdfFailed })
7424
+ ]
7425
+ }
7426
+ ),
7427
+ /* @__PURE__ */ jsx(
7428
+ "button",
7429
+ {
7430
+ type: "button",
7431
+ onClick: () => {
7432
+ retried.current = true;
7433
+ retry();
7434
+ },
7435
+ className: cn(CARD_ACTION, "text-foreground", focusOutlineClass),
7436
+ children: s.tryAgain
7437
+ }
7438
+ )
7439
+ ] });
7440
+ }
7441
+ const gated = answer.state === "ready" ? viewableUrl(answer.url) : void 0;
7442
+ const url = gated && frameableUrl({ url: gated, fromResolver: true });
7443
+ if (url) return /* @__PURE__ */ jsx(DocumentPreview, { url, name, pdf: true, file, whole: false });
7444
+ return /* @__PURE__ */ jsx(
7445
+ "p",
7446
+ {
7447
+ role: "status",
7448
+ className: "rounded-lg border border-border bg-card/40 px-3.5 py-3 text-[12.5px] leading-relaxed text-muted-foreground",
7449
+ children: s.documentPdfUnavailable
7450
+ }
7451
+ );
7452
+ }
7302
7453
  function DocumentValue({ value, whole = false }) {
7303
7454
  const s = useFieldStrings();
7304
7455
  const resolve = useResolveUrl();
7456
+ const resolveDocumentPdf = useResolveDocumentPdf();
7305
7457
  const isApp = useFieldPresentation() === "app";
7306
7458
  const [open, setOpen] = useState(false);
7307
7459
  const file = useStuffFile("document", value);
@@ -7311,6 +7463,10 @@ function DocumentValue({ value, whole = false }) {
7311
7463
  const preview = previewableUrl(content, resolve);
7312
7464
  const pdf = preview !== void 0 && isPdfDocument(content, preview);
7313
7465
  const opened = whole && isApp && pdf;
7466
+ const askPdf = preview === void 0 && !browserRenders(content, content.url) ? resolveDocumentPdf?.(content.url, {
7467
+ filename: content.filename,
7468
+ mimeType: content.mimeType
7469
+ }) : void 0;
7314
7470
  return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
7315
7471
  /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-start gap-2.5", children: [
7316
7472
  /* @__PURE__ */ jsx(FileText, { "aria-hidden": true, className: "mt-0.5 size-4 shrink-0 text-muted-foreground" }),
@@ -7328,7 +7484,7 @@ function DocumentValue({ value, whole = false }) {
7328
7484
  ),
7329
7485
  content.snippet && /* @__PURE__ */ jsx("p", { className: "line-clamp-2 text-[12px] leading-relaxed text-muted-foreground", children: content.snippet })
7330
7486
  ] }),
7331
- preview && !opened && /* @__PURE__ */ jsxs(
7487
+ (preview || askPdf) && !opened && /* @__PURE__ */ jsxs(
7332
7488
  "button",
7333
7489
  {
7334
7490
  type: "button",
@@ -7345,7 +7501,8 @@ function DocumentValue({ value, whole = false }) {
7345
7501
  }
7346
7502
  )
7347
7503
  ] }),
7348
- preview && (opened || open) && /* @__PURE__ */ jsx(DocumentPreview, { url: preview, name, pdf, file, whole: opened })
7504
+ preview && (opened || open) && /* @__PURE__ */ jsx(DocumentPreview, { url: preview, name, pdf, file, whole: opened }),
7505
+ askPdf && open && /* @__PURE__ */ jsx(ConvertedDocumentPreview, { ask: askPdf, name, file }, content.url)
7349
7506
  ] });
7350
7507
  }
7351
7508
  var TILE_RENDITION_SIDE = 576;
@@ -8969,6 +9126,6 @@ function pdfFileName(field, value, baseName) {
8969
9126
  return name === void 0 ? "" : name.replace(/\.[^.]+$/, "");
8970
9127
  }
8971
9128
 
8972
- export { BooleanField, ConceptPill, DEFAULT_FIELD_STRINGS, DateField, DocumentField, EN_FIELD_STRINGS, EnumField, FIELD_STRINGS_BY_LOCALE, FR_FIELD_STRINGS, FieldDomIdProvider, FieldPresentationProvider, FieldRenderer, FieldShell, FieldStringsProvider, FilePicker, FilePickerEntry, FillWithExample, HtmlPreview, ImageField, JsonView, ListField, Markdown, MethodForm, NumberField, ObjectField, OptionalToggle, PdfDownloadButton, ProseField, ResultEnvProvider, ResultField, ResultRoot, SUPPORTED_LOCALES, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, StuffViewer, Switch, TextField, ToggleGroup, ToggleGroupItem, UnknownField, actionShadowClass, checkedAccentClass, cn, enumLabeler, fieldControlClass, fieldLabel, fieldStringsFor, focusOutlineClass, headingClass, isThenable, linkTextClass, panelFlatShadowClass, panelRaisedShadowClass, panelSurfaceClass, renderHtmlPdf, resolveHtmlStorageUrls, saveInBrowser, selectedBorderClass, selectedFillClass, selectedFillStrongClass, useFieldDomId, useFieldPresentation, useFieldStrings, useMethodForm, useProseImages, useResolveRendition, useResolveShareUrl, useResolveUrl, useResolvedUrl };
8973
- //# sourceMappingURL=chunk-TD62R3P6.js.map
8974
- //# sourceMappingURL=chunk-TD62R3P6.js.map
9129
+ export { BooleanField, ConceptPill, DEFAULT_FIELD_STRINGS, DateField, DocumentField, EN_FIELD_STRINGS, EnumField, FIELD_STRINGS_BY_LOCALE, FR_FIELD_STRINGS, FieldDomIdProvider, FieldPresentationProvider, FieldRenderer, FieldShell, FieldStringsProvider, FilePicker, FilePickerEntry, FillWithExample, HtmlPreview, ImageField, JsonView, ListField, Markdown, MethodForm, NumberField, ObjectField, OptionalToggle, PdfDownloadButton, ProseField, ResultEnvProvider, ResultField, ResultRoot, SUPPORTED_LOCALES, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, StuffViewer, Switch, TextField, ToggleGroup, ToggleGroupItem, UnknownField, actionShadowClass, checkedAccentClass, cn, enumLabeler, fieldControlClass, fieldLabel, fieldStringsFor, focusOutlineClass, headingClass, isThenable, linkTextClass, panelFlatShadowClass, panelRaisedShadowClass, panelSurfaceClass, renderHtmlPdf, resolveHtmlStorageUrls, saveInBrowser, selectedBorderClass, selectedFillClass, selectedFillStrongClass, useFieldDomId, useFieldPresentation, useFieldStrings, useMethodForm, useProseImages, useResolveDocumentPdf, useResolveRendition, useResolveShareUrl, useResolveUrl, useResolvedUrl };
9130
+ //# sourceMappingURL=chunk-ROM2KLQD.js.map
9131
+ //# sourceMappingURL=chunk-ROM2KLQD.js.map