Files
Wand-Enhancer-luanslimadev/web-panel/CLAUDE.md
T
kitbyte a0b3968d33 refactor(web-panel): update architecture + cheat i18n
Reorganize the panel around product capabilities and integrate the
remote i18n feature from origin/master into the new structure.

- Restructure src into capability features (app, trainer, library,
  remote-session, appearance, shared); move the bridge to bridge/src.
- Add lingui-based UI localization and wrap remaining user-facing
  strings (Trans / msg macros); fix the 'END В·' mojibake.
- Port WeMod cheat-metadata i18n: capture the access token from the
  snapshot's renderer in the bridge and fetch localized trainer_meta
  in remote-session.i18n; guarantee snapshot sync on token failure.
- Wire vitest to the app's lingui/preact pipeline (mergeConfig).
2026-06-15 00:10:49 +03:00

11 KiB

Use these rules as defaults, not as a reason to add ceremonial folders or wrapper layers.

Core Principles

  • Organize code around product capabilities, not framework vocabulary.
  • Keep related UI, state, rules, and data access close until a real boundary justifies moving them apart.
  • Dependencies point from composition and UI toward stable rules and narrow capabilities.
  • Protect rendering code from business, state-management, and infrastructure complexity.
  • Keep one source of truth and derive everything else.
  • Apply KISS, YAGNI, and DRY together. Remove duplicated knowledge, not merely similar syntax.
  • Prefer explicit, readable flow over clever abstractions and hidden behavior.

Screaming Architecture

The repository structure and public APIs should reveal what the product does.

Prefer:

features/
  checkout/
  search/
  account-security/

Avoid making the application read primarily as:

components/
hooks/
services/
stores/
utils/

Technical folders are useful inside a capability, where their owner is clear. Generic top-level folders easily become dependency magnets with unclear ownership.

Names should use product language. Prefer useCheckoutSummary, reserveStock, and AccountSecurityPanel over useData, processItems, and GenericPanel.

Suggested Structure

Start with the smallest structure that makes ownership obvious:

src/
  app/                 startup, providers, router, global composition
  pages/               route-level composition
  features/
    <capability>/
      index.ts         optional public API
      ui/              optional rendering components
      model/           optional state, view models, decisions
      api/             optional external data access
      lib/             optional feature-local pure helpers
  domains/             optional shared product rules and types
  shared/
    ui/                domain-free visual primitives
    api/               generic transport/query infrastructure
    lib/               genuinely generic pure helpers

Folders are created when they contain a real responsibility. A small feature may be one cohesive file. Do not create empty layers in anticipation of future complexity.

Dependency Direction

  • app installs providers, constructs dependencies, and composes the application.
  • pages compose capabilities for a route. They do not own business rules or data protocols.
  • A feature owns one user-recognizable capability end to end.
  • Feature UI consumes its own model/view-model API, not raw infrastructure.
  • Shared domain code contains reusable product rules and stays independent of React and I/O.
  • shared contains only domain-free code. Product-specific code is not shared merely because two files use it.
  • Avoid feature-to-feature imports. Compose features in a page, promote truly shared rules to a domain module, or introduce a named workflow when coordination is the actual responsibility.
  • Cyclic imports are an architecture problem, not something to solve with a tooling workaround.

For a simple feature, direct ui -> model -> api dependencies are sufficient. Introduce ports, facades, dependency injection, or workflows only when they hide real complexity, enable important tests, or separate unstable infrastructure.

Make Composition Read Like The Product

Pages and other composition boundaries should use capability-level APIs.

Prefer:

<CheckoutSummary />
<PlaceOrderButton />

Over:

<Card>
  <Select options={paymentOptions} onChange={handlePaymentChange} />
  <Button onClick={handleSubmit}>Submit</Button>
</Card>

The second version makes the page understand checkout behavior and low-level UI configuration. That knowledge belongs to the checkout capability.

This does not mean wrapping every native element or design-system primitive. Semantic HTML and visual primitives are correct inside feature UI. Create a capability component when it hides product behavior or gives composition code a clearer product-level API.

Avoid "raw components" whose consumers must know internal options, state transitions, query shapes, or protocol details. Avoid generic configuration-driven components that combine unrelated product modes behind dozens of props.

UI Boundary

  • Components render data and translate DOM events into named user intents.
  • Keep business decisions, data mapping, persistence, protocol handling, and multi-step async flows outside rendering components.
  • UI receives render-ready values. It should not reconstruct domain meaning from raw DTOs.
  • Prefer intent props and commands such as onApprove, renameProject, or submitOrder over generic onChange, setState, or patch APIs at capability boundaries.
  • Keep ephemeral visual state local: focus, hover, open/closed, and uncommitted input usually belong in the component.
  • Split components by responsibility and API clarity, not by arbitrary line limits.
  • Prefer slots and composition over components with many layout modes and boolean props.
  • Use semantic HTML and preserve accessibility behavior.

A view-model hook is useful when it protects UI from state shape, async coordination, or business decisions. Do not create a pass-through hook that only renames one value to satisfy a diagram.

