@mostajs/qrpanel 0.1.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/LICENSE +15 -0
- package/README.md +141 -0
- package/dist/client.d.ts +30 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +40 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +27 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +45 -0
- package/dist/server.js.map +1 -0
- package/package.json +60 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
@mostajs/config
|
|
2
|
+
Copyright (C) 2026 Dr Hamid MADANI <drmdh@msn.com>
|
|
3
|
+
|
|
4
|
+
This program is free software: you can redistribute it and/or modify it under
|
|
5
|
+
the terms of the GNU Affero General Public License as published by the Free
|
|
6
|
+
Software Foundation, either version 3 of the License, or (at your option) any
|
|
7
|
+
later version.
|
|
8
|
+
|
|
9
|
+
This program is distributed in the hope that it will be useful, but WITHOUT
|
|
10
|
+
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
11
|
+
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
12
|
+
details.
|
|
13
|
+
|
|
14
|
+
You should have received a copy of the GNU Affero General Public License
|
|
15
|
+
along with this program. If not, see <https://www.gnu.org/licenses/agpl-3.0.html>.
|
package/README.md
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# @mostajs/qrpanel
|
|
2
|
+
|
|
3
|
+
QR code panel — générateur server-side *(PNG / SVG / data URL via la lib `qrcode`)* et composant React `<QrPanel>` *(QR + lien hypertexte copiable + actions copier / ouvrir / mailto)*.
|
|
4
|
+
|
|
5
|
+
**Auteur** : Dr Hamid MADANI <drmdh@msn.com>
|
|
6
|
+
|
|
7
|
+
## Cross-OS
|
|
8
|
+
|
|
9
|
+
Aucune dépendance native. La lib `qrcode` est pure JS — fonctionne identiquement sur **Linux, macOS, Windows**, en runtime Node ou Edge. Pas de chromium, pas de node-gyp.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install @mostajs/qrpanel
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Usage côté serveur
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import { generateQrPng, generateQrSvg, generateQrDataUrl } from '@mostajs/qrpanel/server'
|
|
21
|
+
|
|
22
|
+
// Buffer PNG pour streamer dans une route
|
|
23
|
+
const png = await generateQrPng('https://example.com/invite/abc', {
|
|
24
|
+
width: 600,
|
|
25
|
+
errorCorrectionLevel: 'M',
|
|
26
|
+
darkColor: '#2563eb',
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
// SVG pour embed inline
|
|
30
|
+
const svg = await generateQrSvg('https://...', { width: 200 })
|
|
31
|
+
|
|
32
|
+
// Data URL pour <img src> direct
|
|
33
|
+
const dataUrl = await generateQrDataUrl('https://...')
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Endpoint Next.js (App Router)
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
// app/api/qr/route.ts
|
|
40
|
+
import { NextResponse } from 'next/server'
|
|
41
|
+
import { generateQrPng } from '@mostajs/qrpanel/server'
|
|
42
|
+
|
|
43
|
+
export const runtime = 'nodejs'
|
|
44
|
+
|
|
45
|
+
export async function GET(req: Request) {
|
|
46
|
+
const url = new URL(req.url).searchParams.get('text') ?? ''
|
|
47
|
+
const png = await generateQrPng(url)
|
|
48
|
+
return new NextResponse(png as any, {
|
|
49
|
+
headers: { 'Content-Type': 'image/png' },
|
|
50
|
+
})
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Usage côté client (React)
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
import { QrPanel } from '@mostajs/qrpanel/client'
|
|
58
|
+
|
|
59
|
+
<QrPanel
|
|
60
|
+
modes={[
|
|
61
|
+
{
|
|
62
|
+
key: 'direct',
|
|
63
|
+
label: 'Direct',
|
|
64
|
+
url: 'https://app.example.com/projet-x',
|
|
65
|
+
qrSrc: '/api/qr?text=' + encodeURIComponent('https://app.example.com/projet-x'),
|
|
66
|
+
description: 'Le QR mène directement au questionnaire.',
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
key: 'invite',
|
|
70
|
+
label: 'Invite token',
|
|
71
|
+
url: 'https://app.example.com/invite/eyJ...',
|
|
72
|
+
qrSrc: '/api/qr?text=' + encodeURIComponent('https://app.example.com/invite/eyJ...'),
|
|
73
|
+
description: 'Token signé valable 60 jours, idéal pour mailing.',
|
|
74
|
+
},
|
|
75
|
+
]}
|
|
76
|
+
title="QR code & lien d'invitation"
|
|
77
|
+
mailSubject="Invitation au questionnaire"
|
|
78
|
+
/>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Avec un seul mode
|
|
82
|
+
|
|
83
|
+
```tsx
|
|
84
|
+
<QrPanel modes={[
|
|
85
|
+
{ key: 'd', label: 'Direct', url, qrSrc },
|
|
86
|
+
]} />
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Le toggle de modes est masqué automatiquement quand `modes.length === 1`.
|
|
90
|
+
|
|
91
|
+
## API
|
|
92
|
+
|
|
93
|
+
### Server
|
|
94
|
+
|
|
95
|
+
| Fonction | Retour | Cas |
|
|
96
|
+
|----------|--------|-----|
|
|
97
|
+
| `generateQrPng(text, opts)` | `Promise<Buffer>` | Stream PNG dans une route |
|
|
98
|
+
| `generateQrSvg(text, opts)` | `Promise<string>` | Embed inline, scalable |
|
|
99
|
+
| `generateQrDataUrl(text, opts)` | `Promise<string>` | `<img src>` direct |
|
|
100
|
+
|
|
101
|
+
### Options communes
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
interface QrOptions {
|
|
105
|
+
width?: number // px, default 600
|
|
106
|
+
margin?: number // modules, default 2
|
|
107
|
+
errorCorrectionLevel?: 'L' | 'M' | 'Q' | 'H' // default 'M'
|
|
108
|
+
darkColor?: string // hex, default '#0f172a'
|
|
109
|
+
lightColor?: string // hex, default '#ffffff'
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Client `<QrPanel>`
|
|
114
|
+
|
|
115
|
+
```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
|
+
interface QrPanelProps {
|
|
125
|
+
modes: QrPanelMode[]
|
|
126
|
+
initialModeIndex?: number // default 0
|
|
127
|
+
title?: string
|
|
128
|
+
mailSubject?: string // default 'Invitation'
|
|
129
|
+
mailBodyTemplate?: string // {url} = placeholder
|
|
130
|
+
qrSize?: number // px, default 260
|
|
131
|
+
className?: string
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Style
|
|
136
|
+
|
|
137
|
+
Le composant utilise des classes Tailwind v3+. L'app consumer doit avoir Tailwind dans son pipeline. Les classes sont des chaînes, le composant reste portable *(utilisable même si l'app override le CSS)*.
|
|
138
|
+
|
|
139
|
+
## Licence
|
|
140
|
+
|
|
141
|
+
AGPL-3.0-or-later.
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export interface QrPanelMode {
|
|
2
|
+
/** Identifiant interne du mode (libre — sera passé en argument à `qrSrc`). */
|
|
3
|
+
key: string;
|
|
4
|
+
/** Libellé bouton. */
|
|
5
|
+
label: string;
|
|
6
|
+
/** URL absolue à afficher / copier dans ce mode. */
|
|
7
|
+
url: string;
|
|
8
|
+
/** URL de l'image QR pour ce mode (endpoint API ou data URL). */
|
|
9
|
+
qrSrc: string;
|
|
10
|
+
/** Description courte affichée sous l'en-tête. */
|
|
11
|
+
description?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface QrPanelProps {
|
|
14
|
+
/** Liste des modes proposés (1 ou plusieurs). */
|
|
15
|
+
modes: QrPanelMode[];
|
|
16
|
+
/** Mode initial — index dans `modes`. Default 0. */
|
|
17
|
+
initialModeIndex?: number;
|
|
18
|
+
/** Titre du panel. Default 'QR code & lien d\'invitation'. */
|
|
19
|
+
title?: string;
|
|
20
|
+
/** Pré-remplissage `mailto`. Default 'Invitation'. */
|
|
21
|
+
mailSubject?: string;
|
|
22
|
+
/** Pré-remplissage `mailto` body — `{url}` est remplacé par l'URL courante. */
|
|
23
|
+
mailBodyTemplate?: string;
|
|
24
|
+
/** Taille de l'image QR. Default 260. */
|
|
25
|
+
qrSize?: number;
|
|
26
|
+
/** Classe CSS racine optionnelle. */
|
|
27
|
+
className?: string;
|
|
28
|
+
}
|
|
29
|
+
export declare function QrPanel({ modes, initialModeIndex, title, mailSubject, mailBodyTemplate, qrSize, className, }: QrPanelProps): import("react/jsx-runtime").JSX.Element | null;
|
|
30
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +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"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// @mostajs/qrpanel/client — React component
|
|
2
|
+
// Author: Dr Hamid MADANI <drmdh@msn.com>
|
|
3
|
+
//
|
|
4
|
+
// Composant `<QrPanel>` agnostique de l'app. Affiche un QR + l'URL
|
|
5
|
+
// textuelle copiable + 3 actions (copier, ouvrir, mailto).
|
|
6
|
+
// Le composant ne génère pas le QR lui-même : l'app fournit le `qrSrc`
|
|
7
|
+
// (URL d'un endpoint API ou data URL inline).
|
|
8
|
+
//
|
|
9
|
+
// Style : Tailwind classes — l'app les compile dans son build (le
|
|
10
|
+
// component reste portable car les classes sont des chaînes).
|
|
11
|
+
//
|
|
12
|
+
// Note: marqué 'use client' pour Next.js App Router. Pour SPA (Vite,
|
|
13
|
+
// CRA, etc.), la directive est ignorée — le component fonctionne tel
|
|
14
|
+
// quel.
|
|
15
|
+
'use client';
|
|
16
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
17
|
+
import { useState, useId } from 'react';
|
|
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 = '', }) {
|
|
20
|
+
const [idx, setIdx] = useState(Math.min(Math.max(0, initialModeIndex), modes.length - 1));
|
|
21
|
+
const [copied, setCopied] = useState(false);
|
|
22
|
+
const textareaId = useId();
|
|
23
|
+
const current = modes[idx] ?? modes[0];
|
|
24
|
+
if (!current)
|
|
25
|
+
return null;
|
|
26
|
+
const mailto = `mailto:?subject=${encodeURIComponent(mailSubject)}&body=${encodeURIComponent(mailBodyTemplate.replace('{url}', current.url))}`;
|
|
27
|
+
async function copy() {
|
|
28
|
+
try {
|
|
29
|
+
await navigator.clipboard.writeText(current.url);
|
|
30
|
+
setCopied(true);
|
|
31
|
+
setTimeout(() => setCopied(false), 2000);
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
const el = document.getElementById(textareaId);
|
|
35
|
+
el?.select();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
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" })] })] })] })] }));
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.tsx"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// @mostajs/qrpanel — barrel
|
|
2
|
+
// Author: Dr Hamid MADANI <drmdh@msn.com>
|
|
3
|
+
//
|
|
4
|
+
// Re-export complet pour les consumers qui ne séparent pas client/server.
|
|
5
|
+
// Préfère `@mostajs/qrpanel/server` ou `@mostajs/qrpanel/client` pour
|
|
6
|
+
// éviter d'inclure React côté serveur ou qrcode côté client.
|
|
7
|
+
export * from './server.js';
|
|
8
|
+
export * from './client.js';
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface QrOptions {
|
|
2
|
+
/** Largeur/hauteur de l'image en pixels. Default 600. */
|
|
3
|
+
width?: number;
|
|
4
|
+
/** Marge blanche autour du QR (en modules). Default 2. */
|
|
5
|
+
margin?: number;
|
|
6
|
+
/**
|
|
7
|
+
* Niveau de correction d'erreur. Plus élevé = plus de redondance →
|
|
8
|
+
* QR plus dense mais résistant aux occlusions / impressions.
|
|
9
|
+
* L = 7 % (default qrcode lib)
|
|
10
|
+
* M = 15 % (recommandé pour usage écran)
|
|
11
|
+
* Q = 25 %
|
|
12
|
+
* H = 30 % (recommandé si logo overlay)
|
|
13
|
+
*/
|
|
14
|
+
errorCorrectionLevel?: 'L' | 'M' | 'Q' | 'H';
|
|
15
|
+
/** Couleur des modules sombres. Default '#0f172a' (slate-900). */
|
|
16
|
+
darkColor?: string;
|
|
17
|
+
/** Couleur du fond. Default '#ffffff'. */
|
|
18
|
+
lightColor?: string;
|
|
19
|
+
}
|
|
20
|
+
/** Génère un PNG (Buffer) d'un QR code encodant `text`. */
|
|
21
|
+
export declare function generateQrPng(text: string, opts?: QrOptions): Promise<Buffer>;
|
|
22
|
+
/** Génère un SVG (string) d'un QR code encodant `text`. */
|
|
23
|
+
export declare function generateQrSvg(text: string, opts?: QrOptions): Promise<string>;
|
|
24
|
+
/** Génère un Data URL `data:image/png;base64,...` — pratique pour
|
|
25
|
+
* embarquer directement dans une balise `<img src>` sans endpoint dédié. */
|
|
26
|
+
export declare function generateQrDataUrl(text: string, opts?: QrOptions): Promise<string>;
|
|
27
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,SAAS;IACxB,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;;;;;OAOG;IACH,oBAAoB,CAAC,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAA;IAC5C,kEAAkE;IAClE,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,0CAA0C;IAC1C,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,2DAA2D;AAC3D,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,SAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAUjF;AAED,2DAA2D;AAC3D,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,SAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAWjF;AAED;6EAC6E;AAC7E,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,SAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAUrF"}
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// @mostajs/qrpanel/server — server-side QR generation (Node)
|
|
2
|
+
// Author: Dr Hamid MADANI <drmdh@msn.com>
|
|
3
|
+
//
|
|
4
|
+
// Wrap minimaliste de la lib `qrcode` (npm). Cross-OS natif (Linux,
|
|
5
|
+
// macOS, Windows) — pas de chromium/puppeteer requis.
|
|
6
|
+
import QRCode from 'qrcode';
|
|
7
|
+
/** Génère un PNG (Buffer) d'un QR code encodant `text`. */
|
|
8
|
+
export function generateQrPng(text, opts = {}) {
|
|
9
|
+
return QRCode.toBuffer(text, {
|
|
10
|
+
width: opts.width ?? 600,
|
|
11
|
+
margin: opts.margin ?? 2,
|
|
12
|
+
errorCorrectionLevel: opts.errorCorrectionLevel ?? 'M',
|
|
13
|
+
color: {
|
|
14
|
+
dark: opts.darkColor ?? '#0f172a',
|
|
15
|
+
light: opts.lightColor ?? '#ffffff',
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
/** Génère un SVG (string) d'un QR code encodant `text`. */
|
|
20
|
+
export function generateQrSvg(text, opts = {}) {
|
|
21
|
+
return QRCode.toString(text, {
|
|
22
|
+
type: 'svg',
|
|
23
|
+
width: opts.width ?? 600,
|
|
24
|
+
margin: opts.margin ?? 2,
|
|
25
|
+
errorCorrectionLevel: opts.errorCorrectionLevel ?? 'M',
|
|
26
|
+
color: {
|
|
27
|
+
dark: opts.darkColor ?? '#0f172a',
|
|
28
|
+
light: opts.lightColor ?? '#ffffff',
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
/** Génère un Data URL `data:image/png;base64,...` — pratique pour
|
|
33
|
+
* embarquer directement dans une balise `<img src>` sans endpoint dédié. */
|
|
34
|
+
export function generateQrDataUrl(text, opts = {}) {
|
|
35
|
+
return QRCode.toDataURL(text, {
|
|
36
|
+
width: opts.width ?? 600,
|
|
37
|
+
margin: opts.margin ?? 2,
|
|
38
|
+
errorCorrectionLevel: opts.errorCorrectionLevel ?? 'M',
|
|
39
|
+
color: {
|
|
40
|
+
dark: opts.darkColor ?? '#0f172a',
|
|
41
|
+
light: opts.lightColor ?? '#ffffff',
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAC7D,0CAA0C;AAC1C,EAAE;AACF,oEAAoE;AACpE,sDAAsD;AAEtD,OAAO,MAAM,MAAM,QAAQ,CAAA;AAsB3B,2DAA2D;AAC3D,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,OAAkB,EAAE;IAC9D,OAAO,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE;QAC3B,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,GAAG;QACxB,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,CAAC;QACxB,oBAAoB,EAAE,IAAI,CAAC,oBAAoB,IAAI,GAAG;QACtD,KAAK,EAAE;YACL,IAAI,EAAE,IAAI,CAAC,SAAS,IAAI,SAAS;YACjC,KAAK,EAAE,IAAI,CAAC,UAAU,IAAI,SAAS;SACpC;KACF,CAAC,CAAA;AACJ,CAAC;AAED,2DAA2D;AAC3D,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,OAAkB,EAAE;IAC9D,OAAO,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE;QAC3B,IAAI,EAAE,KAAK;QACX,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,GAAG;QACxB,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,CAAC;QACxB,oBAAoB,EAAE,IAAI,CAAC,oBAAoB,IAAI,GAAG;QACtD,KAAK,EAAE;YACL,IAAI,EAAE,IAAI,CAAC,SAAS,IAAI,SAAS;YACjC,KAAK,EAAE,IAAI,CAAC,UAAU,IAAI,SAAS;SACpC;KACF,CAAC,CAAA;AACJ,CAAC;AAED;6EAC6E;AAC7E,MAAM,UAAU,iBAAiB,CAAC,IAAY,EAAE,OAAkB,EAAE;IAClE,OAAO,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE;QAC5B,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,GAAG;QACxB,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,CAAC;QACxB,oBAAoB,EAAE,IAAI,CAAC,oBAAoB,IAAI,GAAG;QACtD,KAAK,EAAE;YACL,IAAI,EAAE,IAAI,CAAC,SAAS,IAAI,SAAS;YACjC,KAAK,EAAE,IAAI,CAAC,UAAU,IAAI,SAAS;SACpC;KACF,CAAC,CAAA;AACJ,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mostajs/qrpanel",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "QR code panel — server-side PNG generator (qrcode npm, cross-OS, no chromium) + React client component <QrPanel> with copy/share/mailto.",
|
|
5
|
+
"author": "Dr Hamid MADANI <drmdh@msn.com>",
|
|
6
|
+
"license": "AGPL-3.0-or-later",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "dist/index.js",
|
|
9
|
+
"types": "dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js",
|
|
14
|
+
"default": "./dist/index.js"
|
|
15
|
+
},
|
|
16
|
+
"./server": {
|
|
17
|
+
"types": "./dist/server.d.ts",
|
|
18
|
+
"import": "./dist/server.js",
|
|
19
|
+
"default": "./dist/server.js"
|
|
20
|
+
},
|
|
21
|
+
"./client": {
|
|
22
|
+
"types": "./dist/client.d.ts",
|
|
23
|
+
"import": "./dist/client.js",
|
|
24
|
+
"default": "./dist/client.js"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"dist",
|
|
29
|
+
"LICENSE",
|
|
30
|
+
"README.md"
|
|
31
|
+
],
|
|
32
|
+
"keywords": [
|
|
33
|
+
"qrcode",
|
|
34
|
+
"qr",
|
|
35
|
+
"react",
|
|
36
|
+
"invite",
|
|
37
|
+
"magic-link"
|
|
38
|
+
],
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "tsc",
|
|
41
|
+
"dev": "tsc --watch",
|
|
42
|
+
"prepublishOnly": "npm run build"
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"qrcode": "^1.5.4"
|
|
46
|
+
},
|
|
47
|
+
"peerDependencies": {
|
|
48
|
+
"react": ">=18 <20"
|
|
49
|
+
},
|
|
50
|
+
"peerDependenciesMeta": {
|
|
51
|
+
"react": { "optional": true }
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@types/node": "^22.0.0",
|
|
55
|
+
"@types/qrcode": "^1.5.5",
|
|
56
|
+
"@types/react": "^19.0.0",
|
|
57
|
+
"react": "^19.0.0",
|
|
58
|
+
"typescript": "^5.6.0"
|
|
59
|
+
}
|
|
60
|
+
}
|