From c333fcec32b1f90bf0da6bb14d2609c20e38a74f Mon Sep 17 00:00:00 2001 From: Adam Malczewski Date: Fri, 26 Jun 2026 22:21:55 +0900 Subject: style: switch from tabs to 2-space indentation (incl. svelte) --- src/features/settings/index.ts | 4 +- src/features/settings/logic/view-model.test.ts | 104 ++++++------- src/features/settings/logic/view-model.ts | 32 ++-- src/features/settings/ui/ChatLimitField.svelte | 186 ++++++++++++------------ src/features/settings/ui/ChatLimitField.test.ts | 128 ++++++++-------- 5 files changed, 227 insertions(+), 227 deletions(-) (limited to 'src/features/settings') diff --git a/src/features/settings/index.ts b/src/features/settings/index.ts index 69e7cd0..3942e97 100644 --- a/src/features/settings/index.ts +++ b/src/features/settings/index.ts @@ -4,6 +4,6 @@ export { default as ChatLimitField } from "./ui/ChatLimitField.svelte"; /** Public module manifest — aggregated by the shell's "Loaded Modules" view. */ export const manifest = { - name: "settings", - description: "FE-local settings (chat limit)", + name: "settings", + description: "FE-local settings (chat limit)", } as const; diff --git a/src/features/settings/logic/view-model.test.ts b/src/features/settings/logic/view-model.test.ts index 65fcae9..93c4786 100644 --- a/src/features/settings/logic/view-model.test.ts +++ b/src/features/settings/logic/view-model.test.ts @@ -3,59 +3,59 @@ import { MAX_CHAT_LIMIT, MIN_CHAT_LIMIT } from "../../../core/chunks"; import { chatLimitChanged, parseChatLimit } from "./view-model"; describe("parseChatLimit", () => { - it("parses a plain integer", () => { - expect(parseChatLimit("256")).toEqual({ ok: true, value: 256 }); - expect(parseChatLimit("100")).toEqual({ ok: true, value: 100 }); - }); - - it("trims surrounding whitespace", () => { - expect(parseChatLimit(" 50 ")).toEqual({ ok: true, value: 50 }); - }); - - it("floors a decimal", () => { - expect(parseChatLimit("100.9")).toEqual({ ok: true, value: 100 }); - }); - - it("clamps below the floor to MIN_CHAT_LIMIT", () => { - expect(parseChatLimit("0")).toEqual({ ok: true, value: MIN_CHAT_LIMIT }); - expect(parseChatLimit("-5")).toEqual({ ok: true, value: MIN_CHAT_LIMIT }); - expect(parseChatLimit("5")).toEqual({ ok: true, value: MIN_CHAT_LIMIT }); - }); - - it("clamps above the ceiling to MAX_CHAT_LIMIT", () => { - expect(parseChatLimit("99999999")).toEqual({ ok: true, value: MAX_CHAT_LIMIT }); - }); - - it("rejects an empty string", () => { - expect(parseChatLimit("")).toEqual({ ok: false, error: expect.any(String) }); - expect(parseChatLimit(" ")).toEqual({ ok: false, error: expect.any(String) }); - }); - - it("rejects non-numeric input", () => { - expect(parseChatLimit("abc")).toEqual({ ok: false, error: expect.any(String) }); - expect(parseChatLimit("twelve")).toEqual({ ok: false, error: expect.any(String) }); - }); + it("parses a plain integer", () => { + expect(parseChatLimit("256")).toEqual({ ok: true, value: 256 }); + expect(parseChatLimit("100")).toEqual({ ok: true, value: 100 }); + }); + + it("trims surrounding whitespace", () => { + expect(parseChatLimit(" 50 ")).toEqual({ ok: true, value: 50 }); + }); + + it("floors a decimal", () => { + expect(parseChatLimit("100.9")).toEqual({ ok: true, value: 100 }); + }); + + it("clamps below the floor to MIN_CHAT_LIMIT", () => { + expect(parseChatLimit("0")).toEqual({ ok: true, value: MIN_CHAT_LIMIT }); + expect(parseChatLimit("-5")).toEqual({ ok: true, value: MIN_CHAT_LIMIT }); + expect(parseChatLimit("5")).toEqual({ ok: true, value: MIN_CHAT_LIMIT }); + }); + + it("clamps above the ceiling to MAX_CHAT_LIMIT", () => { + expect(parseChatLimit("99999999")).toEqual({ ok: true, value: MAX_CHAT_LIMIT }); + }); + + it("rejects an empty string", () => { + expect(parseChatLimit("")).toEqual({ ok: false, error: expect.any(String) }); + expect(parseChatLimit(" ")).toEqual({ ok: false, error: expect.any(String) }); + }); + + it("rejects non-numeric input", () => { + expect(parseChatLimit("abc")).toEqual({ ok: false, error: expect.any(String) }); + expect(parseChatLimit("twelve")).toEqual({ ok: false, error: expect.any(String) }); + }); }); describe("chatLimitChanged", () => { - it("is false when the typed value normalizes to the current limit", () => { - expect(chatLimitChanged("256", 256)).toBe(false); - }); - - it("is true when the typed value differs", () => { - expect(chatLimitChanged("100", 256)).toBe(true); - expect(chatLimitChanged("256", 100)).toBe(true); - }); - - it("is false for empty / invalid input (nothing submittable)", () => { - expect(chatLimitChanged("", 256)).toBe(false); - expect(chatLimitChanged("abc", 256)).toBe(false); - }); - - it("is false when the typed value clamps to the current limit", () => { - // 5 clamps to MIN (10); current is already 10 → no change. - expect(chatLimitChanged("5", MIN_CHAT_LIMIT)).toBe(false); - // A huge value clamps to MAX; current is already MAX → no change. - expect(chatLimitChanged("99999999", MAX_CHAT_LIMIT)).toBe(false); - }); + it("is false when the typed value normalizes to the current limit", () => { + expect(chatLimitChanged("256", 256)).toBe(false); + }); + + it("is true when the typed value differs", () => { + expect(chatLimitChanged("100", 256)).toBe(true); + expect(chatLimitChanged("256", 100)).toBe(true); + }); + + it("is false for empty / invalid input (nothing submittable)", () => { + expect(chatLimitChanged("", 256)).toBe(false); + expect(chatLimitChanged("abc", 256)).toBe(false); + }); + + it("is false when the typed value clamps to the current limit", () => { + // 5 clamps to MIN (10); current is already 10 → no change. + expect(chatLimitChanged("5", MIN_CHAT_LIMIT)).toBe(false); + // A huge value clamps to MAX; current is already MAX → no change. + expect(chatLimitChanged("99999999", MAX_CHAT_LIMIT)).toBe(false); + }); }); diff --git a/src/features/settings/logic/view-model.ts b/src/features/settings/logic/view-model.ts index caf76ee..73f6d17 100644 --- a/src/features/settings/logic/view-model.ts +++ b/src/features/settings/logic/view-model.ts @@ -17,8 +17,8 @@ import { normalizeChatLimit } from "../../../core/chunks"; /** Outcome of persisting a chat-limit setting. */ export type ChatLimitSaveResult = - | { readonly ok: true; readonly chatLimit: number } - | { readonly ok: false; readonly error: string }; + | { readonly ok: true; readonly chatLimit: number } + | { readonly ok: false; readonly error: string }; export type SaveChatLimit = (value: number) => Promise; @@ -26,8 +26,8 @@ export type SaveChatLimit = (value: number) => Promise; /** Result of parsing a typed chat-limit string. */ export type ChatLimitParse = - | { readonly ok: true; readonly value: number } - | { readonly ok: false; readonly error: string }; + | { readonly ok: true; readonly value: number } + | { readonly ok: false; readonly error: string }; /** * Parse a typed chat-limit string into a normalized limit (floored + clamped to @@ -37,15 +37,15 @@ export type ChatLimitParse = * from user typing. */ export function parseChatLimit(raw: string): ChatLimitParse { - const trimmed = raw.trim(); - if (trimmed.length === 0) { - return { ok: false, error: "Enter a number." }; - } - const n = Number(trimmed); - if (!Number.isFinite(n)) { - return { ok: false, error: "Must be a number." }; - } - return { ok: true, value: normalizeChatLimit(n) }; + const trimmed = raw.trim(); + if (trimmed.length === 0) { + return { ok: false, error: "Enter a number." }; + } + const n = Number(trimmed); + if (!Number.isFinite(n)) { + return { ok: false, error: "Must be a number." }; + } + return { ok: true, value: normalizeChatLimit(n) }; } /** @@ -54,7 +54,7 @@ export function parseChatLimit(raw: string): ChatLimitParse { * dirty-check for the input — it must NOT mutate or clamp, only compare. */ export function chatLimitChanged(typed: string, current: number): boolean { - const parsed = parseChatLimit(typed); - if (!parsed.ok) return false; - return parsed.value !== current; + const parsed = parseChatLimit(typed); + if (!parsed.ok) return false; + return parsed.value !== current; } diff --git a/src/features/settings/ui/ChatLimitField.svelte b/src/features/settings/ui/ChatLimitField.svelte index 9b9911a..502212f 100644 --- a/src/features/settings/ui/ChatLimitField.svelte +++ b/src/features/settings/ui/ChatLimitField.svelte @@ -1,104 +1,104 @@
- Chat limit -
- { - if (e.key === "Enter") handleSave(); - }} - aria-label="Chat limit" - /> - -
- {#if error} -

{error}

- {:else if justSaved && !dirty} -

Saved{savedValue !== null ? `: ${savedValue}.` : "."}

- {:else} -

- Max loaded chunks per conversation ({MIN_CHAT_LIMIT}–{MAX_CHAT_LIMIT}). Lowering it unloads - older history; raise it and use "Show earlier" to page back in. -

- {/if} + Chat limit +
+ { + if (e.key === "Enter") handleSave(); + }} + aria-label="Chat limit" + /> + +
+ {#if error} +

{error}

+ {:else if justSaved && !dirty} +

Saved{savedValue !== null ? `: ${savedValue}.` : "."}

+ {:else} +

+ Max loaded chunks per conversation ({MIN_CHAT_LIMIT}–{MAX_CHAT_LIMIT}). Lowering it unloads + older history; raise it and use "Show earlier" to page back in. +

+ {/if}
diff --git a/src/features/settings/ui/ChatLimitField.test.ts b/src/features/settings/ui/ChatLimitField.test.ts index 73bc449..8f8da25 100644 --- a/src/features/settings/ui/ChatLimitField.test.ts +++ b/src/features/settings/ui/ChatLimitField.test.ts @@ -6,84 +6,84 @@ import ChatLimitField from "./ChatLimitField.svelte"; // A fake save that resolves ok with the value it was given. function fakeSave(): { - saves: number[]; - last: number | null; - impl: (value: number) => Promise; + saves: number[]; + last: number | null; + impl: (value: number) => Promise; } { - const saves: number[] = []; - return { - saves, - last: null, - impl: async (value: number) => { - saves.push(value); - return { ok: true, chatLimit: value }; - }, - }; + const saves: number[] = []; + return { + saves, + last: null, + impl: async (value: number) => { + saves.push(value); + return { ok: true, chatLimit: value }; + }, + }; } describe("ChatLimitField", () => { - it("seeds the input from the persisted limit and disables Set while unchanged", () => { - render(ChatLimitField, { props: { chatLimit: 256, save: fakeSave().impl } }); - expect(screen.getByLabelText("Chat limit")).toHaveValue("256"); - expect(screen.getByRole("button", { name: "Set" })).toBeDisabled(); - }); + it("seeds the input from the persisted limit and disables Set while unchanged", () => { + render(ChatLimitField, { props: { chatLimit: 256, save: fakeSave().impl } }); + expect(screen.getByLabelText("Chat limit")).toHaveValue("256"); + expect(screen.getByRole("button", { name: "Set" })).toBeDisabled(); + }); - it("enables Set when the typed value differs (regression: number-binding coercion)", async () => { - const user = userEvent.setup(); - const save = fakeSave(); - render(ChatLimitField, { props: { chatLimit: 256, save: save.impl } }); + it("enables Set when the typed value differs (regression: number-binding coercion)", async () => { + const user = userEvent.setup(); + const save = fakeSave(); + render(ChatLimitField, { props: { chatLimit: 256, save: save.impl } }); - const input = screen.getByLabelText("Chat limit"); - await user.clear(input); - await user.type(input, "10"); + const input = screen.getByLabelText("Chat limit"); + await user.clear(input); + await user.type(input, "10"); - expect(screen.getByRole("button", { name: "Set" })).toBeEnabled(); - }); + expect(screen.getByRole("button", { name: "Set" })).toBeEnabled(); + }); - it("keeps Set disabled for non-numeric input", async () => { - const user = userEvent.setup(); - const save = fakeSave(); - render(ChatLimitField, { props: { chatLimit: 256, save: save.impl } }); + it("keeps Set disabled for non-numeric input", async () => { + const user = userEvent.setup(); + const save = fakeSave(); + render(ChatLimitField, { props: { chatLimit: 256, save: save.impl } }); - const input = screen.getByLabelText("Chat limit"); - await user.clear(input); - await user.type(input, "abc"); + const input = screen.getByLabelText("Chat limit"); + await user.clear(input); + await user.type(input, "abc"); - expect(screen.getByRole("button", { name: "Set" })).toBeDisabled(); - }); + expect(screen.getByRole("button", { name: "Set" })).toBeDisabled(); + }); - it("clicking Set calls save with the parsed value and shows the confirmation", async () => { - const user = userEvent.setup(); - const save = fakeSave(); - const { rerender } = render(ChatLimitField, { - props: { chatLimit: 256, save: save.impl }, - }); + it("clicking Set calls save with the parsed value and shows the confirmation", async () => { + const user = userEvent.setup(); + const save = fakeSave(); + const { rerender } = render(ChatLimitField, { + props: { chatLimit: 256, save: save.impl }, + }); - const input = screen.getByLabelText("Chat limit"); - await user.clear(input); - await user.type(input, "50"); - await user.click(screen.getByRole("button", { name: "Set" })); + const input = screen.getByLabelText("Chat limit"); + await user.clear(input); + await user.type(input, "50"); + await user.click(screen.getByRole("button", { name: "Set" })); - expect(save.saves).toEqual([50]); - // In the real app the reactive `chatLimit` prop updates to the saved value - // (the store sets it synchronously), which clears `dirty` and reveals the - // "Saved" badge. Rerender simulates that propagation. - rerender({ chatLimit: 50, save: save.impl }); - expect(screen.getByText(/Saved/i)).toBeInTheDocument(); - }); + expect(save.saves).toEqual([50]); + // In the real app the reactive `chatLimit` prop updates to the saved value + // (the store sets it synchronously), which clears `dirty` and reveals the + // "Saved" badge. Rerender simulates that propagation. + rerender({ chatLimit: 50, save: save.impl }); + expect(screen.getByText(/Saved/i)).toBeInTheDocument(); + }); - it("clamps a below-floor value when saving", async () => { - const user = userEvent.setup(); - const save = fakeSave(); - render(ChatLimitField, { props: { chatLimit: 256, save: save.impl } }); + it("clamps a below-floor value when saving", async () => { + const user = userEvent.setup(); + const save = fakeSave(); + render(ChatLimitField, { props: { chatLimit: 256, save: save.impl } }); - const input = screen.getByLabelText("Chat limit"); - await user.clear(input); - await user.type(input, "5"); // clamps to MIN (10) - await user.click(screen.getByRole("button", { name: "Set" })); + const input = screen.getByLabelText("Chat limit"); + await user.clear(input); + await user.type(input, "5"); // clamps to MIN (10) + await user.click(screen.getByRole("button", { name: "Set" })); - // The save port receives the clamped value (the view-model clamps before save). - expect(save.saves).toEqual([10]); - expect(input).toHaveValue("10"); - }); + // The save port receives the clamped value (the view-model clamps before save). + expect(save.saves).toEqual([10]); + expect(input).toHaveValue("10"); + }); }); -- cgit v1.2.3