Commit Graph
1005 Commits
Author SHA1 Message Date
bflatasticandClaude Opus 5.5 5b05a5ae80 Viewer: arrow keys nudge the selected layers 1 pixel at the current magnification (#290)
The arrow keys did nothing to a selected layer, so it couldn't be lined
up precisely. As in After Effects, over the Composition panel or the
Timeline an arrow key moves the selected layers 1 pixel at the viewer's
magnification (half a comp pixel at 200 %, so sub-pixel steps come from
zooming in) and Shift+arrow 10, one undo step per press; holding the
key repeats. Not while typing or with mask points selected.

The move is the new engine command layer.nudge {x, y (comp pixels),
layers?}: through each layer's parents into Position (or X/Y Position),
a key at the current time when Position is animated, locked layers left
alone. It shares the Align panel's move.

Tests: nudge_moves_layers_by_comp_pixels (engine),
arrow_keys_nudge_the_selected_layer_at_the_viewer_magnification
(ui_viewer).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 17:20:43 +07:00
bflatasticandClaude Opus 5.5 e6fd1461d7 Timeline: the bar past a layer's source is striped; time remapping it ends at the source's end (#290)
A footage or precomp layer dragged out past its source's end holds its
last frame, but its bar looked the same all the way, so the source's
end couldn't be seen. That part of the bar (and any part before the
source's start) is now darkened and striped in the label colour, with
an automation id (timeline.layer.<id>.bar.afterSource / beforeSource);
with time remapping on, the remap decides what shows there, so it isn't.

Enable Time Remapping on such a layer put its end key at the extended
out point; it now ends at the source's end, where the last frame starts
to hold (After Effects). Trimmed and reversed layers keep their keys.

Tests: time_remap_on_an_extended_layer_ends_at_the_source_end (engine),
the_bar_past_the_source_end_is_striped (ui_timeline_switches).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 17:20:42 +07:00
bflatasticandClaude Opus 5.5 567a947fd8 Timeline: deleting Time Remap turns time remapping off (#290)
Selecting Time Remap in the Timeline (its name, which selects all its
keys) and pressing Delete removed the keys, and a second Delete then
deleted the whole layer. As in After Effects, Edit > Clear with Time
Remap selected now turns time remapping off (Layer > Time > Enable Time
Remapping off), in one undo step, and the layer stays; one selected
Time Remap key on its own is still deleted as a key.

Test: deleting_time_remap_turns_time_remapping_off (engine).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 17:12:22 +07:00
bflatasticandClaude Opus 5.5 56b374800d Masks: an open mask leaves its layer whole until it is closed (#290)
While a mask was being drawn with the Pen, its open path was filled as
if closed, so the layer went black except inside the points placed so
far, which made rotoscoping by eye impossible. As in After Effects, an
open mask makes no transparency: the layer shows whole until the path
is closed. Effects that take a mask as a path (Stroke and the others)
still get open masks.

Test: open_masks_make_no_transparency (render).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 17:12:22 +07:00
bflatasticandClaude Opus 5.5 bf09a4fcad Expressions: the pick whip inserts at the cursor while editing (#284)
Dragging an expression's pick whip always replaced the whole
expression, also while it was being edited (and the editor then wrote
its old text back over the reference). As in After Effects, a pick
whip pressed while the expression is being edited now puts the
reference at the cursor, replacing the selected text, and the editing
goes on with the cursor after the reference. Without an edit in
progress it replaces the expression as before.

`prop.pickWhip` takes the text being edited (`expression`) and the
selection in it (`range`, in characters) to insert there.

Tests: the_expression_pick_whip_inserts_at_the_cursor_while_editing
(ui_inline_edit), pick_whip_inserts_into_the_expression_being_edited
(engine).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:59:54 +07:00
bflatasticandClaude Opus 5.5 02bc2b8f9a Puppet: the tools stay on the rigged layer and pins go on the deformed mesh (#284)
With a Puppet tool, a click off the pins picked the topmost layer under
the pointer by its own bounds, and selected it. So a click where the
deformation had stretched the mesh past the layer's bounds went to the
layer behind (selecting it and giving it a Puppet effect), and any
click on another layer switched to it.

While selected layers have a Puppet effect, a click (or a press that
might start a pin marquee) now works only on them, as in After
Effects: on the layer whose mesh, as deformed now, is under the
pointer (the pin lands on the deformed shape), else the one whose own
art is (a new mesh); elsewhere nothing happens and the selection
stays. Without a rigged layer selected, the tools pick and rig the
layer under the pointer as before.

`puppet::contains` tests a point against a deformed mesh; `puppet.addPin`
uses it too (its own test only held for one triangle winding).

Test: puppet_pins_go_on_the_deformed_mesh_and_the_tool_stays_on_the_rigged_layer
(ui_viewer).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:59:53 +07:00
bflatasticandClaude Opus 5.5 065f0c91e7 Viewer: the eyedropper samples under the pointer in a region of interest (#284)
Effect eyedroppers (other than keyers', which pick from the effect's
input) sample the composite the viewer shows; a test covers that on
main. But they, and the Info panel, read the shown frame as if it
covered the whole comp. With a Region of Interest, or the Extended
Viewer of a 3D view, the frame covers only that area, so they read a
pixel somewhere else. They now map the point through the area the
frame covers (`shown_region`, `frame_pixel`).

Tests: the_eyedropper_samples_under_the_pointer_in_a_region_of_interest
(ui_effect_controls), eyedropper_samples_straight_colour (fx_widgets).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:59:53 +07:00
bflatasticandClaude Opus 5.5 cf7263d6c6 Graph Editor: a handle drag moves the handles of every selected key (#284)
Dragging a Bezier handle in the Graph Editor changed only that key's
ease. As in After Effects, the same side's handles of every selected
key now move with it, by as much as the pointer moved (time and value
or speed alike), in one undo step. Each handle keeps its offset from
where the drag began, so a handle stopped at a limit (100 % influence)
doesn't drag the others on.

Test: a_graph_handle_drag_moves_the_handles_of_every_selected_key
(ui_keyframes).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:59:53 +07:00
bflatasticandClaude Opus 5.5 04ccf6f906 Timeline: keyframes without a dark outline (#284)
Each half of a keyframe glyph had a 1 px near-black border, so keys
that overlap read as a tangle of borders. Keys are now flat, as in
After Effects: each half is filled and edged in the key's own colour
(the hairline closes the seam between the halves), so overlapping keys
read as one shape. The Light theme's keyframe grey is darker so
unselected keys still stand out on its light time graph without the
border.

Test: keyframes_are_drawn_without_a_dark_outline (icons).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:59:53 +07:00
bflatasticandClaude Opus 5.5 59c55e8a26 Timeline: dragging over stopwatches sets them all (#284)
As in After Effects, pressing a property's stopwatch toggles it, and
dragging on over the stopwatches above or below gives every property
the pointer passes the state the first one got (animated or not),
whatever its own, in one undo step. Alt+click still adds an
expression.

The gesture is the layer switches' press-and-drag from #238, moved
into `switch_gesture` and shared by both. `prop.toggleAnimation` takes
`merge` so the drag folds into one undo step.

Tests: dragging_over_stopwatches_sets_them_all (ui_timeline_switches),
stopwatch_toggles_with_a_merge_key_are_one_undo_step (engine).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:59:53 +07:00
bflatasticandClaude Opus 5.5 2eb56cff1c Timeline: a test that the cached frames show in green (#284)
#284 reported no cache indication in the Timeline. Main has one: a
green line under the work area bar for the frames cached at the
viewer's resolution, view and options (blue for disk-cached frames).
This test pins it down: once the viewer's frame has rendered, the
Timeline paints the green line from the start of the work area.

Test: the_timeline_shows_the_cached_frames_in_green (ui_ram_preview).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:59:53 +07:00
bflatasticandClaude Opus 5.5 976784b0a6 Timeline: Shift-clicking layer bars selects the range (#284)
Shift-clicking a layer's name selected the layers from the last one
clicked to it, but Shift-clicking a layer bar in the time graph only
added that layer. Bars now select the same range (shared with the
name column: `shift_range`), and a click on a bar becomes where the next
range starts, as a click on a name does.

Test: timeline_shift_click_on_layer_bars_selects_the_range
(ui_workflow_selection).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:59:53 +07:00
bflatasticandClaude Opus 5.5 f9570d783a Viewer: scrubbing drops the viewer frames still waiting (#284)
Scrubbing asks for the viewer frame under the pointer at every step.
Each newer request only demoted the earlier ones to prefetch, so every
frame the pointer passed was still rendered, oldest first, holding the
frame threads the frame under the pointer needed. A newer viewer frame
of a comp now drops that comp's viewer frames that haven't started
(and releases them, so they can be asked for again). Frames already
rendering finish into the cache (rendering can't be interrupted
mid-frame), and the viewer keeps its last frame until the new one is
in. Prefetch frames and other comps' viewer frames stay queued.

Test: a_newer_viewer_frame_drops_the_ones_still_waiting (frames).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:59:53 +07:00
bflatasticandClaude Opus 5.5 2419605cc4 Timeline: a press in the time ruler moves the current time (#284)
The ruler moved the current-time indicator only once the pointer had
dragged past egui's drag threshold, or on release for a click. As in
After Effects, the time now jumps to the pointer as soon as the button
goes down in the ruler, then follows the drag.

Test: a_press_in_the_ruler_moves_the_current_time (ui_timeline_zoom).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:59:53 +07:00
bflatasticandClaude Opus 5.5 be926adb92 Render Queue: output files end in their format's extension once (#293)
Renders on macOS came out as `Comp 1.mov.ecproj`, and an image
sequence's every frame as `Comp 1_00001.png.ecproj`; another output
was `Comp 2.mov.mov`.

Cause: the save dialogs for anything but a project went through the
project dialog hook, which filters to `.ecproj`; macOS's save panel
appends the allowed extension to any other name. 0.4.0's Render Queue
Choose File… used it (#192 moved Output To to its own dialog since),
and Save Frame As, Save Current Preview, Export Lottie, Save a Copy As
XML, Collect Files, Watch Folder and the Premiere export form's Browse
still did. Nothing then fixed the name: Output To kept `.ecproj` (no
format claims it), and changing the format replaced only the last
extension (`Comp 2.mov.ecproj` → `Comp 2.mov.mov`). `[compName]` of a
comp named `Comp 2.mov` also doubled it.

Fix: `Hooks::save_dialog` asks the dialog for the default name's own
extension (`pick_save_file`), the project dialog only for projects or
on hosts without another (the web); the desktop dialog has no filter
for a name without an extension (folders). `OutputFormat::with_extension`
makes an output path end in the format's extension exactly once
(project extensions and repeats of the format's dropped, a missing one
added; `logo.png.mov` kept), used by Output To, format changes and
`resolve_output`, so existing projects and templates render to the
right name too. Output To picks the file type from the extension
before a stray `.ecproj`.

Test: output_names_end_in_the_format_extension_once (project),
output_to_ends_in_the_format_extension_once (engine) and
save_dialogs_offer_the_file_extension (ui); the engine and ui tests
fail with the fix reverted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:51:12 +07:00
bflatasticandClaude Opus 5.5 fc5771f00f Media: resampling footage audio guards a zero mix rate (#274)
A requested rate of 0 would divide by zero in the resampler; it now
reads through the plain path, which returns silence for it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:43:21 +07:00
BFlatandClaude Opus 5.5 928117db25 Fix #368: tflite rejects models whose scratch or work exceeds a budget
Model::read bounded each tensor but nothing else, so a tiny hostile
.tflite could make Model::run abort on a huge CONV_2D patch allocation,
or keep it busy for minutes. Read now works out from the static shapes,
with checked arithmetic, what one run takes and refuses a model over a
fixed budget: kernel side (32), one operator's working space (2^22
floats), multiply-adds (2^33), values written (2^30), floats alive at
once (2^29) and constant values decoded or laid out at load (2^27).

The shapes run() computes itself now have to match the declared ones
(PAD, CONCATENATION, MAX_POOL_2D channels), and a zero dimension no
longer lets the others grow without bound.

Reported by KazW, with the suggested load-time budget.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:33:32 +07:00
Nguyen Thanh Cong 581326ec9f Effects: Audio Spectrum uses linear frequency bands and amplitude 2026-10-09 16:27:29 +07:00
BFlatandClaude Opus 5.5 597f239c57 Fix #367: weighted_nms terminates on NaN or zero-area boxes
A degenerate detection box (any NaN coordinate, or zero area) has an
overlap of 0 with itself, so `top` was partitioned into the remainder and
the loop never shrank `dets`. `top` now always joins its own cluster, so
every pass removes at least one detection. Well-formed boxes behave as
before (their self-overlap is 1).

Reported with the fix by KazW.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:16:19 +07:00
7b632bdbc1 MCP: report render progress and handle cancellation
Keep serving during long renders, recover failed workers, and remove only files opened by the interrupted export. Cover progress, cancellation, EOF and output ownership with regression tests.

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 00:20:22 -07:00
storybored379andOpenAI Codex 164538fecb MCP: add core tools, annotations and resources
Add strict tool keys, resource reads and panic recovery with regression coverage. Keep existing documented tools listed.

Co-Authored-By: OpenAI Codex <noreply@openai.com>
2026-10-09 00:09:37 -07:00
heojunfoandClaude Opus 5.5 303056eec4 Export: AIFF 32-bit float writes AIFF-C fl32 instead of 24-bit (#317)
The Output Module offers 32 Bit Float for AIFF and the queue reports F32, but
audio_file silently wrote 24-bit PCM because plain AIFF has no float. Write
an AIFF-C file instead: FORM type AIFC, an FVER chunk (AIFC Version 1), and a
COMM chunk naming the fl32 compression type, with big-endian IEEE float
samples in SSND. 16- and 24-bit stay plain AIFF.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 15:33:04 +09:00
Nguyen Thanh Cong 101d1956ac UI: Draw panel focus outline after panel body to prevent clipping 2026-10-09 12:58:31 +07:00
Nguyen Thanh Cong b5da7a5754 UI: Commit text edit session when switching away from Type tool 2026-10-09 12:17:13 +07:00
Brandon ThomasandClaude Opus 5.5 cae67546dd Media: checked offset and as_chunks_mut in the stereo interleave (#305 follow-up)
clippy 1.99 flags chunks_exact_mut with a constant size; the skip offset
was also an unchecked slice.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 00:45:44 -04:00
Jonas NeubauerandClaude Opus 5.5 22ce74ea0d Fix NVIDIA Dust & Scratches threshold rounding (#211)
Squashed from #211 without its before/after PNG (we don't commit binary images).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 00:22:05 -04:00
Brandon Thomas 48dc1ec4e5 Merge pull request #200 from EthanSuquet/fix/text-scale-continuous-raster
Render: text layers are always continuously rasterised
2026-10-09 00:21:56 -04:00
Brandon Thomas 7434cfb798 Merge pull request #305 from storytold/fix/274-audio-stutter
Media: footage audio at another sample rate no longer stutters (#274)
2026-10-09 00:21:54 -04:00
Brandon ThomasandClaude Opus 5.5 e4d1a4494f G3: audio scrub test waits on the clock, not a step count (flaky under load)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 00:17:53 -04:00
Brandon ThomasandClaude Opus 5.5 742833bfa4 i18n: rustfmt the language table (#309 follow-up)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 00:15:20 -04:00
Brandon Thomas 1f5218378c Merge pull request #298 from midasdf/fix/vertical-japanese-punctuation
M9: place Japanese vertical punctuation using font metrics
2026-10-09 00:04:52 -04:00
Brandon Thomas f43b956382 Merge pull request #310 from DWAA1660/fix/285-empty-glyph-fallback
Text: fallback skips a font whose glyph for the character is empty (#285)
2026-10-09 00:04:50 -04:00
Brandon Thomas 2a968c4d0e Merge pull request #309 from FeiLiuEM/i18n/zh-hans
i18n: add Simplified and Traditional Chinese (zh-hans, zh-hant) UI translation
2026-10-09 00:04:49 -04:00
Brandon ThomasandClaude Opus 5.5 459c8987fe MCP: add the autosave _meta without IndexMut (#280 follow-up)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 00:04:47 -04:00
Brandon Thomas 6c838325fa Merge pull request #280 from jonassneu/fix/mcp-autosave
MCP: preserve unsaved work across server restarts (#258)

# Conflicts:
#	apps/effectcraft-cli/tests/cli.rs
#	contributors/people.toml
2026-10-08 23:51:21 -04:00
Brandon Thomas db4f6df89d Merge pull request #213 from midasdf/fix/type-tool-long-press
M9: open grouped tool flyouts on long press

# Conflicts:
#	ROADMAP.md
2026-10-08 23:50:55 -04:00
Brandon Thomas 12689aa288 Merge pull request #294 from pat-the-wizard/docs/261-environment-light
3D: layer.newLight lists the Environment light and how to set its source; parity notes reflections (#261)
2026-10-08 23:50:36 -04:00
heojunfoandClaude Opus 5.5 2cd7a36d66 Puppet: the first pin no longer turns the layer about it (#313)
With one Position pin and no Bend pins, every rotation and scale about the
pin is free in the ARAP energy. The 1e-9 regulariser (about 1e-12 after the
edge-length scaling) is too weak to hold them, so the CG solve turned
round-off into a visible rotation of several pixels, depending on where the
pin landed. The exact solution is the rest mesh translated by the pin's
offset, so deform() returns it directly in that case.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 12:34:00 +09:00
MindlessRuff 1b2c5d1944 fix(export): keep values above 1.0 in EXR output (#339)
The EXR sequence writer and comp.saveFrameAsExr clamped colour to 0..1 before the sRGB decode, so a 32 bpc project lost every over-range value. The lower bound stays at 0 and alpha stays clamped. Tests cover Rgba and Rgb sequences and saveFrameAsExr.
2026-10-08 20:22:49 -07:00
Brandon ThomasandClaude Opus 5.5 254192be57 G3: GL adapters pass the WebGL2-limits decline test (FreeBSD CI)
On FreeBSD the only adapter is llvmpipe over GL, which GpuContext::new
declines for its backend before checking limits; accept either reason.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 23:19:24 -04:00
FeiLiuEM d2f74c13e9 i18n: adopt the Chinese wording Adobe's own documentation settles
Reviewed #321 (the alternative Simplified Chinese catalog) row by row against Adobe's
Simplified Chinese After Effects documentation and kept what the documentation confirms:

- Adobe's "混合模式和图层样式" page names the blend modes verbatim, so 相乘 (Multiply), 相加
  (Add), 相减 (Subtract), 相除 (Divide), 动态抖动溶解 (Dancing Dissolve), 模板 Alpha /
  模板亮度 / 轮廓 Alpha / 轮廓亮度 (Stencil / Silhouette) and 冷光预乘 (Luminescent Premul)
  come from it. 滤色, 深色, 浅色, 实色混合 and 明度 stay, because that page gives those names too.
- "设置、选择和删除关键帧" gives 指数比例 (Exponential Scale), 消隐 (Shy), 对齐 (Snapping) and
  the 选定 wording; "预合成、嵌套和预渲染" gives 合成微型流程图; the motion-graphics templates
  page uses 基本图形 for the panel but 动态图形模板 for the template; the keyboard-shortcuts
  page gives 双立方 (Bicubic sampling), 灰度系数 and 主要.
- The 16 label colours now carry the 色 suffix the Chinese edition uses (浅绿色, 淡紫色, 桃红色,
  紫红色, 海泡沫, 深绿色), plus a few Adobe-standard phrasings: 置于顶层 / 置于底层, 与图层相同,
  色彩管理, Internet 标准 RGB, 另存为模板, 时间伸缩, 减少项目, 单独尺寸, 倾斜, 关.
- Kept mine where those pages contradict #321 (屏幕 for Screen, 较深的颜色, 较浅的颜色,
  纯色混合, 发光度, 资源管理器 for Finder, 整理工程 for Dependencies, 素材 for Material), where
  they drop the spaces Adobe's own pages use around Latin text (3D 图层, RPF 摄像机导入,
  在 Finder 中显示), and where the sibling apps' catalogs agree with this file.

`TRADITIONAL_CHINESE` mirrors the same changes (37 rows). Credit to @cjcwwww for #321, where
these rows come from.
2026-10-09 08:51:23 +08:00
FeiLiuEM 2b494b9cfc i18n: add Traditional Chinese (zh-hant) UI translation
- New TRADITIONAL_CHINESE catalog: the same (command, English label) keys as JAPANESE and
  SIMPLIFIED_CHINESE, one entry per fixed menu row (618 rows). The wording follows the
  Traditional Chinese catalogs PhotoCraft, PdfCraft, VectorCraft and LightCraft already ship
  (`檔案`, `開啟`, `儲存`, `圖層`, `遮色片`, `影格`, `預設集`, `快速鍵`, …).
- Match System maps a Traditional Chinese locale (`zh-TW`, `zh-HK`, `zh-Hant`, `zh_MO`) to
  `zh-hant` instead of English, now that there is a catalog for it.
- `simplified()` and `traditional()` replace the Simplified-only `chinese()`; the macOS Services
  item is 服务 in Simplified and 服務 in Traditional.
- `general.language` gains 繁體中文; `docs/preferences.md` documents the locale mapping.
- Tests: all three catalogs must cover the same fixed TREE entries; the shape check covers both
  Chinese catalogs; label/undo/panel-title/submenu/native-menu behaviour in zh-hant with file and
  layer names kept verbatim; the Traditional locale mapping; the preference round-trips.
2026-10-09 01:08:52 +08:00
DWAA1660andClaude Opus 5.5 8cb120d0ec Text: fallback skips a font whose glyph for the character is empty (#285)
Face::has_char counted any non-zero cmap entry as coverage. Some fonts map
whole scripts to empty glyphs (a decorative Latin font installed on a Linux
machine maps あ, 水, ア and 。 to glyphs with no contours), so once such a
face was loaded, fallback_for could pick it and Japanese text rendered as
nothing; cjk_falls_back_to_a_system_family failed whenever another test had
loaded that face first.

has_char (and file_covers, used for faces not yet loaded) now also require
the glyph to draw something: an outline with at least one segment, a colour
glyph, or a bitmap in any strike. Characters that are blank by nature
(whitespace, and the default-ignorable code points such as soft hyphen,
joiners and variation selectors) still count as covered by any mapping.
Face::glyph is unchanged, so shaping with the chosen face is as before.

Test: an_empty_glyph_does_not_cover_its_character builds a minimal TrueType
font in code (no font file added to the repo) mapping U+E000 to an empty
glyph and U+E001 to a triangle, and checks has_char and fallback_for.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K3vVft4VXvvy8SpbbUceTW
2026-10-08 16:48:13 +00:00
FeiLiuEM ed0a724c93 i18n: Simplified Chinese (zh-hans) UI translation
- The i18n module now selects a catalog by language code instead of testing for Japanese:
  `language(app)` returns the code, `lookup` is shared by every catalog, and the undo/redo
  and workspace / 3D-view titles are formatted per language.
- New SIMPLIFIED_CHINESE catalog: the same (command, English label) keys as JAPANESE, one
  entry per fixed menu row of the engine tree (618 rows).
- `general.language` gains 简体中文 (`zh-hans`). Match System maps a Simplified Chinese locale
  (zh, zh-CN, zh_CN.UTF-8, zh-Hans-CN, zh-SG) to it and a Traditional one (zh-TW, zh-HK,
  zh-Hant) to English, which has no catalog of its own yet.
- The macOS Services menu item is 服务 in Chinese.
- Tests: both catalogs must cover the same fixed TREE entries; `...`, brackets and spacing
  keep the English shape; label/undo/panel-title/submenu/native-menu behaviour in zh-hans
  with file and layer names kept verbatim; the locale mapping; the preference round-trips
  through prefs.json.
2026-10-09 00:42:28 +08:00
bflatasticandClaude Opus 5.5 ec892ef145 Media: footage audio at another sample rate no longer stutters (#274)
AAC audio in an MP4/MOV whose sample rate differs from the mix rate
(a 44.1 kHz clip in the 48 kHz preview or export, or the reverse)
stuttered and sounded low-pitched in previews and in exported movies.

Cause: FilmCraft's MP4 reader, asked for audio at a rate other than
the file's, applied the edit list's priming offset (1024 samples for
AAC) twice when it resampled: each read started about 1024 source
samples early and extrapolated a ramp over its first ~940 samples
(peaks near 100 against a 0.5 tone). Export and the preview mix ask
for audio a frame or a device buffer at a time, so the ramp repeated
dozens of times a second. Same-rate reads take FilmCraft's copy path
and were exact. FilmCraft fixed this upstream in 5bf2203, 87 commits
past the pinned revision.

Fix: MediaPool reads footage audio at the source's own rate and
resamples it itself (linear, by absolute sample position, so a range
comes out the same however it is split into reads), for every
container.

Test: resampled_audio_read_in_frame_pieces_matches_ffmpeg (44.1 kHz
AAC read at 48 kHz and 48 kHz at 44.1 kHz in 29.97 fps frame pieces,
against ffmpeg's decode: max error 44 before, 0.002 after) and
movies_carry_resampled_footage_audio (a 29.97 fps comp with 44.1 kHz
AAC footage exported to MP4/AAC and MOV/PCM, decoded by ffmpeg: max
error 8.7 before, 0.03 / 0.002 after).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 22:16:35 +07:00
BFlat d60cb71e29 Merge pull request #210 from jonassneu/fix/blank-panel-context-menus
G2: add context menus to blank Project and Timeline areas
2026-10-08 22:11:11 +07:00
DWAA1660 69310216fb Fix #263: explain property-group references in property commands 2026-10-08 08:49:42 -06:00
midasdfandCodex f224678541 M9: place Japanese vertical punctuation using font metrics
Co-Authored-By: Codex <noreply@openai.com>
2026-10-08 23:36:26 +09:00
BFlat 929a9c0f01 Merge pull request #278 from storytold/fix/259-fractal-soft-linear
Effects: Fractal Noise's Soft Linear has no lattice creases (#259)
2026-10-08 21:19:54 +07:00
bflatasticandClaude Opus 5.5 69dc1b26fb Import: a 3D model doesn't ask how to interpret its alpha (#264)
Importing a glTF / OBJ model opened Interpret Footage: the probe
reports straight alpha and the file has no alpha label, so with
Settings ▸ Import ▸ Interpret Unlabeled Alpha As at Ask User (the
default) the model was listed as unlabeled. Only image footage
(stills, sequences, video) can have unlabeled alpha; a model renders
its own, and audio and data have none.

Test: importing_a_model_does_not_ask_about_alpha (engine).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 21:18:52 +07:00