ファイル
btrc-hub/frontend/AGENTS.md
T
2026-07-18 02:29:55 +09:00

31 KiB

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:

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:

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 2-space block indentation in TypeScript and TSX.
  • Use 4-space continuation indentation for wrapped expressions, arguments, ternary branches, method chains, object pairs, arrays, and JSX attributes.
  • Treat the user's PostImportSourcePage.tsx and PostImportReviewPage.tsx formatting as the local reference shape.
  • 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, use tabs for leading 8-column compression only.
  • A tab does not represent one indentation level.
  • Determine visible indentation with 2-space block indentation and 4-space continuation indentation first, then compress only complete leading runs of 8 spaces into tabs.
  • 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.
  • Keep residual leading 2, 4, or 6 spaces after any tab compression.
  • Examples: 2 columns = 2 spaces, 4 columns = 4 spaces, 6 columns = 6 spaces, 8 columns = 1 tab, 10 columns = 1 tab + 2 spaces, 12 columns = 1 tab + 4 spaces.

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.

Dialogues

  • Dialogue work follows the shared-frontend reuse rules below.

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.
  • Do not mix runtime values and type specifiers in one named import declaration.
  • Do not write import { value, type TypeName } from ....
  • Keep short value imports from one module on one line when they fit within 99 characters.
  • Order imports as four groups with a blank line between groups: external value imports, @/... value imports, external type imports, @/... 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.
  • In JavaScript, JSX, TypeScript, and TSX, use cn from @/lib/utils whenever className combines multiple values, conditional classes, or a caller-provided className prop.
  • Do not construct className with template literals, ${ ... }, string concatenation, arrays joined with spaces, or feature-local class-merging helpers.
  • A static className="..." containing only fixed classes does not need cn.
  • 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.
  • Do not add user-facing copy, helper text, descriptions, notes, tooltips, placeholders, empty-state messages, loading messages, or explanatory text unless the user explicitly specified the wording.
  • When new user-facing wording appears necessary, ask the user for the exact wording and placement before implementing it.
  • Do not invent replacement copy when removing unrequested wording.
  • Do not use タグなし as user-facing copy for an empty tag state. When the tag state is empty, show no copy. If actual data contains the tag name タグなし, treat it as ordinary data and display it normally.
  • 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.
  • Block bodies for components, functions, callbacks, if, try, catch, finally, loops, and JSX nesting use 2 spaces per level.
  • Put the opening brace of try, catch, and finally blocks on the next line at the same indentation as the keyword.
  • Do not indent the opening { one level deeper than try, catch, or finally.
  • Indent the block body 2 spaces deeper than the keyword and opening brace.
  • Put the closing } on its own line at the same indentation as the keyword.
  • Do not write try {, catch {, or finally {.
  • Wrapped expressions, arguments, ternary branches, method chains, and object pairs use 4-space continuation indentation relative to the owning expression. Do not confuse this with 2-space block indentation.
  • Tabs are leading 8-column compression only. They do not represent one nesting level. Decide visible indentation first, then compress only complete leading runs of 8 spaces into tabs.
  • Do not add braces around a single-line if body merely for formatting.
  • Use braces for multi-line if, else, and loop bodies.
  • Multi-stage ternary expressions must use explicit parentheses for each condition group and nested branch. Do not rely on indentation alone to show ? / : pairing.
  • Keep short inline props types local when they remain readable and within the line limit; do not mechanically extract a named type with no reuse benefit.
  • In JavaScript, JSX, TypeScript, and TSX, never use _1, _2, or similar Ruby-style numbered parameter names. Reserve numbered parameters for Ruby. Use a meaningful callback parameter name such as row, item, value, entry, or result.
  • In multi-line object literals, keep the opening { with the first pair when the line length allows it; do not mechanically explode short objects into Prettier-style vertical blocks.
  • Method chains should align as a continuation under the receiver expression; do not indent chains more deeply than the normal continuation depth.
  • PostImportSourcePage.tsx and PostImportReviewPage.tsx are the current canonical examples for block indentation, continuation indentation, import grouping, ternary grouping, method-chain placement, and local inline props types.
  • 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 block indentation uses 2 spaces per level, wrapped continuations use the repository's 4-space continuation alignment, and complete leading runs of 8 spaces may be compressed to tabs.
  • For long Tailwind className strings, wrap across lines only when needed.
  • Keep continuation indentation aligned with the repository's 4-space continuation rule while keeping block indentation at 2 spaces.
  • Keep short value imports from one module on one line when they fit within 99 characters.
  • In TypeScript and TSX function declarations, including const arrow function declarations, classify the parameter list before placing the closing ).
  • Block indentation example:
const Component = () => {
  const value = loadValue ()

  useEffect (() => {
    if (value != null)
      useValue (value)
  }, [value])
}
  • try / catch / finally brace placement example:
try
{
  doWork ()
}
catch
{
  recover ()
}
finally
{
  cleanUp ()
}
  • Continuation indentation example:
const editingRow =
    Number.isFinite (editingSourceRow)
    ? rows.find (row => row.sourceRow === editingSourceRow) ?? null
    : null
  • Import grouping example:
import { useNavigate, useParams, useSearchParams } from 'react-router-dom'

import { loadPostImportSession } from '@/lib/postImportSession'

import type { FC } from 'react'

import type { PostImportRow } from '@/lib/postImportSession'
  • Inline props type example:
const Footer = (
    { loading,
      onSubmit }: { loading:  boolean
		    onSubmit: () => void },
) => null
  • Ternary grouping example:
const rows =
    repairMode === 'failed'
    ? (
	[...source].sort ((a, b) => {
	  const aFailed = a.failed ? 0 : 1
	  const bFailed = b.failed ? 0 : 1
	  return aFailed - bFailed
	}))
    : source
  • 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.

Shared frontend systems

Before creating a new component, hook, helper, store, context, or other frontend abstraction, search at least:

  • src/components/common
  • src/components/layout
  • src/components/ui
  • src/components/dialogues
  • src/lib
  • src/lib/dialogues
  • src/stores
  • src/types.ts

Also inspect the existing pages and components in the same feature.

Search by responsibility, not by filename alone. Check display, interaction, state, communication, validation, and permission behaviour before deciding that an existing implementation is unsuitable.

Component placement and reuse order

When adding UI, use this order:

  1. reuse an existing feature component
  2. reuse an existing component from components/common
  3. reuse an existing layout component from components/layout
  4. use an existing primitive from components/ui through the established common API
  5. extend an existing component minimally
  6. add a feature-local component in the feature area
  7. add a new common component only when multiple features clearly share a stable visual contract

Do not place a one-screen component in a common directory merely because its name starts with Common.

Low-level primitives

Treat components/ui as low-level primitives. If a higher-level common API already exists for dialogues, toast, form validation, navigation, or similar behaviour, feature code must use that API instead of assembling primitives directly.

Examples of existing preferred entrypoints include:

  • dialogue: @/lib/dialogues/useDialogue
  • toast: the existing toast API
  • internal navigation: PrefetchLink
  • form errors: FieldError, FieldWarning, FormField
  • buttons: Button
  • conditional class merge: cn

Do not evade the rule with aliases or thin wrappers around the low-level primitive.

Dialogues

Feature-facing dialogue work must use @/lib/dialogues/useDialogue.

Reuse the existing common dialogue API and common dialogue component. Do not import @/components/ui/dialog directly in feature code to assemble bespoke dialogue shells, and do not evade this rule with aliases such as Dialog as Dialogue.

Do not reimplement overlay, portal, close button, header, footer, focus handling, Escape handling, outside-click handling, or confirmation flow in feature code.

Keep business-specific form content in feature code, and keep the visual and behavioural dialogue shell in common code.

Use British spelling Dialogue for project-defined dialogue identifiers. Keep exact third-party spellings only at the external boundary where compatibility requires them.

API calls

Rails API calls must use src/lib/api.ts.

Do not create feature-local Axios instances, fetch wrappers, header injectors, camelCase converters, or generic error converters. If blob or other special transport behaviour is already supported by the common API, use the existing options instead of bypassing the wrapper.

Query keys, server state, and prefetch

Before adding query state, inspect:

  • src/lib/queryKeys.ts
  • existing domain helpers
  • existing prefetchers
  • the root query-key hierarchy
  • current mutation invalidation patterns
  • the app-wide QueryClient

Do not write ad hoc query-key arrays in feature code. Do not duplicate fetcher, prefetcher, or invalidation helpers for the same resource.

Domain helpers

For posts, tags, wiki, materials, and other domain work, inspect the existing helpers in src/lib/*.ts before adding logic to a page component.

Do not accumulate these in page components when an existing helper layer should own them:

  • API request construction
  • response-shape conversion
  • query-key construction
  • canonical URL generation
  • permission calculation
  • storage serialisation
  • domain-specific parsing

Keep purely local one-screen display shaping local when that is the clearest place for it.

Permission helpers

Use the existing permission helpers such as src/lib/users.ts when deciding editability, role checks, admin/member visibility, and similar UI behaviour.

Do not scatter user?.role, numeric role comparisons, or string comparisons through components. Frontend visibility control should be consistent even though backend authorization remains the final gate.

Validation errors

Before adding feature-local validation-error handling, inspect:

  • useValidationErrors
  • apiErrors
  • FieldError
  • FieldWarning
  • FormField
  • inputClass

Do not create a new generic hook, field-error state shape, or error-rendering component for a pattern the shared error stack already covers. Keep only genuinely feature-specific business errors local.

Forms and fields

Before creating a new input, textarea, date/time field, tag input, label, or error layout, inspect at least:

  • Form
  • FormField
  • FieldError
  • FieldWarning
  • DateTimeField
  • TagInput
  • TextArea
  • Label
  • Button

Do not create a same-function field component merely because the spacing or surface styling is slightly different. Prefer feature-level composition over bloated common-field option lists.

Navigation and prefetch

Use PrefetchLink and existing router helpers for internal navigation. Do not introduce feature-local <a>, window.location, or custom prefetch logic for internal routes. Keep path-segment encoding aligned with the existing rules.

State management

Before adding state, decide whether the source of truth should be:

  • component-local state
  • URL search params
  • TanStack Query server state
  • an existing Zustand store
  • an existing event bus
  • an existing storage helper

Do not create a new global store, context, or event bus for one screen when local state or an existing mechanism is enough. Do not create a second store for the same responsibility.

Storage and settings

When touching localStorage, sessionStorage, or user settings, inspect existing settings helpers, storage helpers, expiry handling, versioning, and sanitisers.

Do not reimplement per-component key naming, JSON parsing and serialisation, expiry, or schema checks when a shared helper already owns the pattern.

Hooks

Before creating a custom hook, search existing src/lib/use*.ts and src/lib/use*.tsx.

Hooks are for shared stateful behaviour or React lifecycle integration. Do not turn a pure function, one-off helper, or mere re-export shim into useFoo.

Stores, contexts, and event buses

Add a new store, context, or event bus only when the current mechanisms cannot express the requirement and there are multiple genuinely separate consumers.

Do not hold the same information redundantly across URL state, query cache, local component state, Zustand, and an event bus. Keep one source of truth.

Types

If a domain type already exists in src/types.ts or a domain helper, reuse it instead of redefining the same shape in a feature file.

Small local props and draft types may stay local. Do not create giant catch-all type files such as CommonTypes.ts.

Styling utilities

Use existing styling utilities such as cn and inputClass.

Do not add feature-local class-merge helpers, generic status-colour mappers, or responsive wrapper helpers when a shared utility already exists. Keep common tone names visual only; feature-specific state names stay in feature code.

Layout

Before adding page shells, padding rules, viewport-height handling, sidebar offsets, or footer offsets, inspect existing layout components such as MainArea, top navigation, sidebar, page title, and section-title patterns.

Do not create a second layout shell before checking whether the current layout can be reused or minimally extended.

  • Frontend のスマホ/PC表示境界は原則 md とする。
  • button stack、footer action、dialogue action は md 未満で縦並び、 md 以上で横並びとする。
  • 同じ画面内で smmd を混在させて中間 layout を作らない。
  • 明確に別の responsive 要件がある component だけを例外とする。

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:

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

Good:

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

Also acceptable when short:

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:

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

Good:

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:

const value = {
    open,
    activeScope,
}

Good:

const value = {
    open,
    activeScope }

Bad:

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

Good:

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:

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

Good:

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:

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

Bad:

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:

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

Good:

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:

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

Bad:

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

Good:

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:

const value = compute (
    first,
    second
)

Good:

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:

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

Good:

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

Bad:

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

Good:

<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:

const items = [
    first,
    second,
]

Good:

const items = [
    first,
    second]

Good when short:

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:

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

Bad:

<Component prop={ value }/>

Good:

<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 block indentation uses 2 spaces per level, wrapped continuations use the repository's 4-space continuation alignment, and complete leading runs of 8 spaces may be compressed to tabs.
  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.