Limits & Compatibility

Every tool has edges. This page shows where Melker's are, what degrades gracefully, what breaks, and how to diagnose it.

Sections 1-2 cover when not to use Melker. Section 3-4 cover terminal compatibility. Sections 5-8 are debugging guides. Section 9 is a real-world case study.

1. When Melker is wrong

Cases where another tool is the better choice.

ScenarioWhy Melker is wrongUse instead
Pixel-perfect design with brand guidelinesTerminal cells are coarse. Colors are theme-dependent.Web app
Long-running daemon or background serviceMelker owns the terminal. No detach/re-attach.systemd + plain CLI
Heavy form input (file upload, drag-drop)No native file picker, no drag-drop.Web app or native GUI
Team has no terminal cultureOnboarding cost is real. Don't force it.Whatever they use now
App needs to run on Windows cmd.exeRelies on ANSI escape codes and Unix TTY.PowerShell or web app
Sub-millisecond rendering (games, video)30 fps ceiling, character-grid resolution.Native GPU app
Go team, Elm architecture preferredMelker is TypeScript (Deno or Node.js).Bubble Tea
Python team, CSS styling wantedDifferent ecosystem.Textual
Rust, no_std, or embedded targetJS runtime too heavy.Ratatui
Need React ecosystem and component libraryMelker has its own component model, not React.Ink
Must ship a single static binaryNo deno compile support. Node.js requires runtime install.Bubble Tea, Ratatui
Need a mature ecosystemMelker is new. Smaller community, fewer examples.Bubble Tea, Ink, Textual

If your use case isn't in this list, Melker is probably fine. The rest of this page is about what happens at the edges.

For a detailed feature-by-feature comparison with other TUI frameworks, see the TUI Comparison.

2. What Melker doesn't have

Feature gaps visible in the ecosystem. Stated plainly.

Missing featureImpactWorkaround
Grid layoutFlexbox only. No CSS Grid.Nested row/column containers
Collapsible sectionsNo built-in accordion/collapsible.Toggle visibility with display: none
Syntax highlightingCode blocks in markdown are plain text.Use a <canvas> with custom rendering
Single binary distCan't deno compile yet.Distribute as .melker file, user installs Deno or Node.js
RTL textNo right-to-left text layout support.Manual text direction in app code
Dependency graphNo fine-grained reactivity (Solid signals, React hooks).createState() + bind for most cases. Explicit setValue() for the rest.

These are design choices, not bugs. Melker trades fine-grained reactivity for a simpler model (createState + bind handles most cases without a dependency graph), and trades grid layout for a smaller layout engine. If any of these are dealbreakers, the table in Section 1 points to frameworks that have them.

3. Terminal compatibility

What works where. Graphics modes, color depth, and resolution.

Graphics mode support

Terminalsextantquadranthalfblocksixelkittyiterm2Notes
Ghosttyyesyesyesnoyesno
Kittyyesyesyesnoyesno
iTerm2yesyesyesyesnoyes
WezTermyesyesyesyesyesyes
Alacrittyyesyesyesnonono
footyesyesyesyesnono
xtermyesyesyesyesnono
KonsoleyesyesyesyesyesyesSixel right-edge quirk
Windows Terminalyesyesyesnonono
VS Code terminalyesyesyesyesnono
RionoyesyesnonoyesUse MELKER_GFX_MODE=iterm2
TERM=linuxnonoyesnononoConsole, no Unicode 13
tmux/screenyesyesyesnononoProtocols auto-disabled
SSHyesyesyesnononoProtocols auto-disabled

Color depth

TerminalTruecolor256-color16-colorDetection method
Most modernyesyesyesCOLORTERM=truecolor
TERM=linuxnonoyesTERM value
Claude sandboxnonograyscaleIS_SANDBOX=yes

Mode resolution comparison

ModePixels/cellResolution (80x24)UnicodeFont support
sextant2x3160x7213.0Most modern mono
quadrant2x2160x481.0Near-universal
halfblock1x280x481.0Near-universal
block1x180x24N/AUniversal
pattern2x3160x72N/AUniversal (ASCII)
sixelnativenativeN/ATerminal-specific
kittynativenativeN/AKitty, Ghostty
iterm2nativenativeN/AiTerm2, WezTerm

4. Degraded rendering

Same app rendered under different graphics modes and color depths. The image degrades. The text stays sharp.

Graphics mode degradation

sextant (2x3, default)
                  ┌────────────────────────────┐
   🬞🬵🬚🬋🬋🬋🬩🬋🬋🬩🬱🬭🬏                              
 🬞🬻🬹🬭🬭🬭🬭🬱🬹🬭🬭🬭🬭🬱🬹🬏  System Status              
 🬻🬻🬦🬦🬹🬹🬏🬏🬭🬞🬵🬹🬓🬓🬴  CPU: 42% Mem: 3.1 GB       
 🬬🬨🬊🬕🬬🬱🬵🬍🬆🬂🬕🬕  Uptime: 14d 6h             
 🬹🬹🬹🬹🬍🬬🬝🬄🬹🬓🬹🬹  Load: 1.2 0.8 0.5          
 🬨🬊🬻🬆🬂🬂🬊🬵🬄🬎                             
 🬊🬬🬂🬁🬂🬀🬂🬂🬂🬂🬁🬂🬀🬂🬆🬆                             
  🬁🬊🬎🬎🬊🬂🬎🬎🬎🬊🬆🬎🬎🬂                              
                                              
                                              
                  └────────────────────────────┘
quadrant (2x2, Unicode 1.0)
                  ┌────────────────────────────┐
                                
   System Status              
   CPU: 42% Mem: 3.1 GB       
   Uptime: 14d 6h             
   Load: 1.2 0.8 0.5          
                              
                              
  ▀▀▀▀▀▀                              
                                              
                                              
                  └────────────────────────────┘
halfblock (1x2, TERM=linux)
                  ┌────────────────────────────┐
   ▀▀▀                              
    System Status              
   CPU: 42% Mem: 3.1 GB       
   Uptime: 14d 6h             
   Load: 1.2 0.8 0.5          
                              
                              
                               
                                              
                                              
                  └────────────────────────────┘
block (1x1, universal)
                                                
                                                
                    System Status               
                    CPU: 42% Mem: 3.1 GB        
                    Uptime: 14d 6h              
                    Load: 1.2 0.8 0.5           
                                                
                                                
                                                
                                                
                                                
                                                

All four are the same app. Melker picks the best mode your terminal supports, or you override with MELKER_GFX_MODE or the gfxMode prop. The image degrades. The box-drawing and text stay sharp in all modes.

Color depth degradation

Same app, same sextant mode, three color depths. This is the constraint that catches people off guard: you think the limit is character-grid resolution, but it's actually color depth.

truecolor (24-bit, default)
                  ┌────────────────────────────┐
   🬞🬵🬚🬋🬋🬋🬩🬋🬋🬩🬱🬭🬏                              
 🬞🬻🬹🬭🬭🬭🬭🬱🬹🬭🬭🬭🬭🬱🬹🬏  System Status              
 🬻🬻🬦🬦🬹🬹🬏🬏🬭🬞🬵🬹🬓🬓🬴  CPU: 42% Mem: 3.1 GB       
 🬬🬨🬊🬕🬬🬱🬵🬍🬆🬂🬕🬕  Uptime: 14d 6h             
 🬹🬹🬹🬹🬍🬬🬝🬄🬹🬓🬹🬹  Load: 1.2 0.8 0.5          
 🬨🬊🬻🬆🬂🬂🬊🬵🬄🬎                             
 🬊🬬🬂🬁🬂🬀🬂🬂🬂🬂🬁🬂🬀🬂🬆🬆                             
  🬁🬊🬎🬎🬊🬂🬎🬎🬎🬊🬆🬎🬎🬂                              
                                              
                                              
                  └────────────────────────────┘
