Merge branch 'openchamber:main' into fix/walkthrough-remote-default-branch
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user