Skip to content

Commit c263e7c

Browse files
committed
ci(primitives): transition to new css primitives
GitHub no longer distributes primitives in JSON format. Also, the names of the values (i.e. CSS variables) have changed. Most of the new names correspond 1-to-1 with one of the old names. Some colors have also changed slightly (e.g. `fg-default`), but otherwise remain mostly the same. See https://primer.style/foundations/primitives/migrating. Source color primitives from `@primer/primitives/dist/internalCss` instead of `@primer/primitives/dist/css/functional/themes` as only the former directory contains the base colors (scales). Convert new primer css primitives/variables directly to lua in .github/workflows/csstolua.lua (runs in CI). This script generates some debugging info in case an error occurs (which can be found in CI logs). Convert to a nested table structure for idiomatic usage in lua. The primitives table now provides type-hints via lsp, and accessing invalid names at runtime will throw an error. Append `.default` to names/keypaths which are too short and would otherwise collide with existing tables. - `scale` no longer exists, but is still provided (by us) for backwards-compatibility and ergonomics. The new names are in the format of `base.color.red[4]` (for example to access `scale.red[5]`). The values in `scale` are 1-indexed for lua, but the original upstream names (in `base.color.*`) are 0-indexed and left untouched. - `scale.gray` no longer exists, use `scale.neutral` in its place - `*.subtle` variants no longer exist, see the link above for the corresponding replacements
1 parent 6b46163 commit c263e7c

14 files changed

+496
-364
lines changed

.github/workflows/csstolua.lua

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
local res = {}
2+
3+
local function is_valid_ident(ident)
4+
local keyword = {
5+
['do'] = true,
6+
['end'] = true,
7+
['if'] = true,
8+
['then'] = true,
9+
['local'] = true,
10+
['function'] = true,
11+
['return'] = true,
12+
['while'] = true,
13+
['repeat'] = true,
14+
['until'] = true,
15+
['for'] = true,
16+
['in'] = true,
17+
['true'] = true,
18+
['false'] = true,
19+
['nil'] = true,
20+
}
21+
22+
if type(ident) ~= 'string' or keyword[ident] then
23+
return false
24+
end
25+
26+
return ident:find('^[_%a][_%w]*$') ~= nil
27+
end
28+
29+
local function set(cssvar, v)
30+
local before, last = cssvar:match('^(.+)%-+(.+)$')
31+
print(('\ncss variable: %s'):format(cssvar))
32+
print(('css variable prefix: %s'):format(before))
33+
print(('css variable suffix: %s'):format(last))
34+
print(('css variable value: %s'):format(v))
35+
36+
-- Top-level key
37+
if not last then
38+
res[tonumber(cssvar) or cssvar] = v
39+
return
40+
end
41+
42+
last = tonumber(last) or last
43+
local cur = res
44+
for k in before:gmatch('[^%-_]+') do
45+
k = tonumber(k) or k
46+
cur[k] = cur[k] or {}
47+
cur = cur[k]
48+
end
49+
50+
-- Path is too short: append `default`
51+
if type(cur[last]) == 'table' then
52+
local suffix = 'default'
53+
print(('keypath is too short, appending suffix "%s"'):format(suffix))
54+
print(('new name: %s-%s'):format(cssvar, suffix))
55+
cur, last = cur[last], suffix
56+
end
57+
58+
-- Check that duplicates have the same value
59+
if cur[last] ~= nil and cur[last] ~= v then
60+
error(([[
61+
variable appears multiple times with different values:
62+
- %s
63+
- %s
64+
]]):format(cur[last], v))
65+
end
66+
67+
cur[last] = v
68+
end
69+
70+
local function print_recur(value, _ind)
71+
_ind = _ind or 0
72+
73+
if type(value) == 'table' then
74+
io.write('m {')
75+
_ind = _ind + 2
76+
77+
for k, v in pairs(value) do
78+
local fmt = '[%q] = '
79+
if type(k) == 'number' then
80+
fmt = '[%s] = '
81+
elseif is_valid_ident(k) then
82+
fmt = '%s = '
83+
end
84+
io.write(('\n%s' .. fmt):format((' '):rep(_ind), k))
85+
print_recur(v, _ind)
86+
io.write(',')
87+
end
88+
89+
_ind = _ind - 2
90+
io.write(('\n%s}'):format((' '):rep(_ind)))
91+
else
92+
io.write(('%q'):format(value))
93+
end
94+
end
95+
96+
local defs = {}
97+
for ln in io.lines() do
98+
local k, v = ln:match('^%s*%-%-(%w.-)%s*:%s*(.-)%s*;%s*$')
99+
if k then
100+
table.insert(defs, { k, v })
101+
end
102+
end
103+
104+
-- Since we are un-flattening, ensure that longer keys (whose prefix could
105+
-- match another key) are visited first.
106+
table.sort(defs, function(a, b)
107+
return a[1] > b[1]
108+
end)
109+
110+
for _, kv in ipairs(defs) do
111+
set(unpack(kv))
112+
end
113+
114+
-- Add `scale` key for convenience and backwards-compatibility
115+
assert(res.scale == nil)
116+
res.scale = {}
117+
for color, scale in pairs(res.base.color) do
118+
if type(scale) == 'table' then
119+
res.scale[color] = {}
120+
for i, v in pairs(scale) do
121+
res.scale[color][i + 1] = v
122+
end
123+
else
124+
res.scale[color] = scale
125+
end
126+
end
127+
128+
-- NOTE: the metatable `mt` helps to catch errors (e.g. during CI tests)
129+
io.write([=[
130+
local mt = {
131+
__index = function(_, k)
132+
error('invalid index: ' .. k)
133+
end,
134+
}
135+
---@generic T
136+
---@param tbl T
137+
---@return T
138+
local function m(tbl)
139+
return setmetatable(tbl, mt)
140+
end
141+
local M = ]=])
142+
143+
print_recur(res)
144+
145+
io.write('\n')

