Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,15 @@ jobs:
}
if versions != {version}:
raise SystemExit(f"tag/version mismatch: {tag} != {sorted(versions)}")
wix_version = tauri["bundle"]["windows"]["wix"]["version"]
if wix_version != "1.0.0.3":
raise SystemExit(f"unexpected Windows installer version: {wix_version}")
notes = Path("docs/releases") / f"{version}.md"
if not notes.is_file() or not notes.read_text(encoding="utf-8").strip():
raise SystemExit(f"release notes are missing or empty: {notes}")
prerelease = "-" in version.split("+", 1)[0]
if version == "1.0.0-beta.2" and not prerelease:
raise SystemExit("OpenTake 1.0.0-beta.2 must remain a prerelease")
if version == "1.0.0-beta.3" and not prerelease:
raise SystemExit("OpenTake 1.0.0-beta.3 must remain a prerelease")
if not prerelease:
raise SystemExit("this release workflow publishes prereleases only")

Expand Down Expand Up @@ -636,6 +639,7 @@ jobs:
RELEASE_SHA: ${{ needs.validate.outputs.source_sha }}
RELEASE_VERSION: ${{ needs.validate.outputs.version }}
NOTES_PATH: ${{ needs.validate.outputs.notes_path }}
PYTHONDONTWRITEBYTECODE: '1'
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
Expand Down
22 changes: 11 additions & 11 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ members = [
]

[workspace.package]
version = "1.0.0-beta.2"
version = "1.0.0-beta.3"
edition = "2021"
license = "GPL-3.0-or-later"
repository = "https://github.com/appergb/OpenTake"
Expand Down
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -303,9 +303,9 @@ cd ..
cargo tauri dev
```

> **Current Status**: `1.0.0-beta.2` candidate. The local editing, preview,
> **Current Status**: `1.0.0-beta.3` candidate. The local editing, preview,
> persistence, export, Agent, Motion Canvas, and reviewed AI workflow verticals
> are implemented. See the [Beta release notes](docs/releases/1.0.0-beta.2.md)
> are implemented. See the [Beta release notes](docs/releases/1.0.0-beta.3.md)
> for validation scope and platform/provider limits.

The sibling directory `palmier-pro-upstream/` contains upstream Swift sources for reference during porting.
Expand All @@ -318,7 +318,8 @@ The sibling directory `palmier-pro-upstream/` contains upstream Swift sources fo
|:--|:--|:--|
| `0.1.0-dev` | 2026-06 | Phase 0+1: Cargo workspace + Domain models + Edit ops + Tauri scaffold |
| `1.0.0-beta.1` | 2026-08-01 | First installable Beta: end-to-end local editor, Agent, Motion and reviewed AI workflows |
| `1.0.0-beta.2` | 2026-08-03 | Hardened Beta: official Codex login, atomic timeline gestures, secure MCP and interaction polish |
| `1.0.0-beta.2` | 2026-08-08 | Hardened Beta: official Codex login, atomic timeline gestures, secure MCP and interaction polish |
| `1.0.0-beta.3` | 2026-08-09 | Playback Beta: app-wide Space transport, native HEVC source preview and release-pipeline hardening |
| *(planned)* `1.0.0` | TBD | Phase 10: Full release — CapCut parity + deep Agent integration |

📖 [Full Roadmap](docs/architecture/ROADMAP.md)
Expand Down
97 changes: 77 additions & 20 deletions crates/opentake-motion/src/renderer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1779,6 +1779,26 @@ mod chromium_backend {
width: u32,
height: u32,
) -> MotionResult<()> {
// Screencast sizing is based on the compositor surface, so resize
// the browser contents before overriding the logical viewport.
let window = self.command("Browser.getWindowForTarget", json!({}), Some(session))?;
let window_id = window
.get("windowId")
.and_then(Value::as_u64)
.ok_or_else(|| {
MotionError::render_failed(format!(
"Chromium CDP response is missing integer field \"windowId\": {window}"
))
})?;
self.command(
"Browser.setContentsSize",
json!({
"windowId": window_id,
"width": width,
"height": height
}),
None,
)?;
self.command(
"Emulation.setDeviceMetricsOverride",
device_metrics_params(width, height),
Expand Down Expand Up @@ -3167,7 +3187,7 @@ mod chromium_backend {
}

#[test]
fn device_metrics_keep_layout_and_capture_viewport_exact() {
fn browser_contents_are_resized_before_device_metrics() {
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
let client_stream = TcpStream::connect(listener.local_addr().unwrap()).unwrap();
let (server_stream, _) = listener.accept().unwrap();
Expand All @@ -3177,15 +3197,64 @@ mod chromium_backend {
None,
);
let mut server_socket = WebSocket::from_raw_socket(server_stream, Role::Server, None);
let (observed_sender, observed_receiver) = mpsc::channel();
let server = thread::spawn(move || {
let request = match server_socket.read().unwrap() {
Message::Text(text) => serde_json::from_str::<Value>(text.as_ref()).unwrap(),
other => panic!("expected device-metrics request, got {other:?}"),
};
assert_eq!(
request,
let mut observed = Vec::new();
loop {
let request = match server_socket.read().unwrap() {
Message::Text(text) => {
serde_json::from_str::<Value>(text.as_ref()).unwrap()
}
other => panic!("expected viewport-sizing request, got {other:?}"),
};
let id = request["id"].as_u64().unwrap();
let method = request["method"].as_str().unwrap().to_owned();
let result = if method == "Browser.getWindowForTarget" {
json!({"windowId": 42, "bounds": {}})
} else {
json!({})
};
server_socket
.send(Message::text(
json!({"id": id, "result": result}).to_string(),
))
.unwrap();
observed.push(request);
if method == "Emulation.setDeviceMetricsOverride" {
break;
}
}
observed_sender.send(observed).unwrap();
});

let mut cdp = Cdp::new(
client_socket,
SandboxPolicy::default(),
MotionCancellationToken::new(),
Instant::now() + Duration::from_secs(1),
);
cdp.set_device_metrics("render-session", 48, 32).unwrap();
server.join().unwrap();
assert_eq!(
observed_receiver.recv().unwrap(),
vec![
json!({
"id": 1,
"method": "Browser.getWindowForTarget",
"params": {},
"sessionId": "render-session"
}),
json!({
"id": 2,
"method": "Browser.setContentsSize",
"params": {
"windowId": 42,
"width": 48,
"height": 32
}
}),
json!({
"id": 3,
"method": "Emulation.setDeviceMetricsOverride",
"params": {
"width": 48,
Expand All @@ -3197,20 +3266,8 @@ mod chromium_backend {
},
"sessionId": "render-session"
})
);
server_socket
.send(Message::text(json!({"id": 1, "result": {}}).to_string()))
.unwrap();
});

let mut cdp = Cdp::new(
client_socket,
SandboxPolicy::default(),
MotionCancellationToken::new(),
Instant::now() + Duration::from_secs(1),
]
);
cdp.set_device_metrics("render-session", 48, 32).unwrap();
server.join().unwrap();
}

#[test]
Expand Down
24 changes: 24 additions & 0 deletions docs/architecture/PLAYBACK-ENGINE.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,23 @@ surface. Reverse and positive-speed media can remain on this route when no
compositor-only content is present. This route has no libmpv dependency or
transparent native-player hole.

## Source-media preview route

The source tab uses the Rust streaming engine whenever the packaged playback
capability is available. `playback_start(mediaId)` projects the selected video
into a private one-track timeline at the project fps, then reuses the same
FFmpeg RGBA decode, compositor, cpal audio clock, exact publication, and
identity-scoped pause/seek/stop lifecycle as timeline playback. The source
asset is read directly; this route does not create a whole-file proxy or
transcode by default.

Paused source frames and paused seeks use `composite_frame(sourceMediaId)` so
they also decode through FFmpeg instead of switching back to WebKit. A terminal
frame remains painted, Play rewinds it to frame zero, and selecting another
asset retires the previous source session before accepting its publications.
The ordinary `<video>` source preview remains only for the browser shell and a
minimal build where the native playback capability handshake is unavailable.

## Rust route and exact publication

Rust continuously decodes, builds the RenderPlan, composites with wgpu, and
Expand Down Expand Up @@ -121,6 +138,13 @@ the same project retain valid caches.
text/color/multi-track Rust project, then switched back across the project
boundary without retaining the previous duration or playhead. Receipt:
[playback-route-lifecycle-real-device-2026-08-01.md](../audit/2026-07-14/runtime-artifacts/automated/playback-route-lifecycle-real-device-2026-08-01.md).
- 2026-08-09 source-preview regression: the production source projection and a
seeked native start at frame 1572 decoded the reported 2.5 GB HEVC Main10,
yuv420p10le, 3840x2160, ~100 Mbps + PCM asset for three seconds. It published
61 frames, advanced to frame 1662, kept the minimum non-black pixel ratio at
0.945, and observed zero neon-green corruption. The probe remains ignored by
the default workspace gate because it requires that external real-device
fixture.

Artifact hashes and the separation between older installed-app evidence and
fresh detached bundles are recorded in
Expand Down
Loading
Loading