@mostajs/qrpanel 0.3.3 → 0.5.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/CHANGELOG.md ADDED
@@ -0,0 +1,44 @@
1
+ # @mostajs/qrpanel — Changelog
2
+
3
+ **Auteur** : Dr Hamid MADANI <drmdh@msn.com>
4
+
5
+ ## 0.5.0 — 2026-05-21
6
+
7
+ ### Feature — `<QrImage>` : génération QR locale côté navigateur
8
+
9
+ Nouveau sous-export **`@mostajs/qrpanel/qr-image`** comblant le cas non
10
+ couvert jusqu'ici : générer un QR code **dans le navigateur**, sans
11
+ round-trip serveur ni service externe.
12
+
13
+ - **`<QrImage data size … />`** — composant React qui génère le QR
14
+ localement (data URL via `qrcode`) et le rend en `<img>`. Style-neutre
15
+ (`style` / `className`), utilisable sans Tailwind.
16
+ - **`useQrDataUrl(data, opts)`** — hook primitif renvoyant
17
+ `{ src, loading, error }`.
18
+ - **COEP-safe** : sous cross-origin isolation (`require-corp`), une image
19
+ QR cross-origin est bloquée par la politique ; la génération locale est
20
+ alors la seule option côté client.
21
+ - Sous-export dédié : `qrcode` n'entre dans le bundle client que pour les
22
+ consumers qui importent `@mostajs/qrpanel/qr-image`.
23
+
24
+ Complémentarité des trois briques :
25
+ - `./server` — `generateQr{Png,Svg,DataUrl}` (Node, thèmes, @resvg natif).
26
+ - `./client` — `<QrPanel>` (copie / partage / mailto, `qrSrc` fourni).
27
+ - `./qr-image` — `<QrImage>` (génération locale navigateur).
28
+
29
+ ### Change — `@mostajs/auth` et `@resvg/resvg-js` en peer deps optionnelles
30
+
31
+ Ces deux dépendances ne servent qu'à `./server` (`@resvg` pour la
32
+ rasterisation thématique, `@mostajs/auth` pour `buildInviteUrls`). Elles
33
+ passent de `dependencies` à **`peerDependencies` optionnelles** : un
34
+ consumer de `./qr-image` ou `./client` n'embarque plus la galaxie
35
+ auth/rbac/net ni un binaire natif inutile. Les consumers de `./server`
36
+ doivent désormais installer `@mostajs/auth` et `@resvg/resvg-js`
37
+ eux-mêmes.
38
+
39
+ ## 0.4.0
40
+
41
+ Générateur QR serveur (PNG/SVG/DataUrl) avec 12 thèmes intégrés
42
+ (image-as-frame, ECC=H), pilotage par `.qrconfig.json`, cross-OS sans
43
+ chromium ; composant React `<QrPanel>` (copie / ouvrir / mailto) ;
44
+ helper `buildInviteUrls` (invite-token HMAC) ; CLI `qrpanel`.
package/README.md CHANGED
@@ -15,10 +15,11 @@ Aucune dépendance Chromium / Puppeteer / node-gyp. Pure-JS pour `qrcode`, prebu
15
15
  ```js
16
16
  /** @type {import('next').NextConfig} */
17
17
  const nextConfig = {
18
- serverExternalPackages: [
19
- '@mostajs/qrpanel',
20
- '@resvg/resvg-js',
21
- ],
18
+ // Seulement @resvg/resvg-js (vraie dep native).
19
+ // ⚠️ Ne PAS mettre '@mostajs/qrpanel' ici : ça externaliserait
20
+ // aussi /client (composant React <QrPanel>), et un Server
21
+ // Component qui le rend crasherait avec "React.useState=null".
22
+ serverExternalPackages: ['@resvg/resvg-js'],
22
23
  }
23
24
  export default nextConfig
24
25
  ```
