[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6CEC6M1EFS17XMAXBPNT0AM":3,"skill-same:01M6CEC6M1EFS17XMAXBPNT0AM":57},{"skillNo":4,"name":5,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":6,"repositoryNo":11,"starsCount":12,"licenseSpdxId":13,"licenseFileUrl":6,"sourceHtmlUrl":14,"categories":15,"keywords":21,"publishedAt":22,"linkOnly":23,"markdownContent":24,"markdownBody":24,"frontMatter":25,"refName":26,"commitSha":27,"skillDirectory":28,"forksCount":29,"repositoryHtmlUrl":30,"repositoryHomepageUrl":6,"ownerHtmlUrl":31,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":32,"install":33,"resources":46},"01M6CEC6M1EFS17XMAXBPNT0AM","canvas",null,"LongIV999\u002FOpenclawd","LongIV999","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F92411512?v=4","01M6CEACAHRWWFRVKBQPSQ2WBM","01M6CEACEJDYKGDKD1P6A7J7VV",0,"MIT","https:\u002F\u002Fgithub.com\u002FLongIV999\u002FOpenclawd\u002Fblob\u002Fmain\u002Fskills\u002Fcanvas\u002FSKILL.md",[16],{"categoryNo":17,"categoryName":18,"taxonomyType":19,"taxonomyTypeDesc":20},"01JBSEEDCAT000000000000015","视觉设计","SCENARIO","使用场景",[5],"2026-11-10 10:12:30",false,"# Canvas Skill\n\nDisplay HTML content on connected OpenClaw nodes (Mac app, iOS, Android).\n\n## Overview\n\nThe canvas tool lets you present web content on any connected node's canvas view. Great for:\n\n- Displaying games, visualizations, dashboards\n- Showing generated HTML content\n- Interactive demos\n\n## How It Works\n\n### Architecture\n\n```\n┌─────────────────┐     ┌──────────────────┐     ┌─────────────┐\n│  Canvas Host    │────▶│   Node Bridge    │────▶│  Node App   │\n│  (HTTP Server)  │     │  (TCP Server)    │     │ (Mac\u002FiOS\u002F   │\n│  Port 18793     │     │  Port 18790      │     │  Android)   │\n└─────────────────┘     └──────────────────┘     └─────────────┘\n```\n\n1. **Canvas Host Server**: Serves static HTML\u002FCSS\u002FJS files from `canvasHost.root` directory\n2. **Node Bridge**: Communicates canvas URLs to connected nodes\n3. **Node Apps**: Render the content in a WebView\n\n### Tailscale Integration\n\nThe canvas host server binds based on `gateway.bind` setting:\n\n| Bind Mode  | Server Binds To     | Canvas URL Uses            |\n| ---------- | ------------------- | -------------------------- |\n| `loopback` | 127.0.0.1           | localhost (local only)     |\n| `lan`      | LAN interface       | LAN IP address             |\n| `tailnet`  | Tailscale interface | Tailscale hostname         |\n| `auto`     | Best available      | Tailscale > LAN > loopback |\n\n**Key insight:** The `canvasHostHostForBridge` is derived from `bridgeHost`. When bound to Tailscale, nodes receive URLs like:\n\n```\nhttp:\u002F\u002F\u003Ctailscale-hostname>:18793\u002F__openclaw__\u002Fcanvas\u002F\u003Cfile>.html\n```\n\nThis is why localhost URLs don't work - the node receives the Tailscale hostname from the bridge!\n\n## Actions\n\n| Action     | Description                          |\n| ---------- | ------------------------------------ |\n| `present`  | Show canvas with optional target URL |\n| `hide`     | Hide the canvas                      |\n| `navigate` | Navigate to a new URL                |\n| `eval`     | Execute JavaScript in the canvas     |\n| `snapshot` | Capture screenshot of canvas         |\n\n## Configuration\n\nIn `~\u002F.openclaw\u002Fopenclaw.json`:\n\n```json\n{\n  \"canvasHost\": {\n    \"enabled\": true,\n    \"port\": 18793,\n    \"root\": \"\u002FUsers\u002Fyou\u002Fclawd\u002Fcanvas\",\n    \"liveReload\": true\n  },\n  \"gateway\": {\n    \"bind\": \"auto\"\n  }\n}\n```\n\n### Live Reload\n\nWhen `liveReload: true` (default), the canvas host:\n\n- Watches the root directory for changes (via chokidar)\n- Injects a WebSocket client into HTML files\n- Automatically reloads connected canvases when files change\n\nGreat for development!\n\n## Workflow\n\n### 1. Create HTML content\n\nPlace files in the canvas root directory (default `~\u002Fclawd\u002Fcanvas\u002F`):\n\n```bash\ncat > ~\u002Fclawd\u002Fcanvas\u002Fmy-game.html \u003C\u003C 'HTML'\n\u003C!DOCTYPE html>\n\u003Chtml>\n\u003Chead>\u003Ctitle>My Game\u003C\u002Ftitle>\u003C\u002Fhead>\n\u003Cbody>\n  \u003Ch1>Hello Canvas!\u003C\u002Fh1>\n\u003C\u002Fbody>\n\u003C\u002Fhtml>\nHTML\n```\n\n### 2. Find your canvas host URL\n\nCheck how your gateway is bound:\n\n```bash\ncat ~\u002F.openclaw\u002Fopenclaw.json | jq '.gateway.bind'\n```\n\nThen construct the URL:\n\n- **loopback**: `http:\u002F\u002F127.0.0.1:18793\u002F__openclaw__\u002Fcanvas\u002F\u003Cfile>.html`\n- **lan\u002Ftailnet\u002Fauto**: `http:\u002F\u002F\u003Chostname>:18793\u002F__openclaw__\u002Fcanvas\u002F\u003Cfile>.html`\n\nFind your Tailscale hostname:\n\n```bash\ntailscale status --json | jq -r '.Self.DNSName' | sed 's\u002F\\.$\u002F\u002F'\n```\n\n### 3. Find connected nodes\n\n```bash\nopenclaw nodes list\n```\n\nLook for Mac\u002FiOS\u002FAndroid nodes with canvas capability.\n\n### 4. Present content\n\n```\ncanvas action:present node:\u003Cnode-id> target:\u003Cfull-url>\n```\n\n**Example:**\n\n```\ncanvas action:present node:mac-63599bc4-b54d-4392-9048-b97abd58343a target:http:\u002F\u002Fpeters-mac-studio-1.sheep-coho.ts.net:18793\u002F__openclaw__\u002Fcanvas\u002Fsnake.html\n```\n\n### 5. Navigate, snapshot, or hide\n\n```\ncanvas action:navigate node:\u003Cnode-id> url:\u003Cnew-url>\ncanvas action:snapshot node:\u003Cnode-id>\ncanvas action:hide node:\u003Cnode-id>\n```\n\n## Debugging\n\n### White screen \u002F content not loading\n\n**Cause:** URL mismatch between server bind and node expectation.\n\n**Debug steps:**\n\n1. Check server bind: `cat ~\u002F.openclaw\u002Fopenclaw.json | jq '.gateway.bind'`\n2. Check what port canvas is on: `lsof -i :18793`\n3. Test URL directly: `curl http:\u002F\u002F\u003Chostname>:18793\u002F__openclaw__\u002Fcanvas\u002F\u003Cfile>.html`\n\n**Solution:** Use the full hostname matching your bind mode, not localhost.\n\n### \"node required\" error\n\nAlways specify `node:\u003Cnode-id>` parameter.\n\n### \"node not connected\" error\n\nNode is offline. Use `openclaw nodes list` to find online nodes.\n\n### Content not updating\n\nIf live reload isn't working:\n\n1. Check `liveReload: true` in config\n2. Ensure file is in the canvas root directory\n3. Check for watcher errors in logs\n\n## URL Path Structure\n\nThe canvas host serves from `\u002F__openclaw__\u002Fcanvas\u002F` prefix:\n\n```\nhttp:\u002F\u002F\u003Chost>:18793\u002F__openclaw__\u002Fcanvas\u002Findex.html  → ~\u002Fclawd\u002Fcanvas\u002Findex.html\nhttp:\u002F\u002F\u003Chost>:18793\u002F__openclaw__\u002Fcanvas\u002Fgames\u002Fsnake.html → ~\u002Fclawd\u002Fcanvas\u002Fgames\u002Fsnake.html\n```\n\nThe `\u002F__openclaw__\u002Fcanvas\u002F` prefix is defined by `CANVAS_HOST_PATH` constant.\n\n## Tips\n\n- Keep HTML self-contained (inline CSS\u002FJS) for best results\n- Use the default index.html as a test page (has bridge diagnostics)\n- The canvas persists until you `hide` it or navigate away\n- Live reload makes development fast - just save and it updates!\n- A2UI JSON push is WIP - use HTML files for now\n",{},"main","56e72bb5549c5fa0a0470501619427b4f104aa92","skills\u002Fcanvas",1,"https:\u002F\u002Fgithub.com\u002FLongIV999\u002FOpenclawd","https:\u002F\u002Fgithub.com\u002FLongIV999","2026-02-15 08:26:56",{"source":7,"skillName":5,"commands":34,"prompt":44,"zipAvailable":45},[35,38,41],{"tool":36,"command":37},"npx","npx skills add LongIV999\u002FOpenclawd --skill canvas",{"tool":39,"command":40},"bunx","bunx skills add LongIV999\u002FOpenclawd --skill canvas",{"tool":42,"command":43},"pnpm","pnpm dlx skills add LongIV999\u002FOpenclawd --skill canvas","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FLongIV999\u002FOpenclawd\u002Fblob\u002Fmain\u002Fskills\u002Fcanvas\u002FSKILL.md",true,[47,53],{"path":48,"resourceType":49,"resourceTypeDesc":50,"sizeBytes":51,"sourceHtmlUrl":52},"SOURCE.md","REFERENCE","参考文档",200,"https:\u002F\u002Fgithub.com\u002Fjiayaoqijia\u002Fcryptoskill\u002Fblob\u002Fmain\u002Fskills\u002Fexchanges\u002Fbinance-mcp-server\u002FSOURCE.md",{"path":54,"resourceType":49,"resourceTypeDesc":50,"sizeBytes":55,"sourceHtmlUrl":56},"TRUST.auto.yaml",2217,"https:\u002F\u002Fgithub.com\u002Fjiayaoqijia\u002Fcryptoskill\u002Fblob\u002Fmain\u002Fskills\u002Fexchanges\u002Fbinance-mcp-server\u002FTRUST.auto.yaml",[58,67,78,98,111,125],{"skillNo":59,"name":60,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":6,"repositoryNo":11,"starsCount":12,"licenseSpdxId":13,"licenseFileUrl":6,"sourceHtmlUrl":61,"categories":62,"keywords":63,"publishedAt":22},"01M6CEFMR22ANX8BMWBY9J8VW3","youtube-to-obsidian","https:\u002F\u002Fgithub.com\u002FLongIV999\u002FOpenclawd\u002Fblob\u002Fmain\u002Fskills\u002Fyoutube-to-obsidian\u002FSKILL.md",[],[64,65,66],"youtube to obsidian","youtube","obsidian",{"skillNo":68,"name":69,"nameZh":6,"description":70,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":6,"repositoryNo":11,"starsCount":12,"licenseSpdxId":13,"licenseFileUrl":6,"sourceHtmlUrl":71,"categories":72,"keywords":73,"publishedAt":22},"01M6CEFJW8HMZ7EY2C7FXDN8HG","weather","Get current weather and forecasts (no API key required).","https:\u002F\u002Fgithub.com\u002FLongIV999\u002FOpenclawd\u002Fblob\u002Fmain\u002Fskills\u002Fweather\u002FSKILL.md",[],[69,74,75,76,77],"help","https","api","curl",{"skillNo":79,"name":80,"nameZh":6,"description":81,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":6,"repositoryNo":11,"starsCount":12,"licenseSpdxId":13,"licenseFileUrl":6,"sourceHtmlUrl":82,"categories":83,"keywords":84,"publishedAt":22},"01M6CEFGENXXNDQTCWVC82VBFT","wacli","Send WhatsApp messages to other people or search\u002Fsync WhatsApp history via the wacli CLI (not for normal user chats).","https:\u002F\u002Fgithub.com\u002FLongIV999\u002FOpenclawd\u002Fblob\u002Fmain\u002Fskills\u002Fwacli\u002FSKILL.md",[],[80,85,86,87,88,75,89,90,91,92,93,94,95,96,97],"github","cli","sync","search","messages","history","cmd","brew","user","install","tap","whatsapp","go",{"skillNo":99,"name":100,"nameZh":6,"description":101,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":6,"repositoryNo":11,"starsCount":12,"licenseSpdxId":13,"licenseFileUrl":6,"sourceHtmlUrl":102,"categories":103,"keywords":104,"publishedAt":22},"01M6CEFERBY58R1VW8Z8ZCYEMY","voice-call","Start voice calls via the OpenClaw voice-call plugin.","https:\u002F\u002Fgithub.com\u002FLongIV999\u002FOpenclawd\u002Fblob\u002Fmain\u002Fskills\u002Fvoice-call\u002FSKILL.md",[],[105,106,107,108,109,110],"voice","voice call","openclaw","plugin","plugins","start",{"skillNo":112,"name":113,"nameZh":6,"description":114,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":6,"repositoryNo":11,"starsCount":12,"licenseSpdxId":13,"licenseFileUrl":6,"sourceHtmlUrl":115,"categories":116,"keywords":120,"publishedAt":22},"01M6CEFD0C501CWXD9E4BMV2B2","video-frames","Extract frames or short clips from videos using ffmpeg.","https:\u002F\u002Fgithub.com\u002FLongIV999\u002FOpenclawd\u002Fblob\u002Fmain\u002Fskills\u002Fvideo-frames\u002FSKILL.md",[117],{"categoryNo":118,"categoryName":119,"taxonomyType":19,"taxonomyTypeDesc":20},"01JBSEEDCAT000000000000016","图像与多媒体",[121,122,92,75,123,124,94],"Video","video frames","short","extract",{"skillNo":126,"name":127,"nameZh":6,"description":128,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":6,"repositoryNo":11,"starsCount":12,"licenseSpdxId":13,"licenseFileUrl":6,"sourceHtmlUrl":129,"categories":130,"keywords":139,"publishedAt":22},"01M6CEFAJ48939CYW40VTBYGVP","trello","Manage Trello boards, lists, and cards via the Trello REST API.","https:\u002F\u002Fgithub.com\u002FLongIV999\u002FOpenclawd\u002Fblob\u002Fmain\u002Fskills\u002Ftrello\u002FSKILL.md",[131,134],{"categoryNo":132,"categoryName":133,"taxonomyType":19,"taxonomyTypeDesc":20},"01JBSEEDCAT000000000000010","后端与接口",{"categoryNo":135,"categoryName":136,"taxonomyType":137,"taxonomyTypeDesc":138},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[127,75,140,141,142,76,143,144,145],"cloud","developer","REST","jq","atlassian","manage"]