Greasy Fork is available in English.
轻松导出 ChatGPT 聊天记录,以便进一步分析或分享。
< 脚本 ChatGPT Exporter 的反馈
Great script. OpenAI have recently changed the footer container which has broken the Export button... here's the fixed code, feel free to update the script:
--- userscript.js (v2.30.0) +++ userscript.js (v2.30.1) @@ -// @version 2.30.0 +// @version 2.30.1 @@ - const MenuItem = ({ text: text2, successText, disabled = false, title: title2, icon: Icon, onClick, className }) => { + const MenuItem = ({ text: text2, successText, disabled = false, title: title2, icon: Icon, onClick, className, variant = "default" }) => { const [loading, setLoading] = h$4(false); const [succeed, setSucceed] = h$4(false); const handleClick = typeof onClick === "function" ? async (e2) => { e2.preventDefault(); if (loading) return; try { setLoading(true); const result = await onClick(); if (result) { setSucceed(true); setTimeout(() => setSucceed(false), TIMEOUT); } } catch (error2) { console.error(error2); } finally { setLoading(false); } } : void 0; + if (variant === "sidebar-footer") { + return /* @__PURE__ */ o$8( + "div", + { + className: `group __menu-item hoverable gap-2 ms-2 me-1.5 gap-2! pe-1.5 data-fill:max-w-full [&>div:first-child]:gap-2! ${className ?? ""}`, + onClick: handleClick, + onTouchStart: handleClick, + disabled, + title: title2, + "data-fill": "", + "data-size": "large", + "data-sidebar-item": "true", + children: loading ? /* @__PURE__ */ o$8("div", { className: "flex justify-center items-center w-full h-full", children: /* @__PURE__ */ o$8(IconLoading, { className: "w-4 h-4" }) }) : /* @__PURE__ */ o$8(k$3, { children: [ + Icon && /* @__PURE__ */ o$8("div", { className: "flex items-center justify-center [opacity:var(--menu-item-icon-opacity,1)] icon-lg", children: /* @__PURE__ */ o$8(Icon, {}) }), + /* @__PURE__ */ o$8("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ o$8("div", { className: "truncate", children: succeed && successText ? successText : text2 }) }) + ] }) + } + ); + } return /* @__PURE__ */ o$8( "div", { className: ` menu-item flex flex-shrink-0 py-3 px-3 items-center gap-3 rounded-lg mb-2 bg-menu hover:bg-gray-500/10 transition-colors duration-200 text-menu text-sm cursor-pointer border border-menu ${className}`, @@ - function MenuInner({ container }) { + function MenuInner({ container, variant = "default" }) { const { t: t2 } = useTranslation(); const disabled = getHistoryDisabled(); @@ if (disabled) { return /* @__PURE__ */ o$8( MenuItem, { - className: "mt-1", + className: variant === "sidebar-footer" ? "mb-1" : "mt-1", text: "Chat History disabled", icon: IconArrowRightFromBracket, - disabled: true + disabled: true, + variant } ); } @@ /* @__PURE__ */ o$8($cef8881cdc69808e$export$41fb9f06171c75f4, { children: /* @__PURE__ */ o$8( MenuItem, { - className: "mt-1", + className: variant === "sidebar-footer" ? "mb-1" : "mt-1", text: t2("ExportHelper"), icon: IconArrowRightFromBracket, + variant, onClick: () => { setOpen(true); return true; } } ) }), @@ - /* @__PURE__ */ o$8(Divider, {}) + variant !== "sidebar-footer" && /* @__PURE__ */ o$8(Divider, {}) ] }); } @@ - function Menu({ container }) { - return /* @__PURE__ */ o$8(SettingProvider, { children: /* @__PURE__ */ o$8(MenuInner, { container }) }); + function Menu({ container, variant = "default" }) { + return /* @__PURE__ */ o$8(SettingProvider, { children: /* @__PURE__ */ o$8(MenuInner, { container, variant }) }); + } + function getSidebarFooterAnchor(nav) { + const footer = nav.nextElementSibling; + if (!(footer instanceof HTMLElement) || footer.querySelector("nav")) return null; + const profileButton = footer.querySelector('[data-testid="accounts-profile-button"]'); + if (!profileButton) return null; + return profileButton.parentElement || footer; } @@ const styleEl = document.createElement("style"); styleEl.id = "sentinel-css"; document.head.append(styleEl); const injectionMap = /* @__PURE__ */ new Map(); const injectNavMenu = (nav) => { - if (injectionMap.has(nav)) return; + if (injectionMap.has(nav) || nav.hasAttribute("inert") || nav.id === "stage-sidebar-tiny-bar" || nav.className.includes("group/tiny-bar")) return; console.log("[Exporter] Injecting nav", nav); - const container = getMenuContainer(); + const sidebarFooter = getSidebarFooterAnchor(nav); + const container = getMenuContainer(sidebarFooter ? "sidebar-footer" : "default"); injectionMap.set(nav, container); + if (sidebarFooter) { + sidebarFooter.prepend(container); + console.log("[Exporter] Prepended container to sidebar footer", sidebarFooter); + return; + } const chatList = nav.querySelector(":scope > div.sticky.bottom-0"); if (chatList) { chatList.prepend(container); console.log("[Exporter] Prepended container to chat list", chatList); } else { container.style.backgroundColor = "#171717"; container.style.position = "sticky"; container.style.bottom = "72px"; nav.append(container); console.log("[Exporter] Fallback to appending container to nav", nav); } }; sentinel.on("nav", injectNavMenu); setInterval(() => { injectionMap.forEach((container, nav) => { - if (!nav.isConnected) { + if (!nav.isConnected || !container.isConnected) { container.remove(); injectionMap.delete(nav); } }); @@ - function getMenuContainer() { + function getMenuContainer(variant = "default") { const container = document.createElement("div"); container.style.zIndex = "99"; - D$4(/* @__PURE__ */ o$8(Menu, { container }), container); + if (variant === "sidebar-footer") { + container.style.width = "100%"; + } + D$4(/* @__PURE__ */ o$8(Menu, { container, variant }), container); return container; }
Thanks! New version has been published with fix to adapt to footer changes.
登录以发布留言。
Great script. OpenAI have recently changed the footer container which has broken the Export button... here's the fixed code, feel free to update the script:
Manual patch diff