main
mula.liu 2026-03-02 10:54:40 +08:00
parent 301a6a4a2d
commit ee25f7ea5a
5 changed files with 151 additions and 5 deletions

View File

@ -19,6 +19,8 @@
"react": "^19.2.0", "react": "^19.2.0",
"react-dom": "^19.2.0", "react-dom": "^19.2.0",
"react-markdown": "^10.1.0", "react-markdown": "^10.1.0",
"rehype-raw": "^7.0.0",
"rehype-sanitize": "^6.0.0",
"remark-gfm": "^4.0.1", "remark-gfm": "^4.0.1",
"tailwind-merge": "^3.5.0", "tailwind-merge": "^3.5.0",
"three": "^0.183.1", "three": "^0.183.1",

View File

@ -1,4 +1,4 @@
import { useMemo, useState } from 'react'; import { useEffect, useMemo, useState } from 'react';
import { Boxes, MoonStar, Sparkles, SunMedium } from 'lucide-react'; import { Boxes, MoonStar, Sparkles, SunMedium } from 'lucide-react';
import { import {
useAppStore, useAppStore,
@ -13,7 +13,7 @@ import { appEn } from './i18n/app.en';
import './App.css'; import './App.css';
function App() { function App() {
const { theme, setTheme, locale, setLocale } = useAppStore(); const { theme, setTheme, locale, setLocale, activeBots } = useAppStore();
const [showImageFactory, setShowImageFactory] = useState(false); const [showImageFactory, setShowImageFactory] = useState(false);
const [showCreateWizard, setShowCreateWizard] = useState(false); const [showCreateWizard, setShowCreateWizard] = useState(false);
useBotsSync(); useBotsSync();
@ -28,6 +28,17 @@ function App() {
return { forcedBotId, compactMode }; return { forcedBotId, compactMode };
}, []); }, []);
useEffect(() => {
const forced = urlView.forcedBotId;
if (!forced) {
document.title = t.title;
return;
}
const bot = activeBots[forced];
const botName = String(bot?.name || '').trim();
document.title = botName ? `${t.title} - ${botName}` : `${t.title} - ${forced}`;
}, [activeBots, t.title, urlView.forcedBotId]);
return ( return (
<div className={`app-shell ${urlView.compactMode ? 'app-shell-compact' : ''}`} data-theme={theme}> <div className={`app-shell ${urlView.compactMode ? 'app-shell-compact' : ''}`} data-theme={theme}>
<div className="app-frame"> <div className="app-frame">

View File

@ -3,6 +3,8 @@ import axios from 'axios';
import { Activity, Boxes, Check, Clock3, EllipsisVertical, Eye, EyeOff, FileText, FolderOpen, Hammer, MessageSquareText, Paperclip, Plus, Power, PowerOff, RefreshCw, Repeat2, Save, Settings2, SlidersHorizontal, TriangleAlert, Trash2, UserRound, Waypoints, X } from 'lucide-react'; import { Activity, Boxes, Check, Clock3, EllipsisVertical, Eye, EyeOff, FileText, FolderOpen, Hammer, MessageSquareText, Paperclip, Plus, Power, PowerOff, RefreshCw, Repeat2, Save, Settings2, SlidersHorizontal, TriangleAlert, Trash2, UserRound, Waypoints, X } from 'lucide-react';
import ReactMarkdown from 'react-markdown'; import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm'; import remarkGfm from 'remark-gfm';
import rehypeRaw from 'rehype-raw';
import rehypeSanitize from 'rehype-sanitize';
import { APP_ENDPOINTS } from '../../config/env'; import { APP_ENDPOINTS } from '../../config/env';
import { useAppStore } from '../../store/appStore'; import { useAppStore } from '../../store/appStore';
import type { ChatMessage } from '../../types/bot'; import type { ChatMessage } from '../../types/bot';
@ -681,7 +683,11 @@ export function BotDashboardModule({
item.role === 'user' ? ( item.role === 'user' ? (
<div className="whitespace-pre-wrap">{normalizeUserMessageText(item.text)}</div> <div className="whitespace-pre-wrap">{normalizeUserMessageText(item.text)}</div>
) : ( ) : (
<ReactMarkdown remarkPlugins={[remarkGfm]} components={markdownComponents}> <ReactMarkdown
remarkPlugins={[remarkGfm]}
rehypePlugins={[rehypeRaw, rehypeSanitize]}
components={markdownComponents}
>
{decorateWorkspacePathsForMarkdown(displayText)} {decorateWorkspacePathsForMarkdown(displayText)}
</ReactMarkdown> </ReactMarkdown>
) )
@ -2508,7 +2514,11 @@ export function BotDashboardModule({
/> />
) : workspacePreview.isMarkdown ? ( ) : workspacePreview.isMarkdown ? (
<div className="workspace-markdown"> <div className="workspace-markdown">
<ReactMarkdown remarkPlugins={[remarkGfm]} components={markdownComponents}> <ReactMarkdown
remarkPlugins={[remarkGfm]}
rehypePlugins={[rehypeRaw, rehypeSanitize]}
components={markdownComponents}
>
{workspacePreview.content} {workspacePreview.content}
</ReactMarkdown> </ReactMarkdown>
</div> </div>

View File

@ -47,10 +47,13 @@ export const useAppStore = create<AppStore>((set) => ({
const prev = state.activeBots[bot.id]; const prev = state.activeBots[bot.id];
const incomingState = (bot.current_state || '').toUpperCase(); const incomingState = (bot.current_state || '').toUpperCase();
const prevState = (prev?.current_state || '').toUpperCase(); const prevState = (prev?.current_state || '').toUpperCase();
const latestEventTs = prev?.events?.[prev.events.length - 1]?.ts || 0;
const transientStateFresh = latestEventTs > 0 && Date.now() - latestEventTs < 15000;
const keepTransientState = const keepTransientState =
bot.docker_status === 'RUNNING' && bot.docker_status === 'RUNNING' &&
(incomingState === '' || incomingState === 'IDLE') && (incomingState === '' || incomingState === 'IDLE') &&
(prevState === 'THINKING' || prevState === 'TOOL_CALL'); (prevState === 'THINKING' || prevState === 'TOOL_CALL') &&
transientStateFresh;
const incomingAction = (bot.last_action || '').trim(); const incomingAction = (bot.last_action || '').trim();
nextBots[bot.id] = { nextBots[bot.id] = {

View File

@ -1285,6 +1285,11 @@ electron-to-chromium@^1.5.263:
resolved "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.302.tgz#032a5802b31f7119269959c69fe2015d8dad5edb" resolved "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.302.tgz#032a5802b31f7119269959c69fe2015d8dad5edb"
integrity sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg== integrity sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg==
entities@^6.0.0:
version "6.0.1"
resolved "https://registry.npmmirror.com/entities/-/entities-6.0.1.tgz#c28c34a43379ca7f61d074130b2f5f7020a30694"
integrity sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==
es-define-property@^1.0.1: es-define-property@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" resolved "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa"
@ -1682,6 +1687,55 @@ hasown@^2.0.2:
dependencies: dependencies:
function-bind "^1.1.2" function-bind "^1.1.2"
hast-util-from-parse5@^8.0.0:
version "8.0.3"
resolved "https://registry.npmmirror.com/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz#830a35022fff28c3fea3697a98c2f4cc6b835a2e"
integrity sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==
dependencies:
"@types/hast" "^3.0.0"
"@types/unist" "^3.0.0"
devlop "^1.0.0"
hastscript "^9.0.0"
property-information "^7.0.0"
vfile "^6.0.0"
vfile-location "^5.0.0"
web-namespaces "^2.0.0"
hast-util-parse-selector@^4.0.0:
version "4.0.0"
resolved "https://registry.npmmirror.com/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz#352879fa86e25616036037dd8931fb5f34cb4a27"
integrity sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==
dependencies:
"@types/hast" "^3.0.0"
hast-util-raw@^9.0.0:
version "9.1.0"
resolved "https://registry.npmmirror.com/hast-util-raw/-/hast-util-raw-9.1.0.tgz#79b66b26f6f68fb50dfb4716b2cdca90d92adf2e"
integrity sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==
dependencies:
"@types/hast" "^3.0.0"
"@types/unist" "^3.0.0"
"@ungap/structured-clone" "^1.0.0"
hast-util-from-parse5 "^8.0.0"
hast-util-to-parse5 "^8.0.0"
html-void-elements "^3.0.0"
mdast-util-to-hast "^13.0.0"
parse5 "^7.0.0"
unist-util-position "^5.0.0"
unist-util-visit "^5.0.0"
vfile "^6.0.0"
web-namespaces "^2.0.0"
zwitch "^2.0.0"
hast-util-sanitize@^5.0.0:
version "5.0.2"
resolved "https://registry.npmmirror.com/hast-util-sanitize/-/hast-util-sanitize-5.0.2.tgz#edb260d94e5bba2030eb9375790a8753e5bf391f"
integrity sha512-3yTWghByc50aGS7JlGhk61SPenfE/p1oaFeNwkOOyrscaOkMGrcW9+Cy/QAIOBpZxP1yqDIzFMR0+Np0i0+usg==
dependencies:
"@types/hast" "^3.0.0"
"@ungap/structured-clone" "^1.0.0"
unist-util-position "^5.0.0"
hast-util-to-jsx-runtime@^2.0.0: hast-util-to-jsx-runtime@^2.0.0:
version "2.3.6" version "2.3.6"
resolved "https://registry.npmmirror.com/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz#ff31897aae59f62232e21594eac7ef6b63333e98" resolved "https://registry.npmmirror.com/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz#ff31897aae59f62232e21594eac7ef6b63333e98"
@ -1703,6 +1757,19 @@ hast-util-to-jsx-runtime@^2.0.0:
unist-util-position "^5.0.0" unist-util-position "^5.0.0"
vfile-message "^4.0.0" vfile-message "^4.0.0"
hast-util-to-parse5@^8.0.0:
version "8.0.1"
resolved "https://registry.npmmirror.com/hast-util-to-parse5/-/hast-util-to-parse5-8.0.1.tgz#95aa391cc0514b4951418d01c883d1038af42f5d"
integrity sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==
dependencies:
"@types/hast" "^3.0.0"
comma-separated-tokens "^2.0.0"
devlop "^1.0.0"
property-information "^7.0.0"
space-separated-tokens "^2.0.0"
web-namespaces "^2.0.0"
zwitch "^2.0.0"
hast-util-whitespace@^3.0.0: hast-util-whitespace@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.npmmirror.com/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz#7778ed9d3c92dd9e8c5c8f648a49c21fc51cb621" resolved "https://registry.npmmirror.com/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz#7778ed9d3c92dd9e8c5c8f648a49c21fc51cb621"
@ -1710,6 +1777,17 @@ hast-util-whitespace@^3.0.0:
dependencies: dependencies:
"@types/hast" "^3.0.0" "@types/hast" "^3.0.0"
hastscript@^9.0.0:
version "9.0.1"
resolved "https://registry.npmmirror.com/hastscript/-/hastscript-9.0.1.tgz#dbc84bef6051d40084342c229c451cd9dc567dff"
integrity sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==
dependencies:
"@types/hast" "^3.0.0"
comma-separated-tokens "^2.0.0"
hast-util-parse-selector "^4.0.0"
property-information "^7.0.0"
space-separated-tokens "^2.0.0"
hermes-estree@0.25.1: hermes-estree@0.25.1:
version "0.25.1" version "0.25.1"
resolved "https://registry.npmmirror.com/hermes-estree/-/hermes-estree-0.25.1.tgz#6aeec17d1983b4eabf69721f3aa3eb705b17f480" resolved "https://registry.npmmirror.com/hermes-estree/-/hermes-estree-0.25.1.tgz#6aeec17d1983b4eabf69721f3aa3eb705b17f480"
@ -1732,6 +1810,11 @@ html-url-attributes@^3.0.0:
resolved "https://registry.npmmirror.com/html-url-attributes/-/html-url-attributes-3.0.1.tgz#83b052cd5e437071b756cd74ae70f708870c2d87" resolved "https://registry.npmmirror.com/html-url-attributes/-/html-url-attributes-3.0.1.tgz#83b052cd5e437071b756cd74ae70f708870c2d87"
integrity sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ== integrity sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==
html-void-elements@^3.0.0:
version "3.0.0"
resolved "https://registry.npmmirror.com/html-void-elements/-/html-void-elements-3.0.0.tgz#fc9dbd84af9e747249034d4d62602def6517f1d7"
integrity sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==
ieee754@^1.2.1: ieee754@^1.2.1:
version "1.2.1" version "1.2.1"
resolved "https://registry.npmmirror.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" resolved "https://registry.npmmirror.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
@ -2556,6 +2639,13 @@ parse-entities@^4.0.0:
is-decimal "^2.0.0" is-decimal "^2.0.0"
is-hexadecimal "^2.0.0" is-hexadecimal "^2.0.0"
parse5@^7.0.0:
version "7.3.0"
resolved "https://registry.npmmirror.com/parse5/-/parse5-7.3.0.tgz#d7e224fa72399c7a175099f45fc2ad024b05ec05"
integrity sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==
dependencies:
entities "^6.0.0"
path-exists@^4.0.0: path-exists@^4.0.0:
version "4.0.0" version "4.0.0"
resolved "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" resolved "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
@ -2739,6 +2829,23 @@ readdirp@~3.6.0:
dependencies: dependencies:
picomatch "^2.2.1" picomatch "^2.2.1"
rehype-raw@^7.0.0:
version "7.0.0"
resolved "https://registry.npmmirror.com/rehype-raw/-/rehype-raw-7.0.0.tgz#59d7348fd5dbef3807bbaa1d443efd2dd85ecee4"
integrity sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==
dependencies:
"@types/hast" "^3.0.0"
hast-util-raw "^9.0.0"
vfile "^6.0.0"
rehype-sanitize@^6.0.0:
version "6.0.0"
resolved "https://registry.npmmirror.com/rehype-sanitize/-/rehype-sanitize-6.0.0.tgz#16e95f4a67a69cbf0f79e113c8e0df48203db73c"
integrity sha512-CsnhKNsyI8Tub6L4sm5ZFsme4puGfc6pYylvXo1AeqaGbjOYyzNv3qZPwvs0oMJ39eryyeOdmxwUIo94IpEhqg==
dependencies:
"@types/hast" "^3.0.0"
hast-util-sanitize "^5.0.0"
remark-gfm@^4.0.1: remark-gfm@^4.0.1:
version "4.0.1" version "4.0.1"
resolved "https://registry.npmmirror.com/remark-gfm/-/remark-gfm-4.0.1.tgz#33227b2a74397670d357bf05c098eaf8513f0d6b" resolved "https://registry.npmmirror.com/remark-gfm/-/remark-gfm-4.0.1.tgz#33227b2a74397670d357bf05c098eaf8513f0d6b"
@ -3197,6 +3304,14 @@ utility-types@^3.11.0:
resolved "https://registry.npmmirror.com/utility-types/-/utility-types-3.11.0.tgz#607c40edb4f258915e901ea7995607fdf319424c" resolved "https://registry.npmmirror.com/utility-types/-/utility-types-3.11.0.tgz#607c40edb4f258915e901ea7995607fdf319424c"
integrity sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw== integrity sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==
vfile-location@^5.0.0:
version "5.0.3"
resolved "https://registry.npmmirror.com/vfile-location/-/vfile-location-5.0.3.tgz#cb9eacd20f2b6426d19451e0eafa3d0a846225c3"
integrity sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==
dependencies:
"@types/unist" "^3.0.0"
vfile "^6.0.0"
vfile-message@^4.0.0: vfile-message@^4.0.0:
version "4.0.3" version "4.0.3"
resolved "https://registry.npmmirror.com/vfile-message/-/vfile-message-4.0.3.tgz#87b44dddd7b70f0641c2e3ed0864ba73e2ea8df4" resolved "https://registry.npmmirror.com/vfile-message/-/vfile-message-4.0.3.tgz#87b44dddd7b70f0641c2e3ed0864ba73e2ea8df4"
@ -3227,6 +3342,11 @@ vite@^7.3.1:
optionalDependencies: optionalDependencies:
fsevents "~2.3.3" fsevents "~2.3.3"
web-namespaces@^2.0.0:
version "2.0.1"
resolved "https://registry.npmmirror.com/web-namespaces/-/web-namespaces-2.0.1.tgz#1010ff7c650eccb2592cebeeaf9a1b253fd40692"
integrity sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==
webgl-constants@^1.1.1: webgl-constants@^1.1.1:
version "1.1.1" version "1.1.1"
resolved "https://registry.npmmirror.com/webgl-constants/-/webgl-constants-1.1.1.tgz#f9633ee87fea56647a60b9ce735cbdfb891c6855" resolved "https://registry.npmmirror.com/webgl-constants/-/webgl-constants-1.1.1.tgz#f9633ee87fea56647a60b9ce735cbdfb891c6855"