Andy Matuschak mode - V2.7 (updated for 0.7+ new panes)

@s-kyy I’ve never seen that happen before. I wonder if there’s something else going on there. Perhaps you could post or PM me your whole obsidian.css file and I’ll see if I can figure out what’s going on there.

@s-kyy, @jellishero and anyone else with this problem: The only way I could replicate it myself was by shrinking the window so that the left sidebar was too tall for the window. This is clearly not what was happening in @s-kyy’s screen recording, but it did cause the same whole-ui-scrolling effect, and I was able to come up with a fix:

html, body { overflow: hidden; }

You can see it working in the following screen recording:

2 Likes

@death.au Would it be possible to modify this so that you have an infinitely vertically scrollable sidebar (like in Roam)? I tried modifying your code by replacing all instances of “vertical” with “horizontal” (lazy hack I know) and removing the parts that rotate headers, but it didn’t do anything.

I envision it working like so:

  • The panel width is fixed on the RHS
  • New panels get stacked (either bottom/top, personal preference)
  • RHS panels scroll infinitely in the vertical direction, the main panel stays in place
  • Clicking on a panel brings it into focus and maximizes it (if minimized)

Bonus points if we can somehow hack it to bring a panel to the main window when you click its title.

I worked out something like you were asking, though it’s not really exact.
This CSS works on any horizontally stacked panels, though you still have to do at least the first horizontal split manually via the menu (as panels split vertically by default).
Scrolling is also a bit weird, because you’re scrolling vertically through individual notes and the whole note stack.
Also the note titles stick at the top, but not the bottom, so when you scroll back up, notes disappear off the bottom (this is the same with normal Andy’s mode notes disappearing off the right).

Also, I can’t really do anything about clicking note titles in just CSS… That will have to wait for plugins. Again, that’s something I want to bring to normal Andy’s mode as well. When we have a proper plugin API, I’ll deprecate this CSS and maintain a plugin instead.

Here’s a screenshot of what I have (code is below):

/* everything under .mod-root now. Don't want Andy messing with sidebars */
.mod-root .workspace-split.mod-horizontal {
  overflow-y: auto;
  --header-height: 36px;
}

.mod-root .workspace-split.mod-horizontal>div {
  position: sticky;
  top: 0;
  display:block;
  height:auto;
  max-height:100%;
}

/* shift sticky position, so titles will stack up to the left */
/* This will currently stack to a maximum of 10 before resetting */
.mod-root .workspace-split.mod-horizontal>div:nth-child(10n-8) {
  top: calc(var(--header-height) * 0);
  max-height: calc(100% - (var(--header-height) * 0));
}

.mod-root .workspace-split.mod-horizontal>div:nth-child(10n-7) {
  top: calc(var(--header-height) * 1);
  max-height: calc(100% - (var(--header-height) * 1));
}

.mod-root .workspace-split.mod-horizontal>div:nth-child(10n-6) {
  top: calc(var(--header-height) * 2);
  max-height: calc(100% - (var(--header-height) * 2));
}

.mod-root .workspace-split.mod-horizontal>div:nth-child(10n-5) {
  top: calc(var(--header-height) * 3);
  max-height: calc(100% - (var(--header-height) * 3));
}

.mod-root .workspace-split.mod-horizontal>div:nth-child(10n-4) {
  top: calc(var(--header-height) * 4);
  max-height: calc(100% - (var(--header-height) * 4));
}

.mod-root .workspace-split.mod-horizontal>div:nth-child(10n-3) {
  top: calc(var(--header-height) * 5);
  max-height: calc(100% - (var(--header-height) * 5));
}

.mod-root .workspace-split.mod-horizontal>div:nth-child(10n-2) {
  top: calc(var(--header-height) * 6);
  max-height: calc(100% - (var(--header-height) * 6));
}

.mod-root .workspace-split.mod-horizontal>div:nth-child(10n-1) {
  top: calc(var(--header-height) * 7);
  max-height: calc(100% - (var(--header-height) * 7));
}

