Reactive decorations

I’m trying to create a code viewing interface that blurs lines by default, and reveals them on mouse hover. Here’s what that looks like:

Screen Recording 2020-09-29 at 1.46.28 PM.mov

To implement this effect, I’m using a kind of “reactive” decoration. Here’s what that looks like:

class LineBlur implements PluginValue {
  decorations: RangeSet<Decoration>

  constructor(program: string, on_hover: (ref: number) ⇒ void) {
    let builder = new RangeSetBuilder<Decoration>();
    let idx = 0;
    program.split('\n').forEach((line, i) ⇒ {
      let length = line.length + 1;
      let mark = Decoration.mark({
        tagName: 'span', className='blur-line', attributes: {onmouseover: `window.line_hover(${i})`}
      });
      builder.add(idx, idx + length, mark);
      idx += length;
    });
    this.decorations = builder.finish();

    (window as any).line_hover = (i: number) ⇒ {
      on_hover(i);
    };
  }

  update(update: ViewUpdate) {
    // pass
  }
}

I have two questions.

  1. To make the effect reactive, I use CSS :hover styles on the blur-line span, and I use a global window function to run JS callbacks on hover. The window hack is obviously gross, is there a better way to do that?
  2. I want to modify this effect such that the lines adjacent to the mouse cursor will also de-blur. For that effect, I think I would need to move the de-blur logic from CSS psuedo-selectors into Javascript. However, I’m not sure how to change the decoration styles dynamically from within a Plugin, any tips on how to do this or avoid the issue?

The window hack is obviously gross, is there a better way to do that?

Sound like you should be able to add event handlers to the plugin.

You could create a state field that tracks the current hovered line, and update the decorations based on that in your plugin.

1 Like