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
10 changes: 8 additions & 2 deletions apps/staged/src-tauri/src/timeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,14 @@ fn build_branch_timeline(store: &Arc<Store>, branch_id: &str) -> Result<BranchTi
// Local branch: fetch commits from the local worktree
let worktree_path = Path::new(&wd.path);
if worktree_path.exists() {
let git_commits =
git::get_commits_since_base(worktree_path, &branch.base_branch).unwrap_or_default();
let git_commits = match git::get_commits_since_base(worktree_path, &branch.base_branch)
{
Ok(commits) => commits,
Err(e) => {
log::warn!("Failed to get commits since base for branch {branch_id}: {e:?}");
vec![]
}
};

// For each git commit, look up our metadata (session linkage)
for gc in git_commits {
Expand Down
54 changes: 30 additions & 24 deletions apps/staged/src/lib/features/branches/BranchCard.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,11 @@
(isRemote && remoteWorkspaceStatus === 'starting')
);

/** True during provisioning OR the gap between worktree-ready and timeline-loaded. */
let isSettingUp = $derived(
isProvisioning || (isLocal && !!branch.worktreePath && !timeline && !error)
);

/** Empty timeline used during provisioning so the action buttons render. */
const emptyTimeline: BranchTimelineData = { commits: [], notes: [], reviews: [], images: [] };

Expand Down Expand Up @@ -171,22 +176,23 @@

/** Label for the provisioning timeline row, if applicable. */
let provisioningLabel = $derived.by(() => {
if (isLocal && !branch.worktreePath && !worktreeError) {
if (setupPhase) {
const labels: Record<string, string> = {
cloning: 'Cloning repository…',
fetching: 'Fetching latest changes…',
creating_worktree: 'Creating worktree…',
running_setup_actions: 'Running setup actions…',
};
return labels[setupPhase] ?? 'Setting up…';
if (!isSettingUp) return undefined;
if (isProvisioning) {
if (isLocal) {
if (setupPhase) {
const labels: Record<string, string> = {
cloning: 'Cloning repository…',
fetching: 'Fetching latest changes…',
creating_worktree: 'Creating worktree…',
running_setup_actions: 'Running setup actions…',
};
return labels[setupPhase] ?? 'Setting up…';
}
return 'Setting up…';
}
return 'Setting up…';
}
if (isRemote && remoteWorkspaceStatus === 'starting') {
return 'Starting workspace…';
}
return undefined;
return 'Looking for changes…';
});

/** Map blox orchestrator CommandType enum names to display labels. */
Expand All @@ -199,15 +205,14 @@

/** Detail text for the provisioning row (e.g. git progress percentages or step info). */
let provisioningDetail = $derived.by(() => {
if (isLocal && !branch.worktreePath && !worktreeError) return setupDetail;
if (isRemote && remoteWorkspaceStatus === 'starting') {
if (setupDetail && setupPhase) {
const label = remoteCommandLabels[setupPhase] ?? setupPhase;
return `${setupDetail} · ${label}`;
}
return setupDetail;
if (!isProvisioning) return null;
if (isLocal) return setupDetail;
// Remote workspace starting
if (setupDetail && setupPhase) {
const label = remoteCommandLabels[setupPhase] ?? setupPhase;
return `${setupDetail} · ${label}`;
}
return null;
return setupDetail;
});

/** True when the branch has at least one finalized commit (code changes vs base). */
Expand Down Expand Up @@ -446,6 +451,7 @@
return;
}

commands.invalidateBranchTimeline(branch.id);
loadTimeline();
// Handle PR session completion
if (prButton && eventSessionId === prButton.getPrSessionId()) {
Expand Down Expand Up @@ -948,7 +954,7 @@
{repoLabel}
{isLocal}
{isRemote}
{isProvisioning}
{isSettingUp}
{remoteWorkspaceStatus}
{onDelete}
{onRename}
Expand All @@ -971,7 +977,7 @@
{workspaceError}
fallbackError={error}
/>
{:else if loading && !isProvisioning}
{:else if loading && !isSettingUp}
<div class="loading">
<Spinner size={14} />
<span>Loading...</span>
Expand All @@ -981,7 +987,7 @@
<span>{error}</span>
<button class="retry-btn" onclick={() => loadTimeline()}>Retry</button>
</div>
{:else if timeline || isProvisioning}
{:else if timeline || isSettingUp}
<BranchTimeline
timeline={timeline ?? emptyTimeline}
repoDir={branch.worktreePath}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
repoLabel?: ProjectRepo | null;
isLocal: boolean;
isRemote: boolean;
isProvisioning: boolean;
isSettingUp: boolean;
remoteWorkspaceStatus: string | null;
onDelete?: () => void;
onRename?: (branchName: string) => void;
Expand All @@ -77,7 +77,7 @@
repoLabel = null,
isLocal,
isRemote,
isProvisioning,
isSettingUp,
remoteWorkspaceStatus,
onDelete,
onRename,
Expand Down Expand Up @@ -617,7 +617,7 @@
</div>
{/each}
<!-- Primary run action button -->
{#if !isProvisioning && primaryRunAction}
{#if !isSettingUp && primaryRunAction}
{@const execution = primaryActionExecution}
{@const isRunning = execution?.status === 'running'}
{@const isStopping = execution && stoppingExecutions.has(execution.executionId)}
Expand Down Expand Up @@ -755,7 +755,7 @@
</button>
{#if showMoreMenu}
<div class="more-menu">
{#if !isProvisioning}
{#if !isSettingUp}
<!-- Remote-only: Copy workspace name -->
{#if isRemote && branch.workspaceName}
<button
Expand Down
1 change: 1 addition & 0 deletions apps/staged/src/lib/features/projects/ProjectHome.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@
setProjects(projectsList);
projects = projectsList;
branchesByProject = new Map(branchesByProject).set(projectId, branches);
commands.invalidateProjectBranchTimelines(branches.map((b) => b.id));
workspaceLifecycle.enqueueInitialSetup(projectId, branches);
replaceProjectRepos(projectId, repos);
void repoBadgeStore.ensureForRepos(
Expand Down