@@ -24,6 +24,53 @@ set(STAMP_FILE "${UI_BINARY_DIR}/.ui-stamp")
2424set (UI_CPP "${UI_BINARY_DIR } /ui.cpp" )
2525set (UI_H "${UI_BINARY_DIR } /ui.h" )
2626
27+ # A dist directory is only usable if it holds the COMPLETE asset set that
28+ # llama-ui-embed requires. A partial dist -- e.g. an npm PWA-assets build that
29+ # died in the native `sharp` step (common on Termux / very new Node), leaving
30+ # only index.html -- must not be trusted, or llama-ui-embed aborts the whole
31+ # build with "missing required asset(s): loading.html ...". Treat an incomplete
32+ # dist as unusable so provisioning falls through to the HF download instead of
33+ # hard-failing. Mirrors the required-asset list in tools/ui/embed.cpp.
34+ function (dist_is_complete dir out_var )
35+ set (${out_var} FALSE PARENT_SCOPE )
36+ if (NOT EXISTS "${dir} /index.html" )
37+ return ()
38+ endif ()
39+ file (GLOB_RECURSE _files RELATIVE "${dir} " "${dir} /*" )
40+ set (_have_loading FALSE )
41+ set (_have_manifest FALSE )
42+ set (_have_sw FALSE )
43+ set (_have_build FALSE )
44+ set (_have_version FALSE )
45+ set (_have_bundle_js FALSE )
46+ set (_have_bundle_css FALSE )
47+ set (_have_workbox FALSE )
48+ foreach (_f ${_files} )
49+ get_filename_component (_b "${_f} " NAME )
50+ if (_b STREQUAL "loading.html" )
51+ set (_have_loading TRUE )
52+ elseif (_b STREQUAL "manifest.webmanifest" )
53+ set (_have_manifest TRUE )
54+ elseif (_b STREQUAL "sw.js" )
55+ set (_have_sw TRUE )
56+ elseif (_b STREQUAL "build.json" )
57+ set (_have_build TRUE )
58+ elseif (_b STREQUAL "version.json" )
59+ set (_have_version TRUE )
60+ elseif (_b MATCHES "^bundle.*\\ .js$" )
61+ set (_have_bundle_js TRUE )
62+ elseif (_b MATCHES "^bundle.*\\ .css$" )
63+ set (_have_bundle_css TRUE )
64+ elseif (_b MATCHES "^workbox.*\\ .js$" )
65+ set (_have_workbox TRUE )
66+ endif ()
67+ endforeach ()
68+ if (_have_loading AND _have_manifest AND _have_sw AND _have_build AND _have_version
69+ AND _have_bundle_js AND _have_bundle_css AND _have_workbox)
70+ set (${out_var} TRUE PARENT_SCOPE )
71+ endif ()
72+ endfunction ()
73+
2774function (npm_build_should_skip out_var )
2875 set (${out_var} FALSE PARENT_SCOPE )
2976
@@ -271,9 +318,15 @@ endfunction()
271318# 1. Priority 1: pre-built assets supplied in tools/ui/dist
272319# ---------------------------------------------------------------------------
273320if (EXISTS "${SRC_DIST_DIR} /index.html" )
274- message (STATUS "UI: using pre-built assets from ${SRC_DIST_DIR} " )
275- emit_files ("${SRC_DIST_DIR} " )
276- return ()
321+ dist_is_complete ("${SRC_DIST_DIR} " SRC_DIST_OK )
322+ if (SRC_DIST_OK)
323+ message (STATUS "UI: using pre-built assets from ${SRC_DIST_DIR} " )
324+ emit_files ("${SRC_DIST_DIR} " )
325+ return ()
326+ else ()
327+ message (STATUS "UI: ${SRC_DIST_DIR} has index.html but is missing required assets "
328+ "(partial or failed UI build); ignoring it and trying npm/HF instead" )
329+ endif ()
277330endif ()
278331
279332# ---------------------------------------------------------------------------
@@ -305,10 +358,7 @@ if(NOT provisioned AND HF_ENABLED)
305358 endif ()
306359 endif ()
307360
308- set (have_assets FALSE )
309- if (EXISTS "${DIST_DIR} /index.html" )
310- set (have_assets TRUE )
311- endif ()
361+ dist_is_complete ("${DIST_DIR} " have_assets )
312362 if (stamp_ok AND have_assets)
313363 message (STATUS "UI: HF stamp '${stamped} ' matches version, skipping HF fetch" )
314364 set (provisioned TRUE )
@@ -328,8 +378,18 @@ endif()
328378# 4. Fallback: warn about stale or missing assets, then emit whatever we have
329379# ---------------------------------------------------------------------------
330380if (NOT provisioned)
331- if (EXISTS "${DIST_DIR} /index.html" )
332- message (WARNING "UI: provisioning failed; embedding stale assets from ${DIST_DIR} " )
381+ dist_is_complete ("${DIST_DIR} " DIST_COMPLETE )
382+ if (DIST_COMPLETE)
383+ message (WARNING "UI: provisioning failed; embedding stale (but complete) assets from ${DIST_DIR} " )
384+ elseif (EXISTS "${DIST_DIR} /index.html" )
385+ # A partial dist would make llama-ui-embed abort the build. Drop it so we
386+ # degrade to a no-embedded-UI binary with a clear message instead.
387+ message (WARNING "UI: provisioning failed and ${DIST_DIR} is incomplete; "
388+ "building WITHOUT an embedded UI. For an offline build, extract a "
389+ "complete pre-built UI (from a llama.cpp release at "
390+ "https://github.com/ggml-org/llama.cpp/releases) into tools/ui/dist, "
391+ "or configure with -DBUILD_UI=OFF to always use the HF-downloaded UI." )
392+ file (REMOVE_RECURSE "${DIST_DIR} " )
333393 else ()
334394 message (WARNING "UI: no assets available - building without an embedded UI. "
335395 "In a disconnected environment, download the pre-built UI "
0 commit comments