# frontend/AGENTS.md

## Scope

These rules apply to work under `frontend/`.

This is a Vite + React + TypeScript app using TanStack Query, Tailwind CSS,
Framer Motion, Radix UI-style components, MDX, and Zustand.

## Commands

Use only scripts that exist in `package.json`:

```sh
npm run dev
npm run build
npm run lint
npm run preview
```

`npm run build` runs `tsc -b && vite build`, and `postbuild` runs
`node scripts/generate-sitemap.js`.

There is currently no `test` script in `package.json`. Do not run or report
`npm test` unless a test script is added.

After frontend changes, run:

```sh
npm run build
npm run lint
```

If either command cannot be run or fails, report the exact command and failure.

Do not create, modify, or run tests unless the user explicitly asks for test
work. When the user asks for tests, keep working and rerun them until they
pass or the remaining failure is clearly blocked.

## TypeScript

- TypeScript is strict. `tsconfig.app.json` enables `strict`,
  `noUnusedLocals`, `noUnusedParameters`, `erasableSyntaxOnly`,
  `noFallthroughCasesInSwitch`, and `noUncheckedSideEffectImports`.
- Keep types explicit at module boundaries, API helpers, and exported utilities.
- Use `import type` for type-only imports.
- Prefer existing shared types from `src/types.ts` before adding local duplicate types.
- Preserve the repository's existing spacing style in TypeScript, including
  GNU-style spacing before call parentheses where it is already used.
- Prefer single quotes for strings unless interpolation or escaping makes double quotes better.
- Never write a TypeScript or TSX line longer than 99 characters.
- Aim to keep TypeScript and TSX lines within 79 characters where practical.
- Use 4-space logical indentation in TypeScript and TSX.
- For arrays, never put whitespace or a line break immediately before `]`.
- Keep the first element on the same line as `[` by default.
- If an array would exceed the line limit, break after `[` and indent
  elements by 4 spaces.
- In TypeScript and TSX only, replace every leading run of 8 spaces with a tab
  to reduce bytes.
- Treat one leading tab as exactly equivalent to 8 leading spaces.
- Use tabs only for leading indentation. Never replace spaces that occur after
  a non-space character on the same line.

## React

- Use function components.
- Existing page components commonly export an anonymous function satisfying
  `FC`; match nearby file style when editing.
- React hooks must be called unconditionally and at the top level of components or custom hooks.
- Gate editing and other privileged controls with shared permission helpers
  such as `canEditContent`, instead of showing controls and relying only on a
  later API failure.
- Keep page-level components under `src/pages`.
- Keep shared and feature components under `src/components`.
- Use `react-router-dom` route params and navigation patterns already present in `src/App.tsx`.
- Encode URL path-segment values with `encodeURIComponent`.

## TanStack Query

- Use `@tanstack/react-query` for server state.
- Query keys should come from `src/lib/queryKeys.ts`; add key builders there
  instead of using ad hoc arrays in components.
- Fetch functions should live in domain helpers under `src/lib`, such as
  `posts.ts`, `tags.ts`, or `wiki.ts`.
- Use `useQueryClient().invalidateQueries` with the shared root keys when
  mutations affect cached lists or detail views.
- The app-wide `QueryClient` is configured in `src/main.tsx`; do not create
  additional clients in feature code.

## API calls

- Use `src/lib/api.ts` for HTTP calls.
- The API wrapper attaches `X-Transfer-Code` from `localStorage` and converts
  non-blob responses to camelCase.
- Send Rails snake_case params and request body keys where the backend expects them.
- Do not bypass the API wrapper unless there is a specific reason, such as a
  third-party request outside the Rails API.
- For blob responses, pass `responseType: 'blob'` so the wrapper does not camelCase the body.

## Imports and aliases

- The `@` alias points to `frontend/src`.
- Prefer `@/...` imports for app code instead of long relative paths.
- Keep type imports separate with `import type`.
- Match existing import grouping: external packages, app modules, then type imports.

## Tailwind and UI

- Tailwind scans `src/**/*.{html,js,ts,jsx,tsx,mdx}`.
- Use `cn` from `src/lib/utils.ts` for conditional class names and class merging.
- Reuse components from `src/components/common`, `src/components/layout`, and
  `src/components/ui` before adding new primitives.
- Keep Tailwind classes consistent with nearby components.
- Prefer restrained, content-first UI chrome: avoid adding card backgrounds,
  heavy borders, or nested panel decoration unless the surrounding screen
  already uses them.
- Keep operational screens dense and direct; trim explanatory copy and use
  short Japanese labels that fit the control.
- Preserve existing Japanese tone and orthography in nearby UI text, including
  old-kana wording where the file already uses it.
- When adding dynamic tag colour classes, update `tailwind.config.js` safelist
  if the class cannot be statically detected.
- Do not introduce new UI libraries or production dependencies without approval.

## TypeScript and TSX formatting

- The delimiter-placement and line-breaking rules in this section apply to
  both plain TypeScript `.ts` and TSX `.tsx`, unless a bullet explicitly says
  it is JSX- or React-specific.
- Preserve compact TSX expression shapes such as inline ternary branches and
  closing `</div>)` forms when nearby code uses them.
- Treat TypeScript and TSX formatting rules as hard constraints, not
  preferences. Before finishing a TypeScript or TSX edit, inspect the edited
  hunks for closing `)`, `]`, and `}` placement and fix violations instead of
  relying on formatter defaults.
- After every TypeScript or TSX edit, perform a style-only self-review of the
  edited hunks before running verification or reporting completion. The task is
  not complete while any edited TypeScript or TSX hunk violates these local
  formatting rules.
- The TypeScript/TSX self-review must classify every edited leading or trailing
  `)`, `]`, and `}` by syntax role before deciding whether it is valid. Do not
  apply a rule by glyph alone. A closing `)` for a function parameter list is
  different from a closing `)` for a function call. A closing `}` for a block
  is different from a closing `}` for an object, type literal, import list, or
  destructuring pattern.
- The TSX-specific self-review must confirm there are no common Prettier-style
  React component declarations with a multi-line destructured parameter.
- The TypeScript/TSX self-review must confirm multi-line function declaration
  parameter `)` placement follows the detailed parameter-list rules below.
- The TypeScript/TSX self-review must confirm call-expression `)` is never at
  the beginning of a line.
- The TypeScript/TSX self-review must confirm
  object/type/import/destructuring `}` is not at the beginning of a line.
- The TypeScript/TSX self-review must confirm multi-line
  function/lambda/callback/block `}` is on its own line and never at the end
  of the previous line.
- The TypeScript/TSX self-review must confirm array `]` is not at the
  beginning of a line.
- The TSX-specific self-review must confirm JSX closing markers and closing
  parentheses keep the surrounding compact style.
- The TypeScript/TSX self-review must confirm leading indentation follows
  4-space logical indentation with tabs only as leading 8-space compression.
- For long Tailwind `className` strings, wrap across lines only when needed.
- Keep continuation indentation aligned with the 4-space logical indentation
  rule, using tabs only as leading 8-space compression.
- In TypeScript and TSX function declarations, including `const` arrow
  function declarations, classify the parameter list before placing the closing
  `)`.
- If the parameter list itself is given its own multi-line block after the
  function's opening `(`, put the closing parameter `)` at the beginning of its
  own line before the return type or `=>`.
- If the only line break is inside a parameter's inline type, object type, or
  destructuring shape, and the parameter list itself is not split as a separate
  block, keep the closing parameter `)` on the same line as that parameter's
  final `}`. In this case, moving `)` to a new line is wrong.
- Do not write React component declarations in the common Prettier form
  `const Component = ({ ... }) => (` when the destructured parameter spans
  multiple lines. Use the project form with the opening `(` after `=`, the
  destructured object as the argument, and the closing parameter `)` on its own
  line before `=>`.
- In TypeScript and TSX, never place a closing parenthesis at the beginning of
  a line except for a multi-line function declaration parameter list.
- Never place a closing square bracket at the beginning of a line.
- For object literals, type literals, import/export named bindings, destructuring
  patterns, and other associative-array-style braces, do not place the closing
  brace at the beginning of a line. Keep `}` on the same line as the final
  property, binding, or specifier unless that would violate the line limit.
  Function, lambda, callback, and block closing braces are exempt and should
  stay on their own line when that fits the local style. When a function,
  lambda, callback, or block body spans multiple lines, do not put its closing
  `}` at the end of the previous line.
- For arrays and tuple-like lists, do not place the closing `]` at the
  beginning of a line. Keep `]` on the same line as the final element unless
  that would violate the line limit.
- For function and method calls, do not place the closing `)` at the beginning
  of a line. The only TypeScript/TSX exception is the closing parameter `)` of a
  multi-line function declaration.
- Do not add braces around `if`, `else`, or `for` bodies when the body is a
  single physical line.
- Always add braces around `if`, `else`, or `for` bodies when the body spans
  two or more physical lines, even if it is one statement.
- Use correct British English spelling for new identifiers, filenames,
  component names, helper names, comments, and developer-facing prose unless
  editing an already established American-English API that must keep its
  existing spelling for compatibility.
- Prefer British English spellings such as `behaviour`, `colour`, `realise`,
  `theatre`, `centre`, `favourite`, `optimise`, and `catalogue`.
- Avoid American or Canadian spellings such as `behavior`, `color`, `realize`,
  `theater`, `center`, `favorite`, `optimize`, and `catalog`.
- Even when an external library or API uses the wrong spelling, prefer to
  correct it at the local boundary and use proper British English names in this
  frontend codebase. For example, prefer
  `import { color: colour } from '@external-lib'` over spreading `color`
  through local code.
- Apply the same boundary correction to object destructuring, wrapper helpers,
  adapter layers, and local variable names when doing so does not break the
  external contract.
- In this frontend, prefer names such as `BehaviourSettingsSection.tsx`, not
  `BehaviorSettingsSection.tsx`.
- Avoid reformatting unrelated JSX.

### Delimiter decision table

Use this table before accepting any edited TypeScript or TSX hunk. The table is
more authoritative than formatter habit. Unless a subsection explicitly
mentions JSX, it applies equally to `.ts` and `.tsx`.

#### Import and export named bindings

Bad:

```ts
import {
    Button,
    Card,
} from '@/components/ui'
```

Good:

```ts
import { Button,
	 Card } from '@/components/ui'
```

Also acceptable when short:

```ts
import { Button, Card } from '@/components/ui'
```

Rule: named-binding `}` is associative-array-style syntax. It must not be alone
at the beginning of a line. Prefer keeping `{` with the first binding and `}`
with the final binding when this fits the line limit.

#### Type literals

Bad:

```ts
type Props = {
    open: boolean
    onOpenChange: (open: boolean) => void
}
```

Good:

```ts
type Props = {
    open: boolean
    onOpenChange: (open: boolean) => void }
```

Rule: type-literal `}` is not a block close. It must stay on the same line as
the final property unless that would break the hard line limit.

#### Object literals

Bad:

```ts
const value = {
    open,
    activeScope,
}
```

Good:

```ts
const value = {
    open,
    activeScope }
```

Bad:

```ts
const value = useMemo (() => ({
    open,
    activeScope,
}), [open, activeScope])
```

Good:

```ts
const value = useMemo (() => ({
    open,
    activeScope }), [open, activeScope])
```

Rule: object-literal `}` is associative-array-style syntax. It must not be on a
line by itself. Keep it with the final property, and keep call `)` off the
beginning of a line.

#### Destructuring parameters

Bad:

```tsx
const Component: FC<Props> = ({
    open,
    onOpenChange,
}) => {
  return null
}
```

Good:

```tsx
const Component: FC<Props> = (
    { open,
      onOpenChange },
) => {
  return null
}
```

Rule: when a React component or helper takes a destructured object parameter
that spans multiple lines, do not use the Prettier-style `= ({ ... }) =>`
shape. Put the function parameter list on its own lines. The destructuring `}`
stays with the final binding. The parameter-list `)` is then allowed and
required at the beginning of its own line.

#### Inline typed destructuring parameter

Good:

```ts
const RouteTransitionWrapper = ({ user, setUser }: {
    user:     User | null
    setUser:  Dispatch<SetStateAction<User | null>> }) => {
  return null
}
```

Bad:

```ts
const RouteTransitionWrapper = ({ user, setUser }: {
    user:     User | null
    setUser:  Dispatch<SetStateAction<User | null>>
}) => {
  return null
}
```

Rule: this is not a separately split parameter-list block. The line break is
inside the inline type. Keep the type-literal `}` and parameter-list `)` on the
same line as the final type property.

#### Multi-line normal parameter list

Bad:

```ts
const updateDraft = <Key extends keyof Settings,> (
    key: Key,
    value: Settings[Key]) => {
  return null
}
```

Good:

```ts
const updateDraft = <Key extends keyof Settings,> (
    key:    Key,
    value:  Settings[Key],
) => {
  return null
}
```

Rule: when the parameter list itself is split across multiple parameter lines,
the closing parameter `)` goes at the beginning of its own line before `=>` or
the return type.

#### Function and callback blocks

Bad:

```ts
const handleSave = () => { save ()
}
```

Bad:

```ts
const handleSave = () => {
  save () }
```

Good:

```ts
const handleSave = () => {
  save ()
}
```

Rule: block `}` closes executable code, not associative data. Multi-line
function, lambda, callback, `if`, `for`, `switch`, and similar block braces
belong on their own line.

#### Function and method calls

Bad:

```ts
const value = compute (
    first,
    second
)
```

Good:

```ts
const value = compute (
    first,
    second)
```

Rule: call-expression `)` must not be at the beginning of a line. The exception
for leading `)` applies only to function declaration parameter lists, never to
calls.

#### JSX closing markers

Bad:

```tsx
<Button
    type="button"
    onClick={handleSave}
>
  保存
</Button>
)
```

Good:

```tsx
<Button
    type="button"
    onClick={handleSave}>
  保存
</Button>)
```

Bad:

```tsx
<Input
    value={value}
    onChange={handleChange}
/>
```

Good:

```tsx
<Input
    value={value}
    onChange={handleChange}/>
```

Rule: keep `>` or `/>` with the final prop, and keep JSX closing parentheses in
the local compact form such as `</div>)`.

#### Arrays and tuples

Bad:

```ts
const items = [
    first,
    second,
]
```

Good:

```ts
const items = [
    first,
    second]
```

Good when short:

```ts
const items = [first, second]
```

Rule: array and tuple `]` must not be at the beginning of a line. Keep it with
the final element unless that would break the hard line limit.

#### Single-line braces

Good:

```ts
const next = { ...current, enabled: true }
const tag = { id, name }
```

Bad:

```tsx
<Component prop={ value }/>
```

Good:

```tsx
<Component prop={value}/>
```

Rule: JavaScript object braces on one line get one inner space. JSX expression
braces do not get inner spaces.

#### Final TSX self-review checklist

Before reporting completion after a TypeScript or TSX edit, check the edited
hunks line by line:

1. No import/export/type/object/destructuring `}` appears alone at the beginning
   of a line.
2. No call-expression `)` appears at the beginning of a line.
3. Any leading `)` is definitely closing a function declaration parameter list,
   not a call.
4. Any parameter-list `)` at the end of a line is valid because the parameter
   list itself was not split, only an inline type or destructuring shape was.
5. No array or tuple `]` appears at the beginning of a line.
6. Multi-line executable block `}` is on its own line.
7. JSX `>` and `/>` stay with the final prop unless nearby code proves
   otherwise.
8. JSX closing parentheses keep the compact local style.
9. Leading indentation is 4-space logical indentation with tabs used only as
   leading 8-space compression.
10. No line has trailing whitespace.

## Lint and build constraints

- ESLint uses `@eslint/js`, `typescript-eslint`, `eslint-plugin-react-hooks`,
  and `eslint-plugin-react-refresh`.
- The hooks rules are enforced; fix hook ordering instead of disabling the rule.
- `react-refresh/only-export-components` is enabled as a warning with `allowConstantExport`.
- Build failures from unused locals or unused parameters are TypeScript
  errors, not lint-only issues.

## Files to avoid in routine work

- Do not edit `dist/` output directly.
- Do not inspect or modify `node_modules/` unless explicitly needed.
- Keep generated build artifacts out of source changes unless the user asks for them.
