A filesystem without lock support returned an error from try_lock, which
read as 'held' and disabled crash detection for good.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
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.
`effectcraft-cli render --out proj/out/d.mp4 --project proj/x.ecproj` wrote
`proj/proj/out/d.mp4`: the Render Queue resolves a relative output against its own base, while
`--project` and every other CLI path are relative to the working directory. The CLI now makes
`--out` absolute against the working directory before queueing it, so the file lands where the
shell user expects; an absolute path is unchanged, and `rendered[].output` in the JSON result is
the absolute path. Render Queue outputs set inside a project (and `render --queue`) resolve as
before: tokens expanded, then `export.defaultOutputFolder`, the project's folder, the working
directory.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- 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.
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
- 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.
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>
`supported` was only called on desktop builds, so the wasm32 check
warned that it was never used. The browser build, which has no system
locale here, now resolves Match System through supported(None)
(English) instead of a literal.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Two measurements on Turbulent Basic: with one 16 px layer whose lattice
lines run through pixel centres, the slope across the lines relative to
the average slope (0.019 with the quintic fade, 0.75 now; must be over
0.5); and at the issue's settings, the gradient along the lattice axes
against the diagonals (1.21 before, 1.03 now; must be under 1.15).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Soft Linear was value noise with a quintic fade between two lattice
values per axis. That fade has zero slope across every lattice line and
at every lattice point, so the noise's contours met the lattice at right
angles and flattened around its points. The turbulent fold (1 - |s|) and
high contrast turned that into a visible square grid at each layer's cell
size (Turbulent Basic, Contrast 140, Brightness -35: gradients 1.3x as
strong along the axes as along the diagonals; Spline 1.1x).
Soft Linear is now linear interpolation softened by a one-cell box
filter, i.e. the uniform quadratic B-spline over the 3 x 3 lattice values
around the nearest one (evolution still fades between two slices): smooth
everywhere, with half the neighbours' difference as its slope on lattice
lines, and cheaper than Spline (9 taps instead of 16). Averaging
lowers the noise's spread, so a fixed gain about mid-grey restores the
quintic fade's variance and existing projects keep their contrast and
brightness (issue settings: mean 0.356 -> 0.362, std 0.147 -> 0.152,
axis bias 1.28 -> 1.05).
Only Fractal / Turbulent Noise's Soft Linear changes: generate's
value_noise and its other callers are untouched. noise3 now shares
generate's lattice hash and fade instead of copying them. The GPU
kernels (pointwise Fractal Noise and fxn_fractal) call the same
soft_linear_noise. No golden hashes pin these cases.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- release.yml: `freebsd` job (FreeBSD 14.3 VM, the freebsd.yml packages) running the new
packaging/freebsd/package.sh; `flatpak` matrix (x86_64 on ubuntu-24.04, aarch64 on
ubuntu-24.04-arm) repackaging the Linux tarball with packaging/linux/flatpak-bundle.sh and the
new ai.storyteller.effectcraft.bundle.yml; both added to the draft release's needs.
- AppImages embed gh-releases-zsync update information and ship a .zsync beside them; the Linux
job installs zsync and checks both.
- version, Linux and web jobs drop `environment: release` (they read no secrets), so a dispatch on
a branch is a full dry run of every unsigned job; the draft release still needs the signed jobs.
- Flatpak finish-args documented and shared by the bundle and Flathub manifests (lint checks they
match); audio, GPU, portals, xdg-pictures/videos/documents, network for the optional model
downloads. The Flathub manifest now also installs effectcraft-cli.
- packaging-lint runs the FreeBSD layout dry run.
- README: Downloads section (every file a release ships) and nav entry; docs/releasing.md updated.
Co-authored-by: Brandon Thomas <shared2@storyteller.ai>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The host test writes a binary glTF cube (built in the test), imports it
with the real media layer, makes New Comp from Selection and renders
with no accelerator (the CPU path an Intel HD Graphics 5500 most likely
gets): the comp is Advanced 3D and the red cube covers part of the
frame. Before the fix the comp stayed Classic 3D and drew nothing. The
engine test checks the switch, its single undo step, the toasts, that a
second model doesn't switch again, and primitives.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Imported GLB/glTF models were invisible in the Composition viewer while
text showed. Only the Advanced 3D renderer draws model and primitive
layers, and new compositions (and New Comp from Selection) are Classic
3D by default, but adding a model layer left the comp's renderer alone,
so the model drew nothing on every backend. It wasn't the reporter's
GPU: on this machine the viewer showed nothing for a GLB in a new comp
with the GPU compositor (Vulkan) and with Mercury Software Only, and
the model in both once the comp used Advanced 3D.
As After Effects documents ("Advanced 3D gets automatically selected
when you add a 3D model to the composition"), layer.newModel /
layer.addItem of a model and layer.newPrimitive now switch a Classic
3D comp to Advanced 3D inside the same edit (one undo step, Geometry
Options added to its text and shape layers like Composition Settings
does) and toast that the comp changed renderer. Switching a comp that
has model layers back to Classic 3D toasts why they disappear.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Files dropped on the window or picked in the Import dialog were probed
one after the other on the UI thread: the app froze while a video was
read, nothing said what was being imported or how many files were
left, and only the last new item was selected. People ended up
importing the same file again and again.
file.import takes `background`: the files are probed in a background
job (Window > Progress, jobs.list / jobs.cancel) that names the file
being read and how many there are ("b.png (2 of 3)"); then they are
added in one undo step, all selected (the Project panel scrolls to
them, a file already in the project arrives again as in After
Effects) and a toast reports the count ("Imported 3 items", with the
first error). Drops and the Import dialog use it, and an Importing
card at the bottom left shows the job with its progress bar and
Cancel. Agents and scripts still import at once by default. The
toast registers an automation id.
Test: background_imports_report_progress_and_select_what_arrived
(engine), dropped_files_show_import_progress_then_the_items_they_made
(ui_drag_drop).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Moving a puppet pin keys its Position, but the Timeline didn't show
those keys where people look for them. U revealed every pin's Position
as one more flat "Position" row under the layer, indistinguishable from
the layer's own Position and from each other, and the reveal was a
snapshot: a pin placed or moved after U (or with the layer twirled open
and Effects closed) got keys that no row showed.
As in After Effects, revealed properties inside an effect now show
under the effect and its groups (Puppet > Mesh 1 > Deform > Puppet
Pin 1 > Position), twirled open by the reveal and closable like any
group; Transform and mask properties stay as they were. Placing, moving
or recording a pin in the viewer keeps its Position in view: on a
twirled-open layer its groups open, and a U reveal takes it in. A
collapsed layer stays collapsed. The keys select, move and delete like
any others.
Test: puppet_pin_keys_show_under_their_pins_in_the_timeline,
placing_a_pin_opens_its_groups_on_a_twirled_open_layer (ui_viewer).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Two copies opened close together made the second take the first's
launch-pending marker for a crash and switch Window Graphics to OpenGL.
Where OpenGL can't open a window (NVIDIA on X11: "gl not compatible with
provided surface"), every later launch then failed, with no way to reach
the setting.
The marker is now locked while its launch runs, so a locked marker is a
launch still opening, not a crash. And a window that fails to open on
OpenGL puts Window Graphics back to Automatic.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#276 documents ["mcp", "--gpu"] and get_project's renderer.active / why in
the Headless setup notes of the same file, so the pitfall only repeated them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1RGTt6AE3ysiaMQ7X6Nea
The Environment light existed (LightKind::Environment) but layer.newLight's help
and the kind errors of layer.newLight and layer.lightSettings listed only
Parallel|Spot|Point|Ambient, so agents could not find it. The help now lists it
and says how to give it an equirectangular image: prop.set
lightOptions/source to that layer's id, or leave it empty for the comp's
Environment Layer (layer.environment). layer.lightSettings documents its kind
values too.
docs/parity.md (3D) notes that Advanced 3D surfaces reflect only the
Environment light and that Appears in Reflections is stored but has no visible
effect yet. Object-to-object reflections stay tracked in #261.
Test: tests_3d::environment_light_is_listed_and_takes_a_source_layer (help and
both kind errors name every LightKind; an Environment light takes a source
layer by id, with undo).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1RGTt6AE3ysiaMQ7X6Nea
The Learn workspace opens the Home screen on its Learn tab, but no other workspace closed it, so
after Learn the tutorials stayed over Default, Review and the rest until a project was created or
Home was toggled. It wasn't about clicking quickly: any switch away from Learn kept them. Leaving
Learn for another workspace now closes the Home screen it opened.
Test: ui_panels leaving_the_learn_workspace_closes_its_tutorials (clicks through the workspace
tabs: the Home screen and the tutorials show only on Learn).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Most panels lay out their own rows and scrolled only with the mouse wheel: Effect Controls,
Effects & Presets, the Media Browser, Ease Presets and the Timeline's layer rows had no scroll
bar, Properties and Preview a thin indicator that couldn't be dragged, and Render Queue, History,
Markers, Character, Paragraph and Info didn't scroll at all, so a panel shorter than its content
cut it off. Without a wheel or a working touchpad there was no way down. The Project panel had
the only real scroll bars, hand-rolled, and its vertical one was hit-tested under the rows, so it
only dragged where no row was.
- widgets::scroll_bar: the Project panel's bars generalised (horizontal or vertical, drag the
thumb or press the track to move it there, automation id with "scroll/max"); the Project
panel's two bars and the Timeline's column bar use it, drawn over the rows.
- widgets::PanelScroll: the wheel offset, its clamping and the bar for a panel's own rows, used by
Effect Controls, Properties, Effects & Presets, Media Browser, Ease Presets, Preview, Render
Queue, History (which still follows the current state), Markers, Character, Paragraph and Info.
The Timeline's layer rows get a bar on the right edge.
- Effect Controls: an animated vector's values (a puppet pin's Position) ran under the keyframe
navigator in a narrow panel. They now end before it, moving left over the name, which is cut
short.
Test: ui_scrolling panels_taller_than_their_space_have_a_scroll_bar_to_drag (eleven panels in a
short window: a press on the track and a thumb drag scroll them),
effect_values_stay_clear_of_the_keyframe_navigator.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The menu bar's own menus (Edit has 30 entries) had no scroll area, only their submenus did, so
on a short screen Edit ran past the bottom of the window. The font menu was capped at 420
points and never moved up, the Preview panel's and the viewer's popup lists neither scrolled nor
moved, and the Timeline's Blending Mode and Expression Language submenus didn't scroll. egui's
scroll bars also only appeared while the pointer was over the list, so a list could only be
scrolled with a wheel or touchpad.
- widgets::popup_list: the one popup list behind popup_menu, the font menu, the Preview panel's
dropdowns and the viewer's popups: as tall as the window allows, scrolling beyond, moved up to
fit the window.
- widgets::menu_scroll: the same for menus and submenus (the menu bar's, Blending Mode, the
Expression Language menu, the viewer's Select menu).
- Scroll bars always show (solid scroll style), so they can be dragged.
- A first window that doesn't fit the screen is maximized: the 1680 x 1020 default on a smaller
screen is only shrunk to the monitor's size, so with its title bar it ran under the taskbar and
cut menus off at the bottom of the screen even when they fitted the window.
Test: ui_scrolling a_menu_taller_than_the_window_scrolls (Edit in a 520-point window scrolls
its last entry into view and it takes the click); overflows_screen unit test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
get_project / render.backend reported "gpuAcceleration": true with
"active": "cpu" and no reason, which read as a broken GPU. The
headless MCP server only attaches the GPU compositor when started with
--gpu, which docs/agents.md didn't mention, and a failed --gpu said
only "no usable GPU adapter" (the adapter's error went to a log no
one sees). The session now keeps the host's reason there is no GPU
compositor (accel_note): the CLI's "headless: renders on the CPU
unless started with --gpu", or the desktop app's compositor setup or
retirement error. The status reports it as `why` whenever it renders
on the CPU (or that the project's renderer is Software Only). --gpu
failures include the adapter's error (Gpu::try_headless).
docs/agents.md documents `mcp --gpu`.
Test: render_backend_says_why_it_renders_on_the_cpu (engine).
Checked with effectcraft-cli mcp and mcp --gpu.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>