Export ChatGPT conversations with one click — backup & share effortlessly!
< 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