@pipelex/mthds-form 0.26.0 → 0.27.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.
- package/dist/{chunk-QWHUUMSB.js → chunk-4VAO6BDE.js} +4 -4
- package/dist/{chunk-QWHUUMSB.js.map → chunk-4VAO6BDE.js.map} +1 -1
- package/dist/{chunk-QINLJ5RM.js → chunk-56WECFBD.js} +6 -3
- package/dist/chunk-56WECFBD.js.map +1 -0
- package/dist/{chunk-RSYOZZZF.js → chunk-AZIUFSIQ.js} +141 -40
- package/dist/chunk-AZIUFSIQ.js.map +1 -0
- package/dist/{chunk-JSPF4BQP.js → chunk-QQK6Y55E.js} +96 -3
- package/dist/chunk-QQK6Y55E.js.map +1 -0
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.js +3 -3
- package/dist/core/native-content.d.ts +103 -1
- package/dist/core/native-content.js +1 -1
- package/dist/core/save-plan.js +3 -3
- package/dist/core/stuff-files.js +2 -2
- package/dist/generative/index.js +4 -4
- package/dist/react/index.d.ts +25 -2
- package/dist/react/index.js +4 -4
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/dist/chunk-JSPF4BQP.js.map +0 -1
- package/dist/chunk-QINLJ5RM.js.map +0 -1
- package/dist/chunk-RSYOZZZF.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { collectStuffFiles } from './chunk-
|
|
2
|
-
import { viewableUrl, readDataUrl, dataUrlExtensions } from './chunk-
|
|
1
|
+
import { collectStuffFiles } from './chunk-56WECFBD.js';
|
|
2
|
+
import { viewableUrl, readDataUrl, dataUrlExtensions } from './chunk-QQK6Y55E.js';
|
|
3
3
|
|
|
4
4
|
// src/core/save-plan.ts
|
|
5
5
|
function planStuffSave(field, value, options) {
|
|
@@ -136,5 +136,5 @@ function jsonCopy(value) {
|
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
export { planFileSave, planStuffSave, splitFileName, typedFileStem };
|
|
139
|
-
//# sourceMappingURL=chunk-
|
|
140
|
-
//# sourceMappingURL=chunk-
|
|
139
|
+
//# sourceMappingURL=chunk-4VAO6BDE.js.map
|
|
140
|
+
//# sourceMappingURL=chunk-4VAO6BDE.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/save-plan.ts"],"names":["extension"],"mappings":"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","file":"chunk-QWHUUMSB.js","sourcesContent":["import { dataUrlExtensions, readDataUrl, viewableUrl } from './native-content';\nimport { collectStuffFiles, type StuffFile, type StuffFileKind } from './stuff-files';\nimport type { RunField } from './descriptor';\n\n/**\n * What saving a result MEANS, decided with no DOM: which files, what each is\n * called, what type it is, and where its bytes are.\n *\n * ## Why the plan is separate from the delivery\n *\n * Handing bytes to a reader is the part that depends on where the view runs.\n * An ordinary browser tab saves through an object URL and a clicked link; a\n * view inside a sandboxed frame is refused all of that and has to ask its host\n * to do it, typically by passing links and inline contents across a bridge that\n * fetches the links itself. What to save is the same answer in both places, so\n * it is computed once, here, and a delivery only has to carry it out. The\n * default browser delivery lives in `./react`; a host supplies its own through\n * `ResultEnvProvider`'s `saveFiles`. See docs/result-view.md § \"Saving a\n * result\".\n *\n * ## Why a planned file carries a URL rather than bytes\n *\n * A host's download bridge takes links and fetches them itself, which is also\n * what keeps a cross-origin fetch out of a frame that cannot make one. So a\n * stored file is planned as the URL the gate admitted, and only the default\n * browser delivery fetches it. Content the stuff carries inline (the JSON copy\n * of the data, an HTML page) is planned as its text.\n */\n\n/** What a planned file is: one of the stuff's own files, or the JSON copy of its data. */\nexport type SaveFileKind = StuffFileKind | 'data';\n\ninterface SaveFileCommon {\n /**\n * What to save it as: a bare file name, never a path. The file's own\n * `filename` when it states one, else the base name and the file's place\n * below the result's root (`report-figures-0-image.png`), or the base name\n * alone for a file that is the whole result (`output.png`). No two files in\n * one plan share a name, compared without case.\n */\n name: string;\n /**\n * The media type. For a `data:` URL it is the type the gate admitted; for any\n * other URL it is what the payload states, or what the name's extension\n * implies, or `application/octet-stream` when nothing says.\n */\n mimeType: string;\n kind: SaveFileKind;\n /** Where it sat in the result, dotted (`output.figures.0.image`); the root's name for the JSON copy. */\n path: string;\n}\n\n/**\n * One file to hand to the reader: exactly one of `url` and `text` is set.\n *\n * `url` is the string the URL gate returned, after the host's resolver was\n * asked first, so a delivery never acts on a member nothing judged. It may be a\n * root-relative path, which a planner with no document cannot resolve; the\n * result view makes one absolute against its own document before a delivery\n * sees it. `text` is content the result carries inline.\n */\nexport type SaveFile =\n | (SaveFileCommon & { url: string; text?: undefined })\n | (SaveFileCommon & { text: string; url?: undefined });\n\n/** A file the result holds that cannot be saved, because no URL the gate admits was found for it. */\nexport interface UnavailableFile {\n /** What it would have been saved as, so the reader can be told which one. */\n name: string;\n kind: StuffFileKind;\n path: string;\n}\n\n/** Everything saving a whole result involves. */\nexport interface SavePlan {\n /** What to deliver, in the order the result shows it, the JSON copy last. */\n files: SaveFile[];\n /**\n * What the result holds and cannot be delivered. The JSON copy still carries\n * each one's reference, so the reader keeps it even without the bytes.\n */\n unavailable: UnavailableFile[];\n}\n\n/** A file a delivery did not hand over, and why when it knows. */\nexport interface SaveFailure {\n file: SaveFile;\n reason?: string;\n}\n\n/**\n * What a delivery reports. Every planned file not listed in `failed` was\n * delivered, so an empty list means everything went out.\n */\nexport interface SaveResult {\n failed: SaveFailure[];\n}\n\n/**\n * How files reach the reader. The default saves them in the browser; a host\n * whose view runs in a sandboxed frame supplies one that asks the host to.\n *\n * It resolves rather than rejects when a file does not go out, and names that\n * file in `failed`, so the control that asked can tell the reader. A throw or\n * a rejection is read as every file having failed.\n */\nexport type SaveFiles = (files: readonly SaveFile[]) => Promise<SaveResult>;\n\nexport interface SavePlanOptions {\n /**\n * Names the whole stuff: the JSON copy is `<baseName>.json`, a file with no\n * name of its own below the root is `<baseName>-<place>`, and a file that is\n * the whole stuff is `<baseName>.<ext>`.\n */\n baseName: string;\n /** The same resolver the result view paints through, asked before the payload's own URLs. */\n resolveUrl?: (url: string) => string | undefined;\n}\n\n/**\n * Everything a stuff contains, planned for saving.\n *\n * The data goes down as JSON, and any image, document or HTML page inside it\n * goes down as ITSELF: a JSON file holding a URL to a picture is not the\n * picture, and a reader who saves a report with three attachments wants the\n * three attachments.\n *\n * The JSON copy is planned whenever the stuff is anything more than one file. A\n * result that IS one image has nothing left over once the image is saved, so\n * `{\"url\": …}` beside it would be a second file saying less than the first. That\n * holds only when the image can be saved: a bare file the gate refuses leaves\n * the JSON copy as the one thing the reader gets, because it still carries the\n * reference.\n */\nexport function planStuffSave(field: RunField, value: unknown, options: SavePlanOptions): SavePlan {\n const found = collectStuffFiles(field, value);\n const files: SaveFile[] = [];\n const unavailable: UnavailableFile[] = [];\n for (const file of found) {\n const planned = planFileSave(file, options);\n if (planned) files.push(planned);\n else\n unavailable.push({ name: nameFor(file, options.baseName), kind: file.kind, path: file.path });\n }\n\n // A stuff that is nothing BUT one file, read from the collector rather than\n // from `field.kind`: a `native.Html` node's kind is `object`, and the whole\n // stuff is still the page.\n const isBareFile = found.length === 1 && found[0]?.path === field.name;\n if (isBareFile && files.length === 1) return { files, unavailable };\n\n const jsonName = `${safeName(options.baseName) || 'result'}.json`;\n return {\n files: [\n ...distinctNames(files, jsonName),\n {\n name: jsonName,\n mimeType: 'application/json',\n kind: 'data',\n path: field.name,\n text: jsonCopy(value),\n },\n ],\n unavailable,\n };\n}\n\n/**\n * The plan's files with no two sharing a name, and none taking the JSON copy's.\n *\n * A name derived from a file's place is unique by construction, but a name the\n * payload states is not: two documents a method wrote as `scan.pdf`, or one it\n * called `report.json` beside the JSON copy of `report`. A browser suffixes a\n * duplicate download itself, but a host's delivery may write the names as given,\n * into a folder or an archive, where the second file silently replaces the\n * first. So the first file keeps its name and each later one gets a number\n * before its extension (`scan-2.pdf`), compared without case, as the file\n * systems readers mostly use compare them.\n *\n * That is the whole plan's concern: a file's own button saves one file, which\n * collides with nothing, under its own name.\n */\nfunction distinctNames(files: readonly SaveFile[], reserved: string): SaveFile[] {\n const taken = new Set([reserved.toLowerCase()]);\n return files.map((file) => {\n let name = file.name;\n for (let count = 2; taken.has(name.toLowerCase()); count += 1) {\n name = numbered(file.name, count);\n }\n taken.add(name.toLowerCase());\n return name === file.name ? file : { ...file, name };\n });\n}\n\n/** `scan.pdf` as `scan-2.pdf`: the number before the extension, so the type still reads. */\nfunction numbered(name: string, count: number): string {\n const extension = extensionOf(name);\n return extension\n ? `${name.slice(0, -(extension.length + 1))}-${count}.${name.slice(-extension.length)}`\n : `${name}-${count}`;\n}\n\n/**\n * One file, planned on its own — what a file's own download button saves.\n *\n * The file's `path` starts at the root's name, as `collectStuffFiles` builds\n * it: the name leaves that first segment out, because the base name already\n * stands for the root.\n *\n * `undefined` when the file cannot be saved: a stored reference the host's\n * resolver does not answer and no URL the gate admits beside it. The button is\n * not drawn then, so a reader is never offered a download that cannot happen.\n */\nexport function planFileSave(file: StuffFile, options: SavePlanOptions): SaveFile | undefined {\n const common = { kind: file.kind, path: file.path };\n if (file.text !== undefined) {\n return {\n ...common,\n name: nameFor(file, options.baseName),\n mimeType: 'text/html',\n text: file.text,\n };\n }\n // Judged by the same gate the result view paints through, in the same\n // durability order: the host's resolver over the durable reference first,\n // then `public_url`, then the reference itself. What the gate returns is what\n // is planned, never the raw member.\n const url =\n viewableUrl(options.resolveUrl?.(file.url ?? '')) ??\n viewableUrl(file.publicUrl) ??\n viewableUrl(file.url);\n if (!url) return undefined;\n const encoded = readDataUrl(url);\n if (encoded) {\n // A `data:` URL is its own file, and the gate admitted it for its declared\n // type alone. That type names it: the payload's `filename` and the tail of\n // the payload are both the payload's say, and a `data:image/png` URL saved\n // under `image.html` is a page that runs when the reader opens it.\n return {\n ...common,\n name: nameFor(file, options.baseName, dataUrlExtensions(encoded.mediaType)),\n mimeType: encoded.mediaType,\n url,\n };\n }\n const name = nameFor(file, options.baseName);\n return { ...common, name, mimeType: file.mimeType ?? typeForName(name), url };\n}\n\n/** The type a name's extension implies, for a stored file whose payload states none. */\nconst TYPE_FOR_EXTENSION: Readonly<Record<string, string>> = {\n png: 'image/png',\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n gif: 'image/gif',\n webp: 'image/webp',\n avif: 'image/avif',\n svg: 'image/svg+xml',\n pdf: 'application/pdf',\n html: 'text/html',\n};\n\nfunction typeForName(name: string): string {\n const extension = name.match(/\\.([A-Za-z0-9]{1,8})$/)?.[1]?.toLowerCase();\n return (extension && TYPE_FOR_EXTENSION[extension]) || 'application/octet-stream';\n}\n\n/**\n * What a saved file is called.\n *\n * The file's own `filename` wins when it has one: it is the name the producer\n * chose and the reader recognises. Otherwise the base name and the file's place\n * below the root name it (`report-attachments-2`), which is unlovely but\n * unambiguous, since two images from one result must not both land as\n * `image.png` and overwrite each other in the download folder.\n *\n * The path's first segment is left out because it is always the root's name,\n * and the base name already stands for the root, as it does in `<base>.json`:\n * keeping it would save the one image a pipe outputs as `output-output.png`. A\n * file that IS the root has no place below it and is named by the base name\n * alone. Every segment is a field's name or an index, neither of which holds a\n * dot, so splitting on dots recovers them.\n *\n * `allowed` is set for a `data:` URL, whose admitted type is the only thing\n * allowed to decide the extension: a name carrying one of those extensions\n * keeps it, and any other extension is replaced by the type's own.\n */\nfunction nameFor(file: StuffFile, base: string, allowed?: readonly string[]): string {\n const own = file.filename ? safeName(file.filename) : '';\n const place = file.path.split('.').slice(1).join('-');\n const named = safeName(base) || 'result';\n const stem = own || (place ? `${named}-${place}` : named);\n const stated = own ? extensionOf(own) : '';\n if (allowed) {\n if (stated && allowed.includes(stated)) return stem;\n const bare = stated ? stem.slice(0, -(stated.length + 1)) : stem;\n const extension = allowed[0];\n return extension ? `${bare}.${extension}` : bare;\n }\n if (own) return own;\n const extension = file.extension ?? extensionOf(pathOf(file.url ?? ''));\n return extension ? `${stem}.${extension}` : stem;\n}\n\n/** The extension a name ends in, lowercased, or none. Never guessed from the bytes. */\nfunction extensionOf(name: string): string {\n return name.match(/\\.([A-Za-z0-9]{1,8})$/)?.[1]?.toLowerCase() ?? '';\n}\n\n/** A URL without its query and fragment, so `x.png?sig=…` still ends in `.png`. */\nfunction pathOf(url: string): string {\n return url.split(/[?#]/)[0] ?? url;\n}\n\n/**\n * A name safe to hand a delivery: its last path segment, without control\n * characters, trimmed. A browser sanitises a download name itself, but a host's\n * bridge may write it to disk as given, and a payload's `filename` is the\n * payload's say. Empty when nothing is left.\n */\nfunction safeName(name: string): string {\n const trimmed = (printable(name).split(/[/\\\\]/).pop() ?? '').trim();\n return trimmed === '.' || trimmed === '..' ? '' : trimmed;\n}\n\n/** The name without its control characters, which no file system wants in a name. */\nfunction printable(name: string): string {\n return Array.from(name)\n .filter((character) => {\n const code = character.charCodeAt(0);\n return code > 0x1f && code !== 0x7f;\n })\n .join('');\n}\n\n/**\n * A planned name split at its extension, the way the plan reads one:\n * `devis.pdf` is `devis` and `.pdf`, `report.final.html` is `report.final` and\n * `.html`, and a name with no extension of one to eight letters and digits is\n * all stem. The extension keeps its dot, so a name rebuilt from the two halves\n * is the name.\n */\nexport function splitFileName(name: string): { stem: string; extension: string } {\n const extension = name.match(/\\.[A-Za-z0-9]{1,8}$/)?.[0] ?? '';\n return extension && extension.length < name.length\n ? { stem: name.slice(0, -extension.length), extension }\n : { stem: name, extension: '' };\n}\n\n/**\n * The name a reader typed for a file, made safe the way a planned name is, and\n * a little stricter, since this one is typed rather than produced.\n *\n * Control characters go, as from every planned name. A path separator is\n * REPLACED rather than cut at: a planned name keeps its last path segment\n * because a payload's `filename` may be a path, but a reader who types\n * `Devis 12/2026` means a name, and keeping `2026` alone would throw away what\n * they wrote. The characters a common file system refuses in a name\n * (`: * ? \" < > |`) are replaced the same way, with a hyphen, because a host's\n * bridge may write the name to disk as given. Leading dots go, so the file is\n * not hidden, and trailing dots and spaces, which one file system drops\n * silently. A copy of the extension the reader typed at the end is dropped,\n * since the extension is added back after it: `devis.pdf` stays `devis.pdf`\n * rather than becoming `devis.pdf.pdf`.\n *\n * Empty when nothing is left, which the caller reads as \"keep the planned name\".\n */\nexport function typedFileStem(typed: string, extension: string): string {\n let stem = printable(typed)\n .replace(/[/\\\\:*?\"<>|]/g, '-')\n .trim();\n if (extension && stem.toLowerCase().endsWith(extension.toLowerCase())) {\n stem = stem.slice(0, -extension.length);\n }\n return stem\n .replace(/^[.\\s]+/, '')\n .replace(/[.\\s]+$/, '')\n .trim();\n}\n\n/**\n * The data as JSON, the way the JSON view shows it. A BigInt, which a JSON\n * parser configured to produce one can hand over, is written as its digits\n * rather than failing the whole save.\n */\nfunction jsonCopy(value: unknown): string {\n const text = JSON.stringify(\n value,\n (_key, member: unknown) => (typeof member === 'bigint' ? member.toString() : member),\n 2,\n );\n return text ?? 'null';\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/core/save-plan.ts"],"names":["extension"],"mappings":";;;;AAsIO,SAAS,aAAA,CAAc,KAAA,EAAiB,KAAA,EAAgB,OAAA,EAAoC;AACjG,EAAA,MAAM,KAAA,GAAQ,iBAAA,CAAkB,KAAA,EAAO,KAAK,CAAA;AAC5C,EAAA,MAAM,QAAoB,EAAC;AAC3B,EAAA,MAAM,cAAiC,EAAC;AACxC,EAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,IAAA,MAAM,OAAA,GAAU,YAAA,CAAa,IAAA,EAAM,OAAO,CAAA;AAC1C,IAAA,IAAI,OAAA,EAAS,KAAA,CAAM,IAAA,CAAK,OAAO,CAAA;AAAA;AAE7B,MAAA,WAAA,CAAY,IAAA,CAAK,EAAE,IAAA,EAAM,OAAA,CAAQ,MAAM,OAAA,CAAQ,QAAQ,CAAA,EAAG,IAAA,EAAM,IAAA,CAAK,IAAA,EAAM,IAAA,EAAM,IAAA,CAAK,MAAM,CAAA;AAAA,EAChG;AAKA,EAAA,MAAM,UAAA,GAAa,MAAM,MAAA,KAAW,CAAA,IAAK,MAAM,CAAC,CAAA,EAAG,SAAS,KAAA,CAAM,IAAA;AAClE,EAAA,IAAI,cAAc,KAAA,CAAM,MAAA,KAAW,GAAG,OAAO,EAAE,OAAO,WAAA,EAAY;AAElE,EAAA,MAAM,WAAW,CAAA,EAAG,QAAA,CAAS,OAAA,CAAQ,QAAQ,KAAK,QAAQ,CAAA,KAAA,CAAA;AAC1D,EAAA,OAAO;AAAA,IACL,KAAA,EAAO;AAAA,MACL,GAAG,aAAA,CAAc,KAAA,EAAO,QAAQ,CAAA;AAAA,MAChC;AAAA,QACE,IAAA,EAAM,QAAA;AAAA,QACN,QAAA,EAAU,kBAAA;AAAA,QACV,IAAA,EAAM,MAAA;AAAA,QACN,MAAM,KAAA,CAAM,IAAA;AAAA,QACZ,IAAA,EAAM,SAAS,KAAK;AAAA;AACtB,KACF;AAAA,IACA;AAAA,GACF;AACF;AAiBA,SAAS,aAAA,CAAc,OAA4B,QAAA,EAA8B;AAC/E,EAAA,MAAM,wBAAQ,IAAI,GAAA,CAAI,CAAC,QAAA,CAAS,WAAA,EAAa,CAAC,CAAA;AAC9C,EAAA,OAAO,KAAA,CAAM,GAAA,CAAI,CAAC,IAAA,KAAS;AACzB,IAAA,IAAI,OAAO,IAAA,CAAK,IAAA;AAChB,IAAA,KAAA,IAAS,KAAA,GAAQ,GAAG,KAAA,CAAM,GAAA,CAAI,KAAK,WAAA,EAAa,CAAA,EAAG,KAAA,IAAS,CAAA,EAAG;AAC7D,MAAA,IAAA,GAAO,QAAA,CAAS,IAAA,CAAK,IAAA,EAAM,KAAK,CAAA;AAAA,IAClC;AACA,IAAA,KAAA,CAAM,GAAA,CAAI,IAAA,CAAK,WAAA,EAAa,CAAA;AAC5B,IAAA,OAAO,SAAS,IAAA,CAAK,IAAA,GAAO,OAAO,EAAE,GAAG,MAAM,IAAA,EAAK;AAAA,EACrD,CAAC,CAAA;AACH;AAGA,SAAS,QAAA,CAAS,MAAc,KAAA,EAAuB;AACrD,EAAA,MAAM,SAAA,GAAY,YAAY,IAAI,CAAA;AAClC,EAAA,OAAO,SAAA,GACH,GAAG,IAAA,CAAK,KAAA,CAAM,GAAG,EAAE,SAAA,CAAU,MAAA,GAAS,CAAA,CAAE,CAAC,CAAA,CAAA,EAAI,KAAK,CAAA,CAAA,EAAI,IAAA,CAAK,KAAA,CAAM,CAAC,SAAA,CAAU,MAAM,CAAC,CAAA,CAAA,GACnF,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,KAAK,CAAA,CAAA;AACtB;AAaO,SAAS,YAAA,CAAa,MAAiB,OAAA,EAAgD;AAC5F,EAAA,MAAM,SAAS,EAAE,IAAA,EAAM,KAAK,IAAA,EAAM,IAAA,EAAM,KAAK,IAAA,EAAK;AAClD,EAAA,IAAI,IAAA,CAAK,SAAS,MAAA,EAAW;AAC3B,IAAA,OAAO;AAAA,MACL,GAAG,MAAA;AAAA,MACH,IAAA,EAAM,OAAA,CAAQ,IAAA,EAAM,OAAA,CAAQ,QAAQ,CAAA;AAAA,MACpC,QAAA,EAAU,WAAA;AAAA,MACV,MAAM,IAAA,CAAK;AAAA,KACb;AAAA,EACF;AAKA,EAAA,MAAM,GAAA,GACJ,WAAA,CAAY,OAAA,CAAQ,UAAA,GAAa,KAAK,GAAA,IAAO,EAAE,CAAC,CAAA,IAChD,YAAY,IAAA,CAAK,SAAS,CAAA,IAC1B,WAAA,CAAY,KAAK,GAAG,CAAA;AACtB,EAAA,IAAI,CAAC,KAAK,OAAO,MAAA;AACjB,EAAA,MAAM,OAAA,GAAU,YAAY,GAAG,CAAA;AAC/B,EAAA,IAAI,OAAA,EAAS;AAKX,IAAA,OAAO;AAAA,MACL,GAAG,MAAA;AAAA,MACH,IAAA,EAAM,QAAQ,IAAA,EAAM,OAAA,CAAQ,UAAU,iBAAA,CAAkB,OAAA,CAAQ,SAAS,CAAC,CAAA;AAAA,MAC1E,UAAU,OAAA,CAAQ,SAAA;AAAA,MAClB;AAAA,KACF;AAAA,EACF;AACA,EAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,IAAA,EAAM,OAAA,CAAQ,QAAQ,CAAA;AAC3C,EAAA,OAAO,EAAE,GAAG,MAAA,EAAQ,IAAA,EAAM,QAAA,EAAU,KAAK,QAAA,IAAY,WAAA,CAAY,IAAI,CAAA,EAAG,GAAA,EAAI;AAC9E;AAGA,IAAM,kBAAA,GAAuD;AAAA,EAC3D,GAAA,EAAK,WAAA;AAAA,EACL,GAAA,EAAK,YAAA;AAAA,EACL,IAAA,EAAM,YAAA;AAAA,EACN,GAAA,EAAK,WAAA;AAAA,EACL,IAAA,EAAM,YAAA;AAAA,EACN,IAAA,EAAM,YAAA;AAAA,EACN,GAAA,EAAK,eAAA;AAAA,EACL,GAAA,EAAK,iBAAA;AAAA,EACL,IAAA,EAAM;AACR,CAAA;AAEA,SAAS,YAAY,IAAA,EAAsB;AACzC,EAAA,MAAM,YAAY,IAAA,CAAK,KAAA,CAAM,uBAAuB,CAAA,GAAI,CAAC,GAAG,WAAA,EAAY;AACxE,EAAA,OAAQ,SAAA,IAAa,kBAAA,CAAmB,SAAS,CAAA,IAAM,0BAAA;AACzD;AAsBA,SAAS,OAAA,CAAQ,IAAA,EAAiB,IAAA,EAAc,OAAA,EAAqC;AACnF,EAAA,MAAM,MAAM,IAAA,CAAK,QAAA,GAAW,QAAA,CAAS,IAAA,CAAK,QAAQ,CAAA,GAAI,EAAA;AACtD,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,IAAA,CAAK,KAAA,CAAM,GAAG,EAAE,KAAA,CAAM,CAAC,CAAA,CAAE,IAAA,CAAK,GAAG,CAAA;AACpD,EAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,IAAI,CAAA,IAAK,QAAA;AAChC,EAAA,MAAM,OAAO,GAAA,KAAQ,KAAA,GAAQ,GAAG,KAAK,CAAA,CAAA,EAAI,KAAK,CAAA,CAAA,GAAK,KAAA,CAAA;AACnD,EAAA,MAAM,MAAA,GAAS,GAAA,GAAM,WAAA,CAAY,GAAG,CAAA,GAAI,EAAA;AACxC,EAAA,IAAI,OAAA,EAAS;AACX,IAAA,IAAI,MAAA,IAAU,OAAA,CAAQ,QAAA,CAAS,MAAM,GAAG,OAAO,IAAA;AAC/C,IAAA,MAAM,IAAA,GAAO,SAAS,IAAA,CAAK,KAAA,CAAM,GAAG,EAAE,MAAA,CAAO,MAAA,GAAS,CAAA,CAAE,CAAA,GAAI,IAAA;AAC5D,IAAA,MAAMA,UAAAA,GAAY,QAAQ,CAAC,CAAA;AAC3B,IAAA,OAAOA,UAAAA,GAAY,CAAA,EAAG,IAAI,CAAA,CAAA,EAAIA,UAAS,CAAA,CAAA,GAAK,IAAA;AAAA,EAC9C;AACA,EAAA,IAAI,KAAK,OAAO,GAAA;AAChB,EAAA,MAAM,SAAA,GAAY,KAAK,SAAA,IAAa,WAAA,CAAY,OAAO,IAAA,CAAK,GAAA,IAAO,EAAE,CAAC,CAAA;AACtE,EAAA,OAAO,SAAA,GAAY,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,SAAS,CAAA,CAAA,GAAK,IAAA;AAC9C;AAGA,SAAS,YAAY,IAAA,EAAsB;AACzC,EAAA,OAAO,KAAK,KAAA,CAAM,uBAAuB,IAAI,CAAC,CAAA,EAAG,aAAY,IAAK,EAAA;AACpE;AAGA,SAAS,OAAO,GAAA,EAAqB;AACnC,EAAA,OAAO,GAAA,CAAI,KAAA,CAAM,MAAM,CAAA,CAAE,CAAC,CAAA,IAAK,GAAA;AACjC;AAQA,SAAS,SAAS,IAAA,EAAsB;AACtC,EAAA,MAAM,OAAA,GAAA,CAAW,SAAA,CAAU,IAAI,CAAA,CAAE,KAAA,CAAM,OAAO,CAAA,CAAE,GAAA,EAAI,IAAK,EAAA,EAAI,IAAA,EAAK;AAClE,EAAA,OAAO,OAAA,KAAY,GAAA,IAAO,OAAA,KAAY,IAAA,GAAO,EAAA,GAAK,OAAA;AACpD;AAGA,SAAS,UAAU,IAAA,EAAsB;AACvC,EAAA,OAAO,MAAM,IAAA,CAAK,IAAI,CAAA,CACnB,MAAA,CAAO,CAAC,SAAA,KAAc;AACrB,IAAA,MAAM,IAAA,GAAO,SAAA,CAAU,UAAA,CAAW,CAAC,CAAA;AACnC,IAAA,OAAO,IAAA,GAAO,MAAQ,IAAA,KAAS,GAAA;AAAA,EACjC,CAAC,CAAA,CACA,IAAA,CAAK,EAAE,CAAA;AACZ;AASO,SAAS,cAAc,IAAA,EAAmD;AAC/E,EAAA,MAAM,YAAY,IAAA,CAAK,KAAA,CAAM,qBAAqB,CAAA,GAAI,CAAC,CAAA,IAAK,EAAA;AAC5D,EAAA,OAAO,SAAA,IAAa,UAAU,MAAA,GAAS,IAAA,CAAK,SACxC,EAAE,IAAA,EAAM,KAAK,KAAA,CAAM,CAAA,EAAG,CAAC,SAAA,CAAU,MAAM,GAAG,SAAA,EAAU,GACpD,EAAE,IAAA,EAAM,IAAA,EAAM,WAAW,EAAA,EAAG;AAClC;AAoBO,SAAS,aAAA,CAAc,OAAe,SAAA,EAA2B;AACtE,EAAA,IAAI,IAAA,GAAO,UAAU,KAAK,CAAA,CACvB,QAAQ,eAAA,EAAiB,GAAG,EAC5B,IAAA,EAAK;AACR,EAAA,IAAI,SAAA,IAAa,KAAK,WAAA,EAAY,CAAE,SAAS,SAAA,CAAU,WAAA,EAAa,CAAA,EAAG;AACrE,IAAA,IAAA,GAAO,IAAA,CAAK,KAAA,CAAM,CAAA,EAAG,CAAC,UAAU,MAAM,CAAA;AAAA,EACxC;AACA,EAAA,OAAO,IAAA,CACJ,QAAQ,SAAA,EAAW,EAAE,EACrB,OAAA,CAAQ,SAAA,EAAW,EAAE,CAAA,CACrB,IAAA,EAAK;AACV;AAOA,SAAS,SAAS,KAAA,EAAwB;AACxC,EAAA,MAAM,OAAO,IAAA,CAAK,SAAA;AAAA,IAChB,KAAA;AAAA,IACA,CAAC,MAAM,MAAA,KAAqB,OAAO,WAAW,QAAA,GAAW,MAAA,CAAO,UAAS,GAAI,MAAA;AAAA,IAC7E;AAAA,GACF;AACA,EAAA,OAAO,IAAA,IAAQ,MAAA;AACjB","file":"chunk-4VAO6BDE.js","sourcesContent":["import { dataUrlExtensions, readDataUrl, viewableUrl } from './native-content';\nimport { collectStuffFiles, type StuffFile, type StuffFileKind } from './stuff-files';\nimport type { RunField } from './descriptor';\n\n/**\n * What saving a result MEANS, decided with no DOM: which files, what each is\n * called, what type it is, and where its bytes are.\n *\n * ## Why the plan is separate from the delivery\n *\n * Handing bytes to a reader is the part that depends on where the view runs.\n * An ordinary browser tab saves through an object URL and a clicked link; a\n * view inside a sandboxed frame is refused all of that and has to ask its host\n * to do it, typically by passing links and inline contents across a bridge that\n * fetches the links itself. What to save is the same answer in both places, so\n * it is computed once, here, and a delivery only has to carry it out. The\n * default browser delivery lives in `./react`; a host supplies its own through\n * `ResultEnvProvider`'s `saveFiles`. See docs/result-view.md § \"Saving a\n * result\".\n *\n * ## Why a planned file carries a URL rather than bytes\n *\n * A host's download bridge takes links and fetches them itself, which is also\n * what keeps a cross-origin fetch out of a frame that cannot make one. So a\n * stored file is planned as the URL the gate admitted, and only the default\n * browser delivery fetches it. Content the stuff carries inline (the JSON copy\n * of the data, an HTML page) is planned as its text.\n */\n\n/** What a planned file is: one of the stuff's own files, or the JSON copy of its data. */\nexport type SaveFileKind = StuffFileKind | 'data';\n\ninterface SaveFileCommon {\n /**\n * What to save it as: a bare file name, never a path. The file's own\n * `filename` when it states one, else the base name and the file's place\n * below the result's root (`report-figures-0-image.png`), or the base name\n * alone for a file that is the whole result (`output.png`). No two files in\n * one plan share a name, compared without case.\n */\n name: string;\n /**\n * The media type. For a `data:` URL it is the type the gate admitted; for any\n * other URL it is what the payload states, or what the name's extension\n * implies, or `application/octet-stream` when nothing says.\n */\n mimeType: string;\n kind: SaveFileKind;\n /** Where it sat in the result, dotted (`output.figures.0.image`); the root's name for the JSON copy. */\n path: string;\n}\n\n/**\n * One file to hand to the reader: exactly one of `url` and `text` is set.\n *\n * `url` is the string the URL gate returned, after the host's resolver was\n * asked first, so a delivery never acts on a member nothing judged. It may be a\n * root-relative path, which a planner with no document cannot resolve; the\n * result view makes one absolute against its own document before a delivery\n * sees it. `text` is content the result carries inline.\n */\nexport type SaveFile =\n | (SaveFileCommon & { url: string; text?: undefined })\n | (SaveFileCommon & { text: string; url?: undefined });\n\n/** A file the result holds that cannot be saved, because no URL the gate admits was found for it. */\nexport interface UnavailableFile {\n /** What it would have been saved as, so the reader can be told which one. */\n name: string;\n kind: StuffFileKind;\n path: string;\n}\n\n/** Everything saving a whole result involves. */\nexport interface SavePlan {\n /** What to deliver, in the order the result shows it, the JSON copy last. */\n files: SaveFile[];\n /**\n * What the result holds and cannot be delivered. The JSON copy still carries\n * each one's reference, so the reader keeps it even without the bytes.\n */\n unavailable: UnavailableFile[];\n}\n\n/** A file a delivery did not hand over, and why when it knows. */\nexport interface SaveFailure {\n file: SaveFile;\n reason?: string;\n}\n\n/**\n * What a delivery reports. Every planned file not listed in `failed` was\n * delivered, so an empty list means everything went out.\n */\nexport interface SaveResult {\n failed: SaveFailure[];\n}\n\n/**\n * How files reach the reader. The default saves them in the browser; a host\n * whose view runs in a sandboxed frame supplies one that asks the host to.\n *\n * It resolves rather than rejects when a file does not go out, and names that\n * file in `failed`, so the control that asked can tell the reader. A throw or\n * a rejection is read as every file having failed.\n */\nexport type SaveFiles = (files: readonly SaveFile[]) => Promise<SaveResult>;\n\nexport interface SavePlanOptions {\n /**\n * Names the whole stuff: the JSON copy is `<baseName>.json`, a file with no\n * name of its own below the root is `<baseName>-<place>`, and a file that is\n * the whole stuff is `<baseName>.<ext>`.\n */\n baseName: string;\n /** The same resolver the result view paints through, asked before the payload's own URLs. */\n resolveUrl?: (url: string) => string | undefined;\n}\n\n/**\n * Everything a stuff contains, planned for saving.\n *\n * The data goes down as JSON, and any image, document or HTML page inside it\n * goes down as ITSELF: a JSON file holding a URL to a picture is not the\n * picture, and a reader who saves a report with three attachments wants the\n * three attachments.\n *\n * The JSON copy is planned whenever the stuff is anything more than one file. A\n * result that IS one image has nothing left over once the image is saved, so\n * `{\"url\": …}` beside it would be a second file saying less than the first. That\n * holds only when the image can be saved: a bare file the gate refuses leaves\n * the JSON copy as the one thing the reader gets, because it still carries the\n * reference.\n */\nexport function planStuffSave(field: RunField, value: unknown, options: SavePlanOptions): SavePlan {\n const found = collectStuffFiles(field, value);\n const files: SaveFile[] = [];\n const unavailable: UnavailableFile[] = [];\n for (const file of found) {\n const planned = planFileSave(file, options);\n if (planned) files.push(planned);\n else\n unavailable.push({ name: nameFor(file, options.baseName), kind: file.kind, path: file.path });\n }\n\n // A stuff that is nothing BUT one file, read from the collector rather than\n // from `field.kind`: a `native.Html` node's kind is `object`, and the whole\n // stuff is still the page.\n const isBareFile = found.length === 1 && found[0]?.path === field.name;\n if (isBareFile && files.length === 1) return { files, unavailable };\n\n const jsonName = `${safeName(options.baseName) || 'result'}.json`;\n return {\n files: [\n ...distinctNames(files, jsonName),\n {\n name: jsonName,\n mimeType: 'application/json',\n kind: 'data',\n path: field.name,\n text: jsonCopy(value),\n },\n ],\n unavailable,\n };\n}\n\n/**\n * The plan's files with no two sharing a name, and none taking the JSON copy's.\n *\n * A name derived from a file's place is unique by construction, but a name the\n * payload states is not: two documents a method wrote as `scan.pdf`, or one it\n * called `report.json` beside the JSON copy of `report`. A browser suffixes a\n * duplicate download itself, but a host's delivery may write the names as given,\n * into a folder or an archive, where the second file silently replaces the\n * first. So the first file keeps its name and each later one gets a number\n * before its extension (`scan-2.pdf`), compared without case, as the file\n * systems readers mostly use compare them.\n *\n * That is the whole plan's concern: a file's own button saves one file, which\n * collides with nothing, under its own name.\n */\nfunction distinctNames(files: readonly SaveFile[], reserved: string): SaveFile[] {\n const taken = new Set([reserved.toLowerCase()]);\n return files.map((file) => {\n let name = file.name;\n for (let count = 2; taken.has(name.toLowerCase()); count += 1) {\n name = numbered(file.name, count);\n }\n taken.add(name.toLowerCase());\n return name === file.name ? file : { ...file, name };\n });\n}\n\n/** `scan.pdf` as `scan-2.pdf`: the number before the extension, so the type still reads. */\nfunction numbered(name: string, count: number): string {\n const extension = extensionOf(name);\n return extension\n ? `${name.slice(0, -(extension.length + 1))}-${count}.${name.slice(-extension.length)}`\n : `${name}-${count}`;\n}\n\n/**\n * One file, planned on its own — what a file's own download button saves.\n *\n * The file's `path` starts at the root's name, as `collectStuffFiles` builds\n * it: the name leaves that first segment out, because the base name already\n * stands for the root.\n *\n * `undefined` when the file cannot be saved: a stored reference the host's\n * resolver does not answer and no URL the gate admits beside it. The button is\n * not drawn then, so a reader is never offered a download that cannot happen.\n */\nexport function planFileSave(file: StuffFile, options: SavePlanOptions): SaveFile | undefined {\n const common = { kind: file.kind, path: file.path };\n if (file.text !== undefined) {\n return {\n ...common,\n name: nameFor(file, options.baseName),\n mimeType: 'text/html',\n text: file.text,\n };\n }\n // Judged by the same gate the result view paints through, in the same\n // durability order: the host's resolver over the durable reference first,\n // then `public_url`, then the reference itself. What the gate returns is what\n // is planned, never the raw member.\n const url =\n viewableUrl(options.resolveUrl?.(file.url ?? '')) ??\n viewableUrl(file.publicUrl) ??\n viewableUrl(file.url);\n if (!url) return undefined;\n const encoded = readDataUrl(url);\n if (encoded) {\n // A `data:` URL is its own file, and the gate admitted it for its declared\n // type alone. That type names it: the payload's `filename` and the tail of\n // the payload are both the payload's say, and a `data:image/png` URL saved\n // under `image.html` is a page that runs when the reader opens it.\n return {\n ...common,\n name: nameFor(file, options.baseName, dataUrlExtensions(encoded.mediaType)),\n mimeType: encoded.mediaType,\n url,\n };\n }\n const name = nameFor(file, options.baseName);\n return { ...common, name, mimeType: file.mimeType ?? typeForName(name), url };\n}\n\n/** The type a name's extension implies, for a stored file whose payload states none. */\nconst TYPE_FOR_EXTENSION: Readonly<Record<string, string>> = {\n png: 'image/png',\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n gif: 'image/gif',\n webp: 'image/webp',\n avif: 'image/avif',\n svg: 'image/svg+xml',\n pdf: 'application/pdf',\n html: 'text/html',\n};\n\nfunction typeForName(name: string): string {\n const extension = name.match(/\\.([A-Za-z0-9]{1,8})$/)?.[1]?.toLowerCase();\n return (extension && TYPE_FOR_EXTENSION[extension]) || 'application/octet-stream';\n}\n\n/**\n * What a saved file is called.\n *\n * The file's own `filename` wins when it has one: it is the name the producer\n * chose and the reader recognises. Otherwise the base name and the file's place\n * below the root name it (`report-attachments-2`), which is unlovely but\n * unambiguous, since two images from one result must not both land as\n * `image.png` and overwrite each other in the download folder.\n *\n * The path's first segment is left out because it is always the root's name,\n * and the base name already stands for the root, as it does in `<base>.json`:\n * keeping it would save the one image a pipe outputs as `output-output.png`. A\n * file that IS the root has no place below it and is named by the base name\n * alone. Every segment is a field's name or an index, neither of which holds a\n * dot, so splitting on dots recovers them.\n *\n * `allowed` is set for a `data:` URL, whose admitted type is the only thing\n * allowed to decide the extension: a name carrying one of those extensions\n * keeps it, and any other extension is replaced by the type's own.\n */\nfunction nameFor(file: StuffFile, base: string, allowed?: readonly string[]): string {\n const own = file.filename ? safeName(file.filename) : '';\n const place = file.path.split('.').slice(1).join('-');\n const named = safeName(base) || 'result';\n const stem = own || (place ? `${named}-${place}` : named);\n const stated = own ? extensionOf(own) : '';\n if (allowed) {\n if (stated && allowed.includes(stated)) return stem;\n const bare = stated ? stem.slice(0, -(stated.length + 1)) : stem;\n const extension = allowed[0];\n return extension ? `${bare}.${extension}` : bare;\n }\n if (own) return own;\n const extension = file.extension ?? extensionOf(pathOf(file.url ?? ''));\n return extension ? `${stem}.${extension}` : stem;\n}\n\n/** The extension a name ends in, lowercased, or none. Never guessed from the bytes. */\nfunction extensionOf(name: string): string {\n return name.match(/\\.([A-Za-z0-9]{1,8})$/)?.[1]?.toLowerCase() ?? '';\n}\n\n/** A URL without its query and fragment, so `x.png?sig=…` still ends in `.png`. */\nfunction pathOf(url: string): string {\n return url.split(/[?#]/)[0] ?? url;\n}\n\n/**\n * A name safe to hand a delivery: its last path segment, without control\n * characters, trimmed. A browser sanitises a download name itself, but a host's\n * bridge may write it to disk as given, and a payload's `filename` is the\n * payload's say. Empty when nothing is left.\n */\nfunction safeName(name: string): string {\n const trimmed = (printable(name).split(/[/\\\\]/).pop() ?? '').trim();\n return trimmed === '.' || trimmed === '..' ? '' : trimmed;\n}\n\n/** The name without its control characters, which no file system wants in a name. */\nfunction printable(name: string): string {\n return Array.from(name)\n .filter((character) => {\n const code = character.charCodeAt(0);\n return code > 0x1f && code !== 0x7f;\n })\n .join('');\n}\n\n/**\n * A planned name split at its extension, the way the plan reads one:\n * `devis.pdf` is `devis` and `.pdf`, `report.final.html` is `report.final` and\n * `.html`, and a name with no extension of one to eight letters and digits is\n * all stem. The extension keeps its dot, so a name rebuilt from the two halves\n * is the name.\n */\nexport function splitFileName(name: string): { stem: string; extension: string } {\n const extension = name.match(/\\.[A-Za-z0-9]{1,8}$/)?.[0] ?? '';\n return extension && extension.length < name.length\n ? { stem: name.slice(0, -extension.length), extension }\n : { stem: name, extension: '' };\n}\n\n/**\n * The name a reader typed for a file, made safe the way a planned name is, and\n * a little stricter, since this one is typed rather than produced.\n *\n * Control characters go, as from every planned name. A path separator is\n * REPLACED rather than cut at: a planned name keeps its last path segment\n * because a payload's `filename` may be a path, but a reader who types\n * `Devis 12/2026` means a name, and keeping `2026` alone would throw away what\n * they wrote. The characters a common file system refuses in a name\n * (`: * ? \" < > |`) are replaced the same way, with a hyphen, because a host's\n * bridge may write the name to disk as given. Leading dots go, so the file is\n * not hidden, and trailing dots and spaces, which one file system drops\n * silently. A copy of the extension the reader typed at the end is dropped,\n * since the extension is added back after it: `devis.pdf` stays `devis.pdf`\n * rather than becoming `devis.pdf.pdf`.\n *\n * Empty when nothing is left, which the caller reads as \"keep the planned name\".\n */\nexport function typedFileStem(typed: string, extension: string): string {\n let stem = printable(typed)\n .replace(/[/\\\\:*?\"<>|]/g, '-')\n .trim();\n if (extension && stem.toLowerCase().endsWith(extension.toLowerCase())) {\n stem = stem.slice(0, -extension.length);\n }\n return stem\n .replace(/^[.\\s]+/, '')\n .replace(/[.\\s]+$/, '')\n .trim();\n}\n\n/**\n * The data as JSON, the way the JSON view shows it. A BigInt, which a JSON\n * parser configured to produce one can hand over, is written as its digits\n * rather than failing the whole save.\n */\nfunction jsonCopy(value: unknown): string {\n const text = JSON.stringify(\n value,\n (_key, member: unknown) => (typeof member === 'bigint' ? member.toString() : member),\n 2,\n );\n return text ?? 'null';\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readHtmlContent, readImageContent, readDocumentContent, isNativeHtmlNode } from './chunk-
|
|
1
|
+
import { readHtmlContent, readImageContent, readDocumentContent, isNativeHtmlNode, isNativeSearchResultNode } from './chunk-QQK6Y55E.js';
|
|
2
2
|
import { ownProp } from './chunk-V3OV2XQT.js';
|
|
3
3
|
|
|
4
4
|
// src/core/stuff-files.ts
|
|
@@ -41,7 +41,9 @@ function walk(field, raw, path, out) {
|
|
|
41
41
|
case "object": {
|
|
42
42
|
if (typeof value !== "object" || value === null || Array.isArray(value)) return;
|
|
43
43
|
const record = value;
|
|
44
|
+
const skipped = isNativeSearchResultNode(field) ? SEARCH_SOURCES : void 0;
|
|
44
45
|
for (const child of field.fields) {
|
|
46
|
+
if (child.name === skipped) continue;
|
|
45
47
|
walk(child, ownProp(record, child.name), [...here, child.name], out);
|
|
46
48
|
}
|
|
47
49
|
return;
|
|
@@ -55,6 +57,7 @@ function walk(field, raw, path, out) {
|
|
|
55
57
|
return;
|
|
56
58
|
}
|
|
57
59
|
}
|
|
60
|
+
var SEARCH_SOURCES = "sources";
|
|
58
61
|
function unwrapContent(field, value) {
|
|
59
62
|
if (!field.contentKey) return value;
|
|
60
63
|
if (typeof value !== "object" || value === null || Array.isArray(value)) return value;
|
|
@@ -63,5 +66,5 @@ function unwrapContent(field, value) {
|
|
|
63
66
|
}
|
|
64
67
|
|
|
65
68
|
export { collectStuffFiles, readStuffFile };
|
|
66
|
-
//# sourceMappingURL=chunk-
|
|
67
|
-
//# sourceMappingURL=chunk-
|
|
69
|
+
//# sourceMappingURL=chunk-56WECFBD.js.map
|
|
70
|
+
//# sourceMappingURL=chunk-56WECFBD.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/stuff-files.ts"],"names":["content"],"mappings":"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","file":"chunk-56WECFBD.js","sourcesContent":["import { ownProp } from './own-property';\nimport {\n isNativeHtmlNode,\n isNativeSearchResultNode,\n readDocumentContent,\n readHtmlContent,\n readImageContent,\n} from './native-content';\nimport type { RunField } from './descriptor';\n\n/**\n * One file a stuff carries: what to fetch, and what to call it once saved.\n */\nexport interface StuffFile {\n /**\n * The durable reference or URL the value states, for a file held BY\n * REFERENCE. Absent for inline content, which needs no fetch.\n */\n url?: string;\n /**\n * The content itself, for a file the stuff carries INLINE — markup, today.\n * Exactly one of `url` and `text` is set.\n */\n text?: string;\n /** The extension to save inline content under, without the dot. */\n extension?: string;\n /**\n * The URL that works outside this session, when the value carries one. A host\n * whose display URL is session-scoped (an owner-checked proxy route) needs\n * this for anything that leaves the browser.\n */\n publicUrl?: string;\n /** The file's own name when it states one — otherwise the caller derives it. */\n filename?: string;\n mimeType?: string;\n /**\n * Where it sat, dotted, starting at the root's name:\n * `report.attachments.2.scan`. A saved file with no name of its own is named\n * by the base name and the segments after the first (`report-attachments-2-scan`),\n * since the base name stands for the root, so a hand-built path must start at\n * the root's name too, or its first real segment is lost.\n */\n path: string;\n kind: StuffFileKind;\n}\n\n/**\n * What a file is: a picture, a document, or a page of markup the stuff carries\n * inline. It is the descriptor's statement (the node's kind, or its concept for\n * `native.Html`), never a reading of the bytes.\n */\nexport type StuffFileKind = 'image' | 'document' | 'markup';\n\n/**\n * One file value, read as the file it is declared to be — or `undefined` when\n * the value holds nothing to save (no URL, no markup).\n *\n * The walk below calls this for every file node it reaches, and the result view\n * calls it for the one file a control is drawn beside, so a file's own download\n * button and the whole-result download read the value the same way. `kind` is\n * the caller's statement from the descriptor; this never inspects the value to\n * decide what it is.\n */\nexport function readStuffFile(\n kind: StuffFileKind,\n value: unknown,\n path: string,\n): StuffFile | undefined {\n if (kind === 'markup') {\n const content = readHtmlContent(value);\n return content?.innerHtml\n ? { text: content.innerHtml, extension: 'html', path, kind }\n : undefined;\n }\n const content = kind === 'image' ? readImageContent(value) : readDocumentContent(value);\n if (!content?.url) return undefined;\n return {\n url: content.url,\n publicUrl: content.publicUrl,\n filename: content.filename,\n mimeType: content.mimeType,\n path,\n kind,\n };\n}\n\n/**\n * Every file inside a stuff, found by walking the DESCRIPTOR — never the value.\n *\n * The distinction is the whole point. A payload walk would have to decide what\n * a file is by looking at it: an object with a `url`, a string that ends in\n * `.pdf`. That is the guessing this package exists to remove, and it is wrong in\n * both directions — a method whose output legitimately has a `url` field of its\n * own is not a document, and a stored image whose reference carries no extension\n * is. The descriptor already states `kind: \"image\"` or `kind: \"document\"`, so\n * the walk reads the declaration and consults the value only for the members\n * that declaration promises.\n *\n * Ordering is the authored one, depth-first, which is the order the rendered\n * view puts them in — so a download of three images numbers them the way the\n * reader saw them.\n */\nexport function collectStuffFiles(field: RunField, value: unknown): StuffFile[] {\n const found: StuffFile[] = [];\n walk(field, value, [], found);\n return found;\n}\n\nfunction walk(field: RunField, raw: unknown, path: string[], out: StuffFile[]): void {\n const value = unwrapContent(field, raw);\n const here = path.length > 0 ? path : [field.name];\n\n // Markup, before the kind switch, exactly as the renderer takes it: a\n // `native.Html` node's KIND is `object` (its content model has two members),\n // so the switch below would walk into it looking for files and find none.\n //\n // It is a file even though nothing stores it as one. The stuff IS a page —\n // the reader is looking at a rendered report — and handing them a JSON\n // envelope with the page inside it as a string is the same mistake as handing\n // them a URL instead of a picture. `isNativeHtmlNode` asks the descriptor\n // whether the concept refines `native.Html`, which is the standard's own way\n // to ask; it never looks at the value to decide.\n if (isNativeHtmlNode(field)) {\n const file = readStuffFile('markup', value, here.join('.'));\n if (file) out.push(file);\n return;\n }\n\n switch (field.kind) {\n case 'image':\n case 'document': {\n const file = readStuffFile(field.kind, value, here.join('.'));\n if (file) out.push(file);\n return;\n }\n case 'object': {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return;\n const record = value as Record<string, unknown>;\n // A search result's sources are the web pages its answer was written\n // from. Its model states each as a `native.Document`, but none is a file\n // the result holds: saving one would fetch somebody's page across\n // origins and file it as `output-sources-0`. The JSON copy keeps every\n // address. Only that member is passed over, so a concept refining a\n // search result keeps any file it adds; the concept says it, wherever\n // the node sits, and the value is never asked.\n const skipped = isNativeSearchResultNode(field) ? SEARCH_SOURCES : undefined;\n for (const child of field.fields) {\n if (child.name === skipped) continue;\n walk(child, ownProp(record, child.name), [...here, child.name], out);\n }\n return;\n }\n case 'list': {\n if (!Array.isArray(value)) return;\n // The element descriptor's own `name` is unused by the standard, so the\n // index is what identifies an entry — the same thing the rendered list\n // shows beside each row.\n value.forEach((entry, index) => walk(field.item, entry, [...here, String(index)], out));\n return;\n }\n default:\n // Every other kind is a scalar. It cannot contain a file, and a `native`\n // node with no declared members (an `unknown` kind — a composite, say)\n // states nothing a walk could follow, so there is nothing to look at.\n return;\n }\n}\n\n/** The member of `native.SearchResult` that holds its sources, as its content model names it. */\nconst SEARCH_SOURCES = 'sources';\n\n/**\n * The one unwrap path, matching the renderer's: a content model is opened by\n * the property the schema named, and by nothing else.\n */\nfunction unwrapContent(field: RunField, value: unknown): unknown {\n if (!field.contentKey) return value;\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return value;\n const unwrapped = ownProp(value as Record<string, unknown>, field.contentKey);\n return unwrapped === undefined ? value : unwrapped;\n}\n"]}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { planFileSave, planStuffSave, splitFileName, typedFileStem } from './chunk-
|
|
1
|
+
import { planFileSave, planStuffSave, splitFileName, typedFileStem } from './chunk-4VAO6BDE.js';
|
|
2
2
|
import { seedInputs, closeOptionalStructures, openOptionalStructures } from './chunk-VH5CDQDR.js';
|
|
3
3
|
import { sourceFileValue, roomForChosenFiles, roomForDroppedFiles, addChosenFiles, placeDroppedFiles, chosenFilesAdded } from './chunk-NSHGLM6A.js';
|
|
4
|
-
import { readStuffFile } from './chunk-
|
|
4
|
+
import { readStuffFile } from './chunk-56WECFBD.js';
|
|
5
5
|
import { apiInputsFromRunValues, sameRunValues, runValuesFromInputs, setValueAtPath } from './chunk-ZX52CQJ4.js';
|
|
6
|
-
import { TYPED_SCALAR_MARKERS, viewableUrl, isNativeDateNode, isNativeHtmlNode, readHtmlContent, isNativeCompositeNode, readCompositeContent, isNativePageNode, nativeVerdictOf, isNativeValueNode, readDateContent, formatDateContent, readPageContent, readYesNoContent, readRatingContent, readChoiceContent, readDocumentContent, readImageContent, readDataUrl } from './chunk-
|
|
6
|
+
import { TYPED_SCALAR_MARKERS, viewableUrl, splitCitations, webHref, isNativeDateNode, isNativeHtmlNode, readHtmlContent, isNativeCompositeNode, readCompositeContent, isNativePageNode, nativeVerdictOf, isNativeSearchResultNode, readSearchResultContent, isNativeValueNode, readDateContent, formatDateContent, readPageContent, readYesNoContent, readRatingContent, readChoiceContent, readDocumentContent, readImageContent, readDataUrl, citedSource } from './chunk-QQK6Y55E.js';
|
|
7
7
|
import { buildRunFields } from './chunk-ZB4RYUKV.js';
|
|
8
8
|
import { conceptCategory } from './chunk-FABY3DEY.js';
|
|
9
9
|
import { acceptLabel, acceptMap, isAcceptedFile, normalizeMimeType, formatOfFilename, DOCUMENT_FORMATS, IMAGE_FORMATS } from './chunk-XCO6GTEC.js';
|
|
@@ -14,7 +14,7 @@ import { clsx } from 'clsx';
|
|
|
14
14
|
import { twMerge } from 'tailwind-merge';
|
|
15
15
|
import { ChevronDown, ChevronUp, Check, List, Braces, ListChecks, ToggleLeft, Hash, Image as Image$1, FileText, CalendarDays, Type, Folder, Search, X, ImageIcon, Loader2, ImageOff, Upload, Link2, EyeOff, Eye, Minus, Plus, Trash2, Sparkles, MoreHorizontal, TriangleAlert, FileDown, Copy, Download, ChevronRight, ExternalLink, ChevronLeft } from 'lucide-react';
|
|
16
16
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
17
|
-
import * as
|
|
17
|
+
import * as React9 from 'react';
|
|
18
18
|
import { createContext, use, useId, useMemo, useContext, useState, useEffect, useRef, useLayoutEffect, useCallback, Fragment as Fragment$1, useSyncExternalStore } from 'react';
|
|
19
19
|
import * as SwitchPrimitives from '@radix-ui/react-switch';
|
|
20
20
|
import { useDropzone } from 'react-dropzone';
|
|
@@ -115,7 +115,7 @@ function fieldLabel(title, name, presentation) {
|
|
|
115
115
|
if (title !== void 0) return title;
|
|
116
116
|
return presentation === "app" ? humanizeFieldName(name) : name;
|
|
117
117
|
}
|
|
118
|
-
var Switch =
|
|
118
|
+
var Switch = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
119
119
|
SwitchPrimitives.Root,
|
|
120
120
|
{
|
|
121
121
|
className: cn(
|
|
@@ -290,6 +290,7 @@ var EN_FIELD_STRINGS = {
|
|
|
290
290
|
ratingLevel: (position) => `Level ${position}`,
|
|
291
291
|
pageNumber: (position) => `Page ${position}`,
|
|
292
292
|
extractedTable: "Table",
|
|
293
|
+
searchSources: "Sources",
|
|
293
294
|
viewImage: (name) => `View ${name}`,
|
|
294
295
|
imagePosition: (position, count) => `${position} of ${count}`,
|
|
295
296
|
previousImage: "Previous picture",
|
|
@@ -473,6 +474,7 @@ var FR_FIELD_STRINGS = {
|
|
|
473
474
|
ratingLevel: (position) => `Niveau ${position}`,
|
|
474
475
|
pageNumber: (position) => `Page ${position}`,
|
|
475
476
|
extractedTable: "Tableau",
|
|
477
|
+
searchSources: "Sources",
|
|
476
478
|
viewImage: (name) => `Voir ${name}`,
|
|
477
479
|
imagePosition: (position, count) => `${position} sur ${count}`,
|
|
478
480
|
previousImage: "Image pr\xE9c\xE9dente",
|
|
@@ -860,7 +862,7 @@ var keepPress = (event) => event.stopPropagation();
|
|
|
860
862
|
function DialogSurface({ children }) {
|
|
861
863
|
return /* @__PURE__ */ jsx("div", { className: "contents", onClick: keepPress, onMouseDown: keepPress, children });
|
|
862
864
|
}
|
|
863
|
-
var DialogOverlay =
|
|
865
|
+
var DialogOverlay = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
864
866
|
DialogPrimitive.Overlay,
|
|
865
867
|
{
|
|
866
868
|
ref,
|
|
@@ -869,7 +871,7 @@ var DialogOverlay = React5.forwardRef(({ className, ...props }, ref) => /* @__PU
|
|
|
869
871
|
}
|
|
870
872
|
));
|
|
871
873
|
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
|
|
872
|
-
var DialogContent =
|
|
874
|
+
var DialogContent = React9.forwardRef(({ className, layout = "dialog", returnFocus, onCloseAutoFocus, children, ...props }, ref) => /* @__PURE__ */ jsxs(DialogSurface, { children: [
|
|
873
875
|
/* @__PURE__ */ jsx(DialogOverlay, {}),
|
|
874
876
|
/* @__PURE__ */ jsx(
|
|
875
877
|
DialogPrimitive.Content,
|
|
@@ -890,7 +892,7 @@ var DialogContent = React5.forwardRef(({ className, layout = "dialog", returnFoc
|
|
|
890
892
|
)
|
|
891
893
|
] }));
|
|
892
894
|
DialogContent.displayName = DialogPrimitive.Content.displayName;
|
|
893
|
-
var DialogTitle =
|
|
895
|
+
var DialogTitle = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
894
896
|
DialogPrimitive.Title,
|
|
895
897
|
{
|
|
896
898
|
ref,
|
|
@@ -899,7 +901,7 @@ var DialogTitle = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE
|
|
|
899
901
|
}
|
|
900
902
|
));
|
|
901
903
|
DialogTitle.displayName = DialogPrimitive.Title.displayName;
|
|
902
|
-
var DialogDescription =
|
|
904
|
+
var DialogDescription = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
903
905
|
DialogPrimitive.Description,
|
|
904
906
|
{
|
|
905
907
|
ref,
|
|
@@ -931,7 +933,7 @@ var toggleVariants = cva(
|
|
|
931
933
|
}
|
|
932
934
|
}
|
|
933
935
|
);
|
|
934
|
-
var Toggle =
|
|
936
|
+
var Toggle = React9.forwardRef(({ className, variant, size, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
935
937
|
TogglePrimitive.Root,
|
|
936
938
|
{
|
|
937
939
|
ref,
|
|
@@ -940,11 +942,11 @@ var Toggle = React5.forwardRef(({ className, variant, size, ...props }, ref) =>
|
|
|
940
942
|
}
|
|
941
943
|
));
|
|
942
944
|
Toggle.displayName = TogglePrimitive.Root.displayName;
|
|
943
|
-
var ToggleGroupContext =
|
|
945
|
+
var ToggleGroupContext = React9.createContext({
|
|
944
946
|
size: "default",
|
|
945
947
|
variant: "default"
|
|
946
948
|
});
|
|
947
|
-
var ToggleGroup =
|
|
949
|
+
var ToggleGroup = React9.forwardRef(({ className, variant, size, children, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
948
950
|
ToggleGroupPrimitive.Root,
|
|
949
951
|
{
|
|
950
952
|
ref,
|
|
@@ -954,8 +956,8 @@ var ToggleGroup = React5.forwardRef(({ className, variant, size, children, ...pr
|
|
|
954
956
|
}
|
|
955
957
|
));
|
|
956
958
|
ToggleGroup.displayName = ToggleGroupPrimitive.Root.displayName;
|
|
957
|
-
var ToggleGroupItem =
|
|
958
|
-
const context =
|
|
959
|
+
var ToggleGroupItem = React9.forwardRef(({ className, children, variant, size, ...props }, ref) => {
|
|
960
|
+
const context = React9.useContext(ToggleGroupContext);
|
|
959
961
|
return /* @__PURE__ */ jsx(
|
|
960
962
|
ToggleGroupPrimitive.Item,
|
|
961
963
|
{
|
|
@@ -1866,7 +1868,7 @@ function ClearButton({ disabled, onClick }) {
|
|
|
1866
1868
|
}
|
|
1867
1869
|
var Select = SelectPrimitive.Root;
|
|
1868
1870
|
var SelectValue = SelectPrimitive.Value;
|
|
1869
|
-
var SelectTrigger =
|
|
1871
|
+
var SelectTrigger = React9.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
|
|
1870
1872
|
SelectPrimitive.Trigger,
|
|
1871
1873
|
{
|
|
1872
1874
|
ref,
|
|
@@ -1885,7 +1887,7 @@ var SelectTrigger = React5.forwardRef(({ className, children, ...props }, ref) =
|
|
|
1885
1887
|
}
|
|
1886
1888
|
));
|
|
1887
1889
|
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
|
|
1888
|
-
var SelectScrollUpButton =
|
|
1890
|
+
var SelectScrollUpButton = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
1889
1891
|
SelectPrimitive.ScrollUpButton,
|
|
1890
1892
|
{
|
|
1891
1893
|
ref,
|
|
@@ -1895,7 +1897,7 @@ var SelectScrollUpButton = React5.forwardRef(({ className, ...props }, ref) => /
|
|
|
1895
1897
|
}
|
|
1896
1898
|
));
|
|
1897
1899
|
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
|
|
1898
|
-
var SelectScrollDownButton =
|
|
1900
|
+
var SelectScrollDownButton = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
1899
1901
|
SelectPrimitive.ScrollDownButton,
|
|
1900
1902
|
{
|
|
1901
1903
|
ref,
|
|
@@ -1905,7 +1907,7 @@ var SelectScrollDownButton = React5.forwardRef(({ className, ...props }, ref) =>
|
|
|
1905
1907
|
}
|
|
1906
1908
|
));
|
|
1907
1909
|
SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName;
|
|
1908
|
-
var SelectContent =
|
|
1910
|
+
var SelectContent = React9.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsx(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
|
|
1909
1911
|
SelectPrimitive.Content,
|
|
1910
1912
|
{
|
|
1911
1913
|
ref,
|
|
@@ -1933,7 +1935,7 @@ var SelectContent = React5.forwardRef(({ className, children, position = "popper
|
|
|
1933
1935
|
}
|
|
1934
1936
|
) }));
|
|
1935
1937
|
SelectContent.displayName = SelectPrimitive.Content.displayName;
|
|
1936
|
-
var SelectLabel =
|
|
1938
|
+
var SelectLabel = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
1937
1939
|
SelectPrimitive.Label,
|
|
1938
1940
|
{
|
|
1939
1941
|
ref,
|
|
@@ -1942,7 +1944,7 @@ var SelectLabel = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE
|
|
|
1942
1944
|
}
|
|
1943
1945
|
));
|
|
1944
1946
|
SelectLabel.displayName = SelectPrimitive.Label.displayName;
|
|
1945
|
-
var SelectItem =
|
|
1947
|
+
var SelectItem = React9.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
|
|
1946
1948
|
SelectPrimitive.Item,
|
|
1947
1949
|
{
|
|
1948
1950
|
ref,
|
|
@@ -1958,7 +1960,7 @@ var SelectItem = React5.forwardRef(({ className, children, ...props }, ref) => /
|
|
|
1958
1960
|
}
|
|
1959
1961
|
));
|
|
1960
1962
|
SelectItem.displayName = SelectPrimitive.Item.displayName;
|
|
1961
|
-
var SelectSeparator =
|
|
1963
|
+
var SelectSeparator = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
1962
1964
|
SelectPrimitive.Separator,
|
|
1963
1965
|
{
|
|
1964
1966
|
ref,
|
|
@@ -2927,7 +2929,7 @@ function FieldRenderer({ field, value, onChange, id, error, env }) {
|
|
|
2927
2929
|
}
|
|
2928
2930
|
}
|
|
2929
2931
|
var AlertDialog = AlertDialogPrimitive.Root;
|
|
2930
|
-
var AlertDialogContent =
|
|
2932
|
+
var AlertDialogContent = React9.forwardRef(({ className, returnFocus, onCloseAutoFocus, ...props }, ref) => /* @__PURE__ */ jsxs(DialogSurface, { children: [
|
|
2931
2933
|
/* @__PURE__ */ jsx(AlertDialogPrimitive.Overlay, { className: "fixed inset-0 z-50 bg-black/50" }),
|
|
2932
2934
|
/* @__PURE__ */ jsx(
|
|
2933
2935
|
AlertDialogPrimitive.Content,
|
|
@@ -2943,7 +2945,7 @@ var AlertDialogContent = React5.forwardRef(({ className, returnFocus, onCloseAut
|
|
|
2943
2945
|
)
|
|
2944
2946
|
] }));
|
|
2945
2947
|
AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName;
|
|
2946
|
-
var AlertDialogTitle =
|
|
2948
|
+
var AlertDialogTitle = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
2947
2949
|
AlertDialogPrimitive.Title,
|
|
2948
2950
|
{
|
|
2949
2951
|
ref,
|
|
@@ -2952,7 +2954,7 @@ var AlertDialogTitle = React5.forwardRef(({ className, ...props }, ref) => /* @_
|
|
|
2952
2954
|
}
|
|
2953
2955
|
));
|
|
2954
2956
|
AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName;
|
|
2955
|
-
var AlertDialogDescription =
|
|
2957
|
+
var AlertDialogDescription = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
2956
2958
|
AlertDialogPrimitive.Description,
|
|
2957
2959
|
{
|
|
2958
2960
|
ref,
|
|
@@ -2965,7 +2967,7 @@ var AlertDialogCancel = AlertDialogPrimitive.Cancel;
|
|
|
2965
2967
|
var AlertDialogAction = AlertDialogPrimitive.Action;
|
|
2966
2968
|
var DropdownMenu = DropdownMenuPrimitive.Root;
|
|
2967
2969
|
var DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
|
|
2968
|
-
var DropdownMenuContent =
|
|
2970
|
+
var DropdownMenuContent = React9.forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
2969
2971
|
DropdownMenuPrimitive.Content,
|
|
2970
2972
|
{
|
|
2971
2973
|
ref,
|
|
@@ -2978,7 +2980,7 @@ var DropdownMenuContent = React5.forwardRef(({ className, sideOffset = 4, ...pro
|
|
|
2978
2980
|
}
|
|
2979
2981
|
));
|
|
2980
2982
|
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
|
|
2981
|
-
var DropdownMenuItem =
|
|
2983
|
+
var DropdownMenuItem = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
2982
2984
|
DropdownMenuPrimitive.Item,
|
|
2983
2985
|
{
|
|
2984
2986
|
ref,
|
|
@@ -4388,7 +4390,7 @@ function usePdfViewer() {
|
|
|
4388
4390
|
var TooltipProvider = TooltipPrimitive.Provider;
|
|
4389
4391
|
var TooltipRoot = TooltipPrimitive.Root;
|
|
4390
4392
|
var TooltipTrigger = TooltipPrimitive.Trigger;
|
|
4391
|
-
var TooltipContent =
|
|
4393
|
+
var TooltipContent = React9.forwardRef(({ className, sideOffset = 6, ...props }, ref) => /* @__PURE__ */ jsx(TooltipPrimitive.Portal, { children: /* @__PURE__ */ jsx(
|
|
4392
4394
|
TooltipPrimitive.Content,
|
|
4393
4395
|
{
|
|
4394
4396
|
ref,
|
|
@@ -5604,13 +5606,14 @@ function isTextEntry(target) {
|
|
|
5604
5606
|
return target.isContentEditable || target.closest("input, textarea, select") !== null;
|
|
5605
5607
|
}
|
|
5606
5608
|
var LEX_OPTIONS = { gfm: true, breaks: true };
|
|
5607
|
-
function Markdown({ text, className, proseImages }) {
|
|
5608
|
-
const tokens =
|
|
5609
|
+
function Markdown({ text, className, proseImages, citations }) {
|
|
5610
|
+
const tokens = React9.useMemo(() => marked.lexer(text, LEX_OPTIONS), [text]);
|
|
5609
5611
|
const fromEnv = useProseImages();
|
|
5610
5612
|
const mode = proseImages ?? fromEnv;
|
|
5611
|
-
return /* @__PURE__ */ jsx(ProseImagesContext, { value: mode, children: /* @__PURE__ */ jsx("div", { className: cn("space-y-2 text-[13px] leading-relaxed text-foreground", className), children: /* @__PURE__ */ jsx(Blocks, { tokens }) }) });
|
|
5613
|
+
return /* @__PURE__ */ jsx(ProseImagesContext, { value: mode, children: /* @__PURE__ */ jsx(CitationsContext, { value: citations, children: /* @__PURE__ */ jsx("div", { className: cn("space-y-2 text-[13px] leading-relaxed text-foreground", className), children: /* @__PURE__ */ jsx(Blocks, { tokens }) }) }) });
|
|
5612
5614
|
}
|
|
5613
|
-
var ProseImagesContext =
|
|
5615
|
+
var ProseImagesContext = React9.createContext("link");
|
|
5616
|
+
var CitationsContext = React9.createContext(void 0);
|
|
5614
5617
|
var HEADING_CLASS = {
|
|
5615
5618
|
1: "text-[16px] font-semibold",
|
|
5616
5619
|
2: "text-[14.5px] font-semibold",
|
|
@@ -5727,7 +5730,7 @@ function InlineToken({ token }) {
|
|
|
5727
5730
|
switch (token.type) {
|
|
5728
5731
|
case "text": {
|
|
5729
5732
|
const text = token;
|
|
5730
|
-
return text.tokens ? /* @__PURE__ */ jsx(Inline, { tokens: text.tokens }) : /* @__PURE__ */ jsx(
|
|
5733
|
+
return text.tokens ? /* @__PURE__ */ jsx(Inline, { tokens: text.tokens }) : /* @__PURE__ */ jsx(CitedText, { text: decode(text.text) });
|
|
5731
5734
|
}
|
|
5732
5735
|
case "escape":
|
|
5733
5736
|
return /* @__PURE__ */ jsx(Fragment, { children: token.text });
|
|
@@ -5744,7 +5747,9 @@ function InlineToken({ token }) {
|
|
|
5744
5747
|
case "link": {
|
|
5745
5748
|
const link = token;
|
|
5746
5749
|
const href = safeHref(link.href);
|
|
5747
|
-
if (!href)
|
|
5750
|
+
if (!href) {
|
|
5751
|
+
return /* @__PURE__ */ jsx(CitationsContext, { value: void 0, children: /* @__PURE__ */ jsx(Inline, { tokens: link.tokens }) });
|
|
5752
|
+
}
|
|
5748
5753
|
return (
|
|
5749
5754
|
// The flag is set for everything under the anchor, so a nested `image`
|
|
5750
5755
|
// token knows not to open one of its own — see {@link ProseImage}.
|
|
@@ -5766,9 +5771,34 @@ function InlineToken({ token }) {
|
|
|
5766
5771
|
return /* @__PURE__ */ jsx(Fragment, { children: rawOf(token) });
|
|
5767
5772
|
}
|
|
5768
5773
|
}
|
|
5774
|
+
function CitedText({ text }) {
|
|
5775
|
+
const citations = React9.use(CitationsContext);
|
|
5776
|
+
const insideLink = React9.use(InsideLinkContext);
|
|
5777
|
+
if (!citations || insideLink) return /* @__PURE__ */ jsx(Fragment, { children: text });
|
|
5778
|
+
return /* @__PURE__ */ jsx(Fragment, { children: splitCitations(text).map((segment, index) => {
|
|
5779
|
+
const target = "citation" in segment ? citations(segment.citation) : void 0;
|
|
5780
|
+
const href = target ? webHref(target.href) : void 0;
|
|
5781
|
+
if (!target || !href) return /* @__PURE__ */ jsx(React9.Fragment, { children: segment.text }, index);
|
|
5782
|
+
return /* @__PURE__ */ jsx(
|
|
5783
|
+
"a",
|
|
5784
|
+
{
|
|
5785
|
+
href,
|
|
5786
|
+
target: "_blank",
|
|
5787
|
+
rel: "noopener noreferrer",
|
|
5788
|
+
title: target.label,
|
|
5789
|
+
className: cn(
|
|
5790
|
+
"mx-px whitespace-nowrap rounded-sm underline underline-offset-2",
|
|
5791
|
+
focusOutlineClass
|
|
5792
|
+
),
|
|
5793
|
+
children: segment.text
|
|
5794
|
+
},
|
|
5795
|
+
index
|
|
5796
|
+
);
|
|
5797
|
+
}) });
|
|
5798
|
+
}
|
|
5769
5799
|
function ProseImage({ token }) {
|
|
5770
|
-
const mode =
|
|
5771
|
-
const insideLink =
|
|
5800
|
+
const mode = React9.use(ProseImagesContext);
|
|
5801
|
+
const insideLink = React9.use(InsideLinkContext);
|
|
5772
5802
|
const src = proseImageUrl(token.href);
|
|
5773
5803
|
if (!src) return /* @__PURE__ */ jsx(Fragment, { children: token.text });
|
|
5774
5804
|
if (mode === "load") {
|
|
@@ -5794,7 +5824,7 @@ function ProseImage({ token }) {
|
|
|
5794
5824
|
}
|
|
5795
5825
|
);
|
|
5796
5826
|
}
|
|
5797
|
-
var InsideLinkContext =
|
|
5827
|
+
var InsideLinkContext = React9.createContext(false);
|
|
5798
5828
|
function proseImageUrl(href) {
|
|
5799
5829
|
const url = viewableUrl(href);
|
|
5800
5830
|
return url && /^https?:/.test(url) ? url : void 0;
|
|
@@ -7864,6 +7894,64 @@ function VerdictValue({ reading, prominent }) {
|
|
|
7864
7894
|
reading.measure !== void 0 && /* @__PURE__ */ jsx("p", { className: "text-[12.5px] text-muted-foreground", children: reading.measure })
|
|
7865
7895
|
] });
|
|
7866
7896
|
}
|
|
7897
|
+
function SearchResultValue({ content }) {
|
|
7898
|
+
const s = useFieldStrings();
|
|
7899
|
+
const headingId = useId();
|
|
7900
|
+
const citations = (citation) => {
|
|
7901
|
+
const source = citedSource(content, citation);
|
|
7902
|
+
return source?.href ? { href: source.href, label: source.title ?? source.address } : void 0;
|
|
7903
|
+
};
|
|
7904
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
7905
|
+
content.answer.trim() === "" ? /* @__PURE__ */ jsx(Absent, {}) : /* @__PURE__ */ jsx(Markdown, { text: content.answer, citations }),
|
|
7906
|
+
content.sources.length > 0 && /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
7907
|
+
/* @__PURE__ */ jsx("p", { id: headingId, className: "text-[12px] font-medium text-muted-foreground", children: s.searchSources }),
|
|
7908
|
+
/* @__PURE__ */ jsx(
|
|
7909
|
+
"ul",
|
|
7910
|
+
{
|
|
7911
|
+
"aria-labelledby": headingId,
|
|
7912
|
+
className: "grid grid-cols-[max-content_minmax(0,1fr)] gap-x-2 gap-y-1",
|
|
7913
|
+
children: content.sources.map((source) => /* @__PURE__ */ jsxs(
|
|
7914
|
+
"li",
|
|
7915
|
+
{
|
|
7916
|
+
className: "col-span-2 grid grid-cols-subgrid items-baseline",
|
|
7917
|
+
children: [
|
|
7918
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono text-[11px] tabular-nums text-muted-foreground", children: source.citations.map((citation) => `[${citation}]`).join("") }),
|
|
7919
|
+
/* @__PURE__ */ jsx(SearchSource, { source })
|
|
7920
|
+
]
|
|
7921
|
+
},
|
|
7922
|
+
source.citations[0]
|
|
7923
|
+
))
|
|
7924
|
+
}
|
|
7925
|
+
)
|
|
7926
|
+
] })
|
|
7927
|
+
] });
|
|
7928
|
+
}
|
|
7929
|
+
function SearchSource({ source }) {
|
|
7930
|
+
const body = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
7931
|
+
/* @__PURE__ */ jsx(
|
|
7932
|
+
"span",
|
|
7933
|
+
{
|
|
7934
|
+
className: cn(
|
|
7935
|
+
"text-[13px] leading-5 wrap-anywhere text-foreground",
|
|
7936
|
+
source.href && "underline underline-offset-2"
|
|
7937
|
+
),
|
|
7938
|
+
children: source.title ?? source.address
|
|
7939
|
+
}
|
|
7940
|
+
),
|
|
7941
|
+
source.host && /* @__PURE__ */ jsx("span", { className: "block text-[12px] leading-4 text-muted-foreground wrap-anywhere", children: source.host })
|
|
7942
|
+
] });
|
|
7943
|
+
if (!source.href) return /* @__PURE__ */ jsx("div", { className: "min-w-0", children: body });
|
|
7944
|
+
return /* @__PURE__ */ jsx(
|
|
7945
|
+
"a",
|
|
7946
|
+
{
|
|
7947
|
+
href: source.href,
|
|
7948
|
+
target: "_blank",
|
|
7949
|
+
rel: "noopener noreferrer",
|
|
7950
|
+
className: cn("block min-w-0 rounded-sm max-sm:py-1.5", focusOutlineClass),
|
|
7951
|
+
children: body
|
|
7952
|
+
}
|
|
7953
|
+
);
|
|
7954
|
+
}
|
|
7867
7955
|
function isLongText(value) {
|
|
7868
7956
|
return typeof value === "string" && (value.includes("\n") || value.length > CELL_LENGTH_LIMIT);
|
|
7869
7957
|
}
|
|
@@ -7948,6 +8036,16 @@ function ResultNode({
|
|
|
7948
8036
|
/* @__PURE__ */ jsx(VerdictValue, { reading, prominent: whole })
|
|
7949
8037
|
] });
|
|
7950
8038
|
}
|
|
8039
|
+
const search = isApp && isNativeSearchResultNode(field) ? readSearchResultContent(unwrapped) : void 0;
|
|
8040
|
+
if (search) {
|
|
8041
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
8042
|
+
search.answer.trim() === "" ? header : /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2", children: [
|
|
8043
|
+
/* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: header }),
|
|
8044
|
+
/* @__PURE__ */ jsx(CopyButton, { value: search.answer, label: s.copyText })
|
|
8045
|
+
] }),
|
|
8046
|
+
/* @__PURE__ */ jsx(SearchResultValue, { content: search })
|
|
8047
|
+
] });
|
|
8048
|
+
}
|
|
7951
8049
|
switch (field.kind) {
|
|
7952
8050
|
case "object": {
|
|
7953
8051
|
const memberValue = (child) => typeof unwrapped === "object" && unwrapped !== null ? ownProp(unwrapped, child.name) : void 0;
|
|
@@ -8033,9 +8131,12 @@ function ResultNode({
|
|
|
8033
8131
|
) : isApp && nativeVerdictOf(field.item) ? (
|
|
8034
8132
|
// Verdicts in `app`, one per line, each drawn by the verdict arm.
|
|
8035
8133
|
/* @__PURE__ */ jsx("div", { className: "divide-y divide-border/60 overflow-hidden rounded-lg border border-border", children: items.map((item, index) => /* @__PURE__ */ jsx("div", { className: "bg-card/40 px-3 py-1.5", children: /* @__PURE__ */ jsx(ResultAt, { segment: String(index), children: /* @__PURE__ */ jsx(ResultField, { field: field.item, value: item, depth: depth + 1, hideLabel: true }) }) }, index)) })
|
|
8036
|
-
) : columns ? (
|
|
8134
|
+
) : columns && !(isApp && isNativeSearchResultNode(field.item)) ? (
|
|
8037
8135
|
// Same shape every row: the labels are column headers, not per-row
|
|
8038
|
-
// labels. See `ObjectTable`.
|
|
8136
|
+
// labels. See `ObjectTable`. A list of search results in `app` is
|
|
8137
|
+
// the exception: a row would hide each answer's citations and its
|
|
8138
|
+
// sources until it is opened, so each search takes a card of its
|
|
8139
|
+
// own below and is drawn by the search arm. `studio` keeps the table.
|
|
8039
8140
|
/* @__PURE__ */ jsx(
|
|
8040
8141
|
ObjectTable,
|
|
8041
8142
|
{
|
|
@@ -8351,5 +8452,5 @@ function pdfFileName(field, value, baseName) {
|
|
|
8351
8452
|
}
|
|
8352
8453
|
|
|
8353
8454
|
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, checkedAccentClass, cn, fieldControlClass, fieldStringsFor, focusOutlineClass, humanizeFieldName, linkTextClass, renderHtmlPdf, resolveHtmlStorageUrls, saveInBrowser, selectedBorderClass, selectedFillClass, selectedFillStrongClass, useFieldDomId, useFieldPresentation, useFieldStrings, useMethodForm, useProseImages, useResolveRendition, useResolveShareUrl, useResolveUrl, useResolvedUrl };
|
|
8354
|
-
//# sourceMappingURL=chunk-
|
|
8355
|
-
//# sourceMappingURL=chunk-
|
|
8455
|
+
//# sourceMappingURL=chunk-AZIUFSIQ.js.map
|
|
8456
|
+
//# sourceMappingURL=chunk-AZIUFSIQ.js.map
|