Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create tree plugin #1374

Merged
merged 11 commits into from Aug 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 5 additions & 4 deletions .github/workflows/main.yml
Expand Up @@ -84,7 +84,8 @@ jobs:

- name: Create the conda environment
shell: bash -l {0}
run: mamba install -q python=${{ matrix.python_version }} pip jupyterlab_pygments==0.1.0 pytest-cov pytest-rerunfailures nodejs=18 yarn ipywidgets matplotlib xeus-cling "traitlets>=5.0.3,<6" ipykernel
# TODO unpin pyzmq
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the issue with pyzmq?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 25.1.1 is not available as wheel yet on Mac OS

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah ok, maybe mentioning this in the comment could be useful for later when we get back to it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant next to the TODO, or in a new issue.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

run: mamba install -q python=${{ matrix.python_version }} pip jupyterlab_pygments==0.1.0 pytest-cov pytest-rerunfailures nodejs=18 yarn ipywidgets matplotlib xeus-cling "traitlets>=5.0.3,<6" ipykernel pyzmq==25.1.0

- name: Install dependencies
shell: bash -l {0}
Expand Down Expand Up @@ -136,6 +137,6 @@ jobs:
- name: Run test
run: |
set VOILA_TEST_DEBUG=1
py.test tests/app --async-test-timeout=240 --reruns 2 --reruns-delay 1
py.test tests/server --async-test-timeout=240 --reruns 2 --reruns-delay 1 --trace
py.test tests/execute_output_test.py
py.test tests/app --async-test-timeout=240 --reruns 2 --reruns-delay 1 -x
py.test tests/server --async-test-timeout=240 --reruns 2 --reruns-delay 1 --trace -x
py.test tests/execute_output_test.py -x
2 changes: 1 addition & 1 deletion packages/voila/package.json
Expand Up @@ -22,7 +22,7 @@
"@jupyterlab/mainmenu": "^4.0.0",
"@jupyterlab/markdownviewer-extension": "^4.0.0",
"@jupyterlab/markedparser-extension": "^4.0.0",
"@jupyterlab/mathjax2-extension": "^4.0.0-alpha.21",
"@jupyterlab/mathjax2-extension": "^4.0.0",
"@jupyterlab/nbformat": "^4.0.0",
"@jupyterlab/notebook": "^4.0.0",
"@jupyterlab/outputarea": "^4.0.0",
Expand Down
15 changes: 15 additions & 0 deletions packages/voila/src/plugins/tree/browser.ts
@@ -0,0 +1,15 @@
import { FileBrowser, DirListing } from '@jupyterlab/filebrowser';
import { VoilaDirListing } from './listing';

export class VoilaFileBrowser extends FileBrowser {
/**
* Create the underlying DirListing instance.
*
* @param options - The DirListing constructor options.
*
* @returns The created DirListing instance.
*/
protected createDirListing(options: DirListing.IOptions): DirListing {
return new VoilaDirListing(options);
}
}
62 changes: 62 additions & 0 deletions packages/voila/src/plugins/tree/index.ts
@@ -0,0 +1,62 @@
/***************************************************************************
* Copyright (c) 2023, Voilà contributors *
* Copyright (c) 2023, QuantStack *
* *
* Distributed under the terms of the BSD 3-Clause License. *
* *
* The full license is in the file LICENSE, distributed with this software. *
****************************************************************************/
import {
JupyterFrontEnd,
JupyterFrontEndPlugin
} from '@jupyterlab/application';
import { DocumentManager } from '@jupyterlab/docmanager';
import { DocumentRegistry } from '@jupyterlab/docregistry';
import { FilterFileBrowserModel } from '@jupyterlab/filebrowser';

import { VoilaFileBrowser } from './browser';
import { Widget } from '@lumino/widgets';

