From fc92dcdbd7b5e78e29ecacf1504ba0b70feb7678 Mon Sep 17 00:00:00 2001 From: PythonLuvr Date: Tue, 2 Jun 2026 22:55:05 +0700 Subject: [PATCH] a11y semantics + macOS/standalone build fixes (salvaged from #1) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Salvages the safe, English parts of #1 (which also bundled a Spanish UI fork, a personal sync script, and an unverified supervisor/sub-agent feature — those are intentionally left out). a11y: - lib/a11y: reusable prop factories (modalProps, chatLogProps, statusRegionProps, composerProps, ariaExpanded, ariaCurrent) - chat message list as a role="log" aria-live region; streaming cursor as a status region; composer aria-label + describedby + sr-only hint - role="dialog"/aria-modal on settings, channel, invite, onboarding modals - ); } @@ -175,6 +179,7 @@ function ServerIcon({ }} title={`${server.name} · right-click to edit`} aria-label={`Open ${server.name} server`} + {...ariaCurrent(active)} className="relative group" > {active && ( diff --git a/components/settings-modal.tsx b/components/settings-modal.tsx index 200f331..226a201 100644 --- a/components/settings-modal.tsx +++ b/components/settings-modal.tsx @@ -18,6 +18,7 @@ import { import { UploadButton } from "@/components/upload-button"; import { HostingPanel } from "@/components/settings-hosting-panel"; import { JoinForm } from "@/components/settings-hosting-join-form"; +import { modalProps } from "@/lib/a11y"; export type SettingsTab = "general" | "agent" | "sidebar" | "boardroom" | "sync" | "about"; @@ -52,6 +53,7 @@ export function SettingsModal({
e.stopPropagation()} + {...modalProps("Settings")} className="bg-[#0d0d0f] border border-neutral-800 rounded-2xl shadow-2xl w-full max-w-5xl flex flex-col max-h-[90vh] overflow-hidden" >
diff --git a/electron/after-pack.js b/electron/after-pack.js index 45eda26..48bc9ea 100644 --- a/electron/after-pack.js +++ b/electron/after-pack.js @@ -9,7 +9,19 @@ const path = require("path"); exports.default = async function afterPack(context) { const projectRoot = context.packager.info.projectDir; const appOutDir = context.appOutDir; - const dest = path.join(appOutDir, "resources", "app", ".next", "standalone"); + const productName = context.packager.appInfo.productName; + + // On macOS, electron-builder places the .app bundle at + // appOutDir/.app/Contents/Resources/, NOT at + // appOutDir/resources/. The old path left the standalone server outside the + // .app bundle, so process.resourcesPath could not find it and the embedded + // Next.js server never started. + const resourcesDir = + process.platform === "darwin" + ? path.join(appOutDir, `${productName}.app`, "Contents", "Resources") + : path.join(appOutDir, "resources"); + + const dest = path.join(resourcesDir, "app", ".next", "standalone"); // Wipe whatever electron-builder copied first (it may have partial contents). if (fs.existsSync(dest)) { diff --git a/electron/main.js b/electron/main.js index 74748ec..bf9e4bd 100644 --- a/electron/main.js +++ b/electron/main.js @@ -371,6 +371,11 @@ if (!hasSingleInstanceLock) { }); app.whenReady().then(async () => { + // Must run after ready. Without it, Chromium only builds the accessibility + // tree when a screen reader is already running at launch, so a reader + // started after the app (e.g. VoiceOver) would see an empty tree. + app.setAccessibilitySupportEnabled(true); + await createMainWindow(); await createMiniWindow(); diff --git a/lib/a11y/index.ts b/lib/a11y/index.ts new file mode 100644 index 0000000..c434df0 --- /dev/null +++ b/lib/a11y/index.ts @@ -0,0 +1,80 @@ +/** + * Accessibility helpers for War Room. + * + * Small prop factories so screen-reader semantics (dialog roles, live regions, + * expand/collapse state) stay consistent across components instead of being + * hand-rolled at each call site. + */ + +/** + * Props for an accessible modal dialog. + * Usage:
+ */ +export function modalProps(title: string): { + role: "dialog"; + "aria-modal": true; + "aria-label": string; +} { + return { role: "dialog", "aria-modal": true, "aria-label": title }; +} + +/** + * Props for a collapsible section toggle button. + * Usage: