@unlocked2412
I’ve modified it to just copy to the clipboard, and added “Task” as a style now, too. Task prepends a line with 1. [ ]
and treats it like list.
/*{
"type": "action",
"targets": ["omnioutliner"],
"author": "MacDork, based on work by Marc A. Kastner",
"description": "Create Markdown & copy it to the clipboard",
"label": "Markdown to Clipboard",
"paletteLabel": "MarkDown"
}*/
/* https://discourse.omnigroup.com/t/request-copy-selected-rows-in-markdown/44714/2
*/
function msg(msg) {
console.log(msg)
}
function checkCodeBlockSiblings(item) {
/* use item.followingSiblings and item.precedingSiblings to get
arrays of other siblings to check for more code blocks before assigning
pre and post markdown. Returns array of pre and post markdown */
var newline = '\n'
var pre = ''
var post = ''
var precededByCode = false
var followedByCode = false
msg("--------------\nProcessing " + item.topic)
var precedingArray = item.precedingSiblings
if (typeof precedingArray != 'undefined' && precedingArray instanceof Array) {
// we know it has a sibling
var precItem = precedingArray[precedingArray.length - 1]
if (precItem != null) {
// probably redundant
msg("preceding topic: " + precItem.topic)
if (typeof (precItem.style.namedStyles[0]) != 'undefined') {
// this item is styled; let's see what it is
precStyle = precItem.style.namedStyles[0].name
if (precStyle == "Code") {
precededByCode = true
msg("prec was code, so precededByCode = " + precededByCode)
}
} // end of block that checked for named styles
} // possibly redundant null check
}
if (precededByCode) {
// preceded by code block, so don't add more markdown
pre = ''
} else {
// wasn't preceded by a code block, so add the markdown
pre = newline + "```" + newline
}
// repeat the process for the post markdown
var followingArray = item.followingSiblings
if (typeof followingArray != 'undefined' && followingArray instanceof Array) {
var folItem = followingArray[0]
if (folItem != null) {
if (typeof (folItem.style.namedStyles[0]) != 'undefined') {
// this item is styled; let's see what it is
folStyle = folItem.style.namedStyles[0].name
if (folStyle == "Code") {
followedByCode = true
msg("following is code, so followedByCode = " + followedByCode)
}
}
}
}
if (followedByCode) {
// followed by code block, so don't add more markdown
post = ''
} else {
// not followed by a code block, so add the markdown
post = newline + "```" + newline
}
return [pre, post]
}
function getMarkdown(item) {
/*
Given an item, return value array with all the mardown needed, and
info needed to determine the level of indention
*/
var isList = false
var pre = ''
var post = ''
var numPounds = item.level + 1 // +1 since only the doc title should have 1
var newline = '\n'
// check the item to see if it's styled first
if (typeof (item.style.namedStyles[0]) !== "undefined") {
styleName = item.style.namedStyles[0].name // first named style
switch(styleName) {
case "Ordered List":
pre = "1. "
isList = true
break;
case "Unordered List":
pre = "* "
isList = true
break;
case "Task":
pre = "1. [ ] "
isList = true
break;
case "Paragraph":
pre = newline
post = newline
break;
case "Blockquote":
pre = "> "
// post = newline
break;
case "Code":
codeMarkdown = checkCodeBlockSiblings(item)
pre = codeMarkdown[0]
post = codeMarkdown[1]
// pre = "```" + newline
// post = newline + "```"
break;
} // end of switch statement
} else {
/* Handle un-styled item as a heading; level depth = num of #'s + 1.
I'm adding one additional # to account for my feeling that the doc
title should have 1 #, and every other heading should be smaller than
the title.
*/
pre = newline + '#'.repeat(numPounds) + " "
post = newline
}
return [pre, post, isList]
}
function getNumTabs (item) {
/* Given an item, returns the number of tabs to indent it
Recursively calls itself on its parent to gather parent's info. End
condition is when we reach a parent whose number of tabs is 0.
Using documentation from this page: https://omni-automation.com/omnioutliner/item.html
item.parent: (Item or nil r/o) • Returns the item that contains this item, or null if this is the root item.
*/
var parent = item.parent
// check to be sure we haven't returned a null parent
if (parent != null) {
var parentMD = getMarkdown(parent)
parentIsList = parentMD[2] //
// if parent's not a list, return 0; if it is, we need to go higher
if (!parentIsList) {
return 0
} else if (parentIsList) {
// parent was a list, so let's recurse higher
return 1 + getNumTabs(parent)
}
} else {
// we've reached the root item; return 0
return 0
}
}
var _ = function() {
var action = new PlugIn.Action(function(selection, sender) {
var topics = new Array()
var tab = '\t'
var newline = '\n'
var noteString = ''
var numTabs = 0
var tabs = ''
console.clear()
// add document title
docTitle = "# " + document.name
topics.push(docTitle)
// loop through the document's items
rootItem.descendants.forEach(function(item) {
// reset variables for this iteration
var mdInfo = getMarkdown(item)
var pre = mdInfo[0]
var post = mdInfo[1]
var isList = mdInfo[2]
//msg("index: " + item.index)
// now find out how far to indent this item
if (isList) {
numTabs = getNumTabs(item)
tabs = tab.repeat(numTabs)
}
// create the string w/ all its markdown
mdTopic = tabs + pre + item.topic + post
topics.push(mdTopic)
// handle item notes as a paragraph
if (item.note) {
noteString = item.note + newline
topics.push(noteString)
noteString = ''
}
})
mdText = topics.join(newline) //convert array to string
Pasteboard.general.string = mdText
});
action.validate = function(selection, sender) {
if (rootItem.descendants.length > 0) {
return true
} else {
return false
}
}
return action
}();
_;