@vibelord/lanes 1.1.1 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -5
- package/bin/lanes-install.mjs +166 -13
- package/lib/plan.mjs +113 -3
- package/package.json +1 -1
- package/payload/lanes/README.md +47 -23
- package/payload/lanes/VERSION +1 -1
- package/payload/lanes/bin/lanes +2753 -202
- package/payload/lanes/client/ghostty/lanes-palette-light.conf +19 -19
- package/payload/lanes/client/ghostty/lanes-palette.conf +19 -19
- package/payload/lanes/client/lanes-launch +289 -40
- package/payload/lanes/client/lanes-window +15 -3
- package/payload/lanes/container/hooks/claude-settings.json +1 -0
- package/payload/lanes/container/lane +147 -92
- package/payload/lanes/container/lane-edit +100 -0
- package/payload/lanes/container/lane-mcp +1150 -0
- package/payload/lanes/container/lane-signal +109 -29
- package/payload/lanes/container/lane_core.py +1318 -0
- package/payload/lanes/container/vimrc +31 -0
- package/payload/lanes/docs/ARCHITECTURE.md +793 -16
- package/payload/lanes/docs/DESIGN.md +127 -46
- package/payload/lanes/docs/INSTALL.md +175 -18
- package/payload/lanes/docs/RUNBOOK.md +968 -76
- package/payload/lanes/docs/SHARING.md +3 -2
- package/payload/lanes/docs/USERGUIDE.md +967 -150
- package/payload/lanes/holders.lock +5 -0
- package/payload/lanes/install/enable-docs.sh +78 -0
- package/payload/lanes/install/enable-web.sh +2 -0
- package/payload/lanes/install/host-install.sh +197 -7
- package/payload/lanes/install/services.py +148 -18
- package/payload/lanes/install.sh +68 -7
- package/payload/lanes/libexec/agent_state.py +74 -1
- package/payload/lanes/libexec/cursor_sync.py +6 -0
- package/payload/lanes/libexec/device_sync.py +47 -10
- package/payload/lanes/libexec/docs_store.py +995 -0
- package/payload/lanes/libexec/docsd.py +2010 -0
- package/payload/lanes/libexec/economy_poll.py +1 -0
- package/payload/lanes/libexec/enrold.py +41 -22
- package/payload/lanes/libexec/keys.py +54 -23
- package/payload/lanes/libexec/lanes_lib.py +620 -22
- package/payload/lanes/libexec/mdlite.py +575 -0
- package/payload/lanes/libexec/members.py +21 -2
- package/payload/lanes/libexec/notify.py +169 -29
- package/payload/lanes/libexec/registrations.py +216 -18
- package/payload/lanes/libexec/session_actions.py +182 -14
- package/payload/lanes/libexec/sidebar +11 -3
- package/payload/lanes/libexec/sidebar.py +2232 -365
- package/payload/lanes/libexec/statusd.py +231 -12
- package/payload/lanes/libexec/studio_host.py +8 -1
- package/payload/lanes/libexec/tokens.py +148 -35
- package/payload/lanes/libexec/tokens.sh +86 -2
- package/payload/lanes/libexec/update.py +1044 -0
- package/payload/lanes/libexec/uploads.py +538 -0
- package/payload/lanes/libexec/users.py +614 -0
- package/payload/lanes/libexec/view.sh +34 -6
- package/payload/lanes/systemd/lanes-docsd.service +29 -0
- package/payload/lanes/systemd/lanes-ensure.service +3 -0
- package/payload/lanes/systemd/lanes-gc.service +1 -1
- package/payload/lanes/systemd/lanes-update.service +17 -0
- package/payload/lanes/systemd/lanes-update.timer +17 -0
- package/payload/lanes/web/Caddyfile +24 -1
- package/payload/lanes/web/README.md +165 -28
- package/payload/lanes/web/browser.js +4 -8
- package/payload/lanes/web/docs.css +52 -0
- package/payload/lanes/web/docs.js +36 -0
- package/payload/lanes/web/mobile.css +17 -17
- package/payload/lanes/web/mobile.js +45 -32
- package/payload/lanes/web/upload.css +17 -0
- package/payload/lanes/web/upload.js +84 -0
- package/payload/lanes/workspaces.example/README.md +3 -1
- package/payload/lanes/zellij/config.kdl +106 -56
- package/payload/manifest.json +6 -3
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""The Lanes design tokens: ONE table, rendered
|
|
3
|
-
`--md`, `--js
|
|
4
|
-
the
|
|
5
|
-
when any committed block
|
|
2
|
+
"""The Lanes design tokens: ONE table, rendered eight ways. The sidebar imports `sgr_table` (truecolor, 256, 16 or
|
|
3
|
+
none); `--kdl`, `--css`, `--sh`, `--md`, `--js`, `--ghostty-dark|--ghostty-light` and `--docs-css` write the Zellij
|
|
4
|
+
theme, the phone CSS, the viewer's shell colour sets, the docs tables, the phone's terminal palette, the optional
|
|
5
|
+
Ghostty palette files and the docs page's CSS (plan W10 W6) between markers; `--check` fails when any committed block
|
|
6
|
+
differs. Spec:
|
|
7
|
+
docs/superpowers/specs/2026-10-03-lanes-cockpit-design.md; palette v2 + the 256-colour set: cockpit v3 P1 (LNS-69).
|
|
6
8
|
No third-party code; importable with no side effects."""
|
|
7
9
|
import difflib
|
|
8
10
|
import os
|
|
@@ -12,30 +14,54 @@ from pathlib import Path
|
|
|
12
14
|
ROLES = ('surface', 'surface-raised', 'line', 'accent-wash', 'accent-line', 'press', 'chip',
|
|
13
15
|
'ink', 'ink-dim', 'ink-faint', 'accent', 'on-accent', 'on-accent-urgent',
|
|
14
16
|
'tone-urgent', 'tone-attention', 'tone-working', 'tone-done', 'scrim', 'shadow')
|
|
17
|
+
# Palette v2 (cockpit v3 P1, 2026-10-06): vivid, high-contrast roles in both variants. v1 had darkened the light tones
|
|
18
|
+
# until they passed (amber #8a5a00 read olive-brown). Surfaces are v1's but for the dark accent-wash, the cursor row.
|
|
15
19
|
TOKENS = {
|
|
16
|
-
'dark': {'surface': '#101318', 'surface-raised': '#191e25', 'line': '#2c333d', 'accent-wash': '#
|
|
17
|
-
'accent-line': '#4b6955', 'press': '#26332c', 'chip': '#ffffff0f', 'ink': '#
|
|
18
|
-
'ink-faint': '#
|
|
19
|
-
'tone-urgent': '#
|
|
20
|
+
'dark': {'surface': '#101318', 'surface-raised': '#191e25', 'line': '#2c333d', 'accent-wash': '#1c3326',
|
|
21
|
+
'accent-line': '#4b6955', 'press': '#26332c', 'chip': '#ffffff0f', 'ink': '#f2f5f8', 'ink-dim': '#a6b1be',
|
|
22
|
+
'ink-faint': '#6f7a87', 'accent': '#8fe3a6', 'on-accent': '#101318', 'on-accent-urgent': '#7a1a12',
|
|
23
|
+
'tone-urgent': '#ff7b6e', 'tone-attention': '#ffc233', 'tone-working': '#6fb6ff', 'tone-done': '#5fdc8a',
|
|
20
24
|
'scrim': '#000000a6', 'shadow': '0 -16px 80px #0007'},
|
|
21
25
|
'light': {'surface': '#f3f5f2', 'surface-raised': '#ffffff', 'line': '#d5ded6', 'accent-wash': '#e5f0e6',
|
|
22
|
-
'accent-line': '#acc9b4', 'press': '#d8e9db', 'chip': '#edf3ec', 'ink': '#
|
|
23
|
-
'ink-faint': '#
|
|
24
|
-
'tone-urgent': '#
|
|
26
|
+
'accent-line': '#acc9b4', 'press': '#d8e9db', 'chip': '#edf3ec', 'ink': '#111a14', 'ink-dim': '#4a5661',
|
|
27
|
+
'ink-faint': '#6f7b87', 'accent': '#1f6f3f', 'on-accent': '#ffffff', 'on-accent-urgent': '#ffd3cc',
|
|
28
|
+
'tone-urgent': '#c41c1c', 'tone-attention': '#a04d00', 'tone-working': '#1a56c4', 'tone-done': '#0d7338',
|
|
25
29
|
'scrim': '#1a222b80', 'shadow': '0 -16px 80px #0003'},
|
|
26
30
|
}
|
|
27
31
|
# 16-colour fallback: the user's own palette resolves these. Selection is achromatic (bold) there, dim is bright-black.
|
|
28
32
|
ANSI16 = {'ink': '39', 'ink-dim': '90', 'ink-faint': '90', 'accent': '1;39',
|
|
29
33
|
'tone-urgent': '31', 'tone-attention': '33', 'tone-working': '34', 'tone-done': '32'}
|
|
34
|
+
# Painted roles that are a BACKGROUND, by the token they take their colour from. `selected` is the cursor row (cockpit
|
|
35
|
+
# v3 P2 paints it): the accent-wash the Zellij themes' *_selected components and the phone's current row
|
|
36
|
+
# (--lanes-accent-wash) already use, with ink on it. Not a CSS variable of its own: the phone needs none, and a copy
|
|
37
|
+
# per variant would cost 58 B of the 37 B mobile.css still has under spec §8's cap.
|
|
38
|
+
BACKGROUNDS = {'selected': 'accent-wash'}
|
|
39
|
+
PAINTED = tuple(ANSI16) + tuple(BACKGROUNDS) # the roles the TUI ever emits
|
|
40
|
+
# The colour modes whose colours Lanes defines (cockpit v3 P2/P3): the sidebar spends the palette in them, and view.sh
|
|
41
|
+
# (`LZ_VIVID`) the warning tone on its lead. 16 is the user's own palette and none has no colour: both keep v1's bytes.
|
|
42
|
+
VIVID = ('truecolor', '256')
|
|
43
|
+
# The 256-colour set: one xterm index per painted role, always from 16-255 (fixed RGB; 0-15 are the user's palette).
|
|
44
|
+
# Each is the nearest entry (RGB distance) in its token's hue family — greys for the inks — that keeps every text role
|
|
45
|
+
# >= 4.2:1 on surface, surface-raised and the 256 `selected` band; `selected` is the nearest entry to the wash. The
|
|
46
|
+
# floor is 4.2, not 4.5, for the cube's six levels alone (test_tokens.Xterm256 re-derives every index). Light accent
|
|
47
|
+
# and done share 22 (#005f00): the nearest passing entry, 23, is a teal.
|
|
48
|
+
XTERM256 = {
|
|
49
|
+
'dark': {'ink': 255, 'ink-dim': 249, 'ink-faint': 243, 'accent': 115, 'tone-urgent': 209, 'tone-attention': 215,
|
|
50
|
+
'tone-working': 75, 'tone-done': 78, 'selected': 235},
|
|
51
|
+
'light': {'ink': 233, 'ink-dim': 240, 'ink-faint': 243, 'accent': 22, 'tone-urgent': 160, 'tone-attention': 94,
|
|
52
|
+
'tone-working': 26, 'tone-done': 22, 'selected': 254},
|
|
53
|
+
}
|
|
30
54
|
# Lane-identity palette for the two places that DEFINE a palette: Zellij's `web_client { theme {} }` (the phone's
|
|
31
55
|
# xterm.js; dark only, see _web_client) and the optional Ghostty file. A Zellij theme's legacy `black…white` keys are
|
|
32
|
-
# NOT one of them (DEF-1413). The TUI itself keeps 38;5;N. Slots 1-4 are the four tones
|
|
56
|
+
# NOT one of them (DEF-1413). The TUI itself keeps 38;5;N. Slots 1-4 are the four tones and 7 / 15 the ink pair
|
|
57
|
+
# (PALETTE16_ROLES); v2 re-saturates the rest (more chroma than v1 in every chromatic slot). Every slot >= 4.5:1.
|
|
33
58
|
PALETTE16 = {
|
|
34
|
-
'dark': ['#
|
|
35
|
-
'#
|
|
36
|
-
'light': ['#
|
|
37
|
-
'#
|
|
59
|
+
'dark': ['#8792a0', '#ff7b6e', '#5fdc8a', '#ffc233', '#6fb6ff', '#c58bff', '#3fd9cf', '#a6b1be',
|
|
60
|
+
'#97a3b1', '#ff9a8e', '#86f0ad', '#ffd866', '#98cbff', '#dcb2ff', '#7deee6', '#f2f5f8'],
|
|
61
|
+
'light': ['#2f3b34', '#c41c1c', '#0d7338', '#a04d00', '#1a56c4', '#8228c4', '#006f73', '#4a5661',
|
|
62
|
+
'#55625b', '#d0221a', '#0e7a3a', '#a85200', '#1f5fd8', '#9133d0', '#00797c', '#111a14'],
|
|
38
63
|
}
|
|
64
|
+
PALETTE16_ROLES = {1: 'tone-urgent', 2: 'tone-done', 3: 'tone-attention', 4: 'tone-working', 7: 'ink-dim', 15: 'ink'}
|
|
39
65
|
# key -> (unicode, ascii twin, tone). Position disambiguates: agent glyphs in column 2, stream glyphs in column 33.
|
|
40
66
|
AGENT_GLYPHS = {'needs_approval': ('◆', '!', 'tone-urgent'), 'needs_input': ('◇', '?', 'tone-attention'),
|
|
41
67
|
'working': ('▸', '>', 'tone-working'), 'done': ('✓', 'v', 'tone-done'),
|
|
@@ -52,11 +78,30 @@ MARKS = {'cursor': ('▌', '|', 'accent'), 'viewing': ('›', '=', 'accent'), 'v
|
|
|
52
78
|
# Tone-less punctuation (spec §3 completeness rule): every ` · ` and every quote the sidebar draws is a lookup, so
|
|
53
79
|
# LANES_GLYPHS=ascii yields a seven-bit frame. `sep` shares `-` with `rule` on purpose: neither carries state.
|
|
54
80
|
'sep': ('·', '-', 'ink-dim'), 'quote-open': ('‘', "'", 'ink'), 'quote-close': ('’', "'", 'ink'),
|
|
55
|
-
'
|
|
81
|
+
# The confirmation's dash (` › close ‘sh’ — y⏎ confirm · Esc cancel`, cockpit v3 S3/S6); `-` like `sep`.
|
|
82
|
+
'dash': ('—', '-', 'ink-dim'),
|
|
83
|
+
# The focus legend's arrow (` ▌ keys go to sidebar · Esc → lane`, cockpit v3 S4).
|
|
84
|
+
'arrow': ('→', '->', 'accent'),
|
|
85
|
+
# Column 3 of a row whose registration is on its way (`P`, cockpit v3 S8): the session stays listed, dimmed
|
|
86
|
+
# mark, until the registry lists its lane. The truncation pair on purpose — position disambiguates (column 3,
|
|
87
|
+
# never the end of a name) — and seven-bit `~` is free in column 3 (`=` `2` `3` are the other marks there).
|
|
88
|
+
'pending': ('…', '~', 'ink-dim'),
|
|
89
|
+
# Column 3 of a lane its owner pinned (`p`, cockpit v3 R5): it leads its section and never falls below `⋮ N
|
|
90
|
+
# more`. The lowest of column 3's marks: pending, the viewports and the viewing mark win over it. Seven-bit
|
|
91
|
+
# `^` is free in column 3 (`=` `2` `3` `~` are the others; the upload strip's `^` is no column-3 mark).
|
|
92
|
+
'pinned': ('▲', '^', 'accent'),
|
|
93
|
+
'ok': ('✓', 'OK', 'tone-done'), 'fail': ('✗', 'X', 'tone-urgent'),
|
|
94
|
+
# The docs cockpit on the upload strip (` ↑ uploads · ▤ docs 3 (U)`, plan W10 W7): the strip's own tone.
|
|
95
|
+
# Seven-bit `#` is free on that row (`^` uploads, `v` armed, `-` sep are the others).
|
|
96
|
+
'docs': ('▤', '#', 'ink-dim')}
|
|
56
97
|
ECONOMY_TAIL = {'starving': ('⚠', '!', 'tone-attention'), 'downgraded': ('↓', 'v', 'tone-attention'), 'paid': ('$', '$', 'ink-dim')}
|
|
57
98
|
MOBILE_AGENT_LABELS = {'needs_approval': 'Needs approval', 'needs_input': 'Needs you', 'working': 'Working',
|
|
58
99
|
'done': 'Done', 'errored': 'Error', 'shell': 'Shell', 'idle': 'Idle'}
|
|
59
|
-
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def hex_of(variant, role):
|
|
103
|
+
"""The colour a role paints: its own token, or for a background role the token it takes (`selected` -> wash)."""
|
|
104
|
+
return TOKENS[variant][BACKGROUNDS.get(role, role)]
|
|
60
105
|
|
|
61
106
|
|
|
62
107
|
def luminance(hexval):
|
|
@@ -74,17 +119,24 @@ def rgb(hexval):
|
|
|
74
119
|
return tuple(int(hexval[i:i + 2], 16) for i in (1, 3, 5))
|
|
75
120
|
|
|
76
121
|
|
|
77
|
-
def sgr_table(mode, variant):
|
|
78
|
-
"""Complete escape per painted role. `mode` truecolor|16|none; `variant` dark|light. Resolved ONCE per process.
|
|
122
|
+
def sgr_table(mode, variant, dim16=None):
|
|
123
|
+
"""Complete escape per painted role. `mode` truecolor|256|16|none; `variant` dark|light. Resolved ONCE per process.
|
|
124
|
+
Foreground roles are `38;…`, `selected` (the cursor row) `48;…`; in 16/none it is empty — the palette there is the
|
|
125
|
+
user's, and the cursor stays the bold bar. `dim16` overrides the 16-colour dim (default: `LANES_DIM=2` -> SGR 2,
|
|
126
|
+
else 90); the shell renderer pins 90 so the committed block never depends on the generator's environment."""
|
|
79
127
|
table = {'bold': '\033[1m', 'reset': '\033[0m'}
|
|
80
|
-
|
|
128
|
+
if dim16 is None:
|
|
129
|
+
dim16 = '\033[2m' if os.environ.get('LANES_DIM') == '2' else '\033[90m'
|
|
81
130
|
for role in PAINTED:
|
|
131
|
+
bold, layer = '\033[1m' if role == 'accent' else '', 48 if role in BACKGROUNDS else 38
|
|
82
132
|
if mode == 'none':
|
|
83
|
-
table[role] =
|
|
133
|
+
table[role] = bold
|
|
84
134
|
elif mode == '16':
|
|
85
|
-
table[role] = dim16 if role in ('ink-dim', 'ink-faint') else f'\033[{ANSI16[role]}m'
|
|
135
|
+
table[role] = '' if role in BACKGROUNDS else dim16 if role in ('ink-dim', 'ink-faint') else f'\033[{ANSI16[role]}m'
|
|
136
|
+
elif mode == '256':
|
|
137
|
+
table[role] = bold + '\033[%d;5;%dm' % (layer, XTERM256[variant][role])
|
|
86
138
|
else:
|
|
87
|
-
table[role] =
|
|
139
|
+
table[role] = bold + '\033[%d;2;%d;%d;%dm' % ((layer,) + rgb(hex_of(variant, role)))
|
|
88
140
|
return table
|
|
89
141
|
|
|
90
142
|
|
|
@@ -194,18 +246,77 @@ def render_css():
|
|
|
194
246
|
return '\n'.join(out) + '\n'
|
|
195
247
|
|
|
196
248
|
|
|
249
|
+
# The docs page (plan W10 W6, LNS-107; web/docs.css) is its own HTML document, served by lanes-docsd, not the phone
|
|
250
|
+
# shell: no html.lanes-mobile, no Zellij data-zj-theme. Its block puts the roles it paints on :root, dark by default and
|
|
251
|
+
# light under the system's prefers-color-scheme, and only those roles (docs.css has a 2.5 KB cap; the full --css block
|
|
252
|
+
# alone is 2 KB). test_docs_assets pins that docs.css uses exactly these.
|
|
253
|
+
DOCS_ROLES = ('surface', 'surface-raised', 'line', 'ink', 'ink-dim', 'accent')
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def render_docs_css():
|
|
257
|
+
out = ['/* BEGIN lanes tokens (generated by libexec/tokens.py --docs-css; do not edit by hand) */', ':root {']
|
|
258
|
+
out += [f' --lanes-{role}: {TOKENS["dark"][role]};' for role in DOCS_ROLES]
|
|
259
|
+
out += [' color-scheme: dark;', '}', '@media (prefers-color-scheme: light) {', ' :root {']
|
|
260
|
+
out += [f' --lanes-{role}: {TOKENS["light"][role]};' for role in DOCS_ROLES]
|
|
261
|
+
out += [' color-scheme: light;', ' }', '}', '/* END lanes tokens */']
|
|
262
|
+
return '\n'.join(out) + '\n'
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
# The painted roles as the viewer's shell variables (`tokens.sh`), in PAINTED order. LZ_DIM kept its v1 name.
|
|
266
|
+
SH_NAMES = {'ink': 'LZ_INK', 'ink-dim': 'LZ_DIM', 'ink-faint': 'LZ_FAINT', 'accent': 'LZ_ACCENT',
|
|
267
|
+
'tone-urgent': 'LZ_URGENT', 'tone-attention': 'LZ_ATTENTION', 'tone-working': 'LZ_WORKING',
|
|
268
|
+
'tone-done': 'LZ_DONE', 'selected': 'LZ_SELECTED'}
|
|
269
|
+
SH_SETS = (('256', 'dark'), ('256', 'light'), ('truecolor', 'dark'), ('truecolor', 'light'))
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def _sh_set(mode, variant, indent):
|
|
273
|
+
"""One set's assignments: every painted role, then `LZ_VIVID` (`1` in a VIVID mode, else empty) — what view.sh asks
|
|
274
|
+
instead of re-matching the mode words itself (cockpit v3 P3)."""
|
|
275
|
+
table = sgr_table(mode, variant, dim16='\033[90m')
|
|
276
|
+
lines = [indent + "%s='%s'" % (SH_NAMES[role], table[role].replace('\033', '\\033')) for role in PAINTED]
|
|
277
|
+
return lines + [indent + "LZ_VIVID='%s'" % ('1' if mode in VIVID else '')]
|
|
278
|
+
|
|
279
|
+
|
|
197
280
|
def render_sh():
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
281
|
+
"""The viewer's colour sets as POSIX-shell literals holding `\\033` as text (they go into printf's FORMAT). A plain
|
|
282
|
+
`. tokens.sh` assigns the 16-colour set — the user's own palette; `lz_tones MODE VARIANT` re-points every LZ_* tone
|
|
283
|
+
variable to the 256 / truecolor set of dark / light, `lz_tones none` (one argument) to none, and anything else —
|
|
284
|
+
`lz_tones 16`, no argument, an unknown mode or variant — back to 16 colours. Every set also says `LZ_VIVID` (`1`
|
|
285
|
+
in truecolor and 256). The `case` word reads `${1:-}:${2:-}`: view.sh runs `set -u`, where a bare `$2` aborts the
|
|
286
|
+
shell on a one-argument call (DEF-1772). Assignments and one `case` only: sourcing or calling it forks nothing
|
|
287
|
+
(cockpit v3 P1). view.sh calls `lz_tones "$color" "$theme"` with its device's `.color`/`.theme` stamps on every
|
|
288
|
+
status screen (cockpit v3 P3)."""
|
|
289
|
+
lines = ['# BEGIN lanes tokens (generated by libexec/tokens.py --sh; do not edit by hand)',
|
|
290
|
+
"LZ_BOLD='\\033[1m'", "LZ_RESET='\\033[0m'"]
|
|
291
|
+
lines += _sh_set('16', 'dark', '')
|
|
292
|
+
lines += ['lz_tones() {', ' case "${1:-}:${2:-}" in']
|
|
293
|
+
for mode, variant in SH_SETS:
|
|
294
|
+
lines += [f' {mode}:{variant})'] + _sh_set(mode, variant, ' ') + [' ;;']
|
|
295
|
+
lines += [' none:*)'] + _sh_set('none', 'dark', ' ') + [' ;;']
|
|
296
|
+
lines += [' *)'] + _sh_set('16', 'dark', ' ') + [' ;;']
|
|
297
|
+
lines += [' esac', '}', '# END lanes tokens']
|
|
298
|
+
return '\n'.join(lines) + '\n'
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def _md_role(role):
|
|
302
|
+
"""One row of the role table: hex per variant, the 256 index per variant, the 16-colour SGR, the CSS variable."""
|
|
303
|
+
painted = role in PAINTED
|
|
304
|
+
x256 = [f'`{XTERM256[v][role]}`' if painted else '`—`' for v in ('dark', 'light')]
|
|
305
|
+
ansi = '—' if role in BACKGROUNDS else ANSI16.get(role, '—')
|
|
306
|
+
return (f'| {role} | `{hex_of("dark", role)}` | `{hex_of("light", role)}` | {x256[0]} | {x256[1]} | `{ansi}` | '
|
|
307
|
+
f'`--lanes-{BACKGROUNDS.get(role, role)}` |')
|
|
203
308
|
|
|
204
309
|
|
|
205
310
|
def render_md():
|
|
206
311
|
rows = ['<!-- BEGIN lanes tokens (generated by libexec/tokens.py --md; do not edit by hand) -->',
|
|
207
|
-
'| Role | Dark | Light | ANSI-16 | CSS variable |', '
|
|
208
|
-
|
|
312
|
+
'| Role | Dark | Light | 256 dark | 256 light | ANSI-16 | CSS variable |', '|---|---|---|---|---|---|---|']
|
|
313
|
+
for role in ROLES:
|
|
314
|
+
rows.append(_md_role(role))
|
|
315
|
+
rows += [_md_role(bg) for bg, source in BACKGROUNDS.items() if source == role] # `selected` under its wash
|
|
316
|
+
rows += ['', '| Slot | ANSI | Dark | Light | Role |', '|---|---|---|---|---|']
|
|
317
|
+
names = ANSI_NAMES + tuple('bright ' + n for n in ANSI_NAMES)
|
|
318
|
+
rows += [f'| {i} | {names[i]} | `{PALETTE16["dark"][i]}` | `{PALETTE16["light"][i]}` | {PALETTE16_ROLES.get(i, "—")} |'
|
|
319
|
+
for i in range(16)]
|
|
209
320
|
rows += ['', '| Glyph | Codepoint | ASCII | Tone | Meaning |', '|---|---|---|---|---|']
|
|
210
321
|
for name, table in (('agent', AGENT_GLYPHS), ('stream', GLYPHS), ('mark', MARKS), ('economy', ECONOMY_TAIL)):
|
|
211
322
|
for key, (glyph, ascii_twin, tone) in table.items():
|
|
@@ -242,14 +353,16 @@ def render_ghostty(variant):
|
|
|
242
353
|
|
|
243
354
|
|
|
244
355
|
RENDERERS = {'kdl': render_kdl, 'css': render_css, 'sh': render_sh, 'md': render_md, 'js': render_js,
|
|
245
|
-
'ghostty-dark': lambda: render_ghostty('dark'), 'ghostty-light': lambda: render_ghostty('light')
|
|
356
|
+
'ghostty-dark': lambda: render_ghostty('dark'), 'ghostty-light': lambda: render_ghostty('light'),
|
|
357
|
+
'docs-css': render_docs_css}
|
|
246
358
|
TARGETS = {'kdl': 'zellij/config.kdl', 'css': 'web/mobile.css', 'sh': 'libexec/tokens.sh', 'md': 'docs/DESIGN.md',
|
|
247
359
|
'js': 'web/mobile.js', 'ghostty-dark': 'client/ghostty/lanes-palette.conf',
|
|
248
|
-
'ghostty-light': 'client/ghostty/lanes-palette-light.conf'}
|
|
360
|
+
'ghostty-light': 'client/ghostty/lanes-palette-light.conf', 'docs-css': 'web/docs.css'}
|
|
249
361
|
MARKERS = {'kdl': ('// BEGIN lanes tokens', '// END lanes tokens'), 'css': ('/* BEGIN lanes tokens', '/* END lanes tokens */'),
|
|
250
362
|
'sh': ('# BEGIN lanes tokens', '# END lanes tokens'), 'md': ('<!-- BEGIN lanes tokens', '<!-- END lanes tokens -->'),
|
|
251
363
|
'js': ('// BEGIN lanes tokens', '// END lanes tokens'),
|
|
252
|
-
'ghostty-dark': ('# BEGIN lanes tokens', '# END lanes tokens'), 'ghostty-light': ('# BEGIN lanes tokens', '# END lanes tokens')
|
|
364
|
+
'ghostty-dark': ('# BEGIN lanes tokens', '# END lanes tokens'), 'ghostty-light': ('# BEGIN lanes tokens', '# END lanes tokens'),
|
|
365
|
+
'docs-css': ('/* BEGIN lanes tokens', '/* END lanes tokens */')}
|
|
253
366
|
|
|
254
367
|
|
|
255
368
|
def render(kind):
|
|
@@ -301,7 +414,7 @@ def main(argv):
|
|
|
301
414
|
return 0
|
|
302
415
|
kind = argv[0].lstrip('-') if argv else ''
|
|
303
416
|
if kind not in RENDERERS:
|
|
304
|
-
sys.exit('usage: tokens.py --kdl|--css|--sh|--md|--js|--ghostty-dark|--ghostty-light|--check|--write')
|
|
417
|
+
sys.exit('usage: tokens.py --kdl|--css|--sh|--md|--js|--ghostty-dark|--ghostty-light|--docs-css|--check|--write')
|
|
305
418
|
sys.stdout.write(render(kind))
|
|
306
419
|
return 0
|
|
307
420
|
|
|
@@ -1,11 +1,95 @@
|
|
|
1
|
-
# Lanes
|
|
1
|
+
# Lanes SGR literals for view.sh status screens: the 16-colour set on sourcing, and `lz_tones MODE VARIANT` for the
|
|
2
|
+
# 256-colour, truecolor and none sets (`lz_tones none` takes one argument; safe under view.sh's `set -u`). view.sh calls
|
|
3
|
+
# `lz_tones "$color" "$theme"` with its device's clients/<device>.color and .theme stamps on every status screen; every
|
|
4
|
+
# set also says LZ_VIVID (`1` in truecolor and 256, the colours Lanes defines). Sourced, never executed.
|
|
2
5
|
# The block below is generated: edit libexec/tokens.py and run `python3 libexec/tokens.py --write`.
|
|
3
6
|
# BEGIN lanes tokens (generated by libexec/tokens.py --sh; do not edit by hand)
|
|
4
7
|
LZ_BOLD='\033[1m'
|
|
8
|
+
LZ_RESET='\033[0m'
|
|
9
|
+
LZ_INK='\033[39m'
|
|
5
10
|
LZ_DIM='\033[90m'
|
|
11
|
+
LZ_FAINT='\033[90m'
|
|
12
|
+
LZ_ACCENT='\033[1;39m'
|
|
6
13
|
LZ_URGENT='\033[31m'
|
|
7
14
|
LZ_ATTENTION='\033[33m'
|
|
8
15
|
LZ_WORKING='\033[34m'
|
|
9
16
|
LZ_DONE='\033[32m'
|
|
10
|
-
|
|
17
|
+
LZ_SELECTED=''
|
|
18
|
+
LZ_VIVID=''
|
|
19
|
+
lz_tones() {
|
|
20
|
+
case "${1:-}:${2:-}" in
|
|
21
|
+
256:dark)
|
|
22
|
+
LZ_INK='\033[38;5;255m'
|
|
23
|
+
LZ_DIM='\033[38;5;249m'
|
|
24
|
+
LZ_FAINT='\033[38;5;243m'
|
|
25
|
+
LZ_ACCENT='\033[1m\033[38;5;115m'
|
|
26
|
+
LZ_URGENT='\033[38;5;209m'
|
|
27
|
+
LZ_ATTENTION='\033[38;5;215m'
|
|
28
|
+
LZ_WORKING='\033[38;5;75m'
|
|
29
|
+
LZ_DONE='\033[38;5;78m'
|
|
30
|
+
LZ_SELECTED='\033[48;5;235m'
|
|
31
|
+
LZ_VIVID='1'
|
|
32
|
+
;;
|
|
33
|
+
256:light)
|
|
34
|
+
LZ_INK='\033[38;5;233m'
|
|
35
|
+
LZ_DIM='\033[38;5;240m'
|
|
36
|
+
LZ_FAINT='\033[38;5;243m'
|
|
37
|
+
LZ_ACCENT='\033[1m\033[38;5;22m'
|
|
38
|
+
LZ_URGENT='\033[38;5;160m'
|
|
39
|
+
LZ_ATTENTION='\033[38;5;94m'
|
|
40
|
+
LZ_WORKING='\033[38;5;26m'
|
|
41
|
+
LZ_DONE='\033[38;5;22m'
|
|
42
|
+
LZ_SELECTED='\033[48;5;254m'
|
|
43
|
+
LZ_VIVID='1'
|
|
44
|
+
;;
|
|
45
|
+
truecolor:dark)
|
|
46
|
+
LZ_INK='\033[38;2;242;245;248m'
|
|
47
|
+
LZ_DIM='\033[38;2;166;177;190m'
|
|
48
|
+
LZ_FAINT='\033[38;2;111;122;135m'
|
|
49
|
+
LZ_ACCENT='\033[1m\033[38;2;143;227;166m'
|
|
50
|
+
LZ_URGENT='\033[38;2;255;123;110m'
|
|
51
|
+
LZ_ATTENTION='\033[38;2;255;194;51m'
|
|
52
|
+
LZ_WORKING='\033[38;2;111;182;255m'
|
|
53
|
+
LZ_DONE='\033[38;2;95;220;138m'
|
|
54
|
+
LZ_SELECTED='\033[48;2;28;51;38m'
|
|
55
|
+
LZ_VIVID='1'
|
|
56
|
+
;;
|
|
57
|
+
truecolor:light)
|
|
58
|
+
LZ_INK='\033[38;2;17;26;20m'
|
|
59
|
+
LZ_DIM='\033[38;2;74;86;97m'
|
|
60
|
+
LZ_FAINT='\033[38;2;111;123;135m'
|
|
61
|
+
LZ_ACCENT='\033[1m\033[38;2;31;111;63m'
|
|
62
|
+
LZ_URGENT='\033[38;2;196;28;28m'
|
|
63
|
+
LZ_ATTENTION='\033[38;2;160;77;0m'
|
|
64
|
+
LZ_WORKING='\033[38;2;26;86;196m'
|
|
65
|
+
LZ_DONE='\033[38;2;13;115;56m'
|
|
66
|
+
LZ_SELECTED='\033[48;2;229;240;230m'
|
|
67
|
+
LZ_VIVID='1'
|
|
68
|
+
;;
|
|
69
|
+
none:*)
|
|
70
|
+
LZ_INK=''
|
|
71
|
+
LZ_DIM=''
|
|
72
|
+
LZ_FAINT=''
|
|
73
|
+
LZ_ACCENT='\033[1m'
|
|
74
|
+
LZ_URGENT=''
|
|
75
|
+
LZ_ATTENTION=''
|
|
76
|
+
LZ_WORKING=''
|
|
77
|
+
LZ_DONE=''
|
|
78
|
+
LZ_SELECTED=''
|
|
79
|
+
LZ_VIVID=''
|
|
80
|
+
;;
|
|
81
|
+
*)
|
|
82
|
+
LZ_INK='\033[39m'
|
|
83
|
+
LZ_DIM='\033[90m'
|
|
84
|
+
LZ_FAINT='\033[90m'
|
|
85
|
+
LZ_ACCENT='\033[1;39m'
|
|
86
|
+
LZ_URGENT='\033[31m'
|
|
87
|
+
LZ_ATTENTION='\033[33m'
|
|
88
|
+
LZ_WORKING='\033[34m'
|
|
89
|
+
LZ_DONE='\033[32m'
|
|
90
|
+
LZ_SELECTED=''
|
|
91
|
+
LZ_VIVID=''
|
|
92
|
+
;;
|
|
93
|
+
esac
|
|
94
|
+
}
|
|
11
95
|
# END lanes tokens
|