From 45f4311993b01db40873f4ec9b3d37ad5e5ef17c Mon Sep 17 00:00:00 2001 From: Krzysztof Rudnicki Date: Mon, 27 Jul 2026 07:35:55 +0200 Subject: [PATCH] app_icons: add home_inventory storage-box glyph; whitelist it on the phone MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The new ~/home_inventory Flutter app needs two things from this repo: a family launcher icon, and a focus-mode whitelist entry so the daemon does not pm disable-user it ~1s after launch. The glyph is an isometric carton — a hexagon plus the three edges meeting at the near-top corner. A front-on rectangle with a lid seam was tried first and rejected on the contact sheet: a seam plus a divider reads as a browser window, not a box. Body is 488x440, not a rounder 500x440, because STROKE_WIDTH straddles the path and adds 72px to each rendered dimension; 488 is the widest body whose ink still fits SAFE_BOX. The generator warned about exactly this at 500. Accent stays the one shared family accent — apps differ by glyph, not colour. Co-Authored-By: Claude Opus 5 (1M context) --- ...ventory-icon-and-whitelist-2026-07-27.json | 52 +++++++++++++++++++ phone_focus_mode/config.sh | 1 + python_pkg/app_icons/apps.py | 8 +++ python_pkg/app_icons/glyphs.py | 20 +++++++ 4 files changed, 81 insertions(+) create mode 100644 docs/superpowers/evidence/home-inventory-icon-and-whitelist-2026-07-27.json diff --git a/docs/superpowers/evidence/home-inventory-icon-and-whitelist-2026-07-27.json b/docs/superpowers/evidence/home-inventory-icon-and-whitelist-2026-07-27.json new file mode 100644 index 00000000..23b7a7bf --- /dev/null +++ b/docs/superpowers/evidence/home-inventory-icon-and-whitelist-2026-07-27.json @@ -0,0 +1,52 @@ +{ + "intent": "Let the new ~/home_inventory Flutter app get an on-style family launcher icon, and let it survive the phone focus daemon so it can be installed and used on BL9000EEA0000102.", + "scope": [ + "python_pkg/app_icons/glyphs.py — new 'storage-box' glyph", + "python_pkg/app_icons/apps.py — new 'home_inventory' registry entry", + "phone_focus_mode/config.sh — WHITELIST entry com.kuhy.home_inventory", + "Non-goal: no change to the icon family's shared accent, canvas, stroke or safe-box constants", + "Non-goal: no change to any existing app's glyph" + ], + "changes": [ + "Added _STORAGE_BOX: an isometric carton drawn as a regular hexagon (centre 512,512, circumradius 240) plus the three edges meeting at the near-top corner. Opposite faces sit 208px apart, leaving 136px hollow after the 72px stroke, so no face fills in at 48dp; rendered ink is 488x552, inside SAFE_BOX on both axes.", + "A first attempt drew a front-on rectangle with a lid seam and a tape line. Rejected after rendering the contact sheet: a seam plus a divider reads as a browser window or a table header, not a box. The isometric form collides with nothing else in the family.", + "Registered app_icons entry key='home_inventory', glyph='storage-box', icon_name='home-inventory', accent='#B8862E' (the one shared family accent — not a per-app colour), linux=False.", + "Added com.kuhy.home_inventory to the '--- User-requested productive apps ---' block of the focus-mode WHITELIST, beside the other com.kuhy.* apps." + ], + "verification": [ + { + "command": "pre-commit run --files phone_focus_mode/config.sh python_pkg/app_icons/apps.py python_pkg/app_icons/glyphs.py", + "result": "pass", + "evidence": "All hooks Passed (ruff, ruff format, mypy, pylint, bandit, pytest-with-coverage, shellcheck, codespell, binary block, secret scan); three hooks Skipped for having no matching files." + }, + { + "command": "python3 -m pytest python_pkg/app_icons --cov=python_pkg.app_icons --cov-report=term-missing", + "result": "pass", + "evidence": "43 passed. Every app_icons module at 100%: apps.py 24/24, glyphs.py 23/23, cli.py 60/60, generate.py 41/41, preview.py 41/41, render.py 48/48, style.py 11/11. The registry invariant tests iterate APPS/GLYPHS, so the new entries are covered by construction." + }, + { + "command": "PYTHONPATH=~/testsAndMisc python3 -m python_pkg.app_icons generate --app home_inventory --android", + "result": "pass", + "evidence": "Wrote 6 asset files to ~/home_inventory/assets/icon and regenerated the Android mipmaps, with no safe-box warning. An earlier 500px-wide variant emitted 'glyph exceeds the 560px safe box by 12x0px' because STROKE_WIDTH adds 72px to each rendered dimension; the body was narrowed to 488px." + }, + { + "command": "PYTHONPATH=~/testsAndMisc python3 -m python_pkg.app_icons preview -o /tmp/icon_sheet2.png", + "result": "pass", + "evidence": "Contact sheet read directly. The glyph reads as a 3D package at full size, circle-masked and 48dp, and is distinct from cloud-down, barbell, clock, shield-cutlery, checklist and chain-link." + }, + { + "command": "adb -s BL9000EEA0000102 shell grep -c 'com.kuhy.home_inventory' /data/local/tmp/focus_mode/config.sh", + "result": "pass", + "evidence": "Returned 1 after pushing config.sh and running 'focus_ctl.sh restart' (Daemon stopped PID 6504, started PID 25811). The release APK then installed, launched and stayed running rather than being pm disable-user'd ~1s after launch." + } + ], + "risks": [ + "The whitelist was pushed to the phone with the targeted three-command adb path rather than phone_focus_mode/deploy.sh, so the Magisk hosts module was deliberately not touched. If a future full deploy.sh run overwrites /data/local/tmp/focus_mode/config.sh from the repo, this entry is already committed here and survives.", + "app_icons/cli.py derives --app choices from sorted(apps.APPS), so anyone on an older checkout cannot regenerate this app's icon until they pull.", + "The glyph is tuned to the current STROKE_WIDTH of 72 and SAFE_BOX of 560; changing either would need the 488x440 body re-checked against the safe box." + ], + "rollback": [ + "Revert this commit. The icon assets already written into ~/home_inventory/assets/icon and its android mipmaps live in that repo, so delete them there separately if the app is abandoned.", + "After rollback, re-run 'python3 -m pytest python_pkg/app_icons' to confirm the registry invariants still hold, and re-push phone_focus_mode/config.sh to the phone so its whitelist matches the repo again — otherwise the phone keeps whitelisting a package the repo no longer lists." + ] +} diff --git a/phone_focus_mode/config.sh b/phone_focus_mode/config.sh index 5a9115e0..42e0d86e 100755 --- a/phone_focus_mode/config.sh +++ b/phone_focus_mode/config.sh @@ -339,6 +339,7 @@ com.stronglifts.app com.kuhy.workout_app com.kuhy.diet_guard_app com.kuhy.habit_stack +com.kuhy.home_inventory com.shazam.android com.ichi2.anki com.metrolist.music diff --git a/python_pkg/app_icons/apps.py b/python_pkg/app_icons/apps.py index 31e7a1df..14938fdb 100644 --- a/python_pkg/app_icons/apps.py +++ b/python_pkg/app_icons/apps.py @@ -97,6 +97,14 @@ APPS: Final[dict[str, AppIcon]] = { icon_name="habit-stack", linux=False, ), + AppIcon( + key="home_inventory", + repo=_HOME / "home_inventory", + accent="#B8862E", + glyph="storage-box", + icon_name="home-inventory", + linux=False, + ), ) } diff --git a/python_pkg/app_icons/glyphs.py b/python_pkg/app_icons/glyphs.py index 4dca4eb8..1362a0b8 100644 --- a/python_pkg/app_icons/glyphs.py +++ b/python_pkg/app_icons/glyphs.py @@ -93,6 +93,25 @@ _CHAIN_LINK = """\ """ +# An isometric storage carton: hexagonal silhouette plus the three edges that +# meet at the near-top corner (one down the front, two up the top face). +# +# A front-on rectangle with a lid seam was tried first and rejected — a seam +# plus a divider reads as a browser window or a table header, not a box. The +# isometric form has no such collision anywhere else in the family, and it is +# the shape people already recognise as "a package of stuff". +# +# Geometry: a regular hexagon, centre (512,512), circumradius 240, pointy top +# and bottom. Opposite faces sit 208px apart, leaving 136px of hollow once the +# 72px stroke is drawn — comfortably past MIN_NEGATIVE_SPACE, so no face fills +# in at 48dp. Rendered ink is 488x552, inside SAFE_BOX on both axes. +_STORAGE_BOX = """\ + + + + """ + + GLYPHS: Final[dict[str, Glyph]] = { glyph.name: glyph for glyph in ( @@ -102,6 +121,7 @@ GLYPHS: Final[dict[str, Glyph]] = { Glyph("shield-cutlery", "Shield guarding a fork and knife", _SHIELD_CUTLERY), Glyph("checklist", "Two ticked-off list rows", _CHECKLIST), Glyph("chain-link", "Two interlocking chain links", _CHAIN_LINK), + Glyph("storage-box", "Isometric storage carton", _STORAGE_BOX), ) }