This is an automated email from the ASF dual-hosted git repository.

piotr pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iggy.git


The following commit(s) were added to refs/heads/master by this push:
     new effbab6f6 feat(bench): add embeddable chart endpoints with PNG 
rendering (#2833)
effbab6f6 is described below

commit effbab6f6a01386b65c115d9aa0d877c28b5dd83
Author: Hubert Gruszecki <[email protected]>
AuthorDate: Sun Mar 1 21:02:50 2026 +0100

    feat(bench): add embeddable chart endpoints with PNG rendering (#2833)
    
    The dashboard lacked a way to share benchmark charts externally
    (e.g. in GitHub issues, docs, or third-party pages).
    
    Add /embed/{uuid} for client-side rendering (self-contained HTML
    page with ECharts) and /embed/{uuid}/chart.png for server-side
    PNG rendering (charming SSR with deno_core/V8). A dedicated
    render thread pins V8 to a single OS thread, with disk caching
    so only the first request per variant incurs render cost. The
    frontend gets an embed modal (iframe/image tabs, markdown/HTML
    snippets, copy/download) wired to a topbar button.
---
 .asf.yaml                                          |    2 +-
 .github/actions/rust/pre-merge/action.yml          |    8 +-
 .github/workflows/_build_rust_artifacts.yml        |   11 +-
 Cargo.lock                                         | 1262 +++++++++++++++++++-
 DEPENDENCIES.md                                    |  123 +-
 core/bench/dashboard/README.md                     |    4 +-
 core/bench/dashboard/frontend/Cargo.toml           |    2 +-
 core/bench/dashboard/frontend/assets/style.css     |  139 +++
 .../frontend/src/components/embed_modal.rs         |  205 ++++
 .../frontend/src/components/layout/topbar.rs       |   41 +
 .../bench/dashboard/frontend/src/components/mod.rs |    2 +-
 core/bench/dashboard/frontend/src/state/ui.rs      |    7 +
 core/bench/dashboard/server/Cargo.toml             |    3 +-
 core/bench/dashboard/server/src/cache/loader.rs    |    1 +
 core/bench/dashboard/server/src/cache/storage.rs   |    4 +
 core/bench/dashboard/server/src/cache/watcher.rs   |    7 +
 core/bench/dashboard/server/src/error.rs           |    6 +
 core/bench/dashboard/server/src/handlers.rs        |  450 +++++++
 core/bench/dashboard/server/src/main.rs            |    8 +
 core/bench/dashboard/server/src/render.rs          |   93 ++
 core/bench/report/src/plotting/chart.rs            |    1 +
 21 files changed, 2350 insertions(+), 29 deletions(-)

diff --git a/.asf.yaml b/.asf.yaml
index d9ca3e856..491791f5b 100644
--- a/.asf.yaml
+++ b/.asf.yaml
@@ -46,7 +46,7 @@ github:
     io_uring_tpc:
       required_pull_request_reviews:
         dismiss_stale_reviews: true
-        required_approving_review_count: 1  
+        required_approving_review_count: 1
       required_conversation_resolution: true
   custom_subjects:
     new_discussion: "{title}"
diff --git a/.github/actions/rust/pre-merge/action.yml 
b/.github/actions/rust/pre-merge/action.yml
index eb852c6d6..453b28efd 100644
--- a/.github/actions/rust/pre-merge/action.yml
+++ b/.github/actions/rust/pre-merge/action.yml
@@ -173,7 +173,13 @@ runs:
 
     - name: Build aarch64-musl
       if: inputs.task == 'build-aarch64-musl'
-      run: cargo build --locked --target aarch64-unknown-linux-musl
+      # Exclude dashboard crates: charming's "ssr" feature pulls deno_core -> 
v8,
+      # which has no prebuilt binary for aarch64-unknown-linux-musl.
+      # TODO: support when upstream rusty_v8 publishes aarch64-musl binaries.
+      run: >-
+        cargo build --locked --workspace --target aarch64-unknown-linux-musl
+        --exclude iggy-bench-dashboard-server
+        --exclude bench-dashboard-frontend
       shell: bash
       env:
         # Disable GCC outline atomics to avoid undefined __aarch64_ldadd4_sync
diff --git a/.github/workflows/_build_rust_artifacts.yml 
b/.github/workflows/_build_rust_artifacts.yml
index ff4258883..3c2be13a3 100644
--- a/.github/workflows/_build_rust_artifacts.yml
+++ b/.github/workflows/_build_rust_artifacts.yml
@@ -144,7 +144,16 @@ jobs:
             export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER=musl-gcc
           fi
 
-          cargo build --locked --release --target ${{ matrix.target }} 
"${bin_flags[@]}"
+          # iggy-bench-dashboard-server enables charming's "ssr" feature 
(deno_core -> v8).
+          # v8 has no prebuilt binary for aarch64-unknown-linux-musl, so 
exclude dashboard
+          # crates to prevent cargo feature unification from pulling V8 into 
unrelated binaries.
+          # TODO: build V8 from source (V8_FROM_SOURCE=1, ~30min + clang) or 
wait for upstream support.
+          exclude_flags=()
+          if [[ "${{ matrix.target }}" == "aarch64-unknown-linux-musl" ]]; then
+            exclude_flags+=(--workspace --exclude iggy-bench-dashboard-server 
--exclude bench-dashboard-frontend)
+          fi
+
+          cargo build --locked --release --target ${{ matrix.target }} 
"${bin_flags[@]}" "${exclude_flags[@]}"
 
       - name: Package binaries
         id: pkg
diff --git a/Cargo.lock b/Cargo.lock
index 7ce811cec..e020afd0f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -299,6 +299,24 @@ dependencies = [
  "memchr",
 ]
 
+[[package]]
+name = "aligned"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "ee4508988c62edf04abd8d92897fca0c2995d907ce1dfeaf369dac3716a40685"
+dependencies = [
+ "as-slice",
+]
+
+[[package]]
+name = "aligned-vec"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b"
+dependencies = [
+ "equator",
+]
+
 [[package]]
 name = "alloc-no-stdlib"
 version = "2.0.4"
@@ -410,6 +428,12 @@ dependencies = [
  "zstd",
 ]
 
+[[package]]
+name = "arbitrary"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
+
 [[package]]
 name = "arc-swap"
 version = "1.8.1"
@@ -419,6 +443,17 @@ dependencies = [
  "rustversion",
 ]
 
+[[package]]
+name = "arg_enum_proc_macro"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.115",
+]
+
 [[package]]
 name = "argon2"
 version = "0.5.3"
@@ -621,6 +656,15 @@ version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "b0f477b951e452a0b6b4a10b53ccd569042d1d01729b519e02074a9c0958a063"
 
+[[package]]
+name = "as-slice"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516"
+dependencies = [
+ "stable_deref_trait",
+]
+
 [[package]]
 name = "asn1-rs"
 version = "0.7.1"
@@ -891,6 +935,49 @@ dependencies = [
  "cc",
 ]
 
+[[package]]
+name = "av-scenechange"
+version = "0.14.1"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "0f321d77c20e19b92c39e7471cf986812cbb46659d2af674adc4331ef3f18394"
+dependencies = [
+ "aligned",
+ "anyhow",
+ "arg_enum_proc_macro",
+ "arrayvec",
+ "log",
+ "num-rational",
+ "num-traits",
+ "pastey 0.1.1",
+ "rayon",
+ "thiserror 2.0.18",
+ "v_frame",
+ "y4m",
+]
+
+[[package]]
+name = "av1-grain"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "8cfddb07216410377231960af4fcab838eaa12e013417781b78bd95ee22077f8"
+dependencies = [
+ "anyhow",
+ "arrayvec",
+ "log",
+ "nom 8.0.0",
+ "num-rational",
+ "v_frame",
+]
+
+[[package]]
+name = "avif-serialize"
+version = "0.8.8"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "375082f007bd67184fb9c0374614b29f9aaa604ec301635f72338bb65386a53d"
+dependencies = [
+ "arrayvec",
+]
+
 [[package]]
 name = "aws-lc-rs"
 version = "1.15.4"
@@ -999,6 +1086,12 @@ dependencies = [
  "tower-service",
 ]
 
+[[package]]
+name = "az"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "be5eb007b7cacc6c660343e96f650fedf4b5a77512399eb952ca6642cf8d13f7"
+
 [[package]]
 name = "backon"
 version = "1.6.0"
@@ -1028,6 +1121,16 @@ version = "0.22.1"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
 
+[[package]]
+name = "base64-simd"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "339abbe78e73178762e23bea9dfd08e697eb3f3301cd4be981c0f78ba5859195"
+dependencies = [
+ "outref",
+ "vsimd",
+]
+
 [[package]]
 name = "base64ct"
 version = "1.8.3"
@@ -1052,7 +1155,7 @@ checksum = 
"3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1"
 
 [[package]]
 name = "bench-dashboard-frontend"
-version = "0.5.1-edge.1"
+version = "0.5.2-edge.1"
 dependencies = [
  "bench-dashboard-shared",
  "bench-report",
@@ -1140,6 +1243,26 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "bindgen"
+version = "0.71.1"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3"
+dependencies = [
+ "bitflags 2.10.0",
+ "cexpr",
+ "clang-sys",
+ "itertools 0.13.0",
+ "log",
+ "prettyplease",
+ "proc-macro2",
+ "quote",
+ "regex",
+ "rustc-hash",
+ "shlex",
+ "syn 2.0.115",
+]
+
 [[package]]
 name = "bit-set"
 version = "0.8.0"
@@ -1155,6 +1278,12 @@ version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
 
+[[package]]
+name = "bit_field"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6"
+
 [[package]]
 name = "bitflags"
 version = "1.3.2"
@@ -1170,6 +1299,15 @@ dependencies = [
  "serde_core",
 ]
 
+[[package]]
+name = "bitstream-io"
+version = "4.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "60d4bd9d1db2c6bdf285e223a7fa369d5ce98ec767dec949c6ca62863ce61757"
+dependencies = [
+ "core2",
+]
+
 [[package]]
 name = "bitvec"
 version = "1.0.1"
@@ -1380,6 +1518,12 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "built"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "f4ad8f11f288f48ca24471bbd51ac257aaeaaa07adae295591266b792902ae64"
+
 [[package]]
 name = "bumpalo"
 version = "3.19.1"
@@ -1438,6 +1582,12 @@ version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
 
+[[package]]
+name = "byteorder-lite"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
+
 [[package]]
 name = "bytes"
 version = "1.11.1"
@@ -1471,6 +1621,26 @@ dependencies = [
  "serde_core",
 ]
 
+[[package]]
+name = "capacity_builder"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "8f2d24a6dcf0cd402a21b65d35340f3a49ff3475dc5fdac91d22d2733e6641c6"
+dependencies = [
+ "capacity_builder_macros",
+ "itoa",
+]
+
+[[package]]
+name = "capacity_builder_macros"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "3b4a6cae9efc04cc6cbb8faf338d2c497c165c83e74509cf4dbedea948bbf6e5"
+dependencies = [
+ "quote",
+ "syn 2.0.115",
+]
+
 [[package]]
 name = "cargo-platform"
 version = "0.3.2"
@@ -1513,6 +1683,15 @@ version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
 
+[[package]]
+name = "cexpr"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
+dependencies = [
+ "nom 7.1.3",
+]
+
 [[package]]
 name = "cfg-if"
 version = "1.0.4"
@@ -1543,11 +1722,15 @@ source = 
"registry+https://github.com/rust-lang/crates.io-index";
 checksum = "df19cf7d4540f4db08203dce5171421fdfc5e83ea3f14d7b5329f0dbff3bf3c2"
 dependencies = [
  "charming_macros",
+ "deno_core",
  "handlebars",
+ "image",
  "js-sys",
+ "resvg",
  "serde",
  "serde-wasm-bindgen",
  "serde_json",
+ "serde_v8",
  "serde_with",
  "wasm-bindgen",
  "web-sys",
@@ -1588,6 +1771,17 @@ dependencies = [
  "inout",
 ]
 
+[[package]]
+name = "clang-sys"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
+dependencies = [
+ "glob",
+ "libc",
+ "libloading",
+]
+
 [[package]]
 name = "clap"
 version = "4.5.58"
@@ -1663,6 +1857,12 @@ dependencies = [
  "thiserror 2.0.18",
 ]
 
+[[package]]
+name = "color_quant"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
+
 [[package]]
 name = "colorchoice"
 version = "1.0.4"
@@ -2044,6 +2244,12 @@ dependencies = [
  "unicode-segmentation",
 ]
 
+[[package]]
+name = "cooked-waker"
+version = "5.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "147be55d677052dabc6b22252d5dd0fd4c29c8c27aa4f2fbef0f94aa003b406f"
+
 [[package]]
 name = "cookie"
 version = "0.16.2"
@@ -2071,6 +2277,15 @@ version = "0.8.7"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
 
+[[package]]
+name = "core2"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505"
+dependencies = [
+ "memchr",
+]
+
 [[package]]
 name = "core_affinity"
 version = "0.8.3"
@@ -2082,6 +2297,15 @@ dependencies = [
  "winapi",
 ]
 
+[[package]]
+name = "core_maths"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "77745e017f5edba1a9c1d854f6f3a52dac8a12dd5af5d2f54aecf61e43d80d30"
+dependencies = [
+ "libm",
+]
+
 [[package]]
 name = "cpufeatures"
 version = "0.2.17"
@@ -2205,7 +2429,7 @@ dependencies = [
  "crossterm_winapi",
  "document-features",
  "parking_lot",
- "rustix",
+ "rustix 1.1.3",
  "winapi",
 ]
 
@@ -2551,6 +2775,12 @@ version = "2.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea"
 
+[[package]]
+name = "data-url"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376"
+
 [[package]]
 name = "dbus"
 version = "0.9.10"
@@ -2573,6 +2803,126 @@ dependencies = [
  "zeroize",
 ]
 
+[[package]]
+name = "debugid"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d"
+dependencies = [
+ "serde",
+ "uuid",
+]
+
+[[package]]
+name = "deno_core"
+version = "0.351.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "4301eb6d378f3ae81fbac4cde14c3f467379efd7d46043268d76905effe3611d"
+dependencies = [
+ "anyhow",
+ "az",
+ "bincode",
+ "bit-set",
+ "bit-vec",
+ "bytes",
+ "capacity_builder",
+ "cooked-waker",
+ "deno_core_icudata",
+ "deno_error",
+ "deno_ops",
+ "deno_path_util",
+ "deno_unsync",
+ "futures",
+ "indexmap 2.13.0",
+ "libc",
+ "parking_lot",
+ "percent-encoding",
+ "pin-project",
+ "serde",
+ "serde_json",
+ "serde_v8",
+ "smallvec",
+ "sourcemap",
+ "static_assertions",
+ "thiserror 2.0.18",
+ "tokio",
+ "url",
+ "v8",
+ "wasm_dep_analyzer",
+]
+
+[[package]]
+name = "deno_core_icudata"
+version = "0.74.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "fe4dccb6147bb3f3ba0c7a48e993bfeb999d2c2e47a81badee80e2b370c8d695"
+
+[[package]]
+name = "deno_error"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "612ec3fc481fea759141b0c57810889b0a4fb6fee8f10748677bfe492fd30486"
+dependencies = [
+ "deno_error_macro",
+ "libc",
+ "serde",
+ "serde_json",
+ "tokio",
+ "url",
+]
+
+[[package]]
+name = "deno_error_macro"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "8380a4224d5d2c3f84da4d764c4326cac62e9a1e3d4960442d29136fc07be863"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.115",
+]
+
+[[package]]
+name = "deno_ops"
+version = "0.227.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "1bab1eaf578a8cc0ae6fb933e91dc3388b41df22e5974d5891c17ba66b3a0bbb"
+dependencies = [
+ "indexmap 2.13.0",
+ "proc-macro-rules",
+ "proc-macro2",
+ "quote",
+ "stringcase",
+ "strum",
+ "strum_macros",
+ "syn 2.0.115",
+ "thiserror 2.0.18",
+]
+
+[[package]]
+name = "deno_path_util"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "516f813389095889776b81cc9108ff6f336fd9409b4b12fc0138aea23d2708e1"
+dependencies = [
+ "deno_error",
+ "percent-encoding",
+ "sys_traits",
+ "thiserror 2.0.18",
+ "url",
+]
+
+[[package]]
+name = "deno_unsync"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "6742a724e8becb372a74c650a1aefb8924a5b8107f7d75b3848763ea24b27a87"
+dependencies = [
+ "futures-util",
+ "parking_lot",
+ "tokio",
+]
+
 [[package]]
 name = "der"
 version = "0.7.10"
@@ -3000,6 +3350,26 @@ dependencies = [
  "syn 2.0.115",
 ]
 
+[[package]]
+name = "equator"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc"
+dependencies = [
+ "equator-macro",
+]
+
+[[package]]
+name = "equator-macro"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.115",
+]
+
 [[package]]
 name = "equivalent"
 version = "1.0.2"
@@ -3067,6 +3437,15 @@ dependencies = [
  "windows-sys 0.61.2",
 ]
 
+[[package]]
+name = "euclid"
+version = "0.22.13"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "df61bf483e837f88d5c2291dcf55c67be7e676b3a51acc48db3a7b163b91ed63"
+dependencies = [
+ "num-traits",
+]
+
 [[package]]
 name = "event-listener"
 version = "5.4.1"
@@ -3098,6 +3477,21 @@ dependencies = [
  "once_cell",
 ]
 
+[[package]]
+name = "exr"
+version = "1.74.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "4300e043a56aa2cb633c01af81ca8f699a321879a7854d3896a0ba89056363be"
+dependencies = [
+ "bit_field",
+ "half",
+ "lebe",
+ "miniz_oxide",
+ "rayon-core",
+ "smallvec",
+ "zune-inflate",
+]
+
 [[package]]
 name = "ext-trait"
 version = "1.0.1"
@@ -3148,6 +3542,35 @@ dependencies = [
  "getrandom 0.2.17",
 ]
 
+[[package]]
+name = "fax"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "f05de7d48f37cd6730705cbca900770cab77a89f413d23e100ad7fad7795a0ab"
+dependencies = [
+ "fax_derive",
+]
+
+[[package]]
+name = "fax_derive"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.115",
+]
+
+[[package]]
+name = "fdeflate"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c"
+dependencies = [
+ "simd-adler32",
+]
+
 [[package]]
 name = "ferroid"
 version = "0.8.9"
