function log(level, ...args) {
console[level](`[${new Date().toISOString()}]`, ...args);
}
function createOutputCell(executionCount) {
const cell = document.createElement("div");
cell.className = "cell";
if (executionCount !== undefined) {
cell.dataset.n = executionCount.toString();
}
const outputArea = document.querySelector("#outputArea");
assert(outputArea, "outputArea not found");
outputArea.appendChild(cell);
return cell;
}
function isDisplayDataOrExecuteResult(msg) {
return (
msg.header.msg_type === "display_data" ||
msg.header.msg_type === "execute_result"
);
}
export async function onMessage(message) {
log("info", "Received message:", message);
message.buffers = message.buffers.map((b64) =>
Uint8Array.from(atob(b64), (c) => c.charCodeAt(0)),
);
log("debug", "Decoded buffers:", message.buffers);
const manager = globalThis.widgetManager;
assert(manager, "widgetManager not found");
log("debug", "Widget manager found");
if (isDisplayDataOrExecuteResult(message)) {
log("info", "Handling display data or execute result");
const { data, execution_count } = message.content;
const output = createOutputCell(execution_count);
if (data["application/vnd.jupyter.widget-view+json"]) {
log("debug", "Creating widget view");
const { model_id } = data["application/vnd.jupyter.widget-view+json"];
const model = await manager.get_model(model_id);
log("debug", "Got model:", model);
const view = await manager.create_view(model, {});
log("debug", "Created view:", view);
await manager.display_view(view, { el: output });
log("debug", "Displayed view");
} else if (data["text/html"]) {
log("debug", "Displaying HTML content");
const range = document.createRange();
const fragment = range.createContextualFragment(data["text/html"]);
output.appendChild(fragment);
} else if (data["text/plain"]) {
log("debug", "Displaying plain text content");
const pre = document.createElement("pre");
pre.textContent = data["text/plain"];
output.appendChild(pre);
}
return;
}
if (message.header.msg_type === "comm_open") {
log("info", "Handling comm_open message");
const commId = message.content.comm_id;
const comm = new Comm(commId, message.header);
log("debug", "Created new Comm:", comm);
manager.handle_comm_open(
comm,
message,
);
log("debug", "Handled comm_open");
return;
}
if (message.header.msg_type === "comm_msg") {
log("info", "Handling comm_msg message");
const commId = message.content.comm_id;
manager.get_model(commId).then((model) => {
log("debug", "Got model for comm_msg:", model);
model.comm?.handle_msg(message);
log("debug", "Handled comm_msg");
});
return;
}
if (message.header.msg_type === "comm_close") {
log("info", "Handling comm_close message");
const commId = message.content.comm_id;
manager.get_model(commId).then((model) => {
log("debug", "Got model for comm_close:", model);
model.comm?.handle_close(message);
log("debug", "Handled comm_close");
});
return;
}
if (message.header.msg_type === "stream") {
console.log(message.content);
return;
}
}
export class Comm {
comm_id;
get target_name() {
return "jupyter.widgets";
}
#on_msg = undefined;
#on_close = undefined;
#header;
constructor(modelId, header) {
this.comm_id = modelId;
this.#header = header;
}
send(data, _callbacks, metadata, buffers) {
log("info", "Comm.send called");
const msg_id = crypto.randomUUID();
const msg = {
parent_header: this.#header,
content: { comm_id: this.comm_id, data: data },
metadata: metadata,
buffers: buffers || [],
header: {
msg_id,
msg_type: "comm_msg",
date: new Date().toISOString(),
username: "sidecar",
session: "fake-todo",
version: "5.0",
},
};
log("debug", "Sending message:", msg);
fetch("/message", { method: "POST", body: JSON.stringify(msg) })
.then(() => log("debug", "Message sent successfully"))
.catch((error) => log("error", "Error sending message:", error));
return this.comm_id;
}
open() {
return this.comm_id;
}
close() {
return this.comm_id;
}
on_msg(cb) {
this.#on_msg = cb.bind(this);
}
on_close(cb) {
this.#on_close = cb.bind(this);
}
handle_msg(msg) {
this.#on_msg?.(msg);
}
handle_close(msg) {
this.#on_close?.(msg);
}
}
function assert(condition, message) {
if (!condition) {
throw new Error(message);
}
}