/**
* The voila file browser provider.
*/
export const treeWidgetPlugin: JupyterFrontEndPlugin<void> = {
id: '@voila-dashboards/voila:tree-widget',
description: 'Provides the file browser.',
activate: (app: JupyterFrontEnd): void => {
const docRegistry = new DocumentRegistry();
const docManager = new DocumentManager({
registry: docRegistry,
manager: app.serviceManager,
opener
});
const fbModel = new FilterFileBrowserModel({
manager: docManager,
refreshInterval: 2147483646
});
const fb = new VoilaFileBrowser({
id: 'filebrowser',
model: fbModel
});

fb.addClass('voila-FileBrowser');
fb.showFileCheckboxes = false;
fb.showLastModifiedColumn = false;

const title = new Widget();
title.node.innerText = 'Select items to open with Voilà.';
fb.toolbar.addItem('title', title);

const spacerTop = new Widget();
spacerTop.addClass('spacer-top-widget');
app.shell.add(spacerTop, 'main');

app.shell.add(fb, 'main');

const spacerBottom = new Widget();
spacerBottom.addClass('spacer-bottom-widget');
app.shell.add(spacerBottom, 'main');
},

autoStart: true
};
33 changes: 33 additions & 0 deletions packages/voila/src/plugins/tree/listing.ts
@@ -0,0 +1,33 @@
import { DirListing } from '@jupyterlab/filebrowser';
import { Contents } from '@jupyterlab/services';
import { showErrorMessage } from '@jupyterlab/apputils';
import { PageConfig, URLExt } from '@jupyterlab/coreutils';

export class VoilaDirListing extends DirListing {
/**
* Handle the opening of an item.
*/
protected handleOpen(item: Contents.IModel): void {
if (item.type === 'directory') {
const localPath = this.model.manager.services.contents.localPath(
item.path
);
this.model
.cd(`/${localPath}`)
.catch((error) => showErrorMessage('Open directory', error));
} else {
const path = item.path;
const baseUrl = PageConfig.getBaseUrl();
const frontend = PageConfig.getOption('frontend');
const query = PageConfig.getOption('query');
const url = URLExt.join(baseUrl, frontend, 'render', path) + `?${query}`;
window.open(url, '_blank');
}
}

handleEvent(event: Event): void {
if (event.type === 'click') {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this.evtDblClick(event as MouseEvent);
}
}
}
4 changes: 3 additions & 1 deletion packages/voila/src/services/servicemanager.ts
Expand Up @@ -2,6 +2,7 @@ import { ServiceManager } from '@jupyterlab/services';
import { VoilaEventManager } from './event';
import { VoilaUserManager } from './user';
import { VoilaKernelSpecManager } from './kernelspec';
import { ContentsManager } from '@jupyterlab/services';

const alwaysTrue = () => true;

Expand All @@ -18,7 +19,8 @@ export class VoilaServiceManager extends ServiceManager {
standby: options?.standby ?? alwaysTrue,
kernelspecs: options?.kernelspecs ?? new VoilaKernelSpecManager({}),
events: options?.events ?? new VoilaEventManager(),
user: options?.user ?? new VoilaUserManager({})
user: options?.user ?? new VoilaUserManager({}),
contents: options?.contents ?? new ContentsManager()
});
}
}
24 changes: 16 additions & 8 deletions packages/voila/src/tree.ts
Expand Up @@ -8,19 +8,24 @@
* Copyright (c) Jupyter Development Team. *
* Distributed under the terms of the Modified BSD License. *
****************************************************************************/
import '../style/index.js';
import '@jupyterlab/filebrowser/style/index.js';

import { PageConfig, URLExt } from '@jupyterlab/coreutils';
import { ContentsManager, Drive } from '@jupyterlab/services';

import { VoilaApp } from './app';
import { treeWidgetPlugin } from './plugins/tree';
import { VoilaServiceManager } from './services/servicemanager';
import { VoilaShell } from './shell';
import { activePlugins, createModule, loadComponent } from './tools';
import {
pathsPlugin,
themePlugin,
themesManagerPlugin,
translatorPlugin
translatorPlugin,
widgetManager
} from './voilaplugins';
import { VoilaServiceManager } from './services/servicemanager';
import { VoilaShell } from './shell';
import { activePlugins, createModule, loadComponent } from './tools';

