Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 3 additions & 6 deletions entrypoints/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,13 +132,10 @@ export default defineBackground(() => {
// The result is an array of execution results
if (results && results.length > 0 && results[0].result) {
const bodyContent = results[0].result
console.log("Body content:", bodyContent)

browser.tabs.query({ active: true, currentWindow: true }, () => {
browser.tabs.sendMessage(activeTab.id!, {
type: "COPY_TEXT",
payload: bodyContent,
})
browser.tabs.sendMessage(activeTab.id!, {
type: "COPY_TEXT",
payload: bodyContent,
})
}
} catch (error) {
Expand Down
114 changes: 108 additions & 6 deletions entrypoints/content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,65 @@ import { getVideoSubtitle } from "@/lib/yt/getVideoSubtitle"

const tiktoken = new Tiktoken(o200k_base)

function removeRedundantNewlinesFromText(text: string): string {
if (!text || typeof text !== "string") {
return text
}

let result = text

result = result.replace(/\r\n/g, "\n")
result = result.replace(/\r/g, "\n")

result = result.replace(
/^[ \t\f\v\u00a0\u2000-\u200a\u202f\u205f\u3000]+$/gm,
"",
)

let previousLength
do {
previousLength = result.length
result = result.replace(/\n[ \t\f\v\u00a0\u2000-\u200a\u202f\u205f\u3000]*\n/g, "\n\n")
result = result.replace(/\n{3,}/g, "\n\n")
} while (result.length !== previousLength)

result = result.replace(/^\n+/, "")
result = result.replace(/\n+$/, "")

return result
}

const SOURCE_INFO_MARKER = "<!-- cpdown-source-info -->"

function buildSourceMetadata(
pageTitle: string,
pageUrl: string,
): { title: string; url: string; copyTime: string } {
return {
title: pageTitle,
url: pageUrl,
copyTime: new Date().toLocaleString("zh-CN", {
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
}),
}
}

function hasSourceInfo(text: string): boolean {
return text.includes(SOURCE_INFO_MARKER) || text.includes("**来源信息**")
}

function formatSourceInfo(metadata: {
title: string
url: string
copyTime: string
}): string {
return `\n\n---\n\n${SOURCE_INFO_MARKER}\n\n**来源信息**\n\n- 📄 标题:${metadata.title}\n- 🔗 链接:${metadata.url}\n- ⏰ 复制时间:${metadata.copyTime}`
}

// Utility to copy markdown to clipboard, respond to sender and optionally show toast/confetti
const copyAndNotify = async ({
markdown,
Expand All @@ -22,24 +81,36 @@ const copyAndNotify = async ({
showConfetti,
sendResponse,
successMessagePrefix,
sourceMetadata,
}: {
markdown: string
wrapInTripleBackticks: boolean
showSuccessToast: boolean
showConfetti: boolean
sendResponse: (response: { success: boolean }) => void
successMessagePrefix: string
sourceMetadata?: {
title: string
url: string
copyTime: string
}
}) => {
let finalContent = markdown

if (wrapInTripleBackticks) {
markdown = `\`\`\`md\n${markdown}\n\`\`\``
finalContent = `\`\`\`md\n${finalContent}\n\`\`\``
}

if (sourceMetadata && !hasSourceInfo(finalContent)) {
finalContent = finalContent + formatSourceInfo(sourceMetadata)
}

try {
await navigator.clipboard.writeText(markdown)
await navigator.clipboard.writeText(finalContent)
} catch (error) {
// Fallback for when document is not focused (e.g., DevTools is open)
const textarea = document.createElement("textarea")
textarea.value = markdown
textarea.value = finalContent
textarea.style.position = "fixed"
textarea.style.opacity = "0"
document.body.appendChild(textarea)
Expand All @@ -50,7 +121,7 @@ const copyAndNotify = async ({

sendResponse({ success: true })

const tokens = tiktoken.encode(markdown)
const tokens = tiktoken.encode(finalContent)

if (showSuccessToast) {
showNotification(`${successMessagePrefix} (${tokens.length} tokens)`)
Expand Down Expand Up @@ -89,6 +160,8 @@ export default defineContentScript({
showConfetti,
useDeffudle,
wrapInTripleBackticks,
removeRedundantNewlines,
enableSourceTracking,
} = options

const html = msg.payload
Expand Down Expand Up @@ -140,13 +213,25 @@ export default defineContentScript({
.turndown(html)
}

if (removeRedundantNewlines) {
markdown = removeRedundantNewlinesFromText(markdown)
}

const sourceMetadata = enableSourceTracking
? buildSourceMetadata(
document.title || "Untitled Page",
window.location.href,
)
: undefined

await copyAndNotify({
markdown,
wrapInTripleBackticks,
showSuccessToast,
showConfetti,
sendResponse,
successMessagePrefix: "Copied as markdown",
sourceMetadata,
})

return true
Expand All @@ -155,8 +240,13 @@ export default defineContentScript({
if (msg.type === "COPY_YOUTUBE_SUBTITLE") {
const options = await getOptions()

const { showSuccessToast, showConfetti, wrapInTripleBackticks } =
options
const {
showSuccessToast,
showConfetti,
wrapInTripleBackticks,
removeRedundantNewlines,
enableSourceTracking,
} = options

const videoId = msg.payload

Expand All @@ -177,13 +267,25 @@ export default defineContentScript({

markdown = `# ${title}\n\n\n${markdown}`

if (removeRedundantNewlines) {
markdown = removeRedundantNewlinesFromText(markdown)
}

const sourceMetadata = enableSourceTracking
? buildSourceMetadata(
document.title || title,
window.location.href,
)
: undefined

await copyAndNotify({
markdown,
wrapInTripleBackticks,
showSuccessToast,
showConfetti,
sendResponse,
successMessagePrefix: "Subtitle copied to clipboard",
sourceMetadata,
})

return true
Expand Down
22 changes: 22 additions & 0 deletions entrypoints/options/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,28 @@ export const App = () => {
}
infoLink="https://x.com/raycastapp/status/1691464764516343808"
/>

<div className="border-border border-t"></div>

<ToggleOption
title="排版优化 - 去除冗余换行"
description="开启后自动清理复制内容中多余的空行,让文本阅读更整洁流畅"
checked={options.removeRedundantNewlines}
onCheckedChange={(checked) =>
handleOptionChange("removeRedundantNewlines", checked)
}
/>

<div className="border-border border-t"></div>

<ToggleOption
title="资料溯源增强"
description="开启后复制正文时,自动拼接附带网页标题、原始来源链接、实时复制时间信息,统一排版展示"
checked={options.enableSourceTracking}
onCheckedChange={(checked) =>
handleOptionChange("enableSourceTracking", checked)
}
/>
</>
)}
</div>
Expand Down
4 changes: 4 additions & 0 deletions lib/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ export type OptionsState = {
wrapInTripleBackticks: boolean
showSuccessToast: boolean
showConfetti: boolean
removeRedundantNewlines: boolean
enableSourceTracking: boolean
}

export const defaultOptions: OptionsState = {
Expand All @@ -14,6 +16,8 @@ export const defaultOptions: OptionsState = {
wrapInTripleBackticks: true,
showSuccessToast: true,
showConfetti: false,
removeRedundantNewlines: false,
enableSourceTracking: false,
}

export async function getOptions(): Promise<OptionsState> {
Expand Down
Loading
Loading