forked from XiaoMo/ChatGPT-Next-Web
commit
fe9dd88c3f
@ -26,13 +26,13 @@
|
|||||||
@media only screen and (min-width: 600px) {
|
@media only screen and (min-width: 600px) {
|
||||||
.tight-container {
|
.tight-container {
|
||||||
--window-width: 100vw;
|
--window-width: 100vw;
|
||||||
--window-height: 100vh;
|
--window-height: var(--full-height);
|
||||||
--window-content-width: calc(100% - var(--sidebar-width));
|
--window-content-width: calc(100% - var(--sidebar-width));
|
||||||
|
|
||||||
@include container();
|
@include container();
|
||||||
|
|
||||||
max-width: 100vw;
|
max-width: 100vw;
|
||||||
max-height: 100vh;
|
max-height: var(--full-height);
|
||||||
|
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
}
|
}
|
||||||
@ -74,7 +74,7 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
left: -100%;
|
left: -100%;
|
||||||
z-index: 999;
|
z-index: 999;
|
||||||
height: 100vh;
|
height: var(--full-height);
|
||||||
transition: all ease 0.3s;
|
transition: all ease 0.3s;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,13 @@ import DownloadIcon from "../icons/download.svg";
|
|||||||
|
|
||||||
import { Message, SubmitKey, useChatStore, ChatSession } from "../store";
|
import { Message, SubmitKey, useChatStore, ChatSession } from "../store";
|
||||||
import { showModal, showToast } from "./ui-lib";
|
import { showModal, showToast } from "./ui-lib";
|
||||||
import { copyToClipboard, downloadAs, isIOS, selectOrCopy } from "../utils";
|
import {
|
||||||
|
copyToClipboard,
|
||||||
|
downloadAs,
|
||||||
|
isIOS,
|
||||||
|
isMobileScreen,
|
||||||
|
selectOrCopy,
|
||||||
|
} from "../utils";
|
||||||
import Locale from "../locales";
|
import Locale from "../locales";
|
||||||
|
|
||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
@ -102,7 +108,7 @@ export function ChatList() {
|
|||||||
state.currentSessionIndex,
|
state.currentSessionIndex,
|
||||||
state.selectSession,
|
state.selectSession,
|
||||||
state.removeSession,
|
state.removeSession,
|
||||||
]
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -196,7 +202,7 @@ export function Chat(props: {
|
|||||||
setPromptHints(promptStore.search(text));
|
setPromptHints(promptStore.search(text));
|
||||||
},
|
},
|
||||||
100,
|
100,
|
||||||
{ leading: true, trailing: true }
|
{ leading: true, trailing: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
const onPromptSelect = (prompt: Prompt) => {
|
const onPromptSelect = (prompt: Prompt) => {
|
||||||
@ -210,7 +216,7 @@ export function Chat(props: {
|
|||||||
if (!dom) return;
|
if (!dom) return;
|
||||||
const paddingBottomNum: number = parseInt(
|
const paddingBottomNum: number = parseInt(
|
||||||
window.getComputedStyle(dom).paddingBottom,
|
window.getComputedStyle(dom).paddingBottom,
|
||||||
10
|
10,
|
||||||
);
|
);
|
||||||
dom.scrollTop = dom.scrollHeight - dom.offsetHeight + paddingBottomNum;
|
dom.scrollTop = dom.scrollHeight - dom.offsetHeight + paddingBottomNum;
|
||||||
};
|
};
|
||||||
@ -284,9 +290,7 @@ export function Chat(props: {
|
|||||||
|
|
||||||
// for auto-scroll
|
// for auto-scroll
|
||||||
const latestMessageRef = useRef<HTMLDivElement>(null);
|
const latestMessageRef = useRef<HTMLDivElement>(null);
|
||||||
|
const [autoScroll, setAutoScroll] = useState(true);
|
||||||
// wont scroll while hovering messages
|
|
||||||
const [autoScroll, setAutoScroll] = useState(false);
|
|
||||||
|
|
||||||
// preview messages
|
// preview messages
|
||||||
const messages = (session.messages as RenderMessage[])
|
const messages = (session.messages as RenderMessage[])
|
||||||
@ -300,7 +304,7 @@ export function Chat(props: {
|
|||||||
preview: true,
|
preview: true,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
: []
|
: [],
|
||||||
)
|
)
|
||||||
.concat(
|
.concat(
|
||||||
userInput.length > 0
|
userInput.length > 0
|
||||||
@ -312,14 +316,24 @@ export function Chat(props: {
|
|||||||
preview: true,
|
preview: true,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
: []
|
: [],
|
||||||
);
|
);
|
||||||
|
|
||||||
// auto scroll
|
// auto scroll
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const dom = latestMessageRef.current;
|
const dom = latestMessageRef.current;
|
||||||
if (dom && !isIOS() && autoScroll) {
|
const inputDom = inputRef.current;
|
||||||
|
|
||||||
|
// only scroll when input overlaped message body
|
||||||
|
let shouldScroll = true;
|
||||||
|
if (dom && inputDom) {
|
||||||
|
const domRect = dom.getBoundingClientRect();
|
||||||
|
const inputRect = inputDom.getBoundingClientRect();
|
||||||
|
shouldScroll = domRect.top > inputRect.top;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dom && autoScroll && shouldScroll) {
|
||||||
dom.scrollIntoView({
|
dom.scrollIntoView({
|
||||||
block: "end",
|
block: "end",
|
||||||
});
|
});
|
||||||
@ -340,7 +354,7 @@ export function Chat(props: {
|
|||||||
const newTopic = prompt(Locale.Chat.Rename, session.topic);
|
const newTopic = prompt(Locale.Chat.Rename, session.topic);
|
||||||
if (newTopic && newTopic !== session.topic) {
|
if (newTopic && newTopic !== session.topic) {
|
||||||
chatStore.updateCurrentSession(
|
chatStore.updateCurrentSession(
|
||||||
(session) => (session.topic = newTopic!)
|
(session) => (session.topic = newTopic!),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
@ -475,7 +489,7 @@ export function Chat(props: {
|
|||||||
onFocus={() => setAutoScroll(true)}
|
onFocus={() => setAutoScroll(true)}
|
||||||
onBlur={() => {
|
onBlur={() => {
|
||||||
setAutoScroll(false);
|
setAutoScroll(false);
|
||||||
setTimeout(() => setPromptHints([]), 100);
|
setTimeout(() => setPromptHints([]), 500);
|
||||||
}}
|
}}
|
||||||
autoFocus={!props?.sideBarShowing}
|
autoFocus={!props?.sideBarShowing}
|
||||||
/>
|
/>
|
||||||
@ -586,7 +600,7 @@ export function Home() {
|
|||||||
state.newSession,
|
state.newSession,
|
||||||
state.currentSessionIndex,
|
state.currentSessionIndex,
|
||||||
state.removeSession,
|
state.removeSession,
|
||||||
]
|
],
|
||||||
);
|
);
|
||||||
const loading = !useHasHydrated();
|
const loading = !useHasHydrated();
|
||||||
const [showSideBar, setShowSideBar] = useState(true);
|
const [showSideBar, setShowSideBar] = useState(true);
|
||||||
@ -604,7 +618,9 @@ export function Home() {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`${
|
className={`${
|
||||||
config.tightBorder ? styles["tight-container"] : styles.container
|
config.tightBorder && !isMobileScreen()
|
||||||
|
? styles["tight-container"]
|
||||||
|
: styles.container
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -23,7 +23,7 @@ import {
|
|||||||
import { Avatar, PromptHints } from "./home";
|
import { Avatar, PromptHints } from "./home";
|
||||||
|
|
||||||
import Locale, { AllLangs, changeLang, getLang } from "../locales";
|
import Locale, { AllLangs, changeLang, getLang } from "../locales";
|
||||||
import { getCurrentCommitId } from "../utils";
|
import { getCurrentVersion } from "../utils";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { UPDATE_URL } from "../constant";
|
import { UPDATE_URL } from "../constant";
|
||||||
import { SearchService, usePromptStore } from "../store/prompt";
|
import { SearchService, usePromptStore } from "../store/prompt";
|
||||||
@ -60,7 +60,7 @@ export function Settings(props: { closeSettings: () => void }) {
|
|||||||
|
|
||||||
const updateStore = useUpdateStore();
|
const updateStore = useUpdateStore();
|
||||||
const [checkingUpdate, setCheckingUpdate] = useState(false);
|
const [checkingUpdate, setCheckingUpdate] = useState(false);
|
||||||
const currentId = getCurrentCommitId();
|
const currentId = getCurrentVersion();
|
||||||
const remoteId = updateStore.remoteId;
|
const remoteId = updateStore.remoteId;
|
||||||
const hasNewVersion = currentId !== remoteId;
|
const hasNewVersion = currentId !== remoteId;
|
||||||
|
|
||||||
@ -267,19 +267,17 @@ export function Settings(props: { closeSettings: () => void }) {
|
|||||||
></input>
|
></input>
|
||||||
</SettingItem>
|
</SettingItem>
|
||||||
|
|
||||||
<div className="no-mobile">
|
<SettingItem title={Locale.Settings.TightBorder}>
|
||||||
<SettingItem title={Locale.Settings.TightBorder}>
|
<input
|
||||||
<input
|
type="checkbox"
|
||||||
type="checkbox"
|
checked={config.tightBorder}
|
||||||
checked={config.tightBorder}
|
onChange={(e) =>
|
||||||
onChange={(e) =>
|
updateConfig(
|
||||||
updateConfig(
|
(config) => (config.tightBorder = e.currentTarget.checked),
|
||||||
(config) => (config.tightBorder = e.currentTarget.checked),
|
)
|
||||||
)
|
}
|
||||||
}
|
></input>
|
||||||
></input>
|
</SettingItem>
|
||||||
</SettingItem>
|
|
||||||
</div>
|
|
||||||
</List>
|
</List>
|
||||||
<List>
|
<List>
|
||||||
<SettingItem
|
<SettingItem
|
||||||
|
@ -3,3 +3,4 @@ export const REPO = "ChatGPT-Next-Web";
|
|||||||
export const REPO_URL = `https://github.com/${OWNER}/${REPO}`;
|
export const REPO_URL = `https://github.com/${OWNER}/${REPO}`;
|
||||||
export const UPDATE_URL = `${REPO_URL}#%E4%BF%9D%E6%8C%81%E6%9B%B4%E6%96%B0-keep-updated`;
|
export const UPDATE_URL = `${REPO_URL}#%E4%BF%9D%E6%8C%81%E6%9B%B4%E6%96%B0-keep-updated`;
|
||||||
export const FETCH_COMMIT_URL = `https://api.github.com/repos/${OWNER}/${REPO}/commits?per_page=1`;
|
export const FETCH_COMMIT_URL = `https://api.github.com/repos/${OWNER}/${REPO}/commits?per_page=1`;
|
||||||
|
export const FETCH_TAG_URL = `https://api.github.com/repos/${OWNER}/${REPO}/tags?per_page=1`;
|
||||||
|
@ -8,11 +8,11 @@ import { ACCESS_CODES, IS_IN_DOCKER } from "./api/access";
|
|||||||
let COMMIT_ID: string | undefined;
|
let COMMIT_ID: string | undefined;
|
||||||
try {
|
try {
|
||||||
COMMIT_ID = process
|
COMMIT_ID = process
|
||||||
.execSync("git rev-parse --short HEAD")
|
.execSync("git describe --tags --abbrev=0")
|
||||||
.toString()
|
.toString()
|
||||||
.trim();
|
.trim();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("No git or not from git repo.")
|
console.error("No git or not from git repo.");
|
||||||
}
|
}
|
||||||
|
|
||||||
export const metadata = {
|
export const metadata = {
|
||||||
@ -22,13 +22,13 @@ export const metadata = {
|
|||||||
title: "ChatGPT Next Web",
|
title: "ChatGPT Next Web",
|
||||||
statusBarStyle: "black-translucent",
|
statusBarStyle: "black-translucent",
|
||||||
},
|
},
|
||||||
themeColor: "#fafafa"
|
themeColor: "#fafafa",
|
||||||
};
|
};
|
||||||
|
|
||||||
function Meta() {
|
function Meta() {
|
||||||
const metas = {
|
const metas = {
|
||||||
version: COMMIT_ID ?? "unknown",
|
version: COMMIT_ID ?? "unknown",
|
||||||
access: (ACCESS_CODES.size > 0 || IS_IN_DOCKER) ? "enabled" : "disabled",
|
access: ACCESS_CODES.size > 0 || IS_IN_DOCKER ? "enabled" : "disabled",
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
import { persist } from "zustand/middleware";
|
import { persist } from "zustand/middleware";
|
||||||
import { FETCH_COMMIT_URL } from "../constant";
|
import { FETCH_COMMIT_URL, FETCH_TAG_URL } from "../constant";
|
||||||
import { getCurrentCommitId } from "../utils";
|
import { getCurrentVersion } from "../utils";
|
||||||
|
|
||||||
export interface UpdateStore {
|
export interface UpdateStore {
|
||||||
lastUpdate: number;
|
lastUpdate: number;
|
||||||
@ -19,16 +19,15 @@ export const useUpdateStore = create<UpdateStore>()(
|
|||||||
remoteId: "",
|
remoteId: "",
|
||||||
|
|
||||||
async getLatestCommitId(force = false) {
|
async getLatestCommitId(force = false) {
|
||||||
const overOneHour = Date.now() - get().lastUpdate > 3600 * 1000;
|
const overTenMins = Date.now() - get().lastUpdate > 10 * 60 * 1000;
|
||||||
const shouldFetch = force || overOneHour;
|
const shouldFetch = force || overTenMins;
|
||||||
if (!shouldFetch) {
|
if (!shouldFetch) {
|
||||||
return getCurrentCommitId();
|
return getCurrentVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = await (await fetch(FETCH_COMMIT_URL)).json();
|
const data = await (await fetch(FETCH_TAG_URL)).json();
|
||||||
const sha = data[0].sha as string;
|
const remoteId = data[0].name as string;
|
||||||
const remoteId = sha.substring(0, 7);
|
|
||||||
set(() => ({
|
set(() => ({
|
||||||
lastUpdate: Date.now(),
|
lastUpdate: Date.now(),
|
||||||
remoteId,
|
remoteId,
|
||||||
@ -37,13 +36,13 @@ export const useUpdateStore = create<UpdateStore>()(
|
|||||||
return remoteId;
|
return remoteId;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("[Fetch Upstream Commit Id]", error);
|
console.error("[Fetch Upstream Commit Id]", error);
|
||||||
return getCurrentCommitId();
|
return getCurrentVersion();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
name: UPDATE_KEY,
|
name: UPDATE_KEY,
|
||||||
version: 1,
|
version: 1,
|
||||||
}
|
},
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
|
@ -53,12 +53,13 @@
|
|||||||
--sidebar-width: 300px;
|
--sidebar-width: 300px;
|
||||||
--window-content-width: calc(100% - var(--sidebar-width));
|
--window-content-width: calc(100% - var(--sidebar-width));
|
||||||
--message-max-width: 80%;
|
--message-max-width: 80%;
|
||||||
|
--full-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media only screen and (max-width: 600px) {
|
@media only screen and (max-width: 600px) {
|
||||||
:root {
|
:root {
|
||||||
--window-width: 100vw;
|
--window-width: 100vw;
|
||||||
--window-height: 100vh;
|
--window-height: var(--full-height);
|
||||||
--sidebar-width: 100vw;
|
--sidebar-width: 100vw;
|
||||||
--window-content-width: var(--window-width);
|
--window-content-width: var(--window-width);
|
||||||
--message-max-width: 100%;
|
--message-max-width: 100%;
|
||||||
@ -80,14 +81,14 @@ body {
|
|||||||
color: var(--black);
|
color: var(--black);
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
height: 100vh;
|
height: var(--full-height);
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
font-family: "Noto Sans SC", "SF Pro SC", "SF Pro Text", "SF Pro Icons",
|
font-family: "Noto Sans SC", "SF Pro SC", "SF Pro Text", "SF Pro Icons",
|
||||||
"PingFang SC", "Helvetica Neue", "Helvetica", "Arial", sans-serif;
|
"PingFang SC", "Helvetica Neue", "Helvetica", "Arial", sans-serif;
|
||||||
|
|
||||||
@media only screen and (max-width: 600px) {
|
@media only screen and (max-width: 600px) {
|
||||||
background-color: var(--second);
|
background-color: var(--second);
|
||||||
@ -119,6 +120,11 @@ select {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
background-color: var(--white);
|
background-color: var(--white);
|
||||||
color: var(--black);
|
color: var(--black);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="checkbox"] {
|
input[type="checkbox"] {
|
||||||
@ -196,7 +202,7 @@ div.math {
|
|||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
height: 100vh;
|
height: var(--full-height);
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
background-color: rgba($color: #000000, $alpha: 0.5);
|
background-color: rgba($color: #000000, $alpha: 0.5);
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -120,33 +120,3 @@
|
|||||||
cursor: help;
|
cursor: help;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin light {
|
|
||||||
.markdown-body pre {
|
|
||||||
filter: invert(1) hue-rotate(90deg) brightness(1.3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@mixin dark {
|
|
||||||
.markdown-body pre {
|
|
||||||
filter: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
:root {
|
|
||||||
@include light();
|
|
||||||
}
|
|
||||||
|
|
||||||
.light {
|
|
||||||
@include light();
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark {
|
|
||||||
@include dark();
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
:root {
|
|
||||||
@include dark();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -45,6 +45,10 @@ export function isIOS() {
|
|||||||
return /iphone|ipad|ipod/.test(userAgent);
|
return /iphone|ipad|ipod/.test(userAgent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isMobileScreen() {
|
||||||
|
return window.innerWidth <= 600;
|
||||||
|
}
|
||||||
|
|
||||||
export function selectOrCopy(el: HTMLElement, content: string) {
|
export function selectOrCopy(el: HTMLElement, content: string) {
|
||||||
const currentSelection = window.getSelection();
|
const currentSelection = window.getSelection();
|
||||||
|
|
||||||
@ -72,7 +76,7 @@ export function queryMeta(key: string, defaultValue?: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let currentId: string;
|
let currentId: string;
|
||||||
export function getCurrentCommitId() {
|
export function getCurrentVersion() {
|
||||||
if (currentId) {
|
if (currentId) {
|
||||||
return currentId;
|
return currentId;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user