mirror of
https://github.com/lencx/ChatGPT.git
synced 2024-10-01 01:06:13 -04:00
feat: export history
This commit is contained in:
parent
b4d764abbe
commit
7f480b4943
13
README.md
13
README.md
@ -11,15 +11,17 @@
|
|||||||
|
|
||||||
**Latest:**
|
**Latest:**
|
||||||
|
|
||||||
- `Mac`: [ChatGPT_0.1.5_x64.dmg](https://github.com/lencx/ChatGPT/releases/download/v0.1.5/ChatGPT_0.1.5_x64.dmg)
|
- `Mac`: [ChatGPT_0.1.6_x64.dmg](https://github.com/lencx/ChatGPT/releases/download/v0.1.5/ChatGPT_0.1.6_x64.dmg)
|
||||||
- `Linux`: [chat-gpt_0.1.5_amd64.deb](https://github.com/lencx/ChatGPT/releases/download/v0.1.5/chat-gpt_0.1.5_amd64.deb)
|
- `Linux`: [chat-gpt_0.1.6_amd64.deb](https://github.com/lencx/ChatGPT/releases/download/v0.1.5/chat-gpt_0.1.6_amd64.deb)
|
||||||
- `Windows`: [ChatGPT_0.1.5_x64_en-US.msi](https://github.com/lencx/ChatGPT/releases/download/v0.1.5/ChatGPT_0.1.5_x64_en-US.msi)
|
- `Windows`: [ChatGPT_0.1.6_x64_en-US.msi](https://github.com/lencx/ChatGPT/releases/download/v0.1.5/ChatGPT_0.1.6_x64_en-US.msi)
|
||||||
|
|
||||||
[Other version...](https://github.com/lencx/ChatGPT/releases)
|
[Other version...](https://github.com/lencx/ChatGPT/releases)
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- multi-platform: `macOS` `Linux` `Windows`
|
- multi-platform: `macOS` `Linux` `Windows`
|
||||||
|
- export ChatGPT history (PNG, PDF and Share Link)
|
||||||
|
- always on top (whether the window should always be on top of other windows)
|
||||||
- inject script
|
- inject script
|
||||||
- auto updater
|
- auto updater
|
||||||
- app menu
|
- app menu
|
||||||
@ -32,10 +34,9 @@
|
|||||||
<img width="600" src="./assets/chat.png" alt="chat">
|
<img width="600" src="./assets/chat.png" alt="chat">
|
||||||
<img width="600" src="./assets/auto-update.png" alt="auto update">
|
<img width="600" src="./assets/auto-update.png" alt="auto update">
|
||||||
|
|
||||||
## TODO
|
## Related
|
||||||
|
|
||||||
- [ ] export chat history
|
- [ChatGPT Export and Share](https://github.com/liady/ChatGPT-pdf) - A Chrome extension for downloading your ChatGPT history to PNG, PDF or creating a sharable link
|
||||||
- [ ] ...
|
|
||||||
|
|
||||||
## FAQ
|
## FAQ
|
||||||
|
|
||||||
|
BIN
assets/chat.png
BIN
assets/chat.png
Binary file not shown.
Before Width: | Height: | Size: 726 KiB After Width: | Height: | Size: 653 KiB |
@ -1,12 +1,14 @@
|
|||||||
use tauri::Manager;
|
use crate::utils;
|
||||||
|
use std::fs;
|
||||||
|
use tauri::{api, command, AppHandle, Manager};
|
||||||
|
|
||||||
#[tauri::command]
|
#[command]
|
||||||
pub fn drag_window(app: tauri::AppHandle) {
|
pub fn drag_window(app: AppHandle) {
|
||||||
app.get_window("core").unwrap().start_dragging().unwrap();
|
app.get_window("core").unwrap().start_dragging().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[command]
|
||||||
pub fn fullscreen(app: tauri::AppHandle) {
|
pub fn fullscreen(app: AppHandle) {
|
||||||
let win = app.get_window("core").unwrap();
|
let win = app.get_window("core").unwrap();
|
||||||
if win.is_fullscreen().unwrap() {
|
if win.is_fullscreen().unwrap() {
|
||||||
win.set_fullscreen(false).unwrap();
|
win.set_fullscreen(false).unwrap();
|
||||||
@ -14,3 +16,15 @@ pub fn fullscreen(app: tauri::AppHandle) {
|
|||||||
win.set_fullscreen(true).unwrap();
|
win.set_fullscreen(true).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[command]
|
||||||
|
pub fn download(_app: AppHandle, name: String, blob: Vec<u8>) {
|
||||||
|
let path = api::path::download_dir().unwrap().join(name);
|
||||||
|
fs::write(&path, blob).unwrap();
|
||||||
|
utils::open_file(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[command]
|
||||||
|
pub fn open_link(app: AppHandle, url: String) {
|
||||||
|
api::shell::open(&app.shell_scope(), url, None).unwrap();
|
||||||
|
}
|
||||||
|
@ -1,22 +1,17 @@
|
|||||||
use crate::utils;
|
use crate::{conf, utils};
|
||||||
use tauri::{
|
use tauri::{
|
||||||
utils::assets::EmbeddedAssets, AboutMetadata, AppHandle, Context, CustomMenuItem, Manager,
|
utils::assets::EmbeddedAssets, AboutMetadata, AppHandle, Context, CustomMenuItem, Manager,
|
||||||
Menu, MenuItem, Submenu, SystemTray, SystemTrayEvent, SystemTrayMenu, SystemTrayMenuItem,
|
Menu, MenuItem, Submenu, SystemTray, SystemTrayEvent, SystemTrayMenu, WindowMenuEvent,
|
||||||
WindowMenuEvent,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- Menu
|
// --- Menu
|
||||||
pub fn init(context: &Context<EmbeddedAssets>) -> Menu {
|
pub fn init(chat_conf: &conf::ChatConfJson, context: &Context<EmbeddedAssets>) -> Menu {
|
||||||
let name = &context.package_info().name;
|
let name = &context.package_info().name;
|
||||||
let app_menu = Submenu::new(
|
let app_menu = Submenu::new(
|
||||||
name,
|
name,
|
||||||
Menu::new()
|
Menu::new()
|
||||||
.add_native_item(MenuItem::About(name.into(), AboutMetadata::default()))
|
.add_native_item(MenuItem::About(name.into(), AboutMetadata::default()))
|
||||||
.add_native_item(MenuItem::Separator)
|
.add_native_item(MenuItem::Separator)
|
||||||
.add_item(
|
|
||||||
CustomMenuItem::new("inject_script".to_string(), "Inject Script")
|
|
||||||
.accelerator("CmdOrCtrl+J"),
|
|
||||||
)
|
|
||||||
.add_native_item(MenuItem::Separator)
|
.add_native_item(MenuItem::Separator)
|
||||||
.add_native_item(MenuItem::Hide)
|
.add_native_item(MenuItem::Hide)
|
||||||
.add_native_item(MenuItem::HideOthers)
|
.add_native_item(MenuItem::HideOthers)
|
||||||
@ -25,6 +20,28 @@ pub fn init(context: &Context<EmbeddedAssets>) -> Menu {
|
|||||||
.add_native_item(MenuItem::Quit),
|
.add_native_item(MenuItem::Quit),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let always_on_top = CustomMenuItem::new("always_on_top".to_string(), "Always On Top")
|
||||||
|
.accelerator("CmdOrCtrl+T");
|
||||||
|
|
||||||
|
let preferences_menu = Submenu::new(
|
||||||
|
"Preferences",
|
||||||
|
Menu::new()
|
||||||
|
.add_item(
|
||||||
|
CustomMenuItem::new("inject_script".to_string(), "Inject Script")
|
||||||
|
.accelerator("CmdOrCtrl+J"),
|
||||||
|
)
|
||||||
|
.add_item(if chat_conf.always_on_top {
|
||||||
|
always_on_top.selected()
|
||||||
|
} else {
|
||||||
|
always_on_top
|
||||||
|
})
|
||||||
|
.add_native_item(MenuItem::Separator)
|
||||||
|
.add_item(
|
||||||
|
CustomMenuItem::new("awesome".to_string(), "Awesome ChatGPT")
|
||||||
|
.accelerator("CmdOrCtrl+Z"),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
let edit_menu = Submenu::new(
|
let edit_menu = Submenu::new(
|
||||||
"Edit",
|
"Edit",
|
||||||
Menu::new()
|
Menu::new()
|
||||||
@ -74,6 +91,7 @@ pub fn init(context: &Context<EmbeddedAssets>) -> Menu {
|
|||||||
|
|
||||||
Menu::new()
|
Menu::new()
|
||||||
.add_submenu(app_menu)
|
.add_submenu(app_menu)
|
||||||
|
.add_submenu(preferences_menu)
|
||||||
.add_submenu(edit_menu)
|
.add_submenu(edit_menu)
|
||||||
.add_submenu(view_menu)
|
.add_submenu(view_menu)
|
||||||
.add_submenu(help_menu)
|
.add_submenu(help_menu)
|
||||||
@ -83,12 +101,27 @@ pub fn init(context: &Context<EmbeddedAssets>) -> Menu {
|
|||||||
pub fn menu_handler(event: WindowMenuEvent<tauri::Wry>) {
|
pub fn menu_handler(event: WindowMenuEvent<tauri::Wry>) {
|
||||||
let win = Some(event.window()).unwrap();
|
let win = Some(event.window()).unwrap();
|
||||||
let app = win.app_handle();
|
let app = win.app_handle();
|
||||||
|
let state: tauri::State<conf::ChatState> = app.state();
|
||||||
let script_path = utils::script_path().to_string_lossy().to_string();
|
let script_path = utils::script_path().to_string_lossy().to_string();
|
||||||
let issues_url = "https://github.com/lencx/ChatGPT/issues".to_string();
|
let menu_id = event.menu_item_id();
|
||||||
|
|
||||||
match event.menu_item_id() {
|
let core_window = app.get_window("core").unwrap();
|
||||||
// App
|
let menu_handle = core_window.menu_handle();
|
||||||
"inject_script" => inject_script(&app, script_path),
|
|
||||||
|
match menu_id {
|
||||||
|
// Preferences
|
||||||
|
"inject_script" => open(&app, script_path),
|
||||||
|
"awesome" => open(&app, conf::AWESOME_URL.to_string()),
|
||||||
|
"always_on_top" => {
|
||||||
|
let mut always_on_top = state.always_on_top.lock().unwrap();
|
||||||
|
*always_on_top = !*always_on_top;
|
||||||
|
menu_handle
|
||||||
|
.get_item(menu_id)
|
||||||
|
.set_selected(*always_on_top)
|
||||||
|
.unwrap();
|
||||||
|
win.set_always_on_top(*always_on_top).unwrap();
|
||||||
|
conf::ChatConfJson::update_chat_conf(*always_on_top);
|
||||||
|
}
|
||||||
// View
|
// View
|
||||||
"reload" => win.eval("window.location.reload()").unwrap(),
|
"reload" => win.eval("window.location.reload()").unwrap(),
|
||||||
"go_back" => win.eval("window.history.go(-1)").unwrap(),
|
"go_back" => win.eval("window.history.go(-1)").unwrap(),
|
||||||
@ -111,7 +144,7 @@ pub fn menu_handler(event: WindowMenuEvent<tauri::Wry>) {
|
|||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
// Help
|
// Help
|
||||||
"report_bug" => inject_script(&app, issues_url),
|
"report_bug" => open(&app, conf::ISSUES_URL.to_string()),
|
||||||
"dev_tools" => {
|
"dev_tools" => {
|
||||||
win.open_devtools();
|
win.open_devtools();
|
||||||
win.close_devtools();
|
win.close_devtools();
|
||||||
@ -122,35 +155,24 @@ pub fn menu_handler(event: WindowMenuEvent<tauri::Wry>) {
|
|||||||
|
|
||||||
// --- SystemTray Menu
|
// --- SystemTray Menu
|
||||||
pub fn tray_menu() -> SystemTray {
|
pub fn tray_menu() -> SystemTray {
|
||||||
SystemTray::new().with_menu(
|
SystemTray::new().with_menu(SystemTrayMenu::new())
|
||||||
SystemTrayMenu::new()
|
|
||||||
.add_item(CustomMenuItem::new("show".to_string(), "Show ChatGPT"))
|
|
||||||
.add_item(CustomMenuItem::new("hide".to_string(), "Hide ChatGPT"))
|
|
||||||
.add_item(CustomMenuItem::new(
|
|
||||||
"inject_script".to_string(),
|
|
||||||
"Inject Script",
|
|
||||||
))
|
|
||||||
.add_native_item(SystemTrayMenuItem::Separator)
|
|
||||||
.add_item(CustomMenuItem::new("quit".to_string(), "Quit ChatGPT")),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- SystemTray Event
|
// --- SystemTray Event
|
||||||
pub fn tray_handler(app: &AppHandle, event: SystemTrayEvent) {
|
pub fn tray_handler(app: &AppHandle, event: SystemTrayEvent) {
|
||||||
let script_path = utils::script_path().to_string_lossy().to_string();
|
|
||||||
let win = app.get_window("core").unwrap();
|
let win = app.get_window("core").unwrap();
|
||||||
|
|
||||||
if let SystemTrayEvent::MenuItemClick { id, .. } = event {
|
if let SystemTrayEvent::LeftClick { .. } = event {
|
||||||
match id.as_str() {
|
// TODO: tray window
|
||||||
"quit" => std::process::exit(0),
|
if win.is_visible().unwrap() {
|
||||||
"show" => win.show().unwrap(),
|
win.hide().unwrap();
|
||||||
"hide" => win.hide().unwrap(),
|
} else {
|
||||||
"inject_script" => inject_script(app, script_path),
|
win.show().unwrap();
|
||||||
_ => (),
|
win.set_focus().unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn inject_script(app: &AppHandle, path: String) {
|
pub fn open(app: &AppHandle, path: String) {
|
||||||
tauri::api::shell::open(&app.shell_scope(), path, None).unwrap();
|
tauri::api::shell::open(&app.shell_scope(), path, None).unwrap();
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
use crate::utils;
|
use crate::{conf, utils};
|
||||||
use tauri::{utils::config::WindowUrl, window::WindowBuilder, App};
|
use tauri::{utils::config::WindowUrl, window::WindowBuilder, App};
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
use tauri::TitleBarStyle;
|
use tauri::TitleBarStyle;
|
||||||
|
|
||||||
pub fn init(app: &mut App) -> std::result::Result<(), Box<dyn std::error::Error>> {
|
pub fn init(
|
||||||
|
app: &mut App,
|
||||||
|
chat_conf: conf::ChatConfJson,
|
||||||
|
) -> std::result::Result<(), Box<dyn std::error::Error>> {
|
||||||
let conf = utils::get_tauri_conf().unwrap();
|
let conf = utils::get_tauri_conf().unwrap();
|
||||||
let url = conf.build.dev_path.to_string();
|
let url = conf.build.dev_path.to_string();
|
||||||
|
|
||||||
@ -15,12 +18,13 @@ pub fn init(app: &mut App) -> std::result::Result<(), Box<dyn std::error::Error>
|
|||||||
.inner_size(800.0, 600.0)
|
.inner_size(800.0, 600.0)
|
||||||
.hidden_title(true)
|
.hidden_title(true)
|
||||||
.title_bar_style(TitleBarStyle::Overlay)
|
.title_bar_style(TitleBarStyle::Overlay)
|
||||||
|
.always_on_top(chat_conf.always_on_top)
|
||||||
.initialization_script(&utils::user_script())
|
.initialization_script(&utils::user_script())
|
||||||
.initialization_script(include_str!("../assets/html2canvas.js"))
|
.initialization_script(include_str!("../assets/html2canvas.js"))
|
||||||
.initialization_script(include_str!("../assets/jspdf.js"))
|
.initialization_script(include_str!("../assets/jspdf.js"))
|
||||||
.initialization_script(include_str!("../assets/core.js"))
|
.initialization_script(include_str!("../assets/core.js"))
|
||||||
.initialization_script(include_str!("../assets/import.js"))
|
.initialization_script(include_str!("../assets/import.js"))
|
||||||
.user_agent("5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36")
|
.user_agent(conf::USER_AGENT)
|
||||||
.build()?;
|
.build()?;
|
||||||
|
|
||||||
#[cfg(not(target_os = "macos"))]
|
#[cfg(not(target_os = "macos"))]
|
||||||
@ -29,12 +33,13 @@ pub fn init(app: &mut App) -> std::result::Result<(), Box<dyn std::error::Error>
|
|||||||
.resizable(true)
|
.resizable(true)
|
||||||
.fullscreen(false)
|
.fullscreen(false)
|
||||||
.inner_size(800.0, 600.0)
|
.inner_size(800.0, 600.0)
|
||||||
|
.always_on_top(chat_conf.always_on_top)
|
||||||
.initialization_script(&utils::user_script())
|
.initialization_script(&utils::user_script())
|
||||||
.initialization_script(include_str!("../assets/html2canvas.js"))
|
.initialization_script(include_str!("../assets/html2canvas.js"))
|
||||||
.initialization_script(include_str!("../assets/jspdf.js"))
|
.initialization_script(include_str!("../assets/jspdf.js"))
|
||||||
.initialization_script(include_str!("../assets/core.js"))
|
.initialization_script(include_str!("../assets/core.js"))
|
||||||
.initialization_script(include_str!("../assets/import.js"))
|
.initialization_script(include_str!("../assets/import.js"))
|
||||||
.user_agent("5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36")
|
.user_agent(conf::USER_AGENT)
|
||||||
.build()?;
|
.build()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
85
src-tauri/src/assets/core.js
vendored
85
src-tauri/src/assets/core.js
vendored
@ -1,41 +1,45 @@
|
|||||||
// *** Core Script - IPC ***
|
// *** Core Script - IPC ***
|
||||||
document.addEventListener('DOMContentLoaded', async () => {
|
const uid = () => window.crypto.getRandomValues(new Uint32Array(1))[0];
|
||||||
const uid = () => window.crypto.getRandomValues(new Uint32Array(1))[0];
|
function transformCallback(callback = () => {}, once = false) {
|
||||||
function transformCallback(callback = () => {}, once = false) {
|
const identifier = uid();
|
||||||
const identifier = uid();
|
const prop = `_${identifier}`;
|
||||||
const prop = `_${identifier}`;
|
Object.defineProperty(window, prop, {
|
||||||
Object.defineProperty(window, prop, {
|
value: (result) => {
|
||||||
value: (result) => {
|
if (once) {
|
||||||
if (once) {
|
Reflect.deleteProperty(window, prop);
|
||||||
Reflect.deleteProperty(window, prop);
|
}
|
||||||
}
|
return callback(result)
|
||||||
return callback(result)
|
},
|
||||||
},
|
writable: false,
|
||||||
writable: false,
|
configurable: true,
|
||||||
configurable: true,
|
})
|
||||||
})
|
return identifier;
|
||||||
return identifier;
|
}
|
||||||
}
|
async function invoke(cmd, args) {
|
||||||
async function invoke(cmd, args) {
|
return new Promise((resolve, reject) => {
|
||||||
return new Promise((resolve, reject) => {
|
if (!window.__TAURI_POST_MESSAGE__) reject('__TAURI_POST_MESSAGE__ does not exist!');
|
||||||
if (!window.__TAURI_POST_MESSAGE__) reject('__TAURI_POST_MESSAGE__ does not exist!');
|
const callback = transformCallback((e) => {
|
||||||
const callback = transformCallback((e) => {
|
resolve(e);
|
||||||
resolve(e);
|
Reflect.deleteProperty(window, `_${error}`);
|
||||||
Reflect.deleteProperty(window, `_${error}`);
|
}, true)
|
||||||
}, true)
|
const error = transformCallback((e) => {
|
||||||
const error = transformCallback((e) => {
|
reject(e);
|
||||||
reject(e);
|
Reflect.deleteProperty(window, `_${callback}`);
|
||||||
Reflect.deleteProperty(window, `_${callback}`);
|
}, true)
|
||||||
}, true)
|
window.__TAURI_POST_MESSAGE__({
|
||||||
window.__TAURI_POST_MESSAGE__({
|
cmd,
|
||||||
cmd,
|
callback,
|
||||||
callback,
|
error,
|
||||||
error,
|
...args
|
||||||
...args
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
window.uid = uid;
|
||||||
|
window.invoke = invoke;
|
||||||
|
window.transformCallback = transformCallback;
|
||||||
|
|
||||||
|
async function init() {
|
||||||
async function platform() {
|
async function platform() {
|
||||||
return invoke('platform', {
|
return invoke('platform', {
|
||||||
__tauriModule: 'Os',
|
__tauriModule: 'Os',
|
||||||
@ -74,4 +78,13 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
})
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
document.readyState === "complete" ||
|
||||||
|
document.readyState === "interactive"
|
||||||
|
) {
|
||||||
|
init();
|
||||||
|
} else {
|
||||||
|
document.addEventListener("DOMContentLoaded", init);
|
||||||
|
}
|
16
src-tauri/src/assets/import.js
vendored
16
src-tauri/src/assets/import.js
vendored
@ -109,15 +109,7 @@ function handleImg(imgData) {
|
|||||||
for (let i = 0; i < binaryData.length; i++) {
|
for (let i = 0; i < binaryData.length; i++) {
|
||||||
data.push(binaryData.charCodeAt(i));
|
data.push(binaryData.charCodeAt(i));
|
||||||
}
|
}
|
||||||
const blob = new Blob([new Uint8Array(data)], { type: "image/png" });
|
invoke('download', { name: `chatgpt-${Date.now()}.png`, blob: Array.from(new Uint8Array(data)) });
|
||||||
const url = URL.createObjectURL(blob);
|
|
||||||
|
|
||||||
// window.open(url, "_blank");
|
|
||||||
|
|
||||||
// const a = document.createElement("a");
|
|
||||||
// a.href = url;
|
|
||||||
// a.download = "chat-gpt-image.png";
|
|
||||||
// a.click();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handlePdf(imgData, canvas, pixelRatio) {
|
function handlePdf(imgData, canvas, pixelRatio) {
|
||||||
@ -130,7 +122,9 @@ function handlePdf(imgData, canvas, pixelRatio) {
|
|||||||
var pdfWidth = pdf.internal.pageSize.getWidth();
|
var pdfWidth = pdf.internal.pageSize.getWidth();
|
||||||
var pdfHeight = pdf.internal.pageSize.getHeight();
|
var pdfHeight = pdf.internal.pageSize.getHeight();
|
||||||
pdf.addImage(imgData, "PNG", 0, 0, pdfWidth, pdfHeight);
|
pdf.addImage(imgData, "PNG", 0, 0, pdfWidth, pdfHeight);
|
||||||
pdf.save("chat-gpt.pdf");
|
|
||||||
|
const data = pdf.__private__.getArrayBuffer(pdf.__private__.buildDocument());
|
||||||
|
invoke('download', { name: `chatgpt-${Date.now()}.pdf`, blob: Array.from(new Uint8Array(data)) });
|
||||||
}
|
}
|
||||||
|
|
||||||
class Elements {
|
class Elements {
|
||||||
@ -206,7 +200,7 @@ async function sendRequest() {
|
|||||||
})
|
})
|
||||||
.then((response) => response.json())
|
.then((response) => response.json())
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
window.open(data.url, "_blank");
|
invoke('open_link', { url: data.url });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
3
src-tauri/src/assets/jspdf.js
vendored
3
src-tauri/src/assets/jspdf.js
vendored
File diff suppressed because one or more lines are too long
66
src-tauri/src/conf.rs
Normal file
66
src-tauri/src/conf.rs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
use crate::utils::{chat_root, create_file, exists};
|
||||||
|
use std::fs;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
pub const USER_AGENT: &str = "5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36";
|
||||||
|
pub const ISSUES_URL: &str = "https://github.com/lencx/ChatGPT/issues";
|
||||||
|
pub const AWESOME_URL: &str = "https://github.com/lencx/ChatGPT/blob/main/AWESOME.md";
|
||||||
|
|
||||||
|
pub struct ChatState {
|
||||||
|
pub always_on_top: Mutex<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ChatState {
|
||||||
|
pub fn default(chat_conf: &ChatConfJson) -> Self {
|
||||||
|
ChatState {
|
||||||
|
always_on_top: Mutex::new(chat_conf.always_on_top),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
|
||||||
|
pub struct ChatConfJson {
|
||||||
|
pub always_on_top: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ChatConfJson {
|
||||||
|
/// init chat.conf.json
|
||||||
|
/// path: ~/.chatgpt/chat.conf.json
|
||||||
|
pub fn init() -> PathBuf {
|
||||||
|
let conf_file = ChatConfJson::conf_path();
|
||||||
|
if !exists(&conf_file) {
|
||||||
|
create_file(&conf_file).unwrap();
|
||||||
|
fs::write(&conf_file, r#"{"always_on_top": false}"#).unwrap();
|
||||||
|
}
|
||||||
|
conf_file
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn conf_path() -> PathBuf {
|
||||||
|
chat_root().join("chat.conf.json")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_chat_conf() -> Self {
|
||||||
|
let config_file = fs::read_to_string(ChatConfJson::conf_path()).unwrap();
|
||||||
|
let config: serde_json::Value =
|
||||||
|
serde_json::from_str(&config_file).expect("failed to parse chat.conf.json");
|
||||||
|
serde_json::from_value(config).unwrap_or_else(|_| ChatConfJson::chat_conf_default())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_chat_conf(always_on_top: bool) {
|
||||||
|
let mut conf = ChatConfJson::get_chat_conf();
|
||||||
|
conf.always_on_top = always_on_top;
|
||||||
|
fs::write(
|
||||||
|
ChatConfJson::conf_path(),
|
||||||
|
serde_json::to_string(&conf).unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn chat_conf_default() -> Self {
|
||||||
|
serde_json::from_value(serde_json::json!({
|
||||||
|
"always_on_top": false,
|
||||||
|
}))
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
}
|
@ -4,20 +4,41 @@
|
|||||||
)]
|
)]
|
||||||
|
|
||||||
mod app;
|
mod app;
|
||||||
|
mod conf;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
use app::{cmd, menu, setup};
|
use app::{cmd, menu, setup};
|
||||||
|
use conf::ChatConfJson;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
ChatConfJson::init();
|
||||||
let context = tauri::generate_context!();
|
let context = tauri::generate_context!();
|
||||||
|
let chat_conf = ChatConfJson::get_chat_conf();
|
||||||
|
let chat_conf2 = chat_conf.clone();
|
||||||
|
|
||||||
tauri::Builder::default()
|
tauri::Builder::default()
|
||||||
.invoke_handler(tauri::generate_handler![cmd::drag_window, cmd::fullscreen])
|
.manage(conf::ChatState::default(&chat_conf))
|
||||||
.setup(setup::init)
|
.invoke_handler(tauri::generate_handler![
|
||||||
.menu(menu::init(&context))
|
cmd::drag_window,
|
||||||
|
cmd::fullscreen,
|
||||||
|
cmd::download,
|
||||||
|
cmd::open_link
|
||||||
|
])
|
||||||
|
.setup(|app| setup::init(app, chat_conf2))
|
||||||
|
.menu(menu::init(&chat_conf, &context))
|
||||||
.system_tray(menu::tray_menu())
|
.system_tray(menu::tray_menu())
|
||||||
.on_menu_event(menu::menu_handler)
|
.on_menu_event(menu::menu_handler)
|
||||||
.on_system_tray_event(menu::tray_handler)
|
.on_system_tray_event(menu::tray_handler)
|
||||||
|
.on_window_event(|event| {
|
||||||
|
// https://github.com/tauri-apps/tauri/discussions/2684
|
||||||
|
if let tauri::WindowEvent::CloseRequested { api, .. } = event.event() {
|
||||||
|
// TODO: https://github.com/tauri-apps/tauri/issues/3084
|
||||||
|
// event.window().hide().unwrap();
|
||||||
|
// https://github.com/tauri-apps/tao/pull/517
|
||||||
|
event.window().minimize().unwrap();
|
||||||
|
api.prevent_close();
|
||||||
|
}
|
||||||
|
})
|
||||||
.run(context)
|
.run(context)
|
||||||
.expect("error while running ChatGPT application");
|
.expect("error while running ChatGPT application");
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,15 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use std::fs::{self, File};
|
use std::{
|
||||||
use std::path::{Path, PathBuf};
|
fs::{self, File},
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
process::Command,
|
||||||
|
};
|
||||||
use tauri::utils::config::Config;
|
use tauri::utils::config::Config;
|
||||||
|
|
||||||
|
pub fn chat_root() -> PathBuf {
|
||||||
|
tauri::api::path::home_dir().unwrap().join(".chatgpt")
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_tauri_conf() -> Option<Config> {
|
pub fn get_tauri_conf() -> Option<Config> {
|
||||||
let config_file = include_str!("../tauri.conf.json");
|
let config_file = include_str!("../tauri.conf.json");
|
||||||
let config: Config =
|
let config: Config =
|
||||||
@ -22,8 +29,7 @@ pub fn create_file(path: &Path) -> Result<File> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn script_path() -> PathBuf {
|
pub fn script_path() -> PathBuf {
|
||||||
let root = tauri::api::path::home_dir().unwrap().join(".chatgpt");
|
let script_file = chat_root().join("main.js");
|
||||||
let script_file = root.join("main.js");
|
|
||||||
if !exists(&script_file) {
|
if !exists(&script_file) {
|
||||||
create_file(&script_file).unwrap();
|
create_file(&script_file).unwrap();
|
||||||
fs::write(&script_file, format!("// *** ChatGPT User Script ***\n// @github: https://github.com/lencx/ChatGPT \n// @path: {}\n\nconsole.log('🤩 Hello ChatGPT!!!');", &script_file.to_string_lossy())).unwrap();
|
fs::write(&script_file, format!("// *** ChatGPT User Script ***\n// @github: https://github.com/lencx/ChatGPT \n// @path: {}\n\nconsole.log('🤩 Hello ChatGPT!!!');", &script_file.to_string_lossy())).unwrap();
|
||||||
@ -39,3 +45,19 @@ pub fn user_script() -> String {
|
|||||||
user_script_content
|
user_script_content
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn open_file(path: PathBuf) {
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
Command::new("open").arg("-R").arg(path).spawn().unwrap();
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
Command::new("explorer")
|
||||||
|
.arg("/select,")
|
||||||
|
.arg(path)
|
||||||
|
.spawn()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// https://askubuntu.com/a/31071
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
Command::new("xdg-open").arg(path).spawn().unwrap();
|
||||||
|
}
|
||||||
|
@ -44,6 +44,10 @@
|
|||||||
"shortDescription": "ChatGPT",
|
"shortDescription": "ChatGPT",
|
||||||
"targets": "all",
|
"targets": "all",
|
||||||
"windows": {
|
"windows": {
|
||||||
|
"webviewInstallMode": {
|
||||||
|
"silent": true,
|
||||||
|
"type": "downloadBootstrapper"
|
||||||
|
},
|
||||||
"certificateThumbprint": null,
|
"certificateThumbprint": null,
|
||||||
"digestAlgorithm": "sha256",
|
"digestAlgorithm": "sha256",
|
||||||
"timestampUrl": ""
|
"timestampUrl": ""
|
||||||
|
Loading…
Reference in New Issue
Block a user