---
title: "kScript (legacy) construct map"
description: "Every kScript (legacy) construct beside the Indicator form that replaces it, grouped by lifetime: a value on every bar, a snapshot of the finished run, or an…"
order: 91
section: "migrations"
---

<!-- source: docs/indicators/migrations/construct-map.md; generated by packages/cli/scripts/gen-indicator-docs.ts, do not edit -->

# kScript (legacy) construct map

Every kScript (legacy) construct beside the Indicator form that replaces it, grouped by lifetime: a value on every bar, a snapshot of the finished run, or an object that keeps its identity while you move it. [From kScript](./from-kscript.md) covers the numeric core of a port; this page carries the rest: plots and style words, text and tables, profiles and panels, drawing objects and their mutation, pane anchors, frame snapshots, sources, and the constructs with no home yet.

The same AssemblyScript module compiles once and runs on your machine and in
the chart tab; `source: "series"` inputs require the daemon.

In code-first packages, declarations in `src/indicator.ts` produce the sheet
at `wrun/metadata.json`. Import declarations from `./sdk/declare`, read named
`in_*` accessors in `state()`, and write `out_*` values followed by `emitRow()`
in `finalize()`. Numeric outputs remain metrics, including data-only outputs.

The current line declaration is `output("close", line, overlay)`. There is
no `out.line` declaration in this SDK; `out.inset` is available. A table cell
marked **sheet only** has no equivalent code-first declaration today.

## Time series, shapes and backgrounds

Declare the presentation once, then write one numeric value per ready bar.

| kScript | Sheet field | Code-first call | Difference |
| --- | --- | --- | --- |
| `plot(value, plotType=...)` | `outputs[].plot` and `.panel` | `output(name, line, overlay)` or the matching plot identifier | Kind is fixed in the declaration; `overlay` draws on price, `lower` in the script pane. The browser places the whole package by its first drawn output (or the sheet `overlay` override): numeric outputs of one package cannot split across panes. |
| `plotLine(value, ...)` | `outputs[]: { plot: "line", color, width, line_style, opacity }` | `output(name, line, overlay, options)` | One number per bar; no `smooth` or `glow` output option. |
| `plotLine(value, fill=true)` | `ranges[]: { upper, lower }` over two outputs | `range(upperName, lowerName, options)` | The browser draws a filled band between two declared outputs; a single `plot: "area"` output renders as an unfilled line today (no overlay area renderer). |
| `plotBar(value, ...)` | `outputs[].plot: "bar"` | `output(name, bar, lower)` | One scalar per bar; a `[low, high]` span becomes a `box()` instead. |
| `plotHistogram(value, base=...)` | `outputs[].plot: "histogram"` | `output(name, histogram, lower)` | Baseline is zero; emit distance from a baseline or use `box()` for an absolute span. |
| `plotCandle([o,h,l,c], ...)` | `boxes[]` body plus `segments[]` wick | `box(name, { top, bottom, ... })` and `segment(name, { yFrom, yTo, ... })` | No four-output OHLC binding, even though `plot: "candle"` is accepted. |
| `plotShape(value, shape, ...)` | `renderers[]: { kind: "shape", output, shape, where? }` | `render.shape(name, { output, shape, where })` | One mark per ready bar; `where` is a finite, nonzero gate. |
| `plot(plotType="point")` | `renderers[]: { kind: "shape", output, shape }` | `render.shape(name, { output, shape: "circle" })` | One mark per ready bar at the output value; a `plot: "scatter"` output renders as a connected line today. Use `panel.scatter` for numeric x/y pairs. |
| `hline(value, ...)` | `outputs[].plot: "line"` | `output(name, line, lower)` and `out_<name>(value)` on every ready bar | No separate horizontal-line primitive; an extended line handle also works. |
| `plotBgColor(color, forceOnChart?)` | `renderers[]: { kind: "bgcolor", where, color?, color_by?, colors? }` | `render.bgcolor(name, { where, color, color_by, colors })` | Per-bar background tint; a gate selects the bar. No `forceOnChart` option on this renderer. |
| `barcolor(color)` | No candle-recolor field | No direct call; `render.bgcolor(...)` is the nearest tint | Background tint does not recolor candle bodies. |
| `plotBatches(count, fn)` | No batched plot field; `handles`, `levels[]` or `panels[]` serve different lifetimes | Loop over handle calls, or declare `plot.levels(...)` / `panel.*(...)` | Handles keep a bounded live set; frames replace a snapshot, not a history of batches. |
| `plotPie(value, price, radius, ...)` | No per-bar pie field; `panels[].kind: "pie"` is a snapshot alternative | `panel.pie({ name, title, x: "category", place, frame })` | A panel pie has up to 24 slices and no bar-time/price/radius placement. |
| A compact `plotBar` / `plotLine` strip | `outputs[]: { plot: "inset", inset: { dock, height_px?, shape? } }` | `out.inset(name, { dock: "bottom", height_px: 28, shape: "histogram" })` | Keeps every ready row in a pane strip; 16..120 px tall, top or bottom, histogram/area/line. |

