From aedaa2d78712ab1a890e2dece4cd8ee91ffd1916 Mon Sep 17 00:00:00 2001 From: harshjeet Date: Wed, 22 Jul 2026 08:24:02 +0530 Subject: [PATCH 1/3] feat: add ReadAloud component with Puter AI and Web Speech API support --- .../.vitepress/theme/components/ReadAloud.vue | 685 ++++++++++++++++++ docs/.vitepress/theme/index.ts | 32 +- package-lock.json | 167 ++++- package.json | 9 +- 4 files changed, 876 insertions(+), 17 deletions(-) create mode 100644 docs/.vitepress/theme/components/ReadAloud.vue diff --git a/docs/.vitepress/theme/components/ReadAloud.vue b/docs/.vitepress/theme/components/ReadAloud.vue new file mode 100644 index 0000000..f5bc86a --- /dev/null +++ b/docs/.vitepress/theme/components/ReadAloud.vue @@ -0,0 +1,685 @@ + + + + + \ No newline at end of file diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts index 9533c49..5a7c571 100644 --- a/docs/.vitepress/theme/index.ts +++ b/docs/.vitepress/theme/index.ts @@ -3,7 +3,10 @@ import type { Theme } from 'vitepress' import DefaultTheme from 'vitepress/theme' import { useData } from 'vitepress' import { createMermaidRenderer } from 'vitepress-mermaid-renderer' -import { type App } from 'vue' +import type { App } from 'vue' + +import './style.css' + import ChapterMeta from './components/ChapterMeta.vue' import MentalModel from './components/MentalModel.vue' import CommonMistake from './components/CommonMistake.vue' @@ -15,27 +18,31 @@ import ChallengeBlock from './components/ChallengeBlock.vue' import Recap from './components/Recap.vue' import ProjectResult from './components/ProjectResult.vue' import ReadingProgress from './components/ReadingProgress.vue' -import './style.css' +import ReadAloud from './components/ReadAloud.vue' export default { extends: DefaultTheme, - Layout: () => { + + Layout() { const { isDark } = useData() - const initMermaid = () => { + const renderMermaid = () => { createMermaidRenderer({ - theme: 'default', + theme: isDark.value ? 'dark' : 'default', }) } - nextTick(() => initMermaid()) - watch( - () => isDark.value, - () => initMermaid(), - ) + nextTick(renderMermaid) + + watch(isDark, () => { + nextTick(renderMermaid) + }) - return h(DefaultTheme.Layout) + return h(DefaultTheme.Layout, null, { + 'doc-before': () => h(ReadAloud), + }) }, + enhanceApp({ app }: { app: App }) { app.component('ChapterMeta', ChapterMeta) app.component('MentalModel', MentalModel) @@ -48,5 +55,6 @@ export default { app.component('Recap', Recap) app.component('ProjectResult', ProjectResult) app.component('ReadingProgress', ReadingProgress) + app.component('ReadAloud', ReadAloud) }, -} satisfies Theme +} satisfies Theme \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index e98aea6..a792c08 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "the_quickshell_book", "version": "1.0.0", "dependencies": { + "@heyputer/puter.js": "^2.5.4", "vitepress": "^1.6.3" }, "devDependencies": { @@ -20,7 +21,7 @@ "vitepress-mermaid-renderer": "^1.1.27" }, "engines": { - "node": ">=24.0.0" + "node": ">=22.0.0" } }, "node_modules/@algolia/abtesting": { @@ -827,6 +828,22 @@ "node": ">=18" } }, + "node_modules/@heyputer/kv.js": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@heyputer/kv.js/-/kv.js-0.2.1.tgz", + "integrity": "sha512-YhVtzz7ZA/HmuaDvzZZhhUyQWBvp3/TXeY4jULssTdLJwT+tEM4BTYHXttORX+V5auvrYinjj8dNFQnby5T82w==", + "license": "MIT" + }, + "node_modules/@heyputer/puter.js": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@heyputer/puter.js/-/puter.js-2.5.4.tgz", + "integrity": "sha512-gZNbTB2k+CLL3zG/RHwpQlcj35+rwQyEn1YebwC09aiMlCT9L3Wr9uU0I18fk/tnA3EFWXWC6Zf2yyse2aJ6oQ==", + "license": "Apache-2.0", + "dependencies": { + "@heyputer/kv.js": "^0.2.1", + "open": "^10.2.0" + } + }, "node_modules/@iconify-json/simple-icons": { "version": "1.2.91", "resolved": "https://registry.npmjs.org/@iconify-json/simple-icons/-/simple-icons-1.2.91.tgz", @@ -2028,6 +2045,21 @@ "node": ">=8" } }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ccount": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", @@ -2657,6 +2689,46 @@ "dev": true, "license": "MIT" }, + "node_modules/default-browser": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.0.tgz", + "integrity": "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==", + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", + "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/delaunator": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.1.0.tgz", @@ -2988,6 +3060,21 @@ "node": ">=12" } }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", @@ -3021,6 +3108,24 @@ "node": ">=0.10.0" } }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -3043,6 +3148,21 @@ "url": "https://github.com/sponsors/mesqueeb" } }, + "node_modules/is-wsl": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", + "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/js-yaml": { "version": "3.15.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.0.tgz", @@ -3347,6 +3467,24 @@ "regex-recursion": "^6.0.2" } }, + "node_modules/open": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", + "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", + "license": "MIT", + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "wsl-utils": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/package-manager-detector": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.7.0.tgz", @@ -3618,6 +3756,18 @@ "points-on-path": "^0.2.1" } }, + "node_modules/run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -4533,6 +4683,21 @@ } } }, + "node_modules/wsl-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", + "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/zwitch": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", diff --git a/package.json b/package.json index 6c97477..2a0b52b 100644 --- a/package.json +++ b/package.json @@ -15,15 +15,16 @@ "validate:links": "tsx scripts/validate-links.ts" }, "dependencies": { + "@heyputer/puter.js": "^2.5.4", "vitepress": "^1.6.3" }, "devDependencies": { - "vitepress-mermaid-renderer": "^1.1.27", + "fast-glob": "^3.3.2", + "gray-matter": "^4.0.3", "mermaid": "^11.6.0", + "playwright": "^1.49.0", "tsx": "^4.19.0", "typescript": "^5.7.0", - "playwright": "^1.49.0", - "gray-matter": "^4.0.3", - "fast-glob": "^3.3.2" + "vitepress-mermaid-renderer": "^1.1.27" } } From 31cbe203eabb6fe6e8d232daf4283357c0941aec Mon Sep 17 00:00:00 2001 From: harshjeet Date: Wed, 22 Jul 2026 08:32:04 +0530 Subject: [PATCH 2/3] feat: implement Read Aloud engine with Puter AI and browser TTS support via new composables --- .../.vitepress/theme/components/ReadAloud.vue | 436 +----------------- .../theme/composables/useArticleText.ts | 52 +++ .../theme/composables/useBrowserTTS.ts | 97 ++++ .../theme/composables/usePuterTTS.ts | 139 ++++++ .../theme/composables/useReadAloudEngine.ts | 226 +++++++++ 5 files changed, 538 insertions(+), 412 deletions(-) create mode 100644 docs/.vitepress/theme/composables/useArticleText.ts create mode 100644 docs/.vitepress/theme/composables/useBrowserTTS.ts create mode 100644 docs/.vitepress/theme/composables/usePuterTTS.ts create mode 100644 docs/.vitepress/theme/composables/useReadAloudEngine.ts diff --git a/docs/.vitepress/theme/components/ReadAloud.vue b/docs/.vitepress/theme/components/ReadAloud.vue index f5bc86a..d4822d0 100644 --- a/docs/.vitepress/theme/components/ReadAloud.vue +++ b/docs/.vitepress/theme/components/ReadAloud.vue @@ -1,414 +1,26 @@