diff --git a/apps/executeJS/src-tauri/src/bootstrap.js b/apps/executeJS/src-tauri/src/bootstrap.js index a8d34cf..4299994 100644 --- a/apps/executeJS/src-tauri/src/bootstrap.js +++ b/apps/executeJS/src-tauri/src/bootstrap.js @@ -5,44 +5,52 @@ const { ops } = core; // console 객체 정의 globalThis.console = { log: (...args) => { - const message = args.map(arg => { - if (typeof arg === 'object') { - return JSON.stringify(arg, null, 2); - } - return String(arg); - }).join(' '); + const message = args + .map((arg) => { + if (typeof arg === 'object') { + return JSON.stringify(arg, null, 2); + } + return String(arg); + }) + .join(' '); ops.op_console_log(message); }, - + error: (...args) => { - const message = args.map(arg => { - if (typeof arg === 'object') { - return JSON.stringify(arg, null, 2); - } - return String(arg); - }).join(' '); + const message = args + .map((arg) => { + if (typeof arg === 'object') { + return JSON.stringify(arg, null, 2); + } + return String(arg); + }) + .join(' '); ops.op_custom_print(message, true); }, - + warn: (...args) => { - const message = args.map(arg => { - if (typeof arg === 'object') { - return JSON.stringify(arg, null, 2); - } - return String(arg); - }).join(' '); + const message = args + .map((arg) => { + if (typeof arg === 'object') { + return JSON.stringify(arg, null, 2); + } + return String(arg); + }) + .join(' '); ops.op_custom_print(`[WARN] ${message}`, false); }, - + info: (...args) => { - const message = args.map(arg => { - if (typeof arg === 'object') { - return JSON.stringify(arg, null, 2); - } - return String(arg); - }).join(' '); + const message = args + .map((arg) => { + if (typeof arg === 'object') { + return JSON.stringify(arg, null, 2); + } + return String(arg); + }) + .join(' '); ops.op_custom_print(`[INFO] ${message}`, false); - } + }, }; // alert 함수 정의 @@ -59,7 +67,7 @@ globalThis.print = (message, isErr = false) => { globalThis.require = (moduleName) => { // 간단한 npm 모듈 시뮬레이션 const modules = { - 'lodash': { + lodash: { map: (array, iteratee) => { if (!Array.isArray(array)) { throw new Error('First argument must be an array'); @@ -93,33 +101,38 @@ globalThis.require = (moduleName) => { chunks.push(array.slice(i, i + size)); } return chunks; - } + }, }, - 'moment': { + moment: { now: () => new Date(), format: (date, format) => { if (!(date instanceof Date)) { date = new Date(date); } return date.toISOString(); - } + }, }, - 'uuid': { + uuid: { v4: () => { - return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { - const r = Math.random() * 16 | 0; - const v = c == 'x' ? r : (r & 0x3 | 0x8); - return v.toString(16); - }); - } - } + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace( + /[xy]/g, + function (c) { + const r = (Math.random() * 16) | 0; + const v = c == 'x' ? r : (r & 0x3) | 0x8; + return v.toString(16); + } + ); + }, + }, }; if (modules[moduleName]) { return modules[moduleName]; } - throw new Error(`Cannot find module '${moduleName}'. Available modules: ${Object.keys(modules).join(', ')}`); + throw new Error( + `Cannot find module '${moduleName}'. Available modules: ${Object.keys(modules).join(', ')}` + ); }; // 기본적인 전역 객체들 정의 diff --git a/apps/executeJS/src-tauri/tauri.conf.json b/apps/executeJS/src-tauri/tauri.conf.json index 5914665..9ec2e05 100644 --- a/apps/executeJS/src-tauri/tauri.conf.json +++ b/apps/executeJS/src-tauri/tauri.conf.json @@ -27,12 +27,7 @@ }, "bundle": { "active": true, - "targets": [ - "msi", - "nsis", - "dmg", - "app" - ], + "targets": ["msi", "nsis", "dmg", "app"], "icon": [], "macOS": { "entitlements": null, diff --git a/apps/executeJS/src/index.css b/apps/executeJS/src/index.css index 4ce0249..1995143 100644 --- a/apps/executeJS/src/index.css +++ b/apps/executeJS/src/index.css @@ -15,6 +15,38 @@ * { box-sizing: border-box; } + + body { + margin: 0; + padding: 0; + background-color: #0f172a; /* slate-950 */ + color: #f8fafc; /* slate-50 */ + } + + /* 스크롤바 스타일링 */ + ::-webkit-scrollbar { + width: 8px; + height: 8px; + } + + ::-webkit-scrollbar-track { + background: #1e293b; /* slate-800 */ + } + + ::-webkit-scrollbar-thumb { + background: #475569; /* slate-600 */ + border-radius: 4px; + } + + ::-webkit-scrollbar-thumb:hover { + background: #64748b; /* slate-500 */ + } + + /* 선택 영역 스타일링 */ + ::selection { + background-color: #3b82f6; /* blue-500 */ + color: white; + } } @layer components { @@ -23,13 +55,13 @@ } .panel-resize-handle { - width: 0.25rem; - background-color: hsl(0 0% 88.7%); + width: 4px; + background-color: #334155; /* slate-700 */ transition: background-color 150ms; cursor: col-resize; } .panel-resize-handle:hover { - background-color: hsl(0 0% 78%); + background-color: #475569; /* slate-600 */ } } diff --git a/apps/executeJS/src/pages/editor/editor-page.test.tsx b/apps/executeJS/src/pages/editor/editor-page.test.tsx index 761ba10..d5692e9 100644 --- a/apps/executeJS/src/pages/editor/editor-page.test.tsx +++ b/apps/executeJS/src/pages/editor/editor-page.test.tsx @@ -16,8 +16,6 @@ vi.mock('@legendapp/state/react', () => ({ describe('EditorPage', () => { it('renders without crashing', () => { render(); - expect( - screen.getByText(/Cmd\+Enter를 눌러 코드를 실행하세요/) - ).toBeInTheDocument(); + expect(screen.getByText(/실행 \(Cmd\+Enter\)/)).toBeInTheDocument(); }); }); diff --git a/apps/executeJS/src/pages/editor/editor-page.tsx b/apps/executeJS/src/pages/editor/editor-page.tsx index a90cbf1..1f9bab6 100644 --- a/apps/executeJS/src/pages/editor/editor-page.tsx +++ b/apps/executeJS/src/pages/editor/editor-page.tsx @@ -3,9 +3,10 @@ import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels'; import { CodeEditor } from '@/widgets/code-editor'; import { OutputPanel } from '@/widgets/output-panel'; import { useExecutionStore } from '@/features/execute-code'; +import { PlayIcon, StopIcon } from '@radix-ui/react-icons'; export const EditorPage: React.FC = () => { - const [code, setCode] = useState(''); + const [code, setCode] = useState('console.log("Hello, ExecuteJS!");'); const { result: executionResult, isExecuting, @@ -14,8 +15,9 @@ export const EditorPage: React.FC = () => { // 코드 실행 핸들러 const handleExecuteCode = (codeToExecute?: string) => { - if (codeToExecute) { - executeCode(codeToExecute); + const codeToRun = codeToExecute || code; + if (codeToRun.trim()) { + executeCode(codeToRun); } }; @@ -25,29 +27,75 @@ export const EditorPage: React.FC = () => { }; return ( -
+
+ {/* 헤더 */} +
+
+
ExecuteJS
+
+ +
+ +
+
+ {/* 메인 컨텐츠 영역 */} -
- +
+ {/* 왼쪽 패널 - 코드 에디터 */} -
- +
+
+ + Editor + +
+
+ +
{/* 리사이즈 핸들 */} - + {/* 오른쪽 패널 - 출력 결과 */} - +
+
+ + Output + +
+
+ +
+
diff --git a/apps/executeJS/src/widgets/code-editor/code-editor.tsx b/apps/executeJS/src/widgets/code-editor/code-editor.tsx index b958758..7845edc 100644 --- a/apps/executeJS/src/widgets/code-editor/code-editor.tsx +++ b/apps/executeJS/src/widgets/code-editor/code-editor.tsx @@ -1,5 +1,5 @@ -import React, { useRef, useEffect } from 'react'; -import { Editor } from '@monaco-editor/react'; +import React, { useRef } from 'react'; +import { Editor, EditorProps } from '@monaco-editor/react'; import type { CodeEditorProps } from '../../shared/types'; export const CodeEditor: React.FC = ({ @@ -11,33 +11,17 @@ export const CodeEditor: React.FC = ({ }) => { const editorRef = useRef(null); - // Cmd+Enter 키바인딩 설정 - useEffect(() => { - const handleKeyDown = (event: KeyboardEvent) => { - if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') { - event.preventDefault(); - onExecute(); - } - }; - - document.addEventListener('keydown', handleKeyDown); - return () => document.removeEventListener('keydown', handleKeyDown); - }, [onExecute]); - // Monaco Editor 설정 - const handleEditorDidMount = (editor: any, monaco: any) => { + const handleEditorDidMount: EditorProps['onMount'] = (editor, monaco) => { try { editorRef.current = editor; // Cmd+Enter 키바인딩 추가 if (monaco && monaco.KeyMod && monaco.KeyCode) { - editor.addCommand( - monaco.KeyMod.CmdOrCtrl | monaco.KeyCode.Enter, - () => { - const currentValue = editor.getValue(); - onExecute?.(currentValue); - } - ); + editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter, () => { + const currentValue = editor.getValue(); + onExecute?.(currentValue); + }); } // 에디터 포커스 @@ -48,7 +32,7 @@ export const CodeEditor: React.FC = ({ }; // 에디터 옵션 - const editorOptions = { + const editorOptions: EditorProps['options'] = { selectOnLineNumbers: true, roundedSelection: false, readOnly: false, @@ -57,8 +41,8 @@ export const CodeEditor: React.FC = ({ minimap: { enabled: false }, scrollBeyondLastLine: false, fontSize: 14, - lineHeight: 20, - fontFamily: 'JetBrains Mono, Fira Code, monospace', + lineHeight: 22, + fontFamily: 'JetBrains Mono, Fira Code, Consolas, Monaco, monospace', wordWrap: 'on' as const, wrappingIndent: 'indent' as const, tabSize: 2, @@ -70,6 +54,23 @@ export const CodeEditor: React.FC = ({ bracketPairs: true, indentation: true, }, + padding: { top: 16, bottom: 16 }, + scrollbar: { + vertical: 'auto' as const, + horizontal: 'auto' as const, + useShadows: false, + verticalHasArrows: false, + horizontalHasArrows: false, + verticalScrollbarSize: 8, + horizontalScrollbarSize: 8, + }, + contextmenu: true, + mouseWheelZoom: true, + cursorBlinking: 'blink' as const, + // Enter 키로 실행되지 않도록 설정 + quickSuggestions: false, + suggestOnTriggerCharacters: false, + acceptSuggestionOnEnter: 'off' as const, }; return ( diff --git a/apps/executeJS/src/widgets/output-panel/output-panel.tsx b/apps/executeJS/src/widgets/output-panel/output-panel.tsx index f0a474a..43373f6 100644 --- a/apps/executeJS/src/widgets/output-panel/output-panel.tsx +++ b/apps/executeJS/src/widgets/output-panel/output-panel.tsx @@ -1,76 +1,41 @@ import React from 'react'; -import { Box, Text, Flex, Spinner } from '@radix-ui/themes'; -import { CheckCircledIcon, CrossCircledIcon } from '@radix-ui/react-icons'; import type { OutputPanelProps } from '../../shared/types'; export const OutputPanel: React.FC = ({ result, isExecuting, }) => { - console.log('OutputPanel Debug:', { - result, - isExecuting, - }); - if (isExecuting) { return ( - - - - 코드를 실행 중입니다... - - +
+
+
+
+ 코드를 실행 중입니다... +
+
+
); } if (!result) { - return ( - - - - Cmd+Enter를 눌러 코드를 실행하세요 - - - - ); + return
; } return ( - - {/* 실행 결과 헤더 */} - - {result.success ? ( - - ) : ( - - )} - - {result.success ? '실행 성공' : '실행 실패'} - - - {new Date(result.timestamp).toLocaleTimeString()} - - - - {/* 코드 */} - - - 실행한 코드: - -
-          {result.code}
-        
-
- - {/* 결과 */} - - - 실행 결과: - -
-          {result.success ? result.result : result.error}
-        
-
-
+
+ {/* 실행 결과만 표시 - 화면 전체 사용 */} +
+
+
+            
+              {result.success ? result.result : result.error}
+            
+          
+
+
+
); };