Skip to content

Commit

Permalink
fix: handle render error in code block
Browse files Browse the repository at this point in the history
Signed-off-by: Innei <i@innei.in>
  • Loading branch information
Innei committed Aug 15, 2024
1 parent c41756d commit 8511a79
Show file tree
Hide file tree
Showing 10 changed files with 78 additions and 60 deletions.
4 changes: 2 additions & 2 deletions src/renderer/src/components/common/ErrorElement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@ export function ErrorElement() {
</div>
<h3 className="text-xl">{message}</h3>
{import.meta.env.DEV && stack ? (
<div className="mt-4 cursor-text overflow-auto whitespace-pre rounded-md bg-red-50 p-4 text-left font-mono text-sm text-red-600">
<pre className="mt-4 max-h-48 cursor-text overflow-auto whitespace-pre-line rounded-md bg-red-50 p-4 text-left font-mono text-sm text-red-600">
{attachOpenInEditor(stack)}
</div>
</pre>
) : null}

<p className="my-8">
Expand Down
4 changes: 2 additions & 2 deletions src/renderer/src/components/errors/ModalError.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ export const ModalErrorFallback: FC<AppErrorFallbackProps> = (props) => {
</div>
<div className="text-lg font-bold">{message}</div>
{import.meta.env.DEV && stack ? (
<div className="mt-4 cursor-text overflow-auto whitespace-pre rounded-md bg-red-50 p-4 text-left font-mono text-sm text-red-600">
<pre className="mt-4 max-h-48 cursor-text overflow-auto whitespace-pre-line rounded-md bg-red-50 p-4 text-left font-mono text-sm text-red-600">
{attachOpenInEditor(stack)}
</div>
</pre>
) : null}

<p className="my-8">
Expand Down
4 changes: 2 additions & 2 deletions src/renderer/src/components/errors/PageError.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ export const PageErrorFallback: FC<AppErrorFallbackProps> = (props) => {
</div>
<div className="text-lg font-bold">{message}</div>
{import.meta.env.DEV && stack ? (
<div className="mt-4 cursor-text overflow-auto whitespace-pre rounded-md bg-red-50 p-4 text-left font-mono text-sm text-red-600">
<pre className="mt-4 max-h-48 cursor-text overflow-auto whitespace-pre-line rounded-md bg-red-50 p-4 text-left font-mono text-sm text-red-600">
{attachOpenInEditor(stack)}
</div>
</pre>
) : null}

<p className="my-8">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import {
import { isElectronBuild } from "@renderer/constants"
import { tipcClient } from "@renderer/lib/client"
import { cn } from "@renderer/lib/utils"
import { useIsomorphicLayoutEffect } from "foxact/use-isomorphic-layout-effect"
import type { FC } from "react"
import {
useInsertionEffect,
useLayoutEffect,
useMemo,
useRef,
useState,
Expand Down Expand Up @@ -95,7 +95,7 @@ export const ShikiHighLighter: FC<ShikiProps> = (props) => {
const codeTheme = useUISettingSelector(
(s) => overrideTheme || s.codeHighlightTheme,
)
useLayoutEffect(() => {
useIsomorphicLayoutEffect(() => {
let isMounted = true
setLoaded(false)

Expand Down Expand Up @@ -214,7 +214,10 @@ const ShikiCode: FC<
className,
)}
>
<div dangerouslySetInnerHTML={{ __html: rendered }} />
<div
dangerouslySetInnerHTML={{ __html: rendered }}
data-language={language}
/>
<CopyButton
value={code}
className="absolute right-1 top-1 opacity-0 duration-200 group-hover:opacity-100"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
pre {
@apply !m-0 overflow-auto p-4;

font-size: min(1em, 16px);
font-size: 0.875em;
}

pre code {
Expand Down
18 changes: 18 additions & 0 deletions src/renderer/src/components/ui/markdown/Markdown.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { parseHtml } from "@renderer/lib/parse-html"
import type { RemarkOptions } from "@renderer/lib/parse-markdown"
import { parseMarkdown } from "@renderer/lib/parse-markdown"
import { cn } from "@renderer/lib/utils"
Expand Down Expand Up @@ -26,3 +27,20 @@ export const Markdown: Component<
</article>
)
}

export const HTML: Component<
{
children: string | null | undefined
} & Partial<{
renderInlineStyle: boolean
}>
> = ({ children, renderInlineStyle }) => {
const stableRemarkOptions = useState({ renderInlineStyle })[0]

const markdownElement = useMemo(
() => children && parseHtml(children, { ...stableRemarkOptions }).content,
[children, stableRemarkOptions],
)

return markdownElement
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { captureException } from "@sentry/react"
import { useEffect } from "react"

export const BlockError = (props: { error: any, message: string }) => {
useEffect(() => {
captureException(props.error)
}, [])
return (
<div className="center flex min-h-12 flex-col rounded bg-red-400 py-4 text-sm text-white dark:bg-red-800">
{props.message}

<pre className="m-0 bg-transparent">{props.error?.message}</pre>
</div>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export const MarkdownLink = (props: LinkProps) => {
}, [feedSiteUrl, props])
const entryId = isBizId(props.href) ? props.href : null
const entry = useEntry(entryId)

useAuthQuery(Queries.entries.byId(entryId!), {
enabled: !!entryId && !entry,
staleTime: 1000 * 60 * 5,
Expand Down
17 changes: 13 additions & 4 deletions src/renderer/src/lib/parse-html.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
MarkdownLink,
MarkdownP,
} from "@renderer/components/ui/markdown/renderers"
import { BlockError } from "@renderer/components/ui/markdown/renderers/BlockErrorBoundary"
import { Media } from "@renderer/components/ui/media"
import type { Components } from "hast-util-to-jsx-runtime"
import { toJsxRuntime } from "hast-util-to-jsx-runtime"
Expand All @@ -18,11 +19,11 @@ import rehypeStringify from "rehype-stringify"
import { unified } from "unified"
import { VFile } from "vfile"

export const parseHtml = async (
export const parseHtml = (
content: string,
options?: {
options?: Partial<{
renderInlineStyle: boolean
},
}>,
) => {
const file = new VFile(content)
const { renderInlineStyle = false } = options || {}
Expand Down Expand Up @@ -94,6 +95,7 @@ export const parseHtml = async (
if (!props.children) return null

let language = ""

let codeString = null as string | null
if (props.className?.includes("language-")) {
language = props.className.replace("language-", "")
Expand All @@ -116,7 +118,14 @@ export const parseHtml = async (
"props" in props.children && props.children.props.children
if (!code) return null

codeString = extractCodeFromHtml(renderToString(code))
try {
codeString = extractCodeFromHtml(renderToString(code))
} catch (error) {
return createElement(BlockError, {
error,
message: "Code Block Render Error",
})
}
}

if (!codeString) return null
Expand Down
64 changes: 18 additions & 46 deletions src/renderer/src/modules/entry-content/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { useUISettingKey } from "@renderer/atoms/settings/ui"
import { useWhoami } from "@renderer/atoms/user"
import { m } from "@renderer/components/common/Motion"
import { AutoResizeHeight } from "@renderer/components/ui/auto-resize-height"
import { HTML } from "@renderer/components/ui/markdown"
import { ScrollArea } from "@renderer/components/ui/scroll-area"
import { isWebBuild, ROUTE_FEED_PENDING } from "@renderer/constants"
import { useEntryReadabilityToggle } from "@renderer/hooks/biz/useEntryActions"
Expand All @@ -19,7 +20,6 @@ import {
import { useAuthQuery, useTitle } from "@renderer/hooks/common"
import { stopPropagation } from "@renderer/lib/dom"
import { FeedViewType } from "@renderer/lib/enum"
import { parseHtml } from "@renderer/lib/parse-html"
import type { ActiveEntryId } from "@renderer/models"
import {
useIsSoFWrappedElement,
Expand All @@ -28,8 +28,8 @@ import {
import { Queries } from "@renderer/queries"
import { useEntry, useEntryReadHistory } from "@renderer/store/entry"
import { useFeedById, useFeedHeaderTitle } from "@renderer/store/feed"
import type { FC, ReactNode } from "react"
import { useEffect, useLayoutEffect, useRef, useState } from "react"
import type { FC } from "react"
import { useEffect, useLayoutEffect, useRef } from "react"

import { LoadingCircle } from "../../components/ui/loading"
import { EntryPlaceholderDaily } from "../ai/ai-daily/EntryPlaceholderDaily"
Expand Down Expand Up @@ -79,27 +79,7 @@ function EntryContentRender({ entryId }: { entryId: string }) {

const entryHistory = useEntryReadHistory(entryId)

const [content, setContent] = useState<JSX.Element>()
const readerRenderInlineStyle = useUISettingKey("readerRenderInlineStyle")
useLayoutEffect(() => {
// Fallback data, if local data is broken should fallback to cached query data.
const processContent = entry?.entries.content ?? data?.entries.content
if (processContent) {
parseHtml(processContent, {
renderInlineStyle: readerRenderInlineStyle,
}).then((parsed) => {
setContent(parsed.content)
})
} else {
setContent(undefined)
}
}, [
data?.entries.content,
entry?.entries.content,
readerRenderInlineStyle,
// Only for dx, hmr
parseHtml,
])

const translation = useAuthQuery(
Queries.ai.translation({
Expand Down Expand Up @@ -137,12 +117,13 @@ function EntryContentRender({ entryId }: { entryId: string }) {

const isInReadabilityMode = useEntryIsInReadability(entryId)
const scrollerRef = useRef<HTMLDivElement>(null)

useEffect(() => {
scrollerRef.current?.scrollTo(0, 0)
}, [entryId])
if (!entry) return null

const content = entry?.entries.content ?? data?.entries.content

return (
<EntryContentProvider
entryId={entry.entries.id}
Expand Down Expand Up @@ -232,11 +213,13 @@ function EntryContentRender({ entryId }: { entryId: string }) {
</AutoResizeHeight>
</div>
)}
{!isInReadabilityMode ? (
content
) : (
<ReadabilityContent entryId={entryId} />
)}
<article>
{!isInReadabilityMode ? (
<HTML renderInlineStyle={readerRenderInlineStyle}>{content}</HTML>
) : (
<ReadabilityContent entryId={entryId} />
)}
</article>
</div>
</WrappedElementProvider>
{!content && (
Expand Down Expand Up @@ -305,22 +288,6 @@ const TitleMetaHandler: Component<{
const ReadabilityContent = ({ entryId }: { entryId: string }) => {
const result = useEntryReadabilityContent(entryId)

const [renderer, setRenderer] = useState<ReactNode | null>(null)
useLayoutEffect(() => {
if (!result) return
const { content: processContent } = result

if (processContent) {
parseHtml(processContent, {
renderInlineStyle: true,
}).then((parsed) => {
setRenderer(parsed.content)
})
} else {
setRenderer(null)
}
}, [result, parseHtml])

return (
<div className="grow">
{result ? (
Expand All @@ -337,7 +304,12 @@ const ReadabilityContent = ({ entryId }: { entryId: string }) => {
</span>
</div>
)}
{renderer}
<article>
<HTML>
{result?.content ?? ""}
</HTML>
</article>

</div>
)
}
Expand Down

0 comments on commit 8511a79

Please sign in to comment.