package/dist/client.d.ts CHANGED
@@ -21,10 +21,21 @@ export interface QrPanelProps {
21
21
  mailSubject?: string;
22
22
  /** Pré-remplissage `mailto` body — `{url}` est remplacé par l'URL courante. */
23
23
  mailBodyTemplate?: string;
24
+ /**
25
+ * Destinataires placés dans `To:` du `mailto:` *(visibles entre eux)*.
26
+ * Liste d'emails (array ou string CSV). Combinable avec `mailBcc`.
27
+ */
28
+ mailTo?: string[] | string;
29
+ /**
30
+ * Destinataires placés dans `Bcc:` *(invisibles entre eux — recommandé
31
+ * pour les listes de cohort où les participants ne doivent pas voir
32
+ * les adresses des autres)*. Liste d'emails (array ou string CSV).
33
+ */
34
+ mailBcc?: string[] | string;
24
35
  /** Taille de l'image QR. Default 260. */
25
36
  qrSize?: number;
26
37
  /** Classe CSS racine optionnelle. */
27
38
  className?: string;
28
39
  }
29
- export declare function QrPanel({ modes, initialModeIndex, title, mailSubject, mailBodyTemplate, qrSize, className, }: QrPanelProps): import("react/jsx-runtime").JSX.Element | null;
40
+ export declare function QrPanel({ modes, initialModeIndex, title, mailSubject, mailBodyTemplate, mailTo, mailBcc, qrSize, className, }: QrPanelProps): import("react/jsx-runtime").JSX.Element | null;
30
41
  //# sourceMappingURL=client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.tsx"],"names":[],"mappings":"AAkBA,MAAM,WAAW,WAAW;IAC1B,8EAA8E;IAC9E,GAAG,EAAE,MAAM,CAAA;IACX,sBAAsB;IACtB,KAAK,EAAE,MAAM,CAAA;IACb,oDAAoD;IACpD,GAAG,EAAE,MAAM,CAAA;IACX,iEAAiE;IACjE,KAAK,EAAE,MAAM,CAAA;IACb,kDAAkD;IAClD,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,iDAAiD;IACjD,KAAK,EAAE,WAAW,EAAE,CAAA;IACpB,oDAAoD;IACpD,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,sDAAsD;IACtD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,+EAA+E;IAC/E,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,yCAAyC;IACzC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,qCAAqC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAID,wBAAgB,OAAO,CAAC,EACtB,KAAK,EAAE,gBAAoB,EAAE,KAAsC,EACnE,WAA0B,EAAE,gBAA+B,EAC3D,MAAY,EAAE,SAAc,GAC7B,EAAE,YAAY,kDAkGd"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.tsx"],"names":[],"mappings":"AAkBA,MAAM,WAAW,WAAW;IAC1B,8EAA8E;IAC9E,GAAG,EAAE,MAAM,CAAA;IACX,sBAAsB;IACtB,KAAK,EAAE,MAAM,CAAA;IACb,oDAAoD;IACpD,GAAG,EAAE,MAAM,CAAA;IACX,iEAAiE;IACjE,KAAK,EAAE,MAAM,CAAA;IACb,kDAAkD;IAClD,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,iDAAiD;IACjD,KAAK,EAAE,WAAW,EAAE,CAAA;IACpB,oDAAoD;IACpD,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,sDAAsD;IACtD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,+EAA+E;IAC/E,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CAAA;IAC1B;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CAAA;IAC3B,yCAAyC;IACzC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,qCAAqC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAWD,wBAAgB,OAAO,CAAC,EACtB,KAAK,EAAE,gBAAoB,EAAE,KAAsC,EACnE,WAA0B,EAAE,gBAA+B,EAC3D,MAAM,EAAE,OAAO,EACf,MAAY,EAAE,SAAc,GAC7B,EAAE,YAAY,kDA8Gd"}
package/dist/client.js CHANGED
@@ -16,14 +16,33 @@
16
16
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
17
17
  import { useState, useId } from 'react';
18
18
  const DEFAULT_BODY = 'Bonjour,\n\nVoici ton lien personnel :\n\n{url}\n\n';
