Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
792ef17
feat(web): choose a sidebar layout in Settings
quanticstudios Nov 5, 2026
d336c2e
fix(web): Allow and Deny on a timeline approval are real buttons
quanticstudios Nov 5, 2026
0250670
fix(web): say Waiting for you while a thread is blocked on you
quanticstudios Nov 5, 2026
e8a3713
feat(web): a setting for a quiet stop button while a thread waits on you
quanticstudios Nov 5, 2026
6ec638e
fix(web): a thread waiting on approval no longer reads quieter than a…
quanticstudios Nov 5, 2026
da1ecb8
fix(web): raise the dimmest muted text
quanticstudios Nov 5, 2026
22a2892
fix(web): grow small targets to 24px with pseudo-elements
quanticstudios Nov 5, 2026
0ec1854
fix(web): number keys pick a question's option while the answer box i…
quanticstudios Nov 5, 2026
748b1b8
fix(web): stop floating a classified failure over the first message
quanticstudios Nov 5, 2026
341b2bd
fix(web): correct the scope sentence on read-only settings pages
quanticstudios Nov 5, 2026
c62b9a3
fix(web): keep the terminal's actions off its first line
quanticstudios Nov 5, 2026
f87639a
feat(web): a setting for a shimmer on the chat's Working row
quanticstudios Nov 5, 2026
3fb344d
feat(web): a setting to hide the provider logo when only one exists
quanticstudios Nov 5, 2026
b008ce0
feat(web): a setting for the waiting count in the window title
quanticstudios Nov 5, 2026
8404818
fix(web): the worker guide stops at a tinted row in the Detailed sidebar
quanticstudios Nov 5, 2026
28984be
feat(web): a provider badge on each Detailed and Slim row
quanticstudios Nov 5, 2026
d66fbf8
fix(web): keep Advanced last in the Composer settings
quanticstudios Nov 5, 2026
fab4cd5
feat(web): Detailed and Slim rows copy pitwall's sidebar rows
quanticstudios Nov 5, 2026
051b56d
Merge main into t3-design-pass
quanticstudios Nov 6, 2026
aef73c3
fix(web): Settle stays reachable beside answers on Detailed rows, and…
quanticstudios Nov 6, 2026
402f842
fix(web): number keys act only outside the answer box
quanticstudios Nov 6, 2026
7249e5c
fix(web): Slim counts workers with a static robot, not a chevron
quanticstudios Nov 6, 2026
ddde15e
fix(web): Detailed answers that do not fit take their own line
quanticstudios Nov 6, 2026
5b4fb3f
feat(web): a setting for the terminal's actions strip
quanticstudios Nov 6, 2026
6abd78a
fix(web): Detailed and Slim pills and dots reach their contrast targets
quanticstudios Nov 6, 2026
db418a9
fix(web): commit 6's surfaces use T3's full muted tokens
quanticstudios Nov 6, 2026
7d29b8c
fix(web): the time hides under Settle on keyboard focus
quanticstudios Nov 6, 2026
478b41f
fix(web): Detailed and Slim rows keep their marks in forced colors
quanticstudios Nov 6, 2026
d576ca0
fix(web): row animations stop while the sidebar is collapsed
quanticstudios Nov 6, 2026
487cb2c
test(web): fold chip closing and focus recovery
quanticstudios Nov 6, 2026
5646f86
refactor(web): drop rules and row attributes nothing reads
quanticstudios Nov 6, 2026
5396447
docs: keep the sidebar layout section to its settings path
quanticstudios Nov 6, 2026
0b30bea
fix(web): Detailed and Slim pills keep their state colour
quanticstudios Nov 6, 2026
87fa740
fix(web): activating a thread with a question lets go of the row
quanticstudios Nov 6, 2026
511a038
fix(web): keyboard focus hides the time only for the action beside it
quanticstudios Nov 6, 2026
91cb9ab
fix(web): light theme Approval stays amber, not red
quanticstudios Nov 6, 2026
17077b4
fix(web): releasing row focus leaves a rename input alone
quanticstudios Nov 6, 2026
da7e5ab
Merge remote-tracking branch 'github/main' into t3-design-pass
maria-rcks Nov 6, 2026
edc2727
fix(web): wrapped answer pills and notice links keep their own hit areas
maria-rcks Nov 6, 2026
46f8f16
Merge remote-tracking branch 'github/main' into t3-design-pass
maria-rcks Nov 7, 2026
f50a375
fix(web): window title counts only enabled environments
maria-rcks Nov 7, 2026
67cd62a
fix(web): row answer pills keep a 24px hit area on a coarse pointer
quanticstudios Nov 8, 2026
a5eb46c
fix(web): window title counts the threads in Waiting on you
quanticstudios Nov 8, 2026
0cf5a7e
Merge remote-tracking branch 'github/main' into t3-design-pass
maria-rcks Nov 10, 2026
056d856
fix(web): long approval labels stay readable on hover
maria-rcks Nov 10, 2026
bac7bda
fix(web): close sidebar answers when opening a thread
maria-rcks Nov 10, 2026
92200a2
Merge remote-tracking branch 'github/main' into t3-design-pass
maria-rcks Nov 11, 2026
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
32 changes: 31 additions & 1 deletion apps/web/src/appInit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { startMainAppLocationTracking } from "./components/sidebar/mainAppLocati
import { startDesktopUpdateLaunchNotice } from "./components/sidebar/updateEverythingRun";
import { startSlowRpcRequestToasts } from "./components/SlowRpcRequestToastCoordinator";
import { startThreadNotifications } from "./components/ThreadNotificationCoordinator";
import { waitingOnYouCountAtom } from "./components/sidebar/variants/sidebarVariant.logic";
import { startGuideRunNotifications } from "./components/pullRequest/guide/PullRequestGuideRunCoordinator";
import { APP_BASE_NAME, APP_DISPLAY_NAME, APP_STAGE_LABEL } from "./branding";
import { resolveServerBackedAppDisplayName } from "./branding.logic";
Expand Down Expand Up @@ -117,19 +118,48 @@ function startClientAppearance(): void {
apply();
}

const waitingThreadCountAtom = waitingOnYouCountAtom(environmentThreadShells);

function startDocumentTitle(): void {
let name = document.title;
let waiting = 0;
let stopCounting: (() => void) | null = null;
const apply = () => {
document.title = waiting > 0 ? `(${waiting}) ${name}` : name;
};
const applySetting = () => {
const enabled = getClientSettings().windowTitleWaitingCount;
if (enabled && stopCounting === null) {
stopCounting = appAtomRegistry.subscribe(
waitingThreadCountAtom,
(count) => {
waiting = count;
apply();
},
{ immediate: true },
);
} else if (!enabled && stopCounting !== null) {
stopCounting();
stopCounting = null;
waiting = 0;
apply();
}
};
subscribeClientSettings(applySetting);
appAtomRegistry.subscribe(
primaryServerConfigAtom,
(config) => {
document.title = resolveServerBackedAppDisplayName({
name = resolveServerBackedAppDisplayName({
baseName: APP_BASE_NAME,
fallbackDisplayName: APP_DISPLAY_NAME,
fallbackStageLabel: APP_STAGE_LABEL,
primaryServerVersion: config?.environment.serverVersion ?? null,
});
apply();
},
{ immediate: true },
);
applySetting();
}

/**
Expand Down
23 changes: 18 additions & 5 deletions apps/web/src/components/ChatView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,7 @@ import {
getThreadErrorBannerKey,
isThreadErrorBannerDismissedForSession,
shouldShowThreadErrorBanner,
threadFailureIsShownElsewhere,
} from "./chat/ThreadErrorBanner";
import { QueuedRunsControl, type QueuedRunsControlHandle } from "./chat/QueuedRunsControl";
import { useThreadLinkedPullRequest } from "./pullRequest/useThreadLinkedPullRequest";
Expand Down Expand Up @@ -997,7 +998,10 @@ export default function ChatView(props: ChatViewProps) {
snoozedUntil: activeThreadShell?.snoozedUntil,
});
const timelineThreadError =
limitRecoveryRun !== null && visibleThreadError === serverRuntime?.lastError
serverRuntime?.status === "failed" &&
threadFailureIsShownElsewhere(serverRuntime.lastErrorClass, serverRuntime.lastErrorCode) &&
activeThreadShell?.latestRun &&
visibleThreadError === serverRuntime.lastError
? null
: visibleThreadError;

Expand Down Expand Up @@ -1799,6 +1803,15 @@ export default function ChatView(props: ChatViewProps) {
composerRef.current?.focusAtEnd();
};
const focusComposerStable = useStableCallback(focusComposer);
// why: the number keys pick a question's option only while focus is outside the answer box.
const focusComposerUnlessQuestionWaits = useStableCallback(() => {
if (
!activePendingProgress?.activeQuestion ||
activePendingUserInput?.responseCapability === "not_resumable"
) {
focusComposer();
}
});
const canInterruptRunningThread =
canOperateThread && deriveCanInterruptRunningThread(activeThread !== undefined, activeRuntime);
const onInterrupt = async () => {
Expand Down Expand Up @@ -2117,12 +2130,12 @@ export default function ChatView(props: ChatViewProps) {
useEffect(() => {
if (embedded || !activeThread?.id || terminalUiState.terminalOpen) return;
const frame = window.requestAnimationFrame(() => {
focusComposerStable();
focusComposerUnlessQuestionWaits();
});
return () => {
window.cancelAnimationFrame(frame);
};
}, [activeThread?.id, embedded, focusComposerStable, terminalUiState.terminalOpen]);
}, [activeThread?.id, embedded, focusComposerUnlessQuestionWaits, terminalUiState.terminalOpen]);

// oxlint-disable-next-line t3code/no-unreasoned-state-sync -- window focus listener that refocuses the composer after the window regains focus
useEffect(() => {
Expand All @@ -2139,7 +2152,7 @@ export default function ChatView(props: ChatViewProps) {
!isInSideChatPane(document.activeElement) &&
shouldRefocusComposerOnWindowFocus(document.activeElement)
) {
focusComposerStable();
focusComposerUnlessQuestionWaits();
}
});
});
Expand All @@ -2152,7 +2165,7 @@ export default function ChatView(props: ChatViewProps) {
}, [
activeThread?.id,
embedded,
focusComposerStable,
focusComposerUnlessQuestionWaits,
isMobileViewport,
terminalUiState.terminalOpen,
]);
Expand Down
16 changes: 5 additions & 11 deletions apps/web/src/components/CommandPaletteResults.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -186,9 +186,7 @@ function ThreadRowContext({
.filter((part) => part && part !== item.matchHint?.text)
.join(" · ");
return context ? (
<span className="max-w-[45%] shrink-0 truncate text-muted-foreground/70 text-xs">
{context}
</span>
<span className="max-w-[45%] shrink-0 truncate text-muted-foreground text-xs">{context}</span>
) : null;
}

Expand All @@ -210,9 +208,7 @@ function DisabledCommandPaletteResultRow(props: {
) : props.item.matchHint ? (
<SearchMatchHintLine hint={props.item.matchHint} />
) : props.item.description ? (
<span className="min-w-0 text-muted-foreground/70 text-xs">
{props.item.description}
</span>
<span className="min-w-0 text-muted-foreground text-xs">{props.item.description}</span>
) : null}
</span>
) : (
Expand Down Expand Up @@ -272,9 +268,7 @@ function CommandPaletteResultRow(props: {
) : props.item.matchHint ? (
<SearchMatchHintLine hint={props.item.matchHint} />
) : props.item.description ? (
<span className="min-w-0 text-muted-foreground/70 text-xs">
{props.item.description}
</span>
<span className="min-w-0 text-muted-foreground text-xs">{props.item.description}</span>
) : null}
</span>
) : (
Expand All @@ -293,13 +287,13 @@ function CommandPaletteResultRow(props: {
/>
) : null}
{props.item.timestamp ? (
<span className="min-w-12 shrink-0 text-right text-xs tabular-nums text-muted-foreground/70">
<span className="min-w-12 shrink-0 text-right text-xs tabular-nums text-muted-foreground">
{props.item.timestamp}
</span>
) : null}
{shortcutLabel ? <CommandShortcut>{shortcutLabel}</CommandShortcut> : null}
{props.item.kind === "submenu" ? (
<ChevronRightIcon className="-me-0.5 ms-auto size-4 shrink-0 text-muted-foreground/70" />
<ChevronRightIcon className="-me-0.5 ms-auto size-4 shrink-0 text-muted-foreground" />
) : null}
</CommandItem>
);
Expand Down
12 changes: 12 additions & 0 deletions apps/web/src/components/Sidebar.logic.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,18 @@ describe("resolveSidebarThreadStatus", () => {
).toBe(false);
});

it("never recedes a thread waiting on approval, even once read", () => {
expect(
shouldRecedeSidebarThread({
status: "approval",
isUnread: false,
isWoke: false,
isActive: false,
isSelected: false,
}),
).toBe(false);
});

it("reports worktree setup as preparing, and failed only while the latest run failed", () => {
expect(
resolveSidebarThreadStatus({
Expand Down
3 changes: 2 additions & 1 deletion apps/web/src/components/Sidebar.logic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -834,6 +834,7 @@ export function shouldRecedeSidebarThread(input: {
if (
input.isActive ||
input.isSelected ||
input.status === "approval" ||
input.status === "input" ||
input.status === "request"
) {
Expand All @@ -842,7 +843,7 @@ export function shouldRecedeSidebarThread(input: {
if (input.status === "preparing" || input.status === "working" || input.status === "waiting") {
return true;
}
if (input.status === "ready" || input.status === "approval") {
if (input.status === "ready") {
return !input.isUnread && !input.isWoke;
}
return false;
Expand Down
Loading
Loading