Printing TaskPaper 3 documents with Marked 2 CSS templates


#21

Still no joy. I have a slightly newer version of Taskpaper (Version 3.3 Preview (215)). My console errors are coming up from Marked 2 not TP. I’m afraid I cannot share my document as it contains sensitive information. The two errors in the console

Marked 2[14160]: -[SFLListItem synthesizeMissingPropertyValues] Failed to updated bookmark for item Clipboard Preview.... with error Error Domain=NSCocoaErrorDomain Code=260 "The file couldn’t be opened because it doesn’t exist."

Marked 2[14160]: VDKQueue tried to add the path .... to watchedPathEntries, but the VDKQueuePathEntry was nil. It's possible that the host process has hit its max open file descriptors limit.


#22

Probably worth checking to see whether this also happens when you just copy some vanilla Markdown and attempt to print from clipboard in Marked 2.

Possibly an issue for the Marked 2 forum ?

Good luck !


#23

Hard to understand why but it seems to be an issue with Marked 2.


#24

I think, if you look/ask on the Marked 2 support forum, there may be some issue, after an upgrade, which can be fixed by clearing a Marked 2 cache or settings file.


#25

@complexpoint, is there any chance this script could be modified to just ‘Copy to Markdown’ without the need of Marked 2 and/or KM? This would be such a great feature for me who loves to write my notes in Taskpaper (from meetings and others) and then post them i Markdown format?


#26

I use taskpaper the same way, 1 file per project / topics, lot of notes, meeting notes, link to other files/email inside…and tasks, that I transfer to remember / omnifocus. I send report which are part of this file generally using Markdown.


#27

Hmm, just bought Marked2 based on this discussion. Install the KM macro. restart everything and no love, with the existing key combinations I get some Taskpaper search function. But no text preview or printing. Marked2 is installed. Taskpaper 3.7.6 (311). Any suggestions ot get this to work?


#28

Let’s take it step by step.

First, here’s a version with the dependence on Keyboard Maestro (for options settings) disabled.

What happens if you run this (making sure to copy all of the code - accidentally missing out a line or two at the end or start is another common glitch that can easily arise):

// COPY THE ACTIVE TASKPAPER DOCUMENT INTO THE CLIPBOARD IN A FORM THAT CAN BE
// PREVIEWED AND PRINTED (WITH CSS STYLESHEETS) USING MARKED 2


// appIsInstalled :: String -> Bool
function appIsInstalled(strBundleId) {
    ObjC.import('AppKit');

    return ObjC.unwrap(
        $.NSWorkspace.sharedWorkspace
        .URLForApplicationWithBundleIdentifier(
            strBundleId
        )
        .fileSystemRepresentation
    ) !== undefined;
}

var strKME = "com.stairways.keyboardmaestro.engine",
    blnKM = false, //appIsInstalled(strKME), 
    kmVars = blnKM ? Application(strKME)
    .variables : undefined;

// Marked 2 > Preview > Clipboard Preview
// http://marked2app.com/

// If Marked 2 is installed, the script opens it and displays a CSS-formatted clipboard preview


// Draft 0.09  Rob Trew 2016-04-13

// 0.09 Allows for hiding of @saved searches
// 0.08 Allows for printing only outline-visible + filter-visible lines

// 0.06 Allows for an API change in 3.2 Preview (197)
//    (item.bodyContentString || item.bodyDisplayString)

// 0.05 adds further options (at bottom of script):
//     - baseHeaderLevel
//  - hideProjectColon
//  - markedStyle
//  - tagEmphasis
//  - tagsToHide

// 0.02 adds option for blank line between a task and its note(s)

(function (dctOptions) {
    'use strict';


    // TASKPAPER CONTEXT

    function TaskPaperContext(editor, options) {

        // tagString :: item -> String
        function tagString(item, lstHidden, strMDChar) {
            strMDChar = strMDChar || '';

            var dctAttribs = item.attributes,
                lstTags = Object.keys(dctAttribs)
                .filter(function (k) {
                    return lstHidden.indexOf(k) === -1 &&
                        k !== 'data-type' && k !== 'indent';
                })
                .map(function (k) {
                    var v = dctAttribs[k];

                    return strMDChar + '@' + k.slice(5) +
                        (v ? '(' + v + ')' : '') + strMDChar;
                });

            return ' ' + lstTags.join(' ');
        }

        var blnNoteGap = parseInt(options.noteGap, 10),
            blnShowAll = !(parseInt(options.hideFoldedAndFiltered, 10)),
            blnHideSearches = parseInt(options.hideSavedSearches, 10),
            intBaseLevel = options.baseHeaderLevel,
            strStyle = options.markedStyle,
            blnColon = !parseInt(options.hideProjectColon, 10),
            maybeHidden = options.tagsToHide,
            lstHidden = maybeHidden instanceof Array ? ['search'].concat(
                maybeHidden)
            .map(
                function (tag) {
                    return tag !== '*' ? (
                        'data-' + (tag.charAt(0) === '@' ? tag.slice(1) :
                            tag)
                    ) : '*';
                }) : ['search'],
            blnAllTagsHidden = lstHidden.indexOf('*') !== -1,
            strEmphasis = options.tagEmphasis;

        // Lines to print
        var lstVisible = editor.outline.items
            .filter(function (x) {
                return (blnShowAll || editor.isDisplayed(x));
            }),
            lstItems = blnHideSearches ? lstVisible.filter(function (x) {
                return x.bodyString.indexOf('@search') !== 0;
            }) : lstVisible;
        lngLast = lstItems.length - 1;

        return (
                intBaseLevel ? 'Base Header Level: ' +
                intBaseLevel.toString() + '\n' : ''
            ) + (
                strStyle ? 'Marked Style: ' + strStyle + '\n\n' : ''
            ) + lstItems

            // Indent levels required by Markdown format
            .reduce(function (a, item) {
                var strType = item.getAttribute('data-type'),
                    blnProj = strType === 'project';

                if (blnProj) a.depth = item.depth;
                a.lines.push(
                    blnProj ? {
                        isProject: true,
                        item: item,
                        mdIndent: 0
                    } : {
                        isNote: strType !== 'task',
                        item: item,
                        mdIndent: item.depth - a.depth
                    }
                );

                return a;
            }, {
                lines: [],
                depth: 0
            })
            .lines


            // Hash prefixes, zero indents and extra line breaks for projects,
            // adjusted indents for tasks and notes.
            .map(function (mItem, i, xs) {

                var item = mItem.item,
                    blnProject = mItem.isProject,
                    blnNote = mItem.isNote || false,
                    blnTask = !(blnProject || blnNote),
                    blnGap = blnNote && blnNoteGap,
                    blnLastGap = (blnGap && (i < lngLast)) ? (!xs[i + 1]
                        .isNote
                    ) : false
                strText = (item.bodyContentString || item.bodyDisplayString ||
                    '');

                return (
                        blnProject ? (
                            '\n' + Array(item.depth + 1)
                            .join('#') + ' '
                        ) : Array(mItem.mdIndent)
                        .join('\t')
                    ) +
                    (blnTask ? '- ' : '') +
                    (blnGap ? '<p>' : '') +
                    strText +
                    (blnProject && blnColon ? ':' : '') +
                    (blnAllTagsHidden ? '' : tagString(item, lstHidden,
                        strEmphasis)) +
                    (blnLastGap ? '<p>' : '');
            })
            .join('\n');
    }


    // JAVASCRIPT FOR AUTOMATION CONTEXT

    // menuItemClick :: String -> [String] -> IO ()
    function menuItemClick(strAppID, lstMenuPath) {
        var oApp = Application(strAppID),
            strAppName = oApp.name(),
            lngChain = lstMenuPath.length,
            blnResult = false;

        if (lngChain > 1) {

            var appSE = Application("System Events"),
                lstApps = appSE.processes.where({
                    name: strAppName
                }),
                procApp = lstApps.length ? lstApps[0] : null;

            if (procApp) {
                oApp.activate();
                var strMenu = lstMenuPath[0],
                    fnMenu = procApp.menuBars[0].menus.byName(strMenu),
                    lngLast = lngChain - 1;

                for (var i = 1; i < lngLast; i++) {
                    strMenu = lstMenuPath[i];
                    fnMenu = fnMenu.menuItems[strMenu].menus[strMenu];
                }

                fnMenu.menuItems[
                    lstMenuPath[lngLast]
                ].click();
                blnResult = true;
            }
        }
        return blnResult;
    }

    var strMarked2 = "com.brettterpstra.marked2";

    var ds = Application("TaskPaper")
        .documents,
        d = ds.length ? ds[0] : undefined;

    var strClip = d ? d.evaluate({
        script: TaskPaperContext.toString(),
        withOptions: dctOptions
    }) : '';


    // Place Markdown copy in clipboard,
    // displaying in Marked 2 if it is installed
    if (strClip.length > 0) {
        var a = Application.currentApplication(),
            sa = (a.includeStandardAdditions = true, a);

        sa.setTheClipboardTo(strClip);

        if (appIsInstalled(strMarked2)) {
            Application(strMarked2)
                .activate();

            menuItemClick(strMarked2, ['Preview', 'Clipboard Preview']);
        }
    }

    return strClip;

})(blnKM ? { // Read Keyboard Maestro values in macro
    // Integer 1-6  Render unindented projects as H1-H6
    baseHeaderLevel: kmVars['Base header level'].value(),

    // Bool (Print outline-visible and filter-visible lines only ?)
    hideFoldedAndFiltered: kmVars['Hide folded and filtered'].value(),

    // Bool
    hideProjectColon: kmVars['Hide project colon'].value(),

    // Bool
    hideSavedSearches: kmVars['Hide saved searches'].value(),

    // StyleName or empty string or undefined to use Marked default
    markedStyle: kmVars['Marked 2 style'].value(),

    // blank line between a task and its note text ?
    noteGap: kmVars['Gap between notes'].value(),

    // asterisk for italic, two for bold, backtick for code
    tagEmphasis: kmVars['Tag emphasis'].value(),

    // ['*'] hides all tags in printed version
    // ['done', 'due'] hides @done and @due tags
    // [] or false hides no tags
    tagsToHide: kmVars['Tags to hide'].value()
        .split(/[\s\,\;]+/)
} : {
    // USE MANUAL VALUES (1 for Bool true, 0 for Bool false)
    // Integer 1-6  Render unindented projects as H1-H6
    baseHeaderLevel: 4,

    // 1|0 (Print outline-visible and filter-visible lines only ?)
    hideFoldedAndFiltered: 1,

    // 1|0 (true|false)
    hideProjectColon: 1,

    // 1|0
    hideSavedSearches: 1,

    // StyleName or empty string or undefined to use Marked default
    markedStyle: 'Antique',

    // 1|0 blank line between a task and its note text ?
    noteGap: 1,

    // asterisk for italic, two for bold, backtick for code
    tagEmphasis: '*',

    // ['*'] hides all tags in printed version
    // ['done', 'due'] hides @done and @due tags
    // [] or false hides no tags
    tagsToHide: ['alert']
});