From 0005fff01d2d816dcfe651f7536d1eabedad79b5 Mon Sep 17 00:00:00 2001 From: 6d486f49 Date: Thu, 25 Jun 2026 19:27:58 -0400 Subject: [PATCH] Vibe link and card meta data fixes --- Chrono/Web/Components/CardDialog.razor.css | 2 +- Chrono/Web/Pages/Cards.razor | 83 ++++++++++++++++--- Chrono/Web/Pages/Cards.razor.css | 56 +++++++++++++ chrono.docs/.obsidian/workspace.json | 20 ++--- .../Agent/[[Phasetide]]/Stern Arbiter.md | 4 +- .../[[Splintergleam]]/Boatswain Corvus.md | 2 +- .../[[Sungrace]]/Time Devourer Soval.md | 2 +- .../Spell/[[Phasetide]]/By the Numbers.md | 2 +- 8 files changed, 143 insertions(+), 28 deletions(-) diff --git a/Chrono/Web/Components/CardDialog.razor.css b/Chrono/Web/Components/CardDialog.razor.css index 4f24121..fa45919 100644 --- a/Chrono/Web/Components/CardDialog.razor.css +++ b/Chrono/Web/Components/CardDialog.razor.css @@ -235,7 +235,7 @@ text-decoration-color: #7b73ff; } -@ + @media (max-width: 768px) { .detail-layout { flex-direction: column; diff --git a/Chrono/Web/Pages/Cards.razor b/Chrono/Web/Pages/Cards.razor index 6952644..defaacc 100644 --- a/Chrono/Web/Pages/Cards.razor +++ b/Chrono/Web/Pages/Cards.razor @@ -26,11 +26,15 @@ @onclick='@(() => SetCategory("Immortalized"))'> Immortalized - + + + +
+ @@ -206,21 +210,70 @@ private IEnumerable ApplyFilters() { - var filtered = allCards.Where(c => + var primarySet = allCards.Where(c => c.MatchesSearch(search) && (categoryFilter == "" || c.Category == categoryFilter) && (factionFilter == "" || c.Faction == factionFilter) && (costFilter == "" || c.Cost?.ToString() == costFilter) ); - return sortBy switch + var sortedPrimary = sortBy switch { - "Cost" => sortDescending ? filtered.OrderByDescending(c => c.Cost) : filtered.OrderBy(c => c.Cost), - "Efficiency" => sortDescending ? filtered.OrderByDescending(c => c.StatEfficiency) : filtered.OrderBy(c => c.StatEfficiency), - "Attack" => sortDescending ? filtered.OrderByDescending(c => c.Attack) : filtered.OrderBy(c => c.Attack), - "Health" => sortDescending ? filtered.OrderByDescending(c => c.Health) : filtered.OrderBy(c => c.Health), - _ => sortDescending ? filtered.OrderByDescending(c => c.Name) : filtered.OrderBy(c => c.Name) + "Cost" => sortDescending ? primarySet.OrderByDescending(c => c.Cost) : primarySet.OrderBy(c => c.Cost), + "Efficiency" => sortDescending ? primarySet.OrderByDescending(c => c.StatEfficiency) : primarySet.OrderBy(c => c.StatEfficiency), + "Attack" => sortDescending ? primarySet.OrderByDescending(c => c.Attack) : primarySet.OrderBy(c => c.Attack), + "Health" => sortDescending ? primarySet.OrderByDescending(c => c.Health) : primarySet.OrderBy(c => c.Health), + _ => sortDescending ? primarySet.OrderByDescending(c => c.Name) : primarySet.OrderBy(c => c.Name) }; + + if (!agentLink) + { + return sortedPrimary; + } + + var result = new List(); + var seen = new HashSet(); + foreach (var card in sortedPrimary) + { + if (seen.Contains(card.Name)) continue; + + // Determine the "Chain Head" (the Agent) + var head = card; + if (card.Category == "Immortalized" && card.ImmortalizeFrom != null) + { + var source = allCards.FirstOrDefault(c => c.Name == card.ImmortalizeFrom); + if (source != null) head = source; + } + + // Add the chain starting from head + if (!seen.Contains(head.Name)) + { + result.Add(head); + seen.Add(head.Name); + } + + if (head.ImmortalizeTo != null) + { + foreach (var targetName in head.ImmortalizeTo) + { + if (seen.Contains(targetName)) continue; + var linked = allCards.FirstOrDefault(c => c.Name == targetName); + if (linked != null) + { + result.Add(linked); + seen.Add(linked.Name); + } + } + } + + // Ensure the original card is added if it was somehow not part of the head's chain + if (!seen.Contains(card.Name)) + { + result.Add(card); + seen.Add(card.Name); + } + } + return result; } private void SetImmortal(string imm) @@ -253,6 +306,12 @@ private void ToggleAgentLink() { agentLink = !agentLink; + OnToggleAgentLink(); + } + + private void OnToggleAgentLink() + { + _ = AnalyticsService.TrackEvent("toggle_agent_link", new { enabled = agentLink }); } private void ClearCostFilter() diff --git a/Chrono/Web/Pages/Cards.razor.css b/Chrono/Web/Pages/Cards.razor.css index f4b422c..a938d0c 100644 --- a/Chrono/Web/Pages/Cards.razor.css +++ b/Chrono/Web/Pages/Cards.razor.css @@ -74,6 +74,62 @@ font-size: 0.9rem; } +.category-divider { + width: 1px; + height: 1.5rem; + background: var(--border); + margin: 0 0.6rem; + align-self: center; + opacity: 0.6; +} + +.tab.link-toggle { + border-style: dashed; + gap: 0.6rem; + user-select: none; + padding-right: 0.8rem; + margin-left: 0.2rem; +} + +.tab.link-toggle.active { + border-style: solid; + background: rgba(108, 99, 255, 0.1); + border-color: var(--accent); + color: var(--text-primary); +} + +.toggle-switch { + width: 28px; + height: 16px; + background: var(--bg-hover); + border-radius: 10px; + position: relative; + transition: all 0.2s; + border: 1px solid var(--border); + flex-shrink: 0; +} + +.toggle-switch.on { + background: var(--accent); + border-color: rgba(255, 255, 255, 0.2); +} + +.toggle-switch::after { + content: ''; + position: absolute; + width: 10px; + height: 10px; + background: white; + border-radius: 50%; + top: 2px; + left: 2px; + transition: transform 0.2s; +} + +.toggle-switch.on::after { + transform: translateX(12px); +} + /* ── Filter Bar ── */ .filter-bar { display: flex; diff --git a/chrono.docs/.obsidian/workspace.json b/chrono.docs/.obsidian/workspace.json index 3397b4f..68f81ff 100644 --- a/chrono.docs/.obsidian/workspace.json +++ b/chrono.docs/.obsidian/workspace.json @@ -13,12 +13,12 @@ "state": { "type": "markdown", "state": { - "file": "Deck/Overflowing Security.md", + "file": "Agent/[[Phasetide]]/Stern Arbiter.md", "mode": "source", "source": false }, "icon": "lucide-file", - "title": "Overflowing Security" + "title": "Stern Arbiter" } } ] @@ -53,7 +53,7 @@ "state": { "type": "search", "state": { - "query": "The Ripper", + "query": "...", "matchingCase": false, "explainSearch": false, "collapseAll": false, @@ -74,12 +74,12 @@ "title": "Bookmarks" } } - ] + ], + "currentTab": 1 } ], "direction": "horizontal", - "width": 331.5, - "collapsed": true + "width": 331.5 }, "right": { "id": "47f3bcd7aed212ab", @@ -186,8 +186,11 @@ }, "active": "852a1aaf6b54ec43", "lastOpenFiles": [ - "Deck/Big Energy.md", + "Immortalized/[[Sungrace]]/Time Devourer Soval.md", + "Spell/[[Phasetide]]/By the Numbers.md", + "Immortalized/[[Splintergleam]]/Boatswain Corvus.md", "Deck/Overflowing Security.md", + "Deck/Big Energy.md", "Agent/[[Lifeblood]]/Aardvark Precinct Captain.md", "Agent/[[Lifeblood]]/Egg Tender.md", "Quest/Daily Wins.md", @@ -212,9 +215,6 @@ "Rule/Fast.md", "_Store.base", "_Timeline.base", - "Store/Core Set Packs x1.md", - "Store/Core Set Packs x5.md", - "Store/Core Set Packs x10.md", "_Quests.base", "Quest", "Store", diff --git a/chrono.docs/Agent/[[Phasetide]]/Stern Arbiter.md b/chrono.docs/Agent/[[Phasetide]]/Stern Arbiter.md index 829b562..6bf62bf 100644 --- a/chrono.docs/Agent/[[Phasetide]]/Stern Arbiter.md +++ b/chrono.docs/Agent/[[Phasetide]]/Stern Arbiter.md @@ -6,8 +6,8 @@ rarity: Lost cost: 7 attack: 5 health: 5 -description: "[[Play]]: Return an Action from your Graveyard to your hand. It costs 3 less and has \"If I would be put into a Graveyard, instead [[Erase]] me.\"." -immortalizeWhen: "I've seen you play 3+ cards wi..." +description: '[[Play]]: Return an Action from your Graveyard to your hand. It costs 3 less and has "If I would be put into a Graveyard, instead [[Erase]] me.".' +immortalizeWhen: I've seen you play 3+ cards with reduced cost. immortalizeTo: - "[[Grand Judge Dhael]]" immortalizeFrom: N/A diff --git a/chrono.docs/Immortalized/[[Splintergleam]]/Boatswain Corvus.md b/chrono.docs/Immortalized/[[Splintergleam]]/Boatswain Corvus.md index e09fb2a..13ffb43 100644 --- a/chrono.docs/Immortalized/[[Splintergleam]]/Boatswain Corvus.md +++ b/chrono.docs/Immortalized/[[Splintergleam]]/Boatswain Corvus.md @@ -6,7 +6,7 @@ rarity: Lost cost: 7 attack: 6 health: 10 -description: "[[Overpower]]. Round Start: Deal 1 to the enemy Core twice. When you deal non-combat damage to the enemy Core, grant the weakest enemy Exposed. [[Core Strike]]: Deal 3 to all enemies and t..." +description: "[[Overpower]]. Round Start: Deal 1 to the enemy Core twice. When you deal non-combat damage to the enemy Core, grant the weakest enemy Exposed. [[Core Strike]]: Deal 3 to all enemies and the enemy Core.]]" immortalizeWhen: N/A immortalizeTo: - N/A diff --git a/chrono.docs/Immortalized/[[Sungrace]]/Time Devourer Soval.md b/chrono.docs/Immortalized/[[Sungrace]]/Time Devourer Soval.md index d01d736..31428d9 100644 --- a/chrono.docs/Immortalized/[[Sungrace]]/Time Devourer Soval.md +++ b/chrono.docs/Immortalized/[[Sungrace]]/Time Devourer Soval.md @@ -6,7 +6,7 @@ rarity: Lost cost: 10 attack: 12 health: 12 -description: "For each Timeline in the Timeline Stack, I gain a random positive keyword. The first time each round I would be destroyed, instead heal me to my maximum durability and destroy the..." +description: For each Timeline in the Timeline Stack, I gain a random positive keyword. The first time each round I would be destroyed, instead heal me to my maximum durability and destroy the current Timeline. immortalizeWhen: N/A immortalizeTo: - N/A diff --git a/chrono.docs/Spell/[[Phasetide]]/By the Numbers.md b/chrono.docs/Spell/[[Phasetide]]/By the Numbers.md index 7f5e837..b5053e5 100644 --- a/chrono.docs/Spell/[[Phasetide]]/By the Numbers.md +++ b/chrono.docs/Spell/[[Phasetide]]/By the Numbers.md @@ -4,7 +4,7 @@ category: Spell artist: Bi Pi rarity: Common cost: 2 -description: "Reveal the top card of both decks. Create a copy of the card with the higher cost in hand, then shuffle both cards into their respective decks. In a tie, create a copy of both card..." +description: Reveal the top card of both decks. Create a copy of the card with the higher cost in hand, then shuffle both cards into their respective decks. In a tie, create a copy of both cards. faction: "[[Phasetide]]" set: "[[Core Set]]" speed: "[[Immediate]]"