| name | fantasia-testing |
| description | Runs and extends Fantasia Archive tests: Vitest unit tests vs Playwright component and E2E tests, including rebuild-before-Playwright rules and file naming. Use when writing tests, debugging CI, or when the user mentions Vitest, Playwright, component tests, or e2e. |
Fantasia Archive — testing
Cursor rules (detailed structure)
Match existing tests to the letter when adding or editing:
Connected tests for any feature change
Treat tests as part of the same deliverable as production edits—not an optional follow-up.
- Discover — Search the repository (for example ripgrep) for the component or dialog folder name, exported helpers,
data-test-locator strings, T_dialogName, COMPONENT_NAME, action or keybind ids, store symbols, and i18n keys you added or changed. Follow imports and menu _data/ entries that reference the feature.
- Vitest — Run
yarn vitest run with explicit paths for every matching *.vitest.test.ts (feature _tests/, colocated scripts/_tests/, src/scripts/**/_tests, src/stores/_tests, src-electron/**/_tests, helpers/**/_tests, i18n/_tests when implicated). Before commits, yarn test:unit (full workspace) or yarn testbatch:verify must still pass.
- Playwright (component) — For each matching
src/**/_tests/*.playwright.test.ts, run yarn test:components:single --component=<bucket>/<ComponentFolder> or yarn test:components in its own terminal after yarn quasar:build:electron when the built bundle would exercise changed renderer code (testing-terminal-isolation.mdc).
- Playwright (E2E) — For each matching
e2e-tests/*.playwright.spec.ts, run yarn test:e2e:single --spec=… or yarn test:e2e with the same rebuild rule.
CI scope: the default Verify workflow runs yarn testbatch:verify only (lint, types, stylelint, Vitest coverage). It does not run component or E2E Playwright—those must be run locally (or via yarn testbatch:ensure:nochange) when the feature touches flows they cover.
Vitest coverage tiers (CI)
Same rules as vitest-tests.mdc (Vitest coverage tiers (CI) section): 100% all metrics on src-electron; 100% all metrics on helpers/**/*.ts files that unit-helpers actually instruments (helpers/playwrightHelpers_* trees are excluded from that pool—see vitest.helpers.config.mts); 100% all metrics on unit-src-renderer src .ts (boot, scripts, stores); 100% on all four for scoped i18n/ entry unit-i18n; 100% on all four for merged unit-components src/components/**/*.ts, src/layouts/**/*.ts, and src/pages/**/*.ts; matching .vue SFCs have no threshold—~60% watermarks for review (src/components/foundation/** excluded from the pool). Configs live under vitest/; entry vitest.config.mts sets repo root and extends per project.
Unit tests (Vitest)
- Commands:
yarn test:unit runs the Vitest multi-project root in vitest.config.mts (unit-electron, unit-src-renderer, unit-helpers, unit-i18n, unit-components) without coverage. yarn testbatch:verify ends with yarn test:coverage:verify: 100% on all four v8 metrics for src-electron (vitest.electron.config.mts); unit-helpers (vitest.helpers.config.mts) only attributes coverage to non-playwright helpers/**/*.ts matches ( helpers/playwrightHelpers_* excluded; the project may report no tests until other helper packages land); 100% on all four for scoped i18n/ (unit-i18n); 100% on all four for unit-src-renderer src .ts (vitest.src-renderer.config.mts); 100% on all four for unit-components merged .ts globs, with .vue SFCs only watermarked (60% lower band for review—no failing gate) (vitest.components.config.mts). Use yarn test:coverage:electron, yarn test:coverage:helpers, yarn test:coverage:i18n, or yarn test:coverage:src to debug one slice.
- Execution policy: use
yarn test:unit while iterating; before commits run yarn testbatch:verify (testing-terminal-isolation.mdc). Do not chain unit/coverage commands with yarn quasar:build:electron or Playwright in one shell line.
- Machine-readable reports:
test-results/vitest-report/test-results-vitest-*.json per project (electron, src-renderer, helpers, i18n, components).
- Scope: Logic in
src/ and src-electron/ (including main-process modules) with *.vitest.test.ts co-located under _tests/ folders; component mounting tests use @vue/test-utils + shared vitest.setup.ts.
helpers/ packages: helpers/playwrightHelpers_universal, helpers/playwrightHelpers_e2e, and helpers/playwrightHelpers_component are Playwright harness only—extend them alongside yarn test:components / yarn test:e2e after yarn quasar:build:electron; do not add *.vitest.test.ts there. When you add non-Playwright packages under helpers/<name>/, treat them like src-electron helpers: colocate _tests/*.vitest.test.ts; yarn test:unit runs them via helpers/**/*.vitest.test.ts (unit-helpers config excludes playwright trees from strict coverage attribution). Keep harness packages at repo-root helpers/; do not duplicate them under src/.
- SFC baseline: under
src/components/**, src/layouts/**, and src/pages/**, maintain one colocated _tests/<Name>.vitest.test.ts per feature .vue (add/rename/remove both together). Extracted scripts/*.ts next to a component SFC should gain scripts/_tests/*.vitest.test.ts when they hold real logic (same idea as src/scripts helpers). When several scripts/*.ts files are merged into fewer modules for readability, consolidate the matching scripts/_tests/*.vitest.test.ts files the same way (fewer files, grouped by concern) so tests stay easy to discover — see code-size-decomposition.mdc Module count: prefer logical grouping.
- Return object literals in factories or test helpers: same project-wide rule as production —
return { ... } lists identifiers or literals bound before the return; no inline functions or ternary logic as property values (code-size-decomposition.mdc Return object literals).
- Floating
Window* Vitest mounts: production frames teleported to document.body via _FaFloatingWindowBodyTeleport. wrapper.find(...) after mount often misses teleported nodes — stub using the parent’s import binding (for example FaFloatingWindowBodyTeleport) and <div><slot /></div> (see WindowAppStyling.vitest.test.ts) or query document.body. See fantasia-floating-windows.
- Renderer examples:
src/scripts/** helpers, store/composable state transitions, and other deterministic src/ logic that does not require full Electron runtime wiring.
_data/ is for production feeds (not automated-test fixture blobs): do not add Vitest suites aimed only at _data/ paths; validate production data indirectly via components or scripts. Vitest fixtures and Playwright fixture objects (props payloads, key lists, gold values) stay inside the respective *.vitest.test.ts / *.playwright.test.ts files as inline const data — no extra _tests/*.ts files used only as fixture dumps. Do not use _tests/_data/.
- Style: Flat
test / test.skip only (no describe), JSDoc above each test naming the function under test, titles like Test that ... — see src-electron/**/_tests/*.vitest.test.ts and the vitest rule above.
- Typing: Avoid
any in test code and fixtures; use concrete interfaces, inferred literals, or unknown narrowed before assertion/use.
- Shared type naming: Preserve project naming conventions for imported types (
I_ interfaces, T_ aliases) and prefer descriptive names such as I_appMenuList / T_dialogName.
- Coverage semantics: 1:1 component-to-suite parity means coverage presence for
.vue; src .ts in the Vitest coverage include lists must meet the enforced percentages (see vitest-tests.mdc). .vue rows below ~60% lines or statements in the unit-components report warrant investigation.
Playwright (component + E2E)
Critical: Playwright targets a built, production Electron app. After any source change affecting what tests exercise, run quasar build -m electron (or yarn quasar:build:electron) before Playwright. Use Node.js 22.22.0+ locally (package.json engines) so Electron / native steps match CI.
Stale packaged bundle — The Electron harness starts Fantasia Archive.exe (or the OS equivalent) from dist/electron/Packaged, not the live Vite dev server. When a spec fails in ways that ignore your latest src/ or src-electron/ edits (for example IPC round trips still matching pre-change behavior), assume the dist/electron tree is behind and rebuild before another Playwright run.
Electron userData isolation: Specs set TEST_ENV to components or e2e. The main process then uses %APPDATA%/<package.json name>/playwright-user-data (this app: fantasia-archive/playwright-user-data, not fantasia-archive-dev). appIdentity_manager.ts applies that path in Electron. playwrightIsolatedUserDataDirName.ts exports PLAYWRIGHT_ISOLATED_USER_DATA_DIR_NAME with no electron import so playwrightUserDataReset.ts can run in Node without importing fixAppName—otherwise Playwright test collection hits import { app } from 'electron' and fails. Component and E2E specs group tests in test.describe.serial; each group’s test.beforeAll should await launchFaPlaywrightComponentHarnessWindow / launchFaPlaywrightE2eAppWindow, which own resetFaPlaywrightIsolatedUserData() ordering (do not call resetFaPlaywrightIsolatedUserData() manually in test.beforeEach while the same ElectronApplication stays open—the on-disk profile would diverge from the running process). Shared Playwright modules live under helpers/playwrightHelpers_universal / _e2e / _component; add future Electron-free helper packages elsewhere under helpers/<name>/. Do not use test.describe.parallel for these Electron specs unless the user explicitly asks; do not commit test.describe.serial.only or other .only hooks except for short local debugging.
Playwright keyboard.press and app keybinds (cross-OS)
- Module:
helpers/playwrightHelpers_universal/faPlaywrightKeyboardChords.ts — shared keyboard.press strings for Electron component and E2E specs (no unit-helpers Vitest target for playwrightHelpers_* trees today).
- Defaults using
primary in FA_KEYBIND_COMMAND_DEFINITIONS: On darwin the effective modifier is Meta; on win32 / linux it is Control. Use getFaPlaywrightDefaultToggleDevtoolsPressString() and getFaPlaywrightDefaultActionMonitorOpenPressString() (and new getters in the same file if you add more defaults) instead of hardcoding Control+F12 or Meta+F12.
- User capture / overrides that store
ctrl: Playwright must send the physical Control key on every host OS (for example Control+Shift+F12). Use FA_PLAYWRIGHT_PRESS_CONTROL_SHIFT_F12, FA_PLAYWRIGHT_PRESS_CONTROL_SHIFT_F11, or FA_PLAYWRIGHT_PRESS_ADJUSTED_TOGGLE_DEVTOOLS_F12 as appropriate — not Meta on macOS for those flows.
- Monaco select all in tests:
getFaPlaywrightMonacoSelectAllPressString() (Meta+A vs Control+A).
- Full policy and import-order note: playwright-tests.mdc section Keyboard strings; product keybind architecture: AGENTS.md Global keyboard shortcuts (faKeybinds) and fantasia-keybinds.
Cross-toolchain alignment (Storybook + Electron + same repo)
- Storybook — Runs from
.storybook-workspace/; config must keep staticDirs (and related Vite public/ wiring) in sync with the Quasar app so public assets resolve the same way in Storybook dev and yarn storybook:build output. See .storybook-workspace/.storybook/main.ts. Static build lands in .storybook-workspace/storybook-static/. Workspace storybook:build and test:storybook:smoke use --quiet --loglevel warn; production viteFinal further lowers Vite noise (warnings still show). Storybook VRT uses .storybook-workspace/playwright.storybook-visual.config.ts and .storybook-workspace/visual-tests/; root yarn test:storybook:visual* chains yarn storybook:build with yarn --cwd .storybook-workspace test:storybook:visual*. Per-story [storybook-visual] progress logs are off by default; set FA_STORYBOOK_VISUAL_VERBOSE=1 when debugging. @playwright/test, playwright, and http-server for that flow are devDependencies of .storybook-workspace (install with yarn --cwd .storybook-workspace install).
- Electron
file:// — Packaged renderer paths are not a web origin at /. If import.meta.env.BASE_URL is '/' or empty, building public/ URLs as /images/... can fail loading; use a relative prefix (e.g. ./) for those assets (see SocialContactSingleButton.vue).
- Playwright — Same rebuild rule as above:
yarn quasar:build:electron before yarn test:components / yarn test:e2e when exercised sources changed; flaky UI in tests after a green Storybook pass often means a stale build or a file:// URL mismatch.
- Storybook visual snapshots — Keep
layouts-componenttestinglayout--with-social-contact-single-button and pages-componenttesting--social-contact-single-button excluded from snapshot collection; they are Playwright harness utility previews and are not meaningful VRT surfaces (see EXCLUDED_STORY_IDS in .storybook-workspace/visual-tests/storybook.visual.playwright.test.ts). The GlobalLanguageSelector story uses skip-visual as well: VRT’s static iframe often omits the fixed title-bar control from captures even though yarn storybook:run shows it for manual review. For other stories, an empty iframe root after the render wait is a failure unless the story opts out with tags: ['skip-visual-render-check'] (rare) or its id is added to EXCLUDED_STORY_IDS with an explicit reason. The render gate treats #storybook-root and #root as two mounts (checks each for element children alongside portaled q-dialog / q-menu / role="dialog" signals); querySelector on '#storybook-root, #root' alone must not dominate readiness, since first-match DOM order might surface an idle #root while #storybook-root mounts the preview.
- Storybook VRT
maxDiffPixels — storybook.visual.playwright.test.ts passes maxDiffPixels into toHaveScreenshot. That value is the maximum count of differing pixels over the full screenshot (after Playwright’s per-pixel threshold), not a width. Large iframes mean even ~2000 differing pixels is a small fraction of the image; the cap mainly absorbs font / subpixel / Chromium differences between a developer Windows machine and GitHub Actions windows-latest when comparing the same committed -chromium-win32.png baseline. If yarn test:storybook:visual passes locally but yarn testbatch:ensure:nochange fails on GitHub with diffs slightly over the cap, check CI’s reported pixel counts and diff PNGs—then either bump maxDiffPixels with a comment citing observed CI counts, or run yarn test:storybook:visual:update when UI changes are intentional. See README Storybook visual baseline policy and storybook-stories.mdc.
- One-shot full suite —
yarn testbatch:ensure:nochange runs yarn testbatch:verify + yarn quasar:build:electron:summarized + yarn test:components + yarn test:e2e + yarn test:storybook:smoke + yarn test:storybook:visual (committed snapshot compare). yarn testbatch:ensure:change is the same through smoke, then yarn test:storybook:visual:update for intentional baseline refresh only.
Config highlights (playwright.config.ts)
- Single
outputDir: test-results/playwright-artifacts (per-test subfolders; Playwright copies testInfo.attach path-based files into each test's attachments/). testMatch limits runs to src/components/** and e2e-tests/**. Terminal reporter list (one line per test as it runs; failures in full). HTML report: test-results/playwright-report (attachment bytes are duplicated into playwright-report/data/). yarn test:components / yarn test:e2e (and single-spec variants) run via .utility-scripts/playwrightWithArtifactTrim.mjs, which deletes test-results/playwright-artifacts after the run so only the HTML report tree (including data/*.webm) remains. Raw recordVideo output uses an OS temp dir and is removed after attach (helpers/playwrightHelpers_universal/playwrightElectronRecordVideo.ts).
workers: 1, fullyParallel: false — assume sequential, single-worker runs unless you change config.
- Electron component and E2E specs use
test.describe.serial: test.beforeAll awaits launchFaPlaywrightComponentHarnessWindow / launchFaPlaywrightE2eAppWindow (suiteTestInfo = testInfo, buildLaunchEnv returning env overrides); test.afterAll awaits tearDownFaPlaywrightElectronSerialSuite so WebMs attach cleanly. Lifecycle code lives under helpers/playwrightHelpers_universal (faPlaywrightSerialSuiteLifecycleLaunch / faPlaywrightSerialSuiteLifecycleTeardown). Disable the synthetic cursor with FA_PLAYWRIGHT_CURSOR_MARKER=0; skip video with FA_PLAYWRIGHT_NO_VIDEO. Recordings use 1920×1080 via recordVideo.size.
Videos and HTML report (human review and agents)
- Each
test.describe.serial group’s electron.launch can produce a usable WebM recording attached via that suite’s TestInfo. After yarn test:components, yarn test:e2e, or the :single / :single:ci variants, open test-results/playwright-report/index.html in a browser, drill into a test row, and use Attachments to play or save the video. Files the UI plays from live under test-results/playwright-report/data/ (content-addressed names).
- Report and scratch output are ephemeral across runs: every Playwright invocation regenerates
test-results/playwright-report/. Running a different suite (component vs E2E) or re-running the same suite replaces the previous report—nothing is accumulated there. The yarn Playwright scripts also remove test-results/playwright-artifacts after each run via .utility-scripts/playwrightWithArtifactTrim.mjs so duplicate on-disk copies are not kept beside the report.
- LLMs / agents: do not assume you can meaningfully “analyze” full motion video from repo context alone. Prefer telling the user to open
test-results/playwright-report/index.html (or to share a screenshot or textual failure) rather than treating raw .webm blobs as inspectable prose.
Component tests
-
Renderer readiness (hash routing): page.evaluate, waitForFunction, and page.waitForURL defaults run in Playwright worlds that do not see Electron contextBridge.exposeInMainWorld globals, and hash #/componentTesting/… navigations often omit classic navigation events waitForURL waits on. Prefer waitForFaRendererContentBridgeApis(appWindow) from helpers/playwrightHelpers_universal/waitForFaRendererContentBridgeApis.ts — it polls appWindow.url() until componentTesting/ appears. E2E launches that stay on #/ should await waitForFaE2eRendererDomReady(appWindow) (DOMContentLoaded timeout) instead.
-
Structure: Prefer launchFaPlaywrightComponentHarnessWindow + tearDownFaPlaywrightElectronSerialSuite; keep header constants (extraEnvSettings, selectorList), test.describe.serial, JSDoc per test, and shared appWindow per .cursor/rules/playwright-tests.mdc.
-
Locators: Prefer data-test-locator plus other data-test-* from .vue templates (never bare data-test); document static values in selectorList and use small helpers beside it for dynamic data-test-locator suffixes (see SocialContactSingleButton.playwright.test.ts vs DialogAppSettings.playwright.test.ts appSettingsSelector). Exception locators (Quasar portaled [role="tooltip"], E2E menu strings, etc.) still belong in selectorList as the full selector string under a clear key (for example quasarTooltip: '[role="tooltip"]') — avoid raw literals in the test body. For suites with many tooltips, put duplicate tooltip text on the trigger (data-test-tooltip-text) for bulk string checks and keep at least one hover + live tooltip assertion (appWindow.locator(selectorList.quasarTooltip), etc.) so behavior stays real (DialogAppSettings.vue).
-
Layout width/height: Prefer data-test-layout-width, data-test-layout-height, and domain caps such as data-test-error-card-width (wired from the same props or markup as sizing) so component and E2E specs stay stable when the window or parent is narrow. Use locator.boundingBox() only when you need rendered pixels and the harness guarantees enough space, or for inequality checks. See the Layout size (width and height) section in playwright-tests.mdc.
-
Typing: Keep selectors, props payloads, and helper arguments strongly typed; avoid any.
-
Command: yarn test:components
-
Execution policy: run this command in its own terminal invocation; never combine it with other verification commands in one chained shell command.
-
Location: Under src/components/, files ending in .playwright.test.ts (often in a _tests/ subfolder next to the component).
-
Single test: yarn test:components:single --component=<bucket>/<ComponentName> (for example dialogs/DialogAppSettings, elements/ErrorCard; see package.json for Windows %npm_config_*% variants). src/components/foundation/** does not ship Playwright specs (Storybook-only catalogues).
-
Interactive picker: yarn test:components:list → runs testRunner_component.mjs (discovers *.playwright.test.ts under src/components/, lists choices as bucket/ComponentFolder matching the repo tree).
E2E tests
-
Structure: Same test.describe.serial / beforeAll / afterAll pattern as components; e2e files use TEST_ENV: 'e2e', may use numeric faFrontendRenderTimer, and sometimes getByText with visible labels — see e2e-tests/*.playwright.spec.ts in the repo.
-
Project management / .faproject: checkProjectManagementFlow.playwright.spec.ts covers splash Create new project, Load existing project, Resume Latest Project split (dropdown arrow opens splashPage-resumeMenu; primary segment loads MRU head after spellcheck-clear), and top-menu Project Management create/load flows. Stage the next create or open path with e2eSetNextProjectCreatePath / e2eSetNextProjectOpenPath from playwrightE2eProjectPaths.ts (pairs with projectManagement_manager.ts / functions/faProjectManagementE2ePathOverride.ts in main). When the welcome screen is visible, use getByRole('button', { name: projectMenu.title, exact: true }) ( projectMenu from L_project) so Playwright strict mode does not match Create new project or other labels that contain project. After at least one successful create/open, SplashControlsResumeDropdown exposes data-test-locator hooks such as splashPage-btn-resume-latest, splashPage-resumeMenu, and per-row splashPage-recentProject-<index>; prefer those over BEM/class selectors—use .q-btn-dropdown__arrow-container vs .q-btn-dropdown--current for opening the menu vs clicking the primary segment.
-
Command: yarn test:e2e
-
Execution policy: run this command in its own terminal invocation; keep E2E output isolated from other command logs.
-
Location: e2e-tests/*.playwright.spec.ts
-
Single spec: yarn test:e2e:single --spec=<stem> — stem only, no .playwright.spec.ts (for example checkDevToolsFunctionality). yarn test:e2e:single:ci --spec=<file> takes the full e2e-tests/ file name including the suffix (same string yarn test:e2e:list uses). Yarn forwards these flags with the same npm_config_* pattern as --component= on component runs (see package.json).
-
Interactive picker: yarn test:e2e:list → testRunner_e2e.mjs
Full project gate
yarn testbatch:ensure:nochange runs yarn testbatch:verify (lint + types + stylelint + Vitest coverage with layered gates per vitest-tests.mdc) + yarn quasar:build:electron:summarized + Playwright component + Playwright E2E + Storybook smoke + Storybook visual compare in one chain. yarn testbatch:ensure:change ends with Storybook visual snapshot update instead of compare — use only when baselines should change (see testing-terminal-isolation.mdc for when to split commands across terminals instead).
Checklist when changing UI or Electron shell
- Quality gate in one terminal:
yarn testbatch:verify — fix issues per eslint-typescript.mdc and vitest-tests.mdc (testing-terminal-isolation.mdc).
- Connected test sweep — follow Connected tests for any feature change above (grep for locators, dialog names, locale keys; run every implicated Vitest path, then component and E2E Playwright after rebuild when in scope).
- Rebuild:
yarn quasar:build:electron (or quasar build -m electron) — its own terminal.
yarn test:components / yarn test:e2e as needed — each in its own terminal; do not chain with yarn quasar:build:electron or with each other in one line, unless you intentionally run yarn testbatch:ensure:nochange or yarn testbatch:ensure:change.
- When Storybook-backed UI or VRT snapshots are in scope: run
yarn test:storybook:smoke and yarn test:storybook:visual (or use yarn testbatch:ensure:nochange to cover verify + build + Playwright + Storybook in one shot). Use yarn testbatch:ensure:change only when deliberately updating committed Storybook snapshots.
Choosing Vitest vs Playwright in renderer work
- Prefer Vitest when validating pure/data/state behavior in
src/ and keep assertions deterministic.
- Prefer Playwright when validating user-facing interaction flow, full component rendering behavior, or anything relying on the built Electron app runtime.
Storybook smoke checks (component authoring support)
- Storybook commands:
yarn storybook:run (interactive) and yarn test:storybook:smoke / storybook dev --smoke-test --ci (startup verification).
- Visual regression (Playwright + static Storybook): after
yarn storybook:build, run yarn test:storybook:visual from the repo root, or yarn --cwd .storybook-workspace test:storybook:visual if storybook-static/ is already present. Update baselines with yarn test:storybook:visual:update (or workspace test:storybook:visual:update). HTML/report output and artifacts stay under repo-root test-results/storybook-visual-* locally (not run in GitHub Actions; use yarn testbatch:ensure:nochange or the individual scripts when you need the full gate). Screenshot drift surfaces Expected / Actual / Diff on each failing per-story test.step in test-results/storybook-visual-report/index.html (soft assertions keep the run walking the story list).
- Keep component Storybook stories in
_tests/ subfolders as src/components/**/_tests/<Component>.stories.ts, with meta.title Components/<bucket>/<ComponentName> (same dialogs / elements / foundation / globals / other buckets as src/components/). foundation/ catalogues are Storybook-only (no Playwright specs); see AGENTS.md Foundation components.
src/layouts/** and src/pages/** stories (if present) live in _tests/ subfolders (src/layouts/**/_tests/*.stories.ts, src/pages/**/_tests/*.stories.ts) and are canvas-only previews; do not expect or add Storybook Docs/autodocs for them (see storybook-stories.mdc).
- When components rely on i18n-backed markdown docs, avoid importing full locale roots in Storybook mocks; use focused
L_* locale module imports plus placeholder documents.* strings in .storybook-workspace/.storybook/mocks/externalFileLoader.ts to prevent markdown import-analysis failures. Any new L_* registered in i18n/en-US/index.ts for namespaces used by stories (dialogs.*, globalFunctionality.*, etc.) must also be added to that file’s defaultMessages or Storybook will show broken/untranslated keys.
TypeScript interfaces and types (types/)
- Put shared
interface / type declarations in repository-root types/ (import with app/types/...). Prefer one domain-oriented module per feature area with brief JSDoc on exports (see types/I_appMenusDataList.ts). Do not add colocated <filename>.types.ts under src/, src-electron/, or .storybook-workspace/. Ambient augmentations for third-party modules also live under types/ and are loaded with a side-effect import from the owning boot file or src/stores/index.ts (see types/piniaModuleAugmentation.ts).
- For JavaScript (
.js), TypeScript (.ts), Vue (.vue), and JSON (.json, .jsonc, .json5) files, enforce expanded multi-line object literals via ESLint (object-curly-newline + object-property-newline) and keep files auto-fixable with eslint --fix.