3d4e574916
Shared UI primitives to end the "two ways to do X" drift on surfaces: - EmptyState (ui/EmptyState.tsx): one centered empty block — optional focal icon/badge + Body1 message + optional muted hint, with a `compact` variant for in-content placeholders. Adopted by Downloads, History, Library (L116), and — compact — the Terminal log, Diagnostics, and TemplateManager (L117). - Banner (ui/Banner.tsx) + LinkSuggestion (ui/LinkSuggestion.tsx): one tinted notification row (icon + truncating/wrap content + actions, tone brand/warning) replacing five hand-rolled style blocks — the DownloadBar copied-link suggestion / channel nudge / duplicate warning and the Library copied-link suggestion (UI19). - useTextStyles().truncate: shared single-line-ellipsis utility, adopted in Library rows + the playlist panel + Banner; `title` now also sets minWidth:0 so it shrinks in a flex row (L126). - L102: Settings "Default format" now uses the same SegmentedControl (Video/ Audio) + quality Select as the DownloadBar — one mental model; the combined `kind|quality` string encoding is gone. Resolved by verified convention (documented in code / CODE-AUDIT.md): - L103 busy = in-button icon→Spinner everywhere (done in L134; no adjacent- spinner pattern survives). - L115 list-row actions icon-only+tooltip vs card-toolbar actions labelled. - L120 Fluent <Card> for static content cards; tokenized surface recipe for interactive/list-item cards. - UI20 toggles = solid brand, list-selection = brand-tint (rule noted on SegmentedControl); the cited Library pill is gone via UI18. L128 (build-time DCE of preview seeds) deferred to Batch 14 with PERF8. Verified: typecheck (node+web) + 268 tests + eslint + production build green; touched files prettier-clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
192 lines
5.1 KiB
TypeScript
192 lines
5.1 KiB
TypeScript
import { makeStyles, tokens, shorthands } from '@fluentui/react-components'
|
|
import { THUMB_LG } from '../../thumbSizes'
|
|
import { SPACE, RADIUS, ELEVATION } from '../ui/tokens'
|
|
|
|
export const useDownloadBarStyles = makeStyles({
|
|
root: {
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
gap: SPACE.cozy,
|
|
// Page-level card: card padding + card radius, and the app's one "raised"
|
|
// elevation — the download bar floats above the queue (UI3/UI7/L100).
|
|
padding: SPACE.section,
|
|
backgroundColor: tokens.colorNeutralBackground1,
|
|
...shorthands.borderRadius(RADIUS.card),
|
|
boxShadow: ELEVATION.raised
|
|
},
|
|
// Highlight while a link / .url file is dragged over the card.
|
|
rootDragging: {
|
|
outline: `2px dashed ${tokens.colorBrandStroke1}`,
|
|
outlineOffset: '-2px'
|
|
},
|
|
urlRow: {
|
|
display: 'flex',
|
|
gap: '8px'
|
|
},
|
|
url: {
|
|
flexGrow: 1
|
|
},
|
|
// --- metadata preview card ---
|
|
preview: {
|
|
display: 'flex',
|
|
gap: '12px',
|
|
padding: '10px',
|
|
backgroundColor: tokens.colorNeutralBackground2,
|
|
...shorthands.borderRadius(tokens.borderRadiusLarge),
|
|
border: `1px solid ${tokens.colorNeutralStroke2}`
|
|
},
|
|
previewThumb: {
|
|
flexShrink: 0,
|
|
width: `${THUMB_LG.w}px`,
|
|
height: `${THUMB_LG.h}px`,
|
|
objectFit: 'cover',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
backgroundColor: tokens.colorBrandBackground2,
|
|
color: tokens.colorBrandForeground1,
|
|
// One thumbnail radius app-wide (UI6): control tier / Medium.
|
|
...shorthands.borderRadius(RADIUS.control)
|
|
},
|
|
previewBody: {
|
|
flexGrow: 1,
|
|
minWidth: 0,
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
justifyContent: 'center',
|
|
gap: '2px'
|
|
},
|
|
statusRow: {
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
gap: '8px',
|
|
color: tokens.colorNeutralForeground3
|
|
},
|
|
errorRow: {
|
|
color: tokens.colorPaletteRedForeground1
|
|
},
|
|
controls: {
|
|
display: 'flex',
|
|
alignItems: 'flex-end',
|
|
gap: '16px',
|
|
flexWrap: 'wrap'
|
|
},
|
|
control: {
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
gap: '4px'
|
|
},
|
|
quality: {
|
|
minWidth: '220px'
|
|
},
|
|
spacer: {
|
|
flexGrow: 1
|
|
},
|
|
// --- playlist selection ---
|
|
plPanel: {
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
gap: '8px',
|
|
padding: '12px',
|
|
backgroundColor: tokens.colorNeutralBackground2,
|
|
...shorthands.borderRadius(tokens.borderRadiusLarge),
|
|
border: `1px solid ${tokens.colorNeutralStroke2}`
|
|
},
|
|
plHeader: {
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
gap: '8px'
|
|
},
|
|
// Just the layout role; the truncation + weight come from the shared text.title
|
|
// (L114/L126), merged on at the call site.
|
|
plHeaderGrow: {
|
|
flexGrow: 1
|
|
},
|
|
plList: {
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
maxHeight: '260px',
|
|
overflowY: 'auto',
|
|
paddingRight: '4px'
|
|
},
|
|
plItemRow: {
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
gap: '8px'
|
|
},
|
|
plItem: {
|
|
display: 'flex',
|
|
alignItems: 'flex-start',
|
|
padding: '2px 0',
|
|
flexGrow: 1,
|
|
minWidth: 0
|
|
},
|
|
plItemLabel: {
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
minWidth: 0
|
|
},
|
|
// --- trim / schedule panels ---
|
|
trimBlock: {
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
gap: '8px',
|
|
alignItems: 'flex-start'
|
|
},
|
|
optButtons: {
|
|
display: 'flex',
|
|
gap: '8px'
|
|
},
|
|
trimPanel: {
|
|
alignSelf: 'stretch',
|
|
padding: '12px',
|
|
backgroundColor: tokens.colorNeutralBackground2,
|
|
...shorthands.borderRadius(tokens.borderRadiusLarge),
|
|
border: `1px solid ${tokens.colorNeutralStroke2}`
|
|
},
|
|
// Native datetime-local input, themed to sit beside the Fluent controls.
|
|
// No explicit `colorScheme` here (UI32): it inherits the resolved in-app scheme
|
|
// set on the app root in App.tsx, so the native calendar popup follows the app's
|
|
// Light/Dark theme — consistent with the native <Select> — rather than the OS
|
|
// preference (which the former `'light dark'` value tied it to).
|
|
dtInput: {
|
|
fontFamily: tokens.fontFamilyBase,
|
|
fontSize: tokens.fontSizeBase300,
|
|
padding: '6px 10px',
|
|
...shorthands.borderRadius(tokens.borderRadiusMedium),
|
|
border: `1px solid ${tokens.colorNeutralStroke1}`,
|
|
backgroundColor: tokens.colorNeutralBackground1,
|
|
color: tokens.colorNeutralForeground1
|
|
},
|
|
// --- command preview & options panels ---
|
|
commandBlock: {
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
gap: '8px'
|
|
},
|
|
commandPreviewPanel: {
|
|
padding: '12px',
|
|
backgroundColor: tokens.colorNeutralBackground2,
|
|
...shorthands.borderRadius(tokens.borderRadiusLarge),
|
|
border: `1px solid ${tokens.colorNeutralStroke2}`,
|
|
fontFamily: tokens.fontFamilyMonospace,
|
|
fontSize: tokens.fontSizeBase200,
|
|
overflowX: 'auto',
|
|
whiteSpace: 'pre-wrap',
|
|
wordBreak: 'break-all',
|
|
color: tokens.colorNeutralForeground1
|
|
},
|
|
commandError: {
|
|
color: tokens.colorStatusDangerForeground1
|
|
},
|
|
optionsPanel: {
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
gap: '12px',
|
|
padding: '12px',
|
|
backgroundColor: tokens.colorNeutralBackground2,
|
|
...shorthands.borderRadius(tokens.borderRadiusLarge),
|
|
border: `1px solid ${tokens.colorNeutralStroke2}`
|
|
}
|
|
})
|