Created
December 10, 2025 11:45
-
-
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 file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // 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