From 0184ceed8e4fd833fdcb42f4e7247147246a9bd3 Mon Sep 17 00:00:00 2001 From: Jiachen Yu Date: Tue, 20 Oct 2026 04:39:07 +0800 Subject: [PATCH 1/2] Add Page Designer (page-designer), a MiniMax Code port of fx0 Page Designer turns a description into a single self-contained HTML page and keeps refining it in conversation, with a sandboxed live preview and a version history. It is a port of fx0 by Vercel Labs (https://github.com/vercel-labs/fx0, Apache-2.0, commit e584782): the design instructions, the HTML validation rules, the preview CSP and bootstrap, and the exact-replacement edit tool are adapted from fx0, and each adapted file says what changed. The package ships under Apache-2.0 with a NOTICE that credits fx0; the app's About dialog links to it and states that the port is not affiliated with or endorsed by Vercel. What is different: the Agent is the user's own MiniMax Code Agent (context.agent, one hidden session per page, model and thinking level picked in the app) and writes pages through the Mini App's own MCP endpoint. Pages live in context.dataDir. fx0's ten brand-inspired design.md styles are replaced by six original styles plus design.md upload. Every route answers only loopback requests (Host header and browser Origin), writes must be same-site JSON, previews run in a sandbox="allow-scripts" iframe under fx0's CSP, and the Node process makes no outbound requests. --- README.md | 1 + README.zh-CN.md | 1 + .../page-designer/.minimax-plugin/plugin.json | 20 + plugins/yujiachen-y/page-designer/LICENSE | 192 ++ plugins/yujiachen-y/page-designer/NOTICE | 23 + plugins/yujiachen-y/page-designer/README.md | 172 ++ .../yujiachen-y/page-designer/README.zh-CN.md | 102 ++ .../page-designer/docs/preview.jpg | Bin 0 -> 166589 bytes plugins/yujiachen-y/page-designer/icon.png | Bin 0 -> 16527 bytes .../page-designer/miniapp/client/app.css | 1232 +++++++++++++ .../page-designer/miniapp/client/app.js | 1596 +++++++++++++++++ .../miniapp/client/client-api.ts | 387 ++++ .../page-designer/miniapp/client/index.html | 17 + .../page-designer/miniapp/miniapp.json | 24 + .../page-designer/miniapp/node/artifact.mjs | 291 +++ .../page-designer/miniapp/node/mcp.mjs | 92 + .../page-designer/miniapp/node/miniapp-api.ts | 397 ++++ .../page-designer/miniapp/node/prompts.mjs | 98 + .../page-designer/miniapp/node/runs.mjs | 422 +++++ .../page-designer/miniapp/node/server.mjs | 527 ++++++ .../page-designer/miniapp/node/store.mjs | 230 +++ .../page-designer/miniapp/node/styles.mjs | 222 +++ .../yujiachen-y/page-designer/package.json | 13 + .../page-designer/servers.mcp.json | 12 + .../page-designer/tests/artifact.test.mjs | 84 + .../page-designer/tests/flow.test.mjs | 403 +++++ .../page-designer/tests/helpers.mjs | 151 ++ 27 files changed, 6709 insertions(+) create mode 100644 plugins/yujiachen-y/page-designer/.minimax-plugin/plugin.json create mode 100644 plugins/yujiachen-y/page-designer/LICENSE create mode 100644 plugins/yujiachen-y/page-designer/NOTICE create mode 100644 plugins/yujiachen-y/page-designer/README.md create mode 100644 plugins/yujiachen-y/page-designer/README.zh-CN.md create mode 100644 plugins/yujiachen-y/page-designer/docs/preview.jpg create mode 100644 plugins/yujiachen-y/page-designer/icon.png create mode 100644 plugins/yujiachen-y/page-designer/miniapp/client/app.css create mode 100644 plugins/yujiachen-y/page-designer/miniapp/client/app.js create mode 100644 plugins/yujiachen-y/page-designer/miniapp/client/client-api.ts create mode 100644 plugins/yujiachen-y/page-designer/miniapp/client/index.html create mode 100644 plugins/yujiachen-y/page-designer/miniapp/miniapp.json create mode 100644 plugins/yujiachen-y/page-designer/miniapp/node/artifact.mjs create mode 100644 plugins/yujiachen-y/page-designer/miniapp/node/mcp.mjs create mode 100644 plugins/yujiachen-y/page-designer/miniapp/node/miniapp-api.ts create mode 100644 plugins/yujiachen-y/page-designer/miniapp/node/prompts.mjs create mode 100644 plugins/yujiachen-y/page-designer/miniapp/node/runs.mjs create mode 100644 plugins/yujiachen-y/page-designer/miniapp/node/server.mjs create mode 100644 plugins/yujiachen-y/page-designer/miniapp/node/store.mjs create mode 100644 plugins/yujiachen-y/page-designer/miniapp/node/styles.mjs create mode 100644 plugins/yujiachen-y/page-designer/package.json create mode 100644 plugins/yujiachen-y/page-designer/servers.mcp.json create mode 100644 plugins/yujiachen-y/page-designer/tests/artifact.test.mjs create mode 100644 plugins/yujiachen-y/page-designer/tests/flow.test.mjs create mode 100644 plugins/yujiachen-y/page-designer/tests/helpers.mjs diff --git a/README.md b/README.md index 7bdf6c4a..f9d1ff85 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,7 @@ The MiniMax Code Agent communicates through the host MCP client. Business reques | [心屿 / Xinyu Island](plugins/yujiachen-y/xinyu-daily/) | A cozy daily-mood island that plays like an indie game: an inspiration card shaped by your answers, mood stickers, a joy jar, a decision slot machine, a Book of Answers, and 20 hidden secrets guarded by a grumpy crab. Optionally, Xiaoyu writes you a daily letter and chats with you using your model. Requires MiniMax Code 3.1.2 or later | [yujiachen-y](https://github.com/yujiachen-y) | | [MoMo Collab](plugins/amszuidas/momo-collab/) | A project steward that turns stray thoughts, screenshots, and links into ideas, writes clear ideas up as tasks, runs each task in its own Agent session with a full handoff, checks the result against your done criteria, and tracks everything on a board. Requires MiniMax Code 3.1.2 or later | [amszuidas](https://github.com/amszuidas) | | [Anatomy Gallery](plugins/zch0227/anatomy-gallery/) | A gallery for interactive isometric figures made with the anatomy skill: hang them in rooms you organize, view light and dark editions full screen, keep every version, and have an Agent create or edit figures from the gallery with a per-run process log. The anatomy skill is bundled. Requires MiniMax Code 3.1.2 or later | [zch0227](https://github.com/zch0227) | +| [Page Designer](plugins/yujiachen-y/page-designer/) | Describe a page and your MiniMax Code Agent writes it as one self-contained HTML file: live sandboxed preview, every version kept, copy or download, your choice of model, six starting styles or your own `design.md`. A port of Vercel Labs' open-source fx0 (Apache-2.0). Requires MiniMax Code 3.1.2 or later | [yujiachen-y](https://github.com/yujiachen-y) |
Preview: Token Usage Board diff --git a/README.zh-CN.md b/README.zh-CN.md index dd0c744f..d71b016a 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -76,6 +76,7 @@ MiniMax Code Agent 通过宿主 MCP 客户端与 MiniApp 协作。业务请求 | [心屿](plugins/yujiachen-y/xinyu-daily/README.zh-CN.md) | 像独立小游戏的每日心情小岛:由你的回答决定的今日灵感卡、心情贴纸、小确幸罐子、纠结老虎机、答案之书,还有坏脾气螃蟹藏起来的 20 个秘密;可选开启小屿陪伴,借用你的模型每天写信、陪你聊天。需要 MiniMax Code 3.1.2 或更高版本 | [yujiachen-y](https://github.com/yujiachen-y) | | [协作 · MoMo](plugins/amszuidas/momo-collab/README.zh-CN.md) | 项目管家 MoMo:把随手的想法、截图和链接攒成想法,写清后变成任务,派 Agent 会话带着完整背景去执行,按验收标准核对结果后交你审核,进展在看板上一眼可见。需要 MiniMax Code 3.1.2 或更高版本 | [amszuidas](https://github.com/amszuidas) | | [Anatomy 画廊](plugins/zch0227/anatomy-gallery/README.zh-CN.md) | 集中展示 anatomy 技能做出的交互式等距图解:按自建展区挂墙,全屏查看明暗两版,保留全部历史版本;在画廊里让 Agent 新建或修改作品,每次运行都有过程记录。自带 anatomy 技能。需要 MiniMax Code 3.1.2 或更高版本 | [zch0227](https://github.com/zch0227) | +| [Page Designer](plugins/yujiachen-y/page-designer/README.zh-CN.md) | 描述一个页面,MiniMax Code 的 Agent 把它写成一个独立的 HTML 文件:实时沙箱预览、保留全部版本、可复制或下载,可选模型,内置六种起始风格或使用自己的 `design.md`。移植自 Vercel Labs 的开源项目 fx0(Apache-2.0)。需要 MiniMax Code 3.1.2 或更高版本 | [yujiachen-y](https://github.com/yujiachen-y) |
预览:Token 用量看板 diff --git a/plugins/yujiachen-y/page-designer/.minimax-plugin/plugin.json b/plugins/yujiachen-y/page-designer/.minimax-plugin/plugin.json new file mode 100644 index 00000000..dceb3ec0 --- /dev/null +++ b/plugins/yujiachen-y/page-designer/.minimax-plugin/plugin.json @@ -0,0 +1,20 @@ +{ + "schemaVersion": 1, + "name": "page-designer", + "displayName": "Page Designer", + "version": "1.0.0", + "description": "Describe a page and an Agent writes it as one self-contained HTML file, with a live sandboxed preview, every version kept, and your MiniMax Code model. A MiniMax Code port of fx0 by Vercel Labs.", + "author": "yujiachen-y", + "icon": "icon.png", + "category": "Design & Sites", + "exampleQueries": [ + "Open Page Designer", + "Open Page Designer and make a landing page for a neighborhood bakery", + "打开 Page Designer,做一个番茄钟小页面" + ], + "apps": [], + "mcpServers": [ + "servers.mcp.json" + ], + "skills": [] +} diff --git a/plugins/yujiachen-y/page-designer/LICENSE b/plugins/yujiachen-y/page-designer/LICENSE new file mode 100644 index 00000000..39fa4b31 --- /dev/null +++ b/plugins/yujiachen-y/page-designer/LICENSE @@ -0,0 +1,192 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to the Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by the Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding any notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2026 Jiachen Yu (yujiachen-y) + Portions copyright 2026 Vercel, Inc. (fx0, https://github.com/vercel-labs/fx0) + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/yujiachen-y/page-designer/NOTICE b/plugins/yujiachen-y/page-designer/NOTICE new file mode 100644 index 00000000..99f2e79e --- /dev/null +++ b/plugins/yujiachen-y/page-designer/NOTICE @@ -0,0 +1,23 @@ +Page Designer +Copyright 2026 Jiachen Yu (yujiachen-y) + +This product is a MiniMax Code port of fx0 by Vercel Labs +(https://github.com/vercel-labs/fx0, commit e584782), which is licensed under +the Apache License, Version 2.0. Copyright 2026 Vercel, Inc. + +The following parts are adapted from fx0, and each adapted file says so at +its top together with the changes made: + + miniapp/node/artifact.mjs from lib/design-artifact.ts (HTML validation, + the preview CSP and sandbox bootstrap) and the + edit_design handler in lib/agent.ts + miniapp/node/prompts.mjs from DESIGN_INSTRUCTIONS in lib/agent.ts + miniapp/node/server.mjs the publish_design, edit_design and read_design + tool descriptions, from lib/tools.ts + +Everything else, including the Mini App runtime, the page, the style presets +and the icon, was written for this package. + +Page Designer is an independent community project. It is not affiliated with, +sponsored by, or endorsed by Vercel. "fx0" and "Vercel" are used here only to +identify the original work. diff --git a/plugins/yujiachen-y/page-designer/README.md b/plugins/yujiachen-y/page-designer/README.md new file mode 100644 index 00000000..2d8049b8 --- /dev/null +++ b/plugins/yujiachen-y/page-designer/README.md @@ -0,0 +1,172 @@ +# Page Designer (`page-designer`) + +English | [简体中文](README.zh-CN.md) + +Describe a page and an Agent writes it as one self-contained HTML file. The page shows up in a +live, sandboxed preview next to the chat, every version is kept, and you can copy or download the +HTML. The Agent is your own MiniMax Code Agent, running the model you pick. + +Page Designer is a MiniMax Code port of [fx0](https://github.com/vercel-labs/fx0), the +open-source design agent by Vercel Labs. See [Credits](#credits). + +![Page Designer](docs/preview.jpg) + +## What you can do + +- **Describe a page**: a landing page, a small tool, a game, an explainer, a résumé. The Agent + picks reasonable details and builds it right away, then you ask for changes in plain words. +- **Start from a style**: six built-in starting styles written for this package (quiet minimal, + editorial, soft and friendly, bold poster, night console, pocket arcade), or upload your own + `design.md`. +- **Pick the model**: any model your MiniMax Code can use, with its thinking level. The choice is + kept per page. +- **Every version**: each publish or edit is a new version. Open any of them from the chat or the + version menu, and copy or download its HTML. +- **Preview widths**: desktop, tablet and phone, plus reload. Links in the page open outside it. +- **Approvals and questions**: when the Agent asks to use a tool, the page shows a card with + Allow once and Deny, or you can decide in the conversation. Agent questions are answered in a form + above the composer, or skipped. +- **Pop out**: open the Mini App in its own window when the side panel feels narrow. +- English and Chinese interface, light and dark themes following the system. + +## Install + +Requires MiniMax Code 3.1.2 or later, because the package declares `agentAccess`, +`agentConcurrency` and `surface.window`; earlier versions cannot install it. + +Copy this directory, including the hidden `.minimax-plugin/`, into the MiniMax Code plugins +directory as `page-designer/` (`~/.minimax/plugins/page-designer/` by default; see the root README's +Install section). Restart MiniMax Code, then open "Page Designer" from the Mini App entry in the +right panel or ask the Agent to "Open Page Designer". + +## Tested environment + +- MiniMax Code 3.1.4-preview.4694 on macOS 27.0, an internal preview build of the desktop app + that ships the 3.1.2 Mini App Agent API. Installed through the Host publish flow, opened in the + right panel, and opened by link (`/studio?p=`). +- Real Agent runs with MiniMax-M3.1-Flash picked in the model menu (the turn reported that model): + a new page from a Chinese description with the "Soft & friendly" style (version 1, title set by + the Agent), two follow-up edits through `edit_design` (versions 2 and 3, the preview switched to + each new version by itself), a second page from an English description, and Stop pressed during + a run (the run ended with "Stopped." and the composer worked again). None of these runs raised a + permission request. +- In the real page: version chips and the version menu, preview widths, Copy HTML (the clipboard + held the raw HTML of version 3), "Show conversation" (the call resolved), and a click on an + `https://` link inside the preview reaching `window.open`. No console errors or failed requests. +- In Google Chrome 155 (headless) against the same Node entry with a scripted Agent: the 420 px + layout with Chat and Preview tabs, the 900 px layout with the page drawer, light and dark themes, + the permission card, the questionnaire form, the About dialog, Download (file name and raw + content), a page that navigates itself away (restored), and a page that blocks for 1.5 s while + loading (loaded once, no reload loop). +- `npm test`: 18 `node:test` cases that run the Node entry with a scripted Agent, covering the MCP + tools and validation, approvals relaying only the user's decision, questionnaire continuation, + a lost session replaced once, Stop before and during a turn, the loopback and same-origin JSON + guards (a DNS-rebound hostname is refused on every route), and persistence across a restart. +- Not verified: a permission request or questionnaire raised by a real Agent run (covered with the + scripted Agent only), the pop-out window, where MiniMax Code opens a link clicked in the preview, + Download inside MiniMax Code, and Windows. + +## Data & access + +- **State**: everything is stored under the Host-provided `context.dataDir`, one folder per page: + `projects//project.json` (title, chat transcript, run records, chosen model and style, the + linked session id) and `projects//v.html` (each version exactly as published). Deleting a + page in Page Designer removes its folder; the linked Agent conversation stays in MiniMax Code. + The page keeps only the model choice and preview width in `localStorage`. +- **Files read outside `dataDir`**: none. Uploaded `design.md` files are read by the page and sent + to the Node process as text; they are stored inside the page's `project.json`. +- **Files written outside `dataDir`**: none by Page Designer. Download saves the HTML through the + browser's normal download flow. +- **Agent turns** (`agentAccess: ["session.run"]`): each page gets one hidden session created with + `context.agent.create()`, and every message you send is one turn in it, run from Node so it keeps + going when the panel closes. These sessions use your primary MiniMax Code Agent, so its own + settings and memory apply, and they work in this Mini App's own Agent directory. The first turn carries the design brief (adapted from fx0), the + chosen style and your message; later turns carry a short reminder and your message. The brief asks + the Agent to work only through Page Designer's MCP tools, to stay away from files, the shell and + subagents, and allows web search and web fetch for reference material. Your message is passed as + marked user text; what the Agent sends back is shown as text and only the HTML it passes to the MCP + tools, after validation, becomes a version. If the linked session has disappeared, Page Designer + creates a new one once and sends the brief again. +- **Permissions**: every `permission.requested` in a Page Designer run is shown as a card with + Allow once, Deny and "Decide in conversation". Page Designer never answers a request by itself: + Allow once and Deny relay your click for that one request. "Decide in conversation" opens the + session in the chat side panel, where MiniMax Code's own card decides. +- **MCP**: the package declares an MCP server served by the Mini App itself at + `/mcp/page-designer`, with `publish_design`, `edit_design`, `read_design` and `list_designs`. + Agents can also call them from ordinary chats while the Mini App runs; `publish_design` without a + `project_id` starts a new page, and every result carries `open_path` (`/studio?p=`), the + route that opens that page. +- **Validation**: a version must be one complete HTML document of at most 512 KiB with inline + scripts and styles, embedded `data:` assets, no HTML comments, no forms, no nested frames, no + external `src`, and only `#fragment` or `https://` links (fx0's rules). Rejected HTML is returned + to the Agent with the reason and stored nowhere. +- **Preview sandbox**: versions render in an iframe with `sandbox="allow-scripts"` (opaque origin, + no access to the Mini App, its storage or the `window.mcode` bridge) and fx0's content security + policy, which blocks every network request, form, frame, worker and external asset. WebRTC is + switched off before the page runs. A page that navigates itself away is restored. A clicked + `https://` link is handed to the Mini App page, which opens it with `window.open`; MiniMax Code + decides where it opens. The raw-HTML route used for + copy and download is served with a `sandbox` CSP, so a direct visit never runs the page with the + Mini App's origin. +- **Loopback only**: every route, the MCP endpoint included, answers only when the Host header names + this machine (`127.0.0.1`, `localhost`, `::1`) and a browser Origin, when sent, is this listener, + so a web page whose hostname was re-pointed at 127.0.0.1 cannot reach it. Writes must also be + JSON (415 otherwise) and not marked cross-site by the browser. +- **Network**: the Node process makes no outbound requests. Web search and web fetch, when the + Agent uses them, run inside the Agent's own tools. +- **Processes**: none spawned. + +## Differences from fx0 + +- The Agent is MiniMax Code's, not libfx, and it uses the models configured in MiniMax Code instead + of Vercel AI Gateway. There is no sign-in, billing, free trial or BotID. +- Pages live on this computer in the Mini App's data folder instead of Redis and Neon. A run that is + interrupted by a restart is marked interrupted rather than resumed. +- The Agent writes through MCP tools, so the preview updates when a version is published or + edited, not while the HTML streams in. +- Messages sent while the Agent works are not steered into the running turn; stop it or wait. + Image attachments are not supported. +- The ten brand-inspired `design.md` styles bundled with fx0 are not included. The six built-in + styles are original, and you can still upload any `design.md` you like. + +## Credits + +Page Designer exists because Vercel Labs published [fx0](https://github.com/vercel-labs/fx0) under +the Apache License 2.0. These parts are adapted from fx0 at commit `e584782`, and each adapted file +names its source and the changes at its top: + +- `miniapp/node/artifact.mjs`: the HTML validation rules, the preview content security policy and + sandbox bootstrap (`lib/design-artifact.ts`), and the exact-replacement edits (`lib/agent.ts`). +- `miniapp/node/prompts.mjs`: the design instructions (`DESIGN_INSTRUCTIONS` in `lib/agent.ts`). +- `miniapp/node/server.mjs`: the descriptions of the page tools (`lib/tools.ts`). + +The Mini App runtime, the page, the style presets and the icon were written for this package. +Thank you to the fx0 authors. Page Designer is an independent community project and is not +affiliated with or endorsed by Vercel. See [NOTICE](./NOTICE). + +## Files + +```text +.minimax-plugin/plugin.json Plugin manifest (declares the MCP server) +package.json Mini App declaration +servers.mcp.json MCP server entry; its URL is a placeholder that the Host binds to the + Mini App's /mcp/page-designer route (miniapp.json mcpEndpoints) +miniapp/miniapp.json Payload roots, Node entry, page route, MCP endpoint, agentAccess +miniapp/client/ The page (index.html, app.js, app.css) +miniapp/node/server.mjs Node entry: HTTP routes, SSE, MCP tools +miniapp/node/runs.mjs Agent sessions and turns, approvals, questions +miniapp/node/store.mjs Projects and versions under dataDir +miniapp/node/artifact.mjs HTML validation, preview sandbox, exact edits (adapted from fx0) +miniapp/node/prompts.mjs The design brief (adapted from fx0) +miniapp/node/styles.mjs Built-in starting styles +miniapp/node/mcp.mjs Minimal MCP (JSON-RPC over HTTP) handler +miniapp/*/*.ts Type declarations for the runtime APIs; not loaded at runtime +tests/ node:test suites (`npm test`, Node 22 or later) +docs/preview.jpg README screenshot +LICENSE, NOTICE Apache License 2.0 and attribution +icon.png Plugin icon +``` + +## License + +[Apache License 2.0](./LICENSE), the license of fx0. See [NOTICE](./NOTICE) for attribution. diff --git a/plugins/yujiachen-y/page-designer/README.zh-CN.md b/plugins/yujiachen-y/page-designer/README.zh-CN.md new file mode 100644 index 00000000..f7c11eee --- /dev/null +++ b/plugins/yujiachen-y/page-designer/README.zh-CN.md @@ -0,0 +1,102 @@ +# Page Designer(`page-designer`) + +[English](README.md) | 简体中文 + +描述你想要的页面,Agent 把它写成一个独立的 HTML 文件。页面显示在对话旁边的实时沙箱预览里,每个版本都会保留,可以复制或下载 HTML。写页面的是你自己的 MiniMax Code Agent,用的是你选择的模型。 + +Page Designer 是 Vercel Labs 开源设计 Agent [fx0](https://github.com/vercel-labs/fx0) 在 MiniMax Code 上的移植版,详见[致谢](#致谢)。 + +![Page Designer](docs/preview.jpg) + +## 能做什么 + +- **描述一个页面**:落地页、小工具、小游戏、交互讲解、简历都可以。Agent 会先自己定好合理的细节、直接做出来,之后你用大白话提修改。 +- **选择起始风格**:内置六种为本插件原创的风格(安静极简、杂志编辑、柔和友好、大胆海报、夜间控制台、口袋游戏),也可以上传自己的 `design.md`。 +- **选择模型**:MiniMax Code 里能用的模型都可以选,也能选思考档位;每个页面会记住自己的选择。 +- **保留每个版本**:每次发布或修改都是一个新版本。可以从对话或版本菜单打开任意版本,复制或下载它的 HTML。 +- **预览宽度**:桌面、平板、手机三档,支持重新载入。页面里的链接在预览之外打开。 +- **权限和提问**:Agent 想使用某个工具时,页面会显示卡片,可以「允许一次」「拒绝」,或者去对话里决定。Agent 的提问在输入框上方的表单里回答,也可以跳过。 +- **独立窗口**:侧边栏太窄时,可以把 Mini App 弹出到单独的窗口。 +- 界面支持中英文,明暗主题跟随系统。 + +## 安装 + +需要 MiniMax Code 3.1.2 或更高版本,因为包里声明了 `agentAccess`、`agentConcurrency` 和 `surface.window`,更早的版本无法安装。 + +把这个目录(包括隐藏的 `.minimax-plugin/`)复制到 MiniMax Code 的插件目录,命名为 `page-designer/`(默认是 `~/.minimax/plugins/page-designer/`,见根目录 README 的安装说明)。重启 MiniMax Code,然后从右侧面板的 Mini App 入口打开「Page Designer」,或者让 Agent「打开 Page Designer」。 + +## 测试环境 + +- macOS 27.0 上的 MiniMax Code 3.1.4-preview.4694。这是桌面端的内部预览版,已包含 3.1.2 的 Mini App Agent API。通过 Host 发布流程安装,在右侧面板打开,也用链接(`/studio?p=`)打开过。 +- 真实 Agent 运行,在模型菜单里选了 MiniMax-M3.1-Flash,运行回报的也是这个模型: + - 用中文描述加「柔和友好」风格新建页面,得到版本 1,标题由 Agent 设定; + - 两次追加修改走 `edit_design`,得到版本 2 和 3,预览自动切换到新版本; + - 用英文描述新建第二个页面; + - 运行中点停止,显示「已停止。」,之后输入框恢复可用。 + + 这几次运行都没有触发权限请求。 +- 真实页面里:版本标签和版本菜单、预览宽度、复制 HTML(剪贴板里是版本 3 的原始 HTML)、「查看对话」(调用成功返回)、点击预览里的 `https://` 链接会走到 `window.open`。没有控制台报错或失败请求。 +- 在 Google Chrome 155(无头模式)里,用同一个 Node 入口配脚本化 Agent 测过: + - 420 px 下的「对话 / 预览」Tab 布局、900 px 下的页面抽屉、明暗主题; + - 权限卡片、问卷表单、关于对话框; + - 下载(文件名和原始内容); + - 页面自己跳走后会被恢复;加载时卡住 1.5 秒的页面只加载一次,不会循环重载。 +- `npm test`:18 个 `node:test` 用例,用脚本化 Agent 跑 Node 入口。覆盖 MCP 工具和校验、权限只转达用户的决定、问卷续答、会话丢失时重建一次、运行前和运行中的停止、本机与同源 JSON 校验(DNS 重绑定的域名在所有路由上都被拒绝),以及重启后的数据保留。 +- 未验证:真实 Agent 运行触发的权限请求或问卷(只用脚本化 Agent 覆盖过)、弹出窗口、预览里点击的链接最终在哪里打开、MiniMax Code 内的下载、Windows。 + +## 数据与权限 + +- **状态**:所有数据都存放在 Host 提供的 `context.dataDir` 下,每个页面一个文件夹: + - `projects//project.json`:标题、对话记录、运行记录、所选模型和风格、关联的会话 id; + - `projects//v.html`:每个版本,与发布时完全一致。 + + 在 Page Designer 里删除页面会删掉这个文件夹,关联的 Agent 对话仍保留在 MiniMax Code 里。浏览器 `localStorage` 里只保存模型选择和预览宽度。 +- **读取 `dataDir` 以外的文件**:无。上传的 `design.md` 由页面读取,以文本形式发给 Node 进程,存进该页面的 `project.json`。 +- **写入 `dataDir` 以外的文件**:Page Designer 本身不写。下载走浏览器的正常下载流程。 +- **Agent 运行**(`agentAccess: ["session.run"]`): + - 每个页面用 `context.agent.create()` 创建一个隐藏会话,你发的每条消息是其中一轮。运行由 Node 发起,所以关掉面板也会继续。 + - 这些会话使用你的主 MiniMax Code Agent,它自己的设置和记忆都会生效,工作目录是这个 Mini App 自己的 Agent 目录。 + - 第一轮会带上设计指令(改编自 fx0)、所选风格和你的消息,之后每轮只带一段简短提醒和你的消息。 + - 指令要求 Agent 只通过 Page Designer 的 MCP 工具工作,不碰文件、shell 和子 Agent,允许用网页搜索和抓取找参考。 + - 你的消息会作为标记好的用户文本传入。Agent 的回复只作为文本显示,只有它交给 MCP 工具、并通过校验的 HTML 才会成为版本。 + - 如果关联的会话不见了,Page Designer 会新建一个会话并重新发送一次指令。 +- **权限**:Page Designer 运行中的每个 `permission.requested` 都显示为卡片,提供「允许一次」「拒绝」和「去对话里决定」。Page Designer 不会自行答复任何请求:「允许一次」和「拒绝」只把你对这一个请求的点击转达过去。「去对话里决定」会在聊天侧栏打开这个会话,由 MiniMax Code 自己的卡片决定。 +- **MCP**:包里声明了一个由 Mini App 自己在 `/mcp/page-designer` 提供的 MCP 服务器,包含 `publish_design`、`edit_design`、`read_design` 和 `list_designs`。Mini App 运行期间,普通对话里的 Agent 也能调用: + - 不带 `project_id` 调用 `publish_design` 会新建页面; + - 每次返回都带 `open_path`(`/studio?p=`),也就是打开该页面的路由。 +- **校验**:沿用 fx0 的规则。一个版本必须是不超过 512 KiB 的完整 HTML 文档,满足以下条件: + - 脚本和样式内联,资源用 `data:` 嵌入,没有外部 `src`; + - 没有 HTML 注释、表单、嵌套框架; + - 链接只允许 `#片段` 或 `https://`。 + + 不合规的 HTML 会把原因返回给 Agent,任何地方都不会保存。 +- **预览沙箱**: + - 版本渲染在 `sandbox="allow-scripts"` 的 iframe 里,源是不透明的,无法访问 Mini App、它的存储或 `window.mcode` 桥。 + - 套用 fx0 的内容安全策略,屏蔽所有网络请求、表单、框架、worker 和外部资源。页面运行前会先关闭 WebRTC。 + - 页面自己跳走后会被恢复。点击的 `https://` 链接交给 Mini App 页面用 `window.open` 打开,具体在哪里打开由 MiniMax Code 决定。 + - 复制和下载用的原始 HTML 路由带 `sandbox` CSP,即使直接访问,也不会以 Mini App 的源运行页面。 +- **只接受本机请求**:所有路由(包括 MCP 端点)都要求 Host 头是本机(`127.0.0.1`、`localhost`、`::1`),浏览器带的 Origin 必须是这个监听地址本身。所以即使某个网页把自己的域名解析到 127.0.0.1(DNS 重绑定),也访问不到。写请求还必须是 JSON(否则 415),且不能被浏览器标记为跨站。 +- **网络**:Node 进程不发起任何外部请求。Agent 用到的网页搜索和抓取,在 Agent 自己的工具里运行。 +- **进程**:不启动任何进程。 + +## 与 fx0 的不同 + +- Agent 是 MiniMax Code 的,不是 libfx;模型来自 MiniMax Code 的配置,不走 Vercel AI Gateway。没有登录、计费、免费试用和 BotID。 +- 页面保存在本机 Mini App 的数据目录里,而不是 Redis 和 Neon。被重启打断的运行会标记为中断,不会续跑。 +- Agent 通过 MCP 工具写页面,所以预览在发布或修改完成时更新,不会在 HTML 生成过程中实时滚动出现。 +- Agent 工作时发的消息不会插入当前这一轮,需要先停止或等待它完成。不支持图片附件。 +- fx0 自带的十个品牌风格 `design.md` 没有收录。内置的六种风格是原创的,你仍然可以上传任意 `design.md`。 + +## 致谢 + +Page Designer 的存在,要感谢 Vercel Labs 以 Apache License 2.0 开源了 [fx0](https://github.com/vercel-labs/fx0)。以下部分改编自 fx0 的提交 `e584782`,每个改编的文件开头都写明了来源和改动: + +- `miniapp/node/artifact.mjs`:HTML 校验规则、预览内容安全策略和沙箱引导脚本(`lib/design-artifact.ts`),以及精确替换式修改(`lib/agent.ts`)。 +- `miniapp/node/prompts.mjs`:设计指令(`lib/agent.ts` 中的 `DESIGN_INSTRUCTIONS`)。 +- `miniapp/node/server.mjs`:页面工具的描述(`lib/tools.ts`)。 + +Mini App 运行时、页面、风格预设和图标是为本插件编写的。感谢 fx0 的作者们。Page Designer 是独立的社区项目,与 Vercel 没有隶属或背书关系,详见 [NOTICE](./NOTICE)。 + +## 许可证 + +[Apache License 2.0](./LICENSE),与 fx0 相同。署名见 [NOTICE](./NOTICE)。 diff --git a/plugins/yujiachen-y/page-designer/docs/preview.jpg b/plugins/yujiachen-y/page-designer/docs/preview.jpg new file mode 100644 index 0000000000000000000000000000000000000000..813e55cee9feb797b21073a6d52defd27a03a961 GIT binary patch literal 166589 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

2ccU|Uwt&gTa;Tlf_ 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 literal 0 HcmV?d00001 diff --git a/plugins/yujiachen-y/page-designer/miniapp/client/app.css b/plugins/yujiachen-y/page-designer/miniapp/client/app.css new file mode 100644 index 00000000..b73cac00 --- /dev/null +++ b/plugins/yujiachen-y/page-designer/miniapp/client/app.css @@ -0,0 +1,1232 @@ +/* Page Designer. Colors follow the MiniMax Code Mini App baseline through --mcode-* variables. */ + +:root, +[data-theme='light'] { + --mcode-bg: #ffffff; + --mcode-bg-secondary: #f5f5f5; + --mcode-surface: #ffffff; + --mcode-surface-muted: #fafafa; + --mcode-text: #171717; + --mcode-text-muted: #666666; + --mcode-text-subtle: #adadad; + --mcode-border: #0a0a0a14; + --mcode-border-strong: #0a0a0af2; + --mcode-accent: #0094fc; + --mcode-success: #04b54b; + --mcode-warning: #f56811; + --mcode-danger: #f73646; + --mcode-primary-action-bg: #171717; + --mcode-primary-action-text: #ffffff; + --pd-hover: #0a0a0a0a; + --pd-user-bubble: #f2f2f2; + --pd-stage: #f5f5f5; + --pd-shadow: 0 0 20px rgb(10 10 10 / 8%); + --pd-brand: #ff7a59; + color-scheme: light; +} + +[data-theme='dark'] { + --mcode-bg: #171717; + --mcode-bg-secondary: #1c1c1c; + --mcode-surface: #262626; + --mcode-surface-muted: #262626; + --mcode-text: #ededed; + --mcode-text-muted: #949494; + --mcode-text-subtle: #666666; + --mcode-border: #ffffff14; + --mcode-border-strong: #fffffff2; + --mcode-accent: #0077d9; + --mcode-success: #009c3d; + --mcode-warning: #e25507; + --mcode-danger: #e31937; + --mcode-primary-action-bg: #ffffff; + --mcode-primary-action-text: #171717; + --pd-hover: #ffffff0f; + --pd-user-bubble: #262626; + --pd-stage: #101010; + --pd-shadow: 0 0 20px rgb(0 0 0 / 40%); + color-scheme: dark; +} + +* { + box-sizing: border-box; +} + +html, +body { + height: 100%; + margin: 0; + overflow: hidden; +} + +body { + background: var(--mcode-bg); + color: var(--mcode-text); + font-family: ui-sans-serif, -apple-system, system-ui, 'Segoe UI', 'PingFang SC', Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 20px; + -webkit-font-smoothing: antialiased; +} + +button, +input, +select, +textarea { + font: inherit; + color: inherit; +} + +code, +pre { + font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace; + font-size: 12.5px; +} + +[hidden] { + display: none !important; +} + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0 0 0 0); + white-space: nowrap; + border: 0; +} + +.muted { + color: var(--mcode-text-muted); +} + +.small { + font-size: 12px; + line-height: 16px; +} + +.spacer { + flex: 1; +} + +.icon { + display: inline-flex; + width: 16px; + height: 16px; + flex: none; +} + +:focus-visible { + outline: 2px solid var(--mcode-accent); + outline-offset: 2px; +} + +/* App frame ---------------------------------------------------------------------------------- */ + +.app { + display: grid; + grid-template-columns: 232px minmax(0, 1fr); + height: 100%; +} + +.boot { + margin: 24px; + color: var(--mcode-text-muted); +} + +.sidebar { + display: flex; + flex-direction: column; + gap: 12px; + min-height: 0; + padding: 16px 12px 12px; + background: var(--mcode-bg-secondary); + border-right: 1px solid var(--mcode-border); +} + +.brand { + display: flex; + align-items: center; + gap: 8px; + padding: 0 4px; + font-size: 15px; + font-weight: 600; +} + +.logo { + display: inline-flex; + flex: none; +} + +.project-nav { + flex: 1; + min-height: 0; + overflow-y: auto; + margin: 0 -4px; + padding: 0 4px; +} + +.project-list { + list-style: none; + margin: 0; + padding: 0; + display: flex; + flex-direction: column; + gap: 2px; +} + +.empty-list { + padding: 8px; +} + +.project { + display: flex; + flex-direction: column; + gap: 2px; + width: 100%; + padding: 8px 10px; + border: 0; + border-radius: 8px; + background: transparent; + text-align: left; + cursor: pointer; +} + +.project:hover { + background: var(--pd-hover); +} + +.project.current { + background: var(--mcode-surface); + box-shadow: inset 0 0 0 1px var(--mcode-border); +} + +.project-title { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-weight: 500; +} + +.project-meta { + color: var(--mcode-text-muted); + font-size: 12px; + line-height: 16px; +} + +.pill { + display: inline-flex; + align-items: center; + gap: 4px; + padding: 0 8px; + border-radius: 999px; + font-size: 12px; + line-height: 18px; +} + +.pill.working { + color: var(--mcode-accent); + background: color-mix(in srgb, var(--mcode-accent) 12%, transparent); +} + +.pill.working::before { + content: ''; + width: 6px; + height: 6px; + border-radius: 50%; + background: currentColor; +} + +.sidebar-foot { + border-top: 1px solid var(--mcode-border); + padding-top: 8px; +} + +.scrim { + display: none; +} + +.main { + display: flex; + flex-direction: column; + min-width: 0; + min-height: 0; +} + +.tabs-bar { + display: none; +} + +.panes { + flex: 1; + display: grid; + grid-template-columns: minmax(340px, 400px) minmax(0, 1fr); + min-height: 0; +} + +/* Buttons and inputs ------------------------------------------------------------------------- */ + +.btn { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 6px; + height: 32px; + padding: 0 12px; + border: 1px solid var(--mcode-border); + border-radius: 8px; + background: var(--mcode-surface); + cursor: pointer; + white-space: nowrap; +} + +.btn:hover:not(:disabled) { + background: var(--mcode-surface-muted); + border-color: color-mix(in srgb, var(--mcode-border-strong) 25%, transparent); +} + +.btn:disabled { + opacity: 0.45; + cursor: default; +} + +.btn.primary { + background: var(--mcode-primary-action-bg); + color: var(--mcode-primary-action-text); + border-color: transparent; +} + +.btn.primary:hover:not(:disabled) { + background: var(--mcode-primary-action-bg); + opacity: 0.86; +} + +.btn.danger { + background: var(--mcode-danger); + color: #fff; + border-color: transparent; +} + +.btn.danger:hover:not(:disabled) { + background: var(--mcode-danger); + opacity: 0.88; +} + +.btn.ghost { + background: transparent; + border-color: transparent; +} + +.btn.ghost:hover:not(:disabled) { + background: var(--pd-hover); + border-color: transparent; +} + +.btn.danger-hover:hover:not(:disabled) { + color: var(--mcode-danger); +} + +.btn.small { + height: 26px; + padding: 0 8px; + font-size: 12px; +} + +.btn.block { + width: 100%; + height: 36px; +} + +.btn.icon-only { + width: 32px; + padding: 0; +} + +.btn.send, +.btn.danger.icon-only { + width: 36px; + height: 36px; + border-radius: 10px; +} + +.link, +.link-button, +.md a { + color: var(--mcode-accent); + text-decoration: none; +} + +.link { + display: inline-flex; + align-items: center; + gap: 4px; +} + +.link:hover, +.link-button:hover, +.md a:hover { + text-decoration: underline; +} + +.link-button { + border: 0; + padding: 0; + background: none; + cursor: pointer; + font-size: inherit; +} + +.input, +.select { + height: 32px; + border: 1px solid var(--mcode-border); + border-radius: 8px; + background: var(--mcode-surface); + padding: 0 10px; + min-width: 0; +} + +.input:focus, +.select:focus, +.composer:focus-within { + border-color: var(--mcode-border-strong); + outline: none; +} + +.select { + padding-right: 6px; + max-width: 100%; + cursor: pointer; +} + +/* Chat --------------------------------------------------------------------------------------- */ + +.chat { + display: flex; + flex-direction: column; + min-width: 0; + min-height: 0; + border-right: 1px solid var(--mcode-border); + background: var(--mcode-bg); +} + +.chat-head { + display: flex; + align-items: center; + gap: 4px; + min-height: 52px; + padding: 10px 12px 10px 16px; + border-bottom: 1px solid var(--mcode-border); +} + +.menu-button { + display: none; + margin-left: -6px; +} + +.title-wrap { + display: flex; + align-items: center; + gap: 4px; + flex: 1; + min-width: 0; +} + +.title { + flex: 1; + min-width: 0; + margin: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 15px; + line-height: 24px; + font-weight: 600; +} + +.title .input { + width: 100%; + font-weight: 500; +} + +.title-actions { + display: flex; + gap: 2px; +} + +.banner { + display: flex; + align-items: center; + gap: 12px; + margin: 12px 16px 0; + padding: 10px 12px; + border-radius: 8px; + color: var(--mcode-danger); + background: color-mix(in srgb, var(--mcode-danger) 10%, transparent); +} + +.thread-scroll { + flex: 1; + min-height: 0; + overflow-y: auto; + padding: 16px 16px 8px; + scroll-behavior: smooth; +} + +.thread, +.live { + display: flex; + flex-direction: column; + gap: 12px; +} + +.live:not(:empty) { + margin-top: 12px; +} + +.msg.user { + align-self: flex-end; + max-width: 88%; +} + +.bubble { + padding: 8px 12px; + border-radius: 12px 12px 4px 12px; + background: var(--pd-user-bubble); + white-space: pre-wrap; + overflow-wrap: anywhere; +} + +.bubble-label { + display: block; + color: var(--mcode-text-muted); + font-size: 12px; + line-height: 16px; +} + +.msg.assistant { + max-width: 100%; + overflow-wrap: anywhere; +} + +.md p, +.md ul, +.md ol, +.md pre { + margin: 0 0 8px; +} + +.md > :last-child { + margin-bottom: 0; +} + +.md ul, +.md ol { + padding-left: 20px; +} + +.md .md-heading { + font-weight: 600; +} + +.md code { + padding: 1px 4px; + border-radius: 4px; + background: var(--mcode-bg-secondary); +} + +.md pre { + padding: 10px 12px; + border-radius: 8px; + background: var(--mcode-bg-secondary); + overflow-x: auto; +} + +.md pre code { + padding: 0; + background: none; +} + +.streaming .md > :last-child::after { + content: ''; + display: inline-block; + width: 6px; + height: 14px; + margin-left: 2px; + vertical-align: -2px; + background: var(--mcode-text-muted); + animation: blink 1s steps(2) infinite; +} + +.style-note { + margin: 0; + font-size: 12px; +} + +.activity { + display: flex; + align-items: center; + gap: 6px; + margin: 0; + color: var(--mcode-text-muted); + font-size: 12px; + line-height: 16px; +} + +.activity.failed { + color: var(--mcode-warning); +} + +.version-chip { + display: flex; + align-items: center; + gap: 8px; + min-height: 36px; + padding: 4px 6px 4px 12px; + border: 1px solid var(--mcode-border); + border-radius: 10px; + font-size: 13px; +} + +.version-chip > span:nth-child(2) { + flex: 1; + font-weight: 500; +} + +.version-chip.shown { + border-color: color-mix(in srgb, var(--mcode-accent) 45%, transparent); + background: color-mix(in srgb, var(--mcode-accent) 6%, transparent); +} + +.version-chip .muted.small { + padding-right: 6px; +} + +.version-dot { + width: 8px; + height: 8px; + border-radius: 50%; + background: var(--mcode-success); +} + +.notice { + margin: 0; + padding: 8px 12px; + border-radius: 8px; + background: var(--mcode-bg-secondary); + color: var(--mcode-text-muted); + font-size: 13px; +} + +.notice.error { + color: var(--mcode-danger); + background: color-mix(in srgb, var(--mcode-danger) 8%, transparent); +} + +.working-line { + display: flex; + align-items: center; + gap: 8px; + margin: 0; + color: var(--mcode-text-muted); +} + +.spinner { + width: 14px; + height: 14px; + border: 2px solid color-mix(in srgb, var(--mcode-text-muted) 30%, transparent); + border-top-color: var(--mcode-text-muted); + border-radius: 50%; + animation: spin 0.8s linear infinite; + flex: none; +} + +.card { + padding: 14px 16px; + border: 1px solid var(--mcode-border); + border-radius: 12px; + background: var(--mcode-surface); + box-shadow: var(--pd-shadow); +} + +.card h3 { + margin: 0 0 6px; + font-size: 14px; + font-weight: 600; +} + +.card p { + margin: 0 0 8px; +} + +.card.permission { + border-color: color-mix(in srgb, var(--mcode-warning) 45%, transparent); +} + +.tool-input { + max-height: 140px; + margin: 0 0 8px; + padding: 8px 10px; + overflow: auto; + border-radius: 8px; + background: var(--mcode-bg-secondary); + white-space: pre-wrap; + overflow-wrap: anywhere; +} + +.card-actions { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 8px; + margin-top: 8px; +} + +/* Questionnaire */ + +.question-slot:not(:empty) { + padding: 0 16px 12px; +} + +.question { + max-height: 50vh; + overflow-y: auto; +} + +.q-step { + display: flex; + flex-direction: column; + gap: 6px; + margin: 0 0 12px; + padding: 0; + border: 0; +} + +.q-step legend { + margin-bottom: 6px; + padding: 0; + font-weight: 500; +} + +.q-header { + display: block; + color: var(--mcode-text-muted); + font-size: 12px; + font-weight: 400; +} + +.q-option { + display: flex; + align-items: flex-start; + gap: 8px; + padding: 6px 8px; + border-radius: 8px; + cursor: pointer; +} + +.q-option:hover { + background: var(--pd-hover); +} + +.q-option input { + margin: 3px 0 0; +} + +.q-desc { + display: block; +} + +/* Empty state */ + +.empty { + display: flex; + flex-direction: column; + gap: 8px; + padding: 8px 0 16px; +} + +.empty h2 { + margin: 8px 0 0; + font-size: 18px; + line-height: 28px; + font-weight: 600; +} + +.empty h3 { + margin: 16px 0 4px; + color: var(--mcode-text-muted); + font-size: 12px; + line-height: 16px; + font-weight: 500; +} + +.empty > p { + margin: 0; +} + +.chips { + display: flex; + flex-direction: column; + gap: 6px; +} + +.chip { + padding: 8px 12px; + border: 1px solid var(--mcode-border); + border-radius: 8px; + background: var(--mcode-surface); + text-align: left; + cursor: pointer; +} + +.chip:hover { + background: var(--mcode-surface-muted); + border-color: color-mix(in srgb, var(--mcode-border-strong) 25%, transparent); +} + +.style-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); + gap: 8px; +} + +.style-card { + position: relative; + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 4px; + padding: 10px 12px; + border: 1px solid var(--mcode-border); + border-radius: 12px; + background: var(--mcode-surface); + text-align: left; + cursor: pointer; +} + +.style-card:hover { + border-color: color-mix(in srgb, var(--mcode-border-strong) 25%, transparent); +} + +.style-card.selected { + border-color: var(--mcode-accent); + box-shadow: inset 0 0 0 1px var(--mcode-accent); +} + +.swatches { + display: flex; + gap: 4px; + height: 18px; + align-items: center; + margin-bottom: 2px; +} + +.swatch { + width: 18px; + height: 18px; + border-radius: 50%; + box-shadow: inset 0 0 0 1px var(--mcode-border); +} + +.swatch.none { + background: repeating-linear-gradient(135deg, transparent 0 3px, var(--mcode-border) 3px 5px); +} + +.style-name { + font-weight: 500; +} + +.style-hint { + color: var(--mcode-text-muted); + font-size: 12px; + line-height: 16px; +} + +.credit { + margin-top: 16px !important; +} + +/* Composer */ + +.composer { + display: flex; + flex-direction: column; + gap: 8px; + margin: 0 16px 16px; + padding: 10px 10px 10px 12px; + border: 1px solid var(--mcode-border); + border-radius: 12px; + background: var(--mcode-surface); +} + +.composer-input { + width: 100%; + min-height: 44px; + max-height: 220px; + padding: 0; + border: 0; + outline: none; + background: transparent; + resize: none; + line-height: 20px; +} + +.composer-input::placeholder { + color: var(--mcode-text-subtle); +} + +.composer-row { + display: flex; + align-items: center; + gap: 8px; +} + +.model-controls { + display: flex; + gap: 6px; + min-width: 0; + flex-wrap: wrap; +} + +.model-controls .select { + height: 28px; + max-width: 180px; + font-size: 12px; + background: var(--mcode-surface-muted); +} + +/* Preview ------------------------------------------------------------------------------------ */ + +.preview { + display: flex; + flex-direction: column; + min-width: 0; + min-height: 0; + background: var(--pd-stage); +} + +.preview-head { + display: flex; + align-items: center; + gap: 8px; + min-height: 52px; + padding: 10px 12px; + border-bottom: 1px solid var(--mcode-border); + background: var(--mcode-bg); +} + +.preview-head .select { + height: 30px; + font-size: 13px; +} + +.preview-actions { + display: flex; + gap: 2px; +} + +.segmented { + display: inline-flex; + padding: 2px; + border-radius: 8px; + background: var(--mcode-bg-secondary); +} + +.seg { + display: inline-flex; + align-items: center; + justify-content: center; + width: 30px; + height: 26px; + border: 0; + border-radius: 6px; + background: transparent; + color: var(--mcode-text-muted); + cursor: pointer; +} + +.seg[aria-pressed='true'] { + background: var(--mcode-surface); + color: var(--mcode-text); + box-shadow: 0 1px 2px rgb(10 10 10 / 12%); +} + +.preview-body { + position: relative; + flex: 1; + min-height: 0; + display: flex; +} + +.stage { + flex: 1; + display: flex; + justify-content: center; + min-width: 0; + min-height: 0; + overflow: auto; +} + +.device-frame { + position: relative; + width: 100%; + height: 100%; + background: #fff; +} + +.preview[data-device='tablet'] .stage, +.preview[data-device='phone'] .stage { + padding: 16px; +} + +.preview[data-device='tablet'] .device-frame { + width: min(820px, 100%); + border-radius: 12px; + box-shadow: var(--pd-shadow); + overflow: hidden; +} + +.preview[data-device='phone'] .device-frame { + width: min(390px, 100%); + border-radius: 16px; + box-shadow: var(--pd-shadow); + overflow: hidden; +} + +.preview-frame { + display: block; + width: 100%; + height: 100%; + border: 0; + background: #fff; +} + +.preview-overlay { + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + background: var(--pd-stage); + color: var(--mcode-text-muted); +} + +.preview-empty { + flex: 1; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 4px; + padding: 24px; + text-align: center; +} + +.preview-empty .logo svg { + width: 40px; + height: 40px; + opacity: 0.9; +} + +/* Dialogs and toast ------------------------------------------------------------------------- */ + +.dialog { + width: min(460px, calc(100vw - 32px)); + padding: 20px; + border: 1px solid var(--mcode-border); + border-radius: 12px; + background: var(--mcode-surface); + color: var(--mcode-text); + box-shadow: var(--pd-shadow); +} + +.dialog::backdrop { + background: rgb(10 10 10 / 35%); +} + +.dialog h2 { + margin: 0 0 8px; + font-size: 16px; + line-height: 24px; +} + +.dialog p { + margin: 0 0 10px; + line-height: 21px; +} + +.about-head { + display: flex; + align-items: center; + gap: 10px; + margin-bottom: 4px; +} + +.about-head h2 { + margin: 0; +} + +.about-links { + display: flex; + flex-wrap: wrap; + gap: 4px 16px; + margin: 0 0 4px; + padding: 0; + list-style: none; +} + +.dialog-actions { + display: flex; + justify-content: flex-end; + gap: 8px; + margin-top: 16px; +} + +.toast { + position: fixed; + left: 50%; + bottom: 24px; + z-index: 30; + max-width: calc(100vw - 32px); + padding: 8px 14px; + border-radius: 8px; + background: var(--mcode-primary-action-bg); + color: var(--mcode-primary-action-text); + box-shadow: var(--pd-shadow); + opacity: 0; + pointer-events: none; + transform: translate(-50%, 8px); + transition: opacity 0.15s ease, transform 0.15s ease; +} + +.toast.show { + opacity: 1; + transform: translate(-50%, 0); +} + +.toast[data-tone='error'] { + background: var(--mcode-danger); + color: #fff; +} + +/* Responsive -------------------------------------------------------------------------------- */ + +@media (max-width: 1079px) { + .app { + grid-template-columns: minmax(0, 1fr); + } + + .sidebar { + position: fixed; + inset: 0 auto 0 0; + z-index: 20; + width: 260px; + transform: translateX(-100%); + transition: transform 0.18s ease; + box-shadow: var(--pd-shadow); + } + + .sidebar-open .sidebar { + transform: none; + } + + .sidebar-open .scrim { + display: block; + position: fixed; + inset: 0; + z-index: 15; + background: rgb(10 10 10 / 25%); + } + + .menu-button { + display: inline-flex; + } + + .panes { + grid-template-columns: minmax(320px, 360px) minmax(0, 1fr); + } +} + +@media (max-width: 759px) { + .tabs-bar { + display: block; + padding: 8px 12px 0; + border-bottom: 1px solid var(--mcode-border); + } + + .tabs { + display: flex; + gap: 4px; + } + + .tabs [role='tab'] { + flex: 1; + height: 32px; + border: 0; + border-bottom: 2px solid transparent; + background: transparent; + color: var(--mcode-text-muted); + cursor: pointer; + } + + .tabs [role='tab'][aria-selected='true'] { + color: var(--mcode-text); + border-bottom-color: var(--mcode-text); + font-weight: 500; + } + + .panes { + grid-template-columns: minmax(0, 1fr); + } + + .chat { + border-right: 0; + } + + .app[data-tab='chat'] .preview, + .app[data-tab='preview'] .chat { + display: none; + } + + .preview-head { + flex-wrap: wrap; + } + + .thread-scroll { + padding: 12px; + } + + .composer { + margin: 0 12px 12px; + } + + .question-slot:not(:empty) { + padding: 0 12px 12px; + } +} + +@keyframes spin { + to { + transform: rotate(360deg); + } +} + +@keyframes blink { + 50% { + opacity: 0; + } +} + +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + scroll-behavior: auto !important; + } +} diff --git a/plugins/yujiachen-y/page-designer/miniapp/client/app.js b/plugins/yujiachen-y/page-designer/miniapp/client/app.js new file mode 100644 index 00000000..17ff82e7 --- /dev/null +++ b/plugins/yujiachen-y/page-designer/miniapp/client/app.js @@ -0,0 +1,1596 @@ +// @ts-check +// Page Designer page. Everything here talks only to this Mini App's own routes, plus the +// optional window.mcode.miniapp page APIs (opening a session in the chat side panel, popping +// the page out into its own window). + +/// + +const FX0_URL = 'https://github.com/vercel-labs/fx0'; +const LICENSE_URL = 'https://www.apache.org/licenses/LICENSE-2.0'; +const SOURCE_URL = 'https://github.com/MiniMax-AI/MiniMax-Code-MiniApps/tree/main/plugins/yujiachen-y/page-designer'; + +// --------------------------------------------------------------------------------------------- +// Strings + +const LANG = /^zh\b/i.test(navigator.language || '') ? 'zh' : 'en'; +document.documentElement.lang = LANG === 'zh' ? 'zh-CN' : 'en'; + +/** @type {Record<'en' | 'zh', Record>} */ +const STRINGS = { + en: { + appName: 'Page Designer', + newPage: 'New page', + pages: 'Pages', + noPages: 'No pages yet.', + versionsCount: '{n} versions', + versionsOne: '1 version', + versionsNone: 'No version yet', + working: 'Working', + about: 'About & credits', + menu: 'Pages', + chatTab: 'Chat', + previewTab: 'Preview', + rename: 'Rename', + renameLabel: 'Page title', + save: 'Save', + cancel: 'Cancel', + delete: 'Delete', + deleteTitle: 'Delete this page?', + deleteBody: '“{title}” and all its versions will be removed from Page Designer. The Agent conversation is kept in MiniMax Code.', + showConversation: 'Show conversation', + popOut: 'Open in window', + emptyTitle: 'What should we make?', + emptyBody: 'Describe a page. Your MiniMax Code Agent writes it as one self-contained HTML file, and it shows up in the preview.', + examplesLabel: 'Try one', + example1: 'A landing page for a neighborhood bakery with a weekly menu', + example2: 'A pomodoro timer with a calm animated background', + example3: 'An interactive explainer of how a bicycle gear works', + example4: 'A one-page résumé for a product designer', + styleLabel: 'Start from a style', + styleNone: 'No style', + styleNoneHint: 'Let the Agent choose.', + styleUpload: 'Your design.md', + styleUploadHint: 'Upload a design.md file.', + styleUploaded: 'Using {name}', + styleTooLarge: 'That file is too large (60,000 characters at most).', + styleUnreadable: 'That file could not be read as text.', + styleOnPage: 'Style: {name}', + credit: 'A MiniMax Code port of fx0 by Vercel Labs.', + creditLink: 'About', + placeholderNew: 'Describe the page you want…', + placeholderFollow: 'Ask for a change…', + send: 'Send', + stop: 'Stop', + model: 'Model', + modelDefault: 'Default ({name})', + effort: 'Thinking', + effortAuto: 'Auto', + modelsUnavailable: 'Models could not be loaded.', + agentUnavailable: 'This version of MiniMax Code does not let Page Designer run the Agent. Update to 3.1.2 or later.', + you: 'You', + agent: 'Agent', + answered: 'Answered', + versionChipPublish: 'Version {n}', + versionChipEdit: 'Version {n} · edited', + view: 'View', + viewing: 'Showing', + toolsUsed: 'Used {tools}', + toolFailed: '{tool} failed', + toolPublish: 'publish', + toolEdit: 'edit', + toolRead: 'read the page', + toolList: 'list pages', + toolSearch: 'web search', + toolFetch: 'fetch a page', + statusThinking: 'Thinking…', + statusTool: 'Using {tool}…', + statusPublish: 'Writing the page…', + statusEdit: 'Editing the page…', + permissionTitle: 'The Agent wants to use {tool}', + allowOnce: 'Allow once', + deny: 'Deny', + decideInChat: 'Decide in conversation', + questionTitle: 'The Agent has a question', + questionOther: 'Other', + questionOtherPlaceholder: 'Type your answer', + questionSubmit: 'Answer', + questionSkip: 'Skip', + questionUnsupported: 'Answer this in the conversation.', + previewEmpty: 'The page will appear here.', + previewLoading: 'Loading preview…', + previewFailed: 'The preview could not be loaded.', + latest: 'Latest (v{n})', + versionOption: 'Version {n}', + desktop: 'Desktop', + tablet: 'Tablet', + phone: 'Phone', + device: 'Preview width', + copy: 'Copy HTML', + copied: 'Copied the HTML.', + copyFailed: 'Could not copy. Try Download instead.', + download: 'Download', + downloaded: 'Downloaded {file}.', + retry: 'Retry', + reload: 'Reload preview', + disconnected: 'Page Designer restarted. Refresh the page to reconnect.', + refresh: 'Refresh', + aboutTitle: 'About Page Designer', + aboutP1: 'Page Designer is a MiniMax Code port of fx0, an open-source design agent by Vercel Labs: you describe a page and an agent writes it as one HTML file.', + aboutP2: 'The design instructions, the HTML safety rules, the exact-replacement edits and the sandboxed preview are adapted from fx0, under the Apache License 2.0. Thank you to the fx0 authors for building it in the open.', + aboutP3: 'What is different here: the pages are written by your own MiniMax Code Agent, with the model you pick, through this Mini App’s tools. Pages, versions and chats stay on this computer.', + aboutP4: 'Page Designer is an independent community project. It is not affiliated with or endorsed by Vercel.', + aboutFx0: 'fx0 on GitHub', + aboutLicense: 'Apache License 2.0', + aboutSource: 'Page Designer source', + close: 'Close', + errBUSY: 'The Agent is busy right now. Try again in a moment.', + errAGENT_UNAVAILABLE: 'The Agent is not available right now. Try again in a moment.', + errAGENT_OUTCOME_UNKNOWN: 'It is not clear whether that request ran. Check the conversation before trying again.', + errSESSION_NOT_FOUND: 'The linked conversation no longer exists.', + errINVALID_ARGUMENTS: 'The selected model cannot be used. Pick another one.', + errNOT_PERMITTED: 'Page Designer is not allowed to run the Agent.', + errREQUEST_NOT_FOUND: 'That request has already been handled.', + errTURN_ERROR: 'The Agent stopped with an error.', + errABORTED: 'Stopped.', + errREQUEST_CANCELLED: 'Stopped.', + errUNKNOWN: 'Something went wrong.', + errNetwork: 'Page Designer could not be reached.', + }, + zh: { + appName: 'Page Designer', + newPage: '新建页面', + pages: '页面', + noPages: '还没有页面。', + versionsCount: '{n} 个版本', + versionsOne: '1 个版本', + versionsNone: '还没有版本', + working: '进行中', + about: '关于与致谢', + menu: '页面列表', + chatTab: '对话', + previewTab: '预览', + rename: '重命名', + renameLabel: '页面标题', + save: '保存', + cancel: '取消', + delete: '删除', + deleteTitle: '删除这个页面?', + deleteBody: '「{title}」和它的所有版本会从 Page Designer 中移除,MiniMax Code 里的 Agent 对话会保留。', + showConversation: '查看对话', + popOut: '在窗口中打开', + emptyTitle: '想做一个什么页面?', + emptyBody: '描述你想要的页面,MiniMax Code 的 Agent 会把它写成一个独立的 HTML 文件,并显示在预览里。', + examplesLabel: '试试这些', + example1: '一家社区面包店的落地页,带每周菜单', + example2: '一个番茄钟,背景是舒缓的动画', + example3: '交互式讲解自行车变速器是怎么工作的', + example4: '一页纸的产品设计师简历', + styleLabel: '选择一个起始风格', + styleNone: '不指定', + styleNoneHint: '让 Agent 自己决定。', + styleUpload: '自己的 design.md', + styleUploadHint: '上传一个 design.md 文件。', + styleUploaded: '使用 {name}', + styleTooLarge: '文件太大了(最多 60,000 个字符)。', + styleUnreadable: '这个文件无法作为文本读取。', + styleOnPage: '风格:{name}', + credit: '移植自 Vercel Labs 的开源项目 fx0。', + creditLink: '了解更多', + placeholderNew: '描述你想要的页面…', + placeholderFollow: '想怎么改…', + send: '发送', + stop: '停止', + model: '模型', + modelDefault: '默认({name})', + effort: '思考', + effortAuto: '自动', + modelsUnavailable: '无法加载模型列表。', + agentUnavailable: '当前版本的 MiniMax Code 不支持 Page Designer 运行 Agent,请升级到 3.1.2 或更高版本。', + you: '你', + agent: 'Agent', + answered: '已回答', + versionChipPublish: '版本 {n}', + versionChipEdit: '版本 {n} · 修改', + view: '查看', + viewing: '预览中', + toolsUsed: '使用了 {tools}', + toolFailed: '{tool} 失败', + toolPublish: '发布', + toolEdit: '修改', + toolRead: '读取页面', + toolList: '列出页面', + toolSearch: '网页搜索', + toolFetch: '抓取网页', + statusThinking: '思考中…', + statusTool: '正在使用 {tool}…', + statusPublish: '正在写页面…', + statusEdit: '正在修改页面…', + permissionTitle: 'Agent 想使用 {tool}', + allowOnce: '允许一次', + deny: '拒绝', + decideInChat: '去对话里决定', + questionTitle: 'Agent 有个问题', + questionOther: '其他', + questionOtherPlaceholder: '输入你的回答', + questionSubmit: '回答', + questionSkip: '跳过', + questionUnsupported: '请在对话里回答这个问题。', + previewEmpty: '页面会显示在这里。', + previewLoading: '正在载入预览…', + previewFailed: '预览载入失败。', + latest: '最新(v{n})', + versionOption: '版本 {n}', + desktop: '桌面', + tablet: '平板', + phone: '手机', + device: '预览宽度', + copy: '复制 HTML', + copied: '已复制 HTML。', + copyFailed: '复制失败,可以改用下载。', + download: '下载', + downloaded: '已下载 {file}。', + retry: '重试', + reload: '重新载入预览', + disconnected: 'Page Designer 已重启,请刷新页面重新连接。', + refresh: '刷新', + aboutTitle: '关于 Page Designer', + aboutP1: 'Page Designer 是 Vercel Labs 开源设计 Agent fx0 在 MiniMax Code 上的移植版:你描述一个页面,Agent 把它写成一个 HTML 文件。', + aboutP2: '设计指令、HTML 安全规则、精确替换式修改和沙箱预览都改编自 fx0,遵循 Apache License 2.0。感谢 fx0 的作者们把它开源出来。', + aboutP3: '不同之处:页面由你自己的 MiniMax Code Agent 用你选择的模型,通过这个 Mini App 的工具写出;页面、版本和对话都保存在这台电脑上。', + aboutP4: 'Page Designer 是独立的社区项目,与 Vercel 没有隶属或背书关系。', + aboutFx0: 'GitHub 上的 fx0', + aboutLicense: 'Apache License 2.0', + aboutSource: 'Page Designer 源码', + close: '关闭', + errBUSY: 'Agent 正忙,请稍后再试。', + errAGENT_UNAVAILABLE: 'Agent 暂时不可用,请稍后再试。', + errAGENT_OUTCOME_UNKNOWN: '无法确认这次请求是否已执行,请先查看对话再决定是否重试。', + errSESSION_NOT_FOUND: '关联的对话已不存在。', + errINVALID_ARGUMENTS: '所选模型无法使用,请换一个。', + errNOT_PERMITTED: 'Page Designer 没有运行 Agent 的权限。', + errREQUEST_NOT_FOUND: '这个请求已经处理过了。', + errTURN_ERROR: 'Agent 出错停止了。', + errABORTED: '已停止。', + errREQUEST_CANCELLED: '已停止。', + errUNKNOWN: '出了点问题。', + errNetwork: '无法连接 Page Designer。', + }, +}; + +/** @param {string} key @param {Record} [vars] */ +function t(key, vars) { + const text = STRINGS[LANG][key] ?? STRINGS.en[key] ?? key; + return vars ? text.replace(/\{(\w+)\}/g, (_, name) => String(vars[name] ?? '')) : text; +} + +/** @param {string} code */ +function errorText(code) { + return STRINGS[LANG][`err${code}`] ?? STRINGS.en[`err${code}`] ?? t('errUNKNOWN'); +} + +// --------------------------------------------------------------------------------------------- +// DOM helpers + +/** + * @param {string} tag + * @param {Record | null} [props] + * @param {...(Node | string | null | undefined | false | (Node | string | null | undefined | false)[])} children + * @returns {HTMLElement} + */ +function h(tag, props, ...children) { + const node = document.createElement(tag); + for (const [key, value] of Object.entries(props ?? {})) { + if (value === undefined || value === null || value === false) continue; + if (key.startsWith('on') && typeof value === 'function') node.addEventListener(key.slice(2).toLowerCase(), value); + else if (key === 'class') node.className = value; + else if (key === 'dataset') Object.assign(node.dataset, value); + else if (key in node && typeof value !== 'string') /** @type {any} */ (node)[key] = value; + else node.setAttribute(key, value === true ? '' : String(value)); + } + for (const child of children.flat()) { + if (child === null || child === undefined || child === false) continue; + node.append(typeof child === 'string' ? document.createTextNode(child) : child); + } + return node; +} + +const ICONS = { + plus: '', + menu: '', + send: '', + stop: '', + copy: '', + download: '', + trash: '', + pencil: '', + desktop: '', + tablet: '', + phone: '', + chat: '', + window: '', + reload: '', + info: '', + check: '', + external: '', + upload: '', +}; + +/** @param {keyof typeof ICONS} name */ +function icon(name) { + const span = document.createElement('span'); + span.className = 'icon'; + span.setAttribute('aria-hidden', 'true'); + span.innerHTML = `${ICONS[name]}`; + return span; +} + +const LOGO_SVG = + ''; + +/** @param {number} ms */ +function formatDate(ms) { + const date = new Date(ms); + const sameDay = new Date().toDateString() === date.toDateString(); + return new Intl.DateTimeFormat(LANG === 'zh' ? 'zh-CN' : 'en', sameDay ? { hour: '2-digit', minute: '2-digit' } : { month: 'short', day: 'numeric' }).format(date); +} + +/** @param {string} url */ +function openExternal(url) { + try { + const parsed = new URL(url); + if (parsed.protocol !== 'https:' || parsed.username || parsed.password) return; + // With noopener, window.open returns null even when it worked; MiniMax Code decides where it opens. + window.open(parsed.href, '_blank', 'noopener,noreferrer'); + } catch { + // not a URL: nothing to open + } +} + +/** @param {string} href @param {string} label */ +function externalLink(href, label) { + return h('a', { href, target: '_blank', rel: 'noopener noreferrer', class: 'link' }, label, icon('external')); +} + +// --------------------------------------------------------------------------------------------- +// Markdown (a safe subset: paragraphs, lists, headings, code, bold, italics, https links) + +/** @param {string} text */ +function inline(text) { + /** @type {(Node | string)[]} */ + const parts = []; + const pattern = /(`[^`\n]+`)|(\*\*[^*\n]+\*\*)|(\*[^*\n]+\*)|(\[[^\]\n]+\]\(https:\/\/[^\s)]+\))|(https:\/\/[^\s<>()]+[^\s<>().,;:!?'"])/g; + let last = 0; + for (const match of text.matchAll(pattern)) { + const index = match.index ?? 0; + if (index > last) parts.push(text.slice(last, index)); + const token = match[0]; + if (match[1]) parts.push(h('code', null, token.slice(1, -1))); + else if (match[2]) parts.push(h('strong', null, token.slice(2, -2))); + else if (match[3]) parts.push(h('em', null, token.slice(1, -1))); + else if (match[4]) { + const label = token.slice(1, token.indexOf('](')); + const href = token.slice(token.indexOf('](') + 2, -1); + parts.push(h('a', { href, target: '_blank', rel: 'noopener noreferrer' }, label)); + } else parts.push(h('a', { href: token, target: '_blank', rel: 'noopener noreferrer' }, token)); + last = index + token.length; + } + if (last < text.length) parts.push(text.slice(last)); + return parts; +} + +/** @param {string} source */ +function markdown(source) { + const root = h('div', { class: 'md' }); + const chunks = source.replace(/\r\n?/g, '\n').split(/^```[^\n]*\n?/m); + chunks.forEach((chunk, index) => { + if (index % 2 === 1) { + root.append(h('pre', null, h('code', null, chunk.replace(/\n$/, '')))); + return; + } + for (const block of chunk.split(/\n{2,}/)) { + const lines = block.split('\n').filter((line) => line.trim() !== ''); + if (lines.length === 0) continue; + if (lines.every((line) => /^\s*[-*]\s+/.test(line))) { + root.append(h('ul', null, lines.map((line) => h('li', null, inline(line.replace(/^\s*[-*]\s+/, '')))))); + } else if (lines.every((line) => /^\s*\d+[.)]\s+/.test(line))) { + root.append(h('ol', null, lines.map((line) => h('li', null, inline(line.replace(/^\s*\d+[.)]\s+/, '')))))); + } else if (lines.length === 1 && /^#{1,6}\s+/.test(lines[0])) { + root.append(h('p', { class: 'md-heading' }, inline(lines[0].replace(/^#{1,6}\s+/, '')))); + } else { + const paragraph = h('p'); + lines.forEach((line, lineIndex) => { + if (lineIndex > 0) paragraph.append(h('br')); + paragraph.append(...inline(line)); + }); + root.append(paragraph); + } + } + }); + return root; +} + +// --------------------------------------------------------------------------------------------- +// API + +class ApiError extends Error { + /** @param {string} code @param {string} [message] @param {number} [status] */ + constructor(code, message, status) { + super(message || code); + this.code = code; + this.status = status; + } +} + +/** @param {string} method @param {string} path @param {unknown} [body] */ +async function api(method, path, body) { + /** @type {Response} */ + let response; + try { + response = await fetch(path, { + method, + credentials: 'same-origin', + headers: body === undefined && method === 'GET' ? {} : { 'content-type': 'application/json' }, + body: method === 'GET' ? undefined : JSON.stringify(body ?? {}), + }); + } catch { + throw new ApiError('network', t('errNetwork')); + } + if (response.status === 401) { + showDisconnected(); + throw new ApiError('unauthorized', t('disconnected'), 401); + } + const type = response.headers.get('content-type') ?? ''; + const data = type.includes('application/json') ? await response.json().catch(() => ({})) : await response.text(); + if (!response.ok) { + const code = typeof data === 'object' && data?.error ? String(data.error) : 'UNKNOWN'; + const message = typeof data === 'object' && data?.message ? String(data.message) : errorText(code); + throw new ApiError(code, /^[A-Z_]+$/.test(code) ? errorText(code) : message, response.status); + } + return data; +} + +// --------------------------------------------------------------------------------------------- +// State + +const MODEL_KEY = 'page-designer.model'; +const DEVICE_KEY = 'page-designer.device'; + +const state = { + agentAvailable: true, + /** @type {any[]} */ projects: [], + /** @type {any[]} */ styles: [], + /** @type {any} */ models: null, + modelsFailed: false, + /** model choice: '' means the runtime default, otherwise providerId + '\u0000' + modelId */ + modelKey: '', + effort: '', + /** @type {string | null} */ currentId: null, + /** @type {any} */ project: null, + /** @type {any} */ live: null, + /** @type {number | null} */ viewing: null, + device: localStorage.getItem(DEVICE_KEY) || 'desktop', + tab: 'chat', + /** @type {{ preset: string } | { name: string, text: string } | null} */ newStyle: null, + sending: false, + /** the next render of a project opens at its latest message */ + scrollToEnd: false, +}; + +try { + const saved = JSON.parse(localStorage.getItem(MODEL_KEY) || 'null'); + if (saved && typeof saved.modelKey === 'string') { + state.modelKey = saved.modelKey; + state.effort = typeof saved.effort === 'string' ? saved.effort : ''; + } +} catch { + // a broken preference is ignored +} + +/** @param {any} selection */ +function keyOf(selection) { + return selection && selection.providerId && selection.modelId ? `${selection.providerId}\u0000${selection.modelId}` : ''; +} + +function currentModelOption() { + if (!state.models || !state.modelKey) return null; + return state.models.models.find((/** @type {any} */ item) => keyOf(item) === state.modelKey) ?? null; +} + +/** The selection to send with a request, or null to keep the session's model. */ +function modelSelection() { + if (!state.models) return null; + if (!state.modelKey) return state.models.default ?? null; + const option = currentModelOption(); + if (!option) return state.models.default ?? null; + /** @type {Record} */ + const selection = { providerId: option.providerId, modelId: option.modelId }; + if (state.effort && option.thinking?.efforts?.includes(state.effort)) selection.thinking = { effort: state.effort }; + return selection; +} + +/** @param {any} model */ +function modelName(model) { + if (!model) return ''; + const option = state.models?.models?.find((/** @type {any} */ item) => item.providerId === model.providerId && item.modelId === model.modelId); + return option?.displayName ?? model.modelId ?? ''; +} + +// --------------------------------------------------------------------------------------------- +// Layout + +const root = /** @type {HTMLElement} */ (document.getElementById('app')); +const els = /** @type {Record} */ ({}); + +function buildShell() { + root.replaceChildren(); + root.removeAttribute('aria-busy'); + + els.projectList = h('ul', { class: 'project-list' }); + els.sidebar = h( + 'aside', + { class: 'sidebar', id: 'sidebar', 'aria-label': t('pages') }, + h('div', { class: 'brand' }, logo(), h('span', null, t('appName'))), + h('button', { type: 'button', class: 'btn primary block', onclick: () => selectProject(null) }, icon('plus'), t('newPage')), + h('nav', { class: 'project-nav', 'aria-label': t('pages') }, els.projectList), + h('div', { class: 'sidebar-foot' }, h('button', { type: 'button', class: 'btn ghost small', onclick: openAbout }, icon('info'), t('about'))), + ); + els.scrim = h('div', { class: 'scrim', onclick: () => toggleSidebar(false) }); + + els.title = h('h1', { class: 'title' }); + els.titleActions = h('div', { class: 'title-actions' }); + els.tabs = h( + 'div', + { class: 'tabs', role: 'tablist', 'aria-label': t('appName') }, + h('button', { type: 'button', role: 'tab', id: 'tab-chat', 'aria-controls': 'chat-panel', onclick: () => setTab('chat') }, t('chatTab')), + h('button', { type: 'button', role: 'tab', id: 'tab-preview', 'aria-controls': 'preview-panel', onclick: () => setTab('preview') }, t('previewTab')), + ); + els.menuButton = h('button', { type: 'button', class: 'btn icon-only ghost menu-button', 'aria-label': t('menu'), 'aria-controls': 'sidebar', 'aria-expanded': 'false', onclick: () => toggleSidebar() }, icon('menu')); + els.banner = h('div', { class: 'banner', role: 'alert', hidden: true }); + els.thread = h('div', { class: 'thread', role: 'log', 'aria-live': 'polite', 'aria-relevant': 'additions' }); + els.live = h('div', { class: 'live' }); + els.question = h('div', { class: 'question-slot' }); + els.composer = buildComposer(); + + els.chat = h( + 'section', + { class: 'chat', id: 'chat-panel', role: 'tabpanel', 'aria-labelledby': 'tab-chat' }, + h('header', { class: 'chat-head' }, els.menuButton, h('div', { class: 'title-wrap' }, els.title, els.titleActions)), + els.banner, + h('div', { class: 'thread-scroll' }, els.thread, els.live), + els.question, + els.composer, + ); + + els.preview = buildPreview(); + els.toast = h('div', { class: 'toast', role: 'status', 'aria-live': 'polite' }); + + root.append( + els.sidebar, + els.scrim, + h('main', { class: 'main' }, h('div', { class: 'tabs-bar' }, els.tabs), h('div', { class: 'panes' }, els.chat, els.preview)), + els.toast, + ); + setTab(state.tab); +} + +function logo() { + const span = h('span', { class: 'logo' }); + span.innerHTML = LOGO_SVG; + return span; +} + +/** @param {boolean} [open] */ +function toggleSidebar(open) { + const next = open ?? !root.classList.contains('sidebar-open'); + root.classList.toggle('sidebar-open', next); + els.menuButton.setAttribute('aria-expanded', String(next)); +} + +/** @param {'chat' | 'preview'} tab */ +function setTab(tab) { + state.tab = tab; + root.dataset.tab = tab; + for (const button of els.tabs.querySelectorAll('[role="tab"]')) { + const selected = button.id === `tab-${tab}`; + button.setAttribute('aria-selected', String(selected)); + button.setAttribute('tabindex', selected ? '0' : '-1'); + } + if (tab === 'preview') preview.ensureShown(); +} + +let toastTimer = 0; +/** @param {string} text @param {'info' | 'error'} [tone] */ +function toast(text, tone = 'info') { + els.toast.textContent = text; + els.toast.dataset.tone = tone; + els.toast.classList.add('show'); + clearTimeout(toastTimer); + toastTimer = window.setTimeout(() => els.toast.classList.remove('show'), 3200); +} + +function showDisconnected() { + els.banner.hidden = false; + els.banner.replaceChildren( + h('span', null, t('disconnected')), + h('button', { type: 'button', class: 'btn small', onclick: () => location.reload() }, t('refresh')), + ); +} + +// --------------------------------------------------------------------------------------------- +// Sidebar + +function renderProjects() { + if (state.projects.length === 0) { + els.projectList.replaceChildren(h('li', { class: 'muted empty-list' }, t('noPages'))); + return; + } + els.projectList.replaceChildren( + ...state.projects.map((project) => { + const current = project.id === state.currentId; + const meta = project.running + ? h('span', { class: 'pill working' }, t('working')) + : h('span', null, project.versions === 0 ? t('versionsNone') : project.versions === 1 ? t('versionsOne') : t('versionsCount', { n: project.versions }), ' · ', formatDate(project.updatedAt)); + return h( + 'li', + null, + h( + 'button', + { type: 'button', class: `project${current ? ' current' : ''}`, 'aria-current': current ? 'page' : undefined, onclick: () => selectProject(project.id) }, + h('span', { class: 'project-title' }, project.title), + h('span', { class: 'project-meta' }, meta), + ), + ); + }), + ); +} + +// --------------------------------------------------------------------------------------------- +// Header + +function renderHeader() { + const project = state.project; + els.titleActions.replaceChildren(); + if (!project) { + els.title.textContent = t('newPage'); + return; + } + els.title.textContent = project.title; + els.title.title = project.title; + els.titleActions.append(h('button', { type: 'button', class: 'btn icon-only ghost', 'aria-label': t('rename'), title: t('rename'), onclick: startRename }, icon('pencil'))); + const agentApi = window.mcode?.miniapp?.agent; + if (agentApi?.open && project.sessionId) { + els.titleActions.append( + h('button', { type: 'button', class: 'btn icon-only ghost', 'aria-label': t('showConversation'), title: t('showConversation'), onclick: () => openConversation() }, icon('chat')), + ); + } + els.titleActions.append( + h('button', { type: 'button', class: 'btn icon-only ghost danger-hover', 'aria-label': t('delete'), title: t('delete'), disabled: !!state.live, onclick: confirmDelete }, icon('trash')), + ); +} + +function startRename() { + const project = state.project; + if (!project) return; + const input = /** @type {HTMLInputElement} */ (h('input', { class: 'input', value: project.title, 'aria-label': t('renameLabel'), maxlength: '80' })); + // Enter or Escape removes the input, which also fires blur: only the first one counts, so + // Escape really cancels and Enter saves once. + let finished = false; + const finish = async (/** @type {boolean} */ save) => { + if (finished) return; + finished = true; + const title = input.value.trim(); + if (save && title && title !== project.title) { + try { + await api('PATCH', `/api/projects/${project.id}`, { title }); + } catch (error) { + toast(/** @type {Error} */ (error).message, 'error'); + } + } + renderHeader(); + }; + input.addEventListener('keydown', (event) => { + if (event.key === 'Enter' && !event.isComposing) void finish(true); + if (event.key === 'Escape') void finish(false); + }); + input.addEventListener('blur', () => void finish(true), { once: true }); + els.title.replaceChildren(input); + els.titleActions.replaceChildren(); + input.focus(); + input.select(); +} + +async function openConversation() { + const sessionId = state.project?.sessionId; + const agentApi = window.mcode?.miniapp?.agent; + if (!sessionId || !agentApi?.open) return; + try { + await agentApi.open(sessionId); + } catch (error) { + toast(errorText(String(/** @type {Error} */ (error)?.message || 'UNKNOWN').toUpperCase()), 'error'); + } +} + +/** @param {string} title @param {string} body @param {string} confirmLabel */ +function confirmDialog(title, body, confirmLabel) { + return new Promise((resolve) => { + const dialog = /** @type {HTMLDialogElement} */ ( + h( + 'dialog', + { class: 'dialog', 'aria-labelledby': 'confirm-title' }, + h('h2', { id: 'confirm-title' }, title), + h('p', null, body), + h( + 'div', + { class: 'dialog-actions' }, + h('button', { type: 'button', class: 'btn', onclick: () => dialog.close('cancel') }, t('cancel')), + h('button', { type: 'button', class: 'btn danger', onclick: () => dialog.close('confirm') }, confirmLabel), + ), + ) + ); + dialog.addEventListener('close', () => { + resolve(dialog.returnValue === 'confirm'); + dialog.remove(); + }); + document.body.append(dialog); + dialog.showModal(); + }); +} + +async function confirmDelete() { + const project = state.project; + if (!project) return; + if (!(await confirmDialog(t('deleteTitle'), t('deleteBody', { title: project.title }), t('delete')))) return; + try { + await api('DELETE', `/api/projects/${project.id}`); + await refreshState(); + selectProject(null); + } catch (error) { + toast(/** @type {Error} */ (error).message, 'error'); + } +} + +function openAbout() { + const dialog = /** @type {HTMLDialogElement} */ ( + h( + 'dialog', + { class: 'dialog about', 'aria-labelledby': 'about-title' }, + h('div', { class: 'about-head' }, logo(), h('h2', { id: 'about-title' }, t('aboutTitle'))), + h('p', null, t('aboutP1')), + h('p', null, t('aboutP2')), + h('p', null, t('aboutP3')), + h('p', { class: 'muted' }, t('aboutP4')), + h('ul', { class: 'about-links' }, h('li', null, externalLink(FX0_URL, t('aboutFx0'))), h('li', null, externalLink(LICENSE_URL, t('aboutLicense'))), h('li', null, externalLink(SOURCE_URL, t('aboutSource')))), + h('div', { class: 'dialog-actions' }, h('button', { type: 'button', class: 'btn primary', onclick: () => dialog.close() }, t('close'))), + ) + ); + dialog.addEventListener('close', () => dialog.remove()); + document.body.append(dialog); + dialog.showModal(); +} + +// --------------------------------------------------------------------------------------------- +// Thread + +/** @param {string} name */ +function toolLabel(name) { + const n = name.toLowerCase(); + if (n.includes('publish_design')) return t('toolPublish'); + if (n.includes('edit_design')) return t('toolEdit'); + if (n.includes('read_design')) return t('toolRead'); + if (n.includes('list_designs')) return t('toolList'); + if (n.includes('search')) return t('toolSearch'); + if (n.includes('fetch')) return t('toolFetch'); + return name.replace(/^mcp__/, '').replace(/__/g, ' · '); +} + +/** Successful page writes already show as version chips. */ +function quietTool(/** @type {any} */ message) { + return message.ok && /publish_design|edit_design/i.test(message.name); +} + +function renderThread() { + const project = state.project; + const scroller = els.thread.parentElement; + const nearBottom = state.scrollToEnd || !scroller || scroller.scrollHeight - scroller.scrollTop - scroller.clientHeight < 80; + if (!project) { + if (scroller) scroller.scrollTop = 0; + els.thread.replaceChildren(emptyState()); + return; + } + /** @type {HTMLElement[]} */ + const items = []; + if (project.style) items.push(h('p', { class: 'style-note muted' }, t('styleOnPage', { name: project.style.name }))); + /** @type {any[]} */ + let tools = []; + const flushTools = () => { + const shown = tools.filter((item) => !quietTool(item)); + tools = []; + if (shown.length === 0) return; + const failed = shown.filter((item) => !item.ok); + const used = [...new Set(shown.filter((item) => item.ok).map((item) => toolLabel(item.name)))]; + if (used.length) items.push(h('p', { class: 'activity' }, icon('check'), t('toolsUsed', { tools: used.join(LANG === 'zh' ? '、' : ', ') }))); + for (const item of failed) items.push(h('p', { class: 'activity failed' }, icon('info'), t('toolFailed', { tool: toolLabel(item.name) }))); + }; + for (const message of project.messages) { + if (message.kind === 'tool') { + tools.push(message); + continue; + } + flushTools(); + items.push(renderMessage(message)); + } + flushTools(); + els.thread.replaceChildren(...items); + state.scrollToEnd = false; + if (nearBottom && scroller) scroller.scrollTop = scroller.scrollHeight; +} + +/** @param {any} message */ +function renderMessage(message) { + switch (message.kind) { + case 'user': + return h('div', { class: 'msg user' }, h('span', { class: 'sr-only' }, `${t('you')}: `), h('div', { class: 'bubble' }, message.text)); + case 'answer': + return h('div', { class: 'msg user' }, h('div', { class: 'bubble' }, h('span', { class: 'bubble-label' }, t('answered')), message.text || '')); + case 'assistant': + return h('div', { class: 'msg assistant' }, h('span', { class: 'sr-only' }, `${t('agent')}: `), markdown(message.text)); + case 'version': { + const shown = (state.viewing ?? latestVersion()) === message.n; + return h( + 'div', + { class: `version-chip${shown ? ' shown' : ''}` }, + h('span', { class: 'version-dot', 'aria-hidden': 'true' }), + h('span', null, t(message.via === 'edit' ? 'versionChipEdit' : 'versionChipPublish', { n: message.n })), + shown + ? h('span', { class: 'muted small' }, t('viewing')) + : h('button', { type: 'button', class: 'btn ghost small', onclick: () => showVersion(message.n) }, t('view')), + ); + } + case 'notice': { + const detail = message.text ? `${errorText(message.code)} ${message.text}` : errorText(message.code); + return h('p', { class: `notice ${message.tone === 'error' ? 'error' : ''}`, role: message.tone === 'error' ? 'alert' : undefined }, detail); + } + default: + return h('span'); + } +} + +function latestVersion() { + return state.project?.versions?.at(-1)?.n ?? 0; +} + +function emptyState() { + const examples = ['example1', 'example2', 'example3', 'example4'].map((key) => + h('button', { type: 'button', class: 'chip', onclick: () => fillComposer(t(key)) }, t(key)), + ); + const styleCards = [ + styleCard('none', t('styleNone'), t('styleNoneHint'), []), + ...state.styles.map((style) => styleCard(style.id, style.name[LANG] ?? style.name.en, style.description[LANG] ?? style.description.en, style.swatches)), + uploadCard(), + ]; + return h( + 'section', + { class: 'empty', 'aria-labelledby': 'empty-title' }, + h('h2', { id: 'empty-title' }, t('emptyTitle')), + h('p', { class: 'muted' }, t('emptyBody')), + state.agentAvailable ? null : h('p', { class: 'notice error', role: 'alert' }, t('agentUnavailable')), + h('h3', null, t('examplesLabel')), + h('div', { class: 'chips' }, examples), + h('h3', { id: 'style-label' }, t('styleLabel')), + h('div', { class: 'style-grid', role: 'radiogroup', 'aria-labelledby': 'style-label' }, styleCards), + h('p', { class: 'credit muted small' }, t('credit'), ' ', h('button', { type: 'button', class: 'link-button', onclick: openAbout }, t('creditLink'))), + ); +} + +function selectedStyleId() { + if (!state.newStyle) return 'none'; + return 'preset' in state.newStyle ? state.newStyle.preset : 'upload'; +} + +/** @param {string} id @param {string} name @param {string} hint @param {string[]} swatches */ +function styleCard(id, name, hint, swatches) { + const checked = selectedStyleId() === id; + return h( + 'button', + { + type: 'button', + role: 'radio', + 'aria-checked': String(checked), + class: `style-card${checked ? ' selected' : ''}`, + onclick: () => { + state.newStyle = id === 'none' ? null : { preset: id }; + renderThread(); + }, + }, + h('span', { class: 'swatches', 'aria-hidden': 'true' }, swatches.length ? swatches.map((color) => h('span', { class: 'swatch', style: `background:${color}` })) : h('span', { class: 'swatch none' })), + h('span', { class: 'style-name' }, name), + h('span', { class: 'style-hint' }, hint), + ); +} + +function uploadCard() { + const checked = selectedStyleId() === 'upload'; + const custom = state.newStyle && 'text' in state.newStyle ? state.newStyle : null; + const input = /** @type {HTMLInputElement} */ (h('input', { type: 'file', accept: '.md,.markdown,.txt,text/markdown,text/plain', class: 'sr-only', tabindex: '-1', 'aria-hidden': 'true' })); + input.addEventListener('change', async () => { + const file = input.files?.[0]; + if (!file) return; + if (file.size > 400_000) return toast(t('styleTooLarge'), 'error'); + try { + const text = await file.text(); + if (text.includes('\u0000')) return toast(t('styleUnreadable'), 'error'); + if (text.length > 60_000) return toast(t('styleTooLarge'), 'error'); + state.newStyle = { name: file.name.slice(0, 80), text }; + renderThread(); + } catch { + toast(t('styleUnreadable'), 'error'); + } + }); + return h( + 'button', + { type: 'button', role: 'radio', 'aria-checked': String(checked), class: `style-card${checked ? ' selected' : ''}`, onclick: () => input.click() }, + h('span', { class: 'swatches', 'aria-hidden': 'true' }, icon('upload')), + h('span', { class: 'style-name' }, t('styleUpload')), + h('span', { class: 'style-hint' }, custom ? t('styleUploaded', { name: custom.name }) : t('styleUploadHint')), + input, + ); +} + +// --------------------------------------------------------------------------------------------- +// Live run + +function renderLive() { + const live = state.live; + els.live.replaceChildren(); + if (!live) return; + const scroller = els.live.parentElement; + const nearBottom = !scroller || scroller.scrollHeight - scroller.scrollTop - scroller.clientHeight < 120; + if (live.text) els.live.append(h('div', { class: 'msg assistant streaming' }, markdown(live.text))); + let status = t('statusThinking'); + if (live.tool) { + if (/publish_design/i.test(live.tool)) status = t('statusPublish'); + else if (/edit_design/i.test(live.tool)) status = t('statusEdit'); + else status = t('statusTool', { tool: toolLabel(live.tool) }); + } + if (live.permissions.length === 0) { + els.live.append( + h('p', { class: 'working-line', role: 'status' }, h('span', { class: 'spinner', 'aria-hidden': 'true' }), status, live.model ? h('span', { class: 'muted small' }, ` · ${modelName(live.model)}`) : null), + ); + } + for (const permission of live.permissions) els.live.append(permissionCard(permission)); + if (nearBottom && scroller) scroller.scrollTop = scroller.scrollHeight; +} + +/** @param {any} permission */ +function permissionCard(permission) { + const agentApi = window.mcode?.miniapp?.agent; + const decide = async (/** @type {'allowOnce' | 'deny'} */ decision, /** @type {HTMLElement} */ card) => { + for (const button of card.querySelectorAll('button')) /** @type {HTMLButtonElement} */ (button).disabled = true; + try { + await api('POST', `/api/projects/${state.currentId}/permissions`, { requestId: permission.requestId, decision }); + } catch (error) { + toast(/** @type {Error} */ (error).message, 'error'); + for (const button of card.querySelectorAll('button')) /** @type {HTMLButtonElement} */ (button).disabled = false; + } + }; + const card = h( + 'section', + { class: 'card permission', 'aria-label': t('permissionTitle', { tool: permission.toolName }) }, + h('h3', null, t('permissionTitle', { tool: toolLabel(permission.toolName) })), + permission.reason ? h('p', null, permission.reason) : null, + permission.toolInput ? h('pre', { class: 'tool-input' }, permission.toolInput) : null, + ); + card.append( + h( + 'div', + { class: 'card-actions' }, + agentApi?.open && state.project?.sessionId ? h('button', { type: 'button', class: 'btn ghost', onclick: () => openConversation() }, t('decideInChat')) : null, + h('span', { class: 'spacer' }), + h('button', { type: 'button', class: 'btn', onclick: () => decide('deny', card) }, t('deny')), + h('button', { type: 'button', class: 'btn primary', onclick: () => decide('allowOnce', card) }, t('allowOnce')), + ), + ); + return card; +} + +// --------------------------------------------------------------------------------------------- +// Questionnaire + +function renderQuestion() { + const pending = state.project?.pendingQuestion; + els.question.replaceChildren(); + els.composer.hidden = !!pending; + if (!pending) return; + const request = pending.request ?? {}; + const steps = Array.isArray(request.steps) ? request.steps : []; + const form = h('form', { class: 'card question', 'aria-labelledby': 'question-title' }); + form.append(h('h3', { id: 'question-title' }, request.title || t('questionTitle'))); + if (pending.mode !== 'questionnaire' || steps.length === 0) { + form.append(h('p', null, t('questionUnsupported'))); + } + steps.forEach((/** @type {any} */ step, index) => { + const name = `step-${index}`; + const type = step.selectionMode === 'multiple' ? 'checkbox' : 'radio'; + const otherInput = /** @type {HTMLInputElement} */ (h('input', { class: 'input', type: 'text', placeholder: t('questionOtherPlaceholder'), 'aria-label': t('questionOther'), dataset: { otherFor: name } })); + form.append( + h( + 'fieldset', + { class: 'q-step', dataset: { stepId: step.id, name } }, + h('legend', null, step.header ? h('span', { class: 'q-header' }, step.header) : null, step.question), + step.description ? h('p', { class: 'muted small' }, step.description) : null, + ...(step.options ?? []).map((/** @type {any} */ option) => + h('label', { class: 'q-option' }, h('input', { type, name, value: option.id }), h('span', null, h('span', null, option.label), option.description ? h('span', { class: 'muted small q-desc' }, option.description) : null)), + ), + h('label', { class: 'q-option other' }, h('input', { type, name, value: '__other__' }), h('span', null, t('questionOther'))), + otherInput, + ), + ); + otherInput.addEventListener('input', () => { + const other = /** @type {HTMLInputElement | null} */ (form.querySelector(`input[name="${name}"][value="__other__"]`)); + if (other && otherInput.value.trim()) other.checked = true; + }); + }); + const agentApi = window.mcode?.miniapp?.agent; + form.append( + h( + 'div', + { class: 'card-actions' }, + agentApi?.open && state.project?.sessionId ? h('button', { type: 'button', class: 'btn ghost', onclick: () => openConversation() }, t('decideInChat')) : null, + h('span', { class: 'spacer' }), + h('button', { type: 'button', class: 'btn', onclick: () => void submitAnswers(steps, null) }, t('questionSkip')), + steps.length ? h('button', { type: 'submit', class: 'btn primary' }, t('questionSubmit')) : null, + ), + ); + form.addEventListener('submit', (event) => { + event.preventDefault(); + void submitAnswers(steps, form); + }); + els.question.append(form); +} + +/** @param {any[]} steps @param {HTMLFormElement | null} form */ +async function submitAnswers(steps, form) { + const answers = steps.map((step, index) => { + if (!form) return { stepId: step.id, selectedOptionIds: [], selectedOther: false, skipped: true }; + const name = `step-${index}`; + const checked = [...form.querySelectorAll(`input[name="${name}"]:checked`)].map((input) => /** @type {HTMLInputElement} */ (input).value); + const otherText = /** @type {HTMLInputElement | null} */ (form.querySelector(`input[data-other-for="${name}"]`))?.value.trim() ?? ''; + const selectedOther = checked.includes('__other__') && !!otherText; + const selectedOptionIds = checked.filter((value) => value !== '__other__'); + if (selectedOptionIds.length === 0 && !selectedOther) return { stepId: step.id, selectedOptionIds: [], selectedOther: false, skipped: true }; + return { stepId: step.id, selectedOptionIds, selectedOther, ...(selectedOther ? { otherText } : {}) }; + }); + if (answers.length === 0) return; + try { + await api('POST', `/api/projects/${state.currentId}/answer`, { answers, model: modelSelection() }); + await loadProject(); + } catch (error) { + toast(/** @type {Error} */ (error).message, 'error'); + } +} + +// --------------------------------------------------------------------------------------------- +// Composer + +function buildComposer() { + const textarea = /** @type {HTMLTextAreaElement} */ (h('textarea', { class: 'composer-input', rows: '3', 'aria-label': t('placeholderNew'), maxlength: '20000' })); + const modelSelect = /** @type {HTMLSelectElement} */ (h('select', { class: 'select', 'aria-label': t('model') })); + const effortSelect = /** @type {HTMLSelectElement} */ (h('select', { class: 'select', 'aria-label': t('effort') })); + const sendButton = /** @type {HTMLButtonElement} */ (h('button', { type: 'submit', class: 'btn primary icon-only send', 'aria-label': t('send'), title: t('send') }, icon('send'))); + const stopButton = /** @type {HTMLButtonElement} */ (h('button', { type: 'button', class: 'btn danger icon-only', 'aria-label': t('stop'), title: t('stop'), hidden: true, onclick: stopRun }, icon('stop'))); + els.textarea = textarea; + els.modelSelect = modelSelect; + els.effortSelect = effortSelect; + els.sendButton = sendButton; + els.stopButton = stopButton; + + textarea.addEventListener('keydown', (event) => { + if (event.key === 'Enter' && !event.shiftKey && !event.isComposing && event.keyCode !== 229) { + event.preventDefault(); + form.requestSubmit(); + } + }); + textarea.addEventListener('input', () => { + textarea.style.height = 'auto'; + textarea.style.height = `${Math.min(textarea.scrollHeight, 220)}px`; + }); + modelSelect.addEventListener('change', () => { + state.modelKey = modelSelect.value; + state.effort = ''; + saveModelPreference(); + renderModelControls(); + }); + effortSelect.addEventListener('change', () => { + state.effort = effortSelect.value; + saveModelPreference(); + }); + + const form = /** @type {HTMLFormElement} */ ( + h( + 'form', + { class: 'composer' }, + textarea, + h('div', { class: 'composer-row' }, h('div', { class: 'model-controls' }, modelSelect, effortSelect), h('span', { class: 'spacer' }), stopButton, sendButton), + ) + ); + form.addEventListener('submit', (event) => { + event.preventDefault(); + void send(); + }); + return form; +} + +function saveModelPreference() { + localStorage.setItem(MODEL_KEY, JSON.stringify({ modelKey: state.modelKey, effort: state.effort })); +} + +function renderModelControls() { + const select = /** @type {HTMLSelectElement} */ (els.modelSelect); + const effort = /** @type {HTMLSelectElement} */ (els.effortSelect); + if (!state.models) { + select.replaceChildren(h('option', { value: '' }, state.modelsFailed ? t('modelsUnavailable') : '…')); + select.disabled = true; + effort.hidden = true; + return; + } + select.disabled = false; + const defaultName = modelName(state.models.default) || state.models.default?.modelId || ''; + select.replaceChildren( + h('option', { value: '' }, t('modelDefault', { name: defaultName })), + ...state.models.models.map((/** @type {any} */ option) => h('option', { value: keyOf(option) }, option.displayName || option.modelId)), + ); + if (state.modelKey && !currentModelOption()) state.modelKey = ''; + select.value = state.modelKey; + const option = currentModelOption(); + const efforts = option?.thinking?.efforts ?? []; + effort.hidden = efforts.length === 0; + effort.replaceChildren(h('option', { value: '' }, t('effortAuto')), ...efforts.map((/** @type {string} */ value) => h('option', { value }, value))); + if (!efforts.includes(state.effort)) state.effort = ''; + effort.value = state.effort; +} + +function renderComposer() { + const busy = !!state.live || state.sending; + const textarea = /** @type {HTMLTextAreaElement} */ (els.textarea); + textarea.placeholder = state.project ? t('placeholderFollow') : t('placeholderNew'); + textarea.setAttribute('aria-label', textarea.placeholder); + /** @type {HTMLButtonElement} */ (els.sendButton).hidden = !!state.live; + /** @type {HTMLButtonElement} */ (els.sendButton).disabled = busy || !state.agentAvailable; + /** @type {HTMLButtonElement} */ (els.stopButton).hidden = !state.live; +} + +/** @param {string} text */ +function fillComposer(text) { + const textarea = /** @type {HTMLTextAreaElement} */ (els.textarea); + textarea.value = text; + textarea.dispatchEvent(new Event('input')); + textarea.focus(); +} + +async function send() { + const textarea = /** @type {HTMLTextAreaElement} */ (els.textarea); + const text = textarea.value.trim(); + if (!text || state.live || state.sending || !state.agentAvailable) return; + state.sending = true; + renderComposer(); + try { + if (!state.currentId) { + const style = state.newStyle ? ('preset' in state.newStyle ? { preset: state.newStyle.preset } : state.newStyle) : null; + const created = await api('POST', '/api/projects', { text, style, model: modelSelection() }); + textarea.value = ''; + textarea.dispatchEvent(new Event('input')); + state.newStyle = null; + await refreshState(); + await selectProject(created.id); + } else { + await api('POST', `/api/projects/${state.currentId}/messages`, { text, model: modelSelection() }); + textarea.value = ''; + textarea.dispatchEvent(new Event('input')); + await loadProject(); + } + } catch (error) { + toast(/** @type {Error} */ (error).message, 'error'); + } finally { + state.sending = false; + renderComposer(); + } +} + +async function stopRun() { + if (!state.currentId) return; + try { + await api('POST', `/api/projects/${state.currentId}/cancel`, {}); + } catch (error) { + toast(/** @type {Error} */ (error).message, 'error'); + } +} + +// --------------------------------------------------------------------------------------------- +// Preview + +function buildPreview() { + const frame = /** @type {HTMLIFrameElement} */ (h('iframe', { class: 'preview-frame', sandbox: 'allow-scripts', referrerpolicy: 'no-referrer', title: t('previewTab') })); + const overlay = h('div', { class: 'preview-overlay', role: 'status' }); + const empty = h('div', { class: 'preview-empty' }, logo(), h('p', { class: 'muted' }, t('previewEmpty'))); + const versionSelect = /** @type {HTMLSelectElement} */ (h('select', { class: 'select', 'aria-label': t('versionOption', { n: '' }).trim() })); + versionSelect.addEventListener('change', () => { + const value = Number(versionSelect.value); + showVersion(value === latestVersion() ? null : value); + }); + const deviceGroup = h( + 'div', + { class: 'segmented', role: 'group', 'aria-label': t('device') }, + ...(/** @type {const} */ (['desktop', 'tablet', 'phone'])).map((device) => + h('button', { type: 'button', class: 'seg', 'aria-label': t(device), title: t(device), dataset: { device }, onclick: () => setDevice(device) }, icon(device)), + ), + ); + const windowHandle = window.mcode?.miniapp?.window; + const popButton = windowHandle + ? h( + 'button', + { + type: 'button', + class: 'btn icon-only ghost', + 'aria-label': t('popOut'), + title: t('popOut'), + onclick: () => void windowHandle.update({ mode: 'window' }).catch(() => {}), + }, + icon('window'), + ) + : null; + els.popButton = popButton ?? h('span'); + els.versionSelect = versionSelect; + els.deviceGroup = deviceGroup; + els.frame = frame; + els.overlay = overlay; + els.previewEmpty = empty; + els.previewActions = h( + 'div', + { class: 'preview-actions' }, + h('button', { type: 'button', class: 'btn icon-only ghost', 'aria-label': t('reload'), title: t('reload'), onclick: () => preview.reload() }, icon('reload')), + h('button', { type: 'button', class: 'btn icon-only ghost', 'aria-label': t('copy'), title: t('copy'), onclick: copyHtml }, icon('copy')), + h('button', { type: 'button', class: 'btn icon-only ghost', 'aria-label': t('download'), title: t('download'), onclick: downloadHtml }, icon('download')), + popButton, + ); + els.stage = h('div', { class: 'stage' }, h('div', { class: 'device-frame' }, frame, overlay)); + return h( + 'section', + { class: 'preview', id: 'preview-panel', role: 'tabpanel', 'aria-labelledby': 'tab-preview' }, + h('header', { class: 'preview-head' }, deviceGroup, versionSelect, h('span', { class: 'spacer' }), els.previewActions), + h('div', { class: 'preview-body' }, els.stage, empty), + ); +} + +/** @param {string} device */ +function setDevice(device) { + state.device = ['desktop', 'tablet', 'phone'].includes(device) ? device : 'desktop'; + localStorage.setItem(DEVICE_KEY, state.device); + renderPreviewChrome(); +} + +function renderPreviewChrome() { + const project = state.project; + const latest = latestVersion(); + const shown = state.viewing ?? latest; + els.preview.dataset.device = state.device; + for (const button of els.deviceGroup.querySelectorAll('button')) { + button.setAttribute('aria-pressed', String(/** @type {HTMLElement} */ (button).dataset.device === state.device)); + } + const hasVersion = !!project && latest > 0; + els.previewEmpty.hidden = hasVersion; + els.stage.hidden = !hasVersion; + const select = /** @type {HTMLSelectElement} */ (els.versionSelect); + select.hidden = !hasVersion; + els.previewActions.hidden = !hasVersion; + if (hasVersion) { + const versions = [...project.versions].reverse(); + select.replaceChildren(...versions.map((/** @type {any} */ v) => h('option', { value: String(v.n) }, v.n === latest ? t('latest', { n: v.n }) : t('versionOption', { n: v.n })))); + select.value = String(shown); + } +} + +/** @param {number | null} n */ +function showVersion(n) { + state.viewing = n; + renderPreviewChrome(); + renderThread(); + preview.sync(); + if (root.dataset.tab === 'chat' && window.matchMedia('(max-width: 759px)').matches) setTab('preview'); +} + +const preview = (() => { + let loadedKey = ''; + let token = ''; + let html = ''; + let request = 0; + let readySignals = 0; + let consumedSignals = 0; + let restores = 0; + let watching = false; + + window.addEventListener('message', (event) => { + const frame = /** @type {HTMLIFrameElement} */ (els.frame); + if (!frame || event.source !== frame.contentWindow || !event.data || event.data.token !== token) return; + if (event.data.type === 'ready') readySignals += 1; + if (event.data.type === 'painted' || event.data.type === 'ready') els.overlay.hidden = true; + // The design cannot open popups; a clicked https link is opened here, during the click. + if (event.data.type === 'open' && typeof event.data.url === 'string') openExternal(event.data.url); + }); + + // Every document the design loads reports "ready" with this preview's token. A load that + // does not is the design navigating itself away, so the design is put back (as fx0 does). + // Timing alone is not used: a hidden panel throttles the design's timers. + function watchLoads() { + if (watching) return; + watching = true; + /** @type {HTMLIFrameElement} */ (els.frame).addEventListener('load', () => { + setTimeout(() => { + if (!html) return; + if (readySignals > consumedSignals) { + consumedSignals = readySignals; + restores = 0; + return; + } + // A design that is only slow to load must not reload forever. + if (restores >= 2) return; + restores += 1; + restore(); + }, 600); + }); + } + + function restore() { + /** @type {HTMLIFrameElement} */ (els.frame).srcdoc = html; + } + + async function sync() { + const project = state.project; + const n = state.viewing ?? latestVersion(); + if (!project || n === 0) { + loadedKey = ''; + html = ''; + return; + } + const key = `${project.id}:${n}`; + if (key === loadedKey) return; + loadedKey = key; + const mine = ++request; + els.overlay.hidden = false; + els.overlay.replaceChildren(h('span', { class: 'spinner', 'aria-hidden': 'true' }), t('previewLoading')); + try { + const data = await api('GET', `/api/projects/${project.id}/preview/${n}`); + if (mine !== request) return; + watchLoads(); + token = data.token; + html = data.html; + consumedSignals = readySignals; + restores = 0; + /** @type {HTMLIFrameElement} */ (els.frame).srcdoc = html; + // A document that never reports (scripts blocked by an error) still shows after a moment. + setTimeout(() => { + if (mine === request) els.overlay.hidden = true; + }, 1500); + } catch (error) { + if (mine !== request) return; + loadedKey = ''; + els.overlay.replaceChildren( + h('span', null, t('previewFailed')), + h('button', { type: 'button', class: 'btn small', onclick: () => void sync() }, t('retry')), + ); + } + } + + return { + sync, + reload() { + loadedKey = ''; + void sync(); + }, + ensureShown() { + void sync(); + }, + reset() { + loadedKey = ''; + html = ''; + token = ''; + /** @type {HTMLIFrameElement} */ (els.frame).removeAttribute('srcdoc'); + }, + }; +})(); + +async function currentHtml() { + const project = state.project; + const n = state.viewing ?? latestVersion(); + if (!project || n === 0) throw new Error('no version'); + const text = await api('GET', `/api/projects/${project.id}/versions/${n}`); + return { text: String(text), n, project }; +} + +async function copyHtml() { + try { + const { text } = await currentHtml(); + await navigator.clipboard.writeText(text); + toast(t('copied')); + } catch { + toast(t('copyFailed'), 'error'); + } +} + +/** @param {string} title */ +function fileSlug(title) { + const slug = title + .normalize('NFKD') + .replace(/[^\w\s-]/g, '') + .trim() + .toLowerCase() + .replace(/[\s_]+/g, '-') + .slice(0, 48); + return slug || 'page'; +} + +async function downloadHtml() { + try { + const { text, n, project } = await currentHtml(); + const file = `${fileSlug(project.title)}-v${n}.html`; + const url = URL.createObjectURL(new Blob([text], { type: 'text/html;charset=utf-8' })); + const link = h('a', { href: url, download: file, hidden: true }); + document.body.append(link); + link.click(); + link.remove(); + setTimeout(() => URL.revokeObjectURL(url), 10_000); + toast(t('downloaded', { file })); + } catch { + toast(t('errUNKNOWN'), 'error'); + } +} + +// --------------------------------------------------------------------------------------------- +// Data flow + +async function refreshState() { + const data = await api('GET', '/api/state'); + state.agentAvailable = data.agentAvailable; + state.projects = data.projects; + state.styles = data.styles; + renderProjects(); +} + +async function loadModels() { + if (!state.agentAvailable) return; + try { + state.models = await api('GET', '/api/models'); + state.modelsFailed = false; + } catch { + state.modelsFailed = true; + } + renderModelControls(); + renderLive(); +} + +async function loadProject() { + const id = state.currentId; + if (!id) return; + try { + const project = await api('GET', `/api/projects/${id}`); + if (state.currentId !== id) return; + const grew = (project.versions.at(-1)?.n ?? 0) !== latestVersion(); + state.project = project; + state.live = project.live; + if (state.viewing !== null && !project.versions.some((/** @type {any} */ v) => v.n === state.viewing)) state.viewing = null; + renderAll(); + if (grew && state.viewing === null) preview.sync(); + } catch (error) { + if (/** @type {ApiError} */ (error).code === 'not_found') { + await refreshState().catch(() => {}); + selectProject(null); + } + } +} + +/** @param {string | null} id */ +async function selectProject(id) { + if (state.currentId !== id) { + state.viewing = null; + preview.reset(); + } + state.currentId = id; + state.project = null; + state.live = null; + state.scrollToEnd = true; + history.replaceState(null, '', id ? `${location.pathname}?p=${id}` : location.pathname); + toggleSidebar(false); + renderAll(); + if (id) { + await loadProject(); + const saved = state.project?.model; + if (saved && state.models) { + const option = state.models.models.find((/** @type {any} */ item) => keyOf(item) === keyOf(saved)); + if (option) { + state.modelKey = keyOf(saved); + state.effort = saved.thinking?.effort ?? ''; + renderModelControls(); + } + } + } else { + setTab('chat'); + /** @type {HTMLTextAreaElement} */ (els.textarea).focus(); + } +} + +function renderAll() { + renderProjects(); + renderHeader(); + renderThread(); + renderLive(); + renderQuestion(); + renderComposer(); + renderPreviewChrome(); +} + +let projectTimer = 0; +let listTimer = 0; +function connectEvents() { + const source = new EventSource('/api/events'); + let dropped = false; + source.addEventListener('message', (event) => { + let data; + try { + data = JSON.parse(event.data); + } catch { + return; + } + if (data.type === 'live' && data.id === state.currentId) { + state.live = data.live; + renderLive(); + renderComposer(); + renderHeader(); + } else if (data.type === 'project' && data.id === state.currentId) { + clearTimeout(projectTimer); + projectTimer = window.setTimeout(() => void loadProject(), 80); + } else if (data.type === 'projects') { + clearTimeout(listTimer); + listTimer = window.setTimeout(() => void refreshState().catch(() => {}), 120); + } + }); + source.addEventListener('error', () => { + dropped = true; + }); + source.addEventListener('open', () => { + if (!dropped) return; + dropped = false; + void refreshState().catch(() => {}); + void loadProject(); + }); +} + +function watchTheme() { + const query = window.matchMedia('(prefers-color-scheme: dark)'); + const apply = () => { + document.documentElement.dataset.theme = query.matches ? 'dark' : 'light'; + }; + apply(); + query.addEventListener('change', apply); +} + +async function boot() { + watchTheme(); + buildShell(); + setDevice(state.device); + renderModelControls(); + try { + await refreshState(); + } catch (error) { + els.banner.hidden = false; + els.banner.textContent = /** @type {Error} */ (error).message; + } + connectEvents(); + void loadModels(); + // A page link is /studio?p=; MiniMax Code can open it directly. + const linked = new URLSearchParams(location.search).get('p') ?? ''; + await selectProject(/^p-[0-9a-f]{8}$/.test(linked) && state.projects.some((project) => project.id === linked) ? linked : null); + document.addEventListener('keydown', (event) => { + if (event.key === 'Escape' && root.classList.contains('sidebar-open')) toggleSidebar(false); + }); +} + +void boot(); diff --git a/plugins/yujiachen-y/page-designer/miniapp/client/client-api.ts b/plugins/yujiachen-y/page-designer/miniapp/client/client-api.ts new file mode 100644 index 00000000..2ce7ab69 --- /dev/null +++ b/plugins/yujiachen-y/page-designer/miniapp/client/client-api.ts @@ -0,0 +1,387 @@ +/** + * Copyable Mini App Client declarations for TypeScript and JavaScript/JSDoc authoring. + * No Host implementation or runtime import: Electron preload provides window.mcode. + * Keep the .ts filename because Electron dependency packaging filters out .d.ts assets. + * Node start(context) uses miniapp-api.ts instead. See docs/client.md for usage and errors. + */ +export interface MiniAppMessageAPI { + /** + * Ask the user to append plain text to this Mini App's conversation input. + * Accepts at most 100,000 UTF-16 code units and preserves spaces, tabs, and newlines. + * An empty string still requires confirmation and leaves the draft unchanged. + * Resolves after the confirmed append; rejects on cancellation or failure. + * Does not send a message or insert, edit, or delete sent chat history. + */ + append(text: string): Promise; +} + +export type MiniAppAgentQuestionMode = 'questionnaire' | 'feature-enable' | 'plan'; + +export interface MiniAppAgentQuestionOption { + readonly id: string; + readonly label: string; + readonly description?: string; +} + +export interface MiniAppAgentQuestionStep { + readonly id: string; + readonly header?: string; + readonly question: string; + readonly description?: string; + readonly selectionMode: 'single' | 'multiple'; + readonly options: readonly MiniAppAgentQuestionOption[]; + /** Free-text "other" is always accepted; see `MiniAppAgentQuestionAnswer.otherText`. */ + readonly allowOther: true; +} + +/** Same questionnaire the Host chat renders; render every step and answer each one. */ +export interface MiniAppAgentQuestionnaire { + readonly schemaVersion: 2; + readonly id: string; + readonly title?: string; + readonly steps: readonly MiniAppAgentQuestionStep[]; + readonly mode?: MiniAppAgentQuestionMode; + /** Plan review Markdown and other mode-specific data. */ + readonly modePayload?: { readonly [key: string]: unknown }; + /** Unix ms; the request is withdrawn after this. */ + readonly expiresAt?: number; +} + +export interface MiniAppAgentQuestionAnswer { + readonly stepId: string; + readonly selectedOptionIds: readonly string[]; + readonly selectedOther: boolean; + readonly otherText?: string; + readonly skipped?: boolean; +} + +/** A model as the Host's composer selects it; take the values from `models()`. */ +export interface MiniAppAgentModelSelection { + readonly providerId: string; + readonly modelId: string; + /** `''` means thinking is off; it is preserved, distinct from an absent `variant`. */ + readonly variant?: string; + readonly thinking?: { readonly effort: string }; +} + +/** One model this user can use. */ +export interface MiniAppAgentModelOption { + readonly providerId: string; + readonly modelId: string; + readonly displayName: string; + readonly variants?: readonly string[]; + readonly thinking?: { readonly efforts: readonly string[]; readonly default?: string }; +} + +export interface MiniAppAgentModels { + readonly models: readonly MiniAppAgentModelOption[]; + /** What a session without a host chat model starts with. */ + readonly default: MiniAppAgentModelSelection; + /** The host chat's current model (the default when the chat has none); only when opened from a chat. */ + readonly host?: MiniAppAgentModelSelection; +} + +export interface MiniAppAgentAgentOption { + /** Canonical name; what `agent` takes. */ + readonly name: string; + readonly displayName: string; + /** The default agent. */ + readonly primary: boolean; +} + +export interface MiniAppAgentAgents { + readonly agents: readonly MiniAppAgentAgentOption[]; +} + +/** Final outcome of one Agent turn. */ +export interface MiniAppAgentRunResult { + /** Pass back in `run({ sessionId })` to continue the same conversation. */ + readonly sessionId: string; + /** Absent when no turn ran: an `answer` accepted without starting a continuation. */ + readonly turnId?: string; + /** The model the turn was dispatched with; absent when no turn ran. */ + readonly model?: MiniAppAgentModelSelection; + /** `aborted` only when the Host confirmed the abort; an unconfirmed cancel rejects with `result_unknown`. */ + readonly status: 'finished' | 'error' | 'aborted'; + /** Final assistant reply; absent when the turn produced none or did not finish. */ + readonly text?: string; + readonly error?: { readonly code: string; readonly message: string }; + /** Set when the turn ended waiting on a questionnaire; render `request` and continue with `agent.answer`. */ + readonly pendingQuestion?: { + readonly requestId: string; + readonly mode: MiniAppAgentQuestionMode; + readonly request: MiniAppAgentQuestionnaire; + }; +} + +/** + * Progress of one run. `turnId` matches the result's, except that a `permission.requested` raised + * by a delegated child session carries that child's own `sessionId` and `turnId`; `reply` with + * those, and do not filter events by the root turn. Ignore unknown `type` values: new event + * types may be added without notice. + * - `run.queued`: all run slots are taken and the call waits at 1-based `position`; sent once, + * before the turn starts, so it has no `turnId` and names a session only when continuing one. + * - `turn.started`: the Agent began the turn; `model` is the model it was dispatched with. + * - `text.delta`: a piece of the reply as it streams. + * - `message.committed`: one complete assistant message (a turn can have several). + * - `tool.started` / `tool.finished`: a tool call by name; `ok` is false when it failed. + * - `permission.requested`: a tool call waits for approval; decide it with `agent.reply`. + * - `question.requested`: the turn paused on a questionnaire; continue with `agent.answer`. + * - `turn.finished`: always last; same `status`, `text`, and `error` as the run result. + * Every event except `run.queued` names its `sessionId`, so `reply` works before the `run` that + * created the session has returned. `answer` does not: call it after the paused `run` resolves, + * or it rejects as busy. + */ +export type MiniAppAgentRunEvent = + | { readonly type: 'run.queued'; readonly sessionId?: string; readonly position: number } + | { + readonly type: 'turn.started'; + readonly sessionId: string; + readonly turnId: string; + readonly model: MiniAppAgentModelSelection; + } + | { + readonly type: 'text.delta'; + readonly sessionId: string; + readonly turnId: string; + readonly text: string; + } + | { + readonly type: 'message.committed'; + readonly sessionId: string; + readonly turnId: string; + readonly text: string; + } + | { + readonly type: 'tool.started'; + readonly sessionId: string; + readonly turnId: string; + readonly name: string; + } + | { + readonly type: 'tool.finished'; + readonly sessionId: string; + readonly turnId: string; + readonly name: string; + readonly ok: boolean; + } + | { + readonly type: 'permission.requested'; + readonly sessionId: string; + /** Absent when the runtime did not attribute the ask to a turn. */ + readonly turnId?: string; + readonly requestId: string; + readonly toolName: string; + readonly toolInput?: string; + readonly reason: string; + } + | { + readonly type: 'question.requested'; + readonly sessionId: string; + readonly turnId?: string; + readonly requestId: string; + readonly mode: MiniAppAgentQuestionMode; + readonly request: MiniAppAgentQuestionnaire; + } + | { + readonly type: 'turn.finished'; + readonly sessionId: string; + readonly turnId: string; + readonly status: 'finished' | 'error' | 'aborted'; + readonly text?: string; + readonly error?: { readonly code: string; readonly message: string }; + }; + +export interface MiniAppAgentRunInput { + /** Plain text, at most 100,000 UTF-16 code units. */ + readonly text: string; + /** Continue a session this Mini App created earlier; omit to start a new one. */ + readonly sessionId?: string; + /** + * Switch the session to this model before the turn (validated like the composer validates a new + * session's model: it must be one `models()` lists; unusable is `invalid_request`, missing BYOK + * credentials end the turn with `status: 'error'`); the session keeps it. Omit to use the session's current model. + */ + readonly model?: MiniAppAgentModelSelection; + /** + * Only when starting a session (no `sessionId`): an agent `name` from `agents()`. A session + * never changes its agent, so passing it with `sessionId` is `invalid_request`. + */ + readonly agent?: string; + /** Your id for `cancel(requestId)`; 1-128 characters, unique among this page's active runs. */ + readonly requestId?: string; +} + +export interface MiniAppAgentReplyInput { + readonly sessionId: string; + /** `requestId` of the `permission.requested` event. */ + readonly requestId: string; + /** No `allowAlways`: a Mini App never writes the user's global rules. */ + readonly decision: 'allowOnce' | 'deny'; +} + +export interface MiniAppAgentAnswerInput { + readonly sessionId: string; + /** `requestId` of the `question.requested` event or of `result.pendingQuestion`. */ + readonly questionId: string; + /** One answer per questionnaire step. */ + readonly answers: readonly MiniAppAgentQuestionAnswer[]; + /** Same as `MiniAppAgentRunInput.model`, applied before the continuation turn. */ + readonly model?: MiniAppAgentModelSelection; + /** Your id for `cancel(requestId)`, as in `run`. */ + readonly requestId?: string; +} + +export interface MiniAppAgentRunOptions { + readonly onEvent?: (event: MiniAppAgentRunEvent) => void; +} + +/** + * Absent on older Hosts. Calls reject with `not_permitted` unless the manifest declares + * `agentAccess: ["session.run"]`. A new session uses the Agent of the chat this page is open in; + * a page without a host chat uses the primary Agent unless `run({ agent })` / `create({ agent })` + * names one from `agents()`. Continuing a session keeps its Agent. + * The Agent works in the host chat's project directory when it has one, otherwise in this Mini + * App's own Agent directory; a continued session keeps its directory. A new session takes the + * model and thinking level of the host chat (the runtime default when the chat has none) and keeps + * them; `run` / `answer` with `model` switch the session's model first. + * It runs one turn with that Agent's full capabilities (files, shell, Skills, MCP including this + * Mini App's own), and resolves when it ends. + * + * Interaction is yours to render: handle `permission.requested` with `reply` and + * `question.requested` with `answer`. A permission request waits only while this run passed + * `onEvent` or the session is open in a chat side panel through `open`; otherwise it is denied + * and the model is told why. A run with `onEvent` that does not handle the request waits until + * `cancel` or the page closes; there is no timeout. Up to `agentConcurrency` runs (default 4) per + * Mini App run at once, one per session; further `run` / `answer` calls wait in order (up to four + * times that many, see `run.queued`), and `cancel` on a waiting call rejects it with `cancelled`. + * `busy` means the session already has a run in progress or the wait queue is full. + * Failures reject with an `Error` whose `message` is `invalid_request`, `not_permitted`, `busy`, + * `session_not_found`, `runtime_unavailable`, `cancelled`, `result_unknown`, or + * `request_not_found`. `runtime_unavailable` means nothing was started; `result_unknown` means + * the turn may have run (or ended without the Host knowing whether a question is pending), so + * never repeat it automatically. + */ +export interface MiniAppAgentAPI { + run( + input: MiniAppAgentRunInput, + options?: MiniAppAgentRunOptions, + ): Promise; + /** Decide a pending permission request; `request_not_found` when it is unknown or settled. */ + reply(input: MiniAppAgentReplyInput): Promise; + /** + * Answer a questionnaire; the continuation turn is observed like `run`. Rejects with + * `result_unknown` when the answer was recorded but the continuation turn was not observed. + */ + answer( + input: MiniAppAgentAnswerInput, + options?: MiniAppAgentRunOptions, + ): Promise; + /** Ask the Host to stop an active run or answer; unknown or finished ids are ignored. */ + cancel(requestId: string): void; + /** + * Show one of this Mini App's sessions as a tab in the side panel of the chat the + * Mini App is open in, where the user can read the conversation and answer + * permission and question cards themselves. Permission requests wait for the tab only + * while it stays open; once the user closes it, handle `permission.requested` yourself + * or call `open` again. Any session this Mini App owns can be opened, whichever chat + * created it; `session_not_found` otherwise. + */ + open(sessionId: string): Promise; + /** + * Returns the model catalog, runtime `default` and optional chat `host`. Pass `default` / `host` + * directly as `model` to `run` / `answer`. For a catalog entry, extract `providerId` / `modelId`, + * add a selected `variant` if needed and preserve `variant: ''`. If thinking is supported, + * select an effort from `thinking.efforts` and pass `thinking: { effort }`. + */ + models(): Promise; + /** The agents the user has configured; `agent` of `create` / a first `run` must be one of them. */ + agents(): Promise; + /** + * Create a session bound to this chat (agent, project directory, model) without running a + * turn, and return its `sessionId`; continue it with `run({ sessionId })` here or hand it to + * Node code. `agent` picks the session's agent from `agents()`; a session never changes it. + * Takes no run slot. If the Host's 60 s deadline passes it rejects with `runtime_unavailable` + * although the session may already exist. + */ + create(input?: { readonly agent?: string }): Promise<{ readonly sessionId: string }>; +} + +export interface MiniAppClientAPI { + readonly message: MiniAppMessageAPI; + readonly agent?: MiniAppAgentAPI; + /** + * Proposed: shape may still change. Feature-detect with + * `window.mcode?.miniapp?.window` before use. + */ + readonly window?: MiniAppWindowHandle; +} + +/** Live state of the popped-out window: current position, size, and flags — not the manifest declaration. */ +export interface MiniAppWindowOptions { + readonly width: number; + readonly height: number; + readonly minWidth: number; + readonly minHeight: number; + readonly x: number; + readonly y: number; + readonly visible: boolean; + readonly resizable: boolean; + readonly frame: boolean; + readonly transparent: boolean; + readonly alwaysOnTop: boolean; + readonly clickThrough: boolean; + readonly skipTaskbar: boolean; +} + +export type MiniAppWindowState = + | { readonly mode: 'inline' } + | { readonly mode: 'window'; readonly window: MiniAppWindowOptions }; + +/** Every field is optional; omitted fields are left unchanged. */ +export type MiniAppWindowPatch = Partial<{ + mode: 'inline' | 'window'; + width: number; + height: number; + x: number; + y: number; + visible: boolean; + resizable: boolean; + alwaysOnTop: boolean; + clickThrough: boolean; + skipTaskbar: boolean; +}>; + +export interface MiniAppWindowDisposable { + dispose(): void; +} + +/** + * Page-side handle for this Mini App's own window (proposed). Separate from + * `message`; the Host injects both under the same `mcode.miniapp` namespace. + */ +export interface MiniAppWindowHandle { + /** Opaque and stable across pop-out and return; never a Session or launch identifier. */ + readonly id: string; + getState(): Promise; + /** + * `update({ mode: ... })` or `focus()` require the call to originate from a + * user gesture (`navigator.userActivation.isActive`); otherwise they reject + * with `not_permitted` before any Host round trip. + */ + update(patch: MiniAppWindowPatch): Promise; + focus(): Promise; + /** Returns a disposable; call `dispose()` to stop receiving updates. */ + onDidChangeState(listener: (state: MiniAppWindowState) => void): MiniAppWindowDisposable; +} + +export interface McodeClientAPI { + readonly miniapp: MiniAppClientAPI; +} + +declare global { + interface Window { + /** Absent outside a supported managed Mini App main page. Check before use. */ + readonly mcode?: McodeClientAPI; + } +} diff --git a/plugins/yujiachen-y/page-designer/miniapp/client/index.html b/plugins/yujiachen-y/page-designer/miniapp/client/index.html new file mode 100644 index 00000000..6a1daf1b --- /dev/null +++ b/plugins/yujiachen-y/page-designer/miniapp/client/index.html @@ -0,0 +1,17 @@ + + + + + + + Page Designer + + + + +

+ + + diff --git a/plugins/yujiachen-y/page-designer/miniapp/miniapp.json b/plugins/yujiachen-y/page-designer/miniapp/miniapp.json new file mode 100644 index 00000000..f95b7b8f --- /dev/null +++ b/plugins/yujiachen-y/page-designer/miniapp/miniapp.json @@ -0,0 +1,24 @@ +{ + "schemaVersion": 1, + "artifacts": { + "client": ["./miniapp/client"], + "node": ["./miniapp/node"] + }, + "runtime": { + "kind": "process", + "entry": "./miniapp/node/server.mjs", + "lifecycle": "on-demand" + }, + "surface": { + "path": "/studio", + "window": { + "width": 1280, + "height": 820, + "minWidth": 720, + "minHeight": 480 + } + }, + "mcpEndpoints": [{ "server": "page-designer", "path": "/mcp/page-designer" }], + "agentAccess": ["session.run"], + "agentConcurrency": 4 +} diff --git a/plugins/yujiachen-y/page-designer/miniapp/node/artifact.mjs b/plugins/yujiachen-y/page-designer/miniapp/node/artifact.mjs new file mode 100644 index 00000000..7e546bcb --- /dev/null +++ b/plugins/yujiachen-y/page-designer/miniapp/node/artifact.mjs @@ -0,0 +1,291 @@ +// @ts-check +// The design artifact: one self-contained HTML document, its safety checks and the sandbox +// bootstrap the preview runs it with. +// +// Adapted from fx0 by Vercel Labs, lib/design-artifact.ts and the edit_design handler in +// lib/agent.ts (https://github.com/vercel-labs/fx0, commit e584782, Apache-2.0). +// Changes from the original: ported from TypeScript to JavaScript; the bootstrap reports to the +// Mini App page instead of fx0's wrapper route and sends no heartbeat timer; the +// exact-replacement edit logic moved here from lib/agent.ts so the MCP tools and the tests +// share it. + +export const MAX_DESIGN_BYTES = 512 * 1024; +export const MAX_EDITS = 20; + +/** Applied inside the preview document, ahead of everything the design contains. */ +export const DESIGN_EMBEDDED_CSP = [ + "default-src 'none'", + "script-src 'unsafe-inline'", + "style-src 'unsafe-inline'", + 'img-src data:', + 'font-src data:', + 'media-src data:', + "connect-src 'none'", + "child-src 'none'", + "frame-src 'none'", + "worker-src 'none'", + "object-src 'none'", + "manifest-src 'none'", + "base-uri 'none'", + "form-action 'none'", +].join('; '); + +/** + * WebRTC is switched off before the design runs (CSP does not cover it). With a token, the + * document also reports to the page when it has loaded and painted, and hands clicked https + * links to the page, because the sandbox grants no popups or top navigation. + * @param {string} [readyToken] + */ +function bootstrap(readyToken) { + const heartbeat = readyToken + ? `const token=${JSON.stringify(readyToken)};const send=type=>parent.postMessage({type,token},"*");addEventListener("load",()=>{send("ready");requestAnimationFrame(()=>requestAnimationFrame(()=>send("painted")))},{once:true});addEventListener("click",event=>{const link=event.target instanceof Element&&event.target.closest("a[href]");const url=link?link.getAttribute("href")||"":"";if(/^https:/i.test(url)){event.preventDefault();parent.postMessage({type:"open",token,url},"*")}},true);` + : ''; + return ``; +} + +/** @param {string} value */ +function escapeAttribute(value) { + return value.replaceAll('&', '&').replaceAll('"', '"').replaceAll('<', '<').replaceAll('>', '>'); +} + +/** + * Insert the CSP meta tag and the bootstrap right after the doctype, before any design content. + * @param {string} html a document that passed validateDesignHtml + * @param {string} [readyToken] + */ +export function prepareDesignHtml(html, readyToken) { + const csp = ``; + return html.replace(/^(\s*)/i, `$1${csp}${bootstrap(readyToken)}`); +} + +const PROHIBITED_TAGS = new Map([ + ['base', ' elements'], + ['form', 'forms'], + ['iframe', ''), /' }); + assert.equal(outside.isError, true); + assert.match(outside.value, /must not contain