This is an automated email from the ASF dual-hosted git repository.
github-merge-queue[bot] pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/texera.git
The following commit(s) were added to refs/heads/main by this push:
new 084309c355 feat(python-notebook-migration): make embedded jupyter
notebook read-only (#6945)
084309c355 is described below
commit 084309c355979548ba76b97ba8ef6753bd256ba7
Author: Ryan Zhang <[email protected]>
AuthorDate: Thu Jul 30 14:42:45 2026 -0700
feat(python-notebook-migration): make embedded jupyter notebook read-only
(#6945)
### What changes were proposed in this PR?
The notebook migration service generates a Jupyter notebook and embeds
it in the Texera app as a reference view of the migrated workflow. It is
meant for reading, not editing. This PR configures the embedded Jupyter
container so the notebook renders as a clean, read-only view.
Changes, all scoped to the notebook migration service container assets:
1. `custom.css` (new): hides the classic Notebook chrome (header with
logo and logout, kernel and trust indicators, the File/Edit/View menu,
and the toolbar), and fixes a second, non-functional scrollbar by making
`#site` the single scroll container.
2. `Dockerfile`: copies `custom.css` into
`/home/jovyan/.jupyter/custom/custom.css`, next to the existing
`custom.js`.
3. `custom.js`: read-only hardening. Sets every code cell's CodeMirror
editor to `readOnly: "nocursor"`, calls `keyboard_manager.disable()` so
cells cannot be edited or executed, and overrides
`MarkdownCell.prototype.unrender` so markdown cells stay rendered. The
existing click-to-highlight postMessage integration is unchanged.
This does not touch the Texera frontend or backend.
### UI Comparison
Before this PR:
<img width="787" height="684" alt="2026-07-27_13-38-52"
src="https://github.com/user-attachments/assets/65cc08bd-ffcf-4343-b245-344830b6b59c"
/>
After this PR:
<img width="787" height="684" alt="image"
src="https://github.com/user-attachments/assets/41440d7d-af4e-4212-993b-0c4b1a05ea2f"
/>
### Any related issues, documentation, discussions?
Closes #6943
Parent issue #4301
### How was this PR tested?
Tested manually by building the container and opening the embedded
notebook in the Texera app:
1. The classic Notebook chrome (header, menu, toolbar, kernel and trust
indicators) is hidden; only the notebook cells are visible.
2. Clicking into a code cell does not put it in edit mode, typing does
nothing, and cells cannot be run.
3. Double-clicking a markdown cell keeps it rendered instead of dropping
into its editable source.
4. Only one working scrollbar is present.
No automated tests were added: the change is static container assets
(CSS and client-side JS in the classic Notebook UI) with no code path
exercisable by the existing unit test suites.
### Was this PR authored or co-authored using generative AI tooling?
Generated-by: Claude Code (Claude Opus 4.8)
---
.github/labeler.yml | 1 +
.../src/main/resources/Dockerfile | 3 +-
.../src/main/resources/custom.css | 47 ++++++++++++++++++++++
.../src/main/resources/custom.js | 34 ++++++++++++++++
4 files changed, 84 insertions(+), 1 deletion(-)
diff --git a/.github/labeler.yml b/.github/labeler.yml
index d5a20fad71..65aa19f99e 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -47,6 +47,7 @@ platform:
- 'computing-unit-managing-service/**'
- 'config-service/**'
- 'file-service/**'
+ - 'notebook-migration-service/**'
- 'workflow-compiling-service/**'
agent-service:
diff --git a/notebook-migration-service/src/main/resources/Dockerfile
b/notebook-migration-service/src/main/resources/Dockerfile
index 699b271943..bea79793f2 100644
--- a/notebook-migration-service/src/main/resources/Dockerfile
+++ b/notebook-migration-service/src/main/resources/Dockerfile
@@ -17,8 +17,9 @@
FROM jupyter/base-notebook:notebook-6.5.4
-# Copy custom JavaScript for Jupyter and the startup script
+# Copy custom JavaScript/CSS for Jupyter and the startup script
COPY custom.js /home/jovyan/.jupyter/custom/custom.js
+COPY custom.css /home/jovyan/.jupyter/custom/custom.css
COPY start-texera-jupyter.sh /usr/local/bin/start-texera-jupyter.sh
# Ensure correct permissions. custom.js must stay writable by jovyan so the
diff --git a/notebook-migration-service/src/main/resources/custom.css
b/notebook-migration-service/src/main/resources/custom.css
new file mode 100644
index 0000000000..5e20e01eb2
--- /dev/null
+++ b/notebook-migration-service/src/main/resources/custom.css
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/*
+ * The notebook is embedded in the Texera app as a read-only view of the
+ * generated workflow. Hide the classic Notebook chrome (logo/name/logout,
+ * kernel and trust indicators, the File/Edit/View menu, and the toolbar) so
+ * only the notebook cells remain.
+ */
+#header,
+#menubar-container,
+#maintoolbar-container {
+ display: none !important;
+}
+
+/* Classic Notebook scrolls inside #site, but the document also scrolls, which
+ shows a second, non-functional scrollbar. Stop the document from scrolling
so
+ only #site's scrollbar remains. */
+html,
+body {
+ height: 100% !important;
+ overflow: hidden !important;
+}
+
+/* The notebook offsets #site by the (now-hidden) header height; make it fill
the
+ iframe (its own overflow provides the single, working scrollbar). */
+#site {
+ top: 0 !important;
+ height: 100vh !important;
+ overflow-y: auto !important;
+}
diff --git a/notebook-migration-service/src/main/resources/custom.js
b/notebook-migration-service/src/main/resources/custom.js
index 70fc4ce931..672104dfc0 100644
--- a/notebook-migration-service/src/main/resources/custom.js
+++ b/notebook-migration-service/src/main/resources/custom.js
@@ -47,6 +47,40 @@ require(["base/js/events"], function (events) {
});
});
+// Read-only viewing: the embedded notebook is a reference view of the
generated
+// workflow, not an editing surface. Make every code cell's editor read-only
and
+// disable Jupyter's keyboard shortcuts so cells cannot be edited or executed.
+// (The chrome hidden via custom.css already removes the run/save/menu
controls.)
+require(["base/js/events"], function (events) {
+ function makeReadOnly() {
+ if (!window.Jupyter || !Jupyter.notebook) {
+ return;
+ }
+ // readOnly "true" blocks editing while still letting the
+ // editor take focus, so a viewer can select and copy the generated code.
+ Jupyter.notebook.get_cells().forEach(function (cell) {
+ if (cell.code_mirror) {
+ cell.code_mirror.setOption("readOnly", true);
+ }
+ });
+ if (Jupyter.keyboard_manager) {
+ Jupyter.keyboard_manager.disable();
+ }
+ }
+
+ // notebook_loaded fires once cells (and their editors) exist; kernel_ready
+ // fires on every (re)start and re-applies in case the manager was
re-enabled.
+ events.on("notebook_loaded.Notebook", makeReadOnly);
+ events.on("kernel_ready.Kernel", makeReadOnly);
+});
+
+// Keep markdown cells rendered: overriding unrender() stops a double-click (or
+// Enter) from dropping a markdown cell into its editable source view. The
+// prototype override applies to all existing and future markdown cells.
+require(["notebook/js/textcell"], function (textcell) {
+ textcell.MarkdownCell.prototype.unrender = function () {};
+});
+
// Listen for messages from the Texera app (or parent window)
window.addEventListener("message", function (event) {
// Verify the message origin