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
@@ -0,0 +1,17 @@
import { openWidgetWindow, closeWidgetWindow, focusWidgetWindow } from './widgetWindows';
import { showNativeNotification } from './notifications';
import { checkForUpdates, getAppVersion } from './updater';
import type { DesktopBridge } from './types';
/** frontend App 로드 전 호출 — window.__goldenDesktopBridge 등록 */
export function installDesktopBridge(): void {
const bridge: DesktopBridge = {
openWidgetWindow,
closeWidgetWindow,
focusWidgetWindow,
showNativeNotification,
checkForUpdates,
getAppVersion,
};
window.__goldenDesktopBridge = bridge;
}
+19
View File
@@ -0,0 +1,19 @@
import {
isPermissionGranted,
requestPermission,
sendNotification,
} from '@tauri-apps/plugin-notification';
export async function showNativeNotification(title: string, body: string): Promise<void> {
let granted = await isPermissionGranted();
if (!granted) {
const perm = await requestPermission();
granted = perm === 'granted';
}
if (!granted) return;
await sendNotification({
title,
body,
});
}
+21
View File
@@ -0,0 +1,21 @@
/**
* 데스크톱(Tauri) 브릿지 타입 — frontend에서 window.__goldenDesktopBridge 로 호출
*/
import type { FloatingWidgetInstance } from '@frontend/types/floatingWidget';
export interface DesktopBridge {
openWidgetWindow: (instance: 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 {};
+26
View File
@@ -0,0 +1,26 @@
import { check } from '@tauri-apps/plugin-updater';
import { relaunch } from '@tauri-apps/plugin-process';
import { getVersion } from '@tauri-apps/api/app';
export async function getAppVersion(): Promise<string> {
return getVersion();
}
export async function checkForUpdates(): Promise<{
available: boolean;
version?: string;
message?: string;
}> {
try {
const update = await check();
if (!update) {
return { available: false, message: '최신 버전입니다.' };
}
await update.downloadAndInstall();
await relaunch();
return { available: true, version: update.version, message: '업데이트 설치 후 재시작합니다.' };
} catch (e) {
const msg = e instanceof Error ? e.message : '업데이트 확인 실패';
return { available: false, message: msg };
}
}
+63
View File
@@ -0,0 +1,63 @@
import { WebviewWindow } from '@tauri-apps/api/webviewWindow';
import type { FloatingWidgetInstance } from '@frontend/types/floatingWidget';
const widgetWindows = new Map<string, WebviewWindow>();
function widgetUrl(instance: FloatingWidgetInstance): string {
const params = new URLSearchParams({
id: instance.id,
rows: String(instance.rows),
cols: String(instance.cols),
width: String(instance.width),
height: String(instance.height),
});
if (instance.rowFr?.length) params.set('rowFr', instance.rowFr.join(','));
if (instance.colFr?.length) params.set('colFr', instance.colFr.join(','));
params.set('slots', JSON.stringify(instance.slots));
return `widget.html?${params.toString()}`;
}
export async function openWidgetWindow(instance: FloatingWidgetInstance): Promise<void> {
const label = `widget-${instance.id}`;
const existing = widgetWindows.get(instance.id) ?? (await WebviewWindow.getByLabel(label));
if (existing) {
await existing.setFocus();
widgetWindows.set(instance.id, existing);
return;
}
const win = new WebviewWindow(label, {
url: widgetUrl(instance),
title: 'GoldenChart Widget',
width: instance.width,
height: instance.height,
minWidth: 320,
minHeight: 240,
decorations: true,
resizable: true,
center: false,
x: instance.position.x,
y: instance.position.y,
});
widgetWindows.set(instance.id, win);
void win.once('tauri://destroyed', () => {
widgetWindows.delete(instance.id);
});
}
export async function closeWidgetWindow(id: string): Promise<void> {
const label = `widget-${id}`;
const win = widgetWindows.get(id) ?? (await WebviewWindow.getByLabel(label));
if (win) {
await win.close();
widgetWindows.delete(id);
}
}
export async function focusWidgetWindow(id: string): Promise<void> {
const label = `widget-${id}`;
const win = widgetWindows.get(id) ?? (await WebviewWindow.getByLabel(label));
if (win) await win.setFocus();
}