Preview a non-current draft

I have a JavaScript action which constructs a MarkDown table from the contents of the current draft. It places the table in a different draft. Now I want to preview the table in that other draft as HTML, then return to continue editing the original draft.

But I can’t work out how to get the HTML Preview action to show a different draft from the current one. Is this possible please? Or can I programmatically switch the current draft to the one with the table, do the preview, then switch back afterwards?

One approach might be to take inspiration on the action I posted yesterday for previewing multiple drafts.

It creates HTML from a set of Drafts, store the result with added style info in a variable, then generates a preview from that variable.

If you keep editing and generating new drafts you will potentially be creating a lot of redundant drafts you will need to remove later, so I think a preview of temporary content it better.

Do you have a final converted version you want to keep? If so you could ti’s the cancel/continue result that a scripted preview generates to then optionally go on and create a draft with the full content, sav it to file, etc.

Great, thanks very much! That does the trick and gives a better workflow than using a separate draft. For the moment at least I just want to look at the table, not create a permanent version..

But I haven’t got CSS to work yet. I made a template in

iCloud/Drafts/Library/Templates/PreviewStyle.txt

containing

<style> (the CSS I want) </style>

and can include it via

draft.processTemplate("\n\n[[template|PreviewStyle.txt]]\n\n") + myContent

as your action does, but it does not seem to affect the style of the displayed HTML. Any hints on that would be appreciated.

Edited to add: Could the problem be that the included style element will be in the HTML body, whereas ideally it should be in the head?

I suspect the problem is in the processing of your style by the Markdown processor. It is finicky and unless you whitespace your content right, you can end up with paragraph tags that break it. I think often you need spacing between the style tags and the styling content.


Let’s do a quick worked example starting with some content in a draft to process just in case you’ve missed something else on the way.

Here I have created a bit of Markdown and within it I have placed some lines of CSV text that I would like to transform into a table when previewed.

# CSV Example

"Fruit", "Colour", "Units"  
"Apple", "Red", 6  
"Banana", "Yellow", 9  
"Passion Fruit", "Purple", 2  
"Apple", "Green", 7  
"Orange", "Orange", 6  

*Table above correct as of 2025-09-20.*

But, I want to style the table, so let’s create the same preview file as you, iCloud Drive/Drafts/Library/Templates/PreviewStyle.txt.

<style>

    table
    {
        border-collapse: collapse;
        margin: 25px 0;
        font-size: 0.9em;
        font-family: sans-serif;
        min-width: 400px;
        box-shadow: 0 0 20px rgba(0, 0, 0, 0.15);
    }

    thead tr 
    {
        background-color: #009879;
        color: #ffffff;
        text-align: left;
    }

    th, td 
    {
        padding: 12px 15px;
    }

    tbody tr 
    {
        border-bottom: 1px solid #dddddd;
    }

    tbody tr:nth-of-type(even)
    {
        background-color: #f3f3f3;
    }

    tbody tr:last-of-type 
    {
        border-bottom: 2px solid #009879;
    }

    tbody tr.active-row 
    {
        font-weight: bold;
        color: #009879;
    }

</style>

I can then create an action with a script step that:

  1. Takes the content of the current draft.
  2. Converts the CSV lines to a Markdown table.
  3. Adds the styling from file.
  4. Converts that all to HTML and shows it as a preview.
	// Initialise content
	const STYLEDCONTENT = draft.processTemplate("\n\n[[template|PreviewStyle.txt]]\n\n") + convertCsvBlocksToMarkdown(draft.content);

	// Initialise build
	let hp = HTMLPreview.create();
	let mmd = MultiMarkdown.create();
	mmd.format = "html";

	// Generate preview
	hp.show(mmd.render(STYLEDCONTENT));
For anyone who wants the extra code to process the draft content’s CSV into a Markdown table format, it is here, but isn’t actually all that relevant to the underlying request.
function convertCsvBlocksToMarkdown(p_strMD)
{
	// Initialise
	let astrLines = p_strMD.split(/\r?\n/);
	let astrOut = [];
	let inCode = false;
	let i = 0;

	// Process lines
	while (i < astrLines.length)
	{
		const LINE = astrLines[i];

		// Toggle state for any fenced codeblock
		if (LINE.trim().startsWith('```'))
		{
			inCode = !inCode;
			astrOut.push(LINE);
			i++;
			continue;
		}

		// If we're not in a code block and this line looks like CSV (contains a comma),
		// gather subsequent consecutive lines that also look like CSV lines.
		if (!inCode && LINE.trim() !== '' && LINE.indexOf(',') !== -1) 
		{
			const BLOCK = [];
			while (i < astrLines.length)
			{
				let strCurrentLine = astrLines[i];

				// Blank line ends consecutive block of lines
				if (strCurrentLine.trim() === '') break;

				// No comma, so not CSV-like
				if (strCurrentLine.indexOf(',') === -1) break;

				// Don't cross code fences
				if (strCurrentLine.trim().startsWith('```')) break;

				BLOCK.push(strCurrentLine);
				i++;
			}

			// Only treat as CSV if there's at least a header + one data row (two CSV lines)
			if (BLOCK.length >= 2)
			{
				const ROWS = BLOCK.map(parseCSVLine);
				const COLUMNCOUNT = Math.max(...ROWS.map(r => r.length), ROWS[0].length);
				
				// Normalise rows to same column count
				for (const r of ROWS) while (r.length < COLUMNCOUNT) r.push('');

				// Build markdown table
				astrOut.push('| ' + ROWS[0].map(escapeCell).join(' | ') + ' |');
				astrOut.push('| ' + new Array(COLUMNCOUNT).fill('---').join(' | ') + ' |');
				for (let r = 1; r < ROWS.length; r++) astrOut.push('| ' + ROWS[r].map(escapeCell).join(' | ') + ' |');
			} 
			else astrOut.push(...BLOCK);

			continue;
		}

		// default: copy the line
		astrOut.push(LINE);
		i++;
	}

	return astrOut.join('\n');
}


