uifolio 0.5.1 → 0.5.4

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 CHANGED
@@ -2,311 +2,112 @@
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/uifolio.svg)](https://www.npmjs.com/package/uifolio)
4
4
  [![license](https://img.shields.io/npm/l/uifolio.svg)](https://www.npmjs.com/package/uifolio)
5
- [![node](https://img.shields.io/node/v/uifolio.svg)](https://www.npmjs.com/package/uifolio)
6
5
 
7
- Ma librairie de composants et animations : le code vit dans le projet, se copie et se modifie.
6
+ A small, theme-friendly UI registry (components live in the target project, copied and edited) with a shadcn-like CLI (`uifolio add`).
8
7
 
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.
8
+ ## Using the components in another project (CLI)
12
9
 
13
- ## Démarrage
10
+ Like shadcn: you install the files into **your** project, with their helpers,
11
+ and the alias is rewritten automatically.
14
12
 
15
13
  ```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
14
+ # from any project, through the npm package
54
15
  npx uifolio@latest add calendar --color "#10B981" --radius 6
55
16
  pnpm dlx uifolio@latest add video-player otp
56
17
 
57
- # depuis la racine du projet qui contient UIfolio
18
+ # from the root of the project that contains UIfolio
58
19
  npm run uifolio -- list
59
- npm run uifolio -- init --cwd ../mon-projet
60
- npm run uifolio -- add video-player calendar otp --cwd ../mon-projet
20
+ npm run uifolio -- init --cwd ../my-project
21
+ npm run uifolio -- add video-player calendar otp --cwd ../my-project
61
22
 
62
- # ou directement, exécutable via « bin »
63
- node cli/uifolio.mjs add otp --cwd ../mon-projet
23
+ # or directly, through the « bin »
24
+ node cli/uifolio.mjs add otp --cwd ../my-project
64
25
  node cli/uifolio.mjs add file-upload --alias "#lib" --dry-run # simulate
65
26
  ```
66
27
 
67
- | Commande | Rôle |
28
+ | Command | Purpose |
68
29
  | --- | --- |
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
- `--page <chemin>` (créé s'il n'existe pas), `--interactive` (forcer les questions),
76
- `--force` (écraser), `--dry-run` (simuler), `--color <hex>` et `--radius <px>`.
77
-
78
- `add` ne s'arrête pas à la copie : sans `--dir` ni `--page`, la CLI demande
79
- **dans quel dossier** copier les composants, puis **dans quel fichier** les
80
- utiliser. La page est créée si elle manque ; si elle existe, l'import et le
81
- `<Composant />` sont ajoutés dans son JSX (sans doublon). Réponds `-` à la
82
- seconde question pour installer sans toucher à une page.
83
-
84
- Les couleurs passent par des **tokens Tailwind CSS 4** : la CLI écrit
85
- `uifolio-theme.css` (`:root`, `.dark`, puis `@theme inline` qui expose
86
- `--color-primary`, `--color-muted-foreground`, …) et l'importe automatiquement
87
- dans le CSS global du projet (celui qui fait `@import 'tailwindcss'`). Les
88
- composants gardent donc `bg-primary`, `border-primary/50`, `ring-primary/40` :
89
- aucune couleur en dur, `--color` change `--primary` dans le thème et `--radius`
90
- y ajoute `--radius`. Sans CSS global Tailwind détecté, le fichier est écrit à la
91
- racine et l'import à ajouter est affiché.
92
-
93
- À la fin, la CLI affiche les dépendances npm détectées, par ex. :
94
-
95
- ```bash
96
- npm install clsx tailwind-merge lucide-react hls.js
97
- ```
98
-
99
- ## Thèmes clair/sombre
100
-
101
- - Tokens CSS : `:root` (clair) et `.dark` (sombre) dans `src/index.css`,
102
- branchés à Tailwind via `@theme inline` + `@custom-variant dark`.
103
- - `useTheme()` (`src/lib/use-theme.ts`) : `{ theme, setTheme, toggle }`,
104
- persistance `localStorage('uifolio-theme')`.
105
- - `<ThemeToggle />` (`src/components/theme-toggle.tsx`) : bouton Sun/Moon.
106
- - Jamais de couleur figée dans les composants : tout passe par
107
- `background`, `foreground`, `card`, `muted`, `border`, `primary`, `accent`, `ring`.
108
-
109
- ## Upload réel
110
-
111
- Le `FileUpload` ne connaît pas ton backend : il appelle
112
- `uploader(file, onProgress) => Promise<url>` et affiche progression, succès et lien
113
- vers l'URL publique renvoyée.
30
+ | `uifolio list` | the available components |
31
+ | `uifolio init` | writes `uifolio.json` (dir, lib, alias) in the target project |
32
+ | `uifolio add <names...>` | copies the component **+ transitive dependencies** (helpers `@/lib/*`) |
114
33
 
115
- La démo utilise `createUploader()` (`src/lib/uploader.ts`) :
34
+ Options: `--cwd <dir>`, `--dir <path>` (default `src/components/ui`), `--lib <path>` (default `src/lib`), `--alias <alias>` (default `@`), `--page <path>` (created if missing), `--interactive` (force the questions), `--force` (overwrite), `--dry-run` (simulate), `--color <hex>` and `--radius <px>`.
116
35
 
117
- 1. `POST /api/upload/sign` → `{ key, uploadUrl, publicUrl }` (URL signée) ;
118
- 2. `PUT` du fichier en **XHR avec progression** ;
119
- 3. **reprise automatique** (2 retries, backoff) en réutilisant la même URL signée ;
120
- un 4xx ne réessaie pas (refus définitif).
36
+ `add` does not stop at the copy. The components always land in
37
+ `src/components/ui` (override with `--dir`) and the page imports them through
38
+ the `@/` alias. Without `--page`, the CLI asks where to use them:
121
39
 
122
- Côté serveur, le plugin `upload-plugin.ts` (branché dans `vite.config.ts`) :
40
+ 1. *Do you already have a file ready to receive the component (…component…) (y/N) ?* `yes` → *In which file should the component (…component…) be copied (e.g. src/pages/calendar.tsx) ?*: the import and a ready-to-render example are added without duplication, and the file is created when missing;
41
+ 2. `no` → *Would you like to create a new folder and an import file (Y/n) ?* `yes` → *Please specify the new path (e.g. src/pages/calendar.tsx) ?*: the folder and the file are created; `no` → install only, no file is touched.
123
42
 
124
- - **Mode local (défaut, zéro config)** : écrit dans `public/uploads/` et sert
125
- `/uploads/<key>` ;
126
- - **Mode S3 compatible** (AWS S3, Cloudflare R2, MinIO) : si `S3_ENDPOINT`,
127
- `S3_BUCKET`, `S3_ACCESS_KEY_ID` et `S3_SECRET_ACCESS_KEY` sont définis, le serveur
128
- renvoie une URL PUT signée **SigV4** (implémentée avec `node:crypto`, aucun SDK)
129
- et le navigateur envoie directement au bucket.
43
+ The example is a small component (`<CalendarExample />` plus its definition)
44
+ that carries the data and props each component needs (items for `Stories` and
45
+ `CommandMenu`, a source for the players, children for the resizable panels…),
46
+ so the component is visible immediately instead of an empty `<Component />`.
130
47
 
131
- Configuration dans `.env` — voir `.env.example`.
48
+ Answering `-` to a path question installs the component without creating or
49
+ modifying any page.
132
50
 
133
- Brancher son propre uploader :
51
+ Colors go through **Tailwind CSS 4 tokens**: the CLI writes
52
+ `uifolio-theme.css` (`:root`, `.dark`, then `@theme inline`, which exposes
53
+ `--color-primary`, `--color-muted-foreground`, …) and imports it automatically
54
+ into the project's global CSS (the one with `@import 'tailwindcss'`). The
55
+ components therefore keep `bg-primary`, `border-primary/50`, `ring-primary/40`:
56
+ no hard-coded colors. `--color` changes `--primary` in the theme and `--radius`
57
+ adds `--radius` to it. When no global Tailwind CSS is found, the file is written
58
+ at the root and the import to add is printed.
134
59
 
135
- ```tsx
136
- const upload = createUploader({ signEndpoint: '/api/upload/sign' })
60
+ At the end, the CLI prints the detected npm dependencies, for example:
137
61
 
138
- // ou ton propre backend :
139
- const upload = async (file: File, onProgress: (v: number) => void) => {
140
- const form = new FormData()
141
- form.append('file', file)
142
- const res = await fetch('/api/ton-endpoint', { method: 'POST', body: form })
143
- const { url } = await res.json()
144
- return url
145
- }
146
-
147
- <FileUpload accept="image/*" uploader={upload} onChange={setFiles} />
62
+ ```bash
63
+ npm install clsx tailwind-merge lucide-react hls.js
148
64
  ```
149
65
 
150
- ## Stack
66
+ ### Preview parity
151
67
 
152
- - Vite 8 · React 19 · TypeScript
153
- - Tailwind CSS 4 (tokens CSS, aucune config JS)
154
- - `lucide-react` pour les icônes, `clsx` + `tailwind-merge` pour `cn()`
155
- - `hls.js` importé **dynamiquement**, uniquement quand un flux `.m3u8` est lu
156
- - Aucune autre dépendance : éditeur, calendrier, time picker, panneaux, palette,
157
- OTP et signature SigV4 sont maison
68
+ The docs preview and the installed component render identically because the page
69
+ written by the CLI ships a demo of the same shape as the docs. If a component
70
+ needs data to show anything (for example Stories, CommandMenu, FileUpload,
71
+ AudioPlayer, VideoPlayer or the resizable panels), the page is created with that
72
+ data, not an empty instance.
158
73
 
159
- ## Composants
74
+ If the docs entry for a component is missing, the detail page does not show a blank
75
+ placeholder. It falls back to the preview as-is (or a minimal coherent state) and
76
+ only marks the installation as complete when that is relevant.
160
77
 
161
- | Composant | Fichier | Ce qu'il gère |
162
- | --- | --- | --- |
163
- | VideoPlayer | `src/components/ui/video-player.tsx` | URL, File/Blob, qualités, HLS, YouTube/Vimeo, sous-titres VTT, contrôles et lecture |
164
- | Calendar | `src/components/ui/calendar.tsx` | date simple, plage start–end, saisie manuelle, min/max, jours désactivés |
165
- | TimePicker | `src/components/ui/time-picker.tsx` | 12/24 h, secondes, pas, saisie et flèches |
166
- | AudioPlayer | `src/components/ui/audio-player.tsx` | variantes carte/compact, seek, volume, vitesse, égaliseur |
167
- | Stories | `src/components/ui/stories.tsx` | viewer Instagram : images, vidéos, slides texte, pause, progression |
168
- | RichTextEditor | `src/components/ui/rich-text-editor.tsx` | WYSIWYG `contentEditable`, toolbar, HTML en sortie |
169
- | FileUpload | `src/components/ui/file-upload.tsx` | drag & drop, validation, aperçus, progression, URL publique, retrait |
170
- | CommandMenu | `src/components/ui/command-menu.tsx` | palette Ctrl/⌘ + K, recherche, clavier |
171
- | ResizablePanels | `src/components/ui/resizable.tsx` | groupes horizontal/vertical, poignées, min-size, clavier |
172
- | OtpInput | `src/components/ui/otp-input.tsx` | 4 champs, collage, rotate + coche de succès |
173
- | ColorPicker | `src/components/ui/color-picker.tsx` | panneau avancé, saturation/luminosité, teinte, alpha, pipette, canaux RGB, presets, HEX/RGB/HSL/RGBA |
78
+ ## Components
174
79
 
175
- Helpers copiables (la CLI les suit automatiquement) : `src/lib/utils.ts`,
80
+ | Component | File | What it handles |
81
+ | --- | --- | --- |
82
+ | VideoPlayer | `src/components/ui/video-player.tsx` | URL, File/Blob, qualities, HLS, YouTube/Vimeo, VTT subtitles, controls and playback |
83
+ | Calendar | `src/components/ui/calendar.tsx` | single date, start–end range, manual input, min/max, disabled days |
84
+ | TimePicker | `src/components/ui/time-picker.tsx` | 12/24 h, seconds, step, typing and arrow keys |
85
+ | AudioPlayer | `src/components/ui/audio-player.tsx` | card/compact variants, seek, volume, speed, equalizer |
86
+ | Stories | `src/components/ui/stories.tsx` | Instagram viewer: images, videos, text slides, pause, progress |
87
+ | RichTextEditor | `src/components/ui/rich-text-editor.tsx` | `contentEditable` WYSIWYG, toolbar, HTML output |
88
+ | FileUpload | `src/components/ui/file-upload.tsx` | drag & drop, validation, previews, progress, public URL, removal |
89
+ | CommandMenu | `src/components/ui/command-menu.tsx` | Ctrl/⌘ + K palette, search, keyboard |
90
+ | ResizablePanels | `src/components/ui/resizable.tsx` | horizontal/vertical groups, handles, min-size, keyboard |
91
+ | OtpInput | `src/components/ui/otp-input.tsx` | 4 fields, paste, rotate + success check |
92
+ | ColorPicker | `src/components/ui/color-picker.tsx` | advanced panel, saturation/lightness, hue, alpha, eyedropper, RGB channels, presets, HEX/RGB/HSL/RGBA |
93
+ | Table | `src/components/ui/table.tsx` | typed data table, uniform `px-4` cells and a wider gap before Actions, selection and row actions |
94
+
95
+ Copyable helpers (the CLI follows them automatically): `src/lib/utils.ts`,
176
96
  `src/lib/video.ts`, `src/lib/vtt.ts`, `src/lib/date.ts`, `src/lib/uploader.ts`, `src/lib/color.ts`,
177
97
  `src/lib/use-theme.ts`, `src/lib/use-hotkeys.ts`, `src/lib/use-reduced-motion.ts`.
178
98
 
179
- ## VideoPlayer
180
-
181
- ```tsx
182
- <VideoPlayer
183
- sources={file ?? '/video.mp4'}
184
- aspectRatio="16/9"
185
- fit="cover"
186
- controls="full" // full | minimal | none
187
- accent="#facc15"
188
- autoPlay
189
- muted
190
- loop
191
- lazy
192
- subtitles={[
193
- { src: '/fr.vtt', label: 'Français', lang: 'fr', default: true },
194
- { src: '/en.vtt', label: 'English', lang: 'en' },
195
- ]}
196
- subtitleClassName="bottom-20"
197
- overlay={(state) => <MonBadge playing={state.playing} />}
198
- />
199
- ```
200
-
201
- - Sources : URL directe, `File`/`Blob`, tableau de qualités (`{ src, quality }`), `.m3u8`,
202
- YouTube et Vimeo.
203
- - Pour les embeds, l'iframe n'est injectée qu'au clic (façade + miniature) et les contrôles
204
- natifs sont masqués : la barre du composant pilote le lecteur de la plateforme
205
- (progression, temps, volume, vitesse, lecture/pause) via `postMessage`.
206
- - `ref` expose `play`, `pause`, `seek`, `mute`, `setVolume`, `setPlaybackRate`,
207
- `requestFullscreen`, `getVideo`.
208
- - La barre de progression et le volume suivent l'arrondi global `--radius` (poignées
209
- comprises), comme le reste du catalogue.
210
- - Optimisations : chargement lazy, pause hors écran, `preload` maîtrisé, `hls.js` en chunk séparé,
211
- object URLs révoquées, changement de qualité sans perdre la position.
212
- - Sous-titres : parsing VTT tolérant, pistes fichier ou URL, style libre via
213
- `subtitleClassName`, menu CC.
214
-
215
- ## Calendar / TimePicker
216
-
217
- ```tsx
218
- const [date, setDate] = useState<CalendarValue>(null)
219
- <Calendar mode="single" value={date} onChange={setDate} min={min} max={max} />
220
- <Calendar mode="range" value={range} onChange={setRange} />
221
- <Calendar disabled={(day) => day.getDay() === 0} />
222
-
223
- <TimePicker value={time} onChange={setTime} withSeconds step={15} hour12 />
224
- ```
225
-
226
- La saisie manuelle accepte `jj/mm/aaaa`, `jj-mm-aaaa` et `aaaa-mm-jj`.
227
-
228
- ## Audio / Stories
229
-
230
- ```tsx
231
- <AudioPlayer src="/audio.wav" title="Titre" cover="/cover.svg" accent="#facc15" />
232
- <AudioPlayer variant="compact" src="/audio.wav" title="Titre" />
233
-
234
- <Stories items={stories} open={open} onOpenChange={setOpen} duration={5000} />
235
- ```
236
-
237
- Une story peut être une image, une vidéo ou un slide texte avec son propre fond et sa durée.
238
-
239
- ## Éditeur / Upload / Raccourcis / Panneaux / OTP
240
-
241
- ```tsx
242
- <RichTextEditor value={html} onChange={setHtml} toolbar />
243
-
244
- <FileUpload accept="image/*,video/*" maxFiles={3} uploader={upload} onChange={setFiles} />
245
-
246
- <CommandMenu items={items} open={open} onOpenChange={setOpen} />
247
- useHotkeys({ 'mod+k': () => setOpen(true) })
248
-
249
- <ResizablePanelGroup direction="horizontal">
250
- <ResizablePanel defaultSize={34} minSize={20}>…</ResizablePanel>
251
- <ResizableHandle />
252
- <ResizablePanel defaultSize={66} minSize={25}>…</ResizablePanel>
253
- </ResizablePanelGroup>
254
-
255
- <OtpInput length={4} value={code} onChange={setCode} onComplete={valider} />
256
- ```
257
-
258
- L'OTP aligne de 4 à 8 champs, forme un cercle à la validation puis affiche une coche.
259
- L'animation respecte `prefers-reduced-motion`.
260
-
261
- ## Animations
262
-
263
- Chaque animation a sa propre page documentée : `#/animations/otp` pour l'OTP, puis
264
- `#/animations/accordeon-horizontal` et `#/animations/storytelling` pour les cartes
265
- horizontales, et `#/animations/empilement` pour les cartes qui s'empilent au scroll avec
266
- une couleur de texte pilotée par la progression. Les couleurs suivent `--primary`. Le
267
- nombre de cartes se règle de 4 à 8 directement dans chaque aperçu. Ces démos autonomes ne
268
- sont pas installables via la CLI.
269
-
270
- ## Publier le paquet
271
-
272
- Le paquet publié ne contient que la CLI, les composants et les helpers (`files`) : aucune
273
- dépendance d'exécution, React et Tailwind restent en `devDependencies` pour ne pas les
274
- installer chez ceux qui lancent `npx uifolio`.
99
+ ## Publishing the package
275
100
 
276
101
  ```bash
277
- npm login # une fois par machine
278
- npm profile enable-2fa auth-and-writes # npm refuse de publier sans 2FA
279
- npm publish --otp 123456 # prepublishOnly vérifie cli/uifolio.mjs
102
+ npm publish
280
103
  ```
281
104
 
282
- Un jeton *granular* avec « Bypass 2FA » fonctionne aussi, mais npm restreint
283
- progressivement ces jetons pour la publication : la 2FA + `--otp` est la voie sûre.
284
-
285
- Ensuite `npx uifolio@latest add <composant>` et `pnpm dlx uifolio@latest add <composant>`
286
- fonctionnent.
287
-
288
- ### Publication automatique (GitHub Actions)
105
+ Automatic publishing is wired in `.github/workflows/publish.yml` and runs on tag push.
289
106
 
290
- `.github/workflows/publish.yml` publie sur npm à chaque tag `v*` : il vérifie que le tag
291
- correspond à la version de `package.json`, installe avec `npm ci`, lance `oxlint`,
292
- `npm pack --dry-run` et `npm test`, puis publie via **trusted publishing** (OIDC, aucun
293
- token stocké). Un second job crée ensuite la **GitHub Release** du tag avec les notes
294
- générées à partir des commits.
295
-
296
- ```bash
297
- npm version patch # 0.3.0 → 0.3.1, commit + tag v0.3.1
298
- git push --follow-tags # déclenche le workflow
299
- ```
107
+ ### Automatic publishing (GitHub Actions)
300
108
 
301
- Prérequis, une seule fois :
109
+ The workflow publishes on tag `v*`. Run `git tag vX.Y.Z && git push --tags` to publish.
302
110
 
303
- 1. première publication manuelle (ci-dessus) — le paquet doit exister sur npm ;
304
- 2. npmjs.com → `uifolio` → Settings → **Trusted publishing** → GitHub Actions :
305
- organisation `Barronakn`, dépôt `UIfolio`, workflow `publish.yml`,
306
- action « npm publish » autorisée ;
307
- 3. une configuration neuve doit réussir sa première publication **sous 2 jours**,
308
- sinon elle expire et il faut la recréer.
111
+ More info: [README on GitHub](https://github.com/Barronakn/UIfolio#readme).
309
112
 
310
- Le workflow doit être présent sur le commit taggé. Après activation, tu peux durcir le
311
- paquet avec Settings → Publishing access → « Require two-factor authentication and
312
- disallow tokens ».
113
+ License: [MIT](https://opensource.org/license/mit).