diff --git a/apps/desktop/layer/renderer/src/modules/ai-chat/components/layouts/ChatInterface.tsx b/apps/desktop/layer/renderer/src/modules/ai-chat/components/layouts/ChatInterface.tsx index 147cf80e0..e5b3a6eb0 100644 --- a/apps/desktop/layer/renderer/src/modules/ai-chat/components/layouts/ChatInterface.tsx +++ b/apps/desktop/layer/renderer/src/modules/ai-chat/components/layouts/ChatInterface.tsx @@ -45,6 +45,7 @@ import { generateAndUpdateChatTitle } from "../../utils/titleGeneration" import { GlobalFileDropZone } from "../file/GlobalFileDropZone" import { AIErrorFallback } from "./AIErrorFallback" import { ChatInput } from "./ChatInput" +import { ChatShortcutsRow } from "./ChatShortcutsRow" import { CollapsibleError } from "./CollapsibleError" import { WelcomeScreen } from "./WelcomeScreen" @@ -311,7 +312,7 @@ const ChatInterfaceContent = ({ centerInputOnEmpty }: ChatInterfaceProps) => {
{!hasMessages && !isLoadingHistory ? ( - + ) : ( <> { )} > {error && } + handleSendMessage(prompt)} /> void +} + +export const ChatShortcutsRow: React.FC = ({ onSelect }) => { + const { t } = useTranslation("ai") + const aiSettings = useAISettingValue() + const mainEntryId = useMainEntryId() + const showSettings = useSettingModal() + + const shortcutsToDisplay = useMemo(() => { + const shortcuts = aiSettings.shortcuts ?? [] + const list: typeof shortcuts = [] + const entry: typeof shortcuts = [] + + for (const shortcut of shortcuts) { + if (!shortcut.enabled) continue + const targets = + shortcut.displayTargets && shortcut.displayTargets.length > 0 + ? shortcut.displayTargets + : DEFAULT_SHORTCUT_TARGETS + if (targets.includes("list")) { + list.push(shortcut) + } + if (targets.includes("entry")) { + entry.push(shortcut) + } + } + + return (mainEntryId ? entry : list) ?? [] + }, [aiSettings.shortcuts, mainEntryId]) + + const handleCustomize = useCallback(() => { + showSettings("ai") + }, [showSettings]) + + return ( +
+
+ + + + {shortcutsToDisplay.map((shortcut) => ( + onSelect(shortcut.prompt)} + animationDelay={0} + size="sm" + title={shortcut.hotkey ? `${shortcut.name} (${shortcut.hotkey})` : shortcut.name} + > + {shortcut.name} + + ))} +
+
+ ) +} diff --git a/apps/desktop/layer/renderer/src/modules/ai-chat/components/layouts/WelcomeScreen.tsx b/apps/desktop/layer/renderer/src/modules/ai-chat/components/layouts/WelcomeScreen.tsx index 7bb9001f9..7002effb6 100644 --- a/apps/desktop/layer/renderer/src/modules/ai-chat/components/layouts/WelcomeScreen.tsx +++ b/apps/desktop/layer/renderer/src/modules/ai-chat/components/layouts/WelcomeScreen.tsx @@ -1,14 +1,11 @@ import { Folo } from "@follow/components/icons/folo.js" import { ScrollArea } from "@follow/components/ui/scroll-area/ScrollArea.js" import { useElementWidth } from "@follow/hooks" -import { DEFAULT_SHORTCUT_TARGETS } from "@follow/shared/settings/interface" import { clsx } from "@follow/utils" -import type { EditorState } from "lexical" import { AnimatePresence, m } from "motion/react" -import { useEffect, useMemo, useRef } from "react" +import { useEffect, useRef } from "react" import { useTranslation } from "react-i18next" -import { useAISettingValue } from "~/atoms/settings/ai" import { AISpline } from "~/modules/ai-chat/components/3d-models/AISpline" import { useAttachScrollBeyond } from "../../hooks/useAttachScrollBeyond" @@ -20,38 +17,16 @@ import { DefaultWelcomeContent, EntryWelcomeContent } from "../welcome" import { AIChatRoot } from "./AIChatRoot" interface WelcomeScreenProps { - onSend: (message: EditorState | string) => void centerInputOnEmpty?: boolean } -export const WelcomeScreen = ({ onSend, centerInputOnEmpty }: WelcomeScreenProps) => { +export const WelcomeScreen = ({ centerInputOnEmpty }: WelcomeScreenProps) => { const { t } = useTranslation("ai") - const aiSettings = useAISettingValue() const mainEntryId = useMainEntryId() const { todayTimelineSummaryId, canReuseTimelineSummary, hasEntryContext } = useTimelineSummarySession() - const enabledShortcuts = aiSettings.shortcuts || [] const shouldFetchTimelineSummary = canReuseTimelineSummary - const { list: listShortcuts, entry: entryShortcuts } = useMemo(() => { - const list: typeof enabledShortcuts = [] - const entry: typeof enabledShortcuts = [] - for (const shortcut of enabledShortcuts) { - if (!shortcut.enabled) continue - const targets = - shortcut.displayTargets && shortcut.displayTargets.length > 0 - ? shortcut.displayTargets - : DEFAULT_SHORTCUT_TARGETS - if (targets.includes("list")) { - list.push(shortcut) - } - if (targets.includes("entry")) { - entry.push(shortcut) - } - } - return { list, entry } - }, [enabledShortcuts]) - const { handleScroll } = useAttachScrollBeyond() const showTimelineSummary = shouldFetchTimelineSummary @@ -87,18 +62,9 @@ export const WelcomeScreen = ({ onSend, centerInputOnEmpty }: WelcomeScreenProps > {hasEntryContext && mainEntryId ? ( - + ) : ( - + )}
diff --git a/apps/desktop/layer/renderer/src/modules/ai-chat/components/welcome/DefaultWelcomeContent.tsx b/apps/desktop/layer/renderer/src/modules/ai-chat/components/welcome/DefaultWelcomeContent.tsx index 3232131e6..890bfaa7b 100644 --- a/apps/desktop/layer/renderer/src/modules/ai-chat/components/welcome/DefaultWelcomeContent.tsx +++ b/apps/desktop/layer/renderer/src/modules/ai-chat/components/welcome/DefaultWelcomeContent.tsx @@ -1,38 +1 @@ -import type { AIShortcut } from "@follow/shared/settings/interface" -import type { EditorState } from "lexical" -import { m } from "motion/react" -import { useTranslation } from "react-i18next" - -import { useSettingModal } from "~/modules/settings/modal/use-setting-modal-hack" - -import { ShortcutButtonGrid } from "./ShortcutButtonGrid" - -interface DefaultWelcomeContentProps { - onSend: (message: EditorState | string) => void - shortcuts: AIShortcut[] -} - -export const DefaultWelcomeContent: React.FC = ({ - onSend, - shortcuts, -}) => { - const { t } = useTranslation("ai") - const showSettings = useSettingModal() - - return ( - - showSettings("ai")} - customizeLabel={t("customize_shortcuts")} - customizeTitle={t("customize_shortcuts_title")} - /> - - ) -} +export const DefaultWelcomeContent: React.FC = () => null diff --git a/apps/desktop/layer/renderer/src/modules/ai-chat/components/welcome/EntryWelcomeContent.tsx b/apps/desktop/layer/renderer/src/modules/ai-chat/components/welcome/EntryWelcomeContent.tsx index c4fa6ab4b..a83a0be3c 100644 --- a/apps/desktop/layer/renderer/src/modules/ai-chat/components/welcome/EntryWelcomeContent.tsx +++ b/apps/desktop/layer/renderer/src/modules/ai-chat/components/welcome/EntryWelcomeContent.tsx @@ -1,44 +1,20 @@ -import type { AIShortcut } from "@follow/shared/settings/interface" -import type { EditorState } from "lexical" import { m } from "motion/react" -import { useTranslation } from "react-i18next" - -import { useSettingModal } from "~/modules/settings/modal/use-setting-modal-hack" import { EntrySummaryCard } from "./EntrySummaryCard" -import { ShortcutButtonGrid } from "./ShortcutButtonGrid" interface EntryWelcomeContentProps { entryId: string - onSend: (message: EditorState | string) => void - shortcuts: AIShortcut[] } -export const EntryWelcomeContent: React.FC = ({ - entryId, - onSend, - shortcuts, -}) => { - const { t } = useTranslation("ai") - const showSettings = useSettingModal() - - return ( -
+export const EntryWelcomeContent: React.FC = ({ entryId }) => ( +
+ - - showSettings("ai")} - customizeLabel={t("customize_shortcuts")} - customizeTitle={t("customize_shortcuts_title")} - /> - -
- ) -} + +
+) diff --git a/apps/desktop/layer/renderer/src/modules/ai-chat/components/welcome/ShortcutButtonGrid.tsx b/apps/desktop/layer/renderer/src/modules/ai-chat/components/welcome/ShortcutButtonGrid.tsx deleted file mode 100644 index 37e9ac9d3..000000000 --- a/apps/desktop/layer/renderer/src/modules/ai-chat/components/welcome/ShortcutButtonGrid.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import { AIShortcutButton } from "@follow/components/ui/ai-shortcut-button/index.js" -import type { AIShortcut } from "@follow/shared/settings/interface" -import type { EditorState } from "lexical" -import { useMemo } from "react" - -interface ShortcutButtonGridProps { - shortcuts: AIShortcut[] - onSend: (message: EditorState | string) => void - onCustomize: () => void - customizeLabel: string - customizeTitle: string - className?: string - limit?: number -} - -const baseClassName = "relative flex flex-wrap items-center justify-center gap-2" - -export const ShortcutButtonGrid: React.FC = ({ - shortcuts, - onSend, - onCustomize, - customizeLabel, - customizeTitle, - className, - limit = 6, -}) => { - const enabledShortcuts = useMemo( - () => shortcuts.filter((shortcut) => shortcut.enabled), - [shortcuts], - ) - const displayShortcuts = useMemo( - () => enabledShortcuts.slice(0, limit), - [enabledShortcuts, limit], - ) - const wrapperClassName = className ? `${baseClassName} ${className}` : baseClassName - - return ( -
- {displayShortcuts.map((shortcut, index) => ( - onSend(shortcut.prompt)} - animationDelay={index * 0.1} - title={shortcut.hotkey ? `${shortcut.name} (${shortcut.hotkey})` : shortcut.name} - > - {shortcut.name} - - ))} - - {enabledShortcuts.length === 0 && ( - - {customizeLabel} - - )} -
- ) -}