- D1 (soma) block-tree.ts:104 — replace `doc as never` (which disabled all
type checking on the descend container) with `doc as unknown as {...}`, the
honest cast that still asserts the permissive descend-node shape
childStacks() reads.
- D2 (eidos) — extract the duplicated ~25-line "file picker → FileReader →
data URL" flow (copy-pasted in words-block-gutter.svelte and
words-block-panel.svelte) into a shared `pickImageFile(doc, onPick)` helper
in words-image-file.ts. Both consumers now call it; zero inline FileReader
blocks remain. Kept as a plain helper (not ActiveDom) — a transient
<input type=file> + FileReader is not instrumented by active-dom, so a
direct Document touch is correct; the caller resolves the right Document.
Gates: npm run check 1 error (pre-existing grafito, not Words) · words soma
452/452 · prettier clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
active-uix
parent
49802e9257
commit
1186d794cd
@ -0,0 +1,44 @@
|
|||||||
|
/**
|
||||||
|
* Native image-file picker → data URL.
|
||||||
|
*
|
||||||
|
* Shared by the two Words consumers that let the user insert an image
|
||||||
|
* from disk: the gutter inserter (`words-block-gutter.svelte`) and the
|
||||||
|
* inspector's image panel upload button (`words-block-panel.svelte`).
|
||||||
|
* Both need the identical "open file dialog → FileReader → data URL"
|
||||||
|
* flow, so it lives here once instead of being copy-pasted.
|
||||||
|
*
|
||||||
|
* Kept as a plain helper (NOT routed through ActiveDom): a transient
|
||||||
|
* `<input type="file">` plus `FileReader` is not instrumented by the
|
||||||
|
* active-dom runtime, so a direct `Document` touch is correct here. The
|
||||||
|
* caller resolves the right `Document` (content's `ownerDocument`, or the
|
||||||
|
* global as a fallback) and passes it in, keeping this helper SSR-safe
|
||||||
|
* and portal/iframe-correct.
|
||||||
|
*
|
||||||
|
* `onPick` fires only when the user actually chooses a file; it receives
|
||||||
|
* the data-URL `src` and a derived `alt` (the filename without its
|
||||||
|
* extension, or `undefined` when the name is empty).
|
||||||
|
*/
|
||||||
|
export function pickImageFile(
|
||||||
|
doc: Document,
|
||||||
|
onPick: (src: string, alt: string | undefined) => void
|
||||||
|
): void {
|
||||||
|
const input = doc.createElement('input');
|
||||||
|
input.type = 'file';
|
||||||
|
input.accept = 'image/*';
|
||||||
|
input.style.display = 'none';
|
||||||
|
input.addEventListener('change', () => {
|
||||||
|
const file = input.files?.[0];
|
||||||
|
input.remove();
|
||||||
|
if (!file) return;
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = () => {
|
||||||
|
const src = typeof reader.result === 'string' ? reader.result : '';
|
||||||
|
if (!src) return;
|
||||||
|
const alt = file.name.replace(/\.[^/.]+$/, '') || undefined;
|
||||||
|
onPick(src, alt);
|
||||||
|
};
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
});
|
||||||
|
doc.body.appendChild(input);
|
||||||
|
input.click();
|
||||||
|
}
|
||||||
Loading…
Reference in new issue