From 7954c579d30921ef4762620cad2a58e6f603185e Mon Sep 17 00:00:00 2001 From: Phillip Tarrant Date: Fri, 10 Jul 2026 20:39:09 -0500 Subject: [PATCH] =?UTF-8?q?feat(theme):=20showcase=20harness=20=E2=80=94?= =?UTF-8?q?=20the=20human-eyeball=20proof?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 (1M context) --- client/scenes/theme/theme_showcase.tscn | 5 + client/scripts/theme/theme_showcase.gd | 97 ++++++++++++++++++++ client/scripts/theme/theme_showcase.gd.uid | 1 + client/tests/unit/test_theme_showcase.gd | 11 +++ client/tests/unit/test_theme_showcase.gd.uid | 1 + 5 files changed, 115 insertions(+) create mode 100644 client/scenes/theme/theme_showcase.tscn create mode 100644 client/scripts/theme/theme_showcase.gd create mode 100644 client/scripts/theme/theme_showcase.gd.uid create mode 100644 client/tests/unit/test_theme_showcase.gd create mode 100644 client/tests/unit/test_theme_showcase.gd.uid diff --git a/client/scenes/theme/theme_showcase.tscn b/client/scenes/theme/theme_showcase.tscn new file mode 100644 index 0000000..531fed8 --- /dev/null +++ b/client/scenes/theme/theme_showcase.tscn @@ -0,0 +1,5 @@ +[gd_scene load_steps=2 format=3] +[ext_resource type="Script" path="res://scripts/theme/theme_showcase.gd" id="1"] +[node name="ThemeShowcase" type="Control"] +anchors_preset = 15 +script = ExtResource("1") diff --git a/client/scripts/theme/theme_showcase.gd b/client/scripts/theme/theme_showcase.gd new file mode 100644 index 0000000..f2ac838 --- /dev/null +++ b/client/scripts/theme/theme_showcase.gd @@ -0,0 +1,97 @@ +# client/scripts/theme/theme_showcase.gd +extends Control +## The eyeball proof for the shared Theme (spec §7). Builds every token, stylebox, +## font, surface, and bar on one scrollable screen. No AI, no state. §2: presentation. +const Palette = preload("res://scripts/theme/palette.gd") +const ThemeKeys = preload("res://scripts/theme/theme_keys.gd") +const GAME_THEME := "res://assets/theme/game_theme.tres" + + +func _ready() -> void: + theme = load(GAME_THEME) + set_anchors_preset(Control.PRESET_FULL_RECT) + var scroll := ScrollContainer.new() + scroll.set_anchors_preset(Control.PRESET_FULL_RECT) + add_child(scroll) + var col := VBoxContainer.new() + col.custom_minimum_size = Vector2(1900, 0) + col.add_theme_constant_override("separation", 16) + scroll.add_child(col) + + col.add_child(_heading("SHARED THEME — SHOWCASE")) + col.add_child(_swatches()) + col.add_child(_variations()) + col.add_child(_rarity()) + col.add_child(_bars()) + col.add_child(_surfaces()) + + +func _heading(text: String) -> Label: + var l := Label.new() + l.text = text + l.theme_type_variation = &"Heading" + return l + + +func _swatches() -> Control: + var grid := GridContainer.new() + grid.columns = 8 + for c in [Palette.STAGE_1, Palette.BAY_STRIPE_A, Palette.DARK_PANEL_70, + Palette.SHEET_TOP, Palette.PARCHMENT_CARD, Palette.BLOOD, Palette.GOLD, + Palette.STEEL, Palette.GREEN, Palette.INK_BODY, Palette.CREAM]: + var r := ColorRect.new() + r.color = c + r.custom_minimum_size = Vector2(120, 60) + grid.add_child(r) + return grid + + +func _variations() -> Control: + var row := HBoxContainer.new() + row.add_theme_constant_override("separation", 12) + var cta := Button.new(); cta.text = "ENTER THE WORLD"; cta.theme_type_variation = ThemeKeys.PRIMARY_CTA + var tab := Button.new(); tab.text = "INACTIVE"; tab.theme_type_variation = ThemeKeys.TAB + var tab_a := Button.new(); tab_a.text = "ACTIVE"; tab_a.theme_type_variation = ThemeKeys.TAB_ACTIVE + var chip := Button.new(); chip.text = "INTIMIDATE · STR"; chip.theme_type_variation = ThemeKeys.CHIP + for b in [cta, tab, tab_a, chip]: + row.add_child(b) + var card := PanelContainer.new(); card.theme_type_variation = ThemeKeys.PARCHMENT_CARD + card.custom_minimum_size = Vector2(160, 80) + row.add_child(card) + return row + + +func _rarity() -> Control: + var row := HBoxContainer.new() + for i in range(5): + var tile := PanelContainer.new() + tile.theme_type_variation = ThemeKeys.ITEM_TILE + tile.custom_minimum_size = Vector2(72, 72) + var bar := ColorRect.new() + bar.color = Palette.rarity_color(i) + bar.custom_minimum_size = Vector2(60, 4) + tile.add_child(bar) + row.add_child(tile) + return row + + +func _bars() -> Control: + var row := HBoxContainer.new() + for kind in [&"hp", &"mp", &"stamina"]: + var pair = Palette.stat_bar(kind) + var bar := ColorRect.new() + bar.color = pair[0] + bar.custom_minimum_size = Vector2(200, 18) + row.add_child(bar) + return row + + +func _surfaces() -> Control: + var row := HBoxContainer.new() + row.custom_minimum_size = Vector2(0, 240) + for path in ["res://scenes/theme/surfaces/ParchmentPanel.tscn", + "res://scenes/theme/surfaces/DarkBay.tscn"]: + var s = load(path).instantiate() + s.custom_minimum_size = Vector2(600, 220) + row.add_child(s) + return row diff --git a/client/scripts/theme/theme_showcase.gd.uid b/client/scripts/theme/theme_showcase.gd.uid new file mode 100644 index 0000000..bde8f3e --- /dev/null +++ b/client/scripts/theme/theme_showcase.gd.uid @@ -0,0 +1 @@ +uid://ciqf5caopnsh3 diff --git a/client/tests/unit/test_theme_showcase.gd b/client/tests/unit/test_theme_showcase.gd new file mode 100644 index 0000000..c05b017 --- /dev/null +++ b/client/tests/unit/test_theme_showcase.gd @@ -0,0 +1,11 @@ +# client/tests/unit/test_theme_showcase.gd +extends "res://addons/gut/test.gd" + + +func test_showcase_instances_without_error(): + var packed = load("res://scenes/theme/theme_showcase.tscn") + assert_true(packed is PackedScene) + var node = packed.instantiate() + add_child_autofree(node) # runs _ready(); any engine error here fails the suite + assert_true(node is Control) + assert_true(node.theme is Theme, "showcase did not apply game_theme") diff --git a/client/tests/unit/test_theme_showcase.gd.uid b/client/tests/unit/test_theme_showcase.gd.uid new file mode 100644 index 0000000..5feaeb2 --- /dev/null +++ b/client/tests/unit/test_theme_showcase.gd.uid @@ -0,0 +1 @@ +uid://e4p6b65e14mw