--- name: 切换 Checkbox 标记 description: 将选中的文本块转化为 Checkbox 控件(再次运行取消)。 author: ymjr version: 1.0.0 license: MIT usage: 选中画板中的文本,运行此脚本将其转化为 Checkbox。 dependencies: - 必须依赖 [Feature-Checkbox-Engine] 核心引擎处理后台交互 autorun: false --- /* ```javascript */ var locales = { zh: { notice_select: "请先选中至少一个文本元素!", notice_removed: "⭕ 已移除 Checkbox 控件特性", notice_added: "✨ 转化成功!现在可以 Shift+点击 切换状态了" }, en: { notice_select: "Please select at least one text element!", notice_removed: "⭕ Checkbox feature removed", notice_added: "✨ Converted! Shift+Click to toggle state" } }; // ========================================== // 美观的 HTML Toast 通知 // ========================================== function showModernToast(plugin, message, isSuccess = true) { if (!plugin._ymjr_checkbox_state) { plugin._ymjr_checkbox_state = { ui: {} }; } const state = plugin._ymjr_checkbox_state; if (state.ui.toast && state.ui.toast.parentNode) { state.ui.toast.parentNode.removeChild(state.ui.toast); } const toast = document.createElement("div"); const bgColor = isSuccess ? 'rgba(46, 204, 113, 0.95)' : 'rgba(231, 76, 60, 0.95)'; const icon = isSuccess ? '✅' : '⚠️'; toast.style.cssText = ` position: absolute; bottom: 40px; left: 50%; transform: translateX(-50%) translateY(20px); background: ${bgColor}; color: white; padding: 10px 20px; border-radius: 8px; font-family: sans-serif; font-size: 14px; font-weight: 500; box-shadow: 0 8px 24px rgba(0,0,0,0.2); backdrop-filter: blur(8px); opacity: 0; transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275); z-index: 99999; pointer-events: none; display: flex; align-items: center; gap: 8px; `; toast.innerHTML = `${icon} ${message}`; document.body.appendChild(toast); state.ui.toast = toast; requestAnimationFrame(() => { toast.style.transform = 'translateX(-50%) translateY(0)'; toast.style.opacity = '1'; }); setTimeout(() => { toast.style.transform = 'translateX(-50%) translateY(-20px)'; toast.style.opacity = '0'; setTimeout(() => { if (toast.parentNode) toast.parentNode.removeChild(toast); }, 400); }, 2500); } // ========================================== // 核心逻辑处理 // ========================================== const selectedEls = ea.getViewSelectedElements(); const textElements = selectedEls.filter(el => el.type === "text"); if (textElements.length === 0) { showModernToast(ExcalidrawAutomate.plugin, t("notice_select"), false); return; } const isAlreadyCheckbox = textElements[0]?.customData?.isCheckbox; ea.copyViewElementsToEAforEditing(textElements); const elementsToUpdate = ea.getElements(); elementsToUpdate.forEach((el) => { if (isAlreadyCheckbox) { if (el.customData) { delete el.customData.isCheckbox; } } else { el.customData = { ...el.customData, isCheckbox: true }; if (!el.text.startsWith("- [ ]") && !el.text.startsWith("- [x]")) { el.text = "- [ ] " + el.text; el.originalText = "- [ ] " + el.originalText; } } }); await ea.addElementsToView(false, false, false); const message = isAlreadyCheckbox ? t("notice_removed") : t("notice_added"); showModernToast(ExcalidrawAutomate.plugin, message, true);