diff --git a/apps/desktop/src/settings/DesktopClientSettings.test.ts b/apps/desktop/src/settings/DesktopClientSettings.test.ts
index cb63ba62605a..4867d8524093 100644
--- a/apps/desktop/src/settings/DesktopClientSettings.test.ts
+++ b/apps/desktop/src/settings/DesktopClientSettings.test.ts
@@ -18,6 +18,7 @@ const clientSettings: ClientSettings = {
browserDefaultZoomFactor: 1.25,
browserDefaultAppearance: "dark",
browserAutoShowFloatingPreview: false,
+ compactLegacySidebarStatuses: false,
confirmQuit: true,
confirmThreadArchive: true,
confirmThreadDelete: false,
diff --git a/apps/desktop/src/settings/LastCodeSettingsImport.test.ts b/apps/desktop/src/settings/LastCodeSettingsImport.test.ts
index 0d4a992a97de..f2ff48a9ae4c 100644
--- a/apps/desktop/src/settings/LastCodeSettingsImport.test.ts
+++ b/apps/desktop/src/settings/LastCodeSettingsImport.test.ts
@@ -91,6 +91,7 @@ describe("LastCodeSettingsImport", () => {
const sourceCustom = ProviderInstanceId.make("source_custom");
const lastCodeCustom = ProviderInstanceId.make("lastcode_custom");
const sourceClient = { ...DEFAULT_CLIENT_SETTINGS, fontSizeInterface: 17 };
+ Reflect.deleteProperty(sourceClient, "compactLegacySidebarStatuses");
Reflect.deleteProperty(sourceClient, "legacySidebarScale");
Reflect.deleteProperty(sourceClient, "roundedProjectIcons");
sourceClient.favorites = [
@@ -103,6 +104,7 @@ describe("LastCodeSettingsImport", () => {
};
const destinationClient = {
...DEFAULT_CLIENT_SETTINGS,
+ compactLegacySidebarStatuses: true,
legacySidebarScale: 75,
roundedProjectIcons: true,
environmentIconColors: { primary: "#2563eb", remote: "#7c3aed" },
@@ -215,6 +217,7 @@ describe("LastCodeSettingsImport", () => {
) as unknown,
);
assert.equal(importedClient.fontSizeInterface, 17);
+ assert.equal(importedClient.compactLegacySidebarStatuses, true);
assert.equal(importedClient.legacySidebarScale, 75);
assert.equal(importedClient.roundedProjectIcons, true);
assert.deepEqual(importedClient.environmentIconColors, {
diff --git a/apps/desktop/src/settings/LastCodeSettingsImport.ts b/apps/desktop/src/settings/LastCodeSettingsImport.ts
index d21cfdfbd12f..806b227f9564 100644
--- a/apps/desktop/src/settings/LastCodeSettingsImport.ts
+++ b/apps/desktop/src/settings/LastCodeSettingsImport.ts
@@ -236,6 +236,7 @@ function mergeClientSettings(sourceRaw: string, destinationRaw: string | null):
...source,
favorites,
providerModelPreferences,
+ compactLegacySidebarStatuses: destination.compactLegacySidebarStatuses,
environmentIconColors: destination.environmentIconColors,
legacySidebarScale: destination.legacySidebarScale,
roundedProjectIcons: destination.roundedProjectIcons,
diff --git a/apps/web/src/components/LegacySidebar.tsx b/apps/web/src/components/LegacySidebar.tsx
index 1c632a4d48f7..45172ea581c8 100644
--- a/apps/web/src/components/LegacySidebar.tsx
+++ b/apps/web/src/components/LegacySidebar.tsx
@@ -340,6 +340,7 @@ function buildThreadJumpLabelMap(input: {
interface SidebarThreadRowProps {
thread: SidebarThreadSummary;
+ compactStatusIndicators: boolean;
showLocalEnvironmentIcon: boolean;
configuredEnvironmentIconColor: EnvironmentIconColor | undefined;
projectCwd: string | null;
@@ -889,7 +890,9 @@ export const SidebarThreadRow = memo(function SidebarThreadRow(props: SidebarThr
)}
- {threadStatus && }
+ {threadStatus && (
+
+ )}
{renamingThreadKey === threadKey ? (
>;
environmentIconColors: Readonly>;
@@ -1201,6 +1205,7 @@ const SidebarProjectThreadList = memo(function SidebarProjectThreadList(
) {
const {
legacySidebarScale,
+ compactStatusIndicators,
scaleStyle,
providerEntriesByEnvironmentId,
environmentIconColors,
@@ -1271,6 +1276,7 @@ const SidebarProjectThreadList = memo(function SidebarProjectThreadList(
>;
desktopLocalEnvironmentIds: ReadonlySet;
@@ -1372,6 +1379,7 @@ interface SidebarProjectItemProps {
const SidebarProjectItem = memo(function SidebarProjectItem(props: SidebarProjectItemProps) {
const {
legacySidebarScale,
+ compactStatusIndicators,
scaleStyle,
providerEntriesByEnvironmentId,
project,
@@ -2755,6 +2763,7 @@ const SidebarProjectItem = memo(function SidebarProjectItem(props: SidebarProjec
void;
projectsLength: number;
legacySidebarScale: LegacySidebarScale;
+ compactStatusIndicators: boolean;
projectTreeScaleStyle: CSSProperties;
providerEntriesByEnvironmentId: ReadonlyMap>;
desktopLocalEnvironmentIds: ReadonlySet;
@@ -3328,6 +3338,7 @@ const SidebarProjectsContent = memo(function SidebarProjectsContent(
attachProjectListAutoAnimateRef,
projectsLength,
legacySidebarScale,
+ compactStatusIndicators,
projectTreeScaleStyle,
providerEntriesByEnvironmentId,
desktopLocalEnvironmentIds,
@@ -3461,6 +3472,7 @@ const SidebarProjectsContent = memo(function SidebarProjectsContent(
{(dragHandleProps) => (
s.sidebarThreadPreviewCount);
const legacySidebarScale = useClientSettings((s) => s.legacySidebarScale);
+ const compactStatusIndicators = useClientSettings(
+ (settings) => settings.compactLegacySidebarStatuses,
+ );
const environmentIconColors = useClientSettings((s) => s.environmentIconColors);
const showLocalEnvironmentIcon = useClientSettings((s) => s.showLocalEnvironmentIcon);
const serverProviders = useAtomValue(primaryServerProvidersAtom);
@@ -4247,6 +4263,7 @@ export default function LegacySidebar() {
attachProjectListAutoAnimateRef={attachProjectListAutoAnimateRef}
projectsLength={projects.length}
legacySidebarScale={legacySidebarScale}
+ compactStatusIndicators={compactStatusIndicators}
projectTreeScaleStyle={scaleStyle}
providerEntriesByEnvironmentId={providerEntriesByEnvironmentId}
desktopLocalEnvironmentIds={desktopLocalEnvironmentIds}
diff --git a/apps/web/src/components/ThreadStatusIndicators.tsx b/apps/web/src/components/ThreadStatusIndicators.tsx
index b2a8dcb7046e..a94fc5e6e6b0 100644
--- a/apps/web/src/components/ThreadStatusIndicators.tsx
+++ b/apps/web/src/components/ThreadStatusIndicators.tsx
@@ -361,6 +361,7 @@ export function ThreadStatusLabel({
diff --git a/apps/web/src/components/settings/LastCodeSettings.tsx b/apps/web/src/components/settings/LastCodeSettings.tsx
index 537b02ffd020..c083c65d5291 100644
--- a/apps/web/src/components/settings/LastCodeSettings.tsx
+++ b/apps/web/src/components/settings/LastCodeSettings.tsx
@@ -234,6 +234,19 @@ export function LastCodeSettingsPanel() {
/>
}
/>
+
+ updateClientSettings({ compactLegacySidebarStatuses: Boolean(checked) })
+ }
+ aria-label="Compact status indicators"
+ />
+ }
+ />
{
it("matches normalized title substrings", () => {
expect(searchSettings(" WORD WRAP ", ITEMS).map((item) => item.id)).toEqual(["word-wrap"]);
expect(searchSettings("glass").map((item) => item.id)).toEqual(["setting-glass-opacity"]);
+ expect(searchSettings("compact status").map((item) => item.id)).toEqual([
+ "compact-status-indicators",
+ ]);
expect(searchSettings("xyzzy")).toEqual([]);
});
diff --git a/apps/web/src/components/settings/settingsSearch.ts b/apps/web/src/components/settings/settingsSearch.ts
index f59bb20b8cdd..1d94858750c9 100644
--- a/apps/web/src/components/settings/settingsSearch.ts
+++ b/apps/web/src/components/settings/settingsSearch.ts
@@ -260,6 +260,11 @@ export const SETTINGS_SEARCH_ITEMS = [
title: "Scale legacy sidebar",
to: "/settings/lastcode",
},
+ {
+ id: "compact-status-indicators",
+ title: "Compact status indicators",
+ to: "/settings/lastcode",
+ },
{
id: "rounded-project-icons",
title: "Rounded project icons",
diff --git a/docs/user/thread-sidebar.md b/docs/user/thread-sidebar.md
index 4a86d647afda..955387a97216 100644
--- a/docs/user/thread-sidebar.md
+++ b/docs/user/thread-sidebar.md
@@ -65,6 +65,11 @@ LastCode header, Search field, Projects heading, drafts, status notices, and foo
their standard size. On desktop, **View -> Actual Size**, **Zoom In**, and **Zoom Out** continue to
zoom the whole application and compose with the legacy sidebar scale.
+To keep the same status colors while using less horizontal space, enable **Compact status
+indicators** in **Settings -> LastCode -> Appearance**. Legacy thread rows then show only the
+colored status dot; the full status remains available as a tooltip. This preference is off by
+default.
+
## Environment icons in LastCode
Open **Settings -> LastCode -> Environments** to choose the icon color for the primary machine and
diff --git a/packages/contracts/src/settings.test.ts b/packages/contracts/src/settings.test.ts
index 23b585778464..a93d564b40ab 100644
--- a/packages/contracts/src/settings.test.ts
+++ b/packages/contracts/src/settings.test.ts
@@ -115,6 +115,7 @@ describe("ClientSettings environment icons", () => {
describe("ClientSettings sidebar", () => {
it("defaults to the current sidebar with automatic merge and inactivity settling", () => {
const settings = decodeClientSettings({});
+ expect(settings.compactLegacySidebarStatuses).toBe(false);
expect(settings.legacySidebarEnabled).toBe(false);
expect(settings.legacySidebarScale).toBe(100);
expect(settings.sidebarAutoSettleAfterDays).toBe(3);
@@ -138,6 +139,16 @@ describe("ClientSettings sidebar", () => {
);
});
+ it("preserves compact legacy sidebar status indicators", () => {
+ expect(
+ decodeClientSettings({ compactLegacySidebarStatuses: true }).compactLegacySidebarStatuses,
+ ).toBe(true);
+ expect(
+ decodeClientSettingsPatch({ compactLegacySidebarStatuses: true })
+ .compactLegacySidebarStatuses,
+ ).toBe(true);
+ });
+
it.each([50, 75, 100])("accepts a legacy sidebar scale within 50..100: %s", (value) => {
expect(decodeClientSettings({ legacySidebarScale: value }).legacySidebarScale).toBe(value);
expect(decodeClientSettingsPatch({ legacySidebarScale: value }).legacySidebarScale).toBe(value);
diff --git a/packages/contracts/src/settings.ts b/packages/contracts/src/settings.ts
index d65f63759974..aa09a5e6eb0b 100644
--- a/packages/contracts/src/settings.ts
+++ b/packages/contracts/src/settings.ts
@@ -181,6 +181,9 @@ export const ClientSettingsSchema = Schema.Struct({
confirmQuit: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(true))),
confirmThreadArchive: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(false))),
confirmThreadDelete: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(true))),
+ compactLegacySidebarStatuses: Schema.Boolean.pipe(
+ Schema.withDecodingDefault(Effect.succeed(false)),
+ ),
dismissedProviderUpdateNotificationKeys: Schema.Array(TrimmedNonEmptyString).pipe(
Schema.withDecodingDefault(Effect.succeed([])),
),
@@ -902,6 +905,7 @@ export const ClientSettingsPatch = Schema.Struct({
confirmQuit: Schema.optionalKey(Schema.Boolean),
confirmThreadArchive: Schema.optionalKey(Schema.Boolean),
confirmThreadDelete: Schema.optionalKey(Schema.Boolean),
+ compactLegacySidebarStatuses: Schema.optionalKey(Schema.Boolean),
diffIgnoreWhitespace: Schema.optionalKey(Schema.Boolean),
environmentIdentificationMode: Schema.optionalKey(EnvironmentIdentificationMode),
environmentIconColors: Schema.optionalKey(Schema.Record(EnvironmentId, EnvironmentIconColor)),