diff --git a/tests/test_web_driver.py b/tests/test_web_driver.py index 688cfdd..aa5b629 100644 --- a/tests/test_web_driver.py +++ b/tests/test_web_driver.py @@ -16,21 +16,25 @@ class WebDriverTests(unittest.TestCase): def test_view_tokens_includes_token_names(self) -> None: rendered = driver.view_tokens("x = 1\n") - self.assertIn("NAME", rendered) - self.assertIn("NUMBER", rendered) + self.assertIn("NAME", rendered["text"]) + self.assertIn("NUMBER", rendered["text"]) + self.assertEqual(len(rendered["lines"]), len(rendered["text"].splitlines())) def test_view_ast_returns_module_dump(self) -> None: rendered = driver.view_ast("x = 1\n", optimize=False) - self.assertIn("Module(", rendered) - self.assertIn("Assign(", rendered) + self.assertIn("Module(", rendered["text"]) + self.assertIn("Assign(", rendered["text"]) + self.assertEqual(len(rendered["lines"]), len(rendered["text"].splitlines())) def test_view_pseudo_smoke(self) -> None: rendered = driver.view_pseudo("def f(x):\n return x\n\nprint(f(42))\n") - self.assertIn("LOAD_CONST", rendered) + self.assertIn("LOAD_CONST", rendered["text"]) + self.assertEqual(len(rendered["lines"]), len(rendered["text"].splitlines())) def test_view_compiled_smoke(self) -> None: rendered = driver.view_compiled("x = 1\n") - self.assertIn("LOAD_CONST", rendered) + self.assertIn("LOAD_CONST", rendered["text"]) + self.assertEqual(len(rendered["lines"]), len(rendered["text"].splitlines())) def test_instruction_items_supports_list_and_get_instructions(self) -> None: inst = dis.Instruction( diff --git a/web/index.html b/web/index.html index a23b275..6791b58 100644 --- a/web/index.html +++ b/web/index.html @@ -83,8 +83,22 @@ padding: 14px; display: grid; gap: 14px; - grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); - grid-auto-rows: minmax(320px, 1fr); + grid-template-columns: repeat(3, minmax(320px, 1fr)); + grid-auto-rows: minmax(320px, auto); + } + @media (max-width: 1200px) { + main { + grid-template-columns: repeat(2, minmax(320px, 1fr)); + } + .panel.source-panel { grid-column: span 1; } + .panel.run-panel { grid-column: span 1; } + } + @media (max-width: 860px) { + main { + grid-template-columns: minmax(320px, 1fr); + } + .panel.source-panel { grid-column: 1 / -1; } + .panel.run-panel { grid-column: 1 / -1; } } .panel { display: flex; @@ -96,6 +110,7 @@ min-height: 320px; } .panel.hidden { display: none; } + .panel.source-panel { grid-column: span 2; } .panel > h2 { margin: 0; padding: 8px 12px; @@ -108,6 +123,31 @@ letter-spacing: 0.04em; text-transform: uppercase; } + .panel-titlebar { + margin: 0; + padding: 8px 12px; + display: flex; + align-items: center; + justify-content: flex-start; + gap: 10px; + background: #1b212a; + border-bottom: 1px solid var(--panel-border); + letter-spacing: 0.04em; + text-transform: uppercase; + } + .panel-titlebar h2 { + margin: 0; + font-size: 12px; + font-weight: 600; + color: #f0f4f8; + } + .run-controls { + display: flex; + flex: 1; + align-items: flex-start; + justify-content: center; + padding: 12px; + } .panel > .content { flex: 1; overflow: auto; @@ -142,6 +182,9 @@ min-height: 0; } .ace_editor { font-size: 12px !important; } + .source-panel #editor { + min-height: 360px; + } .banner { padding: 8px 16px; color: var(--error); @@ -167,7 +210,6 @@