Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/executeJS/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"react-dom": "latest",
"react-hook-form": "^7.66.0",
"react-resizable-panels": "^3.0.6",
"react-router-dom": "^7.9.5",
"zustand": "^5.0.8"
},
"devDependencies": {
Expand Down
9 changes: 9 additions & 0 deletions apps/executeJS/src-tauri/capabilities/main.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "main",
"windows": ["main", "settings"],
"permissions": [
"core:window:allow-set-title"
]
}

120 changes: 119 additions & 1 deletion apps/executeJS/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ mod commands;
mod js_executor;

use commands::*;
use tauri::Manager;
use tauri::menu::{MenuBuilder, SubmenuBuilder};
use tauri::{Manager, WebviewUrl, WebviewWindowBuilder};

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
Expand All @@ -28,6 +29,123 @@ pub fn run() {

builder
.setup(|app_handle| {
// Window Menu
let about_menu = SubmenuBuilder::new(app_handle, "About")
.text("about", "About ExecuteJS")
.separator()
.text("settings", "Settings...")
.separator()
.text("quit", "Quit ExecuteJS")
.build()?;

let file_menu = SubmenuBuilder::new(app_handle, "File")
.text("new_tab", "New Tab")
.separator()
.text("close_tab", "Close Tab")
.build()?;

let edit_menu = SubmenuBuilder::new(app_handle, "Edit")
.undo()
.redo()
.separator()
.cut()
.copy()
.paste()
.separator()
.select_all()
.build()?;

let view_menu = SubmenuBuilder::new(app_handle, "View")
.text("reload", "Reload")
.text("toggle_devtools", "Toggle Developer Tools")
.build()?;

// Main Menu
let menu = MenuBuilder::new(app_handle)
.items(&[&about_menu, &file_menu, &edit_menu, &view_menu])
.build()?;

app_handle.set_menu(menu)?;

// Menu Event
app_handle.on_menu_event(move |app_handle, event| {
match event.id().0.as_str() {
// About Menu
"about" => {
// TODO: About ExecuteJS 다이얼로그 표시
eprintln!("About ExecuteJS 메뉴 클릭됨");
}
"settings" => {
// Settings 창이 이미 열려있는지 확인
if let Some(existing_window) = app_handle.get_webview_window("settings")
{
existing_window.set_focus().unwrap_or_default();
} else {
// 새 Settings 창 생성
// 개발 모드에서는 devUrl 사용, 프로덕션에서는 App 경로 사용
let url = if cfg!(debug_assertions) {
WebviewUrl::External(
"http://localhost:1420/settings"
.parse()
.expect("failed to parse dev settings URL"),
)
} else {
// TODO: 해시 라우팅을 사용하여 Settings 페이지로 이동 처리. 다른 방법으로 수정 필요
WebviewUrl::App("index.html#/settings".into())
};

match WebviewWindowBuilder::new(app_handle, "settings", url)
.title("General")
.inner_size(800.0, 600.0)
.min_inner_size(600.0, 400.0)
.resizable(true)
.build()
{
Ok(settings_window) => {
// Settings 창에서 개발자 도구 자동 열기
#[cfg(debug_assertions)]
{
settings_window.open_devtools();
}
}
Err(e) => {
eprintln!("Settings 창 생성 실패: {:?}", e);
}
}
}
}
"quit" => {
app_handle.exit(0);
}

// File Menu
"new_tab" => {
// TODO: 새 탭 추가
eprintln!("New Tab 메뉴 클릭됨");
}
"close_tab" => {
// TODO: 현재 탭 닫기
eprintln!("Close Tab 메뉴 클릭됨");
}

// View Menu
"reload" => {
if let Some(window) = app_handle.get_webview_window("main") {
window.eval("window.location.reload()").unwrap();
}
}
"toggle_devtools" => {
if let Some(window) = app_handle.get_webview_window("main") {
window.open_devtools();
window.close_devtools();
}
}
_ => {
eprintln!("메뉴 이벤트: {:?}", event.id());
}
}
});

// JavaScript 실행기 상태 관리
#[cfg(debug_assertions)]
{
Expand Down
3 changes: 2 additions & 1 deletion apps/executeJS/src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
}
],
"security": {
"csp": null
"csp": null,
"capabilities": ["main"]
}
},
"bundle": {
Expand Down
5 changes: 3 additions & 2 deletions apps/executeJS/src/app/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import React from 'react';

import { Providers } from './providers';
import { PlaygroundPage } from '../pages/playground';
import { Router } from './router';

export const App: React.FC = () => {
return (
<Providers>
<PlaygroundPage />
<Router />
</Providers>
);
};
15 changes: 15 additions & 0 deletions apps/executeJS/src/app/router.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { BrowserRouter, Routes, Route } from 'react-router-dom';

import { PlaygroundPage } from '@/pages/playground';
import { SettingsPage } from '@/pages/settings';

export const Router = () => {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<PlaygroundPage />} />
<Route path="/settings" element={<SettingsPage />} />
</Routes>
</BrowserRouter>
);
};
1 change: 1 addition & 0 deletions apps/executeJS/src/pages/settings/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { SettingsPage } from './settings-page';
121 changes: 121 additions & 0 deletions apps/executeJS/src/pages/settings/settings-page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import React, { useRef, useState } from 'react';
import { getCurrentWindow, Window } from '@tauri-apps/api/window';

