Hiding frontmatter

There are certain pages that I use frontmatter, but I would like to hide it. One example case is custom decorations, I already see them on the page, I don’t want to look as frontmatter definitions.
Is there a way to hide or fold frontmatter?

2 Likes

This would be very useful, it would allow you to have a panel with metadata and present only those relevant to the display just below and formatted.

You can fold it (Outline: Fold) but this won’t persist. Feel free to create a GitHub issue for it with specifics on how you would like this to work.

Added GitHub Issue, more like an (enhancement).

3 Likes

As a workaround to fully hide it for some pages you can add the following combination of space-style and pageDecoration frontmatter:

Add style that allows to hide frontmatter on pages with css class no-frontmatter:

```space-style
.no-frontmatter .sb-frontmatter {
    display: none;
}
```

Then add cssClasses decoration to the pages where you want to hide it e.g. using frontmatter:

---
pageDecoration:
    cssClasses:
      - no-frontmatter
---

I use it myself for hiding it on prefixed pages, could probably also be done using object decorations for all pages with prefixes. Changing the frontmatter again requires commenting the style so it is really only a workaround and a proper solution would be preferred.

11 Likes

Not a perfect solve, but a great workaround!

I was looking for this too, and came up with a solution I thought might be worth sharing. I haven’t tested it across too many browsers yet, but it’s working for me. Uses just one bit of css in the space-style to hide the frontmatter block down to just the first line (showing that there is frontmatter) until you move the cursor into it to make edits.

.sb-frontmatter.sb-line-frontmatter-outside:has(+ .sb-frontmatter) ~ .sb-frontmatter {
    display:none
}
7 Likes

Thanks for this, it works great on Edge :slight_smile:

Is it possible with CSS to move the cursor position to below the frontmatter? On page load, the cursor goes to the first line (which contains frontmatter) but given we want it folded it’d be nice to place the cursor below the last line of frontmatter.

Edit: Looks like I might be able to use this solution.

I was just about to say, you can do something like this. But if Zef’s example works, then that is much cleaner! It leaves the cursor inside the front matter, but with a single arrow down you can get out of it.

Just because I was working on it anyway, if you still want to move your cursor below the front matter automagically.

event.listen {
  name = "editor:pageLoaded",
  run = function(e)
    local page = editor.getText()
    if not string.startsWith(page, "---") then
      print("🪅 no frontmatter")
      return
    end

    -- find the position of the closing dashes of the frontmatter
    local skipOpening = string.sub(page, 3)
    local posClosing = string.find(skipOpening, "%-%-%-")
    local posAfterFM = posClosing + 3 + 2 -- open (3) + closing (2) matter
    
    local pos = editor.getCursor()
    if (pos >= posAfterFM) then
      print("🪅 cursor is already past frontmatter: ", pos)
      return
    end
    
    print("🪅 moving cursor to:", posAfterFM)
    editor.moveCursor(posAfterFM)
  end
}
1 Like

So I’ve also come up with a solution to this that creates a command to hide the frontmatter and tags while using the vertical menu from Mr. Red.

First I should say I organize all of my notes by tags, which I put at the end of paragraphs and pages and everywhere – tags all over the place. So when I view notes, I like to hide the front matter and those tags so it’s visually clean with just the text I want to read. The code below is for both tags and frontmatter, but you can see they’re clearly separated, so if you only want to hide frontmatter, just delete the tags sections.

I’ll admit I’m not a programmer, so I’m not sure how elegant this solution is – it works well, though!

I first created a page in my library to input the space script:

silverbullet.registerCommand({name: "Toggle UI Elements"}, async () => {
  // Get the body element
  const body = document.body;
  
  // Toggle both classes
  body.classList.toggle("hide-tags");
  body.classList.toggle("hide-frontmatter");
  
  // Show notification
  await editor.flashNotification("UI elements visibility toggled");
});

In SETTINGS, add:

/* Styles for hidden elements */
body.hide-tags .sb-hashtag {
  display: none;
}

body.hide-frontmatter .sb-frontmatter {
  display: none;
}

Then in the space-config for the vertical menu settings, I added:

  • icon: eye
    command: “{[Toggle UI Elements]}”
    description: “Toggle UI visibility (tags & frontmatter)”

If you don’t want to add to the menu, you can just run it as a regular command.

1 Like

Pretty smart solution. I wonder if there’s going to be a way to still do that in v2, considering space-script will be removed.

@zef will there be a Lua DOM manipulation API? :innocent:

3 Likes

I came up with this for v2 and space-lua, to temporarily unhide the hidden frontmatter. I know it’s not the most elegant solution and it’s more like a hack, but it works for me.

If you have your frontmatter hidden with following:

[space-style]

.no-frontmatter .sb-frontmatter { display: none; }

I created this handy little command which does the following:

  • press “Ctrl-0” → checks if the page has a frontmatter (if first line is “—”) then add “:construction:” to the beginning of the line, disabling the frontmatter temporarily.
  • if you press “Ctrl-0” again → it will check if the first line ends with “—” then it will “enable” the frontmatter by changing the first line back from “:construction:—” to “—” . toggling the frontmatter on again.
  • if no frontmatter was found the info that the page has no frontmatter.

[space-lua]

function toggleFrontmatter()
  editor.moveCursor(0, true)
  local firstLine = editor.getCurrentLine()

    if firstLine.text == "---" then
            editor.insertAtCursor("🚧")
    else if firstLine.text:endsWith("---") then
            editor.replaceRange(firstLine.from, firstLine.to, "---")
    else editor.flashNotification("⚠️Page has no frontmatter!")  end
    end
end

command.define {
  name = "Show: Toggle Disabled Frontmatter",
  key  = "Ctrl-0",
  run  = function()
    toggleFrontmatter()
  end
}

go on, give it a try and tell me what you think!

Do you have a better solution to “unhiding/toggling” the hidden frontmatter?

here my script to toggle Frontmatter (Ctrl-Alt-f) for all pages or to auto-hide Frontmatter for a particular page by specifying

---
hide-frontmatter: true
---

in the corresponding Frontmatter section.

-- Toggle frontmatter visibility with auto-hide support
-- Auto-hides if page has hide-frontmatter: true in frontmatter

local styleId = 'hide-frontmatter-css'
local style = '.sb-frontmatter, .sb-line-frontmatter-outside, .sb-frontmatter-marker { display: none !important; }'

local function scrollToFrontmatter()
  local fm = js.window.document.querySelector('.sb-frontmatter, .sb-line-frontmatter-outside')
  if fm then
    fm.scrollIntoView({ behavior = 'smooth', block = 'start' })
  end
end

local function setVisibility(hidden)
  local el = js.window.document.getElementById(styleId)
  if hidden and not el then
    js.window.document.head.insertAdjacentHTML('beforeend', '<style id="' .. styleId .. '">' .. style .. '</style>')
  elseif not hidden and el then
    el.remove()
    js.window.setTimeout(scrollToFrontmatter, 50)
  end
end

local function pageHasHideFrontmatter()
  local pageName = editor.getCurrentPage()
  if not pageName then return false end
  
  local success, pageText = pcall(function() return space.readPage(pageName) end)
  if not success or not pageText then return false end
  
  -- Find frontmatter block boundaries
  local startPos = string.find(pageText, "^%-%-%-")
  if not startPos then return false end
  
  -- Find the end of frontmatter (second ---)
  local endPos = string.find(pageText, "[\r\n]+%-%-%-", startPos + 3)
  if not endPos then return false end
  
  -- Extract frontmatter content (between first --- and second ---)
  local fm = string.sub(pageText, startPos + 3, endPos - 1)
  fm = string.gsub(fm, "^%s*[\r\n]+", "")  -- Remove leading whitespace/newlines
  fm = string.gsub(fm, "[\r\n]+%s*$", "")  -- Remove trailing whitespace/newlines
  
  -- Check for hide-frontmatter: true (case-insensitive)
  return string.match(string.lower(fm), "hide%-frontmatter%s*:%s*true") ~= nil
end

function toggleFrontmatterVisibility()
  local isHidden = js.window.document.getElementById(styleId) ~= nil
  setVisibility(not isHidden)
  js.window.localStorage.setItem('frontmatterHidden', tostring(not isHidden))
end

local function updateVisibility()
  local shouldHide = pageHasHideFrontmatter() or 
                     js.window.localStorage.getItem('frontmatterHidden') == 'true'
  setVisibility(shouldHide)
end

command.define {
  name = "Toggle Frontmatter Visibility",
  key = "Ctrl-Alt-f",
  mac = "Cmd-Alt-f",
  run = toggleFrontmatterVisibility
}

local function delayedUpdate(delay)
  js.window.setTimeout(updateVisibility, delay or 100)
end

event.listen { name = 'editor:pageLoaded', run = function() delayedUpdate(100) end }
event.listen { name = 'editor:pageSaved', run = function() delayedUpdate(100) end }
event.listen { name = 'system:ready', run = function() delayedUpdate(200) end }
1 Like

What an interesting mix of space-lua / CSS / JS :exploding_head:

Instead of the regex searching and checking for a text match key-value pair (hide-frontmatter: true)
you could have simply used the index.extractFrontmatter() function, this return exactly the value “true” if inserted.

but other than that a nice implementaion :clap:
will definitely use it in the future :wink:
Thanks!

1 Like