@signalcore/ui 1.0.0-beta.0 → 1.0.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -1
- package/dist/AgentAbout.js +7 -4
- package/dist/AgentAbout.js.map +1 -1
- package/dist/AgentChat.js +10 -2
- package/dist/AgentChat.js.map +1 -1
- package/dist/AgentTasks.js +2 -1
- package/dist/AgentTasks.js.map +1 -1
- package/dist/VoiceCall.js +1 -1
- package/dist/VoiceCall.js.map +1 -1
- package/dist/about.d.ts +34 -144
- package/dist/about.js +6 -8
- package/dist/about.js.map +1 -1
- package/dist/automation/cron.d.ts +1 -1
- package/dist/automation/cron.js.map +1 -1
- package/dist/automation/marks.js +7 -3
- package/dist/automation/marks.js.map +1 -1
- package/dist/automation/messages.d.ts +2 -0
- package/dist/automation/messages.js +2 -0
- package/dist/automation/messages.js.map +1 -1
- package/dist/automation/types.d.ts +25 -128
- package/dist/automation/types.js +36 -0
- package/dist/automation/types.js.map +1 -0
- package/dist/automation/when.d.ts +1 -1
- package/dist/automation/when.js +5 -1
- package/dist/automation/when.js.map +1 -1
- package/dist/blocks.d.ts +23 -1
- package/dist/browser.js +2 -2
- package/dist/browser.js.map +1 -1
- package/dist/call.d.ts +1 -1
- package/dist/call.js.map +1 -1
- package/dist/contract.d.ts +262 -0
- package/dist/credentials.js.map +1 -1
- package/dist/dev.js +1 -1
- package/dist/dev.js.map +1 -1
- package/dist/es.js +9 -1
- package/dist/es.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -1
- package/dist/kit/AudioPlayer.js +1 -1
- package/dist/kit/AudioPlayer.js.map +1 -1
- package/dist/kit/Choices.js +1 -1
- package/dist/kit/Choices.js.map +1 -1
- package/dist/kit/FileField.js +1 -1
- package/dist/kit/FileField.js.map +1 -1
- package/dist/kit/Popover.js.map +1 -1
- package/dist/kit/Tile.d.ts +1 -1
- package/dist/kit/Tile.js +1 -1
- package/dist/kit/Tile.js.map +1 -1
- package/dist/kit/messages.d.ts +13 -0
- package/dist/kit/messages.js +7 -1
- package/dist/kit/messages.js.map +1 -1
- package/dist/kit/uploads.d.ts +2 -1
- package/dist/kit/uploads.js +1 -1
- package/dist/kit/uploads.js.map +1 -1
- package/dist/kit/waveform.js.map +1 -1
- package/dist/markdown.js.map +1 -1
- package/dist/marks.js +2 -3
- package/dist/marks.js.map +1 -1
- package/dist/surface/Markdown.js +1 -1
- package/dist/surface/Markdown.js.map +1 -1
- package/dist/surface/Surface.js +43 -4
- package/dist/surface/Surface.js.map +1 -1
- package/dist/surface/scope.d.ts +1 -1
- package/dist/surface/scope.js.map +1 -1
- package/dist/surface/values.js +1 -1
- package/dist/surface/values.js.map +1 -1
- package/dist/tint.d.ts +1 -1
- package/dist/tint.js +1 -1
- package/dist/tint.js.map +1 -1
- package/dist/types.d.ts +7 -0
- package/dist/types.js.map +1 -1
- package/dist/usePolled.js +1 -1
- package/dist/usePolled.js.map +1 -1
- package/dist/voice/webgl.js +1 -1
- package/dist/voice/webgl.js.map +1 -1
- package/dist/widget.js +14 -14
- package/package.json +1 -1
- package/src/AgentAbout.tsx +10 -3
- package/src/AgentChat.tsx +20 -3
- package/src/AgentTasks.tsx +2 -2
- package/src/VoiceCall.tsx +1 -1
- package/src/about.ts +42 -144
- package/src/automation/cron.ts +1 -1
- package/src/automation/marks.tsx +6 -2
- package/src/automation/messages.ts +4 -0
- package/src/automation/types.ts +58 -118
- package/src/automation/when.ts +3 -1
- package/src/blocks.ts +25 -0
- package/src/browser.ts +4 -4
- package/src/call.ts +1 -1
- package/src/contract.ts +1720 -0
- package/src/credentials.ts +2 -2
- package/src/dev.ts +1 -1
- package/src/es.ts +8 -0
- package/src/index.ts +2 -0
- package/src/kit/AudioPlayer.tsx +1 -1
- package/src/kit/Choices.tsx +1 -1
- package/src/kit/FileField.tsx +1 -1
- package/src/kit/Popover.tsx +1 -1
- package/src/kit/Tile.tsx +1 -1
- package/src/kit/messages.ts +20 -0
- package/src/kit/uploads.ts +2 -2
- package/src/kit/waveform.ts +1 -1
- package/src/markdown.ts +1 -1
- package/src/marks.tsx +3 -123
- package/src/styles.css +24 -0
- package/src/surface/Markdown.tsx +1 -1
- package/src/surface/Surface.tsx +53 -4
- package/src/surface/scope.ts +3 -1
- package/src/surface/values.ts +5 -5
- package/src/tint.ts +1 -1
- package/src/types.ts +8 -0
- package/src/usePolled.ts +2 -2
- package/src/voice/webgl.ts +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FileField.js","names":[],"sources":["../../src/kit/FileField.tsx"],"sourcesContent":["import { ArrowClockwiseIcon, UploadSimpleIcon, XIcon } from \"@phosphor-icons/react\";\nimport { useEffect, useRef, useState, type CSSProperties, type DragEvent } from \"react\";\n\nimport type { Attachment } from \"../types\";\nimport { useKit, useParts } from \"./context\";\nimport { ariaOf, type ControlBase } from \"./Field\";\nimport { FileMark } from \"./FileMark\";\nimport { formatSize } from \"./numbers\";\nimport { failureOf, familyOf, isAbort, limitsOf, refusalOf, type FileHost } from \"./uploads\";\n\n// --- One file -----------------------------------------------------------------------------\n\nexport interface FileChipProps {\n name: string;\n /** Its weight in bytes; none while it is not known. */\n size?: number | undefined;\n /** Its type, for its glyph when its name has no extension. */\n type?: string | undefined;\n /** On its way: how much of it has gone, from 0 to 1. */\n progress?: number | undefined;\n /** What went wrong with it, in place of its size. */\n problem?: string | null | undefined;\n /** Opens it: the chip is a button. */\n onOpen?: (() => void) | undefined;\n onRetry?: (() => void) | undefined;\n onRemove?: (() => void) | undefined;\n}\n\n/**\n * A file as a chip: the glyph of its family, its name, and under it its size, how much has gone while it uploads (a line filling at its foot), or what went wrong in the danger\n * tone; at its end, trying it again and taking it away. With `onOpen` it opens on a click.\n */\nexport function FileChip({\n name,\n size,\n type = \"\",\n progress,\n problem,\n onOpen,\n onRetry,\n onRemove,\n}: FileChipProps) {\n const { code, messages } = useKit();\n const part = useParts();\n const family = familyOf(name, type);\n // The name says its extension; under it, how big it is, or how far it has gone.\n const meta =\n problem ??\n (progress !== undefined\n ? `${messages.uploading} ${new Intl.NumberFormat(code, { style: \"percent\" }).format(progress)}`\n : size !== undefined\n ? formatSize(size, code)\n : \"\");\n const body = (\n <>\n <FileMark name={name} type={type} size={28} />\n <span className={part(\"sc-attachment__text\")}>\n <span className={part(\"sc-attachment__name\")} title={name}>\n {name}\n </span>\n {meta !== \"\" && <span className={part(\"sc-attachment__meta\")}>{meta}</span>}\n </span>\n </>\n );\n return (\n <div\n data-family={family}\n className={part(\"sc-attachment\", ...(problem ? ([\"sc-attachment--failed\"] as const) : []))}\n >\n {onOpen ? (\n <button\n type=\"button\"\n onClick={onOpen}\n aria-label={messages.open(name)}\n className={part(\"sc-attachment__main\")}\n >\n {body}\n </button>\n ) : (\n <span className={part(\"sc-attachment__main\")}>{body}</span>\n )}\n {onRetry && (\n <button\n type=\"button\"\n onClick={onRetry}\n aria-label={messages.retry}\n title={messages.retry}\n className={part(\"sc-attachment__action\")}\n >\n <ArrowClockwiseIcon aria-hidden weight=\"bold\" />\n </button>\n )}\n {onRemove && (\n <button\n type=\"button\"\n onClick={onRemove}\n aria-label={messages.remove(name)}\n title={messages.remove(name)}\n className={part(\"sc-attachment__action\")}\n >\n <XIcon aria-hidden weight=\"bold\" />\n </button>\n )}\n {progress !== undefined && (\n <span\n role=\"progressbar\"\n aria-label={name}\n aria-valuemin={0}\n aria-valuemax={100}\n aria-valuenow={Math.round(progress * 100)}\n style={{ \"--_progress\": progress } as CSSProperties}\n className={part(\"sc-attachment__bar\")}\n />\n )}\n </div>\n );\n}\n\n/** Files already sent, as chips in a row that wraps; with `onOpen`, each opens on a click. */\nexport function FileChips({\n files,\n onOpen,\n}: {\n files: readonly Attachment[];\n onOpen?: ((file: Attachment) => void) | undefined;\n}) {\n const part = useParts();\n return (\n <ul className={part(\"sc-attachments\")}>\n {files.map((file) => (\n <li key={file.id}>\n <FileChip\n name={file.name}\n size={file.size}\n type={file.media_type}\n onOpen={onOpen && (() => onOpen(file))}\n />\n </li>\n ))}\n </ul>\n );\n}\n\n// --- Files on their way -------------------------------------------------------------------\n\n/** A file picked here, on its way or in: what its chip draws. */\nexport interface Upload {\n /** Tells it from the others here. */\n key: number;\n name: string;\n size: number | undefined;\n type: string;\n /**\n * uploading: on its way (`progress`, 0 to 1); done: stored as `id`; failed: its upload failed,\n * to be tried again; refused: never sent, a kind not taken or too heavy (`problem` says which).\n */\n state: \"uploading\" | \"done\" | \"failed\" | \"refused\";\n progress: number;\n id: string | null;\n problem: string | null;\n /** The bytes, kept to try again. */\n file: File | null;\n}\n\n/** What files may be picked: their kinds, their most in MB, one or many, how many at most. */\nexport interface UploadRules {\n accept?: readonly string[] | undefined;\n maxMb?: number | undefined;\n /** False: one file, a new pick replacing it. */\n multiple?: boolean | undefined;\n max?: number | undefined;\n}\n\n/** Files picked and how each goes, and what can be done with them. */\nexport interface Uploads {\n items: readonly Upload[];\n /** The ids of the files in, in the order they were picked. */\n ids: string[];\n /** What the files still wait on: one on its way, or one that failed or was refused; null once all are in. */\n waiting: \"uploading\" | \"failed\" | null;\n add: (files: readonly File[]) => void;\n remove: (key: number) => void;\n retry: (key: number) => void;\n /** Takes them all away, letting go of any on its way. */\n clear: () => void;\n}\n\nlet picks = 0;\n\n/** A file already in, by its id: what `useUploads` starts from. */\nexport function uploaded(id: string, known: Attachment | undefined): Upload {\n return {\n key: ++picks,\n name: known?.name ?? \"\",\n size: known?.size,\n type: known?.media_type ?? \"\",\n state: \"done\",\n progress: 1,\n id,\n problem: null,\n file: null,\n };\n}\n\n/**\n * Files picked and sent at once through `host`: each is refused before it goes when it is not a\n * kind taken, is too heavy, or is one too many; then it is on its way, then in (its id) or failed\n * (to be tried again). With `multiple` false a new pick replaces what was there. Whatever is on\n * its way when its file goes (taken away, cleared, the component gone) is let go.\n */\nexport function useUploads(\n host: FileHost | undefined,\n { accept, maxMb, multiple = true, max = Infinity }: UploadRules,\n start: () => Upload[] = () => [],\n): Uploads {\n const { code, messages } = useKit();\n const [items, setItems] = useState<Upload[]>(start);\n const running = useRef(new Map<number, AbortController>());\n\n useEffect(() => {\n const now = running.current;\n return () => {\n for (const controller of now.values()) controller.abort();\n now.clear();\n };\n }, []);\n\n const change = (key: number, patch: Partial<Upload>) => {\n setItems((now) => now.map((item) => (item.key === key ? { ...item, ...patch } : item)));\n };\n\n const letGo = (key: number) => {\n running.current.get(key)?.abort();\n running.current.delete(key);\n };\n\n const send = (key: number, file: File) => {\n if (!host) return;\n const controller = new AbortController();\n running.current.set(key, controller);\n host\n .upload(file, (progress) => change(key, { progress }), controller.signal)\n .then(\n (stored) => {\n change(key, { state: \"done\", id: stored.id, progress: 1, file: null });\n },\n (error: unknown) => {\n if (isAbort(error)) return;\n change(key, { state: \"failed\", problem: failureOf(error, maxMb, code, messages) });\n },\n )\n .finally(() => {\n if (running.current.get(key) === controller) running.current.delete(key);\n });\n };\n\n const add = (files: readonly File[]) => {\n if (!host || files.length === 0) return;\n const picked = multiple ? files : files.slice(0, 1);\n const kept = multiple ? items.filter((item) => item.state !== \"refused\").length : 0;\n const fresh = picked.map((file, index): Upload => {\n const problem =\n kept + index >= max\n ? messages.tooMany(max)\n : refusalOf(file, accept, maxMb, code, messages);\n return {\n key: ++picks,\n name: file.name || messages.file,\n size: file.size,\n type: file.type,\n state: problem === null ? \"uploading\" : \"refused\",\n progress: 0,\n id: null,\n problem,\n file: problem === null ? file : null,\n };\n });\n if (!multiple) for (const key of running.current.keys()) letGo(key);\n setItems((now) => (multiple ? [...now, ...fresh] : fresh));\n for (const item of fresh) if (item.file) send(item.key, item.file);\n };\n\n const remove = (key: number) => {\n letGo(key);\n setItems((now) => now.filter((item) => item.key !== key));\n };\n\n const retry = (key: number) => {\n const item = items.find((each) => each.key === key);\n if (item?.state !== \"failed\" || !item.file) return;\n change(key, { state: \"uploading\", progress: 0, problem: null });\n send(key, item.file);\n };\n\n const clear = () => {\n for (const key of [...running.current.keys()]) letGo(key);\n setItems([]);\n };\n\n const ids = items.flatMap((item) => (item.state === \"done\" && item.id ? [item.id] : []));\n const waiting = items.some((item) => item.state === \"uploading\")\n ? \"uploading\"\n : items.some((item) => item.state === \"failed\" || item.state === \"refused\")\n ? \"failed\"\n : null;\n return { items, ids, waiting, add, remove, retry, clear };\n}\n\n/**\n * The files picked, as chips: each with how it goes; taken away while they can be, the failed\n * tried again; once in, a name not known here is read from the host's files, and opens there.\n */\nexport function UploadChips({\n uploads,\n host,\n closed = false,\n}: {\n uploads: Uploads;\n host: FileHost | undefined;\n closed?: boolean | undefined;\n}) {\n const { messages } = useKit();\n const part = useParts();\n return (\n <ul className={part(\"sc-attachments\")}>\n {uploads.items.map((item) => {\n const stored = item.id !== null ? host?.known.get(item.id) : undefined;\n const { open } = host ?? {};\n return (\n <li key={item.key}>\n <FileChip\n name={stored?.name ?? (item.name || messages.file)}\n size={stored?.size ?? item.size}\n type={stored?.media_type ?? item.type}\n progress={item.state === \"uploading\" ? item.progress : undefined}\n problem={item.problem}\n onOpen={stored && open && (() => open(stored))}\n onRetry={\n !closed && item.state === \"failed\" ? () => uploads.retry(item.key) : undefined\n }\n onRemove={closed ? undefined : () => uploads.remove(item.key)}\n />\n </li>\n );\n })}\n </ul>\n );\n}\n\n/** Whether what is dragged carries files (not words, not a link). */\nexport function carriesFiles(event: DragEvent): boolean {\n return [...event.dataTransfer.types].includes(\"Files\");\n}\n\n// --- The field ----------------------------------------------------------------------------\n\nexport interface FileFieldProps extends ControlBase {\n /** The ids of the files in. */\n value: string[];\n onChange: (ids: string[]) => void;\n /** Where its files go and are found; none: it takes none (closed). */\n files?: FileHost | undefined;\n /** The kinds it takes: types, families (\"image/*\"), extensions (\".xlsx\"); none: any. */\n accept?: readonly string[] | undefined;\n multiple?: boolean | undefined;\n /** The most a file may weigh, in MB (50 at most). */\n maxMb?: number | undefined;\n /**\n * Hears what the field still waits on before its form may go, in words (a file on its way,\n * one that failed), and null once nothing.\n */\n onWait?: ((problem: string | null) => void) | undefined;\n}\n\n/**\n * Files to give: a zone to drop them on or press to choose them, saying what it takes; each file\n * uploads at once (a line fills under it), then sits as a chip to take away; what is wrong with\n * one (too heavy, a kind not taken, an upload that failed, to try again) says so on its chip.\n * The value is the ids of the files in. One file unless `multiple`: once it is in, the zone\n * steps aside until it is taken away. Closed, the chips are read, and open where the host shows\n * files.\n */\nexport function FileField({\n value,\n onChange,\n files,\n accept,\n multiple = false,\n maxMb,\n onWait,\n ...control\n}: FileFieldProps) {\n const { code, messages } = useKit();\n const part = useParts();\n const picker = useRef<HTMLInputElement>(null);\n const [over, setOver] = useState(false);\n const uploads = useUploads(files, { accept, maxMb, multiple }, () =>\n value.map((id) => uploaded(id, files?.known.get(id))),\n );\n const closed = control.disabled === true || files === undefined;\n\n // The ids go up whenever the files in change.\n const sent = useRef(value.join(\" \"));\n const ids = uploads.ids;\n useEffect(() => {\n const now = ids.join(\" \");\n if (now === sent.current) return;\n sent.current = now;\n onChange(ids);\n }, [ids, onChange]);\n\n const wait =\n uploads.waiting === \"uploading\"\n ? messages.stillUploading\n : uploads.waiting === \"failed\"\n ? messages.fixFiles\n : null;\n useEffect(() => {\n onWait?.(wait);\n }, [wait, onWait]);\n\n // Files in whose names are not known here are asked for, once.\n const missing = ids.filter((id) => !files?.known.has(id)).join(\" \");\n useEffect(() => {\n if (missing !== \"\") files?.find?.(missing.split(\" \"));\n }, [missing, files]);\n\n const held = uploads.items.some((item) => item.state !== \"refused\" && item.state !== \"failed\");\n const zone = closed ? uploads.items.length === 0 : multiple || !held;\n const { id, labelledBy } = control;\n\n return (\n <div className={part(\"sc-upload\")}>\n {zone && (\n <button\n type=\"button\"\n id={id}\n disabled={closed}\n data-drop\n data-over={over || undefined}\n data-invalid={control.invalid || undefined}\n {...ariaOf(control)}\n // Its name is its label and what it says.\n aria-labelledby={\n labelledBy === undefined || id === undefined ? labelledBy : `${labelledBy} ${id}`\n }\n aria-required={undefined}\n onClick={() => picker.current?.click()}\n onDragOver={(event) => {\n if (closed || !carriesFiles(event)) return;\n event.preventDefault();\n event.dataTransfer.dropEffect = \"copy\";\n setOver(true);\n }}\n onDragLeave={() => {\n setOver(false);\n }}\n onDrop={(event) => {\n if (closed) return;\n event.preventDefault();\n setOver(false);\n uploads.add([...event.dataTransfer.files]);\n }}\n className={part(\"sc-upload__zone\")}\n >\n <span aria-hidden className={part(\"sc-upload__icon\")}>\n <UploadSimpleIcon weight=\"bold\" />\n </span>\n <span className={part(\"sc-upload__text\")}>\n <strong>{messages.upload(multiple)}</strong>\n <small>\n {messages.drop}. {limitsOf(accept, maxMb, code, messages)}\n </small>\n </span>\n </button>\n )}\n <input\n ref={picker}\n type=\"file\"\n hidden\n tabIndex={-1}\n accept={accept?.join(\",\")}\n multiple={multiple}\n onChange={(event) => {\n uploads.add([...(event.target.files ?? [])]);\n event.target.value = \"\";\n }}\n />\n {uploads.items.length > 0 && <UploadChips uploads={uploads} host={files} closed={closed} />}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;AAgCA,SAAgB,SAAS,EACvB,MACA,MACA,OAAO,IACP,UACA,SACA,QACA,SACA,YACgB;CAChB,MAAM,EAAE,MAAM,aAAa,OAAO;CAClC,MAAM,OAAO,SAAS;CACtB,MAAM,SAAS,SAAS,MAAM,IAAI;CAElC,MAAM,OACJ,YACC,aAAa,KAAA,IACV,GAAG,SAAS,UAAU,GAAG,IAAI,KAAK,aAAa,MAAM,EAAE,OAAO,UAAU,CAAC,CAAC,CAAC,OAAO,QAAQ,MAC1F,SAAS,KAAA,IACP,WAAW,MAAM,IAAI,IACrB;CACR,MAAM,OACJ,qBAAA,YAAA,EAAA,UAAA,CACE,oBAAC,UAAD;EAAgB;EAAY;EAAM,MAAM;CAAK,CAAA,GAC7C,qBAAC,QAAD;EAAM,WAAW,KAAK,qBAAqB;EAA3C,UAAA,CACE,oBAAC,QAAD;GAAM,WAAW,KAAK,qBAAqB;GAAG,OAAO;GAClD,UAAA;EACG,CAAA,GACL,SAAS,MAAM,oBAAC,QAAD;GAAM,WAAW,KAAK,qBAAqB;GAAI,UAAA;EAAW,CAAA,CACtE;CACN,CAAA,CAAA,EAAA,CAAA;CAEJ,OACE,qBAAC,OAAD;EACE,eAAa;EACb,WAAW,KAAK,iBAAiB,GAAI,UAAW,CAAC,uBAAuB,IAAc,CAAC,CAAE;EAF3F,UAAA;GAIG,SACC,oBAAC,UAAD;IACE,MAAK;IACL,SAAS;IACT,cAAY,SAAS,KAAK,IAAI;IAC9B,WAAW,KAAK,qBAAqB;IAEpC,UAAA;GACK,CAAA,IAER,oBAAC,QAAD;IAAM,WAAW,KAAK,qBAAqB;IAAI,UAAA;GAAW,CAAA;GAE3D,WACC,oBAAC,UAAD;IACE,MAAK;IACL,SAAS;IACT,cAAY,SAAS;IACrB,OAAO,SAAS;IAChB,WAAW,KAAK,uBAAuB;IAEvC,UAAA,oBAAC,oBAAD;KAAoB,eAAA;KAAY,QAAO;IAAQ,CAAA;GACzC,CAAA;GAET,YACC,oBAAC,UAAD;IACE,MAAK;IACL,SAAS;IACT,cAAY,SAAS,OAAO,IAAI;IAChC,OAAO,SAAS,OAAO,IAAI;IAC3B,WAAW,KAAK,uBAAuB;IAEvC,UAAA,oBAAC,OAAD;KAAO,eAAA;KAAY,QAAO;IAAQ,CAAA;GAC5B,CAAA;GAET,aAAa,KAAA,KACZ,oBAAC,QAAD;IACE,MAAK;IACL,cAAY;IACZ,iBAAe;IACf,iBAAe;IACf,iBAAe,KAAK,MAAM,WAAW,GAAG;IACxC,OAAO,EAAE,eAAe,SAAS;IACjC,WAAW,KAAK,oBAAoB;GACrC,CAAA;EAEA;;AAET;;AAGA,SAAgB,UAAU,EACxB,OACA,UAIC;CACD,MAAM,OAAO,SAAS;CACtB,OACE,oBAAC,MAAD;EAAI,WAAW,KAAK,gBAAgB;EACjC,UAAA,MAAM,KAAK,SACV,oBAAC,MAAD,EAAA,UACE,oBAAC,UAAD;GACE,MAAM,KAAK;GACX,MAAM,KAAK;GACX,MAAM,KAAK;GACX,QAAQ,iBAAiB,OAAO,IAAI;EACrC,CAAA,EACC,GAPK,KAAK,EAOV,CACL;CACC,CAAA;AAER;AA8CA,IAAI,QAAQ;;AAGZ,SAAgB,SAAS,IAAY,OAAuC;CAC1E,OAAO;EACL,KAAK,EAAE;EACP,MAAM,OAAO,QAAQ;EACrB,MAAM,OAAO;EACb,MAAM,OAAO,cAAc;EAC3B,OAAO;EACP,UAAU;EACV;EACA,SAAS;EACT,MAAM;CACR;AACF;;;;;;;AAQA,SAAgB,WACd,MACA,EAAE,QAAQ,OAAO,WAAW,MAAM,MAAM,YACxC,cAA8B,CAAC,GACtB;CACT,MAAM,EAAE,MAAM,aAAa,OAAO;CAClC,MAAM,CAAC,OAAO,YAAY,SAAmB,KAAK;CAClD,MAAM,UAAU,uBAAO,IAAI,IAA6B,CAAC;CAEzD,gBAAgB;EACd,MAAM,MAAM,QAAQ;EACpB,aAAa;GACX,KAAK,MAAM,cAAc,IAAI,OAAO,GAAG,WAAW,MAAM;GACxD,IAAI,MAAM;EACZ;CACF,GAAG,CAAC,CAAC;CAEL,MAAM,UAAU,KAAa,UAA2B;EACtD,UAAU,QAAQ,IAAI,KAAK,SAAU,KAAK,QAAQ,MAAM;GAAE,GAAG;GAAM,GAAG;EAAM,IAAI,IAAK,CAAC;CACxF;CAEA,MAAM,SAAS,QAAgB;EAC7B,QAAQ,QAAQ,IAAI,GAAG,CAAC,EAAE,MAAM;EAChC,QAAQ,QAAQ,OAAO,GAAG;CAC5B;CAEA,MAAM,QAAQ,KAAa,SAAe;EACxC,IAAI,CAAC,MAAM;EACX,MAAM,aAAa,IAAI,gBAAgB;EACvC,QAAQ,QAAQ,IAAI,KAAK,UAAU;EACnC,KACG,OAAO,OAAO,aAAa,OAAO,KAAK,EAAE,SAAS,CAAC,GAAG,WAAW,MAAM,CAAC,CACxE,MACE,WAAW;GACV,OAAO,KAAK;IAAE,OAAO;IAAQ,IAAI,OAAO;IAAI,UAAU;IAAG,MAAM;GAAK,CAAC;EACvE,IACC,UAAmB;GAClB,IAAI,QAAQ,KAAK,GAAG;GACpB,OAAO,KAAK;IAAE,OAAO;IAAU,SAAS,UAAU,OAAO,OAAO,MAAM,QAAQ;GAAE,CAAC;EACnF,CACF,CAAC,CACA,cAAc;GACb,IAAI,QAAQ,QAAQ,IAAI,GAAG,MAAM,YAAY,QAAQ,QAAQ,OAAO,GAAG;EACzE,CAAC;CACL;CAEA,MAAM,OAAO,UAA2B;EACtC,IAAI,CAAC,QAAQ,MAAM,WAAW,GAAG;EACjC,MAAM,SAAS,WAAW,QAAQ,MAAM,MAAM,GAAG,CAAC;EAClD,MAAM,OAAO,WAAW,MAAM,QAAQ,SAAS,KAAK,UAAU,SAAS,CAAC,CAAC,SAAS;EAClF,MAAM,QAAQ,OAAO,KAAK,MAAM,UAAkB;GAChD,MAAM,UACJ,OAAO,SAAS,MACZ,SAAS,QAAQ,GAAG,IACpB,UAAU,MAAM,QAAQ,OAAO,MAAM,QAAQ;GACnD,OAAO;IACL,KAAK,EAAE;IACP,MAAM,KAAK,QAAQ,SAAS;IAC5B,MAAM,KAAK;IACX,MAAM,KAAK;IACX,OAAO,YAAY,OAAO,cAAc;IACxC,UAAU;IACV,IAAI;IACJ;IACA,MAAM,YAAY,OAAO,OAAO;GAClC;EACF,CAAC;EACD,IAAI,CAAC,UAAU,KAAK,MAAM,OAAO,QAAQ,QAAQ,KAAK,GAAG,MAAM,GAAG;EAClE,UAAU,QAAS,WAAW,CAAC,GAAG,KAAK,GAAG,KAAK,IAAI,KAAM;EACzD,KAAK,MAAM,QAAQ,OAAO,IAAI,KAAK,MAAM,KAAK,KAAK,KAAK,KAAK,IAAI;CACnE;CAEA,MAAM,UAAU,QAAgB;EAC9B,MAAM,GAAG;EACT,UAAU,QAAQ,IAAI,QAAQ,SAAS,KAAK,QAAQ,GAAG,CAAC;CAC1D;CAEA,MAAM,SAAS,QAAgB;EAC7B,MAAM,OAAO,MAAM,MAAM,SAAS,KAAK,QAAQ,GAAG;EAClD,IAAI,MAAM,UAAU,YAAY,CAAC,KAAK,MAAM;EAC5C,OAAO,KAAK;GAAE,OAAO;GAAa,UAAU;GAAG,SAAS;EAAK,CAAC;EAC9D,KAAK,KAAK,KAAK,IAAI;CACrB;CAEA,MAAM,cAAc;EAClB,KAAK,MAAM,OAAO,CAAC,GAAG,QAAQ,QAAQ,KAAK,CAAC,GAAG,MAAM,GAAG;EACxD,SAAS,CAAC,CAAC;CACb;CAQA,OAAO;EAAE;EAAO,KANJ,MAAM,SAAS,SAAU,KAAK,UAAU,UAAU,KAAK,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,CAMnE;EAAG,SALL,MAAM,MAAM,SAAS,KAAK,UAAU,WAAW,IAC3D,cACA,MAAM,MAAM,SAAS,KAAK,UAAU,YAAY,KAAK,UAAU,SAAS,IACtE,WACA;EACwB;EAAK;EAAQ;EAAO;CAAM;AAC1D;;;;;AAMA,SAAgB,YAAY,EAC1B,SACA,MACA,SAAS,SAKR;CACD,MAAM,EAAE,aAAa,OAAO;CAC5B,MAAM,OAAO,SAAS;CACtB,OACE,oBAAC,MAAD;EAAI,WAAW,KAAK,gBAAgB;EACjC,UAAA,QAAQ,MAAM,KAAK,SAAS;GAC3B,MAAM,SAAS,KAAK,OAAO,OAAO,MAAM,MAAM,IAAI,KAAK,EAAE,IAAI,KAAA;GAC7D,MAAM,EAAE,SAAS,QAAQ,CAAC;GAC1B,OACE,oBAAC,MAAD,EAAA,UACE,oBAAC,UAAD;IACE,MAAM,QAAQ,SAAS,KAAK,QAAQ,SAAS;IAC7C,MAAM,QAAQ,QAAQ,KAAK;IAC3B,MAAM,QAAQ,cAAc,KAAK;IACjC,UAAU,KAAK,UAAU,cAAc,KAAK,WAAW,KAAA;IACvD,SAAS,KAAK;IACd,QAAQ,UAAU,eAAe,KAAK,MAAM;IAC5C,SACE,CAAC,UAAU,KAAK,UAAU,iBAAiB,QAAQ,MAAM,KAAK,GAAG,IAAI,KAAA;IAEvE,UAAU,SAAS,KAAA,UAAkB,QAAQ,OAAO,KAAK,GAAG;GAC7D,CAAA,EACC,GAbK,KAAK,GAaV;EAER,CAAC;CACC,CAAA;AAER;;AAGA,SAAgB,aAAa,OAA2B;CACtD,OAAO,CAAC,GAAG,MAAM,aAAa,KAAK,CAAC,CAAC,SAAS,OAAO;AACvD;;;;;;;;;AA8BA,SAAgB,UAAU,EACxB,OACA,UACA,OACA,QACA,WAAW,OACX,OACA,QACA,GAAG,WACc;CACjB,MAAM,EAAE,MAAM,aAAa,OAAO;CAClC,MAAM,OAAO,SAAS;CACtB,MAAM,SAAS,OAAyB,IAAI;CAC5C,MAAM,CAAC,MAAM,WAAW,SAAS,KAAK;CACtC,MAAM,UAAU,WAAW,OAAO;EAAE;EAAQ;EAAO;CAAS,SAC1D,MAAM,KAAK,OAAO,SAAS,IAAI,OAAO,MAAM,IAAI,EAAE,CAAC,CAAC,CACtD;CACA,MAAM,SAAS,QAAQ,aAAa,QAAQ,UAAU,KAAA;CAGtD,MAAM,OAAO,OAAO,MAAM,KAAK,GAAG,CAAC;CACnC,MAAM,MAAM,QAAQ;CACpB,gBAAgB;EACd,MAAM,MAAM,IAAI,KAAK,GAAG;EACxB,IAAI,QAAQ,KAAK,SAAS;EAC1B,KAAK,UAAU;EACf,SAAS,GAAG;CACd,GAAG,CAAC,KAAK,QAAQ,CAAC;CAElB,MAAM,OACJ,QAAQ,YAAY,cAChB,SAAS,iBACT,QAAQ,YAAY,WAClB,SAAS,WACT;CACR,gBAAgB;EACd,SAAS,IAAI;CACf,GAAG,CAAC,MAAM,MAAM,CAAC;CAGjB,MAAM,UAAU,IAAI,QAAQ,OAAO,CAAC,OAAO,MAAM,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG;CAClE,gBAAgB;EACd,IAAI,YAAY,IAAI,OAAO,OAAO,QAAQ,MAAM,GAAG,CAAC;CACtD,GAAG,CAAC,SAAS,KAAK,CAAC;CAEnB,MAAM,OAAO,QAAQ,MAAM,MAAM,SAAS,KAAK,UAAU,aAAa,KAAK,UAAU,QAAQ;CAC7F,MAAM,OAAO,SAAS,QAAQ,MAAM,WAAW,IAAI,YAAY,CAAC;CAChE,MAAM,EAAE,IAAI,eAAe;CAE3B,OACE,qBAAC,OAAD;EAAK,WAAW,KAAK,WAAW;EAAhC,UAAA;GACG,QACC,qBAAC,UAAD;IACE,MAAK;IACD;IACJ,UAAU;IACV,aAAA;IACA,aAAW,QAAQ,KAAA;IACnB,gBAAc,QAAQ,WAAW,KAAA;IACjC,GAAI,OAAO,OAAO;IAElB,mBACE,eAAe,KAAA,KAAa,OAAO,KAAA,IAAY,aAAa,GAAG,WAAW,GAAG;IAE/E,iBAAe,KAAA;IACf,eAAe,OAAO,SAAS,MAAM;IACrC,aAAa,UAAU;KACrB,IAAI,UAAU,CAAC,aAAa,KAAK,GAAG;KACpC,MAAM,eAAe;KACrB,MAAM,aAAa,aAAa;KAChC,QAAQ,IAAI;IACd;IACA,mBAAmB;KACjB,QAAQ,KAAK;IACf;IACA,SAAS,UAAU;KACjB,IAAI,QAAQ;KACZ,MAAM,eAAe;KACrB,QAAQ,KAAK;KACb,QAAQ,IAAI,CAAC,GAAG,MAAM,aAAa,KAAK,CAAC;IAC3C;IACA,WAAW,KAAK,iBAAiB;IA7BnC,UAAA,CA+BE,oBAAC,QAAD;KAAM,eAAA;KAAY,WAAW,KAAK,iBAAiB;KACjD,UAAA,oBAAC,kBAAD,EAAkB,QAAO,OAAQ,CAAA;IAC7B,CAAA,GACN,qBAAC,QAAD;KAAM,WAAW,KAAK,iBAAiB;KAAvC,UAAA,CACE,oBAAC,UAAD,EAAA,UAAS,SAAS,OAAO,QAAQ,EAAU,CAAA,GAC3C,qBAAC,SAAD,EAAA,UAAA;MACG,SAAS;MAAK;MAAG,SAAS,QAAQ,OAAO,MAAM,QAAQ;KACnD,EAAA,CAAA,CACH;IACA,CAAA,CAAA;;GAEV,oBAAC,SAAD;IACE,KAAK;IACL,MAAK;IACL,QAAA;IACA,UAAU;IACV,QAAQ,QAAQ,KAAK,GAAG;IACd;IACV,WAAW,UAAU;KACnB,QAAQ,IAAI,CAAC,GAAI,MAAM,OAAO,SAAS,CAAC,CAAE,CAAC;KAC3C,MAAM,OAAO,QAAQ;IACvB;GACD,CAAA;GACA,QAAQ,MAAM,SAAS,KAAK,oBAAC,aAAD;IAAsB;IAAS,MAAM;IAAe;GAAS,CAAA;EACvF;;AAET"}
|
|
1
|
+
{"version":3,"file":"FileField.js","names":[],"sources":["../../src/kit/FileField.tsx"],"sourcesContent":["import { ArrowClockwiseIcon, UploadSimpleIcon, XIcon } from \"@phosphor-icons/react\";\nimport { useEffect, useRef, useState, type CSSProperties, type DragEvent } from \"react\";\n\nimport type { Attachment } from \"../types\";\nimport { useKit, useParts } from \"./context\";\nimport { ariaOf, type ControlBase } from \"./Field\";\nimport { FileMark } from \"./FileMark\";\nimport { formatSize } from \"./numbers\";\nimport { failureOf, familyOf, isAbort, limitsOf, refusalOf, type FileHost } from \"./uploads\";\n\n// --- One file -----------------------------------------------------------------------------\n\nexport interface FileChipProps {\n name: string;\n /** Its weight in bytes; none while it is not known. */\n size?: number | undefined;\n /** Its type, for its glyph when its name has no extension. */\n type?: string | undefined;\n /** On its way: how much of it has gone, from 0 to 1. */\n progress?: number | undefined;\n /** What went wrong with it, in place of its size. */\n problem?: string | null | undefined;\n /** Opens it: the chip is a button. */\n onOpen?: (() => void) | undefined;\n onRetry?: (() => void) | undefined;\n onRemove?: (() => void) | undefined;\n}\n\n/**\n * A file as a chip: the glyph of its family, its name, and under it its size, how much has gone while it uploads (a line filling at its foot), or what went wrong in the danger\n * tone; at its end, trying it again and taking it away. With `onOpen` it opens on a click.\n */\nexport function FileChip({\n name,\n size,\n type = \"\",\n progress,\n problem,\n onOpen,\n onRetry,\n onRemove,\n}: FileChipProps) {\n const { code, messages } = useKit();\n const part = useParts();\n const family = familyOf(name, type);\n // The name says its extension; under it, how big it is, or how far it has gone.\n const meta =\n problem ??\n (progress !== undefined\n ? `${messages.uploading} ${new Intl.NumberFormat(code, { style: \"percent\" }).format(progress)}`\n : size !== undefined\n ? formatSize(size, code)\n : \"\");\n const body = (\n <>\n <FileMark name={name} type={type} size={28} />\n <span className={part(\"sc-attachment__text\")}>\n <span className={part(\"sc-attachment__name\")} title={name}>\n {name}\n </span>\n {meta !== \"\" && <span className={part(\"sc-attachment__meta\")}>{meta}</span>}\n </span>\n </>\n );\n return (\n <div\n data-family={family}\n className={part(\"sc-attachment\", ...(problem ? ([\"sc-attachment--failed\"] as const) : []))}\n >\n {onOpen ? (\n <button\n type=\"button\"\n onClick={onOpen}\n aria-label={messages.open(name)}\n className={part(\"sc-attachment__main\")}\n >\n {body}\n </button>\n ) : (\n <span className={part(\"sc-attachment__main\")}>{body}</span>\n )}\n {onRetry && (\n <button\n type=\"button\"\n onClick={onRetry}\n aria-label={messages.retry}\n title={messages.retry}\n className={part(\"sc-attachment__action\")}\n >\n <ArrowClockwiseIcon aria-hidden weight=\"bold\" />\n </button>\n )}\n {onRemove && (\n <button\n type=\"button\"\n onClick={onRemove}\n aria-label={messages.remove(name)}\n title={messages.remove(name)}\n className={part(\"sc-attachment__action\")}\n >\n <XIcon aria-hidden weight=\"bold\" />\n </button>\n )}\n {progress !== undefined && (\n <span\n role=\"progressbar\"\n aria-label={name}\n aria-valuemin={0}\n aria-valuemax={100}\n aria-valuenow={Math.round(progress * 100)}\n style={{ \"--_progress\": progress } as CSSProperties}\n className={part(\"sc-attachment__bar\")}\n />\n )}\n </div>\n );\n}\n\n/** Files already sent, as chips in a row that wraps; with `onOpen`, each opens on a click. */\nexport function FileChips({\n files,\n onOpen,\n}: {\n files: readonly Attachment[];\n onOpen?: ((file: Attachment) => void) | undefined;\n}) {\n const part = useParts();\n return (\n <ul className={part(\"sc-attachments\")}>\n {files.map((file) => (\n <li key={file.id}>\n <FileChip\n name={file.name}\n size={file.size}\n type={file.media_type}\n onOpen={onOpen && (() => onOpen(file))}\n />\n </li>\n ))}\n </ul>\n );\n}\n\n// --- Files on their way -------------------------------------------------------------------\n\n/** A file picked here, on its way or in: what its chip draws. */\nexport interface Upload {\n /** Tells it from the others here. */\n key: number;\n name: string;\n size: number | undefined;\n type: string;\n /**\n * uploading: on its way (`progress`, 0 to 1); done: stored as `id`; failed: its upload failed,\n * to be tried again; refused: never sent, a kind not taken or too heavy (`problem` says which).\n */\n state: \"uploading\" | \"done\" | \"failed\" | \"refused\";\n progress: number;\n id: string | null;\n problem: string | null;\n /** The bytes, kept to try again. */\n file: File | null;\n}\n\n/** What files may be picked: their kinds, their most in MB, one or many, how many at most. */\nexport interface UploadRules {\n accept?: readonly string[] | undefined;\n maxMb?: number | undefined;\n /** False: one file, a new pick replacing it. */\n multiple?: boolean | undefined;\n max?: number | undefined;\n}\n\n/** Files picked and how each goes, and what can be done with them. */\nexport interface Uploads {\n items: readonly Upload[];\n /** The ids of the files in, in the order they were picked. */\n ids: string[];\n /** What the files still wait on: one on its way, or one that failed or was refused; null once all are in. */\n waiting: \"uploading\" | \"failed\" | null;\n add: (files: readonly File[]) => void;\n remove: (key: number) => void;\n retry: (key: number) => void;\n /** Takes them all away, letting go of any on its way. */\n clear: () => void;\n}\n\nlet picks = 0;\n\n/** A file already in, by its id: what `useUploads` starts from. */\nfunction uploaded(id: string, known: Attachment | undefined): Upload {\n return {\n key: ++picks,\n name: known?.name ?? \"\",\n size: known?.size,\n type: known?.media_type ?? \"\",\n state: \"done\",\n progress: 1,\n id,\n problem: null,\n file: null,\n };\n}\n\n/**\n * Files picked and sent at once through `host`: each is refused before it goes when it is not a\n * kind taken, is too heavy, or is one too many; then it is on its way, then in (its id) or failed\n * (to be tried again). With `multiple` false a new pick replaces what was there. Whatever is on\n * its way when its file goes (taken away, cleared, the component gone) is let go.\n */\nexport function useUploads(\n host: FileHost | undefined,\n { accept, maxMb, multiple = true, max = Infinity }: UploadRules,\n start: () => Upload[] = () => [],\n): Uploads {\n const { code, messages } = useKit();\n const [items, setItems] = useState<Upload[]>(start);\n const running = useRef(new Map<number, AbortController>());\n\n useEffect(() => {\n const now = running.current;\n return () => {\n for (const controller of now.values()) controller.abort();\n now.clear();\n };\n }, []);\n\n const change = (key: number, patch: Partial<Upload>) => {\n setItems((now) => now.map((item) => (item.key === key ? { ...item, ...patch } : item)));\n };\n\n const letGo = (key: number) => {\n running.current.get(key)?.abort();\n running.current.delete(key);\n };\n\n const send = (key: number, file: File) => {\n if (!host) return;\n const controller = new AbortController();\n running.current.set(key, controller);\n host\n .upload(file, (progress) => change(key, { progress }), controller.signal)\n .then(\n (stored) => {\n change(key, { state: \"done\", id: stored.id, progress: 1, file: null });\n },\n (error: unknown) => {\n if (isAbort(error)) return;\n change(key, { state: \"failed\", problem: failureOf(error, maxMb, code, messages) });\n },\n )\n .finally(() => {\n if (running.current.get(key) === controller) running.current.delete(key);\n });\n };\n\n const add = (files: readonly File[]) => {\n if (!host || files.length === 0) return;\n const picked = multiple ? files : files.slice(0, 1);\n const kept = multiple ? items.filter((item) => item.state !== \"refused\").length : 0;\n const fresh = picked.map((file, index): Upload => {\n const problem =\n kept + index >= max\n ? messages.tooMany(max)\n : refusalOf(file, accept, maxMb, code, messages);\n return {\n key: ++picks,\n name: file.name || messages.file,\n size: file.size,\n type: file.type,\n state: problem === null ? \"uploading\" : \"refused\",\n progress: 0,\n id: null,\n problem,\n file: problem === null ? file : null,\n };\n });\n if (!multiple) for (const key of running.current.keys()) letGo(key);\n setItems((now) => (multiple ? [...now, ...fresh] : fresh));\n for (const item of fresh) if (item.file) send(item.key, item.file);\n };\n\n const remove = (key: number) => {\n letGo(key);\n setItems((now) => now.filter((item) => item.key !== key));\n };\n\n const retry = (key: number) => {\n const item = items.find((each) => each.key === key);\n if (item?.state !== \"failed\" || !item.file) return;\n change(key, { state: \"uploading\", progress: 0, problem: null });\n send(key, item.file);\n };\n\n const clear = () => {\n for (const key of [...running.current.keys()]) letGo(key);\n setItems([]);\n };\n\n const ids = items.flatMap((item) => (item.state === \"done\" && item.id ? [item.id] : []));\n const waiting = items.some((item) => item.state === \"uploading\")\n ? \"uploading\"\n : items.some((item) => item.state === \"failed\" || item.state === \"refused\")\n ? \"failed\"\n : null;\n return { items, ids, waiting, add, remove, retry, clear };\n}\n\n/**\n * The files picked, as chips: each with how it goes; taken away while they can be, the failed\n * tried again; once in, a name not known here is read from the host's files, and opens there.\n */\nexport function UploadChips({\n uploads,\n host,\n closed = false,\n}: {\n uploads: Uploads;\n host: FileHost | undefined;\n closed?: boolean | undefined;\n}) {\n const { messages } = useKit();\n const part = useParts();\n return (\n <ul className={part(\"sc-attachments\")}>\n {uploads.items.map((item) => {\n const stored = item.id !== null ? host?.known.get(item.id) : undefined;\n const { open } = host ?? {};\n return (\n <li key={item.key}>\n <FileChip\n name={stored?.name ?? (item.name || messages.file)}\n size={stored?.size ?? item.size}\n type={stored?.media_type ?? item.type}\n progress={item.state === \"uploading\" ? item.progress : undefined}\n problem={item.problem}\n onOpen={stored && open && (() => open(stored))}\n onRetry={\n !closed && item.state === \"failed\" ? () => uploads.retry(item.key) : undefined\n }\n onRemove={closed ? undefined : () => uploads.remove(item.key)}\n />\n </li>\n );\n })}\n </ul>\n );\n}\n\n/** Whether what is dragged carries files (not words, not a link). */\nexport function carriesFiles(event: DragEvent): boolean {\n return [...event.dataTransfer.types].includes(\"Files\");\n}\n\n// --- The field ----------------------------------------------------------------------------\n\nexport interface FileFieldProps extends ControlBase {\n /** The ids of the files in. */\n value: string[];\n onChange: (ids: string[]) => void;\n /** Where its files go and are found; none: it takes none (closed). */\n files?: FileHost | undefined;\n /** The kinds it takes: types, families (\"image/*\"), extensions (\".xlsx\"); none: any. */\n accept?: readonly string[] | undefined;\n multiple?: boolean | undefined;\n /** The most a file may weigh, in MB (50 at most). */\n maxMb?: number | undefined;\n /**\n * Hears what the field still waits on before its form may go, in words (a file on its way,\n * one that failed), and null once nothing.\n */\n onWait?: ((problem: string | null) => void) | undefined;\n}\n\n/**\n * Files to give: a zone to drop them on or press to choose them, saying what it takes; each file\n * uploads at once (a line fills under it), then sits as a chip to take away; what is wrong with\n * one (too heavy, a kind not taken, an upload that failed, to try again) says so on its chip.\n * The value is the ids of the files in. One file unless `multiple`: once it is in, the zone\n * steps aside until it is taken away. Closed, the chips are read, and open where the host shows\n * files.\n */\nexport function FileField({\n value,\n onChange,\n files,\n accept,\n multiple = false,\n maxMb,\n onWait,\n ...control\n}: FileFieldProps) {\n const { code, messages } = useKit();\n const part = useParts();\n const picker = useRef<HTMLInputElement>(null);\n const [over, setOver] = useState(false);\n const uploads = useUploads(files, { accept, maxMb, multiple }, () =>\n value.map((id) => uploaded(id, files?.known.get(id))),\n );\n const closed = control.disabled === true || files === undefined;\n\n // The ids go up whenever the files in change.\n const sent = useRef(value.join(\" \"));\n const ids = uploads.ids;\n useEffect(() => {\n const now = ids.join(\" \");\n if (now === sent.current) return;\n sent.current = now;\n onChange(ids);\n }, [ids, onChange]);\n\n const wait =\n uploads.waiting === \"uploading\"\n ? messages.stillUploading\n : uploads.waiting === \"failed\"\n ? messages.fixFiles\n : null;\n useEffect(() => {\n onWait?.(wait);\n }, [wait, onWait]);\n\n // Files in whose names are not known here are asked for, once.\n const missing = ids.filter((id) => !files?.known.has(id)).join(\" \");\n useEffect(() => {\n if (missing !== \"\") files?.find?.(missing.split(\" \"));\n }, [missing, files]);\n\n const held = uploads.items.some((item) => item.state !== \"refused\" && item.state !== \"failed\");\n const zone = closed ? uploads.items.length === 0 : multiple || !held;\n const { id, labelledBy } = control;\n\n return (\n <div className={part(\"sc-upload\")}>\n {zone && (\n <button\n type=\"button\"\n id={id}\n disabled={closed}\n data-drop\n data-over={over || undefined}\n data-invalid={control.invalid || undefined}\n {...ariaOf(control)}\n // Its name is its label and what it says.\n aria-labelledby={\n labelledBy === undefined || id === undefined ? labelledBy : `${labelledBy} ${id}`\n }\n aria-required={undefined}\n onClick={() => picker.current?.click()}\n onDragOver={(event) => {\n if (closed || !carriesFiles(event)) return;\n event.preventDefault();\n event.dataTransfer.dropEffect = \"copy\";\n setOver(true);\n }}\n onDragLeave={() => {\n setOver(false);\n }}\n onDrop={(event) => {\n if (closed) return;\n event.preventDefault();\n setOver(false);\n uploads.add([...event.dataTransfer.files]);\n }}\n className={part(\"sc-upload__zone\")}\n >\n <span aria-hidden className={part(\"sc-upload__icon\")}>\n <UploadSimpleIcon weight=\"bold\" />\n </span>\n <span className={part(\"sc-upload__text\")}>\n <strong>{messages.upload(multiple)}</strong>\n <small>\n {messages.drop}. {limitsOf(accept, maxMb, code, messages)}\n </small>\n </span>\n </button>\n )}\n <input\n ref={picker}\n type=\"file\"\n hidden\n tabIndex={-1}\n accept={accept?.join(\",\")}\n multiple={multiple}\n onChange={(event) => {\n uploads.add([...(event.target.files ?? [])]);\n event.target.value = \"\";\n }}\n />\n {uploads.items.length > 0 && <UploadChips uploads={uploads} host={files} closed={closed} />}\n </div>\n );\n}\n"],"mappings":"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"}
|
package/dist/kit/Popover.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Popover.js","names":[],"sources":["../../src/kit/Popover.tsx"],"sourcesContent":["import {\n autoUpdate,\n flip,\n FloatingFocusManager,\n FloatingPortal,\n offset,\n shift,\n size,\n useDismiss,\n useFloating,\n useInteractions,\n useRole,\n} from \"@floating-ui/react\";\nimport type { ReactNode } from \"react\";\n\nimport { useParts } from \"./context\";\n\n/** The least height a panel keeps, flipping above its trigger before it shrinks under this. */\nconst LEAST = 180;\n\n/**\n * A panel that floats by its trigger: under it, flipped above when there is no room, kept in the\n * window, as tall as fits; Escape or a press outside closes it. `width` \"trigger\" makes it as\n * wide as its trigger (a list), \"own\" leaves it its own (a calendar). `role` is the panel's for\n * a dialog; a combobox words its own.\n */\nexport function usePopover(\n open: boolean,\n setOpen: (open: boolean) => void,\n { width, role }: { width: \"trigger\" | \"own\"; role?: \"dialog\" },\n) {\n const floating = useFloating({\n open,\n onOpenChange: setOpen,\n placement: \"bottom-start\",\n // Placed by its offsets, so its opening can move it with a transform of its own.\n transform: false,\n whileElementsMounted: autoUpdate,\n middleware: [\n offset(6),\n flip({ padding: 8, fallbackStrategy: \"initialPlacement\" }),\n shift({ padding: 8 }),\n size({\n padding: 8,\n apply({ rects, availableHeight, elements }) {\n elements.floating.style.maxHeight = `${Math.max(LEAST, availableHeight)}px`;\n if (width === \"trigger\") elements.floating.style.width = `${rects.reference.width}px`;\n },\n }),\n ],\n });\n const dismiss = useDismiss(floating.context);\n const dialog = useRole(floating.context, { role: \"dialog\", enabled: role === \"dialog\" });\n const interactions = useInteractions([dismiss, dialog]);\n return { ...floating, ...interactions };\n}\n\
|
|
1
|
+
{"version":3,"file":"Popover.js","names":[],"sources":["../../src/kit/Popover.tsx"],"sourcesContent":["import {\n autoUpdate,\n flip,\n FloatingFocusManager,\n FloatingPortal,\n offset,\n shift,\n size,\n useDismiss,\n useFloating,\n useInteractions,\n useRole,\n} from \"@floating-ui/react\";\nimport type { ReactNode } from \"react\";\n\nimport { useParts } from \"./context\";\n\n/** The least height a panel keeps, flipping above its trigger before it shrinks under this. */\nconst LEAST = 180;\n\n/**\n * A panel that floats by its trigger: under it, flipped above when there is no room, kept in the\n * window, as tall as fits; Escape or a press outside closes it. `width` \"trigger\" makes it as\n * wide as its trigger (a list), \"own\" leaves it its own (a calendar). `role` is the panel's for\n * a dialog; a combobox words its own.\n */\nexport function usePopover(\n open: boolean,\n setOpen: (open: boolean) => void,\n { width, role }: { width: \"trigger\" | \"own\"; role?: \"dialog\" },\n) {\n const floating = useFloating({\n open,\n onOpenChange: setOpen,\n placement: \"bottom-start\",\n // Placed by its offsets, so its opening can move it with a transform of its own.\n transform: false,\n whileElementsMounted: autoUpdate,\n middleware: [\n offset(6),\n flip({ padding: 8, fallbackStrategy: \"initialPlacement\" }),\n shift({ padding: 8 }),\n size({\n padding: 8,\n apply({ rects, availableHeight, elements }) {\n elements.floating.style.maxHeight = `${Math.max(LEAST, availableHeight)}px`;\n if (width === \"trigger\") elements.floating.style.width = `${rects.reference.width}px`;\n },\n }),\n ],\n });\n const dismiss = useDismiss(floating.context);\n const dialog = useRole(floating.context, { role: \"dialog\", enabled: role === \"dialog\" });\n const interactions = useInteractions([dismiss, dialog]);\n return { ...floating, ...interactions };\n}\n\ntype PopoverState = ReturnType<typeof usePopover>;\n\n/**\n * Where the panel goes instead of the page's end: the host's dialog the trigger sits in (a modal\n * one keeps the pointer and the focus inside itself), or the element the host marked\n * `data-sc-portal` around it (a shadow root's, whose styles stop at its edge: the widget's).\n * None is `undefined`, the page's end: floating-ui reads a `null` root as one still to come, and\n * draws nothing until it does.\n */\nconst layerOf = (trigger: Element | null): HTMLElement | undefined =>\n trigger?.closest<HTMLElement>(\n '[role=\"dialog\"], [role=\"alertdialog\"], dialog, [data-sc-portal]',\n ) ?? undefined;\n\n/**\n * The panel itself, in a portal at the page's end so nothing clips it (inside the host's dialog\n * or `data-sc-portal`, when the trigger is in one); it carries `sc`, so the host's tokens reach\n * it there. With `focus` it takes the focus while open (a calendar, which puts it on a day) and\n * gives it back to the trigger on close; tabbing out closes it. Without, focus stays where it is\n * (a combobox's box).\n */\nexport function Popover({\n popover,\n focus,\n label,\n children,\n}: {\n popover: PopoverState;\n focus: boolean;\n label?: string | undefined;\n children: ReactNode;\n}) {\n const part = useParts();\n const panel = (\n <div\n ref={popover.refs.setFloating}\n style={popover.floatingStyles}\n aria-label={label}\n {...popover.getFloatingProps({\n // Escape closes the panel and goes no further: not the page's own Escape, not the chat's.\n onKeyDown: (event) => {\n if (event.key !== \"Escape\") return;\n event.preventDefault();\n event.stopPropagation();\n popover.context.onOpenChange(false);\n },\n })}\n className={`sc ${part(\"sc-popover\")}`}\n >\n {children}\n </div>\n );\n return (\n <FloatingPortal root={layerOf(popover.refs.domReference.current)}>\n {focus ? (\n <FloatingFocusManager context={popover.context} initialFocus={-1} modal={false}>\n {panel}\n </FloatingFocusManager>\n ) : (\n panel\n )}\n </FloatingPortal>\n );\n}\n"],"mappings":";;;;;AAkBA,MAAM,QAAQ;;;;;;;AAQd,SAAgB,WACd,MACA,SACA,EAAE,OAAO,QACT;CACA,MAAM,WAAW,YAAY;EAC3B;EACA,cAAc;EACd,WAAW;EAEX,WAAW;EACX,sBAAsB;EACtB,YAAY;GACV,OAAO,CAAC;GACR,KAAK;IAAE,SAAS;IAAG,kBAAkB;GAAmB,CAAC;GACzD,MAAM,EAAE,SAAS,EAAE,CAAC;GACpB,KAAK;IACH,SAAS;IACT,MAAM,EAAE,OAAO,iBAAiB,YAAY;KAC1C,SAAS,SAAS,MAAM,YAAY,GAAG,KAAK,IAAI,OAAO,eAAe,EAAE;KACxE,IAAI,UAAU,WAAW,SAAS,SAAS,MAAM,QAAQ,GAAG,MAAM,UAAU,MAAM;IACpF;GACF,CAAC;EACH;CACF,CAAC;CACD,MAAM,UAAU,WAAW,SAAS,OAAO;CAC3C,MAAM,SAAS,QAAQ,SAAS,SAAS;EAAE,MAAM;EAAU,SAAS,SAAS;CAAS,CAAC;CACvF,MAAM,eAAe,gBAAgB,CAAC,SAAS,MAAM,CAAC;CACtD,OAAO;EAAE,GAAG;EAAU,GAAG;CAAa;AACxC;;;;;;;;AAWA,MAAM,WAAW,YACf,SAAS,QACP,qEACF,KAAK,KAAA;;;;;;;;AASP,SAAgB,QAAQ,EACtB,SACA,OACA,OACA,YAMC;CACD,MAAM,OAAO,SAAS;CACtB,MAAM,QACJ,oBAAC,OAAD;EACE,KAAK,QAAQ,KAAK;EAClB,OAAO,QAAQ;EACf,cAAY;EACZ,GAAI,QAAQ,iBAAiB,EAE3B,YAAY,UAAU;GACpB,IAAI,MAAM,QAAQ,UAAU;GAC5B,MAAM,eAAe;GACrB,MAAM,gBAAgB;GACtB,QAAQ,QAAQ,aAAa,KAAK;EACpC,EACF,CAAC;EACD,WAAW,MAAM,KAAK,YAAY;EAEjC;CACE,CAAA;CAEP,OACE,oBAAC,gBAAD;EAAgB,MAAM,QAAQ,QAAQ,KAAK,aAAa,OAAO;EAC5D,UAAA,QACC,oBAAC,sBAAD;GAAsB,SAAS,QAAQ;GAAS,cAAc;GAAI,OAAO;GACtE,UAAA;EACmB,CAAA,IAEtB;CAEY,CAAA;AAEpB"}
|
package/dist/kit/Tile.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { ReactNode } from "react";
|
|
|
2
2
|
//#region src/kit/Tile.d.ts
|
|
3
3
|
/**
|
|
4
4
|
* Tiles in a grid that asks its own width, not the window's: two across, one when narrow, three
|
|
5
|
-
* when there is room. What an agent works with,
|
|
5
|
+
* when there is room. What an agent works with, the agents at a glance.
|
|
6
6
|
*/
|
|
7
7
|
export declare function Tiles({ label, className, children }: {
|
|
8
8
|
/** The grid's name, for screen readers, when no heading above it says it. */
|
package/dist/kit/Tile.js
CHANGED
|
@@ -3,7 +3,7 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
//#region src/kit/Tile.tsx
|
|
4
4
|
/**
|
|
5
5
|
* Tiles in a grid that asks its own width, not the window's: two across, one when narrow, three
|
|
6
|
-
* when there is room. What an agent works with,
|
|
6
|
+
* when there is room. What an agent works with, the agents at a glance.
|
|
7
7
|
*/
|
|
8
8
|
function Tiles({ label, className, children }) {
|
|
9
9
|
const part = useParts();
|
package/dist/kit/Tile.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Tile.js","names":[],"sources":["../../src/kit/Tile.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\n\nimport { joined, useParts } from \"./context\";\n\n/**\n * Tiles in a grid that asks its own width, not the window's: two across, one when narrow, three\n * when there is room. What an agent works with,
|
|
1
|
+
{"version":3,"file":"Tile.js","names":[],"sources":["../../src/kit/Tile.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\n\nimport { joined, useParts } from \"./context\";\n\n/**\n * Tiles in a grid that asks its own width, not the window's: two across, one when narrow, three\n * when there is room. What an agent works with, the agents at a glance.\n */\nexport function Tiles({\n label,\n className,\n children,\n}: {\n /** The grid's name, for screen readers, when no heading above it says it. */\n label?: string | undefined;\n className?: string | undefined;\n children: ReactNode;\n}) {\n const part = useParts();\n return (\n <div className={joined(part(\"sc-tiles\"), className)}>\n <ul aria-label={label} className={part(\"sc-tiles__grid\")}>\n {children}\n </ul>\n </div>\n );\n}\n\nexport interface TileProps {\n /** At its start, usually a small `Mark`: what kind of thing it is. */\n mark?: ReactNode;\n /** What it is, in a word or two. */\n title: ReactNode;\n /** How many it holds, at its end, in figures that line up. */\n count?: ReactNode;\n /** In the count's place, what says it better: a few logos and how many more. */\n end?: ReactNode;\n /** Pressing the tile: it opens what it stands for. */\n onPress?: (() => void) | undefined;\n /** Holding nothing yet: quieter, still to press. */\n quiet?: boolean | undefined;\n className?: string | undefined;\n}\n\n/**\n * One tile of `Tiles`, on one line: its mark, its name, and at its end its count (or what says it\n * better). With `onPress` the whole tile is a button.\n */\nexport function Tile({ mark, title, count, end, onPress, quiet, className }: TileProps) {\n const part = useParts();\n const body = (\n <>\n {mark}\n <span className={part(\"sc-tile__title\")}>{title}</span>\n {end ?? (count !== undefined && <span className={part(\"sc-tile__count\")}>{count}</span>)}\n </>\n );\n return (\n <li\n className={joined(\n part(\"sc-tile\", ...(quiet ? ([\"sc-tile--quiet\"] as const) : [])),\n className,\n )}\n >\n {onPress ? (\n <button type=\"button\" onClick={onPress} className={part(\"sc-tile__main\")}>\n {body}\n </button>\n ) : (\n <div className={part(\"sc-tile__main\")}>{body}</div>\n )}\n </li>\n );\n}\n"],"mappings":";;;;;;;AAQA,SAAgB,MAAM,EACpB,OACA,WACA,YAMC;CACD,MAAM,OAAO,SAAS;CACtB,OACE,oBAAC,OAAD;EAAK,WAAW,OAAO,KAAK,UAAU,GAAG,SAAS;EAChD,UAAA,oBAAC,MAAD;GAAI,cAAY;GAAO,WAAW,KAAK,gBAAgB;GACpD;EACC,CAAA;CACD,CAAA;AAET;;;;;AAsBA,SAAgB,KAAK,EAAE,MAAM,OAAO,OAAO,KAAK,SAAS,OAAO,aAAwB;CACtF,MAAM,OAAO,SAAS;CACtB,MAAM,OACJ,qBAAA,UAAA,EAAA,UAAA;EACG;EACD,oBAAC,QAAD;GAAM,WAAW,KAAK,gBAAgB;GAAI,UAAA;EAAY,CAAA;EACrD,QAAQ,UAAU,KAAA,KAAa,oBAAC,QAAD;GAAM,WAAW,KAAK,gBAAgB;GAAI,UAAA;EAAY,CAAA;CACtF,EAAA,CAAA;CAEJ,OACE,oBAAC,MAAD;EACE,WAAW,OACT,KAAK,WAAW,GAAI,QAAS,CAAC,gBAAgB,IAAc,CAAC,CAAE,GAC/D,SACF;EAEC,UAAA,UACC,oBAAC,UAAD;GAAQ,MAAK;GAAS,SAAS;GAAS,WAAW,KAAK,eAAe;GACpE,UAAA;EACK,CAAA,IAER,oBAAC,OAAD;GAAK,WAAW,KAAK,eAAe;GAAI,UAAA;EAAU,CAAA;CAElD,CAAA;AAER"}
|
package/dist/kit/messages.d.ts
CHANGED
|
@@ -93,6 +93,19 @@ export interface KitMessages {
|
|
|
93
93
|
position: string;
|
|
94
94
|
/** An automation and its runs: when it runs, how it stands, what each run did. */
|
|
95
95
|
automation: AutomationMessages;
|
|
96
|
+
/** An app for the person to connect, as its connection stands. */
|
|
97
|
+
connect: ConnectMessages;
|
|
98
|
+
}
|
|
99
|
+
/** The words of an app's card: its button, and what it says where the button isn't. */
|
|
100
|
+
export interface ConnectMessages {
|
|
101
|
+
/** The button that opens the app's consent page, given the app's name. */
|
|
102
|
+
authorize: (app: string) => string;
|
|
103
|
+
/** It works. */
|
|
104
|
+
connected: string;
|
|
105
|
+
/** It failed, or the account was lost: the agent asks for it again. */
|
|
106
|
+
failed: string;
|
|
107
|
+
/** Before the platform has the consent page. */
|
|
108
|
+
preparing: string;
|
|
96
109
|
}
|
|
97
110
|
export declare const KIT_MESSAGES: KitMessages;
|
|
98
111
|
//#endregion
|
package/dist/kit/messages.js
CHANGED
|
@@ -58,7 +58,13 @@ const KIT_MESSAGES = {
|
|
|
58
58
|
play: "Play",
|
|
59
59
|
pause: "Pause",
|
|
60
60
|
position: "Position",
|
|
61
|
-
automation: AUTOMATION_MESSAGES
|
|
61
|
+
automation: AUTOMATION_MESSAGES,
|
|
62
|
+
connect: {
|
|
63
|
+
authorize: (app) => `Authorize in ${app}`,
|
|
64
|
+
connected: "Connected",
|
|
65
|
+
failed: "It couldn't be connected",
|
|
66
|
+
preparing: "Getting it ready"
|
|
67
|
+
}
|
|
62
68
|
};
|
|
63
69
|
//#endregion
|
|
64
70
|
export { KIT_MESSAGES };
|
package/dist/kit/messages.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messages.js","names":[],"sources":["../../src/kit/messages.ts"],"sourcesContent":["import type { DayPickerLocale } from \"react-day-picker\";\n\nimport { AUTOMATION_MESSAGES, type AutomationMessages } from \"../automation/messages\";\n\n/**\n * The language the kit's dates are read in: a locale from `@signalcore/ui/locale` (the\n * calendar's, with its words), whose `code` also formats days, times, numbers and lists.\n */\nexport type KitLocale = DayPickerLocale;\n\n/** The words the kit's controls say; a host gives its own, in its language. */\nexport interface KitMessages {\n /** A field left empty that must be filled. */\n required: string;\n /** A day, a time, both, an option, a rating still to pick; also an empty picker's invitation. */\n chooseDate: string;\n chooseTime: string;\n chooseMoment: string;\n chooseOption: string;\n chooseRating: string;\n /** A box that must be ticked to go on. */\n check: string;\n invalidEmail: string;\n invalidPhone: string;\n invalidUrl: string;\n invalidNumber: string;\n /** A time typed in a shape that is not one. */\n invalidTime: string;\n /** A number under its least, or over its most, given formatted. */\n atLeast: (min: string) => string;\n atMost: (max: string) => string;\n /** A day or a time that is not offered. */\n unavailableDate: string;\n unavailableTime: string;\n /** Beside the label of a field that may stay empty. */\n optional: string;\n /** In a long list's box while nothing is picked. */\n pick: string;\n /** In that box while the person types. */\n search: string;\n noResults: string;\n /** The button that shows or hides a secret as it is typed. */\n show: string;\n hide: string;\n /** A number's steppers, for screen readers. */\n increase: string;\n decrease: string;\n /** A picked tag's remove button, for screen readers. */\n remove: (label: string) => string;\n /** A box ticked or not, as an answer reads. */\n yes: string;\n no: string;\n /** A rating, as an answer reads. */\n rated: (value: number, max: number) => string;\n /** One star of a rating, for screen readers. */\n stars: (count: number) => string;\n /** A choice's last row, to answer in one's own words, and its box while empty. */\n other: string;\n otherPlaceholder: string;\n /** A form's submit when the agent gave it no words. */\n send: string;\n /** Where a form's submit was, once it is sent. */\n sent: string;\n /** A file field left empty that must be filled. */\n chooseFile: string;\n /** A file field's drop zone: what it asks, and how. */\n upload: (multiple: boolean) => string;\n drop: string;\n /**\n * Under them, what it takes: the kinds (named and joined, \"\" when anything) and the most a\n * file may weigh, formatted.\n */\n limits: (kinds: string, size: string) => string;\n /** A family of kinds as the limits name it: \"image\" for any image, \"video\", \"audio\", \"text\". */\n anyOf: (family: string) => string;\n /** A file on its way, beside how much has gone. */\n uploading: string;\n /** A file that was not sent: heavier than the most (formatted), a kind not taken, one too many. */\n tooBig: (size: string) => string;\n wrongType: string;\n tooMany: (max: number) => string;\n /** A file whose upload failed, and the button that tries it again. */\n uploadFailed: string;\n retry: string;\n /** A form sent while a file is still on its way, or one failed. */\n stillUploading: string;\n fixFiles: string;\n /** A file's chip, opened on a click, for screen readers. */\n open: (name: string) => string;\n /** A file whose name is not known yet. */\n file: string;\n /** An audio player's button, and its way through, for screen readers. */\n play: string;\n pause: string;\n position: string;\n /** An automation and its runs: when it runs, how it stands, what each run did. */\n automation: AutomationMessages;\n}\n\nconst FAMILIES: Readonly<Record<string, string>> = {\n image: \"images\",\n video: \"videos\",\n audio: \"audio\",\n text: \"text\",\n};\n\nexport const KIT_MESSAGES: KitMessages = {\n required: \"This field is required\",\n chooseDate: \"Choose a day\",\n chooseTime: \"Choose a time\",\n chooseMoment: \"Choose a day and a time\",\n chooseOption: \"Choose an option\",\n other: \"Another answer\",\n otherPlaceholder: \"Write your answer\",\n chooseRating: \"Choose a rating\",\n check: \"Tick the box to go on\",\n invalidEmail: \"Enter a valid email\",\n invalidPhone: \"Enter a valid phone number\",\n invalidUrl: \"Enter a valid address\",\n invalidNumber: \"Enter a number\",\n invalidTime: \"Write a time like 9:30\",\n atLeast: (min) => `At least ${min}`,\n atMost: (max) => `At most ${max}`,\n unavailableDate: \"That day is not available\",\n unavailableTime: \"That time is not available\",\n optional: \"Optional\",\n pick: \"Choose…\",\n search: \"Search…\",\n noResults: \"No results\",\n show: \"Show\",\n hide: \"Hide\",\n increase: \"Increase\",\n decrease: \"Decrease\",\n remove: (label) => `Remove ${label}`,\n yes: \"Yes\",\n no: \"No\",\n rated: (value, max) => `${value} of ${max}`,\n stars: (count) => (count === 1 ? \"1 star\" : `${count} stars`),\n send: \"Send\",\n sent: \"Sent\",\n chooseFile: \"Add a file\",\n upload: (multiple) => (multiple ? \"Upload files\" : \"Upload a file\"),\n drop: \"Drop it here or choose\",\n limits: (kinds, size) => (kinds ? `${kinds}, up to ${size}` : `Up to ${size}`),\n anyOf: (family) => FAMILIES[family] ?? family,\n uploading: \"Uploading\",\n tooBig: (size) => `Over ${size}`,\n wrongType: \"This kind of file isn't taken here\",\n tooMany: (max) => `At most ${max} files`,\n uploadFailed: \"Couldn't upload it\",\n retry: \"Retry\",\n stillUploading: \"Wait for the upload to finish\",\n fixFiles: \"Remove or retry the files that failed\",\n open: (name) => `Open ${name}`,\n file: \"File\",\n play: \"Play\",\n pause: \"Pause\",\n position: \"Position\",\n automation: AUTOMATION_MESSAGES,\n};\n"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"messages.js","names":[],"sources":["../../src/kit/messages.ts"],"sourcesContent":["import type { DayPickerLocale } from \"react-day-picker\";\n\nimport { AUTOMATION_MESSAGES, type AutomationMessages } from \"../automation/messages\";\n\n/**\n * The language the kit's dates are read in: a locale from `@signalcore/ui/locale` (the\n * calendar's, with its words), whose `code` also formats days, times, numbers and lists.\n */\nexport type KitLocale = DayPickerLocale;\n\n/** The words the kit's controls say; a host gives its own, in its language. */\nexport interface KitMessages {\n /** A field left empty that must be filled. */\n required: string;\n /** A day, a time, both, an option, a rating still to pick; also an empty picker's invitation. */\n chooseDate: string;\n chooseTime: string;\n chooseMoment: string;\n chooseOption: string;\n chooseRating: string;\n /** A box that must be ticked to go on. */\n check: string;\n invalidEmail: string;\n invalidPhone: string;\n invalidUrl: string;\n invalidNumber: string;\n /** A time typed in a shape that is not one. */\n invalidTime: string;\n /** A number under its least, or over its most, given formatted. */\n atLeast: (min: string) => string;\n atMost: (max: string) => string;\n /** A day or a time that is not offered. */\n unavailableDate: string;\n unavailableTime: string;\n /** Beside the label of a field that may stay empty. */\n optional: string;\n /** In a long list's box while nothing is picked. */\n pick: string;\n /** In that box while the person types. */\n search: string;\n noResults: string;\n /** The button that shows or hides a secret as it is typed. */\n show: string;\n hide: string;\n /** A number's steppers, for screen readers. */\n increase: string;\n decrease: string;\n /** A picked tag's remove button, for screen readers. */\n remove: (label: string) => string;\n /** A box ticked or not, as an answer reads. */\n yes: string;\n no: string;\n /** A rating, as an answer reads. */\n rated: (value: number, max: number) => string;\n /** One star of a rating, for screen readers. */\n stars: (count: number) => string;\n /** A choice's last row, to answer in one's own words, and its box while empty. */\n other: string;\n otherPlaceholder: string;\n /** A form's submit when the agent gave it no words. */\n send: string;\n /** Where a form's submit was, once it is sent. */\n sent: string;\n /** A file field left empty that must be filled. */\n chooseFile: string;\n /** A file field's drop zone: what it asks, and how. */\n upload: (multiple: boolean) => string;\n drop: string;\n /**\n * Under them, what it takes: the kinds (named and joined, \"\" when anything) and the most a\n * file may weigh, formatted.\n */\n limits: (kinds: string, size: string) => string;\n /** A family of kinds as the limits name it: \"image\" for any image, \"video\", \"audio\", \"text\". */\n anyOf: (family: string) => string;\n /** A file on its way, beside how much has gone. */\n uploading: string;\n /** A file that was not sent: heavier than the most (formatted), a kind not taken, one too many. */\n tooBig: (size: string) => string;\n wrongType: string;\n tooMany: (max: number) => string;\n /** A file whose upload failed, and the button that tries it again. */\n uploadFailed: string;\n retry: string;\n /** A form sent while a file is still on its way, or one failed. */\n stillUploading: string;\n fixFiles: string;\n /** A file's chip, opened on a click, for screen readers. */\n open: (name: string) => string;\n /** A file whose name is not known yet. */\n file: string;\n /** An audio player's button, and its way through, for screen readers. */\n play: string;\n pause: string;\n position: string;\n /** An automation and its runs: when it runs, how it stands, what each run did. */\n automation: AutomationMessages;\n /** An app for the person to connect, as its connection stands. */\n connect: ConnectMessages;\n}\n\n/** The words of an app's card: its button, and what it says where the button isn't. */\nexport interface ConnectMessages {\n /** The button that opens the app's consent page, given the app's name. */\n authorize: (app: string) => string;\n /** It works. */\n connected: string;\n /** It failed, or the account was lost: the agent asks for it again. */\n failed: string;\n /** Before the platform has the consent page. */\n preparing: string;\n}\n\nconst FAMILIES: Readonly<Record<string, string>> = {\n image: \"images\",\n video: \"videos\",\n audio: \"audio\",\n text: \"text\",\n};\n\nexport const KIT_MESSAGES: KitMessages = {\n required: \"This field is required\",\n chooseDate: \"Choose a day\",\n chooseTime: \"Choose a time\",\n chooseMoment: \"Choose a day and a time\",\n chooseOption: \"Choose an option\",\n other: \"Another answer\",\n otherPlaceholder: \"Write your answer\",\n chooseRating: \"Choose a rating\",\n check: \"Tick the box to go on\",\n invalidEmail: \"Enter a valid email\",\n invalidPhone: \"Enter a valid phone number\",\n invalidUrl: \"Enter a valid address\",\n invalidNumber: \"Enter a number\",\n invalidTime: \"Write a time like 9:30\",\n atLeast: (min) => `At least ${min}`,\n atMost: (max) => `At most ${max}`,\n unavailableDate: \"That day is not available\",\n unavailableTime: \"That time is not available\",\n optional: \"Optional\",\n pick: \"Choose…\",\n search: \"Search…\",\n noResults: \"No results\",\n show: \"Show\",\n hide: \"Hide\",\n increase: \"Increase\",\n decrease: \"Decrease\",\n remove: (label) => `Remove ${label}`,\n yes: \"Yes\",\n no: \"No\",\n rated: (value, max) => `${value} of ${max}`,\n stars: (count) => (count === 1 ? \"1 star\" : `${count} stars`),\n send: \"Send\",\n sent: \"Sent\",\n chooseFile: \"Add a file\",\n upload: (multiple) => (multiple ? \"Upload files\" : \"Upload a file\"),\n drop: \"Drop it here or choose\",\n limits: (kinds, size) => (kinds ? `${kinds}, up to ${size}` : `Up to ${size}`),\n anyOf: (family) => FAMILIES[family] ?? family,\n uploading: \"Uploading\",\n tooBig: (size) => `Over ${size}`,\n wrongType: \"This kind of file isn't taken here\",\n tooMany: (max) => `At most ${max} files`,\n uploadFailed: \"Couldn't upload it\",\n retry: \"Retry\",\n stillUploading: \"Wait for the upload to finish\",\n fixFiles: \"Remove or retry the files that failed\",\n open: (name) => `Open ${name}`,\n file: \"File\",\n play: \"Play\",\n pause: \"Pause\",\n position: \"Position\",\n automation: AUTOMATION_MESSAGES,\n connect: {\n authorize: (app) => `Authorize in ${app}`,\n connected: \"Connected\",\n failed: \"It couldn't be connected\",\n preparing: \"Getting it ready\",\n },\n};\n"],"mappings":";;AAiHA,MAAM,WAA6C;CACjD,OAAO;CACP,OAAO;CACP,OAAO;CACP,MAAM;AACR;AAEA,MAAa,eAA4B;CACvC,UAAU;CACV,YAAY;CACZ,YAAY;CACZ,cAAc;CACd,cAAc;CACd,OAAO;CACP,kBAAkB;CAClB,cAAc;CACd,OAAO;CACP,cAAc;CACd,cAAc;CACd,YAAY;CACZ,eAAe;CACf,aAAa;CACb,UAAU,QAAQ,YAAY;CAC9B,SAAS,QAAQ,WAAW;CAC5B,iBAAiB;CACjB,iBAAiB;CACjB,UAAU;CACV,MAAM;CACN,QAAQ;CACR,WAAW;CACX,MAAM;CACN,MAAM;CACN,UAAU;CACV,UAAU;CACV,SAAS,UAAU,UAAU;CAC7B,KAAK;CACL,IAAI;CACJ,QAAQ,OAAO,QAAQ,GAAG,MAAM,MAAM;CACtC,QAAQ,UAAW,UAAU,IAAI,WAAW,GAAG,MAAM;CACrD,MAAM;CACN,MAAM;CACN,YAAY;CACZ,SAAS,aAAc,WAAW,iBAAiB;CACnD,MAAM;CACN,SAAS,OAAO,SAAU,QAAQ,GAAG,MAAM,UAAU,SAAS,SAAS;CACvE,QAAQ,WAAW,SAAS,WAAW;CACvC,WAAW;CACX,SAAS,SAAS,QAAQ;CAC1B,WAAW;CACX,UAAU,QAAQ,WAAW,IAAI;CACjC,cAAc;CACd,OAAO;CACP,gBAAgB;CAChB,UAAU;CACV,OAAO,SAAS,QAAQ;CACxB,MAAM;CACN,MAAM;CACN,OAAO;CACP,UAAU;CACV,YAAY;CACZ,SAAS;EACP,YAAY,QAAQ,gBAAgB;EACpC,WAAW;EACX,QAAQ;EACR,WAAW;CACb;AACF"}
|
package/dist/kit/uploads.d.ts
CHANGED
|
@@ -24,7 +24,7 @@ export interface FileHost {
|
|
|
24
24
|
play?: ((file: Attachment) => Promise<string>) | undefined;
|
|
25
25
|
}
|
|
26
26
|
/** A file as picked: its name, its type as the browser gave it ("" when none), its weight. */
|
|
27
|
-
|
|
27
|
+
interface Picked {
|
|
28
28
|
name: string;
|
|
29
29
|
type: string;
|
|
30
30
|
size: number;
|
|
@@ -39,4 +39,5 @@ export type FileFamily = "image" | "pdf" | "sheet" | "doc" | "slides" | "text" |
|
|
|
39
39
|
/** A file's family, by its name's extension first, then by its type. */
|
|
40
40
|
export declare function familyOf(name: string, type: string): FileFamily;
|
|
41
41
|
//#endregion
|
|
42
|
+
export {};
|
|
42
43
|
//# sourceMappingURL=uploads.d.ts.map
|
package/dist/kit/uploads.js
CHANGED
|
@@ -160,6 +160,6 @@ function familyOf(name, type) {
|
|
|
160
160
|
return family === "image" || family === "audio" || family === "video" || family === "text" ? family : "other";
|
|
161
161
|
}
|
|
162
162
|
//#endregion
|
|
163
|
-
export { MAX_MB, accepts,
|
|
163
|
+
export { MAX_MB, accepts, failureOf, familyOf, isAbort, kindsOf, limitOf, limitsOf, refusalOf, typeOf };
|
|
164
164
|
|
|
165
165
|
//# sourceMappingURL=uploads.js.map
|
package/dist/kit/uploads.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uploads.js","names":[],"sources":["../../src/kit/uploads.ts"],"sourcesContent":["/**\n * Files a person picks, as pure functions: whether a field takes one (its kind, its weight), how\n * a field's kinds and limits read, what family a file is of (for its glyph), and the words for\n * what went wrong. Nothing here touches the page or the network.\n */\nimport type { Attachment } from \"../types\";\nimport type { KitMessages } from \"./messages\";\nimport { formatSize } from \"./numbers\";\n\n/** The most a file may weigh anywhere, in MB: the API's ceiling. */\nexport const MAX_MB = 50;\nconst MB = 1024 * 1024;\n\n/**\n * Sends one file where it belongs, telling how much of it has gone (0 to 1) as it goes; aborting\n * `signal` lets it go. Resolves to the file as stored.\n */\nexport type Uploader = (\n file: File,\n progress: (share: number) => void,\n signal: AbortSignal,\n) => Promise<Attachment>;\n\n/** Where a field's files go and how the ones already sent are found: the conversation's. */\nexport interface FileHost {\n upload: Uploader;\n /** The files known by id: said in the conversation, uploaded to it. */\n known: ReadonlyMap<string, Attachment>;\n /** Asks for files it does not know yet, by id: read back from the conversation. */\n find?: ((ids: readonly string[]) => void) | undefined;\n /** Opens one, when the host shows files. */\n open?: ((file: Attachment) => void) | undefined;\n /**\n * Where a picture, a video or a sound plays from in the page's own player: an address the\n * player streams with no credential, for a while.\n */\n play?: ((file: Attachment) => Promise<string>) | undefined;\n}\n\n/** A file as picked: its name, its type as the browser gave it (\"\" when none), its weight. */\nexport interface Picked {\n name: string;\n type: string;\n size: number;\n}\n\n/** A name's extension, lowercase, without its dot; \"\" for one without (`README`, `.env`). */\nexport function extensionOf(name: string): string {\n const dot = name.lastIndexOf(\".\");\n return dot <= 0 ? \"\" : name.slice(dot + 1).toLowerCase();\n}\n\n/** The types a browser often leaves out, by extension. */\nconst TYPES: Readonly<Record<string, string>> = {\n pdf: \"application/pdf\",\n png: \"image/png\",\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n gif: \"image/gif\",\n webp: \"image/webp\",\n heic: \"image/heic\",\n heif: \"image/heif\",\n csv: \"text/csv\",\n txt: \"text/plain\",\n md: \"text/markdown\",\n json: \"application/json\",\n doc: \"application/msword\",\n docx: \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n xls: \"application/vnd.ms-excel\",\n xlsx: \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n ppt: \"application/vnd.ms-powerpoint\",\n pptx: \"application/vnd.openxmlformats-officedocument.presentationml.presentation\",\n zip: \"application/zip\",\n};\n\n/** A file's type: the browser's, else the one its extension names, else \"\". */\nexport function typeOf(file: Pick<Picked, \"name\" | \"type\">): string {\n return (file.type || TYPES[extensionOf(file.name)] || \"\").toLowerCase();\n}\n\n/**\n * Whether a file is of a kind `accept` lists, read as an <input accept> reads it: a type\n * (\"application/pdf\"), a family (\"image/*\") or an extension (\".xlsx\"); none takes anything.\n */\nexport function accepts(\n accept: readonly string[] | undefined,\n file: Pick<Picked, \"name\" | \"type\">,\n): boolean {\n if (accept === undefined || accept.length === 0) return true;\n const type = typeOf(file);\n const extension = extensionOf(file.name);\n return accept.some((raw) => {\n const rule = raw.trim().toLowerCase();\n if (rule === \"*\" || rule === \"*/*\") return true;\n if (rule.startsWith(\".\")) return extension !== \"\" && rule === `.${extension}`;\n if (rule.endsWith(\"/*\")) return type.startsWith(rule.slice(0, -1));\n return type === rule;\n });\n}\n\n/** The most a field takes per file, in MB: its own, within the ceiling. */\nexport function limitOf(maxMb: number | undefined): number {\n return maxMb !== undefined && maxMb > 0 ? Math.min(maxMb, MAX_MB) : MAX_MB;\n}\n\n/** That most, as people read it: \"10 MB\". */\nfunction limitWords(maxMb: number | undefined, code: string): string {\n return formatSize(limitOf(maxMb) * MB, code);\n}\n\n/** What keeps a file from being sent, in words: a kind not taken, or past the most; else null. */\nexport function refusalOf(\n file: Picked,\n accept: readonly string[] | undefined,\n maxMb: number | undefined,\n code: string,\n messages: KitMessages,\n): string | null {\n if (!accepts(accept, file)) return messages.wrongType;\n if (file.size > limitOf(maxMb) * MB) return messages.tooBig(limitWords(maxMb, code));\n return null;\n}\n\n/**\n * What a failed upload says: past what the server takes (413), a kind it refused (415), or that\n * it failed, to be tried again.\n */\nexport function failureOf(\n error: unknown,\n maxMb: number | undefined,\n code: string,\n messages: KitMessages,\n): string {\n const status =\n typeof error === \"object\" && error !== null && \"status\" in error ? error.status : undefined;\n if (status === 413) return messages.tooBig(limitWords(maxMb, code));\n if (status === 415) return messages.wrongType;\n return messages.uploadFailed;\n}\n\n/** Whether an upload's failure is the person letting it go: nothing to show. */\nexport function isAbort(error: unknown): boolean {\n return error instanceof DOMException && error.name === \"AbortError\";\n}\n\n/** Short names for the types people know by one. */\nconst SHORT: Readonly<Record<string, string>> = Object.fromEntries(\n Object.entries(TYPES).map(([extension, type]) => [type, extension.toUpperCase()]),\n);\n\n/**\n * The kinds a field takes as people name them, each once: a family (\"image/*\") by `anyOf`, a type\n * by its short name (\"PDF\", \"XLSX\"), an extension by its letters; none when it takes anything.\n */\nexport function kindsOf(accept: readonly string[] | undefined, messages: KitMessages): string[] {\n const rules = (accept ?? []).map((rule) => rule.trim().toLowerCase());\n if (rules.some((rule) => rule === \"*\" || rule === \"*/*\")) return [];\n const names = rules.map((rule) => {\n if (rule.startsWith(\".\")) return rule.slice(1).toUpperCase();\n if (rule.endsWith(\"/*\")) return messages.anyOf(rule.slice(0, -2));\n return SHORT[rule] ?? (rule.split(\"/\")[1] ?? rule).toUpperCase();\n });\n return [...new Set(names.filter((name) => name !== \"\"))];\n}\n\n/** The line under a drop zone: the kinds it takes, joined (\"Images or PDF\"), and its most. */\nexport function limitsOf(\n accept: readonly string[] | undefined,\n maxMb: number | undefined,\n code: string,\n messages: KitMessages,\n): string {\n const kinds = kindsOf(accept, messages);\n const joined =\n kinds.length === 0 ? \"\" : new Intl.ListFormat(code, { type: \"disjunction\" }).format(kinds);\n const line = messages.limits(joined, limitWords(maxMb, code));\n return line.charAt(0).toLocaleUpperCase(code) + line.slice(1);\n}\n\n/** The families a file's glyph tells apart. */\nexport type FileFamily =\n \"image\" | \"pdf\" | \"sheet\" | \"doc\" | \"slides\" | \"text\" | \"archive\" | \"audio\" | \"video\" | \"other\";\n\nconst FAMILIES: Readonly<Record<string, FileFamily>> = {\n png: \"image\",\n jpg: \"image\",\n jpeg: \"image\",\n gif: \"image\",\n webp: \"image\",\n avif: \"image\",\n heic: \"image\",\n heif: \"image\",\n svg: \"image\",\n bmp: \"image\",\n pdf: \"pdf\",\n csv: \"sheet\",\n tsv: \"sheet\",\n xls: \"sheet\",\n xlsx: \"sheet\",\n ods: \"sheet\",\n numbers: \"sheet\",\n doc: \"doc\",\n docx: \"doc\",\n odt: \"doc\",\n rtf: \"doc\",\n pages: \"doc\",\n ppt: \"slides\",\n pptx: \"slides\",\n odp: \"slides\",\n key: \"slides\",\n txt: \"text\",\n md: \"text\",\n markdown: \"text\",\n json: \"text\",\n yaml: \"text\",\n yml: \"text\",\n xml: \"text\",\n html: \"text\",\n log: \"text\",\n zip: \"archive\",\n rar: \"archive\",\n \"7z\": \"archive\",\n tar: \"archive\",\n gz: \"archive\",\n mp3: \"audio\",\n wav: \"audio\",\n m4a: \"audio\",\n ogg: \"audio\",\n mp4: \"video\",\n mov: \"video\",\n webm: \"video\",\n};\n\n/** A file's family, by its name's extension first, then by its type. */\nexport function familyOf(name: string, type: string): FileFamily {\n const known = FAMILIES[extensionOf(name)];\n if (known) return known;\n const media = type.toLowerCase();\n if (media === \"application/pdf\") return \"pdf\";\n const family = media.split(\"/\")[0];\n return family === \"image\" || family === \"audio\" || family === \"video\" || family === \"text\"\n ? family\n : \"other\";\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"uploads.js","names":[],"sources":["../../src/kit/uploads.ts"],"sourcesContent":["/**\n * Files a person picks, as pure functions: whether a field takes one (its kind, its weight), how\n * a field's kinds and limits read, what family a file is of (for its glyph), and the words for\n * what went wrong. Nothing here touches the page or the network.\n */\nimport type { Attachment } from \"../types\";\nimport type { KitMessages } from \"./messages\";\nimport { formatSize } from \"./numbers\";\n\n/** The most a file may weigh anywhere, in MB: the API's ceiling. */\nexport const MAX_MB = 50;\nconst MB = 1024 * 1024;\n\n/**\n * Sends one file where it belongs, telling how much of it has gone (0 to 1) as it goes; aborting\n * `signal` lets it go. Resolves to the file as stored.\n */\nexport type Uploader = (\n file: File,\n progress: (share: number) => void,\n signal: AbortSignal,\n) => Promise<Attachment>;\n\n/** Where a field's files go and how the ones already sent are found: the conversation's. */\nexport interface FileHost {\n upload: Uploader;\n /** The files known by id: said in the conversation, uploaded to it. */\n known: ReadonlyMap<string, Attachment>;\n /** Asks for files it does not know yet, by id: read back from the conversation. */\n find?: ((ids: readonly string[]) => void) | undefined;\n /** Opens one, when the host shows files. */\n open?: ((file: Attachment) => void) | undefined;\n /**\n * Where a picture, a video or a sound plays from in the page's own player: an address the\n * player streams with no credential, for a while.\n */\n play?: ((file: Attachment) => Promise<string>) | undefined;\n}\n\n/** A file as picked: its name, its type as the browser gave it (\"\" when none), its weight. */\ninterface Picked {\n name: string;\n type: string;\n size: number;\n}\n\n/** A name's extension, lowercase, without its dot; \"\" for one without (`README`, `.env`). */\nfunction extensionOf(name: string): string {\n const dot = name.lastIndexOf(\".\");\n return dot <= 0 ? \"\" : name.slice(dot + 1).toLowerCase();\n}\n\n/** The types a browser often leaves out, by extension. */\nconst TYPES: Readonly<Record<string, string>> = {\n pdf: \"application/pdf\",\n png: \"image/png\",\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n gif: \"image/gif\",\n webp: \"image/webp\",\n heic: \"image/heic\",\n heif: \"image/heif\",\n csv: \"text/csv\",\n txt: \"text/plain\",\n md: \"text/markdown\",\n json: \"application/json\",\n doc: \"application/msword\",\n docx: \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n xls: \"application/vnd.ms-excel\",\n xlsx: \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n ppt: \"application/vnd.ms-powerpoint\",\n pptx: \"application/vnd.openxmlformats-officedocument.presentationml.presentation\",\n zip: \"application/zip\",\n};\n\n/** A file's type: the browser's, else the one its extension names, else \"\". */\nexport function typeOf(file: Pick<Picked, \"name\" | \"type\">): string {\n return (file.type || TYPES[extensionOf(file.name)] || \"\").toLowerCase();\n}\n\n/**\n * Whether a file is of a kind `accept` lists, read as an <input accept> reads it: a type\n * (\"application/pdf\"), a family (\"image/*\") or an extension (\".xlsx\"); none takes anything.\n */\nexport function accepts(\n accept: readonly string[] | undefined,\n file: Pick<Picked, \"name\" | \"type\">,\n): boolean {\n if (accept === undefined || accept.length === 0) return true;\n const type = typeOf(file);\n const extension = extensionOf(file.name);\n return accept.some((raw) => {\n const rule = raw.trim().toLowerCase();\n if (rule === \"*\" || rule === \"*/*\") return true;\n if (rule.startsWith(\".\")) return extension !== \"\" && rule === `.${extension}`;\n if (rule.endsWith(\"/*\")) return type.startsWith(rule.slice(0, -1));\n return type === rule;\n });\n}\n\n/** The most a field takes per file, in MB: its own, within the ceiling. */\nexport function limitOf(maxMb: number | undefined): number {\n return maxMb !== undefined && maxMb > 0 ? Math.min(maxMb, MAX_MB) : MAX_MB;\n}\n\n/** That most, as people read it: \"10 MB\". */\nfunction limitWords(maxMb: number | undefined, code: string): string {\n return formatSize(limitOf(maxMb) * MB, code);\n}\n\n/** What keeps a file from being sent, in words: a kind not taken, or past the most; else null. */\nexport function refusalOf(\n file: Picked,\n accept: readonly string[] | undefined,\n maxMb: number | undefined,\n code: string,\n messages: KitMessages,\n): string | null {\n if (!accepts(accept, file)) return messages.wrongType;\n if (file.size > limitOf(maxMb) * MB) return messages.tooBig(limitWords(maxMb, code));\n return null;\n}\n\n/**\n * What a failed upload says: past what the server takes (413), a kind it refused (415), or that\n * it failed, to be tried again.\n */\nexport function failureOf(\n error: unknown,\n maxMb: number | undefined,\n code: string,\n messages: KitMessages,\n): string {\n const status =\n typeof error === \"object\" && error !== null && \"status\" in error ? error.status : undefined;\n if (status === 413) return messages.tooBig(limitWords(maxMb, code));\n if (status === 415) return messages.wrongType;\n return messages.uploadFailed;\n}\n\n/** Whether an upload's failure is the person letting it go: nothing to show. */\nexport function isAbort(error: unknown): boolean {\n return error instanceof DOMException && error.name === \"AbortError\";\n}\n\n/** Short names for the types people know by one. */\nconst SHORT: Readonly<Record<string, string>> = Object.fromEntries(\n Object.entries(TYPES).map(([extension, type]) => [type, extension.toUpperCase()]),\n);\n\n/**\n * The kinds a field takes as people name them, each once: a family (\"image/*\") by `anyOf`, a type\n * by its short name (\"PDF\", \"XLSX\"), an extension by its letters; none when it takes anything.\n */\nexport function kindsOf(accept: readonly string[] | undefined, messages: KitMessages): string[] {\n const rules = (accept ?? []).map((rule) => rule.trim().toLowerCase());\n if (rules.some((rule) => rule === \"*\" || rule === \"*/*\")) return [];\n const names = rules.map((rule) => {\n if (rule.startsWith(\".\")) return rule.slice(1).toUpperCase();\n if (rule.endsWith(\"/*\")) return messages.anyOf(rule.slice(0, -2));\n return SHORT[rule] ?? (rule.split(\"/\")[1] ?? rule).toUpperCase();\n });\n return [...new Set(names.filter((name) => name !== \"\"))];\n}\n\n/** The line under a drop zone: the kinds it takes, joined (\"Images or PDF\"), and its most. */\nexport function limitsOf(\n accept: readonly string[] | undefined,\n maxMb: number | undefined,\n code: string,\n messages: KitMessages,\n): string {\n const kinds = kindsOf(accept, messages);\n const joined =\n kinds.length === 0 ? \"\" : new Intl.ListFormat(code, { type: \"disjunction\" }).format(kinds);\n const line = messages.limits(joined, limitWords(maxMb, code));\n return line.charAt(0).toLocaleUpperCase(code) + line.slice(1);\n}\n\n/** The families a file's glyph tells apart. */\nexport type FileFamily =\n \"image\" | \"pdf\" | \"sheet\" | \"doc\" | \"slides\" | \"text\" | \"archive\" | \"audio\" | \"video\" | \"other\";\n\nconst FAMILIES: Readonly<Record<string, FileFamily>> = {\n png: \"image\",\n jpg: \"image\",\n jpeg: \"image\",\n gif: \"image\",\n webp: \"image\",\n avif: \"image\",\n heic: \"image\",\n heif: \"image\",\n svg: \"image\",\n bmp: \"image\",\n pdf: \"pdf\",\n csv: \"sheet\",\n tsv: \"sheet\",\n xls: \"sheet\",\n xlsx: \"sheet\",\n ods: \"sheet\",\n numbers: \"sheet\",\n doc: \"doc\",\n docx: \"doc\",\n odt: \"doc\",\n rtf: \"doc\",\n pages: \"doc\",\n ppt: \"slides\",\n pptx: \"slides\",\n odp: \"slides\",\n key: \"slides\",\n txt: \"text\",\n md: \"text\",\n markdown: \"text\",\n json: \"text\",\n yaml: \"text\",\n yml: \"text\",\n xml: \"text\",\n html: \"text\",\n log: \"text\",\n zip: \"archive\",\n rar: \"archive\",\n \"7z\": \"archive\",\n tar: \"archive\",\n gz: \"archive\",\n mp3: \"audio\",\n wav: \"audio\",\n m4a: \"audio\",\n ogg: \"audio\",\n mp4: \"video\",\n mov: \"video\",\n webm: \"video\",\n};\n\n/** A file's family, by its name's extension first, then by its type. */\nexport function familyOf(name: string, type: string): FileFamily {\n const known = FAMILIES[extensionOf(name)];\n if (known) return known;\n const media = type.toLowerCase();\n if (media === \"application/pdf\") return \"pdf\";\n const family = media.split(\"/\")[0];\n return family === \"image\" || family === \"audio\" || family === \"video\" || family === \"text\"\n ? family\n : \"other\";\n}\n"],"mappings":"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"}
|
package/dist/kit/waveform.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"waveform.js","names":[],"sources":["../../src/kit/waveform.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\n\n/** The most a file may weigh to be drawn as its waveform: past it, it draws even. */\nconst MOST_BYTES = 8 * 1024 * 1024;\n\n/** An audio as drawn: its bars' heights, and how long it is (what an Ogg doesn't say upfront). */\
|
|
1
|
+
{"version":3,"file":"waveform.js","names":[],"sources":["../../src/kit/waveform.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\n\n/** The most a file may weigh to be drawn as its waveform: past it, it draws even. */\nconst MOST_BYTES = 8 * 1024 * 1024;\n\n/** An audio as drawn: its bars' heights, and how long it is (what an Ogg doesn't say upfront). */\ninterface Waveform {\n heights: number[];\n seconds: number;\n}\n\nconst drawn = new Map<string, Promise<Waveform | null>>();\n\n/**\n * An audio's waveform as `bars` heights between 0 and 1 (each stretch's energy, the strongest at\n * 1), and its length. Read once per address and kept; null while it is read, and for audio the\n * browser can't read (or too heavy to), which draws even.\n */\nexport function useWaveform(src: string | undefined, bars: number): Waveform | null {\n const [heights, setHeights] = useState<Waveform | null>(null);\n useEffect(() => {\n if (src === undefined) return;\n let live = true;\n const key = `${String(bars)} ${src}`;\n let reading = drawn.get(key);\n if (reading === undefined) {\n reading = read(src, bars);\n drawn.set(key, reading);\n }\n void reading.then((read) => {\n if (live) setHeights(read);\n });\n return () => {\n live = false;\n };\n }, [src, bars]);\n return heights;\n}\n\nasync function read(src: string, bars: number): Promise<Waveform | null> {\n try {\n const response = await fetch(src, { credentials: \"same-origin\" });\n if (!response.ok) return null;\n if (Number(response.headers.get(\"content-length\") ?? 0) > MOST_BYTES) return null;\n const context = new AudioContext();\n try {\n const audio = await context.decodeAudioData(await response.arrayBuffer());\n return { heights: energies(audio, bars), seconds: audio.duration };\n } finally {\n void context.close();\n }\n } catch {\n return null;\n }\n}\n\n/**\n * The energy (root mean square) of each of `bars` even stretches, the strongest channel's, scaled\n * so the strongest stretch is 1: speech reads as its syllables and pauses, where the loudest\n * sample of each would stand nearly even.\n */\nexport function energies(\n audio: Pick<AudioBuffer, \"numberOfChannels\" | \"getChannelData\">,\n bars: number,\n): number[] {\n const channels = Array.from({ length: audio.numberOfChannels }, (_, index) =>\n audio.getChannelData(index),\n );\n const length = channels[0]?.length ?? 0;\n const stretch = Math.max(1, Math.floor(length / bars));\n const found = Array.from({ length: bars }, (_, bar) => {\n const from = bar * stretch;\n const to = Math.min(length, from + stretch);\n let strongest = 0;\n for (const data of channels) {\n let sum = 0;\n for (let at = from; at < to; at += 1) sum += (data[at] ?? 0) ** 2;\n strongest = Math.max(strongest, to > from ? Math.sqrt(sum / (to - from)) : 0);\n }\n return strongest;\n });\n const top = Math.max(...found, 0);\n return top === 0 ? found.map(() => 0) : found.map((value) => value / top);\n}\n"],"mappings":";;;AAGA,MAAM,aAAa;AAQnB,MAAM,wBAAQ,IAAI,IAAsC;;;;;;AAOxD,SAAgB,YAAY,KAAyB,MAA+B;CAClF,MAAM,CAAC,SAAS,cAAc,SAA0B,IAAI;CAC5D,gBAAgB;EACd,IAAI,QAAQ,KAAA,GAAW;EACvB,IAAI,OAAO;EACX,MAAM,MAAM,GAAG,OAAO,IAAI,EAAE,GAAG;EAC/B,IAAI,UAAU,MAAM,IAAI,GAAG;EAC3B,IAAI,YAAY,KAAA,GAAW;GACzB,UAAU,KAAK,KAAK,IAAI;GACxB,MAAM,IAAI,KAAK,OAAO;EACxB;EACA,QAAa,MAAM,SAAS;GAC1B,IAAI,MAAM,WAAW,IAAI;EAC3B,CAAC;EACD,aAAa;GACX,OAAO;EACT;CACF,GAAG,CAAC,KAAK,IAAI,CAAC;CACd,OAAO;AACT;AAEA,eAAe,KAAK,KAAa,MAAwC;CACvE,IAAI;EACF,MAAM,WAAW,MAAM,MAAM,KAAK,EAAE,aAAa,cAAc,CAAC;EAChE,IAAI,CAAC,SAAS,IAAI,OAAO;EACzB,IAAI,OAAO,SAAS,QAAQ,IAAI,gBAAgB,KAAK,CAAC,IAAI,YAAY,OAAO;EAC7E,MAAM,UAAU,IAAI,aAAa;EACjC,IAAI;GACF,MAAM,QAAQ,MAAM,QAAQ,gBAAgB,MAAM,SAAS,YAAY,CAAC;GACxE,OAAO;IAAE,SAAS,SAAS,OAAO,IAAI;IAAG,SAAS,MAAM;GAAS;EACnE,UAAU;GACR,QAAa,MAAM;EACrB;CACF,QAAQ;EACN,OAAO;CACT;AACF;;;;;;AAOA,SAAgB,SACd,OACA,MACU;CACV,MAAM,WAAW,MAAM,KAAK,EAAE,QAAQ,MAAM,iBAAiB,IAAI,GAAG,UAClE,MAAM,eAAe,KAAK,CAC5B;CACA,MAAM,SAAS,SAAS,EAAE,EAAE,UAAU;CACtC,MAAM,UAAU,KAAK,IAAI,GAAG,KAAK,MAAM,SAAS,IAAI,CAAC;CACrD,MAAM,QAAQ,MAAM,KAAK,EAAE,QAAQ,KAAK,IAAI,GAAG,QAAQ;EACrD,MAAM,OAAO,MAAM;EACnB,MAAM,KAAK,KAAK,IAAI,QAAQ,OAAO,OAAO;EAC1C,IAAI,YAAY;EAChB,KAAK,MAAM,QAAQ,UAAU;GAC3B,IAAI,MAAM;GACV,KAAK,IAAI,KAAK,MAAM,KAAK,IAAI,MAAM,GAAG,QAAQ,KAAK,OAAO,MAAM;GAChE,YAAY,KAAK,IAAI,WAAW,KAAK,OAAO,KAAK,KAAK,OAAO,KAAK,KAAK,IAAI,CAAC;EAC9E;EACA,OAAO;CACT,CAAC;CACD,MAAM,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC;CAChC,OAAO,QAAQ,IAAI,MAAM,UAAU,CAAC,IAAI,MAAM,KAAK,UAAU,QAAQ,GAAG;AAC1E"}
|
package/dist/markdown.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markdown.js","names":[],"sources":["../src/markdown.ts"],"sourcesContent":["/**\n * The little Markdown an agent's words use, read into a tree the renderer draws: headings,\n * paragraphs and bullet lists, with bold, italic, code and links inside. No library: this is all\n * a surface's text (and a file of notes) needs, and what it cannot read it shows as written.\n */\n\nexport type Inline =\n | { kind: \"text\" | \"strong\" | \"em\" | \"code\"; text: string }\n | { kind: \"link\"; text: string; href: string };\n\
|
|
1
|
+
{"version":3,"file":"markdown.js","names":[],"sources":["../src/markdown.ts"],"sourcesContent":["/**\n * The little Markdown an agent's words use, read into a tree the renderer draws: headings,\n * paragraphs and bullet lists, with bold, italic, code and links inside. No library: this is all\n * a surface's text (and a file of notes) needs, and what it cannot read it shows as written.\n */\n\nexport type Inline =\n | { kind: \"text\" | \"strong\" | \"em\" | \"code\"; text: string }\n | { kind: \"link\"; text: string; href: string };\n\ntype Node =\n | { kind: \"heading\"; level: number; inline: Inline[] }\n | { kind: \"paragraph\"; inline: Inline[] }\n | { kind: \"list\"; items: Inline[][] };\n\n/** `# Title`, up to six marks, a space, the words (closing marks dropped). */\nconst HEADING = /^ {0,3}(#{1,6})\\s+(.*?)(?:\\s+#+)?\\s*$/;\nconst ITEM = /^\\s*[-*]\\s+(.*)$/;\nconst INLINE = /`([^`]+)`|\\*\\*([^*]+)\\*\\*|\\*([^*]+)\\*|\\[([^\\]]+)\\]\\(([^)\\s]+)\\)/g;\n// The words are the agent's, not the host's: a link may open a page, never run anything.\nconst SAFE_HREF = /^(https?:|mailto:)/i;\n\nexport function parse(markdown: string): Node[] {\n const nodes: Node[] = [];\n let lines: string[] = [];\n const paragraph = () => {\n if (lines.length > 0) nodes.push({ kind: \"paragraph\", inline: inline(lines.join(\"\\n\")) });\n lines = [];\n };\n for (const line of markdown.split(\"\\n\")) {\n const heading = HEADING.exec(line);\n const item = ITEM.exec(line);\n if (heading) {\n paragraph();\n const [, marks = \"#\", text = \"\"] = heading;\n nodes.push({ kind: \"heading\", level: marks.length, inline: inline(text) });\n } else if (item) {\n paragraph();\n const last = nodes.at(-1);\n const text = inline(item[1] ?? \"\");\n if (last?.kind === \"list\") last.items.push(text);\n else nodes.push({ kind: \"list\", items: [text] });\n } else if (line.trim() === \"\") {\n paragraph();\n } else {\n lines.push(line.trim());\n }\n }\n paragraph();\n return nodes;\n}\n\nexport function inline(text: string): Inline[] {\n const out: Inline[] = [];\n let at = 0;\n for (const match of text.matchAll(INLINE)) {\n if (match.index > at) out.push({ kind: \"text\", text: text.slice(at, match.index) });\n const [whole, code, strong, em, label, href] = match;\n if (code !== undefined) out.push({ kind: \"code\", text: code });\n else if (strong !== undefined) out.push({ kind: \"strong\", text: strong });\n else if (em !== undefined) out.push({ kind: \"em\", text: em });\n else if (label !== undefined && href !== undefined && SAFE_HREF.test(href)) {\n out.push({ kind: \"link\", text: label, href });\n } else out.push({ kind: \"text\", text: whole });\n at = match.index + whole.length;\n }\n if (at < text.length) out.push({ kind: \"text\", text: text.slice(at) });\n return out;\n}\n"],"mappings":";;AAgBA,MAAM,UAAU;AAChB,MAAM,OAAO;AACb,MAAM,SAAS;AAEf,MAAM,YAAY;AAElB,SAAgB,MAAM,UAA0B;CAC9C,MAAM,QAAgB,CAAC;CACvB,IAAI,QAAkB,CAAC;CACvB,MAAM,kBAAkB;EACtB,IAAI,MAAM,SAAS,GAAG,MAAM,KAAK;GAAE,MAAM;GAAa,QAAQ,OAAO,MAAM,KAAK,IAAI,CAAC;EAAE,CAAC;EACxF,QAAQ,CAAC;CACX;CACA,KAAK,MAAM,QAAQ,SAAS,MAAM,IAAI,GAAG;EACvC,MAAM,UAAU,QAAQ,KAAK,IAAI;EACjC,MAAM,OAAO,KAAK,KAAK,IAAI;EAC3B,IAAI,SAAS;GACX,UAAU;GACV,MAAM,GAAG,QAAQ,KAAK,OAAO,MAAM;GACnC,MAAM,KAAK;IAAE,MAAM;IAAW,OAAO,MAAM;IAAQ,QAAQ,OAAO,IAAI;GAAE,CAAC;EAC3E,OAAO,IAAI,MAAM;GACf,UAAU;GACV,MAAM,OAAO,MAAM,GAAG,EAAE;GACxB,MAAM,OAAO,OAAO,KAAK,MAAM,EAAE;GACjC,IAAI,MAAM,SAAS,QAAQ,KAAK,MAAM,KAAK,IAAI;QAC1C,MAAM,KAAK;IAAE,MAAM;IAAQ,OAAO,CAAC,IAAI;GAAE,CAAC;EACjD,OAAO,IAAI,KAAK,KAAK,MAAM,IACzB,UAAU;OAEV,MAAM,KAAK,KAAK,KAAK,CAAC;CAE1B;CACA,UAAU;CACV,OAAO;AACT;AAEA,SAAgB,OAAO,MAAwB;CAC7C,MAAM,MAAgB,CAAC;CACvB,IAAI,KAAK;CACT,KAAK,MAAM,SAAS,KAAK,SAAS,MAAM,GAAG;EACzC,IAAI,MAAM,QAAQ,IAAI,IAAI,KAAK;GAAE,MAAM;GAAQ,MAAM,KAAK,MAAM,IAAI,MAAM,KAAK;EAAE,CAAC;EAClF,MAAM,CAAC,OAAO,MAAM,QAAQ,IAAI,OAAO,QAAQ;EAC/C,IAAI,SAAS,KAAA,GAAW,IAAI,KAAK;GAAE,MAAM;GAAQ,MAAM;EAAK,CAAC;OACxD,IAAI,WAAW,KAAA,GAAW,IAAI,KAAK;GAAE,MAAM;GAAU,MAAM;EAAO,CAAC;OACnE,IAAI,OAAO,KAAA,GAAW,IAAI,KAAK;GAAE,MAAM;GAAM,MAAM;EAAG,CAAC;OACvD,IAAI,UAAU,KAAA,KAAa,SAAS,KAAA,KAAa,UAAU,KAAK,IAAI,GACvE,IAAI,KAAK;GAAE,MAAM;GAAQ,MAAM;GAAO;EAAK,CAAC;OACvC,IAAI,KAAK;GAAE,MAAM;GAAQ,MAAM;EAAM,CAAC;EAC7C,KAAK,MAAM,QAAQ,MAAM;CAC3B;CACA,IAAI,KAAK,KAAK,QAAQ,IAAI,KAAK;EAAE,MAAM;EAAQ,MAAM,KAAK,MAAM,EAAE;CAAE,CAAC;CACrE,OAAO;AACT"}
|
package/dist/marks.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
//#region src/marks.tsx
|
|
3
3
|
/**
|
|
4
|
-
* The marks Qué puede draws, each a 16-unit stroke glyph in `currentColor`: the
|
|
5
|
-
*
|
|
6
|
-
* glyph of each app, channel and device. No icon library: a host reads nothing but this package.
|
|
4
|
+
* The marks Qué puede draws, each a 16-unit stroke glyph in `currentColor`: the shipped actions'
|
|
5
|
+
* marks and the simplest faithful glyph of each app, channel and device. No icon library: a host reads nothing but this package.
|
|
7
6
|
*/
|
|
8
7
|
const STROKE = {
|
|
9
8
|
fill: "none",
|
package/dist/marks.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"marks.js","names":[],"sources":["../src/marks.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\n\nimport type {\n ActionSource,\n AutomationKind,\n ChannelKind,\n DeviceKind,\n Icon,\n Provider,\n} from \"./about\";\n\n/**\n * The marks Qué puede draws, each a 16-unit stroke glyph in `currentColor`: the icons a generated\n * line may carry, the shipped actions' marks, the kinds of automation, and the simplest faithful\n * glyph of each app, channel and device. No icon library: a host reads nothing but this package.\n */\n\nconst STROKE = {\n fill: \"none\",\n stroke: \"currentColor\",\n strokeWidth: 1.5,\n strokeLinecap: \"round\",\n strokeLinejoin: \"round\",\n} as const;\n\ninterface GlyphProps {\n /** A brand's own tint, set by the stylesheet on the mark alone. */\n brand?: ChannelKind | undefined;\n}\n\n/** One glyph: the paths inside the shared frame. */\nfunction Glyph({ children, brand }: GlyphProps & { children: ReactNode }) {\n return (\n <svg viewBox=\"0 0 16 16\" aria-hidden data-brand={brand} {...STROKE}>\n {children}\n </svg>\n );\n}\n\nconst message = (\n <path d=\"M3 3h10a1 1 0 0 1 1 1v6a1 1 0 0 1-1 1H7.6l-3.1 2.6V11H3a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1z\" />\n);\nconst phone = (\n <path d=\"M4.3 2.5h2l1.1 2.9-1.5 1a7.3 7.3 0 0 0 3.7 3.7l1-1.5 2.9 1.1v2a1.2 1.2 0 0 1-1.3 1.2A10.5 10.5 0 0 1 3.1 3.8a1.2 1.2 0 0 1 1.2-1.3z\" />\n);\nconst mail = (\n <>\n <rect x=\"2.5\" y=\"3.5\" width=\"11\" height=\"9\" rx=\"1.5\" />\n <path d=\"m2.5 5.5 5.5 3.7 5.5-3.7\" />\n </>\n);\nconst calendar = (\n <>\n <rect x=\"2.5\" y=\"3.5\" width=\"11\" height=\"10\" rx=\"1.5\" />\n <path d=\"M2.5 7h11M5.5 2v3M10.5 2v3\" />\n </>\n);\nconst clock = (\n <>\n <circle cx=\"8\" cy=\"8\" r=\"5.75\" />\n <path d=\"M8 4.75V8l2.2 1.4\" />\n </>\n);\nconst search = (\n <>\n <circle cx=\"7\" cy=\"7\" r=\"4.25\" />\n <path d=\"m10.2 10.2 3.3 3.3\" />\n </>\n);\nconst file = (\n <>\n <path d=\"M4 2.5h5l3.5 3.5v7.5a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1v-10a1 1 0 0 1 1-1z\" />\n <path d=\"M9 2.5V6h3.5\" />\n </>\n);\nconst table = (\n <>\n <rect x=\"2.5\" y=\"3\" width=\"11\" height=\"10\" rx=\"1.5\" />\n <path d=\"M2.5 6.5h11M6.5 6.5V13\" />\n </>\n);\nconst bell = (\n <>\n <path d=\"M4 11V7.5a4 4 0 0 1 8 0V11l1 1.5H3z\" />\n <path d=\"M6.5 13.5a1.5 1.5 0 0 0 3 0\" />\n </>\n);\nconst hand = (\n <>\n <path d=\"M5.5 8.2V4.5a1 1 0 0 1 2 0v3M7.5 7.5v-4a1 1 0 0 1 2 0v4M9.5 7.5V4.5a1 1 0 0 1 2 0v4.6\" />\n <path d=\"M5.5 8.2 4.4 7a1 1 0 0 0-1.6 1.2L5 11.6a3.6 3.6 0 0 0 3.1 1.9H9a2.5 2.5 0 0 0 2.5-2.5V9.1\" />\n </>\n);\nconst user = (\n <>\n <circle cx=\"8\" cy=\"5.5\" r=\"2.75\" />\n <path d=\"M3 13.5a5 5 0 0 1 10 0\" />\n </>\n);\nconst globe = (\n <>\n <circle cx=\"8\" cy=\"8\" r=\"5.75\" />\n <path d=\"M2.25 8h11.5M8 2.25c2 2.1 2 9.4 0 11.5M8 2.25c-2 2.1-2 9.4 0 11.5\" />\n </>\n);\nconst code = <path d=\"m5.5 5-3 3 3 3M10.5 5l3 3-3 3M9.3 3.5 6.7 12.5\" />;\nconst lock = (\n <>\n <rect x=\"3.5\" y=\"7\" width=\"9\" height=\"6.5\" rx=\"1.5\" />\n <path d=\"M5.5 7V5a2.5 2.5 0 0 1 5 0v2\" />\n </>\n);\nconst cart = (\n <>\n <path d=\"M2 3h1.8l1.4 7h7.3l1.3-5H4.4\" />\n <circle cx=\"6\" cy=\"12.8\" r=\".9\" fill=\"currentColor\" stroke=\"none\" />\n <circle cx=\"11.5\" cy=\"12.8\" r=\".9\" fill=\"currentColor\" stroke=\"none\" />\n </>\n);\nconst card = (\n <>\n <rect x=\"2\" y=\"3.5\" width=\"12\" height=\"9\" rx=\"1.5\" />\n <path d=\"M2 6.5h12M4.5 10h3\" />\n </>\n);\nconst map = (\n <>\n <path d=\"m2.5 4.5 3.5-1.5 4 1.5 3.5-1.5v9l-3.5 1.5-4-1.5-3.5 1.5z\" />\n <path d=\"M6 3v9M10 4.5v9\" />\n </>\n);\nconst check = <path d=\"m3.5 8.5 3 3 6-7\" />;\nconst warning = (\n <>\n <path d=\"M8 2.5 14 13H2z\" />\n <path d=\"M8 6.5v3\" />\n <circle cx=\"8\" cy=\"11.3\" r=\".8\" fill=\"currentColor\" stroke=\"none\" />\n </>\n);\nconst sparkle = (\n <>\n <path d=\"M7 2.5c.4 2.9 1.5 4 4.4 4.4-2.9.4-4 1.5-4.4 4.4-.4-2.9-1.5-4-4.4-4.4C5.5 6.5 6.6 5.4 7 2.5z\" />\n <path d=\"M12.3 10.5c.2 1 .5 1.3 1.5 1.5-1 .2-1.3.5-1.5 1.5-.2-1-.5-1.3-1.5-1.5 1-.2 1.3-.5 1.5-1.5z\" />\n </>\n);\n\nconst ICONS: Readonly<Record<Icon, ReactNode>> = {\n message,\n phone,\n mail,\n calendar,\n clock,\n search,\n file,\n table,\n bell,\n hand,\n user,\n globe,\n code,\n lock,\n cart,\n card,\n map,\n check,\n warning,\n sparkle,\n};\n\n/** The mark of a generated line. */\nexport const iconOf = (icon: Icon): ReactNode => <Glyph>{ICONS[icon]}</Glyph>;\n\nconst pane = (\n <>\n <rect x=\"2.5\" y=\"3\" width=\"11\" height=\"10\" rx=\"1.5\" />\n <path d=\"M2.5 6h11\" />\n </>\n);\nconst brain = (\n <>\n <path d=\"M7.5 2.75a2.4 2.4 0 0 0-3.3 2.6 2.4 2.4 0 0 0-.4 4.4A2.4 2.4 0 0 0 7.5 13.3z\" />\n <path d=\"M8.5 2.75a2.4 2.4 0 0 1 3.3 2.6 2.4 2.4 0 0 1 .4 4.4 2.4 2.4 0 0 1-3.7 3.55z\" />\n </>\n);\nconst book = (\n <path d=\"M2.5 3.5H7a1 1 0 0 1 1 1v9a1 1 0 0 0-1-1H2.5zM13.5 3.5H9a1 1 0 0 0-1 1v9a1 1 0 0 1 1-1h4.5z\" />\n);\nconst bolt = <path d=\"M9 2 3.5 9h4L7 14l5.5-7h-4z\" />;\n\n/** The shipped actions' marks, by name; an own action is code, one this package does not know a bolt. */\nconst ACTIONS: Readonly<Record<string, ReactNode>> = {\n show: pane,\n ask_person: message,\n escalate: hand,\n notify: bell,\n remember: brain,\n recall: brain,\n search_files: search,\n load_skill: book,\n browser_open: globe,\n browser_read: globe,\n browser_click: globe,\n browser_fill: globe,\n browser_scroll: globe,\n browser_back: globe,\n browser_submit: globe,\n browser_hand_over: hand,\n};\n\nexport const actionMark = (name: string, source: ActionSource): ReactNode => (\n <Glyph>{source === \"own\" ? code : (ACTIONS[name] ?? bolt)}</Glyph>\n);\n\nconst hook = (\n <>\n <path d=\"M2.5 8h7M7 5.5 9.5 8 7 10.5\" />\n <path d=\"M11 3.5h1.5a1 1 0 0 1 1 1v7a1 1 0 0 1-1 1H11\" />\n </>\n);\n\nconst AUTOMATIONS: Readonly<Record<AutomationKind, ReactNode>> = {\n schedule: clock,\n webhook: hook,\n event: bolt,\n};\n\nexport const automationMark = (kind: AutomationKind): ReactNode => (\n <Glyph>{AUTOMATIONS[kind]}</Glyph>\n);\n\n/** A letter alone: the simplest monogram of an app. */\nfunction Monogram({ letter }: { letter: string }) {\n return (\n <text\n x=\"8\"\n y=\"12.25\"\n textAnchor=\"middle\"\n fontSize=\"12.5\"\n fontWeight=\"500\"\n fontFamily=\"inherit\"\n fill=\"currentColor\"\n stroke=\"none\"\n >\n {letter}\n </text>\n );\n}\n\nconst hexagon = (\n <>\n <path d=\"M8 1.75 13.4 4.9v6.2L8 14.25 2.6 11.1V4.9z\" />\n <circle cx=\"8\" cy=\"8\" r=\"1.4\" fill=\"currentColor\" stroke=\"none\" />\n </>\n);\nconst braces = (\n <path d=\"M6 2.5c-1.5 0-2 .6-2 2v1.8c0 1-.5 1.7-1.5 1.7 1 0 1.5.7 1.5 1.7v1.8c0 1.4.5 2 2 2M10 2.5c1.5 0 2 .6 2 2v1.8c0 1 .5 1.7 1.5 1.7-1 0-1.5.7-1.5 1.7v1.8c0 1.4-.5 2-2 2\" />\n);\n\nconst PROVIDERS: Readonly<Record<Provider, ReactNode>> = {\n arcade: <Monogram letter=\"A\" />,\n composio: <Monogram letter=\"C\" />,\n mcp: hexagon,\n api: braces,\n};\n\n/** An app's mark: its own, when the provider gives a URL; the provider's glyph otherwise. */\nexport const providerMark = (provider: Provider, icon: string | undefined): ReactNode =>\n icon === undefined ? <Glyph>{PROVIDERS[provider]}</Glyph> : <img src={icon} alt=\"\" />;\n\nconst whatsapp = (\n <>\n <path d=\"M8 2.25A5.75 5.75 0 0 0 3.1 11L2.3 13.7l2.8-.75A5.75 5.75 0 1 0 8 2.25z\" />\n <path\n d=\"M6.2 5.3c.2-.3.5-.3.7-.3l.5 1.2c.1.2 0 .4-.1.5l-.4.4c.4.9 1.1 1.6 2 2l.4-.4c.1-.1.3-.2.5-.1l1.2.5c0 .2 0 .5-.3.7-.5.3-1.2.4-2.5-.5C6.9 8.4 6.3 7.3 6 6.1c-.1-.3 0-.6.2-.8z\"\n fill=\"currentColor\"\n stroke=\"none\"\n />\n </>\n);\nconst instagram = (\n <>\n <rect x=\"2.5\" y=\"2.5\" width=\"11\" height=\"11\" rx=\"3\" />\n <circle cx=\"8\" cy=\"8\" r=\"2.6\" />\n <circle cx=\"11.2\" cy=\"4.8\" r=\".7\" fill=\"currentColor\" stroke=\"none\" />\n </>\n);\nconst sms = (\n <>\n {message}\n <circle cx=\"5.5\" cy=\"7\" r=\".7\" fill=\"currentColor\" stroke=\"none\" />\n <circle cx=\"8\" cy=\"7\" r=\".7\" fill=\"currentColor\" stroke=\"none\" />\n <circle cx=\"10.5\" cy=\"7\" r=\".7\" fill=\"currentColor\" stroke=\"none\" />\n </>\n);\n\nconst CHANNELS: Readonly<Record<ChannelKind, ReactNode>> = {\n whatsapp,\n instagram,\n email: mail,\n sms,\n web: globe,\n phone,\n};\n\n/** A channel's glyph, carrying its brand for the stylesheet's tint. */\nexport const channelMark = (kind: ChannelKind): ReactNode => (\n <Glyph brand={kind}>{CHANNELS[kind]}</Glyph>\n);\n\nconst chrome = (\n <>\n <circle cx=\"8\" cy=\"8\" r=\"5.75\" />\n <circle cx=\"8\" cy=\"8\" r=\"2.25\" />\n <path d=\"M8 5.75h5.5M6.05 9.1 3.3 4.4M9.95 9.1l-2.7 4.6\" />\n </>\n);\nconst panes = (\n <>\n <rect x=\"2.5\" y=\"2.5\" width=\"4.75\" height=\"4.75\" rx=\"1\" />\n <rect x=\"8.75\" y=\"2.5\" width=\"4.75\" height=\"4.75\" rx=\"1\" />\n <rect x=\"2.5\" y=\"8.75\" width=\"4.75\" height=\"4.75\" rx=\"1\" />\n <rect x=\"8.75\" y=\"8.75\" width=\"4.75\" height=\"4.75\" rx=\"1\" />\n </>\n);\n\nconst DEVICES: Readonly<Record<DeviceKind, ReactNode>> = { extension: chrome, installer: panes };\n\nexport const deviceMark = (kind: DeviceKind): ReactNode => <Glyph>{DEVICES[kind]}</Glyph>;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"marks.js","names":[],"sources":["../src/marks.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\n\nimport type { ActionSource, ChannelKind, DeviceKind, Provider } from \"./about\";\n\n/**\n * The marks Qué puede draws, each a 16-unit stroke glyph in `currentColor`: the shipped actions'\n * marks and the simplest faithful glyph of each app, channel and device. No icon library: a host reads nothing but this package.\n */\n\nconst STROKE = {\n fill: \"none\",\n stroke: \"currentColor\",\n strokeWidth: 1.5,\n strokeLinecap: \"round\",\n strokeLinejoin: \"round\",\n} as const;\n\ninterface GlyphProps {\n /** A brand's own tint, set by the stylesheet on the mark alone. */\n brand?: ChannelKind | undefined;\n}\n\n/** One glyph: the paths inside the shared frame. */\nfunction Glyph({ children, brand }: GlyphProps & { children: ReactNode }) {\n return (\n <svg viewBox=\"0 0 16 16\" aria-hidden data-brand={brand} {...STROKE}>\n {children}\n </svg>\n );\n}\n\nconst message = (\n <path d=\"M3 3h10a1 1 0 0 1 1 1v6a1 1 0 0 1-1 1H7.6l-3.1 2.6V11H3a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1z\" />\n);\nconst phone = (\n <path d=\"M4.3 2.5h2l1.1 2.9-1.5 1a7.3 7.3 0 0 0 3.7 3.7l1-1.5 2.9 1.1v2a1.2 1.2 0 0 1-1.3 1.2A10.5 10.5 0 0 1 3.1 3.8a1.2 1.2 0 0 1 1.2-1.3z\" />\n);\nconst mail = (\n <>\n <rect x=\"2.5\" y=\"3.5\" width=\"11\" height=\"9\" rx=\"1.5\" />\n <path d=\"m2.5 5.5 5.5 3.7 5.5-3.7\" />\n </>\n);\nconst search = (\n <>\n <circle cx=\"7\" cy=\"7\" r=\"4.25\" />\n <path d=\"m10.2 10.2 3.3 3.3\" />\n </>\n);\nconst bell = (\n <>\n <path d=\"M4 11V7.5a4 4 0 0 1 8 0V11l1 1.5H3z\" />\n <path d=\"M6.5 13.5a1.5 1.5 0 0 0 3 0\" />\n </>\n);\nconst hand = (\n <>\n <path d=\"M5.5 8.2V4.5a1 1 0 0 1 2 0v3M7.5 7.5v-4a1 1 0 0 1 2 0v4M9.5 7.5V4.5a1 1 0 0 1 2 0v4.6\" />\n <path d=\"M5.5 8.2 4.4 7a1 1 0 0 0-1.6 1.2L5 11.6a3.6 3.6 0 0 0 3.1 1.9H9a2.5 2.5 0 0 0 2.5-2.5V9.1\" />\n </>\n);\nconst globe = (\n <>\n <circle cx=\"8\" cy=\"8\" r=\"5.75\" />\n <path d=\"M2.25 8h11.5M8 2.25c2 2.1 2 9.4 0 11.5M8 2.25c-2 2.1-2 9.4 0 11.5\" />\n </>\n);\nconst code = <path d=\"m5.5 5-3 3 3 3M10.5 5l3 3-3 3M9.3 3.5 6.7 12.5\" />;\n\nconst pane = (\n <>\n <rect x=\"2.5\" y=\"3\" width=\"11\" height=\"10\" rx=\"1.5\" />\n <path d=\"M2.5 6h11\" />\n </>\n);\nconst brain = (\n <>\n <path d=\"M7.5 2.75a2.4 2.4 0 0 0-3.3 2.6 2.4 2.4 0 0 0-.4 4.4A2.4 2.4 0 0 0 7.5 13.3z\" />\n <path d=\"M8.5 2.75a2.4 2.4 0 0 1 3.3 2.6 2.4 2.4 0 0 1 .4 4.4 2.4 2.4 0 0 1-3.7 3.55z\" />\n </>\n);\nconst book = (\n <path d=\"M2.5 3.5H7a1 1 0 0 1 1 1v9a1 1 0 0 0-1-1H2.5zM13.5 3.5H9a1 1 0 0 0-1 1v9a1 1 0 0 1 1-1h4.5z\" />\n);\nconst bolt = <path d=\"M9 2 3.5 9h4L7 14l5.5-7h-4z\" />;\n\n/** The shipped actions' marks, by name; an own action is code, one this package does not know a bolt. */\nconst ACTIONS: Readonly<Record<string, ReactNode>> = {\n show: pane,\n ask_person: message,\n escalate: hand,\n notify: bell,\n remember: brain,\n recall: brain,\n search_files: search,\n load_skill: book,\n browser_open: globe,\n browser_read: globe,\n browser_click: globe,\n browser_fill: globe,\n browser_scroll: globe,\n browser_back: globe,\n browser_submit: globe,\n browser_hand_over: hand,\n};\n\nexport const actionMark = (name: string, source: ActionSource): ReactNode => (\n <Glyph>{source === \"own\" ? code : (ACTIONS[name] ?? bolt)}</Glyph>\n);\n\n/** A letter alone: the simplest monogram of an app. */\nfunction Monogram({ letter }: { letter: string }) {\n return (\n <text\n x=\"8\"\n y=\"12.25\"\n textAnchor=\"middle\"\n fontSize=\"12.5\"\n fontWeight=\"500\"\n fontFamily=\"inherit\"\n fill=\"currentColor\"\n stroke=\"none\"\n >\n {letter}\n </text>\n );\n}\n\nconst hexagon = (\n <>\n <path d=\"M8 1.75 13.4 4.9v6.2L8 14.25 2.6 11.1V4.9z\" />\n <circle cx=\"8\" cy=\"8\" r=\"1.4\" fill=\"currentColor\" stroke=\"none\" />\n </>\n);\nconst braces = (\n <path d=\"M6 2.5c-1.5 0-2 .6-2 2v1.8c0 1-.5 1.7-1.5 1.7 1 0 1.5.7 1.5 1.7v1.8c0 1.4.5 2 2 2M10 2.5c1.5 0 2 .6 2 2v1.8c0 1 .5 1.7 1.5 1.7-1 0-1.5.7-1.5 1.7v1.8c0 1.4-.5 2-2 2\" />\n);\n\nconst PROVIDERS: Readonly<Record<Provider, ReactNode>> = {\n arcade: <Monogram letter=\"A\" />,\n composio: <Monogram letter=\"C\" />,\n mcp: hexagon,\n api: braces,\n};\n\n/** An app's mark: its own, when the provider gives a URL; the provider's glyph otherwise. */\nexport const providerMark = (provider: Provider, icon: string | undefined): ReactNode =>\n icon === undefined ? <Glyph>{PROVIDERS[provider]}</Glyph> : <img src={icon} alt=\"\" />;\n\nconst whatsapp = (\n <>\n <path d=\"M8 2.25A5.75 5.75 0 0 0 3.1 11L2.3 13.7l2.8-.75A5.75 5.75 0 1 0 8 2.25z\" />\n <path\n d=\"M6.2 5.3c.2-.3.5-.3.7-.3l.5 1.2c.1.2 0 .4-.1.5l-.4.4c.4.9 1.1 1.6 2 2l.4-.4c.1-.1.3-.2.5-.1l1.2.5c0 .2 0 .5-.3.7-.5.3-1.2.4-2.5-.5C6.9 8.4 6.3 7.3 6 6.1c-.1-.3 0-.6.2-.8z\"\n fill=\"currentColor\"\n stroke=\"none\"\n />\n </>\n);\nconst instagram = (\n <>\n <rect x=\"2.5\" y=\"2.5\" width=\"11\" height=\"11\" rx=\"3\" />\n <circle cx=\"8\" cy=\"8\" r=\"2.6\" />\n <circle cx=\"11.2\" cy=\"4.8\" r=\".7\" fill=\"currentColor\" stroke=\"none\" />\n </>\n);\nconst sms = (\n <>\n {message}\n <circle cx=\"5.5\" cy=\"7\" r=\".7\" fill=\"currentColor\" stroke=\"none\" />\n <circle cx=\"8\" cy=\"7\" r=\".7\" fill=\"currentColor\" stroke=\"none\" />\n <circle cx=\"10.5\" cy=\"7\" r=\".7\" fill=\"currentColor\" stroke=\"none\" />\n </>\n);\n\nconst CHANNELS: Readonly<Record<ChannelKind, ReactNode>> = {\n whatsapp,\n instagram,\n email: mail,\n sms,\n web: globe,\n phone,\n};\n\n/** A channel's glyph, carrying its brand for the stylesheet's tint. */\nexport const channelMark = (kind: ChannelKind): ReactNode => (\n <Glyph brand={kind}>{CHANNELS[kind]}</Glyph>\n);\n\nconst chrome = (\n <>\n <circle cx=\"8\" cy=\"8\" r=\"5.75\" />\n <circle cx=\"8\" cy=\"8\" r=\"2.25\" />\n <path d=\"M8 5.75h5.5M6.05 9.1 3.3 4.4M9.95 9.1l-2.7 4.6\" />\n </>\n);\nconst panes = (\n <>\n <rect x=\"2.5\" y=\"2.5\" width=\"4.75\" height=\"4.75\" rx=\"1\" />\n <rect x=\"8.75\" y=\"2.5\" width=\"4.75\" height=\"4.75\" rx=\"1\" />\n <rect x=\"2.5\" y=\"8.75\" width=\"4.75\" height=\"4.75\" rx=\"1\" />\n <rect x=\"8.75\" y=\"8.75\" width=\"4.75\" height=\"4.75\" rx=\"1\" />\n </>\n);\n\nconst DEVICES: Readonly<Record<DeviceKind, ReactNode>> = { extension: chrome, installer: panes };\n\nexport const deviceMark = (kind: DeviceKind): ReactNode => <Glyph>{DEVICES[kind]}</Glyph>;\n"],"mappings":"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"}
|
package/dist/surface/Markdown.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Markdown.js","names":[],"sources":["../../src/surface/Markdown.tsx"],"sourcesContent":["import { Fragment } from \"react\";\n\nimport { parse, type Inline } from \"../markdown\";\n\n/** A heading sits two levels under the page's own (its title, a dialog's): `#` is an `h3`. */\nconst HEADINGS = [\"h3\", \"h4\", \"h5\", \"h6\"] as const;\n\n/** The little Markdown of an agent's words, as headings, paragraphs and lists; the chat's too. */\nexport function Markdown({ text }: { text: string }) {\n return parse(text).map((node, index) => {\n if (node.kind === \"heading\") {\n const Heading = HEADINGS[Math.min(node.level, HEADINGS.length) - 1] ?? \"h6\";\n return (\n <Heading key={index}>\n <Words inline={node.inline} />\n </Heading>\n );\n }\n return node.kind === \"list\" ? (\n <ul key={index}>\n {node.items.map((item, at) => (\n <li key={at}>\n <Words inline={item} />\n </li>\n ))}\n </ul>\n ) : (\n <p key={index}>\n <Words inline={node.inline} />\n </p>\n );\n });\n}\n\n/** One line's words, its bold, italic, code and links drawn; a link opens apart, never in place. */\
|
|
1
|
+
{"version":3,"file":"Markdown.js","names":[],"sources":["../../src/surface/Markdown.tsx"],"sourcesContent":["import { Fragment } from \"react\";\n\nimport { parse, type Inline } from \"../markdown\";\n\n/** A heading sits two levels under the page's own (its title, a dialog's): `#` is an `h3`. */\nconst HEADINGS = [\"h3\", \"h4\", \"h5\", \"h6\"] as const;\n\n/** The little Markdown of an agent's words, as headings, paragraphs and lists; the chat's too. */\nexport function Markdown({ text }: { text: string }) {\n return parse(text).map((node, index) => {\n if (node.kind === \"heading\") {\n const Heading = HEADINGS[Math.min(node.level, HEADINGS.length) - 1] ?? \"h6\";\n return (\n <Heading key={index}>\n <Words inline={node.inline} />\n </Heading>\n );\n }\n return node.kind === \"list\" ? (\n <ul key={index}>\n {node.items.map((item, at) => (\n <li key={at}>\n <Words inline={item} />\n </li>\n ))}\n </ul>\n ) : (\n <p key={index}>\n <Words inline={node.inline} />\n </p>\n );\n });\n}\n\n/** One line's words, its bold, italic, code and links drawn; a link opens apart, never in place. */\nfunction Words({ inline }: { inline: Inline[] }) {\n return inline.map((part, index) => {\n switch (part.kind) {\n case \"text\":\n return <Fragment key={index}>{part.text}</Fragment>;\n case \"strong\":\n return <strong key={index}>{part.text}</strong>;\n case \"em\":\n return <em key={index}>{part.text}</em>;\n case \"code\":\n return <code key={index}>{part.text}</code>;\n case \"link\":\n return (\n <a key={index} href={part.href} target=\"_blank\" rel=\"noopener noreferrer\">\n {part.text}\n </a>\n );\n }\n });\n}\n"],"mappings":";;;;;AAKA,MAAM,WAAW;CAAC;CAAM;CAAM;CAAM;AAAI;;AAGxC,SAAgB,SAAS,EAAE,QAA0B;CACnD,OAAO,MAAM,IAAI,CAAC,CAAC,KAAK,MAAM,UAAU;EACtC,IAAI,KAAK,SAAS,WAAW;GAC3B,MAAM,UAAU,SAAS,KAAK,IAAI,KAAK,OAAO,SAAS,MAAM,IAAI,MAAM;GACvE,OACE,oBAAC,SAAD,EAAA,UACE,oBAAC,OAAD,EAAO,QAAQ,KAAK,OAAS,CAAA,EACtB,GAFK,KAEL;EAEb;EACA,OAAO,KAAK,SAAS,SACnB,oBAAC,MAAD,EAAA,UACG,KAAK,MAAM,KAAK,MAAM,OACrB,oBAAC,MAAD,EAAA,UACE,oBAAC,OAAD,EAAO,QAAQ,KAAO,CAAA,EACpB,GAFK,EAEL,CACL,EACC,GANK,KAML,IAEJ,oBAAC,KAAD,EAAA,UACE,oBAAC,OAAD,EAAO,QAAQ,KAAK,OAAS,CAAA,EAC5B,GAFK,KAEL;CAEP,CAAC;AACH;;AAGA,SAAS,MAAM,EAAE,UAAgC;CAC/C,OAAO,OAAO,KAAK,MAAM,UAAU;EACjC,QAAQ,KAAK,MAAb;GACE,KAAK,QACH,OAAO,oBAAC,UAAD,EAAA,UAAuB,KAAK,KAAe,GAA5B,KAA4B;GACpD,KAAK,UACH,OAAO,oBAAC,UAAD,EAAA,UAAqB,KAAK,KAAa,GAA1B,KAA0B;GAChD,KAAK,MACH,OAAO,oBAAC,MAAD,EAAA,UAAiB,KAAK,KAAS,GAAtB,KAAsB;GACxC,KAAK,QACH,OAAO,oBAAC,QAAD,EAAA,UAAmB,KAAK,KAAW,GAAxB,KAAwB;GAC5C,KAAK,QACH,OACE,oBAAC,KAAD;IAAe,MAAM,KAAK;IAAM,QAAO;IAAS,KAAI;IACjD,UAAA,KAAK;GACL,GAFK,KAEL;EAET;CACF,CAAC;AACH"}
|