@@ -145,114 +145,48 @@ pub fn ensure_dartlab(app_dir: &Path) -> Result<(), String> {
145145 Ok ( ( ) )
146146}
147147
148+ /// UI 빌드 파일 확인.
149+ ///
150+ /// dartlab PyPI wheel에 `dartlab/ui/build/`가 포함되어 있으므로
151+ /// `pip install dartlab`만으로 UI가 설치된다.
152+ /// 이 함수는 설치 후 파일 존재 여부만 확인한다.
148153pub fn ensure_ui_build ( app_dir : & Path ) -> Result < ( ) , String > {
149- let ui_dir = paths:: dartlab_ui_dir ( app_dir) ;
150- let build_dir = ui_dir. join ( "build" ) ;
151- let version_file = build_dir. join ( ".dartlab_ui_version" ) ;
152-
153- let current_ver = get_dartlab_version ( app_dir) ;
154-
155- if build_dir. exists ( ) && build_dir. join ( "index.html" ) . exists ( ) {
156- if let Some ( ref ver) = current_ver {
157- if let Ok ( saved) = std:: fs:: read_to_string ( & version_file) {
158- if saved. trim ( ) == ver. trim ( ) {
159- return Ok ( ( ) ) ;
160- }
161- logger:: log ( & format ! (
162- "dartlab 버전 변경 감지 ({} → {ver}) — UI 재다운로드" ,
163- saved. trim( )
164- ) ) ;
165- } else {
166- return Ok ( ( ) ) ;
167- }
168- } else {
169- return Ok ( ( ) ) ;
170- }
171- }
172-
173- logger:: log ( "UI 빌드 파일 다운로드 중..." ) ;
154+ let build_dir = paths:: dartlab_ui_dir ( app_dir) . join ( "build" ) ;
174155
175- let zip_url = "https://github.com/eddmpython/dartlab/archive/refs/heads/master.zip" ;
176- let zip_path = app_dir. join ( "dartlab-ui.zip" ) ;
177- download_file ( zip_url, & zip_path) ?;
178-
179- let extract_dir = app_dir. join ( "_dartlab_ui_tmp" ) ;
180- if extract_dir. exists ( ) {
181- std:: fs:: remove_dir_all ( & extract_dir) . ok ( ) ;
156+ if build_dir. join ( "index.html" ) . exists ( ) {
157+ logger:: log ( "UI 빌드 파일 확인 완료" ) ;
158+ return Ok ( ( ) ) ;
182159 }
183- std:: fs:: create_dir_all ( & extract_dir) . map_err ( |e| e. to_string ( ) ) ?;
184- extract_zip ( & zip_path, & extract_dir) ?;
185- std:: fs:: remove_file ( & zip_path) . ok ( ) ;
186160
187- let src_build = find_build_dir ( & extract_dir)
188- . ok_or ( "다운로드한 레포에서 ui/build 디렉토리를 찾을 수 없습니다" ) ?;
189-
190- std:: fs:: create_dir_all ( & ui_dir) . map_err ( |e| e. to_string ( ) ) ?;
191-
192- if build_dir. exists ( ) {
193- std:: fs:: remove_dir_all ( & build_dir) . ok ( ) ;
194- }
195- copy_dir_all ( & src_build, & build_dir) ?;
196-
197- std:: fs:: remove_dir_all ( & extract_dir) . ok ( ) ;
198-
199- if !build_dir. join ( "index.html" ) . exists ( ) {
200- return Err ( "UI 빌드 파일 복사 완료되었으나 index.html이 없습니다" . into ( ) ) ;
201- }
202-
203- if let Some ( ref ver) = current_ver {
204- std:: fs:: write ( & version_file, ver) . ok ( ) ;
205- }
206-
207- logger:: log ( "UI 빌드 파일 설치 완료" ) ;
208- Ok ( ( ) )
209- }
210-
211- fn get_dartlab_version ( app_dir : & Path ) -> Option < String > {
161+ // wheel에 포함되어야 하는데 없으면 dartlab 재설치 시도
162+ logger:: log ( "UI 빌드 파일 누락 — dartlab 재설치 시도" ) ;
163+ let uv = paths:: uv_bin ( app_dir) ;
212164 let python = paths:: python_bin ( app_dir) ;
213- let output = Command :: new ( & python)
214- . args ( [ "-c" , "import dartlab; print(dartlab.__version__)" ] )
165+ let output = Command :: new ( & uv)
166+ . args ( [
167+ "pip" ,
168+ "install" ,
169+ "--force-reinstall" ,
170+ "dartlab[ai,llm]" ,
171+ "--python" ,
172+ python. to_str ( ) . unwrap ( ) ,
173+ ] )
215174 . current_dir ( app_dir)
216175 . creation_flags ( CREATE_NO_WINDOW )
217176 . output ( )
218- . ok ( ) ?;
219- if !output. status . success ( ) {
220- return None ;
221- }
222- let ver = String :: from_utf8_lossy ( & output. stdout ) . trim ( ) . to_string ( ) ;
223- if ver. is_empty ( ) { None } else { Some ( ver) }
224- }
177+ . map_err ( |e| e. to_string ( ) ) ?;
225178
226- fn find_build_dir ( extract_dir : & Path ) -> Option < std:: path:: PathBuf > {
227- if let Ok ( entries) = std:: fs:: read_dir ( extract_dir) {
228- for entry in entries. flatten ( ) {
229- let candidate = entry
230- . path ( )
231- . join ( "src" )
232- . join ( "dartlab" )
233- . join ( "ui" )
234- . join ( "build" ) ;
235- if candidate. join ( "index.html" ) . exists ( ) {
236- return Some ( candidate) ;
237- }
238- }
179+ if !output. status . success ( ) {
180+ let stderr = String :: from_utf8_lossy ( & output. stderr ) ;
181+ return Err ( format ! ( "dartlab 재설치 실패: {stderr}" ) ) ;
239182 }
240- None
241- }
242183
243- fn copy_dir_all ( src : & Path , dst : & Path ) -> Result < ( ) , String > {
244- std:: fs:: create_dir_all ( dst) . map_err ( |e| e. to_string ( ) ) ?;
245- for entry in std:: fs:: read_dir ( src) . map_err ( |e| e. to_string ( ) ) ? {
246- let entry = entry. map_err ( |e| e. to_string ( ) ) ?;
247- let ty = entry. file_type ( ) . map_err ( |e| e. to_string ( ) ) ?;
248- let dst_path = dst. join ( entry. file_name ( ) ) ;
249- if ty. is_dir ( ) {
250- copy_dir_all ( & entry. path ( ) , & dst_path) ?;
251- } else {
252- std:: fs:: copy ( entry. path ( ) , & dst_path) . map_err ( |e| e. to_string ( ) ) ?;
253- }
184+ if build_dir. join ( "index.html" ) . exists ( ) {
185+ logger:: log ( "UI 빌드 파일 재설치 후 확인 완료" ) ;
186+ Ok ( ( ) )
187+ } else {
188+ Err ( "dartlab 재설치 후에도 UI 빌드 파일(index.html)이 없습니다" . into ( ) )
254189 }
255- Ok ( ( ) )
256190}
257191
258192fn cleanup_legacy ( app_dir : & Path ) {
0 commit comments