.github/workflows/update-color-primitives.yml renamed to .github/workflows/update-primitives.yml

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
name: Get/Update Primer Color Primitives
1+
name: Get/Update Primer Primitives
22

33
env:
44
_DEST_DIR: '${{ github.workspace }}/lua/github-theme/palette/primitives'
5-
_JSON_DIR: '${{ github.workspace }}/node_modules/@primer/primitives/dist/json/colors'
5+
_SRC_DIR: '${{ github.workspace }}/node_modules/@primer/primitives/dist/internalCss'
66
_LICENSE_GLOB: '${{ github.workspace }}/node_modules/@primer/primitives/[Ll][Ii][Cc][Ee][Nn][Ss][Ee]*'
77
_PRIMITIVES_PKGJSON: '${{ github.workspace }}/node_modules/@primer/primitives/package.json'
8+
_CSSTOLUA: '${{ github.workspace }}/.github/workflows/csstolua.lua'
89

910
on:
1011
workflow_dispatch:
@@ -22,7 +23,7 @@ on:
2223
- scripts/**/*
2324

2425
jobs:
25-
get-colors:
26+
install-primitives:
2627
runs-on: ubuntu-latest
2728
permissions: # NOTE: `write` is not applied for PR's from forks
2829
checks: write
@@ -34,6 +35,9 @@ jobs:
3435

3536
steps:
3637
- uses: actions/checkout@v4
38+
- uses: rhysd/action-setup-vim@v1
39+
with:
40+
neovim: true
3741

3842
- uses: actions/setup-node@v4
3943
with:
@@ -44,21 +48,22 @@ jobs:
4448

