odoro 1.0.9 → 2.0.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.
Files changed (85) hide show
  1. package/README.md +94 -0
  2. package/client.d.ts +90 -16
  3. package/dist/build-LZAUDJST.js +5 -0
  4. package/dist/{chunk-34RGOZFA.js → chunk-4D56Z7G3.js} +49 -50
  5. package/dist/{chunk-JMEHF3KN.js → chunk-7ZB5MAM6.js} +4 -4
  6. package/dist/{chunk-LHZGX5ML.js → chunk-OMHEQVIW.js} +295 -160
  7. package/dist/{chunk-3SZIN6VG.js → chunk-P3DJITWE.js} +10 -10
  8. package/dist/{chunk-ZVL7EXJO.js → chunk-PRWVZ2SM.js} +22 -14
  9. package/dist/chunk-Q2KCMTB5.js +741 -0
  10. package/dist/chunk-QOVBLN7A.js +241 -0
  11. package/dist/chunk-WWTDEV4Y.js +604 -0
  12. package/dist/cli.d.ts +12 -13
  13. package/dist/cli.js +84 -63
  14. package/dist/{commands-4JRBD55Z.js → commands-EUGVLQWL.js} +44 -45
  15. package/dist/{commands-AHMXBWQQ.js → commands-GYA7NTCX.js} +124 -124
  16. package/dist/{create-CYVSVDAD.js → create-QX5LEWMS.js} +158 -154
  17. package/dist/index.d.ts +603 -151
  18. package/dist/index.js +6 -6
  19. package/dist/{package-EIDLKAA6.js → package-T4Z7OBOH.js} +5 -5
  20. package/dist/preview-P4WY5GEY.js +4 -0
  21. package/dist/registry/index.d.ts +75 -76
  22. package/dist/registry/index.js +1 -1
  23. package/dist/{server-4UGN3SFS.js → server-2WP562EV.js} +3 -3
  24. package/package.json +5 -5
  25. package/templates/react-ts/README.md +71 -35
  26. package/templates/react-ts/_env.example +9 -0
  27. package/templates/react-ts/_variants/with-engine/src/background.tsx +118 -0
  28. package/templates/react-ts/_variants/without-libs/src/App.tsx +380 -0
  29. package/templates/react-ts/_variants/without-libs/src/background.tsx +24 -0
  30. package/templates/react-ts/_variants/without-libs/src/entry-server.tsx +39 -0
  31. package/templates/react-ts/_variants/without-libs/src/main.tsx +26 -0
  32. package/templates/{react-ts-server/_variantes/sans-libs/client → react-ts/_variants/without-libs}/src/styles.css +137 -137
  33. package/templates/react-ts/{_variantes/sans-routeur → _variants/without-router}/src/App.tsx +141 -141
  34. package/templates/react-ts/_variants/without-router/src/entry-server.tsx +39 -0
  35. package/templates/react-ts/index.html +4 -4
  36. package/templates/react-ts/odoro.config.ts +5 -0
  37. package/templates/react-ts/src/App.tsx +219 -209
  38. package/templates/react-ts/src/background.tsx +66 -0
  39. package/templates/react-ts/src/entry-server.tsx +85 -0
  40. package/templates/react-ts/src/main.tsx +13 -4
  41. package/templates/react-ts/src/router.tsx +65 -45
  42. package/templates/react-ts/src/styles.css +5 -5
  43. package/templates/react-ts-server/Dockerfile +9 -9
  44. package/templates/react-ts-server/README.md +97 -45
  45. package/templates/react-ts-server/_env.example +31 -33
  46. package/templates/react-ts-server/_variants/with-engine/client/src/background.tsx +118 -0
  47. package/templates/react-ts-server/_variants/without-libs/client/src/App.tsx +380 -0
  48. package/templates/react-ts-server/_variants/without-libs/client/src/background.tsx +24 -0
  49. package/templates/react-ts-server/_variants/without-libs/client/src/entry-server.tsx +39 -0
  50. package/templates/react-ts-server/_variants/without-libs/client/src/main.tsx +26 -0
  51. package/templates/{react-ts/_variantes/sans-libs → react-ts-server/_variants/without-libs/client}/src/styles.css +137 -137
  52. package/templates/react-ts-server/{_variantes/sans-routeur → _variants/without-router}/client/src/App.tsx +141 -141
  53. package/templates/react-ts-server/_variants/without-router/client/src/entry-server.tsx +39 -0
  54. package/templates/react-ts-server/client/index.html +4 -4
  55. package/templates/react-ts-server/client/src/App.tsx +228 -210
  56. package/templates/react-ts-server/client/src/account.tsx +267 -0
  57. package/templates/react-ts-server/client/src/auth.tsx +139 -0
  58. package/templates/react-ts-server/client/src/background.tsx +66 -0
  59. package/templates/react-ts-server/client/src/entry-server.tsx +92 -0
  60. package/templates/react-ts-server/client/src/main.tsx +17 -5
  61. package/templates/react-ts-server/client/src/router.tsx +77 -45
  62. package/templates/react-ts-server/client/src/styles.css +5 -5
  63. package/templates/react-ts-server/odoro.config.ts +7 -4
  64. package/templates/react-ts-server/package.json +2 -0
  65. package/templates/react-ts-server/scripts/dev.mjs +14 -14
  66. package/templates/react-ts-server/server/src/main.ts +119 -45
  67. package/templates/react-ts-server/server/src/modules/auth/index.ts +238 -0
  68. package/templates/react-ts-server/server/src/modules/auth/password.ts +122 -0
  69. package/templates/react-ts-server/server/src/modules/auth/store.ts +193 -0
  70. package/templates/react-ts-server/server/src/modules/health/index.ts +45 -46
  71. package/dist/build-JFQHODAT.js +0 -5
  72. package/dist/chunk-22KJTV2R.js +0 -380
  73. package/dist/chunk-DL3NPC4H.js +0 -113
  74. package/dist/chunk-TQUJ3MFS.js +0 -268
  75. package/dist/preview-7DKEAQOQ.js +0 -4
  76. package/templates/react-ts/_variantes/avec-moteur/src/fond.tsx +0 -118
  77. package/templates/react-ts/_variantes/sans-libs/src/App.tsx +0 -382
  78. package/templates/react-ts/_variantes/sans-libs/src/fond.tsx +0 -23
  79. package/templates/react-ts/_variantes/sans-libs/src/main.tsx +0 -17
  80. package/templates/react-ts/src/fond.tsx +0 -66
  81. package/templates/react-ts-server/_variantes/avec-moteur/client/src/fond.tsx +0 -118
  82. package/templates/react-ts-server/_variantes/sans-libs/client/src/App.tsx +0 -382
  83. package/templates/react-ts-server/_variantes/sans-libs/client/src/fond.tsx +0 -23
  84. package/templates/react-ts-server/_variantes/sans-libs/client/src/main.tsx +0 -17
  85. package/templates/react-ts-server/client/src/fond.tsx +0 -66
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
- export { startPreviewServer } from './chunk-ZVL7EXJO.js';
3
- export { defineConfig, loadConfig } from './chunk-DL3NPC4H.js';
2
+ export { startPreviewServer } from './chunk-PRWVZ2SM.js';
3
+ export { clientEnv, defineConfig, loadConfig, loadEnv } from './chunk-QOVBLN7A.js';
4
4
  import './chunk-T6RLHSCW.js';
