feat(desktop): integrate Chat component into desktop app

Add @multica/store and zustand to desktop dependencies. Replace
placeholder chat page with the shared Chat component. Add Toaster
for toast notifications and remove padding on the chat route.
Change Chat root from h-dvh to h-full for container adaptability.
Add showHeader prop to Chat; desktop passes showHeader={false}
since it has its own layout header.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Naiyuan Qing 2026-02-03 20:07:16 +08:00
parent a088875118
commit ff80cf0732
5 changed files with 51 additions and 89 deletions

View file

@ -13,13 +13,15 @@
"@hugeicons/core-free-icons": "^3.1.1",
"@hugeicons/react": "^1.1.4",
"@multica/sdk": "workspace:*",
"@multica/store": "workspace:*",
"@multica/ui": "workspace:*",
"qrcode.react": "^4.2.0",
"react": "catalog:",
"react-dom": "catalog:",
"react-router-dom": "^7.13.0",
"socket.io-client": "^4.8.3",
"uuid": "^13.0.0"
"uuid": "^13.0.0",
"zustand": "catalog:"
},
"devDependencies": {
"@tailwindcss/vite": "^4.1.18",

View file

@ -1,15 +1,5 @@
import { useNavigate } from 'react-router-dom'
import { Button } from '@multica/ui/components/ui/button'
import { Chat } from '@multica/ui/components/chat'
export default function ChatPage() {
const navigate = useNavigate()
return (
<div className="flex h-screen flex-col items-center justify-center gap-4">
<h1 className="text-2xl font-bold">Chat</h1>
<Button variant="outline" onClick={() => navigate('/')}>
Back to Home
</Button>
</div>
)
return <Chat showHeader={false} />
}

View file

@ -1,4 +1,5 @@
import { Outlet, NavLink, useLocation } from 'react-router-dom'
import { Toaster } from '@multica/ui/components/ui/sonner'
import { Button } from '@multica/ui/components/ui/button'
import { HugeiconsIcon } from '@hugeicons/react'
import {
@ -55,9 +56,10 @@ export default function Layout() {
</nav>
{/* Content */}
<main className="flex-1 overflow-auto p-4">
<main className={cn('flex-1 overflow-auto', location.pathname === '/chat' ? '' : 'p-4')}>
<Outlet />
</main>
<Toaster />
</div>
)
}

View file

@ -21,7 +21,11 @@ import { useScrollFade } from "@multica/ui/hooks/use-scroll-fade";
import { useAutoScroll } from "@multica/ui/hooks/use-auto-scroll";
import { cn } from "@multica/ui/lib/utils";
export function Chat() {
interface ChatProps {
showHeader?: boolean;
}
export function Chat({ showHeader = true }: ChatProps) {
useHubInit()
const deviceId = useDeviceId()
const activeAgentId = useHubStore((s) => s.activeAgentId)
@ -67,30 +71,32 @@ export function Chat() {
useAutoScroll(mainRef)
return (
<div className="h-dvh flex flex-col overflow-hidden w-full">
<div className="h-full flex flex-col overflow-hidden w-full">
{/* Header */}
<header>
<div className="flex items-center justify-between px-4 py-2 max-w-4xl mx-auto">
<div className="flex items-center gap-2.5">
<img src="/icon.png" alt="Multica" className="size-6 rounded-md" />
<span className="text-sm tracking-wide font-[family-name:var(--font-brand)]">
Multica
</span>
{showHeader && (
<header>
<div className="flex items-center justify-between px-4 py-2 max-w-4xl mx-auto">
<div className="flex items-center gap-2.5">
<img src="/icon.png" alt="Multica" className="size-6 rounded-md" />
<span className="text-sm tracking-wide font-[family-name:var(--font-brand)]">
Multica
</span>
</div>
<div className="flex items-center gap-1">
{isConnected && (
<Button
variant="ghost"
size="sm"
onClick={handleDisconnect}
className="text-xs text-muted-foreground"
>
Disconnect
</Button>
)}
</div>
</div>
<div className="flex items-center gap-1">
{isConnected && (
<Button
variant="ghost"
size="sm"
onClick={handleDisconnect}
className="text-xs text-muted-foreground"
>
Disconnect
</Button>
)}
</div>
</div>
</header>
</header>
)}
{/* Main */}
<main ref={mainRef} className="flex-1 overflow-y-auto min-h-0" style={fadeStyle}>

66
pnpm-lock.yaml generated
View file

@ -156,6 +156,9 @@ importers:
'@multica/sdk':
specifier: workspace:*
version: link:../../packages/sdk
'@multica/store':
specifier: workspace:*
version: link:../../packages/store
'@multica/ui':
specifier: workspace:*
version: link:../../packages/ui
@ -177,6 +180,9 @@ importers:
uuid:
specifier: ^13.0.0
version: 13.0.0
zustand:
specifier: 'catalog:'
version: 5.0.10(@types/react@19.1.17)(react@19.2.3)(use-sync-external-store@1.6.0(react@19.2.3))
devDependencies:
'@tailwindcss/vite':
specifier: ^4.1.18
@ -14307,7 +14313,7 @@ snapshots:
eslint: 9.39.2(jiti@2.6.1)
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1))
eslint-plugin-expo: 1.0.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1))
eslint-plugin-react: 7.37.5(eslint@9.39.2(jiti@2.6.1))
eslint-plugin-react-hooks: 5.2.0(eslint@9.39.2(jiti@2.6.1))
globals: 16.4.0
@ -14322,8 +14328,8 @@ snapshots:
'@next/eslint-plugin-next': 16.1.6
eslint: 9.39.2(jiti@2.6.1)
eslint-import-resolver-node: 0.3.9
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1))
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1))
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1))
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1))
eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.2(jiti@2.6.1))
eslint-plugin-react: 7.37.5(eslint@9.39.2(jiti@2.6.1))
eslint-plugin-react-hooks: 7.0.1(eslint@9.39.2(jiti@2.6.1))
@ -14345,21 +14351,6 @@ snapshots:
transitivePeerDependencies:
- supports-color
eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)):
dependencies:
'@nolyfill/is-core-module': 1.0.39
debug: 4.4.3
eslint: 9.39.2(jiti@2.6.1)
get-tsconfig: 4.13.0
is-bun-module: 2.0.0
stable-hash: 0.0.5
tinyglobby: 0.2.15
unrs-resolver: 1.11.1
optionalDependencies:
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1))
transitivePeerDependencies:
- supports-color
eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)):
dependencies:
'@nolyfill/is-core-module': 1.0.39
@ -14371,18 +14362,18 @@ snapshots:
tinyglobby: 0.2.15
unrs-resolver: 1.11.1
optionalDependencies:
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1))
transitivePeerDependencies:
- supports-color
eslint-module-utils@2.12.1(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)):
eslint-module-utils@2.12.1(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)):
dependencies:
debug: 3.2.7
optionalDependencies:
'@typescript-eslint/parser': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
eslint: 9.39.2(jiti@2.6.1)
eslint-import-resolver-node: 0.3.9
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1))
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1))
transitivePeerDependencies:
- supports-color
@ -14395,7 +14386,7 @@ snapshots:
- supports-color
- typescript
eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)):
eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)):
dependencies:
'@rtsao/scc': 1.1.0
array-includes: 3.1.9
@ -14406,36 +14397,7 @@ snapshots:
doctrine: 2.1.0
eslint: 9.39.2(jiti@2.6.1)
eslint-import-resolver-node: 0.3.9
eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1))
hasown: 2.0.2
is-core-module: 2.16.1
is-glob: 4.0.3
minimatch: 3.1.2
object.fromentries: 2.0.8
object.groupby: 1.0.3
object.values: 1.2.1
semver: 6.3.1
string.prototype.trimend: 1.0.9
tsconfig-paths: 3.15.0
optionalDependencies:
'@typescript-eslint/parser': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
transitivePeerDependencies:
- eslint-import-resolver-typescript
- eslint-import-resolver-webpack
- supports-color
eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)):
dependencies:
'@rtsao/scc': 1.1.0
array-includes: 3.1.9
array.prototype.findlastindex: 1.2.6
array.prototype.flat: 1.3.3
array.prototype.flatmap: 1.3.3
debug: 3.2.7
doctrine: 2.1.0
eslint: 9.39.2(jiti@2.6.1)
eslint-import-resolver-node: 0.3.9
eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1))
eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1))
hasown: 2.0.2
is-core-module: 2.16.1
is-glob: 4.0.3