add extra colours in index.css and replace hex values
This commit is contained in:
@@ -76,8 +76,8 @@ const Topbar = ({
|
|||||||
{user.name}
|
{user.name}
|
||||||
{isStreaming && (
|
{isStreaming && (
|
||||||
<span className="relative flex h-2 w-2">
|
<span className="relative flex h-2 w-2">
|
||||||
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-[#B5432E] opacity-75" />
|
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-live opacity-75" />
|
||||||
<span className="relative inline-flex rounded-full h-2 w-2 bg-[#B5432E]" />
|
<span className="relative inline-flex rounded-full h-2 w-2 bg-live" />
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
@@ -86,7 +86,7 @@ const Topbar = ({
|
|||||||
onClick={() => navigate("/profile")} />
|
onClick={() => navigate("/profile")} />
|
||||||
<DropdownItem item="Logout"
|
<DropdownItem item="Logout"
|
||||||
onClick={() => handleLogout()}
|
onClick={() => handleLogout()}
|
||||||
className={"text-[#B5432E] font-medium"} />
|
className={"text-error font-medium"} />
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
</div>
|
</div>
|
||||||
) :
|
) :
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ type Props = React.ButtonHTMLAttributes<HTMLButtonElement>;
|
|||||||
const PrimaryButton: React.FC<Props> = ({ className = "", ...props }) => {
|
const PrimaryButton: React.FC<Props> = ({ className = "", ...props }) => {
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
className={`bg-[#A85A38] text-[#FDFBF7] font-body text-sm rounded-lg hover:bg-[#94492A] disabled:bg-[#C9AE9C] transition-colors duration-150 h-10 px-4 ${className}`}
|
className={`bg-terracotta text-on-accent font-body text-sm rounded-lg hover:bg-terracotta-hover disabled:bg-terracotta-disabled transition-colors duration-150 h-10 px-4 ${className}`}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ const VideoCard = ({ id, title, duration, createdAt, onEdit, onDelete, className
|
|||||||
className="w-full aspect-video object-cover group-hover:scale-105 transition-transform duration-200"
|
className="w-full aspect-video object-cover group-hover:scale-105 transition-transform duration-200"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<span className="absolute top-1.5 left-1.5 bg-[#2B2620]/75 text-[#FBF8F2] px-1.5 py-0.5 rounded text-xs font-data pointer-events-none">
|
<span className="absolute top-1.5 left-1.5 bg-text-primary/75 text-on-dark px-1.5 py-0.5 rounded text-xs font-data pointer-events-none">
|
||||||
{formatTime(duration)}
|
{formatTime(duration)}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
@@ -82,7 +82,7 @@ const VideoCard = ({ id, title, duration, createdAt, onEdit, onDelete, className
|
|||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
onClick={(e) => stopAndRun(e, () => { setMenuOpen(v => !v); setConfirmDelete(false); })}
|
onClick={(e) => stopAndRun(e, () => { setMenuOpen(v => !v); setConfirmDelete(false); })}
|
||||||
className="bg-[#2B2620]/75 hover:bg-[#2B2620]/90 text-[#FBF8F2] rounded p-0.5 transition-colors"
|
className="bg-text-primary/75 hover:bg-text-primary/90 text-on-dark rounded p-0.5 transition-colors"
|
||||||
>
|
>
|
||||||
<MoreVertical size={14} />
|
<MoreVertical size={14} />
|
||||||
</button>
|
</button>
|
||||||
@@ -101,7 +101,7 @@ const VideoCard = ({ id, title, duration, createdAt, onEdit, onDelete, className
|
|||||||
{onDelete && (
|
{onDelete && (
|
||||||
<button
|
<button
|
||||||
onClick={(e) => stopAndRun(e, () => setConfirmDelete(true))}
|
onClick={(e) => stopAndRun(e, () => setConfirmDelete(true))}
|
||||||
className="flex items-center gap-2 w-full px-3 py-2 text-sm text-[#B5432E] hover:bg-hover"
|
className="flex items-center gap-2 w-full px-3 py-2 text-sm text-error hover:bg-hover"
|
||||||
>
|
>
|
||||||
<Trash2 size={13} /> Delete
|
<Trash2 size={13} /> Delete
|
||||||
</button>
|
</button>
|
||||||
@@ -113,7 +113,7 @@ const VideoCard = ({ id, title, duration, createdAt, onEdit, onDelete, className
|
|||||||
<div className="flex gap-1.5">
|
<div className="flex gap-1.5">
|
||||||
<button
|
<button
|
||||||
onClick={(e) => stopAndRun(e, onDelete!)}
|
onClick={(e) => stopAndRun(e, onDelete!)}
|
||||||
className="flex-1 text-xs bg-[#B5432E] text-[#FDFBF7] rounded px-2 py-1 hover:bg-[#94492A] transition-colors"
|
className="flex-1 text-xs bg-error text-on-accent rounded px-2 py-1 hover:bg-terracotta-hover transition-colors"
|
||||||
>
|
>
|
||||||
Delete
|
Delete
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -19,9 +19,28 @@
|
|||||||
--color-text-secondary: #6E6559;
|
--color-text-secondary: #6E6559;
|
||||||
--color-muted: #9A9084;
|
--color-muted: #9A9084;
|
||||||
|
|
||||||
|
/* ── Accents ─────────────────────────────────────────────────────────── */
|
||||||
|
--color-terracotta: #A85A38;
|
||||||
|
--color-terracotta-hover: #94492A;
|
||||||
|
--color-terracotta-light: #C87A52;
|
||||||
|
--color-terracotta-disabled: #C9AE9C;
|
||||||
|
--color-olive: #6E7A50;
|
||||||
|
|
||||||
|
/* ── Status ──────────────────────────────────────────────────────────── */
|
||||||
|
--color-live: #B5432E;
|
||||||
|
--color-error: #B5432E;
|
||||||
|
|
||||||
|
/* ── Ink on dark / accent surfaces ───────────────────────────────────── */
|
||||||
|
--color-on-dark: #FBF8F2;
|
||||||
|
--color-on-accent: #FDFBF7;
|
||||||
|
|
||||||
/* ── Lines, chips, placeholders ──────────────────────────────────────── */
|
/* ── Lines, chips, placeholders ──────────────────────────────────────── */
|
||||||
--color-hairline: #F0EBE2;
|
--color-hairline: #F0EBE2;
|
||||||
--color-hover: #E7E0D2;
|
--color-hover: #E7E0D2;
|
||||||
|
--color-selected: #F6EFE6;
|
||||||
|
--color-ring: #E2DACB;
|
||||||
|
--color-inactive: #DED7CA;
|
||||||
|
--color-inactive-strong: #C4BAA9;
|
||||||
}
|
}
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
@@ -43,7 +62,7 @@ body {
|
|||||||
@utility font-heading {
|
@utility font-heading {
|
||||||
font-family: "Newsreader", serif;
|
font-family: "Newsreader", serif;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
color: #2B2620;
|
color: var(--color-text-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Clip trim slider (react-range-slider-input). Its own stylesheet ships blue
|
/* Clip trim slider (react-range-slider-input). Its own stylesheet ships blue
|
||||||
@@ -55,16 +74,16 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#range-slider .range-slider__range {
|
#range-slider .range-slider__range {
|
||||||
background: #C87A52;
|
background: var(--color-terracotta-light);
|
||||||
}
|
}
|
||||||
|
|
||||||
#range-slider .range-slider__thumb {
|
#range-slider .range-slider__thumb {
|
||||||
width: 18px;
|
width: 18px;
|
||||||
height: 18px;
|
height: 18px;
|
||||||
background: #A85A38;
|
background: var(--color-terracotta);
|
||||||
box-shadow: 0 1px 2px rgba(90, 40, 20, 0.2);
|
box-shadow: 0 1px 2px rgba(90, 40, 20, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#range-slider .range-slider__thumb:focus-visible {
|
#range-slider .range-slider__thumb:focus-visible {
|
||||||
box-shadow: 0 0 0 4px rgba(168, 90, 56, 0.35);
|
box-shadow: 0 0 0 4px color-mix(in oklab, var(--color-terracotta) 35%, transparent);
|
||||||
}
|
}
|
||||||
@@ -142,7 +142,7 @@ const ClipEdit = () => {
|
|||||||
<Playbar
|
<Playbar
|
||||||
video={videoRef.current}
|
video={videoRef.current}
|
||||||
videoMetadata={metadata}
|
videoMetadata={metadata}
|
||||||
className={"w-full accent-[#A85A38]"}
|
className={"w-full accent-terracotta"}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<PlaybackSlider
|
<PlaybackSlider
|
||||||
@@ -150,7 +150,7 @@ const ClipEdit = () => {
|
|||||||
videoMetadata={metadata}
|
videoMetadata={metadata}
|
||||||
sliderValue={playbackValue}
|
sliderValue={playbackValue}
|
||||||
setSliderValue={setPlaybackValue}
|
setSliderValue={setPlaybackValue}
|
||||||
className={"w-full accent-[#A85A38]"}
|
className={"w-full accent-terracotta"}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ClipRangeSlider
|
<ClipRangeSlider
|
||||||
@@ -162,7 +162,7 @@ const ClipEdit = () => {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{error && (
|
{error && (
|
||||||
<div className={"text-[#B5432E] text-center mt-2"}>
|
<div className={"text-error text-center mt-2"}>
|
||||||
{error}
|
{error}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ const EditClip = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{error && <p className="text-sm text-[#B5432E]">{error}</p>}
|
{error && <p className="text-sm text-error">{error}</p>}
|
||||||
|
|
||||||
<div className="flex items-center gap-3 pt-1">
|
<div className="flex items-center gap-3 pt-1">
|
||||||
<PrimaryButton onClick={handleSave} disabled={saving} className="w-28">
|
<PrimaryButton onClick={handleSave} disabled={saving} className="w-28">
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ const EditVod = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{error && <p className="text-sm text-[#B5432E]">{error}</p>}
|
{error && <p className="text-sm text-error">{error}</p>}
|
||||||
|
|
||||||
<div className="flex items-center gap-3 pt-1">
|
<div className="flex items-center gap-3 pt-1">
|
||||||
<PrimaryButton onClick={handleSave} disabled={saving} className="w-28">
|
<PrimaryButton onClick={handleSave} disabled={saving} className="w-28">
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ const Home = () => {
|
|||||||
className="bg-card border border-hairline rounded-xl p-5 flex flex-col gap-3 shadow-sm"
|
className="bg-card border border-hairline rounded-xl p-5 flex flex-col gap-3 shadow-sm"
|
||||||
>
|
>
|
||||||
<div className="w-9 h-9 rounded-lg flex items-center justify-center shrink-0">
|
<div className="w-9 h-9 rounded-lg flex items-center justify-center shrink-0">
|
||||||
<Icon size={18} className="text-[#A85A38]" />
|
<Icon size={18} className="text-terracotta" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="font-body text-text-strong font-bold text-sm mb-1">{title}</p>
|
<p className="font-body text-text-strong font-bold text-sm mb-1">{title}</p>
|
||||||
|
|||||||
@@ -233,7 +233,7 @@ const MediaPlayer = ({ noun, getMediaUrl, fetchDetails }: MediaPlayerProps) => {
|
|||||||
{/* Player */}
|
{/* Player */}
|
||||||
<div
|
<div
|
||||||
ref={containerRef}
|
ref={containerRef}
|
||||||
className="relative bg-[#2B2620] rounded-xl overflow-hidden flex-1 min-h-0 select-none"
|
className="relative bg-text-primary rounded-xl overflow-hidden flex-1 min-h-0 select-none"
|
||||||
onMouseMove={showControls}
|
onMouseMove={showControls}
|
||||||
onMouseLeave={() => isPlaying && setControlsVisible(false)}
|
onMouseLeave={() => isPlaying && setControlsVisible(false)}
|
||||||
onClick={togglePlay}
|
onClick={togglePlay}
|
||||||
@@ -265,7 +265,7 @@ const MediaPlayer = ({ noun, getMediaUrl, fetchDetails }: MediaPlayerProps) => {
|
|||||||
{/* Buffering spinner */}
|
{/* Buffering spinner */}
|
||||||
{isWaiting && mediaUrl && (
|
{isWaiting && mediaUrl && (
|
||||||
<div className="absolute inset-0 flex items-center justify-center pointer-events-none">
|
<div className="absolute inset-0 flex items-center justify-center pointer-events-none">
|
||||||
<div className="w-11 h-11 rounded-full border-[3px] border-[#FBF8F2]/20 border-t-[#FBF8F2] animate-spin" />
|
<div className="w-11 h-11 rounded-full border-[3px] border-on-dark/20 border-t-on-dark animate-spin" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -282,20 +282,20 @@ const MediaPlayer = ({ noun, getMediaUrl, fetchDetails }: MediaPlayerProps) => {
|
|||||||
{/* Scrub bar */}
|
{/* Scrub bar */}
|
||||||
<div
|
<div
|
||||||
ref={progressRef}
|
ref={progressRef}
|
||||||
className="relative h-1 rounded-full bg-[#FBF8F2]/25 cursor-pointer mb-3 group/prog hover:h-1.5 transition-[height] duration-150"
|
className="relative h-1 rounded-full bg-on-dark/25 cursor-pointer mb-3 group/prog hover:h-1.5 transition-[height] duration-150"
|
||||||
onMouseDown={(e) => { setIsDragging(true); seekTo(e.clientX); }}
|
onMouseDown={(e) => { setIsDragging(true); seekTo(e.clientX); }}
|
||||||
>
|
>
|
||||||
<div className="absolute inset-y-0 left-0 bg-[#FBF8F2]/35 rounded-full" style={{ width: `${bufferedPct}%` }} />
|
<div className="absolute inset-y-0 left-0 bg-on-dark/35 rounded-full" style={{ width: `${bufferedPct}%` }} />
|
||||||
<div className="absolute inset-y-0 left-0 bg-[#C87A52] rounded-full" style={{ width: `${progressPct}%` }} />
|
<div className="absolute inset-y-0 left-0 bg-terracotta-light rounded-full" style={{ width: `${progressPct}%` }} />
|
||||||
<div
|
<div
|
||||||
className="absolute top-1/2 -translate-y-1/2 w-3 h-3 bg-[#FBF8F2] rounded-full shadow opacity-0 group-hover/prog:opacity-100 transition-opacity"
|
className="absolute top-1/2 -translate-y-1/2 w-3 h-3 bg-on-dark rounded-full shadow opacity-0 group-hover/prog:opacity-100 transition-opacity"
|
||||||
style={{ left: `calc(${progressPct}% - 6px)` }}
|
style={{ left: `calc(${progressPct}% - 6px)` }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Buttons row */}
|
{/* Buttons row */}
|
||||||
<div className="flex items-center gap-3 text-[#FBF8F2]">
|
<div className="flex items-center gap-3 text-on-dark">
|
||||||
<button onClick={togglePlay} className="hover:text-[#FBF8F2]/70 transition-colors">
|
<button onClick={togglePlay} className="hover:text-on-dark/70 transition-colors">
|
||||||
{isPlaying
|
{isPlaying
|
||||||
? <Pause size={20} fill="currentColor" />
|
? <Pause size={20} fill="currentColor" />
|
||||||
: <Play size={20} fill="currentColor" />}
|
: <Play size={20} fill="currentColor" />}
|
||||||
@@ -303,7 +303,7 @@ const MediaPlayer = ({ noun, getMediaUrl, fetchDetails }: MediaPlayerProps) => {
|
|||||||
|
|
||||||
{/* Volume — slider expands on hover */}
|
{/* Volume — slider expands on hover */}
|
||||||
<div className="flex items-center gap-1.5 group/vol">
|
<div className="flex items-center gap-1.5 group/vol">
|
||||||
<button onClick={toggleMute} className="hover:text-[#FBF8F2]/70 transition-colors">
|
<button onClick={toggleMute} className="hover:text-on-dark/70 transition-colors">
|
||||||
<VolumeIcon size={18} />
|
<VolumeIcon size={18} />
|
||||||
</button>
|
</button>
|
||||||
<div className="overflow-hidden w-0 group-hover/vol:w-16 transition-[width] duration-200">
|
<div className="overflow-hidden w-0 group-hover/vol:w-16 transition-[width] duration-200">
|
||||||
@@ -312,18 +312,18 @@ const MediaPlayer = ({ noun, getMediaUrl, fetchDetails }: MediaPlayerProps) => {
|
|||||||
min={0} max={1} step={0.02}
|
min={0} max={1} step={0.02}
|
||||||
value={effectiveVolume}
|
value={effectiveVolume}
|
||||||
onChange={handleVolumeChange}
|
onChange={handleVolumeChange}
|
||||||
className="w-16 accent-[#FBF8F2] cursor-pointer"
|
className="w-16 accent-on-dark cursor-pointer"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span className="text-xs font-data tabular-nums text-[#FBF8F2]/80">
|
<span className="text-xs font-data tabular-nums text-on-dark/80">
|
||||||
{formatTime(currentTime)} / {formatTime(duration)}
|
{formatTime(currentTime)} / {formatTime(duration)}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<div className="flex-1" />
|
<div className="flex-1" />
|
||||||
|
|
||||||
<button onClick={toggleFullscreen} className="hover:text-[#FBF8F2]/70 transition-colors">
|
<button onClick={toggleFullscreen} className="hover:text-on-dark/70 transition-colors">
|
||||||
{isFullscreen ? <Minimize size={18} /> : <Maximize size={18} />}
|
{isFullscreen ? <Minimize size={18} /> : <Maximize size={18} />}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -331,10 +331,10 @@ const MediaPlayer = ({ noun, getMediaUrl, fetchDetails }: MediaPlayerProps) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{error && <div className="text-[#B5432E] text-sm mt-3">{error}</div>}
|
{error && <div className="text-error text-sm mt-3">{error}</div>}
|
||||||
{!mediaLoaded && !error && (
|
{!mediaLoaded && !error && (
|
||||||
<div className="text-muted text-sm mt-3 flex items-center gap-2">
|
<div className="text-muted text-sm mt-3 flex items-center gap-2">
|
||||||
<div className="w-3 h-3 rounded-full border-2 border-[#DED7CA] border-t-muted animate-spin" />
|
<div className="w-3 h-3 rounded-full border-2 border-inactive border-t-muted animate-spin" />
|
||||||
Loading video...
|
Loading video...
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ const MyClips = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{error && <p className="text-sm text-[#B5432E] mt-4">{error.toString()}</p>}
|
{error && <p className="text-sm text-error mt-4">{error.toString()}</p>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ const MyVods = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{error && <p className="text-sm text-[#B5432E] mt-4">{error}</p>}
|
{error && <p className="text-sm text-error mt-4">{error}</p>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ const Profile = () => {
|
|||||||
title="Copy key"
|
title="Copy key"
|
||||||
className="p-2 rounded-md bg-fields hover:bg-hover transition-colors duration-150 text-text-secondary"
|
className="p-2 rounded-md bg-fields hover:bg-hover transition-colors duration-150 text-text-secondary"
|
||||||
>
|
>
|
||||||
{copied ? <Check size={16} className="text-[#6E7A50]" /> : <Copy size={16} />}
|
{copied ? <Check size={16} className="text-olive" /> : <Copy size={16} />}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ type TrackedJob = {
|
|||||||
|
|
||||||
const CardHeader = ({ icon: Icon, title, accent = "primary" }: { icon: React.ElementType; title: string; accent?: "primary" | "accent" }) => (
|
const CardHeader = ({ icon: Icon, title, accent = "primary" }: { icon: React.ElementType; title: string; accent?: "primary" | "accent" }) => (
|
||||||
<div className="flex items-center gap-2.5">
|
<div className="flex items-center gap-2.5">
|
||||||
<div className={clsx("w-8 h-8 rounded-lg flex items-center justify-center flex-shrink-0", accent === "primary" ? "bg-[#A85A38]/10" : "bg-[#6E7A50]/10")}>
|
<div className={clsx("w-8 h-8 rounded-lg flex items-center justify-center flex-shrink-0", accent === "primary" ? "bg-terracotta/10" : "bg-olive/10")}>
|
||||||
<Icon size={16} className={accent === "primary" ? "text-[#A85A38]" : "text-[#6E7A50]"} />
|
<Icon size={16} className={accent === "primary" ? "text-terracotta" : "text-olive"} />
|
||||||
</div>
|
</div>
|
||||||
<h2 className="text-xl font-heading text-text-primary">{title}</h2>
|
<h2 className="text-xl font-heading text-text-primary">{title}</h2>
|
||||||
</div>
|
</div>
|
||||||
@@ -35,22 +35,22 @@ const JobStatus = ({ job }: { job: TrackedJob }) => {
|
|||||||
if (job.state === 'FAILED') {
|
if (job.state === 'FAILED') {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-start gap-2 text-sm">
|
<div className="flex items-start gap-2 text-sm">
|
||||||
<X size={15} className="text-[#B5432E] mt-0.5 flex-shrink-0" />
|
<X size={15} className="text-error mt-0.5 flex-shrink-0" />
|
||||||
<p className="text-[#B5432E]">{job.label} failed: {job.errorOutput ?? "Unknown error"}</p>
|
<p className="text-error">{job.label} failed: {job.errorOutput ?? "Unknown error"}</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (job.state === 'SUCCEEDED') {
|
if (job.state === 'SUCCEEDED') {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-2 text-sm">
|
<div className="flex items-center gap-2 text-sm">
|
||||||
<Check size={15} className="text-[#6E7A50] flex-shrink-0" />
|
<Check size={15} className="text-olive flex-shrink-0" />
|
||||||
<p className="text-text-body">{job.label} saved</p>
|
<p className="text-text-body">{job.label} saved</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Loader2 size={15} className="text-[#A85A38] animate-spin flex-shrink-0" />
|
<Loader2 size={15} className="text-terracotta animate-spin flex-shrink-0" />
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<p className="text-sm text-text-secondary mb-1">{job.label}…</p>
|
<p className="text-sm text-text-secondary mb-1">{job.label}…</p>
|
||||||
<progress value={job.progress} className="w-full h-1.5 rounded bg-fields" />
|
<progress value={job.progress} className="w-full h-1.5 rounded bg-fields" />
|
||||||
@@ -246,12 +246,12 @@ const StreamControl = () => {
|
|||||||
{/* Hero status bar */}
|
{/* Hero status bar */}
|
||||||
<Box className={clsx(
|
<Box className={clsx(
|
||||||
"p-6 flex items-center justify-between gap-6 border-l-4",
|
"p-6 flex items-center justify-between gap-6 border-l-4",
|
||||||
isStreaming ? "border-l-[#B5432E]" : "border-l-hairline"
|
isStreaming ? "border-l-live" : "border-l-hairline"
|
||||||
)}>
|
)}>
|
||||||
<div className="flex items-center gap-3.5">
|
<div className="flex items-center gap-3.5">
|
||||||
<span className="relative flex h-3.5 w-3.5">
|
<span className="relative flex h-3.5 w-3.5">
|
||||||
{isStreaming && <span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-[#B5432E] opacity-75" />}
|
{isStreaming && <span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-live opacity-75" />}
|
||||||
<span className={clsx("relative inline-flex rounded-full h-3.5 w-3.5", isStreaming ? "bg-[#B5432E]" : "bg-[#C4BAA9]")} />
|
<span className={clsx("relative inline-flex rounded-full h-3.5 w-3.5", isStreaming ? "bg-live" : "bg-inactive-strong")} />
|
||||||
</span>
|
</span>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-2xl font-heading text-text-primary leading-tight">{isStreaming ? "Live" : "Offline"}</p>
|
<p className="text-2xl font-heading text-text-primary leading-tight">{isStreaming ? "Live" : "Offline"}</p>
|
||||||
@@ -269,7 +269,7 @@ const StreamControl = () => {
|
|||||||
<p className={labelClass}>elapsed</p>
|
<p className={labelClass}>elapsed</p>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<Radio size={28} className="text-[#DED7CA]" />
|
<Radio size={28} className="text-inactive" />
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
@@ -278,7 +278,7 @@ const StreamControl = () => {
|
|||||||
{/* Quick mark toolbar */}
|
{/* Quick mark toolbar */}
|
||||||
<Box className="p-4 flex flex-col gap-3">
|
<Box className="p-4 flex flex-col gap-3">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<Bookmark size={18} className="text-[#6E7A50] flex-shrink-0" />
|
<Bookmark size={18} className="text-olive flex-shrink-0" />
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={markerMessage}
|
value={markerMessage}
|
||||||
@@ -291,7 +291,7 @@ const StreamControl = () => {
|
|||||||
Add Marker
|
Add Marker
|
||||||
</PrimaryButton>
|
</PrimaryButton>
|
||||||
</div>
|
</div>
|
||||||
{markerError && <p className="text-sm text-[#B5432E]">{markerError}</p>}
|
{markerError && <p className="text-sm text-error">{markerError}</p>}
|
||||||
|
|
||||||
{markers.length > 0 && (
|
{markers.length > 0 && (
|
||||||
<div className="flex gap-2 overflow-x-auto pb-0.5 -mx-1 px-1 pt-1.5">
|
<div className="flex gap-2 overflow-x-auto pb-0.5 -mx-1 px-1 pt-1.5">
|
||||||
@@ -304,22 +304,22 @@ const StreamControl = () => {
|
|||||||
onClick={() => handleChipClick(m.id)}
|
onClick={() => handleChipClick(m.id)}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
"flex flex-col items-start gap-0.5 rounded-lg border px-3 py-1.5 text-left transition-colors duration-150",
|
"flex flex-col items-start gap-0.5 rounded-lg border px-3 py-1.5 text-left transition-colors duration-150",
|
||||||
isStart && "border-[#A85A38] bg-[#F6EFE6] ring-1 ring-[#A85A38]",
|
isStart && "border-terracotta bg-selected ring-1 ring-terracotta",
|
||||||
isEnd && "border-[#6E7A50] bg-[#F6EFE6] ring-1 ring-[#6E7A50]",
|
isEnd && "border-olive bg-selected ring-1 ring-olive",
|
||||||
!isStart && !isEnd && "border-hairline hover:border-[#E2DACB] hover:bg-hover"
|
!isStart && !isEnd && "border-hairline hover:border-ring hover:bg-hover"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<span className="text-xs font-medium text-text-strong max-w-40 truncate">{m.message}</span>
|
<span className="text-xs font-medium text-text-strong max-w-40 truncate">{m.message}</span>
|
||||||
<span className="font-data text-[11px] text-muted">
|
<span className="font-data text-[11px] text-muted">
|
||||||
{formatLocalDate(m.timestamp, { hour: "2-digit", minute: "2-digit", second: "2-digit" })}
|
{formatLocalDate(m.timestamp, { hour: "2-digit", minute: "2-digit", second: "2-digit" })}
|
||||||
{isStart && <span className="text-[#A85A38] font-medium"> · start</span>}
|
{isStart && <span className="text-terracotta font-medium"> · start</span>}
|
||||||
{isEnd && <span className="text-[#6E7A50] font-medium"> · end</span>}
|
{isEnd && <span className="text-olive font-medium"> · end</span>}
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={(e) => { e.stopPropagation(); handleDeleteMarker(m.id); }}
|
onClick={(e) => { e.stopPropagation(); handleDeleteMarker(m.id); }}
|
||||||
title="Delete marker"
|
title="Delete marker"
|
||||||
className="hidden group-hover:flex items-center justify-center absolute -top-1.5 -right-1.5 w-4 h-4 rounded-full bg-muted hover:bg-[#B5432E] text-[#FDFBF7] transition-colors duration-150"
|
className="hidden group-hover:flex items-center justify-center absolute -top-1.5 -right-1.5 w-4 h-4 rounded-full bg-muted hover:bg-error text-on-accent transition-colors duration-150"
|
||||||
>
|
>
|
||||||
<X size={10} />
|
<X size={10} />
|
||||||
</button>
|
</button>
|
||||||
@@ -346,7 +346,7 @@ const StreamControl = () => {
|
|||||||
</div>
|
</div>
|
||||||
<input type="text" placeholder="Title" value={clipTitle} onChange={(e) => setClipTitle(e.target.value)} className={inputClass} />
|
<input type="text" placeholder="Title" value={clipTitle} onChange={(e) => setClipTitle(e.target.value)} className={inputClass} />
|
||||||
<textarea placeholder="Description" value={clipDescription} onChange={(e) => setClipDescription(e.target.value)} rows={2} className={inputClass + " resize-none"} />
|
<textarea placeholder="Description" value={clipDescription} onChange={(e) => setClipDescription(e.target.value)} rows={2} className={inputClass + " resize-none"} />
|
||||||
{clipError && <p className="text-sm text-[#B5432E]">{clipError}</p>}
|
{clipError && <p className="text-sm text-error">{clipError}</p>}
|
||||||
<PrimaryButton onClick={handleClip} className="self-start mt-auto">Clip</PrimaryButton>
|
<PrimaryButton onClick={handleClip} className="self-start mt-auto">Clip</PrimaryButton>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
@@ -359,21 +359,21 @@ const StreamControl = () => {
|
|||||||
<div className="flex items-center gap-2 flex-wrap">
|
<div className="flex items-center gap-2 flex-wrap">
|
||||||
<span className={clsx(
|
<span className={clsx(
|
||||||
"text-xs font-medium px-2.5 py-1 rounded-full border",
|
"text-xs font-medium px-2.5 py-1 rounded-full border",
|
||||||
startMarker ? "border-[#A85A38] text-[#A85A38] bg-[#F6EFE6]" : "border-dashed border-[#DED7CA] text-muted"
|
startMarker ? "border-terracotta text-terracotta bg-selected" : "border-dashed border-inactive text-muted"
|
||||||
)}>
|
)}>
|
||||||
{startMarker ? startMarker.message : "Tap a marker to set start"}
|
{startMarker ? startMarker.message : "Tap a marker to set start"}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-[#C4BAA9]">→</span>
|
<span className="text-inactive-strong">→</span>
|
||||||
<span className={clsx(
|
<span className={clsx(
|
||||||
"text-xs font-medium px-2.5 py-1 rounded-full border",
|
"text-xs font-medium px-2.5 py-1 rounded-full border",
|
||||||
endMarker ? "border-[#6E7A50] text-[#6E7A50] bg-[#F6EFE6]" : "border-dashed border-[#DED7CA] text-muted"
|
endMarker ? "border-olive text-olive bg-selected" : "border-dashed border-inactive text-muted"
|
||||||
)}>
|
)}>
|
||||||
{endMarker ? endMarker.message : "Tap a marker to set end"}
|
{endMarker ? endMarker.message : "Tap a marker to set end"}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<input type="text" placeholder="Title" value={markerSaveTitle} onChange={(e) => setMarkerSaveTitle(e.target.value)} className={inputClass} />
|
<input type="text" placeholder="Title" value={markerSaveTitle} onChange={(e) => setMarkerSaveTitle(e.target.value)} className={inputClass} />
|
||||||
<textarea placeholder="Description" value={markerSaveDescription} onChange={(e) => setMarkerSaveDescription(e.target.value)} rows={2} className={inputClass + " resize-none"} />
|
<textarea placeholder="Description" value={markerSaveDescription} onChange={(e) => setMarkerSaveDescription(e.target.value)} rows={2} className={inputClass + " resize-none"} />
|
||||||
{markerSaveError && <p className="text-sm text-[#B5432E]">{markerSaveError}</p>}
|
{markerSaveError && <p className="text-sm text-error">{markerSaveError}</p>}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<PrimaryButton onClick={handleSaveByMarkers} disabled={markers.length < 2} className="self-start mt-auto">Save</PrimaryButton>
|
<PrimaryButton onClick={handleSaveByMarkers} disabled={markers.length < 2} className="self-start mt-auto">Save</PrimaryButton>
|
||||||
|
|||||||
Reference in New Issue
Block a user