diff --git a/src/main/services/team/fileLock.ts b/src/main/services/team/fileLock.ts index 9af59297..9152bbda 100644 --- a/src/main/services/team/fileLock.ts +++ b/src/main/services/team/fileLock.ts @@ -99,16 +99,50 @@ function releaseLock(lockPath: string): void { } } +function sleepSync(ms: number): void { + const deadline = Date.now() + ms; + while (Date.now() < deadline) { + // Synchronous callers need the same cross-process lock as controller writes. + } +} + +function resolveLockOptions(options: FileLockOptions): Required { + return { + acquireTimeoutMs: options.acquireTimeoutMs ?? ACQUIRE_TIMEOUT_MS, + staleTimeoutMs: options.staleTimeoutMs ?? STALE_TIMEOUT_MS, + retryIntervalMs: options.retryIntervalMs ?? RETRY_INTERVAL_MS, + }; +} + +export function withFileLockSync( + filePath: string, + fn: () => T, + options: FileLockOptions = {} +): T { + const resolvedOptions = resolveLockOptions(options); + const lockPath = `${filePath}.lock`; + const deadline = Date.now() + resolvedOptions.acquireTimeoutMs; + + while (!tryAcquire(lockPath, resolvedOptions)) { + if (Date.now() >= deadline) { + throw new Error(`File lock timeout: ${filePath}`); + } + sleepSync(Math.min(resolvedOptions.retryIntervalMs, Math.max(0, deadline - Date.now()))); + } + + try { + return fn(); + } finally { + releaseLock(lockPath); + } +} + export async function withFileLock( filePath: string, fn: () => Promise, options: FileLockOptions = {} ): Promise { - const resolvedOptions = { - acquireTimeoutMs: options.acquireTimeoutMs ?? ACQUIRE_TIMEOUT_MS, - staleTimeoutMs: options.staleTimeoutMs ?? STALE_TIMEOUT_MS, - retryIntervalMs: options.retryIntervalMs ?? RETRY_INTERVAL_MS, - }; + const resolvedOptions = resolveLockOptions(options); const lockPath = `${filePath}.lock`; const deadline = Date.now() + resolvedOptions.acquireTimeoutMs; diff --git a/src/renderer/index.html b/src/renderer/index.html index 33d141f0..6006ef3c 100644 --- a/src/renderer/index.html +++ b/src/renderer/index.html @@ -237,10 +237,17 @@ animation: splash-tagline-type 1.05s steps(28, end) 0.22s forwards; will-change: clip-path; } - #splash-status-row { + #splash-loading { display: flex; width: min(320px, 78vw); - margin-top: 14px; + margin-top: 24px; + flex-direction: column; + align-items: center; + gap: 8px; + } + #splash-status-row { + display: flex; + width: 100%; align-items: baseline; justify-content: center; gap: 6px; @@ -274,9 +281,8 @@ white-space: nowrap; } #splash-hint { - width: min(320px, 78vw); + width: 100%; min-height: 15px; - margin-top: 6px; font-family: ui-sans-serif, system-ui, @@ -347,7 +353,6 @@ position: relative; width: min(240px, 64vw); height: 3px; - margin-top: 10px; overflow: hidden; border-radius: 999px; background: rgba(255, 255, 255, 0.1); @@ -611,12 +616,14 @@
Agent Teams AI
Get more done by doing less.
-
-
Preparing workspace...
-
0s
+
+ +
+
Preparing workspace...
+
0s
+
+
-
-