Something went wrong. Try again.
Monorepo for Aesthetic.Computer aesthetic.computer
Something went wrong. Try again.
Python
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943"""Aesthetic Computer Python Library for Jupyter Notebooks
Usage: import aesthetic
# Toggle between production and localhost: aesthetic.USE_PRODUCTION = True # Use aesthetic.computer (production) aesthetic.USE_PRODUCTION = False # Use localhost:8888 (default)
Examples: # Use localhost (default) %%ac (ink red) (line 0 0 100 100)
# Switch to production aesthetic.USE_PRODUCTION = True
# Now all pieces load from aesthetic.computer %%ac prompt
Note: All URLs include ?notebook=true for custom boot animation"""
import sysimport importlibimport urllib.parseimport hashlibimport subprocessimport os
# Check if the module is already loadedif "aesthetic" in sys.modules: importlib.reload(sys.modules["aesthetic"])
from IPython.display import display, IFrame, HTMLfrom IPython.core.magic import Magics, cell_magic, line_magic, magics_class
DEFAULT_DENSITY = 2
# Global configuration for production vs localhost# Set to True to use aesthetic.computer (production), False for localhost:8888 (default)USE_PRODUCTION = False
def _get_base_url(): """Get the base URL based on USE_PRODUCTION setting""" return "https://aesthetic.computer" if USE_PRODUCTION else "https://localhost:8888"
def _get_ipython_context(): try: from IPython import get_ipython ip = get_ipython() if ip is not None: return ip.user_ns.copy() except Exception: pass return {}
def _safe_eval(expr, context=None): """Safely evaluate math expressions with access to IPython variables""" if context is None: context = {}
if expr == "100%" or (isinstance(expr, str) and expr.endswith('%')): return expr
try: return int(expr) except (ValueError, TypeError): pass
try: return float(expr) except (ValueError, TypeError): pass
import math safe_dict = { '__builtins__': {}, 'abs': abs, 'min': min, 'max': max, 'round': round, 'int': int, 'float': float, 'sum': sum, 'len': len, 'pow': pow, 'math': math, 'pi': math.pi, 'e': math.e, 'sin': math.sin, 'cos': math.cos, 'tan': math.tan, 'sqrt': math.sqrt, 'log': math.log, 'log10': math.log10, 'exp': math.exp, 'floor': math.floor, 'ceil': math.ceil, **context, }
try: result = eval(expr, safe_dict) if isinstance(result, float) and result.is_integer(): return int(result) return result except Exception as e: print(f"Warning: Could not evaluate '{expr}': {e}") return expr
def _normalize_density(value, context=None, default=DEFAULT_DENSITY): if value is None: return default if isinstance(value, (int, float)): return value if isinstance(value, str): evaluated = _safe_eval(value, context) if isinstance(evaluated, (int, float)): return evaluated return default
def _scale_dimension(value, density): if isinstance(value, (int, float)) and isinstance(density, (int, float)): scaled = value * density if isinstance(scaled, float) and scaled.is_integer(): return int(scaled) return scaled return value
def _compute_iframe_dimensions(width, height, density): return _scale_dimension(width, density), _scale_dimension(height, density)
def show(piece, width="100%", height=54, density=None): importlib.reload(importlib.import_module('aesthetic')) base_url = _get_base_url() url = f"{base_url}/{piece}?nolabel=true&nogap=true¬ebook=true" density_value = _normalize_density(density, _get_ipython_context()) iframe_width, iframe_height = _compute_iframe_dimensions(width, height, density_value) if density_value is not None: url += f"&density={density_value}" display(IFrame(src=url, width=iframe_width, height=iframe_height, frameborder="0"))
def encode_kidlisp_with_node(code): """ Use Node.js to encode kidlisp code using the same function as kidlisp.mjs Falls back to Python implementation if Node.js is not available """ try: # Get the directory of this script script_dir = os.path.dirname(os.path.abspath(__file__)) encoder_script = os.path.join(script_dir, 'encode_kidlisp.mjs') # Run the Node.js encoder result = subprocess.run( ['node', encoder_script, code], capture_output=True, text=True, timeout=5 ) if result.returncode == 0: return result.stdout.strip() else: print(f"Node.js encoder failed: {result.stderr}") raise subprocess.CalledProcessError(result.returncode, 'node') except (subprocess.CalledProcessError, subprocess.TimeoutExpired, FileNotFoundError) as e: print(f"Falling back to Python encoder: {e}") # Fallback to Python implementation with full URL base_url = _get_base_url() encoded = code.replace(' ', '_').replace('\n', '~') return f"{base_url}/{encoded}?nolabel=true&nogap=true¬ebook=true"
def _highlight_kidlisp(code): """Generate syntax-highlighted HTML for KidLisp code""" # Simple KidLisp syntax highlighting highlighted = code # KidLisp keywords and functions (green) keywords = [ 'ink', 'wipe', 'line', 'rect', 'circle', 'box', 'paste', 'write', 'text', 'sin', 'cos', 'tan', 'sqrt', 'abs', 'floor', 'ceil', 'round', 'max', 'min', 'random', 'noise', 'if', 'let', 'loop', 'for', 'while', 'define', 'defn', 'lambda', 'map', 'filter', 'reduce', 'quote', 'eval', 'quote', 'true', 'false' ] # Build regex pattern for keywords import re for kw in keywords: pattern = r'\b' + kw + r'\b' highlighted = re.sub(pattern, f'<span style="color: #4CAF50; font-weight: bold;">{kw}</span>', highlighted, flags=re.IGNORECASE) # Numbers (blue) highlighted = re.sub(r'\b(\d+\.?\d*)\b', r'<span style="color: #2196F3;">\1</span>', highlighted) # Strings (orange) highlighted = re.sub(r'"([^"]*)"', r'<span style="color: #FF9800;">"\1"</span>', highlighted) # Comments (gray) highlighted = re.sub(r';[^\n]*', lambda m: f'<span style="color: #999;">%s</span>' % m.group(0), highlighted) # Parentheses (purple) highlighted = highlighted.replace('(', '<span style="color: #9C27B0;">(</span>') highlighted = highlighted.replace(')', '<span style="color: #9C27B0;">)</span>') return highlighted
def _display_kidlisp_source(code): """Display KidLisp source code with syntax highlighting below the iframe""" if not code or code.startswith('$'): # External code reference - show a note display(HTML(f''' <div style="margin-top: 12px; padding: 8px 12px; background: #f5f5f5; border-left: 3px solid #999; font-family: monospace; font-size: 12px; color: #666;"> <span style="color: #999;">📌 External code reference: <code>{code}</code></span> </div> ''')) else: # Inline code - show with syntax highlighting highlighted_code = _highlight_kidlisp(code) display(HTML(f''' <pre style="margin-top: 12px; padding: 12px; background: #f9f9f9; border: 1px solid #e0e0e0; border-radius: 4px; font-family: 'Courier New', monospace; font-size: 12px; line-height: 1.5; overflow-x: auto; white-space: pre-wrap; word-wrap: break-word;"> {highlighted_code} </pre> '''))
def kidlisp(code, width="100%", height=500, auto_scale=False, tv_mode=False, density=None, show_source=True): """ Run kidlisp code in Aesthetic Computer. Args: code (str): The kidlisp code to execute width: Virtual width in AC pixels (default: "100%") height: Virtual height in AC pixels (default: 400) auto_scale (bool): If True, iframe will scale to fit content (default: False) tv_mode (bool): If True, disables touch and keyboard input for TV display (default: False) density (number|str): Virtual pixel density (default: 2) show_source (bool): If True, display syntax-highlighted source below iframe (default: True) """ importlib.reload(importlib.import_module('aesthetic')) # Use Node.js to encode kidlisp code and get the full URL clean_code = code.strip() url = encode_kidlisp_with_node(clean_code) # Add TV mode parameter if requested if tv_mode: separator = "&" if "?" in url else "?" url += f"{separator}tv=true"
density_value = _normalize_density(density, _get_ipython_context()) if density_value is not None: separator = "&" if "?" in url else "?" url += f"{separator}density={density_value}" # Create a stable ID based on content hash to reduce flicker on re-runs content_hash = hashlib.md5(f"{clean_code}{width}{height}{tv_mode}{density_value}".encode()).hexdigest()[:8] iframe_id = f"ac-iframe-{content_hash}"
iframe_width, iframe_height = _compute_iframe_dimensions(width, height, density_value) # Always use HTML approach with no margins, positioned at top-left if auto_scale: iframe_html = f''' <div style="margin: -8px -8px 0 -8px; padding: 0; overflow: hidden;"> <iframe id="{iframe_id}" src="{url}" width="{iframe_width}" height="{iframe_height}" frameborder="0" style="background: transparent; margin: 0; padding: 0; border: none; display: block; transform-origin: top left; max-width: 100%; height: auto; aspect-ratio: 1/1;"> </iframe> </div> ''' else: iframe_html = f''' <div style="margin: -8px -8px 0 -8px; padding: 0; overflow: hidden;"> <iframe id="{iframe_id}" src="{url}" width="{iframe_width}" height="{iframe_height}" frameborder="0" style="background: transparent; margin: 0; padding: 0; border: none; display: block;"> </iframe> </div> ''' display(HTML(iframe_html)) # Display source code if requested if show_source: _display_kidlisp_source(clean_code)
def kidlisp_display(code, width="100%", height=400, auto_scale=False, density=None): """ Run kidlisp code with minimal visual footprint - designed for display-only cells. Args: code (str): The kidlisp code to execute width: Width of the iframe (default: "100%") height: Height of the iframe (default: 400) auto_scale (bool): If True, iframe will scale to fit content (default: False) density (number|str): Virtual pixel density (default: 2) """ # This function is identical to kidlisp() but with a different name # The "display-only" aspect comes from how you use it in your notebook return kidlisp(code, width, height, auto_scale, False, density)
def show_side_by_side(*pieces_and_options): """ Display multiple pieces side by side in a horizontal layout.
Args: *pieces_and_options: Can be either: - Just piece names as strings: show_side_by_side("clock:4", "clock:5") - Tuples of (piece, width, height): show_side_by_side(("clock:4", 200, 100), ("clock:5", 150, 75)) - Mix of both """ importlib.reload(importlib.import_module('aesthetic'))
base_url = _get_base_url() iframes_html = []
for item in pieces_and_options: if isinstance(item, tuple): piece, width, height = item else: piece = item width = 200 height = 100
url = f"{base_url}/{piece}?nolabel=true&nogap=true¬ebook=true" iframe_html = f'<iframe src="{url}" width="{width}" height="{height}" frameborder="0" style="margin: 0; padding: 0; border: none;"></iframe>' iframes_html.append(iframe_html) # Create a container div with flexbox layout and no gaps container_html = f''' <div style="display: flex; flex-wrap: wrap; align-items: flex-start; gap: 0; margin: 0; padding: 0;"> {"".join(iframes_html)} </div> ''' display(HTML(container_html))
# Short aliases for kidlisp functiondef k(*args, **kwargs): """Ultra-short alias for kidlisp function""" return λ(*args, **kwargs)
def _(*args, **kwargs): """Single underscore alias for kidlisp function""" return λ(*args, **kwargs)
# Even shorter - single character functionsdef l(*args, **kwargs): """Single letter 'l' for lisp""" return λ(*args, **kwargs)
# Lambda symbol alias - perfect for functional programming!def λ(*args, **kwargs): """ Lambda symbol alias for kidlisp function - λ() Usage: λ("kidlisp code") # Default: 100% width, 32px height λ("kidlisp code", 300) # Single number = height (width stays 100%) λ("kidlisp code", 800, 600) # Two numbers = width, height λ("kidlisp code", (800, 600)) # Resolution tuple λ(("kidlisp code", 500, 300)) # Tuple format: (code, width, height) λ("kidlisp code", resolution=(800, 600)) # Named resolution tuple """ # Default values code = None width = "100%" height = 30 auto_scale = kwargs.get('auto_scale', False) density = kwargs.get('density', None) resolution = kwargs.get('resolution', None) # Parse positional arguments if len(args) >= 1: code = args[0] if len(args) >= 2: # Check if second argument is a resolution tuple if isinstance(args[1], tuple) and len(args[1]) == 2: resolution = args[1] elif isinstance(args[1], (int, float)): # Single number = height only (width stays 100%) height = args[1] else: width = args[1] if len(args) >= 3: # Third argument could be height or resolution tuple if isinstance(args[2], tuple) and len(args[2]) == 2: resolution = args[2] else: # If we have 3 args and second was a number, then this is width, height if isinstance(args[1], (int, float)): width = args[1] height = args[2] else: height = args[2] if len(args) >= 4: auto_scale = args[3] # Handle tuple input for resolution in first parameter if isinstance(code, tuple): if len(code) == 3: # Format: ("code", width, height) code, width, height = code elif len(code) == 2: # Format: ("code", height) - width stays 100% code, height = code else: raise ValueError("Tuple must have 2 or 3 elements: (code, height[, width])") # Handle resolution tuple parameter (overrides other width/height settings) if resolution is not None: if isinstance(resolution, tuple) and len(resolution) == 2: width, height = resolution else: raise ValueError("Resolution must be a tuple of (width, height)") return kidlisp(code, width, height, auto_scale, False, density)
def _is_numeric_like(s): """Check if a string looks numeric without evaluation (avoids warnings)""" if not isinstance(s, str): return False s = s.strip() if not s: return False try: float(s) return True except (ValueError, TypeError): return False
# IPython Magic Commands for Native Kidlisp Syntax@magics_classclass AestheticComputerMagics(Magics): """IPython magic commands for native kidlisp syntax in Jupyter notebooks""" @cell_magic def ac(self, line, cell): """ Cell magic to execute kidlisp code or piece invocations from PRODUCTION (aesthetic.computer). Uppercase %%AC loads from development localhost instead. Usage (Production): %%ac (ink red) (line 0 0 100 100) Or for piece invocations: %%ac clock cdefg With size options: %%ac 400 # Height only (width = 100%) %%ac 800 600 # Width and height %%ac 320 240*2 # Math expressions supported (your kidlisp code here)
Development (use %%AC uppercase for localhost:8888): %%AC prompt
Note: Width/height are virtual AC pixels. Iframe size is scaled by density. Parameters support math expressions and variables from the current namespace. Examples: %%ac 400*2 # Height = 800, width = 100% (production) %%AC 400*2 # Height = 800, width = 100% (development) %%ac my_width my_height %%ac int(800/2) int(600*1.5) # Width = 400, height = 900 %%ac 320 240 density=2 # Virtual size with explicit density """ # Parse and evaluate arguments with math support args = line.strip().split() if line.strip() else [] width = "100%" height = 30 tv_mode = False density = None # Look for tv_mode parameter filtered_args = [] for arg in args: if arg.startswith('tv_mode='): tv_mode = arg.split('=')[1].lower() in ('true', '1', 'yes') elif arg.startswith('density=') or arg.startswith('d='): density = arg.split('=', 1)[1] else: filtered_args.append(arg) args = filtered_args context = _get_ipython_context() if len(args) >= 1: # If only one argument, treat it as height and keep width as "100%" if len(args) == 1: height_expr = args[0] height = _safe_eval(height_expr, context) # width stays as "100%" (already set above) else: # If two or more arguments, first is width, second is height width_expr = args[0] width = _safe_eval(width_expr, context) if len(args) >= 2: height_expr = args[1] height = _safe_eval(height_expr, context)
density_value = _normalize_density(density, context) iframe_width, iframe_height = _compute_iframe_dimensions(width, height, density_value) # Check if the cell content is a piece invocation or kidlisp code cell_content = cell.strip() # Detect if this is a piece invocation vs kidlisp code # Piece invocations: # - Don't start with ( or ; # - Don't contain newlines (single line piece calls) # - First word is likely a piece name (not a kidlisp function) is_piece_invocation = ( cell_content and not cell_content.startswith('(') and not cell_content.startswith(';') and '\n' not in cell_content and not cell_content.startswith('~') and # kidlisp can start with ~ len(cell_content.split()) >= 1 ) # Force PRODUCTION mode for lowercase %ac global USE_PRODUCTION old_production = USE_PRODUCTION try: USE_PRODUCTION = True if is_piece_invocation: # Handle as piece invocation - construct URL directly # Replace spaces with ~ for piece parameters piece_url = cell_content.replace(' ', '~') base_url = _get_base_url() url = f"{base_url}/{piece_url}?nolabel=true&nogap=true¬ebook=true"
# Add TV mode parameter if requested if tv_mode: url += "&tv=true"
if density_value is not None: url += f"&density={density_value}" # Create iframe directly content_hash = hashlib.md5(f"{cell_content}{width}{height}{tv_mode}{density_value}".encode()).hexdigest()[:8] iframe_id = f"ac-iframe-{content_hash}" iframe_html = f''' <div style="margin: -8px -8px 0 -8px; padding: 0; overflow: hidden;"> <iframe id="{iframe_id}" src="{url}" width="{iframe_width}" height="{iframe_height}" frameborder="0" style="background: transparent; margin: 0; padding: 0; border: none; display: block;"> </iframe> </div> ''' display(HTML(iframe_html)) # Display source code _display_kidlisp_source(cell_content) else: # Handle as kidlisp code return kidlisp(cell_content, width, height, False, tv_mode, density_value) finally: USE_PRODUCTION = old_production @cell_magic def AC(self, line, cell): """ Cell magic to execute kidlisp code or piece invocations from DEVELOPMENT (localhost:8888). Lowercase %%ac loads from production aesthetic.computer instead. Usage (Development): %%AC prompt Or for piece invocations: %%AC clock cdefg With size options: %%AC 400 # Height only (width = 100%) %%AC 800 600 # Width and height Production (use %%ac lowercase for aesthetic.computer): %%ac prompt
Note: Uppercase AC = localhost/dev, lowercase ac = production domain """ # Parse and evaluate arguments with math support args = line.strip().split() if line.strip() else [] width = "100%" height = 30 tv_mode = False density = None # Look for tv_mode parameter filtered_args = [] for arg in args: if arg.startswith('tv_mode='): tv_mode = arg.split('=')[1].lower() in ('true', '1', 'yes') elif arg.startswith('density=') or arg.startswith('d='): density = arg.split('=', 1)[1] else: filtered_args.append(arg) args = filtered_args context = _get_ipython_context() if len(args) >= 1: # If only one argument, treat it as height and keep width as "100%" if len(args) == 1: height_expr = args[0] height = _safe_eval(height_expr, context) # width stays as "100%" (already set above) else: # If two or more arguments, first is width, second is height width_expr = args[0] width = _safe_eval(width_expr, context) if len(args) >= 2: height_expr = args[1] height = _safe_eval(height_expr, context)
density_value = _normalize_density(density, context) iframe_width, iframe_height = _compute_iframe_dimensions(width, height, density_value) # Check if the cell content is a piece invocation or kidlisp code cell_content = cell.strip() # Detect if this is a piece invocation vs kidlisp code is_piece_invocation = ( cell_content and not cell_content.startswith('(') and not cell_content.startswith(';') and '\n' not in cell_content and not cell_content.startswith('~') and len(cell_content.split()) >= 1 ) # Force DEVELOPMENT mode (localhost) for uppercase AC global USE_PRODUCTION old_production = USE_PRODUCTION try: USE_PRODUCTION = False if is_piece_invocation: # Handle as piece invocation - construct URL directly piece_url = cell_content.replace(' ', '~') base_url = _get_base_url() # This will now use localhost url = f"{base_url}/{piece_url}?nolabel=true&nogap=true¬ebook=true"
# Add TV mode parameter if requested if tv_mode: url += "&tv=true"
if density_value is not None: url += f"&density={density_value}" # Create iframe directly content_hash = hashlib.md5(f"{cell_content}{width}{height}{tv_mode}{density_value}".encode()).hexdigest()[:8] iframe_id = f"ac-iframe-{content_hash}" iframe_html = f''' <div style="margin: -8px -8px 0 -8px; padding: 0; overflow: hidden;"> <iframe id="{iframe_id}" src="{url}" width="{iframe_width}" height="{iframe_height}" frameborder="0" style="background: transparent; margin: 0; padding: 0; border: none; display: block;"> </iframe> </div> ''' display(HTML(iframe_html)) # Display source code _display_kidlisp_source(cell_content) else: # Handle as kidlisp code return kidlisp(cell_content, width, height, False, tv_mode, density_value) finally: USE_PRODUCTION = old_production @line_magic def ac_line(self, line): """ Line magic to execute a single line of kidlisp code or piece invocation. Usage: %ac (ink red) (circle 25 25 10) %ac clock cdefg %ac 100 clock cdefg # Height = 100, width = 100% %ac 400 200 clock cdefg # Width = 400, height = 200 %ac 320 240 density=2 clock # Virtual size with explicit density
Note: Width/height are virtual AC pixels. Iframe size is scaled by density. Note: For piece invocations with special characters like {}, use cell magic %%ac instead to avoid Python syntax parsing issues. """ line_content = line.strip() # Parse potential size parameters from the beginning of the line parts = line_content.split() width = "100%" height = 30 density = None content_start_index = 0
context = _get_ipython_context()
filtered_parts = [] for part in parts: if part.startswith('density=') or part.startswith('d='): density = part.split('=', 1)[1] else: filtered_parts.append(part)
parts = filtered_parts # Check if first part(s) are numeric parameters (use _is_numeric_like to avoid warning) if len(parts) >= 1 and _is_numeric_like(parts[0]): # First part looks numeric - parse it first_num = _safe_eval(parts[0], context) if isinstance(first_num, (int, float)): height = first_num content_start_index = 1 # Check if second part is also numeric (width) if len(parts) >= 3 and _is_numeric_like(parts[1]): second_num = _safe_eval(parts[1], context) if isinstance(second_num, (int, float)): width = first_num height = second_num content_start_index = 2 # Extract the actual content (after size parameters) if content_start_index > 0: actual_content = ' '.join(parts[content_start_index:]) else: actual_content = line_content # Detect if this is a piece invocation vs kidlisp code is_piece_invocation = ( actual_content and not actual_content.startswith('(') and not actual_content.startswith(';') and not actual_content.startswith('~') and len(actual_content.split()) >= 1 ) density_value = _normalize_density(density, context) iframe_width, iframe_height = _compute_iframe_dimensions(width, height, density_value)
if is_piece_invocation: # Handle as piece invocation - construct URL directly # Replace spaces with ~ for piece parameters piece_url = actual_content.replace(' ', '~') base_url = _get_base_url() url = f"{base_url}/{piece_url}?nolabel=true&nogap=true¬ebook=true"
if density_value is not None: url += f"&density={density_value}" # Create iframe directly content_hash = hashlib.md5(f"{actual_content}{width}{height}{density_value}".encode()).hexdigest()[:8] iframe_id = f"ac-iframe-{content_hash}" iframe_html = f''' <div style="margin: -8px -8px 0 -8px; padding: 0; overflow: hidden;"> <iframe id="{iframe_id}" src="{url}" width="{iframe_width}" height="{iframe_height}" frameborder="0" style="background: transparent; margin: 0; padding: 0; border: none; display: block;"> </iframe> </div> ''' display(HTML(iframe_html)) else: # Handle as kidlisp code return kidlisp(actual_content, width, height, False, False, density_value) @line_magic def AC(self, line): """ Line magic to execute a single line of kidlisp code or piece invocation from DEVELOPMENT. Usage: %AC (ink red) (circle 25 25 10) %AC clock cdefg %AC 100 clock cdefg # Height = 100, width = 100% %AC 400 200 clock cdefg # Width = 400, height = 200
Note: Uppercase AC = localhost/dev, lowercase ac = production domain Note: Width/height are virtual AC pixels. Iframe size is scaled by density. """ line_content = line.strip() # Parse potential size parameters from the beginning of the line parts = line_content.split() width = "100%" height = 30 density = None content_start_index = 0
context = _get_ipython_context()
filtered_parts = [] for part in parts: if part.startswith('density=') or part.startswith('d='): density = part.split('=', 1)[1] else: filtered_parts.append(part)
parts = filtered_parts # Check if first part(s) are numeric parameters (use _is_numeric_like to avoid warning) if len(parts) >= 1 and _is_numeric_like(parts[0]): # First part looks numeric - parse it first_num = _safe_eval(parts[0], context) if isinstance(first_num, (int, float)): height = first_num content_start_index = 1 # Check if second part is also numeric (width) if len(parts) >= 3 and _is_numeric_like(parts[1]): second_num = _safe_eval(parts[1], context) if isinstance(second_num, (int, float)): width = first_num height = second_num content_start_index = 2 # Extract the actual content (after size parameters) if content_start_index > 0: actual_content = ' '.join(parts[content_start_index:]) else: actual_content = line_content # Detect if this is a piece invocation vs kidlisp code is_piece_invocation = ( actual_content and not actual_content.startswith('(') and not actual_content.startswith(';') and not actual_content.startswith('~') and len(actual_content.split()) >= 1 ) density_value = _normalize_density(density, context) iframe_width, iframe_height = _compute_iframe_dimensions(width, height, density_value)
# Force DEVELOPMENT mode (localhost) for uppercase AC global USE_PRODUCTION old_production = USE_PRODUCTION try: USE_PRODUCTION = False if is_piece_invocation: # Handle as piece invocation - construct URL directly # Replace spaces with ~ for piece parameters piece_url = actual_content.replace(' ', '~') base_url = _get_base_url() # This will now use localhost url = f"{base_url}/{piece_url}?nolabel=true&nogap=true¬ebook=true"
if density_value is not None: url += f"&density={density_value}" # Create iframe directly content_hash = hashlib.md5(f"{actual_content}{width}{height}{density_value}".encode()).hexdigest()[:8] iframe_id = f"ac-iframe-{content_hash}" iframe_html = f''' <div style="margin: -8px -8px 0 -8px; padding: 0; overflow: hidden;"> <iframe id="{iframe_id}" src="{url}" width="{iframe_width}" height="{iframe_height}" frameborder="0" style="background: transparent; margin: 0; padding: 0; border: none; display: block;"> </iframe> </div> ''' display(HTML(iframe_html)) else: # Handle as kidlisp code return kidlisp(actual_content, width, height, False, False, density_value) finally: USE_PRODUCTION = old_production
# Automatic IPython/Jupyter setup - makes λ globally availabledef _setup_ipython(): """Automatically setup λ and magic commands in IPython/Jupyter environment""" try: from IPython import get_ipython ip = get_ipython() if ip is not None: # Add λ to the user namespace ip.user_ns['λ'] = λ # Also add to the global namespace for pylance ip.user_global_ns['λ'] = λ # Register as a built-in so it's available everywhere import builtins builtins.λ = λ # Register the magic commands class magic_instance = AestheticComputerMagics(ip) ip.register_magics(AestheticComputerMagics) # Create and register line magics with production/dev modes forced def ac_line_prod(line): """Line magic for production (%ac)""" global USE_PRODUCTION old_production = USE_PRODUCTION try: USE_PRODUCTION = True return magic_instance.ac_line(line) finally: USE_PRODUCTION = old_production def AC_line_dev(line): """Line magic for development (%AC)""" global USE_PRODUCTION old_production = USE_PRODUCTION try: USE_PRODUCTION = False return magic_instance.ac_line(line) finally: USE_PRODUCTION = old_production # Register the line magics with correct names ip.register_magic_function(ac_line_prod, 'line', 'ac') ip.register_magic_function(AC_line_dev, 'line', 'AC') return True except ImportError: pass return False
# Auto-setup when module is imported_setup_ipython()
# Export all functions and configuration for proper IDE support__all__ = ['show', 'show_side_by_side', 'kidlisp', 'kidlisp_display', 'k', '_', 'l', 'λ', 'AestheticComputerMagics', 'USE_PRODUCTION']