Skip to content

Commit

Permalink
feat(media): add support for video and audio elements in markdown
Browse files Browse the repository at this point in the history
  • Loading branch information
purocean committed Jan 2, 2025
1 parent 5b16b97 commit f823def
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 5 deletions.
20 changes: 18 additions & 2 deletions src/renderer/components/DefaultPreviewerRender.ce.vue
Original file line number Diff line number Diff line change
Expand Up @@ -348,13 +348,29 @@ body.find-in-preview-highlight ::selection {
cursor: zoom-in;
}
p > img[auto-center] {
video {
max-width: 100%;
}
audio {
max-width: 100%;
width: 300px;
height: 42px;
}
p > img[auto-center],
p > video[auto-center],
p > audio[auto-center] {
display: block;
margin-left: auto;
margin-right: auto;
}
img {
p > audio[auto-center] {
width: 100%;
}
img, video {
&.inline,
&[src*=".inline"],
&[origin-src*=".inline"] {
Expand Down
13 changes: 12 additions & 1 deletion src/renderer/plugins/markdown-link/lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import type { Components, Doc, ParseLinkResult, PathItem, PositionState, Resourc

const RE_POS = /:([0-9]+),?([0-9]+)?$/
const RE_EXTERNAL_LINK = /^[a-zA-Z_+-]{1,}:/
const RE_VIDEO = /\.(mp4|webm|ogg)$/
const RE_AUDIO = /\.(mp3|wav|ogg)$/

export function isAnchorToken (token: Token) {
return token.tag === 'a'
Expand Down Expand Up @@ -116,7 +118,16 @@ export function convertResourceState (currentFile: PathItem, state: StateCore, b
name: fileName,
}

token.attrSet(DOM_ATTR_NAME.LOCAL_IMAGE, 'true')
if (RE_VIDEO.test(fileName)) {
token.tag = 'video'
token.type = 'media'
} else if (RE_AUDIO.test(fileName)) {
token.tag = 'audio'
token.type = 'media'
} else {
token.attrSet(DOM_ATTR_NAME.LOCAL_IMAGE, 'true')
}

token.attrSet(DOM_ATTR_NAME.TARGET_PATH, filePath)
token.attrSet(DOM_ATTR_NAME.TARGET_REPO, repo)
token.attrSet(originAttr, attrVal)
Expand Down
19 changes: 19 additions & 0 deletions src/renderer/plugins/markdown-render-vnode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@ const attrNameReg = /^[a-zA-Z_:][a-zA-Z0-9:._-]*$/
const attrEventReg = /^on/i
const defaultRules = {} as any

function onLeavepictureinpicture (e: Event) {
const target = e.target as HTMLMediaElement
if (!target.isConnected) {
target.pause()
} else {
(target as any).scrollIntoViewIfNeeded()
}
}

function validateAttrName (name: string) {
return attrNameReg.test(name) && !attrEventReg.test(name)
}
Expand Down Expand Up @@ -169,6 +178,16 @@ defaultRules.image = function (tokens: Token[], idx: number, options: any, env:
}, [])
}

defaultRules.media = function (tokens: Token[], idx: number, _options: any, _env: any, slf: Renderer) {
const token = tokens[idx]
return createVNode(token.tag, {
controlsList: 'nodownload',
controls: true,
onLeavepictureinpicture,
...slf.renderAttrs(token) as any,
}, [])
}

defaultRules.hardbreak = function () {
return createVNode('br')
}
Expand Down
9 changes: 8 additions & 1 deletion src/renderer/plugins/media-player.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,18 @@ const MediaPlayer = defineComponent({
}
})

function onLeavepictureinpicture (e: Event) {
const target = e.target as HTMLMediaElement
if (!target.isConnected) {
target.pause()
}
}

return () => src.value ? h(
'div',
{ style: 'width: 100%; height: 100%; display: flex; align-items: center; justify-content: center' },
type.value === 'video'
? h('video', { src: src.value, controls: true, disablePictureInPicture: true, style: 'width: 100%; height: 100%; object-fit: contain' })
? h('video', { src: src.value, controls: true, onLeavepictureinpicture, style: 'width: 100%; height: 100%; object-fit: contain' })
: type.value === 'audio'
? h('audio', { src: src.value, controls: true })
: ''
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/plugins/misc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export default {
tokens[idx + 1]?.type === 'inline' &&
tokens[idx + 2]?.type === 'paragraph_close' &&
tokens[idx + 1]?.children?.length === 1 &&
tokens[idx + 1]?.children?.[0]?.type === 'image'
(tokens[idx + 1]?.children?.[0]?.type === 'image' || tokens[idx + 1]?.children?.[0]?.type === 'media')
) {
tokens[idx + 1]?.children?.[0].attrSet(ctx.args.DOM_ATTR_NAME.ONLY_CHILD, 'true')
}
Expand Down

0 comments on commit f823def

Please sign in to comment.