Skip to content

yu314-coder/CodeBench

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

154 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

CodeBench

A self-contained developer / scientific / AI workstation for iPad and Mac. Python 3.14, Jupyter notebooks, C, C++, Fortran, pdflatex, and local LLMs — all running on-device, no internet required.

  iPad / iPadOS / Mac Catalyst
  ┌──────────────────────────────────────────────────────────┐
  │                                                          │
  │   Monaco editor ─── IntelliSense + signature help        │
  │       ▲                  ▲                               │
  │       │                  │                               │
  │       │            ┌─────┴───────┐                       │
  │       │            │  vim mode   │  ⌘P ⇧⌘,               │
  │       │            └─────────────┘                       │
  │       ▼                                                  │
  │   Notebook editor  ─── per-cell Run + persistent kernel  │
  │       │                                                  │
  │       ▼                                                  │
  │   Visual debugger ─── step / inspect / call stack        │
  │       │                                                  │
  │       ▼                                                  │
  │   Python / C / C++ / Fortran / pdflatex                  │
  │       │                                                  │
  │       ▼                                                  │
  │   SwiftTerm terminal ◄──► PTY ◄──► CPython REPL          │
  │       │                                                  │
  │       ▼                                                  │
  │   Local LLM chat + RAG + image gen                       │
  │   (bundled GGUF · OpenAI · Anthropic · Ollama remote)    │
  │                                                          │
  └──────────────────────────────────────────────────────────┘

Built on python-ios-lib — the Python 3.14 runtime + 30+ native iOS Python libraries project. Every library listed below has its own reference documentation in that repo; links go directly to each library's main doc.


What's new

AI Assist — Apple Intelligence, chat modes, and conversation memory

The in-editor AI assistant now runs on Apple's on-device foundation models (AFM 3, iOS 26+ — private, free, zero downloads) or any downloaded GGUF, and finally behaves like a real chat:

  • Model pill — one picker for Apple Intelligence (on-device), Apple Cloud Pro (iOS 27), the built-in GGUF catalog (Qwen 3.5 / Gemma 4 / Llama 3.2 / …), any .gguf on disk, or the Models tab. Selecting Apple also drives the terminal ai REPL.
  • Mode pill — Coding vs ChatCoding sees the open file and answers with runnable patches (the classic behavior). Chat is a plain conversation on a selectable system-prompt persona (Default / Coder / Tutor / Writer / Translator / Analyst / Creative / Concise, or a custom prompt you write) — your editor code is never sent in Chat mode.
  • Conversation memory — every turn is remembered and replayed within the model's context budget, so "make it shorter" / "why did that fail?" work. Auto-run corrections replace the failed attempt in memory, so the assistant remembers the code that worked. A ✎ New-chat button wipes transcript + memory.
  • UI polish — adaptive panel width (280–360 pt), a compact ⚡ auto-run toggle, and a welcome card with tappable mode-aware starter prompts instead of a blank panel. Long-press the AI Assist chip for a quick model/mode menu without opening the panel.

Libraries tab — interactive storage donut + real sizes

The Libraries tab now opens with an interactive ring chart of the entire app's disk footprint — every library ≥ 10 MB is its own tappable slice (bpy 425 MB, LaTeX 277 MB, torch 123 MB, …down to faiss 10 MB), with Python runtime, the LaTeX distribution, offline wheels, and app core as honest buckets so the total matches the real install size. Tap a slice to see its share and filter the list to it.

Every package card also shows its true on-disk size (native frameworks correctly attributed — torch counts its 99 MB libtorch_python, bpy its 66 MB USD) plus an orange NATIVE badge on cross-compiled packages; the header reads "N packages · X GB · M native"; and a sort menu offers Largest first. Package detail pages show "N MB on disk".

cpu-z / gpu-z — cross-platform comparable benchmarks in the terminal

Two new shell commands measure the device live (nothing is a lookup table):

  • cpu-z — single-core (Python loop, zlib, memory copy) and multi-core (zlib across all cores) scores on the official CPU-Z bench scale (M1 ≈ 580 single / 3400 multi), plus AMX matmul GFLOPS and SHA-256 as info lines. Directly comparable with Windows/Linux CPU-Z screenshots.
  • gpu-z — Metal device info straight from MTLCreateSystemDefaultDevice (family, Metal level, unified-memory budget) + fp16/fp32 matmul GFLOPS through the torch Metal bridge, scored on the Geekbench 6 Compute scale (M1 ≈ 32k; an M4 posts ~53–56k). -v shows bridge diagnostics.

Fixing gpu-z's initial 0-GFLOPS reading also uncovered and fixed a real bug: the torch→numpy conversion layer was ~20,000× too slow, silently forcing the entire torch Metal bridge onto the CPU. With the fix, torchmetal.enable() genuinely GPU-accelerates torch.matmul (M4: 1400+ GFLOPS fp16) for all user code — see python-ios-lib for that side (which also gained parquet-enabled pyarrow and a Metal-backed moderngl).

How the scores are computed

Every number is measured on your device at the moment you run the command — there is no spec-sheet lookup, no model-name table. The scores are then mapped onto two well-known external scales so you can compare across platforms. They are labeled est. because we cannot run CPUID's or Geekbench's proprietary workloads; instead our own workloads are calibrated against published Apple-silicon anchor scores, and validated against real results (an iPad M4 maps to ≈ 820 single on our scale; official CPU-Z posts the M4 at ≈ 800–830).

cpu-z — workloads

# Benchmark What it measures How One-M1-core reference
1 Python loop interpreter + integer ALU, one core 3,000,000 iterations of acc += i*3 + (i>>2) ^ (i&7) → Mops/s 9.0 Mops/s
2 zlib compress (1 thread) C-speed CPU throughput, build-independent 4 MB high-entropy blob (random.Random(0xC0DE).randbytes, deterministic so every device compresses identical data), level 6 → MB/s 50 MB/s
3 Memory copy (1 thread) memory subsystem 64 MB numpy buffer copied 4×, read+write bytes counted → GB/s 13 GB/s
4 zlib × N threads true multi-core scaling same blob compressed concurrently in cpu_count threads — zlib releases the GIL, so N threads genuinely occupy N cores → aggregate MB/s (scored against the same 50 MB/s single-core ref)

cpu-z — formulas

single_ratio = mean( python/9.0 , zlib_1T/50 , copy_1T/13 )   # vs ONE M1 core
single_score = 580 × single_ratio                              # CPU-Z v17: M1 ≈ 580

multi_ratio  = zlib_NT / 50                                    # aggregate vs one M1 core
multi_score  = 680 × multi_ratio                               # calibrated: M1 (8-core, ≈5× scaling) ≈ 3400

Both scores share one scale (everything is normalized to a single M1 core), which is why multi reads ~3–6× single on a healthy chip — the same relationship Geekbench and CPU-Z show. Multi is scored from the zlib run alone because CPU-Z's multi test scales near-linearly with cores; mixing in bus-bound work would hide core count.

cpu-z — shown but deliberately NOT scored

  • SHA-256 — on Apple silicon this is fixed-function crypto: it measures whether the Python build links a hardware-accelerated hashlib (≈ 3,000 MB/s) or falls back to software (≈ 500 MB/s) — a 6–7× swing that says nothing about the CPU. The line is labeled HW crypto / software path instead.
  • Matmul fp32 (AMX) — Apple's matrix coprocessor via Accelerate. It is neither "a core" nor "all cores", so it gets an info line rather than skewing either score.
  • Memory copy × 4 threads — the memory bus is shared; parallel copies don't scale with cores, so including it would drag the multi score down arbitrarily.

gpu-z — workloads and formula

The info block is read directly from Metal (MTLCreateSystemDefaultDevice via ctypes: device name, MTLGPUFamily, Metal version, unified memory, recommendedMaxWorkingSetSize). The benchmark then runs real torch.matmul calls routed to the GPU by the torch Metal bridge (MPSMatrixMultiplication underneath):

  • Probe-grow sizing — square matmuls at k = 512 → 1024 → 2048; growth stops early if a single call exceeds 0.35 s, then up to 4 repetitions inside a 0.6 s budget. GFLOPS = 2k³·reps / elapsed.
  • Every call is materialized — one element of the result is read back after each matmul, so the timing includes the CPU↔GPU copies both ways. This is intentional: the score reflects what Python code actually obtains, not a marketing TFLOPS figure (an M4 posts ≈ 950 GFLOPS fp32 here against 4.4 TFLOPS theoretical — the copies are the tax).
gpu_ratio = mean( fp16_GFLOPS/800 , fp32_GFLOPS/500 )   # refs = measured M1-class MPS incl. copies
gpu_score = 34000 × gpu_ratio                            # Geekbench 6 Compute: M1 ≈ 32k

Calibration check: a measured M4 ratio of ≈ 1.6 maps to ≈ 53–56k — real Geekbench 6 Metal posts the M4 at ≈ 57k (within ~5 %). Attention (SDPA) is displayed as an info line but not scored: the bridge executes it as a matmul → softmax → matmul chain with a copy at every hop, so its throughput measures bridge dispatch overhead rather than GPU speed. TechPowerUp's GPU-Z has no benchmark at all (it is an info/sensor tool), which is why the cross-platform anchor is Geekbench 6 Compute — the number Windows/Linux GPUs are compared with (OpenCL/Vulkan there, Metal on Apple).

Run-to-run variance of ±10 % is normal for both commands (thermals, background load); cpu-z -q runs a shorter, noisier pass, and gpu-z -i prints the info block without loading torch.

AI Assist chat — ChatGPT-style UI

The in-editor AI chat was rebuilt around a streaming, conversational UI (open it from the editor toolbar):

  • Collapsible "thinking" — a reasoning model's <think> output folds into a pill you can expand; the final answer renders beneath it.
  • Compiled LaTeX / math$…$ and $$…$$ in replies render as native vector glyphs via SwiftMath, inline in the chat bubble (no web view).
  • Code cards — fenced code blocks become titled cards with copy + an inline Run button.
  • File attachments — a 📎 in the composer attaches PDFs / text / source files (shown as chips above the input) for the model to read.
  • Run Python from chat — with Auto-run AI code enabled, Python the assistant emits is executed automatically and its output (text, charts, tables) flows straight into the preview pane.

IntelliSense — full module & member completion

Autocomplete now indexes every importable module (not a hardcoded shortlist) and lazily fetches each module's members / attributes on demand from the live Python daemon — so typing a module name and . lists its real API surface, with kind-aware icons and signature help.

Jupyter notebook editor (.ipynb)

Tap any .ipynb file in the file browser → opens directly in the editor box as a cell-stacked notebook (not raw JSON in Monaco). Each cell has its own toolbar (Run ▶, move ↑/↓, insert +, delete 🗑, code↔markdown toggle 🅼/ƒ). Per-cell Run uses a persistent in-process Python kernel so variables defined in cell 1 are reachable from cell 2.

  • Output capture per cell: stdout, stderr, last-expression result, matplotlib PNG (auto-savefig), HTML (pandas DataFrames, plotly), red error tracebacks
  • Markdown cells render with GFM + KaTeX inline math, tap to switch to edit mode
  • Toolbar at top: ▶ Run All, ↻ Restart Kernel, 💾 Save (writes back to .ipynb JSON in nbformat v4)
  • No modal popup — the notebook lives inside editorContainer, replacing Monaco for the duration of .ipynb editing. Opening a .py swaps Monaco back inline.

Visual debugger

debug-gui script.py in the terminal → a floating toolbar appears at the top of the editor with:

  • Continue · ⏭ Step Over · ⤓ Step Into · ⤴ Step Out · ⏹ Stop
  • 🔍 Variable inspector panel (slides in from right) — live f_locals + f_globals tree, repr-clipped to 200 chars per value
  • Call stack — top 3 frames shown beneath the variables panel
  • Current-line arrow in Monaco's gutter (golden, auto-scrolls into view)
  • Persisted breakpoints (~/.codebench/breakpoints/<file>.bps) honoured at launch
  • Headless fallback — if the UI isn't compiled in (older build), the debugger auto-continues after 3 s so scripts don't hang

Configurable AI providers (⇧⌘,)

The AI subsystem can now dispatch to any of four backends, switched per-request or per-session:

