This is an automated email from the ASF dual-hosted git repository.

github-merge-queue[bot] pushed a commit to branch 
gh-readonly-queue/main/pr-6945-3ea2346ccdf3b25041cbb23baa6e87fe68c37f4b
in repository https://gitbox.apache.org/repos/asf/texera.git

commit ee0a01e8fec19fbe6fd405e011213faa72d7280a
Author: Ryan Zhang <[email protected]>
AuthorDate: Mon Jul 27 14:55:44 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)
---
 .../src/main/resources/Dockerfile                  |  3 +-
 .../src/main/resources/custom.css                  | 47 ++++++++++++++++++++++
 .../src/main/resources/custom.js                   | 33 +++++++++++++++
 3 files changed, 82 insertions(+), 1 deletion(-)

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..6bee424368 100644
--- a/notebook-migration-service/src/main/resources/custom.js
+++ b/notebook-migration-service/src/main/resources/custom.js
@@ -47,6 +47,39 @@ 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;
+    }
+    // "nocursor" also blocks focus, so a click can't put a cell into edit 
mode.
+    Jupyter.notebook.get_cells().forEach(function (cell) {
+      if (cell.code_mirror) {
+        cell.code_mirror.setOption("readOnly", "nocursor");
+      }
+    });
+    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

Reply via email to