.mod-root .workspace-split.mod-horizontal>div:nth-child(10n+0) {
  top: calc(var(--header-height) * 8);
  max-height: calc(100% - (var(--header-height) * 8));
}

.mod-root .workspace-split.mod-horizontal>div:nth-child(10n+1) {
  top: calc(var(--header-height) * 9);
  max-height: calc(100% - (var(--header-height) * 9));
}
11 Likes

tried to send a PM, didn’t work, so sadly I’ll need to ask here even though kinda off-topic…

can you tell me how did you get the transparency/blur as shown on your screenshot?

It’s suuuuper nice.

Translucency is an upcoming feature in Obsidian. It’ll be a plugin option supported on both Windows and Mac (sadly Linux cannot use it due to OS constraints, I believe). Death’s just ahead of the curve!

2 Likes

How nice! Thanks for letting me know
I’ll be looking forward to it

1 Like

Is there any way to keep the first page in focus, so that when you scroll the other pages the first page stays open? I expected pinning the page to achieve this but it didn’t.

Related to this, is there any way to make the first page title horizontal along the top (default) rather than vertical down the side (Andy mode)?

1 Like

Have you tried this code? Andy Matuschak mode - V2.7 (updated for 0.7+ new panes)
It doesn’t work like the original CSS but is IMO much better if you want to write something on the left and have a lot of open documents on the right.

Edit: Sorry overlooked that the original request was from you.
I reckon what you want could probably be achieved with another split?

The vertical scrolling works well in that it doesn’t infringe on the note on the left-hand side, but I see horizontal scrolling being better (as long as there’s a way to keep the left page in focus) since it means you have a different scrolling direction for scrolling within vs between notes (which @death.au alluded to).

Essentially what I’m envisioning is the left note stays on top and in focus, then the overflow of the right hand notes scroll behind the left notes.

I did a thing. Not 100% sure if it’s exactly what you’re after, but I had fun doing it nonetheless.
It effectively leaves the first pane static and treats the rest as “normal” andy’s mode, but shifted by one pane size so nothing overlaps with that first pane. You need a bit of horizontal room to work with (~1500px + sidebars), so if you have a smaller screen or resize the window down this won’t be a great experience.

Let me know how you go with it

/* Andy Matuschak mode! modified so that the first pane is "sticky" */

/* everything under .mod-root now. Don't want Andy messing with sidebars */
/* also, Andy only makes sense for vertical splits, at the root level, right? */
.mod-root.workspace-split.mod-vertical {
  overflow-x: auto;
  --header-width: 36px;
  --pane-width: 700px;
  /* <- 36px is the header height in the default theme */
}

.mod-root.workspace-split.mod-vertical>div {
  min-width: calc(var(--pane-width) + var(--header-width));
  /* <-- 700px is the default theme's "readable" max-width */
  box-shadow: 0px 0px 20px 20px rgba(0, 0, 0, 0.25);
  position: sticky;
  left: 0;
}

/* shift sticky position, so titles will stack up to the left */
/* This will currently stack to a maximum of 10 before resetting */
.mod-root.workspace-split.mod-vertical>div:nth-child(10n-8) {
  left: calc((var(--header-width) * 9) + var(--pane-width) + var(--header-width));
}

.mod-root.workspace-split.mod-vertical>div:nth-child(10n-7) {
  left: calc((var(--header-width) * 0) + var(--pane-width) + var(--header-width));
}

.mod-root.workspace-split.mod-vertical>div:nth-child(10n-6) {
  left: calc((var(--header-width) * 1) + var(--pane-width) + var(--header-width));
}

.mod-root.workspace-split.mod-vertical>div:nth-child(10n-5) {
  left: calc((var(--header-width) * 2) + var(--pane-width) + var(--header-width));
}

