---
name: 文本全局样式设置
description: 悬浮面板:批量管理选中文字元素的全局字重与加粗状态。
author: ymjr
version: 1.0.0
license: MIT
usage: 选中一个或多个文本元素(或包含绑定文本的图形),运行此脚本将唤起“全局样式”悬浮控制面板。通过面板的开关和输入框可以批量设置选中文字的字重 (Weight) 和加粗状态 (Bold)。
features:
- 构建原生的悬浮 DOM 控制面板
- 智能穿透 `boundElements` 获取绑定的文本内容并批量写入样式 `customData`
dependencies:
- 需要依赖 [Feature-Text-Render-Engine] 常驻底层进行渲染
autorun: false
---
/*
```javascript
*/
var locales = {
zh: {
notice_select: "请先选中包含文本的元素!",
ui_title: "🅰️ 文本全局样式",
ui_bold: "全局加粗 (Bold)",
ui_weight: "字重 (Weight)"
},
en: {
notice_select: "Please select elements containing text first!",
ui_title: "🅰️ Global Text Style",
ui_bold: "Global Bold",
ui_weight: "Font Weight"
}
};
const { Notice } = ea.obsidian;
function getSelectedTextElements() {
const api = ea.getExcalidrawAPI();
const elementsMap = api?.App?.scene?.getNonDeletedElementsMap?.() || new Map();
const selectedEls = ea.getViewSelectedElements();
const textEls = [];
for (const el of selectedEls) {
if (el.type === "text") {
textEls.push(el);
} else if (el.boundElements) {
for (const bound of el.boundElements) {
if (bound.type === "text") {
const textEl = elementsMap.get(bound.id);
if (textEl) textEls.push(textEl);
}
}
}
}
return textEls;
}
function applyStyle(type, value) {
const textEls = getSelectedTextElements();
if (textEls.length === 0) {
new Notice(t("notice_select"));
return;
}
textEls.forEach((el) => {
el.customData = el.customData || {};
if (type === 'bold') {
el.customData.bold = value;
if (!value) delete el.customData.bold;
} else if (type === 'weight') {
el.customData.font = el.customData.font || {};
el.customData.font.weight = value;
if (!value) delete el.customData.font.weight;
}
});
ea.copyViewElementsToEAforEditing(textEls);
ea.addElementsToView();
}
function createPanel() {
if (document.getElementById("ea-text-globals-panel")) return;
const panel = document.createElement('div');
panel.id = "ea-text-globals-panel";
panel.style.cssText = `position:fixed; top:120px; right:80px; width:240px; background:var(--background-primary); border:1px solid var(--background-modifier-border); box-shadow:0 8px 24px rgba(0,0,0,0.2); border-radius:8px; z-index:9999; display:flex; flex-direction:column;`;
const header = document.createElement('div');
header.style.cssText = `padding:10px 15px; background:var(--background-secondary); cursor:move; border-bottom:1px solid var(--background-modifier-border); display:flex; justify-content:space-between; align-items:center; user-select:none; border-radius:8px 8px 0 0;`;
header.innerHTML = `${t("ui_title")}`;
panel.appendChild(header);
const content = document.createElement('div');
content.style.cssText = `padding:15px; display:flex; flex-direction:column; gap:12px;`;
// 粗体开关
const rowBold = document.createElement('div');
rowBold.style.cssText = "display:flex; justify-content:space-between; align-items:center;";
rowBold.innerHTML = ``;
const boldCheck = document.createElement('input');
boldCheck.type = "checkbox";
boldCheck.onchange = (e) => applyStyle('bold', e.target.checked);
rowBold.appendChild(boldCheck);
content.appendChild(rowBold);
// 字重输入
const rowWeight = document.createElement('div');
rowWeight.style.cssText = "display:flex; justify-content:space-between; align-items:center;";
rowWeight.innerHTML = ``;
const weightInput = document.createElement('input');
weightInput.type = "text";
weightInput.placeholder = "e.g. 800";
weightInput.style.cssText = "width:80px; text-align:center; background:var(--background-modifier-form-field); border-radius:4px;";
weightInput.onchange = (e) => applyStyle('weight', e.target.value);
rowWeight.appendChild(weightInput);
content.appendChild(rowWeight);
panel.appendChild(content);
document.body.appendChild(panel);
header.querySelector('#close-btn').onclick = () => panel.remove();
let isDragging = false, startX, startY, initLeft, initTop;
header.onmousedown = (e) => {
if(e.target.tagName === 'BUTTON') return;
isDragging = true; startX = e.clientX; startY = e.clientY;
const rect = panel.getBoundingClientRect(); initLeft = rect.left; initTop = rect.top;
e.preventDefault();
document.onmousemove = (e) => {
if(!isDragging) return;
panel.style.left = (initLeft + e.clientX - startX) + 'px';
panel.style.top = (initTop + e.clientY - startY) + 'px';
};
document.onmouseup = () => { isDragging = false; document.onmousemove = null; document.onmouseup = null; };
};
}
createPanel();