- 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