Skip to content

Add Date added - #218

Open
Norphirion wants to merge 3 commits into
SteamClientHomebrew:mainfrom
Norphirion:add-date-added
Open

Add Date added#218
Norphirion wants to merge 3 commits into
SteamClientHomebrew:mainfrom
Norphirion:add-date-added

Conversation

@Norphirion

@Norphirion Norphirion commented Aug 5, 2026

Copy link
Copy Markdown

Date added

Adds an Added card to the stats row on a game's library page, next to Cloud status, Achievements and Size on disk, showing the date the game entered the user's library.

Steam records this date but never surfaces it anywhere in the client. It makes it easy to tell a years-old backlog entry from something picked up in the last sale.

The date is read from rt_purchased_time on Steam's local app overview: no network access, no account linking, no configuration. Class names are resolved at runtime via findClassModule, so the card keeps Steam's own styling rather than hardcoding hashed class names.

Task Checklist

Developer

  • I am the original author or an authorized maintainer of this plugin.
  • I have complied with all license requirements for the libraries used, including providing appropriate notices where necessary.
  • My plugin is fully open source and does not depend on any external paid services, except for widely trusted and well-known platforms. Additionally, neither I nor anyone associated with me profits from any such services.

Plugin Functionality

  • I have tested the plugin on both the Stable and Beta Steam update channels.
  • My plugin is unique, or provides additional or alternative functionality to plugins already on the store.

Backend Configuration

  • No: I use a standard Millennium python backend in my plugin.
  • No: I use custom binaries that or rely on other FOSS projects that aren't written directly using Millennium's python backend.

Community Contribution

  • I have tested and left feedback on two other plugin pull requests.
  • I have added links to those feedback comments in this PR.

Testing Instructions

  • Verified by a third party on Steam Client Stable.
  • Verified by a third party on Steam Client Beta.

@github-actions github-actions Bot changed the title feat: add date-added Add Date added Aug 5, 2026
@Shightrox

Copy link
Copy Markdown

Disclosure: I am the author of #224 and tested this PR as part of the Community Contribution requirement.

Tested on Windows 11, Steam Client Stable, Millennium 3.4.0, using the exact pinned plugin commit b349cac37510647db747971aa5e6267b461d1d56.

Runtime results

  • The plugin loaded without a backend and without making its own network requests.
  • On CS2 (730) and Dota 2 (570), where Steam exposed a positive rt_purchased_time, the injected cards had non-empty text and followed the active app route correctly.
  • On two owned games where that field was absent (105600 and 1604000), no empty/stale card was rendered.
  • I found no Date added warnings or errors in webhelper_js.txt during navigation.
  • Steam exposed two visible GameStatsSection hosts on both tested pages, and the plugin inserted a card into both. This may be intentional for responsive layouts, but it is worth manually confirming that the user never sees a duplicate in any library layout.

Issues found

  1. The PluginDatabase build workflow is not currently reproducible from a clean checkout. Under Node 20.20.2, using the database's pnpm flow, the repository has no tracked pnpm-lock.yaml (only package-lock.json). pnpm install therefore resolved @steambrew/ttc from ^3.2.6 to 3.3.7 and its TypeScript peer to 7.0.2; pnpm run build failed, including TS5108 because moduleResolution=node10 is removed in TS 7, followed by a rollup-plugin-inject-process-env resolution error. Please commit a pnpm lockfile or pin a known-compatible toolchain and verify a clean Node 20 pnpm install && pnpm run build.
  2. There is no unload cleanup. start() creates a permanent one-second interval, but the plugin returns no onDismount; removeCards() removes React hosts without calling root.unmount(). Disabling/reloading the plugin can therefore leave the interval and React roots alive. Returning a cleanup function that clears the interval, unmounts each root, and removes the hosts would make hot reload/disable safe.
  3. plugin.json reports 1.0.1, while package.json reports 1.0.0.
  4. The $schema URL currently returns 404 (src/sys/plugin-schema.json has moved), so editor validation is not working.
  5. The package declares MIT and the README says MIT, but the pinned repository has no LICENSE file. Adding the actual license text would make the licensing claim verifiable.

