Commit e59acf4
committed
feat(desktop): add overrides-only pricing editor with a catalog picker (#4164)
Add the editable Pricing Settings surface (#2015) to the migrated
`features/usage` feature (#4425) as the Usage "pricing" tab, over the Host's CAS
+ reconciliation protocol. Per the maintainer direction on #2218 the surface is
**overrides-only**: the table lists only the user's custom rows, and the built-in
models.dev catalog (~1.4k rows) is reached only through the Add flow — never
rendered as a table. (#3129's user-overridable model facts deliberately exclude
pricing and ship no UI, so pricing remains its own dedicated surface.)
Renderer (feature-owned, ratchet-clean):
- Pricing controller, view-model, copy, and editor UI under `features/usage/`,
rendered as the Usage "pricing" tab. Its services come from a dedicated
`UsagePricingServices` port + provider + `platform/desktop` adapter wired
through `composition/desktop-feature-services.tsx`, so the sole
`window.maka.settings.pricing` bridge access stays in the platform zone.
- Overrides-only table: `overrideRows` = the Host's `custom` entries (the Host
collapses an overridden built-in into one custom row). The Usage range/summary
toolbar is hidden on this tab (#2015 acceptance #2 — not time-scoped).
- Add flow uses an Astryx `Typeahead` catalog picker over the Host's `builtin`
entries (renders only the top matches, never the full list; a pick pre-fills
the built-in price), with a manual-entry fallback for a model not in the
catalog (local/new keys). Edit locks the key. Duplicate detection stays over
the full built-in ∪ overrides union.
Correctness (fixes found in review of the earlier full-table revision):
- A committed mutation fences an in-flight reload (shared authority sequence), so
a slow refresh can't overwrite the saved authority or clear a write-block.
- A Host generation change resets all transient state (editor/draft/busy latches
+ action guard), so a dialog can't stick saving and an old-Host draft can't be
saved onto the new authority.
- An Add conflict whose key now exists elsewhere converts the Add into an Edit
locked on that key, so the required second save upserts instead of being
silently blocked by the duplicate check.
- A saved-but-refresh-failed outcome clears the now-stale list (no speculative
final list, per #2015) while retaining the draft until a successful refresh.
Main / preload: the CAS pricing IPC (`usage:pricing:load` / `usage:pricing:mutate`)
over `DesktopRuntimeHostClient`, a public `reconcilePricingMutation` for the
reconciled-control path (reload + compare intent; never replay), and the
declaration-only `desktop-pricing.d.ts` + main-only `desktop-pricing-decode.ts`.
Core: remove the orphaned `UsageStats.pricing` field + its usage-stats projection.
Tests: pricing view-model + a `PricingEditor` render suite (overrides-only table,
catalog/manual Add, saved / refresh-failed / conflict / reconcile-unavailable /
invalid-draft, and one regression per correctness fix above), plus the load/mutate
IPC (base pass-through, malformed base, reconcile-no-replay). A Desktop E2E drives
设置 → 使用统计 → 定价配置: overrides-only (no 内置 rows), the absent range toolbar
(#2), the catalog/manual Add UI, and editor focus restore (#11). Storybook stories
(populated / empty / loading / load-failed) and the Astryx surface inventory
regenerated.
Refs #4164 #2015 #4425 #2218
Generated-by: Claude Code1 parent 6ab7251 commit e59acf4
35 files changed
Lines changed: 3243 additions & 180 deletions
File tree
- apps/desktop
- e2e
- src
- main
- __tests__
- preload
- renderer
- composition
- features/usage
- controller
- ui
- platform/desktop
- settings
- styles/settings
- stories/settings
- docs
- packages/core/src
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
Lines changed: 0 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
226 | 226 | | |
227 | 227 | | |
228 | 228 | | |
229 | | - | |
230 | 229 | | |
231 | 230 | | |
232 | 231 | | |
| |||
0 commit comments