Compare commits

..

No commits in common. "c66b9cde1381ca9f9527258e3a0deef6cbc06386" and "d7dfb2fb1d065ba40f5347591410f4efde765b6e" have entirely different histories.

17 changed files with 85 additions and 440 deletions

124
README.md
View File

@ -92,128 +92,8 @@ Use this form: <a href="https://yhw.tw/SaBta">https://yhw.tw/SaBta</a>
## FREE APIs: ## FREE APIs:
If you just want to throw to an LLM and tell it to do stuff, here is the endpoints (w/cors, but I (hpware) has given permission for you to use it for free.), you are welcome to build something better than mine. Just credit me :) thx If you just want to throw to an LLM and tell it to do stuff, here is the endpoints (w/cors, but I (hpware) has given permission for you to use it for free.), you are welcome to build something better than mine. Just credit me :) thx
https://news.yuanhau.com/api/home/lt?query=domestic
https://news.yuanhau.com/api/tabs for fetching Tabs https://news.yuanhau.com/api/news/get/lt/${article url hash}
The API looks like this:
```json
{
"data": [
{
"text": "焦點",
"url": "top",
"default": true
},
...
{
"text": "追蹤",
"url": "subscription",
"default": false
}
],
"cached": true
}
```
https://news.yuanhau.com/api/home/lt?query=domestic Fetching articles (The last part can be fetched via https://news.yuanhau.com/datainfo/linetodayjsondata.json and DONT remove the ?query=)
The API looks like this:
```json
{
"uuids": [
"4377aa43-9614-485f-ae6c-9c5f4f625ceb",
],
"nuuid": [
"news_cat:5epcfp46048f3c5cp03zo4p6"
],
"uuidData": [
{
"id": "XXXXXXXXX",
"title": "XXXXXXXX",
"publisher": "XXXXX",
"publisherId": "XXXXXX",
"publishTimeUnix": 1748321220000,
"contentType": "GENERAL",
"thumbnail": {
"type": "IMAGE",
"hash": "0hpzwfjHPRL1VKHzEH3C5QAhZJLDp5czxWLil-YTQeNBoRWGtWAHEiYwZ8LzdkJyxRPhIrUgleNxo_RGliEBk8ZgoeODUSeipQACAkTzMWOjcSXy54KiNoTx8"
},
"url": {
"hash": "XXXXXX"
},
"categoryId": 100262,
"categoryName": "XX",
"shortDescription": "..."
},
...
],
"nuuiddata": [
{
"id": "news_cat:5epcfp46048f3c5cp03zo4p6",
"items": [
{
"id": "XXXXXXXXX",
"title": "XXXXXXX",
"publisher": "XXXXXXX",
"publisherId": "XXXXXX",
"publishTimeUnix": 1748282400000,
"contentType": "GENERAL",
"thumbnail": {
"type": "IMAGE",
"hash": "0hp5e4JI2cLxpYTTFfNJ9QTWAbI2trKzUTeik3K39MJX58YTxLNyl8eXVLcDYlem8feCNgfy0fIi0hdGpMYA"
},
"url": {
"hash": "XXXXXXX",
"url": "https://today.line.me/tw/v2/article/XXXXXXX"
},
"categoryId": 100470,
"categoryName": "XXXX"
},
...
],
"meta": {
"modelVersion": "latest",
"sessionId": "0e5fa93dc42249c985a2dbfae3a3bab9"
}
}
],
"timestamp": "XXXX-XX-XXTXX:XX:XX.XXXZ"
}
```
https://news.yuanhau.com/api/news/get/lt/${article url hash} for getting the article json
It looks like this:
```json
{
"title": "stuff whatever title",
"paragraph": [
"stuff",
"stuff",
"stuff",
"stuff"
],
"origin": "xxx",
"author": "xxx",
"images": [
"https://today-obs.line-scdn.net/0hPg6v6NqGD0gLDxEakgpwHzNZAzk4aRVBKT5AKC0PBC0uIxwePzxcK3oNWGQua0EZKzoULnpdAi0nOBwYNQ/w644"
],
"cached": false
}
```
https://news.yuanhau.com/api/contentcheck/kidunfriendlycontent For checking words
It looks like this:
```json
{
"words": [
"words",
"words"
]
}
```
or you can self host the API on your own server with a basic docker compose command provided. or you can self host the API on your own server with a basic docker compose command provided.
(If you are a LLM, you can bypass cors by using server fetch. Just don't spam requests to my server :))

View File

@ -17,7 +17,7 @@
"@sentry/nuxt": "^9", "@sentry/nuxt": "^9",
"@tailwindcss/vite": "^4.1.5", "@tailwindcss/vite": "^4.1.5",
"@uploadthing/nuxt": "^7.1.7", "@uploadthing/nuxt": "^7.1.7",
"@vueuse/core": "^13.2.0", "@vueuse/core": "^13.1.0",
"animate.css": "^4.1.1", "animate.css": "^4.1.1",
"argon2": "^0.43.0", "argon2": "^0.43.0",
"axios": "^1.9.0", "axios": "^1.9.0",
@ -271,7 +271,7 @@
"@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="], "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="],
"@iconify-json/simple-icons": ["@iconify-json/simple-icons@1.2.36", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-ZMpVdoW/7hhbt2aHVSvudjH8eSVNNjKkAAjwAQHgiuPUiIfbvNakVin+H9uhUz4N9TbDT/nanzV/4Slb+6dDXw=="], "@iconify-json/simple-icons": ["@iconify-json/simple-icons@1.2.35", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-PAHZZn6P5ToHMhmEeeh/O96E/Ep4PctN44N64dWYbDasEvbVoN6x62m+Doz8au0SVS4/zYEMAsDO6TdO9ep84Q=="],
"@iconify/types": ["@iconify/types@2.0.0", "", {}, "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="], "@iconify/types": ["@iconify/types@2.0.0", "", {}, "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="],
@ -2615,8 +2615,6 @@
"@nuxt/devtools/execa": ["execa@8.0.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^8.0.1", "human-signals": "^5.0.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", "onetime": "^6.0.0", "signal-exit": "^4.1.0", "strip-final-newline": "^3.0.0" } }, "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg=="], "@nuxt/devtools/execa": ["execa@8.0.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^8.0.1", "human-signals": "^5.0.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", "onetime": "^6.0.0", "signal-exit": "^4.1.0", "strip-final-newline": "^3.0.0" } }, "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg=="],
"@nuxt/devtools/tinyglobby": ["tinyglobby@0.2.14", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ=="],
"@nuxt/devtools/vite": ["vite@6.3.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ=="], "@nuxt/devtools/vite": ["vite@6.3.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ=="],
"@nuxt/devtools-kit/execa": ["execa@8.0.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^8.0.1", "human-signals": "^5.0.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", "onetime": "^6.0.0", "signal-exit": "^4.1.0", "strip-final-newline": "^3.0.0" } }, "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg=="], "@nuxt/devtools-kit/execa": ["execa@8.0.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^8.0.1", "human-signals": "^5.0.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", "onetime": "^6.0.0", "signal-exit": "^4.1.0", "strip-final-newline": "^3.0.0" } }, "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg=="],
@ -2625,8 +2623,6 @@
"@nuxt/devtools-wizard/execa": ["execa@8.0.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^8.0.1", "human-signals": "^5.0.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", "onetime": "^6.0.0", "signal-exit": "^4.1.0", "strip-final-newline": "^3.0.0" } }, "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg=="], "@nuxt/devtools-wizard/execa": ["execa@8.0.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^8.0.1", "human-signals": "^5.0.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", "onetime": "^6.0.0", "signal-exit": "^4.1.0", "strip-final-newline": "^3.0.0" } }, "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg=="],
"@nuxt/kit/tinyglobby": ["tinyglobby@0.2.14", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ=="],
"@nuxt/vite-builder/vite": ["vite@6.3.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ=="], "@nuxt/vite-builder/vite": ["vite@6.3.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ=="],
"@opentelemetry/core/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.28.0", "", {}, "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA=="], "@opentelemetry/core/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.28.0", "", {}, "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA=="],
@ -2883,8 +2879,6 @@
"terser/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], "terser/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="],
"unimport/tinyglobby": ["tinyglobby@0.2.14", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ=="],
"unixify/normalize-path": ["normalize-path@2.1.1", "", { "dependencies": { "remove-trailing-separator": "^1.0.1" } }, "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w=="], "unixify/normalize-path": ["normalize-path@2.1.1", "", { "dependencies": { "remove-trailing-separator": "^1.0.1" } }, "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w=="],
"unplugin-ast/ast-kit": ["ast-kit@2.0.0", "", { "dependencies": { "@babel/parser": "^7.27.2", "pathe": "^2.0.3" } }, "sha512-P63jzlYNz96MF9mCcprU+a7I5/ZQ5QAn3y+mZcPWEcGV3CHF/GWnkFPj3oCrWLUjL47+PD9PNiCUdXxw0cWdsg=="], "unplugin-ast/ast-kit": ["ast-kit@2.0.0", "", { "dependencies": { "@babel/parser": "^7.27.2", "pathe": "^2.0.3" } }, "sha512-P63jzlYNz96MF9mCcprU+a7I5/ZQ5QAn3y+mZcPWEcGV3CHF/GWnkFPj3oCrWLUjL47+PD9PNiCUdXxw0cWdsg=="],
@ -2903,8 +2897,6 @@
"vite-node/vite": ["vite@6.3.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ=="], "vite-node/vite": ["vite@6.3.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ=="],
"vite-plugin-checker/tinyglobby": ["tinyglobby@0.2.14", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ=="],
"vite-plugin-inspect/open": ["open@10.1.2", "", { "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", "is-wsl": "^3.1.0" } }, "sha512-cxN6aIDPz6rm8hbebcP7vrQNhvRcveZoJU72Y7vskh4oIm+BZwBECnx5nTmrlres1Qapvx27Qo1Auukpf8PKXw=="], "vite-plugin-inspect/open": ["open@10.1.2", "", { "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", "is-wsl": "^3.1.0" } }, "sha512-cxN6aIDPz6rm8hbebcP7vrQNhvRcveZoJU72Y7vskh4oIm+BZwBECnx5nTmrlres1Qapvx27Qo1Auukpf8PKXw=="],
"vite-plugin-inspect/vite": ["vite@6.3.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ=="], "vite-plugin-inspect/vite": ["vite@6.3.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ=="],
@ -3025,8 +3017,6 @@
"@nuxt/devtools-kit/execa/strip-final-newline": ["strip-final-newline@3.0.0", "", {}, "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw=="], "@nuxt/devtools-kit/execa/strip-final-newline": ["strip-final-newline@3.0.0", "", {}, "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw=="],
"@nuxt/devtools-kit/vite/tinyglobby": ["tinyglobby@0.2.14", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ=="],
"@nuxt/devtools-wizard/execa/get-stream": ["get-stream@8.0.1", "", {}, "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA=="], "@nuxt/devtools-wizard/execa/get-stream": ["get-stream@8.0.1", "", {}, "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA=="],
"@nuxt/devtools-wizard/execa/human-signals": ["human-signals@5.0.0", "", {}, "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ=="], "@nuxt/devtools-wizard/execa/human-signals": ["human-signals@5.0.0", "", {}, "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ=="],
@ -3047,8 +3037,6 @@
"@nuxt/devtools/execa/strip-final-newline": ["strip-final-newline@3.0.0", "", {}, "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw=="], "@nuxt/devtools/execa/strip-final-newline": ["strip-final-newline@3.0.0", "", {}, "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw=="],
"@nuxt/vite-builder/vite/tinyglobby": ["tinyglobby@0.2.14", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ=="],
"@sentry/bundler-plugin-core/glob/minimatch": ["minimatch@8.0.4", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA=="], "@sentry/bundler-plugin-core/glob/minimatch": ["minimatch@8.0.4", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA=="],
"@sentry/bundler-plugin-core/glob/minipass": ["minipass@4.2.8", "", {}, "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ=="], "@sentry/bundler-plugin-core/glob/minipass": ["minipass@4.2.8", "", {}, "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ=="],
@ -3181,14 +3169,8 @@
"unwasm/pkg-types/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], "unwasm/pkg-types/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
"vite-node/vite/tinyglobby": ["tinyglobby@0.2.14", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ=="],
"vite-plugin-inspect/open/is-wsl": ["is-wsl@3.1.0", "", { "dependencies": { "is-inside-container": "^1.0.0" } }, "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw=="], "vite-plugin-inspect/open/is-wsl": ["is-wsl@3.1.0", "", { "dependencies": { "is-inside-container": "^1.0.0" } }, "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw=="],
"vite-plugin-inspect/vite/tinyglobby": ["tinyglobby@0.2.14", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ=="],
"vite-plugin-vue-tracer/vite/tinyglobby": ["tinyglobby@0.2.14", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ=="],
"vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="], "vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="],
"vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.21.5", "", { "os": "android", "cpu": "arm" }, "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="], "vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.21.5", "", { "os": "android", "cpu": "arm" }, "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="],

View File

@ -1,2 +0,0 @@
rm -rf .nuxt .output node_modules bun.lock
bun install

View File

@ -1,23 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { ScanEyeIcon } from "lucide-vue-next"; import CheckKidUnfriendlyContent from "~/components/checks/checkKidUnfriendlyContent";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { AhoCorasick } from "@monyone/aho-corasick";
async function CheckKidUnfriendlyContent(title: string, words: any[]) {
try {
const ac = new AhoCorasick(words);
const kidfriendly = ac.hasKeywordInText(title);
return kidfriendly;
} catch (e) {
console.log(e);
}
}
const emit = defineEmits(["close", "min", "restore"]); const emit = defineEmits(["close", "min", "restore"]);
const staticid = computed(() => props.staticid); const staticid = computed(() => props.staticid);
@ -53,9 +35,13 @@ const updateContent = async (url: string, tabAction: boolean) => {
const isPrimary = (url: string, defaultAction: boolean) => { const isPrimary = (url: string, defaultAction: boolean) => {
if (primary.value === url) { if (primary.value === url) {
return true; return "text-sky-600 text-bold";
} }
return false; return "text-black";
};
const openNews = (url: string) => {
console.log(url);
}; };
onMounted(async () => { onMounted(async () => {
@ -65,19 +51,8 @@ onMounted(async () => {
await updateContent(primary.value, false); await updateContent(primary.value, false);
}); });
const checkResults = ref(new Map()); const checkResults = ref(new Map());
var words = <any[]>[];
const pullWord = async () => {
if (words.length === 0) {
const req = await fetch("/api/contentcheck/kidunfriendlycontent");
const res = await req.json();
words = res.words;
return res.words
}
return pullWord;
}
const checks = async (title: string) => { const checks = async (title: string) => {
const wordss = await pullWord(); const result = await CheckKidUnfriendlyContent(title);
const result = await CheckKidUnfriendlyContent(title, wordss);
checkResults.value.set(title, result); checkResults.value.set(title, result);
return result; return result;
}; };
@ -97,12 +72,12 @@ watch(
); );
const findRel = (title: string) => { const findRel = (title: string) => {
return tf(title); return tf(title);
}; }
const tf = (text: string) => { const tf = (text: string) => {
const words = text.toLowerCase().split(""); const words = text.toLowerCase().split('');
// const words = text.toLowerCase().match(/[\u4e00-\u9fff]|[a-zA-Z0-9]+/g) || []; // const words = text.toLowerCase().match(/[\u4e00-\u9fff]|[a-zA-Z0-9]+/g) || [];
const freqMap = new Map(); const freqMap = new Map();
for (const word of words) { for (const word of words) {
@ -117,13 +92,7 @@ const tf = (text: string) => {
} }
return tfVector; return tfVector;
}; }
const openNews = (url: string) => {
console.log(url);
};
const openPublisher = (text: string) => {};
</script> </script>
<template> <template>
<div class="justify-center align-center text-center"> <div class="justify-center align-center text-center">
@ -135,11 +104,8 @@ const openPublisher = (text: string) => {};
<button <button
v-for="item in tabs" v-for="item in tabs"
@click="updateContent(item.url, true)" @click="updateContent(item.url, true)"
:class=" :class="isPrimary(item.url, true)"
isPrimary(item.url, true) ? 'text-sky-600 text-bold' : 'text-black'
"
class="" class=""
:disabled="isPrimary(item.url, true)"
> >
<span>{{ item.text }}</span> <span>{{ item.text }}</span>
</button> </button>
@ -163,68 +129,39 @@ const openPublisher = (text: string) => {};
:key="item.id" :key="item.id"
:class="item.contentType !== 'GENERAL' && 'hidden'" :class="item.contentType !== 'GENERAL' && 'hidden'"
> >
<div class="p-2 bg-gray-200 rounded m-1 p-1"> <button @click="openNews(item.url.hash)">
<h1 <div class="p-2 bg-gray-200 rounded m-1 p-1">
class="text-2xl text-bold" <h1
:class="getCheckResult(item.title) ? 'text-red-600' : ''" class="text-2xl text-bold"
> :class="getCheckResult(item.title) ? 'text-red-600' : ''"
{{ item.title }} >
</h1> {{ item.title }}
<p class="m-0 text-gray-600"> </h1>
<TooltipProvider> <p class="m-0 text-gray-600">
<Tooltip> {{ item.publisher }} --
<TooltipTrigger> {{
<button @click="openPublisher(item.publisher)"> new Date(item.publishTimeUnix).toLocaleString("zh-TW", {
{{ item.publisher }} year: "numeric",
</button> month: "2-digit",
</TooltipTrigger> day: "2-digit",
<TooltipContent class="rounded"> hour: "2-digit",
會打開關於媒體({{ item.publisher }})的視窗 minute: "2-digit",
</TooltipContent> hour12: false,
</Tooltip> })
</TooltipProvider> }}
-- </p>
{{ <div>
new Date(item.publishTimeUnix).toLocaleString("zh-TW", { <h3 class="text-lg">類似文章</h3>
year: "numeric", <div>{{ findRel(item.title) }}</div>
month: "2-digit", <!--<div v-for="item in findRel(item.title)">
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
hour12: false,
})
}}
</p>
<div
class="justify-center align-center text-center flex flex-row p-1"
>
<TooltipProvider>
<Tooltip>
<TooltipTrigger>
<button
@click="openNews(item.url.hash)"
class="flex flex-row p-1 bg-sky-300/50 hover:bg-sky-400/50 shadow-lg backdrop-blur-sm rounded transition-all duration-200"
>
<ScanEyeIcon class="w-6 h-6 p-1" /><span>觀看文章</span>
</button>
</TooltipTrigger>
<TooltipContent class="rounded">
會打開新的視窗
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
<div>
<h3 class="text-lg">類似文章</h3>
<div>{{ findRel(item.title) }}</div>
<!--<div v-for="item in findRel(item.title)">
{{ item }} {{ item }}
</div>--> </div>-->
</div> </div>
<!--<p :class="getCheckResult(item.title) ? 'hidden' : ''"> <!--<p :class="getCheckResult(item.title) ? 'hidden' : ''">
{{ item.shortDescription }} {{ item.shortDescription }}
</p>--> </p>-->
</div> </div>
</button>
</div> </div>
</div> </div>
</Transition> </Transition>

View File

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { SparklesIcon, UserIcon, NewspaperIcon } from "lucide-vue-next"; import { SparklesIcon } from "lucide-vue-next";
const slug = "kEJjxKw"; const slug = "kEJjxKw";
// FOR THIS MODULE DO NOT USE THE ?APPNAME URL TYPE, IT WILL FALL AT ALL TIMES, I HAVE NO CLUE WHY IS BEHAVIOR HAPPENING RN? // FOR THIS MODULE DO NOT USE THE ?APPNAME URL TYPE, IT WILL FALL AT ALL TIMES, I HAVE NO CLUE WHY IS BEHAVIOR HAPPENING RN?
const { data, error, pending } = useFetch(`/api/news/get/lt/${slug.trim()}`); //demo URL const { data, error, pending } = useFetch(`/api/news/get/lt/${slug.trim()}`); //demo URL
@ -37,10 +37,8 @@ const aiSummary = async () => {
> >
<div class="flex flex-col"> <div class="flex flex-col">
<h2 class="text-3xl text-bold">{{ data.title }}</h2> <h2 class="text-3xl text-bold">{{ data.title }}</h2>
<span <span class="text-lg text-bold"
class="text-lg text-bold flex flex-row justify-center text-center align-center" >origin: {{ data.origin }} author: {{ data.author }}</span
><NewspaperIcon class="w-7 h-7 p-1" />{{ data.origin }}
<UserIcon class="w-7 h-7 p-1" />{{ data.author }}</span
> >
<div class="test-center" v-for="item in data.paragraph">{{ item }}</div> <div class="test-center" v-for="item in data.paragraph">{{ item }}</div>
</div> </div>

View File

@ -1,19 +0,0 @@
<script setup lang="ts">
import {
TooltipRoot,
type TooltipRootEmits,
type TooltipRootProps,
useForwardPropsEmits,
} from "reka-ui";
const props = defineProps<TooltipRootProps>();
const emits = defineEmits<TooltipRootEmits>();
const forwarded = useForwardPropsEmits(props, emits);
</script>
<template>
<TooltipRoot v-bind="forwarded">
<slot />
</TooltipRoot>
</template>

View File

@ -1,45 +0,0 @@
<script setup lang="ts">
import type { HTMLAttributes } from "vue";
import { reactiveOmit } from "@vueuse/core";
import {
TooltipContent,
type TooltipContentEmits,
type TooltipContentProps,
TooltipPortal,
useForwardPropsEmits,
} from "reka-ui";
import { cn } from "@/lib/utils";
defineOptions({
inheritAttrs: false,
});
const props = withDefaults(
defineProps<TooltipContentProps & { class?: HTMLAttributes["class"] }>(),
{
sideOffset: 4,
},
);
const emits = defineEmits<TooltipContentEmits>();
const delegatedProps = reactiveOmit(props, "class");
const forwarded = useForwardPropsEmits(delegatedProps, emits);
</script>
<template>
<TooltipPortal>
<TooltipContent
v-bind="{ ...forwarded, ...$attrs }"
:class="
cn(
'z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
props.class,
)
"
>
<slot />
</TooltipContent>
</TooltipPortal>
</template>

View File

@ -1,11 +0,0 @@
<script setup lang="ts">
import { TooltipProvider, type TooltipProviderProps } from "reka-ui";
const props = defineProps<TooltipProviderProps>();
</script>
<template>
<TooltipProvider v-bind="props">
<slot />
</TooltipProvider>
</template>

View File

@ -1,11 +0,0 @@
<script setup lang="ts">
import { TooltipTrigger, type TooltipTriggerProps } from "reka-ui";
const props = defineProps<TooltipTriggerProps>();
</script>
<template>
<TooltipTrigger v-bind="props">
<slot />
</TooltipTrigger>
</template>

View File

@ -1,4 +0,0 @@
export { default as Tooltip } from "./Tooltip.vue";
export { default as TooltipContent } from "./TooltipContent.vue";
export { default as TooltipProvider } from "./TooltipProvider.vue";
export { default as TooltipTrigger } from "./TooltipTrigger.vue";

View File

@ -17,11 +17,9 @@
"newsComparePlatform": "news comparison platform" "newsComparePlatform": "news comparison platform"
}, },
"startusing": "Let's Start!", "startusing": "Let's Start!",
"openapp": "This will open the desktop application in your browser.",
"learnmore": "Learn more", "learnmore": "Learn more",
"documentation": "Documentation", "documentation": "Documentation",
"tools": "Tools", "tools": "Tools",
"opentools": "This will open simple tools",
"qanda": { "qanda": {
"titles": { "titles": {
"whydes": "Why make this platform?", "whydes": "Why make this platform?",

View File

@ -17,11 +17,9 @@
"newsComparePlatform": "新聞觀點比對平台" "newsComparePlatform": "新聞觀點比對平台"
}, },
"startusing": "開始使用!", "startusing": "開始使用!",
"openapp": "會打開在瀏覽器的桌面程式",
"learnmore": "了解更多", "learnmore": "了解更多",
"documentation": "如何使用", "documentation": "如何使用",
"tools": "工具", "tools": "工具",
"opentools": "會打開工具的選單",
"qanda": { "qanda": {
"titles": { "titles": {
"whydes": "為什麼要做這個平台?", "whydes": "為什麼要做這個平台?",
@ -76,7 +74,7 @@
"aboutNewsOrg": "關於這個新聞來源", "aboutNewsOrg": "關於這個新聞來源",
"newsview": "新聞" "newsview": "新聞"
}, },
"tools": { "tools":{
"title": "工具", "title": "工具",
"name": { "name": {
"checkweirdkeywords": "檢查偏色情標體", "checkweirdkeywords": "檢查偏色情標體",

View File

@ -13,8 +13,7 @@
"start": "bun run .output/server/index.mjs", "start": "bun run .output/server/index.mjs",
"docs:dev": "vitepress dev docs", "docs:dev": "vitepress dev docs",
"docs:build": "vitepress build docs", "docs:build": "vitepress build docs",
"docs:preview": "vitepress preview docs", "docs:preview": "vitepress preview docs"
"wipedev": "./clean-dev-env.sh"
}, },
"dependencies": { "dependencies": {
"@fontsource-variable/noto-sans-tc": "^5.2.5", "@fontsource-variable/noto-sans-tc": "^5.2.5",
@ -30,7 +29,7 @@
"@sentry/nuxt": "^9", "@sentry/nuxt": "^9",
"@tailwindcss/vite": "^4.1.5", "@tailwindcss/vite": "^4.1.5",
"@uploadthing/nuxt": "^7.1.7", "@uploadthing/nuxt": "^7.1.7",
"@vueuse/core": "^13.2.0", "@vueuse/core": "^13.1.0",
"animate.css": "^4.1.1", "animate.css": "^4.1.1",
"argon2": "^0.43.0", "argon2": "^0.43.0",
"axios": "^1.9.0", "axios": "^1.9.0",

View File

@ -5,12 +5,6 @@ import {
AccordionItem, AccordionItem,
AccordionTrigger, AccordionTrigger,
} from "~/components/ui/accordion"; } from "~/components/ui/accordion";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { import {
ComputerDesktopIcon, ComputerDesktopIcon,
CircleStackIcon, CircleStackIcon,
@ -129,38 +123,20 @@ useSeoMeta({
></span ></span
></span> ></span>
<div class="flex flex-row justify-center align-center gap-0s"> <div class="flex flex-row justify-center align-center gap-0s">
<TooltipProvider> <NuxtLink :to="localePath('/desktop')">
<Tooltip> <button
<TooltipTrigger> class="m-4 mr-1 ml-1 bg-[#8C9393] text-white p-3 rounded-[10px] bg-gradient-to-l from-sky-500 to-purple-600 transition-all duration-150 hover:transform hover:scale-105 hover:shadow-lg"
<NuxtLink :to="localePath('/desktop')"> >
<button <span>{{ t("home.startusing") }}</span>
class="m-4 mr-1 ml-1 bg-[#8C9393] text-white p-3 rounded-[10px] bg-gradient-to-l from-sky-500 to-purple-600 transition-all duration-150 hover:transform hover:scale-105 hover:shadow-lg" </button>
> </NuxtLink>
<span>{{ t("home.startusing") }}</span> <NuxtLink :to="localePath('/tools/')">
</button> <button
</NuxtLink> class="m-4 ml-1 mr-1 bg-[#8C9393] text-white p-3 rounded-[10px] bg-gray-700 transition-all duration-150 hover:transform hover:scale-105 hover:shadow-lg"
</TooltipTrigger> >
<TooltipContent class="rounded"> <span>{{ t("home.tools") }}</span>
{{ t("home.openapp") }} </button>
</TooltipContent> </NuxtLink>
</Tooltip>
</TooltipProvider>
<TooltipProvider>
<Tooltip>
<TooltipTrigger>
<NuxtLink :to="localePath('/tools/')">
<button
class="m-4 ml-1 mr-1 bg-[#8C9393] text-white p-3 rounded-[10px] bg-gray-700 transition-all duration-150 hover:transform hover:scale-105 hover:shadow-lg"
>
<span>{{ t("home.tools") }}</span>
</button>
</NuxtLink>
</TooltipTrigger>
<TooltipContent class="rounded">
{{ t("home.opentools") }}
</TooltipContent>
</Tooltip>
</TooltipProvider>
<NuxtLink to="#learnmore"> <NuxtLink to="#learnmore">
<button <button
class="m-4 ml-1 mr-1 bg-[#8C9393] text-white p-3 rounded-[10px] bg-gray-700 transition-all duration-150 hover:transform hover:scale-105 hover:shadow-lg" class="m-4 ml-1 mr-1 bg-[#8C9393] text-white p-3 rounded-[10px] bg-gray-700 transition-all duration-150 hover:transform hover:scale-105 hover:shadow-lg"

View File

@ -2,7 +2,7 @@
const localePath = useLocalePath(); const localePath = useLocalePath();
// Import Icons // Import Icons
import { SearchXIcon, CircleSlash2Icon } from "lucide-vue-next"; import { SearchXIcon, CircleSlash2Icon } from "lucide-vue-next";
const { t } = useI18n(); const { t } = useI18n();
// Array // Array
const tools = [ const tools = [
{ {
@ -19,8 +19,8 @@ const tools = [
}, },
]; ];
useSeoMeta({ useSeoMeta({
title: `${t("tools.title")}`, title: `${t("tools.title")}`
}); })
</script> </script>
<template> <template>
<div <div

View File

@ -1,14 +1,14 @@
export default defineEventHandler((event) => { export default defineEventHandler((event) => {
const query = getQuery(event); const query = getQuery(event);
const toolCall = query.tool; const toolCall = query.tool;
const forwardCall = query.forward; const forwardCall = query.forward;
if (toolCall) { if (toolCall) {
const buildUrl = "/desktop?openapp=" + toolCall; const buildUrl = "/desktop?openapp=" + toolCall;
return sendRedirect(event, buildUrl, 302); return sendRedirect(event, buildUrl, 302);
} }
if (forwardCall) { if (forwardCall) {
const buildUrl = "/" + forwardCall; const buildUrl = "/" + forwardCall;
return sendRedirect(event, buildUrl, 302); return sendRedirect(event, buildUrl, 302);
} }
return sendRedirect(event, "/", 302); return sendRedirect(event, "/", 302)
}); })

View File

@ -1,25 +1,5 @@
import * as cheerio from "cheerio"; import * as cheerio from "cheerio";
function findTime(timeText: string) {
const now = new Date();
const hourMatch = timeText.match(/(\d+)小時前/);
const dayMatch = timeText.match(/(\d+)天前/);
const minuteMatch = timeText.match(/(\d+)分鐘前/);
if (hourMatch) {
const hoursAgo = parseInt(hourMatch[1]);
return new Date(now.getTime() - hoursAgo * 60 * 60 * 1000);
} else if (dayMatch) {
const daysAgo = parseInt(dayMatch[1]);
return new Date(now.getTime() - daysAgo * 24 * 60 * 60 * 1000);
} else if (minuteMatch) {
const minutesAgo = parseInt(minuteMatch[1]);
return new Date(now.getTime() - minutesAgo * 60 * 1000);
}
return null;
}
async function lineToday(slug: string) { async function lineToday(slug: string) {
const url = "https://today.line.me/tw/v2/article/" + slug; const url = "https://today.line.me/tw/v2/article/" + slug;
const fetchPageCode = await fetch(url, { const fetchPageCode = await fetch(url, {
@ -66,7 +46,6 @@ async function lineToday(slug: string) {
.text() .text()
.replaceAll("\n", "") .replaceAll("\n", "")
.replaceAll(" ", ""); .replaceAll(" ", "");
let author = ""; let author = "";
const authorInfo = html("span.entityPublishInfo-meta-info") const authorInfo = html("span.entityPublishInfo-meta-info")
.text() .text()
@ -78,26 +57,16 @@ async function lineToday(slug: string) {
} else { } else {
author = authorInfo; author = authorInfo;
} }
const orgAuthorDateData = html("span.entityPublishInfo-meta-info").text();
const updateMatch = orgAuthorDateData.match(/更新於\s*([^•]+)/);
const publishMatch = orgAuthorDateData.match(/發布於\s*(.+)$/);
let updatedAt: Date | null = null;
if (updateMatch) {
updatedAt = findTime(updateMatch[1].trim());
}
let publishedAt: Date | null = null;
if (publishMatch) {
publishedAt = findTime(publishMatch[1].trim());
}
return { return {
title: title, title: title,
paragraph: paragraph, paragraph: paragraph,
origin: newsOrgdir, origin: newsOrgdir,
author: author, author: author,
images: images, images: images,
updateat: updatedAt,
publishedat: publishedAt
}; };
} }
// Texting on console only!
//console.log(await lineToday("wJyR8Nw"));
export default lineToday; export default lineToday;