Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ Worker messages:
- **Visualizer modes:** in `box` mode every call is a `Frame` box at its call site. In `substitution` mode (the default; prop `visualizerMode`, embed `?visualizer=`), a call with `shortForm` renders as `Frame` scope `'inline'`: its return expression with parameters substituted, in parentheses in operator context. The outermost box says why it isn't replaced (the call-enter step's `boxReason`, `visualizerBoxReason`).
- **Tree mode («Baum», capability `tree`, embed `features=tree`):** `src/treeMode.ts`. `treeModeSteps` takes the substitution mode's steps and adds a step of its own (`treePhase` `'open'`/`'close'`, a copy of a nearby step with `step` set to the state it shows) before and after each statement expression's reductions (`TreeEpisode`: open in one frame between `openIndex` and `closeIndex`). It is also a substitution mode (`substitutionMode` is true), so inline calls, pending boxes and hosts work as there. `Frame` splices the open tree into its line like a child (`childRanges` entry with `frame: null`, `ExpressionTreeBox`) and hangs the boxes of the calls in it below. `buildExpressionTree` builds the tree from the AST and `buildSubstitutionsUpTo` (so it can't diverge from the chain; node keys `frameId:nodeId`, an inline call's body with the callee's frame). `episodeTreeLayout` lays a tree out where it opened or last grew (an inline call replaced, a box opened or closed), with room for every later text, so reductions move nothing; `TreeView` then takes that `layout`. A call of the program's own labels its argument edges with the parameters (`edgeLabel`, from the episode's `calls`; `edgeLabelPlacement` in `treeLayout.ts`, room reserved by the layout). A call that stays a box is a `kind: 'box'` node of measured size (`ExpressionTreeBox` measures the drawn box in characters and rows, growth-only; the layout gives a box several rows), drawn by `TreeView`'s `renderBox`. Check changes with `test:tree` and the «Baum» cases in `test:console`.
- **Steady height (compact hosts):** while visualizing, the canvas keeps the height of the tallest step from the start (`steadyHeightCandidates`: up to 120 likely tallest steps, drawn hidden in `.visualizer-measure` through the same `rootFrame(stepViewAt(…))` as the visible step, and measured), and only grows; the console reserves the last step's output (`.visual-output-steady`). The embed caps the visualizer at 80% of `windowHeight` (the macro's). A step's view is `StepView`; compute it with `stepViewAt`, not inline, so the measurement and the visible canvas stay the same.
- **Global variables:** the module frame has no variables column; `GlobalsFooter` shows them in `.visualizer-globals`, below the scrolling `.visualizer-view`, both inside `.visualizer-frame` (from the start if `traceHasGlobals`; an invisible layer of the last step's entries reserves its height). Hover, tap or focus opens `.visualizer-globals-peek` (the definition via `globalDefinitionSource`, else the full value). An arrow with an end in the footer has `layer: 'frame'` and is drawn in `.visualizer-connectors--frame` over view and footer (`connectorBetween`, `localsValueElement`); all arrows are measured again when the view scrolls. The current call's box (`.frame-box--current`) keeps its variable row sticky at the view's bottom.
- **Layout and scrolling:** nested boxes show their variables below their code (CSS). The scroll effect follows the active box horizontally. It sets `scrollLeft`/`scrollTop` on the visualizer view only; don't use `scrollIntoView`, which also scrolls the LiaScript page around the embed and moves the step buttons.
- **Console and Stop:** output, REPL inputs, Tests reports and notes form one transcript (`ConsoleEntry` list after `output`). While Python is busy for more than `STOP_BUTTON_DELAY`, the run button is **Stop**: `stopPython` terminates the worker and calls `startWorker` again, so the REPL namespace is gone after a stop - except while `input()` waits: then Stop only cancels the wait (`EOFError`). The embed reports this to its parent (protocol version 4, `glpython:busy`/`glpython:stop`).
- **One Monaco model for all files:** a tab switch replaces the editor's text. `updateCode` ignores flush events (`setValue`), since `@monaco-editor/react` shows a read-only file (`ProjectFile.readOnly`) with `setValue` before its `onChange` knows the new tab - the text would land in the previous file.
Expand Down
34 changes: 26 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,24 @@ The visualizer's toolbar is one line: first step, previous step, next step,
last step, the scrubber, the step counter, the box/substitution switch, the
**⋯ Options** menu (animated steps, and with `export` the → chain's comments and
copy button) and ✕ to exit (`visualizerFirstStep`, `visualizerLastStep`,
`visualizerOptions`; ✕ uses `exitVisualizer`). The variables panel only appears
when there are variables; in a visualizer narrower than 900px it sits below the
code.
`visualizerOptions`; ✕ uses `exitVisualizer`).

A call box lists its variables in a row below its code; the box of the current
call, when it's taller than the view, keeps that row at the view's bottom edge
while the box is on screen. The file's (global) variables are in a footer below
the code that doesn't scroll with it, at every width, so the code has the full
width: name and value (`auswerten Funktion`, `x 3`) in a row that wraps, at
most about three lines high (then it scrolls). A program without global
variables has no footer; one with them has it from the start, as tall as at
the last step. Hovering over an entry, tapping it (touch) or focusing it opens
a box over the code, upwards from the pointer and inside the visualizer (at
most its width and ⅔ of its height, scrolling inside): a function's (or
class's) whole definition, highlighted, also while it's open as a call box; any
other value in full. The box stays while the pointer moves into it; it closes
when the pointer leaves both, on a tap elsewhere or with Escape. Arrows from a
call to a global function, and between a global variable and the code, end at
its entry in the footer. Text: `visualizerGlobals` (the footer's label for
screen readers), optional.

The toolbar switches between two ways of showing a function call.
In **substitution** mode (the default), a call to a function whose body is a
Expand Down Expand Up @@ -219,7 +234,8 @@ keeping its first elements and its last one: `[1, 2, 3, 4, 5, …, 100]`.
elements too. A long string is shortened in the middle.
- **In full:** an abbreviated value is underlined with dots. A click (or Enter)
shows it in full, with its number of elements or characters (`100 Werte`,
also below it in the variables panel). The runtime keeps up to 50 000
also below it in a call box's variables). A global variable's box (in the
footer) shows it in full, with its length. The runtime keeps up to 50 000
characters of it.

A long value in a line wraps within the code instead of running on below the
Expand All @@ -245,10 +261,11 @@ leaf. `['+', ['var', 'x'], ['f', 1, 2]]`:

Labels have no quotes, string leaves do. The rule is generic, so `['Anna', 'Ben']`
is drawn too; the text stays as it is. Where:
- **Variables panel:** below the value; a tree wider than the panel
- **Variables of a call box:** below the value; a tree wider than the panel
(48 characters) has a link «Baum zeigen» (`showTree`) to the full-value
dialog instead. The options menu's «Listen als Baum zeigen»
(`visualizerListTrees`) switches the trees off and on.
dialog instead. A global variable's tree is in its box (hover or tap its
entry in the footer), below the full value. The options menu's «Listen als
Baum zeigen» (`visualizerListTrees`) switches the trees off and on.
- **Full-value dialog:** below the value's text.
- **REPL:** a value that reads as a tree has a «Baum» button (`replShowTree`)
next to «Visualisieren»; it shows the tree below the value and hides it
Expand All @@ -257,7 +274,8 @@ is drawn too; the text stays as it is. Where:
A tree of more than 100 nodes, deeper than 10 or containing itself isn't
drawn; long labels and leaves are shortened in their middle. For screen
readers each tree is an image described by the list's text. A tree wider
than its place (the console, the dialog, a narrow panel) scrolls sideways:
than its place (the console, the dialog, a global variable's box, a narrow
panel) scrolls sideways:
it starts with its root in the middle, a ‹ or › at an edge shows that more is
hidden there, and it takes the focus, so the arrow keys, Home and End scroll
it. The positions come from the package's own implementation of van der Ploeg's
Expand Down
Loading
Loading