{"id":"2c6ddeea-9a96-4f99-9fdb-8afa8de473b7","entityType":"agent","slug":"clawhub-zhang6714268-myapp-creator","name":"Myapp Creator","canonicalUrl":"https://www.xpersona.co/agent/clawhub-zhang6714268-myapp-creator","canonicalPath":"/agent/clawhub-zhang6714268-myapp-creator","generatedAt":"2026-10-10T06:44:31.695Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T02:03:40.289Z","emptyReason":null},"description":"Create or update standalone single-file HTML apps with specified features, saving them to fe-service without invoking LLM directly.","descriptionLabel":"Source description","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. 1.8K downloads reported by the source. Last updated 10/10/2026.","installCommand":"clawhub skill install s17ea5pqb53nd5kct35sadct2s86darh:myapp-creator","sourceUrl":"https://clawhub.ai/zhang6714268/myapp-creator","homepage":"https://clawhub.ai/zhang6714268/skills/myapp-creator","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/zhang6714268/myapp-creator","kind":"source"},{"label":"Homepage","url":"https://clawhub.ai/zhang6714268/skills/myapp-creator","kind":"homepage"}],"safetyScore":84,"overallRank":62,"popularityScore":65,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"Myapp Creator technical dossier on Xpersona with agent coverage, OPENCLEW support, and live trust metadata."},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-10-10T02:03:40.289Z","emptyReason":null},"protocols":[{"protocol":"OPENCLEW","label":"OpenClaw","status":"self-declared","notes":"Declared in the public agent profile."}],"capabilities":[],"verifiedCount":0,"selfDeclaredCount":1,"capabilityMatrix":{"rows":[{"key":"OPENCLEW","type":"protocol","support":"unknown","confidenceSource":"profile","notes":"Listed on profile"}],"flattenedTokens":"protocol:OPENCLEW|unknown|profile"}},"adoption":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T02:03:40.289Z","emptyReason":null},"stars":null,"forks":null,"downloads":1787,"packageName":null,"latestVersion":"1.0.31","tractionLabel":"1.8K downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T02:03:40.289Z","emptyReason":null},"lastUpdatedAt":"2026-10-10T02:03:40.289Z","lastCrawledAt":"2026-10-10T02:03:40.289Z","lastIndexedAt":null,"nextCrawlAt":"2026-10-11T02:03:40.289Z","lastVerifiedAt":null,"highlights":[{"version":"1.0.31","createdAt":"2026-05-28T12:47:47.524Z","changelog":"myapp-creator 1.0.31 - Updated version to 1.0.31 across all relevant metadata and documentation files. - Synchronized content and formatting in SKILL.md and related doc/config files. - No functional logic or code changes; this is a metadata and documentation version update.","fileCount":41,"zipByteSize":83484},{"version":"1.0.30","createdAt":"2026-05-28T11:55:57.610Z","changelog":"- Bumped version to 1.0.30. - Synced and updated documentation and metadata in SKILL.md, clawhub.yaml, and metadata.json across all myapp-creator subfolders. - No feature, logic, or behavior changes; this update focuses on consistency and version meta updates.","fileCount":41,"zipByteSize":86174},{"version":"1.0.29","createdAt":"2026-05-28T09:49:58.930Z","changelog":"- 调整版本号为 1.0.29 - 更新 SKILL.md、clawhub.yaml、metadata.json 及相关路径下的文档，版本信息同步到 1.0.29 - 其他内容、规则和功能保持不变","fileCount":41,"zipByteSize":86391},{"version":"1.0.28","createdAt":"2026-05-28T09:13:37.275Z","changelog":"- Refined游戏类应用右栏布局规则：如有预览区，右栏不再展示排行榜和操作说明，右栏内容分配逻辑更加明确。 - 优化布局模板说明，明确了游戏右栏的结构使用和切换条件。 - 删除重复或冗余骨架模板写法，精简 SKILL.md 文档说明。 - 移除 skill-card.md 文件，相关文档内容整合进主文档。","fileCount":41,"zipByteSize":84584},{"version":"1.0.27","createdAt":"2026-05-27T12:34:34.007Z","changelog":"myapp-creator 1.0.27 - 游戏类应用右栏骨架样式升级，状态区与方向键始终可见，排行榜与操作说明各自独立滚动，提升多内容展示体验。 - `#sidebar` 结构调整，去除旧版 `.sidebar-scroll` 容器，采用更清晰的 flex 列布局方案。 - 调整文档描述，细化横屏多设备适配建议，明确字体与响应式单位用法。 - 规范Leaderboard/Instructions滚动容器css写法，确保内容区自适应收缩。 - 补充部分样式应用解释，细化通用兼容性约束表述。","fileCount":41,"zipByteSize":84960},{"version":"1.0.26","createdAt":"2026-05-27T11:46:29.998Z","changelog":"- Improved: File structure refactored to avoid redundant nesting in the skills directory. - Added: New register.sh and update.sh scripts at the correct skills/myapp-creator/scripts location. - Fixed: clawhub.yaml and metadata.json relocated to eliminate path duplication. - Clean-up: Removed duplicated and nested myapp-creator directories for better maintainability.","fileCount":41,"zipByteSize":84462},{"version":"1.0.25","createdAt":"2026-05-27T09:41:26.949Z","changelog":"- 优化了游戏页面右栏布局：信息区（关卡/得分/预览/排行/说明）可独立滚动，方向键固定在底部，始终可见 - 不再强制右栏部分内容隐藏或收缩，提升了小屏适配性和信息可访问性 - CSS 模板及骨架注释同步更新，明确分离 `#sidebar-scroll`（信息区）与 `#controls`（操作区） - 增强相关文档说明，便于开发参考新版布局标准","fileCount":41,"zipByteSize":84328},{"version":"1.0.24","createdAt":"2026-05-27T09:00:33.642Z","changelog":"myapp-creator 1.0.24 - Added LICENSE file to the repository. - Updated metadata and configuration files (yaml/json) to reflect new version and settings. - Documentation (SKILL.md) updated to version 1.0.24. - Minor adjustments and maintenance on scripts and versioning files.","fileCount":41,"zipByteSize":83970}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install s17ea5pqb53nd5kct35sadct2s86darh:myapp-creator","setupComplexity":"low","setupSteps":["Install using `clawhub skill install s17ea5pqb53nd5kct35sadct2s86darh:myapp-creator` in an isolated environment before connecting it to live workloads.","No published capability contract is available yet, so validate auth and request/response behavior manually.","Review the upstream CLAWHUB listing at https://clawhub.ai/zhang6714268/myapp-creator before using production credentials."],"contract":{"contractStatus":"missing","authModes":[],"requires":[],"forbidden":[],"supportsMcp":false,"supportsA2a":false,"supportsStreaming":false,"inputSchemaRef":null,"outputSchemaRef":null,"dataRegion":null,"contractUpdatedAt":null,"sourceUpdatedAt":null,"freshnessSeconds":null},"invocationGuide":{"preferredApi":{"snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-zhang6714268-myapp-creator/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-zhang6714268-myapp-creator/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-zhang6714268-myapp-creator/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-zhang6714268-myapp-creator/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-zhang6714268-myapp-creator/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-zhang6714268-myapp-creator/trust\""],"jsonRequestTemplate":{"query":"summarize this repo","constraints":{"maxLatencyMs":2000,"protocolPreference":["OPENCLEW"]}},"jsonResponseTemplate":{"ok":true,"result":{"summary":"...","confidence":0.9},"meta":{"source":"CLAWHUB","generatedAt":"2026-10-10T06:44:31.692Z"}},"retryPolicy":{"maxAttempts":3,"backoffMs":[500,1500,3500],"retryableConditions":["HTTP_429","HTTP_503","NETWORK_TIMEOUT"]}},"endpoints":{"dossierUrl":"https://www.xpersona.co/api/v1/agents/clawhub-zhang6714268-myapp-creator/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-zhang6714268-myapp-creator/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-zhang6714268-myapp-creator/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-zhang6714268-myapp-creator/trust"}},"reliability":{"evidence":{"source":"runtime-metrics","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No trust, reliability, or runtime telemetry is available."},"trust":{"status":"unavailable","handshakeStatus":"UNKNOWN","verificationFreshnessHours":null,"reputationScore":null,"p95LatencyMs":null,"successRate30d":null,"fallbackRate":null,"attempts30d":null,"trustUpdatedAt":null,"trustConfidence":"unknown","sourceUpdatedAt":null,"freshnessSeconds":null},"decisionGuardrails":{"doNotUseIf":["Contract metadata is missing or unavailable for deterministic execution."],"safeUseWhen":[],"riskFlags":["missing_or_unavailable_contract","trust_data_unavailable","schema_references_missing"],"operationalConfidence":"low"},"executionMetrics":{"observedLatencyMsP50":null,"observedLatencyMsP95":null,"estimatedCostUsd":null,"uptime30d":null,"rateLimitRpm":null,"rateLimitBurst":null,"lastVerifiedAt":null,"verificationSource":null},"runtimeMetrics":{"successRate":null,"avgLatencyMs":null,"avgCostUsd":null,"hallucinationRate":null,"retryRate":null,"disputeRate":null,"p50Latency":null,"p95Latency":null,"lastUpdated":null}},"benchmarks":{"evidence":{"source":"no-benchmark-data","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No benchmark suites or observed failure patterns are available."},"suites":[],"failurePatterns":[]},"artifacts":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T02:03:40.289Z","emptyReason":null},"readme":"Skill: Myapp Creator\n\nOwner: zhang6714268\n\nSummary: Create or update standalone single-file HTML apps with specified features, saving them to fe-service without invoking LLM directly.\n\nTags: latest:1.0.31\n\nVersion history:\n\nv1.0.31 | 2026-05-28T12:47:47.524Z | auto\n\nmyapp-creator 1.0.31\n\n- Updated version to 1.0.31 across all relevant metadata and documentation files.\n- Synchronized content and formatting in SKILL.md and related doc/config files.\n- No functional logic or code changes; this is a metadata and documentation version update.\n\nv1.0.30 | 2026-05-28T11:55:57.610Z | auto\n\n- Bumped version to 1.0.30.\n- Synced and updated documentation and metadata in SKILL.md, clawhub.yaml, and metadata.json across all myapp-creator subfolders.\n- No feature, logic, or behavior changes; this update focuses on consistency and version meta updates.\n\nv1.0.29 | 2026-05-28T09:49:58.930Z | auto\n\n- 调整版本号为 1.0.29\n- 更新 SKILL.md、clawhub.yaml、metadata.json 及相关路径下的文档，版本信息同步到 1.0.29\n- 其他内容、规则和功能保持不变\n\nv1.0.28 | 2026-05-28T09:13:37.275Z | auto\n\n- Refined游戏类应用右栏布局规则：如有预览区，右栏不再展示排行榜和操作说明，右栏内容分配逻辑更加明确。\n- 优化布局模板说明，明确了游戏右栏的结构使用和切换条件。\n- 删除重复或冗余骨架模板写法，精简 SKILL.md 文档说明。\n- 移除 skill-card.md 文件，相关文档内容整合进主文档。\n\nv1.0.27 | 2026-05-27T12:34:34.007Z | auto\n\nmyapp-creator 1.0.27\n\n- 游戏类应用右栏骨架样式升级，状态区与方向键始终可见，排行榜与操作说明各自独立滚动，提升多内容展示体验。\n- `#sidebar` 结构调整，去除旧版 `.sidebar-scroll` 容器，采用更清晰的 flex 列布局方案。\n- 调整文档描述，细化横屏多设备适配建议，明确字体与响应式单位用法。\n- 规范Leaderboard/Instructions滚动容器css写法，确保内容区自适应收缩。\n- 补充部分样式应用解释，细化通用兼容性约束表述。\n\nv1.0.26 | 2026-05-27T11:46:29.998Z | auto\n\n- Improved: File structure refactored to avoid redundant nesting in the skills directory.\n- Added: New register.sh and update.sh scripts at the correct skills/myapp-creator/scripts location.\n- Fixed: clawhub.yaml and metadata.json relocated to eliminate path duplication.\n- Clean-up: Removed duplicated and nested myapp-creator directories for better maintainability.\n\nv1.0.25 | 2026-05-27T09:41:26.949Z | auto\n\n- 优化了游戏页面右栏布局：信息区（关卡/得分/预览/排行/说明）可独立滚动，方向键固定在底部，始终可见\n- 不再强制右栏部分内容隐藏或收缩，提升了小屏适配性和信息可访问性\n- CSS 模板及骨架注释同步更新，明确分离 `#sidebar-scroll`（信息区）与 `#controls`（操作区）\n- 增强相关文档说明，便于开发参考新版布局标准\n\nv1.0.24 | 2026-05-27T09:00:33.642Z | auto\n\nmyapp-creator 1.0.24\n\n- Added LICENSE file to the repository.\n- Updated metadata and configuration files (yaml/json) to reflect new version and settings.\n- Documentation (SKILL.md) updated to version 1.0.24.\n- Minor adjustments and maintenance on scripts and versioning files.\n\nv1.0.23 | 2026-05-27T08:00:00.847Z | auto\n\nVersion 1.0.23\n\n- Updated SKILL.md, metadata, and config files to increment version to 1.0.23.\n- Applied synchronization across core and skills/myapp-creator subfolders to ensure consistent versioning and metadata.\n- No changes to functionality or end-user features.\n\nv1.0.22 | 2026-05-26T08:30:29.240Z | auto\n\nmyapp-creator 1.0.22\n\n- 更新 SKILL.md、metadata.json、clawhub.yaml 及多级目录，保持配置与文档一致性\n- 文档中版本号升级至 1.0.22\n- 统一与主入口、子目录下说明文件的内容，修正历史路径和内容堆叠问题\n- 未变更功能逻辑，仅为配置、描述和版本信息整理补充\n\nv1.0.21 | 2026-05-26T07:25:36.171Z | auto\n\nmyapp-creator 1.0.21\n\n- 更新文档，优化游戏应用横屏布局的右栏 `#leaderboard` 和 `#instructions` 的 `flex` 属性说明，使其更明确支持空间不足时自动收缩到隐藏（`flex:1 1 0`）。\n- 修正文档描述细节，保持与实现一致。  \n- 其余逻辑和接口保持兼容。\n\nv1.0.20 | 2026-05-26T06:56:00.722Z | auto\n\n- 更新游戏类页面布局规范：右栏结构从“排行榜/操作说明/方向键”调整为“状态/预览/排行榜/说明/控制”，并明确展示优先级，空间不足时非核心区可自动隐藏。\n- 补充右栏每一部分的优先级/压缩规则和布局说明，提升多设备自适应表现。\n- 明确布局默认要求，并优化示例代码，使描述和约束更准确。\n- 移除 skill-card.md 文件，简化冗余内容。\n\nv1.0.19 | 2026-05-26T03:46:46.232Z | auto\n\n- 修订了游戏类应用右侧控制区的按钮符号（由“◀ ⏸ ▶”调整为“◀ II ▶”）以对齐统一规范\n- 版本号从 1.0.18 升级至 1.0.19\n- 同步更新各层级描述文档与元数据，确保多处 SKILL.md 与 version.txt 文件内容一致\n- 未添加新功能，聚焦于文档和规范细节优化\n\nv1.0.18 | 2026-05-25T12:57:01.875Z | auto\n\n**Version 1.0.18**\n\n- 明确禁止在 Canvas 渲染中使用 ctx.roundRect()，并提供兼容 Chrome 85 的圆角矩形绘制 helper (`rr(ctx, x, y, w, h, r)`)。\n- 补充说明禁止在 Canvas 内使用 emoji 字符，要求用形状或文字代替。\n- SKILL.md 多处细则补充与强化，特别针对 Android 8.1 WebView（Chrome/85）兼容性问题进一步说明。\n\nv1.0.17 | 2026-05-25T11:33:07.523Z | auto\n\nmyapp-creator 1.0.17 更新日志\n\n- 多个元数据文件（metadata.json、clawhub.yaml、version.txt 等）更新至 1.0.17 版本。\n- 文档（SKILL.md）内容有细节修订与版本号同步至 1.0.17。\n- 涉及 skill 目录下的多处文档与配置文件结构同步。\n- 代码逻辑未显示变动，主要为版本升级与文档维护。\n\nv1.0.16 | 2026-05-25T09:40:54.679Z | auto\n\n- 增加了对 CSS `inset` 属性的兼容性要求：禁止在样式中使用 `inset`，需写明 `top:0; left:0; right:0; bottom:0`（Chrome 85 不支持 `inset` 简写）。\n- 提醒：`box-shadow` 的 `inset` 关键字不受此限制，可正常使用。\n- 示例代码及模板已相应替换，确保遮罩层和弹窗兼容性更好。\n- 版本号升级至 1.0.16。\n\nv1.0.15 | 2026-05-25T09:11:34.845Z | auto\n\n- 修正游戏类应用遮罩层布局文档，明确强制用 flexbox 水平/垂直居中，禁止使用固定 top/left 像素定位，避免内容贴左上角。\n- 增加遮罩层实现详细注释，覆盖“开始/结束/暂停”状态，优化开发者对布局细节的理解。\n- 其余规范、兼容性要求、触发规则保持不变。\n\nv1.0.14 | 2026-05-25T08:23:18.565Z | auto\n\nmyapp-creator v1.0.14\n\n- 优化了游戏类应用的 Canvas 初始化模板，解决 Android 8.1 WebView 下初次加载时尺寸为 0 的问题，需使用 window.load + 双层 requestAnimationFrame 获取尺寸和 offsetWidth 兜底。\n- 明确 Canvas 必须使用 position:absolute 撑满，不依赖 flex 布局。\n- 更新了游戏方向键 UI 约束：暂停键强制放到 3×3 grid 的正中心格，不允许单独分离，更强调炫酷视觉与按压反馈。\n- 其他文档描述同步、模板示例同步为最新推荐实践。\n\nv1.0.13 | 2026-05-22T13:10:32.336Z | auto\n\nmyapp-creator 1.0.13\n\n- 游戏类应用横版布局模板优化：canvas 左栏区域不留空白，宽高自适应；右栏宽度及样式调整，细化样式参数说明。\n- 增加 canvas 像素与展示尺寸一致的 JS 初始化代码示例，并说明格子尺寸自适应做法。\n- 右栏操作说明、排行榜、方向按钮区更详细的样式建议，便于页面更美观和兼容横屏多设备。\n\nv1.0.12 | 2026-05-22T12:43:05.794Z | auto\n\n- 游戏类应用页面强制采用横版左右分栏布局，左侧为游戏区、右侧为操作区（含排行榜/说明/控制按钮），禁止竖版堆叠。\n- 所有方向控制游戏（如贪吃蛇等）必须将方向键和暂停键放在右侧操作区，不能单独浮动在游戏区。\n- 新增右栏操作按钮布局与样式的详细模板，规范方向键与暂停按钮的布局方式。\n- 其余基本规范不变（安卓 8.1 WebView 兼容、交互/触屏要求、模板分类型选择等）。\n\nv1.0.11 | 2026-05-22T12:11:03.811Z | auto\n\n- 强化游戏类应用适配要求，强制方向控制类游戏页面必须渲染四个方向触摸按钮（↑↓←→），不可用滑动或键盘操作替代\n- SKILL.md 添加了方向按钮布局标准模板与JS调用示例，便于直接复用\n- 补充说明滑动手势仅可作为辅助交互，方向控制按钮为唯一可靠入口\n- 其他细节优化与适配规范澄清\n\nv1.0.10 | 2026-05-22T11:47:22.339Z | auto\n\nmyapp-creator 1.0.10\n\n- 新增适配说明：明确目标运行环境为 Android 8.1 WebView（Chrome 85），新增 Web API/CSS/触摸交互等兼容性约束与开发建议。\n- 补充游戏类适配要点：要求必须提供屏幕虚拟按键和手势支持，详细列出触摸事件、防误触、按钮区域等要求。\n- 明确禁止使用不兼容的 JS/CSS 特性（如 aspect-ratio、structuredClone、BigInt、ResizeObserver 等）。\n- 增强文档和表格等模板场景下的兼容与体验描述。\n- 文档结构优化，提升条理性与实操可读性。\n\nv1.0.9 | 2026-05-20T07:14:17.956Z | auto\n\nmyapp-creator 1.0.9\n\n- 明确增加最高优先级规则，强调棋类/游戏类绝对禁止AI对手、人机模式（只允许双人轮流操作），和导出/打印/保存相关功能代码禁止。\n- 创建流程中增加了“HTML 自检”环节，详细列出必须逐项检查（如代码不得包含AI相关函数、任何保存/导出/打印功能），严格要求修正后再提交。\n- 补充棋类游戏“正确实现方式”示例，强调只允许双人交替操作。\n- 业务流程描述更为严密，便于规范开发者和维护者的实现行为。\n- 文档结构优化，更便于检索核心硬性要求。\n\nv1.0.8 | 2026-05-20T07:00:00.855Z | user\n\n强化禁止AI对手/人机对战约束，棋类游戏只允许双人同屏轮流对战；硬约束区顶部重复禁止项\n\nv1.0.7 | 2026-05-20T06:34:45.684Z | user\n\n禁止游戏类人机对战功能；禁止所有类型的导出/打印/保存按钮\n\nv1.0.6 | 2026-05-19T08:55:27.807Z | user\n\n图标改用SVG path图形替代emoji，修复resvg渲染emoji为黑色方块的问题\n\nv1.0.5 | 2026-05-19T08:16:10.947Z | user\n\n创建/更新成功后展示功能特性、操作方式和文件链接；SVG图标自动转PNG；横屏多设备响应式适配\n\nv1.0.4 | 2026-05-14T09:23:56.875Z | user\n\n横屏多设备适配：支持智能屏960x600、闺蜜机1280x720、pad1920x1080、pad17 2560x1600，使用响应式布局clamp/vw/vh\n\nv1.0.3 | 2026-05-13T09:45:24.690Z | auto\n\n- Expanded skill支持范围，可自动区分“应用”与“文档”类需求，新增文档/表格/PPT生成能力。\n- 补充丰富样例，详化语义判断规则，对非产出型请求（如聊天/问答/小说）不再触发技能。\n- 配置交互式HTML、A4文档、PPT、Excel等多模板详细生成规范，含布局、功能与交互要求。\n- 提升HTML通用约束，要求模板自带viewport等标签，完善外部依赖和文件大小限制说明。\n- 完善前缀兼容处理，保留老入口的特殊快速触发需求。\n- 新增多脚本和元数据文件，补全技能工程结构。\n\nv1.0.2 | 2026-05-11T07:19:20.240Z | auto\n\nmyapp-creator 1.0.2\n\n- Updated metadata and versioning files for release 1.0.2\n- Minor documentation and configuration adjustments (README.md, clawhub.yaml, SKILL.md)\n- No changes to core logic or skill behavior\n\nv1.0.1 | 2026-05-11T06:44:36.847Z | auto\n\n- Updated version to 1.0.1.\n- Modified configuration files: README.md, clawhub.yaml, metadata.json, publish.sh, and version.txt.\n- No changes to skill logic or workflow.\n- Documentation and metadata improvements.\n\nv1.0.0 | 2026-05-09T12:24:02.209Z | auto\n\nmyapp-creator 1.0.0 初始发布：\n\n- 支持用一句话生成独立单文件 HTML 应用，自动落库到 fe-service。\n- 明确的触发标记机制：创建、更新、安装自检有专用前缀。\n- 全流程 300 秒超时保护，超时或失败直接给用户友好兜底回复。\n- HTML 资源严格约束：全内联、外部引用仅限指定 CDN、大小上限 60KB。\n- 不主动调用大模型，所有内容外部传入；输出禁止包含 HTML 原文或敏感信息。\n- 工具接口调用错误严禁重试，保障安全合规。\n\nArchive index:\n\nArchive v1.0.31: 41 files, 83484 bytes\n\nFiles: clawhub.yaml (565b), LICENSE (912b), metadata.json (464b), openai.yaml (3724b), publish.sh (2611b), README.md (4584b), scripts/_common.sh (761b), scripts/get.sh (437b), scripts/ping.sh (832b), scripts/register.sh (1400b), scripts/update.sh (1161b), skill-card.md (2455b), SKILL.md (41178b), skills/myapp-creator/_meta.json (132b), skills/myapp-creator/clawhub.yaml (565b), skills/myapp-creator/metadata.json (464b), skills/myapp-creator/openai.yaml (3724b), skills/myapp-creator/publish.sh (2611b), skills/myapp-creator/README.md (4584b), skills/myapp-creator/scripts/_common.sh (761b), skills/myapp-creator/scripts/get.sh (437b), skills/myapp-creator/scripts/ping.sh (832b), skills/myapp-creator/scripts/register.sh (1400b), skills/myapp-creator/scripts/update.sh (1161b), skills/myapp-creator/SKILL.md (41178b), skills/myapp-creator/skills/myapp-creator/_meta.json (132b), skills/myapp-creator/skills/myapp-creator/clawhub.yaml (565b), skills/myapp-creator/skills/myapp-creator/metadata.json (464b), skills/myapp-creator/skills/myapp-creator/openai.yaml (3257b), skills/myapp-creator/skills/myapp-creator/publish.sh (2611b), skills/myapp-creator/skills/myapp-creator/README.md (4180b), skills/myapp-creator/skills/myapp-creator/scripts/_common.sh (761b), skills/myapp-creator/skills/myapp-creator/scripts/get.sh (437b), skills/myapp-creator/skills/myapp-creator/scripts/ping.sh (832b), skills/myapp-creator/skills/myapp-creator/scripts/register.sh (1400b), skills/myapp-creator/skills/myapp-creator/scripts/update.sh (1161b), skills/myapp-creator/skills/myapp-creator/SKILL.md (41178b), skills/myapp-creator/skills/myapp-creator/version.txt (7b), skills/myapp-creator/version.txt (7b), version.txt (7b), _meta.json (133b)\n\nFile v1.0.31:SKILL.md\n\n# myapp-creator\n\nVersion: 1.0.31\n\n让用户用一句话生成可独立打开的单文件 HTML 应用或文档，并落库到 fe-service。\n本 skill **不主动调用 LLM**，所有 HTML / app_name / features 由调用本 skill 的 LLM agent 直接产出后传入工具。\n\n> **⚠️ 最高优先级规则（违反即为严重错误）：**\n> 1. 游戏类应用 **绝对禁止** 包含任何形式的 AI 对手、人机对战、电脑自动下棋/出牌。棋类/对战类游戏 **只允许** 双人同屏轮流操作，不实现任何计算机决策逻辑。\n> 2. 所有应用 **绝对禁止** 出现\"导出\"、\"打印\"、\"保存\"按钮或相关功能代码。\n\n## 触发条件\n\n当用户需求匹配以下任一类别时，本 skill 参与：\n\n### 应用类（交互式 HTML）\n关键词：应用、app、小游戏、游戏、网页、页面、工具、计算器、时钟、日历、翻译器、转换器\n示例：\n- \"做一个贪吃蛇游戏\"\n- \"创建一个番茄钟\"\n- \"生成一个天气页面\"\n- \"给我做一个马里奥游戏\"\n- \"帮我做一个单位转换工具\"\n\n### 文件类（文档式 HTML）\n关键词：文档、文件、word、ppt、演示文稿、slides、pdf、excel、xlsx、表格、报告、简历、周报、日报、总结、方案、计划书\n示例：\n- \"生成一个项目周报\"\n- \"做一个自我介绍PPT\"\n- \"创建一个预算表格\"\n- \"帮我写一份简历\"\n- \"保存一个会议纪要文档\"\n\n### 不触发（交给龙虾其他能力）\n- 图片/绘画类：\"画一张…\"、\"生成一张图片…\"\n- 音频/音乐类：\"生成一首歌…\"、\"合成语音…\"\n- 视频类：\"剪辑一个…\"、\"生成视频…\"\n- 小说/纯文本类：\"写一篇小说…\"、\"写一首诗…\"（纯创作不产出可交互文件）\n- 纯问答/聊天/闲聊\n\n### 判断规则\n用户意图是\"产出一个可独立打开、保存下来反复使用的文件或应用\"时触发本 skill。\n如果用户只是想让你\"说一段话\"或\"回答问题\"，不要触发。\n\n## 触发标记（兼容旧入口）\n\n以下前缀仍可直接触发，不需要语义判断：\n- `[create_app]<描述>` → 创建新应用/文件\n- `[update_app:app_id=<n>]<描述>` → 更新已有应用/文件\n- `[install_check:session=<sid>]` → 安装自检（仅调 myapp_ping）\n- 不带上述前缀 → 按语义判断是否匹配触发条件\n\n## 创建流程\n\n最长等待 300s。若 300s 内不能完成 HTML 生成与 `myapp_register` 调用，直接回复\"应用创建失败，请稍后重试\"。\n\n1. 解析用户描述，判断类型（应用类 or 文件类），产出：\n   - `app_name`：应用/文件名，≤30 字\n   - `features`：JSON 字符串数组，3~8 条，每条 ≤30 字，描述具体功能/内容要点\n   - `html_content`：完整可独立打开的单文件 HTML（按类型选择模板风格）\n\n2. HTML 通用约束：\n   - 自包含：CSS/JS 全部内联在 `<style>` / `<script>`\n   - 不依赖本地资源\n   - 外部资源仅允许：unpkg.com / jsdelivr.net / cdnjs.cloudflare.com\n   - 总长度 ≤ 60KB（UTF-8 字节数）\n   - 不引用任何小度专属 API\n   - 必须包含 `<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">`\n\n   **目标运行环境 — Android 8.1 WebView（Chrome/85）**：\n   所有 HTML 最终运行在搭载 Android 8.1 的智能屏 WebView 中，无鼠标/硬键盘，仅支持触摸和左右滑动。\n   生成代码时必须遵守以下兼容规则：\n\n   **JS / Web API 兼容**（feature detection 优先，禁止仅凭 UA 判断）：\n   - 使用 `typeof IntersectionObserver !== 'undefined'` 等特性检测，不要直接调用可能不存在的 API\n   - 禁止使用 Chrome 86+ 才支持的 API：`CSS.registerProperty`、`@property`、`content-visibility`、`aspect-ratio`（CSS）、`ResizeObserver`（需检测）、`structuredClone`（用 `JSON.parse(JSON.stringify())` 替代）\n   - 禁止使用 ES2020+ 语法：`??=`、`||=`、`&&=`、`import()`动态导入、`BigInt`、`globalThis`（用 `window` 替代）\n   - **禁止使用 `ctx.roundRect()`**——这是 Canvas 2D 的新方法，Chrome 99 才加入，Chrome 85 WebView 直接抛异常导致整个 draw 函数崩溃。必须用以下自定义 helper 替代：\n     ```js\n     function rr(ctx, x, y, w, h, r) {\n       ctx.beginPath();\n       ctx.moveTo(x + r, y);\n       ctx.lineTo(x + w - r, y);  ctx.arcTo(x + w, y,     x + w, y + r,     r);\n       ctx.lineTo(x + w, y + h - r); ctx.arcTo(x + w, y + h, x + w - r, y + h, r);\n       ctx.lineTo(x + r, y + h);  ctx.arcTo(x,     y + h, x,     y + h - r, r);\n       ctx.lineTo(x, y + r);      ctx.arcTo(x,     y,     x + r, y,         r);\n       ctx.closePath();\n     }\n     // 用法：rr(ctx, x, y, w, h, radius); ctx.fill();\n     ```\n   - **禁止在 Canvas 内用 emoji 字符**（如 🌕🔥❤️ 等）——Android WebView 字体渲染异常会抛异常或显示方块。Canvas 内所有文字必须用纯 ASCII/中文文字，用几何图形代替 emoji（如\"♥ × 3\"改用红色圆形绘制）\n   - 可安全使用：`Promise`、`async/await`、`fetch`、`CSS Grid`、`Flexbox`、`CSS Variables`、`IntersectionObserver`（需检测）、`requestAnimationFrame`\n   - `localStorage` / `sessionStorage` 在 WebView 中可用，但不要依赖跨页面持久化\n\n   **CSS 兼容**：\n   - `100vh` 在 Android WebView 中等于视口高度（无地址栏问题），可直接使用\n   - `position: sticky` 在 Chrome 85 中支持，但父容器不能有 `overflow: hidden`，否则失效\n   - `position: fixed` 在 WebView 中软键盘弹出时可能错位，避免在输入框场景依赖 fixed 定位\n   - **禁止使用 CSS `inset` 属性**（`inset: 0` 等简写）——Chrome 87 才支持，Chrome 85 不识别，会导致遮罩层无法撑满、内容偏移到左上角。替换写法：`top:0; left:0; right:0; bottom:0`（注意：`box-shadow` 中的 `inset` 关键字不受此限制，可以正常使用）\n   - 禁止使用 `backdrop-filter`（Chrome 85 需前缀，WebView 不保证支持）\n   - `clamp()` 在 Chrome 85 中已支持，可正常使用\n   - 滚动容器加 `-webkit-overflow-scrolling: touch` 保证惯性滚动\n\n   **触摸与交互**：\n   - 所有可点击元素最小触摸区域 ≥ 44×44px\n   - 使用 `touchstart`/`touchmove`/`touchend` 处理手势；`click` 在 WebView 中有 300ms 延迟，交互敏感场景改用 touch 事件\n   - 消除 300ms 延迟：在 `<meta name=\"viewport\">` 中加 `user-scalable=no` 或在 CSS 中加 `touch-action: manipulation`\n   - 禁止依赖 `hover` 状态（无鼠标），交互反馈改用 `:active` 或 JS touch 事件\n   - 左右滑动手势用 `touchstart`/`touchend` 计算 `deltaX` 实现，不要用第三方手势库（CDN 可能不可达）\n   - 游戏类应用禁止使用键盘事件（`keydown`/`keyup`）作为唯一控制方式，必须提供触摸按钮\n\n   **软键盘与输入框**：\n   - 输入框获焦时 WebView 会 resize 视口，避免用 `height: 100vh` 做全屏布局的唯一约束\n   - 输入框弹出软键盘后 `fixed` 元素可能被遮挡，底部操作栏改用 `position: sticky` 或监听 `visualViewport.resize`\n   - 如需监听软键盘：`window.visualViewport` 在 Chrome 61+ 可用，Chrome 85 支持\n\n   **弹窗 / Modal**：\n   - 自定义弹窗用 `position: fixed; top:0; left:0; width:100%; height:100%` 覆盖全屏\n   - 弹窗内滚动区域加 `overflow-y: auto; -webkit-overflow-scrolling: touch`\n   - 禁止使用原生 `alert()`/`confirm()`/`prompt()`（WebView 中可能被宿主拦截）\n\n   - **横屏多设备适配**：目标设备均为横屏展示，尺寸差异较大：\n     | 设备 | 视口宽×高比例 |\n     |------|-----------|\n     | 普通智能屏 | 960×600 |\n     | mini智能屏 | 960×480 |\n     | 闺蜜机 | 1280×720 |\n     | 秋月 | 1280×800 |\n     | pad | 1920×1080 |\n     | pad17 | 2560×1600 |\n     布局以横屏为前提（宽 > 高），使用 `vw`/`vh`/百分比等响应式单位，禁止固定像素宽度导致小屏溢出或大屏留白。\n     字体大小建议用 `clamp()` 适配，如 `font-size: clamp(14px, 2vw, 18px)`。\n\n3. HTML 模板风格（按类型选择）：\n\n   **应用类** — 交互式：\n   - 纯单机离线游戏/工具，不依赖任何 server 服务\n   - **严禁实现 AI 对手 / 人机对战 / 电脑自动下棋等功能**，棋类游戏只做双人同屏轮流对战（两个玩家在同一屏幕上交替操作）\n   - 棋类游戏正确做法示例：五子棋 → 黑白双方轮流点击落子，判断五连胜负，无任何电脑决策代码；象棋 → 红黑双方轮流移动棋子\n   - 功能简单易上手，玩法直观，适合触屏快速操作\n   - 支持触屏操作（touch 事件）\n   - 美观的 UI 设计\n   - 根容器使用 `width:100vw; height:100vh; overflow:hidden` 撑满全屏\n   - 关键元素尺寸用 `vmin` 或百分比，确保 960px 小屏到 2560px 大屏均可用\n\n   **游戏类强制横版左右布局**（所有游戏类应用必须遵守）：\n\n   > ⚠️ **强制要求**：所有游戏页面必须是横版布局，整体分为左右两栏，**禁止竖版堆叠布局**。\n\n   布局结构如下：\n   ```\n   ┌─────────────────────────────┬──────────────────┐\n   │                             │  状态（必展示）    │\n   │                             │  关卡/得分/生命    │\n   │       游戏区域（左栏）        ├──────────────────┤\n   │    占总宽约 65%~70%          │  下一块/预览      │\n   │    高度撑满 100vh            ├──────────────────┤\n   │                             │ ◀ II ▶  方向键   │\n   └─────────────────────────────┴──────────────────┘\n   ```\n\n   **右栏布局规则（状态区和方向键始终可见）**：\n   - `#status`（关卡、难度、得分/目标分、生命）：`flex:0 0 auto`，**必须展示，不可压缩**\n   - `#game-preview`（下一块/预览区）：`flex:0 0 auto`，**有预览需求的游戏必须展示，不可压缩**\n   - `#leaderboard`（排行榜）和 `#instructions`（操作说明）：**当游戏有 `#game-preview` 时，不展示排行榜和操作说明**（右栏空间有限，预览区优先）。仅在无预览需求的游戏中，才用 `flex:1 1 0; min-height:0` + 标题 h3 固定 + `.sb-inner` 内容区 `overflow-y:auto` 独立滚动的方式展示\n   - `#controls`（方向键+暂停）：`flex:0 0 auto`，**固定在右栏底部，始终可见，不参与滚动**\n\n   CSS 骨架模板（必须使用）：\n   ```html\n   <div id=\"app\" style=\"width:100vw;height:100vh;display:flex;flex-direction:row;overflow:hidden;background:#1a1a2e;\">\n     <!-- 左栏：游戏区域，尽量大，canvas 撑满整个左栏，不留空白 -->\n     <div id=\"game-area\" style=\"flex:1;position:relative;min-width:0;\">\n       <canvas id=\"canvas\" style=\"display:block;width:100%;height:100%;\"></canvas>\n       <!-- 开始/结束/暂停 遮罩层：必须用以下写法居中，禁止只写 top:0;left:0 导致内容贴左上角 -->\n       <!-- <div id=\"overlay\" style=\"position:absolute;top:0;left:0;right:0;bottom:0;display:flex;flex-direction:column;\n            align-items:center;justify-content:center;text-align:center;\n            background:rgba(0,0,0,.6);z-index:10;\">\n         <h2 style=\"color:#fff;font-size:clamp(18px,3vw,32px);margin:0 0 2vh;\">游戏标题</h2>\n         <p style=\"color:#ccc;font-size:clamp(12px,1.8vw,18px);margin:0 0 3vh;\">操作说明文字</p>\n         <button style=\"padding:1.2vh 4vw;font-size:clamp(14px,2vw,20px);border-radius:2vmin;\n           border:none;background:#e74c3c;color:#fff;touch-action:manipulation;\">开始游戏</button>\n       </div> -->\n       <!-- ⚠️ 遮罩内所有文字/按钮必须通过 flexbox 居中，严禁使用固定 top/left 像素值定位 -->\n     </div>\n     <!-- 右栏：固定宽度，状态区+方向键始终可见 -->\n     <div id=\"sidebar\" style=\"width:26vw;min-width:180px;max-width:300px;height:100vh;\n          display:flex;flex-direction:column;padding:1.5vh 1.2vw;gap:0.8vh;box-sizing:border-box;overflow:hidden;\">\n       <!-- ① 游戏状态：必展示，不可压缩 -->\n       <div id=\"status\" style=\"flex:0 0 auto;font-size:clamp(10px,1.4vw,14px);\">关卡/得分/生命...</div>\n       <!-- ② 预览区（下一块等）：有预览需求时展示；若有预览区则不放排行榜和操作说明 -->\n       <div id=\"game-preview\" style=\"flex:1 1 0;min-height:0;\">...</div>\n       <!-- ③④ 排行榜+操作说明：仅在无预览区的游戏中使用（与 ② 二选一） -->\n       <!--\n       <div id=\"leaderboard\" style=\"flex:1 1 0;min-height:0;display:flex;flex-direction:column;font-size:clamp(10px,1.3vw,13px);\">\n         <h3 style=\"flex:0 0 auto;margin:0 0 0.4vh;\">排行榜</h3>\n         <div class=\"sb-inner\" style=\"flex:1 1 0;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;\n              scrollbar-width:thin;scrollbar-color:rgba(255,255,255,.3) transparent;\">\n         </div>\n       </div>\n       <div id=\"instructions\" style=\"flex:1 1 0;min-height:0;display:flex;flex-direction:column;font-size:clamp(10px,1.3vw,13px);\">\n         <h3 style=\"flex:0 0 auto;margin:0 0 0.4vh;\">操作说明</h3>\n         <div class=\"sb-inner\" style=\"flex:1 1 0;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;\n              scrollbar-width:thin;scrollbar-color:rgba(255,255,255,.3) transparent;\">\n         </div>\n       </div>\n       -->\n       <!-- ⑤ 方向键+暂停：固定在底部，始终可见 -->\n       <div id=\"controls\" style=\"flex:0 0 auto;padding-top:0.8vh;\">...</div>\n     </div>\n   </div>\n   ```\n   Canvas 尺寸初始化（JS 中必须这样写，确保 Android 8.1 WebView 中 canvas 像素与显示尺寸一致）：\n\n   > ⚠️ **Android 8.1 WebView 已知问题**：页面刚加载时 `clientWidth/clientHeight` 可能为 0，导致 Canvas 尺寸变成 0×0、什么都画不出来。必须用以下模板，通过 `window.load` + 双层 `requestAnimationFrame` 延迟读取尺寸，并用 `offsetWidth` 做兜底。\n\n   Canvas CSS（必须用 `position:absolute` 撑满，不依赖 flex 布局尺寸）：\n   ```html\n   <div id=\"game-area\" style=\"flex:1;position:relative;min-width:0;\">\n     <canvas id=\"canvas\" style=\"position:absolute;top:0;left:0;width:100%;height:100%;display:block;\"></canvas>\n   </div>\n   ```\n\n   Canvas 尺寸初始化（必须在 `window.load` + 双层 `requestAnimationFrame` 后执行）：\n   ```js\n   function initCanvas() {\n     const gameArea = document.getElementById('game-area');\n     const canvas = document.getElementById('canvas');\n     // offsetWidth 兜底：Android 8.1 WebView 刚加载时 clientWidth 可能为 0\n     const w = gameArea.clientWidth  || gameArea.offsetWidth  || window.innerWidth * 0.7;\n     const h = gameArea.clientHeight || gameArea.offsetHeight || window.innerHeight;\n     canvas.width  = w;\n     canvas.height = h;\n     // 格子大小根据 canvas 尺寸动态计算\n     const COLS = 30, ROWS = Math.floor(h / (w / COLS));\n     const cellW = w / COLS, cellH = h / ROWS;\n     // ... 其余游戏初始化逻辑\n   }\n\n   // 必须在 window.load 后、双层 rAF 确保布局渲染完毕再读尺寸\n   window.addEventListener('load', function() {\n     requestAnimationFrame(function() {\n       requestAnimationFrame(function() {\n         initCanvas();\n       });\n     });\n   });\n   ```\n\n   **游戏类 WebView 触摸适配要点**（Android 8.1 WebView 无键盘/鼠标）：\n\n   > ⚠️ **强制要求（主控方式）**：所有游戏的**主要控制方式必须是触摸坐标直接映射**——玩家手指按在画布哪里，角色/目标就跟到哪里（或朝那个方向移动），不需要抬手再点方向按钮。右下角的 ↑↓←→ 方向键和暂停键保留作为**辅助控制**，不是唯一控制入口。暂停按钮必须位于方向键 3×3 grid 的**正中心格**。\n\n   **触摸坐标映射实现模板**（贴在 Canvas/游戏容器上）：\n   ```js\n   // 主控：手指位置直接映射到游戏坐标\n   canvas.addEventListener('touchstart', onTouch, {passive: false});\n   canvas.addEventListener('touchmove',  onTouch, {passive: false});\n\n   function onTouch(e) {\n     e.preventDefault();\n     const rect = canvas.getBoundingClientRect();\n     const touch = e.touches[0];\n     // 将屏幕像素坐标换算为 canvas 内部坐标\n     const cx = (touch.clientX - rect.left) * (canvas.width  / rect.width);\n     const cy = (touch.clientY - rect.top)  * (canvas.height / rect.height);\n     movePlayerTo(cx, cy);   // 替换为游戏实际函数：直接设置目标坐标\n   }\n   ```\n   - 对于贪吃蛇、俄罗斯方块等离散格子类游戏，触摸映射改为**滑动方向**：`touchstart` 记录起点，`touchend` 计算 `deltaX/deltaY`，阈值 20px 内忽略，超过则取绝对值较大的轴作为方向\n   - 对于飞机大战、跑酷等连续坐标类游戏，用 `touchmove` 实时跟随手指坐标\n   - 左侧游戏遮罩/操作说明中必须用一句话说明触摸操作方式，例如：「触摸屏幕控制移动，手指在哪飞机就去哪」\n\n   **飞机大战类游戏专项规则**：\n   - **子弹必须自动连续发射**（`setInterval` 或游戏主循环中每隔固定帧数发射），**禁止要求玩家手动点击射击**\n   - 自动射频简单难度约 300ms/发，中等 200ms/发，困难 100ms/发\n   - 操作说明中写「触摸屏幕移动飞机，子弹自动发射」\n\n   **俄罗斯方块/消除类游戏专项规则**：\n   - **贴地即锁，零等待**：下落计时器触发时若方块无法继续下移，立刻执行 `lock()` → `spawn()`，下一块在同一帧内出现。**禁止 LOCK_DELAY 缓冲期**\n   - **`spawnPiece()` 必须将新方块赋值给全局变量**（如 `cur = shape`），否则 `drawGrid()` 中 `if(cur != null)` 判断为 false 导致方块不显示\n   - **经典 NES 速度标准**：第 1 关 800ms → 第 5 关 533ms → 第 10 关 333ms → 第 15 关 200ms → 第 20 关 100ms → 第 29 关+ 83ms（极限）\n   - 难度递增公式：`interval = Math.max(83, 800 - (level - 1) * 5 * (1000/60))`（每关减约 5 帧，60fps 基准）\n   - 预览区（下一块）在右栏 `#game-preview` 中展示，有预览时不展示排行榜和操作说明\n\n   **方向键 + 暂停按钮模板**（放在右栏 `#controls` 内，作为辅助控制）：\n\n   布局规则：**暂停按钮放在方向键 3×3 grid 的正中心格**，不单独放在键盘上方。整体视觉要炫酷：方向键用半透明玻璃质感，暂停键用蓝色渐变 + 光晕，`:active` 有明显按压反馈。\n\n   ```html\n   <div id=\"controls\" style=\"flex:0 0 auto;display:flex;flex-direction:column;align-items:center;\n        gap:0.8vh;touch-action:none;user-select:none;\">\n     <!-- 3×3 方向键grid，暂停在正中心 -->\n     <div style=\"display:grid;grid-template-columns:repeat(3,13vmin);grid-template-rows:repeat(3,13vmin);gap:1.2vmin;\">\n       <div></div>\n       <button class=\"dpad-btn\" id=\"btn-up\">▲</button>\n       <div></div>\n       <button class=\"dpad-btn\" id=\"btn-left\">◀</button>\n       <button class=\"dpad-btn\" id=\"btn-pause\">II</button>\n       <button class=\"dpad-btn\" id=\"btn-right\">▶</button>\n       <div></div>\n       <button class=\"dpad-btn\" id=\"btn-down\">▼</button>\n       <div></div>\n     </div>\n   </div>\n   <style>\n   /* 方向键：半透明玻璃质感 */\n   .dpad-btn {\n     background: linear-gradient(145deg, rgba(255,255,255,.22), rgba(255,255,255,.06));\n     border: 1.5px solid rgba(255,255,255,.35);\n     border-radius: 50%;\n     color: #fff;\n     font-size: 4.5vmin;\n     touch-action: manipulation;\n     display: flex;\n     align-items: center;\n     justify-content: center;\n     box-shadow: 0 2px 8px rgba(0,0,0,.4), inset 0 1px 0 rgba(255,255,255,.25);\n     cursor: pointer;\n     transition: transform .08s;\n   }\n   .dpad-btn:active {\n     background: linear-gradient(145deg, rgba(255,255,255,.45), rgba(255,255,255,.15));\n     transform: scale(.88);\n     box-shadow: 0 1px 4px rgba(0,0,0,.5), inset 0 1px 0 rgba(255,255,255,.3);\n   }\n   /* 暂停键与方向键样式完全一致，仅字号加粗 + 细边框区分；用 II 代替 ⏸ 符号（WebView 渲染兼容） */\n   #btn-pause {\n     font-size: 4.5vmin;\n     font-weight: bold;\n     border-color: rgba(255,255,255,.6);\n   }\n   </style>\n   <script>\n   document.getElementById('btn-up').addEventListener('touchstart',    e=>{e.preventDefault();changeDir(0,-1);},{passive:false});\n   document.getElementById('btn-down').addEventListener('touchstart',   e=>{e.preventDefault();changeDir(0, 1);},{passive:false});\n   document.getElementById('btn-left').addEventListener('touchstart',   e=>{e.preventDefault();changeDir(-1,0);},{passive:false});\n   document.getElementById('btn-right').addEventListener('touchstart',  e=>{e.preventDefault();changeDir( 1,0);},{passive:false});\n   document.getElementById('btn-pause').addEventListener('touchstart',  e=>{e.preventDefault();togglePause();},{passive:false});\n   // 桌面调试保留键盘\n   document.addEventListener('keydown', e=>{\n     if(e.key==='ArrowUp')    changeDir(0,-1);\n     if(e.key==='ArrowDown')  changeDir(0, 1);\n     if(e.key==='ArrowLeft')  changeDir(-1,0);\n     if(e.key==='ArrowRight') changeDir( 1,0);\n     if(e.key===' ')          togglePause();\n   });\n   </script>\n   ```\n   - `changeDir(dx, dy)` 和 `togglePause()` 替换为游戏实际函数名；坐标映射类游戏中方向键可改为触发\"持续向该方向移动\"逻辑\n   - 按钮尺寸用 `vmin` 单位，960px 小屏到 2560px 大屏均可用\n   - 暂停键必须在中心格，禁止移到键盘外部单独放置\n\n   **游戏角色视觉规范**（卡通可爱风格，禁止只画纯色圆点/方块）：\n   - 贪吃蛇：蛇头用圆角矩形 + 两个白色眼睛（小圆 + 黑色瞳孔）+ 根据朝向旋转；蛇身用渐变色圆角矩形，节节之间颜色略有变化；食物用苹果/草莓等图形（红色圆 + 绿色小叶子）\n   - 贪吃蛇蛇头绘制示例（Canvas 2D）：\n     ```js\n     function drawHead(ctx, x, y, w, h, dir) {\n       ctx.save();\n       ctx.translate(x + w/2, y + h/2);\n       const angle = {right:0, down:Math.PI/2, left:Math.PI, up:-Math.PI/2}[dir] || 0;\n       ctx.rotate(angle);\n       // 头部圆角矩形\n       ctx.fillStyle = '#2ecc71';\n       roundRect(ctx, -w/2, -h/2, w, h, w*0.3);\n       ctx.fill();\n       // 眼睛\n       [[-w*0.15, -h*0.15],[w*0.15, -h*0.15]].forEach(([ex,ey])=>{\n         ctx.fillStyle='#fff'; ctx.beginPath(); ctx.arc(ex,ey,w*0.12,0,Math.PI*2); ctx.fill();\n         ctx.fillStyle='#222'; ctx.beginPath(); ctx.arc(ex+w*0.03,ey,w*0.06,0,Math.PI*2); ctx.fill();\n       });\n       ctx.restore();\n     }\n     function roundRect(ctx,x,y,w,h,r){\n       ctx.beginPath();ctx.moveTo(x+r,y);ctx.lineTo(x+w-r,y);ctx.arcTo(x+w,y,x+w,y+r,r);\n       ctx.lineTo(x+w,y+h-r);ctx.arcTo(x+w,y+h,x+w-r,y+h,r);ctx.lineTo(x+r,y+h);\n       ctx.arcTo(x,y+h,x,y+h-r,r);ctx.lineTo(x,y+r);ctx.arcTo(x,y,x+r,y,r);ctx.closePath();\n     }\n     ```\n   - 俄罗斯方块：方块用渐变色 + 高光边框，不同形状用不同颜色\n   - 飞机大战：飞机用多边形或 SVG path 绘制，子弹用细长椭圆，敌机颜色与玩家区分；**子弹必须自动连续发射，禁止要求玩家手动点击射击**；操作说明写「触摸屏幕移动飞机，子弹自动发射」\n   - 通用原则：所有游戏角色必须有辨识度，能看出是什么，不能只是纯色几何形状\n\n   **平台跳跃 / 跑酷类游戏触摸控制规范**（马里奥、跑酷等左右移动+跳跃类游戏必须遵守）：\n\n   > 核心设计：左右半屏控制方向，上滑触发跳跃，斜滑（如右上方）同时触发移动+跳跃，完全模拟实体手柄的拇指操作。\n\n   ```js\n   let tSX = 0, tSY = 0; // 触摸起点，每次 touchstart 或跳跃触发后重置\n\n   canvas.addEventListener('touchstart', e => {\n     e.preventDefault();\n     const t = e.touches[0];\n     tSX = t.clientX; tSY = t.clientY;\n   }, {passive: false});\n\n   canvas.addEventListener('touchmove', e => {\n     e.preventDefault();\n     const t = e.touches[0];\n     const dx = t.clientX - tSX;\n     const dy = t.clientY - tSY; // 负值 = 向上\n\n     // 左右移动：根据手指当前位置在画布左/右半区判断方向\n     const rect = canvas.getBoundingClientRect();\n     if (t.clientX < rect.left + rect.width / 2) {\n       moveLeft();\n     } else {\n       moveRight();\n     }\n\n     // 上滑检测跳跃：阈值 20px，触发后立即重置 tSY 防止重复触发\n     if (dy < -20) {\n       jump();\n       tSY = t.clientY; // 重置参考点，允许同一次触摸连续触发（斜滑等）\n     }\n   }, {passive: false});\n\n   canvas.addEventListener('touchend', e => {\n     stopMove(); // 手指抬起停止移动\n   }, {passive: false});\n   ```\n   - `dy < -20`（负值向上）触发跳跃，同一次 `touchmove` 序列可多次触发（重置 tSY）\n   - 斜向上滑（如右上方）会同时执行 `moveRight()` + `jump()`，天然支持斜跳\n   - 阈值 20px 优于 28px，更灵敏，适合快节奏横板游戏\n   - 右栏方向键：◀▶ 控制移动，▲ 触发跳跃，▼ 可用于下蹲/快速下落\n   - 触摸坐标映射为主控，方向键为辅助——两套控制并行，不互斥\n   - 防止页面滚动干扰游戏：游戏容器加 `touch-action:none`，Canvas 加 `{ passive: false }` 并 `e.preventDefault()`\n   - 游戏暂停/重启按钮触摸区域 ≥ 44×44px\n   - 禁止在游戏主循环中使用 `alert()`，改用页面内自定义弹窗显示胜负信息\n\n   **跳跃 / 平台类游戏角色定位规范**（跳一跳、跑酷、马里奥等必须遵守）：\n\n   > ⚠️ **已知 Bug 根因**：用 `ph = PLAYER_H / squish` 作为绘制高度来定位头部/身体，当落地弹跳动画触发 `squish ≠ 1` 时，`ph` 变大，角色视觉上\"沉入\"平台下方。\n\n   **正确做法**：`py` 始终表示角色**脚底**的 y 坐标（即站在平台顶面），`squish` 只影响**宽度**（身体变宽/变窄），绝不影响高度定位：\n   ```js\n   // py = 脚底 y（始终贴平台顶面，不受 squish 影响）\n   // squish: 落地压缩系数，1=正常，>1=横向压扁（宽变大、高不变）\n   function drawPlayer(ctx, px, py, squish) {\n     const W = PLAYER_W * squish;   // 宽度随 squish 变化\n     const H = PLAYER_H;            // 高度固定，不除以 squish\n     const x = px - W / 2;\n     const y = py - H;              // 脚底 py 往上量固定 PLAYER_H\n     // 绘制身体：rr(ctx, x, y, W, H, radius)\n   }\n   ```\n   - `squish` 动画只修改 `W`（或同时等比缩小 `H` 但必须同步上移 `y = py - H`），确保脚底始终在 `py`\n   - 禁止写 `y = py - PLAYER_H / squish`，这会在 squish > 1 时让角色上移，squish < 1 时下沉\n\n\n\n   > ⚠️ **强制要求**：所有游戏类应用必须内置难度阶段机制，**默认从简单难度开始**，通过 10 关后自动升级为中等难度，通过 20 关后升级为困难。以飞机大战为参照基准，其他游戏按比例换算。\n\n   **难度分级标准**（以飞机大战为基准示例）：\n   | 阶段 | 关卡范围 | 敌机坠落速度 | 子弹发射间隔 | 过关目标分 | 每关目标分增量 |\n   |------|---------|------------|------------|---------|------------|\n   | 简单（Easy）  | 第 1~10 关  | 基础速度 × 1/2 | 300ms/发 | 100 分 | +10 分/关 |\n   | 中等（Medium）| 第 11~20 关 | 基础速度 × 80% | 200ms/发 | 200 分 | +10 分/关 |\n   | 困难（Hard）  | 第 21 关起  | 基础速度  | 100ms/发 | 300 分 | +10 分/关 |\n\n   - **难度参数必须用变量控制**，根据关卡号计算，禁止魔法数字硬编码：\n     ```js\n     let level = 1; // 每通关 +1\n\n     function getDifficulty(level) {\n       if (level <= 10) return {\n         speedMul: 1/2,  bulletInterval: 300,\n         targetScore: 100 + (level - 1) * 10   // 100, 110, 120...\n       };\n       if (level <= 20) return {\n         speedMul: 0.8,  bulletInterval: 200,\n         targetScore: 200 + (level - 11) * 10  // 200, 210, 220...\n       };\n       return {\n         speedMul: 1,  bulletInterval: 100,\n         targetScore: 300 + (level - 21) * 10  // 300, 310, 320...\n       };\n     }\n     ```\n   - **不设硬性关卡上限**，进入困难阶段后目标分持续增加，让玩家可以持续挑战\n   - 关卡信息（当前关卡、当前难度名称、得分 / 目标分、生命）必须在右栏顶部实时显示\n\n   **文档/Word/PDF 风格**：\n   - 容器使用响应式宽度：`max-width: min(1200px, 92vw); margin: 3vh auto; padding: 4vh 5vw`\n   - 在 960px 小屏上内容几乎满宽，在 1920px+ 大屏上居中且不超过 1200px\n   - 正文字号 `clamp(14px, 1.8vw, 18px)`，标题 `clamp(20px, 3vw, 32px)`\n   - 清晰的标题/段落/列表排版，使用衬线或无衬线字体\n   - 页面顶部可显示文档标题和日期\n\n   **PPT/演示文稿风格**：\n   - 引入 reveal.js：`<link rel=\"stylesheet\" href=\"https://unpkg.com/reveal.js@5/dist/reveal.css\">`\n   - 引入主题：`<link rel=\"stylesheet\" href=\"https://unpkg.com/reveal.js@5/dist/theme/white.css\">`\n   - 引入 JS：`<script src=\"https://unpkg.com/reveal.js@5/dist/reveal.js\"></script>`\n   - 内容用 `<div class=\"reveal\"><div class=\"slides\"><section>...</section></div></div>` 包裹\n   - 每个 `<section>` 是一页幻灯片\n   - 初始化：`<script>Reveal.initialize({hash:true, touch:true, width:960, height:600});</script>`\n     reveal.js 内部会等比缩放到实际视口，960×600 基准保证所有设备文字清晰不溢出\n   - 5~15 页为宜\n\n   **Excel/表格风格**：\n   - 容器 `width:100vw; height:100vh; overflow:auto; box-sizing:border-box; padding:2vh 3vw`\n   - HTML `<table>` 宽度 100%，带 border 和交替行色\n   - 表头字号 `clamp(12px, 1.6vw, 16px)`，单元格字号 `clamp(12px, 1.4vw, 15px)`\n   - 表头点击可排序（内联 JS 实现）\n   - 单元格 `contenteditable=\"true\"` 可编辑\n\n   **所有类型通用禁止项**：\n   - 禁止出现\"导出\"、\"打印\"、\"保存\"功能按钮\n   - 禁止 `@media print` 相关样式\n   - 禁止 `window.print()`、文件下载等功能\n\n4. 图标生成（icon_base64）：\n   - 产出一个 256×256 的 SVG 图标\n   - 风格：圆角矩形纯色背景 + 居中的主题图形（用 SVG path/circle/rect 绘制，**禁止使用 `<text>` 或 emoji**）\n   - 背景用饱和度高的纯色，中间图形用与背景对比强烈的颜色（可用白色、也可用多色彩搭配，参考 App Store 图标风格）\n   - SVG 文本做 base64 编码后传入 icon_base64（不带 `data:image/svg+xml;base64,` 前缀）\n   - SVG 原文不超过 3KB，**禁止使用 `<text>` 元素或 emoji**\n\n5. **【必须执行】HTML 自检 — 提交前逐项检查**：\n   生成 html_content 后，**必须**在脑内逐项检查以下条件，任一不通过则必须修改代码后再提交：\n\n   **自检项 A — 禁止 AI/电脑对手**（仅游戏/棋类/对战类需检查）：\n   - [ ] 代码中是否存在任何\"电脑走棋/出牌/移动\"的函数？（如 `computerMove`, `aiMove`, `makeAIMove`, `botPlay`, `autoPlay`, `cpuTurn`）→ 若有，**删除整个函数及其调用**\n   - [ ] 代码中是否存在 `minimax`, `alphaBeta`, `evaluate`, `bestMove`, `getRandomMove`, `Math.random()` 用于选择落子位置？→ 若有，**删除**\n   - [ ] 代码中是否存在\"难度选择\"（easy/medium/hard）或\"人机/人人\"模式切换？→ 若有，**删除，只保留双人模式**\n   - [ ] 游戏流程是否为：玩家A操作 → 切换到玩家B → 玩家B操作 → 切换回玩家A → 循环？→ 必须是\n   - [ ] 是否有任何 `setTimeout`/`setInterval` 用于延迟执行电脑的操作？→ 若有，**删除**\n\n   **自检项 B — 禁止导出/打印/保存**（所有类型需检查）：\n   - [ ] 是否存在\"导出\"、\"打印\"、\"保存\"、\"下载\"按钮或菜单项？→ 若有，**删除**\n   - [ ] 是否存在 `window.print()`, `document.execCommand('SaveAs')`, `URL.createObjectURL`, `download` 属性？→ 若有，**删除**\n\n   **自检项 C — Android 8.1 WebView 兼容**（所有类型需检查）：\n   - [ ] 是否使用了 Chrome 86+ 才支持的 CSS/JS API（`@property`、`content-visibility`、`structuredClone`、`??=`/`||=`/`&&=` 赋值运算符）？→ 若有，**替换为兼容写法**\n   - [ ] 是否使用了 `ctx.roundRect()`？→ Chrome 99+，**必须替换为自定义 `rr()` helper（用 arcTo 实现）**\n   - [ ] Canvas 内是否使用了 emoji 字符（fillText 中含 🔥❤️ 等）？→ WebView 字体渲染异常，**必须替换为纯文字或几何图形**\n   - [ ] 游戏布局是否为横版左右两栏（左栏游戏区、右栏控制区）？→ **若是竖版堆叠，必须改为横版**\n   - [ ] 右栏有 `#game-preview`（下一块/预览区）时，是否已去掉 `#leaderboard` 和 `#instructions`？→ **预览区与排行/说明互斥，有预览时不展示排行和说明**。无预览区的游戏中，排行/说明用 `flex:1 1 0;min-height:0` + h3 固定 + `.sb-inner` 内滚动。`#controls` 始终 `flex:0 0 auto` 固定底部\n   - [ ] Canvas CSS 是否用 `position:absolute;top:0;left:0;width:100%;height:100%` 撑满左栏，而非依赖 flex 布局？→ **若只用 `width:100%;height:100%` 而无 `position:absolute`，必须修正**\n   - [ ] Canvas 尺寸初始化是否在 `window.load` + 双层 `requestAnimationFrame` 内执行，且用 `offsetWidth` 做兜底（防止 Android 8.1 WebView 初始 `clientWidth` 为 0）？→ **若直接在顶层读 `clientWidth`，必须改为延迟初始化模板**\n   - [ ] 需要方向控制的游戏：是否在 Canvas 上绑定了 `touchstart`/`touchmove` 做坐标直接映射（主控），且右下角仍保留方向键 + 暂停按钮（辅助）？→ **若只有方向按钮而无触摸坐标映射，必须补充**\n   - [ ] 飞机大战类游戏：子弹是否自动连续发射（`setInterval` 或主循环计时），而非要求玩家手动点击射击？→ **若需要手动触发，必须改为自动发射**\n   - [ ] 俄罗斯方块/消除类游戏：①初始下落间隔是否为800ms（经典NES速度）？②贴地后是否立刻lock+spawn（零等待，无LOCK_DELAY缓冲）？③`spawnPiece()`是否将新方块赋值给全局变量（`cur=shape`）？→ **若不符合，按专项规则修正**\n   - [ ] 游戏内的开始/结束/暂停遮罩层是否用 `position:absolute;top:0;left:0;right:0;bottom:0;display:flex;align-items:center;justify-content:center` 居中（**禁止用 `inset:0`，Chrome 85 不支持**）？→ **若用 `inset:0` 或固定像素定位，内容会在智能屏上贴左上角，必须修正**\n   - [ ] 游戏是否实现了三段式难度（简单1~10关：速度×1/2、300ms/发、100+10×n分 → 中等11~20关：速度×80%、200ms/发、200+10×n分 → 困难21关起：基础速度、100ms/发、300+10×n分），且第1关使用简单参数？→ **若全程同一难度或开局就很快，必须按难度分级模板修正**\n   - [ ] 游戏角色是否有视觉辨识度（蛇头有眼睛、食物有形状等），而不是纯色圆点/方块？→ **若只是纯色几何形，必须改为卡通风格**\n   - [ ] 是否使用了原生 `alert()`/`confirm()`/`prompt()`？→ 若有，**改为页面内自定义弹窗**\n   - [ ] 游戏容器/Canvas 是否加了 `touch-action:none` 并在 touch 事件中调用 `e.preventDefault()`？→ 若未处理，**补充**\n   - [ ] 可点击/可触摸元素的触摸区域是否 ≥ 44×44px？→ 若不足，**调整尺寸**\n   - [ ] 跳跃/平台类游戏（跳一跳、跑酷等）：绘制角色时是否用固定常量 `PLAYER_H` 定位脚底坐标（`py` 为脚底 y，头顶为 `py - PLAYER_H`），而非用 `PLAYER_H / squish` 等变形高度定位？→ **`squish`（落地压缩动画）只能影响角色宽度/变窄，绝不能影响脚底 `py` 的计算，否则 squish≠1 时角色视觉上会\"沉入\"平台下方**\n\n   ⚠️ 如果自检发现违规代码，**不要提交**，先修正 html_content 再继续下一步。\n\n5.5. **【必须执行】JS 语法验证**（应用类/游戏类必须，文件类可选）：\n   在调用 `myapp_register` 之前，用 `exec` 工具对 html_content 中的 `<script>` 代码块做语法验证，确保不因语法错误导致游戏在智能屏上完全空白：\n   ```bash\n   # 提取 <script> 内容写临时文件，用 node --check 验证\n   node -e \"\n   const fs = require('fs');\n   const html = fs.readFileSync('/tmp/_skill_check.html','utf8');\n   const scripts = html.match(/<script[\\s\\S]*?>([\\s\\S]*?)<\\/script>/gi) || [];\n   scripts.forEach((s,i) => {\n     const code = s.replace(/<\\/?script[^>]*>/gi,'');\n     fs.writeFileSync('/tmp/_check_'+i+'.js', code);\n   });\n   \" && node --check /tmp/_check_*.js 2>&1\n   ```\n   - 实际操作：将 html_content 写入 `/tmp/_skill_check.html`，运行上面命令\n   - 如果 `node --check` 输出任何错误，必须修复对应 JS 代码后重新验证，直到无错误\n   - 验证通过后再调用 `myapp_register`\n\n6. 调用 `myapp_register` 工具：\n   - 入参：`dumi_id, cuid, query, app_name, html_content, features, icon_base64, tag`\n   - `tag`：根据第 1 步判断的类型填写——**应用类（交互式）填 `\"app\"`，文件类（文档/PPT/表格）填 `\"doc\"`**；不传时服务端默认 `\"app\"`\n   - `dumi_id` 与 `cuid` 从对话上下文中获取（OpenClaw 会注入）\n\n7. 工具返回成功后，向用户展示：\n   - 告知创建完成：\"《<app_name>》已创建完成。\"\n   - 展示功能/玩法（根据类型区分）：\n     - **应用类**：\n       - 列出\"游戏特性\"或\"功能特性\"：从 features 中提取 3~6 条核心点\n       - 列出\"操作方式\"：说明触屏/键盘的操作方法（如方向键控制、暂停等）\n     - **文件类**（文档/PPT/表格）：\n       - 简要概括文档核心内容（如关键数据、主要结论、章节概要等，3~5 条）\n       - 提示\"点击链接即可查看完整内容\"\n   - 展示文件位置：给出返回的 `html_url` 链接\n   - 提示用户：\"下次可以在「我的创作」页面查看和管理你创建的所有应用\"\n\n8. 失败/超长/超出能力 → 回复\"应用创建失败，请稍后重试\"\n   - **不要重试**，**不要伪造成功**，**不要把 html_content 输出到对话**\n\n## 更新流程\n\n最长等待 300s。若 300s 内不能完成旧功能读取、HTML 重新生成与 `myapp_update` 调用，直接回复\"应用更新失败，请稍后重试\"。\n\n1. **先调 `myapp_get(app_id)`** 拿到 `app_name` 与旧 `features`、旧 `query`\n2. 把\"旧 features + 用户新需求\"作为完整上下文，重新生成：\n   - 新 features（合并增删，仍是 1~12 条全集，不是 diff）\n   - 新 html_content（保持原有类型风格）\n   - 若应用主题/类型发生变化，重新生成 icon_base64（同创建流程第 4 步）\n3. 调用 `myapp_update(app_id, query, html_content, features, icon_base64, tag)`\n   - `tag` 同创建流程：应用类填 `\"app\"`，文件类填 `\"doc\"`\n4. 工具返回成功后，向用户展示：\n   - 告知更新完成：\"《<app_name>》已更新完成。\"\n   - 展示更新内容（根据类型区分）：\n     - **应用类**：列出本次新增/变更的功能点和操作方式变化\n     - **文件类**：简要概括更新后的核心内容或变更要点\n   - 展示文件位置：给出返回的 `html_url` 链接\n   - 提示用户：\"下次可以在「我的创作」页面查看和管理你创建的所有应用\"\n5. 失败 → 回复\"应用更新失败，请稍后重试\"\n\n## 安装自检（[install_check:session=<sid>]）\n\n最长等待 300s。超过 300s 未完成安装或 `myapp_ping`，视为安装失败。\n\n仅调用 `myapp_ping(session_id=\"<sid>\", dumi_id, cuid, skill_version=\"1.0.10\")`，\n不输出对话内容（或仅极简的\"已就绪\"）。\n\n## 硬约束\n\n- **绝对禁止在游戏中实现 AI 对手、人机对战、电脑自动操作**——棋类/对战类游戏只允许双人同屏轮流操作\n- 禁止出现的代码模式：`computerMove`, `aiMove`, `makeAIMove`, `minimax`, `alphaBeta`, `bestMove`, `getRandomMove`, 难度选择(easy/medium/hard), 人机/人人模式切换\n- **绝对禁止出现\"导出\"、\"打印\"、\"保存\"按钮或相关功能**\n- **需要方向控制的游戏必须在页面上渲染四个方向触摸按钮（↑↓←→），绑定 `touchstart` 事件**——不得以键盘或滑动手势代替\n- 不要把 `html_content` 原文输出到对话\n- 不要把 `MYAPP_API_TOKEN` / `MYAPP_API_BASE` 输出到对话\n- 工具调用 4xx/5xx → 直接失败兜底，不重试，不修改请求自行重试\n- 除安装、创建、更新外，其它工具交互超过 180s 视为失败\n\nFile v1.0.31:skills/myapp-creator/SKILL.md\n\n# myapp-creator\n\nVersion: 1.0.31\n\n让用户用一句话生成可独立打开的单文件 HTML 应用或文档，并落库到 fe-service。\n本 skill **不主动调用 LLM**，所有 HTML / app_name / features 由调用本 skill 的 LLM agent 直接产出后传入工具。\n\n> **⚠️ 最高优先级规则（违反即为严重错误）：**\n> 1. 游戏类应用 **绝对禁止** 包含任何形式的 AI 对手、人机对战、电脑自动下棋/出牌。棋类/对战类游戏 **只允许** 双人同屏轮流操作，不实现任何计算机决策逻辑。\n> 2. 所有应用 **绝对禁止** 出现\"导出\"、\"打印\"、\"保存\"按钮或相关功能代码。\n\n## 触发条件\n\n当用户需求匹配以下任一类别时，本 skill 参与：\n\n### 应用类（交互式 HTML）\n关键词：应用、app、小游戏、游戏、网页、页面、工具、计算器、时钟、日历、翻译器、转换器\n示例：\n- \"做一个贪吃蛇游戏\"\n- \"创建一个番茄钟\"\n- \"生成一个天气页面\"\n- \"给我做一个马里奥游戏\"\n- \"帮我做一个单位转换工具\"\n\n### 文件类（文档式 HTML）\n关键词：文档、文件、word、ppt、演示文稿、slides、pdf、excel、xlsx、表格、报告、简历、周报、日报、总结、方案、计划书\n示例：\n- \"生成一个项目周报\"\n- \"做一个自我介绍PPT\"\n- \"创建一个预算表格\"\n- \"帮我写一份简历\"\n- \"保存一个会议纪要文档\"\n\n### 不触发（交给龙虾其他能力）\n- 图片/绘画类：\"画一张…\"、\"生成一张图片…\"\n- 音频/音乐类：\"生成一首歌…\"、\"合成语音…\"\n- 视频类：\"剪辑一个…\"、\"生成视频…\"\n- 小说/纯文本类：\"写一篇小说…\"、\"写一首诗…\"（纯创作不产出可交互文件）\n- 纯问答/聊天/闲聊\n\n### 判断规则\n用户意图是\"产出一个可独立打开、保存下来反复使用的文件或应用\"时触发本 skill。\n如果用户只是想让你\"说一段话\"或\"回答问题\"，不要触发。\n\n## 触发标记（兼容旧入口）\n\n以下前缀仍可直接触发，不需要语义判断：\n- `[create_app]<描述>` → 创建新应用/文件\n- `[update_app:app_id=<n>]<描述>` → 更新已有应用/文件\n- `[install_check:session=<sid>]` → 安装自检（仅调 myapp_ping）\n- 不带上述前缀 → 按语义判断是否匹配触发条件\n\n## 创建流程\n\n最长等待 300s。若 300s 内不能完成 HTML 生成与 `myapp_register` 调用，直接回复\"应用创建失败，请稍后重试\"。\n\n1. 解析用户描述，判断类型（应用类 or 文件类），产出：\n   - `app_name`：应用/文件名，≤30 字\n   - `features`：JSON 字符串数组，3~8 条，每条 ≤30 字，描述具体功能/内容要点\n   - `html_content`：完整可独立打开的单文件 HTML（按类型选择模板风格）\n\n2. HTML 通用约束：\n   - 自包含：CSS/JS 全部内联在 `<style>` / `<script>`\n   - 不依赖本地资源\n   - 外部资源仅允许：unpkg.com / jsdelivr.net / cdnjs.cloudflare.com\n   - 总长度 ≤ 60KB（UTF-8 字节数）\n   - 不引用任何小度专属 API\n   - 必须包含 `<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">`\n\n   **目标运行环境 — Android 8.1 WebView（Chrome/85）**：\n   所有 HTML 最终运行在搭载 Android 8.1 的智能屏 WebView 中，无鼠标/硬键盘，仅支持触摸和左右滑动。\n   生成代码时必须遵守以下兼容规则：\n\n   **JS / Web API 兼容**（feature detection 优先，禁止仅凭 UA 判断）：\n   - 使用 `typeof IntersectionObserver !== 'undefined'` 等特性检测，不要直接调用可能不存在的 API\n   - 禁止使用 Chrome 86+ 才支持的 API：`CSS.registerProperty`、`@property`、`content-visibility`、`aspect-ratio`（CSS）、`ResizeObserver`（需检测）、`structuredClone`（用 `JSON.parse(JSON.stringify())` 替代）\n   - 禁止使用 ES2020+ 语法：`??=`、`||=`、`&&=`、`import()`动态导入、`BigInt`、`globalThis`（用 `window` 替代）\n   - **禁止使用 `ctx.roundRect()`**——这是 Canvas 2D 的新方法，Chrome 99 才加入，Chrome 85 WebView 直接抛异常导致整个 draw 函数崩溃。必须用以下自定义 helper 替代：\n     ```js\n     function rr(ctx, x, y, w, h, r) {\n       ctx.beginPath();\n       ctx.moveTo(x + r, y);\n       ctx.lineTo(x + w - r, y);  ctx.arcTo(x + w, y,     x + w, y + r,     r);\n       ctx.lineTo(x + w, y + h - r); ctx.arcTo(x + w, y + h, x + w - r, y + h, r);\n       ctx.lineTo(x + r, y + h);  ctx.arcTo(x,     y + h, x,     y + h - r, r);\n       ctx.lineTo(x, y + r);      ctx.arcTo(x,     y,     x + r, y,         r);\n       ctx.closePath();\n     }\n     // 用法：rr(ctx, x, y, w, h, radius); ctx.fill();\n     ```\n   - **禁止在 Canvas 内用 emoji 字符**（如 🌕🔥❤️ 等）——Android WebView 字体渲染异常会抛异常或显示方块。Canvas 内所有文字必须用纯 ASCII/中文文字，用几何图形代替 emoji（如\"♥ × 3\"改用红色圆形绘制）\n   - 可安全使用：`Promise`、`async/await`、`fetch`、`CSS Grid`、`Flexbox`、`CSS Variables`、`IntersectionObserver`（需检测）、`requestAnimationFrame`\n   - `localStorage` / `sessionStorage` 在 WebView 中可用，但不要依赖跨页面持久化\n\n   **CSS 兼容**：\n   - `100vh` 在 Android WebView 中等于视口高度（无地址栏问题），可直接使用\n   - `position: sticky` 在 Chrome 85 中支持，但父容器不能有 `overflow: hidden`，否则失效\n   - `position: fixed` 在 WebView 中软键盘弹出时可能错位，避免在输入框场景依赖 fixed 定位\n   - **禁止使用 CSS `inset` 属性**（`inset: 0` 等简写）——Chrome 87 才支持，Chrome 85 不识别，会导致遮罩层无法撑满、内容偏移到左上角。替换写法：`top:0; left:0; right:0; bottom:0`（注意：`box-shadow` 中的 `inset` 关键字不受此限制，可以正常使用）\n   - 禁止使用 `backdrop-filter`（Chrome 85 需前缀，WebView 不保证支持）\n   - `clamp()` 在 Chrome 85 中已支持，可正常使用\n   - 滚动容器加 `-webkit-overflow-scrolling: touch` 保证惯性滚动\n\n   **触摸与交互**：\n   - 所有可点击元素最小触摸区域 ≥ 44×44px\n   - 使用 `touchstart`/`touchmove`/`touchend` 处理手势；`click` 在 WebView 中有 300ms 延迟，交互敏感场景改用 touch 事件\n   - 消除 300ms 延迟：在 `<meta name=\"viewport\">` 中加 `user-scalable=no` 或在 CSS 中加 `touch-action: manipulation`\n   - 禁止依赖 `hover` 状态（无鼠标），交互反馈改用 `:active` 或 JS touch 事件\n   - 左右滑动手势用 `touchstart`/`touchend` 计算 `deltaX` 实现，不要用第三方手势库（CDN 可能不可达）\n   - 游戏类应用禁止使用键盘事件（`keydown`/`keyup`）作为唯一控制方式，必须提供触摸按钮\n\n   **软键盘与输入框**：\n   - 输入框获焦时 WebView 会 resize 视口，避免用 `height: 100vh` 做全屏布局的唯一约束\n   - 输入框弹出软键盘后 `fixed` 元素可能被遮挡，底部操作栏改用 `position: sticky` 或监听 `visualViewport.resize`\n   - 如需监听软键盘：`window.visualViewport` 在 Chrome 61+ 可用，Chrome 85 支持\n\n   **弹窗 / Modal**：\n   - 自定义弹窗用 `position: fixed; top:0; left:0; width:100%; height:100%` 覆盖全屏\n   - 弹窗内滚动区域加 `overflow-y: auto; -webkit-overflow-scrolling: touch`\n   - 禁止使用原生 `alert()`/`confirm()`/`prompt()`（WebView 中可能被宿主拦截）\n\n   - **横屏多设备适配**：目标设备均为横屏展示，尺寸差异较大：\n     | 设备 | 视口宽×高比例 |\n     |------|-----------|\n     | 普通智能屏 | 960×600 |\n     | mini智能屏 | 960×480 |\n     | 闺蜜机 | 1280×720 |\n     | 秋月 | 1280×800 |\n     | pad | 1920×1080 |\n     | pad17 | 2560×1600 |\n     布局以横屏为前提（宽 > 高），使用 `vw`/`vh`/百分比等响应式单位，禁止固定像素宽度导致小屏溢出或大屏留白。\n     字体大小建议用 `clamp()` 适配，如 `font-size: clamp(14px, 2vw, 18px)`。\n\n3. HTML 模板风格（按类型选择）：\n\n   **应用类** — 交互式：\n   - 纯单机离线游戏/工具，不依赖任何 server 服务\n   - **严禁实现 AI 对手 / 人机对战 / 电脑自动下棋等功能**，棋类游戏只做双人同屏轮流对战（两个玩家在同一屏幕上交替操作）\n   - 棋类游戏正确做法示例：五子棋 → 黑白双方轮流点击落子，判断五连胜负，无任何电脑决策代码；象棋 → 红黑双方轮流移动棋子\n   - 功能简单易上手，玩法直观，适合触屏快速操作\n   - 支持触屏操作（touch 事件）\n   - 美观的 UI 设计\n   - 根容器使用 `width:100vw; height:100vh; overflow:hidden` 撑满全屏\n   - 关键元素尺寸用 `vmin` 或百分比，确保 960px 小屏到 2560px 大屏均可用\n\n   **游戏类强制横版左右布局**（所有游戏类应用必须遵守）：\n\n   > ⚠️ **强制要求**：所有游戏页面必须是横版布局，整体分为左右两栏，**禁止竖版堆叠布局**。\n\n   布局结构如下：\n   ```\n   ┌─────────────────────────────┬──────────────────┐\n   │                             │  状态（必展示）    │\n   │                             │  关卡/得分/生命    │\n   │       游戏区域（左栏）        ├──────────────────┤\n   │    占总宽约 65%~70%          │  下一块/预览      │\n   │    高度撑满 100vh            ├──────────────────┤\n   │                             │ ◀ II ▶  方向键   │\n   └─────────────────────────────┴──────────────────┘\n   ```\n\n   **右栏布局规则（状态区和方向键始终可见）**：\n   - `#status`（关卡、难度、得分/目标分、生命）：`flex:0 0 auto`，**必须展示，不可压缩**\n   - `#game-preview`（下一块/预览区）：`flex:0 0 auto`，**有预览需求的游戏必须展示，不可压缩**\n   - `#leaderboard`（排行榜）和 `#instructions`（操作说明）：**当游戏有 `#game-preview` 时，不展示排行榜和操作说明**（右栏空间有限，预览区优先）。仅在无预览需求的游戏中，才用 `flex:1 1 0; min-height:0` + 标题 h3 固定 + `.sb-inner` 内容区 `overflow-y:auto` 独立滚动的方式展示\n   - `#controls`（方向键+暂停）：`flex:0 0 auto`，**固定在右栏底部，始终可见，不参与滚动**\n\n   CSS 骨架模板（必须使用）：\n   ```html\n   <div id=\"app\" style=\"width:100vw;height:100vh;display:flex;flex-direction:row;overflow:hidden;background:#1a1a2e;\">\n     <!-- 左栏：游戏区域，尽量大，canvas 撑满整个左栏，不留空白 -->\n     <div id=\"game-area\" style=\"flex:1;position:relative;min-width:0;\">\n       <canvas id=\"canvas\" style=\"display:block;width:100%;height:100%;\"></canvas>\n       <!-- 开始/结束/暂停 遮罩层：必须用以下写法居中，禁止只写 top:0;left:0 导致内容贴左上角 -->\n       <!-- <div id=\"overlay\" style=\"position:absolute;top:0;left:0;right:0;bottom:0;display:flex;flex-direction:column;\n            align-items:center;justify-content:center;text-align:center;\n            background:rgba(0,0,0,.6);z-index:10;\">\n         <h2 style=\"color:#fff;font-size:clamp(18px,3vw,32px);margin:0 0 2vh;\">游戏标题</h2>\n         <p style=\"color:#ccc;font-size:clamp(12px,1.8vw,18px);margin:0 0 3vh;\">操作说明文字</p>\n         <button style=\"padding:1.2vh 4vw;font-size:clamp(14px,2vw,20px);border-radius:2vmin;\n           border:none;background:#e74c3c;color:#fff;touch-action:manipulation;\">开始游戏</button>\n       </div> -->\n       <!-- ⚠️ 遮罩内所有文字/按钮必须通过 flexbox 居中，严禁使用固定 top/left 像素值定位 -->\n     </div>\n     <!-- 右栏：固定宽度，状态区+方向键始终可见 -->\n     <div id=\"sidebar\" style=\"width:26vw;min-width:180px;max-width:300px;height:100vh;\n          display:flex;flex-direction:column;padding:1.5vh 1.2vw;gap:0.8vh;box-sizing:border-box;overflow:hidden;\">\n       <!-- ① 游戏状态：必展示，不可压缩 -->\n       <div id=\"status\" style=\"flex:0 0 auto;font-size:clamp(10px,1.4vw,14px);\">关卡/得分/生命...</div>\n       <!-- ② 预览区（下一块等）：有预览需求时展示；若有预览区则不放排行榜和操作说明 -->\n       <div id=\"game-preview\" style=\"flex:1 1 0;min-height:0;\">...</div>\n       <!-- ③④ 排行榜+操作说明：仅在无预览区的游戏中使用（与 ② 二选一） -->\n       <!--\n       <div id=\"leaderboard\" style=\"flex:1 1 0;min-height:0;display:flex;flex-direction:column;font-size:clamp(10px,1.3vw,13px);\">\n         <h3 style=\"flex:0 0 auto;margin:0 0 0.4vh;\">排行榜</h3>\n         <div class=\"sb-inner\" style=\"flex:1 1 0;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;\n              scrollbar-width:thin;scrollbar-color:rgba(255,255,255,.3) transparent;\">\n         </div>\n       </div>\n       <div id=\"instructions\" style=\"flex:1 1 0;min-height:0;display:flex;flex-direction:column;font-size:clamp(10px,1.3vw,13px);\">\n         <h3 style=\"flex:0 0 auto;margin:0 0 0.4vh;\">操作说明</h3>\n         <div class=\"sb-inner\" style=\"flex:1 1 0;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;\n              scrollbar-width:thin;scrollbar-color:rgba(255,255,255,.3) transparent;\">\n         </div>\n       </div>\n       -->\n       <!-- ⑤ 方向键+暂停：固定在底部，始终可见 -->\n       <div id=\"controls\" style=\"flex:0 0 auto;padding-top:0.8vh;\">...</div>\n     </div>\n   </div>\n   ```\n   Canvas 尺寸初始化（JS 中必须这样写，确保 Android 8.1 WebView 中 canvas 像素与显示尺寸一致）：\n\n   > ⚠️ **Android 8.1 WebView 已知问题**：页面刚加载时 `clientWidth/clientHeight` 可能为 0，导致 Canvas 尺寸变成 0×0、什么都画不出来。必须用以下模板，通过 `window.load` + 双层 `requestAnimationFrame` 延迟读取尺寸，并用 `offsetWidth` 做兜底。\n\n   Canvas CSS（必须用 `position:absolute` 撑满，不依赖 flex 布局尺寸）：\n   ```html\n   <div id=\"game-area\" style=\"flex:1;position:relative;min-width:0;\">\n     <canvas id=\"canvas\" style=\"position:absolute;top:0;left:0;width:100%;height:100%;display:block;\"></canvas>\n   </div>\n   ```\n\n   Canvas 尺寸初始化（必须在 `window.load` + 双层 `requestAnimationFrame` 后执行）：\n   ```js\n   function initCanvas() {\n     const gameArea = document.getElementById('game-area');\n     const canvas = document.getElementById('canvas');\n     // offsetWidth 兜底：Android 8.1 WebView 刚加载时 clientWidth 可能为 0\n     const w = gameArea.clientWidth  || gameArea.offsetWidth  || window.innerWidth * 0.7;\n     const h = gameArea.clientHeight || gameArea.offsetHeight || window.innerHeight;\n     canvas.width  = w;\n     canvas.height = h;\n     // 格子大小根据 canvas 尺寸动态计算\n     const COLS = 30, ROWS = Math.floor(h / (w / COLS));\n     const cellW = w / COLS, cellH = h / ROWS;\n     // ... 其余游戏初始化逻辑\n   }\n\n   // 必须在 window.load 后、双层 rAF 确保布局渲染完毕再读尺寸\n   window.addEventListener('load', function() {\n     requestAnimationFrame(function() {\n       requestAnimationFrame(function() {\n         initCanvas();\n       });\n     });\n   });\n   ```\n\n   **游戏类 WebView 触摸适配要点**（Android 8.1 WebView 无键盘/鼠标）：\n\n   > ⚠️ **强制要求（主控方式）**：所有游戏的**主要控制方式必须是触摸坐标直接映射**——玩家手指按在画布哪里，角色/目标就跟到哪里（或朝那个方向移动），不需要抬手再点方向按钮。右下角的 ↑↓←→ 方向键和暂停键保留作为**辅助控制**，不是唯一控制入口。暂停按钮必须位于方向键 3×3 grid 的**正中心格**。\n\n   **触摸坐标映射实现模板**（贴在 Canvas/游戏容器上）：\n   ```js\n   // 主控：手指位置直接映射到游戏坐标\n   canvas.addEventListener('touchstart', onTouch, {passive: false});\n   canvas.addEventListener('touchmove',  onTouch, {passive: false});\n\n   function onTouch(e) {\n     e.preventDefault();\n     const rect = canvas.getBoundingClientRect();\n     const touch = e.touches[0];\n     // 将屏幕像素坐标换算为 canvas 内部坐标\n     const cx = (touch.clientX - rect.left) * (canvas.width  / rect.width);\n     const cy = (touch.clientY - rect.top)  * (canvas.height / rect.height);\n     movePlayerTo(cx, cy);   // 替换为游戏实际函数：直接设置目标坐标\n   }\n   ```\n   - 对于贪吃蛇、俄罗斯方块等离散格子类游戏，触摸映射改为**滑动方向**：`touchstart` 记录起点，`touchend` 计算 `deltaX/deltaY`，阈值 20px 内忽略，超过则取绝对值较大的轴作为方向\n   - 对于飞机大战、跑酷等连续坐标类游戏，用 `touchmove` 实时跟随手指坐标\n   - 左侧游戏遮罩/操作说明中必须用一句话说明触摸操作方式，例如：「触摸屏幕控制移动，手指在哪飞机就去哪」\n\n   **飞机大战类游戏专项规则**：\n   - **子弹必须自动连续发射**（`setInterval` 或游戏主循环中每隔固定帧数发射），**禁止要求玩家手动点击射击**\n   - 自动射频简单难度约 300ms/发，中等 200ms/发，困难 100ms/发\n   - 操作说明中写「触摸屏幕移动飞机，子弹自动发射」\n\n   **俄罗斯方块/消除类游戏专项规则**：\n   - **贴地即锁，零等待**：下落计时器触发时若方块无法继续下移，立刻执行 `lock()` → `spawn()`，下一块在同一帧内出现。**禁止 LOCK_DELAY 缓冲期**\n   - **`spawnPiece()` 必须将新方块赋值给全局变量**（如 `cur = shape`），否则 `drawGrid()` 中 `if(cur != null)` 判断为 false 导致方块不显示\n   - **经典 NES 速度标准**：第 1 关 800ms → 第 5 关 533ms → 第 10 关 333ms → 第 15 关 200ms → 第 20 关 100ms → 第 29 关+ 83ms（极限）\n   - 难度递增公式：`interval = Math.max(83, 800 - (level - 1) * 5 * (1000/60))`（每关减约 5 帧，60fps 基准）\n   - 预览区（下一块）在右栏 `#game-preview` 中展示，有预览时不展示排行榜和操作说明\n\n   **方向键 + 暂停按钮模板**（放在右栏 `#controls` 内，作为辅助控制）：\n\n   布局规则：**暂停按钮放在方向键 3×3 grid 的正中心格**，不单独放在键盘上方。整体视觉要炫酷：方向键用半透明玻璃质感，暂停键用蓝色渐变 + 光晕，`:active` 有明显按压反馈。\n\n   ```html\n   <div id=\"controls\" style=\"flex:0 0 auto;display:flex;flex-direction:column;align-items:center;\n        gap:0.8vh;touch-action:none;user-select:none;\">\n     <!-- 3×3 方向键grid，暂停在正中心 -->\n     <div style=\"display:grid;grid-template-columns:repeat(3,13vmin);grid-template-rows:repeat(3,13vmin);gap:1.2vmin;\">\n       <div></div>\n       <button class=\"dpad-btn\" id=\"btn-up\">▲</button>\n       <div></div>\n       <button class=\"dpad-btn\" id=\"btn-left\">◀</button>\n       <button class=\"dpad-btn\" id=\"btn-pause\">II</button>\n       <button class=\"dpad-btn\" id=\"btn-right\">▶</button>\n       <div></div>\n       <button class=\"dpad-btn\" id=\"btn-down\">▼</button>\n       <div></div>\n     </div>\n   </div>\n   <style>\n   /* 方向键：半透明玻璃质感 */\n   .dpad-btn {\n     background: linear-gradient(145deg, rgba(255,255,255,.22), rgba(255,255,255,.06));\n     border: 1.5px solid rgba(255,255,255,.35);\n     border-radius: 50%;\n     color: #fff;\n     font-size: 4.5vmin;\n     touch-action: manipulation;\n     display: flex;\n     align-items: center;\n     justify-content: center;\n     box-shadow: 0 2px 8px rgba(0,0,0,.4), inset 0 1px 0 rgba(255,255,255,.25);\n     cursor: pointer;\n     transition: transform .08s;\n   }\n   .dpad-btn:active {\n     background: linear-gradient(145deg, rgba(255,255,255,.45), rgba(255,255,255,.15));\n     transform: scale(.88);\n     box-shadow: 0 1px 4px rgba(0,0,0,.5), inset 0 1px 0 rgba(255,255,255,.3);\n   }\n   /* 暂停键与方向键样式完全一致，仅字号加粗 + 细边框区分；用 II 代替 ⏸ 符号（WebView 渲染兼容） */\n   #btn-pause {\n     font-size: 4.5vmin;\n     font-weight: bold;\n     border-color: rgba(255,255,255,.6);\n   }\n   </style>\n   <script>\n   document.getElementById('btn-up').addEventListener('touchstart',    e=>{e.preventDefault();changeDir(0,-1);},{passive:false});\n   document.getElementById('btn-down').addEventListener('touchstart',   e=>{e.preventDefault();changeDir(0, 1);},{passive:false});\n   document.getElementById('btn-left').addEventListener('touchstart',   e=>{e.preventDefault();changeDir(-1,0);},{passive:false});\n   document.getElementById('btn-right').addEventListener('touchstart',  e=>{e.preventDefault();changeDir( 1,0);},{passive:false});\n   document.getElementById('btn-pause').addEventListener('touchstart',  e=>{e.preventDefault();togglePause();},{passive:false});\n   // 桌面调试保留键盘\n   document.addEventListener('keydown', e=>{\n     if(e.key==='ArrowUp')    changeDir(0,-1);\n     if(e.key==='ArrowDown')  changeDir(0, 1);\n     if(e.key==='ArrowLeft')  changeDir(-1,0);\n     if(e.key==='ArrowRight') changeDir( 1,0);\n     if(e.key===' ')          togglePause();\n   });\n   </script>\n   ```\n   - `changeDir(dx, dy)` 和 `togglePause()` 替换为游戏实际函数名；坐标映射类游戏中方向键可改为触发\"持续向该方向移动\"逻辑\n   - 按钮尺寸用 `vmin` 单位，960px 小屏到 2560px 大屏均可用\n   - 暂停键必须在中心格，禁止移到键盘外部单独放置\n\n   **游戏角色视觉规范**（卡通可爱风格，禁止只画纯色圆点/方块）：\n   - 贪吃蛇：蛇头用圆角矩形 + 两个白色眼睛（小圆 + 黑色瞳孔）+ 根据朝向旋转；蛇身用渐变色圆角矩形，节节之间颜色略有变化；食物用苹果/草莓等图形（红色圆 + 绿色小叶子）\n   - 贪吃蛇蛇头绘制示例（Canvas 2D）：\n     ```js\n     function drawHead(ctx, x, y, w, h, dir) {\n       ctx.save();\n       ctx.translate(x + w/2, y + h/2);\n       const angle = {right:0, down:Math.PI/2, left:Math.PI, up:-Math.PI/2}[dir] || 0;\n       ctx.rotate(angle);\n       // 头部圆角矩形\n       ctx.fillStyle = '#2ecc71';\n       roundRect(ctx, -w/2, -h/2, w, h, w*0.3);\n       ctx.fill();\n       // 眼睛\n       [[-w*0.15, -h*0.15],[w*0.15, -h*0.15]].forEach(([ex,ey])=>{\n         ctx.fillStyle='#fff'; ctx.beginPath(); ctx.arc(ex,ey,w*0.12,0,Math.PI*2); ctx.fill();\n         ctx.fillStyle='#222'; ctx.beginPath(); ctx.arc(ex+w*0.03,ey,w*0.06,0,Math.PI*2); ctx.fill();\n       });\n       ctx.restore();\n     }\n     function roundRect(ctx,x,y,w,h,r){\n       ctx.beginPath();ctx.moveTo(x+r,y);ctx.lineTo(x+w-r,y);ctx.arcTo(x+w,y,x+w,y+r,r);\n       ctx.lineTo(x+w,y+h-r);ctx.arcTo(x+w,y+h,x+w-r,y+h,r);ctx.lineTo(x+r,y+h);\n       ctx.arcTo(x,y+h,x,y+h-r,r);ctx.lineTo(x,y+r);ctx.arcTo(x,y,x+r,y,r);ctx.closePath();\n     }\n     ```\n   - 俄罗斯方块：方块用渐变色 + 高光边框，不同形状用不同颜色\n   - 飞机大战：飞机用多边形或 SVG path 绘制，子弹用细长椭圆，敌机颜色与玩家区分；**子弹必须自动连续发射，禁止要求玩家手动点击射击**；操作说明写「触摸屏幕移动飞机，子弹自动发射」\n   - 通用原则：所有游戏角色必须有辨识度，能看出是什么，不能只是纯色几何形状\n\n   **平台跳跃 / 跑酷类游戏触摸控制规范**（马里奥、跑酷等左右移动+跳跃类游戏必须遵守）：\n\n   > 核心设计：左右半屏控制方向，上滑触发跳跃，斜滑（如右上方）同时触发移动+跳跃，完全模拟实体手柄的拇指操作。\n\n   ```js\n   let tSX = 0, tSY = 0; // 触摸起点，每次 touchstart 或跳跃触发后重置\n\n   canvas.addEventListener('touchstart', e => {\n     e.preventDefault();\n     const t = e.touches[0];\n     tSX = t.clientX; tSY = t.clientY;\n   }, {passive: false});\n\n   canvas.addEventListener('touchmove', e => {\n     e.preventDefault();\n     const t = e.touches[0];\n     const dx = t.clientX - tSX;\n     const dy = t.clientY - tSY; // 负值 = 向上\n\n     // 左右移动：根据手指当前位置在画布左/右半区判断方向\n     const rect = canvas.getBoundingClientRect();\n     if (t.clientX < rect.left + rect.width / 2) {\n       moveLeft();\n     } else {\n       moveRight();\n     }\n\n     // 上滑检测跳跃：阈值 20px，触发后立即重置 tSY 防止重复触发\n     if (dy < -20) {\n       jump();\n       tSY = t.clientY; // 重置参考点，允许同一次触摸连续触发（斜滑等）\n     }\n   }, {passive: false});\n\n   canvas.addEventListener('touchend', e => {\n     stopMove(); // 手指抬起停止移动\n   }, {passive: false});\n   ```\n   - `dy < -20`（负值向上）触发跳跃，同一次 `touchmove` 序列可多次触发（重置 tSY）\n   - 斜向上滑（如右上方）会同时执行 `moveRight()` + `jump()`，天然支持斜跳\n   - 阈值 20px 优于 28px，更灵敏，适合快节奏横板游戏\n   - 右栏方向键：◀▶ 控制移动，▲ 触发跳跃，▼ 可用于下蹲/快速下落\n   - 触摸坐标映射为主控，方向键为辅助——两套控制并行，不互斥\n   - 防止页面滚动干扰游戏：游戏容器加 `touch-action:none`，Canvas 加 `{ passive: false }` 并 `e.preventDefault()`\n   - 游戏暂停/重启按钮触摸区域 ≥ 44×44px\n   - 禁止在游戏主循环中使用 `alert()`，改用页面内自定义弹窗显示胜负信息\n\n   **跳跃 / 平台类游戏角色定位规范**（跳一跳、跑酷、马里奥等必须遵守）：\n\n   > ⚠️ **已知 Bug 根因**：用 `ph = PLAYER_H / squish` 作为绘制高度来定位头部/身体，当落地弹跳动画触发 `squish ≠ 1` 时，`ph` 变大，角色视觉上\"沉入\"平台下方。\n\n   **正确做法**：`py` 始终表示角色**脚底**的 y 坐标（即站在平台顶面），`squish` 只影响**宽度**（身体变宽/变窄），绝不影响高度定位：\n   ```js\n   // py = 脚底 y（始终贴平台顶面，不受 squish 影响）\n   // squish: 落地压缩系数，1=正常，>1=横向压扁（宽变大、高不变）\n   function drawPlayer(ctx, px, py, squish) {\n     const W = PLAYER_W * squish;   // 宽度随 squish 变化\n     const H = PLAYER_H;            // 高度固定，不除以 squish\n     const x = px - W / 2;\n     const y = py - H;              // 脚底 py 往上量固定 PLAYER_H\n     // 绘制身体：rr(ctx, x, y, W, H, radius)\n   }\n   ```\n   - `squish` 动画只修改 `W`（或同时等比缩小 `H` 但必须同步上移 `y = py - H`），确保脚底始终在 `py`\n   - 禁止写 `y = py - PLAYER_H / squish`，这会在 squish > 1 时让角色上移，squish < 1 时下沉\n\n\n\n   > ⚠️ **强制要求**：所有游戏类应用必须内置难度阶段机制，**默认从简单难度开始**，通过 10 关后自动升级为中等难度，通过 20 关后升级为困难。以飞机大战为参照基准，其他游戏按比例换算。\n\n   **难度分级标准**（以飞机大战为基准示例）：\n   | 阶段 | 关卡范围 | 敌机坠落速度 | 子弹发射间隔 | 过关目标分 | 每关目标分增量 |\n   |------|---------|------------|------------|---------|------------|\n   | 简单（Easy）  | 第 1~10 关  | 基础速度 × 1/2 | 300ms/发 | 100 分 | +10 分/关 |\n   | 中等（Medium）| 第 11~20 关 | 基础速度 × 80% | 200ms/发 | 200 分 | +10 分/关 |\n   | 困难（Hard）  | 第 21 关起  | 基础速度  | 100ms/发 | 300 分 | +10 分/关 |\n\n   - **难度参数必须用变量控制**，根据关卡号计算，禁止魔法数字硬编码：\n     ```js\n     let level = 1; // 每通关 +1\n\n     function getDifficulty(level) {\n       if (level <= 10) return {\n         speedMul: 1/2,  bulletInterval: 300,\n         targetScore: 100 + (level - 1) * 10   // 100, 110, 120...\n       };\n       if (level <= 20) return {\n         speedMul: 0.8,  bulletInterval: 200,\n         targetScore: 200 + (level - 11) * 10  // 200, 210, 220...\n       };\n       return {\n         speedMul: 1,  bulletInterval: 100,\n         targetScore: 300 + (level - 21) * 10  // 300, 310, 320...\n       };\n     }\n     ```\n   - **不设硬性关卡上限**，进入困难阶段后目标分持续增加，让玩家可以持续挑战\n   - 关卡信息（当前关卡、当前难度名称、得分 / 目标分、生命）必须在右栏顶部实时显示\n\n   **文档/Word/PDF 风格**：\n   - 容器使用响应式宽度：`max-width: min(1200px, 92vw); margin: 3vh auto; padding: 4vh 5vw`\n   - 在 960px 小屏上内容几乎满宽，在 1920px+ 大屏上居中且不超过 1200px\n   - 正文字号 `clamp(14px, 1.8vw, 18px)`，标题 `clamp(20px, 3vw, 32px)`\n   - 清晰的标题/段落/列表排版，使用衬线或无衬线字体\n   - 页面顶部可显示文档标题和日期\n\n   **PPT/演示文稿风格**：\n   - 引入 reveal.js：`<link rel=\"stylesheet\" href=\"https://unpkg.com/reveal.js@5/dist/reveal.css\">`\n   - 引入主题：`<link rel=\"stylesheet\" href=\"https://unpkg.com/reveal.js@5/dist/theme/white.css\">`\n   - 引入 JS：`<script src=\"https://unpkg.com/reveal.js@5/dist/reveal.js\"></script>`\n   - 内容用 `<div class=\"reveal\"><div class=\"slides\"><section>...</section></div></div>` 包裹\n   - 每个 `<section>` 是一页幻灯片\n   - 初始化：`<script>Reveal.initialize({hash:true, touch:true, width:960, height:600});</script>`\n     reveal.js 内部会等比缩放到实际视口，960×600 基准保证所有设备文字清晰不溢出\n   - 5~15 页为宜\n\n   **Excel/表格风格**：\n   - 容器 `width:100vw; height:100vh; overflow:auto; box-sizing:border-box; padding:2vh 3vw`\n   - HTML `<table>` 宽度 100%，带 border 和交替行色\n   - 表头字号 `clamp(12px, 1.6vw, 16px)`，单元格字号 `clamp(12px, 1.4vw, 15px)`\n   - 表头点击可排序（内联 JS 实现）\n   - 单元格 `contenteditable=\"true\"` 可编辑\n\n   **所有类型通用禁止项**：\n   - 禁止出现\"导出\"、\"打印\"、\"保存\"功能按钮\n   - 禁止 `@media print` 相关样式\n   - 禁止 `window.print()`、文件下载等功能\n\n4. 图标生成（icon_base64）：\n   - 产出一个 256×256 的 SVG 图标\n   - 风格：圆角矩形纯色背景 + 居中的主题图形（用 SVG path/circle/rect 绘制，**禁止使用 `<text>` 或 emoji**）\n   - 背景用饱和度高的纯色，中间图形用与背景对比强烈的颜色（可用白色、也可用多色彩搭配，参考 App Store 图标风格）\n   - SVG 文本做 base64 编码后传入 icon_base64（不带 `data:image/svg+xml;base64,` 前缀）\n   - SVG 原文不超过 3KB，**禁止使用 `<text>` 元素或 emoji**\n\n5. **【必须执行】HTML 自检 — 提交前逐项检查**：\n   生成 html_content 后，**必须**在脑内逐项检查以下条件，任一不通过则必须修改代码后再提交：\n\n   **自检项 A — 禁止 AI/电脑对手**（仅游戏/棋类/对战类需检查）：\n   - [ ] 代码中是否存在任何\"电脑走棋/出牌/移动\"的函数？（如 `computerMove`, `aiMove`, `makeAIMove`, `botPlay`, `autoPlay`, `cpuTurn`）→ 若有，**删除整个函数及其调用**\n   - [ ] 代码中是否存在 `minimax`, `alphaBeta`, `evaluate`, `bestMove`, `getRandomMove`, `Math.random()` 用于选择落子位置？→ 若有，**删除**\n   - [ ] 代码中是否存在\"难度选择\"（easy/medium/hard）或\"人机/人人\"模式切换？→ 若有，**删除，只保留双人模式**\n   - [ ] 游戏流程是否为：玩家A操作 → 切换到玩家B → 玩家B操作 → 切换回玩家A → 循环？→ 必须是\n   - [ ] 是否有任何 `setTimeout`/`setInterval` 用于延迟执行电脑的操作？→ 若有，**删除**\n\n   **自检项 B — 禁止导出/打印/保存**（所有类型需检查）：\n   - [ ] 是否存在\"导出\"、\"打印\"、\"保存\"、\"下载\"按钮或菜单项？→ 若有，**删除**\n   - [ ] 是否存在 `window.print()`, `document.execCommand('SaveAs')`, `URL.createObjectURL`, `download` 属性？→ 若有，**删除**\n\n   **自检项 C — Android 8.1 WebView 兼容**（所有类型需检查）：\n   - [ ] 是否使用了 Chrome 86+ 才支持的 CSS/JS API（`@property`、`content-visibility`、`structuredClone`、`??=`/`||=`/`&&=` 赋值运算符）？→ 若有，**替换为兼容写法**\n   - [ ] 是否使用了 `ctx.roundRect()`？→ Chrome 99+，**必须替换为自定义 `rr()` helper（用 arcTo 实现）**\n   - [ ] Canvas 内是否使用了 emoji 字符（fillText 中含 🔥❤️ 等）？→ WebView 字体渲染异常，**必须替换为纯文字或几何图形**\n   - [ ] 游戏布局是否为横版左右两栏（左栏游戏区、右栏控制区）？→ **若是竖版堆叠，必须改为横版**\n   - [ ] 右栏有 `#game-preview`（下一块/预览区）时，是否已去掉 `#leaderboard` 和 `#instructions`？→ **预览区与排行/说明互斥，有预览时不展示排行和说明**。无预览区的游戏中，排行/说明用 `flex:1 1 0;min-height:0` + h3 固定 + `.sb-inner` 内滚动。`#controls` 始终 `flex:0 0 auto` 固定底部\n   - [ ] Canvas CSS 是否用 `position:absolute;top:0;left:0;width:100%;height:100%` 撑满左栏，而非依赖 flex 布局？→ **若只用 `width:100%;height:100%` 而无 `position:absolute`，必须修正**\n   - [ ] Canvas 尺寸初始化是否在 `window.load` + 双层 `requestAnimationFrame` 内执行，且用 `offsetWidth` 做兜底（防止 Android 8.1 WebView 初始 `clientWidth` 为 0）？→ **若直接在顶层读 `clientWidth`，必须改为延迟初始化模板**\n   - [ ] 需要方向控制的游戏：是否在 Canvas 上绑定了 `touchstart`/`touchmove` 做坐标直接映射（主控），且右下角仍保留方向键 + 暂停按钮（辅助）？→ **若只有方向按钮而无触摸坐标映射，必须补充**\n   - [ ] 飞机大战类游戏：子弹是否自动连续发射（`setInterval` 或主循环计时），而非要求玩家手动点击射击？→ **若需要手动触发，必须改为自动发射**\n   - [ ] 俄罗斯方块/消除类游戏：①初始下落间隔是否为800ms（经典NES速度）？②贴地后是否立刻lock+spawn（零等待，无LOCK_DELAY缓冲）？③`spawnPiece()`是否将新方块赋值给全局变量（`cur=shape`）？→ **若不符合，按专项规则修正**\n   - [ ] 游戏内的开始/结束/暂停遮罩层是否用 `position:absolute;top:0;left:0;right:0;bottom:0;display:flex;align-items:center;justify-content:center` 居中（**禁止用 `inset:0`，Chrome 85 不支持**）？→ **若用 `inset:0` 或固定像素定位，内容会在智能屏上贴左上角，必须修正**\n   - [ ] 游戏是否实现了三段式难度（简单1~10关：速度×1/2、300ms/发、100+10×n分 → 中等11~20关：速度×80%、200ms/发、200+10×n分 → 困难21关起：基础速度、100ms/发、300+10×n分），且第1关使用简单参数？→ **若全程同一难度或开局就很快，必须按难度分级模板修正**\n   - [ ] 游戏角色是否有视觉辨识度（蛇头有眼睛、食物有形状等），而不是纯色圆点/方块？→ **若只是纯色几何形，必须改为卡通风格**\n   - [ ] 是否使用了原生 `alert()`/`confirm()`/`prompt()`？→ 若有，**改为页面内自定义弹窗**\n   - [ ] 游戏容器/Canvas 是否加了 `touch-action:none` 并在 touch 事件中调用 `e.preventDefault()`？→ 若未处理，**补充**\n   - [ ] 可点击/可触摸元素的触摸区域是否 ≥ 44×44px？→ 若不足，**调整尺寸**\n   - [ ] 跳跃/平台类游戏（跳一跳、跑酷等）：绘制角色时是否用固定常量 `PLAYER_H` 定位脚底坐标（`py` 为脚底 y，头顶为 `py - PLAYER_H`），而非用 `PLAYER_H / squish` 等变形高度定位？→ **`squish`（落地压缩动画）只能影响角色宽度/变窄，绝不能影响脚底 `py` 的计算，否则 squish≠1 时角色视觉上会\"沉入\"平台下方**\n\n   ⚠️ 如果自检发现违规代码，**不要提交**，先修正 html_content 再继续下一步。\n\n5.5. **【必须执行】JS 语法验证**（应用类/游戏类必须，文件类可选）：\n   在调用 `myapp_register` 之前，用 `exec` 工具对 html_content 中的 `<script>` 代码块做语法验证，确保不因语法错误导致游戏在智能屏上完全空白：\n   ```bash\n   # 提取 <script> 内容写临时文件，用 node --check 验证\n   node -e \"\n   const fs = require('fs');\n   const html = fs.readFileSync('/tmp/_skill_check.html','utf8');\n   const scripts = html.match(/<script[\\s\\S]*?>([\\s\\S]*?)<\\/script>/gi) || [];\n   scripts.forEach((s,i) => {\n     const code = s.replace(/<\\/?script[^>]*>/gi,'');\n     fs.writeFileSync('/tmp/_check_'+i+'.js', code);\n   });\n   \" && node --check /tmp/_check_*.js 2>&1\n   ```\n   - 实际操作：将 html_content 写入 `/tmp/_skill_check.html`，运行上面命令\n   - 如果 `node --check` 输出任何错误，必须修复对应 JS 代码后重新验证，直到无错误\n   - 验证通过后再调用 `myapp_register`\n\n6. 调用 `myapp_register` 工具：\n   - 入参：`dumi_id, cuid, query, app_name, html_content, features, icon_base64, tag`\n   - `tag`：根据第 1 步判断的类型填写——**应用类（交互式）填 `\"app\"`，文件类（文档/PPT/表格）填 `\"doc\"`**；不传时服务端默认 `\"app\"`\n   - `dumi_id` 与 `cuid` 从对话上下文中获取（OpenClaw 会注入）\n\n7. 工具返回成功后，向用户展示：\n   - 告知创建完成：\"《<app_name>》已创建完成。\"\n   - 展示功能/玩法（根据类型区分）：\n     - **应用类**：\n       - 列出\"游戏特性\"或\"功能特性\"：从 features 中提取 3~6 条核心点\n       - 列出\"操作方式\"：说明触屏/键盘的操作方法（如方向键控制、暂停等）\n     - **文件类**（文档/PPT/表格）：\n       - 简要概括文档核心内容（如关键数据、主要结论、章节概要等，3~5 条）\n       - 提示\"点击链接即可查看完整内容\"\n   - 展示文件位置：给出返回的 `html_url` 链接\n   - 提示用户：\"下次可以在「我的创作」页面查看和管理你创建的所有应用\"\n\n8. 失败/超长/超出能力 → 回复\"应用创建失败，请稍后重试\"\n   - **不要重试**，**不要伪造成功**，**不要把 html_content 输出到对话**\n\n## 更新流程\n\n最长等待 300s。若 300s 内不能完成旧功能读取、HTML 重新生成与 `myapp_update` 调用，直接回复\"应用更新失败，请稍后重试\"。\n\n1. **先调 `myapp_get(app_id)`** 拿到 `app_name` 与旧 `features`、旧 `query`\n2. 把\"旧 features + 用户新需求\"作为完整上下文，重新生成：\n   - 新 features（合并增删，仍是 1~12 条全集，不是 diff）\n   - 新 html_content（保持原有类型风格）\n   - 若应用主题/类型发生变化，重新生成 icon_base64（同创建流程第 4 步）\n3. 调用 `myapp_update(app_id, query, html_content, features, icon_base64, tag)`\n   - `tag` 同创建流程：应用类填 `\"app\"`，文件类填 `\"doc\"`\n4. 工具返回成功后，向用户展示：\n   - 告知更新完成：\"《<app_name>》已更新完成。\"\n   - 展示更新内容（根据类型区分）：\n     - **应用类**：列出本次新增/变更的功能点和操作方式变化\n     - **文件类**：简要概括更新后的核心内容或变更要点\n   - 展示文件位置：给出返回的 `html_url` 链接\n   - 提示用户：\"下次可以在「我的创作」页面查看和管理你创建的所有应用\"\n5. 失败 → 回复\"应用更新失败，请稍后重试\"\n\n## 安装自检（[install_check:session=<sid>]）\n\n最长等待 300s。超过 300s 未完成安装或 `myapp_ping`，视为安装失败。\n\n仅调用 `myapp_ping(session_id=\"<sid>\", dumi_id, cuid, skill_version=\"1.0.10\")`，\n不输出对话内容（或仅极简的\"已就绪\"）。\n\n## 硬约束\n\n- **绝对禁止在游戏中实现 AI 对手、人机对战、电脑自动操作**——棋类/对战类游戏只允许双人同屏轮流操作\n- 禁止出现的代码模式：`computerMove`, `aiMove`, `makeAIMove`, `minimax`, `alphaBeta`, `bestMove`, `getRandomMove`, 难度选择(easy/medium/hard), 人机/人人模式切换\n- **绝对禁止出现\"导出\"、\"打印\"、\"保存\"按钮或相关功能**\n- **需要方向控制的游戏必须在页面上渲染四个方向触摸按钮（↑↓←→），绑定 `touchstart` 事件**——不得以键盘或滑动手势代替\n- 不要把 `html_content` 原文输出到对话\n- 不要把 `MYAPP_API_TOKEN` / `MYAPP_API_BASE` 输出到对话\n- 工具调用 4xx/5xx → 直接失败兜底，不重试，不修改请求自行重试\n- 除安装、创建、更新外，其它工具交互超过 180s 视为失败\n\nFile v1.0.31:skills/myapp-creator/skills/myapp-creator/SKILL.md\n\n# myapp-creator\n\nVersion: 1.0.31\n\n让用户用一句话生成可独立打开的单文件 HTML 应用或文档，并落库到 fe-service。\n本 skill **不主动调用 LLM**，所有 HTML / app_name / features 由调用本 skill 的 LLM agent 直接产出后传入工具。\n\n> **⚠️ 最高优先级规则（违反即为严重错误）：**\n> 1. 游戏类应用 **绝对禁止** 包含任何形式的 AI 对手、人机对战、电脑自动下棋/出牌。棋类/对战类游戏 **只允许** 双人同屏轮流操作，不实现任何计算机决策逻辑。\n> 2. 所有应用 **绝对禁止** 出现\"导出\"、\"打印\"、\"保存\"按钮或相关功能代码。\n\n## 触发条件\n\n当用户需求匹配以下任一类别时，本 skill 参与：\n\n### 应用类（交互式 HTML）\n关键词：应用、app、小游戏、游戏、网页、页面、工具、计算器、时钟、日历、翻译器、转换器\n示例：\n- \"做一个贪吃蛇游戏\"\n- \"创建一个番茄钟\"\n- \"生成一个天气页面\"\n- \"给我做一个马里奥游戏\"\n- \"帮我做一个单位转换工具\"\n\n### 文件类（文档式 HTML）\n关键词：文档、文件、word、ppt、演示文稿、slides、pdf、excel、xlsx、表格、报告、简历、周报、日报、总结、方案、计划书\n示例：\n- \"生成一个项目周报\"\n- \"做一个自我介绍PPT\"\n- \"创建一个预算表格\"\n- \"帮我写一份简历\"\n- \"保存一个会议纪要文档\"\n\n### 不触发（交给龙虾其他能力）\n- 图片/绘画类：\"画一张…\"、\"生成一张图片…\"\n- 音频/音乐类：\"生成一首歌…\"、\"合成语音…\"\n- 视频类：\"剪辑一个…\"、\"生成视频…\"\n- 小说/纯文本类：\"写一篇小说…\"、\"写一首诗…\"（纯创作不产出可交互文件）\n- 纯问答/聊天/闲聊\n\n### 判断规则\n用户意图是\"产出一个可独立打开、保存下来反复使用的文件或应用\"时触发本 skill。\n如果用户只是想让你\"说一段话\"或\"回答问题\"，不要触发。\n\n## 触发标记（兼容旧入口）\n\n以下前缀仍可直接触发，不需要语义判断：\n- `[create_app]<描述>` → 创建新应用/文件\n- `[update_app:app_id=<n>]<描述>` → 更新已有应用/文件\n- `[install_check:session=<sid>]` → 安装自检（仅调 myapp_ping）\n- 不带上述前缀 → 按语义判断是否匹配触发条件\n\n## 创建流程\n\n最长等待 300s。若 300s 内不能完成 HTML 生成与 `myapp_register` 调用，直接回复\"应用创建失败，请稍后重试\"。\n\n1. 解析用户描述，判断类型（应用类 or 文件类），产出：\n   - `app_name`：应用/文件名，≤30 字\n   - `features`：JSON 字符串数组，3~8 条，每条 ≤30 字，描述具体功能/内容要点\n   - `html_content`：完整可独立打开的单文件 HTML（按类型选择模板风格）\n\n2. HTML 通用约束：\n   - 自包含：CSS/JS 全部内联在 `<style>` / `<script>`\n   - 不依赖本地资源\n   - 外部资源仅允许：unpkg.com / jsdelivr.net / cdnjs.cloudflare.com\n   - 总长度 ≤ 60KB（UTF-8 字节数）\n   - 不引用任何小度专属 API\n   - 必须包含 `<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">`\n\n   **目标运行环境 — Android 8.1 WebView（Chrome/85）**：\n   所有 HTML 最终运行在搭载 Android 8.1 的智能屏 WebView 中，无鼠标/硬键盘，仅支持触摸和左右滑动。\n   生成代码时必须遵守以下兼容规则：\n\n   **JS / Web API 兼容**（feature detection 优先，禁止仅凭 UA 判断）：\n   - 使用 `typeof IntersectionObserver !== 'undefined'` 等特性检测，不要直接调用可能不存在的 API\n   - 禁止使用 Chrome 86+ 才支持的 API：`CSS.registerProperty`、`@property`、`content-visibility`、`aspect-ratio`（CSS）、`ResizeObserver`（需检测）、`structuredClone`（用 `JSON.parse(JSON.stringify())` 替代）\n   - 禁止使用 ES2020+ 语法：`??=`、`||=`、`&&=`、`import()`动态导入、`BigInt`、`globalThis`（用 `window` 替代）\n   - **禁止使用 `ctx.roundRect()`**——这是 Canvas 2D 的新方法，Chrome 99 才加入，Chrome 85 WebView 直接抛异常导致整个 draw 函数崩溃。必须用以下自定义 helper 替代：\n     ```js\n     function rr(ctx, x, y, w, h, r) {\n       ctx.beginPath();\n       ctx.moveTo(x + r, y);\n       ctx.lineTo(x + w - r, y);  ctx.arcTo(x + w, y,     x + w, y + r,     r);\n       ctx.lineTo(x + w, y + h - r); ctx.arcTo(x + w, y + h, x + w - r, y + h, r);\n       ctx.lineTo(x + r, y + h);  ctx.arcTo(x,     y + h, x,     y + h - r, r);\n       ctx.lineTo(x, y + r);      ctx.arcTo(x,     y,     x + r, y,         r);\n       ctx.closePath();\n     }\n     // 用法：rr(ctx, x, y, w, h, radius); ctx.fill();\n     ```\n   - **禁止在 Canvas 内用 emoji 字符**（如 🌕🔥❤️ 等）——Android WebView 字体渲染异常会抛异常或显示方块。Canvas 内所有文字必须用纯 ASCII/中文文字，用几何图形代替 emoji（如\"♥ × 3\"改用红色圆形绘制）\n   - 可安全使用：`Promise`、`async/await`、`fetch`、`CSS Grid`、`Flexbox`、`CSS Variables`、`IntersectionObserver`（需检测）、`requestAnimationFrame`\n   - `localStorage` / `sessionStorage` 在 WebView 中可用，但不要依赖跨页面持久化\n\n   **CSS 兼容**：\n   - `100vh` 在 Android WebView 中等于视口高度（无地址栏问题），可直接使用\n   - `position: sticky` 在 Chrome 85 中支持，但父容器不能有 `overflow: hidden`，否则失效\n   - `position: fixed` 在 WebView 中软键盘弹出时可能错位，避免在输入框场景依赖 fixed 定位\n   - **禁止使用 CSS `inset` 属性**（`inset: 0` 等简写）——Chrome 87 才支持，Chrome 85 不识别，会导致遮罩层无法撑满、内容偏移到左上角。替换写法：`top:0; left:0; right:0; bottom:0`（注意：`box-shadow` 中的 `inset` 关键字不受此限制，可以正常使用）\n   - 禁止使用 `backdrop-filter`（Chrome 85 需前缀，WebView 不保证支持）\n   - `clamp()` 在 Chrome 85 中已支持，可正常使用\n   - 滚动容器加 `-webkit-overflow-scrolling: touch` 保证惯性滚动\n\n   **触摸与交互**：\n   - 所有可点击元素最小触摸区域 ≥ 44×44px\n   - 使用 `touchstart`/`touchmove`/`touchend` 处理手势；`click` 在 WebView 中有 300ms 延迟，交互敏感场景改用 touch 事件\n   - 消除 300ms 延迟：在 `<meta name=\"viewport\">` 中加 `user-scalable=no` 或在 CSS 中加 `touch-action: manipulation`\n   - 禁止依赖 `hover` 状态（无鼠标），交互反馈改用 `:active` 或 JS touch 事件\n   - 左右滑动手势用 `touchstart`/`touchend` 计算 `deltaX` 实现，不要用第三方手势库（CDN 可能不可达）\n   - 游戏类应用禁止使用键盘事件（`keydown`/`keyup`）作为唯一控制方式，必须提供触摸按钮\n\n   **软键盘与输入框**：\n   - 输入框获焦时 WebView 会 resize 视口，避免用 `height: 100vh` 做全屏布局的唯一约束\n   - 输入框弹出软键盘后 `fixed` 元素可能被遮挡，底部操作栏改用 `position: sticky` 或监听 `visualViewport.resize`\n   - 如需监听软键盘：`window.visualViewport` 在 Chrome 61+ 可用，Chrome 85 支持\n\n   **弹窗 / Modal**：\n   - 自定义弹窗用 `position: fixed; top:0; left:0; width:100%; height:100%` 覆盖全屏\n   - 弹窗内滚动区域加 `overflow-y: auto; -webkit-overflow-scrolling: touch`\n   - 禁止使用原生 `alert()`/`confirm()`/`prompt()`（WebView 中可能被宿主拦截）\n\n   - **横屏多设备适配**：目标设备均为横屏展示，尺寸差异较大：\n     | 设备 | 视口宽×高比例 |\n     |------|-----------|\n     | 普通智能屏 | 960×600 |\n     | mini智能屏 | 960×480 |\n     | 闺蜜机 | 1280×720 |\n     | 秋月 | 1280×800 |\n     | pad | 1920×1080 |\n     | pad17 | 2560×1600 |\n     布局以横屏为前提（宽 > 高），使用 `vw`/`vh`/百分比等响应式单位，禁止固定像素宽度导致小屏溢出或大屏留白。\n     字体大小建议用 `clamp()` 适配，如 `font-size: clamp(14px, 2vw, 18px)`。\n\n3. HTML 模板风格（按类型选择）：\n\n   **应用类** — 交互式：\n   - 纯单机离线游戏/工具，不依赖任何 server 服务\n   - **严禁实现 AI 对手 / 人机对战 / 电脑自动下棋等功能**，棋类游戏只做双人同屏轮流对战（两个玩家在同一屏幕上交替操作）\n   - 棋类游戏正确做法示例：五子棋 → 黑白双方轮流点击落子，判断五连胜负，无任何电脑决策代码；象棋 → 红黑双方轮流移动棋子\n   - 功能简单易上手，玩法直观，适合触屏快速操作\n   - 支持触屏操作（touch 事件）\n   - 美观的 UI 设计\n   - 根容器使用 `width:100vw; height:100vh; overflow:hidden` 撑满全屏\n   - 关键元素尺寸用 `vmin` 或百分比，确保 960px 小屏到 2560px 大屏均可用\n\n   **游戏类强制横版左右布局**（所有游戏类应用必须遵守）：\n\n   > ⚠️ **强制要求**：所有游戏页面必须是横版布局，整体分为左右两栏，**禁止竖版堆叠布局**。\n\n   布局结构如下：\n   ```\n   ┌─────────────────────────────┬──────────────────┐\n   │                             │  状态（必展示）    │\n   │                             │  关卡/得分/生命    │\n   │       游戏区域（左栏）        ├──────────────────┤\n   │    占总宽约 65%~70%          │  下一块/预览      │\n   │    高度撑满 100vh            ├──────────────────┤\n   │                             │ ◀ II ▶  方向键   │\n   └─────────────────────────────┴──────────────────┘\n   ```\n\n   **右栏布局规则（状态区和方向键始终可见）**：\n   - `#status`（关卡、难度、得分/目标分、生命）：`flex:0 0 auto`，**必须展示，不可压缩**\n   - `#game-preview`（下一块/预览区）：`flex:0 0 auto`，**有预览需求的游戏必须展示，不可压缩**\n   - `#leaderboard`（排行榜）和 `#instructions`（操作说明）：**当游戏有 `#game-preview` 时，不展示排行榜和操作说明**（右栏空间有限，预览区优先）。仅在无预览需求的游戏中，才用 `flex:1 1 0; min-height:0` + 标题 h3 固定 + `.sb-inner` 内容区 `overflow-y:auto` 独立滚动的方式展示\n   - `#controls`（方向键+暂停）：`flex:0 0 auto`，**固定在右栏底部，始终可见，不参与滚动**\n\n   CSS 骨架模板（必须使用）：\n   ```html\n   <div id=\"app\" style=\"width:100vw;height:100vh;display:flex;flex-direction:row;overflow:hidden;background:#1a1a2e;\">\n     <!-- 左栏：游戏区域，尽量大，canvas 撑满整个左栏，不留空白 -->\n     <div id=\"game-area\" style=\"flex:1;position:relative;min-width:0;\">\n       <canvas id=\"canvas\" style=\"display:block;width:100%;height:100%;\"></canvas>\n       <!-- 开始/结束/暂停 遮罩层：必须用以下写法居中，禁止只写 top:0;left:0 导致内容贴左上角 -->\n       <!-- <div id=\"overlay\" style=\"position:absolute;top:0;left:0;right:0;bottom:0;display:flex;flex-direction:column;\n            align-items:center;justify-content:center;text-align:center;\n            background:rgba(0,0,0,.6);z-index:10;\">\n         <h2 style=\"color:#fff;font-size:clamp(18px,3vw,32px);margin:0 0 2vh;\">游戏标题</h2>\n         <p style=\"color:#ccc;font-size:clamp(12px,1.8vw,18px);margin:0 0 3vh;\">操作说明文字</p>\n         <button style=\"padding:1.2vh 4vw;font-size:clamp(14px,2vw,20px);border-radius:2vmin;\n           border:none;background:#e74c3c;color:#fff;touch-action:manipulation;\">开始游戏</button>\n       </div> -->\n       <!-- ⚠️ 遮罩内所有文字/按钮必须通过 flexbox 居中，严禁使用固定 top/left 像素值定位 -->\n     </div>\n     <!-- 右栏：固定宽度，状态区+方向键始终可见 -->\n     <div id=\"sidebar\" style=\"width:26vw;min-width:180px;max-width:300px;height:100vh;\n          display:flex;flex-direction:column;padding:1.5vh 1.2vw;gap:0.8vh;box-sizing:border-box;overflow:hidden;\">\n       <!-- ① 游戏状态：必展示，不可压缩 -->\n       <div id=\"status\" style=\"flex:0 0 auto;font-size:clamp(10px,1.4vw,14px);\">关卡/得分/生命...</div>\n       <!-- ② 预览区（下一块等）：有预览需求时展示；若有预览区则不放排行榜和操作说明 -->\n       <div id=\"game-preview\" style=\"flex:1 1 0;min-height:0;\">...</div>\n       <!-- ③④ 排行榜+操作说明：仅在无预览区的游戏中使用（与 ② 二选一） -->\n       <!--\n       <div id=\"leaderboard\" style=\"flex:1 1 0;min-height:0;display:flex;flex-direction:column;font-size:clamp(10px,1.3vw,13px);\">\n         <h3 style=\"flex:0 0 auto;margin:0 0 0.4vh;\">排行榜</h3>\n         <div class=\"sb-inner\" style=\"flex:1 1 0;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;\n              scrollbar-width:thin;scrollbar-color:rgba(255,255,255,.3) transparent;\">\n         </div>\n       </div>\n       <div id=\"instructions\" style=\"flex:1 1 0;min-height:0;display:flex;flex-direction:column;font-size:clamp(10px,1.3vw,13px);\">\n         <h3 style=\"flex:0 0 auto;margin:0 0 0.4vh;\">操作说明</h3>\n         <div class=\"sb-inner\" style=\"flex:1 1 0;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;\n              scrollbar-width:thin;scrollbar-color:rgba(255,255,255,.3) transparent;\">\n         </div>\n       </div>\n       -->\n       <!-- ⑤ 方向键+暂停：固定在底部，始终可见 -->\n       <div id=\"controls\" style=\"flex:0 0 auto;padding-top:0.8vh;\">...</div>\n     </div>\n   </div>\n   ```\n   Canvas 尺寸初始化（JS 中必须这样写，确保 Android 8.1 WebView 中 canvas 像素与显示尺寸一致）：\n\n   > ⚠️ **Android 8.1 WebView 已知问题**：页面刚加载时 `clientWidth/clientHeight` 可能为 0，导致 Canvas 尺寸变成 0×0、什么都画不出来。必须用以下模板，通过 `window.load` + 双层 `requestAnimationFrame` 延迟读取尺寸，并用 `offsetWidth` 做兜底。\n\n   Canvas CSS（必须用 `position:absolute` 撑满，不依赖 flex 布局尺寸）：\n   ```html\n   <div id=\"game-area\" style=\"flex:1;position:relative;min-width:0;\">\n     <canvas id=\"canvas\" style=\"position:absolute;top:0;left:0;width:100%;height:100%;display:block;\"></canvas>\n   </div>\n   ```\n\n   Canvas 尺寸初始化（必须在 `window.load` + 双层 `requestAnimationFrame` 后执行）：\n   ```js\n   function initCanvas() {\n     const gameArea = document.getElementById('game-area');\n     const canvas = document.getElementById('canvas');\n     // offsetWidth 兜底：Android 8.1 WebView 刚加载时 clientWidth 可能为 0\n     const w = gameArea.clientWidth  || gameArea.offsetWidth  || window.innerWidth * 0.7;\n     const h = gameArea.clientHeight || gameArea.offsetHeight || window.innerHeight;\n     canvas.width  = w;\n     canvas.height = h;\n     // 格子大小根据 canvas 尺寸动态计算\n     const COLS = 30, ROWS = Math.floor(h / (w / COLS));\n     const cellW = w / COLS, cellH = h / ROWS;\n     // ... 其余游戏初始化逻辑\n   }\n\n   // 必须在 window.load 后、双层 rAF 确保布局渲染完毕再读尺寸\n   window.addEventListener('load', function() {\n     requestAnimationFrame(function() {\n       requestAnimationFrame(function() {\n         initCanvas();\n       });\n     });\n   });\n   ```\n\n   **游戏类 WebView 触摸适配要点**（Android 8.1 WebView 无键盘/鼠标）：\n\n   > ⚠️ **强制要求（主控方式）**：所有游戏的**主要控制方式必须是触摸坐标直接映射**——玩家手指按在画布哪里，角色/目标就跟到哪里（或朝那个方向移动），不需要抬手再点方向按钮。右下角的 ↑↓←→ 方向键和暂停键保留作为**辅助控制**，不是唯一控制入口。暂停按钮必须位于方向键 3×3 grid 的**正中心格**。\n\n   **触摸坐标映射实现模板**（贴在 Canvas/游戏容器上）：\n   ```js\n   // 主控：手指位置直接映射到游戏坐标\n   canvas.addEventListener('touchstart', onTouch, {passive: false});\n   canvas.addEventListener('touchmove',  onTouch, {passive: false});\n\n   function onTouch(e) {\n     e.preventDefault();\n     const rect = canvas.getBoundingClientRect();\n     const touch = e.touches[0];\n     // 将屏幕像素坐标换算为 canvas 内部坐标\n     const cx = (touch.clientX - rect.left) * (canvas.width  / rect.width);\n     const cy = (touch.clientY - rect.top)  * (canvas.height / rect.height);\n     movePlayerTo(cx, cy);   // 替换为游戏实际函数：直接设置目标坐标\n   }\n   ```\n   - 对于贪吃蛇、俄罗斯方块等离散格子类游戏，触摸映射改为**滑动方向**：`touchstart` 记录起点，`touchend` 计算 `deltaX/deltaY`，阈值 20px 内忽略，超过则取绝对值较大的轴作为方向\n   - 对于飞机大战、跑酷等连续坐标类游戏，用 `touchmove` 实时跟随手指坐标\n   - 左侧游戏遮罩/操作说明中必须用一句话说明触摸操作方式，例如：「触摸屏幕控制移动，手指在哪飞机就去哪」\n\n   **飞机大战类游戏专项规则**：\n   - **子弹必须自动连续发射**（`setInterval` 或游戏主循环中每隔固定帧数发射），**禁止要求玩家手动点击射击**\n   - 自动射频简单难度约 300ms/发，中等 200ms/发，困难 100ms/发\n   - 操作说明中写「触摸屏幕移动飞机，子弹自动发射」\n\n   **俄罗斯方块/消除类游戏专项规则**：\n   - **贴地即锁，零等待**：下落计时器触发时若方块无法继续下移，立刻执行 `lock()` → `spawn()`，下一块在同一帧内出现。**禁止 LOCK_DELAY 缓冲期**\n   - **`spawnPiece()` 必须将新方块赋值给全局变量**（如 `cur = shape`），否则 `drawGrid()` 中 `if(cur != null)` 判断为 false 导致方块不显示\n   - **经典 NES 速度标准**：第 1 关 800ms → 第 5 关 533ms → 第 10 关 333ms → 第 15 关 200ms → 第 20 关 100ms → 第 29 关+ 83ms（极限）\n   - 难度递增公式：`interval = Math.max(83, 800 - (level - 1) * 5 * (1000/60))`（每关减约 5 帧，60fps 基准）\n   - 预览区（下一块）在右栏 `#game-preview` 中展示，有预览时不展示排行榜和操作说明\n\n   **方向键 + 暂停按钮模板**（放在右栏 `#controls` 内，作为辅助控制）：\n\n   布局规则：**暂停按钮放在方向键 3×3 grid 的正中心格**，不单独放在键盘上方。整体视觉要炫酷：方向键用半透明玻璃质感，暂停键用蓝色渐变 + 光晕，`:active` 有明显按压反馈。\n\n   ```html\n   <div id=\"controls\" style=\"flex:0 0 auto;display:flex;flex-direction:column;align-items:center;\n        gap:0.8vh;touch-action:none;user-select:none;\">\n     <!-- 3×3 方向键grid，暂停在正中心 -->\n     <div style=\"display:grid;grid-template-columns:repeat(3,13vmin);grid-template-rows:repeat(3,13vmin);gap:1.2vmin;\">\n       <div></div>\n       <button class=\"dpad-btn\" id=\"btn-up\">▲</button>\n       <div></div>\n       <button class=\"dpad-btn\" id=\"btn-left\">◀</button>\n       <button class=\"dpad-btn\" id=\"btn-pause\">II</button>\n       <button class=\"dpad-btn\" id=\"btn-right\">▶</button>\n       <div></div>\n       <button class=\"dpad-btn\" id=\"btn-down\">▼</button>\n       <div></div>\n     </div>\n   </div>\n   <style>\n   /* 方向键：半透明玻璃质感 */\n   .dpad-btn {\n     background: linear-gradient(145deg, rgba(255,255,255,.22), rgba(255,255,255,.06));\n     border: 1.5px solid rgba(255,255,255,.35);\n     border-radius: 50%;\n     color: #fff;\n     font-size: 4.5vmin;\n     touch-action: manipulation;\n     display: flex;\n     align-items: center;\n     justify-content: center;\n     box-shadow: 0 2px 8px rgba(0,0,0,.4), inset 0 1px 0 rgba(255,255,255,.25);\n     cursor: pointer;\n     transition: transform .08s;\n   }\n   .dpad-btn:active {\n     background: linear-gradient(145deg, rgba(255,255,255,.45), rgba(255,255,255,.15));\n     transform: scale(.88);\n     box-shadow: 0 1px 4px rgba(0,0,0,.5), inset 0 1px 0 rgba(255,255,255,.3);\n   }\n   /* 暂停键与方向键样式完全一致，仅字号加粗 + 细边框区分；用 II 代替 ⏸ 符号（WebView 渲染兼容） */\n   #btn-pause {\n     font-size: 4.5vmin;\n     font-weight: bold;\n     border-color: rgba(255,255,255,.6);\n   }\n   </style>\n   <script>\n   document.getElementById('btn-up').addEventListener('touchstart',    e=>{e.preventDefault();changeDir(0,-1);},{passive:false});\n   document.getElementById('btn-down').addEventListener('touchstart',   e=>{e.preventDefault();changeDir(0, 1);},{passive:false});\n   document.getElementById('btn-left').addEventListener('touchstart',   e=>{e.preventDefault();changeDir(-1,0);},{passive:false});\n   document.getElementById('btn-right').addEventListener('touchstart',  e=>{e.preventDefault();changeDir( 1,0);},{passive:false});\n   document.getElementById('btn-pause').addEventListener('touchstart',  e=>{e.preventDefault();togglePause();},{passive:false});\n   // 桌面调试保留键盘\n   document.addEventListener('keydown', e=>{\n     if(e.key==='ArrowUp')    changeDir(0,-1);\n     if(e.key==='ArrowDown')  changeDir(0, 1);\n     if(e.key==='ArrowLeft')  changeDir(-1,0);\n     if(e.key==='ArrowRight') changeDir( 1,0);\n     if(e.key===' ')          togglePause();\n   });\n   </script>\n   ```\n   - `changeDir(dx, dy)` 和 `togglePause()` 替换为游戏实际函数名；坐标映射类游戏中方向键可改为触发\"持续向该方向移动\"逻辑\n   - 按钮尺寸用 `vmin` 单位，960px 小屏到 2560px 大屏均可用\n   - 暂停键必须在中心格，禁止移到键盘外部单独放置\n\n   **游戏角色视觉规范**（卡通可爱风格，禁止只画纯色圆点/方块）：\n   - 贪吃蛇：蛇头用圆角矩形 + 两个白色眼睛（小圆 + 黑色瞳孔）+ 根据朝向旋转；蛇身用渐变色圆角矩形，节节之间颜色略有变化；食物用苹果/草莓等图形（红色圆 + 绿色小叶子）\n   - 贪吃蛇蛇头绘制示例（Canvas 2D）：\n     ```js\n     function drawHead(ctx, x, y, w, h, dir) {\n       ctx.save();\n       ctx.translate(x + w/2, y + h/2);\n       const angle = {right:0, down:Math.PI/2, left:Math.PI, up:-Math.PI/2}[dir] || 0;\n       ctx.rotate(angle);\n       // 头部圆角矩形\n       ctx.fillStyle = '#2ecc71';\n       roundRect(ctx, -w/2, -h/2, w, h, w*0.3);\n       ctx.fill();\n       // 眼睛\n       [[-w*0.15, -h*0.15],[w*0.15, -h*0.15]].forEach(([ex,ey])=>{\n         ctx.fillStyle='#fff'; ctx.beginPath(); ctx.arc(ex,ey,w*0.12,0,Math.PI*2); ctx.fill();\n         ctx.fillStyle='#222'; ctx.beginPath(); ctx.arc(ex+w*0.03,ey,w*0.06,0,Math.PI*2); ctx.fill();\n       });\n       ctx.restore();\n     }\n     function roundRect(ctx,x,y,w,h,r){\n       ctx.beginPath();ctx.moveTo(x+r,y);ctx.lineTo(x+w-r,y);ctx.arcTo(x+w,y,x+w,y+r,r);\n       ctx.lineTo(x+w,y+h-r);ctx.arcTo(x+w,y+h,x+w-r,y+h,r);ctx.lineTo(x+r,y+h);\n       ctx.arcTo(x,y+h,x,y+h-r,r);ctx.lineTo(x,y+r);ctx.arcTo(x,y,x+r,y,r);ctx.closePath();\n     }\n     ```\n   - 俄罗斯方块：方块用渐变色 + 高光边框，不同形状用不同颜色\n   - 飞机大战：飞机用多边形或 SVG path 绘制，子弹用细长椭圆，敌机颜色与玩家区分；**子弹必须自动连续发射，禁止要求玩家手动点击射击**；操作说明写「触摸屏幕移动飞机，子弹自动发射」\n   - 通用原则：所有游戏角色必须有辨识度，能看出是什么，不能只是纯色几何形状\n\n   **平台跳跃 / 跑酷类游戏触摸控制规范**（马里奥、跑酷等左右移动+跳跃类游戏必须遵守）：\n\n   > 核心设计：左右半屏控制方向，上滑触发跳跃，斜滑（如右上方）同时触发移动+跳跃，完全模拟实体手柄的拇指操作。\n\n   ```js\n   let tSX = 0, tSY = 0; // 触摸起点，每次 touchstart 或跳跃触发后重置\n\n   canvas.addEventListener('touchstart', e => {\n     e.preventDefault();\n     const t = e.touches[0];\n     tSX = t.clientX; tSY = t.clientY;\n   }, {passive: false});\n\n   canvas.addEventListener('touchmove', e => {\n     e.preventDefault();\n     const t = e.touches[0];\n     const dx = t.clientX - tSX;\n     const dy = t.clientY - tSY; // 负值 = 向上\n\n     // 左右移动：根据手指当前位置在画布左/右半区判断方向\n     const rect = canvas.getBoundingClientRect();\n     if (t.clientX < rect.left + rect.width / 2) {\n       moveLeft();\n     } else {\n       moveRight();\n     }\n\n     // 上滑检测跳跃：阈值 20px，触发后立即重置 tSY 防止重复触发\n     if (dy < -20) {\n       jump();\n       tSY = t.clientY; // 重置参考点，允许同一次触摸连续触发（斜滑等）\n     }\n   }, {passive: false});\n\n   canvas.addEventListener('touchend', e => {\n     stopMove(); // 手指抬起停止移动\n   }, {passive: false});\n   ```\n   - `dy < -20`（负值向上）触发跳跃，同一次 `touchmove` 序列可多次触发（重置 tSY）\n   - 斜向上滑（如右上方）会同时执行 `moveRight()` + `jump()`，天然支持斜跳\n   - 阈值 20px 优于 28px，更灵敏，适合快节奏横板游戏\n   - 右栏方向键：◀▶ 控制移动，▲ 触发跳跃，▼ 可用于下蹲/快速下落\n   - 触摸坐标映射为主控，方向键为辅助——两套控制并行，不互斥\n   - 防止页面滚动干扰游戏：游戏容器加 `touch-action:none`，Canvas 加 `{ passive: false }` 并 `e.preventDefault()`\n   - 游戏暂停/重启按钮触摸区域 ≥ 44×44px\n   - 禁止在游戏主循环中使用 `alert()`，改用页面内自定义弹窗显示胜负信息\n\n   **跳跃 / 平台类游戏角色定位规范**（跳一跳、跑酷、马里奥等必须遵守）：\n\n   > ⚠️ **已知 Bug 根因**：用 `ph = PLAYER_H / squish` 作为绘制高度来定位头部/身体，当落地弹跳动画触发 `squish ≠ 1` 时，`ph` 变大，角色视觉上\"沉入\"平台下方。\n\n   **正确做法**：`py` 始终表示角色**脚底**的 y 坐标（即站在平台顶面），`squish` 只影响**宽度**（身体变宽/变窄），绝不影响高度定位：\n   ```js\n   // py = 脚底 y（始终贴平台顶面，不受 squish 影响）\n   // squish: 落地压缩系数，1=正常，>1=横向压扁（宽变大、高不变）\n   function drawPlayer(ctx, px, py, squish) {\n     const W = PLAYER_W * squish;   // 宽度随 squish 变化\n     const H = PLAYER_H;            // 高度固定，不除以 squish\n     const x = px - W / 2;\n     const y = py - H;              // 脚底 py 往上量固定 PLAYER_H\n     // 绘制身体：rr(ctx, x, y, W, H, radius)\n   }\n   ```\n   - `squish` 动画只修改 `W`（或同时等比缩小 `H` 但必须同步上移 `y = py - H`），确保脚底始终在 `py`\n   - 禁止写 `y = py - PLAYER_H / squish`，这会在 squish > 1 时让角色上移，squish < 1 时下沉\n\n\n\n   > ⚠️ **强制要求**：所有游戏类应用必须内置难度阶段机制，**默认从简单难度开始**，通过 10 关后自动升级为中等难度，通过 20 关后升级为困难。以飞机大战为参照基准，其他游戏按比例换算。\n\n   **难度分级标准**（以飞机大战为基准示例）：\n   | 阶段 | 关卡范围 | 敌机坠落速度 | 子弹发射间隔 | 过关目标分 | 每关目标分增量 |\n   |------|---------|------------|------------|---------|------------|\n   | 简单（Easy）  | 第 1~10 关  | 基础速度 × 1/2 | 300ms/发 | 100 分 | +10 分/关 |\n   | 中等（Medium）| 第 11~20 关 | 基础速度 × 80% | 200ms/发 | 200 分 | +10 分/关 |\n   | 困难（Hard）  | 第 21 关起  | 基础速度  | 100ms/发 | 300 分 | +10 分/关 |\n\n   - **难度参数必须用变量控制**，根据关卡号计算，禁止魔法数字硬编码：\n     ```js\n     let level = 1; // 每通关 +1\n\n     function getDifficulty(level) {\n       if (level <= 10) return {\n         speedMul: 1/2,  bulletInterval: 300,\n         targetScore: 100 + (level - 1) * 10   // 100, 110, 120...\n       };\n       if (level <= 20) return {\n         speedMul: 0.8,  bulletInterval: 200,\n         targetScore: 200 + (level - 11) * 10  // 200, 210, 220...\n       };\n       return {\n         speedMul: 1,  bulletInterval: 100,\n         targetScore: 300 + (level - 21) * 10  // 300, 310, 320...\n       };\n     }\n     ```\n   - **不设硬性关卡上限**，进入困难阶段后目标分持续增加，让玩家可以持续挑战\n   - 关卡信息（当前关卡、当前难度名称、得分 / 目标分、生命）必须在右栏顶部实时显示\n\n   **文档/Word/PDF 风格**：\n   - 容器使用响应式宽度：`max-width: min(1200px, 92vw); margin: 3vh auto; padding: 4vh 5vw`\n   - 在 960px 小屏上内容几乎满宽，在 1920px+ 大屏上居中且不超过 1200px\n   - 正文字号 `clamp(14px, 1.8vw, 18px)`，标题 `clamp(20px, 3vw, 32px)`\n   - 清晰的标题/段落/列表排版，使用衬线或无衬线字体\n   - 页面顶部可显示文档标题和日期\n\n   **PPT/演示文稿风格**：\n   - 引入 reveal.js：`<link rel=\"stylesheet\" href=\"https://unpkg.com/reveal.js@5/dist/reveal.css\">`\n   - 引入主题：`<link rel=\"stylesheet\" href=\"https://unpkg.com/reveal.js@5/dist/theme/white.css\">`\n   - 引入 JS：`<script src=\"https://unpkg.com/reveal.js@5/dist/reveal.js\"></script>`\n   - 内容用 `<div class=\"reveal\"><div class=\"slides\"><section>...</section></div></div>` 包裹\n   - 每个 `<section>` 是一页幻灯片\n   - 初始化：`<script>Reveal.initialize({hash:true, touch:true, width:960, height:600});</script>`\n     reveal.js 内部会等比缩放到实际视口，960×600 基准保证所有设备文字清晰不溢出\n   - 5~15 页为宜\n\n   **Excel/表格风格**：\n   - 容器 `width:100vw; height:100vh; overflow:auto; box-sizing:border-box; padding:2vh 3vw`\n   - HTML `<table>` 宽度 100%，带 border 和交替行色\n   - 表头字号 `clamp(12px, 1.6vw, 16px)`，单元格字号 `clamp(12px, 1.4vw, 15px)`\n   - 表头点击可排序（内联 JS 实现）\n   - 单元格 `contenteditable=\"true\"` 可编辑\n\n   **所有类型通用禁止项**：\n   - 禁止出现\"导出\"、\"打印\"、\"保存\"功能按钮\n   - 禁止 `@media print` 相关样式\n   - 禁止 `window.print()`、文件下载等功能\n\n4. 图标生成（icon_base64）：\n   - 产出一个 256×256 的 SVG 图标\n   - 风格：圆角矩形纯色背景 + 居中的主题图形（用 SVG path/circle/rect 绘制，**禁止使用 `<text>` 或 emoji**）\n   - 背景用饱和度高的纯色，中间图形用与背景对比强烈的颜色（可用白色、也可用多色彩搭配，参考 App Store 图标风格）\n   - SVG 文本做 base64 编码后传入 icon_base64（不带 `data:image/svg+xml;base64,` 前缀）\n   - SVG 原文不超过 3KB，**禁止使用 `<text>` 元素或 emoji**\n\n5. **【必须执行】HTML 自检 — 提交前逐项检查**：\n   生成 html_content 后，**必须**在脑内逐项检查以下条件，任一不通过则必须修改代码后再提交：\n\n   **自检项 A — 禁止 AI/电脑对手**（仅游戏/棋类/对战类需检查）：\n   - [ ] 代码中是否存在任何\"电脑走棋/出牌/移动\"的函数？（如 `computerMove`, `aiMove`, `makeAIMove`, `botPlay`, `autoPlay`, `cpuTurn`）→ 若有，**删除整个函数及其调用**\n   - [ ] 代码中是否存在 `minimax`, `alphaBeta`, `evaluate`, `bestMove`, `getRandomMove`, `Math.random()` 用于选择落子位置？→ 若有，**删除**\n   - [ ] 代码中是否存在\"难度选择\"（easy/medium/hard）或\"人机/人人\"模式切换？→ 若有，**删除，只保留双人模式**\n   - [ ] 游戏流程是否为：玩家A操作 → 切换到玩家B → 玩家B操作 → 切换回玩家A → 循环？→ 必须是\n   - [ ] 是否有任何 `setTimeout`/`setInterval` 用于延迟执行电脑的操作？→ 若有，**删除**\n\n   **自检项 B — 禁止导出/打印/保存**（所有类型需检查）：\n   - [ ] 是否存在\"导出\"、\"打印\"、\"保存\"、\"下载\"按钮或菜单项？→ 若有，**删除**\n   - [ ] 是否存在 `window.print()`, `document.execCommand('SaveAs')`, `URL.createObjectURL`, `download` 属性？→ 若有，**删除**\n\n   **自检项 C — Android 8.1 WebView 兼容**（所有类型需检查）：\n   - [ ] 是否使用了 Chrome 86+ 才支持的 CSS/JS API（`@property`、`content-visibility`、`structuredClone`、`??=`/`||=`/`&&=` 赋值运算符）？→ 若有，**替换为兼容写法**\n   - [ ] 是否使用了 `ctx.roundRect()`？→ Chrome 99+，**必须替换为自定义 `rr()` helper（用 arcTo 实现）**\n   - [ ] Canvas 内是否使用了 emoji 字符（fillText 中含 🔥❤️ 等）？→ WebView 字体渲染异常，**必须替换为纯文字或几何图形**\n   - [ ] 游戏布局是否为横版左右两栏（左栏游戏区、右栏控制区）？→ **若是竖版堆叠，必须改为横版**\n   - [ ] 右栏有 `#game-preview`（下一块/预览区）时，是否已去掉 `#leaderboard` 和 `#instructions`？→ **预览区与排行/说明互斥，有预览时不展示排行和说明**。无预览区的游戏中，排行/说明用 `flex:1 1 0;min-height:0` + h3 固定 + `.sb-inner` 内滚动。`#controls` 始终 `flex:0 0 auto` 固定底部\n   - [ ] Canvas CSS 是否用 `position:absolute;top:0;left:0;width:100%;height:100%` 撑满左栏，而非依赖 flex 布局？→ **若只用 `width:100%;height:100%` 而无 `position:absolute`，必须修正**\n   - [ ] Canvas 尺寸初始化是否在 `window.load` + 双层 `requestAnimationFrame` 内执行，且用 `offsetWidth` 做兜底（防止 Android 8.1 WebView 初始 `clientWidth` 为 0）？→ **若直接在顶层读 `clientWidth`，必须改为延迟初始化模板**\n   - [ ] 需要方向控制的游戏：是否在 Canvas 上绑定了 `touchstart`/`touchmove` 做坐标直接映射（主控），且右下角仍保留方向键 + 暂停按钮（辅助）？→ **若只有方向按钮而无触摸坐标映射，必须补充**\n   - [ ] 飞机大战类游戏：子弹是否自动连续发射（`setInterval` 或主循环计时），而非要求玩家手动点击射击？→ **若需要手动触发，必须改为自动发射**\n   - [ ] 俄罗斯方块/消除类游戏：①初始下落间隔是否为800ms（经典NES速度）？②贴地后是否立刻lock+spawn（零等待，无LOCK_DELAY缓冲）？③`spawnPiece()`是否将新方块赋值给全局变量（`cur=shape`）？→ **若不符合，按专项规则修正**\n   - [ ] 游戏内的开始/结束/暂停遮罩层是否用 `position:absolute;top:0;left:0;right:0;bottom:0;display:flex;align-items:center;justify-content:center` 居中（**禁止用 `inset:0`，Chrome 85 不支持**）？→ **若用 `inset:0` 或固定像素定位，内容会在智能屏上贴左上角，必须修正**\n   - [ ] 游戏是否实现了三段式难度（简单1~10关：速度×1/2、300ms/发、100+10×n分 → 中等11~20关：速度×80%、200ms/发、200+10×n分 → 困难21关起：基础速度、100ms/发、300+10×n分），且第1关使用简单参数？→ **若全程同一难度或开局就很快，必须按难度分级模板修正**\n   - [ ] 游戏角色是否有视觉辨识度（蛇头有眼睛、食物有形状等），而不是纯色圆点/方块？→ **若只是纯色几何形，必须改为卡通风格**\n   - [ ] 是否使用了原生 `alert()`/`confirm()`/`prompt()`？→ 若有，**改为页面内自定义弹窗**\n   - [ ] 游戏容器/Canvas 是否加了 `touch-action:none` 并在 touch 事件中调用 `e.preventDefault()`？→ 若未处理，**补充**\n   - [ ] 可点击/可触摸元素的触摸区域是否 ≥ 44×44px？→ 若不足，**调整尺寸**\n   - [ ] 跳跃/平台类游戏（跳一跳、跑酷等）：绘制角色时是否用固定常量 `PLAYER_H` 定位脚底坐标（`py` 为脚底 y，头顶为 `py - PLAYER_H`），而非用 `PLAYER_H / squish` 等变形高度定位？→ **`squish`（落地压缩动画）只能影响角色宽度/变窄，绝不能影响脚底 `py` 的计算，否则 squish≠1 时角色视觉上会\"沉入\"平台下方**\n\n   ⚠️ 如果自检发现违规代码，**不要提交**，先修正 html_content 再继续下一步。\n\n5.5. **【必须执行】JS 语法验证**（应用类/游戏类必须，文件类可选）：\n   在调用 `myapp_register` 之前，用 `exec` 工具对 html_content 中的 `<script>` 代码块做语法验证，确保不因语法错误导致游戏在智能屏上完全空白：\n   ```bash\n   # 提取 <script> 内容写临时文件，用 node --check 验证\n   node -e \"\n   const fs = require('fs');\n   const html = fs.readFileSync('/tmp/_skill_check.html','utf8');\n   const scripts = html.match(/<script[\\s\\S]*?>([\\s\\S]*?)<\\/script>/gi) || [];\n   scripts.forEach((s,i) => {\n     const code = s.replace(/<\\/?script[^>]*>/gi,'');\n     fs.writeFileSync('/tmp/_check_'+i+'.js', code);\n   });\n   \" && node --check /tmp/_check_*.js 2>&1\n   ```\n   - 实际操作：将 html_content 写入 `/tmp/_skill_check.html`，运行上面命令\n   - 如果 `node --check` 输出任何错误，必须修复对应 JS 代码后重新验证，直到无错误\n   - 验证通过后再调用 `myapp_register`\n\n6. 调用 `myapp_register` 工具：\n   - 入参：`dumi_id, cuid, query, app_name, html_content, features, icon_base64, tag`\n   - `tag`：根据第 1 步判断的类型填写——**应用类（交互式）填 `\"app\"`，文件类（文档/PPT/表格）填 `\"doc\"`**；不传时服务端默认 `\"app\"`\n   - `dumi_id` 与 `cuid` 从对话上下文中获取（OpenClaw 会注入）\n\n7. 工具返回成功后，向用户展示：\n   - 告知创建完成：\"《<app_name>》已创建完成。\"\n   - 展示功能/玩法（根据类型区分）：\n     - **应用类**：\n       - 列出\"游戏特性\"或\"功能特性\"：从 features 中提取 3~6 条核心点\n       - 列出\"操作方式\"：说明触屏/键盘的操作方法（如方向键控制、暂停等）\n     - **文件类**（文档/PPT/表格）：\n       - 简要概括文档核心内容（如关键数据、主要结论、章节概要等，3~5 条）\n       - 提示\"点击链接即可查看完整内容\"\n   - 展示文件位置：给出返回的 `html_url` 链接\n   - 提示用户：\"下次可以在「我的创作」页面查看和管理你创建的所有应用\"\n\n8. 失败/超长/超出能力 → 回复\"应用创建失败，请稍后重试\"\n   - **不要重试**，**不要伪造成功**，**不要把 html_content 输出到对话**\n\n## 更新流程\n\n最长等待 300s。若 300s 内不能完成旧功能读取、HTML 重新生成与 `myapp_update` 调用，直接回复\"应用更新失败，请稍后重试\"。\n\n1. **先调 `myapp_get(app_id)`** 拿到 `app_name` 与旧 `features`、旧 `query`\n2. 把\"旧 features + 用户新需求\"作为完整上下文，重新生成：\n   - 新 features（合并增删，仍是 1~12 条全集，不是 diff）\n   - 新 html_content（保持原有类型风格）\n   - 若应用主题/类型发生变化，重新生成 icon_base64（同创建流程第 4 步）\n3. 调用 `myapp_update(app_id, query, html_content, features, icon_base64, tag)`\n   - `tag` 同创建流程：应用类填 `\"app\"`，文件类填 `\"doc\"`\n4. 工具返回成功后，向用户展示：\n   - 告知更新完成：\"《<app_name>》已更新完成。\"\n   - 展示更新内容（根据类型区分）：\n     - **应用类**：列出本次新增/变更的功能点和操作方式变化\n     - **文件类**：简要概括更新后的核心内容或变更要点\n   - 展示文件位置：给出返回的 `html_url` 链接\n   - 提示用户：\"下次可以在「我的创作」页面查看和管理你创建的所有应用\"\n5. 失败 → 回复\"应用更新失败，请稍后重试\"\n\n## 安装自检（[install_check:session=<sid>]）\n\n最长等待 300s。超过 300s 未完成安装或 `myapp_ping`，视为安装失败。\n\n仅调用 `myapp_ping(session_id=\"<sid>\", dumi_id, cuid, skill_version=\"1.0.10\")`，\n不输出对话内容（或仅极简的\"已就绪\"）。\n\n## 硬约束\n\n- **绝对禁止在游戏中实现 AI 对手、人机对战、电脑自动操作**——棋类/对战类游戏只允许双人同屏轮流操作\n- 禁止出现的代码模式：`computerMove`, `aiMove`, `makeAIMove`, `minimax`, `alphaBeta`, `bestMove`, `getRandomMove`, 难度选择(easy/medium/hard), 人机/人人模式切换\n- **绝对禁止出现\"导出\"、\"打印\"、\"保存\"按钮或相关功能**\n- **需要方向控制的游戏必须在页面上渲染四个方向触摸按钮（↑↓←→），绑定 `touchstart` 事件**——不得以键盘或滑动手势代替\n- 不要把 `html_content` 原文输出到对话\n- 不要把 `MYAPP_API_TOKEN` / `MYAPP_API_BASE` 输出到对话\n- 工具调用 4xx/5xx → 直接失败兜底，不重试，不修改请求自行重试\n- 除安装、创建、更新外，其它工具交互超过 180s 视为失败\n\nFile v1.0.31:README.md\n\n# myapp-creator\n\n让小度用户用一句话造一个 HTML 小应用，落库到 fe-service。\n\n- 包名：ClawHub 页面 URL 为 `https://clawhub.ai/<发布者>/myapp-creator`；**`clawhub install / inspect` 只允许裸 slug `myapp-creator`**（参数里含 `/` 会报 `Invalid slug`）。fe-service `MYAPP_SKILL_NAME` 可仍写全路径，下发 install_prompt 时会自动剥成裸 slug。\n- 当前版本：见 `version.txt`\n- License：MIT-0\n\n## 用户视角是什么\n\n用户在小度 Swan 端的\"我的应用\"页输入一句话（如\"做一个五子棋\"），fe-service 通过对话深链触发龙虾 OpenClaw 的 LLM agent；agent 在本 skill 的 prompt 约束下生成单文件 HTML 与功能列表，再调本 skill 的 `myapp_register` 工具把数据落到 fe-service。\n\n整个流程对用户透明：用户只看见一个进度条和最终的应用卡片。\n\n## 安装方式\n\n### A. 自动安装（推荐）\n\n用户首次进入\"我的应用\"页时，fe-service 会下发一段 install_prompt，由龙虾 agent 自动执行：\n\n```\nclawhub install myapp-creator --version <VERSION>\n```\n\n页面路径里虽有 `zhang6714268/`，**安装时不要带前缀**，否则 `Invalid slug`。\n\n若本机无 `clawhub`，agent 需先 `npm i -g clawhub`（或 `npx clawhub@latest install ...`）。**不要用 `openclaw skills install`**，新版 OpenClaw 的 `skills` 子命令会报 `too many arguments`。\n\n并写入 `~/.mcporter/mcporter.json` 中的环境变量。**用户无需手动操作。**\n\n### B. 手动安装（兜底/调试）\n\n```bash\nclawhub install myapp-creator --version 1.0.0\n# 或：npx --yes clawhub@latest install myapp-creator --version 1.0.0\n```\n\n然后在 **`~/.mcporter/mcporter.json`** 里为 **myapp-creator** 增加 `skills.myapp-creator.env`（与顶层 `mcpServers` **并列**，不是写进 `xiaodu` 里）。若文件中已有 `xiaodu` / `xiaodu-iot` 等，请**合并**如下结构，勿删掉原有 `mcpServers`：\n\n```json\n{\n  \"mcpServers\": { },\n  \"skills\": {\n    \"myapp-creator\": {\n      \"env\": {\n        \"MYAPP_API_BASE\": \"http://group.dumi-SwanSSRNode12-env.dumi.all\",\n        \"MYAPP_API_TOKEN\": \"<由 fe-service 团队下发>\"\n      }\n    }\n  }\n}\n```\n\n最后重新加载（任选其一，以本机 CLI 为准）：\n\n```bash\nopenclaw gateway restart\n# 若无 gateway 子命令：重启 OpenClaw 应用\n```\n\n## 工具清单\n\n| 工具 | 入参 | 用途 |\n|---|---|---|\n| `myapp_register` | dumi_id, cuid, query, app_name, html_content, features | 创建新应用 |\n| `myapp_update`   | app_id, query, html_content, features | 更新已有应用 |\n| `myapp_get`      | app_id | 读应用详情（更新前用） |\n| `myapp_ping`     | session_id, dumi_id, cuid, skill_version | 安装自检 |\n\n详细约束与触发条件见 `SKILL.md`。\n\n## 故障排查\n\n| 现象 | 排查 |\n|---|---|\n| 调 register/update 返回 401 | 检查 `MYAPP_API_TOKEN` 是否与 fe-service 配置一致 |\n| 调 register/update 返回 502 (BOS upload failed) | fe-service 端 BOS_UPLOAD_AK/SK 未配置或权限不足 |\n| 调 register/update 返回 400 | 入参超长或缺字段；检查 `app_name`/`features`/`html_content` 是否符合 SKILL.md 约束 |\n| 调 register/update 返回 timeout | fe-service 不可达；检查 `MYAPP_API_BASE` 与本机网络 |\n| `myapp_get` 404 | app 已被软删，或 app_id 写错 |\n| 安装自检 60s 仍未完成 | 检查 mcporter env 中 token / base 是否正确写入；重启 openclaw |\n| `./publish.sh` 报 `too many arguments for 'skills'` | **openclaw** 不再支持 `skills publish/install`；发布用 `clawhub`，安装用 `clawhub install <裸slug> --version …`（不要 `owner/skill`） |\n| `ping.sh` 报 `param session_id required` | 须用环境变量或传入 JSON；见上文 **§C** |\n\n## 发版\n\n```bash\n# 0) 安装并登录 ClawHub CLI（发布走 clawhub，不要用已弃用的 openclaw skills publish）\nnpm i -g clawhub\nclawhub login\n\n# 1) 改 version.txt 和 clawhub.yaml 中的 version\n# 2) 跑发布（内部: clawhub skill publish . --version <x.y.z>）\n./publish.sh\n\n# 3) 同步更新 fe-service 中的 MYAPP_SKILL_REQUIRED_VERSION\n```\n\n## 安全说明\n\n- 本 skill 代码为公开包，**不包含任何 token**。\n- 所有调用都需要 `MYAPP_API_TOKEN`，token 由 fe-service 在用户首次安装时通过 install_prompt 下发，落到用户本机 mcporter.json env。\n- 没有合法 dumi_id + token 的用户即使装上 skill，调用 fe-service 也会被 401 拒绝。\n- 安全模型与 [`dueros-mcp/xiaodu-control-official`](https://clawhub.ai/dueros-mcp/xiaodu-control-official) 一致。\n\nFile v1.0.31:skills/myapp-creator/README.md\n\n# myapp-creator\n\n让小度用户用一句话造一个 HTML 小应用，落库到 fe-service。\n\n- 包名：ClawHub 页面 URL 为 `https://clawhub.ai/<发布者>/myapp-creator`；**`clawhub install / inspect` 只允许裸 slug `myapp-creator`**（参数里含 `/` 会报 `Invalid slug`）。fe-service `MYAPP_SKILL_NAME` 可仍写全路径，下发 install_prompt 时会自动剥成裸 slug。\n- 当前版本：见 `version.txt`\n- License：MIT-0\n\n## 用户视角是什么\n\n用户在小度 Swan 端的\"我的应用\"页输入一句话（如\"做一个五子棋\"），fe-service 通过对话深链触发龙虾 OpenClaw 的 LLM agent；agent 在本 skill 的 prompt 约束下生成单文件 HTML 与功能列表，再调本 skill 的 `myapp_register` 工具把数据落到 fe-service。\n\n整个流程对用户透明：用户只看见一个进度条和最终的应用卡片。\n\n## 安装方式\n\n### A. 自动安装（推荐）\n\n用户首次进入\"我的应用\"页时，fe-service 会下发一段 install_prompt，由龙虾 agent 自动执行：\n\n```\nclawhub install myapp-creator --version <VERSION>\n```\n\n页面路径里虽有 `zhang6714268/`，**安装时不要带前缀**，否则 `Invalid slug`。\n\n若本机无 `clawhub`，agent 需先 `npm i -g clawhub`（或 `npx clawhub@latest install ...`）。**不要用 `openclaw skills install`**，新版 OpenClaw 的 `skills` 子命令会报 `too many arguments`。\n\n并写入 `~/.mcporter/mcporter.json` 中的环境变量。**用户无需手动操作。**\n\n### B. 手动安装（兜底/调试）\n\n```bash\nclawhub install myapp-creator --version 1.0.0\n# 或：npx --yes clawhub@latest install myapp-creator --version 1.0.0\n```\n\n然后在 **`~/.mcporter/mcporter.json`** 里为 **myapp-creator** 增加 `skills.myapp-creator.env`（与顶层 `mcpServers` **并列**，不是写进 `xiaodu` 里）。若文件中已有 `xiaodu` / `xiaodu-iot` 等，请**合并**如下结构，勿删掉原有 `mcpServers`：\n\n```json\n{\n  \"mcpServers\": { },\n  \"skills\": {\n    \"myapp-creator\": {\n      \"env\": {\n        \"MYAPP_API_BASE\": \"http://group.dumi-SwanSSRNode12-env.dumi.all\",\n        \"MYAPP_API_TOKEN\": \"<由 fe-service 团队下发>\"\n      }\n    }\n  }\n}\n```\n\n最后重新加载（任选其一，以本机 CLI 为准）：\n\n```bash\nopenclaw gateway restart\n# 若无 gateway 子命令：重启 OpenClaw 应用\n```\n\n## 工具清单\n\n| 工具 | 入参 | 用途 |\n|---|---|---|\n| `myapp_register` | dumi_id, cuid, query, app_name, html_content, features | 创建新应用 |\n| `myapp_update`   | app_id, query, html_content, features | 更新已有应用 |\n| `myapp_get`      | app_id | 读应用详情（更新前用） |\n| `myapp_ping`     | session_id, dumi_id, cuid, skill_version | 安装自检 |\n\n详细约束与触发条件见 `SKILL.md`。\n\n## 故障排查\n\n| 现象 | 排查 |\n|---|---|\n| 调 register/update 返回 401 | 检查 `MYAPP_API_TOKEN` 是否与 fe-service 配置一致 |\n| 调 register/update 返回 502 (BOS upload failed) | fe-service 端 BOS_UPLOAD_AK/SK 未配置或权限不足 |\n| 调 register/update 返回 400 | 入参超长或缺字段；检查 `app_name`/`features`/`html_content` 是否符合 SKILL.md 约束 |\n| 调 register/update 返回 timeout | fe-service 不可达；检查 `MYAPP_API_BASE` 与本机网络 |\n| `myapp_get` 404 | app 已被软删，或 app_id 写错 |\n| 安装自检 60s 仍未完成 | 检查 mcporter env 中 token / base 是否正确写入；重启 openclaw |\n| `./publish.sh` 报 `too many arguments for 'skills'` | **openclaw** 不再支持 `skills publish/install`；发布用 `clawhub`，安装用 `clawhub install <裸slug> --version …`（不要 `owner/skill`） |\n| `ping.sh` 报 `param session_id required` | 须用环境变量或传入 JSON；见上文 **§C** |\n\n## 发版\n\n```bash\n# 0) 安装并登录 ClawHub CLI（发布走 clawhub，不要用已弃用的 openclaw skills publish）\nnpm i -g clawhub\nclawhub login\n\n# 1) 改 version.txt 和 clawhub.yaml 中的 version\n# 2) 跑发布（内部: clawhub skill publish . --version <x.y.z>）\n./publish.sh\n\n# 3) 同步更新 fe-service 中的 MYAPP_SKILL_REQUIRED_VERSION\n```\n\n## 安全说明\n\n- 本 skill 代码为公开包，**不包含任何 token**。\n- 所有调用都需要 `MYAPP_API_TOKEN`，token 由 fe-service 在用户首次安装时通过 install_prompt 下发，落到用户本机 mcporter.json env。\n- 没有合法 dumi_id + token 的用户即使装上 skill，调用 fe-service 也会被 401 拒绝。\n- 安全模型与 [`dueros-mcp/xiaodu-control-official`](https://clawhub.ai/dueros-mcp/xiaodu-control-official) 一致。\n\nFile v1.0.31:skills/myapp-creator/skills/myapp-creator/README.md\n\n# myapp-creator\n\n让小度用户用一句话造一个 HTML 小应用，落库到 fe-service。\n\n- 包名：ClawHub 页面 URL 为 `https://clawhub.ai/<发布者>/myapp-creator`；**`clawhub install / inspect` 只允许裸 slug `myapp-creator`**（参数里含 `/` 会报 `Invalid slug`）。fe-service `MYAPP_SKILL_NAME` 可仍写全路径，下发 install_prompt 时会自动剥成裸 slug。\n- 当前版本：见 `version.txt`\n- License：MIT-0\n\n## 用户视角是什么\n\n用户在小度 Swan 端的\"我的应用\"页输入一句话（如\"做一个五子棋\"），fe-service 通过对话深链触发龙虾 OpenClaw 的 LLM agent；agent 在本 skill 的 prompt 约束下生成单文件 HTML 与功能列表，再调本 skill 的 `myapp_register` 工具把数据落到 fe-service。\n\n整个流程对用户透明：用户只看见一个进度条和最终的应用卡片。\n\n## 安装方式\n\n### A. 自动安装（推荐）\n\n用户首次进入\"我的应用\"页时，fe-service 会下发一段 install_prompt，由龙虾 agent 自动执行：\n\n```\nclawhub install myapp-creator --version <VERSION>\n```\n\n页面路径里虽有 `zhang6714268/`，**安装时不要带前缀**，否则 `Invalid slug`。\n\n若本机无 `clawhub`，agent 需先 `npm i -g clawhub`（或 `npx clawhub@latest install ...`）。**不要用 `openclaw skills install`**，新版 OpenClaw 的 `skills` 子命令会报 `too many arguments`。\n\n并写入 `~/.mcporter/mcporter.json` 中的环境变量。**用户无需手动操作。**\n\n### B. 手动安装（兜底/调试）\n\n```bash\nclawhub install myapp-creator --version 1.0.0\n# 或：npx --yes clawhub@latest install myapp-creator --version 1.0.0\n```\n\n然后在 `~/.mcporter/mcporter.json` 的 `skills.myapp-creator.env` 写入：\n\n```json\n{\n  \"MYAPP_API_BASE\": \"http://group.dumi-SwanSSRNode12-env.dumi.all\",\n  \"MYAPP_API_TOKEN\": \"<由 fe-service 团队下发>\"\n}\n```\n\n最后重新加载（任选其一，以本机 CLI 为准）：\n\n```bash\nopenclaw gateway restart\n# 若无 gateway 子命令：重启 OpenClaw 应用\n```\n\n## 工具清单\n\n| 工具 | 入参 | 用途 |\n|---|---|---|\n| `myapp_register` | dumi_id, cuid, query, app_name, html_content, features | 创建新应用 |\n| `myapp_update`   | app_id, query, html_content, features | 更新已有应用 |\n| `myapp_get`      | app_id | 读应用详情（更新前用） |\n| `myapp_ping`     | session_id, dumi_id, cuid, skill_version | 安装自检 |\n\n详细约束与触发条件见 `SKILL.md`。\n\n## 故障排查\n\n| 现象 | 排查 |\n|---|---|\n| 调 register/update 返回 401 | 检查 `MYAPP_API_TOKEN` 是否与 fe-service 配置一致 |\n| 调 register/update 返回 502 (BOS upload failed) | fe-service 端 BOS_UPLOAD_AK/SK 未配置或权限不足 |\n| 调 register/update 返回 400 | 入参超长或缺字段；检查 `app_name`/`features`/`html_content` 是否符合 SKILL.md 约束 |\n| 调 register/update 返回 timeout | fe-service 不可达；检查 `MYAPP_API_BASE` 与本机网络 |\n| `myapp_get` 404 | app 已被软删，或 app_id 写错 |\n| 安装自检 60s 仍未完成 | 检查 mcporter env 中 token / base 是否正确写入；重启 openclaw |\n| `./publish.sh` 报 `too many arguments for 'skills'` | **openclaw** 不再支持 `skills publish/install`；发布用 `clawhub`，安装用 `clawhub install <裸slug> --version …`（不要 `owner/skill`） |\n\n## 发版\n\n```bash\n# 0) 安装并登录 ClawHub CLI（发布走 clawhub，不要用已弃用的 openclaw skills publish）\nnpm i -g clawhub\nclawhub login\n\n# 1) 改 version.txt 和 clawhub.yaml 中的 version\n# 2) 跑发布（内部: clawhub skill publish . --version <x.y.z>）\n./publish.sh\n\n# 3) 同步更新 fe-service 中的 MYAPP_SKILL_REQUIRED_VERSION\n```\n\n## 安全说明\n\n- 本 skill 代码为公开包，**不包含任何 token**。\n- 所有调用都需要 `MYAPP_API_TOKEN`，token 由 fe-service 在用户首次安装时通过 install_prompt 下发，落到用户本机 mcporter.json env。\n- 没有合法 dumi_id + token 的用户即使装上 skill，调用 fe-service 也会被 401 拒绝。\n- 安全模型与 [`dueros-mcp/xiaodu-control-official`](https://clawhub.ai/dueros-mcp/xiaodu-control-official) 一致。\n\nFile v1.0.31:skills/myapp-creator/_meta.json\n\n{\n  \"ownerId\": \"kn70tr711s8jjaf5gd823aep6586ca9e\",\n  \"slug\": \"myapp-creator\",\n  \"version\": \"1.0.3\",\n  \"publishedAt\": 1778665524690\n}\n\nFile v1.0.31:skills/myapp-creator/skills/myapp-creator/_meta.json\n\n{\n  \"ownerId\": \"kn70tr711s8jjaf5gd823aep6586ca9e\",\n  \"slug\": \"myapp-creator\",\n  \"version\": \"1.0.2\",\n  \"publishedAt\": 1778483960240\n}\n\nFile v1.0.31:_meta.json\n\n{\n  \"ownerId\": \"kn70tr711s8jjaf5gd823aep6586ca9e\",\n  \"slug\": \"myapp-creator\",\n  \"version\": \"1.0.31\",\n  \"publishedAt\": 1779972467524\n}\n\nFile v1.0.31:skill-card.md\n\n## Description:\n\nMyapp Creator helps an agent create or update standalone single-file HTML apps and documents for Xiaodu users, then register them with fe-service.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[zhang6714268](https://clawhub.ai/user/zhang6714268)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and agents serving Xiaodu Swan users use this skill to turn short app or document requests into self-contained HTML. The skill supports creating, updating, reading, and installation-checking app records through its configured service tools.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The skill stores and transmits a long-lived API token together with user and app data.\n\nMitigation: Install only in trusted local environments, protect the local token configuration, prefer short-lived or scoped tokens where available, and avoid placing secrets or private content in app prompts.\n\nRisk: A configurable MYAPP_API_BASE can direct generated app content and identifiers to an unintended service.\n\nMitigation: Use HTTPS and a fixed, approved backend host; confirm the fe-service operator before installing or running the skill.\n\nRisk: Unpinned npx/npm install paths can resolve to a CLI version that has not been separately reviewed.\n\nMitigation: Prefer a vetted ClawHub CLI version or separately review the resolved CLI package before using unpinned install commands.\n\n## Reference(s):\n\n- [ClawHub skill page](https://clawhub.ai/zhang6714268/skills/myapp-creator)\n- [Publisher profile](https://clawhub.ai/user/zhang6714268)\n- [Related Xiaodu control skill card](https://clawhub.ai/dueros-mcp/xiaodu-control-official)\n\n## Skill Output:\n\n**Output Type(s):** [text, code, shell commands, configuration]\n\n**Output Format:** [Markdown guidance with single-file HTML, JSON feature lists, and shell-backed tool calls]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Generated HTML should be self-contained, touch-friendly, no more than 60KB, and compatible with Android 8.1 WebView.]\n\n## Skill Version(s):\n\n1.0.31 (source: server release metadata, artifact metadata.json, clawhub.yaml)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nFile v1.0.31:metadata.json\n\n{\n    \"name\": \"myapp-creator\",\n    \"namespace\": \"zhang6714268\",\n    \"version\": \"1.0.31\",\n    \"description\": \"让小度用户用一句话创建 HTML 小应用，落库到 fe-service\",\n    \"license\": \"MIT-0\",\n    \"runtime\": {\n        \"bins\": [\"bash\", \"curl\", \"jq\"],\n        \"env\": [\"MYAPP_API_BASE\", \"MYAPP_API_TOKEN\"]\n    },\n    \"tools\": [\"myapp_register\", \"myapp_update\", \"myapp_get\", \"myapp_ping\"],\n    \"tags\": [\"dueros\", \"html\", \"low-code\", \"single-shot-app\"]\n}\n\nFile v1.0.31:skills/myapp-creator/metadata.json\n\n{\n    \"name\": \"myapp-creator\",\n    \"namespace\": \"zhang6714268\",\n    \"version\": \"1.0.31\",\n    \"description\": \"让小度用户用一句话创建 HTML 小应用，落库到 fe-service\",\n    \"license\": \"MIT-0\",\n    \"runtime\": {\n        \"bins\": [\"bash\", \"curl\", \"jq\"],\n        \"env\": [\"MYAPP_API_BASE\", \"MYAPP_API_TOKEN\"]\n    },\n    \"tools\": [\"myapp_register\", \"myapp_update\", \"myapp_get\", \"myapp_ping\"],\n    \"tags\": [\"dueros\", \"html\", \"low-code\", \"single-shot-app\"]\n}\n\nArchive v1.0.30: 41 files, 86174 bytes\n\nFiles: clawhub.yaml (565b), LICENSE (912b), metadata.json (464b), openai.yaml (3724b), publish.sh (2611b), README.md (4584b), scripts/_common.sh (761b), scripts/get.sh (437b), scripts/ping.sh (832b), scripts/register.sh (1400b), scripts/update.sh (1161b), skill-card.md (2675b), SKILL.md (44246b), skills/myapp-creator/_meta.json (132b), skills/myapp-creator/clawhub.yaml (565b), skills/myapp-creator/metadata.json (464b), skills/myapp-creator/openai.yaml (3724b), skills/myapp-creator/publish.sh (2611b), skills/myapp-creator/README.md (4584b), skills/myapp-creator/scripts/_common.sh (761b), skills/myapp-creator/scripts/get.sh (437b), skills/myapp-creator/scripts/ping.sh (832b), skills/myapp-creator/scripts/register.sh (1400b), skills/myapp-creator/scripts/update.sh (1161b), skills/myapp-creator/SKILL.md (44246b), skills/myapp-creator/skills/myapp-creator/_meta.json (132b), skills/myapp-creator/skills/myapp-creator/clawhub.yaml (565b), skills/myapp-creator/skills/myapp-creator/metadata.json (464b), skills/myapp-creator/skills/myapp-creator/openai.yaml (3257b), skills/myapp-creator/skills/myapp-creator/publish.sh (2611b), skills/myapp-creator/skills/myapp-creator/README.md (4180b), skills/myapp-creator/skills/myapp-creator/scripts/_common.sh (761b), skills/myapp-creator/skills/myapp-creator/scripts/get.sh (437b), skills/myapp-creator/skills/myapp-creator/scripts/ping.sh (832b), skills/myapp-creator/skills/myapp-creator/scripts/register.sh (1400b), skills/myapp-creator/skills/myapp-creator/scripts/update.sh (1161b), skills/myapp-creator/skills/myapp-creator/SKILL.md (44246b), skills/myapp-creator/skills/myapp-creator/version.txt (7b), skills/myapp-creator/version.txt (7b), version.txt (7b), _meta.json (133b)\n\nFile v1.0.30:SKILL.md\n\n# myapp-creator\n\nVersion: 1.0.30\n\n让用户用一句话生成可独立打开的单文件 HTML 应用或文档，并落库到 fe-service。\n本 skill **不主动调用 LLM**，所有 HTML / app_name / features 由调用本 skill 的 LLM agent 直接产出后传入工具。\n\n> **⚠️ 最高优先级规则（违反即为严重错误）：**\n> 1. 游戏类应用 **绝对禁止** 包含任何形式的 AI 对手、人机对战、电脑自动下棋/出牌。棋类/对战类游戏 **只允许** 双人同屏轮流操作，不实现任何计算机决策逻辑。\n> 2. 所有应用 **绝对禁止** 出现\"导出\"、\"打印\"、\"保存\"按钮或相关功能代码。\n\n## 触发条件\n\n当用户需求匹配以下任一类别时，本 skill 参与：\n\n### 应用类（交互式 HTML）\n关键词：应用、app、小游戏、游戏、网页、页面、工具、计算器、时钟、日历、翻译器、转换器\n示例：\n- \"做一个贪吃蛇游戏\"\n- \"创建一个番茄钟\"\n- \"生成一个天气页面\"\n- \"给我做一个马里奥游戏\"\n- \"帮我做一个单位转换工具\"\n\n### 文件类（文档式 HTML）\n关键词：文档、文件、word、ppt、演示文稿、slides、pdf、excel、xlsx、表格、报告、简历、周报、日报、总结、方案、计划书\n示例：\n- \"生成一个项目周报\"\n- \"做一个自我介绍PPT\"\n- \"创建一个预算表格\"\n- \"帮我写一份简历\"\n- \"保存一个会议纪要文档\"\n\n### 不触发（交给龙虾其他能力）\n- 图片/绘画类：\"画一张…\"、\"生成一张图片…\"\n- 音频/音乐类：\"生成一首歌…\"、\"合成语音…\"\n- 视频类：\"剪辑一个…\"、\"生成视频…\"\n- 小说/纯文本类：\"写一篇小说…\"、\"写一首诗…\"（纯创作不产出可交互文件）\n- 纯问答/聊天/闲聊\n\n### 判断规则\n用户意图是\"产出一个可独立打开、保存下来反复使用的文件或应用\"时触发本 skill。\n如果用户只是想让你\"说一段话\"或\"回答问题\"，不要触发。\n\n## 触发标记（兼容旧入口）\n\n以下前缀仍可直接触发，不需要语义判断：\n- `[create_app]<描述>` → 创建新应用/文件\n- `[update_app:app_id=<n>]<描述>` → 更新已有应用/文件\n- `[install_check:session=<sid>]` → 安装自检（仅调 myapp_ping）\n- 不带上述前缀 → 按语义判断是否匹配触发条件\n\n## 创建流程\n\n最长等待 300s。若 300s 内不能完成 HTML 生成与 `myapp_register` 调用，直接回复\"应用创建失败，请稍后重试\"。\n\n1. 解析用户描述，判断类型（应用类 or 文件类），产出：\n   - `app_name`：应用/文件名，≤30 字\n   - `features`：JSON 字符串数组，3~8 条，每条 ≤30 字，描述具体功能/内容要点\n   - `html_content`：完整可独立打开的单文件 HTML（按类型选择模板风格）\n\n2. HTML 通用约束：\n   - 自包含：CSS/JS 全部内联在 `<style>` / `<script>`\n   - 不依赖本地资源\n   - 外部资源仅允许：unpkg.com / jsdelivr.net / cdnjs.cloudflare.com\n   - 总长度 ≤ 60KB（UTF-8 字节数）\n   - 不引用任何小度专属 API\n   - 必须包含 `<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">`\n\n   **目标运行环境 — Android 8.1 WebView（Chrome/85）**：\n   所有 HTML 最终运行在搭载 Android 8.1 的智能屏 WebView 中，无鼠标/硬键盘，仅支持触摸和左右滑动。\n   生成代码时必须遵守以下兼容规则：\n\n   **JS / Web API 兼容**（feature detection 优先，禁止仅凭 UA 判断）：\n   - 使用 `typeof IntersectionObserver !== 'undefined'` 等特性检测，不要直接调用可能不存在的 API\n   - 禁止使用 Chrome 86+ 才支持的 API：`CSS.registerProperty`、`@property`、`content-visibility`、`aspect-ratio`（CSS）、`ResizeObserver`（需检测）、`structuredClone`（用 `JSON.parse(JSON.stringify())` 替代）\n   - 禁止使用 ES2020+ 语法：`??=`、`||=`、`&&=`、`import()`动态导入、`BigInt`、`globalThis`（用 `window` 替代）\n   - **禁止使用 `ctx.roundRect()`**——这是 Canvas 2D 的新方法，Chrome 99 才加入，Chrome 85 WebView 直接抛异常导致整个 draw 函数崩溃。必须用以下自定义 helper 替代：\n     ```js\n     function rr(ctx, x, y, w, h, r) {\n       ctx.beginPath();\n       ctx.moveTo(x + r, y);\n       ctx.lineTo(x + w - r, y);  ctx.arcTo(x + w, y,     x + w, y + r,     r);\n       ctx.lineTo(x + w, y + h - r); ctx.arcTo(x + w, y + h, x + w - r, y + h, r);\n       ctx.lineTo(x + r, y + h);  ctx.arcTo(x,     y + h, x,     y + h - r, r);\n       ctx.lineTo(x, y + r);      ctx.arcTo(x,     y,     x + r, y,         r);\n       ctx.closePath();\n     }\n     // 用法：rr(ctx, x, y, w, h, radius); ctx.fill();\n     ```\n   - **禁止在 Canvas 内用 emoji 字符**（如 🌕🔥❤️ 等）——Android WebView 字体渲染异常会抛异常或显示方块。Canvas 内所有文字必须用纯 ASCII/中文文字，用几何图形代替 emoji（如\"♥ × 3\"改用红色圆形绘制）\n   - 可安全使用：`Promise`、`async/await`、`fetch`、`CSS Grid`、`Flexbox`、`CSS Variables`、`IntersectionObserver`（需检测）、`requestAnimationFrame`\n   - `localStorage` / `sessionStorage` 在 WebView 中可用，但不要依赖跨页面持久化\n\n   **CSS 兼容**：\n   - `100vh` 在 Android WebView 中等于视口高度（无地址栏问题），可直接使用\n   - `position: sticky` 在 Chrome 85 中支持，但父容器不能有 `overflow: hidden`，否则失效\n   - `position: fixed` 在 WebView 中软键盘弹出时可能错位，避免在输入框场景依赖 fixed 定位\n   - **禁止使用 CSS `inset` 属性**（`inset: 0` 等简写）——Chrome 87 才支持，Chrome 85 不识别，会导致遮罩层无法撑满、内容偏移到左上角。替换写法：`top:0; left:0; right:0; bottom:0`（注意：`box-shadow` 中的 `inset` 关键字不受此限制，可以正常使用）\n   - 禁止使用 `backdrop-filter`（Chrome 85 需前缀，WebView 不保证支持）\n   - `clamp()` 在 Chrome 85 中已支持，可正常使用\n   - 滚动容器加 `-webkit-overflow-scrolling: touch` 保证惯性滚动\n\n   **触摸与交互**：\n   - 所有可点击元素最小触摸区域 ≥ 44×44px\n   - 使用 `touchstart`/`touchmove`/`touchend` 处理手势；`click` 在 WebView 中有 300ms 延迟，交互敏感场景改用 touch 事件\n   - 消除 300ms 延迟：在 `<meta name=\"viewport\">` 中加 `user-scalable=no` 或在 CSS 中加 `touch-action: manipulation`\n   - 禁止依赖 `hover` 状态（无鼠标），交互反馈改用 `:active` 或 JS touch 事件\n   - 左右滑动手势用 `touchstart`/`touchend` 计算 `deltaX` 实现，不要用第三方手势库（CDN 可能不可达）\n   - 游戏类应用禁止使用键盘事件（`keydown`/`keyup`）作为唯一控制方式，必须提供触摸按钮\n\n   **软键盘与输入框**：\n   - 输入框获焦时 WebView 会 resize 视口，避免用 `height: 100vh` 做全屏布局的唯一约束\n   - 输入框弹出软键盘后 `fixed` 元素可能被遮挡，底部操作栏改用 `position: sticky` 或监听 `visualViewport.resize`\n   - 如需监听软键盘：`window.visualViewport` 在 Chrome 61+ 可用，Chrome 85 支持\n\n   **弹窗 / Modal**：\n   - 自定义弹窗用 `position: fixed; top:0; left:0; width:100%; height:100%` 覆盖全屏\n   - 弹窗内滚动区域加 `overflow-y: auto; -webkit-overflow-scrolling: touch`\n   - 禁止使用原生 `alert()`/`confirm()`/`prompt()`（WebView 中可能被宿主拦截）\n\n   - **横屏多设备适配**：目标设备均为横屏展示，尺寸差异较大：\n     | 设备 | 视口宽×高比例 |\n     |------|-----------|\n     | 普通智能屏 | 960×600 |\n     | mini智能屏 | 960×480 |\n     | 闺蜜机 | 1280×720 |\n     | 秋月 | 1280×800 |\n     | pad | 1920×1080 |\n     | pad17 | 2560×1600 |\n     布局以横屏为前提（宽 > 高），使用 `vw`/`vh`/百分比等响应式单位，禁止固定像素宽度导致小屏溢出或大屏留白。\n     字体大小建议用 `clamp()` 适配，如 `font-size: clamp(14px, 2vw, 18px)`。\n\n3. HTML 模板风格（按类型选择）：\n\n   **应用类** — 交互式：\n   - 纯单机离线游戏/工具，不依赖任何 server 服务\n   - **严禁实现 AI 对手 / 人机对战 / 电脑自动下棋等功能**，棋类游戏只做双人同屏轮流对战（两个玩家在同一屏幕上交替操作）\n   - 棋类游戏正确做法示例：五子棋 → 黑白双方轮流点击落子，判断五连胜负，无任何电脑决策代码；象棋 → 红黑双方轮流移动棋子\n   - 功能简单易上手，玩法直观，适合触屏快速操作\n   - 支持触屏操作（touch 事件）\n   - 美观的 UI 设计\n   - 根容器使用 `width:100vw; height:100vh; overflow:hidden` 撑满全屏\n   - 关键元素尺寸用 `vmin` 或百分比，确保 960px 小屏到 2560px 大屏均可用\n\n   **游戏类强制横版左右布局**（所有游戏类应用必须遵守）：\n\n   > ⚠️ **强制要求**：所有游戏页面必须是横版布局，整体分为左右两栏，**禁止竖版堆叠布局**。\n\n   布局结构如下：\n   ```\n   ┌─────────────────────────────┬──────────────────┐\n   │                             │  状态（必展示）    │\n   │                             │  关卡/得分/生命    │\n   │       游戏区域（左栏）        ├──────────────────┤\n   │    占总宽约 65%~70%          │  下一块/预览      │\n   │    高度撑满 100vh            ├──────────────────┤\n   │                             │ ◀ II ▶  方向键   │\n   └─────────────────────────────┴──────────────────┘\n   ```\n\n   **右栏布局规则（状态区和方向键始终可见）**：\n   - `#status`（关卡、难度、得分/目标分、生命）：`flex:0 0 auto`，**必须展示，不可压缩**\n   - `#game-preview`（下一块/预览区）：`flex:0 0 auto`，**有预览需求的游戏必须展示，不可压缩**\n   - `#leaderboard`（排行榜）和 `#instructions`（操作说明）：**当游戏有 `#game-preview` 时，不展示排行榜和操作说明**（右栏空间有限，预览区优先）。仅在无预览需求的游戏中，才用 `flex:1 1 0; min-height:0` + 标题 h3 固定 + `.sb-inner` 内容区 `overflow-y:auto` 独立滚动的方式展示\n   - `#controls`（方向键+暂停）：`flex:0 0 auto`，**固定在右栏底部，始终可见，不参与滚动**\n\n   CSS 骨架模板（必须使用）：\n   ```html\n   <div id=\"app\" style=\"width:100vw;height:100vh;display:flex;flex-direction:row;overflow:hidden;background:#1a1a2e;\">\n     <!-- 左栏：游戏区域，尽量大，canvas 撑满整个左栏，不留空白 -->\n     <div id=\"game-area\" style=\"flex:1;position:relative;min-width:0;\">\n       <canvas id=\"canvas\" style=\"display:block;width:100%;height:100%;\"></canvas>\n       <!-- 开始/结束/暂停 遮罩层：必须用以下写法居中，禁止只写 top:0;left:0 导致内容贴左上角 -->\n       <!-- <div id=\"overlay\" style=\"position:absolute;top:0;left:0;right:0;bottom:0;display:flex;flex-direction:column;\n            align-items:center;justify-content:center;text-align:center;\n            background:rgba(0,0,0,.6);z-index:10;\">\n         <h2 style=\"color:#fff;font-size:clamp(18px,3vw,32px);margin:0 0 2vh;\">游戏标题</h2>\n         <p style=\"color:#ccc;font-size:clamp(12px,1.8vw,18px);margin:0 0 3vh;\">操作说明文字</p>\n         <button style=\"padding:1.2vh 4vw;font-size:clamp(14px,2vw,20px);border-radius:2vmin;\n           border:none;background:#e74c3c;color:#fff;touch-action:manipulation;\">开始游戏</button>\n       </div> -->\n       <!-- ⚠️ 遮罩内所有文字/按钮必须通过 flexbox 居中，严禁使用固定 top/left 像素值定位 -->\n     </div>\n     <!-- 右栏：固定宽度，状态区+方向键始终可见 -->\n     <div id=\"sidebar\" style=\"width:26vw;min-width:180px;max-width:300px;height:100vh;\n          display:flex;flex-direction:column;padding:1.5vh 1.2vw;gap:0.8vh;box-sizing:border-box;overflow:hidden;\">\n       <!-- ① 游戏状态：必展示，不可压缩 -->\n       <div id=\"status\" style=\"flex:0 0 auto;font-size:clamp(10px,1.4vw,14px);\">关卡/得分/生命...</div>\n       <!-- ② 预览区（下一块等）：有预览需求时展示；若有预览区则不放排行榜和操作说明 -->\n       <div id=\"game-preview\" style=\"flex:1 1 0;min-height:0;\">...</div>\n       <!-- ③④ 排行榜+操作说明：仅在无预览区的游戏中使用（与 ② 二选一） -->\n       <!--\n       <div id=\"leaderboard\" style=\"flex:1 1 0;min-height:0;display:flex;flex-direction:column;font-size:clamp(10px,1.3vw,13px);\">\n         <h3 style=\"flex:0 0 auto;margin:0 0 0.4vh;\">排行榜</h3>\n         <div class=\"sb-inner\" style=\"flex:1 1 0;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;\n              scrollbar-width:thin;scrollbar-color:rgba(255,255,255,.3) transparent;\">\n         </div>\n       </div>\n       <div id=\"instructions\" style=\"flex:1 1 0;min-height:0;display:flex;flex-direction:column;font-size:clamp(10px,1.3vw,13px);\">\n         <h3 style=\"flex:0 0 auto;margin:0 0 0.4vh;\">操作说明</h3>\n         <div class=\"sb-inner\" style=\"flex:1 1 0;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;\n              scrollbar-width:thin;scrollbar-color:rgba(255,255,255,.3) transparent;\">\n         </div>\n       </div>\n       -->\n       <!-- ⑤ 方向键+暂停：固定在底部，始终可见 -->\n       <div id=\"controls\" style=\"flex:0 0 auto;padding-top:0.8vh;\">...</div>\n     </div>\n   </div>\n   ```\n   Canvas 尺寸初始化（JS 中必须这样写，确保 Android 8.1 WebView 中 canvas 像素与显示尺寸一致）：\n\n   > ⚠️ **Android 8.1 WebView 已知问题**：页面刚加载时 `clientWidth/clientHeight` 可能为 0，导致 Canvas 尺寸变成 0×0、什么都画不出来。必须用以下模板，通过 `window.load` + 双层 `requestAnimationFrame` 延迟读取尺寸，并用 `offsetWidth` 做兜底。\n\n   Canvas CSS（必须用 `position:absolute` 撑满，不依赖 flex 布局尺寸）：\n   ```html\n   <div id=\"game-area\" style=\"flex:1;position:relative;min-width:0;\">\n     <canvas id=\"canvas\" style=\"position:absolute;top:0;left:0;width:100%;height:100%;display:block;\"></canvas>\n   </div>\n   ```\n\n   Canvas 尺寸初始化（必须在 `window.load` + 双层 `requestAnimationFrame` 后执行）：\n   ```js\n   function initCanvas() {\n     const gameArea = document.getElementById('game-area');\n     const canvas = document.getElementById('canvas');\n     // offsetWidth 兜底：Android 8.1 WebView 刚加载时 clientWidth 可能为 0\n     const w = gameArea.clientWidth  || gameArea.offsetWidth  || window.innerWidth * 0.7;\n     const h = gameArea.clientHeight || gameArea.offsetHeight || window.innerHeight;\n     canvas.width  = w;\n     canvas.height = h;\n     // 格子大小根据 canvas 尺寸动态计算\n     const COLS = 30, ROWS = Math.floor(h / (w / COLS));\n     const cellW = w / COLS, cellH = h / ROWS;\n     // ... 其余游戏初始化逻辑\n   }\n\n   // 必须在 window.load 后、双层 rAF 确保布局渲染完毕再读尺寸\n   window.addEventListener('load', function() {\n     requestAnimationFrame(function() {\n       requestAnimationFrame(function() {\n         initCanvas();\n       });\n     });\n   });\n   ```\n\n   **游戏类 WebView 触摸适配要点**（Android 8.1 WebView 无键盘/鼠标）：\n\n   > ⚠️ **强制要求（主控方式）**：所有游戏的**主要控制方式必须是触摸坐标直接映射**——玩家手指按在画布哪里，角色/目标就跟到哪里（或朝那个方向移动），不需要抬手再点方向按钮。右下角的 ↑↓←→ 方向键和暂停键保留作为**辅助控制**，不是唯一控制入口。暂停按钮必须位于方向键 3×3 grid 的**正中心格**。\n\n   **触摸坐标映射实现模板**（贴在 Canvas/游戏容器上）：\n   ```js\n   // 主控：手指位置直接映射到游戏坐标\n   canvas.addEventListener('touchstart', onTouch, {passive: false});\n   canvas.addEventListener('touchmove',  onTouch, {passive: false});\n\n   function onTouch(e) {\n     e.preventDefault();\n     const rect = canvas.getBoundingClientRect();\n     const touch = e.touches[0];\n     // 将屏幕像素坐标换算为 canvas 内部坐标\n     const cx = (touch.clientX - rect.left) * (canvas.width  / rect.width);\n     const cy = (touch.clientY - rect.top)  * (canvas.height / rect.height);\n     movePlayerTo(cx, cy);   // 替换为游戏实际函数：直接设置目标坐标\n   }\n   ```\n   - 对于贪吃蛇、俄罗斯方块等离散格子类游戏，触摸映射改为**滑动方向**：`touchstart` 记录起点，`touchend` 计算 `deltaX/deltaY`，阈值 20px 内忽略，超过则取绝对值较大的轴作为方向\n   - 对于飞机大战、跑酷等连续坐标类游戏，用 `touchmove` 实时跟随手指坐标\n   - 左侧游戏遮罩/操作说明中必须用一句话说明触摸操作方式，例如：「触摸屏幕控制移动，手指在哪飞机就去哪」\n\n   **飞机大战类游戏专项规则**：\n   - **子弹必须自动连续发射**（`setInterval` 或游戏主循环中每隔固定帧数发射），**禁止要求玩家手动点击射击**\n   - 自动射频简单难度约 300ms/发，中等 200ms/发，困难 100ms/发\n   - 操作说明中写「触摸屏幕移动飞机，子弹自动发射」\n\n   **俄罗斯方块/消除类游戏专项规则**：\n   - **贴地即锁，零等待**：下落计时器触发时若方块无法继续下移，立刻执行 `lock()` → `spawn()`，下一块在同一帧内出现。**禁止 LOCK_DELAY 缓冲期**\n   - **`spawnPiece()` 必须将新方块赋值给全局变量**（如 `cur = shape`），否则 `drawGrid()` 中 `if(cur != null)` 判断为 false 导致方块不显示\n   - **经典 NES 速度标准**：第 1 关 800ms → 第 5 关 533ms → 第 10 关 333ms → 第 15 关 200ms → 第 20 关 100ms → 第 29 关+ 83ms（极限）\n   - 难度递增公式：`interval = Math.max(83, 800 - (level - 1) * 5 * (1000/60))`（每关减约 5 帧，60fps 基准）\n   - 预览区（下一块）在右栏 `#game-preview` 中展示，有预览时不展示排行榜和操作说明\n\n   **方向键 + 暂停按钮模板**（放在右栏 `#controls` 内，作为辅助控制）：\n\n   布局规则：**暂停按钮放在方向键 3×3 grid 的正中心格**，不单独放在键盘上方。整体视觉要炫酷：方向键用半透明玻璃质感，暂停键用蓝色渐变 + 光晕，`:active` 有明显按压反馈。\n\n   ```html\n   <div id=\"controls\" style=\"flex:0 0 auto;display:flex;flex-direction:column;align-items:center;\n        gap:0.8vh;touch-action:none;user-select:none;\">\n     <!-- 3×3 方向键grid，暂停在正中心 -->\n     <div style=\"display:grid;grid-template-columns:repeat(3,13vmin);grid-template-rows:repeat(3,13vmin);gap:1.2vmin;\">\n       <div></div>\n       <button class=\"dpad-btn\" id=\"btn-up\">▲</button>\n       <div></div>\n       <button class=\"dpad-btn\" id=\"btn-left\">◀</button>\n       <button class=\"dpad-btn\" id=\"btn-pause\">II</button>\n       <button class=\"dpad-btn\" id=\"btn-right\">▶</button>\n       <div></div>\n       <button class=\"dpad-btn\" id=\"btn-down\">▼</button>\n       <div></div>\n     </div>\n   </div>\n   <style>\n   /* 方向键：半透明玻璃质感 */\n   .dpad-btn {\n     background: linear-gradient(145deg, rgba(255,255,255,.22), rgba(255,255,255,.06));\n     border: 1.5px solid rgba(255,255,255,.35);\n     border-radius: 50%;\n     color: #fff;\n     font-size: 4.5vmin;\n     touch-action: manipulation;\n     display: flex;\n     align-items: center;\n     justify-content: center;\n     box-shadow: 0 2px 8px rgba(0,0,0,.4), inset 0 1px 0 rgba(255,255,255,.25);\n     cursor: pointer;\n     transition: transform .08s;\n   }\n   .dpad-btn:active {\n     background: linear-gradient(145deg, rgba(255,255,255,.45), rgba(255,255,255,.15));\n     transform: scale(.88);\n     box-shadow: 0 1px 4px rgba(0,0,0,.5), inset 0 1px 0 rgba(255,255,255,.3);\n   }\n   /* 暂停键与方向键样式完全一致，仅字号加粗 + 细边框区分；用 II 代替 ⏸ 符号（WebView 渲染兼容） */\n   #btn-pause {\n     font-size: 4.5vmin;\n     font-weight: bold;\n     border-color: rgba(255,255,255,.6);\n   }\n   </style>\n   <script>\n   document.getElementById('btn-up').addEventListener('touchstart',    e=>{e.preventDefault();changeDir(0,-1);},{passive:false});\n   document.getElementById('btn-down').addEventListener('touchstart',   e=>{e.preventDefault();changeDir(0, 1);},{passive:false});\n   document.getElementById('btn-left').addEventListener('touchstart',   e=>{e.preventDefault();changeDir(-1,0);},{passive:false});\n   document.getElementById('btn-right').addEventListener('touchstart',  e=>{e.preventDefault();changeDir( 1,0);},{passive:false});\n   document.getElementById('btn-pause').addEventListener('touchstart',  e=>{e.preventDefault();togglePause();},{passive:false});\n   // 桌面调试保留键盘\n   document.addEventListener('keydown', e=>{\n     if(e.key==='ArrowUp')    changeDir(0,-1);\n     if(e.key==='ArrowDown')  changeDir(0, 1);\n     if(e.key==='ArrowLeft')  changeDir(-1,0);\n     if(e.key==='ArrowRight') changeDir( 1,0);\n     if(e.key===' ')          togglePause();\n   });\n   </script>\n   ```\n   - `changeDir(dx, dy)` 和 `togglePause()` 替换为游戏实际函数名；坐标映射类游戏中方向键可改为触发\"持续向该方向移动\"逻辑\n   - 按钮尺寸用 `vmin` 单位，960px 小屏到 2560px 大屏均可用\n   - 暂停键必须在中心格，禁止移到键盘外部单独放置\n\n   **游戏角色视觉规范**（卡通可爱风格，禁止只画纯色圆点/方块）：\n   - 贪吃蛇：蛇头用圆角矩形 + 两个白色眼睛（小圆 + 黑色瞳孔）+ 根据朝向旋转；蛇身用渐变色圆角矩形，节节之间颜色略有变化；食物用苹果/草莓等图形（红色圆 + 绿色小叶子）\n   - 贪吃蛇蛇头绘制示例（Canvas 2D）：\n     ```js\n     function drawHead(ctx, x, y, w, h, dir) {\n       ctx.save();\n       ctx.translate(x + w/2, y + h/2);\n       const angle = {right:0, down:Math.PI/2, left:Math.PI, up:-Math.PI/2}[dir] || 0;\n       ctx.rotate(angle);\n       // 头部圆角矩形\n       ctx.fillStyle = '#2ecc71';\n       roundRect(ctx, -w/2, -h/2, w, h, w*0.3);\n       ctx.fill();\n       // 眼睛\n       [[-w*0.15, -h*0.15],[w*0.15, -h*0.15]].forEach(([ex,ey])=>{\n         ctx.fillStyle='#fff'; ctx.beginPath(); ctx.arc(ex,ey,w*0.12,0,Math.PI*2); ctx.fill();\n         ctx.fillStyle='#222'; ctx.beginPath(); ctx.arc(ex+w*0.03,ey,w*0.06,0,Math.PI*2); ctx.fill();\n       });\n       ctx.restore();\n     }\n     function roundRect(ctx,x,y,w,h,r){\n       ctx.beginPath();ctx.moveTo(x+r,y);ctx.lineTo(x+w-r,y);ctx.arcTo(x+w,y,x+w,y+r,r);\n       ctx.lineTo(x+w,y+h-r);ctx.arcTo(x+w,y+h,x+w-r,y+h,r);ctx.lineTo(x+r,y+h);\n       ctx.arcTo(x,y+h,x,y+h-r,r);ctx.lineTo(x,y+r);ctx.arcTo(x,y,x+r,y,r);ctx.closePath();\n     }\n     ```\n   - 俄罗斯方块：方块用渐变色 + 高光边框，不同形状用不同颜色\n   - 飞机大战：飞机用多边形或 SVG path 绘制，子弹用细长椭圆，敌机颜色与玩家区分；**子弹必须自动连续发射，禁止要求玩家手动点击射击**；操作说明写「触摸屏幕移动飞机，子弹自动发射」\n   - 通用原则：所有游戏角色必须有辨识度，能看出是什么，不能只是纯色几何形状\n\n   **平台跳跃 / 跑酷类游戏触摸控制规范**（马里奥、跑酷等左右移动+跳跃类游戏必须遵守）：\n\n   > 核心设计：左右半屏控制方向，上滑触发跳跃，斜滑（如右上方）同时触发移动+跳跃，完全模拟实体手柄的拇指操作。\n\n   ```js\n   let tSX = 0, tSY = 0; // 触摸起点，每次 touchstart 或跳跃触发后重置\n\n   canvas.addEventListener('touchstart', e => {\n     e.preventDefault();\n     const t = e.touches[0];\n     tSX = t.clientX; tSY = t.clientY;\n   }, {passive: false});\n\n   canvas.addEventListener('touchmove', e => {\n     e.preventDefault();\n     const t = e.touches[0];\n     const dx = t.clientX - tSX;\n     const dy = t.clientY - tSY; // 负值 = 向上\n\n     // 左右移动：根据手指当前位置在画布左/右半区判断方向\n     const rect = canvas.getBoundingClientRect();\n     if (t.clientX < rect.left + rect.width / 2) {\n       moveLeft();\n     } else {\n       moveRight();\n     }\n\n     // 上滑检测跳跃：阈值 20px，触发后立即重置 tSY 防止重复触发\n     if (dy < -20) {\n       jump();\n       tSY = t.clientY; // 重置参考点，允许同一次触摸连续触发（斜滑等）\n     }\n   }, {passive: false});\n\n   canvas.addEventListener('touchend', e => {\n     stopMove(); // 手指抬起停止移动\n   }, {passive: false});\n   ```\n   - `dy < -20`（负值向上）触发跳跃，同一次 `touchmove` 序列可多次触发（重置 tSY）\n   - 斜向上滑（如右上方）会同时执行 `moveRight()` + `jump()`，天然支持斜跳\n   - 阈值 20px 优于 28px，更灵敏，适合快节奏横板游戏\n   - 右栏方向键：◀▶ 控制移动，▲ 触发跳跃，▼ 可用于下蹲/快速下落\n   - 触摸坐标映射为主控，方向键为辅助——两套控制并行，不互斥\n   - 防止页面滚动干扰游戏：游戏容器加 `touch-action:none`，Canvas 加 `{ passive: false }` 并 `e.preventDefault()`\n   - 游戏暂停/重启按钮触摸区域 ≥ 44×44px\n   - 禁止在游戏主循环中使用 `alert()`，改用页面内自定义弹窗显示胜负信息\n\n   **跳跃 / 平台类游戏角色定位规范**（跳一跳、跑酷、马里奥等必须遵守）：\n\n   > ⚠️ **已知 Bug 根因**：用 `ph = PLAYER_H / squish` 作为绘制高度来定位头部/身体，当落地弹跳动画触发 `squish ≠ 1` 时，`ph` 变大，角色视觉上\"沉入\"平台下方。\n\n   **正确做法**：`py` 始终表示角色**脚底**的 y 坐标（即站在平台顶面），`squish` 只影响**宽度**（身体变宽/变窄），绝不影响高度定位：\n   ```js\n   // py = 脚底 y（始终贴平台顶面，不受 squish 影响）\n   // squish: 落地压缩系数，1=正常，>1=横向压扁（宽变大、高不变）\n   function drawPlayer(ctx, px, py, squish) {\n     const W = PLAYER_W * squish;   // 宽度随 squish 变化\n     const H = PLAYER_H;            // 高度固定，不除以 squish\n     const x = px - W / 2;\n     const y = py - H;              // 脚底 py 往上量固定 PLAYER_H\n     // 绘制身体：rr(ctx, x, y, W, H, radius)\n   }\n   ```\n   - `squish` 动画只修改 `W`（或同时等比缩小 `H` 但必须同步上移 `y = py - H`），确保脚底始终在 `py`\n   - 禁止写 `y = py - PLAYER_H / squish`，这会在 squish > 1 时让角色上移，squish < 1 时下沉\n\n\n\n   > ⚠️ **强制要求**：所有游戏类应用必须内置难度阶段机制，**默认从简单难度开始**，通过 10 关后自动升级为中等难度，通过 20 关后升级为困难。以飞机大战为参照基准，其他游戏按比例换算。\n\n   **难度分级标准**（以飞机大战为基准示例）：\n   | 阶段 | 关卡范围 | 敌机坠落速度 | 子弹发射间隔 | 过关目标分 | 每关目标分增量 |\n   |------|---------|------------|------------|---------|------------|\n   | 简单（Easy）  | 第 1~10 关  | 基础速度 × 1/2 | 300ms/发 | 100 分 | +10 分/关 |\n   | 中等（Medium）| 第 11~20 关 | 基础速度 × 80% | 200ms/发 | 200 分 | +10 分/关 |\n   | 困难（Hard）  | 第 21 关起  | 基础速度  | 100ms/发 | 300 分 | +10 分/关 |\n\n   - **难度参数必须用变量控制**，根据关卡号计算，禁止魔法数字硬编码：\n     ```js\n     let level = 1; // 每通关 +1\n\n     function getDifficulty(level) {\n       if (level <= 10) return {\n         speedMul: 1/2,  bulletInterval: 300,\n         targetScore: 100 + (level - 1) * 10   // 100, 110, 120...\n       };\n       if (level <= 20) return {\n         speedMul: 0.8,  bulletInterval: 200,\n         targetScore: 200 + (level - 11) * 10  // 200, 210, 220...\n       };\n       return {\n         speedMul: 1,  bulletInterval: 100,\n         targetScore: 300 + (level - 21) * 10  // 300, 310, 320...\n       };\n     }\n     ```\n   - **不设硬性关卡上限**，进入困难阶段后目标分持续增加，让玩家可以持续挑战\n   - 关卡信息（当前关卡、当前难度名称、得分 / 目标分、生命）必须在右栏顶部实时显示\n\n   **文档/Word/PDF 风格**：\n   - 容器使用响应式宽度：`max-width: min(1200px, 92vw); margin: 3vh auto; padding: 4vh 5vw`\n   - 在 960px 小屏上内容几乎满宽，在 1920px+ 大屏上居中且不超过 1200px\n   - 正文字号 `clamp(14px, 1.8vw, 18px)`，标题 `clamp(20px, 3vw, 32px)`\n   - 清晰的标题/段落/列表排版，使用衬线或无衬线字体\n   - 页面顶部可显示文档标题和日期\n\n   **PPT/演示文稿风格**：\n   - 引入 reveal.js：`<link rel=\"stylesheet\" href=\"https://unpkg.com/reveal.js@5/dist/reveal.css\">`\n   - 引入主题：`<link rel=\"stylesheet\" href=\"https://unpkg.com/reveal.js@5/dist/theme/white.css\">`\n   - 引入 JS：`<script src=\"https://unpkg.com/reveal.js@5/dist/reveal.js\"></script>`\n   - 内容用 `<div class=\"reveal\"><div class=\"slides\"><section>...</section></div></div>` 包裹\n   - 每个 `<section>` 是一页幻灯片\n   - 初始化：`<script>Reveal.initialize({hash:true, touch:true, width:960, height:600});</script>`\n     reveal.js 内部会等比缩放到实际视口，960×600 基准保证所有设备文字清晰不溢出\n   - 5~15 页为宜\n\n   **Excel/表格风格**：\n   - 容器 `width:100vw; height:100vh; overflow:auto; box-sizing:border-box; padding:2vh 3vw`\n   - HTML `<table>` 宽度 100%，带 border 和交替行色\n   - 表头字号 `clamp(12px, 1.6vw, 16px)`，单元格字号 `clamp(12px, 1.4vw, 15px)`\n   - 表头点击可排序（内联 JS 实现）\n   - 单元格 `contenteditable=\"true\"` 可编辑\n\n   **所有类型通用禁止项**：\n   - 禁止出现\"导出\"、\"打印\"、\"保存\"功能按钮\n   - 禁止 `@media print` 相关样式\n   - 禁止 `window.print()`、文件下载等功能\n\n4. 图标生成（icon_base64）：\n   - 产出一个 256×256 的极简 SVG 图标\n   - 风格：圆角矩形纯色背景 + 居中的白色简笔图形（用 SVG path/circle/rect 绘制，**禁止使用 `<text>` 或 emoji**）\n   - 色彩鲜明，背景色与应用/文件主题相关，图形用白色（fill=\"#fff\"）\n   - SVG 文本做 base64 编码后传入 icon_base64（不带 `data:image/svg+xml;base64,` 前缀）\n   - 服务端会自动将 SVG 转换为 PNG 存储，无需 Agent 处理\n   - SVG 原文不超过 3KB\n   - **重要**：不要使用 `<text>` 元素或 emoji 字符，服务端渲染不支持字体，会变成黑色方块\n   - 图形设计原则：用 2~4 个简单几何元素组合表达应用主题，线条粗细 8~12px，保持简洁\n   - 各类型模板参考（直接替换背景色和图形路径）：\n\n     **游戏类**（贪吃蛇 — 绿色背景 + 蛇形曲线）：\n     ```\n     <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"256\" height=\"256\"><rect width=\"256\" height=\"256\" rx=\"48\" fill=\"#2ECC71\"/><path d=\"M80 160 Q100 120 128 140 Q156 160 176 120 Q196 80 176 60\" fill=\"none\" stroke=\"#fff\" stroke-width=\"12\" stroke-linecap=\"round\"/><circle cx=\"176\" cy=\"60\" r=\"10\" fill=\"#fff\"/></svg>\n     ```\n\n     **文档类**（报告 — 蓝色背景 + 文档图形）：\n     ```\n     <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"256\" height=\"256\"><rect width=\"256\" height=\"256\" rx=\"48\" fill=\"#3498DB\"/><rect x=\"80\" y=\"56\" width=\"96\" height=\"144\" rx=\"8\" fill=\"none\" stroke=\"#fff\" stroke-width=\"10\"/><line x1=\"100\" y1=\"96\" x2=\"156\" y2=\"96\" stroke=\"#fff\" stroke-width=\"8\" stroke-linecap=\"round\"/><line x1=\"100\" y1=\"120\" x2=\"156\" y2=\"120\" stroke=\"#fff\" stroke-width=\"8\" stroke-linecap=\"round\"/><line x1=\"100\" y1=\"144\" x2=\"136\" y2=\"144\" stroke=\"#fff\" stroke-width=\"8\" stroke-linecap=\"round\"/></svg>\n     ```\n\n     **PPT类**（演示 — 紫色背景 + 幻灯片图形）：\n     ```\n     <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"256\" height=\"256\"><rect width=\"256\" height=\"256\" rx=\"48\" fill=\"#9B59B6\"/><rect x=\"60\" y=\"72\" width=\"136\" height=\"96\" rx=\"8\" fill=\"none\" stroke=\"#fff\" stroke-width=\"10\"/><line x1=\"128\" y1=\"168\" x2=\"128\" y2=\"192\" stroke=\"#fff\" stroke-width=\"10\" stroke-linecap=\"round\"/><line x1=\"100\" y1=\"192\" x2=\"156\" y2=\"192\" stroke=\"#fff\" stroke-width=\"10\" stroke-linecap=\"round\"/></svg>\n     ```\n\n     **表格类**（数据 — 橙色背景 + 网格图形）：\n     ```\n     <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"256\" height=\"256\"><rect width=\"256\" height=\"256\" rx=\"48\" fill=\"#E67E22\"/><rect x=\"72\" y=\"72\" width=\"112\" height=\"112\" rx=\"8\" fill=\"none\" stroke=\"#fff\" stroke-width=\"10\"/><line x1=\"72\" y1=\"112\" x2=\"184\" y2=\"112\" stroke=\"#fff\" stroke-width=\"8\"/><line x1=\"72\" y1=\"148\" x2=\"184\" y2=\"148\" stroke=\"#fff\" stroke-width=\"8\"/><line x1=\"120\" y1=\"72\" x2=\"120\" y2=\"184\" stroke=\"#fff\" stroke-width=\"8\"/></svg>\n     ```\n\n     **工具类**（计算器/时钟 — 青色背景 + 齿轮图形）：\n     ```\n     <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"256\" height=\"256\"><rect width=\"256\" height=\"256\" rx=\"48\" fill=\"#1ABC9C\"/><circle cx=\"128\" cy=\"128\" r=\"40\" fill=\"none\" stroke=\"#fff\" stroke-width=\"10\"/><line x1=\"128\" y1=\"128\" x2=\"128\" y2=\"100\" stroke=\"#fff\" stroke-width=\"8\" stroke-linecap=\"round\"/><line x1=\"128\" y1=\"128\" x2=\"148\" y2=\"140\" stroke=\"#fff\" stroke-width=\"8\" stroke-linecap=\"round\"/></svg>\n     ```\n\n   - 根据具体应用主题自由调整：更换背景色、修改 path 形状来表达应用特征\n   - 背景色建议：游戏用暖色/亮色（绿、红、橙），文档用冷色（蓝、靛），工具用中性色（青、灰蓝）\n\n5. **【必须执行】HTML 自检 — 提交前逐项检查**：\n   生成 html_content 后，**必须**在脑内逐项检查以下条件，任一不通过则必须修改代码后再提交：\n\n   **自检项 A — 禁止 AI/电脑对手**（仅游戏/棋类/对战类需检查）：\n   - [ ] 代码中是否存在任何\"电脑走棋/出牌/移动\"的函数？（如 `computerMove`, `aiMove`, `makeAIMove`, `botPlay`, `autoPlay`, `cpuTurn`）→ 若有，**删除整个函数及其调用**\n   - [ ] 代码中是否存在 `minimax`, `alphaBeta`, `evaluate`, `bestMove`, `getRandomMove`, `Math.random()` 用于选择落子位置？→ 若有，**删除**\n   - [ ] 代码中是否存在\"难度选择\"（easy/medium/hard）或\"人机/人人\"模式切换？→ 若有，**删除，只保留双人模式**\n   - [ ] 游戏流程是否为：玩家A操作 → 切换到玩家B → 玩家B操作 → 切换回玩家A → 循环？→ 必须是\n   - [ ] 是否有任何 `setTimeout`/`setInterval` 用于延迟执行电脑的操作？→ 若有，**删除**\n\n   **自检项 B — 禁止导出/打印/保存**（所有类型需检查）：\n   - [ ] 是否存在\"导出\"、\"打印\"、\"保存\"、\"下载\"按钮或菜单项？→ 若有，**删除**\n   - [ ] 是否存在 `window.print()`, `document.execCommand('SaveAs')`, `URL.createObjectURL`, `download` 属性？→ 若有，**删除**\n\n   **自检项 C — Android 8.1 WebView 兼容**（所有类型需检查）：\n   - [ ] 是否使用了 Chrome 86+ 才支持的 CSS/JS API（`@property`、`content-visibility`、`structuredClone`、`??=`/`||=`/`&&=` 赋值运算符）？→ 若有，**替换为兼容写法**\n   - [ ] 是否使用了 `ctx.roundRect()`？→ Chrome 99+，**必须替换为自定义 `rr()` helper（用 arcTo 实现）**\n   - [ ] Canvas 内是否使用了 emoji 字符（fillText 中含 🔥❤️ 等）？→ WebView 字体渲染异常，**必须替换为纯文字或几何图形**\n   - [ ] 游戏布局是否为横版左右两栏（左栏游戏区、右栏控制区）？→ **若是竖版堆叠，必须改为横版**\n   - [ ] 右栏有 `#game-preview`（下一块/预览区）时，是否已去掉 `#leaderboard` 和 `#instructions`？→ **预览区与排行/说明互斥，有预览时不展示排行和说明**。无预览区的游戏中，排行/说明用 `flex:1 1 0;min-height:0` + h3 固定 + `.sb-inner` 内滚动。`#controls` 始终 `flex:0 0 auto` 固定底部\n   - [ ] Canvas CSS 是否用 `position:absolute;top:0;left:0;width:100%;height:100%` 撑满左栏，而非依赖 flex 布局？→ **若只用 `width:100%;height:100%` 而无 `position:absolute`，必须修正**\n   - [ ] Canvas 尺寸初始化是否在 `window.load` + 双层 `requestAnimationFrame` 内执行，且用 `offsetWidth` 做兜底（防止 Android 8.1 WebView 初始 `clientWidth` 为 0）？→ **若直接在顶层读 `clientWidth`，必须改为延迟初始化模板**\n   - [ ] 需要方向控制的游戏：是否在 Canvas 上绑定了 `touchstart`/`touchmove` 做坐标直接映射（主控），且右下角仍保留方向键 + 暂停按钮（辅助）？→ **若只有方向按钮而无触摸坐标映射，必须补充**\n   - [ ] 飞机大战类游戏：子弹是否自动连续发射（`setInterval` 或主循环计时），而非要求玩家手动点击射击？→ **若需要手动触发，必须改为自动发射**\n   - [ ] 俄罗斯方块/消除类游戏：①初始下落间隔是否为800ms（经典NES速度）？②贴地后是否立刻lock+spawn（零等待，无LOCK_DELAY缓冲）？③`spawnPiece()`是否将新方块赋值给全局变量（`cur=shape`）？→ **若不符合，按专项规则修正**\n   - [ ] 游戏内的开始/结束/暂停遮罩层是否用 `position:absolute;top:0;left:0;right:0;bottom:0;display:flex;align-items:center;justify-content:center` 居中（**禁止用 `inset:0`，Chrome 85 不支持**）？→ **若用 `inset:0` 或固定像素定位，内容会在智能屏上贴左上角，必须修正**\n   - [ ] 游戏是否实现了三段式难度（简单1~10关：速度×1/2、300ms/发、100+10×n分 → 中等11~20关：速度×80%、200ms/发、200+10×n分 → 困难21关起：基础速度、100ms/发、300+10×n分），且第1关使用简单参数？→ **若全程同一难度或开局就很快，必须按难度分级模板修正**\n   - [ ] 游戏角色是否有视觉辨识度（蛇头有眼睛、食物有形状等），而不是纯色圆点/方块？→ **若只是纯色几何形，必须改为卡通风格**\n   - [ ] 是否使用了原生 `alert()`/`confirm()`/`prompt()`？→ 若有，**改为页面内自定义弹窗**\n   - [ ] 游戏容器/Canvas 是否加了 `touch-action:none` 并在 touch 事件中调用 `e.preventDefault()`？→ 若未处理，**补充**\n   - [ ] 可点击/可触摸元素的触摸区域是否 ≥ 44×44px？→ 若不足，**调整尺寸**\n   - [ ] 跳跃/平台类游戏（跳一跳、跑酷等）：绘制角色时是否用固定常量 `PLAYER_H` 定位脚底坐标（`py` 为脚底 y，头顶为 `py - PLAYER_H`），而非用 `PLAYER_H / squish` 等变形高度定位？→ **`squish`（落地压缩动画）只能影响角色宽度/变窄，绝不能影响脚底 `py` 的计算，否则 squish≠1 时角色视觉上会\"沉入\"平台下方**\n\n   ⚠️ 如果自检发现违规代码，**不要提交**，先修正 html_content 再继续下一步。\n\n5.5. **【必须执行】JS 语法验证**（应用类/游戏类必须，文件类可选）：\n   在调用 `myapp_register` 之前，用 `exec` 工具对 html_content 中的 `<script>` 代码块做语法验证，确保不因语法错误导致游戏在智能屏上完全空白：\n   ```bash\n   # 提取 <script> 内容写临时文件，用 node --check 验证\n   node -e \"\n   const fs = require('fs');\n   const html = fs.readFileSync('/tmp/_skill_check.html','utf8');\n   const scripts = html.match(/<script[\\s\\S]*?>([\\s\\S]*?)<\\/script>/gi) || [];\n   scripts.forEach((s,i) => {\n     const code = s.replace(/<\\/?script[^>]*>/gi,'');\n     fs.writeFileSync('/tmp/_check_'+i+'.js', code);\n   });\n   \" && node --check /tmp/_check_*.js 2>&1\n   ```\n   - 实际操作：将 html_content 写入 `/tmp/_skill_check.html`，运行上面命令\n   - 如果 `node --check` 输出任何错误，必须修复对应 JS 代码后重新验证，直到无错误\n   - 验证通过后再调用 `myapp_register`\n\n6. 调用 `myapp_register` 工具：\n   - 入参：`dumi_id, cuid, query, app_name, html_content, features, icon_base64, tag`\n   - `tag`：根据第 1 步判断的类型填写——**应用类（交互式）填 `\"app\"`，文件类（文档/PPT/表格）填 `\"doc\"`**；不传时服务端默认 `\"app\"`\n   - `dumi_id` 与 `cuid` 从对话上下文中获取（OpenClaw 会注入）\n\n7. 工具返回成功后，向用户展示：\n   - 告知创建完成：\"《<app_name>》已创建完成。\"\n   - 展示功能/玩法（根据类型区分）：\n     - **应用类**：\n       - 列出\"游戏特性\"或\"功能特性\"：从 features 中提取 3~6 条核心点\n       - 列出\"操作方式\"：说明触屏/键盘的操作方法（如方向键控制、暂停等）\n     - **文件类**（文档/PPT/表格）：\n       - 简要概括文档核心内容（如关键数据、主要结论、章节概要等，3~5 条）\n       - 提示\"点击链接即可查看完整内容\"\n   - 展示文件位置：给出返回的 `html_url` 链接\n   - 提示用户：\"下次可以在「我的创作」页面查看和管理你创建的所有应用\"\n\n8. 失败/超长/超出能力 → 回复\"应用创建失败，请稍后重试\"\n   - **不要重试**，**不要伪造成功**，**不要把 html_content 输出到对话**\n\n## 更新流程\n\n最长等待 300s。若 300s 内不能完成旧功能读取、HTML 重新生成与 `myapp_update` 调用，直接回复\"应用更新失败，请稍后重试\"。\n\n1. **先调 `myapp_get(app_id)`** 拿到 `app_name` 与旧 `features`、旧 `query`\n2. 把\"旧 features + 用户新需求\"作为完整上下文，重新生成：\n   - 新 features（合并增删，仍是 1~12 条全集，不是 diff）\n   - 新 html_content（保持原有类型风格）\n   - 若应用主题/类型发生变化，重新生成 icon_base64（同创建流程第 4 步）\n3. 调用 `myapp_update(app_id, query, html_content, features, icon_base64, tag)`\n   - `tag` 同创建流程：应用类填 `\"app\"`，文件类填 `\"doc\"`\n4. 工具返回成功后，向用户展示：\n   - 告知更新完成：\"《<app_name>》已更新完成。\"\n   - 展示更新内容（根据类型区分）：\n     - **应用类**：列出本次新增/变更的功能点和操作方式变化\n     - **文件类**：简要概括更新后的核心内容或变更要点\n   - 展示文件位置：给出返回的 `html_url` 链接\n   - 提示用户：\"下次可以在「我的创作」页面查看和管理你创建的所有应用\"\n5. 失败 → 回复\"应用更新失败，请稍后重试\"\n\n## 安装自检（[install_check:session=<sid>]）\n\n最长等待 300s。超过 300s 未完成安装或 `myapp_ping`，视为安装失败。\n\n仅调用 `myapp_ping(session_id=\"<sid>\", dumi_id, cuid, skill_version=\"1.0.10\")`，\n不输出对话内容（或仅极简的\"已就绪\"）。\n\n## 硬约束\n\n- **绝对禁止在游戏中实现 AI 对手、人机对战、电脑自动操作**——棋类/对战类游戏只允许双人同屏轮流操作\n- 禁止出现的代码模式：`computerMove`, `aiMove`, `makeAIMove`, `minimax`, `alphaBeta`, `bestMove`, `getRandomMove`, 难度选择(easy/medium/hard), 人机/人人模式切换\n- **绝对禁止出现\"导出\"、\"打印\"、\"保存\"按钮或相关功能**\n- **需要方向控制的游戏必须在页面上渲染四个方向触摸按钮（↑↓←→），绑定 `touchstart` 事件**——不得以键盘或滑动手势代替\n- 不要把 `html_content` 原文输出到对话\n- 不要把 `MYAPP_API_TOKEN` / `MYAPP_API_BASE` 输出到对话\n- 工具调用 4xx/5xx → 直接失败兜底，不重试，不修改请求自行重试\n- 除安装、创建、更新外，其它工具交互超过 180s 视为失败\n\nFile v1.0.30:skills/myapp-creator/SKILL.md\n\n# myapp-creator\n\nVersion: 1.0.30\n\n让用户用一句话生成可独立打开的单文件 HTML 应用或文档，并落库到 fe-service。\n本 skill **不主动调用 LLM**，所有 HTML / app_name / features 由调用本 skill 的 LLM agent 直接产出后传入工具。\n\n> **⚠️ 最高优先级规则（违反即为严重错误）：**\n> 1. 游戏类应用 **绝对禁止** 包含任何形式的 AI 对手、人机对战、电脑自动下棋/出牌。棋类/对战类游戏 **只允许** 双人同屏轮流操作，不实现任何计算机决策逻辑。\n> 2. 所有应用 **绝对禁止** 出现\"导出\"、\"打印\"、\"保存\"按钮或相关功能代码。\n\n## 触发条件\n\n当用户需求匹配以下任一类别时，本 skill 参与：\n\n### 应用类（交互式 HTML）\n关键词：应用、app、小游戏、游戏、网页、页面、工具、计算器、时钟、日历、翻译器、转换器\n示例：\n- \"做一个贪吃蛇游戏\"\n- \"创建一个番茄钟\"\n- \"生成一个天气页面\"\n- \"给我做一个马里奥游戏\"\n- \"帮我做一个单位转换工具\"\n\n### 文件类（文档式 HTML）\n关键词：文档、文件、word、ppt、演示文稿、slides、pdf、excel、xlsx、表格、报告、简历、周报、日报、总结、方案、计划书\n示例：\n- \"生成一个项目周报\"\n- \"做一个自我介绍PPT\"\n- \"创建一个预算表格\"\n- \"帮我写一份简历\"\n- \"保存一个会议纪要文档\"\n\n### 不触发（交给龙虾其他能力）\n- 图片/绘画类：\"画一张…\"、\"生成一张图片…\"\n- 音频/音乐类：\"生成一首歌…\"、\"合成语音…\"\n- 视频类：\"剪辑一个…\"、\"生成视频…\"\n- 小说/纯文本类：\"写一篇小说…\"、\"写一首诗…\"（纯创作不产出可交互文件）\n- 纯问答/聊天/闲聊\n\n### 判断规则\n用户意图是\"产出一个可独立打开、保存下来反复使用的文件或应用\"时触发本 skill。\n如果用户只是想让你\"说一段话\"或\"回答问题\"，不要触发。\n\n## 触发标记（兼容旧入口）\n\n以下前缀仍可直接触发，不需要语义判断：\n- `[create_app]<描述>` → 创建新应用/文件\n- `[update_app:app_id=<n>]<描述>` → 更新已有应用/文件\n- `[install_check:session=<sid>]` → 安装自检（仅调 myapp_ping）\n- 不带上述前缀 → 按语义判断是否匹配触发条件\n\n## 创建流程\n\n最长等待 300s。若 300s 内不能完成 HTML 生成与 `myapp_register` 调用，直接回复\"应用创建失败，请稍后重试\"。\n\n1. 解析用户描述，判断类型（应用类 or 文件类），产出：\n   - `app_name`：应用/文件名，≤30 字\n   - `features`：JSON 字符串数组，3~8 条，每条 ≤30 字，描述具体功能/内容要点\n   - `html_content`：完整可独立打开的单文件 HTML（按类型选择模板风格）\n\n2. HTML 通用约束：\n   - 自包含：CSS/JS 全部内联在 `<style>` / `<script>`\n   - 不依赖本地资源\n   - 外部资源仅允许：unpkg.com / jsdelivr.net / cdnjs.cloudflare.com\n   - 总长度 ≤ 60KB（UTF-8 字节数）\n   - 不引用任何小度专属 API\n   - 必须包含 `<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">`\n\n   **目标运行环境 — Android 8.1 WebView（Chrome/85）**：\n   所有 HTML 最终运行在搭载 Android 8.1 的智能屏 WebView 中，无鼠标/硬键盘，仅支持触摸和左右滑动。\n   生成代码时必须遵守以下兼容规则：\n\n   **JS / Web API 兼容**（feature detection 优先，禁止仅凭 UA 判断）：\n   - 使用 `typeof IntersectionObserver !== 'undefined'` 等特性检测，不要直接调用可能不存在的 API\n   - 禁止使用 Chrome 86+ 才支持的 API：`CSS.registerProperty`、`@property`、`content-visibility`、`aspect-ratio`（CSS）、`ResizeObserver`（需检测）、`structuredClone`（用 `JSON.parse(JSON.stringify())` 替代）\n   - 禁止使用 ES2020+ 语法：`??=`、`||=`、`&&=`、`import()`动态导入、`BigInt`、`globalThis`（用 `window` 替代）\n   - **禁止使用 `ctx.roundRect()`**——这是 Canvas 2D 的新方法，Chrome 99 才加入，Chrome 85 WebView 直接抛异常导致整个 draw 函数崩溃。必须用以下自定义 helper 替代：\n     ```js\n     function rr(ctx, x, y, w, h, r) {\n       ctx.beginPath();\n       ctx.moveTo(x + r, y);\n       ctx.lineTo(x + w - r, y);  ctx.arcTo(x + w, y,     x + w, y + r,     r);\n       ctx.lineTo(x + w, y + h - r); ctx.arcTo(x + w, y + h, x + w - r, y + h, r);\n       ctx.lineTo(x + r, y + h);  ctx.arcTo(x,     y + h, x,     y + h - r, r);\n       ctx.lineTo(x, y + r);      ctx.arcTo(x,     y,     x + r, y,         r);\n       ctx.closePath();\n     }\n     // 用法：rr(ctx, x, y, w, h, radius); ctx.fill();\n     ```\n   - **禁止在 Canvas 内用 emoji 字符**（如 🌕🔥❤️ 等）——Android WebView 字体渲染异常会抛异常或显示方块。Canvas 内所有文字必须用纯 ASCII/中文文字，用几何图形代替 emoji（如\"♥ × 3\"改用红色圆形绘制）\n   - 可安全使用：`Promise`、`async/await`、`fetch`、`CSS Grid`、`Flexbox`、`CSS Variables`、`IntersectionObserver`（需检测）、`requestAnimationFrame`\n   - `localStorage` / `sessionStorage` 在 WebView 中可用，但不要依赖跨页面持久化\n\n   **CSS 兼容**：\n   - `100vh` 在 Android WebView 中等于视口高度（无地址栏问题），可直接使用\n   - `position: sticky` 在 Chrome 85 中支持，但父容器不能有 `overflow: hidden`，否则失效\n   - `position: fixed` 在 WebView 中软键盘弹出时可能错位，避免在输入框场景依赖 fixed 定位\n   - **禁止使用 CSS `inset` 属性**（`inset: 0` 等简写）——Chrome 87 才支持，Chrome 85 不识别，会导致遮罩层无法撑满、内容偏移到左上角。替换写法：`top:0; left:0; right:0; bottom:0`（注意：`box-shadow` 中的 `inset` 关键字不受此限制，可以正常使用）\n   - 禁止使用 `backdrop-filter`（Chrome 85 需前缀，WebView 不保证支持）\n   - `clamp()` 在 Chrome 85 中已支持，可正常使用\n   - 滚动容器加 `-webkit-overflow-scrolling: touch` 保证惯性滚动\n\n   **触摸与交互**：\n   - 所有可点击元素最小触摸区域 ≥ 44×44px\n   - 使用 `touchstart`/`touchmove`/`touchend` 处理手势；`click` 在 WebView 中有 300ms 延迟，交互敏感场景改用 touch 事件\n   - 消除 300ms 延迟：在 `<meta name=\"viewport\">` 中加 `user-scalable=no` 或在 CSS 中加 `touch-action: manipulation`\n   - 禁止依赖 `hover` 状态（无鼠标），交互反馈改用 `:active` 或 JS touch 事件\n   - 左右滑动手势用 `touchstart`/`touchend` 计算 `deltaX` 实现，不要用第三方手势库（CDN 可能不可达）\n   - 游戏类应用禁止使用键盘事件（`keydown`/`keyup`）作为唯一控制方式，必须提供触摸按钮\n\n   **软键盘与输入框**：\n   - 输入框获焦时 WebView 会 resize 视口，避免用 `height: 100vh` 做全屏布局的唯一约束\n   - 输入框弹出软键盘后 `fixed` 元素可能被遮挡，底部操作栏改用 `position: sticky` 或监听 `visualViewport.resize`\n   - 如需监听软键盘：`window.visualViewport` 在 Chrome 61+ 可用，Chrome 85 支持\n\n   **弹窗 / Modal**：\n   - 自定义弹窗用 `position: fixed; top:0; left:0; width:100%; height:100%` 覆盖全屏\n   - 弹窗内滚动区域加 `overflow-y: auto; -webkit-overflow-scrolling: touch`\n   - 禁止使用原生 `alert()`/`confirm()`/`prompt()`（WebView 中可能被宿主拦截）\n\n   - **横屏多设备适配**：目标设备均为横屏展示，尺寸差异较大：\n     | 设备 | 视口宽×高比例 |\n     |------|-----------|\n     | 普通智能屏 | 960×600 |\n     | mini智能屏 | 960×480 |\n     | 闺蜜机 | 1280×720 |\n     | 秋月 | 1280×800 |\n     | pad | 1920×1080 |\n     | pad17 | 2560×1600 |\n     布局以横屏为前提（宽 > 高），使用 `vw`/`vh`/百分比等响应式单位，禁止固定像素宽度导致小屏溢出或大屏留白。\n     字体大小建议用 `clamp()` 适配，如 `font-size: clamp(14px, 2vw, 18px)`。\n\n3. HTML 模板风格（按类型选择）：\n\n   **应用类** — 交互式：\n   - 纯单机离线游戏/工具，不依赖任何 server 服务\n   - **严禁实现 AI 对手 / 人机对战 / 电脑自动下棋等功能**，棋类游戏只做双人同屏轮流对战（两个玩家在同一屏幕上交替操作）\n   - 棋类游戏正确做法示例：五子棋 → 黑白双方轮流点击落子，判断五连胜负，无任何电脑决策代码；象棋 → 红黑双方轮流移动棋子\n   - 功能简单易上手，玩法直观，适合触屏快速操作\n   - 支持触屏操作（touch 事件）\n   - 美观的 UI 设计\n   - 根容器使用 `width:100vw; height:100vh; overflow:hidden` 撑满全屏\n   - 关键元素尺寸用 `vmin` 或百分比，确保 960px 小屏到 2560px 大屏均可用\n\n   **游戏类强制横版左右布局**（所有游戏类应用必须遵守）：\n\n   > ⚠️ **强制要求**：所有游戏页面必须是横版布局，整体分为左右两栏，**禁止竖版堆叠布局**。\n\n   布局结构如下：\n   ```\n   ┌─────────────────────────────┬──────────────────┐\n   │                             │  状态（必展示）    │\n   │                             │  关卡/得分/生命    │\n   │       游戏区域（左栏）        ├──────────────────┤\n   │    占总宽约 65%~70%          │  下一块/预览      │\n   │    高度撑满 100vh            ├──────────────────┤\n   │                             │ ◀ II ▶  方向键   │\n   └─────────────────────────────┴──────────────────┘\n   ```\n\n   **右栏布局规则（状态区和方向键始终可见）**：\n   - `#status`（关卡、难度、得分/目标分、生命）：`flex:0 0 auto`，**必须展示，不可压缩**\n   - `#game-preview`（下一块/预览区）：`flex:0 0 auto`，**有预览需求的游戏必须展示，不可压缩**\n   - `#leaderboard`（排行榜）和 `#instructions`（操作说明）：**当游戏有 `#game-preview` 时，不展示排行榜和操作说明**（右栏空间有限，预览区优先）。仅在无预览需求的游戏中，才用 `flex:1 1 0; min-height:0` + 标题 h3 固定 + `.sb-inner` 内容区 `overflow-y:auto` 独立滚动的方式展示\n   - `#controls`（方向键+暂停）：`flex:0 0 auto`，**固定在右栏底部，始终可见，不参与滚动**\n\n   CSS 骨架模板（必须使用）：\n   ```html\n   <div id=\"app\" style=\"width:100vw;height:100vh;display:flex;flex-direction:row;overflow:hidden;background:#1a1a2e;\">\n     <!-- 左栏：游戏区域，尽量大，canvas 撑满整个左栏，不留空白 -->\n     <div id=\"game-area\" style=\"flex:1;position:relative;min-width:0;\">\n       <canvas id=\"canvas\" style=\"display:block;width:100%;height:100%;\"></canvas>\n       <!-- 开始/结束/暂停 遮罩层：必须用以下写法居中，禁止只写 top:0;left:0 导致内容贴左上角 -->\n       <!-- <div id=\"overlay\" style=\"position:absolute;top:0;left:0;right:0;bottom:0;display:flex;flex-direction:column;\n            align-items:center;justify-content:center;text-align:center;\n            background:rgba(0,0,0,.6);z-index:10;\">\n         <h2 style=\"color:#fff;font-size:clamp(18px,3vw,32px);margin:0 0 2vh;\">游戏标题</h2>\n         <p style=\"color:#ccc;font-size:clamp(12px,1.8vw,18px);margin:0 0 3vh;\">操作说明文字</p>\n         <button style=\"padding:1.2vh 4vw;font-size:clamp(14px,2vw,20px);border-radius:2vmin;\n           border:none;background:#e74c3c;color:#fff;touch-action:manipulation;\">开始游戏</button>\n       </div> -->\n       <!-- ⚠️ 遮罩内所有文字/按钮必须通过 flexbox 居中，严禁使用固定 top/left 像素值定位 -->\n     </div>\n     <!-- 右栏：固定宽度，状态区+方向键始终可见 -->\n     <div id=\"sidebar\" style=\"width:26vw;min-width:180px;max-width:300px;height:100vh;\n          display:flex;flex-direction:column;padding:1.5vh 1.2vw;gap:0.8vh;box-sizing:border-box;overflow:hidden;\">\n       <!-- ① 游戏状态：必展示，不可压缩 -->\n       <div id=\"status\" style=\"flex:0 0 auto;font-size:clamp(10px,1.4vw,14px);\">关卡/得分/生命...</div>\n       <!-- ② 预览区（下一块等）：有预览需求时展示；若有预览区则不放排行榜和操作说明 -->\n       <div id=\"game-preview\" style=\"flex:1 1 0;min-height:0;\">...</div>\n       <!-- ③④ 排行榜+操作说明：仅在无预览区的游戏中使用（与 ② 二选一） -->\n       <!--\n       <div id=\"leaderboard\" style=\"flex:1 1 0;min-height:0;display:flex;flex-direction:column;font-size:clamp(10px,1.3vw,13px);\">\n         <h3 style=\"flex:0 0 auto;margin:0 0 0.4vh;\">排行榜</h3>\n         <div class=\"sb-inner\" style=\"flex:1 1 0;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;\n              scrollbar-width:thin;scrollbar-color:rgba(255,255,255,.3) transparent;\">\n         </div>\n       </div>\n       <div id=\"instructions\" style=\"flex:1 1 0;min-height:0;display:flex;flex-direction:column;font-size:clamp(10px,1.3vw,13px);\">\n         <h3 style=\"flex:0 0 auto;margin:0 0 0.4vh;\">操作说明</h3>\n         <div class=\"sb-inner\" style=\"flex:1 1 0;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;\n              scrollbar-width:thin;scrollbar-color:rgba(255,255,255,.3) transparent;\">\n         </div>\n       </div>\n       -->\n       <!-- ⑤ 方向键+暂停：固定在底部，始终可见 -->\n       <div id=\"controls\" style=\"flex:0 0 auto;padding-top:0.8vh;\">...</div>\n     </div>\n   </div>\n   ```\n   Canvas 尺寸初始化（JS 中必须这样写，确保 Android 8.1 WebView 中 canvas 像素与显示尺寸一致）：\n\n   > ⚠️ **Android 8.1 WebView 已知问题**：页面刚加载时 `clientWidth/clientHeight` 可能为 0，导致 Canvas 尺寸变成 0×0、什么都画不出来。必须用以下模板，通过 `window.load` + 双层 `requestAnimationFrame` 延迟读取尺寸，并用 `offsetWidth` 做兜底。\n\n   Canvas CSS（必须用 `position:absolute` 撑满，不依赖 flex 布局尺寸）：\n   ```html\n   <div id=\"game-area\" style=\"flex:1;position:relative;min-width:0;\">\n     <canvas id=\"canvas\" style=\"position:absolute;top:0;left:0;width:100%;height:100%;display:block;\"></canvas>\n   </div>\n   ```\n\n   Canvas 尺寸初始化（必须在 `window.load` + 双层 `requestAnimationFrame` 后执行）：\n   ```js\n   function initCanvas() {\n     const gameArea = document.getElementById('game-area');\n     const canvas = document.getElementById('canvas');\n     // offsetWidth 兜底：Android 8.1 WebView 刚加载时 clientWidth 可能为 0\n     const w = gameArea.clientWidth  || gameArea.offsetWidth  || window.innerWidth * 0.7;\n     const h = gameArea.clientHeight || gameArea.offsetHeight || window.innerHeight;\n     canvas.width  = w;\n     canvas.height = h;\n     // 格子大小根据 canvas 尺寸动态计算\n     const COLS = 30, ROWS = Math.floor(h / (w / COLS));\n     const cellW = w / COLS, cellH = h / ROWS;\n     // ... 其余游戏初始化逻辑\n   }\n\n   // 必须在 window.load 后、双层 rAF 确保布局渲染完毕再读尺寸\n   window.addEventListener('load', function() {\n     requestAnimationFrame(function() {\n       requestAnimationFrame(function() {\n         initCanvas();\n       });\n     });\n   });\n   ```\n\n   **游戏类 WebView 触摸适配要点**（Android 8.1 WebView 无键盘/鼠标）：\n\n   > ⚠️ **强制要求（主控方式）**：所有游戏的**主要控制方式必须是触摸坐标直接映射**——玩家手指按在画布哪里，角色/目标就跟到哪里（或朝那个方向移动），不需要抬手再点方向按钮。右下角的 ↑↓←→ 方向键和暂停键保留作为**辅助控制**，不是唯一控制入口。暂停按钮必须位于方向键 3×3 grid 的**正中心格**。\n\n   **触摸坐标映射实现模板**（贴在 Canvas/游戏容器上）：\n   ```js\n   // 主控：手指位置直接映射到游戏坐标\n   canvas.addEventListener('touchstart', onTouch, {passive: false});\n   canvas.addEventListener('touchmove',  onTouch, {passive: false});\n\n   function onTouch(e) {\n     e.preventDefault();\n     const rect = canvas.getBoundingClientRect();\n     const touch = e.touches[0];\n     // 将屏幕像素坐标换算为 canvas 内部坐标\n     const cx = (touch.clientX - rect.left) * (canvas.width  / rect.width);\n     const cy = (touch.clientY - rect.top)  * (canvas.height / rect.height);\n     movePlayerTo(cx, cy);   // 替换为游戏实际函数：直接设置目标坐标\n   }\n   ```\n   - 对于贪吃蛇、俄罗斯方块等离散格子类游戏，触摸映射改为**滑动方向**：`touchstart` 记录起点，`touchend` 计算 `deltaX/deltaY`，阈值 20px 内忽略，超过则取绝对值较大的轴作为方向\n   - 对于飞机大战、跑酷等连续坐标类游戏，用 `touchmove` 实时跟随手指坐标\n   - 左侧游戏遮罩/操作说明中必须用一句话说明触摸操作方式，例如：「触摸屏幕控制移动，手指在哪飞机就去哪」\n\n   **飞机大战类游戏专项规则**：\n   - **子弹必须自动连续发射**（`setInterval` 或游戏主循环中每隔固定帧数发射），**禁止要求玩家手动点击射击**\n   - 自动射频简单难度约 300ms/发，中等 200ms/发，困难 100ms/发\n   - 操作说明中写「触摸屏幕移动飞机，子弹自动发射」\n\n   **俄罗斯方块/消除类游戏专项规则**：\n   - **贴地即锁，零等待**：下落计时器触发时若方块无法继续下移，立刻执行 `lock()` → `spawn()`，下一块在同一帧内出现。**禁止 LOCK_DELAY 缓冲期**\n   - **`spawnPiece()` 必须将新方块赋值给全局变量**（如 `cur = shape`），否则 `drawGrid()` 中 `if(cur != null)` 判断为 false 导致方块不显示\n   - **经典 NES 速度标准**：第 1 关 800ms → 第 5 关 533ms → 第 10 关 333ms → 第 15 关 200ms → 第 20 关 100ms → 第 29 关+ 83ms（极限）\n   - 难度递增公式：`interval = Math.max(83, 800 - (level - 1) * 5 * (1000/60))`（每关减约 5 帧，60fps 基准）\n   - 预览区（下一块）在右栏 `#game-preview` 中展示，有预览时不展示排行榜和操作说明\n\n   **方向键 + 暂停按钮模板**（放在右栏 `#controls` 内，作为辅助控制）：\n\n   布局规则：**暂停按钮放在方向键 3×3 grid 的正中心格**，不单独放在键盘上方。整体视觉要炫酷：方向键用半透明玻璃质感，暂停键用蓝色渐变 + 光晕，`:active` 有明显按压反馈。\n\n   ```html\n   <div id=\"controls\" style=\"flex:0 0 auto;display:flex;flex-direction:column;align-items:center;\n        gap:0.8vh;touch-action:none;user-select:none;\">\n     <!-- 3×3 方向键grid，暂停在正中心 -->\n     <div style=\"display:grid;grid-template-columns:repeat(3,13vmin);grid-template-rows:repeat(3,13vmin);gap:1.2vmin;\">\n       <div></div>\n       <button class=\"dpad-btn\" id=\"btn-up\">▲</button>\n       <div></div>\n       <button class=\"dpad-btn\" id=\"btn-left\">◀</button>\n       <button class=\"dpad-btn\" id=\"btn-pause\">II</button>\n       <button class=\"dpad-btn\" id=\"btn-right\">▶</button>\n       <div></div>\n       <button class=\"dpad-btn\" id=\"btn-down\">▼</button>\n       <div></div>\n     </div>\n   </div>\n   <style>\n   /* 方向键：半透明玻璃质感 */\n   .dpad-btn {\n     background: linear-gradient(145deg, rgba(255,255,255,.22), rgba(255,255,255,.06));\n     border: 1.5px solid rgba(255,255,255,.35);\n     border-radius: 50%;\n     color: #fff;\n     font-size: 4.5vmin;\n     touch-action: manipulation;\n     display: flex;\n     align-items: center;\n     justify-content: center;\n     box-shadow: 0 2px 8px rgba(0,0,0,.4), inset 0 1px 0 rgba(255,255,255,.25);\n     cursor: pointer;\n     transition: transform .08s;\n   }\n   .dpad-btn:active {\n     background: linear-gradient(145deg, rgba(255,255,255,.45), rgba(255,255,255,.15));\n     transform: scale(.88);\n     box-shadow: 0 1px 4px rgba(0,0,0,.5), inset 0 1px 0 rgba(255,255,255,.3);\n   }\n   /* 暂停键与方向键样式完全一致，仅字号加粗 + 细边框区分；用 II 代替 ⏸ 符号（WebView 渲染兼容） */\n   #btn-pause {\n     font-size: 4.5vmin;\n     font-weight: bold;\n     border-color: rgba(255,255,255,.6);\n   }\n   </style>\n   <script>\n   document.getElementById('btn-up').addEventListener('touchstart',    e=>{e.preventDefault();changeDir(0,-1);},{passive:false});\n   document.getElementById('btn-down').addEventListener('touchstart',   e=>{e.preventDefault();changeDir(0, 1);},{passive:false});\n   document.getElementById('btn-left').addEventListener('touchstart',   e=>{e.preventDefault();changeDir(-1,0);},{passive:false});\n   document.getElementById('btn-right').addEventListener('touchstart',  e=>{e.preventDefault();changeDir( 1,0);},{passive:false});\n   document.getElementById('btn-pause').addEventListener('touchstart',  e=>{e.preventDefault();togglePause();},{passive:false});\n   // 桌面调试保留键盘\n   document.addEventListener('keydown', e=>{\n     if(e.key==='ArrowUp')    changeDir(0,-1);\n     if(e.key==='ArrowDown')  changeDir(0, 1);\n     if(e.key==='ArrowLeft')  changeDir(-1,0);\n     if(e.key==='ArrowRight') changeDir( 1,0);\n     if(e.key===' ')          togglePause();\n   });\n   </script>\n   ```\n   - `changeDir(dx, dy)` 和 `togglePause()` 替换为游戏实际函数名；坐标映射类游戏中方向键可改为触发\"持续向该方向移动\"逻辑\n   - 按钮尺寸用 `vmin` 单位，960px 小屏到 2560px 大屏均可用\n   - 暂停键必须在中心格，禁止移到键盘外部单独放置\n\n   **游戏角色视觉规范**（卡通可爱风格，禁止只画纯色圆点/方块）：\n   - 贪吃蛇：蛇头用圆角矩形 + 两个白色眼睛（小圆 + 黑色瞳孔）+ 根据朝向旋转；蛇身用渐变色圆角矩形，节节之间颜色略有变化；食物用苹果/草莓等图形（红色圆 + 绿色小叶子）\n   - 贪吃蛇蛇头绘制示例（Canvas 2D）：\n     ```js\n     function drawHead(ctx, x, y, w, h, dir) {\n       ctx.save();\n       ctx.translate(x + w/2, y + h/2);\n       const angle = {right:0, down:Math.PI/2, left:Math.PI, up:-Math.PI/2}[dir] || 0;\n       ctx.rotate(angle);\n       // 头部圆角矩形\n       ctx.fillStyle = '#2ecc71';\n       roundRect(ctx, -w/2, -h/2, w, h, w*0.3);\n       ctx.fill();\n       // 眼睛\n       [[-w*0.15, -h*0.15],[w*0.15, -h*0.15]].forEach(([ex,ey])=>{\n         ctx.fillStyle='#fff'; ctx.beginPath(); ctx.arc(ex,ey,w*0.12,0,Math.PI*2); ctx.fill();\n         ctx.fillStyle='#222'; ctx.beginPath(); ctx.arc(ex+w*0.03,ey,w*0.06,0,Math.PI*2); ctx.fill();\n       });\n       ctx.restore();\n     }\n     function roundRect(ctx,x,y,w,h,r){\n       ctx.beginPath();ctx.moveTo(x+r,y);ctx.lineTo(x+w-r,y);ctx.arcTo(x+w,y,x+w,y+r,r);\n       ctx.lineTo(x+w,y+h-r);ctx.arcTo(x+w,y+h,x+w-r,y+h,r);ctx.lineTo(x+r,y+h);\n       ctx.arcTo(x,y+h,x,y+h-r,r);ctx.lineTo(x,y+r);ctx.arcTo(x,y,x+r,y,r);ctx.closePath();\n     }\n     ```\n   - 俄罗斯方块：方块用渐变色 + 高光边框，不同形状用不同颜色\n   - 飞机大战：飞机用多边形或 SVG path 绘制，子弹用细长椭圆，敌机颜色与玩家区分；**子弹必须自动连续发射，禁止要求玩家手动点击射击**；操作说明写「触摸屏幕移动飞机，子弹自动发射」\n   - 通用原则：所有游戏角色必须有辨识度，能看出是什么，不能只是纯色几何形状\n\n   **平台跳跃 / 跑酷类游戏触摸控制规范**（马里奥、跑酷等左右移动+跳跃类游戏必须遵守）：\n\n   > 核心设计：左右半屏控制方向，上滑触发跳跃，斜滑（如右上方）同时触发移动+跳跃，完全模拟实体手柄的拇指操作。\n\n   ```js\n   let tSX = 0, tSY = 0; // 触摸起点，每次 touchstart 或跳跃触发后重置\n\n   canvas.addEventListener('touchstart', e => {\n     e.preventDefault();\n     const t = e.touches[0];\n     tSX = t.clientX; tSY = t.clientY;\n   }, {passive: false});\n\n   canvas.addEventListener('touchmove', e => {\n     e.preventDefault();\n     const t = e.touches[0];\n     const dx = t.clientX - tSX;\n     const dy = t.clientY - tSY; // 负值 = 向上\n\n     // 左右移动：根据手指当前位置在画布左/右半区判断方向\n     const rect = canvas.getBoundingClientRect();\n     if (t.clientX < rect.left + rect.width / 2) {\n       moveLeft();\n     } else {\n       moveRi\n\nArchive v1.0.29: 41 files, 86391 bytes\n\nFiles: clawhub.yaml (565b), LICENSE (912b), metadata.json (464b), openai.yaml (3724b), publish.sh (2611b), README.md (4584b), scripts/_common.sh (761b), scripts/get.sh (437b), scripts/ping.sh (832b), scripts/register.sh (1400b), scripts/update.sh (1161b), skill-card.md (2702b), SKILL.md (44423b), skills/myapp-creator/_meta.json (132b), skills/myapp-creator/clawhub.yaml (565b), skills/myapp-creator/metadata.json (464b), skills/myapp-creator/openai.yaml (3724b), skills/myapp-creator/publish.sh (2611b), skills/myapp-creator/README.md (4584b), skills/myapp-creator/scripts/_common.sh (761b), skills/myapp-creator/scripts/get.sh (437b), skills/myapp-creator/scripts/ping.sh (832b), skills/myapp-creator/scripts/register.sh (1400b), skills/myapp-creator/scripts/update.sh (1161b), skills/myapp-creator/SKILL.md (44423b), skills/myapp-creator/skills/myapp-creator/_meta.json (132b), skills/myapp-creator/skills/myapp-creator/clawhub.yaml (565b), skills/myapp-creator/skills/myapp-creator/metadata.json (464b), skills/myapp-creator/skills/myapp-creator/openai.yaml (3257b), skills/myapp-creator/skills/myapp-creator/publish.sh (2611b), skills/myapp-creator/skills/myapp-creator/README.md (4180b), skills/myapp-creator/skills/myapp-creator/scripts/_common.sh (761b), skills/myapp-creator/skills/myapp-creator/scripts/get.sh (437b), skills/myapp-creator/skills/myapp-creator/scripts/ping.sh (832b), skills/myapp-creator/skills/myapp-creator/scripts/register.sh (1400b), skills/myapp-creator/skills/myapp-creator/scripts/update.sh (1161b), skills/myapp-creator/skills/myapp-creator/SKILL.md (44423b), skills/myapp-creator/skills/myapp-creator/version.txt (7b), skills/myapp-creator/version.txt (7b), version.txt (7b), _meta.json (133b)\n\nArchive v1.0.28: 41 files, 84584 bytes\n\nFiles: clawhub.yaml (565b), LICENSE (912b), metadata.json (464b), openai.yaml (3724b), publish.sh (2611b), README.md (4584b), scripts/_common.sh (761b), scripts/get.sh (437b), scripts/ping.sh (832b), scripts/register.sh (1400b), scripts/update.sh (1161b), skill-card.md (2493b), SKILL.md (43126b), skills/myapp-creator/_meta.json (132b), skills/myapp-creator/clawhub.yaml (565b), skills/myapp-creator/metadata.json (464b), skills/myapp-creator/openai.yaml (3724b), skills/myapp-creator/publish.sh (2611b), skills/myapp-creator/README.md (4584b), skills/myapp-creator/scripts/_common.sh (761b), skills/myapp-creator/scripts/get.sh (437b), skills/myapp-creator/scripts/ping.sh (832b), skills/myapp-creator/scripts/register.sh (1400b), skills/myapp-creator/scripts/update.sh (1161b), skills/myapp-creator/SKILL.md (43126b), skills/myapp-creator/skills/myapp-creator/_meta.json (132b), skills/myapp-creator/skills/myapp-creator/clawhub.yaml (565b), skills/myapp-creator/skills/myapp-creator/metadata.json (464b), skills/myapp-creator/skills/myapp-creator/openai.yaml (3257b), skills/myapp-creator/skills/myapp-creator/publish.sh (2611b), skills/myapp-creator/skills/myapp-creator/README.md (4180b), skills/myapp-creator/skills/myapp-creator/scripts/_common.sh (761b), skills/myapp-creator/skills/myapp-creator/scripts/get.sh (437b), skills/myapp-creator/skills/myapp-creator/scripts/ping.sh (832b), skills/myapp-creator/skills/myapp-creator/scripts/register.sh (1400b), skills/myapp-creator/skills/myapp-creator/scripts/update.sh (1161b), skills/myapp-creator/skills/myapp-creator/SKILL.md (43126b), skills/myapp-creator/skills/myapp-creator/version.txt (7b), skills/myapp-creator/version.txt (7b), version.txt (7b), _meta.json (133b)\n\nArchive v1.0.27: 41 files, 84960 bytes\n\nFiles: clawhub.yaml (565b), LICENSE (912b), metadata.json (464b), openai.yaml (3724b), publish.sh (2611b), README.md (4584b), scripts/_common.sh (761b), scripts/get.sh (437b), scripts/ping.sh (832b), scripts/register.sh (1400b), scripts/update.sh (1161b), skill-card.md (2697b), SKILL.md (43630b), skills/myapp-creator/_meta.json (132b), skills/myapp-creator/clawhub.yaml (565b), skills/myapp-creator/metadata.json (464b), skills/myapp-creator/openai.yaml (3724b), skills/myapp-creator/publish.sh (2611b), skills/myapp-creator/README.md (4584b), skills/myapp-creator/scripts/_common.sh (761b), skills/myapp-creator/scripts/get.sh (437b), skills/myapp-creator/scripts/ping.sh (832b), skills/myapp-creator/scripts/register.sh (1400b), skills/myapp-creator/scripts/update.sh (1161b), skills/myapp-creator/SKILL.md (43630b), skills/myapp-creator/skills/myapp-creator/_meta.json (132b), skills/myapp-creator/skills/myapp-creator/clawhub.yaml (565b), skills/myapp-creator/skills/myapp-creator/metadata.json (464b), skills/myapp-creator/skills/myapp-creator/openai.yaml (3257b), skills/myapp-creator/skills/myapp-creator/publish.sh (2611b), skills/myapp-creator/skills/myapp-creator/README.md (4180b), skills/myapp-creator/skills/myapp-creator/scripts/_common.sh (761b), skills/myapp-creator/skills/myapp-creator/scripts/get.sh (437b), skills/myapp-creator/skills/myapp-creator/scripts/ping.sh (832b), skills/myapp-creator/skills/myapp-creator/scripts/register.sh (1400b), skills/myapp-creator/skills/myapp-creator/scripts/update.sh (1161b), skills/myapp-creator/skills/myapp-creator/SKILL.md (43630b), skills/myapp-creator/skills/myapp-creator/version.txt (7b), skills/myapp-creator/version.txt (7b), version.txt (7b), _meta.json (133b)\n\nArchive v1.0.26: 41 files, 84462 bytes\n\nFiles: clawhub.yaml (565b), LICENSE (912b), metadata.json (464b), openai.yaml (3724b), publish.sh (2611b), README.md (4584b), scripts/_common.sh (761b), scripts/get.sh (437b), scripts/ping.sh (832b), scripts/register.sh (1400b), scripts/update.sh (1161b), skill-card.md (2299b), SKILL.md (43033b), skills/myapp-creator/_meta.json (132b), skills/myapp-creator/clawhub.yaml (565b), skills/myapp-creator/metadata.json (464b), skills/myapp-creator/openai.yaml (3724b), skills/myapp-creator/publish.sh (2611b), skills/myapp-creator/README.md (4584b), skills/myapp-creator/scripts/_common.sh (761b), skills/myapp-creator/scripts/get.sh (437b), skills/myapp-creator/scripts/ping.sh (832b), skills/myapp-creator/scripts/register.sh (1400b), skills/myapp-creator/scripts/update.sh (1161b), skills/myapp-creator/SKILL.md (43033b), skills/myapp-creator/skills/myapp-creator/_meta.json (132b), skills/myapp-creator/skills/myapp-creator/clawhub.yaml (565b), skills/myapp-creator/skills/myapp-creator/metadata.json (464b), skills/myapp-creator/skills/myapp-creator/openai.yaml (3257b), skills/myapp-creator/skills/myapp-creator/publish.sh (2611b), skills/myapp-creator/skills/myapp-creator/README.md (4180b), skills/myapp-creator/skills/myapp-creator/scripts/_common.sh (761b), skills/myapp-creator/skills/myapp-creator/scripts/get.sh (437b), skills/myapp-creator/skills/myapp-creator/scripts/ping.sh (832b), skills/myapp-creator/skills/myapp-creator/scripts/register.sh (1400b), skills/myapp-creator/skills/myapp-creator/scripts/update.sh (1161b), skills/myapp-creator/skills/myapp-creator/SKILL.md (43033b), skills/myapp-creator/skills/myapp-creator/version.txt (7b), skills/myapp-creator/version.txt (7b), version.txt (7b), _meta.json (133b)\n\nArchive v1.0.25: 41 files, 84328 bytes\n\nFiles: clawhub.yaml (565b), LICENSE (912b), metadata.json (464b), openai.yaml (3724b), publish.sh (2611b), README.md (4584b), scripts/_common.sh (761b), scripts/get.sh (437b), scripts/ping.sh (832b), scripts/register.sh (1400b), scripts/update.sh (1161b), skill-card.md (2629b), SKILL.md (43033b), skills/myapp-creator/_meta.json (132b), skills/myapp-creator/clawhub.yaml (565b), skills/myapp-creator/metadata.json (464b), skills/myapp-creator/openai.yaml (3724b), skills/myapp-creator/publish.sh (2611b), skills/myapp-creator/README.md (4584b), skills/myapp-creator/scripts/_common.sh (761b), skills/myapp-creator/scripts/get.sh (437b), skills/myapp-creator/scripts/ping.sh (832b), skills/myapp-creator/scripts/register.sh (1302b), skills/myapp-creator/scripts/update.sh (1015b), skills/myapp-creator/SKILL.md (43033b), skills/myapp-creator/skills/myapp-creator/_meta.json (132b), skills/myapp-creator/skills/myapp-creator/clawhub.yaml (565b), skills/myapp-creator/skills/myapp-creator/metadata.json (464b), skills/myapp-creator/skills/myapp-creator/openai.yaml (325...","readmeExcerpt":"Skill: Myapp Creator Owner: zhang6714268 Summary: Create or update standalone single-file HTML apps with specified features, saving them to fe-service without invoking LLM directly. Tags: latest:1.0.31 Version history: v1.0.31 | 2026-05-28T12:47:47.524Z | auto myapp-creator 1.0.31 - Updated version to 1.0.31 across all relevant metadata and documentation files. - Synchronized content and formatting in SKILL.md and re","codeSnippets":[],"executableExamples":[{"language":"js","snippet":"function rr(ctx, x, y, w, h, r) {\n       ctx.beginPath();\n       ctx.moveTo(x + r, y);\n       ctx.lineTo(x + w - r, y);  ctx.arcTo(x + w, y,     x + w, y + r,     r);\n       ctx.lineTo(x + w, y + h - r); ctx.arcTo(x + w, y + h, x + w - r, y + h, r);\n       ctx.lineTo(x + r, y + h);  ctx.arcTo(x,     y + h, x,     y + h - r, r);\n       ctx.lineTo(x, y + r);      ctx.arcTo(x,     y,     x + r, y,         r);\n       ctx.closePath();\n     }\n     // 用法：rr(ctx, x, y, w, h, radius); ctx.fill();"},{"language":"text","snippet":"┌─────────────────────────────┬──────────────────┐\n   │                             │  状态（必展示）    │\n   │                             │  关卡/得分/生命    │\n   │       游戏区域（左栏）        ├──────────────────┤\n   │    占总宽约 65%~70%          │  下一块/预览      │\n   │    高度撑满 100vh            ├──────────────────┤\n   │                             │ ◀ II ▶  方向键   │\n   └─────────────────────────────┴──────────────────┘"},{"language":"html","snippet":"<div id=\"app\" style=\"width:100vw;height:100vh;display:flex;flex-direction:row;overflow:hidden;background:#1a1a2e;\">\n     <!-- 左栏：游戏区域，尽量大，canvas 撑满整个左栏，不留空白 -->\n     <div id=\"game-area\" style=\"flex:1;position:relative;min-width:0;\">\n       <canvas id=\"canvas\" style=\"display:block;width:100%;height:100%;\"></canvas>\n       <!-- 开始/结束/暂停 遮罩层：必须用以下写法居中，禁止只写 top:0;left:0 导致内容贴左上角 -->\n       <!-- <div id=\"overlay\" style=\"position:absolute;top:0;left:0;right:0;bottom:0;display:flex;flex-direction:column;\n            align-items:center;justify-content:center;text-align:center;\n            background:rgba(0,0,0,.6);z-index:10;\">\n         <h2 style=\"color:#fff;font-size:clamp(18px,3vw,32px);margin:0 0 2vh;\">游戏标题</h2>\n         <p style=\"color:#ccc;font-size:clamp(12px,1.8vw,18px);margin:0 0 3vh;\">操作说明文字</p>\n         <button style=\"padding:1.2vh 4vw;font-size:clamp(14px,2vw,20px);border-radius:2vmin;\n           border:none;background:#e74c3c;color:#fff;touch-action:manipulation;\">开始游戏</button>\n       </div> -->\n       <!-- ⚠️ 遮罩内所有文字/按钮必须通过 flexbox 居中，严禁使用固定 top/left 像素值定位 -->\n     </div>\n     <!-- 右栏：固定宽度，状态区+方向键始终可见 -->\n     <div id=\"sidebar\" style=\"width:26vw;min-width:180px;max-width:300px;height:100vh;\n          display:flex;flex-direction:column;padding:1.5vh 1.2vw;gap:0.8vh;box-sizing:border-box;overflow:hidden;\">\n       <!-- ① 游戏状态：必展示，不可压缩 -->\n       <div id=\"status\" style=\"flex:0 0 auto;font-size:clamp(10px,1.4vw,14px);\">关卡/得分/生命...</div>\n       <!-- ② 预览区（下一块等）：有预览需求时展示；若有预览区则不放排行榜和操作说明 -->\n       <div id=\"game-preview\" style=\"flex:1 1 0;min-height:0;\">...</div>\n       <!-- ③④ 排行榜+操作说明：仅在无预览区的游戏中使用（与 ② 二选一） -->\n       <!--\n       <div id=\"leaderboard\" style=\"flex:1 1 0;min-height:0;display:flex;flex-direction:column;font-size:clamp(10px,1.3vw,13px);\">\n         <h3 style=\"flex:0 0 auto;margin:0 0 0.4vh;\">排行榜</h3>\n         <div class=\"sb-inner\" style=\"flex:1 1 0;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;\n              scrollbar-width:thin;scrollbar-"},{"language":"html","snippet":"<div id=\"game-area\" style=\"flex:1;position:relative;min-width:0;\">\n     <canvas id=\"canvas\" style=\"position:absolute;top:0;left:0;width:100%;height:100%;display:block;\"></canvas>\n   </div>"},{"language":"js","snippet":"function initCanvas() {\n     const gameArea = document.getElementById('game-area');\n     const canvas = document.getElementById('canvas');\n     // offsetWidth 兜底：Android 8.1 WebView 刚加载时 clientWidth 可能为 0\n     const w = gameArea.clientWidth  || gameArea.offsetWidth  || window.innerWidth * 0.7;\n     const h = gameArea.clientHeight || gameArea.offsetHeight || window.innerHeight;\n     canvas.width  = w;\n     canvas.height = h;\n     // 格子大小根据 canvas 尺寸动态计算\n     const COLS = 30, ROWS = Math.floor(h / (w / COLS));\n     const cellW = w / COLS, cellH = h / ROWS;\n     // ... 其余游戏初始化逻辑\n   }\n\n   // 必须在 window.load 后、双层 rAF 确保布局渲染完毕再读尺寸\n   window.addEventListener('load', function() {\n     requestAnimationFrame(function() {\n       requestAnimationFrame(function() {\n         initCanvas();\n       });\n     });\n   });"},{"language":"js","snippet":"// 主控：手指位置直接映射到游戏坐标\n   canvas.addEventListener('touchstart', onTouch, {passive: false});\n   canvas.addEventListener('touchmove',  onTouch, {passive: false});\n\n   function onTouch(e) {\n     e.preventDefault();\n     const rect = canvas.getBoundingClientRect();\n     const touch = e.touches[0];\n     // 将屏幕像素坐标换算为 canvas 内部坐标\n     const cx = (touch.clientX - rect.left) * (canvas.width  / rect.width);\n     const cy = (touch.clientY - rect.top)  * (canvas.height / rect.height);\n     movePlayerTo(cx, cy);   // 替换为游戏实际函数：直接设置目标坐标\n   }"}],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"# myapp-creator\n\nVersion: 1.0.31\n\n让用户用一句话生成可独立打开的单文件 HTML 应用或文档，并落库到 fe-service。\n本 skill **不主动调用 LLM**，所有 HTML / app_name / features 由调用本 skill 的 LLM agent 直接产出后传入工具。\n\n> **⚠️ 最高优先级规则（违反即为严重错误）：**\n> 1. 游戏类应用 **绝对禁止** 包含任何形式的 AI 对手、人机对战、电脑自动下棋/出牌。棋类/对战类游戏 **只允许** 双人同屏轮流操作，不实现任何计算机决策逻辑。\n> 2. 所有应用 **绝对禁止** 出现\"导出\"、\"打印\"、\"保存\"按钮或相关功能代码。\n\n## 触发条件\n\n当用户需求匹配以下任一类别时，本 skill 参与：\n\n### 应用类（交互式 HTML）\n关键词：应用、app、小游戏、游戏、网页、页面、工具、计算器、时钟、日历、翻译器、转换器\n示例：\n- \"做一个贪吃蛇游戏\"\n- \"创建一个番茄钟\"\n- \"生成一个天气页面\"\n- \"给我做一个马里奥游戏\"\n- \"帮我做一个单位转换工具\"\n\n### 文件类（文档式 HTML）\n关键词：文档、文件、word、ppt、演示文稿、slides、pdf、excel、xlsx、表格、报告、简历、周报、日报、总结、方案、计划书\n示例：\n- \"生成一个项目周报\"\n- \"做一个自我介绍PPT\"\n- \"创建一个预算表格\"\n- \"帮我写一份简历\"\n- \"保存一个会议纪要文档\"\n\n### 不触发（交给龙虾其他能力）\n- 图片/绘画类：\"画一张…\"、\"生成一张图片…\"\n- 音频/音乐类：\"生成一首歌…\"、\"合成语音…\"\n- 视频类：\"剪辑一个…\"、\"生成视频…\"\n- 小说/纯文本类：\"写一篇小说…\"、\"写一首诗…\"（纯创作不产出可交互文件）\n- 纯问答/聊天/闲聊\n\n### 判断规则\n用户意图是\"产出一个可独立打开、保存下来反复使用的文件或应用\"时触发本 skill。\n如果用户只是想让你\"说一段话\"或\"回答问题\"，不要触发。\n\n## 触发标记（兼容旧入口）\n\n以下前缀仍可直接触发，不需要语义判断：\n- `[create_app]<描述>` → 创建新应用/文件\n- `[update_app:app_id=<n>]<描述>` → 更新已有应用/文件\n- `[install_check:session=<sid>]` → 安装自检（仅调 myapp_ping）\n- 不带上述前缀 → 按语义判断是否匹配触发条件\n\n## 创建流程\n\n最长等待 300s。若 300s 内不能完成 HTML 生成与 `myapp_register` 调用，直接回复\"应用创建失败，请稍后重试\"。\n\n1. 解析用户描述，判断类型（应用类 or 文件类），产出：\n   - `app_name`：应用/文件名，≤30 字\n   - `features`：JSON 字符串数组，3~8 条，每条 ≤30 字，描述具体功能/内容要点\n   - `html_content`：完整可独立打开的单文件 HTML（按类型选择模板风格）\n\n2. HTML 通用约束：\n   - 自包含：CSS/JS 全部内联在 `<style>` / `<script>`\n   - 不依赖本地资源\n   - 外部资源仅允许：unpkg.com / jsdelivr.net / cdnjs.cloudflare.com\n   - 总长度 ≤ 60KB（UTF-8 字节数）\n   - 不引用任何小度专属 API\n   - 必须包含 `<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">`\n\n   **目标运行环境 — Android 8.1 WebView（Chrome/85）**：\n   所有 HTML 最终运行在搭载 Android 8.1 的智能屏 WebView 中，无鼠标/硬键盘，仅支持触摸和左右滑动。\n   生成代码时必须遵守以下兼容规则：\n\n   **JS / Web API 兼容**（feature detection 优先，禁止仅凭 UA 判断）：\n   - 使用 `typeof IntersectionObserver !== 'undefined'` 等特性检测，不要直接调用可能不存在的 API\n   - 禁止使用 Chrome 86+ 才支持的 API：`CSS.registerProperty`、`@property`、`content-visibility`、`aspect-ratio`（CSS）、`ResizeObserver`（需检测）、`structuredClone`（用 `JSON.parse(JSON.stringify())` 替代）\n   - 禁止使用 ES2020+ 语法：`??=`、`||=`、`&&=`、`import()`动态导入、`BigInt`、`globalThis`（用 `window` 替代）\n   - **禁止使用 `ctx.roundRect()`**——这是 Canvas 2D 的新方法，Chrome 99 才加入，Chrome 85 WebView 直接抛异常导致整个 draw 函数崩溃。必须用以下自定义 helper 替代：\n     ```js\n     function rr(ctx, x, y, w, h, r) {\n       ctx.beginPath();\n       ctx.moveTo(x + r, y);\n       ctx.lineTo(x + w - r, y);  ctx.arcTo(x + w, y,     x + w, y + r,     r);\n       ctx.lineTo(x + w, y + h - r); ctx.arcTo(x + w, y + h, x + w - r, y + h, r);\n       ctx.lineTo(x + r, y + h);  ctx.arcTo(x,     y + h, x,     y + h - r, r);\n       ctx.lineTo(x, y + r);      ctx.arcTo(x,     y,     x + r, y,         r);\n       ctx.closePath();\n     }\n     // 用法：rr(ctx, x, y, w, h, radius); ctx.fill();\n     ```\n   - **禁止在 Canvas 内用 emoji 字符**（如 🌕🔥❤️ 等）——Android WebView 字体渲染异常会抛异常或显示方块。Canvas 内所有文字必须用纯 ASCII/中文文字，用几何图形代替 emoji（如\"♥ × 3\"改用红色圆形绘制）\n   - 可安全使用：`Promise`、`async/await`、`fetch`、`CSS Grid`、`Flexbox`、`CSS Variables`、`Inte"},{"path":"skills/myapp-creator/SKILL.md","content":"# myapp-creator\n\nVersion: 1.0.31\n\n让用户用一句话生成可独立打开的单文件 HTML 应用或文档，并落库到 fe-service。\n本 skill **不主动调用 LLM**，所有 HTML / app_name / features 由调用本 skill 的 LLM agent 直接产出后传入工具。\n\n> **⚠️ 最高优先级规则（违反即为严重错误）：**\n> 1. 游戏类应用 **绝对禁止** 包含任何形式的 AI 对手、人机对战、电脑自动下棋/出牌。棋类/对战类游戏 **只允许** 双人同屏轮流操作，不实现任何计算机决策逻辑。\n> 2. 所有应用 **绝对禁止** 出现\"导出\"、\"打印\"、\"保存\"按钮或相关功能代码。\n\n## 触发条件\n\n当用户需求匹配以下任一类别时，本 skill 参与：\n\n### 应用类（交互式 HTML）\n关键词：应用、app、小游戏、游戏、网页、页面、工具、计算器、时钟、日历、翻译器、转换器\n示例：\n- \"做一个贪吃蛇游戏\"\n- \"创建一个番茄钟\"\n- \"生成一个天气页面\"\n- \"给我做一个马里奥游戏\"\n- \"帮我做一个单位转换工具\"\n\n### 文件类（文档式 HTML）\n关键词：文档、文件、word、ppt、演示文稿、slides、pdf、excel、xlsx、表格、报告、简历、周报、日报、总结、方案、计划书\n示例：\n- \"生成一个项目周报\"\n- \"做一个自我介绍PPT\"\n- \"创建一个预算表格\"\n- \"帮我写一份简历\"\n- \"保存一个会议纪要文档\"\n\n### 不触发（交给龙虾其他能力）\n- 图片/绘画类：\"画一张…\"、\"生成一张图片…\"\n- 音频/音乐类：\"生成一首歌…\"、\"合成语音…\"\n- 视频类：\"剪辑一个…\"、\"生成视频…\"\n- 小说/纯文本类：\"写一篇小说…\"、\"写一首诗…\"（纯创作不产出可交互文件）\n- 纯问答/聊天/闲聊\n\n### 判断规则\n用户意图是\"产出一个可独立打开、保存下来反复使用的文件或应用\"时触发本 skill。\n如果用户只是想让你\"说一段话\"或\"回答问题\"，不要触发。\n\n## 触发标记（兼容旧入口）\n\n以下前缀仍可直接触发，不需要语义判断：\n- `[create_app]<描述>` → 创建新应用/文件\n- `[update_app:app_id=<n>]<描述>` → 更新已有应用/文件\n- `[install_check:session=<sid>]` → 安装自检（仅调 myapp_ping）\n- 不带上述前缀 → 按语义判断是否匹配触发条件\n\n## 创建流程\n\n最长等待 300s。若 300s 内不能完成 HTML 生成与 `myapp_register` 调用，直接回复\"应用创建失败，请稍后重试\"。\n\n1. 解析用户描述，判断类型（应用类 or 文件类），产出：\n   - `app_name`：应用/文件名，≤30 字\n   - `features`：JSON 字符串数组，3~8 条，每条 ≤30 字，描述具体功能/内容要点\n   - `html_content`：完整可独立打开的单文件 HTML（按类型选择模板风格）\n\n2. HTML 通用约束：\n   - 自包含：CSS/JS 全部内联在 `<style>` / `<script>`\n   - 不依赖本地资源\n   - 外部资源仅允许：unpkg.com / jsdelivr.net / cdnjs.cloudflare.com\n   - 总长度 ≤ 60KB（UTF-8 字节数）\n   - 不引用任何小度专属 API\n   - 必须包含 `<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">`\n\n   **目标运行环境 — Android 8.1 WebView（Chrome/85）**：\n   所有 HTML 最终运行在搭载 Android 8.1 的智能屏 WebView 中，无鼠标/硬键盘，仅支持触摸和左右滑动。\n   生成代码时必须遵守以下兼容规则：\n\n   **JS / Web API 兼容**（feature detection 优先，禁止仅凭 UA 判断）：\n   - 使用 `typeof IntersectionObserver !== 'undefined'` 等特性检测，不要直接调用可能不存在的 API\n   - 禁止使用 Chrome 86+ 才支持的 API：`CSS.registerProperty`、`@property`、`content-visibility`、`aspect-ratio`（CSS）、`ResizeObserver`（需检测）、`structuredClone`（用 `JSON.parse(JSON.stringify())` 替代）\n   - 禁止使用 ES2020+ 语法：`??=`、`||=`、`&&=`、`import()`动态导入、`BigInt`、`globalThis`（用 `window` 替代）\n   - **禁止使用 `ctx.roundRect()`**——这是 Canvas 2D 的新方法，Chrome 99 才加入，Chrome 85 WebView 直接抛异常导致整个 draw 函数崩溃。必须用以下自定义 helper 替代：\n     ```js\n     function rr(ctx, x, y, w, h, r) {\n       ctx.beginPath();\n       ctx.moveTo(x + r, y);\n       ctx.lineTo(x + w - r, y);  ctx.arcTo(x + w, y,     x + w, y + r,     r);\n       ctx.lineTo(x + w, y + h - r); ctx.arcTo(x + w, y + h, x + w - r, y + h, r);\n       ctx.lineTo(x + r, y + h);  ctx.arcTo(x,     y + h, x,     y + h - r, r);\n       ctx.lineTo(x, y + r);      ctx.arcTo(x,     y,     x + r, y,         r);\n       ctx.closePath();\n     }\n     // 用法：rr(ctx, x, y, w, h, radius); ctx.fill();\n     ```\n   - **禁止在 Canvas 内用 emoji 字符**（如 🌕🔥❤️ 等）——Android WebView 字体渲染异常会抛异常或显示方块。Canvas 内所有文字必须用纯 ASCII/中文文字，用几何图形代替 emoji（如\"♥ × 3\"改用红色圆形绘制）\n   - 可安全使用：`Promise`、`async/await`、`fetch`、`CSS Grid`、`Flexbox`、`CSS Variables`、`Inte"},{"path":"skills/myapp-creator/skills/myapp-creator/SKILL.md","content":"# myapp-creator\n\nVersion: 1.0.31\n\n让用户用一句话生成可独立打开的单文件 HTML 应用或文档，并落库到 fe-service。\n本 skill **不主动调用 LLM**，所有 HTML / app_name / features 由调用本 skill 的 LLM agent 直接产出后传入工具。\n\n> **⚠️ 最高优先级规则（违反即为严重错误）：**\n> 1. 游戏类应用 **绝对禁止** 包含任何形式的 AI 对手、人机对战、电脑自动下棋/出牌。棋类/对战类游戏 **只允许** 双人同屏轮流操作，不实现任何计算机决策逻辑。\n> 2. 所有应用 **绝对禁止** 出现\"导出\"、\"打印\"、\"保存\"按钮或相关功能代码。\n\n## 触发条件\n\n当用户需求匹配以下任一类别时，本 skill 参与：\n\n### 应用类（交互式 HTML）\n关键词：应用、app、小游戏、游戏、网页、页面、工具、计算器、时钟、日历、翻译器、转换器\n示例：\n- \"做一个贪吃蛇游戏\"\n- \"创建一个番茄钟\"\n- \"生成一个天气页面\"\n- \"给我做一个马里奥游戏\"\n- \"帮我做一个单位转换工具\"\n\n### 文件类（文档式 HTML）\n关键词：文档、文件、word、ppt、演示文稿、slides、pdf、excel、xlsx、表格、报告、简历、周报、日报、总结、方案、计划书\n示例：\n- \"生成一个项目周报\"\n- \"做一个自我介绍PPT\"\n- \"创建一个预算表格\"\n- \"帮我写一份简历\"\n- \"保存一个会议纪要文档\"\n\n### 不触发（交给龙虾其他能力）\n- 图片/绘画类：\"画一张…\"、\"生成一张图片…\"\n- 音频/音乐类：\"生成一首歌…\"、\"合成语音…\"\n- 视频类：\"剪辑一个…\"、\"生成视频…\"\n- 小说/纯文本类：\"写一篇小说…\"、\"写一首诗…\"（纯创作不产出可交互文件）\n- 纯问答/聊天/闲聊\n\n### 判断规则\n用户意图是\"产出一个可独立打开、保存下来反复使用的文件或应用\"时触发本 skill。\n如果用户只是想让你\"说一段话\"或\"回答问题\"，不要触发。\n\n## 触发标记（兼容旧入口）\n\n以下前缀仍可直接触发，不需要语义判断：\n- `[create_app]<描述>` → 创建新应用/文件\n- `[update_app:app_id=<n>]<描述>` → 更新已有应用/文件\n- `[install_check:session=<sid>]` → 安装自检（仅调 myapp_ping）\n- 不带上述前缀 → 按语义判断是否匹配触发条件\n\n## 创建流程\n\n最长等待 300s。若 300s 内不能完成 HTML 生成与 `myapp_register` 调用，直接回复\"应用创建失败，请稍后重试\"。\n\n1. 解析用户描述，判断类型（应用类 or 文件类），产出：\n   - `app_name`：应用/文件名，≤30 字\n   - `features`：JSON 字符串数组，3~8 条，每条 ≤30 字，描述具体功能/内容要点\n   - `html_content`：完整可独立打开的单文件 HTML（按类型选择模板风格）\n\n2. HTML 通用约束：\n   - 自包含：CSS/JS 全部内联在 `<style>` / `<script>`\n   - 不依赖本地资源\n   - 外部资源仅允许：unpkg.com / jsdelivr.net / cdnjs.cloudflare.com\n   - 总长度 ≤ 60KB（UTF-8 字节数）\n   - 不引用任何小度专属 API\n   - 必须包含 `<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">`\n\n   **目标运行环境 — Android 8.1 WebView（Chrome/85）**：\n   所有 HTML 最终运行在搭载 Android 8.1 的智能屏 WebView 中，无鼠标/硬键盘，仅支持触摸和左右滑动。\n   生成代码时必须遵守以下兼容规则：\n\n   **JS / Web API 兼容**（feature detection 优先，禁止仅凭 UA 判断）：\n   - 使用 `typeof IntersectionObserver !== 'undefined'` 等特性检测，不要直接调用可能不存在的 API\n   - 禁止使用 Chrome 86+ 才支持的 API：`CSS.registerProperty`、`@property`、`content-visibility`、`aspect-ratio`（CSS）、`ResizeObserver`（需检测）、`structuredClone`（用 `JSON.parse(JSON.stringify())` 替代）\n   - 禁止使用 ES2020+ 语法：`??=`、`||=`、`&&=`、`import()`动态导入、`BigInt`、`globalThis`（用 `window` 替代）\n   - **禁止使用 `ctx.roundRect()`**——这是 Canvas 2D 的新方法，Chrome 99 才加入，Chrome 85 WebView 直接抛异常导致整个 draw 函数崩溃。必须用以下自定义 helper 替代：\n     ```js\n     function rr(ctx, x, y, w, h, r) {\n       ctx.beginPath();\n       ctx.moveTo(x + r, y);\n       ctx.lineTo(x + w - r, y);  ctx.arcTo(x + w, y,     x + w, y + r,     r);\n       ctx.lineTo(x + w, y + h - r); ctx.arcTo(x + w, y + h, x + w - r, y + h, r);\n       ctx.lineTo(x + r, y + h);  ctx.arcTo(x,     y + h, x,     y + h - r, r);\n       ctx.lineTo(x, y + r);      ctx.arcTo(x,     y,     x + r, y,         r);\n       ctx.closePath();\n     }\n     // 用法：rr(ctx, x, y, w, h, radius); ctx.fill();\n     ```\n   - **禁止在 Canvas 内用 emoji 字符**（如 🌕🔥❤️ 等）——Android WebView 字体渲染异常会抛异常或显示方块。Canvas 内所有文字必须用纯 ASCII/中文文字，用几何图形代替 emoji（如\"♥ × 3\"改用红色圆形绘制）\n   - 可安全使用：`Promise`、`async/await`、`fetch`、`CSS Grid`、`Flexbox`、`CSS Variables`、`Inte"},{"path":"README.md","content":"# myapp-creator\n\n让小度用户用一句话造一个 HTML 小应用，落库到 fe-service。\n\n- 包名：ClawHub 页面 URL 为 `https://clawhub.ai/<发布者>/myapp-creator`；**`clawhub install / inspect` 只允许裸 slug `myapp-creator`**（参数里含 `/` 会报 `Invalid slug`）。fe-service `MYAPP_SKILL_NAME` 可仍写全路径，下发 install_prompt 时会自动剥成裸 slug。\n- 当前版本：见 `version.txt`\n- License：MIT-0\n\n## 用户视角是什么\n\n用户在小度 Swan 端的\"我的应用\"页输入一句话（如\"做一个五子棋\"），fe-service 通过对话深链触发龙虾 OpenClaw 的 LLM agent；agent 在本 skill 的 prompt 约束下生成单文件 HTML 与功能列表，再调本 skill 的 `myapp_register` 工具把数据落到 fe-service。\n\n整个流程对用户透明：用户只看见一个进度条和最终的应用卡片。\n\n## 安装方式\n\n### A. 自动安装（推荐）\n\n用户首次进入\"我的应用\"页时，fe-service 会下发一段 install_prompt，由龙虾 agent 自动执行：\n\n```\nclawhub install myapp-creator --version <VERSION>\n```\n\n页面路径里虽有 `zhang6714268/`，**安装时不要带前缀**，否则 `Invalid slug`。\n\n若本机无 `clawhub`，agent 需先 `npm i -g clawhub`（或 `npx clawhub@latest install ...`）。**不要用 `openclaw skills install`**，新版 OpenClaw 的 `skills` 子命令会报 `too many arguments`。\n\n并写入 `~/.mcporter/mcporter.json` 中的环境变量。**用户无需手动操作。**\n\n### B. 手动安装（兜底/调试）\n\n```bash\nclawhub install myapp-creator --version 1.0.0\n# 或：npx --yes clawhub@latest install myapp-creator --version 1.0.0\n```\n\n然后在 **`~/.mcporter/mcporter.json`** 里为 **myapp-creator** 增加 `skills.myapp-creator.env`（与顶层 `mcpServers` **并列**，不是写进 `xiaodu` 里）。若文件中已有 `xiaodu` / `xiaodu-iot` 等，请**合并**如下结构，勿删掉原有 `mcpServers`：\n\n```json\n{\n  \"mcpServers\": { },\n  \"skills\": {\n    \"myapp-creator\": {\n      \"env\": {\n        \"MYAPP_API_BASE\": \"http://group.dumi-SwanSSRNode12-env.dumi.all\",\n        \"MYAPP_API_TOKEN\": \"<由 fe-service 团队下发>\"\n      }\n    }\n  }\n}\n```\n\n最后重新加载（任选其一，以本机 CLI 为准）：\n\n```bash\nopenclaw gateway restart\n# 若无 gateway 子命令：重启 OpenClaw 应用\n```\n\n## 工具清单\n\n| 工具 | 入参 | 用途 |\n|---|---|---|\n| `myapp_register` | dumi_id, cuid, query, app_name, html_content, features | 创建新应用 |\n| `myapp_update`   | app_id, query, html_content, features | 更新已有应用 |\n| `myapp_get`      | app_id | 读应用详情（更新前用） |\n| `myapp_ping`     | session_id, dumi_id, cuid, skill_version | 安装自检 |\n\n详细约束与触发条件见 `SKILL.md`。\n\n## 故障排查\n\n| 现象 | 排查 |\n|---|---|\n| 调 register/update 返回 401 | 检查 `MYAPP_API_TOKEN` 是否与 fe-service 配置一致 |\n| 调 register/update 返回 502 (BOS upload failed) | fe-service 端 BOS_UPLOAD_AK/SK 未配置或权限不足 |\n| 调 register/update 返回 400 | 入参超长或缺字段；检查 `app_name`/`features`/`html_content` 是否符合 SKILL.md 约束 |\n| 调 register/update 返回 timeout | fe-service 不可达；检查 `MYAPP_API_BASE` 与本机网络 |\n| `myapp_get` 404 | app 已被软删，或 app_id 写错 |\n| 安装自检 60s 仍未完成 | 检查 mcporter env 中 token / base 是否正确写入；重启 openclaw |\n| `./publish.sh` 报 `too many arguments for 'skills'` | **openclaw** 不再支持 `skills publish/install`；发布用 `clawhub`，安装用 `clawhub install <裸slug> --version …`（不要 `owner/skill`） |\n| `ping.sh` 报 `param session_id required` | 须用环境变量或传入 JSON；见上文 **§C** |\n\n## 发版\n\n```bash\n# 0) 安装并登录 ClawHub CLI（发布走 clawhub，不要用已弃用的 openclaw skills publish）\nnpm i -g clawhub\nclawhub login\n\n# 1) 改 version.txt 和 clawhub.yaml 中的 version\n# 2) 跑发布（内部: clawhub skill publish . --version <x.y.z>）\n./publish.sh\n\n# 3) 同步更新 fe-service 中的 MYAPP_SKILL_REQUIRED_VERSION\n```\n\n## 安全说明\n\n- 本 skill 代码为公开包，*"},{"path":"skills/myapp-creator/README.md","content":"# myapp-creator\n\n让小度用户用一句话造一个 HTML 小应用，落库到 fe-service。\n\n- 包名：ClawHub 页面 URL 为 `https://clawhub.ai/<发布者>/myapp-creator`；**`clawhub install / inspect` 只允许裸 slug `myapp-creator`**（参数里含 `/` 会报 `Invalid slug`）。fe-service `MYAPP_SKILL_NAME` 可仍写全路径，下发 install_prompt 时会自动剥成裸 slug。\n- 当前版本：见 `version.txt`\n- License：MIT-0\n\n## 用户视角是什么\n\n用户在小度 Swan 端的\"我的应用\"页输入一句话（如\"做一个五子棋\"），fe-service 通过对话深链触发龙虾 OpenClaw 的 LLM agent；agent 在本 skill 的 prompt 约束下生成单文件 HTML 与功能列表，再调本 skill 的 `myapp_register` 工具把数据落到 fe-service。\n\n整个流程对用户透明：用户只看见一个进度条和最终的应用卡片。\n\n## 安装方式\n\n### A. 自动安装（推荐）\n\n用户首次进入\"我的应用\"页时，fe-service 会下发一段 install_prompt，由龙虾 agent 自动执行：\n\n```\nclawhub install myapp-creator --version <VERSION>\n```\n\n页面路径里虽有 `zhang6714268/`，**安装时不要带前缀**，否则 `Invalid slug`。\n\n若本机无 `clawhub`，agent 需先 `npm i -g clawhub`（或 `npx clawhub@latest install ...`）。**不要用 `openclaw skills install`**，新版 OpenClaw 的 `skills` 子命令会报 `too many arguments`。\n\n并写入 `~/.mcporter/mcporter.json` 中的环境变量。**用户无需手动操作。**\n\n### B. 手动安装（兜底/调试）\n\n```bash\nclawhub install myapp-creator --version 1.0.0\n# 或：npx --yes clawhub@latest install myapp-creator --version 1.0.0\n```\n\n然后在 **`~/.mcporter/mcporter.json`** 里为 **myapp-creator** 增加 `skills.myapp-creator.env`（与顶层 `mcpServers` **并列**，不是写进 `xiaodu` 里）。若文件中已有 `xiaodu` / `xiaodu-iot` 等，请**合并**如下结构，勿删掉原有 `mcpServers`：\n\n```json\n{\n  \"mcpServers\": { },\n  \"skills\": {\n    \"myapp-creator\": {\n      \"env\": {\n        \"MYAPP_API_BASE\": \"http://group.dumi-SwanSSRNode12-env.dumi.all\",\n        \"MYAPP_API_TOKEN\": \"<由 fe-service 团队下发>\"\n      }\n    }\n  }\n}\n```\n\n最后重新加载（任选其一，以本机 CLI 为准）：\n\n```bash\nopenclaw gateway restart\n# 若无 gateway 子命令：重启 OpenClaw 应用\n```\n\n## 工具清单\n\n| 工具 | 入参 | 用途 |\n|---|---|---|\n| `myapp_register` | dumi_id, cuid, query, app_name, html_content, features | 创建新应用 |\n| `myapp_update`   | app_id, query, html_content, features | 更新已有应用 |\n| `myapp_get`      | app_id | 读应用详情（更新前用） |\n| `myapp_ping`     | session_id, dumi_id, cuid, skill_version | 安装自检 |\n\n详细约束与触发条件见 `SKILL.md`。\n\n## 故障排查\n\n| 现象 | 排查 |\n|---|---|\n| 调 register/update 返回 401 | 检查 `MYAPP_API_TOKEN` 是否与 fe-service 配置一致 |\n| 调 register/update 返回 502 (BOS upload failed) | fe-service 端 BOS_UPLOAD_AK/SK 未配置或权限不足 |\n| 调 register/update 返回 400 | 入参超长或缺字段；检查 `app_name`/`features`/`html_content` 是否符合 SKILL.md 约束 |\n| 调 register/update 返回 timeout | fe-service 不可达；检查 `MYAPP_API_BASE` 与本机网络 |\n| `myapp_get` 404 | app 已被软删，或 app_id 写错 |\n| 安装自检 60s 仍未完成 | 检查 mcporter env 中 token / base 是否正确写入；重启 openclaw |\n| `./publish.sh` 报 `too many arguments for 'skills'` | **openclaw** 不再支持 `skills publish/install`；发布用 `clawhub`，安装用 `clawhub install <裸slug> --version …`（不要 `owner/skill`） |\n| `ping.sh` 报 `param session_id required` | 须用环境变量或传入 JSON；见上文 **§C** |\n\n## 发版\n\n```bash\n# 0) 安装并登录 ClawHub CLI（发布走 clawhub，不要用已弃用的 openclaw skills publish）\nnpm i -g clawhub\nclawhub login\n\n# 1) 改 version.txt 和 clawhub.yaml 中的 version\n# 2) 跑发布（内部: clawhub skill publish . --version <x.y.z>）\n./publish.sh\n\n# 3) 同步更新 fe-service 中的 MYAPP_SKILL_REQUIRED_VERSION\n```\n\n## 安全说明\n\n- 本 skill 代码为公开包，*"}],"languages":[],"docsSourceLabel":"CLAWHUB","editorialOverview":null,"editorialQuality":{"score":100,"threshold":65,"status":"thin","wordCount":1438,"uniquenessScore":34,"reasons":["uniqueness-below-45"]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-10-10T02:03:40.289Z","emptyReason":"No screenshots, media assets, or demo links are available."},"primaryImageUrl":null,"mediaAssetCount":0,"assets":[],"demoUrl":null},"ownerResources":{"evidence":{"source":"unclaimed","verified":false,"confidence":"low","updatedAt":"2026-10-10T02:03:40.289Z","emptyReason":"This page has not been claimed by the agent owner."},"hasCustomPage":false,"customPageUpdatedAt":null,"customLinks":[],"structuredLinks":{"docsUrl":null,"demoUrl":null,"supportUrl":null,"pricingUrl":null,"statusUrl":null},"customPage":null},"relatedAgents":{"evidence":{"source":"protocol-neighbors","verified":false,"confidence":"medium","updatedAt":"2026-10-10T06:44:31.695Z","emptyReason":null},"items":[{"id":"8ebccd8e-3863-4187-8355-c3f14e1f9edf","entityType":"agent","canonicalPath":"/agent/iofficeai-aionui","slug":"iofficeai-aionui","name":"AionUi","description":"Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!","url":"https://github.com/iOfficeAI/AionUi","homepage":"https://www.aionui.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-10-09T19:11:12.944Z","createdAt":"2026-02-25T03:38:16.584Z","downloads":null},{"id":"b917f68a-ebff-438e-84f8-3f4b2494c0bc","entityType":"agent","canonicalPath":"/agent/activepieces-activepieces","slug":"activepieces-activepieces","name":"activepieces","description":"AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents","url":"https://github.com/activepieces/activepieces","homepage":"https://www.activepieces.com","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-15T02:22:12.426Z","createdAt":"2026-02-25T03:38:12.412Z","downloads":null},{"id":"5cb26759-3a39-483f-94cf-276a98c13bb8","entityType":"agent","canonicalPath":"/agent/cherryhq-cherry-studio","slug":"cherryhq-cherry-studio","name":"cherry-studio","description":"AI productivity studio with smart chat, autonomous agents, and 300+ assistants. Unified access to frontier LLMs","url":"https://github.com/CherryHQ/cherry-studio","homepage":"https://cherry-ai.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-11T14:38:40.986Z","createdAt":"2026-02-25T03:38:19.379Z","downloads":null},{"id":"6f6582d0-5d76-4f0f-b81d-86520247950b","entityType":"agent","canonicalPath":"/agent/copilotkit-copilotkit","slug":"copilotkit-copilotkit","name":"CopilotKit","description":"The Frontend for Agents & Generative UI. React + Angular","url":"https://github.com/CopilotKit/CopilotKit","homepage":"https://docs.copilotkit.ai","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-03-25T09:50:57.846Z","createdAt":"2026-02-25T03:39:14.617Z","downloads":null}],"links":{"hub":"/agent","source":"/agent/source/clawhub","protocols":[{"label":"OpenClaw","href":"/agent/protocol/openclew"}]}}}