Command Tutorial & Reference
Explore svw commands by task. Each entry includes a ready-to-use example and, where available, a recorded demonstration.
Waveform Files
:open
<file…> - open waveform file(s); multiple files join the open-file list (:files / :active)
Try it
printf 'addall\nopen tutorial_demo_b.vcd\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :open demo
:files
- list open files (:active to switch)
Try it
printf 'open tutorial_demo_b.vcd capture_03.fsdb capture_04.fsdb capture_05.fsdb capture_06.fsdb capture_07.fsdb capture_08.fsdb capture_09.fsdb capture_10.fsdb capture_11.fsdb capture_12.fsdb capture_13.fsdb capture_14.fsdb capture_15.fsdb capture_16.fsdb capture_17.fsdb capture_18.fsdb capture_19.fsdb capture_20.fsdb capture_21.fsdb capture_22.fsdb capture_23.fsdb capture_24.fsdb capture_25.fsdb capture_26.fsdb capture_27.fsdb capture_28.fsdb capture_29.fsdb capture_30.fsdb capture_31.fsdb capture_32.fsdb capture_33.fsdb capture_34.fsdb capture_35.fsdb capture_36.fsdb capture_37.fsdb capture_38.fsdb capture_39.fsdb capture_40.fsdb capture_41.fsdb\nfiles\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :files demo
:active
<n> - switch the active file by number (Tab lazily lists open files); selecting the current file is not an undo step
Try it
printf 'open tutorial_demo_b.vcd\nactive \n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :active demo
:reload
- reload the current waveform file
Try it
printf 'addall\nreload\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :reload demo
:refresh
- refresh a growing KBX capture without reopening or losing the view; the external data snapshot is explicit and does not consume Ctrl+Z model history
Try it
printf 'addall\nrefresh\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_delta.kbx"
recorded demo — :refresh demo
:warnings
- list the parse warnings of the open waveform (VCD parser diagnostics with line numbers) in the report overlay
Try it
printf 'warnings\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_warnings.vcd"
recorded demo — :warnings demo
Signal Discovery
:add
<path…> - add signals to the display list ('*' glob)
Try it
printf 'add top.clk top.instruction_bus\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :add demo
:importlist
<file> - transactionally add signal paths and '*' globs from a bounded text file; blank lines and '#' comments are ignored
Try it
printf 'delall\nimportlist tutorial_signals.txt\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :importlist demo
:addall
[scope] - add all signals under scope (default: root)
Try it
printf 'addall\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :addall demo
:find
<regexp> [in <scope>] - add all matching signals (POSIX regex, smart-case / :set ic)
Try it
printf 'delall\nfind pipeline_stage_0[0-5]\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :find demo
:scope
<path> [<t0> <t1>] - open a scope's signals in the Telescope picker without changing hierarchy state or consuming Ctrl+Z history; preview the current wave-panel interval by default or an explicit time range
Try it
printf 'addall\ngoto 400ns\nzoom in\nscope top\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :scope demo
:scope range
scope <path> <t0> <t1>
Try it
printf 'scope top 200ns 500ns\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :scope range demo
:tree
[scope] - lazy design browser: optional [scope] becomes the subtree root (Tab completes scope paths); only the first visible page is loaded until j/k reaches its frontier, Enter folds/expands a scope or adds/locates and flashes a signal, gg/G top/bottom, q/Esc close; headless prints the selected subtree as indented text (+ collapsed / - expanded)
Try it
printf 'delall\npanelwidth 64\ntree top.cpu\ntree top.cpu\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :tree demo
:tree no-argument
[scope] - lazy design browser: optional [scope] becomes the subtree root (Tab completes scope paths); only the first visible page is loaded until j/k reaches its frontier, Enter folds/expands a scope or adds/locates and flashes a signal, gg/G top/bottom, q/Esc close; headless prints the selected subtree as indented text (+ collapsed / - expanded)
Try it
printf 'delall\ntree\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :tree no-argument demo
Signal Panel
:delall
- delete all rows from the display list
Try it
printf 'addall\ndelall\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :delall demo
:del
{id…} - delete display rows (1-based index, name, or g:group for the whole group)
Try it
printf 'addall\ndel 1\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :del demo
:d
{id…} - alias for :del
Try it
printf 'addall\nd\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :d demo
:yank
[id] - copy a row to the clipboard, no delete (g:group yanks the whole group block); the clipboard survives Ctrl+Z and yanking does not consume undo history
Try it
printf 'addall\nyank\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :yank demo
:detail
[id] - open the Signal Detail float of a row (the K key); headless prints the detail lines
Try it
printf 'design index --top tutorial_packed tutorial_packed.sv\nadd tutorial_packed.packet\ndetail\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_packed.vcd"
recorded demo — :detail demo
:copypath
[id] - copy a row's full hierarchical path to the clipboard (OSC 52); headless prints the path
Try it
printf 'addall\ncopypath 2\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :copypath demo
:move
{id} <pos> - move a row (or a g:group / retained :expand block) so it shows row number <pos>; selecting an expanded child moves its parent and complete child block; an already-positioned row is not an undo step
Try it
printf 'addall\nmove 1 2\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :move demo
:move group
move g:<group> <pos>
Try it
printf 'addall\ngroup 1-5 sys\nmove g:sys 3\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :move group demo
:move expanded
{id} <pos> - move a row (or a g:group / retained :expand block) so it shows row number <pos>; selecting an expanded child moves its parent and complete child block; an already-positioned row is not an undo step
Try it
printf 'delall\nadd top.clk top.instruction_bus top.reset_n\nexpand 2\npanelwidth 64\nmove 3 19\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :move expanded demo
:blank
- insert a blank separator row after the selection
Try it
printf 'addall\nblank\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :blank demo
:comment
<text> - insert a comment row after the selection
Try it
printf 'addall\ncomment 1 main-clock\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :comment demo
:alias
{id} <name> - rename a trace (alias)
Try it
printf 'addall\nalias 1 clock\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :alias demo
:unalias
{id} - remove a trace alias
Try it
printf 'addall\nalias 1 clock\nunalias 1\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :unalias demo
:hl
<regexp> - highlight matching displayed traces
Try it
printf 'addall\nhl reset_n\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :hl demo
:unhl
<regexp> - remove highlight from matching traces
Try it
printf 'hl reset_n\nunhl reset_n\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :unhl demo
:sort alpha
alpha|sig|reverse - sort the display list; a group block moves as one unit (alpha: header name; sig: first member's definition order) and its members reorder inside the block; blank/comment rows keep their alpha|sig slots
Try it
printf 'addall\ngroup 1,top.reset_n,/^top[.]cpu[.]pipeline_stage_0[0-2].*/ ahb\nsort alpha\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :sort alpha demo
:sort reverse
alpha|sig|reverse - sort the display list; a group block moves as one unit (alpha: header name; sig: first member's definition order) and its members reorder inside the block; blank/comment rows keep their alpha|sig slots
Try it
printf 'addall\ngroup 1,top.reset_n,/^top[.]cpu[.]pipeline_stage_0[0-2].*/ sys\nsort reverse\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :sort reverse demo
:filter
<pattern>|on|off - hide display rows not matching <pattern> (/regexp/ or plain substring over alias/full/leaf names); 'off' hides nothing, 'on' restores the last pattern, and a group header survives while its name or any member matches; no argument clears
Try it
printf 'addall\nfilter /pipeline_stage_0[0-5]/\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :filter demo
:filter on
filter on
Try it
printf 'addall\nfilter /pipeline_stage_0[0-5]/\nfilter off\nfilter on\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :filter on demo
:filter off
filter off
Try it
printf 'addall\nfilter /pipeline_stage_0[0-5]/\nfilter off\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :filter off demo
:tmark
[{id…}|clear] - toggle batch marks (v / Shift+Enter): no argument toggles the selected signal row, selectors toggle every matched signal row, 'clear' removes all marks (Esc) and is not an undo step when empty; :group without a selector consumes the marked set
Try it
printf 'tmark 1,3\ngroup picked\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :tmark demo
:tmark clear
tmark clear
Try it
printf 'addall\ntmark 1,3\ntmark clear\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :tmark clear demo
:group
<ids,names,/regexp/> <name> | group <name> | group rename <name> | group combine <name> - move selected signals into one bounded collapsible group; 'rename' changes the selected or enclosing group and repeating its current name is not an undo step; 'combine' concatenates a named group's bit-vector signals into one vector whose :expand restores the group
Try it
printf 'addall\ngroup 1,top.reset_n,/^top[.]cpu[.]pipeline_stage_0[0-2].*/ sys\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :group demo
:group rename
group rename <name>
Try it
printf 'addall\ngroup 1-3 sys\ngroup rename renamed system\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :group rename demo
:group combine
group combine <name>
Try it
printf 'addall\ngroup 1-3 sys\ngroup combine sys\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :group combine demo
:ungroup
- remove the enclosing group header
Try it
printf 'group 1-2 sys\nungroup\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :ungroup demo
Signal Format & Derived Signals
:loadalias
<file> - load enum value aliases for the selected signal from a file; reloading identical aliases from the same file is not an undo step
Try it
printf 'addall\nloadalias tutorial_aliases.txt\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :loadalias demo
:aliasval
{id} <value> <name> [color] | aliasval {id} del <value> - edit one value alias of a display signal in-app: value as hex/dec/verilog literal; the optional palette color paints that value in the bus band and value column; coexists with :loadalias entries (later wins) and is saved with the session; repeating the same name/color is not an undo step
Try it
printf 'addall\naliasval top.instruction_bus 0x0001 FETCH\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :aliasval demo
:fmt
hex|dec|signed|bin|oct|ascii|char|time|real|float32|float64|real2bits | invert|reverse|rjustify|popcnt|msgbar on|off | shift <n>|off | fill0|fill1|filloff - data format of the selected trace; float32/float64 decode the bit pattern as IEEE754 and require an exact 32/64-bit vector; hex/oct/dec/signed are exact at any width; repeating the active format is not an undo step
Try it
printf 'delall\nadd top.radix3\nfmt bin\nfmt hex\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_radix.vcd"
recorded demo — :fmt demo
:fmt float
hex|dec|signed|bin|oct|ascii|char|time|real|float32|float64|real2bits | invert|reverse|rjustify|popcnt|msgbar on|off | shift <n>|off | fill0|fill1|filloff - data format of the selected trace; float32/float64 decode the bit pattern as IEEE754 and require an exact 32/64-bit vector; hex/oct/dec/signed are exact at any width; repeating the active format is not an undo step
Try it
printf 'addall\ngoto 35\nfmt float32\n2\nfmt float64\n3\nfmt dec\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_float.vcd"
recorded demo — :fmt float demo
:analog step
analog step
Try it
printf 'addall\ndel 3\ndel 3\ndel 3\nanalog off\nanalog step\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :analog step demo
:analog interp
analog interp
Try it
printf 'addall\ndel 3\ndel 3\ndel 3\nanalog interp\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :analog interp demo
:analog off
analog off
Try it
printf 'addall\ndel 3\ndel 3\ndel 3\nanalog step\nanalog off\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :analog off demo
:expand
{id} - retain a vector parent and show its bits on a connected │/├─/└─ tree; Enter on the parent folds/opens them; repeating :expand on the already-open selected vector is not an undo step; a vector made by :group combine restores its original group
Try it
printf 'addall\nexpand 3\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :expand demo
:expand group
expand <combined-group-vector>
Try it
printf 'addall\ngroup 1-3 sys\ngroup combine sys\nexpand\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :expand group demo
:combine
{id…} - combine adjacent standalone bits into a vector; selecting a complete :expand child block restores its retained parent
Try it
printf 'addall\ncombine 1 2\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :combine demo
:expr and
expr <a> & <b>
Try it
printf 'addall\nexpr top.clk & top.reset_n\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :expr and demo
:expr unary
expr ~<sig>
Try it
printf 'addall\nexpr ~top.reset_n\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :expr unary demo
:expr reduce
expr &<sig>
Try it
printf 'addall\nexpr &top.state\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :expr reduce demo
:expr arith
expr <a> + <b>
Try it
printf 'addall\nexpr top.state + 1\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :expr arith demo
:expr shift
expr <a> << <n>
Try it
printf 'addall\nexpr top.state << 2\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :expr shift demo
:expr compare
expr <a> >= <b>
Try it
printf 'addall\nexpr top.state >= 4\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :expr compare demo
:expr ternary
expr <cond> ? <a> : <b>
Try it
printf 'addall\nexpr top.clk ? top.state : 0\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :expr ternary demo
:expr bitsel
expr <sig>[msb:lsb]
Try it
printf 'addall\nexpr top.instruction_bus[3:0]\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :expr bitsel demo
:expr concat
expr {a, b}
Try it
printf 'addall\nexpr {top.clk, top.state[2:0]}\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :expr concat demo
:expr counter
expr counter(<sig>, rise|fall|any)
Try it
printf 'addall\nexpr counter(top.clk, rise)\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :expr counter demo
:expr timeshift
expr <sig> ->> <n>
Try it
printf 'addall\nexpr top.clk ->> 20\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :expr timeshift demo
:bus
create <name> {expr, sig[msb:lsb], …} | bus edit {id} {…} - virtual bus: splice signals or expressions (repeatable, bit-selectable) into one row; repeating an unchanged edit is not an undo step; saved with the session
Try it
printf 'addall\nbus create nibble {top.instruction_bus[3:0]}\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :bus demo
:subbus
{id} <width> - split a vector into ceil(N/width) sub-bus rows inserted after it (expand as sub-bus); the last row takes the remainder and the declared msb/lsb direction is honored
Try it
printf 'addall\nsubbus 3 4\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :subbus demo
:subbus expanded
{id} <width> - split a vector into ceil(N/width) sub-bus rows inserted after it (expand as sub-bus); the last row takes the remainder and the declared msb/lsb direction is honored
Try it
printf 'delall\nadd top.instruction_bus\nexpand 1\npanelwidth 64\nsubbus 1 4\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :subbus expanded demo
:counter
{id} rise|fall|any [name] - virtual counter signal: +1 per matching edge of the source, rendered as a 32-bit bus, saved with the session
Try it
printf 'addall\ncounter 1 any\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :counter demo
:savealias
<file> - export the selected signal's value aliases (:loadalias/:aliasval) as an alias file (:loadalias compatible); colors are not part of the file format
Try it
printf 'addall\nsavealias out_aliases.txt\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :savealias demo
:color
<name>|normal|cycle|<0-255> - set selected trace color (theme name or xterm-256 index); 'cycle' rotates the palette (green/yellow/red/blue/magenta/cyan/orange/violet) over all traces; no argument opens the palette picker float; an unchanged color assignment is not an undo step
Try it
printf 'addall\ncolor red\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :color demo
Time Navigation & Search
:goto
<time|A-Z> - move the left edge / cursor to a time or named marker; repeating the active target is not an undo step
Try it
printf 'addall\ngoto 800\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :goto demo
:zoom in
zoom in
Try it
printf 'addall\ngoto 400\nzoom in\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :zoom in demo
:zoom out
zoom out
Try it
printf 'addall\ngoto 400\nzoom in\nzoom in\nzoom in\nzoom out\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :zoom out demo
:zoom full
in|out|full|fit|undo|<amount> - zoom control; repeated views, zoom limits, and undo with no prior zoom are not undo steps
Try it
printf 'addall\ngoto 400\nzoom in\nzoom in\nzoom in\nzoom full\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :zoom full demo
:zoom fit
in|out|full|fit|undo|<amount> - zoom control; repeated views, zoom limits, and undo with no prior zoom are not undo steps
Try it
printf 'addall\nmark 100\nbmark 300\nzoom fit\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :zoom fit demo
:zoom undo
in|out|full|fit|undo|<amount> - zoom control; repeated views, zoom limits, and undo with no prior zoom are not undo steps
Try it
printf 'addall\nzoom in\nzoom in\nzoom in\nzoom undo\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :zoom undo demo
:edge rise
edge rise next|prev [count]
Try it
printf 'addall\nedge rise next\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :edge rise demo
:edge fall
edge fall next|prev [count]
Try it
printf 'addall\nedge fall next\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :edge fall demo
:edge any
edge any next|prev [count]
Try it
printf 'addall\nedge any next\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :edge any demo
:pattern
[-all|-nomark|-clear] <sig>=<value>[ & …] [forward|backward] - pattern search from the primary marker; a found match stays marked with a dashed line until -clear (-nomark suppresses it, -all marks every hit); clearing empty marks or repeating an unchanged -all is not an undo step; <value> uses the :searchvalue grammar - hex (0x4f), decimal, 0/1/x/z bits, '?' one-bit and '*' padding wildcards for bit signals, a number for real signals, a '*' glob for string signals (A->B transitions are :searchvalue-only)
Try it
printf 'addall\ngoto 0\nzoom in\nzoom in\nmark 100\npattern top.state=0011 backward\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :pattern demo
:pattern real
[-all|-nomark|-clear] <sig>=<value>[ & …] [forward|backward] - pattern search from the primary marker; a found match stays marked with a dashed line until -clear (-nomark suppresses it, -all marks every hit); clearing empty marks or repeating an unchanged -all is not an undo step; <value> uses the :searchvalue grammar - hex (0x4f), decimal, 0/1/x/z bits, '?' one-bit and '*' padding wildcards for bit signals, a number for real signals, a '*' glob for string signals (A->B transitions are :searchvalue-only)
Try it
printf 'addall\ngoto 0\nzoom in\nzoom in\nmark 100\npattern top.voltage=3.000 backward\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :pattern real demo
:pattern string
[-all|-nomark|-clear] <sig>=<value>[ & …] [forward|backward] - pattern search from the primary marker; a found match stays marked with a dashed line until -clear (-nomark suppresses it, -all marks every hit); clearing empty marks or repeating an unchanged -all is not an undo step; <value> uses the :searchvalue grammar - hex (0x4f), decimal, 0/1/x/z bits, '?' one-bit and '*' padding wildcards for bit signals, a number for real signals, a '*' glob for string signals (A->B transitions are :searchvalue-only)
Try it
printf 'addall\ngoto 0\nzoom in\nzoom in\nmark 100\npattern top.phase=BO* backward\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :pattern string demo
:pattern all
[-all|-nomark|-clear] <sig>=<value>[ & …] [forward|backward] - pattern search from the primary marker; a found match stays marked with a dashed line until -clear (-nomark suppresses it, -all marks every hit); clearing empty marks or repeating an unchanged -all is not an undo step; <value> uses the :searchvalue grammar - hex (0x4f), decimal, 0/1/x/z bits, '?' one-bit and '*' padding wildcards for bit signals, a number for real signals, a '*' glob for string signals (A->B transitions are :searchvalue-only)
Try it
printf 'addall\ngoto 0\nzoom in\nzoom in\npattern -all top.clk=1\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :pattern all demo
:pattern clear
pattern -clear
Try it
printf 'addall\npattern -all top.clk=1\npattern -clear\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :pattern clear demo
:shifttime
<±t> | shifttime {id} <±t> | shifttime reset - shift the view of all traces or one trace in time (File/Signal Time); repeating a shift or resetting zero shifts is not an undo step
Try it
printf 'addall\nshifttime 100\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :shifttime demo
:searchvalue any
searchvalue any
Try it
printf 'addall\nsearchvalue any\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :searchvalue any demo
:searchvalue rise
searchvalue rise
Try it
printf 'addall\nsearchvalue rise\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :searchvalue rise demo
:searchvalue fall
searchvalue fall
Try it
printf 'addall\nsearchvalue fall\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :searchvalue fall demo
:searchvalue pattern
searchvalue <pattern> [id|name]
Try it
printf 'addall\nsearchvalue 000000?1\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :searchvalue pattern demo
:searchvalue jump
searchvalue jump next|prev
Try it
printf 'addall\nsearchvalue 000000?1\nsearchvalue jump next\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :searchvalue jump demo
:compress range
compress <t0> <t1>
Try it
printf 'addall\ncompress 200 800\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_compress.vcd"
recorded demo — :compress range demo
:compress clear
<t0> <t1> | compress clear - hide a time range from the wave view; clearing an empty compression map is not an undo step
Try it
printf 'addall\ncompress 200 800\ncompress clear\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_compress.vcd"
recorded demo — :compress clear demo
:fetch
left|right [n] - extend the visible time range; a zoom-limit no-op is not an undo step
Try it
printf 'addall\nfetch left 10\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :fetch demo
:discard
left|right [n] - shrink the visible time range; a zoom-limit no-op is not an undo step
Try it
printf 'addall\ndiscard left 10\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :discard demo
Markers & Measurements
:mark
[time] - place/move the primary marker; an explicit time focuses the visible wave position; repeating an unchanged placement is not an undo step
Try it
printf 'addall\nmark 400\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :mark demo
:bmark
[time] - place/move the alt (baseline) marker; an explicit time focuses the visible wave position; repeating an unchanged placement is not an undo step
Try it
printf 'addall\nbmark 300\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :bmark demo
:unmark
[alt] - delete the primary marker ('alt' deletes the baseline); clearing an absent marker is not an undo step
Try it
printf 'addall\nmark 10\nunmark\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :unmark demo
:unmark alt
unmark [alt]
Try it
printf 'addall\nmark 10\nbmark 30\nunmark alt\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :unmark alt demo
:drop
<A-Z> [time] - place a unique named marker; repeating the same letter and time is not an undo step
Try it
printf 'addall\ndrop A 500\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :drop demo
:collect
<A-Z>|all - delete named marker(s); clearing an absent marker is not an undo step
Try it
printf 'addall\ndrop A 100\ndrop B 300\ncollect all\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :collect demo
:marks
- list all markers
Try it
printf 'addall\nmark 100\nbmark 300\ndrop A 500\nmarks\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :marks demo
:anchor
anchor set <name> [time] | anchor list
Try it
printf 'addall\ngoto 200\nanchor set reset_done\ngoto 500\nmark @reset_done\nanchor list\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :anchor demo
:delta freq
delta freq
Try it
printf 'addall\nmark 100\nbmark 300\ndelta freq\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :delta freq demo
:delta time
delta time
Try it
printf 'addall\nmark 100\nbmark 300\ndelta time\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :delta time demo
:delta fix
delta fix
Try it
printf 'addall\nmark 100\nbmark 300\ndelta fix\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :delta fix demo
:delta unfix
delta unfix
Try it
printf 'addall\nmark 100\nbmark 300\ndelta fix\ndelta unfix\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :delta unfix demo
:delta cycles
freq|time|fix|unfix | delta cycles <period> - marker delta display: time/frequency, frozen (fix), or clock cycles period accepts unit suffixes like 6.4ns; repeating the active mode/value is not an undo step
Try it
printf 'addall\nmark 25\nbmark 10\ndelta cycles 5\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_delta.kbx"
recorded demo — :delta cycles demo
:annotate
<text> [time] | annotate del <n> | annotate list - floating text annotation pinned to a time; the time defaults to the primary marker/cursor and accepts unit suffixes; 'list' is read-only; saved with the session
Try it
printf 'addall\nannotate 5 first-edge\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :annotate demo
Waveform Analysis
:toggle
[regexp] - switching-activity report: per-signal transition counts and stable signals (not a coverage database)
Try it
printf 'addall\ntoggle\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :toggle demo
:compare
<file> [range <t0> <t1>] [ignorexz] | compare {id} {id} | compare next|prev|clear|report - compare the open waveform with a reference file (optionally limited to a time range; ignorexz skips mismatches where either side has x/z bits), or two display rows against each other; mismatches become vertical marks navigated with next/prev; a two-row compare also opens a detail report of both values, reopened with 'compare report'; report reopening, identical results and clearing empty results are not undo steps
Try it
printf 'addall\ncompare tutorial_demo_b.vcd\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :compare demo
:diff
{id} {id} | diff report - alias of :compare {id} {id}: mismatch marks of two display rows plus the detail report overlay; reopening the report or repeating an identical result is not an undo step
Try it
printf 'addall\ndiff 1 2\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :diff demo
:report
events [t0 t1] [regexp] | report samples <event-expression> [t0 t1] [-track <sig>]… [-sample native|pre-edge] | report property <match|assert|deassert|switch> <expression> [t0 t1] [-same-time all|final] | report extract [t0 t1] -source <name> <event> <predicate> [-payload <name> <signal>]… [-sample <name> native|pre-edge]… | report values <sig> [t0 t1] [-when <expr>] [-o <file>] | report records <sig> [t0 t1] [filters] [-o <file>] | report assertions <sig> [t0 t1] [-o <file>] | report protocol <valid> <ready> <clock> [t0 t1] [-payload <sig>]… [-sample native|pre-edge] | report failures <artifact> [-signal <sig>]… - signal reports: events = transition counts; samples = named/wildcard/edge event unions with iff guards and explicit event/sample times; property = typed four-state match/assert/deassert/switch captures; extract = merged named event sources with predicates and payloads; values = per-change time+value lines with optional condition; records/assertions = typed stream evidence; protocol = ready/valid diagnostics with native or pre-edge sampling; failures = bounded regression artifacts correlated with waveform values; -o writes supported reports to a file
Try it
printf 'addall\nreport events\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :report demo
:report protocol
report protocol <valid> <ready> <clock> [t0 t1] [-payload <signal>]… [-sample native|pre-edge]
Try it
printf 'report protocol top.valid top.ready top.clk 0 65 -payload top.data -sample pre-edge\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_protocol.vcd"
recorded demo — :report protocol demo
:report samples
report samples <event-expression> [t0 t1] [-track <sig>]… [-sample native|pre-edge]
Try it
printf 'report samples "posedge tb_top.clk iff tb_top.dut.issue_fire" 0 45 -track tb_top.dut.issue_fire -track tb_top.dut.issue_pc -sample pre-edge\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_event_sampling.vcd"
recorded demo — :report samples demo
:report property
report property <match|assert|deassert|switch> <expression> [t0 t1] [-same-time all|final]
Try it
printf 'report property deassert "top.data == 8'haa" 0 65\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_protocol.vcd"
recorded demo — :report property demo
:report extract
report extract [t0 t1] -source <name> <event> <predicate> [-payload <name> <signal>]… [-sample <name> native|pre-edge]…
Try it
printf 'report extract 0 65 -source rise "posedge top.clk" top.valid -payload rise top.data -source fall "negedge top.clk" "!top.valid" -payload fall top.data -sample fall pre-edge\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_protocol.vcd"
recorded demo — :report extract demo
:report failures
report failures <artifact> [-signal <signal>]…
Try it
printf 'report failures tutorial_failures.txt -signal top.data\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_protocol.vcd"
recorded demo — :report failures demo
:stuck
[t0 t1] - highlight display signals with no changes in the time range (default: visible window); already-highlighted results are not an undo step
Try it
printf 'addall\nstuck\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :stuck demo
:expanddelta
[time] - list the delta-cycle sub-steps of the selected signal at the cursor (or [time]) in the report overlay; needs source seq data (T9)
Try it
printf 'addall\ndel 1\ngoto 20\nexpanddelta\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_delta.kbx"
recorded demo — :expanddelta demo
Coverage
:coverage load
coverage load <file>
Try it
printf 'coverage load tutorial_coverage.xml\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :coverage load demo
:coverage summary
coverage summary
Try it
printf 'coverage load tutorial_coverage.xml\ncoverage summary\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :coverage summary demo
:coverage points
coverage points
Try it
printf 'coverage load tutorial_coverage.xml\ncoverage points\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :coverage points demo
:coverage clear
coverage clear
Try it
printf 'coverage load tutorial_coverage.xml\ncoverage clear\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :coverage clear demo
Semantic Design
:design index
design index <compiler-args...>
Try it
printf 'design index --top tutorial_design tutorial_design.sv\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :design index demo
:design summary
design summary
Try it
printf 'design index --top tutorial_design tutorial_design.sv\ndesign summary\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :design summary demo
:design instances
design instances
Try it
printf 'design index --top tutorial_design tutorial_design.sv\ndesign instances\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :design instances demo
:design objects
design objects [filter]
Try it
printf 'design index --top tutorial_design tutorial_design.sv\ndesign objects selected\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :design objects demo
:design processes
design processes
Try it
printf 'design index --top tutorial_design tutorial_design.sv\ndesign processes\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :design processes demo
:design assignments
design assignments
Try it
printf 'design index --top tutorial_design tutorial_design.sv\ndesign assignments\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :design assignments demo
:design diagnostics
design diagnostics
Try it
printf 'design index --top tutorial_design tutorial_design.sv\ndesign diagnostics\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :design diagnostics demo
:design source
design source <path-or-id>
Try it
printf 'design index --top tutorial_design tutorial_design.sv\ndesign source tutorial_design.selected\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :design source demo
:design clear
design clear
Try it
printf 'design index --top tutorial_design tutorial_design.sv\ndesign clear\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :design clear demo
:xprobe status
xprobe status
Try it
printf 'design index --top tutorial_design tutorial_design.sv\nxprobe status\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_design.vcd"
recorded demo — :xprobe status demo
:xprobe rebuild
xprobe rebuild
Try it
printf 'design index --top tutorial_design tutorial_design.sv\nxprobe rebuild\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_design.vcd"
recorded demo — :xprobe rebuild demo
:xprobe object
xprobe object <path-or-id>
Try it
printf 'design index --top tutorial_design tutorial_design.sv\nxprobe object tutorial_design.selected\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_design.vcd"
recorded demo — :xprobe object demo
:xprobe source-hover
hover a report source path
Try it
printf 'design index --top tutorial_design tutorial_design.sv\nxprobe object tutorial_design.selected\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_design.vcd"
recorded demo — :xprobe source-hover demo
:xprobe signal
xprobe signal <wave-path>
Try it
printf 'design index --top tutorial_design tutorial_design.sv\nxprobe signal tutorial_design.selected\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_design.vcd"
recorded demo — :xprobe signal demo
:xprobe diagnostics
xprobe diagnostics
Try it
printf 'design index --top tutorial_design tutorial_design.sv\nxprobe diagnostics\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_design.vcd"
recorded demo — :xprobe diagnostics demo
:xprobe map
xprobe map list|add|del|clear
Try it
printf 'design index --top tutorial_design tutorial_design.sv\nxprobe map add tutorial_design sim_top\nxprobe map list\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_design.vcd"
recorded demo — :xprobe map demo
:xprobe alias
xprobe alias list|add|del|clear
Try it
printf 'design index --top tutorial_design tutorial_design.sv\nxprobe alias add tutorial_design.selected tutorial_design.selected\nxprobe alias list\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_design.vcd"
recorded demo — :xprobe alias demo
Source & Root-Cause Debug
:sourceview object
sourceview object <design-path-or-id> [time]
Try it
printf 'design index --top tutorial_design tutorial_design.sv\nsourceview object tutorial_design.selected 10\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_design.vcd"
recorded demo — :sourceview object demo
:sourceview signal
sourceview signal <wave-path> [time]
Try it
printf 'design index --top tutorial_design tutorial_design.sv\nsourceview signal tutorial_design.source_a 10\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_design.vcd"
recorded demo — :sourceview signal demo
:sourceview refresh
sourceview refresh [time]
Try it
printf 'design index --top tutorial_design tutorial_design.sv\nsourceview object tutorial_design.selected 10\nsourceview refresh 10\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_design.vcd"
recorded demo — :sourceview refresh demo
:drivers
<design-path-or-id> [bit-offset-msb:lsb] - list bit-precise continuous, procedural and module-port assignments that drive an elaborated object range; report overlapping multi-driver offsets; the source cockpit target is undoable, and reopening an identical result is not an undo step
Try it
printf 'design index --top tutorial_design tutorial_design.sv\ndrivers tutorial_design.selected\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_design.vcd"
recorded demo — :drivers demo
:loads
<design-path-or-id> [bit-offset-msb:lsb] - list bit-precise static fanout assignments that read an elaborated object range; the source cockpit target is undoable, and reopening an identical result is not an undo step
Try it
printf 'design index --top tutorial_design tutorial_design.sv\nloads tutorial_design.source_a\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_design.vcd"
recorded demo — :loads demo
:active-driver
<design-path-or-id> [range <msb:lsb>] [at <time>] [seq <signal-local-sequence|final>] - classify each bit-precise static driver as active, inactive or unknown using projected guards and waveform evidence; synchronize the target source/waveform and add mapped inputs; reopening an identical result is not an undo step
Try it
printf 'design index --top tutorial_design tutorial_design.sv\nactive-driver tutorial_design.selected at 10\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_design.vcd"
recorded demo — :active-driver demo
:tracex
<design-path-or-id> [range <msb:lsb>] [at <time>] [seq <signal-local-sequence|final>] - trace X/Z values backward through non-inactive bit-precise drivers with bounded traversal and explicit process/delta evidence gaps; add mapped trace signals; reopening an identical result is not an undo step
Try it
printf 'design index --top tutorial_design tutorial_design.sv\ntracex tutorial_design.selected at 10\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_design.vcd"
recorded demo — :tracex demo
Layout & Appearance
:set grid on
set grid on
Try it
printf 'addall\nset grid on\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :set grid on demo
:set grid off
set grid off
Try it
printf 'set grid on\nset grid off\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :set grid off demo
:set grid edge
set grid edge rise|fall|both [start <n>]
Try it
printf 'addall\nset grid edge rise\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :set grid edge demo
:set grid period
set grid period <t> [start <n>]
Try it
printf 'addall\nset grid period 100\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :set grid period demo
:set glitch
set glitch on|off
Try it
printf 'addall\nset glitch on\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_delta.kbx"
recorded demo — :set glitch demo
:set ic
set ic on|off
Try it
printf 'addall\nset ic on\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :set ic demo
:set base_symbols
set base_symbols on|off
Try it
printf 'addall\nset base_symbols on\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :set base_symbols demo
:set hiername
set hiername on|off
Try it
printf 'addall\nset hiername on\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :set hiername demo
:set highlight
set highlight on|off
Try it
printf 'addall\nset highlight off\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :set highlight demo
:set snap
set snap on|off
Try it
printf 'addall\nset snap on\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :set snap demo
:set cursorcenter
set cursorcenter on|off
Try it
printf 'addall\nset cursorcenter on\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :set cursorcenter demo
:set timeunit
set timeunit <unit>
Try it
printf 'addall\nset timeunit us\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :set timeunit demo
:set timeunit file
set timeunit file
Try it
printf 'set timeunit us\nset timeunit file\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :set timeunit file demo
:panels signals
panels signals toggle
Try it
printf 'addall\npanels signals toggle\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :panels signals demo
:panelwidth
panelwidth <cols>|auto
Try it
printf 'addall\npanelwidth 40\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :panelwidth demo
:split
- add a wave pane below with its own display list and time window (T17, up to 4 panes); the new pane copies the focused one, Tab cycles the focus, :only collapses back to one
Try it
printf 'addall\nsplit\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :split demo
:only
- collapse the split wave view back to a single pane, keeping the focused pane's view and display list (T17); already being in one pane is not an undo step
Try it
printf 'addall\nsplit\nonly\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :only demo
:theme
<builtin|file.yaml> - switch the skin: a built-in preset or a YAML file with colors/icons/palette; theme changes are restored by Ctrl+Z, while no argument only lists the presets
Try it
printf 'addall\ntheme dracula\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :theme demo
Sessions & Automation
:plugins
[focus|unload <id>] | plugins actions <manifest> | plugins run <manifest> <action> '<arguments-json>' | plugins exec [manifest] <command> <scope> [start end] - list and manage loaded UI plugins, or discover/run manifest actions and short namespaced command presets through the shared headless service
Try it
printf 'plugins\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :plugins demo
:plugins complete-ids
[focus|unload <id>] | plugins actions <manifest> | plugins run <manifest> <action> '<arguments-json>' | plugins exec [manifest] <command> <scope> [start end] - list and manage loaded UI plugins, or discover/run manifest actions and short namespaced command presets through the shared headless service
Try it
printf '\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :plugins complete-ids demo
:plugins complete-commands
[focus|unload <id>] | plugins actions <manifest> | plugins run <manifest> <action> '<arguments-json>' | plugins exec [manifest] <command> <scope> [start end] - list and manage loaded UI plugins, or discover/run manifest actions and short namespaced command presets through the shared headless service
Try it
printf '\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_protocol.vcd"
recorded demo — :plugins complete-commands demo
:plugins actions
[focus|unload <id>] | plugins actions <manifest> | plugins run <manifest> <action> '<arguments-json>' | plugins exec [manifest] <command> <scope> [start end] - list and manage loaded UI plugins, or discover/run manifest actions and short namespaced command presets through the shared headless service
Try it
printf 'plugins actions tcl-protocol-decoder/plugin.json\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :plugins actions demo
:plugins run
[focus|unload <id>] | plugins actions <manifest> | plugins run <manifest> <action> '<arguments-json>' | plugins exec [manifest] <command> <scope> [start end] - list and manage loaded UI plugins, or discover/run manifest actions and short namespaced command presets through the shared headless service
Try it
printf 'plugins run tcl-protocol-decoder/plugin.json check '{"profile":"ready-valid","clock":"top.clk","start":0,"end":65,"roles":{"valid":"top.valid","ready":"top.ready"}}'\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_protocol.vcd"
recorded demo — :plugins run demo
:plugins run-x
[focus|unload <id>] | plugins actions <manifest> | plugins run <manifest> <action> '<arguments-json>' | plugins exec [manifest] <command> <scope> [start end] - list and manage loaded UI plugins, or discover/run manifest actions and short namespaced command presets through the shared headless service
Try it
printf 'plugins run tcl-x-detective/plugin.json scan '{"signal":"top.instruction_bus","start":0,"end":100,"class":"X","min_duration":1}'\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :plugins run-x demo
:plugins run-triage
[focus|unload <id>] | plugins actions <manifest> | plugins run <manifest> <action> '<arguments-json>' | plugins exec [manifest] <command> <scope> [start end] - list and manage loaded UI plugins, or discover/run manifest actions and short namespaced command presets through the shared headless service
Try it
printf 'plugins run tcl-failure-triage/plugin.json triage '{"file":"tcl-failure-triage/failures.junit.xml","limit":32}'\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :plugins run-triage demo
:plugins run-cli-protocol
[focus|unload <id>] | plugins actions <manifest> | plugins run <manifest> <action> '<arguments-json>' | plugins exec [manifest] <command> <scope> [start end] - list and manage loaded UI plugins, or discover/run manifest actions and short namespaced command presets through the shared headless service
Try it
printf '\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_protocol.vcd"
recorded demo — :plugins run-cli-protocol demo
:plugins run-cli-transactions
[focus|unload <id>] | plugins actions <manifest> | plugins run <manifest> <action> '<arguments-json>' | plugins exec [manifest] <command> <scope> [start end] - list and manage loaded UI plugins, or discover/run manifest actions and short namespaced command presets through the shared headless service
Try it
printf '\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_transactions.vcd"
recorded demo — :plugins run-cli-transactions demo
:plugins run-cli-x
[focus|unload <id>] | plugins actions <manifest> | plugins run <manifest> <action> '<arguments-json>' | plugins exec [manifest] <command> <scope> [start end] - list and manage loaded UI plugins, or discover/run manifest actions and short namespaced command presets through the shared headless service
Try it
printf '\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :plugins run-cli-x demo
:plugins run-cli-triage
[focus|unload <id>] | plugins actions <manifest> | plugins run <manifest> <action> '<arguments-json>' | plugins exec [manifest] <command> <scope> [start end] - list and manage loaded UI plugins, or discover/run manifest actions and short namespaced command presets through the shared headless service
Try it
printf '\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :plugins run-cli-triage demo
:plugins exec
[focus|unload <id>] | plugins actions <manifest> | plugins run <manifest> <action> '<arguments-json>' | plugins exec [manifest] <command> <scope> [start end] - list and manage loaded UI plugins, or discover/run manifest actions and short namespaced command presets through the shared headless service
Try it
printf 'plugins exec ready-valid top 0 65\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_protocol.vcd"
recorded demo — :plugins exec demo
:save
<file.svw> - save the waveform session, complete ordered open-file list and stable source-debug target; a closed cockpit stays closed when restored and can be reopened with :sourceview refresh
Try it
printf 'addall\nsave session.svw\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :save demo
:save multifile-session
<file.svw> - save the waveform session, complete ordered open-file list and stable source-debug target; a closed cockpit stays closed when restored and can be reopened with :sourceview refresh
Try it
printf 'open tutorial_demo_b.vcd\nsave multifile-session.svw\nactive 1\nsource multifile-session.svw\nfiles\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :save multifile-session demo
:record
<file.svw>|off - record session commands to a :source-replayable script (T16)
Try it
printf 'addall\nrecord session.svwcmd\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :record demo
:source
<file.svw|file.svwcmd> - restore an svw 1 session as one undoable state change, including its ordered open-file list and stable source-debug target without reopening a saved closed cockpit, or replay a command script using each contained command's own undo boundary; reloading an identical session and scripts containing only read-only/external commands create no empty Ctrl+Z layer
Try it
printf 'addall\nsource tutorial_demo.svwcmd\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :source demo
Help & Application
:q
- quit
Try it
printf 'q\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :q demo
:q!
- quit immediately without confirmation
Try it
printf 'q!\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :q! demo
:version
- show version
Try it
printf 'version\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :version demo
:help
[cmd] - open the help page, or show one command's usage
Try it
printf 'addall\nhelp zoom\n' | svw --headless "$(dirname "$(which svw)")/../share/svw/samples/tutorial_demo.vcd"
recorded demo — :help demo
Mouse right-click actions
right-click: Signal Detail
open the selected signal's detailed value and type view
recorded demo — right-click: Signal Detail
right-click: Copy Full Path
copy the full hierarchical signal path
recorded demo — right-click: Copy Full Path
right-click: Radix
cycle the selected signal's display radix
recorded demo — right-click: Radix
right-click: Alias
open the command line prefilled for the selected signal
recorded demo — right-click: Alias
right-click: Expand
expand a vector into bit rows, then right-click a child and combine the complete block again
recorded demo — right-click: Expand
right-click: Combine
combine an expanded bit block back into its original vector, then right-click that parent and expand it again
recorded demo — right-click: Combine
right-click: Highlight
toggle persistent row highlighting
recorded demo — right-click: Highlight
right-click: Elaborated Source
dock syntax-highlighted elaborated RTL with its focus line
recorded demo — right-click: Elaborated Source
right-click: Drivers
show the static semantic drivers of the mapped object
recorded demo — right-click: Drivers
right-click: Trace X
trace an unknown value from the current waveform time
recorded demo — right-click: Trace X
right-click: Delete Row
remove the selected display row
recorded demo — right-click: Delete Row
right-click: Group Marked
group the current batch-marked rows
recorded demo — right-click: Group Marked
right-click: Collapse Group
collapse or expand a group from its header
recorded demo — right-click: Collapse Group
right-click: Combine Group as Vector
concatenate a group's signals into one retained vector
recorded demo — right-click: Combine Group as Vector
right-click: Rename Group
open the selected group name in the command editor and rename the complete group block
recorded demo — right-click: Rename Group
right-click: Delete Group
delete a complete bounded group block
recorded demo — right-click: Delete Group
right-click: Ungroup
dissolve a group while retaining its signal rows
recorded demo — right-click: Ungroup
right-click: Restore Combined Group
restore the original bounded group through the combined vector's Restore Group action
recorded demo — right-click: Restore Combined Group
mouse: right-click places the alt marker
right-click in the wave area drops the alt/baseline marker (edge-snapped), same as middle click or Alt+click
recorded demo — mouse: right-click places the alt marker
UI essentials
n/N search marker follows both directions
lowercase n and uppercase N move the primary yellow marker, select the globally matched signal, stay visible and wrap between the first and last results
recorded demo — n/N search marker follows both directions
Left/Right search with an empty signal panel
Right finds and reveals the first value match even when no row is selected; Left follows the same cyclic search state back to the last match.
recorded demo — Left/Right search with an empty signal panel
extract to an indexed KBX, then search globally
stream any supported waveform into a globally ordered KBX; the next headless search uses its source-wide directory instead of scanning every signal.
recorded demo — extract to an indexed KBX, then search globally
read-only command scripts add no empty undo layer
:source delegates Ctrl+Z ownership to each replayed command; a version-only script leaves the preceding marker as the next real undo action
recorded demo — read-only command scripts add no empty undo layer
reloading an identical session adds no empty undo layer
The first svw 1 restore remains one transaction; repeating the same canonical state leaves Ctrl+Z pointed at that restore and returns directly to the later marker
recorded demo — reloading an identical session adds no empty undo layer
closed source cockpits retain their session target
Saving after Esc keeps the stable source target without reopening the dismissed cockpit; after another target is visited, loading the session and refreshing returns to the saved object
recorded demo — closed source cockpits retain their session target
expand exact internal evidence IDs
Reports prioritize human-readable evidence while retaining exact object, signal, assignment and process IDs behind i
recorded demo — expand exact internal evidence IDs
analysis queries do not hide the previous undo
Reopening or recomputing the same comparison and updating explicit cross-probe state leave the previous waveform edit directly reachable by one Ctrl+Z; the remap remains visible
recorded demo — analysis queries do not hide the previous undo
KBX refresh does not create a false Model undo
Refreshing the explicit external KBX snapshot never hides the previous waveform edit behind a partial undo layer
recorded demo — KBX refresh does not create a false Model undo
source refresh follows the Ctrl+Z target
Ctrl+Z restores the source target together with waveform and cockpit state, so refresh cannot jump back to the later object
recorded demo — source refresh follows the Ctrl+Z target
reopening source debug results is not an undo step
Repeated sourceview, active-driver and Trace-X reports leave the preceding real marker state reachable by one Ctrl+Z
recorded demo — reopening source debug results is not an undo step
static drivers own one source-cockpit undo step
A distinct drivers target is undoable, while reopening its identical result adds no empty layer; one Ctrl+Z restores the previous source target without clearing the marker
recorded demo — static drivers own one source-cockpit undo step
static loads own one source-cockpit undo step
A distinct loads target is undoable, while reopening its identical result adds no empty layer; one Ctrl+Z restores the previous source target without clearing the marker
recorded demo — static loads own one source-cockpit undo step
command editor wraps without hiding input
As a command reaches the right edge, the editor grows by one row and keeps every typed character visible.
recorded demo — command editor wraps without hiding input
recall commands from the launch directory
svw loads a validated .svw_history file at startup; Up and Down recall commands entered in earlier runs.
recorded demo — recall commands from the launch directory
return from history to the unfinished draft
After Up recalls the newest command, Down crosses the history boundary and restores the exact unfinished command instead of clearing it; Down on a fresh draft is a no-op.
recorded demo — return from history to the unfinished draft
reject unsafe command history before recall
A correct header is not sufficient when a history command contains invalid UTF-8 or terminal controls; svw rejects the whole file, so Up remains empty and version runs normally.
recorded demo — reject unsafe command history before recall
reject an oversized persisted command before recall
A correct-header history file cannot make Up copy and render an unbounded command; the whole file stays untouched and the newly typed version command runs normally.
recorded demo — reject an oversized persisted command before recall
type and edit Unicode without splitting characters
Raw UTF-8 input reaches the command editor atomically; Left and Backspace remove the emoji before Z as one visible character while preserving CJK and a combining accent.
recorded demo — type and edit Unicode without splitting characters
signal search keeps a long query's cursor visible
The one-row search prompt follows the active edit point, preserves Unicode display-cluster boundaries and reserves space for its result count instead of hiding typed text.
recorded demo — signal search keeps a long query's cursor visible
record byte-exact terminal diagnostics
--terminal-record writes a compressed trace of output bytes, write results, input, resize events and terminal environment metadata for offline diagnosis and replay.
recorded demo — record byte-exact terminal diagnostics
scroll a large signal panel without glyph artifacts
Fast signal-list scrolling completes every terminal frame; UTF-8 glyphs are never left truncated or replaced.
recorded demo — scroll a large signal panel without glyph artifacts
combining-character incremental repaint integrity
Selection updates retain each base-plus-combining sequence as one terminal cell; a same-size full repaint leaves the signal and waveform panels unchanged.
recorded demo — combining-character incremental repaint integrity
mouse: click to select a signal
click a signal row to select it
recorded demo — mouse: click to select a signal
mouse: double click reveals the scope
double click a signal row to open the picker filtered to the signal's scope
recorded demo — mouse: double click reveals the scope
mouse: click to place the marker
click anywhere in the wave area to drop the primary marker (edge-snapped)
recorded demo — mouse: click to place the marker
mouse: drag a box selection
drag in the wave area for a highlighted box selection
recorded demo — mouse: drag a box selection
mouse: wheel scrolls the time axis
wheel up/down scrolls the waveform in time
recorded demo — mouse: wheel scrolls the time axis
mouse: toolbar buttons
click toolbar buttons (here: zoom in)
recorded demo — mouse: toolbar buttons
mouse: overview minimap
click the TIME overview to jump the view there
recorded demo — mouse: overview minimap
large timestamps stay separated
wide ruler labels are thinned with a blank-cell safety gap instead of overwriting adjacent timestamps
recorded demo — large timestamps stay separated
modern terminal: drag the panel divider
a truecolor/256-color terminal uses jittered SGR mouse packets to drag the signal panel's right border; x/z ranges keep their semantic tint but render one centered value like numeric bus ranges, and the scrollbar remains explicit
recorded demo — modern terminal: drag the panel divider
8-color-class UTF-8 terminal: basic mouse resize
an older UTF-8 terminal with no 256-color declaration can resize the signal panel from basic X10 press/release alone; Unicode chrome, centered x/z values, semantic backgrounds and the scrollbar remain visible
recorded demo — 8-color-class UTF-8 terminal: basic mouse resize
8-color Unicode: resize the power-failure fixture
the exact tutorial_power_failure.vcd case accepts a basic X10 drag from the visible signal-panel border; the expanded panel exposes retention_restore in full
recorded demo — 8-color Unicode: resize the power-failure fixture
8-color Unicode: resize without a scrollbar
at 240x120 all seven traces fit without a scrollbar; a UTF-8-extended X10 drag still expands the signal name
recorded demo — 8-color Unicode: resize without a scrollbar
mouse: a row drag cannot become a double-click
after reordering a signal, an immediate click at the old screen cell selects its new row instead of opening search
recorded demo — mouse: a row drag cannot become a double-click
mouse: scrollbar capture survives pane overshoot
dragging past the narrow divider and below the pane keeps tracking and clamps the Signal Panel to its final row
recorded demo — mouse: scrollbar capture survives pane overshoot
non-Unicode toolbar uses readable mnemonics
ASCII mode labels full view as F, rising/falling edges as ^/v with direction, any edge as E, search as / and grid as G; the complete label remains clickable
recorded demo — non-Unicode toolbar uses readable mnemonics
Signal Detail: mouse value format
click the pinned Binary button; h/b/u/s/f provide the same hex, binary, unsigned, signed and float choices
recorded demo — Signal Detail: mouse value format
Signal Detail: one-bit scalar formats
A one-bit scalar offers only bit-oriented hex/binary views; signed and unsigned vector choices are absent.
recorded demo — Signal Detail: one-bit scalar formats
Signal Detail yank: Unicode character count
copy a detail page containing CJK and a combining cluster; the toast reports visible characters instead of UTF-8 bytes
recorded demo — Signal Detail yank: Unicode character count
right-click: empty groups expose only valid actions
an empty group omits Combine as Vector because no signal bits exist to concatenate
recorded demo — right-click: empty groups expose only valid actions
mouse: pin Signal Detail hover
pause over a signal row to anchor the exact K Signal Detail content at the pointer; three fixed dots pulse for 3.5 seconds before PINNED appears, and q/Esc dismisses it
recorded demo — mouse: pin Signal Detail hover
mouse: pin and close a group summary
pause over a group title to pin its member/collapse summary after 3.5 seconds; q or Esc closes it immediately even when the terminal has queued passive mouse motion
recorded demo — mouse: pin and close a group summary
mouse: inspect source from pinned Signal Detail
a semantically mapped signal shows its source path and line; after the detail float pins, hover that link for bounded, syntax-highlighted SystemVerilog context
recorded demo — mouse: inspect source from pinned Signal Detail
mouse: pin a folded-cell tooltip
hover a wave cell containing multiple changes; the common 3.5-second timer pins the value chain until q/Esc
recorded demo — mouse: pin a folded-cell tooltip
SPC / searchvalue shortcut
Space then / prefills :searchvalue; enter a value pattern and n jumps to the first global match. Space Space and Space s / remain the signal-picker shortcuts.
recorded demo — SPC / searchvalue shortcut
? help page
? opens the full help overlay; search jumps directly to the task-grouped command index
recorded demo — ? help page
help search follows a long query's edit point
Help, report and detail overlays share a bounded, Unicode-safe less-style search editor; its direction sigil stays fixed while the query tail and block cursor remain visible.
recorded demo — help search follows a long query's edit point
help wraps long command descriptions
Long command usage and description text wraps inside the help panel instead of being clipped at the right edge.
recorded demo — help wraps long command descriptions
picker: Ctrl+A adds all matches
in the / Telescope picker, Ctrl+A adds every current match to the display list at once (same as :find)
recorded demo — picker: Ctrl+A adds all matches
group label band
groups are one-line colored labels; Enter or a click collapses them and shows the member count
recorded demo — group label band
Enter on a scalar is quiet
Enter folds groups and expanded vectors; on an ordinary scalar signal it intentionally does nothing
recorded demo — Enter on a scalar is quiet
expanded-vector parent and children
:expand retains a disclosure parent; its child bits are joined by a continuous │/├─/└─ tree, and Enter folds or opens the complete child block
recorded demo — expanded-vector parent and children
mouse: open a folded vector
even a fast double-click on the retained vector parent opens its complete bit tree exactly once and never searches
recorded demo — mouse: open a folded vector
mouse: double-click collapses a group once
a fast second click is absorbed after the first collapse, so the group stays folded instead of immediately reopening
recorded demo — mouse: double-click collapses a group once
Ctrl+Z undo with terminal keyboard reporting
a cancelled dd prefix and inert scalar Enter do not consume undo; one CSI-u Ctrl+Z restores the last real edit
recorded demo — Ctrl+Z undo with terminal keyboard reporting
boundary no-ops preserve the previous Ctrl+Z action
minimum panel/row sizing, an unchanged color, and an already-full zoom create no empty undo layers
recorded demo — boundary no-ops preserve the previous Ctrl+Z action
Ctrl+Z restores a keyboard signal reorder
Alt+Down moves the selected trace as a real display-list edit; one Ctrl+Z restores its original position
recorded demo — Ctrl+Z restores a keyboard signal reorder
Ctrl+Z clears keyboard batch marks in place
v and Shift+Enter each create one recoverable VISUAL mark; one Ctrl+Z clears that mark without removing signals added before it
recorded demo — Ctrl+Z clears keyboard batch marks in place
Ctrl+Z clears mouse batch marks in place
Ctrl+click and Shift+click each own one recoverable VISUAL mark; undo clears the mark or range without deleting the existing signal rows
recorded demo — Ctrl+Z clears mouse batch marks in place
Ctrl+Z restores keyboard marker placement
m and b each own one marker snapshot; Ctrl+Z clears only that primary or baseline marker and preserves the signals added before it
recorded demo — Ctrl+Z restores keyboard marker placement
idempotent marker commands do not consume Ctrl+Z
repeating mark/bmark/drop or clearing an absent primary, baseline or named marker leaves undo pointed at the last real session edit
recorded demo — idempotent marker commands do not consume Ctrl+Z
signal-list no-ops preserve the previous Ctrl+Z action
repeated add, alias, highlight, sort, find, and filter requests create no invisible undo layers
recorded demo — signal-list no-ops preserve the previous Ctrl+Z action
repeated settings preserve the previous Ctrl+Z action
active set, delta, and time-shift values plus a default-off toggle create no invisible undo layers
recorded demo — repeated settings preserve the previous Ctrl+Z action
format and layout no-ops preserve the previous Ctrl+Z
repeating the active pane, panel width, trace format or analog mode, listing annotations, and clearing empty compare results create no invisible undo layers
recorded demo — format and layout no-ops preserve the previous Ctrl+Z
idempotent commands preserve the previous Ctrl+Z
current-file selection, empty mark clears, in-place move, repeated color/stuck/alias/group state, and unchanged alias file loads create no invisible undo layers
recorded demo — idempotent commands preserve the previous Ctrl+Z
repeated pattern marks preserve the previous Ctrl+Z
reapplying an unchanged :pattern -all result does not hide the previous real edit behind an empty undo layer
recorded demo — repeated pattern marks preserve the previous Ctrl+Z
searchvalue queries preserve the previous Ctrl+Z
querying the active search or repeating its mode, pattern and signal scope creates no invisible undo layer
recorded demo — searchvalue queries preserve the previous Ctrl+Z
unchanged vector and bus commands preserve Ctrl+Z
reopening an already-open selected vector or reapplying an identical virtual-bus splice creates no invisible undo layer
recorded demo — unchanged vector and bus commands preserve Ctrl+Z
external databases do not consume waveform Ctrl+Z history
coverage load/clear and design clear remain explicit database lifecycle operations while one Ctrl+Z still reaches the preceding waveform edit
recorded demo — external databases do not consume waveform Ctrl+Z history
Ctrl+Z restores mouse marker placement
wave clicks place the primary marker, while Alt+click, middle-click and right-click place the baseline marker; each gesture is independently undoable
recorded demo — Ctrl+Z restores mouse marker placement
cancelled color picker creates no empty undo layer
q or Esc cancels the deferred palette without consuming Ctrl+Z; the next undo reaches the preceding real edit
recorded demo — cancelled color picker creates no empty undo layer
Ctrl+Z restores the previous visual theme
a real theme switch owns one complete session snapshot; undo restores the prior skin without removing signals, while a theme query creates no empty undo layer
recorded demo — Ctrl+Z restores the previous visual theme
Ctrl+Z restores a mouse signal reorder
a completed row drag owns one undo snapshot; Ctrl+Z restores the original signal ordering
recorded demo — Ctrl+Z restores a mouse signal reorder
Ctrl+Z reopens a mouse-folded group
a structural label click owns one undo snapshot; Ctrl+Z reopens the same group instead of deleting it
recorded demo — Ctrl+Z reopens a mouse-folded group
Ctrl+Z restores a Ctrl+wheel zoom
mouse-centered zoom owns one undo snapshot and restores the prior time view without removing signals
recorded demo — Ctrl+Z restores a Ctrl+wheel zoom
Ctrl+Z restores a mouse box zoom
releasing a waveform box selection commits one undo snapshot for the resulting zoom
recorded demo — Ctrl+Z restores a mouse box zoom
Ctrl+Z restores a mouse panel resize
the divider redraws live while dragging and commits one undo snapshot when released
recorded demo — Ctrl+Z restores a mouse panel resize
Ctrl+Z restores a toolbar grid toggle
the visible grid button owns one display-state snapshot; undo restores the grid without removing earlier signals
recorded demo — Ctrl+Z restores a toolbar grid toggle
contextual help for the tree window
? explains the active popup; closing help returns to the tree with its selection and lazy state intact
recorded demo — contextual help for the tree window
contextual help for reports
? opens the report window's scrolling, search, copy and identifier-display key reference
recorded demo — contextual help for reports
contextual help for signal detail
? explains value formats, mouse selection, navigation and copy behavior for the active signal-detail window
recorded demo — contextual help for signal detail
contextual help for the color picker
? explains movement, apply and cancel keys for the active color palette
recorded demo — contextual help for the color picker
contextual help for the marker list
? explains marker selection and time jumping without discarding the marker-list state
recorded demo — contextual help for the marker list
contextual help for quit confirmation
? explains confirmation and cancellation without itself accepting or dismissing the pending quit
recorded demo — contextual help for quit confirmation
:<N> signal jump
type a signal's panel number as a command to jump straight to it (vim line jump)
recorded demo — :<N> signal jump
:<time> wave jump
with W focus, type a time directly to move the waveform; bare values use the file timescale and explicit s/ms/us/ns/ps/fs suffixes are converted strictly
recorded demo — :<time> wave jump
G: last signal
G jumps to the last visible signal (vim)
recorded demo — G: last signal
gg: first signal
gg jumps back to the first visible signal (vim)
recorded demo — gg: first signal
Tcl UI plugin: layouts and emergency recovery
--plugin explicitly starts a user-provided tclsh out of process; waveform/session files never auto-load code. The plugin owns bounded retained cells, can dock beside the live built-in frame or take over the full TUI, and never receives the terminal descriptor or a C++ object pointer
recorded demo — Tcl UI plugin: layouts and emergency recovery
Tcl UI plugin: native signal/wave linkage
a plugin row submits a typed signal/time anchor; svw atomically selects and flashes the native signal row, moves the native waveform cursor, and transfers focus from the plugin surface to the W panel
recorded demo — Tcl UI plugin: native signal/wave linkage
multiple Tcl panels: adaptive layout and native linkage
svw composes independent plugin windows beside the native signal/wave frame, owns their draggable and Alt+arrow dividers, and lets a plugin result reveal its typed signal and time back in the native panels
recorded demo — multiple Tcl panels: adaptive layout and native linkage
Tcl protocol checker profiles and clocked evidence
one bounded native clock-edge sampler feeds reusable Tcl checker state and normalized results; mouse or keyboard switches protocol profiles without rebuilding svw
recorded demo — Tcl protocol checker profiles and clocked evidence
X/Z detective: bounded unknown intervals and native jump
scan the selected signal over the visible range, list exact X, Z and mixed-unknown intervals, then reveal one interval in the native signal and waveform panels
recorded demo — X/Z detective: bounded unknown intervals and native jump
failure triage: regression evidence linked to waveform time
load a bounded non-executable regression failure report, review the selected failure, and jump its exact time into the native waveform
recorded demo — failure triage: regression evidence linked to waveform time
animated wait feedback
operations that cross the response threshold keep the TUI alive with an elapsed-time spinner until their result is published
recorded demo — animated wait feedback
v batch marks (VISUAL)
v or Shift+Enter marks signal rows and the status line shows VISUAL (Esc clears); :group without selectors groups the marked set, and Tab offers it as the first candidate
recorded demo — v batch marks (VISUAL)
group an expanded vector block
:expand keeps the vector and reveals bit rows; marking a child for a structural edit carries the retained parent and complete child block
recorded demo — group an expanded vector block
mouse batch marks (Ctrl/Shift+click)
Ctrl+click toggles a row's batch mark, Shift+click marks the range from the selected row (VISUAL badge); :group without selectors groups the marked set
recorded demo — mouse batch marks (Ctrl/Shift+click)
Plugin Development
Tcl UI plugins
svw loads Tcl UI plugins only when the user explicitly supplies --plugin <manifest>. Waveforms and session files never cause executable plugin code to load.
Each plugin runs in a separate user-provided tclsh process. Set SVW_TCLSH when the interpreter is not named tclsh on PATH. svw and the child exchange bounded newline-delimited JSON-RPC 2.0 using the exact protocol name svw.ui.plugin.v1; svw does not link or ship a Tcl runtime. A breaking wire change will use a new exact protocol name. Existing v1 fields and meanings are never repurposed after release.
The complete method, notification, type, quota, and compatibility contract is in Tcl UI Plugin Protocol v1. Manifest capabilities are mandatory; supported entries in optional_capabilities are granted when available. The hello response is the authority for granted capabilities and discoverable methods, notifications, limits, and enum values. Tcl plugins use ::svw::has_capability before an optional operation; ::svw::supports_method, ::svw::supports_notification and ::svw::supports_value probe additive host features.
Plugins may submit retained surfaces and atomic layout contributions. Repeat --plugin to run several independent panels in one session, for example svw wave.kbx --plugin smart/plugin.json --plugin temporal/plugin.json. svw keeps one native frame, adaptively packs all contributed windows, and owns the mouse dividers and Alt+arrow resizing between them. svw remains the sole terminal owner: plugins cannot write ANSI to the TUI, receive a tty descriptor, or access C++ objects. An opaque fullscreen surface replaces the core frame and prevents unnecessary waveform rendering until it is removed. Three consecutive Escape keys terminate the focused plugin and atomically restore the native layout even if the plugin stops responding.
:plugins lists explicitly loaded plugins, versions, states, and declared actions. :plugins focus <id> focuses its first ready surface and :plugins unload <id> stops it. These host-only operations cannot cross a plugin's core.command.invoke boundary.
A manifest may declare a separate headless.entry and bounded actions. Agent, MCP, JSON-RPC, and :plugins run adapt the same sampling/execution service and result schema; they do not duplicate protocol logic or expose a generic command bridge. clocked-samples.v1 supplies bounded clock-aligned values with explicit native or pre-edge sampling and separate event/sample times, event-samples.v1 supplies guarded named/wildcard/edge unions, extraction-plan.v1 merges independent named sources and typed payloads, property-captures.v1 supplies typed match/assert/deassert/switch evidence, signal-changes.v1 supplies a bounded final-value transition stream, and bounded-arguments.v1 forwards only the caller's size-limited JSON object to an explicitly selected trusted plugin. The protocol checker, X/Z detective, and failure-triage examples exercise all three adapters.
headless.commands adds namespaced presets without registering global : commands. With the plugin loaded, :plugins exec ready-valid top.dut_if uses the visible range. The explicit form is :plugins exec path/plugin.json ready-valid top.dut_if [start end].
The dependency-free v1 SDK and example are published from the same reviewed source as fixed static-site downloads. Save the .txt responses with the shown runtime names; they are deliberately not included in the GitHub binary Release archive:
SDK, example entry, manifest, and SHA-256 list.
curl -fLO https://svw.run/assets/svw-plugin-sdk-v1.txt
curl -fLO https://svw.run/assets/svw-plugin-example-main-v1.txt
curl -fLO https://svw.run/assets/svw-plugin-example-manifest-v1.txt
curl -fLO https://svw.run/assets/svw-plugin-downloads-v1.sha256.txt
# Linux:
sha256sum -c svw-plugin-downloads-v1.sha256.txt
# macOS uses: shasum -a 256 -c svw-plugin-downloads-v1.sha256.txt
mv svw-plugin-sdk-v1.txt svw_plugin.tcl
mv svw-plugin-example-main-v1.txt main.tcl
mv svw-plugin-example-manifest-v1.txt plugin.json
svw waveform.kbx --plugin plugin.json
The tracked development copies remain in examples/tcl-ui-plugin. The static publisher copies their exact bytes and generates the checksum list; it never exports svw product source.
ui.draw, ui.focus, ui.fullscreen, ui.layout, and ui.report are independent manifest capabilities. A plugin granted core.command.invoke can call an existing public svw command through ::svw::command; internal development commands are rejected at the command-registry boundary. The host runs accepted commands through the normal command path and returns ok plus the normal message/diagnostic to the optional callback. That capability is intentionally powerful: a public command has the same mutation, file-output and quit behavior as interactive use; it is not a read-only automation channel. Capabilities protect svw API ownership; they are not an operating-system sandbox. Tcl plugins are trusted local code, and Tcl's own exec has the user's operating-system authority. v1 therefore does not claim a misleading process.exec sandbox.
core.state.read provides a bounded core.state.get snapshot, and wave.read provides the lazy point query wave.value.get, the bounded resolved-change query wave.changes.range, and the paged, direct-child hierarchy query wave.scope.children. The Tcl helpers accept an optional callback that receives the raw JSON result line. The dependency-free SDK includes a bounded structural JSON decoder; field helpers take an explicit path, for example string_field $line {result message} and string_field $line {result sample value}. They never search unrelated nested objects for a matching key, so additive response fields cannot silently change which value a callback reads. json_decode, json_get, json_type, and json_payload are available when a callback needs a value not covered by a typed field helper. The first protocol version intentionally does not expose an unbounded “all changes” or recursive hierarchy request: custom views use bounded data APIs, so a large waveform cannot silently defeat svw's lazy reader.
ui.report lets a plugin publish one bounded immutable snapshot into the native report overlay through ::svw::report_publish. The host validates and copies the complete title/line set before opening it. The copy remains valid after plugin unload and can be replaced by a later native or plugin report; report text is never executed.
core.navigation is the native-panel integration boundary. A plugin row with a signal anchor calls ::svw::reveal_signal; a row with a signal and native tick calls ::svw::reveal_wave; a time-only record calls ::svw::reveal_time. These are not command-string aliases. The host resolves and validates the whole target first, then selects and scrolls the native signal row, moves the native cursor, flashes the row/time intersection, and transfers keyboard focus from the plugin surface to the native S or W panel. Adding a signal is always an explicit boolean choice. Conversely, plugins subscribe to selection, cursor, view, and wave state groups so native panel changes refresh their bounded view.
Sixteen focused examples exercise the extension boundary:
examples/tcl-scope-browseradds a focusable hierarchy window beside theexamples/tcl-python-semantickeeps Python outside svw while preserving oneexamples/tcl-waveform-notebookcaptures the current cursor, selectedexamples/tcl-reset-clock-healthanalyzes the selected one-bit signal overexamples/tcl-x-detectivefinds X, Z, and mixed-unknown intervals on theexamples/tcl-protocol-decoderuses one native bounded clock-edge samplerexamples/tcl-failure-triageloads a bounded, non-executable regressionexamples/tcl-uvm-timelinefilters phase, objection, sequence,examples/tcl-coverage-holesbrowses bounded coverage exports and reusesexamples/tcl-temporal-queryevaluates a finite rise/fall/unknown/equalsexamples/tcl-smart-watchsequentially samples at most 64 explicit signalexamples/tcl-memory-inspectorlazily samples only the visible page of anexamples/tcl-fsm-visualizerdecodes bounded state transitions and directedexamples/tcl-simulator-controlowns a capability-negotiated externalexamples/tcl-debug-recipespreviews non-executable command recipes andexamples/tcl-workflow-consoledemonstrates plugin-local actions, public
native frame. Enter expands only the selected scope and requests one bounded page; collapsed rows show the known direct-scope count.
semantic authority. Its trusted Tcl process starts a bounded Python orchestrator, displays a spinner, and renders semantic driver/load results. Python asks the configured design compiler for the canonical bundle and consumes it only through the public svw agent JSON interface; it embeds no parser or duplicate AST projection. Configure executable paths with one-line python.txt, viewer.txt, and design-compiler.txt files beside the plugin when those tools are not on PATH.
signal, and a short user note in a bounded, non-executable sidecar. The plugin never sources sidecar content; selecting a note atomically reveals its signal and time in the native panels.
the visible interval. Bounded change pages drive clock period/jitter/duty and reset pulse reports; a fixed transition budget marks partial results instead of draining an arbitrarily dense signal.
selected signal, reports their duration, and reveals the selected signal/time target in the native panels.
plus reusable Tcl rule/state/result helpers. Switchable profiles cover ready/valid, AXI4/AXI4-Lite and ACE channels, AXI4-Stream, APB, AHB-Lite, Avalon-ST/MM, Wishbone B4, a TileLink channel, request/acknowledge, and generic FIFO or ID-tagged request/response correlation. The correlators report phase, latency, outstanding depth, reset termination, unmatched endpoints, multi-beat progress, and clipped-window completeness. AXI/ACE and TileLink profiles are explicitly channel-level checks, not full ordering, burst, cache, or coherence compliance.
failure report, shows failure details, and links each failure time to the native waveform without letting a log activate plugin code. An optional baseline report turns the same pane into a new/common/fixed regression diff.
configuration, resource, and register activity from a bounded simulator export aligned to the waveform's native time axis.
the public coverage source command for the selected file and line.
query language over one selected signal and the visible interval.
paths at the cursor without walking the waveform schema.
explicit address-to-signal memory map.
edge counts from a value-to-state map.
simulator adapter after explicit user connect and keeps requests bounded.
requires a second explicit action before sequential public-command execution.
command composition, and atomic native report publication without dynamic global command registration.
The Python example is an integration pattern, not a new svw dependency or a second design database contract. Production integrations should consume the same elaborated source set and options used to build their design data; the small sources.f and demo.sv exist only to make the example reproducible.
The Tcl SDK writes protocol messages to stdout, so plugin logs must go to stderr. Its event handlers receive dictionaries for ui.resize, ui.layout, ui.input, and subscribed state.changed notifications. SDK drawing helpers buffer operations until surface_commit; one bounded ui.surface.update request then validates and publishes the complete generation atomically. RPC helpers return their request id. Core, waveform and surface commit helpers accept an optional callback; surface creation accepts it as -callback, and the other resource/layout/focus/subscription helpers accept it as their final optional argument. SDK option lists reject unknown names rather than silently accepting a misspelling. Host-owned UI resource effects from one plugin commit in receive order, while dependent Core operations must be issued from the first operation's callback. Protocol colors are default, palette:N, or #RRGGBB, never svw's internal integer encoding. Surface and layout ids remain plugin-local on the wire. An ui.layout dictionary includes placed, current generation, and the effective rectangle; node is non-empty only while the surface is embedded in the active layout. A false placed value therefore lets a plugin redraw the surface at its declared rectangle after layout restore instead of guessing from a missing event. Panels must redraw from this effective rectangle rather than assuming their requested cells remained exact: another plugin, a terminal resize, or a user divider drag may legitimately change both width and height. After the hello response, ::svw::limit <name> returns the negotiated session limit so a plugin does not need to embed host constants.
Tcl UI Plugin Protocol v1 Reference
This is the compatibility reference for svw.ui.plugin.v1. The protocol is used only for plugins explicitly selected with svw --plugin <manifest>. Waveform and session files never activate plugins.
Compatibility
Every frame is one UTF-8 JSON-RPC 2.0 object followed by a newline. The maximum frame is 1 MiB. Empty lines, top-level JSON-RPC batches, and plugin notifications are not accepted in v1. A request is:
{"jsonrpc":"2.0","id":1,"method":"session.ping","params":{}}
The response contains the same id and exactly one of result or error. When a parse or invalid-request failure prevents the host from recovering a valid id, the error response uses id:null. An id is either a UTF-8 string of at most max_id_bytes or a signed 64-bit integer; floating-point ids and null are rejected. A plugin must not reuse an id while its response is pending. Plugins must ignore unknown result members and unknown host notifications. Requests are schema-strict: unknown envelope or params members are rejected so a misspelling cannot silently change behavior. Published v1 names, field types and semantics are not removed or repurposed; a breaking protocol uses a new exact name.
Compatibility is intentionally asymmetric. Request envelopes, params, and objects nested inside params are closed and schema-strict, so adding a request member to an existing v1 method is not a compatible extension. Result objects and their nested objects, error data, manifest root members, and host-notification params and their nested objects are open: receivers ignore members they do not know. New methods, notifications, capabilities and registered values may be added; plugins discover them from session.hello before use. A new value never takes on the meaning of an existing value.
Responses may be delayed and may arrive in a different order from requests. Host notifications such as ui.resize may appear between a request and its response. A client correlates only by id; it must continue dispatching host notifications while a core or waveform request is pending.
Host-owned UI resource, layout, focus and subscription effects from one plugin are committed in frame-receive order. A later such request therefore observes the successful effects of earlier such requests, which makes create–draw–focus and restore–destroy sequences deterministic. This guarantee does not order a deferred Core or waveform operation against later requests; when one Core result is an input to another request, the plugin sends the dependent request from the first request's callback.
Errors contain a numeric code, stable name, human-readable message, and may gain object-valued data. Plugins branch on name, not message.
| Error name | Code |
|---|---|
parse_error |
-32700 |
invalid_request |
-32600 |
method_not_found |
-32601 |
invalid_params |
-32602 |
internal_error |
-32603 |
capability_denied |
-32001 |
conflict |
-32002 |
not_found |
-32003 |
unavailable |
-32004 |
quota |
-32005 |
Manifest and handshake
{
"id": "example-ui",
"version": "1.0.0",
"entry": "main.tcl",
"protocol": "svw.ui.plugin.v1",
"capabilities": ["ui.draw", "ui.focus"],
"optional_capabilities": ["ui.layout"]
}
The entry must be a regular file inside the manifest directory. Capabilities in capabilities are required: an unknown or unavailable required capability rejects the manifest. optional_capabilities may name a capability unknown to an older v1 host; known items are granted and unknown items are ignored. The plugin checks the hello result's granted capabilities before using an optional feature. Duplicate names within or across the two arrays are rejected. v1 capabilities are core.command.invoke, core.navigation, core.state.read, wave.read, ui.draw, ui.focus, ui.fullscreen, ui.layout, and ui.report. Plugin and local resource ids use [A-Za-z0-9][A-Za-z0-9._-]*; entry paths reject control/NUL bytes and are canonicalized before the host verifies that they remain inside the manifest directory. version is opaque plugin metadata of at most 64 ASCII bytes using letters, digits, ., -, or +; svw does not infer protocol compatibility from it. A manifest is at most 64 KiB and the required-plus-optional capability count is at most 32.
The plugin process receives a scrubbed environment: the host keeps only its documented runtime baseline and variables whose names start with SVW_PLUGIN_CONFIG_. That prefix is an explicit user opt-in for non-secret plugin configuration, not a security boundary; plugins remain trusted local code. Arbitrary parent variables and secrets are not forwarded. The forwarded configuration is bounded to 64 variables and 64 KiB in total.
An optional headless declaration is independent of the UI handshake:
"headless": {
"entry": "headless.tcl",
"actions": [{"id":"check", "summary":"Bounded check",
"input":"clocked-samples.v1"}],
"commands": [{"id":"ready-valid", "summary":"Short check",
"action":"check", "profile":"ready-valid", "clock":"clk",
"roles":{"valid":"valid", "ready":"ready", "data":"data"}}]
}
The entry follows the same canonical in-directory regular-file rule. There are at most 16 unique actions; ids use the plugin id grammar and summaries are at most 160 bytes. An explicitly launched headless process receives one bounded JSON line, returns one svw.plugin.action-result.v1 line, and is terminated. Action input is one of clocked-samples.v1, event-samples.v1, extraction-plan.v1, property-captures.v1, signal-changes.v1, or bounded-arguments.v1. The extraction adapter merges named independent event sources, typed predicates, sample modes, and payloads into one bounded table. The property adapter accepts a typed expression, match|assert|deassert|switch, range, timestamp policy, and bounded limit; the host sends filtered captures, truth states, operands, and completeness. The event adapter accepts a bounded named/wildcard/edge union with per-term iff, explicit native or pre-edge sampling, and a role-to-signal tracked map. The host performs bounded waveform sampling before Tcl runs. The last adapter forwards the bounded JSON argument object unchanged and is intended for explicitly loaded trusted plugins that validate project artifacts themselves. There are at most 32 unique namespaced commands. Each targets a declared action and stores a profile plus relative clock/role leaves. plugins exec joins those leaves to one exact caller-supplied scope and defaults to the current visible range; optional paired start/end override it.
The base capability mapping is part of the frozen method registry:
| Method | Required capability |
|---|---|
session.hello |
- |
session.ping |
- |
session.shutdown |
- |
core.command.invoke |
core.command.invoke |
core.signal.reveal |
core.navigation |
core.time.reveal |
core.navigation |
core.wave.reveal |
core.navigation |
core.state.get |
core.state.read |
event.subscribe |
core.state.read |
event.unsubscribe |
core.state.read |
ui.focus.release |
ui.focus |
ui.focus.request |
ui.focus |
ui.layout.replace |
ui.layout |
ui.layout.restore |
ui.layout |
ui.report.publish |
ui.report |
ui.surface.create |
ui.draw |
ui.surface.destroy |
ui.draw |
ui.surface.update |
ui.draw |
wave.changes.range |
wave.read |
wave.samples.clocked |
wave.read |
wave.scope.children |
wave.read |
wave.value.get |
wave.read |
Parameter-selected authority is also frozen and machine checked:
| Method | Condition | Additional required capability |
|---|---|---|
ui.surface.create |
mode=fullscreen |
ui.fullscreen |
ui.surface.create |
focusable=true |
ui.focus |
The parameter and result object of every method are frozen as well. Scheduling is deliberately not exposed: any response may be deferred or reordered, so a plugin always correlates it by id and continues processing notifications.
| Method | Params object | Result object |
|---|---|---|
session.hello |
session.hello.params |
session.hello.result |
session.ping |
empty |
empty |
session.shutdown |
empty |
empty |
core.command.invoke |
core.command.invoke.params |
core.command.invoke.result |
core.signal.reveal |
core.signal.reveal.params |
core.signal.reveal.result |
core.time.reveal |
core.time.reveal.params |
core.time.reveal.result |
core.wave.reveal |
core.wave.reveal.params |
core.wave.reveal.result |
core.state.get |
empty |
core.state.get.result |
event.subscribe |
event.subscription.params |
empty |
event.unsubscribe |
event.subscription.params |
empty |
ui.focus.release |
empty |
empty |
ui.focus.request |
ui.focus.request.params |
empty |
ui.layout.replace |
ui.layout.replace.params |
empty |
ui.layout.restore |
empty |
empty |
ui.report.publish |
ui.report.publish.params |
ui.report.publish.result |
ui.surface.create |
ui.surface.create.params |
ui.surface.create.result |
ui.surface.destroy |
ui.surface.destroy.params |
empty |
ui.surface.update |
ui.surface.update.params |
ui.surface.update.result |
wave.changes.range |
wave.changes.range.params |
wave.changes.range.result |
wave.samples.clocked |
wave.samples.clocked.params |
wave.samples.clocked.result |
wave.scope.children |
wave.scope.children.params |
wave.scope.children.result |
wave.value.get |
wave.value.get.params |
wave.value.get.result |
The complete v1 object-field contract is frozen below. Objects not listed as having fields use an empty params or result object as described by their method. conditional means the field is required only in the condition stated in the surrounding prose.
| Object | Field | Type | Presence |
|---|---|---|---|
manifest |
id |
id |
required |
manifest |
version |
version |
required |
manifest |
entry |
relative-path |
required |
manifest |
protocol |
protocol |
required |
manifest |
capabilities |
capability[] |
required |
manifest |
optional_capabilities |
capability[] |
optional |
request |
jsonrpc |
literal:2.0 |
required |
request |
id |
string-or-int64 |
required |
request |
method |
method |
required |
request |
params |
object |
required |
response |
jsonrpc |
literal:2.0 |
required |
response |
id |
string-or-int64-or-null |
required |
response |
result |
object |
conditional |
response |
error |
error |
conditional |
notification |
jsonrpc |
literal:2.0 |
required |
notification |
method |
notification |
required |
notification |
params |
object |
required |
error |
code |
int |
required |
error |
name |
error-name |
required |
error |
message |
string |
required |
error |
data |
object |
optional |
session.hello.params |
protocol |
protocol |
required |
session.hello.params |
plugin |
id |
required |
session.hello.result |
protocol |
protocol |
required |
session.hello.result |
plugin |
id |
required |
session.hello.result |
capabilities |
capability[] |
required |
session.hello.result |
limits |
limit-map |
required |
session.hello.result |
methods |
method[] |
required |
session.hello.result |
notifications |
notification[] |
required |
session.hello.result |
values |
value-map |
required |
core.command.invoke.params |
command |
string |
required |
core.command.invoke.result |
ok |
bool |
required |
core.command.invoke.result |
message |
string |
required |
core.signal.reveal.params |
signal |
string |
required |
core.signal.reveal.params |
add |
bool |
required |
core.signal.reveal.result |
signal |
string |
required |
core.signal.reveal.result |
row |
nonnegative-int64 |
required |
core.signal.reveal.result |
added |
bool |
required |
core.signal.reveal.result |
filter_cleared |
bool |
required |
core.time.reveal.params |
time |
int64 |
required |
core.time.reveal.result |
time |
int64 |
required |
core.wave.reveal.params |
signal |
string |
required |
core.wave.reveal.params |
time |
int64 |
required |
core.wave.reveal.params |
add |
bool |
required |
core.wave.reveal.result |
signal |
string |
required |
core.wave.reveal.result |
time |
int64 |
required |
core.wave.reveal.result |
row |
nonnegative-int64 |
required |
core.wave.reveal.result |
added |
bool |
required |
core.wave.reveal.result |
filter_cleared |
bool |
required |
core.state.get.result |
state |
state |
required |
event.subscription.params |
events |
state-group[] |
required |
wave.value.get.params |
signal |
string |
required |
wave.value.get.params |
time |
int64 |
required |
wave.value.get.result |
sample |
wave-sample |
required |
wave.changes.range.params |
signal |
string |
required |
wave.changes.range.params |
start |
int64 |
required |
wave.changes.range.params |
end |
int64 |
required |
wave.changes.range.params |
cursor |
string |
required |
wave.changes.range.params |
limit |
positive-int |
required |
wave.changes.range.result |
page |
wave-changes-page |
required |
wave.samples.clocked.params |
clock |
string |
required |
wave.samples.clocked.params |
signals |
string[] |
required |
wave.samples.clocked.params |
start |
int64 |
required |
wave.samples.clocked.params |
end |
int64 |
required |
wave.samples.clocked.params |
edge |
clock-edge |
required |
wave.samples.clocked.params |
cursor |
string |
required |
wave.samples.clocked.params |
limit |
positive-int |
required |
wave.samples.clocked.result |
page |
clocked-samples-page |
required |
wave.scope.children.params |
scope |
string |
required |
wave.scope.children.params |
offset |
nonnegative-int64 |
required |
wave.scope.children.params |
limit |
positive-int |
required |
wave.scope.children.result |
page |
wave-scope-page |
required |
ui.report.publish.params |
title |
string |
required |
ui.report.publish.params |
lines |
string[] |
required |
ui.report.publish.result |
rows |
nonnegative-int64 |
required |
state |
file |
string |
required |
state |
format |
string |
required |
state |
timescale |
string |
required |
state |
minimum_time |
int64 |
required |
state |
maximum_time |
int64 |
required |
state |
cursor |
int64 |
required |
state |
view_start |
int64 |
required |
state |
view_end |
int64 |
required |
state |
focus |
focus-value |
required |
state |
selected_signal |
string |
required |
state |
wave_revision |
uint64 |
required |
state |
wave_loading |
bool |
required |
state |
wave_progress |
int:0..100 |
required |
wave-sample |
signal |
string |
required |
wave-sample |
time |
int64 |
required |
wave-sample |
width |
uint32 |
required |
wave-sample |
kind |
wave-kind |
required |
wave-sample |
value |
string |
required |
wave-changes-page |
signal |
string |
required |
wave-changes-page |
start |
int64 |
required |
wave-changes-page |
end |
int64 |
required |
wave-changes-page |
cursor |
string |
required |
wave-changes-page |
next_cursor |
string |
required |
wave-changes-page |
width |
uint32 |
required |
wave-changes-page |
has_more |
bool |
required |
wave-changes-page |
changes |
wave-change[] |
required |
clocked-samples-page |
clock |
string |
required |
clocked-samples-page |
signals |
string[] |
required |
clocked-samples-page |
start |
int64 |
required |
clocked-samples-page |
end |
int64 |
required |
clocked-samples-page |
edge |
clock-edge |
required |
clocked-samples-page |
cursor |
string |
required |
clocked-samples-page |
next_cursor |
string |
required |
clocked-samples-page |
has_more |
bool |
required |
clocked-samples-page |
samples |
clocked-sample[] |
required |
clocked-sample |
time |
int64 |
required |
clocked-sample |
values |
wave-sample[] |
required |
wave-change |
time |
int64 |
required |
wave-change |
kind |
wave-kind |
required |
wave-change |
value |
string |
required |
wave-scope-page |
scope |
string |
required |
wave-scope-page |
offset |
nonnegative-int64 |
required |
wave-scope-page |
next_offset |
nonnegative-int64 |
required |
wave-scope-page |
known_count |
nonnegative-int64 |
required |
wave-scope-page |
count_exact |
bool |
required |
wave-scope-page |
has_more |
bool |
required |
wave-scope-page |
children |
wave-scope-child[] |
required |
wave-scope-child |
name |
string |
required |
wave-scope-child |
path |
string |
required |
wave-scope-child |
subscope_count |
nonnegative-int64 |
required |
wave-scope-child |
subscope_count_exact |
bool |
required |
ui.surface.create.params |
surface |
id |
required |
ui.surface.create.params |
mode |
surface-mode |
optional |
ui.surface.create.params |
x |
int |
required |
ui.surface.create.params |
y |
int |
required |
ui.surface.create.params |
width |
positive-int |
required |
ui.surface.create.params |
height |
positive-int |
required |
ui.surface.create.params |
z |
int |
required |
ui.surface.create.params |
opaque |
bool |
optional |
ui.surface.create.params |
focusable |
bool |
optional |
ui.surface.create.result |
generation |
uint64 |
required |
ui.surface.update.params |
surface |
id |
required |
ui.surface.update.params |
clear |
bool |
optional |
ui.surface.update.params |
expected_generation |
nonnegative-int64 |
optional |
ui.surface.update.params |
ops |
draw-op[] |
required |
ui.surface.update.result |
generation |
uint64 |
required |
ui.surface.destroy.params |
surface |
id |
required |
ui.focus.request.params |
surface |
id |
required |
text-op |
op |
literal:text |
required |
text-op |
x |
int |
required |
text-op |
y |
int |
required |
text-op |
text |
string |
required |
text-op |
fg |
color |
optional |
text-op |
bg |
color |
optional |
fill-op |
op |
literal:fill |
required |
fill-op |
x |
int |
required |
fill-op |
y |
int |
required |
fill-op |
width |
nonnegative-int |
required |
fill-op |
height |
nonnegative-int |
required |
fill-op |
fg |
color |
optional |
fill-op |
bg |
color |
optional |
ui.layout.replace.params |
root |
layout-node |
required |
layout-leaf |
id |
id |
required |
layout-leaf |
kind |
literal:leaf |
optional |
layout-leaf |
view |
view |
required |
layout-leaf |
cells |
positive-int |
optional |
layout-leaf |
weight |
positive-int |
optional |
layout-leaf |
min_width |
positive-int |
optional |
layout-leaf |
min_height |
positive-int |
optional |
layout-container |
id |
id |
required |
layout-container |
kind |
container-kind |
required |
layout-container |
children |
layout-node[] |
required |
layout-container |
cells |
positive-int |
optional |
layout-container |
weight |
positive-int |
optional |
layout-container |
min_width |
positive-int |
optional |
layout-container |
min_height |
positive-int |
optional |
layout-container |
gap |
nonnegative-int |
conditional |
state.changed.params |
revision |
uint64 |
required |
state.changed.params |
changed |
state-group[] |
required |
state.changed.params |
state |
state |
required |
ui.resize.params |
width |
positive-int |
required |
ui.resize.params |
height |
positive-int |
required |
ui.focus.changed.params |
focused |
bool |
required |
ui.focus.changed.params |
surface |
id |
conditional |
ui.layout.params |
revision |
uint64 |
required |
ui.layout.params |
surface |
id |
required |
ui.layout.params |
placed |
bool |
required |
ui.layout.params |
node |
id |
conditional |
ui.layout.params |
generation |
uint64 |
required |
ui.layout.params |
x |
int |
required |
ui.layout.params |
y |
int |
required |
ui.layout.params |
width |
positive-int |
required |
ui.layout.params |
height |
positive-int |
required |
ui.input.params |
surface |
id |
required |
ui.input.params |
key |
input-key |
required |
ui.input.params |
modifiers |
modifier[] |
required |
ui.input.params |
character |
string |
conditional |
ui.input.params |
mouse_kind |
mouse-kind |
conditional |
ui.input.params |
button |
mouse-button |
conditional |
ui.input.params |
x |
int |
conditional |
ui.input.params |
y |
int |
conditional |
Creating a fullscreen surface additionally requires ui.fullscreen; creating a focusable surface additionally requires ui.focus.
The first request must be session.hello with protocol and plugin matching the manifest. The result returns the selected protocol, plugin id, granted capabilities, immutable host limits, supported methods, supported notifications, and a values object containing the supported values for each registered category. Plugins use these lists to probe additive v1 features instead of parsing an application version. session.ping has empty params. session.shutdown atomically releases the caller's surfaces, layout and focus.
The limits object is the authoritative bound for that session. Plugins read these values rather than embed current numbers. A compatible v1 host may raise a limit, but must not silently accept a request and truncate it.
| Limit | Meaning |
|---|---|
max_message_bytes |
Maximum UTF-8 bytes in one inbound protocol frame. |
max_string_bytes |
Maximum bytes in a bounded identifier/path/command string. |
max_text_bytes |
Maximum bytes in one drawing text, command diagnostic, or waveform value. |
max_surface_ops |
Maximum drawing operations in one atomic update. |
max_surfaces |
Maximum live surfaces in the host registry. |
max_surface_cells |
Maximum aggregate cell capacity of all live surfaces. |
max_surface_update_cells |
Maximum clipped cells touched by one update. |
max_surface_dimension |
Maximum surface dimension and absolute coordinate bound. |
max_surface_z |
Maximum absolute surface z value. |
max_layout_nodes |
Maximum nodes in one retained layout. |
max_layout_depth |
Maximum root-relative layout depth. |
max_layout_children |
Maximum children of one layout container. |
max_layout_weight |
Maximum flex weight. |
max_layout_gap |
Maximum inter-child gap in cells. |
max_id_bytes |
Maximum bytes in a request or resource id. |
max_subscriptions |
Maximum state groups in one subscription request. |
max_messages_per_second |
Maximum inbound frames per plugin rate window. |
max_pending_core_requests |
Maximum unfinished core requests accepted from one plugin. |
max_scope_children_page |
Maximum direct child scopes returned by one hierarchy request. |
max_wave_changes_page |
Maximum resolved change points returned by one range request. |
max_clocked_sample_signals |
Maximum digital signals sampled together at one clock edge. |
max_clocked_samples_page |
Maximum clock edges returned by one clocked sample request. |
max_report_lines |
Maximum rows in one immutable native report snapshot. |
max_report_bytes |
Maximum decoded UTF-8 bytes across one native report's rows. |
handshake_timeout_ms |
Time allowed for the initial session.hello request. |
recovery_escape_count |
Consecutive Escape keys reserved for emergency plugin recovery. |
recovery_sequence_ms |
Maximum interval between emergency recovery Escape keys. |
Core and waveform methods
core.command.invoke(core.command.invokecapability): params contain onecore.signal.reveal(core.navigation): resolves one exact, format-neutralcore.wave.reveal(core.navigation): performs the same exact signal-rowcore.time.reveal(core.navigation): moves and focuses the native wavecore.state.get(core.state.read): returnsstatewithfile,format,wave.scope.children(wave.read): returns one bounded page of direct childwave.value.get(wave.read): params contain a stable signal path and signedwave.changes.range(wave.read): returns a bounded page of resolved valuewave.samples.clocked(wave.read): samples the final values of up to
non-empty command string bounded by max_string_bytes, without a leading-shell interpretation. One optional leading : is ignored. Only commands marked public by the installed host command registry, including public focused-panel direct forms, may execute; internal development commands are rejected. This is the normal command path, so commands can mutate the session, open data, write command-defined files, or request host exit with the same behavior as interactive use. A completed invocation always returns result fields ok and message; a normal command diagnostic has ok:false and is not confused with a JSON-RPC transport/capability error. Public command syntax remains discoverable from the installed host; the method does not turn every command grammar into an independently frozen plugin method.
signal path, selects and scrolls its row into view in the native signal panel, flashes the row, releases plugin-surface focus, and transfers keyboard focus to that native panel. add:false requires the signal to be present; add:true may append it explicitly. A hiding signal filter is cleared with an undo snapshot, and the result reports row, added, and filter_cleared.
reveal and atomically moves the native waveform cursor to the in-range native-tick time, keeps that time visible, flashes the signal/time target, and transfers focus to the native wave panel. Signal resolution and time validation finish before any visible state is changed; a failed request cannot leave a partially added signal or moved cursor.
panel at one in-range native tick when a plugin record has no meaningful signal anchor. If a native signal row is selected, its time intersection is flashed; no signal-list mutation is performed.
timescale, minimum_time, maximum_time, cursor, view_start, view_end, focus, selected_signal, wave_revision, wave_loading, and integer wave_progress in 0..100.
scopes. An empty scope selects the waveform root; otherwise scope is a previously discovered stable scope path. offset is a nonnegative child index and limit is 1..max_scope_children_page. The result echoes scope and offset, returns next_offset, and never includes signals or descendants. known_count and each subscope_count describe the hierarchy frontier currently known to the lazy reader; the matching *_exact flag is false until that count is complete. has_more means the caller may request next_offset; one request admits at most one bounded backend schema page, even when that page reveals no new direct child scope.
64-bit native-tick time. The sample result contains signal, time, width, kind, and value. This is a bounded, format-neutral point lookup; it does not enumerate the signal catalog or return a transition collection. Completion latency is deliberately not a wire guarantee: a backend with an indexed point operation may answer directly, while a generic compatible backend may scan that signal. Plugins continue serving notifications and correlate the eventual response by id.
changes for one stable signal path in the inclusive native-tick interval start..end. limit is 1..max_wave_changes_page. The first request uses an empty cursor; while has_more is true, pass the returned opaque next_cursor with the same signal and interval. Times are strictly increasing across pages. Multiple delta writes at one timestamp are intentionally collapsed to the final visible value, matching the normal waveform display; delta-cycle analysis remains a separate data contract. The host touches only the named signal and at most one bounded page, so the request cost is independent of total hierarchy cardinality. v1 still has no unbounded signal, recursive hierarchy, transition, or time-range dump.
max_clocked_sample_signals exact digital signal paths at rising or falling edges of one exact one-bit clock. The interval is inclusive, each page is bounded by max_clocked_samples_page, and the opaque cursor excludes the last returned edge. Host-side edge alignment gives Tcl checkers one common same-timestamp policy without materializing every role's transition stream.
Core commands and waveform reads may be deferred and may complete in a different order from requests; clients must not infer host scheduling from observed response timing. A slow reader request does not stop protocol I/O or the wait animation. The number of unfinished core requests is bounded by the negotiated limit, and duplicate pending ids are rejected. Once accepted, a core operation has at-most-once semantics and may finish even if its caller disconnects; unloading a UI is not a rollback mechanism for an already started command.
Native report publication
ui.report.publish requires ui.report. Params contain a non-empty bounded title and a non-empty lines string array. Each line is independently bounded by max_text_bytes; the complete snapshot is bounded by max_report_lines and max_report_bytes. The host validates the complete request before atomically replacing and opening the native report overlay, and returns the accepted rows count. Tab-separated lines use the native table renderer; all text still passes through the terminal-control filter.
The report is an immutable copy owned by the host after publication. Plugin unload does not close or invalidate it, and a later native command or plugin may replace it. Publication never executes report text and does not register a command, key, completion, or help entry. Dynamic global : command registration is intentionally absent from v1: its namespace, collision, help/completion, tutorial, unload and in-flight invocation lifecycle remain a separate future contract. Manifest headless.commands are bounded presets under the host-owned :plugins exec namespace. Plugins may also expose local surface actions and compose existing public commands through core.command.invoke.
Surfaces
All surface ids are local to the caller and are never returned with an internal owner prefix.
ui.surface.create requires ui.draw and these params: surface, x, y, width, height, and z. Optional mode is overlay, modal, or fullscreen; fullscreen additionally requires ui.fullscreen. Docking is a layout operation, not a fourth surface mode. Optional opaque and focusable default false; a focusable surface requires ui.focus. The result generation is zero, meaning not yet published. Surfaces with different z values compose and hit-test in ascending z order; ordering between equal-z surfaces is deliberately unspecified, so a plugin assigns distinct z values whenever overlap matters. An overlay receives mouse input only inside its committed rectangle. A focused modal or fullscreen surface also receives mouse input outside that rectangle; its local coordinates can therefore be negative or beyond its dimensions. An opaque fullscreen surface that covers the viewport suppresses core-frame rendering until it is removed or resized away from full coverage.
The live registry's aggregate surface capacity is bounded by max_surface_cells; allocation can therefore return quota when another surface already consumes the budget. ui.surface.update requires ui.draw, surface, and bounded ops. Optional clear defaults false. Optional expected_generation rejects stale drawing. The complete request is validated against a copy, then atomically published as one new generation. ops contains at most max_surface_ops operations and a text value contains at most max_text_bytes UTF-8 bytes. Supported operations are:
{"op":"text","x":2,"y":1,"text":"hello","fg":"palette:12","bg":"default"}
{"op":"fill","x":0,"y":0,"width":20,"height":3,"fg":"#C8D3F5","bg":"#222436"}
Colors are exactly default, palette:N where N is 0..255, or #RRGGBB. Coordinates are terminal cells. Text is UTF-8, host-width measured and clipped. Tabs advance to the next eight-cell stop; other terminal control codepoints are neutralized at the Frame boundary and can never become raw terminal control sequences. fill replaces the affected glyphs with spaces while applying its colors. An invalid operation leaves the current generation unchanged. The sum of clipped cells touched by all operations is bounded by max_surface_update_cells, preventing overlapping large fills from turning a small request into unbounded raster work.
ui.surface.destroy requires ui.draw and a local surface id. It also releases focus if that surface held the focus lease. A surface referenced by the active layout cannot be destroyed; the plugin restores or replaces that layout first, so no accepted request leaves a dangling visible leaf.
Layout and focus
ui.layout.replace requires ui.layout and one root. The complete tree is validated and installed atomically. Node kind is leaf (default), row, column, or stack. Every node needs a unique local id. Leaves use either builtin.frame or surface:<local-id> as view. Containers use children. Optional constraints are cells, weight, min_width, and min_height; row and column containers additionally accept gap; stack containers reject it because their children overlap. When present, cells, min_width, and min_height are positive integers—there is no public negative sentinel. A leaf requires exactly one view and no children; a container requires non-empty children and no view. A surface and builtin.frame may each appear at most once because each live view has one resolved rectangle. No other builtin.* name exists in v1, and plugins must not guess one. Native chrome, signal and wave leaves can be added later only as discoverable registered values after they have independent renderer, overlay and input geometry semantics. Plugins use negotiated limits rather than relying on current numeric values. Each plugin owns only its submitted layout contribution. A second plugin does not replace or reject the first contribution: the host keeps the native frame once, extracts each plugin's surface subtree, and adaptively packs independent plugin windows into bounded rows and columns. A single contribution retains its exact v1 tree, including an intentional fullscreen layout. With multiple contributors the core owns the aggregate tree and reflows panels according to their declared minimums; no plugin can position or resize another plugin's surface. ui.layout.restore removes only the caller's contribution. A surface embedded by a layout temporarily uses the resolved leaf rectangle; when it is no longer placed, it returns to the rectangle declared by ui.surface.create. Either resize invalidates the prior generation and requires redraw, so stale dock content cannot remain over the restored core UI.
Rows allocate the horizontal axis and columns the vertical axis. A child with cells gets that fixed primary-axis size subject to its minimum; other children receive their minimum plus the remaining cells in weight proportion and declaration-order remainder. gap is inserted only between placed children. Stack children share one rectangle; the core frame is composed first and plugin surfaces retain their explicit z ordering. Omitting builtin.frame suppresses native-frame rendering. If a later terminal resize cannot satisfy minimums, the host keeps the last valid placement, reports the resize, and lets the plugin submit a replacement layout.
The separators produced by a row or column are host-owned controls. Dragging a separator with the left mouse button resizes its adjacent native/plugin or plugin/plugin windows while honoring both minimums. While a plugin surface is focused, Alt+Left/Right resizes the nearest horizontal split by two cells and Alt+Up/Down resizes the nearest vertical split by one row. These successful resize gestures are reserved by the host and are not delivered as ui.input; the resulting ui.layout notifications are the plugin's redraw contract.
ui.focus.request requires ui.focus and an owned, committed, focusable surface. A mouse press on a committed focusable surface also acquires its focus lease. A programmatic request may move focus between the caller's surfaces but returns conflict instead of stealing another plugin's lease; an explicit user mouse press may transfer focus. ui.focus.release is idempotent when no plugin is focused but cannot release another plugin's lease. The negotiated number of consecutive Escape keys, each no more than recovery_sequence_ms after the previous one, is reserved for host recovery. The earlier Escape keys are delivered normally; the final one is consumed, then the host terminates that plugin and atomically removes its surfaces, layout and focus lease. Any non-Escape input or an expired interval resets the sequence.
Subscriptions and notifications
event.subscribe and event.unsubscribe require core.state.read. Their non-empty events array accepts cursor, view, selection, focus, and wave. The first host pass after a new subscription sends a snapshot even if the value did not just change. Later unchanged passes are coalesced.
Host notifications are JSON-RPC requests without id:
state.changed: monotonicrevision, subscribedchangedgroups, and theui.resize: terminalwidthandheight.ui.focus.changed:focusedand, when true, the focused localsurface.ui.layout: one notification for each surface owned by the plugin wheneverui.input: localsurface, normalizedkey,modifiers, optional
typed state snapshot.
It is sent initially and whenever that plugin gains, moves, or loses its focus lease, including an outside mouse click.
the layout revision changes. It carries local surface, revision, current generation, placed, and effective x, y, width, height. node is present only when placed is true. A placed:false notification after restore/removal explicitly reports the return to the surface's declared rectangle; generation zero requires redraw.
character, and mouse fields when applicable. modifiers is an array, not an internal bit mask. Mouse x and y are zero-based surface-local cells; button is a name and is present only for press/release/drag.
Resize, resolved-layout and focused-input notifications are resource lifecycle events and do not require state subscription. The focus field in a core state snapshot is the last native signal/wave panel focus; plugin focus lease changes are reported only by ui.focus.changed.
The following wire values are the v1 baseline. Hosts may add values where the surrounding result/notification is documented as extensible; plugins handle an unknown value without treating it as an existing one.
| Category | Value |
|---|---|
state_group |
cursor |
state_group |
view |
state_group |
selection |
state_group |
focus |
state_group |
wave |
surface_mode |
overlay |
surface_mode |
modal |
surface_mode |
fullscreen |
layout_kind |
leaf |
layout_kind |
row |
layout_kind |
column |
layout_kind |
stack |
draw_op |
text |
draw_op |
fill |
builtin_view |
builtin.frame |
clock_edge |
rising |
clock_edge |
falling |
input_key |
char |
input_key |
up |
input_key |
down |
input_key |
left |
input_key |
right |
input_key |
home |
input_key |
end |
input_key |
page-up |
input_key |
page-down |
input_key |
delete |
input_key |
f2 |
input_key |
mouse |
input_key |
enter |
input_key |
backspace |
input_key |
escape |
input_key |
unknown |
input_modifier |
shift |
input_modifier |
alt |
input_modifier |
control |
mouse_kind |
press |
mouse_kind |
release |
mouse_kind |
drag |
mouse_kind |
wheel-up |
mouse_kind |
wheel-down |
mouse_kind |
move |
mouse_button |
left |
mouse_button |
middle |
mouse_button |
right |
focus_value |
signal |
focus_value |
wave |
wave_kind |
bits |
wave_kind |
real |
wave_kind |
string |
Tcl SDK v1 procedures
The following dependency-free Tcl procedures are the stable v1 SDK baseline. New helpers may be added compatibly; existing names, positional arguments and defaults are not removed or repurposed. Names containing an underscore-prefixed namespace component are implementation details and are not SDK API.
| Procedure | Arguments |
|---|---|
::svw::quote |
value |
::svw::request |
method params ?callback? |
::svw::has_capability |
name |
::svw::limit |
name |
::svw::supports_method |
name |
::svw::supports_notification |
name |
::svw::supports_value |
category value |
::svw::ping |
?callback? |
::svw::surface_create |
id x y width height ?option value ...? |
::svw::surface_clear |
id |
::svw::surface_text |
id x y text ?option value ...? |
::svw::surface_fill |
id x y width height ?option value ...? |
::svw::surface_commit |
id ?callback? |
::svw::surface_destroy |
id ?callback? |
::svw::focus |
id ?callback? |
::svw::focus_release |
?callback? |
::svw::layout_replace |
root-json ?callback? |
::svw::layout_restore |
?callback? |
::svw::layout::leaf |
id view ?option value ...? |
::svw::layout::row |
id children ?option value ...? |
::svw::layout::column |
id children ?option value ...? |
::svw::layout::stack |
id children ?option value ...? |
::svw::ui::label |
surface x y text ?option value ...? |
::svw::ui::box |
surface x y width height ?option value ...? |
::svw::ui::progress |
surface x y width fraction ?option value ...? |
::svw::ui::list |
surface x y values ?option value ...? |
::svw::ui::button |
surface x y label ?option value ...? |
::svw::ui::input |
surface x y width value ?option value ...? |
::svw::ui::tabs |
surface x y labels selected ?option value ...? |
::svw::ui::table |
surface x y headers rows ?option value ...? |
::svw::ui::tree |
surface x y nodes ?option value ...? |
::svw::command |
command ?callback? |
::svw::reveal_signal |
signal ?add? ?callback? |
::svw::reveal_time |
time ?callback? |
::svw::reveal_wave |
signal time ?add? ?callback? |
::svw::report_publish |
title lines ?callback? |
::svw::state_get |
?callback? |
::svw::subscribe |
events ?callback? |
::svw::unsubscribe |
events ?callback? |
::svw::wave_value |
signal time ?callback? |
::svw::wave_changes |
signal start end cursor limit ?callback? |
::svw::wave_samples_clocked |
clock signals start end edge cursor limit ?callback? |
::svw::wave_scope_children |
scope offset limit ?callback? |
::svw::shutdown |
- |
::svw::on |
event callback |
::svw::bind |
key callback |
::svw::unbind |
key |
::svw::timer |
milliseconds callback |
::svw::timer_cancel |
token |
::svw::json_decode |
document |
::svw::json_get |
document path |
::svw::json_type |
value |
::svw::json_payload |
value |
::svw::string_field |
line path |
::svw::number_field |
line path |
::svw::bool_field |
line path |
::svw::string_array_field |
line path |
::svw::run |
- |
Tcl UI 插件
只有用户显式传入 --plugin <manifest> 时,svw 才会加载 Tcl UI 插件。 波形文件和会话文件永远不会触发可执行插件代码。
每个插件都在用户提供的独立 tclsh 进程中运行。如果解释器在 PATH 中的名称 不是 tclsh,请设置 SVW_TCLSH。svw 与子进程使用精确协议名 svw.ui.plugin.v1,通过有界、按换行分帧的 JSON-RPC 2.0 通信;svw 不链接、 也不分发 Tcl 运行时。任何破坏兼容性的线协议变更都会使用新的精确协议名。 v1 发布后,已有字段和语义永远不会被挪作他用。
完整的方法、通知、类型、配额和兼容性约定见 Tcl UI 插件协议 v1 参考。manifest 中的 capabilities 是强制要求;optional_capabilities 中受当前 host 支持的项目会被 授予。hello 响应是已授予能力、可发现方法、通知、限制和枚举值的唯一权威来源。 Tcl 插件应在可选操作前调用 ::svw::has_capability;使用 ::svw::supports_method、::svw::supports_notification 和 ::svw::supports_value 探测 v1 中可兼容追加的 host 能力。
插件可以提交 retained surface 和原子布局贡献。重复传入 --plugin 即可在同一会话 运行多个独立 panel,例如 svw wave.kbx --plugin smart/plugin.json --plugin temporal/plugin.json。svw 只保留一份原生 frame,自适应排布所有贡献窗口,并持有 窗口间的鼠标分隔线和 Alt+方向键缩放。svw 始终是终端的唯一所有者: 插件不能向 TUI 写 ANSI、不能得到 tty 描述符,也不能访问 C++ 对象。不透明的 全屏 surface 会替代核心 frame,并在移除前避免无意义的波形渲染。即使插件停止 响应,连续按三次 Escape 也会终止当前聚焦插件,并原子恢复原生布局。
:plugins 显示显式加载插件的版本、状态和 manifest 动作;:plugins focus <id> 聚焦其 首个已就绪 surface,:plugins unload <id> 停止插件。这些 host-only 操作不能通过插件的 core.command.invoke 边界。
manifest 还可声明独立的 headless.entry 和有界动作。Agent、MCP、JSON-RPC 与 :plugins run 只是同一采样/执行服务和结果 schema 的适配器,不分别复制协议逻辑,也不提供 通用命令桥。clocked-samples.v1 提供有界的时钟沿对齐值,可显式选择 native 或 pre-edge,并分别返回事件时间与采样时间, event-samples.v1 提供带 guard 的 named/wildcard/edge union, extraction-plan.v1 合并独立的命名 source 与类型化 payload, property-captures.v1 提供类型感知的 match/assert/deassert/switch 证据, signal-changes.v1 提供有界且同一时间只保留终值的 transition stream, bounded-arguments.v1 只把调用方受消息大小限制的 JSON object 转交给显式选择的 受信任插件。协议检查器、X/Z 定位和回归分诊示例分别覆盖这三种适配器。
headless.commands 可声明 namespaced 短命令预设,但不会注册全局 : 命令。插件已加载 时可直接执行 :plugins exec ready-valid top.dut_if,默认检查可见范围;显式形式是 :plugins exec path/plugin.json ready-valid top.dut_if [start end]。
无外部依赖的 v1 SDK 和示例来自同一份已审阅源码,并以固定静态站点下载物发布。 请把 .txt 响应保存为下面展示的运行时文件名。GitHub 二进制 Release 归档不会 包含这些文件:
SDK、 示例入口、 manifest 和 SHA-256 清单。
curl -fLO https://svw.run/assets/svw-plugin-sdk-v1.txt
curl -fLO https://svw.run/assets/svw-plugin-example-main-v1.txt
curl -fLO https://svw.run/assets/svw-plugin-example-manifest-v1.txt
curl -fLO https://svw.run/assets/svw-plugin-downloads-v1.sha256.txt
# Linux:
sha256sum -c svw-plugin-downloads-v1.sha256.txt
# macOS 使用:shasum -a 256 -c svw-plugin-downloads-v1.sha256.txt
mv svw-plugin-sdk-v1.txt svw_plugin.tcl
mv svw-plugin-example-main-v1.txt main.tcl
mv svw-plugin-example-manifest-v1.txt plugin.json
svw waveform.kbx --plugin plugin.json
受 Git 跟踪的开发副本位于 examples/tcl-ui-plugin。静态发布器只复制它们的 精确字节并生成校验和清单,绝不会导出 svw 产品源码。
ui.draw、ui.focus、ui.fullscreen、ui.layout 和 ui.report 是相互独立的 manifest 能力。获得 core.command.invoke 的插件可以通过 ::svw::command 调用现有公开 svw 命令;内部开发命令会在命令注册表边界被拒绝。host 通过正常命令路径执行 已接受的命令,并把 ok 和正常消息或诊断交给可选 callback。这个能力有意保持 强大:公开命令具有与交互使用相同的修改、文件输出和退出行为,它不是只读自动化 通道。能力控制的是 svw API 权限,而不是操作系统沙箱。Tcl 插件属于受信任的本地 代码,Tcl 自带的 exec 具有用户的操作系统权限;因此 v1 不会声称存在误导性的 process.exec 沙箱。
core.state.read 提供有界的 core.state.get 快照,wave.read 提供 lazy 的点 查询 wave.value.get、有界的已解析变化查询 wave.changes.range,以及只返回直接子 scope 的分页查询 wave.scope.children。Tcl helper 接受可选 callback,callback 会收到原始 JSON 结果行。无依赖 SDK 包含有界的结构化 JSON 解码器;字段 helper 使用显式路径, 例如 string_field $line {result message} 和 string_field $line {result sample value}。它们不会在不相关的嵌套对象中搜索同名 key,因此响应新增字段不会悄悄改变 callback 读取的值。如果 callback 需要 typed helper 尚未覆盖的值,可使用 json_decode、json_get、json_type 和 json_payload。首版协议有意不提供无界的“全部变化”或递归层级请求:自定义 view 只能使用有界数据 API,超大波形不能悄悄破坏 svw 的 lazy reader。
ui.report 允许插件通过 ::svw::report_publish 把一个有界不可变快照发布到原生 report overlay。host 会先完整校验并复制 title/line,再打开报告。插件卸载后副本 仍然有效,后续原生或插件报告可以替换它;报告文本永远不会被执行。
core.navigation 是插件与原生窗口之间的集成边界。带信号锚点的插件行调用 ::svw::reveal_signal,同时带信号和原生 tick 的行调用 ::svw::reveal_wave, 只有时间锚点的记录调用 ::svw::reveal_time。它们不是命令字符串的别名:host 先完整解析并校验目标,再选择并滚动原生 signal 行、移动原生 wave cursor、闪烁 信号与时间交点,并把键盘焦点从插件 surface 交还原生 S 或 W 窗口。是否把 尚未显示的信号加入 signal panel 必须由插件显式传入布尔值。反方向上,插件订阅 selection、cursor、view 和 wave 状态组,原生窗口变化会刷新插件的有界视图。
仓库中有十六个用于验证扩展边界的示例:
examples/tcl-scope-browser在原生 frame 旁增加可聚焦的层级窗口。Enter 只展开examples/tcl-python-semantic把 Python 保持在 svw 进程之外,同时只保留一个语义examples/tcl-waveform-notebook把当前 cursor、所选信号和简短笔记保存到有界且examples/tcl-reset-clock-health对可见区间内所选的一位信号做健康检查。有界变化examples/tcl-x-detective查找所选信号的 X、Z 与 mixed-unknown 区间,显示持续examples/tcl-protocol-decoder复用一个原生有界时钟沿采样器,以及 Tclexamples/tcl-failure-triage读取有界且不可执行的回归失败报告,显示失败详情,examples/tcl-uvm-timeline从与波形原生时间轴对齐的有界 simulator export 中筛选examples/tcl-coverage-holes浏览有界 coverage export,并复用公开examples/tcl-temporal-query在一个所选信号和可见区间上执行有限的examples/tcl-smart-watch在 cursor 处顺序采样最多 64 个显式信号路径,不遍历examples/tcl-memory-inspector只 lazy 采样显式地址到信号 memory map 的当前examples/tcl-fsm-visualizer用 value-to-state map 解码有界状态迁移与有向边计数。examples/tcl-simulator-control只在用户显式 connect 后持有能力协商的外部examples/tcl-debug-recipes预览不可执行的命令 recipe,并要求第二次显式操作后才examples/tcl-workflow-console展示插件局部 action、公开命令组合和原生报告原子
当前 scope 并请求一个有界页面;收起的行显示当前已知的直接子 scope 数。
权威来源。受信任的 Tcl 进程启动有边界的 Python 编排器、显示等待转圈,并绘制语义 driver/load 结果。Python 让已配置的设计编译器生成规范语义包,只通过公开的 svw agent JSON 接口消费结果,不嵌入另一套 parser 或 AST 投影。相关工具不在 PATH 时,可在插件目录的单行 python.txt、viewer.txt 和 design-compiler.txt 中指定绝对路径。
不可执行的 sidecar。插件绝不会 source sidecar 内容;选中笔记后,它会在原生 signal 与 wave 窗口中原子显露该信号和时间。
页用于计算 clock 周期、jitter、duty 和 reset 脉宽;固定 transition 预算会明确 标记 partial 结果,不会默默抽干任意高密度信号。
时间,并在原生窗口中显露所选信号与时间目标。
规则、跨页状态和统一结果组件。可切换 profile 覆盖 ready/valid、 AXI4/AXI4-Lite 与 ACE channel、AXI4-Stream、APB、AHB-Lite、Avalon-ST/MM、 Wishbone B4、TileLink channel、request/acknowledge,以及通用 FIFO 或带 ID 的 request/response 关联。关联结果包含 phase、latency、outstanding 深度、 reset 终止、未配对端点、多 beat 进度和裁剪窗口完整性。AXI/ACE 与 TileLink profile 明确只做 channel 级检查,不冒充完整 ordering、burst、cache 或 coherence compliance checker。
并把每个失败时间关联到原生波形;日志本身不会激活插件代码。可选 baseline 报告 会让同一窗口显示 new/common/fixed 回归差异。
phase、objection、sequence、configuration、resource 和 register 活动。
coverage source 命令打开所选文件和行。
rise/fall/unknown/equals 查询语言。
波形 schema。
可见页。
simulator adapter,并限制每个请求。
顺序调用公开命令。
发布,不注册动态全局命令。
Python 示例展示的是集成模式,不会给 svw 增加新的运行时依赖或第二套 design 数据 契约。生产插件应复用生成 design 数据时相同的 elaborated 源文件与选项;示例中的 sources.f 和 demo.sv 只用于可重复演示。
Tcl SDK 把协议消息写到 stdout,因此插件日志必须写 stderr。ui.resize、 ui.layout、ui.input 和已订阅的 state.changed 通知会以 dictionary 传给事件 handler。SDK 绘制 helper 会缓存操作,直到 surface_commit;随后一个有界的 ui.surface.update 请求会校验并原子发布完整 generation。RPC helper 返回请求 id。Core、波形和 surface commit helper 接受可选 callback;surface create 通过 -callback 接收,其余资源、布局、焦点和订阅 helper 则把 callback 作为最后一个 可选参数。SDK option list 会拒绝未知名称,不会静默接受拼写错误。同一插件的 host-owned UI 资源效果按接收顺序提交;相互依赖的 Core 操作必须从前一个操作的 callback 中发起。
协议颜色只能是 default、palette:N 或 #RRGGBB,绝不暴露 svw 内部整数编码。 surface 与布局 id 在线协议中始终是插件局部 id。ui.layout dictionary 包含 placed、当前 generation 和生效矩形;只有 surface 位于活动布局中时,node 才非空。因此 placed:false 能让插件在布局恢复后按 create 时声明的矩形重绘, 无需根据“没有事件”进行猜测。panel 必须按该生效矩形重绘,不能假设自己请求的 cells 始终精确不变;其他插件、终端 resize 或用户拖动分隔线都可能合法改变宽度和 高度。hello 响应后,::svw::limit <name> 返回本会话协商 得到的限制,插件无需内嵌 host 常量。
Tcl UI 插件协议 v1 参考
本文是 svw.ui.plugin.v1 的兼容性参考。只有用户显式执行 svw --plugin <manifest> 时才会使用该协议;波形文件和会话文件永远不会激活 插件。
兼容性
每个 frame 是一个 UTF-8 JSON-RPC 2.0 对象,末尾跟一个换行符,最大 1 MiB。 v1 不接受空行、顶层 JSON-RPC batch 或插件 notification。请求示例:
{"jsonrpc":"2.0","id":1,"method":"session.ping","params":{}}
响应携带相同 id,并且只能包含 result 或 error 之一。解析失败或无效请求 导致 host 无法恢复有效 id 时,错误响应使用 id:null。id 只能是长度不超过 max_id_bytes 的 UTF-8 字符串或有符号 64 位整数;浮点数和 null 都会被拒绝。 响应尚未返回时,插件不得复用 id。插件必须忽略未知的 result member 和未知的 host notification。请求 schema 是严格封闭的:未知 envelope 或 params member 会被拒绝,拼写错误不能静默改变行为。已发布的 v1 名称、字段类型和语义不会删除 或挪用;破坏兼容性的协议必须使用新的精确名称。
兼容性有意采用不对称规则。请求 envelope、params 以及嵌套在 params 内的对象 都是封闭且 schema-strict 的,因此给既有 v1 方法增加请求 member 不属于兼容扩展。 result 对象及其嵌套对象、错误 data、manifest 根 member、host notification 的 params 及其嵌套对象都是开放的,接收方必须忽略未知 member。可以新增方法、通知、 能力和注册值;插件使用 session.hello 发现后才能使用。新值永远不能获得已有值的 语义。
响应可以延迟,也可以不按请求顺序到达;ui.resize 等 host notification 可以出现 在请求与响应之间。client 只能通过 id 关联响应,并且在 Core 或波形请求等待期间 必须继续分发 host notification。
同一插件拥有的 UI 资源、布局、焦点和订阅效果按 frame 接收顺序提交。因此后一个 此类请求能观察到前一个成功请求的效果,使 create–draw–focus 与 restore–destroy 序列保持确定性。这个保证不会把 deferred Core 或波形操作与后续请求排序;当一个 Core 结果是另一个请求的输入时,插件必须从第一个请求的 callback 发起后续请求。
错误包含数值 code、稳定的 name、便于阅读的 message,并可新增对象类型的 data。插件只能按 name 分支,不能解析 message。
| 错误名 | 代码 |
|---|---|
parse_error |
-32700 |
invalid_request |
-32600 |
method_not_found |
-32601 |
invalid_params |
-32602 |
internal_error |
-32603 |
capability_denied |
-32001 |
conflict |
-32002 |
not_found |
-32003 |
unavailable |
-32004 |
quota |
-32005 |
Manifest 与握手
{
"id": "example-ui",
"version": "1.0.0",
"entry": "main.tcl",
"protocol": "svw.ui.plugin.v1",
"capabilities": ["ui.draw", "ui.focus"],
"optional_capabilities": ["ui.layout"]
}
entry 必须是 manifest 目录内的普通文件。capabilities 中的能力是强制要求: 必需能力未知或不可用会拒绝整个 manifest。optional_capabilities 可以包含旧 v1 host 不认识的能力;已知项会被授予,未知项会被忽略。插件在使用可选功能前检查 hello 结果中的 capabilities。两个数组内部以及跨数组的重复名称都会被拒绝。 v1 能力为 core.command.invoke、core.navigation、core.state.read、wave.read、ui.draw、 ui.focus、ui.fullscreen、ui.layout 和 ui.report。
插件与局部资源 id 必须匹配 [A-Za-z0-9][A-Za-z0-9._-]*。entry path 拒绝控制 字符和 NUL;host canonicalize 路径后再次确认其仍位于 manifest 目录内。 version 是最多 64 个 ASCII byte 的不透明插件元数据,只允许字母、数字、.、 -、+;svw 不会从中推断协议兼容性。manifest 最大 64 KiB,必需和可选能力 合计最多 32 项。
插件进程只接收清理后的环境:host 除了保留文档约定的运行时基础变量,只转发名称以 SVW_PLUGIN_CONFIG_ 开头的变量。这个前缀表示用户显式选择传入非机密插件配置, 不是安全边界;插件仍然是受信任的本地代码。其他父进程变量和 secret 不会被转发。 转发配置最多 64 个变量,总编码大小最多 64 KiB。
可选的 headless 声明独立于 UI 握手:
"headless": {
"entry": "headless.tcl",
"actions": [{"id":"check", "summary":"Bounded check",
"input":"clocked-samples.v1"}],
"commands": [{"id":"ready-valid", "summary":"Short check",
"action":"check", "profile":"ready-valid", "clock":"clk",
"roles":{"valid":"valid", "ready":"ready", "data":"data"}}]
}
该入口同样必须是 canonical 后仍位于 manifest 目录内的普通文件。动作最多 16 个且 id 唯一;id 使用插件 id 语法,summary 最多 160 bytes。只有调用方显式指定 manifest/action 时才启动 headless 进程;它接收一条有界 JSON,返回一条 svw.plugin.action-result.v1,随后由 host 终止。 action 的 input 只能是 clocked-samples.v1、event-samples.v1、 extraction-plan.v1、property-captures.v1、signal-changes.v1 或 bounded-arguments.v1。extraction adapter 把命名的独立 event source、类型化 predicate、sample mode 与 payload 合并为一个有界表。 property adapter 接受类型感知表达式、match|assert|deassert|switch、范围、同时间戳策略与 有界 limit;host 提供已筛选的 capture、truth、operand 与完整性。event adapter 接受有界的 named/wildcard/edge union、逐 term iff、native/pre-edge 与 role-to-signal tracked 映射。host 在启动 Tcl 前完成有界波形采样;最后一种原样 转交已经受消息大小限制的 JSON argument object,供显式加载且会自行校验项目产物的受信任 插件使用。 namespaced 短命令最多 32 个;它引用已声明 action,并保存 profile 与相对 clock/role 叶子。plugins exec 将叶子拼到调用方给出的精确 scope,默认使用当前可见范围,也可成对 指定 start/end。
基础能力映射属于已冻结的方法注册表:
| 方法 | 必需能力 |
|---|---|
session.hello |
- |
session.ping |
- |
session.shutdown |
- |
core.command.invoke |
core.command.invoke |
core.signal.reveal |
core.navigation |
core.time.reveal |
core.navigation |
core.wave.reveal |
core.navigation |
core.state.get |
core.state.read |
event.subscribe |
core.state.read |
event.unsubscribe |
core.state.read |
ui.focus.release |
ui.focus |
ui.focus.request |
ui.focus |
ui.layout.replace |
ui.layout |
ui.layout.restore |
ui.layout |
ui.report.publish |
ui.report |
ui.surface.create |
ui.draw |
ui.surface.destroy |
ui.draw |
ui.surface.update |
ui.draw |
wave.changes.range |
wave.read |
wave.samples.clocked |
wave.read |
wave.scope.children |
wave.read |
wave.value.get |
wave.read |
由参数选择的额外权限同样被冻结并由机器检查:
| 方法 | 条件 | 额外必需能力 |
|---|---|---|
ui.surface.create |
mode=fullscreen |
ui.fullscreen |
ui.surface.create |
focusable=true |
ui.focus |
每个方法的参数和结果对象也已经冻结。调度有意不进入协议:任何响应都可能 deferred 或重排,插件必须始终按 id 关联,并继续处理通知。
| 方法 | 参数对象 | 结果对象 |
|---|---|---|
session.hello |
session.hello.params |
session.hello.result |
session.ping |
empty |
empty |
session.shutdown |
empty |
empty |
core.command.invoke |
core.command.invoke.params |
core.command.invoke.result |
core.signal.reveal |
core.signal.reveal.params |
core.signal.reveal.result |
core.time.reveal |
core.time.reveal.params |
core.time.reveal.result |
core.wave.reveal |
core.wave.reveal.params |
core.wave.reveal.result |
core.state.get |
empty |
core.state.get.result |
event.subscribe |
event.subscription.params |
empty |
event.unsubscribe |
event.subscription.params |
empty |
ui.focus.release |
empty |
empty |
ui.focus.request |
ui.focus.request.params |
empty |
ui.layout.replace |
ui.layout.replace.params |
empty |
ui.layout.restore |
empty |
empty |
ui.report.publish |
ui.report.publish.params |
ui.report.publish.result |
ui.surface.create |
ui.surface.create.params |
ui.surface.create.result |
ui.surface.destroy |
ui.surface.destroy.params |
empty |
ui.surface.update |
ui.surface.update.params |
ui.surface.update.result |
wave.changes.range |
wave.changes.range.params |
wave.changes.range.result |
wave.samples.clocked |
wave.samples.clocked.params |
wave.samples.clocked.result |
wave.scope.children |
wave.scope.children.params |
wave.scope.children.result |
wave.value.get |
wave.value.get.params |
wave.value.get.result |
下面的表是完整 v1 对象字段约定。没有列出字段的对象使用相应方法中说明的空 params 或 result 对象。conditional 表示只在周围文字所述条件成立时必需。
| 对象 | 字段 | 类型 | 出现条件 |
|---|---|---|---|
manifest |
id |
id |
required |
manifest |
version |
version |
required |
manifest |
entry |
relative-path |
required |
manifest |
protocol |
protocol |
required |
manifest |
capabilities |
capability[] |
required |
manifest |
optional_capabilities |
capability[] |
optional |
request |
jsonrpc |
literal:2.0 |
required |
request |
id |
string-or-int64 |
required |
request |
method |
method |
required |
request |
params |
object |
required |
response |
jsonrpc |
literal:2.0 |
required |
response |
id |
string-or-int64-or-null |
required |
response |
result |
object |
conditional |
response |
error |
error |
conditional |
notification |
jsonrpc |
literal:2.0 |
required |
notification |
method |
notification |
required |
notification |
params |
object |
required |
error |
code |
int |
required |
error |
name |
error-name |
required |
error |
message |
string |
required |
error |
data |
object |
optional |
session.hello.params |
protocol |
protocol |
required |
session.hello.params |
plugin |
id |
required |
session.hello.result |
protocol |
protocol |
required |
session.hello.result |
plugin |
id |
required |
session.hello.result |
capabilities |
capability[] |
required |
session.hello.result |
limits |
limit-map |
required |
session.hello.result |
methods |
method[] |
required |
session.hello.result |
notifications |
notification[] |
required |
session.hello.result |
values |
value-map |
required |
core.command.invoke.params |
command |
string |
required |
core.command.invoke.result |
ok |
bool |
required |
core.command.invoke.result |
message |
string |
required |
core.signal.reveal.params |
signal |
string |
required |
core.signal.reveal.params |
add |
bool |
required |
core.signal.reveal.result |
signal |
string |
required |
core.signal.reveal.result |
row |
nonnegative-int64 |
required |
core.signal.reveal.result |
added |
bool |
required |
core.signal.reveal.result |
filter_cleared |
bool |
required |
core.time.reveal.params |
time |
int64 |
required |
core.time.reveal.result |
time |
int64 |
required |
core.wave.reveal.params |
signal |
string |
required |
core.wave.reveal.params |
time |
int64 |
required |
core.wave.reveal.params |
add |
bool |
required |
core.wave.reveal.result |
signal |
string |
required |
core.wave.reveal.result |
time |
int64 |
required |
core.wave.reveal.result |
row |
nonnegative-int64 |
required |
core.wave.reveal.result |
added |
bool |
required |
core.wave.reveal.result |
filter_cleared |
bool |
required |
core.state.get.result |
state |
state |
required |
event.subscription.params |
events |
state-group[] |
required |
wave.value.get.params |
signal |
string |
required |
wave.value.get.params |
time |
int64 |
required |
wave.value.get.result |
sample |
wave-sample |
required |
wave.changes.range.params |
signal |
string |
required |
wave.changes.range.params |
start |
int64 |
required |
wave.changes.range.params |
end |
int64 |
required |
wave.changes.range.params |
cursor |
string |
required |
wave.changes.range.params |
limit |
positive-int |
required |
wave.changes.range.result |
page |
wave-changes-page |
required |
wave.samples.clocked.params |
clock |
string |
required |
wave.samples.clocked.params |
signals |
string[] |
required |
wave.samples.clocked.params |
start |
int64 |
required |
wave.samples.clocked.params |
end |
int64 |
required |
wave.samples.clocked.params |
edge |
clock-edge |
required |
wave.samples.clocked.params |
cursor |
string |
required |
wave.samples.clocked.params |
limit |
positive-int |
required |
wave.samples.clocked.result |
page |
clocked-samples-page |
required |
wave.scope.children.params |
scope |
string |
required |
wave.scope.children.params |
offset |
nonnegative-int64 |
required |
wave.scope.children.params |
limit |
positive-int |
required |
wave.scope.children.result |
page |
wave-scope-page |
required |
ui.report.publish.params |
title |
string |
required |
ui.report.publish.params |
lines |
string[] |
required |
ui.report.publish.result |
rows |
nonnegative-int64 |
required |
state |
file |
string |
required |
state |
format |
string |
required |
state |
timescale |
string |
required |
state |
minimum_time |
int64 |
required |
state |
maximum_time |
int64 |
required |
state |
cursor |
int64 |
required |
state |
view_start |
int64 |
required |
state |
view_end |
int64 |
required |
state |
focus |
focus-value |
required |
state |
selected_signal |
string |
required |
state |
wave_revision |
uint64 |
required |
state |
wave_loading |
bool |
required |
state |
wave_progress |
int:0..100 |
required |
wave-sample |
signal |
string |
required |
wave-sample |
time |
int64 |
required |
wave-sample |
width |
uint32 |
required |
wave-sample |
kind |
wave-kind |
required |
wave-sample |
value |
string |
required |
wave-changes-page |
signal |
string |
required |
wave-changes-page |
start |
int64 |
required |
wave-changes-page |
end |
int64 |
required |
wave-changes-page |
cursor |
string |
required |
wave-changes-page |
next_cursor |
string |
required |
wave-changes-page |
width |
uint32 |
required |
wave-changes-page |
has_more |
bool |
required |
wave-changes-page |
changes |
wave-change[] |
required |
clocked-samples-page |
clock |
string |
required |
clocked-samples-page |
signals |
string[] |
required |
clocked-samples-page |
start |
int64 |
required |
clocked-samples-page |
end |
int64 |
required |
clocked-samples-page |
edge |
clock-edge |
required |
clocked-samples-page |
cursor |
string |
required |
clocked-samples-page |
next_cursor |
string |
required |
clocked-samples-page |
has_more |
bool |
required |
clocked-samples-page |
samples |
clocked-sample[] |
required |
clocked-sample |
time |
int64 |
required |
clocked-sample |
values |
wave-sample[] |
required |
wave-change |
time |
int64 |
required |
wave-change |
kind |
wave-kind |
required |
wave-change |
value |
string |
required |
wave-scope-page |
scope |
string |
required |
wave-scope-page |
offset |
nonnegative-int64 |
required |
wave-scope-page |
next_offset |
nonnegative-int64 |
required |
wave-scope-page |
known_count |
nonnegative-int64 |
required |
wave-scope-page |
count_exact |
bool |
required |
wave-scope-page |
has_more |
bool |
required |
wave-scope-page |
children |
wave-scope-child[] |
required |
wave-scope-child |
name |
string |
required |
wave-scope-child |
path |
string |
required |
wave-scope-child |
subscope_count |
nonnegative-int64 |
required |
wave-scope-child |
subscope_count_exact |
bool |
required |
ui.surface.create.params |
surface |
id |
required |
ui.surface.create.params |
mode |
surface-mode |
optional |
ui.surface.create.params |
x |
int |
required |
ui.surface.create.params |
y |
int |
required |
ui.surface.create.params |
width |
positive-int |
required |
ui.surface.create.params |
height |
positive-int |
required |
ui.surface.create.params |
z |
int |
required |
ui.surface.create.params |
opaque |
bool |
optional |
ui.surface.create.params |
focusable |
bool |
optional |
ui.surface.create.result |
generation |
uint64 |
required |
ui.surface.update.params |
surface |
id |
required |
ui.surface.update.params |
clear |
bool |
optional |
ui.surface.update.params |
expected_generation |
nonnegative-int64 |
optional |
ui.surface.update.params |
ops |
draw-op[] |
required |
ui.surface.update.result |
generation |
uint64 |
required |
ui.surface.destroy.params |
surface |
id |
required |
ui.focus.request.params |
surface |
id |
required |
text-op |
op |
literal:text |
required |
text-op |
x |
int |
required |
text-op |
y |
int |
required |
text-op |
text |
string |
required |
text-op |
fg |
color |
optional |
text-op |
bg |
color |
optional |
fill-op |
op |
literal:fill |
required |
fill-op |
x |
int |
required |
fill-op |
y |
int |
required |
fill-op |
width |
nonnegative-int |
required |
fill-op |
height |
nonnegative-int |
required |
fill-op |
fg |
color |
optional |
fill-op |
bg |
color |
optional |
ui.layout.replace.params |
root |
layout-node |
required |
layout-leaf |
id |
id |
required |
layout-leaf |
kind |
literal:leaf |
optional |
layout-leaf |
view |
view |
required |
layout-leaf |
cells |
positive-int |
optional |
layout-leaf |
weight |
positive-int |
optional |
layout-leaf |
min_width |
positive-int |
optional |
layout-leaf |
min_height |
positive-int |
optional |
layout-container |
id |
id |
required |
layout-container |
kind |
container-kind |
required |
layout-container |
children |
layout-node[] |
required |
layout-container |
cells |
positive-int |
optional |
layout-container |
weight |
positive-int |
optional |
layout-container |
min_width |
positive-int |
optional |
layout-container |
min_height |
positive-int |
optional |
layout-container |
gap |
nonnegative-int |
conditional |
state.changed.params |
revision |
uint64 |
required |
state.changed.params |
changed |
state-group[] |
required |
state.changed.params |
state |
state |
required |
ui.resize.params |
width |
positive-int |
required |
ui.resize.params |
height |
positive-int |
required |
ui.focus.changed.params |
focused |
bool |
required |
ui.focus.changed.params |
surface |
id |
conditional |
ui.layout.params |
revision |
uint64 |
required |
ui.layout.params |
surface |
id |
required |
ui.layout.params |
placed |
bool |
required |
ui.layout.params |
node |
id |
conditional |
ui.layout.params |
generation |
uint64 |
required |
ui.layout.params |
x |
int |
required |
ui.layout.params |
y |
int |
required |
ui.layout.params |
width |
positive-int |
required |
ui.layout.params |
height |
positive-int |
required |
ui.input.params |
surface |
id |
required |
ui.input.params |
key |
input-key |
required |
ui.input.params |
modifiers |
modifier[] |
required |
ui.input.params |
character |
string |
conditional |
ui.input.params |
mouse_kind |
mouse-kind |
conditional |
ui.input.params |
button |
mouse-button |
conditional |
ui.input.params |
x |
int |
conditional |
ui.input.params |
y |
int |
conditional |
创建 fullscreen surface 还需要 ui.fullscreen;创建 focusable surface 还需要 ui.focus。
首个请求必须是 session.hello,其中 protocol 与 plugin 必须匹配 manifest。 结果返回选定协议、插件 id、已授予能力、不可变 host 限制、受支持的 methods、 notifications,以及包含各注册类别可用值的 values 对象。插件应使用这些列表 探测 v1 可追加功能,而不是解析应用版本。session.ping 的 params 为空。 session.shutdown 会原子释放调用者的 surface、布局和焦点。
limits 是本会话边界的权威来源。插件必须读取它,不能内嵌当前数值。兼容的 v1 host 可以提高限制,但不能接受请求后静默截断。
| 限制 | 含义 |
|---|---|
max_message_bytes |
单个入站协议 frame 允许的最大 UTF-8 byte 数。 |
max_string_bytes |
有界标识符、路径或命令字符串允许的最大 byte 数。 |
max_text_bytes |
单个绘制文本、命令诊断或波形值允许的最大 byte 数。 |
max_surface_ops |
一次原子更新允许的最大绘制操作数。 |
max_surfaces |
host registry 中允许的最大 live surface 数。 |
max_surface_cells |
所有 live surface 的最大 cell 总容量。 |
max_surface_update_cells |
一次更新实际触达的最大裁剪后 cell 数。 |
max_surface_dimension |
surface 尺寸与坐标绝对值的最大边界。 |
max_surface_z |
surface z 绝对值的最大边界。 |
max_layout_nodes |
一棵 retained 布局允许的最大 node 数。 |
max_layout_depth |
相对 root 的最大布局深度。 |
max_layout_children |
单个布局 container 允许的最大 child 数。 |
max_layout_weight |
flex weight 的最大值。 |
max_layout_gap |
child 间 gap 的最大 cell 数。 |
max_id_bytes |
请求或资源 id 允许的最大 byte 数。 |
max_subscriptions |
单次订阅请求允许的最大 state group 数。 |
max_messages_per_second |
每个插件 rate window 允许的最大入站 frame 数。 |
max_pending_core_requests |
允许单个插件同时存在的最大未完成 Core 请求数。 |
max_scope_children_page |
单次层级请求允许返回的最大直接子 scope 数。 |
max_wave_changes_page |
单次范围请求允许返回的最大已解析变化点数。 |
max_clocked_sample_signals |
Maximum digital signals sampled together at one clock edge. |
max_clocked_samples_page |
Maximum clock edges returned by one clocked sample request. |
max_report_lines |
单个不可变原生报告快照允许的最大行数。 |
max_report_bytes |
单个原生报告所有行允许的最大已解码 UTF-8 byte 数。 |
handshake_timeout_ms |
初始 session.hello 请求允许的时间。 |
recovery_escape_count |
为插件紧急恢复保留的连续 Escape 键数量。 |
recovery_sequence_ms |
紧急恢复 Escape 键之间允许的最大间隔。 |
Core 与波形方法
core.command.invoke(能力core.command.invoke):params 包含一个非空core.signal.reveal(能力core.navigation)解析一个精确信号路径,选择、滚动并core.wave.reveal原子执行同一 signal reveal,并校验、移动到给定原生 tick,core.state.get(能力core.state.read):返回state,字段包括file、wave.scope.children(能力wave.read):返回一页有界的直接子 scope。空wave.value.get(能力wave.read):params 包含稳定信号路径和有符号 64 位原生wave.changes.range(能力wave.read):对一个稳定信号路径返回原生 tick 闭区间
command 字符串,长度受 max_string_bytes 限制,不做 shell 解释;可选的单个 前导 : 会被忽略。只允许执行安装版本命令注册表中标记为 public 的命令,包括 公开的聚焦 panel direct form;内部开发命令会被拒绝。它走正常命令路径,因此 可以修改会话、打开数据、写出命令定义的文件,或像交互操作一样请求 host 退出。 完成后始终返回 ok 与 message;普通命令诊断使用 ok:false,不会与 JSON-RPC transport/capability error 混淆。公开命令语法由已安装 host 提供发现能力,这个 方法不会把每条命令 grammar 变成独立冻结的插件方法。
闪烁原生 signal 行,然后把插件 surface 的键盘焦点交还原生 S panel。 add:false 要求该信号已显示,add:true 才允许显式加入;若 filter 隐藏目标, host 会先保存 undo 再清除 filter。
闪烁信号/时间交点后聚焦原生 W panel。core.time.reveal 用于没有信号锚点的 时间记录,只移动 wave cursor,不修改 signal list。失败请求不会留下部分修改。
format、timescale、minimum_time、maximum_time、cursor、view_start、 view_end、focus、selected_signal、wave_revision、wave_loading,以及 0..100 的整数 wave_progress。
scope 表示波形根,非空值应使用之前发现的稳定 scope path。offset 是非负的 子项下标,limit 必须在 1..max_scope_children_page。结果回显 scope 与 offset,返回 next_offset,绝不包含 signal 或递归后代。known_count 与每个 subscope_count 表示 lazy reader 当前已知的层级 frontier;对应的 *_exact 为 false 时该计数仍可能增长。has_more 表示可以继续请求 next_offset。每次请求 最多让 backend 接纳一个有界 schema page,即使该页没有发现新的直接子 scope。
tick time。sample 结果包含 signal、time、width、kind 和 value。 这是有界、格式无关的点查询,不枚举 signal catalog,也不返回 transition 集合。 完成延迟不是线协议保证:有索引点查询的 backend 可以直接回答,通用兼容 backend 可以扫描该信号。插件在等待期间继续处理通知,并按 id 关联最终响应。
start..end 内的一页有界、已解析值变化。limit 必须在 1..max_wave_changes_page。首个请求使用空 cursor;当 has_more 为 true 时, 使用返回的 opaque next_cursor 和相同信号/区间继续请求。跨页时间严格递增。 同一时间戳的多次 delta 写入会折叠为最终可见值,与普通波形显示一致;delta-cycle 分析属于另一个数据契约。host 只访问指定信号和一页有界结果,因此代价与层级总 信号数无关。v1 仍不提供无界 signal、递归 hierarchy、transition 或时间范围 dump。
Core 命令和波形读取可以 deferred,也可以乱序完成;client 不得从响应时序推断 host 调度。慢 reader 请求不会阻塞协议 I/O 或等待动画。未完成 Core 请求数受协商限制, 重复 pending id 会被拒绝。Core 操作一旦接受就具有 at-most-once 语义,即使调用者 断开也可能继续完成;卸载 UI 不能回滚已启动的命令。
原生报告发布
ui.report.publish 需要 ui.report。params 包含非空且有界的 title 与非空 lines 字符串数组。每一行受 max_text_bytes 限制;整个快照同时受 max_report_lines 与 max_report_bytes 限制。host 在完整校验后原子替换并打开 原生 report overlay,返回实际接收的 rows。Tab 分隔行使用原生表格渲染,全部 文本仍会经过终端控制字符过滤。
发布后,报告是由 host 持有的不可变副本。插件卸载不会关闭或使它失效,后续原生 命令或其他插件可以替换它。发布报告不会执行文本,也不会注册命令、按键、补全或 help 条目。v1 有意不提供动态全局 : 命令注册:namespace、冲突、help/completion、 教程、卸载和执行中调用的生命周期需要独立的未来契约。manifest headless.commands 改为使用 host 管理的 :plugins exec namespace;插件也可在自己的 聚焦 surface 中提供局部 action,并通过 core.command.invoke 组合既有公开命令。
Surface
所有 surface id 都是调用者局部 id,返回值绝不会附加内部 owner 前缀。
ui.surface.create 需要 ui.draw,params 为 surface、x、y、width、 height、z。可选 mode 为 overlay、modal 或 fullscreen;fullscreen 还要求 ui.fullscreen。docking 是布局操作,不是第四种 surface mode。可选的 opaque 与 focusable 默认为 false;focusable surface 需要 ui.focus。结果 generation 为零,表示尚未发布。不同 z 的 surface 按升序组合并 hit-test;相同 z 的顺序有意不定义,重叠有意义时插件必须分配不同 z。
overlay 只在已提交矩形内接收鼠标输入。获得焦点的 modal 或 fullscreen surface 也接收矩形外输入,因此局部坐标可以为负或超过尺寸。覆盖 viewport 的不透明 fullscreen surface 会抑制 core frame 渲染,直到被移除或 resize 后不再完全覆盖。
live registry 的 surface 总容量受 max_surface_cells 限制,因此其他 surface 已 占用预算时,创建可以返回 quota。ui.surface.update 需要 ui.draw、surface 和有界 ops;可选 clear 默认为 false,可选 expected_generation 拒绝陈旧绘制。 完整请求先在副本上校验,再作为一个新 generation 原子发布。ops 最多包含 max_surface_ops 个操作,单个 text 最多 max_text_bytes 个 UTF-8 byte:
{"op":"text","x":2,"y":1,"text":"hello","fg":"palette:12","bg":"default"}
{"op":"fill","x":0,"y":0,"width":20,"height":3,"fg":"#C8D3F5","bg":"#222436"}
颜色精确限定为 default、0..255 的 palette:N 或 #RRGGBB。坐标单位为终端 cell。host 按显示宽度测量并裁剪 UTF-8 文本。Tab 前进到下一个八 cell tab stop; 其他终端控制 codepoint 会在 Frame 边界被中和,绝不能成为原始终端控制序列。 fill 用空格替换目标 glyph 并应用颜色。无效操作不改变当前 generation。所有操作 实际触达的裁剪后 cell 总数受 max_surface_update_cells 限制,避免重叠的大 fill 把小请求放大为无界 raster 工作。
ui.surface.destroy 需要 ui.draw 和局部 surface id;如果该 surface 持有焦点, 也会同时释放。活动布局引用的 surface 不能销毁;插件必须先恢复或替换布局,因此 任何已接受请求都不会留下悬空可见 leaf。
布局与焦点
ui.layout.replace 需要 ui.layout 和一个 root,整棵树校验后原子安装。node kind 为 leaf(默认)、row、column 或 stack;每个 node 都需要唯一的 局部 id。leaf 的 view 只能是 builtin.frame 或 surface:<local-id>;container 使用 children。可选约束为 cells、weight、min_width、min_height;row 与 column 还接受 gap,stack 因 child 重叠而拒绝它。cells、min_width、 min_height 出现时必须是正整数,没有公开的负值 sentinel。leaf 必须恰有一个 view 且没有 children;container 必须有非空 children 且没有 view。 surface 与 builtin.frame 各最多出现一次,因为每个 live view 只有一个解析后矩形。 v1 不存在其他 builtin.* 名称,插件不得猜测。原生 chrome、signal、wave leaf 只有 在具备独立 renderer、overlay 与 input geometry 语义后,才能作为可发现注册值新增。
插件使用协商限制,不能依赖当前数值。每个插件只拥有自己提交的布局贡献;第二个插件 不会替换第一个插件,也不会因第一个插件存在而被拒绝。host 只保留一份原生 frame, 抽取各插件的 surface 子树,再按照声明的 minimum 将独立插件窗口自适应排成有界的行列。 只有一个贡献时仍精确保留其 v1 布局(包括有意的 fullscreen);有多个贡献时,聚合布局 归核心所有,任何插件都不能放置或缩放另一个插件的 surface。ui.layout.restore 只删除 调用者自己的贡献。嵌入布局的 surface 临时使用解析后的 leaf 矩形;不再 placed 时回到 ui.surface.create 声明的矩形。两种 resize 都会使先前 generation 失效并要求重绘,陈旧 dock 内容不会覆盖恢复后的核心 UI。
row 分配水平轴,column 分配垂直轴。带 cells 的 child 在满足 minimum 的前提下 取得固定主轴尺寸;其他 child 先取得 minimum,再按 weight 比例分配剩余 cell, 余数按声明顺序分配。gap 只插在实际 placed 的 child 之间。stack child 共用一个 矩形;core frame 先组合,插件 surface 保留显式 z 顺序。省略 builtin.frame 会抑制原生 frame 渲染。如果后续终端 resize 无法满足 minimum,host 保留最后一次 有效 placement、报告 resize,并允许插件提交替代布局。
row/column 产生的分隔线是 host 控件。用鼠标左键拖动分隔线,可以在满足双方 minimum 的前提下调整相邻的原生/插件或插件/插件窗口。插件 surface 聚焦时,Alt+Left/Right 把最近的水平 split 调整两列,Alt+Up/Down 把最近的垂直 split 调整一行。成功的缩放 手势由 host 保留,不会作为 ui.input 发送给插件;插件应根据随后收到的 ui.layout notification 重绘。
ui.focus.request 需要 ui.focus 以及属于调用者、已提交、可聚焦的 surface。 鼠标按下已提交的 focusable surface 也会获取焦点 lease。程序请求可以在调用者自己 的 surface 间移动焦点,但不能抢占其他插件的 lease,否则返回 conflict;显式用户 鼠标点击可以转移焦点。无插件聚焦时,ui.focus.release 幂等成功,但它不能释放 其他插件的 lease。连续 Escape 的协商数量由 host 保留用于紧急恢复,相邻按键间隔 不得超过 recovery_sequence_ms。前面的 Escape 正常发送,最后一个由 host 消费, 随后终止插件并原子移除其 surface、布局和焦点 lease。非 Escape 输入或超时会重置 序列。
订阅与通知
event.subscribe 与 event.unsubscribe 需要 core.state.read。非空 events 数组接受 cursor、view、selection、focus、wave。新增订阅后的首次 host pass 会发送快照,即使值刚刚没有变化;之后未变化的 pass 会合并。
host notification 是没有 id 的 JSON-RPC request:
state.changed:单调revision、已订阅的changedgroup 和 typedstate快照。ui.resize:终端width与height。ui.focus.changed:focused,以及为 true 时的局部surface。初始状态、插件ui.layout:布局 revision 改变时,对插件拥有的每个 surface 各发送一次;包含ui.input:局部surface、规范化key、modifiers、可选character,以及
获得/移动/失去焦点(包括点击外部)时都会发送。
局部 surface、revision、当前 generation、placed 和生效的 x、y、 width、height。只有 placed:true 时才有 node。恢复或移除布局后的 placed:false 明确报告回到 create 时声明的矩形;generation 为零要求重绘。
适用时的鼠标字段。modifiers 是数组,不是内部 bit mask。鼠标 x、y 是从零 开始的 surface 局部 cell;button 是名称,只在 press/release/drag 时出现。
resize、布局解析和聚焦输入 notification 属于资源生命周期事件,不需要 state 订阅。Core state 快照中的 focus 是最后一次原生 signal/wave panel 焦点; 插件焦点 lease 只通过 ui.focus.changed 报告。
下表是 v1 线协议值基线。周围 result/notification 明确可扩展时,host 可以新增值; 插件必须把未知值当作未知值处理,不能套用已有值语义。
| 类别 | 值 |
|---|---|
state_group |
cursor |
state_group |
view |
state_group |
selection |
state_group |
focus |
state_group |
wave |
surface_mode |
overlay |
surface_mode |
modal |
surface_mode |
fullscreen |
layout_kind |
leaf |
layout_kind |
row |
layout_kind |
column |
layout_kind |
stack |
draw_op |
text |
draw_op |
fill |
builtin_view |
builtin.frame |
clock_edge |
rising |
clock_edge |
falling |
input_key |
char |
input_key |
up |
input_key |
down |
input_key |
left |
input_key |
right |
input_key |
home |
input_key |
end |
input_key |
page-up |
input_key |
page-down |
input_key |
delete |
input_key |
f2 |
input_key |
mouse |
input_key |
enter |
input_key |
backspace |
input_key |
escape |
input_key |
unknown |
input_modifier |
shift |
input_modifier |
alt |
input_modifier |
control |
mouse_kind |
press |
mouse_kind |
release |
mouse_kind |
drag |
mouse_kind |
wheel-up |
mouse_kind |
wheel-down |
mouse_kind |
move |
mouse_button |
left |
mouse_button |
middle |
mouse_button |
right |
focus_value |
signal |
focus_value |
wave |
wave_kind |
bits |
wave_kind |
real |
wave_kind |
string |
Tcl SDK v1 过程
下表是无外部依赖 Tcl SDK 的稳定 v1 基线。可以兼容新增 helper;已有名称、位置参数 和默认值不会删除或挪用。名称中包含下划线前缀 namespace component 的过程属于实现 细节,不是 SDK API。
| 过程 | 参数 |
|---|---|
::svw::quote |
value |
::svw::request |
method params ?callback? |
::svw::has_capability |
name |
::svw::limit |
name |
::svw::supports_method |
name |
::svw::supports_notification |
name |
::svw::supports_value |
category value |
::svw::ping |
?callback? |
::svw::surface_create |
id x y width height ?option value ...? |
::svw::surface_clear |
id |
::svw::surface_text |
id x y text ?option value ...? |
::svw::surface_fill |
id x y width height ?option value ...? |
::svw::surface_commit |
id ?callback? |
::svw::surface_destroy |
id ?callback? |
::svw::focus |
id ?callback? |
::svw::focus_release |
?callback? |
::svw::layout_replace |
root-json ?callback? |
::svw::layout_restore |
?callback? |
::svw::layout::leaf |
id view ?option value ...? |
::svw::layout::row |
id children ?option value ...? |
::svw::layout::column |
id children ?option value ...? |
::svw::layout::stack |
id children ?option value ...? |
::svw::ui::label |
surface x y text ?option value ...? |
::svw::ui::box |
surface x y width height ?option value ...? |
::svw::ui::progress |
surface x y width fraction ?option value ...? |
::svw::ui::list |
surface x y values ?option value ...? |
::svw::ui::button |
surface x y label ?option value ...? |
::svw::ui::input |
surface x y width value ?option value ...? |
::svw::ui::tabs |
surface x y labels selected ?option value ...? |
::svw::ui::table |
surface x y headers rows ?option value ...? |
::svw::ui::tree |
surface x y nodes ?option value ...? |
::svw::command |
command ?callback? |
::svw::reveal_signal |
signal ?add? ?callback? |
::svw::reveal_time |
time ?callback? |
::svw::reveal_wave |
signal time ?add? ?callback? |
::svw::report_publish |
title lines ?callback? |
::svw::state_get |
?callback? |
::svw::subscribe |
events ?callback? |
::svw::unsubscribe |
events ?callback? |
::svw::wave_value |
signal time ?callback? |
::svw::wave_changes |
signal start end cursor limit ?callback? |
::svw::wave_samples_clocked |
clock signals start end edge cursor limit ?callback? |
::svw::wave_scope_children |
scope offset limit ?callback? |
::svw::shutdown |
- |
::svw::on |
event callback |
::svw::bind |
key callback |
::svw::unbind |
key |
::svw::timer |
milliseconds callback |
::svw::timer_cancel |
token |
::svw::json_decode |
document |
::svw::json_get |
document path |
::svw::json_type |
value |
::svw::json_payload |
value |
::svw::string_field |
line path |
::svw::number_field |
line path |
::svw::bool_field |
line path |
::svw::string_array_field |
line path |
::svw::run |
- |