feat(quickshell): add dense telemetry bar
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
FROM nixos/nix:2.30.2
|
||||
|
||||
ARG NIXPKGS_REV=e5bdc4a41d4c072fe1e3787eaa0320a384741d44
|
||||
ARG QSMCP_REV=76b1b008f0352064121af9334065012e31d80fc2
|
||||
|
||||
ENV NIX_CONFIG="experimental-features = nix-command flakes" \
|
||||
PATH="/nix/var/nix/profiles/default/bin:${PATH}"
|
||||
|
||||
RUN nix profile install --profile /nix/var/nix/profiles/default \
|
||||
"github:NixOS/nixpkgs/${NIXPKGS_REV}#quickshell" \
|
||||
"github:NixOS/nixpkgs/${NIXPKGS_REV}#nodejs_22"
|
||||
|
||||
RUN mkdir -p /opt/qsmcp \
|
||||
&& git init /opt/qsmcp \
|
||||
&& git -C /opt/qsmcp remote add origin https://github.com/fedsfarm/qsmcp.git \
|
||||
&& git -C /opt/qsmcp fetch --depth 1 origin "${QSMCP_REV}" \
|
||||
&& git -C /opt/qsmcp checkout --detach FETCH_HEAD \
|
||||
&& test "$(git -C /opt/qsmcp rev-parse HEAD)" = "${QSMCP_REV}" \
|
||||
&& cd /opt/qsmcp \
|
||||
&& npm ci --omit=dev \
|
||||
&& rm -rf /opt/qsmcp/.git
|
||||
|
||||
# qsmcp's CLI hard-codes a 1568px safety cap. Desktop bar previews need their
|
||||
# true target width so responsive layout and text collisions are testable.
|
||||
RUN node -e 'const fs=require("fs"); const p="/opt/qsmcp/src/index.ts"; const s=fs.readFileSync(p,"utf8"); const old="max_dimension: 1568"; if (!s.includes(old)) throw new Error("qsmcp max_dimension source changed"); fs.writeFileSync(p,s.replace(old,"max_dimension: 4096"))'
|
||||
|
||||
ENV QSMCP_SHELL_ROOT=/workspace/quickshell \
|
||||
QSMCP_QS_BIN=/nix/var/nix/profiles/default/bin/qs \
|
||||
QT_QUICK_BACKEND=software
|
||||
|
||||
COPY entrypoint.sh /usr/local/bin/quickshell-preview
|
||||
RUN chmod +x /usr/local/bin/quickshell-preview
|
||||
|
||||
RUN mkdir -p /workspace/quickshell
|
||||
WORKDIR /workspace/quickshell
|
||||
|
||||
ENTRYPOINT ["/usr/local/bin/quickshell-preview"]
|
||||
@@ -0,0 +1,39 @@
|
||||
# Headless Quickshell previews
|
||||
|
||||
This directory provides a reproducible Docker wrapper around
|
||||
[qsmcp](https://github.com/fedsfarm/qsmcp). It renders a Quickshell `Item` to a
|
||||
PNG with Qt's `grabToImage()` without connecting to the host Wayland session.
|
||||
|
||||
Both nixpkgs and qsmcp are pinned in `Dockerfile`. The runner uses
|
||||
`docker create` plus `docker cp` rather than bind mounts, so it also works with
|
||||
rootless/remote daemons whose mount namespace cannot see the checkout.
|
||||
|
||||
## Smoke test
|
||||
|
||||
Start Docker, then run from the repository root:
|
||||
|
||||
```sh
|
||||
./tools/quickshell-preview/render.sh
|
||||
```
|
||||
|
||||
The default output is:
|
||||
|
||||
```text
|
||||
/tmp/homelab-quickshell-preview/smoke.png
|
||||
```
|
||||
|
||||
## Render another Item-rooted component
|
||||
|
||||
```sh
|
||||
./tools/quickshell-preview/render.sh \
|
||||
tests/DenseBarHeadless.qml \
|
||||
.artifacts/quickshell-preview/dense-bar-1920.png \
|
||||
1920 164
|
||||
```
|
||||
|
||||
The component path is relative to `dotfiles/quickshell`. Keep production
|
||||
`PanelWindow` wrappers thin and put their visual content in an `Item` component
|
||||
so it can be rendered through this offscreen path.
|
||||
|
||||
`PanelWindow`-rooted previews require a nested compositor and are intentionally
|
||||
outside this first smoke-test image.
|
||||
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
mkdir -p /tmp/qsmcp-runtime /tmp/qsmcp-home /output
|
||||
chmod 700 /tmp/qsmcp-runtime
|
||||
|
||||
export HOME=/tmp/qsmcp-home
|
||||
export XDG_RUNTIME_DIR=/tmp/qsmcp-runtime
|
||||
|
||||
exec /nix/var/nix/profiles/default/bin/node \
|
||||
/opt/qsmcp/src/index.ts render "$@"
|
||||
Executable
+58
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
repo_root="$(git -C "$(dirname "${BASH_SOURCE[0]}")" rev-parse --show-toplevel)"
|
||||
tool_dir="$repo_root/tools/quickshell-preview"
|
||||
shell_root="$repo_root/dotfiles/quickshell"
|
||||
image="homelab-quickshell-preview:qsmcp-76b1b008"
|
||||
|
||||
component="${1:-tests/HeadlessSmoke.qml}"
|
||||
output="${2:-${TMPDIR:-/tmp}/homelab-quickshell-preview/smoke.png}"
|
||||
width="${3:-720}"
|
||||
height="${4:-120}"
|
||||
|
||||
if ! docker info >/dev/null 2>&1; then
|
||||
echo "error: Docker-compatible daemon is unavailable" >&2
|
||||
exit 69
|
||||
fi
|
||||
|
||||
case "$output" in
|
||||
/*) ;;
|
||||
*) output="$PWD/$output" ;;
|
||||
esac
|
||||
|
||||
output_dir="$(dirname "$output")"
|
||||
output_name="$(basename "$output")"
|
||||
mkdir -p "$output_dir"
|
||||
|
||||
container_id=""
|
||||
cleanup() {
|
||||
if [[ -n "$container_id" ]]; then
|
||||
docker rm --force "$container_id" >/dev/null 2>&1 || true
|
||||
fi
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
docker build --tag "$image" --file "$tool_dir/Dockerfile" "$tool_dir"
|
||||
|
||||
container_id="$(docker create \
|
||||
--network none \
|
||||
--tmpfs /tmp:rw,noexec,nosuid,size=256m \
|
||||
"$image" \
|
||||
"$component" \
|
||||
--width "$width" \
|
||||
--height "$height" \
|
||||
--dpr 1 \
|
||||
--padding 0 \
|
||||
--background solid \
|
||||
--bg '#0a0a0a' \
|
||||
--out "/output/$output_name")"
|
||||
|
||||
# docker cp streams files through the API, so this works with rootless and
|
||||
# remote daemons whose filesystem cannot see the client's repository path.
|
||||
docker cp "$shell_root/." "$container_id:/workspace/quickshell"
|
||||
docker start --attach "$container_id"
|
||||
docker cp "$container_id:/output/$output_name" "$output"
|
||||
|
||||
test -s "$output"
|
||||
printf 'rendered=%s\n' "$output"
|
||||
Reference in New Issue
Block a user