@infinilabs/doc-detail 0.0.0 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,63 +1,55 @@
1
+ import { ButtonProps } from 'antd';
1
2
  import { FC } from 'react';
3
+ import { HTMLAttributes } from 'react';
4
+ import { ReactNode } from 'react';
5
+
6
+ export declare const ActionButton: FC<ActionButtonProps>;
7
+
8
+ declare type ActionButtonProps = ButtonProps;
2
9
 
3
10
  export declare const DocDetail: FC<DocDetailProps>;
4
11
 
5
- declare interface DocDetailProps {
12
+ declare interface DocDetailProps extends HTMLAttributes<HTMLDivElement> {
6
13
  data: {
7
- source: {
8
- type: string;
9
- name: string;
10
- id: string;
14
+ id?: string;
15
+ created?: ReactNode;
16
+ updated?: ReactNode;
17
+ _system?: {
18
+ owner_id?: string;
19
+ parent_path?: string;
20
+ tenant_id?: string;
11
21
  };
12
- category: string;
13
- categories: string[];
14
- cover: string;
15
- title: string;
16
- summary: string;
17
- type: string;
18
- lang: string;
19
- content: string;
20
- icon: string;
21
- thumbnail: string;
22
- tags: string[];
23
- url: string;
24
- size: number;
25
- owner: {
26
- avatar: string;
27
- username: string;
28
- userid: string;
22
+ metadata?: {
23
+ ai_insights?: string;
24
+ colors?: string[];
25
+ content_type?: MetadataContentType;
26
+ height?: number;
27
+ mime_type?: string;
28
+ users?: null | unknown;
29
+ width?: number;
29
30
  };
30
- metadata: {
31
- version: string;
32
- department: string;
33
- last_reviewed: string;
34
- file_extension: string;
35
- icon_link: string;
36
- has_thumbnail: boolean;
37
- kind: string;
38
- parents: string[];
39
- properties: Record<string, string>;
40
- spaces: string[];
41
- starred: boolean;
42
- driveId: string;
43
- thumbnail_link: string;
44
- video_media_metadata?: {
45
- durationMillis: string;
46
- width: number;
47
- height: number;
48
- };
49
- image_media_metadata?: {
50
- width: number;
51
- height: number;
52
- };
31
+ source?: {
32
+ type?: string;
33
+ name?: string;
34
+ id?: string;
53
35
  };
54
- last_updated_by: {
55
- user: {
56
- avatar: string;
57
- username: string;
58
- userid: string;
59
- };
60
- timestamp: string;
36
+ type?: string;
37
+ category?: string;
38
+ title?: string;
39
+ summary?: string;
40
+ icon?: string;
41
+ thumbnail?: string;
42
+ cover?: string;
43
+ tags?: string[];
44
+ url?: string;
45
+ size?: ReactNode;
46
+ owner: {
47
+ type?: string;
48
+ id?: string;
49
+ icon?: string;
50
+ title?: string;
51
+ subtitle?: string;
52
+ cover?: string;
61
53
  };
62
54
  };
63
55
  i18n?: {
@@ -74,6 +66,9 @@ declare interface DocDetailProps {
74
66
  size?: string;
75
67
  };
76
68
  };
69
+ extraButtons?: ReactNode[];
77
70
  }
78
71
 
72
+ declare type MetadataContentType = "image" | "video" | "markdown" | "pdf" | "docx" | "pptx" | "xlsx";
73
+
79
74
  export { }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@infinilabs/doc-detail",
3
3
  "private": false,
4
- "version": "0.0.0",
4
+ "version": "0.0.2",
5
5
  "type": "module",
6
6
  "main": "dist/doc-detail.cjs",
7
7
  "module": "dist/doc-detail.js",
@@ -22,9 +22,13 @@
22
22
  "preview": "vite preview"
23
23
  },
24
24
  "dependencies": {
25
+ "@ant-design/x-markdown": "^2.1.3",
25
26
  "antd": "^6.0.0",
26
27
  "clsx": "^2.1.1",
28
+ "docx-preview": "^0.3.7",
27
29
  "lucide-react": "^0.562.0",
30
+ "motion": "^12.23.26",
31
+ "pptx-preview": "^1.0.7",
28
32
  "react": "^19.2.0",
29
33
  "react-dom": "^19.2.0",
30
34
  "react-pdf": "^10.2.0",
@@ -46,5 +50,9 @@
46
50
  "antd": ">=5",
47
51
  "react": ">=17",
48
52
  "react-dom": ">=17"
53
+ },
54
+ "repository": {
55
+ "type": "git",
56
+ "url": "https://github.com/infinilabs/ci"
49
57
  }
50
58
  }
package/src/App.tsx CHANGED
@@ -1,64 +1,35 @@
1
- import { DocDetail } from "./components";
1
+ import { Bot } from "lucide-react";
2
+ import { ActionButton, DocDetail } from "./components";
2
3
 
3
4
  const App = () => {
4
5
  return (
5
6
  <div className="h-screen">
6
7
  <DocDetail
7
8
  data={{
9
+ type: "file",
8
10
  source: {
9
- type: "connector",
10
11
  name: "My Hugo Site",
11
- id: "e806831dacc3",
12
12
  },
13
13
  category: "report",
14
- categories: ["business", "quarterly_reports"],
15
- cover: "https://picsum.photos/seed/report-cover/640/360",
16
14
  title: "Q3 Business Report",
17
- summary: "An overview of the company financial performance for Q3.",
18
- type: "PDF",
19
- lang: "en",
20
- content:
21
- "This quarters revenue increased by 15%, driven by strong sales in the APAC region...",
22
15
  icon: "https://picsum.photos/seed/file-icon/40/40",
23
- thumbnail: "https://picsum.photos/seed/report-thumb/320/180",
24
- tags: ["finance", "quarterly", "business", "report"],
25
- url: "https://drive.google.com/file/d/abc123/view",
26
16
  size: 1048576,
27
- owner: {
28
- avatar: "https://picsum.photos/seed/user-avatar/64/64",
29
- username: "jdoe",
30
- userid: "user123",
31
- },
17
+ url: "http://192.168.3.181:9101/coco-server/test.pdf",
32
18
  metadata: {
33
- version: "1.2",
34
- department: "Finance",
35
- last_reviewed: "2024-10-20",
36
- file_extension: "pdf",
37
- icon_link: "https://picsum.photos/seed/filetype-icon/40/40",
38
- has_thumbnail: true,
39
- kind: "drive#file",
40
- parents: ["folder123"],
41
- properties: { shared: "true" },
42
- spaces: ["drive"],
43
- starred: false,
44
- driveId: "drive123",
45
- thumbnail_link: "https://picsum.photos/seed/file-thumb/320/180",
46
- video_media_metadata: {
47
- durationMillis: "60000",
48
- width: 1920,
49
- height: 1080,
50
- },
51
- image_media_metadata: { width: 1024, height: 768 },
19
+ content_type: "pdf",
20
+ ai_insights: `# AI Interpretation\n### Welcome to XMarkdown!\n- Project 1 \n- Project 2\n- Project 3`,
52
21
  },
53
- last_updated_by: {
54
- user: {
55
- avatar: "https://picsum.photos/seed/editor-avatar/64/64",
56
- username: "editor123",
57
- userid: "editor123@example.com",
58
- },
59
- timestamp: "2024-11-01T15:30:00Z",
22
+ owner: {
23
+ title: "Alice Johnson",
60
24
  },
25
+ created: "2026-01-09T02:30:10.188Z",
26
+ updated: "2026-01-09T02:30:10.188Z",
61
27
  }}
28
+ extraButtons={[
29
+ <ActionButton key="bot" icon={<Bot />}>
30
+ Continue Chat
31
+ </ActionButton>,
32
+ ]}
62
33
  />
63
34
  </div>
64
35
  );
@@ -0,0 +1,51 @@
1
+ import { Button, type ButtonProps } from "antd";
2
+ import { useState, type FC, type MouseEvent } from "react";
3
+ import { motion } from "motion/react";
4
+
5
+ export type ActionButtonProps = ButtonProps;
6
+
7
+ const ActionButton: FC<ActionButtonProps> = (props) => {
8
+ const { icon, children, onMouseOver, onMouseOut, ...rest } = props;
9
+
10
+ const [hovered, setHovered] = useState(false);
11
+
12
+ const handleMouseOver = (event: MouseEvent<HTMLButtonElement>) => {
13
+ setHovered(true);
14
+
15
+ onMouseOver?.(event);
16
+ };
17
+
18
+ const handleMouseOut = (event: MouseEvent<HTMLButtonElement>) => {
19
+ setHovered(false);
20
+
21
+ onMouseOut?.(event);
22
+ };
23
+
24
+ return (
25
+ <Button
26
+ {...rest}
27
+ color="primary"
28
+ variant="filled"
29
+ shape="round"
30
+ className="gap-0"
31
+ onMouseOver={handleMouseOver}
32
+ onMouseOut={handleMouseOut}
33
+ >
34
+ <span className="inline-flex items-center children:size-4">{icon}</span>
35
+
36
+ <motion.span
37
+ className="overflow-hidden"
38
+ initial={false}
39
+ animate={{
40
+ width: hovered ? "auto" : 0,
41
+ opacity: Number(hovered),
42
+ paddingLeft: Number(hovered) * 8,
43
+ }}
44
+ >
45
+ {children}
46
+ </motion.span>
47
+ </Button>
48
+ );
49
+ };
50
+
51
+ export default ActionButton;
@@ -1,99 +1,24 @@
1
1
  import type { FC } from "react";
2
- import type { DocDetailProps } from "../..";
3
2
  import { Collapse } from "antd";
4
3
 
4
+ import type { DocDetailProps } from "@/components/DocDetail";
5
+ import Markdown from "../Preview/components/Markdown";
6
+
5
7
  const AIInterpretation: FC<DocDetailProps> = (props) => {
6
- const { i18n } = props;
8
+ const { data, i18n } = props;
7
9
 
8
10
  return (
9
11
  <Collapse
10
12
  size="small"
11
13
  classNames={{
12
14
  root: "bg-transparent",
15
+ body: "p-4!",
13
16
  }}
14
17
  items={[
15
18
  {
16
19
  key: "ai-interpretation",
17
20
  label: i18n?.labels?.aiInterpretation ?? "AI Interpretation",
18
- children: (
19
- <p>
20
- Lorem ipsum dolor, sit amet consectetur adipisicing elit. Ad
21
- doloribus unde porro, est molestiae voluptates provident quam ex
22
- cumque quasi asperiores dicta vero. Ullam corrupti exercitationem
23
- rerum temporibus tempora fugit. Nihil impedit odit vel
24
- reprehenderit itaque voluptatum laborum dolore quae earum
25
- explicabo quibusdam laudantium officia, esse, recusandae ratione?
26
- Vero temporibus rem, quisquam facilis vel officia possimus nihil
27
- porro ad minus! Similique explicabo fugit consectetur modi nulla
28
- quasi unde vitae non eos, vel dolorem dolor voluptates veniam.
29
- Consectetur minima voluptatem quisquam ipsa facere! Maxime nam
30
- quae blanditiis architecto ipsa tempore qui. Pariatur earum
31
- debitis quis magnam temporibus impedit tempore reiciendis,
32
- exercitationem veritatis corrupti tempora et suscipit totam unde
33
- nulla quae perspiciatis molestiae harum dolores iusto corporis!
34
- Architecto explicabo facilis consequuntur neque. Fuga quisquam
35
- placeat fugit rerum nam laborum ad, debitis nemo magnam eius
36
- officia tenetur, ducimus incidunt velit assumenda nisi,
37
- praesentium perferendis quibusdam commodi! Ab tenetur minima ad
38
- quae aspernatur maiores! Nostrum rem voluptatibus ullam
39
- reiciendis, harum quod quas nesciunt id voluptatum natus quisquam?
40
- Quam rerum deleniti error labore impedit, molestiae sapiente
41
- maxime velit dolore. Cumque unde repellat eligendi nemo
42
- consectetur! Error porro non in! Inventore, numquam, quod quam
43
- tempore officiis placeat optio non ullam tenetur voluptates
44
- laborum ipsam dolores a eos vitae nemo saepe architecto culpa,
45
- soluta velit! Id, magni. Laborum consequuntur magni architecto
46
- velit, accusantium delectus alias, amet libero, voluptatibus iure
47
- fugit magnam corporis id quas ut suscipit necessitatibus ad nihil.
48
- Molestias nesciunt, dolore facere autem ratione voluptatum vero.
49
- Saepe, nihil asperiores necessitatibus delectus fugiat dolorem
50
- cum, temporibus fuga dolores ipsum officiis odio repellendus
51
- repudiandae similique incidunt quisquam. Beatae, illo! Iste
52
- mollitia dolor fuga repellendus, perferendis accusamus modi
53
- beatae! Ducimus repellat voluptate asperiores, accusantium omnis
54
- voluptatem beatae laudantium provident? Et sequi vel molestiae
55
- temporibus repellat nulla dolorem adipisci quod dignissimos
56
- aliquid! Assumenda, minima laboriosam. Voluptates amet nobis odit
57
- veritatis!Lorem ipsum dolor, sit amet consectetur adipisicing
58
- elit. Ad doloribus unde porro, est molestiae voluptates provident
59
- quam ex cumque quasi asperiores dicta vero. Ullam corrupti
60
- exercitationem rerum temporibus tempora fugit. Nihil impedit odit
61
- vel reprehenderit itaque voluptatum laborum dolore quae earum
62
- explicabo quibusdam laudantium officia, esse, recusandae ratione?
63
- Vero temporibus rem, quisquam facilis vel officia possimus nihil
64
- porro ad minus! Similique explicabo fugit consectetur modi nulla
65
- quasi unde vitae non eos, vel dolorem dolor voluptates veniam.
66
- Consectetur minima voluptatem quisquam ipsa facere! Maxime nam
67
- quae blanditiis architecto ipsa tempore qui. Pariatur earum
68
- debitis quis magnam temporibus impedit tempore reiciendis,
69
- exercitationem veritatis corrupti tempora et suscipit totam unde
70
- nulla quae perspiciatis molestiae harum dolores iusto corporis!
71
- Architecto explicabo facilis consequuntur neque. Fuga quisquam
72
- placeat fugit rerum nam laborum ad, debitis nemo magnam eius
73
- officia tenetur, ducimus incidunt velit assumenda nisi,
74
- praesentium perferendis quibusdam commodi! Ab tenetur minima ad
75
- quae aspernatur maiores! Nostrum rem voluptatibus ullam
76
- reiciendis, harum quod quas nesciunt id voluptatum natus quisquam?
77
- Quam rerum deleniti error labore impedit, molestiae sapiente
78
- maxime velit dolore. Cumque unde repellat eligendi nemo
79
- consectetur! Error porro non in! Inventore, numquam, quod quam
80
- tempore officiis placeat optio non ullam tenetur voluptates
81
- laborum ipsam dolores a eos vitae nemo saepe architecto culpa,
82
- soluta velit! Id, magni. Laborum consequuntur magni architecto
83
- velit, accusantium delectus alias, amet libero, voluptatibus iure
84
- fugit magnam corporis id quas ut suscipit necessitatibus ad nihil.
85
- Molestias nesciunt, dolore facere autem ratione voluptatum vero.
86
- Saepe, nihil asperiores necessitatibus delectus fugiat dolorem
87
- cum, temporibus fuga dolores ipsum officiis odio repellendus
88
- repudiandae similique incidunt quisquam. Beatae, illo! Iste
89
- mollitia dolor fuga repellendus, perferendis accusamus modi
90
- beatae! Ducimus repellat voluptate asperiores, accusantium omnis
91
- voluptatem beatae laudantium provident? Et sequi vel molestiae
92
- temporibus repellat nulla dolorem adipisci quod dignissimos
93
- aliquid! Assumenda, minima laboriosam. Voluptates amet nobis odit
94
- veritatis!
95
- </p>
96
- ),
21
+ children: <Markdown content={data?.metadata?.ai_insights} />,
97
22
  },
98
23
  ]}
99
24
  />
@@ -0,0 +1,37 @@
1
+ import type { DocDetailProps } from "@/components/DocDetail";
2
+ import { useEffect, useRef, type FC } from "react";
3
+ import { renderAsync } from "docx-preview";
4
+
5
+ interface DocxProps extends DocDetailProps {
6
+ url: string;
7
+ }
8
+
9
+ const Docx: FC<DocxProps> = (props) => {
10
+ const { url } = props;
11
+
12
+ const containerRef = useRef<HTMLDivElement>(null);
13
+
14
+ const renderDocx = async () => {
15
+ if (!containerRef.current) return;
16
+
17
+ const response = await fetch(url);
18
+
19
+ const arrayBuffer = await response.arrayBuffer();
20
+
21
+ containerRef.current.innerHTML = "";
22
+
23
+ renderAsync(arrayBuffer, containerRef.current!, void 0, {
24
+ inWrapper: false,
25
+ ignoreWidth: true,
26
+ ignoreHeight: true,
27
+ });
28
+ };
29
+
30
+ useEffect(() => {
31
+ renderDocx();
32
+ }, [url]);
33
+
34
+ return <div ref={containerRef} className="[&>.docx]:p-0!" />;
35
+ };
36
+
37
+ export default Docx;
@@ -0,0 +1,30 @@
1
+ import { XMarkdown, type XMarkdownProps } from "@ant-design/x-markdown";
2
+ import { useEffect, useState, type FC } from "react";
3
+
4
+ interface MarkdownProps extends XMarkdownProps {
5
+ url?: string;
6
+ }
7
+
8
+ const Markdown: FC<MarkdownProps> = (props) => {
9
+ const { url, ...rest } = props;
10
+
11
+ const [content, setContent] = useState(rest.content);
12
+
13
+ const fetchContent = async (url: string) => {
14
+ const response = await fetch(url);
15
+
16
+ const text = await response.text();
17
+
18
+ setContent(text);
19
+ };
20
+
21
+ useEffect(() => {
22
+ if (!url) return;
23
+
24
+ fetchContent(url);
25
+ }, [url]);
26
+
27
+ return <XMarkdown {...rest} content={content} />;
28
+ };
29
+
30
+ export default Markdown;
@@ -1,46 +1,35 @@
1
1
  import { Document, Page, pdfjs } from "react-pdf";
2
- import pdfFile from "./test.pdf";
3
- import { useState } from "react";
4
- import { cn } from "@/utils/cn";
2
+ import { useState, type FC } from "react";
3
+ import type { DocDetailProps } from "@/components/DocDetail";
4
+ import { Pagination } from "antd";
5
5
 
6
6
  pdfjs.GlobalWorkerOptions.workerSrc = `https://unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs`;
7
7
 
8
- const Pdf = () => {
8
+ interface PdfProps extends DocDetailProps {
9
+ url: string;
10
+ }
11
+
12
+ const Pdf: FC<PdfProps> = (props) => {
13
+ const { url } = props;
14
+
9
15
  const [numPages, setNumPages] = useState(0);
10
16
  const [pageNumber, setPageNumber] = useState(1);
11
17
 
12
18
  return (
13
19
  <div className="flex flex-col gap-2">
14
- <div className="text-text-secondary">搜索匹配结果</div>
15
-
16
- {numPages > 0 && (
17
- <div className="flex gap-1">
18
- {Array.from({ length: numPages }, (_, index) => {
19
- const page = index + 1;
20
-
21
- return (
22
- <div
23
- key={page}
24
- className={cn(
25
- "inline-flex items-center h-6 px-3 rounded-lg cursor-pointer bg-bg-layout text-text-secondary",
26
- {
27
- "text-primary bg-primary-bg": pageNumber === page,
28
- }
29
- )}
30
- onClick={() => {
31
- setPageNumber(page);
32
- }}
33
- >
34
- 第{page}页
35
- </div>
36
- );
37
- })}
38
- </div>
39
- )}
20
+ <div className="flex justify-end">
21
+ <Pagination
22
+ size="small"
23
+ pageSize={1}
24
+ total={numPages}
25
+ current={pageNumber}
26
+ onChange={(page) => setPageNumber(page)}
27
+ />
28
+ </div>
40
29
 
41
30
  <div className="border border-solid border-border rounded-lg overflow-hidden">
42
31
  <Document
43
- file={pdfFile}
32
+ file={url}
44
33
  onLoadSuccess={(pdf) => {
45
34
  setNumPages(pdf.numPages);
46
35
  }}
@@ -0,0 +1,37 @@
1
+ import { useEffect, useRef, type FC } from "react";
2
+ import { init } from "pptx-preview";
3
+
4
+ import type { DocDetailProps } from "@/components/DocDetail";
5
+
6
+ interface PptxProps extends DocDetailProps {
7
+ url: string;
8
+ }
9
+
10
+ const Pptx: FC<PptxProps> = (props) => {
11
+ const { url } = props;
12
+
13
+ const containerRef = useRef<HTMLDivElement>(null);
14
+
15
+ const renderPdf = async () => {
16
+ if (!containerRef.current) return;
17
+
18
+ const pptx = init(containerRef.current, {
19
+ width: 960,
20
+ height: 540,
21
+ });
22
+
23
+ const response = await fetch(url);
24
+
25
+ const arrayBuffer = await response.arrayBuffer();
26
+
27
+ pptx.preview(arrayBuffer);
28
+ };
29
+
30
+ useEffect(() => {
31
+ renderPdf();
32
+ }, [url]);
33
+
34
+ return <div ref={containerRef} />;
35
+ };
36
+
37
+ export default Pptx;
@@ -1,16 +1,49 @@
1
- import type { FC } from "react";
2
- import type { DocDetailProps } from "../..";
1
+ import { type FC } from "react";
2
+ import type {
3
+ DocDetailProps,
4
+ MetadataContentType,
5
+ } from "@/components/DocDetail";
3
6
  import { Collapse } from "antd";
4
7
  import Pdf from "./components/Pdf";
8
+ import Markdown from "./components/Markdown";
9
+ import Docx from "./components/Docx";
10
+ import Pptx from "./components/Pptx";
5
11
 
6
12
  const Preview: FC<DocDetailProps> = (props) => {
7
13
  const { data, i18n } = props;
8
14
 
15
+ const renderFile = (type: MetadataContentType, url: string) => {
16
+ if (type === "markdown") {
17
+ return <Markdown url={url} />;
18
+ }
19
+
20
+ if (type === "pdf") {
21
+ return <Pdf url={url} {...props} />;
22
+ }
23
+
24
+ if (type === "docx") {
25
+ return <Docx url={url} {...props} />;
26
+ }
27
+
28
+ if (type === "pptx") {
29
+ return <Pptx url={url} {...props} />;
30
+ }
31
+
32
+ return null;
33
+ };
34
+
9
35
  const renderContent = () => {
10
- const { type } = data;
36
+ const { url } = data;
37
+ const type = data?.metadata?.content_type;
38
+
39
+ if (!type || !url) return;
11
40
 
12
41
  if (type === "image") {
13
- return <img src={data.url} className="w-full" />;
42
+ return <img src={url} className="w-full" />;
43
+ }
44
+
45
+ if (type === "video") {
46
+ return <video src={url} className="w-full" controls />;
14
47
  }
15
48
 
16
49
  return (
@@ -19,12 +52,13 @@ const Preview: FC<DocDetailProps> = (props) => {
19
52
  defaultActiveKey={["preview"]}
20
53
  classNames={{
21
54
  root: "bg-transparent",
55
+ body: "p-4!",
22
56
  }}
23
57
  items={[
24
58
  {
25
59
  key: "preview",
26
60
  label: i18n?.labels?.preview ?? "Preview",
27
- children: <Pdf />,
61
+ children: renderFile(type, url),
28
62
  },
29
63
  ]}
30
64
  />