diff --git a/agent/docs/iframe.md b/agent/docs/iframe.md index b8b74eba..512f2d2f 100644 --- a/agent/docs/iframe.md +++ b/agent/docs/iframe.md @@ -47,8 +47,8 @@ When the iframe loads, CUI sends a `setup` message: // In your page script window.addEventListener("message", (e) => { if (e.data.type === "setup") { - const { theme, locale, token } = e.data.message; - // Apply theme, store token, set locale + const { theme, locale } = e.data.message; + // Apply theme, set locale document.documentElement.setAttribute("data-theme", theme); } }); @@ -165,7 +165,7 @@ window.addEventListener("message", (e) => { switch (type) { case "setup": - // Initial setup with theme, locale, token + // Initial setup with theme, locale break; case "refresh": // CUI requests page refresh diff --git a/agent/docs/mcp.md b/agent/docs/mcp.md index 9e0a0966..af33e977 100644 --- a/agent/docs/mcp.md +++ b/agent/docs/mcp.md @@ -68,6 +68,16 @@ Map Yao Processes directly to MCP tools: } ``` +**HTTP (REST API)** + +```json +{ + "transport": "http", + "url": "https://mcp.example.com/api", + "authorization_token": "$ENV.TOKEN" +} +``` + **SSE (Server-Sent Events)** ```json diff --git a/agent/docs/pages.md b/agent/docs/pages.md index c91e6ac6..58cb459d 100644 --- a/agent/docs/pages.md +++ b/agent/docs/pages.md @@ -182,7 +182,7 @@ const self = this as Component; // Event handler bound to s:on-click="HandleClick" self.HandleClick = async (event: Event, data: EventData) => { - const result = await $Backend().Call("ApiGetData", data.id); + const result = await $Backend().Call("GetData", data.id); console.log(result); }; @@ -191,7 +191,7 @@ self.HandleSubmit = async (event: Event) => { event.preventDefault(); const form = event.target as HTMLFormElement; const formData = new FormData(form); - await $Backend().Call("ApiSubmit", Object.fromEntries(formData)); + await $Backend().Call("Submit", Object.fromEntries(formData)); }; ``` @@ -201,7 +201,7 @@ self.HandleSubmit = async (event: Event) => { import { $Backend, Yao } from "@yao/sui"; // Call backend method -const data = await $Backend().Call("ApiMethodName", arg1, arg2); +const data = await $Backend().Call("MethodName", arg1, arg2); // Direct API calls const yao = new Yao(); @@ -550,7 +550,7 @@ ctx.Send({ import { $Backend, Yao } from "@yao/sui"; // Call backend method defined in .backend.ts -const data = await $Backend().Call("ApiMethodName", arg1, arg2); +const data = await $Backend().Call("MethodName", arg1, arg2); // Direct API calls const yao = new Yao(); diff --git a/sui/docs/agent-sui.md b/sui/docs/agent-sui.md index a04a7e41..29ffa803 100644 --- a/sui/docs/agent-sui.md +++ b/sui/docs/agent-sui.md @@ -273,7 +273,7 @@ const self = this as Component; // Event handler bound to s:on-click="HandleClick" self.HandleClick = async (event: Event, data: EventData) => { - const result = await $Backend().Call("ApiGetData", data.id); + const result = await $Backend().Call("GetData", data.id); console.log(result); }; @@ -282,7 +282,7 @@ self.HandleSubmit = async (event: Event) => { event.preventDefault(); const form = event.target as HTMLFormElement; const formData = new FormData(form); - await $Backend().Call("ApiSubmit", Object.fromEntries(formData)); + await $Backend().Call("Submit", Object.fromEntries(formData)); }; ``` diff --git a/sui/docs/backend-scripts.md b/sui/docs/backend-scripts.md index a163d16b..eca2fa4f 100644 --- a/sui/docs/backend-scripts.md +++ b/sui/docs/backend-scripts.md @@ -58,20 +58,20 @@ function BeforeRender(request: Request): Record { ## API Methods -Functions prefixed with `Api` are exposed as callable endpoints: +Functions prefixed with `Api` are exposed as callable endpoints. The backend automatically adds the `Api` prefix, so frontend calls use the method name without the prefix: ```typescript -// Callable from frontend as: this.backend.ApiGetUsers() +// Callable from frontend as: $Backend().Call("GetUsers") function ApiGetUsers(request: Request): any[] { return Process("models.user.Get", {}); } -// Callable from frontend as: this.backend.ApiCreateUser(name, email) +// Callable from frontend as: $Backend().Call("CreateUser", name, email) function ApiCreateUser(name: string, email: string, request: Request): any { return Process("models.user.Create", { name, email }); } -// Callable from frontend as: this.backend.ApiDeleteUser(id) +// Callable from frontend as: $Backend().Call("DeleteUser", id) function ApiDeleteUser(id: string, request: Request): boolean { Process("models.user.Delete", id); return true; @@ -81,19 +81,20 @@ function ApiDeleteUser(id: string, request: Request): boolean { ### Calling from Frontend ```typescript -function Page(component: HTMLElement) { - this.root = component; +import { $Backend, Component } from "@yao/sui"; - this.loadUsers = async () => { - const users = await this.backend.ApiGetUsers(); - console.log(users); - }; +const self = this as Component; - this.createUser = async () => { - const user = await this.backend.ApiCreateUser("John", "john@example.com"); - console.log("Created:", user); - }; -} +self.LoadUsers = async () => { + // Call "ApiGetUsers" in backend script (without "Api" prefix) + const users = await $Backend().Call("GetUsers"); + console.log(users); +}; + +self.CreateUser = async () => { + const user = await $Backend().Call("CreateUser", "John", "john@example.com"); + console.log("Created:", user); +}; ``` ## Constants @@ -115,10 +116,12 @@ const __sui_constants = { Access in frontend: ```typescript -function Page(component: HTMLElement) { - console.log(this.constants.API_URL); // "/api/v1" - console.log(this.constants.MAX_ITEMS); // 100 -} +import { Component } from "@yao/sui"; + +const self = this as Component; + +console.log(self.constants.API_URL); // "/api/v1" +console.log(self.constants.MAX_ITEMS); // 100 ``` ## Helpers @@ -147,11 +150,13 @@ function validateEmail(email: string): boolean { Access in frontend: ```typescript -function Page(component: HTMLElement) { - const formatted = this.helpers.formatDate("2024-01-15"); - const price = this.helpers.formatCurrency(99.99); - const isValid = this.helpers.validateEmail("test@example.com"); -} +import { Component } from "@yao/sui"; + +const self = this as Component; + +const formatted = self.helpers.formatDate("2024-01-15"); +const price = self.helpers.formatCurrency(99.99); +const isValid = self.helpers.validateEmail("test@example.com"); ``` ## Request Object diff --git a/sui/docs/components.md b/sui/docs/components.md index 5a78be84..befe4a2c 100644 --- a/sui/docs/components.md +++ b/sui/docs/components.md @@ -43,11 +43,13 @@ A component is just a page with a single root element: **`/card/card.ts`**: ```typescript -function card(component: HTMLElement) { - this.root = component; - this.store = new __sui_store(component); - this.props = new __sui_props(component); -} +import { Component } from "@yao/sui"; + +const self = this as Component; + +// self.root - Root element +// self.store - Data store +// self.props - Props from attributes ``` ## Using Components @@ -94,17 +96,16 @@ Props are passed as attributes: Access props in the component script: ```typescript -function userCard(component: HTMLElement) { - this.root = component; - this.props = new __sui_props(component); +import { Component } from "@yao/sui"; - // Get single prop - const name = this.props.Get("name"); +const self = this as Component; - // Get all props - const allProps = this.props.List(); - // { name: "John", email: "john@example.com", avatar: "...", role: "admin" } -} +// Get single prop +const name = self.props.Get("name"); + +// Get all props +const allProps = self.props.List(); +// { name: "John", email: "john@example.com", avatar: "...", role: "admin" } ``` Access props in backend script: @@ -221,7 +222,7 @@ self.watch = { // Event handlers (bound to s:on-click="HandleClick") self.HandleClick = async (event: Event, data: EventData) => { - const result = await $Backend().Call("ApiMethod", data.id); + const result = await $Backend().Call("Method", data.id); // Handle result }; ``` @@ -339,7 +340,6 @@ Component CSS is automatically scoped using namespace attributes: ## Important Notes 1. **Single Root Element**: Components must have exactly one root element -2. **Route as Identifier**: The page route becomes the component name (e.g., `/card` → `card()`) -3. **Scoped Styles**: CSS is automatically scoped to prevent conflicts -4. **Recursive Prevention**: SUI detects and prevents recursive component inclusion -5. **Script Naming**: Function name is derived from the route path +2. **Scoped Styles**: CSS is automatically scoped to prevent conflicts +3. **Recursive Prevention**: SUI detects and prevents recursive component inclusion +4. **Component Pattern**: Use `const self = this as Component` to access component APIs diff --git a/sui/docs/event-handling.md b/sui/docs/event-handling.md index 29730ff4..8313de9a 100644 --- a/sui/docs/event-handling.md +++ b/sui/docs/event-handling.md @@ -121,7 +121,7 @@ self.DeleteItem = async (event: Event, data: EventData) => { const item = data.item; // Object from s:json-item if (confirm(`Delete ${item.name}?`)) { - await $Backend().Call("ApiDeleteItem", id); + await $Backend().Call("DeleteItem", id); (event.target as HTMLElement).closest(".item")?.remove(); } }; @@ -285,7 +285,7 @@ self.HandleSubmit = async (event: Event) => { const password = formData.get("password"); try { - await $Backend().Call("ApiLogin", email, password); + await $Backend().Call("Login", email, password); window.location.href = "/dashboard"; } catch (error) { alert("Login failed"); @@ -380,7 +380,7 @@ self.AddTodo = async (event: Event) => { const input = form.querySelector("input") as HTMLInputElement; if (input.value.trim()) { - const todo = await $Backend().Call("ApiAddTodo", input.value); + const todo = await $Backend().Call("AddTodo", input.value); const todos = self.state.Get("todos") || []; self.state.Set("todos", [...todos, todo]); input.value = ""; @@ -389,11 +389,11 @@ self.AddTodo = async (event: Event) => { self.ToggleTodo = async (event: Event, data: EventData) => { const checkbox = event.target as HTMLInputElement; - await $Backend().Call("ApiToggleTodo", data.id, checkbox.checked); + await $Backend().Call("ToggleTodo", data.id, checkbox.checked); }; self.DeleteTodo = async (event: Event, data: EventData) => { - await $Backend().Call("ApiDeleteTodo", data.id); + await $Backend().Call("DeleteTodo", data.id); const todos = self.state.Get("todos").filter((t: any) => t.id !== data.id); self.state.Set("todos", todos); }; diff --git a/sui/docs/frontend-api.md b/sui/docs/frontend-api.md index 64a7c053..f0daf8ad 100644 --- a/sui/docs/frontend-api.md +++ b/sui/docs/frontend-api.md @@ -40,23 +40,26 @@ const items = component.queryAll(".item"); // Returns NodeList ### Via $Backend +The backend automatically adds the `Api` prefix to method names, so you call without the prefix: + ```typescript import { $Backend } from "@yao/sui"; -// Call backend API methods -const users = await $Backend().Call("ApiGetUsers"); -const user = await $Backend().Call("ApiGetUser", 123); -const result = await $Backend().Call("ApiCreateUser", "John", "john@example.com"); +// Call backend API methods (backend functions are ApiGetUsers, ApiGetUser, ApiCreateUser) +const users = await $Backend().Call("GetUsers"); +const user = await $Backend().Call("GetUser", 123); +const result = await $Backend().Call("CreateUser", "John", "john@example.com"); ``` ### Direct Call ```typescript // __sui_backend_call(route, headers, method, ...args) +// Note: method name here also gets Api prefix added automatically const result = await __sui_backend_call( "/users/list", // Page route { "X-Custom-Header": "value" }, // Custom headers - "ApiGetUsers", // Method name + "GetUsers", // Method name (backend has ApiGetUsers) { page: 1, limit: 10 } // Arguments ); ``` @@ -81,7 +84,7 @@ import { $Backend, Component } from "@yao/sui"; const self = this as Component; self.RefreshUsers = async () => { - const users = await $Backend().Call("ApiGetUsers"); + const users = await $Backend().Call("GetUsers"); // Render with data await self.render("userList", { users }); @@ -352,7 +355,7 @@ async function loadUsers() { // Create user self.CreateUser = async (event: Event, data: EventData) => { - const response = await $Backend().Call("ApiCreateUser", data.name, data.email); + const response = await $Backend().Call("CreateUser", data.name, data.email); const users = self.state.Get("users"); self.state.Set("users", [...users, response]); }; @@ -500,7 +503,7 @@ init(); // Event handlers self.HandleSave = async (event: Event, data: EventData) => { try { - await $Backend().Call("ApiSave", data); + await $Backend().Call("Save", data); sendAction("notify.success", { message: "Saved successfully!" }); } catch (error: any) { sendAction("notify.error", { message: error.message }); diff --git a/sui/docs/i18n.md b/sui/docs/i18n.md index d33ec269..09da0f24 100644 --- a/sui/docs/i18n.md +++ b/sui/docs/i18n.md @@ -112,18 +112,18 @@ Named keys are used internally for translation lookup. The `keys` section in loc ### Scripts ```typescript -function Page(component: HTMLElement) { - this.root = component; +import { Component } from "@yao/sui"; - this.showMessage = () => { - const message = __m("Operation completed"); - alert(message); - }; +const self = this as Component; - this.confirm = () => { - return confirm(__m("Are you sure you want to delete?")); - }; -} +self.ShowMessage = () => { + const message = __m("Operation completed"); + alert(message); +}; + +self.Confirm = () => { + return confirm(__m("Are you sure you want to delete?")); +}; ``` ## Locale Detection @@ -223,17 +223,17 @@ This command: Contact - + ```