#meta

Sam’s home page: a greeting, buttons for the things he does every day, and a few numbers about his space.

Implementation

home = {}

-- Widget links open pages through editor.navigate, so they work wherever the space is served
local function go(ref)
  return function()
    editor.navigate(ref)
  end
end

function home.greeting(name)
  local hour = tonumber(os.date "%H")
  local part = hour < 12 and "morning" or hour < 18 and "afternoon" or "evening"
  local date = os.date "%A" .. " " .. tonumber(os.date "%d") .. " " .. os.date "%B"
  return widget.htmlBlock(dom.div {
    class = "home home-greeting",
    dom.h1 { "Good " .. part .. ", " .. name },
    dom.p { date },
  })
end

local function action(label, command)
  return dom.button {
    class = "home-action",
    onclick = function()
      editor.invokeCommand(command)
    end,
    label,
  }
end

function home.actions()
  return widget.htmlBlock(dom.div {
    class = "home home-actions",
    action("Quick note", "Quick Note"),
    action("Today’s journal", "Journal: Today"),
  })
end

local function tile(value, label, page)
  return dom.a {
    class = "home-tile",
    onclick = go(page),
    dom.strong { tostring(value) },
    dom.span { label },
  }
end

function home.tiles()
  local books = query[
    from p = index.contentPages "book"
    select p
  ](<
    from p = index.contentPages "book"
    select p
  >)
  local reading, finished = "–", 0
  for _, b in ipairs(books) do
    if b.status == "reading" then
      reading = b.name
    end
    if b.status == "finished" then
      finished = finished + 1
    end
  end
  local openTasks = query[
    from t = index.tasks()
    where not t.done
  ](<
    from t = index.tasks()
    where not t.done
  >)
  local journalDays = query[
    from p = index.pages "journal"
    order by p.name desc
  ](<
    from p = index.pages "journal"
    order by p.name desc
  >)
  return widget.htmlBlock(dom.div {
    class = "home home-tiles",
    tile(#openTasks, "open tasks", "index"),
    tile(finished, "books finished", "Reading List"),
    tile(#journalDays, "journal days", journalDays[1].name),
    dom.a {
      class = "home-tile home-tile-wide",
      onclick = go(reading),
      dom.span { "Reading now" },
      dom.em { reading },
    },
  })
end

Style

/* Sam’s accent colour, used by SilverBullet’s own UI too */
html:root {
  --ui-accent-color: #2563eb;
}

/* Let the home widgets sit on the page without the usual widget frame */
.sb-lua-directive-block:has(.home) {
  border: none !important;
  background: none !important;
}

/* Greeting and date */
.home-greeting h1 {
  font-size: 2.2em;
  font-weight: 600;
  margin: 0.2em 0 0;
  color: #172554;
}
.home-greeting p {
  margin: 0.1em 0 0;
  color: #78716c;
}

/* The row of buttons */
.home-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 4px 0;
}
.home-action {
  font: inherit;
  font-size: 0.85em;
  padding: 6px 14px;
  border-radius: 999px;
  border: 1px solid #bfdbfe;
  background: #eff6ff;
  color: #1e40af;
  cursor: pointer;
}

/* Stat tiles, with a wide “Reading now” tile below them */
.home-tiles {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
}
.home-tile {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 12px 14px;
  border-radius: 12px;
  background: linear-gradient(135deg, #eff6ff, #dbeafe);
  color: #1e3a8a !important;
  text-decoration: none !important;
}
.home-tile strong {
  font-size: 2em;
  line-height: 1;
}
.home-tile span {
  font-size: 0.8em;
  color: #1d4ed8;
}
.home-tile-wide {
  grid-column: 1 / -1;
  flex-direction: row;
  align-items: baseline;
  gap: 10px;
  background: linear-gradient(135deg, #2563eb, #3b82f6);
}
.home-tile-wide span,
.home-tile-wide em {
  color: #fff !important;
}
.home-tile-wide em {
  font-size: 1.05em;
  font-weight: 600;
}

/* Widget links navigate on click, so give them a pointer */
.home a {
  cursor: pointer;
}