@@ -1556,11 +1556,115 @@ grouped with a text element, e.g. via a script calling
15561556changes elsewhere, including ` getViewColorPalette() ` since its logic itself
15571557was already confirmed correct and unchanged.
15581558
1559- ** If resumed:** ` ExcalidrawAutomate.ts ` (57) is the natural next file-by-file
1560- triage target, followed by re-checking ` AIUtils.ts ` (46, previously declined
1561- as external-boundary — worth confirming that conclusion still holds now
1562- that so much else has changed) and a final full-repo sweep once the large
1563- clusters are gone.
1559+ ** Done (2026-08-14, session 4): ` ExcalidrawAutomate.ts ` fully triaged, 57 → 0.**
1560+ On ` master ` (the ` excalidraw-type-import-fix ` PR was merged as #2892 in the
1561+ interim; ` master ` is now at ` 2.27.0-beta.2 ` ). Same methodology, three
1562+ clusters:
1563+
1564+ 1 . ** Safe, fixed (49 of 57).** ` cloneElement() ` /` cloneElements() ` 's inner
1565+ map both do ` JSON.parse(JSON.stringify(el)) ` to deep-clone an already-known
1566+ ` ExcalidrawElement ` — cast to ` Mutable<ExcalidrawElement> ` (` cloneElement ` )
1567+ and a small local `ClonedElementDraft = Mutable<ExcalidrawElement > &
1568+ {containerId?; startBinding?; endBinding?}` ( ` cloneElements`, since the
1569+ remap logic generically probes text/arrow-only relationship fields across
1570+ whatever element variant it receives — matches the union's actual base
1571+ fields plus the two Excalidraw-typed cross-cutting ones, not an invented
1572+ shape). One follow-on: `newEl.boundElements.map((bound: {id: string; type:
1573+ string}) => ...)` had a same-shape-but-looser-than-necessary inline
1574+ annotation; swapped for the real imported ` BoundElement ` type. Also a
1575+ ` new Promise((resolve) => ...) ` with no generic, resolved with `string |
1576+ null` in different branches — TS had defaulted the whole chain to
1577+ ` unknown ` ; added the explicit ` Promise<string | null> ` .
1578+ 2 . ** Initially suppressed as external-boundary (8), then genuinely fixed
1579+ once the actual sources became available — 0 suppressions remain.** Two
1580+ distinct sources, both traced to their exact origin before touching
1581+ anything: ` addMermaid() ` 's ` result.files ` traced to the upstream
1582+ ` mermaidToExcalidraw() ` function's own declared return type (`{
1583+ elements?: ExcalidrawElement[ ] ; files?: any; error?: string } | undefined`
1584+ in ` @zsviczian/excalidraw ` 's ` MermaidToExcalidrawLib.d.ts ` ) — `files?:
1585+ any` was the * library's own* declared type, not something the earlier
1586+ ` @excalidraw/common ` fix masked or could improve from this side. First
1587+ pass suppressed both with ` eslint-disable-next-line ` plus a one-line
1588+ justification per site, matching this repo's documented suppression
1589+ format. The user then fixed it at the actual source: updated
1590+ ` mermaidToExcalidraw ` 's declaration in the fork
1591+ (` packages/excalidraw/components/TTDDialog/MermaidToExcalidrawLib.ts ` )
1592+ to ` files?: BinaryFiles ` , rebuilt, and refreshed this repo's installed
1593+ ` node_modules ` copy — both suppressions simply deleted, no cast needed,
1594+ the value is now genuinely typed. For ` cloneElements() ` 's
1595+ ` JSON.parse(elementsOrClipboard) ` , the user pointed at the authoritative
1596+ source instead of accepting the suppression: ` actionCopy ` /
1597+ ` serializeAsClipboardJSON() ` in the fork's own
1598+ ` packages/excalidraw/clipboard.ts ` show the real envelope Excalidraw's
1599+ own copy action produces (`{type: "excalidraw/clipboard", elements,
1600+ files}` , ` EXPORT_DATA_TYPES.excalidrawClipboard === "excalidraw/clipboard"`
1601+ confirmed against ` packages/common/src/constants.ts ` , matching the
1602+ plugin's own runtime check exactly). Replaced the suppressions with a real
1603+ union type (`{type: string; elements: ExcalidrawElement[ ] } |
1604+ ExcalidrawElement[ ] `, the second arm being this method's own added
1605+ convenience for a bare JSON-stringified array, not an Excalidraw-produced
1606+ format) and one added ` !Array.isArray(parsed) ` guard ahead of the
1607+ ` .type ` check purely to let the union narrow before that property access
1608+ — logically a no-op versus the original runtime behavior, since a bare
1609+ array was already implicitly guaranteed to fail the `.type ===
1610+ "excalidraw/clipboard"` comparison (arrays have no ` .type`).
1611+
1612+ ` npm run build ` /` npm run lib ` /` node --check dist/main.js ` all pass clean
1613+ with ** zero ` eslint-disable ` suppressions anywhere in this file** ;
1614+ 33-warning circular-dependency baseline unchanged; ` dist/main.js ` is
1615+ 4,716,872 bytes (+19 bytes from the prior checkpoint, from the fork's own
1616+ rebuild — this repo's change is compile-time only). Confirmed via
1617+ ` git stash ` /pop full-repo ESLint diff: 229 → 172 (57 fewer), zero
1618+ regressions, the entire delta contained to this one file. Committed
1619+ (` efb00d1d ` ) on branch ` excalidraw-automate-cleanup ` , pushed; PR not yet
1620+ opened (` gh ` unavailable in this environment — no token/CLI to author it
1621+ directly, link and prepared title/body handed to the user instead).
1622+
1623+ ** Done (2026-08-14, session 5): ` excalidrawViewUtils.ts ` fully triaged,
1624+ 20 → 0.** On branch ` excalidraw-automate-cleanup ` (continued rather than
1625+ starting a new branch). Two clusters:
1626+
1627+ 1 . ** The exact ` RegExpMatchIteratorResult ` -shaped bug again (16 of 20).**
1628+ ` isTextImageTransclusion() ` 's ` const match = text.trim().matchAll(...).next(); `
1629+ — a bare ` let ` /` const ` with no annotation, assigned via ` .next() ` on a
1630+ ` matchAll() ` iterator, collapsing to implicit ` any ` and cascading through
1631+ 16 downstream accesses. Same exact shape as the very first fix in this
1632+ whole ` no-unsafe-* ` effort (` ExcalidrawData.ts ` , session predating this
1633+ page's start) — annotated ` IteratorResult<RegExpMatchArray, undefined> ` .
1634+ The plan's own "if resumed" note several sections up predicted exactly
1635+ this: "look for more ` ExcalidrawData.ts ` -shaped cases... a local variable
1636+ whose real type is already known... just missing on one declaration."
1637+ 2 . ** ` Function.prototype.bind() ` degrading a real signature to ` any ` (4).**
1638+ ` getViewColorPalette() ` 's `cmFactory = view.hookServer?.getCM?.bind(...) ??
1639+ view.plugin.ea.getCM.bind(...)`. Probed each side independently
1640+ (throwaway type-probes, added and reverted): ` view.hookServer?.getCM ` and
1641+ ` view.plugin.ea.getCM ` both resolve cleanly to their real declared
1642+ signature, ` (color: TInput) => ColorMaster ` — the ` any ` is introduced
1643+ exclusively by ` .bind() ` 's own type declaration failing to preserve it.
1644+ A plain variable type annotation on ` cmFactory ` satisfied ` tsc ` but
1645+ * not* ` eslint ` 's ` no-unsafe-assignment ` (which still flags assigning a
1646+ provably-` any ` -typed expression regardless of the target annotation);
1647+ switched to an explicit ` as (color: TInput) => ColorMaster ` cast instead,
1648+ which both tools accept. Imported the real ` TInput ` /` ColorMaster ` types
1649+ from ` @zsviczian/colormaster ` — the file's own pre-existing local
1650+ ` ColorMasterLike ` structural type and ` isColorMasterLike() ` guard (used
1651+ for everything * after* the ` cmFactory(...) ` call) were left untouched,
1652+ since only the call signature itself needed fixing.
1653+
1654+ ` npm run build ` /` npm run lib ` /` node --check dist/main.js ` all pass clean;
1655+ 33-warning circular-dependency baseline and ` dist/main.js ` byte size both
1656+ unchanged (4,716,872 bytes — compile-time only). Confirmed via `git
1657+ stash`/pop full-repo ESLint diff: 172 → 152 (20 fewer), zero regressions,
1658+ entire delta contained to this one file.
1659+
1660+ ** If resumed:** ` AIUtils.ts ` (46, previously declined as external-boundary)
1661+ is the natural next target — worth confirming that conclusion still holds
1662+ given how many things in this effort turned out to have a real fix hiding
1663+ behind what first looked like a boundary, rather than assuming. After that,
1664+ a final full-repo sweep once the remaining clusters are gone. Realistic
1665+ floor is still not 0 — ` AIUtils.ts ` 's provider-JSON boundary and Obsidian's
1666+ own ` FrontMatterCache: {[key: string]: any} ` remain genuine, unavoidable
1667+ external boundaries by their own nature, not by insufficient effort.
15641668
15651669## Related, separate effort: ` ExcalidrawData.ts ` structural extraction
15661670
0 commit comments