`render.shape` supports `circle`, `cross`, `triangle_up`, `triangle_down`,
`diamond`, `arrow_up`, `arrow_down`, `flag` and `square`. kScript's character
marks use text instead. For above/below-bar marks, compute the y output;
for fixed pane placement, use anchored handles.

### Style words

| kScript | Sheet field | Code-first call or option | Difference |
| --- | --- | --- | --- |
| `colors` + per-bar `colorIndex`; `colorGradient(...)` / `palette(...)` used to choose a color | `outputs[].color_by` + `.colors` | `output(name, line, overlay, { color_by: "tone", colors: [...] })` | Emit the palette index as another numeric output; there is no built-in gradient/palette helper. |
| Per-bar `width` | `outputs[].width_by` + `.widths` | `output(name, line, overlay, { width_by: "weight", widths: [...] })` | A ladder of 1..10 widths, each 0.5..20; no arbitrary per-bar width field. |
| `lineStyle`, constant opacity | `outputs[].line_style`, `.opacity` | `output(name, line, overlay, { line_style: "dashed", opacity: 0.6 })` | Static series style; handle setters can change individual objects. |
| `label`, `desc`, `forceOnChart` | Output `name`, `description`, `panel` | `output(name, line, overlay, { description })` | Placement belongs to each output; there is no script-wide plot call. |
| Redraw-only style inputs | `params[].style: { output, property }` | **Sheet only** | A style parameter changes presentation without reaching the module; compute parameters remain numeric. |
| `zOrder`, `set_zorder` | Handle `zOrder` after mutation; widget `z` | Handle `.zorder(n)`; `draw.card`, `draw.feed`, `draw.meter` option `z` | Numeric plots and declared coordinate drawings have no general `zOrder` option. |

Palette and width indexes are floored; a finite out-of-range index selects
entry zero. A non-finite width index keeps the static width. A non-finite
background color index selects no tint. Names of palette and gate outputs
must differ from the output they control.

## Text, labels and tables

String slots carry text per bar; the renderer decides which rows remain visible.

