Merge branch 'openchamber:main' into fix/walkthrough-remote-default-branch

This commit is contained in:
Rajat Asthana
2026-08-04 22:27:56 +05:30
committed by GitHub
47 changed files with 1579 additions and 333 deletions
@@ -6,10 +6,10 @@ import { useI18n } from '@/lib/i18n';
import { useConfigStore } from '@/stores/useConfigStore';
import { runtimeFetch } from '@/lib/runtime-fetch';
import { updateDesktopSettings } from '@/lib/persistence';
import type { WalkthroughBlockedReason, WalkthroughModel } from '@/lib/walkthrough/types';
import type { WalkthroughBlockedState, WalkthroughModel } from '@/lib/walkthrough/types';
interface WalkthroughBlockerProps {
reason: WalkthroughBlockedReason;
reason: WalkthroughBlockedState;
model?: WalkthroughModel;
requiredChars?: number;
availableChars?: number;
@@ -102,6 +102,7 @@ export const WalkthroughBlocker = ({
if (reason === 'no-model') return t('walkthrough.blocked.noModel.description');
if (reason === 'empty-diff') return t('walkthrough.blocked.emptyDiff.description');
if (reason === 'only-generated') return t('walkthrough.blocked.onlyGenerated.description');
if (reason === 'server-unsupported') return t('walkthrough.blocked.serverUnsupported.description');
if (reason === 'output-exhausted') {
return label
? t('walkthrough.blocked.outputExhausted.description', { model: label })
@@ -125,6 +126,7 @@ export const WalkthroughBlocker = ({
if (reason === 'no-model') return t('walkthrough.blocked.noModel.title');
if (reason === 'empty-diff') return t('walkthrough.blocked.emptyDiff.title');
if (reason === 'only-generated') return t('walkthrough.blocked.onlyGenerated.title');
if (reason === 'server-unsupported') return t('walkthrough.blocked.serverUnsupported.title');
if (reason === 'output-exhausted') return t('walkthrough.blocked.outputExhausted.title');
if (reason === 'structured-output-unsupported') return t('walkthrough.blocked.structuredOutput.title');
return t('walkthrough.blocked.contextTooSmall.title');
@@ -156,7 +158,9 @@ export const WalkthroughBlocker = ({
</div>
)}
{(reason === 'empty-diff' || reason === 'only-generated') && (
{/* Retry is the whole remedy once the server is updated, so it stays in
reach rather than sending the user back through the panel header. */}
{(reason === 'empty-diff' || reason === 'only-generated' || reason === 'server-unsupported') && (
<Button type="button" variant="outline" size="sm" onClick={onRetry}>
{t('walkthrough.action.refresh')}
</Button>
@@ -2,6 +2,7 @@ import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { Icon } from '@/components/icon/Icon';
import { FileTypeIcon } from '@/components/icons/FileTypeIcon';
import { Button } from '@/components/ui/button';
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
import { useI18n } from '@/lib/i18n';
import { groupHunksByFile } from '@/lib/walkthrough/model';
import type { WalkthroughStopView, WalkthroughView } from '@/lib/walkthrough/model';
@@ -20,8 +21,13 @@ interface WalkthroughStreamProps {
wrapLines: boolean;
}
// Importance says where to spend attention, not what is wrong: a stop is marked
// because it drives the rest of the change, never because something was found in
// it. A red pill said the opposite — status colours are read as findings, and a
// walkthrough deliberately hands out no verdicts — so the emphasis is carried by
// weight and an outline instead, and the tooltip states the axis outright.
const IMPORTANCE_CLASS: Record<WalkthroughStopImportance, string> = {
critical: 'bg-status-error/10 text-status-error',
critical: 'border border-[var(--interactive-border)] font-medium text-foreground',
normal: 'bg-surface-muted text-muted-foreground',
context: 'bg-surface-muted text-muted-foreground',
};
@@ -41,11 +47,22 @@ const StopHeader = ({ stopView }: { stopView: WalkthroughStopView }) => {
exactly as tall as one without: vertical padding on a smaller type
size was pushing past the tallest element in the row. */}
{stop.importance !== 'normal' && (
<span className={cn('typography-micro flex h-5 items-center rounded px-1.5 leading-none', IMPORTANCE_CLASS[stop.importance])}>
{stop.importance === 'critical'
? t('walkthrough.importance.critical')
: t('walkthrough.importance.context')}
</span>
<Tooltip>
<TooltipTrigger
className={cn('typography-micro flex h-5 items-center rounded px-1.5 leading-none', IMPORTANCE_CLASS[stop.importance])}
>
{stop.importance === 'critical'
? t('walkthrough.importance.critical')
: t('walkthrough.importance.context')}
</TooltipTrigger>
<TooltipContent className="max-w-64">
<p className="typography-micro leading-tight">
{stop.importance === 'critical'
? t('walkthrough.importance.criticalHint')
: t('walkthrough.importance.contextHint')}
</p>
</TooltipContent>
</Tooltip>
)}
</div>
<p className="typography-body text-muted-foreground">{stop.prose}</p>
@@ -10,7 +10,9 @@ import {
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
import { useI18n, type Locale } from '@/lib/i18n';
import { openExternalUrl } from '@/lib/url';
import { buildWalkthroughView } from '@/lib/walkthrough/model';
import type { WalkthroughSource, WalkthroughWorkingTreeScope } from '@/lib/walkthrough/types';
import { ModelSelector } from '@/components/sections/agents/ModelSelector';
@@ -41,6 +43,12 @@ interface WalkthroughViewProps {
const SCOPES: WalkthroughWorkingTreeScope[] = ['all', 'staged', 'working'];
// What a walkthrough is — and what it deliberately is not — cannot be read off
// the panel: the first question users asked about it was whether its marks were
// review findings. The guide answers that, so it is reachable from the surface
// itself rather than only from the release announcement.
const WALKTHROUGH_GUIDE_URL = 'https://docs.openchamber.dev/walkthrough/';
// DropdownMenuLabel defaults to the same size and weight as its items, which
// makes a heading read as another choice. This matches SelectLabel, the
// treatment used by the worktree picker.
@@ -444,6 +452,9 @@ export const WalkthroughView = ({ directory }: WalkthroughViewProps) => {
|| entry.error?.code === 'empty-diff'
|| entry.error?.code === 'only-generated'
|| entry.error?.code === 'output-exhausted'
// Client-detected rather than reported: the server answered something that
// was not JSON, so it has no walkthrough routes at all.
|| entry.error?.code === 'server-unsupported'
? entry.error.code
: entry.readiness && !entry.readiness.ready && !view
&& entry.readiness.reason !== 'no-provider-login'
@@ -536,6 +547,25 @@ export const WalkthroughView = ({ directory }: WalkthroughViewProps) => {
</DropdownMenu>
<div className="ml-auto flex min-w-0 items-center gap-1">
<Tooltip>
<TooltipTrigger asChild>
<Button
type="button"
variant="ghost"
size="sm"
aria-label={t('walkthrough.help.guide')}
onClick={() => {
void openExternalUrl(WALKTHROUGH_GUIDE_URL);
}}
>
<Icon name="question" className="size-4" />
</Button>
</TooltipTrigger>
<TooltipContent>
<p className="typography-micro leading-tight">{t('walkthrough.help.guide')}</p>
</TooltipContent>
</Tooltip>
{/* A walkthrough nobody can read is worth nothing, so the prose
language is a per-review choice like the model — defaulting to the
interface language, which is the best evidence of what the reader