5
- export { buildProject, reportBuild } from './chunk-22KJTV2R.js';
6
- export { ModuleGraph, detectSelfAccepting, optimizeDeps, scanDependencies, startDevServer } from './chunk-LHZGX5ML.js';
7
- export { depFileName } from './chunk-TQUJ3MFS.js';
8
- import './chunk-JMEHF3KN.js';
5
+ export { buildManifest, buildProject, chunksFor, prerender, reportBuild } from './chunk-Q2KCMTB5.js';
6
+ export { ModuleGraph, detectSelfAccepting, optimizeDeps, scanDependencies, startDevServer } from './chunk-OMHEQVIW.js';
7
+ export { depFileName, hasGlob, transformGlob } from './chunk-WWTDEV4Y.js';
8
+ import './chunk-7ZB5MAM6.js';
@@ -2,11 +2,11 @@
2
2
  // package.json
3
3
  var package_default = {
4
4
  name: "odoro",
5
- version: "1.0.9",
5
+ version: "2.0.0",
6
6
  type: "module",
7
7
  license: "UNLICENSED",
8
8
  author: "BouBouw",
9
- description: "Moteur de developpement et echafaudage Odoro : odoro create, dev, build, preview.",
9
+ description: "Odoro development engine and scaffolder: odoro create, dev, build, preview.",
10
10
  engines: {
11
11
  node: ">=20"
12
12
  },
@@ -37,12 +37,12 @@ var package_default = {
37
37
  }
38
38
  },
39
39
  scripts: {
40
- build: "node scripts/versions-famille.mjs && tsup",
40
+ build: "node scripts/family-versions.mjs && tsup",
41
41
  dev: "tsup --watch",
42
42
  test: "vitest run",
43
- typecheck: "node scripts/versions-famille.mjs && tsc --noEmit",
43
+ typecheck: "node scripts/family-versions.mjs && tsc --noEmit",
44
44
  prepublishOnly: "pnpm run build",
45
- "versions:famille": "node scripts/versions-famille.mjs"
45
+ "versions:family": "node scripts/family-versions.mjs"
46
46
  },
