mirror of
https://github.com/lencx/ChatGPT.git
synced 2024-10-01 01:06:13 -04:00
refactor: conf
This commit is contained in:
parent
60b7e3148a
commit
d9612d3bed
1479
Cargo.lock
generated
1479
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -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",
|
||||
|
@ -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
BIN
public/bmc.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.6 KiB |
BIN
public/wxp.png
Normal file
BIN
public/wxp.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 248 KiB |
166
src-tauri/src/scripts/chat.js → scripts/chat.js
vendored
166
src-tauri/src/scripts/chat.js → scripts/chat.js
vendored
@ -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);
|
||||
}
|
68
src-tauri/src/scripts/cmd.js → scripts/cmd.js
vendored
68
src-tauri/src/scripts/cmd.js → scripts/cmd.js
vendored
@ -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);
|
||||
}
|
94
src-tauri/src/scripts/core.js → scripts/core.js
vendored
94
src-tauri/src/scripts/core.js → scripts/core.js
vendored
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
190
src-tauri/src/scripts/export.js → scripts/export.js
vendored
190
src-tauri/src/scripts/export.js → scripts/export.js
vendored
@ -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
41
scripts/manifest.json
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
@ -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;
|
||||
}({}));
|
||||
})({});
|
@ -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
44
sponsor.html
Normal 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>
|
@ -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" }
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
0
src-tauri/src/app/script.rs
Normal file
0
src-tauri/src/app/script.rs
Normal 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();
|
||||
|
@ -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)
|
||||
|
@ -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")]
|
||||
|
@ -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,
|
||||
|
@ -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
5
src/main.scss
vendored
@ -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
38
src/routes.tsx
vendored
@ -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 />,
|
||||
},
|
||||
];
|
||||
|
||||
|
4
src/view/about/index.tsx
vendored
4
src/view/about/index.tsx
vendored
@ -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>
|
||||
);
|
||||
};
|
||||
|
128
src/view/model/SyncCustom/Form.tsx
vendored
128
src/view/model/SyncCustom/Form.tsx
vendored
@ -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
192
src/view/prompts/SyncCustom/Form.tsx
vendored
Normal 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);
|
@ -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}
|
@ -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';
|
@ -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
34
src/view/scripts/Editor.tsx
vendored
Normal 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
97
src/view/scripts/Head.tsx
vendored
Normal 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
72
src/view/scripts/config.tsx
vendored
Normal 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
10
src/view/scripts/index.scss
vendored
Normal 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
72
src/view/scripts/index.tsx
vendored
Normal 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>
|
||||
);
|
||||
}
|
4
src/view/settings/General.tsx
vendored
4
src/view/settings/General.tsx
vendored
@ -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 />
|
||||
|
4
src/view/settings/MainWindow.tsx
vendored
4
src/view/settings/MainWindow.tsx
vendored
@ -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 }}
|
||||
|
4
src/view/settings/TrayWindow.tsx
vendored
4
src/view/settings/TrayWindow.tsx
vendored
@ -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 }}
|
||||
|
Loading…
Reference in New Issue
Block a user