Is there any way of moving tasks/lines in OmniOutliner to a specific heading like this in “TaskPaper” (see image)
Where project = Heading
This would help a lot not having to drill down in the outline and find the heading where you want to move the new task you just created at the top of the document.
TaskPaper is probably a faster and more flexible instrument for reorganising outlines, but here, anyway, is a draft plug for OO.
It lets you move any selected rows (with their descendants) to the chosen heading row.
By default, the menu of destinations includes all rows at the top 3 levels of indentation.
You can adjust this, in the JS source of the plugin, by editing the line:
Thank you for you´re quick and detailed response :)
I tried this plugin, but it looks like this way will quickly grow out of hand as the outline grows (see image) so I think I will stick to TaskPaper for now.
( You could experiment with setting numberOfLevelsToShow to 2 (or even 1), for a sparser target listing, but that won’t, of course be helpful in all workflows )
(It depends a bit on how your project nodes are defined or identified)
Two possible approaches come to mind, both using the .identifier properties of the chosen target header and selected rows:
Exclude all selected rows (and their descendants) from the target header listing
or include them, to allow for a full map of the document at the given level, but warn and desist if an inadvertent attempt is made to insert a row under itself.
I’ve updated the plugin above to version 3, using the approach of showing all headings at the specified levels, but not allowing a recursive move, and showing an explanatory message if one is accidentally attempted.
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;
};
That’s gives me a great start with some ideas. Thanks! For a test document I’ve just been using the one for ACME Customers downloadable in the Organize Items section at https://omni-automation.com/omnioutliner/outline.html. It has the kind of multicolumn structure I use for this kind of simple reorganization (similar to the Categories feature in Numbers, though that one can easily go down several levels). Hope to keep the same document rather than generate a new one.
I was originally thinking Outline.ungroup must be the answer. But I don’t have a coding background, just a rudimentary knowledge of JavaScript and a little more experience with AppleScript, and am slow at visualizing how to read a listing in the API (I think Outline.ungroup has neither documentation nor any examples I could find on omni-automation.com site to study) and then actually get the thing to work in a script.
I quite understand. The existing documentation does assume more concepts than it supplies, and the site is rather wordy and dense – a bit over-rich in forests of detail, and rather hazier and weaker on clear overview and fundamental concepts.
I never like to suggest code which includes deletion, but here is a basic sketch of one approach to:
ungrouping,
and then selecting the now parentless group labels.
(in place of selecting them, you could of course, with dummy data, experiment with deleting them, but do take care and make backups if you take that road …)
JS Source
/*{
"author": "Author Name",
"targets": ["omnioutliner"],
"type": "action",
"identifier": "com.mycompany.ungroup",
"version": "0.1",
"description": "A plug-in that...",
"label": "unGroup",
"mediumLabel": "unGroup",
"paletteLabel": "unGroup",
}*/
(() => Object.assign(
new PlugIn.Action(selection => {
const main = () => {
const
editor = selection.editor,
topLevelRows = rootItem.children;
// If all top level items have children
// capture the top level identifiers
// ungroup
// and select the (now) childless orginal top level items
// possibly for deletion.
return topLevelRows.every(
x => x.hasChildren
) ? (
selection.outline.ungroup(topLevelRows),
topLevelRows.map(
x => (
// In Editor, updated.
// Could be deleted ...
editor.nodeForObject(x).isSelected = true,
// In Console, appended to log.
x.topic
)
).join(', ')
) : 'May not be grouped by column.';
};
return console.log(main());
}), {
validate: selection => true
}
))();
Thanks for making this great automation! I have some questions about it.
How to keep the cursor / selection at the current (Doing) line? When moving lines from Doing to Done I want to keep the cursor or selected row at Doing. Is this possible?
Right now all the rows under Done will expand after moving. In other documents I have a lot of collapsed lines under Done. How to prevent this from happening?
I can’t speak for how others are placed, but perhaps you could offer it to anyone here who posts omniJS plugins.
The best way to price is simply to:
make a serious private assessment of how much it would be worth to you, over its life-cycle, in time redirected to more productive activity, and then
offer half of that to the contractor, to allow margin for both win/win and a bit of negotiating flexibility.
If half of its real value to you is too little to be worth the time it would take others to produce, then that may be a helpful indicator that you can probably do without it, in practice.
I generally prefer less modal outliners with fewer widgets – TaskPaper in particular – but occasionally used OmniOutliner in contexts where I needed unique identifiers for nodes in the outline.
As it happens I’m getting that now from a working prototype of another light-weight outliner (not released, at this point).
I don’t think I’ll personally be making much use of OmniOutliner, though I wish it well.