Project: JSVisualizer
Version: 1.13
Created: 2026-05-25
Last updated: 2026-08-27
Author: Tetsuo Tanaka
| Version | Date | Changes |
|---|---|---|
| 0.1 | 2026-05-25 | Initial draft |
| 0.2 | 2026-05-25 | Added step-control UI (2-row × 4-col buttons), 3-layer code highlight, theme switching |
| 0.3 | 2026-05-26 | Phase 3 implementation: V-06 BarChart / V-07 ColorBox / V-08 Timeline / V-09 Heatmap |
| 0.4 | 2026-05-26 | Phase 3.5: V-02b LineTrace added; AnimatedTrace set as inactive |
| 0.5 | 2026-05-26 | Phase 4/5: V-10 RecursionTree / V-11 Lifetime / V-12 ControlFlow / V-13 MemoryView / V-14 ObjectGraph; new TraceBuilder methods |
| 0.6 | 2026-05-26 | Phase 6 polish: keyboard tab switching (1–9), active-tab persistence, error badges (parse vs runtime), 17 samples, color-blindness support, GitHub Pages deploy |
| 0.7 | 2026-05-26 | Fixes 1–8: destructuring support, draggable pane resizer, CodeMirror 6, program name display, always-visible Console panel, LineTrace improvements, TraceTable target column |
| 0.8 | 2026-06-02 | V-15 CallTree added; LineTrace 2-pane; ScopeView/StateView scope merge; Heatmap dots + ratio; RecursionTree arg expansion; Console height drag; callStack order fix |
| 0.9 | 2026-06-03 | Scope display algorithm rework (lexical scope mergeScopesForDisplay); buildRecursionTree filter to recursive-only + cost; buildCallTree fully independent; Heatmap dynamic background + 3× dot width |
| 1.0 | 2026-06-04 | Exact outer-frame variable display: Environment.snapshotOwn() + Recorder.frameEnvStack; frameEnvs per TraceEvent; mergeScopesForDisplay 3rd arg |
| 1.1 | 2026-06-04 | ScopeView/CallStackView removed from tabs (inactive); LineTrace switched to single-pane + snippet; ColorBox multi-select + per-pointer rows + no string truncation; Timeline dynamic Y-axis |
| 1.2 | 2026-06-04 | while/for condition evaluation added to humanStep; LineTrace & ExecTrace condition columns; Heatmap connect lines always-on; ColorBox max-size pre-computation |
| 1.3 | 2026-06-05 | V-02c SubstTrace and V-02d ExprTrace added; tab count 14 → 16 |
| 1.4 | 2026-06-08 | ExprTrace improvements: VariableDeclaration position via regex; more statement types; variable value timeline; real-time update in update() |
| 1.5 | 2026-06-16 | Diff highlighting (formatValueDiff); ObjectGraph hierarchical layout (Kahn topo-sort + longest-path) with elbow connectors, port spread, connected-component separation; object-identity bug fix in Environment.snapshot() |
| 1.6 | 2026-07-17 | (1) Tab cleanup: V-03 TraceTable, V-06 BarChart, V-08 Timeline set as inactive (tab count 16 → 13). (2) ControlFlow rework: replaced buildControlFlow() with buildCFG() — AST-based DOM flowchart with if/else shown as side-by-side true/false columns and loops as condition + body; unexecuted nodes grayed out (cf-node--dead); execution count badge (×N) per node. (3) execCount fix: CfgBuilder now counts line executions by transition (only when line changes), not all AST enter events. (4) SubstTrace & ExprTrace object expansion: fmtPlain() gains depth arg; depth < 3 expands object values recursively (keys omitted: {3, null}, {2, {3, null}}); depth ≥ 3 abbreviates as {…}; function-valued properties filtered out. (5) Sample expansion: 4 Study Tasks added for CELDA 2026 evaluation experiment; sample count 17 → 21; test count 66 → 70 |
| 1.7 | 2026-07-20 | (1) Header layout redesign: Edit mode shows Edit/Run buttons + sample select; Run mode shows Edit/Run buttons + step controls in the header center (footer removed). CSS visibility toggled via .app-header.run-mode. Header height changed to auto (min 44px); app-main uses flex: 1. (2) Slider maximization & 2-row layout: .slider-area { min-width: 180px } causes the slider to wrap to a second row on narrow windows. body { min-width: 820px } + html { overflow-x: auto } shows a horizontal scrollbar below the minimum width. (3) View description bar: .view-desc element auto-inserted below the tab bar. ViewSwitcher.register() accepts a 4th description argument; displayed on tab switch. All 13 views have descriptions. (4) Light mode UI improvements: active tab uses white background + blue top border + blue text + bold (active tab contrast enhancement via :root:not([data-theme="dark"])); console background set to white (var(--bg)) in light mode only. |
| 1.8 | 2026-07-20 | Language switching (i18n): New src/i18n.js module (STRINGS object, t(key) / getLang() / setLang() functions, langchange custom event). Language persisted to localStorage('jsv-lang') as 'ja' or 'en'; defaults to 'ja'. EN/日 toggle button (btn-lang) added to the right of the header. Static HTML elements use data-i18n="key" attributes, batch-updated by applyI18n(). Tab labels and descriptions are passed to ViewSwitcher.register() as { ja: '...', en: '...' } objects and re-rendered by ViewSwitcher.setLang(lang). resolveStr(v, lang) helper resolves both plain strings and {ja,en} objects. |
| 1.9 | 2026-08-12 | F-04 URL query code loading added: new exerciseId/codeId/bhvApiBase query params load code from an external source (e.g. BhvVisualizer) (src/core/exercise-source.js, ADR-029). Purely additive — the default Fibonacci sample and the 21 built-in samples are unaffected. README documents the query params and the expected API response JSON shape |
| 1.10 | 2026-08-12 | F-04 redesigned from "ID + base URL" to "complete URL": replaced exerciseId/codeId/bhvApiBase with exercise/code (the caller passes a directly fetchable URL) (ADR-031). Simplified the response format to title/code only, dropping the unused id/exerciseId. Since ID matching is no longer needed, removed the exerciseId+codeId lookup logic and instead auto-loads the first code when exercise alone is given. exercise+code can be combined as two independent fetches (code wins). No backward compatibility (pre-launch, so the old params were dropped outright) |
| 1.11 | 2026-08-12 | F-04: added optional title to the exercise response, reflected in the sample selector placeholder: code-editor.js gained setPlaceholderLabel() — when an exercise title is available, it replaces the sample selector's default placeholder ("─ Sample ─") with the exercise title (removing the target option's data-i18n attribute so a later language switch doesn't overwrite it) (ADR-032). Also stopped changing the sample selector's value when auto-loading the first code for exercise-only requests, so the placeholder keeps showing the exercise title |
| 1.12 | 2026-08-12 | F-04: hide the built-in samples from the sample selector while exercise/code is present: real-world testing showed the "─ Exercise ─" group was easy to miss, buried below the 8 built-in groups; rather than just reordering it, the built-in samples are now removed instead. code-editor.js's addRemoteGroup() was replaced with setRemoteCodes(items) (removes all built-in optgroups, then adds only the given codes). code-only requests use the same function to leave a single-code selector. (ADR-033) Standalone loads without either query param are unaffected |
| 1.13 | 2026-08-27 | F-11: added URL query (view) for specifying the initial view: for BhvVisualizer's session-level pre/post questions ("predict before touching the tool → operate to check → answer again"), leaving ViewSwitcher's existing behavior (restoring the last-active tab from localStorage) as-is meant each student could resume from an unrelated past session's tab, so the "operate to check" step wasn't consistent across students. Added view to exercise-source.js's parseQuery() and a new ViewSwitcher.setInitialView(id), which takes priority over the localStorage value for only the page's first run (the stored value itself is left untouched). A # BHV:-tag-free general feature (ADR-036) |
In programming education, students often cannot understand what a program is doing, making it hard to fix bugs. The root cause is an inability to follow how memory state changes over time — the dynamic behavior of a running program.
Existing approaches have the following problems:
| Approach | Problem |
|---|---|
| Paper / static slides | Cannot follow behavior continuously |
| PowerPoint animations | High authoring cost; must be remade when code changes |
| Algorithm Visualizer | Requires embedding custom visualization code; low generality |
| Python Tutor | Python-only; fixed layout; no JavaScript support |
Provide a web application that visualizes the execution of any JavaScript code from multiple angles, with no special annotations required.
- Target language: JavaScript (ES6+)
- Target users: Beginner-to-intermediate programming learners, educators
- Target environment: Modern web browsers (latest Chrome / Firefox / Safari)
| Type | Description |
|---|---|
| Learner | A student who wants to verify and debug their own code's behavior |
| Educator | A teacher who wants to use it as a live demonstration during a lecture |
| Content creator | A designer who wants to quickly generate animated trace diagrams or flowcharts |
- User enters JavaScript code in the code editor
- User clicks ▶ Run
- The app analyzes all steps and prepares visualizations
- User steps through the execution one step at a time
- User switches between views to examine the behavior from different angles
- User presses one of the 8 step buttons (or the corresponding keyboard shortcut)
- The current position in the code (line, expression, call site) is highlighted, and all visualization views update in sync
- User can jump to any position with the slider or the first/last buttons
- User selects a view by clicking a tab in the right pane
- The selected view is mounted immediately and rendered at the current step
- Keyboard keys
1–9switch to the Nth registered tab (<textarea>/<input>focus suppresses this) - The last active tab is saved to
localStorage('jsv-active-tab')and restored on next launch
- User selects a learning scenario from the sample selector in the header (21 samples)
- The code is inserted into the editor automatically and is ready to run
- User clicks the EN button (or 日 button) at the top-right of the header
- All UI text (button labels, tab names, descriptions, settings panel, etc.) switches instantly to English or Japanese
- The selected language is saved to
localStorage('jsv-lang')and restored on next visit
- User clicks the ⚙ button in the top-right corner to open the settings panel
- User selects Light or Dark
- The setting is applied immediately and persists across sessions
| Item | Specification |
|---|---|
| Input | JavaScript (ES6+) text |
| Syntax highlighting | CodeMirror 6 real-time highlighting (keywords, strings, numbers, comments); auto-switches with light/dark theme via Compartment + MutationObserver |
| Samples | Preset selector (21 programs: Bubble Sort, Fibonacci, Binary Tree, etc.); program name displayed in header when selected |
| Error display | Parse and runtime errors shown as distinct badges below the editor. Cursor moves to the error location with a blink animation when location info is available |
| Destructuring | Supports ES6 destructuring: [a, b] = [b, a], ({ x, y } = obj), etc. |
In Run mode, the header center holds the step buttons in a single row (wide) or two rows (narrow), plus first/last buttons at each end.
Wide: ⏮ ⏭ │ ⏪Func ⏩Func │ ◁Human ▷Human │ ◀◀Stmt ▶▶Stmt │ ◀Expr ▶Expr │──slider──│ counter
Narrow: ⏮ ⏭ │ ⏪Func ⏩Func │ ◁Human ▷Human │ ◀◀Stmt ▶▶Stmt │ ◀Expr ▶Expr
─────────────────────── slider ──────────────────────────── │ counter
| Action | Button | Keyboard | Description |
|---|---|---|---|
| First step | ⏮ | Home |
Jump to step 0 |
| Expr back | ◀Expr | b / ← |
Decrease cursor by 1 |
| Expr forward | ▶Expr | n / → |
Increase cursor by 1 |
| Stmt back | ◀◀Stmt | V |
Go to start of previous statement |
| Stmt forward | ▶▶Stmt | v |
Go to start of next statement |
| Human back | ◁Human | H |
Go to previous humanStep |
| Human forward | ▷Human | h |
Go to next humanStep |
| Func back | ⏪Func | F |
Go to previous callDepth change |
| Func forward | ⏩Func | f |
Go to next callDepth change |
| Last step | ⏭ | End |
Jump to the final step |
| Jump to position | Slider | — | Jump to any position in the trace |
Button colors: fine-grained (Expr/Human) = accent blue; coarse-grained (Stmt/Func) = gray
| Granularity | Internal API | Definition |
|---|---|---|
| Expression (Expr) | cursor ± 1 | Every AST node enter/exit — finest granularity |
| Statement (Stmt) | stepOver() → matchIdx |
Statement nodes only (skips sub-expressions) |
| Human | humanStep() / humanStepBack() |
Meaningful change points: assignments, condition tests, while/do-while/for condition eval per iteration, for update expr, function calls |
| Function (Func) | Move cursor to next callDepth change in trace |
Function call / return as a single unit |
| Item | Spec |
|---|---|
| Query parameters | exercise (a complete URL to fetch an exercise from), code (a complete URL to fetch a specific code from). The caller passes a fetchable URL directly — JSVisualizer has no ID scheme or API path convention of its own |
| Fetching | Fetches the exercise/code value directly and applies the response to the editor / sample selector (src/core/exercise-source.js) |
| Behavior | exercise only → replaces the sample selector's contents with the exercise's codes (instead of the built-in samples) and automatically loads the first one into the editor. code only → loads that code directly into the editor, and replaces the sample selector with just that one code. Both → the selector stays as the exercise's code list, and the editor shows the code value (which takes priority over the automatic first-code load). Neither → no-op (default Fibonacci sample and the 21 built-in samples are unaffected). While exercise/code is present, the built-in samples are temporarily removed from the sample selector (ADR-033) |
| Error handling | A nonexistent/non-public URL or a network error shows a message in the error banner below the editor |
| Expected response format | exercise → { title?, codes: [{ title, code }] }; code → { title, code }. Any non-200 status is treated as "not found / not public". See the README ("Expected API response format") for the full JSON shape |
| Placeholder display | If the exercise response includes a title, the sample selector's placeholder (default "─ Sample ─") is replaced with the exercise title (code-editor.js's setPlaceholderLabel(), ADR-032) |
| Purpose | Works as a general-purpose "direct link to a specific piece of code" even when JSVisualizer is used standalone. In the BhvVisualizer integration, this is the delivery path for exercises/codes a teacher created. Because a complete URL is passed, code can be hosted anywhere, not just on BhvVisualizer. See the README ("Loading code from a URL query") and ADR-031 for details |
Tab label: Call Stack
- Call Stack panel: frames from
mergeScopesForDisplay(). A "Global" frame is always shown first (even when the call stack is empty), followed by call frames (innermost-first, with labels likefactorial(6); the innermost frame is highlighted) - Changed variables flash
The former "State" tab's Current Step card (phase/nodeType/line/value) and Variables card (redundant with the Call Stack's innermost frame) were removed per ADR-026. Console output is in the always-visible panel at the bottom of the right pane (see F-14).
Input: state.event, state.scopes, state.callStack, state.frameEnvs
Tab label: Variable
- Single-pane layout: line-number column + variable matrix table
- Line-number column shows the line number and a 15-character snippet of source
- Rows = source lines (all lines shown statically); columns = variable names (dynamic, added as variables appear)
- Each cell shows the variable's value at the last execution of that line
- Changed cells flash orange when the cursor advances
- Diff highlighting: for the active row (
lt-row--active), changed values are highlighted orange-bold viaformatValueDiff()(arrays/objects highlighted element-by-element) - Functions and class values are not shown in columns
- Current line is highlighted and scrolled into view
- Column visibility: toolbar buttons above the table toggle each variable column on/off
- Column reordering: drag
<th>elements to reorder columns
Input: builder.getHumanStepList(), builder.trace, builder.source, state.cursor, state.event
Tab label: Exec Trace
- Rows = one per humanStep, in execution order
- All humanStep rows rendered at
init()time update()only moves the highlight row and callsscrollIntoView()— O(n)- Columns: # | Line | Code (first 30 chars) | Variable columns (in appearance order) | Condition columns (in appearance order)
- Variable columns: values via
flattenEnv; diff-highlighted orange-bold viaformatValueDiff()atinit()time - Condition columns: while/for condition values shown per iteration (both
trueand finalfalse)
- Variable columns: values via
Input: builder.getHumanStepList(), builder.trace, builder.source, state.cursor
Tab label: Subst
- Shows recursive function calls as a substitution-model expansion
- Starts from the first user-defined function call (e.g.,
factorial(5)) - Each
ReturnStatementreplaces the call string with the evaluated return expression, adding a new row - The final row shows the top-level call's resolved value (e.g.,
→ 120) - Two highlights:
.stx-hl-expanded(orange background): the part just replaced.stx-hl-pending(blue bold): the next call to be substituted
update()attaches.stx-line--activeto the latest row ≤ cursor; past rows get.stx-line--past
Input: builder.trace, builder.source
Tab label: Expr
- Shows the evaluation of a single statement's expression as a sequence of partial substitutions converging to the final value
- Each section is visible while
cursor >= enterIdx && cursor <= exitIdx - Variable columns show only identifiers that appear in the expression text (functions excluded)
- Two highlights:
.xev-hl-expanded(orange background): the part just substituted.xev-hl-pending(blue bold): the next sub-expression to be evaluated
update()marks the latest row ≤ cursor as active and rewrites that row's variable cells in real time fromtrace[cursor].env
Supported statement types: ExpressionStatement, VariableDeclaration (init), IfStatement test, WhileStatement test (per iteration), ReturnStatement argument, ForStatement init/test (per iteration)/update (per iteration)
Input: builder.trace, builder.source
Tab label: All Steps (currently unregistered)
- All humanSteps rendered at
init()time update()only moves the highlight row and scrolls into view — O(1)- Columns: # | Line | Event | Target | Value
- Target: variable name (for assignments),
funcName(args)(for calls),"return"(for return statements)
- Target: variable name (for assignments),
Input: builder.getHumanStepList(), builder.trace
- Scope chain shown as nested boxes
- Innermost frame (currently executing scope) highlighted with accent border
- Changed variables flash
- Stack frames shown as stacked cards
- Cards slide in on function call; top frame highlighted with accent color
Tab label: Bar Chart (currently unregistered)
- Numeric variables and numeric arrays shown as vertical bar charts
- Value changes animated as bar-height CSS transitions
- Variable selection chips (arrays selected by default; multi-select enabled)
- Bar color gradient from blue (small) to red (large) based on ratio to max value
- "Step forward to see the bar chart" guide shown at initial step (before any variables exist)
- Tab grayed out if the trace contains no numeric variables or numeric arrays
Input: state.event.env, state.cursor, builder.trace
Tab label: Arrays
- Multiple arrays displayed simultaneously; blocks wrap when the area is too narrow (
flex-wrap: wrap) - Each array block has a label, border, and background color (
.cb-array-block) - Box color gradient from blue to red based on value magnitude
- Variable selection chips (multi-select; last selection cannot be deselected)
- Pointer detection: integer variables in
[0, arr.length)range are auto-detected and their target box is highlighted; each pointer variable appears in its own row - String values shown without truncation
- Pre-computed max size:
#scanTrace()determinesmaxWidthandmaxGridHeightper array so blocks don't shift between steps - Tab grayed out if the trace contains no array variables
Input: state.event.env, state.cursor, builder.trace
Tab label: Timeline (currently unregistered)
- All humanSteps scanned at
init()to build each variable's value history - X-axis = humanStep index; Y-axis = variable value
- Multiple variables shown as color-coded SVG line charts
- Y-axis min/max recalculates dynamically when variable chip selection changes
update()only moves the cursor vertical line — O(log n) binary search
Input: builder.getHumanStepList(), builder.trace
Tab label: Heatmap
- Source lines shown with execution count as
"N / M times"format + orange background intensity, updated per step - Timeline dots: each line's execution moments shown as dots in a fixed-width track (360px); past dots highlighted in accent color, future dots in gray
- Connector lines: SVG lines between consecutive-humanStep dots that transition to different lines — always visible (no toggle)
update()updates background color, count text, and dot states for all lines
Input: builder.source, builder.buildHeatmap(), builder.getHumanStepList(), builder.trace, state.event, state.cursor
Reference implementation rendering buildRecursionTree() (recursive calls only, with cost) as an SVG tree. Per ADR-027, V-10b Call Tree below now unifies node display and cost, so this view has been removed from tab registration (code kept at src/views/recursion-tree/).
Input: builder.buildRecursionTree(), state.cursor
Tab label: Call Tree
- All function calls — not just recursive ones — shown as an SVG tree
- Node display unified with RecursionTree (ADR-027): function name (line 1), args (
fmtArgsLines(), up to 2 lines), return value, and cost (cost:N, bottom-left) - Node colors and icons: not-yet-called (gray, dashed, "…") / executing (blue, thick border, "▶") / completed (green, "✓")
- Layout: recursive subtree-width calculation (leaf = NODE_W=160; parent = sum of children + gap); NODE_H=80
update()only swaps node CSS classes- Tab grayed out if the trace contains no function calls
Input: builder.buildCallTree(), state.cursor
Tab label: Lifetime
- X-axis = humanStep index; Y-axis = call depth — a Gantt-style flame chart (SVG)
- Each bar shows the function name with arguments and local variable values
- Bar width is computed dynamically to accommodate labels without truncation, capped at 3× the minimum chart width
- Bar color coded by
callDepth(different colors for different nesting depths) update()only moves the cursor vertical line
Input: builder.getHumanStepList(), builder.trace, state.cursor
Tab label: Control Flow
- Nodes = executed source lines (in first-seen order, arranged vertically)
- Forward edges (right, blue); back edges / loop returns (left, orange dashed)
- Node background color intensity = execution count
- Current node highlighted with accent border
- Color-blindness support: back edges (loop returns) shown as dashed orange lines (
stroke-dasharray: 6 3)
Input: builder.buildControlFlow(), state.event
Tab label: Memory
- Left column: stack (scope frames and primitive variables)
- Right column: heap (objects and arrays in
#NID boxes) - SVG overlay: Bézier-curve arrows from reference cells to heap objects
WeakMaptracks object identity to prevent duplicate heap nodes- Changed variable rows highlighted in yellow
- Arrows recalculated via
getBoundingClientRect()afterrequestAnimationFrame
Input: state.scopes, state.callStack, state.changedVars, state.frameEnvs
Tab label: Objects
- Nodes = objects and arrays (up to 6 levels deep, cycle detection via
WeakMap) - Edges = property references to other objects (label = property name)
- Variable name labels shown above root nodes
- Primitive variables listed in the top-left corner
- Layout: hierarchical (Kahn topological sort + longest-path column assignment; edges flow left→right)
- Edges: elbow connectors (
M x1,y1 H mx V y2 H x2); port spread distributes multiple edges from the same node vertically - Connected-component separation: undirected BFS detects components; each component laid out independently and stacked vertically; dashed boundary rect drawn when ≥2 components
- Node colors: 6-color palette (
--og-bg-0through--og-bg-5) by node index - Tab grayed out if the trace contains no heap objects
Input: state.variables, state.scopes
Three highlight layers are simultaneously rendered over the code panel:
| Layer | Color | Condition | Description |
|---|---|---|---|
| 1. Line highlight | 🟦 Blue (left border + background) | Always | The entire line at event.loc.line |
| 2. Expression highlight | 🟧 Orange (semi-transparent) | When event.loc and event.end both exist |
Character range of the expression being evaluated |
| 3. Call-site highlight | 🟣 Purple (dashed underline) | When callStack.length > 0 |
The CallExpression that invoked the currently executing function |
Expression and call-site highlights use position: absolute; calc(N * 1ch) for monospace-accurate character-level placement.
- Tabs at the top of the right pane switch the active view
- On tab switch:
destroy()the previous view, theninit()the new one - On each run (
adapter.load()→adapter.moveTo(0)→'ready'event): the view is remounted with the latestTraceBuilder - Keyboard shortcuts
1–9switch to the Nth registered tab (suppressed when<textarea>/<input>is focused) - Active tab saved to
localStorage('jsv-active-tab')and restored on next launch - If a
view=<registered ID>URL query is present, it takes priority over thelocalStoragevalue for only the page's first run, opening that view instead (ViewSwitcher.setInitialView()). Consumed after use — later runs in the same page load fall back to the normal priority (localStorage→ first registered view). ThelocalStoragevalue itself is left untouched (ADR-036) - Tab grayout: tabs whose view would show nothing for the entire run are grayed out (
opacity: 0.38) viastatic hasContent(builder)— checked once per run without mounting the view
| Item | Specification |
|---|---|
| Default | Light theme (Catppuccin Latte base) |
| How to switch | Header ⚙ button → Settings panel radio buttons |
| Choices | ☀️ Light / 🌙 Dark |
| Persistence | Saved to localStorage('jsv-theme') |
| FOUC prevention | Inline script in <head> applies dark theme before CSS loads |
| Item | Specification |
|---|---|
| Languages | Japanese (ja) and English (en) |
| How to switch | Click the EN / 日 button (btn-lang) in the top-right of the header |
| Scope | Button labels, tab names, view descriptions, console title, settings panel text (~46 items) |
| Not localized | Error messages (from JSInterpreter), sample program names |
| Implementation | Static HTML uses data-i18n="key" attributes, batch-updated by applyI18n(). Tab labels and descriptions are passed as { ja: '...', en: '...' } objects and re-rendered by ViewSwitcher.setLang() |
| Persistence | localStorage('jsv-lang') (default 'ja') |
| Event flow | setLang() → dispatchEvent('langchange') → applyI18n() + switcher.setLang() |
Syntax errors and runtime errors are visually distinguished below the editor.
| Error type | Detection | Badge label |
|---|---|---|
| Syntax error | err instanceof SyntaxError, err.name === 'SyntaxError', message matches /^\[Parser\]/i or Unexpected token, etc. |
"Syntax Error" (red badge) |
| Runtime error | All other runtime exceptions | "Runtime Error" (orange badge) |
When the error includes location information (line/column), the CodeMirror cursor moves to that position and the active line blinks with a red animation (3 flashes).
A fixed panel at the bottom of the right pane, always visible regardless of which tab is selected.
| Item | Specification |
|---|---|
| Position | Fixed below the view container (default height 110px; drag top edge to resize between 40–400px; saved to localStorage('jsv-console-h')) |
| Content | console.log / console.warn / console.error output lines with log-count badge |
| Update | Updated on every adapter.ready and adapter.step event |
| Styling | warn → orange row; error → red row |
| Metric | Target |
|---|---|
new JSDebugger(source) completion (≤100-line program) |
Within 500 ms |
| UI update per step operation | Within 50 ms |
| Maximum trace length | 100,000 steps |
TraceBuilder aggregation method (first call) |
Should not be perceptible during view init() |
- Chrome / Firefox / Safari (latest versions)
- Mobile browsers not in scope (responsive layout to be considered in a later phase)
- All step operations (8 directions + first/last) and tab switching (
1–9) must be keyboard-operable - State must be communicated via shape, text, and pattern — not color alone
- CallTree: dashed border (not-yet-called), thick border (executing), icons (…/▶/✓)
- ControlFlow: back edges shown as dashed lines
- Both light and dark themes must maintain sufficient contrast ratios
- SVG views should carry
role="img"andaria-label
- Each view must implement the
init / update / reset / destroycommon interface - Adding or removing a view must not require modifying the app core (only
ViewSwitcher.register()) TraceBuilderaggregation methods must be idempotent and cached (same result each call)
| Item | Specification |
|---|---|
| Hosting | GitHub Pages (https://tntetsu.github.io/JSVisualizer/) |
| Deploy trigger | Push to main branch or manual workflow_dispatch |
| CI pipeline | ① Clone JSInterpreter → ② npm ci → ③ npm test (71 tests) → ④ npm run build → ⑤ Upload to GitHub Pages |
| Build artifacts | web/ directory (app.bundle.js / interpreter.bundle.js / index.html / style.css) |
| Concurrency | concurrency: pages limits to one active deploy at a time (cancels previous) |
| Term | Definition |
|---|---|
| TraceEvent | Information for one step of program execution: which line/column, what statement/expression ran, and what value was resolved |
| cursor | Integer index into the trace array indicating the current position |
| humanStep | A "meaningful change point" a human would record when tracing on paper: assignments, condition tests, loop updates, function calls, etc. |
| humanStep index (hi) | Subscript into the array returned by getHumanStepList() (0-based); used as the X-axis in the Lifetime chart |
| snapshot | A copy of variables, scopes, and call stack at a given step |
| diff / changedVars | The set of variable names that changed between two consecutive snapshots |
| omniscient debugging | A debugging approach where the program runs to completion first and all steps are recorded, allowing navigation to any step afterward |
| expression highlight | Highlighting the character range of the expression being evaluated in orange (semi-transparent) |
| call-site highlight | While inside a function, highlighting the call expression that invoked it in purple |
| FOUC | Flash of Unstyled Content — a momentary flash of default styles on page load; prevented by an inline script in <head> |
| jsv-theme | localStorage key that persists the theme setting; value "dark" applies the dark theme |
| jsv-active-tab | localStorage key that persists the active tab; value is the view's registration ID string |
| jsv-editor-pct | localStorage key that persists the editor pane width (%); managed by PaneResizer; clamped to 15–75 |
| jsv-console-h | localStorage key that persists the console panel height (px); managed by app.js; clamped to 40–400 |
| error badge | A small label that visually identifies the error type — either "Syntax Error" or "Runtime Error" |
| jsv-lang | localStorage key that persists the display language; value is 'ja' (Japanese) or 'en' (English); defaults to 'ja' |