1 Commits

Author SHA1 Message Date
fzk 03e36ccb93 适配火狐浏览器 2024-11-20 09:12:54 +08:00
110 changed files with 1193 additions and 5144 deletions
+1 -5
View File
@@ -19,8 +19,6 @@ yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
pnpm-lock.yaml
# local env files
.env*.local
@@ -37,7 +35,5 @@ dist/
.idea
.vscode
pnpm-lock.yaml
.env
server
+17 -46
View File
@@ -1,12 +1,8 @@
# codebox-一键复制代码/下载文章
## codebox-一键复制代码/下载文章
[中文](README.md) | [English](README_EN.md)
## 代码
## 网站
1. 官网
https://www.code-box.fun/
2. 文档
https://code-box.fun/docs
https://github.com/027xiguapi/code-box
## 介绍
@@ -18,10 +14,10 @@ https://code-box.fun/docs
直接下载安装:
1. 进入应用商店
- [Chrome应用商店](https://chrome.google.com/webstore/category/extensions?hl=zh-CN)
- [Edge应用商店](https://microsoftedge.microsoft.com/addons/Microsoft-Edge-Extensions-Home?hl=zh-CN)
- [Chrome网上应用商店](https://chrome.google.com/webstore/category/extensions?hl=zh-CN)
- [Edge网上应用商店](https://microsoftedge.microsoft.com/addons/Microsoft-Edge-Extensions-Home?hl=zh-CN)
- [360浏览器应用商店](https://ext.se.360.cn/#/extension-detail?id=acnnhjllgegbndgknlliobjlekgilbdf)
- [Firefox应用商店](https://addons.mozilla.org/zh-CN/firefox/)
- [Firefox网上应用商店](https://addons.mozilla.org/zh-CN/firefox/)
2. 搜索:`codebox`
@@ -40,7 +36,7 @@ https://code-box.fun/docs
### 安装方式三:公众号下载
1. 关注公众号
![img](https://github.com/027xiguapi/code-box/blob/main/public/wx/gzh.jpg)
![img](https://raw.githubusercontent.com/027xiguapi/code-box/main/public/wx/qrcode_wx.jpg)
2. 点击 `软件下载`
@@ -71,28 +67,13 @@ https://code-box.fun/docs
### 自定义
- 插入自定义样式(css代码)
- 下载所有图片
- 自定义选择下载html
- 自定义选择下载markdown
- 自定义选择下载pdf
### 微信
- 插入自定义样式(css代码)
- 一键下载文章html
- 一键下载文章markdown
- 一键下载文章pdf
- 一键下载所有图片
### 掘金
- 插入自定义样式(css代码)
- 一键下载文章html
- 一键下载文章markdown
- 一键下载文章pdf
### CSDN
- 一键下载文章html、pdf或markdown文件
- 一键下载文章html或markdown文件
- 打开任意一个`CSDN`博客即可开始复制代码
- 未登录`CSDN`状态下,支持选中代码
- 未登录`CSDN`状态下,代码右上角按钮一键复制
@@ -101,61 +82,54 @@ https://code-box.fun/docs
- 自动展开代码块
- 移动端屏蔽跳转APP
- 非vip用户,不再提示vip观看,且完整展示文章
- 插入自定义样式(css代码)
### 知乎
- 一键下载文章html、pdf或markdown文件
- 一键下载文章html或markdown文件
- 一键复制代码
- 未登录`知乎`状态下,不再提示展开阅读全文,且完整展示文章
- 未登录`知乎`状态下,不会再出现强制登录弹窗
- 插入自定义样式(css代码)
### 百度
- 一键下载对话框html、pdf或markdown文件
- 一键下载对话框html或markdown文件
- 关闭AI对话框
### 简书
- 一键下载文章html、pdf或markdown文件
- 一键下载文章html或markdown文件
- 移动端,一键复制代码
- 不再提示展开阅读全文,且完整展示文章
- 不会再出现强制登录弹窗
- 插入自定义样式(css代码)
### 脚本之家
- 一键下载文章html、pdf或markdown文件
- 一键下载文章html或markdown文件
- 打开任意一个`脚本之家`博客即可开始复制代码
- 未登录`脚本之家`状态下,支持选中代码
- 屏蔽广告
- 移动端未登录`脚本之家`状态下,代码右上角按钮一键复制
- 插入自定义样式(css代码)
### 博客园
- 一键下载文章html、pdf或markdown文件
- 一键下载文章html或markdown文件
- 一键复制代码
- 插入自定义样式(css代码)
### 51CTO博客
- 一键下载文章html、pdf或markdown文件
- 一键下载文章html或markdown文件
- 未登录`51CTO博客`状态下,支持选中代码
- 未登录`51CTO博客`状态下,代码右上角按钮一键复制
- 未登录`51CTO博客`状态下,不会再出现强制登录弹窗
- 移动端未登录`51CTO博客`状态下,代码右上角按钮一键复制
- 插入自定义样式(css代码)
### php中文网
- 一键下载文章html、pdf或markdown文件
- 一键下载文章html或markdown文件
- 未登录`php中文网`状态下,支持选中代码
- 未登录`php中文网`状态下,代码右上角按钮一键复制
- 未登录`php中文网`状态下,不会再出现强制登录弹窗
- 未登录`php中文网`状态下,移动端代码右上角按钮一键复制
- 插入自定义样式(css代码)
![img](https://raw.githubusercontent.com/027xiguapi/code-box/main/public/8.png)
![img](https://raw.githubusercontent.com/027xiguapi/code-box/main/public/1.jpg)
@@ -175,14 +149,11 @@ https://code-box.fun/docs
3. [Chrome 应用商店一站式支持](https://support.google.com/chrome_webstore/contact/one_stop_support)
4. [开箱即用的web打印和下载](https://juejin.cn/post/7412672713376497727)
5. [导出微信公众号文章为PDF](https://greasyfork.org/en/scripts/510683-%E5%AF%BC%E5%87%BA%E5%BE%AE%E4%BF%A1%E5%85%AC%E4%BC%97%E5%8F%B7%E6%96%87%E7%AB%A0%E4%B8%BApdf/code)
6. [微信公众号推文图片一键下载](https://greasyfork.org/zh-CN/scripts/40583-%E5%BE%AE%E4%BF%A1%E5%85%AC%E4%BC%97%E5%8F%B7%E6%8E%A8%E6%96%87%E5%9B%BE%E7%89%87%E4%B8%80%E9%94%AE%E4%B8%8B%E8%BD%BD/code)
## 隐私政策
[网页](https://027xiguapi.github.io/code-box/privacy-policy.html)
## 联系
## 支持
![img](https://github.com/027xiguapi/code-box/blob/main/public/wx/gzh.jpg)
![u_wx](https://github.com/027xiguapi/code-box/blob/main/public/wx/u_wx.jpg)
![img](https://raw.githubusercontent.com/027xiguapi/code-box/main/public/wx/qrcode_wx.jpg)
-184
View File
@@ -1,184 +0,0 @@
# Codebox - One-click Code Copying/Downloading Articles
[中文](README.md) | [English](README_EN.md)
## official website
https://www.code-box.fun/
## Introduction
This browser plugin can be used on websites like CSDN/Zhihu/Script Home/Blog Garden/51CTO Blog/PHP Chinese Network/Juejin/WeChat, etc., to download articles as HTML or Markdown files with one click; to copy code without logging in; to support selected code; or to copy code with one click from the button in the upper right corner of the code; to remove the prompt to follow the blogger to read the full text; to remove the login pop-up window; to remove the pop-up window to jump to the APP.
## Installation
### Installation Method 1: Plugin Store (Recommended)
Directly download and install:
1. Enter the app store
- [Chrome Web Store](https://chrome.google.com/webstore/category/extensions?hl=zh-CN)
- [Edge Web Store](https://microsoftedge.microsoft.com/addons/Microsoft-Edge-Extensions-Home?hl=zh-CN)
- [360 Browser App Store](https://ext.se.360.cn/#/extension-detail?id=acnnhjllgegbndgknlliobjlekgilbdf)
- [Firefox Web Store](https://addons.mozilla.org/zh-CN/firefox/)
2. Search: `codebox`
![0f8137ce6861f2a387095aef446fe60](https://raw.githubusercontent.com/027xiguapi/code-box/main/public/webstore/0f8137ce6861f2a387095aef446fe60.png)
![img](https://raw.githubusercontent.com/027xiguapi/code-box/main/public/webstore/img.png)
![1724640161752](https://raw.githubusercontent.com/027xiguapi/code-box/main/public/webstore/1724640161752.jpg)
### Installation Method 2: Direct Installation
1. Installation address:
- [Go to Chrome Extension Store](https://chrome.google.com/webstore/detail/acnnhjllgegbndgknlliobjlekgilbdf)
- [Go to 360 Browser Extension Store](https://ext.se.360.cn/#/extension-detail?id=acnnhjllgegbndgknlliobjlekgilbdf)
- [Go to Firefox Extension Store](https://addons.mozilla.org/zh-CN/firefox/addon/code-box/)
- [Go to Edge Extension Store](https://microsoftedge.microsoft.com/addons/detail/code-box/cfpdbfmncaampihkmejogihjkenkonbn)
### Installation Method 3: Download from WeChat Official Account
1. Follow the WeChat official account
![img](https://raw.githubusercontent.com/027xiguapi/code-box/main/public/wx/qrcode_wx.jpg)
2. Click `Software Download`
![img](https://raw.githubusercontent.com/027xiguapi/code-box/main/public/wx/download.jpg)
### Installation Method 4: Source Code Installation
1. Clone the source code
```sh
git clone https://github.com/027xiguapi/code-box.git
```
2. Install and package
```sh
pnpm install
pnpm dev
pnpm build
```
3. For Google Chrome, you can enter the plugin management page from the upper right menu -> More tools -> Extensions, or you can directly enter chrome://extensions in the address bar to access it.
4. Check the developer mode, and you can directly load the plugin in the form of a folder.
## Function
![img-1](https://raw.githubusercontent.com/027xiguapi/code-box/main/public/config.jpg)
### Customization
- Insert custom styles (CSS code)
- Download all images
- Customize the selection to download HTML
- Customize the selection to download Markdown
- Customize the selection to download PDF
### WeChat
- Insert custom styles (CSS code)
- One-click download of article HTML
- One-click download of article Markdown
- One-click download of article PDF
- One-click download of all images
### Nuggets
- Insert custom styles (CSS code)
- One-click download of article HTML
- One-click download of article Markdown
- One-click download of article PDF
### CSDN
- One-click download of article HTML, PDF, or Markdown files
- Open any `CSDN` blog to start copying code
- Support for selected code when not logged in to `CSDN`
- One-click copy of code from the button in the upper right corner of the code when not logged in to `CSDN`
- No more forced login pop-ups when not logged in to `CSDN`
- No more prompts to follow the blogger to read the full text when not following the blogger, and the full text is displayed
- Automatically expand code blocks
- Block APP redirection on mobile
- For non-VIP users, no more prompts for VIP viewing, and the full text is displayed
- Insert custom styles (CSS code)
### Zhihu
- One-click download of article HTML, PDF, or Markdown files
- One-click copy of code
- No more prompts to expand and read the full text when not logged in to `Zhihu`, and the full text is displayed
- No more forced login pop-ups when not logged in to `Zhihu`
- Insert custom styles (CSS code)
### Baidu
- One-click download of dialog box HTML, PDF, or Markdown files
- Close AI dialog box
### Jian Shu
- One-click download of article HTML, PDF, or Markdown files
- One-click copy of code on mobile
- No more prompts to expand and read the full text, and the full text is displayed
- No more forced login pop-ups
- Insert custom styles (CSS code)
### Script Home
- One-click download of article HTML, PDF, or Markdown files
- Open any `Script Home` blog to start copying code
- Support for selected code when not logged in to `Script Home`
- Block ads
- One-click copy of code from the button in the upper right corner of the code on mobile when not logged in to `Script Home`
- Insert custom styles (CSS code)
### Blog Garden
- One-click download of article HTML, PDF, or Markdown files
- One-click copy of code
- Insert custom styles (CSS code)
### 51CTO Blog
- One-click download of article HTML, PDF, or Markdown files
- Support for selected code when not logged in to `51CTO Blog`
- One-click copy of code from the button in the upper right corner of the code when not logged in to `51CTO Blog`
- No more forced login pop-ups when not logged in to `51CTO Blog`
- One-click copy of code from the button in the upper right corner of the code on mobile when not logged in to `51CTO Blog`
- Insert custom styles (CSS code)
### PHP Chinese Network
- One-click download of article HTML, PDF, or Markdown files
- Support for selected code when not logged in to `PHP Chinese Network`
- One-click copy of code from the button in the upper right corner of the code when not logged in to `PHP Chinese Network`
- No more forced login pop-ups when not logged in to `PHP Chinese Network`
- One-click copy of code from the button in the upper right corner of the code on mobile when not logged in to `PHP Chinese Network`
- Insert custom styles (CSS code)
![img](https://raw.githubusercontent.com/027xiguapi/code-box/main/public/8.png)
![img](https://raw.githubusercontent.com/027xiguapi/code-box/main/public/1.jpg)
![img](https://raw.githubusercontent.com/027xiguapi/code-box/main/public/2.png)
![img](https://raw.githubusercontent.com/027xiguapi/code-box/main/public/3.jpg)
![img](https://raw.githubusercontent.com/027xiguapi/code-box/main/public/4.jpg)
![img](https://raw.githubusercontent.com/027xiguapi/code-box/main/public/5.jpg)
![img](https://raw.githubusercontent.com/027xiguapi/code-box/main/public/6.jpg)
![img](https://raw.githubusercontent.com/027xiguapi/code-box/main/public/7.jpg)
![img](https://raw.githubusercontent.com/027xiguapi/code-box/main/public/1723096379951.jpg)
## Reference
1. [copy-csdn](https://github.com/openHacking/copy-csdn)
2. [plasmo](https://github.com/PlasmoHQ/plasmo)
3. [Chrome App Store One-stop Support](https://support.google.com/chrome_webstore/contact/one_stop_support)
4. [Out-of-the-box web printing and downloading](https://juejin.cn/post/7412672713376497727)
5. [Export WeChat Official Account Articles to PDF](https://greasyfork.org/en/scripts/510683-%E5%AF%BC%E5%87%BA%E5%BE%AE%E4%BF%A1%E5%85%AC%E4%BC%97%E5%8F%B7%E6%96%87%E7%AB%A0%E4%B8%BApdf/code)
6. [One-click Download of WeChat Official Account Article Images](https://greasyfork.org/zh-CN/scripts/40583-%E5%BE%AE%E4%BF%A1%E5%85%AC%E4%BC%97%E5%8F%B7%E6%8E%A8%E6%96%87%E5%9B%BE%E7%89%87%E4%B8%80%E9%94%AE%E4%B8%8B%E8%BD%BD/code)
## Privacy Policy
[Webpage](https://027xiguapi.github.io/code-box/privacy-policy.html)
## Support
![img](https://raw.githubusercontent.com/027xiguapi/code-box/main/public/wx/qrcode_wx.jpg)
![img](https://github.com/027xiguapi/code-box/blob/main/public/wx/u_wx.jpg)
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

+33 -5
View File
@@ -13,14 +13,42 @@ chrome.runtime.onInstalled.addListener(function (object) {
chrome.runtime.requestUpdateCheck(function (status, details) {
if (status === "update_available") {
console.log("有更新可用!版本" + details.version)
// chrome.action.setBadgeBackgroundColor({ color: "#fc5430" })
// chrome.action.setBadgeText({
// text: "升级"
// })
// chrome.runtime.reload()
chrome.action.setBadgeBackgroundColor({ color: "#fc5430" })
chrome.action.setBadgeText({
text: "升级"
})
chrome.runtime.reload()
} else if (status === "no_update") {
console.log("没有可用更新。")
} else if (status === "throttled") {
console.log("更新检查被限流。")
}
})
chrome.tabs.onUpdated.addListener((tabId, _, tab) => {
// console.log(tabId, _, tab)
// if (!("sidePanel" in chrome)) {
// return
// }
//
// void (async () => {
// if (!tab.url) {
// return
// }
// // const url = new URL(tab.url)
// // if (
// // url.origin === ""
// // ) {
// // await chrome.sidePanel.setOptions({
// // tabId,
// // // path: "pages/options.html",
// // enabled: true
// // })
// // } else {
// // await chrome.sidePanel.setOptions({
// // tabId,
// // enabled: false
// // })
// // }
// })()
})
-52
View File
@@ -1,52 +0,0 @@
import JSZip from "jszip"
import type { PlasmoMessaging } from "@plasmohq/messaging"
const handler: PlasmoMessaging.MessageHandler = async (req, res) => {
const { imageUrls, title, action, onProgress } = req.body
if (action === "downloadAllImages") {
const zip = new JSZip()
const fetchImage = (url) => {
return fetch(url)
.then((response) => response.blob())
.then((blob) => {
const fileName = url.split("/").pop().split("?")[0]
return { blob, fileName }
})
}
Promise.all(imageUrls.map(fetchImage))
.then((results) => {
results.forEach(({ blob, fileName }, index) => {
onProgress && onProgress(index + 1, imageUrls.length)
zip.file(fileName || `image-${index}.jpg`, blob)
})
return zip.generateAsync({ type: "blob" })
})
.then((zipBlob) => {
const reader = new FileReader()
reader.onload = function (event) {
const dataUrl = event.target.result as string
chrome.downloads.download(
{
url: dataUrl,
filename: `${title}.zip`
},
() => {
res.send({ code: 200, msg: "success" })
}
)
}
reader.readAsDataURL(zipBlob)
})
.catch((err) => {
console.error(err)
res.send({ code: 0, msg: err })
})
}
}
export default handler
+5 -13
View File
@@ -1,17 +1,9 @@
import { type PlasmoMessaging } from "@plasmohq/messaging"
import type { PlasmoMessaging } from "@plasmohq/messaging"
const handler: PlasmoMessaging.MessageHandler = async (req, res) => {
try {
chrome.tabs.query({ active: true, currentWindow: true }, ([tab]) => {
const { active } = req.body
if (active) {
chrome.sidePanel.open({ tabId: tab.id })
}
})
} catch (error) {
console.error("Error toggling side panel:", error)
}
const handler: PlasmoMessaging.MessageHandler = (req, res) => {
chrome.tabs.query({ active: true, currentWindow: true }, ([tab]) => {
chrome.sidePanel.open({ tabId: tab.id })
})
}
export default handler
+40 -119
View File
@@ -1,57 +1,37 @@
import { CheckOutlined, CloseOutlined } from "@ant-design/icons"
import { KeyOutlined } from "@ant-design/icons"
import { Button, Input, Space } from "antd"
import dayjs from "dayjs"
import qrcodeUrl from "raw:~/public/wx/gzh.jpg"
import React, { useRef, useState } from "react"
import qrcodeUrl from "raw:~/public/wx/qrcode_wx.jpg"
import { useRef, useState } from "react"
import { sendToBackground } from "@plasmohq/messaging"
import { useStorage } from "@plasmohq/storage/dist/hook"
const validateContent = {
blueButton: {
backgroundColor: "#1677FF",
border: "1px solid #1677FF",
color: "#fff",
borderRadius: "4px",
padding: "7px 15px",
fontSize: "14px"
},
redButton: {
backgroundColor: "#d9363e",
border: "1px solid #d9363e",
color: "#fff",
borderRadius: "4px",
padding: "7px 15px",
fontSize: "14px"
}
}
import { verifyTOTP } from "~utils/2FA"
export default function ValidateContent(props) {
const [validTime, setValidTime] = useStorage("app-validTime", "1730390400")
const [activationCode, setActivationCode] = useState("")
const [isValid, setIsValid] = useState(true)
const handleChange = (event) => {
const code = event.target.value
setActivationCode(code)
}
const [isValid, setIsValid] = useState(false)
const inputRef = useRef(null)
function handleSubmit() {
if (Number(validTime) > dayjs().unix()) {
props.handleOk()
} else if (process.env.PLASMO_PUBLIC_CODEBOX_SECRET3 == activationCode) {
let code = inputRef.current.input.value
if (verifyTOTP(process.env.PLASMO_PUBLIC_CODEBOX_SECRET1, code)) {
let time = dayjs().add(20, "day").unix()
setValidTime(String(time))
setIsValid(true)
} else if (verifyTOTP(process.env.PLASMO_PUBLIC_CODEBOX_SECRET2, code)) {
let time = dayjs().add(65, "day").unix()
setValidTime(String(time))
setIsValid(true)
} else if (process.env.PLASMO_PUBLIC_CODEBOX_SECRET3 == code) {
let time = dayjs().add(7, "day").unix()
setValidTime(String(time))
setIsValid(true)
props.handleOk()
} else {
setIsValid(false)
}
}
function handleCancel() {
props.handleCancel()
}
function help() {
sendToBackground({
name: "tab",
@@ -61,98 +41,39 @@ export default function ValidateContent(props) {
})
}
const typeMap = {
downloadHtml: "下载HTML",
downloadMarkdown: "下载markdown",
editMarkdown: "编辑markdown",
parseMarkdown: "解析markdown",
downloadPdf: "下载PDF"
}
return (
<div
style={{
width: "350px",
padding: "10px",
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
backgroundColor: "white",
borderRadius: "5px",
textAlign: "center"
}}>
{Number(validTime) > dayjs().unix() ? (
<>
{isValid ? (
<p className="valid" style={{ fontSize: "16px", color: "red" }}>
已激活!确认{typeMap[props.type]}?
验证成功
</p>
) : (
<>
<div style={{ fontSize: "14px", color: "red" }}>
此功能需要关注公众号
<p>
【codebox代码助手】获取验证码(
<a onClick={help} target="_blank">
帮助
</a>
)
</p>
此功能需要关注公众号【codebox代码助手】获取验证码(
<a onClick={help} target="_blank">
帮助
</a>
)
</div>
<div>
<img style={{ margin: "auto" }} src={qrcodeUrl} alt="微信公众号" />
<img src={qrcodeUrl} alt="微信公众号" />
</div>
<div
style={{
marginTop: "10px",
display: "flex",
justifyContent: "center",
alignItems: "center"
}}>
<label
htmlFor="activationCodeInput"
style={{ display: "block", marginRight: "7px" }}>
验证码:
</label>
<input
type="text"
id="activationCodeInput"
value={activationCode}
onChange={handleChange}
style={{
padding: "8px",
borderRadius: "4px",
border: isValid ? "1px solid #ccc" : "1px solid red",
width: "200px",
fontSize: "14px"
}}
placeholder="请输入验证码"
<Space.Compact style={{ width: "100%" }}>
<Input
placeholder="输入激活码"
ref={inputRef}
prefix={<KeyOutlined />}
/>
</div>
{!isValid ? (
<div style={{ color: "red", fontSize: "14px", marginTop: "5px" }}>
验证码错误
</div>
) : (
<></>
)}
<Button
type="primary"
className="valid-submit"
onClick={handleSubmit}>
提交
</Button>
</Space.Compact>
</>
)}
<div
style={{
margin: "10px 50px 0",
display: "flex",
justifyContent: "space-between"
}}>
<button
className="valid-submit"
style={validateContent.blueButton}
onClick={handleSubmit}>
<CheckOutlined /> 提交
</button>
<button style={validateContent.redButton} onClick={handleCancel}>
<CloseOutlined /> 取消
</button>
</div>
</div>
</>
)
}
-58
View File
@@ -1,58 +0,0 @@
import { DownloadOutlined, StarTwoTone } from "@ant-design/icons"
import { useState } from "react"
import { sendToContentScript } from "@plasmohq/messaging"
import { i18n } from "~tools"
export default function DownloadImages({ name }) {
const [isDownloading, setIsDownloading] = useState(false)
const [progress, setProgress] = useState(0)
const handleDownload = async () => {
if (isDownloading) return
try {
setIsDownloading(true)
setProgress(0)
const res = await sendToContentScript({
name: `${name}-downloadImages`,
body: {
onProgress: (current, total) => {
const percentage = Math.round((current / total) * 100)
setProgress(percentage)
}
}
})
} catch (error) {
console.error("Failed to download images:", error)
} finally {
setIsDownloading(false)
setProgress(0)
}
}
return (
<div
className={`item download ${isDownloading ? "downloading" : ""}`}
onClick={handleDownload}
style={{
opacity: isDownloading ? 0.7 : 1,
cursor: isDownloading ? "not-allowed" : "pointer"
}}>
<span>
<StarTwoTone twoToneColor="#eb2f96" style={{ marginRight: "5px" }} />
{isDownloading
? `${i18n("downloading")} (${progress}%)`
: i18n("downloadAllImg")}
</span>
<DownloadOutlined
style={{
color: isDownloading ? "#999" : "#52c41a",
fontSize: "16px"
}}
/>
</div>
)
}
-23
View File
@@ -1,23 +0,0 @@
import CssCode from "~component/items/cssCode"
import DownloadHtml from "~component/items/downloadHtml"
import DownloadMarkdown from "~component/items/downloadMarkdown"
import DownloadPdf from "~component/items/downloadPdf"
import EditMarkdown from "~component/items/editMarkdown"
import ShowTag from "~component/items/showTag"
import { i18n } from "~tools"
export default function Options(props) {
let { name } = props
return (
<fieldset>
<legend>{i18n(name + "Config")}</legend>
<CssCode name={name}></CssCode>
<ShowTag name={name}></ShowTag>
<EditMarkdown name={name}></EditMarkdown>
<DownloadMarkdown name={name}></DownloadMarkdown>
<DownloadHtml name={name}></DownloadHtml>
<DownloadPdf name={name}></DownloadPdf>
</fieldset>
)
}
-25
View File
@@ -1,25 +0,0 @@
import { EditOutlined, StarTwoTone } from "@ant-design/icons"
import { sendToContentScript } from "@plasmohq/messaging"
import { i18n } from "~tools"
export default function ParseMarkdown(props) {
let { name } = props
async function parseMarkdown() {
sendToContentScript({
name: `${name}-parseMarkdown`
})
}
return (
<div className="item download" onClick={parseMarkdown}>
<span>
<StarTwoTone twoToneColor="#eb2f96" style={{ marginRight: "5px" }} />
{i18n("parseMarkdown")}
</span>
<EditOutlined style={{ color: "#52c41a", fontSize: "16px" }} />
</div>
)
}
-30
View File
@@ -1,30 +0,0 @@
import { EditOutlined, StarTwoTone } from "@ant-design/icons"
import { sendToContentScript } from "@plasmohq/messaging"
import { useStorage } from "@plasmohq/storage/dist/hook"
import { i18n } from "~tools"
export default function SetAiType(props) {
const [aiType, setAiType] = useStorage("app-aiType", (v) =>
v === undefined ? "chatgpt" : v
)
return (
<div className="item">
<span>
<StarTwoTone twoToneColor="#eb2f96" style={{ marginRight: "5px" }} />
选择AI
</span>
<select
name="custom-aiType"
id="custom-aiType"
value={aiType}
onChange={(e) => setAiType(e.target.value)}>
<option value="deepseek">deepseek</option>
<option value="chatgpt">chatGPT</option>
<option value="kimi">kimi</option>
</select>
</div>
)
}
-5
View File
@@ -1,5 +0,0 @@
import Options from "~component/items/options"
export default function Doc360() {
return <Options name="360doc" />
}
-2
View File
@@ -5,7 +5,6 @@ import { useStorage } from "@plasmohq/storage/hook"
import CssCode from "~component/items/cssCode"
import DownloadHtml from "~component/items/downloadHtml"
import DownloadMarkdown from "~component/items/downloadMarkdown"
import DownloadPdf from "~component/items/downloadPdf"
import EditMarkdown from "~component/items/editMarkdown"
import ShowTag from "~component/items/showTag"
import { i18n } from "~tools"
@@ -62,7 +61,6 @@ export default function Cto51({ forwardRef }) {
<EditMarkdown name="51cto"></EditMarkdown>
<DownloadMarkdown name="51cto"></DownloadMarkdown>
<DownloadHtml name="51cto"></DownloadHtml>
<DownloadPdf name="51cto"></DownloadPdf>
</fieldset>
)
}
-4
View File
@@ -5,9 +5,7 @@ import { useStorage } from "@plasmohq/storage/hook"
import CssCode from "~component/items/cssCode"
import DownloadHtml from "~component/items/downloadHtml"
import DownloadMarkdown from "~component/items/downloadMarkdown"
import DownloadPdf from "~component/items/downloadPdf"
import EditMarkdown from "~component/items/editMarkdown"
import ShowTag from "~component/items/showTag"
import { i18n } from "~tools"
export default function Baidu({ forwardRef }) {
@@ -39,11 +37,9 @@ export default function Baidu({ forwardRef }) {
<label className="codebox-switch" htmlFor="baidu-closeAIBox"></label>
</div>
<CssCode name="baidu"></CssCode>
<ShowTag name="baidu"></ShowTag>
<EditMarkdown name="baidu"></EditMarkdown>
<DownloadMarkdown name="baidu"></DownloadMarkdown>
<DownloadHtml name="baidu"></DownloadHtml>
<DownloadPdf name="baidu"></DownloadPdf>
</fieldset>
)
}
-4
View File
@@ -5,9 +5,7 @@ import { useStorage } from "@plasmohq/storage/hook"
import CssCode from "~component/items/cssCode"
import DownloadHtml from "~component/items/downloadHtml"
import DownloadMarkdown from "~component/items/downloadMarkdown"
import DownloadPdf from "~component/items/downloadPdf"
import EditMarkdown from "~component/items/editMarkdown"
import ShowTag from "~component/items/showTag"
import { i18n } from "~tools"
export default function Cnblogs({ forwardRef }) {
@@ -39,12 +37,10 @@ export default function Cnblogs({ forwardRef }) {
<label className="codebox-switch" htmlFor="cnblogs-copyCode"></label>
</div>
<CssCode name="cnblogs"></CssCode>
<ShowTag name="cnblogs"></ShowTag>
<EditMarkdown name="cnblogs"></EditMarkdown>
<DownloadMarkdown name="cnblogs"></DownloadMarkdown>
<DownloadHtml name="cnblogs"></DownloadHtml>
<CssCode name="cnblogs"></CssCode>
<DownloadPdf name="cnblogs"></DownloadPdf>
</fieldset>
)
}
+3 -41
View File
@@ -1,11 +1,9 @@
import { BookOutlined, DownloadOutlined, StarTwoTone } from "@ant-design/icons"
import { DownloadOutlined, StarTwoTone } from "@ant-design/icons"
import { useImperativeHandle } from "react"
import { sendToBackground, sendToContentScript } from "@plasmohq/messaging"
import { useStorage } from "@plasmohq/storage/hook"
import DownloadImages from "~component/items/downloadImages"
import SetAiType from "~component/items/setAiType"
import { i18n } from "~tools"
export default function Config({ forwardRef }) {
@@ -15,28 +13,13 @@ export default function Config({ forwardRef }) {
const [closeLog, setCloseLog] = useStorage("config-closeLog", (v) =>
v === undefined ? true : v
)
const [allShowTag, setAllShowTag] = useStorage("config-allShowTag", (v) =>
v === undefined ? true : v
)
function downloadFullImg() {
async function downloadImg() {
sendToContentScript({
name: "app-full-page-screenshot"
})
}
function getSummary() {
sendToContentScript({
name: "app-get-summary"
})
sendToBackground({
name: "sidepanel",
body: {
active: true
}
})
}
function handleReset() {
setCopyCode(true)
setCloseLog(true)
@@ -49,7 +32,6 @@ export default function Config({ forwardRef }) {
return (
<fieldset>
<legend>{i18n("AppConfig")}</legend>
<SetAiType />
<div className="item">
<span>{i18n("copyCode")}</span>
<input
@@ -62,18 +44,6 @@ export default function Config({ forwardRef }) {
/>
<label className="codebox-switch" htmlFor="config-copyCode"></label>
</div>
<div className="item">
<span>{i18n("allShowTag")}</span>
<input
type="checkbox"
id="allShowTag"
name="allShowTag"
className="codebox-offscreen"
checked={allShowTag}
onChange={(e) => setAllShowTag(e.target.checked)}
/>
<label htmlFor="allShowTag" className="codebox-switch"></label>
</div>
<div className="item">
<span>{i18n("configCloseLog")}</span>
<input
@@ -86,21 +56,13 @@ export default function Config({ forwardRef }) {
/>
<label className="codebox-switch" htmlFor="config-closeLog"></label>
</div>
<DownloadImages name="app"></DownloadImages>
<div className="item download" onClick={downloadFullImg}>
<div className="item download" onClick={downloadImg}>
<span>
<StarTwoTone twoToneColor="#eb2f96" style={{ marginRight: "5px" }} />
{i18n("fullPageScreenshot")}
</span>
<DownloadOutlined style={{ color: "#52c41a", fontSize: "16px" }} />
</div>
<div className="item download" onClick={getSummary}>
<span>
<StarTwoTone twoToneColor="#eb2f96" style={{ marginRight: "5px" }} />
总结文章
</span>
<BookOutlined style={{ color: "#52c41a", fontSize: "16px" }} />
</div>
</fieldset>
)
}
+47 -85
View File
@@ -1,7 +1,6 @@
import React, { useEffect, useRef, useState } from "react"
import React, { useEffect, useState } from "react"
import Cto51 from "~component/options/51cto"
import Doc360 from "~component/options/360doc"
import Baidu from "~component/options/baidu"
import Cnblogs from "~component/options/cnblogs"
import Config from "~component/options/config"
@@ -10,106 +9,69 @@ import Custom from "~component/options/custom"
import Jb51 from "~component/options/jb51"
import Jianshu from "~component/options/jianshu"
import Juejin from "~component/options/juejin"
import Lougu from "~component/options/lougu"
import Medium from "~component/options/medium"
import Mp from "~component/options/mp"
import Oschina from "~component/options/oschina"
import Paywallbuster from "~component/options/paywallbuster"
import Php from "~component/options/php"
import Pianshen from "~component/options/pianshen"
import Segmentfault from "~component/options/segmentfault"
import Weixin from "~component/options/weixin"
import Zhihu from "~component/options/zhihu"
let csdnIsShow = false
let zhihuIsShow = false
let baiduIsShow = false
let jianshuIsShow = false
let jb51IsShow = false
let cnblogsIsShow = false
let ctoIsShow = false
let juejinIsShow = false
let phpIsShow = false
let oschinaIsShow = false
let segmentfaultIsShow = false
let mediumIsShow = false
let paywallbusterIsShow = false
let doc360IsShow = false
let mpIsShow = false
let weixinIsShow = false
let pianshenIsShow = false
let louguIsShow = false
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
const currentTab = tabs[0]
if (currentTab) {
const url = new URL(currentTab.url)
const { hostname, pathname } = url
csdnIsShow = hostname.includes("csdn")
zhihuIsShow = hostname.includes("zhihu")
baiduIsShow = hostname.includes("baidu")
jianshuIsShow = hostname.includes("jianshu")
jb51IsShow = hostname.includes("jb51")
cnblogsIsShow = hostname.includes("cnblogs")
ctoIsShow = hostname.includes("51cto")
juejinIsShow = hostname.includes("juejin")
phpIsShow = hostname.includes("php")
oschinaIsShow = hostname.includes("oschina")
segmentfaultIsShow = hostname.includes("segmentfault")
mediumIsShow = hostname.includes("medium")
paywallbusterIsShow = hostname.includes("paywallbuster")
doc360IsShow = hostname.includes("360doc")
pianshenIsShow = hostname.includes("pianshen")
louguIsShow = hostname.includes("lougu")
if (hostname.includes("weixin")) {
if (pathname.includes("cgi-bin")) {
mpIsShow = true
} else {
weixinIsShow = true
}
}
}
})
export default function Content() {
const csdnRef = useRef<any>()
const zhihuRef = useRef<any>()
const baiduRef = useRef<any>()
const juejinRef = useRef<any>()
const oschinaRef = useRef<any>()
const jianshuRef = useRef<any>()
const jb51Ref = useRef<any>()
const cnblogsRef = useRef<any>()
const cto51Ref = useRef<any>()
const phpRef = useRef<any>()
const segmentfaultRef = useRef<any>()
const weixinRef = useRef<any>()
const customRef = useRef<any>()
const appRef = useRef<any>()
const [csdnIsShow, setCsdnIsShow] = useState<boolean>(false)
const [zhihuIsShow, setZhihuIsShow] = useState<boolean>(false)
const [baiduIsShow, setBaiduIsShow] = useState<boolean>(false)
const [jianshuIsShow, setJianshuIsShow] = useState<boolean>(false)
const [jb51IsShow, setJb51IsShow] = useState<boolean>(false)
const [cnblogsIsShow, setCnblogsShow] = useState<boolean>(false)
const [ctoIsShow, set51ctoIsShow] = useState<boolean>(false)
const [juejinIsShow, setJuejinIsShow] = useState<boolean>(false)
const [phpIsShow, setPhpIsShow] = useState<boolean>(false)
const [oschinaIsShow, setOschinaIsShow] = useState<boolean>(false)
const [segmentfaultIsShow, setSegmentfaultIsShow] = useState<boolean>(false)
const [weixinIsShow, setWeixinIsShow] = useState<boolean>(false)
const [mediumIsShow, setMediumIsShow] = useState<boolean>(false)
useEffect(() => {
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
const currentTab = tabs[0]
if (currentTab) {
const url = new URL(currentTab.url)
const hostname = url.hostname
hostname.includes("csdn") && setCsdnIsShow(true)
hostname.includes("zhihu") && setZhihuIsShow(true)
hostname.includes("baidu") && setBaiduIsShow(true)
hostname.includes("jianshu") && setJianshuIsShow(true)
hostname.includes("jb51") && setJb51IsShow(true)
hostname.includes("cnblogs") && setCnblogsShow(true)
hostname.includes("51cto") && set51ctoIsShow(true)
hostname.includes("juejin") && setJuejinIsShow(true)
hostname.includes("php") && setPhpIsShow(true)
hostname.includes("oschina") && setOschinaIsShow(true)
hostname.includes("segmentfault") && setSegmentfaultIsShow(true)
hostname.includes("weixin") && setWeixinIsShow(true)
hostname.includes("medium") && setMediumIsShow(true)
}
})
}, [])
return (
<div className="content">
{csdnIsShow ? <Csdn forwardRef={csdnRef} /> : <></>}
{zhihuIsShow ? <Zhihu forwardRef={zhihuRef} /> : <></>}
{baiduIsShow ? <Baidu forwardRef={baiduRef} /> : <></>}
{jianshuIsShow ? <Jianshu forwardRef={jianshuRef} /> : <></>}
{jb51IsShow ? <Jb51 forwardRef={jb51Ref} /> : <></>}
{cnblogsIsShow ? <Cnblogs forwardRef={cnblogsRef} /> : <></>}
{ctoIsShow ? <Cto51 forwardRef={cto51Ref} /> : <></>}
{csdnIsShow ? <Csdn /> : <></>}
{zhihuIsShow ? <Zhihu /> : <></>}
{baiduIsShow ? <Baidu /> : <></>}
{jianshuIsShow ? <Jianshu /> : <></>}
{jb51IsShow ? <Jb51 /> : <></>}
{cnblogsIsShow ? <Cnblogs /> : <></>}
{ctoIsShow ? <Cto51 /> : <></>}
{juejinIsShow ? <Juejin /> : <></>}
{phpIsShow ? <Php forwardRef={phpRef} /> : <></>}
{phpIsShow ? <Php /> : <></>}
{oschinaIsShow ? <Oschina /> : <></>}
{segmentfaultIsShow ? <Segmentfault /> : <></>}
{weixinIsShow ? <Weixin /> : <></>}
{mediumIsShow ? <Medium /> : <></>}
{mpIsShow ? <Mp /> : <></>}
{paywallbusterIsShow ? <Paywallbuster /> : <></>}
{doc360IsShow ? <Doc360 /> : <></>}
{pianshenIsShow ? <Pianshen /> : <></>}
{louguIsShow ? <Lougu /> : <></>}
<Custom />
<Config forwardRef={customRef} />
<Config />
</div>
)
}
-3
View File
@@ -5,7 +5,6 @@ import { useStorage } from "@plasmohq/storage/hook"
import CssCode from "~component/items/cssCode"
import DownloadHtml from "~component/items/downloadHtml"
import DownloadMarkdown from "~component/items/downloadMarkdown"
import DownloadPdf from "~component/items/downloadPdf"
import EditMarkdown from "~component/items/editMarkdown"
import ShowTag from "~component/items/showTag"
import { i18n } from "~tools"
@@ -142,13 +141,11 @@ export default function Csdn({ forwardRef }) {
htmlFor="csdn-closeRedirectModal"
className="codebox-switch"></label>
</div>
<ShowTag name="csdn"></ShowTag>
<CssCode name="csdn"></CssCode>
<ShowTag name="csdn"></ShowTag>
<EditMarkdown name="csdn"></EditMarkdown>
<DownloadMarkdown name="csdn"></DownloadMarkdown>
<DownloadHtml name="csdn"></DownloadHtml>
<DownloadPdf name="csdn"></DownloadPdf>
</fieldset>
)
}
+3 -31
View File
@@ -5,45 +5,17 @@ import { sendToContentScript } from "@plasmohq/messaging"
import CssCode from "~component/items/cssCode"
import DownloadHtml from "~component/items/downloadHtml"
import DownloadMarkdown from "~component/items/downloadMarkdown"
import DownloadPdf from "~component/items/downloadPdf"
import EditMarkdown from "~component/items/editMarkdown"
import ParseMarkdown from "~component/items/parseMarkdown"
import { i18n } from "~tools"
export default function Custom() {
function makerQRPost() {
sendToContentScript({
name: "app-makerQRPost"
})
}
function getArticle() {
sendToContentScript({
name: "app-getArticle"
})
}
return (
<fieldset>
<legend>{i18n("customConfig")}</legend>
<CssCode name="custom"></CssCode>
<ParseMarkdown name="custom" />
<EditMarkdown name="custom" />
<DownloadMarkdown name="custom" />
<DownloadHtml name="custom" />
<DownloadPdf name="custom" />
<div className="item download" onClick={makerQRPost}>
<span>
<StarTwoTone twoToneColor="#eb2f96" style={{ marginRight: "5px" }} />
{i18n("makerQRPost")}
</span>
<DownloadOutlined style={{ color: "#52c41a", fontSize: "16px" }} />
</div>
<div className="item download" onClick={getArticle}>
<span>
<StarTwoTone twoToneColor="#eb2f96" style={{ marginRight: "5px" }} />
{i18n("getArticle")}
</span>
<DownloadOutlined style={{ color: "#52c41a", fontSize: "16px" }} />
</div>
<EditMarkdown name="custom"></EditMarkdown>
<DownloadMarkdown name="custom"></DownloadMarkdown>
<DownloadHtml name="custom"></DownloadHtml>
</fieldset>
)
}
-4
View File
@@ -5,9 +5,7 @@ import { useStorage } from "@plasmohq/storage/hook"
import CssCode from "~component/items/cssCode"
import DownloadHtml from "~component/items/downloadHtml"
import DownloadMarkdown from "~component/items/downloadMarkdown"
import DownloadPdf from "~component/items/downloadPdf"
import EditMarkdown from "~component/items/editMarkdown"
import ShowTag from "~component/items/showTag"
import { i18n } from "~tools"
export default function Jb51({ forwardRef }) {
@@ -54,12 +52,10 @@ export default function Jb51({ forwardRef }) {
/>
<label className="codebox-switch" htmlFor="jb51-copyCode"></label>
</div>
<ShowTag name="jb51"></ShowTag>
<CssCode name="jb51"></CssCode>
<EditMarkdown name="jb51"></EditMarkdown>
<DownloadMarkdown name="jb51"></DownloadMarkdown>
<DownloadHtml name="jb51"></DownloadHtml>
<DownloadPdf name="jb51"></DownloadPdf>
</fieldset>
)
}
-4
View File
@@ -5,9 +5,7 @@ import { useStorage } from "@plasmohq/storage/hook"
import CssCode from "~component/items/cssCode"
import DownloadHtml from "~component/items/downloadHtml"
import DownloadMarkdown from "~component/items/downloadMarkdown"
import DownloadPdf from "~component/items/downloadPdf"
import EditMarkdown from "~component/items/editMarkdown"
import ShowTag from "~component/items/showTag"
import { i18n } from "~tools"
export default function Jianshu({ forwardRef }) {
@@ -76,12 +74,10 @@ export default function Jianshu({ forwardRef }) {
className="codebox-switch"
htmlFor="jianshu-autoOpenCode"></label>
</div>
<ShowTag name="jianshu"></ShowTag>
<CssCode name="jianshu"></CssCode>
<EditMarkdown name="jianshu"></EditMarkdown>
<DownloadMarkdown name="jianshu"></DownloadMarkdown>
<DownloadHtml name="jianshu"></DownloadHtml>
<DownloadPdf name="jianshu"></DownloadPdf>
</fieldset>
)
}
+16 -2
View File
@@ -1,5 +1,19 @@
import Options from "~component/items/options"
import CssCode from "~component/items/cssCode"
import DownloadHtml from "~component/items/downloadHtml"
import DownloadMarkdown from "~component/items/downloadMarkdown"
import EditMarkdown from "~component/items/editMarkdown"
import ShowTag from "~component/items/showTag"
import { i18n } from "~tools"
export default function Juejin() {
return <Options name="juejin" />
return (
<fieldset>
<legend>{i18n("juejinConfig")}</legend>
<CssCode name="juejin"></CssCode>
<ShowTag name="juejin"></ShowTag>
<EditMarkdown name="juejin"></EditMarkdown>
<DownloadMarkdown name="juejin"></DownloadMarkdown>
<DownloadHtml name="juejin"></DownloadHtml>
</fieldset>
)
}
-5
View File
@@ -1,5 +0,0 @@
import Options from "~component/items/options"
export default function Lougu() {
return <Options name="lougu" />
}
+16 -2
View File
@@ -1,5 +1,19 @@
import Options from "~component/items/options"
import CssCode from "~component/items/cssCode"
import DownloadHtml from "~component/items/downloadHtml"
import DownloadMarkdown from "~component/items/downloadMarkdown"
import EditMarkdown from "~component/items/editMarkdown"
import ShowTag from "~component/items/showTag"
import { i18n } from "~tools"
export default function Medium() {
return <Options name="medium" />
return (
<fieldset>
<legend>{i18n("mediumConfig")}</legend>
<CssCode name="medium"></CssCode>
<ShowTag name="medium"></ShowTag>
<EditMarkdown name="medium"></EditMarkdown>
<DownloadMarkdown name="medium"></DownloadMarkdown>
<DownloadHtml name="medium"></DownloadHtml>
</fieldset>
)
}
-26
View File
@@ -1,26 +0,0 @@
import { BookOutlined, StarTwoTone } from "@ant-design/icons"
import { sendToContentScript } from "@plasmohq/messaging"
import { i18n } from "~tools"
export default function Weixin() {
async function getThumbMedia() {
sendToContentScript({
name: `mp-getMediaResources`
})
}
return (
<fieldset>
<legend>{i18n("weixinConfig")}</legend>
<div className="item download" onClick={getThumbMedia}>
<span>
<StarTwoTone twoToneColor="#eb2f96" style={{ marginRight: "5px" }} />
{i18n("getMediaResources")}
</span>
<BookOutlined style={{ color: "#52c41a", fontSize: "16px" }} />
</div>
</fieldset>
)
}
+14 -2
View File
@@ -1,5 +1,17 @@
import Options from "~component/items/options"
import CssCode from "~component/items/cssCode"
import DownloadHtml from "~component/items/downloadHtml"
import DownloadMarkdown from "~component/items/downloadMarkdown"
import EditMarkdown from "~component/items/editMarkdown"
import { i18n } from "~tools"
export default function Oschina() {
return <Options name="oschina" />
return (
<fieldset>
<legend>{i18n("oschinaConfig")}</legend>
<CssCode name="oschina"></CssCode>
<EditMarkdown name="oschina"></EditMarkdown>
<DownloadMarkdown name="oschina"></DownloadMarkdown>
<DownloadHtml name="oschina"></DownloadHtml>
</fieldset>
)
}
-5
View File
@@ -1,5 +0,0 @@
import Options from "~component/items/options"
export default function Paywallbuster() {
return <Options name="paywallbuster" />
}
+5 -8
View File
@@ -5,9 +5,7 @@ import { useStorage } from "@plasmohq/storage/hook"
import CssCode from "~component/items/cssCode"
import DownloadHtml from "~component/items/downloadHtml"
import DownloadMarkdown from "~component/items/downloadMarkdown"
import DownloadPdf from "~component/items/downloadPdf"
import EditMarkdown from "~component/items/editMarkdown"
import ShowTag from "~component/items/showTag"
import { i18n } from "~tools"
export default function Php({ forwardRef }) {
@@ -55,12 +53,11 @@ export default function Php({ forwardRef }) {
/>
<label htmlFor="php-closeLoginModal" className="codebox-switch"></label>
</div>
<CssCode name="php" />
<ShowTag name="php" />
<EditMarkdown name="php" />
<DownloadMarkdown name="php" />
<DownloadHtml name="php" />
<DownloadPdf name="php" />
<CssCode name="php"></CssCode>
<EditMarkdown name="php"></EditMarkdown>
<DownloadMarkdown name="php"></DownloadMarkdown>
<DownloadHtml name="php"></DownloadHtml>
<CssCode name="php"></CssCode>
</fieldset>
)
}
-5
View File
@@ -1,5 +0,0 @@
import Options from "~component/items/options"
export default function Pianshen() {
return <Options name="pianshen" />
}
+14 -2
View File
@@ -1,5 +1,17 @@
import Options from "~component/items/options"
import CssCode from "~component/items/cssCode"
import DownloadHtml from "~component/items/downloadHtml"
import DownloadMarkdown from "~component/items/downloadMarkdown"
import EditMarkdown from "~component/items/editMarkdown"
import { i18n } from "~tools"
export default function Segmentfault() {
return <Options name="segmentfault" />
return (
<fieldset>
<legend>{i18n("segmentfaultConfig")}</legend>
<CssCode name="segmentfault"></CssCode>
<EditMarkdown name="segmentfault"></EditMarkdown>
<DownloadMarkdown name="segmentfault"></DownloadMarkdown>
<DownloadHtml name="segmentfault"></DownloadHtml>
</fieldset>
)
}
+6 -25
View File
@@ -1,10 +1,5 @@
import { DownloadOutlined, StarTwoTone } from "@ant-design/icons"
import { sendToContentScript } from "@plasmohq/messaging"
import CssCode from "~component/items/cssCode"
import DownloadHtml from "~component/items/downloadHtml"
import DownloadImages from "~component/items/downloadImages"
import DownloadMarkdown from "~component/items/downloadMarkdown"
import DownloadPdf from "~component/items/downloadPdf"
import EditMarkdown from "~component/items/editMarkdown"
@@ -12,29 +7,15 @@ import ShowTag from "~component/items/showTag"
import { i18n } from "~tools"
export default function Weixin() {
async function getThumbMedia() {
sendToContentScript({
name: `weixin-getThumbMedia`
})
}
return (
<fieldset>
<legend>{i18n("weixinConfig")}</legend>
<CssCode name="weixin" />
<ShowTag name="weixin" />
<EditMarkdown name="weixin" />
<DownloadMarkdown name="weixin" />
<DownloadHtml name="weixin" />
<DownloadPdf name="weixin" />
<DownloadImages name="weixin" />
<div className="item download" onClick={getThumbMedia}>
<span>
<StarTwoTone twoToneColor="#eb2f96" style={{ marginRight: "5px" }} />
{i18n("getThumbMedia")}
</span>
<DownloadOutlined style={{ color: "#52c41a", fontSize: "16px" }} />
</div>
<CssCode name="weixin"></CssCode>
<ShowTag name="weixin"></ShowTag>
<EditMarkdown name="weixin"></EditMarkdown>
<DownloadMarkdown name="weixin"></DownloadMarkdown>
<DownloadHtml name="weixin"></DownloadHtml>
<DownloadPdf name="weixin"></DownloadPdf>
</fieldset>
)
}
+4 -8
View File
@@ -5,9 +5,7 @@ import { useStorage } from "@plasmohq/storage/hook"
import CssCode from "~component/items/cssCode"
import DownloadHtml from "~component/items/downloadHtml"
import DownloadMarkdown from "~component/items/downloadMarkdown"
import DownloadPdf from "~component/items/downloadPdf"
import EditMarkdown from "~component/items/editMarkdown"
import ShowTag from "~component/items/showTag"
import { i18n } from "~tools"
export default function Zhihu({ forwardRef }) {
@@ -74,12 +72,10 @@ export default function Zhihu({ forwardRef }) {
/>
<label className="codebox-switch" htmlFor="zhihu-autoOpenCode"></label>
</div>
<CssCode name="zhihu" />
<ShowTag name="zhihu" />
<EditMarkdown name="zhihu" />
<DownloadMarkdown name="zhihu" />
<DownloadHtml name="zhihu" />
<DownloadPdf name="zhihu" />
<CssCode name="zhihu"></CssCode>
<EditMarkdown name="zhihu"></EditMarkdown>
<DownloadMarkdown name="weixin"></DownloadMarkdown>
<DownloadHtml name="weixin"></DownloadHtml>
</fieldset>
)
}
-11
View File
@@ -1,11 +0,0 @@
export default function TagBtnStyle() {
const style = document.createElement("style")
style.textContent = `
.codebox-tagBtn .btn:hover {
color: #fff;
background: #1e80ff;
border-radius: 3px;
}
`
return style
}
-176
View File
@@ -1,176 +0,0 @@
import imageSrc from "raw:~/public/wx/xcx.jpg"
import React, { useEffect, useState } from "react"
import { TOTP } from "~utils/totp"
interface QRCodeModalProps {
onConfirm: () => void
onClose: () => void
}
const styles = {
overlay: {
position: "fixed" as const,
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: "rgba(0, 0, 0, 0.5)",
display: "flex",
justifyContent: "center",
alignItems: "center",
zIndex: 1000
},
content: {
backgroundColor: "white",
padding: "2rem",
borderRadius: "8px",
position: "relative" as const,
maxWidth: "90%",
boxShadow: "0 2px 10px rgba(0, 0, 0, 0.1)"
},
closeButton: {
position: "absolute" as const,
top: "10px",
right: "10px",
background: "none",
border: "none",
fontSize: "18px",
cursor: "pointer",
padding: "0.5rem"
},
qrcodeBox: {
width: "100%",
textAlign: "center" as const
},
qrcodeImage: {
maxWidth: "260px",
width: "100%",
height: "auto",
margin: "0px auto"
},
scanTip: {
textAlign: "center" as const,
color: "#666"
},
captchaSection: {
marginTop: "1.5rem",
borderTop: "1px solid #eee",
paddingTop: "1.5rem"
},
captchaRow: {
display: "flex",
justifyContent: "space-between",
alignItems: "center",
marginBottom: "1rem"
},
captchaText: {
fontSize: "16px",
letterSpacing: "2px",
color: "#333"
},
inputGroup: {
display: "flex",
gap: "0.5rem"
},
input: {
flex: 1,
padding: "0.8rem",
border: "1px solid #ddd",
borderRadius: "4px",
fontSize: "14px",
outline: "none",
"&:focus": {
borderColor: "#007bff"
}
},
verifyButton: {
padding: "0.8rem 1.5rem",
backgroundColor: "#007bff",
color: "white",
border: "none",
borderRadius: "4px",
cursor: "pointer",
"&:hover": {
backgroundColor: "#0056b3"
}
},
errorText: {
color: "#ff4444",
fontSize: "14px",
marginTop: "6px"
}
}
export default function QRCodeModal({ onClose, onConfirm }: QRCodeModalProps) {
const [inputCode, setInputCode] = useState<string>("")
const [isValid, setIsValid] = useState<boolean | null>(null)
const handleVerify = () => {
const formattedInput = inputCode.replace(/\s/g, "")
const secretKey = process.env.PLASMO_PUBLIC_CODEBOX_SECRET4
if (
formattedInput.length == 6 &&
TOTP.verifyTOTP(secretKey, formattedInput)
) {
setIsValid(true)
onConfirm()
} else if (process.env.PLASMO_PUBLIC_CODEBOX_SECRET3 == formattedInput) {
setIsValid(true)
onConfirm()
} else {
setIsValid(false)
}
}
// 在原有return中添加:
return (
<div style={styles.overlay}>
<div style={styles.content}>
<button style={styles.closeButton} onClick={onClose} aria-label="Close">
×
</button>
{imageSrc && (
<div style={styles.qrcodeBox}>
<img
src={imageSrc}
alt="WeChat QR Code"
style={styles.qrcodeImage}
/>
<div style={styles.scanTip}>扫描二维码获取验证码</div>
</div>
)}
{/* 新增验证码区域 */}
<div style={styles.captchaSection}>
<div style={styles.inputGroup}>
<input
type="text"
value={inputCode}
onChange={(e) => {
// 只允许数字和空格
const val = e.target.value.replace(/[^\d\s]/g, "")
setInputCode(val)
setIsValid(null) // 清除验证状态
}}
placeholder="请输入验证码"
style={{
...styles.input,
borderColor: isValid === false ? "#ff4444" : "#ddd"
}}
maxLength={11} // 6数字+5空格
/>
<button style={styles.verifyButton} onClick={handleVerify}>
验证
</button>
</div>
{isValid === false && (
<div style={styles.errorText}>验证码错误,请重新输入</div>
)}
</div>
</div>
</div>
)
}
-213
View File
@@ -1,213 +0,0 @@
import React, { useEffect, useRef, useState } from "react"
import { useMessage } from "@plasmohq/messaging/hook"
import { useStorage } from "@plasmohq/storage/dist/hook"
import ValidModal from "~component/ui/validModal"
import { addCss, saveHtml, saveMarkdown } from "~tools"
import { useEditMarkdown } from "~utils/editMarkdownHook"
import { useParseMarkdown } from "~utils/parseMarkdownHook"
import { Print } from "~utils/print"
import Turndown from "~utils/turndown"
import Tooltip from "../ui/tooltip"
const turndownService = Turndown()
export default function CustomDomSelector() {
const [aiType, setAiType] = useStorage("app-aiType")
const [position, setPosition] = useState({ top: 0, left: 0 })
const [isReady, setIsReady] = useState(false)
const [isSelect, setIsSelect] = useState(false)
const [isModalOpen, setIsModalOpen] = useState(false)
const downloadType = useRef("")
const [editContent, setEditContent] = useEditMarkdown()
const [parseContent, setParseContent] = useParseMarkdown()
const articleTitle = document
.querySelector<HTMLElement>("head title")
?.innerText.trim()
useEffect(() => {
if (aiType) {
addCss(`.codebox-current { outline: 2px solid #42b88350 !important; }`)
if (isReady) {
addEventListeners()
} else {
removeEventListeners()
}
}
return () => {
removeEventListeners()
}
}, [aiType, isReady, isSelect])
const addEventListeners = () => {
document.addEventListener("mousemove", handleMouseMove)
document.addEventListener("click", handleClick)
}
const removeEventListeners = () => {
document.removeEventListener("mousemove", handleMouseMove)
document.removeEventListener("click", handleClick)
}
const handleMouseMove = (event: MouseEvent) => {
if (isReady && !isSelect) {
const target = event.target as HTMLElement
highlightElement(target)
}
}
const handleClick = (event: MouseEvent) => {
if (isReady) {
const target = event.target as HTMLElement
const tooltip = target.closest(".codebox-tooltip")
const modal = target.closest(".codebox-modal")
const csui = target.closest("#codebox-csui")
if (!tooltip && !modal && !csui) {
setIsSelect(true)
highlightElement(target)
updateTooltipPosition(target)
event.stopPropagation()
event.preventDefault()
}
}
}
const highlightElement = (element: HTMLElement) => {
removeHighlight()
element.classList.add("codebox-current")
}
const removeHighlight = () => {
const currentList = document.querySelectorAll(".codebox-current")
currentList.forEach((el) => el.classList.remove("codebox-current"))
}
const updateTooltipPosition = (element: HTMLElement) => {
const rect = element.getBoundingClientRect()
const distanceTop = rect.top + window.scrollY
const distanceLeft = rect.left + window.scrollX
const top =
distanceTop < 50 ? distanceTop + rect.height + 5 : distanceTop - 40
const left = distanceLeft + 5
setPosition({ top: top, left: left })
window.scrollTo({ top: top - 150, behavior: "smooth" })
}
useMessage(async (req: any, res: any) => {
const name = req.name
const isApp = name.split("-")[0] == "app"
console.log(isApp)
if (name && !isApp) {
const type = name.split("-")[1]
setCustom(type)
}
})
const setCustom = (type: string) => {
downloadType.current = type
setIsReady(true)
setIsSelect(false)
}
const handleOkModal = () => {
const selector = document.querySelector<HTMLElement>(".codebox-current")
if (!selector || !downloadType.current) return
selector.classList.remove("codebox-current")
switch (downloadType.current) {
case "downloadHtml":
saveHtml(selector, articleTitle)
break
case "downloadMarkdown":
const markdown = turndownService.turndown(selector)
saveMarkdown(markdown, articleTitle)
break
case "editMarkdown":
setEditContent(selector)
break
case "parseMarkdown":
setParseContent(selector, aiType)
break
case "downloadPdf":
Print.print(selector, { title: articleTitle })
.then(() => console.log("Printing complete"))
.catch((error) => console.error("Printing failed:", error))
break
}
resetState()
setTimeout(() => {
setIsModalOpen(false)
}, 1000)
}
const handleConfirm = () => {
setIsModalOpen(true)
}
const handleCancel = () => {
resetState()
}
const resetState = () => {
removeHighlight()
setIsSelect(false)
setIsReady(false)
}
const navigateElement = (direction: "parent" | "child" | "prev" | "next") => {
const selector = document.querySelector(".codebox-current")
if (!selector) return
let newElement: HTMLElement | null = null
switch (direction) {
case "parent":
newElement = selector.parentElement
break
case "child":
newElement = selector.firstElementChild as HTMLElement
break
case "prev":
newElement = selector.previousElementSibling as HTMLElement
break
case "next":
newElement = selector.nextElementSibling as HTMLElement
break
}
if (newElement) {
highlightElement(newElement)
updateTooltipPosition(newElement)
}
}
const handleCancelModal = () => {
setIsModalOpen(false)
}
return (
<>
{isSelect && (
<Tooltip
left={position.left}
top={position.top}
onConfirm={handleConfirm}
onCancel={handleCancel}
onNavigate={navigateElement}
/>
)}
{isModalOpen && (
<ValidModal
onClose={handleCancelModal}
onConfirm={handleOkModal}
type={downloadType.current}
/>
)}
</>
)
}
-68
View File
@@ -1,68 +0,0 @@
import React from "react"
import ValidateContent from "~component/contents/validateContent"
const Modal = ({ isOpen, onClose, onConfirm, message }) => {
if (!isOpen) return null
const modalOverlayStyle = {
position: "fixed",
top: 0,
left: 0,
right: 0,
bottom: 0,
background: "rgba(0, 0, 0, 0.5)",
display: "flex",
justifyContent: "center",
alignItems: "center",
zIndex: 1000
}
const modalStyle = {
background: "white",
padding: "20px",
borderRadius: "8px",
boxShadow: "0 4px 8px rgba(0, 0, 0, 0.1)",
textAlign: "center"
}
const modalButtonsStyle = {
marginTop: "20px"
}
const buttonStyle = {
borderRadius: "4px",
padding: "2px 7px",
margin: "0 10px",
fontSize: "16px",
cursor: "pointer"
}
const cancelBtnStyle = {
...buttonStyle,
backgroundColor: "#d9363e",
border: "1px solid #d9363e",
color: "#fff"
}
const confirmBtnStyle = {
...buttonStyle,
backgroundColor: "#1677FF",
border: "1px solid #1677FF",
color: "#fff"
}
return (
<div style={modalOverlayStyle as React.CSSProperties}>
<div style={modalStyle as React.CSSProperties}>
<ValidateContent
type="parseMarkdown"
handleOk={onConfirm}
handleCancel={onClose}
/>
</div>
</div>
)
}
export default Modal
-66
View File
@@ -1,66 +0,0 @@
import { CloseOutlined } from "@ant-design/icons"
import React, { useState } from "react"
import { i18n } from "~tools"
const tagsStyles = {
tags: {
height: "28px",
display: "flex",
cursor: "pointer",
alignItems: "center",
color: "#1e80ff",
width: "160px",
backgroundColor: "rgb(255, 255, 255, .3)",
borderRadius: "5px",
justifyContent: "space-between",
padding: "0 8px",
marginTop: "-20px",
fontSize: "14px"
}
}
export default function Tags(props: any) {
const [isShowTag, setIsShowTag] = useState(true)
const handleEdit = () => {
props.onEdit()
}
const handleDownload = () => {
props.onDownload()
}
const handleParse = () => {
props.onParse()
}
const handlePrint = () => {
props.onPrint()
}
const handleClose = () => {
setIsShowTag(false)
}
return isShowTag ? (
<div className="codebox-tagBtn" style={tagsStyles.tags}>
<div onClick={handleEdit} className="btn">
{i18n("edit")}
</div>
<div onClick={handleDownload} className="btn">
{i18n("download")}
</div>
<div onClick={handlePrint} className="btn">
{i18n("print")}
</div>
<div onClick={handleParse} className="btn">
{i18n("parse")}
</div>
<div onClick={handleClose} className="btn">
<CloseOutlined />
</div>
</div>
) : (
<></>
)
}
-105
View File
@@ -1,105 +0,0 @@
import qrcodeUrl from "raw:~/public/wx/gzh.jpg"
import React, { useState } from "react"
import { i18n } from "~tools"
import { editQRCodeImg } from "~utils/makerQRPost"
const boxStyles = {
box: {
position: "fixed" as const,
border: "1px solid #D9DADC",
left: "25px",
top: "85px",
width: "140px",
padding: "16px",
cursor: "pointer"
},
close: {
position: "absolute" as const,
top: "-5px",
right: "0px",
background: "none",
border: "none",
fontSize: "20px",
cursor: "pointer",
padding: "10px"
},
img: {
width: "100%"
},
item: {
color: "#000000",
fontSize: "16px",
marginBottom: "3px"
}
}
export default function ToolBox(props: any) {
const [isShowTag, setIsShowTag] = useState(true)
const handleGetDescription = () => {
props.onGetDescription()
}
const handleEditMarkdown = () => {
props.onEditMarkdown()
}
const handleDownloadMarkdown = () => {
props.onDownloadMarkdown()
}
const handleParseMarkdown = () => {
props.onParseMarkdown()
}
const handlePrint = () => {
props.onPrint()
}
const getArticle = () => {
const href = encodeURIComponent(location.href)
window.open(`https://paywallbuster.com/articles/?article=${href}`)
}
const handleClose = () => {
setIsShowTag(false)
}
return isShowTag ? (
<div
id="ws_cmbm"
className="ws_cmbmc"
style={{ ...boxStyles.box, ...props.style }}>
<button style={boxStyles.close} onClick={handleClose} aria-label="Close">
×
</button>
<img src={qrcodeUrl} alt="qrcodeUrl" style={boxStyles.img} />
<div style={boxStyles.item}>
<a onClick={handleGetDescription}>{i18n("getDescription")}</a>
</div>
<div style={boxStyles.item}>
<a onClick={handleEditMarkdown}>{i18n("editMarkdown")}</a>
</div>
<div style={boxStyles.item}>
<a onClick={handleDownloadMarkdown}>{i18n("downloadMarkdown")}</a>
</div>
<div style={boxStyles.item}>
<a onClick={handleParseMarkdown}>{i18n("parseMarkdown")}</a>
</div>
<div style={boxStyles.item}>
<a onClick={handlePrint}>{i18n("downloadPdf")}</a>
</div>
<div style={boxStyles.item}>
<a onClick={() => editQRCodeImg()}>{i18n("makerQRPost")}</a>
</div>
<div style={boxStyles.item}>
<a onClick={() => getArticle()}>{i18n("getArticle")}</a>
</div>
<a style={boxStyles.item} href="https://www.code-box.fun" target="_blank">
帮助
</a>
</div>
) : (
<></>
)
}
-98
View File
@@ -1,98 +0,0 @@
import {
CheckOutlined,
CloseOutlined,
DownSquareOutlined,
LeftSquareOutlined,
RightSquareOutlined,
UpSquareOutlined
} from "@ant-design/icons"
import React from "react"
const tooltipStyles = {
tooltip: {
position: "absolute" as const,
zIndex: "2147483641",
backgroundColor: "#fff",
border: "1px solid #eee",
borderRadius: "5px",
padding: "8px",
boxShadow: "0 2px 8px rgba(0, 0, 0, 0.15)",
width: "450px",
display: "flex",
justifyContent: "space-between"
},
button: {
backgroundColor: "#fff",
border: "1px solid #eee",
color: "#000",
borderRadius: "4px",
padding: "2px 7px",
fontSize: "14px",
cursor: "pointer"
},
blueButton: {
backgroundColor: "#1677FF",
border: "1px solid #1677FF",
color: "#fff",
borderRadius: "4px",
padding: "2px 7px",
fontSize: "14px"
},
redButton: {
backgroundColor: "#d9363e",
border: "1px solid #d9363e",
color: "#fff",
borderRadius: "4px",
padding: "2px 7px",
fontSize: "14px"
}
}
export default function Tooltip(props: any) {
const handleConfirm = () => {
props.onConfirm()
}
const navigateElement = (direction: "parent" | "child" | "prev" | "next") => {
props.onNavigate(direction)
}
const handleCancel = () => {
props.onCancel()
}
const setTooltipStyle = () => {
return { ...tooltipStyles.tooltip, left: props.left, top: props.top }
}
return (
<div style={setTooltipStyle()} className="codebox-tooltip">
<button style={tooltipStyles.blueButton} onClick={handleConfirm}>
<CheckOutlined /> 确定
</button>
<button
style={tooltipStyles.button}
onClick={() => navigateElement("parent")}>
<UpSquareOutlined /> 父节点
</button>
<button
style={tooltipStyles.button}
onClick={() => navigateElement("child")}>
<DownSquareOutlined /> 子节点
</button>
<button
style={tooltipStyles.button}
onClick={() => navigateElement("prev")}>
<LeftSquareOutlined /> 上一个
</button>
<button
style={tooltipStyles.button}
onClick={() => navigateElement("next")}>
<RightSquareOutlined /> 下一个
</button>
<button style={tooltipStyles.redButton} onClick={handleCancel}>
<CloseOutlined /> 取消
</button>
</div>
)
}
-227
View File
@@ -1,227 +0,0 @@
import dayjs from "dayjs"
import qrcodeUrl from "raw:~/public/wx/gzh.jpg"
import React, { useEffect, useRef, useState } from "react"
import { sendToBackground } from "@plasmohq/messaging"
import { useStorage } from "@plasmohq/storage/dist/hook"
const styles = {
overlay: {
position: "fixed" as const,
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: "rgba(0, 0, 0, 0.5)",
display: "flex",
justifyContent: "center",
alignItems: "center",
zIndex: 1000
},
container: {
width: "350px",
padding: "2rem",
position: "absolute" as const,
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
backgroundColor: "white",
borderRadius: "5px",
textAlign: "center" as const,
boxShadow: "0 2px 10px rgba(0, 0, 0, 0.1)"
},
closeButton: {
position: "absolute" as const,
top: "10px",
right: "10px",
background: "none",
border: "none",
fontSize: "18px",
cursor: "pointer",
padding: "0.5rem"
},
activatedText: {
fontSize: "16px",
color: "red"
},
noticeText: {
fontSize: "14px",
color: "red"
},
helpLink: {
cursor: "pointer"
},
qrcodeImage: {
margin: "auto"
},
inputContainer: {
marginTop: "10px",
display: "flex",
justifyContent: "center",
alignItems: "center"
},
label: {
display: "block",
marginRight: "7px"
},
captchaSection: {
marginTop: "1.5rem",
borderTop: "1px solid #eee",
paddingTop: "1.5rem"
},
captchaRow: {
display: "flex",
justifyContent: "space-between",
alignItems: "center",
marginBottom: "1rem"
},
captchaText: {
fontSize: "16px",
letterSpacing: "2px",
color: "#333"
},
inputGroup: {
display: "flex",
gap: "0.5rem"
},
input: {
flex: 1,
padding: "0.8rem",
border: "1px solid #ddd",
borderRadius: "4px",
fontSize: "16px",
outline: "none",
"&:focus": {
borderColor: "#007bff"
}
},
verifyButton: {
padding: "0.8rem 1.5rem",
backgroundColor: "#007bff",
color: "white",
border: "none",
borderRadius: "4px",
cursor: "pointer",
"&:hover": {
backgroundColor: "#0056b3"
}
},
errorText: {
color: "#ff4444",
fontSize: "16px",
marginTop: "0.5rem"
}
}
export default function ValidModal(props) {
const [validTime, setValidTime] = useStorage("app-validTime", "1730390400")
const [inputCode, setInputCode] = useState<string>("")
const [isValid, setIsValid] = useState(true)
useEffect(() => {
if (Number(validTime) > dayjs().unix()) {
setTimeout(() => {
handleSubmit()
}, 1000)
}
}, [validTime])
function handleSubmit() {
if (Number(validTime) > dayjs().unix()) {
props.onConfirm()
} else if (process.env.PLASMO_PUBLIC_CODEBOX_SECRET3 == inputCode) {
let time = dayjs().add(3, "day").unix()
setValidTime(String(time))
setIsValid(true)
props.onConfirm()
} else {
setIsValid(false)
}
}
function handleCancel() {
props.onClose()
}
function help() {
sendToBackground({
name: "tab",
body: {
url: "feed.html"
}
})
}
const typeMap = {
downloadHtml: "下载HTML",
downloadMarkdown: "下载markdown",
editMarkdown: "编辑markdown",
parseMarkdown: "解析markdown",
downloadPdf: "下载PDF"
}
return (
<div style={styles.overlay} className="codebox-modal">
<div style={styles.container}>
<button
style={styles.closeButton}
onClick={handleCancel}
aria-label="Close">
×
</button>
{Number(validTime) > dayjs().unix() ? (
<>
<p style={styles.activatedText}>
已激活!正在{typeMap[props.type]}...
</p>
</>
) : (
<>
<div style={styles.noticeText}>
此功能需要关注公众号
<p>
【codebox代码助手】获取验证码(
<a onClick={help} target="_blank" style={styles.helpLink}>
帮助
</a>
)
</p>
</div>
<div>
<img
style={styles.qrcodeImage}
src={qrcodeUrl}
alt="微信公众号"
/>
</div>
<div style={styles.captchaSection}>
<div style={styles.inputGroup}>
<input
type="text"
value={inputCode}
onChange={(e) => {
const val = e.target.value.replace(/[^\d\s]/g, "")
setInputCode(val)
setIsValid(null) // 清除验证状态
}}
placeholder="请输入验证码"
style={{
...styles.input,
borderColor: isValid === false ? "#ff4444" : "#ddd"
}}
maxLength={11} // 6数字+5空格
/>
<button style={styles.verifyButton} onClick={handleSubmit}>
验证
</button>
</div>
{isValid === false && (
<div style={styles.errorText}>验证码错误,请重新输入</div>
)}
</div>
</>
)}
</div>
</div>
)
}
-105
View File
@@ -1,105 +0,0 @@
import type {
PlasmoCSConfig,
PlasmoCSUIProps,
PlasmoGetShadowHostId
} from "plasmo"
import React, { type FC } from "react"
import { useMessage } from "@plasmohq/messaging/hook"
import { useStorage } from "@plasmohq/storage/dist/hook"
import ToolBox from "~component/ui/toolBox"
import { saveHtml, saveMarkdown } from "~tools"
import useCssCodeHook from "~utils/cssCodeHook"
import { useEditMarkdown } from "~utils/editMarkdownHook"
import { useParseMarkdown } from "~utils/parseMarkdownHook"
import { Print } from "~utils/print"
import Turndown from "~utils/turndown"
export const config: PlasmoCSConfig = {
matches: ["https://*.360doc.com/content/*", "http://*.360doc.com/content/*"]
}
const turndownService = Turndown()
const articleTitle = document
.querySelector<HTMLElement>("head title")
.innerText.trim()
export const getShadowHostId: PlasmoGetShadowHostId = () => "codebox-360doc"
const PlasmoOverlay: FC<PlasmoCSUIProps> = () => {
const [parseContent, setParseContent] = useParseMarkdown()
const [allShowTag, setAllShowTag] = useStorage("config-allShowTag", true)
const [showTag, setShowTag] = useStorage<boolean>("360doc-showTag", true)
const [cssCode, runCss] = useCssCodeHook("360doc")
const [content, setContent] = useEditMarkdown()
useMessage(async (req, res) => {
if (req.name == "360doc-isShow") {
res.send({ isShow: true })
}
if (req.name == "360doc-editMarkdown") {
editMarkdown()
}
if (req.name == "360doc-downloadMarkdown") {
downloadMarkdown()
}
if (req.name == "360doc-downloadHtml") {
downloadHtml()
}
if (req.name == "360doc-downloadPdf") {
downloadPdf()
}
})
function getDescription() {
const summary = document.querySelector<HTMLMetaElement>(
'meta[name="description"]'
).content
summary && prompt("文章摘要:", summary)
}
function downloadPdf() {
const article = document.querySelector<HTMLElement>("#bgchange")
if (article) {
Print.print(article, { title: articleTitle })
.then(() => console.log("Printing complete"))
.catch((error) => console.error("Printing failed:", error))
}
}
function editMarkdown() {
const dom = document.querySelector("#bgchange")
setContent(dom, articleTitle)
}
function downloadMarkdown() {
const html = document.querySelector("#bgchange")
const markdown = turndownService.turndown(html)
saveMarkdown(markdown, articleTitle)
}
function downloadHtml() {
const dom = document.querySelector("#bgchange")
saveHtml(dom, articleTitle)
}
function parseMarkdown() {
const dom = document.querySelector("#bgchange")
setParseContent(dom)
}
return showTag && allShowTag ? (
<ToolBox
onGetDescription={getDescription}
onEditMarkdown={editMarkdown}
onDownloadMarkdown={downloadMarkdown}
onPrint={downloadPdf}
onParseMarkdown={parseMarkdown}
/>
) : (
<></>
)
}
export default PlasmoOverlay
+42 -41
View File
@@ -11,13 +11,9 @@ import { v4 as uuidv4 } from "uuid"
import { useMessage } from "@plasmohq/messaging/hook"
import { useStorage } from "@plasmohq/storage/hook"
import TagBtnStyle from "~component/tagBtn/style"
import Tags from "~component/ui/tags"
import { addCss, i18n, saveHtml, saveMarkdown } from "~tools"
import useCssCodeHook from "~utils/cssCodeHook"
import { useEditMarkdown } from "~utils/editMarkdownHook"
import { useParseMarkdown } from "~utils/parseMarkdownHook"
import { Print } from "~utils/print"
import { useContent } from "~utils/editMarkdownHook"
import Turndown from "~utils/turndown"
export const config: PlasmoCSConfig = {
@@ -25,9 +21,7 @@ export const config: PlasmoCSConfig = {
}
const turndownService = Turndown()
const articleTitle = document
.querySelector<HTMLElement>("head title")
.innerText.trim()
const articleTitle = document.querySelector<HTMLElement>("head title").innerText
const HOST_ID = "codebox-51cto"
export const getShadowHostId: PlasmoGetShadowHostId = () => HOST_ID
@@ -35,18 +29,35 @@ export const getShadowHostId: PlasmoGetShadowHostId = () => HOST_ID
export const getOverlayAnchor: PlasmoGetOverlayAnchor = async () =>
document.querySelector("article .title")
export const getStyle: PlasmoGetStyle = () => TagBtnStyle()
export const getStyle: PlasmoGetStyle = () => {
const style = document.createElement("style")
style.textContent = `
.codebox-tagBtn {
height: 28px;
display: flex;
cursor: pointer;
align-items: center;
color: #1e80ff;
width: 60px;
background: #fff;
border-radius: 5px;
justify-content: space-between;
padding: 0 8px;
margin-top: -20px;
font-size: 14px;
}
`
return style
}
const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
const [parseContent, setParseContent] = useParseMarkdown()
const [showTag, setShowTag] = useStorage<boolean>("51cto-showTag", true)
const [allShowTag, setAllShowTag] = useStorage("config-allShowTag", true)
const [showTag, setShowTag] = useStorage<boolean>("51cto-showTag")
const [cssCode, runCss] = useCssCodeHook("51cto")
const [copyCode] = useStorage<boolean>("51cto-copyCode")
const [closeLoginModal] = useStorage<boolean>("51cto-closeLoginModal")
const [history, setHistory] = useStorage<any[]>("codebox-history")
const [closeLog] = useStorage("config-closeLog", true)
const [content, setContent] = useEditMarkdown()
const [content, setContent] = useContent()
useEffect(() => {
closeLog || console.log("51CTO status", { closeLoginModal, copyCode })
@@ -61,7 +72,7 @@ const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
res.send({ isShow: true })
}
if (req.name == "51cto-editMarkdown") {
editMarkdown()
setContent("article")
}
if (req.name == "51cto-downloadMarkdown") {
downloadMarkdown()
@@ -69,9 +80,6 @@ const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
if (req.name == "51cto-downloadHtml") {
downloadHtml()
}
if (req.name == "51cto-downloadPdf") {
downloadPdf()
}
})
/* 未登录复制代码 */
@@ -221,43 +229,36 @@ const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
addCss(css)
}
function downloadPdf() {
var article = document.querySelector<HTMLElement>("article .article-detail")
if (article) {
Print.print(article, { title: articleTitle })
.then(() => console.log("Printing complete"))
.catch((error) => console.error("Printing failed:", error))
}
}
function downloadMarkdown() {
const html = document.querySelector<HTMLElement>("article .article-detail")
const html = document.querySelector("article")
const markdown = turndownService.turndown(html)
saveMarkdown(markdown, articleTitle)
}
function downloadHtml() {
const dom = document.querySelector<HTMLElement>("article .article-detail")
const dom = document.querySelector("article")
saveHtml(dom, articleTitle)
}
function editMarkdown() {
const dom = document.querySelector<HTMLElement>("article .article-detail")
setContent(dom, articleTitle)
function handleEdit() {
setContent("article")
}
function parseMarkdown() {
const dom = document.querySelector<HTMLElement>("article .article-detail")
setParseContent(dom)
function handleDownload() {
const html = document.querySelector("article")
const markdown = turndownService.turndown(html)
saveMarkdown(markdown, articleTitle)
}
return showTag && allShowTag ? (
<Tags
onEdit={editMarkdown}
onDownload={downloadMarkdown}
onPrint={downloadPdf}
onParse={parseMarkdown}
/>
function closeTag() {
setShowTag(false)
}
return showTag ? (
<div className="codebox-tagBtn">
<div onClick={handleEdit}>{i18n("edit")}</div>
<div onClick={handleDownload}>{i18n("download")}</div>
</div>
) : (
<></>
)
+15 -70
View File
@@ -1,40 +1,26 @@
import type {
PlasmoCSConfig,
PlasmoCSUIProps,
PlasmoGetShadowHostId
} from "plasmo"
import { useEffect, useState, type FC } from "react"
import type { PlasmoCSConfig } from "plasmo"
import { useEffect } from "react"
import { useMessage } from "@plasmohq/messaging/hook"
import { useStorage } from "@plasmohq/storage/hook"
import ToolBox from "~component/ui/toolBox"
import { addCss, saveHtml, saveMarkdown } from "~tools"
import useCssCodeHook from "~utils/cssCodeHook"
import { useEditMarkdown } from "~utils/editMarkdownHook"
import { useParseMarkdown } from "~utils/parseMarkdownHook"
import { Print } from "~utils/print"
import { useContent } from "~utils/editMarkdownHook"
import Turndown from "~utils/turndown"
export const config: PlasmoCSConfig = {
matches: ["https://baijiahao.baidu.com/*", "https://www.baidu.com/*"]
matches: ["https://*.baidu.com/*"]
}
const turndownService = Turndown()
const articleTitle = document
.querySelector<HTMLElement>("head title")
.innerText.trim()
const articleTitle = document.querySelector<HTMLElement>("head title").innerText
export const getShadowHostId: PlasmoGetShadowHostId = () => "codebox-baidu"
const isBaijiahao = location.hostname.includes("baijiahao")
const PlasmoOverlay: FC<PlasmoCSUIProps> = () => {
const [parseContent, setParseContent] = useParseMarkdown()
const [allShowTag, setAllShowTag] = useStorage("config-allShowTag", true)
const [showTag, setShowTag] = useStorage<boolean>("baidu-showTag", true)
export default function Custom() {
const [cssCode, runCss] = useCssCodeHook("baidu")
const [closeAIBox] = useStorage<boolean>("baidu-closeAIBox")
const [closeLog] = useStorage("config-closeLog", true)
const [content, setContent] = useEditMarkdown()
const [content, setContent] = useContent()
useEffect(() => {
closeLog || console.log("baidu", { closeAIBox })
@@ -46,7 +32,7 @@ const PlasmoOverlay: FC<PlasmoCSUIProps> = () => {
res.send({ isShow: true })
}
if (req.name == "baidu-editMarkdown") {
editMarkdown()
setContent(".wd-ai-index-pc")
}
if (req.name == "baidu-downloadMarkdown") {
downloadMarkdown()
@@ -63,64 +49,23 @@ const PlasmoOverlay: FC<PlasmoCSUIProps> = () => {
}`)
}
function getDescription() {
const summary = document.querySelector<HTMLMetaElement>(
'meta[name="description"]'
).content
summary && prompt("文章摘要:", summary)
}
function downloadPdf() {
const article = document.querySelector<HTMLElement>(
isBaijiahao ? "#ssr-content .EaCvy" : ".wd-ai-index-pc"
)
if (article) {
Print.print(article, { title: articleTitle })
.then(() => console.log("Printing complete"))
.catch((error) => console.error("Printing failed:", error))
}
}
function editMarkdown() {
const dom = document.querySelector(
isBaijiahao ? "#ssr-content .EaCvy" : ".wd-ai-index-pc"
)
setContent(dom, articleTitle)
const html = document.querySelector("article.article")
const markdown = turndownService.turndown(html)
setContent(markdown)
window.open("https://md.randbox.top", "_blank")
}
function downloadMarkdown() {
const html = document.querySelector(
isBaijiahao ? "#ssr-content .EaCvy" : ".wd-ai-index-pc"
)
const html = document.querySelector(".wd-ai-index-pc")
const markdown = turndownService.turndown(html)
saveMarkdown(markdown, articleTitle)
}
function downloadHtml() {
const dom = document.querySelector(
isBaijiahao ? "#ssr-content .EaCvy" : ".wd-ai-index-pc"
)
const dom = document.querySelector(".wd-ai-index-pc")
saveHtml(dom, articleTitle)
}
function parseMarkdown() {
const dom = document.querySelector<HTMLElement>(
isBaijiahao ? "#ssr-content .EaCvy" : ".wd-ai-index-pc"
)
setParseContent(dom)
}
return showTag && allShowTag && isBaijiahao ? (
<ToolBox
onGetDescription={getDescription}
onEditMarkdown={editMarkdown}
onDownloadMarkdown={downloadMarkdown}
onPrint={downloadPdf}
onParseMarkdown={parseMarkdown}
/>
) : (
<></>
)
return <div style={{ display: "none" }}></div>
}
export default PlasmoOverlay
-100
View File
@@ -1,100 +0,0 @@
import dayjs from "dayjs"
import type { PlasmoCSConfig } from "plasmo"
import React, { useEffect, useState } from "react"
import { Storage } from "@plasmohq/storage"
import { useStorage } from "@plasmohq/storage/hook"
import QRCodeModal from "~component/ui/QRCodeModal"
export const config: PlasmoCSConfig = {
matches: ["https://chatgpt.com/*", "https://chatgpt.com/c/*"]
}
export default function Chatgpt() {
const [validTime, setValidTime] = useStorage("ai-validTime", "1737872293")
const [isModalOpen, setIsModalOpen] = useState(false)
const [content, setContent] = useStorage({
key: "ai-content",
instance: new Storage({
area: "local"
})
})
useEffect(() => {
let timer = null
let index = 0
if (content) {
if (Number(validTime) < dayjs().unix()) {
timer = setInterval(() => {
const editorElement = document.querySelector(
"._prosemirror-parent_cy42l_1 p"
)
index++
if (editorElement && index < 30) {
setIsModalOpen(true)
clearTimeout(timer)
} else if (index >= 30) {
clearTimeout(timer)
}
}, 1000)
} else {
handleSetContent()
}
}
return () => {
clearTimeout(timer)
}
}, [content])
const handleCancelModal = () => {
setContent("")
setIsModalOpen(false)
}
function handleConfirmModal() {
let time = dayjs().add(1, "day").unix()
setValidTime(String(time))
handleSetContent()
setTimeout(() => {
setIsModalOpen(false)
}, 500)
}
function handleSetContent() {
const editorElement = document.querySelector(
"._prosemirror-parent_cy42l_1 p"
) as HTMLElement
if (editorElement) {
editorElement.innerText = content
let index = 0
const timer = setInterval(() => {
const buttonElement = document.querySelector(
"button[data-testid=send-button]"
) as HTMLElement
index++
if (buttonElement && index < 30) {
buttonElement.click()
setContent("")
clearTimeout(timer)
} else if (index >= 30) {
clearTimeout(timer)
}
}, 1000)
}
}
return (
<>
{isModalOpen && content && (
<QRCodeModal
onClose={handleCancelModal}
onConfirm={handleConfirmModal}
/>
)}
</>
)
}
+10 -74
View File
@@ -1,23 +1,13 @@
import type {
PlasmoCSConfig,
PlasmoCSUIProps,
PlasmoGetOverlayAnchor,
PlasmoGetShadowHostId,
PlasmoGetStyle
} from "plasmo"
import React, { useEffect, useRef, type FC } from "react"
import type { PlasmoCSConfig } from "plasmo"
import { useEffect, useRef } from "react"
import { v4 as uuidv4 } from "uuid"
import { useMessage } from "@plasmohq/messaging/hook"
import { useStorage } from "@plasmohq/storage/hook"
import TagBtnStyle from "~component/tagBtn/style"
import ToolBox from "~component/ui/toolBox"
import { saveHtml, saveMarkdown } from "~tools"
import useCssCodeHook from "~utils/cssCodeHook"
import { useEditMarkdown } from "~utils/editMarkdownHook"
import { useParseMarkdown } from "~utils/parseMarkdownHook"
import { Print } from "~utils/print"
import { useContent } from "~utils/editMarkdownHook"
import Turndown from "~utils/turndown"
export const config: PlasmoCSConfig = {
@@ -26,27 +16,14 @@ export const config: PlasmoCSConfig = {
}
const turndownService = Turndown()
const articleTitle = document
.querySelector<HTMLElement>("head title")
.innerText.trim()
const articleTitle = document.querySelector<HTMLElement>("head title").innerText
const HOST_ID = "codebox-cnblogs"
export const getShadowHostId: PlasmoGetShadowHostId = () => HOST_ID
export const getOverlayAnchor: PlasmoGetOverlayAnchor = async () =>
document.querySelector("#post_detail")
export const getStyle: PlasmoGetStyle = () => TagBtnStyle()
const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
const [parseContent, setParseContent] = useParseMarkdown()
const [allShowTag, setAllShowTag] = useStorage("config-allShowTag", true)
const [showTag, setShowTag] = useStorage<boolean>("cnblogs-showTag", true)
export default function cnblogs() {
const [cssCode, runCss] = useCssCodeHook("cnblogs")
const [copyCode] = useStorage<boolean>("cnblogs-copyCode")
const [history, setHistory] = useStorage<any[]>("codebox-history")
const [closeLog] = useStorage("config-closeLog", true)
const [content, setContent] = useEditMarkdown()
const [content, setContent] = useContent()
useEffect(() => {
closeLog || console.log("cnblogs copyCode", copyCode)
@@ -58,7 +35,7 @@ const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
res.send({ isShow: true })
}
if (req.name == "cnblogs-editMarkdown") {
editMarkdown()
setContent("article.article")
}
if (req.name == "cnblogs-downloadMarkdown") {
downloadMarkdown()
@@ -66,9 +43,6 @@ const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
if (req.name == "cnblogs-downloadHtml") {
downloadHtml()
}
if (req.name == "cnblogs-downloadPdf") {
downloadPdf()
}
})
// 功能一: 修改复制按钮,支持一键复制
@@ -145,54 +119,16 @@ const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
})
}
function getDescription() {
const summary = document.querySelector<HTMLMetaElement>(
'meta[name="description"]'
).content
summary && prompt("文章摘要:", summary)
}
function downloadPdf() {
const article = document.querySelector<HTMLElement>("#post_detail .post")
if (article) {
Print.print(article, { title: articleTitle })
.then(() => console.log("Printing complete"))
.catch((error) => console.error("Printing failed:", error))
}
}
function editMarkdown() {
const dom = document.querySelector<HTMLElement>("#post_detail")
setContent(dom, articleTitle)
}
function downloadMarkdown() {
const html = document.querySelector<HTMLElement>("#post_detail")
const html = document.querySelector("#post_detail")
const markdown = turndownService.turndown(html)
saveMarkdown(markdown, articleTitle)
}
function downloadHtml() {
const dom = document.querySelector("#post_detail .post")
const dom = document.querySelector("#post_detail")
saveHtml(dom, articleTitle)
}
function parseMarkdown() {
const dom = document.querySelector<HTMLElement>("#post_detail")
setParseContent(dom)
}
return showTag && allShowTag ? (
<ToolBox
onGetDescription={getDescription}
onEditMarkdown={editMarkdown}
onDownloadMarkdown={downloadMarkdown}
onPrint={downloadPdf}
onParseMarkdown={parseMarkdown}
/>
) : (
<></>
)
return <div style={{ display: "none" }}></div>
}
export default PlasmoOverlay
+5 -10
View File
@@ -19,10 +19,10 @@ export const config: PlasmoCSConfig = {
matches: [
"https://*.npmjs.com/*",
"https://*.medium.com/*",
"https://mp.weixin.qq.com/*",
"https://day.js.org/*",
"https://stackoverflow.com/*",
"https://dev.to/*",
"https://greasyfork.org/*"
"https://dev.to/*"
]
}
@@ -70,12 +70,7 @@ const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
const element = anchor.element
const style = window.getComputedStyle(element)
let width = style.getPropertyValue("width")
if (location.host.includes("greasyfork")) {
const codeContainer = element.closest(".code-container")
width = window.getComputedStyle(codeContainer).getPropertyValue("width")
}
const width = style.getPropertyValue("width")
const onCopy = async () => {
try {
@@ -85,9 +80,9 @@ const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
let textContent = ""
if (codeBlock) {
textContent = codeBlock.innerText
textContent = codeBlock.textContent
} else {
textContent = preBlock && preBlock.innerText
textContent = preBlock && preBlock.textContent
}
navigator.clipboard.writeText(textContent)
+41 -38
View File
@@ -11,13 +11,9 @@ import { v4 as uuidv4 } from "uuid"
import { useMessage } from "@plasmohq/messaging/hook"
import { useStorage } from "@plasmohq/storage/hook"
import TagBtnStyle from "~component/tagBtn/style"
import Tags from "~component/ui/tags"
import { addCss, i18n, saveHtml, saveMarkdown } from "~tools"
import useCssCodeHook from "~utils/cssCodeHook"
import { useEditMarkdown } from "~utils/editMarkdownHook"
import { useParseMarkdown } from "~utils/parseMarkdownHook"
import { Print } from "~utils/print"
import { useContent } from "~utils/editMarkdownHook"
import Turndown from "~utils/turndown"
export const config: PlasmoCSConfig = {
@@ -34,12 +30,29 @@ export const getOverlayAnchor: PlasmoGetOverlayAnchor = async () =>
document.querySelector(".blog-content-box .title-article") ||
document.querySelector(".article-detail .title")
export const getStyle: PlasmoGetStyle = () => TagBtnStyle()
export const getStyle: PlasmoGetStyle = () => {
const style = document.createElement("style")
style.textContent = `
.codebox-tagBtn {
height: 28px;
display: flex;
cursor: pointer;
align-items: center;
color: #1e80ff;
width: 60px;
background: #fff;
border-radius: 5px;
justify-content: space-between;
padding: 0 8px;
margin-top: -20px;
font-size: 14px;
}
`
return style
}
const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
const [parseContent, setParseContent] = useParseMarkdown()
const [allShowTag, setAllShowTag] = useStorage("config-allShowTag", true)
const [showTag, setShowTag] = useStorage<boolean>("csdn-showTag", true)
const [showTag, setShowTag] = useStorage<boolean>("csdn-showTag")
const [cssCode, runCss] = useCssCodeHook("csdn")
const [closeAds] = useStorage<boolean>("csdn-closeAds")
const [copyCode] = useStorage<boolean>("csdn-copyCode")
@@ -50,7 +63,7 @@ const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
const [closeRedirectModal] = useStorage<boolean>("csdn-closeLoginModal")
const [history, setHistory] = useStorage<any[]>("codebox-history")
const [closeLog] = useStorage("config-closeLog", true)
const [content, setContent] = useEditMarkdown()
const [content, setContent] = useContent()
useEffect(() => {
closeLog ||
@@ -85,7 +98,7 @@ const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
res.send({ isShow: true })
}
if (req.name == "csdn-editMarkdown") {
editMarkdown()
setContent(".blog-content-box")
}
if (req.name == "csdn-downloadMarkdown") {
downloadMarkdown()
@@ -93,9 +106,6 @@ const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
if (req.name == "csdn-downloadHtml") {
downloadHtml()
}
if (req.name == "csdn-downloadPdf") {
downloadPdf()
}
})
/* 未登录复制代码 */
@@ -270,20 +280,6 @@ const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
})
}
function downloadPdf() {
const article = document.querySelector<HTMLElement>(".blog-content-box")
if (article) {
Print.print(article, { title: articleTitle })
.then(() => console.log("Printing complete"))
.catch((error) => console.error("Printing failed:", error))
}
}
function editMarkdown() {
const dom = document.querySelector(".blog-content-box")
setContent(dom, articleTitle)
}
function downloadMarkdown() {
const html = document.querySelector(".blog-content-box")
const markdown = turndownService.turndown(html)
@@ -295,18 +291,25 @@ const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
saveHtml(dom, articleTitle)
}
function parseMarkdown() {
const dom = document.querySelector(".blog-content-box")
setParseContent(dom)
function handleEdit() {
setContent(".blog-content-box")
}
return showTag && allShowTag ? (
<Tags
onEdit={editMarkdown}
onDownload={downloadMarkdown}
onPrint={downloadPdf}
onParse={parseMarkdown}
/>
function handleDownload() {
const html = document.querySelector(".blog-content-box")
const markdown = turndownService.turndown(html)
saveMarkdown(markdown, articleTitle)
}
function closeTag() {
setShowTag(false)
}
return showTag ? (
<div className="codebox-tagBtn">
<div onClick={handleEdit}>{i18n("edit")}</div>
<div onClick={handleDownload}>{i18n("download")}</div>
</div>
) : (
<></>
)
+320 -64
View File
@@ -1,3 +1,15 @@
import { StyleProvider } from "@ant-design/cssinjs"
import {
CheckOutlined,
CloseOutlined,
DownSquareOutlined,
LeftSquareOutlined,
RightSquareOutlined,
UpSquareOutlined
} from "@ant-design/icons"
import { Button, Flex, message, Modal } from "antd"
import antdResetCssText from "data-text:antd/dist/reset.css"
import dayjs from "dayjs"
import type {
PlasmoCSConfig,
PlasmoGetShadowHostId,
@@ -9,95 +21,339 @@ import { sendToBackground } from "@plasmohq/messaging"
import { useMessage } from "@plasmohq/messaging/hook"
import { useStorage } from "@plasmohq/storage/hook"
import CustomDomSelector from "~component/ui/customDomSelector"
import { setIcon } from "~tools"
import { getSummary } from "~utils/coze"
import ValidateContent from "~component/contents/validateContent"
import { ThemeProvider } from "~theme"
import { addCss, saveHtml, saveMarkdown, scrollToTop, setIcon } from "~tools"
import useCssCodeHook from "~utils/cssCodeHook"
import DrawImages from "~utils/drawImages"
import makerQRPost from "~utils/makerQRPost"
import { useContent } from "~utils/editMarkdownHook"
import Turndown from "~utils/turndown"
const turndownService = Turndown()
const articleTitle =
document.querySelector<HTMLElement>("head title")?.innerText
const HOST_ID = "codebox-csui"
export const getShadowHostId: PlasmoGetShadowHostId = () => HOST_ID
const articleTitle = document
.querySelector<HTMLElement>("head title")
?.innerText.trim()
export const getStyle: PlasmoGetStyle = () => {
const style = document.createElement("style")
style.textContent = antdResetCssText
return style
}
let isDownloadType = "markdown"
let isReady = false
let isSelect = false
export default function CustomOverlay() {
const [summary, setSummary] = useStorage("app-summary", "")
const [cssCode, runCss] = useCssCodeHook("custom")
const [content, setContent] = useContent()
const [validTime, setValidTime] = useStorage("app-validTime", "1730390400")
const [isCurrentDom, setIsCurrentDom] = useState<boolean>(false)
const [rect, setRect] = useState<any>(() => {
return { top: 0, right: 0 }
})
const [messageApi, contextHolder] = message.useMessage()
useEffect(() => {
setIcon(true)
getSelection()
const hostname = location.hostname
setIcon(
hostname.includes("csdn") ||
hostname.includes("zhihu") ||
hostname.includes("baidu") ||
hostname.includes("jianshu") ||
hostname.includes("jb51") ||
hostname.includes("cnblogs") ||
hostname.includes("51cto") ||
hostname.includes("juejin") ||
hostname.includes("php") ||
hostname.includes("oschina") ||
hostname.includes("segmentfault") ||
hostname.includes("weixin") ||
hostname.includes("medium")
)
}, [])
useMessage(async (req: any, res: any) => {
if (req.name == "app-downloadImages") {
await downloadImages(req.body?.onProgress)
useMessage(async (req, res) => {
if (req.name == "custom-isShow") {
res.send({ isShow: true })
}
if (req.name == "app-get-summary") {
setSummary("")
const res = await getSummary(location.href)
if (res.code == 0) {
const result = JSON.parse(res.data)
setSummary(result)
}
if (req.name == "custom-downloadHtml") {
setCustom("html")
}
if (req.name == "app-makerQRPost") {
makerQRPost()
if (req.name == "custom-downloadMarkdown") {
setCustom("downloadMarkdown")
}
if (req.name == "app-getArticle") {
const href = encodeURIComponent(location.href)
window.open(`https://paywallbuster.com/articles/?article=${href}`)
if (req.name == "custom-downloadPdf") {
setCustom("pdf")
}
if (req.name == "custom-downloadImg") {
setCustom("img")
}
if (req.name == "custom-editMarkdown") {
setCustom("editMarkdown")
}
if (req.name == "app-full-page-screenshot") {
if (confirm("确认截图?")) {
const { scrollHeight, clientHeight } = document.documentElement
const devicePixelRatio = window.devicePixelRatio || 1
const { scrollHeight, clientHeight } = document.documentElement
const devicePixelRatio = window.devicePixelRatio || 1
let capturedHeight = 0
let capturedImages = []
let capturedHeight = 0
let capturedImages = []
const captureAndScroll = async () => {
const scrollAmount = clientHeight * devicePixelRatio
const res = await sendToBackground({ name: "screenshot" })
const dataUrl = res.dataUrl
const captureAndScroll = async () => {
const scrollAmount = clientHeight * devicePixelRatio
const res = await sendToBackground({ name: "screenshot" })
const dataUrl = res.dataUrl
capturedHeight += scrollAmount
if (capturedHeight < scrollHeight * devicePixelRatio) {
capturedImages.push(dataUrl)
window.scrollTo(0, capturedHeight)
setTimeout(captureAndScroll, 2000) // Adjust the delay as needed
} else {
DrawImages(capturedImages, articleTitle)
}
capturedHeight += scrollAmount
if (capturedHeight < scrollHeight * devicePixelRatio) {
capturedImages.push(dataUrl)
window.scrollTo(0, capturedHeight)
setTimeout(captureAndScroll, 2000) // Adjust the delay as needed
} else {
DrawImages(capturedImages, articleTitle)
}
captureAndScroll()
}
captureAndScroll()
}
})
async function downloadImages(
onProgress?: (current: number, total: number) => void
) {
const imageUrls = Array.from(document.images).map((img) => img.src)
try {
const res = await sendToBackground({
name: "download",
body: {
action: "downloadAllImages",
imageUrls: imageUrls,
title: articleTitle,
onProgress: onProgress
}
})
if (res.code == 0) {
alert("下载失败")
}
} catch (error) {
console.error(`Failed to download images:`, error)
}
function setCustom(type) {
isReady = true
isSelect = false
isDownloadType = type
messageApi.info("请在页面选择要下载区域!")
}
return <CustomDomSelector />
function getSelection() {
addCss(`.codebox-current { border: 1px solid #7983ff!important; }`)
document.addEventListener("mousemove", (event) => {
const target = event.target as HTMLElement
if (isReady && target && !isSelect) {
removeCurrentDom()
target.classList.add("codebox-current")
}
})
document.addEventListener("click", (event) => {
const target = event.target as HTMLElement
const tooltip = target.closest("#codebox-csui")
const modal = target.closest(".ant-modal")
const submit = target.closest(".valid-submit")
if (isReady && target && !tooltip && !modal && !submit) {
isSelect = true
setIsCurrentDom(true)
removeCurrentDom()
target.classList.add("codebox-current")
setTooltip()
event.stopPropagation()
event.preventDefault()
}
})
}
function setTooltip() {
const currentDom = document.querySelector<HTMLElement>(".codebox-current")
const rect = currentDom.getBoundingClientRect()
const distanceTop = rect.top + window.scrollY
const distanceLeft = rect.left + window.scrollX
const top = distanceTop < 50 ? distanceTop + 15 : distanceTop - 40
const left = distanceLeft + 5
setRect({ top, left })
scrollToTop(currentDom)
}
function removeCurrentDom() {
const currentList = document.querySelectorAll(".codebox-current")
currentList.forEach((el) => {
el.classList.remove("codebox-current")
})
}
function downloadHtml(currentDom) {
saveHtml(currentDom, articleTitle)
}
function downloadMarkdown(currentDom) {
const markdown = turndownService.turndown(currentDom)
saveMarkdown(markdown, articleTitle)
}
function handleOk() {
const currentDom = document.querySelector(".codebox-current")
if (isDownloadType == "html") {
removeCurrentDom()
downloadHtml(currentDom)
} else if (isDownloadType == "downloadMarkdown") {
removeCurrentDom()
downloadMarkdown(currentDom)
} else if (isDownloadType == "editMarkdown") {
setContent(".codebox-current")
removeCurrentDom()
}
isReady = false
isSelect = false
setIsCurrentDom(false)
}
function handleConfirm() {
Modal.confirm({
title: "提示",
content: (
<>
<div style={{ fontSize: "18px" }}>是否保存?</div>
{Number(validTime) > dayjs().unix() ? (
<></>
) : (
<ValidateContent></ValidateContent>
)}
</>
),
okText: "确认",
okButtonProps: {
// disabled: Number(validTime) <= dayjs().unix()
},
onOk: () => {
handleOk()
},
cancelText: "取消",
onCancel: () => {
handleCancel()
}
})
}
function handleCancel() {
removeCurrentDom()
isReady = false
isSelect = false
setIsCurrentDom(false)
}
function handleSetParent(event) {
const currentDom = document.querySelector(".codebox-current")
const parent = currentDom.parentElement
if (parent) {
removeCurrentDom()
parent.classList.add("codebox-current")
setTooltip()
}
event.stopPropagation()
}
function handleSetChild(event) {
const currentDom = document.querySelector(".codebox-current")
const child = currentDom.firstElementChild
if (child) {
removeCurrentDom()
child.classList.add("codebox-current")
setTooltip()
}
event.stopPropagation()
}
function handleSetPrev(event) {
const currentDom = document.querySelector(".codebox-current")
const previousSibling = currentDom.previousElementSibling
if (previousSibling) {
removeCurrentDom()
previousSibling.classList.add("codebox-current")
setTooltip()
}
event.stopPropagation()
}
function handleSetNext(event) {
const currentDom = document.querySelector(".codebox-current")
const nextSibling = currentDom.nextElementSibling
if (nextSibling) {
removeCurrentDom()
nextSibling.classList.add("codebox-current")
setTooltip()
}
event.stopPropagation()
}
return (
<ThemeProvider>
{contextHolder}
<StyleProvider container={document.getElementById(HOST_ID).shadowRoot}>
{isCurrentDom ? (
<div
className="codebox-tooltip"
style={{
position: "absolute",
top: `${rect.top}px`,
left: `${rect.left}px`,
width: "520px",
background: "#fff",
border: "1px solid #eee",
borderRadius: "5px"
}}>
<Flex wrap gap="small">
<Button
color="primary"
variant="text"
size="small"
icon={<CheckOutlined />}
onClick={handleConfirm}>
确定
</Button>
<Button
color="default"
variant="text"
size="small"
icon={<UpSquareOutlined />}
onClick={handleSetParent}>
父节点
</Button>
<Button
color="default"
variant="text"
size="small"
icon={<DownSquareOutlined />}
onClick={handleSetChild}>
子节点
</Button>
<Button
color="default"
variant="text"
size="small"
icon={<LeftSquareOutlined />}
onClick={handleSetPrev}>
上一个
</Button>
<Button
color="default"
variant="text"
size="small"
icon={<RightSquareOutlined />}
onClick={handleSetNext}>
下一个
</Button>
<Button
color="danger"
variant="text"
size="small"
icon={<CloseOutlined />}
onClick={handleCancel}>
取消
</Button>
</Flex>
</div>
) : (
<></>
)}
</StyleProvider>
</ThemeProvider>
)
}
-101
View File
@@ -1,101 +0,0 @@
import dayjs from "dayjs"
import type { PlasmoCSConfig } from "plasmo"
import React, { useEffect, useState } from "react"
import { Storage } from "@plasmohq/storage"
import { useStorage } from "@plasmohq/storage/hook"
import QRCodeModal from "~component/ui/QRCodeModal"
export const config: PlasmoCSConfig = {
matches: ["https://chat.deepseek.com/*"]
}
export default function Deepseek() {
const [validTime, setValidTime] = useStorage("ai-validTime", "1737872293")
const [isModalOpen, setIsModalOpen] = useState(false)
const [content, setContent] = useStorage({
key: "ai-content",
instance: new Storage({
area: "local"
})
})
useEffect(() => {
let timer = null
let index = 0
if (content) {
if (Number(validTime) < dayjs().unix()) {
timer = setInterval(() => {
const editorElement = document.querySelector(
"#chat-input"
) as HTMLElement
index++
if (editorElement && index < 30) {
setIsModalOpen(true)
clearTimeout(timer)
} else if (index >= 30) {
clearTimeout(timer)
}
}, 1000)
} else {
handleSetContent()
}
}
return () => {
clearTimeout(timer)
}
}, [content, validTime])
const handleCancelModal = () => {
setContent("")
setIsModalOpen(false)
}
function handleConfirmModal() {
let time = dayjs().add(1, "day").unix()
setValidTime(String(time))
handleSetContent()
setTimeout(() => {
setIsModalOpen(false)
}, 500)
}
function handleSetContent() {
const editorElement = document.querySelector(
"#chat-input"
) as HTMLInputElement
const buttonElement = document.querySelector(".f6d670") as HTMLElement
if (editorElement) {
editorElement.value = content
editorElement.dispatchEvent(new Event("input", { bubbles: true }))
editorElement.dispatchEvent(new Event("change", { bubbles: true }))
editorElement.focus()
editorElement.setSelectionRange(
editorElement.value.length,
editorElement.value.length
)
document.execCommand("InsertText", false, content)
setContent("")
setTimeout(() => {
buttonElement.click()
}, 1000)
}
}
return (
<>
{isModalOpen && content && (
<QRCodeModal
onClose={handleCancelModal}
onConfirm={handleConfirmModal}
/>
)}
</>
)
}
+8 -69
View File
@@ -1,22 +1,13 @@
import type {
PlasmoCSConfig,
PlasmoCSUIProps,
PlasmoGetOverlayAnchor,
PlasmoGetShadowHostId,
PlasmoGetStyle
} from "plasmo"
import React, { useEffect, type FC } from "react"
import type { PlasmoCSConfig } from "plasmo"
import { useEffect } from "react"
import { v4 as uuidv4 } from "uuid"
import { useMessage } from "@plasmohq/messaging/hook"
import { useStorage } from "@plasmohq/storage/hook"
import ToolBox from "~component/ui/toolBox"
import { addCss, saveHtml, saveMarkdown } from "~tools"
import useCssCodeHook from "~utils/cssCodeHook"
import { useEditMarkdown } from "~utils/editMarkdownHook"
import { useParseMarkdown } from "~utils/parseMarkdownHook"
import { Print } from "~utils/print"
import { useContent } from "~utils/editMarkdownHook"
import Turndown from "~utils/turndown"
export const config: PlasmoCSConfig = {
@@ -24,26 +15,15 @@ export const config: PlasmoCSConfig = {
}
const turndownService = Turndown()
const articleTitle = document
.querySelector<HTMLElement>("head title")
.innerText.trim()
const articleTitle = document.querySelector<HTMLElement>("head title").innerText
const HOST_ID = "codebox-jb51"
export const getShadowHostId: PlasmoGetShadowHostId = () => HOST_ID
export const getOverlayAnchor: PlasmoGetOverlayAnchor = async () =>
document.querySelector("#article .title")
const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
const [parseContent, setParseContent] = useParseMarkdown()
const [allShowTag, setAllShowTag] = useStorage("config-allShowTag", true)
const [showTag, setShowTag] = useStorage<boolean>("jb51-showTag", true)
export default function jb51() {
const [cssCode, runCss] = useCssCodeHook("jb51")
const [closeAds] = useStorage<boolean>("jb51-closeAds")
const [copyCode] = useStorage<boolean>("jb51-copyCode")
const [history, setHistory] = useStorage<any[]>("codebox-history")
const [closeLog] = useStorage("config-closeLog", true)
const [content, setEditMarkdown] = useEditMarkdown()
const [content, setContent] = useContent()
useEffect(() => {
closeLog || console.log("jb51 status", { closeAds, copyCode })
@@ -56,7 +36,7 @@ const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
res.send({ isShow: true })
}
if (req.name == "jb51-editMarkdown") {
editMarkdown()
setContent("#article")
}
if (req.name == "jb51-downloadMarkdown") {
downloadMarkdown()
@@ -64,9 +44,6 @@ const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
if (req.name == "jb51-downloadHtml") {
downloadHtml()
}
if (req.name == "jb51-downloadPdf") {
downloadPdf()
}
})
/* 未登录复制代码 */
@@ -222,27 +199,6 @@ const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
}`)
}
function getDescription() {
const summary = document.querySelector<HTMLMetaElement>(
'meta[name="description"]'
).content
summary && prompt("文章摘要:", summary)
}
function downloadPdf() {
const article = document.querySelector<HTMLElement>("#article")
if (article) {
Print.print(article, { title: articleTitle })
.then(() => console.log("Printing complete"))
.catch((error) => console.error("Printing failed:", error))
}
}
function editMarkdown() {
const dom = document.querySelector("#article")
setEditMarkdown(dom, articleTitle)
}
function downloadMarkdown() {
const html = document.querySelector("#article")
const markdown = turndownService.turndown(html)
@@ -254,22 +210,5 @@ const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
saveHtml(dom, articleTitle)
}
function parseMarkdown() {
const dom = document.querySelector<HTMLElement>("#article")
setParseContent(dom)
}
return showTag && allShowTag ? (
<ToolBox
onGetDescription={getDescription}
onEditMarkdown={editMarkdown}
onDownloadMarkdown={downloadMarkdown}
onPrint={downloadPdf}
onParseMarkdown={parseMarkdown}
/>
) : (
<></>
)
return <div style={{ display: "none" }}></div>
}
export default PlasmoOverlay
+9 -74
View File
@@ -1,24 +1,13 @@
import type {
PlasmoCSConfig,
PlasmoCSUIProps,
PlasmoGetOverlayAnchor,
PlasmoGetShadowHostId,
PlasmoGetStyle
} from "plasmo"
import React, { useEffect, useRef, type FC } from "react"
import type { PlasmoCSConfig } from "plasmo"
import { useEffect, useRef } from "react"
import { v4 as uuidv4 } from "uuid"
import { useMessage } from "@plasmohq/messaging/hook"
import { useStorage } from "@plasmohq/storage/hook"
import TagBtnStyle from "~component/tagBtn/style"
import Tags from "~component/ui/tags"
import ToolBox from "~component/ui/toolBox"
import { addCss, i18n, saveHtml, saveMarkdown } from "~tools"
import { addCss, saveHtml, saveMarkdown } from "~tools"
import useCssCodeHook from "~utils/cssCodeHook"
import { useEditMarkdown } from "~utils/editMarkdownHook"
import { useParseMarkdown } from "~utils/parseMarkdownHook"
import { Print } from "~utils/print"
import { useContent } from "~utils/editMarkdownHook"
import Turndown from "~utils/turndown"
export const config: PlasmoCSConfig = {
@@ -26,29 +15,16 @@ export const config: PlasmoCSConfig = {
}
const turndownService = Turndown()
const articleTitle = document
.querySelector<HTMLElement>("head title")
.innerText.trim()
const articleTitle = document.querySelector<HTMLElement>("head title").innerText
const HOST_ID = "codebox-jianshu"
export const getShadowHostId: PlasmoGetShadowHostId = () => HOST_ID
export const getOverlayAnchor: PlasmoGetOverlayAnchor = async () =>
document.querySelector("section.ouvJEz")
export const getStyle: PlasmoGetStyle = () => TagBtnStyle()
const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
const [parseContent, setParseContent] = useParseMarkdown()
const [allShowTag, setAllShowTag] = useStorage("config-allShowTag", true)
const [showTag, setShowTag] = useStorage<boolean>("jianshu-showTag", true)
export default function Jianshu() {
const [cssCode, runCss] = useCssCodeHook("jianshu")
const [closeLoginModal] = useStorage<boolean>("jianshu-closeLoginModal")
const [copyCode] = useStorage<boolean>("jianshu-copyCode")
const [autoOpenCode] = useStorage<boolean>("jianshu-autoOpenCode")
const [history, setHistory] = useStorage<any[]>("codebox-history")
const [closeLog] = useStorage("config-closeLog", true)
const [content, setContent] = useEditMarkdown()
const [content, setContent] = useContent()
useEffect(() => {
closeLog ||
@@ -63,7 +39,7 @@ const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
res.send({ isShow: true })
}
if (req.name == "jianshu-editMarkdown") {
editMarkdown()
setContent("section.ouvJEz")
}
if (req.name == "jianshu-downloadMarkdown") {
downloadMarkdown()
@@ -71,9 +47,6 @@ const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
if (req.name == "jianshu-downloadHtml") {
downloadHtml()
}
if (req.name == "jianshu-downloadPdf") {
downloadPdf()
}
})
// 一键复制
@@ -176,27 +149,6 @@ const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
}
}
function getDescription() {
const summary = document.querySelector<HTMLMetaElement>(
'meta[name="description"]'
).content
summary && prompt("文章摘要:", summary)
}
function downloadPdf() {
const article = document.querySelector<HTMLElement>("section.ouvJEz")
if (article) {
Print.print(article, { title: articleTitle })
.then(() => console.log("Printing complete"))
.catch((error) => console.error("Printing failed:", error))
}
}
function editMarkdown() {
const dom = document.querySelector("section.ouvJEz")
setContent(dom, articleTitle)
}
function downloadMarkdown() {
const html = document.querySelector("section.ouvJEz")
const markdown = turndownService.turndown(html)
@@ -208,22 +160,5 @@ const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
saveHtml(dom, articleTitle)
}
function parseMarkdown() {
const dom = document.querySelector("section.ouvJEz")
setParseContent(dom)
}
return showTag && allShowTag ? (
<ToolBox
onGetDescription={getDescription}
onEditMarkdown={editMarkdown}
onDownloadMarkdown={downloadMarkdown}
onPrint={downloadPdf}
onParseMarkdown={parseMarkdown}
/>
) : (
<></>
)
return <div style={{ display: "none" }}></div>
}
export default PlasmoOverlay
+46 -50
View File
@@ -5,21 +5,18 @@ import type {
PlasmoGetShadowHostId,
PlasmoGetStyle
} from "plasmo"
import React, { useEffect, useRef, useState, type FC } from "react"
import { useEffect, useRef, type FC } from "react"
import { useMessage } from "@plasmohq/messaging/hook"
import { useStorage } from "@plasmohq/storage/dist/hook"
import ToolBox from "~component/ui/toolBox"
import { i18n, saveHtml, saveMarkdown } from "~tools"
import useCssCodeHook from "~utils/cssCodeHook"
import { useEditMarkdown } from "~utils/editMarkdownHook"
import { useParseMarkdown } from "~utils/parseMarkdownHook"
import { Print } from "~utils/print"
import { useContent } from "~utils/editMarkdownHook"
import Turndown from "~utils/turndown"
export const config: PlasmoCSConfig = {
matches: ["https://*.juejin.cn/post/*"]
matches: ["https://*.juejin.cn/*"]
}
const turndownService = Turndown({
@@ -52,29 +49,46 @@ const turndownService = Turndown({
}
}
})
const articleTitle = document
.querySelector<HTMLElement>("head title")!
.innerText.trim()
const articleTitle = document.querySelector<HTMLElement>("head title").innerText
const HOST_ID = "codebox-juejin"
export const getShadowHostId: PlasmoGetShadowHostId = () => HOST_ID
export const getOverlayAnchor: PlasmoGetOverlayAnchor = async () =>
document.querySelector("#juejin")
document.querySelector("article.article .article-title")
export const getStyle: PlasmoGetStyle = () => {
const style = document.createElement("style")
style.textContent = `
.codebox-tagBtn {
height: 28px;
display: flex;
cursor: pointer;
align-items: center;
color: #1e80ff;
width: 60px;
background: #fff;
border-radius: 5px;
justify-content: space-between;
padding: 0 8px;
margin-top: -30px;
font-size: 14px;
}
`
return style
}
const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
const [parseContent, setParseContent] = useParseMarkdown()
const [allShowTag, setAllShowTag] = useStorage("config-allShowTag", true)
const [showTag, setShowTag] = useStorage<boolean>("juejin-showTag", true)
const [showTag, setShowTag] = useStorage<boolean>("juejin-showTag")
const [cssCode, runCss] = useCssCodeHook("juejin")
const [content, setContent] = useEditMarkdown()
const [content, setContent] = useContent()
useMessage(async (req, res) => {
if (req.name == "juejin-isShow") {
res.send({ isShow: true })
}
if (req.name == "juejin-editMarkdown") {
editMarkdown()
setContent("article.article")
}
if (req.name == "juejin-downloadMarkdown") {
downloadMarkdown()
@@ -82,32 +96,8 @@ const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
if (req.name == "juejin-downloadHtml") {
downloadHtml()
}
if (req.name == "juejin-downloadPdf") {
downloadPdf()
}
})
function getDescription() {
const summary = document.querySelector<HTMLMetaElement>(
'meta[name="description"]'
).content
summary && prompt("文章摘要:", summary)
}
function downloadPdf() {
const article = document.querySelector<HTMLElement>("article.article")
if (article) {
Print.print(article, { title: articleTitle })
.then(() => console.log("Printing complete"))
.catch((error) => console.error("Printing failed:", error))
}
}
function editMarkdown() {
const dom = document.querySelector("article.article")
setContent(dom, articleTitle)
}
function downloadMarkdown() {
const html = document.querySelector("article.article")
const markdown = turndownService.turndown(html)
@@ -119,19 +109,25 @@ const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
saveHtml(dom, articleTitle)
}
function parseMarkdown() {
const dom = document.querySelector("article.article")
setParseContent(dom)
function handleEdit() {
setContent("article.article")
}
return showTag && allShowTag ? (
<ToolBox
onGetDescription={getDescription}
onEditMarkdown={editMarkdown}
onDownloadMarkdown={downloadMarkdown}
onPrint={downloadPdf}
onParseMarkdown={parseMarkdown}
/>
function handleDownload() {
const html = document.querySelector("article.article")
const markdown = turndownService.turndown(html)
saveMarkdown(markdown, articleTitle)
}
function closeTag() {
setShowTag(false)
}
return showTag ? (
<div className="codebox-tagBtn">
<div onClick={handleEdit}>{i18n("edit")}</div>
<div onClick={handleDownload}>{i18n("download")}</div>
</div>
) : (
<></>
)
-103
View File
@@ -1,103 +0,0 @@
import dayjs from "dayjs"
import type { PlasmoCSConfig } from "plasmo"
import React, { useEffect, useState } from "react"
import { Storage } from "@plasmohq/storage"
import { useStorage } from "@plasmohq/storage/hook"
import QRCodeModal from "~component/ui/QRCodeModal"
export const config: PlasmoCSConfig = {
matches: ["https://kimi.moonshot.cn/*", "https://kimi.moonshot.cn/chat/*"]
}
export default function Kimi() {
const [validTime, setValidTime] = useStorage("ai-validTime", "1737872293")
const [isModalOpen, setIsModalOpen] = useState(false)
const [content, setContent] = useStorage({
key: "ai-content",
instance: new Storage({
area: "local"
})
})
useEffect(() => {
let timer = null
let index = 0
if (content) {
if (Number(validTime) < dayjs().unix()) {
timer = setInterval(() => {
const editorElement = document.querySelector(
"div[data-lexical-editor=true]"
)
index++
if (editorElement && index < 30) {
setIsModalOpen(true)
clearTimeout(timer)
} else if (index >= 30) {
clearTimeout(timer)
}
}, 1000)
} else {
handleSetContent()
}
}
return () => {
clearTimeout(timer)
}
}, [content, validTime])
const handleCancelModal = () => {
setContent("")
setIsModalOpen(false)
}
function handleConfirmModal() {
let time = dayjs().add(1, "day").unix()
setValidTime(String(time))
handleSetContent()
setTimeout(() => {
setIsModalOpen(false)
}, 500)
}
function handleSetContent() {
const editorElement = document.querySelector(
"div[data-lexical-editor=true]"
) as HTMLElement
const buttonElement = document.querySelector(
".chat-editor-action .send-button"
) as HTMLElement
if (editorElement) {
editorElement.focus()
const event = new InputEvent("input", {
bubbles: true,
cancelable: true,
data: content,
inputType: "insertText"
})
editorElement.dispatchEvent(event)
setContent("")
setTimeout(() => {
buttonElement.click()
}, 1000)
}
}
return (
<>
{isModalOpen && content && (
<QRCodeModal
onClose={handleCancelModal}
onConfirm={handleConfirmModal}
/>
)}
</>
)
}
-108
View File
@@ -1,108 +0,0 @@
import type {
PlasmoCSConfig,
PlasmoCSUIProps,
PlasmoGetShadowHostId
} from "plasmo"
import React, { type FC } from "react"
import { useMessage } from "@plasmohq/messaging/hook"
import { useStorage } from "@plasmohq/storage/dist/hook"
import ToolBox from "~component/ui/toolBox"
import { saveHtml, saveMarkdown } from "~tools"
import useCssCodeHook from "~utils/cssCodeHook"
import { useEditMarkdown } from "~utils/editMarkdownHook"
import { useParseMarkdown } from "~utils/parseMarkdownHook"
import { Print } from "~utils/print"
import Turndown from "~utils/turndown"
export const config: PlasmoCSConfig = {
matches: ["https://*.luogu.com.cn/article/*"]
}
const turndownService = Turndown()
const articleTitle = document
.querySelector<HTMLElement>("head title")
.innerText.trim()
export const getShadowHostId: PlasmoGetShadowHostId = () => "codebox-luogu"
const PlasmoOverlay: FC<PlasmoCSUIProps> = () => {
const [parseContent, setParseContent] = useParseMarkdown()
const [allShowTag, setAllShowTag] = useStorage("config-allShowTag", true)
const [showTag, setShowTag] = useStorage<boolean>("luogu-showTag", true)
const [cssCode, runCss] = useCssCodeHook("luogu")
const [content, setContent] = useEditMarkdown()
useMessage(async (req, res) => {
if (req.name == "luogu-isShow") {
res.send({ isShow: true })
}
if (req.name == "luogu-editMarkdown") {
editMarkdown()
}
if (req.name == "luogu-downloadMarkdown") {
downloadMarkdown()
}
if (req.name == "luogu-downloadHtml") {
downloadHtml()
}
if (req.name == "luogu-downloadPdf") {
downloadPdf()
}
})
function getDescription() {
const summary = document.querySelector<HTMLMetaElement>(
'meta[name="description"]'
).content
summary && prompt("文章摘要:", summary)
}
function downloadPdf() {
const article = document.querySelector<HTMLElement>(".article-content")
if (article) {
Print.print(article, { title: articleTitle })
.then(() => console.log("Printing complete"))
.catch((error) => console.error("Printing failed:", error))
}
}
function editMarkdown() {
const dom = document.querySelector(".article-content")
setContent(dom, articleTitle)
}
function downloadMarkdown() {
const html = document.querySelector(".article-content")
const markdown = turndownService.turndown(html)
saveMarkdown(markdown, articleTitle)
}
function downloadHtml() {
const dom = document.querySelector(".article-content")
saveHtml(dom, articleTitle)
}
function parseMarkdown() {
const dom = document.querySelector(".article-content")
setParseContent(dom)
}
return showTag && allShowTag ? (
<ToolBox
style={{
left: "75px"
}}
onGetDescription={getDescription}
onEditMarkdown={editMarkdown}
onDownloadMarkdown={downloadMarkdown}
onPrint={downloadPdf}
onParseMarkdown={parseMarkdown}
/>
) : (
<></>
)
}
export default PlasmoOverlay
+7 -15
View File
@@ -5,33 +5,25 @@ import { Storage } from "@plasmohq/storage"
import { useStorage } from "@plasmohq/storage/dist/hook"
export const config: PlasmoCSConfig = {
matches: ["https://md.randbox.top/*", "https://md.code-box.fun/*"]
matches: ["https://md.randbox.top/*"]
}
export default function Markdown() {
const [post, setPost] = useStorage({
key: "md-post",
const [content] = useStorage({
key: "md-content",
instance: new Storage({
area: "local"
})
})
useEffect(() => {
if (post) {
const posts = JSON.parse(window.localStorage.getItem("MD__posts")) || []
const _post = JSON.parse(post)
if (posts[0] && _post.content != posts[0].content) {
posts.unshift({
content: _post.content,
title: _post.title || "文章1"
})
window.localStorage.setItem("MD__posts", JSON.stringify(posts))
setPost("")
if (content) {
if (window.localStorage.getItem("__editor_content") != content) {
window.localStorage.setItem("__editor_content", content)
location.reload()
}
}
}, [post])
}, [content])
return <div style={{ display: "none" }}></div>
}
+62 -62
View File
@@ -1,19 +1,18 @@
import type {
PlasmoCSConfig,
PlasmoCSUIProps,
PlasmoGetShadowHostId
PlasmoGetOverlayAnchor,
PlasmoGetShadowHostId,
PlasmoGetStyle
} from "plasmo"
import { useEffect, useRef, type FC } from "react"
import { useMessage } from "@plasmohq/messaging/hook"
import { useStorage } from "@plasmohq/storage/dist/hook"
import ToolBox from "~component/ui/toolBox"
import { i18n, saveHtml, saveMarkdown } from "~tools"
import useCssCodeHook from "~utils/cssCodeHook"
import { useEditMarkdown } from "~utils/editMarkdownHook"
import { useParseMarkdown } from "~utils/parseMarkdownHook"
import { Print } from "~utils/print"
import { useContent } from "~utils/editMarkdownHook"
import Turndown from "~utils/turndown"
export const config: PlasmoCSConfig = {
@@ -91,60 +90,55 @@ const turndownOption = {
}
const turndownService = Turndown(turndownOption)
const articleTitle = document
.querySelector<HTMLElement>("head title")
.innerText.trim()
const articleTitle = document.querySelector<HTMLElement>("head title").innerText
export const getShadowHostId: PlasmoGetShadowHostId = () => "codebox-medium"
const HOST_ID = "codebox-medium"
export const getShadowHostId: PlasmoGetShadowHostId = () => HOST_ID
const PlasmoOverlay: FC<PlasmoCSUIProps> = () => {
const [parseContent, setParseContent] = useParseMarkdown()
const [allShowTag, setAllShowTag] = useStorage("config-allShowTag", true)
const [showTag, setShowTag] = useStorage<boolean>("medium-showTag", true)
export const getOverlayAnchor: PlasmoGetOverlayAnchor = async () =>
document.querySelector("article .pw-post-title")
export const getStyle: PlasmoGetStyle = () => {
const style = document.createElement("style")
style.textContent = `
.codebox-tagBtn {
height: 28px;
display: flex;
cursor: pointer;
align-items: center;
color: #1e80ff;
width: 60px;
background: #fff;
border-radius: 5px;
justify-content: space-between;
padding: 0 8px;
margin-top: -30px;
font-size: 14px;
}
`
return style
}
const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
const [showTag, setShowTag] = useStorage<boolean>("medium-showTag")
const [cssCode, runCss] = useCssCodeHook("medium")
const [content, setContent] = useEditMarkdown(turndownOption)
const [content, setContent] = useContent(turndownOption)
useMessage(async (req, res) => {
switch (req.name) {
case "medium-isShow":
res.send({ isShow: true })
break
case "medium-editMarkdown":
editMarkdown()
break
case "medium-downloadMarkdown":
downloadMarkdown()
break
case "medium-downloadHtml":
downloadHtml()
break
case "medium-downloadPdf":
downloadPdf()
break
if (req.name == "medium-isShow") {
res.send({ isShow: true })
}
if (req.name == "medium-editMarkdown") {
setContent("article.article")
}
if (req.name == "medium-downloadMarkdown") {
downloadMarkdown()
}
if (req.name == "medium-downloadHtml") {
downloadHtml()
}
})
function getDescription() {
const summary = document.querySelector<HTMLMetaElement>(
'meta[name="description"]'
).content
summary && prompt("文章摘要:", summary)
}
function downloadPdf() {
const article = document.querySelector<HTMLElement>("article")
if (article) {
Print.print(article, { title: articleTitle })
.then(() => console.log("Printing complete"))
.catch((error) => console.error("Printing failed:", error))
}
}
function editMarkdown() {
const dom = document.querySelector("article")
setContent(dom, articleTitle)
}
function downloadMarkdown() {
const html = document.querySelector("article")
const markdown = turndownService.turndown(html)
@@ -156,19 +150,25 @@ const PlasmoOverlay: FC<PlasmoCSUIProps> = () => {
saveHtml(dom, articleTitle)
}
function parseMarkdown() {
const dom = document.querySelector("article")
setParseContent(dom)
function handleEdit() {
setContent("article")
}
return showTag && allShowTag ? (
<ToolBox
onGetDescription={getDescription}
onEditMarkdown={editMarkdown}
onDownloadMarkdown={downloadMarkdown}
onPrint={downloadPdf}
onParseMarkdown={parseMarkdown}
/>
function handleDownload() {
const html = document.querySelector("article")
const markdown = turndownService.turndown(html)
saveMarkdown(markdown, articleTitle)
}
function closeTag() {
setShowTag(false)
}
return showTag ? (
<div className="codebox-tagBtn">
<div onClick={handleEdit}>{i18n("edit")}</div>
<div onClick={handleDownload}>{i18n("download")}</div>
</div>
) : (
<></>
)
-47
View File
@@ -1,47 +0,0 @@
import type {
PlasmoCSConfig,
PlasmoCSUIProps,
PlasmoGetOverlayAnchorList,
PlasmoGetShadowHostId
} from "plasmo"
import { type FC } from "react"
import { i18n } from "~tools"
export const config: PlasmoCSConfig = {
matches: ["https://mp.weixin.qq.com/cgi-bin/*"]
}
const HOST_ID = "codebox-weixin"
export const getShadowHostId: PlasmoGetShadowHostId = () => HOST_ID
export const getOverlayAnchorList: PlasmoGetOverlayAnchorList = async () =>
document.querySelectorAll(
".weui-desktop-img-picker__item .weui-desktop-img-picker__img-title"
)
const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
function handleCopy() {
try {
const item = anchor.element.closest(".weui-desktop-img-picker__item")
const img = item.querySelector(".weui-desktop-img-picker__img-thumb")
const computedStyle = window.getComputedStyle(img)
const backgroundImage = computedStyle.getPropertyValue("background-image")
const imageUrlMatch = backgroundImage.match(/url\(["']?(.*?)["']?\)/)
if (imageUrlMatch && imageUrlMatch[1]) {
const imageUrl = imageUrlMatch[1]
navigator.clipboard.writeText(imageUrl)
alert("URL 已复制到剪贴板")
}
} catch (error) {
console.error("复制失败:", error)
alert("复制失败")
}
}
return <button onClick={handleCopy}>{i18n("copyURL")}</button>
}
export default PlasmoOverlay
+7 -74
View File
@@ -1,22 +1,10 @@
import type {
PlasmoCSConfig,
PlasmoCSUIProps,
PlasmoGetOverlayAnchor,
PlasmoGetShadowHostId,
PlasmoGetStyle
} from "plasmo"
import React, { type FC } from "react"
import type { PlasmoCSConfig } from "plasmo"
import { useMessage } from "@plasmohq/messaging/hook"
import { useStorage } from "@plasmohq/storage/dist/hook"
import TagBtnStyle from "~component/tagBtn/style"
import ToolBox from "~component/ui/toolBox"
import { saveHtml, saveMarkdown } from "~tools"
import useCssCodeHook from "~utils/cssCodeHook"
import { useEditMarkdown } from "~utils/editMarkdownHook"
import { useParseMarkdown } from "~utils/parseMarkdownHook"
import { Print } from "~utils/print"
import { useContent } from "~utils/editMarkdownHook"
import Turndown from "~utils/turndown"
export const config: PlasmoCSConfig = {
@@ -24,32 +12,18 @@ export const config: PlasmoCSConfig = {
}
const turndownService = Turndown()
const articleTitle = document
.querySelector<HTMLElement>("head title")
.innerText.trim()
const articleTitle = document.querySelector<HTMLElement>("head title").innerText
const HOST_ID = "codebox-oschina"
export const getShadowHostId: PlasmoGetShadowHostId = () => HOST_ID
export const getOverlayAnchor: PlasmoGetOverlayAnchor = async () =>
document.querySelector(".blog-detail-container") ||
document.querySelector(".news-detail-container")
export const getStyle: PlasmoGetStyle = () => TagBtnStyle()
const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
const [parseContent, setParseContent] = useParseMarkdown()
const [allShowTag, setAllShowTag] = useStorage("config-allShowTag", true)
const [showTag, setShowTag] = useStorage<boolean>("oschina-showTag", true)
export default function Oschina() {
const [cssCode, runCss] = useCssCodeHook("oschina")
const [content, setContent] = useEditMarkdown()
const [content, setContent] = useContent()
useMessage(async (req, res) => {
if (req.name == "oschina-isShow") {
res.send({ isShow: true })
}
if (req.name == "oschina-editMarkdown") {
editMarkdown()
setContent(".article-box")
}
if (req.name == "oschina-downloadMarkdown") {
downloadMarkdown()
@@ -57,32 +31,8 @@ const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
if (req.name == "oschina-downloadHtml") {
downloadHtml()
}
if (req.name == "oschina-downloadPdf") {
downloadPdf()
}
})
function getDescription() {
const summary = document.querySelector<HTMLMetaElement>(
'meta[name="description"]'
).content
summary && prompt("文章摘要:", summary)
}
function downloadPdf() {
const article = document.querySelector<HTMLElement>(".article-box")
if (article) {
Print.print(article, { title: articleTitle })
.then(() => console.log("Printing complete"))
.catch((error) => console.error("Printing failed:", error))
}
}
function editMarkdown() {
const dom = document.querySelector(".article-box")
setContent(dom, articleTitle)
}
function downloadMarkdown() {
const html = document.querySelector(".article-box")
const markdown = turndownService.turndown(html)
@@ -94,22 +44,5 @@ const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
saveHtml(dom, articleTitle)
}
function parseMarkdown() {
const dom = document.querySelector(".article-box")
setParseContent(dom)
}
return showTag && allShowTag ? (
<ToolBox
onGetDescription={getDescription}
onEditMarkdown={editMarkdown}
onDownloadMarkdown={downloadMarkdown}
onPrint={downloadPdf}
onParseMarkdown={parseMarkdown}
/>
) : (
<></>
)
return <div style={{ display: "none" }}></div>
}
export default PlasmoOverlay
-138
View File
@@ -1,138 +0,0 @@
import type {
PlasmoCSConfig,
PlasmoCSUIProps,
PlasmoGetOverlayAnchorList,
PlasmoGetShadowHostId
} from "plasmo"
import React, { type FC } from "react"
import { useMessage } from "@plasmohq/messaging/dist/hook"
import { useStorage } from "@plasmohq/storage/dist/hook"
import ToolBox from "~component/ui/toolBox"
import { i18n, saveHtml, saveMarkdown } from "~tools"
import useCssCodeHook from "~utils/cssCodeHook"
import { useEditMarkdown } from "~utils/editMarkdownHook"
import { useParseMarkdown } from "~utils/parseMarkdownHook"
import { Print } from "~utils/print"
import Turndown from "~utils/turndown"
export const config: PlasmoCSConfig = {
matches: [
"https://paywallbuster.com/articles/*",
"https://archive.is/*",
"https://archive.ph/*"
]
}
const turndownService = Turndown()
const articleTitle = document
.querySelector<HTMLElement>("head title")
.innerText.trim()
export const getShadowHostId: PlasmoGetShadowHostId = () =>
"codebox-paywallbuster"
const boxStyles = {
box: {
position: "fixed" as const,
border: "1px solid #D9DADC",
left: "25px",
top: "30px",
textAlign: "center" as const,
width: "140px",
padding: "5px",
cursor: "pointer"
}
}
const isPaywallbuster = location.hostname.includes("paywallbuster")
const PlasmoOverlay: FC<PlasmoCSUIProps> = () => {
const [parseContent, setParseContent] = useParseMarkdown()
const [allShowTag, setAllShowTag] = useStorage("config-allShowTag", true)
const [showTag, setShowTag] = useStorage<boolean>(
"paywallbuster-showTag",
true
)
const [cssCode, runCss] = useCssCodeHook("paywallbuster")
const [content, setContent] = useEditMarkdown()
useMessage(async (req, res) => {
if (req.name == "paywallbuster-isShow") {
res.send({ isShow: true })
}
if (req.name == "paywallbuster-editMarkdown") {
editMarkdown()
}
if (req.name == "paywallbuster-downloadMarkdown") {
downloadMarkdown()
}
if (req.name == "paywallbuster-downloadHtml") {
downloadHtml()
}
if (req.name == "paywallbuster-downloadPdf") {
downloadPdf()
}
})
function getDescription() {
const summary = document.querySelector<HTMLMetaElement>(
'meta[name="description"]'
).content
summary && prompt("文章摘要:", summary)
}
function downloadPdf() {
const article = document.querySelector<HTMLElement>("article")
if (article) {
Print.print(article, { title: articleTitle })
.then(() => console.log("Printing complete"))
.catch((error) => console.error("Printing failed:", error))
}
}
function editMarkdown() {
const dom = document.querySelector("article")
setContent(dom, articleTitle)
}
function downloadMarkdown() {
const html = document.querySelector("article")
const markdown = turndownService.turndown(html)
saveMarkdown(markdown, articleTitle)
}
function downloadHtml() {
const dom = document.querySelector("article")
saveHtml(dom, articleTitle)
}
function parseMarkdown() {
const dom = document.querySelector("article")
setParseContent(dom)
}
function handleOpenSource() {
const iframe = document.querySelector<any>("#content-frame")
iframe && window.open(iframe.src, "_blank")
}
return showTag ? (
isPaywallbuster ? (
<div style={boxStyles.box}>
<a onClick={handleOpenSource}>打开源链接</a>
</div>
) : (
<ToolBox
onGetDescription={getDescription}
onEditMarkdown={editMarkdown}
onDownloadMarkdown={downloadMarkdown}
onPrint={downloadPdf}
onParseMarkdown={parseMarkdown}
/>
)
) : (
<></>
)
}
export default PlasmoOverlay
+71 -65
View File
@@ -1,23 +1,18 @@
import { Button } from "antd"
import type {
PlasmoCSConfig,
PlasmoCSUIProps,
PlasmoGetOverlayAnchor,
PlasmoGetShadowHostId,
PlasmoGetStyle
PlasmoGetInlineAnchorList
} from "plasmo"
import React, { useEffect, useState, type FC } from "react"
import { useEffect, useState, type FC } from "react"
import { v4 as uuidv4 } from "uuid"
import { useMessage } from "@plasmohq/messaging/hook"
import { useStorage } from "@plasmohq/storage/hook"
import TagBtnStyle from "~component/tagBtn/style"
import Tags from "~component/ui/tags"
import ToolBox from "~component/ui/toolBox"
import { addCss, i18n, removeCss, saveHtml, saveMarkdown } from "~tools"
import { addCss, removeCss, saveHtml, saveMarkdown } from "~tools"
import useCssCodeHook from "~utils/cssCodeHook"
import { useEditMarkdown } from "~utils/editMarkdownHook"
import { useParseMarkdown } from "~utils/parseMarkdownHook"
import { Print } from "~utils/print"
import { useContent } from "~utils/editMarkdownHook"
import Turndown from "~utils/turndown"
export const config: PlasmoCSConfig = {
@@ -25,28 +20,61 @@ export const config: PlasmoCSConfig = {
}
const turndownService = Turndown()
const articleTitle = document
.querySelector<HTMLElement>("head title")
.innerText.trim()
const articleTitle = document.querySelector<HTMLElement>("head title").innerText
const HOST_ID = "codebox-php"
export const getShadowHostId: PlasmoGetShadowHostId = () => HOST_ID
export const getInlineAnchorList: PlasmoGetInlineAnchorList = async () => {
const preList = document.querySelectorAll("pre")
export const getOverlayAnchor: PlasmoGetOverlayAnchor = async () =>
document.querySelector(".wzContent .wzctTitle")
const anchors = []
Array.from(preList).map((pre) => {
const classList = pre.classList
if (pre.textContent && !classList.contains("CodeMirror-line"))
anchors.push(pre)
})
export const getStyle: PlasmoGetStyle = () => TagBtnStyle()
return anchors.map((element) => ({
element,
insertPosition: "afterbegin"
// insertPosition: "beforebegin"
}))
}
const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
const [parseContent, setParseContent] = useParseMarkdown()
const [allShowTag, setAllShowTag] = useStorage("config-allShowTag", true)
const [showTag, setShowTag] = useStorage<boolean>("php-showTag", true)
const [cssCode, runCss] = useCssCodeHook("php")
const [copyCode] = useStorage("php-copyCode", true)
const [closeLoginModal] = useStorage<boolean>("php-closeLoginModal")
const [history, setHistory] = useStorage<any[]>("codebox-history")
const [closeLog] = useStorage("config-closeLog", true)
const [content, setContent] = useEditMarkdown()
const [content, setContent] = useContent()
const [isCopy, setIsCopy] = useState(false)
const element = anchor.element
const style = window.getComputedStyle(element)
const marginTop = style.getPropertyValue("margin-top")
const onCopy = async () => {
try {
const target = anchor.element as HTMLElement
const preBlock = target.closest("pre")
const codeBlock = target.querySelector("code")
let textContent = ""
if (codeBlock) {
textContent = codeBlock.textContent
} else {
textContent = preBlock && preBlock.textContent
}
navigator.clipboard.writeText(textContent)
setIsCopy(true)
setTimeout(() => {
setIsCopy(false)
}, 1000)
} catch (error) {
console.log(error)
}
}
useEffect(() => {
closeLog || console.log("PHP status", { closeLoginModal })
@@ -59,7 +87,7 @@ const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
res.send({ isShow: true })
}
if (req.name == "php-editMarkdown") {
editMarkdown()
setContent(".phpscMain .php-article")
}
if (req.name == "php-downloadMarkdown") {
downloadMarkdown()
@@ -67,22 +95,8 @@ const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
if (req.name == "php-downloadHtml") {
downloadHtml()
}
if (req.name == "oschina-downloadPdf") {
downloadPdf()
}
})
function downloadPdf() {
const article = document.querySelector<HTMLElement>(
".phpscMain .php-article"
)
if (article) {
Print.print(article, { title: articleTitle })
.then(() => console.log("Printing complete"))
.catch((error) => console.error("Printing failed:", error))
}
}
/* 未登录复制代码 */
function copyCodeCssFunc(copyCode) {
copyCode
@@ -122,18 +136,6 @@ const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
addCss(css)
}
function getDescription() {
const summary = document.querySelector<HTMLMetaElement>(
'meta[name="description"]'
).content
summary && prompt("文章摘要:", summary)
}
function editMarkdown() {
const dom = document.querySelector(".phpscMain .php-article")
setContent(dom, articleTitle)
}
function downloadMarkdown() {
const html = document.querySelector(".phpscMain .php-article")
const markdown = turndownService.turndown(html)
@@ -145,21 +147,25 @@ const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
saveHtml(dom, articleTitle)
}
function parseMarkdown() {
const dom = document.querySelector(".phpscMain .php-article")
setParseContent(dom)
}
return showTag && allShowTag ? (
<ToolBox
onGetDescription={getDescription}
onEditMarkdown={editMarkdown}
onDownloadMarkdown={downloadMarkdown}
onPrint={downloadPdf}
onParseMarkdown={parseMarkdown}
/>
) : (
<></>
return (
<>
{copyCode ? (
<div
className="codebox-copyCodeHeader"
style={{ marginBottom: "-" + marginTop }}>
<span className="codebox-copyCodeLogo"></span>
<Button
color="primary"
variant="filled"
onClick={onCopy}
className="codebox-copyCodeBtn">
{isCopy ? "复制成功" : "复制"}
</Button>
</div>
) : (
<></>
)}
</>
)
}
-108
View File
@@ -1,108 +0,0 @@
import type {
PlasmoCSConfig,
PlasmoCSUIProps,
PlasmoGetShadowHostId
} from "plasmo"
import React, { type FC } from "react"
import { useMessage } from "@plasmohq/messaging/hook"
import { useStorage } from "@plasmohq/storage/dist/hook"
import ToolBox from "~component/ui/toolBox"
import { saveHtml, saveMarkdown } from "~tools"
import useCssCodeHook from "~utils/cssCodeHook"
import { useEditMarkdown } from "~utils/editMarkdownHook"
import { useParseMarkdown } from "~utils/parseMarkdownHook"
import { Print } from "~utils/print"
import Turndown from "~utils/turndown"
export const config: PlasmoCSConfig = {
matches: [
"https://*.pianshen.com/question/*",
"http://*.pianshen.com/article/*"
]
}
const turndownService = Turndown()
const articleTitle = document
.querySelector<HTMLElement>("head title")
.innerText.trim()
export const getShadowHostId: PlasmoGetShadowHostId = () => "codebox-pianshen"
const PlasmoOverlay: FC<PlasmoCSUIProps> = () => {
const [parseContent, setParseContent] = useParseMarkdown()
const [allShowTag, setAllShowTag] = useStorage("config-allShowTag", true)
const [showTag, setShowTag] = useStorage<boolean>("pianshen-showTag", true)
const [cssCode, runCss] = useCssCodeHook("pianshen")
const [content, setContent] = useEditMarkdown()
useMessage(async (req, res) => {
if (req.name == "pianshen-isShow") {
res.send({ isShow: true })
}
if (req.name == "pianshen-editMarkdown") {
editMarkdown()
}
if (req.name == "pianshen-downloadMarkdown") {
downloadMarkdown()
}
if (req.name == "pianshen-downloadHtml") {
downloadHtml()
}
if (req.name == "pianshen-downloadPdf") {
downloadPdf()
}
})
function getDescription() {
const summary = document.querySelector<HTMLMetaElement>(
'meta[name="description"]'
).content
summary && prompt("文章摘要:", summary)
}
function downloadPdf() {
const article = document.querySelector<HTMLElement>("#article_content")
if (article) {
Print.print(article, { title: articleTitle })
.then(() => console.log("Printing complete"))
.catch((error) => console.error("Printing failed:", error))
}
}
function editMarkdown() {
const dom = document.querySelector("#article_content")
setContent(dom, articleTitle)
}
function downloadMarkdown() {
const html = document.querySelector("#article_content")
const markdown = turndownService.turndown(html)
saveMarkdown(markdown, articleTitle)
}
function downloadHtml() {
const dom = document.querySelector("#article_content")
saveHtml(dom, articleTitle)
}
function parseMarkdown() {
const dom = document.querySelector("#article_content")
setParseContent(dom)
}
return showTag && allShowTag ? (
<ToolBox
onGetDescription={getDescription}
onEditMarkdown={editMarkdown}
onDownloadMarkdown={downloadMarkdown}
onPrint={downloadPdf}
onParseMarkdown={parseMarkdown}
/>
) : (
<></>
)
}
export default PlasmoOverlay
+11 -81
View File
@@ -1,58 +1,29 @@
import type {
PlasmoCSConfig,
PlasmoCSUIProps,
PlasmoGetOverlayAnchor,
PlasmoGetShadowHostId,
PlasmoGetStyle
} from "plasmo"
import React, { type FC } from "react"
import type { PlasmoCSConfig } from "plasmo"
import { useMessage } from "@plasmohq/messaging/hook"
import { useStorage } from "@plasmohq/storage/dist/hook"
import TagBtnStyle from "~component/tagBtn/style"
import Tags from "~component/ui/tags"
import ToolBox from "~component/ui/toolBox"
import { i18n, saveHtml, saveMarkdown } from "~tools"
import { saveHtml, saveMarkdown } from "~tools"
import useCssCodeHook from "~utils/cssCodeHook"
import { useEditMarkdown } from "~utils/editMarkdownHook"
import { useParseMarkdown } from "~utils/parseMarkdownHook"
import { Print } from "~utils/print"
import { useContent } from "~utils/editMarkdownHook"
import Turndown from "~utils/turndown"
export const config: PlasmoCSConfig = {
matches: ["https://*.segmentfault.com/**"]
matches: ["https://*.segmentfault.com/*"]
}
const turndownService = Turndown()
const articleTitle = document
.querySelector<HTMLElement>("head title")
.innerText.trim()
const articleTitle = document.querySelector<HTMLElement>("head title").innerText
const HOST_ID = "codebox-segmentfault"
export const getShadowHostId: PlasmoGetShadowHostId = () => HOST_ID
export const getOverlayAnchor: PlasmoGetOverlayAnchor = async () =>
document.querySelector(".article-wrap.container")
export const getStyle: PlasmoGetStyle = () => TagBtnStyle()
const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
const [parseContent, setParseContent] = useParseMarkdown()
const [allShowTag, setAllShowTag] = useStorage("config-allShowTag", true)
const [showTag, setShowTag] = useStorage<boolean>(
"segmentfault-showTag",
true
)
export default function Segmentfault() {
const [cssCode, runCss] = useCssCodeHook("segmentfault")
const [content, setContent] = useEditMarkdown()
const [content, setContent] = useContent()
useMessage(async (req, res) => {
if (req.name == "segmentfault-isShow") {
res.send({ isShow: true })
}
if (req.name == "segmentfault-editMarkdown") {
editMarkdown()
setContent("article.article")
}
if (req.name == "segmentfault-downloadMarkdown") {
downloadMarkdown()
@@ -60,59 +31,18 @@ const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
if (req.name == "segmentfault-downloadHtml") {
downloadHtml()
}
if (req.name == "segmentfault-downloadPdf") {
downloadPdf()
}
})
function getDescription() {
const summary = document.querySelector<HTMLMetaElement>(
'meta[name="description"]'
).content
summary && prompt("文章摘要:", summary)
}
function downloadPdf() {
const article = document.querySelector<HTMLElement>(".container")
if (article) {
Print.print(article, { title: articleTitle })
.then(() => console.log("Printing complete"))
.catch((error) => console.error("Printing failed:", error))
}
}
function editMarkdown() {
const dom = document.querySelector(".container")
setContent(dom, articleTitle)
}
function downloadMarkdown() {
const html = document.querySelector(".container")
const html = document.querySelector("article.article")
const markdown = turndownService.turndown(html)
saveMarkdown(markdown, articleTitle)
}
function downloadHtml() {
const dom = document.querySelector(".container")
const dom = document.querySelector("article.article")
saveHtml(dom, articleTitle)
}
function parseMarkdown() {
const dom = document.querySelector(".container")
setParseContent(dom)
}
return showTag && allShowTag ? (
<ToolBox
onGetDescription={getDescription}
onEditMarkdown={editMarkdown}
onDownloadMarkdown={downloadMarkdown}
onPrint={downloadPdf}
onParseMarkdown={parseMarkdown}
/>
) : (
<></>
)
return <div style={{ display: "none" }}></div>
}
export default PlasmoOverlay
+179 -261
View File
@@ -1,5 +1,4 @@
import { saveAs } from "file-saver"
import JSZip from "jszip"
import html2pdf from "html2pdf.js"
import type {
PlasmoCSConfig,
PlasmoCSUIProps,
@@ -7,154 +6,62 @@ import type {
PlasmoGetShadowHostId,
PlasmoGetStyle
} from "plasmo"
import qrcodeUrl from "raw:~/public/wx/gzh.jpg"
import React, { useEffect, useState, type FC } from "react"
import { useEffect, type FC } from "react"
import { useMessage } from "@plasmohq/messaging/hook"
import { useStorage } from "@plasmohq/storage/dist/hook"
import { saveHtml, saveMarkdown } from "~tools"
import { i18n, saveHtml, saveMarkdown } from "~tools"
import useCssCodeHook from "~utils/cssCodeHook"
import { useEditMarkdown } from "~utils/editMarkdownHook"
import makerQRPost from "~utils/makerQRPost"
import { useParseMarkdown } from "~utils/parseMarkdownHook"
import { Print } from "~utils/print"
import { useContent } from "~utils/editMarkdownHook"
import Turndown from "~utils/turndown"
export const config: PlasmoCSConfig = {
matches: ["https://mp.weixin.qq.com/s/*", "https://mp.weixin.qq.com/s?src=*"]
matches: ["https://mp.weixin.qq.com/s/*"]
}
const turndownService = Turndown()
const articleTitle = document
.querySelector<HTMLMetaElement>('meta[property="og:title"]')
.content.trim()
const articleTitle = document.querySelector<HTMLElement>("head title").innerText
const HOST_ID = "codebox-weixin"
export const getShadowHostId: PlasmoGetShadowHostId = () => HOST_ID
export const getOverlayAnchor: PlasmoGetOverlayAnchor = async () =>
document.querySelector("#activity-detail")
document.querySelector("#img-content .rich_media_title")
const formatLimit = 5
const formatList = new WeakMap()
const urlReg =
/\b((?:https?:\/\/)?(?![\d-]+\.[\d-]+)([\w-]+(\.[\w-]+)+[\S^\"\'\[\]\{\}\>\<]*))/gi
const ignoreTags = [
"SCRIPT",
"STYLE",
"A",
"TEXTAREA",
"NOSCRIPT",
"CODE",
"TITLE"
]
const formatLinks = (target: Node, nodes: NodeList) => {
if (
!(target instanceof HTMLElement) ||
ignoreTags.includes(target.nodeName) ||
target.translate === false
)
return
const formatTimes = formatList.get(target) || 0
if (formatTimes > formatLimit) return
let modified = false
nodes.forEach((node: any) => {
if (node.nodeType === Node.TEXT_NODE && node.textContent.match(urlReg)) {
const newContent = node.textContent.replace(
urlReg,
(match) =>
`<a href="${match.startsWith("http") ? match : "http://" + match}"
target="_blank"
style="color: #576b95; text-decoration: none;">
${match}
</a>`
)
const wrapper = document.createElement("span")
wrapper.innerHTML = newContent
node.replaceWith(wrapper)
modified = true
}
})
if (modified) {
formatList.set(target, formatTimes + 1)
}
}
const queryChildElements = (element: Node) => {
if (element instanceof Element) {
;[...element.querySelectorAll("*")].forEach((child) =>
formatLinks(child, child.childNodes)
)
}
}
const createLinkObserver = () => {
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
formatLinks(mutation.target, mutation.addedNodes)
mutation.addedNodes.forEach((node) => queryChildElements(node))
})
})
return observer
}
const observer = createLinkObserver()
observer.observe(document, {
subtree: true,
childList: true,
characterData: true
})
queryChildElements(document.documentElement)
const style = {
box: {
position: "fixed" as const,
border: "1px solid #D9DADC",
left: "25px",
top: "25px",
width: "140px",
padding: "16px",
cursor: "pointer"
},
close: {
position: "absolute" as const,
top: "-5px",
right: "0px",
background: "none",
border: "none",
fontSize: "1.5rem",
cursor: "pointer",
padding: "0.5rem"
},
img: {
width: "100%"
},
item: {
color: "#000000"
export const getStyle: PlasmoGetStyle = () => {
const style = document.createElement("style")
style.textContent = `
.codebox-tagBtn {
height: 28px;
display: flex;
cursor: pointer;
align-items: center;
color: #1e80ff;
width: 66px;
background: transparent;
border-radius: 5px;
justify-content: space-between;
padding: 0 8px;
margin-top: -20px;
font-size: 14px;
}
`
return style
}
const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
const [parseContent, setParseContent] = useParseMarkdown()
const [allShowTag, setAllShowTag] = useStorage("config-allShowTag", true)
const [showTag, setShowTag] = useStorage<boolean>("weixin-showTag", true)
const [showTag, setShowTag] = useStorage<boolean>("weixin-showTag")
const [cssCode, runCss] = useCssCodeHook("weixin")
const [history, setHistory] = useStorage<any[]>("codebox-history")
const [content, setContent] = useEditMarkdown()
const [isShow, setIsShow] = useState(true)
const [content, setContent] = useContent()
useMessage(async (req: any, res: any) => {
useMessage(async (req, res) => {
if (req.name == "weixin-isShow") {
res.send({ isShow: true })
}
if (req.name == "weixin-editMarkdown") {
editMarkdown()
setContent("#img-content")
}
if (req.name == "weixin-downloadMarkdown") {
downloadMarkdown()
@@ -165,98 +72,147 @@ const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
if (req.name == "weixin-downloadPdf") {
downloadPdf()
}
if (req.name == "weixin-downloadImages") {
await downloadImages(req.body?.onProgress)
}
if (req.name == "weixin-getThumbMedia") {
getThumbMedia()
}
})
function getThumbMedia() {
const thumbMediaUrl = document.querySelector<HTMLMetaElement>(
'meta[property="og:image"]'
).content
thumbMediaUrl && window.open(thumbMediaUrl)
}
function getDescription() {
const summary = document.querySelector<HTMLMetaElement>(
'meta[name="description"]'
).content
summary && prompt("文章摘要:", summary)
}
async function downloadImages(
onProgress?: (current: number, total: number) => void
) {
const article = document.querySelector("#js_content")
if (!article) return
const images = Array.from(article.getElementsByTagName("img"))
const imageUrls = images
.map((img) => img.dataset.src || img.src)
.filter((url) => url)
.map((url) =>
url.replace(
"//res.wx.qq.com/mmbizwap",
"https://res.wx.qq.com/mmbizwap"
)
)
const zip = new JSZip()
const title = document.title.trim()
const total = imageUrls.length
for (let i = 0; i < imageUrls.length; i++) {
try {
const response = await fetch(imageUrls[i])
const blob = await response.blob()
let ext = ".jpg"
if (
imageUrls[i].includes("wx_fmt=gif") ||
imageUrls[i].includes("mmbiz_gif")
) {
ext = ".gif"
} else if (
imageUrls[i].includes("wx_fmt=png") ||
imageUrls[i].includes("mmbiz_png")
) {
ext = ".png"
} else if (
imageUrls[i].includes("wx_fmt=bmp") ||
imageUrls[i].includes("mmbiz_bmp")
) {
ext = ".bmp"
}
zip.file(`${title}-${i}${ext}`, blob)
if (onProgress) {
onProgress(i + 1, total)
}
} catch (error) {
console.error(`Failed to download image ${i}:`, error)
}
}
const content = await zip.generateAsync({ type: "blob" })
saveAs(content, `${title}-images.zip`)
}
console.log(777, html2pdf)
function downloadPdf() {
const article = document.querySelector<HTMLElement>("#img-content")
if (article) {
Print.print(article, { title: articleTitle })
.then(() => console.log("Printing complete"))
.catch((error) => console.error("Printing failed:", error))
}
}
// 获取文章内容和标题、作者、时间等元信息
var article = document.querySelector<HTMLElement>(".rich_media_content")
var title = document.querySelector<HTMLElement>(".rich_media_title")
var author = document.querySelector<HTMLElement>(".weui-wa-hotarea") // 文章作者
var publishTime = document.querySelector<HTMLElement>("#publish_time") // 文章时间
function editMarkdown() {
const dom = document.querySelector("#img-content")
setContent(dom, articleTitle)
if (article) {
// 创建一个容器用于添加元信息
var metaInfoDiv = document.createElement("div")
metaInfoDiv.style.marginBottom = "20px"
metaInfoDiv.style.borderBottom = "1px solid #eee"
metaInfoDiv.style.paddingBottom = "15px"
// 标题
var titleElement = document.createElement("h1")
titleElement.innerText = title ? title?.innerText.trim() : "未命名文章"
titleElement.style.fontSize = "24px"
titleElement.style.marginBottom = "10px"
metaInfoDiv.appendChild(titleElement)
// 作者
if (author) {
var authorElement = document.createElement("p")
authorElement.innerText = "作者: " + author.innerText.trim()
authorElement.style.fontSize = "14px"
authorElement.style.margin = "5px 0"
metaInfoDiv.appendChild(authorElement)
}
// 时间
if (publishTime) {
var timeElement = document.createElement("p")
timeElement.innerText = "发布时间: " + publishTime.innerText.trim()
timeElement.style.fontSize = "14px"
timeElement.style.margin = "5px 0"
metaInfoDiv.appendChild(timeElement)
}
// 将元信息插入到文章内容的顶部
article.insertBefore(metaInfoDiv, article.firstChild)
// 添加防止图片分页的CSS样式
var style = document.createElement("style")
style.innerHTML = `
.rich_media_content img {
page-break-inside: avoid;
break-inside: avoid;
max-width: 100%;
height: auto;
}
.rich_media_content p, .rich_media_content div {
page-break-inside: avoid;
break-inside: avoid;
}
`
document.head.appendChild(style)
// 确保所有图片加载完成
let images = article.querySelectorAll("img")
let imagePromises = []
images.forEach(function (img) {
// 处理懒加载的图片,确保图片的真实 URL 被加载
if (img.dataset && img.dataset.src) {
img.src = img.dataset.src
}
// 通过跨域获取图片,并将图片转换为 base64 格式
imagePromises.push(
new Promise(function (resolve) {
var imgElement = new Image()
imgElement.crossOrigin = "Anonymous"
imgElement.src = img.src
imgElement.onload = function () {
var canvas = document.createElement("canvas")
canvas.width = imgElement.width
canvas.height = imgElement.height
var ctx = canvas.getContext("2d")
ctx.drawImage(imgElement, 0, 0)
img.src = canvas.toDataURL("image/jpeg") // 使用JPEG格式并压缩质量到70%
resolve()
}
imgElement.onerror = resolve // 即使图片加载失败,继续处理
})
)
})
// 确保图片加载完成后再导出PDF
Promise.all(imagePromises)
.then(function () {
// 使用文章标题作为文件名
var fileName = title
? title.innerText.trim() + ".pdf"
: "WeChat_Article.pdf"
var opt = {
margin: 0.5,
filename: fileName,
image: {
type: "jpeg",
quality: 1 // 降低图片质量以减小PDF体积
},
html2canvas: {
scale: 1.5, // 降低渲染比例以减小PDF体积
useCORS: true, // 允许跨域图片
logging: false // 关闭日志
// 可以根据需要添加其他html2canvas选项
},
jsPDF: {
unit: "in",
format: "a4", // 使用A4格式,比letter更常用且体积可能更小
orientation: "portrait"
},
pagebreak: {
mode: ["avoid-all", "css", "legacy"]
} // 遵循CSS中的page-break规则
}
// 使用 html2pdf 将文章内容导出为 PDF
html2pdf()
.from(article)
.set(opt)
.save()
.then(function () {})
.catch(function (error) {
alert("导出过程中出现问题: " + error.message)
console.log(error)
})
})
.catch(function (error) {
alert("处理图片时出现问题: " + error.message)
console.log(error)
})
} else {
alert("未找到文章内容")
}
}
function downloadMarkdown() {
@@ -270,62 +226,24 @@ const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
saveHtml(dom, articleTitle)
}
function parseMarkdown() {
const dom = document.querySelector("#img-content")
setParseContent(dom)
function handleEdit() {
setContent("#img-content")
}
function onClose() {
setIsShow(false)
function handleDownload() {
const html = document.querySelector("#img-content")
const markdown = turndownService.turndown(html)
saveMarkdown(markdown, articleTitle)
}
return showTag && isShow && allShowTag ? (
<div id="ws_cmbm" className="ws_cmbmc" style={style.box}>
<button style={style.close} onClick={onClose} aria-label="Close">
×
</button>
<img src={qrcodeUrl} alt="qrcodeUrl" style={style.img} />
<div style={style.item}>
<a onClick={getThumbMedia}>文章封面</a>
</div>
<div style={style.item}>
<a onClick={getDescription}>文章摘要</a>
</div>
<div style={style.item}>
<a
style={{ color: "#000" }}
href="javascript:(function(){const rules={'mp.weixin.qq.com':{testReg:/^http(?:s)?:\/\/mp\.weixin\.qq\.com\/s\?.*$/i,query:['__biz','idx','mid','sn','src','timestamp','ver','signature']},other:{testReg:/^(http(?:s)?:\/\/[^?#]*)[?#].*$/i,query:['id','tid','uid','q','wd','query','keyword','keywords']}};const pureUrl=function(url=window.location.href){const hash=url.replace(/^[^#]*(#.*)?$/,'$1'),base=url.replace(/(\?|#).*$/,'');let pureUrl=url;const getQueryString=function(key){let ret=url.match(new RegExp('(?:\\?|&amp;)('+key+'=[^?#&amp;]*)','i'));return null===ret?'':ret[1]},methods={decodeUrl:function(url){return decodeURIComponent(url)}};for(let i in rules){let rule=rules[i],reg=rule.testReg,replace=rule.replace;if(reg.test(url)){let newQuerys='';void 0!==rule.query&amp;&amp;rule.query.length>0&amp;&amp;rule.query.map(query=>{const ret=getQueryString(query);''!==ret&amp;&amp;(newQuerys+=(newQuerys.length?'&amp;':'?')+ret)}),newQuerys+=void 0!==rule.hash&amp;&amp;rule.hash?hash:'',pureUrl=(void 0===replace?base:url.replace(reg,replace))+newQuerys,void 0!==rule.methods&amp;&amp;rule.methods.length>0&amp;&amp;rule.methods.map(methodName=>{pureUrl=methods[methodName](pureUrl)});break}}return pureUrl}();let newnode=document.createElement('input');newnode.id='pure-url-for-copy',newnode.value=pureUrl,document.body.appendChild(newnode);let copyinput=document.getElementById('pure-url-for-copy');copyinput.select();try{document.execCommand('copy');window.location.href===pureUrl?window.location.reload():window.location.href=pureUrl}catch(err){null!=prompt('%E5%87%80%E5%8C%96%E5%90%8E%E7%9A%84%E7%BD%91%E5%9D%80%E6%98%AF%EF%BC%9A',pureUrl)&amp;&amp;(window.location.href=pureUrl)}document.body.removeChild(copyinput)})();">
净化链接
</a>
</div>
<div style={style.item}>
<a
style={{ color: "#000" }}
href="javascript:(function(){prompt( '原始链接:', 'https://mp.weixin.qq.com/s?__biz='+biz+'&amp;idx=1&amp;mid='+mid+'&amp;sn='+sn)})();">
原始链接
</a>
</div>
<div style={style.item}>
<a onClick={editMarkdown}>编辑markdown</a>
</div>
<div style={style.item}>
<a onClick={downloadMarkdown}>下载markdown</a>
</div>
<div style={style.item}>
<a onClick={downloadPdf}>下载PDF</a>
</div>
<div style={style.item}>
<a onClick={parseMarkdown}>解析markdown</a>
</div>
<div style={style.item}>
<a onClick={() => downloadImages()}>下载所有图片</a>
</div>
<div style={style.item}>
<a onClick={() => makerQRPost()}>生成海报</a>
</div>
<a style={style.item} href="https://www.code-box.fun" target="_blank">
帮助
</a>
function closeTag() {
setShowTag(false)
}
return showTag ? (
<div className="codebox-tagBtn">
<div onClick={handleEdit}>{i18n("edit")}</div>
<div onClick={handleDownload}>{i18n("download")}</div>
</div>
) : (
<></>
+8 -104
View File
@@ -1,22 +1,13 @@
import type {
PlasmoCSConfig,
PlasmoCSUIProps,
PlasmoGetOverlayAnchor,
PlasmoGetShadowHostId
} from "plasmo"
import qrcodeUrl from "raw:~/public/wx/gzh.jpg"
import React, { useEffect, useRef, useState, type FC } from "react"
import type { PlasmoCSConfig } from "plasmo"
import { useEffect, useRef } from "react"
import { v4 as uuidv4 } from "uuid"
import { useMessage } from "@plasmohq/messaging/hook"
import { useStorage } from "@plasmohq/storage/hook"
import ToolBox from "~component/ui/toolBox"
import { addCss, saveHtml, saveMarkdown } from "~tools"
import useCssCodeHook from "~utils/cssCodeHook"
import { useEditMarkdown } from "~utils/editMarkdownHook"
import { useParseMarkdown } from "~utils/parseMarkdownHook"
import { Print } from "~utils/print"
import { useContent } from "~utils/editMarkdownHook"
import Turndown from "~utils/turndown"
export const config: PlasmoCSConfig = {
@@ -24,58 +15,16 @@ export const config: PlasmoCSConfig = {
}
const turndownService = Turndown()
const articleTitle = document
.querySelector<HTMLElement>("head title")
.innerText.trim()
const articleTitle = document.querySelector<HTMLElement>("head title").innerText
const HOST_ID = "codebox-zhihu"
export const getShadowHostId: PlasmoGetShadowHostId = () => HOST_ID
export const getOverlayAnchor: PlasmoGetOverlayAnchor = async () =>
document.querySelector("article.Post-Main")
const style = {
box: {
position: "fixed" as const,
border: "1px solid #D9DADC",
left: "25px",
top: "85px",
width: "140px",
padding: "16px",
cursor: "pointer"
},
close: {
position: "absolute" as const,
top: "-5px",
right: "0px",
background: "none",
border: "none",
fontSize: "1.5rem",
cursor: "pointer",
padding: "0.5rem"
},
img: {
width: "100%"
},
item: {
color: "#000000",
fontSize: "1rem",
marginBottom: "3px"
}
}
const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
const [parseContent, setParseContent] = useParseMarkdown()
const [allShowTag, setAllShowTag] = useStorage("config-allShowTag", true)
const [showTag, setShowTag] = useStorage<boolean>("zhihu-showTag", true)
export default function zhihu() {
const [cssCode, runCss] = useCssCodeHook("zhihu")
const [closeLoginModal] = useStorage<boolean>("zhihu-closeLoginModal")
const [copyCode] = useStorage<boolean>("zhihu-copyCode")
const [autoOpenCode] = useStorage<boolean>("zhihu-autoOpenCode")
const [history, setHistory] = useStorage<any[]>("codebox-history")
const [closeLog] = useStorage("config-closeLog", true)
const [content, setContent] = useEditMarkdown()
const [isShow, setIsShow] = useState(true)
const [content, setContent] = useContent()
useEffect(() => {
closeLog ||
@@ -90,7 +39,7 @@ const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
res.send({ isShow: true })
}
if (req.name == "zhihu-editMarkdown") {
editMarkdown()
setContent("article.Post-Main")
}
if (req.name == "zhihu-downloadMarkdown") {
downloadMarkdown()
@@ -98,9 +47,6 @@ const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
if (req.name == "zhihu-downloadHtml") {
downloadHtml()
}
if (req.name == "zhihu-downloadPdf") {
downloadPdf()
}
})
// 功能一: 修改复制按钮,支持一键复制
@@ -214,13 +160,6 @@ const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
}
}
function getDescription() {
const summary = document.querySelector<HTMLMetaElement>(
'meta[name="description"]'
).content
summary && prompt("文章摘要:", summary)
}
function removeRichContentCollapsed() {
const isCollapseds = document.querySelectorAll(".RichContent.is-collapsed")
if (isCollapseds.length > 0) {
@@ -230,20 +169,6 @@ const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
}
}
function downloadPdf() {
const article = document.querySelector<HTMLElement>("article.Post-Main")
if (article) {
Print.print(article, { title: articleTitle })
.then(() => console.log("Printing complete"))
.catch((error) => console.error("Printing failed:", error))
}
}
function editMarkdown() {
const dom = document.querySelector("article.Post-Main")
setContent(dom, articleTitle)
}
function downloadMarkdown() {
const html = document.querySelector("article.Post-Main")
const markdown = turndownService.turndown(html)
@@ -255,26 +180,5 @@ const PlasmoOverlay: FC<PlasmoCSUIProps> = ({ anchor }) => {
saveHtml(dom, articleTitle)
}
function parseMarkdown() {
const dom = document.querySelector("article.Post-Main")
setParseContent(dom)
}
function onClose() {
setIsShow(false)
}
return showTag && isShow && allShowTag ? (
<ToolBox
onGetDescription={getDescription}
onEditMarkdown={editMarkdown}
onDownloadMarkdown={downloadMarkdown}
onPrint={downloadPdf}
onParseMarkdown={parseMarkdown}
/>
) : (
<></>
)
return <div style={{ display: "none" }}></div>
}
export default PlasmoOverlay
+2 -10
View File
@@ -15,7 +15,6 @@
width: 190px;
margin: 0 auto;
padding: 5px 8px;
max-height: 590px;
}
.App-header .title {
@@ -29,18 +28,11 @@
}
.App-link {
margin-top: 5px;
margin-top: 10px;
margin-left: 5px;
font-size: 14px;
}
.App-link .item {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 5px;
font-size: 12px;
}
.popup .App-body {
max-height: 450px;
overflow-y: scroll;
-45
View File
@@ -131,9 +131,6 @@
"zhihuAutoOpenCode": {
"message": "Auto Open Code"
},
"getDescription": {
"message": "Get Description"
},
"edit": {
"message": "Edit"
},
@@ -146,9 +143,6 @@
"downloadMarkdown": {
"message": "Download Markdown"
},
"parseMarkdown": {
"message": "Parse Markdown"
},
"downloadHtml": {
"message": "Download HTML"
},
@@ -158,21 +152,9 @@
"downloadImg": {
"message": "Download Image"
},
"downloadAllImg": {
"message": "Download All Image"
},
"getArticle": {
"message": "Get All Article"
},
"downloading": {
"message": "Downloading"
},
"copyCode": {
"message": "Copy Code"
},
"parse": {
"message": "parse"
},
"copy": {
"message": "copy"
},
@@ -182,9 +164,6 @@
"showTag": {
"message": "Show Tag"
},
"allShowTag": {
"message": "All Tag"
},
"code": {
"message": "Code"
},
@@ -200,31 +179,7 @@
"weixinConfig": {
"message": "Wechat Config"
},
"paywallbusterConfig": {
"message": "Paywallbuster Config"
},
"360docConfig": {
"message": "360doc Config"
},
"pianshenConfig": {
"message": "PianShen Config"
},
"luoguConfig": {
"message": "LuoGu Config"
},
"fullPageScreenshot": {
"message": "Full Page Screenshot"
},
"getThumbMedia": {
"message": "get Thumb Media"
},
"getMediaResources": {
"message": "get Media Resources"
},
"copyURL": {
"message": "copy URL"
},
"makerQRPost": {
"message": "maker Post Image"
}
}
+1 -49
View File
@@ -6,7 +6,7 @@
"message": "实现一键下载文章成html或markdown文件;无需登录一键复制代码;支持选中代码;或者代码右上角按钮的一键复制;解除关注博主即可阅读全文提示;去除登录弹窗;去除跳转APP弹窗;页面自定义样式."
},
"popupDescription": {
"message": "免登录一键复制代码/下载文章"
"message": "免登录一键复制代码"
},
"history": {
"message": "历史记录"
@@ -131,30 +131,18 @@
"zhihuAutoOpenCode": {
"message": "自动展开全文"
},
"parse": {
"message": "解析"
},
"edit": {
"message": "编辑"
},
"download": {
"message": "下载"
},
"print": {
"message": "打印"
},
"getDescription": {
"message": "文章摘要"
},
"editMarkdown": {
"message": "编辑 Markdown"
},
"downloadMarkdown": {
"message": "下载 Markdown"
},
"parseMarkdown": {
"message": "解析 Markdown"
},
"downloadHtml": {
"message": "下载 HTML"
},
@@ -164,15 +152,6 @@
"downloadImg": {
"message": "下载图片"
},
"downloadAllImg": {
"message": "下载所有图片"
},
"getArticle": {
"message": "获取全文"
},
"downloading": {
"message": "正在下载"
},
"copyCode": {
"message": "一键复制代码"
},
@@ -185,9 +164,6 @@
"showTag": {
"message": "显示标签"
},
"allShowTag": {
"message": "所有标签"
},
"code": {
"message": "代码"
},
@@ -203,31 +179,7 @@
"weixinConfig": {
"message": "微信设置"
},
"paywallbusterConfig": {
"message": "paywallbuster设置"
},
"360docConfig": {
"message": "360doc设置"
},
"pianshenConfig": {
"message": "pianshen设置"
},
"luoguConfig": {
"message": "洛谷设置"
},
"fullPageScreenshot": {
"message": "全页面截图"
},
"getThumbMedia": {
"message": "获取封面"
},
"getMediaResources": {
"message": "复制资源"
},
"copyURL": {
"message": "复制URL"
},
"makerQRPost": {
"message": "生成海报"
}
}
+14 -10
View File
@@ -1,13 +1,13 @@
{
"name": "code-box",
"displayName": "__MSG_extensionName__",
"version": "1.3.5",
"version": "0.9.15",
"description": "__MSG_extensionDescription__",
"author": "027xiguapi. <458813868@qq.com>",
"scripts": {
"dev": "plasmo dev",
"build": "plasmo build --zip",
"build:firefox": "plasmo build --zip --target=firefox-mv3",
"build:firefox": "plasmo build --no-minify --no-hoist --zip --target=firefox-mv3",
"package": "plasmo package",
"clean": "rimraf build"
},
@@ -22,19 +22,20 @@
"dependencies": {
"@ant-design/cssinjs": "^1.21.1",
"@ant-design/icons": "^5.5.1",
"@dnd-kit/core": "^6.1.0",
"@dnd-kit/sortable": "^8.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@joplin/turndown": "^4.0.74",
"@joplin/turndown-plugin-gfm": "^1.0.56",
"@plasmohq/messaging": "^0.6.2",
"@plasmohq/storage": "^1.11.0",
"antd": "^5.21.1",
"crypto-js": "^4.2.0",
"dayjs": "^1.11.12",
"file-saver": "^2.0.5",
"html2canvas": "^1.4.1",
"html2pdf.js": "^0.10.2",
"jssha": "^3.3.1",
"jszip": "^3.10.1",
"plasmo": "0.90.3",
"qrcode": "^1.5.4",
"plasmo": "0.89.4",
"react": "18.2.0",
"react-dom": "18.2.0",
"uuid": "^10.0.0"
@@ -49,20 +50,19 @@
"postcss-modules": "^4.3.0",
"prettier": "3.2.4",
"sass": "^1.77.8",
"sharp": "^0.33.5",
"sharp": "^0.33.4",
"typescript": "5.3.3"
},
"manifest": {
"name": "__MSG_extensionName__",
"default_locale": "en",
"default_locale": "zh_CN",
"browser_specific_settings": {
"gecko": {
"id": "$PLASMO_PUBLIC_FIREFOX_EXT_ID"
}
},
"permissions": [
"activeTab",
"downloads"
"activeTab"
],
"host_permissions": [
"https://*/*"
@@ -70,5 +70,9 @@
"omnibox": {
"keyword": "copy"
}
},
"pnpm": {
"patchedDependencies": {
}
}
}
+6 -21
View File
@@ -4,22 +4,14 @@ import { i18n } from "~tools"
import "~index.css"
import { useEffect, useState } from "react"
import { sendToBackground } from "@plasmohq/messaging"
export default function IndexPopup() {
const [sidePanel, setSidePanel] = useState(false)
useEffect(() => {
hanleOpenSidePanel(sidePanel)
}, [sidePanel])
function hanleOpenSidePanel(active) {
function hanleOpenSidePanel() {
sendToBackground({
name: "sidepanel",
body: {
active: active
active: true
}
})
}
@@ -36,16 +28,9 @@ export default function IndexPopup() {
</div>
<div className="App-link">
<div className="item">
<span>{i18n("sidePanel")}</span>
<input
type="checkbox"
id="sidePanel"
name="sidePanel"
className="codebox-offscreen"
checked={sidePanel}
onChange={(e) => setSidePanel(e.target.checked)}
/>
<label htmlFor="sidePanel" className="codebox-switch"></label>
<a className="btn" href="#" onClick={hanleOpenSidePanel}>
{i18n("sidePanel")}😀
</a>
</div>
<div className="item">
<a
@@ -68,7 +53,7 @@ export default function IndexPopup() {
<div>
<a
className="btn"
href="https://mp.weixin.qq.com/s/UrnGRks6R-JJ4oZTdt0Xrw"
href="/tabs/feed.html"
target="_blank"
rel="noreferrer">
{i18n("support")}👍
Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

+1 -1
View File
@@ -62,7 +62,7 @@ pnpm install @plasmohq/messaging
├── background
│ ├── index.ts
│ └── messages
│ ├── tab.ts
│ ├── ping.ts
│ └── icon.ts
```
Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

+12 -39
View File
@@ -8,28 +8,14 @@
github地址:https://github.com/027xiguapi/code-box
自定义
1. 插入自定义样式(css代码)
2. 自定义选择下载html
3. 自定义选择下载markdown
4. 自定义选择下载pdf
5. 一键下载所有图片
6. 在线编辑markdown
微信
1. 插入自定义样式(css代码)
2. 一键下载html
3. 一键下载markdown
4. 一键下载pdf
5. 一键下载所有图片
掘金
1. 插入自定义样式(css代码)
2. 一键选择下载html
3. 一键选择下载markdown
4. 一键选择下载pdf
- 插入自定义样式(css代码)
- 自定义选择下载html
- 自定义选择下载markdown
- 自定义选择下载pdf
CSDN
1. 一键下载文章html、pdf或markdown文件
1. 一键下载文章html或markdown文件
2. 打开任意一个CSDN博客即可开始复制代码
3. 未登录CSDN状态下,支持选中代码
4. 未登录CSDN状态下,代码右上角按钮一键复制
@@ -37,16 +23,12 @@ CSDN
6. 未关注博主状态下,不再提示关注博主即可阅读全文,且完整展示文章
7. 自动展开代码块
8. 移动端屏蔽跳转APP
9. 插入自定义样式(css代码)
知乎
1. 一键下载文章html、pdf或markdown文件
1. 一键下载文章html或markdown文件
2. 一键复制代码
3. 未登录知乎状态下,不再提示展开阅读全文,且完整展示文章
4. 未登录知乎状态下,不会再出现强制登录弹窗
5. 插入自定义样式(css代码)
百度
1. 一键下载对话框html或markdown文件
@@ -57,40 +39,31 @@ CSDN
2. 一键复制代码
3. 不再提示展开阅读全文,且完整展示文章
4. 不会再出现强制登录弹窗
6. 插入自定义样式(css代码)
脚本之家
1. 一键下载文章html、pdf或markdown文件
1. 一键下载文章html或markdown文件
2. 打开任意一个脚本之家博客即可开始复制代码
3. 未登录脚本之家状态下,支持选中代码
4. 屏蔽广告
5. 移动端未登录脚本之家状态下,代码右上角按钮一键复制
6. 插入自定义样式(css代码)
博客园
- 插入自定义样式(css代码)
- 自定义选择下载html
- 自定义选择下载markdown
- 自定义选择下载pdf
1. 一键下载文章html或markdown文件
2. 一键复制代码
51CTO博客
1. 一键下载文章html、pdf或markdown文件
1. 一键下载文章html或markdown文件
2. 未登录51CTO博客状态下,支持选中代码
3. 未登录51CTO博客状态下,代码右上角按钮一键复制
4. 未登录51CTO博客状态下,不会再出现强制登录弹窗
5. 移动端未登录51CTO博客状态下,代码右上角按钮一键复制
6. 插入自定义样式(css代码)
php中文网
1. 一键下载文章html、pdf或markdown文件
### php中文网
1. 一键下载文章html或markdown文件
2. 未登录php中文网状态下,支持选中代码
3. 未登录php中文网状态下,代码右上角按钮一键复制
4. 未登录php中文网状态下,不会再出现强制登录弹窗
5. 未登录php中文网状态下,移动端代码右上角按钮一键复制
6. 插入自定义样式(css代码)
火狐说明:
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

-100
View File
@@ -1,100 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.text-gradient {
font-size: 48px;
margin: auto;
text-align: center;
/* 设置字体透明 */
color: transparent;
/* 设置线性渐变,从红色渐变到蓝色 */
background-image: linear-gradient(45deg, blue, red);
/* 使用 -webkit-background-clip 属性将背景剪裁至文本形状 */
-webkit-background-clip: text;
/* 非Webkit内核浏览器需要使用标准前缀 */
background-clip: text;
/* 把当前元素设置为行内块,以便能够应用背景 */
}
.text {
text-anchor: middle;
dominant-baseline: middle;
fill: url(#gradient);
}
</style>
</head>
<body>
<!-- <svg>
<defs>
<linearGradient id="gradient">
<stop offset="0%" stop-color="#FFCF02" />
<stop offset="100%" stop-color="#FF7352" />
</linearGradient>
</defs>
<text class="text" x="50%" y="50%">为你定制 发现精彩</text>
</svg> -->
<!-- <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-square-code">
<path d="M10 9.5 8 12l2 2.5" />
<path d="m14 9.5 2 2.5-2 2.5" />
<rect width="18" height="18" x="3" y="3" rx="2" />
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-badge">
<linearGradient id="gradient">
<stop offset="0%" stop-color="#FFCF02" />
<stop offset="100%" stop-color="#FF7352" />
</linearGradient>
<path class="text"
d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z" />
<path d="M10 9.5 8 12l2 2.5" />
<path d="m14 9.5 2 2.5-2 2.5" />
</svg> -->
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-badge">
<linearGradient id="gradient">
<stop offset="0%" stop-color="#FFCF02" />
<stop offset="100%" stop-color="#FF7352" />
</linearGradient>
<path class="text"
d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z" />
<path d="M10 9.5 8 12l2 2.5" />
<path d="m14 9.5 2 2.5-2 2.5" />
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 124 124" fill="none">
<rect width="124" height="124" rx="24" fill="#F97316" />
<path
d="M19.375 36.7818V100.625C19.375 102.834 21.1659 104.625 23.375 104.625H87.2181C90.7818 104.625 92.5664 100.316 90.0466 97.7966L26.2034 33.9534C23.6836 31.4336 19.375 33.2182 19.375 36.7818Z"
fill="white" />
<circle cx="63.2109" cy="37.5391" r="18.1641" fill="black" />
<rect opacity="0.4" x="81.1328" y="80.7198" width="17.5687" height="17.3876" rx="4"
transform="rotate(-45 81.1328 80.7198)" fill="#FDBA74" />
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path stroke-width="0" fill="#F97316"
d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z" />
<path d="M10 9.5 8 12l2 2.5" stroke="white"/>
<path d="m14 9.5 2 2.5-2 2.5" stroke="white"/>
</svg>
<div id="editableDiv" contenteditable="true">这是可编辑的内容。</div>
<button onclick="modifyContent()">修改内容</button>
<script>
function modifyContent() {
var editableDiv = document.getElementById('editableDiv');
editableDiv.innerHTML = '这是修改后的内容!'; // 修改为新的内容
}
</script>
</body>
</html>
+7 -7
View File
@@ -1,16 +1,16 @@
const sharp = require("sharp")
const sharp = require('sharp');
const input = "../assets/icon.png"
const input = '../assets/logo.png'
const output = "../assets/logo.png"
const output = '../assets/output.png'
sharp(input)
// .tint({ r: 255, g: 240, b: 16 })
.linear(0.8, 1.5)
.toFile(output, (err, info) => {
if (err) {
console.error("处理图片出错:", err)
return
console.error('处理图片出错:', err);
return;
}
console.log("图片处理完成:", info)
})
console.log('图片处理完成:', info);
});
-7
View File
@@ -1,7 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path stroke-width="0" fill="#F97316"
d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z" />
<path d="M10 9.5 8 12l2 2.5" stroke="white"/>
<path d="m14 9.5 2 2.5-2 2.5" stroke="white"/>
</svg>

Before

Width:  |  Height:  |  Size: 515 B

-44
View File
@@ -1,44 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #fff;
}
div {
text-align: center;
}
img {
width: 20%;
}
p {
font-size: 2em;
color: #000;
font-family: fantasy;
}
div {
font-size: 1em;
color: #666;
font-family: fantasy;
}
</style>
</head>
<body>
<div>
<img src="./128x128.png" alt="logo" />
<p>codebox</p>
<div>One-click Code Copying/Downloading Articles</div>
</div>
</body>
</html>
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

-3
View File
@@ -1,3 +0,0 @@
{
"presets": ["@babel/preset-env"]
}
-83
View File
@@ -1,83 +0,0 @@
import fetch from "node-fetch"
class WechatSender {
constructor() {
this.appid = process.env.WECHAT_APPID
this.secret = process.env.WECHAT_SECRET
if (!this.appid || !this.secret) {
const errorMessage =
"Error: WECHAT_APPID or WECHAT_SECRET is not set in the environment variables"
console.error(errorMessage)
throw new Error(errorMessage)
}
}
async getAccessToken() {
const url = `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${this.appid}&secret=${this.secret}`
const response = await fetch(url)
const data = await response.json()
if (data.errcode) {
throw new Error(`Failed to get access token: ${data.errmsg}`)
}
return data.access_token
}
async addDraft(accessToken, event) {
const url = `https://api.weixin.qq.com/cgi-bin/draft/add?access_token=${accessToken}`
const response = await fetch(url, {
method: "POST",
body: JSON.stringify({
articles: [
{
title: event.title, // 标题
author: event.author, // 作者
digest: event.digest, // 图文消息的摘要,仅有单图文消息才有摘要,多图文此处为空。如果本字段为没有填写,则默认抓取正文前54个字。
content: event.content, // 图文消息的具体内容,支持HTML标签,必须少于2万字符,小于1M,且此处会去除JS,涉及图片url必须来源 "上传图文消息内的图片获取URL"接口获取。外部图片url将被过滤。
content_source_url: event.content_source_url, //图文消息的原文地址,即点击“阅读原文”后的URL
thumb_media_id: event.thumb_media_id, // 图文消息的封面图片素材id(必须是永久MediaID)
need_open_comment: event.need_open_comment, // Uint32 是否打开评论,0不打开(默认),1打开
only_fans_can_comment: event.only_fans_can_comment, // Uint32 是否粉丝才可评论,0所有人可评论(默认),1粉丝才可评论
pic_crop_235_1: event.pic_crop_235_1, // 封面裁剪为2.35:1规格的坐标字段。
pic_crop_1_1: event.pic_crop_1_1 // 封面裁剪为1:1规格的坐标字段,裁剪原理同pic_crop_235_1,裁剪后的图片必须符合规格要求。
}
]
})
})
const data = await response.json()
if (data.errcode) {
throw new Error(`Failed to add draft: ${data.errmsg}`)
}
return data.media_id
}
async publishArticle(accessToken, mediaId) {
const url = `https://api.weixin.qq.com/cgi-bin/freepublish/submit?access_token=${accessToken}`
const response = await fetch(url, {
method: "POST",
body: JSON.stringify({
media_id: mediaId
})
})
const data = await response.json()
if (data.errcode) {
throw new Error(`Failed to publish article: ${data.errmsg}`)
}
return data.publish_id
}
async send(event) {
try {
const accessToken = await this.getAccessToken()
const mediaId = await this.addDraft(accessToken, event)
const articleId = await this.publishArticle(accessToken, mediaId)
console.log(`Article published successfully. Article ID: ${articleId}`)
return articleId
} catch (error) {
console.error("Error in WechatSender:", error.message)
throw error
}
}
}
export default WechatSender
-20
View File
@@ -1,20 +0,0 @@
{
"name": "wechat-sender",
"version": "1.0.0",
"description": "A Node.js module for creating and publishing WeChat articles",
"main": "WechatSender.js",
"type": "module",
"scripts": {
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js"
},
"keywords": ["wechat", "article", "publisher"],
"author": "Your Name",
"license": "ISC",
"dependencies": {
"node-fetch": "^3.2.0"
},
"devDependencies": {
"@babel/preset-env": "^7.16.11",
"jest": "^27.5.1"
}
}
-43
View File
@@ -1,43 +0,0 @@
import dotenv from "dotenv"
import express from "express"
import WechatSender from "./WechatSender.js"
dotenv.config()
const app = express()
app.use(express.json())
const wechatSender = new WechatSender()
app.post("/api/article/send", async (req, res) => {
try {
const article = req.body
const articleId = await wechatSender.send({ ...article })
res.json({ success: true, articleId })
} catch (error) {
console.error("Error sending article:", error)
res.status(500).json({ success: false, error: error.message })
}
})
app.post("/api/article/submit", async (req, res) => {
try {
const article = req.body
const accessToken = await wechatSender.getAccessToken()
const mediaId = await wechatSender.addDraft(accessToken, {
...article
})
res.json({ success: true, mediaId })
} catch (error) {
console.error("Error submitting article:", error)
res.status(500).json({ success: false, error: error.message })
}
})
const PORT = process.env.PORT || 3000
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`)
})
export default app
-64
View File
@@ -1,64 +0,0 @@
import { jest } from "@jest/globals"
import request from "supertest"
import app from "./server.js"
import WechatSender from "./WechatSender.js"
jest.mock("./WechatSender.js")
describe("Express App", () => {
beforeEach(() => {
WechatSender.mockClear()
})
test("POST /api/send-article", async () => {
const mockSend = jest.fn().mockResolvedValue("mock_article_id")
WechatSender.mockImplementation(() => ({
send: mockSend
}))
const response = await request(app).post("/api/send-article").send({
title: "Test Title",
subTitle: "Test Subtitle",
content: "Test Content"
})
expect(response.statusCode).toBe(200)
expect(response.body).toEqual({
success: true,
articleId: "mock_article_id"
})
expect(mockSend).toHaveBeenCalledWith({
title: "Test Title",
subTitle: "Test Subtitle",
content: "Test Content"
})
})
test("POST /api/submit-article", async () => {
const mockGetAccessToken = jest.fn().mockResolvedValue("mock_access_token")
const mockAddDraft = jest.fn().mockResolvedValue("mock_media_id")
WechatSender.mockImplementation(() => ({
getAccessToken: mockGetAccessToken,
addDraft: mockAddDraft
}))
const response = await request(app).post("/api/submit-article").send({
title: "Test Title",
subTitle: "Test Subtitle",
content: "Test Content"
})
expect(response.statusCode).toBe(200)
expect(response.body).toEqual({
success: true,
mediaId: "mock_media_id"
})
expect(mockGetAccessToken).toHaveBeenCalled()
expect(mockAddDraft).toHaveBeenCalledWith("mock_access_token", {
title: "Test Title",
subTitle: "Test Subtitle",
content: "Test Content"
})
})
})
-9
View File
@@ -16,15 +16,6 @@
.valid {
color: red;
}
.summary {
.title {
font-size: 16px;
margin: 10px 0;
}
.content {
font-size: 14px;
}
}
.contentTitle {
margin: 15px 0 10px 0;
}
+11 -28
View File
@@ -2,31 +2,24 @@ import { ThemeProvider } from "~theme"
import "~index.css"
import { DownloadOutlined, PushpinOutlined } from "@ant-design/icons"
import qrcodeUrl from "raw:~/public/wx/gzh.jpg"
import {
DownloadOutlined,
KeyOutlined,
PushpinOutlined
} from "@ant-design/icons"
import dayjs from "dayjs"
import { useRef, useState } from "react"
import { sendToContentScript } from "@plasmohq/messaging"
import { useMessage } from "@plasmohq/messaging/dist/hook"
import { useStorage } from "@plasmohq/storage/dist/hook"
import ValidateContent from "~component/contents/validateContent"
import { verifyTOTP } from "~utils/2FA"
import styles from "./index.module.scss"
function IndexSidePanel() {
const [codes] = useStorage("app-codes", [])
const [summary, setSummary] = useStorage("app-summary", {
title: "",
score: "",
content: ""
})
useMessage((req: any, res: any) => {
if (req.name === "isSidePanelOpen") {
return true
}
if (req.name == "sidepanel") {
req.body.active || window.close()
}
})
function pinCode(index) {
sendToContentScript({ name: `custom-scrollIntoViewCode`, body: { index } })
@@ -41,19 +34,9 @@ function IndexSidePanel() {
<div className={`${styles.sidepanel}`}>
<div className="wechat">
<h1>微信公众号</h1>
<img style={{ margin: "auto" }} src={qrcodeUrl} alt="微信公众号" />
<ValidateContent></ValidateContent>
</div>
<div className="content">
{summary.content ? (
<div className="summary">
<div className="title">
{summary.title}(评分:{summary.score})
</div>
<div className="content">{summary.content}</div>
</div>
) : (
<></>
)}
<h1 className="contentTitle">导航</h1>
{codes.map((code, index) => (
<div
+1 -1
View File
@@ -2,7 +2,7 @@ import chromeUrl from "raw:~/public/webstore/1723513894421.jpg"
import githubUrl from "raw:~/public/webstore/1724640034462.jpg"
import edgeUrl from "raw:~/public/webstore/1724640161752.jpg"
import firefoxUrl from "raw:~/public/webstore/img.png"
import qrcodeUrl from "raw:~/public/wx/gzh.jpg"
import qrcodeUrl from "raw:~/public/wx/qrcode_wx.jpg"
import styles from "./feed.module.scss"

Some files were not shown because too many files have changed in this diff Show More