const disabled = [
'@jupyter-widgets/jupyterlab-manager:plugin',
Expand All @@ -37,10 +42,13 @@ async function main() {
const mods = [
require('@jupyterlab/theme-light-extension'),
require('@jupyterlab/theme-dark-extension'),
require('@jupyterlab/rendermime-extension'),
pathsPlugin,
translatorPlugin,
themePlugin,
themesManagerPlugin
widgetManager,
themesManagerPlugin,
treeWidgetPlugin
];

const mimeExtensions: any[] = [];
Expand Down Expand Up @@ -69,7 +77,6 @@ async function main() {

extensions.forEach((p) => {
if (p.status === 'rejected') {
// There was an error loading the component
console.error(p.reason);
return;
}
Expand Down Expand Up @@ -122,11 +129,12 @@ async function main() {
.forEach((p) => {
console.error((p as PromiseRejectedResult).reason);
});

const drive = new Drive({ apiEndpoint: 'voila/api/contents' });
const cm = new ContentsManager({ defaultDrive: drive });
const app = new VoilaApp({
mimeExtensions,
shell: new VoilaShell(),
serviceManager: new VoilaServiceManager()
serviceManager: new VoilaServiceManager({ contents: cm })
});
app.registerPluginModules(mods);
app.started.then(() => {
Expand Down
28 changes: 28 additions & 0 deletions packages/voila/style/base.css
Expand Up @@ -3,6 +3,10 @@ body {
}
div#main {
height: 100vh;
background-color: var(--jp-layout-color2);
}
div#rendered_cells {
background-color: var(--jp-layout-color1);
}
div#voila-top-panel {
min-height: var(--jp-private-menubar-height);
Expand All @@ -16,3 +20,27 @@ div#rendered_cells {
padding: var(--jp-notebook-padding);
overflow: auto;
}

.voila-FileBrowser {
max-width: 1000px;
box-shadow: var(--jp-elevation-z4);
}

.voila-FileBrowser .jp-DirListing-item {
border-bottom-style: solid;
border-bottom-width: var(--jp-border-width);
border-bottom-color: var(--jp-border-color0);
padding: 10px 12px;
}

.voila-FileBrowser .jp-DirListing-itemText:focus {
outline-style: none;
}

.spacer-top-widget {
max-height: 50px;
}

.spacer-bottom-widget {
max-height: 50px;
}
84 changes: 84 additions & 0 deletions share/jupyter/voila/templates/lab/tree-lab.html
@@ -0,0 +1,84 @@
{% extends "page.html" %}

{% block title %}{{ page_title }}{% endblock %}

{% block stylesheets %}
{{ super() }}

<style>
body {
background-color: var(--jp-layout-color0);
}

.list-header {
width: 80%;
margin-top: 50px;
margin-left: auto;
margin-right: auto;
padding: 0px;
border-style: solid;
border-width: var(--jp-border-width);
border-color: var(--jp-border-color2);
border-bottom: none;
background-color: var(--jp-layout-color2)
}

.list-header-text {
color: var(--jp-ui-font-color0);
font-size: var(--jp-ui-font-size1);
padding: 10px
}

.voila-notebooks {
background-color: var(--jp-layout-color1);
width: 80%;
margin: auto;
padding: 0px;
border-style: solid;
border-width: var(--jp-border-width);
border-color: var(--jp-border-color0);
border-radius: var(--jp-border-radius);
}

.voila-notebooks > li {
color: var(--jp-ui-font-color1);
list-style: none;
border-bottom-style: solid;
border-bottom-width: var(--jp-border-width);
border-bottom-color: var(--jp-border-color0);
}

.voila-notebooks > li:hover {
background-color: var(--jp-layout-color2);
}

.voila-notebooks > li:last-child {
border: none
}

.voila-notebooks > li > a {
display: block;
width: 100%;
height: 100%;
padding: 10px;
}

.voila-notebooks > li > a > i {
padding: 0 10px
}
</style>
{% endblock %}

{% block body %}

{% set openInNewTab = 'target=_blank' %}
<script id="jupyter-config-data" type="application/json">
{{ page_config | tojson }}
</script>

<script src="{{ page_config['fullStaticUrl'] | e }}/treepage.js"></script>
{% set mainStyle = 'style="display: None;"' %}

<div id="voila-tree-main" {{mainStyle | safe}}>

{% endblock %}
11 changes: 1 addition & 10 deletions share/jupyter/voila/templates/lab/tree.html
Expand Up @@ -75,16 +75,8 @@
<script id="jupyter-config-data" type="application/json">
{{ page_config | tojson }}
</script>
{% if (theme != 'dark' and theme != 'light') %}
<script src="{{ page_config['fullStaticUrl'] | e }}/treepage.js"></script>
<div id="voila-tree-main" style="display: None;">
{% set mainStyle = 'style="display: None;"' %}
{% else %}
{% set mainStyle = '' %}
{% endif %}


<div id="voila-tree-main" {{mainStyle}}>
<div id="voila-tree-main" {{mainStyle | safe}}>
<div class="list-header">
<div class="list-header-text">
Select items to open with {{ "Voilà" if frontend == "voila" else frontend.capitalize() }}.
Expand All @@ -105,5 +97,4 @@
{% endif %}
{% endfor %}
</ul>
</div>
{% endblock %}