Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save stephancasas/5e3f17978bff5e8387adf0731c749b48 to your computer and use it in GitHub Desktop.
Save stephancasas/5e3f17978bff5e8387adf0731c749b48 to your computer and use it in GitHub Desktop.
AppleScript: Format selected text using unicode monospace glyph equivalents.
#!/usr/bin/env osascript -l JavaScript
const $attr = Ref();
const $windows = Ref();
const $children = Ref();
const UNICODE_PATTERN = '11110xxx10xxxxxx10xxxxxx10xxxxxx';
function run(_) {
const app = $.AXUIElementCreateApplication(
$.NSWorkspace.sharedWorkspace.frontmostApplication.processIdentifier,
);
// Capture the focused UI element of the frontmost app.
$.AXUIElementCopyAttributeValue(app, 'AXFocusedUIElement', $attr);
const focusedElement = $attr[0].js;
// Does the captured UI element have a selected text attribute?
const $attributes = Ref();
$.AXUIElementCopyAttributeNames(focusedElement, $attributes);
if (!$attributes[0].js.some((attr) => attr.js == 'AXSelectedText')) {
return;
}
// Capture the selected text string from the captured UI element.
$.AXUIElementCopyAttributeValue(focusedElement, 'AXSelectedText', $attr);
const selectedText = $attr[0].js;
// Exit if no content in captured string.
if (!selectedText) {
return;
}
// Convert captured string to unicode monospace character equivalents.
const monospaceText = selectedText
.split('')
.map((char) => {
if (!char.match(/(\d|[a-z])/i)) {
return char;
}
const offset = !!char.match(/\d/)
? 48 // numeric
: char.toLowerCase() == char
? 97 // lowercase
: 65; // uppercase
let unicodeDecimal =
char.charCodeAt(0) -
offset +
(120432 + (offset == 97 ? 26 : offset == 48 ? 390 : 0));
let unicodeBinary = '';
while (unicodeDecimal > 0) {
unicodeBinary = `${parseInt(unicodeDecimal) % 2}${unicodeBinary}`;
unicodeDecimal = parseInt(unicodeDecimal / 2);
}
let take = 0;
const utf8Binary = UNICODE_PATTERN.split('')
.reverse()
.map((seg) => {
if (seg == 'x') {
if (take >= unicodeBinary.length) {
return '0';
}
take++;
return unicodeBinary[unicodeBinary.length - take];
}
return seg;
});
const utf8Decimal = utf8Binary
.reverse()
.join('')
.split('')
.reduce((acc, cur) => acc * 2 + parseInt(cur), 0);
const utf8Percent = utf8Decimal
.toString(16)
.split(/(..)/g)
.filter((byte) => !!byte)
.map((byte) => `%${byte}`)
.join('');
return $.NSString.stringWithString(utf8Percent)
.stringByRemovingPercentEncoding.js;
})
.join('');
// Copy the monospaced text to the pasteboard.
$.NSPasteboard.generalPasteboard.clearContents;
$.NSPasteboard.generalPasteboard.writeObjects([monospaceText]);
// Capture the frontmost app's menu bar.
$.AXUIElementCopyAttributeValue(app, 'AXMenuBar', $attr);
// Locate the "Edit" menu bar item.
$.AXUIElementCopyAttributeValue($attr[0].js, 'AXChildren', $children);
const editMenuBarItem = $children[0].js.find((child) => {
$.AXUIElementCopyAttributeValue(child, 'AXTitle', $attr);
return `${$attr[0].js}`.toLowerCase() == 'edit';
});
// Get the children of the first menu of the "Edit" menu bar item.
$.AXUIElementCopyAttributeValue(editMenuBarItem, 'AXChildren', $children);
$.AXUIElementCopyAttributeValue($children[0].js[0], 'AXChildren', $children);
// Locate the "Paste" menu item by its identifier.
let pasteMenuItem = $children[0].js.find((child) => {
$.AXUIElementCopyAttributeValue(child, 'AXIdentifier', $attr);
return `${$attr[0].js}` == 'paste' || `${$attr[0].js}` == 'paste:';
});
// Activate the "Paste" menu item if discovered.
if (!!pasteMenuItem) {
$.AXUIElementPerformAction(pasteMenuItem, 'AXPress');
return;
}
// Fallback to System Events dispatch of pasteboard accelerator.
Application('System Events').keystroke('v', { using: 'command down' });
}
// prettier-ignore
(() => {
ObjC.import('Cocoa'); // yes, it's necessary -- stop telling me it isn't
ObjC.bindFunction('AXUIElementPerformAction', ['int', ['id', 'id']]);
ObjC.bindFunction('AXValueCreate', ['id', ['unsigned int', 'void*']]);
ObjC.bindFunction('AXUIElementCopyAttributeNames', ['int', ['id', 'id*']]);
ObjC.bindFunction('AXUIElementCreateApplication', ['id', ['unsigned int']]);
ObjC.bindFunction('AXUIElementSetAttributeValue', ['int', ['id', 'id', 'id']]);
ObjC.bindFunction('AXUIElementCopyAttributeValue',['int', ['id', 'id', 'id*']]);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment