From 90154111853dbecbb7b780ea540c4ade2e09a1cc Mon Sep 17 00:00:00 2001 From: Tam Nguyen Duc <1218621+tamnd@users.noreply.github.com> Date: Mon, 15 Jun 2026 00:00:30 +0700 Subject: [PATCH] Restyle the web console as a flat monospace terminal Give ant serve a console vibe: one monospace face throughout, square corners everywhere, and flat panels that float on a tinted canvas with no shadows, no dividing rules, and no rounding. - Embed Geist Mono (variable, latin subset) and serve it from the binary, so the console stays a single static artifact with no CDN. The face is preloaded and registered as font/woff2; --sans now points at the mono stack so headings, body, and chrome all share it. - Drop every box-shadow and the shadow tokens, and zero --radius, --radius-sm, and --radius-lg. The only curve left is the loading spinner, which has to be a circle to read as one. - Turn borders into tone: inputs, chips, badges, folders, cards, and hovers separate by background fill rather than rules. Remove the border-bottom on the topbar, the border-right on the sidebar, and the table cell rules. - Tune type for monospace: 14px base, drop the Inter feature settings and the negative tracking that looked cramped on a fixed-width face. --- web/assets/fonts/geist-mono-LICENSE | 93 ++++++++ web/assets/fonts/geist-mono-latin.woff2 | Bin 0 -> 29896 bytes web/assets/styles.css | 273 +++++++++++++----------- web/console.go | 8 +- web/embed.go | 16 +- web/templates/base.html | 1 + 6 files changed, 254 insertions(+), 137 deletions(-) create mode 100644 web/assets/fonts/geist-mono-LICENSE create mode 100644 web/assets/fonts/geist-mono-latin.woff2 diff --git a/web/assets/fonts/geist-mono-LICENSE b/web/assets/fonts/geist-mono-LICENSE new file mode 100644 index 0000000..2e4aef1 --- /dev/null +++ b/web/assets/fonts/geist-mono-LICENSE @@ -0,0 +1,93 @@ +Copyright 2024 The Geist Project Authors (https://github.com/vercel/geist-font.git) GeistMono-Italic[wght].ttf: Copyright 2024 The Geist Project Authors (https://github.com/vercel/geist-font.git) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/web/assets/fonts/geist-mono-latin.woff2 b/web/assets/fonts/geist-mono-latin.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..ab3d5ee90184e202703f4bc6845a5f570e6192b9 GIT binary patch literal 29896 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 literal 0 HcmV?d00001 diff --git a/web/assets/styles.css b/web/assets/styles.css index b18fbbd..630cae1 100644 --- a/web/assets/styles.css +++ b/web/assets/styles.css @@ -1,50 +1,60 @@ -/* ant console — a hand-written stylesheet in the shadcn/ui idiom: HSL design - tokens, a light/dark theme switched by [data-theme], soft cards, subtle rings - and a restrained accent. No framework, no build step (8000_ant_serve §3). */ +/* ant console — a hand-written stylesheet for a flat, square, monospace console: + HSL design tokens, a light/dark theme switched by [data-theme], panels that + float on a tinted canvas instead of being boxed in, filled inputs, sharp (zero + radius) corners, and Geist Mono as the one UI face for a terminal vibe. No + shadows, no dividing rules, no rounding, no framework, no build step, no CDN: + the font is embedded and served from the binary (8000_ant_serve §2, §3). */ + +@font-face { + font-family: "GeistMono"; + font-style: normal; + font-weight: 100 900; + font-display: swap; + src: url("/assets/fonts/geist-mono-latin.woff2") format("woff2"); +} /* ---- tokens --------------------------------------------------------------- */ :root { - --bg: 0 0% 100%; + --bg: 240 6% 97%; /* page canvas: a faint cool gray */ --fg: 240 10% 9%; - --muted: 240 4% 46%; - --card: 0 0% 100%; + --muted: 240 4% 44%; + --card: 0 0% 100%; /* panels read by floating white on the canvas */ --card-fg: 240 10% 9%; - --border: 240 6% 90%; - --input: 240 6% 90%; - --ring: 240 5% 65%; - --accent-bg: 240 5% 96%; + --border: 240 6% 90%; /* the few hairlines we still keep (focus, skip link) */ + --input: 240 5% 92%; + --ring: 240 5% 60%; + --accent-bg: 240 5% 93%; /* tinted fills: chips, badges, code, inputs, hovers */ + --accent-hover: 240 5% 88%; --accent-fg: 240 6% 20%; --primary: 240 6% 10%; --primary-fg: 0 0% 98%; --danger: 0 72% 51%; --ok: 142 71% 40%; --warn: 38 92% 45%; - --radius: 12px; - --radius-sm: 8px; - --shadow: 0 1px 2px hsl(240 6% 10% / .06), 0 8px 24px -12px hsl(240 6% 10% / .14); - --shadow-sm: 0 1px 2px hsl(240 6% 10% / .07); + --radius: 0; /* console look: every corner is square */ + --radius-sm: 0; + --radius-lg: 0; --sidebar-w: 248px; - --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace; - --sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; + --mono: "GeistMono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; + --sans: var(--mono); /* one monospace family throughout for the terminal vibe */ } [data-theme="dark"] { - --bg: 240 10% 6%; - --fg: 0 0% 96%; - --muted: 240 5% 60%; - --card: 240 9% 9%; - --card-fg: 0 0% 96%; - --border: 240 5% 17%; - --input: 240 5% 18%; - --ring: 240 5% 40%; - --accent-bg: 240 5% 15%; + --bg: 240 10% 5%; + --fg: 0 0% 95%; + --muted: 240 5% 58%; + --card: 240 8% 10%; /* panels sit one step lighter than the canvas */ + --card-fg: 0 0% 95%; + --border: 240 5% 16%; + --input: 240 6% 16%; + --ring: 240 5% 38%; + --accent-bg: 240 6% 15%; + --accent-hover: 240 6% 20%; --accent-fg: 0 0% 92%; --primary: 0 0% 96%; --primary-fg: 240 10% 9%; - --danger: 0 72% 58%; - --ok: 142 64% 50%; - --warn: 38 92% 56%; - --shadow: 0 1px 2px hsl(0 0% 0% / .3), 0 8px 30px -12px hsl(0 0% 0% / .55); - --shadow-sm: 0 1px 2px hsl(0 0% 0% / .35); + --danger: 0 72% 60%; + --ok: 142 60% 52%; + --warn: 38 92% 58%; } /* ---- reset ---------------------------------------------------------------- */ @@ -55,23 +65,25 @@ body { font-family: var(--sans); background: hsl(var(--bg)); color: hsl(var(--fg)); - line-height: 1.55; - font-size: 15px; + line-height: 1.5; + font-size: 14px; -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + text-rendering: optimizeLegibility; } a { color: inherit; text-decoration: none; } img { max-width: 100%; display: block; } button, input, select, textarea { font: inherit; color: inherit; } -code { font-family: var(--mono); font-size: .86em; } +code { font-family: var(--mono); font-size: .9em; } svg.icon { width: 18px; height: 18px; flex: none; } ::selection { background: hsl(var(--fg) / .14); } -:focus-visible { outline: 2px solid hsl(var(--ring)); outline-offset: 2px; border-radius: 4px; } +:focus-visible { outline: 2px solid hsl(var(--ring)); outline-offset: 2px; } .skip { position: absolute; left: -999px; top: 8px; z-index: 50; - background: hsl(var(--card)); padding: 8px 14px; border-radius: var(--radius-sm); - box-shadow: var(--shadow); + background: hsl(var(--card)); padding: 8px 14px; + border: 1px solid hsl(var(--border)); } .skip:focus { left: 8px; } @@ -85,18 +97,16 @@ svg.icon { width: 18px; height: 18px; flex: none; } /* ---- sidebar -------------------------------------------------------------- */ .sidebar { position: sticky; top: 0; height: 100vh; overflow-y: auto; - border-right: 1px solid hsl(var(--border)); background: hsl(var(--bg)); padding: 18px 14px; display: flex; flex-direction: column; gap: 18px; } .brand { display: flex; align-items: center; gap: 10px; padding: 6px 8px; } -.brand-mark { border-radius: 8px; } -.brand-name { font-weight: 700; font-size: 19px; letter-spacing: -.02em; } +.brand-name { font-weight: 700; font-size: 18px; letter-spacing: -.01em; } .nav { display: flex; flex-direction: column; gap: 2px; } .nav-link { display: flex; align-items: center; gap: 10px; - padding: 8px 10px; border-radius: var(--radius-sm); - color: hsl(var(--muted)); font-weight: 500; font-size: 14px; + padding: 8px 10px; + color: hsl(var(--muted)); font-weight: 500; font-size: 13.5px; transition: background .12s, color .12s; } .nav-link:hover { background: hsl(var(--accent-bg)); color: hsl(var(--fg)); } @@ -112,45 +122,47 @@ svg.icon { width: 18px; height: 18px; flex: none; } .nav-flag .icon { width: 13px; height: 13px; } .sidebar-foot { margin-top: auto; padding: 8px 10px; font-size: 12px; } -.dot { width: 9px; height: 9px; border-radius: 50%; display: inline-block; flex: none; } -.dot-lg { width: 13px; height: 13px; } +.dot { width: 8px; height: 8px; display: inline-block; flex: none; } +.dot-lg { width: 12px; height: 12px; } /* ---- topbar --------------------------------------------------------------- */ .topbar { position: sticky; top: 0; z-index: 20; display: flex; align-items: center; gap: 12px; - padding: 12px 36px; border-bottom: 1px solid hsl(var(--border)); - background: hsl(var(--bg) / .85); backdrop-filter: blur(8px); + padding: 12px 36px; + background: hsl(var(--bg) / .8); backdrop-filter: blur(10px); } .omni { flex: 1; display: flex; align-items: center; gap: 8px; - background: hsl(var(--card)); border: 1px solid hsl(var(--input)); - border-radius: 10px; padding: 0 8px 0 12px; max-width: 720px; - transition: border-color .12s, box-shadow .12s; + background: hsl(var(--accent-bg)); border: 1px solid transparent; + padding: 0 8px 0 12px; max-width: 720px; + transition: background .12s, border-color .12s; } -.omni:focus-within { border-color: hsl(var(--ring)); box-shadow: 0 0 0 3px hsl(var(--ring) / .18); } +.omni:focus-within { background: hsl(var(--card)); border-color: hsl(var(--ring)); } .omni-icon { color: hsl(var(--muted)); display: inline-flex; } .omni-input { flex: 1; border: 0; background: transparent; padding: 9px 4px; outline: none; min-width: 0; } .omni-go { border: 0; background: hsl(var(--primary)); color: hsl(var(--primary-fg)); - padding: 6px 14px; border-radius: 8px; font-weight: 600; cursor: pointer; + padding: 7px 14px; font-weight: 600; cursor: pointer; } .omni-go:hover { opacity: .9; } .findbox { display: flex; align-items: center; gap: 6px; - background: hsl(var(--card)); border: 1px solid hsl(var(--input)); - border-radius: 10px; padding: 0 6px 0 10px; + background: hsl(var(--accent-bg)); border: 1px solid transparent; + padding: 0 6px 0 10px; + transition: background .12s, border-color .12s; } -.findbox:focus-within { border-color: hsl(var(--ring)); } +.findbox:focus-within { background: hsl(var(--card)); border-color: hsl(var(--ring)); } .findbox-icon { color: hsl(var(--muted)); display: inline-flex; } .findbox-input { border: 0; background: transparent; padding: 8px 2px; outline: none; width: 130px; } .findbox-on { border: 0; background: transparent; outline: none; cursor: pointer; padding: 6px 2px; color: hsl(var(--muted)); } .icon-btn { display: inline-flex; align-items: center; justify-content: center; - width: 38px; height: 38px; border-radius: 9px; cursor: pointer; - background: transparent; border: 1px solid transparent; color: hsl(var(--muted)); + width: 38px; height: 38px; cursor: pointer; + background: transparent; border: 0; color: hsl(var(--muted)); + transition: background .12s, color .12s; } .icon-btn:hover { background: hsl(var(--accent-bg)); color: hsl(var(--fg)); } .menu-toggle { display: none; } @@ -161,7 +173,7 @@ svg.icon { width: 18px; height: 18px; flex: none; } /* ---- footer --------------------------------------------------------------- */ .foot { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; - padding: 18px 36px; border-top: 1px solid hsl(var(--border)); + padding: 18px 36px; color: hsl(var(--muted)); font-size: 13px; } .foot-link:hover { color: hsl(var(--fg)); } @@ -170,37 +182,34 @@ svg.icon { width: 18px; height: 18px; flex: none; } /* ---- typography ----------------------------------------------------------- */ .page-head { margin-bottom: 22px; } .page-head-row { display: flex; justify-content: space-between; align-items: flex-end; gap: 16px; flex-wrap: wrap; } -.page-title { font-size: 26px; font-weight: 700; letter-spacing: -.02em; } +.page-title { font-size: 24px; font-weight: 700; letter-spacing: -.01em; } .page-sub { color: hsl(var(--muted)); margin-top: 4px; } -.section-title { font-size: 14px; font-weight: 600; color: hsl(var(--muted)); text-transform: uppercase; letter-spacing: .06em; margin: 28px 0 14px; } -.lead { font-size: 17px; color: hsl(var(--muted)); margin: 6px 0 18px; max-width: 70ch; } +.section-title { font-size: 13px; font-weight: 600; color: hsl(var(--muted)); text-transform: uppercase; letter-spacing: .08em; margin: 28px 0 14px; } +.lead { font-size: 16px; color: hsl(var(--muted)); margin: 6px 0 18px; max-width: 76ch; } /* ---- cards ---------------------------------------------------------------- */ .card { background: hsl(var(--card)); color: hsl(var(--card-fg)); - border: 1px solid hsl(var(--border)); border-radius: var(--radius); - padding: 18px; box-shadow: var(--shadow-sm); + padding: 18px; } -.card-title { font-weight: 600; font-size: 13px; text-transform: uppercase; letter-spacing: .05em; color: hsl(var(--muted)); margin-bottom: 12px; } +.card-title { font-weight: 600; font-size: 12px; text-transform: uppercase; letter-spacing: .08em; color: hsl(var(--muted)); margin-bottom: 12px; } .grid { display: grid; gap: 14px; } .grid-domains { grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); } .grid-cards { grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); } /* ---- hero ----------------------------------------------------------------- */ .hero { - border: 1px solid hsl(var(--border)); border-radius: 16px; padding: 34px; margin-bottom: 8px; background: radial-gradient(900px 300px at 12% -20%, hsl(var(--accent-bg)), transparent), hsl(var(--card)); } -.hero-title { font-size: 32px; font-weight: 800; letter-spacing: -.03em; } -.hero-sub { color: hsl(var(--muted)); margin-top: 10px; max-width: 64ch; } +.hero-title { font-size: 30px; font-weight: 800; letter-spacing: -.02em; } +.hero-sub { color: hsl(var(--muted)); margin-top: 10px; max-width: 70ch; } .hero-stats { display: flex; gap: 14px; margin-top: 22px; flex-wrap: wrap; } .stat { display: inline-flex; align-items: center; gap: 10px; - border: 1px solid hsl(var(--border)); border-radius: var(--radius); - padding: 12px 16px; background: hsl(var(--bg)); + padding: 12px 16px; background: hsl(var(--accent-bg)); } .stat .icon { color: hsl(var(--muted)); } .stat-num { font-size: 20px; font-weight: 700; } @@ -208,39 +217,39 @@ svg.icon { width: 18px; height: 18px; flex: none; } .stat-path .stat-label { font-family: var(--mono); font-size: 12px; } /* ---- domain cards --------------------------------------------------------- */ -.domain-card { display: flex; flex-direction: column; gap: 10px; transition: border-color .12s, box-shadow .12s; } -.domain-card:hover { border-color: hsl(var(--ring)); box-shadow: var(--shadow); } +.domain-card { display: flex; flex-direction: column; gap: 10px; transition: background .12s; } +.domain-card:hover { background: hsl(var(--accent-bg)); } .domain-card-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; } -.domain-card-name { font-weight: 700; font-size: 17px; } +.domain-card-name { font-weight: 700; font-size: 16px; } .domain-card-name:hover { text-decoration: underline; } -.domain-card-short { color: hsl(var(--muted)); font-size: 14px; } +.domain-card-short { color: hsl(var(--muted)); font-size: 13.5px; } .domain-card-examples { display: flex; flex-wrap: wrap; gap: 6px; } /* ---- badges & chips ------------------------------------------------------- */ .badge { display: inline-flex; align-items: center; gap: 4px; - font-size: 11.5px; font-weight: 600; padding: 2px 8px; border-radius: 999px; + font-size: 11.5px; font-weight: 600; padding: 3px 8px; background: hsl(var(--accent-bg)); color: hsl(var(--accent-fg)); - border: 1px solid hsl(var(--border)); white-space: nowrap; + white-space: nowrap; } .badge-soft { font-weight: 500; } .badge-cache { color: hsl(var(--muted)); } -.badge-live { background: hsl(var(--ok) / .14); color: hsl(var(--ok)); border-color: hsl(var(--ok) / .3); } +.badge-live { background: hsl(var(--ok) / .15); color: hsl(var(--ok)); } .chip { display: inline-flex; align-items: center; gap: 5px; max-width: 100%; - font-size: 13px; padding: 4px 10px; border-radius: 8px; - background: hsl(var(--accent-bg)); border: 1px solid hsl(var(--border)); - color: hsl(var(--fg)); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; - transition: border-color .12s, background .12s; + font-size: 13px; padding: 5px 10px; + background: hsl(var(--accent-bg)); color: hsl(var(--fg)); + overflow: hidden; text-overflow: ellipsis; white-space: nowrap; + transition: background .12s; } -.chip:hover { border-color: hsl(var(--ring)); } +.chip:hover { background: hsl(var(--accent-hover)); } .chip-scheme { font-weight: 700; color: hsl(var(--muted)); } -.chip-lg { font-size: 15px; padding: 8px 14px; } +.chip-lg { font-size: 14px; padding: 8px 14px; } .chips { display: flex; flex-wrap: wrap; gap: 6px; } /* ---- record cards --------------------------------------------------------- */ -.record-card { display: flex; flex-direction: column; overflow: hidden; padding: 0; transition: border-color .12s, box-shadow .12s, transform .12s; } -.record-card:hover { border-color: hsl(var(--ring)); box-shadow: var(--shadow); transform: translateY(-1px); } +.record-card { display: flex; flex-direction: column; overflow: hidden; padding: 0; transition: background .12s; } +.record-card:hover { background: hsl(var(--accent-bg)); } .record-thumb { aspect-ratio: 16 / 9; overflow: hidden; background: hsl(var(--accent-bg)); } .record-thumb img { width: 100%; height: 100%; object-fit: cover; } .record-body { padding: 14px; display: flex; flex-direction: column; gap: 6px; } @@ -249,14 +258,14 @@ svg.icon { width: 18px; height: 18px; flex: none; } .record-snippet { color: hsl(var(--muted)); font-size: 13px; overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; } .record-uri { font-family: var(--mono); font-size: 11.5px; color: hsl(var(--muted)); margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } -.mini-card { display: flex; align-items: center; gap: 8px; padding: 12px 14px; transition: border-color .12s; } -.mini-card:hover { border-color: hsl(var(--ring)); } +.mini-card { display: flex; align-items: center; gap: 8px; padding: 12px 14px; transition: background .12s; } +.mini-card:hover { background: hsl(var(--accent-bg)); } .mini-card-label { flex: 1; font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } /* ---- resource page -------------------------------------------------------- */ .res-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 16px; flex-wrap: wrap; margin: 14px 0 22px; } .res-head-main { display: flex; gap: 12px; align-items: flex-start; } -.res-title { font-size: 24px; font-weight: 700; letter-spacing: -.02em; word-break: break-word; } +.res-title { font-size: 22px; font-weight: 700; letter-spacing: -.01em; word-break: break-word; } .res-meta { display: flex; align-items: center; gap: 8px; margin-top: 6px; flex-wrap: wrap; } .res-fetched { font-size: 13px; } .res-actions { display: flex; gap: 8px; flex-wrap: wrap; } @@ -265,64 +274,65 @@ svg.icon { width: 18px; height: 18px; flex: none; } .res-side { display: flex; flex-direction: column; gap: 16px; position: sticky; top: 78px; } /* ---- key/value lists ------------------------------------------------------ */ -.kv { display: grid; grid-template-columns: minmax(120px, 200px) 1fr; gap: 2px 18px; } +.kv { display: grid; grid-template-columns: minmax(120px, 200px) 1fr; gap: 4px 18px; } .kv-tight { grid-template-columns: 100px 1fr; gap: 6px 12px; } -.kv-key { color: hsl(var(--muted)); font-size: 13px; padding: 7px 0; border-top: 1px solid hsl(var(--border)); } -.kv-val { padding: 7px 0; border-top: 1px solid hsl(var(--border)); min-width: 0; word-break: break-word; } -.kv > .kv-key:first-of-type, .kv > .kv-val:nth-of-type(1) { border-top: 0; } -.kv-tight .kv-key, .kv-tight .kv-val { border: 0; padding: 2px 0; } +.kv-key { color: hsl(var(--muted)); font-size: 13px; padding: 6px 0; } +.kv-val { padding: 6px 0; min-width: 0; word-break: break-word; } +.kv-tight .kv-key, .kv-tight .kv-val { padding: 2px 0; } .kv-nested { grid-template-columns: minmax(100px, 160px) 1fr; gap: 0 12px; margin: 2px 0; } .value-text { white-space: pre-wrap; } .value-text.clamp { max-height: 16em; overflow: auto; display: block; } .vlist { list-style: none; display: flex; flex-direction: column; gap: 4px; } -.thumb { max-width: 160px; border-radius: 8px; border: 1px solid hsl(var(--border)); } +.thumb { max-width: 160px; } .ext { color: hsl(212 90% 48%); word-break: break-all; } [data-theme="dark"] .ext { color: hsl(212 90% 66%); } .ext:hover { text-decoration: underline; } -.ext-lg { font-size: 16px; } +.ext-lg { font-size: 15px; } .link { display: inline-flex; align-items: center; gap: 6px; } .link:hover { text-decoration: underline; } -.link-group { padding: 10px 0; border-top: 1px solid hsl(var(--border)); } -.link-group:first-child { border-top: 0; padding-top: 0; } -.link-group-field { font-size: 12px; color: hsl(var(--muted)); margin-bottom: 6px; text-transform: uppercase; letter-spacing: .04em; } +.link-group { padding: 8px 0; } +.link-group:first-child { padding-top: 0; } +.link-group-field { font-size: 12px; color: hsl(var(--muted)); margin-bottom: 6px; text-transform: uppercase; letter-spacing: .06em; } /* ---- prose (markdown body) ------------------------------------------------ */ -.prose { line-height: 1.7; } +.prose { line-height: 1.65; } .prose > * + * { margin-top: .9em; } -.prose h1, .prose h2, .prose h3 { font-weight: 700; line-height: 1.3; margin-top: 1.4em; } +.prose h1, .prose h2, .prose h3 { font-weight: 700; line-height: 1.3; margin-top: 1.4em; letter-spacing: -.01em; } .prose h1 { font-size: 1.5em; } .prose h2 { font-size: 1.3em; } .prose h3 { font-size: 1.1em; } .prose a { color: hsl(212 90% 48%); text-decoration: underline; } [data-theme="dark"] .prose a { color: hsl(212 90% 66%); } .prose ul, .prose ol { padding-left: 1.4em; } .prose blockquote { border-left: 3px solid hsl(var(--border)); padding-left: 1em; color: hsl(var(--muted)); } -.prose pre { background: hsl(var(--accent-bg)); padding: 14px; border-radius: var(--radius-sm); overflow: auto; } -.prose code { background: hsl(var(--accent-bg)); padding: .12em .4em; border-radius: 5px; } +.prose pre { background: hsl(var(--accent-bg)); padding: 14px; overflow: auto; } +.prose code { background: hsl(var(--accent-bg)); padding: .12em .4em; } .prose pre code { background: none; padding: 0; } -.prose img { border-radius: var(--radius-sm); margin: .6em 0; } +.prose img { margin: .6em 0; } .prose table { border-collapse: collapse; width: 100%; } -.prose th, .prose td { border: 1px solid hsl(var(--border)); padding: 6px 10px; text-align: left; } +.prose th { text-align: left; padding: 8px 10px; color: hsl(var(--muted)); font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: .06em; } +.prose td { padding: 8px 10px; } +.prose tbody tr:hover td { background: hsl(var(--accent-bg)); } /* ---- raw json / code ------------------------------------------------------ */ .raw summary { cursor: pointer; font-weight: 600; font-size: 13px; color: hsl(var(--muted)); } .code { font-family: var(--mono); font-size: 12.5px; line-height: 1.6; - background: hsl(var(--accent-bg)); padding: 14px; border-radius: var(--radius-sm); + background: hsl(var(--accent-bg)); padding: 14px; overflow: auto; margin-top: 12px; max-height: 520px; } /* ---- buttons -------------------------------------------------------------- */ .btn { display: inline-flex; align-items: center; gap: 7px; cursor: pointer; - padding: 8px 14px; border-radius: 9px; font-weight: 600; font-size: 14px; - background: hsl(var(--primary)); color: hsl(var(--primary-fg)); border: 1px solid transparent; - transition: opacity .12s, background .12s, border-color .12s; + padding: 8px 14px; font-weight: 600; font-size: 13.5px; + background: hsl(var(--primary)); color: hsl(var(--primary-fg)); border: 0; + transition: opacity .12s, background .12s; } .btn:hover { opacity: .9; } .btn .icon { width: 16px; height: 16px; } .btn-primary { background: hsl(var(--primary)); color: hsl(var(--primary-fg)); } -.btn-ghost { background: hsl(var(--card)); color: hsl(var(--fg)); border-color: hsl(var(--border)); } -.btn-ghost:hover { background: hsl(var(--accent-bg)); opacity: 1; border-color: hsl(var(--ring)); } +.btn-ghost { background: hsl(var(--accent-bg)); color: hsl(var(--fg)); } +.btn-ghost:hover { background: hsl(var(--accent-hover)); opacity: 1; } .inline { display: inline; } /* ---- forms ---------------------------------------------------------------- */ @@ -332,24 +342,24 @@ svg.icon { width: 18px; height: 18px; flex: none; } .field-narrow { width: 92px; } .field label { font-size: 12px; font-weight: 600; color: hsl(var(--muted)); } .field input, .field select { - border: 1px solid hsl(var(--input)); background: hsl(var(--bg)); - border-radius: 9px; padding: 9px 11px; outline: none; transition: border-color .12s, box-shadow .12s; + border: 1px solid transparent; background: hsl(var(--accent-bg)); + padding: 9px 11px; outline: none; transition: background .12s, border-color .12s; } -.field input:focus, .field select:focus { border-color: hsl(var(--ring)); box-shadow: 0 0 0 3px hsl(var(--ring) / .18); } +.field input:focus, .field select:focus { background: hsl(var(--card)); border-color: hsl(var(--ring)); } .field-action { align-self: flex-end; } .search-form { align-items: flex-end; } .result { display: flex; flex-direction: column; gap: 12px; } .result-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; } /* ---- banners -------------------------------------------------------------- */ -.banner { padding: 11px 14px; border-radius: var(--radius-sm); margin: 14px 0; font-size: 14px; border: 1px solid; } -.banner-ok { background: hsl(var(--ok) / .12); border-color: hsl(var(--ok) / .35); } -.banner-err { background: hsl(var(--danger) / .1); border-color: hsl(var(--danger) / .35); color: hsl(var(--danger)); } -.banner-warn { background: hsl(var(--warn) / .12); border-color: hsl(var(--warn) / .35); } +.banner { padding: 12px 14px; margin: 14px 0; font-size: 14px; } +.banner-ok { background: hsl(var(--ok) / .13); } +.banner-err { background: hsl(var(--danger) / .12); color: hsl(var(--danger)); } +.banner-warn { background: hsl(var(--warn) / .14); } /* ---- breadcrumbs ---------------------------------------------------------- */ -.crumbs { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; font-size: 13.5px; margin-bottom: 10px; } -.crumb { color: hsl(var(--muted)); padding: 2px 6px; border-radius: 6px; } +.crumbs { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; font-size: 13px; margin-bottom: 10px; } +.crumb { color: hsl(var(--muted)); padding: 2px 6px; } .crumb:hover { background: hsl(var(--accent-bg)); color: hsl(var(--fg)); } .crumb.is-last { color: hsl(var(--fg)); font-weight: 600; } .crumb-sep { color: hsl(var(--muted)); opacity: .6; } @@ -358,23 +368,24 @@ svg.icon { width: 18px; height: 18px; flex: none; } .folders { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 10px; } .folder { display: flex; align-items: center; gap: 10px; - border: 1px solid hsl(var(--border)); border-radius: var(--radius); - padding: 13px 14px; background: hsl(var(--card)); transition: border-color .12s, box-shadow .12s, transform .12s; + padding: 13px 14px; background: hsl(var(--card)); transition: background .12s; } -.folder:hover { border-color: hsl(var(--ring)); box-shadow: var(--shadow-sm); transform: translateY(-1px); } +.folder:hover { background: hsl(var(--accent-bg)); } .folder-icon { display: inline-flex; } .folder-name { flex: 1; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } -.folder-count { font-size: 12px; color: hsl(var(--muted)); background: hsl(var(--accent-bg)); padding: 1px 8px; border-radius: 999px; } +.folder-count { font-size: 12px; color: hsl(var(--muted)); background: hsl(var(--accent-bg)); padding: 1px 8px; } +.folder:hover .folder-count { background: hsl(var(--accent-hover)); } .folder-chevron { color: hsl(var(--muted)); display: inline-flex; } .folder-chevron .icon { width: 16px; height: 16px; } .findbox-inline { padding: 2px 8px; } /* ---- tables --------------------------------------------------------------- */ -.table { width: 100%; border-collapse: collapse; font-size: 14px; } -.table th { text-align: left; font-size: 12px; text-transform: uppercase; letter-spacing: .05em; color: hsl(var(--muted)); padding: 8px 10px; border-bottom: 1px solid hsl(var(--border)); } -.table td { padding: 9px 10px; border-bottom: 1px solid hsl(var(--border)); } -.table tr:last-child td { border-bottom: 0; } +.table { width: 100%; border-collapse: collapse; font-size: 13.5px; } +.table th { text-align: left; font-size: 12px; text-transform: uppercase; letter-spacing: .07em; color: hsl(var(--muted)); padding: 8px 10px; } +.table td { padding: 10px; } +.table tbody tr { transition: background .12s; } +.table tbody tr:hover td { background: hsl(var(--accent-bg)); } /* ---- graph ---------------------------------------------------------------- */ .graph-card { padding: 0; overflow: hidden; } @@ -383,23 +394,24 @@ svg.icon { width: 18px; height: 18px; flex: none; } .graph-noscript { padding: 24px; } .depth-form { display: flex; align-items: center; gap: 8px; } .depth-form label { font-size: 13px; color: hsl(var(--muted)); } -.depth-form select { border: 1px solid hsl(var(--input)); background: hsl(var(--bg)); border-radius: 8px; padding: 6px 8px; } +.depth-form select { border: 1px solid transparent; background: hsl(var(--accent-bg)); padding: 6px 8px; outline: none; } +.depth-form select:focus { background: hsl(var(--card)); border-color: hsl(var(--ring)); } /* ---- empty / state -------------------------------------------------------- */ .empty { display: flex; flex-direction: column; align-items: center; gap: 14px; text-align: center; padding: 56px 24px; color: hsl(var(--muted)); - border: 1px dashed hsl(var(--border)); border-radius: var(--radius); + background: hsl(var(--accent-bg)); } .empty .icon { width: 34px; height: 34px; opacity: .6; } .empty-examples { display: flex; flex-wrap: wrap; gap: 6px; justify-content: center; } .state { text-align: center; padding: 72px 24px; display: flex; flex-direction: column; align-items: center; gap: 12px; } -.state-code { font-size: 44px; font-weight: 800; letter-spacing: -.03em; display: inline-flex; align-items: center; gap: 10px; } +.state-code { font-size: 42px; font-weight: 800; letter-spacing: -.02em; display: inline-flex; align-items: center; gap: 10px; } .state-code .icon { width: 40px; height: 40px; } -.state-title { font-size: 24px; font-weight: 700; } +.state-title { font-size: 22px; font-weight: 700; } .state-uri { font-family: var(--mono); } -.state-msg { color: hsl(var(--muted)); max-width: 60ch; } +.state-msg { color: hsl(var(--muted)); max-width: 64ch; } .state-actions { display: flex; gap: 10px; flex-wrap: wrap; justify-content: center; margin-top: 8px; } /* ---- loading screen ------------------------------------------------------- */ @@ -420,7 +432,8 @@ svg.icon { width: 18px; height: 18px; flex: none; } .layout { grid-template-columns: 1fr; } .sidebar { position: fixed; left: 0; top: 0; z-index: 40; width: 280px; - transform: translateX(-100%); transition: transform .2s ease; box-shadow: var(--shadow); + transform: translateX(-100%); transition: transform .2s ease; + background: hsl(var(--card)); } body.menu-open .sidebar { transform: translateX(0); } body.menu-open::after { content: ""; position: fixed; inset: 0; z-index: 30; background: hsl(0 0% 0% / .4); } diff --git a/web/console.go b/web/console.go index 113a954..130a9c5 100644 --- a/web/console.go +++ b/web/console.go @@ -1,7 +1,9 @@ // Package web is the ant web console: a browser GUI over the whole resource-URI -// namespace, server-rendered in pure Go and styled to match shadcn/ui, with the -// machine-facing JSON API preserved under content negotiation. It is the human -// surface that sits beside the CLI and the MCP server (8000_ant_serve). +// namespace, server-rendered in pure Go with a flat, square, monospace console +// look (panels that float on a tinted canvas, no shadows or rules, no rounding, +// an embedded Geist Mono face), with the machine-facing JSON API preserved under +// content negotiation. It is the +// human surface that sits beside the CLI and the MCP server (8000_ant_serve). // // The console adds no data capability of its own; every page is a thin rendering // of an ant.Engine method. It depends only on the Deref interface, so it is diff --git a/web/embed.go b/web/embed.go index 8c815e3..b9705e3 100644 --- a/web/embed.go +++ b/web/embed.go @@ -1,11 +1,19 @@ package web -import "embed" +import ( + "embed" + "mime" +) // files holds the whole console: the html/template sources under templates/ and -// the static CSS/JS/SVG under assets/. Embedding them keeps ant a single static -// binary — there is no asset directory to ship next to it and nothing to fetch -// at runtime (8000_ant_serve §2, WC1). +// the static CSS/JS/SVG/fonts under assets/. Embedding them keeps ant a single +// static binary: there is no asset directory to ship next to it and nothing to +// fetch at runtime, the embedded Geist Mono face included (8000_ant_serve §2, WC1). // //go:embed templates assets var files embed.FS + +// Register the woff2 type so the embedded font serves as font/woff2 regardless +// of the host's MIME database; without it some systems fall back to a generic +// type and the browser declines to use the font. +func init() { _ = mime.AddExtensionType(".woff2", "font/woff2") } diff --git a/web/templates/base.html b/web/templates/base.html index c1a346d..814a963 100644 --- a/web/templates/base.html +++ b/web/templates/base.html @@ -6,6 +6,7 @@ {{.Title}} · ant +