commit 13cd08de8db18978942324f7a19c1548f9bc630f Author: angworktop Date: Tue Nov 18 18:08:25 2025 +0900 Sure! Pl diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea0bc43 --- /dev/null +++ b/.gitignore @@ -0,0 +1,26 @@ +# Dependencies +node_modules/ +package-lock.json + +# Rust +src-tauri/target/ +src-tauri/Cargo.lock + +# Tauri +src-tauri/gen/ + +# IDE +.idea/ +.vscode/ +*.swp +*.swo +*~ + +# OS +.DS_Store +Thumbs.db + +# Build outputs +dist/ +build/ + diff --git a/PROJECT_STRUCTURE.md b/PROJECT_STRUCTURE.md new file mode 100644 index 0000000..7fd6412 --- /dev/null +++ b/PROJECT_STRUCTURE.md @@ -0,0 +1,114 @@ +# 프로젝트 구조 가이드 + +## 현재 구조 (표준 Tauri 구조) + +``` +ndd-drive/ +├── src/ # 프론트엔드 소스 코드 +│ └── index.html # 메인 HTML 파일 +│ +├── src-tauri/ # Rust 백엔드 (Tauri) +│ ├── src/ # Rust 소스 파일 +│ │ ├── main.rs # 진입점 +│ │ └── lib.rs # 라이브러리 진입점 +│ ├── Cargo.toml # Rust 의존성 관리 +│ ├── tauri.conf.json # Tauri 설정 +│ ├── build.rs # 빌드 스크립트 +│ ├── icons/ # 앱 아이콘 +│ ├── capabilities/ # Tauri 권한 설정 +│ ├── gen/ # 자동 생성 파일 (gitignore) +│ └── target/ # 빌드 결과물 (gitignore) +│ +├── package.json # Node.js 의존성 및 스크립트 +├── node_modules/ # Node.js 패키지 (gitignore) +└── package-lock.json # 패키지 잠금 파일 +``` + +## 개선된 구조 제안 + +### 옵션 1: 현재 구조 유지 + 모듈화 (추천) + +``` +ndd-drive/ +├── src/ # 프론트엔드 +│ ├── index.html +│ ├── css/ +│ │ ├── main.css +│ │ └── components/ +│ ├── js/ +│ │ ├── main.js +│ │ ├── components/ +│ │ └── utils/ +│ ├── assets/ +│ │ ├── images/ +│ │ └── fonts/ +│ └── components/ # 재사용 가능한 컴포넌트 +│ +├── src-tauri/ # Rust 백엔드 +│ ├── src/ +│ │ ├── main.rs +│ │ ├── lib.rs +│ │ ├── commands/ # Tauri 커맨드 (프론트엔드에서 호출) +│ │ │ └── mod.rs +│ │ ├── utils/ # 유틸리티 함수 +│ │ │ └── mod.rs +│ │ └── models/ # 데이터 모델 +│ │ └── mod.rs +│ ├── Cargo.toml +│ ├── tauri.conf.json +│ ├── build.rs +│ ├── icons/ +│ └── capabilities/ +│ +├── package.json +└── README.md +``` + +### 옵션 2: 프론트엔드 프레임워크 사용 시 (React/Vue 등) + +``` +ndd-drive/ +├── src/ # 프론트엔드 (프레임워크) +│ ├── index.html +│ ├── main.jsx / main.tsx +│ ├── App.jsx / App.tsx +│ ├── components/ +│ ├── pages/ +│ ├── hooks/ # React hooks 또는 Vue composables +│ ├── utils/ +│ ├── styles/ +│ └── assets/ +│ +├── src-tauri/ # Rust 백엔드 (동일) +│ └── ... +│ +├── public/ # 정적 파일 +├── package.json +└── vite.config.js / webpack.config.js +``` + +## 각 디렉토리 설명 + +### `/src` - 프론트엔드 +- **index.html**: 앱의 진입점 HTML +- **css/**: 스타일시트 파일 +- **js/**: JavaScript 파일 +- **assets/**: 이미지, 폰트 등 정적 리소스 +- **components/**: 재사용 가능한 UI 컴포넌트 + +### `/src-tauri` - Rust 백엔드 +- **src/main.rs**: Rust 프로그램 진입점 +- **src/lib.rs**: 라이브러리 진입점 (Tauri 앱 설정) +- **src/commands/**: Tauri 커맨드 (프론트엔드에서 호출 가능한 Rust 함수) +- **src/utils/**: 공통 유틸리티 함수 +- **src/models/**: 데이터 구조 및 모델 +- **tauri.conf.json**: Tauri 앱 설정 (윈도우 크기, 권한 등) +- **capabilities/**: 보안 권한 설정 + +## 권장 사항 + +1. **프론트엔드 모듈화**: CSS와 JS를 분리하여 관리 +2. **Rust 코드 모듈화**: 기능별로 파일 분리 (commands, utils 등) +3. **.gitignore**: target/, node_modules/, gen/ 등 빌드 결과물 제외 +4. **문서화**: README.md에 프로젝트 설명 및 실행 방법 작성 + diff --git a/README_WEB.md b/README_WEB.md new file mode 100644 index 0000000..9a77867 --- /dev/null +++ b/README_WEB.md @@ -0,0 +1,41 @@ +# 웹에서 실행하기 + +## 방법 1: npm 스크립트 사용 (추천) + +```bash +npm run web:dev +``` + +또는 + +```bash +npm run web:serve +``` + +브라우저에서 `http://localhost:8080`으로 접속 + +## 방법 2: Python 간단 서버 + +```bash +# Python 3 +cd src +python -m http.server 8080 +``` + +## 방법 3: Node.js http-server + +```bash +npx http-server src -p 8080 -o +``` + +## 방법 4: VS Code Live Server + +VS Code의 "Live Server" 확장 프로그램 사용 + +## 주의사항 + +- 웹에서는 Tauri API가 작동하지 않습니다 +- 트레이 기능은 데스크탑 앱에서만 동작합니다 +- 웹에서는 UI만 확인 가능합니다 +- 버튼 클릭 시 상태 텍스트는 변경되지만, 실제 트레이는 업데이트되지 않습니다 + diff --git a/TRAY_IMPLEMENTATION.md b/TRAY_IMPLEMENTATION.md new file mode 100644 index 0000000..6ad219d --- /dev/null +++ b/TRAY_IMPLEMENTATION.md @@ -0,0 +1,97 @@ +# 시스템 트레이 구현 가이드 + +## 개요 + +Proton Drive처럼 시스템 트레이 아이콘을 표시하고, 로그인 상태에 따라 아이콘 색상과 툴팁을 변경하는 기능을 구현하는 방법입니다. + +## 구조 설명 + +### 1. 파일 구조 + +``` +src-tauri/src/ +├── lib.rs # 메인 앱 설정 (트레이 초기화) +├── tray.rs # 트레이 관련 로직 (별도 모듈) +└── commands/ + └── tray_commands.rs # 프론트엔드에서 호출할 트레이 커맨드 +``` + +### 2. 모듈 분리 이유 + +**`tray.rs` (별도 파일로 분리)** +- ✅ **유지보수성**: 트레이 관련 코드가 한 곳에 모여있어 수정이 쉬움 +- ✅ **확장성**: 새로운 트레이 기능 추가 시 해당 파일만 수정 +- ✅ **가독성**: 메인 코드(`lib.rs`)가 간결해짐 +- ✅ **테스트 용이**: 트레이 로직을 독립적으로 테스트 가능 + +### 3. 구현 방법 + +#### Tauri 2.x에서 시스템 트레이 사용 + +Tauri 2.x에서는 시스템 트레이 기능이 변경되었을 수 있습니다. 다음 중 하나의 방법을 사용해야 합니다: + +**방법 1: Tauri 플러그인 사용 (권장)** +```toml +# Cargo.toml에 추가 +[dependencies] +tauri-plugin-system-tray = "2.0" +``` + +**방법 2: 직접 구현** +- `tray-icon` 크레이트 사용 +- 또는 Tauri 2.x의 새로운 API 사용 + +### 4. 현재 구현된 구조 + +#### `src-tauri/src/tray.rs` +- `TrayStatus` 열거형: 로그인 상태 정의 +- `create_system_tray()`: 트레이 생성 +- `handle_tray_event()`: 트레이 이벤트 처리 +- `update_tray_status()`: 상태에 따른 아이콘/툴팁 업데이트 + +#### `src-tauri/src/commands/tray_commands.rs` +- `update_tray_icon()`: 프론트엔드에서 호출 가능한 커맨드 + +### 5. 사용 예시 + +**프론트엔드에서 호출:** +```javascript +import { invoke } from '@tauri-apps/api/core'; + +// 로그인 상태로 변경 +await invoke('update_tray_icon', { status: 'logged_in' }); + +// 동기화 중 상태로 변경 +await invoke('update_tray_icon', { status: 'syncing' }); +``` + +### 6. 아이콘 색상 변경 방법 + +Proton Drive처럼 아이콘 색상을 변경하려면: + +**옵션 1: 상태별 아이콘 파일 준비** +``` +src-tauri/icons/ +├── icon_logged_out.png (회색) +├── icon_logged_in.png (녹색) +├── icon_syncing.png (파란색) +└── icon_error.png (빨간색) +``` + +**옵션 2: 런타임 아이콘 생성** +- `image` 크레이트를 사용하여 런타임에 아이콘 색상 변경 +- Cargo.toml에 추가: `image = "0.24"` + +### 7. 다음 단계 + +1. **Tauri 2.x API 확인**: 정확한 시스템 트레이 API 확인 필요 +2. **플러그인 설치**: `tauri-plugin-system-tray` 플러그인 사용 여부 확인 +3. **아이콘 준비**: 상태별 아이콘 파일 생성 +4. **테스트**: 각 상태에서 트레이 동작 확인 + +## 참고사항 + +- Tauri 2.x의 시스템 트레이 API는 1.x와 다를 수 있습니다 +- 실제 구현 전에 Tauri 2.x 공식 문서를 확인하세요 +- OS별로 트레이 동작이 다를 수 있습니다 (Windows, macOS, Linux) + diff --git a/package.json b/package.json new file mode 100644 index 0000000..501adfc --- /dev/null +++ b/package.json @@ -0,0 +1,15 @@ +{ + "scripts": { + "tauri": "tauri", + "tauri:dev": "tauri dev", + "tauri:build": "tauri build", + "web:dev": "npx http-server src -p 8080 -o", + "web:serve": "npx serve src -l 8080" + }, + "dependencies": { + "@tauri-apps/api": "^2.0.0" + }, + "devDependencies": { + "@tauri-apps/cli": "^2.9.4" + } +} diff --git a/src-tauri/.gitignore b/src-tauri/.gitignore new file mode 100644 index 0000000..502406b --- /dev/null +++ b/src-tauri/.gitignore @@ -0,0 +1,4 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ +/gen/schemas diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml new file mode 100644 index 0000000..d3f0856 --- /dev/null +++ b/src-tauri/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "app" +version = "0.1.0" +description = "A Tauri App" +authors = ["you"] +license = "" +repository = "" +edition = "2021" +rust-version = "1.77.2" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +name = "app_lib" +crate-type = ["staticlib", "cdylib", "rlib"] + +[build-dependencies] +tauri-build = { version = "2.5.1", features = [] } + +[dependencies] +serde_json = "1.0" +serde = { version = "1.0", features = ["derive"] } +log = "0.4" +tauri = { version = "2.9.2", features = ["tray-icon"] } +tauri-plugin-log = "2" diff --git a/src-tauri/build.rs b/src-tauri/build.rs new file mode 100644 index 0000000..795b9b7 --- /dev/null +++ b/src-tauri/build.rs @@ -0,0 +1,3 @@ +fn main() { + tauri_build::build() +} diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json new file mode 100644 index 0000000..c135d7f --- /dev/null +++ b/src-tauri/capabilities/default.json @@ -0,0 +1,11 @@ +{ + "$schema": "../gen/schemas/desktop-schema.json", + "identifier": "default", + "description": "enables the default permissions", + "windows": [ + "main" + ], + "permissions": [ + "core:default" + ] +} diff --git a/src-tauri/icons/128x128.png b/src-tauri/icons/128x128.png new file mode 100644 index 0000000..77e7d23 Binary files /dev/null and b/src-tauri/icons/128x128.png differ diff --git a/src-tauri/icons/128x128@2x.png b/src-tauri/icons/128x128@2x.png new file mode 100644 index 0000000..0f7976f Binary files /dev/null and b/src-tauri/icons/128x128@2x.png differ diff --git a/src-tauri/icons/32x32.png b/src-tauri/icons/32x32.png new file mode 100644 index 0000000..98fda06 Binary files /dev/null and b/src-tauri/icons/32x32.png differ diff --git a/src-tauri/icons/Square107x107Logo.png b/src-tauri/icons/Square107x107Logo.png new file mode 100644 index 0000000..f35d84f Binary files /dev/null and b/src-tauri/icons/Square107x107Logo.png differ diff --git a/src-tauri/icons/Square142x142Logo.png b/src-tauri/icons/Square142x142Logo.png new file mode 100644 index 0000000..1823bb2 Binary files /dev/null and b/src-tauri/icons/Square142x142Logo.png differ diff --git a/src-tauri/icons/Square150x150Logo.png b/src-tauri/icons/Square150x150Logo.png new file mode 100644 index 0000000..dc2b22c Binary files /dev/null and b/src-tauri/icons/Square150x150Logo.png differ diff --git a/src-tauri/icons/Square284x284Logo.png b/src-tauri/icons/Square284x284Logo.png new file mode 100644 index 0000000..0ed3984 Binary files /dev/null and b/src-tauri/icons/Square284x284Logo.png differ diff --git a/src-tauri/icons/Square30x30Logo.png b/src-tauri/icons/Square30x30Logo.png new file mode 100644 index 0000000..60bf0ea Binary files /dev/null and b/src-tauri/icons/Square30x30Logo.png differ diff --git a/src-tauri/icons/Square310x310Logo.png b/src-tauri/icons/Square310x310Logo.png new file mode 100644 index 0000000..c8ca0ad Binary files /dev/null and b/src-tauri/icons/Square310x310Logo.png differ diff --git a/src-tauri/icons/Square44x44Logo.png b/src-tauri/icons/Square44x44Logo.png new file mode 100644 index 0000000..8756459 Binary files /dev/null and b/src-tauri/icons/Square44x44Logo.png differ diff --git a/src-tauri/icons/Square71x71Logo.png b/src-tauri/icons/Square71x71Logo.png new file mode 100644 index 0000000..2c8023c Binary files /dev/null and b/src-tauri/icons/Square71x71Logo.png differ diff --git a/src-tauri/icons/Square89x89Logo.png b/src-tauri/icons/Square89x89Logo.png new file mode 100644 index 0000000..2c5e603 Binary files /dev/null and b/src-tauri/icons/Square89x89Logo.png differ diff --git a/src-tauri/icons/StoreLogo.png b/src-tauri/icons/StoreLogo.png new file mode 100644 index 0000000..17d142c Binary files /dev/null and b/src-tauri/icons/StoreLogo.png differ diff --git a/src-tauri/icons/icon.icns b/src-tauri/icons/icon.icns new file mode 100644 index 0000000..a2993ad Binary files /dev/null and b/src-tauri/icons/icon.icns differ diff --git a/src-tauri/icons/icon.ico b/src-tauri/icons/icon.ico new file mode 100644 index 0000000..06c23c8 Binary files /dev/null and b/src-tauri/icons/icon.ico differ diff --git a/src-tauri/icons/icon.png b/src-tauri/icons/icon.png new file mode 100644 index 0000000..d1756ce Binary files /dev/null and b/src-tauri/icons/icon.png differ diff --git a/src-tauri/src/commands/mod.rs b/src-tauri/src/commands/mod.rs new file mode 100644 index 0000000..8e752f3 --- /dev/null +++ b/src-tauri/src/commands/mod.rs @@ -0,0 +1,22 @@ +// Tauri 커맨드 모듈 +// 프론트엔드에서 호출할 수 있는 Rust 함수들을 여기에 정의 + +mod tray_commands; + +pub use tray_commands::update_tray_icon; + +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize)] +pub struct GreetResponse { + pub message: String, +} + +/// 예시: 프론트엔드에서 호출할 수 있는 커맨드 +#[tauri::command] +pub fn greet(name: &str) -> GreetResponse { + GreetResponse { + message: format!("Hello, {}! You've been greeted from Rust!", name), + } +} + diff --git a/src-tauri/src/commands/tray_commands.rs b/src-tauri/src/commands/tray_commands.rs new file mode 100644 index 0000000..dcf2a5c --- /dev/null +++ b/src-tauri/src/commands/tray_commands.rs @@ -0,0 +1,31 @@ +// 트레이 관련 Tauri 커맨드 +// 프론트엔드에서 트레이 상태를 업데이트할 수 있는 커맨드 + +use crate::tray::{TrayStatus, update_tray_status}; +use tauri::{AppHandle, Manager}; +use std::sync::{Arc, Mutex}; + +/// 프론트엔드에서 호출: 트레이 상태 업데이트 +#[tauri::command] +pub fn update_tray_icon(app: AppHandle, status: String) -> Result<(), String> { + let tray_status = match status.as_str() { + "logged_out" => TrayStatus::LoggedOut, + "logged_in" => TrayStatus::LoggedIn, + "syncing" => TrayStatus::Syncing, + "error" => TrayStatus::Error, + _ => return Err(format!("Unknown status: {}", status)), + }; + + // 앱 상태에서 트레이 가져오기 + if let Some(tray_state) = app.try_state::>>>() { + if let Ok(tray_mutex) = tray_state.lock() { + if let Some(ref tray) = *tray_mutex { + update_tray_status(&app, tray, tray_status) + .map_err(|e| format!("Failed to update tray: {}", e))?; + } + } + } + + Ok(()) +} + diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs new file mode 100644 index 0000000..dfc5b26 --- /dev/null +++ b/src-tauri/src/lib.rs @@ -0,0 +1,54 @@ +// 모듈 선언 +mod commands; +mod tray; + +use commands::{greet, update_tray_icon}; +use tray::{create_system_tray, update_tray_status, TrayStatus}; +use tauri::Manager; +use std::sync::{Arc, Mutex}; + +#[cfg_attr(mobile, tauri::mobile_entry_point)] +pub fn run() { + tauri::Builder::default() + .invoke_handler(tauri::generate_handler![greet, update_tray_icon]) + .setup(|app| { + // 시스템 트레이 생성 + let app_handle = app.handle().clone(); + let tray = create_system_tray(&app_handle)?; + + // 트레이를 앱 상태에 저장 (나중에 업데이트하기 위해) + app.manage(Arc::new(Mutex::new(Some(tray)))); + + // 초기 트레이 상태 설정 (로그아웃 상태) + if let Some(tray_state) = app.try_state::>>>() { + if let Ok(tray_mutex) = tray_state.lock() { + if let Some(ref tray) = *tray_mutex { + update_tray_status(&app_handle, tray, TrayStatus::LoggedOut)?; + } + } + } + + // X 버튼 클릭 시 종료하지 않고 숨기기 + if let Some(window) = app.get_webview_window("main") { + let window_clone = window.clone(); + window.on_window_event(move |event| { + if let tauri::WindowEvent::CloseRequested { api, .. } = event { + // 종료 대신 숨기기 + api.prevent_close(); + let _ = window_clone.hide(); + } + }); + } + + if cfg!(debug_assertions) { + app.handle().plugin( + tauri_plugin_log::Builder::default() + .level(log::LevelFilter::Info) + .build(), + )?; + } + Ok(()) + }) + .run(tauri::generate_context!()) + .expect("error while running tauri application"); +} diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs new file mode 100644 index 0000000..ad5fe83 --- /dev/null +++ b/src-tauri/src/main.rs @@ -0,0 +1,6 @@ +// Prevents additional console window on Windows in release, DO NOT REMOVE!! +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] + +fn main() { + app_lib::run(); +} diff --git a/src-tauri/src/tray.rs b/src-tauri/src/tray.rs new file mode 100644 index 0000000..82c3b16 --- /dev/null +++ b/src-tauri/src/tray.rs @@ -0,0 +1,100 @@ +// 시스템 트레이 모듈 +// Proton Drive처럼 로그인 상태에 따라 아이콘과 툴팁을 변경하는 기능 + +use tauri::{AppHandle, Manager}; +use tauri::tray::{TrayIconBuilder, MouseButton, MouseButtonState}; +use tauri::menu::{Menu, MenuItem}; +use tauri::tray::TrayIconEvent; + +/// 트레이 상태 열거형 +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum TrayStatus { + LoggedOut, // 로그아웃 상태 + LoggedIn, // 로그인 상태 + Syncing, // 동기화 중 + Error, // 오류 상태 +} + +/// 시스템 트레이 생성 및 설정 +pub fn create_system_tray(app: &AppHandle) -> Result> { + // 트레이 메뉴 생성 + let show_item = MenuItem::with_id(app, "show", "Show Window", true, None::<&str>)?; + let quit_item = MenuItem::with_id(app, "quit", "Quit", true, None::<&str>)?; + + let menu = Menu::new(app)?; + menu.append(&show_item)?; + // separator는 Tauri 2.x에서 다른 방식으로 구현해야 할 수 있음 + menu.append(&quit_item)?; + + // 트레이 아이콘 생성 (기본 아이콘 사용) + // 아이콘은 tauri.conf.json에서 설정된 기본 아이콘 사용 + // 런타임에 아이콘을 변경하려면 별도로 로드해야 함 + + let tray = TrayIconBuilder::new() + .tooltip("NaDoDev Drive") + .menu(&menu) + .on_menu_event(|app, event| { + match event.id.as_ref() { + "show" => { + if let Some(window) = app.get_webview_window("main") { + let _ = window.show(); + let _ = window.set_focus(); + } + } + "quit" => { + app.exit(0); + } + _ => {} + } + }) + .on_tray_icon_event(|tray, event| { + if let TrayIconEvent::Click { + button: MouseButton::Left, + button_state: MouseButtonState::Up, + .. + } = event + { + if let Some(window) = tray.app_handle().get_webview_window("main") { + if window.is_visible().unwrap_or(false) { + let _ = window.hide(); + } else { + let _ = window.show(); + let _ = window.set_focus(); + } + } + } + }) + .build(app)?; + + Ok(tray) +} + +// 트레이 이벤트는 create_system_tray 내부에서 처리됨 + +/// 트레이 아이콘 및 툴팁 업데이트 +/// +/// 참고: Proton Drive처럼 상태별로 다른 아이콘을 사용하려면 +/// icons/ 디렉토리에 상태별 아이콘 파일을 준비해야 합니다: +/// - icon_logged_out.png (로그아웃) +/// - icon_logged_in.png (로그인 - 녹색) +/// - icon_syncing.png (동기화 중 - 파란색) +/// - icon_error.png (오류 - 빨간색) +pub fn update_tray_status(_app: &AppHandle, tray: &tauri::tray::TrayIcon, status: TrayStatus) -> Result<(), Box> { + // 툴팁 텍스트 업데이트 + let tooltip = match status { + TrayStatus::LoggedOut => "NaDoDev Drive - Not logged in", + TrayStatus::LoggedIn => "NaDoDev Drive - Logged in", + TrayStatus::Syncing => "NaDoDev Drive - Syncing...", + TrayStatus::Error => "NaDoDev Drive - Error", + }; + + tray.set_tooltip(Some(tooltip))?; + + // TODO: 아이콘 색상 변경을 위해서는: + // 1. 상태별 아이콘 파일을 준비하고 런타임에 로드하거나 + // 2. image 크레이트를 사용하여 런타임에 아이콘을 동적으로 생성 + // 현재는 툴팁만 업데이트합니다. + + Ok(()) +} + diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json new file mode 100644 index 0000000..4f731a8 --- /dev/null +++ b/src-tauri/tauri.conf.json @@ -0,0 +1,36 @@ +{ + "$schema": "../node_modules/@tauri-apps/cli/config.schema.json", + "productName": "NaDoDev Drive", + "version": "0.1.0", + "identifier": "com.tauri.dev", + "build": { + "frontendDist": "../src", + "beforeDevCommand": "", + "beforeBuildCommand": "npm run build" + }, + "app": { + "windows": [ + { + "title": "NaDoDev Drive", + "width": 800, + "height": 600, + "resizable": true, + "fullscreen": false + } + ], + "security": { + "csp": null + } + }, + "bundle": { + "active": true, + "targets": "all", + "icon": [ + "icons/32x32.png", + "icons/128x128.png", + "icons/128x128@2x.png", + "icons/icon.icns", + "icons/icon.ico" + ] + } +} diff --git a/src/css/main.css b/src/css/main.css new file mode 100644 index 0000000..5ff01e1 --- /dev/null +++ b/src/css/main.css @@ -0,0 +1,99 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; + display: flex; + justify-content: center; + align-items: center; + min-height: 100vh; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: white; +} + +.container { + text-align: center; + padding: 2rem; +} + +h1 { + font-size: 3rem; + margin-bottom: 1rem; + text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3); +} + +p { + font-size: 1.2rem; + opacity: 0.9; +} + +.logo { + font-size: 5rem; + margin-bottom: 1rem; +} + +.tray-controls { + margin-top: 3rem; + padding: 2rem; + background: rgba(255, 255, 255, 0.1); + border-radius: 1rem; + backdrop-filter: blur(10px); + max-width: 500px; + margin-left: auto; + margin-right: auto; +} + +.tray-controls h2 { + font-size: 1.5rem; + margin-bottom: 1rem; +} + +.status-text { + font-size: 1rem; + margin-bottom: 1.5rem; + opacity: 0.9; +} + +#current-status { + font-weight: bold; + color: #ffd700; +} + +.button-group { + display: flex; + flex-direction: column; + gap: 0.75rem; + margin-bottom: 1rem; +} + +.tray-btn { + padding: 0.75rem 1.5rem; + font-size: 1rem; + border: none; + border-radius: 0.5rem; + background: rgba(255, 255, 255, 0.2); + color: white; + cursor: pointer; + transition: all 0.3s ease; + font-weight: 500; +} + +.tray-btn:hover { + background: rgba(255, 255, 255, 0.3); + transform: translateY(-2px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); +} + +.tray-btn:active { + transform: translateY(0); +} + +.hint { + font-size: 0.9rem; + opacity: 0.8; + margin-top: 1rem; +} + diff --git a/src/index.html b/src/index.html new file mode 100644 index 0000000..ba30208 --- /dev/null +++ b/src/index.html @@ -0,0 +1,32 @@ + + + + + + NaDoDev Drive + + + +
+ +

NaDoDev Drive

+

Tauri 데스크탑 애플리케이션

+ +
+

시스템 트레이 테스트

+

현재 상태: 로그아웃

+ +
+ + + + +
+ +

💡 시스템 트레이 아이콘을 확인하세요!

+
+
+ + + + diff --git a/src/js/main.js b/src/js/main.js new file mode 100644 index 0000000..0b9f9ec --- /dev/null +++ b/src/js/main.js @@ -0,0 +1,89 @@ +// 메인 JavaScript 파일 +console.log('NaDoDev Drive 앱이 시작되었습니다.'); + +// Tauri API 체크 및 import +let invoke = null; +let isTauri = false; + +// Tauri 환경 초기화 함수 +async function initTauri() { + try { + // Tauri 환경 체크 + if (window.__TAURI__) { + isTauri = true; + const tauriApi = await import('@tauri-apps/api/core'); + invoke = tauriApi.invoke; + console.log('Tauri 환경에서 실행 중'); + return true; + } else { + console.log('웹 브라우저에서 실행 중 (Tauri API 사용 불가)'); + return false; + } + } catch (error) { + console.log('웹 브라우저에서 실행 중 (Tauri API 사용 불가)', error); + return false; + } +} + +// 트레이 상태 업데이트 함수 +async function updateTrayStatus(status, displayName) { + // UI 업데이트 (항상 실행) + const statusElement = document.getElementById('current-status'); + if (statusElement) { + statusElement.textContent = displayName; + } + + if (isTauri && invoke) { + // Tauri 환경: 실제 트레이 업데이트 + try { + await invoke('update_tray_icon', { status }); + console.log(`트레이 상태가 "${displayName}"로 변경되었습니다.`); + } catch (error) { + console.error('트레이 상태 업데이트 실패:', error); + alert(`오류 발생: ${error}`); + } + } else { + // 웹 환경: UI만 업데이트 (트레이 기능 없음) + console.log(`웹 모드: 상태 표시만 "${displayName}"로 변경됨 (트레이 기능 없음)`); + } +} + +document.addEventListener('DOMContentLoaded', async () => { + console.log('DOM이 로드되었습니다.'); + + // Tauri 초기화 + await initTauri(); + + // 버튼 이벤트 리스너 설정 + const btnLoggedOut = document.getElementById('btn-logged-out'); + const btnLoggedIn = document.getElementById('btn-logged-in'); + const btnSyncing = document.getElementById('btn-syncing'); + const btnError = document.getElementById('btn-error'); + + if (btnLoggedOut) { + btnLoggedOut.addEventListener('click', () => { + updateTrayStatus('logged_out', '로그아웃'); + }); + } + + if (btnLoggedIn) { + btnLoggedIn.addEventListener('click', () => { + updateTrayStatus('logged_in', '로그인'); + }); + } + + if (btnSyncing) { + btnSyncing.addEventListener('click', () => { + updateTrayStatus('syncing', '동기화 중'); + }); + } + + if (btnError) { + btnError.addEventListener('click', () => { + updateTrayStatus('error', '오류'); + }); + } + + console.log('트레이 테스트 UI가 준비되었습니다.'); +}); +