Skip to content

Toolbar color underline reflects active cursor/selection color#125

Open
hendriebeats wants to merge 5 commits into
masterfrom
toolbar-active-color-state
Open

Toolbar color underline reflects active cursor/selection color#125
hendriebeats wants to merge 5 commits into
masterfrom
toolbar-active-color-state

Conversation

@hendriebeats

@hendriebeats hendriebeats commented Feb 21, 2026

Copy link
Copy Markdown
Contributor

Summary

Replaces the static text-color and highlight-color toolbar icons with inline SVGs whose underline bar dynamically reflects the color currently active at the cursor or selection.

backend/templates/study.html

Inline SVG icons

The <img> tags for the text-color and highlight-color toolbar buttons have been replaced with inline <svg> elements. Inlining is required so that the underline <rect> inside each icon can have its fill attribute updated via JavaScript. Both SVGs carry aria-hidden="true" since the parent <button> already has a descriptive title.

editorAttached listener

Listens for the custom editorAttached event fired when the ProseMirror editor mounts, then subscribes to onStateUpdate. On every state change it reads the ProseMirror marks at the relevant position and updates the SVG underline colors.

Cursor vs. selection: When there is no selection (sel.empty), marks are resolved at sel.from — the character to the left of the cursor, which is the standard ProseMirror convention for "active marks". When text is selected, marks are resolved at sel.from + 1 — the first character inside the selection — so the toolbar reflects the color of the selected text rather than the character preceding it.

Color sanitization: Before assigning any color from a mark attribute to an SVG fill, the value is tested against a strict regex (/^#[0-9a-fA-F]{3,8}$|^rgb\(...\)$/). Values that don't match (e.g. a crafted url(...) string from a malicious document) are silently ignored and the default color is used instead.

DOM caching: The two underline elements and the regex are resolved/compiled once when editorAttached fires, outside the hot onStateUpdate callback which runs on every keystroke and cursor movement.

closes #72

hendriebeats and others added 5 commits February 19, 2026 03:34
…t cursor color

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…-hidden

- Validate mark color attrs against safe regex before setting SVG fill
  (prevents potential XSS via crafted document color values)
- Cache getElementById lookups outside the hot state-update callback
- Add aria-hidden="true" to decorative toolbar SVGs
- Add id="highlightColorIcon" to highlight SVG for consistency

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When text is selected, resolve marks at from+1 (first selected character)
rather than from (the character before the selection).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@hendriebeats hendriebeats added Size: Small 21–100 lines: Single-purpose change, easy to review quickly. Size: Micro 1–20 lines: Minor fix, typo, tiny refactor. and removed Size: Small 21–100 lines: Single-purpose change, easy to review quickly. labels Feb 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Size: Micro 1–20 lines: Minor fix, typo, tiny refactor.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

changing text color should change the text decoration color

1 participant