Skip to content

Stop a bullet's open editor taking Studio down on the next keystroke - #108

Merged
IanHeinrich merged 2 commits into
mainfrom
claude/cvforge-bug-fix-pr46-c31363
Aug 30, 2026
Merged

Stop a bullet's open editor taking Studio down on the next keystroke#108
IanHeinrich merged 2 commits into
mainfrom
claude/cvforge-bug-fix-pr46-c31363

Conversation

@IanHeinrich

Copy link
Copy Markdown
Owner

What and why

Ported from IanHeinrich/CareerForge#46, where this was reported from production. cv-forge's copy of the code was character-identical, so it has the same bug.

Opening a bullet's inline text editor in Studio and then typing anything threw on every following rebuild:

RangeError (end): Invalid value: Not in inclusive range 0..8: -1

That aborts the Studio subtree mid-update — the preview goes blank and the error repeats every frame.

VaultItemSelectorList.didUpdateWidget closes an open editor when the entry it belongs to leaves the CV, and recovered that entry by splitting the editor's id on its last underscore. That holds for the two ids the widget composes itself — '<itemId>_title' and '<itemId>_<index>' — but the bullet sub-list passes a bullet's own bare id. A bullet id is a uuid, or exp-1-b1 in the worked example; neither contains an underscore, so lastIndexOf returns -1 and substring(0, -1) throws. The 8 in the message is 'exp-1-b1'.length — anyone on the example CV hits it first.

Wrong twice over: had a bullet id happened to contain an underscore, the split would have named an entry that does not exist and the editor would have silently closed itself instead.

The owner is now carried alongside the id rather than parsed back out of it (typedef _OpenEditor = ({String id, String ownerId})), since only the call site knows which entry a row hangs off — and for a bullet the id genuinely does not say. The three call sites each bind their own owner; the string surgery is gone.

The second commit is unrelated to the bug and dev-only: the Studio preview would not come up locally at all, because it rasterizes build/web/pdfjs/pdf.min.mjs and tool/serve_web.mjs's MIME table had no .mjs entry — a module script served as application/octet-stream is rejected under strict MIME checking, so the preview spins for ever with the only clue in the browser console. That blocked the browser check this template asks for. Pages already types .mjs correctly.

How it was verified

  • dart format --output=none --set-exit-if-changed .
  • flutter analyze — no issues found
  • flutter test --exclude-tags=golden — 888 pass
  • Checked in the browser (required for anything under lib/features/studio/widgets/)

The new test reproduces the production error against the pre-fix code exactly: 0..8: -1 for the fixture id and 0..36: -1 for a uuid.

Browser check, on the example CV: Studio → Work history → expand a bullet sub-list → open a bullet's inline editor → type. The editor stays open, the text takes, the count moves to "tailored", the PDF preview stays rendered, and the console carries no RangeError.

Goldens

  • No golden baselines affected

Anything else

Two things from the upstream PR were deliberately left out as CareerForge-specific: the 0.0.10 → 0.0.11 version bump — cv-forge is on 2.24.0 and backup_service.dart reads packageVersion rather than a duplicated literal, so there is nothing to keep in sync — and the docs/roadmap.md entry. Say the word if a release bump is wanted here.

Opening a bullet's inline text editor and then typing anything threw on
every following rebuild:

    RangeError (end): Invalid value: Not in inclusive range 0..8: -1

That aborts the Studio subtree mid-update, so the preview goes blank and
the error repeats every frame.

`VaultItemSelectorList.didUpdateWidget` closes an open editor when the
entry it belongs to leaves the CV, and recovered that entry by splitting
the editor's id on its last underscore. That holds for the two ids the
widget composes itself, `'<itemId>_title'` and `'<itemId>_<index>'`, but
the bullet sub-list passes a bullet's own bare id. A bullet id is a uuid,
or `exp-1-b1` in the worked example; neither contains an underscore, so
`lastIndexOf` returns -1 and `substring(0, -1)` throws. The 8 in the
message is `'exp-1-b1'.length`, so anyone on the example CV hits it
first.

Wrong twice over: had a bullet id happened to contain an underscore, the
split would have named an entry that does not exist and the editor would
have silently closed itself instead.

The owner is now carried alongside the id rather than parsed back out of
it, since only the call site knows which entry a row hangs off — and for
a bullet the id genuinely does not say.

Ported from IanHeinrich/CareerForge#46, where the same bug was reported
from production.
The PR template asks for a browser check on anything under
lib/features/studio/widgets/, and the Studio preview pane was the thing
that would not come up: it rasterizes build/web/pdfjs/pdf.min.mjs, the
dev server's MIME table had no .mjs entry, and a module script served as
application/octet-stream is rejected outright under strict MIME checking.

The failure is silent from the app's side — the preview just spins for
ever, with the only clue in the browser console — so it reads like the
preview is broken rather than the server.

Dev-only; GitHub Pages already types .mjs correctly.
@IanHeinrich
IanHeinrich merged commit 31ceafa into main Aug 30, 2026
3 checks passed
@IanHeinrich
IanHeinrich deleted the claude/cvforge-bug-fix-pr46-c31363 branch August 30, 2026 17:24
@IanHeinrich IanHeinrich mentioned this pull request Aug 30, 2026
5 tasks
IanHeinrich added a commit that referenced this pull request Aug 30, 2026
Ships the bullet inline-editor crash fix from #108, which landed on main
without a version bump and so did not deploy.

app_version.dart is regenerated from pubspec by build_version.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant