import { customElement, noShadowDOM } from "solid-element"; import {Show, For, createResource, createMemo, createSignal} from "solid-js"; import { TbOutlineCopy } from "solid-icons/tb"; import { resolvePath } from "./utils/path"; import { createPinsStore } from "./stores/pinsStore"; customElement("md-pins", { pins: "", fixed: false, labelStart: "A" }, (props, { element }) => { noShadowDOM(); const [showToast, setShowToast] = createSignal(false); // 创建 store const store = createPinsStore(props.pins, props.fixed, props.labelStart); // 从 element 的 textContent 获取图片路径 const rawSrc = element?.textContent?.trim() || ''; // 设置 rawSrc 到 store store.setRawSrc(rawSrc); // 隐藏原始文本内容 if (element) { element.textContent = ""; } // 从父节点 article 的 data-src 获取当前 markdown 文件完整路径 const articleEl = element?.closest('article[data-src]'); const articlePath = articleEl?.getAttribute('data-src') || ''; // 解析相对路径 const resolvedSrc = resolvePath(articlePath, rawSrc); // 加载图片 const loadImage = (src: string): Promise => { return new Promise((resolve, reject) => { const img = new Image(); img.onload = () => resolve(img); img.onerror = reject; img.src = src; }); }; const [image] = createResource(resolvedSrc, loadImage); const visible = createMemo(() => !image.loading && !!image()); // 添加 pin const addPin = (e: MouseEvent) => { e.preventDefault(); e.stopPropagation(); if (props.fixed) return; const imgRect = (e.target as Element).getBoundingClientRect(); const clickX = ((e.clientX - imgRect.left) / imgRect.width) * 100; const clickY = ((e.clientY - imgRect.top) / imgRect.height) * 100; const x = Math.round(clickX); const y = Math.round(clickY); store.addPin(x, y); }; // 删除 pin const removePin = (index: number, e: MouseEvent) => { e.preventDefault(); e.stopPropagation(); if (props.fixed) return; store.removePin(index); }; // 复制所有 pin 为 :md-editor-pin 格式 const copyPins = () => { const text = store.getCopyText(); navigator.clipboard.writeText(text).then(() => { setShowToast(true); setTimeout(() => setShowToast(false), 2000); }).catch(err => { console.error('复制失败:', err); }); }; return (
{/* 图片容器 */}
{/* 显示图片 */} {/* 透明遮罩层 */}
{/* 复制按钮 HUD */}
{/* Pin 列表 */} {(pin, index) => ( removePin(index(), e)} class={`absolute transform -translate-x-1/2 -translate-y-1/2 pointer-events-auto bg-red-500 text-white text-xs font-bold rounded-full w-6 h-6 flex items-center justify-center shadow-lg ${!props.fixed ? 'cursor-pointer hover:bg-red-600 hover:scale-110 transition-all z-10' : 'cursor-default z-10'}`} style={{ left: `${pin.x}%`, top: `${pin.y}%` }} title={props.fixed ? `(${pin.x}, ${pin.y})` : `点击删除 (${pin.x}, ${pin.y})`} > {pin.label} )}
{/* Toast 提示 */}
已复制 {store.state.pins.length} 个 pin 坐标
); });