The links between OmniGraffle shapes are represented in a left hand panel as an outline structure.
Here is a macro which copies:
- All shape text in the open OG7 canvas,
- in TaskPaper 3 outline format.
The mapping from OG to TP3 is:
- Shape texts -> TP3 items
- Shape notes -> TP3 notes
- Shape userData key value pairs -> TP3 key@(value) tags
- Shape name -> TP3 @name tag
Copy OmniGraffle 7 diagram outline as Taskpaper 3 text outline.kmmacros (25.6 KB)
JS Source text (omniJS + JXA)
(() => {
'use strict';
// Rob Trew (c) 2018
// Ver 0.2
// - Shape name now added as TaskPaper tag
// - Spaces in tag names replaced with underscores
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY
// OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
const jxaMain = () => {
// main :: IO ()
const main = () => {
const
og = Application('OmniGraffle'),
lrTP3 = bindLR(
og.documents.length > 0 ? Right(
taskPaperFromTrees(
JSON.parse(
og.evaluateJavascript(
'(' + ogJSContext + ')()'
)
).nest
)
) : Left('No documents open in OmniGraffle'),
strTP3 => (
standardAdditions()
.setTheClipboardTo(strTP3),
Right(strTP3)
)
);
// Either a message or the copied TaskPaper string.
return lrTP3.Left || lrTP3.Right;
};
// GENERICS ---------------------------------------
// Left :: a -> Either a b
const Left = x => ({
type: 'Either',
Left: x
});
// Right :: b -> Either a b
const Right = x => ({
type: 'Either',
Right: x
});
// bindLR (>>=) :: Either a -> (a -> Either b) -> Either b
const bindLR = (m, mf) =>
m.Right !== undefined ? (
mf(m.Right)
) : m;
// foldl :: (a -> b -> a) -> a -> [b] -> a
const foldl = (f, a, xs) => xs.reduce(f, a);
// foldlTree :: (b -> a -> b) -> b -> Tree a -> b
const foldlTree = (f, acc, node) => {
const go = (a, x) => x.nest.reduce(go, f(a, x));
return go(acc, node);
};
// lines :: String -> [String]
const lines = s => s.split(/[\r\n]/);
// map :: (a -> b) -> [a] -> [b]
const map = (f, xs) => xs.map(f);
// showJSON :: a -> String
const showJSON = x => JSON.stringify(x, null, 2);
// showLog :: a -> IO ()
const showLog = (...args) =>
console.log(
args
.map(JSON.stringify)
.join(' -> ')
);
// unlines :: [String] -> String
const unlines = xs => xs.join('\n');
// TASKPAPER FROM TREES ---------------------------
// taskPaperFromTrees :: [Tree] -> String
const taskPaperFromTrees = xs => {
const go = (strIndent, ts) =>
foldl((a, x) => {
const
rgxSpace = /\s+/g,
nest = x.nest,
root = x.root,
txt = root.text,
name = root.name,
tags = root.userData,
ks = Boolean(tags) ? (
Object.keys(tags)
) : [],
notes = root.notes,
blnName = Boolean(name),
blnNotes = Boolean(notes),
blnTags = ks.length > 0,
blnNest = nest.length > 0,
blnHasData = Boolean(txt) ||
(ks.length > 0) || blnNotes ||
blnNest,
strNext = '\t' + strIndent;
return blnHasData ? (
a + strIndent + '- ' + (root.text || '') +
(blnName ? (
' @' + name.replace(rgxSpace, '_')
) : '') +
(blnTags ? (
foldl(
(t, k) => {
const v = tags[k];
return t +
' @' + k.replace(rgxSpace, '_') +
(Boolean(v) ? (
'(' + v + ')'
) : '');
},
'',
ks
)
) : '') + '\n' +
(blnNotes ? (
unlines(
map(s => strNext + s, lines(notes))
) + '\n'
) : '') + (blnNest ? (
go(strNext, x.nest)
) : '')
) : a;
}, '', ts);
return go('', xs);
};
// MAIN -------------------------------------------
return main();
};
const ogJSContext = () => {
// ogMain :: OG () -> JSON String
const ogMain = () =>
showJSON(
pureTreeOG(canvases[0].outlineRoot)
);
// Node :: a -> [Tree a] -> Tree a
const Node = (v, xs) => ({
type: 'Node',
root: v,
nest: xs || []
});
// pureTreeOG :: OGNode -> Tree OGNode
const pureTreeOG = node => {
const go = x => {
const
g = x.graphic || {},
xs = x.children;
return Node(
['text', 'name', 'notes', 'userData']
.reduce((a, k) => {
const v = g[k];
return Boolean(v) ? (
Object.assign(a, {
[k]: v
})
) : a;
}, {}),
xs.length > 0 ? xs.map(go) : []
);
};
return go(node);
};
// showJSON :: a -> String
const showJSON = x =>
JSON.stringify(x, null, 2);
// MAIN -------------------------------------------
return ogMain();
};
// JXA ------------------------------------------------
// standardAdditions :: () -> Application
const standardAdditions = () =>
Object.assign(Application.currentApplication(), {
includeStandardAdditions: true
});
return jxaMain();
})();