defter 0.8.0

Self-hosted Zotero sync and file storage server, with metadata in SQLite and attachments in S3.
<!DOCTYPE html>
<html lang="{{ layout.text.lang }}">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  {%- block meta %}
  <meta name="robots" content="noindex">
  {%- endblock %}
  {#- htmx reads its configuration here, so no inline script is needed. Under
      the strict CSP it may not eval code or inject an inline style sheet. -#}
  <meta name="htmx-config" content='{"allowEval": false, "includeIndicatorStyles": false, "historyRestoreAsHxRequest": false, "scrollIntoViewOnBoost": false}'>
  <title>{% block title %}{% endblock %} — defter</title>
  {#- The logo, in a fixed teal: a favicon cannot follow the page's theme. -#}
  <link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cg fill='none'%3E%3Cpath fill='%230d9488' d='M2.46 3.79h5.23v5.79H2.46zm5.23 0h5.23v5.79H7.69z' opacity='.2'/%3E%3Crect width='5.23' height='5.77' x='12.92' y='4.51' fill='%230d9488' opacity='.2' rx='2' transform='rotate%28-15 12.92 4.5%29'/%3E%3Crect width='5.23' height='16.92' x='2.46' y='3.54' stroke='%230d9488' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' rx='1.5'/%3E%3Crect width='5.23' height='16.92' x='7.69' y='3.54' stroke='%230d9488' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' rx='1.5'/%3E%3Crect width='5.23' height='16.92' x='12.92' y='4.51' stroke='%230d9488' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' rx='1.5' transform='rotate%28-15 12.92 4.5%29'/%3E%3Cpath stroke='%230d9488' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M2.63 9.16h10.11m1.89 0l4.36-1.32'/%3E%3C/g%3E%3C/svg%3E">
  <link rel="stylesheet" href="{{ layout.stylesheet }}">
  <script defer src="{{ layout.script }}"></script>
  {#- Before first paint, so a dark page does not flash light. -#}
  <script nonce="{{ layout.nonce }}">
    document.documentElement.classList.add('js')
    if (localStorage.getItem('defter-dark') === 'true' || (localStorage.getItem('defter-dark') === null && matchMedia('(prefers-color-scheme: dark)').matches)) document.documentElement.classList.add('dark')
  </script>
</head>
<body>
<div class="min-h-screen flex flex-col">
  <header class="border-b{% if layout.wide %} bg-muted sticky top-0 z-40{% endif %}">
    <div class="mx-auto {% if layout.wide %}max-w-5xl{% else %}max-w-4xl{% endif %} px-4 py-2 md:py-0 md:h-14 flex flex-wrap md:flex-nowrap items-center justify-between gap-x-6 gap-y-2">
      <div class="flex items-baseline gap-3 min-w-0">
        <a href="/" class="flex items-baseline gap-2 font-semibold tracking-tight">
          {% include "logo.html" %}defter
        </a>
        <span class="hidden md:inline text-sm text-muted-foreground">{{ layout.text.tagline }}</span>
      </div>
      <div class="flex items-center gap-1 md:ml-0">
        {%- block header_actions %}
        <form method="get" action="{{ layout.lang_action }}" class="flex items-center gap-1">
          {%- for field in layout.lang_fields %}
          <input type="hidden" name="{{ field.0 }}" value="{{ field.1 }}">
          {%- endfor %}
          <span class="relative inline-flex">
          <select name="lang" data-language data-language-submit aria-label="{{ layout.text.language }}" class="appearance-none rounded-md border bg-background pl-2.5 pr-8 py-1 text-sm">
            {%- for option in layout.languages %}
            <option value="{{ option.code() }}"{% if option.code() == layout.lang.code() %} selected{% endif %}>{{ option.name() }}</option>
            {%- endfor %}
          </select>
          {% include "select-caret.html" %}
          </span>
          <noscript><button type="submit" class="rounded-md border px-2 py-1 text-sm hover:bg-muted">{{ layout.text.apply }}</button></noscript>
        </form>
        {%- endblock %}
        <button type="button" data-theme-toggle aria-label="{{ layout.text.toggle_dark }}"
                class="inline-flex items-center justify-center rounded-md px-2 py-1.5 text-muted-foreground hover:bg-muted hover:text-foreground">
          <span class="dark:hidden">{% include "moon-icon.html" %}</span><span class="hidden dark:inline">{% include "sun-icon.html" %}</span>
        </button>
        {%- if let Some(name) = layout.user %}
        <details class="relative" data-account-menu>
          <summary aria-label="{{ layout.text.account_menu }}" class="flex cursor-pointer list-none items-center gap-1 rounded-md px-3 py-1.5 text-sm hover:bg-muted [&::-webkit-details-marker]:hidden">
            <span class="max-w-[12rem] truncate">{{ name }}</span><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true" class="w-3.5 h-3.5 text-muted-foreground"><path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5"/></svg>
          </summary>
          <div class="absolute right-0 z-50 mt-1 w-44 rounded-md border bg-background py-1 shadow-md">
            <a href="/ui/settings" class="block px-3 py-1.5 text-sm hover:bg-muted">{{ layout.text.settings }}</a>
            {%- if layout.admin %}
            <a href="/ui/admin" class="block px-3 py-1.5 text-sm hover:bg-muted">{{ layout.text.admin_settings }}</a>
            {%- endif %}
            <form method="post" action="/ui/logout">
              <button type="submit" class="block w-full px-3 py-1.5 text-left text-sm hover:bg-muted">{{ layout.text.sign_out }}</button>
            </form>
          </div>
        </details>
        {%- else if layout.sign_in_link %}
        <a href="/ui/login" class="rounded-md px-3 py-1.5 text-sm hover:bg-muted">{{ layout.text.sign_in }}</a>
        {%- endif %}
      </div>
    </div>
  </header>
  <main class="flex-1 mx-auto w-full {% if layout.wide %}max-w-5xl{% else %}max-w-4xl{% endif %} px-4 py-8">
    {% block content %}{% endblock %}
  </main>
  <footer class="border-t">
    <div class="mx-auto {% if layout.wide %}max-w-5xl{% else %}max-w-4xl{% endif %} px-4 py-4 text-xs text-muted-foreground leading-5 flex items-start justify-between">
      <span class="[&_a]:hover:text-foreground [&_a]:transition-colors">
        <span class="block">{{ layout.text.built_with }} <a href="https://crates.io/crates/defter" target="_blank" rel="noreferrer">defter</a> {{ layout.text.by }} <a href="https://front-matter.de" target="_blank" rel="noreferrer">Front&nbsp;Matter</a>, {{ layout.text.software }} <a href="https://opensource.org/license/mit" target="_blank" rel="noreferrer">{{ layout.text.mit_license }}</a>.</span>
      </span>
      <span class="flex items-center gap-3 pl-3 shrink-0">
        <a href="mailto:info@front-matter.de" aria-label="Email" class="hover:text-foreground transition-colors">
          <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect width="20" height="16" x="2" y="4" rx="2"/><path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"/></svg>
        </a>
        <a href="https://hachyderm.io/@mfenner" target="_blank" rel="noreferrer" aria-label="Mastodon" class="hover:text-foreground transition-colors">
          <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M11.19 12.195c2.016-.24 3.77-1.475 3.99-2.603.348-1.778.32-4.339.32-4.339 0-3.47-2.286-4.488-2.286-4.488C12.062.238 10.083.017 8.027 0h-.05C5.92.017 3.942.238 2.79.765c0 0-2.285 1.017-2.285 4.488l-.002.662c-.004.64-.007 1.35.011 2.091.083 3.394.626 6.74 3.78 7.57 1.454.383 2.703.463 3.709.408 1.823-.1 2.847-.647 2.847-.647l-.06-1.317s-1.303.41-2.767.36c-1.45-.05-2.98-.156-3.215-1.928a4 4 0 0 1-.033-.496s1.424.346 3.228.428c1.103.05 2.137-.064 3.188-.189zm1.613-2.47H11.13v-4.08c0-.859-.364-1.295-1.091-1.295-.804 0-1.207.517-1.207 1.541v2.233H7.168V5.89c0-1.024-.403-1.541-1.207-1.541-.727 0-1.091.436-1.091 1.296v4.079H3.197V5.522q0-1.288.66-2.046c.456-.505 1.052-.764 1.793-.764.856 0 1.504.328 1.933.983L8 4.39l.417-.695c.429-.655 1.077-.983 1.934-.983.74 0 1.336.259 1.791.764q.662.757.661 2.046z"/></svg>
        </a>
      </span>
    </div>
  </footer>
</div>
</body>
</html>