From ca1949f502ebc584b6044a90cdca09f51eca79fc Mon Sep 17 00:00:00 2001 From: lencx Date: Thu, 3 Aug 2023 21:24:31 +0800 Subject: [PATCH] fix: layout --- scripts/cmd.js | 28 +++- scripts/core.js | 41 +---- scripts/export.js | 313 ++++++++++++++------------------------ scripts/manifest.json | 6 +- src-tauri/Cargo.toml | 2 +- src-tauri/tauri.conf.json | 3 + 6 files changed, 158 insertions(+), 235 deletions(-) diff --git a/scripts/cmd.js b/scripts/cmd.js index 888efd3..bfef2fc 100644 --- a/scripts/cmd.js +++ b/scripts/cmd.js @@ -1,6 +1,6 @@ /** * @name cmd.js - * @version 0.1.1 + * @version 0.1.2 * @url https://github.com/lencx/ChatGPT/tree/main/scripts/cmd.js */ @@ -122,6 +122,32 @@ function cmdInit() { } }); } + if (mutation.type === 'childList' && mutation.removedNodes.length) { + for (let node of mutation.removedNodes) { + if (node.querySelector('form textarea')) { + initDom(); + cmdTip(); + (async function () { + async function platform() { + return invoke('platform', { + __tauriModule: 'Os', + message: { cmd: 'platform' }, + }); + } + if (__TAURI_METADATA__.__currentWindow.label !== 'tray') { + const _platform = await platform(); + const chatConf = (await invoke('get_app_conf')) || {}; + if (/darwin/.test(_platform) && !chatConf.titlebar) { + const nav = document.body.querySelector('nav'); + if (nav) { + nav.style.paddingTop = '25px'; + } + } + } + })(); + } + } + } } }).observe(document.body, { childList: true, diff --git a/scripts/core.js b/scripts/core.js index 873e72d..06dbc38 100644 --- a/scripts/core.js +++ b/scripts/core.js @@ -1,6 +1,6 @@ /** * @name core.js - * @version 0.1.1 + * @version 0.1.2 * @url https://github.com/lencx/ChatGPT/tree/main/scripts/core.js */ @@ -84,40 +84,13 @@ function coreInit() { document.body.appendChild(topDom); if (window.location.host === 'chat.openai.com') { - const nav = document.body.querySelector('nav'); - const top = topDom.clientHeight + 5; - if (nav) { - const currentPaddingTop = parseInt( - window - .getComputedStyle(document.querySelector('nav'), null) - .getPropertyValue('padding-top') - .replace('px', ''), - 10, - ); - const navStyleDom = document.createElement('style'); - navStyleDom.innerHTML = `nav{ - padding-top:${currentPaddingTop + top}px !important + const intervalId = setInterval(function () { + const nav = document.body.querySelector('nav'); + if (nav) { + nav.style.paddingTop = '25px'; + clearInterval(intervalId); } - button[aria-label="Show sidebar"]{ - margin-top:${top}px !important - } - `; - document.head.appendChild(navStyleDom); - } else { - const navStyleDom = document.createElement('style'); - navStyleDom.innerHTML = `nav{ - padding-top:${top}px !important - } - button[aria-label="Show sidebar"]{ - margin-top:${top}px !important - } - `; - document.head.appendChild(navStyleDom); - const main = document.querySelector('main'); - if (main && main.parentElement.children.length > 1) { - main.parentElement.children[0].style.paddingTop = top + 'px'; - } - } + }, 1000); } topDom.addEventListener('mousedown', () => invoke('drag_window')); diff --git a/scripts/export.js b/scripts/export.js index 939e3bd..f5b79d7 100644 --- a/scripts/export.js +++ b/scripts/export.js @@ -1,164 +1,17 @@ /** * @name export.js - * @version 0.1.4 + * @version 0.1.5 * @url https://github.com/lencx/ChatGPT/tree/main/scripts/export.js */ async function exportInit() { - if (window.location.pathname === '/auth/login') return; - const buttonOuterHTMLFallback = ``; - removeButtons(); - if (window.buttonsInterval) { - clearInterval(window.buttonsInterval); - } - if (window.innerWidth < 767) return; - - const chatConf = (await invoke('get_app_conf')) || {}; - window.buttonsInterval = setInterval(() => { - const formArea = document.querySelector('form>div>div'); - const textarea = formArea.querySelector('div textarea'); - const textareaDiv = formArea.querySelector('div div.absolute'); - const hasBtn = formArea.querySelector('div button'); - const actionsArea = document.querySelector('form>div>div>div'); - - if (!formArea || !actionsArea || (textarea && textareaDiv) || !hasBtn) { - return; - } - - if (shouldAddButtons(actionsArea)) { - let TryAgainButton = actionsArea.querySelector('button'); - if (!TryAgainButton) { - const parentNode = document.createElement('div'); - parentNode.innerHTML = buttonOuterHTMLFallback; - TryAgainButton = parentNode.querySelector('button'); - } - addActionsButtons(actionsArea, TryAgainButton, chatConf); - } else if (shouldRemoveButtons()) { - removeButtons(); - } - }, 1000); - + const SELECTOR = 'main div.group'; + const USER_INPUT_SELECTOR = 'div.empty\\:hidden'; const Format = { PNG: 'png', PDF: 'pdf', }; - function shouldRemoveButtons() { - if (document.querySelector('form .text-2xl')) { - return true; - } - return false; - } - - function shouldAddButtons(actionsArea) { - // first, check if there's a "Try Again" button and no other buttons - const buttons = actionsArea?.querySelectorAll('button'); - - const hasTryAgainButton = Array.from(buttons).some((button) => { - return !/download-/.test(button.id); - }); - - const stopBtn = buttons?.[0]?.innerText; - - if (/Stop generating/gi.test(stopBtn)) { - return false; - } - - if ( - buttons.length === 2 && - (/Regenerate response/gi.test(stopBtn) || buttons[1].innerText === '') - ) { - return true; - } - - if (hasTryAgainButton && buttons.length === 1) { - return true; - } - - // otherwise, check if open screen is not visible - const isOpenScreen = document.querySelector('h1.text-4xl'); - if (isOpenScreen) { - return false; - } - - // check if the conversation is finished and there are no share buttons - const finishedConversation = document.querySelector('form button>svg'); - const hasShareButtons = actionsArea?.querySelectorAll('button[share-ext]'); - if (finishedConversation && !hasShareButtons.length) { - return true; - } - - return false; - } - - function removeButtons() { - const downloadPngButton = document.getElementById('download-png-button'); - const downloadPdfButton = document.getElementById('download-pdf-button'); - const downloadMdButton = document.getElementById('download-markdown-button'); - const refreshButton = document.getElementById('refresh-page-button'); - if (downloadPngButton) { - downloadPngButton.remove(); - } - if (downloadPdfButton) { - downloadPdfButton.remove(); - } - if (downloadPdfButton) { - downloadMdButton.remove(); - } - if (refreshButton) { - refreshButton.remove(); - } - } - - function addActionsButtons(actionsArea, TryAgainButton) { - // Export markdown - const exportMd = TryAgainButton.cloneNode(true); - exportMd.id = 'download-markdown-button'; - exportMd.setAttribute('share-ext', 'true'); - exportMd.title = 'Export Markdown'; - - exportMd.innerHTML = setIcon('md'); - exportMd.onclick = () => { - exportMarkdown(); - }; - actionsArea.appendChild(exportMd); - - // Generate PNG - const downloadPngButton = TryAgainButton.cloneNode(true); - downloadPngButton.id = 'download-png-button'; - downloadPngButton.setAttribute('share-ext', 'true'); - downloadPngButton.title = 'Generate PNG'; - downloadPngButton.innerHTML = setIcon('png'); - downloadPngButton.onclick = () => { - downloadThread(); - }; - actionsArea.appendChild(downloadPngButton); - - // Generate PDF - const downloadPdfButton = TryAgainButton.cloneNode(true); - downloadPdfButton.id = 'download-pdf-button'; - downloadPdfButton.setAttribute('share-ext', 'true'); - downloadPdfButton.title = 'Download PDF'; - downloadPdfButton.innerHTML = setIcon('pdf'); - downloadPdfButton.onclick = () => { - downloadThread({ as: Format.PDF }); - }; - actionsArea.appendChild(downloadPdfButton); - - // Refresh - const refreshButton = TryAgainButton.cloneNode(true); - refreshButton.id = 'refresh-page-button'; - refreshButton.title = 'Refresh the Page'; - refreshButton.innerHTML = setIcon('refresh'); - refreshButton.onclick = () => { - window.location.reload(); - }; - actionsArea.appendChild(refreshButton); - } - - const SELECTOR = 'main div.group'; - const USER_INPUT_SELECTOR = 'div.empty\\:hidden'; - function processNode(node, replaceInUserInput = false) { let j = node.cloneNode(true); if (/dark\:bg-gray-800/.test(node.getAttribute('class'))) { @@ -168,13 +21,14 @@ async function exportInit() { if (replaceInUserInput) { const userInputBlocks = j.querySelectorAll(USER_INPUT_SELECTOR); userInputBlocks.forEach((block) => { - - //For quicker testing use js fiddle: https://jsfiddle.net/xtraeme/x34ao9jp/13/ - block.innerHTML = block.innerHTML - .replace(/ |\u00A0/g, ' ') //Replace =C2=A0 (nbsp non-breaking space) /w breaking-space - .replace(/\t/g, '    ') // Replace tab with 4 non-breaking spaces - .replace(/^ +/gm, function(match) { return ' '.repeat(match.length); }) //Add =C2=A0 - .replace(/\n/g, '
'); + //For quicker testing use js fiddle: https://jsfiddle.net/xtraeme/x34ao9jp/13/ + block.innerHTML = block.innerHTML + .replace(/ |\u00A0/g, ' ') //Replace =C2=A0 (nbsp non-breaking space) /w breaking-space + .replace(/\t/g, '    ') // Replace tab with 4 non-breaking spaces + .replace(/^ +/gm, function (match) { + return ' '.repeat(match.length); + }) //Add =C2=A0 + .replace(/\n/g, '
'); }); } @@ -191,24 +45,17 @@ async function exportInit() { // '''Python import package //so we remove whitespace after tags and add
and/or \n allBlocks.forEach((block) => { - block.innerHTML = block.innerHTML - .replace(/(]*>)\s*/g, '$1\n'); // Add \n or
after opening code tag + block.innerHTML = block.innerHTML.replace(/(]*>)\s*/g, '$1\n'); // Add \n or
after opening code tag }); - const content = nodes.map(i => processNode(i)).join(''); - const updatedContent = nodes.map(i => processNode(i, true)).join(''); + const updatedContent = nodes.map((i) => processNode(i, true)).join(''); const data = ExportMD.turndown(updatedContent); const { id, filename } = getName(); - final_filename = `${filename}`; //`${filename}_${id}`; - - //await invoke('save_file', { name: `notes/${final_filename}_raw.txt`, content: content }); await invoke('save_file', { name: `notes/${id}.md`, content: data }); - await invoke('download_list', { pathname: 'chat.notes.json', final_filename, id, dir: 'notes' }); - //await invoke('download_list', { pathname: 'chat.notes.json', final_filename, final_filename, dir: 'notes' }); + await invoke('download_list', { pathname: 'chat.notes.json', filename, id, dir: 'notes' }); } - async function downloadThread({ as = Format.PNG } = {}) { const { startLoading, stopLoading } = new window.__LoadingMask('Exporting in progress...'); startLoading(); @@ -329,15 +176,6 @@ async function exportInit() { } } - function setIcon(type) { - return { - png: ``, - pdf: ``, - md: ``, - refresh: ``, - }[type]; - } - function formatDateTime() { const now = new Date(); const year = now.getFullYear(); @@ -350,36 +188,119 @@ async function exportInit() { return formattedDateTime; } - function sanitizeFilename(filename) { - if (!filename || filename === '') return ''; + // function sanitizeFilename(filename) { + // if (!filename || filename === '') return ''; - // Replace whitespaces with underscores - let sanitizedFilename = filename.replace(/\s/g, '_'); + // // Replace whitespaces with underscores + // let sanitizedFilename = filename.replace(/\s/g, '_'); - // Replace invalid filename characters with # - const invalidCharsRegex = /[<>:"/\\|?*\x00-\x1F]/g; - sanitizedFilename = sanitizedFilename.replace(invalidCharsRegex, '#'); + // // Replace invalid filename characters with # + // const invalidCharsRegex = /[<>:"/\\|?*\x00-\x1F]/g; + // sanitizedFilename = sanitizedFilename.replace(invalidCharsRegex, '#'); - // Check for filenames ending with period or space (Windows) - if (sanitizedFilename && /[\s.]$/.test(sanitizedFilename)) { - sanitizedFilename = sanitizedFilename.slice(0, -1) + '#'; - } - //console.log(sanitizedFilename); - return sanitizedFilename; + // // Check for filenames ending with period or space (Windows) + // if (sanitizedFilename && /[\s.]$/.test(sanitizedFilename)) { + // sanitizedFilename = sanitizedFilename.slice(0, -1) + '#'; + // } + // //console.log(sanitizedFilename); + // return sanitizedFilename; + // } + + function btnInit() { + const intervalId = setInterval(function () { + const navActionArea = document.querySelector('nav .border-t > div'); + const addArea = document.querySelector('#chatgpt-nav-action-area'); + if (!navActionArea || addArea) return; + const cloneNode = document.createElement('div'); + cloneNode.id = 'chatgpt-nav-action-area'; + cloneNode.classList = `${navActionArea.className} border-b border-white/20 mb-2 pb-2`; + cloneNode.appendChild(addBtn('png')); + cloneNode.appendChild(addBtn('pdf')); + cloneNode.appendChild(addBtn('md')); + cloneNode.appendChild(addBtn('refresh')); + navActionArea.parentNode.insertBefore(cloneNode, navActionArea); + clearInterval(intervalId); + + function debounce(func, wait) { + let timeout; + return function () { + const context = this; + const args = arguments; + clearTimeout(timeout); + timeout = setTimeout(() => { + func.apply(context, args); + }, wait); + }; + } + const target = document.querySelector('nav'); + const debouncedFunction = debounce(function () { + btnInit(); + }, 300); + const observer = new MutationObserver(debouncedFunction); + const config = { attributes: true, childList: true, characterData: true, subtree: true }; + observer.observe(target, config); + }, 1000); + } + + function addBtn(type) { + const btn = document.createElement('button'); + btn.className = `btn dark:text-gray-500 hover:dark:text-gray-300`; + btn.title = { + png: 'Export PNG', + pdf: 'Export PDF', + md: 'Export Markdown', + refresh: 'Refresh the Page', + }[type]; + btn.innerHTML = setIcon(type); + btn.onclick = () => { + const content = document.querySelector('main .group'); + if (!content && type !== 'refresh') { + alert('Please open a thread first.'); + return; + } + switch (type) { + case 'png': + downloadThread(); + break; + case 'pdf': + downloadThread({ as: 'pdf' }); + break; + case 'md': + exportMarkdown(); + break; + case 'refresh': + window.location.reload(); + break; + default: + break; + } + }; + return btn; + } + + function setIcon(type) { + return { + png: ``, + pdf: ``, + md: ``, + refresh: ``, + }[type]; } function getName() { const id = window.crypto.getRandomValues(new Uint32Array(1))[0].toString(36); - const name = + const name = document.querySelector('nav .overflow-y-auto a.hover\\:bg-gray-800')?.innerText?.trim() || ''; - clean_name = sanitizeFilename(name); - return { filename: name ? name : id, - id, - pathname: 'chat.download.json' }; + // clean_name = sanitizeFilename(name); + return { + id, + filename: name ? name : id, + pathname: 'chat.download.json', + }; } -} -window.addEventListener('resize', exportInit); + btnInit(); +} if (document.readyState === 'complete' || document.readyState === 'interactive') { exportInit(); diff --git a/scripts/manifest.json b/scripts/manifest.json index 594630b..a7cbab4 100644 --- a/scripts/manifest.json +++ b/scripts/manifest.json @@ -10,11 +10,11 @@ }, { "name": "cmd.js", - "version": "0.1.1" + "version": "0.1.2" }, { "name": "core.js", - "version": "0.1.1" + "version": "0.1.2" }, { "name": "dalle2.js", @@ -22,7 +22,7 @@ }, { "name": "export.js", - "version": "0.1.4" + "version": "0.1.5" }, { "name": "markdown.export.js", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 8fdc2dd..36c242d 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -26,7 +26,7 @@ dark-light = "1.0.0" wry = "0.*" serde = { version = "1.0", features = ["derive"] } tokio = { version = "1.23.0", features = ["macros"] } -tauri = { version = "1.3.0", features = ["devtools", "fs-create-dir", "fs-exists", "fs-read-dir", "fs-read-file", "fs-remove-dir", "fs-remove-file", "fs-write-file", "global-shortcut", "global-shortcut-all", "os-all", "path-all", "process-all", "shell-all", "shell-open-api", "system-tray", "updater"] } +tauri = { version = "1.3.0", features = ["devtools", "dialog-all", "fs-create-dir", "fs-exists", "fs-read-dir", "fs-read-file", "fs-remove-dir", "fs-remove-file", "fs-write-file", "global-shortcut", "global-shortcut-all", "os-all", "path-all", "process-all", "shell-all", "shell-open-api", "system-tray", "updater"] } tauri-plugin-positioner = { git = "https://github.com/lencx/tauri-plugins-workspace", features = ["system-tray"] } tauri-plugin-log = { git = "https://github.com/lencx/tauri-plugins-workspace", branch = "dev", features = ["colored"] } tauri-plugin-autostart = { git = "https://github.com/lencx/tauri-plugins-workspace", branch = "dev" } diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 40e87fa..ebededd 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -38,6 +38,9 @@ "os": { "all": true }, + "dialog": { + "all": true + }, "process": { "all": true, "exit": true,