Skip to content

Commit

Permalink
feat: 拖拽组件优化
Browse files Browse the repository at this point in the history
1. mermaid 支持左下角标签提示
2. 支持任意svg图片可拖拽,支持excalidraw svg图片拖拽
  • Loading branch information
hulinjiang committed Nov 29, 2024
1 parent 7de09cd commit 8ae7868
Show file tree
Hide file tree
Showing 8 changed files with 2,116 additions and 53 deletions.
1,929 changes: 1,914 additions & 15 deletions package-lock.json

Large diffs are not rendered by default.

9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"@expressive-code/plugin-line-numbers": "^0.38.3",
"@expressive-code/plugin-text-markers": "^0.38.3",
"@floating-ui/dom": "^1.6.12",
"@jsdevtools/rehype-inline-svg": "^1.1.4",
"@myriaddreamin/rehype-typst": "^0.5.0-rc7",
"@napi-rs/simple-git": "0.1.19",
"@tweenjs/tween.js": "^25.0.0",
Expand Down Expand Up @@ -71,6 +72,7 @@
"mermaid": "^11.4.0",
"micromorph": "^0.4.5",
"pixi.js": "^8.5.2",
"playwright": "^1.42.1",
"preact": "^10.24.3",
"preact-render-to-string": "^6.5.11",
"pretty-bytes": "^6.1.1",
Expand All @@ -81,6 +83,7 @@
"rehype-expressive-code": "^0.38.3",
"rehype-katex": "^7.0.1",
"rehype-mathjax": "^6.0.0",
"rehype-mermaid": "^3.0.0",
"rehype-pretty-code": "^0.14.0",
"rehype-raw": "^7.0.0",
"rehype-slug": "^6.0.0",
Expand All @@ -99,17 +102,15 @@
"sharp": "^0.33.5",
"shiki": "^1.23.1",
"source-map-support": "^0.5.21",
"svg-pan-zoom": "^3.6.1",
"to-vfile": "^8.0.0",
"toml": "^3.0.0",
"unified": "^11.0.5",
"unist-util-visit": "^5.0.0",
"vfile": "^6.0.3",
"workerpool": "^9.2.0",
"ws": "^8.18.0",
"yargs": "^17.7.2",
"rehype-mermaid": "^3.0.0",
"playwright": "^1.42.1",
"svg-pan-zoom": "^3.6.1"
"yargs": "^17.7.2"
},
"devDependencies": {
"@types/cli-spinner": "^0.2.3",
Expand Down
21 changes: 19 additions & 2 deletions quartz.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const config: QuartzConfig = {
light: "#f9f4ee", // 温暖的米色背景
lightgray: "#ebe6e0", // 调整为更温暖的浅灰色
gray: "#c0b8b0", // 中性偏暖的灰色
darkgray: "#5a534d", // 深灰带一点褐调
darkgray: "#5a534d", // 深灰带一点褐
dark: "#8b2e2e", // 稍微柔和的深红色
secondary: "#2c5875", // 更柔和的蓝色
tertiary: "#8fb5ac", // 略微提亮的青绿色
Expand All @@ -58,7 +58,24 @@ const config: QuartzConfig = {
Plugin.CreatedModifiedDate({
priority: ["frontmatter", "filesystem"],
}),
Plugin.Mermaid(), //must be before at SyntaxHighlighting !
Plugin.InlineSVG({
maxImageSize: 10000000, // 10MB
maxTotalSize: 100000000, // 100MB
label: {
text: 'SVG',
enabled: true,
excalidraw: {
text: '@Excalidraw', // 自定义 excalidraw 标签文本
enabled: true // 是否显示 excalidraw 标签
}
}
}),
Plugin.Mermaid({
label: {
text: 'Mermaid', // 自定义标签文本
enabled: true // 是否显示标签
}
}),
Plugin.SyntaxHighlighting(),
Plugin.ObsidianFlavoredMarkdown({ enableInHtmlEmbed: false, mermaid: false }),
Plugin.GitHubFlavoredMarkdown(),
Expand Down
26 changes: 9 additions & 17 deletions quartz/components/scripts/svg-pan-zoom.inline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,16 +65,12 @@ function createModal(content: SVGElement): HTMLDivElement {
// 在捕获阶段处理ESC事件
document.addEventListener('keydown', handleEsc, true)

const svgContainer = document.createElement('div')
svgContainer.style.cssText = `
width: 100%;
height: 100%;
position: relative;
`
const innerContainer = document.createElement('div')
innerContainer.className = 'svg-pan-zoom-container'
innerContainer.style.position = 'relative'
innerContainer.setAttribute('data-svg-src', content.getAttribute('data-svg-src') || '')

// 深度克隆 SVG,但排除控制按钮
const clonedSvg = content.cloneNode(true) as SVGElement
// 移除已存在的控制按钮
const existingControls = clonedSvg.querySelector('#svg-pan-zoom-controls')
if (existingControls) {
existingControls.remove()
Expand All @@ -84,12 +80,11 @@ function createModal(content: SVGElement): HTMLDivElement {
clonedSvg.setAttribute('height', '100%')
clonedSvg.classList.add('mermaid-svg')

svgContainer.appendChild(clonedSvg)
innerContainer.appendChild(clonedSvg)
modalContent.appendChild(closeBtn)
modalContent.appendChild(svgContainer)
modalContent.appendChild(innerContainer)
modal.appendChild(modalContent)

// 等待 DOM 更新后初始化 SVG Pan Zoom
let modalPanZoom: any = null
setTimeout(() => {
modalPanZoom = svgPanZoom(clonedSvg, {
Expand Down Expand Up @@ -128,10 +123,9 @@ function createCopyButton(): HTMLButtonElement {
</svg>
`

// 创建提示框
const tooltip = document.createElement('div')
tooltip.className = 'copy-tooltip'
tooltip.textContent = '已复制!'
tooltip.textContent = 'Copied!'

return copyBtn
}
Expand All @@ -142,7 +136,6 @@ function enableSvgPanZoom() {
mermaidSvgs.forEach((svg) => {
if (svg instanceof SVGElement) {
try {
// 读取配置
const configStr = svg.getAttribute('data-svg-pan-zoom')
const config = configStr ? JSON.parse(configStr) : {}

Expand All @@ -156,24 +149,23 @@ function enableSvgPanZoom() {
const wrapper = document.createElement('div')
wrapper.className = 'svg-pan-zoom-container'
wrapper.style.position = 'relative'
wrapper.setAttribute('data-svg-src', svg.getAttribute('data-svg-src') || '')
svg.parentNode?.insertBefore(wrapper, svg)
wrapper.appendChild(svg)

// 添加放大按钮
const expandBtn = createExpandButton()
expandBtn.onclick = () => {
const modal = createModal(svg)
document.body.appendChild(modal)
}
wrapper.appendChild(expandBtn)

// 添加复制代码按钮
const code = svg.getAttribute('data-mermaid-code')
if (code) {
const copyBtn = createCopyButton()
const tooltip = document.createElement('div')
tooltip.className = 'copy-tooltip'
tooltip.textContent = '已复制!'
tooltip.textContent = 'Copied!'
wrapper.appendChild(tooltip)

copyBtn.onclick = async () => {
Expand Down
1 change: 1 addition & 0 deletions quartz/plugins/transformers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ export { SyntaxHighlighting } from "./syntax"
export { TableOfContents } from "./toc"
export { HardLineBreaks } from "./linebreaks"
export { Mermaid } from "./mermaid"
export { InlineSVG } from "./inline-svg"
134 changes: 134 additions & 0 deletions quartz/plugins/transformers/inline-svg.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import {QuartzTransformerPlugin} from "../types"
import rehypeInlineSVG from "@jsdevtools/rehype-inline-svg"
import {visit} from "unist-util-visit"
import {Element} from "hast"
//@ts-ignore
import svgPanZoomScript from "../../components/scripts/svg-pan-zoom.inline.ts"
import {JSResource} from "../../util/resources"

interface Options {
maxImageSize?: number
maxTotalSize?: number
svgPanZoom?: {
zoomEnabled?: boolean
controlIconsEnabled?: boolean
fit?: boolean
center?: boolean
zoomScaleSensitivity?: number
minZoom?: number
maxZoom?: number
panEnabled?: boolean
dblClickZoomEnabled?: boolean
mouseWheelZoomEnabled?: boolean
preventMouseEventsDefault?: boolean
optimizeSvg?: boolean
}
label?: {
text?: string
enabled?: boolean
excalidraw?: {
text?: string
enabled?: boolean
}
}
}

const defaultOptions: Options = {
maxImageSize: 100000, // 100kb
maxTotalSize: 1000000, // 1MB,默认是 maxImageSize 的 10 倍
svgPanZoom: {
zoomEnabled: true,
controlIconsEnabled: true,
fit: true,
center: true,
zoomScaleSensitivity: 0.5,
minZoom: 0.5,
maxZoom: 10,
panEnabled: true,
dblClickZoomEnabled: true,
mouseWheelZoomEnabled: true,
preventMouseEventsDefault: true,
optimizeSvg: true
},
label: {
text: 'svg',
enabled: true,
excalidraw: {
text: 'excalidraw',
enabled: true
}
}
}

export const InlineSVG: QuartzTransformerPlugin<Partial<Options>> = (userOpts) => {
const maxImageSize = userOpts?.maxImageSize ?? defaultOptions.maxImageSize
const maxTotalSize = Math.max(
userOpts?.maxTotalSize ?? defaultOptions.maxTotalSize,
maxImageSize * 2 // 至少是 maxImageSize 的两倍
)

const opts = {
...defaultOptions,
...userOpts,
maxImageSize,
maxTotalSize,
svgPanZoom: {
...defaultOptions.svgPanZoom,
...userOpts?.svgPanZoom
}
}

return {
name: "InlineSVG",
htmlPlugins() {
return [
[rehypeInlineSVG, opts],
() => (tree) => {
visit(tree, 'element', (node: Element & { parent?: Element }) => {
if (node.tagName === 'svg') {
const svgPanZoomConfig = { ...opts.svgPanZoom }
node.properties['data-svg-pan-zoom'] = JSON.stringify(svgPanZoomConfig)

// 先获取现有的类名
const classes = Array.isArray(node.properties.className)
? node.properties.className
: typeof node.properties.className === 'string'
? [node.properties.className]
: []

console.log(node.properties.className," " ,node.properties.alt)

// 检查是否是 excalidraw SVG
const isExcalidraw = classes.includes('excalidraw-svg')

// 设置新的类名
if (isExcalidraw) {
node.properties.className = [...classes, 'mermaid-svg']
} else {
node.properties.className = ['mermaid-svg']
}

// 设置标签文本
if (opts.label?.enabled !== false) {
if (isExcalidraw && opts.label?.excalidraw?.enabled !== false) {
node.properties['data-svg-src'] = opts.label?.excalidraw?.text || 'excalidraw'
} else {
node.properties['data-svg-src'] = opts.label?.text || 'svg'
}
}
}
})
}
]
},
externalResources() {
const js: JSResource[] = []
js.push({
script: svgPanZoomScript,
loadTime: "afterDOMReady",
contentType: "inline",
})
return { js }
}
}
}
12 changes: 12 additions & 0 deletions quartz/plugins/transformers/mermaid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ interface Options {
mouseWheelZoomEnabled?: boolean
preventMouseEventsDefault?: boolean
}
label?: {
text?: string
enabled?: boolean
}
}

const defaultOptions: Options = {
Expand All @@ -46,6 +50,10 @@ const defaultOptions: Options = {
dblClickZoomEnabled: true,
mouseWheelZoomEnabled: true,
preventMouseEventsDefault: true
},
label: {
text: '@mermaid',
enabled: true
}
}

Expand Down Expand Up @@ -115,6 +123,10 @@ export const Mermaid: QuartzTransformerPlugin<Partial<Options>> = (userOpts) =>

if (mermaidCode) {
node.properties['data-mermaid-code'] = mermaidCode
// 使用配置的标签文本
if (opts.label?.enabled !== false) {
node.properties['data-svg-src'] = opts.label?.text || 'mermaid'
}
}

const svgPanZoomConfig = { ...opts.svgPanZoom }
Expand Down
37 changes: 22 additions & 15 deletions quartz/styles/mermaid.scss
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ div.svg-pan-zoom-container {

// 添加 mermaid 提示标签
&::after {
content: '@mermaid';
content: attr(data-svg-src);
position: absolute;
left: 10px;
bottom: 10px;
Expand All @@ -46,7 +46,7 @@ div.svg-pan-zoom-container {
color: var(--secondary);
font-size: 12px;
border-radius: 4px;
pointer-events: none; // 确保不影响鼠标事件
pointer-events: none;
z-index: 1;
}

Expand Down Expand Up @@ -165,19 +165,26 @@ div.svg-pan-zoom-container {
z-index: 1001 !important;
}

// 修改模态框中的 mermaid 提示标签样式
&::after {
content: '@mermaid';
position: absolute;
left: 30px;
bottom: 30px;
padding: 4px 8px;
background: var(--lightgray);
color: var(--secondary);
font-size: 24px;
border-radius: 4px;
pointer-events: none;
z-index: 1;
// 内部容器样式
.svg-pan-zoom-container {
width: 100%;
height: 100%;
margin: 0;

// 使用与正文相同的提示标签样式
&::after {
content: attr(data-svg-src);
position: absolute;
left: 30px;
bottom: 30px;
padding: 4px 8px;
background: var(--lightgray);
color: var(--secondary);
font-size: 24px;
border-radius: 4px;
pointer-events: none;
z-index: 1;
}
}
}

Expand Down

0 comments on commit 8ae7868

Please sign in to comment.