공개: alpha.4 기준선 갱신
This commit is contained in:
parent
debf62f76e
commit
b63832706b
37 changed files with 1839 additions and 822 deletions
206
scripts/ci/capture-kotalk-desktop-screenshots.sh
Executable file
206
scripts/ci/capture-kotalk-desktop-screenshots.sh
Executable file
|
|
@ -0,0 +1,206 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
OUTPUT_DIR="${1:-$ROOT_DIR/docs/assets/latest}"
|
||||
CAPTURE_MODE="${2:-all}"
|
||||
PROJECT_PATH="$ROOT_DIR/src/PhysOn.Desktop/PhysOn.Desktop.csproj"
|
||||
DOTNET_BIN="${DOTNET_BIN:-$HOME/.dotnet/dotnet}"
|
||||
|
||||
if [[ ! -x "$DOTNET_BIN" ]]; then
|
||||
echo "dotnet not found at $DOTNET_BIN" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
TMP_DIR="$(mktemp -d)"
|
||||
trap 'rm -rf "$TMP_DIR"' EXIT
|
||||
|
||||
capture_mode() {
|
||||
local mode="$1"
|
||||
local output_path="$2"
|
||||
local main_title="$3"
|
||||
local detached_title="${4:-}"
|
||||
local data_home="$TMP_DIR/$mode-data"
|
||||
local config_home="$TMP_DIR/$mode-config"
|
||||
local cache_home="$TMP_DIR/$mode-cache"
|
||||
local runtime_home="$TMP_DIR/$mode-runtime"
|
||||
local tree_path="$TMP_DIR/$mode-tree.txt"
|
||||
local log_path="$TMP_DIR/$mode.log"
|
||||
mkdir -p "$data_home" "$config_home" "$cache_home" "$runtime_home"
|
||||
|
||||
env \
|
||||
XDG_DATA_HOME="$data_home" \
|
||||
XDG_CONFIG_HOME="$config_home" \
|
||||
XDG_CACHE_HOME="$cache_home" \
|
||||
XDG_RUNTIME_DIR="$runtime_home" \
|
||||
DOTNET_BIN="$DOTNET_BIN" \
|
||||
PROJECT_PATH="$PROJECT_PATH" \
|
||||
TREE_PATH="$tree_path" \
|
||||
LOG_PATH="$log_path" \
|
||||
OUTPUT_PATH="$output_path" \
|
||||
MAIN_TITLE="$main_title" \
|
||||
DETACHED_TITLE="$detached_title" \
|
||||
MODE="$mode" \
|
||||
xvfb-run -a bash -lc '
|
||||
set -euo pipefail
|
||||
|
||||
refresh_tree() {
|
||||
xwininfo -root -tree >"$TREE_PATH" 2>/dev/null || true
|
||||
}
|
||||
|
||||
find_window_id() {
|
||||
local title="$1"
|
||||
python3 - "$TREE_PATH" "$title" <<'"'"'PY'"'"'
|
||||
import re
|
||||
import sys
|
||||
|
||||
tree_path, title = sys.argv[1], sys.argv[2]
|
||||
pattern = re.compile(r"^\s*(0x[0-9a-fA-F]+)\s+\"([^\"]+)\"")
|
||||
|
||||
with open(tree_path, "r", encoding="utf-8", errors="ignore") as handle:
|
||||
for line in handle:
|
||||
match = pattern.match(line)
|
||||
if not match:
|
||||
continue
|
||||
window_id, window_title = match.groups()
|
||||
if window_title == title:
|
||||
print(window_id)
|
||||
raise SystemExit(0)
|
||||
|
||||
raise SystemExit(1)
|
||||
PY
|
||||
}
|
||||
|
||||
wait_for_window() {
|
||||
local title="$1"
|
||||
local attempts="${2:-40}"
|
||||
local pause="${3:-0.5}"
|
||||
local window_id=""
|
||||
|
||||
for _ in $(seq 1 "$attempts"); do
|
||||
refresh_tree
|
||||
if window_id="$(find_window_id "$title" 2>/dev/null)"; then
|
||||
echo "$window_id"
|
||||
return 0
|
||||
fi
|
||||
sleep "$pause"
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
window_geometry() {
|
||||
local window_id="$1"
|
||||
python3 - "$window_id" <<'"'"'PY'"'"'
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
window_id = sys.argv[1]
|
||||
output = subprocess.check_output(["xwininfo", "-id", window_id], text=True, errors="ignore")
|
||||
patterns = {
|
||||
"x": r"Absolute upper-left X:\s+(-?\d+)",
|
||||
"y": r"Absolute upper-left Y:\s+(-?\d+)",
|
||||
"w": r"Width:\s+(\d+)",
|
||||
"h": r"Height:\s+(\d+)",
|
||||
}
|
||||
|
||||
values = {}
|
||||
for key, pattern in patterns.items():
|
||||
match = re.search(pattern, output)
|
||||
if not match:
|
||||
raise SystemExit(1)
|
||||
values[key] = int(match.group(1))
|
||||
|
||||
if values["w"] < 240 or values["h"] < 240:
|
||||
raise SystemExit(2)
|
||||
|
||||
print(values["x"], values["y"], values["w"], values["h"])
|
||||
PY
|
||||
}
|
||||
|
||||
wait_for_geometry() {
|
||||
local window_id="$1"
|
||||
local attempts="${2:-30}"
|
||||
local pause="${3:-0.4}"
|
||||
local geometry=""
|
||||
local previous=""
|
||||
|
||||
for _ in $(seq 1 "$attempts"); do
|
||||
if geometry="$(window_geometry "$window_id" 2>/dev/null)"; then
|
||||
if [[ "$geometry" == "$previous" ]]; then
|
||||
echo "$geometry"
|
||||
return 0
|
||||
fi
|
||||
previous="$geometry"
|
||||
fi
|
||||
sleep "$pause"
|
||||
done
|
||||
|
||||
if [[ -n "$previous" ]]; then
|
||||
echo "$previous"
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
capture_window() {
|
||||
local window_id="$1"
|
||||
local target_path="$2"
|
||||
local root_capture="${target_path%.*}-root.${target_path##*.}"
|
||||
local geometry
|
||||
read -r crop_x crop_y crop_w crop_h < <(wait_for_geometry "$window_id")
|
||||
import -window root "$root_capture"
|
||||
convert "$root_capture" -crop "${crop_w}x${crop_h}+${crop_x}+${crop_y}" +repage "$target_path"
|
||||
rm -f "$root_capture"
|
||||
}
|
||||
|
||||
create_conversation_fallback() {
|
||||
local source_path="$1"
|
||||
local target_path="$2"
|
||||
convert "$source_path" -gravity east -crop 58%x84%+0+0 +repage "$target_path"
|
||||
}
|
||||
|
||||
if [[ "$MODE" == "sample" ]]; then
|
||||
export KOTALK_DESKTOP_SAMPLE_MODE=1
|
||||
export KOTALK_DESKTOP_OPEN_SAMPLE_WINDOW=1
|
||||
fi
|
||||
export XDG_DATA_HOME="$XDG_DATA_HOME"
|
||||
export XDG_CONFIG_HOME="$XDG_CONFIG_HOME"
|
||||
export XDG_CACHE_HOME="$XDG_CACHE_HOME"
|
||||
export XDG_RUNTIME_DIR="$XDG_RUNTIME_DIR"
|
||||
|
||||
"$DOTNET_BIN" run --project "$PROJECT_PATH" -c Debug >"$LOG_PATH" 2>&1 &
|
||||
app_pid=$!
|
||||
|
||||
cleanup() {
|
||||
kill "$app_pid" >/dev/null 2>&1 || true
|
||||
wait "$app_pid" >/dev/null 2>&1 || true
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
main_id="$(wait_for_window "$MAIN_TITLE" 60 0.5)"
|
||||
capture_window "$main_id" "$OUTPUT_PATH"
|
||||
|
||||
if [[ -n "$DETACHED_TITLE" ]]; then
|
||||
conversation_output="${OUTPUT_PATH%/*}/conversation.${OUTPUT_PATH##*.}"
|
||||
if detached_id="$(wait_for_window "$DETACHED_TITLE" 12 0.5 2>/dev/null)"; then
|
||||
capture_window "$detached_id" "$conversation_output"
|
||||
else
|
||||
create_conversation_fallback "$OUTPUT_PATH" "$conversation_output"
|
||||
fi
|
||||
fi
|
||||
'
|
||||
}
|
||||
|
||||
if [[ "$CAPTURE_MODE" == "all" || "$CAPTURE_MODE" == "onboarding" ]]; then
|
||||
capture_mode "onboarding" "$OUTPUT_DIR/onboarding.png" "KoTalk"
|
||||
fi
|
||||
|
||||
if [[ "$CAPTURE_MODE" == "all" || "$CAPTURE_MODE" == "sample" ]]; then
|
||||
capture_mode "sample" "$OUTPUT_DIR/hero-shell.png" "KoTalk" "제품 운영"
|
||||
fi
|
||||
|
||||
echo "Desktop screenshots written to $OUTPUT_DIR"
|
||||
Loading…
Add table
Add a link
Reference in a new issue