mirror of
https://github.com/FranP-code/ChatGPT.git
synced 2025-10-13 00:13:25 +00:00
feat: export
This commit is contained in:
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
*.js linguist-vendored
|
||||||
@@ -12,22 +12,28 @@ pub fn init(app: &mut App) -> std::result::Result<(), Box<dyn std::error::Error>
|
|||||||
WindowBuilder::new(app, "core", WindowUrl::App(url.into()))
|
WindowBuilder::new(app, "core", WindowUrl::App(url.into()))
|
||||||
.resizable(true)
|
.resizable(true)
|
||||||
.fullscreen(false)
|
.fullscreen(false)
|
||||||
.initialization_script(include_str!("../core.js"))
|
|
||||||
.initialization_script(&utils::user_script())
|
|
||||||
.title_bar_style(TitleBarStyle::Overlay)
|
|
||||||
.inner_size(800.0, 600.0)
|
.inner_size(800.0, 600.0)
|
||||||
.hidden_title(true)
|
.hidden_title(true)
|
||||||
|
.title_bar_style(TitleBarStyle::Overlay)
|
||||||
|
.initialization_script(&utils::user_script())
|
||||||
|
.initialization_script(include_str!("../assets/html2canvas.js"))
|
||||||
|
.initialization_script(include_str!("../assets/jspdf.js"))
|
||||||
|
.initialization_script(include_str!("../assets/core.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("5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36")
|
||||||
.build()?;
|
.build()?;
|
||||||
|
|
||||||
#[cfg(not(target_os = "macos"))]
|
#[cfg(not(target_os = "macos"))]
|
||||||
WindowBuilder::new(app, "core", WindowUrl::App(url.into()))
|
WindowBuilder::new(app, "core", WindowUrl::App(url.into()))
|
||||||
|
.title("ChatGPT")
|
||||||
.resizable(true)
|
.resizable(true)
|
||||||
.fullscreen(false)
|
.fullscreen(false)
|
||||||
.initialization_script(include_str!("../core.js"))
|
|
||||||
.initialization_script(&utils::user_script())
|
|
||||||
.inner_size(800.0, 600.0)
|
.inner_size(800.0, 600.0)
|
||||||
.title("ChatGPT")
|
.initialization_script(&utils::user_script())
|
||||||
|
.initialization_script(include_str!("../assets/html2canvas.js"))
|
||||||
|
.initialization_script(include_str!("../assets/jspdf.js"))
|
||||||
|
.initialization_script(include_str!("../assets/core.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("5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36")
|
||||||
.build()?;
|
.build()?;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// *** Core Script ***
|
// *** Core Script - IPC ***
|
||||||
document.addEventListener('DOMContentLoaded', async () => {
|
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) {
|
||||||
20
src-tauri/src/assets/html2canvas.js
vendored
Normal file
20
src-tauri/src/assets/html2canvas.js
vendored
Normal file
File diff suppressed because one or more lines are too long
244
src-tauri/src/assets/import.js
vendored
Normal file
244
src-tauri/src/assets/import.js
vendored
Normal file
@@ -0,0 +1,244 @@
|
|||||||
|
// *** Core Script - Import***
|
||||||
|
// @ref: https://github.com/liady/ChatGPT-pdf/blob/main/src/content_script.js
|
||||||
|
|
||||||
|
async function init() {
|
||||||
|
if (window.buttonsInterval) {
|
||||||
|
clearInterval(window.buttonsInterval);
|
||||||
|
}
|
||||||
|
window.buttonsInterval = setInterval(() => {
|
||||||
|
const actionsArea = document.querySelector("form>div>div");
|
||||||
|
if (!actionsArea) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const buttons = actionsArea.querySelectorAll("button");
|
||||||
|
const hasTryAgainButton = Array.from(buttons).some((button) => {
|
||||||
|
return !button.id?.includes("download");
|
||||||
|
});
|
||||||
|
if (hasTryAgainButton && buttons.length === 1) {
|
||||||
|
const TryAgainButton = actionsArea.querySelector("button");
|
||||||
|
addActionsButtons(actionsArea, TryAgainButton);
|
||||||
|
} else if (!hasTryAgainButton) {
|
||||||
|
removeButtons();
|
||||||
|
}
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
const Format = {
|
||||||
|
PNG: "png",
|
||||||
|
PDF: "pdf",
|
||||||
|
};
|
||||||
|
|
||||||
|
function addActionsButtons(actionsArea, TryAgainButton) {
|
||||||
|
const downloadButton = TryAgainButton.cloneNode(true);
|
||||||
|
downloadButton.id = "download-png-button";
|
||||||
|
downloadButton.innerText = "Generate PNG";
|
||||||
|
downloadButton.onclick = () => {
|
||||||
|
downloadThread();
|
||||||
|
};
|
||||||
|
actionsArea.appendChild(downloadButton);
|
||||||
|
const downloadPdfButton = TryAgainButton.cloneNode(true);
|
||||||
|
downloadPdfButton.id = "download-pdf-button";
|
||||||
|
downloadPdfButton.innerText = "Download PDF";
|
||||||
|
downloadPdfButton.onclick = () => {
|
||||||
|
downloadThread({ as: Format.PDF });
|
||||||
|
};
|
||||||
|
actionsArea.appendChild(downloadPdfButton);
|
||||||
|
const exportHtml = TryAgainButton.cloneNode(true);
|
||||||
|
exportHtml.id = "download-html-button";
|
||||||
|
exportHtml.innerText = "Share Link";
|
||||||
|
exportHtml.onclick = () => {
|
||||||
|
sendRequest();
|
||||||
|
};
|
||||||
|
actionsArea.appendChild(exportHtml);
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeButtons() {
|
||||||
|
const downloadButton = document.getElementById("download-png-button");
|
||||||
|
const downloadPdfButton = document.getElementById("download-pdf-button");
|
||||||
|
const downloadHtmlButton = document.getElementById("download-html-button");
|
||||||
|
if (downloadButton) {
|
||||||
|
downloadButton.remove();
|
||||||
|
}
|
||||||
|
if (downloadPdfButton) {
|
||||||
|
downloadPdfButton.remove();
|
||||||
|
}
|
||||||
|
if (downloadHtmlButton) {
|
||||||
|
downloadHtmlButton.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function downloadThread({ as = Format.PNG } = {}) {
|
||||||
|
const elements = new Elements();
|
||||||
|
elements.fixLocation();
|
||||||
|
const pixelRatio = window.devicePixelRatio;
|
||||||
|
const minRatio = as === Format.PDF ? 2 : 2.5;
|
||||||
|
window.devicePixelRatio = Math.max(pixelRatio, minRatio);
|
||||||
|
html2canvas(elements.thread, {
|
||||||
|
letterRendering: true,
|
||||||
|
onclone: function (cloneDoc) {
|
||||||
|
//Make small fix of position to all the text containers
|
||||||
|
let listOfTexts = cloneDoc.getElementsByClassName("min-h-[20px]");
|
||||||
|
Array.from(listOfTexts).forEach((text) => {
|
||||||
|
text.style.position = "relative";
|
||||||
|
text.style.top = "-8px";
|
||||||
|
});
|
||||||
|
|
||||||
|
//Delete copy button from code blocks
|
||||||
|
let listOfCopyBtns = cloneDoc.querySelectorAll("button.flex");
|
||||||
|
Array.from(listOfCopyBtns).forEach(
|
||||||
|
(btn) => (btn.style.visibility = "hidden")
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}).then(async function (canvas) {
|
||||||
|
elements.restoreLocation();
|
||||||
|
window.devicePixelRatio = pixelRatio;
|
||||||
|
const imgData = canvas.toDataURL("image/png");
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
if (as === Format.PDF) {
|
||||||
|
return handlePdf(imgData, canvas, pixelRatio);
|
||||||
|
} else {
|
||||||
|
handleImg(imgData);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleImg(imgData) {
|
||||||
|
const binaryData = atob(imgData.split("base64,")[1]);
|
||||||
|
const data = [];
|
||||||
|
for (let i = 0; i < binaryData.length; i++) {
|
||||||
|
data.push(binaryData.charCodeAt(i));
|
||||||
|
}
|
||||||
|
const blob = new Blob([new Uint8Array(data)], { type: "image/png" });
|
||||||
|
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) {
|
||||||
|
const { jsPDF } = window.jspdf;
|
||||||
|
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);
|
||||||
|
pdf.save("chat-gpt.pdf");
|
||||||
|
}
|
||||||
|
|
||||||
|
class Elements {
|
||||||
|
constructor() {
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
init() {
|
||||||
|
// this.threadWrapper = document.querySelector(".cdfdFe");
|
||||||
|
this.spacer = document.querySelector(".w-full.h-48.flex-shrink-0");
|
||||||
|
this.thread = document.querySelector(
|
||||||
|
"[class*='react-scroll-to-bottom']>[class*='react-scroll-to-bottom']>div"
|
||||||
|
);
|
||||||
|
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.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");
|
||||||
|
});
|
||||||
|
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", "");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
restoreLocation() {
|
||||||
|
this.hiddens.forEach((el) => {
|
||||||
|
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.style.minHeight = null;
|
||||||
|
this.images.forEach((img) => {
|
||||||
|
const srcset = img.getAttribute("srcset_old");
|
||||||
|
img.setAttribute("srcset", srcset);
|
||||||
|
img.setAttribute("srcset_old", "");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectElementByClassPrefix(classPrefix) {
|
||||||
|
const element = document.querySelector(`[class^='${classPrefix}']`);
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function sendRequest() {
|
||||||
|
const data = getData();
|
||||||
|
const uploadUrlResponse = await fetch(
|
||||||
|
"https://chatgpt-static.s3.amazonaws.com/url.txt"
|
||||||
|
);
|
||||||
|
const uploadUrl = await uploadUrlResponse.text();
|
||||||
|
fetch(uploadUrl, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
})
|
||||||
|
.then((response) => response.json())
|
||||||
|
.then((data) => {
|
||||||
|
window.open(data.url, "_blank");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getData() {
|
||||||
|
const globalCss = getCssFromSheet(
|
||||||
|
document.querySelector("link[rel=stylesheet]").sheet
|
||||||
|
);
|
||||||
|
const localCss =
|
||||||
|
getCssFromSheet(
|
||||||
|
document.querySelector(`style[data-styled][data-styled-version]`).sheet
|
||||||
|
) || "body{}";
|
||||||
|
const data = {
|
||||||
|
main: document.querySelector("main").outerHTML,
|
||||||
|
// css: `${globalCss} /* GLOBAL-LOCAL */ ${localCss}`,
|
||||||
|
globalCss,
|
||||||
|
localCss,
|
||||||
|
};
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCssFromSheet(sheet) {
|
||||||
|
return Array.from(sheet.cssRules)
|
||||||
|
.map((rule) => rule.cssText)
|
||||||
|
.join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
// run init
|
||||||
|
if (
|
||||||
|
document.readyState === "complete" ||
|
||||||
|
document.readyState === "interactive"
|
||||||
|
) {
|
||||||
|
init();
|
||||||
|
} else {
|
||||||
|
document.addEventListener("DOMContentLoaded", init);
|
||||||
|
}
|
||||||
398
src-tauri/src/assets/jspdf.js
vendored
Normal file
398
src-tauri/src/assets/jspdf.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -2,7 +2,7 @@
|
|||||||
"build": {
|
"build": {
|
||||||
"beforeDevCommand": "",
|
"beforeDevCommand": "",
|
||||||
"beforeBuildCommand": "",
|
"beforeBuildCommand": "",
|
||||||
"devPath": "https://chat.openai.com",
|
"devPath": "https://chat.openai.com/",
|
||||||
"distDir": "../dist"
|
"distDir": "../dist"
|
||||||
},
|
},
|
||||||
"package": {
|
"package": {
|
||||||
|
|||||||
Reference in New Issue
Block a user