| kScript | Sheet field | Code-first call | Difference |
| --- | --- | --- | --- |
| `plotText(text, price, ...)` | `string_slots[]` plus `renderers[]: { kind: "text", y, text, color?, size? }` | `string(slot, { max_bytes })`; `render.text(name, { y, text: slot, color, size })` | One mark for each ready row with a present slot; even a present empty string is a write. |
| `plotPriceLabel(price, text, ...)` | Text renderer with `style: "price_label"` | **Sheet only** for `style`; `render.text` declares plain text | `wrun-3` or later; no tooltip, URL/SVG icon or pill-position options. |
| `plotLabel(text, position, x, y, ...)` | `renderers[]: { kind: "label", text, position }`, or `handles.label.anchor` and `handles.label.align` | **Sheet only** for renderer `position`; `handles.label({ text: slot, anchor: "top_left", align: "left" })` for movable pixel placement | Renderer keeps the latest nonempty text; a handle copies text when drawn, supports pixel offsets, and `align` picks which text edge sits on x. |
| `plotTable(data, position, ...)` | `renderers[]: { kind: "table", rows, cols, cells, position }` | `render.table(name, { rows, cols, cells, position })` | Snapshot from the latest ready row with every cell slot present; up to 32 rows and 8 columns, within the shared 64-slot limit. |
| `table.new(...)`, `setCell(...)` | `panels[]: { kind: "table", frame, series, ... }` or the table renderer above | `panel.table({ name, title, x: "category", place, series, frame })` | Write the whole frame instead of mutating cells; styled cells support text, color, an inline bar and spark. |
| `plotStatRow(value, title, format, polarity, priority)` | `renderers[]: { kind: "stats_row", output, title?, format?, polarity? }` | `render.stats_row(name, { output, title, format, polarity })` | Per-bar strip values, including NaN gaps; no `priority` option. |
| A small `plotTable` / `plotTiles` status card | `drawings[]: { kind: "card", title, rows, anchor?, offset?, z?, state_by? }` | `draw.card(name, { title, rows, anchor, offset, z, state_by })` | Up to 8 cards with 12 rows each; latest complete ready row wins, with an incomplete-row fallback. |
| A chart inside a table cell / card row | Panel cell `spark: [values...]`; card row `spark: { output, window }` | `panel.table(...)` frame cell; `draw.card(...)` row `spark` | Frame sparks contain 1..64 finite numbers; card sparks select the last 2..64 ready output values. |

A text renderer's size is 6..64 integer pixels. At most 64 string slots
share the sheet, each with `max_bytes` of 1..4096. Write through generated
`str_<slot>()` or the reusable `sb_*` builder and `str_<slot>_sb()`.
Omitting a string write is different from writing an empty string.

## Profiles, panels and compact widgets

Frames carry the whole snapshot, including category or numeric axes independent of bar time.

| kScript builtin or idiom | Sheet field | Code-first call | Difference |
| --- | --- | --- | --- |
| `plotBatches` of `plotBar`, `plotShape`, `plotRange` or `plotPriceLabel` used as a docked profile | `levels[]: { name, frame, dock, width_frac?, poc?, labels?, color? }` | `plot.levels({ name, frame, dock: "right", width_frac: 0.12 })` | One docked snapshot, up to 512 strictly monotonic prices; width fraction 0.05..0.5. |
| `plotCurve(..., style="bars")` | `panels[].kind: "bars"` | `panel.bars({ name, title, x, place, series, frame, orientation?, stacked? })` | Up to 8 series and 2000 rows; keys are categories, integer indexes or epoch seconds. |
| `plotCurve(..., style="line")` | `panels[].kind: "line"` | `panel.line({ name, title, x, place, series, frame, stacked? })` | Snapshot lines over category/index/time keys; arbitrary fractional x uses scatter. |
| `plotCurve` / `plotShape` used for numeric x/y points | `panels[].kind: "scatter"` | `panel.scatter({ name, title, x: "index", place, frame })` | `[key, x, y, size?, color?, label?]`; x/y are finite numbers and each point may carry style. |
| `plotCurve(..., style="bars")` used for a distribution | `panels[].kind: "histogram"` | `panel.histogram({ name, title, x: "category", place, frame, bins?, orientation? })` | Supply `[binLabel, nonnegativeCount]` rows; `bins` is 2..200, not automatic binning of raw samples. |
| `plotPie(...)` shown as one summary | `panels[].kind: "pie"` | `panel.pie({ name, title, x: "category", place, frame, hole? })` | Snapshot slices `[name, nonnegativeValue, color?]`; at most 24, hole 0..0.8. |
| `plotMatrix(...)`; heatmaps made from colored `plotCandle` cells | `panels[].kind: "heatmap"` | `panel.heatmap({ name, title, x: "category", place, frame })` | Up to 2000 `[xKey, yKey, valueOrNull]` cells; no separate summary/highlight fields. |
| `plotTable(...)`, `table.new(...)` as a side data table | `panels[].kind: "table"` | `panel.table({ name, title, x: "category", place: "side", series, frame })` | At most 32 rows and 8 columns; row cells match `series` exactly, with no extra key cell. |
| `plotTiles(...)` | `panels[].kind: "tiles"` | `panel.tiles({ name, title, x: "category", place, frame })` | At most 24 `[label, value, caption?, color?, spark?]` tiles; no columns/accent/format declaration. |
| `plotMiniChartGrid(...)` | No OHLC mini-grid field; `panels[].kind: "line"` or a card spark provides a simpler view | `panel.line(...)` or `draw.card(...)` | Panels use `below`/`side`, not arbitrary viewport grid geometry; no candle-grid binding. |
| `plotTable` / `plotPriceLabel` used for an order ladder | `drawings[]: { kind: "ladder", frame, side, divider? }` | `draw.ladder({ name, frame, side: "right", divider? })` | One snapshot with 1..64 `[price, value, fraction, color?]` rows; fraction is 0..1. |
| `plotText` / `table.new` used as a recent-events feed | `drawings[]: { kind: "feed", frame, anchor?, offset?, z? }` | `draw.feed({ name, frame, anchor, offset, z })` | Snapshot of 1..50 `[epochMilliseconds, text, color?]` lines; text 1..80 characters. |
| `plotShape` / `plotTable` used as a progress gauge | `drawings[]: { kind: "meter", label, fraction: { output }, ramp, text?, ... }` | `draw.meter({ name, label, fraction: { output }, ramp, text, anchor, offset, z })` | Latest ready numeric output, not a frame; produce 0..1 and choose 2..5 ramp colors. |

