Skip to content

Commit 862a658

Browse files
authored
feat: add embedded WebUI (#1207)
1 parent 61d8331 commit 862a658

6 files changed

Lines changed: 245 additions & 2 deletions

File tree

.github/workflows/build.yml

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,21 @@ on:
2121
"**/*.c",
2222
"**/*.cpp",
2323
"**/*.cu",
24+
"examples/server/frontend/**",
2425
]
2526
pull_request:
2627
types: [opened, synchronize, reopened]
2728
paths:
2829
[
30+
".github/workflows/**",
2931
"**/CMakeLists.txt",
3032
"**/Makefile",
3133
"**/*.h",
3234
"**/*.hpp",
3335
"**/*.c",
3436
"**/*.cpp",
3537
"**/*.cu",
38+
"examples/server/frontend/**",
3639
]
3740

3841
env:
@@ -53,6 +56,16 @@ jobs:
5356
with:
5457
submodules: recursive
5558

59+
- name: Setup Node
60+
uses: actions/setup-node@v4
61+
with:
62+
node-version: 20
63+
64+
- name: Setup pnpm
65+
uses: pnpm/action-setup@v4
66+
with:
67+
version: 9
68+
5669
- name: Dependencies
5770
id: depends
5871
run: |
@@ -106,6 +119,16 @@ jobs:
106119
with:
107120
submodules: recursive
108121

122+
- name: Setup Node
123+
uses: actions/setup-node@v4
124+
with:
125+
node-version: 20
126+
127+
- name: Setup pnpm
128+
uses: pnpm/action-setup@v4
129+
with:
130+
version: 9
131+
109132
- name: Dependencies
110133
id: depends
111134
run: |
@@ -174,6 +197,16 @@ jobs:
174197
with:
175198
submodules: recursive
176199

200+
- name: Setup Node
201+
uses: actions/setup-node@v4
202+
with:
203+
node-version: 20
204+
205+
- name: Setup pnpm
206+
uses: pnpm/action-setup@v4
207+
with:
208+
version: 9
209+
177210
- name: Get commit hash
178211
id: commit
179212
if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) || github.event.inputs.create_release == 'true' }}
@@ -223,6 +256,16 @@ jobs:
223256
with:
224257
submodules: recursive
225258

259+
- name: Setup Node
260+
uses: actions/setup-node@v4
261+
with:
262+
node-version: 20
263+
264+
- name: Setup pnpm
265+
uses: pnpm/action-setup@v4
266+
with:
267+
version: 9
268+
226269
- name: Dependencies
227270
id: depends
228271
run: |
@@ -294,6 +337,16 @@ jobs:
294337
with:
295338
submodules: recursive
296339

340+
- name: Setup Node
341+
uses: actions/setup-node@v4
342+
with:
343+
node-version: 20
344+
345+
- name: Setup pnpm
346+
uses: pnpm/action-setup@v4
347+
with:
348+
version: 9
349+
297350
- name: Install cuda-toolkit
298351
id: cuda-toolkit
299352
if: ${{ matrix.build == 'cuda12' }}
@@ -399,6 +452,16 @@ jobs:
399452
with:
400453
submodules: recursive
401454

455+
- name: Setup Node
456+
uses: actions/setup-node@v4
457+
with:
458+
node-version: 20
459+
460+
- name: Setup pnpm
461+
uses: pnpm/action-setup@v4
462+
with:
463+
version: 9
464+
402465
- name: Cache ROCm Installation
403466
id: cache-rocm
404467
uses: actions/cache@v4
@@ -502,6 +565,16 @@ jobs:
502565
with:
503566
submodules: recursive
504567

568+
- name: Setup Node
569+
uses: actions/setup-node@v4
570+
with:
571+
node-version: 20
572+
573+
- name: Setup pnpm
574+
uses: pnpm/action-setup@v4
575+
with:
576+
version: 9
577+
505578
- name: Free disk space
506579
run: |
507580
# Remove preinstalled SDKs and caches not needed for this job

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
[submodule "ggml"]
22
path = ggml
33
url = https://github.com/ggml-org/ggml.git
4+
[submodule "examples/server/frontend"]
5+
path = examples/server/frontend
6+
url = https://github.com/leejet/stable-ui.git

examples/server/CMakeLists.txt

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,73 @@
11
set(TARGET sd-server)
22

3+
option(SD_SERVER_BUILD_FRONTEND "Build server frontend with pnpm" ON)
4+
5+
set(FRONTEND_DIR "${CMAKE_CURRENT_SOURCE_DIR}/frontend")
6+
set(GENERATED_HTML_HEADER "${FRONTEND_DIR}/dist/gen_index_html.h")
7+
8+
set(HAVE_FRONTEND_BUILD OFF)
9+
10+
if(SD_SERVER_BUILD_FRONTEND AND EXISTS "${FRONTEND_DIR}")
11+
if(WIN32)
12+
find_program(PNPM_EXECUTABLE NAMES pnpm.cmd pnpm)
13+
else()
14+
find_program(PNPM_EXECUTABLE NAMES pnpm)
15+
endif()
16+
17+
if(PNPM_EXECUTABLE)
18+
message(STATUS "Frontend dir found: ${FRONTEND_DIR}")
19+
message(STATUS "pnpm found: ${PNPM_EXECUTABLE}")
20+
21+
set(HAVE_FRONTEND_BUILD ON)
22+
23+
add_custom_target(${TARGET}_frontend_install
24+
COMMAND "${PNPM_EXECUTABLE}" -C "${FRONTEND_DIR}" install
25+
WORKING_DIRECTORY "${FRONTEND_DIR}"
26+
COMMENT "Installing frontend dependencies"
27+
VERBATIM
28+
)
29+
30+
add_custom_target(${TARGET}_frontend_build
31+
COMMAND "${PNPM_EXECUTABLE}" -C "${FRONTEND_DIR}" run build
32+
WORKING_DIRECTORY "${FRONTEND_DIR}"
33+
COMMENT "Building frontend"
34+
VERBATIM
35+
)
36+
37+
add_custom_target(${TARGET}_frontend_header
38+
COMMAND "${PNPM_EXECUTABLE}" -C "${FRONTEND_DIR}" run build:header
39+
WORKING_DIRECTORY "${FRONTEND_DIR}"
40+
COMMENT "Generating gen_index_html.h"
41+
VERBATIM
42+
)
43+
44+
add_dependencies(${TARGET}_frontend_build ${TARGET}_frontend_install)
45+
add_dependencies(${TARGET}_frontend_header ${TARGET}_frontend_build)
46+
47+
add_custom_target(${TARGET}_frontend
48+
DEPENDS ${TARGET}_frontend_header
49+
)
50+
51+
set_source_files_properties("${GENERATED_HTML_HEADER}" PROPERTIES GENERATED TRUE)
52+
else()
53+
message(WARNING "pnpm not found, frontend build disabled")
54+
endif()
55+
else()
56+
message(STATUS "Frontend disabled or directory not found: ${FRONTEND_DIR}")
57+
endif()
58+
359
add_executable(${TARGET} main.cpp)
60+
61+
if(HAVE_FRONTEND_BUILD)
62+
add_dependencies(${TARGET} ${TARGET}_frontend)
63+
target_sources(${TARGET} PRIVATE "${GENERATED_HTML_HEADER}")
64+
target_include_directories(${TARGET} PRIVATE "${FRONTEND_DIR}/dist")
65+
target_compile_definitions(${TARGET} PRIVATE HAVE_INDEX_HTML)
66+
message(STATUS "HAVE_INDEX_HTML enabled")
67+
else()
68+
message(STATUS "HAVE_INDEX_HTML disabled")
69+
endif()
70+
471
install(TARGETS ${TARGET} RUNTIME)
572
target_link_libraries(${TARGET} PRIVATE stable-diffusion ${CMAKE_THREAD_LIBS_INIT})
673
target_compile_features(${TARGET} PUBLIC c_std_11 cxx_std_17)

examples/server/README.md

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,92 @@
1+
# Frontend
2+
3+
## Build with Frontend
4+
5+
The server can optionally build the web frontend and embed it into the binary as `gen_index_html.h`.
6+
7+
### Requirements
8+
9+
Install the following tools:
10+
11+
* **Node.js** ≥ 22.18
12+
https://nodejs.org/
13+
14+
* **pnpm** ≥ 10
15+
Install via npm:
16+
17+
```bash
18+
npm install -g pnpm
19+
```
20+
21+
Verify installation:
22+
23+
```bash
24+
node -v
25+
pnpm -v
26+
```
27+
28+
### Install frontend dependencies
29+
30+
Go to the frontend directory and install dependencies:
31+
32+
```bash
33+
cd examples/server/frontend
34+
pnpm install
35+
```
36+
37+
### Build the server with CMake
38+
39+
Enable the frontend build option when configuring CMake:
40+
41+
```bash
42+
cmake -B build -DSD_SERVER_BUILD_FRONTEND=ON
43+
cmake --build build --config Release
44+
```
45+
46+
If `pnpm` is available, the build system will automatically run:
47+
48+
```
49+
pnpm run build
50+
pnpm run build:header
51+
```
52+
53+
and embed the generated frontend into the server binary.
54+
55+
## Frontend Repository
56+
57+
The web frontend is maintained in a **separate repository**, https://github.com/leejet/stable-ui.
58+
59+
If you want to modify the UI or frontend logic, please submit pull requests to the **frontend repository**.
60+
61+
This repository (`stable-diffusion.cpp`) only vendors the frontend periodically. Changes from the frontend repo are synchronized:
62+
63+
* approximately **every 1–2 weeks**, or
64+
* when there are **major frontend updates**
65+
66+
Because of this, frontend changes will **not appear here immediately** after being merged upstream.
67+
68+
## Using an external frontend
69+
70+
By default, the server uses the **embedded frontend** generated during the build (`gen_index_html.h`).
71+
72+
You can also serve a custom frontend file instead of the embedded one by using:
73+
74+
```bash
75+
--serve-html-path <path-to-index.html>
76+
```
77+
78+
For example:
79+
80+
```bash
81+
sd-server --serve-html-path ./index.html
82+
```
83+
84+
In this case, the server will load and serve the specified `index.html` file instead of the embedded frontend. This is useful when:
85+
86+
* developing or testing frontend changes
87+
* using a custom UI
88+
* avoiding rebuilding the binary after frontend modifications
89+
190
# Run
291

392
```

examples/server/frontend

Submodule frontend added at 1a34176

examples/server/main.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313

1414
#include "common/common.hpp"
1515

16+
#ifdef HAVE_INDEX_HTML
17+
#include "frontend/dist/gen_index_html.h"
18+
#endif
19+
1620
namespace fs = std::filesystem;
1721

1822
// ----------------------- helpers -----------------------
@@ -380,7 +384,13 @@ int main(int argc, const char** argv) {
380384
return httplib::Server::HandlerResponse::Unhandled;
381385
});
382386

383-
// root
387+
// index html
388+
std::string index_html;
389+
#ifdef HAVE_INDEX_HTML
390+
index_html.assign(reinterpret_cast<const char*>(index_html_bytes), index_html_size);
391+
#else
392+
index_html = "Stable Diffusion Server is running";
393+
#endif
384394
svr.Get("/", [&](const httplib::Request&, httplib::Response& res) {
385395
if (!svr_params.serve_html_path.empty()) {
386396
std::ifstream file(svr_params.serve_html_path);
@@ -392,7 +402,7 @@ int main(int argc, const char** argv) {
392402
res.set_content("Error: Unable to read HTML file", "text/plain");
393403
}
394404
} else {
395-
res.set_content("Stable Diffusion Server is running", "text/plain");
405+
res.set_content(index_html, "text/html");
396406
}
397407
});
398408

0 commit comments

Comments
 (0)