Skip to content

Commit a520fed

Browse files
jxomtmm
andauthored
feat: ui (#216)
* wip * tweak * wip * wip * wip * colors * wip * wip * wip: more HStack + VStack * wip: shift colors * spacer * negated margins * update todo * columns * rows * text/heading * docs * box tweaks * heading tracking * tweaks * tweaks * docs * docs: getting started * chore: jsdoc * docs * docs * feat: Image & Icon * chore: format * docs: box cont. * tweak: refactor box ref dimensions; unit tokens on icon * fix: type * chore: update * docs: up * chore: format * wip: accept * feat: accept header * chore: format * chore: up * feat: html render google fonts * chore: tweaks * fix: icon encoding * chore: format * docs: icon * wip: ui-system docs * refactor: tokens -> vars * wip: docs * chore: tweaks * wip * tweak * wip * wip * wip * colors * wip * wip * wip: more HStack + VStack * wip: shift colors * spacer * negated margins * update todo * columns * rows * text/heading * docs * box tweaks * heading tracking * tweaks * tweaks * docs * docs: getting started * feat: Image & Icon * chore: jsdoc * docs * docs * chore: format * docs: box cont. * tweak: refactor box ref dimensions; unit tokens on icon * fix: type * chore: update * docs: up * wip: accept * feat: accept header * chore: format * chore: up * chore: format * feat: html render google fonts * chore: tweaks * fix: icon encoding * docs: icon * chore: format * wip: ui-system docs * refactor: tokens -> vars * chore: tweaks * wip: docs * docs: more * fix: dimensions * chore: format * feat: divider * chore: format * chore: tweaks * fix: types * chore: tweaks --------- Co-authored-by: Tom Meagher <[email protected]> Co-authored-by: tmm <[email protected]>
1 parent fc3c8b7 commit a520fed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+5743
-303
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@ create-frog/templates
99
node_modules
1010
site/dist
1111
src/ui/.frog
12+
src/ui/icons.ts
1213
tsconfig.*.tsbuildinfo
1314
tsconfig.tsbuildinfo

.scripts/gen-icons.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import path from 'node:path'
2+
import { IconifyJSONIconsData } from '@iconify/types'
3+
import { getIconData, iconToHTML, iconToSVG } from '@iconify/utils'
4+
import { glob } from 'fast-glob'
5+
6+
console.log('Copying icons to package.')
7+
8+
const collectionSet = new Set(['heroicons', 'lucide', 'radix-icons'])
9+
10+
const collections = (await glob('**/@iconify/json/json/*.json'))
11+
.map((collectionPath) => {
12+
const filename = path.basename(collectionPath)
13+
const ext = path.extname(filename)
14+
const name = filename.slice(0, -ext.length)
15+
return { name, path: collectionPath }
16+
})
17+
.filter((collection) => collectionSet.has(collection.name))
18+
19+
const iconMap: Record<string, Record<string, string>> = {}
20+
for (const collection of collectionSet) {
21+
iconMap[collection] = {}
22+
}
23+
24+
let count = 0
25+
for (const collection of collections) {
26+
const file = Bun.file(collection.path)
27+
const json = (await file.json()) as IconifyJSONIconsData
28+
29+
for (const key of Object.keys(json.icons)) {
30+
const item = getIconData(json, key)
31+
if (!item) throw new TypeError(`Invalid icon: ${key}`)
32+
const svg = iconToSVG(item)
33+
const text = iconToHTML(svg.body, svg.attributes)
34+
iconMap[collection.name][key] = encodeURIComponent(text)
35+
}
36+
37+
console.log(collection.name)
38+
count += 1
39+
}
40+
41+
const iconsExport = `export const icons = ${JSON.stringify(iconMap, null, 2)}`
42+
const dist = path.resolve(import.meta.dirname, '../src/ui')
43+
await Bun.write(`${dist}/icons.ts`, `${iconsExport}\n`)
44+
45+
console.log(
46+
`Done. Copied ${count} ${count === 1 ? 'collection' : 'collections'}.`,
47+
)

package.json

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,22 @@
44
"dev:playground": "pnpm --filter playground dev",
55
"dev:cli": "node --import tsx src/cli/index.ts",
66
"dev:create-frog": "bun create-frog/bin.ts",
7-
"dev:ui": "pnpm --filter ui dev",
8-
"build": "pnpm clean && pnpm protobufs:generate && pnpm build:frog && pnpm build:ui && pnpm build:create-frog && bun .scripts/postbuild.ts",
7+
"dev:ui": "pnpm --filter ui build --watch",
8+
"build": "pnpm clean && pnpm gen:icons && pnpm protobufs:generate && pnpm build:frog && pnpm build:ui && pnpm build:create-frog && bun .scripts/postbuild.ts",
99
"build:frog": "tsc --project ./tsconfig.build.json",
1010
"build:create-frog": "rimraf create-frog/_lib && tsc -p create-frog/tsconfig.build.json",
1111
"build:ui": "pnpm --filter ui build",
1212
"changeset": "changeset",
1313
"changeset:release": "pnpm build && changeset publish",
1414
"changeset:version": "changeset version && pnpm install --lockfile-only && bun .scripts/postversion.ts",
15-
"clean": "rimraf src/_lib src/tsconfig.build.tsbuildinfo",
15+
"clean": "rimraf src/_lib src/ui/icons.ts src/ui/.frog src/tsconfig.build.tsbuildinfo",
1616
"create-frog": "node --import tsx ./create-frog/bin.ts",
1717
"docs:dev": "pnpm --filter site dev",
1818
"docs:build": "pnpm --filter site build",
1919
"format": "biome format . --write",
2020
"lint": "biome check . --apply-unsafe",
21-
"postinstall": "pnpm build && pnpm preconstruct",
21+
"gen:icons": "bun .scripts/gen-icons.ts",
22+
"postinstall": "pnpm build && pnpm preconstruct && pnpm gen:icons",
2223
"preconstruct": "bun .scripts/preconstruct.ts",
2324
"protobufs:generate": "pnpm --filter protobufs generate",
2425
"test": "vitest",
@@ -28,6 +29,9 @@
2829
"@biomejs/biome": "^1.5.3",
2930
"@changesets/changelog-github": "^0.5.0",
3031
"@changesets/cli": "^2.27.1",
32+
"@iconify/json": "^2.2.196",
33+
"@iconify/types": "^2.0.0",
34+
"@iconify/utils": "^2.1.22",
3135
"@types/bun": "^1.0.8",
3236
"@types/fs-extra": "^11.0.4",
3337
"@types/node": "^20",

playground/public/frog.png

2.43 MB
Loading

playground/src/fonts.tsx

Lines changed: 7 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,16 @@
11
import { Frog } from 'frog'
2+
import { Heading, VStack, vars } from './ui.js'
23

34
export const app = new Frog({
4-
imageOptions: {
5-
fonts: [
6-
{
7-
name: 'Open Sans',
8-
weight: 400,
9-
source: 'google',
10-
},
11-
{
12-
name: 'Open Sans',
13-
weight: 700,
14-
source: 'google',
15-
},
16-
{
17-
name: 'Madimi One',
18-
source: 'google',
19-
},
20-
],
21-
},
5+
ui: { vars },
226
}).frame('/', (c) => {
237
return c.res({
248
image: (
25-
<div style={{ display: 'flex', flexDirection: 'column' }}>
26-
<div
27-
style={{
28-
color: 'white',
29-
fontFamily: 'Open Sans',
30-
display: 'flex',
31-
fontWeight: 400,
32-
fontSize: 60,
33-
}}
34-
>
35-
Open Sans (normal)
36-
</div>
37-
<div
38-
style={{
39-
color: 'white',
40-
fontFamily: 'Open Sans',
41-
display: 'flex',
42-
fontWeight: 700,
43-
fontSize: 60,
44-
}}
45-
>
46-
Open Sans (bold)
47-
</div>
48-
<div
49-
style={{
50-
color: 'white',
51-
fontFamily: 'Madimi One',
52-
display: 'flex',
53-
fontSize: 60,
54-
}}
55-
>
56-
Madimi One
57-
</div>
58-
</div>
9+
<VStack grow gap="4">
10+
<Heading>Open Sans (normal)</Heading>
11+
<Heading weight="600">Open Sans (bold)</Heading>
12+
<Heading font="madimi">Madimi One</Heading>
13+
</VStack>
5914
),
6015
})
6116
})

playground/src/index.tsx

Lines changed: 32 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,19 @@ import { serveStatic } from '@hono/node-server/serve-static'
22
import { Button, Frog, TextInput } from 'frog'
33
import { devtools } from 'frog/dev'
44
import * as hubs from 'frog/hubs'
5+
import { Box, Heading, vars } from './ui.js'
56

67
import { app as fontsApp } from './fonts.js'
78
import { app as middlewareApp } from './middleware.js'
89
import { app as neynarApp } from './neynar.js'
910
import { app as routingApp } from './routing.js'
1011
import { app as todoApp } from './todos.js'
1112
import { app as transactionApp } from './transaction.js'
13+
import { app as uiSystemApp } from './ui-system.js'
1214

1315
export const app = new Frog({
1416
hub: hubs.frog(),
17+
ui: { vars },
1518
verify: 'silent',
1619
})
1720
.frame('/', (c) => {
@@ -20,40 +23,22 @@ export const app = new Frog({
2023
return c.res({
2124
action: '/action',
2225
image: (
23-
<div
24-
tw="flex"
25-
style={{
26-
alignItems: 'center',
27-
background:
28-
status === 'response'
29-
? 'linear-gradient(to right, #432889, #17101F)'
30-
: 'black',
31-
backgroundSize: '100% 100%',
32-
flexDirection: 'column',
33-
flexWrap: 'nowrap',
34-
height: '100%',
35-
justifyContent: 'center',
36-
textAlign: 'center',
37-
width: '100%',
38-
}}
26+
<Box
27+
grow
28+
background={
29+
status === 'response'
30+
? { custom: 'linear-gradient(to right, #432889, #17101F)' }
31+
: 'background'
32+
}
33+
alignHorizontal="center"
34+
alignVertical="center"
3935
>
40-
<div
41-
style={{
42-
color: 'white',
43-
fontSize: 60,
44-
fontStyle: 'normal',
45-
letterSpacing: '-0.025em',
46-
lineHeight: 1.4,
47-
marginTop: 30,
48-
padding: '0 120px',
49-
whiteSpace: 'pre-wrap',
50-
}}
51-
>
36+
<Heading>
5237
{status === 'response'
5338
? `Nice choice.${fruit ? ` ${fruit.toUpperCase()}!!` : ''}`
5439
: 'Welcome :)'}
55-
</div>
56-
</div>
40+
</Heading>
41+
</Box>
5742
),
5843
intents: [
5944
<TextInput placeholder="Enter custom fruit" />,
@@ -70,20 +55,14 @@ export const app = new Frog({
7055
return c.res({
7156
action: '/',
7257
image: (
73-
<div
74-
style={{
75-
backgroundColor: '#1E1E4C',
76-
color: 'white',
77-
display: 'flex',
78-
alignItems: 'center',
79-
justifyContent: 'center',
80-
fontSize: 60,
81-
width: '100%',
82-
height: '100%',
83-
}}
58+
<Box
59+
grow
60+
background="blue100"
61+
alignHorizontal="center"
62+
alignVertical="center"
8463
>
85-
Yuck! {fruit}! Enter another one.
86-
</div>
64+
<Heading>Yuck! {fruit}! Enter another one.</Heading>
65+
</Box>
8766
),
8867
intents: [
8968
<Button value="watermelon">Watermelon</Button>,
@@ -94,21 +73,8 @@ export const app = new Frog({
9473
})
9574
})
9675
.frame('/buttons', (c) => {
97-
const { buttonValue } = c
9876
return c.res({
99-
image: (
100-
<div
101-
style={{
102-
backgroundColor: '#2D2D2D',
103-
display: 'flex',
104-
fontSize: 60,
105-
width: '100%',
106-
height: '100%',
107-
}}
108-
>
109-
{buttonValue ?? ''}
110-
</div>
111-
),
77+
image: <Box grow backgroundColor="red" />,
11278
intents: [
11379
<Button.Redirect location="http://github.com/honojs/vite-plugins/tree/main/packages/dev-server">
11480
Redirect
@@ -123,23 +89,13 @@ export const app = new Frog({
12389
})
12490
.frame('/no-intents', (c) => {
12591
return c.res({
126-
image: (
127-
<div
128-
style={{ backgroundColor: 'green', width: '100%', height: '100%' }}
129-
>
130-
foo
131-
</div>
132-
),
92+
image: <Box grow backgroundColor="red" />,
13393
imageAspectRatio: '1:1',
13494
})
13595
})
13696
.frame('/falsy-intents', (c) => {
13797
return c.res({
138-
image: (
139-
<div style={{ backgroundColor: 'red', width: '100%', height: '100%' }}>
140-
foo
141-
</div>
142-
),
98+
image: <Box grow backgroundColor="red" />,
14399
intents: [
144100
null,
145101
undefined,
@@ -164,9 +120,9 @@ export const app = new Frog({
164120
const { buttonValue } = c
165121
return c.res({
166122
image: (
167-
<div style={{ backgroundColor: 'red', width: '100%', height: '100%' }}>
123+
<Box grow backgroundColor="red">
168124
{buttonValue ?? 'foo'}
169-
</div>
125+
</Box>
170126
),
171127
intents: [
172128
<Button action="/" value="hello again">
@@ -185,9 +141,9 @@ export const app = new Frog({
185141
const { buttonValue } = c
186142
return c.res({
187143
image: (
188-
<div style={{ backgroundColor: 'red', width: '100%', height: '100%' }}>
144+
<Box grow backgroundColor="red">
189145
{buttonValue ?? 'foo'}
190-
</div>
146+
</Box>
191147
),
192148
intents: [
193149
<Button action="/button-action" value="back">
@@ -199,9 +155,9 @@ export const app = new Frog({
199155
.frame('/image-only', (c) => {
200156
return c.res({
201157
image: (
202-
<div style={{ backgroundColor: 'red', width: '100%', height: '100%' }}>
158+
<Box grow backgroundColor="red">
203159
foo
204-
</div>
160+
</Box>
205161
),
206162
})
207163
})
@@ -221,6 +177,7 @@ export const app = new Frog({
221177
],
222178
})
223179
})
180+
.route('/ui', uiSystemApp)
224181
.route('/fonts', fontsApp)
225182
.route('/middleware', middlewareApp)
226183
.route('/neynar', neynarApp)

playground/src/todos.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export const app = new Frog<{
1010
index: -1,
1111
todos: [],
1212
},
13+
verify: 'silent',
1314
})
1415
.frame('/', (c) => {
1516
const { buttonValue, deriveState, inputText } = c

0 commit comments

Comments
 (0)