Skip to content

Code-block language chip: caret-only visibility vs spec caret-or-hover, plus lifecycle rough edges #22

Closed as not planned

Description

@jjbaumert

Summary: The approved 2026-07-09 syntax-highlighting spec
called for the code-block language-selector chip to be visible on
caret-inside-block or pointer-hover-over-block. The shipped
slice is caret-only. This was a recorded, deliberate deviation at
ship time (hover needs block-targeted dispatch, which is cleanest
once code blocks carry a stable blockId at creation time — today
set_code_block creates the node with empty attrs). Filing this as
a tracked follow-up per the spec, along with two related chip
lifecycle rough edges noted during review.

Severity: S4 — latent/deferred-scope item. The caret-only chip
is fully functional for its primary use case (selecting a language
while editing inside the block); no current user-facing failure,
but it's a known gap against the approved spec and a plausible
source of "why doesn't hovering show the language" reports later.

Environment:

  • Deploy: N/A (design/implementation gap, not an environment-
    specific bug)
  • Commit: code-blocks-syntax-highlighting branch, as shipped
  • Browser: N/A

Repro steps:

  1. Open a document with a code block.
  2. Move the pointer over the code block without placing the caret
    inside it (e.g., hover from outside, or hover a code block that
    isn't the one currently focused).
  3. Observe the chip does not appear.

Expected (per spec): The chip becomes visible on caret-inside
OR pointer-hover-over the code block.

Observed: Chip visibility is driven purely by caret position —
see refresh_code_lang_chip in
frontend/src/components/editor_component.rs, which recomputes
CodeLangChipState from EditorState/DOM-selection anchor only,
with the comment explicitly noting "Visibility + current are
caret-based." There is no pointer-hover listener feeding this
signal.

Related lifecycle rough edges noted during review (same
component, not yet independently confirmed as bugs — noting for
the same follow-up rather than filing as separate tickets, since
fixing hover-visibility is likely to touch the same dispatch
path):

  1. Blur-hide race: the chip may linger visible if the editor
    blurs without an intervening state-change dispatch (a blur-hide
    handler racing against the <select>'s own click, since clicking
    the select itself blurs the contenteditable). Needs a considered
    debounce/ownership approach, not a quick patch, per the review
    note.
  2. Rect-read scoping: refresh_code_lang_chip reads
    getBoundingClientRect() on every dispatch inside the effect
    (guarded by an early return when there's no code block at the
    caret). If profiling ever flags this path, consider scoping the
    rect-reads to only fire on code-block-adjacent dispatches.

Suggested approach: Give code blocks a stable blockId at
creation (currently set_code_block in
frontend/src/editor/commands.rs creates the CodeBlock node with
attrs: HashMap::new(), i.e., no id). With a stable id, hover
detection can dispatch through the same
find_element_by_block_id-style lookup already used by
update_mermaid_source (both in
frontend/src/editor/commands.rs) instead of relying on
caret/selection state, which is the blocker noted at ship time.

Suspected area:

  • frontend/src/components/code_lang_chip.rs — chip component
  • frontend/src/components/editor_component.rs —
    refresh_code_lang_chip (visibility/lifecycle logic)
  • frontend/src/editor/commands.rs — set_code_block (attrs
    creation), find_element_by_block_id, update_mermaid_source
    (precedent pattern for block-targeted dispatch)

Scope expansion suggested: None beyond what's already rolled
into this ticket; the two lifecycle notes above are included here
rather than split out because they're expected to be touched by
the same fix.

Activity

  1. jjbaumert commented on Jul 10, 2026

    @jjbaumert
    OwnerAuthor

    Closing as won't-fix (product decision, 2026-07-10).

    Decision: Caret-only visibility for the code-block language chip is accepted as the intended design. The hover-to-reveal behavior from the spec is deliberately not being implemented.

    Rationale:

    • Low value: The dropdown is already reachable by clicking into the block, which you do anyway to edit it. Hover-reveal only saves a click in the narrow "relabel a block I'm not currently editing" case.
    • Non-trivial cost for an S4: Once the chip can appear over a merely-hovered block, the language <select> must be targeted by block identity — today it mutates whatever block the caret is in, so hover-select would otherwise silently change the wrong block. That targeting (either a DOM→model position map or adding a stable blockId to code blocks, the latter a deliberate CRDT wire-shape + schema-duality change) is the bulk of the work.
    • Bundle budget: Frontend WASM headroom is tight (see WASM bundle-size CI gate down to ~24 KB headroom on main #21); not worth spending it here.

    Lifecycle rough edges noted in this ticket: left unaddressed. The blur-hide lingering race is a genuine latent item but has no confirmed user-facing repro; if it ever actually manifests, file a narrow ticket for just that (caret-only, no hover targeting needed) rather than reopening this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions