Section 01: oriterm Architecture Analysis

Status: Research Complete Goal: Comprehensive mapping of oriterm_ui’s architecture showing what transfers directly to ori_ui, what needs adaptation, and what gaps must be filled.

Context: The ori_term project at /home/eric/projects/ori_term/ contains a production-grade GPU UI framework (oriterm_ui) built in Rust. It is already GPU-agnostic — the layout engine, widget system, animation, interaction management, and event dispatch have zero GPU dependencies. The terminal-specific parts (terminal grid rendering, PTY integration) are confined to oriterm_core and oriterm/src/gpu/prepare/. This means the framework is already general-purpose in architecture — it just doesn’t know it yet.


01.1 Workspace Structure

Source: /home/eric/projects/ori_term/

The workspace has 5 crates with one-way dependency flow:

CratePurposeGPU dependency?Transfers?
oriterm_coreTerminal emulation (grid, VTE, selection, search)NoneNo — terminal-specific
oriterm_muxMultiplexing (split trees, domains, IPC, sessions)NoneNo — terminal-specific
oriterm_uiGPU-agnostic UI framework (layout, widgets, animation)NoneYes — 100%
oritermMain GUI app (GPU rendering, windowing, PTY, fonts)wgpuPartially — GPU pipeline
oriterm_tuiTUI client (terminal-in-terminal)NoneNo — terminal-specific

Key insight: oriterm_ui is the transfer target. It has zero GPU, zero terminal, zero platform dependencies. Everything in it is pure computation.


01.2 Layout Engine — 100% Transferable

Source: oriterm_ui/src/layout/

Current Capabilities

AbstractionCSS EquivalentFile
LayoutBox + Direction::Row/Columndisplay: flex; flex-directionlayout_box.rs
Align (Start/Center/End/Stretch)align-itemsflex.rs
Justify (Start/Center/End/SpaceBetween/SpaceAround)justify-contentflex.rs
SizeSpec::Hugwidth: fit-contentlayout_box.rs
SizeSpec::Fillflex: 1 / width: 100%layout_box.rs
SizeSpec::Fixed(f32)width: Npxlayout_box.rs
min_width/max_width/min_height/max_heightSame CSS propertieslayout_box.rs
padding/margin via InsetsSame CSS propertieslayout_box.rs
gapgapflex.rs
GridColumns::Fixed(n) / AutoFillgrid-template-columnsgrid_solver.rs
clip / overflowoverflow: hiddenlayout_box.rs
LayoutConstraintsFlutter’s BoxConstraintsconstraints.rs

Architecture

  • Input: LayoutBox (declarative descriptor tree — sizing, padding, margin, flex/grid mode, children)
  • Solver: compute_layout(box, constraints) -> LayoutNode — pure function, two-pass (measure → distribute)
  • Output: LayoutNode (resolved Rect per widget, clip rects, widget IDs)
  • Key rule: Widgets NEVER call compute_layout() — the framework does. Widgets only produce LayoutBox descriptors.

CSS Gaps (must be added for full CSS support)

FeatureCurrentNeeded
position: absolute/relative/fixed/stickyNot implementedRequired for overlays, modals, tooltips
z-index stacking contextsNot needed (flat terminal)Required for layered UI
Margin collapsingNot implementedCSS spec requires it
flex-wrapNot implementedRequired for responsive layouts
flex-grow/flex-shrink/flex-basis as separate propsSimplified (Fill/Hug/Fixed)CSS spec has finer control
Percentage unitsNot implementedwidth: 50%
calc()Not implementedwidth: calc(100% - 32px)
CSS Grid full track sizing (fr, minmax, auto-fill/fit)Partial (Fixed/AutoFill only)Full spec
CSS Grid named areasNot implementedNice-to-have

Assessment: The foundation is architecturally correct. Adding CSS features is additive — the solver structure, constraint model, and two-pass algorithm are right. The gaps are features, not redesigns.


01.3 Scene / Draw System — 100% Transferable

Source: oriterm_ui/src/draw/

Scene Structure

Scene {
    quads: Vec<Quad>              — filled/bordered/shadowed rectangles
    text_runs: Vec<TextRun>       — pre-shaped glyph runs
    lines: Vec<LinePrimitive>     — line segments
    icons: Vec<IconPrimitive>     — monochrome atlas icons
    images: Vec<ImagePrimitive>   — textured rectangles

    clip_stack: Vec<Rect>         — internal state
    offset_stack: Vec<(f32, f32)> — internal state
    opacity_stack: Vec<f32>       — internal state
    layer_bg_stack: Vec<Color>    — internal state
}

Key design: Type-separated primitive arrays. Each primitive carries a resolved ContentMask (clip + offset + opacity baked in). The GPU consumer just iterates typed arrays — no stack interpretation at render time.

Drawing API: push_rect(), push_quad(), push_border(), push_text(), push_line() + stack commands (push_clip(), push_offset(), push_opacity() and pop_*() counterparts).

Primitives for general UI that need addition:

  • Rounded rectangle (SDF-based corner rounding)
  • Box shadow (Gaussian or SDF shadow)
  • Gradient fills (linear, radial, conic)
  • Blur / backdrop filter (multi-pass)
  • Rounded-rect clipping (SDF clip in shader)

01.4 Widget System — 90% Transferable

Source: oriterm_ui/src/widgets/ (105+ files)

Widget Trait

pub trait Widget {
    // Core
    fn id(&self) -> WidgetId;
    fn is_focusable(&self) -> bool;
    fn sense(&self) -> Sense;                    // What interactions matter
    fn hit_test_behavior(&self) -> HitTestBehavior;

