From 4ca34e04368420cf97626d1b9803f9b7d647190e Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Wed, 24 May 2023 23:21:18 +0800 Subject: [PATCH 1/3] fix: #1711 input range style in mobile screen --- app/components/input-range.module.scss | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/components/input-range.module.scss b/app/components/input-range.module.scss index 5a555a457e3..e97410529bc 100644 --- a/app/components/input-range.module.scss +++ b/app/components/input-range.module.scss @@ -4,4 +4,9 @@ padding: 5px 15px 5px 10px; font-size: 12px; display: flex; + max-width: 40%; + + input[type="range"] { + max-width: calc(100% - 50px); + } } From 3e65ef3beaa6550763feb1f3bcb9d4e59c2f0a07 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Thu, 25 May 2023 01:04:37 +0800 Subject: [PATCH 2/3] feat: share to ShareGPT --- app/client/api.ts | 37 ++++++- app/components/exporter.tsx | 152 +++++++++++++++++++++++----- app/components/message-selector.tsx | 4 +- app/constant.ts | 2 + app/locales/cn.ts | 2 +- next.config.mjs | 4 + 6 files changed, 173 insertions(+), 28 deletions(-) diff --git a/app/client/api.ts b/app/client/api.ts index a966d7334cc..fb829f97a39 100644 --- a/app/client/api.ts +++ b/app/client/api.ts @@ -1,5 +1,5 @@ import { ACCESS_CODE_PREFIX } from "../constant"; -import { ModelConfig, ModelType, useAccessStore } from "../store"; +import { ChatMessage, ModelConfig, ModelType, useAccessStore } from "../store"; import { ChatGPTApi } from "./platforms/openai"; export const ROLES = ["system", "user", "assistant"] as const; @@ -54,6 +54,41 @@ export class ClientApi { prompts() {} masks() {} + + async share(messages: ChatMessage[], avatarUrl: string | null = null) { + const msgs = messages + .map((m) => ({ + from: m.role === "user" ? "human" : "gpt", + value: m.content, + })) + .concat([ + { + from: "human", + value: + "Share from [ChatGPT Next Web]: https://github.com/Yidadaa/ChatGPT-Next-Web", + }, + ]); + // 敬告二开开发者们,为了开源大模型的发展,请不要修改上述消息,此消息用于后续数据清洗使用 + // Please do not modify this message + + console.log("[Share]", msgs); + const res = await fetch("/sharegpt", { + body: JSON.stringify({ + avatarUrl, + items: msgs, + }), + headers: { + "Content-Type": "application/json", + }, + method: "POST", + }); + + const resJson = await res.json(); + console.log("[Share]", resJson); + if (resJson.id) { + return `https://shareg.pt/${resJson.id}`; + } + } } export const api = new ClientApi(); diff --git a/app/components/exporter.tsx b/app/components/exporter.tsx index a9a1071d838..10d5af994b1 100644 --- a/app/components/exporter.tsx +++ b/app/components/exporter.tsx @@ -12,14 +12,17 @@ import ShareIcon from "../icons/share.svg"; import BotIcon from "../icons/bot.png"; import DownloadIcon from "../icons/download.svg"; -import { useMemo, useRef, useState } from "react"; +import { useEffect, useMemo, useRef, useState } from "react"; import { MessageSelector, useMessageSelector } from "./message-selector"; import { Avatar } from "./emoji"; import dynamic from "next/dynamic"; import NextImage from "next/image"; -import { toBlob, toPng } from "html-to-image"; +import { toBlob, toJpeg, toPng } from "html-to-image"; import { DEFAULT_MASK_AVATAR } from "../store/mask"; +import { api } from "../client/api"; +import { prettyObject } from "../utils/format"; +import { EXPORT_MESSAGE_CLASS_NAME } from "../constant"; const Markdown = dynamic(async () => (await import("./markdown")).Markdown, { loading: () => , @@ -214,37 +217,127 @@ export function MessageExporter() { ); } +export function RenderExport(props: { + messages: ChatMessage[]; + onRender: (messages: ChatMessage[]) => void; +}) { + const domRef = useRef(null); + + useEffect(() => { + if (!domRef.current) return; + const dom = domRef.current; + const messages = Array.from( + dom.getElementsByClassName(EXPORT_MESSAGE_CLASS_NAME), + ); + + if (messages.length !== props.messages.length) { + return; + } + + const renderMsgs = messages.map((v) => { + const [_, role] = v.id.split(":"); + return { + role: role as any, + content: v.innerHTML, + date: "", + }; + }); + + props.onRender(renderMsgs); + }); + + return ( +
+ {props.messages.map((m, i) => ( +
+ +
+ ))} +
+ ); +} + export function PreviewActions(props: { download: () => void; copy: () => void; showCopy?: boolean; + messages?: ChatMessage[]; }) { + const [loading, setLoading] = useState(false); + const [shouldExport, setShouldExport] = useState(false); + + const onRenderMsgs = (msgs: ChatMessage[]) => { + setShouldExport(false); + + api + .share(msgs) + .then((res) => { + if (!res) return; + copyToClipboard(res); + setTimeout(() => { + window.open(res, "_blank"); + }, 800); + }) + .catch((e) => { + console.error("[Share]", e); + showToast(prettyObject(e)); + }) + .finally(() => setLoading(false)); + }; + + const share = async () => { + if (props.messages?.length) { + setLoading(true); + setShouldExport(true); + } + }; + return ( -
- {props.showCopy && ( + <> +
+ {props.showCopy && ( + } + onClick={props.copy} + > + )} } - onClick={props.copy} + icon={} + onClick={props.download} > - )} - } - onClick={props.download} - > - } - onClick={() => showToast(Locale.WIP)} - > -
+ : } + onClick={share} + > +
+
+ {shouldExport && ( + + )} +
+ ); } @@ -323,7 +416,12 @@ export function ImagePreviewer(props: { return (
- +
- +
{mdText}
diff --git a/app/components/message-selector.tsx b/app/components/message-selector.tsx index 837591acb18..300d45375d1 100644 --- a/app/components/message-selector.tsx +++ b/app/components/message-selector.tsx @@ -126,6 +126,8 @@ export function MessageSelector(props: { // eslint-disable-next-line react-hooks/exhaustive-deps }, [startIndex, endIndex]); + const LATEST_COUNT = 4; + return (
@@ -155,7 +157,7 @@ export function MessageSelector(props: { props.updateSelection((selection) => { selection.clear(); messages - .slice(messageCount - 10) + .slice(messageCount - LATEST_COUNT) .forEach((m) => selection.add(m.id!)); }) } diff --git a/app/constant.ts b/app/constant.ts index 577c0af69cd..0fb18c2fb31 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -42,3 +42,5 @@ export const ACCESS_CODE_PREFIX = "ak-"; export const LAST_INPUT_KEY = "last-input"; export const REQUEST_TIMEOUT_MS = 60000; + +export const EXPORT_MESSAGE_CLASS_NAME = "export-markdown"; diff --git a/app/locales/cn.ts b/app/locales/cn.ts index 989a54bf669..48134e38360 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -58,7 +58,7 @@ const cn = { Select: { Search: "搜索消息", All: "选取全部", - Latest: "最近十条", + Latest: "最近几条", Clear: "清除选中", }, Memory: { diff --git a/next.config.mjs b/next.config.mjs index 9c0ce9fa364..34c058b7cbe 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -11,6 +11,10 @@ const nextConfig = { source: "/google-fonts/:path*", destination: "https://fonts.googleapis.com/:path*", }, + { + source: "/sharegpt", + destination: "https://sharegpt.com/api/conversations", + }, ]; const apiUrl = process.env.API_URL; From 9f4a80f6ae1d2cfed96dc99ae7ecbf7266bdb542 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Thu, 25 May 2023 01:08:19 +0800 Subject: [PATCH 3/3] chore: update readme --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8edbac01da3..9607a21ef8e 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ One-Click to deploy well-designed ChatGPT web UI on Vercel. [Demo](https://chatgpt.nextweb.fun/) / [Issues](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [Buy Me a Coffee](https://www.buymeacoffee.com/yidadaa) [演示](https://chatgpt.nextweb.fun/) / [反馈](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [QQ 群](https://github.com/Yidadaa/ChatGPT-Next-Web/discussions/1724) / [打赏开发者](https://user-images.githubusercontent.com/16968934/227772541-5bcd52d8-61b7-488c-a203-0330d8006e2b.jpg) - + [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FYidadaa%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=chatgpt-next-web&repository-name=ChatGPT-Next-Web) [![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web) @@ -38,7 +38,7 @@ One-Click to deploy well-designed ChatGPT web UI on Vercel. - [x] System Prompt: pin a user defined prompt as system prompt [#138](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/138) - [x] User Prompt: user can edit and save custom prompts to prompt list - [x] Prompt Template: create a new chat with pre-defined in-context prompts [#993](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/993) -- [ ] Share as image, share to ShareGPT +- [x] Share as image, share to ShareGPT [#1741](https://github.com/Yidadaa/ChatGPT-Next-Web/pull/1741) - [ ] Desktop App with tauri - [ ] Self-host Model: support llama, alpaca, ChatGLM, BELLE etc. - [ ] Plugins: support network search, calculator, any other apis etc. [#165](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/165) @@ -51,6 +51,7 @@ One-Click to deploy well-designed ChatGPT web UI on Vercel. ## What's New - 🚀 v2.0 is released, now you can create prompt templates, turn your ideas into reality! Read this: [ChatGPT Prompt Engineering Tips: Zero, One and Few Shot Prompting](https://www.allabtai.com/prompt-engineering-tips-zero-one-and-few-shot-prompting/). +- 🚀 v2.7 let's share conversations as image, or share to ShareGPT! ## 主要功能 @@ -70,7 +71,7 @@ One-Click to deploy well-designed ChatGPT web UI on Vercel. - [x] 为每个对话设置系统 Prompt [#138](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/138) - [x] 允许用户自行编辑内置 Prompt 列表 - [x] 预制角色:使用预制角色快速定制新对话 [#993](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/993) -- [ ] 分享为图片,分享到 ShareGPT +- [x] 分享为图片,分享到 ShareGPT 链接 [#1741](https://github.com/Yidadaa/ChatGPT-Next-Web/pull/1741) - [ ] 使用 tauri 打包桌面应用 - [ ] 支持自部署的大语言模型 - [ ] 插件机制,支持联网搜索、计算器、调用其他平台 api [#165](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/165) @@ -84,6 +85,7 @@ One-Click to deploy well-designed ChatGPT web UI on Vercel. - 🚀 v2.0 已经发布,现在你可以使用面具功能快速创建预制对话了! 了解更多: [ChatGPT 提示词高阶技能:零次、一次和少样本提示](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/138)。 - 💡 想要更方便地随时随地使用本项目?可以试下这款桌面插件:https://github.com/mushan0x0/AI0x0.com +- 🚀 v2.7 现在可以将会话分享为图片了,也可以分享到 ShareGPT 的在线链接。 ## Get Started