Repository navigation
Expand file tree
/
Copy pathworkspace.html
More file actions
727 lines (694 loc) · 42.6 KB
/
Copy pathworkspace.html
File metadata and controls
727 lines (694 loc) · 42.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
<!doctype html>
<!--
The Python programming workspace has its own document so learners can bookmark it,
reload it, and return to their saved source without revisiting the landing page.
-->
<html lang="en">
<head>
<!-- UTF-8 safely supports the interface's arrows, symbols, and status glyphs. -->
<meta charset="UTF-8" />
<!-- The viewport declaration makes the responsive layout match the device width. -->
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- This description identifies the page for search results and shared links. -->
<meta
name="description"
content="Edit and visualize beginner Python programs one execution step at a time."
/>
<!-- This tells native browser controls that the page supports both color themes. -->
<meta name="color-scheme" content="dark light" />
<!-- The page-specific title distinguishes the workspace in tabs and bookmarks. -->
<title>Python Programming Workspace | Code Explorer</title>
<!-- The shared terminal mark identifies the project in the browser tab. -->
<link rel="icon" href="favicon.svg" type="image/svg+xml" />
<!-- These early connections reduce the wait for the editor and Python runtime. -->
<link rel="preconnect" href="https://esm.sh" />
<link rel="preconnect" href="https://cdn.jsdelivr.net" />
<!-- Both pages share one stylesheet so their visual language stays consistent. -->
<!--
The version query forces browsers and GitHub Pages caches to fetch the
stylesheet that matches this document instead of reusing an older layout.
-->
<link rel="stylesheet" href="styles.css?v=20260728-35" />
</head>
<body>
<!-- This decorative background is ignored by assistive technology. -->
<div class="ambient-grid" aria-hidden="true"></div>
<!-- The shared header keeps identity, runtime status, theme, and repository access visible. -->
<header class="site-header">
<!-- The brand provides a standard link back to the landing page. -->
<a class="brand" href="index.html" aria-label="Code Explorer home">
<span class="brand-mark" aria-hidden="true">>_</span>
<span>CODE EXPLORER</span>
</a>
<!-- JavaScript updates the runtime status and selected theme in this group. -->
<div class="header-actions">
<!-- Text accompanies the status dot so color is never the only signal. -->
<span class="runtime-status" id="runtimeStatus">
<span class="status-dot"></span>
<span id="runtimeLabel">Runtime offline</span>
</span>
<!-- The named theme action remains understandable without requiring familiarity with an icon. -->
<button class="icon-button theme-button" id="themeButton" type="button" aria-label="Switch to dark mode">
<span id="themeLabel">Dark mode</span>
</button>
<!-- The guide remains one click away while a beginner explores the feature-rich workspace. -->
<a
class="icon-button guide-button"
href="https://github.com/amanalip/code-explorer/blob/main/README.md"
target="_blank"
rel="noreferrer"
aria-label="Open the Code Explorer tool guide in a new tab"
>
Tool Guide
</a>
<!-- Contributors can open the public source repository in a separate tab. -->
<a class="icon-button" href="https://github.com/amanalip/code-explorer" target="_blank" rel="noreferrer" aria-label="Open Code Explorer on GitHub">
<!-- The GitHub mark makes the repository destination recognizable without adding another text label to the header. -->
<svg class="github-icon" viewBox="0 0 16 16" aria-hidden="true" focusable="false">
<!-- Filling with the inherited control color keeps one vector asset compatible with light and dark palettes. -->
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82A7.65 7.65 0 0 1 8 4.58c.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8Z" />
</svg>
</a>
</div>
</header>
<!-- The main region contains the complete editor and visualization workspace. -->
<main>
<!-- This screen is visible immediately because it is now a dedicated page. -->
<section class="workspace" id="workspace" aria-label="Python visualizer workspace">
<!-- This heading names the Python learning path, explains its purpose, and supplies the top-level actions. -->
<div class="workspace-heading">
<div>
<!-- A real link preserves expected browser behavior and works without JavaScript. -->
<a class="back-button" id="backButton" href="index.html">← Home</a>
<h1>Python Programming workspace</h1>
<!-- The short description helps a first-time learner understand the workspace before selecting an action. -->
<p class="workspace-heading-copy">
Write, run, and replay Python programs while following variables, decisions,
loops, functions, objects, and output step by step.
</p>
</div>
<!-- Run and Stop are swapped by JavaScript while Python is executing. -->
<div class="workspace-actions">
<button class="text-button" id="examplesButton" type="button">Examples</button>
<!-- The generated-comment preview becomes available only after a trace supplies trustworthy runtime evidence. -->
<button class="text-button learning-comments-button" id="learningCommentsButton" type="button" disabled>
Learning comments
</button>
<button class="primary-button compact" id="runButton" type="button">
<span class="button-ready">Run trace</span>
<span class="button-loading hidden">Tracing...</span>
</button>
<button class="danger-button hidden" id="stopButton" type="button">Stop</button>
</div>
</div>
<!-- CSS Grid arranges the editor, story, controls, and console responsively. -->
<div class="workspace-grid">
<!-- The editor panel contains CodeMirror or the native textarea fallback. -->
<section class="panel editor-panel" id="editorPanel">
<!-- A numbered panel header makes the interface resemble an instrument console. -->
<div class="panel-header">
<div>
<span class="panel-index">01</span>
<span class="panel-title">PYTHON SOURCE</span>
</div>
<!-- These compact controls change only editor presentation, never the Python source. -->
<div class="editor-header-tools">
<!-- aria-pressed exposes the current wrapping state to assistive technology. -->
<button class="editor-tool-button" id="editorWrapButton" type="button" aria-pressed="true">
Wrap on
</button>
<!--
This non-destructive view toggle displays trace-backed comment widgets
without inserting lines into the learner's saved Python document.
-->
<button
class="editor-tool-button automatic-comments-toggle"
id="editorAutomaticCommentsButton"
type="button"
aria-pressed="false"
disabled
>
Automatic comments off
</button>
<!-- A visible label makes the numeric choices understandable without relying on context. -->
<label class="editor-font-control" for="editorFontSizeSelect">
<span>Font</span>
<select id="editorFontSizeSelect" aria-label="Editor font size">
<option value="12">12 px</option>
<option value="14" selected>14 px</option>
<option value="16">16 px</option>
<option value="18">18 px</option>
<option value="20">20 px</option>
<option value="22">22 px</option>
</select>
</label>
<!-- Copy exports the entire Python document regardless of the current selection. -->
<button class="editor-tool-button editor-clipboard-button" id="editorCopyButton" type="button">
Copy
</button>
<!-- Paste replaces the entire document through the same safe editor update path as examples. -->
<button class="editor-tool-button editor-clipboard-button" id="editorPasteButton" type="button">
Paste
</button>
<!-- The filename remains visible as orientation for the single-file MVP. -->
<span class="file-label">main.py</span>
</div>
</div>
<!--
A reviewed example selection places its learning question directly
above the editor. JavaScript hides this region for pasted or
independently created source and always inserts text safely.
-->
<section
class="selected-program-question hidden"
id="selectedProgramQuestion"
aria-labelledby="selectedProgramQuestionTitle"
>
<span>Selected question</span>
<strong id="selectedProgramQuestionTitle"></strong>
<p id="selectedProgramQuestionDescription"></p>
</section>
<!-- CodeMirror mounts itself into this empty element after its modules load. -->
<div class="editor-shell" id="editorShell">
<div id="editor" aria-label="Python code editor"></div>
</div>
<!-- Live source statistics confirm the program size and selected language. -->
<div class="editor-footer">
<span id="codeStats">0 lines</span>
<span>Python 3</span>
</div>
</section>
<!-- This panel switches among complementary beginner-focused execution views. -->
<section class="panel story-panel">
<!-- Four stable learning areas prevent the growing toolset from becoming one overwhelming tab row. -->
<nav class="learning-mode-nav" aria-label="Learning area">
<button class="learning-mode active" data-mode="trace" type="button">Trace</button>
<button class="learning-mode" data-mode="data" type="button">Data</button>
<button class="learning-mode" data-mode="flow" type="button">Flow</button>
<button class="learning-mode" data-mode="labs" type="button">Labs</button>
</nav>
<!-- Accessible tabs select a view without navigating away from the workspace. -->
<div class="panel-header tabs-header">
<div class="panel-tabs" role="tablist" aria-label="Visualization mode">
<button class="panel-tab active" id="storyTab" data-mode="trace" role="tab" aria-selected="true" type="button">
Story
</button>
<button class="panel-tab" id="beforeAfterTab" data-mode="trace" role="tab" aria-selected="false" type="button">
Before and After
</button>
<button class="panel-tab" id="conditionsTab" data-mode="trace" role="tab" aria-selected="false" type="button">
Conditions
</button>
<button class="panel-tab" id="functionsTab" data-mode="trace" role="tab" aria-selected="false" type="button">
Function Journey
</button>
<button class="panel-tab" id="errorTab" data-mode="trace" role="tab" aria-selected="false" type="button">
Error Coach
</button>
<button class="panel-tab mode-hidden" id="variablesTab" data-mode="data" role="tab" aria-selected="false" type="button">
Variables
</button>
<button class="panel-tab mode-hidden" id="watchesTab" data-mode="data" role="tab" aria-selected="false" type="button">
Watches <span class="tab-badge" id="watchBadge">0</span>
</button>
<button class="panel-tab mode-hidden" id="structuresTab" data-mode="data" role="tab" aria-selected="false" type="button">
Structures
</button>
<button class="panel-tab mode-hidden" id="referencesTab" data-mode="data" role="tab" aria-selected="false" type="button">
References
</button>
<button class="panel-tab mode-hidden" id="mutationTab" data-mode="data" role="tab" aria-selected="false" type="button">
Mutation Explorer
</button>
<button class="panel-tab mode-hidden" id="flowTab" data-mode="flow" role="tab" aria-selected="false" type="button">
Execution Path
</button>
<button class="panel-tab mode-hidden" id="coverageTab" data-mode="flow" role="tab" aria-selected="false" type="button">
Coverage
</button>
<button class="panel-tab mode-hidden" id="loopTableTab" data-mode="flow" role="tab" aria-selected="false" type="button">
Loop Table
</button>
<button class="panel-tab mode-hidden" id="loopTab" data-mode="flow" role="tab" aria-selected="false" type="button">
Loop Lab <span class="tab-badge" id="loopBadge">0</span>
</button>
<button class="panel-tab mode-hidden" id="inputTab" data-mode="labs" role="tab" aria-selected="false" type="button">
Input Playground
</button>
<button class="panel-tab mode-hidden" id="compareTab" data-mode="labs" role="tab" aria-selected="false" type="button">
Compare Runs
</button>
<button class="panel-tab mode-hidden" id="bookmarksTab" data-mode="labs" role="tab" aria-selected="false" type="button">
Trace Bookmarks <span class="tab-badge" id="bookmarkBadge">0</span>
</button>
</div>
<span class="step-count" id="stepCount">STEP 00 / 00</span>
</div>
<!-- The execution story explains the currently selected trace snapshot. -->
<div class="story-view" id="storyView" role="tabpanel">
<!-- This placeholder remains visible until a successful trace is available. -->
<div class="empty-state" id="emptyStory">
<div class="empty-glyph" aria-hidden="true">{ }</div>
<h2>Ready for a trace</h2>
<p>Run the program to turn each executed line into a visual story.</p>
</div>
<!-- JavaScript fills these regions from the active Python trace step. -->
<div class="trace-content hidden" id="traceContent">
<div class="trace-kicker" id="traceKicker">LINE 01 // ASSIGNMENT</div>
<div class="executed-code"><code id="executedCode"></code></div>
<p class="explanation" id="explanation"></p>
<!-- This card isolates only the console text added by the current step. -->
<div class="step-output hidden" id="stepOutputSection">
<div class="section-label">OUTPUT FROM THIS STEP</div>
<pre id="stepOutput"></pre>
</div>
<!-- A diff between adjacent snapshots explains what this step changed. -->
<div class="change-section">
<div class="section-label">WHAT CHANGED</div>
<div class="change-list" id="changeList"></div>
</div>
<!-- Current local and global names are rendered as compact variable cards. -->
<div class="variables-section">
<div class="section-label">VARIABLES IN SCOPE</div>
<div class="variables-grid" id="variablesGrid"></div>
</div>
<!-- Nested function frames appear here when calls enter user code. -->
<div class="call-stack-section" id="callStackSection">
<div class="section-label">CALL STACK</div>
<div class="call-stack" id="callStack"></div>
</div>
</div>
</div>
<!-- Before and After receives a dedicated view so it is discoverable instead of being buried inside Story. -->
<div class="learning-view hidden" id="beforeAfterView" role="tabpanel">
<div class="empty-state" id="emptyBeforeAfter">
<div class="empty-glyph" aria-hidden="true">A/B</div>
<h2>No state comparison yet</h2>
<p>Run a program to compare visible values before and after every instruction.</p>
</div>
<div class="learning-detail hidden" id="beforeAfterContent">
<section class="learning-card before-after-card" id="beforeAfterCard">
<div class="learning-card-heading">
<span class="section-label">BEFORE AND AFTER</span>
<span class="context-badge" id="changeSummary">No changes</span>
</div>
<div class="before-after-grid" id="beforeAfterGrid"></div>
</section>
</div>
</div>
<!-- The condition view reports observed branch behavior without reevaluating learner expressions. -->
<div class="learning-view hidden" id="conditionsView" role="tabpanel">
<div class="empty-state" id="emptyConditions">
<div class="empty-glyph" aria-hidden="true">?</div>
<h2>No condition selected</h2>
<p>Move to an <code>if</code>, <code>elif</code>, or <code>while</code> step to investigate its result.</p>
</div>
<div class="learning-detail hidden" id="conditionsContent"></div>
</div>
<!-- Function Journey connects caller state, parameter binding, local work, and returns. -->
<div class="learning-view hidden" id="functionsView" role="tabpanel">
<div class="empty-state" id="emptyFunctions">
<div class="empty-glyph" aria-hidden="true">ƒ</div>
<h2>No function journey yet</h2>
<p>Run a program with a function call, then step into that function.</p>
</div>
<div class="learning-detail hidden" id="functionsContent"></div>
</div>
<!-- Error Coach is a persistent destination with a calm empty state until a failure occurs. -->
<div class="learning-view hidden" id="errorView" role="tabpanel">
<div class="empty-state" id="emptyErrorCoach">
<div class="empty-glyph" aria-hidden="true">!</div>
<h2>No error to explain</h2>
<p>When Python stops, this view will explain the message and show a practical next check.</p>
</div>
<div class="learning-detail hidden" id="errorContent">
<section class="learning-card error-coach" id="errorCoach">
<div class="section-label">BEGINNER ERROR COACH</div>
<h3 id="errorCoachTitle"></h3>
<p id="errorCoachMeaning"></p>
<p class="coach-tip" id="errorCoachTip"></p>
</section>
</div>
</div>
<!-- The variable view exposes scope, nested contents, and value history. -->
<div class="variables-view hidden" id="variablesView" role="tabpanel">
<!-- This placeholder is replaced after a trace supplies visible variables. -->
<div class="empty-state" id="emptyVariables">
<div class="empty-glyph" aria-hidden="true">x</div>
<h2>No variables yet</h2>
<p>Run a program, then select any variable to inspect its value and history.</p>
</div>
<!-- JavaScript builds scope groups and a detailed inspector inside this region. -->
<div class="variables-content hidden" id="variablesContent">
<div class="scope-browser" id="scopeBrowser"></div>
<div class="variable-inspector" id="variableInspector"></div>
</div>
</div>
<!-- Watches has its own persistent dashboard while the Variables view remains focused on inspection. -->
<div class="learning-view hidden" id="watchesView" role="tabpanel">
<div class="empty-state" id="emptyWatches">
<div class="empty-glyph" aria-hidden="true">◎</div>
<h2>No watched variables</h2>
<p>Open Variables, choose a name, and select Watch to pin it here.</p>
</div>
<div class="learning-detail hidden" id="watchesContent"></div>
</div>
<!-- Structure cards show indexes, keys, mutations, aliases, and object replacement. -->
<div class="learning-view hidden" id="structuresView" role="tabpanel">
<div class="empty-state" id="emptyStructures">
<div class="empty-glyph" aria-hidden="true">[ ]</div>
<h2>No structure selected</h2>
<p>Run a program containing a list, tuple, dictionary, set, or string.</p>
</div>
<div class="learning-detail hidden" id="structuresContent"></div>
</div>
<!-- Mutation Explorer distinguishes same-object changes from name reassignment. -->
<div class="learning-view hidden" id="mutationView" role="tabpanel">
<div class="empty-state" id="emptyMutation">
<div class="empty-glyph" aria-hidden="true">↺</div>
<h2>No object change yet</h2>
<p>Run a program that mutates or reassigns a list, dictionary, or set.</p>
</div>
<div class="learning-detail hidden" id="mutationContent"></div>
</div>
<!-- The reference view teaches names, scopes, objects, and shared references. -->
<div class="references-view hidden" id="referencesView" role="tabpanel">
<div class="empty-state" id="emptyReferences">
<div class="empty-glyph" aria-hidden="true">-></div>
<h2>No references yet</h2>
<p>Run a program to map variable names to the Python objects they reference.</p>
</div>
<!-- Cytoscape mounts its interactive canvas into this dedicated container. -->
<div class="graph-content hidden" id="referencesContent">
<div class="graph-toolbar">
<!-- The compact label and explanation establish the map's teaching purpose. -->
<span class="graph-toolbar-copy">
<span class="graph-toolbar-label">REFERENCE MAP</span>
<span>Names reference objects. This is a conceptual map, not a RAM address view.</span>
</span>
<!-- Every zoom control stays synchronized with Cytoscape's wheel and trackpad zoom. -->
<div class="graph-zoom-controls" role="group" aria-label="Reference map zoom controls">
<!-- The minus button changes zoom by five percent while the slider offers one-percent precision. -->
<button class="graph-zoom-step" id="decreaseReferencesZoomButton" type="button" aria-label="Zoom reference map out">−</button>
<!-- A native range input supplies keyboard support and granular pointer control. -->
<input class="graph-zoom-slider" id="referencesZoomSlider" type="range" min="40" max="200" step="1" value="100" aria-label="Reference map zoom percentage" />
<!-- The output exposes the exact current magnification in a stable-width label. -->
<output class="graph-zoom-value" id="referencesZoomValue" for="referencesZoomSlider">100%</output>
<!-- The plus button offers a quick alternative to dragging the slider. -->
<button class="graph-zoom-step" id="increaseReferencesZoomButton" type="button" aria-label="Zoom reference map in">+</button>
<!-- Fit remains available as a content-aware reset and now updates the slider. -->
<button class="graph-fit-button" id="fitReferencesButton" type="button">Fit</button>
</div>
</div>
<div class="graph-canvas" id="referencesGraph" aria-label="Python reference map"></div>
</div>
</div>
<!-- Coverage separates reached, repeated, missed, and failing source lines. -->
<div class="learning-view hidden" id="coverageView" role="tabpanel">
<div class="empty-state" id="emptyCoverage">
<div class="empty-glyph" aria-hidden="true">%</div>
<h2>No coverage yet</h2>
<p>Run the program to see which executable lines Python reached.</p>
</div>
<div class="learning-detail hidden" id="coverageContent"></div>
</div>
<!-- The flow view summarizes the path the completed execution actually followed. -->
<div class="flow-view hidden" id="flowView" role="tabpanel">
<div class="empty-state" id="emptyFlow">
<div class="empty-glyph loop-glyph" aria-hidden="true">#</div>
<h2>No executed path yet</h2>
<p>Run a program to turn its observed line transitions into a flow map.</p>
</div>
<!-- The graph uses observed transitions, making every displayed edge truthful. -->
<div class="graph-content hidden" id="flowContent">
<div class="graph-toolbar">
<!-- The compact label distinguishes observed flow from static source analysis. -->
<span class="graph-toolbar-copy">
<span class="graph-toolbar-label">EXECUTED PATH</span>
<span>Edge labels show how often a transition occurred.</span>
</span>
<!-- Flow has independent zoom controls so each graph remembers its own useful scale. -->
<div class="graph-zoom-controls" role="group" aria-label="Executed path zoom controls">
<!-- Matching ids let JavaScript connect this button specifically to the Flow graph. -->
<button class="graph-zoom-step" id="decreaseFlowZoomButton" type="button" aria-label="Zoom executed path out">−</button>
<!-- Forty percent reveals larger programs, while two hundred percent supports close inspection. -->
<input class="graph-zoom-slider" id="flowZoomSlider" type="range" min="40" max="200" step="1" value="100" aria-label="Executed path zoom percentage" />
<!-- The live percentage changes for slider, button, Fit, wheel, and trackpad input. -->
<output class="graph-zoom-value" id="flowZoomValue" for="flowZoomSlider">100%</output>
<!-- The plus action advances five percent for a faster alternative to fine slider changes. -->
<button class="graph-zoom-step" id="increaseFlowZoomButton" type="button" aria-label="Zoom executed path in">+</button>
<!-- Fit uses the graph bounds but never leaves the percentage display stale. -->
<button class="graph-fit-button" id="fitFlowButton" type="button">Fit</button>
</div>
</div>
<div class="graph-canvas" id="flowGraph" aria-label="Executed Python flow map"></div>
</div>
</div>
<!-- Loop Lab presents the same trace through an iteration-focused lens. -->
<div class="loop-view hidden" id="loopView" role="tabpanel">
<!-- This placeholder explains why no iteration data is currently shown. -->
<div class="empty-state" id="emptyLoop">
<div class="empty-glyph loop-glyph" aria-hidden="true">↻</div>
<h2>No loop detected yet</h2>
<p>Run code containing a simple <code>for</code> or <code>while</code> loop.</p>
</div>
<!-- The populated view summarizes the loop and every observed iteration. -->
<div class="loop-content hidden" id="loopContent">
<div class="loop-summary">
<span class="loop-type" id="loopType">FOR LOOP</span>
<span class="iteration-count" id="iterationCount">ITERATION 01</span>
</div>
<code class="loop-source" id="loopSource"></code>
<div class="loop-meter"><span id="loopMeter"></span></div>
<div class="section-label">ITERATION HISTORY</div>
<div class="iteration-list" id="iterationList"></div>
</div>
</div>
<!-- Loop Table receives its own comparison-focused view apart from Loop Lab animation. -->
<div class="learning-view hidden" id="loopTableView" role="tabpanel">
<div class="empty-state" id="emptyLoopTable">
<div class="empty-glyph loop-glyph" aria-hidden="true">▦</div>
<h2>No loop table yet</h2>
<p>Run code containing a <code>for</code> or <code>while</code> loop.</p>
</div>
<div class="learning-detail hidden" id="loopTableContent">
<div class="section-label">LOOP TABLE</div>
<div class="loop-table-wrap"><table class="loop-table" id="loopTable"></table></div>
</div>
</div>
<!-- Input Playground owns controlled stdin values without sharing space with comparison. -->
<div class="learning-view hidden" id="inputView" role="tabpanel">
<div class="learning-detail">
<section class="learning-card scenario-card">
<div class="section-label">INPUT PLAYGROUND</div>
<p>Enter one response per line. Python consumes them from top to bottom whenever <code>input()</code> runs.</p>
<textarea id="programInputs" rows="9" spellcheck="false" placeholder="Aman 25"></textarea>
<div class="input-status" id="inputStatus">No input values prepared.</div>
</section>
</div>
</div>
<!-- Compare Runs owns the two bounded trace snapshots and their first behavioral difference. -->
<div class="learning-view hidden" id="compareView" role="tabpanel">
<div class="learning-detail">
<section class="learning-card scenario-card">
<div class="learning-card-heading">
<span class="section-label">COMPARE TWO RUNS</span>
<button class="small-action" id="clearComparisonsButton" type="button">Clear</button>
</div>
<p>Capture the current trace as Run A or Run B, then inspect their first difference.</p>
<div class="comparison-actions">
<button class="text-button" id="captureRunAButton" type="button">Capture Run A</button>
<button class="text-button" id="captureRunBButton" type="button">Capture Run B</button>
</div>
<div class="comparison-result" id="comparisonResult">Run A and Run B are waiting.</div>
</section>
</div>
</div>
<!-- Bookmarks turn a long trace into a learner-authored table of contents. -->
<div class="learning-view hidden" id="bookmarksView" role="tabpanel">
<div class="empty-state" id="emptyBookmarks">
<div class="empty-glyph" aria-hidden="true">★</div>
<h2>No bookmarks yet</h2>
<p>Use the star beside the playback controls to save an important trace moment.</p>
</div>
<div class="learning-detail hidden" id="bookmarksContent"></div>
</div>
</section>
<!-- Playback controls navigate recorded snapshots without rerunning Python. -->
<section class="panel controls-panel">
<!-- These buttons provide discrete and automatic trace navigation. -->
<div class="playback-controls">
<button class="control-button" id="previousButton" type="button" disabled aria-label="Previous step">←</button>
<button class="play-button" id="playButton" type="button" disabled aria-label="Play trace">▶</button>
<button class="control-button" id="nextButton" type="button" disabled aria-label="Next step">→</button>
<button class="control-button restart-button" id="restartButton" type="button" disabled aria-label="Restart trace">↺</button>
<!-- A bookmark records the current trace position without changing execution. -->
<button class="control-button bookmark-button" id="bookmarkButton" type="button" disabled aria-label="Bookmark current step" aria-pressed="false">☆</button>
</div>
<!-- The range input supports direct movement through longer traces. -->
<div class="progress-area">
<input id="stepSlider" type="range" min="0" max="0" value="0" disabled aria-label="Execution step" />
<div class="progress-labels"><span>START</span><span id="progressPercent">0%</span><span>END</span></div>
</div>
<!-- Playback speed changes only the interface timer, never Python execution. -->
<label class="speed-control">
SPEED
<select id="speedSelect">
<option value="1200">0.5x</option>
<option value="750" selected>1x</option>
<option value="400">2x</option>
</select>
</label>
</section>
<!-- Console output stays synchronized with the selected execution snapshot. -->
<section class="panel output-panel">
<div class="panel-header">
<div>
<span class="panel-index">02</span>
<span class="panel-title">CONSOLE OUTPUT</span>
</div>
<button class="clear-output" id="clearOutputButton" type="button">Clear</button>
</div>
<pre class="console" id="consoleOutput"><span class="console-muted">// Output will appear here</span></pre>
</section>
</div>
</section>
</main>
<!-- The same ownership footer appears after the complete execution workspace. -->
<footer class="site-footer">
<!-- Keeping the copyright text concise prevents it from distracting from trace results. -->
<small>© 2026 Aman Ali Pogaku</small>
</footer>
<!-- The native dialog contains data-driven starter programs created by app.js. -->
<dialog class="examples-dialog" id="examplesDialog">
<div class="dialog-header">
<div>
<span class="eyebrow"><span></span> Starter programs</span>
<h2>Explore Python through 134 programs</h2>
<!-- The subtitle explains that ordering is guidance, not recorded progress. -->
<p class="dialog-intro">Follow the recommended order or open any program directly.</p>
</div>
<button class="dialog-close" id="closeExamplesButton" type="button" aria-label="Close examples">×</button>
</div>
<!--
The curriculum explorer separates route navigation, program selection,
and full lesson details. A learner can inspect a program without
replacing the editor until the explicit preview action is selected.
-->
<div class="example-browser-body" id="exampleBrowserBody">
<!-- Category navigation stays visible while learners browse a long filtered result list. -->
<aside class="example-browser-toolbar" aria-label="Starter program categories">
<!--
Search remains local and temporary. It combines with the selected
category and examines reviewed program information, metadata, and source.
-->
<div class="example-search">
<label for="exampleSearchInput">Search programs</label>
<input
id="exampleSearchInput"
type="search"
placeholder="Title, concept, view, or code"
autocomplete="off"
spellcheck="false"
aria-describedby="exampleSearchHelp"
/>
<span id="exampleSearchHelp">Use more words to narrow the results.</span>
</div>
<!-- This static label describes the curriculum order without implying completion tracking. -->
<span class="example-curriculum-label">Recommended order</span>
<!-- The generated vertical buttons expose names, counts, and their pressed state. -->
<div class="example-filters" id="exampleFilters" role="group" aria-label="Filter starter programs"></div>
</aside>
<!--
The middle pane is a compact, vertical program index. Selecting one
row updates the preview but does not alter learner source.
-->
<section class="example-results-pane" aria-label="Python curriculum programs">
<div class="example-results-header">
<div>
<span>Program library</span>
<strong>Choose a lesson to inspect</strong>
</div>
<!-- This live count reports the intersection of search and category. -->
<span class="example-count" id="exampleCount" role="status" aria-live="polite"></span>
</div>
<div class="example-grid" id="exampleGrid"></div>
</section>
<!--
The detail pane receives safe text nodes from app.js. Its Open action
is the only catalog control that replaces the complete editor.
-->
<aside class="example-preview" id="examplePreview" aria-label="Selected Python program preview" aria-live="polite"></aside>
</div>
</dialog>
<!--
Automatic Learning Comments always open in a separate preview. The dialog
keeps generated text reversible and prevents a trace explanation from
silently replacing the learner's original Python source.
-->
<dialog class="learning-comments-dialog" id="learningCommentsDialog">
<!-- The header identifies generated content and provides a conventional close action. -->
<div class="dialog-header learning-comments-header">
<div>
<span class="eyebrow"><span></span> Trace-powered study copy</span>
<div class="learning-comments-title-row">
<h2>Automatic Learning Comments</h2>
<!-- The badge states that this IDE-like surface cannot alter learner source. -->
<span class="learning-comments-readonly">Read only</span>
</div>
</div>
<button class="dialog-close" id="closeLearningCommentsButton" type="button" aria-label="Close learning comments">×</button>
</div>
<!-- Controls adjust explanation density without rerunning or editing Python. -->
<div class="learning-comments-toolbar">
<label for="learningCommentDetail">
<span>Comment detail</span>
<select id="learningCommentDetail">
<option value="essential">Essential</option>
<option value="guided" selected>Guided</option>
<option value="detailed">Detailed</option>
</select>
</label>
<div class="learning-comments-toolbar-copy">
<!-- A compact legend separates generated evidence from original Python before reading begins. -->
<span class="learning-comments-legend" aria-label="Preview legend">
<span><i class="learning-legend-note"></i> Trace note</span>
<span><i class="learning-legend-source"></i> Python source</span>
</span>
<p id="learningCommentsSummary">Run a trace to generate conservative comments from syntax and observed behavior.</p>
</div>
</div>
<!-- The IDE shell is read-only presentation. JavaScript still inserts only safe text nodes. -->
<section class="learning-comments-editor" aria-label="Read-only commented Python study editor">
<!-- Familiar editor chrome gives the long preview a clear file and mode context. -->
<div class="learning-comments-editor-chrome" aria-hidden="true">
<span class="learning-editor-lights"><i></i><i></i><i></i></span>
<span class="learning-editor-tab"><b>PY</b> main.py <em>with trace notes</em></span>
<span class="learning-editor-mode">Study view</span>
</div>
<!-- Rows receive line numbers and syntax colors without changing the copied document. -->
<div class="learning-comments-preview" id="learningCommentsPreview" tabindex="0" role="region" aria-label="Commented Python preview"></div>
<!-- The status bar mirrors useful IDE metadata without pretending the preview is editable. -->
<div class="learning-comments-status" aria-hidden="true">
<span>Python 3</span>
<span id="learningCommentsLineCount">0 lines</span>
<span>UTF-8</span>
<span>LF</span>
<span>Trace notes visible</span>
</div>
</section>
<!-- Actions keep the original safe by default and clearly separate copying from replacement. -->
<div class="learning-comments-actions">
<p><strong>Your original stays unchanged.</strong> Replacing the editor requires one additional confirmation.</p>
<div>
<button class="secondary-button" id="copyLearningCommentsButton" type="button">Copy commented code</button>
<button class="primary-button compact" id="replaceWithLearningCommentsButton" type="button">Replace editor</button>
</div>
</div>
</dialog>
<!-- This polite live region announces completion and errors without stealing focus. -->
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<!-- The version query keeps this page synchronized with the landing page's navigation code. -->
<script type="module" src="app.js?v=20260728-35"></script>
</body>
</html>