@@ -3242,6 +3665,12 @@ dependencies = [
  "zlib-rs",
 ]
 
+[[package]]
+name = "float-cmp"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
+
 [[package]]
 name = "float-cmp"
 version = "0.10.0"
@@ -3287,10 +3716,33 @@ source = 
"registry+https://github.com/rust-lang/crates.io-index";
 checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
 
 [[package]]
-name = "foldhash"
-version = "0.2.0"
+name = "foldhash"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
+
+[[package]]
+name = "fontconfig-parser"
+version = "0.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "bbc773e24e02d4ddd8395fd30dc147524273a83e54e0f312d986ea30de5f5646"
+dependencies = [
+ "roxmltree",
+]
+
+[[package]]
+name = "fontdb"
+version = "0.23.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
+checksum = "457e789b3d1202543297a350643cf459f836cade38934e7a4cf6a39e7cde2905"
+dependencies = [
+ "fontconfig-parser",
+ "log",
+ "memmap2",
+ "slotmap",
+ "tinyvec",
+ "ttf-parser",
+]
 
 [[package]]
 name = "foreign-types"
@@ -3357,6 +3809,16 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "fslock"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "04412b8935272e3a9bae6f48c7bfff74c2911f60525404edfdd28e49884c3bfb"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
 [[package]]
 name = "funty"
 version = "2.0.0"
@@ -3589,6 +4051,26 @@ dependencies = [
  "typed-builder 0.23.2",
 ]
 
+[[package]]
+name = "gif"
+version = "0.13.3"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "4ae047235e33e2829703574b54fdec96bfbad892062d97fed2f76022287de61b"
+dependencies = [
+ "color_quant",
+ "weezl",
+]
+
+[[package]]
+name = "gif"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "f954a9e9159ec994f73a30a12b96a702dde78f5547bcb561174597924f7d4162"
+dependencies = [
+ "color_quant",
+ "weezl",
+]
+
 [[package]]
 name = "git2"
 version = "0.20.4"
@@ -3602,6 +4084,12 @@ dependencies = [
  "url",
 ]
 
+[[package]]
+name = "glob"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
+
 [[package]]
 name = "globset"
 version = "0.4.18"
@@ -3844,6 +4332,15 @@ dependencies = [
  "subtle",
 ]
 
+[[package]]
+name = "gzip-header"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "95cc527b92e6029a62960ad99aa8a6660faa4555fe5f731aab13aa6a921795a2"
+dependencies = [
+ "crc32fast",
+]
+
 [[package]]
 name = "h2"
 version = "0.3.27"
@@ -4491,6 +4988,12 @@ dependencies = [
  "icu_properties",
 ]
 
+[[package]]
+name = "if_chain"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "cd62e6b5e86ea8eeeb8db1de02880a6abc01a397b2ebb64b5d74ac255318f5cb"
+
 [[package]]
 name = "iggy"
 version = "0.9.0"
@@ -4553,13 +5056,14 @@ dependencies = [
 
 [[package]]
 name = "iggy-bench-dashboard-server"
-version = "0.6.1-edge.1"
+version = "0.6.2-edge.1"
 dependencies = [
  "actix-cors",
  "actix-files",
  "actix-web",
  "bench-dashboard-shared",
  "bench-report",
+ "charming",
  "chrono",
  "clap",
  "dashmap",
@@ -4955,6 +5459,52 @@ dependencies = [
  "winapi-util",
 ]
 
+[[package]]
+name = "image"
+version = "0.25.9"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "e6506c6c10786659413faa717ceebcb8f70731c0a60cbae39795fdf114519c1a"
+dependencies = [
+ "bytemuck",
+ "byteorder-lite",
+ "color_quant",
+ "exr",
+ "gif 0.14.0",
+ "image-webp",
+ "moxcms",
+ "num-traits",
+ "png 0.18.0",
+ "qoi",
+ "ravif",
+ "rayon",
+ "rgb",
+ "tiff",
+ "zune-core 0.5.1",
+ "zune-jpeg 0.5.12",
+]
+
+[[package]]
+name = "image-webp"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3"
+dependencies = [
+ "byteorder-lite",
+ "quick-error",
+]
+
+[[package]]
+name = "imagesize"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "edcd27d72f2f071c64249075f42e205ff93c9a4c5f6c6da53e79ed9f9832c285"
+
+[[package]]
+name = "imgref"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "e7c5cedc30da3a610cac6b4ba17597bdf7152cf974e8aab3afb3d54455e371c8"
+
 [[package]]
 name = "impl-more"
 version = "0.1.9"
@@ -5103,6 +5653,17 @@ dependencies = [
  "zip",
 ]
 
+[[package]]
+name = "interpolate_name"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.115",
+]
+
 [[package]]
 name = "inventory"
 version = "0.3.21"
@@ -5131,7 +5692,7 @@ checksum = 
"1838759bb8c2f24cf05a35429d83145c4aa6af43f8ad38477295e12a7320a80e"
 dependencies = [
  "bytes",
  "io-uring",
- "rustix",
+ "rustix 1.1.3",
 ]
 
 [[package]]
@@ -5341,6 +5902,17 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "kurbo"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "c62026ae44756f8a599ba21140f350303d4f08dcdcc71b5ad9c9bb8128c13c62"
+dependencies = [
+ "arrayvec",
+ "euclid",
+ "smallvec",
+]
+
 [[package]]
 name = "language-tags"
 version = "0.3.2"
@@ -5362,6 +5934,12 @@ version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
 
+[[package]]
+name = "lebe"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8"
+
 [[package]]
 name = "left-right"
 version = "0.11.7"
@@ -5477,6 +6055,16 @@ dependencies = [
  "pkg-config",
 ]
 
+[[package]]
+name = "libfuzzer-sys"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "f12a681b7dd8ce12bff52488013ba614b869148d54dd79836ab85aafdd53f08d"
+dependencies = [
+ "arbitrary",
+ "cc",
+]
+
 [[package]]
 name = "libgit2-sys"
 version = "0.18.3+1.9.2"
@@ -5489,6 +6077,16 @@ dependencies = [
  "pkg-config",
 ]
 
+[[package]]
+name = "libloading"
+version = "0.8.9"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55"
+dependencies = [
+ "cfg-if",
+ "windows-link 0.2.1",
+]
+
 [[package]]
 name = "liblzma"
 version = "0.4.5"
@@ -5564,6 +6162,12 @@ version = "0.5.6"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
 
+[[package]]
+name = "linux-raw-sys"
+version = "0.4.15"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
+
 [[package]]
 name = "linux-raw-sys"
 version = "0.11.0"
@@ -5661,6 +6265,15 @@ dependencies = [
  "tracing-subscriber",
 ]
 
+[[package]]
+name = "loop9"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062"
+dependencies = [
+ "imgref",
+]
+
 [[package]]
 name = "lru-slab"
 version = "0.1.2"
@@ -5707,6 +6320,16 @@ version = "0.8.4"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3"
 
+[[package]]
+name = "maybe-rayon"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519"
+dependencies = [
+ "cfg-if",
+ "rayon",
+]
+
 [[package]]
 name = "md-5"
 version = "0.10.6"
@@ -5723,6 +6346,15 @@ version = "2.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
 
+[[package]]
+name = "memmap2"
+version = "0.9.10"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3"
+dependencies = [
+ "libc",
+]
+
 [[package]]
 name = "message_bus"
 version = "0.1.0"
@@ -5869,6 +6501,16 @@ dependencies = [
  "uuid",
 ]
 
+[[package]]
+name = "moxcms"
+version = "0.7.11"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "ac9557c559cd6fc9867e122e20d2cbefc9ca29d80d027a8e39310920ed2f0a97"
+dependencies = [
+ "num-traits",
+ "pxfm",
+]
+
 [[package]]
 name = "murmur3"
 version = "0.5.2"
@@ -5881,6 +6523,12 @@ version = "6.6.666"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "cf5a574dadd7941adeaa71823ecba5e28331b8313fb2e1c6a5c7e5981ea53ad6"
 
+[[package]]
+name = "new_debug_unreachable"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
+
 [[package]]
 name = "nix"
 version = "0.31.1"
@@ -5935,6 +6583,12 @@ version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "30a9e0365849b8ce3d067218268d6c523c1c14f539f7e9157d635d8adfb308fa"
 
+[[package]]
+name = "noop_proc_macro"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8"
+
 [[package]]
 name = "normalize-line-endings"
 version = "0.3.0"
@@ -6029,6 +6683,7 @@ checksum = 
"a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
 dependencies = [
  "num-integer",
  "num-traits",
+ "rand 0.8.5",
  "serde",
 ]
 
@@ -6063,6 +6718,17 @@ version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050"
 
+[[package]]
+name = "num-derive"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.115",
+]
+
 [[package]]
 name = "num-integer"
 version = "0.1.46"
@@ -6463,6 +7129,12 @@ dependencies = [
  "windows-sys 0.61.2",
 ]
 
+[[package]]
+name = "outref"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e"
+
 [[package]]
 name = "p256"
 version = "0.13.2"
@@ -6627,6 +7299,12 @@ version = "1.0.15"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
 
+[[package]]
+name = "pastey"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec"
+
 [[package]]
 name = "pastey"
 version = "0.2.1"
@@ -6751,6 +7429,12 @@ dependencies = [
  "sha2",
 ]
 
+[[package]]
+name = "pico-args"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
+
 [[package]]
 name = "pin-project"
 version = "1.1.10"
@@ -6821,6 +7505,32 @@ version = "0.3.32"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
 
