NodeCaret and TextCaret extensible via a render slot
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 48/100
- Issue type
- Feature
- Clarity
- Mostly clear
- Activity status
- Quiet
- Tech stack
- typescript
- Domain
- frontend
Research direction
Read the existing NodeCaret and TextCaret render paths, then compare them with the slot handling already used by Overlays. Define how custom caret bodies receive the rect, selection, and anchor-name while preserving current placement, lifecycle, accessibility, and default markup; done means both caret visuals remain unchanged without overrides and can be replaced through system_components.
Written by the indexing model from the issue text.
Description
NodeCaret is style-customisable via CSS variables (--node-caret-bg, …) but the component itself isn't replaceable. TextCaret has no customisation surface at all — neither CSS variables nor a slot. This rules out any feature that wants to render its own DOM at the caret position.
Use cases
- Cursor-anchored HUD
- Components pinned to the active caret
- Bespoke caret shapes / animations beyond what CSS variables expose
Proposed direction — render slot, not component swap
Keep NodeCaret's placement and lifecycle logic centralised in svedit; expose the visual as a swappable slot (snippet, in Svelte 5 terms) that defaults to today's bar markup. The consumer brings only the pixels — and is handed back the rect, selection, and anchor-name they'd otherwise have to reconstruct from data-* attributes:
new Session(schema, doc, {
system_components: {
node_caret_body: my_custom_node_caret_visual,
text_caret_body: my_text_caret_snippet,
},
});
The valuable work NodeCaret does isn't drawing the bar — it's the selection-to-DOM mapping (which .node-gap / .svedit-selectable to attach to, when to remount, what anchor-name to publish), blink suppression on movement, and the a11y wiring that already lives in there. A consumer rebuilding all of that just to render a different glyph would re-derive heuristics svedit has already gotten right, and would silently drift out of sync as the selection model evolves upstream.
The same shape applies cleanly to TextCaret — its snippet receives the caret rect plus the text selection, so a popover or inline indicator can position itself without ever touching the selection-tracking code. The seam exposed is exactly the one consumers want — "here's where the caret would be; render whatever" — and nothing more.
TextCaret needs a DOM hook even without an override
Even before any extensibility lands, TextCaret lacks an externally-addressable DOM hook today — consumers can't reliably pin popovers to the text caret without getSelection().getRangeAt(0).getBoundingClientRect(), which doesn't survive svedit's own selection mutations. The render slot above solves this implicitly (the slot's wrapper IS the hook); alternatively, an anchor-name-bearing element on the default caret would solve it without the slot.
Notes
- Backwards-compatible — the snippet defaults to the existing bar markup; consumers who don't register an override see no change.
- Narrow stable surface — the slot's prop contract (rect, selection, anchor-name) is small enough to commit to as a public API; consumers depend only on what they actually use.
- Full component swap as escape hatch — for the 1% case that wants to relocate the caret entirely rather than just restyle it, a
system_components.NodeCaretfull-swap override could live alongside the slot. Doesn't need to ship in the same change; probably shouldn't until there's a concrete use case. - Happy to open a PR if you'd accept it — looks like a small change to thread the snippet prop through the existing render path (where
Overlaysalready does something similar).
- Dominant language
- TypeScript
- Stars
- 675
- Forks
- 18
- Avg merge
- 5h 11m
- Merged PRs (30d)
- 1
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from michael/svedit
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
-
Keyboard First Open
Difficulty 5/5 Over a week Newbie friendliness 25/100
Similar issues
-
clawsweeper:fix-shape-clear clawsweeper:queueable-fix clawsweeper:source-repro impact:ux-friction issue-rating: 🦞 diamond lobster no-stale P3
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
-
community first-timers-only good first issue hacktoberfest help wanted low hanging fruit up-for-grabs
Difficulty 1/5 Under an hour Newbie friendliness 76/100
-
code-quality refactoring
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
github/gh-aw-firewall#8816 ·
-
integration:quickjs org:external priority:backlog topic:code-interpreter topic:middleware type:feature
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
langchain-ai/deepagents#6450 ·
-
Difficulty 1/5 Under an hour Newbie friendliness 88/100
vercel/react-tweet#225 ·