1717import base64
1818import uuid
1919import os
20+ import re
2021
2122from jinja2 import Template
2223
@@ -43,6 +44,40 @@ def _load_image(path: list[str]) -> str:
4344 else :
4445 with open (file_path , 'rb' ) as file :
4546 return base64 .b64decode (file .read ()).decode ('utf-8' )
47+
48+ def escape_newlines_within_query (query : str ) -> str :
49+ """
50+ Replaces unescaped single newline characters within the query string
51+ with escaped newline sequences '\\ \\ n'.
52+ It ignores newlines that are already part of an escaped sequence
53+ or likely intended for formatting at the start/end.
54+ """
55+ def replace_newline (match ):
56+ return '\\ \\ n'
57+
58+ # This regex looks for a newline character that is NOT preceded by a backslash
59+ # and is NOT at the very beginning or end of a non-empty query.
60+ pattern = r"(?<!\\)\\n"
61+
62+ # We'll add conditions to avoid replacing leading/trailing newlines if the query has content
63+ processed_query = query
64+
65+ if query .strip (): # Check if the query has non-whitespace content
66+ if processed_query .startswith ('\n ' ):
67+ processed_query = processed_query [1 :]
68+ if processed_query .endswith ('\n ' ):
69+ processed_query = processed_query [:- 1 ]
70+
71+ escaped_query = re .sub (pattern , replace_newline , processed_query )
72+
73+ # Add back the leading/trailing newlines if they were there
74+ if query .startswith ('\n ' ) and query .strip ():
75+ escaped_query = '\n ' + escaped_query
76+ if query .endswith ('\n ' ) and query .strip ():
77+ escaped_query = escaped_query + '\n '
78+
79+ return escaped_query
80+
4681
4782def generate_visualization_html (query : str , port : int , params : str ):
4883 # Get the directory of the current file (magics.py)
@@ -72,12 +107,12 @@ def generate_visualization_html(query: str, port: int, params: str):
72107
73108 # Create a Jinja2 template
74109 template = Template (template_content )
75-
110+ escaped_query = escape_newlines_within_query ( query )
76111 # Render the template with the graph data and JavaScript content
77112 html_content = template .render (
78113 graph_background_image = graph_background_image ,
79114 bundled_js_code = bundled_js_code , # Pass the actual JS code instead of the path
80- query = query ,
115+ query = escaped_query ,
81116 params = params ,
82117 port = port ,
83118 id = uuid .uuid4 ().hex # Prevent html/js selector collisions between cells
0 commit comments