chore(release): v0.7.1

Version bump via changesets.
This commit is contained in:
Fusion
2026-04-28 00:36:24 -07:00
committed by gsxdsm
parent 6e2fd5ecb1
commit 4ee7f6b2b8
3 changed files with 40 additions and 1 deletions

View File

@@ -433,7 +433,10 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels,
const trimmed = description.trim();
if (!trimmed || isSubmitting || !onCreate) return;
const originalDescription = description;
setIsSubmitting(true);
// Optimistically clear text for rapid entry; restore on failure.
setDescription("");
try {
const createdTask = await onCreate({
description: trimmed,
@@ -467,6 +470,7 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels,
resetForm();
// Note: Focus restoration is handled by useEffect when isSubmitting becomes false
} catch (err) {
setDescription(originalDescription);
addToast(getErrorMessage(err) || "Failed to create task", "error");
// Keep input content on failure so user can retry
} finally {

View File

@@ -847,8 +847,10 @@
otherwise win over the media query. */
min-width: 0 !important;
min-height: 0 !important;
width: 100% !important;
width: 100vw !important;
max-width: 100vw !important;
min-height: 100dvh !important;
height: 100dvh !important;
max-height: 100dvh !important;
border-radius: 0;

View File

@@ -143,7 +143,40 @@ function stripTaskListHeavyFields<T>(task: T): T {
return task;
}
return { ...task, log: [] } as T;
const candidate = task as Record<string, unknown>;
const existingTimed = candidate.timedExecutionMs;
// Mirror the slim REST path (listTasks): aggregate `[timing] … in <N>ms`
// log entries before stripping the log so the board card has the same
// total-execution figure on SSE updates as on the initial fetch.
// Without this, `task:updated` events arrive with log=[] AND
// timedExecutionMs=undefined, causing TaskCard to fall back to
// workflow-only time and flicker every time an update lands.
const timedExecutionMs =
typeof existingTimed === "number"
? existingTimed
: sumTimedLogEntries(candidate.log);
return { ...task, log: [], timedExecutionMs } as T;
}
function sumTimedLogEntries(log: unknown): number {
if (!Array.isArray(log)) return 0;
let total = 0;
for (const entry of log) {
if (!entry || typeof entry !== "object") continue;
const action = typeof (entry as { action?: unknown }).action === "string"
? ((entry as { action: string }).action)
: "";
const outcome = typeof (entry as { outcome?: unknown }).outcome === "string"
? ((entry as { outcome: string }).outcome)
: "";
if (!action.includes("[timing]") && !outcome.includes("[timing]")) continue;
const match = `${action}\n${outcome}`.match(/(\d+(?:\.\d+)?)ms\b/i);
if (!match) continue;
const ms = Number(match[1]);
if (Number.isFinite(ms)) total += ms;
}
return total;
}
function stripTaskEventHeavyFields<T>(payload: T): T {