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.
@@ -1,9 +1,9 @@
1
1
  export interface UploaderOptions {
2
- /** Endpoint de signature. Défaut : /api/upload/sign */
2
+ /** Signing endpoint. Default: /api/upload/sign */
3
3
  signEndpoint?: string
4
- /** Nombre de tentatives supplémentaires en cas d'échec. Défaut : 2 */
4
+ /** Number of extra attempts on failure. Default: 2 */
5
5
  retries?: number
6
- /** Délai de base entre les tentatives (ms). Défaut : 800 */
6
+ /** Base delay between attempts (ms). Default: 800 */
7
7
  retryDelayMs?: number
8
8
  }
9
9
 
@@ -42,7 +42,7 @@ function putWithProgress(
42
42
  onProgress(1)
43
43
  resolvePromise()
44
44
  } else {
45
- reject(new UploadError(`Le serveur a répondu ${request.status}`, request.status))
45
+ reject(new UploadError(`The server returned ${request.status}`, request.status))
46
46
  }
47
47
  }
48
48
  request.onerror = () => reject(new UploadError('Connexion interrompue pendant l’envoi'))
@@ -52,12 +52,12 @@ function putWithProgress(
52
52
  }
53
53
 
54
54
  /**
55
- * Uploader « réel » branché sur /api/upload/sign :
56
- * 1. demande une URL signée au serveur,
57
- * 2. envoie le fichier en PUT avec progression (XHR),
58
- * 3. réessaie automatiquement en réutilisant la même URL signée (reprise).
55
+ * “Real” uploader wired to /api/upload/sign:
56
+ * 1. asks the server for a signed URL,
57
+ * 2. sends the file with a PUT and progress (XHR),
58
+ * 3. retries automatically, reusing the same signed URL (resume).
59
59
  *
60
- * Renvoie l'URL publique du fichier déposé.
60
+ * Returns the public URL of the uploaded file.
61
61
  */
62
62
  export function createUploader(options: UploaderOptions = {}) {
63
63
  const {
@@ -73,7 +73,7 @@ export function createUploader(options: UploaderOptions = {}) {
73
73
  body: JSON.stringify({ name: file.name, size: file.size, type: file.type }),
74
74
  })
75
75
  if (!signResponse.ok) {
76
- throw new UploadError(`Signature refusée (${signResponse.status})`, signResponse.status)
76
+ throw new UploadError(`Signature refused (${signResponse.status})`, signResponse.status)
77
77
  }
78
78
  const signed = (await signResponse.json()) as SignedUpload
79
79
 
@@ -85,7 +85,7 @@ export function createUploader(options: UploaderOptions = {}) {
85
85
  } catch (error) {
86
86
  lastError = error
87
87
  const status = error instanceof UploadError ? error.status : undefined
88
- // 4xx = refus définitif (taille, clé, auth) : inutile de réessayer
88
+ // 4xx = final refusal (size, key, auth): no point retrying
89
89
  if (status !== undefined && status >= 400 && status < 500) break
90
90
  if (attempt < retries) {
91
91
  await new Promise((resolvePromise) =>
@@ -96,11 +96,11 @@ export function createUploader(options: UploaderOptions = {}) {
96
96
  }
97
97
  throw lastError instanceof Error
98
98
  ? lastError
99
- : new UploadError('Échec de l’envoi après plusieurs tentatives')
99
+ : new UploadError('Upload failed after several attempts')
100
100
  }
101
101
  }
102
102
 
103
- /** File d'attente par URL signée : permet la reprise d'un envoi interrompu. */
103
+ /** Queue per signed URL: allows resuming an interrupted upload. */
104
104
  export function resumeUpload(
105
105
  signed: SignedUpload,
106
106
  file: File,
@@ -1,14 +1,14 @@
1
1
  import { useCallback, useEffect, useRef, useState } from 'react'
2
2
 
3
3
  /**
4
- * Retourne un état local piloté par une prop quand celle-ci est explicite.
4
+ * Returns local state driven by a prop when that prop is explicit.
5
5
  *
6
- * Quand le parent fournit explicitement la prop, l'état local se
7
- * synchronise dessus. Quand la prop est `undefined`, le composant garde
8
- * sa propre valeur locale.
6
+ * When the parent explicitly provides the prop, the local state stays
7
+ * in sync with it. When the prop is `undefined`, the component keeps
8
+ * its own local value.
9
9
  *
10
- * Cette version est compatible avec React Compiler : elle n'accède jamais
11
- * aux refs pendant le rendu.
10
+ * This version is compatible with React Compiler: it never reads
11
+ * refs during render.
12
12
  */
13
13
  export function useControllableValue<T>(
14
14
  prop: T | undefined,
@@ -21,14 +21,14 @@ export function useControllableValue<T>(
21
21
 
22
22
  const isControlled = prop !== undefined
23
23
 
24
- // Synchronise l'état local quand le contrôle parent change.
24
+ // Keeps the local state in sync when the parent control changes.
25
25
  useEffect(() => {
26
26
  controlledRef.current = isControlled
27
27
  previousPropRef.current = prop
28
28
  })
29
29
 
30
- // Premier rendu contrôlé : la prop doit remplacer l'état local.
31
- // Passages ultérieurs : synchronise seulement si la prop a changé.
30
+ // First controlled render: the prop must replace the local state.
31
+ // Later renders: only sync when the prop changed.
32
32
  useEffect(() => {
33
33
  if (!isControlled) return
34
34
  const next = prop as T
@@ -25,7 +25,7 @@ function matches(pattern: string, event: KeyboardEvent): boolean {
25
25
  return event.key.toLowerCase() === key
26
26
  }
27
27
 
28
- /** Enregistre des raccourcis globaux : `mod+k`, `shift+?`, `escape`, `arrowup`… */
28
+ /** Registers global shortcuts: `mod+k`, `shift+?`, `escape`, `arrowup`… */
29
29
  export function useHotkeys(map: HotkeyMap, options: { enabled?: boolean } = {}) {
30
30
  const handlers = useRef(map)
31
31
  const enabled = options.enabled ?? true
@@ -21,8 +21,8 @@ function applyTheme(theme: Theme) {
21
21
  }
22
22
 
23
23
  /**
24
- * Thème clair/sombre persistant. `system` suit la préférence système
25
- * tant que l'utilisateur n'a pas fait de choix explicite.
24
+ * Persistent light/dark theme. `system` follows the system preference
25
+ * until the user makes an explicit choice.
26
26
  */
27
27
  export function useTheme() {
28
28
  const [theme, setThemeState] = useState<Theme>(readTheme)
package/src/lib/video.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
- * Helpers vidéo partagés par les composants de la librairie.
3
- * Aucune dépendance : copier ce fichier avec le composant suffit.
2
+ * Video helpers shared by the library components.
3
+ * No dependency: copying this file along with the component is enough.
4
4
  */
5
5
 
6
6
  export type PlayerSource =
@@ -28,7 +28,7 @@ export function toSourceList(input: PlayerSource | PlayerSource[]): PlayerSource
28
28
  return Array.isArray(input) ? input : [input]
29
29
  }
30
30
 
31
- /** Signature stable, utilisée pour ne pas recréer les object URLs à chaque render. */
31
+ /** Stable signature, so object URLs are not recreated on every render. */
32
32
  export function sourceSignature(input: PlayerSource | PlayerSource[]): string {
33
33
  return toSourceList(input)
34
34
  .map((source) => {
@@ -75,7 +75,7 @@ export function youtubeThumbnailFallback(id: string): string {
75
75
  loop?: boolean
76
76
  muted?: boolean
77
77
  controls?: boolean
78
- /** Origine de la page hôte : l'API iframe YouTube lui associe les commandes postMessage. */
78
+ /** Host page origin: the YouTube iframe API matches postMessage commands to it. */
79
79
  origin?: string
80
80
  playsinline?: boolean
81
81
  }
@@ -123,7 +123,7 @@ export function buildEmbedUrl(
123
123
 
124
124
 
125
125
  /* -------------------------------------------------------------------------- */
126
- /* Pilotage des embeds (YouTube / Vimeo) par postMessage */
126
+ /* Driving the embeds (YouTube / Vimeo) through postMessage */
127
127
  /* -------------------------------------------------------------------------- */
128
128
 
129
129
  export type EmbedKind = 'youtube' | 'vimeo'
@@ -132,12 +132,12 @@ export type EmbedCommand =
132
132
  | { type: 'play' }
133
133
  | { type: 'pause' }
134
134
  | { type: 'seek'; time: number }
135
- /** Volume normalisé 0–1, converti dans l'unité de chaque plateforme. */
135
+ /** Normalized volume 0–1, converted into each platform's unit. */
136
136
  | { type: 'volume'; volume: number }
137
137
  | { type: 'mute'; muted: boolean }
138
138
  | { type: 'rate'; rate: number }
139
139
 
140
- /** Message à poster dans l'iframe : chaîne JSON pour YouTube, objet pour Vimeo. */
140
+ /** Message to post into the iframe: a JSON string for YouTube, an object for Vimeo. */
141
141
  export function embedCommand(
142
142
  kind: EmbedKind,
143
143
  command: EmbedCommand,
@@ -190,10 +190,10 @@ const VIMEO_EVENTS = [
190
190
  'playbackratechange',
191
191
  ]
192
192
 
193
- /** Messages à envoyer à l'iframe pour recevoir son état et ses événements. */
193
+ /** Messages to send to the iframe to receive its state and events. */
194
194
  export function embedHandshake(kind: EmbedKind): Array<Record<string, unknown> | string> {
195
195
  if (kind === 'youtube') {
196
- // Démarre l'émission des messages `infoDelivery` (temps, durée, état).
196
+ // Starts the `infoDelivery` messages (time, duration, state).
197
197
  return [JSON.stringify({ event: 'listening', id: 1, channel: 'widget' })]
198
198
  }
199
199
  return [
@@ -249,7 +249,7 @@ function parseYoutubeStatus(raw: unknown): EmbedStatus | null {
249
249
  if (duration !== undefined && duration > 0) status.duration = duration
250
250
 
251
251
  const playerState = toNumber(info.playerState)
252
- // -1 non démarré, 0 terminé, 1 lecture, 2 pause, 3 buffering, 5 prêt.
252
+ // -1 unstarted, 0 ended, 1 playing, 2 paused, 3 buffering, 5 cued.
253
253
  if (playerState !== undefined) status.playing = playerState === 1 || playerState === 3
254
254
 
255
255
  const muted = info.muted
@@ -312,7 +312,7 @@ function parseVimeoStatus(raw: unknown): EmbedStatus | null {
312
312
  return null
313
313
  }
314
314
 
315
- /** Décode un message postMessage émis par l'iframe et en extrait l'état de lecture. */
315
+ /** Decodes a postMessage sent by the iframe and extracts the playback state. */
316
316
  export function parseEmbedMessage(kind: EmbedKind, raw: unknown): EmbedStatus | null {
317
317
  return kind === 'youtube' ? parseYoutubeStatus(raw) : parseVimeoStatus(raw)
318
318
  }
@@ -335,7 +335,7 @@ export function formatTime(value: number): string {
335
335
  return `${minutes}:${paddedSeconds}`
336
336
  }
337
337
 
338
- /** "16/9" | 16/9 | 1.77 | "auto" -> valeur CSS aspect-ratio. */
338
+ /** "16/9" | 16/9 | 1.77 | "auto" -> CSS aspect-ratio value. */
339
339
  export function toAspectRatio(value: string | number | undefined): string | undefined {
340
340
  if (value == null) return undefined
341
341
  if (typeof value === 'number') return String(value)
package/src/lib/vtt.ts CHANGED
@@ -57,7 +57,7 @@ function cleanCueText(lines: string[]): string {
57
57
  .trim()
58
58
  }
59
59
 
60
- /** Parse un fichier WebVTT en cues. Tolérant : ignore entêtes, notes, styles et blocs invalides. */
60
+ /** Parses a WebVTT file into cues. Tolerant: skips headers, notes, styles and invalid blocks. */
61
61
  export function parseVtt(raw: string): VttCue[] {
62
62
  const lines = raw
63
63
  .replace(/^\uFEFF/, '')