type SettingsTab =
| 'general'
| 'build'
| 'formatting'
| 'appearance'
| 'ai'
| 'advanced';

const TAB_TITLES: Record<SettingsTab, string> = {
general: 'General',
build: 'Build',
formatting: 'Formatting',
appearance: 'Appearance',
ai: 'AI',
advanced: 'Advanced',
};

const TABS: { id: SettingsTab; label: string }[] = [
{ id: 'general', label: 'General' },
{ id: 'build', label: 'Build' },
{ id: 'formatting', label: 'Formatting' },
{ id: 'appearance', label: 'Appearance' },
{ id: 'ai', label: 'AI' },
{ id: 'advanced', label: 'Advanced' },
];

export const SettingsPage: React.FC = () => {
const windowRef = useRef<Window>(getCurrentWindow());

const [activeTab, setActiveTab] = useState<SettingsTab>('general');

const updateWindowTitle = async (tabId: SettingsTab) => {
try {
await windowRef.current.setTitle(TAB_TITLES[tabId]);
} catch (error) {
console.error('창 제목 변경 실패:', error);
}
};

const handleTabClick = async (tabId: SettingsTab) => {
setActiveTab(tabId);

await updateWindowTitle(tabId);
};

return (
<div className="flex h-screen bg-gray-900 text-white">
{/* 사이드바 */}
<div className="w-48 border-r border-gray-700 bg-gray-800">
<div className="p-4 border-b border-gray-700">
<h1 className="text-xl font-bold">Settings</h1>
</div>
<nav className="p-2">
{TABS.map((tab) => (
<button
key={tab.id}
onClick={() => handleTabClick(tab.id)}
className={`w-full text-left px-4 py-2 rounded-md mb-1 transition-colors ${
activeTab === tab.id
? 'bg-blue-600 text-white'
: 'text-gray-300 hover:bg-gray-700'
}`}
>
{tab.label}
</button>
))}
</nav>
</div>

{/* 컨텐츠 영역 */}
<div className="flex-1 overflow-y-auto p-8">
{activeTab === 'general' && (
<div>
<h2 className="text-2xl font-bold mb-6">General</h2>
<p className="text-gray-400">일반 설정 옵션이 여기에 추가됩니다.</p>
</div>
)}

{activeTab === 'build' && (
<div>
<h2 className="text-2xl font-bold mb-6">Build</h2>
<p className="text-gray-400">빌드 설정 옵션이 여기에 추가됩니다.</p>
</div>
)}

{activeTab === 'formatting' && (
<div>
<h2 className="text-2xl font-bold mb-6">Formatting</h2>
<p className="text-gray-400">
포맷팅 설정 옵션이 여기에 추가됩니다.
</p>
</div>
)}

{activeTab === 'appearance' && (
<div>
<h2 className="text-2xl font-bold mb-6">Appearance</h2>
<p className="text-gray-400">외관 설정 옵션이 여기에 추가됩니다.</p>
</div>
)}

{activeTab === 'ai' && (
<div>
<h2 className="text-2xl font-bold mb-6">AI</h2>
<p className="text-gray-400">AI 설정 옵션이 여기에 추가됩니다.</p>
</div>
)}

{activeTab === 'advanced' && (
<div>
<h2 className="text-2xl font-bold mb-6">Advanced</h2>
<p className="text-gray-400">고급 설정 옵션이 여기에 추가됩니다.</p>
</div>
)}
</div>
</div>
);
};
7 changes: 7 additions & 0 deletions apps/executeJS/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,11 @@ export default defineConfig(async () => ({
ignored: ['**/src-tauri/**'],
},
},
build: {
rollupOptions: {
input: {
main: './index.html',
},
},
},
}));
Loading