Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified docs/feat-breakdown-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/feat-breakdown.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/feat-cap-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/feat-cap.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/feat-limits-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/feat-limits.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/feat-projects-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/feat-projects.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/shot-cost-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/shot-cost.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/shot-history-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/shot-history.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/shot-now-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/shot-now.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/shot-tasks-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/shot-tasks.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
32 changes: 27 additions & 5 deletions scripts/feature-shots.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,28 @@ def main():
for theme in ("dark", "light"):
for name, route, needle, _why in SHOTS:
rpc(ws, "Page.navigate", {"url": f"{BASE}/#/{route}"})
time.sleep(3.5)
# Wait for the panel rather than for the clock. A fixed pause
# was enough on a small database and silently was not on a real
# one: every shot failed with "no element matching", which
# reads like a renamed panel and is really a screen that had
# not finished fetching. Also put the first-run banners away,
# or they push the panel down and the clip lands on the strip
# above it.
evaluate(ws, """
try {
localStorage.setItem('caprock.update.dismissed', 'offer');
const t = Date.now();
localStorage.setItem('caprock-prompts', JSON.stringify({
'premium-banner': t, 'premium-hint': t, 'share-month': t,
}));
} catch (e) {}
""")
evaluate(ws, f"localStorage.setItem('caprock-theme','{theme}');location.reload()")
time.sleep(3.5)
for _ in range(60):
time.sleep(0.4)
if evaluate(ws, f"document.body.innerText.includes({json.dumps(needle)})"):
break
time.sleep(2.0) # let the bars and numbers paint

# Find the panel whose heading matches, and take its box. The
# walk goes up to the panel container so the border and title
Expand All @@ -115,15 +134,18 @@ def main():
print(f" !! {name}: no element matching {needle!r} on /{route}")
continue

pad = 12
# Asymmetric padding: 12px is right on three sides, but at the
# top it reached into the panel above and clipped a button in
# half, which reads as a broken screenshot rather than a crop.
pad, pad_top = 12, 2
shot = rpc(ws, "Page.captureScreenshot", {
"format": "png",
"captureBeyondViewport": True,
"clip": {
"x": max(0, box["x"] - pad),
"y": max(0, box["y"] - pad),
"y": max(0, box["y"] - pad_top),
"width": min(WIDTH, box["w"] + pad * 2),
"height": box["h"] + pad * 2,
"height": box["h"] + pad + pad_top,
"scale": 2,
},
})
Expand Down
21 changes: 21 additions & 0 deletions scripts/refresh-shots.sh
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,27 @@ echo "→ snapshotting the database (read-only; the live one is untouched)"
mkdir -p "$WORK/data"
sqlite3 "$DATA_DIR/caprock.db" ".backup '$WORK/data/caprock.db'"

# Carry over the plan, and nothing else from the config.
#
# The plan lives in config.json rather than the database, so a throwaway data
# directory has none — and the header then shows "set plan" lit as an unfinished
# action, which is the first thing a reader of the README sees: an instruction
# to configure something they have not been told about yet. Three fields, copied
# by name. Never the whole file: it also holds the report bot token.
if [ -f "$DATA_DIR/config.json" ]; then
python3 - "$DATA_DIR/config.json" "$WORK/data/config.json" <<'PLAN'
import json, sys
src, dst = sys.argv[1], sys.argv[2]
try:
d = json.load(open(src))
except Exception:
sys.exit(0)
keep = {k: d[k] for k in ("plan_kind", "plan_label", "plan_usd_per_month") if k in d}
if keep:
json.dump(keep, open(dst, "w"), indent=2)
PLAN
fi

# The published binary, not a working tree build: the header carries the
# version, and a README showing "dev build" tells the reader they are looking
# at something unreleased. CAPROCK_SHOT_BIN overrides it for testing an
Expand Down
32 changes: 29 additions & 3 deletions scripts/shots.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,13 +252,39 @@ def main():
ws = create_connection(ws_url, timeout=30)
rpc(ws, "Page.enable"); rpc(ws, "Runtime.enable")

# Put the first-run prompts away before the first render, not after it.
#
# Both banners are answered from localStorage at mount, so writing the
# keys once the screen is up changes nothing that is already on it —
# and the navigations that follow are hash changes, which do not reload
# the document and so never re-read the flag. The result was two
# dismissable strips across the top of every capture, taking a sixth of
# the frame before a single figure appeared. Set the keys against the
# origin, then load the document once so they are read.
rpc(ws, "Page.navigate", {"url": f"{BASE}/#/now"})
time.sleep(1.5)
evaluate(ws, """
try {
localStorage.setItem('caprock.update.dismissed', 'offer');
const t = Date.now();
localStorage.setItem('caprock-prompts', JSON.stringify({
'premium-banner': t, 'premium-hint': t, 'share-month': t,
}));
} catch (e) {}
""")

for theme in ("dark", "light"):
for route, name in SHOTS:
# Navigating to the same document with a different hash does
# not reload it, and the dismissal flags above are only read as
# the document boots. Go by way of about:blank so each capture
# is a real load *of its own route* — Page.reload would reload
# the document at its default route and lose the hash, which
# silently made every shot the Now screen.
rpc(ws, "Page.navigate", {"url": "about:blank"})
time.sleep(0.3)
rpc(ws, "Page.navigate", {"url": f"{BASE}/#/{route}"})
time.sleep(1.5)
# Dismiss the update banner: a first-run prompt, not a feature,
# and it pushes the dashboard down in every capture.
evaluate(ws, "try{localStorage.setItem('caprock.update.dismissed','offer')}catch(e){}")
# Toggle to the theme we want by clicking the control, which is
# what a person does. Writing to storage only takes effect on
# the next document load, and a hash navigation is not one.
Expand Down
Loading