Window-specific document script?

I am running into some sort of bug (presumably mine) when I run my jxa script which is intended to look at every taskpaper window open, and return a map of every file that’s open with a list of window information for that file

however, when i have multiple windows of the same filepath open, then it seems to always return duplicate data based on the last window that was focused, rather than one per individual window. so in a case where i have “hello.txt” open in two windows where one has project A focused, and project B on the other window, it will return an array with [projectBstuff, projectBstuff] for that filepath in the map.

it works fine when the filepaths are different. thoughts?

(() => {
  const TaskPaperApp = Application('TaskPaper');
  const windows = TaskPaperApp.windows();

  const filePathToContextNodeInfoMap = {};
  windows.filter(window => window.visible()).forEach(window => {
    const windowDocument = window.document();

    // all ancestors too
    const windowBounds = window.bounds();
    const windowData = {
      width: windowBounds?.width,
      height: windowBounds?.height,
      x: windowBounds?.x,
      y: windowBounds?.y,
    };
    const focusedItemProjectWithAncestors = windowDocument.evaluate({
      script: `(editor, options) => {
        let item = editor.focusedItem;
        let projects = [];
        while (item) {
          if (item.bodyString.trim().endsWith(':')) {
            projects.unshift({
              id: item.id,
              title: item.bodyString.split('\\n')[0],
              windowData: options.windowData
            });
          }
          item = item.parent;
        }
        return projects;
      }`,
      withOptions: { windowData },
    });

    if (focusedItemProjectWithAncestors.length > 0) {
      const filePath = windowDocument.file().toString();
      filePathToContextNodeInfoMap[filePath] = filePathToContextNodeInfoMap[filePath] || [];
      filePathToContextNodeInfoMap[filePath].push(focusedItemProjectWithAncestors);
    }
  });

  return JSON.stringify(filePathToContextNodeInfoMap);
})();

sample output

{
  "[redacted].txt": [
    [
      {
        "id": "XyQfF4UwmwJe",
        "title": "skin care planning:",
        "windowData": {
          "y": 28,
          "width": 748,
          "x": 748,
          "height": 469
        }
      }
    ],
    [
      {
        "id": "XyQfF4UwmwJe",
        "title": "skin care planning:",
        "windowData": {
          "y": 28,
          "width": 748,
          "x": 0,
          "height": 469
        }
      }
    ]
  ]
}

since the code explicitly runs the evaluation on the specific window, i’m not sure what the bug in my code is

i would guess that somehow taskpaper app resolves one editor object for each file hence the behavior exhibited here

Yes, I think you are collapsing your long list of windows down to the shorter list of documents.

You probably need to separate the two, and build a full list of windows which is subsequently decorated by ids and titles drawn from the smaller set of documents.

( .evaluate is a method on a document – not on a window, and there is no such thing as a “window-specific” document. Where a document has multiple windows, each window’s document property is a pointer back to the same document object)

1 Like

each window’s document property is a pointer back to the same document object

thanks for the confirmation.

You probably need to separate the two, and build a full list of windows which is subsequently decorated by ids and titles drawn from the smaller set of documents.

could you elucidate further how exactly i can get the focused item per window if they keep resolving to the same document (lets use an example where there are two windows that point to project A and the other to project B of the same file)?

You may find that you can define the list you want in terms of the [OutlineEditor] array returned by OutlineEditor.getOutlineEditors()

Expand disclosure triangle to view JS source
(() => {
    "use strict";

    // List of the focal lines, editor ids and outline ids
    // of each open Taskpaper window.


    // ---------- TASKPAPER EVALUATION CONTEXT -----------

    const tp3Context = () =>
        OutlineEditor.getOutlineEditors()
        .map(editor => {
            const
                outline = editor.outline,
                focusedItem = editor.focusedItem;

            return {
                editorId: editor.id,
                outlineId: outline.id,
                text: null !== focusedItem
                    ? focusedItem.body.string
                    : outline.items[0].body.string
            };
        });


    // ------------- JXA EVALUATION CONTEXT --------------

    // main :: IO ()
    const main = () => {
        const
            doc = Application("TaskPaper")
            .documents.at(0),
            script = `${tp3Context}`;

        return doc.exists()
            ? doc.evaluate({script})
            : "No document open in TaskPaper.";
    };

    // MAIN ---
    return JSON.stringify(
        main(),
        null, 2
    );
})();

how do you tie a specific outline editor to a specific window though?

btw this is returning 401 OutlineEditor · TaskPaper User's Guide

401

I’m not sure whether @jessegrosjean has any thoughts on that ?

(The following opens without issue here)

https://guide.taskpaper.com/reference/scripting/OutlineEditor.html

On JXA context windows and TaskPaper context editor instances – just to check that we are not diving into problems with an attempted solution, rather than looking at the core problem which you want to solve, could you give us the context of what you are trying to do more broadly – i.e. why you want that list and what you plan to do with the window bounds ?


Depending on the specifics of your context (the problem that you want to solve) it’s possible for example, that you may be able to make use of the OutlineEditor method .getRectForRange(location, length)

I made this post the other day: [demo] Attach metadata GUI to taskpaper app windows

it’s evolved since then (with help of some custom written swift software) so down to when I drag a taskpaper window around the gui attaches to the window in real time. it works for multiple windows of different files, but the problem is this system isn’t working for multiple windows of same file (but different projects) as they resolve to the same document

Understood – in that context I think .getRectForRange won’t help (the Rect is defined in relation to the origin of the editing window, rather than that of the display device)

There may be a way of mapping the ids of individual outlineEditor instances to corresponding system Window ids, but I haven’t seen one yet.

@jessegrosjean ?

That url seems to be working for me too. Though I think this one with the “guide” at the end of the URL is slightly more up-to-date:

https://www.taskpaper.com/guide

1 Like

I’ve just been digging through the code and I don’t think there’s a way to do this right now. I guess it would solve things for you if outline editor could return enclosing window id?

@purple Do you have time energy to dig into TaskPaper’s source code to add this? I’d be willing to pair program with you on this… would love to get others a little familiar with TaskPaper’s code and building the project.

1 Like

sure, sounds great. i took a look at your code. is there a way to discuss the peer programming meetup details over a more private thread?

1 Like

Yes, should be that you can just click on my icon above and you will see a popup with a “Message” button.