@@ -144,85 +144,66 @@ pub fn ensure_ui_build(app_dir: &Path) -> Result<(), String> {
144144 return Ok ( ( ) ) ;
145145 }
146146
147- if !ui_dir. join ( "package.json" ) . exists ( ) {
148- return Err ( "dartlab UI 소스를 찾을 수 없습니다 (package.json 없음)" . into ( ) ) ;
147+ logger:: log ( "UI 빌드 파일 없음 — GitHub에서 다운로드" ) ;
148+
149+ let zip_url = "https://github.com/eddmpython/dartlab/archive/refs/heads/master.zip" ;
150+ let zip_path = app_dir. join ( "dartlab-ui.zip" ) ;
151+ download_file ( zip_url, & zip_path) ?;
152+
153+ let extract_dir = app_dir. join ( "_dartlab_ui_tmp" ) ;
154+ if extract_dir. exists ( ) {
155+ std:: fs:: remove_dir_all ( & extract_dir) . ok ( ) ;
149156 }
157+ std:: fs:: create_dir_all ( & extract_dir) . map_err ( |e| e. to_string ( ) ) ?;
158+ extract_zip ( & zip_path, & extract_dir) ?;
159+ std:: fs:: remove_file ( & zip_path) . ok ( ) ;
150160
151- let npm = find_npm ( ) ?;
161+ let src_build = find_build_dir ( & extract_dir)
162+ . ok_or ( "다운로드한 레포에서 ui/build 디렉토리를 찾을 수 없습니다" ) ?;
152163
153- logger:: log ( "dartlab UI npm install 실행 중..." ) ;
154- let output = Command :: new ( & npm)
155- . args ( [ "install" ] )
156- . current_dir ( & ui_dir)
157- . creation_flags ( CREATE_NO_WINDOW )
158- . output ( )
159- . map_err ( |e| format ! ( "npm install 실행 실패: {e}" ) ) ?;
164+ std:: fs:: create_dir_all ( & ui_dir) . map_err ( |e| e. to_string ( ) ) ?;
160165
161- if !output. status . success ( ) {
162- let stderr = String :: from_utf8_lossy ( & output. stderr ) ;
163- logger:: log ( & format ! ( "npm install stderr: {stderr}" ) ) ;
164- return Err ( format ! ( "npm install 실패: {stderr}" ) ) ;
166+ if build_dir. exists ( ) {
167+ std:: fs:: remove_dir_all ( & build_dir) . ok ( ) ;
165168 }
169+ copy_dir_all ( & src_build, & build_dir) ?;
166170
167- logger:: log ( "dartlab UI 빌드 중..." ) ;
168- let output = Command :: new ( & npm)
169- . args ( [ "run" , "build" ] )
170- . current_dir ( & ui_dir)
171- . creation_flags ( CREATE_NO_WINDOW )
172- . output ( )
173- . map_err ( |e| format ! ( "npm run build 실행 실패: {e}" ) ) ?;
174-
175- if !output. status . success ( ) {
176- let stderr = String :: from_utf8_lossy ( & output. stderr ) ;
177- logger:: log ( & format ! ( "npm run build stderr: {stderr}" ) ) ;
178- return Err ( format ! ( "UI 빌드 실패: {stderr}" ) ) ;
179- }
171+ std:: fs:: remove_dir_all ( & extract_dir) . ok ( ) ;
180172
181- if !build_dir. exists ( ) || !build_dir . join ( "index.html" ) . exists ( ) {
182- return Err ( "UI 빌드 완료되었으나 build/ index.html이 생성되지 않았습니다 " . into ( ) ) ;
173+ if !build_dir. join ( "index.html" ) . exists ( ) {
174+ return Err ( "UI 빌드 파일 복사 완료되었으나 index.html이 없습니다 " . into ( ) ) ;
183175 }
184176
185- logger:: log ( "dartlab UI 빌드 완료" ) ;
177+ logger:: log ( "UI 빌드 파일 설치 완료" ) ;
186178 Ok ( ( ) )
187179}
188180
189- fn find_npm ( ) -> Result < String , String > {
190- if Command :: new ( "npm" )
191- . arg ( "--version" )
192- . creation_flags ( CREATE_NO_WINDOW )
193- . output ( )
194- . map ( |o| o. status . success ( ) )
195- . unwrap_or ( false )
196- {
197- return Ok ( "npm" . to_string ( ) ) ;
198- }
199-
200- let candidates = [
201- r"C:\Program Files\nodejs\npm.cmd" ,
202- r"C:\Program Files (x86)\nodejs\npm.cmd" ,
203- ] ;
204-
205- for c in & candidates {
206- if std:: path:: Path :: new ( c) . exists ( ) {
207- return Ok ( c. to_string ( ) ) ;
181+ fn find_build_dir ( extract_dir : & Path ) -> Option < std:: path:: PathBuf > {
182+ if let Ok ( entries) = std:: fs:: read_dir ( extract_dir) {
183+ for entry in entries. flatten ( ) {
184+ let candidate = entry. path ( )
185+ . join ( "src" ) . join ( "dartlab" ) . join ( "ui" ) . join ( "build" ) ;
186+ if candidate. join ( "index.html" ) . exists ( ) {
187+ return Some ( candidate) ;
188+ }
208189 }
209190 }
191+ None
192+ }
210193
211- if let Ok ( appdata) = std:: env:: var ( "APPDATA" ) {
212- let nvm_dir = std:: path:: Path :: new ( & appdata) . join ( "nvm" ) ;
213- if nvm_dir. exists ( ) {
214- if let Ok ( entries) = std:: fs:: read_dir ( & nvm_dir) {
215- for entry in entries. flatten ( ) {
216- let npm_cmd = entry. path ( ) . join ( "npm.cmd" ) ;
217- if npm_cmd. exists ( ) {
218- return Ok ( npm_cmd. to_string_lossy ( ) . to_string ( ) ) ;
219- }
220- }
221- }
194+ fn copy_dir_all ( src : & Path , dst : & Path ) -> Result < ( ) , String > {
195+ std:: fs:: create_dir_all ( dst) . map_err ( |e| e. to_string ( ) ) ?;
196+ for entry in std:: fs:: read_dir ( src) . map_err ( |e| e. to_string ( ) ) ? {
197+ let entry = entry. map_err ( |e| e. to_string ( ) ) ?;
198+ let ty = entry. file_type ( ) . map_err ( |e| e. to_string ( ) ) ?;
199+ let dst_path = dst. join ( entry. file_name ( ) ) ;
200+ if ty. is_dir ( ) {
201+ copy_dir_all ( & entry. path ( ) , & dst_path) ?;
202+ } else {
203+ std:: fs:: copy ( entry. path ( ) , & dst_path) . map_err ( |e| e. to_string ( ) ) ?;
222204 }
223205 }
224-
225- Err ( "Node.js가 설치되어 있지 않습니다. https://nodejs.org 에서 설치 후 다시 시도해 주세요." . into ( ) )
206+ Ok ( ( ) )
226207}
227208
228209fn cleanup_legacy ( app_dir : & Path ) {
0 commit comments