mirror of
https://github.com/marktext/marktext.git
synced 2025-05-02 19:41:39 +08:00

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.
156 lines
4.2 KiB
JavaScript
156 lines
4.2 KiB
JavaScript
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 image’s alt attribute.
|
||
renderImage()
|
||
)
|
||
])
|
||
]
|
||
: [
|
||
h(wrapperSelector, data, [
|
||
...imageIcons,
|
||
renderImageContainer()
|
||
])
|
||
]
|
||
} else {
|
||
wrapperSelector += `.${CLASS_OR_ID.AG_EMPTY_IMAGE}`
|
||
return [
|
||
h(wrapperSelector, data, [
|
||
...imageIcons,
|
||
renderImageContainer()
|
||
])
|
||
]
|
||
}
|
||
}
|