Skip to content

Rich Probes + Table Projector - #1998

Merged
cyrus- merged 130 commits into
devfrom
table-projector-refractor
May 26, 2026
Merged

Rich Probes + Table Projector#1998
cyrus- merged 130 commits into
devfrom
table-projector-refractor

Conversation

@7h3kk1d

@7h3kk1d 7h3kk1d commented Nov 3, 2025

Copy link
Copy Markdown
Contributor

Summary

Adds Rich Probes (interactive domain-specific visualizations of probed values), the Table Projector (renders lists of labeled tuples as interactive tables), and auto-label support so the table projector works with unlabeled / out-of-order tuples whose labels come from the expected type.

Ported from tables-study (Rich Probes + Table Projector) plus PR #2207 (auto-labeling via elaboration), extracting only these features without pulling in unrelated changes (tutorials, pretty printer rewrite, dynamic statics, etc.).

New features

  • RichProbe module signature — extensible system for domain-specific probe renderers with parse/init/render/update lifecycle
  • TableRenderer — renders probed table values with interactive column menus (drop, rename, sort, group by, filter, move, transform, add column)
  • CardRenderer — renders probed card/hand values with visual card sprites and chooser UI Reverted; the card projector stays as on dev.
  • TableProj — standalone projector that renders lists of labeled tuples as read-only tables
  • Auto-label support — TableProj renders unlabeled tuples that receive labels from the expected type, and tuples with out-of-order labels, by consulting the elaborated expression at view time. When elaboration changes in a way that breaks the table (e.g. the surrounding type changes), the raw syntax is rendered inline with an error banner and dashed red border
  • Projector errors surfaced in UI — new Projector problem category; errors appear in the cursor inspector and problem sidebar
  • Settings toggles — "Rich Probes" and "Tables" in Value Display settings menu
    Settings toggle — "Tables" in Value Display settings menu. (The rich_probes feature flag was removed in 13fe26eddd; rich-probe renderers are now always available via the sample context menu.)
  • Comparison builtinsint_compare, float_compare, sint_compare, nat_compare for table sorting
    Comparison builtinsint_compare, float_compare, sint_compare, nat_compare, plus invert_ord for descending sort

Rich-probe UX

Rich-probe renderers are activated by a per-row badge icon to the left of each probe value.
Rich-probe renderers are surfaced as action-items ("View as …") in the existing sample context menu, one per compatible renderer, next to "Pin enclosing call" / "Step into". When activated, the renderer view opens in a floating modal anchored below the sample — the active menu flips to position: relative and the flex row top-aligns (scoped via :has(.modal-backdrop)) so the modal stacks cleanly below the menu rather than overlapping it. The modal has a floating × close button outside its top-right corner. To keep the sample's outline/background from wrapping the now-in-flow menu, visual fills (background-color, outline, border-radius, box-shadow) moved from .sample to .value; specificity is preserved via .sample:has(.value.X) .value selectors, so non-rich probes render identically to dev.

Infrastructure changes

Refractor & syntax rewrite plumbing (Rich Probes):

  • ~inline parameter on lift_syntax/term_to_seg — controls whether syntax rewrites use multi-line formatting
  • core_settings plumbed through ProjectorBase.View.argsProjectorViewRefractorView
  • SetSyntax action gains kind parameter for refractor-aware syntax rewriting (select term range, replace, preserve manual/ephemeral refractor state)
  • Refractors.to_projector now receives real syntax from TermData instead of dummy whitespace
  • ExpToSegment: newline before |> when not inline, project_table_if for auto-projecting tables (with graceful fallback), reverse application parenthesization fix
  • MakeTerm.for_projection: Any case now returns the underlying exp for non-tuples (enables project_table_if)
  • Refractors.mk_entry and ZipperBase.add_manual gain ~model parameter to preserve state across rewrites
  • ZipperBase.update_refractor — applies updates to both manuals and ephemerals maps so model state persists for auto-probes
  • TableTransforms.apply_transforms extracted as a public function for testability

Elaboration & error plumbing (Auto-labeling, #2207):

  • elaborate_syntax: bool flag on the Projector module type (like the existing dynamics flag)
  • info.elaborated — elaborated sub-expression looked up by inner expression ID, passed to projectors at view time
  • View.t.error: bool flag, kept separate from Model.status so the upcoming ViewCache can key on status without view-time state leaking in
  • error: (model, info) => option(error) function on Projector module type (renamed from diagnose) for reporting render-time errors
  • CachedSyntax.projector_errors map computed alongside shape_map, plus refresh_shapes recomputing shapes and errors after statics update (gated on statics !== old_statics)
  • Exp.find_by_id utility moved from ProofHacks to Exp.re
  • ContextMenu.Projectors.is_applicable consults the elaborated form, so "Add Table" appears for auto-labeled lists; actions_data keeps the current projector's kind available so "Remove Table" stays reachable once elaboration breaks

Misc:

  • WebUtil.empty_hole_svg — helper for rendering empty-hole indicators in table headers without labels

Notes

  • Branch was brought up to date with dev in 7694d63e8b. Notable accommodations for upstream API drift: MakeTerm.from_zip_for_sem and CachedStatics.init now require ~root (threaded through ProjectorPerform.go and CompositionView collapse helpers); ExpToSegment.Settings got a new project_tables field added to derivation/test settings records.

Test plan

  • make dev compiles cleanly
  • All 2466 tests pass (make test-quick)
  • Labeled list of tuples — Table projector activates when "Tables" setting enabled
  • Unlabeled tuples with annotated list-of-tuples type — Table projector infers column headers
  • Tuples with out-of-order labels — Table projector reorders columns per the annotated type
  • When a type change breaks the projected table, raw syntax renders inline with error banner and dashed red border
  • Projector errors appear in cursor inspector and problem sidebar
  • "Remove Table" stays available in the context menu after elaboration breaks
  • Place probe on table expression, enable "Rich Probes" — badge appears on probe values Place probe on table expression, enable "Rich Probes" — dropdown menu shows "View as table" entry Place probe on table expression — sample context menu shows "View as table" entry (rich probes always on)
  • Click badge — modal table with interactive column menus (drop, rename, sort, etc.) Click "View as table" — modal table opens below the menu with interactive column menus (drop, rename, sort, etc.)
  • Close modal via the floating × in the top-right corner
  • Column operations rewrite underlying syntax correctly
  • Card renderer works on card/hand expressions (CardRenderer reverted)
  • Settings toggles for "Tables" and "Rich Probes" work correctly "Tables" settings toggle works correctly
  • Rich probe modal renders above result cells (z-index)
  • Rich probe menu entry works on auto (ephemeral) probes

🤖 Generated with Claude Code

Comment thread src/haz3lcore/projectors/implementations/ProbeProj.re Outdated
Comment thread src/haz3lcore/Editor.re Outdated
Comment thread src/haz3lcore/derived/TermData.re Outdated
Comment thread src/haz3lcore/derived/TermData.re Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds a rich "projector-style" display system for probed values, introducing three interactive renderers (calculator for integers, table editor for structured data, and card view for playing cards). The system allows users to view and manipulate probed values through modal interfaces that can rewrite the underlying syntax.

Key Changes:

  • Added new renderer system with RichProbe interface for domain-specific visualizations
  • Implemented three renderers: TableRenderer, CalculatorRenderer, and CardRenderer
  • Added project_tables setting to control table projection in evaluation output
  • Extended projector system to support dynamic renderer selection and syntax rewriting

Reviewed Changes

Copilot reviewed 52 out of 52 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
src/web/app/common/foo Test data file (987 lines) - should not be committed
test/haz3ltest.re Commented out 3 test suites without explanation
src/haz3lcore/projectors/implementations/TableRenderer.re New table renderer with column operations (817 lines)
src/haz3lcore/projectors/implementations/CalculatorRenderer.re New calculator renderer for integer arithmetic (258 lines)
src/haz3lcore/projectors/implementations/CardRenderer.re New card visualization renderer (326 lines)
src/haz3lcore/projectors/implementations/RichProbe.re New interface for rich probe renderers (47 lines)
src/haz3lcore/projectors/implementations/ProbeProj.re Extended probe projector with renderer registry and modal support
src/haz3lcore/projectors/ProjectorPerform.re Updated action handling to preserve refractor models during syntax updates
src/haz3lcore/pretty/ExpToSegment.re Added table projection support in expression pretty-printing
src/language/CoreSettings.re Added project_tables setting to evaluation configuration
src/web/Settings.re Integrated project_tables toggle in settings UI
CSS files Added styling for calculator, table, and tableprobe projectors

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/haz3lcore/projectors/implementations/TableProj.re Outdated
Comment thread src/web/app/probesystem/ProbeSystem.re Outdated
Comment thread src/web/app/probesystem/ProbeSystem.re Outdated
Comment thread src/haz3lcore/projectors/implementations/TableProj.re Outdated
Comment thread src/haz3lcore/projectors/implementations/ProbeProj.re Outdated
Comment thread src/haz3lcore/projectors/ProjectorInfo.re Outdated
Comment thread src/language/dynamics/transition/Unboxing.re Outdated
Comment thread src/web/app/common/foo Outdated
Comment thread src/haz3lcore/projectors/ProjectorPerform.re Outdated
Comment thread src/haz3lcore/projectors/implementations/TableRenderer.re Outdated
Comment thread src/util/Unicode.re
@@ -1,3 +1,4 @@
[@coverage exclude_file];

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For some reason coverage breaks the regex in here. We should open an issue in the bisect_ppx repo

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 53 out of 53 changed files in this pull request and generated 6 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/haz3lcore/projectors/ProjectorPerform.re Outdated
Comment thread src/haz3lcore/projectors/ProjectorPerform.re Outdated
Comment thread src/haz3lcore/projectors/ProjectorBase.re
Comment thread src/haz3lcore/projectors/implementations/ProbeProj.re
Comment thread src/haz3lcore/projectors/ProjectorPerform.re Outdated
Comment thread src/haz3lcore/projectors/ProjectorPerform.re Outdated
Base automatically changed from probemoar to dev January 16, 2026 23:03
@codecov

codecov Bot commented Feb 4, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 29.68624% with 874 lines in your changes missing coverage. Please review.
✅ Project coverage is 50.58%. Comparing base (d2e85b3) to head (09f74c5).

Files with missing lines Patch % Lines
...3lcore/projectors/implementations/TableRenderer.re 10.05% 170 Missing ⚠️
src/util/Menu.re 1.16% 170 Missing ⚠️
.../haz3lcore/projectors/implementations/ProbeProj.re 1.02% 97 Missing ⚠️
src/web/app/editors/code/ContextMenu.re 0.00% 77 Missing ⚠️
src/util/MenuListener.re 0.00% 72 Missing ⚠️
.../haz3lcore/projectors/implementations/TableProj.re 21.05% 45 Missing ⚠️
src/haz3lcore/projectors/ProjectorPerform.re 13.33% 39 Missing ⚠️
...core/projectors/implementations/TableTransforms.re 87.56% 23 Missing ⚠️
.../haz3lcore/projectors/implementations/RichProbe.re 55.81% 19 Missing ⚠️
...re/projectors/implementations/RichProbeRegistry.re 55.26% 17 Missing ⚠️
... and 33 more
Additional details and impacted files
@@            Coverage Diff             @@
##              dev    #1998      +/-   ##
==========================================
- Coverage   50.91%   50.58%   -0.34%     
==========================================
  Files         300      307       +7     
  Lines       39867    40769     +902     
==========================================
+ Hits        20300    20622     +322     
- Misses      19567    20147     +580     
Files with missing lines Coverage Δ
src/haz3lcore/Editor.re 44.06% <100.00%> (ø)
src/haz3lcore/projectors/ProjectorInit.re 91.30% <100.00%> (+18.57%) ⬆️
...c/haz3lcore/projectors/implementations/FoldProj.re 12.82% <100.00%> (+2.29%) ⬆️
src/haz3lcore/zipper/action/Introduce.re 80.37% <ø> (ø)
src/haz3lcore/zipper/action/Perform.re 50.00% <ø> (ø)
src/language/ProjectorKind.re 89.74% <100.00%> (+0.85%) ⬆️
src/language/builtins/Builtins.re 100.00% <100.00%> (ø)
src/language/builtins/BuiltinsADT.re 98.64% <100.00%> (+0.34%) ⬆️
src/language/builtins/BuiltinsBase.re 55.79% <ø> (-0.76%) ⬇️
src/language/proof/ProofHacks.re 25.46% <ø> (+0.46%) ⬆️
... and 46 more

... and 14 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment thread src/web/app/common/RefractorView.re Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 58 out of 58 changed files in this pull request and generated 10 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/haz3lcore/projectors/ProjectorPerform.re Outdated
Comment thread src/haz3lcore/projectors/implementations/TableRenderer.re Outdated
Comment thread src/haz3lcore/projectors/implementations/CalculatorRenderer.re Outdated
Comment thread src/haz3lcore/projectors/implementations/TableProj.re Outdated
Comment thread src/haz3lcore/pretty/ExpToSegment.re Outdated
Comment thread src/haz3lcore/projectors/implementations/ProbeProj.re
Comment thread src/web/www/style/variables.css Outdated
Comment thread src/haz3lcore/projectors/implementations/TableRenderer.re Outdated
Comment thread src/web/www/style/variables.css Outdated
Comment thread test/haz3ltest.re Outdated
@7h3kk1d
7h3kk1d changed the base branch from dev to dataframe-projector February 4, 2026 20:01
7h3kk1d and others added 10 commits March 19, 2026 12:33
Move card types (suit, rank, card, hand) and conversion utilities into
CardTypes.re, CardSyntax.re, and CardUtil.re. This prepares for the
RichProbe renderer system where CardRenderer will reuse these modules.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add ~inline parameter to lift_syntax and term_to_seg in ProjectorBase
utility type. When inline=false, lift_syntax appends a trailing newline
to give room for probe values. This prepares for the table projector
which needs non-inline syntax rewrites.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Introduce the RichProbe module type signature for domain-specific
probe visualizations. Add CardRenderer (card/hand display) and
TableRenderer (interactive table with column menus, sorting, grouping,
filtering, and type-aware transformations).

Also add WebUtil.empty_hole_svg helper used by TableRenderer.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add Table projector kind and TableProj module for rendering lists of
labeled tuples as read-only tables. Register in ProjectorInit and
add to context menu.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add rich_probes field to CoreSettings.Evaluation and add core_settings
to ProjectorBase.View.args so projectors can access feature flags.
Thread core_settings from globals through ProjectorView and RefractorView
to all projector view functions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add packed_renderer system for heterogeneous RichProbe storage using
sexp serialization. Register TableRenderer and CardRenderer as available
renderers. Add renderer badges to probe value views when rich_probes is
enabled. Add modal overlay for interactive rich probe display. Change
ProbeProj model from unit to probe_model tracking active renderer state.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add proj-table.css for basic table styling (borders, zebra rows, sticky
headers) and proj-tableprobe.css for rich probe table interactions
(column menus, modal backdrop, add-column button). Add table CSS
variables to variables.css.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add RichProbes evaluation action and wire it to a toggle in the nut
menu. When enabled, probe value views show renderer badges for
compatible rich probe visualizations (table, card).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add projector kind to SetSyntax action so refractors (probes) can
properly rewrite underlying syntax. For refractors, select the full
term range and replace it with the new syntax, then re-add the manual
refractor. This enables table column operations (drop, rename, sort,
etc.) to actually modify the program.

Also add CSS for renderer badges (background, hover) and z-index rule
for modal stacking context.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The previous implementation unconditionally added a manual refractor
after syntax replacement, which broke ephemeral (auto) probes. Now
properly distinguishes:
- Manual refractors: replace syntax and re-add manual with preserved model
- Ephemeral refractors: replace syntax only, auto system re-detects

Also add ~model parameter to Refractors.mk_entry and ZipperBase.add_manual
to preserve refractor state across syntax rewrites.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
7h3kk1d and others added 13 commits May 13, 2026 15:22
- Atom.compare_builtin: cls -> option(builtin name). BuiltinsBase.mk_compare
  now sources names from here, so the cls -> name mapping has one source.
  Folded string_compare into the mk_compare list.
- Operators.numeric_bin_op: lift op_bin_num to op_bin given an Atom.cls.
  Moved out of TableTransforms.

Test_Atom covers compare_builtin in addition to conversions_from.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Atom.re now owns the list of (kind, cmp) pairs that need an Ord builtin.
BuiltinsBase iterates over Atom.compare_builtins and assembles the
BuiltinsUtil.fn records, so the per-class data has a single source.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Drop the cls switch in mk_compare in favor of Atom(cls) |> Typ.fresh.
Aliased the top-level Typ as LangTyp since `open Fresh` shadows it.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replaces the cls-shaped Typ.fresh call site in BuiltinsBase, hidden by
the LangTyp alias because `open Fresh` shadows the top-level Typ here.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Atom.compare_builtins now exposes list((name, compare_entry)), mirroring
  Atom.converter_builtins' (name, builtin) shape.
- BuiltinsADT.of_atom_compare turns an entry into a BuiltinsUtil.fn (it
  needs Ord.t, which lives here).
- Builtins.re maps it in alongside the converter mapping; BuiltinsBase no
  longer participates in compare-builtin assembly.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- BuiltinsADT now owns invert_ord and exposes ord_builtins (invert_ord +
  per-cls compares from Atom.compare_builtins). BuiltinsBase no longer
  participates in Ord-related wiring.
- Builtins.re maps the bundle through fn_builtin for consistency with
  neighboring rows.
- TableTransforms.sort_column references BuiltinsADT.invert_ord.name
  instead of a string literal.
- Atom.compare_of_cls is the single exhaustive switch; compare_builtin
  and compare_builtins derive name and list from it (and from
  cls_string_lower) -- adding a new cls forces a compile-time decision.
- New Test_BuiltinsADT covers invert_ord and of_atom_compare end-to-end;
  new Test_Operators covers numeric_bin_op.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Unify len_seg/seg_of_exp/abbreviated_seg_of across ProbeProj and
TableCore. Width is now measured in Unicode columns everywhere, and
abbreviation strips both ascriptions and projector wrappers in both
call sites. Also drops TableCore's dead length_cls (its emitted size
classes had no matching CSS selectors).
handle_key already knows the action count, so it computes the wrapped
target index and dispatches MenuSelect(new_idx) directly. This removes
the unbounded idx growth in MenuDown and the separate MenuUp/MenuDown
actions entirely.
Reset on any failure still — the record is just transient UI state —
but Unknown_renderer and malformed-payload Failures now print a
diagnostic line so they aren't completely silent in dev.
Both ContextMenuListener and ColumnMenuListener attached a
document-level capture-phase pointerdown for click-outside, plus
near-identical state-tracking in sync. Move the shared machinery into
MenuListener.Make and parameterize over menu class, optional keydown
dispatch, and optional scroll-into-view. Each former module is now an
8-line config instantiation.
Tables slide reframes the gradebook example around "tables are lists of
labeled tuples" and adds a projected view of the same data. Probes
slide adds a rich-probes section with an inventory sample whose "View
as table" modal is opened by default.
Thread the active renderer's id into probe_ctx so the sample-menu entry
flips to "Hide <id>" when that renderer's modal is already open. Click
behavior is unchanged — it dispatches ToggleModal either way — but the
label now reflects what the click will do.
Mirror ProjectorInfo.utility.term_to_seg, which already sets
project_tables: false. After CoreSettings.Evaluation.init flipped
project_tables to true, the test's settings auto-wrapped abbreviated
list-of-records output in ^^table(...), exceeding the budget.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@7h3kk1d

