Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ipywidgets -- working on implementing custom messages #6011

Closed
wants to merge 1 commit into from
Closed
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
16 changes: 15 additions & 1 deletion src/packages/frontend/components/icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,7 @@ const IconSpec = {
fire: FireOutlined,
firefox: { IconFont: "firefox" },
flash: ThunderboltOutlined,
"floppy-o": SaveOutlined, // used by matplotlib widget
"flow-chart": { IconFont: "flow-chart" },
folder: FolderOutlined,
"folder-open": FolderOpenOutlined,
Expand Down Expand Up @@ -649,10 +650,23 @@ if (typeof $ != "undefined") {
// @ts-ignore
const that = $(this);
for (const elt of that.find(".fa")) {
let style: CSS | undefined = undefined;
if (elt.className.includes("fa-fw")) {
// could break if subset of some other icon name, but doesn't seem to be.
// this is "fixed width" in font awesome:
style = { width: "1.28571429em", textAlign: "center" };
}
for (const cls of elt.className.split(/\s+/)) {
if (cls == "fa-fw") {
continue;
}
if (cls.startsWith("fa-")) {
ReactDOM.render(
<Icon name={cls.slice(3)} spin={cls == "fa-cocalc-ring"} />,
<Icon
style={style}
name={cls.slice(3)}
spin={cls == "fa-cocalc-ring"}
/>,
elt
);
break;
Expand Down
27 changes: 18 additions & 9 deletions src/packages/frontend/jupyter/project-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ export class JupyterActions extends JupyterActions0 {
5;
cells = cells.toJS();
}
dbg(`cells at manage_init = ${JSON.stringify(cells)}`);
//dbg(`cells at manage_init = ${JSON.stringify(cells)}`);

this.sync_exec_state = underscore.debounce(this.sync_exec_state, 2000);
this._throttled_ensure_positions_are_unique = underscore.debounce(
Expand Down Expand Up @@ -400,13 +400,13 @@ export class JupyterActions extends JupyterActions0 {
// Only one client -- the project itself -- will run this code.
manager_on_cell_change = (id: any, new_cell: any, old_cell: any) => {
const dbg = this.dbg(`manager_on_cell_change(id='${id}')`);
dbg(
`new_cell='${misc.to_json(
new_cell != null ? new_cell.toJS() : undefined
)}',old_cell='${misc.to_json(
old_cell != null ? old_cell.toJS() : undefined
)}')`
);
// dbg(
// `new_cell='${misc.to_json(
// new_cell != null ? new_cell.toJS() : undefined
// )}',old_cell='${misc.to_json(
// old_cell != null ? old_cell.toJS() : undefined
// )}')`
// );

if (
(new_cell != null ? new_cell.get("state") : undefined) === "start" &&
Expand Down Expand Up @@ -1173,6 +1173,13 @@ export class JupyterActions extends JupyterActions0 {
data
);
*/
} else if (type == "message_to_kernel") {
const content =
this.syncdb.ipywidgets_state.getLastMessageToKernel(model_id);
this.jupyter_kernel.send_comm_message_to_kernel(misc.uuid(), model_id, {
method: "custom",
content,
});
} else {
throw Error(`invalid synctable state -- unknown type '${type}'`);
}
Expand All @@ -1183,7 +1190,9 @@ export class JupyterActions extends JupyterActions0 {
const dbg = this.dbg("process_comm_message_from_kernel");
// serializing the full message could cause enormous load on the server, since
// the mesg may contain large buffers. Only do for low level debugging!
// dbg(mesg); // EXTREME DANGER!
// EXTREME DANGER!
const TYPESCRIPT_ERROR_ON_PURPOSE_TO_REMIND_ME = false;
dbg(JSON.stringify(mesg));
// This should be safe:
dbg(JSON.stringify(mesg.header));
if (this.syncdb.ipywidgets_state == null) {
Expand Down
49 changes: 43 additions & 6 deletions src/packages/frontend/jupyter/widgets/manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ export class WidgetManager extends base.ManagerBase<HTMLElement> {
}
this.setWidgetModelIdState = setWidgetModelIdState;
this.init_ipywidgets_state();
window.x = this;
}

private async init_ipywidgets_state(): Promise<void> {
Expand Down Expand Up @@ -126,6 +127,9 @@ export class WidgetManager extends base.ManagerBase<HTMLElement> {
case "message":
this.handle_table_model_message_change(model_id);
break;
case "message_to_kernel":
// only kernel handles this.
break;
default:
throw Error(`unknown state type '${type}'`);
}
Expand Down Expand Up @@ -262,11 +266,34 @@ export class WidgetManager extends base.ManagerBase<HTMLElement> {
model_id: string
): Promise<void> {
const message = this.ipywidgets_state.get_message(model_id);
console.log("message = ", message, size(message));
if (size(message) == 0) return; // temporary until we have delete functionality
// console.log("handle_table_model_message_change", message);
const model = await this.get_model(model_id);
if (model == null) return;
model.trigger("msg:custom", message);
let d = 50;
for (let i = 0; i < 100; i++) {
const model = await this.get_model(model_id);
if (model != null) {
model.trigger("msg:custom", message);
return;
}
await delay(d);
d *= 1.2;
}
console.log(
"handle_table_model_message_change: unable to deliver message since model doesn't exist.",
{ message, model_id }
);
}

private async handle_custom_message_from_model_to_kernel(
model_id: string,
message: object
): Promise<void> {
console.log("handle_custom_message_from_model_to_kernel", {
model_id,
message,
});
this.ipywidgets_state.sendMessageToKernel(model_id, message);
}

async deserialize_state(
Expand Down Expand Up @@ -467,6 +494,16 @@ export class WidgetManager extends base.ManagerBase<HTMLElement> {

// Start listening to model changes.
model.on("change", this.handle_model_change.bind(this));

model.on("msg:custom", (message) => {
this.handle_custom_message_from_model_to_kernel(model_id, message);
});

// DEBUG For low level debugging, we can listen to all events from the model:
// model.on("all", (...evt) => {
// console.log("received event from model", evt);
// });

this.setWidgetModelIdState(model_id, "");
// console.log("create_new-model - FINISHED", { model_id, serialized_state });
}
Expand All @@ -482,6 +519,7 @@ export class WidgetManager extends base.ManagerBase<HTMLElement> {
_data?: any,
_metadata?: any
): Promise<Comm> {
console.log("TODO: _create_comm");
const comm = new Comm(
target_name,
model_id,
Expand All @@ -495,7 +533,7 @@ export class WidgetManager extends base.ManagerBase<HTMLElement> {
model_id: string,
data: any
): string {
// console.log("TODO: process_comm_message_from_browser", model_id, data);
console.log("TODO: process_comm_message_from_browser", model_id, data);
if (data == null) {
throw Error("data must not be null");
}
Expand Down Expand Up @@ -586,8 +624,7 @@ export class WidgetManager extends base.ManagerBase<HTMLElement> {
// NOTE: I completely rewrote the entire k3d widget interface...
module = await import("./k3d");
} else if (moduleName === "jupyter-matplotlib") {
//module = await import("jupyter-matplotlib");
throw Error(`custom widgets: ${moduleName} not installed`);
module = await import("jupyter-matplotlib");
} else if (moduleName === "jupyter-threejs") {
//module = await import("jupyter-threejs");
throw Error(`custom widgets: ${moduleName} not installed`);
Expand Down
85 changes: 68 additions & 17 deletions src/packages/frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion src/packages/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@
"js-cookie": "^2.2.1",
"json-stable-stringify": "^1.0.1",
"jsonic": "^1.0.1",
"jupyter-matplotlib": "^0.11.1",
"k3d": "^2.14.1",
"katex": "^0.15.0",
"langs": "^2.0.0",
Expand Down Expand Up @@ -207,4 +208,4 @@
"bugs": {
"url": "https://github.com/sagemathinc/cocalc/issues"
}
}
}
2 changes: 1 addition & 1 deletion src/packages/project/client.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ class exports.Client extends EventEmitter
kucalc.init(@)

# use to define a logging function that is cleanly used internally
dbg: (f, trunc=1000) =>
dbg: (f, trunc=10000) =>
if DEBUG and @_winston
return (m...) =>
switch m.length
Expand Down
3 changes: 2 additions & 1 deletion src/packages/project/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@
},
"scripts": {
"start": "NODE_OPTIONS='--trace-warnings --unhandled-rejections=strict --enable-source-maps' npx cocalc-project",
"build": "npx tsc && npx coffee -m -c -o dist/ .",
"build": "npx tsc && npm run coffee",
"coffee": "npx coffee -m -c -o dist/ .",
"tsc": "npx tsc --watch --pretty --preserveWatchOutput"
},
"author": "SageMath, Inc.",
Expand Down
Loading