Provider Configure Notes
Bundled GGUF Models tab → tap a .gguf Default, runs through llama.cpp
OpenAI ⇧⌘, → Settings → paste API key Streams chat/completions SSE
Anthropic ⇧⌘, → Settings → paste API key Streams Messages API with system separation
OpenAI-compat ⇧⌘, → set base URL (e.g. http://192.168.1.10:11434/v1) Ollama / vLLM / llama.cpp server

API keys are stored in Keychain (kSecAttrAccessibleWhenUnlockedThisDeviceOnly). Ctrl-C in the terminal cancels in-flight HTTP requests + local generations symmetrically.

Inline matplotlib / pandas display

plt.show() / display(df) / Image.show() / fig.show() from any script — Python or notebook cell — renders inline in the output panel with append semantics. No more "saves to disk, you have to open the file." Backed by a Python-side codebench_inline module that monkey-patches the show/display hooks and ships PNG / HTML artifacts to Swift via inline_*.json signal files.

Vim mode (in Monaco)

Toggle in ⇧⌘, settings. Implements a ~80% subset of vim:

  • Modes: NORMAL · INSERT · VISUAL · V-LINE (status pill bottom-right)
  • Motion: h j k l · w b e · 0 $ ^ · gg G · { } %
  • Insert: i a I A o O
  • Edit: x X r · dd yy cc · d{motion} y{motion} c{motion} · p P · u <C-r> · J · . (last-change repeat)
  • Search: / ? n N
  • Visual: v V then d y c x
  • Cmdline: :w :q :wq :x (:w triggers Monaco save)
  • Counts: 3dd, 5j, 10G, etc.

Persists via localStorage.setItem('codebench.vim.enabled', '1') so it survives editor reloads.

Quick Open (⌘P) + Settings (⇧⌘,)

  • ⌘P — VS Code-style fuzzy file picker. Recent files at the top with "recent · " subtitle, full workspace walk underneath (capped at 5 000 files). Arrow keys + Enter to open.
  • ⇧⌘, — unified settings sheet: AI provider, API key, model name, temperature, max_tokens, vim mode toggle, inline-completion toggle.

Long-press Quick Look in file browser

Right-click / long-press any file in the file browser → "Quick Look" appears at the top of the context menu for:

  • .csv / .tsv → scrollable HTML grid (sticky header, row numbers, alt-row striping; capped at 5 000 × 100 cells)
  • .json → pretty-printed + regex syntax highlighting
  • .yaml / .yml / .toml → monospace pretty-print
  • .png / .jpg / .heic / .webp / .bmp / .tiff / .gif → pinch-zoomable image with dimensions caption
  • .npy / .npz → numpy table view (loaded in-process via the bundled Python; 1-D up to 1000 rows, 2-D up to 500×50)

REPL builtin (repl)

A persistent-namespace Python REPL — variables survive across invocations. Rich displayhook for pandas DataFrames (HTML table inline). Meta-commands :q :clear :show :save FILE. Use repl --keep to preserve the previous session's namespace.

Performance optimizations

Idle wake-ups dropped from ~117/sec to ~1.5/sec by moving three always-on timers to kqueue file-system event sources (DispatchSourceFileSystemObject) with a 2 s safety-net fallback:

Engine Old polling New idle Speedup
PywebviewBridge 10 ms (100 Hz) event-driven 200×
LaTeXEngine 100 ms event-driven 20×
AIEngine 150 ms event-driven 13×

Plus: IntelliSenseEngine 3 s blocking-loop → DispatchSource event watch (no held threads), MonacoEditorView.loadFileBatched collapses 3 JS round trips into 1 (~100 ms saved per file open), C/C++/Fortran Monaco providers lazy-register only on first non-Python file (saves ~210 symbol parses on pure-Python sessions). BackgroundExecutionGuard .sync.async to remove a Swift-Concurrency deadlock vector.

Auto-seeded test bundle (Workspace/Tests/)

On every launch, three test files auto-seed into your Workspace (respecting the .codebench_deleted tombstone so deletes stick):

  • codebench_features_test.py — automated 24-test feature suite for everything above
  • codebench_features_test.ipynb — sample notebook exercising every output type
  • codebench_debug_target.py — small target to step through under debug-gui

User-edited copies are detected by file-head SHA comparison and never overwritten on subsequent launches.


Recent improvements

  • Reliable inline preview for shell-launched pythonpython foo.py from the integrated terminal now displays the resulting matplotlib/plotly chart in the preview pane the same way the Run button does. Previously flaky for several reasons, all fixed:

    • PTY scanner missed [plot saved] /… / [manim rendered] /… markers prefixed with ANSI escape codes (CSI clear-line, prompt redraws). The hasPrefix check is now preceded by stripAnsiEscapes and also handles the [manim rendered] marker.
    • WKWebView.loadFileURL(allowingReadAccessTo:) flaked on macOS Catalyst — the access grant to the sandboxed WebContent process raced the actual load (WebProcessProxy::hasAssumedReadAccessToURL: no access). The HTML branch of showImageOutput now reads the file (up to 20 MB) and uses loadHTMLString(html, baseURL: parentDir), sidestepping the sandbox grant entirely.
    • A blank-HTML preload meant to drop a previous manim video DOM was causing back-to-back async loads for HTML→HTML transitions; the blank's cancellation (NSURLErrorCancelled -999) intermittently propagated into the chart load. Skipped when the next content is also HTML — the new HTML replaces the DOM atomically.
    • The dir-watcher's DispatchSource.makeFileSystemObjectSource(.write) fires on inode create (file size 0) but doesn't re-fire on subsequent content writes into an existing entry. Heavy plotly HTML can take 60–80 s to serialize on iOS Python. A new pollForChartCompletion polls every 0.5 s for up to 120 s once a too-small file is seen, then routes through tryShowChart when size ≥ 4 KB.
    • flush=True added to the embedded _offlinai_*_show print sites so the marker line definitely reaches the PTY.
    • Diagnostic NSLog lines added across the chain: filter Xcode console on [chart-watch] to trace dir-event → poll → load.
  • Inherited from python-ios-lib: matplotlib shim no longer crashes user scripts on chained attribute access (ax.xaxis.line.set_color(...) etc.), and full plotly styling — titles, axis ranges, backgrounds — now applies correctly (was being silently aborted by a __figure__ sentinel leak). See python-ios-lib's recent changes.


What CodeBench adds

Capability How
Monaco code editor (real VS-Code editor) WKWebView-hosted Python IntelliSense — indexes every importable module with lazy member/attribute completion, ~70-entry signature DB, hover docs, auto-resolve from a Python daemon for numpy / scipy / sklearn / matplotlib / sympy. Vim mode (NORMAL/INSERT/VISUAL/V-LINE), inline AI completion (ghost text, 350 ms debounced)
Jupyter notebook editor Cell-stacked .ipynb editor embedded inline in the editor box. Per-cell Run + persistent in-process kernel, output capture (stdout/stderr/PNG/HTML/error), markdown cells with GFM + KaTeX, save back to nbformat v4
Visual debugger Floating toolbar (Continue / Step Over / Step Into / Step Out / Stop) + variable inspector + call stack + golden current-line arrow in gutter. Driven by a Pdb subclass over signal files. Persisted breakpoints
Integrated terminal SwiftTerm backed by a PTY master/slave pair piping into the embedded CPython REPL
pdflatex on-device busytex WASM (pdftex 1.40.25 + xetex + luatex + bibtex8 + xdvipdfmx) running in a hidden WKWebView with TeX Live 2023 packages preloaded into MEMFS. A custom 23 MB overlay adds pgf / tikz / beamer / hyperref / mathtools / microtype / cleveref / fancyhdr / bbm / CJKutf8 / fontspec / ctex and ls-R index
Local + remote LLM chat llama.cpp for GGUF models + ExecuTorch + OpenAI / Anthropic / OpenAI-compat (Ollama, vLLM) HTTP streaming. ChatGPT-style UI: streaming, collapsible thinking, compiled LaTeX, code cards, 📎 file attachments, optional auto-run of AI-generated Python into the preview. Conversation export. API keys in Keychain. Provider switchable via ⇧⌘,
RAG engine In-process vector store for RAG over user-imported docs
Image generation Offline image models via ExecuTorch
Inline rich output plt.show() / display(df) / Image.show() etc. render in the output panel with append semantics — figures, DataFrames, plotly all flow through
Quick Open (⌘P) VS Code-style fuzzy file picker (recent + workspace walk)
Quick Look (long-press file) Inline grid view for CSV/JSON/YAML/TOML/NPY/PNG/JPG without opening Monaco
File browser + tabs iOS document browser with multiple concurrent workspaces
Auto-save Debounced ~600 ms after keystroke, plus on run / tab-switch / view-disappear / app-backgrounding
Tombstone system Files deleted via UI are recorded in <Workspace>/.codebench_deleted so starter-script seeders don't resurrect them on next launch
Auto-seeded test bundle Workspace/Tests/ gets 3 test files on launch (respects tombstone + user-edit detection)

Languages

Language Runtime Main doc
Python 3.14 BeeWare-embedded CPython python-ios-lib README
Jupyter .ipynb Same CPython, cell-stacked editor with persistent in-process kernel (built-in; tap any .ipynb in the file browser)
C Pure-Swift tree-walking interpreter (3.4k LOC, 48 operators, structs, pointers, preprocessor) c-interpreter.md · interpreters.md
C++ Pure-Swift tree-walking interpreter (4.2k LOC, classes, STL, templates, inheritance) cpp-interpreter.md · interpreters.md
Fortran ofort tree-walking interpreter in C (Beliavsky, MIT) via a Swift bridge — modules, allocatable arrays, 45+ intrinsics fortran-interpreter.md · fortran-runtime.md

All four languages share the same Monaco editor + IntelliSense pipeline and auto-save. Python additionally has the dedicated notebook editor for .ipynb files (cell-stacked UI in place of Monaco).


Python libraries — direct links to each library's doc

Every library below is bundled natively on-device. Click the library name to jump to its reference doc in python-ios-lib.

Scientific computing

Library Type Doc
NumPy 2.3.5 Native iOS (arm64) docs/libs/numpy.md · docs/numpy.md
SciPy 1.15.0 Pure Python shim docs/libs/scipy.md · docs/scipy-ios.md
SymPy 1.14.0 Pure Python docs/sympy.md · docs/libs/sympy.md
mpmath 1.4.1 Pure Python docs/mpmath.md

Machine learning

Library Type Doc
PyTorch 2.1.2 (patched) Native iOS (arm64) — full import torch, tensors, autograd, nn, optim, JIT, FFT, distributions. Accelerate-backed linalg docs/libs/pytorch.md
transformers 4.41.2 Pure Python — HuggingFace BERT / GPT-2 / T5 / BART, train + generate on-device docs/libs/transformers.md
tokenizers 0.19.1 Native iOS (Rust) — first public iOS build, real BPE/WordPiece/Unigram trainers, PyO3 bindings docs/libs/tokenizers.md
scikit-learn Pure NumPy (12k+ LOC, 40 modules, 38 metrics) docs/sklearn.md · docs/libs/sklearn.md

Visualization & media

Library Type Doc
matplotlib Native iOS docs/matplotlib.md · docs/libs/matplotlib.md
manim Pure Python docs/manim.md · docs/libs/manim.md
Pillow Native iOS (libjpeg-turbo + zlib) docs/pillow.md
PyAV / FFmpeg Native iOS (libavcodec, libavformat, libavfilter, …) docs/av-pyav.md · docs/libs/media.md
plotly Pure Python docs/plotly.md
svgelements Pure Python docs/svgelements.md
pydub Pure Python (ffmpeg-backed) docs/pydub.md

Utilities

Library Type Doc
networkx Pure Python docs/networkx.md
beautifulsoup4 Pure Python docs/beautifulsoup.md
click Pure Python docs/click.md
jsonschema Pure Python docs/jsonschema.md
PyYAML Pure Python docs/pyyaml.md
pygments Pure Python docs/pygments.md
rich Pure Python docs/rich.md
tqdm Pure Python docs/tqdm.md
Minor libs (requests, dateutil, psutil, watchdog, screeninfo, soupsieve, safetensors, regex, typing_extensions, …) Mixed docs/minor-libs.md

Full top-level reference: python-ios-lib/docs/README.md.


LaTeX (pdflatex)

CodeBench's pdflatex / latex / tex shell commands route to busytex WASM running in a hidden WKWebView. The TeX Live 2023 data packages (texlive-basic + ubuntu-texlive-{latex-base, latex-recommended, fonts-recommended, latex-extra, science}, ~230 MB compressed) preload into MEMFS on first run. A 23 MB overlay data package bundles extra packages pdflatex commonly needs:

  • PGF / TikZ / beamer + themes + translator
  • hyperref + dependencies (kvsetkeys, pdfescape, hycolor, …)
  • mathtools, microtype, cleveref, fancyhdr, geometry, setspace, bbm (+ bbm-macros)
  • CJK + CJKutf8 (CJK family)
  • amscls / amsthm / mathrsfs / booktabs / float / enumitem
  • A kpathsea ls-R index so lookups are O(1)

On top of the pipeline, the Swift-side BusytexEngine adds:

  • Sibling-file collection: scans \includegraphics, \includepdf, \input, \include, \bibliography references, bundles real files + synthesizes 1×1 placeholder PNG/PDF for missing ones so compiles don't abort on a missing figure.
  • Unicode sanitizer: replaces codepoints pdflatex can't render (CJK, symbols past U+0200) with [?] so stray Unicode doesn't fatal-error mid-compile.
  • Auto-lmodern injection: when the doc uses \usepackage[T1]{fontenc} without a T1-capable font family, auto-adds \usepackage{lmodern} so pdftex doesn't try to spawn mktexpk (which needs fork(), unavailable in WASM).
  • Live progress streaming: every pdftex stdout line streams to the terminal as it compiles, plus a Swift-side 3 s heartbeat so you know the engine is alive during long runs.

Full pdflatex doc: media.md#offlinai_latex--local-latex-engine.


Local & remote AI

The AI subsystem (chat, ai builtin, inline code-completion) routes to one of four backends, switchable per-session via ⇧⌘, settings or per-request via the "provider" field in ai_request.json:

Backend Use case
Bundled GGUF (default) Fully offline. Load a .gguf from the Models tab. Runs through llama.cpp / Metal
OpenAI Streaming chat/completions SSE. Just paste an API key — defaults to https://api.openai.com/v1
Anthropic Streaming Messages API. Handles system separation automatically
OpenAI-compat Any endpoint that speaks chat/completions SSE — Ollama, vLLM, llama.cpp server, LM Studio

API keys live in Keychain (kSecAttrAccessibleWhenUnlockedThisDeviceOnly), one slot per provider. Ctrl-C in the terminal cancels in-flight HTTP requests and local generations symmetrically. Inline AI code-completion (ghost text, 350 ms debounced) uses whichever provider is configured.

The chat surface is a ChatGPT-style conversational UI: streaming replies, a collapsible "thinking" pill for reasoning models, inline-compiled LaTeX/math (SwiftMath), copyable code cards with a Run button, 📎 file attachments (PDF / text / source), and — when Auto-run AI code is enabled — automatic execution of the Python the assistant writes, with its output (text, charts, tables) routed into the preview pane.

  • GGUF models via llama.cpp integrated as an XCFramework. Load any Llama / Mistral / Qwen / Phi model, chat with streaming tokens.
  • ExecuTorch backends for Apple-Core-ML / XNNPACK / kernel-optimized inference of PyTorch models.
  • PyTorch → Metal GPU bridge (CodeBench/MetalMatmulBridge.swift) — exposes one @_cdecl C entry point backed by MPSMatrixMultiplication. Python's _torch_metal_bridge.py (in python-ios-lib) reaches it via dlopen(NULL) + dlsym and monkey-patches torch.matmul / mm / bmm / addmm / F.linear / F.scaled_dot_product_attention on every Python startup. Real on-device transformer training in fp32/fp16/bf16 with 2–10× speedup over CPU. Linker flags in OTHER_LDFLAGS (-Wl,-exported_symbol,_cb_metal_*) keep the symbol export-visible through Apple's archive / TestFlight strip pass.
  • LoRA fine-tuning via llama.cpp's Metal backward kernels (separate path from the PyTorch bridge) — see CodeBench/LlamaFinetuner.swift. Trains a LoRA adapter on a GGUF base model in-place.
  • RAG: in-process sentence-embedding + vector store over user-imported text / PDF / markdown.
  • Image generation via ExecuTorch-runnable diffusion-family models.

All models live in the app sandbox; no tokens leave the device.


Shell

The CodeBench shell IS a Python REPL — typing Python executes directly. On top of it there are builtins for POSIX-y operations iOS doesn't give you:

  • File/system: ls, cd, pwd, mkdir, rm, cp, mv, cat, head, tail, grep, find, file, touch, df, du, ncdu, top
  • Languages: python / python3 (with -V / -c / -m / full flag handling), cc / gcc / clang, c++ / g++ / clang++, gfortran / f77 / f90 / f95
  • LaTeX: pdflatex, latex, tex, pdftex, latex-diagnose
  • Markdown / notebook viewers: md / markdown (renders .md to the preview pane via markdown-it-py + KaTeX), nb / ipynb / notebook (read-only .ipynb preview — for the full cell-stacked editor, tap the file in the file browser)
  • AI: ai (chat with the configured provider — bundled GGUF or remote)
  • Debugger: debug (classic pdb in the terminal), debug-gui (floating toolbar + variable inspector in the editor)
  • REPL: repl (persistent-namespace Python with rich displayhook; repl --keep preserves last session's namespace)
  • Test runners: test-libs / test_libs (smoke-test every bundled library)
  • VCS: git clone (via zipball fetch — real Git protocol isn't available sandboxed; HuggingFace hf:// URLs supported via snapshot_download)
  • Package mgmt: pip (install to the per-workspace site-packages dir; smart-skips already-bundled deps)

python --help, python -V, python -c "print(1+1)", python -m pip install … all behave like real CPython.

Keyboard shortcuts

Shortcut Action
⌘P Quick Open file picker (fuzzy match)
⇧⌘, Settings sheet (AI provider, vim mode, inline completion)
⌘/ Keyboard-shortcuts help
⌘A (in terminal) Select all terminal output
⌘C (in terminal) Copy selection
⌘V Paste (Monaco + terminal)
Ctrl-C (in terminal) Interrupt running script / cancel AI generation
Ctrl-D (in terminal) EOF / exit current REPL

Install / build

This repo contains the CodeBench-specific pieces (Swift source, Xcode project, resources, busytex data packages via LFS, build scripts). It depends on python-ios-lib for the runtime layer (Python.xcframework, llama.xcframework, ExecuTorch frameworks, app_packages, Monaco folder, SwiftTerm SPM).

Quickstart (recommended)

git clone https://github.com/yu314-coder/CodeBench
cd CodeBench
./scripts/setup.sh        # clones python-ios-lib + symlinks it in
open CodeBench.xcodeproj

setup.sh clones python-ios-lib into _vendor/python-ios-lib/ and creates symlinks at the workspace root (Frameworks/, app_packages/, Monaco/, Sources/, Package.swift) pointing into the vendored runtime. Both repos keep independent .git histories — pulling updates on either side is just git pull in the corresponding directory. Re-run ./scripts/setup.sh --update to also fast-forward the runtime.

Git LFS auto-pulls the busytex data packages (~244 MB) on clone via the configured filters. If your git client skipped them, run git lfs pull inside CodeBench/Resources/Busytex/ before building.

Manual layout (if you can't use symlinks)

Some setups (Windows-with-WSL, sandboxed CI) don't follow symlinks well. In that case, lay the two repos out side-by-side as one merged workspace:

git clone https://github.com/yu314-coder/python-ios-lib
cd python-ios-lib
git clone https://github.com/yu314-coder/CodeBench _codebench
cp -R _codebench/CodeBench _codebench/CodeBench.xcodeproj _codebench/Info.plist .
cp -R _codebench/scripts/* scripts/ 2>/dev/null || true

Either way, Xcode opens CodeBench.xcodeproj and finds Frameworks/, app_packages/, Monaco/, Sources/, and Package.swift at the workspace root.

Build target

Xcode scheme: CodeBench. Run on a real device, on TestFlight, or via My Mac (Designed for iPad). The first build takes a few minutes; subsequent builds are incremental.

Rebuilding llama.xcframework (optional, advanced)

llama.xcframework is prebuilt and shipped in python-ios-lib/Frameworks/, so the app links against it directly. Only rebuild from llama.cpp source if you need a different upstream version or build flags:

mkdir -p third_party
git clone https://github.com/ggerganov/llama.cpp third_party/llama.cpp
# Then run python-ios-lib/build-xcframework.sh + finish-ios-only.sh
# and replace python-ios-lib/Frameworks/llama.xcframework with the result.

Installed-bundle size

The finished CodeBench.app is ~1 GB:

  • 791 MB Frameworks/ (Python, llama, ExecuTorch xcframeworks — from python-ios-lib)
  • 484 MB app_packages/ (bundled Python site-packages — from python-ios-lib)
  • 254 MB CodeBench/ (Swift source + Resources, of which ~230 MB is the LaTeX data packages — this repo, via LFS)

What's in this repo vs python-ios-lib vs upstream

Verified against gh api /repos/yu314-coder/{repo}/contents/ as of this commit.

This repo (CodeBench) python-ios-lib Upstream only — re-clone if rebuilding
CodeBench/ — Swift source + Resources (busytex data via LFS) Frameworks/ — Python / llama / ExecuTorch / LaTeX xcframeworks third_party/llama.cpp/ggerganov/llama.cpp (~7.6 GB, only needed if rebuilding llama.xcframework)
CodeBench.xcodeproj/ app_packages/ — bundled Python site-packages
Info.plist Monaco/ — Monaco editor WebView bundle
scripts/ — CodeBench-specific build helpers: Sources/ + Package.swift — SwiftTerm SPM integration
  • fetch_busytex_assets.sh (LFS-fallback downloader) docs/ — per-library reference docs (linked from this README)
  • fetch_ios_wheels.py, unpack_wheels.sh, check_wheels.py fix_ffmpeg_paths.sh — install_name_tool rewrite for av.*.framework
  • fix_ffmpeg_paths.sh (mirror) C-lib build dirs: cairo/, cpp/, ffmpeg/, fortran/, gcc/, harfbuzz/, pango/, skia-pathops/
  • test_all_libs.py (smoke test for all 30+ libs) Python-pkg build dirs: numpy_ios/, pandas_ios/, pillow_ios/, psutil_ios/, audioop/, av/, matplotlib/, scipy/, sklearn/, manimpango/, mapbox_earcut/

Files / dirs not in either repo (local-only build scratch, never checked in): third_party/, DerivedData/, build/, .venv/, xcuserdata/. Generated on-demand by the build scripts in python-ios-lib.

Monaco editor: lives in python-ios-lib (14 MB — Monaco/ top-level folder reference). CodeBench's CodeBench.xcodeproj references it at the workspace root, so after you complete the step-2 clone recipe above, the Xcode project finds it.


Acknowledgements

CodeBench stands on:

  • python-ios-lib — the CPython 3.14 runtime and 30+ iOS-ported Python libraries that make the app work
  • BeeWare — the Python.xcframework embedding technique
  • SwiftTerm — xterm-compatible terminal emulator
  • Monaco Editor — VS Code's editor in WKWebView
  • busytex — TeX Live 2023 compiled to WASM
  • llama.cpp / ExecuTorch — local LLM inference
  • SwiftMath — native CoreText math rendering for $…$ inline expressions

License

See LICENSE. Individual dependencies retain their original licenses — consult each project's repo (linked above).

About

Self-contained developer/scientific/AI workstation for iPad and Mac — Python, C, C++, Fortran, pdflatex, local LLMs. All offline. Built on python-ios-lib.

Resources

License

Stars

4 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors