Skip to content

Commit

Permalink
add settings feature
Browse files Browse the repository at this point in the history
  • Loading branch information
zmh-program committed Oct 30, 2023
1 parent df6e13d commit ebc5edc
Show file tree
Hide file tree
Showing 15 changed files with 260 additions and 32 deletions.
1 change: 1 addition & 0 deletions app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
},
"dependencies": {
"@radix-ui/react-alert-dialog": "^1.0.4",
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-context-menu": "^2.1.4",
"@radix-ui/react-dialog": "^1.0.4",
"@radix-ui/react-dropdown-menu": "^2.0.5",
Expand Down
31 changes: 31 additions & 0 deletions app/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion app/src/assets/home.less
Original file line number Diff line number Diff line change
Expand Up @@ -333,11 +333,14 @@

.input-box {
width: 100%;
text-align: center;
color: hsl(var(--text));
white-space: pre-wrap;
padding: 0 2.5rem;

&.align {
text-align: center;
}

&::placeholder {
color: hsl(var(--text-secondary));
opacity: 1;
Expand Down
30 changes: 30 additions & 0 deletions app/src/assets/settings.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
.settings-container {
display: flex;
}

.settings-wrapper {
display: flex;
flex-direction: column;
margin: 1.5rem 0.5rem;
width: 100%;
color: hsl(var(--text));

.item {
display: flex;
flex-direction: row;
align-items: center;
user-select: none;

.value {
transition: .1s;
}
}

& > .item {
margin-bottom: 1rem;

&:last-child {
margin-bottom: 0;
}
}
}
23 changes: 17 additions & 6 deletions app/src/components/home/ChatWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {
} from "@/store/chat.ts";
import { manager } from "@/conversation/manager.ts";
import { formatMessage } from "@/utils/processor.ts";
import { triggerInstallApp } from "@/utils/app.ts";
import ChatInterface from "@/components/home/ChatInterface.tsx";
import { Button } from "@/components/ui/button.tsx";
import router from "@/router.tsx";
Expand Down Expand Up @@ -44,6 +43,7 @@ import { clearHistoryState, getQueryParam } from "@/utils/path.ts";
import { forgetMemory, popMemory, setMemory } from "@/utils/memory.ts";
import { useToast } from "@/components/ui/use-toast.ts";
import { ToastAction } from "@/components/ui/toast.tsx";
import {alignSelector, contextSelector, openDialog} from "@/store/settings.ts";

function ChatSpace() {
const [open, setOpen] = useState(false);
Expand Down Expand Up @@ -115,6 +115,9 @@ function ChatWrapper() {
const web = useSelector(selectWeb);
const messages = useSelector(selectMessages);
const target = useRef(null);
const context = useSelector(contextSelector);
const align = useSelector(alignSelector);

manager.setDispatch(dispatch);

function clearFile() {
Expand All @@ -126,10 +129,15 @@ function ChatWrapper() {
auth: boolean,
model: string,
web: boolean,
context: boolean,
): Promise<boolean> {
const message: string = formatMessage(file, data);
if (message.length > 0 && data.trim().length > 0) {
if (await manager.send(t, auth, { message, web, model, type: "chat" })) {
if (await manager.send(t, auth, {
message, web, model,
ignore_context: !context,
type: "chat",
})) {
forgetMemory("history");
clearFile();
return true;
Expand All @@ -140,7 +148,7 @@ function ChatWrapper() {

async function handleSend(auth: boolean, model: string, web: boolean) {
// because of the function wrapper, we need to update the selector state using props.
if (await processSend(input, auth, model, web)) {
if (await processSend(input, auth, model, web, context)) {
setInput("");
}
}
Expand All @@ -153,7 +161,7 @@ function ChatWrapper() {
useEffect(() => {
if (!init) return;
const query = getQueryParam("q").trim();
if (query.length > 0) processSend(query, auth, model, web).then();
if (query.length > 0) processSend(query, auth, model, web, context).then();
clearHistoryState();
}, [init]);

Expand Down Expand Up @@ -215,7 +223,7 @@ function ChatWrapper() {
)}
<Input
id={`input`}
className={`input-box`}
className={`input-box ${align && 'align'}`}
ref={target}
value={input}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
Expand Down Expand Up @@ -259,7 +267,10 @@ function ChatWrapper() {
<div className={`version`}>
<svg
className={`app`}
onClick={triggerInstallApp}
onClick={() => {
// triggerInstallApp();
dispatch(openDialog());
}}
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
Expand Down
6 changes: 0 additions & 6 deletions app/src/components/home/ModelSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { useTranslation } from "react-i18next";
import { useDispatch, useSelector } from "react-redux";
import { selectAuthenticated } from "@/store/auth.ts";
import { useToast } from "@/components/ui/use-toast.ts";
import { useEffect } from "react";
import { Model } from "@/conversation/types.ts";
import { modelEvent } from "@/events/model.ts";
import { isSubscribedSelector } from "@/store/subscription.ts";
Expand All @@ -30,11 +29,6 @@ function ModelSelector(props: ModelSelectorProps) {
const subscription = useSelector(isSubscribedSelector);
const student = useSelector(teenagerSelector);

useEffect(() => {
if (auth && model === "gpt-3.5-turbo-0613")
dispatch(setModel("gpt-3.5-turbo-16k-0613"));
}, [auth]);

modelEvent.bind((target: string) => {
if (supportModels.find((m) => m.id === target)) {
if (model === target) return;
Expand Down
28 changes: 28 additions & 0 deletions app/src/components/ui/checkbox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import * as React from "react"
import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
import { Check } from "lucide-react"

import { cn } from "@/components/ui/lib/utils"

const Checkbox = React.forwardRef<
React.ElementRef<typeof CheckboxPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>
>(({ className, ...props }, ref) => (
<CheckboxPrimitive.Root
ref={ref}
className={cn(
"peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
className
)}
{...props}
>
<CheckboxPrimitive.Indicator
className={cn("flex items-center justify-center text-current")}
>
<Check className="h-4 w-4" />
</CheckboxPrimitive.Indicator>
</CheckboxPrimitive.Root>
))
Checkbox.displayName = CheckboxPrimitive.Root.displayName

export { Checkbox }
2 changes: 1 addition & 1 deletion app/src/conf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
} from "@/utils/env.ts";
import { getMemory } from "@/utils/memory.ts";

export const version = "3.6.0";
export const version = "3.6.1";
export const dev: boolean = getDev();
export const deploy: boolean = true;
export let rest_api: string = getRestApi(deploy);
Expand Down
1 change: 1 addition & 0 deletions app/src/conversation/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export type ChatProps = {
message: string;
model: string;
web?: boolean;
ignore_context?: boolean;
};

type StreamCallback = (message: StreamMessage) => void;
Expand Down
51 changes: 51 additions & 0 deletions app/src/dialogs/Settings.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import "@/assets/settings.less";
import {useTranslation} from "react-i18next";
import {useDispatch, useSelector} from "react-redux";
import {alignSelector, contextSelector, dialogSelector, setAlign, setContext, setDialog} from "@/store/settings.ts";
import {Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle} from "@/components/ui/dialog.tsx";
import {Checkbox} from "@/components/ui/checkbox.tsx";

function Settings() {
const { t } = useTranslation();
const dispatch = useDispatch();
const open = useSelector(dialogSelector);

const align = useSelector(alignSelector);
const context = useSelector(contextSelector);

return (
<Dialog open={open} onOpenChange={(open) => dispatch(setDialog(open))}>
<DialogContent>
<DialogHeader>
<DialogTitle>{t("settings.title")}</DialogTitle>
<DialogDescription>
<div className={`settings-container`}>
<div className={`settings-wrapper`}>
<div className={`item`}>
<div className={`name`}>
{t('settings.align')}
</div>
<div className={`grow`} />
<Checkbox className={`value`} checked={align} onCheckedChange={(state: boolean) => {
dispatch(setAlign(state));
}} />
</div>
<div className={`item`}>
<div className={`name`}>
{t('settings.context')}
</div>
<div className={`grow`} />
<Checkbox className={`value`} checked={context} onCheckedChange={(state: boolean) => {
dispatch(setContext(state));
}} />
</div>
</div>
</div>
</DialogDescription>
</DialogHeader>
</DialogContent>
</Dialog>
)
}

export default Settings;
2 changes: 2 additions & 0 deletions app/src/dialogs/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Package from "./Package.tsx";
import Subscription from "./Subscription.tsx";
import ShareManagement from "./ShareManagement.tsx";
import Invitation from "./Invitation.tsx";
import Settings from "@/dialogs/Settings.tsx";

function DialogManager() {
return (
Expand All @@ -16,6 +17,7 @@ function DialogManager() {
<Subscription />
<ShareManagement />
<Invitation />
<Settings />
</>
);
}
Expand Down
5 changes: 5 additions & 0 deletions app/src/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,11 @@ const resources = {
contact: {
title: "联系我们",
},
settings: {
title: "设置",
context: "保留上下文",
align: "聊天框居中",
}
},
},
ru: {
Expand Down
2 changes: 2 additions & 0 deletions app/src/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import subscriptionReducer from "./subscription";
import apiReducer from "./api";
import sharingReducer from "./sharing";
import invitationReducer from "./invitation";
import settingsReducer from "./settings";

const store = configureStore({
reducer: {
Expand All @@ -20,6 +21,7 @@ const store = configureStore({
api: apiReducer,
sharing: sharingReducer,
invitation: invitationReducer,
settings: settingsReducer,
},
});

Expand Down
48 changes: 48 additions & 0 deletions app/src/store/settings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import {createSlice} from "@reduxjs/toolkit";
import {getMemory, setMemory} from "@/utils/memory.ts";
import {RootState} from "@/store/index.ts";

export const settingsSlice = createSlice({
name: "settings",
initialState: {
dialog: false,
context: getMemory("context") !== "false",
align: getMemory("align") !== "false",
},
reducers: {
toggleDialog: (state) => {
state.dialog = !state.dialog;
},
setDialog: (state, action) => {
state.dialog = action.payload as boolean;
},
openDialog: (state) => {
state.dialog = true;
},
closeDialog: (state) => {
state.dialog = false;
},
setContext: (state, action) => {
state.context = action.payload as boolean;
setMemory("context", String(action.payload));
},
setAlign: (state, action) => {
state.align = action.payload as boolean;
setMemory("align", String(action.payload));
}
},
})

export const {
toggleDialog,
setDialog,
openDialog,
closeDialog,
setContext,
setAlign,
} = settingsSlice.actions;
export default settingsSlice.reducer;

export const dialogSelector = (state: RootState): boolean => state.settings.dialog;
export const contextSelector = (state: RootState): boolean => state.settings.context;
export const alignSelector = (state: RootState): boolean => state.settings.align;
Loading

0 comments on commit ebc5edc

Please sign in to comment.