@mostajs/qrpanel 0.2.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 +36 -21
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +88 -30
- 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 +22 -5
package/README.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# @mostajs/qrpanel
|
|
2
2
|
|
|
3
|
-
QR code panel — générateur server-side *(PNG / SVG / data URL via la lib `qrcode`)*
|
|
3
|
+
QR code panel — générateur server-side *(PNG / SVG / data URL via la lib `qrcode`)* avec **12 thèmes natifs** *(image-as-frame composite, ECC=H par défaut)*, piloté par un fichier de config `.qrconfig.json` éditable. Composant React `<QrPanel>` *(QR + lien hypertexte copiable + actions copier / ouvrir / mailto)*.
|
|
4
4
|
|
|
5
5
|
**Auteur** : Dr Hamid MADANI <drmdh@msn.com>
|
|
6
6
|
|
|
7
7
|
## Cross-OS
|
|
8
8
|
|
|
9
|
-
Aucune dépendance
|
|
9
|
+
Aucune dépendance Chromium / Puppeteer / node-gyp. Pure-JS pour `qrcode`, prebuilt-binaries Rust pour le rasterizer (`@resvg/resvg-js`). Fonctionne identiquement sur **Linux, macOS, Windows** *(Node ≥18)*.
|
|
10
10
|
|
|
11
11
|
## Installation
|
|
12
12
|
|
|
@@ -14,23 +14,102 @@ Aucune dépendance native. La lib `qrcode` est pure JS — fonctionne identiquem
|
|
|
14
14
|
npm install @mostajs/qrpanel
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
##
|
|
17
|
+
## Quickstart
|
|
18
18
|
|
|
19
19
|
```ts
|
|
20
|
-
import { generateQrPng,
|
|
20
|
+
import { generateQrPng, ensureQrConfig } from '@mostajs/qrpanel/server'
|
|
21
|
+
|
|
22
|
+
// (Optionnel) écrire .qrconfig.json à la racine — lifecycle init / boot.
|
|
23
|
+
ensureQrConfig()
|
|
24
|
+
|
|
25
|
+
// Génère un PNG composite (cadre thème "random" tiré dans themePool).
|
|
26
|
+
const png = await generateQrPng('https://example.com/invite/abc')
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## v0.3 — thèmes & config
|
|
30
|
+
|
|
31
|
+
Le module embarque **12 thèmes vectoriels** *(SVG inline mono-color, ~2-3 Ko chacun, libres de droits — création maison)*, répliqués aux 4 coins d'un cadre décoratif. Le QR reste centré sur un cartouche blanc plein, ECC=H par défaut → scannable même avec cadre coloré.
|
|
32
|
+
|
|
33
|
+
### Thèmes natifs
|
|
34
|
+
|
|
35
|
+
| Clé | Label | Clé | Label |
|
|
36
|
+
|---|---|---|---|
|
|
37
|
+
| `baby` | Bébé | `nature` | Nature |
|
|
38
|
+
| `animals` | Animaux | `tech` | Tech |
|
|
39
|
+
| `science` | Science | `space` | Espace |
|
|
40
|
+
| `physics` | Physique | `music` | Musique |
|
|
41
|
+
| `chemistry` | Chimie | `book` | Livre |
|
|
42
|
+
| `math` | Mathématique | `health` | Santé |
|
|
43
|
+
|
|
44
|
+
### Fichier `.qrconfig.json`
|
|
45
|
+
|
|
46
|
+
Généré à `process.cwd()` via `ensureQrConfig()` ou `npx qrpanel init`. Pilote tous les générateurs au runtime *(cache invalidé par `mtime` — édite le fichier, le prochain appel le relit)*.
|
|
47
|
+
|
|
48
|
+
```jsonc
|
|
49
|
+
{
|
|
50
|
+
"default": {
|
|
51
|
+
"genimage": true, // ← master toggle (false = QR pur, bypass total)
|
|
52
|
+
"format": "svg",
|
|
53
|
+
"width": 600,
|
|
54
|
+
"margin": 2,
|
|
55
|
+
"errorCorrectionLevel": "H", // overlay-safe (30% redondance)
|
|
56
|
+
"darkColor": "#0f172a",
|
|
57
|
+
"lightColor": "#ffffff",
|
|
58
|
+
"theme": "random", // 'random' | clé thème | 'none'
|
|
59
|
+
"themePool": [
|
|
60
|
+
"baby", "animals", "science", "physics",
|
|
61
|
+
"chemistry", "math", "nature", "tech",
|
|
62
|
+
"space", "music", "book", "health"
|
|
63
|
+
],
|
|
64
|
+
"framePadding": 0.13,
|
|
65
|
+
"centerWhiteRatio": 0.62,
|
|
66
|
+
"themeOpacity": 1.0,
|
|
67
|
+
"themeColor": "#1e293b"
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Cascade de résolution
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
defaults compilés < .qrconfig.json (cwd) < options passées à l'appel
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Master toggle `genimage`
|
|
79
|
+
|
|
80
|
+
```jsonc
|
|
81
|
+
{ "default": { "genimage": false } } // OU :
|
|
82
|
+
generateQrPng(url, { genimage: false }) // override ponctuel
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
→ court-circuite tout le pipeline composite, retombe sur le **QR pur** *(comportement v0.2.x identique)*. Utile en debug / CI / mode "print neutre".
|
|
86
|
+
|
|
87
|
+
## Usage côté serveur
|
|
21
88
|
|
|
22
|
-
|
|
23
|
-
|
|
89
|
+
```ts
|
|
90
|
+
import {
|
|
91
|
+
generateQrPng, generateQrSvg, generateQrDataUrl,
|
|
92
|
+
listThemes, ensureQrConfig,
|
|
93
|
+
} from '@mostajs/qrpanel/server'
|
|
94
|
+
|
|
95
|
+
// Buffer PNG composite (cadre thème science)
|
|
96
|
+
const png = await generateQrPng('https://example.com/x', {
|
|
97
|
+
theme: 'science',
|
|
24
98
|
width: 600,
|
|
25
|
-
|
|
26
|
-
|
|
99
|
+
themeColor: '#0ea5e9',
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
// SVG composite avec random tiré dans un sous-set
|
|
103
|
+
const svg = await generateQrSvg('https://example.com/x', {
|
|
104
|
+
theme: 'random',
|
|
105
|
+
themePool: ['math', 'physics', 'chemistry'],
|
|
27
106
|
})
|
|
28
107
|
|
|
29
|
-
//
|
|
30
|
-
const
|
|
108
|
+
// DataURL PNG composite (image inline)
|
|
109
|
+
const dataUrl = await generateQrDataUrl('https://example.com/x')
|
|
31
110
|
|
|
32
|
-
//
|
|
33
|
-
const
|
|
111
|
+
// Override ponctuel — désactive le composite ne serait-ce qu'une fois
|
|
112
|
+
const plain = await generateQrPng('https://example.com/x', { genimage: false })
|
|
34
113
|
```
|
|
35
114
|
|
|
36
115
|
### Endpoint Next.js (App Router)
|
|
@@ -44,13 +123,23 @@ export const runtime = 'nodejs'
|
|
|
44
123
|
|
|
45
124
|
export async function GET(req: Request) {
|
|
46
125
|
const url = new URL(req.url).searchParams.get('text') ?? ''
|
|
47
|
-
const
|
|
126
|
+
const theme = (new URL(req.url).searchParams.get('theme') ?? 'random') as any
|
|
127
|
+
const png = await generateQrPng(url, { theme })
|
|
48
128
|
return new NextResponse(png as any, {
|
|
49
129
|
headers: { 'Content-Type': 'image/png' },
|
|
50
130
|
})
|
|
51
131
|
}
|
|
52
132
|
```
|
|
53
133
|
|
|
134
|
+
### Thème custom inline
|
|
135
|
+
|
|
136
|
+
```ts
|
|
137
|
+
const png = await generateQrPng(url, {
|
|
138
|
+
theme: { svg: '<circle cx="0" cy="0" r="5" fill="currentColor"/>', label: 'My logo' },
|
|
139
|
+
themeColor: '#ff0000',
|
|
140
|
+
})
|
|
141
|
+
```
|
|
142
|
+
|
|
54
143
|
## Usage côté client (React)
|
|
55
144
|
|
|
56
145
|
```tsx
|
|
@@ -62,14 +151,14 @@ import { QrPanel } from '@mostajs/qrpanel/client'
|
|
|
62
151
|
key: 'direct',
|
|
63
152
|
label: 'Direct',
|
|
64
153
|
url: 'https://app.example.com/projet-x',
|
|
65
|
-
qrSrc: '/api/qr?text=' + encodeURIComponent('https://app.example.com/projet-x'),
|
|
154
|
+
qrSrc: '/api/qr?text=' + encodeURIComponent('https://app.example.com/projet-x') + '&theme=science',
|
|
66
155
|
description: 'Le QR mène directement au questionnaire.',
|
|
67
156
|
},
|
|
68
157
|
{
|
|
69
158
|
key: 'invite',
|
|
70
159
|
label: 'Invite token',
|
|
71
160
|
url: 'https://app.example.com/invite/eyJ...',
|
|
72
|
-
qrSrc: '/api/qr?text='
|
|
161
|
+
qrSrc: '/api/qr?text=...&theme=random',
|
|
73
162
|
description: 'Token signé valable 60 jours, idéal pour mailing.',
|
|
74
163
|
},
|
|
75
164
|
]}
|
|
@@ -78,15 +167,30 @@ import { QrPanel } from '@mostajs/qrpanel/client'
|
|
|
78
167
|
/>
|
|
79
168
|
```
|
|
80
169
|
|
|
81
|
-
|
|
170
|
+
## CLI
|
|
82
171
|
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
172
|
+
```bash
|
|
173
|
+
npx qrpanel init # écrit .qrconfig.json (idempotent)
|
|
174
|
+
npx qrpanel themes # liste les 12 thèmes
|
|
175
|
+
npx qrpanel --version
|
|
87
176
|
```
|
|
88
177
|
|
|
89
|
-
|
|
178
|
+
## Helper combiné — `buildInviteUrls`
|
|
179
|
+
|
|
180
|
+
Inchangé depuis v0.2.
|
|
181
|
+
|
|
182
|
+
```ts
|
|
183
|
+
import { buildInviteUrls } from '@mostajs/qrpanel/server'
|
|
184
|
+
|
|
185
|
+
const { directUrl, inviteUrl, inviteToken } = buildInviteUrls({
|
|
186
|
+
baseUrl: 'https://app.example.com',
|
|
187
|
+
directPath: '/projet-x',
|
|
188
|
+
inviteSecret: process.env.INVITE_SECRET!,
|
|
189
|
+
inviteId: project.id,
|
|
190
|
+
ttlMs: 60 * 24 * 3600 * 1000,
|
|
191
|
+
inviteMeta: { kind: 'cohort-invite' },
|
|
192
|
+
})
|
|
193
|
+
```
|
|
90
194
|
|
|
91
195
|
## API
|
|
92
196
|
|
|
@@ -94,47 +198,57 @@ Le toggle de modes est masqué automatiquement quand `modes.length === 1`.
|
|
|
94
198
|
|
|
95
199
|
| Fonction | Retour | Cas |
|
|
96
200
|
|----------|--------|-----|
|
|
97
|
-
| `generateQrPng(text, opts)` | `Promise<Buffer>` |
|
|
98
|
-
| `generateQrSvg(text, opts)` | `Promise<string>` |
|
|
99
|
-
| `generateQrDataUrl(text, opts)` | `Promise<string>` |
|
|
201
|
+
| `generateQrPng(text, opts?)` | `Promise<Buffer>` | PNG composite ou QR pur si genimage=false |
|
|
202
|
+
| `generateQrSvg(text, opts?)` | `Promise<string>` | SVG composite ou QR pur |
|
|
203
|
+
| `generateQrDataUrl(text, opts?)` | `Promise<string>` | DataURL PNG |
|
|
204
|
+
| `loadQrConfig(cwd?)` | `QrConfig` | Lit `.qrconfig.json` *(cached mtime)* |
|
|
205
|
+
| `ensureQrConfig(cwd?, overrides?)` | `string` | Crée `.qrconfig.json` si absent (idempotent) |
|
|
206
|
+
| `listThemes()` | `ThemeKey[]` | Liste des 12 clés natives |
|
|
207
|
+
| `buildInviteUrls(opts)` | `InviteUrls` | URL builder + invite-token signé |
|
|
100
208
|
|
|
101
|
-
###
|
|
209
|
+
### `QrOptions`
|
|
102
210
|
|
|
103
211
|
```ts
|
|
104
212
|
interface QrOptions {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
213
|
+
// Communs
|
|
214
|
+
width?: number
|
|
215
|
+
margin?: number
|
|
216
|
+
errorCorrectionLevel?: 'L' | 'M' | 'Q' | 'H'
|
|
217
|
+
darkColor?: string
|
|
218
|
+
lightColor?: string
|
|
219
|
+
|
|
220
|
+
// v0.3 thématique
|
|
221
|
+
genimage?: boolean // master toggle (override config)
|
|
222
|
+
theme?: ThemeKey | 'random' | 'none' | { svg: string; label?: string }
|
|
223
|
+
themePool?: ThemeKey[]
|
|
224
|
+
framePadding?: number // 0..0.5
|
|
225
|
+
centerWhiteRatio?: number // 0..1
|
|
226
|
+
themeOpacity?: number // 0..1
|
|
227
|
+
themeColor?: string // CSS color
|
|
110
228
|
}
|
|
111
229
|
```
|
|
112
230
|
|
|
113
|
-
### Client `<QrPanel>`
|
|
231
|
+
### Client `<QrPanel>` — inchangé v0.2
|
|
114
232
|
|
|
115
233
|
```ts
|
|
116
|
-
interface QrPanelMode {
|
|
117
|
-
key: string // identifiant interne
|
|
118
|
-
label: string // bouton
|
|
119
|
-
url: string // URL textuelle copiable
|
|
120
|
-
qrSrc: string // src de l'image QR
|
|
121
|
-
description?: string
|
|
122
|
-
}
|
|
123
|
-
|
|
124
234
|
interface QrPanelProps {
|
|
125
235
|
modes: QrPanelMode[]
|
|
126
|
-
initialModeIndex?: number
|
|
236
|
+
initialModeIndex?: number
|
|
127
237
|
title?: string
|
|
128
|
-
mailSubject?: string
|
|
129
|
-
mailBodyTemplate?: string
|
|
130
|
-
qrSize?: number
|
|
238
|
+
mailSubject?: string
|
|
239
|
+
mailBodyTemplate?: string
|
|
240
|
+
qrSize?: number
|
|
131
241
|
className?: string
|
|
132
242
|
}
|
|
133
243
|
```
|
|
134
244
|
|
|
135
|
-
##
|
|
245
|
+
## Dépendances
|
|
136
246
|
|
|
137
|
-
|
|
247
|
+
| Package | Rôle | Pourquoi pas X |
|
|
248
|
+
|---|---|---|
|
|
249
|
+
| `qrcode` | Génère le QR pur (SVG/PNG) | Pure-JS, cross-OS, déjà éprouvé |
|
|
250
|
+
| `@resvg/resvg-js` | Rasterise le SVG composite en PNG | Rust prebuilt binaries (no node-gyp, no chromium) |
|
|
251
|
+
| `@mostajs/auth` | `signInviteToken` pour `buildInviteUrls` | Cohérence écosystème mostajs |
|
|
138
252
|
|
|
139
253
|
## Licence
|
|
140
254
|
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// @mostajs/qrpanel — CLI
|
|
3
|
+
// Author: Dr Hamid MADANI <drmdh@msn.com>
|
|
4
|
+
//
|
|
5
|
+
// Commandes disponibles :
|
|
6
|
+
// qrpanel init → écrit .qrconfig.json à cwd avec les defaults
|
|
7
|
+
// qrpanel themes → liste les 12 thèmes natifs
|
|
8
|
+
// qrpanel --version → version du package
|
|
9
|
+
import { ensureQrConfig, DEFAULT_CONFIG } from './config.js';
|
|
10
|
+
import { listThemes, THEMES } from './themes.js';
|
|
11
|
+
const args = process.argv.slice(2);
|
|
12
|
+
const cmd = args[0] ?? 'help';
|
|
13
|
+
function help() {
|
|
14
|
+
console.log(`@mostajs/qrpanel — CLI
|
|
15
|
+
|
|
16
|
+
Usage:
|
|
17
|
+
qrpanel init [path] Generate .qrconfig.json at cwd (or [path]).
|
|
18
|
+
Idempotent — does not overwrite existing config.
|
|
19
|
+
qrpanel themes List built-in theme keys.
|
|
20
|
+
qrpanel --version | -v Print version.
|
|
21
|
+
qrpanel help | -h This help.
|
|
22
|
+
|
|
23
|
+
Config file (.qrconfig.json) drives all generators. Master toggle:
|
|
24
|
+
"genimage": false → bypass theme pipeline, QR-only output.
|
|
25
|
+
"theme": "random" → randomize from themePool at each generation.
|
|
26
|
+
`);
|
|
27
|
+
}
|
|
28
|
+
async function main() {
|
|
29
|
+
switch (cmd) {
|
|
30
|
+
case 'init': {
|
|
31
|
+
const cwd = args[1] ?? process.cwd();
|
|
32
|
+
const path = ensureQrConfig(cwd);
|
|
33
|
+
console.log(`✓ qrpanel config ready at: ${path}`);
|
|
34
|
+
console.log(` Edit it to customize defaults — master toggle "genimage" toggles the whole pipeline.`);
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
case 'themes': {
|
|
38
|
+
console.log(`Built-in themes (${listThemes().length}):`);
|
|
39
|
+
for (const key of listThemes()) {
|
|
40
|
+
console.log(` - ${key.padEnd(10)} (${THEMES[key].label})`);
|
|
41
|
+
}
|
|
42
|
+
console.log(`\nDefaults config :`);
|
|
43
|
+
console.log(` themePool : ${DEFAULT_CONFIG.default.themePool.join(', ')}`);
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
case '--version':
|
|
47
|
+
case '-v': {
|
|
48
|
+
// Lecture programmatique du package.json sans import JSON (cross-runtime safe)
|
|
49
|
+
const { readFileSync } = await import('node:fs');
|
|
50
|
+
const { fileURLToPath } = await import('node:url');
|
|
51
|
+
const { dirname, join } = await import('node:path');
|
|
52
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
53
|
+
const pkgPath = join(here, '..', 'package.json');
|
|
54
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
55
|
+
console.log(pkg.version);
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
case 'help':
|
|
59
|
+
case '--help':
|
|
60
|
+
case '-h':
|
|
61
|
+
default:
|
|
62
|
+
help();
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
main().catch((e) => {
|
|
67
|
+
console.error('[qrpanel] error:', e.message);
|
|
68
|
+
process.exit(1);
|
|
69
|
+
});
|
|
70
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,yBAAyB;AACzB,0CAA0C;AAC1C,EAAE;AACF,0BAA0B;AAC1B,uEAAuE;AACvE,qDAAqD;AACrD,6CAA6C;AAE7C,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAC5D,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEhD,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;AAClC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,MAAM,CAAA;AAE7B,SAAS,IAAI;IACX,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;CAYb,CAAC,CAAA;AACF,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,QAAQ,GAAG,EAAE,CAAC;QACZ,KAAK,MAAM,CAAC,CAAC,CAAC;YACZ,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,GAAG,EAAE,CAAA;YACpC,MAAM,IAAI,GAAG,cAAc,CAAC,GAAG,CAAC,CAAA;YAChC,OAAO,CAAC,GAAG,CAAC,8BAA8B,IAAI,EAAE,CAAC,CAAA;YACjD,OAAO,CAAC,GAAG,CAAC,wFAAwF,CAAC,CAAA;YACrG,MAAK;QACP,CAAC;QACD,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,OAAO,CAAC,GAAG,CAAC,oBAAoB,UAAU,EAAE,CAAC,MAAM,IAAI,CAAC,CAAA;YACxD,KAAK,MAAM,GAAG,IAAI,UAAU,EAAE,EAAE,CAAC;gBAC/B,OAAO,CAAC,GAAG,CAAC,OAAO,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,CAAC,CAAA;YAC7D,CAAC;YACD,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAA;YAClC,OAAO,CAAC,GAAG,CAAC,iBAAiB,cAAc,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;YAC3E,MAAK;QACP,CAAC;QACD,KAAK,WAAW,CAAC;QACjB,KAAK,IAAI,CAAC,CAAC,CAAC;YACV,+EAA+E;YAC/E,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAA;YAChD,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,CAAA;YAClD,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,CAAA;YACnD,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;YACpD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC,CAAA;YAChD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAwB,CAAA;YAC7E,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;YACxB,MAAK;QACP,CAAC;QACD,KAAK,MAAM,CAAC;QACZ,KAAK,QAAQ,CAAC;QACd,KAAK,IAAI,CAAC;QACV;YACE,IAAI,EAAE,CAAA;YACN,MAAK;IACT,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE;IACjB,OAAO,CAAC,KAAK,CAAC,kBAAkB,EAAG,CAAW,CAAC,OAAO,CAAC,CAAA;IACvD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACjB,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type ThemeKey, type ThemeAsset } from './themes.js';
|
|
2
|
+
import type { QrConfigDefaults } from './config.js';
|
|
3
|
+
export interface ComposeOptions {
|
|
4
|
+
width: number;
|
|
5
|
+
errorCorrectionLevel: 'L' | 'M' | 'Q' | 'H';
|
|
6
|
+
darkColor: string;
|
|
7
|
+
lightColor: string;
|
|
8
|
+
theme: ThemeKey | 'random' | 'none' | {
|
|
9
|
+
svg: string;
|
|
10
|
+
label?: string;
|
|
11
|
+
};
|
|
12
|
+
themePool: ThemeKey[];
|
|
13
|
+
framePadding: number;
|
|
14
|
+
centerWhiteRatio: number;
|
|
15
|
+
themeOpacity: number;
|
|
16
|
+
themeColor: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Résout la prop `theme` en thème concret.
|
|
20
|
+
* - 'random' → tire dans themePool
|
|
21
|
+
* - 'none' → null (caller doit fallback sur QR pur)
|
|
22
|
+
* - { svg } → thème inline custom
|
|
23
|
+
* - clé → thème natif via registry
|
|
24
|
+
*/
|
|
25
|
+
export declare function resolveTheme(theme: ComposeOptions['theme'], pool: ThemeKey[]): ThemeAsset | null;
|
|
26
|
+
/**
|
|
27
|
+
* Génère le SVG composite (theme frame + center white card + QR).
|
|
28
|
+
* Si theme='none', retourne null — le caller doit fallback sur le QR pur.
|
|
29
|
+
*/
|
|
30
|
+
export declare function composeThemedSvg(text: string, opts: ComposeOptions): Promise<string | null>;
|
|
31
|
+
/**
|
|
32
|
+
* Génère le PNG composite via @resvg/resvg-js.
|
|
33
|
+
* Si theme='none', retourne null — le caller doit fallback.
|
|
34
|
+
*/
|
|
35
|
+
export declare function composeThemedPng(text: string, opts: ComposeOptions): Promise<Buffer | null>;
|
|
36
|
+
/**
|
|
37
|
+
* Génère un Data URL PNG du composite.
|
|
38
|
+
* Si theme='none', retourne null — le caller doit fallback.
|
|
39
|
+
*/
|
|
40
|
+
export declare function composeThemedDataUrl(text: string, opts: ComposeOptions): Promise<string | null>;
|
|
41
|
+
/** Helper de merge config + opts pour les fonctions de génération. */
|
|
42
|
+
export declare function mergeComposeOpts(cfg: QrConfigDefaults, opts?: Partial<ComposeOptions>): ComposeOptions;
|
|
43
|
+
//# sourceMappingURL=composer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"composer.d.ts","sourceRoot":"","sources":["../src/composer.ts"],"names":[],"mappings":"AAcA,OAAO,EACL,KAAK,QAAQ,EAAE,KAAK,UAAU,EAE/B,MAAM,aAAa,CAAA;AACpB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAEnD,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAA;IACb,oBAAoB,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAA;IAC3C,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,GAAG;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IACrE,SAAS,EAAE,QAAQ,EAAE,CAAA;IACrB,YAAY,EAAE,MAAM,CAAA;IACpB,gBAAgB,EAAE,MAAM,CAAA;IACxB,YAAY,EAAE,MAAM,CAAA;IACpB,UAAU,EAAE,MAAM,CAAA;CACnB;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,EAC9B,IAAI,EAAE,QAAQ,EAAE,GACf,UAAU,GAAG,IAAI,CAOnB;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,cAAc,GACnB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAmDxB;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,cAAc,GACnB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAUxB;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,cAAc,GACnB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAIxB;AAED,sEAAsE;AACtE,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,gBAAgB,EACrB,IAAI,GAAE,OAAO,CAAC,cAAc,CAAM,GACjC,cAAc,CAahB"}
|
package/dist/composer.js
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
// @mostajs/qrpanel/composer — themed composite generator
|
|
2
|
+
// Author: Dr Hamid MADANI <drmdh@msn.com>
|
|
3
|
+
//
|
|
4
|
+
// Compose un SVG global :
|
|
5
|
+
// 1. background blanc full canvas
|
|
6
|
+
// 2. cadre thématique (4 motifs aux coins) via themes.buildThemeFrameSvg
|
|
7
|
+
// 3. cartouche blanc central (rect)
|
|
8
|
+
// 4. QR centré dans le cartouche, ECC élevé recommandé
|
|
9
|
+
//
|
|
10
|
+
// Le PNG composite est obtenu en rasterisant le SVG via @resvg/resvg-js
|
|
11
|
+
// (rust prebuilt-binaries cross-OS, no chromium, no node-gyp).
|
|
12
|
+
import QRCode from 'qrcode';
|
|
13
|
+
import { Resvg } from '@resvg/resvg-js';
|
|
14
|
+
import { getTheme, pickRandomTheme, buildThemeFrameSvg, } from './themes.js';
|
|
15
|
+
/**
|
|
16
|
+
* Résout la prop `theme` en thème concret.
|
|
17
|
+
* - 'random' → tire dans themePool
|
|
18
|
+
* - 'none' → null (caller doit fallback sur QR pur)
|
|
19
|
+
* - { svg } → thème inline custom
|
|
20
|
+
* - clé → thème natif via registry
|
|
21
|
+
*/
|
|
22
|
+
export function resolveTheme(theme, pool) {
|
|
23
|
+
if (theme === 'none')
|
|
24
|
+
return null;
|
|
25
|
+
if (typeof theme === 'object' && theme && 'svg' in theme) {
|
|
26
|
+
return { key: 'custom', label: theme.label ?? 'Custom', motif: theme.svg };
|
|
27
|
+
}
|
|
28
|
+
const key = theme === 'random' ? pickRandomTheme(pool) : theme;
|
|
29
|
+
return getTheme(key);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Génère le SVG composite (theme frame + center white card + QR).
|
|
33
|
+
* Si theme='none', retourne null — le caller doit fallback sur le QR pur.
|
|
34
|
+
*/
|
|
35
|
+
export async function composeThemedSvg(text, opts) {
|
|
36
|
+
const themeAsset = resolveTheme(opts.theme, opts.themePool);
|
|
37
|
+
if (!themeAsset)
|
|
38
|
+
return null;
|
|
39
|
+
const w = opts.width;
|
|
40
|
+
// 1. QR SVG inline — margin=0, on lui donne sa propre marge via le cartouche
|
|
41
|
+
const qrRawSvg = await QRCode.toString(text, {
|
|
42
|
+
type: 'svg',
|
|
43
|
+
margin: 0,
|
|
44
|
+
errorCorrectionLevel: opts.errorCorrectionLevel,
|
|
45
|
+
color: { dark: opts.darkColor, light: opts.lightColor },
|
|
46
|
+
});
|
|
47
|
+
// 2. Extrait le viewBox et le contenu interne du QR SVG
|
|
48
|
+
const vbMatch = qrRawSvg.match(/viewBox="([^"]+)"/);
|
|
49
|
+
const qrViewBox = vbMatch?.[1] ?? '0 0 21 21';
|
|
50
|
+
const qrInner = qrRawSvg
|
|
51
|
+
.replace(/<\?xml[^>]*\?>\s*/, '')
|
|
52
|
+
.replace(/<svg[^>]*>/, '')
|
|
53
|
+
.replace(/<\/svg>\s*$/, '');
|
|
54
|
+
// 3. Géométrie : cartouche blanc central + QR à l'intérieur (avec
|
|
55
|
+
// une marge de respiration entre le QR et le bord du cartouche)
|
|
56
|
+
const centerSize = w * opts.centerWhiteRatio;
|
|
57
|
+
const centerX = (w - centerSize) / 2;
|
|
58
|
+
const qrInsideMargin = centerSize * 0.06; // 6% de la taille cartouche
|
|
59
|
+
const qrSize = centerSize - 2 * qrInsideMargin;
|
|
60
|
+
const qrX = centerX + qrInsideMargin;
|
|
61
|
+
const qrY = centerX + qrInsideMargin;
|
|
62
|
+
// 4. Cadre thématique (4 coins)
|
|
63
|
+
const frame = buildThemeFrameSvg(themeAsset, {
|
|
64
|
+
width: w,
|
|
65
|
+
color: opts.themeColor,
|
|
66
|
+
opacity: opts.themeOpacity,
|
|
67
|
+
framePadding: opts.framePadding,
|
|
68
|
+
});
|
|
69
|
+
// 5. SVG composite final
|
|
70
|
+
const svg = `<?xml version="1.0" encoding="UTF-8"?>
|
|
71
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${w} ${w}" width="${w}" height="${w}">
|
|
72
|
+
<rect width="${w}" height="${w}" fill="${opts.lightColor}"/>
|
|
73
|
+
${frame}
|
|
74
|
+
<rect x="${centerX}" y="${centerX}" width="${centerSize}" height="${centerSize}" fill="${opts.lightColor}"/>
|
|
75
|
+
<svg x="${qrX}" y="${qrY}" width="${qrSize}" height="${qrSize}" viewBox="${qrViewBox}" shape-rendering="crispEdges">
|
|
76
|
+
${qrInner}
|
|
77
|
+
</svg>
|
|
78
|
+
</svg>`;
|
|
79
|
+
return svg;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Génère le PNG composite via @resvg/resvg-js.
|
|
83
|
+
* Si theme='none', retourne null — le caller doit fallback.
|
|
84
|
+
*/
|
|
85
|
+
export async function composeThemedPng(text, opts) {
|
|
86
|
+
const svg = await composeThemedSvg(text, opts);
|
|
87
|
+
if (!svg)
|
|
88
|
+
return null;
|
|
89
|
+
const resvg = new Resvg(svg, {
|
|
90
|
+
fitTo: { mode: 'width', value: opts.width },
|
|
91
|
+
background: opts.lightColor,
|
|
92
|
+
});
|
|
93
|
+
const rendered = resvg.render();
|
|
94
|
+
return Buffer.from(rendered.asPng());
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Génère un Data URL PNG du composite.
|
|
98
|
+
* Si theme='none', retourne null — le caller doit fallback.
|
|
99
|
+
*/
|
|
100
|
+
export async function composeThemedDataUrl(text, opts) {
|
|
101
|
+
const png = await composeThemedPng(text, opts);
|
|
102
|
+
if (!png)
|
|
103
|
+
return null;
|
|
104
|
+
return `data:image/png;base64,${png.toString('base64')}`;
|
|
105
|
+
}
|
|
106
|
+
/** Helper de merge config + opts pour les fonctions de génération. */
|
|
107
|
+
export function mergeComposeOpts(cfg, opts = {}) {
|
|
108
|
+
return {
|
|
109
|
+
width: opts.width ?? cfg.width,
|
|
110
|
+
errorCorrectionLevel: opts.errorCorrectionLevel ?? cfg.errorCorrectionLevel,
|
|
111
|
+
darkColor: opts.darkColor ?? cfg.darkColor,
|
|
112
|
+
lightColor: opts.lightColor ?? cfg.lightColor,
|
|
113
|
+
theme: opts.theme ?? cfg.theme,
|
|
114
|
+
themePool: opts.themePool ?? cfg.themePool,
|
|
115
|
+
framePadding: opts.framePadding ?? cfg.framePadding,
|
|
116
|
+
centerWhiteRatio: opts.centerWhiteRatio ?? cfg.centerWhiteRatio,
|
|
117
|
+
themeOpacity: opts.themeOpacity ?? cfg.themeOpacity,
|
|
118
|
+
themeColor: opts.themeColor ?? cfg.themeColor,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=composer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"composer.js","sourceRoot":"","sources":["../src/composer.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { ThemeKey } from './themes.js';
|
|
2
|
+
export type QrFormat = 'svg' | 'png' | 'dataUrl';
|
|
3
|
+
export type QrEcc = 'L' | 'M' | 'Q' | 'H';
|
|
4
|
+
/** Valeurs par défaut éditables dans .qrconfig.json. */
|
|
5
|
+
export interface QrConfigDefaults {
|
|
6
|
+
/** Master toggle — false = QR pur (legacy), bypass tout le pipeline thématique. */
|
|
7
|
+
genimage: boolean;
|
|
8
|
+
/** Format préféré quand l'app n'en spécifie pas un. */
|
|
9
|
+
format: QrFormat;
|
|
10
|
+
/** Largeur/hauteur du canvas SVG/PNG en pixels. */
|
|
11
|
+
width: number;
|
|
12
|
+
/** Marge blanche autour du QR (en modules). */
|
|
13
|
+
margin: number;
|
|
14
|
+
/** Niveau de correction d'erreur. 'H' recommandé pour composite (ECC=30%). */
|
|
15
|
+
errorCorrectionLevel: QrEcc;
|
|
16
|
+
/** Couleur des modules sombres du QR. */
|
|
17
|
+
darkColor: string;
|
|
18
|
+
/** Couleur du fond (cartouche central). */
|
|
19
|
+
lightColor: string;
|
|
20
|
+
/** 'random' = tirage dans themePool, ou clé thème, ou 'none' (= image off ponctuel). */
|
|
21
|
+
theme: ThemeKey | 'random' | 'none';
|
|
22
|
+
/** Sous-set des thèmes utilisés quand theme='random'. */
|
|
23
|
+
themePool: ThemeKey[];
|
|
24
|
+
/** Marge cadre image / canvas (proportion 0..0.5). */
|
|
25
|
+
framePadding: number;
|
|
26
|
+
/** Taille du cartouche blanc central (proportion du canvas, 0..1). */
|
|
27
|
+
centerWhiteRatio: number;
|
|
28
|
+
/** Opacité du cadre image (0..1). */
|
|
29
|
+
themeOpacity: number;
|
|
30
|
+
/** Couleur monochrome du cadre image (CSS color). */
|
|
31
|
+
themeColor: string;
|
|
32
|
+
}
|
|
33
|
+
export interface QrConfig {
|
|
34
|
+
default: QrConfigDefaults;
|
|
35
|
+
/** Thèmes custom — clé arbitraire, override ou ajout. */
|
|
36
|
+
customThemes?: Record<string, {
|
|
37
|
+
svg: string;
|
|
38
|
+
label?: string;
|
|
39
|
+
}>;
|
|
40
|
+
}
|
|
41
|
+
export declare const DEFAULT_CONFIG: QrConfig;
|
|
42
|
+
/**
|
|
43
|
+
* Lit la config depuis `cwd` (default `process.cwd()`).
|
|
44
|
+
* Retourne `DEFAULT_CONFIG` si aucun fichier trouvé.
|
|
45
|
+
* Cache invalidé par mtime du fichier.
|
|
46
|
+
*/
|
|
47
|
+
export declare function loadQrConfig(cwd?: string): QrConfig;
|
|
48
|
+
/**
|
|
49
|
+
* Crée `.qrconfig.json` à `cwd` s'il n'existe pas. Idempotent.
|
|
50
|
+
* Retourne le path écrit (ou path existant si déjà là).
|
|
51
|
+
*
|
|
52
|
+
* @param overrides — valeurs par défaut à patcher dans le fichier généré.
|
|
53
|
+
*/
|
|
54
|
+
export declare function ensureQrConfig(cwd?: string, overrides?: Partial<QrConfigDefaults>): string;
|
|
55
|
+
/** Vide le cache mémoire (utile pour les tests). */
|
|
56
|
+
export declare function clearConfigCache(): void;
|
|
57
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAI3C,MAAM,MAAM,QAAQ,GAAG,KAAK,GAAG,KAAK,GAAG,SAAS,CAAA;AAChD,MAAM,MAAM,KAAK,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAA;AAEzC,wDAAwD;AACxD,MAAM,WAAW,gBAAgB;IAC/B,mFAAmF;IACnF,QAAQ,EAAE,OAAO,CAAA;IACjB,uDAAuD;IACvD,MAAM,EAAE,QAAQ,CAAA;IAChB,mDAAmD;IACnD,KAAK,EAAE,MAAM,CAAA;IACb,+CAA+C;IAC/C,MAAM,EAAE,MAAM,CAAA;IACd,8EAA8E;IAC9E,oBAAoB,EAAE,KAAK,CAAA;IAC3B,yCAAyC;IACzC,SAAS,EAAE,MAAM,CAAA;IACjB,2CAA2C;IAC3C,UAAU,EAAE,MAAM,CAAA;IAClB,wFAAwF;IACxF,KAAK,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAA;IACnC,yDAAyD;IACzD,SAAS,EAAE,QAAQ,EAAE,CAAA;IACrB,sDAAsD;IACtD,YAAY,EAAE,MAAM,CAAA;IACpB,sEAAsE;IACtE,gBAAgB,EAAE,MAAM,CAAA;IACxB,qCAAqC;IACrC,YAAY,EAAE,MAAM,CAAA;IACpB,qDAAqD;IACrD,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,gBAAgB,CAAA;IACzB,yDAAyD;IACzD,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAC/D;AAID,eAAO,MAAM,cAAc,EAAE,QAmB5B,CAAA;AAyBD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,GAAG,GAAE,MAAsB,GAAG,QAAQ,CAgClE;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,GAAG,GAAE,MAAsB,EAC3B,SAAS,GAAE,OAAO,CAAC,gBAAgB,CAAM,GACxC,MAAM,CAoBR;AAED,oDAAoD;AACpD,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC"}
|