odoro 1.0.0 → 1.0.2

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 (33) hide show
  1. package/README.md +73 -0
  2. package/dist/cli.js +3 -3
  3. package/dist/{commands-PT3NS65O.js → commands-AHMXBWQQ.js} +25 -5
  4. package/dist/{create-IOFAPDBO.js → create-GD6O2TPL.js} +118 -70
  5. package/dist/{package-6ADADETZ.js → package-VMTZDORI.js} +1 -1
  6. package/dist/registry/index.d.ts +2 -2
  7. package/package.json +1 -1
  8. package/templates/react-ts/_variantes/avec-moteur/src/fond.tsx +118 -0
  9. package/templates/react-ts/_variantes/sans-libs/src/App.tsx +245 -45
  10. package/templates/react-ts/_variantes/sans-libs/src/fond.tsx +23 -0
  11. package/templates/react-ts/_variantes/sans-libs/src/styles.css +357 -42
  12. package/templates/react-ts/_variantes/sans-routeur/src/App.tsx +305 -62
  13. package/templates/react-ts/public/favicon.svg +2 -3
  14. package/templates/react-ts/src/App.tsx +443 -43
  15. package/templates/react-ts/src/fond.tsx +42 -0
  16. package/templates/react-ts/src/router.tsx +72 -0
  17. package/templates/react-ts/src/styles.css +4 -5
  18. package/templates/react-ts-server/_variantes/avec-moteur/client/src/fond.tsx +118 -0
  19. package/templates/react-ts-server/_variantes/sans-libs/client/src/App.tsx +245 -45
  20. package/templates/react-ts-server/_variantes/sans-libs/client/src/fond.tsx +23 -0
  21. package/templates/react-ts-server/_variantes/sans-libs/client/src/styles.css +357 -42
  22. package/templates/react-ts-server/_variantes/sans-routeur/client/src/App.tsx +305 -62
  23. package/templates/react-ts-server/client/public/favicon.svg +2 -3
  24. package/templates/react-ts-server/client/src/App.tsx +443 -43
  25. package/templates/react-ts-server/client/src/fond.tsx +42 -0
  26. package/templates/react-ts-server/client/src/router.tsx +72 -0
  27. package/templates/react-ts-server/client/src/styles.css +4 -5
  28. package/templates/react-ts/src/routes/About.tsx +0 -19
  29. package/templates/react-ts/src/routes/Home.tsx +0 -80
  30. package/templates/react-ts/src/routes/NotFound.tsx +0 -14
  31. package/templates/react-ts-server/client/src/routes/About.tsx +0 -19
  32. package/templates/react-ts-server/client/src/routes/Home.tsx +0 -148
  33. package/templates/react-ts-server/client/src/routes/NotFound.tsx +0 -14
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Le fond decoratif, en surface WebGL.
3
+ *
4
+ * Cette version remplace le degrade statique parce que le moteur a ete retenu a
5
+ * la creation. Le reste de la page est inchange : elle place `<Fond />` et ne
6
+ * sait pas ce qu'il y a derriere.
7
+ *
8
+ * ## Ce que le moteur apporte, et qu'un degrade ne peut pas
9
+ *
10
+ * **Un arbitre.** La surface est demandee au gestionnaire du moteur, qui la
11
+ * refuse si le navigateur n'a pas WebGL, si trop de contextes sont deja
12
+ * ouverts, ou si l'appareil ne suivrait pas. Le refus est une valeur, pas une
13
+ * exception : on affiche alors le degrade, et la page tient.
14
+ *
15
+ * **Le mouvement reduit.** Sous cette preference, rien n'est monte : le fond
16
+ * n'apportait que son mouvement, et la carte graphique n'a pas a s'allumer pour
17
+ * un visiteur qui a demande le calme.
18
+ *
19
+ * **Les couleurs du theme.** Les teintes sont lues dans les jetons, pas ecrites
20
+ * dans le nuanceur. Une bascule clair / sombre les relit, et changer
21
+ * `--o-palette-brand-500` repeint le fond sans toucher a ce fichier.
22
+ *
23
+ * ## Pourquoi des styles en ligne
24
+ *
25
+ * Ce fichier est pose quel que soit le reste : un projet qui a decoche les
26
+ * bibliotheques n'a pas les classes `o-*`, et une classe absente ne peint rien
27
+ * — le fond aurait alors une hauteur nulle, sans que rien ne le signale. Les
28
+ * quelques regles de placement sont donc ecrites ou elles sont sures.
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
+ /** Les jetons lus pour teinter la nappe. */
43
+ const TEINTES = ['--o-palette-brand-500', '--o-palette-brand-300', '--o-theme-bg'] as const
44
+
45
+ /** Le degrade servi tant que la surface n'est pas la — ou pour toujours. */
46
+ function Degrade(): 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
+ /** Une nappe animee derriere le haut de la page. */
62
+ export function Fond(): ReactElement {
63
+ const { quality, reduced } = useMotionState()
64
+ const [hote, setHote] = useState<HTMLElement | null>(null)
65
+ const [teintes, setTeintes] = useState<readonly ShaderColour[]>([])
66
+
67
+ // Relues quand l'hote apparait et quand le theme bascule — ce dernier cas
68
+ // passe par la politique de mouvement, qui se renouvelle a chaque etat.
69
+ useEffect(() => {
70
+ if (hote === null) return
71
+ setTeintes(TEINTES.map((jeton) => readTokenColour(jeton, hote)))
72
+ }, [hote, reduced, quality])
73
+
74
+ const uniforms = useMemo(() => {
75
+ const [a, b, c] = teintes
76
+ if (a === undefined || b === undefined || c === undefined) return undefined
77
+ // Le fond du theme domine, la marque n'est qu'un reflet : ces trois
78
+ // couleurs vont dans un melange, et deux teintes de marque sur trois
79
+ // donneraient une nappe pleine page plutot qu'un halo.
80
+ return { uColorA: c, uColorB: c, uColorC: a, uSpeed: 0.1, uScale: 1.4, uOctaves: 3 }
81
+ }, [teintes])
82
+
83
+ const { ref, ready, refused } = useShaderSurface<HTMLDivElement>({
84
+ fragment: AURORA_FRAGMENT,
85
+ uniforms: uniforms ?? {},
86
+ name: 'fond-accueil',
87
+ })
88
+
89
+ const repli = !ready || refused !== undefined || uniforms === undefined
90
+
91
+ return (
92
+ <div
93
+ aria-hidden="true"
94
+ ref={(element) => {
95
+ setHote(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
+ // Un fond decoratif se remarque quand on le cherche, pas avant. A
106
+ // pleine intensite la nappe passe devant le titre qu'elle est censee
107
+ // porter ; a un cinquieme, elle le souligne.
108
+ opacity: 0.2,
109
+ // Le bas se dissout dans la page : une surface qui s'arrete net dessine
110
+ // une ligne horizontale que rien ne justifie.
111
+ maskImage: 'linear-gradient(to bottom, black 45%, transparent)',
112
+ WebkitMaskImage: 'linear-gradient(to bottom, black 45%, transparent)',
113
+ }}
114
+ >
115
+ {repli ? <Degrade /> : null}
116
+ </div>
117
+ )
118
+ }
@@ -1,71 +1,271 @@
1
- import { useState } from 'react'
1
+ /**
2
+ * Toute la page, en un fichier.
3
+ *
4
+ * ## Sans les bibliotheques
5
+ *
6
+ * Elles n'ont pas ete retenues a la creation : pas de classes `o-*`, pas de
7
+ * jetons, pas de routeur. Le dessin est celui de la version complete — meme
8
+ * barre en gelules, meme titre, memes cartes — porte par `styles.css` en CSS
9
+ * ordinaire.
10
+ *
11
+ * Pour les ajouter plus tard : `npm i @odoro-cli/libs`, puis importer
12
+ * `@odoro-cli/libs/styles.css` dans `main.tsx` avant cette feuille.
13
+ *
14
+ * @module
15
+ */
16
+
17
+ import { useState, type ReactElement } from 'react'
18
+
19
+ import { Fond } from '@/fond'
20
+
21
+ /* -------------------------------------------------------------------------- */
22
+ /* Marque */
23
+ /* -------------------------------------------------------------------------- */
24
+
25
+ /** Le signe de la marque, a la taille demandee. */
26
+ function Signe({ taille = '1.75rem' }: { readonly taille?: string }): ReactElement {
27
+ return (
28
+ <svg
29
+ viewBox="0 0 100 100"
30
+ fill="none"
31
+ aria-hidden="true"
32
+ style={{ width: taille, height: taille, flexShrink: 0 }}
33
+ >
34
+ <path
35
+ d="M20.25 20.25H50a29.75 29.75 0 1 1-29.75 29.75Z"
36
+ stroke="currentColor"
37
+ strokeWidth="10.5"
38
+ strokeLinejoin="miter"
39
+ />
40
+ </svg>
41
+ )
42
+ }
43
+
44
+ /* -------------------------------------------------------------------------- */
45
+ /* Barre de navigation */
46
+ /* -------------------------------------------------------------------------- */
47
+
48
+ /** La barre : la marque en gelule, et le lien vers le site. */
49
+ function Barre(): ReactElement {
50
+ return (
51
+ <header className="barre">
52
+ <div className="barre-contenu">
53
+ <span className="gelule marque">
54
+ <span className="marque-signe">
55
+ <Signe />
56
+ </span>
57
+ <span className="marque-mot">ODORO</span>
58
+ </span>
59
+
60
+ <a
61
+ href="https://odoro.dev"
62
+ target="_blank"
63
+ rel="noreferrer"
64
+ className="gelule gelule-lien"
65
+ >
66
+ odoro.dev
67
+ </a>
68
+ </div>
69
+ </header>
70
+ )
71
+ }
72
+
73
+ /* -------------------------------------------------------------------------- */
74
+ /* Sections */
75
+ /* -------------------------------------------------------------------------- */
76
+
77
+ /** Trois faits, pas trois slogans. */
78
+ const FAITS = ['Zero configuration', 'TypeScript strict', 'Rechargement a chaud']
79
+
80
+ /** Ce que la fenetre du produit montre. */
81
+ const EXTRAIT = `export function App() {
82
+ return (
83
+ <h1 className="hero-titre">
84
+ Votre premiere page.
85
+ </h1>
86
+ )
87
+ }`
88
+
89
+ /** Une fenetre de navigateur miniature. */
90
+ function Fenetre(): ReactElement {
91
+ return (
92
+ <div className="fenetre">
93
+ <div className="fenetre-barre">
94
+ <span className="fenetre-pastilles" aria-hidden="true">
95
+ <span />
96
+ <span />
97
+ <span />
98
+ </span>
99
+ <span className="fenetre-chemin">src/App.tsx</span>
100
+ </div>
101
+ <pre className="fenetre-code">{EXTRAIT}</pre>
102
+ </div>
103
+ )
104
+ }
105
+
106
+ /** Le hero : la promesse, les actions, et la fenetre du produit. */
107
+ function Hero(): ReactElement {
108
+ return (
109
+ <section className="hero">
110
+ <div className="contenu">
111
+ <span className="pastille-marque">
112
+ <Signe taille="0.9rem" />
113
+ Genere par odoro create
114
+ </span>
115
+
116
+ <h1 className="hero-titre">
117
+ Votre premiere page, <span className="accent">deja vivante</span>.
118
+ </h1>
119
+
120
+ <p className="hero-chapeau">
121
+ Le serveur de developpement, la compilation et le rechargement a chaud
122
+ viennent de <code>odoro</code>. Le systeme de style est le votre.
123
+ </p>
2
124
 
3
- /** Trois arguments mis en avant. */
4
- const FEATURES = [
125
+ <div className="hero-actions">
126
+ <a href="#piliers" className="bouton bouton-primaire">
127
+ Voir ce qui est inclus
128
+ </a>
129
+ <a
130
+ href="https://odoro.dev/docs"
131
+ target="_blank"
132
+ rel="noreferrer"
133
+ className="bouton bouton-secondaire"
134
+ >
135
+ Documentation
136
+ </a>
137
+ </div>
138
+
139
+ <div className="faits">
140
+ {FAITS.map((fait) => (
141
+ <span key={fait} className="fait">
142
+ {fait}
143
+ </span>
144
+ ))}
145
+ </div>
146
+
147
+ <div className="hero-fenetre">
148
+ <Fenetre />
149
+ </div>
150
+ </div>
151
+ </section>
152
+ )
153
+ }
154
+
155
+ /** Ce que le projet a deja sous la main. */
156
+ const PILIERS = [
5
157
  {
6
- title: 'Compilation',
7
- body: 'Un serveur de developpement a rechargement a chaud, et une compilation de production.',
158
+ titre: 'Compilation',
159
+ texte:
160
+ 'Un serveur de developpement a rechargement a chaud, et une compilation de production.',
8
161
  },
9
162
  {
10
- title: 'TypeScript',
11
- body: 'Types stricts, alias de chemins, et verification sans emission.',
163
+ titre: 'TypeScript',
164
+ texte: 'Types stricts, alias de chemins, et verification sans emission.',
12
165
  },
13
166
  {
14
- title: 'A vous de jouer',
15
- body: 'Aucun systeme de style impose : le votre prend toute la place.',
167
+ titre: 'A vous de jouer',
168
+ texte: 'Aucun systeme de style impose : le votre prend toute la place.',
16
169
  },
17
170
  ]
18
171
 
19
- /**
20
- * Racine de l'application.
21
- *
22
- * Les bibliotheques Odoro n'ont pas ete retenues a la creation : pas de
23
- * classes `o-*`, pas de jetons, pas de routeur. Les styles de cette page sont
24
- * dans `styles.css`, en CSS ordinaire.
25
- *
26
- * Pour les ajouter plus tard : `npm i @odoro-cli/libs`, puis importer
27
- * `@odoro-cli/libs/styles.css` dans `main.tsx`.
28
- */
29
- export function App() {
30
- const [count, setCount] = useState(0)
31
-
172
+ /** La section des piliers. */
173
+ function Piliers(): ReactElement {
32
174
  return (
33
- <div className="app-shell">
34
- <nav className="nav">
35
- <span className="marque">Odoro</span>
36
- </nav>
37
-
38
- <main className="contenu">
39
- <h1>Un point de depart maitrise.</h1>
40
- <p className="chapeau">
41
- Ce projet a ete genere par <code>odoro create</code>. Le moteur de
42
- developpement et la compilation viennent de <code>odoro</code>.
43
- </p>
44
-
175
+ <section id="piliers" className="section">
176
+ <div className="contenu">
177
+ <h2 className="sur-titre">Ce qui est deja la</h2>
45
178
  <div className="grille">
46
- {FEATURES.map((feature) => (
47
- <article key={feature.title} className="carte">
48
- <h2>{feature.title}</h2>
49
- <p>{feature.body}</p>
179
+ {PILIERS.map((pilier, index) => (
180
+ <article key={pilier.titre} className="carte">
181
+ <span className="carte-numero">{String(index + 1).padStart(2, '0')}</span>
182
+ <h3>{pilier.titre}</h3>
183
+ <p>{pilier.texte}</p>
50
184
  </article>
51
185
  ))}
52
186
  </div>
187
+ </div>
188
+ </section>
189
+ )
190
+ }
191
+
192
+ /** La commande que l'on copie le plus souvent. */
193
+ const COMMANDE = 'npm i @odoro-cli/libs'
194
+
195
+ /** La cloture : une commande, et la porte vers la documentation. */
196
+ function Cloture(): ReactElement {
197
+ const [copie, setCopie] = useState(false)
198
+
199
+ return (
200
+ <section className="section section-centre">
201
+ <div className="contenu-etroit">
202
+ <h2 className="cloture-titre">Ajoutez le systeme de style quand vous voudrez.</h2>
203
+ <p className="cloture-chapeau">
204
+ Les jetons, les utilitaires et les composants vivent dans un paquet a part. Ce
205
+ projet tourne sans eux, et les accueille sans rien casser.
206
+ </p>
53
207
 
54
- <div className="ligne">
208
+ <div className="commande">
209
+ <code>{COMMANDE}</code>
55
210
  <button
56
211
  type="button"
57
- className="bouton"
212
+ className="bouton bouton-fantome"
58
213
  onClick={() => {
59
- setCount((value) => value + 1)
214
+ // Le presse-papiers peut etre refuse — hors contexte sur, ou
215
+ // permission retiree. L'echec ne casse pas la page : le libelle
216
+ // ne change simplement pas.
217
+ void navigator.clipboard
218
+ ?.writeText(COMMANDE)
219
+ .then(() => {
220
+ setCopie(true)
221
+ setTimeout(() => {
222
+ setCopie(false)
223
+ }, 1600)
224
+ })
225
+ .catch(() => undefined)
60
226
  }}
61
227
  >
62
- Compter
228
+ {copie ? 'Copie' : 'Copier'}
63
229
  </button>
64
- <span className="compteur">{count}</span>
65
230
  </div>
66
- </main>
67
231
 
68
- <footer className="pied">Construit avec Odoro.</footer>
232
+ <a
233
+ href="https://odoro.dev/docs/installation"
234
+ target="_blank"
235
+ rel="noreferrer"
236
+ className="lien-discret"
237
+ >
238
+ Lire la marche a suivre
239
+ </a>
240
+ </div>
241
+ </section>
242
+ )
243
+ }
244
+
245
+ /* -------------------------------------------------------------------------- */
246
+ /* Racine */
247
+ /* -------------------------------------------------------------------------- */
248
+
249
+ /** Racine de l'application. */
250
+ export function App(): ReactElement {
251
+ return (
252
+ <div className="app-shell">
253
+ <Barre />
254
+ <main className="principal">
255
+ <Fond />
256
+ <Hero />
257
+ <Piliers />
258
+ <Cloture />
259
+ </main>
260
+ <footer className="pied">
261
+ <span className="marque">
262
+ <span className="marque-signe">
263
+ <Signe taille="1.1rem" />
264
+ </span>
265
+ <span className="marque-mot">ODORO</span>
266
+ </span>
267
+ <span>Construit avec Odoro.</span>
268
+ </footer>
69
269
  </div>
70
270
  )
71
271
  }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Le fond decoratif de la page.
3
+ *
4
+ * Deux nappes de couleur de marque, tres diluees. Rien ne bouge : c'est un
5
+ * degrade, pas une animation.
6
+ *
7
+ * Si le moteur a ete retenu, ce fichier est remplace par une version qui ouvre
8
+ * une surface WebGL animee. `App.tsx` place `<Fond />` et n'en demande pas plus.
9
+ *
10
+ * @module
11
+ */
12
+
13
+ import type { ReactElement } from 'react'
14
+
15
+ /** Deux nappes de marque derriere le haut de la page. */
16
+ export function Fond(): ReactElement {
17
+ return (
18
+ <div className="fond" aria-hidden="true">
19
+ <div className="fond-nappe fond-nappe-haute" />
20
+ <div className="fond-nappe fond-nappe-droite" />
21
+ </div>
22
+ )
23
+ }