From 5060c250c0e96610f613fbbdf062fc531f725d9f Mon Sep 17 00:00:00 2001 From: "mula.liu" Date: Mon, 2 Mar 2026 15:51:47 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0html=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/App.css | 25 ++ frontend/src/App.tsx | 30 ++- frontend/src/i18n/dashboard.en.ts | 3 + frontend/src/i18n/dashboard.zh-cn.ts | 3 + .../modules/dashboard/BotDashboardModule.tsx | 215 +++++++++++++----- 5 files changed, 214 insertions(+), 62 deletions(-) diff --git a/frontend/src/App.css b/frontend/src/App.css index 75d71cc..c119e36 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -794,6 +794,31 @@ body { gap: 4px; } +.modal-title-row.modal-title-with-close { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 10px; + position: relative; + padding-right: 42px; + min-height: 28px; +} + +.modal-title-main { + min-width: 0; + display: grid; + gap: 4px; +} + +.modal-title-actions { + position: absolute; + right: 0; + top: 0; + display: inline-flex; + align-items: center; + gap: 6px; +} + .modal-sub { color: var(--subtitle); font-size: 12px; diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index abf11b3..f45154a 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,5 +1,5 @@ import { useEffect, useMemo, useState } from 'react'; -import { Boxes, MoonStar, Sparkles, SunMedium } from 'lucide-react'; +import { MoonStar, SunMedium, X } from 'lucide-react'; import { useAppStore, } from './store/appStore'; @@ -106,11 +106,15 @@ function App() { {!urlView.compactMode && showImageFactory && (
setShowImageFactory(false)}>
e.stopPropagation()}> -
-

{t.nav.images.title}

- +
+
+

{t.nav.images.title}

+
+
+ +
@@ -122,11 +126,15 @@ function App() { {!urlView.compactMode && showCreateWizard && (
setShowCreateWizard(false)}>
e.stopPropagation()}> -
-

{t.nav.onboarding.title}

- +
+
+

{t.nav.onboarding.title}

+
+
+ +
@@ -229,7 +235,7 @@ function isOfficePath(path: string) { function isPreviewableWorkspacePath(path: string) { const normalized = String(path || '').trim().toLowerCase(); - return ['.md', '.json', '.log', '.txt', '.csv', '.pdf', '.png', '.jpg', '.jpeg', '.webp', '.doc', '.docx', '.xls', '.xlsx', '.xlsm', '.ppt', '.pptx', '.odt', '.ods', '.odp', '.wps'].some((ext) => + return ['.md', '.json', '.log', '.txt', '.csv', '.html', '.htm', '.pdf', '.png', '.jpg', '.jpeg', '.webp', '.doc', '.docx', '.xls', '.xlsx', '.xlsm', '.ppt', '.pptx', '.odt', '.ods', '.odp', '.wps'].some((ext) => normalized.endsWith(ext), ); } @@ -238,7 +244,7 @@ function workspaceFileAction(path: string): 'preview' | 'download' | 'unsupporte const normalized = String(path || '').trim(); if (!normalized) return 'unsupported'; if (isPdfPath(normalized) || isOfficePath(normalized)) return 'download'; - if (isImagePath(normalized)) return 'preview'; + if (isImagePath(normalized) || isHtmlPath(normalized)) return 'preview'; const lower = normalized.toLowerCase(); if (['.md', '.json', '.log', '.txt', '.csv'].some((ext) => lower.endsWith(ext))) return 'preview'; return 'unsupported'; @@ -269,7 +275,7 @@ function decorateWorkspacePathsForMarkdown(text: string) { '[$1]($2)', ); const workspacePathPattern = - /\/root\/\.nanobot\/workspace\/[^\n\r<>"'`]+?\.(?:md|json|log|txt|csv|pdf|png|jpg|jpeg|webp|doc|docx|xls|xlsx|xlsm|ppt|pptx|odt|ods|odp|wps)\b/gi; + /\/root\/\.nanobot\/workspace\/[^\n\r<>"'`]+?\.(?:md|json|log|txt|csv|html|htm|pdf|png|jpg|jpeg|webp|doc|docx|xls|xlsx|xlsm|ppt|pptx|odt|ods|odp|wps)\b/gi; return normalizedExistingLinks.replace(workspacePathPattern, (fullPath) => { const normalized = normalizeDashboardAttachmentPath(fullPath); if (!normalized) return fullPath; @@ -433,6 +439,33 @@ export function BotDashboardModule({ link.click(); link.remove(); }; + const copyWorkspacePreviewUrl = async (filePath: string) => { + const normalized = String(filePath || '').trim(); + if (!selectedBotId || !normalized) return; + const hrefRaw = buildWorkspaceDownloadHref(normalized, false); + const href = (() => { + try { + return new URL(hrefRaw, window.location.origin).href; + } catch { + return hrefRaw; + } + })(); + try { + if (navigator.clipboard?.writeText) { + await navigator.clipboard.writeText(href); + } else { + const ta = document.createElement('textarea'); + ta.value = href; + document.body.appendChild(ta); + ta.select(); + document.execCommand('copy'); + ta.remove(); + } + notify(t.urlCopied, { tone: 'success' }); + } catch { + notify(t.urlCopyFail, { tone: 'error' }); + } + }; const openWorkspacePathFromChat = (path: string) => { const normalized = String(path || '').trim(); if (!normalized) return; @@ -454,7 +487,7 @@ export function BotDashboardModule({ const source = String(text || ''); if (!source) return [source]; const pattern = - /\[(\/root\/\.nanobot\/workspace\/[^\]]+?\.(?:md|json|log|txt|csv|pdf|png|jpg|jpeg|webp|doc|docx|xls|xlsx|xlsm|ppt|pptx|odt|ods|odp|wps))\]\((https:\/\/workspace\.local\/open\/[^)\s]+)\)|\/root\/\.nanobot\/workspace\/[^\n\r<>"'`]+?\.(?:md|json|log|txt|csv|pdf|png|jpg|jpeg|webp|doc|docx|xls|xlsx|xlsm|ppt|pptx|odt|ods|odp|wps)\b|https:\/\/workspace\.local\/open\/[^\s)]+/gi; + /\[(\/root\/\.nanobot\/workspace\/[^\]]+?\.(?:md|json|log|txt|csv|html|htm|pdf|png|jpg|jpeg|webp|doc|docx|xls|xlsx|xlsm|ppt|pptx|odt|ods|odp|wps))\]\((https:\/\/workspace\.local\/open\/[^)\s]+)\)|\/root\/\.nanobot\/workspace\/[^\n\r<>"'`]+?\.(?:md|json|log|txt|csv|html|htm|pdf|png|jpg|jpeg|webp|doc|docx|xls|xlsx|xlsm|ppt|pptx|odt|ods|odp|wps)\b|https:\/\/workspace\.local\/open\/[^\s)]+/gi; const nodes: ReactNode[] = []; let lastIndex = 0; let matchIndex = 0; @@ -885,6 +918,20 @@ export function BotDashboardModule({ ext: fileExt ? `.${fileExt}` : '', isMarkdown: false, isImage: true, + isHtml: false, + }); + return; + } + if (isHtmlPath(normalizedPath)) { + const fileExt = (normalizedPath.split('.').pop() || '').toLowerCase(); + setWorkspacePreview({ + path: normalizedPath, + content: '', + truncated: false, + ext: fileExt ? `.${fileExt}` : '', + isMarkdown: false, + isImage: false, + isHtml: true, }); return; } @@ -910,6 +957,7 @@ export function BotDashboardModule({ ext: textExt ? `.${textExt}` : '', isMarkdown: textExt === 'md' || Boolean(res.data.is_markdown), isImage: false, + isHtml: false, }); } catch (error: any) { const msg = error?.response?.data?.detail || t.fileReadFail; @@ -2102,9 +2150,16 @@ export function BotDashboardModule({ {showBaseModal && (
setShowBaseModal(false)}>
e.stopPropagation()}> -
-

{t.baseConfig}

- {t.baseConfigSub} +
+
+

{t.baseConfig}

+ {t.baseConfigSub} +
+
+ +
@@ -2153,7 +2208,16 @@ export function BotDashboardModule({ {showParamModal && (
setShowParamModal(false)}>
e.stopPropagation()}> -

{t.modelParams}

+
+
+

{t.modelParams}

+
+
+ +
+
setEditForm((p) => ({ ...p, temperature: clampTemperature(Number(e.target.value)) }))} /> @@ -2175,7 +2239,16 @@ export function BotDashboardModule({ {showChannelModal && (
setShowChannelModal(false)}>
e.stopPropagation()}> -

{lc.wizardSectionTitle}

+
+
+

{lc.wizardSectionTitle}

+
+
+ +
+
{lc.wizardSectionDesc}
@@ -2278,10 +2351,6 @@ export function BotDashboardModule({
-
- {lc.wizardSectionDesc} - -
)} @@ -2289,7 +2358,16 @@ export function BotDashboardModule({ {showSkillsModal && (
setShowSkillsModal(false)}>
e.stopPropagation()}> -

{t.skillsPanel}

+
+
+

{t.skillsPanel}

+
+
+ +
+
{botSkills.length === 0 ? (
{t.skillsEmpty}
@@ -2338,10 +2416,6 @@ export function BotDashboardModule({ {t.zipOnlyHint}
-
- - -
)} @@ -2349,7 +2423,16 @@ export function BotDashboardModule({ {showEnvParamsModal && (
setShowEnvParamsModal(false)}>
e.stopPropagation()}> -

{t.envParams}

+
+
+

{t.envParams}

+
+
+ +
+
{t.envParamsDesc}
{envEntries.length === 0 ? ( @@ -2438,17 +2521,24 @@ export function BotDashboardModule({ {showCronModal && (
setShowCronModal(false)}>
e.stopPropagation()}> -
-

{t.cronViewer}

- +
+
+

{t.cronViewer}

+
+
+ + +
{cronLoading ? (
{t.cronLoading}
@@ -2502,10 +2592,6 @@ export function BotDashboardModule({ })}
)} -
- - -
)} @@ -2513,7 +2599,16 @@ export function BotDashboardModule({ {showAgentModal && (
setShowAgentModal(false)}>
e.stopPropagation()}> -

{t.agentFiles}

+
+
+

{t.agentFiles}

+
+
+ +
+
{(['AGENTS', 'SOUL', 'USER', 'TOOLS', 'IDENTITY'] as AgentTab[]).map((tab) => ( @@ -2533,16 +2628,19 @@ export function BotDashboardModule({ {showRuntimeActionModal && (
setShowRuntimeActionModal(false)}>
e.stopPropagation()}> -
-

{t.lastAction}

+
+
+

{t.lastAction}

+
+
+ +
{runtimeAction}
-
- - -
)} @@ -2581,6 +2679,12 @@ export function BotDashboardModule({ src={`${APP_ENDPOINTS.apiBase}/bots/${selectedBotId}/workspace/download?path=${encodeURIComponent(workspacePreview.path)}`} alt={workspacePreview.path.split('/').pop() || 'workspace-image'} /> + ) : workspacePreview.isHtml ? ( +