aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMaheshtheDev <[email protected]>2025-11-09 07:29:01 +0000
committerMaheshtheDev <[email protected]>2025-11-09 07:29:01 +0000
commitc1670cfd8540d898d08c12d3cba7fbe354ebbc7c (patch)
treed6bb31d8973914e75440f53fb870bdf1dad77504
parentadd support for responses api in openai typescript sdk (#549) (diff)
downloadsupermemory-c1670cfd8540d898d08c12d3cba7fbe354ebbc7c.tar.xz
supermemory-c1670cfd8540d898d08c12d3cba7fbe354ebbc7c.zip
fix: hydration issue and selected model in chat (#568)11-08-fix_hydration_issue_and_selected_model_in_chat
- Fixes the Hydration issue on the model selector - Add ability to show the Model
-rw-r--r--apps/web/components/model-selector.tsx44
-rw-r--r--apps/web/components/views/chat/chat-messages.tsx24
-rw-r--r--apps/web/lib/models.tsx65
3 files changed, 82 insertions, 51 deletions
diff --git a/apps/web/components/model-selector.tsx b/apps/web/components/model-selector.tsx
index d0e29974..f71e7d58 100644
--- a/apps/web/components/model-selector.tsx
+++ b/apps/web/components/model-selector.tsx
@@ -4,26 +4,7 @@ import { useState } from "react"
import { Button } from "@repo/ui/components/button"
import { ChevronDown } from "lucide-react"
import { motion } from "motion/react"
-
-const models = [
- {
- id: "gpt-5",
- name: "GPT 5",
- description: "OpenAI's latest model",
- },
- {
- id: "claude-sonnet-4.5",
- name: "Claude Sonnet 4.5",
- description: "Anthropic's advanced model",
- },
- {
- id: "gemini-2.5-pro",
- name: "Gemini 2.5 Pro",
- description: "Google's most capable model",
- },
-] as const
-
-type ModelId = (typeof models)[number]["id"]
+import { models, type ModelId, ModelIcon } from "@/lib/models"
interface ModelSelectorProps {
selectedModel?: ModelId
@@ -54,28 +35,7 @@ export function ModelSelector({
onClick={() => !disabled && setIsOpen(!isOpen)}
disabled={disabled}
>
- <svg
- xmlns="http://www.w3.org/2000/svg"
- width="24"
- height="24"
- fill="none"
- viewBox="0 0 24 24"
- >
- <g
- stroke="currentColor"
- strokeLinecap="round"
- strokeLinejoin="round"
- strokeWidth="1.5"
- clipPath="url(#clip0_4418_9868)"
- >
- <path d="m12.92 2.26 6.51 3.51c.76.41.76 1.58 0 1.99l-6.51 3.51c-.58.31-1.26.31-1.84 0L4.57 7.76c-.76-.41-.76-1.58 0-1.99l6.51-3.51c.58-.31 1.26-.31 1.84 0M3.61 10.13l6.05 3.03c.75.38 1.23 1.15 1.23 1.99v5.72c0 .83-.87 1.36-1.61.99l-6.05-3.03A2.24 2.24 0 0 1 2 16.84v-5.72c0-.83.87-1.36 1.61-.99M20.39 10.13l-6.05 3.03c-.75.38-1.23 1.15-1.23 1.99v5.72c0 .83.87 1.36 1.61.99l6.05-3.03c.75-.38 1.23-1.15 1.23-1.99v-5.72c0-.83-.87-1.36-1.61-.99"></path>
- </g>
- <defs>
- <clipPath id="clip0_4418_9868">
- <path fill="#fff" d="M0 0h24v24H0z"></path>
- </clipPath>
- </defs>
- </svg>
+ <ModelIcon width={24} height={24} />
<span className="text-xs font-medium max-w-32 truncate">
{currentModel.name}
</span>
diff --git a/apps/web/components/views/chat/chat-messages.tsx b/apps/web/components/views/chat/chat-messages.tsx
index 22bc8a2a..ff4eca34 100644
--- a/apps/web/components/views/chat/chat-messages.tsx
+++ b/apps/web/components/views/chat/chat-messages.tsx
@@ -19,6 +19,7 @@ import { Streamdown } from "streamdown"
import { TextShimmer } from "@/components/text-shimmer"
import { usePersistentChat, useProject } from "@/stores"
import { useGraphHighlights } from "@/stores/highlights"
+import { modelNames, ModelIcon } from "@/lib/models"
import { Spinner } from "../../spinner"
interface MemoryResult {
@@ -242,14 +243,7 @@ export function ChatMessages() {
const [input, setInput] = useState("")
const [selectedModel, setSelectedModel] = useState<
"gpt-5" | "claude-sonnet-4.5" | "gemini-2.5-pro"
- >(
- (sessionStorage.getItem(storageKey) as
- | "gpt-5"
- | "claude-sonnet-4.5"
- | "gemini-2.5-pro") ||
- "gemini-2.5-pro" ||
- "gemini-2.5-pro",
- )
+ >("gemini-2.5-pro")
const activeChatIdRef = useRef<string | null>(null)
const shouldGenerateTitleRef = useRef<boolean>(false)
const hasRunInitialMessageRef = useRef<boolean>(false)
@@ -293,6 +287,7 @@ export function ChatMessages() {
}, [currentChatId, id])
useEffect(() => {
+ if (typeof window === "undefined") return
if (currentChatId) {
const savedModel = sessionStorage.getItem(storageKey) as
| "gpt-5"
@@ -309,6 +304,7 @@ export function ChatMessages() {
}, [currentChatId, storageKey])
useEffect(() => {
+ if (typeof window === "undefined") return
if (currentChatId && !hasRunInitialMessageRef.current) {
// Check if there's an initial message from the home page in sessionStorage
const storageKey = `chat-initial-${currentChatId}`
@@ -637,7 +633,17 @@ export function ChatMessages() {
className="w-full text-foreground placeholder:text-muted-foreground rounded-md outline-none resize-none text-base leading-relaxed px-3 py-3 bg-transparent"
rows={3}
/>
- <div className="absolute bottom-2 right-2">
+ <div className="absolute bottom-2 right-2 flex items-center gap-4">
+ <div className="flex items-center gap-1.5">
+ <ModelIcon
+ width={16}
+ height={16}
+ className="text-muted-foreground"
+ />
+ <span className="text-xs text-muted-foreground">
+ {modelNames[selectedModel]}
+ </span>
+ </div>
<Button
type="submit"
disabled={!input.trim()}
diff --git a/apps/web/lib/models.tsx b/apps/web/lib/models.tsx
new file mode 100644
index 00000000..06082d5c
--- /dev/null
+++ b/apps/web/lib/models.tsx
@@ -0,0 +1,65 @@
+export const models = [
+ {
+ id: "gpt-5",
+ name: "GPT 5",
+ description: "OpenAI's latest model",
+ },
+ {
+ id: "claude-sonnet-4.5",
+ name: "Claude Sonnet 4.5",
+ description: "Anthropic's advanced model",
+ },
+ {
+ id: "gemini-2.5-pro",
+ name: "Gemini 2.5 Pro",
+ description: "Google's most capable model",
+ },
+] as const
+
+export type ModelId = (typeof models)[number]["id"]
+
+export const modelNames: Record<ModelId, string> = {
+ "gpt-5": "GPT 5",
+ "claude-sonnet-4.5": "Claude Sonnet 4.5",
+ "gemini-2.5-pro": "Gemini 2.5 Pro",
+}
+
+interface ModelIconProps {
+ width?: number
+ height?: number
+ className?: string
+}
+
+export function ModelIcon({
+ width = 24,
+ height = 24,
+ className,
+}: ModelIconProps) {
+ return (
+ <svg
+ xmlns="http://www.w3.org/2000/svg"
+ width={width}
+ height={height}
+ fill="none"
+ viewBox="0 0 24 24"
+ className={className}
+ aria-label="Model icon"
+ >
+ <title>Model icon</title>
+ <g
+ stroke="currentColor"
+ strokeLinecap="round"
+ strokeLinejoin="round"
+ strokeWidth="1.5"
+ clipPath="url(#clip0_4418_9868)"
+ >
+ <path d="m12.92 2.26 6.51 3.51c.76.41.76 1.58 0 1.99l-6.51 3.51c-.58.31-1.26.31-1.84 0L4.57 7.76c-.76-.41-.76-1.58 0-1.99l6.51-3.51c.58-.31 1.26-.31 1.84 0M3.61 10.13l6.05 3.03c.75.38 1.23 1.15 1.23 1.99v5.72c0 .83-.87 1.36-1.61.99l-6.05-3.03A2.24 2.24 0 0 1 2 16.84v-5.72c0-.83.87-1.36 1.61-.99M20.39 10.13l-6.05 3.03c-.75.38-1.23 1.15-1.23 1.99v5.72c0 .83.87 1.36 1.61.99l6.05-3.03c.75-.38 1.23-1.15 1.23-1.99v-5.72c0-.83.87-1.36-1.61-.99" />
+ </g>
+ <defs>
+ <clipPath id="clip0_4418_9868">
+ <path fill="#fff" d="M0 0h24v24H0z" />
+ </clipPath>
+ </defs>
+ </svg>
+ )
+}