Use case
My workflow usually involves two document windows side-by-side. The window on the right is for my notes. The left is for reading and reference materials, i.e. everything else. Both windows may contain many tabs. I prefer this setup because I don’t need to switch between, or rearrange windows.
My setup relies on the two following functions:
- When I open documents, they appear as tabs in the correct window: notes on the right, everything else on the left.
- When I, for some reason, have many document windows open, I can reorganize them into two windows with ease.
The following script is capable of both.
Script (JXA)
Click to expand
/* This script is intended to maintain a work arrangement of 2 document windows.
One type of documents open in window A; all other documents open in window B. You can change the criteria as you wish.
If the front DT window is a viewer window, selected documents will be opened.
If the front DT window is a document window, all document windows and their tabs will be rearranged.
You might want to experiment with the script to see what it does. */
/* Meowky 2024-08-31 */
function run () {
const app = Application('DEVONthink 3');
app.includeStandardAdditions = true;
const allDWs = app.documentWindows();
// Determine which window a document should open in.
function isNote (r) {
// Change criteria as you wish. The example is for markdown notes.
const criteria = r.type() === 'markdown' && r.database().name() === 'My Notes' && !r.name().startsWith('This is not a note!');
if (criteria) return true;
else return false;
}
// Designate two document windows; other documents will be (re)opened in these two.
// Windows with the most open tabs will be prioritized.
function autoSelectWindows (ws) {
let readingWindow, notesWindow;
// Loop through all document windows.
for (let i = 0; i < ws.length; i++) {
const w = ws[i];
const test = w.tabs().map(x => isNote(x.contentRecord()));
// Determine the "type" of a document window by comparing the number of open notes vs other document types.
if (test.filter(x => x).length < test.filter(x => !x).length) {
if (!readingWindow) readingWindow = w;
else if (readingWindow.tabs().length < w.tabs().length) readingWindow = w;
}
else {
if (!notesWindow) notesWindow = w;
else if (notesWindow.tabs().length < w.tabs().length) notesWindow = w;
}
}
// Return the two windows; values default to undefined.
return {'reading': readingWindow, 'notes': notesWindow};
}
const windows = autoSelectWindows(allDWs);
if (app.thinkWindows()[0].class() === 'viewerWindow' && app.selectedRecords()) {
// Front window is viewer window (that is, main window). Open selected records.
app.selectedRecords().forEach(record => {
// Ignore selected groups.
if (record.type() === 'group') return;
if (isNote(record)) {
if (windows.notes) {
app.openTabFor({record: record, in: windows.notes});
} else {
// Create a new document window for notes if there is no designated window for notes.
windows.notes = app.openTabFor({record: record}).thinkWindow();
}
} else {
if (windows.reading) {
app.openTabFor({record: record, in: windows.reading});
} else {
windows.reading = app.openTabFor({record: record}).thinkWindow();
}
}
})
} else {
// Front window is document window. Reorganize document windows.
// Garner all open tabs in all document windows. NOTE: tabs in viewer windows are untouched.
const allTabs = allDWs.map(x => x.tabs()).flat();
// Loop through the tabs.
allTabs.forEach(tab => {
const record = tab.contentRecord();
if (isNote(record)) {
if (!windows.notes) {
// Create new window for notes if there is no designated window.
app.close(tab, {saving: 'ask'});
windows.notes = app.openTabFor({record: record}).thinkWindow();
}
else if (tab.thinkWindow().id() !== windows.notes.id()) {
// Move the tab if a note is not opened in the designated window for notes.
app.close(tab, {saving: 'ask'});
app.openTabFor({record: record, in: windows.notes});
}
} else {
if (!windows.reading) {
// Create new window ...
app.close(tab, {saving: 'ask'});
windows.reading = app.openTabFor({record: record}).thinkWindow();
}
else if (tab.thinkWindow().id() !== windows.reading.id()) {
// Move ...
app.close(tab, {saving: 'ask'});
app.openTabFor({record: record, in: windows.reading});
}
}
})
}
}
Some additional comments:
- You will need to modify the
criteria
to fit your use case. Start with changing'My Notes'
to the name of your database. - This script manipulates the UI only, not the data. Its exact behavior depends on the
class
of the front DT window. - This script is quite different from the
Open In Two Windows
script shipped along with the software. More specifically, my script can handle more than two documents, and runs with or without selected documents. The other script serves an entirely different purpose. - Want to run the script with a keyboard shortcut? In case you don’t know yet, you can specify the keyboard shortcut in the filename of the script, for example:
MyScript___Shift+Cmd+Option+Control+U.scpt
There are alternative means as well.
Feedback is welcome!