uifolio 0.0.0-stage → 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/LICENSE +21 -0
- package/README.md +304 -2
- package/cli/uifolio.mjs +412 -0
- package/package.json +78 -4
- package/src/components/ui/audio-player.tsx +258 -0
- package/src/components/ui/calendar.tsx +346 -0
- package/src/components/ui/choice.tsx +126 -0
- package/src/components/ui/color-picker.tsx +626 -0
- package/src/components/ui/command-menu.tsx +222 -0
- package/src/components/ui/file-upload.tsx +346 -0
- package/src/components/ui/icon-picker.tsx +357 -0
- package/src/components/ui/motion.tsx +131 -0
- package/src/components/ui/otp-input.tsx +235 -0
- package/src/components/ui/resizable.tsx +189 -0
- package/src/components/ui/rich-text-editor.tsx +283 -0
- package/src/components/ui/stories.tsx +277 -0
- package/src/components/ui/time-picker.tsx +95 -0
- package/src/components/ui/video-player.tsx +1430 -0
- package/src/lib/color.ts +278 -0
- package/src/lib/date.ts +126 -0
- package/src/lib/uploader.ts +110 -0
- package/src/lib/use-hotkeys.ts +52 -0
- package/src/lib/use-reduced-motion.ts +24 -0
- package/src/lib/use-theme.ts +58 -0
- package/src/lib/utils.ts +6 -0
- package/src/lib/video.ts +345 -0
- package/src/lib/vtt.ts +95 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Barronakn
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,305 @@
|
|
|
1
|
-
#
|
|
1
|
+
# UIfolio
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/uifolio)
|
|
4
|
+
[](https://www.npmjs.com/package/uifolio)
|
|
5
|
+
[](https://www.npmjs.com/package/uifolio)
|
|
6
|
+
|
|
7
|
+
Ma librairie de composants et animations : le code vit dans le projet, se copie et se modifie.
|
|
8
|
+
|
|
9
|
+
Le paquet npm `uifolio` contient la CLI, les 14 composants et leurs 9 helpers — rien d'autre.
|
|
10
|
+
Le site de démonstration (Vite, animations, playgrounds) vit dans le dépôt du projet et ne
|
|
11
|
+
fait pas partie du paquet.
|
|
12
|
+
|
|
13
|
+
## Démarrage
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install
|
|
17
|
+
npm run dev # serveur de démo (http://localhost:5173)
|
|
18
|
+
npm run build # typecheck + build de production
|
|
19
|
+
npm run preview # sert le build
|
|
20
|
+
npm run lint # oxlint
|
|
21
|
+
npm test # tests du CLI (copie, couleur, manifeste npm)
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Routes (hash) :
|
|
25
|
+
|
|
26
|
+
- `#/` — page d'accueil (identité UIFolio)
|
|
27
|
+
- `#/docs` — introduction, installation, thème
|
|
28
|
+
- `#/components` — catalogue documenté
|
|
29
|
+
- `#/components/video` — playground VideoPlayer
|
|
30
|
+
- `#/components/calendar`, `/time`, `/audio`, `/stories`, `/editor`, `/upload`,
|
|
31
|
+
`/shortcuts`, `/panels`, `/choice`, `/color-picker`, `/icon-picker`
|
|
32
|
+
- `#/animations/otp` — démo OTP animée
|
|
33
|
+
- `#/animations/accordeon-horizontal`, `/storytelling`, `/empilement`,
|
|
34
|
+
`/texte-progressif`, `/images-fondues` — animations au scroll et fondus d’images
|
|
35
|
+
|
|
36
|
+
Thème **clair/sombre** : bouton dans le header, choix persisté dans
|
|
37
|
+
`localStorage (uifolio-theme)`, initialisation avant le premier rendu (aucun flash),
|
|
38
|
+
défaut = préférence système.
|
|
39
|
+
|
|
40
|
+
**Personnalisation globale** : le panneau de droite (ou l'accordéon sur mobile)
|
|
41
|
+
règle la couleur `--primary` et l'arrondi `--radius` pour tous les composants.
|
|
42
|
+
Le sélecteur avancé propose saturation/luminosité, teinte, alpha, pipette, canaux
|
|
43
|
+
RGB, presets et formats HEX/RGB/HSL/RGBA. Le choix est persisté dans
|
|
44
|
+
`localStorage (uifolio:design)` et repris dans les commandes CLI copiées
|
|
45
|
+
(`--color`, `--radius`).
|
|
46
|
+
|
|
47
|
+
## Utiliser les composants dans un autre projet (CLI)
|
|
48
|
+
|
|
49
|
+
Comme shadcn : tu installes les fichiers dans **ton** projet, avec leurs helpers,
|
|
50
|
+
et l'alias est réécrit automatiquement.
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
# depuis n'importe quel projet, via le paquet npm
|
|
54
|
+
npx uifolio@latest add calendar --color "#10B981" --radius 6
|
|
55
|
+
pnpm dlx uifolio@latest add video-player otp
|
|
56
|
+
|
|
57
|
+
# depuis la racine du projet qui contient UIfolio
|
|
58
|
+
npm run uifolio -- list
|
|
59
|
+
npm run uifolio -- init --cwd ../mon-projet
|
|
60
|
+
npm run uifolio -- add video-player calendar otp --cwd ../mon-projet
|
|
61
|
+
|
|
62
|
+
# ou directement, exécutable via « bin »
|
|
63
|
+
node cli/uifolio.mjs add otp --cwd ../mon-projet
|
|
64
|
+
node cli/uifolio.mjs add file-upload --alias "#lib" --dry-run # simulate
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
| Commande | Rôle |
|
|
68
|
+
| --- | --- |
|
|
69
|
+
| `uifolio list` | les composants disponibles |
|
|
70
|
+
| `uifolio init` | écrit `uifolio.json` (dir, lib, alias) dans le projet cible |
|
|
71
|
+
| `uifolio add <noms...>` | copie composant **+ dépendances transitives** (helpers `@/lib/*`) |
|
|
72
|
+
|
|
73
|
+
Options : `--cwd <dir>`, `--dir <chemin>` (défaut `src/components/ui`),
|
|
74
|
+
`--lib <chemin>` (défaut `src/lib`), `--alias <alias>` (défaut `@`),
|
|
75
|
+
`--force` (écraser), `--dry-run` (simuler), `--color <hex>` et `--radius <px>`.
|
|
76
|
+
|
|
77
|
+
`--color` écrit la couleur **directement dans les fichiers copiés** : les classes
|
|
78
|
+
Tailwind (`bg-primary`, `border-primary/50`, `ring-primary/40`…) et les `var(--primary)`
|
|
79
|
+
deviennent la valeur hex (`bg-[#10b981]`, `border-[#10b981]/50`…). Le composant garde donc
|
|
80
|
+
la couleur choisie même si le projet cible ne définit pas `--primary`. `--radius` (seul)
|
|
81
|
+
écrit `uifolio-theme.css` avec l'arrondi (et les tokens de couleur par défaut).
|
|
82
|
+
|
|
83
|
+
À la fin, la CLI affiche les dépendances npm détectées, par ex. :
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
npm install clsx tailwind-merge lucide-react hls.js
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Le projet cible doit fournir les tokens du thème (`bg-background`, `border`, `text-muted-foreground`…) :
|
|
90
|
+
copie les variables CSS de `src/index.css` (`:root`, `.dark`, `@theme`) si ton projet ne les a pas.
|
|
91
|
+
|
|
92
|
+
## Thèmes clair/sombre
|
|
93
|
+
|
|
94
|
+
- Tokens CSS : `:root` (clair) et `.dark` (sombre) dans `src/index.css`,
|
|
95
|
+
branchés à Tailwind via `@theme inline` + `@custom-variant dark`.
|
|
96
|
+
- `useTheme()` (`src/lib/use-theme.ts`) : `{ theme, setTheme, toggle }`,
|
|
97
|
+
persistance `localStorage('uifolio-theme')`.
|
|
98
|
+
- `<ThemeToggle />` (`src/components/theme-toggle.tsx`) : bouton Sun/Moon.
|
|
99
|
+
- Jamais de couleur figée dans les composants : tout passe par
|
|
100
|
+
`background`, `foreground`, `card`, `muted`, `border`, `primary`, `accent`, `ring`.
|
|
101
|
+
|
|
102
|
+
## Upload réel
|
|
103
|
+
|
|
104
|
+
Le `FileUpload` ne connaît pas ton backend : il appelle
|
|
105
|
+
`uploader(file, onProgress) => Promise<url>` et affiche progression, succès et lien
|
|
106
|
+
vers l'URL publique renvoyée.
|
|
107
|
+
|
|
108
|
+
La démo utilise `createUploader()` (`src/lib/uploader.ts`) :
|
|
109
|
+
|
|
110
|
+
1. `POST /api/upload/sign` → `{ key, uploadUrl, publicUrl }` (URL signée) ;
|
|
111
|
+
2. `PUT` du fichier en **XHR avec progression** ;
|
|
112
|
+
3. **reprise automatique** (2 retries, backoff) en réutilisant la même URL signée ;
|
|
113
|
+
un 4xx ne réessaie pas (refus définitif).
|
|
114
|
+
|
|
115
|
+
Côté serveur, le plugin `upload-plugin.ts` (branché dans `vite.config.ts`) :
|
|
116
|
+
|
|
117
|
+
- **Mode local (défaut, zéro config)** : écrit dans `public/uploads/` et sert
|
|
118
|
+
`/uploads/<key>` ;
|
|
119
|
+
- **Mode S3 compatible** (AWS S3, Cloudflare R2, MinIO) : si `S3_ENDPOINT`,
|
|
120
|
+
`S3_BUCKET`, `S3_ACCESS_KEY_ID` et `S3_SECRET_ACCESS_KEY` sont définis, le serveur
|
|
121
|
+
renvoie une URL PUT signée **SigV4** (implémentée avec `node:crypto`, aucun SDK)
|
|
122
|
+
et le navigateur envoie directement au bucket.
|
|
123
|
+
|
|
124
|
+
Configuration dans `.env` — voir `.env.example`.
|
|
125
|
+
|
|
126
|
+
Brancher son propre uploader :
|
|
127
|
+
|
|
128
|
+
```tsx
|
|
129
|
+
const upload = createUploader({ signEndpoint: '/api/upload/sign' })
|
|
130
|
+
|
|
131
|
+
// ou ton propre backend :
|
|
132
|
+
const upload = async (file: File, onProgress: (v: number) => void) => {
|
|
133
|
+
const form = new FormData()
|
|
134
|
+
form.append('file', file)
|
|
135
|
+
const res = await fetch('/api/ton-endpoint', { method: 'POST', body: form })
|
|
136
|
+
const { url } = await res.json()
|
|
137
|
+
return url
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
<FileUpload accept="image/*" uploader={upload} onChange={setFiles} />
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Stack
|
|
144
|
+
|
|
145
|
+
- Vite 8 · React 19 · TypeScript
|
|
146
|
+
- Tailwind CSS 4 (tokens CSS, aucune config JS)
|
|
147
|
+
- `lucide-react` pour les icônes, `clsx` + `tailwind-merge` pour `cn()`
|
|
148
|
+
- `hls.js` importé **dynamiquement**, uniquement quand un flux `.m3u8` est lu
|
|
149
|
+
- Aucune autre dépendance : éditeur, calendrier, time picker, panneaux, palette,
|
|
150
|
+
OTP et signature SigV4 sont maison
|
|
151
|
+
|
|
152
|
+
## Composants
|
|
153
|
+
|
|
154
|
+
| Composant | Fichier | Ce qu'il gère |
|
|
155
|
+
| --- | --- | --- |
|
|
156
|
+
| VideoPlayer | `src/components/ui/video-player.tsx` | URL, File/Blob, qualités, HLS, YouTube/Vimeo, sous-titres VTT, contrôles et lecture |
|
|
157
|
+
| Calendar | `src/components/ui/calendar.tsx` | date simple, plage start–end, saisie manuelle, min/max, jours désactivés |
|
|
158
|
+
| TimePicker | `src/components/ui/time-picker.tsx` | 12/24 h, secondes, pas, saisie et flèches |
|
|
159
|
+
| AudioPlayer | `src/components/ui/audio-player.tsx` | variantes carte/compact, seek, volume, vitesse, égaliseur |
|
|
160
|
+
| Stories | `src/components/ui/stories.tsx` | viewer Instagram : images, vidéos, slides texte, pause, progression |
|
|
161
|
+
| RichTextEditor | `src/components/ui/rich-text-editor.tsx` | WYSIWYG `contentEditable`, toolbar, HTML en sortie |
|
|
162
|
+
| FileUpload | `src/components/ui/file-upload.tsx` | drag & drop, validation, aperçus, progression, URL publique, retrait |
|
|
163
|
+
| CommandMenu | `src/components/ui/command-menu.tsx` | palette Ctrl/⌘ + K, recherche, clavier |
|
|
164
|
+
| ResizablePanels | `src/components/ui/resizable.tsx` | groupes horizontal/vertical, poignées, min-size, clavier |
|
|
165
|
+
| OtpInput | `src/components/ui/otp-input.tsx` | 4 champs, collage, rotate + coche de succès |
|
|
166
|
+
| ColorPicker | `src/components/ui/color-picker.tsx` | panneau avancé, saturation/luminosité, teinte, alpha, pipette, canaux RGB, presets, HEX/RGB/HSL/RGBA |
|
|
167
|
+
|
|
168
|
+
Helpers copiables (la CLI les suit automatiquement) : `src/lib/utils.ts`,
|
|
169
|
+
`src/lib/video.ts`, `src/lib/vtt.ts`, `src/lib/date.ts`, `src/lib/uploader.ts`, `src/lib/color.ts`,
|
|
170
|
+
`src/lib/use-theme.ts`, `src/lib/use-hotkeys.ts`, `src/lib/use-reduced-motion.ts`.
|
|
171
|
+
|
|
172
|
+
## VideoPlayer
|
|
173
|
+
|
|
174
|
+
```tsx
|
|
175
|
+
<VideoPlayer
|
|
176
|
+
sources={file ?? '/video.mp4'}
|
|
177
|
+
aspectRatio="16/9"
|
|
178
|
+
fit="cover"
|
|
179
|
+
controls="full" // full | minimal | none
|
|
180
|
+
accent="#facc15"
|
|
181
|
+
autoPlay
|
|
182
|
+
muted
|
|
183
|
+
loop
|
|
184
|
+
lazy
|
|
185
|
+
subtitles={[
|
|
186
|
+
{ src: '/fr.vtt', label: 'Français', lang: 'fr', default: true },
|
|
187
|
+
{ src: '/en.vtt', label: 'English', lang: 'en' },
|
|
188
|
+
]}
|
|
189
|
+
subtitleClassName="bottom-20"
|
|
190
|
+
overlay={(state) => <MonBadge playing={state.playing} />}
|
|
191
|
+
/>
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
- Sources : URL directe, `File`/`Blob`, tableau de qualités (`{ src, quality }`), `.m3u8`,
|
|
195
|
+
YouTube et Vimeo.
|
|
196
|
+
- Pour les embeds, l'iframe n'est injectée qu'au clic (façade + miniature) et les contrôles
|
|
197
|
+
natifs sont masqués : la barre du composant pilote le lecteur de la plateforme
|
|
198
|
+
(progression, temps, volume, vitesse, lecture/pause) via `postMessage`.
|
|
199
|
+
- `ref` expose `play`, `pause`, `seek`, `mute`, `setVolume`, `setPlaybackRate`,
|
|
200
|
+
`requestFullscreen`, `getVideo`.
|
|
201
|
+
- La barre de progression et le volume suivent l'arrondi global `--radius` (poignées
|
|
202
|
+
comprises), comme le reste du catalogue.
|
|
203
|
+
- Optimisations : chargement lazy, pause hors écran, `preload` maîtrisé, `hls.js` en chunk séparé,
|
|
204
|
+
object URLs révoquées, changement de qualité sans perdre la position.
|
|
205
|
+
- Sous-titres : parsing VTT tolérant, pistes fichier ou URL, style libre via
|
|
206
|
+
`subtitleClassName`, menu CC.
|
|
207
|
+
|
|
208
|
+
## Calendar / TimePicker
|
|
209
|
+
|
|
210
|
+
```tsx
|
|
211
|
+
const [date, setDate] = useState<CalendarValue>(null)
|
|
212
|
+
<Calendar mode="single" value={date} onChange={setDate} min={min} max={max} />
|
|
213
|
+
<Calendar mode="range" value={range} onChange={setRange} />
|
|
214
|
+
<Calendar disabled={(day) => day.getDay() === 0} />
|
|
215
|
+
|
|
216
|
+
<TimePicker value={time} onChange={setTime} withSeconds step={15} hour12 />
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
La saisie manuelle accepte `jj/mm/aaaa`, `jj-mm-aaaa` et `aaaa-mm-jj`.
|
|
220
|
+
|
|
221
|
+
## Audio / Stories
|
|
222
|
+
|
|
223
|
+
```tsx
|
|
224
|
+
<AudioPlayer src="/audio.wav" title="Titre" cover="/cover.svg" accent="#facc15" />
|
|
225
|
+
<AudioPlayer variant="compact" src="/audio.wav" title="Titre" />
|
|
226
|
+
|
|
227
|
+
<Stories items={stories} open={open} onOpenChange={setOpen} duration={5000} />
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Une story peut être une image, une vidéo ou un slide texte avec son propre fond et sa durée.
|
|
231
|
+
|
|
232
|
+
## Éditeur / Upload / Raccourcis / Panneaux / OTP
|
|
233
|
+
|
|
234
|
+
```tsx
|
|
235
|
+
<RichTextEditor value={html} onChange={setHtml} toolbar />
|
|
236
|
+
|
|
237
|
+
<FileUpload accept="image/*,video/*" maxFiles={3} uploader={upload} onChange={setFiles} />
|
|
238
|
+
|
|
239
|
+
<CommandMenu items={items} open={open} onOpenChange={setOpen} />
|
|
240
|
+
useHotkeys({ 'mod+k': () => setOpen(true) })
|
|
241
|
+
|
|
242
|
+
<ResizablePanelGroup direction="horizontal">
|
|
243
|
+
<ResizablePanel defaultSize={34} minSize={20}>…</ResizablePanel>
|
|
244
|
+
<ResizableHandle />
|
|
245
|
+
<ResizablePanel defaultSize={66} minSize={25}>…</ResizablePanel>
|
|
246
|
+
</ResizablePanelGroup>
|
|
247
|
+
|
|
248
|
+
<OtpInput length={4} value={code} onChange={setCode} onComplete={valider} />
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
L'OTP aligne de 4 à 8 champs, forme un cercle à la validation puis affiche une coche.
|
|
252
|
+
L'animation respecte `prefers-reduced-motion`.
|
|
253
|
+
|
|
254
|
+
## Animations
|
|
255
|
+
|
|
256
|
+
Chaque animation a sa propre page documentée : `#/animations/otp` pour l'OTP, puis
|
|
257
|
+
`#/animations/accordeon-horizontal` et `#/animations/storytelling` pour les cartes
|
|
258
|
+
horizontales, et `#/animations/empilement` pour les cartes qui s'empilent au scroll avec
|
|
259
|
+
une couleur de texte pilotée par la progression. Les couleurs suivent `--primary`. Le
|
|
260
|
+
nombre de cartes se règle de 4 à 8 directement dans chaque aperçu. Ces démos autonomes ne
|
|
261
|
+
sont pas installables via la CLI.
|
|
262
|
+
|
|
263
|
+
## Publier le paquet
|
|
264
|
+
|
|
265
|
+
Le paquet publié ne contient que la CLI, les composants et les helpers (`files`) : aucune
|
|
266
|
+
dépendance d'exécution, React et Tailwind restent en `devDependencies` pour ne pas les
|
|
267
|
+
installer chez ceux qui lancent `npx uifolio`.
|
|
268
|
+
|
|
269
|
+
```bash
|
|
270
|
+
npm login # une fois par machine
|
|
271
|
+
npm profile enable-2fa auth-and-writes # npm refuse de publier sans 2FA
|
|
272
|
+
npm publish --otp 123456 # prepublishOnly vérifie cli/uifolio.mjs
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
Un jeton *granular* avec « Bypass 2FA » fonctionne aussi, mais npm restreint
|
|
276
|
+
progressivement ces jetons pour la publication : la 2FA + `--otp` est la voie sûre.
|
|
277
|
+
|
|
278
|
+
Ensuite `npx uifolio@latest add <composant>` et `pnpm dlx uifolio@latest add <composant>`
|
|
279
|
+
fonctionnent.
|
|
280
|
+
|
|
281
|
+
### Publication automatique (GitHub Actions)
|
|
282
|
+
|
|
283
|
+
`.github/workflows/publish.yml` publie sur npm à chaque tag `v*` : il vérifie que le tag
|
|
284
|
+
correspond à la version de `package.json`, installe avec `npm ci`, lance `oxlint`,
|
|
285
|
+
`npm pack --dry-run` et `npm test`, puis publie via **trusted publishing** (OIDC, aucun
|
|
286
|
+
token stocké). Un second job crée ensuite la **GitHub Release** du tag avec les notes
|
|
287
|
+
générées à partir des commits.
|
|
288
|
+
|
|
289
|
+
```bash
|
|
290
|
+
npm version patch # 0.3.0 → 0.3.1, commit + tag v0.3.1
|
|
291
|
+
git push --follow-tags # déclenche le workflow
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
Prérequis, une seule fois :
|
|
295
|
+
|
|
296
|
+
1. première publication manuelle (ci-dessus) — le paquet doit exister sur npm ;
|
|
297
|
+
2. npmjs.com → `uifolio` → Settings → **Trusted publishing** → GitHub Actions :
|
|
298
|
+
organisation `Barronakn`, dépôt `UIfolio`, workflow `publish.yml`,
|
|
299
|
+
action « npm publish » autorisée ;
|
|
300
|
+
3. une configuration neuve doit réussir sa première publication **sous 2 jours**,
|
|
301
|
+
sinon elle expire et il faut la recréer.
|
|
302
|
+
|
|
303
|
+
Le workflow doit être présent sur le commit taggé. Après activation, tu peux durcir le
|
|
304
|
+
paquet avec Settings → Publishing access → « Require two-factor authentication and
|
|
305
|
+
disallow tokens ».
|