7h3kk1d commented May 19, 2026

Copy link
Copy Markdown
Contributor Author

Combine column menu with ContextMenu

7h3kk1d and others added 11 commits May 19, 2026 15:38
Splits ContextMenu.re into a generic Util.Menu framework (state,
item/Action/Submenu/Back/Divider, render, keyboard) and editor-specific
contents (Shortcuts, *_data builders, caret positioning). TableRenderer's
column menu now uses the same module and adopts .context-menu styling,
including path-driven submenus. Keyboard dispatch for the editor menu
routes through ContextMenuListener at document capture phase instead of
the bespoke handler in CodeEditable.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds Menu.direction_from_id/direction_of/space_from helpers that compute
an open_direction from any element's bounding rect against the #main
viewport, and applies them to the column menu so it opens upward and/or
leftward near viewport edges. The editor menu's .open-* classes carry
caret-anchored transforms, so the column menu uses its own .cm-* classes
keyed to the th cell corners (top/bottom × left/right) with matching
border-radius and animation transform-origin.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
ArrowRight steps into a Submenu row when it's the selected item;
ArrowLeft pops out of a non-root path, targeting the first visible Back
row's destination (or [] as a fallback). Editor menu has no submenus so
both arrows fall through to Unhandled there; column menu's adapter
already maps EnterSubmenu/BackSubmenu to ShowSubmenu, so navigation
works without further changes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Submenus now carry their children inline (Submenu({label, children}))
instead of a target_path string. Util.Menu walks the path through the
tree, synthesises the ← Back row when nested, and clamps the selected
index against the visible row count internally — so callers no longer
build per-path item lists or track separate clamping.

