mochikao 0.1.2 → 0.2.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.fr.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  [English](README.md) · **Français**
4
4
 
5
+ [Site](https://mochikao.alexvolkihar.ovh) · [npm](https://www.npmjs.com/package/mochikao) · [GitHub](https://github.com/Alexvolkihar/mochikao)
6
+
5
7
  Un visage pour chaque nom. `mochikao` transforme n'importe quelle chaîne (pseudo, e-mail, id) en un petit personnage SVG. Le même nom donne toujours le même mochikao.
6
8
 
7
9
  ![Page d'accueil de mochikao : un nom tapé, l'avatar généré et le choix d'expression](docs/home.png)
@@ -130,6 +132,28 @@ mochikao({ name: 'alex', family: produce, shape: 'lemon' });
130
132
 
131
133
  Choisir une famille ne change que la forme : yeux, bouche, accessoire et réglages restent ceux du nom. Là où les options arrivent en texte (attributs HTML, URL, CLI), on déclare les familles disponibles une fois : `defineMochikao({ families: [animals] })` puis `<mochikao-avatar family="animals">`, ou `parseParams(nom, params, [animals, produce])`. L'API et la CLI fournies acceptent les deux.
132
134
 
135
+ **Mélanger des familles.** `mix()` combine plusieurs familles : le nom choisit alors parmi toutes leurs formes, chacune avec la même chance.
136
+
137
+ ```ts
138
+ import { mochikao, blobs, mix } from 'mochikao';
139
+
140
+ mochikao({ name: 'alex', family: mix(blobs, animals) }); // blobs + animaux
141
+ mochikao({ name: 'alex', family: mix(animals, produce) }); // animaux + fruits & légumes
142
+ mochikao({ name: 'alex', family: mix(blobs, animals, produce) }); // tout
143
+ ```
144
+
145
+ | Mode | `family=` | Formes |
146
+ | --- | --- | --- |
147
+ | Blobs (défaut) | `blob` | 10 |
148
+ | Animaux | `animals` | 6 |
149
+ | Fruits & légumes | `produce` | 6 |
150
+ | Blobs + animaux | `animals+blob` | 16 |
151
+ | Blobs + fruits & légumes | `blob+produce` | 16 |
152
+ | Animaux + fruits & légumes | `animals+produce` | 12 |
153
+ | Tout | `all` (ou `animals+blob+produce`) | 22 |
154
+
155
+ Dans les URL, attributs HTML et la CLI, on joint les noms avec `+` (`?family=blob+animals`) ; `all` prend toutes les familles déclarées plus les blobs. L'ordre n'a pas d'importance, et un mélange des mêmes familles donne toujours le même avatar pour un nom.
156
+
133
157
  Une famille, c'est un objet `ShapeFamily` (`name`, `shapes`, `silhouette`, et en option `decorate` et `hue`) : `src/families/` montre comment en écrire une.
134
158
 
135
159
  ### Transition entre expressions
@@ -157,6 +181,8 @@ Mêmes attributs que les options ; `gaze` fait suivre le pointeur aux yeux.
157
181
  GET /api/<nom>.svg?size=&bg=&hue=&tone=&color=&expression=&animate=&family=&shape=&eyes=&mouth=&extra=
158
182
  ```
159
183
 
184
+ Instance hébergée : `https://mochikao.alexvolkihar.ovh/api/<nom>.svg`. À essayer dans l'[éditeur](https://mochikao.alexvolkihar.ovh/editor.html).
185
+
160
186
  Réponse `image/svg+xml` avec `Cache-Control: immutable` (même entrée → même sortie). Les paramètres invalides sont ignorés. Le handler (`api/handler.ts`) est une simple fonction `(req, res)` de `node:http`, à brancher où tu veux.
161
187
 
162
188
  ### CLI
package/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  **English** · [Français](README.fr.md)
4
4
 
5
+ [Website](https://mochikao.alexvolkihar.ovh) · [npm](https://www.npmjs.com/package/mochikao) · [GitHub](https://github.com/Alexvolkihar/mochikao)
6
+
5
7
  A face for every name. `mochikao` turns any string (username, email, ID) into a little SVG character. The same name always gives the same mochikao.
6
8
 
7
9
  ![mochikao home page: a typed name, the generated avatar and the expression picker](docs/home.png)
@@ -130,6 +132,28 @@ mochikao({ name: 'alex', family: produce, shape: 'lemon' });
130
132
 
131
133
  Picking a family only changes the shape: eyes, mouth, accessory and settings still come from the name. Where options arrive as text (HTML attributes, URLs, CLI), declare the available families once: `defineMochikao({ families: [animals] })` then `<mochikao-avatar family="animals">`, or `parseParams(name, params, [animals, produce])`. The bundled API and CLI accept both.
132
134
 
135
+ **Mixing families.** `mix()` combines several families: the name then picks among all their shapes, each shape equally likely.
136
+
137
+ ```ts
138
+ import { mochikao, blobs, mix } from 'mochikao';
139
+
140
+ mochikao({ name: 'alex', family: mix(blobs, animals) }); // blobs + animals
141
+ mochikao({ name: 'alex', family: mix(animals, produce) }); // animals + fruits & veggies
142
+ mochikao({ name: 'alex', family: mix(blobs, animals, produce) }); // everything
143
+ ```
144
+
145
+ | Mode | `family=` | Shapes |
146
+ | --- | --- | --- |
147
+ | Blobs (default) | `blob` | 10 |
148
+ | Animals | `animals` | 6 |
149
+ | Fruits & veggies | `produce` | 6 |
150
+ | Blobs + animals | `animals+blob` | 16 |
151
+ | Blobs + fruits & veggies | `blob+produce` | 16 |
152
+ | Animals + fruits & veggies | `animals+produce` | 12 |
153
+ | Everything | `all` (or `animals+blob+produce`) | 22 |
154
+
155
+ In URLs, HTML attributes and the CLI, join names with `+` (`?family=blob+animals`); `all` takes every family you declared plus the blobs. The order doesn't matter, and a mix with the same families always gives the same avatar for a given name.
156
+
133
157
  A family is a `ShapeFamily` object (`name`, `shapes`, `silhouette`, and optionally `decorate` and `hue`): `src/families/` shows how to write one.
134
158
 
135
159
  ### Transitions between expressions
@@ -157,6 +181,8 @@ Same attributes as the options; `gaze` makes the eyes follow the pointer.
157
181
  GET /api/<name>.svg?size=&bg=&hue=&tone=&color=&expression=&animate=&family=&shape=&eyes=&mouth=&extra=
158
182
  ```
159
183
 
184
+ Hosted instance: `https://mochikao.alexvolkihar.ovh/api/<name>.svg`. Try it in the [editor](https://mochikao.alexvolkihar.ovh/editor.html).
185
+
160
186
  Responds with `image/svg+xml` and `Cache-Control: immutable` (same input → same output). Invalid parameters are ignored. The handler (`api/handler.ts`) is a plain `node:http` `(req, res)` function you can mount anywhere.
161
187
 
162
188
  ### CLI
@@ -14,7 +14,7 @@ const USAGE = `usage : mochikao <nom> [options]
14
14
  --tone <0-1> luminosité
15
15
  --expression <e> idle | happy | sad | mad | love | shy | sick | thinking
16
16
  --animate <a> none | hover | always
17
- --family <f> blob | animals | produce
17
+ --family <f> blob | animals | produce | all, ou un mélange : blob+animals, animals+produce...
18
18
  --color <c> pastel | pale | mid | deep | bright | ink
19
19
  --shape --eyes --mouth --extra épingle un trait
20
20
  --info affiche les traits au lieu du SVG`;
@@ -49,3 +49,16 @@ export interface ShapeFamily<S extends string = string> {
49
49
  hue?(shape: S): number | undefined;
50
50
  }
51
51
  export declare function isFamily(value: unknown): value is ShapeFamily;
52
+ /**
53
+ * Combine plusieurs familles en une : le nom choisit parmi toutes leurs formes.
54
+ *
55
+ * mix(blobs, animals) // blobs et animaux
56
+ * mix(blobs, animals, produce) // tout
57
+ *
58
+ * Chaque forme a la même chance d'être tirée, donc une famille pèse selon son
59
+ * nombre de formes. L'ordre des arguments ne compte pas : les familles sont
60
+ * triées par nom (le résultat s'appelle `animals+blob`), ce qui fixe la liste
61
+ * des formes et donc quel avatar sort pour quel nom. Si deux familles ont une
62
+ * forme du même nom, la première (dans l'ordre trié) l'emporte.
63
+ */
64
+ export declare function mix(...families: ShapeFamily[]): ShapeFamily;
@@ -3,3 +3,36 @@ export function isFamily(value) {
3
3
  return typeof f === 'object' && f !== null && typeof f.name === 'string' &&
4
4
  Array.isArray(f.shapes) && f.shapes.length > 0 && typeof f.silhouette === 'function';
5
5
  }
6
+ /**
7
+ * Combine plusieurs familles en une : le nom choisit parmi toutes leurs formes.
8
+ *
9
+ * mix(blobs, animals) // blobs et animaux
10
+ * mix(blobs, animals, produce) // tout
11
+ *
12
+ * Chaque forme a la même chance d'être tirée, donc une famille pèse selon son
13
+ * nombre de formes. L'ordre des arguments ne compte pas : les familles sont
14
+ * triées par nom (le résultat s'appelle `animals+blob`), ce qui fixe la liste
15
+ * des formes et donc quel avatar sort pour quel nom. Si deux familles ont une
16
+ * forme du même nom, la première (dans l'ordre trié) l'emporte.
17
+ */
18
+ export function mix(...families) {
19
+ const unique = [...new Map(families.filter(isFamily).map((f) => [f.name, f])).values()]
20
+ .sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
21
+ if (unique.length === 0)
22
+ throw new TypeError('mix() attend au moins une famille');
23
+ if (unique.length === 1)
24
+ return unique[0];
25
+ const owner = new Map();
26
+ for (const f of unique)
27
+ for (const s of f.shapes)
28
+ if (!owner.has(s))
29
+ owner.set(s, f);
30
+ const of = (shape) => owner.get(shape) ?? unique[0];
31
+ return {
32
+ name: unique.map((f) => f.name).join('+'),
33
+ shapes: [...owner.keys()],
34
+ silhouette: (shape, traits) => of(shape).silhouette(shape, traits),
35
+ decorate: (shape, body, colors, eyeY) => of(shape).decorate?.(shape, body, colors, eyeY) ?? {},
36
+ hue: (shape) => of(shape).hue?.(shape),
37
+ };
38
+ }
@@ -3,7 +3,7 @@ import { type ShapeFamily } from './family.ts';
3
3
  import { type Animation, type Background, type ColorPreset, type Expression, type ExtraName, type EyesName, type MouthName, type Traits } from './traits.ts';
4
4
  export * from './traits.ts';
5
5
  export { blobs } from './shape.ts';
6
- export { isFamily } from './family.ts';
6
+ export { isFamily, mix } from './family.ts';
7
7
  export type { Decoration, ShapeDef, ShapeFamily } from './family.ts';
8
8
  export { palette, contrast, hslToRgb } from './color.ts';
9
9
  export type { Palette } from './color.ts';
package/dist/src/index.js CHANGED
@@ -6,7 +6,7 @@ import { blobs, buildBody, superellipsePath } from "./shape.js";
6
6
  import { ANIMATIONS, BACKGROUNDS, COLORS, EXPRESSIONS, EXTRAS, EYES, MOUTHS, deriveTraits, pick, positionOf, } from "./traits.js";
7
7
  export * from "./traits.js";
8
8
  export { blobs } from "./shape.js";
9
- export { isFamily } from "./family.js";
9
+ export { isFamily, mix } from "./family.js";
10
10
  export { palette, contrast, hslToRgb } from "./color.js";
11
11
  /** Ce que le nom donne, sans dessiner. */
12
12
  export function resolve(options) {
@@ -1,5 +1,5 @@
1
1
  import type { MochikaoOptions } from './index.ts';
2
- import type { ShapeFamily } from './family.ts';
2
+ import { type ShapeFamily } from './family.ts';
3
3
  type Source = {
4
4
  get(key: string): string | null | undefined;
5
5
  };
@@ -8,6 +8,8 @@ type Source = {
8
8
  * Les valeurs invalides sont ignorées plutôt que de faire échouer le rendu.
9
9
  * `families` liste les familles qu'on accepte de désigner par leur nom
10
10
  * (`?family=animals`) ; sans elles, seuls les blobs sont disponibles.
11
+ * Plusieurs familles se combinent avec « + » (`?family=blob+animals`), et
12
+ * `family=all` prend toutes celles qu'on fournit.
11
13
  */
12
14
  export declare function parseParams(name: string, src: Source, families?: readonly ShapeFamily[]): MochikaoOptions;
13
15
  /**
@@ -1,3 +1,4 @@
1
+ import { mix } from "./family.js";
1
2
  import { blobs } from "./shape.js";
2
3
  import { ANIMATIONS, BACKGROUNDS, COLORS, CONTINUOUS_AXES, EXPRESSIONS, EXTRAS, EYES, MOUTHS } from "./traits.js";
3
4
  const oneOf = (list, v) => v != null && list.includes(v) ? v : undefined;
@@ -7,11 +8,27 @@ const num = (v, min, max) => {
7
8
  const n = Number(v);
8
9
  return Number.isFinite(n) ? Math.min(max, Math.max(min, n)) : undefined;
9
10
  };
11
+ /**
12
+ * `animals`, `blob`, ou un mode combiné : `blob+animals`, `animals+produce`...
13
+ * `all` prend les blobs et toutes les familles fournies. Les noms inconnus
14
+ * sont ignorés ; sans aucun nom valide, on n'a pas de famille.
15
+ */
16
+ function findFamily(value, families) {
17
+ if (!value)
18
+ return undefined;
19
+ const known = [blobs, ...families];
20
+ if (value === 'all')
21
+ return mix(...known);
22
+ const found = value.split(/[+ ]/).map((n) => known.find((f) => f.name === n)).filter((f) => f !== undefined);
23
+ return found.length ? mix(...found) : undefined;
24
+ }
10
25
  /**
11
26
  * Options depuis des paramètres texte (query string, attributs HTML, flags CLI).
12
27
  * Les valeurs invalides sont ignorées plutôt que de faire échouer le rendu.
13
28
  * `families` liste les familles qu'on accepte de désigner par leur nom
14
29
  * (`?family=animals`) ; sans elles, seuls les blobs sont disponibles.
30
+ * Plusieurs familles se combinent avec « + » (`?family=blob+animals`), et
31
+ * `family=all` prend toutes celles qu'on fournit.
15
32
  */
16
33
  export function parseParams(name, src, families = []) {
17
34
  const options = { name };
@@ -26,7 +43,7 @@ export function parseParams(name, src, families = []) {
26
43
  set('expression', oneOf(EXPRESSIONS, src.get('expression')));
27
44
  set('animate', oneOf(ANIMATIONS, src.get('animate')));
28
45
  // `family` désigne une famille par son nom, parmi celles qu'on nous a confiées.
29
- const family = families.find((fam) => fam.name === src.get('family'));
46
+ const family = findFamily(src.get('family'), families);
30
47
  set('family', family);
31
48
  set('shape', oneOf((family ?? blobs).shapes, src.get('shape')));
32
49
  set('eyes', oneOf(EYES, src.get('eyes')));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mochikao",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "description": "Deterministic avatars: any string becomes a cute, unique SVG character. Zero dependencies, React / Vue / Svelte / Solid / web component.",
5
5
  "keywords": [
6
6
  "avatar",
@@ -22,7 +22,7 @@
22
22
  "type": "git",
23
23
  "url": "git+https://github.com/Alexvolkihar/mochikao.git"
24
24
  },
25
- "homepage": "https://github.com/Alexvolkihar/mochikao#readme",
25
+ "homepage": "https://mochikao.alexvolkihar.ovh",
26
26
  "bugs": {
27
27
  "url": "https://github.com/Alexvolkihar/mochikao/issues"
28
28
  },