The core feature behaved correctly in the routes I tested; the clean-build and lifecycle points are the main things I would address before publishing.

@Shightrox Shightrox mentioned this pull request Aug 14, 2026
9 tasks
@Norphirion

Copy link
Copy Markdown
Author

Thank you for this review @Shightrox, the depth of it is genuinely appreciated. Testing on real apps, checking the negative cases (105600, 1604000), reading webhelper_js.txt and actually attempting a clean build is far more than a rubber stamp, and every one of your five points was valid.

Some context that may be useful: I'm not a developer. This plugin was written with Claude Code, and I reviewed and tested the result rather than authored it line by line. So I apologise for the rough edges you had to find, especially the build and lifecycle ones, which are exactly the kind of thing I lacked the background to catch. I'll do my best to address anything else you spot.

All five points are fixed in the updated commit. Details below.

1. Clean build under pnpm

This was the real bug, and your diagnosis was exactly right. typescript was never declared as a dependency at all - it was only present transitively - so pnpm was free to satisfy @steambrew/ttc's peer with TypeScript 7, which is where TS5108 came from.

Fixed by:

  • Pinning every devDependency to an exact version (no ^).
  • Declaring typescript explicitly at 5.9.3, plus a pnpm.overrides entry so it cannot be hoisted to a different major anywhere in the tree.
  • Committing a pnpm-lock.yaml.
  • Switching moduleResolution from node to bundler, which both TypeScript 5 and 7 accept — so the build no longer depends on the resolver that broke for you.
  • Dropping @steambrew/api and @steambrew/webkit, which were declared but never imported.

Verified rather than assumed: from a clean checkout containing only the tracked files, pnpm install --frozen-lockfile followed by pnpm run build succeeds and produces a bundle byte-identical to the committed one. That was with pnpm 9 on Node 24 rather than your Node 20.20.2, so if it still misbehaves on 20 I'd very much like to know.

2. No unload cleanup

Correct, and worse than it looked: the plugin returned nothing at all from definePlugin, with a type cast to force that past the compiler. Plugin has supported onDismount all along.

The plugin now returns a proper Plugin object whose onDismount clears the interval, unmounts every React root and removes the hosts. Two related details:

  • The roots were held in a WeakMap, which cannot be enumerated at teardown, so it is now a Map.
  • That change would have introduced a slower leak of its own, since React detaches our hosts whenever it re-renders the stats section. Each tick now prunes roots whose host is no longer connected and unmounts them.

Only icon is returned, so no settings panel is mounted. Millennium requires title, icon and content together for that, and this plugin has nothing to configure.

3. Version mismatch

Fixed; plugin.json and package.json both read 1.0.1.

4. $schema 404

Removed rather than repointed, and I think this one is worth flagging upstream.

src/sys/ no longer exists in the Millennium repository at all, and I could not find a plugin-schema.json anywhere else in it, it appears to have gone during the move to Starlight. The official PluginTemplate and the other published plugins I checked still carry the same dead URL.

Since $schema is editor-only metadata with no runtime effect, dropping the line seemed better than pointing at a URL I'd be guessing at. Happy to add the correct one if it exists somewhere I didn't look.

5. Missing LICENSE

Added : the MIT text, matching what package.json and the README already claimed.

On the two GameStatsSection hosts

Good catch, and worth documenting. I measured both on a game page:

  • One sits under an ancestor with opacity: 0 — it is the sticky PlayBar that fades in when you scroll. checkVisibility() returns false for it, and elementFromPoint at its centre does not land inside it.
  • The other has nothing hiding it and is the one actually on screen.

So only one is ever visible and the user never sees a duplicate. Injecting into both is deliberate: if the card only went into the in-page section, it would disappear the moment the sticky bar takes over on scroll. I've added a comment in the source explaining this so it doesn't look accidental to the next reader.

Thanks again, the plugin is in noticeably better shape than before your review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants