Testing OmniGraffle 7.3's built-in JavaScript automation

@Sal

Thanks. I’ve learned enough JXA to think it shouldn’t be too hard to get along pretty well without using what I’ve been calling ‘whose’ filter and (I think) without addressing by name.

Interesting about url schemes. Won’t be limited to targeting just Omni apps? Or will it?

It’s great to hear more integrated automation is coming to iOS!

SG

Interesting about url schemes. Won’t be limited to targeting just Omni apps? Or will it?
It’s great to hear more integrated automation is coming to iOS!

When it comes to productivity automation, especially on iOS, the Omni Group is leading the way. So for now, the URL schemes are integrated in Omni applications on both macOS and iOS. Best of all – it’s fun! – SAL

First posted example of App-to-App scripting with OmniJS: http://omni-automation.com/shared/url-app-to-app.html

w00t!

2 Likes

Very nice example of the call and response pattern – thanks

A couple of questions about URLs and plugins:

  • In the URL approach, is there a known limit to the length of the url-encoded script ?
  • If there is a length constraint on the URL channel, can larger scripts return values from an application through the plugin architecture ?

Glad you found the example useful :-)

As far as the length of a URL, I’m not aware of an existing limit on the number of characters in a URL. The AppleScript URLs support built-into the Script Editor in macOS, accepts some very long encoded script URLs, like some of those on https://iworkautomation.com. However, if your script is that long, you may want to rethink the strategy and break it down into smaller pieces.

With regard to calling into plugins: external scripts can call functions within an installed PlugIn’s libraries (Documented on this page: http://omni-automation.com/libraries). Using plugin libraries can dramatically reduce the size and complexity of your scripts!

The OMNI-AUTOMATION site documents how to create libraries and how to call them from plugins and external scripts.

1 Like

Which is the best channel through which to submit feedback & questions while I test this ?

Through the support desk, or perhaps here ?

In the meanwhile, re Copy as JavaScript
I notice that it is not yet picking up text properties – (and I don’t, at the moment, see a Text class in the api) – does that seem likely to be on its way ?

( Copy as AppleScript, for example, returns a list of runs with properties like those below)

{size: 16, alignment: center, color: {0.000000, 0.000000, 0.000000}, font: "HelveticaNeue-Bold", text: "bolded"}

On iOS, we’ve sent some very large (multi-megabyte) attachments between apps using URLs, so as far as I can tell the only limit is based on memory constraints.

Here is great: this has the advantage of making sure that any answers to your questions are available to other people who might have similar questions. It’s also the place Sal and I are most likely to see it (unless you specifically ask a support human to bring something to our attention).

If you’re blocked on something and need a timely response from an Omni employee, please free to reference @SupportHumans to get someone’s attention. (Or email directly as usual, possibly referencing a thread.)

Sounds like a bug! That’s certainly intended to work. I just tested and it seems to be working fine for me in the current test build of OmniGraffle 7.3 (r282441):

var canvas = document.windows[0].selection.canvas
var g1 = canvas.newShape()
g1.text = "This is a test.\n\nThis text has multiple lines.\n\nSome of it is also bold."
g1.autosizing = TextAutosizing.Vertical
g1.textSize = 16
g1.geometry = new Rect(194.00, 96.00, 157.00, 140.00)
g1.shadowColor = null

It does drop the rich formatting of the text (my bold text was lost), but otherwise my text made it across fine. (I think there’s a bug already filed about preserving the formatting, but I’ll double-check.)

But perhaps it’s not working at all under some circumstances? It would be great to track down why it didn’t work for you when you tested! (Maybe share a small example file, or just the Copy As AppleScript output for the shape in question and we can see if running that AppleScript will create a shape that reproduces the problem?)

Hi Ken – forgive me, I was being unclear – the text value itself and the font size are both getting through fine.

What I wondered about was the bold/italic etc of particular text runs, which is coming through in Copy as AS but not yet in Copy as JS.

Oh, sorry, I understand now! Yes, that’s planned but not yet implemented in OmniGraffle 7: it will be based on the work we’ve done for the Text class in OmniOutliner 5’s JavaScript.

How much is this lack of rich text support getting in your way? It would be fairly trivial for us to add round-tripping through a somewhat opaque archive, like a .rtfString or .rtfdData property. Would that be helpful, or should we prioritize getting the full Text class ported to OmniGraffle, or is this not really a high priority for you right now and just a matter of curiosity?

(Building a new scripting platform is a big project, and we know we won’t have everything in place in the initial release. So it really helps to hear feedback from testers like you about which areas are good enough for now, vs. which areas are getting in your way and really need more work ASAP even if they’re stop gaps like a semi-opaque archiving property.)

Thanks, Ken - I think it can probably wait. I’m generating diagrams from text outlines, in which the text nodes (depending on the outline source - Markdown, TaskPaper, OmniOutliner etc) may have some substrings emphasised as bold, italic or strikethrough (really no more than that).

It’s good to be able to carry those emphases / highlights through, and I will certainly need to in due course, but it’s probably not worth disrupting the critical path for :-)

UPDATE

Ah … I should have used:

sa.openLocation(strURL);

(which executes the script immediately)



QUESTION WAS:

Do all URL launches inevitably need to pass through an ‘Allow’ dialog ?

I notice that if I run a URL from a shell command line, then it only works if we specify Safari as the opening application. e.g. (from JXA, creating and launching an OmniJS URL-encoded script), this works, after throwing up a Safari ‘Allow’ dialog:

sa.doShellScript("open -a Safari '" + strURL + "'");

whereas this more general open activates OmniGraffle but seems to have no scripting effect:

sa.doShellScript("open '" + strURL + "'");

(Full test JXA -> OmniJS test script below):

(() => {
    'use strict';

    // renderNode :: {line, fill, shape, shadow} -> {text, geom} -> JS String
    const renderNode = (dctStyle, dctNode, intID) => {
        const
            g = dctNode.geom,
            id = 'g' + intID.toString(),
            sh = dctStyle.shadowColor,
            cnr = dctStyle.cornerRadius;

        return 'var ' + id + intercalate(';\n' + id, [
            ' = canvas.newShape()',
            '.text = "' + dctNode.text + '"',
            '.cornerRadius = ' + (cnr !== undefined ? cnr.toString() : '0'),
            '.textSize = ' + dctStyle.textSize.toString(),
            '.shadowColor = ' + (isNull(sh) ? (
                'null'
            ) : 'Color.RGB(' + intercalate(', ', sh) + ')'),
            '.geometry = new ' + dctStyle.shape,
        ]) + '(' + [g.x, g.y, g.w, g.h].join(', ') + ');\n';
    };

    // renderNodes :: [Node] -> String
    const renderNodes = (dctStyle, lstNodes) =>
        zipWith(
            curry(renderNode)(dctStyle),
            lstNodes,
            enumFromTo(1, lstNodes.length)
        );

    // GENERIC FUNCTIONS ------------------------------------------------------

    // 2 or more arguments
    // curry :: Function -> Function
    const curry = (f, ...args) => {
        const go = xs => xs.length >= f.length ? (f.apply(null, xs)) :
            function () {
                return go(xs.concat([].slice.apply(arguments)));
            };
        return go([].slice.call(args, 1));
    };

    // enumFromTo :: Int -> Int -> [Int]
    const enumFromTo = (m, n) =>
        Array.from({
            length: Math.floor(n - m) + 1
        }, (_, i) => m + i);

    // intercalate :: String -> [a] -> String
    const intercalate = (s, xs) => xs.join(s);

    // isNull :: [a] -> Bool
    const isNull = xs => (xs instanceof Array) ? xs.length < 1 : undefined;

    // min :: Ord a => a -> a -> a
    const min = (a, b) => b < a ? b : a;

    // show :: a -> String
    const show = x => JSON.stringify(x); //, null, 2);

    // unlines :: [String] -> String
    const unlines = xs => xs.join('\n');

    // zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
    const zipWith = (f, xs, ys) =>
        Array.from({
            length: min(xs.length, ys.length)
        }, (_, i) => f(xs[i], ys[i]));

    // TEST -------------------------------------------------------------------


    const dctStyle = {
        textSize: 16,
        shadowColor: [0, 0, 1],
        shape: 'Rect',
        cornerRadius : 9
    };

    const lstNodes = [{
        text: "Taking",
        geom: {
            x: 100,
            y: 100,
            w: 100,
            h: 100
        }
    }, {
        text: "first",
        geom: {
            x: 250,
            y: 100,
            w: 100,
            h: 100
        }
    }, {
        text: "steps",
        geom: {
            x: 400,
            y: 100,
            w: 100,
            h: 100
        }
    }];

    const hdr = 'var canvas = document.windows[0].selection.canvas;\n';

    const strScript = hdr + unlines(renderNodes(dctStyle, lstNodes));

    const omniURLPrefix = 'omnigraffle:///omnijs-run?script=';

    const strURL = omniURLPrefix + encodeURIComponent(strScript);

    var a = Application.currentApplication(),
	sa = (a.includeStandardAdditions = true, a);

    // UPDATE:  I should have used:
   //  sa.openLocation(strURL)

    sa.doShellScript("open -a Safari '" + strURL + "'");

})();

With these new javascript automation capabilities, do you think it is possible to utilize the MathJax https://www.mathjax.org javascript library to grab images of math equations in OmniGraffle? Ultimately looking for automation on IOS. What about OO when it gets javascript?

Thanks.

Stephen

My understanding of MathJax is that it depends on evaluating JavaScript in a browser context*.

You might, however, be able to generate SVG from MathJax, in a browser, and then import that SVG output into OmniGraffle.



To expand a bit - JavaScript is an embedded scripting language, and the programmable ‘Objects’ or interfaces available to it depend on the particular application/context in which it is embedded. If you enter a line like:

Object.keys(this)

In the JS console of Safari or Chrome, you will see a long list of objects starting with Window and Document, which provide programming interfaces to HTML documents and their display. The HTML Document object, and the browser Window object etc are not part of JavaScript itself – they are interfaces to the browser.

If you enter the same line in the OmniGraffle JS console, the objects which you see are interfaces to the structure of OmniGraffle documents and their contents, rather than to the structure of HTML documents, (in terms of which MathJax is defined).

But Mathjax can generate SVG, a format which OG imports, so there could be a route to a useful workflow.

1 Like

iOS OmniGraffle: iPad external keyboard and Scripting console ?

Testing a script URL on iOS, I find that I can run it from iOS Safari without a problem.

(I take my hat off to you all !)

but I have not yet figured out how to launch the same URL from the iOS OG script console:

  • If I paste it to the console and hit an external keyboard RETURN key, this seems to simply add another linefeed, rather than submitting the URL for evaluation
  • I can’t immediately see a button or other GUI alternative to a return key

Any thoughts ? (or perhaps console launching is not quite ready yet ?)

For refce, in case it’s useful, the URL is:

omnigraffle:///omnijs-run?script=var%20canvas%20%3D%20document.windows%5B0%5D.selection.canvas%3B%0Avar%20g1%20%3D%20canvas.newShape()%3B%0Ag1.text%20%3D%20%22Taking%22%3B%0Ag1.cornerRadius%20%3D%209%3B%0Ag1.textSize%20%3D%2016%3B%0Ag1.shadowColor%20%3D%20Color.RGB(0%2C%200%2C%201)%3B%0Ag1.geometry%20%3D%20new%20Rect(100%2C%20100%2C%20100%2C%20100)%3B%0A%0Avar%20g2%20%3D%20canvas.newShape()%3B%0Ag2.text%20%3D%20%22first%22%3B%0Ag2.cornerRadius%20%3D%209%3B%0Ag2.textSize%20%3D%2016%3B%0Ag2.shadowColor%20%3D%20Color.RGB(0%2C%200%2C%201)%3B%0Ag2.geometry%20%3D%20new%20Rect(250%2C%20100%2C%20100%2C%20100)%3B%0A%0Avar%20g3%20%3D%20canvas.newShape()%3B%0Ag3.text%20%3D%20%22steps%22%3B%0Ag3.cornerRadius%20%3D%209%3B%0Ag3.textSize%20%3D%2016%3B%0Ag3.shadowColor%20%3D%20Color.RGB(0%2C%200%2C%201)%3B%0Ag3.geometry%20%3D%20new%20Rect(400%2C%20100%2C%20100%2C%20100)%3B%0A

Here, in contrast, is the result of pasting to Safari and hitting return there:

PS I’m not sure that the keyboard is the issue - I can happily evaluate a simple expression like this in the OG iOS console using an external keyboard

Scratch this - I’m being foolish. Entering a URL in a JS console is no way to launch a URL …

Time to make a coffee. Forgive me.

Thank you for the reply. This is hopeful. A little bit beyond my knowledge but I will try to pursue in omnioutliner.

I continue to find it disappointing that so few IOS apps support the scientific community. A good note taking app with mathjax support doesn’t exist. I long for the day where I can use omnioutliner with mathjax on iOS.

Off topic, but perhaps a markdown editor, or a plain text editor + Marked as a previewer ?
http://marked2app.com/help/MathJax.html

There are a bunch of options for OS X. iOS is where I am primarily. I have thousands of papers in PDF format I need to analyze. Read, annotate, and then ultimately take mathematical notes on. Rinse and repeat each year.

There are so many things I love about the iPad so I have been trying to make it my primary device. Taking notes in an outliner with math equations is my goal.

For now I have hacked together a local copy of mathjax in the editorial app and am using markdown that way. Unfortunately it is difficult to re-arrange the notes in markdown using their outline options. Omnioutliner makes it so much easier to take notes and reorganize later as you synthesize the material.

1 Like

A post was split to a new topic: OmniJS Copy as JavaScript mutates shape

A post was split to a new topic: OmniJS Create Objects with Properties?