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
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,10 @@ describe("config — surface visibility", () => {
const store = new MemoryConfigStore();
store.seed(
getConfigPath("/agent"),
JSON.stringify({ zones: DEFAULT_ZONES, extensionSegments: { hidden: [] } }),
JSON.stringify({
zones: DEFAULT_ZONES,
extensionSegments: { hidden: [] },
}),
);

expect(loadConfig({ agentDir: "/agent", store })).toMatchObject({
Expand All @@ -74,57 +77,63 @@ describe("config — surface visibility", () => {
});
});

it.each([true, null, 0, "false", {}, []])(
"treats non-false statusbar/sidebar values as enabled: %j",
(value) => {
it.each(["statusbarEnabled", "sidebarEnabled"] as const)(
"treats non-false %s values as enabled",
(field) => {
for (const value of [true, null, 0, "false", {}, []]) {
const store = new MemoryConfigStore();
store.seed(
getConfigPath("/agent"),
JSON.stringify({
zones: DEFAULT_ZONES,
extensionSegments: { hidden: [] },
[field]: value,
}),
);

expect(loadConfig({ agentDir: "/agent", store })[field]).toBe(true);
}
},
);

it.each([
{ statusbarEnabled: false, sidebarEnabled: true },
{ statusbarEnabled: true, sidebarEnabled: false },
{ statusbarEnabled: false, sidebarEnabled: false },
] as const)(
"disables only the surface whose stored value is literal false: %j",
(surfaceValues) => {
const store = new MemoryConfigStore();
store.seed(
getConfigPath("/agent"),
JSON.stringify({
zones: DEFAULT_ZONES,
extensionSegments: { hidden: [] },
statusbarEnabled: value,
sidebarEnabled: value,
...surfaceValues,
}),
);

expect(loadConfig({ agentDir: "/agent", store })).toMatchObject({
statusbarEnabled: true,
sidebarEnabled: true,
});
expect(loadConfig({ agentDir: "/agent", store })).toMatchObject(
surfaceValues,
);
},
);

it("disables each surface only for the literal false value", () => {
const store = new MemoryConfigStore();
store.seed(
getConfigPath("/agent"),
JSON.stringify({
zones: DEFAULT_ZONES,
extensionSegments: { hidden: [] },
statusbarEnabled: false,
sidebarEnabled: false,
}),
);

expect(loadConfig({ agentDir: "/agent", store })).toMatchObject({
statusbarEnabled: false,
sidebarEnabled: false,
});
});

it("round-trips both surface fields and publishes them in the schema", () => {
const store = new MemoryConfigStore();
const disabled = {
...config,
statusbarEnabled: false,
sidebarEnabled: false,
sidebarEnabled: true,
} as PiStatusConfig;

saveConfig(disabled, { agentDir: "/agent", store });

const written = JSON.parse(store.read(getConfigPath("/agent")) as string);
expect(written).toMatchObject({ statusbarEnabled: false, sidebarEnabled: false });
expect(written).toMatchObject({
statusbarEnabled: false,
sidebarEnabled: true,
});
expect(loadConfig({ agentDir: "/agent", store })).toMatchObject(disabled);
expect(Object.keys(written).sort()).toContain("sidebarEnabled");
expect(Object.keys(written).sort()).toContain("statusbarEnabled");
Expand Down Expand Up @@ -194,7 +203,9 @@ function cloneDefaultConfig(): PiStatusConfig {
extensionSegments: { hidden: [...DEFAULT_CONFIG.extensionSegments.hidden] },
extensionStatusZone: DEFAULT_CONFIG.extensionStatusZone,
completionNotifications: DEFAULT_CONFIG.completionNotifications,
sidebarPanelLayout: cloneSidebarPanelLayout(DEFAULT_CONFIG.sidebarPanelLayout),
sidebarPanelLayout: cloneSidebarPanelLayout(
DEFAULT_CONFIG.sidebarPanelLayout,
),
sidebarHiddenSegments: [...DEFAULT_CONFIG.sidebarHiddenSegments],
};
}
Expand Down Expand Up @@ -239,7 +250,9 @@ const next: PiStatusConfig = {
extensionStatusZone: config.extensionStatusZone,
completionNotifications: config.completionNotifications,
sidebarPanelLayout: sidebar.sidebarPanelLayout,
sidebarHiddenSegments: sidebar.sidebarHiddenSegments.filter(isPersistedSidebarSegmentId),
sidebarHiddenSegments: sidebar.sidebarHiddenSegments.filter(
isPersistedSidebarSegmentId,
),
};
```

Expand Down Expand Up @@ -270,8 +283,8 @@ Expected: both commands exit successfully; no typed fixture reports a missing su
## Phase acceptance checklist

- [ ] Missing fields load as `true`/`true`.
- [ ] Literal `false` loads as disabled for each field.
- [ ] Invalid non-false values load as enabled.
- [ ] Literal `false` independently disables either field without affecting the other.
- [ ] Invalid non-false values load as enabled for either field.
- [ ] Save/reload preserves both fields.
- [ ] Serialized schema contains both keys.
- [ ] Full TypeScript typecheck passes.
8 changes: 8 additions & 0 deletions src/core/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ export {
};

export const DEFAULT_CONFIG: PiStatusConfig = {
statusbarEnabled: true,
sidebarEnabled: true,
zones: cloneZones(DEFAULT_ZONES),
extensionSegments: { hidden: [] },
extensionStatusZone: "bottomRight",
Expand All @@ -49,6 +51,8 @@ export const DEFAULT_CONFIG: PiStatusConfig = {

function cloneDefaultConfig(): PiStatusConfig {
return {
statusbarEnabled: DEFAULT_CONFIG.statusbarEnabled,
sidebarEnabled: DEFAULT_CONFIG.sidebarEnabled,
zones: cloneZones(DEFAULT_CONFIG.zones),
extensionSegments: { hidden: [...DEFAULT_CONFIG.extensionSegments.hidden] },
extensionStatusZone: DEFAULT_CONFIG.extensionStatusZone,
Expand Down Expand Up @@ -249,6 +253,8 @@ function normalizeLegacyHiddenSegments(input: unknown): string[] {
function normalizeConfig(input: Record<string, unknown>): PiStatusConfig {
const layout = normalizeSidebarLayout(input);
return {
statusbarEnabled: input.statusbarEnabled !== false,
sidebarEnabled: input.sidebarEnabled !== false,
zones: Object.hasOwn(input, "zones")
? normalizeZones(input.zones)
: Object.hasOwn(input, "segments") && Array.isArray(input.segments)
Expand Down Expand Up @@ -340,6 +346,8 @@ export function saveConfig(
sidebarHiddenSegments: config.sidebarHiddenSegments,
});
const next: PiStatusConfig = {
statusbarEnabled: config.statusbarEnabled,
sidebarEnabled: config.sidebarEnabled,
zones: cloneZones(config.zones),
extensionSegments: { hidden: [...config.extensionSegments.hidden] },
extensionStatusZone: config.extensionStatusZone,
Expand Down
2 changes: 2 additions & 0 deletions src/shared/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,8 @@ export interface StatusLineZones {
}

export type PiStatusConfig = {
statusbarEnabled: boolean;
sidebarEnabled: boolean;
zones: StatusLineZones;
extensionSegments: ExtensionSegments;
extensionStatusZone: StatusLineZone;
Expand Down
86 changes: 86 additions & 0 deletions tests/core/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ const expectedDefaultLayout = (): SidebarPanelLayout =>
const expectedDefault = expectedDefaultLayout;

const config: PiStatusConfig = {
statusbarEnabled: true,
sidebarEnabled: true,
zones: { topLeft: ["git-branch"], topRight: [], bottomLeft: [], bottomRight: [] },
extensionSegments: { hidden: ["alpha"] },
extensionStatusZone: "bottomRight",
Expand Down Expand Up @@ -500,6 +502,8 @@ describe("config — direct extension file", () => {
);

expect(loadConfig({ agentDir, store })).toEqual({
statusbarEnabled: true,
sidebarEnabled: true,
zones: DEFAULT_ZONES,
extensionSegments: { hidden: [] },
extensionStatusZone: "bottomRight",
Expand Down Expand Up @@ -534,6 +538,8 @@ describe("config — direct extension file", () => {
store.seed(getConfigPath("/agent"), content);

expect(loadConfig({ agentDir: "/agent", store })).toEqual({
statusbarEnabled: true,
sidebarEnabled: true,
zones: DEFAULT_ZONES,
extensionSegments: { hidden: [] },
extensionStatusZone: "bottomRight",
Expand Down Expand Up @@ -674,6 +680,8 @@ describe("config — filesystem", () => {
const path = join(agentDir, "extensions", "statusline.json");

expect(loadConfig()).toEqual({
statusbarEnabled: true,
sidebarEnabled: true,
zones: DEFAULT_ZONES,
extensionSegments: { hidden: [] },
extensionStatusZone: "bottomRight",
Expand Down Expand Up @@ -783,8 +791,10 @@ describe("config — completion notifications", () => {
"completionNotifications",
"extensionSegments",
"extensionStatusZone",
"sidebarEnabled",
"sidebarHiddenSegments",
"sidebarPanelLayout",
"statusbarEnabled",
"zones",
].sort(),
);
Expand All @@ -807,3 +817,79 @@ describe("config — completion notifications", () => {
expect(loadConfig({ agentDir: "/agent", store }).completionNotifications).toBe(false);
});
});

describe("config — surface visibility", () => {
it("defaults both surfaces to enabled when the fields are absent", () => {
const store = new MemoryConfigStore();
store.seed(
getConfigPath("/agent"),
JSON.stringify({
zones: DEFAULT_ZONES,
extensionSegments: { hidden: [] },
}),
);

expect(loadConfig({ agentDir: "/agent", store })).toMatchObject({
statusbarEnabled: true,
sidebarEnabled: true,
});
});

it.each(["statusbarEnabled", "sidebarEnabled"] as const)(
"treats non-false %s values as enabled",
(field) => {
for (const value of [true, null, 0, "false", {}, []]) {
const store = new MemoryConfigStore();
store.seed(
getConfigPath("/agent"),
JSON.stringify({
zones: DEFAULT_ZONES,
extensionSegments: { hidden: [] },
[field]: value,
}),
);

expect(loadConfig({ agentDir: "/agent", store })[field]).toBe(true);
}
},
);

it.each([
{ statusbarEnabled: false, sidebarEnabled: true },
{ statusbarEnabled: true, sidebarEnabled: false },
{ statusbarEnabled: false, sidebarEnabled: false },
] as const)(
"disables only the surface whose stored value is literal false: %j",
(surfaceValues) => {
const store = new MemoryConfigStore();
store.seed(
getConfigPath("/agent"),
JSON.stringify({
zones: DEFAULT_ZONES,
extensionSegments: { hidden: [] },
...surfaceValues,
}),
);

expect(loadConfig({ agentDir: "/agent", store })).toMatchObject(surfaceValues);
},
);

it("round-trips both surface fields and publishes them in the schema", () => {
const store = new MemoryConfigStore();
const disabled = {
...config,
statusbarEnabled: false,
sidebarEnabled: true,
};

saveConfig(disabled, { agentDir: "/agent", store });

const written = JSON.parse(store.read(getConfigPath("/agent")) as string);
expect(written).toMatchObject({
statusbarEnabled: false,
sidebarEnabled: true,
});
expect(loadConfig({ agentDir: "/agent", store })).toMatchObject(disabled);
});
});
2 changes: 2 additions & 0 deletions tests/core/resolve-footer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ const identityTheme: ThemeLike = { fg: (_c, t) => t, rainbow: (t) => t };

function footerConfig(overrides: Partial<PiStatusConfig> = {}): PiStatusConfig {
return {
statusbarEnabled: true,
sidebarEnabled: true,
zones: structuredClone(DEFAULT_ZONES),
extensionSegments: { hidden: [] },
extensionStatusZone: "bottomRight",
Expand Down
4 changes: 4 additions & 0 deletions tests/core/runtime-state.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {
} from "../../src/shared/types.ts";

const defaultConfig: PiStatusConfig = {
statusbarEnabled: true,
sidebarEnabled: true,
zones: {
topLeft: ["model-with-reasoning"],
topRight: [],
Expand Down Expand Up @@ -83,6 +85,8 @@ describe("RuntimeStateMachine", () => {
it("updates config on config_reload", () => {
const sm = createRuntimeStateMachine(defaultConfig, "medium");
const newConfig: PiStatusConfig = {
statusbarEnabled: true,
sidebarEnabled: true,
zones: { topLeft: ["git-branch"], topRight: [], bottomLeft: [], bottomRight: [] },
extensionSegments: { hidden: ["x"] },
extensionStatusZone: "bottomRight",
Expand Down
2 changes: 2 additions & 0 deletions tests/core/sidebar-layout.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import {

function config(overrides: Partial<PiStatusConfig> = {}): PiStatusConfig {
return {
statusbarEnabled: true,
sidebarEnabled: true,
zones: structuredClone(DEFAULT_ZONES),
extensionSegments: { hidden: [] },
extensionStatusZone: "bottomRight",
Expand Down
2 changes: 2 additions & 0 deletions tests/index-save.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ afterEach(() => {

function config(): PiStatusConfig {
return {
statusbarEnabled: true,
sidebarEnabled: true,
zones: {
topLeft: ["model-with-reasoning"],
topRight: [],
Expand Down
4 changes: 4 additions & 0 deletions tests/index-sidebar-layout.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ function result(details: unknown, overrides: Record<string, unknown> = {}) {

function configWithModelIn(panelId: SidebarPanelId): PiStatusConfig {
return {
statusbarEnabled: true,
sidebarEnabled: true,
zones: structuredClone(DEFAULT_ZONES),
extensionSegments: { hidden: [] },
extensionStatusZone: "bottomRight",
Expand All @@ -46,6 +48,8 @@ function configWithModelIn(panelId: SidebarPanelId): PiStatusConfig {

function configWithPanelsVisible(panels: SidebarPanelId[]): PiStatusConfig {
return {
statusbarEnabled: true,
sidebarEnabled: true,
zones: structuredClone(DEFAULT_ZONES),
extensionSegments: { hidden: [] },
extensionStatusZone: "bottomRight",
Expand Down
2 changes: 2 additions & 0 deletions tests/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,8 @@ describe("extension wiring", () => {
expect(
JSON.parse(readFileSync(join(agentDir, "extensions", "statusline.json"), "utf8")),
).toEqual({
statusbarEnabled: true,
sidebarEnabled: true,
zones: {
topLeft: ["model-with-reasoning"],
topRight: [],
Expand Down
2 changes: 2 additions & 0 deletions tests/tui/dashboard-render.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ function zones(overrides: Partial<StatusLineZones> = {}): StatusLineZones {

function config(overrides: Partial<PiStatusConfig> = {}): PiStatusConfig {
return {
statusbarEnabled: true,
sidebarEnabled: true,
zones: zones(),
extensionSegments: { hidden: [] },
extensionStatusZone: "bottomRight",
Expand Down
2 changes: 2 additions & 0 deletions tests/tui/dashboard-state.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ function zones(overrides: Partial<StatusLineZones> = {}): StatusLineZones {

function config(overrides: Partial<PiStatusConfig> = {}): PiStatusConfig {
return {
statusbarEnabled: true,
sidebarEnabled: true,
zones: zones(),
extensionSegments: { hidden: [] },
extensionStatusZone: "bottomRight",
Expand Down
Loading