Action variants simplify too: EnterSubmenu carries one label (pushed
onto the path), BackSubmenu has no arg (pops one level). Menu.update
is pure now (no items parameter), since clamping happens at render and
keyboard-dispatch time.

TableRenderer's projector model drops the (col, path, sel_idx) tuple
for (col, Menu.t) and consolidates ShowSubmenu/MenuSelect into a single
MenuAction(Menu.action) variant. build_column_menu produces the whole
tree once per render; menu_at/items_at lookups are gone.

ContextMenu.WithContext.update becomes a one-liner forwarding to
Menu.update; clamping is no longer its concern.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
handle_key was dispatching MenuUpdate(Up/Down), which decrement the raw
stored selected_idx via Menu.update. After a Down past the visible
count, the stored value drifts beyond the cap; render clamps visually
but the next Up walks back from the stale number, taking N keypresses
to start moving.

Convert ArrowUp/Down to SetSelected(clamped ± 1) inside handle_key —
the clamped current index is already computed there.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…s bold

The shared .context-menu rule didn't set font properties, so each menu
inherited from its container — the editor menu picked up .code's normal
weight + code font, but the column menu inherited <th>'s bold default
and looked heavier than the editor menu. Set font-family / weight /
size / line-height explicitly on .context-menu.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The column menu sits inside a <th>, which browsers default to
font-weight: bold. That weight inherits through the menu's DOM and
bolded all the menu rows compared to the editor context menu (rendered
inside a non-th container that doesn't have the UA bold).

Wrap the header label in a .column-label span and move the header's
font properties (weight 600, code-font, BR4 color) onto that span.
Reset .table th's font-weight to normal so the UA default doesn't
inherit any further. The label still renders bold; nested children
(the menu) inherit a clean 400.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Drop the local vertical_dir/horizontal_dir/open_direction/available_space
types and get_available_space/determine_direction — they duplicated
Util.Menu's. get_direction now treats the caret as a zero-size anchor at
(caret_left, caret_bottom) and calls Menu.space_from + Menu.direction_of.

direction_class stays local since the editor menu's .open-* CSS carries
caret-anchored transforms that don't apply to the column menu's th-anchored
.cm-* classes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
NutMenu.submenu wrapped the menu in top-menu-item/submenu-icon/icon/
submenu divs whose CSS is scoped to #top-bar — none of those rules apply
inside the editor. The styling that actually works comes from
.context-menu .group .contents …, so just emit those two divs directly
and drop the NutMenu dependency.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
ColumnMenuListener's capture-phase pointerdown handler closes the menu
whenever the click target isn't inside the menu DOM. The ⋮ button isn't
inside the menu — it sits in the th cell — so a click on it was firing
the close path before the button's own on_click reached ShowMenu, which
then saw menu_state=None and opened the menu again. Net effect: clicking
⋮ to "close" reopened it.

Tag the ⋮ button with `menu-trigger` and skip the close-on-outside path
when the click target is under that class. ShowMenu(i) already toggles
when the same column is open; now it can actually fire.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@7h3kk1d
7h3kk1d marked this pull request as ready for review May 26, 2026 14:45
@cyrus-
cyrus- merged commit 4cf18bc into dev May 26, 2026
4 checks passed
@cyrus-
cyrus- deleted the table-projector-refractor branch May 26, 2026 14:59
disconcision added a commit that referenced this pull request May 27, 2026
ExpToSegment.Settings.t gained project_tables (Table Projector from dev #1998).
Set to false for TermEdit round-trip to preserve raw term structure.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants