marktext/src/muya/lib/parser/render/renderInlines/image.js
Lee Holmes 5ffc026a28 This change adds several features to make it easier to write Markdown articles that desire server-based paths (such as /images/my_image.png).
1) There is now a new preference page under images for "Maintaining server paths". You specify a server path (such as /images/) and a local path (such as c:\hugo\static\images), and
Marktext will do the right mapping internally. Image previews will come from the images on your computer, while the content stored in the document will represent the corresponding server paths.
Full documentation has been added to IMAGES.md.
2) Path variables (like ${filename}) have been expanded to support year, month, and day. This helps prevent filename collisions and is common in many blogging platforms.
3) Pasting images and dragging them into Marktext supports this path mapping as well, moving them to the local path as appropriate
4) Image imports (pasting and or dragging) now retain the source image file name if possible, only using the image hash if there is a conflict. Images with names are easier to manage and show up
appropriately in search engines
4) The image edit dialog now has a 'Rename' tab so that you can rename the images to something more specific when they are just pasted from the clipboard. This simplifies the previous workflow that
used to require editing the markdown and renaming the file in the filesystem manually.

There is also now a 'User Notification Dialog' component to let Marktext communicate with the user about error conditions.
2024-01-19 18:42:50 -08:00

156 lines
4.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { CLASS_OR_ID } from '../../../config'
import { getImageInfo } from '../../../utils'
import ImageIcon from '../../../assets/pngicon/image/2.png'
import ImageFailIcon from '../../../assets/pngicon/image_fail/2.png'
import DeleteIcon from '../../../assets/pngicon/delete/2.png'
const renderIcon = (h, className, icon) => {
const selector = `a.${className}`
const iconVnode = h('i.icon', h('i.icon-inner', {
style: {
background: `url(${icon}) no-repeat`,
'background-size': '100%'
}
}, ''))
return h(selector, {
attrs: {
contenteditable: 'false'
}
}, iconVnode)
}
// I dont want operate dom directly, is there any better method? need help!
export default function image (h, cursor, block, token, outerClass) {
const imageInfo = getImageInfo(token.attrs.src, this.muya.options)
const { selectedImage } = this.muya.contentState
const data = {
dataset: {
raw: token.raw
}
}
let id
let isSuccess
let domsrc
let { src } = imageInfo
const alt = token.attrs.alt
const title = token.attrs.title
const width = token.attrs.width
const height = token.attrs.height
if (src) {
({ id, isSuccess, domsrc } = this.loadImageAsync(imageInfo, token.attrs))
}
let wrapperSelector = id
? `span#${isSuccess ? block.key + '_' + id + '_' + token.range.start : id}.${CLASS_OR_ID.AG_INLINE_IMAGE}`
: `span.${CLASS_OR_ID.AG_INLINE_IMAGE}`
const imageIcons = [
renderIcon(h, 'ag-image-icon-success', ImageIcon),
renderIcon(h, 'ag-image-icon-fail', ImageFailIcon),
renderIcon(h, 'ag-image-icon-close', DeleteIcon)
]
const renderImageContainer = (...args) => {
const data = {}
if (title) {
Object.assign(data, {
dataset: { title }
})
}
return h(`span.${CLASS_OR_ID.AG_IMAGE_CONTAINER}`, data, args)
}
if (typeof token.attrs['data-align'] === 'string') {
wrapperSelector += `.${token.attrs['data-align']}`
}
// the src image is still loading, so use the url Map base64.
if (this.urlMap.has(src)) {
// fix: it will generate a new id if the image is not loaded.
const { selectedImage } = this.muya.contentState
if (selectedImage && selectedImage.token.attrs.src === src && selectedImage.imageId !== id) {
selectedImage.imageId = id
}
src = this.urlMap.get(src)
isSuccess = true
}
if (alt.startsWith('loading-')) {
wrapperSelector += `.${CLASS_OR_ID.AG_IMAGE_UPLOADING}`
Object.assign(data.dataset, {
id: alt
})
if (this.urlMap.has(alt)) {
src = this.urlMap.get(alt)
isSuccess = true
}
}
if (src) {
// image is loading...
if (typeof isSuccess === 'undefined') {
wrapperSelector += `.${CLASS_OR_ID.AG_IMAGE_LOADING}`
} else if (isSuccess === true) {
wrapperSelector += `.${CLASS_OR_ID.AG_IMAGE_SUCCESS}`
} else {
wrapperSelector += `.${CLASS_OR_ID.AG_IMAGE_FAIL}`
}
// Add image selected class name.
if (selectedImage) {
const { key, token: selectToken } = selectedImage
if (
key === block.key &&
selectToken.range.start === token.range.start &&
selectToken.range.end === token.range.end
) {
wrapperSelector += `.${CLASS_OR_ID.AG_INLINE_IMAGE_SELECTED}`
}
}
const renderImage = () => {
const data = {
props: { alt: alt.replace(/[`*{}[\]()#+\-.!_>~:|<>$]/g, ''), src: domsrc, title }
}
if (typeof width === 'number') {
Object.assign(data.props, { width })
}
if (typeof height === 'number') {
Object.assign(data.props, { height })
}
return h('img', data)
}
return isSuccess
? [
h(wrapperSelector, data, [
...imageIcons,
renderImageContainer(
// An image description has inline elements as its contents.
// When an image is rendered to HTML, this is standardly used as the images alt attribute.
renderImage()
)
])
]
: [
h(wrapperSelector, data, [
...imageIcons,
renderImageContainer()
])
]
} else {
wrapperSelector += `.${CLASS_OR_ID.AG_EMPTY_IMAGE}`
return [
h(wrapperSelector, data, [
...imageIcons,
renderImageContainer()
])
]
}
}