4549
- name: Generate Lua files
4650
run: |
47-
set -u +f
51+
set -eu +f
4852
shopt -s nocaseglob failglob
4953
license="$(<$_LICENSE_GLOB)"
5054
rm -r "$_DEST_DIR" || :
5155
mkdir -p "$_DEST_DIR"
52-
cd "$_JSON_DIR"
56+
cd "$_SRC_DIR"
5357
5458
if jq -e .version "$_PRIMITIVES_PKGJSON"; then
5559
version="M._VERSION = vim.json.decode([=[$(jq -e .version "$_PRIMITIVES_PKGJSON")]=], { luanil = { object = false, array = false } })"
5660
fi
5761
58-
for file in *.json; do
59-
cat >|"${_DEST_DIR}/${file%.json}.lua" <<EOF
62+
for file in *.css; do
63+
values="$(nvim -l "$_CSSTOLUA" < "$file")"
64+
cat >| "${_DEST_DIR}/$(tr '-' '_' <<< "${file%.css}.lua")" <<EOF
6065
-- NOTE: THIS IS AN AUTO-GENERATED FILE. DO NOT EDIT BY-HAND.
61-
local M = vim.json.decode([=[$(<"$file")]=], { luanil = { object = false, array = false } })
66+
${values}
6267
${version-}
6368
M._LICENSE = [=[
6469
$license]=]
@@ -90,8 +95,8 @@ jobs:
9095
with:
9196
delete-branch: true
9297
branch: generated/primitives/${{ github.ref_name }}
93-
commit-message: 'deps: update color primitives'
94-
title: 'deps: update color primitives'
98+
commit-message: 'deps(primer): update primitives'
99+
title: 'deps(primer): update primitives'
95100
body: |
96101
| | |
97102
| ----------------- | ------------------------ |

lua/github-theme/group/modules/treesitter.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ function M.get(spec, config, _opts)
1313
:gsub('^github_(.-)$', '%1')
1414
)
1515

16-
local pl = primitives.prettylights
16+
local pl = primitives.color.prettylights
1717
local syn = spec.syntax
1818
local stl = config.styles
1919
local P = spec.palette

