| import React from 'react' | import React from 'react' | ||||
| import type { FC } from 'react' | import type { FC } from 'react' | ||||
| import type { Metadata } from 'next' | import type { Metadata } from 'next' | ||||
| import { SharePageContextProvider } from '@/context/share-page-context' | |||||
| export const metadata: Metadata = { | export const metadata: Metadata = { | ||||
| icons: 'data:,', // prevent browser from using default favicon | icons: 'data:,', // prevent browser from using default favicon | ||||
| }> = ({ children }) => { | }> = ({ children }) => { | ||||
| return ( | return ( | ||||
| <div className="min-w-[300px] h-full pb-[env(safe-area-inset-bottom)]"> | <div className="min-w-[300px] h-full pb-[env(safe-area-inset-bottom)]"> | ||||
| <SharePageContextProvider> | |||||
| {children} | |||||
| </SharePageContextProvider> | |||||
| {children} | |||||
| </div> | </div> | ||||
| ) | ) | ||||
| } | } |
| RiPlayLargeFill, | RiPlayLargeFill, | ||||
| } from '@remixicon/react' | } from '@remixicon/react' | ||||
| import Toast from '@/app/components/base/toast' | import Toast from '@/app/components/base/toast' | ||||
| import { useAppContext } from '@/context/app-context' | |||||
| import useTheme from '@/hooks/use-theme' | |||||
| import { Theme } from '@/types/app' | import { Theme } from '@/types/app' | ||||
| import cn from '@/utils/classnames' | import cn from '@/utils/classnames' | ||||
| const [hasStartedPlaying, setHasStartedPlaying] = useState(false) | const [hasStartedPlaying, setHasStartedPlaying] = useState(false) | ||||
| const [hoverTime, setHoverTime] = useState(0) | const [hoverTime, setHoverTime] = useState(0) | ||||
| const [isAudioAvailable, setIsAudioAvailable] = useState(true) | const [isAudioAvailable, setIsAudioAvailable] = useState(true) | ||||
| const { theme } = useAppContext() | |||||
| const { theme } = useTheme() | |||||
| useEffect(() => { | useEffect(() => { | ||||
| const audio = audioRef.current | const audio = audioRef.current |
| isResponding={isResponding} | isResponding={isResponding} | ||||
| chatContainerInnerClassName={`mx-auto pt-6 w-full max-w-[720px] ${isMobile && 'px-4'}`} | chatContainerInnerClassName={`mx-auto pt-6 w-full max-w-[720px] ${isMobile && 'px-4'}`} | ||||
| chatFooterClassName='pb-4' | chatFooterClassName='pb-4' | ||||
| chatFooterInnerClassName={`mx-auto w-full max-w-[720px] ${isMobile && 'px-4'}`} | |||||
| chatFooterInnerClassName={`mx-auto w-full max-w-[720px] ${isMobile ? 'px-2' : 'px-4'}`} | |||||
| onSend={doSend} | onSend={doSend} | ||||
| inputs={currentConversationId ? currentConversationItem?.inputs as any : newConversationInputs} | inputs={currentConversationId ? currentConversationItem?.inputs as any : newConversationInputs} | ||||
| inputsForm={inputsForms} | inputsForm={inputsForms} |
| private buildChecker = false | private buildChecker = false | ||||
| public get theme() { | public get theme() { | ||||
| if (this._theme === undefined) | |||||
| throw new Error('The theme should be built first and then accessed') | |||||
| else | |||||
| if (this._theme === undefined) { | |||||
| this._theme = new Theme() | |||||
| return this._theme | return this._theme | ||||
| } | |||||
| else { | |||||
| return this._theme | |||||
| } | |||||
| } | } | ||||
| public buildTheme(chatColorTheme: string | null = null, chatColorThemeInverted = false) { | public buildTheme(chatColorTheme: string | null = null, chatColorThemeInverted = false) { |
| import MarkdownForm from '@/app/components/base/markdown-blocks/form' | import MarkdownForm from '@/app/components/base/markdown-blocks/form' | ||||
| import ThinkBlock from '@/app/components/base/markdown-blocks/think-block' | import ThinkBlock from '@/app/components/base/markdown-blocks/think-block' | ||||
| import { Theme } from '@/types/app' | import { Theme } from '@/types/app' | ||||
| import { useAppContext } from '@/context/app-context' | |||||
| import useTheme from '@/hooks/use-theme' | |||||
| import cn from '@/utils/classnames' | import cn from '@/utils/classnames' | ||||
| // Available language https://github.com/react-syntax-highlighter/react-syntax-highlighter/blob/master/AVAILABLE_LANGUAGES_HLJS.MD | // Available language https://github.com/react-syntax-highlighter/react-syntax-highlighter/blob/master/AVAILABLE_LANGUAGES_HLJS.MD | ||||
| // or use the non-minified dev environment for full errors and additional helpful warnings. | // or use the non-minified dev environment for full errors and additional helpful warnings. | ||||
| const CodeBlock: any = memo(({ inline, className, children, ...props }: any) => { | const CodeBlock: any = memo(({ inline, className, children, ...props }: any) => { | ||||
| const { theme } = useAppContext() | |||||
| const { theme } = useTheme() | |||||
| const [isSVG, setIsSVG] = useState(true) | const [isSVG, setIsSVG] = useState(true) | ||||
| const match = /language-(\w+)/.exec(className || '') | const match = /language-(\w+)/.exec(className || '') | ||||
| const language = match?.[1] | const language = match?.[1] |
| 'use client' | |||||
| import { useCallback, useEffect, useState } from 'react' | |||||
| import { createContext, useContextSelector } from 'use-context-selector' | |||||
| import type { FC, ReactNode } from 'react' | |||||
| import { Theme } from '@/types/app' | |||||
| export type SharePageContextValue = { | |||||
| theme: Theme | |||||
| setTheme: (theme: Theme) => void | |||||
| } | |||||
| const SharePageContext = createContext<SharePageContextValue>({ | |||||
| theme: Theme.light, | |||||
| setTheme: () => { }, | |||||
| }) | |||||
| export function useSelector<T>(selector: (value: SharePageContextValue) => T): T { | |||||
| return useContextSelector(SharePageContext, selector) | |||||
| } | |||||
| export type SharePageContextProviderProps = { | |||||
| children: ReactNode | |||||
| } | |||||
| export const SharePageContextProvider: FC<SharePageContextProviderProps> = ({ children }) => { | |||||
| const [theme, setTheme] = useState<Theme>(Theme.light) | |||||
| const handleSetTheme = useCallback((theme: Theme) => { | |||||
| setTheme(theme) | |||||
| globalThis.document.documentElement.setAttribute('data-theme', theme) | |||||
| }, []) | |||||
| useEffect(() => { | |||||
| globalThis.document.documentElement.setAttribute('data-theme', theme) | |||||
| // eslint-disable-next-line react-hooks/exhaustive-deps | |||||
| }, []) | |||||
| return ( | |||||
| <SharePageContext.Provider value={{ | |||||
| theme, | |||||
| setTheme: handleSetTheme, | |||||
| }}> | |||||
| {children} | |||||
| </SharePageContext.Provider> | |||||
| ) | |||||
| } | |||||
| export default SharePageContextProvider |