Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
8 changes: 6 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,11 @@ ui-tests/test-results/
ui-tests/playwright-report/

examples/*.ipynb
# Hatchling
# Hatchling
yjs_widgets/_version.py

.yarn/
.yarn/

# Generated code and extension
examples/simple-yjs-widget/simple_yjs_widget/_version.py
examples/simple-yjs-widget/simple_yjs_widget/labextension/
140 changes: 123 additions & 17 deletions examples/simple-yjs-widget/notebooks/simple.ipynb

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

self.ydoc["state"] = self._state = Map()

I think pycrdt considers self.ydoc["_attrs"] to be candidate for synchronization, no?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I am not sure I get what you mean?

Original file line number Diff line number Diff line change
Expand Up @@ -2,56 +2,162 @@
"cells": [
{
"cell_type": "code",
"execution_count": null,
"execution_count": 1,
"id": "2a245ed3-5838-4be6-ad5e-9fa75b9c7ab0",
"metadata": {},
"outputs": [],
"source": [
"from ypywidgets import Widget, reactive"
"from ypywidgets.comm import CommWidget\n",
"from pycrdt import Map"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "18d1ca71-1943-4902-b642-06e3d812e23f",
"execution_count": 2,
"id": "6d9538c9-4858-4c9d-b347-18965660115d",
"metadata": {},
"outputs": [],
"source": [
"class MyWidget(Widget):\n",
" foo = reactive(\"\")\n",
" bar = reactive(\"\")"
"class MySlider(CommWidget):\n",
" def __init__(self):\n",
" super().__init__(\n",
" comm_metadata={\n",
" \"ymodel_name\": \"MySlider\",\n",
" \"create_ydoc\": True,\n",
" }\n",
" )\n",
"\n",
" self.ydoc[\"state\"] = self._state = Map()\n",
"\n",
" @property\n",
" def value(self):\n",
" return self._state['value']\n",
"\n",
" @value.setter\n",
" def value(self, v):\n",
" self._state['value'] = v\n",
"\n",
" @property\n",
" def min(self):\n",
" return self._state.get('min')\n",
"\n",
" @min.setter\n",
" def min(self, v):\n",
" self._state['min'] = v\n",
"\n",
" @property\n",
" def max(self):\n",
" return self._state.get('max')\n",
"\n",
" @max.setter\n",
" def max(self, v):\n",
" self._state['max'] = v\n",
"\n",
" @property\n",
" def step(self):\n",
" return self._state.get('step')\n",
"\n",
" @step.setter\n",
" def step(self, v):\n",
" self._state['step'] = v"
]
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 3,
"id": "8cfb218c-e6fe-48da-a6ee-599bfe524e15",
"metadata": {},
"outputs": [],
"outputs": [
{
"data": {
"application/vnd.jupyter.ywidget-view+json": {
"model_id": "4a2524e43c7244d9b93aeb95383cdc85",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"<__main__.MySlider object at 0x7fe44436af90>"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"w = MyWidget()\n",
"w = MySlider()\n",
"w"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "72a7799e-ce21-43ed-865c-77fa1fc69eb2",
"execution_count": 4,
"id": "9269a3f1-a061-478f-8728-7393a4aec2d0",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"50.0"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"w.value"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "8ac7ea5c-8315-44eb-aa62-f223f86d267d",
"metadata": {},
"outputs": [],
"source": [
"w.foo = 3"
"w.value = 98"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d7289dc9-29c1-4422-8962-1b013bfda40a",
"execution_count": 6,
"id": "bd2c92fb-9653-4de5-8a20-d463205f3a9e",
"metadata": {},
"outputs": [],
"source": [
"w.bar = 4"
"w.value = 36"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "3a5680c6-77ae-4f71-a982-b3447dcb4fd0",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"36.0"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"w.value"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "839e129b-4040-4293-9287-abe4bfd5491d",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
Expand All @@ -70,7 +176,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.3"
"version": "3.13.12"
}
},
"nbformat": 4,
Expand Down
10 changes: 8 additions & 2 deletions examples/simple-yjs-widget/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
"yjs-widgets": "^0.3.3"
},
"devDependencies": {
"@jupyterlab/builder": "~4.0.0",
"@jupyterlab/builder": "^4",
"@types/node": "^16.11.10",
"@typescript-eslint/eslint-plugin": "^4.8.1",
"@typescript-eslint/parser": "^4.8.1",
Expand All @@ -81,6 +81,12 @@
"jupyterlab": {
"extension": true,
"outputDir": "simple_yjs_widget/labextension",
"webpackConfig": "./extension.webpack.config.js"
"webpackConfig": "./extension.webpack.config.js",
"sharedPackages": {
"yjs-widgets": {
"singleton": true,
"bundled": false
}
}
}
}
74 changes: 43 additions & 31 deletions examples/simple-yjs-widget/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,54 +1,66 @@
import * as YjsWidgets from 'yjs-widgets';
import {
IJupyterYModel,
JupyterYModel,
IJupyterYWidgetManager
} from 'yjs-widgets';

import * as Y from 'yjs';

import {
JupyterFrontEnd,
JupyterFrontEndPlugin
} from '@jupyterlab/application';

class MyWidget {
constructor(yModel: YjsWidgets.IJupyterYModel, node: HTMLElement) {
class MySlider {
constructor(yModel: IJupyterYModel, node: HTMLElement) {
this.yModel = yModel;
this.node = node;
this.yModel.sharedModel.setAttr('foo', '');
this.yModel.sharedModel.setAttr('bar', '');
yModel.sharedModel.attrsChanged.connect(() => {
this._attrsChanged();
});
setInterval(() => {
this._changeAttrs();
}, 1000);
this._changeAttrs();

this.state = this.yModel.sharedModel.ydoc.getMap('state');

this.state.observe(this._stateChanged.bind(this));

this.slider = document.createElement('input');
this.slider.setAttribute('type', 'range');

this.state.set('min', 0);
this.state.set('max', 100);
this.state.set('value', 50);
Comment thread
martinRenou marked this conversation as resolved.
Outdated
this.state.set('step', 1);

this._stateChanged();

this.slider.onchange = this._sliderChanged.bind(this);

node.appendChild(this.slider);
}

_changeAttrs(): void {
let foo: string = this.yModel.sharedModel.getAttr('foo') as string;
let bar: string = this.yModel.sharedModel.getAttr('bar') as string;
foo = `#${foo}`;
bar = `#${bar}`;
this.yModel.sharedModel.setAttr('foo', foo);
this.yModel.sharedModel.setAttr('bar', bar);
this.node.innerHTML = `foo=${foo}<br>bar=${bar}`;
_stateChanged(): void {
this.slider.min = this.state.get('min');
this.slider.max = this.state.get('max');
this.slider.value = this.state.get('value');
this.slider.step = this.state.get('step');
}

_attrsChanged(): void {
const foo: string = this.yModel.sharedModel.getAttr('foo') as string;
const bar: string = this.yModel.sharedModel.getAttr('bar') as string;
this.node.innerHTML = `foo=${foo}<br>bar=${bar}`;
_sliderChanged(): void {
this.state.set('min', parseInt(this.slider.min ?? '0'));
this.state.set('max', parseInt(this.slider.max ?? '100'));
this.state.set('value', parseInt(this.slider.value ?? '50'));
this.state.set('step', parseInt(this.slider.step ?? '1'));
}

yModel: YjsWidgets.IJupyterYModel;
state: Y.Map<any>;
yModel: IJupyterYModel;
node: HTMLElement;
slider: HTMLInputElement;
}

const simple: JupyterFrontEndPlugin<void> = {
id: 'example:simple',
autoStart: true,
requires: [YjsWidgets.IJupyterYWidgetManager],
activate: (
app: JupyterFrontEnd,
wm: YjsWidgets.IJupyterYWidgetManager
): void => {
wm.registerWidget('MyWidget', YjsWidgets.JupyterYModel, MyWidget);
requires: [IJupyterYWidgetManager],
activate: (_: JupyterFrontEnd, wm: IJupyterYWidgetManager): void => {
wm.registerWidget('MySlider', JupyterYModel, MySlider);
}
};

Expand Down
Loading
Loading