Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions apps/desktop/src/settings/DesktopClientSettings.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const clientSettings: ClientSettings = {
browserDefaultAppearance: "dark",
browserAutoShowFloatingPreview: false,
compactLegacySidebarStatuses: false,
showThreadWorktreeIndicators: true,
confirmQuit: true,
confirmThreadArchive: true,
confirmThreadDelete: false,
Expand Down
3 changes: 3 additions & 0 deletions apps/desktop/src/settings/LastCodeSettingsImport.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ describe("LastCodeSettingsImport", () => {
const lastCodeCustom = ProviderInstanceId.make("lastcode_custom");
const sourceClient = { ...DEFAULT_CLIENT_SETTINGS, fontSizeInterface: 17 };
Reflect.deleteProperty(sourceClient, "compactLegacySidebarStatuses");
Reflect.deleteProperty(sourceClient, "showThreadWorktreeIndicators");
Reflect.deleteProperty(sourceClient, "legacySidebarScale");
Reflect.deleteProperty(sourceClient, "roundedProjectIcons");
sourceClient.favorites = [
Expand All @@ -105,6 +106,7 @@ describe("LastCodeSettingsImport", () => {
const destinationClient = {
...DEFAULT_CLIENT_SETTINGS,
compactLegacySidebarStatuses: true,
showThreadWorktreeIndicators: false,
legacySidebarScale: 75,
roundedProjectIcons: true,
environmentIconColors: { primary: "#2563eb", remote: "#7c3aed" },
Expand Down Expand Up @@ -218,6 +220,7 @@ describe("LastCodeSettingsImport", () => {
);
assert.equal(importedClient.fontSizeInterface, 17);
assert.equal(importedClient.compactLegacySidebarStatuses, true);
assert.equal(importedClient.showThreadWorktreeIndicators, false);
assert.equal(importedClient.legacySidebarScale, 75);
assert.equal(importedClient.roundedProjectIcons, true);
assert.deepEqual(importedClient.environmentIconColors, {
Expand Down
1 change: 1 addition & 0 deletions apps/desktop/src/settings/LastCodeSettingsImport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ function mergeClientSettings(sourceRaw: string, destinationRaw: string | null):
legacySidebarScale: destination.legacySidebarScale,
roundedProjectIcons: destination.roundedProjectIcons,
showLocalEnvironmentIcon: destination.showLocalEnvironmentIcon,
showThreadWorktreeIndicators: destination.showThreadWorktreeIndicators,
})}\n`;
}

Expand Down
17 changes: 16 additions & 1 deletion apps/web/src/components/LegacySidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,7 @@ function buildThreadJumpLabelMap(input: {
interface SidebarThreadRowProps {
thread: SidebarThreadSummary;
compactStatusIndicators: boolean;
showWorktreeIndicators: boolean;
showLocalEnvironmentIcon: boolean;
configuredEnvironmentIconColor: EnvironmentIconColor | undefined;
projectCwd: string | null;
Expand Down Expand Up @@ -934,7 +935,7 @@ export const SidebarThreadRow = memo(function SidebarThreadRow(props: SidebarThr
</TooltipPopup>
</Tooltip>
)}
<ThreadWorktreeIndicator thread={thread} />
{props.showWorktreeIndicators ? <ThreadWorktreeIndicator thread={thread} /> : null}
{terminalStatus && (
<Tooltip>
<TooltipTrigger
Expand Down Expand Up @@ -1141,6 +1142,7 @@ export const SidebarThreadRow = memo(function SidebarThreadRow(props: SidebarThr
interface SidebarProjectThreadListProps {
legacySidebarScale: LegacySidebarScale;
compactStatusIndicators: boolean;
showWorktreeIndicators: boolean;
scaleStyle: CSSProperties;
providerEntriesByEnvironmentId: ReadonlyMap<string, ReadonlyMap<string, ProviderInstanceEntry>>;
environmentIconColors: Readonly<Record<string, EnvironmentIconColor>>;
Expand Down Expand Up @@ -1206,6 +1208,7 @@ const SidebarProjectThreadList = memo(function SidebarProjectThreadList(
const {
legacySidebarScale,
compactStatusIndicators,
showWorktreeIndicators,
scaleStyle,
providerEntriesByEnvironmentId,
environmentIconColors,
Expand Down Expand Up @@ -1277,6 +1280,7 @@ const SidebarProjectThreadList = memo(function SidebarProjectThreadList(
key={threadKey}
thread={thread}
compactStatusIndicators={compactStatusIndicators}
showWorktreeIndicators={showWorktreeIndicators}
showLocalEnvironmentIcon={showLocalEnvironmentIcon}
configuredEnvironmentIconColor={environmentIconColors[thread.environmentId]}
projectCwd={projectCwd}
Expand Down Expand Up @@ -1351,6 +1355,7 @@ const SidebarProjectThreadList = memo(function SidebarProjectThreadList(
interface SidebarProjectItemProps {
legacySidebarScale: LegacySidebarScale;
compactStatusIndicators: boolean;
showWorktreeIndicators: boolean;
scaleStyle: CSSProperties;
providerEntriesByEnvironmentId: ReadonlyMap<string, ReadonlyMap<string, ProviderInstanceEntry>>;
desktopLocalEnvironmentIds: ReadonlySet<EnvironmentId>;
Expand Down Expand Up @@ -1380,6 +1385,7 @@ const SidebarProjectItem = memo(function SidebarProjectItem(props: SidebarProjec
const {
legacySidebarScale,
compactStatusIndicators,
showWorktreeIndicators,
scaleStyle,
providerEntriesByEnvironmentId,
project,
Expand Down Expand Up @@ -2764,6 +2770,7 @@ const SidebarProjectItem = memo(function SidebarProjectItem(props: SidebarProjec
<SidebarProjectThreadList
legacySidebarScale={legacySidebarScale}
compactStatusIndicators={compactStatusIndicators}
showWorktreeIndicators={showWorktreeIndicators}
scaleStyle={scaleStyle}
providerEntriesByEnvironmentId={providerEntriesByEnvironmentId}
environmentIconColors={props.environmentIconColors}
Expand Down Expand Up @@ -3223,6 +3230,7 @@ interface SidebarProjectsContentProps {
projectsLength: number;
legacySidebarScale: LegacySidebarScale;
compactStatusIndicators: boolean;
showWorktreeIndicators: boolean;
projectTreeScaleStyle: CSSProperties;
providerEntriesByEnvironmentId: ReadonlyMap<string, ReadonlyMap<string, ProviderInstanceEntry>>;
desktopLocalEnvironmentIds: ReadonlySet<EnvironmentId>;
Expand Down Expand Up @@ -3339,6 +3347,7 @@ const SidebarProjectsContent = memo(function SidebarProjectsContent(
projectsLength,
legacySidebarScale,
compactStatusIndicators,
showWorktreeIndicators,
projectTreeScaleStyle,
providerEntriesByEnvironmentId,
desktopLocalEnvironmentIds,
Expand Down Expand Up @@ -3473,6 +3482,7 @@ const SidebarProjectsContent = memo(function SidebarProjectsContent(
<SidebarProjectItem
legacySidebarScale={legacySidebarScale}
compactStatusIndicators={compactStatusIndicators}
showWorktreeIndicators={showWorktreeIndicators}
scaleStyle={projectTreeScaleStyle}
providerEntriesByEnvironmentId={providerEntriesByEnvironmentId}
desktopLocalEnvironmentIds={desktopLocalEnvironmentIds}
Expand Down Expand Up @@ -3514,6 +3524,7 @@ const SidebarProjectsContent = memo(function SidebarProjectsContent(
key={project.projectKey}
legacySidebarScale={legacySidebarScale}
compactStatusIndicators={compactStatusIndicators}
showWorktreeIndicators={showWorktreeIndicators}
scaleStyle={projectTreeScaleStyle}
providerEntriesByEnvironmentId={providerEntriesByEnvironmentId}
desktopLocalEnvironmentIds={desktopLocalEnvironmentIds}
Expand Down Expand Up @@ -3573,6 +3584,9 @@ export default function LegacySidebar() {
const compactStatusIndicators = useClientSettings(
(settings) => settings.compactLegacySidebarStatuses,
);
const showWorktreeIndicators = useClientSettings(
(settings) => settings.showThreadWorktreeIndicators,
);
const environmentIconColors = useClientSettings((s) => s.environmentIconColors);
const showLocalEnvironmentIcon = useClientSettings((s) => s.showLocalEnvironmentIcon);
const serverProviders = useAtomValue(primaryServerProvidersAtom);
Expand Down Expand Up @@ -4264,6 +4278,7 @@ export default function LegacySidebar() {
projectsLength={projects.length}
legacySidebarScale={legacySidebarScale}
compactStatusIndicators={compactStatusIndicators}
showWorktreeIndicators={showWorktreeIndicators}
projectTreeScaleStyle={scaleStyle}
providerEntriesByEnvironmentId={providerEntriesByEnvironmentId}
desktopLocalEnvironmentIds={desktopLocalEnvironmentIds}
Expand Down
7 changes: 6 additions & 1 deletion apps/web/src/components/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: {
environmentLabel: string | null;
environmentKnown: boolean;
showLocalEnvironmentIcon: boolean;
showWorktreeIndicators: boolean;
configuredEnvironmentIconColor: EnvironmentIconColor | undefined;
projectCwd: string | null;
projectFaviconPath: string | null;
Expand Down Expand Up @@ -1606,7 +1607,9 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: {
branch, so the row lost its most stable identifier. */}
{thread.branch ? (
<>
<ThreadWorktreeIndicator thread={thread} />
{props.showWorktreeIndicators ? (
<ThreadWorktreeIndicator thread={thread} />
) : null}
<span className="min-w-0 flex-1 truncate whitespace-nowrap">{thread.branch}</span>
</>
) : (
Expand Down Expand Up @@ -1808,6 +1811,7 @@ export default function Sidebar() {
const projectGroupingSettings = useClientSettings(selectProjectGroupingSettings);
const environmentIconColors = useClientSettings((s) => s.environmentIconColors);
const showLocalEnvironmentIcon = useClientSettings((s) => s.showLocalEnvironmentIcon);
const showWorktreeIndicators = useClientSettings((s) => s.showThreadWorktreeIndicators);
const {
settleThread,
unsettleThread,
Expand Down Expand Up @@ -3859,6 +3863,7 @@ export default function Sidebar() {
environmentLabel={environmentLabelById.get(thread.environmentId) ?? null}
environmentKnown={environmentLabelById.has(thread.environmentId)}
showLocalEnvironmentIcon={showLocalEnvironmentIcon}
showWorktreeIndicators={showWorktreeIndicators}
configuredEnvironmentIconColor={environmentIconColors[thread.environmentId]}
projectCwd={
projectCwdByKey.get(`${thread.environmentId}:${thread.projectId}`) ?? null
Expand Down
13 changes: 13 additions & 0 deletions apps/web/src/components/settings/LastCodeSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,19 @@ export function LastCodeSettingsPanel() {
/>
}
/>
<SettingsRow
{...searchableSetting("show-worktree-indicators")}
description="Show the worktree icon beside threads that use a dedicated worktree."
control={
<Switch
checked={clientSettings.showThreadWorktreeIndicators}
onCheckedChange={(checked) =>
updateClientSettings({ showThreadWorktreeIndicators: Boolean(checked) })
}
aria-label="Show worktree indicators"
/>
}
/>
</SettingsSection>
<SettingsSection
id="environment-icons"
Expand Down
3 changes: 3 additions & 0 deletions apps/web/src/components/settings/settingsSearch.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ describe("searchSettings", () => {
expect(searchSettings("compact status").map((item) => item.id)).toEqual([
"compact-status-indicators",
]);
expect(searchSettings("worktree indicators").map((item) => item.id)).toEqual([
"show-worktree-indicators",
]);
expect(searchSettings("xyzzy")).toEqual([]);
});

Expand Down
5 changes: 5 additions & 0 deletions apps/web/src/components/settings/settingsSearch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,11 @@ export const SETTINGS_SEARCH_ITEMS = [
title: "Compact status indicators",
to: "/settings/lastcode",
},
{
id: "show-worktree-indicators",
title: "Show worktree indicators",
to: "/settings/lastcode",
},
{
id: "rounded-project-icons",
title: "Rounded project icons",
Expand Down
3 changes: 3 additions & 0 deletions docs/user/thread-sidebar.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ indicators** in **Settings -> LastCode -> Appearance**. Legacy thread rows then
colored status dot; the full status remains available as a tooltip. This preference is off by
default.

To hide the worktree icon beside threads that use a dedicated worktree, turn off **Show worktree
indicators** in **Settings -> LastCode -> Appearance**. The icon remains visible by default.

## Environment icons in LastCode

Open **Settings -> LastCode -> Environments** to choose the icon color for the primary machine and
Expand Down
11 changes: 11 additions & 0 deletions packages/contracts/src/settings.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ 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.showThreadWorktreeIndicators).toBe(true);
expect(settings.legacySidebarEnabled).toBe(false);
expect(settings.legacySidebarScale).toBe(100);
expect(settings.sidebarAutoSettleAfterDays).toBe(3);
Expand Down Expand Up @@ -149,6 +150,16 @@ describe("ClientSettings sidebar", () => {
).toBe(true);
});

it("preserves an explicit worktree indicator preference", () => {
expect(
decodeClientSettings({ showThreadWorktreeIndicators: false }).showThreadWorktreeIndicators,
).toBe(false);
expect(
decodeClientSettingsPatch({ showThreadWorktreeIndicators: false })
.showThreadWorktreeIndicators,
).toBe(false);
});

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);
Expand Down
4 changes: 4 additions & 0 deletions packages/contracts/src/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,9 @@ export const ClientSettingsSchema = Schema.Struct({
compactLegacySidebarStatuses: Schema.Boolean.pipe(
Schema.withDecodingDefault(Effect.succeed(false)),
),
showThreadWorktreeIndicators: Schema.Boolean.pipe(
Schema.withDecodingDefault(Effect.succeed(true)),
),
dismissedProviderUpdateNotificationKeys: Schema.Array(TrimmedNonEmptyString).pipe(
Schema.withDecodingDefault(Effect.succeed([])),
),
Expand Down Expand Up @@ -906,6 +909,7 @@ export const ClientSettingsPatch = Schema.Struct({
confirmThreadArchive: Schema.optionalKey(Schema.Boolean),
confirmThreadDelete: Schema.optionalKey(Schema.Boolean),
compactLegacySidebarStatuses: Schema.optionalKey(Schema.Boolean),
showThreadWorktreeIndicators: Schema.optionalKey(Schema.Boolean),
diffIgnoreWhitespace: Schema.optionalKey(Schema.Boolean),
environmentIdentificationMode: Schema.optionalKey(EnvironmentIdentificationMode),
environmentIconColors: Schema.optionalKey(Schema.Record(EnvironmentId, EnvironmentIconColor)),
Expand Down