trix-ui 0.2.3 → 0.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trix-ui",
3
- "version": "0.2.3",
3
+ "version": "0.2.4",
4
4
  "description": "Lite UI CLI, registry tooling, and templates.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -164,11 +164,12 @@ const MusicPlayerCardBase = React.forwardRef<HTMLDivElement, MusicPlayerCardProp
164
164
  const [trackIndex, setTrackIndex] = React.useState(
165
165
  clamp(initialTrackIndex, 0, Math.max((tracks?.length ?? 1) - 1, 0))
166
166
  );
167
- const [uploadedFile, setUploadedFile] = React.useState<File | null>(null);
168
- const [currentTimeSec, setCurrentTimeSec] = React.useState(0);
169
- const [durationSec, setDurationSec] = React.useState(0);
170
- const audioRef = React.useRef<HTMLAudioElement | null>(null);
171
- const shuffleHistoryRef = React.useRef<number[]>([]);
167
+ const [uploadedFile, setUploadedFile] = React.useState<File | null>(null);
168
+ const [currentTimeSec, setCurrentTimeSec] = React.useState(0);
169
+ const [durationSec, setDurationSec] = React.useState(0);
170
+ const [playbackError, setPlaybackError] = React.useState<string | null>(null);
171
+ const audioRef = React.useRef<HTMLAudioElement | null>(null);
172
+ const shuffleHistoryRef = React.useRef<number[]>([]);
172
173
 
173
174
  const isShuffle = isShuffleOn ?? internalShuffle;
174
175
  const isRepeat = isRepeatOn ?? internalRepeat;
@@ -186,16 +187,24 @@ const MusicPlayerCardBase = React.forwardRef<HTMLDivElement, MusicPlayerCardProp
186
187
  ? playlist[clamp(trackIndex, 0, playlist.length - 1)]
187
188
  : fallbackTrack;
188
189
 
189
- const propFileUrl = useObjectUrl(audioFile);
190
- const uploadedFileUrl = useObjectUrl(uploadedFile ?? undefined);
191
- const resolvedSrc = propFileUrl ?? uploadedFileUrl ?? activeTrack.src;
192
- const isPlayable = Boolean(resolvedSrc);
193
-
194
- const derivedProgress =
195
- durationSec > 0 ? clamp((currentTimeSec / durationSec) * 100, 0, 100) : 0;
196
- const pct = clamp(progress ?? derivedProgress, 0, 100);
197
- const resolvedCurrentTime = currentTime ?? formatTime(currentTimeSec);
198
- const resolvedTotalTime = totalTime ?? formatTime(durationSec);
190
+ const propFileUrl = useObjectUrl(audioFile);
191
+ const uploadedFileUrl = useObjectUrl(uploadedFile ?? undefined);
192
+ const resolvedSrc = propFileUrl ?? uploadedFileUrl ?? activeTrack.src;
193
+ const isPlayable = Boolean(resolvedSrc);
194
+ const hasExplicitSource =
195
+ Boolean(audioSrc) ||
196
+ Boolean(audioFile) ||
197
+ Boolean(uploadedFile) ||
198
+ Boolean(playlist.length && activeTrack.src);
199
+ const missingSourceMessage =
200
+ !resolvedSrc && hasExplicitSource ? "Audio file not found." : null;
201
+
202
+ const derivedProgress =
203
+ durationSec > 0 ? clamp((currentTimeSec / durationSec) * 100, 0, 100) : 0;
204
+ const pct = clamp(progress ?? derivedProgress, 0, 100);
205
+ const resolvedCurrentTime = currentTime ?? formatTime(currentTimeSec);
206
+ const resolvedTotalTime = totalTime ?? formatTime(durationSec);
207
+ const errorMessage = playbackError ?? missingSourceMessage;
199
208
 
200
209
  const requestPlay = React.useCallback(
201
210
  async (nextPlaying: boolean): Promise<void> => {
@@ -210,16 +219,21 @@ const MusicPlayerCardBase = React.forwardRef<HTMLDivElement, MusicPlayerCardProp
210
219
  } else {
211
220
  audio.pause();
212
221
  }
213
- } catch {
214
- if (isPlaying === undefined) {
215
- setInternalPlaying(false);
216
- }
217
- onPlayStateChange?.(false);
218
- }
219
- },
220
- [isPlaying, onPlayStateChange, resolvedSrc]
221
- );
222
-
222
+ } catch {
223
+ if (isPlaying === undefined) {
224
+ setInternalPlaying(false);
225
+ }
226
+ setPlaybackError("Audio file is not playable.");
227
+ onPlayStateChange?.(false);
228
+ }
229
+ },
230
+ [isPlaying, onPlayStateChange, resolvedSrc]
231
+ );
232
+
233
+ React.useEffect(() => {
234
+ setPlaybackError(null);
235
+ }, [resolvedSrc]);
236
+
223
237
  React.useEffect(() => {
224
238
  const audio = audioRef.current;
225
239
  if (!audio) return;
@@ -420,19 +434,27 @@ const MusicPlayerCardBase = React.forwardRef<HTMLDivElement, MusicPlayerCardProp
420
434
  )}
421
435
  {...props}
422
436
  >
423
- <audio
424
- ref={audioRef}
425
- preload="metadata"
426
- onTimeUpdate={(event) => {
427
- const audio = event.currentTarget;
428
- setCurrentTimeSec(audio.currentTime);
429
- }}
430
- onLoadedMetadata={(event) => {
431
- const audio = event.currentTarget;
432
- setDurationSec(audio.duration || 0);
433
- }}
434
- onEnded={handleEnded}
435
- />
437
+ <audio
438
+ ref={audioRef}
439
+ preload="metadata"
440
+ onTimeUpdate={(event) => {
441
+ const audio = event.currentTarget;
442
+ setCurrentTimeSec(audio.currentTime);
443
+ }}
444
+ onLoadedMetadata={(event) => {
445
+ const audio = event.currentTarget;
446
+ setDurationSec(audio.duration || 0);
447
+ setPlaybackError(null);
448
+ }}
449
+ onError={() => {
450
+ setPlaybackError("Audio file is not playable.");
451
+ if (isPlaying === undefined) {
452
+ setInternalPlaying(false);
453
+ }
454
+ onPlayStateChange?.(false);
455
+ }}
456
+ onEnded={handleEnded}
457
+ />
436
458
  {/* Album Art */}
437
459
  <div
438
460
  className={cn(
@@ -498,10 +520,13 @@ const MusicPlayerCardBase = React.forwardRef<HTMLDivElement, MusicPlayerCardProp
498
520
  />
499
521
  </div>
500
522
 
501
- <div className="flex justify-between text-[10px] text-stone-400 font-mono mb-4">
502
- <span>{resolvedCurrentTime}</span>
503
- <span>{resolvedTotalTime}</span>
504
- </div>
523
+ <div className="flex justify-between text-[10px] text-stone-400 font-mono mb-4">
524
+ <span>{resolvedCurrentTime}</span>
525
+ <span>{resolvedTotalTime}</span>
526
+ </div>
527
+ {errorMessage ? (
528
+ <p className="mb-3 text-xs text-rose-500">{errorMessage}</p>
529
+ ) : null}
505
530
 
506
531
  {/* Controls */}
507
532
  <div className={cn("flex justify-between items-center mt-auto px-2", controlsClassName)}>