| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
refactor(server): split firecrawl.ts by layer; restore a zero-warning lint firecrawl.ts was 849 lines against the 500-line limit in AGENTS.md:19, with three layers stacked in one file. They are now three files plus the entry: firecrawl-shared.ts 222 proxy-aware fetch, JSON body I/O, URL checks, format mapping, screenshot persistence firecrawl-scrape.ts 255 the single-shot endpoints: scrape, search, map firecrawl-crawl.ts 305 the polling jobs: site crawl, batch scrape firecrawl.ts 108 route binding and the error envelope Splitting the handlers two ways rather than one keeps every file under the limit — submit-and-poll is a different shape from request-and-return, so that is where the seam belongs. firecrawl.ts still re-exports FirecrawlPluginOptions, buildActions, saveScreenshot and wrapExecJs, so firecrawl.verify.ts and any future caller import from the same place. Also fixes two lint warnings I introduced in 2991a24f: workspaceDialogs.tsx and dashboardDialogs.tsx each exported lazy components AND a prefetch hook, and react(only-export-components) is right that this defeats Fast Refresh for the file. The import thunks and the hook built on them moved to workspaceDialogLoaders.ts / dashboardDialogLoaders.ts, leaving the .tsx files exporting components only. npm run lint is back to zero warnings — I had not been running it on those commits, only tsc and the tests. Worth knowing: npx tsc --noEmit accepts imports that npm run build rejects. The build runs tsc -b, which picks up the server project's verbatimModuleSyntax and demands type-only imports for types. Three of the generated import lines here passed --noEmit and failed the build. Validation: tsc -b via npm run build, npm run lint (0 warnings), npm test (162 segments), firecrawl.verify, and all five dashboard dialogs opened in the browser after the module reshuffle. | 1 个月前 | |
feat(editor): add blurred background fill Persist the opt-in clip field through schema v5 migration while preserving older project documents. Keep preview and export compositing aligned, including effects, fades, and spatial GLSL transition alpha. FCPXML emits an explicit loss warning because the format cannot represent the companion layer. | 2 个月前 | |
Merge PR #123 (FlexCrop) — crop feature only, agent governor excluded Trial merge of J160KU's FlexCrop onto v0.2.12 for CI validation. Two things had to be resolved beyond ordinary conflicts. The PR does not build. npm run build runs tsc -b, and on the PR branch alone that fails four ways: two unused imports in systemPrompt.ts and a cast in read-project-tools-check.verify.ts that omits selectedIds/selectedId and then reads them. npx tsc --noEmit passes, which is presumably what was run. Fixed here by completing the cast; the systemPrompt errors disappear with the revert below. The PR also carries 192 lines of agent-behaviour change that its title and summary do not mention, so those are left out of this merge: - src/agent/inspect-loop.ts hooks guardFlexCropToolLoop into tools.ts, the central dispatcher, where it can short-circuit ANY tool call. It caps frame inspection at 6 looks for every agent run — its own comment notes caption/MG verification needs more — and injects "stop", "do not recheck frames", "do not call run_code" into the result stream. - src/agent/sandbox-unavailable.ts turns run_code/probe_media into {ok: true, skipped: true} when E2B is unconfigured. Reporting an unavailable capability as success is the shape of open issue #114, which is fixed on main. - frames-tool.ts raises the frame-sampling cap from 8 back to 16, undoing the hardening in aafac5dc. tools.ts, systemPrompt.ts, probe-tools.ts, run-code-tools.ts, known-errors/SKILL.md and frames-tool.ts are therefore taken from main, and the two new modules plus their verifies are dropped along with their entries in the verify:agent-tools script. tools.ts contained nothing else, so FlexCrop loses nothing by this. FlexCrop itself is sound: storage stays a 0-1 canvas fraction so no migration is needed, pixel conversion is isolated in src/editor/flexCrop.ts with its own verify, edit_item previously rejected transform.crop as an unknown field so no existing agent call changes meaning, and every new verify was registered in test:serial by the author. CHANGELOG keeps the released 0.2.12 section and files FlexCrop under [Unreleased]; the tool catalog was regenerated rather than hand-merged. Local: tsc -b, lint (0 warnings), npm test (162 segments) all pass. | 1 个月前 | |
fix(preview): stop silently dropping MGs and SFX the timeline still shows Two independent ways a clip could sit on the timeline, waveform and all, and simply not reach the viewer. Captions: the toolbar and the renderer each derived "are captions on" from an unset captionsHidden flag, and disagreed. The toolbar counted an enabled caption track OR any text/motion-graphic clip; the renderer counted only an enabled caption track. Since captions-off also hides on-screen text clips - and an MG counts as one as soon as its props carry a text or colour key - disabling a caption track silently hid every such MG while the CC button still read On. One MG with a label prop vanished; its neighbour, whose props are only glowColor/ringColor, kept rendering, which made it look like a broken template. Both sides now share captionOverlayVisible(). Audio: ASR run over non-speech audio invents words far outside the file ("Music" at 24.4s inside a 3.0s sting). AudioClip treats a transcript as an edit list and plays only those word windows, so every generated sound effect went silent. Reject a source window that starts past the media length (now passed down from the pool), and discard an untouched transcript that would swallow more than half the clip. Real word-level deletions still win. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> | 18 天前 | |
fix(editor): a split motion graphic continues its animation instead of restarting Splitting a motion graphic gave two clips that each played the template from frame 0: the second piece replayed the intro, and both faded out at their own ends, so a cut in the middle of an animation showed the opening scene again. Both pieces now keep the length of the whole design (designDurationInFrames) and play their own window of it from srcInFrame. The template's clock is shifted through the enclosing sequence, so useCurrentFrame and useVideoConfig see the design's time and length. A split piece trims like video: its start cuts into or back out of the animation, and its end stops at the design's last frame. Unsplit clips are unchanged. The split tool description and the motion-graphic skills say so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> | 12 天前 | |
fix(editor): serialize caption fonts and preserve backdrop borders | 24 天前 | |
test(preview): check the audio transcript guard through real code, and run both new checks The audio guard lived inline in AudioClip, so its check could only match the source text and re-implement the rule. Move the decision into a pure audibleTranscriptSegments() that AudioClip calls (same behaviour) and test it with real keptSegments output: a hallucinated word past the end of a sound effect, a single bogus word that would swallow most of a clip, real word deletions, normal speech, unknown media length, and a partly out-of-range transcript. Register audio-transcript-guard and caption-overlay-visibility in verify:editor so npm test runs them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> | 18 天前 | |
lint: zero warnings across the repo (27 remaining) Split non-component exports out of component files (6): historyGesture, timelineReadinessKey, folderDrop, clipMediaGeometry, composerSubmitGate, useComposerModelView. Fixed all exhaustive-deps warnings (21) with real fixes — field-level deps (state/status/props objects → the exact fields used), stable refs for effect-scoped cleanups, latest-ref for hover-clearing, memoized transitions lookup — never suppress directives. Plus the earlier 5: regex escapes in external-agent/multicam-tools, control-char checks rewritten per codepoint in media-dir/model-capabilities. npm run lint: 0 warnings. npm test 137 green, tsc -b + build green. | 2 个月前 | |
chore(release): prepare v0.2.0 Harden project storage, upload finalization, export recovery, watched-folder ownership, and native inference lifecycle boundaries. Add cross-platform desktop update packaging and checksum-gated GitHub Release publication while preserving the public v3 project schema, existing media URL semantics, and opt-in defaults. Verified with TypeScript project builds, zero-warning lint, the full test suite, production build, and a browser create/import/edit/reload/close-reopen smoke. | 2 个月前 | |
test(preview): check the audio transcript guard through real code, and run both new checks The audio guard lived inline in AudioClip, so its check could only match the source text and re-implement the rule. Move the decision into a pure audibleTranscriptSegments() that AudioClip calls (same behaviour) and test it with real keptSegments output: a hallucinated word past the end of a sound effect, a single bogus word that would swallow most of a clip, real word deletions, normal speech, unknown media length, and a partly out-of-range transcript. Register audio-transcript-guard and caption-overlay-visibility in verify:editor so npm test runs them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> | 18 天前 | |
test(preview): check the audio transcript guard through real code, and run both new checks The audio guard lived inline in AudioClip, so its check could only match the source text and re-implement the rule. Move the decision into a pure audibleTranscriptSegments() that AudioClip calls (same behaviour) and test it with real keptSegments output: a hallucinated word past the end of a sound effect, a single bogus word that would swallow most of a clip, real word deletions, normal speech, unknown media length, and a partly out-of-range transcript. Register audio-transcript-guard and caption-overlay-visibility in verify:editor so npm test runs them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> | 18 天前 | |
feat(editor): add percentage background fill | 2 个月前 | |
feat(editor): add percentage background fill | 2 个月前 | |
docs: translate code comments to English | 3 个月前 | |
docs: translate code comments to English | 3 个月前 | |
fix(preview): stop silently dropping MGs and SFX the timeline still shows Two independent ways a clip could sit on the timeline, waveform and all, and simply not reach the viewer. Captions: the toolbar and the renderer each derived "are captions on" from an unset captionsHidden flag, and disagreed. The toolbar counted an enabled caption track OR any text/motion-graphic clip; the renderer counted only an enabled caption track. Since captions-off also hides on-screen text clips - and an MG counts as one as soon as its props carry a text or colour key - disabling a caption track silently hid every such MG while the CC button still read On. One MG with a label prop vanished; its neighbour, whose props are only glowColor/ringColor, kept rendering, which made it look like a broken template. Both sides now share captionOverlayVisible(). Audio: ASR run over non-speech audio invents words far outside the file ("Music" at 24.4s inside a 3.0s sting). AudioClip treats a transcript as an edit list and plays only those word windows, so every generated sound effect went silent. Reject a source window that starts past the media length (now passed down from the pool), and discard an untouched transcript that would swallow more than half the clip. Real word-level deletions still win. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> | 18 天前 | |
Fix verified persistence, editor, and agent bugs | 2 个月前 | |
Merge PR #123 (FlexCrop) — crop feature only, agent governor excluded Trial merge of J160KU's FlexCrop onto v0.2.12 for CI validation. Two things had to be resolved beyond ordinary conflicts. The PR does not build. npm run build runs tsc -b, and on the PR branch alone that fails four ways: two unused imports in systemPrompt.ts and a cast in read-project-tools-check.verify.ts that omits selectedIds/selectedId and then reads them. npx tsc --noEmit passes, which is presumably what was run. Fixed here by completing the cast; the systemPrompt errors disappear with the revert below. The PR also carries 192 lines of agent-behaviour change that its title and summary do not mention, so those are left out of this merge: - src/agent/inspect-loop.ts hooks guardFlexCropToolLoop into tools.ts, the central dispatcher, where it can short-circuit ANY tool call. It caps frame inspection at 6 looks for every agent run — its own comment notes caption/MG verification needs more — and injects "stop", "do not recheck frames", "do not call run_code" into the result stream. - src/agent/sandbox-unavailable.ts turns run_code/probe_media into {ok: true, skipped: true} when E2B is unconfigured. Reporting an unavailable capability as success is the shape of open issue #114, which is fixed on main. - frames-tool.ts raises the frame-sampling cap from 8 back to 16, undoing the hardening in aafac5dc. tools.ts, systemPrompt.ts, probe-tools.ts, run-code-tools.ts, known-errors/SKILL.md and frames-tool.ts are therefore taken from main, and the two new modules plus their verifies are dropped along with their entries in the verify:agent-tools script. tools.ts contained nothing else, so FlexCrop loses nothing by this. FlexCrop itself is sound: storage stays a 0-1 canvas fraction so no migration is needed, pixel conversion is isolated in src/editor/flexCrop.ts with its own verify, edit_item previously rejected transform.crop as an unknown field so no existing agent call changes meaning, and every new verify was registered in test:serial by the author. CHANGELOG keeps the released 0.2.12 section and files FlexCrop under [Unreleased]; the tool catalog was regenerated rather than hand-merged. Local: tsc -b, lint (0 warnings), npm test (162 segments) all pass. | 1 个月前 | |
feat: land professional timeline, durable jobs, and safe media flows Add slip/rate-stretch, nested sequences, multicam sync, durable generation/export recovery, media intelligence, and hardened MCP/media boundaries while keeping the implementation local-first and cross-platform. | 2 个月前 | |
docs: translate code comments to English | 3 个月前 | |
fix(preview): text missing and half-screen video display (handoff from grok) Continuation of the grok session 019fd6aa (ran out of budget after writing the changes and running its two verifies). Worked tree changes were verified here: tsc -b clean, npm test full green, build ok, lint 0, dev-server smoke test ok. - Preview transform overlay: non-uniform clip scale axes (scaleX/scaleY) with keyframe sampling (clipTransformScale), edge drags survive uniform scale curves - Keyframe inspector + preview overlay wiring; text clips no longer disappear when transform keyframes are present - Media pool: folder context menu (right-click / ⋯), delete-empty-folder, navigation, drag-into-folder improvements - Agent: edit_item visual props surface scale axes; system prompt notes - TimelineComposition/layouts/types align clip transform resolution - New verifies: previewTransform, preview-transform-overlay, inspector-controls, media-folder-menu (all green) Companion revert f020684: normalize concurrency stayed 2/8 — the 4/16 raise made normalize-media.verify crash (20 hanging gate-held requests get connections closed). | 2 个月前 | |
fix(editor): a split motion graphic continues its animation instead of restarting Splitting a motion graphic gave two clips that each played the template from frame 0: the second piece replayed the intro, and both faded out at their own ends, so a cut in the middle of an animation showed the opening scene again. Both pieces now keep the length of the whole design (designDurationInFrames) and play their own window of it from srcInFrame. The template's clock is shifted through the enclosing sequence, so useCurrentFrame and useVideoConfig see the design's time and length. A split piece trims like video: its start cuts into or back out of the animation, and its end stops at the design's last frame. Unsplit clips are unchanged. The split tool description and the motion-graphic skills say so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> | 12 天前 | |
docs: translate code comments to English | 3 个月前 | |
feat: complete source capability parity | 3 个月前 | |
chore(release): prepare v0.2.0 Harden project storage, upload finalization, export recovery, watched-folder ownership, and native inference lifecycle boundaries. Add cross-platform desktop update packaging and checksum-gated GitHub Release publication while preserving the public v3 project schema, existing media URL semantics, and opt-in defaults. Verified with TypeScript project builds, zero-warning lint, the full test suite, production build, and a browser create/import/edit/reload/close-reopen smoke. | 2 个月前 | |
feat(media): support project files and DeepSeek vision Add DeepSeek vision capability metadata and preserve documents and generic files as project assets. Shared path impact: media import now accepts project-only files, while explicit timeline guards keep existing audio, video, and image editing behavior unchanged. Persistence normalization preserves both legacy and new asset kinds. Verification: live DeepSeek model listing plus text and image requests; browser import, reload, AI document attachment, and model discovery; npx tsc -b --force; npm run lint; npm run verify:affected; TEST_CONCURRENCY=4 npm test; npm run build. | 2 个月前 | |
feat(timeline): connect mixed caption and file workflows | 2 个月前 | |
Merge PR #123 (FlexCrop) — crop feature only, agent governor excluded Trial merge of J160KU's FlexCrop onto v0.2.12 for CI validation. Two things had to be resolved beyond ordinary conflicts. The PR does not build. npm run build runs tsc -b, and on the PR branch alone that fails four ways: two unused imports in systemPrompt.ts and a cast in read-project-tools-check.verify.ts that omits selectedIds/selectedId and then reads them. npx tsc --noEmit passes, which is presumably what was run. Fixed here by completing the cast; the systemPrompt errors disappear with the revert below. The PR also carries 192 lines of agent-behaviour change that its title and summary do not mention, so those are left out of this merge: - src/agent/inspect-loop.ts hooks guardFlexCropToolLoop into tools.ts, the central dispatcher, where it can short-circuit ANY tool call. It caps frame inspection at 6 looks for every agent run — its own comment notes caption/MG verification needs more — and injects "stop", "do not recheck frames", "do not call run_code" into the result stream. - src/agent/sandbox-unavailable.ts turns run_code/probe_media into {ok: true, skipped: true} when E2B is unconfigured. Reporting an unavailable capability as success is the shape of open issue #114, which is fixed on main. - frames-tool.ts raises the frame-sampling cap from 8 back to 16, undoing the hardening in aafac5dc. tools.ts, systemPrompt.ts, probe-tools.ts, run-code-tools.ts, known-errors/SKILL.md and frames-tool.ts are therefore taken from main, and the two new modules plus their verifies are dropped along with their entries in the verify:agent-tools script. tools.ts contained nothing else, so FlexCrop loses nothing by this. FlexCrop itself is sound: storage stays a 0-1 canvas fraction so no migration is needed, pixel conversion is isolated in src/editor/flexCrop.ts with its own verify, edit_item previously rejected transform.crop as an unknown field so no existing agent call changes meaning, and every new verify was registered in test:serial by the author. CHANGELOG keeps the released 0.2.12 section and files FlexCrop under [Unreleased]; the tool catalog was regenerated rather than hand-merged. Local: tsc -b, lint (0 warnings), npm test (162 segments) all pass. | 1 个月前 | |
Merge PR #123 (FlexCrop) — crop feature only, agent governor excluded Trial merge of J160KU's FlexCrop onto v0.2.12 for CI validation. Two things had to be resolved beyond ordinary conflicts. The PR does not build. npm run build runs tsc -b, and on the PR branch alone that fails four ways: two unused imports in systemPrompt.ts and a cast in read-project-tools-check.verify.ts that omits selectedIds/selectedId and then reads them. npx tsc --noEmit passes, which is presumably what was run. Fixed here by completing the cast; the systemPrompt errors disappear with the revert below. The PR also carries 192 lines of agent-behaviour change that its title and summary do not mention, so those are left out of this merge: - src/agent/inspect-loop.ts hooks guardFlexCropToolLoop into tools.ts, the central dispatcher, where it can short-circuit ANY tool call. It caps frame inspection at 6 looks for every agent run — its own comment notes caption/MG verification needs more — and injects "stop", "do not recheck frames", "do not call run_code" into the result stream. - src/agent/sandbox-unavailable.ts turns run_code/probe_media into {ok: true, skipped: true} when E2B is unconfigured. Reporting an unavailable capability as success is the shape of open issue #114, which is fixed on main. - frames-tool.ts raises the frame-sampling cap from 8 back to 16, undoing the hardening in aafac5dc. tools.ts, systemPrompt.ts, probe-tools.ts, run-code-tools.ts, known-errors/SKILL.md and frames-tool.ts are therefore taken from main, and the two new modules plus their verifies are dropped along with their entries in the verify:agent-tools script. tools.ts contained nothing else, so FlexCrop loses nothing by this. FlexCrop itself is sound: storage stays a 0-1 canvas fraction so no migration is needed, pixel conversion is isolated in src/editor/flexCrop.ts with its own verify, edit_item previously rejected transform.crop as an unknown field so no existing agent call changes meaning, and every new verify was registered in test:serial by the author. CHANGELOG keeps the released 0.2.12 section and files FlexCrop under [Unreleased]; the tool catalog was regenerated rather than hand-merged. Local: tsc -b, lint (0 warnings), npm test (162 segments) all pass. | 1 个月前 | |
feat(editor): add blurred background fill Persist the opt-in clip field through schema v5 migration while preserving older project documents. Keep preview and export compositing aligned, including effects, fades, and spatial GLSL transition alpha. FCPXML emits an explicit loss warning because the format cannot represent the companion layer. | 2 个月前 | |
docs: translate code comments to English | 3 个月前 | |
docs: translate code comments to English | 3 个月前 | |
feat: land professional timeline, durable jobs, and safe media flows Add slip/rate-stretch, nested sequences, multicam sync, durable generation/export recovery, media intelligence, and hardened MCP/media boundaries while keeping the implementation local-first and cross-platform. | 2 个月前 | |
feat: harden media captions and offline editing | 2 个月前 | |
fix(preview): text missing and half-screen video display (handoff from grok) Continuation of the grok session 019fd6aa (ran out of budget after writing the changes and running its two verifies). Worked tree changes were verified here: tsc -b clean, npm test full green, build ok, lint 0, dev-server smoke test ok. - Preview transform overlay: non-uniform clip scale axes (scaleX/scaleY) with keyframe sampling (clipTransformScale), edge drags survive uniform scale curves - Keyframe inspector + preview overlay wiring; text clips no longer disappear when transform keyframes are present - Media pool: folder context menu (right-click / ⋯), delete-empty-folder, navigation, drag-into-folder improvements - Agent: edit_item visual props surface scale axes; system prompt notes - TimelineComposition/layouts/types align clip transform resolution - New verifies: previewTransform, preview-transform-overlay, inspector-controls, media-folder-menu (all green) Companion revert f020684: normalize concurrency stayed 2/8 — the 4/16 raise made normalize-media.verify crash (20 hanging gate-held requests get connections closed). | 2 个月前 | |
Merge PR #123 (FlexCrop) — crop feature only, agent governor excluded Trial merge of J160KU's FlexCrop onto v0.2.12 for CI validation. Two things had to be resolved beyond ordinary conflicts. The PR does not build. npm run build runs tsc -b, and on the PR branch alone that fails four ways: two unused imports in systemPrompt.ts and a cast in read-project-tools-check.verify.ts that omits selectedIds/selectedId and then reads them. npx tsc --noEmit passes, which is presumably what was run. Fixed here by completing the cast; the systemPrompt errors disappear with the revert below. The PR also carries 192 lines of agent-behaviour change that its title and summary do not mention, so those are left out of this merge: - src/agent/inspect-loop.ts hooks guardFlexCropToolLoop into tools.ts, the central dispatcher, where it can short-circuit ANY tool call. It caps frame inspection at 6 looks for every agent run — its own comment notes caption/MG verification needs more — and injects "stop", "do not recheck frames", "do not call run_code" into the result stream. - src/agent/sandbox-unavailable.ts turns run_code/probe_media into {ok: true, skipped: true} when E2B is unconfigured. Reporting an unavailable capability as success is the shape of open issue #114, which is fixed on main. - frames-tool.ts raises the frame-sampling cap from 8 back to 16, undoing the hardening in aafac5dc. tools.ts, systemPrompt.ts, probe-tools.ts, run-code-tools.ts, known-errors/SKILL.md and frames-tool.ts are therefore taken from main, and the two new modules plus their verifies are dropped along with their entries in the verify:agent-tools script. tools.ts contained nothing else, so FlexCrop loses nothing by this. FlexCrop itself is sound: storage stays a 0-1 canvas fraction so no migration is needed, pixel conversion is isolated in src/editor/flexCrop.ts with its own verify, edit_item previously rejected transform.crop as an unknown field so no existing agent call changes meaning, and every new verify was registered in test:serial by the author. CHANGELOG keeps the released 0.2.12 section and files FlexCrop under [Unreleased]; the tool catalog was regenerated rather than hand-merged. Local: tsc -b, lint (0 warnings), npm test (162 segments) all pass. | 1 个月前 | |
fix(quality): revive watchdogs, fix orphan checks, and unify skill count Resurrect two silent quality gateways and wire them into the test suite, catch up CHANGELOG and skill-count docs, and convert every orphaned verify/check file into a tracked, npm-wired regression. Quality watchdogs (now in pretest): - verify:shortcuts: catalog count 54->56, keymap scope, native-copy - verify:i18n: check-i18n.mjs now exempts test files; 4 real UI files wrapped in t(); 62 missing en keys added across 9 dict slices - Fixed stableOverrideKeys merging distinct legacy caption overrides when wordRef is empty (3 overrides collapsed to one key) - Fixed buildFollowupWidget silently dropping option-less choice fields (now degrades to a prompt line) - Fixed apply_script repack being silently rejected by the same-track overlap guard: reorderTrackItems now accepts explicit starts and apply_script rebuys atomically per track Docs: - CHANGELOG [Unreleased]: SQLite migration, session-recovery, chat-load-perf, Geist fonts, platform inference routing; fixed link refs - Skill count unified to 26 (README/README_ZH/AGENTS.md/tools.ts) Verify/check cleanup: - Promoted 81 .check.ts to tracked .verify.ts and deleted the originals - Fixed 20+ stale assertions (PROJECT_VERSION 3->7, sourceRevision, caption-track structure, legacy index selectors, shader hash re-pins, abort wiring refactor, stock orientation enum) - 8 new verify groups (editor/captions/agent-tools/components/ media-persist/export-gl/server-extra/sqlite-migration) wired into pretest - Orphan verify count: 158 -> 0 Full npm test, tsc -b --force, and npm run lint all pass. | 2 个月前 | |
feat: land professional timeline, durable jobs, and safe media flows Add slip/rate-stretch, nested sequences, multicam sync, durable generation/export recovery, media intelligence, and hardened MCP/media boundaries while keeping the implementation local-first and cross-platform. | 2 个月前 | |
fix(preview): complete opacity and corner radius rendering | 2 个月前 | |
Merge PR #123 (FlexCrop) — crop feature only, agent governor excluded Trial merge of J160KU's FlexCrop onto v0.2.12 for CI validation. Two things had to be resolved beyond ordinary conflicts. The PR does not build. npm run build runs tsc -b, and on the PR branch alone that fails four ways: two unused imports in systemPrompt.ts and a cast in read-project-tools-check.verify.ts that omits selectedIds/selectedId and then reads them. npx tsc --noEmit passes, which is presumably what was run. Fixed here by completing the cast; the systemPrompt errors disappear with the revert below. The PR also carries 192 lines of agent-behaviour change that its title and summary do not mention, so those are left out of this merge: - src/agent/inspect-loop.ts hooks guardFlexCropToolLoop into tools.ts, the central dispatcher, where it can short-circuit ANY tool call. It caps frame inspection at 6 looks for every agent run — its own comment notes caption/MG verification needs more — and injects "stop", "do not recheck frames", "do not call run_code" into the result stream. - src/agent/sandbox-unavailable.ts turns run_code/probe_media into {ok: true, skipped: true} when E2B is unconfigured. Reporting an unavailable capability as success is the shape of open issue #114, which is fixed on main. - frames-tool.ts raises the frame-sampling cap from 8 back to 16, undoing the hardening in aafac5dc. tools.ts, systemPrompt.ts, probe-tools.ts, run-code-tools.ts, known-errors/SKILL.md and frames-tool.ts are therefore taken from main, and the two new modules plus their verifies are dropped along with their entries in the verify:agent-tools script. tools.ts contained nothing else, so FlexCrop loses nothing by this. FlexCrop itself is sound: storage stays a 0-1 canvas fraction so no migration is needed, pixel conversion is isolated in src/editor/flexCrop.ts with its own verify, edit_item previously rejected transform.crop as an unknown field so no existing agent call changes meaning, and every new verify was registered in test:serial by the author. CHANGELOG keeps the released 0.2.12 section and files FlexCrop under [Unreleased]; the tool catalog was regenerated rather than hand-merged. Local: tsc -b, lint (0 warnings), npm test (162 segments) all pass. | 1 个月前 | |
docs: translate code comments to English | 3 个月前 | |
fix(editor): numeric boundary guards and no-op reference stability Undo-history integrity (no-op contract): - reduce()'s transition-reconcile exit only allocates a new state when reconcileTransitions actually changed something; reconcileTransitions returns the ORIGINAL array when content is unchanged. Previously any rejected/no-op action on a timeline with >=1 transition produced a new state object, pushing a phantom undo step and clearing redo (Cmd+Z "did nothing" and lost redo after every clamped drag nudge). - moveItemWithGroups returns the original state when the clamped delta is 0 and no track change was requested. Numeric validation on tool/LLM-facing action channels (frame domain is integral; NaN stringifies to null and corrupts the persisted doc): - move: rejects non-finite startFrame, rounds fractional targets - split: rounds atFrame before boundary checks - retime: rounds action-supplied startFrame/durationInFrames (srcInFrame intentionally left fractional-capable for playbackRate) - reorderTrackItems.starts (apply_script channel): non-finite or negative pinned start rejects the whole action atomically; fractional starts are rounded - setVolume/setFade/setSpeed: reject non-finite inputs - setCanvas / tl.retarget: reject non-finite or <1 dimensions, round fractional ones (zero canvas divided nested-sequence layout) - updateMarker: rejects non-finite frame fields, clamps to >=0 integers (symmetric with addMarker's command-side clamp) Shared paths touched: reduce() exit (every per-timeline action), move/retime/split/speed/volume/fade/canvas/marker/reorder reducers. Valid inputs behave identically (round is a no-op on the integral frames every existing caller sends); only previously-invalid inputs change outcome, from corrupting state to being rejected or normalized. Verification: tsc -b --force clean; oxlint 0 warnings; new reducerGuards.verify covers no-op reference stability plus every guard above (all passing); full editor verify sweep 35/37 green - the two failures (mediaAssetUsage, transitionPreview) reproduce identically on a clean tree (pre-existing run-check bundling issues, not regressions); browser e2e on localhost:5199 (isolated dev profile): open project, add solid, undo, redo all correct with 0 console errors. | 1 个月前 | |
chore(release): prepare v0.2.0 Harden project storage, upload finalization, export recovery, watched-folder ownership, and native inference lifecycle boundaries. Add cross-platform desktop update packaging and checksum-gated GitHub Release publication while preserving the public v3 project schema, existing media URL semantics, and opt-in defaults. Verified with TypeScript project builds, zero-warning lint, the full test suite, production build, and a browser create/import/edit/reload/close-reopen smoke. | 2 个月前 | |
Merge pull request #23 from 0xsline/codex/contrib-timeline-preview | 2 个月前 | |
feat(media): complete progressive batch placement | 2 个月前 | |
fix(captions): reconcile removed timeline sources (#67) * fix(captions): reconcile removed timeline sources * fix: repair package.json for PR #67 * fix: repair src/editor/linkGroups.ts for PR #67 * fix: repair src/editor/mediaAssetUsage.ts for PR #67 * fix: add src/captions/reconcileSources.ts for PR #67 * fix: add src/editor/captionReferences.verify.ts for PR #67 * fix(captions): reconcile overwrite removals * fix(captions): reconcile overwrite removals Co-authored-by: tianxingleo <tianxingleo@gmail.com> | 2 个月前 | |
chore(release): prepare v0.2.0 Harden project storage, upload finalization, export recovery, watched-folder ownership, and native inference lifecycle boundaries. Add cross-platform desktop update packaging and checksum-gated GitHub Release publication while preserving the public v3 project schema, existing media URL semantics, and opt-in defaults. Verified with TypeScript project builds, zero-warning lint, the full test suite, production build, and a browser create/import/edit/reload/close-reopen smoke. | 2 个月前 | |
chore(release): prepare v0.2.0 Harden project storage, upload finalization, export recovery, watched-folder ownership, and native inference lifecycle boundaries. Add cross-platform desktop update packaging and checksum-gated GitHub Release publication while preserving the public v3 project schema, existing media URL semantics, and opt-in defaults. Verified with TypeScript project builds, zero-warning lint, the full test suite, production build, and a browser create/import/edit/reload/close-reopen smoke. | 2 个月前 | |
feat: harden media captions and offline editing | 2 个月前 | |
feat: harden media captions and offline editing | 2 个月前 | |
feat: harden media captions and offline editing | 2 个月前 | |
feat: harden media captions and offline editing | 2 个月前 | |
fix(media): recount media that lands after a frame-rate change Pool durations are frame counts at the project rate, but an import counts them at the rate it started with. Switching to 24 fps while a 6 s upload was still transcoding recounted the placeholder to 144 frames, then the finished import wrote back 180, so the card read 0:08. Relinked files, phone and watch-folder imports, and agent media landing on a project whose rate changed during the run did the same; template media kept the template's rate. addAsset and pool.relinkAsset now carry the rate their duration was counted at (durationFps), and the reducer recounts it into the project rate as it writes the pool; source identity stays the probed file's. Imports pass the rate they probed at and the commands default to the doc's own rate, so an agent draft's media is recounted when it lands on the live project. The pool import run moves to poolImportRun.ts to keep useEditorMediaIngest.ts under the size cap; the asset it returns to callers that place it right away is recounted too. Fixes #184 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> | 1 个月前 | |
fix(editor): a split motion graphic continues its animation instead of restarting Splitting a motion graphic gave two clips that each played the template from frame 0: the second piece replayed the intro, and both faded out at their own ends, so a cut in the middle of an animation showed the opening scene again. Both pieces now keep the length of the whole design (designDurationInFrames) and play their own window of it from srcInFrame. The template's clock is shifted through the enclosing sequence, so useCurrentFrame and useVideoConfig see the design's time and length. A split piece trims like video: its start cuts into or back out of the animation, and its end stops at the design's last frame. Unsplit clips are unchanged. The split tool description and the motion-graphic skills say so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> | 12 天前 | |
fix(editor): a split motion graphic continues its animation instead of restarting Splitting a motion graphic gave two clips that each played the template from frame 0: the second piece replayed the intro, and both faded out at their own ends, so a cut in the middle of an animation showed the opening scene again. Both pieces now keep the length of the whole design (designDurationInFrames) and play their own window of it from srcInFrame. The template's clock is shifted through the enclosing sequence, so useCurrentFrame and useVideoConfig see the design's time and length. A split piece trims like video: its start cuts into or back out of the animation, and its end stops at the design's last frame. Unsplit clips are unchanged. The split tool description and the motion-graphic skills say so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> | 12 天前 | |
fix(timeline): prevent same-track overlaps Project moves and inserts to the nearest valid gap and rejects geometry updates that introduce a new overlap. Legacy overlap pairs remain loadable, and multicam gaps restore without transient collisions.\n\nRefs #38 | 2 个月前 | |
fix(quality): revive watchdogs, fix orphan checks, and unify skill count Resurrect two silent quality gateways and wire them into the test suite, catch up CHANGELOG and skill-count docs, and convert every orphaned verify/check file into a tracked, npm-wired regression. Quality watchdogs (now in pretest): - verify:shortcuts: catalog count 54->56, keymap scope, native-copy - verify:i18n: check-i18n.mjs now exempts test files; 4 real UI files wrapped in t(); 62 missing en keys added across 9 dict slices - Fixed stableOverrideKeys merging distinct legacy caption overrides when wordRef is empty (3 overrides collapsed to one key) - Fixed buildFollowupWidget silently dropping option-less choice fields (now degrades to a prompt line) - Fixed apply_script repack being silently rejected by the same-track overlap guard: reorderTrackItems now accepts explicit starts and apply_script rebuys atomically per track Docs: - CHANGELOG [Unreleased]: SQLite migration, session-recovery, chat-load-perf, Geist fonts, platform inference routing; fixed link refs - Skill count unified to 26 (README/README_ZH/AGENTS.md/tools.ts) Verify/check cleanup: - Promoted 81 .check.ts to tracked .verify.ts and deleted the originals - Fixed 20+ stale assertions (PROJECT_VERSION 3->7, sourceRevision, caption-track structure, legacy index selectors, shader hash re-pins, abort wiring refactor, stock orientation enum) - 8 new verify groups (editor/captions/agent-tools/components/ media-persist/export-gl/server-extra/sqlite-migration) wired into pretest - Orphan verify count: 158 -> 0 Full npm test, tsc -b --force, and npm run lint all pass. | 2 个月前 | |
chore(release): prepare v0.2.0 Harden project storage, upload finalization, export recovery, watched-folder ownership, and native inference lifecycle boundaries. Add cross-platform desktop update packaging and checksum-gated GitHub Release publication while preserving the public v3 project schema, existing media URL semantics, and opt-in defaults. Verified with TypeScript project builds, zero-warning lint, the full test suite, production build, and a browser create/import/edit/reload/close-reopen smoke. | 2 个月前 | |
feat: land professional timeline, durable jobs, and safe media flows Add slip/rate-stretch, nested sequences, multicam sync, durable generation/export recovery, media intelligence, and hardened MCP/media boundaries while keeping the implementation local-first and cross-platform. | 2 个月前 | |
feat: harden media captions and offline editing | 2 个月前 | |
feat: land professional timeline, durable jobs, and safe media flows Add slip/rate-stretch, nested sequences, multicam sync, durable generation/export recovery, media intelligence, and hardened MCP/media boundaries while keeping the implementation local-first and cross-platform. | 2 个月前 | |
fix(agent): keep imported timelines on the project frame rate Pool durations are frame counts at the project rate, and the media card reads them at the active sequence's rate. import_timeline gave its new timeline the FCPXML/EDL rate without recounting anything, so a 24 fps import in a 30 fps project showed a 6 s clip as 0:08, and placement, trim/slip limits and the agent's source-window math read pool frames at the wrong rate. By then the rate was locked, so the toolbar could not fix it. The parsers now round sequence time onto the project's frames (same exact rationals, one rounding) and the timeline keeps the rate createTimeline gives it; the result warns when the source rate differed. Converting leaves the rest of the project alone, where adopting the file's rate would retime every other sequence and the pool, and cannot follow a 29.97/23.976 file. A recorded tl.create that lands after the user picked another rate now also takes the project's current rate. Saved projects that are already mixed are left as they are: which rate their pool was counted in is not recorded. Refs #184 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> | 1 个月前 | |
chore(release): prepare v0.2.0 Harden project storage, upload finalization, export recovery, watched-folder ownership, and native inference lifecycle boundaries. Add cross-platform desktop update packaging and checksum-gated GitHub Release publication while preserving the public v3 project schema, existing media URL semantics, and opt-in defaults. Verified with TypeScript project builds, zero-warning lint, the full test suite, production build, and a browser create/import/edit/reload/close-reopen smoke. | 2 个月前 | |
fix(agent): preserve recovered server-run draft validity Use normalized ProjectDoc equality for recovered proposal drafts so structurally identical reloads remain applicable while real project changes still invalidate them.\n\nVerification: npx tsc -b --force; npm run lint; npm test; npm run build; real DeepSeek server-run edit, reload, and service-restart smoke. | 2 个月前 | |
fix(editor): prevent rate stretching word-driven audio | 30 天前 | |
fix(editor): prevent rate stretching word-driven audio | 30 天前 | |
chore(release): prepare v0.2.0 Harden project storage, upload finalization, export recovery, watched-folder ownership, and native inference lifecycle boundaries. Add cross-platform desktop update packaging and checksum-gated GitHub Release publication while preserving the public v3 project schema, existing media URL semantics, and opt-in defaults. Verified with TypeScript project builds, zero-warning lint, the full test suite, production build, and a browser create/import/edit/reload/close-reopen smoke. | 2 个月前 | |
fix(media): recount media that lands after a frame-rate change Pool durations are frame counts at the project rate, but an import counts them at the rate it started with. Switching to 24 fps while a 6 s upload was still transcoding recounted the placeholder to 144 frames, then the finished import wrote back 180, so the card read 0:08. Relinked files, phone and watch-folder imports, and agent media landing on a project whose rate changed during the run did the same; template media kept the template's rate. addAsset and pool.relinkAsset now carry the rate their duration was counted at (durationFps), and the reducer recounts it into the project rate as it writes the pool; source identity stays the probed file's. Imports pass the rate they probed at and the commands default to the doc's own rate, so an agent draft's media is recounted when it lands on the live project. The pool import run moves to poolImportRun.ts to keep useEditorMediaIngest.ts under the size cap; the asset it returns to callers that place it right away is recounted too. Fixes #184 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> | 1 个月前 | |
fix(editor): numeric boundary guards and no-op reference stability Undo-history integrity (no-op contract): - reduce()'s transition-reconcile exit only allocates a new state when reconcileTransitions actually changed something; reconcileTransitions returns the ORIGINAL array when content is unchanged. Previously any rejected/no-op action on a timeline with >=1 transition produced a new state object, pushing a phantom undo step and clearing redo (Cmd+Z "did nothing" and lost redo after every clamped drag nudge). - moveItemWithGroups returns the original state when the clamped delta is 0 and no track change was requested. Numeric validation on tool/LLM-facing action channels (frame domain is integral; NaN stringifies to null and corrupts the persisted doc): - move: rejects non-finite startFrame, rounds fractional targets - split: rounds atFrame before boundary checks - retime: rounds action-supplied startFrame/durationInFrames (srcInFrame intentionally left fractional-capable for playbackRate) - reorderTrackItems.starts (apply_script channel): non-finite or negative pinned start rejects the whole action atomically; fractional starts are rounded - setVolume/setFade/setSpeed: reject non-finite inputs - setCanvas / tl.retarget: reject non-finite or <1 dimensions, round fractional ones (zero canvas divided nested-sequence layout) - updateMarker: rejects non-finite frame fields, clamps to >=0 integers (symmetric with addMarker's command-side clamp) Shared paths touched: reduce() exit (every per-timeline action), move/retime/split/speed/volume/fade/canvas/marker/reorder reducers. Valid inputs behave identically (round is a no-op on the integral frames every existing caller sends); only previously-invalid inputs change outcome, from corrupting state to being rejected or normalized. Verification: tsc -b --force clean; oxlint 0 warnings; new reducerGuards.verify covers no-op reference stability plus every guard above (all passing); full editor verify sweep 35/37 green - the two failures (mediaAssetUsage, transitionPreview) reproduce identically on a clean tree (pre-existing run-check bundling issues, not regressions); browser e2e on localhost:5199 (isolated dev profile): open project, add solid, undo, redo all correct with 0 console errors. | 1 个月前 | |
fix(editor): numeric boundary guards and no-op reference stability Undo-history integrity (no-op contract): - reduce()'s transition-reconcile exit only allocates a new state when reconcileTransitions actually changed something; reconcileTransitions returns the ORIGINAL array when content is unchanged. Previously any rejected/no-op action on a timeline with >=1 transition produced a new state object, pushing a phantom undo step and clearing redo (Cmd+Z "did nothing" and lost redo after every clamped drag nudge). - moveItemWithGroups returns the original state when the clamped delta is 0 and no track change was requested. Numeric validation on tool/LLM-facing action channels (frame domain is integral; NaN stringifies to null and corrupts the persisted doc): - move: rejects non-finite startFrame, rounds fractional targets - split: rounds atFrame before boundary checks - retime: rounds action-supplied startFrame/durationInFrames (srcInFrame intentionally left fractional-capable for playbackRate) - reorderTrackItems.starts (apply_script channel): non-finite or negative pinned start rejects the whole action atomically; fractional starts are rounded - setVolume/setFade/setSpeed: reject non-finite inputs - setCanvas / tl.retarget: reject non-finite or <1 dimensions, round fractional ones (zero canvas divided nested-sequence layout) - updateMarker: rejects non-finite frame fields, clamps to >=0 integers (symmetric with addMarker's command-side clamp) Shared paths touched: reduce() exit (every per-timeline action), move/retime/split/speed/volume/fade/canvas/marker/reorder reducers. Valid inputs behave identically (round is a no-op on the integral frames every existing caller sends); only previously-invalid inputs change outcome, from corrupting state to being rejected or normalized. Verification: tsc -b --force clean; oxlint 0 warnings; new reducerGuards.verify covers no-op reference stability plus every guard above (all passing); full editor verify sweep 35/37 green - the two failures (mediaAssetUsage, transitionPreview) reproduce identically on a clean tree (pre-existing run-check bundling issues, not regressions); browser e2e on localhost:5199 (isolated dev profile): open project, add solid, undo, redo all correct with 0 console errors. | 1 个月前 | |
chore(release): prepare v0.2.0 Harden project storage, upload finalization, export recovery, watched-folder ownership, and native inference lifecycle boundaries. Add cross-platform desktop update packaging and checksum-gated GitHub Release publication while preserving the public v3 project schema, existing media URL semantics, and opt-in defaults. Verified with TypeScript project builds, zero-warning lint, the full test suite, production build, and a browser create/import/edit/reload/close-reopen smoke. | 2 个月前 | |
fix(editor): a split motion graphic continues its animation instead of restarting Splitting a motion graphic gave two clips that each played the template from frame 0: the second piece replayed the intro, and both faded out at their own ends, so a cut in the middle of an animation showed the opening scene again. Both pieces now keep the length of the whole design (designDurationInFrames) and play their own window of it from srcInFrame. The template's clock is shifted through the enclosing sequence, so useCurrentFrame and useVideoConfig see the design's time and length. A split piece trims like video: its start cuts into or back out of the animation, and its end stops at the design's last frame. Unsplit clips are unchanged. The split tool description and the motion-graphic skills say so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> | 12 天前 | |
fix(media): recount media that lands after a frame-rate change Pool durations are frame counts at the project rate, but an import counts them at the rate it started with. Switching to 24 fps while a 6 s upload was still transcoding recounted the placeholder to 144 frames, then the finished import wrote back 180, so the card read 0:08. Relinked files, phone and watch-folder imports, and agent media landing on a project whose rate changed during the run did the same; template media kept the template's rate. addAsset and pool.relinkAsset now carry the rate their duration was counted at (durationFps), and the reducer recounts it into the project rate as it writes the pool; source identity stays the probed file's. Imports pass the rate they probed at and the commands default to the doc's own rate, so an agent draft's media is recounted when it lands on the live project. The pool import run moves to poolImportRun.ts to keep useEditorMediaIngest.ts under the size cap; the asset it returns to callers that place it right away is recounted too. Fixes #184 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> | 1 个月前 | |
fix(editor): numeric boundary guards and no-op reference stability Undo-history integrity (no-op contract): - reduce()'s transition-reconcile exit only allocates a new state when reconcileTransitions actually changed something; reconcileTransitions returns the ORIGINAL array when content is unchanged. Previously any rejected/no-op action on a timeline with >=1 transition produced a new state object, pushing a phantom undo step and clearing redo (Cmd+Z "did nothing" and lost redo after every clamped drag nudge). - moveItemWithGroups returns the original state when the clamped delta is 0 and no track change was requested. Numeric validation on tool/LLM-facing action channels (frame domain is integral; NaN stringifies to null and corrupts the persisted doc): - move: rejects non-finite startFrame, rounds fractional targets - split: rounds atFrame before boundary checks - retime: rounds action-supplied startFrame/durationInFrames (srcInFrame intentionally left fractional-capable for playbackRate) - reorderTrackItems.starts (apply_script channel): non-finite or negative pinned start rejects the whole action atomically; fractional starts are rounded - setVolume/setFade/setSpeed: reject non-finite inputs - setCanvas / tl.retarget: reject non-finite or <1 dimensions, round fractional ones (zero canvas divided nested-sequence layout) - updateMarker: rejects non-finite frame fields, clamps to >=0 integers (symmetric with addMarker's command-side clamp) Shared paths touched: reduce() exit (every per-timeline action), move/retime/split/speed/volume/fade/canvas/marker/reorder reducers. Valid inputs behave identically (round is a no-op on the integral frames every existing caller sends); only previously-invalid inputs change outcome, from corrupting state to being rejected or normalized. Verification: tsc -b --force clean; oxlint 0 warnings; new reducerGuards.verify covers no-op reference stability plus every guard above (all passing); full editor verify sweep 35/37 green - the two failures (mediaAssetUsage, transitionPreview) reproduce identically on a clean tree (pre-existing run-check bundling issues, not regressions); browser e2e on localhost:5199 (isolated dev profile): open project, add solid, undo, redo all correct with 0 console errors. | 1 个月前 | |
fix(editor): numeric boundary guards and no-op reference stability Undo-history integrity (no-op contract): - reduce()'s transition-reconcile exit only allocates a new state when reconcileTransitions actually changed something; reconcileTransitions returns the ORIGINAL array when content is unchanged. Previously any rejected/no-op action on a timeline with >=1 transition produced a new state object, pushing a phantom undo step and clearing redo (Cmd+Z "did nothing" and lost redo after every clamped drag nudge). - moveItemWithGroups returns the original state when the clamped delta is 0 and no track change was requested. Numeric validation on tool/LLM-facing action channels (frame domain is integral; NaN stringifies to null and corrupts the persisted doc): - move: rejects non-finite startFrame, rounds fractional targets - split: rounds atFrame before boundary checks - retime: rounds action-supplied startFrame/durationInFrames (srcInFrame intentionally left fractional-capable for playbackRate) - reorderTrackItems.starts (apply_script channel): non-finite or negative pinned start rejects the whole action atomically; fractional starts are rounded - setVolume/setFade/setSpeed: reject non-finite inputs - setCanvas / tl.retarget: reject non-finite or <1 dimensions, round fractional ones (zero canvas divided nested-sequence layout) - updateMarker: rejects non-finite frame fields, clamps to >=0 integers (symmetric with addMarker's command-side clamp) Shared paths touched: reduce() exit (every per-timeline action), move/retime/split/speed/volume/fade/canvas/marker/reorder reducers. Valid inputs behave identically (round is a no-op on the integral frames every existing caller sends); only previously-invalid inputs change outcome, from corrupting state to being rejected or normalized. Verification: tsc -b --force clean; oxlint 0 warnings; new reducerGuards.verify covers no-op reference stability plus every guard above (all passing); full editor verify sweep 35/37 green - the two failures (mediaAssetUsage, transitionPreview) reproduce identically on a clean tree (pre-existing run-check bundling issues, not regressions); browser e2e on localhost:5199 (isolated dev profile): open project, add solid, undo, redo all correct with 0 console errors. | 1 个月前 | |
fix(editor): numeric boundary guards and no-op reference stability Undo-history integrity (no-op contract): - reduce()'s transition-reconcile exit only allocates a new state when reconcileTransitions actually changed something; reconcileTransitions returns the ORIGINAL array when content is unchanged. Previously any rejected/no-op action on a timeline with >=1 transition produced a new state object, pushing a phantom undo step and clearing redo (Cmd+Z "did nothing" and lost redo after every clamped drag nudge). - moveItemWithGroups returns the original state when the clamped delta is 0 and no track change was requested. Numeric validation on tool/LLM-facing action channels (frame domain is integral; NaN stringifies to null and corrupts the persisted doc): - move: rejects non-finite startFrame, rounds fractional targets - split: rounds atFrame before boundary checks - retime: rounds action-supplied startFrame/durationInFrames (srcInFrame intentionally left fractional-capable for playbackRate) - reorderTrackItems.starts (apply_script channel): non-finite or negative pinned start rejects the whole action atomically; fractional starts are rounded - setVolume/setFade/setSpeed: reject non-finite inputs - setCanvas / tl.retarget: reject non-finite or <1 dimensions, round fractional ones (zero canvas divided nested-sequence layout) - updateMarker: rejects non-finite frame fields, clamps to >=0 integers (symmetric with addMarker's command-side clamp) Shared paths touched: reduce() exit (every per-timeline action), move/retime/split/speed/volume/fade/canvas/marker/reorder reducers. Valid inputs behave identically (round is a no-op on the integral frames every existing caller sends); only previously-invalid inputs change outcome, from corrupting state to being rejected or normalized. Verification: tsc -b --force clean; oxlint 0 warnings; new reducerGuards.verify covers no-op reference stability plus every guard above (all passing); full editor verify sweep 35/37 green - the two failures (mediaAssetUsage, transitionPreview) reproduce identically on a clean tree (pre-existing run-check bundling issues, not regressions); browser e2e on localhost:5199 (isolated dev profile): open project, add solid, undo, redo all correct with 0 console errors. | 1 个月前 | |
fix(editor): numeric boundary guards and no-op reference stability Undo-history integrity (no-op contract): - reduce()'s transition-reconcile exit only allocates a new state when reconcileTransitions actually changed something; reconcileTransitions returns the ORIGINAL array when content is unchanged. Previously any rejected/no-op action on a timeline with >=1 transition produced a new state object, pushing a phantom undo step and clearing redo (Cmd+Z "did nothing" and lost redo after every clamped drag nudge). - moveItemWithGroups returns the original state when the clamped delta is 0 and no track change was requested. Numeric validation on tool/LLM-facing action channels (frame domain is integral; NaN stringifies to null and corrupts the persisted doc): - move: rejects non-finite startFrame, rounds fractional targets - split: rounds atFrame before boundary checks - retime: rounds action-supplied startFrame/durationInFrames (srcInFrame intentionally left fractional-capable for playbackRate) - reorderTrackItems.starts (apply_script channel): non-finite or negative pinned start rejects the whole action atomically; fractional starts are rounded - setVolume/setFade/setSpeed: reject non-finite inputs - setCanvas / tl.retarget: reject non-finite or <1 dimensions, round fractional ones (zero canvas divided nested-sequence layout) - updateMarker: rejects non-finite frame fields, clamps to >=0 integers (symmetric with addMarker's command-side clamp) Shared paths touched: reduce() exit (every per-timeline action), move/retime/split/speed/volume/fade/canvas/marker/reorder reducers. Valid inputs behave identically (round is a no-op on the integral frames every existing caller sends); only previously-invalid inputs change outcome, from corrupting state to being rejected or normalized. Verification: tsc -b --force clean; oxlint 0 warnings; new reducerGuards.verify covers no-op reference stability plus every guard above (all passing); full editor verify sweep 35/37 green - the two failures (mediaAssetUsage, transitionPreview) reproduce identically on a clean tree (pre-existing run-check bundling issues, not regressions); browser e2e on localhost:5199 (isolated dev profile): open project, add solid, undo, redo all correct with 0 console errors. | 1 个月前 | |
fix(quality): revive watchdogs, fix orphan checks, and unify skill count Resurrect two silent quality gateways and wire them into the test suite, catch up CHANGELOG and skill-count docs, and convert every orphaned verify/check file into a tracked, npm-wired regression. Quality watchdogs (now in pretest): - verify:shortcuts: catalog count 54->56, keymap scope, native-copy - verify:i18n: check-i18n.mjs now exempts test files; 4 real UI files wrapped in t(); 62 missing en keys added across 9 dict slices - Fixed stableOverrideKeys merging distinct legacy caption overrides when wordRef is empty (3 overrides collapsed to one key) - Fixed buildFollowupWidget silently dropping option-less choice fields (now degrades to a prompt line) - Fixed apply_script repack being silently rejected by the same-track overlap guard: reorderTrackItems now accepts explicit starts and apply_script rebuys atomically per track Docs: - CHANGELOG [Unreleased]: SQLite migration, session-recovery, chat-load-perf, Geist fonts, platform inference routing; fixed link refs - Skill count unified to 26 (README/README_ZH/AGENTS.md/tools.ts) Verify/check cleanup: - Promoted 81 .check.ts to tracked .verify.ts and deleted the originals - Fixed 20+ stale assertions (PROJECT_VERSION 3->7, sourceRevision, caption-track structure, legacy index selectors, shader hash re-pins, abort wiring refactor, stock orientation enum) - 8 new verify groups (editor/captions/agent-tools/components/ media-persist/export-gl/server-extra/sqlite-migration) wired into pretest - Orphan verify count: 158 -> 0 Full npm test, tsc -b --force, and npm run lint all pass. | 2 个月前 | |
fix(quality): revive watchdogs, fix orphan checks, and unify skill count Resurrect two silent quality gateways and wire them into the test suite, catch up CHANGELOG and skill-count docs, and convert every orphaned verify/check file into a tracked, npm-wired regression. Quality watchdogs (now in pretest): - verify:shortcuts: catalog count 54->56, keymap scope, native-copy - verify:i18n: check-i18n.mjs now exempts test files; 4 real UI files wrapped in t(); 62 missing en keys added across 9 dict slices - Fixed stableOverrideKeys merging distinct legacy caption overrides when wordRef is empty (3 overrides collapsed to one key) - Fixed buildFollowupWidget silently dropping option-less choice fields (now degrades to a prompt line) - Fixed apply_script repack being silently rejected by the same-track overlap guard: reorderTrackItems now accepts explicit starts and apply_script rebuys atomically per track Docs: - CHANGELOG [Unreleased]: SQLite migration, session-recovery, chat-load-perf, Geist fonts, platform inference routing; fixed link refs - Skill count unified to 26 (README/README_ZH/AGENTS.md/tools.ts) Verify/check cleanup: - Promoted 81 .check.ts to tracked .verify.ts and deleted the originals - Fixed 20+ stale assertions (PROJECT_VERSION 3->7, sourceRevision, caption-track structure, legacy index selectors, shader hash re-pins, abort wiring refactor, stock orientation enum) - 8 new verify groups (editor/captions/agent-tools/components/ media-persist/export-gl/server-extra/sqlite-migration) wired into pretest - Orphan verify count: 158 -> 0 Full npm test, tsc -b --force, and npm run lint all pass. | 2 个月前 | |
docs: translate code comments to English | 3 个月前 | |
fix(export): flatten nested sequences into the JianYing draft A rough cut the agent builds from sequence items - each windowing a source range of one long timeline - failed JianYing/CapCut export with "timeline has no video clips to export": the request builder kept only top-level video/image/gif/audio items and dropped every sequence. src/editor/sequenceFlatten.ts places every timeline instance reachable through sequence items onto the root timeline: its visible window in its own frames, the root frame that window starts at, and its frames per root frame. The mapping is NestedSequenceLayer's - a sequence shows its child from srcInFrame at playbackRate (sourceWindowForTimelineRange), only inside the item's own window - and the graph is validated first by resolveTimelineRenderPlan, so a cycle, a missing timeline or an fps mismatch fails the export with the SequenceGraphError a render gives. A timeline referenced 43 times is placed 43 times. The request builder maps each leaf clip through its placement: clipped to the sequence window, moved onto the root, its in-point advanced by the frames the window cut, its rate multiplied through the nesting (stills stay stills). Each sequence therefore becomes its own editable clips reading its own source range, not one rendered video. The child's captions render inside the sequence in the preview, so they are clipped and re-timed the same way. Clips and cues now go out in timeline order. Unchanged, as for top-level items before: hidden/muted track state, volume, transitions and caption tracks other than each timeline's default one are not carried. Where a sequence item outlasts its child the render freezes the child's last frame; the draft leaves a gap. Tests: jianyingDraftRequest.verify.ts adds the #160 shape (three sequences over one source timeline, one at 2x, with the child's word captions cut at a window edge), leaves clipped by a window, rates composed through two nesting levels (0.5 x 2 x 1.5), the placements themselves, and missing/cyclic references failing like a render. Refs #160. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> | 1 个月前 | |
perf: reduce hot-path overhead in editor, transcript, and agent streaming - editor/store: memoize activeEditorState so unrelated renders keep downstream [state] memos and effects stable - editor/sequenceGraph: skip graph traversal when the project has no sequence items (runs on every per-timeline action); duplicate timeline id detection is preserved in the fast path - agent/proposal: cache projectDocsDiffer per (baseDoc, currentDoc) identity pair (docs are immutable) so pending-proposal renders stop re-migrating and stringifying two full docs - transcript/TranscriptViews: memoize buildScriptRows and WordRow so drag-over/gap-adjust state changes no longer rebuild rows and re-render every word span on long transcripts - agent/serverRun: batch streaming text/thinking persistence into 200ms flushes via new serverRunStreamDeltaBuffer instead of rewriting the full localStorage record per SSE delta (O(text^2)); cursor+text always land in one atomic patch, non-delta commits fold pending text into their own write, stale cross-run flushes are discarded, and flush failures follow the existing persistenceError abandon path Shared paths touched: useEditor state identity (all editor consumers), per-timeline action validation, proposal staleness checks (browser and external bridge), transcript script view, serverRun durable stream persistence. All changes are memoization/short-circuit/batching only; no action semantics, storage schema, or recovery contract changed. Verification: tsc -b --force clean; oxlint 0 warnings; verify:affected (sequenceGraph, proposal) and serverRunSessionStorage.verify pass; 6-scenario standalone logic test for the delta buffer (batch merge, fold, cross-run discard, failure reporting, drop) passed; browser e2e on localhost:5199 (isolated dev profile): new project -> add solid -> undo -> redo -> dashboard reload with 0 console errors. Pre-existing issue confirmed on a clean tree: useServerRun.verify and serverRunToolRecovery.verify crash under run-check (dynamic require in @vercel/oidc via the ai package), unrelated to this change. | 1 个月前 | |
feat: land professional timeline, durable jobs, and safe media flows Add slip/rate-stretch, nested sequences, multicam sync, durable generation/export recovery, media intelligence, and hardened MCP/media boundaries while keeping the implementation local-first and cross-platform. | 2 个月前 | |
fix(export): decode Windows server-render video with the FFmpeg compositor On Windows a server export whose timeline holds video could sit at ~10% with no error: a frame's "Extracting frame at time ..." delayRender never cleared, so the render waited out the ten-minute per-frame budget before failing. All three reports on the issue come from Windows 10/11 machines rendering H.264 sources. RuntimeVideo discarded browserRenderer and always decoded with @remotion/media's <Video> (Mediabunny + WebCodecs inside chrome-headless-shell). With the Windows render GL backend (angle, D3D11) Chrome can give WebCodecs the D3D11 hardware decoder. Its small picture pool runs dry while Mediabunny holds decoded frames open, and it then stops producing frames without an error. The extraction never settles, so @remotion/media's own <OffthreadVideo> fallback (reached only through an error result) never runs. Remotion reports the same hang with the exact versions this app ships (4.0.509, chrome-headless-shell 149.0.7790.0) and finds <OffthreadVideo> unaffected (remotion-dev/remotion#10701). The #11020 fix only shipped in 4.0.521, and #11393 still reports stalls after it. Forcing the <OffthreadVideo> branch made the reporter's timeline export. remotion/video-decoder.mjs now picks the server render's video decoder: 'offthread' on win32, 'webcodecs' elsewhere, and CC_RENDER_VIDEO_DECODER=webcodecs|offthread overrides it on any platform. render.mjs passes it to the composition as the serverVideoDecoder input prop from renderTimeline, renderClip and renderTimelineStills. TimelineComposition provides it through context (src/editor/serverVideoDecoder.ts). RuntimeVideo (plain clips, background fills, shared runs) and ClipFx (GL effect inputs) render <OffthreadVideo> only in a headless server render. The Player and the in-browser export (isPlayer, isClientSideRendering, browserRenderer) keep @remotion/media whatever the props say. This is not the switch 5b747273 reverted. f32adad1 (PR #97) sent every server render on every platform through <OffthreadVideo>, undoing 594e122b's single frame-accurate decoder path; its removed comments and 359c6fc7 note that the two decoders skew fractional-rate sources by a frame. Here: - Only Windows changes by default. macOS and Linux renders keep the @remotion/media path, which the macOS frame-sync job still pins. - Within one render, plain and effect layers always share one decoder. - The compositor now picks the frames the Player picks. Mediabunny shows the last frame at most 1 ms after the requested time; the compositor shows the last frame at or before it. <OffthreadVideo> is therefore asked 1 ms later (offthreadTrimBefore). On 25, 30 and 60 fps sources both decoders already agreed. On a 29.97 fps source in a 30 fps timeline they differed on 30 of 36 frames (and 13 of 36 when trimmed at 2x); they now agree on every frame. - WebM sources (VP9-alpha MG bakes, the desktop's transparent-MOV proxies) extract as PNG so alpha survives. Other footage keeps the fast opaque (BMP) extraction. ClipFx hands <OffthreadVideo> a stable onVideoFrame. Its image reloads whenever that callback changes identity, which happened every frame just after <OffthreadVideo> revoked the frame's blob URL, logging one failed-load warning per effect frame. Other effects on Windows server renders: - A clip's own audio is mixed by FFmpeg, as the server Audio path already mixes audio items, so speed changes keep pitch. - Each source is copied into Remotion's temporary download directory while the render runs. - Colour conversion and chroma upsampling differ slightly from @remotion/media's. On a synthetic test pattern the two outputs are about 30 dB PSNR apart, while showing the same frames. - GLSL transition windows still read an HTML5 <video>; they did not hang in the reports. Tests: - remotion/video-decoder.verify.mjs: platform defaults, the override in both directions, and the environment read on every render. - src/editor/serverVideoDecoder.verify.ts: the decoder each environment gets, including the hook under real Remotion environment and decoder contexts. A server render follows the server; the Player, the in-browser export and browserRenderer keep @remotion/media. Also the 1 ms shift and the WebM-only transparency. - remotion/video-decoder-render.verify.mjs renders through both decoders. 29.97 fps H.264 with audio in a 30 fps timeline (plain and trimmed 2x) must show exactly the frames the Player's rule selects and keep its audio. A VP9-alpha overlay must leave the red track below it visible. ProRes, which @remotion/media refuses, must render with 'offthread' through renderTimeline (a plain and a GL-effect clip), renderClip and renderTimelineStills, and fail with 'webcodecs'. Mutation-checked: removing the provider, the input prop, either component's switch, the 1 ms shift or the WebM transparency each fails it. - src/gl/clipFxExport.offthread.verify.mjs reruns the effect and transition frame-sync fixture with 'offthread'. clipFxExport.verify.mjs now names its decoder in its output. The new verifies run in npm test, and the render ones also in the macOS frame-sync CI job, where exact frame mapping is asserted. Validation (macOS arm64): npx tsc -p tsconfig.app.json and tsconfig.node.json (both clean); npm run lint (0 warnings); the four new verifies; 44 export/render-related verify segments, including clipFxExport under both decoders; scripts/verify-gate-coverage and verify-registration. The D3D11 hang itself needs a Windows GPU and was not reproduced here. Fixes #162. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> | 1 个月前 | |
fix(export): decode Windows server-render video with the FFmpeg compositor On Windows a server export whose timeline holds video could sit at ~10% with no error: a frame's "Extracting frame at time ..." delayRender never cleared, so the render waited out the ten-minute per-frame budget before failing. All three reports on the issue come from Windows 10/11 machines rendering H.264 sources. RuntimeVideo discarded browserRenderer and always decoded with @remotion/media's <Video> (Mediabunny + WebCodecs inside chrome-headless-shell). With the Windows render GL backend (angle, D3D11) Chrome can give WebCodecs the D3D11 hardware decoder. Its small picture pool runs dry while Mediabunny holds decoded frames open, and it then stops producing frames without an error. The extraction never settles, so @remotion/media's own <OffthreadVideo> fallback (reached only through an error result) never runs. Remotion reports the same hang with the exact versions this app ships (4.0.509, chrome-headless-shell 149.0.7790.0) and finds <OffthreadVideo> unaffected (remotion-dev/remotion#10701). The #11020 fix only shipped in 4.0.521, and #11393 still reports stalls after it. Forcing the <OffthreadVideo> branch made the reporter's timeline export. remotion/video-decoder.mjs now picks the server render's video decoder: 'offthread' on win32, 'webcodecs' elsewhere, and CC_RENDER_VIDEO_DECODER=webcodecs|offthread overrides it on any platform. render.mjs passes it to the composition as the serverVideoDecoder input prop from renderTimeline, renderClip and renderTimelineStills. TimelineComposition provides it through context (src/editor/serverVideoDecoder.ts). RuntimeVideo (plain clips, background fills, shared runs) and ClipFx (GL effect inputs) render <OffthreadVideo> only in a headless server render. The Player and the in-browser export (isPlayer, isClientSideRendering, browserRenderer) keep @remotion/media whatever the props say. This is not the switch 5b747273 reverted. f32adad1 (PR #97) sent every server render on every platform through <OffthreadVideo>, undoing 594e122b's single frame-accurate decoder path; its removed comments and 359c6fc7 note that the two decoders skew fractional-rate sources by a frame. Here: - Only Windows changes by default. macOS and Linux renders keep the @remotion/media path, which the macOS frame-sync job still pins. - Within one render, plain and effect layers always share one decoder. - The compositor now picks the frames the Player picks. Mediabunny shows the last frame at most 1 ms after the requested time; the compositor shows the last frame at or before it. <OffthreadVideo> is therefore asked 1 ms later (offthreadTrimBefore). On 25, 30 and 60 fps sources both decoders already agreed. On a 29.97 fps source in a 30 fps timeline they differed on 30 of 36 frames (and 13 of 36 when trimmed at 2x); they now agree on every frame. - WebM sources (VP9-alpha MG bakes, the desktop's transparent-MOV proxies) extract as PNG so alpha survives. Other footage keeps the fast opaque (BMP) extraction. ClipFx hands <OffthreadVideo> a stable onVideoFrame. Its image reloads whenever that callback changes identity, which happened every frame just after <OffthreadVideo> revoked the frame's blob URL, logging one failed-load warning per effect frame. Other effects on Windows server renders: - A clip's own audio is mixed by FFmpeg, as the server Audio path already mixes audio items, so speed changes keep pitch. - Each source is copied into Remotion's temporary download directory while the render runs. - Colour conversion and chroma upsampling differ slightly from @remotion/media's. On a synthetic test pattern the two outputs are about 30 dB PSNR apart, while showing the same frames. - GLSL transition windows still read an HTML5 <video>; they did not hang in the reports. Tests: - remotion/video-decoder.verify.mjs: platform defaults, the override in both directions, and the environment read on every render. - src/editor/serverVideoDecoder.verify.ts: the decoder each environment gets, including the hook under real Remotion environment and decoder contexts. A server render follows the server; the Player, the in-browser export and browserRenderer keep @remotion/media. Also the 1 ms shift and the WebM-only transparency. - remotion/video-decoder-render.verify.mjs renders through both decoders. 29.97 fps H.264 with audio in a 30 fps timeline (plain and trimmed 2x) must show exactly the frames the Player's rule selects and keep its audio. A VP9-alpha overlay must leave the red track below it visible. ProRes, which @remotion/media refuses, must render with 'offthread' through renderTimeline (a plain and a GL-effect clip), renderClip and renderTimelineStills, and fail with 'webcodecs'. Mutation-checked: removing the provider, the input prop, either component's switch, the 1 ms shift or the WebM transparency each fails it. - src/gl/clipFxExport.offthread.verify.mjs reruns the effect and transition frame-sync fixture with 'offthread'. clipFxExport.verify.mjs now names its decoder in its output. The new verifies run in npm test, and the render ones also in the macOS frame-sync CI job, where exact frame mapping is asserted. Validation (macOS arm64): npx tsc -p tsconfig.app.json and tsconfig.node.json (both clean); npm run lint (0 warnings); the four new verifies; 44 export/render-related verify segments, including clipFxExport under both decoders; scripts/verify-gate-coverage and verify-registration. The D3D11 hang itself needs a Windows GPU and was not reproduced here. Fixes #162. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> | 1 个月前 | |
perf(editor): share one decoder per consecutive same-source video run Split clips from one source rendered one OffthreadVideo element per clip; playback across clip boundaries then staggered multiple concurrent decoders, which stutters the playhead. Audio already shares one element per consecutive run (ContinuousVideoAudio); the visual layer did not. TimelineComposition now groups consecutive same-source clips into shared visual runs: one video element decodes the whole run (trimBefore at the first clip's source frame), and per-clip appearance (opacity, zoom, border radius, filters) is applied from the active clip each frame. Clips with GL effects, background fill, transition extensions/entrances, transforms, keyframes, or filters keep their own element and split the run. Shared-path impact: TimelineComposition serves both the preview Player and headless export. Shared runs only change element count, not output frames: source-frame continuity is guaranteed by the existing canShareAudio predicate (sourceFrameAt adjacency), and per-frame styling mirrors ClipWrapper's appearanceAt, which was extracted to clipFade.ts (the only shared-path refactor; ClipWrapper behavior unchanged). Validation: tsc -b --force clean, oxlint 0 warnings, production build passes, sharedVisual.verify covers the share predicate. Real project: a 10-clip transition-heavy timeline correctly keeps per-clip elements (transitions must decode independently); after removing its transitions the same timeline renders 4 video elements (2 shared runs of 3 + 2 GL-effect clips) instead of 10. | 2 个月前 | |
feat: land professional timeline, durable jobs, and safe media flows Add slip/rate-stretch, nested sequences, multicam sync, durable generation/export recovery, media intelligence, and hardened MCP/media boundaries while keeping the implementation local-first and cross-platform. | 2 个月前 | |
docs: translate code comments to English | 3 个月前 | |
fix(editor): honor explicit source identity during slip | 30 天前 | |
fix(editor): honor explicit source identity during slip | 30 天前 | |
perf: bound editor runtime and storage costs Virtualize large timeline and resource surfaces, defer Agent and media work, and bound rebuildable caches without evicting source media. | 2 个月前 | |
perf: bound editor runtime and storage costs Virtualize large timeline and resource surfaces, defer Agent and media work, and bound rebuildable caches without evicting source media. | 2 个月前 | |
fix(editor): a split motion graphic continues its animation instead of restarting Splitting a motion graphic gave two clips that each played the template from frame 0: the second piece replayed the intro, and both faded out at their own ends, so a cut in the middle of an animation showed the opening scene again. Both pieces now keep the length of the whole design (designDurationInFrames) and play their own window of it from srcInFrame. The template's clock is shifted through the enclosing sequence, so useCurrentFrame and useVideoConfig see the design's time and length. A split piece trims like video: its start cuts into or back out of the animation, and its end stops at the design's last frame. Unsplit clips are unchanged. The split tool description and the motion-graphic skills say so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> | 12 天前 | |
feat: land professional timeline, durable jobs, and safe media flows Add slip/rate-stretch, nested sequences, multicam sync, durable generation/export recovery, media intelligence, and hardened MCP/media boundaries while keeping the implementation local-first and cross-platform. | 2 个月前 | |
fix(editor): a split motion graphic continues its animation instead of restarting Splitting a motion graphic gave two clips that each played the template from frame 0: the second piece replayed the intro, and both faded out at their own ends, so a cut in the middle of an animation showed the opening scene again. Both pieces now keep the length of the whole design (designDurationInFrames) and play their own window of it from srcInFrame. The template's clock is shifted through the enclosing sequence, so useCurrentFrame and useVideoConfig see the design's time and length. A split piece trims like video: its start cuts into or back out of the animation, and its end stops at the design's last frame. Unsplit clips are unchanged. The split tool description and the motion-graphic skills say so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> | 12 天前 | |
perf: reduce hot-path overhead in editor, transcript, and agent streaming - editor/store: memoize activeEditorState so unrelated renders keep downstream [state] memos and effects stable - editor/sequenceGraph: skip graph traversal when the project has no sequence items (runs on every per-timeline action); duplicate timeline id detection is preserved in the fast path - agent/proposal: cache projectDocsDiffer per (baseDoc, currentDoc) identity pair (docs are immutable) so pending-proposal renders stop re-migrating and stringifying two full docs - transcript/TranscriptViews: memoize buildScriptRows and WordRow so drag-over/gap-adjust state changes no longer rebuild rows and re-render every word span on long transcripts - agent/serverRun: batch streaming text/thinking persistence into 200ms flushes via new serverRunStreamDeltaBuffer instead of rewriting the full localStorage record per SSE delta (O(text^2)); cursor+text always land in one atomic patch, non-delta commits fold pending text into their own write, stale cross-run flushes are discarded, and flush failures follow the existing persistenceError abandon path Shared paths touched: useEditor state identity (all editor consumers), per-timeline action validation, proposal staleness checks (browser and external bridge), transcript script view, serverRun durable stream persistence. All changes are memoization/short-circuit/batching only; no action semantics, storage schema, or recovery contract changed. Verification: tsc -b --force clean; oxlint 0 warnings; verify:affected (sequenceGraph, proposal) and serverRunSessionStorage.verify pass; 6-scenario standalone logic test for the delta buffer (batch merge, fold, cross-run discard, failure reporting, drop) passed; browser e2e on localhost:5199 (isolated dev profile): new project -> add solid -> undo -> redo -> dashboard reload with 0 console errors. Pre-existing issue confirmed on a clean tree: useServerRun.verify and serverRunToolRecovery.verify crash under run-check (dynamic require in @vercel/oidc via the ai package), unrelated to this change. | 1 个月前 | |
fix(media): recount media that lands after a frame-rate change Pool durations are frame counts at the project rate, but an import counts them at the rate it started with. Switching to 24 fps while a 6 s upload was still transcoding recounted the placeholder to 144 frames, then the finished import wrote back 180, so the card read 0:08. Relinked files, phone and watch-folder imports, and agent media landing on a project whose rate changed during the run did the same; template media kept the template's rate. addAsset and pool.relinkAsset now carry the rate their duration was counted at (durationFps), and the reducer recounts it into the project rate as it writes the pool; source identity stays the probed file's. Imports pass the rate they probed at and the commands default to the doc's own rate, so an agent draft's media is recounted when it lands on the live project. The pool import run moves to poolImportRun.ts to keep useEditorMediaIngest.ts under the size cap; the asset it returns to callers that place it right away is recounted too. Fixes #184 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> | 1 个月前 | |
fix: integrate PR 137 with retryable storage and safe exports Merge the current main refactors while preserving the contributor commits. Reset failed IndexedDB connection promises and release cached connections on version change or forced close. Use the project reducer to stage tracks and assets before overwrite planning so placement preserves ordering and one-step undo. Keep export duration based on timeline fps: FFmpeg fps retiming drops or duplicates frames without changing playback duration. Return explicit 400/413 responses for malformed or oversized normalization requests without destroying the response socket. Retain deferred download revocation and portable verify paths from the original PR. Shared-path regression evidence: - TypeScript build with --force and oxlint pass. - Connection recovery, normal/overwrite placement, export parameters, normalization HTTP, shared KV fallback/migration, media persistence, export geometry, and affected-runner verifications pass. - Native Chrome tests cover all three IndexedDB stores: actual VersionError, recovery without reload, and connection release for native version upgrade. - Actual FFmpeg 30-to-24 fps retiming yields the planned 4-second duration. - Real DeepSeek v4-flash runAgent calls submit_export for existing audio and downloads a valid 1132-byte FCPXML document with a successful final reply. - Real /export rendering and submitMediaExport download produce a 192078-byte, 1-second WAV. The Chrome response and downloaded file SHA-256 are identical. Initial renderer startup failed during local resource pressure; two warm attempts succeeded. No paid media generation or user project was involved. Full test and production build gates remain with the aggregate integration. | 1 个月前 | |
fix(media): recount media that lands after a frame-rate change Pool durations are frame counts at the project rate, but an import counts them at the rate it started with. Switching to 24 fps while a 6 s upload was still transcoding recounted the placeholder to 144 frames, then the finished import wrote back 180, so the card read 0:08. Relinked files, phone and watch-folder imports, and agent media landing on a project whose rate changed during the run did the same; template media kept the template's rate. addAsset and pool.relinkAsset now carry the rate their duration was counted at (durationFps), and the reducer recounts it into the project rate as it writes the pool; source identity stays the probed file's. Imports pass the rate they probed at and the commands default to the doc's own rate, so an agent draft's media is recounted when it lands on the live project. The pool import run moves to poolImportRun.ts to keep useEditorMediaIngest.ts under the size cap; the asset it returns to callers that place it right away is recounted too. Fixes #184 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> | 1 个月前 | |
feat: land professional timeline, durable jobs, and safe media flows Add slip/rate-stretch, nested sequences, multicam sync, durable generation/export recovery, media intelligence, and hardened MCP/media boundaries while keeping the implementation local-first and cross-platform. | 2 个月前 | |
feat: land professional timeline, durable jobs, and safe media flows Add slip/rate-stretch, nested sequences, multicam sync, durable generation/export recovery, media intelligence, and hardened MCP/media boundaries while keeping the implementation local-first and cross-platform. | 2 个月前 | |
fix(media): recount media that lands after a frame-rate change Pool durations are frame counts at the project rate, but an import counts them at the rate it started with. Switching to 24 fps while a 6 s upload was still transcoding recounted the placeholder to 144 frames, then the finished import wrote back 180, so the card read 0:08. Relinked files, phone and watch-folder imports, and agent media landing on a project whose rate changed during the run did the same; template media kept the template's rate. addAsset and pool.relinkAsset now carry the rate their duration was counted at (durationFps), and the reducer recounts it into the project rate as it writes the pool; source identity stays the probed file's. Imports pass the rate they probed at and the commands default to the doc's own rate, so an agent draft's media is recounted when it lands on the live project. The pool import run moves to poolImportRun.ts to keep useEditorMediaIngest.ts under the size cap; the asset it returns to callers that place it right away is recounted too. Fixes #184 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> | 1 个月前 | |
feat(editor): choose the project frame rate from the timeline toolbar Every GUI path created 30 fps timelines. "New project" hardcodes 30, and "+ Sequence" and duplicate copy the active rate. Only MCP create_project could make a 60 fps timeline. A 60 fps export of a 30 fps timeline cannot use the browser path ("cannot retime the timeline yet") and is retimed by the local renderer. The timeline toolbar now shows the project frame rate next to the aspect-ratio picker, which is where the other canvas setting already lives, and lets you change it until anything is placed. It offers 24/25/30/50/60, the rates every export route renders without retiming. The browser path needs export fps == timeline fps, and the local renderer only accepts these as retime targets. A rate set elsewhere, such as 59.94 through MCP, is shown as the current value but cannot be picked again once changed. The rate is project-wide and locks once any timeline has clips, transitions, markers, link or multicam groups, or captions. All of those are frame counts at the current rate, and converting them all would be a much larger change than this issue needs. It is project-wide because nested sequences must share a rate and pool durations are counted in it. When the rate changes, time-based pool assets (video, audio, image, gif, svg) are recounted so they keep their seconds. Motion graphics keep their authored frame length, as they do when added from the library. The change is a reducer action (tl.setFps), so it is undoable, and agent drafts record it like any other edit. New timelines do not adopt the first clip's frame rate. Source fps is only known after server normalization and is not stored on the asset. Desktop imports skip that probe, and clips are placed before durations settle. Adopting on drop would mean re-probing and retiming placed clips inside the import pipeline. The export dialog now starts at the export rate nearest the timeline's own. It used to fall back to 30 for any rate outside the list, so a 59.94 timeline exported at 30 by default. Fixes #175. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> | 1 个月前 | |
lint: zero warnings across the repo (27 remaining) Split non-component exports out of component files (6): historyGesture, timelineReadinessKey, folderDrop, clipMediaGeometry, composerSubmitGate, useComposerModelView. Fixed all exhaustive-deps warnings (21) with real fixes — field-level deps (state/status/props objects → the exact fields used), stable refs for effect-scoped cleanups, latest-ref for hover-clearing, memoized transitions lookup — never suppress directives. Plus the earlier 5: regex escapes in external-agent/multicam-tools, control-char checks rewritten per codepoint in media-dir/model-capabilities. npm run lint: 0 warnings. npm test 137 green, tsc -b + build green. | 2 个月前 | |
fix(preview): stop silently dropping MGs and SFX the timeline still shows Two independent ways a clip could sit on the timeline, waveform and all, and simply not reach the viewer. Captions: the toolbar and the renderer each derived "are captions on" from an unset captionsHidden flag, and disagreed. The toolbar counted an enabled caption track OR any text/motion-graphic clip; the renderer counted only an enabled caption track. Since captions-off also hides on-screen text clips - and an MG counts as one as soon as its props carry a text or colour key - disabling a caption track silently hid every such MG while the CC button still read On. One MG with a label prop vanished; its neighbour, whose props are only glowColor/ringColor, kept rendering, which made it look like a broken template. Both sides now share captionOverlayVisible(). Audio: ASR run over non-speech audio invents words far outside the file ("Music" at 24.4s inside a 3.0s sting). AudioClip treats a transcript as an edit list and plays only those word windows, so every generated sound effect went silent. Reject a source window that starts past the media length (now passed down from the pool), and discard an untouched transcript that would swallow more than half the clip. Real word-level deletions still win. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> | 18 天前 | |
feat: land professional timeline, durable jobs, and safe media flows Add slip/rate-stretch, nested sequences, multicam sync, durable generation/export recovery, media intelligence, and hardened MCP/media boundaries while keeping the implementation local-first and cross-platform. | 2 个月前 | |
feat: land professional timeline, durable jobs, and safe media flows Add slip/rate-stretch, nested sequences, multicam sync, durable generation/export recovery, media intelligence, and hardened MCP/media boundaries while keeping the implementation local-first and cross-platform. | 2 个月前 | |
Initial commit | 3 个月前 | |
fix(timeline): prevent same-track overlaps Project moves and inserts to the nearest valid gap and rejects geometry updates that introduce a new overlap. Legacy overlap pairs remain loadable, and multicam gaps restore without transient collisions.\n\nRefs #38 | 2 个月前 | |
fix(scripts): repair run-check bundling; drop dead migration files run-check.mjs exists for a real reason — verify files whose import chain contains Vite-only ?raw / .frag imports cannot run under bare tsx — but it bundled node_modules into a single ESM file, which breaks any package doing a CJS dynamic require at import time. ai → @ai-sdk/gateway → @vercel/oidc does exactly that, so every verify whose graph reached the AI SDK died with 'Dynamic require of "path" is not supported' before running a single assertion. That is not hypothetical: package.json's verify:agent-tools invokes run-check for effect-tools.verify and library-edit-item.verify, and both have been failing in npm test. They cannot run under tsx either (ERR_UNKNOWN_FILE_EXTENSION on the ?raw import), so they were dead either way. With packages: 'external' only our source is bundled and node resolves dependencies itself; the output moved inside the project so bare imports resolve against node_modules. Both verifies now pass. This also explains — and closes — the earlier "pre-existing failures" confusion in this series: sweeping with run-check made healthy verifies look broken. Also: - Deleted src/persist/migrations/v3-to-v4.ts, v4-to-v5.ts, v5-to-v6.ts. They are not registered in the chain (dev versions collapsed back to v3) and nothing in the repo references them — grepped across ts/tsx/ mjs/json/md. They only misled readers into adding steps to a chain that is not wired up. Git retains them if a future v3→v4 needs a starting point. No user data is affected: no shipped document is at version 4-6. - .gitignore covers the run-check temp dir in case a run is interrupted. - trackCreateBatching.verify uses the project's own isHistoryControlAction guard so it type-checks under the full solution build. (The AGENTS.md CURRENT_PROJECT_VERSION path correction — shared/project-version.ts, not src/editor/types.ts — was applied locally; AGENTS.md is gitignored by design.) Verification: tsc -b --force clean; oxlint 0 warnings; both previously broken verifies pass through the repaired runner; migrations.verify and unreadablePreservation.verify still pass after the file removals. | 1 个月前 | |
chore(release): prepare v0.2.0 Harden project storage, upload finalization, export recovery, watched-folder ownership, and native inference lifecycle boundaries. Add cross-platform desktop update packaging and checksum-gated GitHub Release publication while preserving the public v3 project schema, existing media URL semantics, and opt-in defaults. Verified with TypeScript project builds, zero-warning lint, the full test suite, production build, and a browser create/import/edit/reload/close-reopen smoke. | 2 个月前 | |
perf(editor): share one decoder per consecutive same-source video run Split clips from one source rendered one OffthreadVideo element per clip; playback across clip boundaries then staggered multiple concurrent decoders, which stutters the playhead. Audio already shares one element per consecutive run (ContinuousVideoAudio); the visual layer did not. TimelineComposition now groups consecutive same-source clips into shared visual runs: one video element decodes the whole run (trimBefore at the first clip's source frame), and per-clip appearance (opacity, zoom, border radius, filters) is applied from the active clip each frame. Clips with GL effects, background fill, transition extensions/entrances, transforms, keyframes, or filters keep their own element and split the run. Shared-path impact: TimelineComposition serves both the preview Player and headless export. Shared runs only change element count, not output frames: source-frame continuity is guaranteed by the existing canShareAudio predicate (sourceFrameAt adjacency), and per-frame styling mirrors ClipWrapper's appearanceAt, which was extracted to clipFade.ts (the only shared-path refactor; ClipWrapper behavior unchanged). Validation: tsc -b --force clean, oxlint 0 warnings, production build passes, sharedVisual.verify covers the share predicate. Real project: a 10-clip transition-heavy timeline correctly keeps per-clip elements (transitions must decode independently); after removing its transitions the same timeline renders 4 video elements (2 shared runs of 3 + 2 GL-effect clips) instead of 10. | 2 个月前 | |
lint: clear the 10 release-pipeline warnings - skill-loading.verify: 4 unnecessary regex escapes (" in literals) - media-dir: control-char check rewritten per codepoint (no-control-regex), unused catch binding dropped - ResourceBrowser: split PreviewCleanup context + hook into ResourcePreviewContext.ts (only-export-components); imports updated in FxThumb/TransitionThumb - transitionPreview: split previewTransitionType into transitionPreview.ts; component imports disambiguated with .tsx - useExportDialogModel / TimelineComposition: pass/construct the exact fields instead of the whole state/status object, making the effect deps truthful (oxlint-disable did not suppress the built-in react-hooks rule in this setup) npm test 137 green, tsc -b + build green. | 2 个月前 | |
lint: clear the 10 release-pipeline warnings - skill-loading.verify: 4 unnecessary regex escapes (" in literals) - media-dir: control-char check rewritten per codepoint (no-control-regex), unused catch binding dropped - ResourceBrowser: split PreviewCleanup context + hook into ResourcePreviewContext.ts (only-export-components); imports updated in FxThumb/TransitionThumb - transitionPreview: split previewTransitionType into transitionPreview.ts; component imports disambiguated with .tsx - useExportDialogModel / TimelineComposition: pass/construct the exact fields instead of the whole state/status object, making the effect deps truthful (oxlint-disable did not suppress the built-in react-hooks rule in this setup) npm test 137 green, tsc -b + build green. | 2 个月前 | |
fix(preview): keep transition media live Avoid replacing active video elements or blocking the Player while transition sources seek. Headless exports still wait for GLSL inputs. | 2 个月前 | |
fix(editor): numeric boundary guards and no-op reference stability Undo-history integrity (no-op contract): - reduce()'s transition-reconcile exit only allocates a new state when reconcileTransitions actually changed something; reconcileTransitions returns the ORIGINAL array when content is unchanged. Previously any rejected/no-op action on a timeline with >=1 transition produced a new state object, pushing a phantom undo step and clearing redo (Cmd+Z "did nothing" and lost redo after every clamped drag nudge). - moveItemWithGroups returns the original state when the clamped delta is 0 and no track change was requested. Numeric validation on tool/LLM-facing action channels (frame domain is integral; NaN stringifies to null and corrupts the persisted doc): - move: rejects non-finite startFrame, rounds fractional targets - split: rounds atFrame before boundary checks - retime: rounds action-supplied startFrame/durationInFrames (srcInFrame intentionally left fractional-capable for playbackRate) - reorderTrackItems.starts (apply_script channel): non-finite or negative pinned start rejects the whole action atomically; fractional starts are rounded - setVolume/setFade/setSpeed: reject non-finite inputs - setCanvas / tl.retarget: reject non-finite or <1 dimensions, round fractional ones (zero canvas divided nested-sequence layout) - updateMarker: rejects non-finite frame fields, clamps to >=0 integers (symmetric with addMarker's command-side clamp) Shared paths touched: reduce() exit (every per-timeline action), move/retime/split/speed/volume/fade/canvas/marker/reorder reducers. Valid inputs behave identically (round is a no-op on the integral frames every existing caller sends); only previously-invalid inputs change outcome, from corrupting state to being rejected or normalized. Verification: tsc -b --force clean; oxlint 0 warnings; new reducerGuards.verify covers no-op reference stability plus every guard above (all passing); full editor verify sweep 35/37 green - the two failures (mediaAssetUsage, transitionPreview) reproduce identically on a clean tree (pre-existing run-check bundling issues, not regressions); browser e2e on localhost:5199 (isolated dev profile): open project, add solid, undo, redo all correct with 0 console errors. | 1 个月前 | |
feat: land professional timeline, durable jobs, and safe media flows Add slip/rate-stretch, nested sequences, multicam sync, durable generation/export recovery, media intelligence, and hardened MCP/media boundaries while keeping the implementation local-first and cross-platform. | 2 个月前 | |
chore(release): prepare v0.2.0 Harden project storage, upload finalization, export recovery, watched-folder ownership, and native inference lifecycle boundaries. Add cross-platform desktop update packaging and checksum-gated GitHub Release publication while preserving the public v3 project schema, existing media URL semantics, and opt-in defaults. Verified with TypeScript project builds, zero-warning lint, the full test suite, production build, and a browser create/import/edit/reload/close-reopen smoke. | 2 个月前 | |
chore(release): prepare v0.2.0 Harden project storage, upload finalization, export recovery, watched-folder ownership, and native inference lifecycle boundaries. Add cross-platform desktop update packaging and checksum-gated GitHub Release publication while preserving the public v3 project schema, existing media URL semantics, and opt-in defaults. Verified with TypeScript project builds, zero-warning lint, the full test suite, production build, and a browser create/import/edit/reload/close-reopen smoke. | 2 个月前 | |
feat(agent): read installed plugin packs into the headless template catalog Headless sessions now see the same template catalog the renderer does, pack templates included: - server/plugins/extension-store.ts exports readInstalledPacks(), the on-disk reader behind the renderer's hydration. - offline-catalogs.ts merges those packs through pluginTemplates() — the very mapping the UI uses — after validating the envelope at the boundary: a pack whose mg-template items lack id/name/code is dropped rather than half-mapped into a template, and an unreadable pack store degrades to the bundled set. - offline-runtime.ts loads the catalog once per session (create() is async) and hands it to the AgentContext, so list_templates / search_templates / add_motion_graphic answer the same thing with or without a renderer. This closes the capability gap the previous commit documented. Supporting split: the pure pack → Tpl/zoom mapping moved out of src/library/pluginResources.ts (which imports ResourceBrowser.tsx) into src/library/pluginTemplateCatalog.ts, because the node-side build has no --jsx and pulled the component in. pluginResources.ts keeps the React hook and the resource-card mapping; the four import sites were updated. offline-runtime.ts also moved its context/status/failure projections into offline-session-view.ts — it had crossed the 500-line gate. Shared paths touched: the extension store gains one exported reader; the offline runtime splits two modules; the library split keeps one mapping implementation. Renderer behavior is unchanged (same function, same inputs). Verification: cli/cli.verify.ts installs a fixture pack into a throwaway HOME and asserts search_templates returns its template — the merge path, not just the bundled list; resourcePreview, offline-runtime, offline-mcp and verify:mcp pass; npm run build and npm run desktop:build:main pass; tsc -b clean; lint 0 warnings; registration and gate coverage pass; npm test 181/181 green. | 1 个月前 | |
feat(agent): add durable token-aware harness | 2 个月前 | |
feat(editor): choose the project frame rate from the timeline toolbar Every GUI path created 30 fps timelines. "New project" hardcodes 30, and "+ Sequence" and duplicate copy the active rate. Only MCP create_project could make a 60 fps timeline. A 60 fps export of a 30 fps timeline cannot use the browser path ("cannot retime the timeline yet") and is retimed by the local renderer. The timeline toolbar now shows the project frame rate next to the aspect-ratio picker, which is where the other canvas setting already lives, and lets you change it until anything is placed. It offers 24/25/30/50/60, the rates every export route renders without retiming. The browser path needs export fps == timeline fps, and the local renderer only accepts these as retime targets. A rate set elsewhere, such as 59.94 through MCP, is shown as the current value but cannot be picked again once changed. The rate is project-wide and locks once any timeline has clips, transitions, markers, link or multicam groups, or captions. All of those are frame counts at the current rate, and converting them all would be a much larger change than this issue needs. It is project-wide because nested sequences must share a rate and pool durations are counted in it. When the rate changes, time-based pool assets (video, audio, image, gif, svg) are recounted so they keep their seconds. Motion graphics keep their authored frame length, as they do when added from the library. The change is a reducer action (tl.setFps), so it is undoable, and agent drafts record it like any other edit. New timelines do not adopt the first clip's frame rate. Source fps is only known after server normalization and is not stored on the asset. Desktop imports skip that probe, and clips are placed before durations settle. Adopting on drop would mean re-probing and retiming placed clips inside the import pipeline. The export dialog now starts at the export rate nearest the timeline's own. It used to fall back to 30 for any rate outside the list, so a 59.94 timeline exported at 30 by default. Fixes #175. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> | 1 个月前 | |
chore(release): prepare v0.2.0 Harden project storage, upload finalization, export recovery, watched-folder ownership, and native inference lifecycle boundaries. Add cross-platform desktop update packaging and checksum-gated GitHub Release publication while preserving the public v3 project schema, existing media URL semantics, and opt-in defaults. Verified with TypeScript project builds, zero-warning lint, the full test suite, production build, and a browser create/import/edit/reload/close-reopen smoke. | 2 个月前 | |
fix: preserve offline edits and optimize editor workflows Recover conflicting offline project documents into verified, retry-safe copies without replacing the original project. Harden skill installation and media stream handling, deduplicate loudness work, reuse transcript projections, and load sequence options only when needed. Split oversized source modules while preserving their public exports. Expand affected verification coverage, add a macOS frame-sync CI job, and update vulnerable media dependency overrides. Shared-path impact: - Persistence recovery runs only for pending conflicting documents. Normal saves keep their existing format, media URLs, and ownership rules. Recovery confirms document and index writes before clearing pending state. - Agent and UI loudness paths retain existing volume limits and undo behavior; repeated sources share bounded analysis and stale sources are rejected. - Transcript, caption, preview, export, server, and desktop module moves keep existing call contracts. Differential checks cover projections and stored job reports; media transport checks cover ranges, empty files, and aborts. Validation: - Full TypeScript project build, zero-warning lint, and production build passed. - npm test preflight passed; all 172 test segments passed after registering the new verification files (TEST_CONCURRENCY=2 npm run test --ignore-scripts). - Final persistence changes passed project-store transport and recovery checks, lint, build, and the 561-verification gate coverage check. - npm audit --omit=dev reported zero vulnerabilities. - Real DeepSeek Agent invoked add_audio and normalize_loudness successfully. Browser checks covered import, save/reopen, undo/redo, and offline recovery with the original project and recovered media preserved. - Native macOS frame-sync checks and real FFmpeg media checks passed. The new remote macOS CI job has not yet run. | 1 个月前 | |
fix(agent): gate history shaking, localize overflow guidance, route missing-pack button Three follow-ups to the overflow-recovery work: - prepareContext no longer shakes stale tool results below the compaction trigger. Shaking is a compaction strategy; the previous version rewrote the model-visible history on every happy-path run. Under pressure the mechanical trim still runs first and recovers the budget without an LLM summary. - contextOverflowGuidance is locale-aware: the guidance now renders through t() (en users get the English entry that was previously dead dictionary data), and extractOverflowOriginal round-trips both spellings. - The chat missing-model-pack button now passes the tool result's settingsRoute to onOpenSettings, and SettingsDialog accepts initialVendor so the dialog opens on the model-pack page instead of the default pane. Shared paths: prepareContext (happy path restored to pre-shake behavior), chat onOpenSettings signature (route is optional; CapabilityGapBanner and other callers pass none), SettingsDialog (initialVendor is optional and defaults to the previous first-page selection). Verification: context-compaction and context-overflow-guidance verifies pass (shake gating and en-localization assertions added); 108 affected verifies pass; tsc -b clean; oxlint 0 warnings. | 1 个月前 | |
Merge PR #123 (FlexCrop) — crop feature only, agent governor excluded Trial merge of J160KU's FlexCrop onto v0.2.12 for CI validation. Two things had to be resolved beyond ordinary conflicts. The PR does not build. npm run build runs tsc -b, and on the PR branch alone that fails four ways: two unused imports in systemPrompt.ts and a cast in read-project-tools-check.verify.ts that omits selectedIds/selectedId and then reads them. npx tsc --noEmit passes, which is presumably what was run. Fixed here by completing the cast; the systemPrompt errors disappear with the revert below. The PR also carries 192 lines of agent-behaviour change that its title and summary do not mention, so those are left out of this merge: - src/agent/inspect-loop.ts hooks guardFlexCropToolLoop into tools.ts, the central dispatcher, where it can short-circuit ANY tool call. It caps frame inspection at 6 looks for every agent run — its own comment notes caption/MG verification needs more — and injects "stop", "do not recheck frames", "do not call run_code" into the result stream. - src/agent/sandbox-unavailable.ts turns run_code/probe_media into {ok: true, skipped: true} when E2B is unconfigured. Reporting an unavailable capability as success is the shape of open issue #114, which is fixed on main. - frames-tool.ts raises the frame-sampling cap from 8 back to 16, undoing the hardening in aafac5dc. tools.ts, systemPrompt.ts, probe-tools.ts, run-code-tools.ts, known-errors/SKILL.md and frames-tool.ts are therefore taken from main, and the two new modules plus their verifies are dropped along with their entries in the verify:agent-tools script. tools.ts contained nothing else, so FlexCrop loses nothing by this. FlexCrop itself is sound: storage stays a 0-1 canvas fraction so no migration is needed, pixel conversion is isolated in src/editor/flexCrop.ts with its own verify, edit_item previously rejected transform.crop as an unknown field so no existing agent call changes meaning, and every new verify was registered in test:serial by the author. CHANGELOG keeps the released 0.2.12 section and files FlexCrop under [Unreleased]; the tool catalog was regenerated rather than hand-merged. Local: tsc -b, lint (0 warnings), npm test (162 segments) all pass. | 1 个月前 | |
refactor(server): split firecrawl.ts by layer; restore a zero-warning lint firecrawl.ts was 849 lines against the 500-line limit in AGENTS.md:19, with three layers stacked in one file. They are now three files plus the entry: firecrawl-shared.ts 222 proxy-aware fetch, JSON body I/O, URL checks, format mapping, screenshot persistence firecrawl-scrape.ts 255 the single-shot endpoints: scrape, search, map firecrawl-crawl.ts 305 the polling jobs: site crawl, batch scrape firecrawl.ts 108 route binding and the error envelope Splitting the handlers two ways rather than one keeps every file under the limit — submit-and-poll is a different shape from request-and-return, so that is where the seam belongs. firecrawl.ts still re-exports FirecrawlPluginOptions, buildActions, saveScreenshot and wrapExecJs, so firecrawl.verify.ts and any future caller import from the same place. Also fixes two lint warnings I introduced in 2991a24f: workspaceDialogs.tsx and dashboardDialogs.tsx each exported lazy components AND a prefetch hook, and react(only-export-components) is right that this defeats Fast Refresh for the file. The import thunks and the hook built on them moved to workspaceDialogLoaders.ts / dashboardDialogLoaders.ts, leaving the .tsx files exporting components only. npm run lint is back to zero warnings — I had not been running it on those commits, only tsc and the tests. Worth knowing: npx tsc --noEmit accepts imports that npm run build rejects. The build runs tsc -b, which picks up the server project's verbatimModuleSyntax and demands type-only imports for types. Three of the generated import lines here passed --noEmit and failed the build. Validation: tsc -b via npm run build, npm run lint (0 warnings), npm test (162 segments), firecrawl.verify, and all five dashboard dialogs opened in the browser after the module reshuffle. | 1 个月前 | |
refactor(server): split firecrawl.ts by layer; restore a zero-warning lint firecrawl.ts was 849 lines against the 500-line limit in AGENTS.md:19, with three layers stacked in one file. They are now three files plus the entry: firecrawl-shared.ts 222 proxy-aware fetch, JSON body I/O, URL checks, format mapping, screenshot persistence firecrawl-scrape.ts 255 the single-shot endpoints: scrape, search, map firecrawl-crawl.ts 305 the polling jobs: site crawl, batch scrape firecrawl.ts 108 route binding and the error envelope Splitting the handlers two ways rather than one keeps every file under the limit — submit-and-poll is a different shape from request-and-return, so that is where the seam belongs. firecrawl.ts still re-exports FirecrawlPluginOptions, buildActions, saveScreenshot and wrapExecJs, so firecrawl.verify.ts and any future caller import from the same place. Also fixes two lint warnings I introduced in 2991a24f: workspaceDialogs.tsx and dashboardDialogs.tsx each exported lazy components AND a prefetch hook, and react(only-export-components) is right that this defeats Fast Refresh for the file. The import thunks and the hook built on them moved to workspaceDialogLoaders.ts / dashboardDialogLoaders.ts, leaving the .tsx files exporting components only. npm run lint is back to zero warnings — I had not been running it on those commits, only tsc and the tests. Worth knowing: npx tsc --noEmit accepts imports that npm run build rejects. The build runs tsc -b, which picks up the server project's verbatimModuleSyntax and demands type-only imports for types. Three of the generated import lines here passed --noEmit and failed the build. Validation: tsc -b via npm run build, npm run lint (0 warnings), npm test (162 segments), firecrawl.verify, and all five dashboard dialogs opened in the browser after the module reshuffle. | 1 个月前 | |
docs: translate code comments to English | 3 个月前 | |
fix(quality): revive watchdogs, fix orphan checks, and unify skill count Resurrect two silent quality gateways and wire them into the test suite, catch up CHANGELOG and skill-count docs, and convert every orphaned verify/check file into a tracked, npm-wired regression. Quality watchdogs (now in pretest): - verify:shortcuts: catalog count 54->56, keymap scope, native-copy - verify:i18n: check-i18n.mjs now exempts test files; 4 real UI files wrapped in t(); 62 missing en keys added across 9 dict slices - Fixed stableOverrideKeys merging distinct legacy caption overrides when wordRef is empty (3 overrides collapsed to one key) - Fixed buildFollowupWidget silently dropping option-less choice fields (now degrades to a prompt line) - Fixed apply_script repack being silently rejected by the same-track overlap guard: reorderTrackItems now accepts explicit starts and apply_script rebuys atomically per track Docs: - CHANGELOG [Unreleased]: SQLite migration, session-recovery, chat-load-perf, Geist fonts, platform inference routing; fixed link refs - Skill count unified to 26 (README/README_ZH/AGENTS.md/tools.ts) Verify/check cleanup: - Promoted 81 .check.ts to tracked .verify.ts and deleted the originals - Fixed 20+ stale assertions (PROJECT_VERSION 3->7, sourceRevision, caption-track structure, legacy index selectors, shader hash re-pins, abort wiring refactor, stock orientation enum) - 8 new verify groups (editor/captions/agent-tools/components/ media-persist/export-gl/server-extra/sqlite-migration) wired into pretest - Orphan verify count: 158 -> 0 Full npm test, tsc -b --force, and npm run lint all pass. | 2 个月前 |
| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
| 1 个月前 | ||
| 2 个月前 | ||
| 1 个月前 | ||
| 18 天前 | ||
| 12 天前 | ||
| 24 天前 | ||
| 18 天前 | ||
| 2 个月前 | ||
| 2 个月前 | ||
| 18 天前 | ||
| 18 天前 | ||
| 2 个月前 | ||
| 2 个月前 | ||
| 3 个月前 | ||
| 3 个月前 | ||
| 18 天前 | ||
| 2 个月前 | ||
| 1 个月前 | ||
| 2 个月前 | ||
| 3 个月前 | ||
| 2 个月前 | ||
| 12 天前 | ||
| 3 个月前 | ||
| 3 个月前 | ||
| 2 个月前 | ||
| 2 个月前 | ||
| 2 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 2 个月前 | ||
| 3 个月前 | ||
| 3 个月前 | ||
| 2 个月前 | ||
| 2 个月前 | ||
| 2 个月前 | ||
| 1 个月前 | ||
| 2 个月前 | ||
| 2 个月前 | ||
| 2 个月前 | ||
| 1 个月前 | ||
| 3 个月前 | ||
| 1 个月前 | ||
| 2 个月前 | ||
| 2 个月前 | ||
| 2 个月前 | ||
| 2 个月前 | ||
| 2 个月前 | ||
| 2 个月前 | ||
| 2 个月前 | ||
| 2 个月前 | ||
| 2 个月前 | ||
| 2 个月前 | ||
| 1 个月前 | ||
| 12 天前 | ||
| 12 天前 | ||
| 2 个月前 | ||
| 2 个月前 | ||
| 2 个月前 | ||
| 2 个月前 | ||
| 2 个月前 | ||
| 2 个月前 | ||
| 1 个月前 | ||
| 2 个月前 | ||
| 2 个月前 | ||
| 30 天前 | ||
| 30 天前 | ||
| 2 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 2 个月前 | ||
| 12 天前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 2 个月前 | ||
| 2 个月前 | ||
| 3 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 2 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 2 个月前 | ||
| 2 个月前 | ||
| 3 个月前 | ||
| 30 天前 | ||
| 30 天前 | ||
| 2 个月前 | ||
| 2 个月前 | ||
| 12 天前 | ||
| 2 个月前 | ||
| 12 天前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 2 个月前 | ||
| 2 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 2 个月前 | ||
| 18 天前 | ||
| 2 个月前 | ||
| 2 个月前 | ||
| 3 个月前 | ||
| 2 个月前 | ||
| 1 个月前 | ||
| 2 个月前 | ||
| 2 个月前 | ||
| 2 个月前 | ||
| 2 个月前 | ||
| 2 个月前 | ||
| 1 个月前 | ||
| 2 个月前 | ||
| 2 个月前 | ||
| 2 个月前 | ||
| 1 个月前 | ||
| 2 个月前 | ||
| 1 个月前 | ||
| 2 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 3 个月前 | ||
| 2 个月前 |