Skip to content

Instantly share code, notes, and snippets.

@foeken
Created June 23, 2025 17:15
Show Gist options
  • Save foeken/2e1c69e52aa251e1ad90b9513398e0f5 to your computer and use it in GitHub Desktop.
Save foeken/2e1c69e52aa251e1ad90b9513398e0f5 to your computer and use it in GitHub Desktop.
Calendar → Tana Paste
// Download the iOS/iPadOS Scriptable App:
// https://apps.apple.com/nl/app/scriptable/id1405459188
// Run the script through a Shortcut, or as a timed notification.
// Regex rules for Supertag choice
const tagRules = [
{ tag: '3P', re: /3P/i },
{ tag: '1-1', re: /(^\S* - \S*$)/i }
];
// These events are skipped:
const skipEventRE = /(?:^|\s)(lunch|block)(?:\s|$)/i;
// These attendees are skipped:
const skipAttendeeRE = /(?:^|\s)(Andre Foeken|André Foeken)(?:\s|$)/i;
// Helpers
const fmt = d => d?.toISOString().slice(0,16).replace("T", " ") ?? '';
const pickTag = t => (tagRules.find(r => r.re.test(t))?.tag) || 'meeting';
const bullet = (lvl = 0) => '\t'.repeat(lvl) + '- ';
// The Script logic
let events = await CalendarEvent.today()
const tanaLines = events
.filter(ev => !skipEventRE.test(ev.title ?? ''))
.map(ev => {
const lines = [];
const add = (lvl, txt) => lines.push(bullet(lvl) + txt);
// Add the title and tag
add(0, `${ev.title} #${pickTag(ev.title)}`);
// Add Date::
add(1, `Date:: [[date:${fmt(ev.startDate)}/${fmt(ev.endDate)}]]`);
// Add Location::
if (ev.location) {
const locLines = ev.location.split(/\r?\n/).filter(Boolean);
if (locLines.length === 1) {
add(1, `Location:: ${locLines[0]}`);
} else {
add(1, 'Location::');
locLines.forEach(l => add(2, l));
}
}
// Add Attendees::
const attendees = ev.attendees?.filter(a => !skipAttendeeRE.test(a.name ?? '')) || []
if (attendees.length) {
add(1, 'Attendees::');
attendees.forEach(a => add(2, a.name));
}
return lines.join('\n');
});
const tanaPaste = "%%tana%%\n" + tanaLines.join('\n');
Pasteboard.copyString(tanaPaste)
Script.complete()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment