Skip to main content

sui-development

Guide for developing web applications using Yao SUI framework. Use when building SUI pages, components, templates, handling data binding, event handling, backend scripts, routing, i18n, or integrating with CUI. Trigger when user mentions SUI, Yao web development, page templates, or frontend/backend integration in Yao applications.

Ir a la instalación

Datos de origen

Repositorio
YaoApp/yao-init
Última actividad en el origen
5 de abril de 2026 a las 12:49
Idioma detectado de SKILL.md
inglés
Estrellas
1
Forks
4

Opciones de instalación

De forma predeterminada está seleccionado el prompt que primero revisa el origen. Puedes cambiar a un comando directo o descargar una copia local.

Revisa los archivos de origen

Lee SKILL.md y los archivos complementarios que muestra SkillsMP antes de decidir si quieres instalarlo.

Explorador de archivos
4 archivos

Mostrando SKILL.md

SKILL.md
Instrucciones de origen · Vista previa de solo lectura
name
sui-development
description
Guide for developing web applications using Yao SUI framework. Use when building SUI pages, components, templates, handling data binding, event handling, backend scripts, routing, i18n, or integrating with CUI. Trigger when user mentions SUI, Yao web development, page templates, or frontend/backend integration in Yao applications.
# SUI Development Guide SUI (Simple UI) is Yao's built-in web framework for building server-rendered pages with reactive components. ## Core Concepts 1. **File-based routing** - Directory structure defines URL routes 2. **Page = Component** - Every page can be used as a component 3. **Server-side rendering** - Templates rendered on server with data binding 4. **Progressive enhancement** - Frontend scripts add interactivity ## Directory Structure ``` /suis/<sui>/templates/<template>/ ├── __document.html # Global document wrapper ├── __data.json # Global data ($global) ├── __assets/ # Static assets ├── __locales/ # i18n files └── pages/ └── <page>/ # Route = folder name ├── <page>.html # Template ├── <page>.css # Styles (auto-scoped) ├── <page>.ts # Frontend script ├── <page>.json # Page data config ├── <page>.config # Page settings (guard, cache, SEO) └── <page>.backend.ts # Server-side logic ``` ## Build Commands ```bash yao sui build <sui> <template> # Build for production yao sui build <sui> <template> -D # Development mode yao sui watch <sui> <template> # Watch mode yao sui trans <sui> <template> # Extract i18n strings ``` --- # Template Syntax ## Data Interpolation ```html {{ name }} <!-- Variable --> {{ user.email }} <!-- Nested --> {{ title ?? 'Default' }} <!-- Default value --> {{ price * quantity }} <!-- Expression --> {{ count > 0 ? 'Yes' : 'No' }} <!-- Ternary --> ``` ## Conditionals ```html <div s:if="{{ isActive }}">Active</div> <div s:elif="{{ isPending }}">Pending</div> <div s:else>Unknown</div> <!-- Operators: ==, !=, >, <, >=, <=, &&, ||, ! --> <div s:if="{{ isAdmin && isActive }}">Admin Panel</div> ``` ## Loops ```html <li s:for="{{ items }}" s:for-item="item" s:for-index="i"> {{ i + 1 }}. {{ item.name }} </li> <!-- With conditional --> <div s:for="{{ users }}" s:for-item="user" s:if="{{ user.active }}"> {{ user.name }} </div> ``` ## Attribute Binding ```html <a href="{{ '/user/' + id }}">Link</a> <button s:attr-disabled="{{ !valid }}">Submit</button> <div class="base {{ isActive ? 'active' : '' }}">Content</div> <div ...props></div> <!-- Spread --> ``` ## Built-in Variables | Variable | Description | Example | | ---------- | ------------------------ | --------------------- | | `$param` | Route parameters | `{{ $param.id }}` | | `$query` | URL query params | `{{ $query.search }}` | | `$payload` | POST body | `{{ $payload.name }}` | | `$global` | Global data | `{{ $global.title }}` | | `$theme` | Current theme | `{{ $theme }}` | | `$locale` | Current locale | `{{ $locale }}` | | `$auth` | OAuth info (when guarded)| `{{ $auth.user_id }}` | ## Built-in Functions ```html {{ P_('models.user.Find', id) }} <!-- Call process --> {{ True(user) }} <!-- Truthy check --> {{ Empty(items) }} <!-- Empty check --> {{ len(items) }} <!-- Array length --> {{ filter(items, .active) }} <!-- Filter array --> ``` --- # Data Binding ## Page Data (`<page>.json`) ```json { "title": "Static value", "$users": "models.user.Get", "$user": { "process": "models.user.Find", "args": ["$param.id"] }, "$items": { "process": "@GetItems", "args": ["active", 10] } } ``` - Keys with `$` prefix call processes - `@MethodName` calls backend script functions - Use `$param`, `$query`, `$payload` in args ## Backend Script (`<page>.backend.ts`) There are three types of backend functions, each with different auth mechanisms: ```typescript // Type declarations for auth interface AuthorizedInfo { user_id?: string; email?: string; team_id?: string; owner_id?: string; } declare function Authorized(): AuthorizedInfo | null; // 1. BeforeRender — called before page render // Auth: request.authorized (request is the first arg) function BeforeRender(request: Request): Record<string, any> { const id = request.params.id; // NOT $param.id return { user: Process("models.user.Find", id), items: Process("models.item.Get", { limit: 10 }), }; } // 2. ApiXxx — called via $Backend().Call("GetUsers") // Auth: Authorized() global function (request is NOT passed) // Args: only what frontend sends function ApiGetUsers(): any[] { const auth = Authorized(); if (!auth?.user_id) throw new Error("unauthorized"); return Process("models.user.Get", { wheres: [{ column: "user_id", value: auth.user_id }], }); } // 3. @FuncName — called from .json data binding // Auth: request.authorized (request is appended as last arg by SUI core) function GetRecord(request: Request): any { return Process("models.record.Find", request.params.id); } ``` > **Important**: `$param` is NOT available in backend scripts. Use `request.params`. > > **CRITICAL**: `ApiXxx` functions called via `$Backend().Call` do NOT receive a `request` > parameter. Use the `Authorized()` global function for user identity. See > `references/backend-api.md` for the full explanation and Go runtime source references. --- # Components Every page is a component. Use `is` attribute or `<import>` to embed: ```html <!-- Using is attribute --> <div is="/card" title="My Card"> <p>Content</p> </div> <!-- Using import --> <import s:as="Card" s:from="/card" /> <Card title="My Card"><p>Content</p></Card> ``` ## Component Structure **`/card/card.html`**: ```html <div class="card"> <h3>{{ title }}</h3> <div class="body"><children></children></div> </div> ``` ## Named Slots ```html <!-- Component --> <div class="modal"> <div class="header"><slot name="header"></slot></div> <div class="body"><children></children></div> <div class="footer"><slot name="footer"></slot></div> </div> <!-- Usage --> <div is="/modal"> <slot name="header"><h2>Title</h2></slot> <p>Body content</p> <slot name="footer"><button>OK</button></slot> </div> ``` ## Props Access **Frontend** (`card.ts`): ```typescript import { Component } from "@yao/sui"; const self = this as Component; const title = self.props.Get("title"); const allProps = self.props.List(); ``` **Backend** (`card.backend.ts`): ```typescript function BeforeRender(request: Request, props: Record<string, any>): Record<string, any> { const userId = props.userId; return { user: Process("models.user.Find", userId) }; } ``` --- # Event Handling ## Event Binding ```html <button s:on-click="HandleClick">Click</button> <input s:on-input="HandleInput" /> <form s:on-submit="HandleSubmit">...</form> <!-- With data --> <button s:on-click="DeleteItem" s:data-id="{{ item.id }}" s:json-item="{{ item }}"> Delete </button> ``` ## Event Handler ```typescript import { $Backend, Component, EventData } from "@yao/sui"; const self = this as Component; self.DeleteItem = async (event: Event, data: EventData) => { const id = data.id; // From s:data-id const item = data.item; // From s:json-item await $Backend().Call("DeleteItem", id); (event.target as HTMLElement).closest(".item")?.remove(); }; self.HandleSubmit = async (event: Event) => { event.preventDefault(); const form = event.target as HTMLFormElement; const formData = new FormData(form); await $Backend().Call("Submit", Object.fromEntries(formData)); }; ``` ## State Management ```typescript const self = this as Component; // Set state self.state.Set("count", 0); // Watch changes self.watch = { count: (value: number) => { self.root.querySelector(".count")!.textContent = String(value); }, }; // Trigger update self.Increment = () => { const count = self.state.Get("count") || 0; self.state.Set("count", count + 1); }; ``` --- # Frontend API ## Backend Calls ```typescript import { $Backend } from "@yao/sui"; // Calls ApiGetUsers() in backend script — no request param is passed const users = await $Backend().Call("GetUsers"); // Calls ApiGetUser(123) — args are passed positionally const user = await $Backend().Call("GetUser", 123);
Ver en GitHub
Este SKILL.md es muy grande, por eso SkillsMP muestra aqui solo la primera seccion. Ver en GitHub