256-color (dithered)
                  ┌────────────────────────────┐
   🬭🬞🬇🬖🬞🬃🬓🬋🬖🬦🬹🬏🬏                              
 🬦🬖🬷🬹🬶🬺🬻🬻🬻🬻🬻🬺🬷🬱▌🬏  System Status              
 ▐🬲🬄🬻🬹🬹🬏🬹🬷🬞🬵🬹🬓🬓🬟🬤  CPU: 42% Mem: 3.1 GB       
 🬉🬷🬗🬊🬕🬬🬱🬵🬜🬆🬂🬀🬝🬐  Uptime: 14d 6h             
 🬹🬺🬞🬹🬫🬬🬝🬄🬻🬹🬚🬉🬹  Load: 1.2 0.8 0.5          
 🬴🬗🬇🬣🬎🬥🬗🬬🬦🬛🬉🬞                             
 🬨🬇🬒🬁🬂🬀🬕🬥🬅🬕🬬🬂🬀🬣🬉🬝                             
  🬂🬁🬎🬄🬉🬎🬄🬉🬁🬃🬈🬁🬎🬀                              
                                              
                                              
                  └────────────────────────────┘
16-color (TERM=linux)
                  ┌────────────────────────────┐
   ▄▄ ▀▀▀▀▀░░ ▄▄                              
  ▀▀░░░░░░░░░░░▀▀  System Status              
 ░░░░▀▀▀▀░░░  CPU: 42% Mem: 3.1 GB       
 ░░▀▀▀▀▀▀▀░░  Uptime: 14d 6h             
 ░░▀▀▀▀▀▀▀░░  Load: 1.2 0.8 0.5          
 ░░░░▀▀██░░░                             
 ▀░▀▀░░░░▀▀▀░▀                             
  ░░░░░░░░░░░░                             
                                              
                                              
                  └────────────────────────────┘

Same sextant mode, same resolution. The 256-color version uses dithering to approximate truecolor gradients. The 16-color version (what you get on TERM=linux) uses shade characters (░▒▓) to simulate tonal range with only 16 colors. Override with COLORTERM=truecolor if your terminal actually supports it.

View source

5. Debugging layout

Common layout problems, what causes them, and how to fix them.

5a. "My component is 0x0"

Symptom: nothing visible, no error.

<img> and <canvas> need width/height as props, not style. Props define the pixel buffer. Style defines layout positioning. They are separate concepts.

Wrong
<img src="photo.png" style="width: fill; height: fill" />
Right
<img src="photo.png" width="fill" height="fill" />

Melker logs a runtime warning for this. The --lint flag also catches it.

5b. "Text is clipped"

Symptom: text cuts off at container edge.

Parent container has no overflow: scroll and content exceeds fixed height. Scrollable containers also need a size constraint (flex: 1 or fixed height), otherwise they grow to fit content and never scroll.

Wrong
<container style="overflow: scroll">
  <text>Long content...</text>
</container>
Right
<container style="overflow: scroll; flex: 1; width: fill">
  <text style="text-wrap: wrap; width: fill">Long content...</text>
</container>

5c. "Select stretches to full width"

Symptom: <select> or <combobox> fills the entire row.

Default cross-axis stretch in column flex layout. Standard flexbox behavior: in a column container, align-items defaults to stretch.

Fix: wrap in a row container or add style="align-items: flex-start" to the parent.

5d. "Emojis break my layout"

Symptom: columns misaligned, text wraps at wrong position.

Emojis have inconsistent widths across terminals. Melker calculates emoji width as 2 characters, but some terminals render them wider or narrower.

Fix: use ASCII text instead. [OK] Success instead of emoji checkmark.

5e. Props vs style by component

ComponentFixed sizeResponsive/fill
canvaswidth={30} height={20}N/A (buffer must be fixed)
imgwidth={30} height={20}width="fill" or width="100%"
containerstyle="width: 30;"style="width: fill;"
textstyle="width: 40;"style="width: fill;"
selectwidth={20}width="50%" or width="fill"

6. Debugging state

State binding, exported value copies, and two-way binding traps.

6a. How state binding works (and where it stops)

Melker has an optional state binding system via $melker.createState(). Bind an element to a state key with bind="key", and state values push to elements automatically on every render. Two-way binding (the default) also syncs user input back to state without manual handler code:

State binding
<input id="query" bind="searchTerm" />
<text bind="count" bind-mode="one-way" />

<script>
  const state = $melker.createState({ searchTerm: '', count: 0 });
  // state.searchTerm auto-updates as the user types (two-way)
  // state.count pushes to the text element (one-way)
</script>

Boolean state keys also toggle CSS classes on the root element, so conditional styling works without script:

Boolean class toggle
.isEmpty #empty-message { display: flex; }

What it doesn't have: no dependency graph, no computed properties, no fine-grained reactivity (Solid signals, React hooks). If element A's value depends on a computation involving state keys X and Y, you write that computation in a handler. For most apps (5-15 interactive elements), createState() + bind covers most cases. For apps that don't use it, the system is skipped (early-exit guard).

6b. "I set $app.count but nothing changed"

Symptom: $app.count = 10 in a ready script, but the original variable stays at 0.

Exported primitive variables are copied by value onto $app. Setting $app.count modifies the copy, not the module binding.

Fix: use setter functions
<script>
  export let count = 0;
  export function setCount(n) { count = n; }
</script>

<script async="ready">
  $app.setCount(10);  // modifies the original
</script>

Objects are copied by reference, so $app.config.debug = true does modify the original. Only primitives (numbers, strings, booleans) have this problem.

6c. "Two-way binding loses my transformation"

Symptom: handler normalizes input (e.g., trim().toLowerCase()), but the value reverts on next render.

Two-way binding is the default. Before each render, reverse sync reads the raw input value back into state, overwriting the handler's normalized result.

Fix: use one-way binding
<input bind="query" bind-mode="one-way" onInput="$app.normalize()" />
<script>
  const state = $melker.createState({ query: '' });
  export function normalize() {
    state.query = $melker.getElementById('query').getValue().trim().toLowerCase();
  }
</script>

Rule of thumb: if your handler modifies the same state key that the element is bound to, use bind-mode="one-way".

7. Debugging permissions

Silent failures from missing policy entries.

7a. "Fetch silently fails"

Symptom: $melker.fetch() returns an error or hangs. No obvious message.

The policy is missing a "net" permission for the host.

Diagnosis: run with --log-level DEBUG --log-file /tmp/app.log, look for Access denied.

Fix: add net permission
<policy>{"permissions": {"net": ["api.example.com"]}}</policy>

Or use --trust for quick testing (bypasses all policy checks).

7b. "Environment variable is undefined"

Symptom: $ENV{MY_VAR} is empty, or Deno.env.get('MY_VAR') returns undefined.

Env vars are sandboxed. Only MELKER_*, HOME, PATH, TERM, and XDG vars are auto-allowed.

Fix: add to policy "env": ["MY_VAR"] or use configSchema's env key for user-configurable values.

8. Debugging terminal

Font issues, protocol fallback, and sandbox detection.

8a. "Sextant characters show as boxes"

Symptom: rectangles or tofu instead of smooth 2x3 block patterns.

Terminal font lacks Unicode 13.0 Symbols for Legacy Computing (U+1FB00-U+1FB3F).

Diagnosis
melker --test-sextant

Fix: switch to a font with sextant support (Cascadia Code, Iosevka, JetBrains Mono), or override:

Override graphics mode
MELKER_GFX_MODE=quadrant melker app.melker

8b. "Sixel images don't appear"

Symptom: blank area where image should be.

Running inside tmux/screen (protocol auto-disabled), or terminal doesn't support sixel.

Diagnosis: check --log-level DEBUG for sixel disabled messages.

Fix: run outside multiplexer, or use character-based modes instead.

8c. "Colors look wrong in Claude Code sandbox"

Symptom: everything is grayscale, backgrounds invisible.

The sandbox terminal reports TERM=linux but only renders brightness, not hue. Background colors are ignored.

What the engine does: detects IS_SANDBOX=yes and switches to grayscale-compatible rendering.

9. Limits in practice: earthquake dashboard

A real app that hit several limits from sections 1-8. What happened, what worked around it.

The earthquake dashboard is a 431-line single-file app that fetches live USGS earthquake data, renders it in a sortable table with sparklines and distribution bars, and plots quakes on a world map. It was built across 8 commits over three weeks (Feb 12 to Mar 7 2026). This section traces which limits from the rest of this page showed up during development, and how they were handled.

What was easy

What was awkward

What surprised

Color resolution matters more than spatial resolution. The dashboard looks good in truecolor terminals, but under TERM=linux (16 colors) the map and magnitude coloring lose most of their information density. You think the limit is character-grid resolution, but it's actually color depth.

Deep dive: How It Works · Dashboard source