ipyvuetify icon indicating copy to clipboard operation
ipyvuetify copied to clipboard

Theme initialisation according to JupyterLab or Voilà (lab)

Open guimillet opened this issue 4 years ago • 16 comments

Theme.dark is initialised as False in Themes.py and this value sets the vuetify theme in Themes.js.

When JupyterLab or Voilà is set to a dark theme, the ipyvuetify widgets are rendered by default in light theme, and one has to manually execute ipyvuetify.model.dark = True to change the ipyvuetify theme.

Could this be changed in Themes.js line 22 by adding

if (document.body.classList.contains('theme-dark') ||
    document.body.attributes.getNamedItem("data-jp-theme-light") &&
    document.body.attributes.getNamedItem('data-jp-theme-light').value=='false') {
    this.dark = true;
}

The first condition checks the use of a dark theme in a Voilà display (with the default lab template) and the second in JupyterLab display.

I tested it in a VuetifyTemplate and it worked.

guimillet avatar Mar 28 '21 22:03 guimillet

I love this idea ❤️

12rambau avatar Apr 05 '21 15:04 12rambau

This feature was implemented in v1.7.0 (9be42b9)

mariobuikhuizen avatar Aug 31 '21 10:08 mariobuikhuizen

Thanks for the implementation with respect to JupyterLab. What about adding the check for Voilà on line 39 of Theme.js?

guimillet avatar Sep 01 '21 10:09 guimillet

It should work when using Voila with e.g. --theme=dark (https://voila.readthedocs.io/en/stable/customize.html)

mariobuikhuizen avatar Sep 01 '21 11:09 mariobuikhuizen

I have tried with --theme=dark as well as with the URL query string ?voila-theme=dark, it didn't work. Does it work on your side? What source code is supposed to catch Voilà dark setting?

guimillet avatar Sep 02 '21 08:09 guimillet

You are right, I misremembered, I was using the VoilaVuetify template in which this is handled.

Would you want to make a PR for this? If not, I can make the change.

mariobuikhuizen avatar Sep 02 '21 09:09 mariobuikhuizen

Although it might work without that change in a future version of Voila: https://github.com/voila-dashboards/voila/pull/846

mariobuikhuizen avatar Sep 02 '21 11:09 mariobuikhuizen

I'll try to make a PR. I have an issue with building the extension. After modifying Themes.js, I run jlpm run prepare in the js folder, but webpack complains as follows:

ERROR in ./lib/Themes.js
Module not found: Error: Can't resolve './plugins/vuetify' in '[...]/src/ipyvuetify/js/lib'
@ ./lib/Themes.js 4:0-40 21:9-16 28:10-17 34:6-13 36:6-13 38:6-13 42:6-13 73:9-16 79:6-13 81:8-15
@ ./lib/nodepsEmbed.js

If I add .js to ./plugins/vuetify, the error disappears.

How do you usually rebuild ipyvuetify under development?

I had a look at Jupyterlab documentation and the cookiecutter-ts extension README, but ipyvuetify seems not to follow the same structure (package.json is in js folder and has no general build script).

guimillet avatar Sep 09 '21 21:09 guimillet

It's based on an older js cookiecutter and is also supporting the classic notebook. I'll have to take a look why it's not building.

mariobuikhuizen avatar Sep 13 '21 08:09 mariobuikhuizen

Running npm run prepare in the js directory works for me.

After running jupyter labextension develop . --overwrite in the top directory, then running npm run watch in the js directory also works for me.

mariobuikhuizen avatar Sep 13 '21 09:09 mariobuikhuizen

I still get an error with npm run prepare for two children of webpack, when it processes ./lib/nodeps.js and ./lib/nodepsEmbed.js:

Child
    Hash: da66faa312a62ab78080
    Time: 2942ms
    Built at: 14/09/2021 08:58:30
    Entrypoint main =
    [0] external "jupyter-vue" 42 bytes {0} [built]
    [1] external "@jupyter-widgets/base" 42 bytes {0} [built]
    [2] ./lib/public-path.js 390 bytes {0} [built]
    [3] ./lib/styles.css 1.06 KiB {0} [built]
    [4] ./node_modules/css-loader/dist/cjs.js!./lib/styles.css 858 bytes {0} [built]
    [8] ./package.json 2.36 KiB {0} [built]
    [9] ./lib/nodeps.js + 167 modules 150 KiB {0} [built]
        | ./lib/nodeps.js 54 bytes [built]
        | ./lib/nodepsEmbed.js 342 bytes [built]
        | ./src/nodepsVuetifyView.js 444 bytes [built]
        | ./lib/generated/index.js 7.76 KiB [built]
        | ./lib/Html.js 501 bytes [built]
        | ./lib/VuetifyTemplate.js 531 bytes [built]
        | ./lib/Themes.js 2.59 KiB [built]
        | ./lib/generated/VuetifyWidget.js 530 bytes [built]
        | ./lib/generated/Text.js 394 bytes [built]
        | ./lib/generated/App.js 430 bytes [built]
        | ./lib/generated/AppBar.js 1.33 KiB [built]
        | ./lib/generated/AppBarNavIcon.js 414 bytes [built]
        | ./lib/generated/Alert.js 1020 bytes [built]
        | ./lib/generated/Autocomplete.js 2.17 KiB [built]
        | ./lib/generated/Avatar.js 665 bytes [built]
        |     + 153 hidden modules
        + 3 hidden modules
    
    ERROR in ./lib/Themes.js
    Module not found: Error: Can't resolve './plugins/vuetify' in '/home/guillaume/src/ipyvuetify/js/lib'
     @ ./lib/Themes.js 4:0-40 21:9-16 28:10-17 34:6-13 36:6-13 38:6-13 42:6-13 73:9-16 79:6-13 81:8-15
     @ ./lib/nodepsEmbed.js
     @ ./lib/nodeps.js
    
    ERROR in chunk main [entry]
    nodeps.js
    /home/guillaume/src/ipyvuetify/js/lib/nodeps.js cfbd2241da5a323bdbb1837bc399228b
    Assigning to rvalue (29:10)
    |       ThemeModel.themeManager.themeChanged.connect(() => {
    |         if (this.get('dark') === null) {
    |           !(function webpackMissingModule() { var e = new Error("Cannot find module './plugins/vuetify'"); e.code = 'MODULE_NOT_FOUND'; throw e; }()).framework.theme.dark = document.body.dataset.jpThemeLight === 'false';
    |         }
    |       }, this);
Child
    Hash: a46f58f9ab21d5221240
    Time: 1825ms
    Built at: 14/09/2021 08:58:29
    Entrypoint main =
    [0] external "jupyter-vue" 42 bytes {0} [built]
    [1] external "@jupyter-widgets/base" 42 bytes {0} [built]
    [2] ./lib/styles.css 1.06 KiB {0} [built]
    [3] ./node_modules/css-loader/dist/cjs.js!./lib/styles.css 858 bytes {0} [built]
    [7] ./package.json 2.36 KiB {0} [built]
    [8] ./lib/nodepsEmbed.js + 166 modules 150 KiB {0} [built]
        | ./lib/nodepsEmbed.js 342 bytes [built]
        | ./src/nodepsVuetifyView.js 444 bytes [built]
        | ./lib/generated/index.js 7.76 KiB [built]
        | ./lib/Html.js 501 bytes [built]
        | ./lib/VuetifyTemplate.js 531 bytes [built]
        | ./lib/Themes.js 2.59 KiB [built]
        | ./lib/generated/VuetifyWidget.js 530 bytes [built]
        | ./lib/generated/Text.js 394 bytes [built]
        | ./lib/generated/App.js 430 bytes [built]
        | ./lib/generated/AppBar.js 1.33 KiB [built]
        | ./lib/generated/AppBarNavIcon.js 414 bytes [built]
        | ./lib/generated/Alert.js 1020 bytes [built]
        | ./lib/generated/Autocomplete.js 2.17 KiB [built]
        | ./lib/generated/Avatar.js 665 bytes [built]
        | ./lib/generated/Badge.js 824 bytes [built]
        |     + 152 hidden modules
        + 3 hidden modules
    
    ERROR in ./lib/Themes.js
    Module not found: Error: Can't resolve './plugins/vuetify' in '/home/guillaume/src/ipyvuetify/js/lib'
     @ ./lib/Themes.js 4:0-40 21:9-16 28:10-17 34:6-13 36:6-13 38:6-13 42:6-13 73:9-16 79:6-13 81:8-15
     @ ./lib/nodepsEmbed.js
    
    ERROR in chunk main [entry]
    nodeps.js
    /home/guillaume/src/ipyvuetify/js/lib/nodepsEmbed.js 216d9b2e91b735de5c7349321e4700f4
    Assigning to rvalue (29:10)
    |       ThemeModel.themeManager.themeChanged.connect(() => {
    |         if (this.get('dark') === null) {
    |           !(function webpackMissingModule() { var e = new Error("Cannot find module './plugins/vuetify'"); e.code = 'MODULE_NOT_FOUND'; throw e; }()).framework.theme.dark = document.body.dataset.jpThemeLight === 'false';
    |         }
    |       }, this);

No issue with ./lib/notebook.js and ./lib/embed.js.

The log file from npm for build:webpack reads:

0 verbose cli [
0 verbose cli   '/usr/bin/node',
0 verbose cli   '/usr/share/nodejs/npm/bin/npm-cli.js',
0 verbose cli   'run',
0 verbose cli   'build:webpack'
0 verbose cli ]
1 info using [email protected]
2 info using [email protected]
3 timing config:load:defaults Completed in 2ms
4 timing config:load:file:/usr/share/nodejs/npm/npmrc Completed in 2ms
5 timing config:load:builtin Completed in 2ms
6 timing config:load:cli Completed in 1ms
7 timing config:load:env Completed in 1ms
8 timing config:load:file:/home/guillaume/src/ipyvuetify/js/.npmrc Completed in 1ms
9 timing config:load:project Completed in 1ms
10 timing config:load:file:/home/guillaume/.npmrc Completed in 0ms
11 timing config:load:user Completed in 0ms
12 timing config:load:file:/etc/npmrc Completed in 0ms
13 timing config:load:global Completed in 0ms
14 timing config:load:cafile Completed in 0ms
15 timing config:load:validate Completed in 1ms
16 timing config:load:setUserAgent Completed in 0ms
17 timing config:load:setEnvs Completed in 1ms
18 timing config:load Completed in 9ms
19 verbose npm-session f2e98852e7934577
20 timing npm:load Completed in 17ms
21 timing command:run-script Completed in 7166ms
22 verbose stack Error: command failed
22 verbose stack     at ChildProcess.<anonymous> (/usr/share/nodejs/@npmcli/promise-spawn/index.js:64:27)
22 verbose stack     at ChildProcess.emit (events.js:314:20)
22 verbose stack     at maybeClose (internal/child_process.js:1022:16)
22 verbose stack     at Process.ChildProcess._handle.onexit (internal/child_process.js:287:5)
23 verbose pkgid [email protected]
24 verbose cwd /home/guillaume/src/ipyvuetify/js
25 verbose Linux 5.10.0-8-amd64
26 verbose argv "/usr/bin/node" "/usr/share/nodejs/npm/bin/npm-cli.js" "run" "build:webpack"
27 verbose node v12.21.0
28 verbose npm  v7.5.2
29 error code 2
30 error path /home/guillaume/src/ipyvuetify/js
31 error command failed
32 error command sh -c webpack
33 verbose exit 2

Could it come from an incompatible version of one of the compilers? npm list answers:

[email protected] /home/guillaume/src/ipyvuetify/js
├── @babel/[email protected]
├── @babel/[email protected]
├── @babel/[email protected]
├── @jupyter-widgets/[email protected]
├── @jupyterlab/[email protected]
├── @jupyterlab/[email protected]
├── @mariobuikhuizen/[email protected]
├── @mdi/[email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
└── [email protected]

guimillet avatar Sep 14 '21 07:09 guimillet

Can you confirm that after running npm run build:babel lib/plugins/vuetify.js is present?

My dependencies are indeed different, you should have the same after resetting package-lock.json (if it has changed) and running npm install

├── @babel/[email protected]
├── @babel/[email protected]
├── @babel/[email protected]
├── @jupyter-widgets/[email protected]
├── @jupyterlab/[email protected]
├── @jupyterlab/[email protected]
├── @mariobuikhuizen/[email protected]
├── @mdi/[email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
└── [email protected]

mariobuikhuizen avatar Sep 14 '21 09:09 mariobuikhuizen

Yes, lib/plugins/vuetify.js is present. Resetting package-lock.json (changed by pip install I guess?) didn't help.

I gave a try with a fresh clone of ipvuetify, as follows:

git clone https://github.com/mariobuikhuizen/ipyvuetify.git mario_ipyvuetify
cd mario_ipyvuetify/
pip install -e .
jupyter labextension develop . --overwrite
cd js
npm run prepare

npm ends with the same error as before.

guimillet avatar Sep 14 '21 12:09 guimillet

Finally, I found the cause: the file https://github.com/mariobuikhuizen/ipyvuetify/blob/master/js/src/plugins/noDepsVuetify.js has the letter D in capital whereas the webpack config file defines the alias 'src/plugins/nodepsVuetify.js'. Renaming js/src/plugins/noDepsVuetify.js to js/src/plugins/nodepsVuetify.js solved the issue.

Running npm run prepare in the js directory works for me.

@mariobuikhuizen Did you run it on macOS?

I am using Linux. Apparently, webpack behaves differently between macOS and Linux with respect to case sensitivity: https://stackoverflow.com/questions/28789050/webpack-fine-on-macos-loader-errors-on-linux It may be good to use a Webpack plugin like case-sensitive-paths-webpack-plugin to notive such issues in the future.

guimillet avatar Oct 28 '21 21:10 guimillet

Currently, after displaying a widget in Jupyterlab, v.theme.dark remains None, and if v.theme.dark is set, then the widget theme remains that of v.theme.dark whatever the Jupyterlab or Voila lab/vuetify dark/light theme, so the None value corresponds to an 'inherit' behavior. I thought it would be useful to add a dark_jlab trait to Themes to know the inherited theme on the python side, but eventually I don't see any clear need for that. Do you think it would be useful? If so, I can add it to the PR.

guimillet avatar Oct 28 '21 23:10 guimillet

Great find! I indeed run it on macOS, which has a case-insensitive file system.

It might be useful to have dark_jlab trait, but I can't think of a scenario in which one would need it yet.

mariobuikhuizen avatar Oct 31 '21 12:10 mariobuikhuizen