function parseCSVLine(p_strLine)
{
	const cells = [];
	let j = 0;
	let L = p_strLine.length;
	while (j < L) 
	{
		// Skip leading spaces
		while (j < L && /\s/.test(p_strLine[j])) j++;
		if (j >= L) { cells.push(''); break; }

		let cell = '';
		if (p_strLine[j] === '"') 
		{
			// Quoted field
			// (Skip opening quote)
			j++; 
			while (j < L) 
			{
				if (p_strLine[j] === '"') 
				{
					if (j + 1 < L && p_strLine[j + 1] === '"') 
					{ 
						// Escaped quote
						cell += '"';
						j += 2;
					} 
					else 
					{
						// Closing quote
						j++; 
						break;
					}
				} 
				else 
				{
					cell += p_strLine[j++];
				}
			}
			// Skip any whitespace after the closing quote
			while (j < L && /\s/.test(p_strLine[j])) j++;
			if (j < L && p_strLine[j] === ',') j++;
		} 
		else 
		{
			// Unquoted field
			while (j < L && p_strLine[j] !== ',') {cell += p_strLine[j++];}
			if (j < L && p_strLine[j] === ',') j++;
			cell = cell.trim();
		}

		cells.push(cell);

		// Handling for trailing comma that would imply an empty final cell (e.g. "a,b,")
		if (j >= L && L > 0 && p_strLine[L - 1] === ',') cells.push('');
	}
	return cells;
}

function escapeCell(p_strInput)
{
	if (p_strInput == null) return '';
	return String(p_strInput).replace(/\|/g, '\\|');
}

When I run the preview, I then get the following preview that includes the styling.

When you run your own, check under tools for the Copy HTML option. You can use this to grab your HTML and see if it looks how you would expect it to.

For this example, it looks like this.

<html><head><meta charset="UTF-8"><style>

<pre><code>table
{
    border-collapse: collapse;
    margin: 25px 0;
    font-size: 0.9em;
    font-family: sans-serif;
    min-width: 400px;
    box-shadow: 0 0 20px rgba(0, 0, 0, 0.15);
}

thead tr 
{
    background-color: #009879;
    color: #ffffff;
    text-align: left;
}

th, td 
{
    padding: 12px 15px;
}

tbody tr 
{
    border-bottom: 1px solid #dddddd;
}

tbody tr:nth-of-type(even)
{
    background-color: #f3f3f3;
}

tbody tr:last-of-type 
{
    border-bottom: 2px solid #009879;
}

tbody tr.active-row 
{
    font-weight: bold;
    color: #009879;
}
</code></pre>

</style>

</head><body><h1>CSV Example</h1>

<table>
<colgroup>
<col>
<col>
<col>
</colgroup>

<thead>
<tr>
	<th> Fruit </th>
	<th> Colour </th>
	<th> Units </th>
</tr>
</thead>

<tbody>
<tr>
	<td> Apple </td>
	<td> Red </td>
	<td> 6 </td>
</tr>
<tr>
	<td> Banana </td>
	<td> Yellow </td>
	<td> 9 </td>
</tr>
<tr>
	<td> Passion Fruit </td>
	<td> Purple </td>
	<td> 2 </td>
</tr>
<tr>
	<td> Apple </td>
	<td> Green </td>
	<td> 7 </td>
</tr>
<tr>
	<td> Orange </td>
	<td> Orange </td>
	<td> 6 </td>
</tr>
</tbody>
</table>

<p><em>Table above correct as of 2025–09–20.</em></p></body></html>

Hopefully, that gives you enough step-by-step to figure out what is going on, but as I noted. I wouldn’t be surprised if you were getting extra paragraph tags in your style section.

For simplicity’s sake, you might prefer to set your Markdown as a template tag, then have a separate HTML Preview action step, rather than scripting it.

Then you can put your template content in that action step and not have to separately construct the full HTML.

To do so, in your script:

draft.setTemplateTag("markdown", myMarkdownString)

Then, in the HTML Preview action step, in the template, use [[markdown]] to insert that content (rather than the default %%[[draft]]%%

Thanks both sylumer and agiletortoise for very helpful suggestions. I do now have a working solution, which I will post later after tidying it up.

One question about setTemplateTag(). The documentation says “subsequent action steps” can use the new tag. Does this mean the tag is not available to processTemplate() during the action step which sets it?

It is available in that current (after it has bn set in the flow of the code) and subsequent steps within an action. That note is just highlighting that it will be available in later steps too.

You can of course verify this practically with a quick test utilising the template tag in the current and additional steps in an action.

My eventual solution was

function previewMarkdown(title, mkdContent) {
	let mkd = GitHubMarkdown.create();
	mkd.format = "html";
	let htmlContent = mkd.render(mkdContent);

	draft.setTemplateTag("THETITLE", title);
	draft.setTemplateTag("THECONTENT", htmlContent);
	let allHTML = draft.processTemplate("[[template|Preview-HTML-FL.txt]]");

	HTMLPreview.create().show(allHTML);
}

where Drafts/Library/Templates/Preview-HTML-FL.txt is similar to the default Preview HTML template but with my preferred style modifications and these two template calls:

<title>[[THETITLE]]</title>

<body>
[[THECONTENT]]
</body>

Thanks again for all the help!

1 Like