.mod-root.workspace-split.mod-vertical>div:nth-child(10n-4) {
  left: calc((var(--header-width) * 3) + var(--pane-width) + var(--header-width));
}

.mod-root.workspace-split.mod-vertical>div:nth-child(10n-3) {
  left: calc((var(--header-width) * 4) + var(--pane-width) + var(--header-width));
}

.mod-root.workspace-split.mod-vertical>div:nth-child(10n-2) {
  left: calc((var(--header-width) * 5) + var(--pane-width) + var(--header-width));
}

.mod-root.workspace-split.mod-vertical>div:nth-child(10n-1) {
  left: calc((var(--header-width) * 6) + var(--pane-width) + var(--header-width));
}

.mod-root.workspace-split.mod-vertical>div:nth-child(10n+0) {
  left: calc((var(--header-width) * 7) + var(--pane-width) + var(--header-width));
}

.mod-root.workspace-split.mod-vertical>div:nth-child(10n+1) {
  left: calc((var(--header-width) * 8) + var(--pane-width) + var(--header-width));
}

.mod-root.workspace-split.mod-vertical>div:first-of-type {
  left: 0;
}

/* now it's time for the fancy vertical titles */

/* first we'll add a bit of gap for the title to sit inside of */
.workspace-leaf:not(:first-of-type) .workspace-leaf-content {
  padding-left: var(--header-width);
  position: relative;
}

/* this is where the magic happens */
.workspace-leaf:not(:first-of-type) .view-header {
  writing-mode: vertical-lr;
  border-right: 1px solid var(--background-secondary-alt);
  border-left: 2px solid var(--background-secondary-alt);
  border-top: none;
  border-bottom: none;
  height: auto;
  width: var(--header-width);
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
}

/* active titles have different border colours */
.workspace-leaf.mod-active:not(:first-of-type) .view-header {
  border-right: 2px solid var(--interactive-accent);
  border-bottom: none;
}

/* unset the title container height and swap padding */
.workspace-leaf:not(:first-of-type) .view-header-title-container {
  height: unset;
  padding-left: unset;
  padding-top: 5px;
}

/* fix the long-title-obscuring shadows */
.workspace-leaf:not(:first-of-type) .view-header-title-container:after {
  width: 100%;
  height: 30px;
  top: unset;
  bottom: 0;
  background: linear-gradient(to bottom, transparent, var(--background-secondary));
}

.workspace-leaf.mod-active:not(:first-of-type) .view-header-title-container:after {
  background: linear-gradient(to bottom, transparent, var(--background-primary-alt));
}

/* swap the padding/margin around for the header and actions icons */
.workspace-leaf:not(:first-of-type) .view-header-icon,
.workspace-leaf:not(:first-of-type) .view-actions {
  padding: 10px 5px;
}

.workspace-leaf:not(:first-of-type) .view-action {
  margin: 8px 0;
}

/* get rid of the gap left by the now-missing horizontal title */
.workspace-leaf:not(:first-of-type) .view-content {
  height: 100%;
}

/* make the fake drop target overlay have a background so you can see it. */
/* TODO: figure out how the fake target overlay works so we can put the title back, too */
.workspace-leaf:not(:first-of-type) .workspace-fake-target-overlay {
  background-color: var(--background-primary);
}
4 Likes

@death.au: awesome. I tried it on my MacBook Air and it works pretty well too !

This works wonderfully! It brings Obsidian a lot closer to matching the easy multitasking/comparison UX of Roam. For me, the value is in not having to worry about pane management, everything is a click and a scroll away.

All that remains is:

  • Hide the overflow of “sidebar” titles as they scroll to the left, to prevent the stacking that limits the number of pages you can have open (OR make it easier to rearrange the sidebar notes, so you can bring the ones you’re actively working on into the leftmost panes)
  • Click on a page title to automatically scroll it in to focus, to help with faster navigation between sidebar pages
  • A keyboard click command that enables you to click the title of the “sticky” page and perform a vertical split action (to move it into the “sidebar”)
  • A keyboard click command to click the title of a sidebar page and move it into the “sticky” position, or at least open a duplicate pane in the sticky position
  • OR, to be even more efficient than the above two bullets (which just seek to replicate Roam), a keyboard click command which swaps the positions of a sidebar page and the sticky page (i.e. to bring a new page into focus but still keep the previous sticky page to hand in the sidebar)

