docs-screenshot
GitHub用于生成符合Silo文档规范的600x400 UI截图,自动合成至标准壁纸背景。通过API捕获应用状态并裁剪拼接,确保图片风格与指南一致,适用于更新或添加应用文档配图。
触发场景
安装
npx skills add silo-code/silo --skill docs-screenshot -g -y
SKILL.md
Frontmatter
{
"name": "docs-screenshot",
"tools": "Bash, Read",
"description": "Capture a 600×400 documentation screenshot from the running Silo app and composite it on the standard wallpaper. Use whenever adding or updating screenshots in apps\/docs\/public\/img\/guide\/."
}
Silo Docs Screenshot
Produces a 600×400 PNG of a Silo UI region composited over the standard orange/purple
wallpaper (apps/docs/public/img/wallpaper.jpg), styled as a windowed screenshot floating
on the desktop — consistent with every other image in the guide.
Quick reference
Corner Desktop visible Window bleeds Window canvas pos Merged geometry Round corner
────────── ───────────────── ─────────────── ───────────────────── ─────────────────── ────────────
top-left top(65) left(80) right, bottom (80, 65) +40+30 all 4
bot-left bot(10) left(80) top, right (80, 0) +40+-35 bot-left only
top-right top(65) right(80) left, bottom (520-OW, 65) +(480-OW)+30 top-right only
bot-right bot(10) right(80) top, left (520-OW, 390-OH) +(480-OW)+(355-OH) bot-right only
center all sides ~50px none ((600-OW)/2, (400-OH)/2) +((600-OW)/2-40)+((400-OH)/2-35) all 4
top-left — top half of app, feature on the LEFT (e.g. workspaces panel, editor).
bot-left — bottom of app, feature on the LEFT (e.g. status bar menu).
top-right — top half of app, feature on the RIGHT (e.g. file explorer, right side panel).
bot-right — bottom of app, feature on the RIGHT (e.g. right-side status items, output panel).
Pipeline
1. Take the screenshot
silo(){ curl -s -m30 -X POST http://127.0.0.1:7878/ \
-H 'X-Silo-Automation: 1' -H 'Content-Type: application/json' \
--data "$1"; }
# Set up any app state first (open panels, menus, etc.), then:
silo '{"op":"screenshot"}' > /tmp/shot.json
python3 -c "
import json,base64
d=json.load(open('/tmp/shot.json'))
r=d['result']
open('/tmp/silo.png','wb').write(base64.b64decode(r['png_base64']))
print(r['width'], r['height'])
"
# Typical output: 3024 1898 (2× retina — logical pixels are half)
2. Choose your crop
The source is 3024×1898 at 2× retina (logical: 1512×949).
Pick the region of interest in 2× pixels and a window output size that fits the composition:
| Style | Typical output window | Typical source crop | Scale |
|---|---|---|---|
| top-left | 580×439 | 900×680 from (0,0) | 0.644× |
| bot-left | 520×390 | 1131×848 from (0,Y) | 0.460× |
Crop rules:
- Source crop
W×Hand output windoww×hmust satisfyW/H = w/h(same aspect ratio). - For top-left: start at (0,0); choose crop height to include the feature; bleed at right + bottom is automatic since
w > 600-80andh > 400-65. - For bot-left: start high enough (Y) that the full feature is inside the crop; the crop bottom is always
1898(window bottom). Status bar =y≈1050is a safe start for a menu+statusbar shot. - To zoom in more: use a smaller source crop (same output window size → larger apparent zoom).
- To zoom out / show more context: use a larger source crop.
3. Compose
WALL="apps/docs/public/img/wallpaper.jpg"
DOCS="apps/docs/public/img/guide"
APP=/tmp/silo.png # or your saved screenshot
# ── Variables ──────────────────────────────────────────────────────────────
CORNER=top-left # top-left | bot-left
CW=900; CH=680 # source crop width × height (2× pixels)
CX=0; CY=0 # source crop origin (2× pixels)
OW=580; OH=439 # output window width × height (pixels)
OUT="$DOCS/feature.png" # destination
# ── Wallpaper background ───────────────────────────────────────────────────
magick "$WALL" -resize "600x400^" -gravity center -extent 600x400 /tmp/bg.png
# ── Crop + scale ───────────────────────────────────────────────────────────
magick "$APP" -crop ${CW}x${CH}+${CX}+${CY} +repage -resize ${OW}x${OH} /tmp/win-scaled.png
# ── Mouse cursor (optional) ────────────────────────────────────────────────
# Use when you want to indicate where the user should click.
# IMPORTANT: Do NOT use SVG — ImageMagick renders it as a plain white square.
# Draw the cursor natively instead. Cursor tip is at pixel (2,2) within the image.
#
# TIP_X=48; TIP_Y=265 # desired tip position in output-window coordinates
# magick -size 24x28 xc:none \
# -fill white -stroke black -strokewidth 1.5 \
# -draw "polygon 2,2 2,22 7,17 11,26 14,24 10,15 18,15" \
# /tmp/cursor.png
# magick /tmp/win-scaled.png /tmp/cursor.png \
# -geometry +$((TIP_X-2))+$((TIP_Y-2)) -composite /tmp/win-scaled.png
#
# Placement rule: subtract 2 from both axes (tip offset within cursor image).
# Place the cursor AFTER crop+scale but BEFORE rounded corners so it gets
# clipped correctly if it's near an edge.
# ── Rounded corners ────────────────────────────────────────────────────────
R=10
case "$CORNER" in
top-left)
# All 4 corners — top-left is the real window corner; others bleed but rounding is consistent
magick /tmp/win-scaled.png \
\( +clone -alpha extract -fill black -colorize 100 \
-fill white -draw "roundrectangle 0,0 $((OW-1)),$((OH-1)) 10,10" \) \
-alpha off -compose CopyOpacity -composite /tmp/win-rounded.png
;;
bot-left)
# Round bottom-left corner only; top bleeds off canvas, right bleeds off canvas
magick /tmp/win-scaled.png \
\( +clone -alpha extract -fill black -colorize 100 \
-fill white -draw "rectangle 0,0 $((OW-1)),$((OH-1))" \
-fill black -draw "rectangle 0,$((OH-R)) $((R-1)),$((OH-1))" \
-fill white -draw "circle ${R},$((OH-R)) 0,$((OH-R))" \
\) -alpha off -compose CopyOpacity -composite /tmp/win-rounded.png
;;
top-right)
# Round top-right corner only; left bleeds off canvas, bottom bleeds off canvas
magick /tmp/win-scaled.png \
\( +clone -alpha extract -fill black -colorize 100 \
-fill white -draw "rectangle 0,0 $((OW-1)),$((OH-1))" \
-fill black -draw "rectangle $((OW-R)),0 $((OW-1)),$((R-1))" \
-fill white -draw "circle $((OW-R)),${R} $((OW-1)),${R}" \
\) -alpha off -compose CopyOpacity -composite /tmp/win-rounded.png
;;
bot-right)
# Round bottom-right corner only; top bleeds off canvas, left bleeds off canvas
magick /tmp/win-scaled.png \
\( +clone -alpha extract -fill black -colorize 100 \
-fill white -draw "rectangle 0,0 $((OW-1)),$((OH-1))" \
-fill black -draw "rectangle $((OW-R)),$((OH-R)) $((OW-1)),$((OH-1))" \
-fill white -draw "circle $((OW-R)),$((OH-R)) $((OW-1)),$((OH-R))" \
\) -alpha off -compose CopyOpacity -composite /tmp/win-rounded.png
;;
esac
# ── Drop shadow ────────────────────────────────────────────────────────────
# shadow 90x20+0+10 → window lands at (40,35) inside the merged image
magick /tmp/win-rounded.png \
\( +clone -background '#00000099' -shadow 90x20+0+10 \) \
+swap -background none -layers merge +repage /tmp/win-shadowed.png
# ── Composite onto background ──────────────────────────────────────────────
# Shadow offset: window in merged image sits at (40,35). Formula: geometry = +(canvas_x−40)+(canvas_y−35)
# top-left: window canvas (80, 65) → +40+30
# bot-left: window canvas (80, 0) → +40+-35
# top-right: window canvas (520−OW, 65) → +(480−OW)+30
# bot-right: window canvas (520−OW, 390−OH) → +(480−OW)+(355−OH)
case "$CORNER" in
top-left) GX=40; GY=30 ;;
bot-left) GX=40; GY=$((-35)) ;;
top-right) GX=$((480-OW)); GY=30 ;;
bot-right) GX=$((480-OW)); GY=$((355-OH)) ;;
esac
magick /tmp/bg.png /tmp/win-shadowed.png -geometry +${GX}+${GY} -composite "$OUT"
magick identify -format "%wx%h\n" "$OUT" # must print 600x400
Shadow offset math (reference)
-shadow 90x20+0+10 with layers merge places the source window at (40, 35) inside
the merged RGBA image regardless of the window dimensions.
Formula: -geometry +(canvas_x − 40)+(canvas_y − 35)
Verify any time the shadow params change:
magick -size 100x100 xc:red \
\( +clone -background '#00000099' -shadow 90x20+0+10 \) \
+swap -background none -layers merge +repage /tmp/t.png
magick identify /tmp/t.png # merged WxH
# window_x_offset = (merged_W - 100) / 2
# window_y_offset determined empirically or via: (merged_H - 100 - 10) / 2
Common shots
| Guide section | Corner | Source crop (2×) | Output window | Output file |
|---|---|---|---|---|
| Workspaces panel | top-left | 900×680 from (0,0) | 580×439 | workspaces-panel.png |
| Workspaces + button | top-left | 900×680 from (0,0) | 580×439 | workspaces-open.png (cursor at + button tip≈(48,265)) |
| Status bar + menu | bot-left | 1131×848 from (0,1050) | 520×390 | workspaces-statusbar.png |
| File explorer panel | top-right | 1324×1000 from (1700,0) | 580×438 | panels-file-explorer.png |
| Getting started | center | full window (no crop) | 480×304 | getting-started-app.png |
Rules
- Output is always 600×400 — verify with
magick identify. - Never cut the app artificially — the window must be a real contiguous region of the app, just cropped at the canvas edge due to bleed.
- Zoom into the feature — pick a crop tight enough that the relevant UI is clearly readable.
- Close any menus or dialogs after capturing so the app is left in a clean state.
- Always embed with
width="400"— the 600px image at 400 CSS pixels gives a sharp 1.5× render on retina displays without being too small. Never usewidth="600"(blurry on retina) orwidth="300"(too small).
版本历史
- 95dfc72 当前 2026-08-27 21:32


