odoro 1.0.2 → 1.0.4
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.md +9 -9
- package/dist/build-KI7IEVRJ.js +4 -0
- package/dist/{chunk-R6PMAMXA.js → chunk-2EE5QUCW.js} +16 -3
- package/dist/{chunk-CJIUP7A3.js → chunk-FJYUQALD.js} +1 -1
- package/dist/cli.js +4 -4
- package/dist/{create-GD6O2TPL.js → create-LVNBZOGV.js} +21 -5
- package/dist/index.js +2 -2
- package/dist/{package-VMTZDORI.js → package-5KU4PW5P.js} +7 -5
- package/dist/{server-ATGFRVSW.js → server-DXOQAPVX.js} +1 -1
- package/package.json +6 -4
- package/templates/react-ts/_variantes/sans-libs/src/App.tsx +122 -11
- package/templates/react-ts/_variantes/sans-libs/src/styles.css +49 -5
- package/templates/react-ts/_variantes/sans-routeur/src/App.tsx +132 -12
- package/templates/react-ts/index.html +11 -0
- package/templates/react-ts/src/App.tsx +177 -22
- package/templates/react-ts-server/_variantes/sans-libs/client/src/App.tsx +122 -11
- package/templates/react-ts-server/_variantes/sans-libs/client/src/styles.css +49 -5
- package/templates/react-ts-server/_variantes/sans-routeur/client/src/App.tsx +132 -12
- package/templates/react-ts-server/client/index.html +11 -0
- package/templates/react-ts-server/client/src/App.tsx +177 -22
- package/dist/build-GIPMPYJ3.js +0 -4
package/README.md
CHANGED
|
@@ -14,15 +14,15 @@ package is what it installs.
|
|
|
14
14
|
|
|
15
15
|
| Command | What it does |
|
|
16
16
|
| ---------------- | --------------------------------------------------------- |
|
|
17
|
-
| `odoro dev` | Development server with hot module replacement.
|
|
18
|
-
| `odoro build` | Production build into `dist/`.
|
|
19
|
-
| `odoro preview` | Serves `dist/` the way a static host would.
|
|
20
|
-
| `odoro create` | Scaffolds a project — same thing as `npm create odoro`.
|
|
21
|
-
| `odoro init` | Writes `odoro.json` so `odoro add` knows where to write.
|
|
22
|
-
| `odoro add <id>` | Copies a registry component into your project.
|
|
23
|
-
| `odoro list` | Prints the registry catalogue.
|
|
24
|
-
| `odoro diff` | Compares what is installed with what the registry serves.
|
|
25
|
-
| `odoro doctor` | Checks that the project is in a fit state.
|
|
17
|
+
| `odoro dev` | Development server with hot module replacement. |
|
|
18
|
+
| `odoro build` | Production build into `dist/`. |
|
|
19
|
+
| `odoro preview` | Serves `dist/` the way a static host would. |
|
|
20
|
+
| `odoro create` | Scaffolds a project — same thing as `npm create odoro`. |
|
|
21
|
+
| `odoro init` | Writes `odoro.json` so `odoro add` knows where to write. |
|
|
22
|
+
| `odoro add <id>` | Copies a registry component into your project. |
|
|
23
|
+
| `odoro list` | Prints the registry catalogue. |
|
|
24
|
+
| `odoro diff` | Compares what is installed with what the registry serves. |
|
|
25
|
+
| `odoro doctor` | Checks that the project is in a fit state. |
|
|
26
26
|
|
|
27
27
|
Run `odoro --help` for the full list, including the database commands.
|
|
28
28
|
|
|
@@ -34,7 +34,9 @@ async function fournisseurDe(root) {
|
|
|
34
34
|
// `generate` enumere les classes du palier courant. Absent d'un paquet
|
|
35
35
|
// plus ancien : le champ reste alors indefini, et le developpement
|
|
36
36
|
// retombe sur la feuille telle qu'elle est livree.
|
|
37
|
-
...typeof generate === "function" ? {
|
|
37
|
+
...typeof generate === "function" ? {
|
|
38
|
+
classesConnues: () => new Set(generate("core").classNames)
|
|
39
|
+
} : {}
|
|
38
40
|
};
|
|
39
41
|
} catch {
|
|
40
42
|
return void 0;
|
|
@@ -720,6 +722,11 @@ async function applyReactRefresh(code, file) {
|
|
|
720
722
|
// La carte de source produite par la compilation precedente est reprise et
|
|
721
723
|
// fusionnee : sans cela, les numeros de ligne du debogueur designeraient le
|
|
722
724
|
// code instrumente plutot que la source.
|
|
725
|
+
//
|
|
726
|
+
// `true` demande a Babel de lire la carte inline du code recu. Il l'accepte
|
|
727
|
+
// — verifie : la source reprise est bien le fichier d'origine — mais les
|
|
728
|
+
// types de la 7.x ne decrivent que la forme objet. L'annotation est
|
|
729
|
+
// incomplete, pas la valeur.
|
|
723
730
|
inputSourceMap: true,
|
|
724
731
|
sourceMaps: "inline",
|
|
725
732
|
// `skipEnvCheck` leve un garde-fou destine aux configurations globales,
|
|
@@ -934,7 +941,9 @@ async function startDevServer(config) {
|
|
|
934
941
|
const fournisseur = await fournisseurDe(config.root);
|
|
935
942
|
const utilitaires = fournisseur?.classesConnues === void 0 ? void 0 : fournisseur.renderUtilitairesPour(fournisseur.classesConnues());
|
|
936
943
|
if (utilitaires !== void 0) {
|
|
937
|
-
info(
|
|
944
|
+
info(
|
|
945
|
+
`utilitaires de developpement : ${String(Math.round(utilitaires.length / 1024))} Ko`
|
|
946
|
+
);
|
|
938
947
|
}
|
|
939
948
|
const entries = extractEntries(await readFile(indexFile, "utf8"), config.root);
|
|
940
949
|
const specifiers = await scanDependencies(config, entries);
|
|
@@ -1005,7 +1014,11 @@ async function startDevServer(config) {
|
|
|
1005
1014
|
};
|
|
1006
1015
|
const serveHtml = async (response) => {
|
|
1007
1016
|
const html = await readFile(indexFile, "utf8");
|
|
1008
|
-
send(
|
|
1017
|
+
send(
|
|
1018
|
+
response,
|
|
1019
|
+
injectClient(html, utilitaires !== void 0),
|
|
1020
|
+
MIME[".html"]
|
|
1021
|
+
);
|
|
1009
1022
|
};
|
|
1010
1023
|
const forward = (incoming, response, target) => {
|
|
1011
1024
|
const url2 = new URL(incoming.url ?? "/", target);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { extractEntries, applyAlias, isBareSpecifier, fournisseurDe } from './chunk-
|
|
2
|
+
import { extractEntries, applyAlias, isBareSpecifier, fournisseurDe } from './chunk-2EE5QUCW.js';
|
|
3
3
|
import { info, colors, size, success, duration } from './chunk-JMEHF3KN.js';
|
|
4
4
|
import { existsSync } from 'fs';
|
|
5
5
|
import { readFile, rm, mkdir, writeFile, cp, stat } from 'fs/promises';
|
package/dist/cli.js
CHANGED
|
@@ -131,7 +131,7 @@ function rootFrom(flags, positional) {
|
|
|
131
131
|
async function run(argv) {
|
|
132
132
|
const { command, positional, flags } = parseArgs(argv);
|
|
133
133
|
if (flags["version"] === true) {
|
|
134
|
-
const manifest = await import('./package-
|
|
134
|
+
const manifest = await import('./package-5KU4PW5P.js');
|
|
135
135
|
console.log(manifest.default.version);
|
|
136
136
|
return 0;
|
|
137
137
|
}
|
|
@@ -142,7 +142,7 @@ async function run(argv) {
|
|
|
142
142
|
switch (command) {
|
|
143
143
|
case "create":
|
|
144
144
|
case "new": {
|
|
145
|
-
const { createCommand } = await import('./create-
|
|
145
|
+
const { createCommand } = await import('./create-LVNBZOGV.js');
|
|
146
146
|
const options = {};
|
|
147
147
|
if (positional[0] !== void 0) options.name = positional[0];
|
|
148
148
|
if (typeof flags["template"] === "string") options.template = flags["template"];
|
|
@@ -156,13 +156,13 @@ async function run(argv) {
|
|
|
156
156
|
return createCommand(options);
|
|
157
157
|
}
|
|
158
158
|
case "dev": {
|
|
159
|
-
const { startDevServer } = await import('./server-
|
|
159
|
+
const { startDevServer } = await import('./server-DXOQAPVX.js');
|
|
160
160
|
const config = await loadConfig(rootFrom(flags, positional), overridesFrom(flags));
|
|
161
161
|
await startDevServer(config);
|
|
162
162
|
return new Promise(() => void 0);
|
|
163
163
|
}
|
|
164
164
|
case "build": {
|
|
165
|
-
const { buildProject, reportBuild } = await import('./build-
|
|
165
|
+
const { buildProject, reportBuild } = await import('./build-KI7IEVRJ.js');
|
|
166
166
|
const config = await loadConfig(rootFrom(flags, positional), overridesFrom(flags));
|
|
167
167
|
const output = await buildProject(config);
|
|
168
168
|
reportBuild(output, process.cwd());
|
|
@@ -142,6 +142,16 @@ function availableTemplates(root = templatesRoot()) {
|
|
|
142
142
|
return readdirSync(root).filter((entry) => statSync(resolve(root, entry)).isDirectory()).sort();
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
+
// src/scaffold/versions-famille.generated.ts
|
|
146
|
+
var VERSIONS_FAMILLE = {
|
|
147
|
+
"@odoro-cli/engine": "1.0.2",
|
|
148
|
+
"@odoro-cli/icons": "1.0.2",
|
|
149
|
+
"@odoro-cli/libs": "1.0.2",
|
|
150
|
+
"@odoro-cli/server": "1.0.2",
|
|
151
|
+
"create-odoro": "1.0.4",
|
|
152
|
+
"odoro": "1.0.4"
|
|
153
|
+
};
|
|
154
|
+
|
|
145
155
|
// src/scaffold/scaffold.ts
|
|
146
156
|
var DOSSIER_VARIANTES = "_variantes";
|
|
147
157
|
var PAQUETS_OPTIONNELS = ["@odoro-cli/libs", "@odoro-cli/icons", "@odoro-cli/engine"];
|
|
@@ -173,6 +183,12 @@ async function copyDirectory(from, to, written, prefix = "") {
|
|
|
173
183
|
function isOdoroPackage(name) {
|
|
174
184
|
return name === "odoro" || name.startsWith("@odoro-cli/");
|
|
175
185
|
}
|
|
186
|
+
function versionDemandee(nom, versionCli2) {
|
|
187
|
+
if (versionCli2 === "latest") return "latest";
|
|
188
|
+
if (nom === "odoro") return `^${versionCli2}`;
|
|
189
|
+
const relevee = VERSIONS_FAMILLE[nom];
|
|
190
|
+
return relevee === void 0 ? "latest" : `^${relevee}`;
|
|
191
|
+
}
|
|
176
192
|
function alignOdoroVersions(manifest, version) {
|
|
177
193
|
const aligned = { ...manifest };
|
|
178
194
|
for (const field of ["dependencies", "devDependencies", "peerDependencies"]) {
|
|
@@ -180,7 +196,7 @@ function alignOdoroVersions(manifest, version) {
|
|
|
180
196
|
if (typeof deps !== "object" || deps === null) continue;
|
|
181
197
|
const next = {};
|
|
182
198
|
for (const [name, range] of Object.entries(deps)) {
|
|
183
|
-
next[name] = isOdoroPackage(name) ?
|
|
199
|
+
next[name] = isOdoroPackage(name) ? versionDemandee(name, version) : range;
|
|
184
200
|
}
|
|
185
201
|
aligned[field] = next;
|
|
186
202
|
}
|
|
@@ -444,9 +460,7 @@ async function createCommand(options) {
|
|
|
444
460
|
})
|
|
445
461
|
));
|
|
446
462
|
if (!templates.includes(template)) {
|
|
447
|
-
prompts.cancel(
|
|
448
|
-
`Unknown template: "${template}". Available: ${templates.join(", ")}.`
|
|
449
|
-
);
|
|
463
|
+
prompts.cancel(`Unknown template: "${template}". Available: ${templates.join(", ")}.`);
|
|
450
464
|
return 1;
|
|
451
465
|
}
|
|
452
466
|
const modules = await askModules(options);
|
|
@@ -485,7 +499,9 @@ async function createCommand(options) {
|
|
|
485
499
|
if (overwrite !== void 0) scaffoldOptions.overwrite = overwrite;
|
|
486
500
|
scaffoldOptions.modules = modules;
|
|
487
501
|
const { files } = await scaffold(scaffoldOptions);
|
|
488
|
-
spinner2.stop(
|
|
502
|
+
spinner2.stop(
|
|
503
|
+
`${String(files.length)} files written to ${colors.cyan(basename(target))}`
|
|
504
|
+
);
|
|
489
505
|
if (withGit && !existsSync(resolve(target, ".git"))) {
|
|
490
506
|
try {
|
|
491
507
|
execSync("git init -q", { cwd: target, stdio: "ignore" });
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
export { startPreviewServer } from './chunk-34ES2ICQ.js';
|
|
3
3
|
export { defineConfig, loadConfig } from './chunk-DL3NPC4H.js';
|
|
4
4
|
import './chunk-T6RLHSCW.js';
|
|
5
|
-
export { buildProject, reportBuild } from './chunk-
|
|
6
|
-
export { ModuleGraph, depFileName, detectSelfAccepting, optimizeDeps, scanDependencies, startDevServer } from './chunk-
|
|
5
|
+
export { buildProject, reportBuild } from './chunk-FJYUQALD.js';
|
|
6
|
+
export { ModuleGraph, depFileName, detectSelfAccepting, optimizeDeps, scanDependencies, startDevServer } from './chunk-2EE5QUCW.js';
|
|
7
7
|
import './chunk-JMEHF3KN.js';
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// package.json
|
|
3
3
|
var package_default = {
|
|
4
4
|
name: "odoro",
|
|
5
|
-
version: "1.0.
|
|
5
|
+
version: "1.0.4",
|
|
6
6
|
type: "module",
|
|
7
7
|
license: "UNLICENSED",
|
|
8
8
|
author: "BouBouw",
|
|
@@ -37,20 +37,22 @@ var package_default = {
|
|
|
37
37
|
}
|
|
38
38
|
},
|
|
39
39
|
scripts: {
|
|
40
|
-
build: "tsup",
|
|
40
|
+
build: "node scripts/versions-famille.mjs && tsup",
|
|
41
41
|
dev: "tsup --watch",
|
|
42
42
|
test: "vitest run",
|
|
43
|
-
typecheck: "tsc --noEmit",
|
|
44
|
-
prepublishOnly: "pnpm run build"
|
|
43
|
+
typecheck: "node scripts/versions-famille.mjs && tsc --noEmit",
|
|
44
|
+
prepublishOnly: "pnpm run build",
|
|
45
|
+
"versions:famille": "node scripts/versions-famille.mjs"
|
|
45
46
|
},
|
|
46
47
|
dependencies: {
|
|
47
|
-
"@babel/core": "^
|
|
48
|
+
"@babel/core": "^7.29.7",
|
|
48
49
|
"@clack/prompts": "^0.11.0",
|
|
49
50
|
esbuild: "^0.28.2",
|
|
50
51
|
picocolors: "^1.1.1",
|
|
51
52
|
"react-refresh": "^0.18.0"
|
|
52
53
|
},
|
|
53
54
|
devDependencies: {
|
|
55
|
+
"@types/babel__core": "^7.20.5",
|
|
54
56
|
"@types/node": "^22.18.8",
|
|
55
57
|
tsup: "^8.5.0",
|
|
56
58
|
typescript: "^5.9.3",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "odoro",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"author": "BouBouw",
|
|
@@ -35,13 +35,14 @@
|
|
|
35
35
|
}
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@babel/core": "^
|
|
38
|
+
"@babel/core": "^7.29.7",
|
|
39
39
|
"@clack/prompts": "^0.11.0",
|
|
40
40
|
"esbuild": "^0.28.2",
|
|
41
41
|
"picocolors": "^1.1.1",
|
|
42
42
|
"react-refresh": "^0.18.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
+
"@types/babel__core": "^7.20.5",
|
|
45
46
|
"@types/node": "^22.18.8",
|
|
46
47
|
"tsup": "^8.5.0",
|
|
47
48
|
"typescript": "^5.9.3",
|
|
@@ -58,9 +59,10 @@
|
|
|
58
59
|
"url": "https://github.com/ODORO-CLI/OdoroKit/issues"
|
|
59
60
|
},
|
|
60
61
|
"scripts": {
|
|
61
|
-
"build": "tsup",
|
|
62
|
+
"build": "node scripts/versions-famille.mjs && tsup",
|
|
62
63
|
"dev": "tsup --watch",
|
|
63
64
|
"test": "vitest run",
|
|
64
|
-
"typecheck": "tsc --noEmit"
|
|
65
|
+
"typecheck": "node scripts/versions-famille.mjs && tsc --noEmit",
|
|
66
|
+
"versions:famille": "node scripts/versions-famille.mjs"
|
|
65
67
|
}
|
|
66
68
|
}
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* @module
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import { useState, type ReactElement } from 'react'
|
|
17
|
+
import { useEffect, useState, type ReactElement } from 'react'
|
|
18
18
|
|
|
19
19
|
import { Fond } from '@/fond'
|
|
20
20
|
|
|
@@ -57,19 +57,128 @@ function Barre(): ReactElement {
|
|
|
57
57
|
<span className="marque-mot">ODORO</span>
|
|
58
58
|
</span>
|
|
59
59
|
|
|
60
|
-
<
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
60
|
+
<div className="barre-fin">
|
|
61
|
+
<a
|
|
62
|
+
href="https://odoro.dev"
|
|
63
|
+
target="_blank"
|
|
64
|
+
rel="noreferrer"
|
|
65
|
+
className="gelule gelule-lien"
|
|
66
|
+
>
|
|
67
|
+
odoro.dev
|
|
68
|
+
</a>
|
|
69
|
+
<BasculeTheme />
|
|
70
|
+
</div>
|
|
68
71
|
</div>
|
|
69
72
|
</header>
|
|
70
73
|
)
|
|
71
74
|
}
|
|
72
75
|
|
|
76
|
+
/* -------------------------------------------------------------------------- */
|
|
77
|
+
/* Theme */
|
|
78
|
+
/* -------------------------------------------------------------------------- */
|
|
79
|
+
|
|
80
|
+
/** Les trois etats de la bascule. */
|
|
81
|
+
type Theme = 'systeme' | 'clair' | 'sombre'
|
|
82
|
+
|
|
83
|
+
/** Ou le choix est memorise. Le script de `index.html` lit la meme cle. */
|
|
84
|
+
const CLE_THEME = 'odoro-theme'
|
|
85
|
+
|
|
86
|
+
/** L'ordre du cycle, au clic. */
|
|
87
|
+
const CYCLE: readonly Theme[] = ['systeme', 'clair', 'sombre']
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Pose le theme sur la racine du document.
|
|
91
|
+
*
|
|
92
|
+
* `systeme` retire l'attribut plutot que d'en poser un troisieme : la feuille
|
|
93
|
+
* de style ecoute `data-theme`, et son absence rend la main a la preference du
|
|
94
|
+
* navigateur.
|
|
95
|
+
*/
|
|
96
|
+
function poserTheme(theme: Theme): void {
|
|
97
|
+
if (theme === 'systeme') delete document.documentElement.dataset.theme
|
|
98
|
+
else document.documentElement.dataset.theme = theme === 'clair' ? 'light' : 'dark'
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Le theme memorise, ou `systeme`. */
|
|
102
|
+
function themeMemorise(): Theme {
|
|
103
|
+
try {
|
|
104
|
+
const valeur = localStorage.getItem(CLE_THEME)
|
|
105
|
+
if (valeur === 'light') return 'clair'
|
|
106
|
+
if (valeur === 'dark') return 'sombre'
|
|
107
|
+
return 'systeme'
|
|
108
|
+
} catch {
|
|
109
|
+
// Stockage refuse — fenetre privee, cookies bloques. Le theme du systeme
|
|
110
|
+
// reste une reponse valable.
|
|
111
|
+
return 'systeme'
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Les trois pictogrammes, dessines dans le flux. */
|
|
116
|
+
function Pictogramme({ theme }: { readonly theme: Theme }): ReactElement {
|
|
117
|
+
const commun = {
|
|
118
|
+
viewBox: '0 0 24 24',
|
|
119
|
+
fill: 'none',
|
|
120
|
+
stroke: 'currentColor',
|
|
121
|
+
strokeWidth: 1.8,
|
|
122
|
+
strokeLinecap: 'round' as const,
|
|
123
|
+
strokeLinejoin: 'round' as const,
|
|
124
|
+
'aria-hidden': true,
|
|
125
|
+
style: { width: '1.15rem', height: '1.15rem' },
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (theme === 'clair') {
|
|
129
|
+
return (
|
|
130
|
+
<svg {...commun}>
|
|
131
|
+
<circle cx="12" cy="12" r="4" />
|
|
132
|
+
<path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4" />
|
|
133
|
+
</svg>
|
|
134
|
+
)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (theme === 'sombre') {
|
|
138
|
+
return (
|
|
139
|
+
<svg {...commun}>
|
|
140
|
+
<path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8Z" />
|
|
141
|
+
</svg>
|
|
142
|
+
)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return (
|
|
146
|
+
<svg {...commun}>
|
|
147
|
+
<rect x="2" y="4" width="20" height="13" rx="2" />
|
|
148
|
+
<path d="M8 21h8M12 17v4" />
|
|
149
|
+
</svg>
|
|
150
|
+
)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** La bascule de theme : systeme, clair, sombre. */
|
|
154
|
+
function BasculeTheme(): ReactElement {
|
|
155
|
+
const [theme, setTheme] = useState<Theme>(() => themeMemorise())
|
|
156
|
+
|
|
157
|
+
useEffect(() => {
|
|
158
|
+
poserTheme(theme)
|
|
159
|
+
try {
|
|
160
|
+
if (theme === 'systeme') localStorage.removeItem(CLE_THEME)
|
|
161
|
+
else localStorage.setItem(CLE_THEME, theme === 'clair' ? 'light' : 'dark')
|
|
162
|
+
} catch {
|
|
163
|
+
// Le theme reste applique pour la session, meme sans stockage.
|
|
164
|
+
}
|
|
165
|
+
}, [theme])
|
|
166
|
+
|
|
167
|
+
return (
|
|
168
|
+
<button
|
|
169
|
+
type="button"
|
|
170
|
+
aria-label={`Theme : ${theme}`}
|
|
171
|
+
title={`Theme : ${theme}`}
|
|
172
|
+
className="gelule bascule"
|
|
173
|
+
onClick={() => {
|
|
174
|
+
setTheme((actuel) => CYCLE[(CYCLE.indexOf(actuel) + 1) % CYCLE.length] ?? 'systeme')
|
|
175
|
+
}}
|
|
176
|
+
>
|
|
177
|
+
<Pictogramme theme={theme} />
|
|
178
|
+
</button>
|
|
179
|
+
)
|
|
180
|
+
}
|
|
181
|
+
|
|
73
182
|
/* -------------------------------------------------------------------------- */
|
|
74
183
|
/* Sections */
|
|
75
184
|
/* -------------------------------------------------------------------------- */
|
|
@@ -249,10 +358,12 @@ function Cloture(): ReactElement {
|
|
|
249
358
|
/** Racine de l'application. */
|
|
250
359
|
export function App(): ReactElement {
|
|
251
360
|
return (
|
|
252
|
-
|
|
361
|
+
// Le fond est pose ici et non dans le contenu : ancre plus bas, il
|
|
362
|
+
// commencait sous la barre et laissait une bande plus sombre derriere elle.
|
|
363
|
+
<div className="app-shell app-shell-fond">
|
|
364
|
+
<Fond />
|
|
253
365
|
<Barre />
|
|
254
366
|
<main className="principal">
|
|
255
|
-
<Fond />
|
|
256
367
|
<Hero />
|
|
257
368
|
<Piliers />
|
|
258
369
|
<Cloture />
|
|
@@ -21,11 +21,14 @@
|
|
|
21
21
|
color-scheme: light dark;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
/*
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
/* Les valeurs du theme sombre, ecrites une fois et reprises deux fois.
|
|
25
|
+
|
|
26
|
+
La bascule pose `data-theme` sur la racine ; sans attribut, la preference du
|
|
27
|
+
navigateur decide. Les deux regles sont donc necessaires : la seconde sans la
|
|
28
|
+
premiere ignorerait le choix du visiteur, la premiere sans la seconde
|
|
29
|
+
rendrait un fond blanc au milieu de la nuit. */
|
|
27
30
|
@media (prefers-color-scheme: dark) {
|
|
28
|
-
:root {
|
|
31
|
+
:root:not([data-theme='light']) {
|
|
29
32
|
--fond: #09090b;
|
|
30
33
|
--encre: #fafafa;
|
|
31
34
|
--doux: #a1a1aa;
|
|
@@ -34,6 +37,25 @@
|
|
|
34
37
|
}
|
|
35
38
|
}
|
|
36
39
|
|
|
40
|
+
:root[data-theme='dark'] {
|
|
41
|
+
--fond: #09090b;
|
|
42
|
+
--encre: #fafafa;
|
|
43
|
+
--doux: #a1a1aa;
|
|
44
|
+
--filet: #27272a;
|
|
45
|
+
--surface: #18181b;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/* Le choix explicite est dit au navigateur : sans cela, `light-dark()`, les
|
|
49
|
+
controles natifs et les barres de defilement continuent de suivre un systeme
|
|
50
|
+
en nuit alors que le visiteur a demande le clair. */
|
|
51
|
+
:root[data-theme='light'] {
|
|
52
|
+
color-scheme: light;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
:root[data-theme='dark'] {
|
|
56
|
+
color-scheme: dark;
|
|
57
|
+
}
|
|
58
|
+
|
|
37
59
|
* {
|
|
38
60
|
box-sizing: border-box;
|
|
39
61
|
}
|
|
@@ -93,12 +115,30 @@ pre {
|
|
|
93
115
|
text-decoration: none;
|
|
94
116
|
}
|
|
95
117
|
|
|
96
|
-
.
|
|
118
|
+
.barre-fin {
|
|
97
119
|
margin-left: auto;
|
|
120
|
+
display: flex;
|
|
121
|
+
align-items: center;
|
|
122
|
+
gap: 0.75rem;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.gelule-lien {
|
|
98
126
|
color: var(--doux);
|
|
99
127
|
font-size: 0.875rem;
|
|
100
128
|
}
|
|
101
129
|
|
|
130
|
+
.bascule {
|
|
131
|
+
width: 3.5rem;
|
|
132
|
+
justify-content: center;
|
|
133
|
+
padding: 0;
|
|
134
|
+
color: var(--doux);
|
|
135
|
+
cursor: pointer;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.bascule:hover {
|
|
139
|
+
color: var(--encre);
|
|
140
|
+
}
|
|
141
|
+
|
|
102
142
|
.gelule-lien:hover {
|
|
103
143
|
color: var(--encre);
|
|
104
144
|
}
|
|
@@ -125,6 +165,10 @@ pre {
|
|
|
125
165
|
|
|
126
166
|
/* ----- Coquille ----------------------------------------------------------- */
|
|
127
167
|
|
|
168
|
+
.app-shell-fond {
|
|
169
|
+
position: relative;
|
|
170
|
+
}
|
|
171
|
+
|
|
128
172
|
.principal {
|
|
129
173
|
position: relative;
|
|
130
174
|
}
|