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:

- capture the grouped outline in a generic tree structure,
- 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;
};
```

( https://github.com/RobTrew/prelude-jxa )

Or perhaps, in terms of `Item.descendants`

, just a new document constructed from something like:

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