Show text selection across embeds in Obsidian (CodeMirror 6)

When a text selection in Obsidian’s Live Preview spans an embed that you’ve made, you can visualise that selection with a minimal CodeMirror 6 plugin that toggles a CSS class on the embed’s root element.

Minimal logic (selection overlap + CSS toggle)

Assumptions:

  • Your embeds are CM6 widgets that render a root element like <div class="my-embed" data-embed-id="...">
  • You already have a DecorationSet field for those widgets (called yourEmbedsField below)
import { EditorView, ViewPlugin, ViewUpdate } from "@codemirror/view";
import type { DecorationSet } from "@codemirror/view";

// Returns true if a selection range crosses the widget range
function selectionSpansWidget(
  selFrom: number,
  selTo: number,
  widgetFrom: number,
  widgetTo: number
): boolean {
  if (selFrom === selTo) return false; // ignore cursor-only
  return (
    (selFrom < widgetFrom && selTo > widgetTo) ||   // wraps widget
    (selFrom < widgetFrom && selTo > widgetFrom) || // ends inside
    (selFrom < widgetTo && selTo > widgetTo)        // starts inside
  );
}

// Given your existing decoration field of embed widgets:
declare const yourEmbedsField: any; // StateField<DecorationSet>

export const highlightSelectionAcrossEmbeds = ViewPlugin.fromClass(class {
  constructor(private view: EditorView) {
    this.updateDom();
  }

  update(u: ViewUpdate) {
    if (u.selectionSet || u.docChanged || u.viewportChanged) {
      this.updateDom();
    }
  }

  updateDom() {
    const embeds: DecorationSet | undefined = this.view.state.field(yourEmbedsField, false);
    if (!embeds) return;

    // Reset all
    this.view.dom.querySelectorAll<HTMLElement>(".my-embed")
      .forEach(el => el.classList.remove("my-embed--highlighted"));

    // For each widget, check if the current selection spans it
    const it = embeds.iter();
    while (it.value) {
      const widget = it.value.spec?.widget as { id?: string } | undefined;
      if (widget?.id) {
        const from = it.from, to = it.to;
        const overlaps = this.view.state.selection.ranges.some(r =>
          selectionSpansWidget(r.from, r.to, from, to)
        );
        if (overlaps) {
          const el = this.view.dom.querySelector<HTMLElement>(`.my-embed[data-embed-id="${widget.id}"]`);
          if (el) el.classList.add("my-embed--highlighted");
        }
      }
      it.next();
    }
  }
});

Minimal CSS

.my-embed {
  position: relative;
}

.my-embed--highlighted {
  background-color: #FF0000
}

That’s It!

Detect if the current selection spans a widget’s range, and toggle a highlight class on the embed’s root element that you adjust in CSS.

Further References:

Thanks…

I also dissect and speculate on design and development.
Digging into subtle details and implications, and exploring broad perspectives and potential paradigm shifts.
Check out my conceptual articles on Substack or find my latest below.


You can also find me on Threads, Bluesky, Mastodon, or xTwitter for more diverse posts about ongoing projects.

My latest articles

Storyboarding Immersive Experiences

Storyboarding 360 degree immersive experiences requires a different approach to traditional media…

Staging XR scenes (Keep doing your crappy little drawings)

Some people create beautiful perspective illustrations to visualise and storyboard their virtual reality designs And it’s tempting to think you’re not a strong designer if you’re not doing that too…

Focal point blocking for XR media

Planning out a linear VR experience requires thinking about where the viewers attention might be. Thinking about the focal points…

Designing immersive experiences

In traditional cinema, TV, or even the more modern phone screen, there’s limited screen real-estate. But removing that limitation creates a design problem…

The future is not prompt engineered

Let’s not pretend the importance of prompt engineering is ubiquitous. The most prevalent power of generative AI is in the way it adapts to us, not the other way around…

The typography of dates, times, & filenames

A deep dive into carefully considered date formatting, line length and general typography attributes of filenames…
Bluesky
Threads
Twitter / X
Mastodon
Instagram


Author:

Date:

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.