mirror of
https://github.com/webadderallorg/Recordly.git
synced 2026-09-24 23:05:49 +00:00
add: theming dark/light mode + system colors for the recording bar, to avoid icons being wrong color, text too etc..
This commit is contained in:
@@ -12,14 +12,11 @@
|
||||
gap: 10px;
|
||||
width: max-content;
|
||||
max-width: 1200px;
|
||||
background: rgba(18, 18, 24, 0.97);
|
||||
border: 1px solid rgba(255, 255, 255, 0.07);
|
||||
background: var(--launch-bar-bg);
|
||||
border: 1px solid var(--launch-bar-border);
|
||||
border-radius: 20px;
|
||||
padding: 11px 20px 11px 18px;
|
||||
box-shadow:
|
||||
0 10px 30px rgba(0, 0, 0, 0.24),
|
||||
0 2px 10px rgba(0, 0, 0, 0.12),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.04);
|
||||
box-shadow: var(--launch-bar-shadow);
|
||||
overflow: visible;
|
||||
position: relative;
|
||||
transform-origin: center bottom;
|
||||
@@ -48,7 +45,7 @@
|
||||
.sep {
|
||||
width: 1px;
|
||||
height: 26px;
|
||||
background: #2a2a34;
|
||||
background: var(--launch-border-strong);
|
||||
margin: 0 6px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
@@ -60,7 +57,7 @@
|
||||
border-radius: 11px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: #6b6b78;
|
||||
color: var(--launch-text-muted);
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
@@ -70,8 +67,8 @@
|
||||
}
|
||||
|
||||
.ib:hover {
|
||||
background: rgba(255, 255, 255, 0.07);
|
||||
color: #eeeef2;
|
||||
background: var(--launch-hover);
|
||||
color: var(--launch-text);
|
||||
}
|
||||
|
||||
.ibActive {
|
||||
@@ -104,8 +101,8 @@
|
||||
width: 300px;
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
background: var(--launch-surface, rgba(18, 18, 24, 0.97));
|
||||
border: 1px solid var(--launch-border, rgba(255, 255, 255, 0.07));
|
||||
background: var(--launch-surface);
|
||||
border: 1px solid var(--launch-border);
|
||||
border-radius: 16px;
|
||||
backdrop-filter: blur(var(--launch-popover-blur, 24px))
|
||||
saturate(var(--launch-popover-saturate, 145%));
|
||||
@@ -114,10 +111,7 @@
|
||||
padding: 8px;
|
||||
margin-top: auto;
|
||||
margin-bottom: 8px;
|
||||
box-shadow:
|
||||
0 16px 40px rgba(0, 0, 0, 0.28),
|
||||
0 2px 12px rgba(0, 0, 0, 0.12),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.08);
|
||||
box-shadow: var(--launch-bar-shadow);
|
||||
pointer-events: auto;
|
||||
animation: menuCardIn 0.18s ease;
|
||||
}
|
||||
@@ -142,14 +136,14 @@
|
||||
}
|
||||
|
||||
.menuCard::-webkit-scrollbar-thumb {
|
||||
background: #2a2a34;
|
||||
border-radius: 2px;
|
||||
background: var(--launch-border-strong);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.ddLabel {
|
||||
font-size: 9px;
|
||||
font-weight: 600;
|
||||
color: var(--launch-label, #8f99b1);
|
||||
color: var(--launch-label);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
padding: 6px 10px 4px;
|
||||
@@ -165,20 +159,20 @@
|
||||
border: none;
|
||||
background: transparent;
|
||||
font-size: 12px;
|
||||
color: var(--launch-text-muted, #aab3c7);
|
||||
color: var(--launch-text-muted);
|
||||
cursor: pointer;
|
||||
transition: all 0.12s ease;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.ddItem:hover {
|
||||
background: var(--launch-hover, rgba(255, 255, 255, 0.1));
|
||||
color: var(--launch-text, #f3f6ff);
|
||||
background: var(--launch-hover);
|
||||
color: var(--launch-text);
|
||||
}
|
||||
|
||||
.ddItemSelected {
|
||||
background: var(--launch-selected, rgba(92, 153, 255, 0.22));
|
||||
color: var(--launch-accent-soft, #dbeafe);
|
||||
background: var(--launch-selected);
|
||||
color: var(--launch-accent);
|
||||
}
|
||||
|
||||
.finalizingState {
|
||||
@@ -270,11 +264,9 @@
|
||||
width: 288px;
|
||||
overflow: hidden;
|
||||
border-radius: 24px;
|
||||
background: rgba(22, 22, 30, 0.95);
|
||||
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||
box-shadow:
|
||||
0 10px 24px rgba(0, 0, 0, 0.28),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.08);
|
||||
background: var(--launch-surface);
|
||||
border: 1px solid var(--launch-border-strong);
|
||||
box-shadow: var(--launch-bar-shadow);
|
||||
cursor: grab;
|
||||
touch-action: none;
|
||||
user-select: none;
|
||||
|
||||
@@ -231,7 +231,7 @@ function LaunchWindowContent() {
|
||||
<Button
|
||||
variant="outline"
|
||||
size="lg"
|
||||
className={`${styles.electronNoDrag} group gap-2 px-3 min-w-0 max-w-[180px] rounded-[11px] font-medium text-[12px] shrink-0 border-[#2a2a34] bg-[#1a1a22] text-[#eeeef2] hover:border-[#3e3e4c] hover:bg-[#20202a] transition-all ${openId === "sources" ? "border-[#3e3e4c] bg-[#20202a]" : ""}`}
|
||||
className={`${styles.electronNoDrag} group gap-2 px-3 min-w-0 max-w-[180px] rounded-[11px] font-medium text-[12px] shrink-0 border-[var(--launch-border)] bg-[var(--launch-surface)] text-[var(--launch-text)] hover:border-[var(--launch-border-strong)] hover:bg-[var(--launch-hover)] transition-all ${openId === "sources" ? "border-[var(--launch-border-strong)] bg-[var(--launch-hover)]" : ""}`}
|
||||
title={selectedSource}
|
||||
>
|
||||
<MonitorIcon size={16} className="shrink-0" />
|
||||
@@ -449,7 +449,7 @@ function LaunchWindowContent() {
|
||||
ref={hudBarRef}
|
||||
layout={!showRecordingWebcamPreview && !isHudDragging}
|
||||
transition={hudStateTransition}
|
||||
className={`${styles.bar} mb-2`}
|
||||
className={`${styles.bar} launch-theme mb-2`}
|
||||
>
|
||||
<div
|
||||
// On Linux (especially Wayland) the compositor owns window
|
||||
|
||||
@@ -50,7 +50,7 @@ export const RecordingControls = ({
|
||||
|
||||
<span
|
||||
className={`font-mono text-xs font-semibold min-w-[52px] text-center tracking-[0.02em] ${
|
||||
paused ? "text-[#fbbf24]" : "text-[#eeeef2]"
|
||||
paused ? "text-[#fbbf24]" : "text-[var(--launch-text)]"
|
||||
}`}
|
||||
>
|
||||
{formatTime(elapsed)}
|
||||
|
||||
@@ -1,53 +1,51 @@
|
||||
.source-selector-popover {
|
||||
background: var(--launch-surface, rgba(18, 18, 24, 0.97));
|
||||
border: 1px solid var(--launch-border, rgba(255, 255, 255, 0.07));
|
||||
background: var(--launch-surface);
|
||||
border: 1px solid var(--launch-border);
|
||||
border-radius: 16px;
|
||||
backdrop-filter: blur(var(--launch-popover-blur, 24px))
|
||||
saturate(var(--launch-popover-saturate, 145%));
|
||||
-webkit-backdrop-filter: blur(var(--launch-popover-blur, 24px))
|
||||
saturate(var(--launch-popover-saturate, 145%));
|
||||
box-shadow:
|
||||
0 16px 40px rgba(0, 0, 0, 0.28),
|
||||
0 2px 12px rgba(0, 0, 0, 0.12),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.08);
|
||||
box-shadow: var(--launch-bar-shadow);
|
||||
}
|
||||
|
||||
.source-selector-item {
|
||||
color: var(--launch-text-muted, #aab3c7);
|
||||
color: var(--launch-text-muted);
|
||||
}
|
||||
|
||||
.source-selector-item:hover {
|
||||
background: var(--launch-hover, rgba(255, 255, 255, 0.1));
|
||||
color: var(--launch-text, #f3f6ff);
|
||||
background: var(--launch-hover);
|
||||
color: var(--launch-text);
|
||||
}
|
||||
|
||||
.source-selector-item-selected {
|
||||
background: var(--launch-selected, rgba(92, 153, 255, 0.22));
|
||||
color: var(--launch-accent-soft, #dbeafe);
|
||||
background: var(--launch-selected);
|
||||
color: var(--launch-accent);
|
||||
}
|
||||
|
||||
.source-selector-thumb-fallback {
|
||||
background: #23232d;
|
||||
background: var(--launch-panel);
|
||||
}
|
||||
|
||||
.source-selector-text {
|
||||
color: var(--launch-text, #f3f6ff);
|
||||
color: var(--launch-text);
|
||||
}
|
||||
|
||||
.source-selector-muted {
|
||||
color: var(--launch-text-muted, #aab3c7);
|
||||
color: var(--launch-text-muted);
|
||||
}
|
||||
|
||||
.source-selector-subtle {
|
||||
color: #747483;
|
||||
color: var(--launch-text-muted);
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.source-selector-label {
|
||||
color: var(--launch-label, #8f99b1);
|
||||
color: var(--launch-label);
|
||||
}
|
||||
|
||||
.source-selector-dot {
|
||||
background: var(--launch-accent, #3d8bff);
|
||||
background: var(--launch-accent);
|
||||
}
|
||||
|
||||
.source-selector-dot-inner {
|
||||
@@ -55,7 +53,7 @@
|
||||
}
|
||||
|
||||
.source-selector-accent-border {
|
||||
border-color: var(--launch-accent, #3d8bff);
|
||||
border-color: var(--launch-accent);
|
||||
}
|
||||
|
||||
/* Custom Scrollbar for Source Selector */
|
||||
@@ -96,7 +94,6 @@
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
.source-selector-marquee-animated {
|
||||
|
||||
@@ -1,9 +1,31 @@
|
||||
.launch-theme {
|
||||
/* Ultra-Refined Light Mode (Premium OS style) */
|
||||
--launch-surface: #ffffff;
|
||||
--launch-panel: #f8fafc;
|
||||
--launch-border: rgba(0, 0, 0, 0.08);
|
||||
--launch-border-strong: rgba(0, 0, 0, 0.16);
|
||||
--launch-text: #020617; /* Deepest Navy/Black */
|
||||
--launch-text-muted: #475569; /* Slate 600 */
|
||||
--launch-label: #64748b; /* Slate 500 - increased contrast */
|
||||
--launch-hover: rgba(0, 0, 0, 0.045);
|
||||
--launch-selected: rgba(37, 99, 235, 0.12); /* Blue 600 at 12% */
|
||||
--launch-accent: #2563eb; /* Blue 600 */
|
||||
--launch-accent-soft: #f1f5f9; /* Slate 100 */
|
||||
--launch-popover-blur: 40px;
|
||||
--launch-popover-saturate: 200%;
|
||||
--launch-bar-bg: rgba(255, 255, 255, 0.98);
|
||||
--launch-bar-border: rgba(0, 0, 0, 0.08);
|
||||
--launch-bar-shadow: 0 12px 40px rgba(0, 0, 0, 0.12), 0 4px 12px rgba(0, 0, 0, 0.06);
|
||||
|
||||
color: var(--launch-text);
|
||||
}
|
||||
|
||||
.dark .launch-theme,
|
||||
.launch-theme.dark {
|
||||
/* Dark Mode values */
|
||||
--launch-surface: rgba(18, 18, 24, 0.97);
|
||||
--launch-panel: rgba(18, 18, 24, 0.82);
|
||||
--launch-border: rgba(255, 255, 255, 0.07);
|
||||
--launch-popover-blur: 24px;
|
||||
--launch-popover-saturate: 145%;
|
||||
--launch-border-strong: rgba(255, 255, 255, 0.12);
|
||||
--launch-text: #f3f6ff;
|
||||
--launch-text-muted: #aab3c7;
|
||||
@@ -12,4 +34,7 @@
|
||||
--launch-selected: rgba(92, 153, 255, 0.22);
|
||||
--launch-accent: #3d8bff;
|
||||
--launch-accent-soft: #dbeafe;
|
||||
--launch-bar-bg: rgba(18, 18, 24, 0.97);
|
||||
--launch-bar-border: rgba(255, 255, 255, 0.07);
|
||||
--launch-bar-shadow: 0 10px 30px rgba(0, 0, 0, 0.24), 0 2px 10px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { TimerIcon } from "@phosphor-icons/react";
|
||||
import type { ReactNode } from "react";
|
||||
import type { ReactElement } from "react";
|
||||
import { useScopedT } from "@/contexts/I18nContext";
|
||||
import styles from "../LaunchWindow.module.css";
|
||||
import { DropdownItem, HudPopover } from "./PopoverScaffold";
|
||||
@@ -13,7 +13,7 @@ export function CountdownPopover({
|
||||
countdownDelay,
|
||||
onSelectDelay,
|
||||
}: {
|
||||
trigger: ReactNode;
|
||||
trigger: ReactElement;
|
||||
countdownDelay: number;
|
||||
onSelectDelay: (delay: number) => void;
|
||||
}) {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useScopedT } from "@/contexts/I18nContext";
|
||||
import { DropdownItem, HudPopover, MicDeviceRow } from "./PopoverScaffold";
|
||||
import { useLaunchPopoverCoordinator } from "./LaunchPopoverCoordinator";
|
||||
import type { DeviceOption } from "./launchPopoverTypes";
|
||||
import type { ReactNode } from "react";
|
||||
import type { ReactElement } from "react";
|
||||
import styles from "../LaunchWindow.module.css";
|
||||
|
||||
const POPOVER_ID = "mic";
|
||||
@@ -20,7 +20,7 @@ export function MicPopover({
|
||||
selectedDeviceId,
|
||||
onSelectDevice,
|
||||
}: {
|
||||
trigger: ReactNode;
|
||||
trigger: ReactElement;
|
||||
disabled?: boolean;
|
||||
systemAudioEnabled: boolean;
|
||||
onToggleSystemAudio: () => void;
|
||||
@@ -73,7 +73,7 @@ export function MicPopover({
|
||||
</DropdownItem>
|
||||
)}
|
||||
{!microphoneEnabled && (
|
||||
<div className="px-3 py-2 text-xs text-[#6b6b78]">{t("recording.selectMicToEnable")}</div>
|
||||
<div className="px-3 py-2 text-xs text-[var(--launch-text-muted)]">{t("recording.selectMicToEnable")}</div>
|
||||
)}
|
||||
{devices.map((device) => (
|
||||
<MicDeviceRow
|
||||
@@ -87,7 +87,7 @@ export function MicPopover({
|
||||
/>
|
||||
))}
|
||||
{devices.length === 0 && (
|
||||
<div className="text-center text-xs text-[#6b6b78] py-4">{t("recording.noMicrophonesFound")}</div>
|
||||
<div className="text-center text-xs text-[var(--launch-text-muted)] py-4">{t("recording.noMicrophonesFound")}</div>
|
||||
)}
|
||||
</HudPopover>
|
||||
);
|
||||
|
||||
@@ -5,10 +5,14 @@ import {
|
||||
TranslateIcon,
|
||||
VideoCameraIcon,
|
||||
ArrowClockwiseIcon,
|
||||
SunIcon,
|
||||
MoonIcon,
|
||||
DesktopIcon,
|
||||
} from "@phosphor-icons/react";
|
||||
import type { ReactNode } from "react";
|
||||
import type { ReactElement } from "react";
|
||||
import { useI18n } from "@/contexts/I18nContext";
|
||||
import { useScopedT } from "@/contexts/I18nContext";
|
||||
import { useTheme } from "@/contexts/ThemeContext";
|
||||
import type { AppLocale } from "@/i18n/config";
|
||||
import { SUPPORTED_LOCALES } from "@/i18n/config";
|
||||
import styles from "../LaunchWindow.module.css";
|
||||
@@ -40,7 +44,7 @@ export function MorePopover({
|
||||
onPreviewUpdateUi,
|
||||
appVersion,
|
||||
}: {
|
||||
trigger: ReactNode;
|
||||
trigger: ReactElement;
|
||||
supportsHudCaptureProtection: boolean;
|
||||
hideHudFromCapture: boolean;
|
||||
onToggleHudCaptureProtection: () => void;
|
||||
@@ -53,6 +57,7 @@ export function MorePopover({
|
||||
}) {
|
||||
const t = useScopedT("launch");
|
||||
const { locale, setLocale } = useI18n();
|
||||
const { preference, setPreference } = useTheme();
|
||||
const { isOpen, requestOpen, requestClose } = useLaunchPopoverCoordinator();
|
||||
const open = isOpen(POPOVER_ID);
|
||||
|
||||
@@ -118,6 +123,39 @@ export function MorePopover({
|
||||
{t("recording.previewUpdateUi", "Preview Update UI")}
|
||||
</DropdownItem>
|
||||
) : null}
|
||||
<div className={styles.ddLabel} style={{ marginTop: 4 }}>
|
||||
{t("recording.appearance", "Appearance")}
|
||||
</div>
|
||||
<DropdownItem
|
||||
icon={<SunIcon size={16} />}
|
||||
selected={preference === "light"}
|
||||
onClick={() => {
|
||||
setPreference("light");
|
||||
requestClose(POPOVER_ID);
|
||||
}}
|
||||
>
|
||||
{t("common.light", "Light")}
|
||||
</DropdownItem>
|
||||
<DropdownItem
|
||||
icon={<MoonIcon size={16} />}
|
||||
selected={preference === "dark"}
|
||||
onClick={() => {
|
||||
setPreference("dark");
|
||||
requestClose(POPOVER_ID);
|
||||
}}
|
||||
>
|
||||
{t("common.dark", "Dark")}
|
||||
</DropdownItem>
|
||||
<DropdownItem
|
||||
icon={<DesktopIcon size={16} />}
|
||||
selected={preference === "system"}
|
||||
onClick={() => {
|
||||
setPreference("system");
|
||||
requestClose(POPOVER_ID);
|
||||
}}
|
||||
>
|
||||
{t("common.system", "System")}
|
||||
</DropdownItem>
|
||||
<div className={styles.ddLabel} style={{ marginTop: 4 }}>
|
||||
{t("recording.language")}
|
||||
</div>
|
||||
@@ -140,7 +178,7 @@ export function MorePopover({
|
||||
marginTop: 8,
|
||||
padding: "4px 12px",
|
||||
fontSize: 11,
|
||||
color: "#6b6b78",
|
||||
color: "var(--launch-text-muted)",
|
||||
textAlign: "center",
|
||||
userSelect: "text",
|
||||
}}
|
||||
|
||||
@@ -8,7 +8,7 @@ import { useScopedT } from "@/contexts/I18nContext";
|
||||
import { DropdownItem, HudPopover } from "./PopoverScaffold";
|
||||
import { useLaunchPopoverCoordinator } from "./LaunchPopoverCoordinator";
|
||||
import type { DeviceOption } from "./launchPopoverTypes";
|
||||
import type { ReactNode } from "react";
|
||||
import type { ReactElement } from "react";
|
||||
|
||||
const POPOVER_ID = "webcam";
|
||||
|
||||
@@ -27,7 +27,7 @@ export function WebcamPopover({
|
||||
selectedVideoDeviceId,
|
||||
onSelectVideoDevice,
|
||||
}: {
|
||||
trigger: ReactNode;
|
||||
trigger: ReactElement;
|
||||
disabled?: boolean;
|
||||
webcamEnabled: boolean;
|
||||
onDisableWebcam: () => void;
|
||||
@@ -61,7 +61,7 @@ export function WebcamPopover({
|
||||
trigger={trigger}
|
||||
align="center"
|
||||
>
|
||||
<div className="px-3 pb-1 pt-2 text-[10px] font-semibold uppercase tracking-[0.08em] text-[#6b6b78]">
|
||||
<div className="px-3 pb-1 pt-2 text-[10px] font-semibold uppercase tracking-[0.08em] text-[var(--launch-label)]">
|
||||
{t("recording.webcam")}
|
||||
</div>
|
||||
{webcamEnabled && (
|
||||
@@ -86,11 +86,11 @@ export function WebcamPopover({
|
||||
</>
|
||||
)}
|
||||
{!webcamEnabled && (
|
||||
<div className="px-3 py-2 text-xs text-[#6b6b78]">{t("recording.selectWebcamToEnable")}</div>
|
||||
<div className="px-3 py-2 text-xs text-[var(--launch-text-muted)]">{t("recording.selectWebcamToEnable")}</div>
|
||||
)}
|
||||
{showWebcamControls && (
|
||||
<div className="flex justify-center px-3 py-2">
|
||||
<div className="h-24 w-24 overflow-hidden rounded-2xl bg-white/5 ring-1 ring-white/10">
|
||||
<div className="h-24 w-24 overflow-hidden rounded-2xl bg-[var(--launch-hover)] ring-1 ring-[var(--launch-border-strong)]">
|
||||
<video
|
||||
ref={setWebcamPreviewNode}
|
||||
className="h-full w-full object-cover"
|
||||
@@ -122,7 +122,7 @@ export function WebcamPopover({
|
||||
</DropdownItem>
|
||||
))}
|
||||
{videoDevices.length === 0 && (
|
||||
<div className="text-center text-xs text-[#6b6b78] py-4">{t("recording.noWebcamsFound")}</div>
|
||||
<div className="text-center text-xs text-[var(--launch-text-muted)] py-4">{t("recording.noWebcamsFound")}</div>
|
||||
)}
|
||||
</HudPopover>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user