47
47
  dependencies: {
48
48
  "@babel/core": "^7.29.7",
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ export { startPreviewServer } from './chunk-PRWVZ2SM.js';
3
+ import './chunk-WWTDEV4Y.js';
4
+ import './chunk-7ZB5MAM6.js';
@@ -1,46 +1,45 @@
1
1
  import * as z from 'zod/mini';
2
2
 
3
3
  /**
4
- * Format d'une entree de registre.
4
+ * Format of a registry entry.
5
5
  *
6
- * ## Pourquoi le schema vit ici
6
+ * ## Why the schema lives here
7
7
  *
8
- * Le format est un **contrat** entre le registre qui publie et le client qui
9
- * telecharge. Le placer du cote client n'est pas arbitraire : c'est la ou la
10
- * validation compte le plus. Le registre valide ce qu'il produit avant de le
11
- * publier ; le client, lui, valide ce qu'il recoit d'un serveur qu'il ne
12
- * controle pas, juste avant d'ecrire des fichiers dans le projet de
13
- * l'utilisateur.
8
+ * The format is a **contract** between the registry that publishes and the
9
+ * client that downloads. Putting it on the client side is not arbitrary: that
10
+ * is where the validation matters most. The registry validates what it produces
11
+ * before publishing it; the client validates what it receives from a server it
12
+ * does not control, just before writing files into the user project.
14
13
  *
15
- * Une seule definition, donc, employee aux deux bouts.
14
+ * A single definition, therefore, used at both ends.
16
15
  *
17
- * ## Le poids de la validation
16
+ * ## The weight of the validation
18
17
  *
19
- * Deux decisions, mesurees plutot que supposees.
18
+ * Two decisions, measured rather than assumed.
20
19
  *
21
- * La bibliotheque est **integree a la compilation** plutot que declaree en
22
- * dependance : installee, elle pese pres de six megaoctets, alors que la
23
- * surface reellement employee en represente une fraction.
20
+ * The library is **inlined at build time** rather than declared as a
21
+ * dependency: installed, it weighs close to six megabytes, while the surface
22
+ * actually used is a fraction of that.
24
23
  *
25
- * Et c'est sa variante concue pour le decoupage qui est employee, non son API
26
- * usuelle : la premiere produit treize kilo-octets minifies, la seconde quatre
27
- * cent vingt-sept. Un facteur trente-trois pour la meme validation. L'ecriture
28
- * y est plus verbeuse — les controles sont des fonctions plutot que des
29
- * methodes chainees — mais plaider le poids pour le moteur graphique et
30
- * l'ignorer ici serait incoherent.
24
+ * And it is its variant designed for splitting that is used, not its usual API:
25
+ * the first produces thirteen minified kilobytes, the second four hundred and
26
+ * twenty-seven. A factor of thirty-three for the same validation. Writing it is
27
+ * more verbose there — the checks are functions rather than chained methods —
28
+ * but pleading weight for the graphics engine and ignoring it here would be
29
+ * inconsistent.
31
30
  *
32
31
  * @module
33
32
  */
34
33
 
35
- /** Categories de composants du registre. */
34
+ /** Component categories of the registry. */
36
35
  declare const CATEGORIES: readonly ["text", "background", "effect", "hero", "image", "ui", "section", "loader", "hooks"];
37
- /** Niveaux de cout d'un composant. */
36
+ /** Cost levels of a component. */
38
37
  declare const PERF_TIERS: readonly ["light", "medium", "heavy"];
39
- /** Backends graphiques declarables. */
38
+ /** Declarable graphics backends. */
40
39
  declare const GL_BACKENDS: readonly ["ogl", "three"];
41
- /** Nature du repli visuel d'un composant. */
40
+ /** Nature of the visual fallback of a component. */
42
41
  declare const FALLBACKS: readonly ["poster", "gradient", "static", "none"];
43
- /** Forme d'une entree, avant les regles qui croisent plusieurs champs. */
42
+ /** Shape of an entry, before the rules that cross several fields. */
44
43
  declare const baseSchema: z.ZodMiniObject<{
45
44
  name: z.ZodMiniString<string>;
46
45
  category: z.ZodMiniEnum<{
@@ -101,16 +100,16 @@ declare const baseSchema: z.ZodMiniObject<{
101
100
  }>>;
102
101
  }, z.core.$strip>;
103
102
  }, z.core.$strip>;
104
- /** Une entree telle qu'elle sort de la validation. */
103
+ /** An entry as it comes out of the validation. */
105
104
  type RegistryMeta = z.infer<typeof baseSchema>;
106
- /** Entree telle qu'elle est ecrite dans un `meta.json`, avant valeurs par defaut. */
105
+ /** Entry as it is written in a `meta.json`, before default values. */
107
106
  type RegistryMetaInput = z.input<typeof baseSchema>;
108
107
  /**
109
- * Description complete d'une entree de registre.
108
+ * Complete description of a registry entry.
110
109
  *
111
- * Les regles qui croisent plusieurs champs sont verifiees ici plutot que dans
112
- * le script de validation : elles font partie du format, et un registre tiers
113
- * qui reutiliserait ce schema doit les subir aussi.
110
+ * The rules that cross several fields are checked here rather than in the
111
+ * validation script: they are part of the format, and a third-party registry
112
+ * reusing this schema must be subject to them too.
114
113
  */
115
114
  declare const metaSchema: z.ZodMiniObject<{
116
115
  name: z.ZodMiniString<string>;
@@ -172,16 +171,16 @@ declare const metaSchema: z.ZodMiniObject<{
172
171
  }>>;
173
172
  }, z.core.$strip>;
174
173
  }, z.core.$strip>;
