Skip to content

Instantly share code, notes, and snippets.

@nickcheng
Created December 10, 2025 11:45
Show Gist options
  • Select an option

  • Save nickcheng/f2adaef19d1621a3d6da5af59edf07c8 to your computer and use it in GitHub Desktop.

Select an option

Save nickcheng/f2adaef19d1621a3d6da5af59edf07c8 to your computer and use it in GitHub Desktop.
This is a script for using in the Draft app on macOS. It toggles the checkbox state on selected lines.
// This is a script for using in the Draft app on macOS
// It toggles the checkbox state on selected lines
// Toggle tasks marks on selected lines
var offState = "- [ ]";
var onState = "- [x]";
// Normalize leading spaces to multiples of 4 (0, 4, 8, 12, ...)
function normalizeIndent(spacesCount) {
return Math.floor(spacesCount / 4) * 4;
}
// grab state
var [lnStart, lnLen] = editor.getSelectedLineRange();
var lnText = editor.getTextInRange(lnStart, lnLen);
var [selStart, selLen] = editor.getSelectedRange();
// just add mark if empty line
if (lnText.length == 0 || lnText == "\n") {
editor.setSelectedText(`${offState} `);
editor.setSelectedRange(selStart + offState.length + 1, 0);
}
else {
// create line array and tracking vars
var lines = lnText.split('\n');
var startOffset = 0;
var lengthAdjust = 0;
var flTrailing = false;
if (lines[lines.length - 1] == "") {
lines.pop();
flTrailing = true;
}
var newLines = [];
const re = /^(\s*[-\*] )?(\[ \]|\[x\])?(.*)/;
const containsRe = /^(\s*[-\*] )?(\[ \]|\[x\])(.*)/;
// determine if we are removing or adding marks
var flRemoving = true;
for (var line of lines) {
if (line.length > 0 && !line.match(containsRe)) {
flRemoving = false;
}
}
if (!flRemoving) {
// add marks
var isFirst = true;
for (var line of lines) {
const match = re.exec(line);
if (match[2] || line.length == 0) {
newLines.push(line);
}
else {
var prefix = match[1];
var suffix = match[3];
if (!prefix) { prefix = ""; }
if (!suffix) { suffix = ""; }
if (prefix) {
// Already has a list marker (- or *), just add the checkbox
// Normalize leading spaces to multiples of 4
var prefixLeadingSpaces = prefix.match(/^(\s*)/)[1];
var prefixContent = prefix.trimStart(); // "- " or "* "
var normalizedSpaces = normalizeIndent(prefixLeadingSpaces.length);
var spacesRemoved = prefixLeadingSpaces.length - normalizedSpaces;
var normalizedPrefix = " ".repeat(normalizedSpaces) + prefixContent;
newLines.push(`${normalizedPrefix}[ ] ${suffix}`);
if (isFirst) {
startOffset = 4 - spacesRemoved; // "[ ] ".length minus removed spaces
}
else {
lengthAdjust += (4 - spacesRemoved);
}
} else {
// No list marker, add full offState (- [ ])
// Normalize leading spaces to multiples of 4
var leadingSpaces = suffix.match(/^(\s*)/)[1];
var suffixContent = suffix.trimStart();
var normalizedSpaces = normalizeIndent(leadingSpaces.length);
var spacesRemoved = leadingSpaces.length - normalizedSpaces;
var normalizedIndent = " ".repeat(normalizedSpaces);
newLines.push(`${normalizedIndent}${offState} ${suffixContent}`);
if (isFirst) {
startOffset = offState.length + 1 - spacesRemoved;
}
else {
lengthAdjust += (offState.length + 1 - spacesRemoved);
}
}
}
isFirst = false;
}
} else {
// remove marks
var isFirst = true;
for (var line of lines) {
if (line.trim() == "") {
newLines.push(line);
continue;
}
const match = re.exec(line);
var prefix = match[1];
var suffix = match[3];
var state = match[2];
if (!prefix) { prefix = ""; }
if (!suffix) { suffix = ""; }
if (suffix.startsWith(" ")) {
suffix = suffix.substr(1);
if (isFirst) { startOffset -= 1; }
else { lengthAdjust -= 1; }
}
newLines.push(`${prefix}${suffix}`);
if (isFirst) {
startOffset -= state.length;
}
else {
lengthAdjust -= state.length;
}
isFirst = false;
}
}
// update text and selection
if (flTrailing) {
newLines.push("");
}
var newText = newLines.join("\n");
editor.setTextInRange(lnStart, lnLen, newText);
editor.setSelectedRange(selStart + startOffset, selLen + lengthAdjust);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment