Well, I am not sure this would be useful for you :-)
FWIW, here is a OmniJS (iOS/Mac) script that opens the share sheet with a text representation of selected tasks preserving only task name and notes.
/*{
"type": "action"
}*/
// Twitter: @unlocked2412
(() => Object.assign(
new PlugIn.Action(selection => {
// OMNI JS CODE ---------------------------------------
const omniJSContext = () => {
// main :: IO ()
const main = () => {
const
ts = selection.tasks,
strText = compose(
unlines,
map(taskPaperFromTree),
map(
fmapPureOF(x => ({
text: x.name,
note: x.note
}))
)
)(ts)
return (
new SharePanel([strText]).show(),
strText
)
};
// FUNCTIONS --
// JS Prelude --------------------------------------------------
// Just :: a -> Maybe a
const Just = x => ({
type: 'Maybe',
Nothing: false,
Just: x
});
// Left :: a -> Either a b
const Left = x => ({
type: 'Either',
Left: x
});
// 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 || []
});
// Nothing :: Maybe a
const Nothing = () => ({
type: 'Maybe',
Nothing: true,
});
// Right :: b -> Either a b
const Right = x => ({
type: 'Either',
Right: x
});
// Tuple (,) :: a -> b -> (a, b)
const Tuple = a =>
b => ({
type: 'Tuple',
'0': a,
'1': b,
length: 2
});
// append (++) :: [a] -> [a] -> [a]
const append = xs =>
// A list obtained by the
// concatenation of two others.
ys => xs.concat(ys);
// bind (>>=) :: Monad m => m a -> (a -> m b) -> m b
const bind = m =>
mf => (Array.isArray(m) ? (
bindList
) : (() => {
const t = m.type;
return 'Either' === t ? (
bindLR
) : 'Maybe' === t ? (
bindMay
) : 'Tuple' === t ? (
bindTuple
) : ('function' === typeof m) ? (
bindFn
) : undefined;
})()(m)(mf));
// bindFn (>>=) :: (a -> b) -> (b -> a -> c) -> a -> c
const bindFn = f =>
// Binary operator applied over f x and x.
bop => x => bop(f(x))(x);
// bindLR (>>=) :: Either a ->
// (a -> Either b) -> Either b
const bindLR = m =>
mf => undefined !== m.Left ? (
m
) : mf(m.Right);
// bindList (>>=) :: [a] -> (a -> [b]) -> [b]
const bindList = xs =>
mf => [...xs].flatMap(mf);
// bindMay (>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
const bindMay = mb =>
// Nothing if mb is Nothing, or the application of the
// (a -> Maybe b) function mf to the contents of mb.
mf => mb.Nothing ? (
mb
) : mf(mb.Just);
// bindTuple (>>=) :: Monoid a => (a, a) -> (a -> (a, b)) -> (a, b)
const bindTuple = tpl =>
f => {
const t2 = f(tpl[1]);
return Tuple(mappend(tpl[0])(t2[0]))(
t2[1]
);
};
// compose (<<<) :: (b -> c) -> (a -> b) -> a -> c
const compose = (...fs) =>
// A function defined by the right-to-left
// composition of all the functions in fs.
fs.reduce(
(f, g) => x => f(g(x)),
x => x
);
// concat :: [[a]] -> [a]
// concat :: [String] -> String
const concat = xs => (
ys => 0 < ys.length ? (
ys.every(Array.isArray) ? (
[]
) : ''
).concat(...ys) : ys
)(list(xs));
// cons :: a -> [a] -> [a]
const cons = x =>
// A list constructed from the item x,
// followed by the existing list xs.
xs => Array.isArray(xs) ? (
[x].concat(xs)
) : 'GeneratorFunction' !== xs
.constructor.constructor.name ? (
x + xs
) : ( // cons(x)(Generator)
function* () {
yield x;
let nxt = xs.next();
while (!nxt.done) {
yield nxt.value;
nxt = xs.next();
}
}
)();
// dropAround :: (a -> Bool) -> [a] -> [a]
// dropAround :: (Char -> Bool) -> String -> String
const dropAround = p =>
xs => dropWhile(p)(
dropWhileEnd(p)(xs)
);
// dropWhile :: (a -> Bool) -> [a] -> [a]
// dropWhile :: (Char -> Bool) -> String -> String
const dropWhile = p =>
xs => {
const n = xs.length;
return xs.slice(
0 < n ? until(
i => n === i || !p(xs[i])
)(i => 1 + i)(0) : 0
);
};
// dropWhileEnd :: (a -> Bool) -> [a] -> [a]
// dropWhileEnd :: (Char -> Bool) -> String -> String
const dropWhileEnd = p =>
xs => {
let i = xs.length;
while (i-- && p(xs[i])) {}
return xs.slice(0, i + 1);
};
// enumFromTo :: Int -> Int -> [Int]
const enumFromTo = m =>
n => !isNaN(m) ? (
Array.from({
length: 1 + n - m
}, (_, i) => m + i)
) : enumFromTo_(m)(n);
// enumFromTo_ :: Enum a => a -> a -> [a]
const enumFromTo_ = m => n => {
const [x, y] = [m, n].map(fromEnum),
b = x + (isNaN(m) ? 0 : m - x);
return Array.from({
length: 1 + (y - x)
}, (_, i) => toEnum(m)(b + i));
};
// foldl :: (a -> b -> a) -> a -> [b] -> a
const foldl = f =>
a => xs => [...xs].reduce(
(x, y) => f(x)(y),
a
);
// fromEnum :: Enum a => a -> Int
const fromEnum = x =>
typeof x !== 'string' ? (
x.constructor === Object ? (
x.value
) : parseInt(Number(x))
) : x.codePointAt(0);
// identity :: a -> a
const identity = x =>
// The identity function. (`id`, in Haskell)
x;
// isDigit :: Char -> Bool
const isDigit = c => {
const n = c.codePointAt(0);
return 48 <= n && 57 >= n;
};
// join :: Monad m => m (m a) -> m a
const join = x =>
bind(x)(identity);
// keys :: Dict -> [String]
const keys = Object.keys;
// length :: [a] -> Int
const length = xs =>
// Returns Infinity over objects without finite
// length. This enables zip and zipWith to choose
// the shorter argument when one is non-finite,
// like cycle, repeat etc
'GeneratorFunction' !== xs.constructor.constructor.name ? (
xs.length
) : Infinity;
// lines :: String -> [String]
const lines = s =>
// A list of strings derived from a single
// newline-delimited string.
0 < s.length ? (
s.split(/[\r\n]/)
) : [];
// list :: StringOrArrayLike b => b -> [a]
const list = xs =>
// xs itself, if it is an Array,
// or an Array derived from xs.
Array.isArray(xs) ? (
xs
) : Array.from(xs || []);
// map :: (a -> b) -> [a] -> [b]
const map = f =>
// The list obtained by applying f
// to each element of xs.
// (The image of xs under f).
xs => [...xs].map(f);
// mappend (<>) :: Monoid a => a -> a -> a
const mappend = a =>
// Associative operation
// defined for various monoid types.
b => (t => (Boolean(t) ? (
'Maybe' === t ? (
mappendMaybe
) : mappendTuple
) : 'function' !== typeof a ? (
append
) : a.toString() !== 'x => y => f(y)(x)' ? (
mappendFn
) : mappendOrd)(a)(b))(a.type);
// mappendFn :: Monoid b => (a -> b) -> (a -> b) -> (a -> b)
const mappendFn = f =>
g => x => mappend(f(x))(
g(x)
);
// mappendMaybe (<>) :: Maybe a -> Maybe a -> Maybe a
const mappendMaybe = a =>
b => a.Nothing ? (
b
) : b.Nothing ? (
a
) : Just(
mappend(a.Just)(
b.Just
)
);
// mappendOrd (<>) :: Ordering -> Ordering -> Ordering
const mappendOrd = cmp =>
cmp1 => a => b => {
const x = cmp(a)(b);
return 0 !== x ? (
x
) : cmp1(a)(b);
};
// mappendTuple (<>) :: (a, b) -> (a, b) -> (a, b)
const mappendTuple = t => t2 =>
Tuple(
mappend(t[0])(
t1[0]
)
)(mappend(t[1])(
t1[1]
));
// min :: Ord a => a -> a -> a
const min = a => b => b < a ? b : a;
// negate :: Num -> Num
const negate = n =>
-n;
// nest :: Tree a -> [a]
const nest = tree => {
// Allowing for lazy (on-demand) evaluation.
// If the nest turns out to be a function –
// rather than a list – that function is applied
// here to the root, and returns a list.
const xs = tree.nest;
return 'function' !== typeof xs ? (
xs
) : xs(root(x));
};
// or :: [Bool] -> Bool
const or = xs =>
xs.some(Boolean);
// read :: Read a => String -> a
const read = JSON.parse;
// replace :: String -> String -> String -> String
// replace :: Regex -> String -> String -> String
const replace = needle => strNew => strHaystack =>
strHaystack.replace(
'string' !== typeof needle ? (
needle
) : new RegExp(needle, 'g'),
strNew
);
// root :: Tree a -> a
const root = tree => tree.root;
// show :: a -> String
// show :: a -> Int -> Indented String
const show = x => {
const
e = ('function' !== typeof x) ? (
x
) : {
type: 'Function',
f: x
};
return JSON.stringify(e, (_, v) => {
const
f = ((null !== v) && (undefined !== v)) ? (() => {
const t = v.type;
return 'Either' === t ? (
showLR
) : 'Function' === t ? (
dct => 'λ' + dct.f.toString()
) : 'Maybe' === t ? (
showMaybe
) : 'Ordering' === t ? (
showOrdering
) : 'Ratio' === t ? (
showRatio
) : 'string' === typeof t && t.startsWith('Tuple') ? (
showTuple
) : Array.isArray(v) ? (
showList
) : undefined;
})() : showUndefined;
return Boolean(f) ? (
f(v)
) : 'string' !== typeof v ? (
v
) : v;
})
};
// showLR :: Either a b -> String
const showLR = lr => {
const k = undefined !== lr.Left ? (
'Left'
) : 'Right';
return k + '(' + unQuoted(show(lr[k])) + ')';
};
// showList :: [a] -> String
const showList = xs =>
'[' + xs.map(show)
.join(', ')
.replace(/[\"]/g, '') + ']';
// showMaybe :: Maybe a -> String
const showMaybe = mb =>
mb.Nothing ? (
'Nothing'
) : 'Just(' + unQuoted(show(mb.Just)) + ')';
// showOrdering :: Ordering -> String
const showOrdering = e =>
0 < e.value ? (
'GT'
) : 0 > e.value ? (
'LT'
) : 'EQ';
// showRatio :: Ratio -> String
const showRatio = r =>
'Ratio' !== r.type ? (
r.toString()
) : r.n.toString() + (
1 !== r.d ? (
'/' + r.d.toString()
) : ''
);
// showTuple :: Tuple -> String
const showTuple = tpl =>
'(' + enumFromTo(0)(tpl.length - 1)
.map(x => unQuoted(show(tpl[x])))
.join(',') + ')';
// showUndefined :: () -> String
const showUndefined = () => '(⊥)';
// toEnum :: a -> Int -> a
const toEnum = e =>
// The first argument is a sample of the type
// allowing the function to make the right mapping
x => ({
'number': Number,
'string': String.fromCodePoint,
'boolean': Boolean,
'object': v => e.min + v
} [typeof e])(x);
// unQuoted :: String -> String
const unQuoted = s =>
dropAround(x => 34 === x.codePointAt(0))(
s
);
// unlines :: [String] -> String
const unlines = xs =>
// A single string formed by the intercalation
// of a list of strings with the newline character.
xs.join('\n');
// until :: (a -> Bool) -> (a -> a) -> a -> a
const until = p => f => x => {
let v = x;
while (!p(v)) v = f(v);
return v;
};
// JS Trees ----------------------------------------------------
// taskPaperFromTree :: Tree -> String
const taskPaperFromTree = x => {
const
rgxSpace = /\s+/g,
go = strIndent => x => {
const
nest = x.nest,
root = x.root,
txt = root.text || '',
tags = root.tags,
ks = Boolean(tags) ? (
Object.keys(tags)
) : [],
note = root.note,
blnNotes = Boolean(note),
blnTags = ks.length > 0,
blnNest = nest.length > 0,
strNext = '\t' + strIndent;
return or([Boolean(txt), blnTags, blnNotes, blnNest]) ? (
strIndent + (root.type !== 'project' ? (
root.type !== 'note' ? (
'- ' + txt
) : txt
) : txt + ':') +
(blnTags ? foldl(
t => k => {
const v = tags[k];
return t + (Boolean(v) ? (
' @' + k.replace(rgxSpace, '_') +
(Boolean(v) && v !== true ? (
'(' + v + ')'
) : '')
) : '');
})('')(
ks
) : '') +
(blnNotes ? (
'\n' + unlines(map(
s => strNext + s
)(lines(note)))
) : '') + (blnNest ? (
'\n' + unlines(map(go(strNext))(nest))
) : '')
) : '';
};
return go('')(x);
};
// OmniFocus OmniJS --------------------------------------------
// fmapPureOF :: (OF Item -> a) -> OF Item -> Tree a
const fmapPureOF = f => item => {
const go = x => Node(f(x))(
x.children.map(go)
);
return go(item);
};
return main();
};
return omniJSContext()
}), {
validate: selection => selection.tasks.length > 0
}
))();