From 191a2dff29b57e56790c76fd2cff0dbfab148bb2 Mon Sep 17 00:00:00 2001 From: DerYokoya <133297414+DerYokoya@users.noreply.github.com> Date: Fri, 29 May 2026 12:32:35 -0400 Subject: [PATCH 01/11] Korean version --- _locales/kr/messages.json | 522 ++++++++++++++++++++++++++++++++++++++ popup/popup.html | 1 + 2 files changed, 523 insertions(+) create mode 100644 _locales/kr/messages.json diff --git a/_locales/kr/messages.json b/_locales/kr/messages.json new file mode 100644 index 0000000..37df6d4 --- /dev/null +++ b/_locales/kr/messages.json @@ -0,0 +1,522 @@ +{ + "extensionName": { + "message": "DeepShare: AI 채팅 내용을 Word로 내보내기" + }, + "extensionDescription": { + "message": "DeepSeek, ChatGPT, Gemini, Grok 등의 AI 채팅 내용을 Word 문서로 내보내고, 수식을 복사하며, DeepSeek 채팅의 긴 스크린샷을 찍을 수 있습니다." + }, + "shareButton": { + "message": "대화 공유" + }, + "modalTitle": { + "message": "대화 공유" + }, + "imageTab": { + "message": "이미지" + }, + "textTab": { + "message": "텍스트" + }, + "wordTab": { + "message": "Word" + }, + "wordExportInfo": { + "message": "다운로드 또는 복사 버튼을 클릭하여 사용자 질문과 AI 응답을 모두 Word 문서로 내보내세요" + }, + "noAiResponses": { + "message": "AI 응답이 없습니다" + }, + "downloadButton": { + "message": "다운로드" + }, + "copyButton": { + "message": "복사" + }, + "docxButton": { + "message": "DOCX로 저장" + }, + "docxFeatureExplanation": { + "message": "AI를 Word로 변환하는 기능을 설정하세요. 스크린샷 및 수식 복사는 무료로 제공되며 별도의 설정이 필요하지 않습니다." + }, + "docxConversionSuccess": { + "message": "Word 문서로 성공적으로 저장되었습니다!" + }, + "docxConverting": { + "message": "Word 문서로 저장 중입니다. 창을 닫지 마세요." + }, + "docxConversionError": { + "message": "Word 문서로 저장하는 데 실패했습니다. 네트워크 연결을 확인하거나 페이지를 새로 고친 후 다시 시도해 주세요!" + }, + "watermarkSettings": { + "message": "워터마크 설정" + }, + "screenshotSettings": { + "message": "채팅 스크린샷" + }, + "docxSettings": { + "message": "AI 채팅에서 Word로" + }, + "docxServerUrlLabel": { + "message": "서버 URL" + }, + "docxApiKeyLabel": { + "message": "API 키" + }, + "docxModeLabel": { + "message": "변환 모드" + }, + "modeLocalLabel": { + "message": "로컬 (Pandoc)" + }, + "modeApiLabel": { + "message": "API" + }, + "copied": { + "message": "복사됨!" + }, + "copyFailed": { + "message": "복사 실패!" + }, + "formulaCopied": { + "message": "수식 복사됨!" + }, + "clickToCopyFormula": { + "message": "수식을 복사하려면 클릭하세요" + }, + "generatingImage": { + "message": "스크린샷 생성 중..." + }, + "generateFailed": { + "message": "스크린샷 생성에 실패했습니다. 페이지를 새로 고침해 주세요!" + }, + "defaultWatermark": { + "message": "DeepSeek AI에서 생성하고 DeepShare 확장 프로그램으로 캡처한 콘텐츠" + }, + "settingsButton": { + "message": "워터마크 설정" + }, + "hideDefaultWatermarkLabel": { + "message": "기본 워터마크 숨기기" + }, + "customWatermarkLabel": { + "message": "사용자 지정 워터마크 텍스트 (선택 사항)" + }, + "customWatermarkPlaceholder": { + "message": "여기에 사용자 지정 워터마크를 입력하세요" + }, + "saveSettings": { + "message": "설정 저장" + }, + "settingsSaved": { + "message": "저장됨" + }, + "sponsorTitle": { + "message": "이용해 주셔서 감사합니다! 도움이 되셨다면, 위챗 QR 코드를 통해 후원해 주세요 😊" + }, + "selectButton": { + "message": "메시지 선택" + }, + "selectAllButton": { + "message": "모두 선택" + }, + "selectAllResponsesButton": { + "message": "모든 응답 선택" + }, + "unselectAllButton": { + "message": "모두 선택 해제" + }, + "loadingHistory": { + "message": "로딩 중..." + }, + "noMessageSelected": { + "message": "메시지를 하나 이상 선택해 주세요" + }, + "sponsorTabLabel": { + "message": "소개" + }, + "totalQuotaLabel": { + "message": "총량:" + }, + "usedQuotaLabel": { + "message": "사용량:" + }, + "remainingQuotaLabel": { + "message": "잔여량:" + }, + "quotaTitle": { + "message": "전환 할당량" + }, + "dailyQuotaLabel": { + "message": "일일 할당량" + }, + "addonQuotaLabel": { + "message": "애드온 할당량" + }, + "dailyResetNote": { + "message": "매일 초기화됨" + }, + "subscriptionExpiryNote": { + "message": "매일 초기화됨 · {date}까지" + }, + "addonExpiryNote": { + "message": "{date}에 만료됨" + }, + "apiKeyHint": { + "message": "API 키를 구매하시겠습니까? 여기를 클릭하세요" + }, + "apiKeyMissing": { + "message": "문서 변환 기능을 사용하려면 확장 프로그램 아이콘을 클릭하여 API 키를 구매하고 입력해 주세요." + }, + "apiKeyMissingShort": { + "message": "구매 후 API 키를 입력해 주세요" + }, + "manualConversionExplanation": { + "message": "Grok, Claude, Meta AI 등을 지원합니다. 대화 내용을 마크다운 입력란에 복사한 후 \"문서로 변환\"을 클릭하면 수식 지원이 포함된 깔끔하게 서식 처리된 Word 문서를 다운로드할 수 있습니다!" + }, + "manualConversionTitle": { + "message": "AI 채팅 내용 붙여넣기" + }, + "markdownInputLabel": { + "message": "마크다운 텍스트" + }, + "markdownInputPlaceholder": { + "message": "여기에 마크다운 형식의 텍스트를 붙여넣으세요..." + }, + "convertToDocx": { + "message": "문서로 변환" + }, + "clearMarkdown": { + "message": "지우기" + }, + "emptyMarkdownError": { + "message": "마크다운 텍스트를 입력해 주세요" + }, + "manualDocxSettings": { + "message": "AI 채팅 내용 붙여넣기" + }, + "formulaTabLabel": { + "message": "수식 복사" + }, + "formulaSettingsTitle": { + "message": "수식 복사 설정" + }, + "formulaCopyTutorialText": { + "message": "수식 복사에 대해 자세히 알아보기:" + }, + "formulaCopyTutorialLink": { + "message": "튜토리얼 보기" + }, + "enableFormulaCopyLabel": { + "message": "수식 복사 활성화" + }, + "formulaFormatLabel": { + "message": "수식 복사 형식" + }, + "formatMathMLLabel": { + "message": "MathML" + }, + "formatLaTeXLabel": { + "message": "LaTeX" + }, + "formatDollarLatexLabel": { + "message": "마크다운" + }, + "formulaFormatHint": { + "message": "Word용 MathML, WPS/Overleaf용 LaTeX, Lark/Notion/Obsidian용 마크다운" + }, + "formulaEngineLabel": { + "message": "변환 엔진" + }, + "engineMathJaxLabel": { + "message": "MathJax" + }, + "engineKaTeXLabel": { + "message": "KaTeX" + }, + "formulaEngineHint": { + "message": "MathJax는 호환성이 더 우수하고, KaTeX는 더 빠릅니다" + }, + "screenshotMethodLabel": { + "message": "스크린샷 방법" + }, + "methodDomToImageLabel": { + "message": "dom-to-image" + }, + "methodHtml2CanvasLabel": { + "message": "html2canvas" + }, + "screenshotMethodHint": { + "message": "스크린샷에 사용할 방법을 선택하세요. 한 방법이 작동하지 않으면 다른 방법을 시도해 보세요. 방법을 변경한 후 페이지를 새로 고침해 주세요." + }, + "removeDividersLabel": { + "message": "구분선 제거" + }, + "removeEmojisLabel": { + "message": "이모지 제거" + }, + "convertMermaidLabel": { + "message": "Mermaid 변환" + }, + "compatModeLabel": { + "message": "호환 모드" + }, + "compatModeTooltip": { + "message": "비표준 마크다운 지원" + }, + "compatModeDocUrl": { + "message": "https://docs.deepshare.app/en/faq/compat-mode" + }, + "hardLineBreaksLabel": { + "message": "강제 줄바꿈" + }, + "hardLineBreaksTooltip": { + "message": "단일 줄 바꿈을 강제 줄 바꿈으로 처리" + }, + "hardLineBreaksDocUrl": { + "message": "https://docs.deepshare.app/en/faq/hard-line-breaks" + }, + "disableAutoNumberingLabel": { + "message": "자동 번호 매기기 안 함" + }, + "disableAutoNumberingTooltip": { + "message": "번호가 매겨진 순서 목록을 일반 텍스트로 변환함" + }, + "disableAutoNumberingDocUrl": { + "message": "https://docs.deepshare.app/en/faq/hard-line-breaks" + }, + "getClipboardError": { + "message": "콘텐츠 가져오기에 실패했습니다" + }, + "clipboardPermissionError": { + "message": "콘텐츠 가져오기에 실패했습니다. 클립보드 읽기 권한을 허용해 주세요" + }, + "refreshButton": { + "message": "새로 고침" + }, + "purchaseQuota": { + "message": "할당량 구매" + }, + "purchaseAddonQuota": { + "message": "애드온 할당량 구매" + }, + "purchaseSubscription": { + "message": "구독 구매" + }, + "renewSubscription": { + "message": "구독 갱신" + }, + "manageSubscription": { + "message": "구독 관리" + }, + "pricePageUrl": { + "message": "https://ds.rick216.cn/en/price.html" + }, + "subscriptionExpiredDays": { + "message": "{days}일 전에 만료됨" + }, + "expirationLabel": { + "message": "유효 기간:" + }, + "unknown": { + "message": "알 수 없음" + }, + "templateLabel": { + "message": "Word 템플릿" + }, + "universalTemplate": { + "message": "범용" + }, + "saveAsImageButton": { + "message": "이미지로 저장" + }, + "screenshotInitiated": { + "message": "스크린샷 생성 중..." + }, + "screenshotSuccess": { + "message": "스크린샷 저장됨" + }, + "screenshotFailed": { + "message": "스크린샷 생성에 실패했습니다. 새로 고침을 시도해 보시겠습니까?" + }, + "conversationTooLong": { + "message": "대화가 너무 길어 하나의 이미지로 캡처할 수 없습니다. 메시지를 더 적게 선택하고 다시 시도해 주세요." + }, + "imageCopied": { + "message": "이미지가 클립보드에 복사되었습니다" + }, + "imageCopyFailed": { + "message": "이미지를 클립보드에 복사하는 데 실패했습니다" + }, + "apiKeyError": { + "message": "API 키 오류 또는 만료되었습니다. 이메일을 확인하거나 고객 서비스(contact@deepshare.app)에 문의해 주세요." + }, + "quotaExceededError": { + "message": "할당량 초과. 충전해 주십시오." + }, + "aboutTabTitle": { + "message": "DeepShare 정보" + }, + "versionLabel": { + "message": "버전:" + }, + "documentationLabel": { + "message": "문서:" + }, + "documentationUrl": { + "message": "https://docs.deepshare.app/en" + }, + + "githubLabel": { + "message": "GitHub:" + }, + "developerEmailLabel": { + "message": "개발자 이메일:" + }, + "acknowledgmentText": { + "message": "DeepShare에 대한 제안을 보내주신 모든 분께 감사드립니다! 많은 기능이 실제 사용자의 필요에서 탄생합니다. 함께 생산성을 높이고, 인생에서 진정으로 중요한 일에 더 많은 시간을 할애해 봅시다." + }, + "referenceSources": { + "message": "참조 자료" + }, + "unusedSources": { + "message": "검토했으나 사용되지 않은 자료" + }, + "saveAsMarkdown": { + "message": "Markdown으로 저장" + }, + "otherSettingsTabLabel": { + "message": "기타 설정" + }, + "otherSettingsTitle": { + "message": "기타 설정" + }, + "geminiSettingsTitle": { + "message": "Gemini" + }, + "exportGeminiSourcesLabel": { + "message": "Deep Research 출처 내보내기" + }, + "exportGeminiSourcesHint": { + "message": "Gemini Deep Research 보고서를 내보낼 때 참조 출처 포함" + }, + "includeGeminiChatLinkLabel": { + "message": "채팅 링크 내보내기" + }, + "includeGeminiChatLinkHint": { + "message": "내보낸 콘텐츠에는 원본 대화 링크가 포함됩니다" + }, + "itemsSelected": { + "message": "$COUNT$개 항목 선택됨", + "description": "Gemini 선택 막대에서 선택한 개수", + "placeholders": { + "COUNT": { + "content": "$1" + } + } + }, + "selectAllQuestions": { + "message": "모든 질문 선택" + }, + "confirmExport": { + "message": "내보내기 확인" + }, + "cancelButton": { + "message": "취소" + }, + "roleUser": { + "message": "사용자" + }, + "roleAssistant": { + "message": "어시스턴트" + }, + "exportedViaDeepShare": { + "message": "DeepShare를 통해 내보냄" + }, + "geminiConversation": { + "message": "Gemini 대화" + }, + "sourceConversationLabel": { + "message": "소스" + }, + "languageSettingsTitle": { + "message": "언어" + }, + "languageSelectLabel": { + "message": "표시 언어" + }, + "languageAuto": { + "message": "자동 (브라우저 기본값)" + }, + "languageHint": { + "message": "원하는 표시 언어를 선택하세요" + }, + "networkError": { + "message": "변환에 실패했습니다. 네트워크 연결을 확인해 주세요" + }, + "onboardingWelcomeTitle": { + "message": "DeepShare에 오신 것을 환영합니다!" + }, + "onboardingWelcomeSubtitle": { + "message": "적절한 수식 형식을 선택하고 확장 프로그램을 고정해 보세요" + }, + "onboardingSelectionTitle": { + "message": "수식 형식 선택" + }, + "onboardingSelectionDescription": { + "message": "나중에 확장 프로그램 설정에서 변경할 수 있습니다" + }, + "onboardingMathMLDescription": { + "message": "Microsoft Word 사용자에게 안성맞춤입니다" + }, + "onboardingLaTeXDescription": { + "message": "WPS, MathType 및 Overleaf 사용자용" + }, + "onboardingDollarLatexDescription": { + "message": "Lark, Notion 및 Obsidian 사용자용" + }, + "onboardingRecommended": { + "message": "권장" + }, + "onboardingLearnMoreText": { + "message": "수식 복사에 대해 자세히 알아보기:" + }, + "onboardingLearnMoreLink": { + "message": "튜토리얼 보기" + }, + "onboardingContinue": { + "message": "계속하기" + }, + "onboardingPinTitle": { + "message": "DeepShare를 도구 모음에 고정하세요" + }, + "onboardingPinSubtitle": { + "message": "설정에 빠르게 접근하려면 확장 프로그램을 고정하세요" + }, + "onboardingPinStep1": { + "message": "브라우저 툴바에서 퍼즐 아이콘을 클릭하세요" + }, + "onboardingPinStep2": { + "message": "DeepShare를 찾아 핀 아이콘을 클릭하세요" + }, + "onboardingPinStep3": { + "message": "설정에 액세스하려면 언제든지 DeepShare 아이콘을 클릭하세요" + }, + "onboardingFinish": { + "message": "알겠습니다!" + }, + "onboardingBack": { + "message": "뒤로" + }, + "onboardingLearnMoreUsageText": { + "message": "확장 프로그램 사용법에 대해 자세히 알아보기:" + }, + "onboardingLearnMoreUsageLink": { + "message": "튜토리얼 보기" + }, + "onboardingRefreshHint": { + "message": "처음 사용하시는 경우, 열려 있는 모든 AI 대화를 새로 고침해 주세요." + }, + "scrollCollecting": { + "message": "메시지를 수집하기 위해 스크롤 중입니다. 잠시만 기다려 주세요..." + } +} \ No newline at end of file diff --git a/popup/popup.html b/popup/popup.html index 111ba2e..dc1bdd2 100644 --- a/popup/popup.html +++ b/popup/popup.html @@ -409,6 +409,7 @@

Language

+ Select your preferred display language From 2e282e1fba8b84d6137d023ce7374508782f37d1 Mon Sep 17 00:00:00 2001 From: DerYokoya <133297414+DerYokoya@users.noreply.github.com> Date: Fri, 29 May 2026 22:09:35 -0400 Subject: [PATCH 02/11] doubao typo --- manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifest.json b/manifest.json index a95c26e..5b784b0 100644 --- a/manifest.json +++ b/manifest.json @@ -129,7 +129,7 @@ "lib/katex.min.js", "scripts/formulaConverter.js", "scripts/notifications.js", - "scripts/copyKatex4Doubao.js" + "scripts/copyKatex4DouBao.js" ] }, { From e507b3020067c1b4568a42908beb579dfd3fc651 Mon Sep 17 00:00:00 2001 From: DerYokoya <133297414+DerYokoya@users.noreply.github.com> Date: Fri, 29 May 2026 22:54:14 -0400 Subject: [PATCH 03/11] Copy markdown DeepSeek --- _locales/en/messages.json | 18 ++ _locales/zh_CN/messages.json | 20 +- manifest.json | 3 +- popup/popup.html | 11 + popup/popup.js | 6 + scripts/copyDeepSeekMarkdown.js | 500 ++++++++++++++++++++++++++++++++ 6 files changed, 556 insertions(+), 2 deletions(-) create mode 100644 scripts/copyDeepSeekMarkdown.js diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 1de7fae..c4eaaec 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -529,5 +529,23 @@ }, "scrollCollecting": { "message": "Scrolling to collect messages, please wait..." + }, + "copyAsMarkdown": { + "message": "Copy as Markdown" + }, + "markdownCopied": { + "message": "Markdown copied to clipboard" + }, + "copyConversationMarkdown": { + "message": "Copy All as Markdown" + }, + "featureDisabled": { + "message": "Markdown copy feature is disabled" + }, + "noMessages": { + "message": "No messages found" + }, + "noContent": { + "message": "No content to copy" } } diff --git a/_locales/zh_CN/messages.json b/_locales/zh_CN/messages.json index fb8c8a9..be37b08 100644 --- a/_locales/zh_CN/messages.json +++ b/_locales/zh_CN/messages.json @@ -528,5 +528,23 @@ }, "scrollCollecting": { "message": "正在滚动读取对话内容,请勿操作..." + }, + "copyAsMarkdown": { + "message": "复制为Markdown" + }, + "markdownCopied": { + "message": "Markdown已复制到剪贴板" + }, + "copyConversationMarkdown": { + "message": "复制全部为Markdown" + }, + "featureDisabled": { + "message": "Markdown复制功能已禁用" + }, + "noMessages": { + "message": "未找到消息" + }, + "noContent": { + "message": "没有内容可复制" } -} +} \ No newline at end of file diff --git a/manifest.json b/manifest.json index 5b784b0..adfeb20 100644 --- a/manifest.json +++ b/manifest.json @@ -66,7 +66,8 @@ "scripts/injectDocxButton.js", "scripts/captureDeepSeek.js", "scripts/saveDeepSeekAsDocx.js", - "scripts/injectDeepSeekButtons.js" + "scripts/injectDeepSeekButtons.js", + "scripts/copyDeepSeekMarkdown.js" ] }, { diff --git a/popup/popup.html b/popup/popup.html index dc1bdd2..6237195 100644 --- a/popup/popup.html +++ b/popup/popup.html @@ -439,6 +439,17 @@

Gemini

How long to wait for Gemini to load more history after each auto-scroll (3-10 seconds) + +
+

DeepSeek

+
+
+ + +
+ Show a "Copy as Markdown" button on DeepSeek responses +
+
diff --git a/popup/popup.js b/popup/popup.js index a9cf821..39a2be5 100644 --- a/popup/popup.js +++ b/popup/popup.js @@ -172,6 +172,12 @@ function loadSettings(highlightApiKey = false, forceDocxTab = false) { // Language preference document.getElementById('languageSelect').value = data.preferredLanguage || 'auto'; + // DeepSeek Markdown setting + document.getElementById('copyDeepSeekMarkdownEnabled').checked = settings.copyDeepSeekMarkdownEnabled !== false; + document.getElementById('copyDeepSeekMarkdownEnabled').addEventListener('change', (e) => { + chrome.storage.sync.set({ copyDeepSeekMarkdownEnabled: e.target.checked }); + }); + // If API key is set, check quota and update renewal/purchase links if (data.docxApiKey) { checkQuota(); diff --git a/scripts/copyDeepSeekMarkdown.js b/scripts/copyDeepSeekMarkdown.js new file mode 100644 index 0000000..8b485a4 --- /dev/null +++ b/scripts/copyDeepSeekMarkdown.js @@ -0,0 +1,500 @@ +/** + * DeepSeek Markdown Copy functionality + * Adds a button to copy AI responses as Markdown format + */ + +(function() { + 'use strict'; + console.debug('DeepShare: Initializing DeepSeek Markdown copy functionality'); + + // Load settings + let copyMarkdownSettings = { + enabled: true, + includeUserQuestion: true, + includeAiResponse: true + }; + + function loadSettings() { + chrome.storage.sync.get({ + copyDeepSeekMarkdownEnabled: true, + includeUserQuestion: true, + includeAiResponse: true + }, (settings) => { + copyMarkdownSettings = { + enabled: settings.copyDeepSeekMarkdownEnabled, + includeUserQuestion: settings.includeUserQuestion, + includeAiResponse: settings.includeAiResponse + }; + }); + } + + // Function to extract AI response as markdown + function extractAiResponseAsMarkdown(messageDiv) { + // Find the markdown content + const markdownDiv = messageDiv.querySelector('.ds-markdown'); + if (!markdownDiv) return null; + + // Use the same extraction logic as in injectDocxButton.js + return extractMarkdownFromElement(markdownDiv); + } + + // Extract user question as markdown + function extractUserQuestionAsMarkdown(messageDiv) { + const userElement = messageDiv.querySelector('.fbb737a4'); + if (!userElement) return null; + + // Get text content from user message + const userText = Array.from(userElement.childNodes || []) + .find(node => node.nodeType === Node.TEXT_NODE)?.textContent?.trim(); + + return userText || null; + } + + // Extract markdown from element with formula handling + function extractMarkdownFromElement(element) { + let result = ''; + + const processNode = (node) => { + // Skip checkboxes and selection UI + if (node.nodeType === Node.ELEMENT_NODE) { + if (node.classList && ( + node.classList.contains('message-checkbox-wrapper') || + node.classList.contains('ds-checkbox-wrapper') + )) { + return; + } + } + + // Handle math formulas (KaTeX) + if (node.classList && node.classList.contains('katex')) { + const annotation = node.querySelector('annotation[encoding="application/x-tex"]'); + if (annotation) { + const isDisplay = node.closest('.katex-display'); + const latex = annotation.textContent.trim(); + if (isDisplay) { + result += '\n$$\n' + latex + '\n$$\n'; + } else { + result += '$' + latex + '$'; + } + return; + } + } + + // Handle code blocks + if (node.tagName === 'PRE') { + const code = node.querySelector('code'); + if (code) { + const language = code.className.match(/language-(\w+)/)?.[1] || ''; + result += '\n```' + language + '\n' + code.textContent + '\n```\n'; + } + return; + } + + // Handle inline code + if (node.tagName === 'CODE' && !node.closest('pre')) { + result += '`' + node.textContent + '`'; + return; + } + + // Handle headings + if (node.tagName && /^H[1-6]$/.test(node.tagName)) { + const level = node.tagName[1]; + const headingMark = '#'.repeat(parseInt(level)); + result += '\n' + headingMark + ' ' + node.textContent.trim() + '\n'; + return; + } + + // Handle bold + if (node.tagName === 'STRONG' || node.tagName === 'B') { + result += '**' + node.textContent + '**'; + return; + } + + // Handle italic + if (node.tagName === 'EM' || node.tagName === 'I') { + result += '*' + node.textContent + '*'; + return; + } + + // Handle links + if (node.tagName === 'A') { + const href = node.getAttribute('href'); + const text = node.textContent; + if (href && href !== text) { + result += '[' + text + '](' + href + ')'; + } else { + result += text; + } + return; + } + + // Handle lists + if (node.tagName === 'UL' || node.tagName === 'OL') { + const items = node.querySelectorAll(':scope > li'); + const isOrdered = node.tagName === 'OL'; + items.forEach((item, idx) => { + const prefix = isOrdered ? `${idx + 1}. ` : '- '; + result += prefix + item.textContent.trim() + '\n'; + }); + return; + } + + // Handle paragraphs + if (node.tagName === 'P') { + result += node.textContent.trim() + '\n\n'; + return; + } + + // Handle line breaks + if (node.tagName === 'BR') { + result += '\n'; + return; + } + + // Handle text nodes + if (node.nodeType === Node.TEXT_NODE) { + result += node.textContent; + return; + } + + // Recursively process children + if (node.childNodes && node.childNodes.length > 0) { + node.childNodes.forEach(processNode); + } + }; + + processNode(element); + + // Clean up excessive newlines + result = result.replace(/\n{3,}/g, '\n\n').trim(); + + return result; + } + + // Copy markdown to clipboard + async function copyMarkdownToClipboard(content) { + await navigator.clipboard.writeText(content); + window.showToastNotification(chrome.i18n?.getMessage('markdownCopied') || 'Markdown copied to clipboard', 'success'); + } + + // Main copy function + async function copyAsMarkdown(copyButton) { + if (!copyMarkdownSettings.enabled) { + window.showToastNotification(chrome.i18n?.getMessage('featureDisabled') || 'Markdown copy feature is disabled', 'info'); + return; + } + + // Find the message container + let messageDiv = copyButton.closest('._4f9bf79, ._9663006'); + if (!messageDiv) { + console.error('Could not find message container'); + window.showToastNotification(chrome.i18n?.getMessage('copyFailed') || 'Failed to copy', 'error'); + return; + } + + const isAiMessage = messageDiv.matches('._4f9bf79'); + let markdownContent = ''; + + if (isAiMessage) { + // For AI responses, only copy AI content (user question can be optionally included via selection) + const aiContent = extractAiResponseAsMarkdown(messageDiv); + if (aiContent) { + markdownContent = `## AI Response\n\n${aiContent}`; + } else { + markdownContent = await getContentViaCopyButton(copyButton); + if (markdownContent) { + markdownContent = `## AI Response\n\n${markdownContent}`; + } + } + } else { + // For user messages, copy as is + const userContent = extractUserQuestionAsMarkdown(messageDiv); + if (userContent) { + markdownContent = `## User Question\n\n${userContent}`; + } + } + + if (markdownContent && markdownContent.trim()) { + await copyMarkdownToClipboard(markdownContent); + } else { + window.showToastNotification(chrome.i18n?.getMessage('noContent') || 'No content to copy', 'error'); + } + } + + // Fallback: use copy button to get content + async function getContentViaCopyButton(copyButton) { + let originalClipboardContent = null; + try { + // Backup original clipboard + try { + originalClipboardContent = await navigator.clipboard.readText(); + } catch (e) { + // Ignore - clipboard might be empty or permission not granted + } + + // Click copy button + copyButton.click(); + await new Promise(resolve => setTimeout(resolve, 300)); + + // Get content + const content = await navigator.clipboard.readText(); + + // Restore original + if (originalClipboardContent !== null) { + try { + await navigator.clipboard.writeText(originalClipboardContent); + } catch (e) {} + } + + return content; + } catch (error) { + console.error('Failed to get content via copy button:', error); + return null; + } + } + + // Copy entire conversation as markdown + async function copyConversationAsMarkdown() { + if (!copyMarkdownSettings.enabled) { + window.showToastNotification(chrome.i18n?.getMessage('featureDisabled') || 'Markdown copy feature is disabled', 'info'); + return; + } + + // Find all messages + const messageSelector = '._9663006, ._4f9bf79._43c05b5, ._4f9bf79.d7dc56a8._43c05b5'; + const messages = document.querySelectorAll(messageSelector); + + if (messages.length === 0) { + window.showToastNotification(chrome.i18n?.getMessage('noMessages') || 'No messages found', 'error'); + return; + } + + let conversationContent = ''; + let messageIndex = 1; + + for (const messageDiv of messages) { + const isUserMessage = messageDiv.matches('._9663006') || messageDiv.querySelector('.d29f3d7d'); + + if (isUserMessage && copyMarkdownSettings.includeUserQuestion) { + const userContent = extractUserQuestionAsMarkdown(messageDiv); + if (userContent) { + conversationContent += `## Message ${messageIndex}: User Question\n\n${userContent}\n\n---\n\n`; + messageIndex++; + } + } else if (!isUserMessage && copyMarkdownSettings.includeAiResponse) { + const copyButton = messageDiv.querySelector('.ds-icon-button[role="button"]'); + let aiContent = extractAiResponseAsMarkdown(messageDiv); + + if (!aiContent && copyButton) { + aiContent = await getContentViaCopyButton(copyButton); + } + + if (aiContent) { + conversationContent += `## Message ${messageIndex}: AI Response\n\n${aiContent}\n\n---\n\n`; + messageIndex++; + } + } + } + + if (conversationContent.trim()) { + // Add title + const title = document.querySelector('.afa34042')?.textContent?.trim() || 'DeepSeek Conversation'; + const timestamp = new Date().toLocaleString(); + const fullMarkdown = `# ${title}\n\n*Exported on ${timestamp} via DeepShare*\n\n---\n\n${conversationContent}`; + + await copyMarkdownToClipboard(fullMarkdown); + } else { + window.showToastNotification(chrome.i18n?.getMessage('noContent') || 'No content to copy', 'error'); + } + } + + // Inject markdown copy button next to existing copy button + function injectMarkdownButton(copyButton, container) { + if (container.querySelector('.deepseek-markdown-copy-btn')) return; + + const mdButton = document.createElement('div'); + mdButton.className = copyButton.className + ' deepseek-markdown-copy-btn'; + mdButton.tabIndex = copyButton.tabIndex || -1; + mdButton.setAttribute('role', 'button'); + mdButton.setAttribute('aria-label', chrome.i18n?.getMessage('copyAsMarkdown') || 'Copy as Markdown'); + mdButton.title = chrome.i18n?.getMessage('copyAsMarkdown') || 'Copy as Markdown'; + + // Copy styling + const copyStyle = copyButton.getAttribute('style') || ''; + mdButton.style.cssText = copyStyle; + + // Create icon + const iconHTML = ` +
+
+ + + + + + + +
+ `; + mdButton.innerHTML = iconHTML; + + // Insert after copy button + copyButton.parentNode.insertBefore(mdButton, copyButton.nextSibling); + + // Add tooltip + let tooltipWrapper = null; + let floatingContainer = null; + + mdButton.addEventListener('mouseenter', () => { + const tooltipText = mdButton.title; + if (!tooltipText) return; + + if (!floatingContainer) { + floatingContainer = document.querySelector('.ds-floating-container'); + if (!floatingContainer) { + floatingContainer = document.createElement('div'); + floatingContainer.className = 'ds-floating-container'; + floatingContainer.style.zIndex = '9999'; + document.body.appendChild(floatingContainer); + } + } + + tooltipWrapper = document.createElement('div'); + tooltipWrapper.className = 'ds-floating-position-wrapper ds-theme'; + tooltipWrapper.style.zIndex = '10000'; + + const tooltipElement = document.createElement('div'); + tooltipElement.className = 'ds-tooltip ds-tooltip--s ds-elevated ds-theme'; + tooltipElement.textContent = tooltipText; + + tooltipWrapper.appendChild(tooltipElement); + floatingContainer.appendChild(tooltipWrapper); + + const btnRect = mdButton.getBoundingClientRect(); + tooltipWrapper.style.opacity = '0'; + const tooltipRect = tooltipWrapper.getBoundingClientRect(); + tooltipWrapper.style.opacity = '1'; + + let top = btnRect.bottom + 4; + let left = btnRect.left + (btnRect.width / 2) - (tooltipRect.width / 2); + tooltipWrapper.setAttribute('data-transform-origin', 'bottom'); + + if (left < 5) left = 5; + if ((left + tooltipRect.width) > (window.innerWidth - 5)) { + left = window.innerWidth - tooltipRect.width - 5; + } + + tooltipWrapper.style.top = `${top}px`; + tooltipWrapper.style.left = `${left}px`; + }); + + mdButton.addEventListener('mouseleave', () => { + if (tooltipWrapper) { + tooltipWrapper.remove(); + tooltipWrapper = null; + } + }); + + // Click handler + mdButton.addEventListener('click', async (e) => { + e.stopPropagation(); + + // Disable button during copy + mdButton.style.opacity = '0.5'; + mdButton.style.pointerEvents = 'none'; + + await copyAsMarkdown(copyButton); + + // Re-enable + mdButton.style.opacity = ''; + mdButton.style.pointerEvents = ''; + }); + } + + // Inject conversation markdown copy button in the share panel + function injectConversationMarkdownButton() { + const shareContainer = document.querySelector('._43d222b'); + if (!shareContainer) return; + + const buttonContainer = shareContainer.querySelector('.fab07e97'); + if (!buttonContainer) return; + + // Check if already injected + if (document.getElementById('copy-conversation-md-btn')) return; + + const createLinkButton = buttonContainer.querySelector('.ds-basic-button--primary'); + if (!createLinkButton) return; + + const mdCopyButton = createLinkButton.cloneNode(true); + mdCopyButton.id = 'copy-conversation-md-btn'; + const span = mdCopyButton.querySelector('span'); + span.textContent = chrome.i18n?.getMessage('copyConversationMarkdown') || 'Copy Markdown'; + + // Remove icon if exists + const iconContainer = mdCopyButton.querySelector('.ds-icon'); + if (iconContainer) { + iconContainer.remove(); + } + + mdCopyButton.addEventListener('click', async () => { + await copyConversationAsMarkdown(); + }); + + buttonContainer.insertBefore(mdCopyButton, createLinkButton); + } + + // Observe and inject buttons + function observeAndInject() { + loadSettings(); + + const observer = new MutationObserver((mutations) => { + for (const mutation of mutations) { + if (mutation.type === 'childList') { + // Inject per-message markdown buttons + const buttonContainers = document.querySelectorAll('.ds-flex[style*="align-items"][style*="gap"], div[class*="ds-flex"][style*="align-items: center"]'); + + buttonContainers.forEach(container => { + const buttonGroup = container.querySelector('.ds-flex[style*="align-items"][style*="gap"], div[class*="ds-flex"][style*="align-items"]') || container; + + const copyButtons = buttonGroup.querySelectorAll('.ds-icon-button[role="button"]'); + + copyButtons.forEach(copyBtn => { + // Check if this is an AI response (not user message) + const isUserMessage = copyBtn.closest('.d29f3d7d, [class*="d29f3d7d"]'); + const isAIContainer = copyBtn.closest('._4f9bf79, [class*="_4f9bf79"]'); + const isAIResponse = isAIContainer && !isUserMessage; + + if (isAIResponse && !copyBtn.parentNode?.querySelector('.deepseek-markdown-copy-btn')) { + injectMarkdownButton(copyBtn, buttonGroup); + } + }); + }); + + // Inject conversation-level markdown button + injectConversationMarkdownButton(); + } + } + }); + + observer.observe(document.body, { + childList: true, + subtree: true + }); + + // Listen for settings changes + chrome.storage.onChanged.addListener((changes) => { + if (changes.copyDeepSeekMarkdownEnabled) { + copyMarkdownSettings.enabled = changes.copyDeepSeekMarkdownEnabled.newValue; + } + if (changes.includeUserQuestion) { + copyMarkdownSettings.includeUserQuestion = changes.includeUserQuestion.newValue; + } + if (changes.includeAiResponse) { + copyMarkdownSettings.includeAiResponse = changes.includeAiResponse.newValue; + } + }); + } + + // Initialize + observeAndInject(); +})(); \ No newline at end of file From cd65ef5774143c684fe0a799843e9ea90a438a50 Mon Sep 17 00:00:00 2001 From: DerYokoya <133297414+DerYokoya@users.noreply.github.com> Date: Fri, 29 May 2026 23:07:42 -0400 Subject: [PATCH 04/11] DeepSeek Save as markdown fixed --- popup/popup.html | 4 +- popup/popup.js | 6 - scripts/copyDeepSeekMarkdown.js | 286 ++++++++++++++++++++------------ 3 files changed, 178 insertions(+), 118 deletions(-) diff --git a/popup/popup.html b/popup/popup.html index 6237195..2f566c5 100644 --- a/popup/popup.html +++ b/popup/popup.html @@ -445,9 +445,9 @@

DeepSeek

- +
- Show a "Copy as Markdown" button on DeepSeek responses + Show a "Save as Markdown" button on DeepSeek responses
diff --git a/popup/popup.js b/popup/popup.js index 39a2be5..a9cf821 100644 --- a/popup/popup.js +++ b/popup/popup.js @@ -172,12 +172,6 @@ function loadSettings(highlightApiKey = false, forceDocxTab = false) { // Language preference document.getElementById('languageSelect').value = data.preferredLanguage || 'auto'; - // DeepSeek Markdown setting - document.getElementById('copyDeepSeekMarkdownEnabled').checked = settings.copyDeepSeekMarkdownEnabled !== false; - document.getElementById('copyDeepSeekMarkdownEnabled').addEventListener('change', (e) => { - chrome.storage.sync.set({ copyDeepSeekMarkdownEnabled: e.target.checked }); - }); - // If API key is set, check quota and update renewal/purchase links if (data.docxApiKey) { checkQuota(); diff --git a/scripts/copyDeepSeekMarkdown.js b/scripts/copyDeepSeekMarkdown.js index 8b485a4..63239cb 100644 --- a/scripts/copyDeepSeekMarkdown.js +++ b/scripts/copyDeepSeekMarkdown.js @@ -52,135 +52,203 @@ // Extract markdown from element with formula handling function extractMarkdownFromElement(element) { - let result = ''; - - const processNode = (node) => { - // Skip checkboxes and selection UI - if (node.nodeType === Node.ELEMENT_NODE) { - if (node.classList && ( - node.classList.contains('message-checkbox-wrapper') || - node.classList.contains('ds-checkbox-wrapper') - )) { - return; - } - } - // Handle math formulas (KaTeX) + // Returns inline markdown string for a node and its children + function extractInline(node) { + if (node.nodeType === Node.TEXT_NODE) return node.textContent; + + if (node.nodeType !== Node.ELEMENT_NODE) return ''; + + // Skip UI chrome + if (node.classList && ( + node.classList.contains('message-checkbox-wrapper') || + node.classList.contains('ds-checkbox-wrapper') + )) return ''; + + // KaTeX inline if (node.classList && node.classList.contains('katex')) { - const annotation = node.querySelector('annotation[encoding="application/x-tex"]'); - if (annotation) { - const isDisplay = node.closest('.katex-display'); - const latex = annotation.textContent.trim(); - if (isDisplay) { - result += '\n$$\n' + latex + '\n$$\n'; - } else { - result += '$' + latex + '$'; - } - return; - } + if (node.closest('.katex-display')) return ''; // handled at block level + const ann = node.querySelector('annotation[encoding="application/x-tex"]'); + return ann ? '$' + ann.textContent.trim() + '$' : node.textContent; } + // Skip the MathML sibling inside katex-display (avoid double output) + if (node.classList && node.classList.contains('katex-mathml')) return ''; - // Handle code blocks - if (node.tagName === 'PRE') { - const code = node.querySelector('code'); - if (code) { - const language = code.className.match(/language-(\w+)/)?.[1] || ''; - result += '\n```' + language + '\n' + code.textContent + '\n```\n'; - } - return; - } + const tag = node.tagName; - // Handle inline code - if (node.tagName === 'CODE' && !node.closest('pre')) { - result += '`' + node.textContent + '`'; - return; + if (tag === 'STRONG' || tag === 'B') return '**' + extractChildren(node) + '**'; + if (tag === 'EM' || tag === 'I') return '*' + extractChildren(node) + '*'; + if (tag === 'S' || tag === 'DEL') return '~~' + extractChildren(node) + '~~'; + if (tag === 'CODE' && !node.closest('pre')) return '`' + node.textContent + '`'; + if (tag === 'A') { + const href = node.getAttribute('href'); + const text = extractChildren(node); + return (href && href !== text) ? '[' + text + '](' + href + ')' : text; } + if (tag === 'BR') return '\n'; + + return extractChildren(node); + } - // Handle headings - if (node.tagName && /^H[1-6]$/.test(node.tagName)) { - const level = node.tagName[1]; - const headingMark = '#'.repeat(parseInt(level)); - result += '\n' + headingMark + ' ' + node.textContent.trim() + '\n'; - return; + function extractChildren(node) { + return Array.from(node.childNodes).map(extractInline).join(''); + } + + // Returns block-level markdown string (with surrounding newlines) + function extractBlock(node, depth) { + if (depth === undefined) depth = 0; + + if (node.nodeType === Node.TEXT_NODE) { + const t = node.textContent; + return t.trim() ? t : ''; } - // Handle bold - if (node.tagName === 'STRONG' || node.tagName === 'B') { - result += '**' + node.textContent + '**'; - return; + if (node.nodeType !== Node.ELEMENT_NODE) return ''; + + // Skip UI chrome + if (node.classList && ( + node.classList.contains('message-checkbox-wrapper') || + node.classList.contains('ds-checkbox-wrapper') + )) return ''; + + const tag = node.tagName; + + // KaTeX display block + if (node.classList && node.classList.contains('katex-display')) { + const ann = node.querySelector('annotation[encoding="application/x-tex"]'); + if (ann) return '\n$$\n' + ann.textContent.trim() + '\n$$\n'; + return ''; + } + if (node.classList && (node.classList.contains('katex') || node.classList.contains('katex-mathml'))) { + return extractInline(node); } - // Handle italic - if (node.tagName === 'EM' || node.tagName === 'I') { - result += '*' + node.textContent + '*'; - return; + // Headings + if (/^H[1-6]$/.test(tag)) { + const level = '#'.repeat(parseInt(tag[1])); + return '\n' + level + ' ' + extractChildren(node).trim() + '\n'; } - // Handle links - if (node.tagName === 'A') { - const href = node.getAttribute('href'); - const text = node.textContent; - if (href && href !== text) { - result += '[' + text + '](' + href + ')'; - } else { - result += text; + // Code block + if (tag === 'PRE') { + const code = node.querySelector('code'); + if (code) { + const lang = code.className.match(/language-(\w+)/)?.[1] || ''; + return '\n```' + lang + '\n' + code.textContent.replace(/\n$/, '') + '\n```\n'; } - return; + return '\n```\n' + node.textContent + '\n```\n'; } - // Handle lists - if (node.tagName === 'UL' || node.tagName === 'OL') { - const items = node.querySelectorAll(':scope > li'); - const isOrdered = node.tagName === 'OL'; - items.forEach((item, idx) => { - const prefix = isOrdered ? `${idx + 1}. ` : '- '; - result += prefix + item.textContent.trim() + '\n'; - }); - return; + // Blockquote + if (tag === 'BLOCKQUOTE') { + const inner = extractChildren(node).trim(); + return '\n' + inner.split('\n').map(l => '> ' + l).join('\n') + '\n'; } - // Handle paragraphs - if (node.tagName === 'P') { - result += node.textContent.trim() + '\n\n'; - return; + // Horizontal rule + if (tag === 'HR') return '\n---\n'; + + // Table + if (tag === 'TABLE') { + let out = '\n'; + const rows = Array.from(node.querySelectorAll('tr')); + rows.forEach((row, i) => { + const cells = Array.from(row.querySelectorAll('th, td')); + out += '| ' + cells.map(c => extractChildren(c).trim()).join(' | ') + ' |\n'; + if (i === 0) { + out += '| ' + cells.map(() => '---').join(' | ') + ' |\n'; + } + }); + return out; } + if (tag === 'THEAD' || tag === 'TBODY' || tag === 'TR' || tag === 'TH' || tag === 'TD') return ''; - // Handle line breaks - if (node.tagName === 'BR') { - result += '\n'; - return; + // Lists + if (tag === 'UL' || tag === 'OL') { + return extractList(node, depth); } + if (tag === 'LI') return ''; // handled inside extractList - // Handle text nodes - if (node.nodeType === Node.TEXT_NODE) { - result += node.textContent; - return; + // Paragraph + if (tag === 'P') { + return '\n' + extractChildren(node).trim() + '\n'; } - // Recursively process children - if (node.childNodes && node.childNodes.length > 0) { - node.childNodes.forEach(processNode); + // Inline elements inside block context — emit inline + if (['STRONG','B','EM','I','S','DEL','CODE','A','SPAN','BR'].includes(tag)) { + return extractInline(node); } - }; - processNode(element); + // Generic container — recurse + return Array.from(node.childNodes).map(c => extractBlock(c, depth)).join(''); + } - // Clean up excessive newlines - result = result.replace(/\n{3,}/g, '\n\n').trim(); + function extractList(listNode, depth) { + const isOrdered = listNode.tagName === 'OL'; + const indent = ' '.repeat(depth); + let out = '\n'; + let orderedIdx = 1; + Array.from(listNode.children).forEach(li => { + if (li.tagName !== 'LI') return; + const prefix = isOrdered ? (orderedIdx++) + '. ' : '* '; + + // Separate inline content from nested lists + let inlinePart = ''; + let nestedPart = ''; + Array.from(li.childNodes).forEach(child => { + if (child.nodeType === Node.ELEMENT_NODE && (child.tagName === 'UL' || child.tagName === 'OL')) { + nestedPart += extractList(child, depth + 1); + } else { + inlinePart += extractInline(child); + } + }); - return result; + out += indent + prefix + inlinePart.trim() + '\n'; + if (nestedPart) out += nestedPart; + }); + return out; + } + + const raw = Array.from(element.childNodes).map(c => extractBlock(c, 0)).join(''); + return raw.replace(/\n{3,}/g, '\n\n').trim(); + } + + // Save markdown content as a .md file + function saveMarkdownFile(content) { + const filename = generateFilename(content) + '.md'; + const blob = new Blob([content], { type: 'text/markdown;charset=utf-8' }); + const url = URL.createObjectURL(blob); + const link = document.createElement('a'); + link.href = url; + link.download = filename; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + URL.revokeObjectURL(url); + window.showToastNotification(chrome.i18n?.getMessage('markdownSaved') || 'Saved as Markdown file', 'success'); } - // Copy markdown to clipboard - async function copyMarkdownToClipboard(content) { - await navigator.clipboard.writeText(content); - window.showToastNotification(chrome.i18n?.getMessage('markdownCopied') || 'Markdown copied to clipboard', 'success'); + function generateFilename(content) { + const now = new Date(); + const timestamp = now.toLocaleString('zh-CN', { + year: 'numeric', month: '2-digit', day: '2-digit', + hour: '2-digit', minute: '2-digit', second: '2-digit', + hour12: false + }).replace(/[\/\s:]/g, '-').replace(',', ''); + + if (!content) return `deepseek_${timestamp}`; + const lines = content.split('\n').filter(l => l.trim().length > 0); + let firstLine = ''; + if (lines.length > 0) { + firstLine = lines[0].replace(/^#+\s*/, '').replace(/[^a-zA-Z0-9_\u4e00-\u9fa5]/g, '').substring(0, 15).trim(); + } + return `${firstLine || 'deepseek'}_${timestamp}`; } - // Main copy function - async function copyAsMarkdown(copyButton) { + // Main save function + async function saveAsMarkdown(copyButton) { if (!copyMarkdownSettings.enabled) { - window.showToastNotification(chrome.i18n?.getMessage('featureDisabled') || 'Markdown copy feature is disabled', 'info'); + window.showToastNotification(chrome.i18n?.getMessage('featureDisabled') || 'Save as Markdown feature is disabled', 'info'); return; } @@ -188,7 +256,7 @@ let messageDiv = copyButton.closest('._4f9bf79, ._9663006'); if (!messageDiv) { console.error('Could not find message container'); - window.showToastNotification(chrome.i18n?.getMessage('copyFailed') || 'Failed to copy', 'error'); + window.showToastNotification(chrome.i18n?.getMessage('saveFailed') || 'Failed to save', 'error'); return; } @@ -196,7 +264,6 @@ let markdownContent = ''; if (isAiMessage) { - // For AI responses, only copy AI content (user question can be optionally included via selection) const aiContent = extractAiResponseAsMarkdown(messageDiv); if (aiContent) { markdownContent = `## AI Response\n\n${aiContent}`; @@ -207,7 +274,6 @@ } } } else { - // For user messages, copy as is const userContent = extractUserQuestionAsMarkdown(messageDiv); if (userContent) { markdownContent = `## User Question\n\n${userContent}`; @@ -215,9 +281,9 @@ } if (markdownContent && markdownContent.trim()) { - await copyMarkdownToClipboard(markdownContent); + saveMarkdownFile(markdownContent); } else { - window.showToastNotification(chrome.i18n?.getMessage('noContent') || 'No content to copy', 'error'); + window.showToastNotification(chrome.i18n?.getMessage('noContent') || 'No content to save', 'error'); } } @@ -254,9 +320,9 @@ } // Copy entire conversation as markdown - async function copyConversationAsMarkdown() { + async function saveConversationAsMarkdown() { if (!copyMarkdownSettings.enabled) { - window.showToastNotification(chrome.i18n?.getMessage('featureDisabled') || 'Markdown copy feature is disabled', 'info'); + window.showToastNotification(chrome.i18n?.getMessage('featureDisabled') || 'Save as Markdown feature is disabled', 'info'); return; } @@ -302,9 +368,9 @@ const timestamp = new Date().toLocaleString(); const fullMarkdown = `# ${title}\n\n*Exported on ${timestamp} via DeepShare*\n\n---\n\n${conversationContent}`; - await copyMarkdownToClipboard(fullMarkdown); + saveMarkdownFile(fullMarkdown); } else { - window.showToastNotification(chrome.i18n?.getMessage('noContent') || 'No content to copy', 'error'); + window.showToastNotification(chrome.i18n?.getMessage('noContent') || 'No content to save', 'error'); } } @@ -316,8 +382,8 @@ mdButton.className = copyButton.className + ' deepseek-markdown-copy-btn'; mdButton.tabIndex = copyButton.tabIndex || -1; mdButton.setAttribute('role', 'button'); - mdButton.setAttribute('aria-label', chrome.i18n?.getMessage('copyAsMarkdown') || 'Copy as Markdown'); - mdButton.title = chrome.i18n?.getMessage('copyAsMarkdown') || 'Copy as Markdown'; + mdButton.setAttribute('aria-label', chrome.i18n?.getMessage('saveAsMarkdown') || 'Save as Markdown'); + mdButton.title = chrome.i18n?.getMessage('saveAsMarkdown') || 'Save as Markdown'; // Copy styling const copyStyle = copyButton.getAttribute('style') || ''; @@ -403,7 +469,7 @@ mdButton.style.opacity = '0.5'; mdButton.style.pointerEvents = 'none'; - await copyAsMarkdown(copyButton); + await saveAsMarkdown(copyButton); // Re-enable mdButton.style.opacity = ''; @@ -428,7 +494,7 @@ const mdCopyButton = createLinkButton.cloneNode(true); mdCopyButton.id = 'copy-conversation-md-btn'; const span = mdCopyButton.querySelector('span'); - span.textContent = chrome.i18n?.getMessage('copyConversationMarkdown') || 'Copy Markdown'; + span.textContent = chrome.i18n?.getMessage('saveConversationMarkdown') || 'Save as Markdown'; // Remove icon if exists const iconContainer = mdCopyButton.querySelector('.ds-icon'); @@ -437,7 +503,7 @@ } mdCopyButton.addEventListener('click', async () => { - await copyConversationAsMarkdown(); + await saveConversationAsMarkdown(); }); buttonContainer.insertBefore(mdCopyButton, createLinkButton); From 774abf6c9365427dceff99b4ba0d2c47bf6153e0 Mon Sep 17 00:00:00 2001 From: DerYokoya <133297414+DerYokoya@users.noreply.github.com> Date: Fri, 29 May 2026 23:12:09 -0400 Subject: [PATCH 05/11] portugueses and taiwan chinese --- _locales/pt_BR/messages.json | 18 ++++++++++++++++++ _locales/pt_PT/messages.json | 18 ++++++++++++++++++ _locales/zh_TW/messages.json | 18 ++++++++++++++++++ 3 files changed, 54 insertions(+) diff --git a/_locales/pt_BR/messages.json b/_locales/pt_BR/messages.json index 1f0b4a1..7da3a0d 100644 --- a/_locales/pt_BR/messages.json +++ b/_locales/pt_BR/messages.json @@ -528,5 +528,23 @@ }, "scrollCollecting": { "message": "Rolando para coletar mensagens, por favor aguarde..." + }, + "copyAsMarkdown": { + "message": "Copiar como Markdown" + }, + "markdownCopied": { + "message": "Markdown copiado para a área de transferência" + }, + "copyConversationMarkdown": { + "message": "Copiar tudo como Markdown" + }, + "featureDisabled": { + "message": "O recurso de cópia Markdown está desativado" + }, + "noMessages": { + "message": "Nenhuma mensagem encontrada" + }, + "noContent": { + "message": "Nenhum conteúdo para copiar" } } diff --git a/_locales/pt_PT/messages.json b/_locales/pt_PT/messages.json index 8ba06b5..0635b99 100644 --- a/_locales/pt_PT/messages.json +++ b/_locales/pt_PT/messages.json @@ -528,5 +528,23 @@ }, "scrollCollecting": { "message": "A percorrer para recolher mensagens, por favor aguarde..." + }, + "copyAsMarkdown": { + "message": "Copiar como Markdown" + }, + "markdownCopied": { + "message": "Markdown copiado para a área de transferência" + }, + "copyConversationMarkdown": { + "message": "Copiar tudo como Markdown" + }, + "featureDisabled": { + "message": "A funcionalidade de cópia Markdown está desativada" + }, + "noMessages": { + "message": "Nenhuma mensagem encontrada" + }, + "noContent": { + "message": "Nenhum conteúdo para copiar" } } diff --git a/_locales/zh_TW/messages.json b/_locales/zh_TW/messages.json index ee113e8..c169e63 100644 --- a/_locales/zh_TW/messages.json +++ b/_locales/zh_TW/messages.json @@ -510,5 +510,23 @@ }, "documentationUrl": { "message": "https://docs.deepshare.app/en" + }, + "aboutTabTitle": { + "message": "關於 DeepShare" + }, + "versionLabel": { + "message": "版本:" + }, + "documentationLabel": { + "message": "文件:" + }, + "githubLabel": { + "message": "GitHub:" + }, + "developerEmailLabel": { + "message": "開發者電子郵件:" + }, + "acknowledgmentText": { + "message": "感謝所有為 DeepShare 提出建議的人!許多功能都源自真實用戶的需求。讓我們一起提升生產力,為生活中真正重要的事情節省時間。" } } From 041fb2b06c313d49b1c0b2da525eceadca6fb6d8 Mon Sep 17 00:00:00 2001 From: DerYokoya <133297414+DerYokoya@users.noreply.github.com> Date: Fri, 29 May 2026 23:13:45 -0400 Subject: [PATCH 06/11] spanish, french, japanese --- _locales/es/messages.json | 18 ++++++++++++++++++ _locales/fr/messages.json | 18 ++++++++++++++++++ _locales/ja/messages.json | 18 ++++++++++++++++++ 3 files changed, 54 insertions(+) diff --git a/_locales/es/messages.json b/_locales/es/messages.json index 1821ee8..0b8aba6 100644 --- a/_locales/es/messages.json +++ b/_locales/es/messages.json @@ -528,5 +528,23 @@ }, "scrollCollecting": { "message": "Desplazando para recopilar mensajes, por favor espere..." + }, + "copyAsMarkdown": { + "message": "Copiar como Markdown" + }, + "markdownCopied": { + "message": "Markdown copiado al portapapeles" + }, + "copyConversationMarkdown": { + "message": "Copiar todo como Markdown" + }, + "featureDisabled": { + "message": "La función de copia de Markdown está desactivada" + }, + "noMessages": { + "message": "No se encontraron mensajes" + }, + "noContent": { + "message": "No hay contenido para copiar" } } diff --git a/_locales/fr/messages.json b/_locales/fr/messages.json index a59efcf..fd34372 100644 --- a/_locales/fr/messages.json +++ b/_locales/fr/messages.json @@ -529,5 +529,23 @@ }, "scrollCollecting": { "message": "Défilement en cours pour collecter les messages, veuillez patienter..." + }, + "copyAsMarkdown": { + "message": "Copier en Markdown" + }, + "markdownCopied": { + "message": "Markdown copié dans le presse-papiers" + }, + "copyConversationMarkdown": { + "message": "Tout copier en Markdown" + }, + "featureDisabled": { + "message": "La fonction de copie Markdown est désactivée" + }, + "noMessages": { + "message": "Aucun message trouvé" + }, + "noContent": { + "message": "Aucun contenu à copier" } } diff --git a/_locales/ja/messages.json b/_locales/ja/messages.json index ee1c63c..2323fe6 100644 --- a/_locales/ja/messages.json +++ b/_locales/ja/messages.json @@ -528,5 +528,23 @@ }, "scrollCollecting": { "message": "スクロールしてメッセージを収集しています。しばらくお待ちください..." + }, + "copyAsMarkdown": { + "message": "Markdownとしてコピー" + }, + "markdownCopied": { + "message": "Markdownをクリップボードにコピーしました" + }, + "copyConversationMarkdown": { + "message": "すべてをMarkdownとしてコピー" + }, + "featureDisabled": { + "message": "Markdownコピー機能は無効になっています" + }, + "noMessages": { + "message": "メッセージが見つかりません" + }, + "noContent": { + "message": "コピーする内容がありません" } } From 22ed0beeb90c572792882a08b3bdb76f17db9903 Mon Sep 17 00:00:00 2001 From: DerYokoya <133297414+DerYokoya@users.noreply.github.com> Date: Fri, 29 May 2026 23:17:18 -0400 Subject: [PATCH 07/11] german --- _locales/de/messages.json | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/_locales/de/messages.json b/_locales/de/messages.json index 5015072..9cbc064 100644 --- a/_locales/de/messages.json +++ b/_locales/de/messages.json @@ -529,5 +529,23 @@ }, "scrollCollecting": { "message": "Nachrichten werden gesammelt, bitte warten..." + }, + "copyAsMarkdown": { + "message": "Als Markdown kopieren" + }, + "markdownCopied": { + "message": "Markdown in die Zwischenablage kopiert" + }, + "copyConversationMarkdown": { + "message": "Alles als Markdown kopieren" + }, + "featureDisabled": { + "message": "Die Markdown-Kopierfunktion ist deaktiviert" + }, + "noMessages": { + "message": "Keine Nachrichten gefunden" + }, + "noContent": { + "message": "Kein Inhalt zum Kopieren" } } From 0ef1d88f0968d2c67def2e97f9d8d59e20faa70d Mon Sep 17 00:00:00 2001 From: DerYokoya <133297414+DerYokoya@users.noreply.github.com> Date: Fri, 29 May 2026 23:18:43 -0400 Subject: [PATCH 08/11] Removed outdated feature that was for testing --- scripts/copyDeepSeekMarkdown.js | 198 -------------------------------- 1 file changed, 198 deletions(-) diff --git a/scripts/copyDeepSeekMarkdown.js b/scripts/copyDeepSeekMarkdown.js index 63239cb..cef02aa 100644 --- a/scripts/copyDeepSeekMarkdown.js +++ b/scripts/copyDeepSeekMarkdown.js @@ -245,79 +245,7 @@ return `${firstLine || 'deepseek'}_${timestamp}`; } - // Main save function - async function saveAsMarkdown(copyButton) { - if (!copyMarkdownSettings.enabled) { - window.showToastNotification(chrome.i18n?.getMessage('featureDisabled') || 'Save as Markdown feature is disabled', 'info'); - return; - } - - // Find the message container - let messageDiv = copyButton.closest('._4f9bf79, ._9663006'); - if (!messageDiv) { - console.error('Could not find message container'); - window.showToastNotification(chrome.i18n?.getMessage('saveFailed') || 'Failed to save', 'error'); - return; - } - - const isAiMessage = messageDiv.matches('._4f9bf79'); - let markdownContent = ''; - - if (isAiMessage) { - const aiContent = extractAiResponseAsMarkdown(messageDiv); - if (aiContent) { - markdownContent = `## AI Response\n\n${aiContent}`; - } else { - markdownContent = await getContentViaCopyButton(copyButton); - if (markdownContent) { - markdownContent = `## AI Response\n\n${markdownContent}`; - } - } - } else { - const userContent = extractUserQuestionAsMarkdown(messageDiv); - if (userContent) { - markdownContent = `## User Question\n\n${userContent}`; - } - } - - if (markdownContent && markdownContent.trim()) { - saveMarkdownFile(markdownContent); - } else { - window.showToastNotification(chrome.i18n?.getMessage('noContent') || 'No content to save', 'error'); - } - } - - // Fallback: use copy button to get content - async function getContentViaCopyButton(copyButton) { - let originalClipboardContent = null; - try { - // Backup original clipboard - try { - originalClipboardContent = await navigator.clipboard.readText(); - } catch (e) { - // Ignore - clipboard might be empty or permission not granted - } - - // Click copy button - copyButton.click(); - await new Promise(resolve => setTimeout(resolve, 300)); - - // Get content - const content = await navigator.clipboard.readText(); - - // Restore original - if (originalClipboardContent !== null) { - try { - await navigator.clipboard.writeText(originalClipboardContent); - } catch (e) {} - } - return content; - } catch (error) { - console.error('Failed to get content via copy button:', error); - return null; - } - } // Copy entire conversation as markdown async function saveConversationAsMarkdown() { @@ -348,12 +276,8 @@ messageIndex++; } } else if (!isUserMessage && copyMarkdownSettings.includeAiResponse) { - const copyButton = messageDiv.querySelector('.ds-icon-button[role="button"]'); let aiContent = extractAiResponseAsMarkdown(messageDiv); - if (!aiContent && copyButton) { - aiContent = await getContentViaCopyButton(copyButton); - } if (aiContent) { conversationContent += `## Message ${messageIndex}: AI Response\n\n${aiContent}\n\n---\n\n`; @@ -374,108 +298,6 @@ } } - // Inject markdown copy button next to existing copy button - function injectMarkdownButton(copyButton, container) { - if (container.querySelector('.deepseek-markdown-copy-btn')) return; - - const mdButton = document.createElement('div'); - mdButton.className = copyButton.className + ' deepseek-markdown-copy-btn'; - mdButton.tabIndex = copyButton.tabIndex || -1; - mdButton.setAttribute('role', 'button'); - mdButton.setAttribute('aria-label', chrome.i18n?.getMessage('saveAsMarkdown') || 'Save as Markdown'); - mdButton.title = chrome.i18n?.getMessage('saveAsMarkdown') || 'Save as Markdown'; - - // Copy styling - const copyStyle = copyButton.getAttribute('style') || ''; - mdButton.style.cssText = copyStyle; - - // Create icon - const iconHTML = ` -
-
- - - - - - - -
- `; - mdButton.innerHTML = iconHTML; - - // Insert after copy button - copyButton.parentNode.insertBefore(mdButton, copyButton.nextSibling); - - // Add tooltip - let tooltipWrapper = null; - let floatingContainer = null; - - mdButton.addEventListener('mouseenter', () => { - const tooltipText = mdButton.title; - if (!tooltipText) return; - - if (!floatingContainer) { - floatingContainer = document.querySelector('.ds-floating-container'); - if (!floatingContainer) { - floatingContainer = document.createElement('div'); - floatingContainer.className = 'ds-floating-container'; - floatingContainer.style.zIndex = '9999'; - document.body.appendChild(floatingContainer); - } - } - - tooltipWrapper = document.createElement('div'); - tooltipWrapper.className = 'ds-floating-position-wrapper ds-theme'; - tooltipWrapper.style.zIndex = '10000'; - - const tooltipElement = document.createElement('div'); - tooltipElement.className = 'ds-tooltip ds-tooltip--s ds-elevated ds-theme'; - tooltipElement.textContent = tooltipText; - - tooltipWrapper.appendChild(tooltipElement); - floatingContainer.appendChild(tooltipWrapper); - - const btnRect = mdButton.getBoundingClientRect(); - tooltipWrapper.style.opacity = '0'; - const tooltipRect = tooltipWrapper.getBoundingClientRect(); - tooltipWrapper.style.opacity = '1'; - - let top = btnRect.bottom + 4; - let left = btnRect.left + (btnRect.width / 2) - (tooltipRect.width / 2); - tooltipWrapper.setAttribute('data-transform-origin', 'bottom'); - - if (left < 5) left = 5; - if ((left + tooltipRect.width) > (window.innerWidth - 5)) { - left = window.innerWidth - tooltipRect.width - 5; - } - - tooltipWrapper.style.top = `${top}px`; - tooltipWrapper.style.left = `${left}px`; - }); - - mdButton.addEventListener('mouseleave', () => { - if (tooltipWrapper) { - tooltipWrapper.remove(); - tooltipWrapper = null; - } - }); - - // Click handler - mdButton.addEventListener('click', async (e) => { - e.stopPropagation(); - - // Disable button during copy - mdButton.style.opacity = '0.5'; - mdButton.style.pointerEvents = 'none'; - - await saveAsMarkdown(copyButton); - - // Re-enable - mdButton.style.opacity = ''; - mdButton.style.pointerEvents = ''; - }); - } // Inject conversation markdown copy button in the share panel function injectConversationMarkdownButton() { @@ -516,26 +338,6 @@ const observer = new MutationObserver((mutations) => { for (const mutation of mutations) { if (mutation.type === 'childList') { - // Inject per-message markdown buttons - const buttonContainers = document.querySelectorAll('.ds-flex[style*="align-items"][style*="gap"], div[class*="ds-flex"][style*="align-items: center"]'); - - buttonContainers.forEach(container => { - const buttonGroup = container.querySelector('.ds-flex[style*="align-items"][style*="gap"], div[class*="ds-flex"][style*="align-items"]') || container; - - const copyButtons = buttonGroup.querySelectorAll('.ds-icon-button[role="button"]'); - - copyButtons.forEach(copyBtn => { - // Check if this is an AI response (not user message) - const isUserMessage = copyBtn.closest('.d29f3d7d, [class*="d29f3d7d"]'); - const isAIContainer = copyBtn.closest('._4f9bf79, [class*="_4f9bf79"]'); - const isAIResponse = isAIContainer && !isUserMessage; - - if (isAIResponse && !copyBtn.parentNode?.querySelector('.deepseek-markdown-copy-btn')) { - injectMarkdownButton(copyBtn, buttonGroup); - } - }); - }); - // Inject conversation-level markdown button injectConversationMarkdownButton(); } From bb19eb9244ba8ffe76da6fd6da44d12040938ab7 Mon Sep 17 00:00:00 2001 From: DerYokoya <133297414+DerYokoya@users.noreply.github.com> Date: Fri, 29 May 2026 23:37:32 -0400 Subject: [PATCH 09/11] Restore button fix and DeepSeek save as markdown --- scripts/copyDeepSeekMarkdown.js | 44 +-- styles/style.css | 462 ++++++++++++++++++++++++++++++++ 2 files changed, 464 insertions(+), 42 deletions(-) diff --git a/scripts/copyDeepSeekMarkdown.js b/scripts/copyDeepSeekMarkdown.js index cef02aa..9e4b5e4 100644 --- a/scripts/copyDeepSeekMarkdown.js +++ b/scripts/copyDeepSeekMarkdown.js @@ -7,27 +7,6 @@ 'use strict'; console.debug('DeepShare: Initializing DeepSeek Markdown copy functionality'); - // Load settings - let copyMarkdownSettings = { - enabled: true, - includeUserQuestion: true, - includeAiResponse: true - }; - - function loadSettings() { - chrome.storage.sync.get({ - copyDeepSeekMarkdownEnabled: true, - includeUserQuestion: true, - includeAiResponse: true - }, (settings) => { - copyMarkdownSettings = { - enabled: settings.copyDeepSeekMarkdownEnabled, - includeUserQuestion: settings.includeUserQuestion, - includeAiResponse: settings.includeAiResponse - }; - }); - } - // Function to extract AI response as markdown function extractAiResponseAsMarkdown(messageDiv) { // Find the markdown content @@ -249,11 +228,6 @@ // Copy entire conversation as markdown async function saveConversationAsMarkdown() { - if (!copyMarkdownSettings.enabled) { - window.showToastNotification(chrome.i18n?.getMessage('featureDisabled') || 'Save as Markdown feature is disabled', 'info'); - return; - } - // Find all messages const messageSelector = '._9663006, ._4f9bf79._43c05b5, ._4f9bf79.d7dc56a8._43c05b5'; const messages = document.querySelectorAll(messageSelector); @@ -269,13 +243,13 @@ for (const messageDiv of messages) { const isUserMessage = messageDiv.matches('._9663006') || messageDiv.querySelector('.d29f3d7d'); - if (isUserMessage && copyMarkdownSettings.includeUserQuestion) { + if (isUserMessage) { const userContent = extractUserQuestionAsMarkdown(messageDiv); if (userContent) { conversationContent += `## Message ${messageIndex}: User Question\n\n${userContent}\n\n---\n\n`; messageIndex++; } - } else if (!isUserMessage && copyMarkdownSettings.includeAiResponse) { + } else if (!isUserMessage) { let aiContent = extractAiResponseAsMarkdown(messageDiv); @@ -333,8 +307,6 @@ // Observe and inject buttons function observeAndInject() { - loadSettings(); - const observer = new MutationObserver((mutations) => { for (const mutation of mutations) { if (mutation.type === 'childList') { @@ -349,18 +321,6 @@ subtree: true }); - // Listen for settings changes - chrome.storage.onChanged.addListener((changes) => { - if (changes.copyDeepSeekMarkdownEnabled) { - copyMarkdownSettings.enabled = changes.copyDeepSeekMarkdownEnabled.newValue; - } - if (changes.includeUserQuestion) { - copyMarkdownSettings.includeUserQuestion = changes.includeUserQuestion.newValue; - } - if (changes.includeAiResponse) { - copyMarkdownSettings.includeAiResponse = changes.includeAiResponse.newValue; - } - }); } // Initialize diff --git a/styles/style.css b/styles/style.css index b565f8b..41c91b8 100644 --- a/styles/style.css +++ b/styles/style.css @@ -1,3 +1,376 @@ +:root { + --modal-bg: #ffffff; + --modal-text: #333333; + --modal-border: #eeeeee; + --button-color: rgb(139, 139, 139); + --button-hover-bg: rgba(77, 107, 254, 0.1); + --button-hover-color: rgb(77, 107, 254); + --modal-overlay: rgba(0, 0, 0, 0.5); +} + +@media (prefers-color-scheme: dark) { + :root { + --modal-bg: #1e1e1e; + --modal-text: #e0e0e0; + --modal-border: #333333; + --button-color: rgb(173, 178, 184); + --button-hover-bg: rgba(77, 107, 254, 0.2); + --button-hover-color: rgb(107, 137, 254); + --modal-overlay: rgba(0, 0, 0, 0.7); + } +} + +/* 分享按钮样式 */ +.deepseek-share-btn { + display: flex; + align-items: center; + gap: 10px; + /* 增加按钮之间的间距 */ + margin-right: 8px; + /* 添加右边距 */ +} + +/* 按钮通用样式 */ +.deepseek-share-btn .share-button, +.deepseek-share-btn .select-button { + background: none; + border: none; + cursor: pointer; + padding: 6px; + color: rgb(139, 139, 139); + border-radius: 6px; + display: flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + transition: all 0.2s ease; +} + +.deepseek-share-btn .share-button:hover, +.deepseek-share-btn .select-button:hover { + background: rgba(139, 139, 139, 0.1); + color: rgb(139, 139, 139); + transform: scale(1.05); +} + +.deepseek-share-btn .share-button:active { + transform: scale(0.95); +} + +/* 深色模式覆盖 */ +@media (prefers-color-scheme: dark) { + + .deepseek-share-btn .share-button, + .deepseek-share-btn .select-button { + color: rgb(173, 178, 184); + /* 深色模式下的颜色 */ + } + + .deepseek-share-btn .share-button:hover, + .deepseek-share-btn .select-button:hover { + background: rgba(173, 178, 184, 0.1); + /* 深色模式下的悬停背景色 */ + color: rgb(98, 99, 99); + } +} + +.select-button svg { + width: 23px; + /* 增加SVG图标大小 */ + height: 23px; + min-width: 23px; + /* 确保最小尺寸 */ + min-height: 23px; +} + +.f8d1e4c0 { + position: relative; +} + +.deepseek-share-modal { + display: none; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: var(--modal-overlay); + z-index: 1000; +} + +.deepseek-share-modal .modal-content { + background: var(--modal-bg); + color: var(--modal-text); + width: 46%; + min-width: 400px; + max-width: 1200px; + margin: 30px auto; + /* 减小顶部边距 */ + border-radius: 12px; + /* 增加圆角 */ + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); +} + +.modal-header { + padding: 6px 20px; + border-bottom: 1px solid var(--modal-border); + display: flex; + justify-content: space-between; + align-items: center; +} + +.header-buttons { + display: flex; + align-items: center; + gap: 4px; +} + +.header-btn { + width: 32px; + height: 32px; + background: none; + border: none; + padding: 6px; + cursor: pointer; + color: var(--button-color); + display: flex; + align-items: center; + justify-content: center; + border-radius: 6px; + transition: all 0.2s ease; +} + +.header-btn:hover { + background: var(--button-hover-bg); + color: var(--button-hover-color); +} + +.header-btn:active { + transform: scale(0.95); +} + +.modal-body { + padding: 17px 20px 20px 20px; + max-height: 80vh; + /* 增加最大高度 */ + overflow-y: auto; +} + +#conversation-content { + white-space: pre-wrap; + word-break: break-all; + color: var(--modal-text); +} + +/* 标签页样式 */ +.tab-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 20px; +} + +.tab-container { + display: flex; + gap: 2px; + border-bottom: 1px solid var(--modal-border); + position: relative; +} + +.tab-btn { + padding: 6px; + background: none; + border: none; + color: var(--modal-text); + cursor: pointer; + opacity: 0.7; + transition: all 0.2s; + min-width: 60px; + text-align: center; + position: relative; +} + +.tab-btn.active { + opacity: 1; +} + +.tab-indicator { + position: absolute; + bottom: -1px; + left: 0; + height: 2px; + background-color: var(--button-hover-color); + transition: transform 0.3s ease; + width: 60px; +} + +.tab-btn[data-tab="text"].active~.tab-indicator { + transform: translateX(calc(100% + 2px)); +} + +.tab-btn[data-tab="image"].active~.tab-indicator { + transform: translateX(0); +} + +/* 面板样式 */ +.tab-panel { + display: none; +} + +.tab-panel.active { + display: block; +} + +/* 文本容器样式 */ +#text-panel { + min-height: 200px; + max-height: 70vh; + overflow-y: auto; + border: 1px solid var(--modal-border); + border-radius: 8px; + background: var(--modal-bg); + position: relative; +} + +#conversation-content { + white-space: pre-wrap; + word-break: break-all; + color: var(--modal-text); + padding: 16px; + margin: 0; +} + +/* 图片容器样式 */ +.image-container { + min-height: 200px; + max-height: 70vh; + /* 增加图片容器最大高度 */ + overflow-y: auto; + /* 移除底部间距 */ + border: 1px solid var(--modal-border); + border-radius: 8px; + /* 增加图片容器圆角 */ + background: var(--modal-bg); + position: relative; +} + +#conversation-image { + width: 100%; + height: auto; + display: block; +} + +.image-loading { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + color: var(--modal-text); +} + +.action-buttons { + display: flex; + gap: 8px; +} + +/* 按钮样式 */ +.download-btn, +.copy-btn { + padding: 4px 10px; + font-size: 12px; + background: var(--button-hover-bg); + color: var(--button-hover-color); + border: 1px solid var(--button-hover-color); + border-radius: 4px; + cursor: pointer; + transition: all 0.2s; +} + +.download-btn:hover, +.copy-btn:hover { + background: var(--button-hover-color); + color: white; + opacity: 0.9; +} + +/* 对话选择复选框样式 */ +.message-checkbox-wrapper { + position: absolute; + top: 3px; + right: 3px; +} + +.message-checkbox { + appearance: none; + -webkit-appearance: none; + width: 18px; + height: 18px; + border: 2px solid var(--button-color); + border-radius: 50%; + cursor: pointer; + position: relative; + outline: none; + transition: all 0.2s ease; +} + +.message-checkbox:checked { + background-color: var(--button-hover-color); + border-color: var(--button-hover-color); +} + +.message-checkbox:checked::after { + content: ''; + position: absolute; + top: 3px; + left: 3px; + width: 8px; + height: 8px; + border-radius: 50%; + background: white; +} + +.message-checkbox:hover { + border-color: var(--button-hover-color); +} + +.select-all-btn, +.select-all-responses-btn { + margin-right: 8px; + padding: 4px 12px; + background: none; + border: 1px solid var(--button-color); + cursor: pointer; + color: var(--button-color); + border-radius: 4px; + font-size: 13px; + height: 28px; + transition: all 0.2s ease; +} + +.select-all-btn:hover, +.select-all-responses-btn:hover { + background: var(--button-hover-bg); + color: var(--button-hover-color); + border-color: var(--button-hover-color); +} + +@media (prefers-color-scheme: dark) { + + .select-all-btn, + .select-all-responses-btn { + color: rgb(173, 178, 184); + border-color: rgb(173, 178, 184); + } + + .select-all-btn:hover, + .select-all-responses-btn:hover { + background: var(--button-hover-bg); + color: var(--button-hover-color); + border-color: var(--button-hover-color); + } +} + /* KaTeX copy functionality styles */ .katex { position: relative; @@ -46,6 +419,82 @@ } } +/* Show format selector when text tab is active */ +.tab-btn[data-tab="text"].active~.action-buttons .format-dropdown-container { + display: inline-block; +} + +/* Format select dropdown styles */ +.format-dropdown-container { + position: relative; + display: inline-block; +} + +.format-select { + appearance: none; + -webkit-appearance: none; + background: var(--modal-bg); + color: var(--modal-text); + border: 1px solid var(--modal-border); + border-radius: 4px; + padding: 4px 24px 4px 8px; + font-size: 13px; + cursor: pointer; + transition: all 0.2s ease; + outline: none; +} + +.format-select:hover { + border-color: var(--button-hover-color); + background: var(--button-hover-bg); +} + +.format-select:focus { + border-color: var(--button-hover-color); + box-shadow: 0 0 0 2px rgba(77, 107, 254, 0.1); +} + +/* Custom dropdown arrow */ +.format-dropdown-container::after { + content: ''; + position: absolute; + right: 8px; + top: 50%; + transform: translateY(-50%); + width: 0; + height: 0; + border-left: 4px solid transparent; + border-right: 4px solid transparent; + border-top: 4px solid var(--button-color); + pointer-events: none; + transition: all 0.2s ease; +} + +.format-dropdown-container:hover::after { + border-top-color: var(--button-hover-color); +} + +/* Dark mode adjustments */ +@media (prefers-color-scheme: dark) { + .format-select { + background: var(--modal-bg); + color: var(--modal-text); + border-color: var(--modal-border); + } + + .format-select:hover { + background: var(--button-hover-bg); + } + + .format-dropdown-container::after { + border-top-color: var(--button-color); + } + + .format-dropdown-container:hover::after { + border-top-color: var(--button-hover-color); + } +} + /* Tooltips for injected buttons (JS-controlled) */ .deepshare-gpt-tooltip { position: fixed; @@ -126,3 +575,16 @@ border-bottom-color: #000000; } } + +/* Compact the share panel button row so all buttons fit on one line */ +._43d222b .fab07e97 { + flex-wrap: nowrap; + gap: 6px !important; +} + +._43d222b .fab07e97 .ds-basic-button { + padding: 4px 10px !important; + font-size: 12px !important; + min-width: unset !important; + white-space: nowrap; +} From 16e2e270687d12ad84ec03ee348790f14ef5e17b Mon Sep 17 00:00:00 2001 From: DerYokoya <133297414+DerYokoya@users.noreply.github.com> Date: Fri, 29 May 2026 23:41:17 -0400 Subject: [PATCH 10/11] removing testing feature --- popup/popup.html | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/popup/popup.html b/popup/popup.html index 2f566c5..6f130e8 100644 --- a/popup/popup.html +++ b/popup/popup.html @@ -433,23 +433,8 @@

Gemini

Include reference sources when exporting Gemini Deep Research reports -
- - - How long to wait for Gemini to load more history after each auto-scroll (3-10 seconds) -
-
-

DeepSeek

-
-
- - -
- Show a "Save as Markdown" button on DeepSeek responses -
-
@@ -494,4 +479,4 @@

About

- + \ No newline at end of file From da66fa333e0e43af6ae878a35f4a80eb2aa1cec7 Mon Sep 17 00:00:00 2001 From: DerYokoya <133297414+DerYokoya@users.noreply.github.com> Date: Fri, 29 May 2026 23:44:37 -0400 Subject: [PATCH 11/11] Fix language switching and cleanup old markdown copy keys --- _locales/de/messages.json | 18 ------------------ _locales/en/messages.json | 18 ------------------ _locales/es/messages.json | 18 ------------------ _locales/fr/messages.json | 18 ------------------ _locales/ja/messages.json | 18 ------------------ _locales/pt_BR/messages.json | 18 ------------------ _locales/pt_PT/messages.json | 18 ------------------ _locales/zh_CN/messages.json | 18 ------------------ popup/popup.html | 5 +++++ 9 files changed, 5 insertions(+), 144 deletions(-) diff --git a/_locales/de/messages.json b/_locales/de/messages.json index 9cbc064..5015072 100644 --- a/_locales/de/messages.json +++ b/_locales/de/messages.json @@ -529,23 +529,5 @@ }, "scrollCollecting": { "message": "Nachrichten werden gesammelt, bitte warten..." - }, - "copyAsMarkdown": { - "message": "Als Markdown kopieren" - }, - "markdownCopied": { - "message": "Markdown in die Zwischenablage kopiert" - }, - "copyConversationMarkdown": { - "message": "Alles als Markdown kopieren" - }, - "featureDisabled": { - "message": "Die Markdown-Kopierfunktion ist deaktiviert" - }, - "noMessages": { - "message": "Keine Nachrichten gefunden" - }, - "noContent": { - "message": "Kein Inhalt zum Kopieren" } } diff --git a/_locales/en/messages.json b/_locales/en/messages.json index c4eaaec..1de7fae 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -529,23 +529,5 @@ }, "scrollCollecting": { "message": "Scrolling to collect messages, please wait..." - }, - "copyAsMarkdown": { - "message": "Copy as Markdown" - }, - "markdownCopied": { - "message": "Markdown copied to clipboard" - }, - "copyConversationMarkdown": { - "message": "Copy All as Markdown" - }, - "featureDisabled": { - "message": "Markdown copy feature is disabled" - }, - "noMessages": { - "message": "No messages found" - }, - "noContent": { - "message": "No content to copy" } } diff --git a/_locales/es/messages.json b/_locales/es/messages.json index 0b8aba6..1821ee8 100644 --- a/_locales/es/messages.json +++ b/_locales/es/messages.json @@ -528,23 +528,5 @@ }, "scrollCollecting": { "message": "Desplazando para recopilar mensajes, por favor espere..." - }, - "copyAsMarkdown": { - "message": "Copiar como Markdown" - }, - "markdownCopied": { - "message": "Markdown copiado al portapapeles" - }, - "copyConversationMarkdown": { - "message": "Copiar todo como Markdown" - }, - "featureDisabled": { - "message": "La función de copia de Markdown está desactivada" - }, - "noMessages": { - "message": "No se encontraron mensajes" - }, - "noContent": { - "message": "No hay contenido para copiar" } } diff --git a/_locales/fr/messages.json b/_locales/fr/messages.json index fd34372..a59efcf 100644 --- a/_locales/fr/messages.json +++ b/_locales/fr/messages.json @@ -529,23 +529,5 @@ }, "scrollCollecting": { "message": "Défilement en cours pour collecter les messages, veuillez patienter..." - }, - "copyAsMarkdown": { - "message": "Copier en Markdown" - }, - "markdownCopied": { - "message": "Markdown copié dans le presse-papiers" - }, - "copyConversationMarkdown": { - "message": "Tout copier en Markdown" - }, - "featureDisabled": { - "message": "La fonction de copie Markdown est désactivée" - }, - "noMessages": { - "message": "Aucun message trouvé" - }, - "noContent": { - "message": "Aucun contenu à copier" } } diff --git a/_locales/ja/messages.json b/_locales/ja/messages.json index 2323fe6..ee1c63c 100644 --- a/_locales/ja/messages.json +++ b/_locales/ja/messages.json @@ -528,23 +528,5 @@ }, "scrollCollecting": { "message": "スクロールしてメッセージを収集しています。しばらくお待ちください..." - }, - "copyAsMarkdown": { - "message": "Markdownとしてコピー" - }, - "markdownCopied": { - "message": "Markdownをクリップボードにコピーしました" - }, - "copyConversationMarkdown": { - "message": "すべてをMarkdownとしてコピー" - }, - "featureDisabled": { - "message": "Markdownコピー機能は無効になっています" - }, - "noMessages": { - "message": "メッセージが見つかりません" - }, - "noContent": { - "message": "コピーする内容がありません" } } diff --git a/_locales/pt_BR/messages.json b/_locales/pt_BR/messages.json index 7da3a0d..1f0b4a1 100644 --- a/_locales/pt_BR/messages.json +++ b/_locales/pt_BR/messages.json @@ -528,23 +528,5 @@ }, "scrollCollecting": { "message": "Rolando para coletar mensagens, por favor aguarde..." - }, - "copyAsMarkdown": { - "message": "Copiar como Markdown" - }, - "markdownCopied": { - "message": "Markdown copiado para a área de transferência" - }, - "copyConversationMarkdown": { - "message": "Copiar tudo como Markdown" - }, - "featureDisabled": { - "message": "O recurso de cópia Markdown está desativado" - }, - "noMessages": { - "message": "Nenhuma mensagem encontrada" - }, - "noContent": { - "message": "Nenhum conteúdo para copiar" } } diff --git a/_locales/pt_PT/messages.json b/_locales/pt_PT/messages.json index 0635b99..8ba06b5 100644 --- a/_locales/pt_PT/messages.json +++ b/_locales/pt_PT/messages.json @@ -528,23 +528,5 @@ }, "scrollCollecting": { "message": "A percorrer para recolher mensagens, por favor aguarde..." - }, - "copyAsMarkdown": { - "message": "Copiar como Markdown" - }, - "markdownCopied": { - "message": "Markdown copiado para a área de transferência" - }, - "copyConversationMarkdown": { - "message": "Copiar tudo como Markdown" - }, - "featureDisabled": { - "message": "A funcionalidade de cópia Markdown está desativada" - }, - "noMessages": { - "message": "Nenhuma mensagem encontrada" - }, - "noContent": { - "message": "Nenhum conteúdo para copiar" } } diff --git a/_locales/zh_CN/messages.json b/_locales/zh_CN/messages.json index be37b08..09bcc25 100644 --- a/_locales/zh_CN/messages.json +++ b/_locales/zh_CN/messages.json @@ -528,23 +528,5 @@ }, "scrollCollecting": { "message": "正在滚动读取对话内容,请勿操作..." - }, - "copyAsMarkdown": { - "message": "复制为Markdown" - }, - "markdownCopied": { - "message": "Markdown已复制到剪贴板" - }, - "copyConversationMarkdown": { - "message": "复制全部为Markdown" - }, - "featureDisabled": { - "message": "Markdown复制功能已禁用" - }, - "noMessages": { - "message": "未找到消息" - }, - "noContent": { - "message": "没有内容可复制" } } \ No newline at end of file diff --git a/popup/popup.html b/popup/popup.html index 6f130e8..a76244e 100644 --- a/popup/popup.html +++ b/popup/popup.html @@ -433,6 +433,11 @@

Gemini

Include reference sources when exporting Gemini Deep Research reports +
+ + + How long to wait for Gemini to load more history after each auto-scroll (3-10 seconds) +