@originkit/avatars 0.0.0-stage → 0.1.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/GUIDE.md +44 -0
- package/LICENSE +58 -0
- package/README.md +119 -2
- package/bin/avatars.js +65 -0
- package/dist/art.d.ts +43 -0
- package/dist/art.js +35 -0
- package/dist/auras/calming.d.ts +18 -0
- package/dist/auras/calming.js +30 -0
- package/dist/auras/controls.d.ts +2 -0
- package/dist/auras/controls.js +60 -0
- package/dist/auras/index.d.ts +119 -0
- package/dist/auras/index.js +355 -0
- package/dist/auras/skies.d.ts +13 -0
- package/dist/auras/skies.js +22 -0
- package/dist/blobs/color.d.ts +19 -0
- package/dist/blobs/color.js +113 -0
- package/dist/blobs/controls.d.ts +2 -0
- package/dist/blobs/controls.js +77 -0
- package/dist/blobs/index.d.ts +5 -0
- package/dist/blobs/index.js +107 -0
- package/dist/blobs/paths.d.ts +33 -0
- package/dist/blobs/paths.js +102 -0
- package/dist/blobs/shapes.d.ts +46 -0
- package/dist/blobs/shapes.js +186 -0
- package/dist/blobs/variance.d.ts +22 -0
- package/dist/blobs/variance.js +42 -0
- package/dist/bots/attachments.d.ts +24 -0
- package/dist/bots/attachments.js +119 -0
- package/dist/bots/calming.d.ts +4 -0
- package/dist/bots/calming.js +19 -0
- package/dist/bots/controls.d.ts +2 -0
- package/dist/bots/controls.js +78 -0
- package/dist/bots/faces.d.ts +23 -0
- package/dist/bots/faces.js +122 -0
- package/dist/bots/heads.d.ts +15 -0
- package/dist/bots/heads.js +66 -0
- package/dist/bots/index.d.ts +5 -0
- package/dist/bots/index.js +47 -0
- package/dist/bots/palette.d.ts +15 -0
- package/dist/bots/palette.js +30 -0
- package/dist/bots/paths.d.ts +25 -0
- package/dist/bots/paths.js +89 -0
- package/dist/bots/trim.d.ts +12 -0
- package/dist/bots/trim.js +37 -0
- package/dist/bubbles/calming.d.ts +7 -0
- package/dist/bubbles/calming.js +19 -0
- package/dist/bubbles/controls.d.ts +2 -0
- package/dist/bubbles/controls.js +69 -0
- package/dist/bubbles/index.d.ts +65 -0
- package/dist/bubbles/index.js +279 -0
- package/dist/buddies/calming.d.ts +8 -0
- package/dist/buddies/calming.js +18 -0
- package/dist/buddies/controls.d.ts +2 -0
- package/dist/buddies/controls.js +35 -0
- package/dist/buddies/index.d.ts +113 -0
- package/dist/buddies/index.js +363 -0
- package/dist/colors.d.ts +20 -0
- package/dist/colors.js +41 -0
- package/dist/combinations.d.ts +9 -0
- package/dist/combinations.js +28 -0
- package/dist/container.d.ts +14 -0
- package/dist/container.js +67 -0
- package/dist/controls.d.ts +128 -0
- package/dist/controls.js +170 -0
- package/dist/critters/calming.d.ts +8 -0
- package/dist/critters/calming.js +18 -0
- package/dist/critters/controls.d.ts +2 -0
- package/dist/critters/controls.js +49 -0
- package/dist/critters/index.d.ts +113 -0
- package/dist/critters/index.js +307 -0
- package/dist/doodles/calming.d.ts +7 -0
- package/dist/doodles/calming.js +18 -0
- package/dist/doodles/controls.d.ts +2 -0
- package/dist/doodles/controls.js +50 -0
- package/dist/doodles/index.d.ts +66 -0
- package/dist/doodles/index.js +236 -0
- package/dist/faces/adventurer-neutral.json +3168 -0
- package/dist/faces/calming.d.ts +8 -0
- package/dist/faces/calming.js +20 -0
- package/dist/faces/controls.d.ts +2 -0
- package/dist/faces/controls.js +38 -0
- package/dist/faces/index.d.ts +44 -0
- package/dist/faces/index.js +108 -0
- package/dist/glances/calming.d.ts +8 -0
- package/dist/glances/calming.js +16 -0
- package/dist/glances/controls.d.ts +2 -0
- package/dist/glances/controls.js +27 -0
- package/dist/glances/index.d.ts +54 -0
- package/dist/glances/index.js +79 -0
- package/dist/glyphs/calming.d.ts +8 -0
- package/dist/glyphs/calming.js +17 -0
- package/dist/glyphs/controls.d.ts +2 -0
- package/dist/glyphs/controls.js +55 -0
- package/dist/glyphs/index.d.ts +62 -0
- package/dist/glyphs/index.js +197 -0
- package/dist/grins/calming.d.ts +8 -0
- package/dist/grins/calming.js +21 -0
- package/dist/grins/controls.d.ts +2 -0
- package/dist/grins/controls.js +33 -0
- package/dist/grins/index.d.ts +81 -0
- package/dist/grins/index.js +240 -0
- package/dist/gummies/art.d.ts +77 -0
- package/dist/gummies/art.js +168 -0
- package/dist/gummies/calming.d.ts +8 -0
- package/dist/gummies/calming.js +21 -0
- package/dist/gummies/controls.d.ts +2 -0
- package/dist/gummies/controls.js +53 -0
- package/dist/gummies/index.d.ts +37 -0
- package/dist/gummies/index.js +283 -0
- package/dist/history.d.ts +21 -0
- package/dist/history.js +48 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +16 -0
- package/dist/kinds.d.ts +30 -0
- package/dist/kinds.js +266 -0
- package/dist/kitties/calming.d.ts +8 -0
- package/dist/kitties/calming.js +19 -0
- package/dist/kitties/controls.d.ts +2 -0
- package/dist/kitties/controls.js +43 -0
- package/dist/kitties/index.d.ts +181 -0
- package/dist/kitties/index.js +673 -0
- package/dist/knits/calming.d.ts +7 -0
- package/dist/knits/calming.js +19 -0
- package/dist/knits/controls.d.ts +2 -0
- package/dist/knits/controls.js +32 -0
- package/dist/knits/index.d.ts +74 -0
- package/dist/knits/index.js +445 -0
- package/dist/mallows/calming.d.ts +8 -0
- package/dist/mallows/calming.js +19 -0
- package/dist/mallows/controls.d.ts +2 -0
- package/dist/mallows/controls.js +45 -0
- package/dist/mallows/index.d.ts +111 -0
- package/dist/mallows/index.js +454 -0
- package/dist/peeks/calming.d.ts +8 -0
- package/dist/peeks/calming.js +21 -0
- package/dist/peeks/controls.d.ts +2 -0
- package/dist/peeks/controls.js +54 -0
- package/dist/peeks/index.d.ts +80 -0
- package/dist/peeks/index.js +225 -0
- package/dist/pixels/calming.d.ts +8 -0
- package/dist/pixels/calming.js +22 -0
- package/dist/pixels/controls.d.ts +3 -0
- package/dist/pixels/controls.js +82 -0
- package/dist/pixels/index.d.ts +34 -0
- package/dist/pixels/index.js +174 -0
- package/dist/pixels/palette.d.ts +29 -0
- package/dist/pixels/palette.js +107 -0
- package/dist/pixels/sprites.d.ts +42 -0
- package/dist/pixels/sprites.js +154 -0
- package/dist/possibilities.d.ts +39 -0
- package/dist/possibilities.js +88 -0
- package/dist/shapes/calming.d.ts +7 -0
- package/dist/shapes/calming.js +19 -0
- package/dist/shapes/controls.d.ts +2 -0
- package/dist/shapes/controls.js +41 -0
- package/dist/shapes/index.d.ts +44 -0
- package/dist/shapes/index.js +104 -0
- package/dist/share.d.ts +21 -0
- package/dist/share.js +83 -0
- package/dist/traits.d.ts +34 -0
- package/dist/traits.js +58 -0
- package/dist/types.d.ts +99 -0
- package/dist/types.js +1 -0
- package/dist/variants.d.ts +11 -0
- package/dist/variants.js +41 -0
- package/mcp/server.js +7 -0
- package/mcp/tools.js +70 -0
- package/package.json +54 -4
package/GUIDE.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Using OriginKit Avatars in your project
|
|
2
|
+
|
|
3
|
+
OriginKit Avatars turns a name into an avatar. The same name and style always give the same SVG, with no network. The styles are listed at the end.
|
|
4
|
+
|
|
5
|
+
## Pick a surface
|
|
6
|
+
|
|
7
|
+
- **MCP** (inside an AI app): the `create_avatar` tool returns the SVG. Use it to look at avatars or to write one file. It is not a runtime dependency of your app.
|
|
8
|
+
- **CLI**: `npx @originkit/avatars <style> <name> [-o file.svg]`, and `npx @originkit/avatars --list` for the styles. Use it in scripts and build steps.
|
|
9
|
+
- **npm**: `npm install @originkit/avatars`, then `import { createAvatar, styles } from "@originkit/avatars"`. Use it when your app draws avatars itself. `createAvatar(style, name)` returns an SVG string and throws on an unknown style.
|
|
10
|
+
- **API**: not live yet. It is coming. Don't call or invent endpoints.
|
|
11
|
+
|
|
12
|
+
## Recipes
|
|
13
|
+
|
|
14
|
+
React or Next.js. The output is trusted, generated SVG, so a data URI is simplest:
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
import { createAvatar } from "@originkit/avatars";
|
|
18
|
+
|
|
19
|
+
const src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(createAvatar("bots", user.id))}`;
|
|
20
|
+
return <img src={src} alt="" width={48} height={48} />;
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Use `<img>`, not inline SVG. Each `<img>` is its own document, while inline SVGs of one style share ids and the second one draws wrong.
|
|
24
|
+
|
|
25
|
+
Plain HTML: write the SVG to a file with the CLI, then `<img src="bobo.svg" alt="">`.
|
|
26
|
+
|
|
27
|
+
Node script that writes files:
|
|
28
|
+
|
|
29
|
+
```js
|
|
30
|
+
import { writeFileSync } from "node:fs";
|
|
31
|
+
import { createAvatar } from "@originkit/avatars";
|
|
32
|
+
|
|
33
|
+
writeFileSync("bobo.svg", createAvatar("bots", "Bobo"));
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Seed with something stable, such as a user id or email, so a user keeps their avatar. A display name that can change will change the avatar.
|
|
37
|
+
|
|
38
|
+
Pick one style for the product from the list at the end of this guide, and use it everywhere. Mixing styles per user is fine too.
|
|
39
|
+
|
|
40
|
+
## Facts
|
|
41
|
+
|
|
42
|
+
- Same input, same SVG, within a major version of the package. A change to how avatars look is always a new major version. There is no randomness and no state.
|
|
43
|
+
- Output is SVG only. For PNG, draw the SVG onto a browser canvas.
|
|
44
|
+
- Faces avatars use "Adventurer Neutral" by Lisa Wischofsky, CC BY 4.0. Where Faces are shown, your app must show that credit visibly, with a link to https://creativecommons.org/licenses/by/4.0/. The SVG carries it as a comment, which is not enough on its own.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 landerdevelopers
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
-------------------------------------------------------------------------------
|
|
24
|
+
Third-party notices
|
|
25
|
+
-------------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
The name hashing in traits.ts, the whole of blobs/, and the superellipse in
|
|
28
|
+
bots/paths.ts are adapted from blobatar (https://github.com/Alain00/blobatar),
|
|
29
|
+
used under the MIT License.
|
|
30
|
+
|
|
31
|
+
The art in faces/adventurer-neutral.json is a remix of "Adventurer Neutral"
|
|
32
|
+
(https://www.figma.com/community/file/1184595184137881796) by Lisa Wischofsky,
|
|
33
|
+
licensed under CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/), as
|
|
34
|
+
distributed by DiceBear. Changes: pupils separated and animated, reactions and
|
|
35
|
+
tap effects added. CC BY 4.0 requires this credit to be visible to anyone a
|
|
36
|
+
published version of Faces reaches.
|
|
37
|
+
|
|
38
|
+
MIT License
|
|
39
|
+
|
|
40
|
+
Copyright (c) 2026 Alain
|
|
41
|
+
|
|
42
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
43
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
44
|
+
in the Software without restriction, including without limitation the rights
|
|
45
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
46
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
47
|
+
furnished to do so, subject to the following conditions:
|
|
48
|
+
|
|
49
|
+
The above copyright notice and this permission notice shall be included in all
|
|
50
|
+
copies or substantial portions of the Software.
|
|
51
|
+
|
|
52
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
53
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
54
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
55
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
56
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
57
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
58
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,120 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @originkit/avatars
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
OriginKit Avatars turns any name into an avatar. 18 styles, SVG out, no network. The same name and style always give the same avatar. One package holds the library, the CLI and the MCP server.
|
|
4
|
+
|
|
5
|
+
Try every style in the studio: https://originkit.dev/avatars
|
|
6
|
+
|
|
7
|
+
## Library
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm install @originkit/avatars
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
```js
|
|
14
|
+
import { createAvatar, styles } from "@originkit/avatars";
|
|
15
|
+
|
|
16
|
+
const svg = createAvatar("bots", "user-8f3a2c"); // an SVG string
|
|
17
|
+
styles; // [{ id: "bots", label: "Bots" }, { id: "blobs", label: "Blobs" }, ...]
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The package exports two things:
|
|
21
|
+
|
|
22
|
+
- `createAvatar(style, name)`: returns the avatar as an SVG string. It throws on a style id that doesn't exist, and the error lists the ids.
|
|
23
|
+
- `styles`: every style as `{ id, label }`, in the studio's order.
|
|
24
|
+
|
|
25
|
+
Seed it with something stable, such as a user id, so each user keeps their avatar. A display name that can change will change the avatar.
|
|
26
|
+
|
|
27
|
+
In a web page, use an `<img>` with a data URI rather than inline SVG, because inline SVGs of one style share ids:
|
|
28
|
+
|
|
29
|
+
```jsx
|
|
30
|
+
<img src={`data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`} alt="" width={48} height={48} />
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## CLI
|
|
34
|
+
|
|
35
|
+
Run it with npx, or install it and call `avatars`.
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
npx @originkit/avatars bots Bobo > bobo.svg
|
|
39
|
+
npx @originkit/avatars bots Bobo -o bobo.svg
|
|
40
|
+
npx @originkit/avatars --list
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## MCP
|
|
44
|
+
|
|
45
|
+
`npx -y @originkit/avatars mcp` runs the MCP server over stdio. It is also hosted at `https://originkit.dev/avatars/mcp`.
|
|
46
|
+
|
|
47
|
+
- `list_styles`: the style ids and labels.
|
|
48
|
+
- `create_avatar`: `style` (an id from `list_styles`) and `name` (1 to 100 characters). Returns the SVG as text, and again as an embedded `image/svg+xml` resource.
|
|
49
|
+
- `usage_guide`: how to add OriginKit Avatars to a project through the CLI, the npm package or MCP. Also served as the resource `avatars://guide`, and the server's instructions point agents to it.
|
|
50
|
+
|
|
51
|
+
The server doesn't write files. The client saves the SVG where it wants it.
|
|
52
|
+
|
|
53
|
+
Claude Code:
|
|
54
|
+
|
|
55
|
+
```sh
|
|
56
|
+
claude mcp add avatars -- npx -y @originkit/avatars mcp
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Codex:
|
|
60
|
+
|
|
61
|
+
```sh
|
|
62
|
+
codex mcp add avatars -- npx -y @originkit/avatars mcp
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Claude on the web, ChatGPT and other apps that take a URL: add a custom connector with `https://originkit.dev/avatars/mcp` (no auth).
|
|
66
|
+
|
|
67
|
+
Cursor, VS Code: one-click buttons on the [docs page](https://www.originkit.dev/docs/avatars).
|
|
68
|
+
|
|
69
|
+
Claude Desktop and other clients (`claude_desktop_config.json` or similar):
|
|
70
|
+
|
|
71
|
+
```json
|
|
72
|
+
{
|
|
73
|
+
"mcpServers": {
|
|
74
|
+
"avatars": { "command": "npx", "args": ["-y", "@originkit/avatars", "mcp"] }
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Styles
|
|
80
|
+
|
|
81
|
+
Pass the id to `createAvatar`, the CLI or the MCP server.
|
|
82
|
+
|
|
83
|
+
| Style | Id | Look |
|
|
84
|
+
| --- | --- | --- |
|
|
85
|
+
| Bots | `bots` | A robot head with parts bolted on |
|
|
86
|
+
| Blobs | `blobs` | A soft blob with a face |
|
|
87
|
+
| Gummies | `gummies` | A jelly, lit from inside |
|
|
88
|
+
| Mallows | `mallows` | Soft clay: a bun, a ghost or a cloud |
|
|
89
|
+
| Faces | `faces` | An illustrated person (needs a credit, see Licence) |
|
|
90
|
+
| Bubbles | `bubbles` | A soft gradient shape under fine grain, with two eyes |
|
|
91
|
+
| Pixels | `pixels` | A pixel-art person |
|
|
92
|
+
| Glyphs | `glyphs` | A solid silhouette with two eyes, on a rounded tile |
|
|
93
|
+
| Peeks | `peeks` | A creature peering into a bright tile, with big glossy eyes |
|
|
94
|
+
| Doodles | `doodles` | A hand-drawn face showing a feeling |
|
|
95
|
+
| Animals | `animals` | A cat, a dog, an owl, a bear or a tiger |
|
|
96
|
+
| Buddies | `buddies` | A tabby or a calico cat |
|
|
97
|
+
| Shapes | `shapes` | A face put together from bold shapes |
|
|
98
|
+
| Critters | `critters` | A monster, a cyclops, a bear, a duck, a chick, an imp or a grump, up close |
|
|
99
|
+
| Grins | `grins` | A button of a face, a tile or round, with a toothy grin |
|
|
100
|
+
| Knits | `knits` | A bust in a knitted balaclava and a ribbed beanie |
|
|
101
|
+
| Glances | `glances` | Just two eyes |
|
|
102
|
+
| Auras | `auras` | Soft colour melting together under fine grain, or a sky, on a rounded tile |
|
|
103
|
+
|
|
104
|
+
Bots, Blobs, Gummies and Mallows are drawn in 3D in the studio. The package returns a flat SVG of each. Buddies and Knits are in the package but not shown in the studio for now.
|
|
105
|
+
|
|
106
|
+
`npx @originkit/avatars --list` prints the same list.
|
|
107
|
+
|
|
108
|
+
## Stability
|
|
109
|
+
|
|
110
|
+
Within a major version, the same input always gives the same SVG. Any change to how avatars look is released as a new major version.
|
|
111
|
+
|
|
112
|
+
## Licence
|
|
113
|
+
|
|
114
|
+
MIT. The Faces style uses "Adventurer Neutral" by Lisa Wischofsky, CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/). Show that credit wherever Faces avatars appear. Faces SVGs also carry it as a comment. See LICENSE for third-party notices.
|
|
115
|
+
|
|
116
|
+
## Test
|
|
117
|
+
|
|
118
|
+
```sh
|
|
119
|
+
npm test --workspace @originkit/avatars
|
|
120
|
+
```
|
package/bin/avatars.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { writeFileSync } from "node:fs";
|
|
3
|
+
import { parseArgs } from "node:util";
|
|
4
|
+
import { createAvatar, styles } from "../dist/index.js";
|
|
5
|
+
|
|
6
|
+
const HELP = `OriginKit Avatars: turn any name into an avatar.
|
|
7
|
+
|
|
8
|
+
Usage
|
|
9
|
+
avatars <style> <name...> print the SVG
|
|
10
|
+
avatars <style> <name...> -o a.svg write it to a file
|
|
11
|
+
avatars --list show the styles
|
|
12
|
+
avatars mcp run the MCP server over stdio
|
|
13
|
+
|
|
14
|
+
Example
|
|
15
|
+
npx @originkit/avatars bots Bobo > bobo.svg`;
|
|
16
|
+
|
|
17
|
+
const fail = (message) => {
|
|
18
|
+
process.stderr.write(`${message}\n`);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// `avatars mcp` hands the process to the MCP server, which owns stdout from here.
|
|
23
|
+
if (process.argv[2] === "mcp") {
|
|
24
|
+
await import("../mcp/server.js");
|
|
25
|
+
} else {
|
|
26
|
+
run();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function run() {
|
|
30
|
+
let args;
|
|
31
|
+
try {
|
|
32
|
+
args = parseArgs({
|
|
33
|
+
allowPositionals: true,
|
|
34
|
+
options: {
|
|
35
|
+
out: { type: "string", short: "o" },
|
|
36
|
+
list: { type: "boolean", short: "l" },
|
|
37
|
+
help: { type: "boolean", short: "h" },
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
} catch (error) {
|
|
41
|
+
fail(`${error.message}\n\n${HELP}`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const { values, positionals } = args;
|
|
45
|
+
|
|
46
|
+
if (values.help) {
|
|
47
|
+
process.stdout.write(`${HELP}\n`);
|
|
48
|
+
} else if (values.list) {
|
|
49
|
+
for (const { id, label } of styles) process.stdout.write(`${id.padEnd(10)}${label}\n`);
|
|
50
|
+
} else {
|
|
51
|
+
const [style, ...words] = positionals;
|
|
52
|
+
const name = words.join(" ").trim();
|
|
53
|
+
if (!style || !name) fail(HELP);
|
|
54
|
+
|
|
55
|
+
let svg;
|
|
56
|
+
try {
|
|
57
|
+
svg = createAvatar(style, name);
|
|
58
|
+
} catch (error) {
|
|
59
|
+
fail(error.message);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (values.out) writeFileSync(values.out, svg);
|
|
63
|
+
else process.stdout.write(svg);
|
|
64
|
+
}
|
|
65
|
+
}
|
package/dist/art.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/** Draws art from DiceBear style definitions, which describe SVG as JSON. */
|
|
2
|
+
import type { Traits } from "./traits.ts";
|
|
3
|
+
export type Attribute = string | {
|
|
4
|
+
type: "color";
|
|
5
|
+
name: string;
|
|
6
|
+
};
|
|
7
|
+
export interface ArtElement {
|
|
8
|
+
name: string;
|
|
9
|
+
attributes?: Record<string, Attribute>;
|
|
10
|
+
children?: ArtElement[];
|
|
11
|
+
}
|
|
12
|
+
export interface Variant {
|
|
13
|
+
elements: ArtElement[];
|
|
14
|
+
weight?: number;
|
|
15
|
+
tags?: string[];
|
|
16
|
+
}
|
|
17
|
+
export interface Component {
|
|
18
|
+
width: number;
|
|
19
|
+
height: number;
|
|
20
|
+
probability?: number;
|
|
21
|
+
translate?: {
|
|
22
|
+
x?: {
|
|
23
|
+
min: number;
|
|
24
|
+
max: number;
|
|
25
|
+
};
|
|
26
|
+
y?: {
|
|
27
|
+
min: number;
|
|
28
|
+
max: number;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
variants: Record<string, Variant>;
|
|
32
|
+
}
|
|
33
|
+
/** `idPrefix` keeps ids unique per face, because a hidden face must not own a shared mask. */
|
|
34
|
+
export declare function draw(element: ArtElement, colors: Record<string, string>, idPrefix: string): string;
|
|
35
|
+
/**
|
|
36
|
+
* Moves the ids that start `stem-` to `ids-`, with every reference to them, so two live
|
|
37
|
+
* avatars on one page never share one. With `ids` left as the stem, nothing changes.
|
|
38
|
+
*/
|
|
39
|
+
export declare const scopeIds: (markup: string, stem: string, ids: string) => string;
|
|
40
|
+
/** `scopeIds` over every piece of markup in an avatar, lists such as ears included. */
|
|
41
|
+
export declare function scopeAll<T extends object>(avatar: T, stem: string, ids: string): T;
|
|
42
|
+
/** A weighted choice, limited to `names` when given. */
|
|
43
|
+
export declare function pickVariant(t: Traits, key: string, variants: Record<string, Variant>, names?: string[]): string;
|
package/dist/art.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/** Draws art from DiceBear style definitions, which describe SVG as JSON. */
|
|
2
|
+
/** `idPrefix` keeps ids unique per face, because a hidden face must not own a shared mask. */
|
|
3
|
+
export function draw(element, colors, idPrefix) {
|
|
4
|
+
const attributes = Object.entries(element.attributes ?? {})
|
|
5
|
+
.map(([key, raw]) => {
|
|
6
|
+
const value = typeof raw === "string" ? raw : colors[raw.name];
|
|
7
|
+
const scoped = key === "id" ? `${idPrefix}${value}` : value.replace("url(#", `url(#${idPrefix}`);
|
|
8
|
+
return ` ${key}="${scoped}"`;
|
|
9
|
+
})
|
|
10
|
+
.join("");
|
|
11
|
+
const children = (element.children ?? []).map((child) => draw(child, colors, idPrefix)).join("");
|
|
12
|
+
return `<${element.name}${attributes}>${children}</${element.name}>`;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Moves the ids that start `stem-` to `ids-`, with every reference to them, so two live
|
|
16
|
+
* avatars on one page never share one. With `ids` left as the stem, nothing changes.
|
|
17
|
+
*/
|
|
18
|
+
export const scopeIds = (markup, stem, ids) => ids === stem ? markup : markup.replace(new RegExp(`(id="|href="#|url\\(#)${stem}-`, "g"), `$1${ids}-`);
|
|
19
|
+
/** `scopeIds` over every piece of markup in an avatar, lists such as ears included. */
|
|
20
|
+
export function scopeAll(avatar, stem, ids) {
|
|
21
|
+
if (ids === stem)
|
|
22
|
+
return avatar;
|
|
23
|
+
const scope = (value) => typeof value === "string" ? scopeIds(value, stem, ids) : Array.isArray(value) ? value.map(scope) : value;
|
|
24
|
+
return Object.fromEntries(Object.entries(avatar).map(([key, value]) => [key, scope(value)]));
|
|
25
|
+
}
|
|
26
|
+
/** A weighted choice, limited to `names` when given. */
|
|
27
|
+
export function pickVariant(t, key, variants, names = Object.keys(variants)) {
|
|
28
|
+
let remaining = t(key) * names.reduce((sum, name) => sum + (variants[name].weight ?? 1), 0);
|
|
29
|
+
for (const name of names) {
|
|
30
|
+
remaining -= variants[name].weight ?? 1;
|
|
31
|
+
if (remaining < 0)
|
|
32
|
+
return name;
|
|
33
|
+
}
|
|
34
|
+
return names[names.length - 1];
|
|
35
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What Possibilities calms on an aura or a sky. Low keeps the eight calmest formations and
|
|
3
|
+
* palettes, one for each variant, and draws the turn, the
|
|
4
|
+
* softness and where each shape sits towards their middle. The grain's range and how far a
|
|
5
|
+
* sky's horizon strays grow with it too, in `index.ts`. Indices are plainest first.
|
|
6
|
+
*/
|
|
7
|
+
import type { Calming } from "../possibilities.ts";
|
|
8
|
+
export declare const CALMING: Calming;
|
|
9
|
+
/**
|
|
10
|
+
* Each type's palettes, plainest first, which Possibilities keeps eight of at the lowest. For
|
|
11
|
+
* skies, the gentle daytime ones, then dusk, and night last; for auras, the soft pastels, then
|
|
12
|
+
* the deep.
|
|
13
|
+
*/
|
|
14
|
+
export declare const PALETTE_ORDER: {
|
|
15
|
+
Sky: number[];
|
|
16
|
+
Aura: number[];
|
|
17
|
+
};
|
|
18
|
+
export declare const NOTES: [string, string, string];
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What Possibilities calms on an aura or a sky. Low keeps the eight calmest formations and
|
|
3
|
+
* palettes, one for each variant, and draws the turn, the
|
|
4
|
+
* softness and where each shape sits towards their middle. The grain's range and how far a
|
|
5
|
+
* sky's horizon strays grow with it too, in `index.ts`. Indices are plainest first.
|
|
6
|
+
*/
|
|
7
|
+
export const CALMING = {
|
|
8
|
+
features: /^flow\.[a-z]$|^(turn|softness)$/,
|
|
9
|
+
lists: {
|
|
10
|
+
// Glow, Arc, Bloom, Drift, Horizon, Split, Swirl, Break, then Veil, Rise, Rings and Streak.
|
|
11
|
+
flow: [12, 8, [0, 3, 8, 5, 1, 7, 4, 6, 9, 10, 11, 2]],
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Each type's palettes, plainest first, which Possibilities keeps eight of at the lowest. For
|
|
16
|
+
* skies, the gentle daytime ones, then dusk, and night last; for auras, the soft pastels, then
|
|
17
|
+
* the deep.
|
|
18
|
+
*/
|
|
19
|
+
export const PALETTE_ORDER = {
|
|
20
|
+
// Clear, Dawn, Mist, Noon, Pastel, Haze, Golden, Dusk, then Evening, Orchid, Ember and Night.
|
|
21
|
+
Sky: [0, 2, 8, 5, 4, 11, 6, 9, 1, 10, 3, 7],
|
|
22
|
+
// Sunrise, Meadow, Twilight, Sky, Dream, Blossom, Fern, Craft, then Flowy, Mint, Ember,
|
|
23
|
+
// Aurora, Nebula, Cobalt and Forest.
|
|
24
|
+
Aura: [0, 2, 5, 8, 6, 3, 13, 9, 11, 14, 1, 4, 7, 10, 12],
|
|
25
|
+
};
|
|
26
|
+
export const NOTES = [
|
|
27
|
+
"calm glows and gentle daytime skies, upright, nearly smooth, the horizon in its place.",
|
|
28
|
+
"more formations and skies, grainier, the horizon higher or lower.",
|
|
29
|
+
"every formation and sky, up to the deepest and night, at any grain and horizon.",
|
|
30
|
+
];
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { choice, clearingColor, slider } from "../controls.js";
|
|
2
|
+
import { narrowed, possibilitiesSlider } from "../possibilities.js";
|
|
3
|
+
import { colorKey } from "../traits.js";
|
|
4
|
+
import { CALMING, NOTES } from "./calming.js";
|
|
5
|
+
import { auraTraits, coloursOf, FACES, faceOf, flowOf, FLOWS, horizonOf, paletteOf, palettesOf, palettesOffered, PARTS, TYPES, typeOf } from "./index.js";
|
|
6
|
+
export const controls = (name, overrides) => {
|
|
7
|
+
const t = auraTraits(name, overrides);
|
|
8
|
+
const type = typeOf(name, overrides);
|
|
9
|
+
const colours = coloursOf(name, overrides, t, type);
|
|
10
|
+
const face = faceOf(t);
|
|
11
|
+
const sky = type === "Sky";
|
|
12
|
+
// A sky's palette swatch is the sky itself, top to bottom; an aura's shows its four colours.
|
|
13
|
+
const swatch = ({ colors, stops }) => stops ? `linear-gradient(${stops.join(", ")})` : `conic-gradient(${colors.map((color, i) => `${color} 0 ${(i + 1) * 25}%`).join(", ")})`;
|
|
14
|
+
return [
|
|
15
|
+
{
|
|
16
|
+
title: "Aura",
|
|
17
|
+
controls: [
|
|
18
|
+
// Each showing what's drawn, which for a variant is a step on from its name's own.
|
|
19
|
+
{ ...choice(t, "type", "Type", "list", TYPES.map((label) => ({ label }))), selected: String(TYPES.indexOf(type)) },
|
|
20
|
+
...(sky
|
|
21
|
+
? []
|
|
22
|
+
: [{ ...narrowed(choice(t, "flow", "Formation", "list", FLOWS.map(({ name: label }) => ({ label }))), t, CALMING), selected: String(FLOWS.indexOf(flowOf(name, overrides))) }]),
|
|
23
|
+
// A new palette starts from its own colours, not the ones set by hand for the last.
|
|
24
|
+
(() => {
|
|
25
|
+
const control = PARTS.reduce(clearingColor, choice(t, "palette", sky ? "Sky" : "Palette", "swatches", palettesOf(type).map((palette) => ({ label: palette.label, color: swatch(palette) }))));
|
|
26
|
+
const selected = String(paletteOf(name, overrides, type));
|
|
27
|
+
// Only those Possibilities offers, plus the one drawn, as with Formation.
|
|
28
|
+
const offered = new Set(palettesOffered(t, type).map(String));
|
|
29
|
+
return { ...control, selected, options: control.options.filter((option) => offered.has(option.id) || option.id === selected) };
|
|
30
|
+
})(),
|
|
31
|
+
// Up front for a sky, beside its palette. Level with the reference's until moved.
|
|
32
|
+
...(sky
|
|
33
|
+
? [{
|
|
34
|
+
type: "slider",
|
|
35
|
+
id: "horizon",
|
|
36
|
+
label: "Horizon",
|
|
37
|
+
value: (horizonOf(t) + 1) / 2,
|
|
38
|
+
display: `${Math.round(horizonOf(t) * 100)}%`,
|
|
39
|
+
range: [-100, 100],
|
|
40
|
+
ends: ["Lower", "Higher"],
|
|
41
|
+
}]
|
|
42
|
+
: []),
|
|
43
|
+
{ type: "colors", id: "aura.colors", label: "Colours", colors: PARTS.map((part) => ({ id: colorKey(part), color: colours[part] })) },
|
|
44
|
+
possibilitiesSlider(t, NOTES),
|
|
45
|
+
slider(t, "grain", "Grain", 0, 100, "%"),
|
|
46
|
+
...(sky ? [] : [slider(t, "softness", "Softness", 0, 100, "%"), slider(t, "turn", "Turn", -30, 30, "°")]),
|
|
47
|
+
],
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
title: "Face",
|
|
51
|
+
controls: [
|
|
52
|
+
{
|
|
53
|
+
...choice(t, "face", "Face", "list", FACES.map((label) => ({ label }))),
|
|
54
|
+
// None until one is picked, whatever the name, as the reference's tiles have none.
|
|
55
|
+
selected: String(FACES.indexOf(face)),
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
},
|
|
59
|
+
];
|
|
60
|
+
};
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auras: a rounded tile of colour, after the reference. Two to four colours melt into each
|
|
3
|
+
* other with no edge anywhere, in a formation of their own: a glow, a horizon, a streak, an
|
|
4
|
+
* arc, a swirl. A fine film grain lies over it all, which also hides any banding in the blend.
|
|
5
|
+
*
|
|
6
|
+
* The colour is a few soft shapes, blurred together as one layer. That layer is drawn once
|
|
7
|
+
* and only ever moved and turned, so the costly blur is never redone as it reacts.
|
|
8
|
+
*
|
|
9
|
+
* A sky is its other type, as the animals are each a type of their own: smooth gradient from
|
|
10
|
+
* top to bottom, after the second reference's times of day and weathers. Both are a rounded tile.
|
|
11
|
+
*/
|
|
12
|
+
import { type Overrides, type Traits } from "../traits.ts";
|
|
13
|
+
export { SKIES } from "./skies.ts";
|
|
14
|
+
/** Each with a reference of its own: a tile of melted colour, or a circle of sky. */
|
|
15
|
+
export declare const TYPES: readonly ["Aura", "Sky"];
|
|
16
|
+
export type Type = (typeof TYPES)[number];
|
|
17
|
+
/**
|
|
18
|
+
* Each as deep, mid, light and accent. The first nine are the first reference's, and the rest
|
|
19
|
+
* after the later ones: Craft's greens on cream, Flowy's sunrise, a fern green with pale rings,
|
|
20
|
+
* and three from the sprayed posters, lime on forest, mint on green and pale blue on cobalt.
|
|
21
|
+
*/
|
|
22
|
+
export declare const PALETTES: {
|
|
23
|
+
label: string;
|
|
24
|
+
colors: string[];
|
|
25
|
+
}[];
|
|
26
|
+
/** Which of a palette's colours each part is, and each can be changed by hand. */
|
|
27
|
+
export declare const PARTS: string[];
|
|
28
|
+
type Colours = {
|
|
29
|
+
deep: string;
|
|
30
|
+
mid: string;
|
|
31
|
+
light: string;
|
|
32
|
+
accent: string;
|
|
33
|
+
};
|
|
34
|
+
/** A soft shape, before the blur: an oval, or a thick stroke along a path. */
|
|
35
|
+
type Blob = {
|
|
36
|
+
kind: "oval";
|
|
37
|
+
cx: number;
|
|
38
|
+
cy: number;
|
|
39
|
+
rx: number;
|
|
40
|
+
ry: number;
|
|
41
|
+
turn: number;
|
|
42
|
+
color: string;
|
|
43
|
+
opacity?: number;
|
|
44
|
+
} | {
|
|
45
|
+
kind: "stroke";
|
|
46
|
+
d: string;
|
|
47
|
+
width: number;
|
|
48
|
+
color: string;
|
|
49
|
+
opacity?: number;
|
|
50
|
+
};
|
|
51
|
+
/** Moves a point by up to `amount` either way, from the name. */
|
|
52
|
+
type Jitter = (key: string, amount: number) => number;
|
|
53
|
+
interface Flow {
|
|
54
|
+
name: string;
|
|
55
|
+
/** Which colour fills behind everything. */
|
|
56
|
+
base: (c: Colours) => string;
|
|
57
|
+
blobs: (c: Colours, j: Jitter) => Blob[];
|
|
58
|
+
/** How much of the usual blur it takes, for one whose shapes must still show through. */
|
|
59
|
+
softness?: number;
|
|
60
|
+
}
|
|
61
|
+
/** The formations, after the reference's tiles. Neighbours are as unlike as they can be. */
|
|
62
|
+
export declare const FLOWS: Flow[];
|
|
63
|
+
export declare const auraTraits: (name: string, overrides?: Overrides) => Traits;
|
|
64
|
+
/** A name's type. Its variants take turns, an aura then a sky, so a strip has both. */
|
|
65
|
+
export declare const typeOf: (name: string, overrides?: Overrides) => Type;
|
|
66
|
+
/** An aura's formation. A strip of variants never repeats one. */
|
|
67
|
+
export declare const flowOf: (name: string, overrides?: Overrides) => Flow;
|
|
68
|
+
/** The palettes a type offers: an aura's, or the skies, with their four colours to pick by hand. */
|
|
69
|
+
export declare const palettesOf: (type: Type) => {
|
|
70
|
+
label: string;
|
|
71
|
+
colors: string[];
|
|
72
|
+
stops?: string[];
|
|
73
|
+
}[];
|
|
74
|
+
/**
|
|
75
|
+
* How far the horizon is moved, from -1 (lower) to 1 (higher). It sits as in the reference
|
|
76
|
+
* until Possibilities is moved, and then strays from it by the name, further the higher it is.
|
|
77
|
+
* Set by hand, it's that.
|
|
78
|
+
*/
|
|
79
|
+
export declare function horizonOf(t: Traits): number;
|
|
80
|
+
/** Which of its type's palettes it has. Variants step on through them, each type's its own. */
|
|
81
|
+
export declare const paletteOf: (name: string, overrides: Overrides | undefined, type: Type) => number;
|
|
82
|
+
/** The palettes Possibilities offers a type, plainest first: all of them until it's moved. */
|
|
83
|
+
export declare function palettesOffered(t: Traits, type: Type): number[];
|
|
84
|
+
/** The palette's colours, each of which can be changed by hand. A sky's are its stops, top to bottom. */
|
|
85
|
+
export declare function coloursOf(name: string, overrides: Overrides | undefined, t: Traits, type: Type): Colours;
|
|
86
|
+
/** A plain face, which is off unless picked: the reference's tiles have none. */
|
|
87
|
+
export declare const FACES: readonly ["None", "Dots", "Calm", "Smile"];
|
|
88
|
+
export declare const faceOf: (t: Traits) => "Calm" | "Dots" | "None" | "Smile";
|
|
89
|
+
export interface Aura {
|
|
90
|
+
type: Type;
|
|
91
|
+
/**
|
|
92
|
+
* The colour layer, with its own ids from `id`: an aura's soft shapes, over a fill reaching
|
|
93
|
+
* well past the tile so its edges never fade, or a sky's gradient, held at its ends beyond it.
|
|
94
|
+
*/
|
|
95
|
+
colour: (id: string) => string;
|
|
96
|
+
/** How far the shapes are blurred together, and how heavy the grain over them is. */
|
|
97
|
+
blur: number;
|
|
98
|
+
grain: number;
|
|
99
|
+
/** The noise's seed, so each name's grain is its own. */
|
|
100
|
+
seed: number;
|
|
101
|
+
/** The formation's turn, in degrees. */
|
|
102
|
+
turn: number;
|
|
103
|
+
face: {
|
|
104
|
+
eyes: string;
|
|
105
|
+
mouth: string;
|
|
106
|
+
};
|
|
107
|
+
blinkDelay: number;
|
|
108
|
+
}
|
|
109
|
+
export declare function createAura(name: string, overrides?: Overrides): Aura;
|
|
110
|
+
/** The colour layer as drawn: blurred and turned for an aura, and as it is for a sky. */
|
|
111
|
+
export declare const colourLayer: (aura: Aura, id: string) => string;
|
|
112
|
+
/** How the face is drawn, over the colour. */
|
|
113
|
+
export declare const FACE_STYLE = "fill=\"#ffffff\" stroke=\"#ffffff\" stroke-width=\"2.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\" opacity=\"0.92\"";
|
|
114
|
+
/** The grain as a filter, at `frequency` cycles a unit: fine, grey, and wholly opaque, to be laid on in overlay. */
|
|
115
|
+
export declare const grainFilter: (id: string, frequency: number, seed: number) => string;
|
|
116
|
+
/** The colour layer's blur. Its region reaches well past the tile, so nothing is clipped before it's blurred. */
|
|
117
|
+
export declare const blurFilter: (id: string, blur: number) => string;
|
|
118
|
+
/** The tile at rest, for thumbnails. */
|
|
119
|
+
export declare function auraSvg(name: string, overrides?: Overrides): string;
|