diff --git a/src/renderer/components/DefaultPreviewerRender.ce.vue b/src/renderer/components/DefaultPreviewerRender.ce.vue index 8107c024..82d9c8ee 100644 --- a/src/renderer/components/DefaultPreviewerRender.ce.vue +++ b/src/renderer/components/DefaultPreviewerRender.ce.vue @@ -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"] { diff --git a/src/renderer/plugins/markdown-link/lib.ts b/src/renderer/plugins/markdown-link/lib.ts index 158b2802..abdd8ff2 100644 --- a/src/renderer/plugins/markdown-link/lib.ts +++ b/src/renderer/plugins/markdown-link/lib.ts @@ -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' @@ -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) diff --git a/src/renderer/plugins/markdown-render-vnode.ts b/src/renderer/plugins/markdown-render-vnode.ts index 796f0c94..30d20191 100644 --- a/src/renderer/plugins/markdown-render-vnode.ts +++ b/src/renderer/plugins/markdown-render-vnode.ts @@ -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) } @@ -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') } diff --git a/src/renderer/plugins/media-player.ts b/src/renderer/plugins/media-player.ts index 4230de52..e03b0724 100644 --- a/src/renderer/plugins/media-player.ts +++ b/src/renderer/plugins/media-player.ts @@ -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 }) : '' diff --git a/src/renderer/plugins/misc.ts b/src/renderer/plugins/misc.ts index 00c40532..aab23eb2 100644 --- a/src/renderer/plugins/misc.ts +++ b/src/renderer/plugins/misc.ts @@ -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') }