19
- export function QrPanel({ modes, initialModeIndex = 0, title = 'QR code & lien d\'invitation', mailSubject = 'Invitation', mailBodyTemplate = DEFAULT_BODY, qrSize = 260, className = '', }) {
19
+ /** Accepte une string CSV ou un array, normalise en array d'emails non-vides. */
20
+ function normaliseRecipients(input) {
21
+ if (!input)
22
+ return [];
23
+ const arr = Array.isArray(input) ? input : input.split(',');
24
+ return arr.map(s => s.trim()).filter(s => s.length > 0);
25
+ }
26
+ export function QrPanel({ modes, initialModeIndex = 0, title = 'QR code & lien d\'invitation', mailSubject = 'Invitation', mailBodyTemplate = DEFAULT_BODY, mailTo, mailBcc, qrSize = 260, className = '', }) {
20
27
  const [idx, setIdx] = useState(Math.min(Math.max(0, initialModeIndex), modes.length - 1));
21
28
  const [copied, setCopied] = useState(false);
22
29
  const textareaId = useId();
23
30
  const current = modes[idx] ?? modes[0];
24
31
  if (!current)
25
32
  return null;
26
- const mailto = `mailto:?subject=${encodeURIComponent(mailSubject)}&body=${encodeURIComponent(mailBodyTemplate.replace('{url}', current.url))}`;
33
+ const toList = normaliseRecipients(mailTo);
34
+ const bccList = normaliseRecipients(mailBcc);
35
+ const parts = [];
36
+ parts.push(`subject=${encodeURIComponent(mailSubject)}`);
37
+ parts.push(`body=${encodeURIComponent(mailBodyTemplate.replace('{url}', current.url))}`);
38
+ if (bccList.length > 0)
39
+ parts.push(`bcc=${encodeURIComponent(bccList.join(','))}`);
40
+ // `To:` est positionné juste après `mailto:` quand fourni (les
41
+ // destinataires To: sont rendus visibles entre eux par tous les clients).
42
+ // Les Bcc: passent par query param `&bcc=`.
43
+ const toPart = toList.length > 0 ? encodeURIComponent(toList.join(',')) : '';
44
+ const mailto = `mailto:${toPart}?${parts.join('&')}`;
45
+ const recipientCount = toList.length + bccList.length;
27
46
  async function copy() {
28
47
  try {
29
48
  await navigator.clipboard.writeText(current.url);
@@ -35,6 +54,6 @@ export function QrPanel({ modes, initialModeIndex = 0, title = 'QR code & lien d
35
54
  el?.select();
36
55
  }
37
56
  }
38
- return (_jsxs("div", { className: 'bg-white border border-slate-200 rounded-xl p-4 space-y-3 ' + className, children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx("h2", { className: "font-semibold text-slate-900", children: title }), modes.length > 1 && (_jsx("div", { className: "flex items-center gap-1 text-xs", children: modes.map((m, i) => (_jsx("button", { type: "button", onClick: () => setIdx(i), className: 'px-2 py-1 rounded ' + (i === idx ? 'bg-blue-600 text-white' : 'bg-slate-100 hover:bg-slate-200'), children: m.label }, m.key))) }))] }), current.description && (_jsx("p", { className: "text-xs text-slate-500", children: current.description })), _jsxs("div", { className: "grid md:grid-cols-2 gap-4 items-start", children: [_jsxs("div", { className: "flex flex-col items-center gap-2", children: [_jsx("img", { src: current.qrSrc, alt: `QR ${current.label}`, width: qrSize, height: qrSize, className: "border border-slate-200 rounded-lg" }), _jsx("a", { href: current.qrSrc, download: `qr-${current.key}.png`, className: "px-3 py-1.5 rounded text-xs bg-emerald-50 hover:bg-emerald-100 text-emerald-700 border border-emerald-200", children: "\u2B07 T\u00E9l\u00E9charger le PNG" })] }), _jsxs("div", { className: "space-y-2", children: [_jsxs("label", { className: "block", children: [_jsx("span", { className: "block text-xs font-medium text-slate-700 mb-1", children: "Lien direct *(\u00E0 coller dans un email)*" }), _jsx("textarea", { id: textareaId, readOnly: true, value: current.url, rows: current.url.length > 60 ? 4 : 2, onClick: (e) => e.currentTarget.select(), className: "w-full px-2 py-1.5 border border-slate-300 rounded text-xs font-mono bg-slate-50 break-all" })] }), _jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsx("button", { type: "button", onClick: copy, className: "px-3 py-1.5 rounded text-xs bg-blue-600 text-white hover:bg-blue-700", children: copied ? '✓ Copié' : '📋 Copier le lien' }), _jsx("a", { href: current.url, target: "_blank", rel: "noreferrer", className: "px-3 py-1.5 rounded text-xs bg-slate-100 hover:bg-slate-200 text-slate-700", children: "\u2197 Ouvrir" }), _jsx("a", { href: mailto, className: "px-3 py-1.5 rounded text-xs bg-amber-50 hover:bg-amber-100 text-amber-700 border border-amber-200", children: "\u2709 Pr\u00E9-remplir un mail" })] })] })] })] }));
57
+ return (_jsxs("div", { className: 'bg-white border border-slate-200 rounded-xl p-4 space-y-3 ' + className, children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx("h2", { className: "font-semibold text-slate-900", children: title }), modes.length > 1 && (_jsx("div", { className: "flex items-center gap-1 text-xs", children: modes.map((m, i) => (_jsx("button", { type: "button", onClick: () => setIdx(i), className: 'px-2 py-1 rounded ' + (i === idx ? 'bg-blue-600 text-white' : 'bg-slate-100 hover:bg-slate-200'), children: m.label }, m.key))) }))] }), current.description && (_jsx("p", { className: "text-xs text-slate-500", children: current.description })), _jsxs("div", { className: "grid md:grid-cols-2 gap-4 items-start", children: [_jsxs("div", { className: "flex flex-col items-center gap-2", children: [_jsx("img", { src: current.qrSrc, alt: `QR ${current.label}`, width: qrSize, height: qrSize, className: "border border-slate-200 rounded-lg" }), _jsx("a", { href: current.qrSrc, download: `qr-${current.key}.png`, className: "px-3 py-1.5 rounded text-xs bg-emerald-50 hover:bg-emerald-100 text-emerald-700 border border-emerald-200", children: "\u2B07 T\u00E9l\u00E9charger le PNG" })] }), _jsxs("div", { className: "space-y-2", children: [_jsxs("label", { className: "block", children: [_jsx("span", { className: "block text-xs font-medium text-slate-700 mb-1", children: "Lien direct *(\u00E0 coller dans un email)*" }), _jsx("textarea", { id: textareaId, readOnly: true, value: current.url, rows: current.url.length > 60 ? 4 : 2, onClick: (e) => e.currentTarget.select(), className: "w-full px-2 py-1.5 border border-slate-300 rounded text-xs font-mono bg-slate-50 break-all" })] }), _jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsx("button", { type: "button", onClick: copy, className: "px-3 py-1.5 rounded text-xs bg-blue-600 text-white hover:bg-blue-700", children: copied ? '✓ Copié' : '📋 Copier le lien' }), _jsx("a", { href: current.url, target: "_blank", rel: "noreferrer", className: "px-3 py-1.5 rounded text-xs bg-slate-100 hover:bg-slate-200 text-slate-700", children: "\u2197 Ouvrir" }), _jsxs("a", { href: mailto, className: "px-3 py-1.5 rounded text-xs bg-amber-50 hover:bg-amber-100 text-amber-700 border border-amber-200", title: recipientCount > 0 ? `Ouvre le client mail avec ${recipientCount} destinataire(s) pré-remplis` : 'Ouvre le client mail avec sujet + corps pré-remplis', children: ["\u2709 Pr\u00E9-remplir un mail", recipientCount > 0 ? ` (${recipientCount})` : ''] })] })] })] })] }));
39
58
  }
