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
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) · Versioning:

## [Unreleased]

## [0.6.2] - 2026-07-21

### Fixed
- **The first-run audience question is visible on a fresh narrow window.**
v0.6.1 moved the audience control into the compact Menu, but its native modal
stayed inside that closed disclosure. The browser opened an invisible modal
backdrop that blocked the page. The gate now portals directly to the document
top layer while the post-choice Easy/Expert toggle remains in the Menu.

## [0.6.1] - 2026-07-21

### Fixed
Expand Down
8 changes: 7 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,12 @@ Polish, then the coordinated launch (Show HN / X; lit-galaxy GIF as hero).
## Current State **[AGENT-MAINTAINED]**

**Current milestone: Phase 1 tester evidence** · Last updated: 2026-07-21 ·
Session note: v0.6.1 closes the four verified user-flow gaps from the
Session note: v0.6.2 is the immediate installed-artifact hotfix for v0.6.1:
the first-run audience modal now portals to the document top layer, so the
closed compact Menu cannot hide the dialog while its modal backdrop blocks a
fresh narrow window. The public PyPI smoke caught the containment defect after
v0.6.1 publication; v0.6.2 supersedes it rather than pretending an immutable
artifact can be replaced. v0.6.1 closes the four verified user-flow gaps from the
2026-07-21 usability audit: the 320 px shell reserves a usable map/study stage,
Modules and Find render from the Star chart, required Home calibration finishes
before coach marks begin, and parser-owned 3D nodes no longer install drag
Expand Down Expand Up @@ -542,6 +547,7 @@ shows lower repeated-commit work without changing derived values.
| 2026-07-21 | The Map gains zoom, Fit, and drag-to-pan; panning rides the container's own scroll and zoom only scales the rendered size | The 2D counterpart of bounded orbit: a 960x2640 diagram in a plain scroll box showed four of nine layers and no way to see the whole shape. Scroll-based panning keeps native scrollbars, keyboard scrolling and screen-reader behaviour intact, and because every coordinate inside the SVG stays backend-computed, React remains a pure renderer of graph-owned geometry. It opens at true size rather than auto-fitting: fitting on mount measured the scroller before layout settled and landed on a scale that was neither fitted nor honest |
| 2026-07-21 | v0.6.0 deepens five private boundaries without changing the HTTP, graph, check, persistence, or learner-visible contracts: Project Selection, Project Activation, Project Mapping Run, Name Atlas, and Learner Projection | Approved by UD as five behavior-preserving waves in one release PR. The deletion test now holds at each seam, stale activation and mapping responses lose atomically, and dependency-scoped learner projections measured ~0.331 ms → ~0.001 ms per hover commit on a synthetic 1,000-node project while preserving derived outputs |
| 2026-07-21 | v0.6.1 treats Modules and Find as global surfaces, sequences first-run decisions as audience → required Home → coach, and makes the 3D parser-owned layout explicitly non-draggable | Approved by UD as implementation of every verified user-flow audit finding. Global commands must never accept hidden state, onboarding must expose one foreground decision at a time, and learners orbit the immutable graph rather than editing its coordinates. The compact shell is a structural breakpoint of the existing Formal Edo interface, not a new visual system |
| 2026-07-21 | First-run audience modal portals to `document.body`; the persistent Easy/Expert toggle remains in responsive header chrome | A native modal inside the closed compact Menu entered the top layer but inherited `display:none` from its ancestor, leaving an invisible backdrop that blocked fresh mobile runs. Modal ownership is a document boundary, not header layout. Caught only by the clean public v0.6.1 installed-artifact smoke; PyPI immutability requires v0.6.2 rather than replacing 0.6.1 |

## Non-Goals — do NOT build (point here when asked)

