feat: range timeline#501
Open
FelipeDefensor wants to merge 8 commits into
Open
Conversation
b03c71c to
9b9e73d
Compare
This was referenced May 4, 2026
9b9e73d to
fc65dad
Compare
FelipeDefensor
commented
May 5, 2026
FelipeDefensor
commented
May 5, 2026
FelipeDefensor
commented
May 5, 2026
FelipeDefensor
commented
May 5, 2026
FelipeDefensor
commented
May 5, 2026
FelipeDefensor
commented
May 5, 2026
FelipeDefensor
commented
May 5, 2026
FelipeDefensor
commented
May 5, 2026
FelipeDefensor
commented
May 5, 2026
FelipeDefensor
commented
May 5, 2026
FelipeDefensor
commented
May 5, 2026
FelipeDefensor
commented
May 5, 2026
FelipeDefensor
commented
May 5, 2026
FelipeDefensor
commented
May 5, 2026
FelipeDefensor
commented
May 5, 2026
FelipeDefensor
commented
May 5, 2026
FelipeDefensor
commented
May 5, 2026
FelipeDefensor
commented
May 5, 2026
FelipeDefensor
commented
May 5, 2026
FelipeDefensor
commented
May 5, 2026
FelipeDefensor
commented
May 5, 2026
FelipeDefensor
commented
May 5, 2026
FelipeDefensor
commented
May 5, 2026
FelipeDefensor
commented
May 5, 2026
| title: str, | ||
| prompt: str, | ||
| kind: type(Timeline) | list[type(Timeline)] | None = None, | ||
| kind: type[Timeline] | list[type[Timeline]] | None = None, |
Collaborator
Author
There was a problem hiding this comment.
These bracket changes should be a separate commit.
Collaborator
Author
There was a problem hiding this comment.
Claude: Acknowledged — the type(Foo) → type[Foo] modernization is in the original feat: range timeline commit. I'll split it out at final-squash time.
FelipeDefensor
commented
May 6, 2026
FelipeDefensor
commented
May 6, 2026
FelipeDefensor
commented
May 6, 2026
FelipeDefensor
commented
May 6, 2026
FelipeDefensor
commented
May 6, 2026
FelipeDefensor
commented
May 6, 2026
FelipeDefensor
commented
May 6, 2026
FelipeDefensor
commented
May 6, 2026
95831cb to
6e5da18
Compare
FelipeDefensor
commented
May 6, 2026
FelipeDefensor
commented
May 6, 2026
FelipeDefensor
commented
May 6, 2026
Symmetric with the existing post() call in undo() — keeps listeners that depend on undo/redo events firing in both directions.
The fixture is auto-applied through qtui and routes settings reads and writes to a dedicated QSettings store, so production settings are never touched and tests don't need per-test save/restore wrappers. Tests should call settings.set(group, name, value) explicitly when they depend on a particular value — settings persist across tests within a module, so reading 'the current value' before mutating returns whatever the previous test left behind, not the default.
These short-form aliases for argparse subcommands have been deprecated for a while. Drop them so the help output stays focused on the canonical forms.
…SELECTED PEP 585 generic-alias syntax for the Timeline type annotations across TimelineUIs. Also fires Post.TIMELINE_UI_SELECTED on _add_to_timeline_ui_select_order / _send_to_top_of_select_order so listeners can react when the active timeline changes, and rewrites get_first_timeline_ui_in_select_order to use isinstance against the timeline subclass instead of looking up the now-removed KIND attr.
Without blockSignals, setText("") in clear_widgets fires textChanged →
INSPECTOR_FIELD_EDITED → state record. The phantom record discards the
redo stack, so any subsequent edit.redo finds nothing to redo.
Mirrors the pattern already in set_widget_value.
Add range timeline: a new timeline kind for annotating arbitrary time
ranges organised into rows. Ranges can be joined into chains, merged,
split, and extended with hierarchy-style pre-start / post-end whiskers.
UI surface:
- Element, timeline, and row context menus
- Ribbon-style toolbar grouped into Ranges / Rows / Display sections,
with split-mode dropdown
- Inspector with label, start, end, length, pre-start/post-end, comments
- Comments indicator on the body (visible when comments non-empty and
body is wide enough)
- Vertical arrow navigation; horizontal + vertical body drag (snaps
to row); copy/paste; full undo/redo
- Ctrl+Up / Ctrl+Down moves the selected range across rows; the same
chord is shared with hierarchy increase_level / decrease_level via
Post.TIMELINE_KEY_PRESS_CTRL_UP/DOWN
CSV import (by-time + by-measure):
- Required: start, end, row. Optional: label, color, comments,
joined_with_next, start_fraction, end_fraction
- Rows auto-created by name; joins validated after parsing
- Replaces existing rows; restores >=1 row invariant on empty import
CLI:
- timelines add range [--row-height N]
- timelines range row {add, set-height, list, rename, set-color,
reset-color, reorder, remove}
- timelines import range {by-time, by-measure}
File format:
- RangeTimeline.SERIALIZABLE: rows, default_row_height
- Range adds row_id, label, color, comments, joined_right, pre_start,
post_end, start_fraction, end_fraction
- Per-row Row.height (optional override)
- Label alignment is session-only (settings, not per-timeline)
Sample CSV fixtures live in TimeLineAnnotator/testing under
csv/test_ranges_by_*.csv.
README updated to 8 timeline kinds.
2ee4586 to
1e0c93b
Compare
Merged
FelipeDefensor
added a commit
that referenced
this pull request
May 6, 2026
- Backend ↔ frontend split: spell out that frontend constants and visual settings live under tilia/ui/, not tilia/timelines/<kind>/. - Code style: prefer ORDERING_ATTRS over redundant key= when sorting components; use logger.error + early return for stale-UI / invariant violations on production paths instead of raise. - New "Commit hygiene" section: split unrelated changes into their own commits; for pre-existing bugs in unrelated code, prompt for opening a separate PR rather than burying the fix in the feature branch.
2 tasks
azfoo
pushed a commit
that referenced
this pull request
May 7, 2026
- Backend ↔ frontend split: spell out that frontend constants and visual settings live under tilia/ui/, not tilia/timelines/<kind>/. - Code style: prefer ORDERING_ATTRS over redundant key= when sorting components; use logger.error + early return for stale-UI / invariant violations on production paths instead of raise. - New "Commit hygiene" section: split unrelated changes into their own commits; for pre-existing bugs in unrelated code, prompt for opening a separate PR rather than burying the fix in the feature branch.
azfoo
pushed a commit
that referenced
this pull request
May 7, 2026
- Backend ↔ frontend split: spell out that frontend constants and visual settings live under tilia/ui/, not tilia/timelines/<kind>/. - Code style: prefer ORDERING_ATTRS over redundant key= when sorting components; use logger.error + early return for stale-UI / invariant violations on production paths instead of raise. - New "Commit hygiene" section: split unrelated changes into their own commits; for pre-existing bugs in unrelated code, prompt for opening a separate PR rather than burying the fix in the feature branch.
…kind
`s` (split) and `e` (merge) are bound to both range and hierarchy
commands; Qt fired both with an "Ambiguous shortcut overload" warning.
`commands.register` now tracks shortcut bindings; `commands.setup_shortcuts`
strips colliding QActions and installs one application-level QShortcut per
chord that posts SHARED_SHORTCUT_FIRED with the bound names. TimelineUIs
listens and fires the command whose kind matches the most-recently-clicked
timeline (walks `_select_order`, matches names by the
`timeline.{kind}.{action}` convention).
A backstop in handle_qt_log_message surfaces "Ambiguous shortcut overload"
as AMBIGUOUS_SHORTCUT in case anything bypasses commands.register, and
non-timeline collisions through the dispatcher surface as the same error
rather than dropping silently.
…licates When every selected range has the same label (or comments), the merged range adopts that single value rather than concatenating identical copies with the configured separator.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds the range timeline — a new timeline kind for annotating arbitrary time ranges, organised into rows. Ranges can be joined into chains, merged, split, and extended with hierarchy-style pre-start / post-end whiskers.
What's in scope
UI
CSV import (by-time + by-measure)
start,end,row. Optional:label,color,comments,joined_with_next,start_fraction,end_fractionCLI
timelines add range [--row-height N]timelines range row {add, set-height, list, rename, set-color, reset-color, reorder, remove}timelines import range {by-time, by-measure}File format
RangeTimeline.SERIALIZABLE:rows,default_row_heightRangeaddsrow_id,label,color,comments,joined_right,pre_start,post_end,start_fraction,end_fractionRow.height(optional override)Stack
This branch sits on top of the cleanup stack (#497, #498, #499, #500) plus the media-commands refactor (#387). When those land on
dev, the merge commits drop on rebase via patch-id matching and the squash sits cleanly on top. No further history rewriting required here.Tests
tests/timelines/range/,tests/ui/timelines/range/,tests/parsers/csv/test_range_from_csv.py,tests/ui/cli/timelines/test_cli_timelines_range.pyandtest_cli_timelines_import.py