refactor: conf

This commit is contained in:
lencx 2023-05-18 15:32:32 +08:00
parent 60b7e3148a
commit d9612d3bed
46 changed files with 1897 additions and 1149 deletions

1479
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -58,7 +58,7 @@
"uuid": "^9.0.0"
},
"devDependencies": {
"@tauri-apps/cli": "^1.2.2",
"@tauri-apps/cli": "^1.3.1",
"@tauri-release/cli": "^0.2.5",
"@types/lodash": "^4.14.191",
"@types/node": "^18.7.10",

View File

@ -4,7 +4,7 @@ specifiers:
'@ant-design/icons': ^4.8.0
'@monaco-editor/react': ^4.4.6
'@tauri-apps/api': ^1.2.0
'@tauri-apps/cli': ^1.2.2
'@tauri-apps/cli': ^1.3.1
'@tauri-release/cli': ^0.2.5
'@types/lodash': ^4.14.191
'@types/node': ^18.7.10
@ -59,7 +59,7 @@ dependencies:
uuid: 9.0.0
devDependencies:
'@tauri-apps/cli': 1.2.3
'@tauri-apps/cli': 1.3.1
'@tauri-release/cli': 0.2.5
'@types/lodash': 4.14.191
'@types/node': 18.11.19
@ -1094,10 +1094,10 @@ packages:
engines: { node: '>= 14.6.0', npm: '>= 6.6.0', yarn: '>= 1.19.1' }
dev: false
/@tauri-apps/cli-darwin-arm64/1.2.3:
/@tauri-apps/cli-darwin-arm64/1.3.1:
resolution:
{
integrity: sha512-phJN3fN8FtZZwqXg08bcxfq1+X1JSDglLvRxOxB7VWPq+O5SuB8uLyssjJsu+PIhyZZnIhTGdjhzLSFhSXfLsw==,
integrity: sha512-QlepYVPgOgspcwA/u4kGG4ZUijlXfdRtno00zEy+LxinN/IRXtk+6ErVtsmoLi1ZC9WbuMwzAcsRvqsD+RtNAg==,
}
engines: { node: '>= 10' }
cpu: [arm64]
@ -1106,10 +1106,10 @@ packages:
dev: true
optional: true
/@tauri-apps/cli-darwin-x64/1.2.3:
/@tauri-apps/cli-darwin-x64/1.3.1:
resolution:
{
integrity: sha512-jFZ/y6z8z6v4yliIbXKBXA7BJgtZVMsITmEXSuD6s5+eCOpDhQxbRkr6CA+FFfr+/r96rWSDSgDenDQuSvPAKw==,
integrity: sha512-fKcAUPVFO3jfDKXCSDGY0MhZFF/wDtx3rgFnogWYu4knk38o9RaqRkvMvqJhLYPuWaEM5h6/z1dRrr9KKCbrVg==,
}
engines: { node: '>= 10' }
cpu: [x64]
@ -1118,10 +1118,10 @@ packages:
dev: true
optional: true
/@tauri-apps/cli-linux-arm-gnueabihf/1.2.3:
/@tauri-apps/cli-linux-arm-gnueabihf/1.3.1:
resolution:
{
integrity: sha512-C7h5vqAwXzY0kRGSU00Fj8PudiDWFCiQqqUNI1N+fhCILrzWZB9TPBwdx33ZfXKt/U4+emdIoo/N34v3TiAOmQ==,
integrity: sha512-+4H0dv8ltJHYu/Ma1h9ixUPUWka9EjaYa8nJfiMsdCI4LJLNE6cPveE7RmhZ59v9GW1XB108/k083JUC/OtGvA==,
}
engines: { node: '>= 10' }
cpu: [arm]
@ -1130,10 +1130,10 @@ packages:
dev: true
optional: true
/@tauri-apps/cli-linux-arm64-gnu/1.2.3:
/@tauri-apps/cli-linux-arm64-gnu/1.3.1:
resolution:
{
integrity: sha512-buf1c8sdkuUzVDkGPQpyUdAIIdn5r0UgXU6+H5fGPq/Xzt5K69JzXaeo6fHsZEZghbV0hOK+taKV4J0m30UUMQ==,
integrity: sha512-Pj3odVO1JAxLjYmoXKxcrpj/tPxcA8UP8N06finhNtBtBaxAjrjjxKjO4968KB0BUH7AASIss9EL4Tr0FGnDuw==,
}
engines: { node: '>= 10' }
cpu: [arm64]
@ -1142,10 +1142,10 @@ packages:
dev: true
optional: true
/@tauri-apps/cli-linux-arm64-musl/1.2.3:
/@tauri-apps/cli-linux-arm64-musl/1.3.1:
resolution:
{
integrity: sha512-x88wPS9W5xAyk392vc4uNHcKBBvCp0wf4H9JFMF9OBwB7vfd59LbQCFcPSu8f0BI7bPrOsyHqspWHuFL8ojQEA==,
integrity: sha512-tA0JdDLPFaj42UDIVcF2t8V0tSha40rppcmAR/MfQpTCxih6399iMjwihz9kZE1n4b5O4KTq9GliYo50a8zYlQ==,
}
engines: { node: '>= 10' }
cpu: [arm64]
@ -1154,10 +1154,10 @@ packages:
dev: true
optional: true
/@tauri-apps/cli-linux-x64-gnu/1.2.3:
/@tauri-apps/cli-linux-x64-gnu/1.3.1:
resolution:
{
integrity: sha512-ZMz1jxEVe0B4/7NJnlPHmwmSIuwiD6ViXKs8F+OWWz2Y4jn5TGxWKFg7DLx5OwQTRvEIZxxT7lXHi5CuTNAxKg==,
integrity: sha512-FDU+Mnvk6NLkqQimcNojdKpMN4Y3W51+SQl+NqG9AFCWprCcSg62yRb84751ujZuf2MGT8HQOfmd0i77F4Q3tQ==,
}
engines: { node: '>= 10' }
cpu: [x64]
@ -1166,10 +1166,10 @@ packages:
dev: true
optional: true
/@tauri-apps/cli-linux-x64-musl/1.2.3:
/@tauri-apps/cli-linux-x64-musl/1.3.1:
resolution:
{
integrity: sha512-B/az59EjJhdbZDzawEVox0LQu2ZHCZlk8rJf85AMIktIUoAZPFbwyiUv7/zjzA/sY6Nb58OSJgaPL2/IBy7E0A==,
integrity: sha512-MpO3akXFmK8lZYEbyQRDfhdxz1JkTBhonVuz5rRqxwA7gnGWHa1aF1+/2zsy7ahjB2tQ9x8DDFDMdVE20o9HrA==,
}
engines: { node: '>= 10' }
cpu: [x64]
@ -1178,10 +1178,10 @@ packages:
dev: true
optional: true
/@tauri-apps/cli-win32-ia32-msvc/1.2.3:
/@tauri-apps/cli-win32-ia32-msvc/1.3.1:
resolution:
{
integrity: sha512-ypdO1OdC5ugNJAKO2m3sb1nsd+0TSvMS9Tr5qN/ZSMvtSduaNwrcZ3D7G/iOIanrqu/Nl8t3LYlgPZGBKlw7Ng==,
integrity: sha512-9Boeo3K5sOrSBAZBuYyGkpV2RfnGQz3ZhGJt4hE6P+HxRd62lS6+qDKAiw1GmkZ0l1drc2INWrNeT50gwOKwIQ==,
}
engines: { node: '>= 10' }
cpu: [ia32]
@ -1190,10 +1190,10 @@ packages:
dev: true
optional: true
/@tauri-apps/cli-win32-x64-msvc/1.2.3:
/@tauri-apps/cli-win32-x64-msvc/1.3.1:
resolution:
{
integrity: sha512-CsbHQ+XhnV/2csOBBDVfH16cdK00gNyNYUW68isedmqcn8j+s0e9cQ1xXIqi+Hue3awp8g3ImYN5KPepf3UExw==,
integrity: sha512-wMrTo91hUu5CdpbElrOmcZEoJR4aooTG+fbtcc87SMyPGQy1Ux62b+ZdwLvL1sVTxnIm//7v6QLRIWGiUjCPwA==,
}
engines: { node: '>= 10' }
cpu: [x64]
@ -1202,23 +1202,23 @@ packages:
dev: true
optional: true
/@tauri-apps/cli/1.2.3:
/@tauri-apps/cli/1.3.1:
resolution:
{
integrity: sha512-erxtXuPhMEGJPBtnhPILD4AjuT81GZsraqpFvXAmEJZ2p8P6t7MVBifCL8LznRknznM3jn90D3M8RNBP3wcXTw==,
integrity: sha512-o4I0JujdITsVRm3/0spfJX7FcKYrYV1DXJqzlWIn6IY25/RltjU6qbC1TPgVww3RsRX63jyVUTcWpj5wwFl+EQ==,
}
engines: { node: '>= 10' }
hasBin: true
optionalDependencies:
'@tauri-apps/cli-darwin-arm64': 1.2.3
'@tauri-apps/cli-darwin-x64': 1.2.3
'@tauri-apps/cli-linux-arm-gnueabihf': 1.2.3
'@tauri-apps/cli-linux-arm64-gnu': 1.2.3
'@tauri-apps/cli-linux-arm64-musl': 1.2.3
'@tauri-apps/cli-linux-x64-gnu': 1.2.3
'@tauri-apps/cli-linux-x64-musl': 1.2.3
'@tauri-apps/cli-win32-ia32-msvc': 1.2.3
'@tauri-apps/cli-win32-x64-msvc': 1.2.3
'@tauri-apps/cli-darwin-arm64': 1.3.1
'@tauri-apps/cli-darwin-x64': 1.3.1
'@tauri-apps/cli-linux-arm-gnueabihf': 1.3.1
'@tauri-apps/cli-linux-arm64-gnu': 1.3.1
'@tauri-apps/cli-linux-arm64-musl': 1.3.1
'@tauri-apps/cli-linux-x64-gnu': 1.3.1
'@tauri-apps/cli-linux-x64-musl': 1.3.1
'@tauri-apps/cli-win32-ia32-msvc': 1.3.1
'@tauri-apps/cli-win32-x64-msvc': 1.3.1
dev: true
/@tauri-release/cli/0.2.5:

BIN
public/bmc.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

BIN
public/wxp.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 KiB

View File

@ -1,7 +1,13 @@
/**
* @name chat.js
* @version 0.1.0
* @url https://github.com/lencx/ChatGPT/tree/main/scripts/chat.js
*/
async function init() {
new MutationObserver(function (mutationsList) {
for (const mutation of mutationsList) {
if (mutation.target.closest("form")) {
if (mutation.target.closest('form')) {
chatBtns();
}
}
@ -10,99 +16,100 @@ async function init() {
subtree: true,
});
document.addEventListener('visibilitychange', () =>
document.getElementsByTagName('textarea')[0]?.focus()
document.getElementsByTagName('textarea')[0]?.focus(),
);
}
async function chatBtns() {
const chatConf = await invoke('get_app_conf') || {};
const chatConf = (await invoke('get_app_conf')) || {};
const synth = window.speechSynthesis;
let currentUtterance = null;
let currentIndex = -1;
const list = Array.from(document.querySelectorAll("main >div>div>div>div>div"));
const list = Array.from(document.querySelectorAll('main >div>div>div>div>div'));
list.forEach((i, idx) => {
if (i.querySelector('.chat-item-copy')) return;
if (!i.querySelector('button.rounded-md')) return;
if (!i.querySelector('.self-end')) return;
const cpbtn = i.querySelector('button.rounded-md').cloneNode(true);
cpbtn.classList.add('chat-item-copy');
cpbtn.title = 'Copy to clipboard';
cpbtn.innerHTML = setIcon('copy');
i.querySelector('.self-end').appendChild(cpbtn);
cpbtn.onclick = () => {
copyToClipboard(i?.innerText?.trim() || '', cpbtn);
}
// if (i.querySelector('.chat-item-copy')) return;
if (i.querySelector('.chat-item-voice')) return;
if (!i.querySelector('button.rounded-md')) return;
if (!i.querySelector('.self-end')) return;
// const cpbtn = i.querySelector('button.rounded-md').cloneNode(true);
// cpbtn.classList.add('chat-item-copy');
// cpbtn.title = 'Copy to clipboard';
// cpbtn.innerHTML = setIcon('copy');
// i.querySelector('.self-end').appendChild(cpbtn);
// cpbtn.onclick = () => {
// copyToClipboard(i?.innerText?.trim() || '', cpbtn);
// }
const saybtn = i.querySelector('button.rounded-md').cloneNode(true);
saybtn.classList.add('chat-item-voice');
saybtn.title = 'Say';
saybtn.innerHTML = setIcon('voice');
i.querySelector('.self-end').appendChild(saybtn);
saybtn.onclick = () => {
if (currentUtterance && currentIndex !== -1) {
synth.cancel();
if (idx === currentIndex) {
saybtn.innerHTML = setIcon('voice');
currentUtterance = null;
currentIndex = -1;
return;
} else if (list[currentIndex].querySelector('.chat-item-voice')) {
list[currentIndex].querySelector('.chat-item-voice').innerHTML = setIcon('voice');
list[idx].querySelector('.chat-item-voice').innerHTML = setIcon('speaking');
}
}
const txt = i?.innerText?.trim() || '';
if (!txt) return;
const utterance = new SpeechSynthesisUtterance(txt);
const voices = speechSynthesis.getVoices();
let voice = voices.find(voice => voice.voiceURI === chatConf.speech_lang);
if (!voice) {
voice = voices.find(voice => voice.lang === 'en-US');
}
utterance.voice = voice;
currentIndex = idx;
utterance.lang = voice.lang;
// utterance.rate = 0.7;
// utterance.pitch = 1.1;
// utterance.volume = 1;
synth.speak(utterance);
amISpeaking = synth.speaking;
saybtn.innerHTML = setIcon('speaking');
currentUtterance = utterance;
currentIndex = idx;
utterance.onend = () => {
const saybtn = i.querySelector('button.rounded-md').cloneNode(true);
saybtn.classList.add('chat-item-voice');
saybtn.title = 'Say';
saybtn.innerHTML = setIcon('voice');
i.querySelector('.self-end').appendChild(saybtn);
saybtn.onclick = () => {
if (currentUtterance && currentIndex !== -1) {
synth.cancel();
if (idx === currentIndex) {
saybtn.innerHTML = setIcon('voice');
currentUtterance = null;
currentIndex = -1;
return;
} else if (list[currentIndex].querySelector('.chat-item-voice')) {
list[currentIndex].querySelector('.chat-item-voice').innerHTML = setIcon('voice');
list[idx].querySelector('.chat-item-voice').innerHTML = setIcon('speaking');
}
}
})
const txt = i?.innerText?.trim() || '';
if (!txt) return;
const utterance = new SpeechSynthesisUtterance(txt);
const voices = speechSynthesis.getVoices();
let voice = voices.find((voice) => voice.voiceURI === chatConf.speech_lang);
if (!voice) {
voice = voices.find((voice) => voice.lang === 'en-US');
}
utterance.voice = voice;
currentIndex = idx;
utterance.lang = voice.lang;
// utterance.rate = 0.7;
// utterance.pitch = 1.1;
// utterance.volume = 1;
synth.speak(utterance);
amISpeaking = synth.speaking;
saybtn.innerHTML = setIcon('speaking');
currentUtterance = utterance;
currentIndex = idx;
utterance.onend = () => {
saybtn.innerHTML = setIcon('voice');
currentUtterance = null;
currentIndex = -1;
};
};
});
}
function copyToClipboard(text, btn) {
window.clearTimeout(window.__cpTimeout);
btn.innerHTML = setIcon('cpok');
if (navigator.clipboard) {
navigator.clipboard.writeText(text);
} else {
var textarea = document.createElement('textarea');
document.body.appendChild(textarea);
textarea.style.position = 'fixed';
textarea.style.clip = 'rect(0 0 0 0)';
textarea.style.top = '10px';
textarea.value = text;
textarea.select();
document.execCommand('copy', true);
document.body.removeChild(textarea);
}
window.__cpTimeout = setTimeout(() => {
btn.innerHTML = setIcon('copy');
}, 1000);
}
// function copyToClipboard(text, btn) {
// window.clearTimeout(window.__cpTimeout);
// btn.innerHTML = setIcon('cpok');
// if (navigator.clipboard) {
// navigator.clipboard.writeText(text);
// } else {
// var textarea = document.createElement('textarea');
// document.body.appendChild(textarea);
// textarea.style.position = 'fixed';
// textarea.style.clip = 'rect(0 0 0 0)';
// textarea.style.top = '10px';
// textarea.value = text;
// textarea.select();
// document.execCommand('copy', true);
// document.body.removeChild(textarea);
// }
// window.__cpTimeout = setTimeout(() => {
// btn.innerHTML = setIcon('copy');
// }, 1000);
// }
function focusOnInput() {
// This currently works because there is only a single `<textarea>` element on the ChatGPT UI page.
document.getElementsByTagName("textarea")[0].focus();
document.getElementsByTagName('textarea')[0].focus();
}
function setIcon(type) {
@ -114,11 +121,8 @@ function setIcon(type) {
}[type];
}
if (
document.readyState === "complete" ||
document.readyState === "interactive"
) {
if (document.readyState === 'complete' || document.readyState === 'interactive') {
init();
} else {
document.addEventListener("DOMContentLoaded", init);
}
document.addEventListener('DOMContentLoaded', init);
}

View File

@ -1,4 +1,8 @@
// *** Core Script - CMD ***
/**
* @name cmd.js
* @version 0.1.0
* @url https://github.com/lencx/ChatGPT/tree/main/scripts/cmd.js
*/
function init() {
const styleDom = document.createElement('style');
@ -105,7 +109,7 @@ function init() {
clearInterval(window.formInterval);
}
window.formInterval = setInterval(() => {
const form = document.querySelector("form textarea");
const form = document.querySelector('form textarea');
if (!form) return;
clearInterval(window.formInterval);
cmdTip();
@ -117,11 +121,11 @@ function init() {
}
if (mutation.target.getAttribute('class') === 'chat-model-cmd-list') {
// The `chatgpt prompt` fill can be done by clicking on the event.
const searchDom = document.querySelector("form .chat-model-cmd-list>div");
const searchDom = document.querySelector('form .chat-model-cmd-list>div');
const searchInput = document.querySelector('form textarea');
if (!searchDom) return;
searchDom.addEventListener('click', (event) => {
const item = event.target.closest("div");
const item = event.target.closest('div');
if (item) {
const val = decodeURIComponent(item.getAttribute('data-prompt'));
searchInput.value = val;
@ -140,7 +144,7 @@ function init() {
async function cmdTip() {
initDom();
const chatModelJson = await invoke('get_chat_model_cmd') || {};
const chatModelJson = (await invoke('get_chat_model_cmd')) || {};
const data = chatModelJson.data;
if (data.length <= 0) return;
@ -156,7 +160,12 @@ async function cmdTip() {
modelDom.style.bottom = '54px';
}
const itemDom = (v) => `<div class="cmd-item" title="${v.prompt}" data-cmd="${v.cmd}" data-prompt="${encodeURIComponent(v.prompt)}"><b title="${v.cmd}">/${v.cmd}</b><i>${v.act}</i></div>`;
const itemDom = (v) =>
`<div class="cmd-item" title="${v.prompt}" data-cmd="${
v.cmd
}" data-prompt="${encodeURIComponent(v.prompt)}"><b title="${v.cmd}">/${v.cmd}</b><i>${
v.act
}</i></div>`;
const renderList = (v) => {
initDom();
modelDom.innerHTML = `<div>${v.map(itemDom).join('')}</div>`;
@ -168,16 +177,23 @@ async function cmdTip() {
};
const setPrompt = (v = '') => {
if (v.trim()) {
window.__CHAT_MODEL_CMD_PROMPT__ = window.__CHAT_MODEL_CMD_PROMPT__?.replace(/\{([^{}]*)\}/, `{${v.trim()}}`);
window.__CHAT_MODEL_CMD_PROMPT__ = window.__CHAT_MODEL_CMD_PROMPT__?.replace(
/\{([^{}]*)\}/,
`{${v.trim()}}`,
);
}
}
};
const searchInput = document.querySelector('form textarea');
// Enter a command starting with `/` and press a space to automatically fill `chatgpt prompt`.
// If more than one command appears in the search results, the first one will be used by default.
function cmdKeydown(event) {
if (!window.__CHAT_MODEL_CMD_PROMPT__) {
if (!event.shiftKey && event.keyCode === 13 && __TAURI_METADATA__.__currentWindow.label === 'tray') {
if (
!event.shiftKey &&
event.keyCode === 13 &&
__TAURI_METADATA__.__currentWindow.label === 'tray'
) {
const btn = document.querySelector('form button');
if (btn) btn.click();
event.preventDefault();
@ -186,20 +202,26 @@ async function cmdTip() {
}
// ------------------ Keyboard scrolling (ArrowUp | ArrowDown) --------------------------
if (event.keyCode === 38 && window.__cmd_index > 0) { // ArrowUp
if (event.keyCode === 38 && window.__cmd_index > 0) {
// ArrowUp
window.__cmd_list[window.__cmd_index].classList.remove('selected');
window.__cmd_index = window.__cmd_index - 1;
window.__cmd_list[window.__cmd_index].classList.add('selected');
window.__CHAT_MODEL_CMD_PROMPT__ = decodeURIComponent(window.__cmd_list[window.__cmd_index].getAttribute('data-prompt'));
window.__CHAT_MODEL_CMD_PROMPT__ = decodeURIComponent(
window.__cmd_list[window.__cmd_index].getAttribute('data-prompt'),
);
searchInput.value = `/${window.__cmd_list[window.__cmd_index].getAttribute('data-cmd')}`;
event.preventDefault();
}
if (event.keyCode === 40 && window.__cmd_index < window.__cmd_list.length - 1) { // ArrowDown
if (event.keyCode === 40 && window.__cmd_index < window.__cmd_list.length - 1) {
// ArrowDown
window.__cmd_list[window.__cmd_index].classList.remove('selected');
window.__cmd_index = window.__cmd_index + 1;
window.__cmd_list[window.__cmd_index].classList.add('selected');
window.__CHAT_MODEL_CMD_PROMPT__ = decodeURIComponent(window.__cmd_list[window.__cmd_index].getAttribute('data-prompt'));
window.__CHAT_MODEL_CMD_PROMPT__ = decodeURIComponent(
window.__cmd_list[window.__cmd_index].getAttribute('data-prompt'),
);
searchInput.value = `/${window.__cmd_list[window.__cmd_index].getAttribute('data-cmd')}`;
event.preventDefault();
}
@ -228,7 +250,8 @@ async function cmdTip() {
event.preventDefault();
}
if (window.__CHAT_MODEL_STATUS__ === 1 && event.keyCode === 9) { // TAB
if (window.__CHAT_MODEL_STATUS__ === 1 && event.keyCode === 9) {
// TAB
const data = searchInput.value.split('|->');
if (data[1]?.trim()) {
setPrompt(data[1]);
@ -238,7 +261,8 @@ async function cmdTip() {
}
// input text
if (window.__CHAT_MODEL_STATUS__ === 2 && event.keyCode === 9) { // TAB
if (window.__CHAT_MODEL_STATUS__ === 2 && event.keyCode === 9) {
// TAB
searchInput.value = window.__CHAT_MODEL_CMD_PROMPT__;
modelDom.innerHTML = '';
delete window.__CHAT_MODEL_STATUS__;
@ -253,7 +277,8 @@ async function cmdTip() {
}
// ------------------ send --------------------------------------------------------------------
if (event.keyCode === 13 && window.__CHAT_MODEL_CMD_PROMPT__) { // Enter
if (event.keyCode === 13 && window.__CHAT_MODEL_CMD_PROMPT__) {
// Enter
const data = searchInput.value.split('|->');
setPrompt(data[1]);
@ -286,7 +311,7 @@ async function cmdTip() {
return;
}
const result = data.filter(i => new RegExp(query.substring(1)).test(i.cmd));
const result = data.filter((i) => new RegExp(query.substring(1)).test(i.cmd));
if (result.length > 0) {
renderList(result);
} else {
@ -310,11 +335,8 @@ function initDom() {
delete window.__cmd_index;
}
if (
document.readyState === "complete" ||
document.readyState === "interactive"
) {
if (document.readyState === 'complete' || document.readyState === 'interactive') {
init();
} else {
document.addEventListener("DOMContentLoaded", init);
}
document.addEventListener('DOMContentLoaded', init);
}

View File

@ -1,4 +1,8 @@
// *** Core Script - IPC ***
/**
* @name core.js
* @version 0.1.0
* @url https://github.com/lencx/ChatGPT/tree/main/scripts/core.js
*/
const uid = () => window.crypto.getRandomValues(new Uint32Array(1))[0];
function transformCallback(callback = () => {}, once = false) {
@ -9,11 +13,11 @@ function transformCallback(callback = () => {}, once = false) {
if (once) {
Reflect.deleteProperty(window, prop);
}
return callback(result)
return callback(result);
},
writable: false,
configurable: true,
})
});
return identifier;
}
async function invoke(cmd, args) {
@ -22,16 +26,16 @@ async function invoke(cmd, args) {
const callback = transformCallback((e) => {
resolve(e);
Reflect.deleteProperty(window, `_${error}`);
}, true)
}, true);
const error = transformCallback((e) => {
reject(e);
Reflect.deleteProperty(window, `_${callback}`);
}, true)
}, true);
window.__TAURI_POST_MESSAGE__({
cmd,
callback,
error,
...args
...args,
});
});
}
@ -44,8 +48,8 @@ async function message(message) {
message: message.toString(),
title: null,
type: null,
buttonLabel: null
}
buttonLabel: null,
},
});
}
@ -62,39 +66,47 @@ async function init() {
async function platform() {
return invoke('platform', {
__tauriModule: 'Os',
message: { cmd: 'platform' }
message: { cmd: 'platform' },
});
}
if (__TAURI_METADATA__.__currentWindow.label !== 'tray') {
const _platform = await platform();
const chatConf = await invoke('get_app_conf') || {};
const chatConf = (await invoke('get_app_conf')) || {};
if (/darwin/.test(_platform) && !chatConf.titlebar) {
const topStyleDom = document.createElement("style");
const topStyleDom = document.createElement('style');
topStyleDom.innerHTML = `#chatgpt-app-window-top{position:fixed;top:0;z-index:999999999;width:100%;height:24px;background:transparent;cursor:grab;cursor:-webkit-grab;user-select:none;-webkit-user-select:none;}#chatgpt-app-window-top:active {cursor:grabbing;cursor:-webkit-grabbing;}`;
document.head.appendChild(topStyleDom);
const topDom = document.createElement("div");
topDom.id = "chatgpt-app-window-top";
const topDom = document.createElement('div');
topDom.id = 'chatgpt-app-window-top';
document.body.appendChild(topDom);
if (window.location.host === 'chat.openai.com') {
const nav = document.body.querySelector('nav');
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 + topDom.clientHeight}px !important}`;
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 + topDom.clientHeight
}px !important}`;
document.head.appendChild(navStyleDom);
}
}
topDom.addEventListener("mousedown", () => invoke("drag_window"));
topDom.addEventListener("touchstart", () => invoke("drag_window"));
topDom.addEventListener("dblclick", () => invoke("fullscreen"));
topDom.addEventListener('mousedown', () => invoke('drag_window'));
topDom.addEventListener('touchstart', () => invoke('drag_window'));
topDom.addEventListener('dblclick', () => invoke('fullscreen'));
}
}
document.addEventListener("click", (e) => {
const origin = e.target.closest("a");
document.addEventListener('click', (e) => {
const origin = e.target.closest('a');
if (!origin || !origin.target) return;
if (origin && origin.href && origin.target !== '_self') {
invoke('open_link', { url: origin.href });
@ -102,14 +114,18 @@ async function init() {
});
// Fix Chinese input method "Enter" on Safari
document.addEventListener("keydown", (e) => {
if(e.keyCode == 229) e.stopPropagation();
}, true)
document.addEventListener(
'keydown',
(e) => {
if (e.keyCode == 229) e.stopPropagation();
},
true,
);
if (window.location.host === 'chat.openai.com') {
window.__sync_prompts = async function() {
window.__sync_prompts = async function () {
await invoke('sync_prompts', { time: Date.now() });
}
};
}
coreZoom();
@ -145,11 +161,11 @@ function coreZoom() {
document.body.appendChild(zoomTipDom);
function zoom(callback) {
if (window.zoomSetTimeout) clearTimeout(window.zoomSetTimeout);
const htmlZoom = window.localStorage.getItem("htmlZoom") || "100%";
const html = document.getElementsByTagName("html")[0];
const htmlZoom = window.localStorage.getItem('htmlZoom') || '100%';
const html = document.getElementsByTagName('html')[0];
const zoom = callback(htmlZoom);
html.style.zoom = zoom;
window.localStorage.setItem("htmlZoom", zoom);
window.localStorage.setItem('htmlZoom', zoom);
zoomTipDom.innerHTML = zoom;
zoomTipDom.style.display = 'block';
zoomTipDom.classList.remove('ZoomTopTipAni');
@ -158,9 +174,9 @@ function coreZoom() {
}, 2500);
}
function zoomDefault() {
const htmlZoom = window.localStorage.getItem("htmlZoom");
const htmlZoom = window.localStorage.getItem('htmlZoom');
if (htmlZoom) {
document.getElementsByTagName("html")[0].style.zoom = htmlZoom;
document.getElementsByTagName('html')[0].style.zoom = htmlZoom;
}
}
function zoomIn() {
@ -176,20 +192,10 @@ function coreZoom() {
window.__zoomIn = zoomIn;
window.__zoomOut = zoomOut;
window.__zoom0 = zoom0;
window.__clearCache = () => {
window.localStorage.clear();
window.sessionStorage.clear();
window.applicationCache && window.applicationCache.update();
window.location.reload();
}
}
if (
document.readyState === "complete" ||
document.readyState === "interactive"
) {
if (document.readyState === 'complete' || document.readyState === 'interactive') {
init();
} else {
document.addEventListener("DOMContentLoaded", init);
}
document.addEventListener('DOMContentLoaded', init);
}

View File

@ -1,8 +1,12 @@
// *** Core Script - DALL·E 2 ***
/**
* @name dalle2.js
* @version 0.1.0
* @url https://github.com/lencx/ChatGPT/tree/main/scripts/dalle2.js
*/
function init() {
document.addEventListener("click", (e) => {
const origin = e.target.closest("a");
document.addEventListener('click', (e) => {
const origin = e.target.closest('a');
if (!origin || !origin.target) return;
if (origin && origin.href && origin.target !== '_self') {
if (/\/(login|signup)$/.test(window.location.href)) {
@ -27,14 +31,11 @@ function init() {
searchInput.focus();
searchInput.value = query;
}
}, 200)
}, 200);
}
if (
document.readyState === "complete" ||
document.readyState === "interactive"
) {
if (document.readyState === 'complete' || document.readyState === 'interactive') {
init();
} else {
document.addEventListener("DOMContentLoaded", init);
}
document.addEventListener('DOMContentLoaded', init);
}

View File

@ -1,27 +1,32 @@
// *** Core Script - Export ***
/**
* @name export.js
* @version 0.1.0
* @url https://github.com/lencx/ChatGPT/tree/main/scripts/export.js
*/
async function init() {
if (window.location.pathname === '/auth/login') return;
const buttonOuterHTMLFallback = `<button class="btn flex justify-center gap-2 btn-neutral" id="download-png-button">Try Again</button>`;
const buttonOuterHTMLFallback = `<button class="btn flex justify-center gap-2 btn-neutral">Try Again</button>`;
removeButtons();
if (window.buttonsInterval) {
clearInterval(window.buttonsInterval);
}
if (window.innerWidth < 767) return;
const chatConf = await invoke('get_app_conf') || {};
const chatConf = (await invoke('get_app_conf')) || {};
window.buttonsInterval = setInterval(() => {
const actionsArea = document.querySelector("form>div>div");
if (!actionsArea) {
const actionsArea = document.querySelector('form>div>div>div');
const hasBtn = document.querySelector('form>div>div>div button');
if (!actionsArea || !hasBtn) {
return;
}
if (shouldAddButtons(actionsArea)) {
let TryAgainButton = actionsArea.querySelector("button");
let TryAgainButton = actionsArea.querySelector('button');
if (!TryAgainButton) {
const parentNode = document.createElement("div");
const parentNode = document.createElement('div');
parentNode.innerHTML = buttonOuterHTMLFallback;
TryAgainButton = parentNode.querySelector("button");
TryAgainButton = parentNode.querySelector('button');
}
addActionsButtons(actionsArea, TryAgainButton, chatConf);
} else if (shouldRemoveButtons()) {
@ -30,12 +35,12 @@ async function init() {
}, 1000);
const Format = {
PNG: "png",
PDF: "pdf",
PNG: 'png',
PDF: 'pdf',
};
function shouldRemoveButtons() {
if (document.querySelector("form .text-2xl")) {
if (document.querySelector('form .text-2xl')) {
return true;
}
return false;
@ -43,7 +48,7 @@ async function init() {
function shouldAddButtons(actionsArea) {
// first, check if there's a "Try Again" button and no other buttons
const buttons = actionsArea.querySelectorAll("button");
const buttons = actionsArea.querySelectorAll('button');
const hasTryAgainButton = Array.from(buttons).some((button) => {
return !/download-/.test(button.id);
@ -51,11 +56,14 @@ async function init() {
const stopBtn = buttons?.[0]?.innerText;
if (/Stop generating/ig.test(stopBtn)) {
if (/Stop generating/gi.test(stopBtn)) {
return false;
}
if (buttons.length === 2 && (/Regenerate response/ig.test(stopBtn) || buttons[1].innerText === '')) {
if (
buttons.length === 2 &&
(/Regenerate response/gi.test(stopBtn) || buttons[1].innerText === '')
) {
return true;
}
@ -64,14 +72,14 @@ async function init() {
}
// otherwise, check if open screen is not visible
const isOpenScreen = document.querySelector("h1.text-4xl");
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]");
const finishedConversation = document.querySelector('form button>svg');
const hasShareButtons = actionsArea.querySelectorAll('button[share-ext]');
if (finishedConversation && !hasShareButtons.length) {
return true;
}
@ -80,10 +88,10 @@ async function init() {
}
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");
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();
}
@ -101,9 +109,9 @@ async function init() {
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.id = 'download-markdown-button';
exportMd.setAttribute('share-ext', 'true');
exportMd.title = 'Export Markdown';
exportMd.innerHTML = setIcon('md');
exportMd.onclick = () => {
@ -113,9 +121,9 @@ async function init() {
// Generate PNG
const downloadPngButton = TryAgainButton.cloneNode(true);
downloadPngButton.id = "download-png-button";
downloadPngButton.setAttribute("share-ext", "true");
downloadPngButton.title = "Generate PNG";
downloadPngButton.id = 'download-png-button';
downloadPngButton.setAttribute('share-ext', 'true');
downloadPngButton.title = 'Generate PNG';
downloadPngButton.innerHTML = setIcon('png');
downloadPngButton.onclick = () => {
downloadThread();
@ -124,9 +132,9 @@ async function init() {
// Generate PDF
const downloadPdfButton = TryAgainButton.cloneNode(true);
downloadPdfButton.id = "download-pdf-button";
downloadPdfButton.setAttribute("share-ext", "true");
downloadPdfButton.title = "Download PDF";
downloadPdfButton.id = 'download-pdf-button';
downloadPdfButton.setAttribute('share-ext', 'true');
downloadPdfButton.title = 'Download PDF';
downloadPdfButton.innerHTML = setIcon('pdf');
downloadPdfButton.onclick = () => {
downloadThread({ as: Format.PDF });
@ -135,8 +143,8 @@ async function init() {
// Refresh
const refreshButton = TryAgainButton.cloneNode(true);
refreshButton.id = "refresh-page-button";
refreshButton.title = "Refresh the Page";
refreshButton.id = 'refresh-page-button';
refreshButton.title = 'Refresh the Page';
refreshButton.innerHTML = setIcon('refresh');
refreshButton.onclick = () => {
window.location.reload();
@ -145,13 +153,15 @@ async function init() {
}
async function exportMarkdown() {
const content = Array.from(document.querySelectorAll('main .items-center>div')).map(i => {
let j = i.cloneNode(true);
if (/dark\:bg-gray-800/.test(i.getAttribute('class'))) {
j.innerHTML = `<blockquote>${i.innerHTML}</blockquote>`;
}
return j.innerHTML;
}).join('');
const content = Array.from(document.querySelectorAll('main .items-center>div'))
.map((i) => {
let j = i.cloneNode(true);
if (/dark\:bg-gray-800/.test(i.getAttribute('class'))) {
j.innerHTML = `<blockquote>${i.innerHTML}</blockquote>`;
}
return j.innerHTML;
})
.join('');
const data = ExportMD.turndown(content);
const { id, filename } = getName();
await invoke('save_file', { name: `notes/${id}.md`, content: data });
@ -170,7 +180,7 @@ async function init() {
}).then(async function (canvas) {
elements.restoreLocation();
window.devicePixelRatio = pixelRatio;
const imgData = canvas.toDataURL("image/png");
const imgData = canvas.toDataURL('image/png');
requestAnimationFrame(() => {
if (as === Format.PDF) {
return handlePdf(imgData, canvas, pixelRatio);
@ -182,36 +192,26 @@ async function init() {
}
async function handleImg(imgData) {
const binaryData = atob(imgData.split("base64,")[1]);
const binaryData = atob(imgData.split('base64,')[1]);
const data = [];
for (let i = 0; i < binaryData.length; i++) {
data.push(binaryData.charCodeAt(i));
}
const { pathname, id, filename } = getName();
await invoke('download', { name: `download/img/${id}.png`, blob: data });
await invoke('download_list', { pathname, filename, id, dir: 'download' });
const name = `ChatGPT_${formatDateTime()}.png`;
await invoke('download_file', { name: name, blob: data });
}
async function handlePdf(imgData, canvas, pixelRatio) {
const { jsPDF } = window.jspdf;
const orientation = canvas.width > canvas.height ? "l" : "p";
var pdf = new jsPDF(orientation, "pt", [
canvas.width / pixelRatio,
canvas.height / pixelRatio,
]);
const orientation = canvas.width > canvas.height ? 'l' : 'p';
var pdf = new jsPDF(orientation, 'pt', [canvas.width / pixelRatio, canvas.height / pixelRatio]);
var pdfWidth = pdf.internal.pageSize.getWidth();
var pdfHeight = pdf.internal.pageSize.getHeight();
pdf.addImage(imgData, "PNG", 0, 0, pdfWidth, pdfHeight, '', 'FAST');
const { pathname, id, filename } = getName();
pdf.addImage(imgData, 'PNG', 0, 0, pdfWidth, pdfHeight, '', 'FAST');
const data = pdf.__private__.getArrayBuffer(pdf.__private__.buildDocument());
await invoke('download', { name: `download/pdf/${id}.pdf`, blob: Array.from(new Uint8Array(data)) });
await invoke('download_list', { pathname, filename, id, dir: 'download' });
}
function getName() {
const id = window.crypto.getRandomValues(new Uint32Array(1))[0].toString(36);
const name = document.querySelector('nav .overflow-y-auto a.hover\\:bg-gray-800')?.innerText?.trim() || '';
return { filename: name ? name : id, id, pathname: 'chat.download.json' };
const name = `ChatGPT_${formatDateTime()}.pdf`;
await invoke('download_file', { name: name, blob: Array.from(new Uint8Array(data)) });
}
class Elements {
@ -222,62 +222,62 @@ async function init() {
// this.threadWrapper = document.querySelector(".cdfdFe");
this.spacer = document.querySelector("[class*='h-48'].w-full.flex-shrink-0");
this.thread = document.querySelector(
"[class*='react-scroll-to-bottom']>[class*='react-scroll-to-bottom']>div"
"[class*='react-scroll-to-bottom']>[class*='react-scroll-to-bottom']>div",
);
// fix: old chat https://github.com/lencx/ChatGPT/issues/185
if (!this.thread) {
this.thread = document.querySelector("main .overflow-y-auto");
this.thread = document.querySelector('main .overflow-y-auto');
}
// h-full overflow-y-auto
this.positionForm = document.querySelector("form").parentNode;
this.positionForm = document.querySelector('form').parentNode;
// this.styledThread = document.querySelector("main");
// this.threadContent = document.querySelector(".gAnhyd");
this.scroller = Array.from(
document.querySelectorAll('[class*="react-scroll-to"]')
).filter((el) => el.classList.contains("h-full"))[0];
this.scroller = Array.from(document.querySelectorAll('[class*="react-scroll-to"]')).filter(
(el) => el.classList.contains('h-full'),
)[0];
// fix: old chat
if (!this.scroller) {
this.scroller = document.querySelector('main .overflow-y-auto');
}
this.hiddens = Array.from(document.querySelectorAll(".overflow-hidden"));
this.images = Array.from(document.querySelectorAll("img[srcset]"));
this.hiddens = Array.from(document.querySelectorAll('.overflow-hidden'));
this.images = Array.from(document.querySelectorAll('img[srcset]'));
}
fixLocation() {
this.hiddens.forEach((el) => {
el.classList.remove("overflow-hidden");
el.classList.remove('overflow-hidden');
});
this.spacer.style.display = "none";
this.thread.style.maxWidth = "960px";
this.thread.style.marginInline = "auto";
this.positionForm.style.display = "none";
this.scroller.classList.remove("h-full");
this.scroller.style.minHeight = "100vh";
this.spacer.style.display = 'none';
this.thread.style.maxWidth = '960px';
this.thread.style.marginInline = 'auto';
this.positionForm.style.display = 'none';
this.scroller.classList.remove('h-full');
this.scroller.style.minHeight = '100vh';
this.images.forEach((img) => {
const srcset = img.getAttribute("srcset");
img.setAttribute("srcset_old", srcset);
img.setAttribute("srcset", "");
const srcset = img.getAttribute('srcset');
img.setAttribute('srcset_old', srcset);
img.setAttribute('srcset', '');
});
//Fix to the text shifting down when generating the canvas
document.body.style.lineHeight = "0.5";
document.body.style.lineHeight = '0.5';
}
restoreLocation() {
this.hiddens.forEach((el) => {
el.classList.add("overflow-hidden");
el.classList.add('overflow-hidden');
});
this.spacer.style.display = null;
this.thread.style.maxWidth = null;
this.thread.style.marginInline = null;
this.positionForm.style.display = null;
this.scroller.classList.add("h-full");
this.scroller.classList.add('h-full');
this.scroller.style.minHeight = null;
this.images.forEach((img) => {
const srcset = img.getAttribute("srcset_old");
img.setAttribute("srcset", srcset);
img.setAttribute("srcset_old", "");
const srcset = img.getAttribute('srcset_old');
img.setAttribute('srcset', srcset);
img.setAttribute('srcset_old', '');
});
document.body.style.lineHeight = null;
}
@ -292,15 +292,31 @@ async function init() {
refresh: `<svg class="chatappico refresh" viewBox="0 0 1024 1024"><path d="M512 63.5C264.3 63.5 63.5 264.3 63.5 512S264.3 960.5 512 960.5 960.5 759.7 960.5 512 759.7 63.5 512 63.5zM198 509.6h87.6c0-136.3 102.3-243.4 233.7-238.5 43.8 0 82.8 14.6 121.7 34.1L597.2 349c-24.4-9.8-53.6-19.5-82.8-19.5-92.5 0-170.4 77.9-170.4 180.1h87.6L314.8 631.3 198 509.6z m540.3-0.1c0 131.4-102.2 243.4-228.8 243.4-43.8 0-82.8-19.4-121.7-38.9l43.8-43.8c24.4 9.8 53.6 19.5 82.8 19.5 92.5 0 170.4-77.9 170.4-180.1h-92.5l116.9-121.7L826 509.5h-87.7z" fill="currentColor"></path></svg>`,
}[type];
}
function formatDateTime() {
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0');
const day = String(now.getDate()).padStart(2, '0');
const hours = String(now.getHours()).padStart(2, '0');
const minutes = String(now.getMinutes()).padStart(2, '0');
const seconds = String(now.getSeconds()).padStart(2, '0');
const formattedDateTime = `${year}_${month}_${day}_${hours}_${minutes}_${seconds}`;
return formattedDateTime;
}
function getName() {
const id = window.crypto.getRandomValues(new Uint32Array(1))[0].toString(36);
const name =
document.querySelector('nav .overflow-y-auto a.hover\\:bg-gray-800')?.innerText?.trim() || '';
return { filename: name ? name : id, id, pathname: 'chat.download.json' };
}
}
window.addEventListener('resize', init);
if (
document.readyState === "complete" ||
document.readyState === "interactive"
) {
if (document.readyState === 'complete' || document.readyState === 'interactive') {
init();
} else {
document.addEventListener("DOMContentLoaded", init);
document.addEventListener('DOMContentLoaded', init);
}

41
scripts/manifest.json Normal file
View File

@ -0,0 +1,41 @@
{
"name": "ChatGPT Script",
"author": "lencx",
"scripts": [
{
"name": "core.js",
"version": "0.1.0",
"url": "https://github.com/lencx/ChatGPT/tree/main/scripts/chat.js"
},
{
"name": "cmd.js",
"version": "0.1.0",
"url": "https://github.com/lencx/ChatGPT/tree/main/scripts/cmd.js"
},
{
"name": "core.js",
"version": "0.1.0",
"url": "https://github.com/lencx/ChatGPT/tree/main/scripts/core.js"
},
{
"name": "dalle2.js",
"version": "0.1.0",
"url": "https://github.com/lencx/ChatGPT/tree/main/scripts/dalle2.js"
},
{
"name": "export.js",
"version": "0.1.0",
"url": "https://github.com/lencx/ChatGPT/tree/main/scripts/export.js"
},
{
"name": "markdown.export.js",
"version": "0.1.0",
"url": "https://github.com/lencx/ChatGPT/tree/main/scripts/markdown.export.js"
},
{
"name": "popup.core.js",
"version": "0.1.0",
"url": "https://github.com/lencx/ChatGPT/tree/main/scripts/popup.core.js"
}
]
}

View File

@ -1,18 +1,24 @@
/**
* @name markdown.export.js
* @version 0.1.0
* @url https://github.com/lencx/ChatGPT/tree/main/scripts/markdown.export.js
*/
var ExportMD = (function () {
if (!TurndownService || !turndownPluginGfm) return;
const hljsREG = /^.*(hljs).*(language-[a-z0-9]+).*$/i;
const gfm = turndownPluginGfm.gfm
const gfm = turndownPluginGfm.gfm;
const turndownService = new TurndownService({
hr: '---'
hr: '---',
})
.use(gfm)
.addRule('code', {
filter: (node) => {
filter(node) {
if (node.nodeName === 'CODE' && hljsREG.test(node.classList.value)) {
return 'code';
}
},
replacement: (content, node) => {
replacement(content, node) {
const classStr = node.getAttribute('class');
if (hljsREG.test(classStr)) {
const lang = classStr.match(/.*language-(\w+)/)[1];
@ -21,18 +27,26 @@ var ExportMD = (function () {
}
return `\`\`\`\n${content}\n\`\`\``;
}
}
},
})
.addRule('ignore-text', {
filter: (node) => {
if (node.nodeName === 'DIV' && node.classList.contains('!invisible')) {
return 'ignore-text';
}
},
replacement: () => '',
})
.addRule('ignore', {
filter: ['button', 'img'],
filter: ['button', 'img', 'svg'],
replacement: () => '',
})
.addRule('table', {
filter: 'table',
replacement: function(content, node) {
replacement(content, node) {
return `\`\`\`${content}\n\`\`\``;
},
});
return turndownService;
}({}));
})({});

View File

@ -1,7 +1,11 @@
// *** Core Script - DALL·E 2 Core ***
/**
* @name popup.core.js
* @version 0.1.0
* @url https://github.com/lencx/ChatGPT/tree/main/scripts/popup.core.js
*/
async function init() {
const chatConf = await invoke('get_app_conf') || {};
const chatConf = (await invoke('get_app_conf')) || {};
if (!chatConf.popup_search) return;
if (!window.FloatingUIDOM) return;
@ -33,13 +37,15 @@ async function init() {
document.body.addEventListener('mousedown', async (e) => {
selectionMenu.style.display = 'none';
if (e.target.id === 'chagpt-selection-menu') {
await invoke('dalle2_search_window', { query: encodeURIComponent(window.__DALLE2_CONTENT__) });
await invoke('dalle2_search_window', {
query: encodeURIComponent(window.__DALLE2_CONTENT__),
});
} else {
delete window.__DALLE2_CONTENT__;
}
});
document.body.addEventListener("mouseup", async (e) => {
document.body.addEventListener('mouseup', async (e) => {
selectionMenu.style.display = 'none';
const selection = window.getSelection();
window.__DALLE2_CONTENT__ = selection.toString().trim();
@ -59,12 +65,8 @@ async function init() {
selectionMenu.style.display = 'block';
computePosition(rootEl, selectionMenu, {
placement: 'top',
middleware: [
flip(),
offset(5),
shift({ padding: 5 })
]
}).then(({x, y}) => {
middleware: [flip(), offset(5), shift({ padding: 5 })],
}).then(({ x, y }) => {
Object.assign(selectionMenu.style, {
left: `${x}px`,
top: `${y}px`,
@ -74,11 +76,8 @@ async function init() {
});
}
if (
document.readyState === "complete" ||
document.readyState === "interactive"
) {
if (document.readyState === 'complete' || document.readyState === 'interactive') {
init();
} else {
document.addEventListener("DOMContentLoaded", init);
document.addEventListener('DOMContentLoaded', init);
}

44
sponsor.html Normal file
View File

@ -0,0 +1,44 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>ChatGPT</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,
Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
margin: 0;
padding: 0;
}
p {
margin: 0;
padding: 5px 10px;
font-weight: bold;
text-align: center;
font-size: 12px;
color: #686868;
}
a {
margin: 20px 10px;
cursor: pointer;
}
img {
width: 240px;
}
</style>
</head>
<body>
<p>🙏 Thank you for your support, which can help ChatGPT Free version to develop better!</p>
<p>🙏 感谢您的支持,可以让 ChatGPT 免费版更好的发展!</p>
<a target="_blank" href="https://www.buymeacoffee.com/lencx">
<img style="width: 160px" src="/bmc.png" />
</a>
<img src="/wxp.png" />
</body>
</html>

View File

@ -22,11 +22,11 @@ thiserror = "1.0.38"
walkdir = "2.3.2"
regex = "1.7.0"
reqwest = "0.11.13"
wry = "0.24.1"
dark-light = "1.0.0"
wry = "0.*"
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1.23.0", features = ["macros"] }
tauri = { version = "1.2.4", 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", "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" }

View File

@ -18,30 +18,31 @@ pub fn fullscreen(app: AppHandle) {
}
}
#[command]
pub fn download(app: AppHandle, name: String, blob: Vec<u8>) {
let win = app.app_handle().get_window("core");
let path = utils::app_root().join(PathBuf::from(name));
utils::create_file(&path).unwrap();
fs::write(&path, blob).unwrap();
tauri::api::dialog::message(
win.as_ref(),
"Save File",
format!("PATH: {}", path.display()),
);
}
// #[command]
// pub fn download(app: AppHandle, name: String, blob: Vec<u8>) {
// let win = app.app_handle().get_window("core");
// let path = utils::app_root().join(PathBuf::from(name));
// utils::create_file(&path).unwrap();
// fs::write(&path, blob).unwrap();
// tauri::api::dialog::message(
// win.as_ref(),
// "Save File",
// format!("PATH: {}", path.display()),
// );
// }
#[command]
pub fn save_file(app: AppHandle, name: String, content: String) {
let win = app.app_handle().get_window("core");
pub fn save_file(_app: AppHandle, name: String, content: String) {
// let win = app.app_handle().get_window("core");
let path = utils::app_root().join(PathBuf::from(name));
utils::create_file(&path).unwrap();
fs::write(&path, content).unwrap();
tauri::api::dialog::message(
win.as_ref(),
"Save File",
format!("PATH: {}", path.display()),
);
utils::open_file(path);
// tauri::api::dialog::message(
// win.as_ref(),
// "Save File",
// format!("PATH: {}", path.display()),
// );
}
#[command]
@ -59,6 +60,13 @@ pub fn open_file(path: PathBuf) {
utils::open_file(path);
}
#[command]
pub fn download_file(name: String, blob: Vec<u8>) {
let file = tauri::api::path::download_dir().unwrap().join(name);
fs::write(&file, blob).unwrap();
utils::open_file(file);
}
#[command]
pub async fn get_data(app: AppHandle, url: String, is_msg: Option<bool>) -> Option<String> {
let is_msg = is_msg.unwrap_or(false);

View File

@ -140,7 +140,6 @@ pub fn init() -> Menu {
popup_search_menu.into(),
CustomMenuItem::new("sync_prompts", "Sync Prompts").into(),
MenuItem::Separator.into(),
CustomMenuItem::new("clear_cache", "Clear Cache").into(),
CustomMenuItem::new("go_conf", "Go to Config")
.accelerator("CmdOrCtrl+Shift+G")
.into(),
@ -150,7 +149,7 @@ pub fn init() -> Menu {
CustomMenuItem::new("clear_conf", "Clear Config").into(),
MenuItem::Separator.into(),
CustomMenuItem::new("nofwl", "NoFWL Desktop Application").into(),
CustomMenuItem::new("buy_coffee", "Buy lencx a coffee").into(),
CustomMenuItem::new("sponsor", "Sponsor Author").into(),
]),
);
@ -243,20 +242,6 @@ pub fn menu_handler(event: WindowMenuEvent<tauri::Wry>) {
"inject_script" => open(&app, &script_path),
"go_conf" => utils::open_file(utils::app_root()),
"clear_conf" => utils::clear_conf(&app),
"clear_cache" => {
let main_win = app.get_window("core");
let tray_win = app.get_window("tray");
if let Some(main) = main_win {
main
.eval("window.__clearCache && window.__clearCache()")
.unwrap();
}
if let Some(tray) = tray_win {
tray
.eval("window.__clearCache && window.__clearCache()")
.unwrap();
}
}
"app_website" => window::cmd::wa_window(
app,
"app_website".into(),
@ -265,7 +250,7 @@ pub fn menu_handler(event: WindowMenuEvent<tauri::Wry>) {
None,
),
"nofwl" => open(&app, conf::NOFWL_APP),
"buy_coffee" => open(&app, conf::BUY_COFFEE),
"sponsor" => window::sponsor_window(app),
"popup_search" => {
let app_conf = AppConf::read();
let popup_search = !app_conf.popup_search;

View File

View File

@ -52,13 +52,8 @@ pub fn init(app: &mut App) -> std::result::Result<(), Box<dyn std::error::Error>
} else {
let app = app.handle();
tauri::async_runtime::spawn(async move {
let link = if app_conf2.main_dashboard {
"index.html"
} else {
&url
};
info!("main_window: {}", link);
let mut main_win = WindowBuilder::new(&app, "core", WindowUrl::App(link.into()))
let url2 = &url;
let mut main_win = WindowBuilder::new(&app, "core", WindowUrl::App(url2.into()))
.title("ChatGPT")
.resizable(true)
.fullscreen(false)
@ -66,7 +61,7 @@ pub fn init(app: &mut App) -> std::result::Result<(), Box<dyn std::error::Error>
.theme(Some(theme))
.always_on_top(app_conf2.stay_on_top)
.initialization_script(&utils::user_script())
.initialization_script(include_str!("../scripts/core.js"))
.initialization_script(include_str!("../../../scripts/core.js"))
.user_agent(&app_conf2.ua_window);
#[cfg(target_os = "macos")]
@ -76,7 +71,7 @@ pub fn init(app: &mut App) -> std::result::Result<(), Box<dyn std::error::Error>
.hidden_title(true);
}
if url == "https://chat.openai.com" && !app_conf2.main_dashboard {
if url == "https://chat.openai.com" {
main_win = main_win
.initialization_script(include_str!("../vendors/floating-ui-core.js"))
.initialization_script(include_str!("../vendors/floating-ui-dom.js"))
@ -84,11 +79,11 @@ pub fn init(app: &mut App) -> std::result::Result<(), Box<dyn std::error::Error>
.initialization_script(include_str!("../vendors/jspdf.js"))
.initialization_script(include_str!("../vendors/turndown.js"))
.initialization_script(include_str!("../vendors/turndown-plugin-gfm.js"))
.initialization_script(include_str!("../scripts/popup.core.js"))
.initialization_script(include_str!("../scripts/export.js"))
.initialization_script(include_str!("../scripts/markdown.export.js"))
.initialization_script(include_str!("../scripts/cmd.js"))
.initialization_script(include_str!("../scripts/chat.js"))
.initialization_script(include_str!("../../../scripts/popup.core.js"))
.initialization_script(include_str!("../../../scripts/export.js"))
.initialization_script(include_str!("../../../scripts/markdown.export.js"))
.initialization_script(include_str!("../../../scripts/cmd.js"))
.initialization_script(include_str!("../../../scripts/chat.js"))
}
main_win.build().unwrap();

View File

@ -23,16 +23,16 @@ pub fn tray_window(handle: &tauri::AppHandle) {
.always_on_top(true)
.theme(Some(theme))
.initialization_script(&utils::user_script())
.initialization_script(include_str!("../scripts/core.js"))
.initialization_script(include_str!("../../../scripts/core.js"))
.user_agent(&app_conf.ua_tray);
if app_conf.tray_origin == "https://chat.openai.com" && !app_conf.tray_dashboard {
tray_win = tray_win
.initialization_script(include_str!("../vendors/floating-ui-core.js"))
.initialization_script(include_str!("../vendors/floating-ui-dom.js"))
.initialization_script(include_str!("../scripts/cmd.js"))
.initialization_script(include_str!("../scripts/chat.js"))
.initialization_script(include_str!("../scripts/popup.core.js"))
.initialization_script(include_str!("../../../scripts/cmd.js"))
.initialization_script(include_str!("../../../scripts/chat.js"))
.initialization_script(include_str!("../../../scripts/popup.core.js"))
}
tray_win.build().unwrap().hide().unwrap();
@ -78,9 +78,9 @@ pub fn dalle2_window(
.inner_size(800.0, 600.0)
.always_on_top(false)
.theme(Some(theme))
.initialization_script(include_str!("../scripts/core.js"))
.initialization_script(include_str!("../../../scripts/core.js"))
.initialization_script(&query)
.initialization_script(include_str!("../scripts/dalle2.js"))
.initialization_script(include_str!("../../../scripts/dalle2.js"))
.build()
.unwrap();
});
@ -91,6 +91,23 @@ pub fn dalle2_window(
}
}
pub fn sponsor_window(handle: tauri::AppHandle) {
tauri::async_runtime::spawn(async move {
if let Some(win) = handle.get_window("sponsor") {
win.show().unwrap()
} else {
WindowBuilder::new(&handle, "sponsor", WindowUrl::App("sponsor.html".into()))
.title("Sponsor")
.resizable(true)
.fullscreen(false)
.inner_size(600.0, 600.0)
.min_inner_size(600.0, 600.0)
.build()
.unwrap();
}
});
}
pub mod cmd {
use super::*;
use log::info;
@ -144,7 +161,7 @@ pub mod cmd {
tauri::async_runtime::spawn(async move {
tauri::WindowBuilder::new(&app, label, tauri::WindowUrl::App(url.parse().unwrap()))
.initialization_script(&script.unwrap_or_default())
.initialization_script(include_str!("../scripts/core.js"))
.initialization_script(include_str!("../../../scripts/core.js"))
.title(title)
.inner_size(960.0, 700.0)
.resizable(true)

View File

@ -67,7 +67,7 @@ impl AppConf {
Self {
titlebar: !cfg!(target_os = "macos"),
hide_dock_icon: false,
save_window_state: false,
save_window_state: true,
theme: "light".into(),
auto_update: "prompt".into(),
#[cfg(target_os = "macos")]

View File

@ -55,11 +55,11 @@ async fn main() {
.invoke_handler(tauri::generate_handler![
cmd::drag_window,
cmd::fullscreen,
cmd::download,
cmd::save_file,
cmd::open_link,
cmd::run_check_update,
cmd::open_file,
cmd::download_file,
cmd::get_data,
gpt::get_chat_model_cmd,
gpt::parse_prompt,

View File

@ -87,7 +87,24 @@
}
},
"security": {
"csp": null
"csp": null,
"dangerousRemoteDomainIpcAccess": [
{
"windows": ["core", "main", "tray"],
"domain": "chat.openai.com",
"enableTauriAPI": true
},
{
"windows": ["core", "main"],
"domain": "labs.openai.com",
"enableTauriAPI": true
},
{
"windows": ["core", "main", "tray"],
"domain": "openai.com",
"enableTauriAPI": true
}
]
},
"updater": {
"active": true,

5
src/main.scss vendored
View File

@ -73,6 +73,11 @@ body,
}
}
.action-btn,
.file-path {
cursor: pointer;
}
.chat-file-path {
font-size: 12px;
font-weight: 500;

38
src/routes.tsx vendored
View File

@ -5,24 +5,21 @@ import {
SyncOutlined,
FileSyncOutlined,
UserOutlined,
DownloadOutlined,
FormOutlined,
GlobalOutlined,
InfoCircleOutlined,
CodeOutlined,
} from '@ant-design/icons';
import type { MenuProps } from 'antd';
import Settings from '@/view/settings';
import About from '@/view/about';
import Awesome from '@/view/awesome';
import UserCustom from '@/view/model/UserCustom';
import SyncPrompts from '@/view/model/SyncPrompts';
import SyncCustom from '@/view/model/SyncCustom';
import SyncRecord from '@/view/model/SyncRecord';
import Download from '@/view/download';
import Scripts from '@/view/scripts';
import UserCustom from '@/view/prompts/UserCustom';
import SyncPrompts from '@/view/prompts/SyncPrompts';
import SyncCustom from '@/view/prompts/SyncCustom';
import SyncRecord from '@/view/prompts/SyncRecord';
import Notes from '@/view/notes';
import Markdown from '@/view/markdown';
import Dashboard from '@/view/dashboard';
export type ChatRouteMetaObject = {
label: string;
@ -46,14 +43,6 @@ export const routes: Array<ChatRouteObject> = [
icon: <SettingOutlined />,
},
},
{
path: '/awesome',
element: <Awesome />,
meta: {
label: 'Awesome',
icon: <GlobalOutlined />,
},
},
{
path: '/notes',
element: <Notes />,
@ -68,9 +57,9 @@ export const routes: Array<ChatRouteObject> = [
hideMenu: true,
},
{
path: '/model',
path: '/prompts',
meta: {
label: 'Language Model',
label: 'Prompts',
icon: <BulbOutlined />,
},
children: [
@ -107,11 +96,11 @@ export const routes: Array<ChatRouteObject> = [
],
},
{
path: '/download',
element: <Download />,
path: '/scripts',
element: <Scripts />,
meta: {
label: 'Download',
icon: <DownloadOutlined />,
label: 'Scripts',
icon: <CodeOutlined />,
},
},
{
@ -124,8 +113,7 @@ export const routes: Array<ChatRouteObject> = [
},
{
path: '/',
element: <Dashboard />,
hideMenu: true,
element: <Settings />,
},
];

View File

@ -69,10 +69,6 @@ const AboutChatGPT = () => {
src="https://user-images.githubusercontent.com/16164244/207228025-117b5f77-c5d2-48c2-a070-774b7a1596f2.png"
/>
</p>
<img
width="250"
src="https://user-images.githubusercontent.com/16164244/219439614-d5c3710c-e0b3-4df9-9b3c-c150ba0ba5f1.png"
/>
</div>
);
};

View File

@ -1,128 +0,0 @@
import {
useEffect,
useState,
ForwardRefRenderFunction,
useImperativeHandle,
forwardRef,
} from 'react';
import { Form, Input, Select, Tooltip } from 'antd';
import { v4 } from 'uuid';
import type { FormProps } from 'antd';
import { DISABLE_AUTO_COMPLETE, chatRoot } from '@/utils';
import useInit from '@/hooks/useInit';
interface SyncFormProps {
record?: Record<string | symbol, any> | null;
type: string;
}
const initFormValue = {
act: '',
enable: true,
tags: [],
prompt: '',
};
const SyncForm: ForwardRefRenderFunction<FormProps, SyncFormProps> = ({ record, type }, ref) => {
const isDisabled = type === 'edit';
const [form] = Form.useForm();
useImperativeHandle(ref, () => ({ form }));
const [root, setRoot] = useState('');
useInit(async () => {
setRoot(await chatRoot());
});
useEffect(() => {
if (record) {
form.setFieldsValue(record);
}
}, [record]);
const pathOptions = (
<Form.Item noStyle name="protocol" initialValue="https">
<Select disabled={isDisabled}>
<Select.Option value="local">{root}</Select.Option>
<Select.Option value="http">http://</Select.Option>
<Select.Option value="https">https://</Select.Option>
</Select>
</Form.Item>
);
const extOptions = (
<Form.Item noStyle name="ext" initialValue="json">
<Select disabled={isDisabled}>
<Select.Option value="csv">.csv</Select.Option>
<Select.Option value="json">.json</Select.Option>
</Select>
</Form.Item>
);
const jsonTip = (
<Tooltip
title={
<pre>
{JSON.stringify(
[
{ cmd: '', act: '', prompt: '' },
{ cmd: '', act: '', prompt: '' },
],
null,
2,
)}
</pre>
}
>
<a>JSON</a>
</Tooltip>
);
const csvTip = (
<Tooltip
title={
<pre>{`"cmd","act","prompt"
"cmd","act","prompt"
"cmd","act","prompt"
"cmd","act","prompt"`}</pre>
}
>
<a>CSV</a>
</Tooltip>
);
return (
<>
<Form form={form} labelCol={{ span: 4 }} initialValues={initFormValue}>
<Form.Item
label="Name"
name="name"
rules={[{ required: true, message: 'Please enter a name!' }]}
>
<Input placeholder="Please enter a name" {...DISABLE_AUTO_COMPLETE} />
</Form.Item>
<Form.Item
label="PATH"
name="path"
rules={[{ required: true, message: 'Please enter the path!' }]}
>
<Input
placeholder="YOUR_PATH"
addonBefore={pathOptions}
addonAfter={extOptions}
{...DISABLE_AUTO_COMPLETE}
/>
</Form.Item>
<Form.Item style={{ display: 'none' }} name="id" initialValue={v4().replace(/-/g, '')}>
<input />
</Form.Item>
</Form>
<div className="tip">
<p>
The file supports only {csvTip} and {jsonTip} formats.
</p>
</div>
</>
);
};
export default forwardRef(SyncForm);

192
src/view/prompts/SyncCustom/Form.tsx vendored Normal file
View File

@ -0,0 +1,192 @@
import {
useEffect,
useState,
ForwardRefRenderFunction,
useImperativeHandle,
forwardRef,
} from 'react';
import { Form, Input, Radio, Upload, Tooltip, message } from 'antd';
import { v4 } from 'uuid';
import { InboxOutlined } from '@ant-design/icons';
import type { FormProps, RadioChangeEvent, UploadProps, UploadFile } from 'antd';
import { DISABLE_AUTO_COMPLETE, chatRoot } from '@/utils';
// import useInit from '@/hooks/useInit';
interface SyncFormProps {
record?: Record<string | symbol, any> | null;
type: string;
}
const initFormValue = {
name: '',
url: '',
file: null,
protocol: 'https',
};
const SyncForm: ForwardRefRenderFunction<FormProps, SyncFormProps> = ({ record, type }, ref) => {
// const isDisabled = type === 'edit';
const [form] = Form.useForm();
useImperativeHandle(ref, () => ({ form }));
// const [root, setRoot] = useState('');
const [protocol, setProtocol] = useState('https');
const [fileList, setFileList] = useState<UploadFile[]>([]);
// useInit(async () => {
// setRoot(await chatRoot());
// });
useEffect(() => {
if (record) {
form.setFieldsValue(record);
}
}, [record]);
// const pathOptions = (
// <Form.Item noStyle name="protocol" initialValue="https">
// <Select disabled={isDisabled}>
// <Select.Option value="local">{root}</Select.Option>
// <Select.Option value="http">http://</Select.Option>
// <Select.Option value="https">https://</Select.Option>
// </Select>
// </Form.Item>
// );
// const extOptions = (
// <Form.Item noStyle name="ext" initialValue="json">
// <Select disabled={isDisabled}>
// <Select.Option value="csv">.csv</Select.Option>
// <Select.Option value="json">.json</Select.Option>
// </Select>
// </Form.Item>
// );
const jsonTip = (
<Tooltip
title={
<pre>
{JSON.stringify(
[
{ cmd: '', act: '', prompt: '' },
{ cmd: '', act: '', prompt: '' },
],
null,
2,
)}
</pre>
}
>
<a>.json</a>
</Tooltip>
);
const csvTip = (
<Tooltip
title={
<pre>{`"cmd","act","prompt"
"cmd","act","prompt"
"cmd","act","prompt"
"cmd","act","prompt"`}</pre>
}
>
<a>.csv</a>
</Tooltip>
);
const handleType = (e: RadioChangeEvent) => {
setProtocol(e.target.value);
};
const uploadOptions: UploadProps = {
onRemove: () => {
setFileList([]);
},
customRequest: () => {},
beforeUpload: (file) => {
const isCSV = /.csv$/.test(file.name);
const isJSON = /.json$/.test(file.name);
const isOk = isCSV || isJSON;
if (!isOk) {
message.error('You can only upload .json or .csv file!');
} else {
setFileList([file]);
}
return isOk || Upload.LIST_IGNORE;
},
maxCount: 1,
fileList,
};
return (
<>
<Form form={form} labelCol={{ span: 4 }} initialValues={initFormValue}>
<Form.Item
label="Name"
name="name"
rules={[{ required: true, message: 'Please enter a name!' }]}
>
<Input placeholder="Please enter a name" {...DISABLE_AUTO_COMPLETE} />
</Form.Item>
<Form.Item label="Protocol" name="protocol" rules={[{ required: true }]}>
<Radio.Group onChange={handleType} value={protocol}>
<Radio value="https">https</Radio>
<Radio value="http">http</Radio>
<Radio value="local">local</Radio>
</Radio.Group>
</Form.Item>
<div style={{ marginLeft: 30, color: '#888' }}>
<p>
<b>.ext</b>: The file supports only {csvTip} and {jsonTip} formats.
</p>
</div>
{['http', 'https'].includes(protocol) && (
<Form.Item
label="URL"
name="url"
rules={[
{ required: true, message: 'Please enter the URL!' },
({ getFieldValue }) => ({
validator(_, value) {
if (!value || /\.json$|\.csv$/.test(getFieldValue('url'))) {
return Promise.resolve();
}
return Promise.reject(new Error('The file supports only .csv and .json formats'));
},
}),
]}
style={{ height: 200 }}
>
<Input
placeholder="your_path/file_name.ext"
addonBefore={`${protocol}://`}
// addonAfter={extOptions}
{...DISABLE_AUTO_COMPLETE}
/>
</Form.Item>
)}
{protocol === 'local' && (
<Form.Item
name="file"
label="File"
rules={[{ required: true, message: 'Please select a file!' }]}
style={{ height: 200 }}
>
<Upload.Dragger {...uploadOptions}>
<p className="ant-upload-drag-icon">
<InboxOutlined />
</p>
<p className="ant-upload-text">Click or drag file to this area to upload</p>
<p className="ant-upload-hint">Only .json or .csv files are supported.</p>
</Upload.Dragger>
</Form.Item>
)}
<Form.Item style={{ display: 'none' }} name="id" initialValue={v4().replace(/-/g, '')}>
<input />
</Form.Item>
</Form>
</>
);
};
export default forwardRef(SyncForm);

View File

@ -130,7 +130,7 @@ export default function SyncCustom() {
type="primary"
onClick={opInfo.opNew}
>
Add PATH
Add Prompt
</Button>
<Table
key="id"
@ -143,7 +143,7 @@ export default function SyncCustom() {
<Modal
open={isVisible}
onCancel={hide}
title="Sync PATH"
title="Add Prompt"
onOk={handleOk}
destroyOnClose
maskClosable={false}

View File

@ -9,7 +9,7 @@ import useColumns from '@/hooks/useColumns';
import FilePath from '@/components/FilePath';
import { useCacheModel } from '@/hooks/useChatModel';
import { useTableRowSelection, TABLE_PAGINATION } from '@/hooks/useTable';
import { getPath } from '@/view/model/SyncCustom/config';
import { getPath } from '@/view/prompts/SyncCustom/config';
import { fmtDate, chatRoot } from '@/utils';
import { syncColumns } from './config';
import useInit from '@/hooks/useInit';

View File

@ -94,13 +94,13 @@ export default function UserCustom() {
});
};
const modalTitle = `${{ new: 'Create', edit: 'Edit' }[opInfo.opType]} Model`;
const modalTitle = `${{ new: 'Create', edit: 'Edit' }[opInfo.opType]} Prompt`;
return (
<div>
<div className="chat-table-btns">
<Button className="chat-add-btn" type="primary" onClick={opInfo.opNew}>
Add Model
Add Prompt
</Button>
<div>
{selectedItems.length > 0 && (

34
src/view/scripts/Editor.tsx vendored Normal file
View File

@ -0,0 +1,34 @@
import { FC, useEffect, useState } from 'react';
import Editor from '@monaco-editor/react';
interface MarkdownEditorProps {
value?: string;
onChange?: (v: string) => void;
mode?: string;
}
const ScriptEditor: FC<MarkdownEditorProps> = ({
value = 'console.log',
onChange,
mode = 'split',
}) => {
const [content, setContent] = useState(value);
useEffect(() => {
setContent(value);
onChange && onChange(value);
}, [value]);
const handleEdit = (e: any) => {
setContent(e);
onChange && onChange(e);
};
return (
<div className="script-editor">
<Editor language="js" value={content} onChange={handleEdit} />
</div>
);
};
export default ScriptEditor;

97
src/view/scripts/Head.tsx vendored Normal file
View File

@ -0,0 +1,97 @@
import { useEffect, useState } from 'react';
import { Tag, Collapse, Tooltip } from 'antd';
import { EditOutlined, FileSyncOutlined } from '@ant-design/icons';
import { path, fs, shell } from '@tauri-apps/api';
import { chatRoot } from '@/utils';
import useInit from '@/hooks/useInit';
export type ScriptInfo = {
name: string;
filePath: string;
file: string;
};
interface ScriptHeadProps {
name: string;
onEdit?: (data: ScriptInfo) => void;
activeKey: string;
}
export default function ScriptHead({ name, onEdit, activeKey }: ScriptHeadProps) {
const [file, setFile] = useState('');
const [filePath, setFilePath] = useState('');
const [editing, setEdit] = useState(false);
useEffect(() => {
if (activeKey !== name) {
setEdit(false);
}
}, [activeKey]);
useInit(async () => {
const filePath = await path.join(await chatRoot(), 'scripts', name);
setFilePath(filePath);
const content = await fs.readTextFile(filePath);
setFile(content);
});
const handleGoFile = () => {
shell.open(filePath);
};
const handleEdit = async () => {
setEdit(true);
onEdit && onEdit({ name, filePath, file });
};
const handleCancel = async () => {
setEdit(false);
};
const handleSave = async () => {
setEdit(false);
};
const handleSync = async () => {};
const handleURL = async () => {
shell.open(`https://github.com/lencx/ChatGPT/blob/main/scripts/${name}`);
};
const version = '0.1.0';
return (
<>
<span>
<Tag color="orange">{version}</Tag>
</span>
{editing ? (
<span>
<Tag className="action-btn" onClick={handleCancel} color="default">
Cancel
</Tag>
<Tag className="action-btn" onClick={handleSave} color="geekblue-inverse">
Save
</Tag>
</span>
) : (
<Tag className="action-btn" title="Script Edit" onClick={handleEdit} color="blue-inverse">
<EditOutlined />
</Tag>
)}
<Tag className="action-btn" title="Script Sync" onClick={handleSync} color="orange-inverse">
<FileSyncOutlined />
</Tag>
<span>
<Tag className="file-path" color="blue" onClick={handleGoFile}>
Path: {filePath}
</Tag>
</span>
<span>
<Tag className="file-path" color="green" onClick={handleURL}>
URL: lencx/ChatGPT/scripts/{name}
</Tag>
</span>
</>
);
}

72
src/view/scripts/config.tsx vendored Normal file
View File

@ -0,0 +1,72 @@
import { useState } from 'react';
import { Link } from 'react-router-dom';
import { Tag, Space, Popconfirm } from 'antd';
import { path, shell } from '@tauri-apps/api';
import useInit from '@/hooks/useInit';
import { fmtDate, chatRoot } from '@/utils';
export const scriptColumns = () => [
{
title: 'File Name',
dataIndex: 'name',
key: 'name',
width: 120,
render: (v: string) => <Tag>{v}</Tag>,
},
{
title: 'Version',
dataIndex: 'version',
key: 'version',
width: 120,
render: (v: string) => <Tag>{v}</Tag>,
},
{
title: 'Path',
dataIndex: 'path',
key: 'path',
width: 200,
render: (_: string, row: any) => <RenderPath row={row} />,
},
{
title: 'Created',
dataIndex: 'created',
key: 'created',
width: 150,
render: fmtDate,
},
{
title: 'Action',
fixed: 'right',
width: 160,
render: (_: any, row: any, actions: any) => {
return (
<Space>
<Link to={`/md/${row.id}`} state={row}>
Edit
</Link>
<Popconfirm
title="Are you sure you want to synchronize? It will overwrite all previous modifications made to this file."
onConfirm={() => actions.setRecord(row, 'sync')}
okText="Yes"
cancelText="No"
>
<a>Sync</a>
</Popconfirm>
</Space>
);
},
},
];
const RenderPath = ({ row }: any) => {
const [filePath, setFilePath] = useState('');
useInit(async () => {
setFilePath(await getPath(row));
});
return <a onClick={() => shell.open(filePath)}>{filePath}</a>;
};
export const getPath = async (row: any) => {
return (await path.join(await chatRoot(), 'notes', row.id)) + `.${row.ext}`;
};

10
src/view/scripts/index.scss vendored Normal file
View File

@ -0,0 +1,10 @@
.chatgpt-script {
.ant-collapse-header {
user-select: none;
-webkit-user-select: none;
}
}
.script-editor {
height: 300px;
}

72
src/view/scripts/index.tsx vendored Normal file
View File

@ -0,0 +1,72 @@
import { useState } from 'react';
import { Table, Tag } from 'antd';
import useData from '@/hooks/useData';
import useColumns from '@/hooks/useColumns';
import { useTableRowSelection, TABLE_PAGINATION } from '@/hooks/useTable';
import { scriptColumns } from './config';
import ScriptHead, { type ScriptInfo } from './Head';
import ScriptEditor from './Editor';
import './index.scss';
// const { Panel } = Collapse;
const SCRIPTS = [
'core.js',
'chat.js',
'cmd.js',
'dalle2.js',
'export.js',
'markdown.export.js',
'popup.core.js',
];
export default function Scripts() {
const [activeKey, setActiveKey] = useState('core.js');
const { columns, ...opInfo } = useColumns(scriptColumns());
const handleActiveKeyChange = (key: any) => {
setActiveKey(key);
};
const panelHeadProps = {
onEdit(data: ScriptInfo) {
setActiveKey(data.name);
},
activeKey,
};
return (
<div className="chatgpt-script">
<Table
columns={columns}
dataSource={SCRIPTS.map((i) => ({ name: i }))}
{...TABLE_PAGINATION}
/>
{/* <Tabs
items={SCRIPTS.map((i) => {
return {
label: <Tag>{i}</Tag>,
key: i,
children: <ScriptEditor />,
}
})}
/> */}
{/* <Collapse
accordion
collapsible="icon"
activeKey={activeKey}
onChange={handleActiveKeyChange}
>
{SCRIPTS.map((i) => {
return (
<Panel header={<ScriptHead name={i} {...panelHeadProps} />} key={i}>
<ScriptEditor />
</Panel>
)
})}
</Collapse> */}
</div>
);
}

View File

@ -25,9 +25,9 @@ export default function General() {
<Form.Item label="Stay On Top" name="stay_on_top" valuePropName="checked">
<Switch />
</Form.Item>
<Form.Item label="Save Window State" name="save_window_state" valuePropName="checked">
{/* <Form.Item label="Save Window State" name="save_window_state" valuePropName="checked">
<Switch />
</Form.Item>
</Form.Item> */}
{platformInfo === 'darwin' && (
<Form.Item label="Titlebar" name="titlebar" valuePropName="checked">
<Switch />

View File

@ -1,7 +1,7 @@
import { Form, Switch, Input, InputNumber, Tooltip } from 'antd';
import { QuestionCircleOutlined } from '@ant-design/icons';
import SwitchOrigin from '@/components/SwitchOrigin';
// import SwitchOrigin from '@/components/SwitchOrigin';
import { DISABLE_AUTO_COMPLETE } from '@/utils';
const PopupSearchLabel = () => {
@ -56,7 +56,7 @@ export default function MainWindow() {
<Form.Item label="Default Height" name="main_height">
<InputNumber />
</Form.Item>
<SwitchOrigin name="main" />
{/* <SwitchOrigin name="main" /> */}
<Form.Item label="User Agent (Main)" name="ua_window">
<Input.TextArea
autoSize={{ minRows: 4, maxRows: 4 }}

View File

@ -2,7 +2,7 @@ import { Form, Switch, Input, InputNumber, Tooltip } from 'antd';
import { QuestionCircleOutlined } from '@ant-design/icons';
import { DISABLE_AUTO_COMPLETE } from '@/utils';
import SwitchOrigin from '@/components/SwitchOrigin';
// import SwitchOrigin from '@/components/SwitchOrigin';
const UALabel = () => {
return (
@ -29,7 +29,7 @@ export default function TrayWindow() {
<Form.Item label="Default Height" name="tray_height">
<InputNumber />
</Form.Item>
<SwitchOrigin name="tray" />
{/* <SwitchOrigin name="tray" /> */}
<Form.Item label={<UALabel />} name="ua_tray">
<Input.TextArea
autoSize={{ minRows: 4, maxRows: 4 }}