Ok. I am dropping the above, as I was tired last night, I and was trying to hastily merge old code together.
My thinking was, well, this should be easy! (famous last words!)
Here is my current attempt (based on excellent code from @complexpoint and @jessegrosjean), which is working, but I get an error: Error -1708: Message not understood.
(() => {
"use strict";
const filePath = "~/Library/Mobile Documents/iCloud~is~workflow~my~workflows/Documents/Links.txt";
const strFullPath = ObjC.unwrap($(filePath).stringByExpandingTildeInPath);
const tp = Application("TaskPaper");
const document = tp.open(Path(strFullPath));
// Rob Trew @2021, with parts from Jesse Grosjean and Jim Krenz
// main :: IO ()
const main = () => {
const windows = Application("Safari").windows;
return either(
// Left channel message in case of a problem.
msg => alert(
"Copy Safari tab list to TaskPaper"
)(msg)
)(
// Simple string value returned if all is well.
x => x
)(
bindLR(
0 < windows.length ? (
Right(windows.at(0).tabs)
) : Left("No windows open in Safari")
)(tabs => {
const
dateString = taskPaperDateString(
new Date()
).slice(0, 10),
linkStrings = zipWith(
name => link =>
`- ${name} → ${link} @link(${dateString})`
)(
tabs.name()
)(
tabs.url()
);
return Right(
Application("TaskPaper")
.document
.evaluate({
script: `${TaskPaperContext}`,
withOptions: {
projectName: "Links",
linkTexts: linkStrings
}
})
);
})
);
};
// -------------------- TASKPAPER --------------------
// TaskPaperContext :: Editor -> Dict -> String
const TaskPaperContext = (editor, options) => {
const tp3Main = () => {
const
outline = editor.outline,
xs = options.linkTexts,
targetProject = projectFoundOrCreated(
outline
)(options.projectName);
outline.groupUndoAndChanges(() => {
let children = xs.map(txt => outline.createItem(txt))
targetProject.insertChildrenBefore(children, targetProject.firstChild);
});
return xs.join("\n");
};
// projectFoundOrCreated :: TP3Outline ->
// String -> Bool -> TP3Item
const projectFoundOrCreated = outline =>
// A reference to a TaskPaper Project item,
// either found in the outline, or created
// at the top of it.
projectName => {
const
k = projectName.trim(),
matches = outline.evaluateItemPath(
`//project "${k}"`
),
blnFound = 0 < matches.length,
project = blnFound ? (
matches[0]
) : outline.createItem(`${k}:`);
return (
blnFound || outline.insertItemsBefore(
project,
outline.root.firstChild
),
project
);
};
return tp3Main();
};
// ----------------------- JXA -----------------------
// alert :: String => String -> IO String
const alert = title =>
s => {
const sa = Object.assign(
Application("System Events"), {
includeStandardAdditions: true
});
return (
sa.activate(),
sa.displayDialog(s, {
withTitle: title,
buttons: ["OK"],
defaultButton: "OK"
}),
s
);
};
// --------------------- GENERIC ---------------------
// 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);
// iso8601Local :: Date -> String
const iso8601Local = dte =>
new Date(dte - (6E4 * dte.getTimezoneOffset()))
.toISOString();
// taskPaperDateString :: Date -> String
const taskPaperDateString = dte => {
const [d, t] = iso8601Local(dte).split("T");
return [d, t.slice(0, 5)].join(" ");
};
// zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
const zipWith = f =>
// A list constructed by zipping with a
// custom function, rather than with the
// default tuple constructor.
xs => ys => xs.map(
(x, i) => f(x)(ys[i])
).slice(
0, Math.min(xs.length, ys.length)
);
return main();
document.save();
})();
My other question: can the four const at the beginning be improved or condensed?