despite being a bear of little brain with ADHD i have managed to make a couple of extensions all by myself and it is fun and good but now i am lost.
i asked mistral ai (a French chatbot that speaks perfect english) to help me make a popclip snippet which makes only the sentence i am currently writing visible.
ok not help. make it for me.
the purpose is to enable my brain to focus on that sentence and not get distracted by the other text on a page.
so my proposed method was as follows
cut the selected sentence and put it on the clipboard.
insert 20 empty lines where the sentence was
paste the sentence into the middle of the space it created i.e. after the 10th line break
insert the cursor at the end of the pasted sentence.
// Get the selected text (the sentence)
const sentence = input.text;
// Get the current selection range
const selection = app.selectedText();
const start = selection.range()[0];
const end = selection.range()[1];
// Cut the sentence to clipboard
app.setTheClipboardTo(sentence);
// Insert 20 empty lines
const emptyLines = Array(21).join('\n');
app.insert(emptyLines);
// Move cursor back to the start of the empty lines
app.setSelectedTextRange([start, start]);
// Paste the sentence
app.paste();
// Move cursor back to the original position
app.setSelectedTextRange([start + sentence.length, start + sentence.length]);
return sentence;
}
it installs ok but it doesn’t do anything. probably there is some obvious reason that a human eye would see straightaway, but not this one, and not the other one either.
Hey, the extension installs but does nothing because the code you got back isn’t a PopClip extension at all - it’s JXA (JavaScript for Automation), the AppleScript-in-JS dialect that macOS uses for scripting apps from the outside. Application.currentApplication(), app.selectedText(), app.insert(), app.paste() - none of those exist in PopClip’s JS environment. The script throws on the first line and dies silently, which is why you see no behaviour.
PopClip scripts run in a sandboxed JS runtime with a global popclip object. The relevant bits for what you’re doing:
• popclip.input.text - the selected text
• popclip.pasteText(str) - replaces the selection with str (no separate cut/paste dance needed; it handles clipboard + Cmd+V internally)
• popclip.pressKey(key, modifiers) - simulates a keystroke
The snippet header also needs to be YAML, not JS comments. // # popclip won’t register as a snippet; it has to start with # popclip.
Here’s a working version that does what you described - pads the sentence with blank lines and parks the caret at the end of it:
name: Isolate Sentence
icon: iconify:material-symbols:edit
description: Pad the selected sentence with blank lines for focused writing.
requirements: [text, paste]
javascript: |
const sentence = popclip.input.text;
const pad = "\n".repeat(10);
popclip.pasteText(pad + sentence + pad);
// After paste, caret sits below the trailing newlines.
// Walk it up to the sentence line, then jump to end of line.
for (let i = 0; i < 10; i++) {
popclip.pressKey(0x7E); // up arrow (kVK_UpArrow)
}
popclip.pressKey("command right"); // Mac's equivalent of End
A couple of things worth knowing:
The whole “cut, then insert blanks, then paste back in the middle” routine isn’t necessary - pasteText replaces the selection in one atomic step, so you just build the padded string in JS and paste it. Much simpler.
On the caret positioning: pasteText leaves the caret after the last trailing newline, so the script walks it back up ten lines and then fires Cmd+Right to land at the end of the sentence. Mac keyboards don’t have a real End key, which is why Cmd+Right is the right call here.
Works well in TextEdit, Obsidian, Bear, most browser textareas. If you ever hit an app where the arrows seem to fire before the paste has settled (rare, but some slower Electron apps can do this), drop a setTimeout between the paste and the arrow loop.
Nice idea for a focus tool, by the way..​​​​​​​​​​​​​​​
this is wonderful thank you so much! it works great in iA writer but in FSnotes perhaps it does the thing you mention where it only moves the insertion point up 10 lines so i’ll have to experiment with the setTimeout…