I’m writing a script to insert some items into my TaskPaper document. Ideally, the items would be inserted at the cursor, but I haven’t been able to achieve this. I can only successfully add items to outline.root
but that’s not perfect. I tried editor.selection
, editor.selection.firstItem
and editor.selection.parent
, but appending items to any of these fails silently.
It would probably be helpful for us to to see the code in which you are framing that.
When you say "AppleScript"
presumably you are submitting some JavaScript to the evaluate
method of a document
?
That’s right, my mistake. Updated the title.
Since you asked, I’ve included the code here, but I’m not sure the details of the code will help much. Where I’m stuck is understanding how to append items at the cursor, instead of at the root or at an item selected using a search. The line let current = outline.root
is what needs to be adjusted. The rest of the script works fine.
function extractSafariTabs() {
const safari = Application("Safari");
const window = safari.windows[0];
return window.tabs().map(tab =>
Object({name: tab.name(), url: tab.url()})
);
}
function saveTabs(editor, options) {
'use strict';
const safariTabs = options.tabList;
const outline = editor.outline;
outline.groupUndoAndChanges(function() {
let current = outline.root;
let items = [];
for( let tabEntry of safariTabs ) {
const entry = editor.outline.createItem(`- ${tabEntry.name}`);
const urlNote = editor.outline.createItem(tabEntry.url);
entry.appendChildren([urlNote]);
items.push(entry);
}
current.appendChildren(items);
editor.setCollapsed(items);
})
return true;
}
const docs = Application("TaskPaper").documents;
const todayDoc = docs[0];
todayDoc.evaluate({
script: saveTabs.toString(),
withOptions: {
tabList: extractSafariTabs(),
}
});
@batkins In case you haven’t found yet here’s scripting API:
https://www.taskpaper.com/guide/reference/scripting/
You were close with firstItem
. I think you want to replace the problem line with:
let current = editor.selection.startItem
Perfect - that worked! Thanks, @jessegrosjean
Jesse beat me to it
Here’s a draft which adds the urls as child nodes of the tab names.
(() => {
"use strict";
// Rough Draft – Rob Trew @2021
// main :: IO ()
const main = () => {
const docs = Application("TaskPaper").documents;
return 0 < docs.length ? (
docs[0].evaluate({
script: `${TaskPaperContext}`,
withOptions: {
tabList: safariTabsNameAndURL()
}
})
) : "No documents open in TaskPaper";
};
// safariTabsNameAndURL :: IO () ->
// [{name::String, url::String}]
const safariTabsNameAndURL = () =>
Application("Safari")
.windows[0]
.tabs().map(tab =>
Object({
name: tab.name(),
url: tab.url()
})
);
// TaskPaperContext :: Editor -> Dict -> IO String
const TaskPaperContext = (editor, options) => {
const
outline = editor.outline,
safariTabs = options.tabList;
editor.selection.endItem.appendChildren(
safariTabs.map(dict => {
const
parent = outline.createItem(dict.name);
return (
parent.appendChildren(
outline.createItem(dict.url)
),
parent
);
})
);
return `${safariTabs.length} tabs added.`;
};
// MAIN ---
return main();
})();
Phew, about time I answer one of these questions before you Of course your answer is better, but sometimes you have to just celebrate being first!!!
Yours is much more informative. I was just being verbose : -)
@complexpoint Does TaskPaper make use of the strings returned from your JavaScript functions? Or is that just for debugging in Script Editor?
That’s right – I just find it speeds up writing and refactoring if every function returns an observable value.
When the top level function is embedded in a KM macro, I often return the text to a notification, or to an alert, if something unexpected seems to have happened – i.e. if the value has come back through a Left channel rather than a Right channel.
as in:
// Left :: a -> Either a b
const Left = x => ({
type: "Either",
Left: x
});
// Right :: b -> Either a b
const Right = x => ({
type: "Either",
Right: x
});
// bindLR (>>=) :: Either a ->
// (a -> Either b) -> Either b
const bindLR = m =>
mf => m.Left ? (
m
) : mf(m.Right);
// either :: (a -> c) -> (b -> c) -> Either a b -> c
const either = fl =>
// Application of the function fl to the
// contents of any Left value in e, or
// the application of fr to its Right value.
fr => e => e.Left ? (
fl(e.Left)
) : fr(e.Right);