Action

Add Lines to List

Posted by @JimS, Last update 11 days ago

UPDATES

11 days ago

5.0

  • Added sourceUUID variable: Allows specifying a particular draft UUID to use as the source content instead of the currently displayed draft. When set to an empty string (“), the action defaults to using the currently displayed draft. When set to 0, the action uses the last modified draft as the source.

  • Added quickAction variable: Controls the action’s execution mode with three possible values: ” (empty) for normal behavior with full functionality including sourceUUID capability, ‘update_only’ for streamlined mode that only updates the target list without showing dialogs or previews, and ‘preview_only’ for preview-only mode that shows a preview of the current draft (if it has the listTag), the specified theListTitle, or prompts for user list selection.

show all updates...

11 days ago

5.0

  • Added sourceUUID variable: Allows specifying a particular draft UUID to use as the source content instead of the currently displayed draft. When set to an empty string (“), the action defaults to using the currently displayed draft. When set to 0, the action uses the last modified draft as the source.

  • Added quickAction variable: Controls the action’s execution mode with three possible values: ” (empty) for normal behavior with full functionality including sourceUUID capability, ‘update_only’ for streamlined mode that only updates the target list without showing dialogs or previews, and ‘preview_only’ for preview-only mode that shows a preview of the current draft (if it has the listTag), the specified theListTitle, or prompts for user list selection.

about 1 year 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, v5.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. Normally, the draft open in the editor is the source draft, but this can be changed by setting the sourceUUID variable to a specific draft’s UUID or to 0 to use the last modified draft.

  • The target list can be specified by setting the theListTitle variable, selected from the existing lists, or created new.

  • 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.)

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

  • 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 or simply run the action when the list is in the editor.

  • The action supports different execution modes through the quickAction variable:

    • '' (empty): Full-featured default mode
    • 'update_only': Only updates the list silently
    • 'preview_only': Displays a preview without modifying any lists
  • 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 47.2 (573) // iOS Version 18.6/iPhone 15 Pro Max

  • Drafts (macOS), Version 47.2 (573) // Sequoia 15.5 (24F74)/MacBookPro18,2


Version History

Version History

1.0 - initial version

1.1

  • If the source draft is empty no content is added to the target list but it can still be loaded and/or previewed.

  • The method used to preview the target list has been changed such that the action ends normally; thus the action After Success configuration is honored.

2.0

  • 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.

  • The list dialog prompts and logic were refined.

  • Changed option excludeBlankLines to excludeInnerBlankLines.

  • The icon color was changed from green to red. By default, the icons of calling actions are green.

  • 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

  • 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 sent to the Preview is modified to add a newline 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 today’s ‘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.

5.0

  • Added sourceUUID variable: Allows specifying a particular draft UUID to use as the source content instead of the currently displayed draft. When set to an empty string (“), the action defaults to using the currently displayed draft. When set to 0, the action uses the last modified draft as the source.

  • Added quickAction variable: Controls the action’s execution mode with three possible values: ” (empty) for normal behavior with full functionality including sourceUUID capability, ‘update_only’ for streamlined mode that only updates the target list without showing dialogs or previews, and ‘preview_only’ for preview-only mode that shows a preview of the current draft (if it has the listTag), the specified theListTitle, or prompts for user list selection.


Drafts Community Post

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


Required Library

MGCheckListPrompt | Drafts Directory

Steps

  • includeAction

    name
    MGCheckListPrompt Library
  • script

    // The UUID of a specific draft to use as the source instead of 
    // the currently displayed draft. If set to '', the action will
    // use the currently displayed draft. If set to '0', the action
    // will use the last modified draft as the source.
    // Note: If this is set to a draft with tag=listTag, then the 
    // list will will not be modified, but it will be opened in the 
    // editor and previewed.
    let sourceUUID = '';
    
    // Controls the action behavior: '' | update_only | preview_only
    // - '': normal behavior (with sourceUUID capability if specified)
    // - 'update_only': only update the target list, no dialog, preview, etc.
    // - 'preview_only': only preview (current draft if it has listTag, 
    //   theListTitle if specified, or user-selected list)
    let quickAction = '';
    
    // 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 _sourceUUID !== 'undefined') sourceUUID = _sourceUUID;
    
    if (typeof _quickAction !== 'undefined') quickAction = _quickAction;
    
    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;
    let draftText; // Moved to global scope
    let list; // Moved to global scope
    
    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 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 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();
    
      let selected_list;
    
      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
    
        let 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, templateDraft = draft) => {
      // Always use the original draft object since that's where 
      // 'Define Template Tag' steps set the templates
      let templateDraftForPreview = draft;
    
      let theMarkdownList = theList.content.replace(/---/g, '\n---\n');
      app.setClipboard(theMarkdownList);
    
      let css;
      if (app.themeMode === 'dark') {
        css = "body { background: lightgrey; color: black; }";
      }
      else {
        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);
    
      // Use configurable font settings with fallbacks
      let fontSizeToUse = theListFontSize || '80%';
      let fontFamilyToUse = theListFontFamily || '"Courier New", Courier, Monaco, "DejaVu Sans Mono", monospace';
    
      draft.setTemplateTag("list_font_size", fontSizeToUse);
      draft.setTemplateTag("list_font_family", fontFamilyToUse);
    };
    
    const main = () => {
      if (draft.hasTag(listTag)) {
        previewList(draft, draft);
        showPreview = true;
        context_cancel = true;
        return;
      }
    
      let sourceDraft = draft;
      if (sourceUUID !== '') {
        if (sourceUUID === '0') {
          // Use the last modified draft as the source
          let allDrafts = Draft.query("", "all", []);
          if (allDrafts.length > 0) {
            // Sort drafts by modification date (most recent first)
            allDrafts.sort((a, b) => b.modifiedAt - a.modifiedAt);
            sourceDraft = allDrafts[0];
            editor.load(sourceDraft); // Open the last modified draft in the editor
          } else {
            msgError("No drafts found to use as source.");
            context.fail();
            return;
          }
        } else {
          sourceDraft = Draft.find(sourceUUID);
          if (!sourceDraft) {
            msgError(`Draft with UUID "${sourceUUID}" not found.`);
            context.fail();
            return;
          }
          editor.load(sourceDraft); // Open the source draft in the editor
        }
      }
    
      if (quickAction === 'preview_only') {
        let listToPreview;
    
        if (sourceDraft.hasTag(listTag)) {
          listToPreview = sourceDraft;
        }
        else if (theListTitle !== '') {
          listToPreview = getList(theListTitle);
          if (!listToPreview) {
            context.cancel();
            return;
          }
        }
        else {
          listToPreview = pickOrCreateList();
          if (!listToPreview) {
            context.cancel();
            return;
          }
        }
    
        previewList(listToPreview, sourceDraft);
        showPreview = true;
        context_cancel = true;
        return;
      }
    
      if (sourceDraft.hasTag(listTag)) {
        previewList(sourceDraft, sourceDraft);
        showPreview = true;
        context_cancel = true;
        return;
      }
    
      if (theListTitle !== '') {
        list = getList(theListTitle);
      }
      else {
        list = pickOrCreateList();
      }
    
      if (list === undefined) {
        context.cancel();
        return;
      }
    
      draftText = sourceDraft.content;
    
      if (countLines(draftText) < 1) {
        if (promptOptionsIfDraftIsEmpty) {
          if (optionsDialogIfDraftIsEmpty() === null) {
            context.cancel();
            return;
          }
        }
    
        if (viewListIfDraftIsEmpty) {
          editor.load(list);
        }
    
        if (previewListIfDraftIsEmpty) {
          previewList(list, sourceDraft);
          showPreview = true;
        }
    
        return;
      }
    
      // The source draft is not empty
    
      if (quickAction !== 'update_only' && promptOptions) {
        if (optionsDialog() === null) {
          context.cancel();
          return;
        }
      }
    
      if (trimDraft) {
        draftText = sourceDraft.content.trim();
      }
    
      if (draftText !== '') {
    
        draftText = draftText.replace(/\n$/, '');
    
        if (excludeInnerBlankLines) {
          draftText = trimInnerBlankLines(draftText);
        }
    
        let draft_lines = draftText.split('\n');
    
        let prefix;
        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 => {
            let new_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 => {
            let new_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.content = list_content;
          list.update();
        }
    
      }
    
      if (quickAction === 'update_only') {
        app.displaySuccessMessage("List updated");
        return;
      }
    
      if (viewListAfterSuccess) {
        editor.load(list);
      }
      else {
        if (!previewListAfterSuccess && draftText.length > 0) {
          app.displaySuccessMessage("List updated");
        }
      }
    
      if (previewListAfterSuccess) {
        previewList(list, 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) {
      // Always use the original draft object since that's where 'Define Template Tag' steps set the templates
      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 , Tags: added to list
    Notification Info
    Log Level Info
Items available in the Drafts Directory are uploaded by community members. Use appropriate caution reviewing downloaded items before use.