以前在 Obsidian 之中處理 Checkbox 時,如果想要有多個狀態,都需要手動處理,現在可以更懶惰了。
多狀態的 Checkbox
最單純的 Checkbox 應該只有「未完成」、「已完成」兩種狀態,但隨著時間推進與使用習慣的改變,兩種狀態的 Checkbox 開始不敷使用,如果我想表達只完成了一半呢?如果我想把這個待辦任務標示成超級重要呢?我們開始需要更多狀態的 Checkbox ,而且後續也可以利用狀態來進行篩選,於是有一些 Obsidian 的外觀主題開始支援不同 Checkbox 狀態的渲染,只要在 Checkbox 之中的文字輸入不同即可。
但實際上操作的時候,我們都需要將游標移動到 [ ] 的中間,並填入對應的英文字母,久了還是有點麻煩。
改善方案
我想 Templater 應該是每個 Obsidian 使用者必備的第三方 Plugin 吧,不過很多人都忽略了這個 Plugin 的一個非常強大的功能,那就是可以執行 Javascript ,於是我在網路上搜尋了一下解法,也確實找到了,於是我參考了這個網站,做出了以下的 Template,只要先建立一個 todo ,直接在上面插入這個 template 就可以嘗試看看了。
<%*
// 已定義的核取方塊狀態列表。可依需求調整。
// 參考: https://github.com/SilentVoid13/Templater/discussions/1236
const states = {
' ': 'Unchecked | Pending',
'x': 'Checked | Done',
'>': 'Rescheduled',
'<': 'Scheduled',
'!': 'Important',
'-': 'Cancelled',
'/': 'In Progress',
'?': 'Question',
'*': 'Star',
'n': 'Note',
'l': 'Location',
'i': 'Information',
'I': 'Idea',
'S': 'Savings | Price | Money',
'p': 'Pro | Good',
'c': 'Con | Bad',
'b': 'Bookmark',
'"': 'Quote',
'u': 'Up | Like',
'd': 'Down | Dislike',
'w': 'Win',
'k': 'Key',
'f': 'Fire | Hot',
'': 'Clear Checkbox | Reset'
};
// 是否顯示選擇新狀態的輸入對話框。
// 設為 false 時,核取方塊會依序循環切換所有狀態。
let promptUser = true;
// 預設狀態:未完成的核取方塊。
// 若目前這行不是核取方塊,或是狀態未知,則會套用此狀態。
const defaultState = ' ';
// 輔助函式
const getLabel = key => (key ? `[${key}] ` : '') + `${states[key]}`;
const setState = (key, line) => line.replace(
/^(\s*)(-|\*|\d\.)?(\s+\[.\])?\s*/,
'$1' + (key ? `- [${key}] ` : '')
);
// 準備變數,檢查目前編輯器所在的行
const stateKeys = Object.keys(states);
const editor = app.workspace.activeLeaf.view.editor;
const cursor = editor.getCursor('from');
const currentLine = editor.getLine(cursor.line);
const stateMatch = currentLine.match(/^[\s-\*]+\s\[(.)\]\s/);
const currentState = stateMatch?.[1] || '';
let newState = defaultState;
//如果找到有效的核取方塊狀態,就尋找下一個狀態
if (currentState) {
let nextIndex = stateKeys.indexOf(currentState) + 1
if (nextIndex >= stateKeys.length) {
nextIndex = 0;
}
newState = stateKeys[nextIndex];
}
// 可選分支:讓使用者手動選擇新狀態
if (promptUser) {
// 顯示「下一個狀態」作為第一個選項,讓它自動被選取
const suggestLabels = [getLabel(newState)];
const suggestKeys = [newState];
stateKeys.map(key => {
if (key === newState) {
return;
}
suggestLabels.push(getLabel(key))
suggestKeys.push(key)
});
newState = await tp.system.suggester(
suggestLabels,
suggestKeys,
false,
"Select new checkbox state"
);
}
// 如果選擇的狀態是字串,就套用到當前行
if ('string' === typeof newState) {
editor.setLine(cursor.line, setState(newState, currentLine));
}
%>