mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-18 13:44:12 +00:00
docs(spec): format finder spec
The unit-test job runs format:check before the tests, so an unformatted markdown file failed the job while every test passed. Promote the bold pseudo-headings in the Phase 3 section to real subheadings. They previously sat directly above numbered list items, which the formatter reads as one paragraph and collapses into a run-on line, mangling items 13-27. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DzALEq9gGWwFmwTdgAiFcY
This commit is contained in:
+31
-26
@@ -11,12 +11,12 @@ business-development one: is it worth investing in, and can it beat the competit
|
||||
Scope decided for the roadmap: **editor + cuesheet**, **navigation-only** (the Finder reveals
|
||||
entries, it does not act on them), and **results stay in rundown order** — no ranking.
|
||||
|
||||
| File | Role |
|
||||
|---|---|
|
||||
| `apps/client/src/views/editor/finder/Finder.tsx` | Modal, input, result list, footer hints |
|
||||
| `apps/client/src/views/editor/finder/useFinder.tsx` | Search logic |
|
||||
| `apps/client/src/views/editor/finder/Finder.module.scss` | Styling |
|
||||
| `apps/client/src/features/rundown/placements/FinderPlacement.tsx` | `mod+F` hotkey + mount |
|
||||
| File | Role |
|
||||
| ----------------------------------------------------------------- | --------------------------------------- |
|
||||
| `apps/client/src/views/editor/finder/Finder.tsx` | Modal, input, result list, footer hints |
|
||||
| `apps/client/src/views/editor/finder/useFinder.tsx` | Search logic |
|
||||
| `apps/client/src/views/editor/finder/Finder.module.scss` | Styling |
|
||||
| `apps/client/src/features/rundown/placements/FinderPlacement.tsx` | `mod+F` hotkey + mount |
|
||||
|
||||
---
|
||||
|
||||
@@ -61,7 +61,7 @@ is hardest.
|
||||
### 1.4 Reach
|
||||
|
||||
Editor-only. But `apps/client/src/views/cuesheet/cuesheet-table/CuesheetTable.tsx:160` already
|
||||
registers a scroll handler commented *"for explicit jumps (finder/keyboard)"* — the plumbing was
|
||||
registers a scroll handler commented _"for explicit jumps (finder/keyboard)"_ — the plumbing was
|
||||
anticipated and built, the Finder was never mounted there. Cheapest high-value win available.
|
||||
|
||||
**Constraint:** the cuesheet is `permission='operator'` and can be exposed via URL presets with
|
||||
@@ -92,7 +92,7 @@ results, so every behaviour described above is unguarded.
|
||||
|
||||
**A runtime-aware, multi-surface finder is a real wedge.** [Shoflo](https://shoflo.tv/),
|
||||
[Rundown Studio](https://rundownstudio.app/) and [Cuez](https://cuez.app/script-rundown/) are cloud
|
||||
rundown *editors* — their search is document search over a document you're authoring. Ontime knows
|
||||
rundown _editors_ — their search is document search over a document you're authoring. Ontime knows
|
||||
show state: what's loaded, what's past, the offset, what's flagged. A finder that marks the loaded
|
||||
event and shows scheduled time and delay per row is a different category of tool, and that data
|
||||
already exists in `RundownMetadata` and is currently discarded. Add that it works identically on the
|
||||
@@ -113,7 +113,7 @@ Sequence accordingly.
|
||||
|
||||
Estimates assume one developer familiar with the codebase.
|
||||
|
||||
### Phase 1 — Correctness *(~0.5 day)*
|
||||
### Phase 1 — Correctness _(~0.5 day)_
|
||||
|
||||
1. **Track `selectedId`, not `selectedIndex`** — derive the active row by id lookup, falling back to
|
||||
the first. Correct across updates, reordering and result changes; better than "reset to 0", which
|
||||
@@ -129,7 +129,7 @@ Estimates assume one developer familiar with the codebase.
|
||||
latent conflict with inline field editing.
|
||||
6. **Fix the `index <n>` bound** to use the event count.
|
||||
|
||||
### Phase 2 — Search depth *(~1.5–2 days)*
|
||||
### Phase 2 — Search depth _(~1.5–2 days)_
|
||||
|
||||
7. **Search cue + title + note + text custom fields** across events, groups and milestones on a bare
|
||||
query. Skip `image`-type custom fields. Cap indexed note length so one pasted script can't
|
||||
@@ -138,8 +138,8 @@ Estimates assume one developer familiar with the codebase.
|
||||
for every key so today's documented syntax keeps working. Drive the footer hint and the
|
||||
`EventEditorEmpty.tsx` cheat sheet from one filter-key constant so they can't drift.
|
||||
9. **Keep rundown order — no ranking.** Deterministic, simpler, and it preserves the early-exit scan
|
||||
(today's cap of 12 is a *scan* cap, not a sort cap; ranking would force scanning everything).
|
||||
*One honest consequence:* widening the searched fields while keeping position order means a
|
||||
(today's cap of 12 is a _scan_ cap, not a sort cap; ranking would force scanning everything).
|
||||
_One honest consequence:_ widening the searched fields while keeping position order means a
|
||||
distant cue match can be pushed off the list by nearer note matches. Two mitigations, both cheap
|
||||
— raise the cap and show "showing 12 of 47", and label which field matched so a note hit is
|
||||
obviously a note hit. If that still isn't enough in practice, group by matched field (cue block,
|
||||
@@ -152,18 +152,20 @@ Estimates assume one developer familiar with the codebase.
|
||||
Highest-value case: `index <n>` staying aligned with 1-based UI event indices when delays, groups
|
||||
and milestones interleave — the current code gets this right and a rewrite is likely to break it.
|
||||
|
||||
### Phase 3 — Visibility, UX and polish *(~1.5 days)*
|
||||
### Phase 3 — Visibility, UX and polish _(~1.5 days)_
|
||||
|
||||
This is the phase that changes how many people ever use the feature.
|
||||
|
||||
**Visibility**
|
||||
#### Visibility
|
||||
|
||||
13. **Add a search control to `RundownHeader.tsx`** with the `mod+F` hint visible on it. Single
|
||||
biggest discoverability win.
|
||||
14. **Add one to `RundownHeaderMobile.tsx`** — mandatory, not optional: without a keyboard the
|
||||
feature currently does not exist on touch devices.
|
||||
15. **Add one to the cuesheet toolbar** when the Finder mounts there (Phase 4).
|
||||
|
||||
**UX**
|
||||
#### UX
|
||||
|
||||
16. **Fix the empty state** — it shows "No results" before you've typed anything. Show the filter
|
||||
hints, or recent searches, on open.
|
||||
17. **Align with the app's other search box.** `SettingsSearch.tsx` has a leading `IoSearch` icon and
|
||||
@@ -175,7 +177,8 @@ This is the phase that changes how many people ever use the feature.
|
||||
21. **Preserve the last query on reopen**, selected so typing replaces it.
|
||||
22. **`Home` / `End` / `PageUp` / `PageDown`** in the result list, matching `useRundownKeyboard`.
|
||||
|
||||
**Polish**
|
||||
#### Polish
|
||||
|
||||
23. **Use `getAccessibleColour`** (`common/utils/styleUtils`) for the index badge. It currently sets
|
||||
`background: var(--color)` raw with fixed foreground text, so a light entry colour is unreadable.
|
||||
The cuesheet `EventRow`, `MilestoneRow` and `OperatorEvent` already do this correctly — the
|
||||
@@ -187,7 +190,7 @@ This is the phase that changes how many people ever use the feature.
|
||||
27. **Reconsider the 100 ms debounce** — with a controlled input (item 11) it's unnecessary at these
|
||||
rundown sizes and just reads as lag.
|
||||
|
||||
### Phase 4 — Cuesheet *(~1 day)*
|
||||
### Phase 4 — Cuesheet _(~1 day)_
|
||||
|
||||
28. **Move the Finder out of `features/rundown/placements/`** — it stops being an editor view once it
|
||||
has two homes — and mount it in the cuesheet. Pass surface and permission explicitly from the
|
||||
@@ -198,10 +201,10 @@ This is the phase that changes how many people ever use the feature.
|
||||
delay per row. Source at render time, not by rebuilding the index (§1.5). This is the
|
||||
differentiator from §2.
|
||||
31. **E2E spec** alongside `209-rundown-shortcuts.spec.ts`, covering both surfaces, with a regression
|
||||
lock for the click bug: click the third result *without* hovering the first two, assert the third
|
||||
lock for the click bug: click the third result _without_ hovering the first two, assert the third
|
||||
entry is selected.
|
||||
|
||||
### Phase 5 — Find and replace *(~3–4 days)*
|
||||
### Phase 5 — Find and replace _(~3–4 days)_
|
||||
|
||||
The strongest expansion, and a better business case than a command palette: renaming a sponsor,
|
||||
speaker or venue across a 200-entry rundown is a real recurring pain that the competitors'
|
||||
@@ -221,6 +224,7 @@ and replace all agree on what "matches" means.
|
||||
**32. The server gap — this is the actual work.** `batchEditEntries` (`rundown.service.ts:166`)
|
||||
applies **one patch to many ids**. Find-and-replace needs **per-entry distinct values** — each
|
||||
entry's own title with its own substring swapped. Two options:
|
||||
|
||||
- N × `putEditEntry`: N round trips, N revision bumps, N `notifyChanges` broadcasts. During a show
|
||||
that's N timer notifications for one user action. Not acceptable at 40 entries.
|
||||
- **A new endpoint taking `Array<PatchWithId<OntimeEntry>>` applied inside one
|
||||
@@ -229,18 +233,19 @@ entry's own title with its own substring swapped. Two options:
|
||||
possible.
|
||||
|
||||
**33. Replaceable fields — deliberately narrow.**
|
||||
- *Safe:* `title`, `note`, text `custom` values. Free text, no structural meaning.
|
||||
- *Opt-in and validated:* `cue`. It's a numbering scheme, not free text — `cueUtils` has
|
||||
|
||||
- _Safe:_ `title`, `note`, text `custom` values. Free text, no structural meaning.
|
||||
- _Opt-in and validated:_ `cue`. It's a numbering scheme, not free text — `cueUtils` has
|
||||
`getIncrement`/`getCueCandidate` and the Renumber dialog exists precisely because of that. The
|
||||
server also rejects an empty cue outright (`rundown.service.ts:177`), so a replace that empties one
|
||||
fails the whole batch.
|
||||
- *Never:* times, booleans, colours, enums, ids, `image`-type custom fields.
|
||||
- _Never:_ times, booleans, colours, enums, ids, `image`-type custom fields.
|
||||
|
||||
**34. Preview is mandatory, because there is no undo anywhere in this app.** I checked — the client
|
||||
has no undo stack of any kind. Find-and-replace would be the first feature that can silently alter
|
||||
dozens of entries. So: show every affected entry with before → after per field, with a per-match
|
||||
opt-out, before anything is written. Confirmation names the count and the field: *"Replace 'Q' with
|
||||
'CUE' in title on 23 entries?"*
|
||||
opt-out, before anything is written. Confirmation names the count and the field: _"Replace 'Q' with
|
||||
'CUE' in title on 23 entries?"_
|
||||
|
||||
**35. A single-step revert.** Because the atomic endpoint gives one revision per operation, capturing
|
||||
the prior values client-side makes "Undo replace" a second batch call. Not an undo history — one
|
||||
@@ -256,7 +261,7 @@ moved. Prevents replacing text the user never saw someone else write. Cheap.
|
||||
|
||||
**38. Editor only for v1.** The cuesheet is `permission='operator'` and its URL presets can be
|
||||
read-only or column-restricted; a correct implementation would have to gate every field through
|
||||
`getCuesheetColumnAccessPolicy().canWrite(key)`. *Finding* is safe to expose broadly; *replacing* is
|
||||
`getCuesheetColumnAccessPolicy().canWrite(key)`. _Finding_ is safe to expose broadly; _replacing_ is
|
||||
not. Warn when playback is running rather than blocking.
|
||||
|
||||
**39. No regex.** Live operators, no undo history, and one bad pattern destroys a rundown.
|
||||
@@ -276,7 +281,7 @@ tests ~1d.
|
||||
- **Virtualising the result list.** A palette showing 400 rows isn't more useful than one showing 50;
|
||||
scanning is the bottleneck, not rendering. Cap and count instead.
|
||||
- **Implicit cross-rundown search.** Revealing an entry in another rundown means changing the
|
||||
*loaded* rundown — a destructive runtime action mid-show. Only the cuesheet has a non-destructive
|
||||
_loaded_ rundown — a destructive runtime action mid-show. Only the cuesheet has a non-destructive
|
||||
"viewed rundown" concept. If ever wanted, an opt-in `rundown:` filter there, never in the editor.
|
||||
- **The operator view and public viewers.** The operator view never calls `setScrollHandler`, so
|
||||
"go to entry" would silently do nothing until one is registered.
|
||||
|
||||
Reference in New Issue
Block a user