Action

Add Lines to List

Posted by @JimS, Last update about 2 months ago

UPDATES

about 2 months ago

4.0

  • If the editor is displaying a list, it is previewed (rather than displaying an error).

  • If a specified list is not found, there is an option to create it.

  • If running the action from a blank draft, can optionally open and/or preview the configured list. Added related settings: viewListIfDraftIsEmpty, previewListIfDraftIsEmpty, promptOptionsIfDraftIsEmpty.

  • Change the default value of newListHeading to ‘###’.

  • Added settings theListFontSize (default: 80%) and theListFontFamily (default “Courier New”, Courier, Monaco, “DejaVu Sans Mono”, monospace). The former affects all of the fonts in the draft list, whereas the latter only affects the list entries in the draft list.

  • The draft content send to the Preview is modified to add a \n before and after each HORIZONTAL_RULE. That enables the content to remain condensed (vertically) in the drafts editor, while producing a preview that works better when the ‘Copy Rich Text’ (macOS) option is used.

  • If todays ‘date group’ already exists, new draft lines are added to the existing group (rather than creating a duplicate group).

  • When adding blank lines, a prefix is no longer added to each blank line.

  • The following are properly rendered when a list item is previewed. The latter three were previously supported; the first two were added to this version.

    • [ ] strike through
    • [ ] ==highlighted==
    • [ ] this is strong
    • [ ] this is italic
    • [ ] this is strong italic
  • Preview font and background colors modified to work better when using ‘Copy Rich Text’ (macOS). The colors vary depending on Editor > Appearance:
    light: body { background: white; color: black; }
    dark: body { background: white; color: black; }

  • When Preview is selected, the list markdown content, with one modification, is written to the system clipboard. The modification is that a newline is added before and after each HORIZONTAL_RULE.

  • Bug Fix: The current time is now properly based on localtime.

Add Lines to List, v4.0


Attribution

This action was inspired by Add to list plus by @dchar. Add to List plus is based on Add to List by @agiletortoise.

In addition to the inspiration, the Add to List plus JavaScript code that presents a dialog to select an existing list, or creates a new one, is used in this action. The viewListAfterSuccess code has similarly been copied.

The action dialog is created and evaluated using MGCheckListPrompt, by @mattgemmell.

Help regarding the Preview (v1.1) was provided by @sylumer, @agiletortoise, and @FlohGro. For more information: End an action, yet still honor the AFTER SUCCESS configuration? - Actions - Help & Questions - Drafts Community

@sylumer suggested that this action be callable from another action. He also provided the necessary guidance.

Overview

This action can be used to add lines from a source draft to target list, i.e., another draft (formatted using markdown). The target list can be: 1) hard-coded, 2) selected from all current lists, or 3) created by this action.

Target lists are drafts that are archived and tagged with a configured value (by default the tag is list). Source drafts can be anywhere in the Drafts application.

Features

  • All lines from the source draft are added individually to the target list.

  • A time prefix (YYYY-MM-DD HH:MM: or HH:MM:) can be included with each added line.

  • Added lines can be appended or prepended.

  • A horizontal rule (—) can be added before each group of lines added to the target draft.

  • A date line (YYYY-MM-DD, DayOfWeek) can be included with each group of lines added to the target draft. If this option is selected, the added date and lines are enclosed by horizontal rules. (This action includes logic to prevent horizontal rules on two consecutive lines.)

  • After the target list is updated, it can be displayed in the Drafts editor and/or previewed (i.e., the markdown is rendered). Note: To preview a target list without changing it, run this action with an empty draft in the Drafts editor.

  • Blank lines above and below text in the source draft can be used or ignored. Similarly, interleaving blank lines can be used or ignored.

  • Several of the options mentioned above can be changed at runtime using an action dialog.

  • The default options can be superseded by using an action that calls this action via the Include Action step. The override options in the caller are the same as the options within this action except with an added leading underscore. For example _listTag can be used to override the option listTag. For an example caller, see: Add Lines to List (caller) | Drafts Directory.

Tested With

  • Drafts (iOS), Version 44.0.1 (441) // iOS Version 17.5.1/iPhone 15 Pro Max

  • Drafts (macOS), version 44.0 (440) // Sonoma 14.4.1 (23E224)/MacBookPro18,2/MacBookPro18,2

Version History

1.0 - initial version

1.1
a. If the source draft is empty no content is added to the target list but it can still be loaded and/or previewed.
b. The method used to to preview the target list has been changed such that the action ends nornally; thus the action After Success configuration is honored.

