@mostajs/qrpanel 0.1.0 → 0.3.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.md +159 -45
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +70 -0
- package/dist/cli.js.map +1 -0
- package/dist/composer.d.ts +43 -0
- package/dist/composer.d.ts.map +1 -0
- package/dist/composer.js +121 -0
- package/dist/composer.js.map +1 -0
- package/dist/config.d.ts +57 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +113 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +64 -11
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +104 -19
- package/dist/server.js.map +1 -1
- package/dist/themes.d.ts +41 -0
- package/dist/themes.d.ts.map +1 -0
- package/dist/themes.js +186 -0
- package/dist/themes.js.map +1 -0
- package/package.json +23 -5
package/dist/config.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// @mostajs/qrpanel/config — runtime config layer
|
|
2
|
+
// Author: Dr Hamid MADANI <drmdh@msn.com>
|
|
3
|
+
//
|
|
4
|
+
// Cascade :
|
|
5
|
+
// 1. defaults compilés (ce fichier)
|
|
6
|
+
// 2. .qrconfig.json (ou .qrconfig.js / .qrconfig) à process.cwd()
|
|
7
|
+
// 3. options passées explicitement à l'appel generateQr* (highest)
|
|
8
|
+
//
|
|
9
|
+
// Cache : la lecture du fichier est cachée en mémoire avec invalidation
|
|
10
|
+
// par mtime — édite le fichier, le prochain appel le relit. Pas de
|
|
11
|
+
// watcher (overkill pour ce cas d'usage).
|
|
12
|
+
import { readFileSync, existsSync, statSync, writeFileSync } from 'node:fs';
|
|
13
|
+
import { join, resolve } from 'node:path';
|
|
14
|
+
// ─── Defaults ──────────────────────────────────────────────────────
|
|
15
|
+
export const DEFAULT_CONFIG = {
|
|
16
|
+
default: {
|
|
17
|
+
genimage: true,
|
|
18
|
+
format: 'svg',
|
|
19
|
+
width: 600,
|
|
20
|
+
margin: 2,
|
|
21
|
+
errorCorrectionLevel: 'H',
|
|
22
|
+
darkColor: '#0f172a',
|
|
23
|
+
lightColor: '#ffffff',
|
|
24
|
+
theme: 'random',
|
|
25
|
+
themePool: [
|
|
26
|
+
'baby', 'animals', 'science', 'physics', 'chemistry', 'math',
|
|
27
|
+
'nature', 'tech', 'space', 'music', 'book', 'health',
|
|
28
|
+
],
|
|
29
|
+
framePadding: 0.13,
|
|
30
|
+
centerWhiteRatio: 0.62,
|
|
31
|
+
themeOpacity: 1.0,
|
|
32
|
+
themeColor: '#1e293b',
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
// ─── Lookup file paths ─────────────────────────────────────────────
|
|
36
|
+
const CONFIG_FILES = ['.qrconfig.json', '.qrconfig.js', '.qrconfig'];
|
|
37
|
+
/** Cherche le fichier config existant à `cwd`, retourne le path absolu ou null. */
|
|
38
|
+
function findConfigFile(cwd) {
|
|
39
|
+
for (const name of CONFIG_FILES) {
|
|
40
|
+
const p = join(cwd, name);
|
|
41
|
+
if (existsSync(p))
|
|
42
|
+
return p;
|
|
43
|
+
}
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
const _cache = new Map();
|
|
47
|
+
// ─── Readers ───────────────────────────────────────────────────────
|
|
48
|
+
/**
|
|
49
|
+
* Lit la config depuis `cwd` (default `process.cwd()`).
|
|
50
|
+
* Retourne `DEFAULT_CONFIG` si aucun fichier trouvé.
|
|
51
|
+
* Cache invalidé par mtime du fichier.
|
|
52
|
+
*/
|
|
53
|
+
export function loadQrConfig(cwd = process.cwd()) {
|
|
54
|
+
const path = findConfigFile(cwd);
|
|
55
|
+
if (!path)
|
|
56
|
+
return DEFAULT_CONFIG;
|
|
57
|
+
const stat = statSync(path);
|
|
58
|
+
const cached = _cache.get(path);
|
|
59
|
+
if (cached && cached.mtimeMs === stat.mtimeMs) {
|
|
60
|
+
return cached.config;
|
|
61
|
+
}
|
|
62
|
+
let parsed;
|
|
63
|
+
try {
|
|
64
|
+
if (path.endsWith('.js')) {
|
|
65
|
+
// require() interop pour .js — sync, pas d'import dynamique
|
|
66
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
67
|
+
const mod = (require)(path);
|
|
68
|
+
parsed = (mod.default ?? mod);
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
const raw = readFileSync(path, 'utf-8');
|
|
72
|
+
parsed = JSON.parse(raw);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch (e) {
|
|
76
|
+
throw new Error(`[qrpanel] failed to parse ${path}: ${e.message}`);
|
|
77
|
+
}
|
|
78
|
+
const merged = {
|
|
79
|
+
default: { ...DEFAULT_CONFIG.default, ...(parsed.default ?? {}) },
|
|
80
|
+
customThemes: parsed.customThemes,
|
|
81
|
+
};
|
|
82
|
+
_cache.set(path, { mtimeMs: stat.mtimeMs, config: merged });
|
|
83
|
+
return merged;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Crée `.qrconfig.json` à `cwd` s'il n'existe pas. Idempotent.
|
|
87
|
+
* Retourne le path écrit (ou path existant si déjà là).
|
|
88
|
+
*
|
|
89
|
+
* @param overrides — valeurs par défaut à patcher dans le fichier généré.
|
|
90
|
+
*/
|
|
91
|
+
export function ensureQrConfig(cwd = process.cwd(), overrides = {}) {
|
|
92
|
+
const existing = findConfigFile(cwd);
|
|
93
|
+
if (existing)
|
|
94
|
+
return existing;
|
|
95
|
+
const path = join(resolve(cwd), '.qrconfig.json');
|
|
96
|
+
const config = {
|
|
97
|
+
default: { ...DEFAULT_CONFIG.default, ...overrides },
|
|
98
|
+
};
|
|
99
|
+
const header = '// @mostajs/qrpanel — édite ce fichier pour piloter la génération QR.\n'
|
|
100
|
+
+ '// Le master toggle "genimage": false bypass tout le pipeline thématique.\n'
|
|
101
|
+
+ '// "theme": "random" tire dans themePool ; figer un thème = "theme": "science" par exemple.\n';
|
|
102
|
+
const body = JSON.stringify(config, null, 2) + '\n';
|
|
103
|
+
// JSON pur (pas de commentaire dans .qrconfig.json) — header en commentaire
|
|
104
|
+
// de fichier .json est invalide, on l'ignore. On garde juste le JSON.
|
|
105
|
+
void header;
|
|
106
|
+
writeFileSync(path, body, { mode: 0o644 });
|
|
107
|
+
return path;
|
|
108
|
+
}
|
|
109
|
+
/** Vide le cache mémoire (utile pour les tests). */
|
|
110
|
+
export function clearConfigCache() {
|
|
111
|
+
_cache.clear();
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export * from './server.js';
|
|
2
2
|
export * from './client.js';
|
|
3
|
+
export { type ThemeKey, type ThemeAsset, THEME_KEYS, listThemes, getTheme, } from './themes.js';
|
|
4
|
+
export { type QrConfig, type QrConfigDefaults, type QrFormat, type QrEcc, DEFAULT_CONFIG, loadQrConfig, ensureQrConfig, clearConfigCache, } from './config.js';
|
|
3
5
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,OAAO,EACL,KAAK,QAAQ,EAAE,KAAK,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,GACjE,MAAM,aAAa,CAAA;AACpB,OAAO,EACL,KAAK,QAAQ,EAAE,KAAK,gBAAgB,EAAE,KAAK,QAAQ,EAAE,KAAK,KAAK,EAC/D,cAAc,EAAE,YAAY,EAAE,cAAc,EAAE,gBAAgB,GAC/D,MAAM,aAAa,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -6,4 +6,6 @@
|
|
|
6
6
|
// éviter d'inclure React côté serveur ou qrcode côté client.
|
|
7
7
|
export * from './server.js';
|
|
8
8
|
export * from './client.js';
|
|
9
|
+
export { THEME_KEYS, listThemes, getTheme, } from './themes.js';
|
|
10
|
+
export { DEFAULT_CONFIG, loadQrConfig, ensureQrConfig, clearConfigCache, } from './config.js';
|
|
9
11
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,4BAA4B;AAC5B,0CAA0C;AAC1C,EAAE;AACF,0EAA0E;AAC1E,sEAAsE;AACtE,6DAA6D;AAE7D,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,4BAA4B;AAC5B,0CAA0C;AAC1C,EAAE;AACF,0EAA0E;AAC1E,sEAAsE;AACtE,6DAA6D;AAE7D,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,OAAO,EAC2B,UAAU,EAAE,UAAU,EAAE,QAAQ,GACjE,MAAM,aAAa,CAAA;AACpB,OAAO,EAEL,cAAc,EAAE,YAAY,EAAE,cAAc,EAAE,gBAAgB,GAC/D,MAAM,aAAa,CAAA"}
|
package/dist/server.d.ts
CHANGED
|
@@ -1,27 +1,80 @@
|
|
|
1
|
+
import type { ThemeKey } from './themes.js';
|
|
1
2
|
export interface QrOptions {
|
|
2
|
-
/** Largeur/hauteur de l'image en pixels. Default 600. */
|
|
3
|
+
/** Largeur/hauteur de l'image en pixels. Default 600 (ou config). */
|
|
3
4
|
width?: number;
|
|
4
|
-
/** Marge blanche autour du QR (en modules). Default 2. */
|
|
5
|
+
/** Marge blanche autour du QR (en modules). Default 2 (ou config). */
|
|
5
6
|
margin?: number;
|
|
6
7
|
/**
|
|
7
|
-
* Niveau de correction d'erreur
|
|
8
|
-
*
|
|
9
|
-
* L = 7 % (default qrcode lib)
|
|
10
|
-
* M = 15 % (recommandé pour usage écran)
|
|
11
|
-
* Q = 25 %
|
|
12
|
-
* H = 30 % (recommandé si logo overlay)
|
|
8
|
+
* Niveau de correction d'erreur :
|
|
9
|
+
* L = 7 % | M = 15 % | Q = 25 % | H = 30 % (recommandé pour overlay)
|
|
13
10
|
*/
|
|
14
11
|
errorCorrectionLevel?: 'L' | 'M' | 'Q' | 'H';
|
|
15
|
-
/** Couleur des modules sombres. Default '#0f172a'
|
|
12
|
+
/** Couleur des modules sombres. Default '#0f172a'. */
|
|
16
13
|
darkColor?: string;
|
|
17
14
|
/** Couleur du fond. Default '#ffffff'. */
|
|
18
15
|
lightColor?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Master toggle. `false` court-circuite tout le pipeline thématique
|
|
18
|
+
* et retombe sur le QR pur (comportement v0.2.x). Override de la
|
|
19
|
+
* valeur config.
|
|
20
|
+
*/
|
|
21
|
+
genimage?: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Thème à appliquer en cadre (4 motifs aux coins). Pris dans le
|
|
24
|
+
* registry natif (12 thèmes). 'random' tire dans `themePool`.
|
|
25
|
+
* 'none' désactive le composite ponctuellement.
|
|
26
|
+
* Objet `{ svg }` → motif custom inline.
|
|
27
|
+
*/
|
|
28
|
+
theme?: ThemeKey | 'random' | 'none' | {
|
|
29
|
+
svg: string;
|
|
30
|
+
label?: string;
|
|
31
|
+
};
|
|
32
|
+
/** Sous-set des thèmes pour le tirage 'random'. Default = tous. */
|
|
33
|
+
themePool?: ThemeKey[];
|
|
34
|
+
/** Marge cadre image / canvas (proportion). Default 0.13. */
|
|
35
|
+
framePadding?: number;
|
|
36
|
+
/** Taille du cartouche blanc central (proportion 0..1). Default 0.62. */
|
|
37
|
+
centerWhiteRatio?: number;
|
|
38
|
+
/** Opacité du cadre image (0..1). Default 1. */
|
|
39
|
+
themeOpacity?: number;
|
|
40
|
+
/** Couleur monochrome du cadre image. Default '#1e293b'. */
|
|
41
|
+
themeColor?: string;
|
|
19
42
|
}
|
|
20
43
|
/** Génère un PNG (Buffer) d'un QR code encodant `text`. */
|
|
21
44
|
export declare function generateQrPng(text: string, opts?: QrOptions): Promise<Buffer>;
|
|
22
45
|
/** Génère un SVG (string) d'un QR code encodant `text`. */
|
|
23
46
|
export declare function generateQrSvg(text: string, opts?: QrOptions): Promise<string>;
|
|
24
|
-
/** Génère un Data URL `data:image/png;base64,...`
|
|
25
|
-
* embarquer directement dans une balise `<img src>` sans endpoint dédié. */
|
|
47
|
+
/** Génère un Data URL `data:image/png;base64,...` */
|
|
26
48
|
export declare function generateQrDataUrl(text: string, opts?: QrOptions): Promise<string>;
|
|
49
|
+
export interface BuildInviteUrlsOptions {
|
|
50
|
+
/** Base absolue (https://app.example.com), sans slash final. */
|
|
51
|
+
baseUrl: string;
|
|
52
|
+
/** Path public direct (ex: '/projet-x'). Sera concaténé à baseUrl. */
|
|
53
|
+
directPath: string;
|
|
54
|
+
/** Secret HMAC pour signer le token invite. */
|
|
55
|
+
inviteSecret: string | Buffer;
|
|
56
|
+
/** Identifiant opaque encodé dans le token (resource id). */
|
|
57
|
+
inviteId: string;
|
|
58
|
+
/** TTL en ms (default 60 jours). */
|
|
59
|
+
ttlMs?: number;
|
|
60
|
+
/** Path du callback invite (default '/invite'). */
|
|
61
|
+
invitePath?: string;
|
|
62
|
+
/** Meta libre encodée dans le token. */
|
|
63
|
+
inviteMeta?: Record<string, string | number | boolean>;
|
|
64
|
+
}
|
|
65
|
+
export interface InviteUrls {
|
|
66
|
+
/** URL absolue mode "direct" (auth standard derrière). */
|
|
67
|
+
directUrl: string;
|
|
68
|
+
/** URL absolue mode "invite" (token HMAC signé). */
|
|
69
|
+
inviteUrl: string;
|
|
70
|
+
/** Le token signé seul (pour storage si besoin). */
|
|
71
|
+
inviteToken: string;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Helper combiné — signe un invite-token et construit la paire d'URLs
|
|
75
|
+
* (direct + invite) prête à passer à `<QrPanel>` côté client.
|
|
76
|
+
*/
|
|
77
|
+
export declare function buildInviteUrls(opts: BuildInviteUrlsOptions): InviteUrls;
|
|
78
|
+
export { listThemes, THEME_KEYS, THEMES, getTheme, type ThemeKey, type ThemeAsset } from './themes.js';
|
|
79
|
+
export { loadQrConfig, ensureQrConfig, DEFAULT_CONFIG, type QrConfig } from './config.js';
|
|
27
80
|
//# sourceMappingURL=server.d.ts.map
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAI3C,MAAM,WAAW,SAAS;IACxB,qEAAqE;IACrE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,sEAAsE;IACtE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;OAGG;IACH,oBAAoB,CAAC,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAA;IAC5C,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,0CAA0C;IAC1C,UAAU,CAAC,EAAE,MAAM,CAAA;IAGnB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,GAAG;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IACtE,mEAAmE;IACnE,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAA;IACtB,6DAA6D;IAC7D,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,yEAAyE;IACzE,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,gDAAgD;IAChD,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,4DAA4D;IAC5D,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAuCD,2DAA2D;AAC3D,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,SAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAgBvF;AAED,2DAA2D;AAC3D,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,SAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAgBvF;AAED,qDAAqD;AACrD,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,SAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAe3F;AAID,MAAM,WAAW,sBAAsB;IACrC,gEAAgE;IAChE,OAAO,EAAE,MAAM,CAAA;IACf,sEAAsE;IACtE,UAAU,EAAE,MAAM,CAAA;IAClB,+CAA+C;IAC/C,YAAY,EAAE,MAAM,GAAG,MAAM,CAAA;IAC7B,6DAA6D;IAC7D,QAAQ,EAAE,MAAM,CAAA;IAChB,oCAAoC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,mDAAmD;IACnD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,wCAAwC;IACxC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAA;CACvD;AAED,MAAM,WAAW,UAAU;IACzB,0DAA0D;IAC1D,SAAS,EAAE,MAAM,CAAA;IACjB,oDAAoD;IACpD,SAAS,EAAE,MAAM,CAAA;IACjB,oDAAoD;IACpD,WAAW,EAAE,MAAM,CAAA;CACpB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,sBAAsB,GAAG,UAAU,CAUxE;AAID,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,QAAQ,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAA;AACtG,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAA"}
|
package/dist/server.js
CHANGED
|
@@ -1,45 +1,130 @@
|
|
|
1
1
|
// @mostajs/qrpanel/server — server-side QR generation (Node)
|
|
2
2
|
// Author: Dr Hamid MADANI <drmdh@msn.com>
|
|
3
3
|
//
|
|
4
|
-
// Wrap minimaliste de la lib `qrcode` (npm)
|
|
5
|
-
//
|
|
4
|
+
// Wrap minimaliste de la lib `qrcode` (npm) + extension thématique
|
|
5
|
+
// pilotée par .qrconfig.json (chargée à process.cwd() au runtime, cache
|
|
6
|
+
// invalidé par mtime).
|
|
7
|
+
//
|
|
8
|
+
// Cross-OS natif (Linux, macOS, Windows) — qrcode est pure-JS, et la
|
|
9
|
+
// rasterization PNG composite passe par @resvg/resvg-js (rust prebuilt
|
|
10
|
+
// binaries cross-OS, pas de chromium ni puppeteer).
|
|
11
|
+
//
|
|
12
|
+
// API legacy 100% rétro-compatible :
|
|
13
|
+
// - generateQrPng/Svg/DataUrl(text, opts) marchent comme en 0.2.x
|
|
14
|
+
// si .qrconfig.json est absent ET opts.genimage non-précisé.
|
|
15
|
+
// - Le master toggle genimage=false (config ou opts) court-circuite
|
|
16
|
+
// toute la chaîne thématique.
|
|
6
17
|
import QRCode from 'qrcode';
|
|
18
|
+
import { signInviteToken } from '@mostajs/auth/lib/invite-token';
|
|
19
|
+
import { loadQrConfig, } from './config.js';
|
|
20
|
+
import { composeThemedSvg, composeThemedPng, composeThemedDataUrl, mergeComposeOpts, } from './composer.js';
|
|
21
|
+
// ─── Internal helpers ──────────────────────────────────────────────
|
|
22
|
+
/**
|
|
23
|
+
* Décide si on bascule sur le pipeline composite ou le legacy.
|
|
24
|
+
* Order : opts.genimage > config.default.genimage. theme='none' force legacy.
|
|
25
|
+
*/
|
|
26
|
+
function shouldComposite(opts, cfg) {
|
|
27
|
+
if (opts.genimage === false)
|
|
28
|
+
return false;
|
|
29
|
+
if (opts.genimage === true) {
|
|
30
|
+
// explicit on : ne respecte pas theme=none ? Si l'app dit explicitement
|
|
31
|
+
// genimage=true mais theme=none, on respecte theme=none (QR pur ponctuel).
|
|
32
|
+
const theme = opts.theme ?? cfg.theme;
|
|
33
|
+
return theme !== 'none';
|
|
34
|
+
}
|
|
35
|
+
// Pas d'override opts → suit la config
|
|
36
|
+
if (cfg.genimage === false)
|
|
37
|
+
return false;
|
|
38
|
+
const theme = opts.theme ?? cfg.theme;
|
|
39
|
+
return theme !== 'none';
|
|
40
|
+
}
|
|
41
|
+
function composeOptsFromQrOptions(opts, cfg) {
|
|
42
|
+
return mergeComposeOpts(cfg, {
|
|
43
|
+
width: opts.width,
|
|
44
|
+
errorCorrectionLevel: opts.errorCorrectionLevel,
|
|
45
|
+
darkColor: opts.darkColor,
|
|
46
|
+
lightColor: opts.lightColor,
|
|
47
|
+
theme: opts.theme,
|
|
48
|
+
themePool: opts.themePool,
|
|
49
|
+
framePadding: opts.framePadding,
|
|
50
|
+
centerWhiteRatio: opts.centerWhiteRatio,
|
|
51
|
+
themeOpacity: opts.themeOpacity,
|
|
52
|
+
themeColor: opts.themeColor,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
// ─── Public API : 3 generators ─────────────────────────────────────
|
|
7
56
|
/** Génère un PNG (Buffer) d'un QR code encodant `text`. */
|
|
8
|
-
export function generateQrPng(text, opts = {}) {
|
|
57
|
+
export async function generateQrPng(text, opts = {}) {
|
|
58
|
+
const cfg = loadQrConfig().default;
|
|
59
|
+
if (shouldComposite(opts, cfg)) {
|
|
60
|
+
const composed = await composeThemedPng(text, composeOptsFromQrOptions(opts, cfg));
|
|
61
|
+
if (composed)
|
|
62
|
+
return composed;
|
|
63
|
+
}
|
|
64
|
+
// Legacy QR pur (qrcode lib direct)
|
|
9
65
|
return QRCode.toBuffer(text, {
|
|
10
|
-
width: opts.width ??
|
|
66
|
+
width: opts.width ?? cfg.width,
|
|
11
67
|
margin: opts.margin ?? 2,
|
|
12
|
-
errorCorrectionLevel: opts.errorCorrectionLevel ??
|
|
68
|
+
errorCorrectionLevel: opts.errorCorrectionLevel ?? cfg.errorCorrectionLevel,
|
|
13
69
|
color: {
|
|
14
|
-
dark: opts.darkColor ??
|
|
15
|
-
light: opts.lightColor ??
|
|
70
|
+
dark: opts.darkColor ?? cfg.darkColor,
|
|
71
|
+
light: opts.lightColor ?? cfg.lightColor,
|
|
16
72
|
},
|
|
17
73
|
});
|
|
18
74
|
}
|
|
19
75
|
/** Génère un SVG (string) d'un QR code encodant `text`. */
|
|
20
|
-
export function generateQrSvg(text, opts = {}) {
|
|
76
|
+
export async function generateQrSvg(text, opts = {}) {
|
|
77
|
+
const cfg = loadQrConfig().default;
|
|
78
|
+
if (shouldComposite(opts, cfg)) {
|
|
79
|
+
const composed = await composeThemedSvg(text, composeOptsFromQrOptions(opts, cfg));
|
|
80
|
+
if (composed)
|
|
81
|
+
return composed;
|
|
82
|
+
}
|
|
21
83
|
return QRCode.toString(text, {
|
|
22
84
|
type: 'svg',
|
|
23
|
-
width: opts.width ??
|
|
85
|
+
width: opts.width ?? cfg.width,
|
|
24
86
|
margin: opts.margin ?? 2,
|
|
25
|
-
errorCorrectionLevel: opts.errorCorrectionLevel ??
|
|
87
|
+
errorCorrectionLevel: opts.errorCorrectionLevel ?? cfg.errorCorrectionLevel,
|
|
26
88
|
color: {
|
|
27
|
-
dark: opts.darkColor ??
|
|
28
|
-
light: opts.lightColor ??
|
|
89
|
+
dark: opts.darkColor ?? cfg.darkColor,
|
|
90
|
+
light: opts.lightColor ?? cfg.lightColor,
|
|
29
91
|
},
|
|
30
92
|
});
|
|
31
93
|
}
|
|
32
|
-
/** Génère un Data URL `data:image/png;base64,...`
|
|
33
|
-
|
|
34
|
-
|
|
94
|
+
/** Génère un Data URL `data:image/png;base64,...` */
|
|
95
|
+
export async function generateQrDataUrl(text, opts = {}) {
|
|
96
|
+
const cfg = loadQrConfig().default;
|
|
97
|
+
if (shouldComposite(opts, cfg)) {
|
|
98
|
+
const composed = await composeThemedDataUrl(text, composeOptsFromQrOptions(opts, cfg));
|
|
99
|
+
if (composed)
|
|
100
|
+
return composed;
|
|
101
|
+
}
|
|
35
102
|
return QRCode.toDataURL(text, {
|
|
36
|
-
width: opts.width ??
|
|
103
|
+
width: opts.width ?? cfg.width,
|
|
37
104
|
margin: opts.margin ?? 2,
|
|
38
|
-
errorCorrectionLevel: opts.errorCorrectionLevel ??
|
|
105
|
+
errorCorrectionLevel: opts.errorCorrectionLevel ?? cfg.errorCorrectionLevel,
|
|
39
106
|
color: {
|
|
40
|
-
dark: opts.darkColor ??
|
|
41
|
-
light: opts.lightColor ??
|
|
107
|
+
dark: opts.darkColor ?? cfg.darkColor,
|
|
108
|
+
light: opts.lightColor ?? cfg.lightColor,
|
|
42
109
|
},
|
|
43
110
|
});
|
|
44
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* Helper combiné — signe un invite-token et construit la paire d'URLs
|
|
114
|
+
* (direct + invite) prête à passer à `<QrPanel>` côté client.
|
|
115
|
+
*/
|
|
116
|
+
export function buildInviteUrls(opts) {
|
|
117
|
+
const base = String(opts.baseUrl).replace(/\/+$/, '');
|
|
118
|
+
const direct = opts.directPath.startsWith('/') ? opts.directPath : '/' + opts.directPath;
|
|
119
|
+
const invitePath = (opts.invitePath ?? '/invite').replace(/\/+$/, '');
|
|
120
|
+
const inviteToken = signInviteToken(opts.inviteSecret, opts.inviteId, opts.ttlMs, opts.inviteMeta);
|
|
121
|
+
return {
|
|
122
|
+
directUrl: base + direct,
|
|
123
|
+
inviteUrl: `${base}${invitePath}/${inviteToken}`,
|
|
124
|
+
inviteToken,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
// ─── Re-exports utiles côté serveur ────────────────────────────────
|
|
128
|
+
export { listThemes, THEME_KEYS, THEMES, getTheme } from './themes.js';
|
|
129
|
+
export { loadQrConfig, ensureQrConfig, DEFAULT_CONFIG } from './config.js';
|
|
45
130
|
//# sourceMappingURL=server.js.map
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAC7D,0CAA0C;AAC1C,EAAE;AACF,oEAAoE;AACpE,
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAC7D,0CAA0C;AAC1C,EAAE;AACF,mEAAmE;AACnE,wEAAwE;AACxE,uBAAuB;AACvB,EAAE;AACF,qEAAqE;AACrE,uEAAuE;AACvE,oDAAoD;AACpD,EAAE;AACF,qCAAqC;AACrC,oEAAoE;AACpE,iEAAiE;AACjE,sEAAsE;AACtE,kCAAkC;AAElC,OAAO,MAAM,MAAM,QAAQ,CAAA;AAC3B,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAA;AAChE,OAAO,EACL,YAAY,GACb,MAAM,aAAa,CAAA;AACpB,OAAO,EACL,gBAAgB,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,gBAAgB,GAE3E,MAAM,eAAe,CAAA;AA8CtB,sEAAsE;AAEtE;;;GAGG;AACH,SAAS,eAAe,CAAC,IAAe,EAAE,GAAqB;IAC7D,IAAI,IAAI,CAAC,QAAQ,KAAK,KAAK;QAAE,OAAO,KAAK,CAAA;IACzC,IAAI,IAAI,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;QAC3B,wEAAwE;QACxE,2EAA2E;QAC3E,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,GAAG,CAAC,KAAK,CAAA;QACrC,OAAO,KAAK,KAAK,MAAM,CAAA;IACzB,CAAC;IACD,uCAAuC;IACvC,IAAI,GAAG,CAAC,QAAQ,KAAK,KAAK;QAAE,OAAO,KAAK,CAAA;IACxC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,GAAG,CAAC,KAAK,CAAA;IACrC,OAAO,KAAK,KAAK,MAAM,CAAA;AACzB,CAAC;AAED,SAAS,wBAAwB,CAAC,IAAe,EAAE,GAAqB;IACtE,OAAO,gBAAgB,CAAC,GAAG,EAAE;QAC3B,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,oBAAoB,EAAE,IAAI,CAAC,oBAAoB;QAC/C,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,YAAY,EAAE,IAAI,CAAC,YAAY;QAC/B,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;QACvC,YAAY,EAAE,IAAI,CAAC,YAAY;QAC/B,UAAU,EAAE,IAAI,CAAC,UAAU;KAC5B,CAAC,CAAA;AACJ,CAAC;AAED,sEAAsE;AAEtE,2DAA2D;AAC3D,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAY,EAAE,OAAkB,EAAE;IACpE,MAAM,GAAG,GAAG,YAAY,EAAE,CAAC,OAAO,CAAA;IAClC,IAAI,eAAe,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;QAC/B,MAAM,QAAQ,GAAG,MAAM,gBAAgB,CAAC,IAAI,EAAE,wBAAwB,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAA;QAClF,IAAI,QAAQ;YAAE,OAAO,QAAQ,CAAA;IAC/B,CAAC;IACD,oCAAoC;IACpC,OAAO,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE;QAC3B,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,GAAG,CAAC,KAAK;QAC9B,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,CAAC;QACxB,oBAAoB,EAAE,IAAI,CAAC,oBAAoB,IAAI,GAAG,CAAC,oBAAoB;QAC3E,KAAK,EAAE;YACL,IAAI,EAAE,IAAI,CAAC,SAAS,IAAI,GAAG,CAAC,SAAS;YACrC,KAAK,EAAE,IAAI,CAAC,UAAU,IAAI,GAAG,CAAC,UAAU;SACzC;KACF,CAAC,CAAA;AACJ,CAAC;AAED,2DAA2D;AAC3D,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAY,EAAE,OAAkB,EAAE;IACpE,MAAM,GAAG,GAAG,YAAY,EAAE,CAAC,OAAO,CAAA;IAClC,IAAI,eAAe,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;QAC/B,MAAM,QAAQ,GAAG,MAAM,gBAAgB,CAAC,IAAI,EAAE,wBAAwB,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAA;QAClF,IAAI,QAAQ;YAAE,OAAO,QAAQ,CAAA;IAC/B,CAAC;IACD,OAAO,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE;QAC3B,IAAI,EAAE,KAAK;QACX,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,GAAG,CAAC,KAAK;QAC9B,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,CAAC;QACxB,oBAAoB,EAAE,IAAI,CAAC,oBAAoB,IAAI,GAAG,CAAC,oBAAoB;QAC3E,KAAK,EAAE;YACL,IAAI,EAAE,IAAI,CAAC,SAAS,IAAI,GAAG,CAAC,SAAS;YACrC,KAAK,EAAE,IAAI,CAAC,UAAU,IAAI,GAAG,CAAC,UAAU;SACzC;KACF,CAAC,CAAA;AACJ,CAAC;AAED,qDAAqD;AACrD,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,IAAY,EAAE,OAAkB,EAAE;IACxE,MAAM,GAAG,GAAG,YAAY,EAAE,CAAC,OAAO,CAAA;IAClC,IAAI,eAAe,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;QAC/B,MAAM,QAAQ,GAAG,MAAM,oBAAoB,CAAC,IAAI,EAAE,wBAAwB,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAA;QACtF,IAAI,QAAQ;YAAE,OAAO,QAAQ,CAAA;IAC/B,CAAC;IACD,OAAO,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE;QAC5B,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,GAAG,CAAC,KAAK;QAC9B,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,CAAC;QACxB,oBAAoB,EAAE,IAAI,CAAC,oBAAoB,IAAI,GAAG,CAAC,oBAAoB;QAC3E,KAAK,EAAE;YACL,IAAI,EAAE,IAAI,CAAC,SAAS,IAAI,GAAG,CAAC,SAAS;YACrC,KAAK,EAAE,IAAI,CAAC,UAAU,IAAI,GAAG,CAAC,UAAU;SACzC;KACF,CAAC,CAAA;AACJ,CAAC;AA8BD;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,IAA4B;IAC1D,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;IACrD,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,GAAG,IAAI,CAAC,UAAU,CAAA;IACxF,MAAM,UAAU,GAAG,CAAC,IAAI,CAAC,UAAU,IAAI,SAAS,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;IACrE,MAAM,WAAW,GAAG,eAAe,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,CAAC,CAAA;IAClG,OAAO;QACL,SAAS,EAAE,IAAI,GAAG,MAAM;QACxB,SAAS,EAAE,GAAG,IAAI,GAAG,UAAU,IAAI,WAAW,EAAE;QAChD,WAAW;KACZ,CAAA;AACH,CAAC;AAED,sEAAsE;AAEtE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAkC,MAAM,aAAa,CAAA;AACtG,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,cAAc,EAAiB,MAAM,aAAa,CAAA"}
|
package/dist/themes.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export type ThemeKey = 'baby' | 'animals' | 'science' | 'physics' | 'chemistry' | 'math' | 'nature' | 'tech' | 'space' | 'music' | 'book' | 'health';
|
|
2
|
+
export interface ThemeAsset {
|
|
3
|
+
key: ThemeKey;
|
|
4
|
+
label: string;
|
|
5
|
+
/** Fragment SVG (sans <svg> wrapper) — un motif centré sur (0,0). */
|
|
6
|
+
motif: string;
|
|
7
|
+
}
|
|
8
|
+
export declare const THEMES: Record<ThemeKey, ThemeAsset>;
|
|
9
|
+
/** Liste des clés thèmes natifs (ordre fixe pour itération déterministe). */
|
|
10
|
+
export declare const THEME_KEYS: ThemeKey[];
|
|
11
|
+
/** Renvoie la liste des clés thèmes natifs. */
|
|
12
|
+
export declare function listThemes(): ThemeKey[];
|
|
13
|
+
/** Récupère un thème par clé. Lance si inconnu. */
|
|
14
|
+
export declare function getTheme(key: ThemeKey): ThemeAsset;
|
|
15
|
+
/**
|
|
16
|
+
* Tire un thème au hasard dans le pool fourni (ou tous les thèmes si pool absent).
|
|
17
|
+
* Utilise Math.random() — pas de seed (déterminisme = à la charge du caller s'il
|
|
18
|
+
* en a besoin).
|
|
19
|
+
*/
|
|
20
|
+
export declare function pickRandomTheme(pool?: ThemeKey[]): ThemeKey;
|
|
21
|
+
/**
|
|
22
|
+
* Construit le fragment SVG du cadre thématique :
|
|
23
|
+
* 4 instances du motif aux 4 coins, monochrome via CSS color, opacité.
|
|
24
|
+
*
|
|
25
|
+
* Le fragment est destiné à être inséré dans un SVG composite global
|
|
26
|
+
* (viewBox 0 0 width width). Coordonnées dans l'espace [0..width].
|
|
27
|
+
*/
|
|
28
|
+
export interface BuildFrameOpts {
|
|
29
|
+
/** Largeur du canvas SVG englobant en unités utilisateur. */
|
|
30
|
+
width: number;
|
|
31
|
+
/** Couleur monochrome appliquée via CSS. Default '#1e293b'. */
|
|
32
|
+
color?: string;
|
|
33
|
+
/** Opacité 0..1. Default 1. */
|
|
34
|
+
opacity?: number;
|
|
35
|
+
/** Marge en proportion du width (où placer les motifs aux coins). Default 0.13. */
|
|
36
|
+
framePadding?: number;
|
|
37
|
+
/** Échelle du motif (base 1 = motif d'envergure ~14 unités locales). Default auto-scale par width. */
|
|
38
|
+
motifScale?: number;
|
|
39
|
+
}
|
|
40
|
+
export declare function buildThemeFrameSvg(theme: ThemeAsset, opts: BuildFrameOpts): string;
|
|
41
|
+
//# sourceMappingURL=themes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"themes.d.ts","sourceRoot":"","sources":["../src/themes.ts"],"names":[],"mappings":"AAWA,MAAM,MAAM,QAAQ,GAChB,MAAM,GACN,SAAS,GACT,SAAS,GACT,SAAS,GACT,WAAW,GACX,MAAM,GACN,QAAQ,GACR,MAAM,GACN,OAAO,GACP,OAAO,GACP,MAAM,GACN,QAAQ,CAAA;AAEZ,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,QAAQ,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,qEAAqE;IACrE,KAAK,EAAE,MAAM,CAAA;CACd;AAMD,eAAO,MAAM,MAAM,EAAE,MAAM,CAAC,QAAQ,EAAE,UAAU,CA4I/C,CAAA;AAED,6EAA6E;AAC7E,eAAO,MAAM,UAAU,EAAE,QAAQ,EAGhC,CAAA;AAED,+CAA+C;AAC/C,wBAAgB,UAAU,IAAI,QAAQ,EAAE,CAEvC;AAED,mDAAmD;AACnD,wBAAgB,QAAQ,CAAC,GAAG,EAAE,QAAQ,GAAG,UAAU,CAIlD;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,GAAG,QAAQ,CAG3D;AAED;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC7B,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAA;IACb,+DAA+D;IAC/D,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,+BAA+B;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,mFAAmF;IACnF,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,sGAAsG;IACtG,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,cAAc,GAAG,MAAM,CAqBlF"}
|
package/dist/themes.js
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
// @mostajs/qrpanel/themes — built-in theme registry
|
|
2
|
+
// Author: Dr Hamid MADANI <drmdh@msn.com>
|
|
3
|
+
//
|
|
4
|
+
// Chaque thème = un petit motif SVG répliqué aux 4 coins du composite,
|
|
5
|
+
// laissant le centre libre pour le cartouche blanc + QR. Style ligne
|
|
6
|
+
// minimaliste mono-color via `currentColor` (le composer pilote la
|
|
7
|
+
// couleur via CSS sur le <g> wrapper).
|
|
8
|
+
//
|
|
9
|
+
// Création maison — design original, libre de droits.
|
|
10
|
+
// viewBox local du motif : centré sur l'origine, ~14 unités d'envergure.
|
|
11
|
+
/** Communs à tous les motifs (style ligne fine et propre). */
|
|
12
|
+
const STROKE = 'stroke="currentColor" stroke-width="2.4" fill="none" stroke-linecap="round" stroke-linejoin="round"';
|
|
13
|
+
const FILL = 'fill="currentColor"';
|
|
14
|
+
export const THEMES = {
|
|
15
|
+
// ─── 1. baby — sucette + cœur ─────────────────────────────────
|
|
16
|
+
baby: {
|
|
17
|
+
key: 'baby', label: 'Bébé',
|
|
18
|
+
motif: `
|
|
19
|
+
<circle cx="-2" cy="-3" r="4.5" ${STROKE}/>
|
|
20
|
+
<line x1="-2" y1="1.5" x2="-2" y2="6" ${STROKE}/>
|
|
21
|
+
<path d="M 5 -2 c 0.8 -1.2 2.5 -1.2 2.5 0.4 c 0 1.4 -2.5 2.8 -2.5 2.8 c 0 0 -2.5 -1.4 -2.5 -2.8 c 0 -1.6 1.7 -1.6 2.5 -0.4 z" ${FILL} opacity="0.9"/>
|
|
22
|
+
`,
|
|
23
|
+
},
|
|
24
|
+
// ─── 2. animals — empreinte de patte (4 doigts + pad) ─────────
|
|
25
|
+
animals: {
|
|
26
|
+
key: 'animals', label: 'Animaux',
|
|
27
|
+
motif: `
|
|
28
|
+
<ellipse cx="-3.5" cy="-3" rx="1.3" ry="1.8" ${FILL}/>
|
|
29
|
+
<ellipse cx="-1" cy="-5" rx="1.2" ry="1.7" ${FILL}/>
|
|
30
|
+
<ellipse cx="1.8" cy="-5" rx="1.2" ry="1.7" ${FILL}/>
|
|
31
|
+
<ellipse cx="4.3" cy="-3" rx="1.3" ry="1.8" ${FILL}/>
|
|
32
|
+
<ellipse cx="0.5" cy="2" rx="3.5" ry="3.2" ${FILL}/>
|
|
33
|
+
`,
|
|
34
|
+
},
|
|
35
|
+
// ─── 3. science — éprouvette + bulles ─────────────────────────
|
|
36
|
+
science: {
|
|
37
|
+
key: 'science', label: 'Science',
|
|
38
|
+
motif: `
|
|
39
|
+
<path d="M -2 -6 L -2 4 a 2.5 2.5 0 0 0 5 0 L 3 -6" ${STROKE}/>
|
|
40
|
+
<line x1="-3" y1="-6" x2="4" y2="-6" ${STROKE}/>
|
|
41
|
+
<circle cx="-0.5" cy="2" r="0.7" ${FILL}/>
|
|
42
|
+
<circle cx="1.4" cy="0" r="0.5" ${FILL}/>
|
|
43
|
+
<circle cx="0.4" cy="-2" r="0.4" ${FILL}/>
|
|
44
|
+
`,
|
|
45
|
+
},
|
|
46
|
+
// ─── 4. physics — atome (noyau + 2 orbites croisées) ──────────
|
|
47
|
+
physics: {
|
|
48
|
+
key: 'physics', label: 'Physique',
|
|
49
|
+
motif: `
|
|
50
|
+
<ellipse cx="0" cy="0" rx="6" ry="2.4" ${STROKE}/>
|
|
51
|
+
<ellipse cx="0" cy="0" rx="6" ry="2.4" transform="rotate(60)" ${STROKE}/>
|
|
52
|
+
<ellipse cx="0" cy="0" rx="6" ry="2.4" transform="rotate(-60)" ${STROKE}/>
|
|
53
|
+
<circle cx="0" cy="0" r="1.4" ${FILL}/>
|
|
54
|
+
`,
|
|
55
|
+
},
|
|
56
|
+
// ─── 5. chemistry — bécher avec graduation et liquide ─────────
|
|
57
|
+
chemistry: {
|
|
58
|
+
key: 'chemistry', label: 'Chimie',
|
|
59
|
+
motif: `
|
|
60
|
+
<path d="M -3.5 -5 L -3.5 4 a 1.5 1.5 0 0 0 1.5 1.5 L 2 5.5 a 1.5 1.5 0 0 0 1.5 -1.5 L 3.5 -5" ${STROKE}/>
|
|
61
|
+
<line x1="-4" y1="-5" x2="4" y2="-5" ${STROKE}/>
|
|
62
|
+
<line x1="-2.7" y1="-2.5" x2="-1.4" y2="-2.5" ${STROKE}/>
|
|
63
|
+
<line x1="-2.7" y1="0" x2="-1.4" y2="0" ${STROKE}/>
|
|
64
|
+
<path d="M -3.5 1.5 L 3.5 1.5 L 3.5 4 a 1.5 1.5 0 0 1 -1.5 1.5 L -2 5.5 a 1.5 1.5 0 0 1 -1.5 -1.5 z" ${FILL} opacity="0.35"/>
|
|
65
|
+
`,
|
|
66
|
+
},
|
|
67
|
+
// ─── 6. math — symbole π et signe = ───────────────────────────
|
|
68
|
+
math: {
|
|
69
|
+
key: 'math', label: 'Mathématique',
|
|
70
|
+
motif: `
|
|
71
|
+
<line x1="-5" y1="-3.5" x2="5" y2="-3.5" ${STROKE}/>
|
|
72
|
+
<line x1="-2.5" y1="-3.5" x2="-2.5" y2="3.5" ${STROKE}/>
|
|
73
|
+
<line x1="2.5" y1="-3.5" x2="2.5" y2="3.5" ${STROKE}/>
|
|
74
|
+
<line x1="-5" y1="5.5" x2="5" y2="5.5" ${STROKE}/>
|
|
75
|
+
<line x1="-5" y1="7.2" x2="5" y2="7.2" ${STROKE}/>
|
|
76
|
+
`,
|
|
77
|
+
},
|
|
78
|
+
// ─── 7. nature — feuille + tige ───────────────────────────────
|
|
79
|
+
nature: {
|
|
80
|
+
key: 'nature', label: 'Nature',
|
|
81
|
+
motif: `
|
|
82
|
+
<path d="M 0 6 C -6 4 -6 -4 0 -6 C 6 -4 6 4 0 6 Z" ${STROKE}/>
|
|
83
|
+
<line x1="0" y1="-6" x2="0" y2="6" ${STROKE}/>
|
|
84
|
+
<line x1="0" y1="-3" x2="-3" y2="-1" ${STROKE}/>
|
|
85
|
+
<line x1="0" y1="0" x2="-3.5" y2="2" ${STROKE}/>
|
|
86
|
+
<line x1="0" y1="-3" x2="3" y2="-1" ${STROKE}/>
|
|
87
|
+
<line x1="0" y1="0" x2="3.5" y2="2" ${STROKE}/>
|
|
88
|
+
`,
|
|
89
|
+
},
|
|
90
|
+
// ─── 8. tech — engrenage 8 dents ──────────────────────────────
|
|
91
|
+
tech: {
|
|
92
|
+
key: 'tech', label: 'Tech',
|
|
93
|
+
motif: `
|
|
94
|
+
<path d="
|
|
95
|
+
M -1 -5.6 L 1 -5.6 L 1.4 -4 L 3 -3.4 L 4.2 -4.5 L 5.5 -3.2 L 4.4 -2 L 5 -0.4 L 6.6 0 L 6.6 2
|
|
96
|
+
L 5 2.4 L 4.4 4 L 5.5 5.2 L 4.2 6.5 L 3 5.4 L 1.4 6 L 1 7.6 L -1 7.6 L -1.4 6 L -3 5.4
|
|
97
|
+
L -4.2 6.5 L -5.5 5.2 L -4.4 4 L -5 2.4 L -6.6 2 L -6.6 0 L -5 -0.4 L -4.4 -2 L -5.5 -3.2
|
|
98
|
+
L -4.2 -4.5 L -3 -3.4 L -1.4 -4 Z" transform="translate(0 -1) scale(0.7)" ${STROKE}/>
|
|
99
|
+
<circle cx="0" cy="-1" r="1.5" transform="scale(0.7)" ${STROKE}/>
|
|
100
|
+
`,
|
|
101
|
+
},
|
|
102
|
+
// ─── 9. space — planète saturne + étoile ──────────────────────
|
|
103
|
+
space: {
|
|
104
|
+
key: 'space', label: 'Espace',
|
|
105
|
+
motif: `
|
|
106
|
+
<circle cx="-1" cy="0" r="3.2" ${STROKE}/>
|
|
107
|
+
<ellipse cx="-1" cy="0" rx="6" ry="1.4" transform="rotate(-20 -1 0)" ${STROKE}/>
|
|
108
|
+
<path d="M 5 -5 L 5.6 -3.5 L 7.2 -3.5 L 6 -2.6 L 6.5 -1.1 L 5 -2 L 3.5 -1.1 L 4 -2.6 L 2.8 -3.5 L 4.4 -3.5 Z" ${FILL}/>
|
|
109
|
+
`,
|
|
110
|
+
},
|
|
111
|
+
// ─── 10. music — note + portée ────────────────────────────────
|
|
112
|
+
music: {
|
|
113
|
+
key: 'music', label: 'Musique',
|
|
114
|
+
motif: `
|
|
115
|
+
<ellipse cx="-2" cy="4" rx="2.2" ry="1.7" transform="rotate(-22 -2 4)" ${FILL}/>
|
|
116
|
+
<line x1="0" y1="3" x2="0" y2="-6" ${STROKE}/>
|
|
117
|
+
<path d="M 0 -6 C 3 -5 4 -2 1.5 -1" ${STROKE}/>
|
|
118
|
+
<ellipse cx="3.5" cy="2.5" rx="1.7" ry="1.3" transform="rotate(-22 3.5 2.5)" ${FILL} opacity="0.6"/>
|
|
119
|
+
<line x1="5" y1="1.5" x2="5" y2="-4" ${STROKE} opacity="0.6"/>
|
|
120
|
+
`,
|
|
121
|
+
},
|
|
122
|
+
// ─── 11. book — livre ouvert ──────────────────────────────────
|
|
123
|
+
book: {
|
|
124
|
+
key: 'book', label: 'Livre',
|
|
125
|
+
motif: `
|
|
126
|
+
<path d="M -6 -4 L 0 -3 L 6 -4 L 6 4 L 0 5 L -6 4 Z" ${STROKE}/>
|
|
127
|
+
<line x1="0" y1="-3" x2="0" y2="5" ${STROKE}/>
|
|
128
|
+
<line x1="-4.5" y1="-2" x2="-1.5" y2="-1.5" ${STROKE} opacity="0.6"/>
|
|
129
|
+
<line x1="-4.5" y1="0" x2="-1.5" y2="0.5" ${STROKE} opacity="0.6"/>
|
|
130
|
+
<line x1="1.5" y1="-1.5" x2="4.5" y2="-2" ${STROKE} opacity="0.6"/>
|
|
131
|
+
<line x1="1.5" y1="0.5" x2="4.5" y2="0" ${STROKE} opacity="0.6"/>
|
|
132
|
+
`,
|
|
133
|
+
},
|
|
134
|
+
// ─── 12. health — croix médicale + battement ──────────────────
|
|
135
|
+
health: {
|
|
136
|
+
key: 'health', label: 'Santé',
|
|
137
|
+
motif: `
|
|
138
|
+
<rect x="-1.8" y="-6" width="3.6" height="12" rx="0.8" ${FILL}/>
|
|
139
|
+
<rect x="-6" y="-1.8" width="12" height="3.6" rx="0.8" ${FILL}/>
|
|
140
|
+
<path d="M -8 7 L -5 7 L -3.5 4 L -1.5 9 L 0 6 L 2 8 L 4 7 L 8 7" stroke="currentColor" stroke-width="1.5" fill="none" stroke-linecap="round" stroke-linejoin="round" transform="translate(0 2)"/>
|
|
141
|
+
`,
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
/** Liste des clés thèmes natifs (ordre fixe pour itération déterministe). */
|
|
145
|
+
export const THEME_KEYS = [
|
|
146
|
+
'baby', 'animals', 'science', 'physics', 'chemistry', 'math',
|
|
147
|
+
'nature', 'tech', 'space', 'music', 'book', 'health',
|
|
148
|
+
];
|
|
149
|
+
/** Renvoie la liste des clés thèmes natifs. */
|
|
150
|
+
export function listThemes() {
|
|
151
|
+
return [...THEME_KEYS];
|
|
152
|
+
}
|
|
153
|
+
/** Récupère un thème par clé. Lance si inconnu. */
|
|
154
|
+
export function getTheme(key) {
|
|
155
|
+
const theme = THEMES[key];
|
|
156
|
+
if (!theme)
|
|
157
|
+
throw new Error(`[qrpanel] unknown theme: "${key}". Available: ${THEME_KEYS.join(', ')}`);
|
|
158
|
+
return theme;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Tire un thème au hasard dans le pool fourni (ou tous les thèmes si pool absent).
|
|
162
|
+
* Utilise Math.random() — pas de seed (déterminisme = à la charge du caller s'il
|
|
163
|
+
* en a besoin).
|
|
164
|
+
*/
|
|
165
|
+
export function pickRandomTheme(pool) {
|
|
166
|
+
const candidates = pool && pool.length > 0 ? pool : THEME_KEYS;
|
|
167
|
+
return candidates[Math.floor(Math.random() * candidates.length)];
|
|
168
|
+
}
|
|
169
|
+
export function buildThemeFrameSvg(theme, opts) {
|
|
170
|
+
const w = opts.width;
|
|
171
|
+
const pad = (opts.framePadding ?? 0.13) * w;
|
|
172
|
+
const color = opts.color ?? '#1e293b';
|
|
173
|
+
const opacity = opts.opacity ?? 1;
|
|
174
|
+
// Auto-scale : à w=600 et padding 13% → motif dimensionné pour rester
|
|
175
|
+
// dans le cadre 0..pad (~78px). Motif local ~14 unités → scale ~5.5.
|
|
176
|
+
const scale = opts.motifScale ?? (pad * 0.45);
|
|
177
|
+
const corners = [
|
|
178
|
+
[pad, pad], // top-left
|
|
179
|
+
[w - pad, pad], // top-right
|
|
180
|
+
[pad, w - pad], // bottom-left
|
|
181
|
+
[w - pad, w - pad], // bottom-right
|
|
182
|
+
];
|
|
183
|
+
const groups = corners.map(([cx, cy]) => `<g transform="translate(${cx} ${cy}) scale(${scale})" style="color:${color};opacity:${opacity}">${theme.motif}</g>`).join('');
|
|
184
|
+
return groups;
|
|
185
|
+
}
|
|
186
|
+
//# sourceMappingURL=themes.js.map
|