mirror of
https://github.com/davidkaya/aryx.git
synced 2026-08-27 21:33:58 +02:00
feat: add session branching frontend UI
- Add "Branch from here" hover button on user messages in ChatPane - Wire onBranchFromMessage to api.branchSession IPC call - Show branch origin banner at top of branched session transcripts - Display GitBranch indicator on branched sessions in sidebar - Resolve source session title from workspace for origin display Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -571,6 +571,14 @@ export default function App() {
|
|||||||
autoApprovedToolNames: settings.autoApprovedToolNames,
|
autoApprovedToolNames: settings.autoApprovedToolNames,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
|
onBranchFromMessage={(messageId) => {
|
||||||
|
void api.branchSession({ sessionId: selectedSession.id, messageId });
|
||||||
|
}}
|
||||||
|
branchOriginLabel={
|
||||||
|
selectedSession.branchOrigin
|
||||||
|
? workspace.sessions.find((s) => s.id === selectedSession.branchOrigin!.sourceSessionId)?.title
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
availableModels={availableModels}
|
availableModels={availableModels}
|
||||||
mcpProbingServerIds={workspace.mcpProbingServerIds}
|
mcpProbingServerIds={workspace.mcpProbingServerIds}
|
||||||
onTerminalToggle={handleTerminalToggle}
|
onTerminalToggle={handleTerminalToggle}
|
||||||
|
|||||||
@@ -64,6 +64,8 @@ interface ChatPaneProps {
|
|||||||
}) => Promise<unknown>;
|
}) => Promise<unknown>;
|
||||||
onUpdateSessionTooling?: (selection: SessionToolingSelection) => void;
|
onUpdateSessionTooling?: (selection: SessionToolingSelection) => void;
|
||||||
onUpdateSessionApprovalSettings?: (settings: { autoApprovedToolNames?: string[] }) => void;
|
onUpdateSessionApprovalSettings?: (settings: { autoApprovedToolNames?: string[] }) => void;
|
||||||
|
onBranchFromMessage?: (messageId: string) => void;
|
||||||
|
branchOriginLabel?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ChatPane({
|
export function ChatPane({
|
||||||
@@ -90,6 +92,8 @@ export function ChatPane({
|
|||||||
onUpdateSessionModelConfig,
|
onUpdateSessionModelConfig,
|
||||||
onUpdateSessionTooling,
|
onUpdateSessionTooling,
|
||||||
onUpdateSessionApprovalSettings,
|
onUpdateSessionApprovalSettings,
|
||||||
|
onBranchFromMessage,
|
||||||
|
branchOriginLabel,
|
||||||
}: ChatPaneProps) {
|
}: ChatPaneProps) {
|
||||||
const [hasComposerContent, setHasComposerContent] = useState(false);
|
const [hasComposerContent, setHasComposerContent] = useState(false);
|
||||||
const [configError, setConfigError] = useState<string>();
|
const [configError, setConfigError] = useState<string>();
|
||||||
@@ -327,6 +331,18 @@ export function ChatPane({
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="mx-auto max-w-3xl px-6 py-4">
|
<div className="mx-auto max-w-3xl px-6 py-4">
|
||||||
|
{/* Branch origin banner */}
|
||||||
|
{session.branchOrigin && (
|
||||||
|
<div className="mb-4 flex items-center gap-2.5 rounded-lg border border-[var(--color-border-subtle)] bg-[var(--color-surface-1)]/60 px-3.5 py-2.5 text-[12px] text-[var(--color-text-secondary)]">
|
||||||
|
<GitBranch className="size-3.5 shrink-0 text-[var(--color-accent)]" />
|
||||||
|
<span>
|
||||||
|
Branched from{' '}
|
||||||
|
<span className="font-medium text-[var(--color-text-primary)]">
|
||||||
|
{branchOriginLabel ?? 'a previous session'}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
{session.messages.map((message, index) => {
|
{session.messages.map((message, index) => {
|
||||||
const isUser = message.role === 'user';
|
const isUser = message.role === 'user';
|
||||||
@@ -364,6 +380,17 @@ export function ChatPane({
|
|||||||
{phaseLabel}
|
{phaseLabel}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
{isUser && onBranchFromMessage && (
|
||||||
|
<button
|
||||||
|
className="ml-auto flex items-center gap-1 rounded-md px-1.5 py-0.5 text-[11px] text-[var(--color-text-muted)] opacity-0 transition-all duration-150 hover:bg-[var(--color-surface-2)] hover:text-[var(--color-accent)] group-hover:opacity-100"
|
||||||
|
onClick={() => onBranchFromMessage(message.id)}
|
||||||
|
title="Branch from here — create a new session starting from this message"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<GitBranch className="size-3" />
|
||||||
|
Branch
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className={
|
className={
|
||||||
|
|||||||
@@ -296,6 +296,11 @@ function SessionItem({
|
|||||||
Archived
|
Archived
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
{session.branchOrigin && (
|
||||||
|
<span className="inline-flex items-center gap-0.5 text-[10px] text-[var(--color-text-muted)]" title="Branched session">
|
||||||
|
<GitBranch className="size-2.5" />
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
<span className="ml-auto text-[10px] text-[var(--color-text-muted)] group-hover:text-[var(--color-text-secondary)]">
|
<span className="ml-auto text-[10px] text-[var(--color-text-muted)] group-hover:text-[var(--color-text-secondary)]">
|
||||||
{relativeTime(session.updatedAt)}
|
{relativeTime(session.updatedAt)}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
Reference in New Issue
Block a user