Two regressions from the lazy text-cell placeholders:
- single-line placeholders centered their text in the full (possibly tall)
cell, so the text jumped upward when the top-aligned editor mounted. Keep
the glyph in a 2rem band at the top (scoped to editable cells via a
`topAligned` prop so the time/duration delay indicator is unaffected).
- multi-line placeholders only grew to their content height, so a short note
in a row made tall by another column was not fully clickable. Use
`min-height: 100%` so the placeholder fills the cell (and still grows with
content, no clipping).
Verified by measuring text/editor positions: single-line jump ~0px; all text
placeholders fill the cell and the whole area is clickable. Cuesheet e2e (4/4).
The lazy editor (Exp A) unmounts on exit, so the editor's own deferred
focus-return (useReactiveTextInput) targeted a detached node and focus was
lost to <body>, breaking table keyboard navigation after submit/escape.
Return focus to the parent cell deterministically in a layout effect once the
display element is back in the DOM (rather than a setTimeout that races the
next keypress), mirroring the time/duration cells.
Update the e2e tests to the lazy interaction model (click the cell to open the
on-demand editor, assert it unmounts on exit) — matching the existing timer
cell test. Verified end-to-end: 202-cuesheet (3/3) and 206-url-preset (5/5).
The metrics scaffold has served its purpose: it proved the lazy text-cell
editor change (Exp A) and ruled out the colour-precompute and lazy-autosize
candidates. Removing it entirely:
- restores AutoTextarea/Tooltip/useReactiveTextInput/EventRow/CuesheetTable
to their original (uninstrumented) form
- deletes common/devtools/cuesheet-metrics
Keeps the validated Exp A optimization (lazy editor mounting). Verified:
typecheck, lint, 181 unit tests, and production build all pass; no scaffold
references remain in the bundle.
Render a lightweight, focusable TextLikeInput for cue/title/note/custom cells
and only mount the real input/textarea editor (with reactive-input hooks and
autosize) on click/focus, mirroring the existing time/duration cells.
Measured baseline vs Exp A at CPU 4x (median of 3, ~1140 row mounts):
- longFrames 201 -> 127 (-37%), maxFrameMs 517 -> 417 (-19%)
- longtask total 58.9s -> 30.7s (-48%), EventRow.mount avg 179 -> 146ms (-18%)
- cell.autosize and cell.reactiveInputInit eliminated from the scroll path
- per-row re-renders 4648 -> 3048 (-34%): stable display height, no virtuoso churn
(the opposite of the reverted lazy-autosize Exp B, which doubled re-renders)
Editing verified end-to-end: click mounts+focuses the editor, edits persist,
editor unmounts back to the display on commit. TextLikeInput now fills its cell
so the whole cell is clickable to edit.
Adds a dev-only, self-contained instrumentation module under
common/devtools/cuesheet-metrics to measure the first-render (mount) cost
of virtualised cuesheet rows, so scroll-performance changes can be backed
by before/after numbers.
- Console-only API on window.__cuesheetPerf (deterministic scroll benchmark
+ manual start/stop), gated by isDev && ?perf=1, runtime-inert in prod.
- Whole-row mount probe plus per-subsystem attribution: colour calc,
getVisibleCells, AutoTextarea autosize, Tooltip portal mount,
useReactiveTextInput hotkey-handler init; FPS/long-frame timing.
- All edits to existing files tagged // PERF-METRICS for a one-pass teardown.
Temporary: to be deleted once the optimizations are proven.
* refactor: allow secondary rundowns
* ui: move dropdown
* feat: follow loaded
* ui: background edit warning
ui: fix disable radio button
* feat(ui): add loaded sufix in the rundown list
* feat(ui): add direct link to background edit from rundown manager
* fix: better fallback
* fix: default to is isCurrentRundown for nav bar colour
* chore: rename navigate to cuesheet
---------
Co-authored-by: alex-arc <ac@omnivox.dk>
* chore: migrate eslint to oxlint
* chore: migrate prettier to oxfmt
* chore: migrate typescript
* chore: toThrow should have a expected value
* chore: cast test value as Day
* chore: small title fix
* chore: mocks should be hoisted
* chore: incorrect async useage
* chore: test should be inside description
* chore: test sohuld include an expeced
* chore: oxfmt
---------
Co-authored-by: alex-Arc <omnivox@LAPTOP-RC5SNBVV.localdomain>
* refactor: align header columns
* refactor: improve scrollbar visibility
* refactor: center align table elements
* refactor: make param elements stateful
* fix: issue with collapsed elements not loosing value
* fix: prevent search params containing multiple alias references
* fix: the issue where a file disappears if it is both migrated and recovered in the same load operation (#1744)
* refactor: disable group action for elements in groups
* refactor: move context menu items into the event element (#1747)
* feat: sheet import new features for v4 (#1730)
* import milestone
* fixup! import milestone
* test: milestone import
* add entries to group
stop on new group or on group-end type
* fixup! add entries to group
* cleanup
* add event target duration
* link start if undefined
* add skip import type
* extract some to the excel paresing functions
* tweaks to presentation
* move file
---------
Co-authored-by: Carlos Valente <carlosvalente@pm.me>
* fix: notify runtimeStore of events bieng groupd
* fix: improve authentication and stage detection in demo
* chore: ship logo with project
* fix: client is referenced by name
* fix: prevent reflow in event editor
* fix: stale render on selected event due to ref mismatch
* Create/Load/Delete multiple rundowns (#1696)
* refactor: restore last loaded rundown
* refactor: initialise rundown in ProjectService
* feat: allow switching rundowns
ensure on coordination between the db object and the working object
server provide list of rundowns
implement switch in the UI
implement delete
implement new rundown button
* fix: render order for floating button
* refactor: appropriate names to service
* refactor: rundown endpoints
* refactor: save last loaded rundown ID
* refactor: rundown management UI
* refactor: emit refetch all on project load
---------
Co-authored-by: Carlos Valente <carlosvalente@pm.me>
* feat: recover single event subscription
* fixup! feat: sheet import new features for v4 (#1730)
* fix: prevent dropping a group inside another
* fixup! refactor: move context menu items into the event element (#1747)
* fix: prevent stale references to custom fields
* fix: propagate updates to all rundowns
* refactor: client rundown metadata (#1728)
* generate metadata in the hook
* move test
* ensure there is always a last element
* use for-loop
* update metadata in useEfect
* fully extract metadata generation
* use direct assignment
* cleanup
---------
Co-authored-by: Carlos Valente <carlosvalente@pm.me>
* refactor: small imporvement and tests for coerce functions (#1752)
* refactor: small imporvement and tests for coerce functions
add test `coerceString`
add test `coerceBoolean`
add test `coerceColour`
* remove old todo
* fix: consistent quick add behaviour
* refactor: create flat rundown with metadata
* fix: show add buttons on top
* feat: allow editing milestones
* refactor: style tweaks to rundown elements
refactor: milestones are full width
refactor: cuesheet header alignment
fix: editor styling in cuesheet
* refactor: virtualise table
* refactor: improve overscan (#1758)
* bump version to 4.0.0-alpha.5
---------
Co-authored-by: Alex Christoffer Rasmussen <ac@omnivox.dk>
The IntersectionObserver callback in rowObserver.ts was capturing the `addVisibleRow` and `removeVisibleRow` actions from the Zustand store only once, at the time of the observer's creation.
This created a potential for a stale closure bug, where the observer would call outdated action functions if the store's state or actions were ever re-initialized. This would lead to the application's state not being updated correctly, causing the bug where visible rows would not render their content.
This commit fixes the issue by calling `useVisibleRowsStore.getState()` inside the observer callback. This ensures that the latest, freshest versions of the action functions are always used, preventing the stale state bug.
- italic and bold for rundown metadata entries
- allow deleting groups
- make groups in order
- correct style for milestones
- open playing groups
- style tweaks