Commit Graph
1021 Commits
Author SHA1 Message Date
skylar taylor-barrick b0c9d47725 Merge remote-tracking branch 'origin/main' into test/backfill-regression-tests 2026-10-23 12:09:54 -06:00
aiongg 0173abde8c CI: dev-profile gate, one test binary per crate, nextest, line tables only (#2235)
* Dev profile: line tables only for workspace crates, no debug info for dependencies

* xtask ci: dev-profile gate, nextest, tests without examples, wasm --keep-going

* One integration-test binary per crate

* xtask ci: run effectcraft-gpu's shared-device tests in one cargo test process

* xtask ci: run every stage and report all failures

* xtask ci: name the stage type (clippy type_complexity)

* xtask ci: run every test pass and report all failures

* cargo fmt
2026-10-23 12:59:51 -04:00
Christopher Linnett 0635649e42 fix: let a rebound Deselect All use Alt variants of Cmd+A and Cmd+Z (#2096)
The text editor's Select All and Undo arms ignored Alt, so a Deselect All
rebound to Cmd+Alt+A or Cmd+Alt+Z was taken as Select All or Undo while a
text edit was open.

The arms now match exactly the chords the editor owns (Cmd/Ctrl+A, and
Cmd/Ctrl+Z with or without Shift); their Alt variants fall through to the
Deselect All binding match. Chords the editor owns still win over a rebound
Deselect All, so Undo keeps working while typing.

Test: a UI kittest rebinds Deselect All to each Alt variant and checks that
Cmd+A and Cmd+Z still edit the text while the rebound chord ends the edit.
2026-10-24 00:03:10 +08:00
Dylan M. Taylor f6ef1a7906 Use chrono instead of hand-rolled UTC conversion (#2023)
* Use chrono for UTC/calendar conversion instead of hand-rolled copies

Replaces the hand-rolled civil_from_days / days_from_civil copies (log
timestamp, ISO metadata dates, Align Video to Data timestamp parsing, the
Numbers effect's date, the Home and Render Queue date columns, and the
benchmark report date) with chrono as a workspace dependency. Output formats
are unchanged and pinned by the existing tests. AGENTS.md now says time goes
through chrono.

Follow-up to storytold/cadcraft#1785

* Keep 5-digit years unprefixed; derive Numbers weekday from the same date

chrono's %Y writes a '+' before years past 9999, where the old code wrote
{y:04}. timestamp, iso_date, format_utc, utc_stamp and xtask's utc now write
the year with {:04} again and let chrono format the rest, so a far-future
clock keeps the plain zero-padded year.

date_from_days took its weekday from the raw input modulo while the
year/month/day fell back to the epoch for day counts outside chrono's range,
so an unrepresentable count printed a fallback date paired with the input's
weekday. The weekday now comes from the same resolved date, so both agree.

Tests cover the year-10000 timestamp and the out-of-range Numbers date.

* Keep the whole date in format_ago for five-digit years

format_ago cut format_utc to 10 bytes, so a year past 9999 lost its last
digit (10000-01-0). Take the date up to the space instead. Tested.

* Keep Numbers dates exact past chrono's range

date_from_days converted the day count by building a Unix timestamp and
calling DateTime::from_timestamp, which falls back to the epoch (and so
reads 1970-01-01, a Thursday) once the date leaves chrono's NaiveDate
range near year ±262143. Numbers' `value` slider is a plain scalar with
no range guard, so a keyframed count reaches that: Long Date of
100_000_000 showed "Thursday, January 1, 1970" instead of the real
"Monday, September 13, 275790" -- exactly the plausible unrelated-date
fallback the review rejects, and the derived weekday matched the stand-in
date, not the input.

Route it through the 400-year Gregorian cycle like pdfcraft 9d4cb3ca:
chrono converts the day within one cycle (num_days_from_ce of the rebased
rest, always in range) and the whole cycles are added back as years, in
i128 so i64::MIN/MAX can't overflow. chrono stays the only calendar
logic. The weekday still comes from the resolved date, so it always
agrees with it.

Tested: date_from_days_past_chrono_range pins the pre-chrono dates and
weekdays at/over chrono's bound on both sides of the 2000-01-01 epoch
(and that the extremes don't overflow); numbers_formats adds the same
through the public numbers_text entry (Long/Short Date). cargo test
-p effectcraft-effects, fmt --check and clippy -D warnings all clean.

* time: preserve out-of-range years instead of the epoch

`DateTime::from_timestamp(..).unwrap_or_default()` changed the existing output:
inputs the old hand-rolled civil-from-days converted (e.g. `10_000_000_000_000`
s → 318857-05-20 17:46:40) collapsed to the epoch. Restore the old conversion
domain with a cycle-based helper: reduce by whole 400-year Gregorian cycles
(146_097 days), convert the in-range remainder with chrono, and carry the year
back as i128 (saturating to the output type). Only the year leaves chrono's
range; month, day and time of day stay exact. chrono remains the only calendar
logic (NaiveDate::from_num_days_from_ce_opt); no new features.

Sites (UTC):
- crates/engine logging.rs `timestamp` (u64 ms): far-future clock keeps its year.
  10e12 s → "318857-05-20T17:46:40.000Z", u64::MAX ms → "584556019-04-03T14:25:51.615Z"
  (was "1970-01-01...").
- crates/engine media_browser.rs `iso_date` (u64): 10e12 s → "318857-05-20T17:46:40Z",
  u64::MAX → "584554051223-11-09T07:00:15Z".
- crates/ui-egui panels/home.rs `format_utc` (i64): 10e12 → "318857-05-20 17:46",
  i64::MIN → "-292277022657-01-27 08:29".
- crates/ui-egui panels/render_queue.rs `utc_stamp` (u64): 10e12 → "318857-05-20
  17:46:40", u64::MAX → "584554051223-11-09 07:00:15".
- xtask bench.rs `utc` (u64): 10e12 → "318857-05-20 17:46 UTC", u64::MAX →
  "584554051223-11-09 07:00 UTC"; added a test.

* Name the 400-year range extension as the one exception to the time rule

The rule said never to hand-roll calendar math, but the range fix
(shifting by whole 400-year Gregorian cycles so dates past chrono's year
range keep their old output) is cycle arithmetic, so the rule and the code
disagreed. The rule now names that exception: the library still computes
month, day and time, and only whole cycles are carried in the year.

* Say the library computes month and day; time-of-day is plain arithmetic

Narrows the range-extension wording: the date library computes the
calendar fields, and splitting seconds into hours, minutes and seconds
is ordinary arithmetic, which is what the code does.
2026-10-23 11:29:09 -04:00
Christopher Linnett 0558c9427c fix: end an open text edit when Deselect All runs (#2096)
While a text layer was being edited, Edit > Deselect All and its shortcuts
selected every character (or did nothing) instead of ending the edit and
deselecting the layer.

- deselect_all now commits the open edit first, through the same route as
  text.endEdit: a created-but-empty type-tool layer is discarded, anything
  typed is kept, and no extra undo step is introduced.
- While the in-canvas editor holds the keyboard, the editor forwards the
  command's currently assigned shortcut (and any function-key chord) so
  Deselect All reaches the command from the comp; bare-letter bindings stay
  typing, and Select All still selects characters.
- The Project-panel focus redirects (invocation and menu enabled-ness) stand
  down while a text edit is open, so the menu and shortcut end the edit even
  when focus stayed on Project after Layer > New > Text.

Tests: three engine cases (commit + discard semantics), six UI kittest cases
(chords, F2, rebinding, menu enabled-ness, Project focus), RED at base.
2026-10-23 23:23:59 +08:00
Zaxaerith 9c0597fbfd fix(ui): add Duplicate to project item context menu (#2207) 2026-10-23 22:45:35 +08:00
Marco Arnulfo 24e61ce273 Composition Settings: the Resolution line shows the frame size the renderer produces (#2167) 2026-10-23 16:34:22 +02:00
Marco Arnulfo c250150dbc Composition Settings: Resolution menu with the frame size and memory at that resolution (fixes #2167) 2026-10-23 16:21:56 +02:00
Dylan M. Taylor 4b48482e8f ui: bound queued synthetic input gestures (#2197)
* ui: bound queued synthetic input gestures

* ui: reserve exact click event count
2026-10-23 08:45:10 -04:00
skylar taylor-barrick 8fe2f51fb0 #979: cover raster footage and a selection elsewhere in the vector-menu test
A raster footage layer's Timeline menu now also has to leave out Create Shapes from Vector Layer. The viewer check keeps the solid selected, so the menu has to follow the layer under the pointer rather than the selection. With the viewer's caps merge made a no-op, the test fails at the viewer check.
2026-10-22 19:13:31 -06:00
Marco Arnulfo 952b04322e Project Settings: Frame Count, Start at 0 or Start at 1 (part of #1807)
file.projectSettings takes frameStart (0|1), the Project Settings dialog shows Frame Count, and scripts get app.project.framesCountType and displayStartFrame. The setting was already read by the Frames and Feet + Frames time display; nothing could change it.
2026-10-23 03:07:01 +02:00
skylar taylor-barrick 7c1183df56 #1497: test that double-clicking an effect property name reveals it in the timeline
Double-clicking a property name in Effect Controls did nothing. The test double-clicks the name and checks the Timeline marks the property revealed and shows its row. Without the fix nothing is revealed.
2026-10-22 18:55:49 -06:00
skylar taylor-barrick 841ff7bc4b #354: test that the panel focus outline is drawn over the panel bodies
The focused panel's outline was painted before the panel body, so body fills hid it. The test finds the outline in the painted shapes and checks no filled rect painted after it reaches its one-pixel ring. Without the fix a body fill overlaps the ring.
2026-10-22 18:55:49 -06:00
skylar taylor-barrick d87a6940f4 #979: test that layer context menus offer Create Shapes from Vector Layer
Right-clicking vector footage in the Timeline or the Composition viewer offered no vector conversion. The test imports an SVG and checks the item is absent for a solid, present on the SVG layer in both menus, and converts it. Without the fix the menu item is missing.
2026-10-22 18:55:49 -06:00
Markus Ang 58d00e78d5 #2034: fit translated Settings page names 2026-10-22 20:12:36 -04:00
Markus Ang 8ac7b3f508 #2135: reject non-ASCII thumbnail text safely 2026-10-22 19:29:42 -04:00
zitann2020andDeepSeek V4.1 Flash 0cf7324d7c i18n(ja): AE says 縦横比, not アスペクト比
The Composition Settings dialog draws 「ピクセル縦横比」 and 「フレーム縦横比」, and its checkbox reads
「縦横比を 1:1 (1.00) に固定」. Three existing rows said アスペクト比.

Co-Authored-By: DeepSeek V4.1 Flash <noreply@deepseek.com>
2026-10-23 05:10:29 +09:00
Christopher Linnett 174f401fe1 timeline: pick layer-valued properties from a layer drop-down (#2093)
The Timeline drew a layer-valued property (an Environment light's Source, a
Displacement Map's layer parameter) as read-only text holding the raw layer
id, so a light's Source could not be chosen in the UI at all - only through
prop.set from the command line, scripting or MCP. Effect Controls already
had a working drop-down for exactly these values: extract it into one shared
widgets::layer_picker (the current entry, "None" first, then "N. name" for
every layer of the comp, the list built only while the popup is open) and
draw it from both panels.

In the Timeline the pick writes through the same set closure as every other
value arm - an undoable prop.set (layer id, or null for None) - and
registers timeline.prop.<uid>.value like its neighbours, so the row names
the layer instead of exposing its id. Effect Controls keeps its rect,
automation id and write; its closed dropdown's "None", previously a
hardcoded literal, now follows the interface language. The dead "Layer {n}"
id text leaves the i18n catalogs with it.

ui_layer_source_picker drives both panels headlessly (open, pick, undo,
dismiss, current-entry highlight, translated None, picking None to clear a
set value), and an ignored wgpu snapshot test keeps the visual record
reproducible.
2026-10-23 01:47:55 +08:00
Madhav Agarwal 3306bf2296 ui: Spacebar hand pans timeline rows vertically (#2083) 2026-10-22 19:15:34 +05:30
Madhav Agarwal ffe3c913e1 ui: scrub hot text from the slider range (#2097) 2026-10-22 19:05:30 +05:30
Egon 5000 a59e7ad67f Timeline: assert the full search-row hierarchy for the elided Contents, add docstrings
The search test now pins every row (layer, Contents, group, path/fill/
Transform) so a future regression that re-adds the inner Contents row or
reorders the flattening fails loudly. Docstrings cover the functions the
diff touches (group_row, all, rec, push_group) for CodeRabbit's
docstring-coverage pre-merge check.
2026-10-22 14:59:25 +02:00
tylerjenningsw c601829be1 3D: drop the NewPrimitive variant's doc comment (#1734) 2026-10-21 14:57:06 -04:00
tylerjenningsw c21210a63a 3D: close the New 3D Primitive dialog when its project or comp changes; untouched dimensions take the type's defaults (#1734)
The draft records the project epoch and comp it was opened for. A script or the
control channel opening a project or activating another comp while it is open
closed nothing before, and OK added the primitive (and switched to Advanced 3D)
in whatever comp was active then, even in a new project reusing the comp id.
2026-10-21 14:51:41 -04:00
tylerjenningsw cb561a6a3d 3D: test the New 3D Primitive dialog from a blank comp (#1734) 2026-10-21 13:31:59 -04:00
tylerjenningsw f14a57fb3e 3D: Layer > New > 3D Primitive... dialog for a primitive's type, name and dimensions (#1734)
OK runs layer.newPrimitive, which switches a Classic 3D comp to Advanced 3D in the same undo step. The dimension fields are the Geometry Options the engine builds for the chosen type.
2026-10-21 13:31:58 -04:00
tylerjenningsw 83cd74c730 Light Settings: test which options each light type shows, and undoing Environment to Spot (#1588) 2026-10-21 13:14:46 -04:00
tylerjenningsw b9929c2d71 Light Settings: test Environment lights and the other types through the dialog (#1588) 2026-10-21 12:41:50 -04:00
tylerjenningsw 17d57a6705 Light Settings: list Environment and keep an existing light's type (#1588)
The dialog offered four hard-coded light types and reopened an Environment light as Spot, so OK turned it into one. It now lists every light type the engine has, keeps the light's own type, and shows Falloff and Shadows only for lights that have them.
2026-10-21 12:41:50 -04:00
arnav-hiwarkarandClaude Opus 5.5 506a5b327f Timeline: the disk-cache bar checks only the visible frames
- `Frames::disk_frames` takes a frame range instead of a frame count, still clamped to the existing 100,000-frame limit.
- The Timeline passes the visible range (`disk_bar_range`), including frames only partly in view, and clips the blue bar to the work-area row.
- Membership stays live: nothing is cached, so the bar updates as before when frames are written, evicted or promoted to RAM.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 21:47:18 +05:30
arnav-hiwarkarandClaude Opus 5.5 b48a77cf70 Preview: a zoom or resolution change evicts the other sizes' frames first
`crates/ui-egui/src/frames.rs`:

- The cache remembers the scale of the frame on screen (the active viewer's urgent request). Prefetch and passive viewers don't change it, so a second viewer at another zoom can't evict the active viewer's frames.
- RAM and GPU eviction take frames of other scales first, then fall back to the existing playhead-aware order.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 21:46:39 +05:30
Nikolai Nyegaard c839ae7598 Settings: list the languages alphabetically by their own name
- English (and Auto where offered) stays first as the source language and fallback; the rest sort by native name with accents folded, so Čeština is under C and Magyar under M; Cyrillic and CJK names follow the Latin ones
- The list used the registry order, which is the order the languages were added
2026-10-21 12:01:47 +02:00
Yura Izmailov d4f06b5a9d UI-FONT: cover the Chinese interface catalog with system fallback faces 2026-10-20 20:43:14 +02:00
tylerjenningsw 0d03863fa5 Merge main into fix/expression-editor-autofocus 2026-10-20 10:12:27 -04:00
Camille Moine 14453386ac Viewer: keep hidden AV layers' controls visible (#1769) 2026-10-20 14:46:05 +02:00
Camille Moine c7eb48dc3a Layer: reveal layer sources in the Project panel (#1809) 2026-10-20 13:59:01 +02:00
Camille Moine 8d887fd6da Layer: replace a layer's source with Alt-drag and Ctrl+Alt+/ (#991, #813)
Alt/Option-drag a Project item onto a Timeline layer, or press Ctrl+Alt+/ (Cmd+Opt+/) with layers and an item selected, to swap the layer's source. The layer keeps its name, timing, transforms, keyframes, expressions, effects, masks, switches, parenting and track matte; a source with audio adds Audio properties when missing. Precompositions can't be nested inside themselves.

AVLayer.replaceSource now runs the same engine command and closes the replaceSource part of #813.
2026-10-20 12:18:46 +02:00
dedandClaude Opus 5.5 ac5799bd26 G5: the playing prefetch window starts at the first uncached frame, ahead × 3 wide
Looking up to the whole RAM preview for uncached frames let the workers spread over the preview
while the playhead waited. On the GPU each worker then built a Classic 3D atlas for its own set
of layers, and on a cold start of the #1812 project the device ran out of memory far more often
(first pass 1-56 % of real time instead of 63-81 %). The window now keeps its old width but
starts at the first frame ahead that isn't cached, so a loop or a restart still renders on at
once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 11:43:30 +03:00
SBaksa f41aa134a8 Text Range Selector test: change Units through the Timeline popup (#1814)
The Index-units test now opens the Units popup in the Timeline and picks
Index, asserts the stored Units value, then reads the painted Start, End
and Offset, so it also fails when the Units control doesn't update the
session.
2026-10-20 12:09:33 +11:00
dedandClaude Opus 5.5 ff20f8ec41 G5: a playing preview prefetches past the frames it already cached
The playback prefetch only looked ahead*3 frames (96 with 16 workers) for uncached frames.
After a loop or a restart from the start those were all cached, so the workers stood idle until
the playhead came within that window of the end of the cached run, then the preview stalled.
A playing preview now looks as far ahead as the RAM preview holds (and the range spans) for
the next uncached frames, still queuing at most ahead at a time; eviction already spares the
frames the playhead reaches first (#595).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 03:43:35 +03:00
Egon 5000 ce928a66da Merge remote-tracking branch 'origin/main' into ui/shape-group-elided-contents
# Conflicts:
#	docs/ui-parity.md
2026-10-19 23:00:22 +02:00
Egon 5000 89eaac0489 Merge remote-tracking branch 'origin/main' into menu-bar-native-behaviour
# Conflicts:
#	crates/ui-egui/src/panels/timeline.rs
2026-10-19 22:59:07 +02:00
TimotheusG d6afa87641 fix(text): isolate CJK fallback language and cache entries (#1141) 2026-10-19 15:30:35 -04:00
Davit MatchakhelidzeandClaude Opus 5.5 4d62e21e90 Merge i18n-improvements (CodeRabbit fixes)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YMTAbGNRxnMEzRvYnQGphF
2026-10-19 18:15:46 +04:00
Davit MatchakhelidzeandClaude Opus 5.5 ad8db289cf i18n: Settings page titles read the menu bar's Settings rows
The panel catalog's \"Type\" is the kind (Camera Settings, the Project column), so
the Type page drew as \"kind\" in Chinese; its title now comes from the
app.settings menu row (文字), as the Settings submenu draws it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YMTAbGNRxnMEzRvYnQGphF
2026-10-19 18:15:35 +04:00
Davit MatchakhelidzeandClaude Opus 5.5 60dab98e9b i18n: saved workspace names stay verbatim in the workspace menu
Only the built-in workspaces read the Window ▸ Workspace rows; a name the user
saved (\"Composition\", say) no longer falls through to the panel catalog.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YMTAbGNRxnMEzRvYnQGphF
2026-10-19 18:15:35 +04:00
SBaksa 71aafd6307 Text Range Selector: test the Timeline shows Index-unit values without % (#1814)
Start, End and Offset are read as painted in the Timeline: with % under
Units = Percentage, without it under Units = Index. Fails on the old
suffix rule ("0.0%").
2026-10-20 01:05:15 +11:00
dorfee 80158bb69b ScriptUI: install scheduled task runner in host sessions 2026-10-19 16:45:39 +03:00
SBaksa c17f9eb959 Text Range Selector: Start, End and Offset show as counts with Units = Index (#1814)
With Units set to Index the selector already counts characters (words,
lines), but the timeline kept showing Start, End and Offset as
percentages ("3.0%"), so switching looked like it did nothing. They are
now shown without the % sign while Units is Index
(render::text::in_index_units).
2026-10-19 21:08:16 +11:00
Camille Moine 8756ec1846 Timeline: Preserve Transparency toggles from the clicked row (#1849)
layer.setSwitch with no value toggles relative to the first selected layer in
composition order, so with a mixed selection (A checked, C unchecked) clicking
C's box could leave C unchanged. The checkbox now sends the value it toggles
to, as the switches do.
2026-10-19 10:25:17 +02:00
Camille Moine 16d43346c4 Timeline: the label swatch and the Modes column act on the selection (#1849)
The label swatch's menu, the Blending Mode menu and Preserve Transparency
passed the clicked layer alone, so a colour or a mode had to be set one layer
at a time. They now act on the whole selection, with the Modes column edits
going only to the selected layers that have video, as its cells do.

The preserve-transparency checkbox also gets an automation id, like the cells
around it.
2026-10-19 09:55:24 +02:00