@geminilight/mindos 0.6.58 → 0.6.59
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/_standalone/.mindos-build-version +1 -1
- package/_standalone/.next/BUILD_ID +1 -1
- package/_standalone/.next/app-path-routes-manifest.json +24 -24
- package/_standalone/.next/build-manifest.json +3 -3
- package/_standalone/.next/cache/.previewinfo +1 -1
- package/_standalone/.next/cache/.rscinfo +1 -1
- package/_standalone/.next/cache/config.json +3 -3
- package/_standalone/.next/prerender-manifest.json +3 -3
- package/_standalone/.next/react-loadable-manifest.json +1 -1
- package/_standalone/.next/server/app/.well-known/agent-card.json/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/_global-error.html +2 -2
- package/_standalone/.next/server/app/_global-error.rsc +1 -1
- package/_standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/_standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/_standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/_standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/_standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/_standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/_standalone/.next/server/app/_not-found/page.js +1 -1
- package/_standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/agents/[agentKey]/page.js +1 -1
- package/_standalone/.next/server/app/agents/[agentKey]/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/agents/[agentKey]/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/agents/page.js +2 -2
- package/_standalone/.next/server/app/agents/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/agents/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/a2a/agents/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/a2a/delegations/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/a2a/discover/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/a2a/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/acp/config/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/acp/detect/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/acp/install/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/acp/registry/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/acp/session/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/agent-activity/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/agents/custom/detect/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/agents/custom/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/ask/route.js +3 -3
- package/_standalone/.next/server/app/api/ask/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/ask-sessions/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/auth/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/backlinks/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/bootstrap/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/changes/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/export/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/extract-pdf/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/file/import/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/file/raw/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/file/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/graph/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/health/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/inbox/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/init/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/agents/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/install/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/install-skill/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/restart/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/status/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/uninstall/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/monitoring/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/recent-files/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/restart/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/search/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/settings/list-models/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/settings/reset-token/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/settings/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/settings/test-key/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/setup/check-path/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/setup/check-port/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/setup/generate-token/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/setup/ls/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/setup/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/sync/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/tree-version/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/uninstall/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/update-check/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/update-status/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/workflows/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/changes/page.js +2 -2
- package/_standalone/.next/server/app/changes/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/changes/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/echo/[segment]/page.js +3 -3
- package/_standalone/.next/server/app/echo/[segment]/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/echo/[segment]/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/echo/page.js +1 -1
- package/_standalone/.next/server/app/echo/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/echo/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/explore/page.js +2 -2
- package/_standalone/.next/server/app/explore/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/explore/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/help/page.js +2 -2
- package/_standalone/.next/server/app/help/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/help/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/inbox/history/page.js +1 -1
- package/_standalone/.next/server/app/inbox/history/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/inbox/history/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/login/page.js +2 -2
- package/_standalone/.next/server/app/login/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/page.js +2 -8
- package/_standalone/.next/server/app/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/setup/page.js +2 -2
- package/_standalone/.next/server/app/setup/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/setup/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/trash/page.js +3 -3
- package/_standalone/.next/server/app/trash/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/trash/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/view/[...path]/page.js +3 -3
- package/_standalone/.next/server/app/view/[...path]/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/view/[...path]/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/wiki/page.js +2 -2
- package/_standalone/.next/server/app/wiki/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/wiki/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app-paths-manifest.json +24 -24
- package/_standalone/.next/server/chunks/3484.js +1 -1
- package/_standalone/.next/server/chunks/530.js +65 -66
- package/_standalone/.next/server/chunks/{6793.js → 8343.js} +2 -2
- package/_standalone/.next/server/chunks/9787.js +2 -0
- package/_standalone/.next/server/middleware-build-manifest.js +1 -1
- package/_standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/_standalone/.next/server/pages/500.html +2 -2
- package/_standalone/.next/server/server-reference-manifest.js +1 -1
- package/_standalone/.next/server/server-reference-manifest.json +1 -1
- package/_standalone/.next/static/chunks/{1814.a7c127b2c73d1f70.js → 1814.a79b84d37df75c43.js} +1 -1
- package/_standalone/.next/static/chunks/3427-2e61a5df1f5e55fb.js +1 -0
- package/_standalone/.next/static/chunks/{1053-fe009233cff06e72.js → 5581-dac72e9f16e5ea29.js} +3 -3
- package/_standalone/.next/static/chunks/6297-085daa21037d5f81.js +1 -0
- package/_standalone/.next/static/chunks/{7249-fa98ca10e9a10f39.js → 7249-6cf8f2b78718c59e.js} +1 -1
- package/_standalone/.next/static/chunks/8520-56ec9ff087c15204.js +22 -0
- package/_standalone/.next/static/chunks/9905-a19d379cb225246e.js +1 -0
- package/_standalone/.next/static/chunks/app/agents/[agentKey]/{page-7bdeab5af8e4f5f2.js → page-35ea6de1af2be3b5.js} +1 -1
- package/_standalone/.next/static/chunks/app/agents/page-b172ea3743adb047.js +1 -0
- package/_standalone/.next/static/chunks/app/changes/{page-5a72144d1080a699.js → page-6d2f49651c0061f7.js} +1 -1
- package/_standalone/.next/static/chunks/app/echo/[segment]/page-84b95256f6e38aae.js +11 -0
- package/_standalone/.next/static/chunks/app/explore/{page-d3d99308146c2240.js → page-d9f58000bc445360.js} +2 -2
- package/_standalone/.next/static/chunks/app/help/{page-222df603080b5fab.js → page-f8cb806371b3175f.js} +1 -1
- package/_standalone/.next/static/chunks/app/inbox/history/{page-07819cf95cb0805f.js → page-26e71fb6f716a4c4.js} +1 -1
- package/_standalone/.next/static/chunks/app/layout-b89b0d955f39a753.js +164 -0
- package/_standalone/.next/static/chunks/app/login/{page-0eeef685052869a6.js → page-18fb00d568cd1f0e.js} +1 -1
- package/_standalone/.next/static/chunks/app/page-a8e6f085f38388bf.js +1 -0
- package/_standalone/.next/static/chunks/app/setup/{page-99fcfc460fa29733.js → page-821714e7477be46c.js} +1 -1
- package/_standalone/.next/static/chunks/app/trash/{page-54cbd5c98d9de69b.js → page-f92b728b78ac0f7e.js} +1 -1
- package/_standalone/.next/static/chunks/app/view/[...path]/{not-found-fc04c2bd4f35bc6f.js → not-found-6e0c75ad26ce8572.js} +1 -1
- package/_standalone/.next/static/chunks/app/view/[...path]/page-f87f4901b5e1a88f.js +12 -0
- package/_standalone/.next/static/chunks/app/wiki/page-641edb1f3cff2f93.js +1 -0
- package/_standalone/.next/static/chunks/{webpack-2c19436659aa657b.js → webpack-72e8d9e9073fd1f9.js} +1 -1
- package/_standalone/.next/static/css/6c104b118d3bc9b7.css +1 -0
- package/_standalone/.next/trace +64 -64
- package/_standalone/app/globals.css +2 -1
- package/_standalone/components/AskFab.tsx +4 -4
- package/_standalone/components/AskModal.tsx +1 -1
- package/_standalone/components/GuideCard.tsx +101 -152
- package/_standalone/components/RightAskPanel.tsx +2 -2
- package/_standalone/components/ask/AskContent.tsx +90 -51
- package/_standalone/components/ask/AskHeader.tsx +218 -18
- package/_standalone/components/ask/MessageList.tsx +66 -47
- package/_standalone/components/ask/SessionHistory.tsx +86 -60
- package/_standalone/components/ask/SessionTabBar.tsx +29 -21
- package/_standalone/components/ask/ThinkingBlock.tsx +6 -5
- package/_standalone/components/ask/ToolCallBlock.tsx +10 -9
- package/_standalone/components/settings/SettingsContent.tsx +1 -1
- package/_standalone/data/skills/mindos/SKILL.md +67 -15
- package/_standalone/data/skills/mindos-zh/SKILL.md +67 -11
- package/_standalone/hooks/useAskSession.ts +23 -1
- package/_standalone/lib/stores/locale-store.ts +20 -6
- package/_standalone/tsconfig.tsbuildinfo +1 -1
- package/app/app/globals.css +2 -1
- package/app/app/layout.tsx +16 -4
- package/app/components/AskFab.tsx +4 -4
- package/app/components/AskModal.tsx +1 -1
- package/app/components/GuideCard.tsx +101 -152
- package/app/components/HomeContent.tsx +116 -575
- package/app/components/RightAskPanel.tsx +2 -2
- package/app/components/WikiHomeContent.tsx +151 -3
- package/app/components/ask/AskContent.tsx +90 -51
- package/app/components/ask/AskHeader.tsx +218 -18
- package/app/components/ask/MessageList.tsx +66 -47
- package/app/components/ask/SessionHistory.tsx +86 -60
- package/app/components/ask/SessionTabBar.tsx +29 -21
- package/app/components/ask/ThinkingBlock.tsx +6 -5
- package/app/components/ask/ToolCallBlock.tsx +10 -9
- package/app/components/settings/SettingsContent.tsx +1 -1
- package/app/data/skills/mindos/SKILL.md +67 -15
- package/app/data/skills/mindos-zh/SKILL.md +67 -11
- package/app/hooks/useAskSession.ts +23 -1
- package/app/lib/i18n/modules/ai-chat.ts +97 -10
- package/app/lib/i18n/modules/onboarding.ts +12 -12
- package/app/lib/stores/LocaleStoreInit.tsx +24 -1
- package/app/lib/stores/locale-store.ts +20 -6
- package/app/lib/types.ts +1 -0
- package/package.json +1 -1
- package/skills/mindos/SKILL.md +67 -15
- package/skills/mindos/references/knowledge-health.md +120 -0
- package/skills/mindos-max/SKILL.md +52 -5
- package/skills/mindos-max-zh/SKILL.md +55 -6
- package/skills/mindos-zh/SKILL.md +67 -11
- package/_standalone/.next/server/chunks/2364.js +0 -2
- package/_standalone/.next/server/chunks/357.js +0 -1
- package/_standalone/.next/static/chunks/178-105779afb62d36d9.js +0 -1
- package/_standalone/.next/static/chunks/2218-d54538000574ffef.js +0 -1
- package/_standalone/.next/static/chunks/2549-e63cf57fa927a41d.js +0 -1
- package/_standalone/.next/static/chunks/9274-296ab35f9f09e42e.js +0 -1
- package/_standalone/.next/static/chunks/app/agents/page-5d1446665ddb3801.js +0 -1
- package/_standalone/.next/static/chunks/app/echo/[segment]/page-b0103509ce34444b.js +0 -11
- package/_standalone/.next/static/chunks/app/layout-7e02ddf4144b01f1.js +0 -186
- package/_standalone/.next/static/chunks/app/page-6a6a12bd6d6812d0.js +0 -7
- package/_standalone/.next/static/chunks/app/view/[...path]/page-ca7bdcbf27f88a46.js +0 -12
- package/_standalone/.next/static/chunks/app/wiki/page-d492256a93f0b8bc.js +0 -1
- package/_standalone/.next/static/css/fd84c8316ead16eb.css +0 -1
- /package/_standalone/.next/static/{2ksXveDzEcnCMRIElDkLq → u8p6oIRTcr_ns-ElNZ9rl}/_buildManifest.js +0 -0
- /package/_standalone/.next/static/{2ksXveDzEcnCMRIElDkLq → u8p6oIRTcr_ns-ElNZ9rl}/_ssgManifest.js +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { useRef, useEffect, memo, useState, useCallback } from 'react';
|
|
4
|
-
import { Sparkles, Loader2, AlertCircle, Wrench, WifiOff, Zap, Copy, Check, ArrowDown } from 'lucide-react';
|
|
4
|
+
import { Sparkles, Loader2, AlertCircle, Wrench, WifiOff, Zap, Copy, Check, ArrowDown, FolderInput, Search, PenLine, Lightbulb } from 'lucide-react';
|
|
5
5
|
import ReactMarkdown from 'react-markdown';
|
|
6
6
|
import remarkGfm from 'remark-gfm';
|
|
7
7
|
import type { Message, ImagePart } from '@/lib/types';
|
|
@@ -12,7 +12,7 @@ import ThinkingBlock from './ThinkingBlock';
|
|
|
12
12
|
|
|
13
13
|
const SKILL_PREFIX_RE = /^Use the skill ([^:]+):\s*/;
|
|
14
14
|
|
|
15
|
-
function CopyMessageButton({ text }: { text: string }) {
|
|
15
|
+
function CopyMessageButton({ text, label }: { text: string; label?: string }) {
|
|
16
16
|
const [copied, setCopied] = useState(false);
|
|
17
17
|
const handleCopy = useCallback(() => {
|
|
18
18
|
copyToClipboard(text).then(ok => {
|
|
@@ -27,8 +27,8 @@ function CopyMessageButton({ text }: { text: string }) {
|
|
|
27
27
|
<button
|
|
28
28
|
type="button"
|
|
29
29
|
onClick={handleCopy}
|
|
30
|
-
className="absolute -bottom-1 right-1 p-1 rounded-md bg-card border border-border/60 shadow-sm text-muted-foreground hover:text-foreground opacity-0 group-hover:opacity-100 transition-opacity"
|
|
31
|
-
title=
|
|
30
|
+
className="absolute -bottom-1 right-1 p-1 rounded-md bg-card border border-border/60 shadow-sm text-muted-foreground hover:text-foreground opacity-100 md:opacity-0 md:group-hover:opacity-100 transition-opacity"
|
|
31
|
+
title={label ?? 'Copy'}
|
|
32
32
|
>
|
|
33
33
|
{copied ? <Check size={11} className="text-success" /> : <Copy size={11} />}
|
|
34
34
|
</button>
|
|
@@ -62,7 +62,7 @@ function UserMessageContent({ content, skillName, images }: { content: string; s
|
|
|
62
62
|
)}
|
|
63
63
|
{/* Skill capsule + text */}
|
|
64
64
|
{resolved && (
|
|
65
|
-
<span className="inline-flex items-center gap-1 px-1.5 py-0.5 rounded-full text-[11px] font-medium bg-
|
|
65
|
+
<span className="inline-flex items-center gap-1 px-1.5 py-0.5 rounded-full text-[11px] font-medium bg-white/20 text-white/90 mr-1 align-middle">
|
|
66
66
|
<Zap size={10} className="shrink-0" />
|
|
67
67
|
{resolved}
|
|
68
68
|
</span>
|
|
@@ -82,16 +82,16 @@ function AssistantMessage({ content, isStreaming }: { content: string; isStreami
|
|
|
82
82
|
prose-h1:text-base prose-h2:text-[15px] prose-h3:text-sm
|
|
83
83
|
prose-ul:my-1.5 prose-li:my-0.5
|
|
84
84
|
prose-ol:my-1.5
|
|
85
|
-
prose-code:text-[0.8em] prose-code:bg-
|
|
86
|
-
prose-pre:bg-
|
|
87
|
-
prose-blockquote:border-l-[var(--amber)] prose-blockquote:text-muted-foreground
|
|
85
|
+
prose-code:text-[0.8em] prose-code:bg-muted/80 prose-code:px-1.5 prose-code:py-0.5 prose-code:rounded-md prose-code:before:content-none prose-code:after:content-none prose-code:font-mono
|
|
86
|
+
prose-pre:bg-muted/60 prose-pre:text-foreground prose-pre:text-xs prose-pre:rounded-lg
|
|
87
|
+
prose-blockquote:border-l-[var(--amber)] prose-blockquote:text-muted-foreground prose-blockquote:not-italic
|
|
88
88
|
prose-a:text-[var(--amber)] prose-a:no-underline hover:prose-a:underline
|
|
89
89
|
prose-strong:text-foreground prose-strong:font-semibold
|
|
90
|
-
prose-table:text-xs prose-th:py-1 prose-td:py-1
|
|
90
|
+
prose-table:text-xs prose-th:py-1.5 prose-td:py-1
|
|
91
91
|
">
|
|
92
92
|
<ReactMarkdown remarkPlugins={[remarkGfm]}>{cleaned}</ReactMarkdown>
|
|
93
93
|
{isStreaming && (
|
|
94
|
-
<span className="inline-block w-1.5 h-3.5 bg-[var(--amber)] ml-0.5 align-middle animate-pulse rounded-
|
|
94
|
+
<span className="inline-block w-1.5 h-3.5 bg-[var(--amber)] ml-0.5 align-middle animate-pulse rounded-full" />
|
|
95
95
|
)}
|
|
96
96
|
</div>
|
|
97
97
|
);
|
|
@@ -129,7 +129,7 @@ function AssistantMessageWithParts({ message, isStreaming }: { message: Message;
|
|
|
129
129
|
return null;
|
|
130
130
|
})}
|
|
131
131
|
{showTrailingSpinner && (
|
|
132
|
-
<div className="flex items-center gap-2 py-1 mt-1">
|
|
132
|
+
<div className="flex items-center gap-2 py-1.5 mt-1.5">
|
|
133
133
|
<Loader2 size={12} className="animate-spin text-[var(--amber)]" />
|
|
134
134
|
<span className="text-xs text-muted-foreground animate-pulse">Executing tool…</span>
|
|
135
135
|
</div>
|
|
@@ -145,9 +145,9 @@ function StepCounter({ parts }: { parts: Message['parts'] }) {
|
|
|
145
145
|
const lastToolCall = toolCalls[toolCalls.length - 1];
|
|
146
146
|
const toolLabel = lastToolCall.type === 'tool-call' ? lastToolCall.toolName : '';
|
|
147
147
|
return (
|
|
148
|
-
<div className="flex items-center gap-1.5 mt-1.5 text-xs text-muted-foreground/
|
|
148
|
+
<div className="flex items-center gap-1.5 mt-2 pt-1.5 border-t border-border/15 text-xs text-muted-foreground/60">
|
|
149
149
|
<Wrench size={10} />
|
|
150
|
-
<span>Step {toolCalls.length}{toolLabel ? ` — ${toolLabel}` : ''}</span>
|
|
150
|
+
<span className="font-medium">Step {toolCalls.length}{toolLabel ? ` — ${toolLabel}` : ''}</span>
|
|
151
151
|
</div>
|
|
152
152
|
);
|
|
153
153
|
}
|
|
@@ -158,13 +158,14 @@ interface MessageListProps {
|
|
|
158
158
|
loadingPhase: 'connecting' | 'thinking' | 'streaming' | 'reconnecting';
|
|
159
159
|
emptyPrompt: string;
|
|
160
160
|
emptyHint?: string;
|
|
161
|
-
suggestions: readonly string[];
|
|
161
|
+
suggestions: readonly { label: string; prompt: string }[];
|
|
162
162
|
onSuggestionClick: (text: string) => void;
|
|
163
163
|
labels: {
|
|
164
164
|
connecting: string;
|
|
165
165
|
thinking: string;
|
|
166
166
|
generating: string;
|
|
167
167
|
reconnecting?: string;
|
|
168
|
+
copyMessage?: string;
|
|
168
169
|
};
|
|
169
170
|
}
|
|
170
171
|
|
|
@@ -183,7 +184,9 @@ export default memo(function MessageList({
|
|
|
183
184
|
const [showScrollDown, setShowScrollDown] = useState(false);
|
|
184
185
|
|
|
185
186
|
const scrollToBottom = useCallback(() => {
|
|
186
|
-
|
|
187
|
+
const container = scrollContainerRef.current;
|
|
188
|
+
if (!container) return;
|
|
189
|
+
container.scrollTo({ top: container.scrollHeight, behavior: 'smooth' });
|
|
187
190
|
}, []);
|
|
188
191
|
|
|
189
192
|
useEffect(() => {
|
|
@@ -202,56 +205,65 @@ export default memo(function MessageList({
|
|
|
202
205
|
}, []);
|
|
203
206
|
|
|
204
207
|
return (
|
|
205
|
-
<div ref={scrollContainerRef} className="relative flex-1 overflow-y-auto overflow-x-hidden px-4 py-
|
|
208
|
+
<div ref={scrollContainerRef} role="log" aria-live="polite" className="relative flex-1 overflow-y-auto overflow-x-hidden px-4 py-5 space-y-5 min-h-0">
|
|
206
209
|
{messages.length === 0 && (
|
|
207
|
-
<div className="flex flex-col items-center justify-center flex-1 min-h-[
|
|
208
|
-
{/* Brand anchor */}
|
|
209
|
-
<div className="w-
|
|
210
|
-
<
|
|
210
|
+
<div className="flex flex-col items-center justify-center flex-1 min-h-[260px] px-6 pt-10 pb-4">
|
|
211
|
+
{/* Brand anchor — refined presence */}
|
|
212
|
+
<div className="relative w-12 h-12 rounded-2xl bg-[var(--amber)]/10 flex items-center justify-center mb-6">
|
|
213
|
+
<div className="absolute inset-0 rounded-2xl bg-[var(--amber)]/5 scale-[1.4]" />
|
|
214
|
+
<Sparkles size={22} className="text-[var(--amber)] relative z-10" />
|
|
211
215
|
</div>
|
|
212
|
-
<p className="text-center text-
|
|
216
|
+
<p className="text-center text-[15px] font-semibold text-foreground tracking-tight mb-2">{emptyPrompt}</p>
|
|
213
217
|
{emptyHint && (
|
|
214
|
-
<p className="text-center text-
|
|
218
|
+
<p className="text-center text-xs text-muted-foreground/80 mb-10 tracking-wide">{emptyHint}</p>
|
|
215
219
|
)}
|
|
216
|
-
{/* Suggestion chips —
|
|
217
|
-
<div className="
|
|
218
|
-
{suggestions.map((s, i) =>
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
220
|
+
{/* Suggestion chips — refined single column */}
|
|
221
|
+
<div className="flex flex-col gap-2.5 max-w-[280px] w-full">
|
|
222
|
+
{suggestions.map((s, i) => {
|
|
223
|
+
const icons = [FolderInput, Search, PenLine, Lightbulb];
|
|
224
|
+
const SugIcon = icons[i % icons.length];
|
|
225
|
+
return (
|
|
226
|
+
<button
|
|
227
|
+
key={i}
|
|
228
|
+
type="button"
|
|
229
|
+
onClick={() => onSuggestionClick(s.prompt)}
|
|
230
|
+
className="group/sug flex items-center gap-3 text-left text-[13px] px-3.5 py-3 rounded-xl border border-border/40 bg-transparent text-muted-foreground hover:text-foreground hover:border-[var(--amber)]/30 hover:bg-[var(--amber)]/5 transition-all leading-snug"
|
|
231
|
+
aria-label={s.prompt}
|
|
232
|
+
>
|
|
233
|
+
<span className="shrink-0 w-8 h-8 rounded-lg bg-muted/60 flex items-center justify-center group-hover/sug:bg-[var(--amber)]/10 transition-colors">
|
|
234
|
+
<SugIcon size={15} className="text-muted-foreground/70 group-hover/sug:text-[var(--amber)] transition-colors" />
|
|
235
|
+
</span>
|
|
236
|
+
<span className="flex-1">{s.label}</span>
|
|
237
|
+
</button>
|
|
238
|
+
);
|
|
239
|
+
})}
|
|
228
240
|
</div>
|
|
229
241
|
</div>
|
|
230
242
|
)}
|
|
231
243
|
{messages.map((m, i) => (
|
|
232
|
-
<div key={i} className={`flex gap-3 ${m.role === 'user' ? 'justify-end' : 'justify-start'}`}>
|
|
244
|
+
<div key={i} className={`flex gap-3 animate-[fadeSlideUp_0.22s_ease_both] ${m.role === 'user' ? 'justify-end' : 'justify-start'}`}>
|
|
233
245
|
{m.role === 'assistant' && (
|
|
234
246
|
<div
|
|
235
|
-
className="w-
|
|
247
|
+
className="w-7 h-7 rounded-lg flex items-center justify-center shrink-0 mt-0.5 bg-[var(--amber)]/8"
|
|
236
248
|
>
|
|
237
|
-
<Sparkles size={
|
|
249
|
+
<Sparkles size={13} className="text-[var(--amber)]" />
|
|
238
250
|
</div>
|
|
239
251
|
)}
|
|
240
252
|
{m.role === 'user' ? (
|
|
241
253
|
<div
|
|
242
|
-
className="max-w-[85%] px-3 py-2 rounded-
|
|
254
|
+
className="max-w-[85%] px-3.5 py-2.5 rounded-2xl rounded-br-lg text-sm leading-relaxed whitespace-pre-wrap bg-[var(--amber)] text-[var(--amber-foreground)] shadow-sm shadow-[var(--amber)]/10"
|
|
243
255
|
>
|
|
244
256
|
<UserMessageContent content={m.content} skillName={m.skillName} images={m.images} />
|
|
245
257
|
</div>
|
|
246
258
|
) : m.content.startsWith('__error__') ? (
|
|
247
|
-
<div className="max-w-[85%] px-3 py-
|
|
248
|
-
<div className="flex items-start gap-2 text-error">
|
|
249
|
-
<AlertCircle size={
|
|
250
|
-
<span className="leading-relaxed">{m.content.slice(9)}</span>
|
|
259
|
+
<div className="max-w-[85%] px-3.5 py-3 rounded-2xl rounded-bl-md border border-error/30 bg-error/10 text-sm shadow-sm">
|
|
260
|
+
<div className="flex items-start gap-2.5 text-error">
|
|
261
|
+
<AlertCircle size={15} className="shrink-0 mt-0.5" />
|
|
262
|
+
<span className="leading-relaxed font-medium">{m.content.slice(9)}</span>
|
|
251
263
|
</div>
|
|
252
264
|
</div>
|
|
253
265
|
) : (
|
|
254
|
-
<div className="group relative max-w-[85%] px-3 py-2 rounded-
|
|
266
|
+
<div className="group relative max-w-[85%] px-3.5 py-2.5 rounded-2xl rounded-bl-lg bg-card border border-border/30 shadow-sm text-foreground text-sm">
|
|
255
267
|
{(m.parts && m.parts.length > 0) || stripThinkingTags(m.content) ? (
|
|
256
268
|
<>
|
|
257
269
|
<AssistantMessageWithParts message={m} isStreaming={isLoading && i === messages.length - 1} />
|
|
@@ -259,17 +271,18 @@ export default memo(function MessageList({
|
|
|
259
271
|
<StepCounter parts={m.parts} />
|
|
260
272
|
)}
|
|
261
273
|
{!(isLoading && i === messages.length - 1) && stripThinkingTags(m.content) && (
|
|
262
|
-
<CopyMessageButton text={stripThinkingTags(m.content)} />
|
|
274
|
+
<CopyMessageButton text={stripThinkingTags(m.content)} label={labels.copyMessage} />
|
|
263
275
|
)}
|
|
264
276
|
</>
|
|
265
277
|
) : isLoading && i === messages.length - 1 ? (
|
|
266
|
-
<div className="flex items-center gap-2 py-1">
|
|
278
|
+
<div className="flex items-center gap-2.5 py-1">
|
|
267
279
|
{loadingPhase === 'reconnecting' ? (
|
|
268
280
|
<WifiOff size={14} className="text-[var(--amber)] animate-pulse" />
|
|
269
281
|
) : (
|
|
270
282
|
<Loader2 size={14} className="animate-spin text-[var(--amber)]" />
|
|
271
283
|
)}
|
|
272
|
-
<span className="text-xs text-muted-foreground
|
|
284
|
+
<span className="text-xs text-muted-foreground">
|
|
285
|
+
<span className="inline-flex items-center gap-1">
|
|
273
286
|
{loadingPhase === 'reconnecting'
|
|
274
287
|
? (labels.reconnecting ?? 'Reconnecting...')
|
|
275
288
|
: loadingPhase === 'connecting'
|
|
@@ -277,6 +290,12 @@ export default memo(function MessageList({
|
|
|
277
290
|
: loadingPhase === 'thinking'
|
|
278
291
|
? labels.thinking
|
|
279
292
|
: labels.generating}
|
|
293
|
+
<span className="inline-flex gap-0.5">
|
|
294
|
+
<span className="w-1 h-1 rounded-full bg-[var(--amber)] animate-bounce [animation-delay:0ms]"></span>
|
|
295
|
+
<span className="w-1 h-1 rounded-full bg-[var(--amber)] animate-bounce [animation-delay:150ms]"></span>
|
|
296
|
+
<span className="w-1 h-1 rounded-full bg-[var(--amber)] animate-bounce [animation-delay:300ms]"></span>
|
|
297
|
+
</span>
|
|
298
|
+
</span>
|
|
280
299
|
</span>
|
|
281
300
|
</div>
|
|
282
301
|
) : null}
|
|
@@ -291,7 +310,7 @@ export default memo(function MessageList({
|
|
|
291
310
|
<button
|
|
292
311
|
type="button"
|
|
293
312
|
onClick={scrollToBottom}
|
|
294
|
-
className="sticky bottom-2 left-1/2 -translate-x-1/2 z-10 p-
|
|
313
|
+
className="sticky bottom-2 left-1/2 -translate-x-1/2 z-10 p-2 rounded-full border border-border/60 bg-card shadow-md text-muted-foreground hover:text-foreground hover:bg-muted transition-all hover:shadow-lg"
|
|
295
314
|
title="Scroll to bottom"
|
|
296
315
|
>
|
|
297
316
|
<ArrowDown size={14} />
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { useState, useRef, useEffect, useCallback } from 'react';
|
|
4
|
-
import { Trash2, Pencil } from 'lucide-react';
|
|
4
|
+
import { Trash2, Pencil, Pin, PinOff } from 'lucide-react';
|
|
5
5
|
import type { ChatSession } from '@/lib/types';
|
|
6
6
|
import { sessionTitle } from '@/hooks/useAskSession';
|
|
7
7
|
|
|
@@ -11,11 +11,25 @@ interface SessionHistoryProps {
|
|
|
11
11
|
onLoad: (id: string) => void;
|
|
12
12
|
onDelete: (id: string) => void;
|
|
13
13
|
onRename: (id: string, title: string) => void;
|
|
14
|
+
onTogglePin: (id: string) => void;
|
|
14
15
|
onClearAll: () => void;
|
|
15
16
|
labels: { title: string; clearAll: string; confirmClear: string; noSessions: string; rename: string };
|
|
16
17
|
}
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
function formatRelativeTime(date: Date): string {
|
|
20
|
+
const now = Date.now();
|
|
21
|
+
const diff = now - date.getTime();
|
|
22
|
+
const mins = Math.floor(diff / 60000);
|
|
23
|
+
if (mins < 1) return 'just now';
|
|
24
|
+
if (mins < 60) return `${mins}m ago`;
|
|
25
|
+
const hours = Math.floor(mins / 60);
|
|
26
|
+
if (hours < 24) return `${hours}h ago`;
|
|
27
|
+
const days = Math.floor(hours / 24);
|
|
28
|
+
if (days < 7) return `${days}d ago`;
|
|
29
|
+
return date.toLocaleDateString(undefined, { month: 'short', day: 'numeric' });
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export default function SessionHistory({ sessions, activeSessionId, onLoad, onDelete, onRename, onTogglePin, onClearAll, labels }: SessionHistoryProps) {
|
|
19
33
|
const [confirmClearAll, setConfirmClearAll] = useState(false);
|
|
20
34
|
const clearTimerRef = useRef<ReturnType<typeof setTimeout>>(undefined);
|
|
21
35
|
const [editingId, setEditingId] = useState<string | null>(null);
|
|
@@ -60,78 +74,90 @@ export default function SessionHistory({ sessions, activeSessionId, onLoad, onDe
|
|
|
60
74
|
}, []);
|
|
61
75
|
|
|
62
76
|
return (
|
|
63
|
-
<div className="border-b border-border px-4 py-
|
|
64
|
-
<div className="flex items-center justify-between mb-2">
|
|
65
|
-
<span className="text-xs text-muted-foreground">{labels.title}</span>
|
|
77
|
+
<div className="border-b border-border/40 px-4 py-3 max-h-[220px] overflow-y-auto">
|
|
78
|
+
<div className="flex items-center justify-between mb-2.5">
|
|
79
|
+
<span className="text-xs font-medium text-muted-foreground">{labels.title}</span>
|
|
66
80
|
{sessions.length > 1 && (
|
|
67
81
|
<button
|
|
68
82
|
type="button"
|
|
69
83
|
onClick={handleClearAll}
|
|
70
|
-
className={`text-2xs px-
|
|
84
|
+
className={`text-2xs px-2 py-0.5 rounded-md transition-colors ${
|
|
71
85
|
confirmClearAll
|
|
72
86
|
? 'bg-error/10 text-error font-medium'
|
|
73
|
-
: 'text-muted-foreground hover:text-error hover:bg-muted'
|
|
87
|
+
: 'text-muted-foreground/60 hover:text-error hover:bg-muted'
|
|
74
88
|
}`}
|
|
75
89
|
>
|
|
76
90
|
{confirmClearAll ? labels.confirmClear : labels.clearAll}
|
|
77
91
|
</button>
|
|
78
92
|
)}
|
|
79
93
|
</div>
|
|
80
|
-
<div className="flex flex-col gap-1
|
|
94
|
+
<div className="flex flex-col gap-1">
|
|
81
95
|
{sessions.length === 0 && (
|
|
82
|
-
<div className="text-xs text-muted-foreground/
|
|
96
|
+
<div className="text-xs text-muted-foreground/50 py-2 text-center">{labels.noSessions}</div>
|
|
83
97
|
)}
|
|
84
|
-
{sessions.map((s) =>
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
98
|
+
{sessions.map((s) => {
|
|
99
|
+
const isActive = activeSessionId === s.id;
|
|
100
|
+
return (
|
|
101
|
+
<div key={s.id} className="group flex items-center gap-0.5">
|
|
102
|
+
{s.pinned && <Pin size={10} className="shrink-0 text-[var(--amber)]/50 -rotate-45 ml-1" />}
|
|
103
|
+
<button
|
|
104
|
+
type="button"
|
|
105
|
+
onClick={() => onLoad(s.id)}
|
|
106
|
+
onDoubleClick={() => startRename(s)}
|
|
107
|
+
className={`flex-1 text-left px-2.5 py-2 rounded-lg text-xs transition-colors min-w-0 ${
|
|
108
|
+
isActive
|
|
109
|
+
? 'bg-[var(--amber)]/8 text-foreground border border-[var(--amber)]/15'
|
|
110
|
+
: 'text-muted-foreground hover:bg-muted hover:text-foreground border border-transparent'
|
|
111
|
+
}`}
|
|
112
|
+
>
|
|
113
|
+
{editingId === s.id ? (
|
|
114
|
+
<input
|
|
115
|
+
ref={inputRef}
|
|
116
|
+
type="text"
|
|
117
|
+
value={editValue}
|
|
118
|
+
onChange={(e) => setEditValue(e.target.value)}
|
|
119
|
+
onBlur={commitRename}
|
|
120
|
+
onKeyDown={(e) => {
|
|
121
|
+
if (e.key === 'Enter') { e.preventDefault(); commitRename(); }
|
|
122
|
+
if (e.key === 'Escape') { e.preventDefault(); cancelRename(); }
|
|
123
|
+
}}
|
|
124
|
+
onClick={(e) => e.stopPropagation()}
|
|
125
|
+
className="w-full bg-transparent border-b border-[var(--amber)] outline-none text-xs text-foreground"
|
|
126
|
+
/>
|
|
127
|
+
) : (
|
|
128
|
+
<div className="truncate font-medium">{sessionTitle(s)}</div>
|
|
129
|
+
)}
|
|
130
|
+
{editingId !== s.id && (
|
|
131
|
+
<div className="text-2xs text-muted-foreground/50 mt-0.5">{formatRelativeTime(new Date(s.updatedAt))}</div>
|
|
132
|
+
)}
|
|
133
|
+
</button>
|
|
134
|
+
<button
|
|
135
|
+
type="button"
|
|
136
|
+
onClick={() => onTogglePin(s.id)}
|
|
137
|
+
className={`p-1.5 rounded-lg transition-opacity opacity-0 group-hover:opacity-100 ${s.pinned ? 'text-[var(--amber)] hover:text-muted-foreground' : 'text-muted-foreground hover:text-[var(--amber)]'}`}
|
|
138
|
+
title={s.pinned ? 'Unpin' : 'Pin'}
|
|
139
|
+
>
|
|
140
|
+
{s.pinned ? <PinOff size={11} /> : <Pin size={11} />}
|
|
141
|
+
</button>
|
|
142
|
+
<button
|
|
143
|
+
type="button"
|
|
144
|
+
onClick={() => startRename(s)}
|
|
145
|
+
className="p-1.5 rounded-lg text-muted-foreground hover:text-foreground hover:bg-muted opacity-0 group-hover:opacity-100 transition-opacity"
|
|
146
|
+
title={labels.rename}
|
|
147
|
+
>
|
|
148
|
+
<Pencil size={11} />
|
|
149
|
+
</button>
|
|
150
|
+
<button
|
|
151
|
+
type="button"
|
|
152
|
+
onClick={() => onDelete(s.id)}
|
|
153
|
+
className="p-1.5 rounded-lg text-muted-foreground hover:text-error hover:bg-error/5 opacity-0 group-hover:opacity-100 transition-opacity"
|
|
154
|
+
title="Delete session"
|
|
155
|
+
>
|
|
156
|
+
<Trash2 size={11} />
|
|
157
|
+
</button>
|
|
158
|
+
</div>
|
|
159
|
+
);
|
|
160
|
+
})}
|
|
135
161
|
</div>
|
|
136
162
|
</div>
|
|
137
163
|
);
|
|
@@ -19,48 +19,56 @@ export default function SessionTabBar({
|
|
|
19
19
|
}: SessionTabBarProps) {
|
|
20
20
|
const { t } = useLocale();
|
|
21
21
|
const visibleSessions = sessions.slice(0, maxTabs);
|
|
22
|
+
const hasMore = sessions.length > maxTabs;
|
|
22
23
|
|
|
23
24
|
if (visibleSessions.length === 0) return null;
|
|
24
25
|
|
|
25
26
|
return (
|
|
26
|
-
<div className="flex items-center border-b border-border shrink-0 bg-background/50">
|
|
27
|
+
<div className="flex items-center border-b border-border/60 shrink-0 bg-background/50" role="tablist">
|
|
27
28
|
<div className="flex flex-1 min-w-0">
|
|
28
29
|
{visibleSessions.map((s) => {
|
|
29
30
|
const isActive = s.id === activeSessionId;
|
|
30
31
|
const title = sessionTitle(s);
|
|
31
32
|
return (
|
|
32
|
-
<
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
33
|
+
<div key={s.id} className="group relative flex items-center min-w-0 max-w-[160px]">
|
|
34
|
+
<button
|
|
35
|
+
type="button"
|
|
36
|
+
role="tab"
|
|
37
|
+
aria-selected={isActive}
|
|
38
|
+
onClick={() => onLoad(s.id)}
|
|
39
|
+
className={`flex-1 min-w-0 px-3 py-2.5 text-xs transition-colors truncate
|
|
40
|
+
${isActive
|
|
41
|
+
? 'text-foreground border-b-2 border-[var(--amber)] bg-card font-medium'
|
|
42
|
+
: 'text-muted-foreground hover:text-foreground hover:bg-muted/50 border-b-2 border-transparent'
|
|
43
|
+
}`}
|
|
44
|
+
title={title}
|
|
45
|
+
>
|
|
46
|
+
{title === '(empty session)' ? t.hints.newChat : title}
|
|
47
|
+
</button>
|
|
44
48
|
{visibleSessions.length > 1 && (
|
|
45
|
-
<
|
|
46
|
-
|
|
47
|
-
tabIndex={0}
|
|
49
|
+
<button
|
|
50
|
+
type="button"
|
|
48
51
|
onClick={(e) => { e.stopPropagation(); onDelete(s.id); }}
|
|
49
|
-
|
|
50
|
-
className="shrink-0 p-0.5 rounded opacity-0 group-hover:opacity-100 hover:bg-muted hover:text-error transition-opacity"
|
|
52
|
+
className="absolute right-0.5 top-1/2 -translate-y-1/2 shrink-0 p-0.5 rounded opacity-100 md:opacity-0 md:group-hover:opacity-100 hover:bg-muted hover:text-error transition-opacity"
|
|
51
53
|
title={t.hints.closeSession}
|
|
54
|
+
aria-label={`${t.hints.closeSession}: ${title}`}
|
|
52
55
|
>
|
|
53
56
|
<X size={10} />
|
|
54
|
-
</
|
|
57
|
+
</button>
|
|
55
58
|
)}
|
|
56
|
-
</
|
|
59
|
+
</div>
|
|
57
60
|
);
|
|
58
61
|
})}
|
|
62
|
+
{hasMore && (
|
|
63
|
+
<span className="flex items-center px-2 text-2xs text-muted-foreground/60">
|
|
64
|
+
+{sessions.length - maxTabs}
|
|
65
|
+
</span>
|
|
66
|
+
)}
|
|
59
67
|
</div>
|
|
60
68
|
<button
|
|
61
69
|
type="button"
|
|
62
70
|
onClick={onNew}
|
|
63
|
-
className="shrink-0 p-2 text-muted-foreground hover:text-foreground hover:bg-muted/50 transition-colors"
|
|
71
|
+
className="shrink-0 p-2 text-muted-foreground hover:text-foreground hover:bg-muted/50 transition-colors rounded-lg"
|
|
64
72
|
title={t.hints.newChat}
|
|
65
73
|
>
|
|
66
74
|
<Plus size={13} />
|
|
@@ -16,11 +16,12 @@ export default function ThinkingBlock({ text, isStreaming }: ThinkingBlockProps)
|
|
|
16
16
|
if (!text && !isStreaming) return null;
|
|
17
17
|
|
|
18
18
|
return (
|
|
19
|
-
<div className="my-1 rounded-
|
|
19
|
+
<div className="my-1.5 rounded-lg border border-border/30 bg-muted/15 text-xs">
|
|
20
20
|
<button
|
|
21
21
|
type="button"
|
|
22
22
|
onClick={() => setExpanded(v => !v)}
|
|
23
|
-
|
|
23
|
+
aria-expanded={expanded}
|
|
24
|
+
className="w-full flex items-center gap-1.5 px-2.5 py-2 text-left hover:bg-muted/30 transition-colors rounded-lg"
|
|
24
25
|
>
|
|
25
26
|
{expanded ? (
|
|
26
27
|
<ChevronDown size={12} className="shrink-0 text-muted-foreground" />
|
|
@@ -35,17 +36,17 @@ export default function ThinkingBlock({ text, isStreaming }: ThinkingBlockProps)
|
|
|
35
36
|
)}
|
|
36
37
|
</span>
|
|
37
38
|
{!expanded && text && (
|
|
38
|
-
<span className="text-muted-foreground/
|
|
39
|
+
<span className="text-muted-foreground/50 truncate flex-1 ml-1">
|
|
39
40
|
{text.slice(0, 80)}{text.length > 80 ? '...' : ''}
|
|
40
41
|
</span>
|
|
41
42
|
)}
|
|
42
43
|
</button>
|
|
43
44
|
{expanded && (
|
|
44
|
-
<div className="px-2 pb-2 pt-
|
|
45
|
+
<div className="px-2.5 pb-2.5 pt-1 border-t border-border/20">
|
|
45
46
|
<div className="text-muted-foreground whitespace-pre-wrap leading-relaxed">
|
|
46
47
|
{text}
|
|
47
48
|
{isStreaming && (
|
|
48
|
-
<span className="inline-block w-1 h-3 bg-muted-foreground/40 ml-0.5 align-middle animate-pulse rounded-
|
|
49
|
+
<span className="inline-block w-1 h-3 bg-muted-foreground/40 ml-0.5 align-middle animate-pulse rounded-full" />
|
|
49
50
|
)}
|
|
50
51
|
</div>
|
|
51
52
|
</div>
|
|
@@ -101,15 +101,16 @@ export default function ToolCallBlock({ part }: { part: ToolCallPart }) {
|
|
|
101
101
|
: formatInput(part.input);
|
|
102
102
|
|
|
103
103
|
return (
|
|
104
|
-
<div className={`my-1 rounded-
|
|
104
|
+
<div className={`my-1.5 rounded-lg border text-xs font-mono ${
|
|
105
105
|
isDestructive
|
|
106
106
|
? 'border-[var(--amber)]/30 bg-background/60'
|
|
107
|
-
: 'border-border/
|
|
107
|
+
: 'border-border/40 bg-background/50'
|
|
108
108
|
}`}>
|
|
109
109
|
<button
|
|
110
110
|
type="button"
|
|
111
111
|
onClick={() => setManualToggle(v => v === null ? !expanded : !v)}
|
|
112
|
-
|
|
112
|
+
aria-expanded={expanded}
|
|
113
|
+
className="w-full flex items-center gap-1.5 px-2.5 py-2 text-left hover:bg-muted/30 transition-colors rounded-lg"
|
|
113
114
|
>
|
|
114
115
|
{expanded ? <ChevronDown size={12} className="shrink-0 text-muted-foreground" /> : <ChevronRight size={12} className="shrink-0 text-muted-foreground" />}
|
|
115
116
|
{isDestructive && <AlertTriangle size={11} className="shrink-0 text-[var(--amber)]" />}
|
|
@@ -161,19 +162,19 @@ export default function ToolCallBlock({ part }: { part: ToolCallPart }) {
|
|
|
161
162
|
</div>
|
|
162
163
|
) : (
|
|
163
164
|
/* Fallback: show input (always), output when available */
|
|
164
|
-
<div className="px-2 pb-2 pt-1 space-y-1">
|
|
165
|
+
<div className="px-2.5 pb-2.5 pt-1.5 space-y-1.5">
|
|
165
166
|
{part.state === 'running' && (
|
|
166
|
-
<div className="text-muted-foreground/60 text-2xs flex items-center gap-1">
|
|
167
|
+
<div className="text-muted-foreground/60 text-2xs flex items-center gap-1.5">
|
|
167
168
|
<Loader2 size={10} className="animate-spin" /> Running...
|
|
168
169
|
</div>
|
|
169
170
|
)}
|
|
170
|
-
<div className="text-muted-foreground">
|
|
171
|
-
<span className="font-semibold">Input: </span>
|
|
171
|
+
<div className="text-muted-foreground leading-relaxed">
|
|
172
|
+
<span className="font-semibold text-foreground/70">Input: </span>
|
|
172
173
|
<span className="break-all whitespace-pre-wrap">{JSON.stringify(part.input, null, 2)}</span>
|
|
173
174
|
</div>
|
|
174
175
|
{part.output !== undefined && part.output !== '' && (
|
|
175
|
-
<div className="text-muted-foreground">
|
|
176
|
-
<span className="font-semibold">Output: </span>
|
|
176
|
+
<div className="text-muted-foreground leading-relaxed">
|
|
177
|
+
<span className="font-semibold text-foreground/70">Output: </span>
|
|
177
178
|
<span className="break-all whitespace-pre-wrap">{part.output.length > 500 ? part.output.slice(0, 500) + '…' : part.output}</span>
|
|
178
179
|
</div>
|
|
179
180
|
)}
|
|
@@ -66,7 +66,7 @@ export default function SettingsContent({ visible, initialTab, variant, onClose
|
|
|
66
66
|
|
|
67
67
|
if (justOpened) {
|
|
68
68
|
apiFetch<SettingsData>('/api/settings').then(d => { setData(d); dataLoaded.current = true; }).catch(() => setStatus('load-error'));
|
|
69
|
-
setFont(localStorage.getItem('prose-font') === 'geist' ? 'inter' : localStorage.getItem('prose-font') ?? '
|
|
69
|
+
setFont(localStorage.getItem('prose-font') === 'geist' ? 'inter' : localStorage.getItem('prose-font') ?? 'ibm-plex-sans');
|
|
70
70
|
setFontSize(localStorage.getItem('prose-font-size') ?? '15px');
|
|
71
71
|
setContentWidth(localStorage.getItem('content-width') ?? '780px');
|
|
72
72
|
const stored = localStorage.getItem('theme');
|