feat(ui): Wild Dragon reskin overlay on the Restreamer UI
Layers Wild Dragon branding on top of upstream restreamer-ui v1.14.0
without forking the whole repo — keeps upstream UI updates flowing in
when we bump RESTREAMER_UI_REF.
Overlay (deploy/truenas/core/ui-overlay/):
public/index.html Wild Dragon title, theme color #0d0e12
public/manifest.json PWA name/short_name/colors
public/favicon.ico multi-res ICO (16/32/64) generated from
a 'WD' monogram in orange #ff6633 on dark
public/logo192.png Apple touch icon
public/logo512.png PWA install icon
src/misc/Logo/images/ rs-logo.svg (square mark, used in the
Header) and logo.svg (wordmark, used in
the Footer) — both Wild-Dragon-themed
src/misc/Logo/{index,rsLogo}.js
link the logos to forge.wilddragon.net
instead of datarhei.com
apply-overlay.sh runs in the Docker ui-builder stage just after the
upstream git clone and just before yarn install. Two phases:
1. rsync the overlay's public/ and src/ on top of the cloned
upstream tree
2. Targeted in-place patches for one-line UI strings (header
title, two welcome captions). Each patch is anchored to a
unique surrounding context and the script fails loudly if the
anchor isn't present — so a future upstream rename surfaces
immediately rather than silently shipping un-rebranded UI.
Image size: ~+50KB (the overlay assets), no measurable build-time
delta. PWA installs and OS bookmarks now show Wild Dragon. The
remaining 'Restreamer'/'datarhei' references in views/Welcome.js,
views/Login.js, views/Settings.js, etc. are deeper-page strings
that aren't worth a one-off overlay; they'll go away when we fork
the UI repo properly for the WebRTC tab milestone.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
10f3e20a6a
commit
7621f88fea
11 changed files with 204 additions and 2 deletions
|
|
@ -39,9 +39,20 @@ FROM node:21-alpine3.20 AS ui-builder
|
|||
ARG RESTREAMER_UI_REF=v1.14.0
|
||||
RUN apk add --no-cache git
|
||||
WORKDIR /ui
|
||||
|
||||
# 1. Pull upstream restreamer-ui at the pinned tag.
|
||||
RUN git clone --depth=1 --branch ${RESTREAMER_UI_REF} \
|
||||
https://github.com/datarhei/restreamer-ui.git . \
|
||||
&& yarn install --frozen-lockfile --network-timeout 600000 \
|
||||
https://github.com/datarhei/restreamer-ui.git .
|
||||
|
||||
# 2. Layer Wild Dragon overlays on top of the upstream tree before the
|
||||
# build runs. apply-overlay.sh does the rsync + targeted seds; see
|
||||
# deploy/truenas/core/ui-overlay/apply-overlay.sh for the contract.
|
||||
COPY deploy/truenas/core/ui-overlay /overlay
|
||||
RUN OVERLAY=/overlay UI=/ui /overlay/apply-overlay.sh
|
||||
|
||||
# 3. Install + build. PUBLIC_URL=./ keeps asset references relative so
|
||||
# the bundle is portable across mount paths.
|
||||
RUN yarn install --frozen-lockfile --network-timeout 600000 \
|
||||
&& PUBLIC_URL="./" GENERATE_SOURCEMAP=false yarn build
|
||||
|
||||
# ---- runtime ----
|
||||
|
|
|
|||
70
deploy/truenas/core/ui-overlay/apply-overlay.sh
Executable file
70
deploy/truenas/core/ui-overlay/apply-overlay.sh
Executable file
|
|
@ -0,0 +1,70 @@
|
|||
#!/bin/sh
|
||||
# apply-overlay.sh — Wild Dragon reskin patches applied to a freshly
|
||||
# cloned datarhei/restreamer-ui tree. Two phases:
|
||||
#
|
||||
# 1. File overlay: rsync the contents of $OVERLAY/{public,src} on top
|
||||
# of the upstream working tree. Whole-file replacements only —
|
||||
# simple and idempotent.
|
||||
#
|
||||
# 2. Targeted in-place sed for one-line UI strings that aren't worth
|
||||
# a whole-file overlay (the header title, a few welcome strings).
|
||||
# Each pattern is anchored to a unique surrounding context so a
|
||||
# future upstream rename doesn't silently rewrite the wrong line.
|
||||
#
|
||||
# Caller: the Dockerfile's ui-builder stage. Expects:
|
||||
# $OVERLAY = /overlay (the COPY destination)
|
||||
# $UI = /ui (the cloned upstream source root)
|
||||
#
|
||||
# Idempotent on a single source tree (rerunning is a no-op).
|
||||
|
||||
set -eu
|
||||
|
||||
OVERLAY="${OVERLAY:-/overlay}"
|
||||
UI="${UI:-/ui}"
|
||||
|
||||
echo "wilddragon-overlay: layering $OVERLAY -> $UI"
|
||||
|
||||
# Phase 1 — file copies. -L follows any future symlinks, -p preserves
|
||||
# perms, -R recursive. We deliberately avoid --delete: the upstream
|
||||
# tree must stay intact except for the files we override.
|
||||
for sub in public src; do
|
||||
if [ -d "$OVERLAY/$sub" ]; then
|
||||
cp -RLp "$OVERLAY/$sub/." "$UI/$sub/"
|
||||
fi
|
||||
done
|
||||
|
||||
# Phase 2 — targeted seds. Each replacement is wrapped in a check so
|
||||
# the script fails loudly if upstream changed the line we're patching
|
||||
# (rather than silently no-op'ing and shipping un-rebranded UI).
|
||||
patch_line() {
|
||||
file="$1"; needle="$2"; replacement="$3"
|
||||
if ! grep -qF "$needle" "$file"; then
|
||||
echo "wilddragon-overlay: ERROR — pattern not found in $file:"
|
||||
echo " $needle"
|
||||
exit 1
|
||||
fi
|
||||
# Use awk for safe literal substitution (sed's regex would mishandle
|
||||
# special chars in the replacement).
|
||||
tmp="$(mktemp)"
|
||||
awk -v n="$needle" -v r="$replacement" '
|
||||
index($0, n) { sub(n, r); }
|
||||
{ print }
|
||||
' "$file" > "$tmp"
|
||||
mv "$tmp" "$file"
|
||||
echo "wilddragon-overlay: patched $(basename "$file") — $needle -> $replacement"
|
||||
}
|
||||
|
||||
patch_line "$UI/src/Header.js" \
|
||||
'<Typography className="headerTitle">Restreamer</Typography>' \
|
||||
'<Typography className="headerTitle">Wild Dragon</Typography>'
|
||||
|
||||
# Welcome view top-of-page card.
|
||||
patch_line "$UI/src/views/Welcome.js" \
|
||||
'title="Welcome to Restreamer v2"' \
|
||||
'title="Welcome to Wild Dragon"'
|
||||
|
||||
patch_line "$UI/src/views/Settings.js" \
|
||||
'title="Welcome to Restreamer v2"' \
|
||||
'title="Welcome to Wild Dragon"'
|
||||
|
||||
echo "wilddragon-overlay: done."
|
||||
BIN
deploy/truenas/core/ui-overlay/public/favicon.ico
Normal file
BIN
deploy/truenas/core/ui-overlay/public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
17
deploy/truenas/core/ui-overlay/public/index.html
Normal file
17
deploy/truenas/core/ui-overlay/public/index.html
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="favicon.ico" />
|
||||
<meta name="viewport" content="minimum-scale=1, initial-scale=1, width=device-width" />
|
||||
<meta name="theme-color" content="#0d0e12" />
|
||||
<meta name="description" content="Wild Dragon — low-latency live video streaming dashboard" />
|
||||
<link rel="apple-touch-icon" href="logo192.png" />
|
||||
<link rel="manifest" href="manifest.json" />
|
||||
<title>Wild Dragon</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
BIN
deploy/truenas/core/ui-overlay/public/logo192.png
Normal file
BIN
deploy/truenas/core/ui-overlay/public/logo192.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.5 KiB |
BIN
deploy/truenas/core/ui-overlay/public/logo512.png
Normal file
BIN
deploy/truenas/core/ui-overlay/public/logo512.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
13
deploy/truenas/core/ui-overlay/public/manifest.json
Normal file
13
deploy/truenas/core/ui-overlay/public/manifest.json
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"short_name": "Wild Dragon",
|
||||
"name": "Wild Dragon — Live Streaming",
|
||||
"icons": [
|
||||
{ "src": "favicon.ico", "sizes": "64x64 32x32 16x16", "type": "image/x-icon" },
|
||||
{ "src": "logo192.png", "type": "image/png", "sizes": "192x192" },
|
||||
{ "src": "logo512.png", "type": "image/png", "sizes": "512x512" }
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#0d0e12",
|
||||
"background_color": "#0d0e12"
|
||||
}
|
||||
24
deploy/truenas/core/ui-overlay/src/misc/Logo/images/logo.svg
Normal file
24
deploy/truenas/core/ui-overlay/src/misc/Logo/images/logo.svg
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 70" width="320" height="70">
|
||||
<!-- Wild Dragon wordmark: small ember+chevron icon followed by the text. -->
|
||||
<defs>
|
||||
<linearGradient id="ember-w" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0" stop-color="#ff8855"/>
|
||||
<stop offset="1" stop-color="#cc3300"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<!-- icon -->
|
||||
<g transform="translate(0,4)">
|
||||
<rect x="2" y="2" width="58" height="58" rx="10" fill="#1a1c22"/>
|
||||
<path d="M14 48 Q22 30 31 38 Q40 30 48 48 Q40 53 31 47 Q22 53 14 48 Z"
|
||||
fill="url(#ember-w)" opacity="0.7"/>
|
||||
<text x="31" y="40" text-anchor="middle"
|
||||
font-family="'DejaVu Sans','Helvetica',sans-serif"
|
||||
font-size="26" font-weight="700" fill="#ff6633">WD</text>
|
||||
</g>
|
||||
<!-- wordmark -->
|
||||
<text x="76" y="48"
|
||||
font-family="'Dosis','Roboto','Helvetica',sans-serif"
|
||||
font-size="36" font-weight="300" letter-spacing="2" fill="#e7e7ea">
|
||||
WILD <tspan fill="#ff6633" font-weight="500">DRAGON</tspan>
|
||||
</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1 KiB |
|
|
@ -0,0 +1,19 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" width="200" height="200">
|
||||
<!-- Wild Dragon mark: dark rounded panel with stylised flame chevron + 'WD' monogram. -->
|
||||
<defs>
|
||||
<linearGradient id="ember" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0" stop-color="#ff6633"/>
|
||||
<stop offset="1" stop-color="#cc3300"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect x="6" y="6" width="188" height="188" rx="32" fill="#0d0e12"/>
|
||||
<!-- Flame chevron underneath the monogram -->
|
||||
<path d="M40 150 Q60 110 100 130 Q140 110 160 150 Q140 165 100 152 Q60 165 40 150 Z"
|
||||
fill="url(#ember)" opacity="0.55"/>
|
||||
<!-- 'W' -->
|
||||
<path d="M50 60 L62 130 L78 90 L94 130 L106 60"
|
||||
stroke="#ff6633" stroke-width="10" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
||||
<!-- 'D' -->
|
||||
<path d="M118 60 L118 130 L138 130 Q165 130 165 95 Q165 60 138 60 L118 60 Z"
|
||||
stroke="#ff6633" stroke-width="10" stroke-linejoin="round" fill="none"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 975 B |
24
deploy/truenas/core/ui-overlay/src/misc/Logo/index.js
Normal file
24
deploy/truenas/core/ui-overlay/src/misc/Logo/index.js
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
import React from 'react';
|
||||
|
||||
import makeStyles from '@mui/styles/makeStyles';
|
||||
|
||||
import company_logo from './images/logo.svg';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
Logo: {
|
||||
height: 27,
|
||||
},
|
||||
}));
|
||||
|
||||
export default function Logo(props) {
|
||||
const classes = useStyles();
|
||||
|
||||
let link = 'https://forge.wilddragon.net/zgaetano/datarhei-dragonfork-core';
|
||||
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
return (
|
||||
<a href={link} className={classes.Logo} target="_blank" rel="noopener noreferrer">
|
||||
<img src={company_logo} alt="Wild Dragon logo" />
|
||||
</a>
|
||||
);
|
||||
}
|
||||
24
deploy/truenas/core/ui-overlay/src/misc/Logo/rsLogo.js
Normal file
24
deploy/truenas/core/ui-overlay/src/misc/Logo/rsLogo.js
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
import React from 'react';
|
||||
|
||||
import makeStyles from '@mui/styles/makeStyles';
|
||||
|
||||
import company_logo from './images/rs-logo.svg';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
Logo: {
|
||||
height: 95,
|
||||
},
|
||||
}));
|
||||
|
||||
export default function Logo(props) {
|
||||
const classes = useStyles();
|
||||
|
||||
let link = 'https://forge.wilddragon.net/zgaetano/datarhei-dragonfork-core';
|
||||
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
return (
|
||||
<a href={link} className={classes.Logo} target="_blank" rel="noopener noreferrer">
|
||||
<img src={company_logo} alt="Wild Dragon mark" />
|
||||
</a>
|
||||
);
|
||||
}
|
||||
Loading…
Reference in a new issue