diff --git a/icons/mgc/arrow_right_up_cute_re.svg b/icons/mgc/arrow_right_up_cute_re.svg
new file mode 100644
index 000000000..187aecd3e
--- /dev/null
+++ b/icons/mgc/arrow_right_up_cute_re.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/renderer/src/components/ui/link/index.tsx b/src/renderer/src/components/ui/link/index.tsx
index 7a9d27708..867f90032 100644
--- a/src/renderer/src/components/ui/link/index.tsx
+++ b/src/renderer/src/components/ui/link/index.tsx
@@ -1,4 +1,9 @@
-import { Tooltip, TooltipContent, TooltipTrigger } from "../tooltip"
+import {
+ Tooltip,
+ TooltipContent,
+ TooltipPortal,
+ TooltipTrigger,
+} from "../tooltip"
interface LinkProps {
href: string
@@ -10,10 +15,36 @@ interface LinkProps {
export const LinkWithTooltip = (props: LinkProps) => (
- {props.children}
+
+ {props.children}
+
-
- {props.href}
-
+
+
+ {props.href}
+
+
+
+)
+
+export const MarkdownLink = (props: LinkProps) => (
+
+
+
+ {props.children}
+
+ {typeof props.children === "string" && }
+
+
+
+
+ {props.href}
+
+
)
diff --git a/src/renderer/src/components/ui/media.tsx b/src/renderer/src/components/ui/media.tsx
index 049bea017..fb624f78d 100644
--- a/src/renderer/src/components/ui/media.tsx
+++ b/src/renderer/src/components/ui/media.tsx
@@ -10,32 +10,39 @@ import { useEventCallback } from "usehooks-ts"
import { usePreviewMedia } from "./media/hooks"
const failedList = new Set()
-export type ImageProps =
- | (ImgHTMLAttributes & {
- proxy?: {
- width: number
- height: number
- }
- disableContextMenu?: boolean
- popper?: boolean
- type: "photo"
- previewImageUrl?: string
- })
- | (VideoHTMLAttributes & {
- proxy?: {
- width: number
- height: number
- }
- disableContextMenu?: boolean
- popper?: boolean
- type: "video"
- previewImageUrl?: string
- })
-const MediaImpl: FC = ({
+
+type BaseProps = {
+ mediaContainerClassName?: string
+}
+export type MediaProps = BaseProps &
+ (
+ | (ImgHTMLAttributes & {
+ proxy?: {
+ width: number
+ height: number
+ }
+ disableContextMenu?: boolean
+ popper?: boolean
+ type: "photo"
+ previewImageUrl?: string
+ })
+ | (VideoHTMLAttributes & {
+ proxy?: {
+ width: number
+ height: number
+ }
+ disableContextMenu?: boolean
+ popper?: boolean
+ type: "video"
+ previewImageUrl?: string
+ })
+ )
+const MediaImpl: FC = ({
className,
proxy,
disableContextMenu,
popper = false,
+ mediaContainerClassName,
...props
}) => {
const { src, style, type, previewImageUrl, ...rest } = props
@@ -89,6 +96,7 @@ const MediaImpl: FC = ({
!(props.width || props.height) && "size-full",
"bg-stone-100 object-cover",
popper && "cursor-zoom-in",
+ mediaContainerClassName,
)}
src={imgSrc}
onClick={handleClick}
@@ -136,6 +144,7 @@ const MediaImpl: FC = ({
hidden && "hidden",
!(props.width || props.height) && "size-full",
"relative bg-stone-100 object-cover",
+ mediaContainerClassName,
)}
onClick={handleClick}
>
diff --git a/src/renderer/src/lib/parse-html.ts b/src/renderer/src/lib/parse-html.ts
index d20c87326..645b68097 100644
--- a/src/renderer/src/lib/parse-html.ts
+++ b/src/renderer/src/lib/parse-html.ts
@@ -1,6 +1,6 @@
import { Checkbox } from "@renderer/components/ui/checkbox"
import { ShikiHighLighter } from "@renderer/components/ui/code-highlighter"
-import { LinkWithTooltip } from "@renderer/components/ui/link"
+import { MarkdownLink } from "@renderer/components/ui/link"
import { Media } from "@renderer/components/ui/media"
import { toJsxRuntime } from "hast-util-to-jsx-runtime"
import { createElement } from "react"
@@ -28,10 +28,7 @@ export const parseHtml = async (
...defaultSchema,
tagNames: renderInlineStyle ?
defaultSchema.tagNames :
- [
- ...defaultSchema.tagNames!,
- "video",
- ],
+ [...defaultSchema.tagNames!, "video"],
attributes: {
...defaultSchema.attributes,
@@ -58,7 +55,7 @@ export const parseHtml = async (
passNode: true,
components: {
a: ({ node, ...props }) =>
- createElement(LinkWithTooltip, { ...props } as any),
+ createElement(MarkdownLink, { ...props } as any),
img: ({ node, ...props }) => {
if (node?.properties.inline) {
return createElement("img", {
@@ -66,9 +63,17 @@ export const parseHtml = async (
style: { maxWidth: "100%", display: "inline" },
})
}
- return createElement(Media, { ...props, popper: true, type: "photo" })
+ return createElement(Media, {
+ ...props,
+ mediaContainerClassName: "w-full rounded",
+ className: "flex justify-center",
+
+ popper: true,
+ type: "photo",
+ })
},
- video: ({ node, ...props }) => createElement(Media, { ...props, popper: true, type: "video" }),
+ video: ({ node, ...props }) =>
+ createElement(Media, { ...props, popper: true, type: "video" }),
p: ({ node, ...props }) => {
if (node?.children) {
for (const item of node.children) {
@@ -79,10 +84,11 @@ export const parseHtml = async (
}
return createElement("p", undefined, props.children)
},
- hr: ({ node, ...props }) => createElement("hr", {
- ...props,
- className: tw`scale-x-50`,
- }),
+ hr: ({ node, ...props }) =>
+ createElement("hr", {
+ ...props,
+ className: tw`scale-x-50`,
+ }),
input: ({ node, ...props }) => {
if (props.type === "checkbox") {
return createElement(Checkbox, {
diff --git a/src/renderer/src/styles/tailwind-extend.css b/src/renderer/src/styles/tailwind-extend.css
index cc1791e10..b75af3dff 100644
--- a/src/renderer/src/styles/tailwind-extend.css
+++ b/src/renderer/src/styles/tailwind-extend.css
@@ -229,3 +229,35 @@
rgba(0, 0, 0, 0.067) 0px 2px 5px, rgba(0, 0, 0, 0.067) 0px 1px 1px;
}
}
+
+/* Link */
+@layer components {
+ .follow-link--underline {
+ color: currentColor;
+ background-image: linear-gradient(
+ theme(colors.theme.accent.DEFAULT),
+ theme(colors.theme.accent.DEFAULT)
+ );
+ background-size: 0% 1.5px;
+ background-repeat: no-repeat;
+ /* NOTE: this won't work with background images */
+
+ transition: all 500ms ease;
+
+ @apply border-0 no-underline;
+
+ background-position: left 1em;
+
+ &:hover {
+ background-size: 100% 1.5px;
+ text-shadow: 0.05em 0 theme(colors.background),
+ -0.05em 0 theme(colors.background);
+
+ transition: all 250ms ease;
+ }
+
+ &.no-shadow {
+ text-shadow: none;
+ }
+ }
+}