{"id":475804,"date":"2026-04-14T03:38:25","date_gmt":"2026-04-14T03:38:25","guid":{"rendered":"https:\/\/savepearlharbor.com\/?p=475804"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=475804","title":{"rendered":"\u0413\u043e\u043b\u043e\u0441\u043e\u0432\u043e\u0439 \u0432\u0432\u043e\u0434 \u0432 Claude \u043d\u0430 \u0440\u0443\u0441\u0441\u043a\u043e\u043c \u2014 \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u043e \u0438 \u043e\u0444\u043b\u0430\u0439\u043d \u0437\u0430 \u043e\u0434\u0438\u043d \u0432\u0435\u0447\u0435\u0440"},"content":{"rendered":"<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p><strong>\u0410\u0432\u0442\u043e\u0440:<\/strong> \u0414\u043c\u0438\u0442\u0440\u0438\u0439 \u0421\u043e\u0441\u0443\u043d\u043e\u0432 \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u043d\u043e \u0441 Claude<br \/><strong>\u0423\u0440\u043e\u0432\u0435\u043d\u044c:<\/strong> \u0434\u043b\u044f \u0442\u0435\u0445 \u043a\u0442\u043e \u043d\u0435 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0441\u0442, \u043d\u043e \u043d\u0435 \u0431\u043e\u0438\u0442\u0441\u044f \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b\u0430<br \/><strong>\u0412\u0440\u0435\u043c\u044f:<\/strong> \u043e\u0434\u0438\u043d \u0432\u0435\u0447\u0435\u0440<br \/><strong>\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442:<\/strong> \u0438\u043a\u043e\u043d\u043a\u0430 \u0432 \u0441\u0442\u0440\u043e\u043a\u0435 \u043c\u0435\u043d\u044e Mac \u2192 \u043d\u0430\u0436\u0430\u043b \u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u2192 \u0433\u043e\u0432\u043e\u0440\u0438\u0448\u044c \u0432 Claude<\/p>\n<hr\/>\n<p>\u042f  \u043c\u0435\u043d\u0435\u0434\u0436\u0435\u0440, \u0444\u0438\u043d\u0430\u043d\u0441\u0438\u0441\u0442 \u0431\u0435\u0437 \u0442\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u0431\u044d\u043a\u0433\u0440\u0430\u0443\u043d\u0434\u0430. \u041c\u043d\u0435 \u043a\u0430\u0439\u0444\u043e\u0432\u043e \u0434\u0438\u043a\u0442\u043e\u0432\u0430\u0442\u044c \u0430 \u043d\u0435 \u043f\u0435\u0447\u0430\u0442\u0430\u0442\u044c, \u0442\u0430\u043a \u0438 \u043d\u0435 \u0441\u043c\u043e\u0433 \u044f \u043e\u0441\u0432\u043e\u0438\u0442\u044c \u0441\u043b\u0435\u043f\u043e\u0439 \u043c\u0435\u0442\u043e\u0434, \u043f\u0435\u0447\u0430\u0442\u0430\u044e \u0433\u043b\u044f\u0434\u044f \u043d\u0430 \u043a\u043b\u0430\u0432\u0443. \u0425\u043e\u0447\u0443 \u0434\u0438\u043a\u0442\u043e\u0432\u0430\u0442\u044c. \u041d\u043e!<\/p>\n<h2>\u041f\u0420\u041e\u0411\u041b\u0415\u041c\u0410: \u0412 Claude \u043f\u043e\u043a\u0430 \u043d\u0435\u0442 \u0440\u0443\u0441\u0441\u043a\u043e\u0433\u043e \u044f\u0437\u044b\u043a\u0430 \u0434\u043b\u044f \u0434\u0438\u043a\u0442\u043e\u0432\u043a\u0438. <\/h2>\n<p>\u041f\u043b\u0430\u0442\u043d\u044b\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u0432\u0440\u043e\u0434\u0435 Voicy \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442, \u043d\u043e \u0443 \u043d\u0438\u0445 \u043b\u0438\u043c\u0438\u0442\u044b, \u043e\u043d\u0438 \u0442\u0440\u0435\u0431\u0443\u044e\u0442 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442 \u0438 \u0441\u0442\u043e\u044f\u0442 \u0434\u0435\u043d\u0435\u0433. \u0423 \u043c\u0435\u043d\u044f \u0443\u0436\u0435 \u0431\u044b\u043b \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d Whisper \u2014 \u0442\u043e\u0442 \u0441\u0430\u043c\u044b\u0439 \u0434\u0432\u0438\u0436\u043e\u043a OpenAI \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u0441\u0435 \u043f\u043b\u0430\u0442\u043d\u044b\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442 \u043f\u043e\u0434 \u043a\u0430\u043f\u043e\u0442\u043e\u043c. \u042f \u0441\u043f\u0440\u043e\u0441\u0438\u043b Claude: \u043c\u043e\u0436\u043d\u043e \u043b\u0438 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u044d\u0442\u043e \u0441\u0430\u043c\u043e\u043c\u0443? \u041e\u043a\u0430\u0437\u0430\u043b\u043e\u0441\u044c \u2014 \u0434\u0430, \u0437\u0430 \u043e\u0434\u0438\u043d \u0432\u0435\u0447\u0435\u0440.<\/p>\n<p>\u0412\u043e\u0442 \u0440\u0430\u0441\u0441\u043a\u0430\u0437 \u043e \u0442\u043e\u043c \u043a\u0430\u043a \u043c\u044b \u044d\u0442\u043e \u043f\u043e\u0441\u0442\u0440\u043e\u0438\u043b\u0438 \u2014 \u0432\u043a\u043b\u044e\u0447\u0430\u044f \u0432\u0441\u0435 \u0433\u0440\u0430\u0431\u043b\u0438 \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0430\u0441\u0442\u0443\u043f\u0438\u043b\u0438 \u043f\u043e \u043f\u0443\u0442\u0438.<\/p>\n<hr\/>\n<h3>\u0427\u0442\u043e \u0432 \u0438\u0442\u043e\u0433\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c<\/h3>\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/418\/3f7\/4d2\/4183f74d2a0d2619354f5a5517d52ae9.png\" alt=\"\u0441\u043b\u0435\u0432\u0430 \u0438\u043a\u043e\u043d\u043a\u0430, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043c\u043e\u0436\u043d\u043e \u0440\u0430\u0441\u0448\u0438\u0440\u044f\u0442\u044c \u0440\u0430\u0437\u043d\u044b\u043c\u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u0430\u043c\u0438\" title=\"\u0441\u043b\u0435\u0432\u0430 \u0438\u043a\u043e\u043d\u043a\u0430, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043c\u043e\u0436\u043d\u043e \u0440\u0430\u0441\u0448\u0438\u0440\u044f\u0442\u044c \u0440\u0430\u0437\u043d\u044b\u043c\u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u0430\u043c\u0438\" width=\"605\" height=\"39\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/418\/3f7\/4d2\/4183f74d2a0d2619354f5a5517d52ae9.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/418\/3f7\/4d2\/4183f74d2a0d2619354f5a5517d52ae9.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<div><figcaption>\u0441\u043b\u0435\u0432\u0430 \u0438\u043a\u043e\u043d\u043a\u0430, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043c\u043e\u0436\u043d\u043e \u0440\u0430\u0441\u0448\u0438\u0440\u044f\u0442\u044c \u0440\u0430\u0437\u043d\u044b\u043c\u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u0430\u043c\u0438<\/figcaption><\/div>\n<\/figure>\n<ul>\n<li>\n<p>\u0418\u043a\u043e\u043d\u043a\u0430 \ud83e\udd16 \u0432 \u0441\u0442\u0440\u043e\u043a\u0435 \u043c\u0435\u043d\u044e Mac \u2014 \u0432\u0441\u0435\u0433\u0434\u0430 \u043f\u043e\u0434 \u0440\u0443\u043a\u043e\u0439<\/p>\n<\/li>\n<li>\n<p>\u041a\u043b\u0438\u043a\u043d\u0443\u043b \u2192 \u0432\u044b\u0431\u0440\u0430\u043b &#171;\u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c&#187; \u2192 \u0438\u043a\u043e\u043d\u043a\u0430 \u0441\u0442\u0430\u043b\u0430 \ud83c\udfa4<\/p>\n<\/li>\n<li>\n<p>\u041d\u0430\u0436\u0430\u043b \u043a\u043d\u043e\u043f\u043a\u0443 \u043d\u0430 <a href=\"http:\/\/claude.ai\" rel=\"noopener noreferrer nofollow\">claude.ai<\/a> \u2014 \u0433\u043e\u0432\u043e\u0440\u0438\u0448\u044c \u043f\u043e-\u0440\u0443\u0441\u0441\u043a\u0438 \u2014 \u0442\u0435\u043a\u0441\u0442 \u0432\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432 \u043f\u043e\u043b\u0435<\/p>\n<\/li>\n<li>\n<p>Whisper \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e \u2014 \u043d\u0438\u043a\u0430\u043a\u0438\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0432 \u043e\u0431\u043b\u0430\u043a\u043e, \u043d\u0438\u043a\u0430\u043a\u0438\u0445 \u043b\u0438\u043c\u0438\u0442\u043e\u0432<\/p>\n<\/li>\n<li>\n<p>\u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 \u0440\u0430\u0441\u0448\u0438\u0440\u044f\u0435\u043c\u0430\u044f: \u0437\u0430\u0445\u043e\u0447\u0435\u0448\u044c \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043d\u043e\u0432\u044b\u0439 AI-\u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u2014 \u043e\u0434\u043d\u0430 \u0441\u0442\u0440\u043e\u043a\u0430 \u0432 \u043a\u043e\u043d\u0444\u0438\u0433\u0435<\/p>\n<\/li>\n<li>\n<p>\u0411\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u043e \u043d\u0430\u0432\u0441\u0435\u0433\u0434\u0430<\/p>\n<\/li>\n<\/ul>\n<hr\/>\n<h3>\u0424\u0438\u043d\u0430\u043b\u044c\u043d\u0430\u044f \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430<\/h3>\n<pre><code>\ud83e\udd16 Menu Bar App (rumps)    \u2514\u2500\u2500 \u25b6 \u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c Whisper \u2192 Claude            \u2193    Python Flask \u0441\u0435\u0440\u0432\u0435\u0440 (localhost:5555)            \u2193    Chrome Extension (\u043a\u043d\u043e\u043f\u043a\u0430 \ud83c\udfa4 \u043d\u0430 claude.ai)            \u2193 \u0430\u0443\u0434\u0438\u043e \u0432 base64    Background Service Worker            \u2193 POST \u0437\u0430\u043f\u0440\u043e\u0441    \/opt\/homebrew\/bin\/whisper \u2192 \u0442\u0435\u043a\u0441\u0442 \u2192 \u043f\u043e\u043b\u0435 \u0432\u0432\u043e\u0434\u0430 Claude<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:87px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041f\u043e\u0447\u0435\u043c\u0443 \u0442\u0430\u043a\u0430\u044f \u0446\u0435\u043f\u043e\u0447\u043a\u0430 \u0430 \u043d\u0435 \u043f\u0440\u043e\u0449\u0435 \u2014 \u043e\u0431\u044a\u044f\u0441\u043d\u044e \u043f\u043e \u0445\u043e\u0434\u0443. \u041a\u0430\u0436\u0434\u044b\u0439 \u044d\u043b\u0435\u043c\u0435\u043d\u0442 \u043f\u043e\u044f\u0432\u0438\u043b\u0441\u044f \u043d\u0435 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e.<\/p>\n<hr\/>\n<h3>\u0427\u0442\u043e \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f<\/h3>\n<ul>\n<li>\n<p>macOS (\u043d\u0430 Linux \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u043d\u043e \u0441 \u043f\u043e\u043f\u0440\u0430\u0432\u043a\u043e\u0439 \u043d\u0430 \u043f\u0443\u0442\u0438)<\/p>\n<\/li>\n<li>\n<p>Whisper: <code>brew install openai-whisper<\/code><\/p>\n<\/li>\n<li>\n<p>ffmpeg: <code>brew install ffmpeg<\/code> (Whisper \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0435\u0433\u043e \u0434\u043b\u044f \u0434\u0435\u043a\u043e\u0434\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0430\u0443\u0434\u0438\u043e)<\/p>\n<\/li>\n<li>\n<p>Python 3: <code>python3 --version<\/code><\/p>\n<\/li>\n<li>\n<p>Chrome \u0431\u0440\u0430\u0443\u0437\u0435\u0440<\/p>\n<\/li>\n<\/ul>\n<p>\u041f\u0440\u043e\u0432\u0435\u0440\u044c \u0447\u0442\u043e Whisper \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442:<\/p>\n<pre><code>whisper --version<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0414\u043e\u043b\u0436\u0435\u043d \u043f\u043e\u044f\u0432\u0438\u0442\u044c\u0441\u044f \u0434\u043b\u0438\u043d\u043d\u044b\u0439 \u0441\u043f\u0438\u0441\u043e\u043a \u044f\u0437\u044b\u043a\u043e\u0432 \u0432\u043a\u043b\u044e\u0447\u0430\u044f Russian. \u0415\u0441\u043b\u0438 \u0432\u0438\u0434\u0438\u0448\u044c \u0441\u043f\u0438\u0441\u043e\u043a \u2014 \u0432\u0441\u0451 \u0433\u043e\u0442\u043e\u0432\u043e.<\/p>\n<h3>\u0428\u0430\u0433 1 \u2014 \u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/h3>\n<pre><code>mkdir -p \/\u043f\u0443\u0442\u044c\/\u043a\/\u043f\u0440\u043e\u0435\u043a\u0442\u0430\u043c\/whisper-claudecd \/\u043f\u0443\u0442\u044c\/\u043a\/\u043f\u0440\u043e\u0435\u043a\u0442\u0430\u043c\/whisper-claudepython3 -m venv venvsource venv\/bin\/activatepip install flask flask-cors rumpsmkdir -p extension\/icons<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041f\u043e\u0447\u0435\u043c\u0443 \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u0430\u044f \u0441\u0440\u0435\u0434\u0430? macOS \u043d\u0430\u0447\u0438\u043d\u0430\u044f \u0441 Ventura \u0437\u0430\u043f\u0440\u0435\u0449\u0430\u0435\u0442 \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0442\u044c \u043f\u0430\u043a\u0435\u0442\u044b \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u043e \u2014 \u044d\u0442\u043e \u0437\u0430\u0449\u0438\u0442\u0430 \u043e\u0442 \u043f\u043e\u043b\u043e\u043c\u043a\u0438 \u0441\u0438\u0441\u0442\u0435\u043c\u043d\u043e\u0433\u043e Python. \u0412\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u0430\u044f \u0441\u0440\u0435\u0434\u0430 \u0440\u0435\u0448\u0430\u0435\u0442 \u044d\u0442\u043e \u0447\u0438\u0441\u0442\u043e.<\/p>\n<p><strong>rumps<\/strong> \u2014 Python \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f Menu Bar \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u043d\u0430 Mac. \u041f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0438\u043a\u043e\u043d\u043a\u0443 \u0432 \u0441\u0442\u0440\u043e\u043a\u0435 \u043c\u0435\u043d\u044e \u0432 50 \u0441\u0442\u0440\u043e\u043a\u0430\u0445 \u043a\u043e\u0434\u0430 \u0432\u043c\u0435\u0441\u0442\u043e \u0441\u043e\u0442\u0435\u043d \u0441\u0442\u0440\u043e\u043a \u043d\u0430 Swift.<\/p>\n<hr\/>\n<h3>\u0428\u0430\u0433 2 \u2014 Python \u0441\u0435\u0440\u0432\u0435\u0440 (server.py)<\/h3>\n<pre><code class=\"python\">from flask import Flask, request, jsonifyfrom flask_cors import CORSimport subprocessimport tempfileimport osimport threadingapp = Flask(__name__)CORS(app)  # \u0440\u0430\u0437\u0440\u0435\u0448\u0430\u0435\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043e\u0442 Chrome \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044fWHISPER_PATH = \"\/opt\/homebrew\/bin\/whisper\"WHISPER_MODEL = \"small\"  # tiny\/base\/small\/medium\/largePORT = 5555@app.route(\"\/ping\", methods=[\"GET\"])def ping():    return jsonify({\"status\": \"ok\"})@app.route(\"\/transcribe\", methods=[\"POST\"])def transcribe():    if \"audio\" not in request.files:        return jsonify({\"error\": \"\u0410\u0443\u0434\u0438\u043e \u0444\u0430\u0439\u043b \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\"}), 400    audio_file = request.files[\"audio\"]    with tempfile.NamedTemporaryFile(suffix=\".webm\", delete=False) as tmp:        tmp_path = tmp.name        audio_file.save(tmp_path)    try:        result = subprocess.run(            [                WHISPER_PATH, tmp_path,                \"--model\", WHISPER_MODEL,                \"--language\", \"ru\",                \"--output_format\", \"txt\",                \"--output_dir\", tempfile.gettempdir(),                \"--fp16\", \"False\",  # \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0434\u043b\u044f Mac Apple Silicon            ],            capture_output=True, text=True, timeout=60        )        txt_path = tmp_path.replace(\".webm\", \".txt\")        if os.path.exists(txt_path):            with open(txt_path, \"r\", encoding=\"utf-8\") as f:                text = f.read().strip()            os.unlink(txt_path)        else:            text = result.stdout.strip()        return jsonify({\"text\": text})    except subprocess.TimeoutExpired:        return jsonify({\"error\": \"Timeout\"}), 500    except Exception as e:        return jsonify({\"error\": str(e)}), 500    finally:        os.unlink(tmp_path)@app.route(\"\/shutdown\", methods=[\"POST\"])def shutdown():    threading.Timer(0.5, lambda: os._exit(0)).start()    return jsonify({\"status\": \"shutting down\"})if __name__ == \"__main__\":    print(f\"[Whisper] \u0417\u0430\u043f\u0443\u0441\u043a\u0430\u044e\u0441\u044c \u043d\u0430 \u043f\u043e\u0440\u0442\u0443 {PORT}, \u043c\u043e\u0434\u0435\u043b\u044c: {WHISPER_MODEL}\")    app.run(host=\"127.0.0.1\", port=PORT, debug=False)<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p><strong>\u0413\u0440\u0430\u0431\u043b\u044f #1:<\/strong> <code>--fp16 False<\/code> \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u0435\u043d \u043d\u0430 Mac \u0441 Apple Silicon. \u0411\u0435\u0437 \u043d\u0435\u0433\u043e Whisper \u043f\u0430\u0434\u0430\u0435\u0442 \u0441 \u043e\u0448\u0438\u0431\u043a\u043e\u0439 \u043f\u0440\u043e float16.<\/p>\n<hr\/>\n<h3>\u0428\u0430\u0433 3 \u2014 Menu Bar \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 (menubar.py)<\/h3>\n<p>\u042d\u0442\u043e \u0441\u0435\u0440\u0434\u0446\u0435 \u0441\u0438\u0441\u0442\u0435\u043c\u044b. \u041e\u0434\u043d\u0430 \u0438\u043a\u043e\u043d\u043a\u0430 \u0432 \u0441\u0442\u0440\u043e\u043a\u0435 \u043c\u0435\u043d\u044e \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0432\u0441\u0435\u043c\u0438 AI-\u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438. \u041d\u043e\u0432\u044b\u0439 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043e\u0434\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u044c\u044e \u0432 \u0441\u043f\u0438\u0441\u043e\u043a <code>TOOLS<\/code>.<\/p>\n<pre><code class=\"python\">import rumpsimport subprocessimport threadingimport osimport timeimport urllib.requestBASE_DIR = \"\/\u043f\u0443\u0442\u044c\/\u043a\/\u043f\u0440\u043e\u0435\u043a\u0442\u0443\/whisper-claude\"VENV_PYTHON = os.path.join(BASE_DIR, \"venv\/bin\/python3\")# \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550# \u0414\u041e\u0411\u0410\u0412\u041b\u042f\u0419 \u041d\u041e\u0412\u042b\u0415 \u0418\u041d\u0421\u0422\u0420\u0423\u041c\u0415\u041d\u0422\u042b \u0421\u042e\u0414\u0410# \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550TOOLS = [    {        \"name\": \"Whisper \u2192 Claude\",        \"script\": os.path.join(BASE_DIR, \"server.py\"),        \"port\": 5555,        \"description\": \"\u0413\u043e\u043b\u043e\u0441\u043e\u0432\u043e\u0439 \u0432\u0432\u043e\u0434 \u043d\u0430 \u0440\u0443\u0441\u0441\u043a\u043e\u043c\",    },    # \u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u2014 \u043f\u0440\u043e\u0441\u0442\u043e \u0434\u043e\u0431\u0430\u0432\u044c \u0431\u043b\u043e\u043a:    # {    #     \"name\": \"\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435\",    #     \"script\": os.path.join(BASE_DIR, \"other_tool.py\"),    #     \"port\": 5556,    #     \"description\": \"\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435\",    # },]def is_port_alive(port, timeout=1):    try:        urllib.request.urlopen(f\"http:\/\/127.0.0.1:{port}\/ping\", timeout=timeout)        return True    except:        return Falsedef stop_port(port):    try:        req = urllib.request.Request(            f\"http:\/\/127.0.0.1:{port}\/shutdown\", method=\"POST\"        )        urllib.request.urlopen(req, timeout=1)    except:        passclass ToolController:    def __init__(self, tool_config, app):        self.config = tool_config        self.app = app        self.process = None        self.header = rumps.MenuItem(            f\"{tool_config['name']}  \u2014  {tool_config['description']}\"        )        self.header.set_callback(None)        self.toggle_btn = rumps.MenuItem(\"    \u25b6 \u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c\", callback=self.toggle)        self.status_label = rumps.MenuItem(\"    \u0421\u0442\u0430\u0442\u0443\u0441: \u23f8 \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\")        self.status_label.set_callback(None)        if is_port_alive(tool_config[\"port\"]):            self._set_running()    def toggle(self, sender):        if is_port_alive(self.config[\"port\"]):            self._stop()        else:            self._start()    def _start(self):        self.toggle_btn.title = \"    \u23f3 \u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u0441\u044f...\"        self.status_label.title = \"    \u0421\u0442\u0430\u0442\u0443\u0441: \u23f3 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u0441\u044f...\"        def run():            env = os.environ.copy()            env[\"PATH\"] = \"\/opt\/homebrew\/bin:\/usr\/local\/bin:\" + env.get(\"PATH\", \"\")            self.process = subprocess.Popen(                [VENV_PYTHON, self.config[\"script\"]],                env=env,                stdout=subprocess.DEVNULL,                stderr=subprocess.DEVNULL,            )            for _ in range(20):                time.sleep(0.5)                if is_port_alive(self.config[\"port\"]):                    self._set_running()                    self.app.update_title()                    return            self.status_label.title = \"    \u0421\u0442\u0430\u0442\u0443\u0441: \u274c \u043e\u0448\u0438\u0431\u043a\u0430 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\"            self.toggle_btn.title = \"    \u25b6 \u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c\"        threading.Thread(target=run, daemon=True).start()    def _stop(self):        stop_port(self.config[\"port\"])        if self.process:            self.process.terminate()            self.process = None        self._set_stopped()        self.app.update_title()    def _set_running(self):        self.toggle_btn.title = \"    \u23f9 \u041e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c\"        self.status_label.title = \"    \u0421\u0442\u0430\u0442\u0443\u0441: \u2705 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442\"    def _set_stopped(self):        self.toggle_btn.title = \"    \u25b6 \u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c\"        self.status_label.title = \"    \u0421\u0442\u0430\u0442\u0443\u0441: \u23f8 \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\"    def check(self):        if is_port_alive(self.config[\"port\"]):            self._set_running()        else:            if self.process:                self.process = None            self._set_stopped()    def is_running(self):        return is_port_alive(self.config[\"port\"])class AILauncher(rumps.App):    def __init__(self):        super().__init__(\"\ud83e\udd16\", quit_button=None)        self.controllers = [ToolController(t, self) for t in TOOLS]        menu_items = []        for i, ctrl in enumerate(self.controllers):            if i &gt; 0:                menu_items.append(None)            menu_items.append(ctrl.header)            menu_items.append(ctrl.toggle_btn)            menu_items.append(ctrl.status_label)        menu_items.append(None)        menu_items.append(rumps.MenuItem(\"\u0412\u044b\u0439\u0442\u0438\", callback=self.quit_app))        self.menu = menu_items        self.update_title()        self.timer = rumps.Timer(self.check_all, 5)        self.timer.start()    def update_title(self):        running = [c for c in self.controllers if c.is_running()]        self.title = \"\ud83c\udfa4\" if running else \"\ud83e\udd16\"    def check_all(self, sender):        for ctrl in self.controllers:            ctrl.check()        self.update_title()    def quit_app(self, sender):        for ctrl in self.controllers:            if ctrl.is_running():                stop_port(ctrl.config[\"port\"])            if ctrl.process:                ctrl.process.terminate()        rumps.quit_application()if __name__ == \"__main__\":    AILauncher().run()<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<hr\/>\n<h3>\u0428\u0430\u0433 4 \u2014 Chrome \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435<\/h3>\n<h4>extension\/manifest.json<\/h4>\n<pre><code class=\"json\">{  \"manifest_version\": 3,  \"name\": \"Whisper \u0434\u043b\u044f Claude\",  \"version\": \"1.0\",  \"description\": \"\u0413\u043e\u043b\u043e\u0441\u043e\u0432\u043e\u0439 \u0432\u0432\u043e\u0434 \u043d\u0430 \u0440\u0443\u0441\u0441\u043a\u043e\u043c \u0447\u0435\u0440\u0435\u0437 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439 Whisper\",  \"permissions\": [\"activeTab\", \"scripting\"],  \"host_permissions\": [    \"https:\/\/claude.ai\/*\",    \"http:\/\/127.0.0.1:5555\/*\"  ],  \"content_scripts\": [    {      \"matches\": [\"https:\/\/claude.ai\/*\"],      \"js\": [\"content.js\"],      \"run_at\": \"document_end\"    }  ],  \"background\": {    \"service_worker\": \"background.js\"  },  \"action\": {    \"default_popup\": \"popup.html\"  },  \"icons\": {    \"16\": \"icons\/icon16.png\",    \"48\": \"icons\/icon48.png\",    \"128\": \"icons\/icon128.png\"  }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p><strong>\u0413\u0440\u0430\u0431\u043b\u044f #2:<\/strong> <code>\"microphone\"<\/code> \u0432 <code>permissions<\/code> \u2014 \u0432 MV3 \u044d\u0442\u043e \u043d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435, Chrome \u0432\u044b\u0434\u0430\u0451\u0442 \u043f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0435\u043d\u0438\u0435. \u0423\u0431\u0438\u0440\u0430\u0435\u043c \u2014 \u043c\u0438\u043a\u0440\u043e\u0444\u043e\u043d \u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 <code>navigator.mediaDevices.getUserMedia()<\/code> \u043f\u0440\u044f\u043c\u043e \u0432 \u043a\u043e\u0434\u0435.<\/p>\n<h4>extension\/background.js<\/h4>\n<p><strong>\u0413\u0440\u0430\u0431\u043b\u044f #3 \u2014 \u0441\u0430\u043c\u0430\u044f \u043d\u0435\u043e\u0447\u0435\u0432\u0438\u0434\u043d\u0430\u044f:<\/strong> content script \u043d\u0430 <a href=\"https:\/\/claude.ai\" rel=\"noopener noreferrer nofollow\"><code>https:\/\/claude.ai<\/code><\/a> \u043d\u0435 \u043c\u043e\u0436\u0435\u0442 \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e \u0434\u0435\u043b\u0430\u0442\u044c fetch \u043a <a href=\"http:\/\/127.0.0.1\" rel=\"noopener noreferrer nofollow\"><code>http:\/\/127.0.0.1<\/code><\/a> \u2014 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u0431\u043b\u043e\u043a\u0438\u0440\u0443\u0435\u0442 mixed content. \u0420\u0435\u0448\u0435\u043d\u0438\u0435: \u0430\u0443\u0434\u0438\u043e \u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0432 base64 \u0432 content.js, \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 <code>chrome.runtime.sendMessage<\/code> \u0432 background.js, \u0438 \u0443\u0436\u0435 \u043e\u0442\u0442\u0443\u0434\u0430 \u043b\u0435\u0442\u0438\u0442 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440.<\/p>\n<pre><code class=\"javascript\">const SERVER_URL = \"http:\/\/127.0.0.1:5555\";chrome.runtime.onMessage.addListener((message, sender, sendResponse) =&gt; {  if (message.action === \"transcribe\") {    handleTranscribe(message.audio).then(sendResponse);    return true; \/\/ \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u043e\u0442\u0432\u0435\u0442  }});async function handleTranscribe(base64Audio) {  try {    const binaryStr = atob(base64Audio);    const bytes = new Uint8Array(binaryStr.length);    for (let i = 0; i &lt; binaryStr.length; i++) {      bytes[i] = binaryStr.charCodeAt(i);    }    const audioBlob = new Blob([bytes], { type: \"audio\/webm\" });    const formData = new FormData();    formData.append(\"audio\", audioBlob, \"recording.webm\");    const response = await fetch(`${SERVER_URL}\/transcribe`, {      method: \"POST\",      body: formData,    });    return await response.json();  } catch (err) {    return { error: \"\u0421\u0435\u0440\u0432\u0435\u0440 \u043d\u0435 \u0437\u0430\u043f\u0443\u0449\u0435\u043d\" };  }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<h4>extension\/content.js<\/h4>\n<pre><code class=\"javascript\">let mediaRecorder = null;let audioChunks = [];let isRecording = false;let micButton = null;function createMicButton() {  if (document.getElementById(\"whisper-mic-btn\")) return;  micButton = document.createElement(\"button\");  micButton.id = \"whisper-mic-btn\";  micButton.innerHTML = \"\ud83c\udfa4\";  Object.assign(micButton.style, {    position: \"fixed\", bottom: \"100px\", right: \"30px\",    width: \"56px\", height: \"56px\", borderRadius: \"50%\",    border: \"none\", background: \"#6b7280\", color: \"white\",    fontSize: \"24px\", cursor: \"pointer\", zIndex: \"99999\",    boxShadow: \"0 4px 12px rgba(0,0,0,0.3)\", transition: \"all 0.2s ease\",  });  micButton.addEventListener(\"click\", toggleRecording);  document.body.appendChild(micButton);}async function toggleRecording() {  isRecording ? stopRecording() : await startRecording();}async function startRecording() {  try {    const stream = await navigator.mediaDevices.getUserMedia({ audio: true });    audioChunks = [];    mediaRecorder = new MediaRecorder(stream, { mimeType: \"audio\/webm\" });    mediaRecorder.ondataavailable = (e) =&gt; { if (e.data.size &gt; 0) audioChunks.push(e.data); };    mediaRecorder.onstop = sendAudioToBackground;    mediaRecorder.start();    isRecording = true;    micButton.innerHTML = \"\u23f9\";    micButton.style.background = \"#ef4444\";    showStatus(\"\ud83d\udd34 \u0413\u043e\u0432\u043e\u0440\u0438... \u043d\u0430\u0436\u043c\u0438 \u0435\u0449\u0451 \u0440\u0430\u0437 \u0447\u0442\u043e\u0431\u044b \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c\", \"recording\");  } catch (err) {    showStatus(\"\u274c \u041d\u0435\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u043c\u0438\u043a\u0440\u043e\u0444\u043e\u043d\u0443\", \"error\");  }}function stopRecording() {  if (mediaRecorder &amp;&amp; isRecording) {    mediaRecorder.stop();    mediaRecorder.stream.getTracks().forEach(t =&gt; t.stop());    isRecording = false;    micButton.innerHTML = \"\u23f3\";    micButton.style.background = \"#f59e0b\";    showStatus(\"\u23f3 \u0420\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u044e \u0440\u0435\u0447\u044c...\", \"processing\");  }}async function sendAudioToBackground() {  const audioBlob = new Blob(audioChunks, { type: \"audio\/webm\" });  const reader = new FileReader();  reader.onloadend = async () =&gt; {    const base64Audio = reader.result.split(\",\")[1];    try {      const response = await chrome.runtime.sendMessage({        action: \"transcribe\", audio: base64Audio,      });      if (response?.text?.trim()) {        insertTextIntoClaude(response.text.trim());        showStatus(\"\u2705 \u0413\u043e\u0442\u043e\u0432\u043e!\", \"success\");      } else {        showStatus(\"\u26a0\ufe0f \u0420\u0435\u0447\u044c \u043d\u0435 \u0440\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u043d\u0430 \u2014 \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439 \u0435\u0449\u0451 \u0440\u0430\u0437\", \"warning\");      }    } catch (err) {      showStatus(\"\u274c \u0417\u0430\u043f\u0443\u0441\u0442\u0438 \u0441\u0435\u0440\u0432\u0435\u0440 \u0447\u0435\u0440\u0435\u0437 \u0438\u043a\u043e\u043d\u043a\u0443 \u0432 \u0441\u0442\u0440\u043e\u043a\u0435 \u043c\u0435\u043d\u044e\", \"error\");    }    setTimeout(() =&gt; {      micButton.innerHTML = \"\ud83c\udfa4\";      micButton.style.background = \"#6b7280\";    }, 3000);  };  reader.readAsDataURL(audioBlob);}function insertTextIntoClaude(text) {  const selectors = ['[contenteditable=\"true\"]', 'div[contenteditable]', 'textarea'];  let inputField = null;  for (const selector of selectors) {    for (const el of document.querySelectorAll(selector)) {      if (el.offsetParent !== null) { inputField = el; break; }    }    if (inputField) break;  }  if (!inputField) return;  inputField.focus();  const sep = (inputField.innerText || inputField.value || \"\").trim() ? \" \" : \"\";  if (inputField.tagName === \"TEXTAREA\") {    inputField.value += sep + text;    inputField.dispatchEvent(new Event(\"input\", { bubbles: true }));  } else {    document.execCommand(\"insertText\", false, sep + text);  }}let statusEl = null;function showStatus(msg, type) {  if (!statusEl) {    statusEl = document.createElement(\"div\");    Object.assign(statusEl.style, {      position: \"fixed\", bottom: \"165px\", right: \"20px\",      padding: \"8px 14px\", borderRadius: \"8px\", fontSize: \"13px\",      fontFamily: \"system-ui\", zIndex: \"99999\", maxWidth: \"280px\",      boxShadow: \"0 2px 8px rgba(0,0,0,0.2)\", transition: \"opacity 0.3s\",    });    document.body.appendChild(statusEl);  }  const colors = {    recording: [\"#fef2f2\",\"#991b1b\"], processing: [\"#fffbeb\",\"#92400e\"],    success: [\"#f0fdf4\",\"#166534\"], error: [\"#fef2f2\",\"#991b1b\"],    warning: [\"#fffbeb\",\"#92400e\"],  };  const [bg, color] = colors[type] || colors.processing;  Object.assign(statusEl.style, { background: bg, color, opacity: \"1\" });  statusEl.textContent = msg;}window.addEventListener(\"beforeunload\", () =&gt; {  navigator.sendBeacon(\"http:\/\/127.0.0.1:5555\/shutdown\");});setTimeout(createMicButton, 2000);new MutationObserver(() =&gt; {  if (!document.getElementById(\"whisper-mic-btn\")) setTimeout(createMicButton, 1000);}).observe(document.body, { childList: true, subtree: true });<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<h4>\u0418\u043a\u043e\u043d\u043a\u0438<\/h4>\n<pre><code>python3 -c \"import struct, zlibdef make_png(size, color):    def chunk(name, data):        c = zlib.crc32(name + data) &amp; 0xffffffff        return struct.pack('&gt;I', len(data)) + name + data + struct.pack('&gt;I', c)    raw = b''    for y in range(size):        raw += b'\\x00'        for x in range(size):            raw += bytes(color)    ihdr = struct.pack('&gt;IIBBBBB', size, size, 8, 2, 0, 0, 0)    return b'\\x89PNG\\r\\n\\x1a\\n' + chunk(b'IHDR', ihdr) + chunk(b'IDAT', zlib.compress(raw)) + chunk(b'IEND', b'')for size in [16, 48, 128]:    with open(f'extension\/icons\/icon{size}.png', 'wb') as f:        f.write(make_png(size, [107, 114, 128]))print('\u0418\u043a\u043e\u043d\u043a\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u044b')\"<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<hr\/>\n<h3>\u0428\u0430\u0433 5 \u2014 \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f \u0432 Chrome<\/h3>\n<ol>\n<li>\n<p>\u041e\u0442\u043a\u0440\u043e\u0439 <code>chrome:\/\/extensions<\/code><\/p>\n<\/li>\n<li>\n<p>\u0412\u043a\u043b\u044e\u0447\u0438 &#171;\u0420\u0435\u0436\u0438\u043c \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430&#187; (\u043f\u0440\u0430\u0432\u044b\u0439 \u0432\u0435\u0440\u0445\u043d\u0438\u0439 \u0443\u0433\u043e\u043b)<\/p>\n<\/li>\n<li>\n<p>\u041d\u0430\u0436\u043c\u0438 &#171;\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0440\u0430\u0441\u043f\u0430\u043a\u043e\u0432\u0430\u043d\u043d\u043e\u0435 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435&#187;<\/p>\n<\/li>\n<li>\n<p>\u0412\u044b\u0431\u0435\u0440\u0438 \u043f\u0430\u043f\u043a\u0443 <code>extension<\/code> \u0432\u043d\u0443\u0442\u0440\u0438 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/p>\n<\/li>\n<\/ol>\n<hr\/>\n<h3>\u0428\u0430\u0433 6 \u2014 \u0410\u0432\u0442\u043e\u0437\u0430\u043f\u0443\u0441\u043a Menu Bar App<\/h3>\n<h4>start_menubar.sh<\/h4>\n<pre><code class=\"bash\">#!\/bin\/bashexport PATH=\"\/opt\/homebrew\/bin:\/usr\/local\/bin:$PATH\"source \/\u043f\u0443\u0442\u044c\/\u043a\/\u043f\u0440\u043e\u0435\u043a\u0442\u0443\/whisper-claude\/venv\/bin\/activatepython3 \/\u043f\u0443\u0442\u044c\/\u043a\/\u043f\u0440\u043e\u0435\u043a\u0442\u0443\/whisper-claude\/menubar.py<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<pre><code>chmod +x start_menubar.sh<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<h4>~\/Library\/LaunchAgents\/com.whisper.claude.plist<\/h4>\n<pre><code class=\"xml\">&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt;&lt;!DOCTYPE plist PUBLIC \"-\/\/Apple\/\/DTD PLIST 1.0\/\/EN\"  \"http:\/\/www.apple.com\/DTDs\/PropertyList-1.0.dtd\"&gt;&lt;plist version=\"1.0\"&gt;&lt;dict&gt;    &lt;key&gt;Label&lt;\/key&gt;    &lt;string&gt;com.whisper.claude&lt;\/string&gt;    &lt;key&gt;ProgramArguments&lt;\/key&gt;    &lt;array&gt;        &lt;string&gt;\/bin\/bash&lt;\/string&gt;        &lt;string&gt;\/\u043f\u0443\u0442\u044c\/\u043a\/\u043f\u0440\u043e\u0435\u043a\u0442\u0443\/whisper-claude\/start_menubar.sh&lt;\/string&gt;    &lt;\/array&gt;    &lt;key&gt;RunAtLoad&lt;\/key&gt;    &lt;true\/&gt;    &lt;key&gt;KeepAlive&lt;\/key&gt;    &lt;true\/&gt;    &lt;key&gt;StandardOutPath&lt;\/key&gt;    &lt;string&gt;\/tmp\/whisper-claude.log&lt;\/string&gt;    &lt;key&gt;StandardErrorPath&lt;\/key&gt;    &lt;string&gt;\/tmp\/whisper-claude-error.log&lt;\/string&gt;&lt;\/dict&gt;&lt;\/plist&gt;<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<pre><code>launchctl load ~\/Library\/LaunchAgents\/com.whisper.claude.plist<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p><strong>\u0413\u0440\u0430\u0431\u043b\u044f #4:<\/strong> LaunchAgent \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u0441\u044f \u0432 \u0443\u0440\u0435\u0437\u0430\u043d\u043d\u043e\u043c \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u0438 \u0431\u0435\u0437 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e\u0433\u043e PATH. ffmpeg \u043d\u0435 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u2014 Whisper \u043f\u0430\u0434\u0430\u0435\u0442 \u0441 <code>FileNotFoundError: ffmpeg<\/code>. \u0420\u0435\u0448\u0435\u043d\u0438\u0435: \u044f\u0432\u043d\u043e \u043f\u0440\u043e\u043f\u0438\u0441\u0430\u0442\u044c <code>\/opt\/homebrew\/bin<\/code> \u0432 <code>start_<\/code><a href=\"http:\/\/menubar.sh\" rel=\"noopener noreferrer nofollow\"><code>menubar.sh<\/code><\/a>.<\/p>\n<hr\/>\n<h3>\u0412\u0441\u0435 \u0433\u0440\u0430\u0431\u043b\u0438 \u0432 \u043e\u0434\u043d\u043e\u0439 \u0442\u0430\u0431\u043b\u0438\u0446\u0435<\/h3>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<th>\n<p align=\"left\">#<\/p>\n<\/th>\n<th>\n<p align=\"left\">\u0413\u0440\u0430\u0431\u043b\u044f<\/p>\n<\/th>\n<th>\n<p align=\"left\">\u0421\u0438\u043c\u043f\u0442\u043e\u043c<\/p>\n<\/th>\n<th>\n<p align=\"left\">\u0420\u0435\u0448\u0435\u043d\u0438\u0435<\/p>\n<\/th>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">1<\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>--fp16<\/code> \u043d\u0430 Mac Apple Silicon<\/p>\n<\/td>\n<td>\n<p align=\"left\">Whisper \u043f\u0430\u0434\u0430\u0435\u0442 \u043f\u0440\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0435<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c <code>--fp16 False<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">2<\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>\"microphone\"<\/code> \u0432 permissions MV3<\/p>\n<\/td>\n<td>\n<p align=\"left\">Chrome \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u043f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0435\u043d\u0438\u0435<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u0423\u0431\u0440\u0430\u0442\u044c \u0438\u0437 manifest.json<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">3<\/p>\n<\/td>\n<td>\n<p align=\"left\">Mixed content \u0432 MV3<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u041a\u043d\u043e\u043f\u043a\u0430 \u0432\u0441\u0435\u0433\u0434\u0430 &#171;\u0441\u0435\u0440\u0432\u0435\u0440 \u043d\u0435 \u0437\u0430\u043f\u0443\u0449\u0435\u043d&#187;<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u041f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c \u0430\u0443\u0434\u0438\u043e \u0447\u0435\u0440\u0435\u0437 background.js<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">4<\/p>\n<\/td>\n<td>\n<p align=\"left\">PATH \u0432 LaunchAgent<\/p>\n<\/td>\n<td>\n<p align=\"left\">ffmpeg \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d, \u0440\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u0432\u0430\u043d\u0438\u0435 \u043f\u0430\u0434\u0430\u0435\u0442<\/p>\n<\/td>\n<td>\n<p align=\"left\">\u041f\u0440\u043e\u043f\u0438\u0441\u0430\u0442\u044c <code>\/opt\/homebrew\/bin<\/code> \u0432 \u0441\u043a\u0440\u0438\u043f\u0442\u0435<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<hr\/>\n<h3>\u041a\u0430\u043a \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043a\u0430\u0436\u0434\u044b\u0439 \u0434\u0435\u043d\u044c<\/h3>\n<ol>\n<li>\n<p>Mac \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u043b\u0441\u044f \u2192 \u0438\u043a\u043e\u043d\u043a\u0430 \ud83e\udd16 \u043f\u043e\u044f\u0432\u0438\u043b\u0430\u0441\u044c \u0432 \u0441\u0442\u0440\u043e\u043a\u0435 \u043c\u0435\u043d\u044e \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438<\/p>\n<\/li>\n<li>\n<p>\u0425\u043e\u0447\u0435\u0448\u044c \u0434\u0438\u043a\u0442\u043e\u0432\u0430\u0442\u044c \u2192 \u043a\u043b\u0438\u043a\u043d\u0438 \u0438\u043a\u043e\u043d\u043a\u0443 \u2192 &#171;\u25b6 \u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c&#187; \u2192 \u0438\u043a\u043e\u043d\u043a\u0430 \u0441\u0442\u0430\u043b\u0430 \ud83c\udfa4<\/p>\n<\/li>\n<li>\n<p>\u041f\u0435\u0440\u0435\u0439\u0434\u0438 \u043d\u0430 <a href=\"http:\/\/claude.ai\" rel=\"noopener noreferrer nofollow\">claude.ai<\/a> \u2192 \u043d\u0430\u0436\u043c\u0438 \u043a\u043d\u043e\u043f\u043a\u0443 \u043c\u0438\u043a\u0440\u043e\u0444\u043e\u043d\u0430 \u0432 \u043f\u0440\u0430\u0432\u043e\u043c \u043d\u0438\u0436\u043d\u0435\u043c \u0443\u0433\u043b\u0443 \u2192 \u0433\u043e\u0432\u043e\u0440\u0438 \u2192 \u043d\u0430\u0436\u043c\u0438 \u0435\u0449\u0451 \u0440\u0430\u0437<\/p>\n<\/li>\n<li>\n<p>\u0422\u0435\u043a\u0441\u0442 \u0432\u0441\u0442\u0430\u0432\u0438\u043b\u0441\u044f \u0432 \u043f\u043e\u043b\u0435 \u2014 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0439<\/p>\n<\/li>\n<\/ol>\n<hr\/>\n<h3>Roadmap v2.0<\/h3>\n<ul>\n<li>\n<p>\u0413\u043e\u0440\u044f\u0447\u0430\u044f \u043a\u043b\u0430\u0432\u0438\u0448\u0430 \u0432\u043c\u0435\u0441\u0442\u043e \u043a\u043b\u0438\u043a\u0430 \u043c\u044b\u0448\u044c\u044e<\/p>\n<\/li>\n<li>\n<p>\u0410\u0432\u0442\u043e\u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0430 \u043f\u043e\u0441\u043b\u0435 \u0440\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u0432\u0430\u043d\u0438\u044f<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 ChatGPT, Gemini \u2014 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043d\u043e\u0432\u044b\u0439 content script<\/p>\n<\/li>\n<li>\n<p>\u041d\u043e\u0432\u044b\u0435 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b \u0432 \u043c\u0435\u043d\u044e \u2014 \u043e\u0434\u043d\u0430 \u0437\u0430\u043f\u0438\u0441\u044c \u0432 \u0441\u043f\u0438\u0441\u043e\u043a <code>TOOLS<\/code><\/p>\n<\/li>\n<\/ul>\n<hr\/>\n<h3>\u0412\u044b\u0432\u043e\u0434<\/h3>\n<p>\u0412\u0435\u0441\u044c \u043f\u0440\u043e\u0435\u043a\u0442 \u2014 \u043e\u0434\u0438\u043d \u0432\u0435\u0447\u0435\u0440. <\/p>\n<p>\u0423\u0434\u0438\u0432\u043b\u044f\u0435\u0442 \u043a\u043e\u043d\u0435\u0447\u043d\u043e \u043d\u043e\u0432\u0430\u044f \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0441 \u0418\u0418 \u0434\u0435\u043b\u0430\u0442\u044c \u0442\u0430\u043a\u0438\u0435 \u043f\u0440\u0438\u043a\u043e\u043b\u044c\u043d\u044b\u0435 \u0448\u0442\u0443\u043a\u0438. \u0414\u0410\u0436\u0435 \u043d\u0435\u043e\u0431\u044b\u0447\u043d\u043e, \u0447\u0442\u043e \u0441\u0434\u0435\u043b\u0430\u043b \u0441\u0430\u043c \u0441\u0435\u0431\u0435 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435 \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440:<\/p>\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/10b\/e02\/0ae\/10be020ae6c6854188445d01b0f4febe.png\" alt=\"\u0441\u0434\u0435\u043b\u0430\u043d\u043e \u0441\u0432\u043e\u0438\u043c\u0438 \u0440\u0443\u043a\u0430\u043c\u0438 \u0438 \u043a\u043b\u043e\u0434\u043e\u043c\" title=\"\u0441\u0434\u0435\u043b\u0430\u043d\u043e \u0441\u0432\u043e\u0438\u043c\u0438 \u0440\u0443\u043a\u0430\u043c\u0438 \u0438 \u043a\u043b\u043e\u0434\u043e\u043c\" width=\"794\" height=\"406\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/10b\/e02\/0ae\/10be020ae6c6854188445d01b0f4febe.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/10b\/e02\/0ae\/10be020ae6c6854188445d01b0f4febe.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<div><figcaption>\u0441\u0434\u0435\u043b\u0430\u043d\u043e \u0441\u0432\u043e\u0438\u043c\u0438 \u0440\u0443\u043a\u0430\u043c\u0438 \u0438 \u043a\u043b\u043e\u0434\u043e\u043c<\/figcaption><\/div>\n<\/figure>\n<p>\u0427\u0435\u0442\u044b\u0440\u0435 \u0433\u0440\u0430\u0431\u043b\u0438 \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u044b \u043f\u043e\u0439\u043c\u0430\u043b\u0438 \u2014 \u043d\u0438 \u043e\u0434\u043d\u0430 \u043d\u0435 \u0431\u044b\u043b\u0430 \u043e\u0447\u0435\u0432\u0438\u0434\u043d\u0430 \u0437\u0430\u0440\u0430\u043d\u0435\u0435, \u043d\u043e \u043a\u0430\u0436\u0434\u0430\u044f \u0440\u0435\u0448\u0430\u043b\u0430\u0441\u044c \u0437\u0430 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043c\u0438\u043d\u0443\u0442.<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0443 \u0442\u0435\u0431\u044f \u0443\u0436\u0435 \u0441\u0442\u043e\u0438\u0442 Whisper: \u0431\u0435\u0440\u0438 \u043a\u043e\u0434 \u0438 \u0434\u0435\u043b\u0430\u0439. \u0415\u0441\u043b\u0438 \u043d\u0435\u0442: <code>brew install openai-whisper ffmpeg<\/code> \u2014 \u0438 \u0432\u043f\u0435\u0440\u0451\u0434. \u0421\u043f\u0430\u0441\u0438\u0431\u043e, \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u043a\u0442\u043e-\u0442\u043e \u0443\u0436\u0435 \u0438\u0449\u0435\u0442 \u0440\u0435\u0448\u0435\u043d\u0438\u0435, \u0434\u043e\u0431\u0440\u043e \u043f\u043e\u0436\u0430\u043b\u043e\u0432\u0430\u0442\u044c. <\/p>\n<\/div>\n<p>\u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/1023104\/\">https:\/\/habr.com\/ru\/articles\/1023104\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u0410\u0432\u0442\u043e\u0440: \u0414\u043c\u0438\u0442\u0440\u0438\u0439 \u0421\u043e\u0441\u0443\u043d\u043e\u0432 \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u043d\u043e \u0441 Claude\u0423\u0440\u043e\u0432\u0435\u043d\u044c: \u0434\u043b\u044f \u0442\u0435\u0445 \u043a\u0442\u043e \u043d\u0435 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0441\u0442, \u043d\u043e \u043d\u0435 \u0431\u043e\u0438\u0442\u0441\u044f \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b\u0430\u0412\u0440\u0435\u043c\u044f: \u043e\u0434\u0438\u043d \u0432\u0435\u0447\u0435\u0440\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442: \u0438\u043a\u043e\u043d\u043a\u0430 \u0432 \u0441\u0442\u0440\u043e\u043a\u0435 \u043c\u0435\u043d\u044e Mac \u2192 \u043d\u0430\u0436\u0430\u043b \u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u2192 \u0433\u043e\u0432\u043e\u0440\u0438\u0448\u044c \u0432 Claude\u042f  \u043c\u0435\u043d\u0435\u0434\u0436\u0435\u0440, \u0444\u0438\u043d\u0430\u043d\u0441\u0438\u0441\u0442 \u0431\u0435\u0437 \u0442\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u0431\u044d\u043a\u0433\u0440\u0430\u0443\u043d\u0434\u0430. \u041c\u043d\u0435 \u043a\u0430\u0439\u0444\u043e\u0432\u043e \u0434\u0438\u043a\u0442\u043e\u0432\u0430\u0442\u044c \u0430 \u043d\u0435 \u043f\u0435\u0447\u0430\u0442\u0430\u0442\u044c, \u0442\u0430\u043a \u0438 \u043d\u0435 \u0441\u043c\u043e\u0433 \u044f \u043e\u0441\u0432\u043e\u0438\u0442\u044c \u0441\u043b\u0435\u043f\u043e\u0439 \u043c\u0435\u0442\u043e\u0434, \u043f\u0435\u0447\u0430\u0442\u0430\u044e \u0433\u043b\u044f\u0434\u044f \u043d\u0430 \u043a\u043b\u0430\u0432\u0443. \u0425\u043e\u0447\u0443 \u0434\u0438\u043a\u0442\u043e\u0432\u0430\u0442\u044c. \u041d\u043e!\u041f\u0420\u041e\u0411\u041b\u0415\u041c\u0410: \u0412 Claude \u043f\u043e\u043a\u0430 \u043d\u0435\u0442 \u0440\u0443\u0441\u0441\u043a\u043e\u0433\u043e \u044f\u0437\u044b\u043a\u0430 \u0434\u043b\u044f \u0434\u0438\u043a\u0442\u043e\u0432\u043a\u0438. \u041f\u043b\u0430\u0442\u043d\u044b\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u0432\u0440\u043e\u0434\u0435 Voicy \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442, \u043d\u043e \u0443 \u043d\u0438\u0445 \u043b\u0438\u043c\u0438\u0442\u044b, \u043e\u043d\u0438 \u0442\u0440\u0435\u0431\u0443\u044e\u0442 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442 \u0438 \u0441\u0442\u043e\u044f\u0442 \u0434\u0435\u043d\u0435\u0433. \u0423 \u043c\u0435\u043d\u044f \u0443\u0436\u0435 \u0431\u044b\u043b \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d Whisper \u2014 \u0442\u043e\u0442 \u0441\u0430\u043c\u044b\u0439 \u0434\u0432\u0438\u0436\u043e\u043a OpenAI \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u0441\u0435 \u043f\u043b\u0430\u0442\u043d\u044b\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442 \u043f\u043e\u0434 \u043a\u0430\u043f\u043e\u0442\u043e\u043c. \u042f \u0441\u043f\u0440\u043e\u0441\u0438\u043b Claude: \u043c\u043e\u0436\u043d\u043e \u043b\u0438 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u044d\u0442\u043e \u0441\u0430\u043c\u043e\u043c\u0443? \u041e\u043a\u0430\u0437\u0430\u043b\u043e\u0441\u044c \u2014 \u0434\u0430, \u0437\u0430 \u043e\u0434\u0438\u043d \u0432\u0435\u0447\u0435\u0440.\u0412\u043e\u0442 \u0440\u0430\u0441\u0441\u043a\u0430\u0437 \u043e \u0442\u043e\u043c \u043a\u0430\u043a \u043c\u044b \u044d\u0442\u043e \u043f\u043e\u0441\u0442\u0440\u043e\u0438\u043b\u0438 \u2014 \u0432\u043a\u043b\u044e\u0447\u0430\u044f \u0432\u0441\u0435 \u0433\u0440\u0430\u0431\u043b\u0438 \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0430\u0441\u0442\u0443\u043f\u0438\u043b\u0438 \u043f\u043e \u043f\u0443\u0442\u0438.\u0427\u0442\u043e \u0432 \u0438\u0442\u043e\u0433\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c\u0441\u043b\u0435\u0432\u0430 \u0438\u043a\u043e\u043d\u043a\u0430, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043c\u043e\u0436\u043d\u043e \u0440\u0430\u0441\u0448\u0438\u0440\u044f\u0442\u044c \u0440\u0430\u0437\u043d\u044b\u043c\u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u0430\u043c\u0438\u0418\u043a\u043e\u043d\u043a\u0430 \ud83e\udd16 \u0432 \u0441\u0442\u0440\u043e\u043a\u0435 \u043c\u0435\u043d\u044e Mac \u2014 \u0432\u0441\u0435\u0433\u0434\u0430 \u043f\u043e\u0434 \u0440\u0443\u043a\u043e\u0439\u041a\u043b\u0438\u043a\u043d\u0443\u043b \u2192 \u0432\u044b\u0431\u0440\u0430\u043b &#171;\u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c&#187; \u2192 \u0438\u043a\u043e\u043d\u043a\u0430 \u0441\u0442\u0430\u043b\u0430 \ud83c\udfa4\u041d\u0430\u0436\u0430\u043b \u043a\u043d\u043e\u043f\u043a\u0443 \u043d\u0430 claude.ai \u2014 \u0433\u043e\u0432\u043e\u0440\u0438\u0448\u044c \u043f\u043e-\u0440\u0443\u0441\u0441\u043a\u0438 \u2014 \u0442\u0435\u043a\u0441\u0442 \u0432\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432 \u043f\u043e\u043b\u0435Whisper \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e \u2014 \u043d\u0438\u043a\u0430\u043a\u0438\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0432 \u043e\u0431\u043b\u0430\u043a\u043e, \u043d\u0438\u043a\u0430\u043a\u0438\u0445 \u043b\u0438\u043c\u0438\u0442\u043e\u0432\u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 \u0440\u0430\u0441\u0448\u0438\u0440\u044f\u0435\u043c\u0430\u044f: \u0437\u0430\u0445\u043e\u0447\u0435\u0448\u044c \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043d\u043e\u0432\u044b\u0439 AI-\u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u2014 \u043e\u0434\u043d\u0430 \u0441\u0442\u0440\u043e\u043a\u0430 \u0432 \u043a\u043e\u043d\u0444\u0438\u0433\u0435\u0411\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u043e \u043d\u0430\u0432\u0441\u0435\u0433\u0434\u0430\u0424\u0438\u043d\u0430\u043b\u044c\u043d\u0430\u044f \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430\ud83e\udd16 Menu Bar App (rumps)    \u2514\u2500\u2500 \u25b6 \u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c Whisper \u2192 Claude            \u2193    Python Flask \u0441\u0435\u0440\u0432\u0435\u0440 (localhost:5555)            \u2193    Chrome Extension (\u043a\u043d\u043e\u043f\u043a\u0430 \ud83c\udfa4 \u043d\u0430 claude.ai)            \u2193 \u0430\u0443\u0434\u0438\u043e \u0432 base64    Background Service Worker            \u2193 POST \u0437\u0430\u043f\u0440\u043e\u0441    \/opt\/homebrew\/bin\/whisper \u2192 \u0442\u0435\u043a\u0441\u0442 \u2192 \u043f\u043e\u043b\u0435 \u0432\u0432\u043e\u0434\u0430 Claude\u041f\u043e\u0447\u0435\u043c\u0443 \u0442\u0430\u043a\u0430\u044f \u0446\u0435\u043f\u043e\u0447\u043a\u0430 \u0430 \u043d\u0435 \u043f\u0440\u043e\u0449\u0435 \u2014 \u043e\u0431\u044a\u044f\u0441\u043d\u044e \u043f\u043e \u0445\u043e\u0434\u0443. \u041a\u0430\u0436\u0434\u044b\u0439 \u044d\u043b\u0435\u043c\u0435\u043d\u0442 \u043f\u043e\u044f\u0432\u0438\u043b\u0441\u044f \u043d\u0435 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e.\u0427\u0442\u043e \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044fmacOS (\u043d\u0430 Linux \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u043d\u043e \u0441 \u043f\u043e\u043f\u0440\u0430\u0432\u043a\u043e\u0439 \u043d\u0430 \u043f\u0443\u0442\u0438)Whisper: brew install openai-whisperffmpeg: brew install ffmpeg (Whisper \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0435\u0433\u043e \u0434\u043b\u044f \u0434\u0435\u043a\u043e\u0434\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0430\u0443\u0434\u0438\u043e)Python 3: python3 &#8212;versionChrome \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u041f\u0440\u043e\u0432\u0435\u0440\u044c \u0447\u0442\u043e Whisper \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442:whisper &#8212;version\u0414\u043e\u043b\u0436\u0435\u043d \u043f\u043e\u044f\u0432\u0438\u0442\u044c\u0441\u044f \u0434\u043b\u0438\u043d\u043d\u044b\u0439 \u0441\u043f\u0438\u0441\u043e\u043a \u044f\u0437\u044b\u043a\u043e\u0432 \u0432\u043a\u043b\u044e\u0447\u0430\u044f Russian. \u0415\u0441\u043b\u0438 \u0432\u0438\u0434\u0438\u0448\u044c \u0441\u043f\u0438\u0441\u043e\u043a \u2014 \u0432\u0441\u0451 \u0433\u043e\u0442\u043e\u0432\u043e.\u0428\u0430\u0433 1 \u2014 \u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430mkdir -p \/\u043f\u0443\u0442\u044c\/\u043a\/\u043f\u0440\u043e\u0435\u043a\u0442\u0430\u043c\/whisper-claudecd \/\u043f\u0443\u0442\u044c\/\u043a\/\u043f\u0440\u043e\u0435\u043a\u0442\u0430\u043c\/whisper-claudepython3 -m venv venvsource venv\/bin\/activatepip install flask flask-cors rumpsmkdir -p extension\/icons\u041f\u043e\u0447\u0435\u043c\u0443 \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u0430\u044f \u0441\u0440\u0435\u0434\u0430? macOS \u043d\u0430\u0447\u0438\u043d\u0430\u044f \u0441 Ventura \u0437\u0430\u043f\u0440\u0435\u0449\u0430\u0435\u0442 \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0442\u044c \u043f\u0430\u043a\u0435\u0442\u044b \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u043e \u2014 \u044d\u0442\u043e \u0437\u0430\u0449\u0438\u0442\u0430 \u043e\u0442 \u043f\u043e\u043b\u043e\u043c\u043a\u0438 \u0441\u0438\u0441\u0442\u0435\u043c\u043d\u043e\u0433\u043e Python. \u0412\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u0430\u044f \u0441\u0440\u0435\u0434\u0430 \u0440\u0435\u0448\u0430\u0435\u0442 \u044d\u0442\u043e \u0447\u0438\u0441\u0442\u043e.rumps \u2014 Python \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f Menu Bar \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u043d\u0430 Mac. \u041f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0438\u043a\u043e\u043d\u043a\u0443 \u0432 \u0441\u0442\u0440\u043e\u043a\u0435 \u043c\u0435\u043d\u044e \u0432 50 \u0441\u0442\u0440\u043e\u043a\u0430\u0445 \u043a\u043e\u0434\u0430 \u0432\u043c\u0435\u0441\u0442\u043e \u0441\u043e\u0442\u0435\u043d \u0441\u0442\u0440\u043e\u043a \u043d\u0430 Swift.\u0428\u0430\u0433 2 \u2014 Python \u0441\u0435\u0440\u0432\u0435\u0440 (server.py)from flask import Flask, request, jsonifyfrom flask_cors import CORSimport subprocessimport tempfileimport osimport threadingapp = Flask(__name__)CORS(app)  # \u0440\u0430\u0437\u0440\u0435\u0448\u0430\u0435\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043e\u0442 Chrome \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044fWHISPER_PATH = &#171;\/opt\/homebrew\/bin\/whisper&#187;WHISPER_MODEL = &#171;small&#187;  # tiny\/base\/small\/medium\/largePORT = 5555@app.route(&#171;\/ping&#187;, methods=[&#171;GET&#187;])def ping():    return jsonify({&#171;status&#187;: &#171;ok&#187;})@app.route(&#171;\/transcribe&#187;, methods=[&#171;POST&#187;])def transcribe():    if &#171;audio&#187; not in request.files:        return jsonify({&#171;error&#187;: &#171;\u0410\u0443\u0434\u0438\u043e \u0444\u0430\u0439\u043b \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d&#187;}), 400    audio_file = request.files[&#171;audio&#187;]    with tempfile.NamedTemporaryFile(suffix=&#187;.webm&#187;, delete=False) as tmp:        tmp_path = tmp.name        audio_file.save(tmp_path)    try:        result = subprocess.run(            [                WHISPER_PATH, tmp_path,                &#171;&#8212;model&#187;, WHISPER_MODEL,                &#171;&#8212;language&#187;, &#171;ru&#187;,                &#171;&#8212;output_format&#187;, &#171;txt&#187;,                &#171;&#8212;output_dir&#187;, tempfile.gettempdir(),                &#171;&#8212;fp16&#187;, &#171;False&#187;,  # \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0434\u043b\u044f Mac Apple Silicon            ],            capture_output=True, text=True, timeout=60        )        txt_path = tmp_path.replace(&#171;.webm&#187;, &#171;.txt&#187;)        if os.path.exists(txt_path):            with open(txt_path, &#171;r&#187;, encoding=&#187;utf-8&#8243;) as f:                text = f.read().strip()            os.unlink(txt_path)        else:            text = result.stdout.strip()        return jsonify({&#171;text&#187;: text})    except subprocess.TimeoutExpired:        return jsonify({&#171;error&#187;: &#171;Timeout&#187;}), 500    except Exception as e:        return jsonify({&#171;error&#187;: str(e)}), 500    finally:        os.unlink(tmp_path)@app.route(&#171;\/shutdown&#187;, methods=[&#171;POST&#187;])def shutdown():    threading.Timer(0.5, lambda: os._exit(0)).start()    return jsonify({&#171;status&#187;: &#171;shutting down&#187;})if __name__ == &#171;__main__&#187;:    print(f&#187;[Whisper] \u0417\u0430\u043f\u0443\u0441\u043a\u0430\u044e\u0441\u044c \u043d\u0430 \u043f\u043e\u0440\u0442\u0443 {PORT}, \u043c\u043e\u0434\u0435\u043b\u044c: {WHISPER_MODEL}&#187;)    app.run(host=&#187;127.0.0.1&#8243;, port=PORT, debug=False)\u0413\u0440\u0430\u0431\u043b\u044f #1: &#8212;fp16 False \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u0435\u043d \u043d\u0430 Mac \u0441 Apple Silicon. \u0411\u0435\u0437 \u043d\u0435\u0433\u043e Whisper \u043f\u0430\u0434\u0430\u0435\u0442 \u0441 \u043e\u0448\u0438\u0431\u043a\u043e\u0439 \u043f\u0440\u043e float16.\u0428\u0430\u0433 3 \u2014 Menu Bar \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 (menubar.py)\u042d\u0442\u043e \u0441\u0435\u0440\u0434\u0446\u0435 \u0441\u0438\u0441\u0442\u0435\u043c\u044b. \u041e\u0434\u043d\u0430 \u0438\u043a\u043e\u043d\u043a\u0430 \u0432 \u0441\u0442\u0440\u043e\u043a\u0435 \u043c\u0435\u043d\u044e \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0432\u0441\u0435\u043c\u0438 AI-\u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438. \u041d\u043e\u0432\u044b\u0439 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043e\u0434\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u044c\u044e \u0432 \u0441\u043f\u0438\u0441\u043e\u043a TOOLS.import rumpsimport subprocessimport threadingimport osimport timeimport urllib.requestBASE_DIR = &#171;\/\u043f\u0443\u0442\u044c\/\u043a\/\u043f\u0440\u043e\u0435\u043a\u0442\u0443\/whisper-claude&#187;VENV_PYTHON = os.path.join(BASE_DIR, &#171;venv\/bin\/python3&#187;)# \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550# \u0414\u041e\u0411\u0410\u0412\u041b\u042f\u0419 \u041d\u041e\u0412\u042b\u0415 \u0418\u041d\u0421\u0422\u0420\u0423\u041c\u0415\u041d\u0422\u042b \u0421\u042e\u0414\u0410# \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550TOOLS = [    {        &#171;name&#187;: &#171;Whisper \u2192 Claude&#187;,        &#171;script&#187;: os.path.join(BASE_DIR, &#171;server.py&#187;),        &#171;port&#187;: 5555,        &#171;description&#187;: &#171;\u0413\u043e\u043b\u043e\u0441\u043e\u0432\u043e\u0439 \u0432\u0432\u043e\u0434 \u043d\u0430 \u0440\u0443\u0441\u0441\u043a\u043e\u043c&#187;,    },    # \u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u2014 \u043f\u0440\u043e\u0441\u0442\u043e \u0434\u043e\u0431\u0430\u0432\u044c \u0431\u043b\u043e\u043a:    # {    #     &#171;name&#187;: &#171;\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435&#187;,    #     &#171;script&#187;: os.path.join(BASE_DIR, &#171;other_tool.py&#187;),    #     &#171;port&#187;: 5556,    #     &#171;description&#187;: &#171;\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435&#187;,    # },]def is_port_alive(port, timeout=1):    try:        urllib.request.urlopen(f&#187;http:\/\/127.0.0.1:{port}\/ping&#187;, timeout=timeout)        return True    except:        return Falsedef stop_port(port):    try:        req = urllib.request.Request(            f&#187;http:\/\/127.0.0.1:{port}\/shutdown&#187;, method=&#187;POST&#187;        )        urllib.request.urlopen(req, timeout=1)    except:        passclass ToolController:    def __init__(self, tool_config, app):        self.config = tool_config        self.app = app        self.process = None        self.header = rumps.MenuItem(            f&#187;{tool_config[&#8216;name&#8217;]}  \u2014  {tool_config[&#8216;description&#8217;]}&#187;        )        self.header.set_callback(None)        self.toggle_btn = rumps.MenuItem(&#187;    \u25b6 \u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c&#187;, callback=self.toggle)        self.status_label = rumps.MenuItem(&#187;    \u0421\u0442\u0430\u0442\u0443\u0441: \u23f8 \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d&#187;)        self.status_label.set_callback(None)        if is_port_alive(tool_config[&#171;port&#187;]):            self._set_running()    def toggle(self, sender):        if is_port_alive(self.config[&#171;port&#187;]):            self._stop()        else:            self._start()    def _start(self):        self.toggle_btn.title = &#187;    \u23f3 \u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u0441\u044f&#8230;&#187;        self.status_label.title = &#187;    \u0421\u0442\u0430\u0442\u0443\u0441: \u23f3 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u0441\u044f&#8230;&#187;        def run():            env = os.environ.copy()            env[&#171;PATH&#187;] = &#171;\/opt\/homebrew\/bin:\/usr\/local\/bin:&#187; + env.get(&#171;PATH&#187;, &#171;&#187;)            self.process = subprocess.Popen(                [VENV_PYTHON, self.config[&#171;script&#187;]],                env=env,                stdout=subprocess.DEVNULL,                stderr=subprocess.DEVNULL,            )            for _ in range(20):                time.sleep(0.5)                if is_port_alive(self.config[&#171;port&#187;]):                    self._set_running()                    self.app.update_title()                    return            self.status_label.title = &#187;    \u0421\u0442\u0430\u0442\u0443\u0441: \u274c \u043e\u0448\u0438\u0431\u043a\u0430 \u0437\u0430\u043f\u0443\u0441\u043a\u0430&#187;            self.toggle_btn.title = &#187;    \u25b6 \u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c&#187;        threading.Thread(target=run, daemon=True).start()    def _stop(self):        stop_port(self.config[&#171;port&#187;])        if self.process:            self.process.terminate()            self.process = None        self._set_stopped()        self.app.update_title()    def _set_running(self):        self.toggle_btn.title = &#187;    \u23f9 \u041e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c&#187;        self.status_label.title = &#187;    \u0421\u0442\u0430\u0442\u0443\u0441: \u2705 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442&#187;    def _set_stopped(self):        self.toggle_btn.title = &#187;    \u25b6 \u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c&#187;        self.status_label.title = &#187;    \u0421\u0442\u0430\u0442\u0443\u0441: \u23f8 \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d&#187;    def check(self):        if is_port_alive(self.config[&#171;port&#187;]):            self._set_running()        else:            if self.process:                self.process = None            self._set_stopped()    def is_running(self):        return is_port_alive(self.config[&#171;port&#187;])class AILauncher(rumps.App):    def __init__(self):        super().__init__(&#171;\ud83e\udd16&#187;, quit_button=None)        self.controllers = [ToolController(t, self) for t in TOOLS]        menu_items = []        for i, ctrl in enumerate(self.controllers):            if i &gt; 0:                menu_items.append(None)            menu_items.append(ctrl.header)            menu_items.append(ctrl.toggle_btn)            menu_items.append(ctrl.status_label)        menu_items.append(None)        menu_items.append(rumps.MenuItem(&#171;\u0412\u044b\u0439\u0442\u0438&#187;, callback=self.quit_app))        self.menu = menu_items        self.update_title()        self.timer = rumps.Timer(self.check_all, 5)        self.timer.start()    def update_title(self):        running = [c for c in self.controllers if c.is_running()]        self.title = &#171;\ud83c\udfa4&#187; if running else &#171;\ud83e\udd16&#187;    def check_all(self, sender):        for ctrl in self.controllers:            ctrl.check()        self.update_title()    def quit_app(self, sender):        for ctrl in self.controllers:            if ctrl.is_running():                stop_port(ctrl.config[&#171;port&#187;])            if ctrl.process:                ctrl.process.terminate()        rumps.quit_application()if __name__ == &#171;__main__&#187;:    AILauncher().run()\u0428\u0430\u0433 4 \u2014 Chrome \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435extension\/manifest.json{  &#171;manifest_version&#187;: 3,  &#171;name&#187;: &#171;Whisper \u0434\u043b\u044f Claude&#187;,  &#171;version&#187;: &#171;1.0&#187;,  &#171;description&#187;: &#171;\u0413\u043e\u043b\u043e\u0441\u043e\u0432\u043e\u0439 \u0432\u0432\u043e\u0434 \u043d\u0430 \u0440\u0443\u0441\u0441\u043a\u043e\u043c \u0447\u0435\u0440\u0435\u0437 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439 Whisper&#187;,  &#171;permissions&#187;: [&#171;activeTab&#187;, &#171;scripting&#187;],  &#171;host_permissions&#187;: [    &#171;https:\/\/claude.ai\/*&#187;,    &#171;http:\/\/127.0.0.1:5555\/*&#187;  ],  &#171;content_scripts&#187;: [    {      &#171;matches&#187;: [&#171;https:\/\/claude.ai\/*&#187;],      &#171;js&#187;: [&#171;content.js&#187;],      &#171;run_at&#187;: &#171;document_end&#187;    }  ],  &#171;background&#187;: {    &#171;service_worker&#187;: &#171;background.js&#187;  },  &#171;action&#187;: {    &#171;default_popup&#187;: &#171;popup.html&#187;  },  &#171;icons&#187;: {    &#171;16&#187;:&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-475804","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/475804","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=475804"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/475804\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=475804"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=475804"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=475804"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}