Repository navigation
Conversation
… in one batch The skill entry is rebuilt around how an agent reads: SKILL.md is a 94-line router whose route table gives the batch to read for each workflow; the first batch is 488-881 compiled lines (was a median of 1,493). Long workflows are split into role pages (single-part 1157 -> outline + role pages, drawing-reconstruction 586 -> outline + stage pages); ql-playbook and notebook-runtime are split by job. Every workflow has Read now / Read when / Unattended mode, and discipline/operating-modes.md says what replaces each gate when nobody can answer. New: scan-reconstruction workflow, core-rules.md (rule IDs), parallel-read blocks for claudecode, zcode, opencode and codex. docs/skill-design.md is the contract; skillbuild --check enforces it (src/simplecadapi/skill/lint.py, opt in through [lint] in skillproj.toml). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…one and still be cached The 2.2.6 ShapeSelector was a frozen dataclass and pickled; the pybind class had no pickle support, so marimo's cell cache marked every cell returning a selector unserializable and the notebook ran again each time. The dict form (to_dict/from_dict) is the state. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…run of the cell cannot replace it One cell has a cache entry for every combination of values it reads (a child notebook run with other overrides, a parameter changed in the editor), all under one digest directory, and the nodes of two entries carry the same ids with other contents. The nodes were written to lineage.<cell>, so the entry run last replaced the record of the others. Restoring another entry then registered nodes that did not belong to its values: a later cell failed with 'not owned by active graph' (ids the other run lacked) or bound to the other run's node of the same id. Records are now named by the entry's key; records written by earlier versions are not read and their entries run again. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ead of re-approximating it transform_rsolid always used GTransform, which rebuilds surfaces/curves as B-splines; for bodies built from B-spline extrusions, booleans and fillets (arm 'upper', 'wrist') that failed BRepCheck although the source was valid. An orthonormal det>0 matrix now uses gp_Trsf + BRepBuilderAPI_Transform; the general path stays for non-rigid matrices. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…s over it features.py is a hand-curated table of 24 operations (primitives, profiles, booleans, hole, fillet, chamfer, shell, patterns, transforms) with typed slots and a template that renders the cells; a drift test checks every template against the live signatures. gui_features.py adds, edits and deletes features through the editor's own cell commands (an edit carries the code it was made from, so an agent's concurrent edit is never overwritten) and keeps an undo/redo history. Routes: GET /api/features/specs, GET /api/features, POST /api/features. Generated cells are ordinary cells (params cell + feature cell, selectors inline, a '# gui:' line to rebuild the form). Primitives and extrudes carry readable face tags; a selection recipe tries the tags first and checks the count. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…, and the protocol in the skill sca new --kind blank writes a part notebook with no cells; the first GUI feature adds the product cell that builds the Part from it, and later features that consume that body move it along. editor-collaboration.md says what a GUI cell looks like, that GUI commands never overwrite an edit made in between, and how the inline selection recipes behave. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…r dialogs The Features tab lists the notebook's features (GUI-made ones editable, code ones marked), shows each one's errors, offers undo/redo and a banner when the product does not build from the tip. Operations are added from a floating command bar (category chips, keyword search, Up/Down/Enter, docked top or bottom, collapsible) fed by a command registry; each one opens a non-modal dialog generated from the operation's typed slots, with viewport picks for faces and edges and a 'use picked face' helper for holes. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…nt for a canvas sketch_doc.py is the document a 2D canvas edits (points, lines, circles, arcs, 24 constraint kinds, the profile that becomes the face): validation (every id and number that reaches code is checked), the sketch cell it renders to (dimensions become scad.var sliders in the params cell), and solve(): the SDK solver without recording anything, returning status, degrees of freedom, solved points, diagnostics and the profiles (about 1.3 ms for a small sketch). POST /api/sketch/solve serves it. features.py gets a face-producing 'sketch' operation and extrude/revolve/cut/boss from a sketch; the feature service keeps solids and profiles apart (tip, open bodies, defaults, the plane normal a profile extrudes along). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
assembly_doc.py is the document a GUI edits (components with placements, the grounded one, fixed/revolute/prismatic/gear/belt/ rack-pinion constraints between connector frames): validation, and three cells it renders to (params sliders for placements and joint drives, one scad.use per child, one layout cell that builds and solves the assembly in a single pass so no cell carries the whole assembly through a chain). The feature service adds, edits, deletes and undoes the three cells together and keeps the undo history valid when a cell is re-created. GET /api/assembly/parts lists the workspace's parts and, with ?path=, the connectors a part declares. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
An SVG 2D editor for the sketch document: select/move, point, line, circle, arc, construction, auto horizontal/vertical, pan and zoom, snapping; a constraint toolbar that enables by the current selection; dimensions edited in place (double-click the label); live solve at every drag step with a temporary fix on the dragged point; status, degrees of freedom and diagnostics; closed loops found client-side and matched to the solver's profiles so the outer profile and the holes can be picked; plane selector. The sketch feature dialog hosts it in a wide docked panel; profile slots are selects over the notebook's sketches and the extrude direction follows the profile's plane normal. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
set_connectors replaces the list of connector frames of the part the product cell builds; the list lives in a marker line the GUI reads back, each frame becomes an add_connector_rpart call, and the first product the GUI makes is a recorded make_part_rpart so connectors can be added to it. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ormal
The sketch plane is a name (XY, XZ, YZ) or a frame {origin, x_axis, y_axis} (the SDK already takes one), validated to be unit and
square; profiles report their normal vector, and the sketch-based operations take 'normal of the sketch plane' as a direction,
which is the default. This is what sketching on a picked face needs.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…DK operations Trim removes the part of a line, circle or arc that contains a point, between the nearest crossings (or the ones named in against); extend lengthens a line until it meets the next entity. Both work on the solved geometry, keep the sketch connected (point_on ties, horizontal/vertical kept, dimensions that cannot hold dropped), are recorded in the operation graph and replay from model JSON. diff_sketch_rsketchdiff reports what an edit created, removed and changed. C++ in src/cpp/src/sketch/edit.cpp, bound in bind_sketch.cpp, with doctest cases, Python tests and API pages; the two operations join the canonical core op set. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…edits are the SDK's operations Sketching happens in the viewport (the camera turns to the chosen plane or face, the model stays visible and orbitable, the drawing is an SVG overlay projected through the live camera); the separate canvas panel is gone. The sketch plane can be a face frame. Trim and extend are not editor features: sketch_doc.apply_edit builds the SDK Sketch from the document, calls scad.<op>_rsketch and turns the result back; the command line (sca sketch op) and /api/sketch/op are the same thin bridge. Cut/boss/extrude from a sketch accept 'against the normal'; profiles carry a plane label; an edit that removes parameters writes the feature cell before the params cell so no cell reads a name that is gone. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Trim (T) and Extend (E) in sketch mode call /api/sketch/op, which runs the SDK's trim_rsketch / extend_rsketch; the frontend only builds the request, picks the nearer end and adopts the answer (undoable, errors shown, created entities flash). The Assembly tab edits the assembly document (components from the workspace's parts, placements, grounding, constraints with connector pickers), shows connector frames as gizmos in the viewport (clickable to fill a picker) and writes the part-side connectors from picked faces. The feature state's undo count now follows the open notebook. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…nd explode_rassembly fillet_corner/chamfer_corner round or bevel the corner of two lines that share a point (tangent and radius/length constraints keep it defined); project_rsketch brings geometry from another sketch or from the edges of a shape into a sketch as fixed construction geometry, which is how sketches in different planes are related; explode_rassembly returns an assembly with the component placements displaced for an exploded view (factor, centre or axis, per-component directions and weights). C++, bound, recorded, replayable, stubbed, documented and tested like trim/extend. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…sembly document /api/sketch/op reaches fillet_corner, chamfer_corner and project (the source is another sketch feature of the notebook, named by from_sketch); the assembly document can carry an exploded view: the product becomes scad.explode_rassembly of the solved assembly and the factor is a slider variable. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… errors shown where they happen Corner tools (R, H) pick a corner or two lines and ask for the radius or distance; Project (J) brings another sketch's geometry in as muted fixed reference geometry; the Assembly panel gets an exploded-view section. All three send the request and adopt the SDK's answer. A feature that fails to run keeps its dialog open with the error, and the next submit edits it instead of adding a duplicate. The feature dialog docks below the Inspector header. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… by its code Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…r components Connector frames were SVG lines whose screen directions were normalised to a fixed length, which destroyed foreshortening when the camera rotated; they are now three.js objects in the viewer's scene (X red, Y green, Z blue with cone heads, Z longer, a translucent contact plane), depth-tested with a faint ghost pass for the hidden ones, sized from the model and following the component's node. The pieces are reusable: scene/layers (a layer host with picking, hover/selected state, apparent-size scaling, ghost materials, anchoring to a node) and components/scene (SceneLabel anchored to a 3D point, AxisTriad, useSceneLayer, axis colours). The first camera view now fits the whole bounding sphere with a margin. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…tual action bar, sketch on a face Finished sketches are drawn in the viewport all the time (dim when an extrusion already uses them, an eye per sketch remembered per notebook, hover and selection linked with the tree); sketches are selectable (a SKETCH selection mode, or armed from a form) and the Extrude/Revolve/Cut/Boss forms take the selected sketch as their profile; 'Finish and extrude' finishes and opens the extrude form with the new sketch. A contextual action bar shows the commands that fit the selection (a planar face: Sketch on this face, Hole here, Shell; a sketch: Extrude, Revolve, Cut, Boss, Edit), driven by a when(selection) predicate on registry commands. The plane chooser highlights faces on hover and refuses curved ones with the reason; the cut form shows the direction as an arrow with a flip button. General pieces live in components/ (ActionBar, HintBanner, SelectionBar). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…tersect) A part holds one solid, so the operations that make a new solid (primitives, extrusions, revolutions, lofts, sweeps, and the sketch extrude/revolve) get an Operation slot and a Combine-with slot instead of leaving a second body beside the first: the solid is built as a private tool and joined to, cut from or intersected with the body so far in the same cell, the feature's role follows the operation, and the product follows. 'New body' starts the first body. Slots can be shown only for some values of another slot (show_when), and the feature state defaults the operation to join when a body exists. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…lid is made The Operation slot of a new solid is a segmented control (New body only when there is no body yet), the Combine-with slot shows as a line when one body is open, slots can be shown only for some values of another slot, and the stand-alone cut/union/intersect and sketch cut/boss commands are hidden unless several bodies are open: a selected sketch offers Extrude (join) and Extrude (cut) as presets of the one Extrude dialog. The chooser and its keyboard logic are general components. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…les); the editor draws the SDK's loops instead of finding its own The canvas found bounded faces of the line/arc graph itself and matched them to the solver's profiles by area and centroid; the SDK offers only simple loops and chains, so a branched or tangent-trimmed sketch showed loops the SDK did not have and 'the profile is still being matched' never cleared. The solve now returns the SDK's own list (indices are the ones make_face_from_sketch_rface takes) plus why an edge set is none, and the canvas draws exactly those. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The viewport camera keeps its class (trackball, occlusion pass, layers hold it); its projection matrix is swapped while a sketch is open, framed so the target plane keeps its size. Pick rays, apparent-size scaling and the sketch plane's own ray follow; ambient occlusion is off while orthographic. The 3D view stays perspective. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… again by the kernel, POST /api/preview/refine, until its chords are under half a pixel; same keys and entities, highlights rebuilt), parts several nodes draw are instanced (faces per colour, edges batched; the nodes stay for picks and visibility), picks go through BVHs (three-mesh-bvh, indirect: the face and edge ranges keep their order), the axis gizmo composited by the output node from its own transparent target Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…etric cylinder is --kind sample); an empty notebook is where it starts, told as info, not a ProductNotFoundError; an operation or drawing started with no notebook open asks for one first and creates it in place Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…loat over it (browser top left and collapsible, stepping aside for a task's own panel; inspector under the axis gizmo, collapsible; selection kinds top centre; the command bar stays the floating search bar and docks under them); code mode keeps its columns and the viewport is never remounted; rows of buttons and tabs scroll sideways (scroll-row, wheel too) instead of wrapping Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…y, zh-CN.json maps it; the browser's language unless chosen in the header, remembered); the chrome, notices, feature dialog, command bar and the operation catalog (names, groups, fields, options, descriptions) translated, a Chinese query finds operations by their Chinese names; a test fails when a literal key has no Chinese entry. Not yet: the assembly panel, sketch mode and the selection hints Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…f a dialog when the selection is all it needs: it is added at once with small defaults (fillet and chamfer 0.5 mm), its numbers float in a panel by the selection and every change rebuilds the model (a failure shows in the panel, the log stays shut), Enter keeps it (the numbers are written into the notebook), Esc takes it back (undone; an edited feature gets its numbers back), the dialog has every option; a feature of the tree is tuned in place the same way. The catalog and SDK index are revalidated (no-cache with an ETag) so an updated editor never serves an old catalog Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d (the face splits into its inner face and ring, roles inset.inner / inset.ring, tracked as fragments, replayable); the editor's Inset on a picked flat face, tuned in place like every operation, so inset then pull builds steps and bosses without a sketch; the operation panel's Enter handler no longer assumes its target is an element Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rism joined) or presses it in (cut), recorded as its extrude and boolean steps; the editor's Press / pull on a picked flat face replaces Pull face / Push in on the selection bar: an arrow on the face along its normal drags the distance (0.5 mm snap, Shift free) while the panel types it, across zero; the face's frame comes from its triangles on screen, at once Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…the mode switch on top of it, run all there in code mode; only its content slides on a mode change), the inspector and the selection kinds float over the viewport in both modes, the header keeps the notebook, status, theme and language (undo and redo are the feature tree's) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ls side by side on one strip, no gap; to code it moves left, to manual right); the command bar sits at the bottom only, sliding up into manual mode and down out of it (docking to the top removed) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… operations' icons in the feature tree - the XY grid is drawn per pixel (scene/ground-grid.ts): power-of-ten spacings fading into each other with the zoom, the coarser level strong, fading out a few camera distances from the target; the X, Y and Z axes are red, green and blue lines through the origin as far as the camera sees - the axis gizmo: a click on an end of an axis turns the view to look from that side (slerped about the target), the arrows in the band around it turn the view by 45° about the screen's axes, and a button beside it switches between perspective and parallel projection (remembered in the browser; the sketch canvas keeps its own and gives the chosen one back) - the floating inspector and the operation panel fold into a one-line pill and back by growing or shrinking (lib/useSizeTransition.ts); a cell folds and unfolds by its height - the feature tree shows each operation's icon (a code feature: a code icon); the sketch, revolve, curve and assembly operations have theirs Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…straints), the drawer (status, constraint list, diagnostics, profile), the plane chooser, the bar (name, finish, face/wire), typed entry, value and corner boxes, project, the hints and edit notes of the sketch editor, the sketch rows of the inspector and the name check; a test checks that the keys held in the sketch tables (tool names, constraint labels and shorts, typed fields, solver states) have their Chinese entries Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…find_product raises NoProductYetError (a ProductNotFoundError, still raised where a product is needed: export, a headless run with a product) and the editor previews it as an empty model ("no solid yet" in the log) instead of reporting ProductNotFoundError; the editor server's own empty-notebook case is gone (the kernel's empty preview covers it), and a notebook's first model is framed when it has one
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…he cup is there at once with a 1 mm wall; an arrow on the opened face drags the thickness into the body (at least 0.1 mm), the panel lists the openings and takes one out, a click on a face of the model (the shelled body itself) opens it too; Esc undoes the feature with every opening added or taken out meanwhile. Fillet and chamfer take more edges the same way (any operation whose one selection takes several picks) Server: the feature list carries what each selection slot holds as references (gui.refs), an edit keeps the references it is given for items with no entity on screen (a face a shell opened is not drawn), and a reference given twice is selected once. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…selection mode): hovered and picked on screen (within a few pixels of the drawn line), drawn wider and in the selection colour, shown in the inspector (kind, edges, length); a picked curve offers Sweep (op.sweep_rsolid with the curve as its path; a sketch picked with it is the profile, and with both given it starts at once), the selection bar hangs at the curve's middle The kernel reports the kind of every top-level value (live_value_kinds) and the feature list uses it for cells written as code: a path or a face made in code is no longer taken for a body, so it is not the product and it is offered where a wire or a face is asked for. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rve feature per line, arc or spline (path, path_2 ...), each starting at the end of the one before (a point `end_of` another curve: `path.get_end_vertex().get_coordinates()`, so it follows that curve), a path closed on its start ends at the first curve's start, and a point snapped to an end of another curve is kept at it; all of them are added or none. An older curve cell of several segments is still one wire. The feature list takes each value's kind from the kernel for every feature (a curve is an edge, a sketch a face or a wire) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n, a face boundary, a binormal guide): the wire of that one edge, made by the recorded make_wire_from_edges_rwire so a graph sees where it came from (as_wire, edges_as_wires; Wire(edge) and an implicit Edge -> Wire conversion in Python). make_wire_from_edges_rwire takes its edges in any order (they are chained by their ends first, then added in turn so each keeps its identity) and an edge that touches none of the others is an error instead of being left out. loft_rshell of open sections (the surface between two curves) no longer fails looking for end wires: only a closed end section is a boundary loop of its own Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ake_wire_from_edges_rwire of the picked curves, in any order), and every form slot that asks for a wire takes a curve (an edge) too Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…faces; recorded with its pieces, replayed the same), a TypeError for anything else Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- the face and shell values no later cell reads are drawn with the model (node/surface/<name>: light blue, seen from both sides; a sketch's face stays the sketch layer's) and picked whole in the "sketch / curve / surface" mode, highlighted and shown in the inspector - picked curves offer Ruled surface (two), Loft surface, Fill (a closed loop: the `fill` feature, make_surface_patch_rface), Flat face (a flat loop: `flat_face`, make_face_from_wire_rface with the loop's normal), Network surface (two families of crossing curves, found from where they cross: Gordon) besides Sweep and Join into a wire (lib/curve-net.ts finds loops, their plane and the families) - picked surfaces offer Sew (`sew`: sew_faces_rshell of faces and shells) and a shell Close into a solid (`solid_from_shell`), which becomes the part's body; each starts at once with its values filled from the picks Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… exploded view, its checks and kind names), the selection hint under the command bar, the part count, and the log's own lines (added, created, exported, a failed or slow cell, re-evaluating); the coverage test also checks the context actions' words, the constraint kinds and the radius fields Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…) instead of path every time Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…off (MANUAL_MODE in lib/manual-mode.ts: no mode switch, command bar, manual keys or sketch/curve pick kind), its code stays; the viewport's renderer, level of detail, instancing and BVH picking stay Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…nvert handle<Geom2d_Line> / handle<Geom2d_Circle> to handle<Geom2d_Curve> implicitly: the Linux builds failed); clang-format 18 on the five files it listed Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…n where a wire was refused is taken as its wire and fails on being open or disconnected (face_from_wire, face_from_wires, wire_from_edges: only the error fields that differ); the type stubs regenerated (import order) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Rewritten to 148 lines each (was 769 and 650): three pillars (efficient, low-overhead, agent-native), two comparison charts (against CadQuery on 200 parts; tokens per task against a mature industrial CAD through its MCP; English and Chinese, drawn by tools/make_readme_charts.py), the two six-axis arms in the new editor, the skill (seven routes and how it is organised) and the plugin system. The update notes live in CHANGELOG.md and docs/updates. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Release: SDK and kernel work, the new viewport, the editor in code mode only
Version 2.2.8 (pyproject, uv.lock, the skill's metadata and SDK overview), the changelog and the update notes in English and Chinese (docs/updates/2.2.8.md and 2.2.8.zh-CN.md, which become the release page). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
release: 2.2.8
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Release 2.2.8: the SDK work of #73 (workplanes, stable references, bodies that split and flow on, edits to existing curves and surfaces,
press_pull_rsolid,inset_face_rsolid), the new editor viewport, the editor in code mode, and the rewritten README; version and notes in #74. Update notes:docs/updates/2.2.8.md.CI was green on the code (#73); only the advisory clang-tidy fails.
🤖 Generated with Claude Code