Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file added docs/images/personal-agent-markdown-gfm.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
"react-markdown": "^10.1.0",
"react-resizable-panels": "^3.0.6",
"react-router-dom": "^7.13.1",
"remark-gfm": "^4.0.1",
"sonner": "^2.0.7",
"tailwind-merge": "^3.5.0",
"tailwindcss": "^4.2.2",
Expand Down
17 changes: 17 additions & 0 deletions frontend/src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -645,6 +645,23 @@
overflow-x: auto;
}
.md-content pre code { background: none; padding: 0; }
.md-content .md-table-wrapper {
max-width: 100%;
margin: 12px 0;
overflow-x: auto;
border: 1px solid var(--border);
border-radius: 8px;
}
.md-content table { width: 100%; border-collapse: collapse; }
.md-content th, .md-content td {
min-width: 8rem;
padding: 8px 12px;
border-bottom: 1px solid var(--border);
vertical-align: top;
}
.md-content th { background: var(--secondary); font-weight: 600; }
.md-content th + th, .md-content td + td { border-left: 1px solid var(--border); }
.md-content tbody tr:last-child td { border-bottom: none; }
.md-content ul, .md-content ol { margin: 6px 0; padding-left: 20px; }
.md-content li { margin: 2px 0; }
.md-content h1, .md-content h2, .md-content h3 { margin: 12px 0 6px; font-weight: 600; }
Expand Down
46 changes: 43 additions & 3 deletions frontend/src/pages/PersonalAgent.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { type DragEvent, type ReactNode, useEffect, useMemo, useRef, useState } from "react";
import { type DragEvent, type ReactNode, useEffect, useId, useMemo, useRef, useState } from "react";
import * as Dialog from "@radix-ui/react-dialog";
import ReactMarkdown from "react-markdown";
import ReactMarkdown, { type Components } from "react-markdown";
import remarkGfm from "remark-gfm";
import {
BookOpen,
Brain,
Expand Down Expand Up @@ -40,6 +41,45 @@ import {
const PAGE_CLASS = "flex-1 w-full max-w-[1800px] mx-auto px-4 py-5 md:px-7 md:py-6 xl:px-8";
const ACCEPTED_DOCUMENTS = ".pdf,.docx,.pptx,.xlsx,.txt,.md,.markdown,.csv,.tsv,.json,.yaml,.yml,.xml,.html,.htm,.rtf,.log,.py,.js,.jsx,.ts,.tsx,.java,.go,.rs,.sql,.css,.sh";

const MARKDOWN_COMPONENTS: Components = {
table: ({ children }) => (
<div className="md-table-wrapper" role="region" aria-label="Markdown 表格" tabIndex={0}>
<table>{children}</table>
</div>
),
};

function MessageMarkdown({ content }: { content: string }) {
const messageId = useId();
const clobberPrefix = `user-content-agent-${messageId}-`;
const footnoteLabelId = `${clobberPrefix}footnote-label`;
// clobberPrefix 隔离脚注及回链;上游固定的 footnote-label 和 aria-describedby 需单独隔离。
//
// clobberPrefix scopes footnotes and backlinks; the upstream fixed footnote-label and aria-describedby need separate scoping.
const components = useMemo<Components>(() => ({
...MARKDOWN_COMPONENTS,
h2: ({ node, ...props }) => (
<h2 {...props} id={node?.properties.id === "footnote-label" ? footnoteLabelId : props.id} />
),
a: ({ node, ...props }) => (
<a
{...props}
aria-describedby={node?.properties.dataFootnoteRef ? footnoteLabelId : props["aria-describedby"]}
/>
),
}), [footnoteLabelId]);

return (
<ReactMarkdown
remarkPlugins={[remarkGfm]}
remarkRehypeOptions={{ clobberPrefix }}
components={components}
>
{content}
</ReactMarkdown>
);
}

const STARTERS = [
{ icon: Brain, text: "根据我的画像,告诉我现在最该补什么" },
{ icon: BookOpen, text: "结合我的错题,帮我安排一周复习计划" },
Expand Down Expand Up @@ -502,7 +542,7 @@ export default function PersonalAgent() {
</div>
<div className="min-w-0 flex-1">
<div className="md-content text-[14px] leading-7 text-text">
<ReactMarkdown>{message.content}</ReactMarkdown>
<MessageMarkdown content={message.content} />
</div>
{!!message.sources?.length && (
<div className="mt-3 flex flex-wrap gap-1.5">
Expand Down
Loading
Loading