+[[package]]
+name = "png"
+version = "0.17.16"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526"
+dependencies = [
+ "bitflags 1.3.2",
+ "crc32fast",
+ "fdeflate",
+ "flate2",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "png"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "97baced388464909d42d89643fe4361939af9b7ce7a31ee32a168f832a70f2a0"
+dependencies = [
+ "bitflags 2.10.0",
+ "crc32fast",
+ "fdeflate",
+ "flate2",
+ "miniz_oxide",
+]
+
 [[package]]
 name = "polling"
 version = "3.11.0"
@@ -6831,7 +7541,7 @@ dependencies = [
  "concurrent-queue",
  "hermit-abi",
  "pin-project-lite",
- "rustix",
+ "rustix 1.1.3",
  "windows-sys 0.61.2",
 ]
 
@@ -6913,7 +7623,7 @@ checksum = 
"ada8f2932f28a27ee7b70dd6c1c39ea0675c55a36879ab92f3a715eaa1e63cfe"
 dependencies = [
  "anstyle",
  "difflib",
- "float-cmp",
+ "float-cmp 0.10.0",
  "normalize-line-endings",
  "predicates-core",
  "regex",
@@ -6997,6 +7707,29 @@ dependencies = [
  "version_check",
 ]
 
+[[package]]
+name = "proc-macro-rules"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "07c277e4e643ef00c1233393c673f655e3672cf7eb3ba08a00bdd0ea59139b5f"
+dependencies = [
+ "proc-macro-rules-macros",
+ "proc-macro2",
+ "syn 2.0.115",
+]
+
+[[package]]
+name = "proc-macro-rules-macros"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "207fffb0fe655d1d47f6af98cc2793405e85929bdbc420d685554ff07be27ac7"
+dependencies = [
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.115",
+]
+
 [[package]]
 name = "proc-macro2"
 version = "1.0.106"
@@ -7019,6 +7752,25 @@ dependencies = [
  "yansi",
 ]
 
+[[package]]
+name = "profiling"
+version = "1.0.17"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773"
+dependencies = [
+ "profiling-procmacros",
+]
+
+[[package]]
+name = "profiling-procmacros"
+version = "1.0.17"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b"
+dependencies = [
+ "quote",
+ "syn 2.0.115",
+]
+
 [[package]]
 name = "prometheus-client"
 version = "0.24.0"
@@ -7133,6 +7885,24 @@ dependencies = [
  "syn 1.0.109",
 ]
 
+[[package]]
+name = "pxfm"
+version = "0.1.27"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "7186d3822593aa4393561d186d1393b3923e9d6163d3fbfd6e825e3e6cf3e6a8"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "qoi"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001"
+dependencies = [
+ "bytemuck",
+]
+
 [[package]]
 name = "quad-rand"
 version = "0.2.3"
@@ -7154,6 +7924,12 @@ dependencies = [
  "winapi",
 ]
 
+[[package]]
+name = "quick-error"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
+
 [[package]]
 name = "quick-xml"
 version = "0.37.5"
@@ -7329,12 +8105,62 @@ source = 
"registry+https://github.com/rust-lang/crates.io-index";
 checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba"
 
 [[package]]
-name = "rand_xoshiro"
-version = "0.8.0"
+name = "rand_xoshiro"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "1f0b2cc7bfeef8f0320ca45f88b00157a03c67137022d59393614352d6bf4312"
+dependencies = [
+ "rand_core 0.10.0",
+]
+
+[[package]]
+name = "rav1e"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "43b6dd56e85d9483277cde964fd1bdb0428de4fec5ebba7540995639a21cb32b"
+dependencies = [
+ "aligned-vec",
+ "arbitrary",
+ "arg_enum_proc_macro",
+ "arrayvec",
+ "av-scenechange",
+ "av1-grain",
+ "bitstream-io",
+ "built",
+ "cfg-if",
+ "interpolate_name",
+ "itertools 0.14.0",
+ "libc",
+ "libfuzzer-sys",
+ "log",
+ "maybe-rayon",
+ "new_debug_unreachable",
+ "noop_proc_macro",
+ "num-derive",
+ "num-traits",
+ "paste",
+ "profiling",
+ "rand 0.9.2",
+ "rand_chacha 0.9.0",
+ "simd_helpers",
+ "thiserror 2.0.18",
+ "v_frame",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "ravif"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "1f0b2cc7bfeef8f0320ca45f88b00157a03c67137022d59393614352d6bf4312"
+checksum = "ef69c1990ceef18a116855938e74793a5f7496ee907562bd0857b6ac734ab285"
 dependencies = [
- "rand_core 0.10.0",
+ "avif-serialize",
+ "imgref",
+ "loop9",
+ "quick-error",
+ "rav1e",
+ "rayon",
+ "rgb",
 ]
 
 [[package]]
@@ -7596,6 +8422,23 @@ dependencies = [
  "tracing",
 ]
 
+[[package]]
+name = "resvg"
+version = "0.45.1"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "a8928798c0a55e03c9ca6c4c6846f76377427d2c1e1f7e6de3c06ae57942df43"
+dependencies = [
+ "gif 0.13.3",
+ "image-webp",
+ "log",
+ "pico-args",
+ "rgb",
+ "svgtypes",
+ "tiny-skia",
+ "usvg",
+ "zune-jpeg 0.4.21",
+]
+
 [[package]]
 name = "retry-policies"
 version = "0.5.1"
@@ -7615,6 +8458,15 @@ dependencies = [
  "subtle",
 ]
 
+[[package]]
+name = "rgb"
+version = "0.8.52"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce"
+dependencies = [
+ "bytemuck",
+]
+
 [[package]]
 name = "ring"
 version = "0.17.14"
@@ -7679,7 +8531,7 @@ dependencies = [
  "http 1.4.0",
  "http-body",
  "http-body-util",
- "pastey",
+ "pastey 0.2.1",
  "pin-project-lite",
  "rand 0.9.2",
  "reqwest",
@@ -7754,6 +8606,12 @@ version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "afab94fb28594581f62d981211a9a4d53cc8130bbcbbb89a0440d9b8e81a7746"
 
+[[package]]
+name = "roxmltree"
+version = "0.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97"
+
 [[package]]
 name = "rsa"
 version = "0.9.10"
@@ -7858,6 +8716,19 @@ dependencies = [
  "nom 7.1.3",
 ]
 
+[[package]]
+name = "rustix"
+version = "0.38.44"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
+dependencies = [
+ "bitflags 2.10.0",
+ "errno",
+ "libc",
+ "linux-raw-sys 0.4.15",
+ "windows-sys 0.52.0",
+]
+
 [[package]]
 name = "rustix"
 version = "1.1.3"
@@ -7867,7 +8738,7 @@ dependencies = [
  "bitflags 2.10.0",
  "errno",
  "libc",
- "linux-raw-sys",
+ "linux-raw-sys 0.11.0",
  "windows-sys 0.61.2",
 ]
 
@@ -7963,6 +8834,24 @@ version = "1.0.22"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
 
+[[package]]
+name = "rustybuzz"
+version = "0.20.1"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "fd3c7c96f8a08ee34eff8857b11b49b07d71d1c3f4e88f8a88d4c9e9f90b1702"
+dependencies = [
+ "bitflags 2.10.0",
+ "bytemuck",
+ "core_maths",
+ "log",
+ "smallvec",
+ "ttf-parser",
+ "unicode-bidi-mirroring",
+ "unicode-ccc",
+ "unicode-properties",
+ "unicode-script",
+]
+
 [[package]]
 name = "ryu"
 version = "1.0.23"
@@ -8218,6 +9107,7 @@ version = "1.0.149"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
 dependencies = [
+ "indexmap 2.13.0",
  "itoa",
  "memchr",
  "serde",
@@ -8277,6 +9167,20 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "serde_v8"
+version = "0.260.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "d4f284b4d521591b17ddee01aff830dd005a04476f7862aca9298c038d00fb7e"
+dependencies = [
+ "deno_error",
+ "num-bigint",
+ "serde",
+ "smallvec",
+ "thiserror 2.0.18",
+ "v8",
+]
+
 [[package]]
 name = "serde_with"
 version = "3.16.1"
@@ -8519,6 +9423,15 @@ dependencies = [
  "value-trait",
 ]
 
+[[package]]
+name = "simd_helpers"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6"
+dependencies = [
+ "quote",
+]
+
 [[package]]
 name = "simdutf8"
 version = "0.1.5"
@@ -8537,6 +9450,15 @@ dependencies = [
  "time",
 ]
 
+[[package]]
+name = "simplecss"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "7a9c6883ca9c3c7c90e888de77b7a5c849c779d25d74a1269b0218b14e8b136c"
+dependencies = [
+ "log",
+]
+
 [[package]]
 name = "simulator"
 version = "0.1.0"
@@ -8569,6 +9491,15 @@ version = "0.4.12"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
 
+[[package]]
+name = "slotmap"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a"
+dependencies = [
+ "version_check",
+]
+
 [[package]]
 name = "smallvec"
 version = "1.15.1"
@@ -8642,6 +9573,24 @@ dependencies = [
  "windows-sys 0.60.2",
 ]
 
+[[package]]
+name = "sourcemap"
+version = "9.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "314d62a489431668f719ada776ca1d49b924db951b7450f8974c9ae51ab05ad7"
+dependencies = [
+ "base64-simd",
+ "bitvec",
+ "data-encoding",
+ "debugid",
+ "if_chain",
+ "rustc-hash",
+ "serde",
+ "serde_json",
+ "unicode-id-start",
+ "url",
+]
+
 [[package]]
 name = "spin"
 version = "0.9.8"
@@ -8901,6 +9850,27 @@ dependencies = [
  "toml 0.8.23",
 ]
 
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
+name = "strict-num"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731"
+dependencies = [
+ "float-cmp 0.9.0",
+]
+
+[[package]]
+name = "stringcase"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "72abeda133c49d7bddece6c154728f83eec8172380c80ab7096da9487e20d27c"
+
 [[package]]
 name = "stringprep"
 version = "0.1.5"
@@ -8968,6 +9938,16 @@ version = "2.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
 
+[[package]]
+name = "svgtypes"
+version = "0.15.3"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "68c7541fff44b35860c1a7a47a7cadf3e4a304c457b58f9870d9706ece028afc"
+dependencies = [
+ "kurbo",
+ "siphasher",
+]
+
 [[package]]
 name = "syn"
 version = "1.0.109"
@@ -9052,6 +10032,26 @@ dependencies = [
  "syn 2.0.115",
 ]
 
+[[package]]
+name = "sys_traits"
+version = "0.1.24"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "5410f31d223892c1ce7a098da845c99d023b4c7f18632bc8f09e60dfae3cbb75"
+dependencies = [
+ "sys_traits_macros",
+]
+
+[[package]]
+name = "sys_traits_macros"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "181f22127402abcf8ee5c83ccd5b408933fec36a6095cf82cda545634692657e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.115",
+]
+
 [[package]]
 name = "sysinfo"
 version = "0.37.2"
@@ -9112,7 +10112,7 @@ dependencies = [
  "fastrand",
  "getrandom 0.4.1",
  "once_cell",
- "rustix",
+ "rustix 1.1.3",
  "windows-sys 0.61.2",
 ]
 
@@ -9122,7 +10122,7 @@ version = "0.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "60b8cb979cb11c32ce1603f8137b22262a9d131aaa5c37b5678025f22b8becd0"
 dependencies = [
- "rustix",
+ "rustix 1.1.3",
  "windows-sys 0.60.2",
 ]
 
@@ -9281,6 +10281,20 @@ dependencies = [
  "ordered-float 2.10.1",
 ]
 
+[[package]]
+name = "tiff"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "af9605de7fee8d9551863fd692cce7637f548dbd9db9180fcc07ccc6d26c336f"
+dependencies = [
+ "fax",
+ "flate2",
+ "half",
+ "quick-error",
+ "weezl",
+ "zune-jpeg 0.4.21",
+]
+
 [[package]]
 name = "time"
 version = "0.3.47"
@@ -9323,6 +10337,32 @@ dependencies = [
  "crunchy",
 ]
 
+[[package]]
+name = "tiny-skia"
+version = "0.11.4"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab"
+dependencies = [
+ "arrayref",
+ "arrayvec",
+ "bytemuck",
+ "cfg-if",
+ "log",
+ "png 0.17.16",
+ "tiny-skia-path",
+]
+
+[[package]]
+name = "tiny-skia-path"
+version = "0.11.4"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93"
+dependencies = [
+ "arrayref",
+ "bytemuck",
+ "strict-num",
+]
+
 [[package]]
 name = "tinystr"
 version = "0.8.2"
@@ -9771,6 +10811,15 @@ version = "0.2.5"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
 
+[[package]]
+name = "ttf-parser"
+version = "0.25.1"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31"
+dependencies = [
+ "core_maths",
+]
+
 [[package]]
 name = "tungstenite"
 version = "0.28.0"
@@ -9888,6 +10937,24 @@ version = "0.3.18"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5"
 
+[[package]]
+name = "unicode-bidi-mirroring"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "5dfa6e8c60bb66d49db113e0125ee8711b7647b5579dc7f5f19c42357ed039fe"
+
+[[package]]
+name = "unicode-ccc"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "ce61d488bcdc9bc8b5d1772c404828b17fc481c0a582b5581e95fb233aef503e"
+
+[[package]]
+name = "unicode-id-start"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "81b79ad29b5e19de4260020f8919b443b2ef0277d242ce532ec7b7a2cc8b6007"
+
 [[package]]
 name = "unicode-ident"
 version = "1.0.23"
@@ -9915,12 +10982,24 @@ version = "0.1.4"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d"
 
+[[package]]
+name = "unicode-script"
+version = "0.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "383ad40bb927465ec0ce7720e033cb4ca06912855fc35db31b5755d0de75b1ee"
+
 [[package]]
 name = "unicode-segmentation"
 version = "1.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
 
+[[package]]
+name = "unicode-vo"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94"
+
 [[package]]
 name = "unicode-width"
 version = "0.1.14"
@@ -10009,6 +11088,33 @@ version = "2.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
 
+[[package]]
+name = "usvg"
+version = "0.45.1"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "80be9b06fbae3b8b303400ab20778c80bbaf338f563afe567cf3c9eea17b47ef"
+dependencies = [
+ "base64 0.22.1",
+ "data-url",
+ "flate2",
+ "fontdb",
+ "imagesize",
+ "kurbo",
+ "log",
+ "pico-args",
+ "roxmltree",
+ "rustybuzz",
+ "simplecss",
+ "siphasher",
+ "strict-num",
+ "svgtypes",
+ "tiny-skia-path",
+ "unicode-bidi",
+ "unicode-script",
+ "unicode-vo",
+ "xmlwriter",
+]
+
 [[package]]
 name = "utf-8"
 version = "0.7.6"
@@ -10047,6 +11153,33 @@ dependencies = [
  "zerocopy",
 ]
 
+[[package]]
+name = "v8"
+version = "137.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "33995a1fee055ff743281cde33a41f0d618ee0bdbe8bdf6859e11864499c2595"
+dependencies = [
+ "bindgen",
+ "bitflags 2.10.0",
+ "fslock",
+ "gzip-header",
+ "home",
+ "miniz_oxide",
+ "paste",
+ "which",
+]
+
+[[package]]
+name = "v_frame"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "666b7727c8875d6ab5db9533418d7c764233ac9c0cff1d469aec8fa127597be2"
+dependencies = [
+ "aligned-vec",
+ "num-traits",
+ "wasm-bindgen",
+]
+
 [[package]]
 name = "v_htmlescape"
 version = "0.15.8"
@@ -10065,7 +11198,7 @@ version = "0.12.1"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "8e80f0c733af0720a501b3905d22e2f97662d8eacfe082a75ed7ffb5ab08cb59"
 dependencies = [
- "float-cmp",
+ "float-cmp 0.10.0",
  "halfbrown",
  "itoa",
  "ryu",
@@ -10132,6 +11265,12 @@ version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
 
+[[package]]
+name = "vsimd"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64"
+
 [[package]]
 name = "wait-timeout"
 version = "0.2.1"
@@ -10284,6 +11423,16 @@ dependencies = [
  "web-sys",
 ]
 
+[[package]]
+name = "wasm_dep_analyzer"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "e51cf5f08b357e64cd7642ab4bbeb11aecab9e15520692129624fb9908b8df2c"
+dependencies = [
+ "deno_error",
+ "thiserror 2.0.18",
+]
+
 [[package]]
 name = "wasmparser"
 version = "0.244.0"
@@ -10358,6 +11507,24 @@ dependencies = [
  "rustls-pki-types",
 ]
 
+[[package]]
+name = "weezl"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88"
+
+[[package]]
+name = "which"
+version = "6.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "b4ee928febd44d98f2f459a4a79bd4d928591333a494a10a868418ac1b39cf1f"
+dependencies = [
+ "either",
+ "home",
+ "rustix 0.38.44",
+ "winsafe",
+]
+
 [[package]]
 name = "whoami"
 version = "1.6.1"
@@ -10919,6 +12086,12 @@ dependencies = [
  "memchr",
 ]
 
+[[package]]
+name = "winsafe"
+version = "0.0.19"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904"
+
 [[package]]
 name = "wit-bindgen"
 version = "0.51.0"
@@ -11047,9 +12220,21 @@ source = 
"registry+https://github.com/rust-lang/crates.io-index";
 checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156"
 dependencies = [
  "libc",
- "rustix",
+ "rustix 1.1.3",
 ]
 
+[[package]]
+name = "xmlwriter"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9"
+
+[[package]]
+name = "y4m"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "7a5a4b21e1a62b67a2970e6831bc091d7b87e119e7f9791aef9702e3bef04448"
+
 [[package]]
 name = "yansi"
 version = "1.0.1"
@@ -11317,3 +12502,42 @@ dependencies = [
  "cc",
  "pkg-config",
 ]
+
+[[package]]
+name = "zune-core"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a"
+
+[[package]]
+name = "zune-core"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9"
+
+[[package]]
+name = "zune-inflate"
+version = "0.2.54"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02"
+dependencies = [
+ "simd-adler32",
+]
+
+[[package]]
+name = "zune-jpeg"
+version = "0.4.21"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713"
+dependencies = [
+ "zune-core 0.4.12",
+]
+
+[[package]]
+name = "zune-jpeg"
+version = "0.5.12"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "410e9ecef634c709e3831c2cfdb8d9c32164fae1c67496d5b68fff728eec37fe"
+dependencies = [
+ "zune-core 0.5.1",
+]
diff --git a/DEPENDENCIES.md b/DEPENDENCIES.md
index 537fcd7b3..acb2ba8c5 100644
--- a/DEPENDENCIES.md
+++ b/DEPENDENCIES.md
@@ -19,6 +19,8 @@ aes-gcm: 0.10.3, "Apache-2.0 OR MIT",
 ahash: 0.7.8, "Apache-2.0 OR MIT",
 ahash: 0.8.12, "Apache-2.0 OR MIT",
 aho-corasick: 1.1.4, "MIT OR Unlicense",
+aligned: 0.4.3, "Apache-2.0 OR MIT",
+aligned-vec: 0.6.4, "MIT",
 alloc-no-stdlib: 2.0.4, "BSD-3-Clause",
 alloc-stdlib: 0.2.2, "BSD-3-Clause",
 allocator-api2: 0.2.21, "Apache-2.0 OR MIT",
@@ -30,7 +32,9 @@ anstyle-query: 1.1.5, "Apache-2.0 OR MIT",
 anstyle-wincon: 3.0.11, "Apache-2.0 OR MIT",
 anyhow: 1.0.101, "Apache-2.0 OR MIT",
 apache-avro: 0.21.0, "Apache-2.0",
+arbitrary: 1.4.2, "Apache-2.0 OR MIT",
 arc-swap: 1.8.1, "Apache-2.0 OR MIT",
+arg_enum_proc_macro: 0.3.4, "MIT",
 argon2: 0.5.3, "Apache-2.0 OR MIT",
 array-init: 2.1.0, "Apache-2.0 OR MIT",
 arrayref: 0.3.9, "BSD-2-Clause",
@@ -47,6 +51,7 @@ arrow-schema: 57.3.0, "Apache-2.0",
 arrow-select: 57.3.0, "Apache-2.0",
 arrow-string: 57.3.0, "Apache-2.0",
 as-any: 0.3.2, "Apache-2.0 OR MIT",
+as-slice: 0.2.1, "Apache-2.0 OR MIT",
 asn1-rs: 0.7.1, "Apache-2.0 OR MIT",
 asn1-rs-derive: 0.6.0, "Apache-2.0 OR MIT",
 asn1-rs-impl: 0.2.0, "Apache-2.0 OR MIT",
@@ -71,30 +76,38 @@ atomic-polyfill: 1.0.3, "Apache-2.0 OR MIT",
 atomic-waker: 1.1.2, "Apache-2.0 OR MIT",
 autocfg: 1.5.0, "Apache-2.0 OR MIT",
 autotools: 0.2.7, "MIT",
+av-scenechange: 0.14.1, "MIT",
+av1-grain: 0.2.5, "BSD-2-Clause",
+avif-serialize: 0.8.8, "BSD-3-Clause",
 aws-lc-rs: 1.15.4, "(Apache-2.0 OR ISC) AND ISC",
 aws-lc-sys: 0.37.1, "(Apache-2.0 OR ISC) AND ISC AND OpenSSL",
 axum: 0.8.8, "MIT",
 axum-core: 0.5.6, "MIT",
 axum-macros: 0.5.0, "MIT",
 axum-server: 0.8.0, "MIT",
+az: 1.3.0, "Apache-2.0 OR MIT",
 backon: 1.6.0, "Apache-2.0",
 base16ct: 0.2.0, "Apache-2.0 OR MIT",
 base64: 0.21.7, "Apache-2.0 OR MIT",
 base64: 0.22.1, "Apache-2.0 OR MIT",
+base64-simd: 0.8.0, "MIT",
 base64ct: 1.8.3, "Apache-2.0 OR MIT",
 bdd: 0.0.1, "Apache-2.0",
 beef: 0.5.2, "Apache-2.0 OR MIT",
-bench-dashboard-frontend: 0.5.1-edge.1, "Apache-2.0",
+bench-dashboard-frontend: 0.5.2-edge.1, "Apache-2.0",
 bench-dashboard-shared: 0.1.0, "Apache-2.0",
 bench-report: 0.2.3-edge.1, "Apache-2.0",
 bench-runner: 0.1.0, "Apache-2.0",
 bigdecimal: 0.4.10, "Apache-2.0 OR MIT",
 bimap: 0.6.3, "Apache-2.0 OR MIT",
 bincode: 1.3.3, "MIT",
+bindgen: 0.71.1, "BSD-3-Clause",
 bit-set: 0.8.0, "Apache-2.0 OR MIT",
 bit-vec: 0.8.0, "Apache-2.0 OR MIT",
+bit_field: 0.10.3, "Apache-2.0 OR MIT",
 bitflags: 1.3.2, "Apache-2.0 OR MIT",
 bitflags: 2.10.0, "Apache-2.0 OR MIT",
+bitstream-io: 4.9.0, "Apache-2.0 OR MIT",
 bitvec: 1.0.1, "MIT",
 blake2: 0.10.6, "Apache-2.0 OR MIT",
 blake3: 1.8.3, "Apache-2.0 OR Apache-2.0 WITH LLVM-exception OR CC0-1.0",
@@ -110,6 +123,7 @@ borsh-derive: 1.6.0, "Apache-2.0",
 brotli: 8.0.2, "BSD-3-Clause AND MIT",
 brotli-decompressor: 5.0.0, "BSD-3-Clause OR MIT",
 bstr: 1.12.1, "Apache-2.0 OR MIT",
+built: 0.8.0, "MIT",
 bumpalo: 3.19.1, "Apache-2.0 OR MIT",
 byte-unit: 5.2.0, "MIT",
 bytecheck: 0.6.12, "MIT",
@@ -117,14 +131,18 @@ bytecheck_derive: 0.6.12, "MIT",
 bytecount: 0.6.9, "Apache-2.0 OR MIT",
 bytemuck: 1.25.0, "Apache-2.0 OR MIT OR Zlib",
 byteorder: 1.5.0, "MIT OR Unlicense",
+byteorder-lite: 0.1.0, "MIT OR Unlicense",
 bytes: 1.11.1, "MIT",
 bytestring: 1.5.0, "Apache-2.0 OR MIT",
 bzip2: 0.6.1, "Apache-2.0 OR MIT",
 camino: 1.2.2, "Apache-2.0 OR MIT",
+capacity_builder: 0.5.0, "MIT",
+capacity_builder_macros: 0.3.0, "MIT",
 cargo-platform: 0.3.2, "Apache-2.0 OR MIT",
 cargo_metadata: 0.23.1, "MIT",
 cc: 1.2.55, "Apache-2.0 OR MIT",
 cesu8: 1.1.0, "Apache-2.0 OR MIT",
+cexpr: 0.6.0, "Apache-2.0 OR MIT",
 cfg-if: 1.0.4, "Apache-2.0 OR MIT",
 cfg_aliases: 0.2.1, "MIT",
 chacha20: 0.10.0, "Apache-2.0 OR MIT",
@@ -132,6 +150,7 @@ charming: 0.6.0, "Apache-2.0 OR MIT",
 charming_macros: 0.1.0, "Apache-2.0 OR MIT",
 chrono: 0.4.43, "Apache-2.0 OR MIT",
 cipher: 0.4.4, "Apache-2.0 OR MIT",
+clang-sys: 1.8.1, "Apache-2.0",
 clap: 4.5.58, "Apache-2.0 OR MIT",
 clap_builder: 4.5.58, "Apache-2.0 OR MIT",
 clap_complete: 4.5.66, "Apache-2.0 OR MIT",
@@ -140,6 +159,7 @@ clap_lex: 1.0.0, "Apache-2.0 OR MIT",
 clock: 0.1.0, "N/A",
 cmake: 0.1.57, "Apache-2.0 OR MIT",
 cobs: 0.3.0, "Apache-2.0 OR MIT",
+color_quant: 1.1.0, "MIT",
 colorchoice: 1.0.4, "Apache-2.0 OR MIT",
 colored: 3.1.1, "MPL-2.0",
 combine: 4.6.7, "MIT",
@@ -170,10 +190,13 @@ const-random-macro: 0.1.16, "Apache-2.0 OR MIT",
 constant_time_eq: 0.4.2, "Apache-2.0 OR CC0-1.0 OR MIT-0",
 convert_case: 0.6.0, "MIT",
 convert_case: 0.10.0, "MIT",
+cooked-waker: 5.0.0, "MPL-2.0",
 cookie: 0.16.2, "Apache-2.0 OR MIT",
 core-foundation: 0.10.1, "Apache-2.0 OR MIT",
 core-foundation-sys: 0.8.7, "Apache-2.0 OR MIT",
+core2: 0.4.0, "Apache-2.0 OR MIT",
 core_affinity: 0.8.3, "Apache-2.0 OR MIT",
+core_maths: 0.1.1, "MIT",
 cpufeatures: 0.2.17, "Apache-2.0 OR MIT",
 cpufeatures: 0.3.0, "Apache-2.0 OR MIT",
 crc: 3.4.0, "Apache-2.0 OR MIT",
@@ -215,8 +238,17 @@ darling_macro: 0.21.3, "MIT",
 darling_macro: 0.23.0, "MIT",
 dashmap: 6.1.0, "MIT",
 data-encoding: 2.10.0, "MIT",
+data-url: 0.3.2, "Apache-2.0 OR MIT",
 dbus: 0.9.10, "Apache-2.0 OR MIT",
 dbus-secret-service: 4.1.0, "Apache-2.0 OR MIT",
+debugid: 0.8.0, "Apache-2.0",
+deno_core: 0.351.0, "MIT",
+deno_core_icudata: 0.74.0, "MIT",
+deno_error: 0.6.1, "MIT",
+deno_error_macro: 0.6.1, "MIT",
+deno_ops: 0.227.0, "MIT",
+deno_path_util: 0.4.0, "MIT",
+deno_unsync: 0.4.4, "MIT",
 der: 0.7.10, "Apache-2.0 OR MIT",
 der-parser: 10.0.0, "Apache-2.0 OR MIT",
 deranged: 0.5.6, "Apache-2.0 OR MIT",
@@ -259,6 +291,8 @@ encoding_rs: 0.8.35, "(Apache-2.0 OR MIT) AND BSD-3-Clause",
 enum_dispatch: 0.3.13, "Apache-2.0 OR MIT",
 enumset: 1.1.10, "Apache-2.0 OR MIT",
 enumset_derive: 0.14.0, "Apache-2.0 OR MIT",
+equator: 0.4.2, "MIT",
+equator-macro: 0.4.2, "MIT",
 equivalent: 1.0.2, "Apache-2.0 OR MIT",
 err_trail: 0.11.0, "Apache-2.0",
 errno: 0.3.14, "Apache-2.0 OR MIT",
@@ -266,14 +300,19 @@ error_set: 0.9.1, "Apache-2.0",
 error_set_impl: 0.9.1, "Apache-2.0",
 etcetera: 0.8.0, "Apache-2.0 OR MIT",
 etcetera: 0.11.0, "Apache-2.0 OR MIT",
+euclid: 0.22.13, "Apache-2.0 OR MIT",
 event-listener: 5.4.1, "Apache-2.0 OR MIT",
 event-listener-strategy: 0.5.4, "Apache-2.0 OR MIT",
 expect-test: 1.5.1, "Apache-2.0 OR MIT",
+exr: 1.74.0, "BSD-3-Clause",
 ext-trait: 1.0.1, "Apache-2.0 OR MIT OR Zlib",
 ext-trait-proc_macros: 1.0.1, "Apache-2.0 OR MIT OR Zlib",
 extension-traits: 1.0.1, "Apache-2.0 OR MIT OR Zlib",
 fastbloom: 0.14.1, "Apache-2.0 OR MIT",
 fastrand: 2.3.0, "Apache-2.0 OR MIT",
+fax: 0.2.6, "MIT",
+fax_derive: 0.2.0, "MIT",
+fdeflate: 0.3.7, "Apache-2.0 OR MIT",
 ferroid: 0.8.9, "Apache-2.0 OR MIT",
 ff: 0.13.1, "Apache-2.0 OR MIT",
 fiat-crypto: 0.2.9, "Apache-2.0 OR BSD-1-Clause OR MIT",
@@ -284,12 +323,15 @@ filetime: 0.2.27, "Apache-2.0 OR MIT",
 find-msvc-tools: 0.1.9, "Apache-2.0 OR MIT",
 flatbuffers: 25.12.19, "Apache-2.0",
 flate2: 1.1.9, "Apache-2.0 OR MIT",
+float-cmp: 0.9.0, "MIT",
 float-cmp: 0.10.0, "MIT",
 flume: 0.11.1, "Apache-2.0 OR MIT",
 flume: 0.12.0, "Apache-2.0 OR MIT",
 fnv: 1.0.7, "Apache-2.0 OR MIT",
 foldhash: 0.1.5, "Zlib",
 foldhash: 0.2.0, "Zlib",
+fontconfig-parser: 0.5.8, "MIT",
+fontdb: 0.23.0, "MIT",
 foreign-types: 0.3.2, "Apache-2.0 OR MIT",
 foreign-types-shared: 0.1.1, "Apache-2.0 OR MIT",
 form_urlencoded: 1.2.2, "Apache-2.0 OR MIT",
@@ -298,6 +340,7 @@ fs-err: 3.3.0, "Apache-2.0 OR MIT",
 fs2: 0.4.3, "Apache-2.0 OR MIT",
 fs_extra: 1.3.0, "MIT",
 fsevent-sys: 4.1.0, "MIT",
+fslock: 0.2.1, "MIT",
 funty: 2.0.0, "MIT",
 futures: 0.3.31, "Apache-2.0 OR MIT",
 futures-channel: 0.3.31, "Apache-2.0 OR MIT",
@@ -319,7 +362,10 @@ getrandom: 0.3.4, "Apache-2.0 OR MIT",
 getrandom: 0.4.1, "Apache-2.0 OR MIT",
 ghash: 0.5.1, "Apache-2.0 OR MIT",
 gherkin: 0.15.0, "Apache-2.0 OR MIT",
+gif: 0.13.3, "Apache-2.0 OR MIT",
+gif: 0.14.0, "Apache-2.0 OR MIT",
 git2: 0.20.4, "Apache-2.0 OR MIT",
+glob: 0.3.3, "Apache-2.0 OR MIT",
 globset: 0.4.18, "MIT OR Unlicense",
 globwalk: 0.9.1, "MIT",
 gloo: 0.11.0, "Apache-2.0 OR MIT",
@@ -337,6 +383,7 @@ gloo-worker: 0.5.0, "Apache-2.0 OR MIT",
 gloo-worker-macros: 0.1.0, "Apache-2.0 OR MIT",
 governor: 0.10.4, "MIT",
 group: 0.13.0, "Apache-2.0 OR MIT",
+gzip-header: 1.0.0, "Apache-2.0 OR MIT",
 h2: 0.3.27, "MIT",
 h2: 0.4.13, "MIT",
 half: 2.7.1, "Apache-2.0 OR MIT",
@@ -390,9 +437,10 @@ id-arena: 2.3.0, "Apache-2.0 OR MIT",
 ident_case: 1.0.1, "Apache-2.0 OR MIT",
 idna: 1.1.0, "Apache-2.0 OR MIT",
 idna_adapter: 1.2.1, "Apache-2.0 OR MIT",
+if_chain: 1.0.3, "Apache-2.0 OR MIT",
 iggy: 0.9.0, "Apache-2.0",
 iggy-bench: 0.4.0, "Apache-2.0",
-iggy-bench-dashboard-server: 0.6.1-edge.1, "Apache-2.0",
+iggy-bench-dashboard-server: 0.6.2-edge.1, "Apache-2.0",
 iggy-cli: 0.11.0, "Apache-2.0",
 iggy-connectors: 0.3.0, "Apache-2.0",
 iggy-mcp: 0.3.0, "Apache-2.0",
@@ -409,6 +457,10 @@ iggy_connector_sdk: 0.2.0, "Apache-2.0",
 iggy_connector_stdout_sink: 0.3.0, "Apache-2.0",
 iggy_examples: 0.0.6, "Apache-2.0",
 ignore: 0.4.25, "MIT OR Unlicense",
+image: 0.25.9, "Apache-2.0 OR MIT",
+image-webp: 0.2.4, "Apache-2.0 OR MIT",
+imagesize: 0.13.0, "MIT",
+imgref: 1.12.0, "Apache-2.0 OR CC0-1.0",
 impl-more: 0.1.9, "Apache-2.0 OR MIT",
 implicit-clone: 0.6.0, "Apache-2.0 OR MIT",
 implicit-clone-derive: 0.1.2, "Apache-2.0 OR MIT",
@@ -421,6 +473,7 @@ inotify-sys: 0.1.5, "ISC",
 inout: 0.1.4, "Apache-2.0 OR MIT",
 integer-encoding: 3.0.4, "MIT",
 integration: 0.0.1, "Apache-2.0",
+interpolate_name: 0.2.4, "MIT",
 inventory: 0.3.21, "Apache-2.0 OR MIT",
 io-uring: 0.7.11, "Apache-2.0 OR MIT",
 io_uring_buf_ring: 0.2.3, "MIT",
@@ -445,9 +498,11 @@ keccak: 0.1.6, "Apache-2.0 OR MIT",
 keyring: 3.6.3, "Apache-2.0 OR MIT",
 kqueue: 1.1.1, "MIT",
 kqueue-sys: 1.0.4, "MIT",
+kurbo: 0.11.3, "Apache-2.0 OR MIT",
 language-tags: 0.3.2, "Apache-2.0 OR MIT",
 lazy_static: 1.5.0, "Apache-2.0 OR MIT",
 leb128fmt: 0.1.0, "Apache-2.0 OR MIT",
+lebe: 0.5.3, "BSD-3-Clause",
 left-right: 0.11.7, "Apache-2.0 OR MIT",
 lending-iterator: 0.1.7, "Apache-2.0 OR MIT OR Zlib",
 lending-iterator-proc_macros: 0.1.7, "Apache-2.0 OR MIT OR Zlib",
@@ -460,7 +515,9 @@ lexical-write-integer: 1.0.6, "Apache-2.0 OR MIT",
 libbz2-rs-sys: 0.2.2, "bzip2-1.0.6",
 libc: 0.2.180, "Apache-2.0 OR MIT",
 libdbus-sys: 0.2.7, "Apache-2.0 OR MIT",
+libfuzzer-sys: 0.4.12, "(Apache-2.0 OR MIT) AND NCSA",
 libgit2-sys: 0.18.3+1.9.2, "Apache-2.0 OR MIT",
+libloading: 0.8.9, "ISC",
 liblzma: 0.4.5, "Apache-2.0 OR MIT",
 liblzma-sys: 0.4.5, "Apache-2.0 OR MIT",
 libm: 0.2.16, "MIT",
@@ -469,6 +526,7 @@ libredox: 0.1.12, "MIT",
 libsqlite3-sys: 0.30.1, "MIT",
 libz-sys: 1.1.23, "Apache-2.0 OR MIT",
 linked-hash-map: 0.5.6, "Apache-2.0 OR MIT",
+linux-raw-sys: 0.4.15, "Apache-2.0 OR Apache-2.0 WITH LLVM-exception OR MIT",
 linux-raw-sys: 0.11.0, "Apache-2.0 OR Apache-2.0 WITH LLVM-exception OR MIT",
 litemap: 0.8.1, "Unicode-3.0",
 litrs: 1.0.0, "Apache-2.0 OR MIT",
@@ -480,14 +538,17 @@ logos: 0.15.1, "Apache-2.0 OR MIT",
 logos-codegen: 0.15.1, "Apache-2.0 OR MIT",
 logos-derive: 0.15.1, "Apache-2.0 OR MIT",
 loom: 0.7.2, "MIT",
+loop9: 0.1.5, "MIT",
 lru-slab: 0.1.2, "Apache-2.0 OR MIT OR Zlib",
 lz4_flex: 0.12.0, "MIT",
 macro_rules_attribute: 0.1.3, "MIT",
 macro_rules_attribute-proc_macro: 0.1.3, "MIT",
 matchers: 0.2.0, "MIT",
 matchit: 0.8.4, "BSD-3-Clause AND MIT",
+maybe-rayon: 0.1.1, "MIT",
 md-5: 0.10.6, "Apache-2.0 OR MIT",
 memchr: 2.8.0, "MIT OR Unlicense",
+memmap2: 0.9.10, "Apache-2.0 OR MIT",
 message_bus: 0.1.0, "Apache-2.0",
 metadata: 0.1.0, "Apache-2.0",
 miette: 7.6.0, "Apache-2.0",
@@ -501,14 +562,17 @@ mio: 1.1.1, "MIT",
 mockall: 0.14.0, "Apache-2.0 OR MIT",
 mockall_derive: 0.14.0, "Apache-2.0 OR MIT",
 moka: 0.12.13, "(Apache-2.0 OR MIT) AND Apache-2.0",
+moxcms: 0.7.11, "Apache-2.0 OR BSD-3-Clause",
 murmur3: 0.5.2, "Apache-2.0 OR MIT",
 never-say-never: 6.6.666, "Apache-2.0 OR MIT OR Zlib",
+new_debug_unreachable: 1.0.6, "MIT",
 nix: 0.31.1, "MIT",
 nom: 7.1.3, "MIT",
 nom: 8.0.0, "MIT",
 nom_locate: 5.0.0, "MIT",
 nonzero_ext: 0.3.0, "Apache-2.0",
 nonzero_lit: 0.1.2, "Apache-2.0 OR CC0-1.0 OR MIT",
+noop_proc_macro: 0.3.0, "MIT",
 normalize-line-endings: 0.3.0, "Apache-2.0",
 notify: 8.2.0, "CC0-1.0",
 notify-types: 2.1.0, "Apache-2.0 OR MIT",
@@ -521,6 +585,7 @@ num-bigint: 0.4.6, "Apache-2.0 OR MIT",
 num-bigint-dig: 0.8.6, "Apache-2.0 OR MIT",
 num-complex: 0.4.6, "Apache-2.0 OR MIT",
 num-conv: 0.2.0, "Apache-2.0 OR MIT",
+num-derive: 0.4.2, "Apache-2.0 OR MIT",
 num-integer: 0.1.46, "Apache-2.0 OR MIT",
 num-iter: 0.1.45, "Apache-2.0 OR MIT",
 num-modular: 0.6.1, "Apache-2.0",
@@ -556,6 +621,7 @@ ordered-float: 2.10.1, "MIT",
 ordered-float: 4.6.0, "MIT",
 ordered-multimap: 0.7.3, "MIT",
 os_pipe: 1.2.3, "MIT",
+outref: 0.5.2, "MIT",
 p256: 0.13.2, "Apache-2.0 OR MIT",
 p384: 0.13.1, "Apache-2.0 OR MIT",
 papaya: 0.2.3, "MIT",
@@ -569,6 +635,7 @@ partitions: 0.1.0, "Apache-2.0",
 passterm: 2.0.1, "BSD-3-Clause",
 password-hash: 0.5.0, "Apache-2.0 OR MIT",
 paste: 1.0.15, "Apache-2.0 OR MIT",
+pastey: 0.1.1, "Apache-2.0 OR MIT",
 pastey: 0.2.1, "Apache-2.0 OR MIT",
 pear: 0.2.9, "Apache-2.0 OR MIT",
 pear_codegen: 0.2.9, "Apache-2.0 OR MIT",
@@ -582,6 +649,7 @@ pest: 2.8.6, "Apache-2.0 OR MIT",
 pest_derive: 2.8.6, "Apache-2.0 OR MIT",
 pest_generator: 2.8.6, "Apache-2.0 OR MIT",
 pest_meta: 2.8.6, "Apache-2.0 OR MIT",
+pico-args: 0.5.0, "MIT",
 pin-project: 1.1.10, "Apache-2.0 OR MIT",
 pin-project-internal: 1.1.10, "Apache-2.0 OR MIT",
 pin-project-lite: 0.2.16, "Apache-2.0 OR MIT",
@@ -590,6 +658,8 @@ pinned: 0.1.0, "Apache-2.0 OR MIT",
 pkcs1: 0.7.5, "Apache-2.0 OR MIT",
 pkcs8: 0.10.2, "Apache-2.0 OR MIT",
 pkg-config: 0.3.32, "Apache-2.0 OR MIT",
+png: 0.17.16, "Apache-2.0 OR MIT",
+png: 0.18.0, "Apache-2.0 OR MIT",
 polling: 3.11.0, "Apache-2.0 OR MIT",
 polonius-the-crab: 0.2.1, "Apache-2.0 OR MIT OR Zlib",
 polyval: 0.6.2, "Apache-2.0 OR MIT",
@@ -608,8 +678,12 @@ proc-macro-crate: 1.3.1, "Apache-2.0 OR MIT",
 proc-macro-crate: 3.4.0, "Apache-2.0 OR MIT",
 proc-macro-error: 1.0.4, "Apache-2.0 OR MIT",
 proc-macro-error-attr: 1.0.4, "Apache-2.0 OR MIT",
+proc-macro-rules: 0.4.0, "Apache-2.0 OR MIT",
+proc-macro-rules-macros: 0.4.0, "Apache-2.0 OR MIT",
 proc-macro2: 1.0.106, "Apache-2.0 OR MIT",
 proc-macro2-diagnostics: 0.10.1, "Apache-2.0 OR MIT",
+profiling: 1.0.17, "Apache-2.0 OR MIT",
+profiling-procmacros: 1.0.17, "Apache-2.0 OR MIT",
 prometheus-client: 0.24.0, "Apache-2.0 OR MIT",
 prometheus-client-derive-encode: 0.5.0, "Apache-2.0 OR MIT",
 prost: 0.14.3, "Apache-2.0",
@@ -620,8 +694,11 @@ protox: 0.9.1, "Apache-2.0 OR MIT",
 protox-parse: 0.9.0, "Apache-2.0 OR MIT",
 ptr_meta: 0.1.4, "MIT",
 ptr_meta_derive: 0.1.4, "MIT",
+pxfm: 0.1.27, "Apache-2.0 OR BSD-3-Clause",
+qoi: 0.4.1, "Apache-2.0 OR MIT",
 quad-rand: 0.2.3, "MIT",
 quanta: 0.12.6, "MIT",
+quick-error: 2.0.1, "Apache-2.0 OR MIT",
 quick-xml: 0.37.5, "MIT",
 quick-xml: 0.38.4, "MIT",
 quinn: 0.11.9, "Apache-2.0 OR MIT",
@@ -639,6 +716,8 @@ rand_core: 0.6.4, "Apache-2.0 OR MIT",
 rand_core: 0.9.5, "Apache-2.0 OR MIT",
 rand_core: 0.10.0, "Apache-2.0 OR MIT",
 rand_xoshiro: 0.8.0, "Apache-2.0 OR MIT",
+rav1e: 0.8.1, "BSD-2-Clause",
+ravif: 0.12.0, "BSD-3-Clause",
 raw-cpuid: 11.6.0, "MIT",
 rayon: 1.11.0, "Apache-2.0 OR MIT",
 rayon-core: 1.13.0, "Apache-2.0 OR MIT",
@@ -658,8 +737,10 @@ reqwest: 0.12.28, "Apache-2.0 OR MIT",
 reqwest-middleware: 0.4.2, "Apache-2.0 OR MIT",
 reqwest-retry: 0.8.0, "Apache-2.0 OR MIT",
 reqwest-tracing: 0.5.8, "Apache-2.0 OR MIT",
+resvg: 0.45.1, "Apache-2.0 OR MIT",
 retry-policies: 0.5.1, "Apache-2.0 OR MIT",
 rfc6979: 0.4.0, "Apache-2.0 OR MIT",
+rgb: 0.8.52, "MIT",
 ring: 0.17.14, "Apache-2.0 AND ISC",
 ringbuffer: 0.16.0, "MIT",
 rkyv: 0.7.46, "MIT",
@@ -671,6 +752,7 @@ rmp-serde: 1.3.1, "MIT",
 roaring: 0.11.3, "Apache-2.0 OR MIT",
 rolling-file: 0.2.0, "Apache-2.0 OR MIT",
 route-recognizer: 0.3.1, "MIT",
+roxmltree: 0.20.0, "Apache-2.0 OR MIT",
 rsa: 0.9.10, "Apache-2.0 OR MIT",
 rust-embed: 8.11.0, "MIT",
 rust-embed-impl: 8.11.0, "MIT",
@@ -680,6 +762,7 @@ rust_decimal: 1.40.0, "MIT",
 rustc-hash: 2.1.1, "Apache-2.0 OR MIT",
 rustc_version: 0.4.1, "Apache-2.0 OR MIT",
 rusticata-macros: 4.1.0, "Apache-2.0 OR MIT",
+rustix: 0.38.44, "Apache-2.0 OR Apache-2.0 WITH LLVM-exception OR MIT",
 rustix: 1.1.3, "Apache-2.0 OR Apache-2.0 WITH LLVM-exception OR MIT",
 rustls: 0.23.36, "Apache-2.0 OR ISC OR MIT",
 rustls-native-certs: 0.8.3, "Apache-2.0 OR ISC OR MIT",
@@ -689,6 +772,7 @@ rustls-platform-verifier: 0.6.2, "Apache-2.0 OR MIT",
 rustls-platform-verifier-android: 0.1.1, "Apache-2.0 OR MIT",
 rustls-webpki: 0.103.9, "ISC",
 rustversion: 1.0.22, "Apache-2.0 OR MIT",
+rustybuzz: 0.20.1, "MIT",
 ryu: 1.0.23, "Apache-2.0 OR BSL-1.0",
 same-file: 1.0.6, "MIT OR Unlicense",
 scc: 2.4.0, "Apache-2.0",
@@ -721,6 +805,7 @@ serde_repr: 0.1.20, "Apache-2.0 OR MIT",
 serde_spanned: 0.6.9, "Apache-2.0 OR MIT",
 serde_spanned: 1.0.4, "Apache-2.0 OR MIT",
 serde_urlencoded: 0.7.1, "Apache-2.0 OR MIT",
+serde_v8: 0.260.0, "MIT",
 serde_with: 3.16.1, "Apache-2.0 OR MIT",
 serde_with_macros: 3.16.1, "Apache-2.0 OR MIT",
 serde_yaml_ng: 0.10.0, "MIT",
@@ -737,11 +822,14 @@ signal-hook-registry: 1.4.8, "Apache-2.0 OR MIT",
 signature: 2.2.0, "Apache-2.0 OR MIT",
 simd-adler32: 0.3.8, "MIT",
 simd-json: 0.17.0, "Apache-2.0 OR MIT",
+simd_helpers: 0.1.0, "MIT",
 simdutf8: 0.1.5, "Apache-2.0 OR MIT",
 simple_asn1: 0.6.3, "ISC",
+simplecss: 0.2.2, "Apache-2.0 OR MIT",
 simulator: 0.1.0, "N/A",
 siphasher: 1.0.2, "Apache-2.0 OR MIT",
 slab: 0.4.12, "MIT",
+slotmap: 1.0.7, "Zlib",
 smallvec: 1.15.1, "Apache-2.0 OR MIT",
 smart-default: 0.7.1, "MIT",
 smawk: 0.3.2, "MIT",
@@ -750,6 +838,7 @@ snafu-derive: 0.8.9, "Apache-2.0 OR MIT",
 snap: 1.1.1, "BSD-3-Clause",
 socket2: 0.5.10, "Apache-2.0 OR MIT",
 socket2: 0.6.2, "Apache-2.0 OR MIT",
+sourcemap: 9.3.2, "BSD-3-Clause",
 spin: 0.9.8, "MIT",
 spinning_top: 0.3.0, "Apache-2.0 OR MIT",
 spki: 0.7.3, "Apache-2.0 OR MIT",
@@ -763,6 +852,9 @@ sqlx-sqlite: 0.8.6, "Apache-2.0 OR MIT",
 sse-stream: 0.2.1, "Apache-2.0 OR MIT",
 stable_deref_trait: 1.2.1, "Apache-2.0 OR MIT",
 static-toml: 1.3.0, "MIT",
+static_assertions: 1.1.0, "Apache-2.0 OR MIT",
+strict-num: 0.1.1, "MIT",
+stringcase: 0.4.0, "MIT",
 stringprep: 0.1.5, "Apache-2.0 OR MIT",
 strsim: 0.11.1, "MIT",
 structmeta: 0.3.0, "Apache-2.0 OR MIT",
@@ -770,6 +862,7 @@ structmeta-derive: 0.3.0, "Apache-2.0 OR MIT",
 strum: 0.27.2, "MIT",
 strum_macros: 0.27.2, "MIT",
 subtle: 2.6.1, "BSD-3-Clause",
+svgtypes: 0.15.3, "Apache-2.0 OR MIT",
 syn: 1.0.109, "Apache-2.0 OR MIT",
 syn: 2.0.115, "Apache-2.0 OR MIT",
 sync_wrapper: 1.0.2, "Apache-2.0",
@@ -778,6 +871,8 @@ synstructure: 0.13.2, "MIT",
 synthez: 0.4.0, "BlueOak-1.0.0",
 synthez-codegen: 0.4.0, "BlueOak-1.0.0",
 synthez-core: 0.4.0, "BlueOak-1.0.0",
+sys_traits: 0.1.24, "MIT",
+sys_traits_macros: 0.1.0, "MIT",
 sysinfo: 0.37.2, "MIT",
 sysinfo: 0.38.1, "MIT",
 tagptr: 0.2.0, "Apache-2.0 OR MIT",
@@ -799,10 +894,13 @@ thiserror-impl: 1.0.69, "Apache-2.0 OR MIT",
 thiserror-impl: 2.0.18, "Apache-2.0 OR MIT",
 thread_local: 1.1.9, "Apache-2.0 OR MIT",
 thrift: 0.17.0, "Apache-2.0",
+tiff: 0.10.3, "MIT",
 time: 0.3.47, "Apache-2.0 OR MIT",
 time-core: 0.1.8, "Apache-2.0 OR MIT",
 time-macros: 0.2.27, "Apache-2.0 OR MIT",
 tiny-keccak: 2.0.2, "CC0-1.0",
+tiny-skia: 0.11.4, "BSD-3-Clause",
+tiny-skia-path: 0.11.4, "BSD-3-Clause",
 tinystr: 0.8.2, "Unicode-3.0",
 tinyvec: 1.10.0, "Apache-2.0 OR MIT OR Zlib",
 tinyvec_macros: 0.1.1, "Apache-2.0 OR MIT OR Zlib",
@@ -840,6 +938,7 @@ tracing-opentelemetry: 0.32.1, "MIT",
 tracing-subscriber: 0.3.22, "MIT",
 trait-variant: 0.1.2, "Apache-2.0 OR MIT",
 try-lock: 0.2.5, "MIT",
+ttf-parser: 0.25.1, "Apache-2.0 OR MIT",
 tungstenite: 0.28.0, "Apache-2.0 OR MIT",
 twox-hash: 2.1.2, "MIT",
 typed-builder: 0.20.1, "Apache-2.0 OR MIT",
@@ -853,11 +952,16 @@ ulid: 1.2.1, "MIT",
 uncased: 0.9.10, "Apache-2.0 OR MIT",
 unicase: 2.9.0, "Apache-2.0 OR MIT",
 unicode-bidi: 0.3.18, "Apache-2.0 OR MIT",
+unicode-bidi-mirroring: 0.4.0, "Apache-2.0 OR MIT",
+unicode-ccc: 0.4.0, "Apache-2.0 OR MIT",
+unicode-id-start: 1.4.0, "(Apache-2.0 OR MIT) AND Unicode-3.0",
 unicode-ident: 1.0.23, "(Apache-2.0 OR MIT) AND Unicode-3.0",
 unicode-linebreak: 0.1.5, "Apache-2.0",
 unicode-normalization: 0.1.25, "Apache-2.0 OR MIT",
 unicode-properties: 0.1.4, "Apache-2.0 OR MIT",
+unicode-script: 0.5.8, "Apache-2.0 OR MIT",
 unicode-segmentation: 1.12.0, "Apache-2.0 OR MIT",
+unicode-vo: 0.1.0, "Apache-2.0 OR MIT",
 unicode-width: 0.1.14, "Apache-2.0 OR MIT",
 unicode-width: 0.2.2, "Apache-2.0 OR MIT",
 unicode-xid: 0.2.6, "Apache-2.0 OR MIT",
@@ -868,11 +972,14 @@ ureq: 3.2.0, "Apache-2.0 OR MIT",
 ureq-proto: 0.5.3, "Apache-2.0 OR MIT",
 url: 2.5.8, "Apache-2.0 OR MIT",
 urlencoding: 2.1.3, "MIT",
+usvg: 0.45.1, "Apache-2.0 OR MIT",
 utf-8: 0.7.6, "Apache-2.0 OR MIT",
 utf8-width: 0.1.8, "MIT",
 utf8_iter: 1.0.4, "Apache-2.0 OR MIT",
 utf8parse: 0.2.2, "Apache-2.0 OR MIT",
 uuid: 1.20.0, "Apache-2.0 OR MIT",
+v8: 137.3.0, "MIT",
+v_frame: 0.3.9, "BSD-2-Clause",
 v_htmlescape: 0.15.8, "Apache-2.0 OR MIT",
 valuable: 0.1.1, "MIT",
 value-trait: 0.12.1, "Apache-2.0 OR MIT",
@@ -882,6 +989,7 @@ vergen-git2: 9.1.0, "Apache-2.0 OR MIT",
 vergen-lib: 9.1.0, "Apache-2.0 OR MIT",
 version_check: 0.9.5, "Apache-2.0 OR MIT",
 void: 1.0.2, "MIT",
+vsimd: 0.8.0, "MIT",
 wait-timeout: 0.2.1, "Apache-2.0 OR MIT",
 walkdir: 2.5.0, "MIT OR Unlicense",
 want: 0.3.1, "MIT",
@@ -897,6 +1005,7 @@ wasm-bindgen-shared: 0.2.108, "Apache-2.0 OR MIT",
 wasm-encoder: 0.244.0, "Apache-2.0 OR Apache-2.0 WITH LLVM-exception OR MIT",
 wasm-metadata: 0.244.0, "Apache-2.0 OR Apache-2.0 WITH LLVM-exception OR MIT",
 wasm-streams: 0.4.2, "Apache-2.0 OR MIT",
+wasm_dep_analyzer: 0.3.0, "MIT",
 wasmparser: 0.244.0, "Apache-2.0 OR Apache-2.0 WITH LLVM-exception OR MIT",
 wasmtimer: 0.4.3, "MIT",
 web-sys: 0.3.85, "Apache-2.0 OR MIT",
@@ -904,6 +1013,8 @@ web-time: 1.1.0, "Apache-2.0 OR MIT",
 webpki-root-certs: 1.0.6, "CDLA-Permissive-2.0",
 webpki-roots: 0.26.11, "CDLA-Permissive-2.0",
 webpki-roots: 1.0.6, "CDLA-Permissive-2.0",
+weezl: 0.1.12, "Apache-2.0 OR MIT",
+which: 6.0.3, "MIT",
 whoami: 1.6.1, "Apache-2.0 OR BSL-1.0 OR MIT",
 widestring: 1.2.1, "Apache-2.0 OR MIT",
 winapi: 0.3.9, "Apache-2.0 OR MIT",
@@ -972,6 +1083,7 @@ windows_x86_64_msvc: 0.52.6, "Apache-2.0 OR MIT",
 windows_x86_64_msvc: 0.53.1, "Apache-2.0 OR MIT",
 winnow: 0.5.40, "MIT",
 winnow: 0.7.14, "MIT",
+winsafe: 0.0.19, "MIT",
 wit-bindgen: 0.51.0, "Apache-2.0 OR Apache-2.0 WITH LLVM-exception OR MIT",
 wit-bindgen-core: 0.51.0, "Apache-2.0 OR Apache-2.0 WITH LLVM-exception OR 
MIT",
 wit-bindgen-rust: 0.51.0, "Apache-2.0 OR Apache-2.0 WITH LLVM-exception OR 
MIT",
@@ -982,6 +1094,8 @@ writeable: 0.6.2, "Unicode-3.0",
 wyz: 0.5.1, "MIT",
 x509-parser: 0.18.1, "Apache-2.0 OR MIT",
 xattr: 1.6.1, "Apache-2.0 OR MIT",
+xmlwriter: 0.1.0, "MIT",
+y4m: 0.8.0, "MIT",
 yansi: 1.0.1, "Apache-2.0 OR MIT",
 yasna: 0.5.2, "Apache-2.0 OR MIT",
 yew: 0.22.0, "Apache-2.0 OR MIT",
@@ -1006,3 +1120,8 @@ zopfli: 0.8.3, "Apache-2.0",
 zstd: 0.13.3, "MIT",
 zstd-safe: 7.2.4, "Apache-2.0 OR MIT",
 zstd-sys: 2.0.16+zstd.1.5.7, "Apache-2.0 OR MIT",
+zune-core: 0.4.12, "Apache-2.0 OR MIT OR Zlib",
+zune-core: 0.5.1, "Apache-2.0 OR MIT OR Zlib",
+zune-inflate: 0.2.54, "Apache-2.0 OR MIT OR Zlib",
+zune-jpeg: 0.4.21, "Apache-2.0 OR MIT OR Zlib",
+zune-jpeg: 0.5.12, "Apache-2.0 OR MIT OR Zlib",
diff --git a/core/bench/dashboard/README.md b/core/bench/dashboard/README.md
index 4c810fdce..90ac5e8f3 100644
--- a/core/bench/dashboard/README.md
+++ b/core/bench/dashboard/README.md
@@ -169,10 +169,10 @@ All endpoints return JSON responses (except artifacts 
which returns a ZIP file)
 
 ### Development Mode
 
-Run the development script to start both the frontend and backend servers:
+Run the development script from root of `apache/iggy` repository to start both 
the frontend and backend servers:
 
 ```bash
-./scripts/run_dev.sh
+./scripts/dashboard/run_dev.sh
 ```
 
 This will start:
diff --git a/core/bench/dashboard/frontend/Cargo.toml 
b/core/bench/dashboard/frontend/Cargo.toml
index 7e02e1e71..0cdbb2f10 100644
--- a/core/bench/dashboard/frontend/Cargo.toml
+++ b/core/bench/dashboard/frontend/Cargo.toml
@@ -18,7 +18,7 @@
 [package]
 name = "bench-dashboard-frontend"
 license = "Apache-2.0"
-version = "0.5.1-edge.1"
+version = "0.5.2-edge.1"
 edition = "2024"
 
 [package.metadata.cargo-machete]
diff --git a/core/bench/dashboard/frontend/assets/style.css 
b/core/bench/dashboard/frontend/assets/style.css
index 327765fca..8a82cce6c 100644
--- a/core/bench/dashboard/frontend/assets/style.css
+++ b/core/bench/dashboard/frontend/assets/style.css
@@ -1737,3 +1737,142 @@ body.dark 
.benchmark-list-item-metric.message-throughput {
     display: inline-block;
     margin-right: var(--spacing-md);
 }
+
+/* Embed Modal */
+
+.embed-modal {
+    position: absolute;
+    left: 0;
+    top: calc(100% + var(--spacing-md));
+    transform: none;
+    background: var(--color-background);
+    border: 1px solid var(--color-border);
+    border-radius: var(--border-radius);
+    padding: var(--spacing-md);
+    min-width: 420px;
+    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+    z-index: 1000;
+}
+
+.embed-modal:before {
+    content: '';
+    position: absolute;
+    left: var(--spacing-md);
+    top: -8px;
+    width: 16px;
+    height: 16px;
+    background: var(--color-background);
+    border: 1px solid var(--color-border);
+    border-right: none;
+    border-bottom: none;
+    transform: rotate(45deg);
+    z-index: -1;
+}
+
+.embed-modal:after {
+    content: '';
+    position: absolute;
+    left: var(--spacing-md);
+    top: -7px;
+    width: 16px;
+    height: 16px;
+    background: var(--color-background);
+    transform: rotate(45deg);
+}
+
+.dark .embed-modal {
+    background: var(--color-dark-tooltip-background);
+    border-color: var(--color-dark-border);
+    color: var(--color-dark-text);
+}
+
+.dark .embed-modal:before {
+    background: var(--color-dark-tooltip-background);
+    border-color: var(--color-dark-border);
+}
+
+.dark .embed-modal:after {
+    background: var(--color-dark-tooltip-background);
+}
+
+.embed-snippet {
+    word-break: break-all;
+}
+
+.embed-tabs {
+    display: flex;
+    gap: var(--spacing-xs);
+    margin-bottom: var(--spacing-sm);
+}
+
+.embed-tab {
+    padding: var(--spacing-xs) var(--spacing-sm);
+    background: none;
+    border: 1px solid var(--color-border);
+    border-radius: var(--border-radius);
+    cursor: pointer;
+    font-size: var(--font-size-xs);
+    color: var(--color-text);
+    transition: all var(--transition-speed);
+}
+
+.embed-tab:hover {
+    background: var(--color-hover);
+}
+
+.embed-tab.active {
+    background: var(--color-active);
+    border-color: var(--color-text-secondary);
+}
+
+.dark .embed-tab {
+    color: var(--color-dark-text);
+    border-color: var(--color-dark-border);
+}
+
+.dark .embed-tab:hover {
+    background: var(--color-dark-hover);
+}
+
+.dark .embed-tab.active {
+    background: var(--color-dark-hover);
+    border-color: var(--color-dark-accent);
+    color: var(--color-dark-accent);
+}
+
+.embed-download-row {
+    margin-top: var(--spacing-sm);
+}
+
+.embed-download-button {
+    width: 100%;
+}
+
+.embed-theme-hint {
+    font-size: 11px;
+    color: #888;
+    margin-top: 4px;
+    font-family: monospace;
+}
+
+.embed-format-toggle {
+    display: flex;
+    gap: 4px;
+    margin-bottom: 6px;
+}
+
+.embed-format-btn {
+    padding: 2px 8px;
+    font-size: 11px;
+    border: 1px solid #555;
+    border-radius: 3px;
+    background: transparent;
+    color: #aaa;
+    cursor: pointer;
+}
+
+.embed-format-btn.active {
+    background: #444;
+    color: #fff;
+    border-color: #777;
+}
diff --git a/core/bench/dashboard/frontend/src/components/embed_modal.rs 
b/core/bench/dashboard/frontend/src/components/embed_modal.rs
new file mode 100644
index 000000000..9609eaec2
--- /dev/null
+++ b/core/bench/dashboard/frontend/src/components/embed_modal.rs
@@ -0,0 +1,205 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use crate::components::selectors::measurement_type_selector::MeasurementType;
+use crate::config::get_api_base_url;
+use gloo::timers::callback::Timeout;
+use web_sys::window;
+use yew::prelude::*;
+
+#[derive(Clone, PartialEq)]
+enum EmbedTab {
+    Iframe,
+    Image,
+}
+
+#[derive(Clone, PartialEq)]
+enum ImageFormat {
+    Markdown,
+    Html,
+}
+
+#[derive(Properties, PartialEq)]
+pub struct EmbedModalProps {
+    pub uuid: String,
+    pub measurement_type: MeasurementType,
+    pub is_dark: bool,
+}
+
+#[function_component(EmbedModal)]
+pub fn embed_modal(props: &EmbedModalProps) -> Html {
+    let active_tab = use_state(|| EmbedTab::Iframe);
+    let image_format = use_state(|| ImageFormat::Markdown);
+    let notification_visible = use_state(|| false);
+
+    let chart_type = match &props.measurement_type {
+        MeasurementType::Latency => "latency",
+        MeasurementType::Throughput => "throughput",
+        MeasurementType::Distribution => "distribution",
+    };
+
+    let theme = if props.is_dark { "dark" } else { "light" };
+    let base_url = get_api_base_url();
+    let embed_url = format!(
+        "{base_url}/embed/{uuid}?type={chart_type}&theme={theme}",
+        uuid = props.uuid,
+    );
+    let benchmark_url = format!("{base_url}/benchmarks/{uuid}", uuid = 
props.uuid);
+    let png_url = format!(
+        
"{base_url}/embed/{uuid}/chart.png?type={chart_type}&theme={theme}&width=1600&height=1200&legend=true",
+        uuid = props.uuid,
+    );
+
+    let iframe_snippet =
+        format!(r#"<iframe src="{embed_url}" width="800" height="600" 
frameborder="0"></iframe>"#);
+
+    let markdown_snippet = format!("[![Iggy 
Benchmark]({png_url})]({benchmark_url})");
+    let html_image_snippet = format!(
+        r#"<a href="{benchmark_url}"><img src="{png_url}" alt="Iggy Benchmark" 
width="800"></a>"#
+    );
+
+    let snippet = match *active_tab {
+        EmbedTab::Iframe => iframe_snippet.clone(),
+        EmbedTab::Image => match *image_format {
+            ImageFormat::Markdown => markdown_snippet.clone(),
+            ImageFormat::Html => html_image_snippet.clone(),
+        },
+    };
+
+    let on_copy = {
+        let snippet = snippet.clone();
+        let notification_visible = notification_visible.clone();
+        Callback::from(move |_| {
+            if let Some(window) = window() {
+                let clipboard = window.navigator().clipboard();
+                let _ = clipboard.write_text(&snippet);
+                notification_visible.set(true);
+
+                let notification_visible = notification_visible.clone();
+                let timeout = Timeout::new(1_000, move || {
+                    notification_visible.set(false);
+                });
+                timeout.forget();
+            }
+        })
+    };
+
+    let on_download = {
+        let png_url = png_url.clone();
+        Callback::from(move |_| {
+            if let Some(window) = window() {
+                let _ = window.open_with_url_and_target(&png_url, "_blank");
+            }
+        })
+    };
+
+    let on_iframe_tab = {
+        let active_tab = active_tab.clone();
+        Callback::from(move |_| active_tab.set(EmbedTab::Iframe))
+    };
+
+    let on_image_tab = {
+        let active_tab = active_tab.clone();
+        Callback::from(move |_| active_tab.set(EmbedTab::Image))
+    };
+
+    let on_markdown_format = {
+        let image_format = image_format.clone();
+        Callback::from(move |_| image_format.set(ImageFormat::Markdown))
+    };
+
+    let on_html_format = {
+        let image_format = image_format.clone();
+        Callback::from(move |_| image_format.set(ImageFormat::Html))
+    };
+
+    html! {
+        <div class="embed-modal">
+            <div class="tooltip-section">
+                <h4>{"Embed Chart"}</h4>
+                <div class="embed-tabs">
+                    <button
+                        class={classes!("embed-tab", (*active_tab == 
EmbedTab::Iframe).then_some("active"))}
+                        onclick={on_iframe_tab}
+                    >
+                        {"Iframe"}
+                    </button>
+                    <button
+                        class={classes!("embed-tab", (*active_tab == 
EmbedTab::Image).then_some("active"))}
+                        onclick={on_image_tab}
+                    >
+                        {"Image"}
+                    </button>
+                </div>
+                {
+                    if *active_tab == EmbedTab::Image {
+                        html! {
+                            <div class="embed-format-toggle">
+                                <button
+                                    class={classes!("embed-format-btn", 
(*image_format == ImageFormat::Markdown).then_some("active"))}
+                                    onclick={on_markdown_format}
+                                >
+                                    {"Markdown"}
+                                </button>
+                                <button
+                                    class={classes!("embed-format-btn", 
(*image_format == ImageFormat::Html).then_some("active"))}
+                                    onclick={on_html_format}
+                                >
+                                    {"HTML"}
+                                </button>
+                            </div>
+                        }
+                    } else {
+                        html! {}
+                    }
+                }
+                <div class="tooltip-content">
+                    <p class="command-row">
+                        <span class="command-text 
embed-snippet">{&snippet}</span>
+                        <span class="copy-button-container">
+                            <button onclick={on_copy} 
class="copy-button">{"Copy"}</button>
+                            <span class={classes!(
+                                "copy-notification",
+                                (*notification_visible).then_some("visible")
+                            )}>
+                                {"Copied!"}
+                            </span>
+                        </span>
+                    </p>
+                    {
+                        if *active_tab == EmbedTab::Image {
+                            html! {
+                                <>
+                                    <p class="embed-download-row">
+                                        <button onclick={on_download} 
class="copy-button embed-download-button">
+                                            {"Download PNG"}
+                                        </button>
+                                    </p>
+                                    <p class="embed-theme-hint">
+                                        {"Themes: dark, light, dark_nobg, 
light_nobg"}
+                                    </p>
+                                </>
+                            }
+                        } else {
+                            html! {}
+                        }
+                    }
+                </div>
+            </div>
+        </div>
+    }
+}
diff --git a/core/bench/dashboard/frontend/src/components/layout/topbar.rs 
b/core/bench/dashboard/frontend/src/components/layout/topbar.rs
index c6ec04efa..49368d24f 100644
--- a/core/bench/dashboard/frontend/src/components/layout/topbar.rs
+++ b/core/bench/dashboard/frontend/src/components/layout/topbar.rs
@@ -16,6 +16,7 @@
 // under the License.
 
 use crate::api;
+use crate::components::embed_modal::EmbedModal;
 use crate::components::selectors::measurement_type_selector::MeasurementType;
 use crate::components::theme::theme_toggle::ThemeToggle;
 use crate::components::tooltips::benchmark_info_toggle::BenchmarkInfoToggle;
@@ -89,6 +90,13 @@ pub fn topbar(props: &TopBarProps) -> Html {
         })
     };
 
+    let on_embed_toggle = {
+        let ui_state = ui_state.clone();
+        Callback::from(move |_| {
+            ui_state.dispatch(UiAction::ToggleEmbedModal);
+        })
+    };
+
     html! {
         <div class="top-buttons">
             <div class="controls">
@@ -112,6 +120,39 @@ pub fn topbar(props: &TopBarProps) -> Html {
                                         <line x1="12" y1="15" x2="12" y2="3"/>
                                     </svg>
                                 </button>
+                                <div class="info-container">
+                                    <button
+                                        class={classes!(
+                                            "icon-button",
+                                            
ui_state.is_embed_modal_visible.then_some("active")
+                                        )}
+                                        onclick={on_embed_toggle.clone()}
+                                        title="Embed Chart"
+                                        
disabled={benchmark_ctx.state.selected_benchmark.is_none()}
+                                    >
+                                        <svg 
xmlns="http://www.w3.org/2000/svg"; width="32" height="32" viewBox="0 0 24 24" 
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" 
stroke-linejoin="round">
+                                            <polyline points="16 18 22 12 16 
6"/>
+                                            <polyline points="8 6 2 12 8 18"/>
+                                        </svg>
+                                    </button>
+                                    {
+                                        if ui_state.is_embed_modal_visible {
+                                            if let Some(benchmark) = 
&benchmark_ctx.state.selected_benchmark {
+                                                html! {
+                                                    <EmbedModal
+                                                        
uuid={benchmark.uuid.to_string()}
+                                                        
measurement_type={selected_measurement.clone()}
+                                                        is_dark={props.is_dark}
+                                                    />
+                                                }
+                                            } else {
+                                                html! {}
+                                            }
+                                        } else {
+                                            html! {}
+                                        }
+                                    }
+                                </div>
                                 <div class="info-container">
                                     <ServerStatsToggle
                                         
is_visible={is_server_stats_tooltip_visible}
diff --git a/core/bench/dashboard/frontend/src/components/mod.rs 
b/core/bench/dashboard/frontend/src/components/mod.rs
index d923c72b0..0781daaf9 100644
--- a/core/bench/dashboard/frontend/src/components/mod.rs
+++ b/core/bench/dashboard/frontend/src/components/mod.rs
@@ -17,7 +17,7 @@
 
 pub mod app_content;
 pub mod chart;
-// pub mod common; // Removed unused module
+pub mod embed_modal;
 pub mod footer;
 pub mod layout;
 pub mod selectors;
diff --git a/core/bench/dashboard/frontend/src/state/ui.rs 
b/core/bench/dashboard/frontend/src/state/ui.rs
index 0a7b691dc..57c0146d1 100644
--- a/core/bench/dashboard/frontend/src/state/ui.rs
+++ b/core/bench/dashboard/frontend/src/state/ui.rs
@@ -33,6 +33,7 @@ pub struct UiState {
     pub selected_measurement: MeasurementType,
     pub is_benchmark_tooltip_visible: bool,
     pub is_server_stats_tooltip_visible: bool,
+    pub is_embed_modal_visible: bool,
 }
 
 impl Default for UiState {
@@ -42,6 +43,7 @@ impl Default for UiState {
             selected_measurement: MeasurementType::Latency,
             is_benchmark_tooltip_visible: false,
             is_server_stats_tooltip_visible: false,
+            is_embed_modal_visible: false,
         }
     }
 }
@@ -50,6 +52,7 @@ pub enum UiAction {
     SetMeasurementType(MeasurementType),
     ToggleBenchmarkTooltip,
     ToggleServerStatsTooltip,
+    ToggleEmbedModal,
     SetViewMode(ViewMode),
 }
 
@@ -70,6 +73,10 @@ impl Reducible for UiState {
                 is_server_stats_tooltip_visible: 
!self.is_server_stats_tooltip_visible,
                 ..(*self).clone()
             },
+            UiAction::ToggleEmbedModal => UiState {
+                is_embed_modal_visible: !self.is_embed_modal_visible,
+                ..(*self).clone()
+            },
             UiAction::SetViewMode(vm) => UiState {
                 view_mode: vm,
                 ..(*self).clone()
diff --git a/core/bench/dashboard/server/Cargo.toml 
b/core/bench/dashboard/server/Cargo.toml
index 4ec3e5a24..f29ad2194 100644
--- a/core/bench/dashboard/server/Cargo.toml
+++ b/core/bench/dashboard/server/Cargo.toml
@@ -18,7 +18,7 @@
 [package]
 name = "iggy-bench-dashboard-server"
 license = "Apache-2.0"
-version = "0.6.1-edge.1"
+version = "0.6.2-edge.1"
 edition = "2024"
 
 [dependencies]
@@ -27,6 +27,7 @@ actix-files = { workspace = true }
 actix-web = { workspace = true }
 bench-dashboard-shared = { workspace = true }
 bench-report = { workspace = true }
+charming = { workspace = true, features = ["ssr", "ssr-raster"] }
 chrono = { workspace = true, features = ["serde"] }
 clap = { workspace = true }
 dashmap = { workspace = true }
diff --git a/core/bench/dashboard/server/src/cache/loader.rs 
b/core/bench/dashboard/server/src/cache/loader.rs
index 5dbff1170..e13217f4e 100644
--- a/core/bench/dashboard/server/src/cache/loader.rs
+++ b/core/bench/dashboard/server/src/cache/loader.rs
@@ -35,6 +35,7 @@ impl BenchmarkCache {
             .map_err(IggyBenchDashboardServerError::Io)?
             .filter_map(|r: std::result::Result<std::fs::DirEntry, 
std::io::Error>| r.ok())
             .filter(|entry| entry.file_type().map(|t| 
t.is_dir()).unwrap_or(false))
+            .filter(|entry| entry.file_name() != "embed_cache")
             .collect();
 
         let mut total_removed_size = 0;
diff --git a/core/bench/dashboard/server/src/cache/storage.rs 
b/core/bench/dashboard/server/src/cache/storage.rs
index 6c1186707..4328fe8e2 100644
--- a/core/bench/dashboard/server/src/cache/storage.rs
+++ b/core/bench/dashboard/server/src/cache/storage.rs
@@ -51,6 +51,10 @@ impl BenchmarkCache {
             .map(|entry| entry.value().0.clone())
     }
 
+    pub fn results_dir(&self) -> &std::path::Path {
+        &self.results_dir
+    }
+
     pub(crate) fn clear(&self) {
         self.benchmarks.clear();
         self.hardware_to_gitref.clear();
diff --git a/core/bench/dashboard/server/src/cache/watcher.rs 
b/core/bench/dashboard/server/src/cache/watcher.rs
index c5e6e622c..b3c98bcdd 100644
--- a/core/bench/dashboard/server/src/cache/watcher.rs
+++ b/core/bench/dashboard/server/src/cache/watcher.rs
@@ -43,6 +43,13 @@ impl CacheWatcher {
                     event.kind,
                     EventKind::Create(_) | EventKind::Modify(_) | 
EventKind::Remove(_)
                 ) {
+                    let in_embed_cache = event
+                        .paths
+                        .iter()
+                        .all(|p| p.components().any(|c| c.as_os_str() == 
"embed_cache"));
+                    if in_embed_cache {
+                        return;
+                    }
                     let cache = Arc::clone(&cache_clone);
                     runtime_handle.spawn(async move {
                         cache.schedule_reload().await;
diff --git a/core/bench/dashboard/server/src/error.rs 
b/core/bench/dashboard/server/src/error.rs
index 9af924a5f..1a9c4d92b 100644
--- a/core/bench/dashboard/server/src/error.rs
+++ b/core/bench/dashboard/server/src/error.rs
@@ -31,6 +31,8 @@ pub enum IggyBenchDashboardServerError {
     InvalidJson(String),
     #[error("Invalid UUID format: {0}")]
     InvalidUuid(String),
+    #[error("Bad request: {0}")]
+    BadRequest(String),
     #[error("Internal error: {0}")]
     InternalError(String),
 }
@@ -41,6 +43,10 @@ impl ResponseError for IggyBenchDashboardServerError {
             IggyBenchDashboardServerError::NotFound(msg) => {
                 HttpResponse::NotFound().json(json!({ "error": msg }))
             }
+            IggyBenchDashboardServerError::InvalidUuid(msg)
+            | IggyBenchDashboardServerError::BadRequest(msg) => {
+                HttpResponse::BadRequest().json(json!({ "error": msg }))
+            }
             _ => HttpResponse::InternalServerError().json(json!({ "error": 
self.to_string() })),
         }
     }
diff --git a/core/bench/dashboard/server/src/handlers.rs 
b/core/bench/dashboard/server/src/handlers.rs
index 44c6bca45..d2f5f7b7a 100644
--- a/core/bench/dashboard/server/src/handlers.rs
+++ b/core/bench/dashboard/server/src/handlers.rs
@@ -15,8 +15,11 @@
 // specific language governing permissions and limitations
 // under the License.
 
+use crate::render::PngRenderPool;
 use crate::{cache::BenchmarkCache, error::IggyBenchDashboardServerError};
 use actix_web::{HttpRequest, HttpResponse, get, web};
+use serde::Deserialize;
+use std::path::PathBuf;
 use std::sync::Arc;
 use tracing::{info, warn};
 use uuid::Uuid;
@@ -27,6 +30,7 @@ type Result<T> = std::result::Result<T, 
IggyBenchDashboardServerError>;
 
 pub struct AppState {
     pub cache: Arc<BenchmarkCache>,
+    pub render_pool: Arc<PngRenderPool>,
 }
 
 #[get("/health")]
@@ -363,6 +367,452 @@ pub async fn get_recent_benchmarks(
     Ok(HttpResponse::Ok().json(benchmarks))
 }
 
+fn default_chart_type() -> String {
+    "latency".to_string()
+}
+
+fn default_theme() -> String {
+    "dark".to_string()
+}
+
+const DEFAULT_PNG_WIDTH: u32 = 1600;
+const DEFAULT_PNG_HEIGHT: u32 = 1200;
+
+fn default_png_width() -> u32 {
+    DEFAULT_PNG_WIDTH
+}
+
+fn default_png_height() -> u32 {
+    DEFAULT_PNG_HEIGHT
+}
+
+fn default_legend() -> bool {
+    true
+}
+
+#[derive(Deserialize)]
+pub struct EmbedQuery {
+    #[serde(rename = "type", default = "default_chart_type")]
+    pub chart_type: String,
+    #[serde(default = "default_theme")]
+    pub theme: String,
+    pub action: Option<String>,
+}
+
+#[derive(Deserialize)]
+pub struct PngQuery {
+    #[serde(rename = "type", default = "default_chart_type")]
+    pub chart_type: String,
+    #[serde(default = "default_theme")]
+    pub theme: String,
+    #[serde(default = "default_png_width")]
+    pub width: u32,
+    #[serde(default = "default_png_height")]
+    pub height: u32,
+    #[serde(default = "default_legend")]
+    pub legend: bool,
+}
+
+fn png_cache_path(cache_dir: &std::path::Path, uuid: &Uuid, query: &PngQuery) 
-> PathBuf {
+    let legend_suffix = if query.legend { "" } else { "_nolegend" };
+    let filename = format!(
+        "{uuid}_{chart_type}_{w}x{h}_{theme}{legend_suffix}.png",
+        uuid = uuid,
+        chart_type = query.chart_type,
+        w = query.width,
+        h = query.height,
+        theme = query.theme,
+    );
+    cache_dir.join(filename)
+}
+
+#[get("/embed/{uuid}/chart.png")]
+pub async fn embed_chart_png(
+    data: web::Data<AppState>,
+    uuid_str: web::Path<String>,
+    query: web::Query<PngQuery>,
+    req: HttpRequest,
+) -> Result<HttpResponse> {
+    let client_addr = get_client_addr(&req);
+    info!(
+        "{}: Embed PNG request for '{}' (type={}, theme={}, {}x{})",
+        client_addr, uuid_str, query.chart_type, query.theme, query.width, 
query.height
+    );
+
+    let uuid = Uuid::parse_str(&uuid_str).map_err(|_| {
+        IggyBenchDashboardServerError::InvalidUuid(format!("Invalid UUID 
format: '{uuid_str}'"))
+    })?;
+
+    let chart_type = &query.chart_type;
+    if chart_type != "latency" && chart_type != "throughput" && chart_type != 
"distribution" {
+        return Err(IggyBenchDashboardServerError::BadRequest(format!(
+            "Invalid chart type: '{chart_type}'. Must be 'latency', 
'throughput', or 'distribution'"
+        )));
+    }
+
+    let cache_dir = data.cache.results_dir().join("embed_cache");
+    let cached_path = png_cache_path(&cache_dir, &uuid, &query);
+
+    if cached_path.exists() {
+        info!("{}: Serving cached PNG for '{}'", client_addr, uuid_str);
+        let bytes = std::fs::read(&cached_path).map_err(|e| {
+            IggyBenchDashboardServerError::InternalError(format!("Failed to 
read cached PNG: {e}"))
+        })?;
+        return Ok(HttpResponse::Ok().content_type("image/png").body(bytes));
+    }
+
+    let json_path = data.cache.get_benchmark_json_path(&uuid).ok_or_else(|| {
+        IggyBenchDashboardServerError::NotFound(format!("Benchmark 
'{uuid_str}' not found"))
+    })?;
+
+    let json_content = std::fs::read_to_string(&json_path).map_err(|e| {
+        IggyBenchDashboardServerError::InternalError(format!(
+            "Failed to read report file '{}': {}",
+            json_path.display(),
+            e
+        ))
+    })?;
+
+    let report: bench_report::report::BenchmarkReport = 
serde_json::from_str(&json_content)
+        .map_err(|e| {
+            IggyBenchDashboardServerError::InternalError(format!(
+                "Failed to deserialize report: {e}"
+            ))
+        })?;
+
+    let theme = &query.theme;
+    let is_dark = theme == "dark" || theme == "dark_nobg";
+    let transparent = theme == "dark_nobg" || theme == "light_nobg";
+
+    if !matches!(
+        theme.as_str(),
+        "dark" | "light" | "dark_nobg" | "light_nobg"
+    ) {
+        return Err(IggyBenchDashboardServerError::BadRequest(format!(
+            "Invalid theme: '{theme}'. Must be 'dark', 'light', 'dark_nobg', 
or 'light_nobg'"
+        )));
+    }
+
+    let hardware_line = format!(
+        "{} @ {} (server {})",
+        report
+            .hardware
+            .identifier
+            .as_deref()
+            .unwrap_or("identifier"),
+        report.hardware.cpu_name,
+        report.params.gitref.as_deref().unwrap_or("version")
+    );
+
+    let chart = if chart_type == "latency" {
+        bench_report::create_latency_chart(&report, is_dark, false)
+    } else if chart_type == "throughput" {
+        bench_report::create_throughput_chart(&report, is_dark, false)
+    } else {
+        bench_report::create_latency_distribution_chart(&report, is_dark, 
false)
+    };
+
+    // Strip interactive-only components and adapt layout for static PNG
+    let mut chart_value = serde_json::to_value(&chart).map_err(|e| {
+        IggyBenchDashboardServerError::InternalError(format!(
+            "Failed to serialize chart for PNG: {e}"
+        ))
+    })?;
+    if let Some(obj) = chart_value.as_object_mut() {
+        obj.remove("dataZoom");
+        obj.insert("toolbox".to_string(), serde_json::json!({ "show": false 
}));
+        let series_count = obj
+            .get("series")
+            .and_then(|s| s.as_array())
+            .map(|s| s.len())
+            .unwrap_or(0);
+        if transparent {
+            obj.insert(
+                "backgroundColor".to_string(),
+                serde_json::json!("transparent"),
+            );
+        } else if !is_dark {
+            obj.insert("backgroundColor".to_string(), 
serde_json::json!("#ffffff"));
+        }
+        // Scale text relative to image dimensions (baseline: 1200x800)
+        let scale = (query.width as f64 / 1200.0).min(query.height as f64 / 
800.0);
+        if let Some(t) = obj
+            .get_mut("title")
+            .and_then(|t| t.as_array_mut())
+            .and_then(|titles| titles.first_mut())
+            .and_then(|t| t.as_object_mut())
+        {
+            // Prepend hardware identifier to existing subtext
+            if let Some(existing) = t.get("subtext").and_then(|s| s.as_str()) {
+                t.insert(
+                    "subtext".to_string(),
+                    serde_json::json!(format!("{hardware_line}\n{existing}")),
+                );
+            }
+            t.insert(
+                "subtextStyle".to_string(),
+                serde_json::json!({
+                    "fontSize": (10.0 * scale).round() as u32,
+                    "lineHeight": (14.0 * scale).round() as u32,
+                }),
+            );
+            t.insert(
+                "textStyle".to_string(),
+                serde_json::json!({ "fontSize": (16.0 * scale).round() as u32 
}),
+            );
+        }
+        if let Some(g) = obj
+            .get_mut("grid")
+            .and_then(|g| g.as_array_mut())
+            .and_then(|grids| grids.first_mut())
+            .and_then(|g| g.as_object_mut())
+        {
+            g.insert("left".to_string(), serde_json::json!("12%"));
+            g.insert("top".to_string(), serde_json::json!("20%"));
+            g.insert("bottom".to_string(), serde_json::json!("10%"));
+            if !query.legend {
+                g.insert("right".to_string(), serde_json::json!("10%"));
+            } else if series_count > 20 {
+                g.insert("right".to_string(), serde_json::json!("25%"));
+            }
+        }
+        if !query.legend {
+            obj.remove("legend");
+        } else if let Some(leg) = obj
+            .get_mut("legend")
+            .and_then(|l| l.as_array_mut())
+            .and_then(|legends| legends.first_mut())
+            .and_then(|l| l.as_object_mut())
+        {
+            {
+                let available_height = query.height as f64 * 0.75;
+                let entry_height = available_height / series_count.max(1) as 
f64;
+                let font_size = (entry_height * 0.55).clamp(6.0, 10.0 * 
scale).round() as u32;
+                let item_gap = (entry_height * 0.2).clamp(1.0, 10.0).round() 
as u32;
+                let item_height = font_size.max(4);
+                let item_width = (item_height as f64 * 1.5).round() as u32;
+
+                leg.insert(
+                    "textStyle".to_string(),
+                    serde_json::json!({ "fontSize": font_size }),
+                );
+                leg.insert("itemGap".to_string(), serde_json::json!(item_gap));
+                leg.insert("itemWidth".to_string(), 
serde_json::json!(item_width));
+                leg.insert("itemHeight".to_string(), 
serde_json::json!(item_height));
+            }
+        }
+    }
+    let chart: charming::Chart = 
serde_json::from_value(chart_value).map_err(|e| {
+        IggyBenchDashboardServerError::InternalError(format!(
+            "Failed to rebuild chart for PNG: {e}"
+        ))
+    })?;
+
+    let png_bytes = data
+        .render_pool
+        .render_png(chart, query.width, query.height, is_dark)
+        .await
+        .map_err(IggyBenchDashboardServerError::InternalError)?;
+
+    std::fs::create_dir_all(&cache_dir).map_err(|e| {
+        IggyBenchDashboardServerError::InternalError(format!(
+            "Failed to create embed cache directory: {e}"
+        ))
+    })?;
+    if let Err(e) = std::fs::write(&cached_path, &png_bytes) {
+        warn!("Failed to cache PNG to {}: {}", cached_path.display(), e);
+    }
+
+    info!(
+        "{}: Generated and cached PNG for '{}' ({}x{})",
+        client_addr, uuid_str, query.width, query.height
+    );
+
+    Ok(HttpResponse::Ok().content_type("image/png").body(png_bytes))
+}
+
+#[get("/embed/{uuid}")]
+pub async fn embed_chart(
+    data: web::Data<AppState>,
+    uuid_str: web::Path<String>,
+    query: web::Query<EmbedQuery>,
+    req: HttpRequest,
+) -> Result<HttpResponse> {
+    let client_addr = get_client_addr(&req);
+    info!(
+        "{}: Embed chart request for '{}' (type={}, theme={})",
+        client_addr, uuid_str, query.chart_type, query.theme
+    );
+
+    let uuid = Uuid::parse_str(&uuid_str).map_err(|_| {
+        IggyBenchDashboardServerError::InvalidUuid(format!("Invalid UUID 
format: '{uuid_str}'"))
+    })?;
+
+    let is_dark = query.theme != "light";
+    let chart_type = &query.chart_type;
+    if chart_type != "latency" && chart_type != "throughput" && chart_type != 
"distribution" {
+        return Err(IggyBenchDashboardServerError::BadRequest(format!(
+            "Invalid chart type: '{chart_type}'. Must be 'latency', 
'throughput', or 'distribution'"
+        )));
+    }
+
+    let json_path = data.cache.get_benchmark_json_path(&uuid).ok_or_else(|| {
+        IggyBenchDashboardServerError::NotFound(format!("Benchmark 
'{uuid_str}' not found"))
+    })?;
+
+    let json_content = std::fs::read_to_string(&json_path).map_err(|e| {
+        IggyBenchDashboardServerError::InternalError(format!(
+            "Failed to read report file '{}': {}",
+            json_path.display(),
+            e
+        ))
+    })?;
+
+    let report: bench_report::report::BenchmarkReport = 
serde_json::from_str(&json_content)
+        .map_err(|e| {
+            IggyBenchDashboardServerError::InternalError(format!(
+                "Failed to deserialize report: {e}"
+            ))
+        })?;
+
+    let hardware_line = format!(
+        "{} @ {} (server {})",
+        report
+            .hardware
+            .identifier
+            .as_deref()
+            .unwrap_or("identifier"),
+        report.hardware.cpu_name,
+        report.params.gitref.as_deref().unwrap_or("version")
+    );
+
+    let chart = if chart_type == "latency" {
+        bench_report::create_latency_chart(&report, is_dark, false)
+    } else if chart_type == "throughput" {
+        bench_report::create_throughput_chart(&report, is_dark, false)
+    } else {
+        bench_report::create_latency_distribution_chart(&report, is_dark, 
false)
+    };
+
+    let chart_json = serde_json::to_string(&chart).map_err(|e| {
+        IggyBenchDashboardServerError::InternalError(format!(
+            "Failed to serialize chart to JSON: {e}"
+        ))
+    })?;
+
+    let is_download = query.action.as_deref() == Some("download");
+    let bg_color = if is_dark { "#070C18" } else { "#ffffff" };
+
+    let hardware_line_js = hardware_line.replace('\\', "\\\\").replace('\'', 
"\\'");
+    let adapt_layout_js = format!(
+        r#"
+  var hardwareLine = '{hardware_line_js}';
+  function adaptLayout() {{
+    var w = window.innerWidth;
+    var h = window.innerHeight;
+    var scale = Math.min(w / 1000, h / 600);
+
+    if (option.title && option.title.length) {{
+      var t = option.title[0];
+      t.textStyle = Object.assign(t.textStyle || {{}}, {{ fontSize: 
Math.round(18 * scale) }});
+      if (t.subtext && t.subtext.indexOf(hardwareLine) === -1) {{
+        t.subtext = hardwareLine + '\n' + t.subtext;
+      }}
+      if (t.subtext) {{
+        t.subtextStyle = Object.assign(t.subtextStyle || {{}}, {{
+          fontSize: Math.round(11 * scale),
+          lineHeight: Math.round(15 * scale)
+        }});
+      }}
+    }}
+    if (option.grid && option.grid.length) {{
+      option.grid[0].top = '18%';
+      option.grid[0].left = '8%';
+      option.grid[0].bottom = '14%';
+    }}
+    if (option.dataZoom && option.dataZoom.length) {{
+      option.dataZoom[0].bottom = '3%';
+    }}
+    if (option.legend && option.legend.length) {{
+      option.legend[0].textStyle = Object.assign(option.legend[0].textStyle || 
{{}}, {{
+        fontSize: Math.round(10 * scale)
+      }});
+    }}
+
+    chart.setOption(option);
+    chart.resize();
+  }}"#
+    );
+
+    let html = if is_download {
+        format!(
+            r#"<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<title>Downloading chart...</title>
+<script 
src="https://cdn.jsdelivr.net/npm/[email protected]/dist/echarts.min.js";></script>
+<style>
+  * {{ margin: 0; padding: 0; box-sizing: border-box; }}
+  body {{ background: transparent; }}
+  #chart {{ width: 1200px; height: 800px; }}
+  #status {{ position: fixed; top: 50%; left: 50%; transform: translate(-50%, 
-50%);
+    font-family: sans-serif; color: #888; font-size: 16px; }}
+</style>
+</head>
+<body>
+<div id="chart"></div>
+<div id="status">Generating PNG...</div>
+<script>
+  var chart = echarts.init(document.getElementById('chart'));
+  var option = {chart_json};
+  {adapt_layout_js}
+  adaptLayout();
+  chart.on('finished', function() {{
+    var url = chart.getDataURL({{ type: 'png', pixelRatio: 2, backgroundColor: 
'rgba(0,0,0,0)' }});
+    var a = document.createElement('a');
+    a.href = url;
+    a.download = '{uuid_str}_{chart_type}.png';
+    document.body.appendChild(a);
+    a.click();
+    document.getElementById('status').textContent = 'Download started. You can 
close this tab.';
+  }});
+</script>
+</body>
+</html>"#
+        )
+    } else {
+        format!(
+            r#"<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<title>Iggy Benchmark Chart</title>
+<script 
src="https://cdn.jsdelivr.net/npm/[email protected]/dist/echarts.min.js";></script>
+<style>
+  * {{ margin: 0; padding: 0; box-sizing: border-box; }}
+  body {{ background: {bg_color}; overflow: hidden; }}
+  #chart {{ width: 100%; height: 100vh; }}
+</style>
+</head>
+<body>
+<div id="chart"></div>
+<script>
+  var chart = echarts.init(document.getElementById('chart'));
+  var option = {chart_json};
+  {adapt_layout_js}
+  adaptLayout();
+  window.addEventListener('resize', function() {{ adaptLayout(); }});
+</script>
+</body>
+</html>"#
+        )
+    };
+
+    Ok(HttpResponse::Ok().content_type("text/html").body(html))
+}
+
 fn get_client_addr(req: &HttpRequest) -> String {
     req.connection_info()
         .peer_addr()
diff --git a/core/bench/dashboard/server/src/main.rs 
b/core/bench/dashboard/server/src/main.rs
index acbdf03ff..4e95afe0c 100644
--- a/core/bench/dashboard/server/src/main.rs
+++ b/core/bench/dashboard/server/src/main.rs
@@ -20,6 +20,7 @@ mod cache;
 mod error;
 mod github;
 mod handlers;
+mod render;
 
 use crate::cache::CacheWatcher;
 use actix_cors::Cors;
@@ -47,6 +48,7 @@ use tracing_subscriber::{
 struct ServerState {
     cache: Arc<BenchmarkCache>,
     _watcher: Arc<CacheWatcher>,
+    render_pool: Arc<render::PngRenderPool>,
 }
 
 async fn index() -> actix_web::Result<NamedFile> {
@@ -106,9 +108,12 @@ async fn main() -> Result<(), std::io::Error> {
         None
     };
 
+    let render_pool = Arc::new(render::PngRenderPool::default_pool());
+
     let state = ServerState {
         cache: Arc::clone(&cache),
         _watcher: Arc::clone(&watcher),
+        render_pool,
     };
 
     info!("Starting server on {}", addr);
@@ -148,6 +153,7 @@ async fn main() -> Result<(), std::io::Error> {
             .wrap(Compress::default())
             .app_data(web::Data::new(AppState {
                 cache: Arc::clone(&state.cache),
+                render_pool: Arc::clone(&state.render_pool),
             }))
             .service(handlers::health_check)
             .service(handlers::list_hardware)
@@ -159,6 +165,8 @@ async fn main() -> Result<(), std::io::Error> {
             .service(handlers::get_benchmark_report_light)
             .service(handlers::get_benchmark_trend)
             .service(handlers::get_test_artifacts_zip)
+            .service(handlers::embed_chart_png)
+            .service(handlers::embed_chart)
             .service(
                 fs::Files::new("/", "frontend/dist")
                     .index_file("index.html")
diff --git a/core/bench/dashboard/server/src/render.rs 
b/core/bench/dashboard/server/src/render.rs
new file mode 100644
index 000000000..ff1d62f75
--- /dev/null
+++ b/core/bench/dashboard/server/src/render.rs
@@ -0,0 +1,93 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use charming::Chart;
+use charming::renderer::image_renderer::{ImageFormat, ImageRenderer};
+use charming::theme::Theme;
+use std::sync::mpsc;
+use tracing::info;
+
+struct RenderJob {
+    chart: Chart,
+    width: u32,
+    height: u32,
+    dark: bool,
+    reply: tokio::sync::oneshot::Sender<Result<Vec<u8>, String>>,
+}
+
+/// Dedicated render thread for PNG generation.
+///
+/// charming's SSR uses deno_core (V8) which has global state that is not
+/// safe to use from multiple OS threads. All rendering is pinned to a single
+/// thread; actix workers send jobs via channel and await results without 
blocking.
+/// Disk caching ensures only the first request for a given chart incurs 
render cost.
+pub struct PngRenderPool {
+    sender: mpsc::Sender<RenderJob>,
+}
+
+impl PngRenderPool {
+    pub fn new() -> Self {
+        let (tx, rx) = mpsc::channel::<RenderJob>();
+
+        std::thread::Builder::new()
+            .name("png-render".to_string())
+            .spawn(move || {
+                for job in rx {
+                    let result = Self::render(&job.chart, job.width, 
job.height, job.dark);
+                    let _ = job.reply.send(result);
+                }
+            })
+            .expect("failed to spawn png render thread");
+
+        info!("PNG render thread started");
+        Self { sender: tx }
+    }
+
+    pub fn default_pool() -> Self {
+        Self::new()
+    }
+
+    fn render(chart: &Chart, width: u32, height: u32, dark: bool) -> 
Result<Vec<u8>, String> {
+        let mut renderer = ImageRenderer::new(width, height);
+        if dark {
+            renderer = renderer.theme(Theme::Dark);
+        }
+        renderer
+            .render_format(ImageFormat::Png, chart)
+            .map_err(|e| format!("PNG render failed: {e}"))
+    }
+
+    pub async fn render_png(
+        &self,
+        chart: Chart,
+        width: u32,
+        height: u32,
+        dark: bool,
+    ) -> Result<Vec<u8>, String> {
+        let (tx, rx) = tokio::sync::oneshot::channel();
+        self.sender
+            .send(RenderJob {
+                chart,
+                width,
+                height,
+                dark,
+                reply: tx,
+            })
+            .map_err(|_| "render pool shut down".to_string())?;
+        rx.await.map_err(|_| "render worker dropped".to_string())?
+    }
+}
diff --git a/core/bench/report/src/plotting/chart.rs 
b/core/bench/report/src/plotting/chart.rs
index 982212691..5a2e02d67 100644
--- a/core/bench/report/src/plotting/chart.rs
+++ b/core/bench/report/src/plotting/chart.rs
@@ -271,6 +271,7 @@ impl IggyChart {
             .data(data)
             .show_symbol(false)
             .smooth(true)
+            .z(1)
             .line_style(LineStyle::new().width(width))
             .item_style(ItemStyle::new().color(color));
 

Reply via email to