    // Lifecycle
    fn layout(&self, ctx: &LayoutCtx<'_>) -> LayoutBox;
    fn prepaint(&mut self, ctx: &mut PrepaintCtx<'_>);
    fn paint(&self, ctx: &mut DrawCtx<'_>);
    fn lifecycle(&mut self, event: &LifecycleEvent, ctx: &mut LifecycleCtx<'_>);
    fn anim_frame(&mut self, event: &AnimFrameEvent, ctx: &mut AnimCtx<'_>);

    // Events
    fn controllers(&self) -> &[Box<dyn EventController>];
    fn on_input(&mut self, event: &InputEvent, bounds: Rect) -> OnInputResult;
    fn on_action(&mut self, action: WidgetAction, bounds: Rect) -> Option<WidgetAction>;

    // Composition
    fn for_each_child_mut(&mut self, visitor: &mut dyn FnMut(&mut dyn Widget));
}

Transferable Widgets

WidgetTransfers?Notes
Container (flex/grid)DirectGeneric layout container
ButtonDirectClick handling + visual states
Label / RichLabelDirectText display
TextInputDirectText editing with cursor/selection
Checkbox / ToggleDirectToggleable state
Dropdown / MenuDirectOverlay-based selection
Scrollbar / ScrollNeeds adaptationSmooth scroll physics needed
DialogDirectModal overlays
Panel / SettingsPanelDirectMulti-section containers
TabBarDirectTab navigation
NumberInputDirectSpinner control
Terminal grid widgetDoes not transferTerminal-specific
PTY panelDoes not transferTerminal-specific

Event System

Two-phase dispatch: Capture (root → leaf) then Bubble (leaf → root). Composable controllers: ClickController, DragController, HoverController, ScrollController, FocusController, TextEditController — attached to widgets, not inherited. Framework-managed state: InteractionManager tracks hot/active/focus per widget. Widgets declare what they sense (Sense::click(), Sense::hover()) but don’t manage state.


01.5 Animation System — 100% Transferable

Source: oriterm_ui/src/animation/

  • Lerp trait: Linear interpolation for f32, Point, Size, Rect, Transform2D, Insets, Color
  • Easing: Linear, EaseIn/Out/InOut, CubicBezier with Newton’s method solver
  • AnimProperty: Embeddable in widgets, manages active animation lifecycle
  • VisualStateAnimator: Couples visual states (Normal/Hovered/Pressed/Disabled) to property animations
  • AnimationGroup: Multi-property transitions with timing coordination

All pure math. Zero terminal dependency.


01.6 GPU Pipeline — 80% Transferable

Source: oriterm/src/gpu/

Architecture: Extract → Prepare → Render

  1. Extract (gpu/extract/): Lock snapshot, copy frame data, unlock. Zero mutation.
  2. Prepare (gpu/prepare/): Pure CPU — convert terminal grid + UI scene to GPU instances. Testable without GPU.
  3. Render (gpu/window_renderer.rs): wgpu submission.

Current GPU Pipelines

PipelineInstance SizePurpose
Background96 bytesSolid-color quads
Foreground (R8)96 bytesMonochrome atlas glyphs
Subpixel FG (RGBA)96 bytesLCD subpixel text (ClearType-style)
Color FG (RGBA)96 bytesEmoji/color glyphs
UI Rect144 bytesUI widget rectangles

Rendering Features Already Built

  • Glyph atlas with guillotine packing (multi-page, 2048x2048)
  • Subpixel positioning with fractional offset quantization
  • sRGB ↔ linear conversion for correct color blending
  • Per-row damage tracking, instance caching, skip-present when idle
  • Mica/Acrylic transparency (Windows), vibrancy (macOS)
  • Embedded WGSL shaders with vertex pulling via @builtin(vertex_index)

Rendering Features Needed for General UI

FeatureStatusDifficulty
Rounded cornersNot in terminal (brutalist)Low — SDF shader, well-documented
Box shadowsNot neededMedium — Gaussian blur pass or SDF
Gradients (linear/radial/conic)Not neededLow — fragment shader
Proportional textNot in terminal (monospace only)High — HarfBuzz + line breaking
Rich inline textNot neededMedium — run-level shaping
SVG / vector graphicsNot neededMedium-High — Vello or tessellation
Blur / backdrop filterNot neededMedium — Kawase or Gaussian multi-pass
Rounded-rect clippingNot neededLow — SDF clip in shader

The biggest gap is proportional text. Monospace text is trivial (fixed-width grid). Variable-width fonts require: HarfBuzz shaping, line breaking (Unicode UAX #14), bidirectional text (UAX #9), font fallback chains. This alone is 3-4 months of work.


01.7 Transfer Summary

CategoryLines (Rust)Est. Lines (Ori)Transfer %
Layout engine~2,000~1,500100% (port)
Scene/draw system~800~600100% (port)
Widget framework~5,000~4,00090% (port, drop terminal widgets)
Animation~500~350100% (port)
Interaction/events~1,200~900100% (port)
GPU pipeline arch~3,000~1,50080% (adapt for Ori FFI)
Total~12,500~8,850

New code needed (not in oriterm):

  • CSS layout gaps (position, flex-wrap, fr units, etc.): ~1,500 lines Ori
  • Proportional text rendering wrapper: ~800 lines Ori
  • Rich scene primitives (rounded rects, shadows, gradients): ~500 lines Ori
  • Reactive state management: ~600 lines Ori
  • Capability-based theming: ~400 lines Ori

Total estimated: ~12,650 lines Ori for the full framework.