From 366b3de936fc55f19e1d2f07dfb6f008e20da775 Mon Sep 17 00:00:00 2001 From: leejet Date: Mon, 19 Jan 2026 01:18:15 +0800 Subject: [PATCH 1/8] Add embedded WebUI --- .gitmodules | 3 +++ examples/server/CMakeLists.txt | 12 +++++++++++- examples/server/frontend | 1 + examples/server/main.cpp | 14 ++++++++++++-- 4 files changed, 27 insertions(+), 3 deletions(-) create mode 160000 examples/server/frontend diff --git a/.gitmodules b/.gitmodules index 5a7851973..5d66c8795 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "ggml"] path = ggml url = https://github.com/ggml-org/ggml.git +[submodule "examples/server/frontend"] + path = examples/server/frontend + url = https://github.com/leejet/stable-ui.git diff --git a/examples/server/CMakeLists.txt b/examples/server/CMakeLists.txt index d19126080..e3f334009 100644 --- a/examples/server/CMakeLists.txt +++ b/examples/server/CMakeLists.txt @@ -1,6 +1,16 @@ set(TARGET sd-server) -add_executable(${TARGET} main.cpp) +set(GENERATED_HTML_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/frontend/dist/gen_index_html.h") + +if(EXISTS ${GENERATED_HTML_HEADER}) + message(STATUS "Found generated header: ${GENERATED_HTML_HEADER}") + add_executable(${TARGET} main.cpp ${GENERATED_HTML_HEADER}) + target_compile_definitions(${TARGET} PRIVATE HAVE_INDEX_HTML) +else() + message(WARNING "Header ${GENERATED_HTML_HEADER} not found. Skipping index_html inclusion.") + add_executable(${TARGET} main.cpp) +endif() + install(TARGETS ${TARGET} RUNTIME) target_link_libraries(${TARGET} PRIVATE stable-diffusion ${CMAKE_THREAD_LIBS_INIT}) target_compile_features(${TARGET} PUBLIC c_std_11 cxx_std_17) \ No newline at end of file diff --git a/examples/server/frontend b/examples/server/frontend new file mode 160000 index 000000000..aa8225dee --- /dev/null +++ b/examples/server/frontend @@ -0,0 +1 @@ +Subproject commit aa8225dee04af93cac03ddeac9d3128bb158daac diff --git a/examples/server/main.cpp b/examples/server/main.cpp index 76199ac69..2b4965532 100644 --- a/examples/server/main.cpp +++ b/examples/server/main.cpp @@ -13,6 +13,10 @@ #include "common/common.hpp" +#ifdef HAVE_INDEX_HTML +#include "frontend/dist/gen_index_html.h" +#endif + namespace fs = std::filesystem; // ----------------------- helpers ----------------------- @@ -312,7 +316,13 @@ int main(int argc, const char** argv) { return httplib::Server::HandlerResponse::Unhandled; }); - // health + // index html + std::string index_html; +#ifdef HAVE_INDEX_HTML + index_html.assign(reinterpret_cast(index_html_bytes), index_html_size); +#else + index_html = "Stable Diffusion Server is running"; +#endif svr.Get("/", [&](const httplib::Request&, httplib::Response& res) { if (!svr_params.serve_html_path.empty()) { std::ifstream file(svr_params.serve_html_path); @@ -324,7 +334,7 @@ int main(int argc, const char** argv) { res.set_content("Error: Unable to read HTML file", "text/plain"); } } else { - res.set_content("Stable Diffusion Server is running", "text/plain"); + res.set_content(index_html, "text/html"); } }); From 3a64b208f2195b7ad438e19bb1ef0e817ee98bac Mon Sep 17 00:00:00 2001 From: leejet Date: Sun, 15 Mar 2026 18:31:47 +0800 Subject: [PATCH 2/8] update frontend --- examples/server/frontend | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/server/frontend b/examples/server/frontend index aa8225dee..884cff9f6 160000 --- a/examples/server/frontend +++ b/examples/server/frontend @@ -1 +1 @@ -Subproject commit aa8225dee04af93cac03ddeac9d3128bb158daac +Subproject commit 884cff9f673d8ac6ebeb953516c05b56c7af427c From ac32c7fa469d619618ca7d0a67ed86ef1ad19e26 Mon Sep 17 00:00:00 2001 From: leejet Date: Sun, 15 Mar 2026 20:23:17 +0800 Subject: [PATCH 3/8] add SD_SERVER_BUILD_FRONTEND option --- examples/server/CMakeLists.txt | 47 +++++++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/examples/server/CMakeLists.txt b/examples/server/CMakeLists.txt index e3f334009..a8cfffd51 100644 --- a/examples/server/CMakeLists.txt +++ b/examples/server/CMakeLists.txt @@ -1,14 +1,49 @@ set(TARGET sd-server) -set(GENERATED_HTML_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/frontend/dist/gen_index_html.h") +option(SD_SERVER_BUILD_FRONTEND "Build server frontend with pnpm" ON) -if(EXISTS ${GENERATED_HTML_HEADER}) - message(STATUS "Found generated header: ${GENERATED_HTML_HEADER}") - add_executable(${TARGET} main.cpp ${GENERATED_HTML_HEADER}) +set(FRONTEND_DIR "${CMAKE_CURRENT_SOURCE_DIR}/frontend") +set(GENERATED_HTML_HEADER "${FRONTEND_DIR}/dist/gen_index_html.h") + +set(HAVE_FRONTEND_BUILD OFF) + +if(SD_SERVER_BUILD_FRONTEND AND EXISTS "${FRONTEND_DIR}") + if(WIN32) + find_program(PNPM_EXECUTABLE NAMES pnpm.cmd pnpm) + else() + find_program(PNPM_EXECUTABLE NAMES pnpm) + endif() + + if(PNPM_EXECUTABLE) + message(STATUS "Frontend dir found: ${FRONTEND_DIR}") + message(STATUS "pnpm found: ${PNPM_EXECUTABLE}") + + set(HAVE_FRONTEND_BUILD ON) + + add_custom_target(${TARGET}_frontend + COMMAND "${PNPM_EXECUTABLE}" -C "${FRONTEND_DIR}" install + COMMAND "${PNPM_EXECUTABLE}" -C "${FRONTEND_DIR}" build + COMMAND "${PNPM_EXECUTABLE}" -C "${FRONTEND_DIR}" build:header + COMMENT "Building frontend and generating gen_index_html.h" + VERBATIM + ) + else() + message(WARNING "pnpm not found, frontend build disabled") + endif() +else() + message(STATUS "Frontend disabled or directory not found: ${FRONTEND_DIR}") +endif() + +add_executable(${TARGET} main.cpp) + +if(HAVE_FRONTEND_BUILD) + add_dependencies(${TARGET} ${TARGET}_frontend) + target_sources(${TARGET} PRIVATE "${GENERATED_HTML_HEADER}") + target_include_directories(${TARGET} PRIVATE "${FRONTEND_DIR}/dist") target_compile_definitions(${TARGET} PRIVATE HAVE_INDEX_HTML) + message(STATUS "HAVE_INDEX_HTML enabled") else() - message(WARNING "Header ${GENERATED_HTML_HEADER} not found. Skipping index_html inclusion.") - add_executable(${TARGET} main.cpp) + message(STATUS "HAVE_INDEX_HTML disabled") endif() install(TARGETS ${TARGET} RUNTIME) From c45f86c9d9db38c3061dab4b48fb199a81092375 Mon Sep 17 00:00:00 2001 From: leejet Date: Sun, 15 Mar 2026 20:57:13 +0800 Subject: [PATCH 4/8] ci: install Node.js and pnpm for frontend build --- .github/workflows/build.yml | 87 +++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9816e4244..5c5ed797a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -21,11 +21,13 @@ on: "**/*.c", "**/*.cpp", "**/*.cu", + "examples/server/frontend/**", ] pull_request: types: [opened, synchronize, reopened] paths: [ + ".github/workflows/**", "**/CMakeLists.txt", "**/Makefile", "**/*.h", @@ -33,6 +35,7 @@ on: "**/*.c", "**/*.cpp", "**/*.cu", + "examples/server/frontend/**", ] env: @@ -53,6 +56,18 @@ jobs: with: submodules: recursive + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: pnpm + cache-dependency-path: examples/server/frontend/pnpm-lock.yaml + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 9 + - name: Dependencies id: depends run: | @@ -106,6 +121,18 @@ jobs: with: submodules: recursive + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: pnpm + cache-dependency-path: examples/server/frontend/pnpm-lock.yaml + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 9 + - name: Dependencies id: depends run: | @@ -174,6 +201,18 @@ jobs: with: submodules: recursive + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: pnpm + cache-dependency-path: examples/server/frontend/pnpm-lock.yaml + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 9 + - name: Get commit hash id: commit if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }} @@ -223,6 +262,18 @@ jobs: with: submodules: recursive + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: pnpm + cache-dependency-path: examples/server/frontend/pnpm-lock.yaml + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 9 + - name: Dependencies id: depends run: | @@ -294,6 +345,18 @@ jobs: with: submodules: recursive + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: pnpm + cache-dependency-path: examples/server/frontend/pnpm-lock.yaml + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 9 + - name: Install cuda-toolkit id: cuda-toolkit if: ${{ matrix.build == 'cuda12' }} @@ -399,6 +462,18 @@ jobs: with: submodules: recursive + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: pnpm + cache-dependency-path: examples/server/frontend/pnpm-lock.yaml + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 9 + - name: Cache ROCm Installation id: cache-rocm uses: actions/cache@v4 @@ -502,6 +577,18 @@ jobs: with: submodules: recursive + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: pnpm + cache-dependency-path: examples/server/frontend/pnpm-lock.yaml + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 9 + - name: Free disk space run: | # Remove preinstalled SDKs and caches not needed for this job From 770e96447d0ed93cde23ee3180afe77952aebd18 Mon Sep 17 00:00:00 2001 From: leejet Date: Sun, 15 Mar 2026 21:03:01 +0800 Subject: [PATCH 5/8] fix ci --- .github/workflows/build.yml | 28 +++++++--------------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5c5ed797a..1fbcbf94d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -56,12 +56,10 @@ jobs: with: submodules: recursive - - name: Setup Node.js + - name: Setup Node uses: actions/setup-node@v4 with: node-version: 20 - cache: pnpm - cache-dependency-path: examples/server/frontend/pnpm-lock.yaml - name: Setup pnpm uses: pnpm/action-setup@v4 @@ -121,12 +119,10 @@ jobs: with: submodules: recursive - - name: Setup Node.js + - name: Setup Node uses: actions/setup-node@v4 with: node-version: 20 - cache: pnpm - cache-dependency-path: examples/server/frontend/pnpm-lock.yaml - name: Setup pnpm uses: pnpm/action-setup@v4 @@ -201,12 +197,10 @@ jobs: with: submodules: recursive - - name: Setup Node.js + - name: Setup Node uses: actions/setup-node@v4 with: node-version: 20 - cache: pnpm - cache-dependency-path: examples/server/frontend/pnpm-lock.yaml - name: Setup pnpm uses: pnpm/action-setup@v4 @@ -262,12 +256,10 @@ jobs: with: submodules: recursive - - name: Setup Node.js + - name: Setup Node uses: actions/setup-node@v4 with: node-version: 20 - cache: pnpm - cache-dependency-path: examples/server/frontend/pnpm-lock.yaml - name: Setup pnpm uses: pnpm/action-setup@v4 @@ -345,12 +337,10 @@ jobs: with: submodules: recursive - - name: Setup Node.js + - name: Setup Node uses: actions/setup-node@v4 with: node-version: 20 - cache: pnpm - cache-dependency-path: examples/server/frontend/pnpm-lock.yaml - name: Setup pnpm uses: pnpm/action-setup@v4 @@ -462,12 +452,10 @@ jobs: with: submodules: recursive - - name: Setup Node.js + - name: Setup Node uses: actions/setup-node@v4 with: node-version: 20 - cache: pnpm - cache-dependency-path: examples/server/frontend/pnpm-lock.yaml - name: Setup pnpm uses: pnpm/action-setup@v4 @@ -577,12 +565,10 @@ jobs: with: submodules: recursive - - name: Setup Node.js + - name: Setup Node uses: actions/setup-node@v4 with: node-version: 20 - cache: pnpm - cache-dependency-path: examples/server/frontend/pnpm-lock.yaml - name: Setup pnpm uses: pnpm/action-setup@v4 From 0584d92c1af7f0b9dc04c5f6f49381265aece422 Mon Sep 17 00:00:00 2001 From: leejet Date: Sun, 15 Mar 2026 21:26:53 +0800 Subject: [PATCH 6/8] fix frontend build --- examples/server/CMakeLists.txt | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/examples/server/CMakeLists.txt b/examples/server/CMakeLists.txt index a8cfffd51..8f5beba81 100644 --- a/examples/server/CMakeLists.txt +++ b/examples/server/CMakeLists.txt @@ -20,13 +20,35 @@ if(SD_SERVER_BUILD_FRONTEND AND EXISTS "${FRONTEND_DIR}") set(HAVE_FRONTEND_BUILD ON) - add_custom_target(${TARGET}_frontend + add_custom_target(${TARGET}_frontend_install COMMAND "${PNPM_EXECUTABLE}" -C "${FRONTEND_DIR}" install - COMMAND "${PNPM_EXECUTABLE}" -C "${FRONTEND_DIR}" build - COMMAND "${PNPM_EXECUTABLE}" -C "${FRONTEND_DIR}" build:header - COMMENT "Building frontend and generating gen_index_html.h" + WORKING_DIRECTORY "${FRONTEND_DIR}" + COMMENT "Installing frontend dependencies" + VERBATIM + ) + + add_custom_target(${TARGET}_frontend_build + COMMAND "${PNPM_EXECUTABLE}" -C "${FRONTEND_DIR}" run build + WORKING_DIRECTORY "${FRONTEND_DIR}" + COMMENT "Building frontend" + VERBATIM + ) + + add_custom_target(${TARGET}_frontend_header + COMMAND "${PNPM_EXECUTABLE}" -C "${FRONTEND_DIR}" run build:header + WORKING_DIRECTORY "${FRONTEND_DIR}" + COMMENT "Generating gen_index_html.h" VERBATIM ) + + add_dependencies(${TARGET}_frontend_build ${TARGET}_frontend_install) + add_dependencies(${TARGET}_frontend_header ${TARGET}_frontend_build) + + add_custom_target(${TARGET}_frontend + DEPENDS ${TARGET}_frontend_header + ) + + set_source_files_properties("${GENERATED_HTML_HEADER}" PROPERTIES GENERATED TRUE) else() message(WARNING "pnpm not found, frontend build disabled") endif() From 1bf25a3fe9f232daee70ebcb889e4e48f077a7c8 Mon Sep 17 00:00:00 2001 From: leejet Date: Sun, 15 Mar 2026 21:43:29 +0800 Subject: [PATCH 7/8] fix(frontend): add packages field to pnpm-workspace.yaml --- examples/server/frontend | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/server/frontend b/examples/server/frontend index 884cff9f6..1a34176cd 160000 --- a/examples/server/frontend +++ b/examples/server/frontend @@ -1 +1 @@ -Subproject commit 884cff9f673d8ac6ebeb953516c05b56c7af427c +Subproject commit 1a34176cd6d39ad3a226b2b69047e71f6797f6bc From e7738383e9ae52a9785d6bb4eda129213acc6ec8 Mon Sep 17 00:00:00 2001 From: leejet Date: Mon, 16 Mar 2026 00:20:17 +0800 Subject: [PATCH 8/8] update server readme --- examples/server/README.md | 89 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/examples/server/README.md b/examples/server/README.md index 38deff61e..8aa2158f5 100644 --- a/examples/server/README.md +++ b/examples/server/README.md @@ -1,3 +1,92 @@ +# Frontend + +## Build with Frontend + +The server can optionally build the web frontend and embed it into the binary as `gen_index_html.h`. + +### Requirements + +Install the following tools: + +* **Node.js** ≥ 22.18 + https://nodejs.org/ + +* **pnpm** ≥ 10 + Install via npm: + +```bash +npm install -g pnpm +``` + +Verify installation: + +```bash +node -v +pnpm -v +``` + +### Install frontend dependencies + +Go to the frontend directory and install dependencies: + +```bash +cd examples/server/frontend +pnpm install +``` + +### Build the server with CMake + +Enable the frontend build option when configuring CMake: + +```bash +cmake -B build -DSD_SERVER_BUILD_FRONTEND=ON +cmake --build build --config Release +``` + +If `pnpm` is available, the build system will automatically run: + +``` +pnpm run build +pnpm run build:header +``` + +and embed the generated frontend into the server binary. + +## Frontend Repository + +The web frontend is maintained in a **separate repository**, https://github.com/leejet/stable-ui. + +If you want to modify the UI or frontend logic, please submit pull requests to the **frontend repository**. + +This repository (`stable-diffusion.cpp`) only vendors the frontend periodically. Changes from the frontend repo are synchronized: + +* approximately **every 1–2 weeks**, or +* when there are **major frontend updates** + +Because of this, frontend changes will **not appear here immediately** after being merged upstream. + +## Using an external frontend + +By default, the server uses the **embedded frontend** generated during the build (`gen_index_html.h`). + +You can also serve a custom frontend file instead of the embedded one by using: + +```bash +--serve-html-path +``` + +For example: + +```bash +sd-server --serve-html-path ./index.html +``` + +In this case, the server will load and serve the specified `index.html` file instead of the embedded frontend. This is useful when: + +* developing or testing frontend changes +* using a custom UI +* avoiding rebuilding the binary after frontend modifications + # Run ```