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,