feat(frontend): unlocks/inventory UI, sigil designer, listening-tool EVP threshold
Workstream F of the character-depth/ghost-log spec.
- InventoryPanel.tsx: essence balance, owned unlocks/items (terminal
listing + Codex-style rarity-glow borders on items), and a buy flow
for a small fixed unlock catalog (currently just "listening_tool",
the only key the contract names) with afford/can't-afford button
states. Prices are fetched from a best-guess /api/inventory/catalog
endpoint and fall back to a flagged "(est.)" price sourced from the
spec's own worked example when that endpoint isn't available yet —
the contract doesn't define a price-list REST shape.
- SigilDesigner.tsx + lib/sigil.ts: constrained geometric builder —
points snap to 24 fixed clock-face slots around a circle, capped at
12 to match the backend's payload limit, connected in placement
order. Five hand-drawn stroke-only rune glyphs (eye/crescent/key/
spiral/thorn) overlay the center. Point-cap enforcement, rune
selection and payload-shape building are pure functions in
lib/sigil.ts, unit-tested directly. Saves via POST
/api/inventory/sigils (path inferred; payload shape matches the
contract exactly: {"points": [[x,y],...], "rune": str}). Art
direction: the builder itself reads like a plotting/debug tool
(crosshair cursor, monospace coordinate HUD) while the rendered
lines + rune glow violet, consistent with GhostGlyph's conventions.
- lib/evp.ts: new evpThresholdDb(hasListeningTool) pure function and
EvpListener.start() now accepts { hasListeningTool } to lower the
EVP anomaly threshold (8dB -> 4dB) when the unlock is owned — wired
from useAuth().user.unlocks in SeancePage's EvpPanel, a real
gameplay effect on which faint signals register as anomalies.
- api.ts User type gains unlocks/essence per the contract's /auth/me
extension; new InventoryPage.tsx mounts both components behind auth
at /inventory, linked from the séance nav.
166/166 tests pass (137 pre-existing + 29 new), i18n coverage check
clean, tsc -b clean.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -13,6 +13,7 @@
|
||||
"home": "the threshold",
|
||||
"codex": "codex",
|
||||
"shop": "shop",
|
||||
"inventory": "inventory",
|
||||
"seance": "séance",
|
||||
"enter": "enter",
|
||||
"logout": "depart"
|
||||
@@ -333,6 +334,43 @@
|
||||
},
|
||||
"honesty": "the Ultimate Quantum Box is in active development — this is a reservation manifest, no payment is taken. the website works fully without it; hardware only knocks louder."
|
||||
},
|
||||
"inventory": {
|
||||
"back": "← back to the séance",
|
||||
"pageTitle": "THE SHELF",
|
||||
"pageSubtitle": "what the veil has left you, and what it will still trade for",
|
||||
"title": "inventory",
|
||||
"essenceLabel": "essence",
|
||||
"unlocksTitle": "unlocks",
|
||||
"noUnlocks": "nothing unlocked yet",
|
||||
"ownedTag": "[OWNED]",
|
||||
"lockedTag": "[LOCKED]",
|
||||
"priceValue": "{{price}} essence",
|
||||
"priceUnknown": "price unknown",
|
||||
"priceEstimate": "(est.)",
|
||||
"buy": "buy",
|
||||
"buying": "buying…",
|
||||
"buyInsufficient": "not enough essence",
|
||||
"buyError": "the purchase failed",
|
||||
"itemsTitle": "items",
|
||||
"itemsLoading": "reading the shelf…",
|
||||
"itemsEmpty": "nothing collected yet",
|
||||
"sigil": {
|
||||
"title": "sigil designer",
|
||||
"canvasLabel": "circle of anchor points",
|
||||
"slotLabel": "anchor point {{slot}}",
|
||||
"coordLabel": "coords",
|
||||
"hoverHint": "hover an anchor point",
|
||||
"pointsLabel": "points",
|
||||
"lastLabel": "last",
|
||||
"runeLabel": "rune",
|
||||
"nameLabel": "name",
|
||||
"namePlaceholder": "untitled sigil",
|
||||
"save": "inscribe sigil",
|
||||
"saving": "inscribing…",
|
||||
"saved": "the sigil is bound",
|
||||
"error": "the sigil would not hold"
|
||||
}
|
||||
},
|
||||
"haunting": {
|
||||
"whisper": {
|
||||
"behindYou": "behind you",
|
||||
|
||||
Reference in New Issue
Block a user