diff --git a/biome.jsonc b/biome.jsonc index dbbd8f56..f04e2c22 100644 --- a/biome.jsonc +++ b/biome.jsonc @@ -64,7 +64,6 @@ }, "rules": { "preset": "recommended", - "a11y": "info", "complexity": { "useLiteralKeys": "off" }, diff --git a/docs/development.md b/docs/development.md index 484ccea0..bc40cdf2 100644 --- a/docs/development.md +++ b/docs/development.md @@ -14,7 +14,7 @@ Use this as the normal edit loop. `npm run fix` applies Biome formatting, import Use focused scripts for tight loops: `npm run typecheck`, `npm run test`, `npm run build`, or the targeted `npm run check:*` scripts. CI and release preflight run the same `npm run check` command as local development. -Biome owns formatting, import organization, general JavaScript/TypeScript/JSON/CSS linting, Preact-compatible React domain rules, GritQL source-shape plugins, GritQL import-boundary restrictions, imperative-DOM bridge naming, and GritQL CSS source policy. Biome warnings fail `npm run check`; info diagnostics stay advisory, including accessibility while Obsidian-specific UI semantics are reviewed rule by rule. The CSS usage script still checks dead authored classes. TypeScript provides compiler-backed checks such as strict type checking, unused locals and parameters, implicit return coverage, switch fallthrough prevention, and deep-readonly `ChatState` snapshots. ESLint runs the TypeScript ESLint strict type-checked preset over source files, plus Obsidian plugin policy for `manifest.json` and `LICENSE`. Tests are covered by TypeScript, Vitest, and Biome rather than typed ESLint so local preflight stays responsive. Some checks intentionally overlap between Biome, TypeScript, and ESLint so the configuration stays simple. +Biome owns formatting, import organization, general JavaScript/TypeScript/JSON/CSS linting, accessibility linting, Preact-compatible React domain rules, GritQL source-shape plugins, GritQL import-boundary restrictions, imperative-DOM bridge naming, and GritQL CSS source policy. Biome warnings fail `npm run check`; rule suppressions must stay local and include the Obsidian-specific rationale when a native Obsidian UI pattern intentionally diverges from a generic browser semantic. The CSS usage script still checks dead authored classes. TypeScript provides compiler-backed checks such as strict type checking, unused locals and parameters, implicit return coverage, switch fallthrough prevention, and deep-readonly `ChatState` snapshots. ESLint runs the TypeScript ESLint strict type-checked preset over source files, plus Obsidian plugin policy for `manifest.json` and `LICENSE`. Tests are covered by TypeScript, Vitest, and Biome rather than typed ESLint so local preflight stays responsive. Some checks intentionally overlap between Biome, TypeScript, and ESLint so the configuration stays simple. ## Generated and Loaded Files diff --git a/src/features/chat/ui/composer.tsx b/src/features/chat/ui/composer.tsx index 307f63cc..44bf6225 100644 --- a/src/features/chat/ui/composer.tsx +++ b/src/features/chat/ui/composer.tsx @@ -395,6 +395,7 @@ function ComposerMetaPickerButton({ onMouseDown: () => void; }): UiNode { return ( + // biome-ignore lint/a11y: Composer meta triggers are visual pointer shortcuts; screen readers get the status summary and full runtime controls remain available through the toolbar and slash commands. { diff --git a/src/features/chat/ui/message-stream/pending-request-block.tsx b/src/features/chat/ui/message-stream/pending-request-block.tsx index f4e6b409..a5bb89d8 100644 --- a/src/features/chat/ui/message-stream/pending-request-block.tsx +++ b/src/features/chat/ui/message-stream/pending-request-block.tsx @@ -397,14 +397,27 @@ function McpElicitationField({ actions: PendingRequestBlockActions; }): UiNode { const current = drafts.get(field.draftKey) ?? field.defaultDraft; + const labelId = mcpElicitationFieldElementId("label", field.draftKey); + const controlId = mcpElicitationFieldElementId("control", field.draftKey); + const labelContent = ( + <> + {field.title} + {field.required ? Required : null} + + ); return (
- + {mcpElicitationFieldHasSingleControl(field) ? ( + + ) : ( +
+ {labelContent} +
+ )} {field.description ?
{field.description}
: null} - +
); } @@ -413,16 +426,21 @@ function McpElicitationFieldControl({ field, current, actions, + controlId, + labelId, }: { field: PendingMcpElicitationFieldViewModel; current: string; actions: PendingRequestBlockActions; + controlId: string; + labelId: string; }): UiNode { switch (field.type) { case "boolean": return (