Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 40 additions & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -534,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"
Expand Down
2 changes: 1 addition & 1 deletion packages/pytest-ipywidgets/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ dependencies = [
"playwright",
"pytest-playwright",
"pillow",
"pixelmatch",
"pixelmatch==0.3.0",
]

[project.urls]
Expand Down
6 changes: 5 additions & 1 deletion packages/solara-vuetify3-app/src/solara-vuetify-app.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
});

Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ packages = [
dependencies = [
"reacton>=1.9",
"ipywidgets>=7.7",
"ipyvuetify>=1.6.10",
"ipyvue>=1.9.0",
"ipyvue>=3.0.0a6",
"ipyvuetify>=3.0.0a4",
"requests",
"humanize",
]
Expand Down
37 changes: 24 additions & 13 deletions solara/components/file_list_widget.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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': '']"
>
<v-list-item-icon>
<v-icon>{{ name === '..' ? 'mdi-keyboard-backspace' : is_file ? 'mdi-file-document' : 'mdi-folder' }}</v-icon>
</v-list-item-icon>

<v-list-item-content>
<div class="solara-file-list-row">
<div class="solara-file-list-icon">
<v-icon>{{ name === '..' ? 'mdi-keyboard-backspace' : is_file ? 'mdi-file-document' : 'mdi-folder' }}</v-icon>
</div>
<v-list-item-title :class="'solara-file-list-' + (is_file ? 'file' : 'dir')">
{{ name }}<span v-if="size"> - {{ size }}</span>
</v-list-item-title>
</v-list-item-content>
</div>
</v-list-item>
</v-list>
</v-sheet>
Expand Down Expand Up @@ -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;
}
</style>
6 changes: 4 additions & 2 deletions solara/server/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -346,8 +346,10 @@ 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
# 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'<script type="module">/*\npath={path}\n*/{content_utf8}</script>'
Expand Down
44 changes: 40 additions & 4 deletions solara/server/static/main-vuetify.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,26 @@ var jupyterWidgetMountPoint = {
return {
renderFn: undefined,
elem: undefined,
component: undefined,
}
},
props: ['mount-id'],
created() {
requestWidget(this.mountId);
},
mounted() {
const vue3 = Vue.version.startsWith('3');
requestWidget(this.mountId)
.then(widgetView => {
if (['VuetifyView', 'VuetifyTemplateView'].includes(widgetView.model.get('_view_name'))) {
this.renderFn = createElement => widgetView.vueRender(createElement);
.then(async widgetView => {
const model = widgetView.model;
if (['VuetifyView', 'VuetifyTemplateView'].includes(model.get('_view_name'))) {
if (['VueTemplateModel', 'VuetifyTemplateModel'].includes(model.get('_model_name'))) {
await registerVueComponents(this, widgetView);
}
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);
Expand All @@ -31,6 +39,9 @@ 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 (!this.elem) {
Expand All @@ -43,6 +54,28 @@ var jupyterWidgetMountPoint = {
}
};

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 => {
if (!requirejs.defined(name) && !requirejs.specified(name)) {
resolve();
return;
}
requirejs([name], module => resolve(callback(module)), () => resolve());
});
}

const widgetResolveFns = {};
const widgetPromises = {};

Expand Down Expand Up @@ -115,6 +148,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()
Expand Down
Loading
Loading