Every panel binds a declared `frame` and uses `place: "below"` or `"side"`.
At most eight panels and four level profiles share a sheet. Bars, line and
table require 1..8 `series` entries; the other panel kinds forbid `series`.
Histogram and heatmap require `x: "category"`.

Panel table cells accept a number, a string of at most 64 characters, or
`{ text?, color?, bar?, spark? }`. A cell bar is a fraction in 0..1.
Cards, feeds and meters default to `anchor: "top_right"`, `offset: [0, 0]`
and `z: 0`; offsets are integers in -200..200. Ladder placement uses `side`.

## Drawing objects and mutation

Declare each handle kind once, create objects with distinct ids, and reuse those ids to move or delete them.

| kScript | Sheet field | Code-first declaration and runtime call | Difference |
| --- | --- | --- | --- |
| `line.new(x1,y1,x2,y2,opts)` | `handles.line` | `handles.line({ panel: "overlay" })`; `draw.line(id).set(x1,y1,x2,y2)` | Chart x uses epoch seconds, not kScript milliseconds; extension past the loaded range is allowed. |
| `box.new(left,top,right,bottom,opts)` | `handles.box` | `handles.box({ color, borderColor, opacity })`; `draw.box(id).set(left,top,right,bottom)` | Persistent object; `boxes[]` / `box(...)` instead produces one declarative shape per bar. |
| `label.new(time,price,text,opts)` | `handles.label` plus `string_slots[]` | `handles.label({ text: "note", size: 12, align: "left" })`; `draw.label(id).set(x,y).text(str_note_sb)` | Text is copied from the slot write on that call; position alone does not create the label. `align` (left, center, right) is the text edge on x; omitted centres. |
| `polyline.new(points,opts)` | `handles.polyline` | `handles.polyline({ color, width })`; `draw.polyline(id).setPoints(points,count)` | Reuse a `StaticArray<f64>` of x/y pairs; 1..256 points per handle. |
| `linefill.new(line1,line2,opts)` | No linefill handle kind | No direct call; `box(...)` slices or box handles approximate a fill | No object that tracks a fill between two moving line ids. |
| `table.new(position,rows,cols,opts)` | No table handle kind | `render.table(...)`, `panel.table(...)` or `draw.card(...)` | Replace a selected table/card snapshot; no `set_position()` or table-handle deletion. |
| `plotRange(time1,price1,time2,price2,...)` | `boxes[]`, or `drawings[].kind: "box"`, or `handles.box` | `box(name, { top, bottom, from, to })`, `draw.box(name, {...})`, or a box handle | Per-bar boxes use offsets within ±500 bars and clamp to loaded data; handles use absolute coordinates. |
| Last-bar `line.new`, `box.new`, `label.new`, `polyline.new` without ongoing identity | `drawings[]` with the corresponding kind | `draw.line`, `draw.box`, `draw.label`, `draw.polyline` from `./sdk/declare` | Declared coordinates name outputs; only the last ready row selects the object, with up to 64 polyline points. |

