Skip to content

Bug: Awareness avatar (Active Users) disappears after switching between Reading/Live Preview view, and fails to mount entirely when inline title is disabled #108

Description

@VRTG-47

Summary

The presence-avatar component (AwarenessView / "Active Users") only mounts correctly for the view mode that was active when the note was first opened. Switching between Reading View and Live Preview afterward causes the avatar to disappear, because the component is inserted next to .inline-title and never re-mounted on view-mode change. Additionally, the component hard-depends on .inline-title existing at all, so disabling "Show inline title" in Obsidian settings prevents the avatar from being created in any view mode.

Environment

  • Obsidian version: [1.12.7]
  • Relay plugin version: [0.7.6]
  • Reproduced in a clean vault with all other themes/snippets/plugins disabled
  • Reproduced with a newly created shared folder and a brand-new note

Bug 1: Avatar mounts once and doesn't survive Reading View ⇄ Live Preview switching

Steps to reproduce

  1. Create a shared folder, create a new note inside it, with at least 2 users connected to the same document.
  2. Set Obsidian's default new-tab view to Reading view.
  3. Close and reopen the note (or open a new tab) — the note opens in Reading view.
  4. Observe: the "Active Users" avatar is visible in Reading view. ✅
  5. Switch to Live Preview / Edit mode on the same note.
  6. Observe: the avatar disappears. ❌
  7. Repeat with default view set to Editing view instead — the avatar shows correctly in Live Preview but disappears when switching to Reading view.

Root cause (confirmed via DevTools)

wrapTitle() in the awareness view class only runs once, guarded by:

wrapTitle(){
  if(!this.view.view.containerEl || this.destroyed || this.awarenessElement) return;
  let t = this.view.view.containerEl.querySelector(".inline-title");
  ...
  this.awarenessElement = document.createElement("div");
  ...
  t.insertAdjacentElement("afterend", this.awarenessElement);
}

Since this.awarenessElement is set after the first successful mount, this method short-circuits on every subsequent call and never re-runs — even though it appears to be called again on attach().

Obsidian maintains separate DOM subtrees for markdown-reading-view and the Live Preview/source editor, both containing an element with class iconic-title-wrapper. When the note is first opened in Reading view, Relay's awareness container is inserted into the Reading-view copy of this wrapper. When the user switches to Live Preview, Obsidian sets that entire subtree (markdown-reading-view and its ancestor iconic-title-wrapper) to display: none — and since Relay never re-mounts into the now-active Live Preview title wrapper, the avatar element is still in the DOM, but sitting inside a hidden subtree.

Verified with DevTools — the avatar element persists, but its ancestor chain shows:

0 user-awareness-container        display: flex     height: 0
1 iconic-title-wrapper            display: none      height: 0   <-- hidden
2 mod-header mod-ui               display: block      height: 0
...
5 markdown-reading-view           display: none      height: 0   <-- hidden
6 view-content                    display: block      height: 1390
7 workspace-leaf-content ...      display: flex        height: 1390

Because an ancestor is display: none, no CSS workaround (including position: fixed !important) can make the avatar visible again — a display:none ancestor suppresses rendering of its entire subtree regardless of the child's own position/z-index.

Expected behavior

The awareness avatar should remain visible regardless of which view mode (Reading / Live Preview) is currently active, and should follow the user when switching between them on the same note.

Suggested fix

Re-run wrapTitle() (or fully re-create awarenessElement) whenever the active view mode changes for the same document — e.g. by listening to workspace.on("layout-change") and/or workspace.on("active-leaf-change"), and resetting this.awarenessElement = undefined before re-calling wrapTitle() so it queries the currently-visible .inline-title instead of reusing a stale one.


Bug 2: Avatar fails to mount entirely when "Show inline title" is disabled

Steps to reproduce

  1. In Obsidian settings → Appearance (or Editor), disable "Show inline title".
  2. Open a note in a shared folder with active collaborators.
  3. Observe: .user-awareness-container is never created in either Reading view or Live Preview. Console shows: Could not find inline-title element to position awareness component.

Root cause

wrapTitle() exclusively looks for .inline-title:

let t = this.view.view.containerEl.querySelector(".inline-title");
if(!t){
  this.warn("Could not find inline-title element to position awareness component");
  return;
}

There is no fallback anchor element, so disabling this fairly common personalization setting silently disables the entire presence feature with no visible error to the end user (only a console warning).

Expected behavior

The awareness avatar should still display somewhere sensible (e.g. view header / title container area) even when inline title is disabled.

Suggested fix

Add a fallback anchor when .inline-title is not found — e.g. .view-header-title-container, .view-header, or .workspace-leaf-content — so the component can still mount and position itself reasonably without depending on inline title being enabled.


Additional notes

  • This was reproduced in a fully clean vault (no other plugins, themes, or CSS snippets enabled), so it is not caused by theme/plugin conflicts.
  • Current workaround: set Obsidian's default new-tab view to "Editing view" in Settings → Editor, which avoids triggering Bug 1 in normal daily use (but does not fix Bug 2).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions