Skip to content

Commit c7a866e

Browse files
Merge branch 'release/v1.8.0' into ponytail/native-gif-export
2 parents 0f66d73 + e95ba8e commit c7a866e

108 files changed

Lines changed: 2682 additions & 2701 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.

crates/compositor/src/compositor.rs

Lines changed: 350 additions & 26 deletions
Large diffs are not rendered by default.

crates/compositor/src/cursor.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
use anyhow::{Context, Result};
66

77
// Paramètres de suivi auto — parité stricte avec
8-
// `src/components/video-editor/videoPlayback/constants.ts` (AUTO_FOLLOW_PARAMS), partagés
8+
// `src/lib/zoomMath/constants.ts` (AUTO_FOLLOW_PARAMS), partagés
99
// là-bas entre preview et export pour que la caméra suive le curseur à l'identique.
1010
const AUTO_FOLLOW_MIN_FACTOR: f32 = 0.1;
1111
const AUTO_FOLLOW_MAX_FACTOR: f32 = 0.25;

electron-builder.json5

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,9 +126,9 @@
126126
// vendors two artifacts into this directory: the shared av*.dll set,
127127
// which the D3D11 compositor addon dlopens at require() time and
128128
// therefore MUST ship, and a standalone static ffmpeg.exe that nothing
129-
// in the app spawns it exists for scripts/bench-export.mjs. Shipping
130-
// it added a large binary to every Windows installer, plus an LGPL
131-
// redistribution obligation, for a file no shipped code opens.
129+
// in the app spawns. Shipping it added a large binary to every Windows
130+
// installer, plus an LGPL redistribution obligation, for a file no
131+
// shipped code opens.
132132
"filter": ["win32-*/*", "!win32-*/ffmpeg.exe"]
133133
}
134134
],

electron/ai-edition/chat-service.toolloop.test.ts

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -244,14 +244,17 @@ describe("runChat tool loop", () => {
244244
invokeMock.mockImplementationOnce(async (args) => {
245245
events.push({ kind: "captured", payload: args.userMessage });
246246
args.sink.text("Hi ");
247+
args.sink.thinking("pondering. ");
247248
args.sink.text("there.");
249+
args.sink.thinking("concluding.");
248250
args.sink.toolStart("addTrim", { startSec: 1, endSec: 2 });
249251
args.sink.toolEnd("addTrim", true, "added trim 0:01.0 – 0:02.0");
250252
return { text: "Done.", document: args.document, mutated: true };
251253
});
252254

253255
const sink = {
254256
text: (delta: string) => fixture.events.push({ kind: "text", payload: delta }),
257+
thinking: (delta: string) => fixture.events.push({ kind: "thinking", payload: delta }),
255258
toolStart: (name: string, args: unknown) =>
256259
fixture.events.push({ kind: "toolStart", payload: { name, args } }),
257260
toolEnd: (name: string, ok: boolean, summary?: string) =>
@@ -262,12 +265,22 @@ describe("runChat tool loop", () => {
262265
const s = createSession("proj_sink");
263266
const result = await runChat("proj_sink", s.id, "cut", stubConfig(), fixtureDocument(), sink);
264267
expect(result.success).toBe(true);
265-
expect(fixture.events.map((e) => e.kind)).toEqual(["text", "text", "toolStart", "toolEnd"]);
266-
expect((fixture.events[0].payload as string) + (fixture.events[1].payload as string)).toBe(
268+
expect(fixture.events.map((e) => e.kind)).toEqual([
269+
"text",
270+
"thinking",
271+
"text",
272+
"thinking",
273+
"toolStart",
274+
"toolEnd",
275+
]);
276+
expect((fixture.events[0].payload as string) + (fixture.events[2].payload as string)).toBe(
267277
"Hi there.",
268278
);
269-
expect(fixture.events[2].payload).toMatchObject({ name: "addTrim" });
270-
expect(fixture.events[3].payload).toMatchObject({
279+
expect((fixture.events[1].payload as string) + (fixture.events[3].payload as string)).toBe(
280+
"pondering. concluding.",
281+
);
282+
expect(fixture.events[4].payload).toMatchObject({ name: "addTrim" });
283+
expect(fixture.events[5].payload).toMatchObject({
271284
name: "addTrim",
272285
ok: true,
273286
summary: expect.stringMatching(/added trim/),
@@ -284,6 +297,7 @@ describe("runChat tool loop", () => {
284297
});
285298
const sinkErr = {
286299
text: (delta: string) => fixture.events.push({ kind: "text", payload: delta }),
300+
thinking: (delta: string) => fixture.events.push({ kind: "thinking", payload: delta }),
287301
toolStart: (name: string, args: unknown) =>
288302
fixture.events.push({ kind: "toolStart", payload: { name, args } }),
289303
toolEnd: (name: string, ok: boolean, summary?: string) =>

electron/ai-edition/chat-service.ts

Lines changed: 7 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,11 @@ export function deleteSession(projectId: string, sessionId: string): boolean {
204204
export interface ChatEventSink {
205205
/** Streamed text delta from the model. */
206206
text?: (delta: string) => void;
207+
/** Streamed delta from the model's reasoning block (Anthropic/MiniMax
208+
* thinking). Provider-agnostic — never called for providers that don't
209+
* expose thinking. The chat panel streams these into a live "Thinking…"
210+
* block so the reasoning phase doesn't feel like dead air. */
211+
thinking?: (delta: string) => void;
207212
/** A tool call is about to execute. */
208213
toolStart?: (name: string, args: unknown) => void;
209214
/** A tool call has finished. `ok=false` carries the model's error message. */
@@ -218,6 +223,7 @@ const noop = () => undefined;
218223
/** ponytail: zero-config sink that swallows every event. */
219224
const NOOP_SINK: Required<ChatEventSink> = {
220225
text: noop,
226+
thinking: noop,
221227
toolStart: noop,
222228
toolEnd: noop,
223229
error: noop,
@@ -323,6 +329,7 @@ export async function runChat(
323329

324330
const agentSink = {
325331
text: (delta: string) => emit.text(delta),
332+
thinking: (delta: string) => emit.thinking(delta),
326333
toolStart: (name: string, args: unknown) => {
327334
emit.toolStart(name, args);
328335
void editsAllowed;
@@ -572,44 +579,6 @@ function emptyDocumentForTextOnly(projectId: string): AxcutDocument {
572579
return createEmptyDocument({ title: "Untitled project", projectId });
573580
}
574581

575-
// ponytail: legacy single-session compatibility for the simpler ChatPanel
576-
// consumers. Picks the most recent session (or auto-creates one) so a stale
577-
// caller keeps working. The multi-session UI is the supported path.
578-
function getOrCreateDefaultSession(projectId: string): ChatSession {
579-
const m = getProjectSessions(projectId);
580-
if (m.size > 0) {
581-
const arr = Array.from(m.values()).sort((a, b) => b.createdAt.localeCompare(a.createdAt));
582-
return arr[0];
583-
}
584-
const created = createSession(projectId);
585-
const s = m.get(created.id);
586-
if (!s) throw new Error("Chat session unavailable.");
587-
return s;
588-
}
589-
590-
export async function runChatDefault(
591-
projectId: string,
592-
message: string,
593-
llmConfig: LlmConfigStore,
594-
sink?: ChatEventSink,
595-
): Promise<AiEditionChatResult> {
596-
const session = getOrCreateDefaultSession(projectId);
597-
return runChat(projectId, session.id, message, llmConfig, undefined, sink);
598-
}
599-
600-
export function getDefaultChatHistory(projectId: string): AiEditionChatMessage[] {
601-
const m = sessionsByProject.get(projectId);
602-
if (!m || m.size === 0) return [];
603-
const arr = Array.from(m.values()).sort((a, b) => b.createdAt.localeCompare(a.createdAt));
604-
return [...arr[0].messages];
605-
}
606-
607-
export function clearDefaultChatHistory(projectId: string): void {
608-
const m = sessionsByProject.get(projectId);
609-
if (!m) return;
610-
for (const s of m.values()) s.messages = [];
611-
}
612-
613582
// --- Compaction (P3.7) ---------------------------------------------------
614583

615584
export interface SessionBudgetSnapshot {

0 commit comments

Comments
 (0)