In any case, generalizing to item paths (rather than tag names), and allowing for indented projects (as well as full-left projects) a first draft might look something like:
Expand disclosure triangle to view JS source
(() => {
"use strict";
// TaskPaper 3 focus cycled to the next item
// matching a given item path.
// Rob Trew @2022
// Ver 0.01
// --------------------- OPTIONS ---------------------
// eslint-disable-next-line quotes
const itemPath = '//project "—"';
// ---------- TASKPAPER EVALUATION CONTEXT -----------
const tp3Context = (editor, options) => {
const outline = editor.outline;
// All tasks on options.itemPath
const
cycleItems = outline.evaluateItemPath(
`${options.itemPath}`
);
// Any current focus item, and its position,
// if any, in the cycle.
const
maybeFocusedItem = editor.focusedItem,
maybePosn = null !== maybeFocusedItem ? (
cycleItems.findIndex(
x => maybeFocusedItem.id === x.id
)
) : -1;
// Zero-based index of next position in the cycle,
// and the item at that index.
const
nextIndex = (1 + maybePosn) % cycleItems.length,
nextFocusItem = cycleItems[nextIndex];
return 0 < cycleItems.length ? (
// Effect
editor.focusedItem = nextFocusItem,
// Value
nextFocusItem.bodyContentString
) : `No items with with path ${options.itemPath}`;
};
// ------------- JXA EVALUATION CONTEXT --------------
// main :: IO ()
const main = () => {
const
doc = Application("TaskPaper")
.documents.at(0);
return doc.exists() ? (
doc.evaluate({
script: `${tp3Context}`,
withOptions: {
itemPath
}
})
) : "No document open in TaskPaper.";
};
// MAIN ---
return main();
})();