The two `draw` imports have different jobs: `./sdk/declare` declares a
run-level drawing, while `./gen/draw` operates on handles. Alias one when
using both. Allocate each generated handle object once at module scope or
in `init()`, and call its geometry/style methods in `finalize()`.

| kScript mutation | Generated handle method | Rule |
| --- | --- | --- |
| Line `set_xy`, `set_xy1`, `set_xy2` | `.set(...)`, `.setXy1(...)`, `.setXy2(...)` | Create with `.set(...)` before a partial setter. |
| Line/polyline `set_color`, `set_width`, `set_style` | `.color(rgba(...))`, `.width(n)`, `.style(LineStyle.Dashed)` | Width 0.5..20; line styles solid, dashed, dotted. |
| Line `set_extend` | `.extend(Extend.Right)` | None, Left, Right or Both. |
| Box `set_bounds`, `set_lefttop`, `set_rightbottom` | `.set(...)`, `.setLeftTop(...)`, `.setRightBottom(...)` | Geometry updates keep the same id. |
| Box `set_bgcolor`, `set_border_color`, `set_border_width` | `.fill(rgba(...))`, `.color(rgba(...))`, `.border(n)` | Border width 0..10; no box border-style setter. |
| Label `set_xy`, `set_text`, `set_color`, `set_bgcolor`, `set_size` | `.set(x,y).text(sender)`, `.text(sender)`, `.color(...)`, `.fill(...)`, `.size(n)` | Send text on every position/text update; size 6..64 integer pixels. |
| Label `set_textalign` | `.align(ALIGN_LEFT)`, `.align(ALIGN_RIGHT)`; `ALIGN_DEFAULT` restores centred text | The text edge that sits on x; (x, y) stays the point. Labels only. |
| Line/box/label `set_opacity`, `set_zorder` | `.opacity(n)`, `.zorder(n)` | Opacity 0..1; z-order is an integer. |
| Polyline `set_points` | `.setPoints(points,count)` | Re-emits the entire point list under the same id. |
| Line/box/label/polyline `delete()` | `.delete()` | Frees the shared id; a missing id is harmless. |

All four kinds share one nonnegative id space. Caps are 500 live handles
per kind, 1500 total, and 4096 draw calls per row. Delete expired objects
explicitly. A cap breach refuses the run; the host never silently drops
drawings. Declarative caps are 16 boxes, 16 segments, 32 renderers and
64 drawings, including cards, ladders, feeds and meters.

### Pane anchors

`handles.line|box|label|polyline({ anchor: "top_left" })` sets the default
for new handles. After creation, `.anchor(ANCHOR_TOP_LEFT)` changes it;
import the constant from `./gen/draw`. `.anchor(ANCHOR_CHART)` restores
chart coordinates even when a default anchor is declared. A label adds
`align` (`left`, `center`, `right`): the text edge that sits on x, with
(x, y) staying the point; `handles.label({ align: "left" })` is the
default, `.align(ALIGN_RIGHT)` changes a live label, and
`.align(ALIGN_DEFAULT)` restores centred text.

| Anchor | x coordinate | y coordinate |
| --- | --- | --- |
| Absent / `ANCHOR_CHART` | Epoch seconds | Price |
| Nine pane spots, `top_left` through `bottom_right` | CSS pixels from left, centre or right | CSS pixels from top, middle or bottom |
| `top`, `bottom` | Epoch seconds | CSS pixels from that edge |
| `left`, `right` | CSS pixels from that edge | Price |

