Skip to content

Release 23.31.0: DVD import, edit history, and exact frame rates - #33

Merged
tautcony merged 149 commits into
masterfrom
release/23.31.0
Oct 14, 2026
Merged

tautcony merged 149 commits into
masterfrom
release/23.31.0

Conversation

@tautcony

@tautcony tautcony commented Oct 13, 2026 •

Copy link
Copy Markdown
Owner

Summary

Add DVD VIDEO_TS import, branching edit history, and exact frame-rate handling.

  • Share undo/redo, history inspection, and preview/apply workflows across desktop and browser.
  • Preserve timestamps and clip edits; fix expression replay, editor focus, settings-save failures, and XML UIDs.
  • Add configurable shortcuts, improve logs and responsive layouts, and publish Core API reference docs.
  • Expand shared CI, browser tests, and package verification.

Compatibility

  • Core uses FrameRateValue; Node uses tagged frame rates and tick strings.
  • Apply content transforms before calling Serialize or Node serialize.
  • Scalar combine/append reject unknown durations and incompatible frame rates.

See CHANGELOG for the full release notes.

Validation

Checks passed for 4d99566a: PR CI, push CI, SonarCloud, and CodeFactor.

… and explicit inspection

- Introduced a new log tool design that prioritizes a compact, searchable list view, with an explicit inspector for detailed information.
- Added specifications for log export functionality, ensuring secondary actions for JSON and CSV exports while preserving the visible state.
… message clarity

- Removed the `MessageKey` property from `ApplicationLogEntry` and related logging methods.
- Updated `LogToolViewModel` to create log entries without relying on message keys.
- Simplified log messages in `StatusDiagnosticsPresenter` to provide clearer context.
- Adjusted localization files to remove obsolete log message keys and ensure consistency.
refactor: extract explicit path handling to a separate function in normalize-changed-text-files.py
chore: add DiscBinaryReadLimits class for defensive limits in binary reads
…improve context menu icons, and update UI bindings
- Introduced a unified shortcut action catalog with stable identifiers and default gestures.
- Added a dedicated "Shortcuts" tab in the settings tool for user-defined gestures, including validation for conflicts and persistence.
- Refactored ShortcutRouter to utilize the active mapping, ensuring legacy behavior is preserved.
- Enhanced ShortcutSettings to support normalization and validation of user-defined shortcuts.
- Implemented comprehensive tests for shortcut mapping, validation, and conflict detection.
- Updated localization resources for shortcut-related UI elements.
…tatements and removing unnecessary pragma directives
- Organized entries by version number and date, highlighting new features, problem improvements, and other enhancements.
- Ensured consistency in formatting and terminology throughout the document for better readability and understanding.
…xpression status

NEW-06: setLocalStorage lost its try/catch. A failed localStorage.setItem now
throws JSException again. Restore the guard so a blocked or full storage
silently keeps the previous settings.

NEW-07: AppendMplsAsync set IsBusy before the content-session guard. An early
return leaked IsBusy and disabled save, reload, refresh, edit, and combine.
Resolve the content session before the operation owns the busy flag.

NEW-09: Use the dedicated Status.ExpressionApplyFailed key for a failed
expression apply. Add the key to the shared Avalonia locales and regenerate
the browser JSON resources.

NEW-10: WasmDialog.DisposeAsync may run after the JS runtime is gone. Catch
JSDisconnectedException so dialog teardown does not throw.

Verification: dotnet test tests/ChapterTool.Wasm.Tests --no-restore (93 passed).
scripts/.venv/bin/python scripts/localization/axaml-to-json.py --check passes.
node --check passes for every browser JS asset.
…s load

NEW-03: a settings document can store a JSON null shortcut value. System.Text.Json
then deserializes a null dictionary value, and Normalize and IsCleared read
gesture.Length. The desktop settings store catches only JsonException, so the
NullReferenceException escaped and aborted the whole settings load.

Treat a null gesture as invalid in Normalize. The action then keeps its catalog
default gesture. Treat a null gesture as not cleared in IsCleared.

Verification: the new SettingsMigrationTests case throws NullReferenceException
at ShortcutSettings.Normalize before the fix and passes after it.
dotnet test tests/ChapterTool.Infrastructure.Tests --no-restore (174 passed, 1 skipped).
…ew reattaches

NEW-11: MainView subscribed to the static SettingsToolViewModel.ShortcutsSaved
event in its constructor and unsubscribed in OnDetachedFromVisualTree. The
composition root keeps one MainView instance, so a detached and reattached view
never received later shortcut saves and the window kept the stale gesture map.

Track the subscription in one flag and subscribe again from
OnAttachedToVisualTree. Both handlers now tolerate repeated calls.

Verification: the new Headless test detaches and reattaches the view, then
asserts the subscription state and the resolved save gesture. The test fails on
the missing resubscription and passes with it.
dotnet test tests/ChapterTool.Avalonia.Headless.Tests --no-restore (151 passed).
NEW-08: NextUid used BitConverter.ToInt32(bytes) & int.MaxValue. That range
starts at 0, but Matroska requires a non-zero UID, and the result changed on
every export of the same content. StableHashCode became unused.

Seed one Random instance from the exported document content and draw each UID
from 1..int.MaxValue-1. One document always exports the same UIDs, different
content exports different UIDs, and no UID is zero.

Verification: the new determinism test fails on the random implementation and
passes on the seeded implementation. dotnet test tests/ChapterTool.Core.Tests
--no-restore (836 passed).
NEW-04: NavigateHistoryAsync handled "undo", "redo", and "navigate" with a
two-way ternary, so a restored history node reported "Edit redone". Map each
action to its own status key. "navigate" now uses History.RestoreApplied, the
same key the browser workspace uses.

NEW-05: HasSelection is SelectedEntry is not null, and two view bindings use it
for IsVisible. No path raised a change notification, so the bound visibility
kept a stale value. Notify HasSelection when the selection changes, when the
tree rebuild replaces the selected instance, and when Dispose clears it.

Verification: both new tests fail on the previous behavior and pass now.
dotnet test tests/ChapterTool.Avalonia.Tests --no-restore (299 passed).
NEW-01 review item. The reported workflow is a rounded frame display over ticks
that are not on a frame boundary. Committing that frame cell unchanged used to
rewrite the timestamp to the frame boundary and add a history node.

The workflow is already protected. CommitCellEditAsync compares the committed
text with the text captured at DataGridBeginningEdit and returns before any
command runs. The new Headless test proves the outcome on a real editor: a
23.976 fps chapter with 240 frames of frame text and a 10.000 s start keeps
100000000 ticks and the same history cursor after Enter.

Verification: the test fails at the expected tick value (100100000) when the
guard in CommitCellEditAsync is removed, and passes with it.
dotnet test tests/ChapterTool.Avalonia.Headless.Tests --no-restore (152 passed).
The portable exact API represents an unknown duration as a null durationTicks.
FromExactSet folded that null into TimeSpan.Zero, and ChapterSegmentService
then used the zero as a real segment offset. A combine or append of an entry
with an unknown duration reported success while the later chapters kept the
wrong start times.

- ChapterSet carries DurationIsKnown. Existing construction sites keep the
  known default.
- FromExactSet sets DurationIsKnown=false for a null durationTicks.
- EditableChapterDocumentAdapter preserves the flag in both directions.
- Combine and Append reject an entry with an unknown duration and report
  Structure.Invalid instead of a silent wrong offset.
- ExactImport now emits "0" for a known zero duration and null only for an
  unknown duration, which matches packages/chaptertool/src/exact-types.ts.

Verification: ChapterSegmentServiceTests shows the previous code returning a
successful combine with a zero offset. dotnet test ChapterTool.slnx
--no-restore (1621 passed, 1 skipped, 0 failed).
The Node package test pass/append cases need the wasm-tools publish, which this
host cannot run; the added TypeScript cases use the same fixture shape as the
existing duration cases.
@tautcony tautcony changed the title Release 23.31.0 Release 23.31.0: DVD import, edit history, and exact frame rates Oct 14, 2026
@sonarqubecloud

Copy link
Copy Markdown

@tautcony
tautcony merged commit bd1cff2 into master Oct 14, 2026
40 checks passed
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