Files
loyaly-merchant/AGENTS.md
2026-08-06 20:59:19 +05:30

141 lines
9.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!-- BEGIN:nextjs-agent-rules -->
# This is NOT the Next.js you know
This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` before writing any code. Heed deprecation notices.
<!-- END:nextjs-agent-rules -->
<!-- ASTRYX:START -->
Astryx v0.2.0 · 154 components
CLI: run every command as `npx astryx <cmd>` (shown below as `astryx ...`).
SETUP (once, in your app entry e.g. main.tsx) — without these, components render unstyled:
import "@astryxdesign/core/reset.css";
import "@astryxdesign/core/astryx.css";
WORKFLOW — discover, don't guess. Before writing UI:
1. `astryx build "<idea>"` — START HERE: returns a kit (closest [page] + [block]s + [component]s). No args = full playbook.
2. `astryx template <name> [--skeleton]` — scaffold the [page]/[block]s it named, or study their layout. Templates are reference code.
3. `astryx component <Name>` — props + examples for every component you use.
RULES:
- No <div> — components do all layout/spacing. Full page → AppShell; sidebar nav → SideNav.
- Frame first: pick the shell (AppShell / Layout+LayoutPanel) and budget regions in px BEFORE writing content (`astryx docs layout`).
- Dense data = rows (Table, List/Item) edge-to-edge — never Card-wrapped list items. Card = dashboard widgets, galleries, settings groups only.
- Status → StatusDot/Token; Badge only for counts and enumerated states, never decoration.
- Custom styling: component props first; else Tailwind utilities backed by tokens (bg-surface, text-primary, rounded-lg) via tailwind-theme.css. No raw hex/px.
- Tokens for every value (`astryx docs tokens`). Brand/accent via `astryx theme` — never override --color-* in :root.
- SELF-CHECK before you finish: re-read the file and replace any style={{…}}, raw <div>/<span> layout, imported .css/@apply, or hardcoded/arbitrary value (e.g. bg-[#fff], p-[13px]) with the component or a token-backed utility. If unsure a component/prop exists, run `astryx component <Name>` / `astryx search "<thing>"`; don't hand-roll CSS.
MORE CLI:
search "<query>" find any component / hook / doc / template / block
component --list 154 components by category
template --list page + block recipes
docs <topic> color, elevation, icons, illustrations, internationalization, layout, migration, motion, principles, shape, spacing, styling, theme, tokens, typography
swizzle <Name> eject component source for deep customization
upgrade --apply run after any @astryxdesign/core bump
<!-- ASTRYX:END -->
<!-- LOYALY:START -->
# Loyaly Merchant Dashboard — project rules
## Toolchain
- **Node 22 is required** (`.nvmrc` → 22.23.1). The astryx CLI hard-fails on Node 20.
`nvm use` before running any `npx astryx` command or `npm run theme:build`.
- Dev server runs on **port 3100** (`lytsup-site` owns 3000).
## Theme
- All design values live in `src/theme/loyalyTheme.ts`. Edit that, then run
`npm run theme:build`, then **commit the generated** `src/theme/loyaly.{css,js,d.ts,variants.d.ts}`.
- `theme:build` is deliberately NOT wired to `prebuild` — it would break `npm run build` on Node 20.
- Import the theme from **`@/theme`**, never `@/theme/loyaly` directly. `@/theme` re-attaches
the Lucide icon registry, which `astryx theme build` cannot serialise (icons are React
components). Skipping it silently degrades every `<Icon>` to Astryx's default glyph set.
- `globals.css` imports `../theme/loyaly.css`, NOT `@astryxdesign/theme-neutral/theme.css`.
Astryx theme CSS is `@scope`-gated on the theme *name*, so neutral's rules match nothing
under `[data-astryx-theme="loyaly"]`.
## The monochrome rule
Gray is the accent. The **only** colour permitted is semantic: success, warning, error.
- Astryx's categorical hues (blue/cyan/teal/purple/pink/orange) are aliased to gray in
`aliasHueToGray()`. green/red/yellow deliberately keep hue — Banner's semantic statuses
resolve *through* those hue tokens.
- Some neutral-theme component styles hardcode colour instead of reading a token
(`badge variant:info`, `progressbar variant:accent`). Those need an explicit `components`
override; `aliasHueToGray()` cannot reach them.
- `/tokens` (dev-only route) is the audit page. After any theme change, load it and run the
chromatic sweep in the browser console — it should report **0** non-semantic colour nodes.
## Known upstream issues (Astryx 0.2.0)
- **`useEntryAnimation` breaks SSR hydration.** Its source assumes `'use client'` modules never
run on the server; in the App Router they do. Any `FieldStatus` (i.e. `TextInput`/`TextArea`
`status={...}`) present at *initial paint* renders without the slide-down class on the server
and with it on the client → an unpatchable class mismatch. Setting status on blur/submit
(the normal path) is unaffected. Don't server-render a field that already has a status.
- Component keys in `defineTheme({components})` are inconsistently cased: `text-input` but
`progressbar`. The CLI warns on unknown keys, but that warning is **not** reliable in
either direction:
- A *misspelled* key (`textinput`) is dropped silently — the CSS is never emitted.
- A key the CLI doesn't know but Astryx *does* use (`table-cell`, `table-header-cell`)
warns yet still emits correctly.
So never trust the warning alone. After any `components` change, grep the generated
`src/theme/loyaly.css` for the rule, then confirm the computed style in the browser.
Valid targets are whatever `themeProps('...')` is called with in `dist/<Component>/*.js`.
- `StyleOverrides` supports structural pseudo-classes, not just interaction ones —
`':first-child'` emits correctly (used for the table first-column lead-in).
## Deployment — never ship `.next/` wholesale
This filled the production disk and took the server down once. A working tree's `.next/`
reaches **2+ GB**, but almost none of it is runtime state:
| path | size (typical) | ships? |
|---|---|---|
| `.next/dev` | 1.8 GB | **no** — Turbopack dev-server cache from `npm run dev` |
| `.next/cache` | 120160 MB | **no** — incremental build cache, build host only |
| `.next/server`, `types`, `trace` | ~24 MB | **no** — inputs to the standalone trace, not read at runtime |
| `.next/standalone` | 54 MB | **yes** — server.js + traced node_modules |
| `.next/static` | 3 MB | **yes** — hashed client assets (nginx serves at `/_next/static/`) |
| `public` | 344 KB | **yes** |
Those last three are the entire payload: **~55 MB unpacked, 18 MB gzipped.**
- Non-Docker deploy: `npm run bundle``dist/loyaly-mer-login.tar.gz`. Never `scp -r .next`.
- Docker: `.dockerignore` excludes `node_modules` and `.next`. **Do not delete it** — without
it `COPY . .` pushes the host's 2 GB `.next` and 639 MB `node_modules` into the build
context, including darwin-arm64 sharp binaries that are wrong for Alpine.
- `npm run clean` drops `.next`, `dist` and the tsbuildinfo when the tree gets heavy.
**`CI_BUILD=1` in container builds.** Next 16.3 enables `turbopackFileSystemCacheForBuild`
by default; it writes 117 MB to `.next/cache` that only pays off when that directory is
restored between builds. Docker/Dokploy starts from a clean layer every time, so it is
written and never read. The Dockerfile sets `CI_BUILD=1`, which flips the flag off in
`next.config.ts`: **~20% less build CPU** (32s vs 4043s measured) and `.next` drops
202 MB → 86 MB. Leave it unset locally — repeat `npm run build` there does reuse the cache.
It changes no output: the deployable payload is 58 MB either way.
## Conventions
- Follow the Astryx rules above: no raw `<div>` for layout, no hardcoded hex/px, component
props first then token-backed Tailwind utilities.
- `src/lib/icons.ts` is the single icon map. Only the 26 semantic names resolve via
`<Icon icon="search"/>`; everything else is `<Icon icon={ICONS.stores}/>`. Never import a
lucide icon directly into a feature file.
## Settings spacing contract
Every Settings screen renders inside `src/features/settings/SettingsPage.tsx`. Do not add
page padding in an individual settings page — change the container instead.
| | value | why |
|---|---|---|
| Horizontal | 32px (`paddingInline={8}`) | clears the sub-nav and the Loyaly AI rail |
| Top | 32px (`paddingBlock={8}`) | title never touches the header |
| Bottom | 48px (`className="pb-12"`) | last card is never flush to the fold |
| Header → body | 32px (`gap={8}`) | title block reads as its own layer |
| Between body blocks | 24px (`gap={6}`) | cards, tables, toolbars |
`SpacingStep` stops at 10 (40px) and Stack has no `paddingBlockEnd`, which is why the 48px
bottom goes through the Tailwind bridge — `pb-12` still resolves to `--spacing-12`, so no
raw pixel value enters the codebase. It sits in the `utilities` layer, which the cascade
puts after `astryx-base`, so it wins over `paddingBlock` without `!important`.
Card padding (24px) and table cell padding (12px, 20px first-column lead-in) are set once
in `loyalyTheme.ts` so every module agrees — not per page.
<!-- LOYALY:END -->