From 5221c5caf0b3f4f9f35b0c18cc7d558a30af8ff8 Mon Sep 17 00:00:00 2001 From: Mario Buikhuizen Date: Fri, 6 Mar 2026 15:57:14 +0100 Subject: [PATCH 01/12] export VuetifyPlugin --- solara/server/static/main-vuetify.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/solara/server/static/main-vuetify.js b/solara/server/static/main-vuetify.js index d4bce7148..4ee292be2 100644 --- a/solara/server/static/main-vuetify.js +++ b/solara/server/static/main-vuetify.js @@ -115,6 +115,9 @@ async function solaraInit(mountId, appName) { console.log('solara init', mountId, appName); define("vue", [], () => Vue); define("vuetify", [], () => Vuetify); + if (typeof vuetifyPlugin !== "undefined") { + define("solara-vuetify-plugin", [], () => ({ vuetifyPlugin })); + } cookies = getCookiesMap(document.cookie); const searchParams = new URLSearchParams(window.location.search); let kernelId = searchParams.get('kernelid') || generateUuid() From 8cac2ffad8c223a66c15b58d9f98c12957f2c9e0 Mon Sep 17 00:00:00 2001 From: Mario Buikhuizen Date: Fri, 6 Mar 2026 16:47:40 +0100 Subject: [PATCH 02/12] publish on github releases --- .github/workflows/test.yaml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 501c1cf59..593ec64e3 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -732,6 +732,7 @@ jobs: runs-on: ubuntu-latest permissions: id-token: write # this permission is mandatory for trusted publishing + contents: write steps: - uses: actions/checkout@v6 @@ -766,6 +767,21 @@ jobs: - name: Test import solara-enterprise run: python -c "import solara_enterprise" + - name: Publish wheels to GitHub Release (vue3-dev) + if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository + env: + GH_TOKEN: ${{ github.token }} + run: | + tag="vue3-dev" + gh release view "$tag" --repo "${{ github.repository }}" >/dev/null 2>&1 || gh release create "$tag" --repo "${{ github.repository }}" --title "$tag" --notes "Auto-updated Vue 3 dev builds" --prerelease --target "${{ github.sha }}" + gh release upload "$tag" \ + dist/*.whl \ + packages/solara-meta/dist/*.whl \ + packages/solara-server/dist/*.whl \ + packages/pytest-ipywidgets/dist/*.whl \ + --repo "${{ github.repository }}" \ + --clobber + - name: Publish solara-meta to PyPI if: startsWith(github.event.ref, 'refs/tags/v') env: From ccc8b25de6269453d4ff2bcfe6f746cbb37b126f Mon Sep 17 00:00:00 2001 From: Mario Buikhuizen Date: Tue, 10 Mar 2026 10:10:16 +0100 Subject: [PATCH 03/12] use object storage for dev release --- .github/workflows/test.yaml | 55 ++++++++++++++++++++++++++----------- pyproject.toml | 4 +-- 2 files changed, 41 insertions(+), 18 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 593ec64e3..86f29ce61 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -107,6 +107,45 @@ jobs: packages/solara-vuetify-app/dist packages/solara-vuetify3-app/dist + - name: Publish dev wheels to object storage + if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository + env: + AWS_ACCESS_KEY_ID: ${{ secrets.S3_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.S3_SECRET_ACCESS_KEY }} + AWS_REGION: nbg1 + AWS_ENDPOINT_URL_S3: https://nbg1.your-objectstorage.com + S3_BUCKET: ipyvue3-packages + run: | + python -m pip install --upgrade awscli + + aws s3 cp dist/ "s3://${S3_BUCKET}/packages/solara/" \ + --recursive \ + --exclude "*" \ + --include "*.whl" \ + --endpoint-url "$AWS_ENDPOINT_URL_S3" \ + --region "$AWS_REGION" + + aws s3 cp packages/solara-meta/dist/ "s3://${S3_BUCKET}/packages/solara-meta/" \ + --recursive \ + --exclude "*" \ + --include "*.whl" \ + --endpoint-url "$AWS_ENDPOINT_URL_S3" \ + --region "$AWS_REGION" + + aws s3 cp packages/solara-server/dist/ "s3://${S3_BUCKET}/packages/solara-server/" \ + --recursive \ + --exclude "*" \ + --include "*.whl" \ + --endpoint-url "$AWS_ENDPOINT_URL_S3" \ + --region "$AWS_REGION" + + aws s3 cp packages/pytest-ipywidgets/dist/ "s3://${S3_BUCKET}/packages/pytest-ipywidgets/" \ + --recursive \ + --exclude "*" \ + --include "*.whl" \ + --endpoint-url "$AWS_ENDPOINT_URL_S3" \ + --region "$AWS_REGION" + code-quality: runs-on: ubuntu-latest strategy: @@ -732,7 +771,6 @@ jobs: runs-on: ubuntu-latest permissions: id-token: write # this permission is mandatory for trusted publishing - contents: write steps: - uses: actions/checkout@v6 @@ -767,21 +805,6 @@ jobs: - name: Test import solara-enterprise run: python -c "import solara_enterprise" - - name: Publish wheels to GitHub Release (vue3-dev) - if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository - env: - GH_TOKEN: ${{ github.token }} - run: | - tag="vue3-dev" - gh release view "$tag" --repo "${{ github.repository }}" >/dev/null 2>&1 || gh release create "$tag" --repo "${{ github.repository }}" --title "$tag" --notes "Auto-updated Vue 3 dev builds" --prerelease --target "${{ github.sha }}" - gh release upload "$tag" \ - dist/*.whl \ - packages/solara-meta/dist/*.whl \ - packages/solara-server/dist/*.whl \ - packages/pytest-ipywidgets/dist/*.whl \ - --repo "${{ github.repository }}" \ - --clobber - - name: Publish solara-meta to PyPI if: startsWith(github.event.ref, 'refs/tags/v') env: diff --git a/pyproject.toml b/pyproject.toml index 5636b9ddd..acccd17bb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,8 +17,8 @@ packages = [ dependencies = [ "reacton>=1.9", "ipywidgets>=7.7", - "ipyvuetify>=1.6.10", - "ipyvue>=1.9.0", +# "ipyvuetify>=1.6.10", +# "ipyvue>=1.9.0", "requests", "humanize", ] From 362198f7a9c0637ef9fad786692cc8a837b02e89 Mon Sep 17 00:00:00 2001 From: Mario Buikhuizen Date: Tue, 10 Mar 2026 12:40:41 +0100 Subject: [PATCH 04/12] fix CI issue with pixelmatch --- packages/pytest-ipywidgets/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pytest-ipywidgets/pyproject.toml b/packages/pytest-ipywidgets/pyproject.toml index 815f3f67d..24dae56a5 100644 --- a/packages/pytest-ipywidgets/pyproject.toml +++ b/packages/pytest-ipywidgets/pyproject.toml @@ -19,7 +19,7 @@ dependencies = [ "playwright", "pytest-playwright", "pillow", - "pixelmatch", + "pixelmatch==0.3.0", ] [project.urls] From 94cc491c999a2a252bb231c79664cd9ff9439705 Mon Sep 17 00:00:00 2001 From: Mario Buikhuizen Date: Wed, 22 Apr 2026 12:49:19 +0200 Subject: [PATCH 05/12] Fix FileBrowser list item alignment --- solara/components/file_list_widget.vue | 37 +++++++++++++++++--------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/solara/components/file_list_widget.vue b/solara/components/file_list_widget.vue index 47c430102..c932a63a9 100644 --- a/solara/components/file_list_widget.vue +++ b/solara/components/file_list_widget.vue @@ -10,17 +10,16 @@ :key="name + '|' + is_file" @click.stop="clicked = { name, is_file }" @dblclick="double_clicked = { name, is_file }" - :class="(clicked && clicked.name == name) ? 'solara-file-list-selected': ''" + :class="['solara-file-list-item', (clicked && clicked.name == name) ? 'solara-file-list-selected': '']" > - - {{ name === '..' ? 'mdi-keyboard-backspace' : is_file ? 'mdi-file-document' : 'mdi-folder' }} - - - +
+
+ {{ name === '..' ? 'mdi-keyboard-backspace' : is_file ? 'mdi-file-document' : 'mdi-folder' }} +
{{ name }} - {{ size }} - +
@@ -60,19 +59,31 @@ module.exports = { } -.solara-file-list .v-list-item__icon, -.solara-file-list .v-list-item__list { - margin-top: 0; - margin-bottom: 0; +.solara-file-list .solara-file-list-row { + align-items: center; + display: flex; + min-height: 28px; + width: 100%; } -.v-application--is-ltr .solara-file-list .v-list-item__icon { +.solara-file-list .solara-file-list-icon { + align-items: center; + display: flex; + flex: 0 0 32px; + justify-content: center; margin-right: 8px; } +.solara-file-list .solara-file-list-item.v-list-item, .solara-file-list .v-list-item { height: 28px; min-height: 0; - padding-left: 0; + padding: 0; + padding-inline-end: 0; + padding-inline-start: 0; +} + +.solara-file-list .v-list-item-title { + line-height: 28px; } From 0092d527afec1b4e4e3d902a8091378618356996 Mon Sep 17 00:00:00 2001 From: Mario Buikhuizen Date: Thu, 14 May 2026 15:21:00 +0200 Subject: [PATCH 06/12] fix: make jupyter-widget tag and full-vue-component work --- solara/server/static/main-vuetify.js | 22 ++- .../popout_external_widget_test.py | 186 ++++++++++++++++++ 2 files changed, 206 insertions(+), 2 deletions(-) create mode 100644 tests/integration/popout_external_widget_test.py diff --git a/solara/server/static/main-vuetify.js b/solara/server/static/main-vuetify.js index 4ee292be2..1f3890965 100644 --- a/solara/server/static/main-vuetify.js +++ b/solara/server/static/main-vuetify.js @@ -11,10 +11,10 @@ var jupyterWidgetMountPoint = { requestWidget(this.mountId); }, mounted() { - const vue3 = Vue.version.startsWith('3'); requestWidget(this.mountId) - .then(widgetView => { + .then(async widgetView => { if (['VuetifyView', 'VuetifyTemplateView'].includes(widgetView.model.get('_view_name'))) { + await registerVueComponents(this, widgetView.model.widget_manager); this.renderFn = createElement => widgetView.vueRender(createElement); } else { while (this.$el.firstChild) { @@ -43,6 +43,24 @@ var jupyterWidgetMountPoint = { } }; +async function registerVueComponents(vueComponent, widgetManager) { + return new Promise(resolve => { + const warn = () => { + console.warn('jupyter-vue unavailable'); + resolve(); + }; + if (!requirejs.defined('jupyter-vue') && !requirejs.specified('jupyter-vue')) { + warn(); + return; + } + requirejs( + ['jupyter-vue'], + jupyterVue => resolve(jupyterVue.addApp(vueComponent.$.appContext.app, widgetManager)), + warn + ); + }); +} + const widgetResolveFns = {}; const widgetPromises = {}; diff --git a/tests/integration/popout_external_widget_test.py b/tests/integration/popout_external_widget_test.py new file mode 100644 index 000000000..c657128b6 --- /dev/null +++ b/tests/integration/popout_external_widget_test.py @@ -0,0 +1,186 @@ +from pathlib import Path +from typing import TYPE_CHECKING, cast + +import ipyvue +import ipywidgets as widgets +import traitlets +from reacton.core import _RenderContext + +import solara +from solara.server import kernel_context + +if TYPE_CHECKING: + import playwright.sync_api + +HERE = Path(__file__).parent + + +ipyvue.register_component_from_string( + "external-split-widget", + """ + + + + + + """, +) + + +ipyvue.register_component_from_string( + "full-vue-only-widget", + """ + + + + """, +) + + +class ExternalLibraryWidget(ipyvue.VueTemplate): + template = """ + + """ + + child = traitlets.Any().tag(sync=True, **widgets.widget_serialization) + + +class JupyterWidgetOnly(ipyvue.VueTemplate): + template = """ + + """ + + child = traitlets.Any().tag(sync=True, **widgets.widget_serialization) + + +class FullVueOnlyWidget(ipyvue.VueTemplate): + template = """ + + """ + + +@solara.component +def ExternalWidgetApp(): + child = solara.use_memo(lambda: widgets.Button(description="Nested widget works"), []) + return ExternalLibraryWidget.element(child=child) + + +@solara.component +def JupyterWidgetOnlyApp(): + child = solara.use_memo(lambda: widgets.Button(description="Plain jupyter-widget works"), []) + return JupyterWidgetOnly.element(child=child) + + +@solara.component +def FullVueOnlyApp(): + return FullVueOnlyWidget.element() + + +external_widget_app = ExternalWidgetApp() +jupyter_widget_only_app = JupyterWidgetOnlyApp() +full_vue_only_app = FullVueOnlyApp() + + +def test_popout_external_library_jupyter_widget(page_session: "playwright.sync_api.Page", solara_server, solara_app, extra_include_path): + with extra_include_path(HERE), solara_app("popout_external_widget_test:ExternalWidgetApp"): + page_session.goto(solara_server.base_url + "?solara-no-close-beacon") + page_session.locator(".external-split-widget").wait_for() + page_session.locator("text=Nested widget works").wait_for() + + contexts = list(kernel_context.contexts.values()) + assert len(contexts) == 1 + context = contexts[0] + kernel_id = context.id + rc = cast(_RenderContext, context.app_object) + widget = rc.find(ExternalLibraryWidget).widget + model_id = widget._model_id + + page_session.goto(solara_server.base_url + f"?kernelid={kernel_id}&modelid={model_id}") + page_session.locator(".external-split-widget").wait_for() + page_session.locator("text=External Vue library pane").wait_for() + page_session.locator("text=Nested widget works").wait_for() + + +def test_popout_jupyter_widget_only(page_session: "playwright.sync_api.Page", solara_server, solara_app, extra_include_path): + with extra_include_path(HERE), solara_app("popout_external_widget_test:JupyterWidgetOnlyApp"): + page_session.goto(solara_server.base_url + "?solara-no-close-beacon") + page_session.locator(".jupyter-widget-only").wait_for() + page_session.locator("text=Plain jupyter-widget works").wait_for() + + contexts = list(kernel_context.contexts.values()) + assert len(contexts) == 1 + context = contexts[0] + kernel_id = context.id + rc = cast(_RenderContext, context.app_object) + widget = rc.find(JupyterWidgetOnly).widget + model_id = widget._model_id + + page_session.goto(solara_server.base_url + f"?kernelid={kernel_id}&modelid={model_id}") + page_session.locator(".jupyter-widget-only").wait_for() + page_session.locator("text=Plain jupyter-widget works").wait_for() + + +def test_popout_full_vue_only(page_session: "playwright.sync_api.Page", solara_server, solara_app, extra_include_path): + with extra_include_path(HERE), solara_app("popout_external_widget_test:FullVueOnlyApp"): + page_session.goto(solara_server.base_url + "?solara-no-close-beacon") + page_session.locator(".full-vue-only-widget").wait_for() + page_session.locator("text=Full Vue component works").wait_for() + + contexts = list(kernel_context.contexts.values()) + assert len(contexts) == 1 + context = contexts[0] + kernel_id = context.id + rc = cast(_RenderContext, context.app_object) + widget = rc.find(FullVueOnlyWidget).widget + model_id = widget._model_id + + page_session.goto(solara_server.base_url + f"?kernelid={kernel_id}&modelid={model_id}") + page_session.locator(".full-vue-only-widget").wait_for() + page_session.locator("text=Full Vue component works").wait_for() From 501e2ff16e432b61a2a808e8dc301510cec501de Mon Sep 17 00:00:00 2001 From: Mario Buikhuizen Date: Thu, 14 May 2026 17:59:23 +0200 Subject: [PATCH 07/12] fix: use addApp from jupyter-vuetify to register datatable --- solara/server/server.py | 4 ++-- solara/server/static/main-vuetify.js | 33 +++++++++++++++++----------- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/solara/server/server.py b/solara/server/server.py index 4f586f809..0398e2042 100644 --- a/solara/server/server.py +++ b/solara/server/server.py @@ -346,8 +346,8 @@ def include_js(path: str, module=False) -> Markup: content, hash = solara.util.get_file_hash(filepath) content_utf8 = content.decode("utf-8") url = f"{root_path}{path}?v={hash}" - # when < 10k we embed, but if we use currentScript, it can break things - embed = len(content) < 1024 * 10 and b"currentScript" not in content + # when < 20k we embed, but if we use currentScript, it can break things + embed = len(content) < 1024 * 20 and b"currentScript" not in content if embed: if module: code = f'' diff --git a/solara/server/static/main-vuetify.js b/solara/server/static/main-vuetify.js index 1f3890965..4707893aa 100644 --- a/solara/server/static/main-vuetify.js +++ b/solara/server/static/main-vuetify.js @@ -13,8 +13,11 @@ var jupyterWidgetMountPoint = { mounted() { requestWidget(this.mountId) .then(async widgetView => { - if (['VuetifyView', 'VuetifyTemplateView'].includes(widgetView.model.get('_view_name'))) { - await registerVueComponents(this, widgetView.model.widget_manager); + const model = widgetView.model; + if (['VuetifyView', 'VuetifyTemplateView'].includes(model.get('_view_name'))) { + if (['VueTemplateModel', 'VuetifyTemplateModel'].includes(model.get('_model_name'))) { + await registerVueComponents(this, widgetView); + } this.renderFn = createElement => widgetView.vueRender(createElement); } else { while (this.$el.firstChild) { @@ -43,21 +46,25 @@ var jupyterWidgetMountPoint = { } }; -async function registerVueComponents(vueComponent, widgetManager) { +async function registerVueComponents(vueComponent, widgetView) { + const app = vueComponent.$.appContext.app; + await loadWidgetModule('jupyter-vue', jupyterVue => + jupyterVue.addApp(app, widgetView.model.widget_manager) + ); + if (widgetView.model.get('_view_module') === 'jupyter-vuetify') { + await loadWidgetModule('jupyter-vuetify', jupyterVuetify => + jupyterVuetify.addApp(app) + ); + } +} + +function loadWidgetModule(name, callback) { return new Promise(resolve => { - const warn = () => { - console.warn('jupyter-vue unavailable'); + if (!requirejs.defined(name) && !requirejs.specified(name)) { resolve(); - }; - if (!requirejs.defined('jupyter-vue') && !requirejs.specified('jupyter-vue')) { - warn(); return; } - requirejs( - ['jupyter-vue'], - jupyterVue => resolve(jupyterVue.addApp(vueComponent.$.appContext.app, widgetManager)), - warn - ); + requirejs([name], module => resolve(callback(module)), () => resolve()); }); } From 996171245e217b61bed8f0283e3d7110adb39569 Mon Sep 17 00:00:00 2001 From: Mario Buikhuizen Date: Thu, 14 May 2026 19:33:45 +0200 Subject: [PATCH 08/12] fix: events not working Avoid caching and reusing the vnode returned by widgetView.vueRender() when rendering Vue 3 widgets in the Solara popout wrapper. Vue 3 expects render functions to return fresh vnode instances. Reusing the same vnode can leave event listeners and child component state stale, which broke interactions such as table checkbox updates in popped-out ipyvuetify templates. Cache the returned component type instead, and create a fresh vnode from that stable type on each render. This preserves component identity without remounting the full widget tree on every update. --- solara/server/server.py | 4 +++- solara/server/static/main-vuetify.js | 7 +++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/solara/server/server.py b/solara/server/server.py index 0398e2042..bcb071017 100644 --- a/solara/server/server.py +++ b/solara/server/server.py @@ -347,7 +347,9 @@ def include_js(path: str, module=False) -> Markup: content_utf8 = content.decode("utf-8") url = f"{root_path}{path}?v={hash}" # when < 20k we embed, but if we use currentScript, it can break things - embed = len(content) < 1024 * 20 and b"currentScript" not in content + # main-vuetify.js must be embedded on the Jupyter server extension page, + # where /solara/static/main-vuetify.js is not a valid static asset URL. + embed = (len(content) < 1024 * 20 or path == "/static/main-vuetify.js") and b"currentScript" not in content if embed: if module: code = f'' diff --git a/solara/server/static/main-vuetify.js b/solara/server/static/main-vuetify.js index 4707893aa..4c30a2698 100644 --- a/solara/server/static/main-vuetify.js +++ b/solara/server/static/main-vuetify.js @@ -4,6 +4,7 @@ var jupyterWidgetMountPoint = { return { renderFn: undefined, elem: undefined, + component: undefined, } }, props: ['mount-id'], @@ -36,6 +37,12 @@ var jupyterWidgetMountPoint = { let h = Vue.h || createElement; if (this.renderFn) { /* workaround for v-menu click */ + if (Vue.h) { + if (!this.component) { + this.component = this.renderFn(createElement).type; + } + return h(this.component); + } if (!this.elem) { this.elem = this.renderFn(createElement); } From a24c4bc3bae167a89532bddfec48479d1e040bd6 Mon Sep 17 00:00:00 2001 From: Mario Buikhuizen Date: Mon, 18 May 2026 14:25:56 +0200 Subject: [PATCH 09/12] fix: Mount Vue 3 widget views as stable components Use vueComponent() for Vue 3 Vuetify widget views instead of calling vueRender() and reusing the returned VNode. Rendering a stable component keeps the widget view inside Vue's normal component lifecycle and avoids the event instability seen in Solara popouts. --- solara/server/static/main-vuetify.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/solara/server/static/main-vuetify.js b/solara/server/static/main-vuetify.js index 4c30a2698..40bcc0a8c 100644 --- a/solara/server/static/main-vuetify.js +++ b/solara/server/static/main-vuetify.js @@ -19,7 +19,11 @@ var jupyterWidgetMountPoint = { if (['VueTemplateModel', 'VuetifyTemplateModel'].includes(model.get('_model_name'))) { await registerVueComponents(this, widgetView); } - this.renderFn = createElement => widgetView.vueRender(createElement); + if (Vue.h) { + this.component = widgetView.vueComponent(); + } else { + this.renderFn = createElement => widgetView.vueRender(createElement); + } } else { while (this.$el.firstChild) { this.$el.removeChild(this.$el.firstChild); @@ -35,14 +39,11 @@ var jupyterWidgetMountPoint = { render(createElement) { // in vue3 we have Vue.h, otherwise fall back to createElement (vue2) let h = Vue.h || createElement; + if (this.component) { + return h(this.component); + } if (this.renderFn) { /* workaround for v-menu click */ - if (Vue.h) { - if (!this.component) { - this.component = this.renderFn(createElement).type; - } - return h(this.component); - } if (!this.elem) { this.elem = this.renderFn(createElement); } From bdef45399cb9697b85ff5f4613a814b79fbee051 Mon Sep 17 00:00:00 2001 From: Mario Buikhuizen Date: Tue, 2 Jun 2026 15:09:07 +0200 Subject: [PATCH 10/12] fix: import all lab components --- packages/solara-vuetify3-app/src/solara-vuetify-app.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/solara-vuetify3-app/src/solara-vuetify-app.js b/packages/solara-vuetify3-app/src/solara-vuetify-app.js index 96a8ce35f..b6e733670 100644 --- a/packages/solara-vuetify3-app/src/solara-vuetify-app.js +++ b/packages/solara-vuetify3-app/src/solara-vuetify-app.js @@ -3,10 +3,14 @@ import * as Vuetify from 'vuetify'; import 'vuetify/dist/vuetify.min.css'; import * as components from 'vuetify/components'; +import * as labComponents from 'vuetify/labs/components'; import * as directives from 'vuetify/directives'; const vuetifyPlugin = Vuetify.createVuetify({ - components, + components: { + ...components, + ...labComponents, + }, directives, }); From baf506d4cc3f8761dd99abdbc4f330650438087a Mon Sep 17 00:00:00 2001 From: Mario Buikhuizen Date: Tue, 2 Jun 2026 17:12:46 +0200 Subject: [PATCH 11/12] ci: add ipyvuetify version --- pyproject.toml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index acccd17bb..be8f44847 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,8 +17,7 @@ packages = [ dependencies = [ "reacton>=1.9", "ipywidgets>=7.7", -# "ipyvuetify>=1.6.10", -# "ipyvue>=1.9.0", + "ipyvuetify @ https://nbg1.your-objectstorage.com/ipyvue3-packages/packages/ipyvuetify/ipyvuetify-3.0.0a3-py2.py3-none-any.whl", "requests", "humanize", ] @@ -33,6 +32,10 @@ Documentation = "https://solara.dev" [tool.hatch.version] path = "solara/__init__.py" +[tool.hatch.metadata] +# TODO: remove line below when "ipyvuetify>=3" is available on PyPI. +allow-direct-references = true + # we ignore build/ but on the website we add solara/website/build # when using SSG [tool.hatch.build] From c57eab47d91bcb6b1d2e3e5ee70c9dab106af586 Mon Sep 17 00:00:00 2001 From: Mario Buikhuizen Date: Fri, 19 Jun 2026 15:26:37 +0200 Subject: [PATCH 12/12] Use PyPI ipyvuetify alpha --- .github/workflows/test.yaml | 2 +- pyproject.toml | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 86f29ce61..b09441068 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -573,7 +573,7 @@ jobs: pip install "jupyterlab<4" "playwright==1.50.0" "pydantic<2" "ipywidgets~=${{ matrix.ipywidgets }}" pip install pytest-retry pip install jupyter_core jupyter-packaging - pip install --pre "ipyvue>=3" "ipyvuetify>=3" + pip install --pre "ipyvuetify>=3.0.0a4" pip freeze --exclude solara --exclude solara-ui --exclude solara-server --exclude pytest-ipywidgets --exclude solara-enterprise > ${{ env.LOCK_FILE_LOCATION }} git diff | tee ${{ env.DIFF_FILE_LOCATION }} [ -s ${{ env.DIFF_FILE_LOCATION }} ] && echo "HAS_DIFF=true" >> "$GITHUB_OUTPUT" || echo "No dependencies changed" diff --git a/pyproject.toml b/pyproject.toml index be8f44847..34f8aedd2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,7 +17,8 @@ packages = [ dependencies = [ "reacton>=1.9", "ipywidgets>=7.7", - "ipyvuetify @ https://nbg1.your-objectstorage.com/ipyvue3-packages/packages/ipyvuetify/ipyvuetify-3.0.0a3-py2.py3-none-any.whl", + "ipyvue>=3.0.0a6", + "ipyvuetify>=3.0.0a4", "requests", "humanize", ] @@ -32,10 +33,6 @@ Documentation = "https://solara.dev" [tool.hatch.version] path = "solara/__init__.py" -[tool.hatch.metadata] -# TODO: remove line below when "ipyvuetify>=3" is available on PyPI. -allow-direct-references = true - # we ignore build/ but on the website we add solara/website/build # when using SSG [tool.hatch.build]