Left/top offsets point right/down; right/bottom offsets point left/up.
Centre offsets are signed, and negative offsets are legal. Coordinates
start at the pane's content rectangle and clip to that pane. A pane anchor
keeps a HUD in place while the chart pans; it is not a timestamp offset.

## Frame snapshots

Declare `frame(name)` once and call generated `writeFrame(slot, json)` in `finalize()` to replace that run's snapshot.

| Sheet or operation | Code-first word | Lifetime and bound |
| --- | --- | --- |
| `frames: [{ name, max_bytes? }]` | `frame("profile")`, optionally `{ max_bytes }` | Up to 8 slots, each at most 96 KiB (98304 UTF-8 bytes). |
| Write the slot | `writeFrame(FRAME_PROFILE, json)` from `./gen/frames` | Finalize only; newest write replaces the previous bytes across the entire run, not just the current bar. |
| Level profile frame | `plot.levels({ frame: "profile", ... })` | `{ prices, values, colors? }`, matching array lengths, 1..512 monotonic prices; null values are gaps. |
| Panel frame | `panel.*({ frame: "summary", ... })` | `{ rows }`, with the selected panel kind's tuple grammar; empty rows are legal. |
| Unwritten / invalid frame | No extra declaration | Unwritten consumers are absent; invalid written JSON or payload refuses selection with `wrun_frame_invalid`. |

Frames and per-row strings share a 2 MiB `wrun-4` transport budget. Expanded
render selections have a separate 2 MiB cap, including panels, handles and
sparks. Frames are snapshots, so writing on every bar does not create a
history of frames. Accumulate what the snapshot needs in bounded guest
memory, then write it, commonly under `bar.isLast()`.

### Example: a docked profile and side bars

These declarations share a close output but bind two independent frames.
The fixed payloads show the exact accepted shapes; computed aggregates use
the same shapes.

```typescript
import { frame, input, line, ohlcv, output, overlay, panel, plot } from "./sdk/declare";
import { in_close } from "./gen/inputs";
import { emitRow, out_close } from "./gen/outputs";
import { FRAME_PROFILE, FRAME_SIDES, writeFrame } from "./gen/frames";
import { bar } from "./gen/draw";

input("close", ohlcv.close);
output("close", line, overlay);
const profile = frame("profile");
const sides = frame("sides");
plot.levels({ name: "volume_levels", frame: profile, dock: "right", poc: true });
panel.bars({
  name: "side_totals", title: "Volume by side", x: "category", place: "side",
  series: [{ name: "Volume", color: "#38bdf8" }], frame: sides,
});

let closeValue: f64 = NaN;
export function init(): void {}
export function state(): i32 { closeValue = in_close(); return 1; }
export function finalize(): void {
  out_close(closeValue);
  if (bar.isLast()) {
    writeFrame(FRAME_PROFILE, '{"prices":[100,101,102],"values":[8,21,13]}');
    writeFrame(FRAME_SIDES, '{"rows":[["Buy",26],["Sell",16]]}');
  }
  emitRow();
}
export function reset(): void { closeValue = NaN; }
```

The extractor selects `wrun-4`, resolves frame handles to names in the sheet,
and generates `FRAME_*` indexes. Frame declaration names, output names,
string-slot names and visual names must not collide.

### Example: a box and label HUD

Both handles use pane pixels and retain their ids while the last close changes.

```typescript
import { handles, input, none, ohlcv, output, overlay, string } from "./sdk/declare";
import { in_close } from "./gen/inputs";
import { emitRow, out_last_close } from "./gen/outputs";
import { sb_clear, sb_f64, sb_text, str_caption_sb } from "./gen/strings";
import { ALIGN_LEFT, ANCHOR_TOP_LEFT, bar, draw } from "./gen/draw";

input("close", ohlcv.close);
output("last_close", none, overlay);
string("caption", { max_bytes: 48 });
handles.box({ color: "#111827", borderColor: "#38bdf8", opacity: 1 });
handles.label({ text: "caption", color: "#ffffff", size: 12 });
const background = draw.box(0);
const caption = draw.label(1);
let closeValue: f64 = NaN;

export function init(): void {}
export function state(): i32 { closeValue = in_close(); return 1; }
export function finalize(): void {
  out_last_close(closeValue);
  if (bar.isLast()) {
    background.set(12, 12, 200, 46).anchor(ANCHOR_TOP_LEFT);
    sb_clear(); sb_text("Close "); sb_f64(closeValue, 2);
    caption.set(20, 22).text(str_caption_sb).anchor(ANCHOR_TOP_LEFT).align(ALIGN_LEFT);
  }
  emitRow();
}
export function reset(): void { closeValue = NaN; }
```

