{"id":1,"date":"2026-05-09T11:43:47","date_gmt":"2026-05-09T11:43:47","guid":{"rendered":"https:\/\/news.miaotao.com\/?p=1"},"modified":"2026-05-10T22:34:39","modified_gmt":"2026-05-10T22:34:39","slug":"hello-world","status":"publish","type":"post","link":"https:\/\/news.miaotao.com\/index.php\/2026\/05\/09\/hello-world\/","title":{"rendered":"\u5173\u4e8e\u591a\u6a21\u6001\u6d4f\u89c8\u5668\u7684\u601d\u8003"},"content":{"rendered":"<p>&lt;!DOCTYPE html&gt;<br \/>\n&lt;html lang=&#8221;zh-CN&#8221;&gt;<br \/>\n&lt;head&gt;<br \/>\n    &lt;meta charset=&#8221;UTF-8&#8243;&gt;<br \/>\n    &lt;meta name=&#8221;viewport&#8221; content=&#8221;width=device-width, initial-scale=1.0, user-scalable=no&#8221;&gt;<br \/>\n    &lt;title&gt;\u591a\u6a21\u6001\u667a\u80fd\u6d4f\u89c8\u5668 \u00b7 \u624b\u52bf\u8bed\u97f3\u4ea4\u4e92\u6f14\u793a&lt;\/title&gt;<br \/>\n    &lt;!&#8211; TensorFlow.js + HandPose \u6a21\u578b (\u8f7b\u91cf\u624b\u52bf\u8bc6\u522b) &#8211;&gt;<br \/>\n    &lt;script src=&#8221;https:\/\/cdn.jsdelivr.net\/npm\/@tensorflow\/tfjs@3.18.0\/dist\/tf.min.js&#8221;&gt;&lt;\/script&gt;<br \/>\n    &lt;script src=&#8221;https:\/\/cdn.jsdelivr.net\/npm\/@tensorflow-models\/handpose@0.0.7\/dist\/handpose.min.js&#8221;&gt;&lt;\/script&gt;<br \/>\n    &lt;style&gt;<br \/>\n        * {<br \/>\n            box-sizing: border-box;<br \/>\n            user-select: none; \/* \u907f\u514d\u62d6\u52a8\u5e72\u6270, \u4f46\u4fdd\u7559\u6587\u5b57\u590d\u5236 *\/<br \/>\n        }<br \/>\n        body {<br \/>\n            margin: 0;<br \/>\n            min-height: 100vh;<br \/>\n            background: linear-gradient(145deg, #0a0f1e 0%, #0c1222 100%);<br \/>\n            font-family: &#8216;Inter&#8217;, &#8216;Segoe UI&#8217;, system-ui, -apple-system, sans-serif;<br \/>\n            display: flex;<br \/>\n            justify-content: center;<br \/>\n            align-items: center;<br \/>\n            padding: 20px;<br \/>\n        }<br \/>\n        \/* \u4e3b\u9762\u677f &#8211; \u672a\u6765\u611f\u73bb\u7483\u98ce\u683c *\/<br \/>\n        .demo-container {<br \/>\n            max-width: 1400px;<br \/>\n            width: 100%;<br \/>\n            background: rgba(15, 25, 45, 0.65);<br \/>\n            backdrop-filter: blur(12px);<br \/>\n            border-radius: 48px;<br \/>\n            border: 1px solid rgba(72, 187, 255, 0.25);<br \/>\n            box-shadow: 0 25px 45px rgba(0,0,0,0.4), 0 0 0 1px rgba(0, 255, 255, 0.1) inset;<br \/>\n            padding: 24px;<br \/>\n            transition: all 0.3s ease;<br \/>\n        }<br \/>\n        h1 {<br \/>\n            margin: 0 0 8px 0;<br \/>\n            font-size: 1.9rem;<br \/>\n            font-weight: 600;<br \/>\n            background: linear-gradient(135deg, #A0E9FF, #6C63FF, #FF6B9D);<br \/>\n            -webkit-background-clip: text;<br \/>\n            background-clip: text;<br \/>\n            color: transparent;<br \/>\n            letter-spacing: -0.3px;<br \/>\n        }<br \/>\n        .sub {<br \/>\n            color: #8e9bb5;<br \/>\n            margin-bottom: 24px;<br \/>\n            border-left: 3px solid #3b82f6;<br \/>\n            padding-left: 16px;<br \/>\n            font-weight: 400;<br \/>\n            font-size: 0.9rem;<br \/>\n        }<br \/>\n        \/* \u53cc\u680f\u5e03\u5c40 *\/<br \/>\n        .sensor-grid {<br \/>\n            display: flex;<br \/>\n            flex-wrap: wrap;<br \/>\n            gap: 24px;<br \/>\n        }<br \/>\n        .camera-card {<br \/>\n            flex: 2;<br \/>\n            min-width: 280px;<br \/>\n            background: rgba(0, 0, 0, 0.45);<br \/>\n            border-radius: 32px;<br \/>\n            backdrop-filter: blur(4px);<br \/>\n            padding: 16px;<br \/>\n            border: 1px solid rgba(59,130,246,0.3);<br \/>\n        }<br \/>\n        .voice-card {<br \/>\n            flex: 1.2;<br \/>\n            min-width: 260px;<br \/>\n            background: rgba(0, 0, 0, 0.45);<br \/>\n            border-radius: 32px;<br \/>\n            padding: 16px;<br \/>\n            border: 1px solid rgba(168,85,247,0.3);<br \/>\n        }<br \/>\n        .video-wrapper {<br \/>\n            position: relative;<br \/>\n            background: #000;<br \/>\n            border-radius: 24px;<br \/>\n            overflow: hidden;<br \/>\n            aspect-ratio: 4 \/ 3;<br \/>\n            margin-bottom: 12px;<br \/>\n            box-shadow: 0 8px 20px rgba(0,0,0,0.5);<br \/>\n        }<br \/>\n        video, canvas {<br \/>\n            position: absolute;<br \/>\n            top: 0;<br \/>\n            left: 0;<br \/>\n            width: 100%;<br \/>\n            height: 100%;<br \/>\n            object-fit: cover;<br \/>\n            border-radius: 20px;<br \/>\n        }<br \/>\n        canvas {<br \/>\n            pointer-events: none;<br \/>\n            z-index: 2;<br \/>\n        }<br \/>\n        video {<br \/>\n            z-index: 1;<br \/>\n            transform: scaleX(-1); \/* \u955c\u50cf\uff0c\u66f4\u81ea\u7136 *\/<br \/>\n        }<br \/>\n        .gesture-status {<br \/>\n            background: #0f172ad9;<br \/>\n            border-radius: 60px;<br \/>\n            padding: 8px 16px;<br \/>\n            margin: 12px 0 8px;<br \/>\n            display: flex;<br \/>\n            align-items: center;<br \/>\n            justify-content: space-between;<br \/>\n            border: 1px solid #38bdf8;<br \/>\n        }<br \/>\n        .gesture-label {<br \/>\n            font-weight: 600;<br \/>\n            background: #1e293b;<br \/>\n            padding: 6px 14px;<br \/>\n            border-radius: 40px;<br \/>\n            font-size: 0.85rem;<br \/>\n            color: #b9ffec;<br \/>\n        }<br \/>\n        .command-badge {<br \/>\n            background: #2dd4bf20;<br \/>\n            padding: 5px 12px;<br \/>\n            border-radius: 24px;<br \/>\n            font-family: monospace;<br \/>\n            font-size: 0.8rem;<br \/>\n            color: #7dd3fc;<br \/>\n        }<br \/>\n        .btn-group {<br \/>\n            display: flex;<br \/>\n            gap: 12px;<br \/>\n            margin-top: 16px;<br \/>\n            flex-wrap: wrap;<br \/>\n        }<br \/>\n        .btn {<br \/>\n            background: #1e2a47;<br \/>\n            border: none;<br \/>\n            padding: 8px 18px;<br \/>\n            border-radius: 60px;<br \/>\n            color: white;<br \/>\n            font-weight: 500;<br \/>\n            backdrop-filter: blur(8px);<br \/>\n            cursor: pointer;<br \/>\n            transition: all 0.2s ease;<br \/>\n            font-size: 0.8rem;<br \/>\n            display: inline-flex;<br \/>\n            align-items: center;<br \/>\n            gap: 6px;<br \/>\n            border: 1px solid rgba(255,255,255,0.1);<br \/>\n        }<br \/>\n        .btn-primary {<br \/>\n            background: #3b82f6;<br \/>\n            box-shadow: 0 2px 8px #3b82f680;<br \/>\n        }<br \/>\n        .btn-primary:hover {<br \/>\n            background: #2563eb;<br \/>\n            transform: scale(1.02);<br \/>\n        }<br \/>\n        .voice-log {<br \/>\n            background: #010409aa;<br \/>\n            border-radius: 24px;<br \/>\n            padding: 14px;<br \/>\n            margin-top: 18px;<br \/>\n            max-height: 200px;<br \/>\n            overflow-y: auto;<br \/>\n            font-family: monospace;<br \/>\n            font-size: 0.8rem;<br \/>\n        }<br \/>\n        .log-entry {<br \/>\n            border-bottom: 1px solid #2d3a5e;<br \/>\n            padding: 6px 0;<br \/>\n            color: #cbd5e6;<br \/>\n        }<br \/>\n        .command-list {<br \/>\n            margin-top: 12px;<br \/>\n            background: #00000030;<br \/>\n            border-radius: 20px;<br \/>\n            padding: 10px;<br \/>\n            font-size: 0.7rem;<br \/>\n        }<br \/>\n        .footer-note {<br \/>\n            margin-top: 24px;<br \/>\n            text-align: center;<br \/>\n            font-size: 0.7rem;<br \/>\n            color: #5f6c8c;<br \/>\n        }<br \/>\n        @keyframes pulse {<br \/>\n            0% { opacity: 0.6; }<br \/>\n            100% { opacity: 1; text-shadow: 0 0 3px cyan; }<br \/>\n        }<br \/>\n        .listening {<br \/>\n            color: #22d3ee;<br \/>\n            animation: pulse 0.8s infinite;<br \/>\n        }<br \/>\n        hr {<br \/>\n            border-color: #2d3748;<br \/>\n            margin: 12px 0;<br \/>\n        }<br \/>\n    &lt;\/style&gt;<br \/>\n&lt;\/head&gt;<br \/>\n&lt;body&gt;<br \/>\n&lt;div class=&#8221;demo-container&#8221;&gt;<br \/>\n    &lt;h1&gt;\ud83c\udf99\ufe0f\u270b \u591a\u6a21\u6001\u795e\u7ecf\u6d4f\u89c8\u5668 \u00b7 \u6982\u5ff5\u6f14\u793a&lt;\/h1&gt;<br \/>\n    &lt;div class=&#8221;sub&#8221;&gt;\u300c\u6444\u50cf\u5934\u624b\u52bf + \u8bed\u97f3\u6307\u4ee4\u300d\u81ea\u7136\u4ea4\u4e92 | \u65b0\u4e00\u4ee3\u667a\u80fd\u6d4f\u89c8\u8303\u578b&lt;\/div&gt;<\/p>\n<p>    &lt;div class=&#8221;sensor-grid&#8221;&gt;<br \/>\n        &lt;!&#8211; \u5de6\u4fa7: \u6444\u50cf\u5934\u624b\u52bf\u8bc6\u522b\u533a &#8211;&gt;<br \/>\n        &lt;div class=&#8221;camera-card&#8221;&gt;<br \/>\n            &lt;div style=&#8221;display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;&#8221;&gt;<br \/>\n                &lt;span style=&#8221;font-weight: 500;&#8221;&gt;\ud83d\udd90\ufe0f \u624b\u52bf\u8ffd\u8e2a &amp; \u547d\u4ee4\u6620\u5c04&lt;\/span&gt;<br \/>\n                &lt;span class=&#8221;command-badge&#8221; id=&#8221;handModelStatus&#8221;&gt;\u26a1 \u52a0\u8f7d\u6a21\u578b\u4e2d&#8230;&lt;\/span&gt;<br \/>\n            &lt;\/div&gt;<br \/>\n            &lt;div class=&#8221;video-wrapper&#8221;&gt;<br \/>\n                &lt;video id=&#8221;webcam&#8221; autoplay muted playsinline&gt;&lt;\/video&gt;<br \/>\n                &lt;canvas id=&#8221;handCanvas&#8221; width=&#8221;640&#8243; height=&#8221;480&#8243;&gt;&lt;\/canvas&gt;<br \/>\n            &lt;\/div&gt;<br \/>\n            &lt;div class=&#8221;gesture-status&#8221;&gt;<br \/>\n                &lt;span&gt;\ud83e\udd1f \u5f53\u524d\u624b\u52bf\u8bc6\u522b&lt;\/span&gt;<br \/>\n                &lt;span class=&#8221;gesture-label&#8221; id=&#8221;gestureResult&#8221;&gt;\u2014\u2014&lt;\/span&gt;<br \/>\n                &lt;span&gt;\ud83d\udc49 \u89e6\u53d1\u52a8\u4f5c: &lt;span id=&#8221;lastAction&#8221;&gt;\u65e0&lt;\/span&gt;&lt;\/span&gt;<br \/>\n            &lt;\/div&gt;<br \/>\n            &lt;div class=&#8221;btn-group&#8221;&gt;<br \/>\n                &lt;button class=&#8221;btn&#8221; id=&#8221;enableCamBtn&#8221;&gt;\ud83d\udcf7 \u5f00\u542f\u6444\u50cf\u5934&lt;\/button&gt;<br \/>\n                &lt;button class=&#8221;btn&#8221; id=&#8221;resetHandBtn&#8221;&gt;\ud83d\udd04 \u91cd\u8f7d\u624b\u52bf\u6a21\u578b&lt;\/button&gt;<br \/>\n            &lt;\/div&gt;<br \/>\n            &lt;div class=&#8221;command-list&#8221;&gt;<br \/>\n                \ud83c\udfaf &lt;strong&gt;\u624b\u52bf\u6307\u4ee4\u96c6&lt;\/strong&gt; (\u6f14\u793a\u7248) &lt;br&gt;<br \/>\n                \ud83d\udc4d &lt;strong&gt;\u5927\u62c7\u6307\u7ad6\u8d77&lt;\/strong&gt; \u2192 \u6253\u5f00\u767e\u5ea6 \u00b7 \u270c\ufe0f &lt;strong&gt;V\u5b57\u624b\u52bf&lt;\/strong&gt; \u2192 \u6253\u5f00Google &lt;br&gt;<br \/>\n                \u270a &lt;strong&gt;\u63e1\u62f3&lt;\/strong&gt; \u2192 \u9875\u9762\u6eda\u52a8\u5230\u5e95\u90e8 \u00b7 \ud83d\udd90\ufe0f &lt;strong&gt;\u624b\u638c\u5f20\u5f00&lt;\/strong&gt; \u2192 \u5237\u65b0\u5f53\u524d\u9875 &lt;br&gt;<br \/>\n                \ud83d\udc46 &lt;strong&gt;\u98df\u6307\u6307\u5411 + \u7a33\u5b9a&lt;\/strong&gt; \u2192 \u8fd4\u56de\u4e0a\u4e00\u9875 (\u4f53\u9a8c\u672a\u6765\u611f)<br \/>\n            &lt;\/div&gt;<br \/>\n        &lt;\/div&gt;<\/p>\n<p>        &lt;!&#8211; \u53f3\u4fa7: \u8bed\u97f3\u63a7\u5236\u533a\u57df &#8211;&gt;<br \/>\n        &lt;div class=&#8221;voice-card&#8221;&gt;<br \/>\n            &lt;div style=&#8221;display: flex; justify-content: space-between; align-items: center;&#8221;&gt;<br \/>\n                &lt;span style=&#8221;font-weight: 500;&#8221;&gt;\ud83c\udf99\ufe0f \u795e\u7ecf\u8bed\u97f3\u63a7\u5236&lt;\/span&gt;<br \/>\n                &lt;button id=&#8221;voiceBtn&#8221; class=&#8221;btn btn-primary&#8221; style=&#8221;padding: 6px 18px;&#8221;&gt;\ud83c\udfa4 \u5f00\u59cb\u8046\u542c&lt;\/button&gt;<br \/>\n            &lt;\/div&gt;<br \/>\n            &lt;div style=&#8221;margin-top: 12px;&#8221;&gt;<br \/>\n                &lt;div style=&#8221;background: #00000055; border-radius: 40px; padding: 8px 12px;&#8221;&gt;<br \/>\n                    &lt;span&gt;\u2728 \u6307\u4ee4\u72b6\u6001: &lt;\/span&gt;&lt;strong id=&#8221;voiceStatus&#8221;&gt;\u5f85\u547d&lt;\/strong&gt;<br \/>\n                &lt;\/div&gt;<br \/>\n                &lt;div style=&#8221;margin-top: 14px;&#8221;&gt;<br \/>\n                    &lt;span&gt;\ud83d\udcdd \u8bc6\u522b\u6587\u672c: &lt;\/span&gt;<br \/>\n                    &lt;div id=&#8221;speechText&#8221; style=&#8221;background:#0F172A; border-radius: 20px; padding: 12px; margin-top: 6px; min-height: 55px; color:#b9e6ff;&#8221;&gt;\u2014&lt;\/div&gt;<br \/>\n                &lt;\/div&gt;<br \/>\n            &lt;\/div&gt;<br \/>\n            &lt;div class=&#8221;voice-log&#8221;&gt;<br \/>\n                &lt;div&gt;\ud83d\udccb \u4ea4\u4e92\u65e5\u5fd7 (\u624b\u52bf\/\u8bed\u97f3)&lt;\/div&gt;<br \/>\n                &lt;div id=&#8221;logContainer&#8221;&gt;<br \/>\n                    &lt;div class=&#8221;log-entry&#8221;&gt;\u2728 \u7cfb\u7edf\u5c31\u7eea\uff0c\u8bf7\u5141\u8bb8\u6444\u50cf\u5934\u548c\u9ea6\u514b\u98ce\u6743\u9650&lt;\/div&gt;<br \/>\n                &lt;\/div&gt;<br \/>\n            &lt;\/div&gt;<br \/>\n            &lt;div class=&#8221;command-list&#8221; style=&#8221;margin-top: 12px;&#8221;&gt;<br \/>\n                \ud83d\udde3\ufe0f &lt;strong&gt;\u8bed\u97f3\u547d\u4ee4\u793a\u4f8b&lt;\/strong&gt; (\u4e2d\u82f1\u6587\u6df7\u5408)&lt;br&gt;<br \/>\n                \u2022 \u201c\u6253\u5f00\u767e\u5ea6\u201d \/ \u201c\u641c\u7d22\u5929\u6c14\u201d (\u6f14\u793a\u8df3\u8f6c\u767e\u5ea6\u5e76\u641c\u7d22\u5173\u952e\u8bcd) &lt;br&gt;<br \/>\n                \u2022 \u201c\u5237\u65b0\u9875\u9762\u201d \/ \u201c\u6eda\u52a8\u5230\u5e95\u90e8\u201d \/ \u201c\u8fd4\u56de\u4e0a\u4e00\u9875\u201d&lt;br&gt;<br \/>\n                \u2022 \u201c\u6253\u5f00\u8c37\u6b4c\u201d \/ \u201c\u6253\u5f00GitHub\u201d&lt;br&gt;<br \/>\n                \u2022 \u201c\u4f60\u597d\u6d4f\u89c8\u5668\u201d (\u95ee\u5019\u53cd\u9988)<br \/>\n            &lt;\/div&gt;<br \/>\n        &lt;\/div&gt;<br \/>\n    &lt;\/div&gt;<br \/>\n    &lt;div class=&#8221;footer-note&#8221;&gt;<br \/>\n        \u26a1 \u65b0\u4e00\u4ee3\u591a\u6a21\u6001\u63a2\u7d22 | \u624b\u52bf\u57fa\u4e8e HandPose \u6a21\u578b (\u5b9e\u65f6) | \u8bed\u97f3\u4f7f\u7528 Web Speech API | \u6f14\u793a\u955c\u50cf\u6444\u50cf\u5934 | \u4ea4\u4e92\u5f0f\u8df3\u8f6c\/\u6eda\u52a8\/\u5237\u65b0<br \/>\n    &lt;\/div&gt;<br \/>\n&lt;\/div&gt;<\/p>\n<p>&lt;script&gt;<br \/>\n    \/\/ &#8212;&#8212;&#8211; DOM \u5143\u7d20\u7ed1\u5b9a &#8212;&#8212;&#8212;-<br \/>\n    const video = document.getElementById(&#8216;webcam&#8217;);<br \/>\n    const canvas = document.getElementById(&#8216;handCanvas&#8217;);<br \/>\n    const ctx = canvas.getContext(&#8216;2d&#8217;);<br \/>\n    const gestureSpan = document.getElementById(&#8216;gestureResult&#8217;);<br \/>\n    const lastActionSpan = document.getElementById(&#8216;lastAction&#8217;);<br \/>\n    const logContainer = document.getElementById(&#8216;logContainer&#8217;);<br \/>\n    const handModelStatusSpan = document.getElementById(&#8216;handModelStatus&#8217;);<br \/>\n    const voiceStatusSpan = document.getElementById(&#8216;voiceStatus&#8217;);<br \/>\n    const speechTextDiv = document.getElementById(&#8216;speechText&#8217;);<br \/>\n    const voiceBtn = document.getElementById(&#8216;voiceBtn&#8217;);<br \/>\n    const enableCamBtn = document.getElementById(&#8216;enableCamBtn&#8217;);<br \/>\n    const resetHandBtn = document.getElementById(&#8216;resetHandBtn&#8217;);<\/p>\n<p>    \/\/ &#8212;&#8212;&#8212;- \u5168\u5c40\u53d8\u91cf &#8212;&#8212;&#8212;-<br \/>\n    let handModel = null;          \/\/ handpose \u6a21\u578b\u5b9e\u4f8b<br \/>\n    let animationId = null;<br \/>\n    let cameraActive = false;<br \/>\n    let lastGestureExecuted = &#8220;&#8221;;   \/\/ \u907f\u514d\u540c\u4e00\u624b\u52bf\u8fde\u7eed\u89e6\u53d1 (\u9632\u6296)<br \/>\n    let lastExecTime = 0;<br \/>\n    const GESTURE_COOLDOWN = 1200;  \/\/ ms<\/p>\n<p>    \/\/ \u8f85\u52a9\u51fd\u6570: \u6dfb\u52a0\u65e5\u5fd7<br \/>\n    function addLog(message, type = &#8220;info&#8221;) {<br \/>\n        const div = document.createElement(&#8216;div&#8217;);<br \/>\n        div.className = &#8216;log-entry&#8217;;<br \/>\n        const time = new Date().toLocaleTimeString();<br \/>\n        div.innerHTML = `[${time}] ${message}`;<br \/>\n        logContainer.prepend(div);<br \/>\n        if(logContainer.children.length &gt; 18) logContainer.removeChild(logContainer.lastChild);<br \/>\n        if(type === &#8216;action&#8217;) {<br \/>\n            lastActionSpan.innerText = message.split(&#8216;\u2192&#8217;)[1] || message;<br \/>\n        }<br \/>\n    }<\/p>\n<p>    \/\/ \u6267\u884c\u6d4f\u89c8\u5668\u52a8\u4f5c (\u6838\u5fc3\u4ea4\u4e92)<br \/>\n    async function executeAction(actionName, extra = null) {<br \/>\n        const now = Date.now();<br \/>\n        if(now &#8211; lastExecTime &lt; 500) return; \/\/ \u9632\u6b62\u9ad8\u9891<br \/>\n        lastExecTime = now;<br \/>\n        addLog(`\ud83c\udfac \u6267\u884c\u52a8\u4f5c: ${actionName} ${extra ? &#8216;(&#8216;+extra+&#8217;)&#8217; : &#8221;}`, &#8216;action&#8217;);<br \/>\n        switch(actionName) {<br \/>\n            case &#8220;open_baidu&#8221;:<br \/>\n                window.open(&#8216;https:\/\/www.baidu.com&#8217;, &#8216;_blank&#8217;);<br \/>\n                addLog(`\ud83c\udf10 \u5df2\u5728\u65b0\u6807\u7b7e\u9875\u6253\u5f00\u767e\u5ea6`, &#8216;action&#8217;);<br \/>\n                break;<br \/>\n            case &#8220;open_google&#8221;:<br \/>\n                window.open(&#8216;https:\/\/www.google.com&#8217;, &#8216;_blank&#8217;);<br \/>\n                addLog(`\ud83d\udd0d \u6253\u5f00 Google`, &#8216;action&#8217;);<br \/>\n                break;<br \/>\n            case &#8220;open_github&#8221;:<br \/>\n                window.open(&#8216;https:\/\/github.com&#8217;, &#8216;_blank&#8217;);<br \/>\n                addLog(`\ud83d\udc19 \u6253\u5f00 GitHub`, &#8216;action&#8217;);<br \/>\n                break;<br \/>\n            case &#8220;refresh&#8221;:<br \/>\n                location.reload();<br \/>\n                break;<br \/>\n            case &#8220;scroll_bottom&#8221;:<br \/>\n                window.scrollTo({ top: document.body.scrollHeight, behavior: &#8216;smooth&#8217; });<br \/>\n                addLog(`\ud83d\udcdc \u6eda\u52a8\u81f3\u9875\u9762\u5e95\u90e8`, &#8216;action&#8217;);<br \/>\n                break;<br \/>\n            case &#8220;back&#8221;:<br \/>\n                history.back();<br \/>\n                addLog(`\u25c0\ufe0f \u6267\u884c\u8fd4\u56de\u4e0a\u4e00\u9875`, &#8216;action&#8217;);<br \/>\n                break;<br \/>\n            case &#8220;search_weather&#8221;:<br \/>\n                let query = extra || &#8220;\u4eca\u65e5\u5929\u6c14&#8221;;<br \/>\n                window.open(`https:\/\/www.baidu.com\/s?wd=${encodeURIComponent(query)}`, &#8216;_blank&#8217;);<br \/>\n                addLog(`\u2601\ufe0f \u641c\u7d22: ${query}`, &#8216;action&#8217;);<br \/>\n                break;<br \/>\n            case &#8220;hello_browser&#8221;:<br \/>\n                speak(&#8220;\u4f60\u597d\uff0c\u65b0\u4e00\u4ee3\u667a\u80fd\u6d4f\u89c8\u5668\u6b63\u5728\u4e3a\u60a8\u670d\u52a1&#8221;);<br \/>\n                addLog(`\ud83d\udc4b \u8bed\u97f3\u95ee\u5019\u53cd\u9988`, &#8216;action&#8217;);<br \/>\n                break;<br \/>\n            default:<br \/>\n                if(actionName.startsWith(&#8220;search_&#8221;)) {<br \/>\n                    let kw = extra || &#8220;AI\u6d4f\u89c8\u5668&#8221;;<br \/>\n                    window.open(`https:\/\/www.baidu.com\/s?wd=${encodeURIComponent(kw)}`, &#8216;_blank&#8217;);<br \/>\n                    addLog(`\ud83d\udd0e \u667a\u80fd\u641c\u7d22: ${kw}`, &#8216;action&#8217;);<br \/>\n                }<br \/>\n        }<br \/>\n    }<\/p>\n<p>    \/\/ \u8bed\u97f3\u5408\u6210\u53cd\u9988<br \/>\n    function speak(text) {<br \/>\n        if(!window.speechSynthesis) return;<br \/>\n        const utterance = new SpeechSynthesisUtterance(text);<br \/>\n        utterance.lang = &#8216;zh-CN&#8217;;<br \/>\n        utterance.rate = 0.95;<br \/>\n        window.speechSynthesis.cancel();<br \/>\n        window.speechSynthesis.speak(utterance);<br \/>\n    }<\/p>\n<p>    \/\/ &#8212;&#8212;&#8212;- \u624b\u52bf\u6838\u5fc3\u903b\u8f91: \u57fa\u4e8ehandpose\u5173\u952e\u70b9\u89c4\u5219\u8bc6\u522b\u4e94\u4e2a\u5e38\u7528\u624b\u52bf &#8212;&#8212;&#8212;-<br \/>\n    function recognizeGesture(landmarks) {<br \/>\n        if (!landmarks || landmarks.length &lt; 21) return &#8220;unknown&#8221;;<br \/>\n        \/\/ \u5173\u952e\u70b9\u7d22\u5f15 (0: wrist, 4: thumb_tip, 8: index_tip, 12: middle_tip, 16: ring_tip, 20: pinky_tip)<br \/>\n        const thumbTip = landmarks[4];<br \/>\n        const thumbMcp = landmarks[2];<br \/>\n        const indexTip = landmarks[8];<br \/>\n        const indexMcp = landmarks[5];<br \/>\n        const middleTip = landmarks[12];<br \/>\n        const middleMcp = landmarks[9];<br \/>\n        const ringTip = landmarks[16];<br \/>\n        const pinkyTip = landmarks[20];<br \/>\n        const wrist = landmarks[0];<\/p>\n<p>        \/\/ \u8ba1\u7b97\u624b\u6307\u4f38\u76f4 \/ \u5f2f\u66f2: \u6bd4\u8f83\u6307\u5c16\u4e0e\u6307\u6839y\u5750\u6807 (\u6444\u50cf\u5934\u5750\u6807\u7cfb\u4e2dy\u4e0a\u5c0f\u4e0b\u5927)<br \/>\n        const thumbUp = (thumbTip[1] &lt; thumbMcp[1] &#8211; 15);   \/\/ \u62c7\u6307\u7ad6\u76f4\u5411\u4e0a<br \/>\n        const indexStraight = (indexTip[1] &lt; indexMcp[1] &#8211; 20);<br \/>\n        const middleStraight = (middleTip[1] &lt; middleMcp[1] &#8211; 20);<br \/>\n        const ringStraight = (ringTip[1] &lt; landmarks[13][1] &#8211; 18);<br \/>\n        const pinkyStraight = (pinkyTip[1] &lt; landmarks[17][1] &#8211; 18);<\/p>\n<p>        \/\/ \u63e1\u62f3\u68c0\u6d4b (\u56db\u4e2a\u6307\u5c16\u4f4e\u4e8e\u5bf9\u5e94\u5173\u8282)<br \/>\n        const isFist = (!indexStraight &amp;&amp; !middleStraight &amp;&amp; !ringStraight &amp;&amp; !pinkyStraight &amp;&amp; !thumbUp);<\/p>\n<p>        \/\/ \u624b\u638c\u5168\u5f00: 4\u6307\u4f38\u76f4 \u4e14 \u62c7\u6307\u5916\u5c55 (\u62c7\u6307\u6a2a\u5411\u8ddd\u79bb\u5927)<br \/>\n        const palmOpen = (indexStraight &amp;&amp; middleStraight &amp;&amp; ringStraight &amp;&amp; pinkyStraight &amp;&amp; (thumbTip[0] &#8211; wrist[0] &gt; 60 || thumbTip[0] &#8211; landmarks[5][0] &gt; 30));<\/p>\n<p>        \/\/ V\u5b57\u624b\u52bf: \u98df\u6307\u4e2d\u6307\u4f38\u76f4\uff0c\u65e0\u540d\u6307\u548c\u5c0f\u6307\u5f2f\u66f2<br \/>\n        const vGesture = (indexStraight &amp;&amp; middleStraight &amp;&amp; !ringStraight &amp;&amp; !pinkyStraight);<\/p>\n<p>        \/\/ \u5927\u62c7\u6307\u7ad6\u8d77: \u4ec5\u5927\u62c7\u6307\u660e\u663e\u5411\u4e0a\uff0c\u5176\u4ed6\u56db\u6307\u5f2f\u66f2\u6216\u81f3\u5c11\u975e\u5168\u4f38<br \/>\n        const thumbOnly = (thumbUp &amp;&amp; !indexStraight &amp;&amp; !middleStraight &amp;&amp; !ringStraight &amp;&amp; !pinkyStraight);<\/p>\n<p>        \/\/ \u98df\u6307\u6307\u5411 (\u7c7b\u4f3c\u70b9\u51fb), \u53ea\u6709\u98df\u6307\u4f38\u76f4, \u5176\u4ed6\u5f2f\u66f2<br \/>\n        const indexPoint = (indexStraight &amp;&amp; !middleStraight &amp;&amp; !ringStraight &amp;&amp; !pinkyStraight &amp;&amp; !thumbUp);<\/p>\n<p>        if (thumbOnly) return &#8220;thumbs_up&#8221;;<br \/>\n        if (vGesture) return &#8220;victory&#8221;;<br \/>\n        if (palmOpen) return &#8220;palm_open&#8221;;<br \/>\n        if (isFist) return &#8220;fist&#8221;;<br \/>\n        if (indexPoint) return &#8220;index_point&#8221;;<br \/>\n        return &#8220;none&#8221;;<br \/>\n    }<\/p>\n<p>    \/\/ \u6839\u636e\u624b\u52bf\u6620\u5c04\u547d\u4ee4<br \/>\n    function mapGestureToAction(gesture) {<br \/>\n        switch(gesture) {<br \/>\n            case &#8220;thumbs_up&#8221;: return &#8220;open_baidu&#8221;;<br \/>\n            case &#8220;victory&#8221;: return &#8220;open_google&#8221;;<br \/>\n            case &#8220;fist&#8221;: return &#8220;scroll_bottom&#8221;;<br \/>\n            case &#8220;palm_open&#8221;: return &#8220;refresh&#8221;;<br \/>\n            case &#8220;index_point&#8221;: return &#8220;back&#8221;;<br \/>\n            default: return null;<br \/>\n        }<br \/>\n    }<\/p>\n<p>    \/\/ \u624b\u52bf\u89e6\u53d1\u5e26\u9632\u6296\u548c\u65e5\u5fd7<br \/>\n    let lastGesture = &#8220;&#8221;;<br \/>\n    function handleGesture(gesture) {<br \/>\n        if(gesture === &#8220;none&#8221; || gesture === &#8220;unknown&#8221;) return;<br \/>\n        const now = Date.now();<br \/>\n        if(gesture === lastGesture &amp;&amp; (now &#8211; lastExecTime) &lt; GESTURE_COOLDOWN) return;<br \/>\n        lastGesture = gesture;<br \/>\n        const action = mapGestureToAction(gesture);<br \/>\n        if(action) {<br \/>\n            let gestureName = gesture === &#8220;thumbs_up&#8221; ? &#8220;\ud83d\udc4d \u5927\u62c7\u6307\u7ad6\u8d77&#8221; : (gesture === &#8220;victory&#8221; ? &#8220;\u270c\ufe0f V\u624b\u52bf&#8221; : (gesture === &#8220;fist&#8221; ? &#8220;\u270a \u63e1\u62f3&#8221; : (gesture === &#8220;palm_open&#8221; ? &#8220;\ud83d\udd90\ufe0f \u624b\u638c\u6253\u5f00&#8221; : &#8220;\ud83d\udc49 \u98df\u6307\u6307\u5411&#8221;)));<br \/>\n            addLog(`\u270b \u624b\u52bf\u3010${gestureName}\u3011\u89e6\u53d1 \u2192 ${action}`, &#8216;action&#8217;);<br \/>\n            executeAction(action);<br \/>\n        }<br \/>\n    }<\/p>\n<p>    \/\/ &#8212;&#8212;&#8212;- \u7ed8\u5236\u624b\u90e8\u5173\u952e\u70b9 &amp; \u8c03\u7528\u8bc6\u522b &#8212;&#8212;&#8212;-<br \/>\n    function drawHand(landmarks, ctx, videoWidth, videoHeight) {<br \/>\n        if(!ctx) return;<br \/>\n        ctx.clearRect(0, 0, canvas.width, canvas.height);<br \/>\n        if(!landmarks) return;<br \/>\n        \/\/ \u7ed8\u5236\u5173\u952e\u70b9\u4e0e\u8fde\u7ebf(\u7b80\u5316\u9aa8\u67b6)<br \/>\n        for(let i=0; i&lt;landmarks.length; i++) {<br \/>\n            const x = landmarks[i][0] * canvas.width;<br \/>\n            const y = landmarks[i][1] * canvas.height;<br \/>\n            ctx.beginPath();<br \/>\n            ctx.arc(x, y, 4, 0, 2*Math.PI);<br \/>\n            ctx.fillStyle = &#8220;#00ffff&#8221;;<br \/>\n            ctx.fill();<br \/>\n            ctx.shadowBlur = 0;<br \/>\n        }<br \/>\n        \/\/ \u7b80\u6613\u8fde\u7ebf (\u624b\u90e8\u8fde\u7ebf\u793a\u4f8b, \u63d0\u5347\u89c6\u89c9)<br \/>\n        const connections = [[0,1],[1,2],[2,3],[3,4],[0,5],[5,6],[6,7],[7,8],[5,9],[9,10],[10,11],[11,12],[9,13],[13,14],[14,15],[15,16],[13,17],[17,18],[18,19],[19,20],[0,17]];<br \/>\n        ctx.beginPath();<br \/>\n        ctx.strokeStyle = &#8220;#a5f0ff&#8221;;<br \/>\n        ctx.lineWidth = 2;<br \/>\n        for(let conn of connections) {<br \/>\n            const [a,b] = conn;<br \/>\n            if(landmarks[a] &amp;&amp; landmarks[b]) {<br \/>\n                const x1 = landmarks[a][0]*canvas.width, y1 = landmarks[a][1]*canvas.height;<br \/>\n                const x2 = landmarks[b][0]*canvas.width, y2 = landmarks[b][1]*canvas.height;<br \/>\n                ctx.moveTo(x1,y1);<br \/>\n                ctx.lineTo(x2,y2);<br \/>\n                ctx.stroke();<br \/>\n            }<br \/>\n        }<br \/>\n    }<\/p>\n<p>    async function detectHand() {<br \/>\n        if(!handModel || !cameraActive || !video.videoWidth) {<br \/>\n            if(handModelStatusSpan) handModelStatusSpan.innerText = handModel ? &#8220;\u26a1 \u7b49\u5f85\u6444\u50cf\u5934&#8221; : &#8220;\u231b \u6a21\u578b\u672a\u52a0\u8f7d&#8221;;<br \/>\n            requestAnimationFrame(detectHand);<br \/>\n            return;<br \/>\n        }<br \/>\n        try {<br \/>\n            const predictions = await handModel.estimateHands(video, false);<br \/>\n            if(predictions.length &gt; 0) {<br \/>\n                const hand = predictions[0];<br \/>\n                const landmarks = hand.landmarks; \/\/ 3D \u70b9\u5f52\u4e00\u5316\u5750\u6807 [x,y,z] 0-1\u8303\u56f4<br \/>\n                if(landmarks) {<br \/>\n                    \/\/ \u8f6c\u6362\u5f52\u4e00\u5316\u5750\u6807\u5230canvas\u5750\u6807\u7cfb\u7ed8\u5236<br \/>\n                    drawHand(landmarks, ctx, video.videoWidth, video.videoHeight);<br \/>\n                    const gesture = recognizeGesture(landmarks);<br \/>\n                    gestureSpan.innerText = gesture === &#8220;thumbs_up&#8221; ? &#8220;\ud83d\udc4d \u62c7\u6307\u8d5e&#8221; : (gesture === &#8220;victory&#8221; ? &#8220;\u270c\ufe0f \u80dc\u5229\/V&#8221; : (gesture === &#8220;fist&#8221; ? &#8220;\u270a \u62f3\u5934&#8221; : (gesture === &#8220;palm_open&#8221; ? &#8220;\ud83d\udd90\ufe0f \u624b\u638c&#8221; : (gesture === &#8220;index_point&#8221; ? &#8220;\ud83d\udc49 \u98df\u6307\u6307\u5411&#8221; : &#8220;\ud83e\udd1a \u5176\u4ed6&#8221;))));<br \/>\n                    handleGesture(gesture);<br \/>\n                } else {<br \/>\n                    ctx.clearRect(0,0,canvas.width,canvas.height);<br \/>\n                    gestureSpan.innerText = &#8220;\u672a\u8bc6\u522b\u624b\u638c&#8221;;<br \/>\n                }<br \/>\n            } else {<br \/>\n                ctx.clearRect(0,0,canvas.width,canvas.height);<br \/>\n                gestureSpan.innerText = &#8220;\u65e0\u624b\u52bf&#8221;;<br \/>\n            }<br \/>\n        } catch(err) {<br \/>\n            console.warn(&#8220;\u624b\u52bf\u68c0\u6d4b\u9519\u8bef&#8221;, err);<br \/>\n        }<br \/>\n        requestAnimationFrame(detectHand);<br \/>\n    }<\/p>\n<p>    \/\/ \u52a0\u8f7d handpose \u6a21\u578b<br \/>\n    async function loadHandModel() {<br \/>\n        handModelStatusSpan.innerText = &#8220;\ud83d\udd04 \u52a0\u8f7d\u624b\u52bf\u6a21\u578b (\u7ea65MB)&#8230;&#8221;;<br \/>\n        try {<br \/>\n            handModel = await handpose.load();<br \/>\n            handModelStatusSpan.innerText = &#8220;\u2705 \u624b\u52bf\u6a21\u578b\u5c31\u7eea&#8221;;<br \/>\n            addLog(&#8220;\ud83e\udde0 \u624b\u52bf\u8bc6\u522b\u6a21\u578b\u5df2\u52a0\u8f7d\uff0c\u53ef\u7528\u6444\u50cf\u5934\u63a7\u5236&#8221;, &#8220;info&#8221;);<br \/>\n        } catch(e) {<br \/>\n            console.error(e);<br \/>\n            handModelStatusSpan.innerText = &#8220;\u274c \u6a21\u578b\u52a0\u8f7d\u5931\u8d25\uff0c\u5237\u65b0\u91cd\u8bd5&#8221;;<br \/>\n            addLog(&#8220;\u26a0\ufe0f \u624b\u52bf\u6a21\u578b\u52a0\u8f7d\u5931\u8d25\uff0c\u8bf7\u68c0\u67e5\u7f51\u7edc&#8221;, &#8220;info&#8221;);<br \/>\n        }<br \/>\n    }<\/p>\n<p>    \/\/ &#8212;&#8212;&#8212;- \u6444\u50cf\u5934\u521d\u59cb\u5316 (\u5e26\u955c\u50cfcanvas\u9002\u914d)&#8212;&#8212;&#8212;-<br \/>\n    async function initCamera() {<br \/>\n        if(!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {<br \/>\n            addLog(&#8220;\u274c \u6d4f\u89c8\u5668\u4e0d\u652f\u6301\u6444\u50cf\u5934\u8bbf\u95ee&#8221;, &#8220;error&#8221;);<br \/>\n            return false;<br \/>\n        }<br \/>\n        try {<br \/>\n            const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: false });<br \/>\n            video.srcObject = stream;<br \/>\n            await new Promise((resolve) =&gt; { video.onloadedmetadata = () =&gt; { resolve(); }; });<br \/>\n            video.play();<br \/>\n            \/\/ \u9002\u914dcanvas\u5c3a\u5bf8<br \/>\n            const updateCanvasSize = () =&gt; {<br \/>\n                if(video.videoWidth) {<br \/>\n                    canvas.width = video.videoWidth;<br \/>\n                    canvas.height = video.videoHeight;<br \/>\n                } else {<br \/>\n                    canvas.width = 640;<br \/>\n                    canvas.height = 480;<br \/>\n                }<br \/>\n            };<br \/>\n            updateCanvasSize();<br \/>\n            video.addEventListener(&#8216;resize&#8217;, updateCanvasSize);<br \/>\n            cameraActive = true;<br \/>\n            addLog(&#8220;\ud83d\udcf7 \u6444\u50cf\u5934\u5df2\u6fc0\u6d3b\uff0c\u73b0\u5728\u53ef\u4ee5\u4f7f\u7528\u624b\u52bf\u4ea4\u4e92 (\u7ad6\u8d77\u62c7\u6307, V\u5b57\u7b49)&#8221;, &#8220;info&#8221;);<br \/>\n            speak(&#8220;\u6444\u50cf\u5934\u5df2\u5f00\u542f\uff0c\u624b\u52bf\u63a7\u5236\u5df2\u5c31\u7eea&#8221;);<br \/>\n            return true;<br \/>\n        } catch(err) {<br \/>\n            addLog(`\u6444\u50cf\u5934\u9519\u8bef: ${err.message}`, &#8220;error&#8221;);<br \/>\n            return false;<br \/>\n        }<br \/>\n    }<\/p>\n<p>    \/\/ &#8212;&#8212;&#8212;- \u8bed\u97f3\u8bc6\u522b\u6a21\u5757 Web Speech &#8212;&#8212;&#8212;-<br \/>\n    let recognition = null;<br \/>\n    let isListening = false;<br \/>\n    function initSpeech() {<br \/>\n        if (!(&#8216;webkitSpeechRecognition&#8217; in window) &amp;&amp; !(&#8216;SpeechRecognition&#8217; in window)) {<br \/>\n            voiceStatusSpan.innerText = &#8220;\u4e0d\u652f\u6301\u8bed\u97f3&#8221;;<br \/>\n            addLog(&#8220;\u26a0\ufe0f \u5f53\u524d\u6d4f\u89c8\u5668\u4e0d\u652f\u6301\u8bed\u97f3\u8bc6\u522b&#8221;, &#8220;error&#8221;);<br \/>\n            return false;<br \/>\n        }<br \/>\n        const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;<br \/>\n        recognition = new SpeechRecognition();<br \/>\n        recognition.continuous = false;<br \/>\n        recognition.interimResults = false;<br \/>\n        recognition.lang = &#8216;zh-CN&#8217;;<br \/>\n        recognition.onstart = () =&gt; {<br \/>\n            isListening = true;<br \/>\n            voiceStatusSpan.innerHTML = &#8216;\ud83c\udfa4 \u8046\u542c\u4e2d&#8230; \u8bf4\u51fa\u6307\u4ee4&#8217;;<br \/>\n            voiceStatusSpan.classList.add(&#8216;listening&#8217;);<br \/>\n        };<br \/>\n        recognition.onend = () =&gt; {<br \/>\n            isListening = false;<br \/>\n            voiceStatusSpan.innerHTML = &#8216;\u5f85\u547d&#8217;;<br \/>\n            voiceStatusSpan.classList.remove(&#8216;listening&#8217;);<br \/>\n        };<br \/>\n        recognition.onresult = (event) =&gt; {<br \/>\n            const transcript = event.results[0][0].transcript;<br \/>\n            speechTextDiv.innerText = transcript;<br \/>\n            addLog(`\ud83c\udfa4 \u8bed\u97f3: ${transcript}`, &#8216;info&#8217;);<br \/>\n            processVoiceCommand(transcript);<br \/>\n        };<br \/>\n        recognition.onerror = (e) =&gt; {<br \/>\n            voiceStatusSpan.innerHTML = &#8216;\u8bed\u97f3\u9519\u8bef&#8217;;<br \/>\n            addLog(`\u8bed\u97f3\u8bc6\u522b\u9519\u8bef: ${e.error}`, &#8216;error&#8217;);<br \/>\n            isListening = false;<br \/>\n        };<br \/>\n        return true;<br \/>\n    }<\/p>\n<p>    function processVoiceCommand(text) {<br \/>\n        const cmd = text.toLowerCase().trim();<br \/>\n        if(cmd.includes(&#8220;\u6253\u5f00\u767e\u5ea6&#8221;) || cmd.includes(&#8220;\u767e\u5ea6\u4e00\u4e0b&#8221;)) {<br \/>\n            executeAction(&#8220;open_baidu&#8221;);<br \/>\n            speak(&#8220;\u6b63\u5728\u6253\u5f00\u767e\u5ea6&#8221;);<br \/>\n        } else if(cmd.includes(&#8220;\u6253\u5f00\u8c37\u6b4c&#8221;) || cmd.includes(&#8220;google&#8221;)) {<br \/>\n            executeAction(&#8220;open_google&#8221;);<br \/>\n            speak(&#8220;\u6253\u5f00\u8c37\u6b4c\u641c\u7d22&#8221;);<br \/>\n        } else if(cmd.includes(&#8220;\u6253\u5f00github&#8221;) || cmd.includes(&#8220;github&#8221;)) {<br \/>\n            executeAction(&#8220;open_github&#8221;);<br \/>\n        } else if(cmd.includes(&#8220;\u5237\u65b0&#8221;) || cmd.includes(&#8220;\u5237\u65b0\u9875\u9762&#8221;)) {<br \/>\n            executeAction(&#8220;refresh&#8221;);<br \/>\n            speak(&#8220;\u5237\u65b0\u9875\u9762&#8221;);<br \/>\n        } else if(cmd.includes(&#8220;\u6eda\u52a8\u5230\u5e95\u90e8&#8221;) || cmd.includes(&#8220;\u5230\u5e95\u90e8&#8221;)) {<br \/>\n            executeAction(&#8220;scroll_bottom&#8221;);<br \/>\n            speak(&#8220;\u6eda\u52a8\u5230\u5e95\u90e8&#8221;);<br \/>\n        } else if(cmd.includes(&#8220;\u8fd4\u56de&#8221;) || cmd.includes(&#8220;\u8fd4\u56de\u4e0a\u4e00\u9875&#8221;)) {<br \/>\n            executeAction(&#8220;back&#8221;);<br \/>\n            speak(&#8220;\u8fd4\u56de\u4e0a\u4e00\u9875&#8221;);<br \/>\n        } else if(cmd.includes(&#8220;\u641c\u7d22\u5929\u6c14&#8221;)) {<br \/>\n            executeAction(&#8220;search_weather&#8221;, &#8220;\u4eca\u65e5\u5929\u6c14&#8221;);<br \/>\n            speak(&#8220;\u641c\u7d22\u5929\u6c14&#8221;);<br \/>\n        } else if(cmd.includes(&#8220;\u4f60\u597d\u6d4f\u89c8\u5668&#8221;) || cmd.includes(&#8220;\u55e8\u6d4f\u89c8\u5668&#8221;)) {<br \/>\n            executeAction(&#8220;hello_browser&#8221;);<br \/>\n        } else if(cmd.includes(&#8220;\u641c\u7d22&#8221;) &amp;&amp; cmd.length &gt; 2) {<br \/>\n            let keyword = cmd.replace(&#8220;\u641c\u7d22&#8221;, &#8220;&#8221;).trim();<br \/>\n            if(keyword) executeAction(&#8220;search_&#8221;, keyword);<br \/>\n            else speak(&#8220;\u8bf7\u8bf4\u51fa\u8981\u641c\u7d22\u7684\u5185\u5bb9&#8221;);<br \/>\n        } else if(cmd.includes(&#8220;\u6253\u5f00&#8221;)) {<br \/>\n            let site = cmd.replace(&#8220;\u6253\u5f00&#8221;, &#8220;&#8221;).trim();<br \/>\n            if(site===&#8217;\u767e\u5ea6&#8217;) executeAction(&#8220;open_baidu&#8221;);<br \/>\n            else if(site===&#8217;\u8c37\u6b4c&#8217;) executeAction(&#8220;open_google&#8221;);<br \/>\n            else speak(&#8220;\u672a\u8bc6\u522b\u7f51\u5740&#8221;);<br \/>\n        } else {<br \/>\n            speak(&#8220;\u672a\u8bc6\u522b\u8bed\u97f3\u6307\u4ee4\uff0c\u8bd5\u8bd5\u201c\u6253\u5f00\u767e\u5ea6\u201d\u6216\u201c\u5237\u65b0\u201d&#8221;);<br \/>\n            addLog(`\u672a\u5904\u7406\u6307\u4ee4: ${text}`, &#8220;info&#8221;);<br \/>\n        }<br \/>\n    }<\/p>\n<p>    function toggleListening() {<br \/>\n        if(!recognition) {<br \/>\n            if(!initSpeech()) return;<br \/>\n        }<br \/>\n        if(isListening) {<br \/>\n            recognition.stop();<br \/>\n            voiceStatusSpan.innerHTML = &#8220;\u505c\u6b62&#8221;;<br \/>\n        } else {<br \/>\n            try {<br \/>\n                recognition.start();<br \/>\n            } catch(e){<br \/>\n                addLog(&#8220;\u8bf7\u5148\u5141\u8bb8\u9ea6\u514b\u98ce\u6743\u9650, \u5237\u65b0\u9875\u9762\u540e\u70b9\u51fb&#8221;, &#8220;error&#8221;);<br \/>\n            }<br \/>\n        }<br \/>\n    }<\/p>\n<p>    \/\/ &#8212;&#8211;\u4e8b\u4ef6\u7ed1\u5b9a&#8212;&#8211;<br \/>\n    enableCamBtn.onclick = async () =&gt; {<br \/>\n        await initCamera();<br \/>\n    };<br \/>\n    resetHandBtn.onclick = () =&gt; {<br \/>\n        if(handModel) {<br \/>\n            addLog(&#8220;\ud83d\udd04 \u624b\u52bf\u6a21\u578b\u91cd\u7f6e&#8221;, &#8220;info&#8221;);<br \/>\n            gestureSpan.innerText = &#8220;\u2014\u2014&#8221;;<br \/>\n        } else {<br \/>\n            loadHandModel();<br \/>\n        }<br \/>\n    };<br \/>\n    voiceBtn.onclick = toggleListening;<\/p>\n<p>    \/\/ \u9875\u9762\u542f\u52a8\uff1a\u52a0\u8f7d\u6a21\u578b\uff0c\u521d\u59cb\u5316\u8bed\u97f3\u9884\u5907\uff0c\u4f46\u4e0d\u81ea\u52a8\u5f00\u6444\u50cf\u5934 (\u9700\u8981\u7528\u6237\u70b9\u51fb )<br \/>\n    loadHandModel();<br \/>\n    initSpeech();  \/\/ \u9884\u7f6e\u8bed\u97f3\u8bc6\u522b\u80fd\u529b<br \/>\n    \/\/ \u542f\u52a8\u68c0\u6d4bloop\u4f46\u4e0d\u68c0\u6d4b(\u7b49\u5f85\u6444\u50cf\u5934)<br \/>\n    (function initLoop() {<br \/>\n        detectHand();<br \/>\n    })();<br \/>\n    addLog(&#8220;\ud83d\udca1 \u70b9\u51fb\u300c\u5f00\u542f\u6444\u50cf\u5934\u300d\u5141\u8bb8\u6743\u9650\uff0c\u6bd4\u51fa\u5927\u62c7\u6307\ud83d\udc4d \u6253\u5f00\u767e\u5ea6\uff0c\u6216\u70b9\u51fb\ud83c\udfa4\u8bf4\u51fa\u6307\u4ee4&#8221;, &#8220;info&#8221;);<br \/>\n    \/\/ \u4f18\u96c5\u63d0\u793a<br \/>\n    if (Notification.permission === &#8220;default&#8221;) Notification.requestPermission();<br \/>\n&lt;\/script&gt;<br \/>\n&lt;\/body&gt;<br \/>\n&lt;\/html&gt;<\/p>\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":54,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-1","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/news.miaotao.com\/index.php\/wp-json\/wp\/v2\/posts\/1","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/news.miaotao.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/news.miaotao.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/news.miaotao.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/news.miaotao.com\/index.php\/wp-json\/wp\/v2\/comments?post=1"}],"version-history":[{"count":1,"href":"https:\/\/news.miaotao.com\/index.php\/wp-json\/wp\/v2\/posts\/1\/revisions"}],"predecessor-version":[{"id":20,"href":"https:\/\/news.miaotao.com\/index.php\/wp-json\/wp\/v2\/posts\/1\/revisions\/20"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/news.miaotao.com\/index.php\/wp-json\/wp\/v2\/media\/54"}],"wp:attachment":[{"href":"https:\/\/news.miaotao.com\/index.php\/wp-json\/wp\/v2\/media?parent=1"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/news.miaotao.com\/index.php\/wp-json\/wp\/v2\/categories?post=1"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/news.miaotao.com\/index.php\/wp-json\/wp\/v2\/tags?post=1"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}