Hi @Christian_Byron and @nemo, I found this idea pretty neat and integrated it into the existing preview button (i.e. I cloned the button and hid the original button via the ControlPanel, to not overwrite any system tiddlers). This way, the button can be used to open and close the external window and changes icons depending on the current state. Also, depending on the chosen preview type, we can alternatively show e.g. the parse tree or the diff output in the external window. I find the parse and widget tree outputs especially benefit from the extra space.
Correspondingly, the thing consists of several tiddlers now:
The toolbar button:
button-classes: tc-text-editor-toolbar-item-start-group
caption: {{$:/language/Buttons/Preview/Caption}}
condition: [<targetTiddler>]
custom-icon: yes
description: {{$:/language/Buttons/Preview/Hint}}
icon: $:/core/images/preview-open
shortcuts: ((preview))
tags: $:/tags/EditorToolbar
title: $:/yaisog/ui/EditorToolbar/preview
type: text/vnd.tiddlywiki
\whitespace trim
<span>
<% if [{$:/state/editpreviewexternal}match[yes]] %>
<% if [window[EditorPreview]] %>
{{$:/core/images/preview-open}}
<% else %>
{{$:/core/images/preview-closed}}
<% endif %>
<% else %>
<$transclude $tiddler={{{ [<edit-preview-state>match[yes]then[$:/core/images/preview-open]else[$:/core/images/preview-closed]] }}} />
<% endif %>
</span>
<% if [{$:/state/editpreviewexternal}match[yes]] %>
<% if [window[EditorPreview]] %>
<$action-sendmessage $message="tm-close-window" $param="EditorPreview" />
<$action-deletetiddler $tiddler="$:/state/showeditpreview--external" /> <!-- not really necessary -->
<% else %>
<$action-sendmessage $message="tm-open-window"
$param="$:/yaisog/ui/EditorToolbar/preview-window"
template="$:/core/ui/ViewTemplate/body/default"
tv-history-list=<<tv-history-list>>
editPreviewStateTiddler="$:/state/showeditpreview--external"
windowID="EditorPreview"
windowTitle="Editor Preview"
width="1920" height="1080" />
<$action-setfield $tiddler="$:/state/showeditpreview--external" text="yes" />
<% endif %>
<% else %>
<$action-setfield $tiddler=<<editPreviewStateTiddler>> $value={{{ [<editPreviewStateTiddler>get[text]toggle[yes],[no]] }}} />
<% endif %>
<$action-sendmessage $message="tm-edit-text-operation" $param="focus-editor"/> <!-- does not focus original window when opening external window -->
I only pass the editPreviewStateTiddler
variable here because I use it in the ViewTemplateBodyFilters cascade to show a “plain” template for tiddler previews in edit mode (my default template is somewhat complex).
Then, there is the “button” for the type dropdown:
button-classes: tc-text-editor-toolbar-item-adjunct
caption: {{$:/language/Buttons/PreviewType/Caption}}
condition: [all[shadows+tiddlers]tag[$:/tags/EditPreview]!has[draft.of]butfirst[]limit[1]]
description: {{$:/language/Buttons/PreviewType/Hint}}
dropdown: $:/yaisog/ui/EditorToolbar/preview-type-dropdown
icon: $:/mwi/images-lucide/chevron-down
tags: $:/tags/EditorToolbar
title: $:/yaisog/ui/EditorToolbar/preview-type
type: text/vnd.tiddlywiki
This only points to this modified type dropdown:
title: $:/yaisog/ui/EditorToolbar/preview-type-dropdown
type: text/vnd.tiddlywiki
\procedure preview-type-button()
<$button tag="a">
<$action-setfield $tiddler="$:/state/editpreviewtype" $value=<<previewType>>/>
<$action-deletetiddler $tiddler=<<dropdown-state>> />
<$transclude tiddler=<<previewType>> field="caption" mode="inline">
<$view tiddler=<<previewType>> field="title" mode="inline"/>
</$transclude>
<$reveal tag="span" state="$:/state/editpreviewtype" type="match" text=<<previewType>> default="$:/core/ui/EditTemplate/body/preview/output">
<$entity entity=" "/>
<$entity entity="✓"/>
</$reveal>
</$button>
\end
<style>
.preview-type-dropdown-checkbox {
padding-inline: 1rem;
}
</style>
<$list filter="[all[shadows+tiddlers]tag[$:/tags/EditPreview]!has[draft.of]]" variable="previewType">
<<preview-type-button>>
</$list>
<span class="preview-type-dropdown-checkbox">
<$checkbox tiddler="$:/state/editpreviewexternal" field="text" checked="yes" unchecked="no" default="no"
checkactions="""<$action-setfield $tiddler=<<editPreviewStateTiddler>> $value="no" />"""
uncheckactions="""<$action-sendmessage $message="tm-close-window" $param="EditorPreview" />""" >
Open in external window
</$checkbox>
</span>
This dropdown adds the checkbox to choose between the regular preview and the external window preview:

Deselecting the checkbox will make previews work as usual.
The preview window contents:
tags:
title: $:/yaisog/ui/EditorToolbar/preview-window
type: text/vnd.tiddlywiki
\import [subfilter{$:/core/config/GlobalImportFilter}]
<% if [<tv-history-list>get[current-tiddler]get[draft.of]] %>
<$tiddler tiddler={{{ [<tv-history-list>get[current-tiddler]] }}} >
<$let tv-tiddler-preview="yes" >
<$transclude $tiddler={{$:/state/editpreviewtype}} mode="inline">
<$transclude $tiddler="$:/core/ui/EditTemplate/body/preview/output" />
</$transclude>
</$let>
</$tiddler>
<% else %>
Current tiddler is not in edit mode.
<% endif %>
The preview window can be closed by clicking the button again or by deselecting the checkbox.
To keep the button icon and actions correct even when closing the preview window by clicking its own close button, I had to write a custom filter function that checks for the existence of the preview window in TW’s window list:
/*\
title: $:/yaisog/modules/filters/window.js
type: application/javascript
module-type: filteroperator
\*/
"use strict";
/*
Export our filter function
*/
exports["window"] = function(source,operator,options) {
var results = [],
windowObj = null,
windowID = operator.operand;
if (typeof window !== 'undefined' && window.$tw && window.$tw.windows) {
// If no parameter is provided, return all window names
if (windowID === "") {
for (var key in window.$tw.windows) {
if (window.$tw.windows.hasOwnProperty(key)) {
var windowObj = window.$tw.windows[key];
if (windowObj) {
results.push(windowObj.name || "");
}
}
}
} else {
// Get specific window object by key
var windowObj = null;
if (window.$tw.windows.hasOwnProperty(windowID)) {
windowObj = window.$tw.windows[windowID];
}
if (windowObj) {
results.push(windowObj.name || "");
}
}
}
return results;
};
Don’t forget to reload the wiki after adding this JS tiddler.