As you’ve said previously, I suspect these would require more than just CSS, but I look forward to your plugin when the API goes live :slight_smile:

Here are some related CSS/plugins from the Roam community in case it’s helpful:

3 Likes

/edit: Found the outline plugin. Was wondering about the TOC.

By the way, awesome work with the CSS.
Extremely useful. Thank you.

I encountered an issue. The hover-preview is displayed at an incorrect location.
Is there a way to fix this?

![Screenshot_2020-09-20_20-32-33|690x388, 75%]

I don’t think there’s a way to fix that without javascript, unfortunately. I’ve had a look into it previously and couldn’t find a workable solution.

1 Like

With your first point, I’m having a little trouble visualising what you mean. Just… not stacking them so they all go over the top of each other? Or do you mean having some sort of limit?
(I do want to make them easier to rearrange, but that’s going to be a javascript problem)

I’ve played around with some javascript to get my head around how this would work as a plugin, and it’s very rough and mostly just playing around, but I did manage to get ‘click to focus’ to work (in actuality, it’s on focus scroll into view) and it’s pretty cool.

I’ll have to play around with the idea of a sticky page. I wonder if I can tap into the existing pinning functionality and make a pane “sticky” while it’s pinned. If so, this might solve some of the points you mention… :thinking:

Edit: That second tweet/gif is very helpful in showing me what you mean. It’s basically what we have now, minus the fancy sideways titles and with some extra focus magic.

1 Like

This code forgoes the stacking titles, and allows the active pane to sit on top of all the others, while keeping a left “main” pane. I also added some margin so you can tell that the right panes are a little separate.
I’m also forgoing the “spine” titles in this particular code for simplicity’s sake but it’s easy enough to add back in and doesn’t affect the result much:

/* Andy Matuschak mode! modified so that the first pane is "sticky" */

/* everything under .mod-root now. Don't want Andy messing with sidebars */
/* also, Andy only makes sense for vertical splits, at the root level, right? */
.mod-root.workspace-split.mod-vertical {
  overflow-x: auto;
  --header-width: 36px;
  --pane-width: 700px;
  /* <- 36px is the header height in the default theme */
  --padding: 10px;
  background-color: var(--background-secondary);
}

.mod-root.workspace-split.mod-vertical>div {
  min-width: calc(var(--pane-width) + var(--header-width));
  /* <-- 700px is the default theme's "readable" max-width */
  box-shadow: 0px 0px 20px 20px rgba(0, 0, 0, 0.25);
  position: sticky;
  left: 0;
}

.mod-root.workspace-split.mod-vertical .workspace-leaf.mod-active,
.mod-root.workspace-split.mod-vertical>div:first-of-type {
  z-index:1;
}

/* shift sticky position, so titles will stack up to the left */
/* This will currently stack to a maximum of 10 before resetting */
.mod-root.workspace-split.mod-vertical>div:not(:first-of-type) {
  left: calc((var(--header-width) * 0) + var(--pane-width) + var(--header-width) + var(--padding));
  margin: var(--padding);
  max-height: calc(100% - var(--padding) - var(--padding));
}

/* make the fake drop target overlay have a background so you can see it. */
/* TODO: figure out how the fake target overlay works so we can put the title back, too */
.workspace-leaf:not(:first-of-type) .workspace-fake-target-overlay {
  background-color: var(--background-primary);
}

Unfortunately, there doesn’t appear to be any sort of class or marker for is pinned, so making pinned panes sticky is going to have to wait for plugins.

2 Likes