feat(docs): live ecosystem diagram, collapsible sidebar - #315
Conversation
Replace the remaining PNG assets used by the docs site and READMEs with 256px webp, cutting ~13 MB of source artwork down to ~380 KB. - packages/docs/public/logos: 11 MB of 1024px PNGs -> 164 KB of webp, including a new maui-iap icon so every framework library has one - root logo.png 1.57 MB -> logo.webp 123 KB; the PNG stays as a 512px shim because READMEs of already-published npm/pub.dev versions fetch that exact raw URL and would otherwise show a broken image - kmp-iap release screenshots and the MCP demo still frame - repoint every reference: 10 raw.githubusercontent URLs across five library READMEs, llms.txt, llms-full.txt, and the MCP video renderer Fix two pre-existing broken references found while sweeping: - kmp-iap README pointed at docs/static/img/logo.png, a path that does not exist in the repo - amazon.sdktester.json (both example apps, 10 entries) pointed smallIconUrl at openiap.dev/img/logo.png, which has no /img/ directory and returns 200 text/html via the SPA fallback rather than 404 Mono marks (apple, expo, horizonos) had their flat backdrop removed with an alpha threshold so the glyph bounding box is tight; without it the anti-aliased fringe kept getbbox() at full frame and the Apple mark rendered ~25% small next to its neighbours. Godot example icons and .github/pr-previews stay PNG: the former are referenced by project.godot/export_presets.cfg and required by Android and iOS export, the latter are linked from already-posted PRs.
/docs/ecosystem rendered a static 2226px webp that had to be re-exported by hand and was unreadable in dark mode. It is now real markup. - every node is a link: core and spec nodes open GitHub, library nodes route to their setup guide, "and more" opens the contact address - framework libraries are not listed in the component. Membership, order, display name, version, setup path and the fallback framework mark all come from LIBRARIES in src/lib/images.ts, so adding a library there makes it appear here with no edit. Artwork overrides are two Partial<Record<...>> maps with SSOT fallbacks - versions render from OPENIAP_VERSIONS and each library's package metadata, so a release train updates the figure automatically Layout is driven by container queries, not media queries: the docs sidebar is user-resizable (300-480px), so .doc-page can be ~350px wide at a 769px viewport and a viewport breakpoint would be wrong by construction. Above a 700px container it is a two-column figure (spec and core down the left, libraries on the right); below that it stacks. Artwork treatment is declared per asset rather than per rule: opaque square icons crop to a rounded tile, flat single-ink marks invert for whichever theme they would vanish into, and compact glyphs get an optical size bump so the Apple mark draws as wide as the Horizon ring.
…psible sidebar Migration page Rename /docs/updates/deprecations to /docs/updates/migration and drop the version from the title, so future trains do not require renaming it again. The old path still resolves: it is in sitemap.xml and other pages deep-link its anchors, so it redirects through NavigatePreservingHash rather than 404ing. The 2.x -> 3.0 content is now one delimited <section> under its own "2.x -> 3.0" heading, with the five former h2 sections demoted to h3 underneath it. Every anchor id is unchanged, so existing deep links from types/purchase, setup/flutter, releases and announcements still land in the right place. A comment marks where the next train goes; the train-independent policy section stays last. Also fixes the removal-schedule tables, whose headers overlapped: "Last compatible major" is nowrap site-wide, which under the fixed table-layout overflowed its 26% track into the next header. Headers wrap inside this table and the columns are rebalanced to 40/30/30. Updated alongside the route: searchData, sitemap.xml, six internal link sites, ALTERNATIVE_BILLING.md, the llms.txt template in the context compiler, knowledge/internal/07-docs-consistency.md, and the hardcoded paths in scripts/audit-deprecation-schedule.mjs, which would otherwise have failed CI on the renamed file. Sidebar The sidebar could be dragged down to 300px and no further, which is where its labels start truncating. It now collapses instead. A bookmark handle sits on the sidebar edge: click it to toggle, drag it to resize, drag it past the minimum to snap shut. A drag is only recognized past a 4px threshold, so a click never nudges the width. Keyboard gets Enter/Space to toggle and arrows to resize. The collapsed state persists, the column animates shut rather than display:none, and visibility flips only after the width transition so the hidden links leave the tab order without cutting the animation short. Content centres in the freed space via container padding, which can be interpolated, instead of an auto margin, which cannot. The mobile drawer previously faded out while it slid, with a 0.2s opacity against a 0.25s transform, so it read as vanishing mid-slide. It now only slides, and the backdrop stays mounted so it can fade out with it instead of disappearing instantly on close.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
🚧 Files skipped from review as they are similar to previous changes (3)
📝 WalkthroughWalkthroughThe documentation site now uses a consolidated migration catalog route, preserves the former route with a redirect, adds a responsive ecosystem diagram, updates sidebar interactions, revises subscription lifecycle documentation, and changes image assets from PNG to WebP. ChangesDocumentation and asset updates
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant EcosystemPage
participant EcosystemDiagram
participant LibraryMetadata
EcosystemPage->>EcosystemDiagram: render ecosystem diagram
EcosystemDiagram->>LibraryMetadata: read LIBRARIES and OPENIAP_VERSIONS
LibraryMetadata-->>EcosystemDiagram: return node and version metadata
EcosystemDiagram-->>EcosystemPage: render linked node cards and rails
Possibly related PRs
Suggested labels: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #315 +/- ##
=======================================
Coverage 71.86% 71.86%
=======================================
Files 134 134
Lines 14407 14407
Branches 4022 4022
=======================================
Hits 10353 10353
Misses 4054 4054
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
Audited every checkable claim on /docs/lifecycle/subscription against
the GraphQL SSOT and the native modules. 37 statements were wrong; each
fix below is verified against packages/gql, packages/apple/Sources or
packages/google.
Names that do not exist in the schema:
- ActiveSubscriptionAndroid is not a type. getActiveSubscriptions
returns [ActiveSubscription!]! on both platforms, and the iOS tab of
the same block already said so, so the two tabs contradicted each
other. The Android field-group heading on the types page was the
likely source and now reads "Android Fields" like its iOS sibling
- transactionState was renamed to purchaseState in the 3.0 train and is
listed as removed in this repo's own migration catalog (4 sites)
- isAcknowledged -> isAcknowledgedAndroid, purchaseTime ->
transactionDate, offerType/offerIdentifier -> renewalOfferType/
renewalOfferId
Values that cannot occur:
- PurchaseState has exactly Pending | Purchased | Unknown. The page
listed `failed` and `deferred`; failures arrive as a PurchaseError
through purchaseErrorListener and Ask to Buy surfaces as pending
- Android showed Play Billing constants (PURCHASED (1), PENDING (2),
UNSPECIFIED (0)) rather than the lowercase wire values; the constants
are kept as an aside
- expirationReason carries StoreKit's raw integer as a string, so the
five symbolic names ("VOLUNTARY", "BILLING_ERROR", …) are never
returned
- subscriptionState is serialized with the SUBSCRIPTION_STATE_ prefix,
so matching on bare "ACTIVE" never hits
Android capability understated in five places: the client does expose
pendingPurchaseUpdateAndroid (pending plan change, Play Billing 5.0+)
and isSuspendedAndroid (billing-issue state), both mapped by the Play
native module. Only the effective date and retry details need the
Developer API.
Also: requestPurchase takes RequestPurchaseProps with a type
discriminator, not a bare sku; getAvailablePurchases is not a
purchase-history source on Android because queryPurchasesAsync returns
only owned purchases; SUBSCRIPTION_ON_HOLD (5) is an account hold, not
the distinct user-initiated PAUSED state; the PurchaseIOS link now
carries its anchor; the announcement link is an internal route rather
than an absolute openiap.dev URL that leaves the SPA; and IAPKit is
OpenIAP's own hosted backend, not a partner, now referenced through
IAPKIT_URL like the rest of the file.
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/docs/src/pages/docs/updates/migration.tsx (1)
7-26: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick winCompute
lastCompatibleMajorfromremovalVersioninstead of hardcoding it.
nativePackageshardcodeslastCompatibleMajor: '2.x'for each entry, but the file already defineslastCompatibleMajor(removalVersion)to derive this value from the removal version. If a future entry'sremovalVersionchanges (for example to4.0.0), the hardcoded string will not update, and the page will show an incorrect "last compatible major" without any error.Use the helper for
nativePackagestoo, the same way it is used forLIBRARIES.♻️ Proposed fix
const nativePackages = [ { name: 'OpenIAP Spec', - lastCompatibleMajor: '2.x', removalVersion: '3.0.0', }, { name: 'openiap-apple', - lastCompatibleMajor: '2.x', removalVersion: '3.0.0', }, { name: 'openiap-google', - lastCompatibleMajor: '2.x', removalVersion: '3.0.0', }, ] as const; const lastCompatibleMajor = (removalVersion: string) => `${Number(removalVersion.split('.')[0]) - 1}.x`;{nativePackages.map((item) => ( <tr key={item.name}> <td>{item.name}</td> <td> - <code>{item.lastCompatibleMajor}</code> + <code>{lastCompatibleMajor(item.removalVersion)}</code> </td> <td> <code>{item.removalVersion}</code> </td> </tr> ))}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/docs/src/pages/docs/updates/migration.tsx` around lines 7 - 26, Update each nativePackages entry to derive lastCompatibleMajor by calling the existing lastCompatibleMajor(removalVersion) helper instead of hardcoding '2.x', matching the established LIBRARIES usage and keeping each entry’s removalVersion as the source of truth.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/docs/src/styles/documentation.css`:
- Around line 382-385: Add a blank line in the .docs-sidebar-handle rule between
the custom property declarations (--docs-bookmark and --docs-bookmark-hover) and
the position declaration to satisfy stylelint's declaration-empty-line-before
rule.
- Around line 427-450: Add a .docs-sidebar.is-resizing rule in documentation.css
with transition: none, ensuring width and flex-basis do not animate while
setSidebarWidth handles qualifying pointermove events.
In `@packages/docs/src/styles/ecosystem-diagram.css`:
- Around line 405-407: Add an empty line immediately before the
grid-template-columns declaration in the ecosystem diagram styles block,
preserving the existing column values.
- Line 188: Update both font-family declarations in ecosystem-diagram.css,
including the declarations around lines 188 and 217, to remove quotes from the
Monaco and Consolas font names while preserving the existing font stack and
fallback order.
---
Outside diff comments:
In `@packages/docs/src/pages/docs/updates/migration.tsx`:
- Around line 7-26: Update each nativePackages entry to derive
lastCompatibleMajor by calling the existing lastCompatibleMajor(removalVersion)
helper instead of hardcoding '2.x', matching the established LIBRARIES usage and
keeping each entry’s removalVersion as the source of truth.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 402cd5c3-f19e-474d-8c1f-41a0f15a67a5
⛔ Files ignored due to path filters (8)
libraries/kmp-iap/images/create_release_and_tag.pngis excluded by!**/*.pnglibraries/kmp-iap/images/draft_release.pngis excluded by!**/*.pnglibraries/kmp-iap/images/github_releases.pngis excluded by!**/*.pnglibraries/kmp-iap/images/github_secrets.pngis excluded by!**/*.pnglibraries/kmp-iap/images/published_on_maven_central.pngis excluded by!**/*.pnglibraries/kmp-iap/images/release_settings.pngis excluded by!**/*.pnglogo.pngis excluded by!**/*.pngpackages/docs/public/docs/images/openiap-mcp-iphone-purchase.pngis excluded by!**/*.png
📒 Files selected for processing (53)
knowledge/_claude-context/context.mdknowledge/internal/07-docs-consistency.mdlibraries/expo-iap/README.mdlibraries/expo-iap/example/amazon.sdktester.jsonlibraries/flutter_inapp_purchase/README.mdlibraries/godot-iap/README.mdlibraries/kmp-iap/README.mdlibraries/kmp-iap/images/create_release_and_tag.webplibraries/kmp-iap/images/draft_release.webplibraries/kmp-iap/images/github_releases.webplibraries/kmp-iap/images/github_secrets.webplibraries/kmp-iap/images/published_on_maven_central.webplibraries/kmp-iap/images/release_settings.webplibraries/react-native-iap/README.mdlibraries/react-native-iap/example/amazon.sdktester.jsonlogo.webppackages/docs/public/docs/images/openiap-mcp-iphone-purchase.webppackages/docs/public/llms-full.txtpackages/docs/public/llms.txtpackages/docs/public/logos/android.webppackages/docs/public/logos/apple.webppackages/docs/public/logos/expo-iap.webppackages/docs/public/logos/expo.webppackages/docs/public/logos/flutter.webppackages/docs/public/logos/flutter_inapp_purchase.webppackages/docs/public/logos/godot-iap.webppackages/docs/public/logos/godot.webppackages/docs/public/logos/horizonos.webppackages/docs/public/logos/kmp-iap.webppackages/docs/public/logos/kmp.webppackages/docs/public/logos/maui-iap.webppackages/docs/public/logos/openiap-apple.webppackages/docs/public/logos/openiap-google.webppackages/docs/public/logos/openiap-gql.webppackages/docs/public/logos/openiap.webppackages/docs/public/logos/react-native-iap.webppackages/docs/public/logos/react.webppackages/docs/public/sitemap.xmlpackages/docs/scripts/render-mcp-demo-video.mjspackages/docs/src/components/EcosystemDiagram.tsxpackages/docs/src/lib/searchData.tspackages/docs/src/pages/docs/ecosystem.tsxpackages/docs/src/pages/docs/index.tsxpackages/docs/src/pages/docs/setup/flutter.tsxpackages/docs/src/pages/docs/types/purchase.tsxpackages/docs/src/pages/docs/updates/announcements.tsxpackages/docs/src/pages/docs/updates/migration.tsxpackages/docs/src/pages/docs/updates/releases.tsxpackages/docs/src/styles/documentation.csspackages/docs/src/styles/ecosystem-diagram.csspackages/google/ALTERNATIVE_BILLING.mdscripts/agent/compile-context.tsscripts/audit-deprecation-schedule.mjs
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/docs/src/pages/docs/lifecycle/subscription.tsx`:
- Around line 166-170: Qualify the Android summary at
packages/docs/src/pages/docs/lifecycle/subscription.tsx#L166-L170 and
packages/docs/src/pages/docs/lifecycle/subscription.tsx#L1431-L1433 to refer
specifically to “subscription lifecycle data,” or include the other
client-visible fields productId, purchaseToken, transactionDate, and
purchaseState; keep both summaries consistent.
- Around line 1120-1126: Update both subscription state lists in
packages/docs/src/pages/docs/lifecycle/subscription.tsx at lines 1120-1126 and
1149-1155 to include SUBSCRIPTION_STATE_PENDING,
SUBSCRIPTION_STATE_PENDING_PURCHASE_CANCELED, and
SUBSCRIPTION_STATE_UNSPECIFIED; document that existing subscriptions in the
pending-purchase-canceled state should be resolved using linkedPurchaseToken.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 66ffdf40-0083-44ec-95f1-d6ceda8f6b21
📒 Files selected for processing (3)
packages/docs/src/lib/searchData.tspackages/docs/src/pages/docs/lifecycle/subscription.tsxpackages/docs/src/pages/docs/types/active-subscription.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/docs/src/lib/searchData.ts
Address CodeRabbit review on #315. The .docs-sidebar transition added for collapse/restore also applied to the drag: setSidebarWidth runs on every qualifying pointermove, so each new width animated over 240ms and the column trailed the cursor. .docs-sidebar.is-resizing now sets transition: none, so a drag snaps and only the collapse/restore animates. Verified in the browser: mid-drag transition-property is none and the width during the drag already equals the settled width (lag 0px, was ~240ms behind). Also from the same review: - blank line between the custom properties and the first declaration in .docs-sidebar-handle and .eco-grid (declaration-empty-line-before) - the mono font stack was duplicated in .eco-node-name and .eco-node-version; it is now a single --eco-mono token, which also drops the unnecessary quotes around Monaco and Consolas (font-family-name-quotes)
…id summary Address the second CodeRabbit round on #315. purchases.subscriptionsv2 returns nine SubscriptionState values; both lists on the page carried only six. Added SUBSCRIPTION_STATE_PENDING, SUBSCRIPTION_STATE_PENDING_PURCHASE_CANCELED and SUBSCRIPTION_STATE_UNSPECIFIED, and documented that a cancelled pending upgrade/downgrade is resolved back to its still-active subscription through linkedPurchaseToken. The repo's own Play normalizers in packages/kit already switch on SUBSCRIPTION_STATE_PENDING, so the page was behind its own backend. The Android takeaway and summary said client-side data is "limited to" three subscription fields, which reads as if the purchase carries nothing else. Both now scope that claim to subscription lifecycle data and name productId, purchaseToken, transactionDate and purchaseState as the client-visible purchase fields.
…w and two iOS claims Findings from the review-self round run because CodeRabbit was rate limited on 2d0d1cc. All seven were reproduced before fixing. Sidebar Collapsed, the sidebar is 0 wide and the handle sits on its edge, so `clientX - sidebarLeft` is only ~25px however the pointer moves. Any travel over the 4px drag threshold therefore fell into the "dragged past the minimum" branch, which re-collapsed an already-collapsed sidebar and tore down the listeners, so the release never reached the click-toggle. The gesture did nothing, silently, and above 768px the bookmark is the only affordance - the drawer toggle is display:none there - with the state persisted to localStorage. Reproduced: collapse, jitter 6px, still collapsed. A drag that starts collapsed is now only a reopen when it travels out past the minimum; anything shorter falls through to the release, which toggles open. Verified: 6px jitter reopens, plain click still collapses, a deliberate 320px drag reopens and resizes to 334px. The prefers-reduced-motion block declared transition:none at (0,1,0) while the collapsed rule re-declares it at (0,3,0), so collapse still animated and only restore was instant. The block now lists the states it has to beat. Verified with reducedMotion: 'reduce' - 0s in both directions. Ecosystem diagram The auto-fit track floors (290px grid, 240px pair) could not shrink, so at a 320px viewport a library card ran 17px past its band border and 1px past the viewport. minmax(min(...px, 100%), 1fr) lets the track collapse. Verified at 280/320/390px: cards now sit 15px inside the band and document scrollWidth equals the viewport at every width. The band title and the "types from openiap-gql" chip both ride the top border nowrap, and overlapped under ~340px. The chip now hides there; the figcaption still carries the relationship. Docs accuracy Two claims I introduced in 27f9e64 were wrong: - getAvailablePurchases: the page cited the native Swift default (onlyIncludeActiveItemsIOS ?? false). Every framework SDK a reader of this page uses normalizes to ?? true - expo-iap useIAP.ts:420 and index.ts:757, react-native-iap index.ts:999 - so the documented default was backwards and a history screen built from it would silently show only active entitlements. - The iOS flow told readers to branch on purchaseState 'pending' and 'unknown'. StoreKitTypesBridge.swift:133 hardcodes .purchased for every PurchaseIOS, so both branches are unreachable; Ask to Buy arrives as a PurchaseError with ErrorCode.DeferredPayment (Types.swift:115). Also replaced a comment citing documentation.css line numbers that this same branch shifted, with the selectors and breakpoints it meant.
Summary
/docs/ecosystemrenders a real, linkable component instead of a static 2226px webp/docs/updates/deprecationsbecomes/docs/updates/migration, restructured as one section per major trainChanges
Ecosystem diagram (
packages/docs/src/components/EcosystemDiagram.tsx)LIBRARIESinsrc/lib/images.ts; adding a library there makes it appear with no edit hereOPENIAP_VERSIONSand each library's package metadata, so a release train updates the figure automatically.doc-pagecan be ~350px wide at a 769px viewport and a media query would be wrong by constructionMigration page
sitemap.xmland other pages deep-link its anchors); every anchor id is unchangedLast compatible majorisnowrapsite-wide and overflowed its 26% track undertable-layout: fixedscripts/audit-deprecation-schedule.mjshardcoded the old path and file name; updated, or CI would have failed on the renameSidebar
display: none, andvisibilityflips only after the width transition so hidden links leave the tab order without cutting the animation shortAssets
packages/docs/public/logos: 11 MB → 164 KB, including a newmaui-iapicon so every framework library has onelogo.png1.57 MB →logo.webp123 KB; the PNG stays as a 512px shim because READMEs of already-published npm/pub.dev versions fetch that exact raw URL.github/pr-previewsstay PNG — the former are required by Android/iOS export, the latter are linked from already-posted PRsPre-existing bugs fixed along the way
kmp-iap/README.mdpointed atdocs/static/img/logo.png, a path that does not exist in the repoamazon.sdktester.json(both example apps, 10 entries) pointedsmallIconUrlatopeniap.dev/img/logo.png— no/img/directory exists, and the SPA fallback returns 200text/htmlrather than 404, so no link checker would catch itTest plan
bun run typecheckpassesbun run buildpassesbunx prettier --checkpassesbun run audit:docscleanbun run audit:paritypassesnode scripts/audit-deprecation-schedule.mjspasses/docs/updates/deprecations#flutter-original-json-androidredirects with the anchor preservedscrollWidth === clientWidth(no overflow)🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Documentation
Follow-up: subscription lifecycle doc corrections
Audited every checkable claim on
/docs/lifecycle/subscriptionagainst the GraphQL SSOT and the native modules — 207 claims, 42 candidates, 37 confirmed wrong after an adversarial refutation pass.Names that do not exist in the schema
ActiveSubscriptionAndroidis not a type —getActiveSubscriptionsreturns[ActiveSubscription!]!on both platforms, and the iOS tab of the same block already said so, so the two tabs contradicted each othertransactionState→purchaseState(renamed in the 3.0 train; listed as removed in this repo's own migration catalog)isAcknowledged→isAcknowledgedAndroid,purchaseTime→transactionDate,offerType/offerIdentifier→renewalOfferType/renewalOfferIdValues that cannot occur
PurchaseStateis exactlyPending | Purchased | Unknown— the page listedfailedanddeferredPURCHASED (1)) instead of the lowercase wire valuesexpirationReasoncarries StoreKit's raw integer as a string, so all five symbolic names were fictionalsubscriptionStateis serialized with theSUBSCRIPTION_STATE_prefixAndroid capability understated in five places — the client does expose
pendingPurchaseUpdateAndroidandisSuspendedAndroid, both mapped by the Play native module.Verified in the browser that no stale identifier survives in either platform tab, and that the anchors other pages deep-link into still resolve.