feat: enhance project setup and architecture documentation

- Updated `tibi-project-setup` skill to clarify project initialization goals and steps.
- Improved `tibi-ssr-caching` skill to detail SSR architecture, responsibilities, and caching mechanisms.
- Introduced `website-solution-architecture` skill for translating website requirements into coherent solutions.
- Refined `AGENTS.md` to provide a structured roadmap for project development phases.
- Added `ADMIN_ASSET_VERSION` to `api/config.yml.env` for asset versioning.
- Updated SSR request flow and cache invalidation logic in `api/hooks/ssr/AGENTS.md`.
- Removed obsolete `esbuild.config.admin.js` and integrated asset versioning into the main `esbuild.config.js`.
- Adjusted `api/collections/content.yml` to utilize asset versioning for admin scripts.
This commit is contained in:
2026-05-12 20:01:22 +00:00
parent 4a604bab0b
commit 491f495c66
23 changed files with 3189 additions and 225 deletions
+88 -25
View File
@@ -19,19 +19,23 @@ Use this skill when:
This project does **NOT** use file-based routing (no SvelteKit router). Instead:
1. Pages are **CMS entries** in the `content` collection with a `path` field.
2. `App.svelte` reacts to URL changes → calls `getCachedEntries("content", { lang, path, active: true })`.
3. The matching `ContentEntry.blocks[]` array is passed to `BlockRenderer.svelte`.
4. Each block has a `type` field that maps to a Svelte component.
2. Public URLs are typically language-prefixed (`/de/...`, `/en/...`), but the DB entry in `content.path` is stored **without** that language prefix.
3. `App.svelte` reacts to URL changes → strips the language prefix → calls `getCachedEntries("content", { lang, path, active: true })`.
4. The same loading path is used for browser navigation and SSR.
5. The matching `ContentEntry.blocks[]` array is passed to `BlockRenderer.svelte`.
6. Each block has a `type` field that maps to a Svelte component.
**Implication:** To add a new page, you create a content entry (via Admin UI or API) — no new Svelte file or route config is needed.
**Important:** When adding new page types, inspect both the frontend route/i18n layer and `api/hooks/config.js` (SSR route validation). A page can exist in the DB and still fail under SSR if the public URL shape and `content.path` mapping are not aligned.
---
## Adding a new page
### Option A: Via Admin UI (preferred for content editors)
1. Open the tibi-admin at `CODING_URL/_/admin/`.
1. Open the Nova admin at `https://{PROJECT_NAME}-tibiadmin.code.testversion.online/`.
2. Navigate to **Inhalte** (Content) collection.
3. Click **New** and fill in:
- `name`: Display name (e.g. "Über uns")
@@ -43,6 +47,13 @@ This project does **NOT** use file-based routing (no SvelteKit router). Instead:
- `meta.title` / `meta.description`: SEO metadata
4. Save. The page is immediately available at `/{lang}{path}`.
**Nova authoring guidance:**
- Prefer meaningful `meta.preview` and field `preview` configs so entries and nested blocks are understandable in breadcrumbs, foreign-key widgets, and arrays.
- Use `containerProps.layout.size` to keep editors on one screen instead of stacking every field vertically.
- Use `dependsOn` to hide block-specific fields until the relevant block type is selected.
- Prefer drill-down editing for larger `object[]` structures instead of flat, folded arrays.
### Option B: Via API
```sh
@@ -74,17 +85,22 @@ To make the page appear in the header/footer menu, edit the corresponding `navig
# Get existing header nav
curl "$CODING_URL/api/navigation?filter[type]=header&filter[language]=de" -H "Token: $ADMIN_TOKEN"
# PUT to update elements array (add your page)
# Look up the content entry ID for your page
curl "$CODING_URL/api/content?filter[path]=/ueber-uns&filter[lang]=de" -H "Token: $ADMIN_TOKEN"
# PUT to update elements array (add your page by FK id)
curl -X PUT "$CODING_URL/api/navigation/<id>" \
-H "Token: $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "elements": [ ...existing, { "name": "Über uns", "page": "/ueber-uns" } ] }'
-d '{ "elements": [ ...existing, { "name": "Über uns", "page": "<content-id>" } ] }'
```
If navigation drives the public website shell, treat navigation as page-critical SSR data. A page is not fully SSR-ready if only the main content entry exists but header/footer navigation is missing.
### Multi-language pages
- Create one `ContentEntry` per language with the **same `translationKey`** but different `lang` and `path`.
- The language switcher in `App.svelte` uses `currentContentEntry.translationKey` to find the equivalent page.
- The language switcher is path-based and derives the target URL from the current route plus `ROUTE_TRANSLATIONS`.
- Add localized route slugs to `ROUTE_TRANSLATIONS` in `frontend/src/lib/i18n.ts` if URLs should differ per language (e.g. `/ueber-uns` vs `/about`).
---
@@ -130,6 +146,8 @@ import MyNewBlock from "./MyNewBlock.svelte"
<MyNewBlock {block} />
```
If block types become numerous, plan for grouping and registry discipline early. A real website built on this starter should not keep extending a demo-style renderer forever without structure.
### Step 3: Extend TypeScript types (if new fields are needed)
Edit `types/global.d.ts` — add fields to `ContentBlockEntry`:
@@ -156,6 +174,9 @@ Edit `api/collections/content.yml` — add subFields under `blocks`:
type: string
- name: myItems
type: object[]
meta:
drillDown: true
preview: title
subFields:
- name: title
type: string
@@ -163,6 +184,14 @@ Edit `api/collections/content.yml` — add subFields under `blocks`:
type: string
```
Use current Nova patterns when extending block schemas:
- `meta.preview` for entry and block previews
- `meta.drillDown: true` for nested arrays that would otherwise become hard to edit
- `containerProps.layout.size` for dense editor layouts
- `dependsOn` for block-type-specific fields
- collection- or field-level `meta.pagebuilder` for registry/default viewport settings
### Step 5: Update mock data (if using MOCK=1)
Add a block with your new type to `frontend/mocking/content.json`.
@@ -173,6 +202,13 @@ Add a block with your new type to `frontend/mocking/content.json`.
yarn validate # TypeScript check — must be warning-free
```
For blocks that appear on SSR pages, also verify:
```sh
yarn build:server
# then request the SSR endpoint directly and check that the block content appears in HTML
```
### Existing block types for reference
| Type | Component | Purpose |
@@ -188,7 +224,7 @@ yarn validate # TypeScript check — must be warning-free
### Step 1: Create collection YAML
Create `api/collections/mycollection.yml`. Use `content.yml` or `navigation.yml` as a template:
Create `api/collections/mycollection.yml`. Use `content.yml`, `navigation.yml`, or a current `tibi-admin-nova` example config as a template:
```yaml
########################################################################
@@ -199,17 +235,13 @@ name: mycollection
meta:
label: { de: "Meine Sammlung", en: "My Collection" }
muiIcon: category # Material UI icon name
rowIdentTpl: { twig: "{{ name }}" } # Row display in admin list
views:
- type: simpleList
mediaQuery: "(max-width: 600px)"
primaryText: name
- type: table
columns:
- name
- source: active
filter: true
viewHint: table
preview:
label: name
table:
- name
- source: active
label: Active
permissions:
public:
@@ -234,9 +266,17 @@ fields:
# Add more fields as needed
```
Use current Nova config:
- `preview` for row/foreign/search display
- object-form `singleton`
- `sidebar` groups instead of ad hoc sidebars
- `pagebuilder` defaults when a collection contains pagebuilder fields
- `viewHint` plus `preview.table` for better admin ergonomics
**Field types:** `string`, `number`, `boolean`, `object`, `object[]`, `string[]`, `file`, `file[]`.
For the full schema reference: `tibi-types/schemas/api-config/collection.json`.
For the full schema reference: `tibi-types/schemas/config/collection.schema.json`.
### Step 2: Include in config.yml
@@ -285,17 +325,30 @@ type EntryTypeSwitch<T extends string> = T extends "medialib"
Common hook patterns:
- **Public filter** — reuse `filter_public.js` to enforce `active: true` for unauthenticated users.
- **Before-save validation** — create `api/hooks/mycollection_validate.js`.
- **Write validation** — add method/step hook files such as `api/hooks/mycollection/post_validate.js` or `api/hooks/mycollection/put_validate.js`.
- **Cache invalidation** — add your collection to `api/hooks/clear_cache.js` if it affects rendered pages.
- **Action endpoints** — prefer `actions:` instead of fake collections when you need forms, newsletters, calculators, imports, or other endpoint-like behavior without CRUD storage.
Reference hook in YAML:
```yaml
hooks:
beforeRead: |
!include hooks/filter_public.js
afterWrite: |
!include hooks/clear_cache.js
get:
read:
type: javascript
file: hooks/filter_public.js
put:
update:
type: javascript
file: hooks/clear_cache.js
post:
create:
type: javascript
file: hooks/clear_cache.js
delete:
delete:
type: javascript
file: hooks/clear_cache.js
```
### Step 6: Add mock data (if using MOCK=1)
@@ -313,6 +366,14 @@ yarn validate # TypeScript check
# If Docker is running, the tibi-server auto-reloads the collection config
```
For collections intended for rich editorial usage, also verify in Nova:
- list/table/card previews are readable
- nested arrays are editable with drill-down where needed
- sidebar groups and layout are usable without scrolling through one long form
- foreign-key displays use meaningful previews
- pagebuilder fields render previews and screenshots correctly
---
## Common pitfalls
@@ -322,3 +383,5 @@ yarn validate # TypeScript check
- **Block `hide` field**: Blocks with `hide: true` are skipped by `BlockRenderer.svelte` — useful for draft blocks.
- **Collection YAML indentation**: YAML uses 2-space indentation. Sub-fields under `object[]` require a `subFields` key.
- **After adding a collection**: The tibi-server auto-reloads hooks on file change, but a new collection in `config.yml` may require `make docker-restart-frontend` or a full `make docker-up`.
- **Do not fake forms as collections** if they are really endpoint logic. Use `actions:` when no CRUD collection is needed.
- **Do not overfit to demo blocks**. Real projects should shape block schemas and admin ergonomics around actual editor workflows.