Add entry to "Open With" only for certain files

Hi, I would like to add an entry to the “Open With” menu of the filebrowser, similar to the “Markdown Preview”. It seems I can achive this with a mime renderer. However, I don’t want to show it on all files of a specific extension, but only on certain ones.

The reason is that I am creating a preview widget for Streamlit apps. Streamlit apps are plain .py files. If you use jupyter-containds, you can run a the streamlit app from within JupyterLab. I basically want to treat all the files that are known to jupyter-containds specially.

Is there a way to register a new mimetype like text/x-python+streamlit that doesn’t just depend on the extension? Alternatively, I think I could just add a command to the outer context menu, and check in isVisible if the user clicked on a streamlit file, but that seems the wrong place to put this. Any ideas?

It seems you can also match by pattern. I just tried it and it works, however it doesn’t compare the full path but only the filename, so you can’t use it to hook individual files. Also, it would be hard to make this dynamic as the user adds new streamlit apps.

const ft: DocumentRegistry.IFileType = {
    name: 'streamlit',
    contentType: 'file',
    fileFormat: 'text',
    extensions: ['.py'],
    pattern: 'untitled.*',
    // ...
};
app.docRegistry.addFileType(ft);

I think I have a solution now. In your extension, you need to request the IFileBrowserFactory. We do this in order to get the tracker of the file browser instance, which tells us which files are selected:

export const someExtension: JupyterFrontEndPlugin<void> = {
  id: '@organisation/some:plugin',
  autoStart: true,
  requires: [JupyterFrontEnd.IPaths, INotebookTracker],
  optional: [ICommandPalette, IMainMenu, ILauncher,
             IFileBrowserFactory],  // <--
  activate: (
    app: JupyterFrontEnd,
    paths: JupyterFrontEnd.IPaths,
    notebooks: INotebookTracker,
    palette: ICommandPalette | null,
    menu: IMainMenu | null,
    launcher: ILauncher | null,
    browser: IFileBrowserFactory | null,  // <--
  ) => {
   // ...
  }
}

Then when adding your command to the context menu, use a custom isVisible function:

function selectionAllowsPreview() {
    const widget = browser?.tracker.currentWidget;
    if (!widget) return false;

    // put your custom critera here
    function good(item: Contents.IModel) {
        return item.path.startsWith("Desktop/")
    }

    const items = toArray(widget.selectedItems());
    return items.length > 0 && items.every(good);
}

commands.addCommand('myextension:test', {
    label: 'Run Dashboard',
    caption: 'Test',
    execute: (_) => {
       console.log("Would execute the command here");
    },
    isVisible: selectionAllowsPreview,
    icon: runIcon,
});
app.contextMenu.addItem({
    command: 'myextension:test',
    selector: '.jp-DirListing-item[data-isdir="false"]',
    rank: 2
});

This is basically the solution suggested here, but it was a bit tricky to figure out where to get the tracker from. Hope it is helpful for somebody else!

1 Like