Removed the Labs page, removed a store

This commit is contained in:
Enrico Ros
2023-11-21 21:31:21 -08:00
parent f9e38c7220
commit bb36dbc4b9
14 changed files with 121 additions and 174 deletions
+1 -1
View File
@@ -35,7 +35,7 @@ by release.
- **Backup/Restore** - save chats, and restore them later
- **[Local model support with Oobabooga server](../docs/config-local-oobabooga)** - run your own LLMs!
- **Flatten conversations** - conversations summarizer with 4 modes
- **Fork conversations** - create a new chat, to experiment with different endings
- **Fork conversations** - create a new chat, to try with different endings
- New commands: /s to add a System message, and /a for an Assistant message
- New Chat modes: Write-only - just appends the message, without assistant response
- Fix STOP generation - in sync with the Vercel team to fix a long-standing NextJS issue
-14
View File
@@ -1,14 +0,0 @@
import * as React from 'react';
import { AppLabs } from '../src/apps/labs/AppLabs';
import { AppLayout } from '~/common/layout/AppLayout';
export default function LabsPage() {
return (
<AppLayout suspendAutoModelsSetup>
<AppLabs />
</AppLayout>
);
}
@@ -10,6 +10,7 @@ import { DConversationId, useChatStore } from '~/common/state/store-chats';
import { OpenAIIcon } from '~/common/components/icons/OpenAIIcon';
import { closeLayoutDrawer } from '~/common/layout/store-applayout';
import { useUIPreferencesStore } from '~/common/state/store-ui';
import { useUXLabsStore } from '~/common/state/store-ux-labs';
import { ConversationItem } from './ConversationItem';
@@ -35,7 +36,8 @@ export function ChatDrawerItems(props: {
conversationIDs: state.conversations.map(_c => _c.id),
maxChatMessages: state.conversations.reduce((longest, _c) => Math.max(longest, _c.messages.length), 0),
}), shallow);
const [experimentalLabs, showSymbols] = useUIPreferencesStore(state => [state.experimentalLabs, state.zenMode !== 'cleaner'], shallow);
const showSymbols = useUIPreferencesStore(state => state.zenMode !== 'cleaner');
const labsEnhancedUI = useUXLabsStore(state => state.labsEnhancedUI);
// derived state
const totalConversations = conversationIDs.length;
@@ -119,7 +121,7 @@ export function ChatDrawerItems(props: {
conversationId={conversationId}
isActive={conversationId === props.conversationId}
isLonely={singleChat}
maxChatMessages={(experimentalLabs || softMaxReached) ? maxChatMessages : 0}
maxChatMessages={(labsEnhancedUI || softMaxReached) ? maxChatMessages : 0}
showSymbols={showSymbols}
onConversationActivate={handleConversationActivate}
onConversationDelete={handleConversationDelete}
@@ -5,6 +5,7 @@ import { Box, MenuItem, Radio, Typography } from '@mui/joy';
import { CloseableMenu } from '~/common/components/CloseableMenu';
import { KeyStroke } from '~/common/components/KeyStroke';
import { useUIPreferencesStore } from '~/common/state/store-ui';
import { useUXLabsStore } from '~/common/state/store-ux-labs';
import { ChatModeId } from '../../AppChat';
@@ -48,10 +49,11 @@ function fixNewLineShortcut(shortcut: string, enterIsNewLine: boolean) {
return shortcut;
}
export function ChatModeMenu(props: { anchorEl: HTMLAnchorElement | null, onClose: () => void, experimental: boolean, chatModeId: ChatModeId, onSetChatModeId: (chatMode: ChatModeId) => void }) {
export function ChatModeMenu(props: { anchorEl: HTMLAnchorElement | null, onClose: () => void, chatModeId: ChatModeId, onSetChatModeId: (chatMode: ChatModeId) => void }) {
// external state
const enterIsNewline = useUIPreferencesStore(state => state.enterIsNewline);
const labsMagicDraw = useUXLabsStore(state => state.labsMagicDraw);
return <CloseableMenu
placement='top-end' sx={{ minWidth: 320 }}
@@ -66,7 +68,7 @@ export function ChatModeMenu(props: { anchorEl: HTMLAnchorElement | null, onClos
{/* ChatMode items */}
{Object.entries(ChatModeItems)
.filter(([, { experimental }]) => props.experimental || !experimental)
.filter(([, { experimental }]) => labsMagicDraw || !experimental)
.map(([key, data]) =>
<MenuItem key={'chat-mode-' + key} onClick={() => props.onSetChatModeId(key as ChatModeId)}>
<Box sx={{ flexGrow: 1, display: 'flex', flexDirection: 'row', alignItems: 'center', gap: 2 }}>
@@ -137,7 +137,7 @@ export function Composer(props: {
const isMobile = useIsMobile();
const [chatModeId, setChatModeId] = React.useState<ChatModeId>('immediate');
const [startupText, setStartupText] = useComposerStartupText();
const [enterIsNewline, experimentalLabs] = useUIPreferencesStore(state => [state.enterIsNewline, state.experimentalLabs], shallow);
const enterIsNewline = useUIPreferencesStore(state => state.enterIsNewline);
const { assistantTyping, systemPurposeId, tokenCount: conversationTokenCount, stopTyping } = useChatStore(state => {
const conversation = state.conversations.find(_c => _c.id === props.conversationId);
return {
@@ -638,7 +638,6 @@ export function Composer(props: {
{!!chatModeMenuAnchor && (
<ChatModeMenu
anchorEl={chatModeMenuAnchor} onClose={handleModeSelectorHide}
experimental={experimentalLabs}
chatModeId={chatModeId} onSetChatModeId={handleModeChange}
/>
)}
@@ -10,6 +10,7 @@ import TelegramIcon from '@mui/icons-material/Telegram';
import { DConversationId, useChatStore } from '~/common/state/store-chats';
import { Link } from '~/common/components/Link';
import { useUIPreferencesStore } from '~/common/state/store-ui';
import { useUXLabsStore } from '~/common/state/store-ux-labs';
import { SystemPurposeId, SystemPurposes } from '../../../../data';
import { usePurposeStore } from './store-purposes';
@@ -46,6 +47,7 @@ export function PersonaSelector(props: { conversationId: DConversationId, runExa
// external state
const showFinder = useUIPreferencesStore(state => state.showPurposeFinder);
const labsPersonaYTCreator = useUXLabsStore(state => state.labsPersonaYTCreator);
const { systemPurposeId, setSystemPurposeId } = useChatStore(state => {
const conversation = state.conversations.find(conversation => conversation.id === props.conversationId);
return {
@@ -184,7 +186,7 @@ export function PersonaSelector(props: { conversationId: DConversationId, runExa
</Grid>
))}
{/* Button to start the YouTube persona creator */}
<Grid>
{labsPersonaYTCreator && <Grid>
<Button
variant='soft' color='neutral'
component={Link} noLinkStyle href='/personas'
@@ -207,9 +209,8 @@ export function PersonaSelector(props: { conversationId: DConversationId, runExa
YouTube persona creator
</div>
</Button>
</Grid>
</Grid>}
</Grid>
<Typography
level='body-sm'
sx={{
-68
View File
@@ -1,68 +0,0 @@
import * as React from 'react';
import { shallow } from 'zustand/shallow';
import { Box, Button, Card, CardContent, Container, Switch, Typography } from '@mui/joy';
import ScienceIcon from '@mui/icons-material/Science';
import { Link } from '~/common/components/Link';
import { useUIPreferencesStore } from '~/common/state/store-ui';
export function AppLabs() {
// external state
const { experimentalLabs, setExperimentalLabs } = useUIPreferencesStore(state => ({
experimentalLabs: state.experimentalLabs, setExperimentalLabs: state.setExperimentalLabs,
}), shallow);
const handleLabsChange = (event: React.ChangeEvent<HTMLInputElement>) => setExperimentalLabs(event.target.checked);
return (
<Box sx={{
backgroundColor: 'background.level1',
display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
flexGrow: 1,
overflowY: 'auto',
minHeight: 96,
p: { xs: 3, md: 6 },
gap: 4,
}}>
<Typography level='h1' sx={{ fontSize: '3.6rem' }}>
Labs <ScienceIcon sx={{ fontSize: '3.3rem' }} />
</Typography>
<Switch checked={experimentalLabs} onChange={handleLabsChange}
endDecorator={experimentalLabs ? 'On' : 'Off'}
slotProps={{ endDecorator: { sx: { minWidth: 26 } } }} />
<Container disableGutters maxWidth='sm'>
<Card>
<CardContent>
<Typography>
The Labs section is where we experiment with new features and ideas.
</Typography>
<Typography level='title-md' sx={{ mt: 2 }}>
Features {experimentalLabs ? 'enabled' : 'disabled'}:
</Typography>
<ul style={{ marginTop: 8, marginBottom: 8, paddingInlineStart: 32 }}>
<li><b>Text tools</b> - complete (highlight differences)</li>
<li><b>YouTube persona synthesizer</b> - alpha, not persisted</li>
<li><b>Chat mode: follow-up/augmentation</b> - alpha (diagrams)</li>
<li><b>Relative chats size</b> - complete</li>
</ul>
<Typography sx={{ mt: 2 }}>
For any questions and creative idea, please join us on Discord, and let&apos;s talk!
</Typography>
</CardContent>
</Card>
</Container>
<Button variant='solid' color='neutral' size='lg' component={Link} href='/' noLinkStyle>
Got it!
</Button>
</Box>
);
}
+25 -26
View File
@@ -1,41 +1,40 @@
import * as React from 'react';
import { shallow } from 'zustand/shallow';
import { Button, FormControl, Switch } from '@mui/joy';
import { FormControl, Typography } from '@mui/joy';
import { FormLabelStart } from '~/common/components/forms/FormLabelStart';
import { closeLayoutPreferences } from '~/common/layout/store-applayout';
import { navigateToLabs } from '~/common/app.routes';
import { useUIPreferencesStore } from '~/common/state/store-ui';
import { FormSwitchControl } from '~/common/components/forms/FormSwitchControl';
import { useUXLabsStore } from '~/common/state/store-ux-labs';
export function UxLabsSettings() {
// external state
const {
experimentalLabs, setExperimentalLabs,
} = useUIPreferencesStore(state => ({
experimentalLabs: state.experimentalLabs, setExperimentalLabs: state.setExperimentalLabs,
}), shallow);
const handleExperimentalLabsChange = (event: React.ChangeEvent<HTMLInputElement>) => setExperimentalLabs(event.target.checked);
const { /*labsEnhancedUI,*/ labsMagicDraw, labsPersonaYTCreator, /*setLabsEnhancedUI,*/ setLabsMagicDraw, setLabsPersonaYTCreator } = useUXLabsStore();
return <>
<FormControl orientation='horizontal' sx={{ justifyContent: 'space-between' }}>
<FormLabelStart title='Experiments'
description={experimentalLabs ? 'Enabled' : 'Disabled'} />
<Switch checked={experimentalLabs} onChange={handleExperimentalLabsChange}
endDecorator={experimentalLabs ? 'On' : 'Off'}
slotProps={{ endDecorator: { sx: { minWidth: 26 } } }} />
</FormControl>
<FormSwitchControl
title='YouTube Personas' description={labsPersonaYTCreator ? 'Creator Enabled' : 'Disabled'}
checked={labsPersonaYTCreator} onChange={setLabsPersonaYTCreator}
/>
<Button variant='soft' onClick={() => {
closeLayoutPreferences();
void navigateToLabs();
}} sx={{ ml: 'auto' }}>
👉 See Experiments
</Button>
{/*<FormSwitchControl*/}
{/* title='Enhanced UI' description={labsEnhancedUI ? 'Enabled' : 'Disabled'}*/}
{/* checked={labsEnhancedUI} onChange={setLabsEnhancedUI}*/}
{/*/>*/}
<FormSwitchControl
title='Assisted Draw' description={labsMagicDraw ? 'Enabled' : 'Disabled'}
checked={labsMagicDraw} onChange={setLabsMagicDraw}
/>
<FormControl orientation='horizontal' sx={{ justifyContent: 'space-between', alignItems: 'center' }}>
<FormLabelStart title='Graduated' />
<Typography level='body-xs'>
Auto Diagrams · Relative chat size · Text Tools
</Typography>
</FormControl>
</>;
}
-3
View File
@@ -8,7 +8,6 @@ import Router from 'next/router';
export const ROUTE_APP_CHAT = '/';
const APP_LINK_CHAT = '/link/chat/:linkId';
const APP_LABS = '/labs';
export const getHomeLink = () => ROUTE_APP_CHAT;
@@ -16,8 +15,6 @@ export const getChatLinkRelativePath = (chatLinkId: string) => APP_LINK_CHAT.rep
export const navigateToChat = async () => await Router.push(ROUTE_APP_CHAT);
export const navigateToLabs = async () => await Router.push(APP_LABS);
export const navigateBack = Router.back;
export interface AppCallQueryParams {
@@ -10,16 +10,18 @@ import { FormLabelStart } from './FormLabelStart';
*/
export function FormSwitchControl(props: {
title: string | React.JSX.Element, description?: string | React.JSX.Element,
value: boolean, onChange: (on: boolean) => void,
on?: string, off?: string, fullWidth?: boolean,
checked: boolean, onChange: (on: boolean) => void,
}) {
return (
<FormControl orientation='horizontal' sx={{ flexWrap: 'wrap', justifyContent: 'space-between', alignItems: 'center' }}>
<FormLabelStart title={props.title} description={props.description} />
<Switch
checked={props.value}
checked={props.checked}
onChange={event => props.onChange(event.target.checked)}
endDecorator={props.value ? 'Enabled' : 'Off'}
sx={{ flexGrow: 1 }}
endDecorator={props.checked ? props.on || 'On' : props.off || 'Off'}
sx={props.fullWidth ? { flexGrow: 1 } : undefined}
slotProps={{ endDecorator: { sx: { minWidth: 26 } } }}
/>
</FormControl>
);
+3 -6
View File
@@ -3,7 +3,7 @@ import * as React from 'react';
import { FormControl, FormLabel, Radio, RadioGroup } from '@mui/joy';
export type FormRadioOption<T extends string> = { label: string, value: T, experimental?: boolean };
export type FormRadioOption<T extends string> = { label: string, value: T, disabled?: boolean };
/**
@@ -14,9 +14,6 @@ export function useFormRadio<T extends string>(initialValue: T, options: FormRad
// state
const [value, setValue] = React.useState<T | null>(initialValue);
// external state
// const experimentalLabs = useUIPreferencesStore(state => state.experimentalLabs);
const handleChange = React.useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
setValue(event.target.value as T | null);
}, []);
@@ -31,10 +28,10 @@ export function useFormRadio<T extends string>(initialValue: T, options: FormRad
value={value} onChange={handleChange}
>
{options.map((option) =>
<Radio key={option.value} disabled={!!option.experimental /*&& !experimentalLabs*/} value={option.value} label={option.label} />)}
<Radio key={option.value} disabled={option.disabled} value={option.value} label={option.label} />)}
</RadioGroup>
</FormControl>,
[/*experimentalLabs,*/ handleChange, hidden, label, options, value],
[handleChange, hidden, label, options, value],
);
return [value, component];
+27 -41
View File
@@ -1,45 +1,13 @@
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
// UI Counters
interface UICountersStore {
actionCounters: Record<string, number>;
incrementActionCounter: (key: string) => void;
clearActionCounter: (key: string) => void;
clearAllActionCounters: () => void;
}
const useUICountersStore = create<UICountersStore>()(
persist(
(set) => ({
actionCounters: {},
incrementActionCounter: (key: string) =>
set((state) => ({
actionCounters: { ...state.actionCounters, [key]: (state.actionCounters[key] || 0) + 1 },
})),
clearActionCounter: (key: string) =>
set((state) => ({
actionCounters: { ...state.actionCounters, [key]: 0 },
})),
clearAllActionCounters: () => set({ actionCounters: {} }),
}),
{
name: 'app-ui-counters',
},
),
);
type UiCounterKey = 'export-share' | 'share-chat-link' | 'call-wizard';
export function useUICounter(key: UiCounterKey) {
const value = useUICountersStore((state) => state.actionCounters[key] || 0);
return { value, novel: !value, touch: () => useUICountersStore.getState().incrementActionCounter(key) };
}
// UI Preferences
interface UIPreferencesStore {
// UI Features
preferredLanguage: string;
setPreferredLanguage: (preferredLanguage: string) => void;
@@ -52,9 +20,6 @@ interface UIPreferencesStore {
enterIsNewline: boolean;
setEnterIsNewline: (enterIsNewline: boolean) => void;
experimentalLabs: boolean;
setExperimentalLabs: (experimentalLabs: boolean) => void;
renderMarkdown: boolean;
setRenderMarkdown: (renderMarkdown: boolean) => void;
@@ -64,12 +29,19 @@ interface UIPreferencesStore {
zenMode: 'clean' | 'cleaner';
setZenMode: (zenMode: 'clean' | 'cleaner') => void;
// UI Counters
actionCounters: Record<string, number>;
incrementActionCounter: (key: string) => void;
}
export const useUIPreferencesStore = create<UIPreferencesStore>()(
persist(
(set) => ({
// UI Features
preferredLanguage: (typeof navigator !== 'undefined') && navigator.language || 'en-US',
setPreferredLanguage: (preferredLanguage: string) => set({ preferredLanguage }),
@@ -82,9 +54,6 @@ export const useUIPreferencesStore = create<UIPreferencesStore>()(
enterIsNewline: false,
setEnterIsNewline: (enterIsNewline: boolean) => set({ enterIsNewline }),
experimentalLabs: false,
setExperimentalLabs: (experimentalLabs: boolean) => set({ experimentalLabs }),
renderMarkdown: true,
setRenderMarkdown: (renderMarkdown: boolean) => set({ renderMarkdown }),
@@ -95,6 +64,14 @@ export const useUIPreferencesStore = create<UIPreferencesStore>()(
zenMode: 'clean',
setZenMode: (zenMode: 'clean' | 'cleaner') => set({ zenMode }),
// UI Counters
actionCounters: {},
incrementActionCounter: (key: string) =>
set((state) => ({
actionCounters: { ...state.actionCounters, [key]: (state.actionCounters[key] || 0) + 1 },
})),
}),
{
name: 'app-ui',
@@ -113,3 +90,12 @@ export const useUIPreferencesStore = create<UIPreferencesStore>()(
},
),
);
export function useUICounter(key: 'export-share' | 'share-chat-link' | 'call-wizard') {
const value = useUIPreferencesStore((state) => state.actionCounters[key] || 0);
return {
value,
novel: !value,
touch: () => useUIPreferencesStore.getState().incrementActionCounter(key),
};
}
+44
View File
@@ -0,0 +1,44 @@
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
// UX Labs Experiments
/**
* Graduated:
* - Persona YT Creator: still under a 'true' flag, to disable it if needed
* - Text Tools: dinamically shown where applicable
* - Chat Mode: follow-ups; moved to Chat Advanced UI, itemized (Auto-title, Auto-diagram)
*/
interface UXLabsStore {
labsEnhancedUI: boolean;
setLabsEnhancedUI: (labsEnhancedUI: boolean) => void;
labsMagicDraw: boolean;
setLabsMagicDraw: (labsMagicDraw: boolean) => void;
labsPersonaYTCreator: boolean;
setLabsPersonaYTCreator: (labsPersonaYTCreator: boolean) => void;
}
export const useUXLabsStore = create<UXLabsStore>()(
persist(
(set) => ({
labsEnhancedUI: false,
setLabsEnhancedUI: (labsEnhancedUI: boolean) => set({ labsEnhancedUI }),
labsMagicDraw: false,
setLabsMagicDraw: (labsMagicDraw: boolean) => set({ labsMagicDraw }),
labsPersonaYTCreator: true, // NOTE: default to true, as it is a graduated experiment
setLabsPersonaYTCreator: (labsPersonaYTCreator: boolean) => set({ labsPersonaYTCreator }),
}),
{
name: 'app-ux-labs',
},
),
);
+2 -2
View File
@@ -95,12 +95,12 @@ export function OpenAISourceSetup(props: { sourceId: DModelSourceId }) {
</Alert>}
{advanced.on && <FormSwitchControl
title='Moderation'
title='Moderation' on='Enabled' fullWidth
description={<>
<Link level='body-sm' href='https://platform.openai.com/docs/guides/moderation/moderation' target='_blank'>Overview</Link>,
{' '}<Link level='body-sm' href='https://openai.com/policies/usage-policies' target='_blank'>policy</Link>
</>}
value={moderationCheck}
checked={moderationCheck}
onChange={on => updateSetup({ moderationCheck: on })}
/>}