feat(desktop): PC·업데이트 팝업 Apple/Windows 브랜드 아이콘
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+31
-1
@@ -13429,6 +13429,14 @@ html.theme-light .tam-disclaimer { color: #90a4ae; }
|
||||
margin: 8px 0 0; font-size: 12px; color: #4ade80; line-height: 1.45;
|
||||
}
|
||||
.desktop-update-modal-body { padding: 4px 2px 0; }
|
||||
.desktop-update-platform-brand {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.desktop-update-platform-brand--startup {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.desktop-update-version {
|
||||
margin: 0 0 12px; font-size: 13px; color: var(--text2);
|
||||
}
|
||||
@@ -13598,7 +13606,29 @@ html.theme-light .tam-disclaimer { color: #90a4ae; }
|
||||
background: var(--bg2); border: 1px solid var(--border);
|
||||
}
|
||||
.app-download-pc-card--empty { opacity: 0.75; }
|
||||
.app-download-pc-icon { font-size: 28px; flex-shrink: 0; width: 36px; text-align: center; }
|
||||
.app-download-pc-icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.app-download-pc-icon--mac {
|
||||
background: #1d1d1f;
|
||||
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
.app-download-pc-icon--windows {
|
||||
background: #0078d4;
|
||||
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.12);
|
||||
}
|
||||
.app-download-pc-icon--android {
|
||||
font-size: 24px;
|
||||
background: var(--bg3);
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
.app-download-pc-body { flex: 1; min-width: 0; }
|
||||
.app-download-pc-body strong { display: block; font-size: 14px; margin-bottom: 4px; }
|
||||
.app-download-pc-btn {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import QRCode from 'qrcode';
|
||||
import DraggableModalFrame from './DraggableModalFrame';
|
||||
import { PlatformBrandIcon } from './icons/PlatformBrandIcons';
|
||||
import {
|
||||
loadDesktopAppBuildReadiness,
|
||||
loadDesktopAppBuildStatus,
|
||||
@@ -161,17 +162,18 @@ const PlatformCard: React.FC<{
|
||||
mobile?: MobileAppReleaseInfoDto | null;
|
||||
}> = ({ platform, release, mobile }) => {
|
||||
const configs = {
|
||||
mac: { label: 'macOS (Apple Silicon · Intel)', icon: '🍎', ext: '.dmg', isLink: false },
|
||||
windows: { label: 'Windows (64-bit)', icon: '🪟', ext: '.exe', isLink: false },
|
||||
android: { label: 'Android (패드 · 폰 · 별도 버전)', icon: '🤖', ext: '.apk', isLink: false },
|
||||
mac: { label: 'macOS (Apple Silicon · Intel)', ext: '.dmg', isLink: false },
|
||||
windows: { label: 'Windows (64-bit)', ext: '.exe', isLink: false },
|
||||
android: { label: 'Android (패드 · 폰 · 별도 버전)', ext: '.apk', isLink: false },
|
||||
} as const;
|
||||
const { label, icon, ext } = configs[platform];
|
||||
const { label, ext } = configs[platform];
|
||||
const brandIcon = <PlatformBrandIcon platform={platform} />;
|
||||
|
||||
if (platform === 'android') {
|
||||
if (!mobile?.available || !mobile.downloadUrl) {
|
||||
return (
|
||||
<div className="app-download-pc-card app-download-pc-card--empty">
|
||||
<div className="app-download-pc-icon" aria-hidden>{icon}</div>
|
||||
{brandIcon}
|
||||
<div>
|
||||
<strong>{label}</strong>
|
||||
<p className="app-download-muted">설치 파일 준비 중 ({ext})</p>
|
||||
@@ -181,7 +183,7 @@ const PlatformCard: React.FC<{
|
||||
}
|
||||
return (
|
||||
<div className="app-download-pc-card">
|
||||
<div className="app-download-pc-icon" aria-hidden>{icon}</div>
|
||||
{brandIcon}
|
||||
<div className="app-download-pc-body">
|
||||
<strong>{label}</strong>
|
||||
<p className="app-download-meta">
|
||||
@@ -207,7 +209,7 @@ const PlatformCard: React.FC<{
|
||||
if (!release?.available || !release.downloadUrl) {
|
||||
return (
|
||||
<div className="app-download-pc-card app-download-pc-card--empty">
|
||||
<div className="app-download-pc-icon" aria-hidden>{icon}</div>
|
||||
{brandIcon}
|
||||
<div>
|
||||
<strong>{label}</strong>
|
||||
<p className="app-download-muted">설치 파일 준비 중 ({ext})</p>
|
||||
@@ -218,7 +220,7 @@ const PlatformCard: React.FC<{
|
||||
|
||||
return (
|
||||
<div className="app-download-pc-card">
|
||||
<div className="app-download-pc-icon" aria-hidden>{icon}</div>
|
||||
{brandIcon}
|
||||
<div className="app-download-pc-body">
|
||||
<strong>{label}</strong>
|
||||
<p className="app-download-meta">
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
type DesktopUpdateProgress,
|
||||
} from '../utils/desktopBridge';
|
||||
import { isMacDesktop, isWindowsDesktop } from '../utils/platform';
|
||||
import { PlatformBrandIcon, type PlatformBrandIconId } from './icons/PlatformBrandIcons';
|
||||
|
||||
interface Props {
|
||||
open: boolean;
|
||||
@@ -84,6 +85,12 @@ const DesktopUpdateModal: React.FC<Props> = ({ open, onClose, autoStart = true }
|
||||
? 100
|
||||
: progress?.downloadPercent;
|
||||
|
||||
const platformBrand: PlatformBrandIconId | null = isMacDesktop()
|
||||
? 'mac'
|
||||
: isWindowsDesktop()
|
||||
? 'windows'
|
||||
: null;
|
||||
|
||||
return (
|
||||
<DraggableModalFrame
|
||||
title="PC 프로그램 업데이트"
|
||||
@@ -93,6 +100,11 @@ const DesktopUpdateModal: React.FC<Props> = ({ open, onClose, autoStart = true }
|
||||
dialogClassName="sp-modal desktop-update-modal"
|
||||
>
|
||||
<div className="desktop-update-modal-body">
|
||||
{platformBrand && (
|
||||
<div className="desktop-update-platform-brand">
|
||||
<PlatformBrandIcon platform={platformBrand} />
|
||||
</div>
|
||||
)}
|
||||
<p className="desktop-update-version">
|
||||
현재 버전 <strong>v{progress?.currentVersion ?? appVersion}</strong>
|
||||
{progress?.newVersion && progress.newVersion !== progress.currentVersion && (
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
import React from 'react';
|
||||
|
||||
/** Apple 실루엣 로고 — 흰색 (다크 배경용) */
|
||||
export const AppleLogoWhite: React.FC<{ size?: number; className?: string }> = ({
|
||||
size = 22,
|
||||
className,
|
||||
}) => (
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 24 24"
|
||||
fill="#ffffff"
|
||||
className={className}
|
||||
aria-hidden
|
||||
>
|
||||
<path d="M17.05 20.28c-.98.95-2.05.88-3.08.4-1.09-.5-2.08-.48-3.24 0-1.44.62-2.2.44-3.06-.4C2.79 15.25 3.51 7.59 9.05 7.31c1.35.07 2.29.74 3.08.8 1.18-.24 2.31-.93 3.57-.84 1.51.12 2.65.72 3.4 1.8-3.12 1.87-2.38 5.98.48 7.13-.57 1.5-1.31 2.99-2.54 4.09l.01-.01zM12.03 7.25c-.15-2.23 1.66-4.07 3.74-4.25.29 2.58-2.34 4.5-3.74 4.25z" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
/** Microsoft Windows 4-pane 로고 */
|
||||
export const WindowsLogo: React.FC<{ size?: number; className?: string }> = ({
|
||||
size = 22,
|
||||
className,
|
||||
}) => (
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 24 24"
|
||||
className={className}
|
||||
aria-hidden
|
||||
>
|
||||
<path fill="#f25022" d="M3 3h8.5v8.5H3V3z" />
|
||||
<path fill="#7fba00" d="M12.5 3H21v8.5h-8.5V3z" />
|
||||
<path fill="#00a4ef" d="M3 12.5h8.5V21H3v-8.5z" />
|
||||
<path fill="#ffb900" d="M12.5 12.5H21V21h-8.5v-8.5z" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
export type PlatformBrandIconId = 'mac' | 'windows' | 'android';
|
||||
|
||||
export const PlatformBrandIcon: React.FC<{
|
||||
platform: PlatformBrandIconId;
|
||||
className?: string;
|
||||
}> = ({ platform, className }) => {
|
||||
if (platform === 'mac') {
|
||||
return (
|
||||
<span className={`app-download-pc-icon app-download-pc-icon--mac${className ? ` ${className}` : ''}`}>
|
||||
<AppleLogoWhite size={22} />
|
||||
</span>
|
||||
);
|
||||
}
|
||||
if (platform === 'windows') {
|
||||
return (
|
||||
<span className={`app-download-pc-icon app-download-pc-icon--windows${className ? ` ${className}` : ''}`}>
|
||||
<WindowsLogo size={22} />
|
||||
</span>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<span className={`app-download-pc-icon app-download-pc-icon--android${className ? ` ${className}` : ''}`} aria-hidden>
|
||||
🤖
|
||||
</span>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user