feat: lodge server, client and admin

This commit is contained in:
hh committed 2026-09-19 23:33:24 +02:00
commit 95fc50e2c2
84 files changed
+7625

No files matched your search

+5
View File
@@ -0,0 +1,5 @@
config.local.json
hook.cache.json
hook.pending.json
review/
decision/
+510
View File
@@ -0,0 +1,510 @@
{
"schemaVersion": 2,
"generatedAt": "2026-09-19T21:29:36Z",
"title": "Design System: lodge",
"extensions": {
"colorMeta": {
"survey-green": {
"role": "primary",
"displayName": "Survey Green",
"canonical": "#24694f",
"tonalRamp": [
"oklch(15% 0.081 165)",
"oklch(27% 0.081 165)",
"oklch(38% 0.081 165)",
"oklch(50% 0.081 165)",
"oklch(61% 0.081 165)",
"oklch(73% 0.081 165)",
"oklch(84% 0.081 165)",
"oklch(95% 0.081 165)"
]
},
"curve-red": {
"role": "secondary",
"displayName": "Curve Red",
"canonical": "#b3341f",
"tonalRamp": [
"oklch(15% 0.167 32)",
"oklch(27% 0.167 32)",
"oklch(38% 0.167 32)",
"oklch(50% 0.167 32)",
"oklch(61% 0.167 32)",
"oklch(73% 0.167 32)",
"oklch(84% 0.167 32)",
"oklch(95% 0.167 32)"
]
},
"ochre": {
"role": "tertiary",
"displayName": "Ochre",
"canonical": "#c9a24a",
"tonalRamp": [
"oklch(15% 0.115 85)",
"oklch(27% 0.115 85)",
"oklch(38% 0.115 85)",
"oklch(50% 0.115 85)",
"oklch(61% 0.115 85)",
"oklch(73% 0.115 85)",
"oklch(84% 0.115 85)",
"oklch(95% 0.115 85)"
]
},
"log-paper": {
"role": "neutral",
"displayName": "Log Paper",
"canonical": "#eef0e6",
"tonalRamp": [
"oklch(15% 0.013 116)",
"oklch(27% 0.013 116)",
"oklch(38% 0.013 116)",
"oklch(50% 0.013 116)",
"oklch(61% 0.013 116)",
"oklch(73% 0.013 116)",
"oklch(84% 0.013 116)",
"oklch(95% 0.013 116)"
]
},
"log-paper-2": {
"role": "neutral",
"displayName": "Log Paper 2",
"canonical": "#e3e7d8",
"tonalRamp": [
"oklch(15% 0.020 119)",
"oklch(27% 0.020 119)",
"oklch(38% 0.020 119)",
"oklch(50% 0.020 119)",
"oklch(61% 0.020 119)",
"oklch(73% 0.020 119)",
"oklch(84% 0.020 119)",
"oklch(95% 0.020 119)"
]
},
"ink": {
"role": "neutral",
"displayName": "Ink",
"canonical": "#15211d",
"tonalRamp": [
"oklch(15% 0.019 172)",
"oklch(27% 0.019 172)",
"oklch(38% 0.019 172)",
"oklch(50% 0.019 172)",
"oklch(61% 0.019 172)",
"oklch(73% 0.019 172)",
"oklch(84% 0.019 172)",
"oklch(95% 0.019 172)"
]
},
"ink-2": {
"role": "neutral",
"displayName": "Ink 2",
"canonical": "#46544d",
"tonalRamp": [
"oklch(15% 0.021 164)",
"oklch(27% 0.021 164)",
"oklch(38% 0.021 164)",
"oklch(50% 0.021 164)",
"oklch(61% 0.021 164)",
"oklch(73% 0.021 164)",
"oklch(84% 0.021 164)",
"oklch(95% 0.021 164)"
]
},
"night-survey-green": {
"role": "primary",
"displayName": "Survey Green, night print",
"canonical": "#74caa6",
"tonalRamp": [
"oklch(15% 0.099 165)",
"oklch(27% 0.099 165)",
"oklch(38% 0.099 165)",
"oklch(50% 0.099 165)",
"oklch(61% 0.099 165)",
"oklch(73% 0.099 165)",
"oklch(84% 0.099 165)",
"oklch(95% 0.099 165)"
]
},
"night-curve-red": {
"role": "secondary",
"displayName": "Curve Red, night print",
"canonical": "#f08a74",
"tonalRamp": [
"oklch(15% 0.129 33)",
"oklch(27% 0.129 33)",
"oklch(38% 0.129 33)",
"oklch(50% 0.129 33)",
"oklch(61% 0.129 33)",
"oklch(73% 0.129 33)",
"oklch(84% 0.129 33)",
"oklch(95% 0.129 33)"
]
},
"night-ochre": {
"role": "tertiary",
"displayName": "Ochre, night print",
"canonical": "#d9b45e",
"tonalRamp": [
"oklch(15% 0.113 86)",
"oklch(27% 0.113 86)",
"oklch(38% 0.113 86)",
"oklch(50% 0.113 86)",
"oklch(61% 0.113 86)",
"oklch(73% 0.113 86)",
"oklch(84% 0.113 86)",
"oklch(95% 0.113 86)"
]
},
"night-ground": {
"role": "neutral",
"displayName": "Night Ground",
"canonical": "#162026",
"tonalRamp": [
"oklch(15% 0.018 235)",
"oklch(27% 0.018 235)",
"oklch(38% 0.018 235)",
"oklch(50% 0.018 235)",
"oklch(61% 0.018 235)",
"oklch(73% 0.018 235)",
"oklch(84% 0.018 235)",
"oklch(95% 0.018 235)"
]
},
"night-ground-2": {
"role": "neutral",
"displayName": "Night Ground 2",
"canonical": "#1d2a31",
"tonalRamp": [
"oklch(15% 0.022 232)",
"oklch(27% 0.022 232)",
"oklch(38% 0.022 232)",
"oklch(50% 0.022 232)",
"oklch(61% 0.022 232)",
"oklch(73% 0.022 232)",
"oklch(84% 0.022 232)",
"oklch(95% 0.022 232)"
]
},
"night-ink": {
"role": "neutral",
"displayName": "Night Ink",
"canonical": "#e6eadc",
"tonalRamp": [
"oklch(15% 0.019 120)",
"oklch(27% 0.019 120)",
"oklch(38% 0.019 120)",
"oklch(50% 0.019 120)",
"oklch(61% 0.019 120)",
"oklch(73% 0.019 120)",
"oklch(84% 0.019 120)",
"oklch(95% 0.019 120)"
]
},
"night-ink-2": {
"role": "neutral",
"displayName": "Night Ink 2",
"canonical": "#a5b2a8",
"tonalRamp": [
"oklch(15% 0.020 153)",
"oklch(27% 0.020 153)",
"oklch(38% 0.020 153)",
"oklch(50% 0.020 153)",
"oklch(61% 0.020 153)",
"oklch(73% 0.020 153)",
"oklch(84% 0.020 153)",
"oklch(95% 0.020 153)"
]
},
"rule": {
"role": "neutral",
"displayName": "Rule (ink at 20%)"
},
"grid": {
"role": "neutral",
"displayName": "Depth Grid"
},
"grid-strong": {
"role": "neutral",
"displayName": "Depth Grid, 40px line"
},
"green-wash": {
"role": "neutral",
"displayName": "Green Wash"
},
"red-wash": {
"role": "neutral",
"displayName": "Red Wash"
},
"night-rule": {
"role": "neutral",
"displayName": "Rule, night print"
},
"night-grid": {
"role": "neutral",
"displayName": "Depth Grid, night print"
},
"night-grid-strong": {
"role": "neutral",
"displayName": "Depth Grid 40px line, night print"
},
"night-green-wash": {
"role": "neutral",
"displayName": "Green Wash, night print"
},
"night-red-wash": {
"role": "neutral",
"displayName": "Red Wash, night print"
}
},
"typographyMeta": {
"display": {
"displayName": "Display",
"purpose": "Repository names on the index and the Machines heading; Archivo at 66% width."
},
"headline": {
"displayName": "Headline",
"purpose": "File name above a note; 1.75rem below 56rem."
},
"title": {
"displayName": "Stamp",
"purpose": "Condensed tabular stamp for names, depths and counts, 0.8125rem to 1.75rem."
},
"body": {
"displayName": "Body",
"purpose": "Interface text; meta lines at 0.875rem in ink-2."
},
"prose": {
"displayName": "Prose",
"purpose": "Rendered notes at a 70ch measure."
},
"label": {
"displayName": "Label",
"purpose": "Uppercase field and group names; always names the value or list under it."
},
"mono": {
"displayName": "Mono",
"purpose": "SHAs, keys, paths, commands, code, source and diffs; ligatures off."
}
},
"shadows": [],
"motion": [
{
"name": "ease",
"value": "cubic-bezier(0.16, 1, 0.3, 1)",
"purpose": "The one easing curve, for every transition and the settle reveal."
},
{
"name": "state",
"value": "150ms",
"purpose": "Colour, background, underline and opacity changes on hover and selection."
},
{
"name": "settle",
"value": "600ms clip-path inset(0 0 100% 0) to inset(0)",
"purpose": "A revision arriving over SSE reveals top-down onto the surface. Nothing else animates."
},
{
"name": "reduced-motion",
"value": "1ms",
"purpose": "All transition and animation durations under prefers-reduced-motion."
}
],
"breakpoints": [
{
"name": "bar-compact",
"value": "40rem"
},
{
"name": "machines-stack",
"value": "48rem"
},
{
"name": "rail",
"value": "56rem"
},
{
"name": "sheet-narrow",
"value": "72rem"
}
],
"themes": {
"selector": ".dark",
"customProperties": {
"light": {
"--paper": "#eef0e6",
"--paper-2": "#e3e7d8",
"--ink": "#15211d",
"--ink-2": "#46544d",
"--rule": "rgb(21 33 29 / 0.2)",
"--grid": "rgb(36 105 79 / 0.13)",
"--grid-strong": "rgb(36 105 79 / 0.3)",
"--green": "#24694f",
"--green-wash": "rgb(36 105 79 / 0.12)",
"--red": "#b3341f",
"--red-wash": "rgb(179 52 31 / 0.11)",
"--ochre": "#c9a24a"
},
"dark": {
"--paper": "#162026",
"--paper-2": "#1d2a31",
"--ink": "#e6eadc",
"--ink-2": "#a5b2a8",
"--rule": "rgb(230 234 220 / 0.2)",
"--grid": "rgb(111 199 163 / 0.09)",
"--grid-strong": "rgb(111 199 163 / 0.24)",
"--green": "#74caa6",
"--green-wash": "rgb(116 202 166 / 0.14)",
"--red": "#f08a74",
"--red-wash": "rgb(240 138 116 / 0.14)",
"--ochre": "#d9b45e"
}
}
}
},
"components": [
{
"name": "Text Action",
"kind": "button",
"refersTo": "action-text",
"description": "Default action: green weight-600 text on a 1px rule, wash on hover. No box.",
"html": "<button class=\"ds-act\">Restore this version</button>",
"css": ".ds-act { font-family: var(--sans); font-size: 1rem; font-weight: 600; padding: 0.125rem 0; color: var(--green); background: none; border: 0; border-bottom: 1px solid currentColor; cursor: pointer; transition: color 150ms var(--ease), background-color 150ms var(--ease); } .ds-act:hover { background: var(--green-wash); } .ds-act:focus-visible { outline: 2px solid var(--red); outline-offset: 2px; }"
},
{
"name": "Text Action, Danger",
"kind": "button",
"refersTo": "action-text-danger",
"description": "Destructive or declining text action in curve red.",
"html": "<button class=\"ds-act ds-act-danger\">Revoke</button>",
"css": ".ds-act { font-family: var(--sans); font-size: 1rem; font-weight: 600; padding: 0.125rem 0; color: var(--green); background: none; border: 0; border-bottom: 1px solid currentColor; cursor: pointer; transition: color 150ms var(--ease), background-color 150ms var(--ease); } .ds-act:hover { background: var(--green-wash); } .ds-act:focus-visible { outline: 2px solid var(--red); outline-offset: 2px; } .ds-act-danger { color: var(--red); } .ds-act-danger:hover { background: var(--red-wash); }"
},
{
"name": "Solid Action",
"kind": "button",
"refersTo": "action-solid",
"description": "The single primary act of a page: paper text on ink, green on hover.",
"html": "<button class=\"ds-solid\">Sign in</button>",
"css": ".ds-solid { font-family: var(--sans); font-size: 1rem; font-weight: 600; padding: 0.625rem 1rem; color: var(--paper); background: var(--ink); border: 0; border-radius: 0; cursor: pointer; transition: background-color 150ms var(--ease); } .ds-solid:hover { background: var(--green); } .ds-solid:focus-visible { outline: 2px solid var(--red); outline-offset: 2px; }"
},
{
"name": "Solid Action, Danger",
"kind": "button",
"refersTo": "action-solid-danger",
"description": "Irreversible deletion, enabled only after the name is typed.",
"html": "<button class=\"ds-solid-danger\">Delete with all history</button>",
"css": ".ds-solid-danger { font-family: var(--sans); font-size: 1rem; font-weight: 600; padding: 0.625rem 1rem; color: var(--paper); background: var(--red); border: 0; border-radius: 0; cursor: pointer; } .ds-solid-danger:disabled { opacity: 0.5; cursor: default; } .ds-solid-danger:focus-visible { outline: 2px solid var(--red); outline-offset: 2px; }"
},
{
"name": "Field",
"kind": "input",
"refersTo": "field",
"description": "Underlined input: 2px ink rule that turns curve red on focus.",
"html": "<label class=\"ds-label\" for=\"ds-token\">Admin token</label><input id=\"ds-token\" class=\"ds-field\" placeholder=\"paste the token\">",
"css": ".ds-label { display: block; font-family: var(--sans); font-size: 0.75rem; font-weight: 500; font-stretch: 80%; color: var(--ink-2); text-transform: uppercase; letter-spacing: 0.06em; } .ds-field { width: 100%; padding: 0.5rem 0; font-family: var(--mono); font-size: 0.8125rem; color: var(--ink); caret-color: var(--red); background: transparent; border: 0; border-bottom: 2px solid var(--ink); border-radius: 0; } .ds-field::placeholder { color: var(--ink-2); } .ds-field:focus-visible { outline: none; border-bottom-color: var(--red); }"
},
{
"name": "Top Bar",
"kind": "nav",
"refersTo": "top-bar",
"description": "3rem bar: condensed wordmark, section links with a red underline on the current one, plain tools on the right.",
"html": "<header class=\"ds-bar\"><a class=\"ds-wordmark\" href=\"#\">lodge</a><nav class=\"ds-nav\"><a href=\"#\" aria-current=\"page\">Repositories</a><a href=\"#\">Machines</a></nav><div class=\"ds-tools\"><button class=\"ds-plain\">Night print</button><button class=\"ds-plain\">Sign out</button></div></header>",
"css": ".ds-bar { display: flex; gap: 1.5rem; align-items: center; height: 3rem; padding: 0 1rem; font-family: var(--sans); color: var(--ink); background: var(--paper); border-bottom: 1px solid var(--rule); } .ds-wordmark { font-size: 1.25rem; font-weight: 800; font-stretch: 68%; letter-spacing: 0.01em; color: inherit; text-decoration: none; } .ds-nav { display: flex; gap: 1.25rem; } .ds-nav a { color: inherit; text-decoration: underline 1px transparent; text-underline-offset: 0.22em; transition: color 150ms var(--ease), text-decoration-color 150ms var(--ease); } .ds-nav a:hover { color: var(--green); text-decoration-color: currentColor; } .ds-nav a[aria-current=page] { text-decoration-thickness: 2px; text-decoration-color: var(--red); } .ds-tools { display: flex; gap: 1.25rem; margin-left: auto; } .ds-plain { padding: 0.25rem 0; font: inherit; color: var(--ink-2); background: none; border: 0; cursor: pointer; transition: color 150ms var(--ease); } .ds-plain:hover { color: var(--ink); } .ds-bar :focus-visible { outline: 2px solid var(--red); outline-offset: 2px; }"
},
{
"name": "Title Block",
"kind": "custom",
"description": "Ruled dl above the column: uppercase field labels over stamped values.",
"html": "<dl class=\"ds-titleblock\"><div class=\"ds-tb-wide\"><dt>Repository</dt><dd class=\"ds-tb-name\">beavergram</dd></div><div><dt>Depth</dt><dd class=\"ds-tb-stamp\">52 / 64</dd></div><div><dt>Root</dt><dd class=\"ds-tb-mono\">3f3f3f3f3f3f</dd></div></dl>",
"css": ".ds-titleblock { display: grid; grid-template-columns: 1fr 1fr; width: 17.5rem; margin: 0; font-family: var(--sans); color: var(--ink); background: var(--paper); border-right: 1px solid var(--ink); border-bottom: 1px solid var(--ink); } .ds-titleblock div { padding: 0.5rem 0.75rem; border-top: 1px solid var(--rule); } .ds-titleblock div + div + div { border-left: 1px solid var(--rule); } .ds-titleblock .ds-tb-wide { grid-column: 1 / -1; border-top: 0; } .ds-titleblock dt { font-size: 0.75rem; font-weight: 500; font-stretch: 80%; color: var(--ink-2); text-transform: uppercase; letter-spacing: 0.06em; } .ds-titleblock dd { margin: 0; } .ds-tb-name, .ds-tb-stamp { font-weight: 600; font-stretch: 68%; font-variant-numeric: tabular-nums; letter-spacing: 0.02em; } .ds-tb-name { font-size: 1.75rem; line-height: 1.1; } .ds-tb-mono { font-family: var(--mono); font-size: 0.8125rem; }"
},
{
"name": "Strip Log Beds",
"kind": "custom",
"description": "Beds on the depth grid: stamped depth, machine hatch, touch mark, formation label; the selected bed carries the red cursor and beds above it lose their hatch.",
"html": "<div class=\"ds-column\"><button class=\"ds-bed ds-bed-above\" style=\"height:26px\"><span class=\"ds-depth\">55</span><span class=\"ds-lith ds-lith-0 ds-fill\"></span><span></span><span class=\"ds-formation\"><b>main</b><code>5e7fa16</code></span></button><button class=\"ds-bed\" aria-selected=\"true\" style=\"height:32px\"><span class=\"ds-depth\">52</span><span class=\"ds-lith ds-lith-1 ds-fill\"></span><span class=\"ds-mark\"></span><span class=\"ds-formation\"><b>main</b><code>24b37e7</code></span></button><button class=\"ds-bed\" style=\"height:38px\"><span class=\"ds-depth\"></span><span class=\"ds-lith ds-lith-0 ds-fill\"></span><span></span><span class=\"ds-formation\"></span></button><button class=\"ds-bed\" style=\"height:26px\"><span class=\"ds-depth\">50</span><span class=\"ds-lith ds-lith-web ds-fill\"></span><span></span><span class=\"ds-formation\"></span></button><button class=\"ds-bed\" style=\"height:32px\"><span class=\"ds-depth\"></span><span class=\"ds-lith ds-lith-2 ds-fill\"></span><span class=\"ds-mark\"></span><span class=\"ds-formation\"><b>feature/history</b><code>9fa1c95</code></span></button></div>",
"css": ".ds-lith { --l: var(--ink); position: relative; background-color: var(--paper); } .ds-lith::after { position: absolute; inset: 0; content: ''; background: var(--l); } .ds-lith-0::after { -webkit-mask: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='6' height='6' fill='none' stroke='black' stroke-width='1'%3E%3Ccircle cx='3' cy='3' r='0.9' fill='black' stroke='none'/%3E%3C/svg%3E\") repeat; mask: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='6' height='6' fill='none' stroke='black' stroke-width='1'%3E%3Ccircle cx='3' cy='3' r='0.9' fill='black' stroke='none'/%3E%3C/svg%3E\") repeat; } .ds-lith-1::after { -webkit-mask: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' fill='none' stroke='black' stroke-width='1'%3E%3Cpath d='M-1 9 9 -1M-1 1 1 -1M7 9 9 7'/%3E%3C/svg%3E\") repeat; mask: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' fill='none' stroke='black' stroke-width='1'%3E%3Cpath d='M-1 9 9 -1M-1 1 1 -1M7 9 9 7'/%3E%3C/svg%3E\") repeat; } .ds-lith-2::after { -webkit-mask: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='10' fill='none' stroke='black' stroke-width='1'%3E%3Cpath d='M0 2.5h7M6 7.5h6M0 7.5h1'/%3E%3C/svg%3E\") repeat; mask: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='10' fill='none' stroke='black' stroke-width='1'%3E%3Cpath d='M0 2.5h7M6 7.5h6M0 7.5h1'/%3E%3C/svg%3E\") repeat; } .ds-lith-3::after { -webkit-mask: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' fill='none' stroke='black' stroke-width='1'%3E%3Cpath d='M0 .5h12M0 4.5h12M.5 0v4M6.5 4v4'/%3E%3C/svg%3E\") repeat; mask: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' fill='none' stroke='black' stroke-width='1'%3E%3Cpath d='M0 .5h12M0 4.5h12M.5 0v4M6.5 4v4'/%3E%3C/svg%3E\") repeat; } .ds-lith-4::after { -webkit-mask: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' fill='none' stroke='black' stroke-width='1'%3E%3Cpath d='M4 2v4M2 4h4'/%3E%3C/svg%3E\") repeat; mask: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' fill='none' stroke='black' stroke-width='1'%3E%3Cpath d='M4 2v4M2 4h4'/%3E%3C/svg%3E\") repeat; } .ds-lith-5::after { -webkit-mask: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='6' fill='none' stroke='black' stroke-width='1'%3E%3Cpath d='M0 3q3 -3 6 0t6 0'/%3E%3C/svg%3E\") repeat; mask: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='6' fill='none' stroke='black' stroke-width='1'%3E%3Cpath d='M0 3q3 -3 6 0t6 0'/%3E%3C/svg%3E\") repeat; } .ds-lith-6::after { -webkit-mask: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='6' fill='none' stroke='black' stroke-width='1'%3E%3Cpath d='M0 5l4 -4l4 4'/%3E%3C/svg%3E\") repeat; mask: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='6' fill='none' stroke='black' stroke-width='1'%3E%3Cpath d='M0 5l4 -4l4 4'/%3E%3C/svg%3E\") repeat; } .ds-lith-web { background-color: var(--ochre); } .ds-lith-web::after, .ds-lith-gone::after { display: none; } .ds-lith-gone { outline: 1px dashed var(--rule); outline-offset: -1px; } .ds-column { display: flex; flex-direction: column; width: 17.5rem; padding: 1.5rem 0 1rem; font-family: var(--sans); background-color: var(--paper-2); background-image: linear-gradient(var(--grid-strong) 1px, transparent 1px), linear-gradient(var(--grid) 1px, transparent 1px), linear-gradient(90deg, var(--grid) 1px, transparent 1px); background-size: 100% 40px, 100% 8px, 8px 100%; border-right: 1px solid var(--ink); } .ds-bed { position: relative; display: grid; flex: none; grid-template-columns: 2.75rem 4.5rem 0.5rem 1fr; align-items: stretch; padding: 0; font: inherit; color: var(--ink); text-align: left; background: none; border: 0; border-top: 1px solid var(--rule); cursor: pointer; } .ds-depth { padding-right: 0.5rem; font-size: 0.8125rem; font-weight: 600; font-stretch: 68%; font-variant-numeric: tabular-nums; line-height: 1.25rem; color: var(--ink-2); text-align: right; } .ds-fill { border-inline: 1px solid var(--ink); } .ds-bed:hover .ds-fill { --l: var(--green); } .ds-bed-above .ds-fill::after { display: none; } .ds-mark { background: var(--green); } .ds-formation { display: flex; gap: 0.375rem; padding-left: 0.375rem; font-size: 0.75rem; line-height: 1.25rem; color: var(--ink-2); white-space: nowrap; } .ds-formation b { font-weight: 600; font-stretch: 80%; color: var(--ink); } .ds-formation code { font-family: var(--mono); font-size: 0.8125em; } .ds-bed[aria-selected=true]::before { position: absolute; inset: -1px 0 auto; z-index: 1; height: 3px; content: ''; background: var(--red); } .ds-bed[aria-selected=true]::after { position: absolute; topLine truncated
},
{
"name": "Lithology Legend",
"kind": "custom",
"description": "The seven machine hatches as CSS masks over ink, plus solid ochre for the web and the dashed outline for a machine that is gone.",
"html": "<div class=\"ds-legend\"><span class=\"ds-swatch ds-lith ds-lith-0\"></span><span class=\"ds-swatch ds-lith ds-lith-1\"></span><span class=\"ds-swatch ds-lith ds-lith-2\"></span><span class=\"ds-swatch ds-lith ds-lith-3\"></span><span class=\"ds-swatch ds-lith ds-lith-4\"></span><span class=\"ds-swatch ds-lith ds-lith-5\"></span><span class=\"ds-swatch ds-lith ds-lith-6\"></span><span class=\"ds-swatch ds-lith ds-lith-web\"></span><span class=\"ds-swatch ds-lith ds-lith-gone\"></span></div>",
"css": ".ds-lith { --l: var(--ink); position: relative; background-color: var(--paper); } .ds-lith::after { position: absolute; inset: 0; content: ''; background: var(--l); } .ds-lith-0::after { -webkit-mask: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='6' height='6' fill='none' stroke='black' stroke-width='1'%3E%3Ccircle cx='3' cy='3' r='0.9' fill='black' stroke='none'/%3E%3C/svg%3E\") repeat; mask: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='6' height='6' fill='none' stroke='black' stroke-width='1'%3E%3Ccircle cx='3' cy='3' r='0.9' fill='black' stroke='none'/%3E%3C/svg%3E\") repeat; } .ds-lith-1::after { -webkit-mask: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' fill='none' stroke='black' stroke-width='1'%3E%3Cpath d='M-1 9 9 -1M-1 1 1 -1M7 9 9 7'/%3E%3C/svg%3E\") repeat; mask: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' fill='none' stroke='black' stroke-width='1'%3E%3Cpath d='M-1 9 9 -1M-1 1 1 -1M7 9 9 7'/%3E%3C/svg%3E\") repeat; } .ds-lith-2::after { -webkit-mask: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='10' fill='none' stroke='black' stroke-width='1'%3E%3Cpath d='M0 2.5h7M6 7.5h6M0 7.5h1'/%3E%3C/svg%3E\") repeat; mask: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='10' fill='none' stroke='black' stroke-width='1'%3E%3Cpath d='M0 2.5h7M6 7.5h6M0 7.5h1'/%3E%3C/svg%3E\") repeat; } .ds-lith-3::after { -webkit-mask: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' fill='none' stroke='black' stroke-width='1'%3E%3Cpath d='M0 .5h12M0 4.5h12M.5 0v4M6.5 4v4'/%3E%3C/svg%3E\") repeat; mask: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' fill='none' stroke='black' stroke-width='1'%3E%3Cpath d='M0 .5h12M0 4.5h12M.5 0v4M6.5 4v4'/%3E%3C/svg%3E\") repeat; } .ds-lith-4::after { -webkit-mask: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' fill='none' stroke='black' stroke-width='1'%3E%3Cpath d='M4 2v4M2 4h4'/%3E%3C/svg%3E\") repeat; mask: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' fill='none' stroke='black' stroke-width='1'%3E%3Cpath d='M4 2v4M2 4h4'/%3E%3C/svg%3E\") repeat; } .ds-lith-5::after { -webkit-mask: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='6' fill='none' stroke='black' stroke-width='1'%3E%3Cpath d='M0 3q3 -3 6 0t6 0'/%3E%3C/svg%3E\") repeat; mask: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='6' fill='none' stroke='black' stroke-width='1'%3E%3Cpath d='M0 3q3 -3 6 0t6 0'/%3E%3C/svg%3E\") repeat; } .ds-lith-6::after { -webkit-mask: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='6' fill='none' stroke='black' stroke-width='1'%3E%3Cpath d='M0 5l4 -4l4 4'/%3E%3C/svg%3E\") repeat; mask: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='6' fill='none' stroke='black' stroke-width='1'%3E%3Cpath d='M0 5l4 -4l4 4'/%3E%3C/svg%3E\") repeat; } .ds-lith-web { background-color: var(--ochre); } .ds-lith-web::after, .ds-lith-gone::after { display: none; } .ds-lith-gone { outline: 1px dashed var(--rule); outline-offset: -1px; } .ds-legend { display: grid; grid-template-columns: repeat(9, 4rem); gap: 0.75rem; padding: 1rem; background: var(--paper); } .ds-swatch { height: 3rem; border: 1px solid var(--ink); } .ds-swatch:hover { --l: var(--green); }"
},
{
"name": "File Row",
"kind": "custom",
"refersTo": "file-row",
"description": "File list row with its change track; hover is a green wash, the open file inverts to paper on ink.",
"html": "<ul class=\"ds-rows\"><li><a href=\"#\"><span class=\"ds-file\"><span class=\"ds-dir\">docs/agents/</span>architecture.md</span><span class=\"ds-track\"><i style=\"left:12%\"></i><i style=\"left:31%\"></i><i style=\"left:48%\"></i><i style=\"left:80%\" class=\"ds-tick-above\"></i></span><span class=\"ds-when\">8 minutes ago</span></a></li><li><a href=\"#\" aria-current=\"true\"><span class=\"ds-file\"><span class=\"ds-dir\">docs/agents/</span>plan.md</span><span class=\"ds-track\"><i style=\"left:5%\"></i><i style=\"left:22%\"></i><i style=\"left:40%\"></i><i style=\"left:66%\"></i></span><span class=\"ds-when\">8 minutes ago</span></a></li></ul>",
"css": ".ds-rows { width: 17rem; padding: 0; margin: 0; list-style: none; font-family: var(--sans); color: var(--ink); background: var(--paper); border-top: 1px solid var(--ink); } .ds-rows a { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 0.125rem 0.75rem; align-items: baseline; padding: 0.5rem 0.375rem; color: inherit; text-decoration: none; border-bottom: 1px solid var(--rule); transition: background-color 150ms var(--ease); } .ds-rows a:hover { background: var(--green-wash); } .ds-rows a[aria-current=true] { color: var(--paper); background: var(--ink); } .ds-rows a:focus-visible { outline: 2px solid var(--red); outline-offset: 2px; } .ds-file { grid-column: 1 / -1; font-weight: 600; } .ds-dir { font-weight: 400; opacity: 0.65; } .ds-track { position: relative; grid-row: 2; grid-column: 1 / -1; height: 0.4375rem; border-bottom: 1px solid currentColor; opacity: 0.85; } .ds-track i { position: absolute; bottom: 0; width: 2px; height: 100%; background: currentColor; } .ds-track .ds-tick-above { opacity: 0.3; } .ds-when { font-size: 0.8125rem; opacity: 0.7; }"
},
{
"name": "Diff Lines",
"kind": "custom",
"description": "Changes view: mono lines with green and red washes, signed gutters, and a dashed gap for collapsed context.",
"html": "<ol class=\"ds-diff\"><li class=\"ds-same\">## Done</li><li class=\"ds-removed\"><del>step 3 pending</del></li><li class=\"ds-added\">step 3 verified on both machines</li><li class=\"ds-gap\">14 unchanged lines</li><li class=\"ds-same\">## Next</li></ol>",
"css": ".ds-diff { padding: 0; margin: 0; list-style: none; font-family: var(--mono); font-size: 0.8125rem; line-height: 1.6; white-space: pre; color: var(--ink); background: var(--paper); } .ds-diff li { position: relative; padding: 0 0.75rem 0 1.75rem; } .ds-same { color: var(--ink-2); } .ds-added { background: var(--green-wash); } .ds-removed { background: var(--red-wash); } .ds-added::before, .ds-removed::before { position: absolute; left: 0.5rem; font-weight: 700; } .ds-added::before { color: var(--green); content: '+'; } .ds-removed::before { color: var(--red); content: '\\2212'; } .ds-removed del { text-decoration-color: var(--red); } .ds-diff .ds-gap { padding-block: 0.25rem; margin: 0.5rem 0; font-family: var(--sans); color: var(--ink-2); border-block: 1px dashed var(--rule); }"
}
],
"narrative": {
"northStar": "The Borehole Strip Log",
"overview": "**Creative North Star: \"The Borehole Strip Log\"**\n\nHistory is a drilled column. Every revision is a bed at its depth, newest at the surface, and the note on screen is the core pulled from the selected depth. The interface is a sheet of survey log paper: a fine green depth grid under the column, ink rules, condensed stamped numerals, and one hatch per machine, the way a geologist's log gives every rock type its own fill. The machines page is the legend of that log.\n\nThe sheet carries no chrome. There are no cards, no boxed buttons, no shadows and no rounded corners. Hierarchy comes from scale contrast (a 4rem condensed repository name against 0.75rem labels), from full-strength ink rules against faint ones, and from fills: ink for the current row, washes for hover, hatches for who wrote what. It refuses the forge layout of a file table with a commits tab, and the metric-card dashboard.\n\nThe dark theme is the same sheet as a night print: the same grid, rules and hatches in pale ink on a near-black blue ground. Nothing is restyled between themes; only the token values change, which is why hatches are masks over the ink colour instead of drawn images.",
"keyCharacteristics": [
"One column of beds is the navigation: drag it, step it with arrow keys or j/k, and every depth, file and view is a deep link.",
"Type, fills and rules only. Zero radius, zero shadow, zero boxed controls.",
"Three working colours with fixed meanings: green selects and links, red is the depth cursor and destruction, ochre is the web's own hand.",
"Archivo's width axis does the stamping: the narrower the setting, the more the text is data.",
"Reading first: the note sits at a 70ch measure and management lives in the margins."
],
"rules": [
{
"name": "The Three Pens Rule",
"body": "Green, red and ochre each have one job. Green means select or go, red means you are here or this destroys, ochre means the web did it. A new state borrows one of those meanings or uses ink; it does not get a fourth colour.",
"section": "colors"
},
{
"name": "The Text Contrast Rule",
"body": "Green and red are used as text on paper, so they are held at 4.5:1 or better against `log-paper`. This is why the shipped light values are darker than the direction contract's #2f7d62 and #c5402f: those failed as text on #eef0e6. Ochre does not pass as text on paper and is used only as a fill or a rule.",
"section": "colors"
},
{
"name": "The Night Print Rule",
"body": "The dark ground is `night-ground` (#162026), not the contract's slate #33444c. The owner saw the slate ground during the build and asked for the darker one back; treat this as a decided value. A `--slate` custom property is still declared in the stylesheet but nothing uses it.",
"section": "colors"
},
{
"name": "The Width Is Meaning Rule",
"body": "Condensed width marks data the system stamped: names, depths, counts, kinds. Sentences a person reads stay at normal width. Do not condense body copy to save space, and do not set a stamp at normal width.",
"section": "typography"
},
{
"name": "The Label Names A Field Rule",
"body": "An uppercase label always names the value or list directly under it, as in a drawing's title block. It is never a decorative line above a headline.",
"section": "typography"
},
{
"name": "The One Sheet Rule",
"body": "Everything is printed on the same sheet. If something needs to stand out, give it an ink rule, a fill or a bigger size, not a shadow or a raised surface.",
"section": "elevation"
}
],
"dos": [
"Do take every colour from the custom properties so the night print follows for free; add a value to both `:root` and `.dark` or not at all.",
"Do draw any new pattern as an SVG tile used as a CSS mask over `--l`, so it inherits ink and can be recoloured by state.",
"Do keep green and red at 4.5:1 or better on `log-paper` when they carry text, and keep ochre to fills and rules.",
"Do use Archivo's width axis for stamped data (66 to 80%) and normal width for sentences; set numerals tabular.",
"Do make every new view state a URL parameter, the way depth, file and view already are.",
"Do keep motion to 150ms state changes on the shared ease, with the clip-reveal `settle` reserved for a revision arriving live.",
"Do name component classes so they cannot collide with Tailwind utilities. Tailwind is imported globally, so a scoped class called `fixed` picked up `position: fixed` during this build. Avoid bare utility words (`fixed`, `static`, `hidden`, `block`, `flex`, `grid`, `container`, `truncate`, `underline`, `transition`) as class names."
],
"donts": [
"Don't add cards, boxed or pill buttons, radii or shadows. Separation is an ink rule, a `rule` line or a fill.",
"Don't build a file table with a commits tab or a metric-card dashboard; history stays a column you drag.",
"Don't give a state a fourth accent colour, and don't set ochre as text.",
"Don't lighten the night ground toward slate; #162026 is the owner's decision.",
"Don't float confirmations in modals or popovers; they open in place.",
"Don't put an uppercase label above a headline as decoration. A label names the field or the list under it.",
"Don't embed hatches as coloured images or fixed-colour backgrounds; they would not follow the theme."
]
}
}
View File
Whitespace-only changes.
+32
View File
@@ -0,0 +1,32 @@
---
version: 1
slug: "frontend"
primary_target: "frontend"
related_targets: []
---
# lodge admin
Scope: the whole authenticated admin (repositories, reader with history, machines, login). Mode: Operate, reading first.
Audience and job: the single owner reads agent notes and their history from a desk and from a phone; management (machines, restore, delete) is the margin. Must not read as a SaaS dashboard.
## Direction contract
THESIS: History is a drilled column. Every revision is a bed at its depth, newest at the surface; the note being read is the core pulled from the selected depth. Refuses the forge layout of file table plus commits tab, and the metric-card dashboard.
OWN-WORLD: Borehole strip log. Log-paper ground #eef0e6 with a fine depth grid, ink #15211d, survey green #2f7d62 for selection and links, curve red #c5402f for the depth cursor, deletions and destructive acts, ochre #c9a24a for modifications, slate #33444c. Each machine owns a lithology hatch (dots, diagonals, dashes, bricks, crosses, waves) drawn as SVG patterns; the machines page is the legend. Archivo with its width axis for condensed stamped numerals and labels, JetBrains Mono for SHAs, code and diffs. No cards, no button chrome: type, fills and rules only. Dark theme is the same sheet as a night print: slate ground, pale ink.
STORY: The owner sees which machine wrote what against which commit, drags to any depth, reads the note as it was, compares, and brings a version back.
FIRST VIEWPORT: Left edge, a full-height strip log about 260px wide: title block (repository name, key, total depth), then tracks: depth numerals, lithology beds sized by change count, formation labels branch@sha where the main commit changes. A red cursor line marks the selected depth. Next, the file list grouped by space, each row with a small track of where it changed. The rest is the note: large file name, one meta line, the text at a 70ch measure. On a phone the log collapses to a thumb-wide rail on the left edge that is dragged; files and reader take turns.
SIGNATURE INTERACTION: dragging along the column re-renders files and note at that depth live; every depth, file and view is a deep link. Motion grammar: a new revision arriving over SSE settles onto the surface with a clip reveal; nothing else animates beyond 150ms state changes.
FORM: borehole strip log, position 5 of 7 on the grounded list, seed 3210ed2a. Raises: scrub with deep links (cyclorama), per-file trend track (six-pack), chrome deleted (metro tiles), hierarchy by scale contrast (specimen).
FINISH: unreviewed and undocumented is unfinished; this build ends with the finish review, the verdict, DESIGN.md, and every shipping raster carrying its provenance
## Unresolved
Repositories with more than 500 revisions show only the newest 500 beds.