Cleanup Stores. [server]

This commit is contained in:
Enrico Ros
2024-10-11 04:02:31 -07:00
parent 4d01f8620a
commit e9dc735989
55 changed files with 276 additions and 309 deletions
+4 -5
View File
@@ -17,16 +17,16 @@ import { Brand } from '~/common/app.config';
import { ROUTE_APP_CHAT, ROUTE_INDEX } from '~/common/app.routes';
// apps access
import { incrementalNewsVersion, useAppNewsStateStore } from '../../src/apps/news/news.version';
import { incrementalNewsVersion } from '../../src/apps/news/news.version';
// capabilities access
import { useCapabilityBrowserSpeechRecognition, useCapabilityElevenLabs, useCapabilityTextToImage } from '~/common/components/useCapabilities';
// stores access
import { getLLMsDebugInfo } from '~/common/stores/llms/store-llms';
import { useAppStateStore } from '~/common/state/store-appstate';
import { useChatStore } from '~/common/stores/chat/store-chats';
import { useFolderStore } from '~/common/state/store-folders';
import { useFolderStore } from '~/common/stores/folders/store-chat-folders';
import { useLogicSherpaStore } from '~/common/logic/store-logic-sherpa';
import { useUXLabsStore } from '~/common/state/store-ux-labs';
// utils access
@@ -81,8 +81,7 @@ function AppDebug() {
const chatsCount = useChatStore.getState().conversations?.length;
const uxLabsExperiments = Object.entries(useUXLabsStore.getState()).filter(([_k, v]) => v === true).map(([k, _]) => k).join(', ');
const { folders, enableFolders } = useFolderStore.getState();
const { lastSeenNewsVersion } = useAppNewsStateStore.getState();
const { usageCount } = useAppStateStore.getState();
const { lastSeenNewsVersion, usageCount } = useLogicSherpaStore.getState();
// derived state
const cClient = {
+1 -1
View File
@@ -3,7 +3,7 @@ import * as React from 'react';
import { Alert, Box, Button, Typography } from '@mui/joy';
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
import { setComposerStartupText } from '../../src/apps/chat/components/composer/store-composer';
import { setComposerStartupText } from '~/common/logic/store-logic-sherpa';
import { callBrowseFetchPage } from '~/modules/browse/browse.client';
+1 -1
View File
@@ -1,8 +1,8 @@
import * as React from 'react';
import { AppNews } from '../src/apps/news/AppNews';
import { markNewsAsSeen } from '../src/apps/news/news.version';
import { markNewsAsSeen } from '~/common/logic/store-logic-sherpa';
import { withNextJSPerPageLayout } from '~/common/layout/withLayout';
+1 -1
View File
@@ -5,7 +5,7 @@ import { Box, Button, Typography } from '@mui/joy';
import { BeamStoreApi, useBeamStore } from '~/modules/beam/store-beam.hooks';
import { BeamView } from '~/modules/beam/BeamView';
import { createBeamVanillaStore } from '~/modules/beam/store-beam-vanilla';
import { createBeamVanillaStore } from '~/modules/beam/store-beam_vanilla';
import { OptimaToolbarIn } from '~/common/layout/optima/portals/OptimaPortalsIn';
import { createDConversation, DConversation } from '~/common/stores/chat/chat.conversation';
+1 -1
View File
@@ -32,7 +32,7 @@ import { getConversation, getConversationSystemPurposeId, useConversation } from
import { optimaActions, optimaOpenModels, optimaOpenPreferences, useSetOptimaAppMenu } from '~/common/layout/optima/useOptima';
import { themeBgAppChatComposer } from '~/common/app.theme';
import { useChatLLM } from '~/common/stores/llms/llms.hooks';
import { useFolderStore } from '~/common/state/store-folders';
import { useFolderStore } from '~/common/stores/folders/store-chat-folders';
import { useGlobalShortcuts } from '~/common/components/shortcuts/useGlobalShortcuts';
import { useIsMobile, useIsTallScreen } from '~/common/components/useMatchMedia';
import { useOverlayComponents } from '~/common/layout/overlays/useOverlayComponents';
+1 -1
View File
@@ -20,7 +20,7 @@ import { openFileForAttaching } from '~/common/components/ButtonAttachFiles';
import { optimaOpenPreferences } from '~/common/layout/optima/useOptima';
import { useBrowserTranslationWarning } from '~/common/components/useIsBrowserTranslating';
import { useCapabilityElevenLabs } from '~/common/components/useCapabilities';
import { useChatOverlayStore } from '~/common/chat-overlay/store-chat-overlay';
import { useChatOverlayStore } from '~/common/chat-overlay/store-perchat_vanilla';
import { useScrollToBottom } from '~/common/scroll-to-bottom/useScrollToBottom';
import { ChatMessage, ChatMessageMemo } from './message/ChatMessage';
+1 -1
View File
@@ -9,7 +9,7 @@ import VerticalSplitOutlinedIcon from '@mui/icons-material/VerticalSplitOutlined
import { ScaledTextBlockRenderer } from '~/modules/blocks/ScaledTextBlockRenderer';
import type { DEphemeral } from '~/common/chat-overlay/store-ephemeralsoverlay-slice';
import type { DEphemeral } from '~/common/chat-overlay/store-perchat_ephemerals_slice';
import { ConversationHandler } from '~/common/chat-overlay/ConversationHandler';
import { adjustContentScaling, ContentScaling, lineHeightChatTextMd } from '~/common/app.theme';
import { useUIPreferencesStore } from '~/common/state/store-ui';
@@ -41,8 +41,8 @@ import { lineHeightTextareaMd } from '~/common/app.theme';
import { optimaOpenPreferences } from '~/common/layout/optima/useOptima';
import { platformAwareKeystrokes } from '~/common/components/KeyStroke';
import { supportsScreenCapture } from '~/common/util/screenCaptureUtils';
import { useAppStateStore } from '~/common/state/store-appstate';
import { useChatComposerOverlayStore } from '~/common/chat-overlay/store-chat-overlay';
import { useChatComposerOverlayStore } from '~/common/chat-overlay/store-perchat_vanilla';
import { useComposerStartupText, useLogicSherpaStore } from '~/common/logic/store-logic-sherpa';
import { useDebouncer } from '~/common/components/useDebouncer';
import { useOverlayComponents } from '~/common/layout/overlays/useOverlayComponents';
import { useUICounter, useUIPreferencesStore } from '~/common/state/store-ui';
@@ -75,7 +75,6 @@ import { StatusBar } from '../StatusBar';
import { TokenBadgeMemo } from './tokens/TokenBadge';
import { TokenProgressbarMemo } from './tokens/TokenProgressbar';
import { useComposerDragDrop } from './useComposerDragDrop';
import { useComposerStartupText } from './store-composer';
const zIndexComposerOverlayMic = 10;
@@ -124,7 +123,7 @@ export function Composer(props: {
labsShowCost: state.labsShowCost,
labsShowShortcutBar: state.labsShowShortcutBar,
})));
const timeToShowTips = useAppStateStore(state => state.usageCount >= 5);
const timeToShowTips = useLogicSherpaStore(state => state.usageCount >= 5);
const { novel: explainShiftEnter, touch: touchShiftEnter } = useUICounter('composer-shift-enter');
const { novel: explainAltEnter, touch: touchAltEnter } = useUICounter('composer-alt-enter');
const { novel: explainCtrlEnter, touch: touchCtrlEnter } = useUICounter('composer-ctrl-enter');
@@ -23,7 +23,7 @@ import { showImageDataURLInNewTab } from '~/common/util/imageUtils';
import { useUIPreferencesStore } from '~/common/state/store-ui';
import type { AttachmentDraftId } from '~/common/attachment-drafts/attachment.types';
import type { AttachmentDraftsStoreApi } from '~/common/attachment-drafts/store-attachment-drafts-slice';
import type { AttachmentDraftsStoreApi } from '~/common/attachment-drafts/store-perchat-attachment-drafts_slice';
import type { LLMAttachmentDraft } from './useLLMAttachmentDrafts';
import type { LLMAttachmentDraftsAction } from './LLMAttachmentsList';
@@ -14,7 +14,7 @@ import { ConfirmationModal } from '~/common/components/modals/ConfirmationModal'
import { useOverlayComponents } from '~/common/layout/overlays/useOverlayComponents';
import type { AttachmentDraftId } from '~/common/attachment-drafts/attachment.types';
import type { AttachmentDraftsStoreApi } from '~/common/attachment-drafts/store-attachment-drafts-slice';
import type { AttachmentDraftsStoreApi } from '~/common/attachment-drafts/store-perchat-attachment-drafts_slice';
import type { DMessageImageRefPart } from '~/common/stores/chat/chat.fragments';
import { ViewImageRefPartModal } from '../../message/fragments-content/ViewImageRefPartModal';
@@ -1,33 +0,0 @@
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
/// Composer Store
interface ComposerStore {
startupText: string | null; // if not null, the composer will load this text at startup
setStartupText: (text: string | null) => void;
}
const useComposerStore = create<ComposerStore>()(
persist((set, _get) => ({
startupText: null,
setStartupText: (text: string | null) => set({ startupText: text }),
}),
{
name: 'app-composer',
version: 1,
}),
);
export const setComposerStartupText = (text: string | null) =>
useComposerStore.getState().setStartupText(text);
export const useComposerStartupText = (): [string | null, (text: string | null) => void] => {
const text = useComposerStore(state => state.startupText);
return [text, useComposerStore.getState().setStartupText];
};
@@ -5,7 +5,7 @@ import FolderIcon from '@mui/icons-material/Folder';
import { DConversationId } from '~/common/stores/chat/chat.conversation';
import { OptimaBarDropdownMemo, OptimaDropdownItems } from '~/common/layout/optima/bar/OptimaBarDropdown';
import { useFolderStore } from '~/common/state/store-folders';
import { useFolderStore } from '~/common/stores/folders/store-chat-folders';
export const ClearFolderText = 'No Folder';
@@ -16,7 +16,7 @@ import StarOutlineRoundedIcon from '@mui/icons-material/StarOutlineRounded';
import type { DConversationId } from '~/common/stores/chat/chat.conversation';
import { CloseableMenu } from '~/common/components/CloseableMenu';
import { DFolder, useFolderStore } from '~/common/state/store-folders';
import { DFolder, useFolderStore } from '~/common/stores/folders/store-chat-folders';
import { DebouncedInputMemo } from '~/common/components/DebouncedInput';
import { FoldersToggleOff } from '~/common/components/icons/FoldersToggleOff';
import { FoldersToggleOn } from '~/common/components/icons/FoldersToggleOn';
@@ -17,7 +17,7 @@ import { SystemPurposeId, SystemPurposes } from '../../../../data';
import { autoConversationTitle } from '~/modules/aifn/autotitle/autoTitle';
import type { DConversationId } from '~/common/stores/chat/chat.conversation';
import type { DFolder } from '~/common/state/store-folders';
import type { DFolder } from '~/common/stores/folders/store-chat-folders';
import { ANIM_BUSY_TYPING } from '~/common/util/dMessageUtils';
import { InlineTextarea } from '~/common/components/InlineTextarea';
import { isDeepEqual } from '~/common/util/hooks/useDeep';
@@ -5,7 +5,7 @@ import AddIcon from '@mui/icons-material/Add';
import FolderIcon from '@mui/icons-material/Folder';
import { InlineTextarea } from '~/common/components/InlineTextarea';
import { getRotatingFolderColor, useFolderStore } from '~/common/state/store-folders';
import { getRotatingFolderColor, useFolderStore } from '~/common/stores/folders/store-chat-folders';
export function AddFolderButton() {
@@ -6,7 +6,7 @@ import { List, ListItem, ListItemButton, ListItemDecorator, Sheet } from '@mui/j
import FolderIcon from '@mui/icons-material/Folder';
import { ContentScaling, themeScalingMap } from '~/common/app.theme';
import { DFolder, useFolderStore } from '~/common/state/store-folders';
import { DFolder, useFolderStore } from '~/common/stores/folders/store-chat-folders';
import { StrictModeDroppable } from '~/common/components/StrictModeDroppable';
import { AddFolderButton } from './AddFolderButton';
@@ -10,7 +10,7 @@ import FolderIcon from '@mui/icons-material/Folder';
import MoreVertIcon from '@mui/icons-material/MoreVert';
import { CloseableMenu } from '~/common/components/CloseableMenu';
import { DFolder, FOLDERS_COLOR_PALETTE, useFolderStore } from '~/common/state/store-folders';
import { DFolder, FOLDERS_COLOR_PALETTE, useFolderStore } from '~/common/stores/folders/store-chat-folders';
import { InlineTextarea } from '~/common/components/InlineTextarea';
import { themeZIndexOverMobileDrawer } from '~/common/app.theme';
@@ -1,6 +1,6 @@
import * as React from 'react';
import type { DFolder } from '~/common/state/store-folders';
import type { DFolder } from '~/common/stores/folders/store-chat-folders';
import { DMessage, DMessageUserFlag, MESSAGE_FLAG_STARRED, messageFragmentsReduceText, messageHasUserFlag, messageUserFlagToEmoji } from '~/common/stores/chat/chat.message';
import { conversationTitle, DConversationId } from '~/common/stores/chat/chat.conversation';
import { getLocalMidnightInUTCTimestamp, getTimeBucketEn } from '~/common/util/timeUtils';
+1 -1
View File
@@ -5,7 +5,7 @@ import { useQuery } from '@tanstack/react-query';
import { Box, Button, Card, CardContent, Divider, Input, Typography } from '@mui/joy';
import WarningRoundedIcon from '@mui/icons-material/WarningRounded';
import { forgetChatLinkItem, useSharedChatLinkItems } from '~/modules/trade/link/store-link';
import { forgetChatLinkItem, useSharedChatLinkItems } from '~/modules/trade/link/store-share-link';
import { Brand } from '~/common/app.config';
import { ConfirmationModal } from '~/common/components/modals/ConfirmationModal';
+1 -1
View File
@@ -4,7 +4,7 @@ import TimeAgo from 'react-timeago';
import { Box, ListDivider, ListItem, ListItemButton, ListItemDecorator, Switch, Typography } from '@mui/joy';
import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline';
import type { SharedChatLinkItem } from '~/modules/trade/link/store-link';
import type { SharedChatLinkItem } from '~/modules/trade/link/store-share-link';
import { Link } from '~/common/components/Link';
import { OptimaDrawerHeader } from '~/common/layout/optima/drawer/OptimaDrawerHeader';
-45
View File
@@ -1,49 +1,4 @@
// NOTE: this is a separate file to help with bundle tracing, as it's included by the ProviderBootstrapLogic (i.e. by All pages)
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
import { useAppStateStore } from '~/common/state/store-appstate';
// update this variable every time you want to broadcast a new version to clients
export const incrementalNewsVersion: number = 16.1; // not notifying for 1.16.8
interface NewsState {
lastSeenNewsVersion: number;
}
export const useAppNewsStateStore = create<NewsState>()(
persist(
(set) => ({
lastSeenNewsVersion: 0,
}),
{
name: 'app-news',
},
),
);
export function shallRedirectToNews() {
const { lastSeenNewsVersion } = useAppNewsStateStore.getState();
const { usageCount } = useAppStateStore.getState();
const isNewsOutdated = (lastSeenNewsVersion || 0) < incrementalNewsVersion;
return isNewsOutdated && usageCount > 2;
}
export function markNewsAsSeen() {
useAppNewsStateStore.setState({ lastSeenNewsVersion: incrementalNewsVersion });
}
// NOTE: moved to the ProviderBootstrapLogic, and to the functions above - we used to have hoooks for switching to the news
/*export function useRedirectToNewsOnUpdates() {
React.useEffect(() => {
const { usageCount, lastSeenNewsVersion } = useAppStateStore.getState();
const isNewsOutdated = (lastSeenNewsVersion || 0) < incrementalVersion;
if (isNewsOutdated && usageCount > 2)
return runWhenIdle(navigateToNews, 20000);
}, []);
}*/
+1 -1
View File
@@ -14,7 +14,7 @@ import { GoogleSearchSettings } from '~/modules/google/GoogleSearchSettings';
import { ProdiaSettings } from '~/modules/t2i/prodia/ProdiaSettings';
import { T2ISettings } from '~/modules/t2i/T2ISettings';
import type { PreferencesTabId } from '~/common/layout/optima/store-optima';
import type { PreferencesTabId } from '~/common/layout/optima/store-layout-optima';
import { GoodModal } from '~/common/components/modals/GoodModal';
import { useIsMobile } from '~/common/components/useMatchMedia';
+1 -1
View File
@@ -32,7 +32,7 @@ import SettingsIcon from '@mui/icons-material/Settings';
import { Brand } from '~/common/app.config';
import { ChatBeamIcon } from '~/common/components/icons/ChatBeamIcon';
import { hasNoChatLinkItems } from '~/modules/trade/link/store-link';
import { hasNoChatLinkItems } from '~/modules/trade/link/store-share-link';
// enable to show all items, for layout development
@@ -13,7 +13,7 @@ import { pdfToImageDataURLs, pdfToText } from '~/common/util/pdfUtils';
import { createDMessageDataInlineText, createDocAttachmentFragment, DMessageAttachmentFragment, DMessageDataInline, DMessageDocPart, DVMimeType, isContentOrAttachmentFragment, isDocPart, specialContentPartToDocAttachmentFragment } from '~/common/stores/chat/chat.fragments';
import type { AttachmentCreationOptions, AttachmentDraft, AttachmentDraftConverter, AttachmentDraftId, AttachmentDraftInput, AttachmentDraftSource, AttachmentDraftSourceOriginFile, DraftEgoFragmentsInputData, DraftWebInputData, DraftYouTubeInputData } from './attachment.types';
import type { AttachmentsDraftsStore } from './store-attachment-drafts-slice';
import type { AttachmentsDraftsStore } from './store-perchat-attachment-drafts_slice';
import { attachmentGetLiveFileId, attachmentSourceSupportsLiveFile } from './attachment.livefile';
import { guessInputContentTypeFromMime, heuristicMimeTypeFixup, mimeTypeIsDocX, mimeTypeIsPDF, mimeTypeIsPlainText, mimeTypeIsSupportedImage, reverseLookupMimeType } from './attachment.mimetypes';
import { imageDataToImageAttachmentFragmentViaDBlob } from './attachment.dblobs';
@@ -10,10 +10,10 @@ import type { DConversationId } from '~/common/stores/chat/chat.conversation';
import type { DMessageFragment } from '~/common/stores/chat/chat.fragments';
import type { DMessageId } from '~/common/stores/chat/chat.message';
import { getAllFilesFromDirectoryRecursively, getDataTransferFilesOrPromises } from '~/common/util/fileSystemUtils';
import { useChatAttachmentsStore } from '~/common/chat-overlay/store-chat-overlay';
import { useChatAttachmentsStore } from '~/common/chat-overlay/store-perchat_vanilla';
import type { AttachmentDraftSourceOriginDTO, AttachmentDraftSourceOriginFile } from './attachment.types';
import type { AttachmentDraftsStoreApi } from './store-attachment-drafts-slice';
import type { AttachmentDraftsStoreApi } from './store-perchat-attachment-drafts_slice';
// enable to debug operations
@@ -3,7 +3,7 @@ import { bareBonesPromptMixer } from '~/modules/persona/pmix/pmix';
import { SystemPurposes } from '../../data';
import { gcChatImageAssets } from '../../apps/chat/editors/image-generate';
import { createBeamVanillaStore } from '~/modules/beam/store-beam-vanilla';
import { createBeamVanillaStore } from '~/modules/beam/store-beam_vanilla';
import type { DConversationId } from '~/common/stores/chat/chat.conversation';
import type { DLLMId } from '~/common/stores/llms/llms.types';
@@ -14,8 +14,8 @@ import { getChatLLMId } from '~/common/stores/llms/store-llms';
import { getChatAutoAI } from '../../apps/chat/store-app-chat';
import { createDEphemeral } from './store-ephemeralsoverlay-slice';
import { createPerChatVanillaStore } from './store-chat-overlay';
import { createDEphemeral } from './store-perchat_ephemerals_slice';
import { createPerChatVanillaStore } from './store-perchat_vanilla';
// configuration
@@ -1,9 +1,9 @@
import { StoreApi, useStore } from 'zustand';
import { createStore as createVanillaStore } from 'zustand/vanilla';
import { AttachmentsDraftsStore, createAttachmentDraftsStoreSlice } from '~/common/attachment-drafts/store-attachment-drafts-slice';
import { ComposerOverlayStore, createComposerOverlayStoreSlice } from './store-composeroverlay-slice';
import { createEphemeralsOverlayStoreSlice, EphemeralsOverlayStore } from './store-ephemeralsoverlay-slice';
import { AttachmentsDraftsStore, createAttachmentDraftsStoreSlice } from '~/common/attachment-drafts/store-perchat-attachment-drafts_slice';
import { ComposerOverlayStore, createComposerOverlayStoreSlice } from './store-perchat_composer_slice';
import { createEphemeralsOverlayStoreSlice, EphemeralsOverlayStore } from './store-perchat_ephemerals_slice';
/* Per-chat overlay store, combining all the slices.
@@ -1,7 +1,7 @@
import * as React from 'react';
import { createPortal } from 'react-dom';
import { OptimaPortalId, useOptimaPortalsStore } from './store-optima-portals';
import { OptimaPortalId, useLayoutPortalsStore } from './store-layout-portals';
export function OptimaDrawerIn(props: { children: React.ReactNode }) {
@@ -25,11 +25,11 @@ export function OptimaToolbarIn(props: { children: React.ReactNode }) {
*/
function useOptimaPortalTargetElement(targetPortalId: OptimaPortalId) {
// get the output element
const targetPortalEl = useOptimaPortalsStore(state => state.portals[targetPortalId]?.element ?? null);
const targetPortalEl = useLayoutPortalsStore(state => state.portals[targetPortalId]?.element ?? null);
// increment/decrement input count
React.useEffect(() => {
const { incrementInputs, decrementInputs } = useOptimaPortalsStore.getState();
const { incrementInputs, decrementInputs } = useLayoutPortalsStore.getState();
incrementInputs(targetPortalId);
return () => decrementInputs(targetPortalId);
}, [targetPortalId]);
@@ -30,7 +30,7 @@ interface OptimaPortalActions {
}
export const useOptimaPortalsStore = create<OptimaPortalState & OptimaPortalActions>((_set, _get) => ({
export const useLayoutPortalsStore = create<OptimaPortalState & OptimaPortalActions>((_set, _get) => ({
// init state
portals: {
@@ -1,5 +1,5 @@
import { OptimaPortalId, useOptimaPortalsStore } from './store-optima-portals';
import { OptimaPortalId, useLayoutPortalsStore } from './store-layout-portals';
export function useOptimaPortalHasInputs(portalTargetId: OptimaPortalId) {
return useOptimaPortalsStore(state => state.portals[portalTargetId]?.inputs >= 1);
return useLayoutPortalsStore(state => state.portals[portalTargetId]?.inputs >= 1);
}
@@ -1,6 +1,6 @@
import * as React from 'react';
import { OptimaPortalId, useOptimaPortalsStore } from './store-optima-portals';
import { OptimaPortalId, useLayoutPortalsStore } from './store-layout-portals';
/**
@@ -20,7 +20,7 @@ export function useOptimaPortalOutRef(portalTargetId: OptimaPortalId, debugCalle
const ref = React.useRef<HTMLElement>(null);
React.useLayoutEffect(() => {
const { setElement } = useOptimaPortalsStore.getState();
const { setElement } = useLayoutPortalsStore.getState();
if (!ref.current) {
console.warn(`useOptimaPortalOut: ref.current is null for type ${portalTargetId} (called by ${debugCallerName})`);
} else {
@@ -101,7 +101,7 @@ export interface OptimaActions {
}
export const useOptimaStore = create<OptimaState & OptimaActions>((_set, _get) => ({
export const useLayoutOptimaStore = create<OptimaState & OptimaActions>((_set, _get) => ({
...initialState,
+20 -20
View File
@@ -1,7 +1,7 @@
import * as React from 'react';
import { useShallow } from 'zustand/react/shallow';
import { OptimaActions, PreferencesTabId, useOptimaStore } from './store-optima';
import { OptimaActions, PreferencesTabId, useLayoutOptimaStore } from './store-layout-optima';
// configuration
@@ -16,52 +16,52 @@ export function optimaActions(): Omit<OptimaActions,
| 'closePanel' | 'openPanel' | 'togglePanel'
| 'openPreferences'
> {
return useOptimaStore.getState();
return useLayoutOptimaStore.getState();
}
export function optimaCloseDrawer() {
useOptimaStore.getState().closeDrawer();
useLayoutOptimaStore.getState().closeDrawer();
}
export function optimaOpenDrawer(event?: React.MouseEvent) {
_eatMouseEvent(event);
useOptimaStore.getState().openDrawer();
useLayoutOptimaStore.getState().openDrawer();
}
export function optimaToggleDrawer(event?: React.MouseEvent) {
_eatMouseEvent(event);
useOptimaStore.getState().toggleDrawer();
useLayoutOptimaStore.getState().toggleDrawer();
}
export function optimaCloseAppMenu() {
useOptimaStore.getState().closeAppMenu();
useLayoutOptimaStore.getState().closeAppMenu();
}
export function optimaOpenAppMenu(event?: React.MouseEvent) {
_eatMouseEvent(event);
useOptimaStore.getState().openAppMenu();
useLayoutOptimaStore.getState().openAppMenu();
}
export function optimaClosePanel() {
useOptimaStore.getState().closePanel();
useLayoutOptimaStore.getState().closePanel();
}
export function optimaOpenPanel(event?: React.MouseEvent) {
_eatMouseEvent(event);
useOptimaStore.getState().openPanel();
useLayoutOptimaStore.getState().openPanel();
}
export function optimaTogglePanel(event?: React.MouseEvent) {
_eatMouseEvent(event);
useOptimaStore.getState().togglePanel();
useLayoutOptimaStore.getState().togglePanel();
}
export function optimaOpenModels() {
useOptimaStore.getState().openModels();
useLayoutOptimaStore.getState().openModels();
}
export function optimaOpenPreferences(changeTab?: PreferencesTabId) {
useOptimaStore.getState().openPreferences(changeTab);
useLayoutOptimaStore.getState().openPreferences(changeTab);
}
function _eatMouseEvent(event?: (React.MouseEvent | React.TouchEvent)) {
@@ -75,19 +75,19 @@ function _eatMouseEvent(event?: (React.MouseEvent | React.TouchEvent)) {
/// React to UI State (mainly within the Optima Layout itself)
export function useOptimaDrawerOpen() {
return useOptimaStore(({ drawerIsOpen }) => drawerIsOpen);
return useLayoutOptimaStore(({ drawerIsOpen }) => drawerIsOpen);
}
export function useOptimaAppMenuOpen() {
return useOptimaStore(({ appMenuIsOpen }) => appMenuIsOpen);
return useLayoutOptimaStore(({ appMenuIsOpen }) => appMenuIsOpen);
}
export function useOptimaPanelOpen() {
return useOptimaStore(({ panelIsOpen }) => panelIsOpen);
return useLayoutOptimaStore(({ panelIsOpen }) => panelIsOpen);
}
export function useOptimaModalsState() {
return useOptimaStore(useShallow(state => ({
return useLayoutOptimaStore(useShallow(state => ({
showKeyboardShortcuts: state.showKeyboardShortcuts,
showPreferences: state.showPreferences,
preferencesTab: state.preferencesTab,
@@ -95,7 +95,7 @@ export function useOptimaModalsState() {
}
export function useOptimaModelsModalsState() {
return useOptimaStore(useShallow(state => ({
return useLayoutOptimaStore(useShallow(state => ({
showModelOptions: state.showModelOptions,
showModels: state.showModels,
showPreferences: state.showPreferences,
@@ -109,7 +109,7 @@ export function useOptimaModelsModalsState() {
* Reacts the App Menu component
*/
export function useOptimaAppMenu() {
return useOptimaStore(state => state.menuComponent);
return useLayoutOptimaStore(state => state.menuComponent);
}
/**
@@ -118,12 +118,12 @@ export function useOptimaAppMenu() {
export function useSetOptimaAppMenu(menu: React.ReactNode, debugCallerName: string) {
React.useEffect(() => {
if (DEBUG_OPTIMA_PLUGGING) console.log(' +PLUG layout', debugCallerName);
useOptimaStore.setState({
useLayoutOptimaStore.setState({
menuComponent: menu,
});
return () => {
if (DEBUG_OPTIMA_PLUGGING) console.log(' -UNplug layout', debugCallerName);
useOptimaStore.setState({
useLayoutOptimaStore.setState({
menuComponent: null,
});
};
@@ -1,12 +1,12 @@
import * as React from 'react';
import { useOverlayStore } from './store-overlays';
import { useLayoutOverlaysStore } from './store-layout-overlays';
export const OverlaysInsert: React.FC = () => {
// external state
const overlays = useOverlayStore(state => state.overlays);
const overlays = useLayoutOverlaysStore(state => state.overlays);
// Transient Overlays / Modals
return overlays.map(({ id, component }) => (
@@ -38,7 +38,7 @@ interface OverlayActions {
// removeOverlaysBy: (predicate: (item: OverlayItem) => boolean) => void;
}
export const useOverlayStore = create<OverlayState & OverlayActions>((set, get) => ({
export const useLayoutOverlaysStore = create<OverlayState & OverlayActions>((set, get) => ({
// state
overlays: [],
@@ -1,6 +1,6 @@
import * as React from 'react';
import { GlobalOverlayId, useOverlayStore } from './store-overlays';
import { GlobalOverlayId, useLayoutOverlaysStore } from './store-layout-overlays';
enum OverlayCloseReason {
@@ -65,7 +65,7 @@ export function useOverlayComponents(): {
): Promise<TResolve> => {
return new Promise<TResolve>((pResolve, pReject) => {
const { appendOverlay, overlayExists, overlayToFront, removeOverlay } = useOverlayStore.getState();
const { appendOverlay, overlayExists, overlayToFront, removeOverlay } = useLayoutOverlaysStore.getState();
// Check if the overlay already exists and exit early
// This is like doReject, but doesn't remove the overlay as we don't insert it
+72
View File
@@ -0,0 +1,72 @@
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
import { useShallow } from 'zustand/react/shallow';
import { incrementalNewsVersion } from '../../apps/news/news.version';
// Sherpa State: navigation thought the app, remembers the counters for progressive disclosure of complex features
interface SherpaStore {
usageCount: number;
lastSeenNewsVersion: number;
chatComposerPrefill: string | null; // if not null, the composer will load this text at startup
setChatComposerPrefill: (text: string | null) => void;
}
export const useLogicSherpaStore = create<SherpaStore>()(
persist(
(set) => ({
usageCount: 0,
lastSeenNewsVersion: 0,
chatComposerPrefill: null,
setChatComposerPrefill: (text) => set({ chatComposerPrefill: text }),
}),
{
name: 'app-state',
},
),
);
// increment the usage count
useLogicSherpaStore.setState((state) => ({ usageCount: (state.usageCount || 0) + 1 }));
/// News Navigation
export function shallRedirectToNews() {
const { lastSeenNewsVersion, usageCount } = useLogicSherpaStore.getState();
// first time user - ignore the news up to the next refresh
if (lastSeenNewsVersion === 0) {
markNewsAsSeen();
return false;
}
// if the news is outdated and the user has used the app a few times, show the news
const isNewsOutdated = (lastSeenNewsVersion || 0) < incrementalNewsVersion;
return isNewsOutdated && usageCount >= 3;
}
export function markNewsAsSeen() {
useLogicSherpaStore.setState({ lastSeenNewsVersion: incrementalNewsVersion });
}
// Chat Composer Prefill
export const setComposerStartupText = (text: string | null) => {
useLogicSherpaStore.getState().setChatComposerPrefill(text);
};
export const useComposerStartupText = (): [string | null, (text: string | null) => void] => {
return useLogicSherpaStore(useShallow(state => [state.chatComposerPrefill, state.setChatComposerPrefill]));
};
@@ -3,11 +3,11 @@ import { useRouter } from 'next/router';
import { gcAttachmentDBlobs } from '~/common/attachment-drafts/attachment.dblobs';
import { gcChatImageAssets } from '../../apps/chat/editors/image-generate';
import { markNewsAsSeen, shallRedirectToNews } from '../../apps/news/news.version';
import { autoConfInitiateConfiguration } from '~/common/logic/autoconf';
import { navigateToNews, ROUTE_APP_CHAT } from '~/common/app.routes';
import { autoConfInitiateConfiguration } from '~/common/logic/store-logic-autoconf_vanilla';
import { estimatePersistentStorageOrThrow, requestPersistentStorage } from '~/common/util/storageUtils';
import { markNewsAsSeen, shallRedirectToNews } from '~/common/logic/store-logic-sherpa';
import { navigateToNews, ROUTE_APP_CHAT } from '~/common/app.routes';
import { useNextLoadProgress } from '~/common/components/useNextLoadProgress';
@@ -20,7 +20,7 @@ export function ProviderBootstrapLogic(props: { children: React.ReactNode }) {
useNextLoadProgress(route, events);
// [bootup] logic
// [boot-up] logic
const isOnChat = route === ROUTE_APP_CHAT;
const doRedirectToNews = isOnChat && shallRedirectToNews();
-25
View File
@@ -1,25 +0,0 @@
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
// App State
interface AppStateData {
usageCount: number;
}
export const useAppStateStore = create<AppStateData>()(
persist(
(set) => ({
usageCount: 0,
}),
{
name: 'app-state',
},
),
);
// increment the usage count
useAppStateStore.setState((state) => ({ usageCount: (state.usageCount || 0) + 1 }));
+1 -1
View File
@@ -7,7 +7,7 @@ import { Is } from '~/common/util/pwaUtils';
// UX Labs Experiments
// UxLabsSettings.tsx contains the graduated settings, but the following are not stated:
// - Text Tools: dinamically shown where applicable
// - Text Tools: dynamically shown where applicable
// - Chat Mode: Follow-Ups; moved to Chat Advanced UI
interface UXLabsStore {
+1 -1
View File
@@ -1,6 +1,6 @@
import type { SystemPurposeId } from '../../../data';
import type { DFolder } from '~/common/state/store-folders';
import type { DFolder } from '~/common/stores/folders/store-chat-folders';
import type { LiveFileId } from '~/common/livefile/liveFile.types';
import { liveFileGetAllValidIDs } from '~/common/livefile/store-live-file';
+116 -116
View File
@@ -1,116 +1,116 @@
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
type TaggedListItem<TId extends string, TTag extends string> = {
id: TId;
listTags: TTag[];
}
/**
* Create a persistent list of type TaggedListItem that will handle list functions, and item editing functions.
* - your item can have all the properties you want, but must have an id and listTags property
* - your item will be serialized/de-serialized to/from localStorage, make sure it's JSON-serializable
*/
export function createStoredTaggetList<TItem extends TaggedListItem<string, string>>(persistName: string) {
// Infer TId and TTag from TItem's id and listTags array type
type TId = TItem['id'];
type TTag = TItem['listTags'][number];
type SelectionMap = {
[K in TTag]?: TId;
};
type TaggedListState = {
// State
items: TItem[];
selections: SelectionMap; // Maps TTag to selected TId
// Actions
addItem: (item: TItem) => void;
removeItem: (itemId: TId) => void;
modifyItem: (itemId: TId, changes: Partial<TItem>) => void;
modifyItemDeep: (itemId: TId, updater: (item: TItem) => TItem) => void;
selectItemForTag: (tag: TTag, itemId: TId) => void;
};
return create<TaggedListState>()(persist((set, get) => ({
items: [] as TItem[],
selections: {} as SelectionMap,
addItem: (item: TItem) => set((state: TaggedListState) => ({
items: [...state.items, item],
})),
removeItem: (itemId: TId) => set((state: TaggedListState) => ({
items: state.items.filter((item: TItem) => item.id !== itemId),
selections: Object.fromEntries(
Object.entries(state.selections).filter(([, selectedId]) => selectedId !== itemId),
) as SelectionMap,
})),
modifyItem: (itemId: TId, changes: Partial<TItem>) => set((state: TaggedListState) => ({
items: state.items.map((item: TItem) => item.id === itemId ? { ...item, ...changes } : item),
})),
modifyItemDeep: (itemId: TId, updater: (item: TItem) => TItem) => set((state: TaggedListState) => ({
items: state.items.map((item: TItem) => item.id === itemId ? updater(item) : item),
})),
selectItemForTag: (tag: TTag, itemId: TId) => {
const item = get().items.find((item) => item.id === itemId);
if (item && item.listTags.includes(tag)) {
set((state) => ({
selections: { ...state.selections, [tag]: itemId },
}));
} else {
console.warn(`Item with id ${itemId} does not support tag ${tag} and cannot be selected for it.`);
}
},
}),
{
name: persistName,
}),
);
}
/* Example:
// Define the specific subtype for VoiceOutModel
type VoiceModelId = string;
type VoiceModelTag = 'voice' | 'text' | 'image' | 'video' | 'audio';
// Define the VoiceOutModel interface
export interface VoiceOutModel extends TaggedListItem<VoiceModelId, VoiceModelTag> {
music: string;
count: number;
fruits: string[];
}
// Create the Zustand store with the specific VoiceOutModel type
const useVoiceOutModels = createStoredTaggetList<VoiceOutModel>('app-voice-synth');
export const useVoiceModel = (modelId: VoiceModelId) => {
const { item, modifyItem } = useVoiceOutModels(useShallow(state => ({
item: state.items.find(item => item.id === modelId) as Readonly<VoiceOutModel>,
modifyItem: state.modifyItem,
})));
// Memoize all the update functions at once
const { setMusic, setCount, setFruits } = React.useMemo(() => ({
setMusic: (music: string) => modifyItem(modelId, { music }),
setCount: (count: number) => modifyItem(modelId, { count }),
setFruits: (fruits: string[]) => modifyItem(modelId, { fruits }),
}), [modifyItem, modelId]);
return { item, setMusic, setCount, setFruits };
};
*/
// import { create } from 'zustand';
// import { persist } from 'zustand/middleware';
//
//
// type TaggedListItem<TId extends string, TTag extends string> = {
// id: TId;
// listTags: TTag[];
// }
//
// /**
// * Create a persistent list of type TaggedListItem that will handle list functions, and item editing functions.
// * - your item can have all the properties you want, but must have an id and listTags property
// * - your item will be serialized/de-serialized to/from localStorage, make sure it's JSON-serializable
// */
// export function createStoredTaggedList<TItem extends TaggedListItem<string, string>>(persistName: string) {
//
// // Infer TId and TTag from TItem's id and listTags array type
// type TId = TItem['id'];
// type TTag = TItem['listTags'][number];
//
// type SelectionMap = {
// [K in TTag]?: TId;
// };
//
// type TaggedListState = {
// // State
// items: TItem[];
// selections: SelectionMap; // Maps TTag to selected TId
//
// // Actions
// addItem: (item: TItem) => void;
// removeItem: (itemId: TId) => void;
// modifyItem: (itemId: TId, changes: Partial<TItem>) => void;
// modifyItemDeep: (itemId: TId, updater: (item: TItem) => TItem) => void;
//
// selectItemForTag: (tag: TTag, itemId: TId) => void;
// };
//
// return create<TaggedListState>()(persist((set, get) => ({
//
// items: [] as TItem[],
// selections: {} as SelectionMap,
//
//
// addItem: (item: TItem) => set((state: TaggedListState) => ({
// items: [...state.items, item],
// })),
//
// removeItem: (itemId: TId) => set((state: TaggedListState) => ({
// items: state.items.filter((item: TItem) => item.id !== itemId),
// selections: Object.fromEntries(
// Object.entries(state.selections).filter(([, selectedId]) => selectedId !== itemId),
// ) as SelectionMap,
// })),
//
// modifyItem: (itemId: TId, changes: Partial<TItem>) => set((state: TaggedListState) => ({
// items: state.items.map((item: TItem) => item.id === itemId ? { ...item, ...changes } : item),
// })),
//
// modifyItemDeep: (itemId: TId, updater: (item: TItem) => TItem) => set((state: TaggedListState) => ({
// items: state.items.map((item: TItem) => item.id === itemId ? updater(item) : item),
// })),
//
//
// selectItemForTag: (tag: TTag, itemId: TId) => {
// const item = get().items.find((item) => item.id === itemId);
// if (item && item.listTags.includes(tag)) {
// set((state) => ({
// selections: { ...state.selections, [tag]: itemId },
// }));
// } else {
// console.warn(`Item with id ${itemId} does not support tag ${tag} and cannot be selected for it.`);
// }
// },
//
// }),
//
// {
// name: persistName,
// }),
// );
// }
//
//
// /* Example:
//
// // Define the specific subtype for VoiceOutModel
// type VoiceModelId = string;
// type VoiceModelTag = 'voice' | 'text' | 'image' | 'video' | 'audio';
//
// // Define the VoiceOutModel interface
// export interface VoiceOutModel extends TaggedListItem<VoiceModelId, VoiceModelTag> {
// music: string;
// count: number;
// fruits: string[];
// }
//
// // Create the Zustand store with the specific VoiceOutModel type
// const useVoiceOutModels = createStoredTaggetList<VoiceOutModel>('app-voice-synth');
//
// export const useVoiceModel = (modelId: VoiceModelId) => {
// const { item, modifyItem } = useVoiceOutModels(useShallow(state => ({
// item: state.items.find(item => item.id === modelId) as Readonly<VoiceOutModel>,
// modifyItem: state.modifyItem,
// })));
//
// // Memoize all the update functions at once
// const { setMusic, setCount, setFruits } = React.useMemo(() => ({
// setMusic: (music: string) => modifyItem(modelId, { music }),
// setCount: (count: number) => modifyItem(modelId, { count }),
// setFruits: (fruits: string[]) => modifyItem(modelId, { fruits }),
// }), [modifyItem, modelId]);
//
// return { item, setMusic, setCount, setFruits };
// };
// */
+1 -1
View File
@@ -6,7 +6,7 @@ import type { DMessage } from '~/common/stores/chat/chat.message';
import { agiUuid } from '~/common/util/idUtils';
import { CUSTOM_FACTORY_ID, FFactoryId, findFusionFactory, FUSION_FACTORIES, FUSION_FACTORY_DEFAULT } from './instructions/beam.gather.factories';
import { RootStoreSlice } from '../store-beam-vanilla';
import { RootStoreSlice } from '../store-beam_vanilla';
import { ScatterStoreSlice } from '../scatter/beam.scatter';
import { gatherStartFusion, gatherStopFusion, Instruction } from './instructions/beam.gather.execution';
import { updateBeamLastConfig } from '../store-module-beam';
+1 -1
View File
@@ -9,7 +9,7 @@ import { createPlaceholderMetaFragment } from '~/common/stores/chat/chat.fragmen
import { findLLMOrThrow } from '~/common/stores/llms/store-llms';
import { getUXLabsHighPerformance } from '~/common/state/store-ux-labs';
import type { RootStoreSlice } from '../store-beam-vanilla';
import type { RootStoreSlice } from '../store-beam_vanilla';
import { SCATTER_DEBUG_STATE, SCATTER_PLACEHOLDER } from '../beam.config';
import { updateBeamLastConfig } from '../store-module-beam';
+1 -1
View File
@@ -1,7 +1,7 @@
import * as React from 'react';
import { type StoreApi, useStore } from 'zustand';
import type { BeamStore } from './store-beam-vanilla';
import type { BeamStore } from './store-beam_vanilla';
export type BeamStoreApi = Readonly<StoreApi<BeamStore>>;
+1 -1
View File
@@ -23,7 +23,7 @@ import { getOriginUrl } from '~/common/util/urlUtils';
import { webShare, webSharePresent } from '~/common/util/pwaUtils';
import type { StorageDeleteSchema, StoragePutSchema } from '../server/link';
import { forgetChatLinkItem } from './store-link';
import { forgetChatLinkItem } from './store-share-link';
export function ChatLinkDetails(props: {
+1 -1
View File
@@ -15,7 +15,7 @@ import { getConversation } from '~/common/stores/chat/store-chats';
import type { StoragePutSchema, StorageUpdateDeletionKeySchema } from '../server/link';
import { ChatLinkDetails } from './ChatLinkDetails';
import { rememberChatLinkItem, updateChatLinkDeletionKey, useLinkStorageOwnerId } from './store-link';
import { rememberChatLinkItem, updateChatLinkDeletionKey, useLinkStorageOwnerId } from './store-share-link';
export function ChatLinkExport(props: {
@@ -25,7 +25,7 @@ interface LinkStore {
}
const useLinkStore = create<LinkStore>()(
const useShareLinkStore = create<LinkStore>()(
persist(
(set) => ({
@@ -52,18 +52,18 @@ const useLinkStore = create<LinkStore>()(
// by AppChatLink
export const useSharedChatLinkItems = () => useLinkStore(useShallow(state => state.chatLinkItems));
export const useSharedChatLinkItems = () => useShareLinkStore(useShallow(state => state.chatLinkItems));
// by ChatLinkExport/ChatLinkDetails
export const rememberChatLinkItem = useLinkStore.getState().chatLinkItemAdd;
export const updateChatLinkDeletionKey = useLinkStore.getState().chatLinkItemChangeDeletionKey;
export const forgetChatLinkItem = useLinkStore.getState().chatLinkItemRemove;
export const useLinkStorageOwnerId = () => useLinkStore(useShallow(state => ({
export const rememberChatLinkItem = useShareLinkStore.getState().chatLinkItemAdd;
export const updateChatLinkDeletionKey = useShareLinkStore.getState().chatLinkItemChangeDeletionKey;
export const forgetChatLinkItem = useShareLinkStore.getState().chatLinkItemRemove;
export const useLinkStorageOwnerId = () => useShareLinkStore(useShallow(state => ({
linkStorageOwnerId: state.linkStorageOwnerId,
setLinkStorageOwnerId: state.setLinkStorageOwnerId,
})));
// by Nav
export function hasNoChatLinkItems() {
return !useLinkStore.getState().chatLinkItems.length;
return !useShareLinkStore.getState().chatLinkItems.length;
}
+1 -1
View File
@@ -11,7 +11,7 @@ import { messageFragmentsReduceText } from '~/common/stores/chat/chat.message';
import { prettyShortChatModelName } from '~/common/util/dMessageUtils';
import { prettyTimestampForFilenames } from '~/common/util/timeUtils';
import { useChatStore } from '~/common/stores/chat/store-chats';
import { useFolderStore } from '~/common/state/store-folders';
import { useFolderStore } from '~/common/stores/folders/store-chat-folders';
import type { ImportedOutcome } from './ImportOutcomeModal';