Open havok2063 opened 11 months ago
I tried installing with npm install [local_path]
. It seemed to install ok with "voila-embed-vuetify": "file:../../github_projects/voila-embed-vuetify"
added as a dependency in my project. But I get the following error when loading the component.
[plugin:vite:import-analysis] Failed to resolve import "vuetify/lib" from "../../github_projects/voila-embed-vuetify/src/JupyterWidgetEmbed.js". Does the file exist?
/Users/Brian/Work/github_projects/voila-embed-vuetify/src/JupyterWidgetEmbed.js:2:46
1 | import Vue from 'vue'; // eslint-disable-line import/no-unresolved
2 | import Vuetify, * as vuetifyComponents from 'vuetify/lib'; // eslint-disable-line import/no-unresolved
| ^
3 | import { addCompiler } from '@mariobuikhuizen/vue-compiler-addon/dist/vue-compiler-addon.esm';
4 | import { provideWidget, requestWidget } from './widgetLocator';
My component is
<template>
<div>
<jupyter-widget-embed
voila-url="http://localhost:14050"
notebook="jdaviz.ipynb"
mount-id="jdaviz"
:request-options=requestOptions
></jupyter-widget-embed>
</div>
</template>
<script lang="ts" setup>
import { JupyterWidgetEmbed } from 'voila-embed-vuetify'
let requestOptions = {"credentials": 'include'}
</script>
I tried redefining the component directly in my frontend. The page at least renders now and does not produce any errors in the console but the spinner spins forever and no content loads. I see Starting WebSocket: ws://localhost:14050/api/kernels/b8444461-72f9-471f-b0dc-84b4482d23ae
in the console. Any ideas on what could be going on?
The component JupyterWidgetEmbed
is defined as
<template>
<div v-if="renderFn">
<!-- Rendered through Vue's render function -->
</div>
<div v-else>
<!-- Default slot or loading indicator -->
<v-chip style="white-space: initial">
[{{ notebook }} - {{ mountId }}]
<v-progress-circular indeterminate></v-progress-circular>
</v-chip>
</div>
</template>
<script lang="ts" setup>
import { ref, Ref, onMounted } from 'vue'
// Declaring types for better TypeScript integration
interface RequestOptions {
// Define the structure of requestOptions
}
interface NotebookLoaded {
[key: string]: boolean;
}
// define which properties are passed in from the parent, i.e. ":xxx"
const props = defineProps<{
voilaUrl: String,
notebook: String,
mountId: String,
requestOptions: Object
}>()
const notebooksLoaded: Ref<NotebookLoaded> = ref({})
let voilaLoaded: Ref<boolean> = ref(false)
let elem = null
let renderFn = ref(null)
const widgetResolveFns = {}
const widgetPromises = {}
function keyFromMountPath(obj: object) {
return `${obj.voilaUrl}${obj.notebook}${obj.mountId}`;
}
function provideWidget(mountPath: string, widgetModel: object) {
const key = keyFromMountPath(mountPath);
if (widgetResolveFns[key]) {
widgetResolveFns[key](widgetModel);
} else {
widgetPromises[key] = Promise.resolve(widgetModel);
}
}
function requestWidget(mountPath: string) {
const key = keyFromMountPath(mountPath);
if (!widgetPromises[key]) {
widgetPromises[key] = new Promise((resolve) => { widgetResolveFns[key] = resolve; });
}
return widgetPromises[key];
}
function getWidgetManager(voila, kernel) {
/* voila >= 0.1.8 */
const context = {
saveState: {
connect: () => {
}
},
/* voila >= 0.2.8 */
sessionContext: {
session: {
kernel
},
kernelChanged: {
connect: () => {
}
},
statusChanged: {
connect: () => {
}
},
connectionStatusChanged: {
connect: () => {
}
},
},
};
const settings = {
saveState: false
};
const rendermime = new voila.RenderMimeRegistry({
initialFactories: voila.standardRendererFactories
});
return new voila.WidgetManager(context, rendermime, settings);
}
async function init(voilaUrl: string, notebook: string, requestOptions: RequestOptions) {
const requireJsPromise = loadRequireJs(voilaUrl);
addVoilaCss(voilaUrl);
const notebookKey = `${voilaUrl}${notebook}`;
if (notebooksLoaded.value[notebookKey]) {
return;
}
notebooksLoaded.value[notebookKey] = true;
const res = await fetch(`${voilaUrl}/voila/render/${notebook}`, requestOptions);
const json = await res.json();
await requireJsPromise;
if (!voilaLoaded.value) {
window.requirejs.config({
// Configuration...
baseUrl: `${voilaUrl}${json.baseUrl}voila`,
waitSeconds: 3000,
map: {
'*': {
'jupyter-vue': `${voilaUrl}/voila/nbextensions/jupyter-vue/nodeps.js`,
'jupyter-vuetify': `${voilaUrl}/voila/nbextensions/jupyter-vuetify/nodeps.js`,
},
},
});
const extensions = json.extensions
.filter((extension) => !extension.includes('jupyter-vue'))
.map((extension) => `${voilaUrl}${extension}`);
window.requirejs(extensions);
voilaLoaded.value = true;
}
window.requirejs(['static/voila'], (voila) => {
window.define('vue', [], () => Vue);
(async () => {
// Kernel and WidgetManager handling code...
const kernel = await voila.connectKernel(`${voilaUrl}${json.baseUrl}`, json.kernelId);
const widgetManager = getWidgetManager(voila, kernel);
await widgetManager._loadFromKernel();
Object.values(widgetManager._models)
.map(async (modelPromise) => {
const model = await modelPromise;
const meta = model.get('_metadata');
const mountId = meta && meta.mount_id;
if (mountId) {
provideWidget({ voilaUrl, notebook, mountId }, model);
}
});
})();
});
}
function loadRequireJs(voilaUrl: string): Promise<void> {
if (document.getElementById('tag-requirejs')) {
return Promise.resolve();
}
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = `${voilaUrl}/voila/static/require.min.js`;
script.id = 'tag-requirejs';
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
});
}
function addVoilaCss(voilaUrl: string): void {
if (!document.getElementById('tag-index.css')) {
const link = document.createElement('link');
link.href = `${voilaUrl}/voila/static/index.css`;
link.type = 'text/css';
link.rel = 'stylesheet';
link.id = 'tag-index.css';
document.head.appendChild(link);
}
if (!document.getElementById('tag-theme-light.css')) {
const link = document.createElement('link');
link.href = `${voilaUrl}/voila/static/theme-light.css`;
link.type = 'text/css';
link.rel = 'stylesheet';
link.id = 'tag-theme-light.css';
document.head.appendChild(link);
}
}
onMounted(() => {
init(props.voilaUrl, props.notebook, props.requestOptions)
const model = requestWidget(props)
console.log('model', model)
model
.then((model) => model.widget_manager.create_view(model))
.then((widgetView) => {
console.log('view', widgetView)
if (['VuetifyView', 'VuetifyTemplateView'].includes(widgetView.model.get('_view_name'))) {
renderFn.value = (createElement) => widgetView.vueRender(createElement)
} else {
// Handle other widget types
}
})
})
</script>
I think the requestWidget
promise is never resolving. Neither of the next .then
statements are being run. I'll keep poking around at it.
You will also need a version of ipyvue and ipyvuetify that's compatible wit Vue3 and Vuetify3. We started working on this in https://github.com/widgetti/ipyvue/pull/82 and https://github.com/widgetti/ipyvuetify/pull/283. Available as ipyvue==3.0.0a2 and ipyvuetify==3.0.0a2.
I haven't tested it yet with this project, it might need changes as well.
Ahh ok. After updating those dependencies, I see 404 errors in the console for the following:
GET http://localhost:14050/voila/vue.js
GET http://localhost:14050/voila/vuetify.js
and a Error: Script error for "vuetify", needed by: http://localhost:14050/voila/nbextensions/jupyter-vuetify/nodeps.js
So likely some things need updating in the init
function? In my voila conda env, I do see the right nodeps files, e..g share/jupyter/nbextensions/jupyter-vuetify/nodeps.js
. And this loads in my browser http://localhost:14050/voila/nbextensions/jupyter-vuetify/nodeps.js
. I'm not too familiar with how these dependencies need to be loaded into the front-end component.
Hi @mariobuikhuizen is it possible to release a version of this onto npm that is compatible with Vue 3. I'd like to use this for a new project. I tried bumping the dependencies to
which is what I'm using in the new project and
npm install
on the local repo, but I ran into issues. I'm also not sure what this dependency is,"@mariobuikhuizen/vue-compiler-addon": "^2.6.10-alpha.1"
and if we still need it?