2.0
a. The default options can now be superseded by using an action that calls this action via the Include Action step. The override options in the caller are the same as the options within this action except with an added leading underscore. For example _listTag can be used to override the option listTag. For an example caller, see: Add Lines to List (caller) | Drafts Directory.
b. The list dialog prompts and logic were refined.
c. Changed option excludeBlankLines to excludeInnerBlankLines.
d. The icon color was changed from green to red. By default, the icons of calling actions are green.
e. Bug fix: When addCurrentDateToEachBlock = true and prependLines = false, the closing horizontal rule was prepended. It is now appended.

3.0
Changed option addCurrentDateToEachLine to addCurrentTimeToEachLine. If true, a YYYY-MM-DD HH:MM: or a HH:MM: prefix is added to each line before it is added to the list. If option addCurrentDateToEachBlock = true, it will be the latter, HH:MM:.

4.0
a. If the editor is displaying a list, it is previewed (rather than displaying an error).
b. If a specified list is not found, there is an option to create it.
c. If running the action from a blank draft, can optionally open and/or preview the configured list. Added related settings: viewListIfDraftIsEmpty, previewListIfDraftIsEmpty, promptOptionsIfDraftIsEmpty.
d. Change the default value of newListHeading to ‘###’.
e. Added settings theListFontSize (default: 80%) and theListFontFamily (default “Courier New”, Courier, Monaco, “DejaVu Sans Mono”, monospace). The former affects all of the fonts in the draft list, whereas the latter only affects the list entries in the draft list.
f. The draft content send to the Preview is modified to add a \n before and after each HORIZONTAL_RULE. That enables the content to remain condensed (vertically) in the drafts editor, while producing a preview that works better when the ‘Copy Rich Text’ (macOS) option is used.
g. If todays ‘date group’ already exists, new draft lines are added to the existing group (rather than creating a duplicate group).
h. When adding blank lines, a prefix is no longer added to each blank line.
i. The following are properly rendered when a list item is previewed. The latter three were previously supported; the first two were added to this version.
- [ ] strike through
- [ ] ==highlighted==
- [ ] this is strong
- [ ] this is italic
- [ ] this is strong italic
j. Preview font and background colors modified to work better when using ‘Copy Rich Text’ (macOS). The colors vary depending on Editor > Appearance:
light: body { background: white; color: black; }
dark: body { background: white; color: black; }
k. When Preview is selected, the list markdown content, with one modification, is written to the system clipboard. The modification is that a newline is added before and after each HORIZONTAL_RULE.
l. Bug Fix: The current time is now properly based on localtime.

Drafts Community Post

ACTION: Add Lines to List, v4.0 - Actions - Share What You’ve Made - Drafts Community

Required Library

MGCheckListPrompt | Drafts Directory

Steps

  • includeAction

    name
    MGCheckListPrompt Library
  • script

    //
    // The tag used for target lists.
    //
    // Note: The target lists must also be archived.
    //
    let listTag = 'list';
    
    //
    // The target list that will be updated. If set to '', the action
    // will search for all archived drafts that include a tag=listTag.
    // A dialog will appear that includes these drafts and an
    // additional option to specify a new list.
    // 
    let theListTitle = '';
    
    //
    // The markdown heading level for target lists. If '', '###'
    // will be used.
    //
    let newListHeading = '###';
    
    //
    // The markdown font size for target lists. If '', '80%'
    // will be used.
    //
    let theListFontSize = '80%';
    
    //
    // The markdown font family for target lists. If '',
    // '"Courier New", Courier, Monaco, "DejaVu Sans Mono", monospace'
    // will be used.
    //
    let theListFontFamily = '"Courier New", Courier, Monaco, "DejaVu Sans Mono", monospace';
    // let theListFontFamily = '"Helvetica Neue", "Helvetica", sans-serif';
    
    // -------------------------------------------------------
    // Boolean options below can be displayed (and changed) at
    // runtime if promptOptions = true
    // -------------------------------------------------------
    
    //
    // If `true`, leading and trailing blank lines are ignored
    // as the displayed draft is processed for addition to the list.
    //
    let trimDraft = true;
    
    //
    // If `true`, blank lines after the top text line and before 
    // the bottom text line are ignored when the displayed draft
    // is processed for addition to the list.
    //
    let excludeInnerBlankLines = true;
    
    //
    // If `true`, appends/prepends `---` if the existing adjacent 
    // line in the list is not `---`.
    //
    let addHorizontalRule = false;
    
    //
    // If `true`, appends/prepends `---` (if the existing adjacent 
    // line in the list is not `---`), appends/prepends 
    // YYYY-MM-DD DayOfWeek, appends/prepends `---`.
    //
    let addCurrentDateToEachBlock = true;
    
    //
    // If `true`, lines from the displayed draft are prepended
    // to the list; otherwise lines are appended.
    //
    let prependLines = true;
    
    //
    // If `true`, a `YYYY-MM-DD HH:MM: ` or a `HH:MM: ` prefix is 
    // added to each line before it is added to the list. It will
    // be the latter if addCurrentDateToEachBlock = `true`.
    //
    let addCurrentTimeToEachLine = true;
    
    //
    // If `true`, the target list is loaded into the Drafts Editor
    // after it is updated.
    //
    let viewListAfterSuccess = false;
    
    //
    // If `true`, the target list is previewed using 'HTML Preview'
    // after it is updated.
    //
    let previewListAfterSuccess = true;
    
    //
    // If `true`, the action displays a dialog that includes boolean 
    // options: trimDraft, excludeBlankLines, addHorizontalRule,
    // addCurrentDateToEachBlock, prependLines, addCurrentDateToEachLine,
    // viewListAfterSuccess, previewListAfterSuccess.
    //
    let promptOptions = true;
    
    // -------------------------------------------------------
    // Boolean options below can be displayed (and changed) at
    // runtime if an empty draft is visible when the action is
    // is run and promptOptionsIfDraftIsEmpty = true.
    // -------------------------------------------------------
    
    //
    // If `true`, the target list is loaded into the Drafts Editor
    // if an empty draft is visible when the action is
    // is run.
    //
    let viewListIfDraftIsEmpty = true;
    
    //
    // If `true`, the target list is previewed using 'HTML Preview'
    // if an empty draft is visible when the action is
    // is run.
    //
    let previewListIfDraftIsEmpty = true;
    
    //
    // If `true`, the action displays a dialog that includes boolean 
    // options viewListIfDraftIsEmpty and previewListIfDraftIsEmpty
    // if an empty draft is visible when the action is
    // is run.
    //
    let promptOptionsIfDraftIsEmpty = true;
    
    
  • script

    // If this action is "called" by another action, check if any of the
    // options have been specified by the calling action.
    
    if (typeof _listTag !== 'undefined') listTag = _listTag;
    
    if (typeof _theListTitle !== 'undefined') theListTitle = _theListTitle;
    
    if (typeof _theListFontSize !== 'undefined') theListFontSize = _theListFontSize;
    
    if (typeof _theListFontFamily !== 'undefined') theListFontFamily = _theListFontFamily;
    
    if (typeof _trimDraft !== 'undefined') trimDraft = _trimDraft;
    
    if (typeof _excludeInnerBlankLines !== 'undefined') excludeInnerBlankLines = _excludeInnerBlankLines;
    
    if (typeof _addHorizontalRule !== 'undefined') addHorizontalRule = _addHorizontalRule;
    
    if (typeof _addCurrentDateToEachBlock !== 'undefined') addCurrentDateToEachBlock = _addCurrentDateToEachBlock;
    
    if (typeof _prependLines !== 'undefined') prependLines = _prependLines;
    
    if (typeof _addCurrentTimeToEachLine !== 'undefined') addCurrentTimeToEachLine = _addCurrentTimeToEachLine;
    
    if (typeof _viewListAfterSuccess !== 'undefined') viewListAfterSuccess = _viewListAfterSuccess;
    
    if (typeof _previewListAfterSuccess !== 'undefined') previewListAfterSuccess = _previewListAfterSuccess;
    
    if (typeof _promptOptions !== 'undefined') promptOptions = _promptOptions;
    
    if (typeof _viewListIfDraftIsEmpty !== 'undefined') viewListIfDraftIsEmpty = _viewListIfDraftIsEmpty;
    
    if (typeof _previewListIfDraftIsEmpty !== 'undefined') previewListIfDraftIsEmpty = _previewListIfDraftIsEmpty;
    
    if (typeof _promptOptionsIfDraftIsEmpty !== 'undefined') promptOptionsIfDraftIsEmpty = _promptOptionsIfDraftIsEmpty;
  • script

    // Refer to the action Description.
    
    let showPreview = false;
    let context_cancel = false;
    
    (() => {
    
      const HORIZONTAL_RULE = '---';
      const MARKDOWN_CHECKBOX = '- [ ] ';
    
      const msgTitleInfo = "💡 Information 💡";
      const msgTitleWarn = "⚠️ Warning ⚠️";
      const msgTitleError = "❌ Error ❌";
    
      const msgbox = (strTitle, strMsg) => {
    
        let promptMsg = Prompt.create();
        promptMsg.title = strTitle;
        promptMsg.message = strMsg;
        promptMsg.addButton("OK");
        promptMsg.isCancellable = false;
        promptMsg.show();
        return;
    
      };
    
      const msgInfo = (strMsg) => msgbox(msgTitleInfo, strMsg);
      const msgWarn = (strMsg) => msgbox(msgTitleWarn, strMsg);
      const msgError = (strMsg) => msgbox(msgTitleError, strMsg);
    
      const listWorkspaceTabs = ["archive"];
    
      const PLACEHOLDER_FOR_BLANK_LINE = String.fromCharCode(0x269D) +
        String.fromCharCode(0x26E9) +
        String.fromCharCode(0x2690) +
        String.fromCharCode(0x2624) +
        String.fromCharCode(0x2613) +
        String.fromCharCode(0x2625) +
        String.fromCharCode(0x269C) +
        String.fromCharCode(0x26C5) +
        String.fromCharCode(0x2600) +
        String.fromCharCode(0x2602);
    
      const FLAG_TO_REMOVE_LINE = String.fromCharCode(0x269C) +
        String.fromCharCode(0x26E8) +
        String.fromCharCode(0x26B1) +
        String.fromCharCode(0x2622) +
        String.fromCharCode(0x2612) +
        String.fromCharCode(0x2623) +
        String.fromCharCode(0x269B) +
        String.fromCharCode(0x26C4) +
        String.fromCharCode(0x2604) +
        String.fromCharCode(0x2601);
    
      const getLists = () => {
    
        return listWorkspaceTabs
          .map(s => Draft.query("", s, [listTag]))
          .filter(arr => arr !== undefined)
          .reduce((acc, v) => acc.concat(v), [])
          .reduce((o, d) => {
            const m = d.content.match(/#+ ([^\n]+)/);
            if (m) {
              o[m[1].trim()] = d;
            }
            return o;
          }, {});
    
      };
    
      const sortcase = (xs) => {
    
        return xs.sort((a, b) => {
          return a.toLowerCase().localeCompare(b.toLowerCase());
        })
    
      };
    
      const split = (s, delim) => {
    
        const i = s.indexOf(delim);
        return [
          s.substr(0, (i >= 0) ? i : s.length),
          (i >= 0) ? s.substr(i) : ''
        ];
    
      };
    
      const countLines = str => {
    
        if (str === '') {
          return 0;
        }
        let lineCount = str.split(/\r\n|\r|\n/).length;
        if (str.endsWith('\n')) {
          lineCount--;
        }
        return lineCount;
    
      };
    
      const hhmm = () => {
        const currentDate = new Date();
        const timeString = currentDate.toLocaleTimeString([], {
          hour12: false,
          hour: '2-digit',
          minute: '2-digit'
        });
        return timeString;
      };
    
      const yyyymmdd_dow = () => {
        const currentDate = new Date();
        const dateString = currentDate.toLocaleDateString('en-CA'); // 'en-CA' format gives 'yyyy/mm/dd' format
        const dayOfWeek = new Intl.DateTimeFormat('en-US', {
          weekday: 'long'
        }).format(currentDate);
        return (`${dateString}; ${dayOfWeek}`);
      };
    
      const yyyymmdd = () => {
        const currentDate = new Date();
        const dateString = currentDate.toLocaleDateString('en-CA'); // 'en-CA' format gives 'yyyy/mm/dd' format
        return (`${dateString}`);
      };
    
      const yyyymmddhhmm = () => {
        const currentDate = new Date();
        const dateString = currentDate.toLocaleDateString('en-CA'); // 'en-CA' format gives 'yyyy/mm/dd' format
        const timeString = currentDate.toLocaleTimeString([], {
          hour12: false,
          hour: '2-digit',
          minute: '2-digit'
        });
        return `${dateString} ${timeString}`;
      };
    
      const append = (txtBlock, txtLineToAppend) => {
    
        return [txtBlock, txtLineToAppend].join('\n');
    
      };
    
      const prepend = (txtBlock, txtLineToAppend) => {
        let lines = txtBlock.split('\n');
    
        let preservedLines = lines.splice(0, 1);
    
        while (lines.length > 0 && lines[0].trim() === '') {
          preservedLines.push(lines.shift());
        }
    
        if (txtLineToAppend.trim() === '') {
          txtLineToAppend = PLACEHOLDER_FOR_BLANK_LINE;
        }
    
        preservedLines.push(txtLineToAppend);
    
        if (lines.length > 0) {
          preservedLines = preservedLines.concat(lines);
        }
    
        return preservedLines.join('\n');
      }
    
      const replaceBlankLinePlaceholders = (txtBlock) => {
        let lines = txtBlock.split('\n');
        lines = lines.map(line => {
          return line === PLACEHOLDER_FOR_BLANK_LINE ? '' : line;
        });
        return lines.join('\n');
      };
    
      const deleteFlaggedLines = (txtBlock) => {
        let lines = txtBlock.split('\n');
        lines = lines.filter(line => {
          return !line.startsWith(FLAG_TO_REMOVE_LINE);
        });
        return lines.join('\n');
      };
    
      const trimInnerBlankLines = str => {
    
        let lines = str.split('\n');
    
        let firstNonBlankLineIndex = lines.findIndex(line => line.trim() !== '');
    
        let lastNonBlankLineIndex = lines.slice().reverse().findIndex(line => line.trim() !== '');
    
        lastNonBlankLineIndex = lastNonBlankLineIndex >= 0 ? lines.length - 1 - lastNonBlankLineIndex : lastNonBlankLineIndex;
    
        return lines.filter((line, index) => index < firstNonBlankLineIndex || index > lastNonBlankLineIndex || line.trim() !== '').join('\n');
    
      };
    
      const lineMatchTop = (txtBlock, txtLineSearch) => {
    
        // Ignoring the first list line (the list Title)
    
        const lines = txtBlock.split('\n');
    
        if (lines.length <= 1) {
          return false;
        }
    
        let i = 1;
        while (i < lines.length && lines[i].trim() === '') {
          i++;
        }
    
        if (i >= lines.length) {
          return false;
        }
    
        return lines[i].trim() === txtLineSearch;
    
      };
    
      const lineMatchBottom = (txtBlock, txtLineSearch) => {
    
        // Ignoring the first list line (the list Title)
    
        const lines = txtBlock.split('\n');
    
        let nonBlankCount = 0;
    
        for (let i = 0; i < lines.length; i++) {
          if (lines[i].trim() !== '') {
            nonBlankCount++;
          }
        }
    
        if (nonBlankCount <= 1) {
          return false;
        }
    
        let i = lines.length - 1;
    
        while (i >= 0 && lines[i].trim() === '') {
          i--;
        }
    
        return lines[i].trim() === txtLineSearch;
      };
    
      const getList = (theListTitle) => {
    
        // If the specified list is found, includes a list tag, and
        // is archived, return the list 'draft object'.
    
        const listDraftTitle = newListHeading + " " + theListTitle;
    
        const dTitleMatch = Draft.queryByTitle(listDraftTitle);
    
        const dArchiveMatch = dTitleMatch.filter(obj => obj.isArchived === true);
    
        const dFullMatch = dArchiveMatch.filter(obj => obj.hasTag(listTag) === true);
    
        switch (dFullMatch.length) {
    
          case 0:
    
            let p = Prompt.create();
            p.title = msgTitleInfo;
            p.message = "Specified list (" + theListTitle + ") not found, does not include tag '" + listTag + "', or is not archived. Do you want to create it?";
    
            p.addButton('Yes');
            p.addButton('No');
    
            p.defaultButtonIndex = 0;
    
            if (!p.show()) {
              context.fail();
              return;
            }
    
            if (p.buttonPressed === 'Yes') {
              list = Draft.create();
              list.content = listDraftTitle + '\n';
              list.addTag(listTag);
              list.isArchived = true;
              list.update();
              return list;
            }
    
            return;
            break;
    
          case 1:
    
            return dFullMatch[0];
            break;
    
          default:
    
            let sError = dFullMatch.length + " list matches '" + theListTitle + "', tag '" + listTag + "', and archived.";
            msgError(sError);
            return;
    
        }
    
      };
    
      const pickOrCreateList = () => {
    
        const lists = getLists();
    
        if (Object.getOwnPropertyNames(lists).length !== 0) {
    
          let p = Prompt.create();
    
          p.title = 'Pick or Create a New List';
    
          sortcase(Object.keys(lists)).map(c => p.addButton(c));
    
          p.addButton('Create a New List');
    
          if (!p.show()) {
            context.fail();
            return;
          }
    
          selected_list = lists[p.buttonPressed];
    
        }
    
        if (typeof selected_list === 'undefined') {
    
          // Generate an dialog to specify the new list name
    
          p = Prompt.create();
          p.title = 'Create New List';
          p.addTextField('listName', 'List Name:', '');
          p.addButton('OK');
    
          if (!p.show()) {
            context.fail();
            return;
          }
    
          const name = p.fieldValues['listName'].trim();
          if (name.length == 0) {
            app.displayErrorMessage("Invalid list name");
            context.fail();
            return;
          }
    
          // Create the new list.
    
          list = Draft.create();
          list.content = newListHeading + ` ${name} \n`;
          list.addTag(listTag);
          list.isArchived = true;
          list.update();
    
        }
        else {
    
          list = selected_list;
    
        }
    
        return list;
    
      };
    
      const optionsDialog = () => {
    
        // Create an MGCheckListPrompt.
        let prompt = new MGCheckListPrompt();
        prompt.message = ""; // Removes header.
        prompt.addItems([
    
          {
            separator: true,
            title: "Source Options"
          },
    
          {
            title: "Trim Draft",
            description: "Exclude leading and trailing blank lines",
            selected: trimDraft
          },
    
          {
            title: "Exclude Inner Blank Lines",
            description: "Exclude blank lines between the first and last text lines",
            selected: excludeInnerBlankLines
          },
    
          {
            separator: true,
            title: "List Options"
          },
    
          {
            title: "Separator",
            description: "Add a Horizontal Rule (---) before adding the draft lines",
            selected: addHorizontalRule
          },
    
          {
            title: "Add Today's Date to Each Block",
            description: "Add the date (YYYY-MM-DD) to the beginning to each block",
            selected: addCurrentDateToEachBlock
          },
    
          {
            title: "Prepend Lines",
            description: "Prepend draft lines to list; otherwise append",
            selected: prependLines
          },
    
          {
            title: "Add Today's Date/Time to Each Line",
            description: "Add the date/time (YYYY-MM-DD HH:MM) to the beginning to each line",
            selected: addCurrentTimeToEachLine
          },
    
          {
            title: "View List",
            description: "If line(s) were successfully added, display the list in the editor",
            selected: viewListAfterSuccess
          },
    
          {
            title: "Preview List",
            description: "If line(s) were successfully added, preview the list",
            selected: previewListAfterSuccess
          },
    
        ]);
    
        let selectedItems = prompt.show();
    
        if (prompt.didShow) {
    
          if (selectedItems != null) {
    
            trimDraft = selectedItems.includes(0);
            excludeInnerBlankLines = selectedItems.includes(1);
            addHorizontalRule = selectedItems.includes(2);
            addCurrentDateToEachBlock = selectedItems.includes(3);
            prependLines = selectedItems.includes(4);
            addCurrentTimeToEachLine = selectedItems.includes(5);
            viewListAfterSuccess = selectedItems.includes(6);
            previewListAfterSuccess = selectedItems.includes(7);
    
          }
          else {
    
            return null;
    
          }
    
        }
        else {
    
          app.displayErrorMessage("Something went wrong.");
          return null;
    
        }
    
      };
    
      const optionsDialogIfDraftIsEmpty = () => {
    
        // Create an MGCheckListPrompt.
        let prompt = new MGCheckListPrompt();
        prompt.message = ""; // Removes header.
        prompt.addItems([
    
          {
            separator: true,
            title: "Empty Draft!!!"
          },
    
          {
            separator: true,
            title: "There is nothing to add to: " + theListTitle
          },
    
          {
            title: "View List",
            description: "Before stopping, display the existing list in the editor",
            selected: viewListIfDraftIsEmpty
          },
    
          {
            title: "Preview List",
            description: "Before stopping, preview the existing list",
            selected: previewListIfDraftIsEmpty
          },
    
        ]);
    
        let selectedItems = prompt.show();
    
        if (prompt.didShow) {
    
          if (selectedItems != null) {
    
            viewListIfDraftIsEmpty = selectedItems.includes(0);
            previewListIfDraftIsEmpty = selectedItems.includes(1);
    
          }
          else {
    
            return null;
    
          }
    
        }
        else {
    
          app.displayErrorMessage("Something went wrong.");
          return null;
    
        }
    
      };
    
      const checkTopForDateBlock = (content, textValueToCheck) => {
    
        let lines = content.split('\n');
    
        let i = 1;
    
        while (i < lines.length && lines[i].trim() === '') {
          i++;
        }
    
        for (; i < lines.length - 1; i++) {
          let line = lines[i].trim();
    
          if (line === HORIZONTAL_RULE) {
            if (lines[i + 1].trim() === textValueToCheck) {
              return i;
            }
          }
        }
    
        return -1;
    
      };
    
      const checkBottomForDateBlock = (content, textValueToCheck) => {
    
        let lines = content.split('\n');
    
        let i = lines.length - 1;
    
        while (i >= 0 && lines[i].trim() === '') {
          i--;
        }
    
        if (i >= 0 && lines[i].trim() === HORIZONTAL_RULE) {
          i--;
    
          while (i >= 0 && (lines[i].trim().startsWith(MARKDOWN_CHECKBOX) || lines[i].trim() === '')) {
            i--;
          }
    
          if (i >= 0 && lines[i].trim() === textValueToCheck) {
            return true;
          }
        }
    
        return false;
    
      };
    
      const previewList = (theList) => {
    
        let theMarkdownList = theList.content.replace(/---/g, '\n---\n');
    
        // Copy the list to the clipbpard   
        app.setClipboard(theMarkdownList);
    
        if (app.themeMode == 'dark') {
          var css = "body { background: lightgrey; color: black; }";
        }
        else {
          var css = "body { background: white; color: black; }";
        }
        draft.setTemplateTag("bodystyle", css);
    
        theMarkdownList = theMarkdownList.replace(/~~(.*)~~/gm, '\<del>$1\<\/del>');
        theMarkdownList = theMarkdownList.replace(/==(.*)==/gm, '\<mark>$1\<\/mark>');
        draft.setTemplateTag("list_markdown", theMarkdownList);
    
        if (theListFontSize === '') {
          theListFontSize = '80%';
        }
    
        if (theListFontFamily === '') {
          theListFontFamily = '"Courier New", Courier, Monaco, "DejaVu Sans Mono", monospace';
        }
    
        draft.setTemplateTag("list_font_size", theListFontSize);
        draft.setTemplateTag("list_font_family", theListFontFamily);
    
      };
    
      // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = 
    
      const main = () => {
    
        // draft == the source draft with the new item(s)
    
        if (draft.hasTag(listTag)) {
    
          // If the editor includes a list, just preview it;
          // context.cancel() will run after the preview.
    
          previewList(draft);
          showPreview = true;
          context_cancel = true;
          return;
    
        }
    
        let list;
    
        if (theListTitle !== '') {
          list = getList(theListTitle);
        }
        else {
          list = pickOrCreateList();
        }
    
        if (list === undefined) {
          context.cancel();
          return;
        }
    
        draftText = draft.content;
    
        if (countLines(draftText) < 1) {
    
          // Empty source draft
    
          if (promptOptionsIfDraftIsEmpty) {
            if (optionsDialogIfDraftIsEmpty() === null) {
              context.cancel();
              return;
            }
          }
    
          if (viewListIfDraftIsEmpty) {
            editor.load(list);
          }
    
          if (previewListIfDraftIsEmpty) {
            previewList(list);
            showPreview = true;
          }
    
          return;
    
        }
    
        // The source draft is not empty
    
        if (promptOptions) {
          if (optionsDialog() === null) {
            context.cancel();
            return;
          }
        }
    
        if (trimDraft) {
          draftText = draft.content.trim();
        }
    
        if (draftText !== '') {
    
          draftText = draftText.replace(/\n$/, '');
    
          if (excludeInnerBlankLines) {
            draftText = trimInnerBlankLines(draftText);
          }
    
          let draft_lines = draftText.split('\n');
    
          if (addCurrentTimeToEachLine && addCurrentDateToEachBlock) {
            prefix = MARKDOWN_CHECKBOX + hhmm() + `: `;
          }
          else if (addCurrentTimeToEachLine) {
            prefix = MARKDOWN_CHECKBOX + yyyymmddhhmm() + `: `;
          }
          else {
            prefix = MARKDOWN_CHECKBOX;
          }
    
          let _yyyymmdd_dow = yyyymmdd_dow();
          let list_content = list.content
    
          if (prependLines) {
    
            if ((addHorizontalRule || addCurrentDateToEachBlock) && !lineMatchTop(list_content, HORIZONTAL_RULE)) {
    
              list_content = prepend(list_content, HORIZONTAL_RULE);
    
            }
    
            if (addCurrentDateToEachBlock) {
    
              let foundIndex = checkTopForDateBlock(list_content, _yyyymmdd_dow);
    
              if (foundIndex !== -1) {
                let list_lines = list_content.split('\n');
    
                for (let i = foundIndex; i < foundIndex + 2; i++) {
                  list_lines[i] = FLAG_TO_REMOVE_LINE + list_lines[i];
                }
    
                list_content = list_lines.join('\n');
              }
    
            }
    
            draft_lines.reverse().forEach(draft_line => {
    
              if (draft_line.trim() == '') {
                new_line = '';
              }
              else {
                new_line = `${prefix}${draft_line}`;
              }
              list_content = prepend(list_content, new_line);
    
            });
    
            if (addCurrentDateToEachBlock) {
    
              list_content = prepend(list_content, _yyyymmdd_dow);
    
              list_content = prepend(list_content, HORIZONTAL_RULE);
    
            }
    
            list_content = deleteFlaggedLines(list_content)
            list.content = replaceBlankLinePlaceholders(list_content);
            list.update();
    
          }
          else {
    
            if (addCurrentDateToEachBlock && checkBottomForDateBlock(list_content, _yyyymmdd_dow)) {
    
              let list_lines = list_content.split('\n');
    
              let i = list_lines.length - 1;
    
              while (i >= 0 && list_lines[i].trim() === '') {
                i--;
              }
    
              if (i >= 0 && list_lines[i].trim() === HORIZONTAL_RULE) {
                list_content = list_lines.slice(0, i).join('\n');
              }
    
            }
            else {
    
              if ((addHorizontalRule || addCurrentDateToEachBlock) && !lineMatchBottom(list_content, HORIZONTAL_RULE)) {
    
                list_content = append(list_content, HORIZONTAL_RULE);
    
              }
    
              if (addCurrentDateToEachBlock) {
    
                list_content = append(list_content, _yyyymmdd_dow);
    
              }
    
            }
    
            draft_lines.forEach(draft_line => {
    
              if (draft_line.trim() == '') {
                new_line = '';
              }
              else {
                new_line = `${prefix}${draft_line}`;
              }
              list_content = append(list_content, new_line);
    
            });
    
            if (addCurrentDateToEachBlock) {
    
              list_content = append(list_content, HORIZONTAL_RULE);
              list.update();
    
            }
    
            list.content = list_content;
            list.update();
          }
    
        }
    
        if (viewListAfterSuccess) {
          editor.load(list);
        }
        else {
          if (!previewListAfterSuccess && draftText.length > 0) {
            app.displaySuccessMessage("List updated");
          }
        }
    
        if (previewListAfterSuccess) {
          previewList(list);
          showPreview = true;
        }
    
      };
    
      main();
    
    })();
  • defineTemplateTag

    name
    preview_css
    template
    @charset "utf-8";
    
    html { 
    	font-size:[[list_font_size]];
    	font-family:"Helvetica Neue", "Helvetica", sans-serif;
    }
    body {
    	margin:0;
    	padding:1em;
    }
    [[bodystyle]]
    @media (max-device-width: 480px) { 
    
    } 
    @media (min-device-width: 481px) { 
    	body {
    		margin:auto;
    		max-width:600px;
    	} 
    }
    
    blockquote {
    	font-style: italic;
    }
    
    code, pre {
        border-radius: 3px;
        padding: .5em;
       color: inherit;
    }
    
    ol, ul { 
       font-family:[[list_font_family]];
    }
    
    table {
      margin: 1em 0;
      border: 1px solid #aaa;
      border-collapse: collapse;
    }
    
    th {
      padding:.25em .5em;
      border: 1px solid #ccc;  
    }
    
    td {
      padding:.25em .5em;
      border: 1px solid #ccc;
    }
  • defineTemplateTag

    name
    htmlpreview
    template
    
    
    	
    		Preview
    		
    		
    	
    	
    
    %%[[list_markdown]]%%
    
    	
    
  • script

    if (showPreview) {
    	const htmlpreview = draft.getTemplateTag("htmlpreview")
    	const theHtml = draft.processTemplate(htmlpreview);
    	const hpObj = HTMLPreview.create();
    	hpObj.show(theHtml);
    }
    
    if (context_cancel) {
    	context.cancel()
    }
    	

Options

  • After Success Nothing
    Notification Info
    Log Level Info
Items available in the Drafts Directory are uploaded by community members. Use appropriate caution reviewing downloaded items before use.