desktop 앱 적용

This commit is contained in:
Macbook
2026-06-14 10:15:54 +09:00
parent 7a2f65088c
commit 86b99d8c10
81 changed files with 8164 additions and 37 deletions
+47
View File
@@ -0,0 +1,47 @@
import { isDesktop } from './platform';
export interface DesktopBridge {
openWidgetWindow: (instance: import('../types/floatingWidget').FloatingWidgetInstance) => Promise<void>;
closeWidgetWindow: (id: string) => Promise<void>;
focusWidgetWindow: (id: string) => Promise<void>;
showNativeNotification: (title: string, body: string) => Promise<void>;
checkForUpdates: () => Promise<{ available: boolean; version?: string; message?: string }>;
getAppVersion: () => Promise<string>;
}
declare global {
interface Window {
__goldenDesktopBridge?: DesktopBridge;
}
}
export function getDesktopBridge(): DesktopBridge | null {
if (!isDesktop()) return null;
return window.__goldenDesktopBridge ?? null;
}
export async function showDesktopNativeNotification(title: string, body: string): Promise<void> {
const bridge = getDesktopBridge();
if (!bridge) return;
try {
await bridge.showNativeNotification(title, body);
} catch {
/* ignore */
}
}
export async function checkDesktopUpdates(): Promise<{ available: boolean; version?: string; message?: string }> {
const bridge = getDesktopBridge();
if (!bridge) return { available: false, message: '데스크톱 앱에서만 사용 가능합니다.' };
return bridge.checkForUpdates();
}
export async function getDesktopAppVersion(): Promise<string | null> {
const bridge = getDesktopBridge();
if (!bridge) return null;
try {
return await bridge.getAppVersion();
} catch {
return null;
}
}
+43
View File
@@ -0,0 +1,43 @@
/** 데스크톱 메인↔위젯 창 상태 동기화 (localStorage) */
export const DESKTOP_SYNC_KEY = 'gc_desktop_sync';
export interface DesktopSyncState {
selectedMarket?: string;
selectedStrategyId?: number | null;
theme?: string;
updatedAt: number;
}
export function readDesktopSync(): DesktopSyncState | null {
try {
const raw = localStorage.getItem(DESKTOP_SYNC_KEY);
if (!raw) return null;
return JSON.parse(raw) as DesktopSyncState;
} catch {
return null;
}
}
export function writeDesktopSync(partial: Omit<DesktopSyncState, 'updatedAt'>): void {
const prev = readDesktopSync() ?? { updatedAt: 0 };
const next: DesktopSyncState = {
...prev,
...partial,
updatedAt: Date.now(),
};
try {
localStorage.setItem(DESKTOP_SYNC_KEY, JSON.stringify(next));
} catch {
/* ignore */
}
}
export function subscribeDesktopSync(cb: (state: DesktopSyncState) => void): () => void {
const onStorage = (e: StorageEvent) => {
if (e.key !== DESKTOP_SYNC_KEY) return;
const state = readDesktopSync();
if (state) cb(state);
};
window.addEventListener('storage', onStorage);
return () => window.removeEventListener('storage', onStorage);
}
+26
View File
@@ -0,0 +1,26 @@
/** 런타임 플랫폼 — web · desktop(Tauri) · mobile */
export type RuntimePlatform = 'web' | 'desktop' | 'mobile';
export function isDesktop(): boolean {
return typeof window !== 'undefined' && '__TAURI__' in window;
}
export function isMobileCapacitor(): boolean {
if (typeof window === 'undefined') return false;
try {
const cap = (window as Window & { Capacitor?: { isNativePlatform?: () => boolean } }).Capacitor;
return cap?.isNativePlatform?.() === true;
} catch {
return false;
}
}
export function getRuntimePlatform(): RuntimePlatform {
if (isDesktop()) return 'desktop';
if (isMobileCapacitor()) return 'mobile';
return 'web';
}
export function isWeb(): boolean {
return getRuntimePlatform() === 'web';
}