Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,21 @@ on:
"**/*.c",
"**/*.cpp",
"**/*.cu",
"examples/server/frontend/**",
]
pull_request:
types: [opened, synchronize, reopened]
paths:
[
".github/workflows/**",
"**/CMakeLists.txt",
"**/Makefile",
"**/*.h",
"**/*.hpp",
"**/*.c",
"**/*.cpp",
"**/*.cu",
"examples/server/frontend/**",
]

env:
Expand All @@ -53,6 +56,16 @@ jobs:
with:
submodules: recursive

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20

- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9

- name: Dependencies
id: depends
run: |
Expand Down Expand Up @@ -106,6 +119,16 @@ jobs:
with:
submodules: recursive

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20

- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9

- name: Dependencies
id: depends
run: |
Expand Down Expand Up @@ -174,6 +197,16 @@ jobs:
with:
submodules: recursive

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20

- 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' }}
Expand Down Expand Up @@ -223,6 +256,16 @@ jobs:
with:
submodules: recursive

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20

- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9

- name: Dependencies
id: depends
run: |
Expand Down Expand Up @@ -294,6 +337,16 @@ jobs:
with:
submodules: recursive

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20

- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9

- name: Install cuda-toolkit
id: cuda-toolkit
if: ${{ matrix.build == 'cuda12' }}
Expand Down Expand Up @@ -399,6 +452,16 @@ jobs:
with:
submodules: recursive

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20

- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9

- name: Cache ROCm Installation
id: cache-rocm
uses: actions/cache@v4
Expand Down Expand Up @@ -502,6 +565,16 @@ jobs:
with:
submodules: recursive

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20

- 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
Expand Down
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -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
67 changes: 67 additions & 0 deletions examples/server/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,73 @@
set(TARGET sd-server)

option(SD_SERVER_BUILD_FRONTEND "Build server frontend with pnpm" ON)

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_install
COMMAND "${PNPM_EXECUTABLE}" -C "${FRONTEND_DIR}" install
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()
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(STATUS "HAVE_INDEX_HTML disabled")
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)
89 changes: 89 additions & 0 deletions examples/server/README.md
Original file line number Diff line number Diff line change
@@ -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 <path-to-index.html>
```

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

```
Expand Down
1 change: 1 addition & 0 deletions examples/server/frontend
Submodule frontend added at 1a3417
14 changes: 12 additions & 2 deletions examples/server/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 -----------------------
Expand Down Expand Up @@ -380,7 +384,13 @@ int main(int argc, const char** argv) {
return httplib::Server::HandlerResponse::Unhandled;
});

// root
// index html
std::string index_html;
#ifdef HAVE_INDEX_HTML
index_html.assign(reinterpret_cast<const char*>(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);
Expand All @@ -392,7 +402,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");
}
});

Expand Down
Loading