State Ownership

Choose the smallest correct owner:

State Preferred owner
Ephemeral visual state local component state
Uncommitted form state the form or feature
URL/shareable navigation state the router/URL
Remote server resource and cache a query/cache layer
Shared capability state that feature's model/store
Cross-capability process a named workflow or app-level model
  • A store is not a bucket for every value used by several components.
  • Split state by capability and lifecycle, not by data type.
  • Expose narrow selectors, hooks, or commands. Do not expose a complete mutable store to all UI.
  • Store transitions should express user or domain intent, not generic object mutation.
  • Derive values instead of storing synchronized copies.
  • Do not use effects to keep two pieces of application state synchronized.
  • React Context is suitable for dependency injection or stable scoped state. Avoid one broad app context whose every update rerenders unrelated consumers.

State-library choice is an implementation detail. Architecture should survive replacing it without rewriting pages and rendering components.

Effects And Async Work

  • Use effects to synchronize with external systems, not to calculate render data or handle user events.
  • Start event-driven work from the event or model command that owns it.
  • Every subscription, timer, listener, or in-flight operation must have a clear owner and cleanup path.
  • The owning feature/model defines pending, success, empty, error, retry, and cancellation semantics.
  • Prevent stale async results and race conditions where users can trigger overlapping work.
  • Do not hide failures with broad catch blocks or silently convert errors into empty data.

Data And Infrastructure

  • Treat network responses, storage, URL input, files, and third-party SDK output as untrusted.
  • Validate and normalize data at the boundary where it enters the application.
  • Map transport DTOs and external errors into product-oriented values before they reach UI.
  • Keep raw fetch, storage APIs, SDK calls, and protocol details out of rendering components.
  • Keep a feature-specific API adapter inside the feature until it has a real shared consumer.
  • Introduce a client, repository, gateway, service, or facade only when its responsibility is distinct and useful.
  • Avoid wrapper chains that only forward calls. One clear adapter is better than Client -> Service -> Facade without separate responsibilities.
  • Inject infrastructure when tests, multiple implementations, lifecycle, or unstable external APIs justify it. Do not introduce dependency injection for every pure helper.

Component And Hook APIs

  • Component and hook APIs describe product intent, not internal implementation.
  • Avoid boolean prop combinations that create unclear or invalid modes. Prefer explicit variants or separate components.
  • Avoid passing raw query results, stores, SDK clients, or large configuration objects through component trees.
  • Keep public props small and cohesive. A component that needs unrelated groups of props likely owns too many responsibilities.
  • Custom hooks encapsulate React state, lifecycle, or reusable reactive behavior. Pure calculations remain plain functions.
  • Do not use useEffect, useMemo, useCallback, or memo by habit. Use them for correctness or measured performance needs.
  • Do not duplicate server or domain state into component state merely to make it editable. Create an explicit draft only when the UX requires commit/cancel semantics.

Public Boundaries

  • Export the smallest useful public surface of a feature.
  • Consumers should use a feature's public components, hooks, commands, and types, not deep internal paths.
  • Keep implementation-only state, DTOs, adapters, and helpers private.
  • Do not create barrel files everywhere. Use a public entry point only where a real boundary exists.
  • A reusable abstraction should have a clear owner and at least one current reason to exist.
  • Avoid generic core, common, helpers, services, or utils modules that collect unrelated responsibilities.

Growing The Architecture

Start local and promote code only after pressure appears:

  • A second consumer may justify shared domain code, but similar code is not automatically the same knowledge.
  • Repeated external integration logic may justify a shared adapter.
  • A process coordinating several capabilities may justify a named workflow.
  • A large feature may split into smaller capabilities when they have distinct responsibilities and lifecycles.
  • Separate packages are useful when an enforceable boundary, independent reuse, or independent lifecycle outweighs their maintenance cost.

Do not begin a small application with every possible layer, package, provider, repository, facade, and design pattern. Strong architecture makes growth cheaper; it does not predict every future requirement.

Testing

  • Test product behavior and public contracts, not implementation trivia.
  • Test pure rules with unit tests.
  • Test feature models and async transitions without rendering where practical.
  • Test components through accessible user behavior.
  • Test infrastructure mapping and validation at external boundaries.
  • Keep end-to-end tests for critical user journeys.
  • Mock external systems and unstable boundaries, not every internal function.
  • Add tests proportional to risk, especially for validation, permissions, races, retries, cancellation, and regressions.

Review Checklist

Before finishing a change, ask:

  • Does the file location make its owner obvious?
  • Does composition code read in product language?
  • Is UI protected from raw state, DTOs, infrastructure, and business decisions?
  • Is there one source of truth?
  • Are effects only synchronizing external systems?
  • Is new shared code genuinely domain-free or genuinely shared?
  • Does every abstraction remove current complexity?
  • Can important behavior be tested without rendering the whole app?
  • Did the change preserve accessibility, error handling, and cleanup?
  • Is this the least code that clearly solves the current problem?