Skip to content

Commit 06bdbe0

Browse files
preston176mazeincodingkcfancherAspeninicyphercodes
authored
Transcript-driven editing (Descript model) + build/test/DB/rebrand fixes (#3)
* style: hover of copy markdown button * fix effects button in light mode * fix inconsistency in blur intensity * changelog: improve phrasing for timeline clip gap description * changelog open graph image * fix changelog opengraph image * changelog: fix date * fix content-collections implicit content deprecation * close effects after last effect removal + spacing * fix: invert logos in /sponsors when dark mode * button color * feat: add changelog link to footer * fix: outline button styling * replace opencut assets * chore: upgrade tailwind-related deps * feat: branding page * rename branding page to brand * checker pattern for brand asset cards (branding page) * fix: auto scroll on link * add context menu (w/ brand options) to header logo allows downloading brand assets directly from the logo in the header * refactor: remove foreground variant from button, make it default * dialog: decrease gap * fix: fallback to opus codec * fix blog build when marble cms isn't available * Fix transcription sample-rate handling (#740) Fix for #703. Adds an optional sampleRate parameter to the decodeAudioToFloat32() function. The caption transcriber passes in a value of 16000 to prepare audio in the format Whisper expects. * Update OpenCut logo in README (#741) * changelog: new version with already-made changes * fix: assets having unreasonable widths and heights * changelog: add already-completed item * untrack .cursor/ * chore: gitignore * feat: masks, properties refactor, shaders, storage migrations, and more * fix: resolve typescript/lint errors * chore: init cloudflare * chore: update botid * chore: next-env * chore: normalize line endings * fix: add missing file * feat: init desktop app * chore: rename proxy to middleware * chore: update dep * chore: add missing open-next config * chore: remove dead songs tab * refactor: clean up number formatting and step handling in property tabs * docs: rewrite agents file * feat: add published status to changelog entries * refactor: move packages sub-folders into web app * feat: add shared Rust crates for cross-platform time utilities Introduce `bridge` proc-macro crate with `#[export]` attribute that conditionally applies `wasm_bindgen` for WASM builds, and `time` crate porting all timecode/frame utilities from TypeScript to Rust. * chore: remove accidentally committed Zone.Identifier stream * fix(security): replace new Function() with safe math expression parser (#753) * fix(security): replace new Function() with safe math expression parser Replace the dangerous new Function() constructor in evaluateMathExpression() with a recursive descent parser that safely evaluates basic arithmetic expressions. The Function constructor is a security anti-pattern that could enable arbitrary code execution if the validation regex is ever bypassed or relaxed. The new parser: - Only supports numbers, +, -, *, /, parentheses, and whitespace - Has no eval-like functionality that could execute arbitrary code - Maintains backward compatibility with existing expressions - Handles operator precedence and parentheses correctly Fixes #725 * Apply suggestion from @coderabbitai[bot] Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --------- Co-authored-by: Maze <167211895+mazeincoding@users.noreply.github.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * chore: remove unused middleware file * fix: remove OpenNext Cloudflare initialization * chore: untrack files * chore: update .gitignore * chore: update deps * fix: improve DATABASE_URL validation in web environment schema * fix: eliminate first-paint flicker in landing handlebars * refactor: name magic numbers and rename el in landing handlebars * style: remove unnecessary margin from keyframe * feat: add keyframe graphs button * refactor: standardize icon component props * style: swap ripple icon * feat: migrate time utilities from typescript to rust WASM * refactor: centralize track placement into its own module * fix: allow timeline paste and duplicate commands to reassign updated tracks * fix: build wasm before bun install in CI * fix: normalize vertical drag direction in preferIndex placement * feat: centralize selection management * feat: separate audio from elements * refactor: move track pruning into a post-command reactor * feat: migrate GPU renderer from WebGL to wgpu/WASM * fix: align background blur with effect blur intensity scale * fix: don't crash when GPU isn't available * fix: update tooltip text for graph editor button * refactor: split constants by domain and isolate timeline panel from core * chore: sort root package.json scripts * fix: use published opencut-wasm package * refactor: update command execute methods to return CommandResult | undefined * chore: update opencut-wasm to 0.1.3 * chore: remove transcription envs * refactor: swap out manual tabs withthe shared tabs component in stickers * style: tabs * refactor: rewrite animation system to use bindings and scalar channels * fix: subscribe to playback time so keyframe button updates on frame change Made-with: Cursor * chore: remove comment * refactor: unify element updates into pipeline and centralize ripple * docs: document rust/apps architecture in AGENTS.md * fix: preserve negative start times during timeline drag * feat: graphs popover * chore: addition to last merge * refactor: use useShiftKey hook in bezier graph * refactor: split presets into data definitions and storage store * fix: whitespace * fix: clean up * fix: clean up graph editor * refactor: split effects and masks into dedicated rust crates, introduce MediaTime and FrameRate * fix: place new overlay tracks at the top of the timeline * fix: respect scale lock when editing keyframed scale * feat: select element on insert * fix: use line-height-based bounds for text elements * fix: fix timeline width calculation for tick-based time * style: compact dropdown and context menu styling * refactor: structured track layout per scene * fix: clicking a selected element collapses multi-selection * changelog: add entry * fix: convert media asset duration to ticks on insert * fix: one more * refactor: gpu webgl fallback and batched command encoding Made-with: Cursor * feat: wasm compositor Made-with: Cursor * feat: position animated independently per axis Made-with: Cursor * feat: cinematic bars, diamond, heart, and star masks Made-with: Cursor * feat: clipboard with keyframe paste Made-with: Cursor * feat: expandable keyframe lanes in timeline Made-with: Cursor * refactor: rendering pipeline Made-with: Cursor * fix: text measurement, rotation handle, video cache races, playback sync Made-with: Cursor * changelog: 0.3.0 Made-with: Cursor * chore: bump opencut-wasm to 0.2.5 * fix: use published opencut-wasm package * fix: stable background blur previews in settings * chore: remove filters tab from assets panel * feat: graph editor for flat keyframe segments * fix: linter * changelog * changelog: publish 0.3.0 * Add support for basic SRT subtitle import to Captions panel (#756) * Add SRT subtitle import to the captions panel [Modified from 0bd641b4102c827ef8d218ada099229f73396077, 2026-03-28 08:46:23 -0400] Parse .srt files into chunks and insert them as text elements on a new text track using the existing caption styling defaults. Displays a warning when malformed cues are skipped during import. * Batch caption text insertion [Modified from eece3bbc5f0578a24a4f90f0ab87fe5ca37e94e9, 2026-03-28 09:13:18 -0400] Batch caption insertion in a BatchCommand so that undo will batch undo all subtitle insertion from either srt file or ai transcriptions. * Move subtitle parsing into a shared interface [Modified from e84404b7857f8c611eda7a65b5d61ee6c9823d75, 2026-03-28 09:52:31 -0400] Extracted the srt specific extraction behind a generic shared interface to allow easier adding of other subtitle formats in the future. * Apply better default styling for subtitles. [Modified from b5deec4285f99873ba5bf57971f7bcab26e8c743, 2026-03-31 11:49:34 -0400] Shrink font size, aim to layout 5% above bottom, keep subtitle width 80% of video, wrap text on new lines if too long. * Update Captions Panel UI Split captions panel into Generate and Import tabs as they are two different approaches for adding captions. * Add biome formatting fixes * Add .ass subtitle import support Extend subtitle import to accept .ass files alongside .srt, map supported .ass styles onto imported text elements, and display warnings when unsupported .ass features are skipped. * Change “Import subtitles” button style * refactor: review fixes * fix: convert subtitle times from seconds to ticks * refactor: replace captions tab switcher with import action button --------- Co-authored-by: Maze Winther <mazewinther@gmail.com> * changelog * chore: hide preview grid guide until v0.4.0 * chore: hide logos sticker category * chore: fix comment * refactor: move constants to domain modules * fix: timeline element resizing * chore: remove unused import * fix: keyframe removal state * test: remove keyframe query * style: export button icon size * feat: draft persistence on Form via persistKey * docs: remove features section * docs: update file structure to be accurate * docs: formatting * feat: editor feedback * chore: add .tokeignore * feat: transcription diagnostics * feat: add support for technical details in changelog entries * docs: fill in v0.3.0 technical changelog entries * changelog: fix date * fix: copy to markdown formatting * feat: changelog now supports markdown within entries * changelog: update to be more accurate * fix: turbo * feat: graph editor supports multiple properties at once * chore: remove debugging throughout codebase * changelog: another accuracy fix * fix: remove redundant "opencut" prefix from storage key * feat: new-release notice and colocate changelog under domain * fix: show changelog notice when stored version is missing or older * tests: remove * chore: force deployment * fix: skip unknown actions in keyboard shortcuts help * fix: remap split-element keybinding to split * chore: force deployment * changelog: rephrasing * fix: repair scene tracks broken by v23-to-v24 migration * feat: move/resize multiple elements * review: fixes * tests: delete * fix: load dotenv before env validation in drizzle config * fix: correct schema path in drizzle config * fix: audio waveform height in timeline * chore: formatting * feat: history view in feedback popover * feat: text / custom masks, fix preview pan, refactor selection * feat: clip volume line and timeline waveform improvements * fix: align LayerUniformBuffer to 16 bytes for WebGL compat * chore: update opencut-wasm version to 0.2.6 in Cargo.lock * fix: default audio waveform color from timeline theme * delete legacy folder * fix: migrate commands to CommandResult selection contract * docs: add prerequisites for rust * fix: WebGL frame rendering and opencut-wasm 0.2.8 * refactor: restructure files to their domains, new preview overlay system, and dep graph * docs: ts conventions to agents.md * chore: delete ffmpeg public assets * fix: project loading thing * chore: formatting * refactor: migration run() and pure v1-to-v2 transform * fix: wasm WebGL only until WebGPU is fixed * chore: bump opencut-wasm version * chore: remove stale files * perf: improve rendering performance by ~20x * fix: surface unsupported video codec on import, fix Input leaks * refactor: centralize editor interaction state Move timeline and preview gestures into dedicated controllers so hooks stay thin around editor state and DOM events. This also routes drag and playback synchronization through manager APIs and reuses persistent wasm surfaces so scrubbing, seeking, and preview rendering stay in sync. Made-with: Cursor * fix: model editor time as wasm MediaTime ticks end-to-end * fix: follow-up to prev commit * fix: linter * refactor: split animation helpers by domain * refactor: thinner hooks * fix: restore README header, badges, links, and star chart Made-with: Cursor * chore: remove accidental file Made-with: Cursor * chore: untrack cargo-tools files * chore: switch from biome to eslint + prettier; fix ton of lint issues * refactor: unify element params * chore: cleanup * refactor: animate params over time * refactor: split mask snap dispatch Made-with: Cursor * refactor: make mask renderer dispatch explicit Made-with: Cursor * refactor: type mask handle identifiers Made-with: Cursor * refactor: split builtin and freeform masks Made-with: Cursor * docs: capture mask rust migration notes Made-with: Cursor * fix: address review findings in masks substrate - Remove dead ternary in getPreferredEdges (always returned undefined; the all-false object was never reached by Object.values().some(Boolean)) - Document why drawWithFeather descriptors zero the feather field in the compositor (analytical renderer handles softness itself; zeroing prevents a second JFA feather pass) - Remove legacyType from V30→V31 migration (not consumed anywhere; a down-migration can map type:"freeform" back to "custom" directly) - Add V30→V31 test coverage: skip-guard branches, freeform passthrough, elements without a masks array - Add snap tests for vertical-edge (bottom) and uniform-scale handle paths Co-authored-by: Cursor <cursoragent@cursor.com> * refactor: remove dead tangent handle variant The { kind: "tangent" } MaskHandleId variant was never constructed — neither getFreeformDisplayHandles nor any other code ever produced a tangent handle object. The in/out control-arm branches in computeFreeformParamUpdate, the tangent case in maskHandleIdKey, and the CUSTOM_MASK_TANGENT_SIZE rendering path in mask-handles.tsx were all unreachable. Removed all four dead paths and removed "tangent" from MaskHandleKind. Co-authored-by: Cursor <cursoragent@cursor.com> * refactor: collapse mask definition registry to a single keyed registry The freeform mask was assembled on every getMaskDefinition("freeform") call, re-spreading params and injecting a wrong SquareIcon placeholder, with a type cast to suppress the structural mismatch. Root cause: MasksRegistry was keyed on BuiltinMaskType, so freeform had no entry. - Re-key MasksRegistry from BuiltinMaskType to MaskType - Register freeform alongside builtins in registerDefaultMasks, with PenToolAddIcon as its menu icon - getMaskDefinition and getMaskDefinitionsForMenu become plain registry lookups with no branching, no manual spreading, no cast - Rename RegisteredBuiltinMaskDefinition to RegisteredMaskDefinition and tighten computeParamUpdate return type to Partial<BaseMaskParams> Co-authored-by: Cursor <cursoragent@cursor.com> * chore: remove file * chore: ignore migrations file * docs: move NOTES.md file * docs: fix readme * chore: update copyright year * feat: transcript-driven timeline editing Adds a Descript-style "edit by editing the transcript" panel that drives real cuts on opencut's timeline. - Switch transcription worker to word-level timestamps (return_timestamps: "word") and group words back into sentence-like segments for display. Subtitles consumer keeps working since segments still expose start/end. - New apps/web/src/transcript-editor/ module: - types.ts: TranscriptDocument with per-word IDs and selection state. - apply-deletion.ts: maps word ranges to splitElements + deleteElements calls, classifying each covering element as fully-inside, partial- left, partial-right, or straddle-both. Latest-first iteration so earlier ranges keep stable timeline coordinates. - transcript-panel.tsx: panel UI. Click word = seek; shift/cmd-click or double-click = toggle selection; delete button cuts the timeline. - Wire panel into the editor route as a toggleable "Transcript" tab in the right column (alongside Properties). Also removes ~90 stale Electron files from apps/desktop that the opencut merge auto-kept; HEAD now matches opencut's apps/desktop tree. Known limitation: each transcript edit emits 3-5 separate timeline commands (one per split / delete) so undo steps through them individually. BatchCommand wrapping deferred to a follow-up. * docs: rewrite README without upstream-fork framing * feat: descript-style transcript panel Major UX overhaul of the transcript editor: - Document-style layout: timestamps in a left margin, generous line-height, reading-width container, prose typography. Each segment is its own paragraph with a 0:00 timestamp. - Active-word highlighting: as the video plays, the current word gets an accent background and auto-scrolls into view. Re-renders driven by editor.playback subscription via useEditor. - Range selection: shift-click extends selection from the last clicked word; cmd/ctrl-click toggles a single word; plain click seeks and anchors the range. - Filler-word detection: scans words against a stop-list (um, uh, like, you know, etc.). Flagged words get a dotted amber underline. Header shows "Remove N fillers" button for one-shot bulk removal. - Cmd+F find: opens a search bar, highlights matches in yellow with the active match in solid yellow, Enter cycles forward, Shift+Enter back, Esc closes. - Single undo per edit: applyTranscriptDeletions now constructs commands directly, executes them in sequence to thread split IDs, then pushes a BatchCommand to the command manager. One Cmd+Z reverses the whole edit. Supporting changes: - src/env/web.ts: defaulted DB/Redis/auth/CMS/FreeSound vars so the editor boots without backing services. Routes that actually use them will fail at use; transcript editor doesn't touch any of them. - src/transcription/models.ts: swap whisper-tiny/small/medium from onnx-community to Xenova exports. The onnx-community ASR exports don't carry cross-attentions, breaking return_timestamps:"word". * chore: rebrand to OpenScript and remove marketing surfaces Renames @opencut/web -> @openscript/web (root scripts and apps/web/package.json). Updates SITE_INFO and visible UI strings (header, footer, onboarding, mobile gate, storage dialog, legal pages, browser-compat banner, better-auth appName). Removes marketing routes that don't belong in an editor product: - /blog, /roadmap, /sponsors, /contributors, /brand, /changelog - src/changelog/* (release notes data + ChangelogNotification component) - src/components/landing/* (hero etc.) - src/components/gitHub-contribute-section.tsx - src/site/sponsors.ts and src/site/external-tools.ts (now-unused) Replaces / with a redirect to /projects so users land on their project list. Strips the OpenCut analytics tracker (databuddy) and the OpenCut nav links from Header. Footer reduced to copyright + Privacy/Terms. Kept (not user-visible): - LICENSE (MIT requires preserving the OpenCut copyright notice) - localStorage/zustand persist keys ("opencut-keybindings", "opencut-storage-persist-dismissed") to avoid orphaning user data - eslint plugin name "opencut/prefer-object-params" in disable comments - The opencut-wasm npm dep (it's the upstream-published artifact) - public/logos/opencut/svg/logo.svg as DEFAULT_LOGO_URL with a TODO to replace with an OpenScript wordmark * chore: remove placeholder logo and discord references - editor-header.tsx: replace OpenCut wordmark <Image> in the project dropdown trigger with a text "OS" mark. Drop the Discord menu item and its now-orphan separator. Cleans up unused imports (Image, Link, FaDiscord, DEFAULT_LOGO_URL, SOCIAL_LINKS, DropdownMenuSeparator). - onboarding.tsx: drop the third onboarding step (the Discord pitch) and the inert roadmap link from step 2. Replace the dynamic step title with a static "OpenScript Onboarding" label for the screen-reader DialogTitle. DEFAULT_LOGO_URL and SOCIAL_LINKS still exist in src/site for the privacy/terms pages that link out to GitHub/X (currently inert "#" hrefs); replace those when real social accounts exist. * fix: repair production build — missing guards + object-param call sites next build was failing on the TypeScript gate (12 errors); three of the root causes were also live runtime bugs. Fix all and add the action plan. - actions/keybindings: implement isShortcutKey + isActionWithOptionalArgs; persistence.ts imported guards that never existed. The 9 pre-existing persistence tests now compile and pass. - stickers: register providers with object params (was registering every built-in provider under key `undefined`, breaking the sticker library). - storage migrations: construct IndexedDBAdapter / call set() with object params in runner + v1-to-v2 (was indexedDB.open(undefined), silently breaking legacy-project migration). - timeline tests: wrap raw numbers with MediaTime brand helpers. - bun.lock: reconcile with package.json (was stale; broke --frozen-lockfile). - add PLAN.md. Verified: tsc --noEmit clean (was 12 errors), next build exits 0, keybindings suite 9/9. * test: run editor-core tests under bun by instantiating real opencut-wasm opencut-wasm is a wasm-pack "bundler"-target package: its entry calls wasm.__wbindgen_start() on an import that bun's test runner doesn't instantiate, so every test reaching @/wasm crashed at module load — the entire editor core (timeline, masks, transcript, storage) was untested. Add a bun test preload that performs the bundler step itself against the real wasm (read bytes, instantiate with the glue as the import object, wire it in, run the start hook), then redirect the bare "opencut-wasm" specifier to the initialized glue. Tests now exercise the actual Rust implementation, not a hand-written stub that could drift from source. Result: 193 tests across 30 files now run (was ~0 in the wasm graph) — 192 pass, 1 skip, 0 fail. Surfaced failures fixed: - resolve.test: time spans used fractional ticks (2.5/5.5); MediaTime is integer ticks. Use integer positions that preserve the overlap scenario. - snap.test (box scale): a centered uniform scale snaps both edges to ±100; expect both vertical guides, not just the right one. - snap.test (point split): De Casteljau subdivision of the straight a→b segment yields collinear handles (-0.1 in / +0.1 out), not zero. - snap.test (text mask): needs a DOM/canvas font-metrics context; skip under bun, runs in a browser/jsdom env. Verified: tsc --noEmit clean, bun test 192 pass / 1 skip / 0 fail. * feat: make transcript edits correct across undo, ripple, and reload The transcript panel stored a per-word `deleted` flag and mutated it on delete, so it drifted from the actual timeline: undo restored the media but left words struck-through, and reload showed the full text over an already-cut timeline. Deletions also bypassed reactors, orphaning tracks. - Derive `deleted` from timeline coverage instead of storing it. A word is deleted when its midpoint is no longer covered by media (new coverage.ts, 7 unit tests). The panel subscribes to the active scene's tracks and recomputes, so undo/redo and reload stay correct automatically — the cut media returning makes the word reappear. `deleted` is dropped from TranscriptWord; filler-words/search take a deletedWordIds set. - Run reactors after a transcript delete (CommandManager.reactToExternalChange). Deletions execute sub-commands incrementally to read live element IDs, so they use push() rather than execute(); push() skips reactors, which left empty audio/overlay tracks behind. (Ripple-on-delete stays in Phase 2 — it needs word-coordinate re-sync.) - Persist the transcript (text + word timings only) per project and rehydrate on mount, so it survives tab-toggle and reload. Deleted-state is NOT stored; it is re-derived from the persisted timeline, so the two cannot diverge. Transcript is removed when its project is deleted. Verified: tsc clean, bun test 199 pass / 1 skip / 0 fail, next build exits 0. * feat: transcript model/language pickers + inline word editing Toward the "edit by transcript" promise (Phase 2, parts a+b): - Model + language selectors in the transcript empty state, threaded into transcribe(). Previously locked to whisper-small/auto despite 4 models and 9 languages being defined but unused. Each model shows an approximate first-use download size (new approxDownloadMb) so users on slow/metered connections can pick Tiny; a note clarifies it runs locally in-browser. - Inline word correction: double-click a word to edit its text (Enter/blur commits, Escape cancels), persisted with the transcript. This is a transcript-only edit — it changes the displayed/exported text, not the timeline or word timing — so it is low-risk and independent of the coverage-derived deleted state. Ripple-close on delete (Phase 2c) is intentionally not included: closing timeline gaps conflicts with deriving deleted-state from gaps and needs an architecture decision (tracked separately). Verified: tsc clean, bun test 199 pass / 1 skip / 0 fail, next build exits 0. * feat(wip): Descript model foundation — data model, mapping, ripple-open, transcript store Phase 2c foundation (steps 1-4 of 10). Inert scaffolding for the destructive Descript transcript model; not yet wired into the delete flow. - types.ts: v2 model — TranscriptWord.deleted, DeletedRange + RemovedPiece (lossless element capture for restore), TranscriptDocument.deletedRanges. - mapping.ts: pure source<->timeline mapping (frozen source seconds minus removed durations of earlier cuts; in-gap clamps to gap edge); integer-tick math. - ripple/open.ts: rippleOpenElements, the inverse of rippleShiftElements (restore). - core/managers/transcript-store.ts: EditorCore-owned holder for the doc, wired into useEditor — lets a Command mutate the transcript from global undo/redo. - storage SerializedTranscript.version loosened to number (v1->v2 migration lands with the wiring). DESIGN.md records the full plan. Tests: 14 new (types/mapping/ripple-open/store). tsc clean, full suite 213 pass / 1 skip / 0 fail. * docs: record Descript-model test-infra findings (EditorCore under bun; save side-effect) EditorCore constructs under bun, but updateTracks triggers SaveManager -> IndexedDB (absent in bun) and would reject — so the fidelity test should drive a pure split/trim+ripple transform on fixture tracks rather than a live editor. Captures the delete input contract (contiguous runs) for the panel rewire. * feat(wip): pure transcript delete transform + split helper Phase 2c steps 5-6 (delete side). Both pure and unit-tested on fixture tracks — no editor/save coupling, so they are the testable core of the destructive model. - timeline/split-element.ts: splitElementAtTime — a pure, generic, retime-aware element split (verbatim mirror of SplitElementsCommand's per-element math, snap-source-once). Net-new split test coverage. (Kept separate from the command for now to avoid touching untested split behavior; noted to keep in sync.) - transcript-editor/delete-transform.ts: deleteRangeFromTracks — removes a [start,end) range across all tracks (split straddlers, drop contained), captures the removed slices as RemovedPiece (with offsetTicks for restore), and ripple-closes the gap uniformly. Returns {afterTracks, removed, durationTicks}. Tests cover straddle-both, fully-contained, no-overlap, ripple-close, capture, and trim math. tsc clean; full suite 219 pass / 1 skip / 0 fail. * feat(wip): pure restore transform + delete<->restore round-trip fidelity gate Phase 2c step 6c/7. restoreRangeToTracks is the pure inverse of deleteRangeFromTracks: ripple-open the gap and re-insert the captured slices (lossless but for a fresh id). The round-trip test is the primary correctness gate for the destructive model and now runs purely (no editor): - straddle-both reconstructs exact contiguous source coverage (startTimes, durations, trimStarts). - a multi-element timeline stays contiguous and the same total length after delete->restore (catches tick drift). - a retimed element round-trips without drift and keeps its retime config. tsc clean; full suite 222 pass / 1 skip / 0 fail. * feat(wip): transcript edit plan functions + TranscriptEditCommand Phase 2c step 6b/6c. Composes the pure transforms into the editable model: - plan.ts: planTranscriptDelete / planTranscriptRestore — pure (modulo a fresh range id). Delete processes runs latest-source-first (so each cut's timeline position, derived from pre-existing cuts, is unaffected by the batch's earlier cuts), marks words deleted, and records DeletedRanges; restore re-opens the gap at its current position, reconstructs the media, un-marks the words, drops the range. Tested: delete records+ripple-closes, restore reverses, unknown id no-op. - transcript-edit-command.ts: TranscriptEditCommand snapshots tracks + doc and applies the plan; undo/redo replay snapshots so one undo reverts the timeline AND the transcript document atomically (the desync fix). dispatchTranscriptEdit records via push() (the plan already rippled — avoid the global ripple toggle double-applying) + runs reactors. Known v1 edge noted: restoring a cut that emptied+pruned a whole track can't rebuild it. tsc clean; full suite 225 pass / 1 skip / 0 fail. * feat(wip): v1->v2 transcript migrator (pure) Phase 2c step 9. migrateTranscript normalizes a persisted transcript to the v2 document shape. v1 (pre-Descript) stored no deleted/deletedRanges and relied on coverage-derived deletion against an already-cut timeline; migrate it once by deriving each word's `deleted` from current timeline coverage and starting an empty deletedRanges log — so pre-v2 cuts show struck but are not restorable (documented). Never mutates the timeline. v2+ docs pass through. Tested: v1 derivation, v2 pass-through, no-tracks fallback. tsc clean. * docs: specify the remaining transcript panel wiring + in-app checklist The Descript-model backend is complete and tested (transforms, plan, command, migrator). The panel rewire is the last integration and is the part that needs in-app verification, so it is specified precisely here (exact deltas + a live test checklist) for a turnkey hand-off rather than shipped unverified. * feat: wire transcript panel to the Descript edit model Phase 2c final step. The panel now drives the destructive, undoable, restorable model end to end: - reads the document from the editor-owned TranscriptStore (reactive), so a command can mutate it from global undo/redo; generate/load/edit write the store. - deleted state comes from the stored `deleted` flag (not coverage). - on mount, loads + migrates (v1->v2) the persisted transcript into the store. - delete builds contiguous runs from the selection and dispatches a TranscriptEditCommand (split + delete + ripple-close + tombstone, one undo step). - seek and the active-word highlight go through the source<->timeline mapping, so they stay correct after cuts. - clicking a struck-through word restores its whole cut (ripple-open + reconstruct); pre-v2 cuts stay non-restorable. - retires coverage from the render hot path (kept only for the v1->v2 migrator) and removes the superseded apply-deletion.ts. tsc clean; full suite 228 pass / 1 skip / 0 fail; next build exits 0. Needs in-app verification (ripple/restore/seek/undo/reload) — checklist in DESIGN.md. * feat: transcription robustness (Phase 3) - Default to the Tiny model (~40 MB): fast even on the single-threaded wasm fallback (no WebGPU / no cross-origin isolation), where larger models are painfully slow. The picker still offers Small/Medium/Large. - Gate Generate on timelineHasAudio: a silent/empty timeline now shows "This timeline has no audio to transcribe" instead of downloading the model and running Whisper on silence (verified in-app). - Cancel button during transcription (the service already supported cancel); cancelling returns to the start rather than surfacing an error. - Preserve recognized text when a model returns no word-level timing, so the user still sees what was transcribed instead of a bare error. Verified: tsc clean, suite 228 pass / 1 skip / 0 fail, next build exits 0; in-app confirmed default=Tiny and the audio gate. * fix: repair DB tooling + correct README setup (Phase 4) - drizzle.config.ts schema path pointed at ./src/lib/db/schema.ts (no such dir), so db:generate/migrate/push all failed. Fix to ./src/db/schema.ts. - The committed migration created a dead `waitlist` table and omitted the `feedback` table the app uses (feedback popover -> db.insert(feedback)), so feedback 500s on a freshly-migrated DB. Regenerated migrations from the corrected schema (pre-launch; no deployed DB depends on the old one): the new 0000 creates accounts/feedback/sessions/users/verifications, no waitlist. - README "Getting started" claimed the editor needs Postgres + Redis. It does not — the editor runs fully local (IndexedDB/OPFS + on-device Whisper). Reworded: those services are only for the optional auth/feedback/sound-search routes, with the .env.local copy + docker + db:migrate steps spelled out. * chore: rebrand cleanup + desktop scaffold honesty (Phase 5) - mobile gate: the "Roadmap" button linked to a removed /roadmap route (404 on mobile/iPad); point it at the GitHub repo instead. - sitemap: drop the removed routes (/contributors, /roadmap, /why-not-capcut, /blog + per-post) that all 404'd; emit only /, /privacy, /terms. - manifest.json: name/description were still "OpenCut" + the old tagline. - LICENSE: copyright OpenCut -> OpenScript. - package.json: remove the dead *:tools scripts (no @openscript/tools package). - social links: set the real GitHub repo (x/discord left as TODO placeholders). - desktop: window title OpenCut -> OpenScript, and README now states plainly that it's an early empty-window scaffold (not wired to the editor) and that commands run from the repo root. Verified: tsc clean, next build exits 0. * chore: remove dead placeholders + OpenCut leftovers (no replacements available) Rather than leave stubs for things we have no real values for: - Drop the placeholder contact email and the "#" X/Twitter + Discord social links from /privacy and /terms; route contact to the real GitHub issues link. SOCIAL_LINKS reduced to the github repo only. - Remove the dead /blog RSS feed (apps/web/src/app/rss.xml) and its now-orphaned blog data layer (apps/web/src/blog) — there is no blog route. - Remove the unused DEFAULT_LOGO_URL and the OpenCut wordmark assets it pointed at (public/logos/opencut, public/landing-page-dark.png); the header uses a text wordmark. - Remove the empty packages/{env,ui,desktop-bridge} scaffolding dirs. - .github docs: rebrand OpenCut -> OpenScript (SUPPORT/CONTRIBUTING/SECURITY/ CODE_OF_CONDUCT/feature_request), remove the dead Discord invite, and point security reports at GitHub security advisories instead of the dead security@opencut.app. Left the legit `opencut-wasm` package name and the opencut/opencut local Postgres creds (consistent with docker-compose). Verified: tsc clean, next build exits 0. * chore: rebrand remaining OpenCut references in rust/ crates Fix the dead github.com/opencut/opencut repository URL (rust/wasm/Cargo.toml, rust/wasm/README.md) to the real OpenScript repo, and OpenScript the rust/ README. Left the published `opencut-wasm` package/crate name and its opencut_wasm_* file names intact (the JS side imports them by that name). * fix: align root next/typescript with apps/web to fix CI build CI `next build` failed type-checking next.config.ts: the root pinned `next: ^16.1.3` (resolving to 16.2.x, which adds `adapterPath` to NextConfig) while apps/web pinned `next: 16.1.3`, so two incompatible `next` copies were visible and `NextConfig` types didn't match ("Property 'adapterPath' is missing"). Pin the root to `next: 16.1.3` (and `typescript: ^5.8.3`) to match apps/web so the workspace resolves a single version. (apps/website keeps its own next 16.1.1 — isolated.) Verified: bun install + `cd apps/web && bun run build` exits 0. * fix(ci): pin wasm-pack to v0.15.0 so wasm-opt builds on macOS `version: latest` mis-resolves to wasm-pack v0.9.1, whose bundled wasm-opt fails ("exited with exit code 1") on the wasm that current rustc/wasm-bindgen emit. Pinning to the current stable fixes the Build WASM step across all three runner OSes. --------- Co-authored-by: Maze Winther <mazewinther@gmail.com> Co-authored-by: kcfancher <keli@keisi.co> Co-authored-by: Aspen <bobfeltner123@gmail.com> Co-authored-by: Rayan Salhab <r.salhab@aiyexpertsolutions.com> Co-authored-by: Maze <167211895+mazeincoding@users.noreply.github.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 4dd762b commit 06bdbe0

1,217 files changed

Lines changed: 131981 additions & 241560 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.dockerignore

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
node_modules
2+
.next
3+
.git
4+
.gitignore
5+
*.md
6+
.env*
7+
!.env.example
8+
.cursor
9+
.vscode
10+
diffs
11+
docs
12+
agent-transcripts
13+
mcps
14+
terminals
15+
apps/web/.font-cache

.github/CODE_OF_CONDUCT.md

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# Contributor Covenant Code of Conduct
2+
3+
## Our Pledge
4+
5+
We as members, contributors, and leaders pledge to make participation in our
6+
community a harassment-free experience for everyone, regardless of age, body
7+
size, visible or invisible disability, ethnicity, sex characteristics, gender
8+
identity and expression, level of experience, education, socio-economic status,
9+
nationality, personal appearance, race, caste, color, religion, or sexual
10+
identity and orientation.
11+
12+
We pledge to act and interact in ways that contribute to an open, welcoming,
13+
diverse, inclusive, and healthy community.
14+
15+
## Our Standards
16+
17+
Examples of behavior that contributes to a positive environment for our
18+
community include:
19+
20+
- Demonstrating empathy and kindness toward other people
21+
- Being respectful of differing opinions, viewpoints, and experiences
22+
- Giving and gracefully accepting constructive feedback
23+
- Accepting responsibility and apologizing to those affected by our mistakes,
24+
and learning from the experience
25+
- Focusing on what is best not just for us as individuals, but for the overall
26+
community
27+
28+
Examples of unacceptable behavior include:
29+
30+
- The use of sexualized language or imagery, and sexual attention or advances of
31+
any kind
32+
- Trolling, insulting or derogatory comments, and personal or political attacks
33+
- Public or private harassment
34+
- Publishing others' private information, such as a physical or email address,
35+
without their explicit permission
36+
- Other conduct which could reasonably be considered inappropriate in a
37+
professional setting
38+
39+
## Enforcement Responsibilities
40+
41+
Community leaders are responsible for clarifying and enforcing our standards of
42+
acceptable behavior and will take appropriate and fair corrective action in
43+
response to any behavior that they deem inappropriate, threatening, offensive,
44+
or harmful.
45+
46+
Community leaders have the right and responsibility to remove, edit, or reject
47+
comments, commits, code, wiki edits, issues, and other contributions that are
48+
not aligned to this Code of Conduct, and will communicate reasons for moderation
49+
decisions when appropriate.
50+
51+
## Scope
52+
53+
This Code of Conduct applies within all community spaces, and also applies when
54+
an individual is officially representing the community in public spaces.
55+
Examples of representing our community include using an official e-mail address,
56+
posting via an official social media account, or acting as an appointed
57+
representative at an online or offline event.
58+
59+
## Enforcement
60+
61+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
62+
reported to the community leaders responsible for enforcement by opening an issue on the project's GitHub repository.
63+
All complaints will be reviewed and investigated promptly and fairly.
64+
65+
All community leaders are obligated to respect the privacy and security of the
66+
reporter of any incident.
67+
68+
## Enforcement Guidelines
69+
70+
Community leaders will follow these Community Impact Guidelines in determining
71+
the consequences for any action they deem in violation of this Code of Conduct:
72+
73+
### 1. Correction
74+
75+
**Community Impact**: Use of inappropriate language or other behavior deemed
76+
unprofessional or unwelcome in the community.
77+
78+
**Consequence**: A private, written warning from community leaders, providing
79+
clarity around the nature of the violation and an explanation of why the
80+
behavior was inappropriate. A public apology may be requested.
81+
82+
## Attribution
83+
84+
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
85+
version 2.1, available at
86+
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
87+
88+
[homepage]: https://www.contributor-covenant.org
89+
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html

.github/CONTRIBUTING.md

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
# Contributing to OpenScript
2+
3+
⚠️ We are currently NOT accepting feature PRs while we build out the core editor.
4+
5+
If you want to contribute:
6+
7+
1. Open an issue first to discuss
8+
2. Wait for maintainer approval
9+
3. Only then start coding
10+
11+
Critical bug fixes may be accepted on a case-by-case basis.
12+
13+
Thank you for your interest in contributing to OpenScript! This document provides guidelines and instructions for contributing.
14+
15+
## Getting Started
16+
17+
### Prerequisites
18+
19+
- [Node.js](https://nodejs.org/en/) (v18 or later)
20+
- [Bun](https://bun.sh/docs/installation)
21+
(for `npm` alternative)
22+
- [Docker](https://docs.docker.com/get-docker/) and [Docker Compose](https://docs.docker.com/compose/install/)
23+
- Rust toolchain (only needed for `apps/desktop`)
24+
25+
> **Note:** Docker is optional, but it's essential for running the local database and Redis services. If you're planning to contribute to frontend features, you can skip the Docker setup. If you have followed the steps below in [Setup](#setup), you're all set to go!
26+
27+
### Setup
28+
29+
1. Fork the repository
30+
2. Clone your fork locally
31+
3. Navigate to the web app directory: `cd apps/web`
32+
4. Copy `.env.example` to `.env.local`:
33+
34+
```bash
35+
# Unix/Linux/Mac
36+
cp .env.example .env.local
37+
38+
# Windows Command Prompt
39+
copy .env.example .env.local
40+
41+
# Windows PowerShell
42+
Copy-Item .env.example .env.local
43+
```
44+
45+
5. Install dependencies: `bun install`
46+
6. Start the development server: `bun run dev`
47+
48+
> **Note:** Web development uses the published `opencut-wasm` package by default, so a fresh clone does not need a local WASM build.
49+
>
50+
> If you are editing `rust/wasm`, run `bun run build:wasm`, then `cd rust/wasm/pkg && bun link`, then `cd ../../../apps/web && bun link opencut-wasm`.
51+
52+
### Desktop setup
53+
54+
Only needed if you're working on `apps/desktop`. See [`apps/desktop/README.md`](../apps/desktop/README.md) — it's a two-step process: Rust toolchain first via `script/setup-rust`, then desktop native dependencies via `apps/desktop/script/setup`.
55+
56+
## What to Focus On
57+
58+
**🎯 Good Areas to Contribute:**
59+
60+
- Timeline functionality and UI improvements
61+
- Project management features
62+
- Performance optimizations
63+
- Bug fixes in existing functionality
64+
- UI/UX improvements
65+
- Documentation and testing
66+
67+
**⚠️ Areas to Avoid:**
68+
69+
- Preview panel enhancements (text fonts, stickers, effects)
70+
- Export functionality improvements
71+
- Preview rendering optimizations
72+
73+
**Why?** We're currently planning a major refactor of the preview system. The current preview renders DOM elements (HTML), but we're moving to a binary rendering approach similar to CapCut. This new system will ensure consistency between preview and export, and provide much better performance and quality.
74+
75+
The current HTML-based preview is essentially a prototype - the binary approach will be the "real deal." To avoid wasted effort, please focus on other areas of the application until this refactor is complete.
76+
77+
If you're unsure whether your idea falls into the preview category, feel free to create a GitHub issue!
78+
79+
## Development Setup
80+
81+
### Local Development
82+
83+
1. Start the database and Redis services:
84+
85+
```bash
86+
# From project root
87+
docker-compose up -d
88+
```
89+
90+
2. Navigate to the web app directory:
91+
92+
```bash
93+
cd apps/web
94+
```
95+
96+
3. Copy `.env.example` to `.env.local`:
97+
98+
```bash
99+
# Unix/Linux/Mac
100+
cp .env.example .env.local
101+
102+
# Windows Command Prompt
103+
copy .env.example .env.local
104+
105+
# Windows PowerShell
106+
Copy-Item .env.example .env.local
107+
```
108+
109+
4. Configure required environment variables in `.env.local`:
110+
111+
**Required Variables:**
112+
113+
```bash
114+
# Database (matches docker-compose.yaml)
115+
DATABASE_URL="postgresql://opencut:opencut@localhost:5432/opencut"
116+
117+
# Generate a secure secret for Better Auth
118+
BETTER_AUTH_SECRET="your-generated-secret-here"
119+
NEXT_PUBLIC_SITE_URL="http://localhost:3000"
120+
121+
# Redis (matches docker-compose.yaml)
122+
UPSTASH_REDIS_REST_URL="http://localhost:8079"
123+
UPSTASH_REDIS_REST_TOKEN="example_token"
124+
125+
# Development
126+
NODE_ENV="development"
127+
```
128+
129+
**Generate BETTER_AUTH_SECRET:**
130+
131+
```bash
132+
# Unix/Linux/Mac
133+
openssl rand -base64 32
134+
135+
# Windows PowerShell (simple method)
136+
[System.Web.Security.Membership]::GeneratePassword(32, 0)
137+
138+
# Cross-platform (using Node.js)
139+
node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"
140+
141+
# Or use an online generator: https://generate-secret.vercel.app/32
142+
```
143+
144+
5. Run database migrations: `bun run db:migrate`
145+
6. Start the development server: `bun run dev`
146+
147+
### Desktop
148+
149+
Working on `apps/desktop`? See [`apps/desktop/README.md`](../apps/desktop/README.md) for setup. Web-only contributors can ignore this entirely.
150+
151+
## How to Contribute
152+
153+
### Reporting Bugs
154+
155+
- Use the bug report template
156+
- Include steps to reproduce
157+
- Provide screenshots if applicable
158+
159+
### Suggesting Features
160+
161+
- Use the feature request template
162+
- Explain the use case
163+
- Consider implementation details
164+
165+
### Code Contributions
166+
167+
1. Create a new branch: `git checkout -b feature/your-feature-name`
168+
2. Make your changes
169+
3. Run the relevant checks for the area you touched:
170+
171+
- Web changes: from `apps/web`, run `bun run lint` and `bun run format`
172+
- Desktop changes: run `./apps/desktop/script/setup` if your environment isn't set up yet
173+
174+
4. Commit your changes with a descriptive message
175+
5. Push to your fork and create a pull request
176+
177+
## Code Style
178+
179+
- We use ESLint for linting and Prettier for formatting
180+
- Run `bun run format` from the `apps/web` directory to format code
181+
- Run `bun run lint` from the `apps/web` directory to check for linting issues
182+
- Follow the existing code patterns
183+
184+
## Pull Request Process
185+
186+
1. Fill out the pull request template completely
187+
2. Link any related issues
188+
3. Ensure CI passes
189+
4. Request review from maintainers
190+
5. Address any feedback
191+
192+
## Community
193+
194+
- Be respectful and inclusive
195+
- Follow our Code of Conduct
196+
- Help others in discussions and issues
197+
198+
Thank you for contributing!
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
name: Bug report
2+
description: Create a report to help us improve
3+
title: "[BUG] "
4+
labels: bug
5+
body:
6+
- type: input
7+
id: Platform
8+
attributes:
9+
label: Platform
10+
description: Please enter the platform on which you encountered the bug.
11+
placeholder: e.g. Windows 11, Ubuntu 14.04
12+
validations:
13+
required: true
14+
- type: input
15+
id: Browser
16+
attributes:
17+
label: Browser
18+
description: Please enter the browser on which you encountered the bug.
19+
placeholder: e.g. Chrome 137, Firefox 137, Safari 17
20+
validations:
21+
required: true
22+
- type: textarea
23+
id: current-behavior
24+
attributes:
25+
label: Current Behavior
26+
description: A concise description of what you're experiencing.
27+
validations:
28+
required: true
29+
- type: textarea
30+
id: expected-behavior
31+
attributes:
32+
label: Expected Behavior
33+
description: A concise description of what you expected to happen.
34+
validations:
35+
required: false
36+
- type: dropdown
37+
id: recurrence-probability
38+
attributes:
39+
label: Recurrence Probability
40+
description: How often does this bug occur?
41+
options:
42+
- Always
43+
- Usually
44+
- Sometimes
45+
- Seldom
46+
default: 0
47+
validations:
48+
required: true
49+
- type: textarea
50+
id: steps-to-reproduce
51+
attributes:
52+
label: Steps To Reproduce
53+
description: Steps to reproduce the behavior.
54+
placeholder: |
55+
1. Go to '...'
56+
2. Click on '....'
57+
3. Scroll down to '....'
58+
4. See error
59+
validations:
60+
required: true
61+
- type: textarea
62+
id: additional-context
63+
attributes:
64+
label: Anything else?
65+
description: |
66+
Links? References? Anything that will give us more context about the issue you are encountering!
67+
68+
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
69+
validations:
70+
required: false

0 commit comments

Comments
 (0)