feat: clickable entry id in ai daily
This commit is contained in:
parent
acfb35abd0
commit
0ffe1e5b6d
|
|
@ -114,6 +114,8 @@
|
|||
"rehype-parse": "9.0.0",
|
||||
"rehype-sanitize": "6.0.0",
|
||||
"rehype-stringify": "10.0.0",
|
||||
"remark-parse": "11.0.0",
|
||||
"remark-rehype": "11.1.0",
|
||||
"semver": "7.6.3",
|
||||
"shiki": "1.12.0",
|
||||
"sonner": "^1.5.0",
|
||||
|
|
|
|||
|
|
@ -271,6 +271,12 @@ importers:
|
|||
rehype-stringify:
|
||||
specifier: 10.0.0
|
||||
version: 10.0.0
|
||||
remark-parse:
|
||||
specifier: 11.0.0
|
||||
version: 11.0.0
|
||||
remark-rehype:
|
||||
specifier: 11.1.0
|
||||
version: 11.1.0
|
||||
semver:
|
||||
specifier: 7.6.3
|
||||
version: 7.6.3
|
||||
|
|
@ -5897,6 +5903,12 @@ packages:
|
|||
rehype-stringify@10.0.0:
|
||||
resolution: {integrity: sha512-1TX1i048LooI9QoecrXy7nGFFbFSufxVRAfc6Y9YMRAi56l+oB0zP51mLSV312uRuvVLPV1opSlJmslozR1XHQ==}
|
||||
|
||||
remark-parse@11.0.0:
|
||||
resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==}
|
||||
|
||||
remark-rehype@11.1.0:
|
||||
resolution: {integrity: sha512-z3tJrAs2kIs1AqIIy6pzHmAHlF1hWQ+OdY4/hv+Wxe35EhyLKcajL33iUEn3ScxtFox9nUvRufR/Zre8Q08H/g==}
|
||||
|
||||
repeat-string@1.6.1:
|
||||
resolution: {integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==}
|
||||
engines: {node: '>=0.10'}
|
||||
|
|
@ -13524,6 +13536,23 @@ snapshots:
|
|||
hast-util-to-html: 9.0.1
|
||||
unified: 11.0.5
|
||||
|
||||
remark-parse@11.0.0:
|
||||
dependencies:
|
||||
'@types/mdast': 4.0.3
|
||||
mdast-util-from-markdown: 2.0.0
|
||||
micromark-util-types: 2.0.0
|
||||
unified: 11.0.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
remark-rehype@11.1.0:
|
||||
dependencies:
|
||||
'@types/hast': 3.0.4
|
||||
'@types/mdast': 4.0.3
|
||||
mdast-util-to-hast: 13.1.0
|
||||
unified: 11.0.5
|
||||
vfile: 6.0.2
|
||||
|
||||
repeat-string@1.6.1:
|
||||
optional: true
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
import { useNavigateEntry } from "@renderer/hooks/biz/useNavigateEntry"
|
||||
import { FeedViewType } from "@renderer/lib/enum"
|
||||
import { useEntryContentContext } from "@renderer/modules/entry-content/hooks"
|
||||
import { useEntry } from "@renderer/store/entry"
|
||||
import { useCallback } from "react"
|
||||
|
||||
import type { LinkProps } from "../../link"
|
||||
import {
|
||||
|
|
@ -13,6 +16,19 @@ import { ensureAndRenderTimeStamp } from "../utils"
|
|||
export const MarkdownLink = (props: LinkProps) => {
|
||||
const { view } = useEntryContentContext()
|
||||
|
||||
const entryId = (/^\w{17}$/.test(props.href)) ? props.href : null
|
||||
const entry = useEntry(entryId)
|
||||
|
||||
const navigate = useNavigateEntry()
|
||||
const onClick = useCallback((e: React.MouseEvent) => {
|
||||
if (entryId) {
|
||||
e.preventDefault()
|
||||
navigate({
|
||||
entryId,
|
||||
})
|
||||
}
|
||||
}, [entryId, navigate])
|
||||
|
||||
const parseTimeStamp = view === FeedViewType.Audios
|
||||
if (parseTimeStamp) {
|
||||
const childrenText = props.children
|
||||
|
|
@ -22,6 +38,7 @@ export const MarkdownLink = (props: LinkProps) => {
|
|||
if (renderer) return renderer
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Tooltip delayDuration={0}>
|
||||
<TooltipTrigger asChild>
|
||||
|
|
@ -30,6 +47,7 @@ export const MarkdownLink = (props: LinkProps) => {
|
|||
href={props.href}
|
||||
title={props.title}
|
||||
target="_blank"
|
||||
onClick={onClick}
|
||||
>
|
||||
{props.children}
|
||||
|
||||
|
|
@ -41,7 +59,7 @@ export const MarkdownLink = (props: LinkProps) => {
|
|||
{!!props.href && (
|
||||
<TooltipPortal>
|
||||
<TooltipContent align="start" className="break-all" side="bottom">
|
||||
{props.href}
|
||||
{entry?.entries.title || props.href}
|
||||
</TooltipContent>
|
||||
</TooltipPortal>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
import {
|
||||
MarkdownLink,
|
||||
} from "@renderer/components/ui/markdown/renderers"
|
||||
import { toJsxRuntime } from "hast-util-to-jsx-runtime"
|
||||
import { createElement } from "react"
|
||||
import { Fragment, jsx, jsxs } from "react/jsx-runtime"
|
||||
import rehypeSanitize from "rehype-sanitize"
|
||||
import rehypeStringify from "rehype-stringify"
|
||||
import remarkParse from "remark-parse"
|
||||
import remarkRehype from "remark-rehype"
|
||||
import { unified } from "unified"
|
||||
import { VFile } from "vfile"
|
||||
|
||||
export const parseMarkdown = async (
|
||||
content: string,
|
||||
) => {
|
||||
const file = new VFile(content)
|
||||
|
||||
const pipeline = unified()
|
||||
.use(remarkParse)
|
||||
.use(remarkRehype)
|
||||
.use(rehypeSanitize)
|
||||
.use(rehypeStringify)
|
||||
|
||||
const tree = pipeline.parse(content)
|
||||
|
||||
const hastTree = pipeline.runSync(tree, file)
|
||||
|
||||
return {
|
||||
content: toJsxRuntime(hastTree, {
|
||||
Fragment,
|
||||
ignoreInvalidStyle: true,
|
||||
jsx: (type, props, key) => jsx(type as any, props, key),
|
||||
jsxs: (type, props, key) => jsxs(type as any, props, key),
|
||||
passNode: true,
|
||||
components: {
|
||||
a: ({ node, ...props }) =>
|
||||
createElement(MarkdownLink, { ...props } as any),
|
||||
},
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
import { Card, CardHeader } from "@renderer/components/ui/card"
|
||||
import { Collapse, CollapseGroup } from "@renderer/components/ui/collapse"
|
||||
import { LoadingCircle } from "@renderer/components/ui/loading"
|
||||
import { Markdown } from "@renderer/components/ui/markdown"
|
||||
import { ScrollArea } from "@renderer/components/ui/scroll-area"
|
||||
import {
|
||||
Tooltip,
|
||||
|
|
@ -12,10 +11,11 @@ import { useAuthQuery } from "@renderer/hooks/common"
|
|||
import { apiClient } from "@renderer/lib/api-fetch"
|
||||
import { defineQuery } from "@renderer/lib/defineQuery"
|
||||
import type { FeedViewType } from "@renderer/lib/enum"
|
||||
import { parseMarkdown } from "@renderer/lib/parse-markdown"
|
||||
import { cn } from "@renderer/lib/utils"
|
||||
import { MarkAllButton } from "@renderer/modules/entry-column/mark-all-button"
|
||||
import type { FC } from "react"
|
||||
import { useState } from "react"
|
||||
import { useEffect, useState } from "react"
|
||||
|
||||
type DailyView = Extract<
|
||||
FeedViewType,
|
||||
|
|
@ -153,6 +153,15 @@ const DailyReportContent: FC<{
|
|||
const [markAllButtonRef, setMarkAllButtonRef] =
|
||||
useState<HTMLButtonElement | null>(null)
|
||||
|
||||
const [eleContent, setEleContent] = useState<JSX.Element>()
|
||||
useEffect(() => {
|
||||
if (content.data) {
|
||||
parseMarkdown(content.data).then(({ content }) => {
|
||||
setEleContent(content)
|
||||
})
|
||||
}
|
||||
}, [content.data])
|
||||
|
||||
return (
|
||||
<Card className="border-none bg-transparent">
|
||||
<CardHeader className="space-y-0 p-0">
|
||||
|
|
@ -164,14 +173,14 @@ const DailyReportContent: FC<{
|
|||
{content.isLoading ? (
|
||||
<LoadingCircle size="large" className="mt-8 text-center" />
|
||||
) : (
|
||||
content.data && (
|
||||
<Markdown className="prose-sm mt-4 px-6 prose-p:my-1 prose-ul:my-1">
|
||||
{content.data}
|
||||
</Markdown>
|
||||
eleContent && (
|
||||
<div className="prose-sm mt-4 px-6 prose-p:my-1 prose-ul:my-1">
|
||||
{eleContent}
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</ScrollArea.ScrollArea>
|
||||
{content.data && (
|
||||
{eleContent && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue