PURPOSE

Playground tab for artifacts, split by the side prop. LEFT (instance) shows a 2-column grid that grants the artifact at runtime and tiers it up on repeat clicks. RIGHT (base) shows a picker plus a tier-values editor that pushes edits back to artifacts/<id>.ts on disk.

OWNS

  • Default export ArtifactsTab (React component, accepts TabProps: missionRef, side).
  • Local state: instanceOpen, baseOpen, selectedArtifact, grantedTiers (record of artifact id to runtime tier 0..4, default -1).
  • Local helper grantArtifact(id) that calls the mission sandbox grant and increments the tracked tier (capped at 4).
  • Internal types and helpers: ArtifactDraft, toDraft, useArtifactPanel, ArtifactPanelBody.

READS FROM

  • ../../data/artifactsARTIFACT_DEFS, ARTIFACT_MAP, ARTIFACT_TIER_COLOR_BY_IDX, ARTIFACT_TIER_NAMES, type ArtifactDef.
  • ./PlaygroundSharedPanel, PushButton, BTN_STYLE, LABEL_STYLE, EditableTextRow, EditableNumberRow, useDraft, type TabProps.
  • missionRef.current for the sandbox grant API.

PUSHES TO

  • ../../services/playgroundPushpushStats({ target: 'artifacts', id, patch: { name, description, tiers } }) writes back to artifacts/<id>.ts.
  • missionRef.current?.sandboxGrantArtifact(id) — runtime grant in the live mission.

DOES NOT

  • Does not edit the common tier (data tier indices 0..3 map to uncommon..legendary; common is computed at runtime from uncommon and is not editable here).
  • Does not persist grantedTiers across remounts — it is local UI state only.
  • Does not validate or reconcile push results beyond returning r.ok (failures are swallowed in a catch returning false).
  • Does not render anything when side === 'base' and no art is resolved from selectedArtifact.

Signals

  • isDirty from useDraft toggles the RESET button visibility in the base editor.
  • grantedTiers[id] >= 0 flips the instance button into the active style (colored border, tier label).
  • Data tier index plus one is used as the runtime tier index for color and name lookup (runtimeTier = tierIdx + 1).
  • Number step is 0.01 when absolute value is under 1, else 1.

Entry points

  • Rendered by the playground screen with side="instance" on the left column and side="base" on the right.
  • TabProps.missionRef must point at a live mission instance exposing sandboxGrantArtifact.
  • ARTIFACT_DEFS[0].id seeds selectedArtifact on mount.

Pattern notes

  • Draft-on-edit, push-on-confirm: useDraft holds the editable copy of name, description, and tiers; pushStats writes the patch back to source.
  • useMemo wraps toDraft(art) so the original snapshot changes only when the selected artifact changes.
  • ArtifactPanelBody is keyed on art.id so switching the picker remounts the editor and resets the draft.
  • Tier blocks are styled with borderLeft colored by ARTIFACT_TIER_COLOR_BY_IDX[runtimeTier] and labelled T{runtimeTier} {ARTIFACT_TIER_NAMES[runtimeTier]}.
  • Tier mutations clone the tiers array and the target tier object before assignment to keep the draft update immutable.
  • The split-by-side pattern lets one tab component supply two different surfaces in the playground layout.