lua/github-theme/palette/github_dark.lua

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -8,53 +8,53 @@ local meta = {
88
local primitives =
99
require('github-theme.palette.primitives.' .. meta.name:gsub('^github%W*', '', 1))
1010

11-
local pl = primitives.prettylights
11+
local pl = primitives.color.prettylights
1212
local scale = primitives.scale
1313

1414
C.WHITE = C(scale.white)
1515
C.BLACK = C(scale.black)
16-
C.BG = C(scale.gray[7])
17-
local BG = C(scale.gray[7])
16+
C.BG = C(scale.neutral[7])
17+
local BG = C(scale.neutral[7])
1818

1919
local palette = {
2020
scale = scale,
2121

2222
orange = scale.orange[4],
2323

24-
black = { base = scale.gray[10], bright = scale.gray[9] },
25-
gray = { base = scale.gray[5], bright = scale.gray[5] },
24+
black = { base = scale.neutral[10], bright = scale.neutral[9] },
25+
gray = { base = scale.neutral[5], bright = scale.neutral[5] },
2626
blue = { base = scale.blue[4], bright = scale.blue[3] },
2727
green = { base = scale.green[4], bright = scale.green[3] },
2828
magenta = { base = scale.purple[4], bright = scale.purple[3] },
2929
pink = { base = scale.pink[4], bright = scale.pink[3] },
3030
red = { base = scale.red[4], bright = scale.red[3] },
31-
white = { base = scale.gray[3], bright = scale.gray[3] },
31+
white = { base = scale.neutral[3], bright = scale.neutral[3] },
3232
yellow = { base = scale.yellow[4], bright = scale.yellow[3] },
3333
cyan = { base = '#76e3ea', bright = '#b3f0ff' },
3434

3535
fg = {
3636
default = '#e6edf3',
3737
muted = '#7d8590',
38-
subtle = scale.gray[5],
38+
subtle = scale.neutral[5],
3939
on_emphasis = scale.white,
4040
},
4141

4242
canvas = {
43-
default = scale.gray[7],
44-
overlay = scale.gray[9],
45-
inset = scale.gray[8],
46-
subtle = scale.gray[9],
43+
default = scale.neutral[7],
44+
overlay = scale.neutral[9],
45+
inset = scale.neutral[8],
46+
subtle = scale.neutral[9],
4747
},
4848

4949
border = {
50-
default = scale.gray[9],
51-
muted = scale.gray[8],
50+
default = scale.neutral[9],
51+
muted = scale.neutral[8],
5252
subtle = BG:blend(C.from_rgba(240, 246, 252, 1), 0.1):to_css(),
5353
},
5454

5555
neutral = {
56-
emphasis_plus = scale.gray[5],
57-
emphasis = scale.gray[5],
56+
emphasis_plus = scale.neutral[5],
57+
emphasis = scale.neutral[5],
5858
muted = BG:blend(C.from_rgba(110, 118, 129, 1), 0.4):to_css(),
5959
subtle = BG:blend(C.from_rgba(110, 118, 129, 1), 0.1):to_css(),
6060
},
@@ -126,16 +126,16 @@ local palette = {
126126
local function generate_spec(pal)
127127
-- stylua: ignore start
128128
local spec = {
129-
bg0 = BG:blend(C(pal.canvas.inset), 0.75):to_css(), -- Dark bg (popup and float)
130-
bg1 = pal.canvas.default, -- Default bg
129+
bg0 = BG:blend(C(primitives.bgColor.inset), 0.75):to_css(), -- Dark bg (popup and float)
130+
bg1 = primitives.bgColor.default, -- Default bg
131131
bg2 = BG:blend(C(pal.neutral.emphasis), 0.1):to_css(), -- Lighter bg (colorcolumn Folds)
132-
bg3 = pal.scale.gray[6], -- Lighter bg (cursor line)
133-
bg4 = pal.scale.gray[4], -- Conceal
132+
bg3 = pal.scale.neutral[6], -- Lighter bg (cursor line)
133+
bg4 = pal.scale.neutral[4], -- Conceal
134134

135135
fg0 = pal.fg.subtle, -- Lighter fg
136-
fg1 = pal.fg.default, -- Default fg
136+
fg1 = primitives.fgColor.default, -- Default fg
137137
fg2 = pal.fg.muted, -- Darker fg (status line)
138-
fg3 = pal.scale.gray[5], -- Darker fg (line numbers, fold columns)
138+
fg3 = pal.scale.neutral[5], -- Darker fg (line numbers, fold columns)
139139

140140
sel0 = BG:blend(C(pal.accent.fg), 0.30):to_css(), -- Visual selection bg
141141
sel1 = BG:blend(C(pal.accent.muted), 0.90):to_css(), -- Popup sel bg
@@ -144,26 +144,26 @@ local function generate_spec(pal)
144144

145145
spec.syntax = {
146146
bracket = spec.fg1, -- Brackets and Punctuation
147-
builtin0 = pl.syntax.constant, -- Builtin variable
147+
builtin0 = pl.syntax.constant.default, -- Builtin variable
148148
builtin1 = pl.syntax.keyword, -- Builtin type
149-
builtin2 = pl.syntax.constant, -- Builtin const
149+
builtin2 = pl.syntax.constant.default, -- Builtin const
150150
comment = pl.syntax.comment, -- Comment
151151
conditional = pl.syntax.keyword, -- Conditional and loop
152-
const = pl.syntax.constant, -- Constants, imports and booleans
152+
const = pl.syntax.constant.default, -- Constants, imports and booleans
153153
dep = pal.scale.red[3], -- Deprecated
154-
field = pl.syntax.constant, -- Field
155-
func = pl.syntax.entity, -- Functions and Titles
154+
field = pl.syntax.constant.default, -- Field
155+
func = pl.syntax.entity.default, -- Functions and Titles
156156
ident = spec.fg1, -- Identifiers
157157
keyword = pl.syntax.keyword, -- Keywords
158-
number = pl.syntax.constant, -- Numbers
159-
operator = pl.syntax.constant, -- Operators
158+
number = pl.syntax.constant.default, -- Numbers
159+
operator = pl.syntax.constant.default, -- Operators
160160
param = spec.fg1, -- Parameters
161161
preproc = pl.syntax.keyword, -- PreProc
162-
regex = pl.syntax.string, -- Regex
162+
regex = pl.syntax.string.default, -- Regex
163163
statement = pl.syntax.keyword, -- Statements
164-
string = pl.syntax.string, -- Strings
164+
string = pl.syntax.string.default, -- Strings
165165
type = pl.syntax.variable, -- Types
166-
tag = pl.syntax.entityTag, -- Tags
166+
tag = pl.syntax.entity.tag, -- Tags
167167
variable = spec.fg1, -- Variables
168168
}
169169

0 commit comments

Comments
 (0)