40
59
  //# sourceMappingURL=client.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ export interface QrDataUrlOptions {
2
+ /** Côté de l'image en pixels. Défaut 200. */
3
+ size?: number;
4
+ /** Marge blanche autour du QR (en modules). Défaut 2. */
5
+ margin?: number;
6
+ /** Correction d'erreur : L 7 % | M 15 % | Q 25 % | H 30 %. Défaut 'M'. */
7
+ errorCorrectionLevel?: 'L' | 'M' | 'Q' | 'H';
8
+ /** Couleur des modules sombres. Défaut '#0f172a'. */
9
+ darkColor?: string;
10
+ /** Couleur du fond. Défaut '#ffffff'. */
11
+ lightColor?: string;
12
+ }
13
+ export interface UseQrDataUrlResult {
14
+ /** Data URL `data:image/png;base64,…` du QR, ou '' tant que non prêt. */
15
+ src: string;
16
+ /** True pendant la génération. */
17
+ loading: boolean;
18
+ /** Message d'erreur si la génération a échoué, sinon null. */
19
+ error: string | null;
20
+ }
21
+ /** Hook : génère le data URL d'un QR localement (navigateur). */
22
+ export declare function useQrDataUrl(data: string, opts?: QrDataUrlOptions): UseQrDataUrlResult;
23
+ export interface QrImageProps extends QrDataUrlOptions {
24
+ /** Donnée encodée dans le QR (URL, texte…). */
25
+ data: string;
26
+ alt?: string;
27
+ style?: React.CSSProperties;
28
+ className?: string;
29
+ }
30
+ /** QR code généré localement et rendu en `<img>` data-URL (COEP-safe). */
31
+ export default function QrImage({ data, alt, style, className, ...opts }: QrImageProps): import("react/jsx-runtime").JSX.Element;
32
+ //# sourceMappingURL=qr-image.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qr-image.d.ts","sourceRoot":"","sources":["../src/qr-image.tsx"],"names":[],"mappings":"AAuBA,MAAM,WAAW,gBAAgB;IAC/B,6CAA6C;IAC7C,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,yDAAyD;IACzD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,0EAA0E;IAC1E,oBAAoB,CAAC,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAA;IAC5C,qDAAqD;IACrD,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,yCAAyC;IACzC,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,yEAAyE;IACzE,GAAG,EAAE,MAAM,CAAA;IACX,kCAAkC;IAClC,OAAO,EAAE,OAAO,CAAA;IAChB,8DAA8D;IAC9D,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CACrB;AAED,iEAAiE;AACjE,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,gBAAqB,GAAG,kBAAkB,CAwB1F;AAED,MAAM,WAAW,YAAa,SAAQ,gBAAgB;IACpD,+CAA+C;IAC/C,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAA;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,0EAA0E;AAC1E,MAAM,CAAC,OAAO,UAAU,OAAO,CAAC,EAC9B,IAAI,EAAE,GAAe,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,IAAI,EACjD,EAAE,YAAY,2CAqBd"}
@@ -0,0 +1,68 @@
1
+ // @mostajs/qrpanel/qr-image — composant QR généré côté navigateur
2
+ // Author: Dr Hamid MADANI <drmdh@msn.com>
3
+ //
4
+ // QR code généré 100% dans le navigateur (data URL via `qrcode`), sans
5
+ // appel réseau ni round-trip serveur. Complète les deux briques
6
+ // existantes de qrpanel :
7
+ // - `<QrPanel>` (./client) attend un `qrSrc` déjà fourni par l'app.
8
+ // - `generateQr*` (./server) génère côté Node uniquement (@resvg natif).
9
+ // `<QrImage>` couvre le cas manquant : génération locale, côté client.
10
+ //
11
+ // Cas d'usage clé — COEP `require-corp` : sous cross-origin isolation
12
+ // (requise par ex. par ffmpeg.wasm / SharedArrayBuffer), une image QR
13
+ // servie par un service externe est bloquée par la politique. La
14
+ // génération locale est alors la seule option côté client.
15
+ //
16
+ // Sub-export dédié (`@mostajs/qrpanel/qr-image`) : `qrcode` n'est
17
+ // embarqué dans le bundle client que pour les consumers qui l'importent.
18
+ 'use client';
19
+ import { jsx as _jsx } from "react/jsx-runtime";
20
+ import { useEffect, useState } from 'react';
21
+ import QRCode from 'qrcode';
22
+ /** Hook : génère le data URL d'un QR localement (navigateur). */
23
+ export function useQrDataUrl(data, opts = {}) {
24
+ const { size = 200, margin = 2, errorCorrectionLevel = 'M', darkColor = '#0f172a', lightColor = '#ffffff', } = opts;
25
+ const [src, setSrc] = useState('');
26
+ const [loading, setLoading] = useState(true);
27
+ const [error, setError] = useState(null);
28
+ useEffect(() => {
29
+ let alive = true;
30
+ setLoading(true);
31
+ setError(null);
32
+ setSrc('');
33
+ QRCode.toDataURL(data, {
34
+ width: size, margin, errorCorrectionLevel,
35
+ color: { dark: darkColor, light: lightColor },
36
+ })
37
+ .then((url) => { if (alive) {
38
+ setSrc(url);
39
+ setLoading(false);
40
+ } })
41
+ .catch((e) => {
42
+ if (alive) {
43
+ setError(e?.message || 'QR generation failed');
44
+ setLoading(false);
45
+ }
46
+ });
47
+ return () => { alive = false; };
48
+ }, [data, size, margin, errorCorrectionLevel, darkColor, lightColor]);
49
+ return { src, loading, error };
50
+ }
51
+ /** QR code généré localement et rendu en `<img>` data-URL (COEP-safe). */
52
+ export default function QrImage({ data, alt = 'QR code', style, className, ...opts }) {
53
+ const size = opts.size ?? 200;
54
+ const { src, loading, error } = useQrDataUrl(data, opts);
55
+ const box = {
56
+ width: size, height: size, display: 'flex',
57
+ alignItems: 'center', justifyContent: 'center',
58
+ background: '#fff', borderRadius: 6, border: '1px solid #e2e8f0',
59
+ fontSize: 11, color: '#94a3b8', textAlign: 'center',
60
+ ...style,
61
+ };
62
+ if (error)
63
+ return _jsx("div", { style: box, className: className, children: "QR indisponible" });
64
+ if (loading || !src)
65
+ return _jsx("div", { style: box, className: className, "aria-busy": "true", children: "\u2026" });
66
+ return (_jsx("img", { src: src, alt: alt, width: size, height: size, style: { background: '#fff', borderRadius: 6, display: 'block', ...style }, className: className }));
67
+ }
68
+ //# sourceMappingURL=qr-image.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qr-image.js","sourceRoot":"","sources":["../src/qr-image.tsx"],"names":[],"mappings":"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"}
package/llms.txt ADDED
@@ -0,0 +1,103 @@
1
+ # @mostajs/qrpanel — fiche LLM
2
+ > Panneau QR code : générateur serveur PNG/SVG à 12 thèmes (image-as-frame, ECC=H), piloté par config (.qrconfig.json), sans chromium, + composant React <QrPanel> avec copier/partager/mailto.
3
+
4
+ - Version: 0.5.0 · Licence: AGPL-3.0-or-later · Auteur: Dr Hamid MADANI <drmdh@msn.com>
5
+ - Chemin: mostajs/mosta-qrpanel · Statut audit: complet (dist/)
6
+
7
+ ## RÔLE
8
+ Génère des QR codes pour apps Node/React. Trois briques complémentaires : (1) serveur —
9
+ PNG/SVG/DataURL thématiques rendus côté Node via @resvg/resvg-js (pas de chromium) ;
10
+ (2) client `<QrPanel>` — panneau d'invitation interactif (QR + lien + copier/partager) ;
11
+ (3) `<QrImage>` — génération locale navigateur, COEP-safe. Inclut un CLI `qrpanel`.
12
+
13
+ ## INSTALLATION
14
+ npm i @mostajs/qrpanel
15
+ Dépendance directe : qrcode. Peers optionnelles : react (>=18 <20) pour ./client et
16
+ ./qr-image ; @mostajs/auth + @resvg/resvg-js requis uniquement par ./server.
17
+
18
+ ## EXPORTS
19
+ - ./server : generateQrPng, generateQrSvg, generateQrDataUrl, buildInviteUrls + (ré-export) themes & config
20
+ - ./client : QrPanel
21
+ - ./qr-image : QrImage (default), useQrDataUrl
22
+ - ./themes : THEMES, THEME_KEYS, listThemes, getTheme, pickRandomTheme, buildThemeFrameSvg
23
+ - ./config : DEFAULT_CONFIG, loadQrConfig, ensureQrConfig, clearConfigCache
24
+ - "." (barrel) : ré-exporte server + client + themes + config
25
+ - bin `qrpanel` → CLI
26
+
27
+ ## EXPORTS PAR SOUS-CHEMIN
28
+ - "." → barrel complet (server + client + themes + config) — ⚠ tire le code natif serveur
29
+ - "./server" → generateQr*, buildInviteUrls, themes, config (Node uniquement)
30
+ - "./client" → QrPanel (React, qrSrc fourni par l'app)
31
+ - "./qr-image" → QrImage, useQrDataUrl (React, génération locale)
32
+ - "./themes" → registry des 12 thèmes natifs
33
+ - "./config" → chargement/écriture de .qrconfig.json
34
+
35
+ ## API — SIGNATURES
36
+ - function generateQrPng(text: string, opts?: QrOptions): Promise<Buffer>
37
+ - function generateQrSvg(text: string, opts?: QrOptions): Promise<string>
38
+ - function generateQrDataUrl(text: string, opts?: QrOptions): Promise<string> // data:image/png;base64
39
+ - function buildInviteUrls(opts: BuildInviteUrlsOptions): InviteUrls
40
+ - function QrPanel(props: QrPanelProps): JSX.Element | null
41
+ - function QrImage(props: QrImageProps): JSX.Element
42
+ - function useQrDataUrl(data: string, opts?: QrDataUrlOptions): { src: string; loading: boolean; error: string|null }
43
+ - function listThemes(): ThemeKey[]
44
+ - function getTheme(key: ThemeKey): ThemeAsset
45
+ - function pickRandomTheme(pool?: ThemeKey[]): ThemeKey
46
+ - function buildThemeFrameSvg(theme: ThemeAsset, opts: BuildFrameOpts): string
47
+ - function loadQrConfig(cwd?: string): QrConfig
48
+ - function ensureQrConfig(cwd?: string, overrides?: Partial<QrConfigDefaults>): string
49
+ - function clearConfigCache(): void
50
+
51
+ ## TYPES CLÉS
52
+ - QrOptions { width?=600; margin?=2; errorCorrectionLevel?='L'|'M'|'Q'|'H'; darkColor?='#0f172a';
53
+ lightColor?='#ffffff'; genimage?: boolean; theme?: ThemeKey|'random'|'none'|{ svg; label? };
54
+ themePool?: ThemeKey[]; framePadding?=0.13; centerWhiteRatio?=0.62; themeOpacity?=1; themeColor?='#1e293b' }
55
+ - BuildInviteUrlsOptions { baseUrl: string; directPath: string; inviteSecret: string|Buffer;
56
+ inviteId: string; ttlMs?; invitePath?='/invite'; inviteMeta? }
57
+ - InviteUrls { directUrl: string; inviteUrl: string; inviteToken: string }
58
+ - QrPanelMode { key; label; url; qrSrc: string; description? }
59
+ - QrPanelProps { modes: QrPanelMode[]; initialModeIndex?=0; title?; mailSubject?; mailBodyTemplate?;
60
+ mailTo?: string[]|string; mailBcc?: string[]|string; qrSize?=260; className? }
61
+ - QrImageProps extends QrDataUrlOptions { data: string; alt?; style?; className? }
62
+ - QrDataUrlOptions { size?=200; margin?=2; errorCorrectionLevel?='M'; darkColor?='#0f172a'; lightColor?='#ffffff' }
63
+ - ThemeKey = 'baby'|'animals'|'science'|'physics'|'chemistry'|'math'|'nature'|'tech'|'space'|'music'|'book'|'health'
64
+ - ThemeAsset { key: ThemeKey; label: string; motif: string }
65
+ - QrConfig { default: QrConfigDefaults; customThemes?: Record<string,{svg;label?}> }
66
+ - QrFormat = 'svg'|'png'|'dataUrl' ; QrEcc = 'L'|'M'|'Q'|'H'
67
+
68
+ ## PATTERN
69
+ ```ts
70
+ // Serveur — QR thématique en route API (Node uniquement)
71
+ import { generateQrPng } from '@mostajs/qrpanel/server';
72
+ const png = await generateQrPng('https://app.example.com/x', { theme: 'tech', errorCorrectionLevel: 'H' });
73
+
74
+ // Client — panneau d'invitation
75
+ import { QrPanel } from '@mostajs/qrpanel/client';
76
+ <QrPanel modes={[{ key: 'direct', label: 'Lien', url, qrSrc: '/api/qr?u=' + url }]} />
77
+
78
+ // Génération locale navigateur (COEP-safe)
79
+ import QrImage from '@mostajs/qrpanel/qr-image';
80
+ <QrImage data="https://example.com" size={240} />
81
+ ```
82
+
83
+ ## DÉPEND DE
84
+ - @mostajs/auth (peer optionnel, >=3.2.0) — requis par buildInviteUrls (signature HMAC du token)
85
+ - @resvg/resvg-js (peer optionnel, >=2.6.2) — requis par generateQr* (rasterisation SVG → PNG)
86
+ - react (peer optionnel, >=18 <20) — pour ./client et ./qr-image
87
+
88
+ ## PIÈGES
89
+ - Ne PAS importer le barrel "." ni "./server" dans un bundle navigateur : @resvg/resvg-js
90
+ est un binaire natif Node. Côté client, utiliser exclusivement ./client ou ./qr-image.
91
+ - `<QrPanel>` NE génère PAS le QR : l'app fournit `qrSrc` (endpoint API ou data URL).
92
+ - `<QrPanel>` requiert Tailwind (style compilé par l'app) ; `<QrImage>` est style-neutre.
93
+ - `genimage: false` court-circuite tout le pipeline thématique (QR pur, comportement legacy).
94
+ - `theme: 'H'` (ECC=30%) recommandé pour les thèmes image-as-frame ; un ECC plus bas peut
95
+ rendre le QR illisible avec un cadre.
96
+ - `loadQrConfig` peut auto-créer `.qrconfig.json` à `process.cwd()` ; désactiver via
97
+ `QRPANEL_AUTO_ENSURE=false`. Échec d'écriture → fallback transparent sur DEFAULT_CONFIG.
98
+ - `mailBcc` recommandé pour les cohortes (les destinataires ne se voient pas) vs `mailTo`.
99
+
100
+ ## RÉFÉRENCES
101
+ - README.md
102
+ - CHANGELOG.md
103
+ - src/, dist/cli.js (CLI `npx qrpanel`)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mostajs/qrpanel",
3
- "version": "0.3.3",
3
+ "version": "0.5.0",
4
4
  "description": "QR code panel — server-side PNG/SVG generator with 12 built-in themes (image-as-frame composite, ECC=H), config-driven (.qrconfig.json), cross-OS no chromium, + React <QrPanel> client with copy/share/mailto.",
5
5
  "author": "Dr Hamid MADANI <drmdh@msn.com>",
6
6
  "license": "AGPL-3.0-or-later",
@@ -26,6 +26,11 @@
26
26
  "import": "./dist/client.js",
27
27
  "default": "./dist/client.js"
28
28
  },
29
+ "./qr-image": {
30
+ "types": "./dist/qr-image.d.ts",
31
+ "import": "./dist/qr-image.js",
32
+ "default": "./dist/qr-image.js"
33
+ },
29
34
  "./themes": {
30
35
  "types": "./dist/themes.d.ts",
31
36
  "import": "./dist/themes.js",
@@ -40,7 +45,9 @@
40
45
  "files": [
41
46
  "dist",
42
47
  "LICENSE",
43
- "README.md"
48
+ "README.md",
49
+ "CHANGELOG.md",
50
+ "llms.txt"
44
51
  ],
45
52
  "keywords": [
46
53
  "qrcode",
@@ -58,20 +65,24 @@
58
65
  "prepublishOnly": "npm run build"
59
66
  },
60
67
  "dependencies": {
61
- "qrcode": "^1.5.4",
62
- "@mostajs/auth": "^3.2.0",
63
- "@resvg/resvg-js": "^2.6.2"
68
+ "qrcode": "^1.5.4"
64
69
  },
65
70
  "peerDependencies": {
66
- "react": ">=18 <20"
71
+ "react": ">=18 <20",
72
+ "@mostajs/auth": ">=3.2.0",
73
+ "@resvg/resvg-js": ">=2.6.2"
67
74
  },
68
75
  "peerDependenciesMeta": {
69
- "react": { "optional": true }
76
+ "react": { "optional": true },
77
+ "@mostajs/auth": { "optional": true },
78
+ "@resvg/resvg-js": { "optional": true }
70
79
  },
71
80
  "devDependencies": {
72
81
  "@types/node": "^22.0.0",
73
82
  "@types/qrcode": "^1.5.5",
74
83
  "@types/react": "^19.0.0",
84
+ "@mostajs/auth": "^3.2.0",
85
+ "@resvg/resvg-js": "^2.6.2",
75
86
  "react": "^19.0.0",
76
87
  "typescript": "^5.6.0"
77
88
  }