MMOframework
Pourquoi l'IA Playground Bibliothèques Documentation Rechercher Agence
FR EN

SKILL

La documentation du framework est rédigée en anglais.

--- name: mmo-client description: Create or edit ".jhs" files (JavaScript+HTML+Style in one file) for MMO's client-side UI framework (mmo.client, namespace MMO\client / JS namespace MMO.elements.*), i.e. write custom elements (<mmo-xxx>) or standalone views (<view id="...">) using the View/Template reactive-binding runtime and the Simple CSS (.scs) design system — tokens like $color.brand, lib::/controls:: recipes (the old theme.* Sass mixins are gone). Use whenever a file has the .jhs extension, lives under a project's src/customElements/ or theme/jhs/ folder, is a project's theme stylesheet (.scs), or the user asks to create/edit an MMO custom element, an MMO view/template, the theme's styles, or mentions "jhs", "scs", "MMO.elements.View", "MMO.elements.CustomElement/CustomShadowElement", or the mmo.client package. This is the client-side sibling of the `mmoframework` skill (mmo.core, PHP) — use that one for backend code in the same project. --- # mmo.client / JHS `mmo.client` is Marc Mouret's client-side UI library (package `mmo.client`, PHP namespace `MMO\client`, JS namespace `MMO.elements.*` / `MMO.core.*`). It defines Web Components and templates from a single custom file format, **`.jhs`** ("JavaScript, HTML, Style"): one file holds the markup, the element's JS class, and its styles — Simple CSS (`.scs`), the language the whole design system is written in. A PHP compiler (`MMO\client\JHSCompiler`) turns each `.jhs` file into a `.js` file that is then run through Google Closure. Nothing about `.jhs` syntax is standard HTML/JS — it has its own small template DSL (reactive bindings, loops, conditionals) layered on top. This skill was built by reading the compiler source (`src/JHSCompiler.php`, `src/jhs/*.php`), the runtime (`src/js/core/Template.js`, `src/js/elements/View.js`, `src/js/elements/CustomElement.js` and friends), the design system (`src/styles/*.scs`, `src/Theme.php`), and real `.jhs` files in this repo — not from the (partially stale) `docs/JHS.md`. Where they disagree, trust this skill and the source. The public MCP server `https://www.mmoframework.fr/mcp.json` (`claude mcp add --transport http mmoframework https://www.mmoframework.fr/mcp.json`) serves mmo.client's documentation and API from any machine: when it is connected, `search_documentation` for an element, a `.jhs` construct or a `.scs` token before guessing. ## Before you write anything 1. **Decide: custom element or view?** - A **custom element** (`<mmo-xxx>` root tag) is a real Web Component with a shadow DOM, its own attributes/lifecycle, registered globally via `customElements.define`. Use it for anything reusable and self-contained (inputs, widgets, layout pieces). Lives under `src/customElements/{elements,inputs,layouts,modules,tools}/mmo-xxx.jhs` in this repo. - A **view** (`<view id="...">` root tag) is just a named, reusable template fragment bound to a plain data `Map` — no shadow DOM, no custom tag, no lifecycle. Use it for a chunk of markup you want to render into an existing element (`src/customElements/views/*.jhs` in this repo, PascalCase filenames). 2. **Read [jhs-syntax.md](https://mmoframework.fr/search/read?uri=mmoframework-skill%3A%2F%2Fmmo.client%2Fmmo-client%2Freferences%2Fjhs-syntax.md) first** — the template DSL (`{path}` / `{=js}` expressions, `<for>`, `<if>/<elseif>/<else>`, special attributes) is unlike anything in mainstream frameworks and every mistake here is a compile-time or silent-runtime failure. 3. **Pick a base class** for the `<script>` class — see [custom-elements.md](https://mmoframework.fr/search/read?uri=mmoframework-skill%3A%2F%2Fmmo.client%2Fmmo-client%2Freferences%2Fcustom-elements.md#base-classes). `CustomShadowElement` covers ~all cases; `Widget`/`CustomAPIElement` add server-data plumbing. 4. **Styling is Simple CSS and always goes through the design tokens (`$color.brand`, `$space.md`, `$radius.sm`) and the `lib::`/`controls::` recipes**, never raw hex colors or magic-number spacing — see [styling.md](https://mmoframework.fr/search/read?uri=mmoframework-skill%3A%2F%2Fmmo.client%2Fmmo-client%2Freferences%2Fstyling.md). A plain `<style>` is already `.scs` and the compiler prepends the prelude for you: `@import "shadow.scs";` in an element, `@import "controls.scs";` in a view — which emits no rule, since a view rendered in the document adds its css to the whole page. The sass design system (`theme.*` mixins) is archived: don't write sass. 5. **Look at a real, similar element** in `src/customElements/**/*.jhs` before writing one from scratch — [examples.md](https://mmoframework.fr/search/read?uri=mmoframework-skill%3A%2F%2Fmmo.client%2Fmmo-client%2Freferences%2Fexamples.md) walks through three (simple attribute element, element with `<for>`/`<if>`, standalone view). ## Reference map | Reference | Load it when you're... | |---|---| | [jhs-syntax.md](https://mmoframework.fr/search/read?uri=mmoframework-skill%3A%2F%2Fmmo.client%2Fmmo-client%2Freferences%2Fjhs-syntax.md) | writing markup: expressions, `<for>`, `<if>`, `<view>`, `<fn>`, special attributes (`value`, `set-xxx`, `bind-xxx`, `on*`, `jhs-compile`) | | [custom-elements.md](https://mmoframework.fr/search/read?uri=mmoframework-skill%3A%2F%2Fmmo.client%2Fmmo-client%2Freferences%2Fcustom-elements.md) | writing the `<script>` class: base classes, lifecycle, attributes↔properties, `documentation()` JSDoc tags | | [views-and-data.md](https://mmoframework.fr/search/read?uri=mmoframework-skill%3A%2F%2Fmmo.client%2Fmmo-client%2Freferences%2Fviews-and-data.md) | rendering a view from JS, or working with `MMO.core.Map` / `MMO.elements.View`'s reactive `get/set/watch/loop` API | | [styling.md](https://mmoframework.fr/search/read?uri=mmoframework-skill%3A%2F%2Fmmo.client%2Fmmo-client%2Freferences%2Fstyling.md) | writing `<style>` or a project's `.scs` stylesheet: tokens, `lib::`/`controls::` recipes, shadow-DOM vs light-DOM context, `lang="sass"`, reskinning | | [examples.md](https://mmoframework.fr/search/read?uri=mmoframework-skill%3A%2F%2Fmmo.client%2Fmmo-client%2Freferences%2Fexamples.md) | you want a complete, working file to copy from | | Guide.md | adding a guided tour: a page's `{page}.tour.json` (filtered by rights, translated, by display level) or an element's `MMO.guide.add()`, the `?` menu of the top bar | | Modes.md | showing more or less by display level (`MMO.modes`: simple, standard, expert) | ## The things that matter most 1. **The root tag name IS the contract.** For a custom element it must start with `mmo-` and contain a hyphen (`<mmo-foo>`), and its `<script>` must define `class Foo { ... }` (PascalCase, `mmo-` prefix stripped) or `class Foo extends SomeBase { ... }` — the compiler finds this class by regex and rewrites `class Foo` into `class MMO_Foo_JHS extends CustomShadowElement(HTMLElement) { ...your body... }`, auto-injecting a generated `render($view)` from your markup and a generated `documentation()` from your JSDoc. Get the class name wrong and compilation fails with "cannot find class". See [custom-elements.md](https://mmoframework.fr/search/read?uri=mmoframework-skill%3A%2F%2Fmmo.client%2Fmmo-client%2Freferences%2Fcustom-elements.md). 2. **Root-tag attributes become `defaultAttributes`/`observedAttributes` automatically** — `<mmo-radio readonly value required>` makes `readonly`, `value`, `required` observed attributes with no explicit `static get observedAttributes()` needed. An attribute written bare (no `="..."`) defaults to a boolean-ish `null` default; write a getter/setter pair (`get value()`/`set value(v)`) to control how the raw attribute string is coerced and stored in `this.view`. 3. **`{path}` is a reactive one-way bind to the data Map; `{=js}` is inline JS with those paths as free variables; `value=` is the one special attribute that's two-way by default** (input/select/textarea, and `set-xxx` on another `mmo-*` element). Nothing else is two-way unless you use `bind-text`/`bind-html` (binds on the element's `blur` event) or wire it yourself in the `<script>`. 4. **Raw HTML insertion is a real XSS sink — and `{path}` in text content is one.** The compiler's text default is html, so `{path}` and `{=...}` in text content compile to `.$html()`/`.$htmlFn()`, not `$text`: like `bind-html`, `jhs-compile="html"`, or `<script>` code that `this.view.set()`s a field bound that way, the value ends up in `element.innerHTML`, unescaped. Only ever let content you control (static template text, developer-written config) or sanitized HTML reach it — never raw user/API text. For anything untrusted use `bind-text="path"`, `set-textContent="{path}"`, or `textContent`/`.text()`/`$view.$text()` from `<script>`/`<fn>`, which build text nodes — [jhs-syntax.md](https://mmoframework.fr/search/read?uri=mmoframework-skill%3A%2F%2Fmmo.client%2Fmmo-client%2Freferences%2Fjhs-syntax.md#expressions-path-and-js) has what each compiles to and its caveats. The DOM helpers are the other way round, text by default: a string child of `MMO.core.dom.createElement()`/`appendChildren()` is a text node, and so is what `MMO.app.notify()`/`alert()`/`confirm()`/`ask()`/`error()`/`showModal()`/`showPanel()` show — only markup you wrote goes through `MMO.core.dom.html('…')`. mmo.client's `docs/SECURITY_REVIEW.md` §1–§3 traces these sinks and proposes the compiler fix. 5. **Don't hand-write what the compiler derives.** Don't write `static get observedAttributes()` if `defaultAttributes`/bare root-tag attributes already cover it; don't write a `render()` method (the compiler generates one from your markup — if you also define one in `<script>`, yours is silently overwritten since the compiler's `render($view){...}` is inserted as a class member alongside your injected body, and JS uses last-definition-wins for duplicate method names — so never name a method `render` in the `<script>` block). ## What the library already gives a page Before writing an element, check that mmo.client (or a library built on it) does not ship it: - **Guided tours** — `MMO.guide` / `MMO\client\Guide`: a `{page}.tour.json` beside a `.phtml`, or `MMO.guide.add()` from an element; a tour only shows, never clicks or saves. Guide.md. - **Display levels** — `MMO.modes` (`level`, `atLeast()`, `set()`), the `level`/`levels` of a tour, a schema field's `level`. Modes.md. - **Long jobs** — `<mmo-job job="name">` follows a job of mmo.core's `JobController` live (Server-Sent Events): its progress, log, result; mmo.core's `docs/Jobs.md`. Never poll a long request from an element. - **Workflows, lists, tables, forms** — `<mmo-workflow>` (Workflow.md), `<mmo-list>`, `<mmo-table>`, `<mmo-select>`, `<mmo-validity>`, `<mmo-password>`, `<mmo-kanban>`: the demo (`http://localhost/MMO/master/libs/client`) shows each one with its attributes. - **Rich text and pages built from sections** — mmo.editor: `<mmo-editor>` (a form field, or the page edited in place) and `<mmo-page-editor>` (a CMS backoffice). Its `docs/mcp/Overview.md` and `docs/mcp/Pages.md`. ## A page's html: the design system's classes A page (`src/pages/**.phtml`) is plain html the theme already styles: write the elements and these classes, no stylesheet of your own for a form or a table. The whole list is `docs/SCS_DESIGN_SYSTEM.md` §5; the essentials: ```html <form method="post" action="..."> <label class="invalid">Customer <input name="customer" required/> <span class="error-message">The customer is required.</span></label> <!-- .helper for a hint --> <div class="field-row"><label>Quantity <input type="number" name="q"/></label> <label>Unit price <input type="number" step="0.01" name="p"/></label></div> <button type="submit" class="primary">Create</button> <a class="button" href="...">Cancel</a> </form> <div class="callout error"><span class="title">Not created</span> ...</div> <!-- .info .success .warning --> <div class="table-wrap"><table class="data"> <thead><tr><th>Number</th><th class="text-right">Total</th></tr></thead> <tbody><tr><td><a href="...">2026-0001</a></td><td class="currency">1 250,00 €</td></tr></tbody> </table></div> <!-- .table-empty: none yet --> <article class="card"><header><h3>Lines</h3></header> ... </article> <!-- .cards: a grid of them --> ``` Tones: `.primary .secondary .success .warning .error .info` (and `.text-*`); layout: `.hflex .vflex .gap .wrap .width-1-2`; text: `.text-right .text-muted .number .currency .date`. Icons: `<mmo-icon name="file-lines">`, the theme's sprite (`www/bin/mmo.client.svg`). ## Compiling / testing a change There's no dev server to "run" — a `.jhs` file only matters once compiled. From PHP (see `src/JHSCompiler.php`): ```php \MMO\client\JHSCompiler::Shared()->compile($outputJsFile, ['path/to/my-element.jhs']); // or, to compile a string and see the generated JS directly (useful for debugging a syntax error): \MMO\client\JHSCompiler::Shared()->compileText($jhsSourceString, debug: true); ``` `compileText` with `debug: true` gives readable, indented output — use it to sanity-check what your markup/loop/if actually compiles to before wiring it into the real build; the element's css is the string in its `.add('style', null, '…')` call. Both methods also take `$sassOpts` and `$scsOpts` (the latter merged over `JHSCompiler::$scsOptions`); an element styled in `.scs` needs neither. A style error — Simple CSS (`SCSCompiler::renderError`) or a `lang="sass"` block (`eSass::getErrorCss`) — is caught and rendered as visible error CSS in the element rather than failing the whole compile — don't mistake "the page loaded" for "the styles compiled cleanly." To actually see a compiled element/page render, run the site and navigate/curl it: `php -S localhost:8000 -t www www/index.php` from the project's folder (it serves the pages and the files under `www/`), or the web server already serving it -- where Apache serves a folder of projects, a project is at its path below it (`~/dev/src/acme.portal` is `http://localhost/acme.portal/`). A library's demo app is its `demo/www` (`libs/client/demo/www`: every element, live). A project behind a login (mmo.oauth) is opened with the link `php admin.php agent-login --user=<username> --url=<site url> -m` prints, never by typing a password: see the `mmo-agent-login` skill. In a test, `$this->http('GET', 'invoices')` asks for a page without any server (mmo.core's `docs/Testing.md`). For one-off CLI scripts (not HTML output), address a library's files through the project's `libs/` link, e.g. `php libs/client/demo/www/index.php`. ## End of a task: commit and push When a task is done, ask whether the changes should be committed and pushed. Never commit or push without the user's explicit yes, which covers only that commit. Whatever else a team does at the end of a task (a ticket, a checklist) is said in the project's own `AGENTS.md`, or the user's own instructions -- not here.