mirror of
https://github.com/dataelement/bisheng.git
synced 2026-08-28 17:14:44 +08:00
refactor(client): share the file-type icon map, drop two dead modules
AiMessageBubble carried its own extension → bisheng-icon table, a copy of what FileIcon already knows. FileIcon exports getFileTypeIcon and the bubble consumes it, so a new file type is registered once. Also deletes FileListRow and the knowledge mock: neither has had a call site for a while (the file list renders through FileTable / FileCard). Suppressions shrink accordingly. The share dialog picks up the icon import while it is here. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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<string, typeof Outlined.File> = {
|
||||
// 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 (
|
||||
<div key={i} className="flex shrink-0 items-center gap-1 text-[#999999]">
|
||||
<FileTypeIcon size={12} className="shrink-0 text-[#CCCCCC]" />
|
||||
|
||||
@@ -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<string, typeof Outlined.File> = {
|
||||
// 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
|
||||
|
||||
@@ -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 (
|
||||
<div className="mb-3 flex shrink-0 items-center gap-1.5 text-text-2" title={name}>
|
||||
<Icon size={14} className="shrink-0 text-text-3" />
|
||||
<span className="truncate text-body-sm">{name}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
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<Array<{
|
||||
@@ -172,10 +195,13 @@ export function KnowledgeSpaceShareDialog({
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className={PERMISSION_DIALOG_CONTENT_CLASS}>
|
||||
<DialogHeader className="shrink-0 text-left">
|
||||
<DialogTitle className="text-left">{dialogTitle}</DialogTitle>
|
||||
<DialogTitle className="text-left">
|
||||
{localize("com_permission.dialog_title")}
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="mt-4 flex min-h-0 flex-1 flex-col overflow-hidden">
|
||||
<ResourceContextBar name={resourceName} resourceType={resourceType} />
|
||||
{permissionPanel}
|
||||
</div>
|
||||
</DialogContent>
|
||||
@@ -185,11 +211,12 @@ export function KnowledgeSpaceShareDialog({
|
||||
<DialogContent className={PERMISSION_DIALOG_CONTENT_CLASS}>
|
||||
<DialogHeader className="shrink-0 text-left">
|
||||
<DialogTitle className="text-left">
|
||||
{localize("com_permission.tab_grant")} - {resourceName}
|
||||
{localize("com_permission.tab_grant")}
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="user-manger mt-4 flex min-h-0 flex-1 flex-col overflow-hidden">
|
||||
<ResourceContextBar name={resourceName} resourceType={resourceType} />
|
||||
<div className="flex items-center gap-3">
|
||||
<div className={`inline-flex items-center justify-center ${SUBJECT_TAB_LIST_CLASS}`}>
|
||||
{SUBJECT_TABS.map((tab) => (
|
||||
|
||||
Reference in New Issue
Block a user