It’s a combination of CSS and CodeMirror augmentations. One of the flaws with a CSS-only approach is that the current CodeMirror settings used by Obsidian, the “active line” class does not appear on a line if text is selected. So my plugin sets cm.setOption("styleActiveLine", {nonEmpty: true}); so that the active line class is still applicable when text is selected on a line.
Another thing that JS augmentation allows is to add things like “clicking on a checkbox toggles the task state” – I just added that this morning, it was only about 5 lines since I’m just calling the editor:toggle-checklist-status command to do it. (It was ironically much more complicated to simulate the appearance of a Chrome checkbox using just CSS; with that part done, implementing the click handler itself was a breeze.)
At this point I’ve gotten it to where there’s not an immediately obvious difference between edit and preview modes: you have to look at some fairly fine details to tell, at least for things that aren’t huge (like a mermaid block or an embed). Getting links to behave more like they do in say, Typora or HyperMD is going to be a fair amount of work though.
That is, though, my overall goal, is to get it to where it can replace Typora in most instances, at least for my personal use cases. It’s one of those things where the last 20% takes 80% of the time, though, especially for fine polish. Right now, for example, the “click on simulated checkbox to toggle to-do state” thing is a little janky because the when the click event fires, the CodeMirror state isn’t fully settled, so I’m using a setTimeout to work around it. I really need to capture clicks the way Obsidian is doing it (mousedown+mouseup) and/or hook into it more at the CodeMirror level than the DOM level. It’s particularly challenging when the click is causing a change in focus from one pane to another, since that’s tripping onfocus and thus setActiveLeaf()… so Obsidian’s state isn’t settled yet, either, at that point. Probably I need to check if the codemirror belongs to the active leaf and is focused, and if not, do that before issuing the command.
So yeah… a LOT of fine details to this thing, to get it anywhere near Typora in terms of UX and reliability. But what I’ve got now is already leagues ahead of any of the CSS-only approaches to this, at least from my POV.
By the way, speaking of CSS issues, I notice that the .markdown-preview-view is setting left and right padding of 10% and making it !important. This seems kind of weird and wrong because it’s using up a lot of screen real estate for margins that don’t seem to be needed. (Source mode, after all, doesn’t have the same.) Currently, my plugin works around it with:
.markdown-preview-view {
padding-left: 40px !important;
padding-right: 40px!important;
ul { padding-inline-start: 20px; }
ul.contains-task-list { padding-inline-start: 25px; }
}
This makes the preview have similar margins to the source mode, and left-aligns bullets and checkboxes for a less ragged reading edge.