Bike 2.0 Preview Extensions API Questions

A minor question, and no hurry:

This is working as I would expect, and very useful – returning any standard utf8 text content of the clipboard:

const text = bike.clipboard.readText("public.utf8-plain-text");

console.log(text);

In build 2.0 229, however, I notice that if we omit the uti argument, then only an undefined value seems to be returned, even when the public.utf8-plain-text pasteboard is populated.

(From the API files, I think I expected that omitting the argument would result in default selection of the public.utf8-plain-text pasteboard item, if there is one)

Thanks, I’ll fix in next release. I was expecting Javascript to Swift bridge to pass me nil values when not defined… but instead was passing “undefined” string, and that’s the UTI type that I was using to read.

1 Like

What is the best way to check the contents of an AppExtensionContext ?

In an extension which has this in the manifest.json:

"permissions": ["clipboardRead", "clipboardWrite"]

(and in which the clipboard methods are working well)

I tried, probably clumsily, to see a result of this kind of thing in the Safari debug console:

export async function activate(context: AppExtensionContext) {
  console.log(
      "Extension Permissions:", 
       JSON.stringify( context.permissions )
  );

  // ...
}

but perhaps an async activate function doesn’t really get a chance to leave a string in the console ?

This is working for me:

import { AppExtensionContext } from 'bike/app'

export async function activate(context: AppExtensionContext) {
  console.log('Extension Permissions:', context.permissions)
}

Few notes…

  1. The logging also works if I JSON.stringify, but just gives an empty dictionary. The context.permissions is a native object that javascript doesn’t know how to stringify.

  2. I have tried this is the latest preview release, so the difference isn’t because I’m running from Xcode… I tested with the download version

  3. Two potential problems that I can think of … a) Maybe you opened the wrong context from the Safari > Debug menu? (I did on my first test). For example I opened the “bike” context, but my logging code was in “tutorial” context. b) Make sure you are not filtering the log, default log level is “notice” and you won’t see that if you are filtering to only show warnings… Should be that “All” is highlighted above the log.

I’m not sure why you would not see this in your log.

Many thanks – I had mistaken context.permissions for a JS dictionary.

I can see it in Window > Logs Explorer > Script Context


FWIW still not seeing it here in the Safari debugger console:

( I assume that the Logs Explorer message about the name module is not relevant here )

[error] ScriptContext: Thrown Exception: Optional(ReferenceError: Can't find variable: module)


(In any case, not in any sense an impediment to progress – just me stumbling around a bit as I experiment to get a sense of things )

Scratch that, forgive me – perhaps I should have opened the console a few more lines – it’s showing now.

1 Like

activate not fired till document opened from file ?

(Bike 2.0 Preview (235))

Extension keybindings seem to fire as predicted in documents opened from file, but I wonder if there is an edge case with newly created documents (not yet saved and reopened) ?

Is it possible that app extension activate events may be bypassed where a document has not been opened from file ?

With the following !Startup extension for example, I seem to have to save and reopen new documents before I can use the extension-defined keybindings (e.g. ⌘8 for toggle expand).

(No obstacle to use, and possibly just some error of mine in the extension code, or some documentation that I have overlooked)

!startup.bkext.zip (5.6 KB)

Glad you are testing this! :slight_smile:

I see the behavior too and am not sure yet on the cause, but it’s something in the keybindings system. For your own debugging, activate is actually getting called correctly, and you should see the commands show up and work from the command palette (Command-Shift-P).

I’ll get the keybindings issue fixed for next release.

1 Like

A quick question about the frontmostWindow property of the Document interface (I’m using Bike 2.0 Preview 240)

In the extension kit I see:

export interface Document {
  readonly fileURL?: URL
  readonly fileType: string
  readonly displayName: string
  readonly windows: Window[]
  readonly frontmostWindow?: Window
}

and I notice that in contexts where expressions like:

doc.windows[0]

successfully return a Window value, I am finding that

doc.frontmostWindow seems to return undefined

(the frontmostWindow property on the bike (rather than Document) object is returning a defined value)

For example, at the Safari console here:

Oops, looks like I never implemented that on Swift side. I’m just making a release this morning and I’ve added a fix for this. I also changed things so that document.windows will always return windows in front to back order.

1 Like

.runExtensionScript

A minimal example showing a slight puzzle with JS Object keys when passing arguments to, and returning values from, the very useful osascript method:

Application("Bike").runExtensionScript


A minimal example showing one key (but not all) replaced by a generic new key of the form k00n

(() => {
    "use strict";

    function run(argv) {

        return Object.fromEntries(argv);

    }

    // main :: IO ()
    const main = () =>
        Application("Bike").runExtensionScript(
            `${run}`,
            { options: [["Alpha", 9], ["Beta", 16]] }
        );


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

Result:

{
  "Beta": 16,
  "k001": 9
}

Or taking a slightly different approach, we obtain an unexpected usrf key, and a flattening of the expected Array of pairs:


(() => {
    "use strict";

    function run(argv) {

        return Object.entries(argv)

    }

    // main :: IO ()
    const main = () =>
        Application("Bike").runExtensionScript(
            `${run}`,
            { options: { "Alpha": 9, "Beta": 16 } }
        );


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

[
  [
    "usrf",
    [
      "Alpha",
      9,
      "Beta",
      16
    ]
  ]
]

A third example may shed some light on the unexpected .usrf key:

(() => {
    "use strict";

    function run(argv) {

        return argv.usrf

    }

    // main :: IO ()
    const main = () =>
        Application("Bike").runExtensionScript(
            `${run}`,
            { options: { "Alpha": 9, "Beta": 16 } }
        );


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

[
  "Alpha",
  9,
  "Beta",
  16
]