12.6 KiB · text History e832e77
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Ctext y='.9em' font-size='88'%3E%E2%9A%99%EF%B8%8F%3C/text%3E%3C/svg%3E">
  <title>{% block title %}devendra{% endblock %}</title>

  <!-- Theme: set data-theme before first paint to avoid a flash. Mirrors the
       ../dms setup — the same daisyUI palettes, system default, and toggle. -->
  <script>
    (() => {
      const DARK = "afterdark", LIGHT = "her";
      const mq = window.matchMedia("(prefers-color-scheme: dark)");
      const sys = () => (mq.matches ? DARK : LIGHT);
      window.devendraSetTheme = (t) => {
        if (t === "system") {
          localStorage.removeItem("devendra:theme");
          document.documentElement.dataset.theme = sys();
        } else {
          localStorage.setItem("devendra:theme", t);
          document.documentElement.dataset.theme = t;
        }
        // Drop focus so the (focus-driven) dropdown closes after a pick.
        if (document.activeElement && document.activeElement.blur) document.activeElement.blur();
      };
      document.documentElement.dataset.theme = localStorage.getItem("devendra:theme") || sys();
      mq.addEventListener("change", () => {
        if (!localStorage.getItem("devendra:theme")) document.documentElement.dataset.theme = sys();
      });
    })();
  </script>

  <style>
    @font-face { font-family: "B612 Mono"; font-style: normal; font-weight: 400;
      font-display: swap; src: url("/assets/fonts/b612-mono-400.woff2") format("woff2"); }
    @font-face { font-family: "B612 Mono"; font-style: normal; font-weight: 700;
      font-display: swap; src: url("/assets/fonts/b612-mono-700.woff2") format("woff2"); }
    @font-face { font-family: "Font Awesome 6 Free"; font-style: normal; font-weight: 900;
      font-display: block; src: url("/assets/fonts/fa-solid-900.woff2") format("woff2"); }

    /* FontAwesome solid — same icon component as ../dms. */
    .fas, .fa-solid { font-family: "Font Awesome 6 Free"; font-weight: 900;
      font-style: normal; display: inline-block; line-height: 1; }
    .fa-fw { width: 1.25em; text-align: center; }
    .fa-server:before        { content: "\f233"; }
    .fa-puzzle-piece:before  { content: "\f12e"; }
    .fa-ticket:before        { content: "\f145"; }
    .fa-upload:before        { content: "\f093"; }
    .fa-palette:before       { content: "\f53f"; }
    .fa-display:before       { content: "\e163"; }
    .fa-pen:before           { content: "\f304"; }
    .fa-trash:before         { content: "\f1f8"; }
    .fa-plus:before          { content: "\2b"; }
    .fa-arrow-left:before    { content: "\f060"; }
    .fa-circle-check:before  { content: "\f058"; }
    .fa-clock:before         { content: "\f017"; }
    .fa-microchip:before     { content: "\f2db"; }

    /* daisyUI-derived palettes: each theme sets four base colours; the rest is
       derived with color-mix so surfaces/borders/muted follow automatically.
       Defaults (:root) match the "her" theme. */
    :root {
      --base-100: #651d1d; --bc: #f0e6e6; --primary: #d5abab; --accent: #fef3c7;
      --accent-fg: #1a1a1a;
      --ok: #34d399; --warn: #fbbf24; --err: #f87171; --info: #60a5fa;
    }
    [data-theme="her"]       { --base-100:#651d1d; --bc:#f0e6e6; --primary:#d5abab; --accent:#fef3c7; }
    [data-theme="afterdark"] { --base-100:#201d65; --bc:#e7e6f4; --primary:#acabd5; --accent:#fef3c7; }
    [data-theme="forest"]    { --base-100:#052e16; --bc:#dcf5e6; --primary:#86efac; --accent:#fef3c7; }
    [data-theme="sky"]       { --base-100:#082f49; --bc:#dceafb; --primary:#7dd3fc; --accent:#fef3c7; }
    [data-theme="clays"]     { --base-100:#451a03; --bc:#f6e8dc; --primary:#f59e0b; --accent:#fef3c7; }
    [data-theme="stones"]    { --base-100:#292524; --bc:#e9e7e5; --primary:#9ca3af; --accent:#fef3c7; }
    [data-theme="lofi"]      { --base-100:#ffffff; --bc:#0d0d0d; --primary:#0d0d0d; --accent:#0d0d0d; --accent-fg:#f5f5f5; }
    [data-theme="black"]     { --base-100:#000000; --bc:#cfcfcf; --primary:#4b4b4b; --accent:#6b6b6b; --accent-fg:#f5f5f5; }

    :root {
      --bg: var(--base-100);
      --fg: var(--bc);
      --surface:  color-mix(in srgb, var(--base-100) 90%, var(--bc));
      --surface2: color-mix(in srgb, var(--base-100) 82%, var(--bc));
      --border:   color-mix(in srgb, var(--base-100) 74%, var(--bc));
      --muted:    color-mix(in srgb, var(--bc) 60%, var(--base-100));
      --primary-fg: color-mix(in srgb, var(--primary) 92%, black);
    }

    * { box-sizing: border-box; }
    html, body { min-height: 100%; }
    body {
      margin: 0; color: var(--fg); background: var(--bg);
      font: 15px/1.55 "B612 Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
    }

    /* daisyUI navbar component (same structure/classes as ../dms). */
    .navbar { display: flex; align-items: center; gap: .25rem; min-height: 4rem;
      padding: .5rem 1rem; border-bottom: 1px solid var(--border); background: var(--surface);
      position: sticky; top: 0; z-index: 20; }
    .navbar .flex-1 { flex: 1 1 auto; }
    .navbar .flex-none { flex: 0 0 auto; }
    /* Only the top-level nav list is a flex row — must NOT match the nested
       .dropdown-content menu (that would force it visible + horizontal). */
    .navbar > .flex-none > ul { list-style: none; display: flex; flex-direction: row;
      align-items: center; gap: .25rem; margin: 0; padding: 0; }
    .navbar > .flex-none > ul > li { list-style: none; }
    .navbar a, .navbar form { text-decoration: none; }
    .brand { font-weight: 700; letter-spacing: .16em; color: var(--accent);
      text-transform: lowercase; }

    /* Base button (daisyUI sizing: 3rem tall). Variants below must come after so
       they win on source order. */
    button, .btn { font: inherit; cursor: pointer; display: inline-flex; align-items: center;
      justify-content: center; gap: .5rem; height: 3rem; min-height: 3rem; padding: 0 1rem;
      border-radius: 8px; border: 1px solid var(--primary); background: var(--primary);
      color: var(--primary-fg); font-weight: 700; font-size: .9rem; white-space: nowrap; }
    button:hover, .btn:hover { filter: brightness(1.08); }
    .btn.secondary { background: transparent; color: var(--primary); }

    /* daisyUI button variants used by the nav. */
    .btn-ghost { background: transparent; border-color: transparent; color: var(--fg); }
    .btn-ghost:hover { background: color-mix(in srgb, var(--fg) 12%, transparent); filter: none; }
    .btn-accent { background: var(--accent); border-color: var(--accent); color: var(--accent-fg); }
    .btn-sm { height: 2rem; min-height: 2rem; padding: 0 .75rem; font-size: .8rem; font-weight: 400; gap: .4rem; }
    .divider-horizontal { display: inline-block; width: 1px; height: 1.3rem;
      background: var(--border); margin: 0 .3rem; }
    @media (max-width: 640px) { .sm-inline { display: none; } }

    main { max-width: 900px; margin: 1.6rem auto; padding: 0 1.2rem; }
    h1 { font-size: 1.35rem; } h2 { font-size: 1.05rem; margin-top: 1.6rem; }
    a { color: var(--primary); }

    table { width: 100%; border-collapse: collapse; margin: 1rem 0; }
    th, td { text-align: left; padding: .5rem .6rem; border-bottom: 1px solid var(--border); }
    th { color: var(--muted); font-weight: 700; font-size: .8rem; text-transform: uppercase; letter-spacing: .04em; }

    .card { background: var(--surface); border: 1px solid var(--border); border-radius: 10px;
      padding: 1rem 1.2rem; margin: 1rem 0; }
    .muted { color: var(--muted); }

    .pill { display: inline-block; padding: .1rem .55rem; border-radius: 999px; font-size: .78rem; font-weight: 700; }
    .pill.ok      { background: color-mix(in srgb, var(--ok) 22%, transparent);   color: var(--ok); }
    .pill.changed { background: color-mix(in srgb, var(--primary) 26%, transparent); color: var(--primary); }
    .pill.failed  { background: color-mix(in srgb, var(--err) 24%, transparent);  color: var(--err); }
    .pill.none    { background: color-mix(in srgb, var(--muted) 22%, transparent); color: var(--muted); }

    /* Title badge (uptime / load next to the device title). */
    .badge { display: inline-flex; align-items: center; gap: .4rem; vertical-align: middle;
      margin-left: .6rem; padding: .2rem .6rem; border-radius: 999px; font-size: .72rem;
      font-weight: 400; color: var(--muted); background: var(--surface2);
      border: 1px solid var(--border); white-space: nowrap; }
    .badge i { color: var(--primary); }
    .badge-sep { opacity: .5; }

    /* Live service-health traffic light (next to the State column). */
    .dot { display: inline-block; width: .65rem; height: .65rem; border-radius: 999px;
      vertical-align: middle; margin-left: .35rem; box-shadow: 0 0 0 1px color-mix(in srgb, var(--bc) 25%, transparent); }
    .dot.ok      { background: var(--ok); }
    .dot.pending { background: var(--warn); }
    .dot.bad     { background: var(--err); }
    .dot.unknown { background: var(--muted); }

    input[type=text], select, textarea { font-family: inherit; width: 100%; padding: .5rem;
      border: 1px solid var(--border); border-radius: 7px; background: var(--surface2); color: var(--fg); }
    select { width: auto; }
    textarea { min-height: 22rem; }

    pre, code { font-family: inherit; }
    pre.code, .code { background: var(--surface2); padding: 1rem; border-radius: 8px; overflow-x: auto; }
    code { background: color-mix(in srgb, var(--base-100) 82%, var(--bc)); padding: .05rem .3rem; border-radius: 4px; }
    pre.code code { background: none; padding: 0; }
    .break { word-break: break-all; }

    /* daisyUI dropdown behaviour (appearance comes from the utility classes on
       the elements, exactly as ../dms writes them). Focus-driven: open while
       the trigger or menu holds focus. */
    .dropdown { position: relative; display: inline-block; }
    .dropdown-content { position: absolute; display: none; }
    .dropdown-end > .dropdown-content { right: 0; }
    .dropdown:focus-within > .dropdown-content { display: block; }
    .dropdown > [role="button"] { cursor: pointer; user-select: none; }
    .menu { list-style: none; margin: 0; }
    .menu li { list-style: none; }
    .menu li > button, .menu li > a { display: flex; align-items: center; gap: .5rem; width: 100%;
      background: transparent; color: var(--fg); border: 0; padding: .45rem .6rem; border-radius: .5rem;
      font: inherit; font-size: .85rem; text-align: left; text-decoration: none; cursor: pointer; }
    .menu li > button:hover, .menu li > a:hover { background: color-mix(in srgb, var(--fg) 10%, transparent); }

    /* Minimal Tailwind/daisyUI utilities used by the copied ../dms markup. */
    .hidden { display: none; }
    @media (min-width: 640px) { .sm\:inline { display: inline; } }
    .flex { display: flex; }
    .items-center { align-items: center; }
    .justify-between { justify-content: space-between; }
    .gap-2 { gap: .5rem; }
    .capitalize { text-transform: capitalize; }
    .z-10 { z-index: 10; }
    .mt-1 { margin-top: .25rem; }
    .p-1 { padding: .25rem; }
    .w-44 { width: 11rem; }
    .rounded-box { border-radius: 1rem; }
    .rounded-full { border-radius: 9999px; }
    .size-3 { width: .75rem; height: .75rem; }
    .border { border-width: 1px; border-style: solid; }
    .bg-base-200 { background: var(--surface); }
    .border-base-300 { border-color: var(--border); }
    .border-base-content\/20 { border-color: color-mix(in srgb, var(--bc) 20%, transparent); }
    .shadow-lg { box-shadow: 0 10px 15px -3px rgba(0,0,0,.3), 0 4px 6px -4px rgba(0,0,0,.25); }
  </style>
</head>
<body>
  <header class="navbar">
    <div class="flex-1">
      <a href="/"><span class="brand">devendra</span></a>
    </div>
    <div class="flex-none">
      <ul>
        <li><a href="/" class="btn btn-ghost btn-sm"><i class="fas fa-server fa-fw"></i><span class="sm-inline">Hosts</span></a></li>
        <li><a href="/fragments" class="btn btn-ghost btn-sm"><i class="fas fa-puzzle-piece fa-fw"></i><span class="sm-inline">Fragments</span></a></li>
        <li><a href="/ticket" class="btn btn-ghost btn-sm"><i class="fas fa-ticket fa-fw"></i><span class="sm-inline">Ticket</span></a></li>
        <li><span class="divider-horizontal"></span></li>
        <li>{% include "_theme_toggle.html" %}</li>
        <li>
          <form method="post" action="/publish" style="margin:0">
            <button type="submit" class="btn btn-sm btn-accent"><i class="fas fa-upload fa-fw"></i><span class="sm-inline">Publish</span></button>
          </form>
        </li>
      </ul>
    </div>
  </header>
  <main>{% block content %}{% endblock %}</main>
</body>
</html>