Move item to Project/Heading in OmniOutliner

If you can send a small anonymised test file to me here by direct message, I’ll take a look.

I haven’t looked closely at what the raw API offers, but if there’s nothing direct, then we may just need to:

  1. capture the grouped outline in a generic tree structure,
  2. capture level 2 and discard level 1

for the generic tree structure:

// Node :: a -> [Tree a] -> Tree a
const Node = v =>
    // Constructor for a Tree node which connects a
    // value of some kind to a list of zero or
    // more child trees.
    xs => ({
        type: 'Node',
        root: v,
        nest: xs || []
    });

// pureTreeOO :: OOItem  -> Tree OOItem
const pureTreeOO = item => {
    const go = x =>
        Node(x)(x.hasChildren ? x.children.map(go) : []);
    return go(item);
};

and to split the tree horizontally into levels:

// levels :: Tree a -> [[a]]
const levels = tree =>
    // A list of lists, grouping the 
    // root values of each level 
    // of the tree.
    cons([tree.root])(
        tree.nest
        .map(levels)
        .reduce(
            uncurry(zipWithLong(append)),
            []
        )
    );


// append (<>) :: [a] -> [a] -> [a]
const append = xs =>
    // Two lists joined into one.
    ys => xs.concat(ys);


// uncurry :: (a -> b -> c) -> ((a, b) -> c)
const uncurry = f =>
    // A function over a pair, derived
    // from a curried function.
    function() {
        const
            args = arguments,
            xy = Boolean(args.length % 2) ? (
                args[0]
            ) : args;
        return f(xy[0])(xy[1]);
    };


// zipWithLong :: (a -> a -> a) -> [a] -> [a] -> [a]
const zipWithLong = f => {
    // A list with the length of the *longer* of 
    // xs and ys, defined by zipping with a
    // custom function, rather than with the
    // default tuple constructor.
    // Any unpaired values, where list lengths differ,
    // are simply appended.
    const go = xs =>
        ys => 0 < xs.length ? (
            0 < ys.length ? (
                [f(xs[0])(ys[0])].concat(
                    go(xs.slice(1))(ys.slice(1))
                )
            ) : xs
        ) : ys
    return go;
};

( GitHub - RobTrew/prelude-jxa: Generic functions for macOS and iOS scripting in Javascript – function names as in Hoogle )

Or perhaps, in terms of Item.descendants, just a new document constructed from something like:

rootItem.descendants.filter(x => 2 === x.level)