| name | mas-frontend-standards |
| description | Use when working on AUTO-MAS frontend Vue, TypeScript, Vite, Electron renderer, routing, API composables, state, styles, forms, validation, or frontend verification tasks. |
MAS Frontend Standards
Objective
Keep AUTO-MAS frontend changes aligned with the current Vue 3, TypeScript, Vite, Electron, Ant Design Vue, Vue Router, OpenAPI, ESLint, Prettier, and Yarn 4 project conventions.
Dependency Installation
- Run frontend dependency commands from the
frontend/ directory.
- Use
yarn install by default.
- If the Electron binary download is slow or fails, use the one-shot mirror command:
yarn cross-env ELECTRON_MIRROR="https://npmmirror.com/mirrors/electron/" yarn install.
Authority
This skill is self-contained for frontend engineering rules. If local code differs from this summary, inspect neighboring implementations and prefer the current module pattern unless it violates a red line here.
Mandatory Intake
Before editing frontend code:
- Confirm branch, remote, and working tree status.
- Inspect the target page, adjacent pages, related components, composables, API wrappers, router entries, and styles.
- Classify the task as new page, fix, refactor, style/UI adjustment, API integration, route change, or documentation-only.
- Select
mas-frontend-ui as a required companion for any UI, layout, component, form, table, modal, feedback, or visual-state change.
- Keep changes limited to files directly required by the task.
Directory And Ownership
| Code type | Place it here | Do not place it here |
|---|
| Route page | src/views/<module>/index.vue or src/views/Xxx.vue | src/components |
| Single-module component | src/views/<module>/components | src/components root |
| Cross-module component | src/components | Copied across page folders |
| Business API wrapper | src/composables/useXxxApi.ts | Vue page or src/api |
| Reusable page flow | src/views/<module>/useXxxLogic.ts or composable | Template expressions |
| Pure utility | src/utils | Copied inside components |
| Domain type | src/types or generated src/api/models | Repeated local component types |
| Shared reactive application state | src/stores with Pinia | Page-level localStorage access |
| Global style token | src/style.css or future src/styles | Repeated page-local variables |
| Global browser UI style such as scrollbars | src/styles, imported once by the renderer entry | Repeated component-level browser pseudo-elements |
| Electron capability | electron and preload types | Direct renderer Node access |
Use the narrowest module boundary first. Promote to shared directories only after real cross-module reuse.
Vue Component Rules
- Use
<script setup lang="ts"> unless a compatibility reason exists.
- Order code as imports, types, props/emits, composables, state, computed, watchers, lifecycle, functions.
- Type props and emits explicitly.
- Keep computed values for derived state; keep watchers for side effects only.
- Split components that exceed 500 lines or contain multiple independent business regions.
- Extract repeated logic into composables or module logic files instead of growing templates.
- Do not introduce a new UI, state, request, or routing library for a small task.
API And Data Flow
- Never hand-edit generated files under
src/api.
- Do not write business
axios or fetch calls directly in Vue pages.
- Use generated
src/api types and services through src/composables/useXxxApi.ts or module logic.
- Treat
response.code !== 200 as failure unless the local contract proves otherwise.
- API composables should expose loading, error, and business functions.
- Pages own business flow such as navigation, closing dialogs, and local state updates.
- Static-resource checks such as audio
HEAD requests are not precedent for backend business API calls.
- Backend schema changes require regenerating frontend API clients; do not manually patch OpenAPI output.
Routing, State, Config
- Route paths use lowercase kebab-case; route names use PascalCase.
- Route components are lazy-loaded and business routes include
meta.title.
- Prefer
navigateTo or navigateToByName; avoid scattered hardcoded paths.
- Prefer Pinia for reactive application data shared across components or pages, coordinated state with multiple readers or writers, and state that should survive component unmounts or route changes during the current session.
- Do not store Pinia-suitable state directly in
localStorage, and do not keep Pinia and localStorage as competing sources of truth.
- Keep truly component-local, short-lived state in the owning component. Do not create a store for a single isolated control or form draft with no cross-component lifecycle.
- Use
localStorage only for small, non-sensitive browser-local preferences that must survive application restarts and do not belong to Pinia, backend data, or Electron configuration. Namespace keys, validate parsed values, and provide safe defaults for missing or invalid data.
- When shared Pinia state also needs durable persistence, prefer an existing backend or Electron configuration owner instead of adding ad hoc
localStorage mirroring.
- Never place tokens, credentials, permissions, secrets, large business datasets, or server-authoritative data in
localStorage.
- API and WebSocket endpoints come from Electron config helpers; do not hardcode backend addresses in business pages.
- Vite-exposed environment variables use the
VITE_ prefix.
Style And Code Quality
- Component styles default to
<style scoped>.
- Global styles are only for tokens, root layout, or deliberate Ant Design Vue global fixes.
- Use CSS variables or Ant Design tokens for color, spacing, radius, shadow, and typography.
- Use kebab-case class names and a semantic page root such as
.queue-page.
- Prefer
@/ imports over deep relative paths.
- Avoid
any; if unavoidable, narrow the scope and explain why.
- Do not use
console.log in business code; use window.electronAPI.getLogger('module').
- Extract magic values such as intervals, timeouts, status codes, and route names into constants when they repeat or carry meaning.
- Keep Ant Design Vue default CSS unless a local layout or readability need requires scoped customization.
- Keep project-wide browser UI rules such as scrollbar styling in one renderer-global stylesheet, imported once from the application entry and driven by light/dark theme variables.
- Derived filters and searches must preserve the source data order. A reduced search result must not be passed to persistence or reorder APIs as if it were the complete collection.
Verification Gate
All commands run from frontend/.
The repo-wide lint and typecheck baseline is not clean. As of v5.4.0-beta.8, untouched dev reports roughly 4900 ESLint problems across ~77 hand-written files (overwhelmingly prettier/prettier indentation drift) and 92 vue-tsc errors across 7 files (~84 of them in the two OkNte edit views). Do not treat a non-empty yarn lint or yarn typecheck output as evidence that you broke something, and do not try to clean the repo as a side effect of an unrelated task.
| Touched surface | Command | Passing criterion |
|---|
| Any business code | npx eslint <your changed files> | clean, exit 0 |
| Types, props/emits, API usage, generated-client consumption | yarn typecheck | no new error naming a file you touched |
A module with a sibling *.test.ts, or shared logic/styles under test | yarn test | fully green |
| Build, routing, or Electron entry | yarn build | succeeds |
| Documentation only | file existence, headings, sections, git status --short | — |
| UI | also follow mas-frontend-ui verification | — |
Rules that follow from the dirty baseline:
- Scope lint to the files you changed.
npx eslint <paths> exits 0 on a clean file and nonzero on a dirty one, so it is a real gate; yarn lint is not, because it cannot pass.
yarn typecheck has no scoping flag, so run it whole and grep the output for your own file paths. Compare against the baseline instead of expecting zero.
- Lint and typecheck are orthogonal.
OkNteUserEdit.vue is lint-clean with 41 type errors; scheduler-debug.ts is the reverse. Passing one says nothing about the other.
yarn test is the one gate that is green repo-wide. It must stay green — a failure there is always yours.
- If you touch a file that is already in the dirty baseline, leave the pre-existing problems alone and say so in your result. Fixing them is a separate, explicitly-requested task.
Prefer yarn typecheck over a full yarn build for type validation; it is much faster and covers the renderer via tsconfig.app.json.
If a command cannot run, state the exact command and the reason. Never claim "complete", "fixed", or "passed" without verification evidence, and never restate a pre-existing baseline failure as a result of your change.
Frontend Tests
Vitest runs with no config file and no DOM environment. There is no vitest.config.ts, no jsdom, and no @vue/test-utils. Tests execute in the default node environment and are colocated next to their subject as *.test.ts.
Three established patterns, in order of preference:
- Pure logic — extract the logic out of the
.vue file into a sibling .ts, then import and test it directly. views/scripts/scriptSearch.ts with scriptSearch.test.ts is the reference. This is the main reason to extract logic from a component: testability.
- Composables — test in node with
vi.mock() for boundaries. Mock @/api (the generated Service) and ant-design-vue (message) rather than reaching for a DOM. See composables/useEmulatorDeviceOptions.test.ts.
- Component structure —
readFileSync the .vue (or .css) source and assert on its text. Used to lock in constraints that have no runtime assertion point, such as overlay z-index, viewport-height clamps, and stylesheet imports. See views/scripts/components/ScriptCreateDialog.test.ts and styles/scrollbar.test.ts.
Do not introduce mount(), jsdom, happy-dom, or @vue/test-utils for a routine change; that is a project-wide testing-stack decision, not a task-level one. If a behavior genuinely cannot be covered by these three patterns, say so in your result instead of adding a test dependency.
Pattern 3 is how several mas-frontend-ui layout rules are actually enforced. When you change an overlay's z-index, a dialog's height clamp, or a global stylesheet import, expect a source-text test to assert on the exact string you edited, and update it in the same change.
Red Lines
| Temptation | Reality |
|---|
| "I can generate a fresh page faster." | Inspect and reuse local page, component, and composable patterns first. |
| "The API call is tiny, so direct fetch is fine." | Business API calls go through generated services and composables. |
| "Shared state is easiest to put in localStorage." | Shared reactive application state belongs in Pinia; localStorage is only the narrow fallback for durable, non-sensitive local preferences. |
| "Pinia should hold every frontend value." | Keep isolated short-lived state local, and keep persistent authoritative data in its backend or Electron configuration owner. |
| "I can tweak generated API files." | src/api is generated; regenerate through the project command instead. |
| "This UI-only change can ignore engineering rules." | UI tasks still obey module, state, API, and verification boundaries. |
"yarn lint is failing, so I broke the build." | ~4900 problems and 92 type errors pre-exist on clean dev. Scope lint to your own files and compare typecheck against the baseline. |
| "Lint passed, so the types are fine." | The two are orthogonal. Lint-clean files carry dozens of type errors in this repo. |
| "I'll fix the surrounding lint noise while I'm here." | Baseline cleanup is a separate, explicitly-requested task; it buries your real diff. |
| "I need jsdom to test this component." | Tests run in node with no DOM. Extract logic to a sibling .ts, or assert on source text. |
Final Response
For frontend tasks, report:
- Changed files.
- Verification commands and results.
- UI checks when applicable.
- Known risks or "no known residual risk".
State that no business code was changed when the task is documentation-only.