Expand Down
2 changes: 1 addition & 1 deletion codemble/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Codemble — a learning game that turns the code AI wrote for you into a galaxy
you light up by understanding it. See CLAUDE.md for the agent brief and roadmap."""

__version__ = "0.6.1"
__version__ = "0.6.2"

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion codemble/web_dist/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
content="Explore the parser-proven structure of your local project with Codemble."
/>
<title>Codemble · Your code, mapped</title>
<script type="module" crossorigin src="/assets/index-0OmvUbwQ.js"></script>
<script type="module" crossorigin src="/assets/index-_4l8WmkY.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-CsUbeE2C.css">
</head>
<body>
Expand Down
2 changes: 1 addition & 1 deletion docs-site/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "codemble-docs",
"type": "module",
"version": "0.6.1",
"version": "0.6.2",
"private": true,
"scripts": {
"dev": "astro dev",
Expand Down
25 changes: 25 additions & 0 deletions docs/releases/v0.6.2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Codemble v0.6.2 — visible first-run choice on mobile

v0.6.2 is an immediate hotfix for the installed v0.6.1 artifact. The final
public-wheel smoke found that a fresh 320 px run opened the audience modal while
the dialog was still contained by the closed compact Menu. The invisible modal
backdrop blocked every control.

The audience gate now portals to the document top layer. It remains the same
required, keyboard-accessible first decision; after selection, the persistent
Easy/Expert toggle still lives in the compact Menu. No parser, graph, checks,
persistence, provider, or progress behavior changed.

## Verification

- Fresh isolated data directory at 320×800: visible audience dialog → choice →
coach marks → compact Menu
- Zero console errors and no horizontal overflow on the installed wheel
- Python tests, Ruff, web contracts/build, Astro check/build, Graphify refresh,
wheel/sdist checksum verification, CI, Pages, GitHub release assets, and
public PyPI `uvx` smoke

## Known limits

- The v0.6.1 artifact remains immutable on PyPI; v0.6.2 supersedes it as latest.
- The Phase 1 milestone still requires human tester evidence in issue #13.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "codemble"
version = "0.6.1"
version = "0.6.2"
description = "A learning game that turns the code AI wrote for you into a galaxy you light up by understanding it."
readme = "README.md"
license = "Apache-2.0"
Expand Down
4 changes: 2 additions & 2 deletions web/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions web/package.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{
"name": "codemble-web",
"private": true,
"version": "0.6.1",
"version": "0.6.2",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"check": "node scripts/check_graph_data.mjs && node scripts/check_name_atlas.mjs && node scripts/check_project_mapping.mjs && node scripts/check_learner_projection.mjs && node scripts/check_learner_session.mjs && node scripts/check_orbit_pointer_guard.mjs && vite build",
"check": "node scripts/check_graph_data.mjs && node scripts/check_name_atlas.mjs && node scripts/check_project_mapping.mjs && node scripts/check_learner_projection.mjs && node scripts/check_learner_session.mjs && node scripts/check_orbit_pointer_guard.mjs && node scripts/check_mode_gate_portal.mjs && vite build",
"benchmark:projection": "node scripts/benchmark_learner_projection.mjs"
},
"dependencies": {
Expand Down
13 changes: 13 additions & 0 deletions web/scripts/check_mode_gate_portal.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";

const source = readFileSync(new URL("../src/ModeControl.jsx", import.meta.url), "utf8");

assert.match(source, /import \{ createPortal \} from "react-dom";/);
assert.match(
source,
/return createPortal\([\s\S]*?<dialog[\s\S]*?document\.body,/,
"the modal gate must not inherit display:none from responsive header chrome",
);

console.log("mode gate portal contract: ok");
11 changes: 9 additions & 2 deletions web/src/ModeControl.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useEffect, useLayoutEffect, useRef } from "react";
import { createPortal } from "react-dom";

// The first-run question frames each option as a description of the learner,
// never as the technical mode name — see task-15-brief.md: a beginner may
Expand Down Expand Up @@ -66,7 +67,12 @@ export function ModeControl({ mode, modeChosen, onChoose }) {
if (modeChosen === null) return null;

if (!modeChosen) {
return (
// A modal belongs to the document top layer, not to the header's responsive
// disclosure. Keeping it in that subtree made a fresh mobile run call
// showModal() on a dialog whose ancestor was display:none: the invisible
// backdrop blocked the whole app. The portal preserves the same component
// state and focus contract while removing layout containment.
return createPortal(
<dialog
ref={dialogRef}
className="mode-gate"
Expand All @@ -85,7 +91,8 @@ export function ModeControl({ mode, modeChosen, onChoose }) {
</button>
))}
</div>
</dialog>
</dialog>,
document.body,
);
}

Expand Down