1Writer turns out to be an excellent companion for omniJS scripting of iOS OmniGraffle 3.
It’s a good powerful text editor, and it also has a well-developed JavaScript interface, from which iOS OmniGraffle scripts can be constructed and launched.
Here is a rough draft of a 1Writer script which converts the active 1Writer (tab-indented) outline into a tree diagram in current OG3 test builds.
(NB OmniGraffle 3 is still in a test build stage - so a couple of glitches:
- After the document is generated, you have to manually click layout now in OmniGraffle 3 which doesn’t yet seem to respond to the Canvas.layout() method in scripts.
(Until you do this all this shapes are initially piled on top of each other in the top-left corner of the canvas). - For some reason, at the moment even the layout now button doesn’t work until you have manually selected another canvas, and then reselected the newly-created tree diagram canvas
 
I’m sure these glitches will be ironed out at some point …
Images, and 1Writer script code (which creates and runs an omniJS script) below:
    (() => {
        'use strict';
        // Ver .030
        // DRAFT SCRIPT FOR TESTING PURPOSES ONLY - NOT FOR USE WITH REAL DATA
        // GENERATE A NESTED DIAGRAM FROM A 1WRITER OUTLINE -----------------------
        // outlineDiagram :: Node {text:String, [Node]}
        function outlineDiagram(options) {
            'use strict';
            // CUSTOM STYLES ------------------------------------------------------
            const
                dctShapeStyle = {
                    //strokeColor: Color.RGB(0.0, 0.0, 0.0)
                },
                dctLineStyle = {
                    //strokeColor: Color.RGB(0.0, 0.0, 1.0)
                };
            // DEFAULT STYLES -----------------------------------------------------
            // shapeDefaults :: Dictionary
            const shapeDefaults = {
                textSize: 12,
                textHorizontalAlignment: HorizontalTextAlignment.Center,
                strokeType: null,
                strokeColor: null,
                shadowFuzziness: 7,
                cornerRadius: 9,
                magnets: [new Point(0, -1), new Point(0, 1)]
            };
            // lineDefaults :: Dictionary
            const lineDefaults = {
                lineType: LineType.Orthogonal,
                shadowColor: null,
                strokeThickness: 2,
                strokeColor: Color.RGB(1.0, 0.0, 0.0)
            };
            // TREE DRAWING -------------------------------------------------------
            // newGraphic ::
            // Canvas -> String -> [Dictionary] -> Maybe [Float] -> Graphic
            const newGraphic = (cnv, strType, lstPropDicts, lstPosnSize) => {
                const g = cnv['new' + strType]();
                if (strType !== 'Line') {
                    const xywh = lstPosnSize.concat(
                        [0, 0, 100, 100].slice(lstPosnSize.length)
                    );
                    g.geometry = new Rect(xywh[0], xywh[1], xywh[2], xywh[3]);
                }
                return stylesApplied(lstPropDicts, g);
            };
            // stylesApplied :: [Dictionary] -> Graphic -> Graphic
            const stylesApplied = (lstDicts, g) =>
                concatMap(
                    d => map(k => [k, d[k]], Object.keys(d)),
                    lstDicts
                )
                .forEach(kv => g[kv[0]] = kv[1]) || g;
            // treeDiagram ::
            // Canvas -> Dictionary -> Dictionary -> Node -> Maybe Shape -> Node
            const treeDiagram = (cnv, dctShpStyle, dctLnStyle, dctNode, parent) => {
                const
                    nest = dctNode.nest,
                    shp = newGraphic(
                        cnv,
                        'Shape', [{
                            text: dctNode.text || ''
                        }, dctShpStyle], []
                    );
                //dctNode.id = shp.id;
                shp.name = dctNode.id;
                //shp.magnets = [new Point(0, -1), new Point(0 ,1)];
                // and any link connected and styled
                if (parent !== undefined) {
                    const lnk = stylesApplied(
                        [dctLnStyle],
                        cnv.connect(parent, shp)
                    );
                    // link positioned behind parent Shape
                    typeof lnk.orderBelow(parent);
                    //dctNode.linkID = lnk.id;
                }
                // Recurse with any children
                if ((nest !== undefined) && (!isNull(nest))) {
                    dctNode.nest = map(x => treeDiagram(
                        cnv, dctShpStyle, dctLnStyle, x, shp
                    ), nest);
                }
                return dctNode;
            };
            // GENERIC FUNCTIONS --------------------------------------------------
            // concatMap :: (a -> [b]) -> [a] -> [b]
            const concatMap = (f, xs) => [].concat.apply([], xs.map(f));
            // isNull :: [a] -> Bool
            const isNull = xs => (xs instanceof Array) ? xs.length < 1 : undefined;
            // log :: a -> IO ()
            const log = x => console.log(show(x));
            // map :: (a -> b) -> [a] -> [b]
            const map = (f, xs) => xs.map(f);
            // Any value -> optional number of indents -> String
            // show :: a -> String
            // show :: a -> Int -> String
            const show = (...x) =>
                JSON.stringify.apply(
                    null, x.length > 1 ? [x[0], null, x[1]] : x
                );
            // OUTLINE DIAGRAM RETURN ---------------------------------------------
            const
                jsoTree = options.tree,
                cnv = addCanvas(),
                layer = cnv.layers[0],
                blnForest = Array.isArray(jsoTree),
                lngForest = blnForest ? jsoTree.length : 0,
                combined = Object.assign;
            // Canvas settings
            cnv.orderBefore(document.portfolio.canvases[0]);
            cnv.layoutInfo.automaticLayout = false;
            ['Right', 'Down'].forEach(x => cnv['autosizes' + x] = true);
            // Hide layer temporarily  -- doesn't work in this build
            // layer.visible = false;
            // log(layer.locked);
            // Set active view to this canvas - perhaps defer this ?
            document.windows[0].selection.view.canvas = cnv; // ????
            console.log('before dctDiagram') // Not showing up in console ...
            const dctDiagram = treeDiagram(
                    cnv,
                    combined({}, shapeDefaults, dctShapeStyle),
                    combined({}, lineDefaults, dctLineStyle),
                    blnForest ? (
                        (lngForest === 1) ? jsoTree[0] : {
                            id: 'Root',
                            text: '',
                            nest: jsoTree
                        }
                    ) : jsoTree
                ),
                shpRoot = cnv.graphicWithName(dctDiagram.id);
            console.log('after dctDiagram'); // Not showing up in console ...
            //shpRoot.name = "Root";
            //dctDiagram.id = "Root";
            //shpRoot.notes = show(dctDiagram);
            //console.log(shpRoot.notes);
            // This doesn't yet trigger an automatic layout on iOS (works on macOS)
            //cnv.layoutInfo.automaticLayout = true;
            // So let's try this ...
            cnv.layout(); // also not currently triggering a layout event
            // This doesn't yet move the selection to the new canvas ... (works on macOS)
            //document.windows[0].selection.view.canvas = cnv;
            // So let's try this ...
            //cnv.orderBefore(document.portfolio.canvases[0])
            // Restore visibility of layer
            // layer.visible = true;
            log(layer.locked);
        };
        // GENERIC ----------------------------------------------------------------
        // A list of functions applied to a list of arguments
        // <*> :: [(a -> b)] -> [a] -> [b]
        const ap = (fs, xs) => //
            [].concat.apply([], fs.map(f => //
                [].concat.apply([], xs.map(x => [f(x)]))));
        // comparing :: (a -> b) -> (a -> a -> Ordering)
        const comparing = f =>
            (x, y) => {
                const
                    a = f(x),
                    b = f(y);
                return a < b ? -1 : (a > b ? 1 : 0);
            };
        // concat :: [[a]] -> [a] | [String] -> String
        const concat = xs =>
            xs.length > 0 ? (() => {
                const unit = typeof xs[0] === 'string' ? '' : [];
                return unit.concat.apply(unit, xs);
            })() : [];
        // concatMap :: (a -> [b]) -> [a] -> [b]
        const concatMap = (f, xs) => [].concat.apply([], xs.map(f));
        // curry :: Function -> Function
        const curry = (f, ...args) => {
            const go = xs => xs.length >= f.length ? (f.apply(null, xs)) :
                function () {
                    return go(xs.concat(Array.from(arguments)));
                };
            return go([].slice.call(args, 1));
        };
        // dropWhile :: (a -> Bool) -> [a] -> [a]
        const dropWhile = (p, xs) => {
            let i = 0;
            for (let lng = xs.length;
                (i < lng) && p(xs[i]); i++) {}
            return xs.slice(i);
        };
        // elem :: Eq a => a -> [a] -> Bool
        const elem = (x, xs) => xs.indexOf(x) !== -1;
        // flip :: (a -> b -> c) -> b -> a -> c
        const flip = f => (a, b) => f.apply(null, [b, a]);
        // intercalate :: String -> [a] -> String
        const intercalate = (s, xs) => xs.join(s);
        // length :: [a] -> Int
        const length = xs => xs.length;
        // lines :: String -> [String]
        const lines = s => s.split(/[\r\n]/);
        // log :: a -> IO ()
        const log = (...args) =>
            console.log(
                args
                .map(JSON.stringify)
                .join(' -> ')
            );
        // map :: (a -> b) -> [a] -> [b]
        const map = (f, xs) => xs.map(f);
        // minimumByMay :: (a -> a -> Ordering) -> [a] -> Maybe a
        const minimumByMay = (f, xs) =>
            xs.reduce((a, x) => a.nothing ? {
                just: x,
                nothing: false
            } : f(x, a.just) < 0 ? {
                just: x,
                nothing: false
            } : a, {
                nothing: true
            });
        // isNull :: [a] | String -> Bool
        const isNull = xs =>
            Array.isArray(xs) || typeof xs === 'string' ? (
                xs.length < 1
            ) : undefined;
        // show :: a -> String
        // show :: a -> Int -> String
        const show = (...x) =>
            JSON.stringify.apply(
                null, x.length > 1 ? [x[0], null, x[1]] : x
            );
        // splitBefore :: (a -> Bool) -> [a] -> [[a]]
        const splitBefore = (p, xs) => {
            const dct = xs.reduce((a, x) =>
                p(x) ? {
                    splits: a.splits.concat([a.active]),
                    active: [x]
                } : {
                    splits: a.splits,
                    active: a.active.concat(x)
                }, {
                    splits: [],
                    active: []
                });
            return dct.splits.concat([dct.active]);
        };
        // splitOn :: String -> String -> [String]
        const splitOn = (cs, xs) => xs.split(cs);
        // stringChars :: String -> [Char]
        const stringChars = s => s.split('');
        // strip :: Text -> Text
        const strip = s => s.trim();
        // takeWhile :: (a -> Bool) -> [a] -> [a]
        const takeWhile = (f, xs) => {
            for (var i = 0, lng = xs.length;
                (i < lng) && f(xs[i]); i++) {}
            return xs.slice(0, i);
        };
        // unconsMay :: [a] -> Maybe (a, [a])
        const unconsMay = xs => xs.length > 0 ? {
            just: [xs[0], xs.slice(1)],
            nothing: false
        } : {
            nothing: true
        };
        // NESTED TEXT -----------------------------------------------------------
        // nestedLines :: Int -> [(Int, String)] -> Node {text:String, nest:[Node]}
        const nestedLines = (startLevel, xs) =>
            concatMap(
                lines => {
                    const mbht = unconsMay(lines);
                    return mbht.nothing ? [] : (() => {
                        const [h, t] = mbht.just;
                        return ([h.level === startLevel ? { // Normal node
                            text: h.text,
                            nest: isNull(t) ? [] : nestedLines(startLevel + 1, t)
                        } : { // Virtual root
                            text: '',
                            nest: nestedLines(startLevel, lines)
                        }]);
                    })();
                },
                splitBefore(x => x.level === startLevel, xs)
            );
        // lineIndents :: String -> [(Int, String)]
        const lineIndents = s =>
            length(strip(s)) > 0 ? (() => {
                const
                    lstLines = lines(s),
                    lineIndent = strLine => {
                        const xs = takeWhile(
                            curry(flip(elem))([' ', '\t']),
                            stringChars(strLine)
                        );
                        return length(xs) > 0 ? [xs] : [];
                    },
                    minDents = minimumByMay(
                        comparing(length),
                        concatMap(lineIndent, lstLines)
                    ),
                    strIndent = minDents.nothing ? '\t' : concat(minDents.just);
                return map(x => {
                        const
                            xs = curry(splitOn)(strIndent, x),
                            rs = dropWhile(x => x === '', xs);
                        return {
                            level: length(xs) - length(rs),
                            text: intercalate(strIndent, rs)
                        };
                    },
                    lstLines
                );
            })() : [{
                level: 0,
                text: ''
            }];
        const oneWriterTextNest = () =>
            nestedLines(0, lineIndents(editor.getText()));
        // 1WRITER -> OMNIJS ----------------------------------------------------------
        // evaluateOmniJS :: (Options -> ()) -> { KeyValues ..}
        const evaluateOmniJS = (f, dctOptions) => {
            //ap([app.setClipboard, app.openURL], //
            //[
            return 'omnigraffle:///omnijs-run?script=' +
                encodeURIComponent(
                    '(' + f.toString() + ')(' +
                    (dctOptions && Object.keys(dctOptions)
                        .length > 0 ? show(dctOptions) : '') + ')'
                )
            //]
            //);
        };
        const strURL = evaluateOmniJS(outlineDiagram, {
            //  [Node {text:String, nest:[Node]}]
            tree: oneWriterTextNest()
        });
        app.setClipboard(strURL);
        app.openURL(strURL);
        //ui.alert(strURL);
        //return ui.alert(
        //    show(
        //        oneWriterTextNest()
        //    )
        //);
    })();
            

