{"owner":"ColorlibHQ","repo":"gentelella","hasSkills":true,"totalSkillsCount":3,"totalTokensCount":3946,"categories":["copilot-instructions","project-spec","cursor-rule"],"hasMcp":false,"mcpConfig":null,"found":[".github/copilot-instructions.md","docs/architecture.md",".cursor/rules/project.mdc"],"skills":{".github/copilot-instructions.md":"# GitHub Copilot Instructions — Gentelella v4\n\nAdmin dashboard template (`4.2.0`) by Colorlib. 58 server-rendered HTML pages in `production/`, built with **Vite 8** (Rolldown). **Vanilla ES2022**, no Bootstrap, no jQuery, no SPA framework. SCSS only. Heavyweight deps — **ECharts 6**, **DataTables.net 3**, **Leaflet 1.9** — are lazy-imported per page. Full reference: `CLAUDE.md`.\n\n## Hard rules\n\n- **Vanilla DOM only.** `querySelector`, `classList`, `addEventListener`. No jQuery, no SPA framework. v4's pitch is \"vanilla and small.\"\n- **Single entry**: `src/main-v4.js`. Page-specific modules are lazy-imported inside it, guarded by DOM presence. Don't add `<script>` tags per page.\n- **Pages auto-discover.** Drop `production/<slug>.html` and `discoverEntries()` in `vite.config.js` picks it up — never edit `rollupOptions.input`.\n- **Shell opt-in**: `<body data-shell=\"admin\" data-page=\"<key>\" data-breadcrumb=\"Home > …\">`. The Vite plugin inlines sidebar/topbar/footer at build/dev time (no FOUC).\n- **Breadcrumbs link themselves.** A segment matching a `NAV` item's text becomes a link (`Forms` → `form.html`; a parent resolves to its first child). Override with a pipe: `data-breadcrumb=\"Home > Projects|projects.html > Acme Redesign\"`. The last segment is the current page and never links; untargeted segments stay plain text.\n- **NAV is one constant** — `NAV` in `src/v4/shell-render.js`, 7 groups. `key` matches `data-page`. New icons go in the `ICONS` object in the same file.\n- **Overlays go through helpers**: `showModal()`/`showToast()`/`openMenu()`/`openPanel()` from `src/v4/{modal,toast,menus}.js`. Never hand-roll a backdrop, escape handler, or focus return.\n- **CSS custom properties for colors.** Tokens in `src/scss/v4/_tokens.scss` under `:root` and `[data-theme=\"dark\"]`. Charts read them via `getComputedStyle(document.documentElement).getPropertyValue('--…')` so dark-mode redraw is automatic.\n- **Lazy ECharts.** Match the modular import pattern in `src/v4/charts.js`. Don't `import * as echarts`.\n- **Subpath-safe URLs.** `import.meta.env.BASE_URL` in JS, `${base}` in the Vite plugin, relative paths in `production/*.html`. Never hard-code a leading `/`.\n- **Idempotent `init<Name>()` exports.** Every module in `src/v4/` has one. Safe to call when its root element is absent, safe to call twice.\n- **No `console.*` in shipped code.** Terser drops them in production; ESLint flags earlier.\n- **Service worker only in prod** (`import.meta.env.PROD` guard) — keeps HMR working in dev.\n\n## File layout\n\n- `src/main-v4.js` — entry; mounts shell + lazy-loads page modules\n- `src/scss/v4/` — 10 SCSS partials (`_tokens`, `_layout`, `_components`, `_widgets`, `_forms`, `_datatable`, `_pages`, `_apps`, `_auth`, `main`)\n- `src/v4/shell.js` — `mountShell()` runtime (sidebar accordion, theme toggle, mobile drawer)\n- `src/v4/shell-render.js` — `NAV` + `ICONS` + pure renderers (also imported by Vite plugin)\n- `src/v4/charts.js` — `initCharts()` + ECharts factories\n- `src/v4/tables.js` — `initTables()` + DataTables wrapper\n- `src/v4/command-palette.js` — ⌘K\n- `src/v4/{modal,toast,menus}.js` — overlay helpers\n- `src/v4/{inbox,kanban,calendar,settings,file-manager}.js` — page modules (lazy-loaded)\n- `src/v4/form-controls.js` — date range, multi-select, rich text\n- `production/` — 58 HTML entry pages, auto-discovered\n- `public/` — static assets copied verbatim to `dist/`\n- `types/gentelella.d.ts` — TypeScript declarations for the public JS surface\n- `scripts/new-page.mjs` — page scaffolder (`npm run new -- <slug>`)\n- `scripts/deploy-preview.sh` — R2 deploy with per-file cache headers\n\n## Anti-patterns\n\n- Don't add jQuery, Bootstrap, or a SPA framework.\n- Don't write Vite entry input lists by hand.\n- Don't add `<script>` tags to `production/*.html` for new modules — lazy-import in `src/main-v4.js`.\n- Don't bypass `mountShell()` to wire your own sidebar/topbar.\n- Don't `new bootstrap.Modal(...)` — there is no Bootstrap.\n- Don't hard-code `/` paths in JS/HTML.\n- Don't import all of ECharts — match `src/v4/charts.js`.\n- Don't use hex colors in components — use CSS custom properties.\n- Don't introduce PostCSS, Tailwind, or any pipeline alongside Vite.\n- Don't edit `dist/`, `node_modules/`, or `docs/screenshots/`.\n\n## Commands\n\n```bash\nnpm run dev                # Vite dev server on :9173 (set PORT to override)\nnpm run build              # Production build → dist/\nnpm run preview            # Serve dist/ on :9174\nnpm run lint               # ESLint over src/\nnpm run lint:fix\nnpm run format             # Prettier write\nnpm run new -- <slug>      # Scaffold a page (use --nav-group, --icon, --title …)\nnpm run screenshots        # Playwright capture (22 pages × light+dark)\nnpm run smoke              # Boot dev server, hit every page, assert 200\nnpm run analyze            # Build + open dist/stats.html\nnpm run deploy:preview     # Build + R2 sync with cache headers\n```\n\nBuild under a subpath: `BASE_PATH=/foo/ npm run build`.\n\n## When generating code\n\n- **New page** → use `npm run new -- <slug> --nav-group \"<Group>\"` rather than crafting the HTML by hand. If you do write by hand, copy the head/body pattern from `production/index.html` (specifically the `data-shell`/`data-page`/`data-breadcrumb` triplet and the `<script type=\"module\" src=\"/src/main-v4.js\">` tag).\n- **New chart** → add a `case` to the switch inside `initCharts()` in `src/v4/charts.js`. Read tokens via `getComputedStyle(document.documentElement).getPropertyValue('--…')`.\n- **New page module** → add the lazy-import block at the bottom of `src/main-v4.js`, then create `src/v4/<name>.js` exporting a single idempotent `init<Name>()`.\n- **New NAV entry** → append to the right group in `NAV` in `src/v4/shell-render.js`. `{ key, href, text, icon }`. `key` must match the target page's `data-page`.\n- **New icon** → append to `ICONS` in `src/v4/shell-render.js`. Inline SVG, `currentColor` stroke, 24×24 viewbox.\n- **New modal/toast** → import `showModal` / `showToast` from `src/v4/modal.js` / `src/v4/toast.js`. Don't render backdrops yourself.\n- **New SCSS** → add a partial under `src/scss/v4/` and `@use` it from `main.scss`. Variables live in `_tokens.scss`; new tokens get both a light and a dark value.\n","docs/architecture.md":"# Architecture\n\nThis page has moved to **<https://gentelella.colorlib.com/docs/architecture/>**.\n\nGentelella v4 documentation is now maintained at <https://gentelella.colorlib.com/docs/> as the single source of truth.\n",".cursor/rules/project.mdc":"---\ndescription: Gentelella v4 — project conventions, recipes, anti-patterns\nglobs:\n  - \"**/*.js\"\n  - \"**/*.scss\"\n  - \"**/*.html\"\n  - \"**/*.css\"\nalwaysApply: true\n---\n\n# Gentelella v4 (`4.2.0`)\n\nAdmin dashboard template by Colorlib. 58 server-rendered HTML pages in [production/](mdc:production), built with **Vite 8** (Rolldown). **Vanilla ES2022**, no Bootstrap, no jQuery, no SPA framework. SCSS only. Heavyweight deps (**ECharts 6**, **DataTables.net 3**, **Leaflet 1.9**) are lazy-imported per page.\n\nFull reference: [CLAUDE.md](mdc:CLAUDE.md). The rules below are the load-bearing ones — apply them by default.\n\n## Hard rules\n\n- **Vanilla DOM only.** `querySelector`, `classList`, `addEventListener`. Never add jQuery, Bootstrap JS, or an SPA framework.\n- **Single entry**: [src/main-v4.js](mdc:src/main-v4.js). Every page loads it. Page-specific modules are lazy-imported inside `main-v4.js` guarded by DOM presence — match that pattern, don't add new `<script>` tags per page.\n- **Pages are auto-discovered.** Drop `production/<slug>.html` in and `discoverEntries()` in [vite.config.js](mdc:vite.config.js) picks it up. Don't edit `rollupOptions.input`.\n- **Shell opt-in via body attributes**: `<body data-shell=\"admin\" data-page=\"<key>\" data-breadcrumb=\"Home > …\">`. The Vite plugin (`shellInjectionPlugin` in [vite.config.js](mdc:vite.config.js)) inlines sidebar/topbar/footer at build time so the shell paints on the first frame.\n- **Breadcrumb segments link themselves.** A segment whose text matches a `NAV` item becomes a link (`Forms` → `form.html`; a parent resolves to its first child). Override with a pipe — `data-breadcrumb=\"Home > Projects|projects.html > Acme Redesign\"`. The last segment is the current page and never links; a segment with no target stays plain text, so drop grouping-only levels instead of shipping a dead crumb.\n- **NAV is one constant**: `NAV` in [src/v4/shell-render.js](mdc:src/v4/shell-render.js), 7 groups. Match leaf `key` to `data-page`. New icons go in the `ICONS` object in the same file (inline SVG, `currentColor` stroke).\n- **Overlays go through helpers**: `showModal()` ([src/v4/modal.js](mdc:src/v4/modal.js)), `showToast()` ([src/v4/toast.js](mdc:src/v4/toast.js)), `openMenu()` / `openPanel()` ([src/v4/menus.js](mdc:src/v4/menus.js)). Never hand-roll a backdrop, escape handler, or focus-return loop.\n- **CSS custom properties for colors**, never hex literals in components. Tokens in [src/scss/v4/_tokens.scss](mdc:src/scss/v4/_tokens.scss). Charts read them via `getComputedStyle(document.documentElement).getPropertyValue('--…')` — that's how dark-mode redraw stays automatic.\n- **Lazy ECharts imports.** Match the modular import pattern in [src/v4/charts.js](mdc:src/v4/charts.js). Don't `import * as echarts`.\n- **Subpath-safe URLs.** `import.meta.env.BASE_URL` in JS, `${base}` in the Vite plugin, relative paths inside `production/*.html`. Never hard-code a leading `/`.\n- **Idempotent `init<Name>()` exports.** Every module in `src/v4/` exposes a single `init*()` that is safe to call when its root element is absent and safe to call twice.\n- **No `console.*` in shipped code.** Terser drops them in production but ESLint flags them earlier.\n- **Service worker only in prod.** Skip the registration when `import.meta.env.DEV` — see the guard in [src/main-v4.js](mdc:src/main-v4.js).\n\n## File layout\n\n- [src/main-v4.js](mdc:src/main-v4.js) — entry; mounts shell, lazy-loads page modules\n- [src/scss/v4/](mdc:src/scss/v4) — 10 SCSS partials, `main.scss` is the entry\n- [src/v4/shell.js](mdc:src/v4/shell.js) — `mountShell()` runtime behavior (sidebar accordion, theme toggle, mobile drawer)\n- [src/v4/shell-render.js](mdc:src/v4/shell-render.js) — `NAV`, `ICONS`, pure renderers (also imported by the Vite plugin)\n- [src/v4/charts.js](mdc:src/v4/charts.js) — `initCharts()` + ECharts factories\n- [src/v4/tables.js](mdc:src/v4/tables.js) — `initTables()` + DataTables init\n- [src/v4/command-palette.js](mdc:src/v4/command-palette.js) — ⌘K\n- [src/v4/modal.js](mdc:src/v4/modal.js), [src/v4/toast.js](mdc:src/v4/toast.js), [src/v4/menus.js](mdc:src/v4/menus.js) — overlay helpers\n- [src/v4/inbox.js](mdc:src/v4/inbox.js), [kanban.js](mdc:src/v4/kanban.js), [calendar.js](mdc:src/v4/calendar.js), [settings.js](mdc:src/v4/settings.js), [file-manager.js](mdc:src/v4/file-manager.js) — page modules, lazy-loaded\n- [src/v4/form-controls.js](mdc:src/v4/form-controls.js) — date range, multi-select, rich text\n- [production/](mdc:production) — 58 HTML entry pages\n- [types/gentelella.d.ts](mdc:types/gentelella.d.ts) — TypeScript declarations for the public JS surface\n- [scripts/new-page.mjs](mdc:scripts/new-page.mjs) — page scaffolder\n- [scripts/deploy-preview.sh](mdc:scripts/deploy-preview.sh) — R2 deploy with cache headers\n\n## Anti-patterns\n\n- Don't add jQuery, Bootstrap, or a SPA framework.\n- Don't write Vite entry input lists by hand — drop the file in `production/`, it's auto-discovered.\n- Don't add `<script>` tags to individual `production/*.html` files for new modules — lazy-import from `src/main-v4.js` instead.\n- Don't bypass `mountShell()` to render your own sidebar/topbar.\n- Don't `new bootstrap.Modal(...)` — there is no Bootstrap.\n- Don't hard-code paths beginning with `/` in JS or HTML — use `BASE_URL` / relative paths.\n- Don't edit `dist/`, `node_modules/`, or `docs/screenshots/`.\n- Don't introduce PostCSS, Tailwind, or any pipeline besides Vite.\n- Don't import all of ECharts — match the modular pattern in [src/v4/charts.js](mdc:src/v4/charts.js).\n- Don't use hex colors in components — refer to CSS custom properties.\n\n## Recipe: new page\n\n```bash\nnpm run new -- reports --title \"Reports\" --nav-group \"Admin\"\n```\n\nOr by hand: drop `production/<slug>.html` with `<body data-shell=\"admin\" data-page=\"<slug>\" data-breadcrumb=\"Home > …\">` + `<script type=\"module\" src=\"/src/main-v4.js\"></script>`. Then append a leaf to `NAV` in [src/v4/shell-render.js](mdc:src/v4/shell-render.js) (`key` matches `data-page`).\n\n## Recipe: new chart\n\n1. Markup: `<div class=\"card chart-card\"><div class=\"chart\" data-chart=\"<id>\"></div></div>` in the page.\n2. `case '<id>':` in `initCharts()` in [src/v4/charts.js](mdc:src/v4/charts.js) returning the ECharts `option`.\n3. Read tokens via `getComputedStyle(document.documentElement).getPropertyValue('--token')` for colors.\n\n## Recipe: new page module\n\nIn `src/main-v4.js`, add:\n\n```js\nif (document.querySelector('.reports-root')) {\n  import('./v4/reports.js').then((m) => m.initReports());\n}\n```\n\nExport a single idempotent `initReports()` from `src/v4/reports.js`.\n\n## Recipe: modal / toast\n\n```js\nimport { showModal } from './v4/modal.js';\nshowModal({\n  title: 'Delete project?',\n  body: 'This cannot be undone.',\n  actions: [\n    { label: 'Cancel', variant: 'ghost' },\n    { label: 'Delete', variant: 'danger', action: () => { /* … */ } }\n  ]\n});\n\nimport { showToast } from './v4/toast.js';\nshowToast('Saved', { variant: 'success' });\n```\n\n## Commands\n\n```bash\nnpm run dev                # Vite dev server on :9173\nnpm run build              # Production build → dist/\nnpm run preview            # Serve built dist/ on :9174\nnpm run lint               # ESLint over src/\nnpm run format             # Prettier write\nnpm run new -- <slug>      # Scaffold a page\nnpm run screenshots        # Playwright capture (22 pages × light+dark)\nnpm run smoke              # Hit every page, assert 200\nnpm run analyze            # Build + open dist/stats.html\nnpm run deploy:preview     # Build + R2 sync\n```\n\nOverride the dev port with `PORT=…`. Build under a subpath with `BASE_PATH=/foo/ npm run build`.\n"},"files":{".github/copilot-instructions.md":"# GitHub Copilot Instructions — Gentelella v4\n\nAdmin dashboard template (`4.2.0`) by Colorlib. 58 server-rendered HTML pages in `production/`, built with **Vite 8** (Rolldown). **Vanilla ES2022**, no Bootstrap, no jQuery, no SPA framework. SCSS only. Heavyweight deps — **ECharts 6**, **DataTables.net 3**, **Leaflet 1.9** — are lazy-imported per page. Full reference: `CLAUDE.md`.\n\n## Hard rules\n\n- **Vanilla DOM only.** `querySelector`, `classList`, `addEventListener`. No jQuery, no SPA framework. v4's pitch is \"vanilla and small.\"\n- **Single entry**: `src/main-v4.js`. Page-specific modules are lazy-imported inside it, guarded by DOM presence. Don't add `<script>` tags per page.\n- **Pages auto-discover.** Drop `production/<slug>.html` and `discoverEntries()` in `vite.config.js` picks it up — never edit `rollupOptions.input`.\n- **Shell opt-in**: `<body data-shell=\"admin\" data-page=\"<key>\" data-breadcrumb=\"Home > …\">`. The Vite plugin inlines sidebar/topbar/footer at build/dev time (no FOUC).\n- **Breadcrumbs link themselves.** A segment matching a `NAV` item's text becomes a link (`Forms` → `form.html`; a parent resolves to its first child). Override with a pipe: `data-breadcrumb=\"Home > Projects|projects.html > Acme Redesign\"`. The last segment is the current page and never links; untargeted segments stay plain text.\n- **NAV is one constant** — `NAV` in `src/v4/shell-render.js`, 7 groups. `key` matches `data-page`. New icons go in the `ICONS` object in the same file.\n- **Overlays go through helpers**: `showModal()`/`showToast()`/`openMenu()`/`openPanel()` from `src/v4/{modal,toast,menus}.js`. Never hand-roll a backdrop, escape handler, or focus return.\n- **CSS custom properties for colors.** Tokens in `src/scss/v4/_tokens.scss` under `:root` and `[data-theme=\"dark\"]`. Charts read them via `getComputedStyle(document.documentElement).getPropertyValue('--…')` so dark-mode redraw is automatic.\n- **Lazy ECharts.** Match the modular import pattern in `src/v4/charts.js`. Don't `import * as echarts`.\n- **Subpath-safe URLs.** `import.meta.env.BASE_URL` in JS, `${base}` in the Vite plugin, relative paths in `production/*.html`. Never hard-code a leading `/`.\n- **Idempotent `init<Name>()` exports.** Every module in `src/v4/` has one. Safe to call when its root element is absent, safe to call twice.\n- **No `console.*` in shipped code.** Terser drops them in production; ESLint flags earlier.\n- **Service worker only in prod** (`import.meta.env.PROD` guard) — keeps HMR working in dev.\n\n## File layout\n\n- `src/main-v4.js` — entry; mounts shell + lazy-loads page modules\n- `src/scss/v4/` — 10 SCSS partials (`_tokens`, `_layout`, `_components`, `_widgets`, `_forms`, `_datatable`, `_pages`, `_apps`, `_auth`, `main`)\n- `src/v4/shell.js` — `mountShell()` runtime (sidebar accordion, theme toggle, mobile drawer)\n- `src/v4/shell-render.js` — `NAV` + `ICONS` + pure renderers (also imported by Vite plugin)\n- `src/v4/charts.js` — `initCharts()` + ECharts factories\n- `src/v4/tables.js` — `initTables()` + DataTables wrapper\n- `src/v4/command-palette.js` — ⌘K\n- `src/v4/{modal,toast,menus}.js` — overlay helpers\n- `src/v4/{inbox,kanban,calendar,settings,file-manager}.js` — page modules (lazy-loaded)\n- `src/v4/form-controls.js` — date range, multi-select, rich text\n- `production/` — 58 HTML entry pages, auto-discovered\n- `public/` — static assets copied verbatim to `dist/`\n- `types/gentelella.d.ts` — TypeScript declarations for the public JS surface\n- `scripts/new-page.mjs` — page scaffolder (`npm run new -- <slug>`)\n- `scripts/deploy-preview.sh` — R2 deploy with per-file cache headers\n\n## Anti-patterns\n\n- Don't add jQuery, Bootstrap, or a SPA framework.\n- Don't write Vite entry input lists by hand.\n- Don't add `<script>` tags to `production/*.html` for new modules — lazy-import in `src/main-v4.js`.\n- Don't bypass `mountShell()` to wire your own sidebar/topbar.\n- Don't `new bootstrap.Modal(...)` — there is no Bootstrap.\n- Don't hard-code `/` paths in JS/HTML.\n- Don't import all of ECharts — match `src/v4/charts.js`.\n- Don't use hex colors in components — use CSS custom properties.\n- Don't introduce PostCSS, Tailwind, or any pipeline alongside Vite.\n- Don't edit `dist/`, `node_modules/`, or `docs/screenshots/`.\n\n## Commands\n\n```bash\nnpm run dev                # Vite dev server on :9173 (set PORT to override)\nnpm run build              # Production build → dist/\nnpm run preview            # Serve dist/ on :9174\nnpm run lint               # ESLint over src/\nnpm run lint:fix\nnpm run format             # Prettier write\nnpm run new -- <slug>      # Scaffold a page (use --nav-group, --icon, --title …)\nnpm run screenshots        # Playwright capture (22 pages × light+dark)\nnpm run smoke              # Boot dev server, hit every page, assert 200\nnpm run analyze            # Build + open dist/stats.html\nnpm run deploy:preview     # Build + R2 sync with cache headers\n```\n\nBuild under a subpath: `BASE_PATH=/foo/ npm run build`.\n\n## When generating code\n\n- **New page** → use `npm run new -- <slug> --nav-group \"<Group>\"` rather than crafting the HTML by hand. If you do write by hand, copy the head/body pattern from `production/index.html` (specifically the `data-shell`/`data-page`/`data-breadcrumb` triplet and the `<script type=\"module\" src=\"/src/main-v4.js\">` tag).\n- **New chart** → add a `case` to the switch inside `initCharts()` in `src/v4/charts.js`. Read tokens via `getComputedStyle(document.documentElement).getPropertyValue('--…')`.\n- **New page module** → add the lazy-import block at the bottom of `src/main-v4.js`, then create `src/v4/<name>.js` exporting a single idempotent `init<Name>()`.\n- **New NAV entry** → append to the right group in `NAV` in `src/v4/shell-render.js`. `{ key, href, text, icon }`. `key` must match the target page's `data-page`.\n- **New icon** → append to `ICONS` in `src/v4/shell-render.js`. Inline SVG, `currentColor` stroke, 24×24 viewbox.\n- **New modal/toast** → import `showModal` / `showToast` from `src/v4/modal.js` / `src/v4/toast.js`. Don't render backdrops yourself.\n- **New SCSS** → add a partial under `src/scss/v4/` and `@use` it from `main.scss`. Variables live in `_tokens.scss`; new tokens get both a light and a dark value.\n","docs/architecture.md":"# Architecture\n\nThis page has moved to **<https://gentelella.colorlib.com/docs/architecture/>**.\n\nGentelella v4 documentation is now maintained at <https://gentelella.colorlib.com/docs/> as the single source of truth.\n",".cursor/rules/project.mdc":"---\ndescription: Gentelella v4 — project conventions, recipes, anti-patterns\nglobs:\n  - \"**/*.js\"\n  - \"**/*.scss\"\n  - \"**/*.html\"\n  - \"**/*.css\"\nalwaysApply: true\n---\n\n# Gentelella v4 (`4.2.0`)\n\nAdmin dashboard template by Colorlib. 58 server-rendered HTML pages in [production/](mdc:production), built with **Vite 8** (Rolldown). **Vanilla ES2022**, no Bootstrap, no jQuery, no SPA framework. SCSS only. Heavyweight deps (**ECharts 6**, **DataTables.net 3**, **Leaflet 1.9**) are lazy-imported per page.\n\nFull reference: [CLAUDE.md](mdc:CLAUDE.md). The rules below are the load-bearing ones — apply them by default.\n\n## Hard rules\n\n- **Vanilla DOM only.** `querySelector`, `classList`, `addEventListener`. Never add jQuery, Bootstrap JS, or an SPA framework.\n- **Single entry**: [src/main-v4.js](mdc:src/main-v4.js). Every page loads it. Page-specific modules are lazy-imported inside `main-v4.js` guarded by DOM presence — match that pattern, don't add new `<script>` tags per page.\n- **Pages are auto-discovered.** Drop `production/<slug>.html` in and `discoverEntries()` in [vite.config.js](mdc:vite.config.js) picks it up. Don't edit `rollupOptions.input`.\n- **Shell opt-in via body attributes**: `<body data-shell=\"admin\" data-page=\"<key>\" data-breadcrumb=\"Home > …\">`. The Vite plugin (`shellInjectionPlugin` in [vite.config.js](mdc:vite.config.js)) inlines sidebar/topbar/footer at build time so the shell paints on the first frame.\n- **Breadcrumb segments link themselves.** A segment whose text matches a `NAV` item becomes a link (`Forms` → `form.html`; a parent resolves to its first child). Override with a pipe — `data-breadcrumb=\"Home > Projects|projects.html > Acme Redesign\"`. The last segment is the current page and never links; a segment with no target stays plain text, so drop grouping-only levels instead of shipping a dead crumb.\n- **NAV is one constant**: `NAV` in [src/v4/shell-render.js](mdc:src/v4/shell-render.js), 7 groups. Match leaf `key` to `data-page`. New icons go in the `ICONS` object in the same file (inline SVG, `currentColor` stroke).\n- **Overlays go through helpers**: `showModal()` ([src/v4/modal.js](mdc:src/v4/modal.js)), `showToast()` ([src/v4/toast.js](mdc:src/v4/toast.js)), `openMenu()` / `openPanel()` ([src/v4/menus.js](mdc:src/v4/menus.js)). Never hand-roll a backdrop, escape handler, or focus-return loop.\n- **CSS custom properties for colors**, never hex literals in components. Tokens in [src/scss/v4/_tokens.scss](mdc:src/scss/v4/_tokens.scss). Charts read them via `getComputedStyle(document.documentElement).getPropertyValue('--…')` — that's how dark-mode redraw stays automatic.\n- **Lazy ECharts imports.** Match the modular import pattern in [src/v4/charts.js](mdc:src/v4/charts.js). Don't `import * as echarts`.\n- **Subpath-safe URLs.** `import.meta.env.BASE_URL` in JS, `${base}` in the Vite plugin, relative paths inside `production/*.html`. Never hard-code a leading `/`.\n- **Idempotent `init<Name>()` exports.** Every module in `src/v4/` exposes a single `init*()` that is safe to call when its root element is absent and safe to call twice.\n- **No `console.*` in shipped code.** Terser drops them in production but ESLint flags them earlier.\n- **Service worker only in prod.** Skip the registration when `import.meta.env.DEV` — see the guard in [src/main-v4.js](mdc:src/main-v4.js).\n\n## File layout\n\n- [src/main-v4.js](mdc:src/main-v4.js) — entry; mounts shell, lazy-loads page modules\n- [src/scss/v4/](mdc:src/scss/v4) — 10 SCSS partials, `main.scss` is the entry\n- [src/v4/shell.js](mdc:src/v4/shell.js) — `mountShell()` runtime behavior (sidebar accordion, theme toggle, mobile drawer)\n- [src/v4/shell-render.js](mdc:src/v4/shell-render.js) — `NAV`, `ICONS`, pure renderers (also imported by the Vite plugin)\n- [src/v4/charts.js](mdc:src/v4/charts.js) — `initCharts()` + ECharts factories\n- [src/v4/tables.js](mdc:src/v4/tables.js) — `initTables()` + DataTables init\n- [src/v4/command-palette.js](mdc:src/v4/command-palette.js) — ⌘K\n- [src/v4/modal.js](mdc:src/v4/modal.js), [src/v4/toast.js](mdc:src/v4/toast.js), [src/v4/menus.js](mdc:src/v4/menus.js) — overlay helpers\n- [src/v4/inbox.js](mdc:src/v4/inbox.js), [kanban.js](mdc:src/v4/kanban.js), [calendar.js](mdc:src/v4/calendar.js), [settings.js](mdc:src/v4/settings.js), [file-manager.js](mdc:src/v4/file-manager.js) — page modules, lazy-loaded\n- [src/v4/form-controls.js](mdc:src/v4/form-controls.js) — date range, multi-select, rich text\n- [production/](mdc:production) — 58 HTML entry pages\n- [types/gentelella.d.ts](mdc:types/gentelella.d.ts) — TypeScript declarations for the public JS surface\n- [scripts/new-page.mjs](mdc:scripts/new-page.mjs) — page scaffolder\n- [scripts/deploy-preview.sh](mdc:scripts/deploy-preview.sh) — R2 deploy with cache headers\n\n## Anti-patterns\n\n- Don't add jQuery, Bootstrap, or a SPA framework.\n- Don't write Vite entry input lists by hand — drop the file in `production/`, it's auto-discovered.\n- Don't add `<script>` tags to individual `production/*.html` files for new modules — lazy-import from `src/main-v4.js` instead.\n- Don't bypass `mountShell()` to render your own sidebar/topbar.\n- Don't `new bootstrap.Modal(...)` — there is no Bootstrap.\n- Don't hard-code paths beginning with `/` in JS or HTML — use `BASE_URL` / relative paths.\n- Don't edit `dist/`, `node_modules/`, or `docs/screenshots/`.\n- Don't introduce PostCSS, Tailwind, or any pipeline besides Vite.\n- Don't import all of ECharts — match the modular pattern in [src/v4/charts.js](mdc:src/v4/charts.js).\n- Don't use hex colors in components — refer to CSS custom properties.\n\n## Recipe: new page\n\n```bash\nnpm run new -- reports --title \"Reports\" --nav-group \"Admin\"\n```\n\nOr by hand: drop `production/<slug>.html` with `<body data-shell=\"admin\" data-page=\"<slug>\" data-breadcrumb=\"Home > …\">` + `<script type=\"module\" src=\"/src/main-v4.js\"></script>`. Then append a leaf to `NAV` in [src/v4/shell-render.js](mdc:src/v4/shell-render.js) (`key` matches `data-page`).\n\n## Recipe: new chart\n\n1. Markup: `<div class=\"card chart-card\"><div class=\"chart\" data-chart=\"<id>\"></div></div>` in the page.\n2. `case '<id>':` in `initCharts()` in [src/v4/charts.js](mdc:src/v4/charts.js) returning the ECharts `option`.\n3. Read tokens via `getComputedStyle(document.documentElement).getPropertyValue('--token')` for colors.\n\n## Recipe: new page module\n\nIn `src/main-v4.js`, add:\n\n```js\nif (document.querySelector('.reports-root')) {\n  import('./v4/reports.js').then((m) => m.initReports());\n}\n```\n\nExport a single idempotent `initReports()` from `src/v4/reports.js`.\n\n## Recipe: modal / toast\n\n```js\nimport { showModal } from './v4/modal.js';\nshowModal({\n  title: 'Delete project?',\n  body: 'This cannot be undone.',\n  actions: [\n    { label: 'Cancel', variant: 'ghost' },\n    { label: 'Delete', variant: 'danger', action: () => { /* … */ } }\n  ]\n});\n\nimport { showToast } from './v4/toast.js';\nshowToast('Saved', { variant: 'success' });\n```\n\n## Commands\n\n```bash\nnpm run dev                # Vite dev server on :9173\nnpm run build              # Production build → dist/\nnpm run preview            # Serve built dist/ on :9174\nnpm run lint               # ESLint over src/\nnpm run format             # Prettier write\nnpm run new -- <slug>      # Scaffold a page\nnpm run screenshots        # Playwright capture (22 pages × light+dark)\nnpm run smoke              # Hit every page, assert 200\nnpm run analyze            # Build + open dist/stats.html\nnpm run deploy:preview     # Build + R2 sync\n```\n\nOverride the dev port with `PORT=…`. Build under a subpath with `BASE_PATH=/foo/ npm run build`.\n"},"items":[{"name":"architecture.md","path":"docs/architecture.md","rawUrl":"https://raw.githubusercontent.com/ColorlibHQ/gentelella/HEAD/docs/architecture.md","title":"docs - System Architecture & Module Boundaries","category":"project-spec","format":"markdown","content":"# Architecture\n\nThis page has moved to **<https://gentelella.colorlib.com/docs/architecture/>**.\n\nGentelella v4 documentation is now maintained at <https://gentelella.colorlib.com/docs/> as the single source of truth.\n","isInternal":false,"tokens":55,"sizeBytes":218},{"name":"project.mdc","path":".cursor/rules/project.mdc","rawUrl":"https://raw.githubusercontent.com/ColorlibHQ/gentelella/HEAD/.cursor/rules/project.mdc","title":"Cursor Rule: project","category":"cursor-rule","format":"mdc","content":"---\ndescription: Gentelella v4 — project conventions, recipes, anti-patterns\nglobs:\n  - \"**/*.js\"\n  - \"**/*.scss\"\n  - \"**/*.html\"\n  - \"**/*.css\"\nalwaysApply: true\n---\n\n# Gentelella v4 (`4.2.0`)\n\nAdmin dashboard template by Colorlib. 58 server-rendered HTML pages in [production/](mdc:production), built with **Vite 8** (Rolldown). **Vanilla ES2022**, no Bootstrap, no jQuery, no SPA framework. SCSS only. Heavyweight deps (**ECharts 6**, **DataTables.net 3**, **Leaflet 1.9**) are lazy-imported per page.\n\nFull reference: [CLAUDE.md](mdc:CLAUDE.md). The rules below are the load-bearing ones — apply them by default.\n\n## Hard rules\n\n- **Vanilla DOM only.** `querySelector`, `classList`, `addEventListener`. Never add jQuery, Bootstrap JS, or an SPA framework.\n- **Single entry**: [src/main-v4.js](mdc:src/main-v4.js). Every page loads it. Page-specific modules are lazy-imported inside `main-v4.js` guarded by DOM presence — match that pattern, don't add new `<script>` tags per page.\n- **Pages are auto-discovered.** Drop `production/<slug>.html` in and `discoverEntries()` in [vite.config.js](mdc:vite.config.js) picks it up. Don't edit `rollupOptions.input`.\n- **Shell opt-in via body attributes**: `<body data-shell=\"admin\" data-page=\"<key>\" data-breadcrumb=\"Home > …\">`. The Vite plugin (`shellInjectionPlugin` in [vite.config.js](mdc:vite.config.js)) inlines sidebar/topbar/footer at build time so the shell paints on the first frame.\n- **Breadcrumb segments link themselves.** A segment whose text matches a `NAV` item becomes a link (`Forms` → `form.html`; a parent resolves to its first child). Override with a pipe — `data-breadcrumb=\"Home > Projects|projects.html > Acme Redesign\"`. The last segment is the current page and never links; a segment with no target stays plain text, so drop grouping-only levels instead of shipping a dead crumb.\n- **NAV is one constant**: `NAV` in [src/v4/shell-render.js](mdc:src/v4/shell-render.js), 7 groups. Match leaf `key` to `data-page`. New icons go in the `ICONS` object in the same file (inline SVG, `currentColor` stroke).\n- **Overlays go through helpers**: `showModal()` ([src/v4/modal.js](mdc:src/v4/modal.js)), `showToast()` ([src/v4/toast.js](mdc:src/v4/toast.js)), `openMenu()` / `openPanel()` ([src/v4/menus.js](mdc:src/v4/menus.js)). Never hand-roll a backdrop, escape handler, or focus-return loop.\n- **CSS custom properties for colors**, never hex literals in components. Tokens in [src/scss/v4/_tokens.scss](mdc:src/scss/v4/_tokens.scss). Charts read them via `getComputedStyle(document.documentElement).getPropertyValue('--…')` — that's how dark-mode redraw stays automatic.\n- **Lazy ECharts imports.** Match the modular import pattern in [src/v4/charts.js](mdc:src/v4/charts.js). Don't `import * as echarts`.\n- **Subpath-safe URLs.** `import.meta.env.BASE_URL` in JS, `${base}` in the Vite plugin, relative paths inside `production/*.html`. Never hard-code a leading `/`.\n- **Idempotent `init<Name>()` exports.** Every module in `src/v4/` exposes a single `init*()` that is safe to call when its root element is absent and safe to call twice.\n- **No `console.*` in shipped code.** Terser drops them in production but ESLint flags them earlier.\n- **Service worker only in prod.** Skip the registration when `import.meta.env.DEV` — see the guard in [src/main-v4.js](mdc:src/main-v4.js).\n\n## File layout\n\n- [src/main-v4.js](mdc:src/main-v4.js) — entry; mounts shell, lazy-loads page modules\n- [src/scss/v4/](mdc:src/scss/v4) — 10 SCSS partials, `main.scss` is the entry\n- [src/v4/shell.js](mdc:src/v4/shell.js) — `mountShell()` runtime behavior (sidebar accordion, theme toggle, mobile drawer)\n- [src/v4/shell-render.js](mdc:src/v4/shell-render.js) — `NAV`, `ICONS`, pure renderers (also imported by the Vite plugin)\n- [src/v4/charts.js](mdc:src/v4/charts.js) — `initCharts()` + ECharts factories\n- [src/v4/tables.js](mdc:src/v4/tables.js) — `initTables()` + DataTables init\n- [src/v4/command-palette.js](mdc:src/v4/command-palette.js) — ⌘K\n- [src/v4/modal.js](mdc:src/v4/modal.js), [src/v4/toast.js](mdc:src/v4/toast.js), [src/v4/menus.js](mdc:src/v4/menus.js) — overlay helpers\n- [src/v4/inbox.js](mdc:src/v4/inbox.js), [kanban.js](mdc:src/v4/kanban.js), [calendar.js](mdc:src/v4/calendar.js), [settings.js](mdc:src/v4/settings.js), [file-manager.js](mdc:src/v4/file-manager.js) — page modules, lazy-loaded\n- [src/v4/form-controls.js](mdc:src/v4/form-controls.js) — date range, multi-select, rich text\n- [production/](mdc:production) — 58 HTML entry pages\n- [types/gentelella.d.ts](mdc:types/gentelella.d.ts) — TypeScript declarations for the public JS surface\n- [scripts/new-page.mjs](mdc:scripts/new-page.mjs) — page scaffolder\n- [scripts/deploy-preview.sh](mdc:scripts/deploy-preview.sh) — R2 deploy with cache headers\n\n## Anti-patterns\n\n- Don't add jQuery, Bootstrap, or a SPA framework.\n- Don't write Vite entry input lists by hand — drop the file in `production/`, it's auto-discovered.\n- Don't add `<script>` tags to individual `production/*.html` files for new modules — lazy-import from `src/main-v4.js` instead.\n- Don't bypass `mountShell()` to render your own sidebar/topbar.\n- Don't `new bootstrap.Modal(...)` — there is no Bootstrap.\n- Don't hard-code paths beginning with `/` in JS or HTML — use `BASE_URL` / relative paths.\n- Don't edit `dist/`, `node_modules/`, or `docs/screenshots/`.\n- Don't introduce PostCSS, Tailwind, or any pipeline besides Vite.\n- Don't import all of ECharts — match the modular pattern in [src/v4/charts.js](mdc:src/v4/charts.js).\n- Don't use hex colors in components — refer to CSS custom properties.\n\n## Recipe: new page\n\n```bash\nnpm run new -- reports --title \"Reports\" --nav-group \"Admin\"\n```\n\nOr by hand: drop `production/<slug>.html` with `<body data-shell=\"admin\" data-page=\"<slug>\" data-breadcrumb=\"Home > …\">` + `<script type=\"module\" src=\"/src/main-v4.js\"></script>`. Then append a leaf to `NAV` in [src/v4/shell-render.js](mdc:src/v4/shell-render.js) (`key` matches `data-page`).\n\n## Recipe: new chart\n\n1. Markup: `<div class=\"card chart-card\"><div class=\"chart\" data-chart=\"<id>\"></div></div>` in the page.\n2. `case '<id>':` in `initCharts()` in [src/v4/charts.js](mdc:src/v4/charts.js) returning the ECharts `option`.\n3. Read tokens via `getComputedStyle(document.documentElement).getPropertyValue('--token')` for colors.\n\n## Recipe: new page module\n\nIn `src/main-v4.js`, add:\n\n```js\nif (document.querySelector('.reports-root')) {\n  import('./v4/reports.js').then((m) => m.initReports());\n}\n```\n\nExport a single idempotent `initReports()` from `src/v4/reports.js`.\n\n## Recipe: modal / toast\n\n```js\nimport { showModal } from './v4/modal.js';\nshowModal({\n  title: 'Delete project?',\n  body: 'This cannot be undone.',\n  actions: [\n    { label: 'Cancel', variant: 'ghost' },\n    { label: 'Delete', variant: 'danger', action: () => { /* … */ } }\n  ]\n});\n\nimport { showToast } from './v4/toast.js';\nshowToast('Saved', { variant: 'success' });\n```\n\n## Commands\n\n```bash\nnpm run dev                # Vite dev server on :9173\nnpm run build              # Production build → dist/\nnpm run preview            # Serve built dist/ on :9174\nnpm run lint               # ESLint over src/\nnpm run format             # Prettier write\nnpm run new -- <slug>      # Scaffold a page\nnpm run screenshots        # Playwright capture (22 pages × light+dark)\nnpm run smoke              # Hit every page, assert 200\nnpm run analyze            # Build + open dist/stats.html\nnpm run deploy:preview     # Build + R2 sync\n```\n\nOverride the dev port with `PORT=…`. Build under a subpath with `BASE_PATH=/foo/ npm run build`.\n","frontmatter":{"description":"Gentelella v4 — project conventions, recipes, anti-patterns","globs":["**/*.js","**/*.scss","**/*.html","**/*.css"],"alwaysApply":true},"isInternal":false,"tokens":2172,"sizeBytes":7682},{"name":"copilot-instructions.md","path":".github/copilot-instructions.md","rawUrl":"https://raw.githubusercontent.com/ColorlibHQ/gentelella/HEAD/.github/copilot-instructions.md","title":"GitHub Copilot Instructions","category":"copilot-instructions","format":"markdown","content":"# GitHub Copilot Instructions — Gentelella v4\n\nAdmin dashboard template (`4.2.0`) by Colorlib. 58 server-rendered HTML pages in `production/`, built with **Vite 8** (Rolldown). **Vanilla ES2022**, no Bootstrap, no jQuery, no SPA framework. SCSS only. Heavyweight deps — **ECharts 6**, **DataTables.net 3**, **Leaflet 1.9** — are lazy-imported per page. Full reference: `CLAUDE.md`.\n\n## Hard rules\n\n- **Vanilla DOM only.** `querySelector`, `classList`, `addEventListener`. No jQuery, no SPA framework. v4's pitch is \"vanilla and small.\"\n- **Single entry**: `src/main-v4.js`. Page-specific modules are lazy-imported inside it, guarded by DOM presence. Don't add `<script>` tags per page.\n- **Pages auto-discover.** Drop `production/<slug>.html` and `discoverEntries()` in `vite.config.js` picks it up — never edit `rollupOptions.input`.\n- **Shell opt-in**: `<body data-shell=\"admin\" data-page=\"<key>\" data-breadcrumb=\"Home > …\">`. The Vite plugin inlines sidebar/topbar/footer at build/dev time (no FOUC).\n- **Breadcrumbs link themselves.** A segment matching a `NAV` item's text becomes a link (`Forms` → `form.html`; a parent resolves to its first child). Override with a pipe: `data-breadcrumb=\"Home > Projects|projects.html > Acme Redesign\"`. The last segment is the current page and never links; untargeted segments stay plain text.\n- **NAV is one constant** — `NAV` in `src/v4/shell-render.js`, 7 groups. `key` matches `data-page`. New icons go in the `ICONS` object in the same file.\n- **Overlays go through helpers**: `showModal()`/`showToast()`/`openMenu()`/`openPanel()` from `src/v4/{modal,toast,menus}.js`. Never hand-roll a backdrop, escape handler, or focus return.\n- **CSS custom properties for colors.** Tokens in `src/scss/v4/_tokens.scss` under `:root` and `[data-theme=\"dark\"]`. Charts read them via `getComputedStyle(document.documentElement).getPropertyValue('--…')` so dark-mode redraw is automatic.\n- **Lazy ECharts.** Match the modular import pattern in `src/v4/charts.js`. Don't `import * as echarts`.\n- **Subpath-safe URLs.** `import.meta.env.BASE_URL` in JS, `${base}` in the Vite plugin, relative paths in `production/*.html`. Never hard-code a leading `/`.\n- **Idempotent `init<Name>()` exports.** Every module in `src/v4/` has one. Safe to call when its root element is absent, safe to call twice.\n- **No `console.*` in shipped code.** Terser drops them in production; ESLint flags earlier.\n- **Service worker only in prod** (`import.meta.env.PROD` guard) — keeps HMR working in dev.\n\n## File layout\n\n- `src/main-v4.js` — entry; mounts shell + lazy-loads page modules\n- `src/scss/v4/` — 10 SCSS partials (`_tokens`, `_layout`, `_components`, `_widgets`, `_forms`, `_datatable`, `_pages`, `_apps`, `_auth`, `main`)\n- `src/v4/shell.js` — `mountShell()` runtime (sidebar accordion, theme toggle, mobile drawer)\n- `src/v4/shell-render.js` — `NAV` + `ICONS` + pure renderers (also imported by Vite plugin)\n- `src/v4/charts.js` — `initCharts()` + ECharts factories\n- `src/v4/tables.js` — `initTables()` + DataTables wrapper\n- `src/v4/command-palette.js` — ⌘K\n- `src/v4/{modal,toast,menus}.js` — overlay helpers\n- `src/v4/{inbox,kanban,calendar,settings,file-manager}.js` — page modules (lazy-loaded)\n- `src/v4/form-controls.js` — date range, multi-select, rich text\n- `production/` — 58 HTML entry pages, auto-discovered\n- `public/` — static assets copied verbatim to `dist/`\n- `types/gentelella.d.ts` — TypeScript declarations for the public JS surface\n- `scripts/new-page.mjs` — page scaffolder (`npm run new -- <slug>`)\n- `scripts/deploy-preview.sh` — R2 deploy with per-file cache headers\n\n## Anti-patterns\n\n- Don't add jQuery, Bootstrap, or a SPA framework.\n- Don't write Vite entry input lists by hand.\n- Don't add `<script>` tags to `production/*.html` for new modules — lazy-import in `src/main-v4.js`.\n- Don't bypass `mountShell()` to wire your own sidebar/topbar.\n- Don't `new bootstrap.Modal(...)` — there is no Bootstrap.\n- Don't hard-code `/` paths in JS/HTML.\n- Don't import all of ECharts — match `src/v4/charts.js`.\n- Don't use hex colors in components — use CSS custom properties.\n- Don't introduce PostCSS, Tailwind, or any pipeline alongside Vite.\n- Don't edit `dist/`, `node_modules/`, or `docs/screenshots/`.\n\n## Commands\n\n```bash\nnpm run dev                # Vite dev server on :9173 (set PORT to override)\nnpm run build              # Production build → dist/\nnpm run preview            # Serve dist/ on :9174\nnpm run lint               # ESLint over src/\nnpm run lint:fix\nnpm run format             # Prettier write\nnpm run new -- <slug>      # Scaffold a page (use --nav-group, --icon, --title …)\nnpm run screenshots        # Playwright capture (22 pages × light+dark)\nnpm run smoke              # Boot dev server, hit every page, assert 200\nnpm run analyze            # Build + open dist/stats.html\nnpm run deploy:preview     # Build + R2 sync with cache headers\n```\n\nBuild under a subpath: `BASE_PATH=/foo/ npm run build`.\n\n## When generating code\n\n- **New page** → use `npm run new -- <slug> --nav-group \"<Group>\"` rather than crafting the HTML by hand. If you do write by hand, copy the head/body pattern from `production/index.html` (specifically the `data-shell`/`data-page`/`data-breadcrumb` triplet and the `<script type=\"module\" src=\"/src/main-v4.js\">` tag).\n- **New chart** → add a `case` to the switch inside `initCharts()` in `src/v4/charts.js`. Read tokens via `getComputedStyle(document.documentElement).getPropertyValue('--…')`.\n- **New page module** → add the lazy-import block at the bottom of `src/main-v4.js`, then create `src/v4/<name>.js` exporting a single idempotent `init<Name>()`.\n- **New NAV entry** → append to the right group in `NAV` in `src/v4/shell-render.js`. `{ key, href, text, icon }`. `key` must match the target page's `data-page`.\n- **New icon** → append to `ICONS` in `src/v4/shell-render.js`. Inline SVG, `currentColor` stroke, 24×24 viewbox.\n- **New modal/toast** → import `showModal` / `showToast` from `src/v4/modal.js` / `src/v4/toast.js`. Don't render backdrops yourself.\n- **New SCSS** → add a partial under `src/scss/v4/` and `@use` it from `main.scss`. Variables live in `_tokens.scss`; new tokens get both a light and a dark value.\n","isInternal":false,"tokens":1719,"sizeBytes":6308}],"systemPromptSnippet":"<yakaai_skills repo=\"ColorlibHQ/gentelella\">\n<!-- File: docs/architecture.md (Tokens: ~55 | Category: project-spec) -->\n# Architecture\n\nThis page has moved to **<https://gentelella.colorlib.com/docs/architecture/>**.\n\nGentelella v4 documentation is now maintained at <https://gentelella.colorlib.com/docs/> as the single source of truth.\n\n<!-- File: .cursor/rules/project.mdc (Tokens: ~2172 | Category: cursor-rule) -->\n---\ndescription: Gentelella v4 — project conventions, recipes, anti-patterns\nglobs:\n  - \"**/*.js\"\n  - \"**/*.scss\"\n  - \"**/*.html\"\n  - \"**/*.css\"\nalwaysApply: true\n---\n\n# Gentelella v4 (`4.2.0`)\n\nAdmin dashboard template by Colorlib. 58 server-rendered HTML pages in [production/](mdc:production), built with **Vite 8** (Rolldown). **Vanilla ES2022**, no Bootstrap, no jQuery, no SPA framework. SCSS only. Heavyweight deps (**ECharts 6**, **DataTables.net 3**, **Leaflet 1.9**) are lazy-imported per page.\n\nFull reference: [CLAUDE.md](mdc:CLAUDE.md). The rules below are the load-bearing ones — apply them by default.\n\n## Hard rules\n\n- **Vanilla DOM only.** `querySelector`, `classList`, `addEventListener`. Never add jQuery, Bootstrap JS, or an SPA framework.\n- **Single entry**: [src/main-v4.js](mdc:src/main-v4.js). Every page loads it. Page-specific modules are lazy-imported inside `main-v4.js` guarded by DOM presence — match that pattern, don't add new `<script>` tags per page.\n- **Pages are auto-discovered.** Drop `production/<slug>.html` in and `discoverEntries()` in [vite.config.js](mdc:vite.config.js) picks it up. Don't edit `rollupOptions.input`.\n- **Shell opt-in via body attributes**: `<body data-shell=\"admin\" data-page=\"<key>\" data-breadcrumb=\"Home > …\">`. The Vite plugin (`shellInjectionPlugin` in [vite.config.js](mdc:vite.config.js)) inlines sidebar/topbar/footer at build time so the shell paints on the first frame.\n- **Breadcrumb segments link themselves.** A segment whose text matches a `NAV` item becomes a link (`Forms` → `form.html`; a parent resolves to its first child). Override with a pipe — `data-breadcrumb=\"Home > Projects|projects.html > Acme Redesign\"`. The last segment is the current page and never links; a segment with no target stays plain text, so drop grouping-only levels instead of shipping a dead crumb.\n- **NAV is one constant**: `NAV` in [src/v4/shell-render.js](mdc:src/v4/shell-render.js), 7 groups. Match leaf `key` to `data-page`. New icons go in the `ICONS` object in the same file (inline SVG, `currentColor` stroke).\n- **Overlays go through helpers**: `showModal()` ([src/v4/modal.js](mdc:src/v4/modal.js)), `showToast()` ([src/v4/toast.js](mdc:src/v4/toast.js)), `openMenu()` / `openPanel()` ([src/v4/menus.js](mdc:src/v4/menus.js)). Never hand-roll a backdrop, escape handler, or focus-return loop.\n- **CSS custom properties for colors**, never hex literals in components. Tokens in [src/scss/v4/_tokens.scss](mdc:src/scss/v4/_tokens.scss). Charts read them via `getComputedStyle(document.documentElement).getPropertyValue('--…')` — that's how dark-mode redraw stays automatic.\n- **Lazy ECharts imports.** Match the modular import pattern in [src/v4/charts.js](mdc:src/v4/charts.js). Don't `import * as echarts`.\n- **Subpath-safe URLs.** `import.meta.env.BASE_URL` in JS, `${base}` in the Vite plugin, relative paths inside `production/*.html`. Never hard-code a leading `/`.\n- **Idempotent `init<Name>()` exports.** Every module in `src/v4/` exposes a single `init*()` that is safe to call when its root element is absent and safe to call twice.\n- **No `console.*` in shipped code.** Terser drops them in production but ESLint flags them earlier.\n- **Service worker only in prod.** Skip the registration when `import.meta.env.DEV` — see the guard in [src/main-v4.js](mdc:src/main-v4.js).\n\n## File layout\n\n- [src/main-v4.js](mdc:src/main-v4.js) — entry; mounts shell, lazy-loads page modules\n- [src/scss/v4/](mdc:src/scss/v4) — 10 SCSS partials, `main.scss` is the entry\n- [src/v4/shell.js](mdc:src/v4/shell.js) — `mountShell()` runtime behavior (sidebar accordion, theme toggle, mobile drawer)\n- [src/v4/shell-render.js](mdc:src/v4/shell-render.js) — `NAV`, `ICONS`, pure renderers (also imported by the Vite plugin)\n- [src/v4/charts.js](mdc:src/v4/charts.js) — `initCharts()` + ECharts factories\n- [src/v4/tables.js](mdc:src/v4/tables.js) — `initTables()` + DataTables init\n- [src/v4/command-palette.js](mdc:src/v4/command-palette.js) — ⌘K\n- [src/v4/modal.js](mdc:src/v4/modal.js), [src/v4/toast.js](mdc:src/v4/toast.js), [src/v4/menus.js](mdc:src/v4/menus.js) — overlay helpers\n- [src/v4/inbox.js](mdc:src/v4/inbox.js), [kanban.js](mdc:src/v4/kanban.js), [calendar.js](mdc:src/v4/calendar.js), [settings.js](mdc:src/v4/settings.js), [file-manager.js](mdc:src/v4/file-manager.js) — page modules, lazy-loaded\n- [src/v4/form-controls.js](mdc:src/v4/form-controls.js) — date range, multi-select, rich text\n- [production/](mdc:production) — 58 HTML entry pages\n- [types/gentelella.d.ts](mdc:types/gentelella.d.ts) — TypeScript declarations for the public JS surface\n- [scripts/new-page.mjs](mdc:scripts/new-page.mjs) — page scaffolder\n- [scripts/deploy-preview.sh](mdc:scripts/deploy-preview.sh) — R2 deploy with cache headers\n\n## Anti-patterns\n\n- Don't add jQuery, Bootstrap, or a SPA framework.\n- Don't write Vite entry input lists by hand — drop the file in `production/`, it's auto-discovered.\n- Don't add `<script>` tags to individual `production/*.html` files for new modules — lazy-import from `src/main-v4.js` instead.\n- Don't bypass `mountShell()` to render your own sidebar/topbar.\n- Don't `new bootstrap.Modal(...)` — there is no Bootstrap.\n- Don't hard-code paths beginning with `/` in JS or HTML — use `BASE_URL` / relative paths.\n- Don't edit `dist/`, `node_modules/`, or `docs/screenshots/`.\n- Don't introduce PostCSS, Tailwind, or any pipeline besides Vite.\n- Don't import all of ECharts — match the modular pattern in [src/v4/charts.js](mdc:src/v4/charts.js).\n- Don't use hex colors in components — refer to CSS custom properties.\n\n## Recipe: new page\n\n```bash\nnpm run new -- reports --title \"Reports\" --nav-group \"Admin\"\n```\n\nOr by hand: drop `production/<slug>.html` with `<body data-shell=\"admin\" data-page=\"<slug>\" data-breadcrumb=\"Home > …\">` + `<script type=\"module\" src=\"/src/main-v4.js\"></script>`. Then append a leaf to `NAV` in [src/v4/shell-render.js](mdc:src/v4/shell-render.js) (`key` matches `data-page`).\n\n## Recipe: new chart\n\n1. Markup: `<div class=\"card chart-card\"><div class=\"chart\" data-chart=\"<id>\"></div></div>` in the page.\n2. `case '<id>':` in `initCharts()` in [src/v4/charts.js](mdc:src/v4/charts.js) returning the ECharts `option`.\n3. Read tokens via `getComputedStyle(document.documentElement).getPropertyValue('--token')` for colors.\n\n## Recipe: new page module\n\nIn `src/main-v4.js`, add:\n\n```js\nif (document.querySelector('.reports-root')) {\n  import('./v4/reports.js').then((m) => m.initReports());\n}\n```\n\nExport a single idempotent `initReports()` from `src/v4/reports.js`.\n\n## Recipe: modal / toast\n\n```js\nimport { showModal } from './v4/modal.js';\nshowModal({\n  title: 'Delete project?',\n  body: 'This cannot be undone.',\n  actions: [\n    { label: 'Cancel', variant: 'ghost' },\n    { label: 'Delete', variant: 'danger', action: () => { /* … */ } }\n  ]\n});\n\nimport { showToast } from './v4/toast.js';\nshowToast('Saved', { variant: 'success' });\n```\n\n## Commands\n\n```bash\nnpm run dev                # Vite dev server on :9173\nnpm run build              # Production build → dist/\nnpm run preview            # Serve built dist/ on :9174\nnpm run lint               # ESLint over src/\nnpm run format             # Prettier write\nnpm run new -- <slug>      # Scaffold a page\nnpm run screenshots        # Playwright capture (22 pages × light+dark)\nnpm run smoke              # Hit every page, assert 200\nnpm run analyze            # Build + open dist/stats.html\nnpm run deploy:preview     # Build + R2 sync\n```\n\nOverride the dev port with `PORT=…`. Build under a subpath with `BASE_PATH=/foo/ npm run build`.\n\n<!-- File: .github/copilot-instructions.md (Tokens: ~1719 | Category: copilot-instructions) -->\n# GitHub Copilot Instructions — Gentelella v4\n\nAdmin dashboard template (`4.2.0`) by Colorlib. 58 server-rendered HTML pages in `production/`, built with **Vite 8** (Rolldown). **Vanilla ES2022**, no Bootstrap, no jQuery, no SPA framework. SCSS only. Heavyweight deps — **ECharts 6**, **DataTables.net 3**, **Leaflet 1.9** — are lazy-imported per page. Full reference: `CLAUDE.md`.\n\n## Hard rules\n\n- **Vanilla DOM only.** `querySelector`, `classList`, `addEventListener`. No jQuery, no SPA framework. v4's pitch is \"vanilla and small.\"\n- **Single entry**: `src/main-v4.js`. Page-specific modules are lazy-imported inside it, guarded by DOM presence. Don't add `<script>` tags per page.\n- **Pages auto-discover.** Drop `production/<slug>.html` and `discoverEntries()` in `vite.config.js` picks it up — never edit `rollupOptions.input`.\n- **Shell opt-in**: `<body data-shell=\"admin\" data-page=\"<key>\" data-breadcrumb=\"Home > …\">`. The Vite plugin inlines sidebar/topbar/footer at build/dev time (no FOUC).\n- **Breadcrumbs link themselves.** A segment matching a `NAV` item's text becomes a link (`Forms` → `form.html`; a parent resolves to its first child). Override with a pipe: `data-breadcrumb=\"Home > Projects|projects.html > Acme Redesign\"`. The last segment is the current page and never links; untargeted segments stay plain text.\n- **NAV is one constant** — `NAV` in `src/v4/shell-render.js`, 7 groups. `key` matches `data-page`. New icons go in the `ICONS` object in the same file.\n- **Overlays go through helpers**: `showModal()`/`showToast()`/`openMenu()`/`openPanel()` from `src/v4/{modal,toast,menus}.js`. Never hand-roll a backdrop, escape handler, or focus return.\n- **CSS custom properties for colors.** Tokens in `src/scss/v4/_tokens.scss` under `:root` and `[data-theme=\"dark\"]`. Charts read them via `getComputedStyle(document.documentElement).getPropertyValue('--…')` so dark-mode redraw is automatic.\n- **Lazy ECharts.** Match the modular import pattern in `src/v4/charts.js`. Don't `import * as echarts`.\n- **Subpath-safe URLs.** `import.meta.env.BASE_URL` in JS, `${base}` in the Vite plugin, relative paths in `production/*.html`. Never hard-code a leading `/`.\n- **Idempotent `init<Name>()` exports.** Every module in `src/v4/` has one. Safe to call when its root element is absent, safe to call twice.\n- **No `console.*` in shipped code.** Terser drops them in production; ESLint flags earlier.\n- **Service worker only in prod** (`import.meta.env.PROD` guard) — keeps HMR working in dev.\n\n## File layout\n\n- `src/main-v4.js` — entry; mounts shell + lazy-loads page modules\n- `src/scss/v4/` — 10 SCSS partials (`_tokens`, `_layout`, `_components`, `_widgets`, `_forms`, `_datatable`, `_pages`, `_apps`, `_auth`, `main`)\n- `src/v4/shell.js` — `mountShell()` runtime (sidebar accordion, theme toggle, mobile drawer)\n- `src/v4/shell-render.js` — `NAV` + `ICONS` + pure renderers (also imported by Vite plugin)\n- `src/v4/charts.js` — `initCharts()` + ECharts factories\n- `src/v4/tables.js` — `initTables()` + DataTables wrapper\n- `src/v4/command-palette.js` — ⌘K\n- `src/v4/{modal,toast,menus}.js` — overlay helpers\n- `src/v4/{inbox,kanban,calendar,settings,file-manager}.js` — page modules (lazy-loaded)\n- `src/v4/form-controls.js` — date range, multi-select, rich text\n- `production/` — 58 HTML entry pages, auto-discovered\n- `public/` — static assets copied verbatim to `dist/`\n- `types/gentelella.d.ts` — TypeScript declarations for the public JS surface\n- `scripts/new-page.mjs` — page scaffolder (`npm run new -- <slug>`)\n- `scripts/deploy-preview.sh` — R2 deploy with per-file cache headers\n\n## Anti-patterns\n\n- Don't add jQuery, Bootstrap, or a SPA framework.\n- Don't write Vite entry input lists by hand.\n- Don't add `<script>` tags to `production/*.html` for new modules — lazy-import in `src/main-v4.js`.\n- Don't bypass `mountShell()` to wire your own sidebar/topbar.\n- Don't `new bootstrap.Modal(...)` — there is no Bootstrap.\n- Don't hard-code `/` paths in JS/HTML.\n- Don't import all of ECharts — match `src/v4/charts.js`.\n- Don't use hex colors in components — use CSS custom properties.\n- Don't introduce PostCSS, Tailwind, or any pipeline alongside Vite.\n- Don't edit `dist/`, `node_modules/`, or `docs/screenshots/`.\n\n## Commands\n\n```bash\nnpm run dev                # Vite dev server on :9173 (set PORT to override)\nnpm run build              # Production build → dist/\nnpm run preview            # Serve dist/ on :9174\nnpm run lint               # ESLint over src/\nnpm run lint:fix\nnpm run format             # Prettier write\nnpm run new -- <slug>      # Scaffold a page (use --nav-group, --icon, --title …)\nnpm run screenshots        # Playwright capture (22 pages × light+dark)\nnpm run smoke              # Boot dev server, hit every page, assert 200\nnpm run analyze            # Build + open dist/stats.html\nnpm run deploy:preview     # Build + R2 sync with cache headers\n```\n\nBuild under a subpath: `BASE_PATH=/foo/ npm run build`.\n\n## When generating code\n\n- **New page** → use `npm run new -- <slug> --nav-group \"<Group>\"` rather than crafting the HTML by hand. If you do write by hand, copy the head/body pattern from `production/index.html` (specifically the `data-shell`/`data-page`/`data-breadcrumb` triplet and the `<script type=\"module\" src=\"/src/main-v4.js\">` tag).\n- **New chart** → add a `case` to the switch inside `initCharts()` in `src/v4/charts.js`. Read tokens via `getComputedStyle(document.documentElement).getPropertyValue('--…')`.\n- **New page module** → add the lazy-import block at the bottom of `src/main-v4.js`, then create `src/v4/<name>.js` exporting a single idempotent `init<Name>()`.\n- **New NAV entry** → append to the right group in `NAV` in `src/v4/shell-render.js`. `{ key, href, text, icon }`. `key` must match the target page's `data-page`.\n- **New icon** → append to `ICONS` in `src/v4/shell-render.js`. Inline SVG, `currentColor` stroke, 24×24 viewbox.\n- **New modal/toast** → import `showModal` / `showToast` from `src/v4/modal.js` / `src/v4/toast.js`. Don't render backdrops yourself.\n- **New SCSS** → add a partial under `src/scss/v4/` and `@use` it from `main.scss`. Variables live in `_tokens.scss`; new tokens get both a light and a dark value.\n</yakaai_skills>"}