本脚本基于以下帖子提供的代码调整的(基于个人需求)
QuickaddJS实现的悬浮窗指令 - 经验分享 - Obsidian 中文论坛
主要调整方向
1.新增命令按钮时候会提供默认样式
2.新建命令按钮的时候可以自定义按钮名,不输入的话就是命令名作为按钮名
3.提供了按钮包含按钮分类文件夹和不包含按钮分类文件夹两种
包含文件夹
var path = '00-MO/4.脚本/config/folderCommands.json';//保存的配置json路径
var flag = true;//是否点击命令按钮便关闭弹窗
var buttonClass = 'grad_button g_blue'//按钮的样式class
var folderClass = 'grad_button g_yellow'//文件夹的样式class
var folderId = '0'//文件夹id
module.exports = async function (params) {
const { app, quickAddApi } = params;
// 添加在脚本开始处
const existingToolbar = document.querySelector('.float-toolbar');
if (existingToolbar) {
document.body.removeChild(existingToolbar);
return;
}
// 读取命令配置
let { commands, windowConfig, inputBoxConfig = {} } = await loadCommands();
// console.log('commands', commands);
// console.log('windowConfig', windowConfig);
// console.log('inputBoxConfig', inputBoxConfig);
const useLastPosition = windowConfig.useLastPosition || false;
// 创建弹窗容器
const popupContainer = document.createElement('div');
popupContainer.setAttribute('id', 'quickAddJsFolderCommandsDiv');
popupContainer.classList.add('float-toolbar');
Object.assign(popupContainer.style, {
position: 'fixed',
width: windowConfig.width || '300px',
height: windowConfig.height || 'auto',
left: useLastPosition && windowConfig.left ? windowConfig.left : '50%',
top: useLastPosition && windowConfig.top ? windowConfig.top : '50%',
transform: useLastPosition ? 'none' : 'translate(-50%, -50%)',
backgroundColor: 'var(--background-primary)',
padding: '20px',
border: '1px solid var(--background-modifier-border)',
borderRadius: '10px',
boxShadow: '0 4px 6px rgba(0, 0, 0, 0.3)',
zIndex: '9999',
cursor: 'move',
userSelect: 'none',
resize: 'none',
overflow: 'hidden',
display: 'block',
});
// 创建命令按钮容器
const commandButtonsContainer = document.createElement('div');
commandButtonsContainer.classList.add('command-buttons-container');
// 创建命令按钮
function createCommandButtons(id) {
commandButtonsContainer.innerHTML = ''; // 清空现有按钮
//id是按钮id,folderId是按钮的文件夹id,type是文件夹还是按钮,0是文件夹,1是按钮
commands.forEach((cmd) => {
if (cmd.folderId === id) {
const button = document.createElement('button');
button.textContent = cmd.text;
button.className = cmd.class;
button.addEventListener('click', async (e) => {
if (e.button === 0) {
// 左键点击
try {
if (cmd.type === '1') {
// console.log('按钮执行===' + cmd.commandId);
// 如果是按钮,执行命令
await app.commands.executeCommandById(cmd.commandId);
if (flag) {
document.body.removeChild(document.getElementById('quickAddJsFolderCommandsDiv'));
}
} else {
// console.log('文件夹执行');
//打开folderId为id的文件夹下的内容
// removeButtonsByClass('quickAddJsFolderCommandsDiv');
folderId = cmd.id;
createCommandButtons(folderId);
}
} catch (error) {
console.error(`执行命令 ${cmd.text} 失败:`, error);
new Notice(`执行命令 ${cmd.text} 失败,打开控制台查看详情`, 1000);
}
}
});
// 右键点击删除按钮
button.addEventListener('contextmenu', (e) => {
e.preventDefault();
// 更新命令列表
commands = commands.filter((c) => c.text !== cmd.text); // 🚩 现在可以重新赋值
saveCommands(commands);
createCommandButtons(folderId);
});
commandButtonsContainer.appendChild(button);
}
});
}
createCommandButtons(folderId);
// 删除按钮
function removeButtonsByClass(className) {
const buttons = document.querySelectorAll(`.${className}`);
buttons.forEach(button => button.remove());
}
// 创建关闭按钮
const closeButton = document.createElement('button');
closeButton.textContent = '×';
Object.assign(closeButton.style, {
position: 'absolute',
top: '5px',
right: '5px',
width: '20px',
height: '20px',
padding: '0',
border: 'none',
background: 'transparent',
color: 'var(--text-muted)',
fontSize: '20px',
lineHeight: '20px',
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
borderRadius: '50%',
transition: 'background-color 0.2s, color 0.2s',
});
// 添加悬停效果
closeButton.addEventListener('mouseover', () => {
closeButton.style.backgroundColor = 'var(--background-modifier-hover)';
closeButton.style.color = 'var(--text-normal)';
});
closeButton.addEventListener('mouseout', () => {
closeButton.style.backgroundColor = 'transparent';
closeButton.style.color = 'var(--text-muted)';
});
// 定义事件处理函数
let isDragging = false;
let offsetX, offsetY;
const handleMouseMove = (e) => {
if (isDragging) {
const x = e.clientX - offsetX;
const y = e.clientY - offsetY;
const maxX = window.innerWidth - popupContainer.offsetWidth / 3;
const maxY = window.innerHeight - popupContainer.offsetHeight / 3;
popupContainer.style.left = `${Math.max(0, Math.min(x, maxX))}px`;
popupContainer.style.top = `${Math.max(0, Math.min(y, maxY))}px`;
popupContainer.style.transform = 'none';
}
};
const handleMouseUp = () => {
if (isDragging) {
isDragging = false;
// 保存新位置
saveWindowConfig({
left: popupContainer.style.left,
top: popupContainer.style.top,
width: popupContainer.style.width,
height: popupContainer.style.height,
useLastPosition: true, // 自动启用记忆位置
});
}
};
// 修改关闭按钮的点击处理程序
closeButton.addEventListener('click', () => {
closeToolbar(handleMouseMove, handleMouseUp, popupContainer);
});
// 创建新增命令按钮
const addCommandButton = document.createElement('button');
addCommandButton.textContent = '新增命令';
//设置新增命令样式
// addCommandButton.classList.add('circle_btn','g_lightgreen');
addCommandButton.style = 'text-align: center; text-transform: uppercase; cursor: pointer; font-size: 20px; letter-spacing: 4px; position: relative; background-color:rgb(37, 152, 234); border: none; color: #fff; padding: 20px; width: 100%; transition-duration: 0.4s; overflow: hidden; box-shadow: 0 5px 15px #193047; border-radius: 4px;';
addCommandButton.addEventListener('click', async () => {
console.log('按键创建===' + folderId);
var element = document.getElementById('quickAddJsFolderCommandsDiv');
//隐藏命令框
element.style.display = 'none';
const allCommands = app.commands.listCommands();
const selectedCommand = await quickAddApi.suggester(
allCommands.map((cmd) => cmd.name),
allCommands
);
if (!selectedCommand) {
new Notice('未选择命令');
element.style.display = 'block';
return;
}
const newCommand = {
text: selectedCommand.name,
folderId: folderId,
type: '1',
class: buttonClass,
commandId: selectedCommand.id,
};
// const popupInput = document.createElement('select');
const popupInput = document.createElement('input');
popupInput.id = 'inputbox';
let useLastPosition = inputBoxConfig.useLastPosition || false;
let inputName
Object.assign(popupInput.style, {
position: 'fixed',
width: inputBoxConfig.width || '300px',
height: inputBoxConfig.height || 'auto',
left: useLastPosition && inputBoxConfig.left ? inputBoxConfig.left : '50%',
top: useLastPosition && inputBoxConfig.top ? inputBoxConfig.top : '50%',
transform: useLastPosition ? 'none' : 'translate(-50%, -50%)',
backgroundColor: 'var(--background-primary)',
padding: '20px',
border: '1px solid var(--background-modifier-border)',
borderRadius: '10px',
boxShadow: '0 4px 6px rgba(0, 0, 0, 0.3)',
zIndex: '9999',
cursor: 'move',
userSelect: 'none',
resize: 'none',
overflow: 'hidden',
});
// options.forEach(option => {
// const opt = document.createElement('option');
// opt.value = option.value;
// opt.textContent = option.text;
// popupSelect.appendChild(opt);
// });
popupInput.addEventListener('keydown', function (event) {
// 检查是否按下了回车键
if (event.keyCode === 13) {
// 获取输入框的值并赋给变量
inputName = popupInput.value;
//恢复命令面板
element.style.display = 'block';
// 关闭输入框,例如从文档中移除
document.body.removeChild(popupInput);
if (/^\s*$/.test(inputName)) {
console.log("字符串为空或只包含空白字符");
} else {
newCommand.text = inputName
}
commands.push(newCommand);
saveCommands(commands);
createCommandButtons(folderId); // 刷新按钮
}
document.removeEventListener('mousemove', handleMouseMove);
document.removeEventListener('mouseup', handleMouseUp);
});
// 将弹窗添加到页面中
document.body.appendChild(popupInput);
// 定义事件处理函数
let isDragging = false;
let offsetX, offsetY;
// 拖拽功能实现
popupInput.addEventListener('mousedown', (e) => {
// if (e.target === resizeHandle) return; // 如果点击的是调整手柄,则不触发拖拽
isDragging = true;
offsetX = e.clientX - popupInput.getBoundingClientRect().left;
offsetY = e.clientY - popupInput.getBoundingClientRect().top;
});
const handleMouseMove = (e) => {
if (isDragging) {
const x = e.clientX - offsetX;
const y = e.clientY - offsetY;
const maxX = window.innerWidth - popupInput.offsetWidth / 3;
const maxY = window.innerHeight - popupInput.offsetHeight / 3;
popupInput.style.left = `${Math.max(0, Math.min(x, maxX))}px`;
popupInput.style.top = `${Math.max(0, Math.min(y, maxY))}px`;
popupInput.style.transform = 'none';
}
};
const handleMouseUp = () => {
if (isDragging) {
isDragging = false;
// 保存新位置
saveInputBoxConfig({
left: popupInput.style.left,
top: popupInput.style.top,
width: popupInput.style.width,
height: popupInput.style.height,
useLastPosition: true, // 自动启用记忆位置
});
}
};
document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('mouseup', handleMouseUp);
});
// 右键创建文件夹
addCommandButton.addEventListener('contextmenu', (e) => {
console.log('文件夹创建===' + folderId);
var element = document.getElementById('quickAddJsFolderCommandsDiv');
//隐藏命令框
element.style.display = 'none';
const popupInput = document.createElement('input');
popupInput.id = 'inputbox';
let useLastPosition = inputBoxConfig.useLastPosition || false;
let inputName
const newCommand = {
text: '',
folderId: folderId,
type: '0',
class: folderClass,
commandId: '文件夹',
};
Object.assign(popupInput.style, {
position: 'fixed',
width: inputBoxConfig.width || '300px',
height: inputBoxConfig.height || 'auto',
left: useLastPosition && inputBoxConfig.left ? inputBoxConfig.left : '50%',
top: useLastPosition && inputBoxConfig.top ? inputBoxConfig.top : '50%',
transform: useLastPosition ? 'none' : 'translate(-50%, -50%)',
backgroundColor: 'var(--background-primary)',
padding: '20px',
border: '1px solid var(--background-modifier-border)',
borderRadius: '10px',
boxShadow: '0 4px 6px rgba(0, 0, 0, 0.3)',
zIndex: '9999',
cursor: 'move',
userSelect: 'none',
resize: 'none',
overflow: 'hidden',
});
popupInput.addEventListener('keydown', function (event) {
// 检查是否按下了回车键
if (event.keyCode === 13) {
// 获取输入框的值并赋给变量
inputName = popupInput.value;
// 关闭输入框,例如从文档中移除
document.body.removeChild(popupInput);
if (/^\s*$/.test(inputName)) {
console.log("字符串为空或只包含空白字符");
new Notice('未输入文件夹名');
// createCommandButtons();
} else {
newCommand.text = inputName
commands.push(newCommand);
saveCommands(commands);
createCommandButtons(); // 刷新按钮
}
// console.log('newCommand', newCommand);
}
//恢复命令面板
element.style.display = 'block';
document.removeEventListener('mousemove', handleMouseMove);
document.removeEventListener('mouseup', handleMouseUp);
});
document.body.appendChild(popupInput);
// 定义事件处理函数
let isDragging = false;
let offsetX, offsetY;
// 拖拽功能实现
popupInput.addEventListener('mousedown', (e) => {
// if (e.target === resizeHandle) return; // 如果点击的是调整手柄,则不触发拖拽
isDragging = true;
offsetX = e.clientX - popupInput.getBoundingClientRect().left;
offsetY = e.clientY - popupInput.getBoundingClientRect().top;
});
const handleMouseMove = (e) => {
if (isDragging) {
const x = e.clientX - offsetX;
const y = e.clientY - offsetY;
const maxX = window.innerWidth - popupInput.offsetWidth / 3;
const maxY = window.innerHeight - popupInput.offsetHeight / 3;
popupInput.style.left = `${Math.max(0, Math.min(x, maxX))}px`;
popupInput.style.top = `${Math.max(0, Math.min(y, maxY))}px`;
popupInput.style.transform = 'none';
}
};
const handleMouseUp = () => {
if (isDragging) {
isDragging = false;
// 保存新位置
saveInputBoxConfig({
left: popupInput.style.left,
top: popupInput.style.top,
width: popupInput.style.width,
height: popupInput.style.height,
useLastPosition: true, // 自动启用记忆位置
});
}
};
document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('mouseup', handleMouseUp);
});
// 创建调整大小的手柄
const resizeHandle = document.createElement('div');
Object.assign(resizeHandle.style, {
position: 'absolute',
bottom: '0',
right: '0',
width: '10px',
height: '10px',
backgroundColor: 'var(--background-modifier-border)',
cursor: 'se-resize',
});
// 拖拽调整大小的逻辑
let isResizing = false;
let startX, startY, startWidth, startHeight;
resizeHandle.addEventListener('mousedown', (e) => {
isResizing = true;
startX = e.clientX;
startY = e.clientY;
startWidth = parseInt(document.defaultView.getComputedStyle(popupContainer).width, 10);
startHeight = parseInt(document.defaultView.getComputedStyle(popupContainer).height, 10);
e.preventDefault(); // 防止文本选中
});
document.addEventListener('mousemove', (e) => {
if (isResizing) {
const newWidth = startWidth + (e.clientX - startX);
const newHeight = startHeight + (e.clientY - startY);
popupContainer.style.width = `${newWidth}px`;
popupContainer.style.height = `${newHeight}px`;
}
});
document.addEventListener('mouseup', () => {
if (isResizing) {
isResizing = false;
// 保存调整后的尺寸
saveWindowConfig({
width: popupContainer.style.width,
height: popupContainer.style.height,
left: popupContainer.style.left,
top: popupContainer.style.top,
});
}
});
// 将命令按钮、新增按钮、关闭按钮和调整手柄添加到弹窗容器中
popupContainer.appendChild(commandButtonsContainer);
popupContainer.appendChild(addCommandButton);
popupContainer.appendChild(closeButton);
popupContainer.appendChild(resizeHandle);
// 将弹窗添加到页面中
document.body.appendChild(popupContainer);
// 拖拽功能实现
popupContainer.addEventListener('mousedown', (e) => {
if (e.target === resizeHandle) return; // 如果点击的是调整手柄,则不触发拖拽
isDragging = true;
offsetX = e.clientX - popupContainer.getBoundingClientRect().left;
offsetY = e.clientY - popupContainer.getBoundingClientRect().top;
});
// 拖拽过程的具体处理
document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('mouseup', handleMouseUp);
};
function closeToolbar(handleMouseMove, handleMouseUp, popupContainer) {
document.removeEventListener('mousemove', handleMouseMove);
document.removeEventListener('mouseup', handleMouseUp);
// 移除弹窗
document.body.removeChild(popupContainer);
}
async function loadCommands() {
try {
const content = await app.vault.adapter.read(path);
// 兼容旧格式(纯数组)和新格式(带配置的对象)
const result = JSON.parse(content);
return Array.isArray(result)
? { commands: result }
: {
commands: result.commands || [],
windowConfig: result.windowConfig || {},
inputBoxConfig: result.inputBoxConfig || {},
};
} catch (error) {
console.error('读取命令文件失败:', error);
return { commands: [] };
}
}
async function saveInputBoxConfig(config) {
try {
const existing = await loadCommands();
const newData = {
commands: existing.commands,
windowConfig: existing.windowConfig || {},
inputBoxConfig: {
...existing.inputBoxConfig,
...config,
},
};
await app.vault.adapter.write(path, JSON.stringify(newData, null, 2));
} catch (error) {
console.error('保存输入框配置失败:', error);
}
}
async function saveWindowConfig(config) {
try {
const existing = await loadCommands();
const newData = {
commands: existing.commands,
inputBoxConfig: existing.inputBoxConfig || {},
windowConfig: {
...existing.windowConfig,
...config,
},
};
await app.vault.adapter.write(path, JSON.stringify(newData, null, 2));
} catch (error) {
console.error('保存窗口配置失败:', error);
}
}
async function saveCommands(commands) {
try {
const existing = await loadCommands();
const newData = {
commands,
inputBoxConfig: existing.inputBoxConfig || {},
windowConfig: existing.windowConfig || {},
};
await app.vault.adapter.write(path, JSON.stringify(newData, null, 2));
} catch (error) {
console.error('保存命令文件失败:', error);
}
}
配置json
{
"commands": [
{
"text": "常用命令",
"folderId": "0",
"type": "0",
"class": "grad_button g_yellow",
"commandId": "文件夹"
}
],
"inputBoxConfig": {
"left": "534px",
"top": "431px",
"width": "300px",
"height": "auto",
"useLastPosition": true
},
"windowConfig": {
"left": "973px",
"top": "59px",
"width": "289px",
"height": "353px",
"useLastPosition": true
}
}
不包含的我放链接了(不然帖子内容过长)
https://wwbr.lanzouy.com/b00ro15a7g
密码:7x91
操作
左键点击按钮激活命令或者打开按钮分类文件夹,右键删除
左键点击新建命令按钮,会弹出命令选择框,选择后是按钮名输入框,点击回车确认(不输入的话则是以命令名作为按钮名),右键点击新建命令按钮则是会弹出输入框,输入内容并回车会创建一个按钮分类文件夹,并进入该文件夹(这时候创建按钮的话,则是会都在这个按钮分类文件夹中)