diff --git a/src/frontend/client/eslint-suppressions.json b/src/frontend/client/eslint-suppressions.json index 89615c358..6df332f13 100644 --- a/src/frontend/client/eslint-suppressions.json +++ b/src/frontend/client/eslint-suppressions.json @@ -882,11 +882,6 @@ "count": 1 } }, - "src/components/FileListRow.tsx": { - "no-restricted-syntax": { - "count": 11 - } - }, "src/components/Files/ActionButton.tsx": { "@typescript-eslint/no-unused-vars": { "count": 1 diff --git a/src/frontend/client/src/components/Chat/AiMessageBubble.tsx b/src/frontend/client/src/components/Chat/AiMessageBubble.tsx index b30d7b501..0ae4763c2 100644 --- a/src/frontend/client/src/components/Chat/AiMessageBubble.tsx +++ b/src/frontend/client/src/components/Chat/AiMessageBubble.tsx @@ -33,7 +33,7 @@ import { } from "~/components/Chat/MessageSelection"; import { copyText, cn } from "~/utils"; import type { AgentEvent, ChatMessage } from "~/api/chatApi"; -import { getFileTypebyFileName, isImageFileName } from "~/components/ui/icon/File/FileIcon"; +import { getFileTypeIcon, isImageFileName } from "~/components/ui/icon/File/FileIcon"; import { MessageImage } from "~/components/Chat/Messages/Content/MessageImage"; // Transient/retryable backend error codes surfaced by daily-mode chat — LLM rate @@ -43,35 +43,6 @@ import { MessageImage } from "~/components/Chat/Messages/Content/MessageImage"; // has to reach the calm "busy" card rather than the red failure one. const RETRYABLE_ERROR_CODES = new Set([12046, 429, 503, 10540, 12045]); -// Map an uploaded file's extension to a bisheng outlined file-type icon. -// Anything not listed falls back to the generic Outlined.File icon. -const FILE_TYPE_ICONS: Record = { - // FileExcel - xls: Outlined.FileExcel, - xlsx: Outlined.FileExcel, - csv: Outlined.FileExcel, - et: Outlined.FileExcel, - // FilePdf - pdf: Outlined.FilePdf, - ppt: Outlined.FilePdf, - dps: Outlined.FilePdf, - // FileTxt - txt: Outlined.FileTxt, - // FileWord - doc: Outlined.FileWord, - docx: Outlined.FileWord, - wps: Outlined.FileWord, - // FileImage - png: Outlined.FileImage, - jpg: Outlined.FileImage, - jpeg: Outlined.FileImage, - bmp: Outlined.FileImage, - // FileEditing - md: Outlined.FileEditing, - // File (generic) - html: Outlined.File, -}; - /** * Uploaded-file list for a user message: a type icon + filename per row, never a * content preview. Stacks vertically and scrolls past 120px. A linear-gradient @@ -133,8 +104,7 @@ function UploadedFileList({ files, conversationId }: { files: any[]; conversatio > {others.map((file, i) => { const fileName = file.name || file.file_name || "File"; - const fileType = getFileTypebyFileName(fileName); - const FileTypeIcon = FILE_TYPE_ICONS[fileType] ?? Outlined.File; + const FileTypeIcon = getFileTypeIcon(fileName); return (
diff --git a/src/frontend/client/src/components/ui/icon/File/FileIcon.tsx b/src/frontend/client/src/components/ui/icon/File/FileIcon.tsx index d5c2f017c..b7c869b24 100644 --- a/src/frontend/client/src/components/ui/icon/File/FileIcon.tsx +++ b/src/frontend/client/src/components/ui/icon/File/FileIcon.tsx @@ -1,3 +1,4 @@ +import { Outlined } from "bisheng-icons"; import { BookType, File, FileMinus, Heading, Image, Loader2, Table2 } from "lucide-react"; import React from "react"; @@ -135,6 +136,39 @@ const getSizeClass = (size: 'sm' | 'md' | 'lg') => { export const getFileTypebyFileName = (fileName: string) => { return fileName ? fileName.split('.').pop()?.toLocaleLowerCase() as FileType : ''; } +// Map an uploaded file's extension to a bisheng outlined file-type icon. +// Anything not listed falls back to the generic Outlined.File icon. +const FILE_TYPE_ICONS: Record = { + // FileExcel + xls: Outlined.FileExcel, + xlsx: Outlined.FileExcel, + csv: Outlined.FileExcel, + et: Outlined.FileExcel, + // FilePdf + pdf: Outlined.FilePdf, + ppt: Outlined.FilePdf, + dps: Outlined.FilePdf, + // FileTxt + txt: Outlined.FileTxt, + // FileWord + doc: Outlined.FileWord, + docx: Outlined.FileWord, + wps: Outlined.FileWord, + // FileImage + png: Outlined.FileImage, + jpg: Outlined.FileImage, + jpeg: Outlined.FileImage, + bmp: Outlined.FileImage, + // FileEditing + md: Outlined.FileEditing, + // File (generic) + html: Outlined.File, +}; + +/** Outlined icon component for a file name, generic `File` when unmapped. */ +export const getFileTypeIcon = (fileName: string): typeof Outlined.File => + FILE_TYPE_ICONS[getFileTypebyFileName(fileName)] ?? Outlined.File; + // Whether an attachment should render as a picture. Judged by filename rather // than any stored MIME type: older messages don't carry one, and the upload // endpoints disagree on where they put it — the name is always there, and this diff --git a/src/frontend/client/src/pages/knowledge/SpaceDetail/KnowledgeSpaceShareDialog.tsx b/src/frontend/client/src/pages/knowledge/SpaceDetail/KnowledgeSpaceShareDialog.tsx index d9970c418..2fa0bab0e 100644 --- a/src/frontend/client/src/pages/knowledge/SpaceDetail/KnowledgeSpaceShareDialog.tsx +++ b/src/frontend/client/src/pages/knowledge/SpaceDetail/KnowledgeSpaceShareDialog.tsx @@ -1,3 +1,4 @@ +import { Outlined } from "bisheng-icons"; import { useCallback, useEffect, useMemo, useState } from "react"; import { INCLUDE_CHILDREN_CHECKBOX_CLASS, @@ -23,12 +24,36 @@ import { TabsList, TabsTrigger, } from "~/components/ui"; +import { getFileTypeIcon } from "~/components/ui/icon/File/FileIcon"; import { useLocalize } from "~/hooks"; import { useRecoilValue } from "recoil"; import store from "~/store"; import { getGrantableRelationModels } from "~/api/permission"; import type { RelationModel, ResourceType } from "~/api/permission"; +/** + * The resource name sits here instead of in the dialog title: file names can run + * past 100 characters, and appended to the title they wrapped under the close + * button. One truncated line keeps the header height fixed; the native tooltip + * still exposes the full name. Icon + name, no container fill — same shape as + * the uploaded-file rows in chat. + */ +function ResourceContextBar({ name, resourceType }: { name: string; resourceType: ResourceType }) { + const Icon = + resourceType === "folder" + ? Outlined.FolderClose + : resourceType === "knowledge_space" || resourceType === "knowledge_library" + ? Outlined.Book + : getFileTypeIcon(name); + + return ( +
+ + {name} +
+ ); +} + interface KnowledgeSpaceShareDialogProps { open: boolean; onOpenChange: (open: boolean) => void; @@ -100,8 +125,6 @@ export function KnowledgeSpaceShareDialog({ setGrantDialogOpen(false); }, [grantSubjectType]); - const dialogTitle = `${localize("com_permission.dialog_title")} - ${resourceName}`; - // F033: department spaces drop the user-group dimension. The list view and // the grant dialog share this array, so both lose the tab at once. const SUBJECT_TABS = useMemo - {dialogTitle} + + {localize("com_permission.dialog_title")} +
+ {permissionPanel}
@@ -185,11 +211,12 @@ export function KnowledgeSpaceShareDialog({ - {localize("com_permission.tab_grant")} - {resourceName} + {localize("com_permission.tab_grant")}
+
{SUBJECT_TABS.map((tab) => (