175
- /** Identifiant complet d'une entree, sous la forme `categorie/nom`. */
174
+ /** Full identifier of an entry, in the form `category/name`. */
176
175
  declare function entryId(meta: Pick<RegistryMeta, 'category' | 'name'>): string;
177
- /** Une entree publiee, code source inline. */
176
+ /** A published entry, source code inlined. */
178
177
  interface PublishedEntry extends RegistryMeta {
179
- /** Identifiant complet. */
178
+ /** Full identifier. */
180
179
  readonly id: string;
181
- /** Contenu des fichiers, indexe par leur chemin dans le composant. */
180
+ /** Content of the files, indexed by their path inside the component. */
182
181
  readonly sources: Readonly<Record<string, string>>;
183
182
  }
184
- /** Resume d'une entree, tel qu'il figure dans l'index. */
183
+ /** Summary of an entry, as it appears in the index. */
185
184
  interface IndexEntry {
186
185
  readonly id: string;
187
186
  readonly name: string;
@@ -192,21 +191,21 @@ interface IndexEntry {
192
191
  readonly backend: false | (typeof GL_BACKENDS)[number];
193
192
  readonly registryDependencies: readonly string[];
194
193
  }
195
- /** Index du registre, servi a la racine. */
194
+ /** Index of the registry, served at the root. */
196
195
  interface RegistryIndex {
197
- /** Version du format, pour que le client sache s'il sait lire. */
196
+ /** Version of the format, so that the client knows whether it can read it. */
198
197
  readonly version: 1;
199
- /** Date de generation, en ISO 8601. */
198
+ /** Generation date, in ISO 8601. */
200
199
  readonly generatedAt: string;
201
- /** Resume de chaque entree, sans le code source. */
200
+ /** Summary of each entry, without the source code. */
202
201
  readonly entries: readonly IndexEntry[];
203
202
  }
204
203
  /**
205
- * Valide une entree et rend des messages lisibles en cas d'echec.
204
+ * Validates an entry and returns readable messages on failure.
206
205
  *
207
- * Les messages bruts sont exacts mais arides : ils sont reformates en chemin
208
- * plus explication, pour qu'un auteur de composant sache quoi corriger sans
209
- * avoir a lire le schema.
206
+ * The raw messages are exact but dry: they are reformatted as path plus
207
+ * explanation, so that a component author knows what to fix without having to
208
+ * read the schema.
210
209
  *
211
210
  * @example
212
211
  * const result = parseMeta(JSON.parse(raw), 'text/split-reveal')
@@ -221,53 +220,53 @@ declare function parseMeta(value: unknown, origin: string): {
221
220
  };
222
221
 
223
222
  /**
224
- * Resolution du graphe de dependances du registre.
223
+ * Resolution of the dependency graph of the registry.
225
224
  *
226
- * Une entree peut en reclamer d'autres — un hook partage, un utilitaire. Il
227
- * faut donc, avant d'ecrire quoi que ce soit chez l'utilisateur, savoir
228
- * exactement quelles entrees installer et dans quel ordre.
225
+ * An entry may require others — a shared hook, a utility. Before writing
226
+ * anything into the user project, we therefore have to know exactly which
227
+ * entries to install and in what order.
229
228
  *
230
- * ## Les cycles
229
+ * ## The cycles
231
230
  *
232
- * Rien n'interdit structurellement a deux entrees de se reclamer l'une
233
- * l'autre, et un parcours naif s'y perdrait indefiniment. Le cycle est donc
234
- * detecte, et **signale avec le chemin qui le compose** : une erreur qui dit
235
- * seulement « cycle detecte » oblige a le chercher a la main.
231
+ * Nothing structurally forbids two entries from requiring each other, and a
232
+ * naive walk would get lost in them forever. The cycle is therefore detected,
233
+ * and **reported with the path that makes it up**: an error that says only
234
+ * "cycle detected" forces you to look for it by hand.
236
235
  *
237
- * ## L'ordre
236
+ * ## The order
238
237
  *
239
- * Les dependances sont installees avant ce qui les reclame. Cela n'a pas
240
- * d'importance pour l'ecriture de fichiers, qui est independante, mais cela en
241
- * a pour ce que l'utilisateur voit defiler : une liste ou les dependances
242
- * apparaissent apres leur consommateur se lit comme une erreur.
238
+ * The dependencies are installed before what requires them. That does not
239
+ * matter for writing files, which is independent, but it does matter for what
240
+ * the user sees scrolling by: a list where the dependencies appear after their
241
+ * consumer reads like an error.
243
242
  *
244
243
  * @module
245
244
  */
246
245
 
247
- /** Ce qu'il faut savoir d'une entree pour resoudre le graphe. */
246
+ /** What has to be known about an entry to resolve the graph. */
248
247
  interface ResolvableEntry {
249
- /** Identifiant complet, sous la forme `categorie/nom`. */
248
+ /** Full identifier, in the form `category/name`. */
250
249
  readonly id: string;
251
- /** Entrees reclamees par celle-ci. */
250
+ /** Entries required by this one. */
252
251
  readonly registryDependencies: readonly string[];
253
252
  }
254
- /** Resultat d'une resolution reussie. */
253
+ /** Result of a successful resolution. */
255
254
  interface ResolvedGraph {
256
- /** Entrees a installer, dependances d'abord. */
255
+ /** Entries to install, dependencies first. */
257
256
  readonly order: readonly string[];
258
- /** Entrees ajoutees qui n'avaient pas ete demandees. */
257
+ /** Entries added that had not been asked for. */
259
258
  readonly implied: readonly string[];
260
259
  }
261
- /** Ce qui a empeche la resolution. */
260
+ /** What prevented the resolution. */
262
261
  type ResolutionProblem = {
263
- readonly kind: 'introuvable';
262
+ readonly kind: 'missing';
264
263
  readonly id: string;
265
264
  readonly requiredBy: string | null;
266
265
  } | {
267
266
  readonly kind: 'cycle';
268
267
  readonly path: readonly string[];
269
268
  };
270
- /** Resultat d'une resolution. */
269
+ /** Result of a resolution. */
271
270
  type ResolutionResult = {
272
271
  readonly ok: true;
273
272
  readonly graph: ResolvedGraph;
@@ -276,18 +275,18 @@ type ResolutionResult = {
276
275
  readonly problems: readonly ResolutionProblem[];
277
276
  };
278
277
  /**
279
- * Met un probleme de resolution en phrase lisible.
278
+ * Turns a resolution problem into a readable sentence.
280
279
  *
281
280
  * @example
282
281
  * describeProblem({ kind: 'cycle', path: ['a', 'b', 'a'] })
283
- * // 'Cycle de dependances : a → b → a'
282
+ * // 'Dependency cycle: a → b → a'
284
283
  */
285
284
  declare function describeProblem(problem: ResolutionProblem): string;
286
285
  /**
287
- * Resout les dependances d'un ensemble d'entrees demandees.
286
+ * Resolves the dependencies of a set of requested entries.
288
287
  *
289
- * @param requested Identifiants demandes par l'utilisateur.
290
- * @param available Toutes les entrees connues, indexees par identifiant.
288
+ * @param requested Identifiers requested by the user.
289
+ * @param available Every known entry, indexed by identifier.
291
290
  *
292
291
  * @example
293
292
  * const result = resolveGraph(['hero/canopy'], catalogue)
@@ -295,10 +294,10 @@ declare function describeProblem(problem: ResolutionProblem): string;
295
294
  */
296
295
  declare function resolveGraph(requested: readonly string[], available: ReadonlyMap<string, ResolvableEntry>): ResolutionResult;
297
296
  /**
298
- * Verifie l'integrite d'un catalogue entier.
297
+ * Checks the integrity of a whole catalogue.
299
298
  *
300
- * Emploi typique : la validation du registre avant publication, ou toutes les
301
- * entrees sont resolues d'un coup plutot qu'une par une.
299
+ * Typical use: the validation of the registry before publication, where every
300
+ * entry is resolved at once rather than one by one.
302
301
  *
303
302
  * @example
304
303
  * const problems = validateCatalogue(catalogue)
@@ -306,7 +305,7 @@ declare function resolveGraph(requested: readonly string[], available: ReadonlyM
306
305
  */
307
306
  declare function validateCatalogue(available: ReadonlyMap<string, ResolvableEntry>): readonly ResolutionProblem[];
308
307
  /**
309
- * Construit un catalogue a partir d'entrees completes.
308
+ * Builds a catalogue from complete entries.
310
309
  *
311
310
  * @example
312
311
  * const catalogue = toCatalogue(metas)
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- export { CATEGORIES, FALLBACKS, GL_BACKENDS, PERF_TIERS, describeProblem, entryId, metaSchema, parseMeta, resolveGraph, toCatalogue, validateCatalogue } from '../chunk-34RGOZFA.js';
2
+ export { CATEGORIES, FALLBACKS, GL_BACKENDS, PERF_TIERS, describeProblem, entryId, metaSchema, parseMeta, resolveGraph, toCatalogue, validateCatalogue } from '../chunk-4D56Z7G3.js';
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
- export { extractEntries, injectClient, startDevServer } from './chunk-LHZGX5ML.js';
3
- import './chunk-TQUJ3MFS.js';
4
- import './chunk-JMEHF3KN.js';
2
+ export { extractEntries, injectClient, startDevServer } from './chunk-OMHEQVIW.js';
3
+ import './chunk-WWTDEV4Y.js';
4
+ import './chunk-7ZB5MAM6.js';
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "odoro",
3
- "version": "1.0.9",
3
+ "version": "2.0.0",
4
4
  "type": "module",
5
5
  "license": "UNLICENSED",
6
6
  "author": "BouBouw",
7
- "description": "Moteur de developpement et echafaudage Odoro : odoro create, dev, build, preview.",
7
+ "description": "Odoro development engine and scaffolder: odoro create, dev, build, preview.",
8
8
  "engines": {
9
9
  "node": ">=20"
10
10
  },
@@ -59,10 +59,10 @@
59
59
  "url": "https://github.com/ODORO-CLI/OdoroKit/issues"
60
60
  },
61
61
  "scripts": {
62
- "build": "node scripts/versions-famille.mjs && tsup",
62
+ "build": "node scripts/family-versions.mjs && tsup",
63
63
  "dev": "tsup --watch",
64
64
  "test": "vitest run",
65
- "typecheck": "node scripts/versions-famille.mjs && tsc --noEmit",
66
- "versions:famille": "node scripts/versions-famille.mjs"
65
+ "typecheck": "node scripts/family-versions.mjs && tsc --noEmit",
66
+ "versions:family": "node scripts/family-versions.mjs"
67
67
  }
68
68
  }
@@ -1,52 +1,88 @@
1
- # Odoro — application monopage
1
+ # Odoro — single-page application
2
2
 
3
- Projet genere par `odoro create`. Le routeur, le moteur d'animation, le systeme
4
- de style et les composants d'interface viennent tous de `@odoro-cli/libs` ; le
5
- serveur de developpement et la compilation viennent du moteur `odoro`.
3
+ Project generated by `odoro create`. The router, the motion engine, the style
4
+ system and the interface components all come from `@odoro-cli/libs`; the
5
+ development server and the build come from the `odoro` engine.
6
6
 
7
7
  ## Structure
8
8
 
9
9
  ```
10
- index.html Document et point d'entree : la balise <script type="module">
11
- designe le fichier a compiler.
12
- odoro.config.ts Configuration du moteur : alias, port, proxy, build.
13
- public/ Fichiers copies tels quels a la racine du site.
10
+ index.html Document and entry point: the <script type="module"> tag
11
+ names the file to build.
12
+ odoro.config.ts Engine configuration: aliases, port, proxy, build.
13
+ .env.example The expected variables. Copy to `.env`, never committed.
14
+ public/ Files copied as-is to the root of the site.
14
15
  src/
15
- main.tsx Montage de React et imports de feuilles de style.
16
- App.tsx Declaration des routes et enveloppe commune.
17
- routes/ Une page par route.
18
- styles.css Styles propres au projet et surcharges de tokens.
19
- odoro-env.d.ts Types ambiants (import.meta.env, imports de ressources).
16
+ main.tsx React mounting and stylesheet imports.
17
+ App.tsx The whole page: nav, sections, footer.
18
+ router.tsx The route table — the only file that names the router.
19
+ entry-server.tsx Prerendering: which routes, and what they put in the head.
20
+ background.tsx The decorative background — the only piece tied to the
21
+ graphics engine.
22
+ styles.css Project styles and token overrides.
23
+ odoro-env.d.ts Ambient types (import.meta.env, asset imports).
20
24
  ```
21
25
 
22
26
  ## Scripts
23
27
 
24
- | Commande | Effet |
25
- | ----------- | --------------------------------------------------- |
26
- | `dev` | Serveur de developpement avec rechargement a chaud. |
27
- | `build` | Compilation de production dans `dist/`. |
28
- | `preview` | Sert `dist/` comme le ferait un hebergeur statique. |
29
- | `typecheck` | Verification des types, sans emission. |
28
+ | Command | Effect |
29
+ | ----------- | ----------------------------------------------- |
30
+ | `dev` | Development server with hot reloading. |
31
+ | `build` | Production build into `dist/`. |
32
+ | `preview` | Serves `dist/` the way a static host would. |
33
+ | `typecheck` | Type checking, without emitting. |
30
34
 
31
- ## Personnalisation
35
+ ## Environment variables
32
36
 
33
- Toutes les valeurs visuelles passent par des variables CSS. Surcharger
34
- `--o-palette-brand-600` dans `src/styles.css` retheme l'application **et** les
35
- composants de la librairie, sans toucher a leur code.
37
+ Copy `.env.example` to `.env`. The files are read from the least specific to
38
+ the most specific — `.env`, `.env.local`, `.env.<mode>`, `.env.<mode>.local` —
39
+ and a variable already set in the environment is never overwritten by a file:
40
+ whatever your host injects wins.
36
41
 
37
- Une seule feuille de style : `@odoro-cli/libs/styles.css`, a importer une fois
38
- a la racine de l'application.
42
+ **Only variables prefixed with `ODORO_` reach the browser**, through
43
+ `import.meta.env`. Anyone who opens the site can then read them. Everything
44
+ else stays on the machine.
39
45
 
40
- Elle ne contient que le socle — variables, preflight, animations. Les
41
- utilitaires sont produits a la compilation, pour les seules classes que votre
42
- code emploie : quelques dizaines de kilooctets en pratique, et toute la palette
43
- disponible sans que rien ne pese.
46
+ ```ts
47
+ const api = import.meta.env.ODORO_API
48
+ ```
49
+
50
+ `odoro build --mode staging` reads `.env.staging` and fills in
51
+ `import.meta.env.MODE`.
52
+
53
+ ## Prerendering
54
+
55
+ `build.prerender` is on: `odoro build` renders every route of
56
+ `src/entry-server.tsx` as a complete HTML document, and `main.tsx` hydrates it
57
+ on load rather than rebuilding everything.
58
+
59
+ That is what lets a search engine, a link preview or a screen reader find text
60
+ in the very first response. `/about` becomes `dist/about/index.html`, which any
61
+ static host serves with no configuration — nothing runs in production.
62
+
63
+ Adding a route: one line in `routes`, and the matching entry in
64
+ `src/router.tsx`. Doing without: remove `prerender` from `odoro.config.ts`,
65
+ then delete `src/entry-server.tsx`.
66
+
67
+ ## Customising
68
+
69
+ Every visual value goes through a CSS variable. Overriding
70
+ `--o-palette-brand-600` in `src/styles.css` rethemes the application **and**
71
+ the library components, without touching their code.
72
+
73
+ A single stylesheet: `@odoro-cli/libs/styles.css`, imported once at the root of
74
+ the application.
75
+
76
+ It carries only the base — variables, preflight, animations. The utilities are
77
+ produced at build time, for the classes your code actually uses: a few tens of
78
+ kilobytes in practice, with the whole palette available and nothing weighing it
79
+ down.
44
80
 
45
- ## Rechargement a chaud
81
+ ## Hot reloading
46
82
 
47
- Modifier un composant remplace son code sans demonter l'arbre : la valeur d'un
48
- compteur, le texte saisi dans un champ, l'onglet ouvert survivent a l'edition.
49
- Une feuille de style est echangee sans rechargement.
83
+ Editing a component swaps its code without unmounting the tree: the value of a
84
+ counter, the text typed into a field, the open tab all survive the edit. A
85
+ stylesheet is swapped with no reload at all.
50
86
 
51
- Un module qui n'exporte pas que des composants recharge la page, et c'est
52
- correct : rien ne permettrait d'en propager le changement sans risque.
87
+ A module that exports more than components reloads the page, and that is
88
+ correct: nothing would let its change propagate safely.
@@ -0,0 +1,9 @@
1
+ # Variables prefixed with ODORO_ go into the published bundle: anyone who opens
2
+ # the site can read them. Everything else — tokens, server-side API keys — stays
3
+ # on the machine, readable by the commands and by the back end.
4
+ #
5
+ # Copy this file to `.env` (ignored by git) and fill in what you need.
6
+ # `.env.production` only applies to `odoro build`, `.env.development` only to
7
+ # `odoro dev`.
8
+
9
+ ODORO_API=http://localhost:3001
@@ -0,0 +1,118 @@
1
+ /**
2
+ * The decorative background, as a WebGL surface.
3
+ *
4
+ * This version replaces the static gradient because the engine was picked at
5
+ * creation. The rest of the page is unchanged: it places `<Background />` and
6
+ * does not know what is behind it.
7
+ *
8
+ * ## What the engine brings that a gradient cannot
9
+ *
10
+ * **An arbiter.** The surface is asked of the engine manager, which refuses it
11
+ * if the browser has no WebGL, if too many contexts are already open, or if
12
+ * the device would not keep up. The refusal is a value, not an exception: the
13
+ * gradient is shown instead, and the page holds.
14
+ *
15
+ * **Reduced motion.** Under that preference nothing is mounted: the background
16
+ * brought only its motion, and the graphics card has no business waking up for
17
+ * a visitor who asked for calm.
18
+ *
19
+ * **The theme colours.** The hues are read from the tokens, not written into
20
+ * the shader. A light / dark switch re-reads them, and changing
21
+ * `--o-palette-brand-500` repaints the background without touching this file.
22
+ *
23
+ * ## Why inline styles
24
+ *
25
+ * This file is laid down whatever the rest: a project that unticked the
26
+ * libraries has no `o-*` classes, and a missing class paints nothing — the
27
+ * background would then be zero pixels tall, with nothing to report it. So the
28
+ * few placement rules are written where they are safe.
29
+ *
30
+ * @module
31
+ */
32
+
33
+ import {
34
+ AURORA_FRAGMENT,
35
+ readTokenColour,
36
+ useMotionState,
37
+ useShaderSurface,
38
+ type ShaderColour,
39
+ } from '@odoro-cli/engine'
40
+ import { useEffect, useMemo, useState, type ReactElement } from 'react'
41
+
42
+ /** The tokens read to tint the glow. */
43
+ const HUES = ['--o-palette-brand-500', '--o-palette-brand-300', '--o-theme-bg'] as const
44
+
45
+ /** The gradient served while the surface is not there — or forever. */
46
+ function Gradient(): ReactElement {
47
+ return (
48
+ <div
49
+ style={{
50
+ position: 'absolute',
51
+ inset: 0,
52
+ opacity: 0.28,
53
+ filter: 'blur(64px)',
54
+ background:
55
+ 'radial-gradient(60% 60% at 50% 0%, var(--o-palette-brand-500, #3b82f6), transparent)',
56
+ }}
57
+ />
58
+ )
59
+ }
60
+
61
+ /** An animated glow behind the top of the page. */
62
+ export function Background(): ReactElement {
63
+ const { quality, reduced } = useMotionState()
64
+ const [host, setHost] = useState<HTMLElement | null>(null)
65
+ const [hues, setHues] = useState<readonly ShaderColour[]>([])
66
+
67
+ // Re-read when the host appears and when the theme switches — the latter
68
+ // goes through the motion policy, which is renewed on every state.
69
+ useEffect(() => {
70
+ if (host === null) return
71
+ setHues(HUES.map((token) => readTokenColour(token, host)))
72
+ }, [host, reduced, quality])
73
+
74
+ const uniforms = useMemo(() => {
75
+ const [a, b, c] = hues
76
+ if (a === undefined || b === undefined || c === undefined) return undefined
77
+ // The theme background dominates, the brand is only a reflection: these
78
+ // three colours go into a mix, and two brand hues out of three would give
79
+ // a full-page glow rather than a halo.
80
+ return { uColorA: c, uColorB: c, uColorC: a, uSpeed: 0.1, uScale: 1.4, uOctaves: 3 }
81
+ }, [hues])
82
+
83
+ const { ref, ready, refused } = useShaderSurface<HTMLDivElement>({
84
+ fragment: AURORA_FRAGMENT,
85
+ uniforms: uniforms ?? {},
86
+ name: 'home-background',
87
+ })
88
+
89
+ const fallback = !ready || refused !== undefined || uniforms === undefined
90
+
91
+ return (
92
+ <div
93
+ aria-hidden="true"
94
+ ref={(element) => {
95
+ setHost(element)
96
+ ref.current = element
97
+ }}
98
+ style={{
99
+ position: 'absolute',
100
+ insetInline: 0,
101
+ top: 0,
102
+ height: '42rem',
103
+ overflow: 'hidden',
104
+ pointerEvents: 'none',
105
+ // A decorative background is noticed when you look for it, not before.
106
+ // At full strength the glow comes in front of the title it is supposed
107
+ // to carry; at a fifth, it underlines it.
108
+ opacity: 0.2,
109
+ // The bottom dissolves into the page: a surface that stops dead draws
110
+ // a horizontal line that nothing justifies.
111
+ maskImage: 'linear-gradient(to bottom, black 45%, transparent)',
112
+ WebkitMaskImage: 'linear-gradient(to bottom, black 45%, transparent)',
113
+ }}
114
+ >
115
+ {fallback ? <Gradient /> : null}
116
+ </div>
117
+ )
118
+ }