修正文件处理

main
mula.liu 2026-03-02 13:55:35 +08:00
parent 500dc23489
commit 20ab8b28e7
2 changed files with 44 additions and 10 deletions

View File

@ -462,6 +462,7 @@
.ops-attach-link {
display: inline-flex;
align-items: center;
gap: 6px;
max-width: 100%;
padding: 4px 8px;
border: 1px solid var(--line);
@ -472,6 +473,14 @@
background: color-mix(in oklab, var(--panel) 78%, transparent);
}
.ops-attach-link-icon {
flex: 0 0 auto;
}
.ops-attach-link-name {
min-width: 0;
}
.ops-attach-link:hover {
border-color: color-mix(in oklab, var(--brand) 56%, var(--line) 44%);
}
@ -1409,11 +1418,14 @@
max-height: calc(100vh - 170px);
}
.workspace-preview-header {
.modal-title-row.workspace-preview-header {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 10px;
position: relative;
padding-right: 72px;
min-height: 28px;
}
.workspace-preview-header-text {
@ -1427,6 +1439,9 @@
align-items: center;
gap: 6px;
flex: 0 0 auto;
position: absolute;
top: 0;
right: 0;
}
.workspace-preview-body {

View File

@ -1,6 +1,6 @@
import { useEffect, useMemo, useRef, useState, type AnchorHTMLAttributes, type ChangeEvent, type KeyboardEvent, type ReactNode } from 'react';
import axios from 'axios';
import { Activity, Boxes, Check, Clock3, EllipsisVertical, Eye, EyeOff, FileText, FolderOpen, Hammer, Maximize2, MessageSquareText, Minimize2, Paperclip, Plus, Power, PowerOff, RefreshCw, Repeat2, Save, Settings2, SlidersHorizontal, TriangleAlert, Trash2, UserRound, Waypoints, X } from 'lucide-react';
import { Activity, Boxes, Check, Clock3, Download, EllipsisVertical, Eye, EyeOff, FileText, FolderOpen, Hammer, Maximize2, MessageSquareText, Minimize2, Paperclip, Plus, Power, PowerOff, RefreshCw, Repeat2, Save, Settings2, SlidersHorizontal, TriangleAlert, Trash2, UserRound, Waypoints, X } from 'lucide-react';
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import rehypeRaw from 'rehype-raw';
@ -234,6 +234,16 @@ function isPreviewableWorkspacePath(path: string) {
);
}
function workspaceFileAction(path: string): 'preview' | 'download' | 'unsupported' {
const normalized = String(path || '').trim();
if (!normalized) return 'unsupported';
if (isPdfPath(normalized) || isOfficePath(normalized)) return 'download';
if (isImagePath(normalized)) return 'preview';
const lower = normalized.toLowerCase();
if (['.md', '.json', '.log', '.txt', '.csv'].some((ext) => lower.endsWith(ext))) return 'preview';
return 'unsupported';
}
const WORKSPACE_LINK_PREFIX = 'https://workspace.local/open/';
function buildWorkspaceLink(path: string) {
@ -426,15 +436,19 @@ export function BotDashboardModule({
const openWorkspacePathFromChat = (path: string) => {
const normalized = String(path || '').trim();
if (!normalized) return;
if (isPdfPath(normalized) || isOfficePath(normalized)) {
const action = workspaceFileAction(normalized);
if (action === 'download') {
triggerWorkspaceFileDownload(normalized);
return;
}
if (!isPreviewableWorkspacePath(normalized)) {
if (action === 'preview') {
void openWorkspaceFilePreview(normalized);
return;
}
if (!isPreviewableWorkspacePath(normalized) || action === 'unsupported') {
notify(fileNotPreviewableLabel, { tone: 'warning' });
return;
}
void openWorkspaceFilePreview(normalized);
};
const renderWorkspaceAwareText = (text: string, keyPrefix: string): ReactNode[] => {
const source = String(text || '');
@ -723,6 +737,7 @@ export function BotDashboardModule({
<div className="ops-chat-attachments">
{(item.attachments || []).map((rawPath) => {
const filePath = normalizeDashboardAttachmentPath(rawPath);
const fileAction = workspaceFileAction(filePath);
const filename = filePath.split('/').pop() || filePath;
return (
<a
@ -731,14 +746,18 @@ export function BotDashboardModule({
href="#"
onClick={(event) => {
event.preventDefault();
if (isPdfPath(filePath) || isOfficePath(filePath)) {
triggerWorkspaceFileDownload(filePath);
return;
}
openWorkspacePathFromChat(filePath);
}}
title={fileAction === 'download' ? t.download : fileAction === 'preview' ? t.previewTitle : t.fileNotPreviewable}
>
{filename}
{fileAction === 'download' ? (
<Download size={12} className="ops-attach-link-icon" />
) : fileAction === 'preview' ? (
<Eye size={12} className="ops-attach-link-icon" />
) : (
<FileText size={12} className="ops-attach-link-icon" />
)}
<span className="ops-attach-link-name">{filename}</span>
</a>
);
})}