A simple, small calculator placed discreetly near the Windows Taskbar or anywhere on the screen
0

Configure Feed

Select the types of activity you want to include in your feed.

docs: verification checklist and updated CLAUDE.md for the tray app

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

authored by

Marco Maroni
Claude Opus 4.8 (1M context)
and committed by
Tangled
(Jul 15, 2026, 1:16 PM +0300) ed72cf1f ae9071db

+124 -37
+80 -37
CLAUDE.md
··· 4 4 5 5 ## What this is 6 6 7 - TaskbarCalculator is a Windows **DeskBand shell extension** — an in-process COM DLL that Explorer loads to embed a small calculator inside the Windows taskbar. It is written in C++ with ATL, targets the Windows shell (`IDeskBand2`), and has no standalone executable: the DLL is registered with the system and hosted by `explorer.exe`. 7 + TaskbarCalculator is a small **Windows 11 system-tray calculator**: a standalone C++/Win32 8 + executable that lives in the notification area and shows a persistent, always-on-top 9 + calculator window driven by keyboard input. 10 + 11 + It was **migrated from a DeskBand shell extension** (an in-process COM DLL hosted by the 12 + taskbar), because Windows 11 no longer loads third-party taskbar DeskBands. The design and 13 + implementation history of that migration live in `docs/superpowers/` (spec, plan, 14 + build-env, verification). The `main` branch still holds the original DeskBand baseline; 15 + the migration was done on `feature/win11-tray-calculator`. 8 16 9 17 ## Build 10 18 11 - There is no CLI build script; this is a Visual Studio project. 19 + Visual Studio 2022 project (no CLI build script of its own). 12 20 13 - - **Toolchain:** Visual Studio 2017 (`v141` platform toolset), Windows 10 SDK `10.0.17134.0`, ATL linked statically (`UseOfAtl=Static`), Unicode character set. 14 - - **Build in VS:** open `TaskbarCalculator.sln`, pick a configuration (`Debug`/`Release`) and platform (`Win32`/`x64`), and build. 15 - - **Build from command line** (Developer Command Prompt / with MSBuild on PATH): 21 + - **Toolchain:** VS 2022 (`v143`), Windows SDK `10.0.26100.0`, ATL linked statically 22 + (`UseOfAtl=Static`), Unicode, C++17. Requires C++/WinRT (SDK cppwinrt headers) for the 23 + start-at-login feature. See `docs/superpowers/BUILD-ENV.md`. 24 + - **Build (x64 is the primary target):** 16 25 ``` 17 - msbuild TaskbarCalculator.sln /p:Configuration=Release /p:Platform=x64 26 + msbuild TaskbarCalculator.vcxproj /p:Configuration=Debug /p:Platform=x64 27 + msbuild TaskbarCalculator.vcxproj /p:Configuration=Release /p:Platform=x64 18 28 ``` 19 - - Output is `TaskbarCalculator.dll` (a `DynamicLibrary`). The exported entry points are defined in `TaskbarCalculator.def`: `DllRegisterServer`, `DllUnregisterServer`, `DllGetClassObject`, `DllCanUnloadNow`, `DllInstall`. 20 - - The solution also references `..\TaskbarCalculatorSetup\TaskbarCalculatorSetup.vdproj`, a setup project that lives **outside this repository**; it will be missing here. 29 + Build the **`.vcxproj` directly**, not the `.sln` — the solution references a setup 30 + project (`..\TaskbarCalculatorSetup\TaskbarCalculatorSetup.vdproj`) that is **not in this 31 + repo**. 32 + - Output is `x64\<Config>\TaskbarCalculator.exe` (a windowed `Application`; entry point 33 + `wWinMain` in `App.cpp`). 21 34 22 - There are **no tests** in this repository. 35 + ## Test 23 36 24 - ## Register / run / debug 37 + The `calc` engine is decoupled from the precompiled header so it builds standalone. Unit 38 + tests use a tiny header-only harness, compiled and run directly with `cl` from an **x64 39 + Native Tools Command Prompt**: 40 + ``` 41 + tests\build-and-run.cmd 42 + ``` 43 + Expected: `16/16 passed`. Add engine cases in `tests/CalcTests.cpp` (the `feed()` helper 44 + threads the output string back in as the next input, exactly as the UI does). 25 45 26 - Because there is no `main()`, you exercise the code by registering the DLL and letting Explorer host it: 46 + ## Package (MSIX) & run 27 47 28 - - **Register:** `regsvr32 TaskbarCalculator.dll` (or `regsvr32 /i:user` to use `DllInstall` with per-user registration via `AtlSetPerUserRegistration`). 29 - - **Unregister:** `regsvr32 /u TaskbarCalculator.dll`. 30 - - After registering, add the band from the taskbar's toolbars context menu. Explorer caches loaded DLLs, so you typically must restart `explorer.exe` between rebuilds. 31 - - Registration is driven by `TaskbarCalculator.rgs` under the CLSID `{1B1E01F4-6B08-44F9-A1E8-40BC40B55578}` with `ThreadingModel = Apartment`. 48 + - **Run (dev):** launch `x64\Debug\TaskbarCalculator.exe`. It adds a tray icon; left-click 49 + toggles the window, right-click opens the menu (Show/Hide, Start at login, About, Exit). 50 + - **Start-at-login** uses the MSIX `windows.startupTask` API, so it only works when the app 51 + is **packaged/installed**; unpackaged it reports `Unavailable` and the menu item is grayed. 52 + - **Package:** from `Packaging\`, run `MakeAssets.ps1` (once), `MakeCert.ps1` (once), then 53 + `BuildMsix.ps1` → `Packaging\TaskbarCalculator.msix`. Install steps (trust the dev cert + 54 + `Add-AppxPackage`) are documented in `docs/superpowers/VERIFICATION.md` and require an 55 + elevated shell. 32 56 33 57 ## Architecture 34 58 35 - The code splits cleanly into three layers: 59 + Three layers, plus small support modules: 36 60 37 - 1. **COM/shell integration — `CCalculatorDeskBand`** (`CalculatorDeskBand.cpp/.h`) 38 - The ATL coclass Explorer instantiates. It implements the DeskBand contract: `IDeskBand2`, `IObjectWithSite`, `IDockingWindow`/`IOleWindow`, `IInputObject`, `IContextMenu`, and `IPersistStreamInit`. Key flow: 39 - - `SetSite` receives the taskbar's site, gets the parent HWND, and creates the child `CCalculatorWindow`. 40 - - `GetBandInfo` reports sizing (`CalcIdealSize`) and title back to the shell. 41 - - `IContextMenu` adds a single "Settings" item that currently just opens the About dialog (`AboutDialogProc` / `AboutDialog.cpp`). 42 - - `IPersistStreamInit` load/save is stubbed out (the commented-out `PROP_MAP` and date-format code are leftovers). 61 + 1. **App shell — `App.cpp`** 62 + `wWinMain` initializes WinRT (`init_apartment`) and common controls, enforces a 63 + **single-instance** guard (named mutex; a second launch signals the first via 64 + `IDM_SHOWHIDE` and exits), creates a **hidden top-level owner window** (`WS_EX_TOOLWINDOW`) 65 + that owns the tray icon, then creates the calculator window and runs the message loop. 66 + `OwnerWndProc` routes the tray callback (`WM_TRAY_CALLBACK`) and menu commands, re-adds the 67 + tray icon on the `TaskbarCreated` broadcast (survives Explorer restarts), and opens the 68 + About dialog (`AboutDialogProc` in `AboutDialog.cpp`, a raw-Win32 dialog over `IDD_ABOUT`). 43 69 44 - 2. **Window/host + input + formatting — `CCalculatorWindow`** (`CalculatorWindow.cpp/.h`) 45 - A `CWindowImpl` that owns a child Win32 `EDIT` control. It: 46 - - Subclasses the edit control (`WndEditProc`) to intercept `WM_KEYDOWN`/`WM_CHAR` and translate keystrokes into calculator command chars (see the `CALC_*` defines in `Calculator.h`). `OnEditKeyDown` handles numpad/VK keys; `OnEditChar` handles typed characters (including hex `A`–`F`, `q`=sqrt, `p`=negate, `r`=Euro, `l`=Lire, `x`/`i`=hex/dec base toggle). 47 - - Owns the `calc` engine instance and pushes each command into it. 48 - - Handles painting via a double-buffered, themed background (`CVisualStyle`), and repositions the edit control on `WM_PAINT`. 49 - - `ReadRegionalSettings()` reads `HKCU\Control Panel\International` (`sDecimal`, `sThousand`, `iDigits`) to drive display formatting and engine precision; re-read on focus and `WM_SETTINGCHANGE`. 50 - - `FormatString()` inserts thousands/decimal separators for display (decimal base only); the engine always works on an unformatted string. 70 + 2. **Calculator window + input + formatting — `CCalculatorWindow`** (`CalculatorWindow.cpp/.h`) 71 + An ATL `CWindowImpl` top-level tool window (`WS_POPUP`, `WS_EX_TOPMOST | WS_EX_TOOLWINDOW`) 72 + that owns a child `EDIT` control. `CreateStandalone` builds it hidden; `ToggleVisible` 73 + shows/hides it (re-asserting top-most); `PlaceInitial` restores the saved position or, on 74 + first run, anchors bottom-right above the tray via `SHAppBarMessage`. It subclasses the 75 + edit control (`WndEditProc`) to translate `WM_KEYDOWN`/`WM_CHAR` into `CALC_*` command 76 + chars (`OnEditKeyDown`/`OnEditChar`), pushes them into `calc`, and paints a themed, 77 + double-buffered background (`CVisualStyle`). `WM_CLOSE` hides to tray (does not exit); 78 + `WM_LBUTTONDOWN` drags the window; `WM_EXITSIZEMOVE` persists the position. 79 + `ReadRegionalSettings` reads `HKCU\Control Panel\International` (`sDecimal`, `sThousand`, 80 + `iDigits`); `FormatString` inserts separators for display while the engine stays unformatted. 51 81 52 82 3. **Calculation engine — `calc`** (`Calculator.cpp`, `Calculator.h`) 53 - A UI-independent finite state machine. `change_state(newChar, oldString, newString)` is the single entry point: it takes the current display string plus a command char and produces the next string. States: `init / first_oper / operation / second_oper / result / error`. Supports the four operations, sqrt, percent, sign inversion, decimal⇔hex base switching, and a hardcoded Italian Lire⇔Euro conversion (`CHANGE_LIRE_EURO = 1936.27`). Errors surface as the literal strings `ERR_STR_OVERFLOW` / `ERR_STR_DIVBYZERO`, which the window layer detects and displays without formatting. 83 + A UI-independent finite state machine. `change_state(newChar, oldString, newString)` is 84 + the single entry point. States: `init / first_oper / operation / second_oper / result / 85 + error`. **Decimal-only**: four operations, sqrt, percent, sign inversion, reciprocal 86 + (1/x), `000`, reset. Errors surface as `ERR_STR_OVERFLOW` / `ERR_STR_DIVBYZERO`, which the 87 + window shows unformatted. (The old hex/base mode and Italian Lire⇔Euro conversion were 88 + removed in the migration.) 54 89 55 - `VisualStyle.cpp/.h` provides an abstract `CVisualStyle` with `CVisualStyle::Create()` returning a classic-vs-themed implementation; it supplies the font and the background-draw routine. `Utils.cpp/.h`, `stdafx.h` (precompiled header, ATL + shell includes), `DllMain.cpp` (`_AtlModule` + DLL exports), and the `.rc`/`resource.h`/`.idl`/`.rgs` files round out the project. 56 - 57 - ## Naming gotchas 58 - 59 - The project began life as a *calendar* DeskBand, and some identifiers still carry the old name even though the class is the calculator: notably the CLSID symbol `CLSID_CalendarDeskBand` (in `Guids.h`) and the module class `CCalculatorDeskBandModule`. These are intentional/legacy — don't "fix" them to match without checking every reference, since the `.rgs`, IDL, and `OBJECT_ENTRY_AUTO` all key off `CLSID_CalendarDeskBand`. 90 + Support modules: **`Settings`** (`HKCU\Software\TaskbarCalculator` — window position & 91 + visibility as `REG_DWORD`), **`StartupManager`** (WinRT `StartupTask` toggle, `Unavailable` 92 + when unpackaged), **`CVisualStyle`** (classic-vs-themed font/background), **`Utils`**, 93 + `stdafx.h` (PCH: ATL + shell + common-controls + UxTheme). 60 94 61 95 ## Conventions 62 96 63 - - TCHAR/`_T()` string macros throughout (compiled Unicode), ATL `CString`, and raw Win32 API — match the surrounding style rather than introducing STL strings or wide-only APIs. 64 - - The calculator command protocol is character-based: every user action becomes a single `TCHAR` fed to `calc::change_state`. When adding a key or operation, wire it in both `OnEditKeyDown`/`OnEditChar` (translation) **and** `Calculator.cpp` (behavior), and add a `CALC_*` define in `Calculator.h`. 97 + - TCHAR/`_T()` string macros (Unicode build), ATL `CString`, raw Win32 — match the 98 + surrounding style. 99 + - The calculator command protocol is character-based: every user action becomes a single 100 + `TCHAR` fed to `calc::change_state`. To add a key/operation, wire it in 101 + `OnEditKeyDown`/`OnEditChar` (translation) **and** `Calculator.cpp` (behavior), add a 102 + `CALC_*` define in `Calculator.h`, and add an engine test in `tests/CalcTests.cpp`. 103 + - Non-ATL source files (`App.cpp`, `TrayIcon.cpp`, `Settings.cpp`, …) do not `using 104 + namespace ATL`; use `GetModuleHandle(NULL)` for the instance handle rather than 105 + `_AtlBaseModule`. 106 + - `Calculator.cpp` is set to **NotUsing** the precompiled header (so it compiles standalone 107 + for the tests); every other `.cpp` uses `stdafx.h`.
+44
docs/superpowers/VERIFICATION.md
··· 1 + # Manual Verification — Windows 11 Tray Calculator 2 + 3 + ## Already verified programmatically (this migration) 4 + 5 + - ✅ Engine unit tests: **16/16 pass** (`tests\build-and-run.cmd`) — arithmetic, sqrt, 6 + reciprocal, percent, negate, 000, reset, divide-by-zero, overflow, and Lire/Euro + hex 7 + keys inert. 8 + - ✅ Debug **and** Release x64 builds succeed (`msbuild ... /p:Platform=x64`). 9 + - ✅ App launches without crashing and stays in its message loop (tray icon added, 10 + window + EDIT + theme created, anchoring path runs). 11 + - ✅ `Settings` registry round-trip (`PosX`/`PosY` written and read back). 12 + - ✅ `Startup::Get()` returns `Unavailable` when unpackaged (expected). 13 + - ✅ MSIX package builds and is signed (`Packaging\TaskbarCalculator.msix`). 14 + 15 + ## To verify interactively (requires a desktop session — do these on the machine) 16 + 17 + - [ ] Tray icon appears on launch 18 + - [ ] Left-click toggles the window show/hide 19 + - [ ] Window is always-on-top: stays visible above another focused app 20 + - [ ] Keyboard input works: `2 + 3 =` → `5`; divide-by-zero shows the error string; 21 + sqrt (`q`), percent (`%`), reciprocal (`i`), negate (`p`), `000` (`z`) 22 + - [ ] Regional decimal/thousands separators respected (change in Control Panel, retype) 23 + - [ ] Drag moves the window; position remembered after exit + relaunch 24 + - [ ] First run anchors bottom-right above the tray 25 + - [ ] "X" hides to tray (does not exit) 26 + - [ ] Tray icon returns after restarting Windows Explorer 27 + - [ ] Second launch does not create a duplicate icon (single instance) 28 + - [ ] Exit removes the tray icon and quits 29 + 30 + ## To verify after installing the MSIX (packaged only) 31 + 32 + Install steps (elevated PowerShell — these make machine-level changes, left to the user): 33 + ```powershell 34 + # 1) Trust the self-signed dev cert (once): 35 + Import-PfxCertificate -FilePath 'Packaging\TaskbarCalculator.pfx' ` 36 + -CertStoreLocation Cert:\LocalMachine\TrustedPeople ` 37 + -Password (ConvertTo-SecureString 'TaskbarCalc!' -Force -AsPlainText) 38 + # 2) Install the package: 39 + Add-AppxPackage -Path Packaging\TaskbarCalculator.msix 40 + ``` 41 + 42 + - [ ] App installs and appears in Start; launches to the tray 43 + - [ ] Right-click → *Start at login* is enabled/checkable (not grayed) 44 + - [ ] Toggling it on shows the app under Settings → Apps → Startup