This sheet needs `wrun-3`, not a frame. Anchors are also available under `wrun-4`.
The label's `.text(...)` creates it before `.anchor(...)` and `.align(...)`
mutate it; `ALIGN_LEFT` starts the caption at x = 20 instead of centring it there.

## Sources and the bar grid

Keep a market input at index zero; all other inputs align to its grid.

| kScript source or idiom | Sheet field | Code-first call | Difference |
| --- | --- | --- | --- |
| `source("ohlcv", ...)` | `inputSources.close: { source: "ohlcv", field: "close" }` | `input("close", ohlcv.close)` | One field per named input; input zero supplies the primary grid. |
| `source("volume_profile", ...)` | Source `volume_profile`; input `cellType: "array", max_cells` | `input("profile", volume_profile.cells, { max_cells: 512 })` | Tuples are `[low, high, buy, sell]`; cap counts tuples, not f64 cells. |
| `source("orderbook", ...)` | Source `book`, `block_size`, optional `max_depth`; celled input | `input("book", book.cells, { max_cells: 128, block_size: 1 })` | Tuples are `[price, abs(size), side]`, +1 bid / -1 ask; exact join to primary bars. |
| `source("trades", ...)` raw tape | No raw tape source | `input(name, trades.volume, { side: "BUY" })` for aggregates | The `trades` source means side-split period aggregates, not individual trade prints. |
| `source("trade_volume_by_size", ...)` | Celled `trade_volume_by_size` vocabulary | `input(name, trade_volume_by_size.cells, { max_cells })` | Declares successfully but this daemon refuses the unavailable data lane. |
| Other-symbol `source` / `request` and higher-timeframe `htf` | `inputSources[name].symbol`, `.exchange`, `.interval` | `input(name, ohlcv.close, { symbol, exchange, interval })` | Pins are daemon behavior; coarser direct feeds align as of candle close. Chart inputs use the chart market/grid. |
| Watch values or installed non-market readings, with no legacy builtin | `inputSources[name]: { source: "series", ref, missing }` | **Sheet only**; read generated `in_<name>()` | Daemon only, never primary; no `series.*` code-first source namespace. |

For `source: "series"`, `ref` is `watch/<slug>/<key>` or an installed
`@scope/name`. `missing` is `carry` (default) or `nan`, never `zero`.
Readings bucket to the primary interval; the last reading in each bucket
wins. Carry uses the latest bucket at or before the bar; nan requires a
reading in that bar's own bucket. A series input has no `field`, selector,
params or interval pin and cannot densify the grid.

### Example: a watch series sheet

Use a metadata-first workspace for this sheet. The generated
`in_watch_flow()` reads the watch value in `state()`; `out_flow(value)` writes
the plotted value in `finalize()` before `emitRow()`.

```json
{
  "id": "watch-flow",
  "name": "Watch flow",
  "inputSources": {
    "close": { "source": "ohlcv", "field": "close" },
    "watch_flow": { "source": "series", "ref": "watch/flow/imbalance", "missing": "carry" }
  },
  "inputs": [{ "index": 0, "name": "close" }, { "index": 1, "name": "watch_flow" }],
  "outputs": [{ "index": 0, "name": "flow", "plot": "line", "panel": "lower" }]
}
```

Series inputs are ABI-neutral; the sheet above uses the `wrun-1` default.
Add `abi_version: "wrun-4"` when the same module also declares frames.

## No home yet

These legacy behaviors need a deliberate substitute; an accepted sheet field alone does not provide every legacy visual.

| kScript builtin or behavior | Missing Indicator word | Nearest workaround |
| --- | --- | --- |
| `plotLine(..., fillGradient=...)`, gradient lines, `momentum_fill`, `smooth`, `glow` | No continuous gradient, smoothed-line or glow output option | Use stepped `color_by`/`colors`, or several bounded box/line layers. |
| `linefill.new(...)` and its setters; gradient clouds | No linefill handle that tracks two moving line ids; no gradient fill | `ranges[]` (`range(upperName, lowerName, options)`) draws a flat filled band between two numeric outputs, with edge width, line style and palette; a fill between two line handles needs per-bar `box()` slices. |
| Per-bar `plotPie(...)` and `plot(plotType="pie")` | No pie at each bar's time and price | Summarize the current values with `panel.pie`; it replaces one frame. |
| `plotCandle(...)` OHLC binding; OHLC tick bars in `plotMiniChartGrid` | No four-output candle binding or OHLC mini-grid | Body boxes plus wick segments on the chart; `panel.line` or card sparks for compact history. |
| `plotMiniChartGrid` grid, moving averages, badges and volume strips | No candle-panel grid or automatic MA/badge composition | Several bounded panels, a card with sparks, or anchored handles. |
| `barcolor(...)` | No candle body recolor channel | `render.bgcolor` for a regime tint, or draw your own candle bodies. |
| `hline(...)`; `plotHistogram(..., base=...)` with a nonzero baseline | No dedicated hline or adjustable histogram baseline | Emit a constant line; subtract the baseline for zero-centred columns, or use absolute-price boxes. |
| Arbitrary `plotBatches` per-bar payload arrays | No batched output channel | Bounded handle loops for retained objects; `plot.levels`, panels or ladders for snapshots. |
| `plotShape(shape="char")`, special location/size behavior | No character-shape, relative-bar-location or size-output option on the shape renderer | `render.text` for characters, computed y for above/below bars, anchored label handles for pane placement. |
| `plotPriceLabel` / `label.new` icons; `set_icon`, `set_glow`, `set_tooltip`; plot/table tooltips | No URL/SVG/image, glow or tooltip fields on these Indicator visuals | Text labels, captions or additional card/table rows. |
| `plotText` / `plotLabel` font family, weight; rich table spans and headers | No matching typography, colspan/rowspan or header-style vocabulary (label handles do take `align`: left, center, right) | Use panel cells for basic text/color/bar/spark content, or anchored, aligned labels and boxes for explicit layout. |
| Box `set_border_style`, table `set_position` / `delete`, line/label `stickyRight` | No matching object setters | Use styled line handles for borders, rebuild a frame for table content, or edge anchors for pixel-x/price-y placement. |
| `plotCurve` marker `"spot"`, markers and arbitrary fractional line x; `plotMatrix` summary/highlight; `plotTiles` columns/accent/format | No exact panel options for those legacy features | Scatter for fractional x; extra rows/series, formatted tile strings, and explicit colors for the annotations. |
| `requestBars(...)` independent OHLC arrays; `ltf(...)` lower-timeframe arrays; unsupported registered `source(...)` classes | No general nested-bars or arbitrary source adapter | Use supported scalar/celled inputs, or prepare a daemon series and render an appropriate snapshot. |
| String, symbol, color, select or other nonnumeric compute `input(...)` | No nonnumeric compute parameter channel | Numeric parameters, sheet-only style bindings, or metadata source pins. |
| `print(...)` / `log(...)` console output | No guest console import | Emit a numeric diagnostic output or a bounded text/card row. |

The contracts behind this map live on their published pages: [Plotting](../functions/plotting.md) for outputs, renderers and declared drawings, [Drawing objects](../functions/drawing-objects.md) for handles and their setters, [Drawing primitives](../functions/drawing-primitives.md) for pane anchors, frames, panels and the compact widgets, [Data sources](../core-concepts/data-sources.md) for the source classes, and the [Limits reference](../reference/limits.md) for every cap. The ABI contracts a sheet names are on [Script definition](../functions/script-definition.md).
