refactor(components): apply branded types at Svelte component call sites

This commit is contained in:
Hibryda 2026-03-11 05:46:22 +01:00
parent c3d2e1daee
commit af3cd45324
3 changed files with 16 additions and 13 deletions

View file

@ -19,6 +19,7 @@
import type { AgentMessage } from '../../adapters/claude-messages'; import type { AgentMessage } from '../../adapters/claude-messages';
import { getProvider, getDefaultProviderId } from '../../providers/registry.svelte'; import { getProvider, getDefaultProviderId } from '../../providers/registry.svelte';
import { loadAnchorsForProject } from '../../stores/anchors.svelte'; import { loadAnchorsForProject } from '../../stores/anchors.svelte';
import { SessionId, ProjectId } from '../../types/ids';
import AgentPane from '../Agent/AgentPane.svelte'; import AgentPane from '../Agent/AgentPane.svelte';
interface Props { interface Props {
@ -31,17 +32,17 @@
let providerId = $derived(project.provider ?? getDefaultProviderId()); let providerId = $derived(project.provider ?? getDefaultProviderId());
let providerMeta = $derived(getProvider(providerId)); let providerMeta = $derived(getProvider(providerId));
let sessionId = $state(crypto.randomUUID()); let sessionId = $state(SessionId(crypto.randomUUID()));
let lastState = $state<ProjectAgentState | null>(null); let lastState = $state<ProjectAgentState | null>(null);
let loading = $state(true); let loading = $state(true);
let hasRestoredHistory = $state(false); let hasRestoredHistory = $state(false);
function handleNewSession() { function handleNewSession() {
sessionId = crypto.randomUUID(); sessionId = SessionId(crypto.randomUUID());
hasRestoredHistory = false; hasRestoredHistory = false;
lastState = null; lastState = null;
registerSessionProject(sessionId, project.id, providerId); registerSessionProject(sessionId, ProjectId(project.id), providerId);
trackProject(project.id, sessionId); trackProject(ProjectId(project.id), sessionId);
onsessionid?.(sessionId); onsessionid?.(sessionId);
} }
@ -58,7 +59,7 @@
const state = await loadProjectAgentState(projectId); const state = await loadProjectAgentState(projectId);
lastState = state; lastState = state;
if (state?.last_session_id) { if (state?.last_session_id) {
sessionId = state.last_session_id as ReturnType<typeof crypto.randomUUID>; sessionId = SessionId(state.last_session_id);
// Restore cached messages into the agent store // Restore cached messages into the agent store
const records = await loadAgentMessages(projectId); const records = await loadAgentMessages(projectId);
@ -67,18 +68,18 @@
hasRestoredHistory = true; hasRestoredHistory = true;
} }
} else { } else {
sessionId = crypto.randomUUID(); sessionId = SessionId(crypto.randomUUID());
} }
} catch (e) { } catch (e) {
console.warn('Failed to load project agent state:', e); console.warn('Failed to load project agent state:', e);
sessionId = crypto.randomUUID(); sessionId = SessionId(crypto.randomUUID());
} finally { } finally {
loading = false; loading = false;
// Load persisted anchors for this project // Load persisted anchors for this project
loadAnchorsForProject(project.id); loadAnchorsForProject(ProjectId(project.id));
// Register session -> project mapping for persistence + health tracking // Register session -> project mapping for persistence + health tracking
registerSessionProject(sessionId, project.id, providerId); registerSessionProject(sessionId, ProjectId(project.id), providerId);
trackProject(project.id, sessionId); trackProject(ProjectId(project.id), sessionId);
onsessionid?.(sessionId); onsessionid?.(sessionId);
} }
} }

View file

@ -15,6 +15,7 @@
import { getProjectHealth, setStallThreshold } from '../../stores/health.svelte'; import { getProjectHealth, setStallThreshold } from '../../stores/health.svelte';
import { fsWatchProject, fsUnwatchProject, onFsWriteDetected, fsWatcherStatus } from '../../adapters/fs-watcher-bridge'; import { fsWatchProject, fsUnwatchProject, onFsWriteDetected, fsWatcherStatus } from '../../adapters/fs-watcher-bridge';
import { recordExternalWrite } from '../../stores/conflicts.svelte'; import { recordExternalWrite } from '../../stores/conflicts.svelte';
import { ProjectId } from '../../types/ids';
import { notify, dismissNotification } from '../../stores/notifications.svelte'; import { notify, dismissNotification } from '../../stores/notifications.svelte';
interface Props { interface Props {
@ -89,7 +90,7 @@
let unlisten: (() => void) | null = null; let unlisten: (() => void) | null = null;
onFsWriteDetected((event) => { onFsWriteDetected((event) => {
if (event.project_id !== projectId) return; if (event.project_id !== projectId) return;
const isNew = recordExternalWrite(projectId, event.file_path, event.timestamp_ms); const isNew = recordExternalWrite(ProjectId(projectId), event.file_path, event.timestamp_ms);
if (isNew) { if (isNew) {
const shortName = event.file_path.split('/').pop() ?? event.file_path; const shortName = event.file_path.split('/').pop() ?? event.file_path;
notify('warning', `External write: ${shortName} — file also modified by agent`); notify('warning', `External write: ${shortName} — file also modified by agent`);

View file

@ -3,6 +3,7 @@
import { PROJECT_ACCENTS } from '../../types/groups'; import { PROJECT_ACCENTS } from '../../types/groups';
import type { ProjectHealth } from '../../stores/health.svelte'; import type { ProjectHealth } from '../../stores/health.svelte';
import { acknowledgeConflicts } from '../../stores/conflicts.svelte'; import { acknowledgeConflicts } from '../../stores/conflicts.svelte';
import { ProjectId } from '../../types/ids';
interface Props { interface Props {
project: ProjectConfig; project: ProjectConfig;
@ -85,7 +86,7 @@
<button <button
class="info-conflict info-conflict-external" class="info-conflict info-conflict-external"
title="{health.externalConflictCount} external write{health.externalConflictCount > 1 ? 's' : ''} — files modified outside agent — click to dismiss" title="{health.externalConflictCount} external write{health.externalConflictCount > 1 ? 's' : ''} — files modified outside agent — click to dismiss"
onclick={(e: MouseEvent) => { e.stopPropagation(); acknowledgeConflicts(project.id); }} onclick={(e: MouseEvent) => { e.stopPropagation(); acknowledgeConflicts(ProjectId(project.id)); }}
> >
{health.externalConflictCount} ext write{health.externalConflictCount > 1 ? 's' : ''} {health.externalConflictCount} ext write{health.externalConflictCount > 1 ? 's' : ''}
</button> </button>
@ -95,7 +96,7 @@
<button <button
class="info-conflict" class="info-conflict"
title="{health.fileConflictCount - (health.externalConflictCount ?? 0)} agent conflict{health.fileConflictCount - (health.externalConflictCount ?? 0) > 1 ? 's' : ''} — click to dismiss" title="{health.fileConflictCount - (health.externalConflictCount ?? 0)} agent conflict{health.fileConflictCount - (health.externalConflictCount ?? 0) > 1 ? 's' : ''} — click to dismiss"
onclick={(e: MouseEvent) => { e.stopPropagation(); acknowledgeConflicts(project.id); }} onclick={(e: MouseEvent) => { e.stopPropagation(); acknowledgeConflicts(ProjectId(project.id)); }}
> >
{health.fileConflictCount - (health.externalConflictCount ?? 0)} conflict{health.fileConflictCount - (health.externalConflictCount ?? 0) > 1 ? 's' : ''} {health.fileConflictCount - (health.externalConflictCount ?? 0)} conflict{health.fileConflictCount - (health.externalConflictCount ?? 0) > 1 ? 's' : ''}
</button> </button>