以前在 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));
}
%>

有任何問題或建議?歡迎聯絡我: ch.lien@proton.me