From f9af6177583c01568104e42375ebfe6ea68ddf6e Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Thu, 19 Feb 2026 10:26:51 +0900 Subject: [PATCH 01/35] add skeleton impl --- lua/wikis/commons/StreamPage.lua | 201 ++++++++++++++++++++++++ lua/wikis/commons/StreamPage/Custom.lua | 10 ++ 2 files changed, 211 insertions(+) create mode 100644 lua/wikis/commons/StreamPage.lua create mode 100644 lua/wikis/commons/StreamPage/Custom.lua diff --git a/lua/wikis/commons/StreamPage.lua b/lua/wikis/commons/StreamPage.lua new file mode 100644 index 00000000000..0f1c02ddd19 --- /dev/null +++ b/lua/wikis/commons/StreamPage.lua @@ -0,0 +1,201 @@ +--- +-- @Liquipedia +-- page=Module:StreamPage +-- +-- Please see https://github.com/Liquipedia/Lua-Modules to contribute +-- + +local Lua = require('Module:Lua') + +local Array = Lua.import('Module:Array') +local Class = Lua.import('Module:Class') +local Countdown = Lua.import('Module:Countdown') +local DateExt = Lua.import('Module:Date/Ext') +local HighlightConditions = Lua.import('Module:HighlightConditions') +local Image = Lua.import('Module:Image') +local Logic = Lua.import('Module:Logic') +local Lpdb = Lua.import('Module:Lpdb') +local MatchGroupUtil = Lua.import('Module:MatchGroup/Util/Custom') +local MatchPage = Lua.requireIfExists('Module:MatchPage') +local MatchTable = Lua.import('Module:MatchTable/Custom') +local MatchTicker = Lua.import('Module:MatchTicker') +local Opponent = Lua.import('Module:Opponent/Custom') +local OpponentDisplay = Lua.import('Module:OpponentDisplay/Custom') +local Page = Lua.import('Module:Page') +local PlayerDisplay = Lua.import('Module:Player/Display/Custom') +local String = Lua.import('Module:StringUtils') +local TeamTemplate = Lua.import('Module:TeamTemplate') +local Tournament = Lua.import('Module:Tournament') + +local Condition = Lua.import('Module:Condition') +local ConditionTree = Condition.Tree +local ConditionNode = Condition.Node +local Comparator = Condition.Comparator +local BooleanOperator = Condition.BooleanOperator +local ColumnName = Condition.ColumnName +local ConditionUtil = Condition.Util + +local GridWidgets = Lua.import('Module:Widget/Grid') +local HtmlWidgets = Lua.import('Module:Widget/Html/All') +local MatchPageAdditionalSection = Lua.import('Module:Widget/Match/Page/AdditionalSection') +local MatchPageHeader = Lua.import('Module:Widget/Match/Page/Header') +local WidgetUtil = Lua.import('Module:Widget/Util') + +---@class BaseStreamPage: BaseClass +---@operator call(table): BaseStreamPage +---@field channel string +---@field provider string +---@field matches MatchGroupUtilMatch[] +local StreamPage = Class.new(function (self, args) + self.channel = assert(Logic.nilIfEmpty(args.channel)) + self.provider = assert(Logic.nilIfEmpty(args.provider)) + self.matches = {} + + self:_fetchMatches() +end) + +---@private +---@return ConditionTree +function StreamPage:_createMatchQueryCondition() + local conditions = ConditionTree(BooleanOperator.all):add{ + ConditionTree(BooleanOperator.any):add{ + ConditionNode(ColumnName(self.provider, 'stream'), Comparator.eq, self.channel), + ConditionNode(ColumnName(self.provider .. '_en_1', 'stream'), Comparator.eq, self.channel) + }, + ConditionNode(ColumnName('finished'), Comparator.eq, 0), + ConditionNode(ColumnName('date'), Comparator.neq, DateExt.defaultDateTime), + ConditionNode(ColumnName('dateexact', Comparator.eq, 1)) + } + + conditions:add(StreamPage:addMatchConditions()) + return conditions +end + +---@private +function StreamPage:_fetchMatches() + local conditions = self:_createMatchQueryCondition() + + Lpdb.executeMassQuery('match2', { + conditions = tostring(conditions), + order = 'date asc', + limit = 25, + }, function (record) + local match = MatchGroupUtil.matchFromRecord(record) + if Array.any(match.opponents, Opponent.isBye) then + return + elseif #self.matches == 5 then + return false + end + Array.appendWith(self.matches, match) + end) +end + +---@protected +---@return AbstractConditionNode|AbstractConditionNode[]? +function StreamPage:addMatchConditions() +end + +---@private +---@return Widget +function StreamPage:_header() + local match = self.matches[1] + local countdownBlock = HtmlWidgets.Div{ + css = { + display = 'block', + ['text-align'] = 'center' + }, + children = Countdown.create{ + date = DateExt.toCountdownArg(match.timestamp, match.timezoneId, match.dateIsExact), + finished = match.finished, + rawdatetime = Logic.readBool(match.finished), + } + } or nil + local tournament = Tournament.partialTournamentFromMatch(match) + Array.forEach(match.opponents, function (opponent, opponentIndex) + if opponent.type ~= Opponent.team then + return + end + ---@cast opponent MatchPageOpponent + opponent.iconDisplay = OpponentDisplay.InlineTeamContainer{ + style = 'icon', + template = opponent.template, + } + opponent.teamTemplateData = TeamTemplate.getRaw(opponent.template) + opponent.seriesDots = {} + end) + return MatchPageHeader{ + countdownBlock = countdownBlock, + isBestOfOne = match.bestof == 1, + mvp = match.extradata.mvp, + opponent1 = match.opponents[1], + opponent2 = match.opponents[2], + parent = tournament.pageName, + phase = MatchGroupUtil.computeMatchPhase(match), + stream = match.stream, + tournamentName = tournament.fullName, + highlighted = HighlightConditions.tournament(tournament) + } +end + +---@return Widget? +function StreamPage:create() + if Logic.isEmpty(self.matches) then + return + elseif self.matches[1].bracketData.matchPage then + return MatchPage{match = self.matches[1]}:render() + end + + return HtmlWidgets.Fragment{children = WidgetUtil.collect( + self:_header(), + GridWidgets.Container{gridCells = { + GridWidgets.Cell{ + cellContent = self:render(), + xs = 'ignore', + sm = 'ignore', + lg = 8, + xl = 8, + xxl = 9, + xxxl = 9, + }, + GridWidgets.Cell{ + cellContent = self:_createMatchTicker(), + xs = 'ignore', + sm = 'ignore', + lg = 4, + xl = 4, + xxl = 3, + xxxl = 3, + } + }}, + self:createBottomContent() + )} +end + +---@private +---@return string|Widget|Html|(string|Widget|Html)[]? +function StreamPage:_createMatchTicker() + local ticker = MatchTicker{ + additionalConditions = 'AND (' .. self:_createMatchQueryCondition() .. ')', + limit = 5, + newStyle = true, + ongoing = true, + upcoming = true, + wrapperClasses = {'new-match-style'}, + } + return MatchPageAdditionalSection{ + header = 'Channel Schedule', + children = ticker:query():create() + } +end + +---@protected +---@return string|Widget|Html|(string|Widget|Html)[]? +function StreamPage:render() +end + +---@protected +---@return string|Widget|Html|(string|Widget|Html)[]? +function StreamPage:createBottomContent() +end + +return StreamPage diff --git a/lua/wikis/commons/StreamPage/Custom.lua b/lua/wikis/commons/StreamPage/Custom.lua new file mode 100644 index 00000000000..2fcafdd119a --- /dev/null +++ b/lua/wikis/commons/StreamPage/Custom.lua @@ -0,0 +1,10 @@ +--- +-- @Liquipedia +-- page=Module:StreamPage/Custom +-- +-- Please see https://github.com/Liquipedia/Lua-Modules to contribute +-- + +local Lua = require('Module:Lua') + +return Lua.import('Module:StreamPage') From 9a7e5e59f0bacae23cf0d0fb681169a34a5727d9 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Thu, 19 Feb 2026 11:17:31 +0900 Subject: [PATCH 02/35] add recent matches --- lua/wikis/commons/StreamPage.lua | 96 +++++++++++++++++++++++++++++++- 1 file changed, 95 insertions(+), 1 deletion(-) diff --git a/lua/wikis/commons/StreamPage.lua b/lua/wikis/commons/StreamPage.lua index 0f1c02ddd19..00ed3fe4216 100644 --- a/lua/wikis/commons/StreamPage.lua +++ b/lua/wikis/commons/StreamPage.lua @@ -24,6 +24,7 @@ local OpponentDisplay = Lua.import('Module:OpponentDisplay/Custom') local Page = Lua.import('Module:Page') local PlayerDisplay = Lua.import('Module:Player/Display/Custom') local String = Lua.import('Module:StringUtils') +local Table = Lua.import('Module:Table') local TeamTemplate = Lua.import('Module:TeamTemplate') local Tournament = Lua.import('Module:Tournament') @@ -37,6 +38,7 @@ local ConditionUtil = Condition.Util local GridWidgets = Lua.import('Module:Widget/Grid') local HtmlWidgets = Lua.import('Module:Widget/Html/All') +local IconImage = Lua.import('Module:Widget/Image/Icon/Image') local MatchPageAdditionalSection = Lua.import('Module:Widget/Match/Page/AdditionalSection') local MatchPageHeader = Lua.import('Module:Widget/Match/Page/Header') local WidgetUtil = Lua.import('Module:Widget/Util') @@ -194,8 +196,100 @@ function StreamPage:render() end ---@protected ----@return string|Widget|Html|(string|Widget|Html)[]? +---@return Widget[]? function StreamPage:createBottomContent() + local match = self.matches[1] + + if Array.all(match.opponents, Opponent.isTbd) then + return + end + + local headToHead = self:_buildHeadToHeadMatchTable() + + return WidgetUtil.collect( + HtmlWidgets.H3{children = 'Match History'}, + HtmlWidgets.Div{ + classes = {'match-bm-match-additional'}, + children = WidgetUtil.collect( + headToHead and MatchPageAdditionalSection{ + css = {flex = '2 0 100%'}, + header = 'Head to Head', + bodyClasses = {'match-table-wrapper'}, + children = headToHead, + } or nil, + Array.map(match.opponents, function (opponent) + local matchTable = self:_buildMatchTable(opponent) + return MatchPageAdditionalSection{ + header = OpponentDisplay.InlineOpponent{opponent = opponent, teamStyle = 'hybrid'}, + bodyClasses = matchTable and {'match-table-wrapper'} or nil, + children = matchTable or IconImage{ + imageLight = match.icon, + imageDark = match.iconDark, + size = '50x32px', + } + } + end) + ) + } + ) +end + +---@private +---@param props table +---@return Html +function StreamPage:_createMatchTable(props) + local match = self.matches[1] + return MatchTable(Table.mergeInto({ + addCategory = false, + edate = match.timestamp - DateExt.daysToSeconds(1) --[[ MatchTable adds 1-day offset to make edate + inclusive, and we don't want that here ]], + limit = 5, + stats = false, + vod = false, + matchPageButtonText = 'short', + }, props)):readConfig():query():buildDisplay() +end + +---@private +---@param opponent standardOpponent +---@return Html? +function StreamPage:_buildMatchTable(opponent) + if opponent.type ~= Opponent.solo and opponent.type ~= Opponent.team then + return + end + local base = opponent.type == Opponent.team and Opponent.team or 'player' + return self:_createMatchTable{ + ['hide_tier'] = true, + limit = 5, + stats = false, + tableMode = opponent.type, + [base] = Opponent.toName(opponent), + useTickerName = true, + } +end + +---@private +---@return Html? +function StreamPage:_buildHeadToHeadMatchTable() + local match = self.matches[1] + if Array.any(match.opponents, Opponent.isTbd) then + return + elseif match.opponents[1].type ~= match.opponents[2].type then + return + elseif match.opponents[1].type ~= Opponent.solo and match.opponents[1].type ~= Opponent.team then + return + end + + local base = match.opponents[1].type == Opponent.team and Opponent.team or 'player' + + return self:_createMatchTable{ + [base] = Opponent.toName(match.opponents[1]), + ['vs' .. base] = Opponent.toName(match.opponents[2]), + tableMode = match.opponents[1].type, + showOpponent = true, + teamStyle = 'hybrid', + useTickerName = true, + } end return StreamPage From e1d8eb3a0ae44ddaeeeacecc4586a88081e0ed4c Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Thu, 19 Feb 2026 11:32:41 +0900 Subject: [PATCH 03/35] cleanup --- lua/wikis/commons/StreamPage.lua | 33 +++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/lua/wikis/commons/StreamPage.lua b/lua/wikis/commons/StreamPage.lua index 00ed3fe4216..2596f4985cf 100644 --- a/lua/wikis/commons/StreamPage.lua +++ b/lua/wikis/commons/StreamPage.lua @@ -7,6 +7,7 @@ local Lua = require('Module:Lua') +local Arguments = Lua.import('Module:Arguments') local Array = Lua.import('Module:Array') local Class = Lua.import('Module:Class') local Countdown = Lua.import('Module:Countdown') @@ -56,6 +57,12 @@ local StreamPage = Class.new(function (self, args) self:_fetchMatches() end) + +function StreamPage.run(frame) + local args = Arguments.getArgs(frame) + return StreamPage(args):create() +end + ---@private ---@return ConditionTree function StreamPage:_createMatchQueryCondition() @@ -66,7 +73,7 @@ function StreamPage:_createMatchQueryCondition() }, ConditionNode(ColumnName('finished'), Comparator.eq, 0), ConditionNode(ColumnName('date'), Comparator.neq, DateExt.defaultDateTime), - ConditionNode(ColumnName('dateexact', Comparator.eq, 1)) + ConditionNode(ColumnName('dateexact'), Comparator.eq, 1) } conditions:add(StreamPage:addMatchConditions()) @@ -144,10 +151,16 @@ function StreamPage:create() if Logic.isEmpty(self.matches) then return elseif self.matches[1].bracketData.matchPage then - return MatchPage{match = self.matches[1]}:render() + return HtmlWidgets.Fragment{children = { + '__NOTOC__', + MatchPage(self.matches[1]):render(), + HtmlWidgets.H3{children = 'Channel Schedule'}, + self:_createMatchTicker():query():create() + }} end return HtmlWidgets.Fragment{children = WidgetUtil.collect( + '__NOTOC__', self:_header(), GridWidgets.Container{gridCells = { GridWidgets.Cell{ @@ -160,7 +173,10 @@ function StreamPage:create() xxxl = 9, }, GridWidgets.Cell{ - cellContent = self:_createMatchTicker(), + cellContent = MatchPageAdditionalSection{ + header = 'Channel Schedule', + children = self:_createMatchTicker():query():create() + }, xs = 'ignore', sm = 'ignore', lg = 4, @@ -174,19 +190,14 @@ function StreamPage:create() end ---@private ----@return string|Widget|Html|(string|Widget|Html)[]? +---@return MatchTicker function StreamPage:_createMatchTicker() - local ticker = MatchTicker{ - additionalConditions = 'AND (' .. self:_createMatchQueryCondition() .. ')', + return MatchTicker{ + additionalConditions = 'AND (' .. tostring(self:_createMatchQueryCondition()) .. ')', limit = 5, newStyle = true, ongoing = true, upcoming = true, - wrapperClasses = {'new-match-style'}, - } - return MatchPageAdditionalSection{ - header = 'Channel Schedule', - children = ticker:query():create() } end From 8b714f1249335bfd2481f181793f2e270c81c1ab Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Thu, 19 Feb 2026 14:52:35 +0900 Subject: [PATCH 04/35] move --- lua/wikis/commons/{StreamPage.lua => StreamPage/Base.lua} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename lua/wikis/commons/{StreamPage.lua => StreamPage/Base.lua} (99%) diff --git a/lua/wikis/commons/StreamPage.lua b/lua/wikis/commons/StreamPage/Base.lua similarity index 99% rename from lua/wikis/commons/StreamPage.lua rename to lua/wikis/commons/StreamPage/Base.lua index 2596f4985cf..871b4e634d2 100644 --- a/lua/wikis/commons/StreamPage.lua +++ b/lua/wikis/commons/StreamPage/Base.lua @@ -1,6 +1,6 @@ --- -- @Liquipedia --- page=Module:StreamPage +-- page=Module:StreamPage/Base -- -- Please see https://github.com/Liquipedia/Lua-Modules to contribute -- @@ -18,7 +18,7 @@ local Logic = Lua.import('Module:Logic') local Lpdb = Lua.import('Module:Lpdb') local MatchGroupUtil = Lua.import('Module:MatchGroup/Util/Custom') local MatchPage = Lua.requireIfExists('Module:MatchPage') -local MatchTable = Lua.import('Module:MatchTable/Custom') +local MatchTable = Lua.import('Module:MatchTable') local MatchTicker = Lua.import('Module:MatchTicker') local Opponent = Lua.import('Module:Opponent/Custom') local OpponentDisplay = Lua.import('Module:OpponentDisplay/Custom') From 9a75277d53860268dab4a470c2f8bff18ca4989c Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Thu, 19 Feb 2026 14:55:20 +0900 Subject: [PATCH 05/35] css --- lua/wikis/commons/StreamPage/Base.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/wikis/commons/StreamPage/Base.lua b/lua/wikis/commons/StreamPage/Base.lua index 871b4e634d2..c519e963135 100644 --- a/lua/wikis/commons/StreamPage/Base.lua +++ b/lua/wikis/commons/StreamPage/Base.lua @@ -175,7 +175,7 @@ function StreamPage:create() GridWidgets.Cell{ cellContent = MatchPageAdditionalSection{ header = 'Channel Schedule', - children = self:_createMatchTicker():query():create() + children = self:_createMatchTicker():query():create():css('width', '100%') }, xs = 'ignore', sm = 'ignore', From d96273b71ffaa9825eb74728b9946155b8e55afe Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Thu, 19 Feb 2026 14:56:54 +0900 Subject: [PATCH 06/35] type annotation --- lua/wikis/commons/StreamPage/Base.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lua/wikis/commons/StreamPage/Base.lua b/lua/wikis/commons/StreamPage/Base.lua index c519e963135..2c7d58afde5 100644 --- a/lua/wikis/commons/StreamPage/Base.lua +++ b/lua/wikis/commons/StreamPage/Base.lua @@ -57,7 +57,8 @@ local StreamPage = Class.new(function (self, args) self:_fetchMatches() end) - +---@param frame Frame +---@return Widget? function StreamPage.run(frame) local args = Arguments.getArgs(frame) return StreamPage(args):create() From 8b75999549cb4b4ee68bc001810fb5ceb4378e7e Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Thu, 19 Feb 2026 15:06:35 +0900 Subject: [PATCH 07/35] add team version --- lua/wikis/commons/StreamPage/Team.lua | 98 +++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 lua/wikis/commons/StreamPage/Team.lua diff --git a/lua/wikis/commons/StreamPage/Team.lua b/lua/wikis/commons/StreamPage/Team.lua new file mode 100644 index 00000000000..7cd63033106 --- /dev/null +++ b/lua/wikis/commons/StreamPage/Team.lua @@ -0,0 +1,98 @@ +--- +-- @Liquipedia +-- page=Module:StreamPage/Team +-- +-- Please see https://github.com/Liquipedia/Lua-Modules to contribute +-- + +local Lua = require('Module:Lua') + +local Arguments = Lua.import('Module:Arguments') +local Array = Lua.import('Module:Array') +local BaseStreamPage = Lua.import('Module:StreamPage/Base') +local Class = Lua.import('Module:Class') +local Image = Lua.import('Module:Image') +local OpponentDisplay = Lua.import('Module:OpponentDisplay/Custom') +local Page = Lua.import('Module:Page') +local PlayerDisplay = Lua.import('Module:Player/Display/Custom') +local String = Lua.import('Module:StringUtils') + +local HtmlWidgets = Lua.import('Module:Widget/Html/All') +local WidgetUtil = Lua.import('Module:Widget/Util') + +---@class TeamStreamPage: BaseStreamPage +---@operator call(table): TeamStreamPage +local TeamStreamPage = Class.new(BaseStreamPage) + +---@param frame Frame +---@return Widget? +function TeamStreamPage.run(frame) + local args = Arguments.getArgs(frame) + return TeamStreamPage(args):create() +end + +---@return Widget|Widget[]? +function TeamStreamPage:render() + return { + HtmlWidgets.H3{children = 'Player Information'}, + self:renderPlayerInformation() + } +end + +---@return Html +function TeamStreamPage:renderPlayerInformation() + return HtmlWidgets.Div{ + classes = {'match-bm-players-wrapper'}, + children = Array.map(self.matches[1].opponents, TeamStreamPage._teamDisplay) + } +end + +---@private +---@param opponent standardOpponent +---@return Widget +function TeamStreamPage._teamDisplay(opponent) + return HtmlWidgets.Div{ + classes = {'match-bm-players-team'}, + children = WidgetUtil.collect( + HtmlWidgets.Div{ + classes = {'match-bm-players-team-header'}, + children = OpponentDisplay.InlineOpponent{opponent = opponent, teamStyle = 'icon'} + }, + Array.map(opponent.players, TeamStreamPage._playerDisplay) + ) + } +end + +---@param player standardPlayer +---@return Widget +function TeamStreamPage._playerDisplay(player) + local lpdbData = mw.ext.LiquipediaDB.lpdb('player', { + conditions = '[[pagename::' .. (Page.pageifyLink(player.pageName) or '') .. ']]', + limit = 1 + })[1] + + local playerData = {} + local image + if lpdbData then + playerData = lpdbData + image = playerData.image + if String.isEmpty(image) then + image = (playerData.extradata or {}).image + end + end + if String.isEmpty(image) then + image = 'Blank Player Image.png' + end + local imageDisplay = Image.display(image, nil, {class = 'img-fluid', size = '600px'}) + + local nameDisplay = PlayerDisplay.InlinePlayer{ + player = player + } + + return HtmlWidgets.Div{ + classes = {'match-bm-players-player', 'match-bm-players-player--col-2'}, + children = {imageDisplay, nameDisplay} + } +end + +return TeamStreamPage From 55cd42c6db555209eb2d5304ebfd8a9688a73946 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Thu, 19 Feb 2026 15:06:51 +0900 Subject: [PATCH 08/35] remove unused imports --- lua/wikis/commons/StreamPage/Base.lua | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lua/wikis/commons/StreamPage/Base.lua b/lua/wikis/commons/StreamPage/Base.lua index 2c7d58afde5..6b75d6fd004 100644 --- a/lua/wikis/commons/StreamPage/Base.lua +++ b/lua/wikis/commons/StreamPage/Base.lua @@ -13,7 +13,6 @@ local Class = Lua.import('Module:Class') local Countdown = Lua.import('Module:Countdown') local DateExt = Lua.import('Module:Date/Ext') local HighlightConditions = Lua.import('Module:HighlightConditions') -local Image = Lua.import('Module:Image') local Logic = Lua.import('Module:Logic') local Lpdb = Lua.import('Module:Lpdb') local MatchGroupUtil = Lua.import('Module:MatchGroup/Util/Custom') @@ -22,9 +21,6 @@ local MatchTable = Lua.import('Module:MatchTable') local MatchTicker = Lua.import('Module:MatchTicker') local Opponent = Lua.import('Module:Opponent/Custom') local OpponentDisplay = Lua.import('Module:OpponentDisplay/Custom') -local Page = Lua.import('Module:Page') -local PlayerDisplay = Lua.import('Module:Player/Display/Custom') -local String = Lua.import('Module:StringUtils') local Table = Lua.import('Module:Table') local TeamTemplate = Lua.import('Module:TeamTemplate') local Tournament = Lua.import('Module:Tournament') @@ -35,7 +31,6 @@ local ConditionNode = Condition.Node local Comparator = Condition.Comparator local BooleanOperator = Condition.BooleanOperator local ColumnName = Condition.ColumnName -local ConditionUtil = Condition.Util local GridWidgets = Lua.import('Module:Widget/Grid') local HtmlWidgets = Lua.import('Module:Widget/Html/All') From aa7f61eb856622884a71c105a548e1e215408fd3 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Thu, 19 Feb 2026 15:07:23 +0900 Subject: [PATCH 09/35] annotations --- lua/wikis/commons/StreamPage/Team.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lua/wikis/commons/StreamPage/Team.lua b/lua/wikis/commons/StreamPage/Team.lua index 7cd63033106..342062ed7d2 100644 --- a/lua/wikis/commons/StreamPage/Team.lua +++ b/lua/wikis/commons/StreamPage/Team.lua @@ -39,7 +39,8 @@ function TeamStreamPage:render() } end ----@return Html +---@protected +---@return Widget function TeamStreamPage:renderPlayerInformation() return HtmlWidgets.Div{ classes = {'match-bm-players-wrapper'}, From 7cd1c26241f01d5799f3cac55eecc4bf57ae107e Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Thu, 19 Feb 2026 15:41:35 +0900 Subject: [PATCH 10/35] basic starcraft impl --- lua/wikis/commons/StreamPage/Starcraft.lua | 139 +++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 lua/wikis/commons/StreamPage/Starcraft.lua diff --git a/lua/wikis/commons/StreamPage/Starcraft.lua b/lua/wikis/commons/StreamPage/Starcraft.lua new file mode 100644 index 00000000000..e446312a6c6 --- /dev/null +++ b/lua/wikis/commons/StreamPage/Starcraft.lua @@ -0,0 +1,139 @@ +--- +-- @Liquipedia +-- page=Module:StreamPage/Starcraft +-- +-- Please see https://github.com/Liquipedia/Lua-Modules to contribute +-- + +local Lua = require('Module:Lua') + +local Arguments = Lua.import('Module:Arguments') +local Array = Lua.import('Module:Array') +local BaseStreamPage = Lua.import('Module:StreamPage/Base') +local Class = Lua.import('Module:Class') +local Currency = Lua.import('Module:Currency') +local DateExt = Lua.import('Module:Date/Ext') +local Image = Lua.import('Module:Image') +local Links = Lua.import('Module:Links') +local OpponentDisplay = Lua.import('Module:OpponentDisplay/Custom') +local Page = Lua.import('Module:Page') +local PlayerDisplay = Lua.import('Module:Player/Display/Custom') +local String = Lua.import('Module:StringUtils') +local Table = Lua.import('Module:Table') + +local HtmlWidgets = Lua.import('Module:Widget/Html/All') +local Link = Lua.import('Module:Widget/Basic/Link') +local WidgetUtil = Lua.import('Module:Widget/Util') + +---@class StarcraftStreamPage: BaseStreamPage +---@operator call(table): StarcraftStreamPage +local StarcraftStreamPage = Class.new(BaseStreamPage) + +---@param frame Frame +---@return Widget? +function StarcraftStreamPage.run(frame) + local args = Arguments.getArgs(frame) + return StarcraftStreamPage(args):create() +end + +---@return Widget|Widget[]? +function StarcraftStreamPage:render() + return { + HtmlWidgets.H3{children = 'Player Information'}, + self:renderPlayerInformation() + } +end + +---@protected +---@return Widget +function StarcraftStreamPage:renderPlayerInformation() + return HtmlWidgets.Div{ + classes = {'match-bm-players-wrapper'}, + css = {width = '100%'}, + children = Array.map(self.matches[1].opponents, StarcraftStreamPage._teamDisplay) + } +end + +---@private +---@param opponent standardOpponent +---@return Widget +function StarcraftStreamPage._teamDisplay(opponent) + return HtmlWidgets.Div{ + classes = {'match-bm-players-team'}, + children = WidgetUtil.collect( + HtmlWidgets.Div{ + classes = {'match-bm-players-team-header'}, + children = OpponentDisplay.InlineOpponent{opponent = opponent, teamStyle = 'icon'} + }, + Array.map(opponent.players, StarcraftStreamPage._playerDisplay) + ) + } +end + +---@param player standardPlayer +---@return Widget +function StarcraftStreamPage._playerDisplay(player) + local lpdbData = mw.ext.LiquipediaDB.lpdb('player', { + conditions = '[[pagename::' .. (Page.pageifyLink(player.pageName) or '') .. ']]', + limit = 1 + })[1] + + local playerData = {} + local image + if lpdbData then + playerData = lpdbData + image = playerData.image + if String.isEmpty(image) then + image = (playerData.extradata or {}).image + end + end + if String.isEmpty(image) then + image = 'Blank Player Image.png' + end + local imageDisplay = Image.display(image, nil, {class = 'img-fluid', size = '600px'}) + + local nameDisplay = PlayerDisplay.InlinePlayer{ + player = player + } + + return HtmlWidgets.Div{ + classes = {'match-bm-players-player', 'match-bm-players-player--col-2'}, + children = { + imageDisplay, + HtmlWidgets.Div{ + css = { + display = 'flex', + ['flex-direction'] = 'column', + }, + children = WidgetUtil.collect( + nameDisplay, + lpdbData.name and HtmlWidgets.Span{children = { + HtmlWidgets.B{children = 'Name: '}, + lpdbData.name + }} or nil, + lpdbData.birthdate ~= DateExt.defaultDate and HtmlWidgets.Span{children = { + HtmlWidgets.B{children = 'Birth: '}, + mw.getContentLanguage():formatDate('F j, Y', lpdbData.birthdate), + ' (' .. DateExt.calculateAge(DateExt.getCurrentTimestamp(), lpdbData.birthdate) .. ')' + }} or nil, + (tonumber(lpdbData.earnings) or 0) > 0 and HtmlWidgets.Span{children = { + HtmlWidgets.B{children = 'Earnings: '}, + Currency.display('usd', lpdbData.earnings, {formatValue = true}) + }} or nil, + HtmlWidgets.Span{children = Array.interleave( + Array.extractValues(Table.map(lpdbData.links or {}, function(key, link) + return key, Link{ + link = link, + children = Links.makeIcon(Links.removeAppendedNumber(key), 21), + linktype = 'external' + } + end), Table.iter.spairs), + ' ' + )} + ) + } + } + } +end + +return StarcraftStreamPage From c657f40986d32be9728913ec669287f1402eb160 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Thu, 19 Feb 2026 16:02:34 +0900 Subject: [PATCH 11/35] copy from live --- lua/wikis/commons/StreamPage/Starcraft.lua | 128 ++++++++++++++++++++- 1 file changed, 127 insertions(+), 1 deletion(-) diff --git a/lua/wikis/commons/StreamPage/Starcraft.lua b/lua/wikis/commons/StreamPage/Starcraft.lua index e446312a6c6..2702ab0f099 100644 --- a/lua/wikis/commons/StreamPage/Starcraft.lua +++ b/lua/wikis/commons/StreamPage/Starcraft.lua @@ -7,14 +7,19 @@ local Lua = require('Module:Lua') +local Abbreviation = Lua.import('Module:Abbreviation') local Arguments = Lua.import('Module:Arguments') local Array = Lua.import('Module:Array') local BaseStreamPage = Lua.import('Module:StreamPage/Base') local Class = Lua.import('Module:Class') local Currency = Lua.import('Module:Currency') local DateExt = Lua.import('Module:Date/Ext') +local Faction = Lua.import('Module:Faction') local Image = Lua.import('Module:Image') +local Json = Lua.import('Module:Json') local Links = Lua.import('Module:Links') +local Logic = Lua.import('Module:Logic') +local Opponent = Lua.import('Module:Opponent/Custom') local OpponentDisplay = Lua.import('Module:OpponentDisplay/Custom') local Page = Lua.import('Module:Page') local PlayerDisplay = Lua.import('Module:Player/Display/Custom') @@ -25,6 +30,9 @@ local HtmlWidgets = Lua.import('Module:Widget/Html/All') local Link = Lua.import('Module:Widget/Basic/Link') local WidgetUtil = Lua.import('Module:Widget/Util') +local RANDOM_RACE = 'r' +local TBD = Abbreviation.make{title = 'To be determined (or to be decided)', text = 'TBD'} + ---@class StarcraftStreamPage: BaseStreamPage ---@operator call(table): StarcraftStreamPage local StarcraftStreamPage = Class.new(BaseStreamPage) @@ -40,7 +48,8 @@ end function StarcraftStreamPage:render() return { HtmlWidgets.H3{children = 'Player Information'}, - self:renderPlayerInformation() + self:renderPlayerInformation(), + self:_mapPool() } end @@ -136,4 +145,121 @@ function StarcraftStreamPage._playerDisplay(player) } end +function StarcraftStreamPage:_mapPool() + local match = self.matches[1] + + local maps = Logic.emptyOr( + Json.parse(mw.ext.LiquipediaDB.lpdb('tournament', { + conditions = '[[pagename::' .. match.parent .. ']]', + query = 'maps', + limit = 1, + })[1].maps), + {'TBA'} + ) --[[ @as any[] ]] + + local race1 = ((match.opponents[1].players)[1] or {}).faction + local race2 = ((match.opponents[2].players)[1] or {}).faction + + local skipMapWinRate = match.opponents[1].type ~= Opponent.solo + or not race1 + or not race2 + or match.opponents[2].type ~= Opponent.solo + or race1 == Faction.defaultFaction + or race1 == RANDOM_RACE + or race2 == Faction.defaultFaction + or race1 ~= race2 + + local mapTable = mw.html.create('table') + :addClass('wikitable') + :css('text-align', 'center') + :css('margin', '0 0 10px 0') + :css('width', '100%') + mapTable:tag('tr') + :addClass('wiki-color-dark wiki-backgroundcolor-light') + :css('font-size', '130%') + :css('padding', '5px 10px') + :tag('th') + :attr('colspan', '2') + :css('padding', '5px') + :wikitext('Map Pool') + + if not skipMapWinRate then + ---@cast race1 -nil + ---@cast race2 -nil + mapTable:tag('tr') + :tag('th'):wikitext('Map') + :tag('th'):wikitext(string.upper(race1) .. 'v' .. string.upper(race2)) + end + + local currentMap = self:_getCurrentMap() + local matchup = skipMapWinRate and '' or race1 .. race2 + + for _, map in ipairs(maps) do + local mapRow = mapTable:tag('tr') + :addClass('stats-row') + + if map == 'TBA' then + mapRow:tag('td') + :attr('colspan', '2') + :node(mw.html.create('span') + :css('text-align', 'center') + :css('font-style', 'italic') + :wikitext('To be announced') + ) + else + if map.link == currentMap then + mapRow:addClass('tournament-highlighted-bg') + end + mapRow:tag('td') + :wikitext('[[' .. map.link .. '|' .. map.displayname .. ']]') + if not skipMapWinRate then + local winRate = StarcraftStreamPage._queryMapWinrate(map.link, matchup) + if String.isNotEmpty(winRate) then + mapRow:tag('td') + :wikitext(winRate) + end + end + end + end + + return mw.html.create('div') + :addClass('sc2-stream-page-middle-column1') + :node(mapTable) +end + +function StarcraftStreamPage._getMaps(mapsInput) + if String.isEmpty(mapsInput) then + return {'TBA'} + end + + return Json.parse(mapsInput) +end + +function StarcraftStreamPage._queryMapWinrate(map, matchup) + local conditions = '[[pagename::' .. string.gsub(map, ' ', '_') .. ']] AND [[type::map_winrates]]' + local LPDBoutput = mw.ext.LiquipediaDB.lpdb('datapoint', { + conditions = conditions, + query = 'extradata', + }) + + if type(LPDBoutput[1]) == 'table' then + if LPDBoutput[1]['extradata'][matchup] == '-' then + return TBD + else + return math.floor(LPDBoutput[1]['extradata'][matchup]*100 + 0.5) .. '%' + end + else + return TBD + end +end + +function StarcraftStreamPage:_getCurrentMap() + local games = self.matches[1].games + for _, game in ipairs(games) do + if Logic.isEmpty(game.winner) then + return game.map + end + end +end + return StarcraftStreamPage From 5353742c8823700881510b989e8bd1a83b1ac0b9 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Thu, 19 Feb 2026 16:04:53 +0900 Subject: [PATCH 12/35] type annotations --- lua/wikis/commons/StreamPage/Starcraft.lua | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/lua/wikis/commons/StreamPage/Starcraft.lua b/lua/wikis/commons/StreamPage/Starcraft.lua index 2702ab0f099..a939c838e02 100644 --- a/lua/wikis/commons/StreamPage/Starcraft.lua +++ b/lua/wikis/commons/StreamPage/Starcraft.lua @@ -145,6 +145,8 @@ function StarcraftStreamPage._playerDisplay(player) } end +---@private +---@return Html function StarcraftStreamPage:_mapPool() local match = self.matches[1] @@ -227,14 +229,9 @@ function StarcraftStreamPage:_mapPool() :node(mapTable) end -function StarcraftStreamPage._getMaps(mapsInput) - if String.isEmpty(mapsInput) then - return {'TBA'} - end - - return Json.parse(mapsInput) -end - +---@param map string +---@param matchup string +---@return string? function StarcraftStreamPage._queryMapWinrate(map, matchup) local conditions = '[[pagename::' .. string.gsub(map, ' ', '_') .. ']] AND [[type::map_winrates]]' local LPDBoutput = mw.ext.LiquipediaDB.lpdb('datapoint', { @@ -253,6 +250,8 @@ function StarcraftStreamPage._queryMapWinrate(map, matchup) end end +---@private +---@return string? function StarcraftStreamPage:_getCurrentMap() local games = self.matches[1].games for _, game in ipairs(games) do From 14011b03b223c0a6bc9374aacda15e36bb6aa360 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Thu, 19 Feb 2026 16:29:48 +0900 Subject: [PATCH 13/35] use widget --- lua/wikis/commons/StreamPage/Starcraft.lua | 114 +++++++++++---------- 1 file changed, 61 insertions(+), 53 deletions(-) diff --git a/lua/wikis/commons/StreamPage/Starcraft.lua b/lua/wikis/commons/StreamPage/Starcraft.lua index a939c838e02..bd51ab20419 100644 --- a/lua/wikis/commons/StreamPage/Starcraft.lua +++ b/lua/wikis/commons/StreamPage/Starcraft.lua @@ -26,6 +26,7 @@ local PlayerDisplay = Lua.import('Module:Player/Display/Custom') local String = Lua.import('Module:StringUtils') local Table = Lua.import('Module:Table') +local DataTable = Lua.import('Module:Widget/Basic/DataTable') local HtmlWidgets = Lua.import('Module:Widget/Html/All') local Link = Lua.import('Module:Widget/Basic/Link') local WidgetUtil = Lua.import('Module:Widget/Util') @@ -159,74 +160,81 @@ function StarcraftStreamPage:_mapPool() {'TBA'} ) --[[ @as any[] ]] - local race1 = ((match.opponents[1].players)[1] or {}).faction - local race2 = ((match.opponents[2].players)[1] or {}).faction + local race1 = ((match.opponents[1].players)[1] or {}).faction --[[ @as string ]] + local race2 = ((match.opponents[2].players)[1] or {}).faction --[[ @as string ]] - local skipMapWinRate = match.opponents[1].type ~= Opponent.solo + local skipMapWinRate = not Array.all(match.opponents, function (opponent) return opponent.type == Opponent.solo end) or not race1 or not race2 - or match.opponents[2].type ~= Opponent.solo or race1 == Faction.defaultFaction or race1 == RANDOM_RACE or race2 == Faction.defaultFaction or race1 ~= race2 - local mapTable = mw.html.create('table') - :addClass('wikitable') - :css('text-align', 'center') - :css('margin', '0 0 10px 0') - :css('width', '100%') - mapTable:tag('tr') - :addClass('wiki-color-dark wiki-backgroundcolor-light') - :css('font-size', '130%') - :css('padding', '5px 10px') - :tag('th') - :attr('colspan', '2') - :css('padding', '5px') - :wikitext('Map Pool') - - if not skipMapWinRate then - ---@cast race1 -nil - ---@cast race2 -nil - mapTable:tag('tr') - :tag('th'):wikitext('Map') - :tag('th'):wikitext(string.upper(race1) .. 'v' .. string.upper(race2)) - end - local currentMap = self:_getCurrentMap() local matchup = skipMapWinRate and '' or race1 .. race2 - for _, map in ipairs(maps) do - local mapRow = mapTable:tag('tr') - :addClass('stats-row') - + ---@param map 'TBA'|{link: string, displayname: string} + ---@return Widget + local function createMapRow(map) if map == 'TBA' then - mapRow:tag('td') - :attr('colspan', '2') - :node(mw.html.create('span') - :css('text-align', 'center') - :css('font-style', 'italic') - :wikitext('To be announced') - ) - else - if map.link == currentMap then - mapRow:addClass('tournament-highlighted-bg') - end - mapRow:tag('td') - :wikitext('[[' .. map.link .. '|' .. map.displayname .. ']]') - if not skipMapWinRate then - local winRate = StarcraftStreamPage._queryMapWinrate(map.link, matchup) - if String.isNotEmpty(winRate) then - mapRow:tag('td') - :wikitext(winRate) - end - end + return HtmlWidgets.Tr{ + classes = {'stats-row'}, + children = HtmlWidgets.Td{ + attributes = {colspan = 2}, + children = HtmlWidgets.Span{ + css = { + ['text-align'] = 'center', + ['font-style'] = 'italic', + }, + children = 'To be announced' + } + } + } end + return HtmlWidgets.Tr{ + classes = { + 'stats-row', + map.link == currentMap and 'tournament-highlighted-bg' or nil + }, + children = WidgetUtil.collect( + HtmlWidgets.Td{children = Link{link = map.link, children = map.displayname}}, + not skipMapWinRate and HtmlWidgets.Td{ + children = StarcraftStreamPage._queryMapWinrate(map.link, matchup) + } or nil + ), + } end - return mw.html.create('div') - :addClass('sc2-stream-page-middle-column1') - :node(mapTable) + return DataTable{ + tableCss = { + ['text-align'] = 'center', + margin = '0 0 10px 0', + }, + children = WidgetUtil.collect( + HtmlWidgets.Tr{ + classes = {'wiki-color-dark', 'wiki-backgroundcolor-light'}, + css = { + ['font-size'] = '130%', + padding = '5px 10px', + }, + children = {HtmlWidgets.Th{ + attributes = {colspan = 2}, + css = {padding = '5px'}, + children = 'Map Pool' + }} + }, + not skipMapWinRate and HtmlWidgets.Tr{children = { + HtmlWidgets.Th{children = 'Map'}, + HtmlWidgets.Th{children = { + race1:upper(), + 'v', + race2:upper() + }} + }} or nil, + Array.map(maps, createMapRow) + ) + } end ---@param map string From f8ce06fa326f454f80f788f8df83def74f6e3844 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Thu, 19 Feb 2026 16:32:33 +0900 Subject: [PATCH 14/35] backport css change to team version --- lua/wikis/commons/StreamPage/Team.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/lua/wikis/commons/StreamPage/Team.lua b/lua/wikis/commons/StreamPage/Team.lua index 342062ed7d2..c8f93937680 100644 --- a/lua/wikis/commons/StreamPage/Team.lua +++ b/lua/wikis/commons/StreamPage/Team.lua @@ -44,6 +44,7 @@ end function TeamStreamPage:renderPlayerInformation() return HtmlWidgets.Div{ classes = {'match-bm-players-wrapper'}, + css = {width = '100%'}, children = Array.map(self.matches[1].opponents, TeamStreamPage._teamDisplay) } end From 52f6256e4cda72a81eac3206403cccdb663f095b Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Thu, 19 Feb 2026 16:33:34 +0900 Subject: [PATCH 15/35] add custom to sc(2) --- lua/wikis/starcraft/StreamPage/Custom.lua | 10 ++++++++++ lua/wikis/starcraft2/StreamPage/Custom.lua | 10 ++++++++++ 2 files changed, 20 insertions(+) create mode 100644 lua/wikis/starcraft/StreamPage/Custom.lua create mode 100644 lua/wikis/starcraft2/StreamPage/Custom.lua diff --git a/lua/wikis/starcraft/StreamPage/Custom.lua b/lua/wikis/starcraft/StreamPage/Custom.lua new file mode 100644 index 00000000000..b9c13b053c0 --- /dev/null +++ b/lua/wikis/starcraft/StreamPage/Custom.lua @@ -0,0 +1,10 @@ +--- +-- @Liquipedia +-- page=Module:StreamPage/Custom +-- +-- Please see https://github.com/Liquipedia/Lua-Modules to contribute +-- + +local Lua = require('Module:Lua') + +return Lua.import('Module:StreamPage/Starcraft') diff --git a/lua/wikis/starcraft2/StreamPage/Custom.lua b/lua/wikis/starcraft2/StreamPage/Custom.lua new file mode 100644 index 00000000000..b9c13b053c0 --- /dev/null +++ b/lua/wikis/starcraft2/StreamPage/Custom.lua @@ -0,0 +1,10 @@ +--- +-- @Liquipedia +-- page=Module:StreamPage/Custom +-- +-- Please see https://github.com/Liquipedia/Lua-Modules to contribute +-- + +local Lua = require('Module:Lua') + +return Lua.import('Module:StreamPage/Starcraft') From 6544e967a4eb1d7fdc855579b1c2bd7e11900e7c Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Thu, 19 Feb 2026 16:34:32 +0900 Subject: [PATCH 16/35] add custom to LoL --- lua/wikis/leagueoflegends/StreamPage/Custom.lua | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 lua/wikis/leagueoflegends/StreamPage/Custom.lua diff --git a/lua/wikis/leagueoflegends/StreamPage/Custom.lua b/lua/wikis/leagueoflegends/StreamPage/Custom.lua new file mode 100644 index 00000000000..5afca71e278 --- /dev/null +++ b/lua/wikis/leagueoflegends/StreamPage/Custom.lua @@ -0,0 +1,10 @@ +--- +-- @Liquipedia +-- page=Module:StreamPage/Custom +-- +-- Please see https://github.com/Liquipedia/Lua-Modules to contribute +-- + +local Lua = require('Module:Lua') + +return Lua.import('Module:StreamPage/Team') From 924c4fe10d63bb55ded94bdf39a13adbcc832712 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Thu, 19 Feb 2026 16:39:44 +0900 Subject: [PATCH 17/35] lint --- lua/wikis/commons/StreamPage/Starcraft.lua | 15 ++++----------- lua/wikis/commons/StreamPage/Team.lua | 14 ++++---------- 2 files changed, 8 insertions(+), 21 deletions(-) diff --git a/lua/wikis/commons/StreamPage/Starcraft.lua b/lua/wikis/commons/StreamPage/Starcraft.lua index bd51ab20419..8944eb53bb7 100644 --- a/lua/wikis/commons/StreamPage/Starcraft.lua +++ b/lua/wikis/commons/StreamPage/Starcraft.lua @@ -86,20 +86,13 @@ function StarcraftStreamPage._playerDisplay(player) local lpdbData = mw.ext.LiquipediaDB.lpdb('player', { conditions = '[[pagename::' .. (Page.pageifyLink(player.pageName) or '') .. ']]', limit = 1 - })[1] + })[1] or {} - local playerData = {} - local image - if lpdbData then - playerData = lpdbData - image = playerData.image - if String.isEmpty(image) then - image = (playerData.extradata or {}).image - end - end + local image = lpdbData.image if String.isEmpty(image) then - image = 'Blank Player Image.png' + image = Logic.emptyOr((lpdbData.extradata or {}).image, 'Blank Player Image.png') --[[@as string]] end + local imageDisplay = Image.display(image, nil, {class = 'img-fluid', size = '600px'}) local nameDisplay = PlayerDisplay.InlinePlayer{ diff --git a/lua/wikis/commons/StreamPage/Team.lua b/lua/wikis/commons/StreamPage/Team.lua index c8f93937680..1e75ed0cf24 100644 --- a/lua/wikis/commons/StreamPage/Team.lua +++ b/lua/wikis/commons/StreamPage/Team.lua @@ -12,6 +12,7 @@ local Array = Lua.import('Module:Array') local BaseStreamPage = Lua.import('Module:StreamPage/Base') local Class = Lua.import('Module:Class') local Image = Lua.import('Module:Image') +local Logic = Lua.import('Module:Logic') local OpponentDisplay = Lua.import('Module:OpponentDisplay/Custom') local Page = Lua.import('Module:Page') local PlayerDisplay = Lua.import('Module:Player/Display/Custom') @@ -73,18 +74,11 @@ function TeamStreamPage._playerDisplay(player) limit = 1 })[1] - local playerData = {} - local image - if lpdbData then - playerData = lpdbData - image = playerData.image - if String.isEmpty(image) then - image = (playerData.extradata or {}).image - end - end + local image = lpdbData.image if String.isEmpty(image) then - image = 'Blank Player Image.png' + image = Logic.emptyOr((lpdbData.extradata or {}).image, 'Blank Player Image.png') --[[@as string]] end + local imageDisplay = Image.display(image, nil, {class = 'img-fluid', size = '600px'}) local nameDisplay = PlayerDisplay.InlinePlayer{ From a31fab09a3663b4485f8ba7f6ccca9e6f8edd19e Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Thu, 19 Feb 2026 18:29:54 +0900 Subject: [PATCH 18/35] feedback from review Co-authored-by: hjpalpha <75081997+hjpalpha@users.noreply.github.com> --- lua/wikis/commons/StreamPage/Starcraft.lua | 27 +++++++++++++--------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/lua/wikis/commons/StreamPage/Starcraft.lua b/lua/wikis/commons/StreamPage/Starcraft.lua index 8944eb53bb7..7e82e6f2d14 100644 --- a/lua/wikis/commons/StreamPage/Starcraft.lua +++ b/lua/wikis/commons/StreamPage/Starcraft.lua @@ -15,6 +15,7 @@ local Class = Lua.import('Module:Class') local Currency = Lua.import('Module:Currency') local DateExt = Lua.import('Module:Date/Ext') local Faction = Lua.import('Module:Faction') +local FnUtil = Lua.import('Module:FnUtil') local Image = Lua.import('Module:Image') local Json = Lua.import('Module:Json') local Links = Lua.import('Module:Links') @@ -60,14 +61,14 @@ function StarcraftStreamPage:renderPlayerInformation() return HtmlWidgets.Div{ classes = {'match-bm-players-wrapper'}, css = {width = '100%'}, - children = Array.map(self.matches[1].opponents, StarcraftStreamPage._teamDisplay) + children = Array.map(self.matches[1].opponents, StarcraftStreamPage._opponentDisplay) } end ---@private ---@param opponent standardOpponent ---@return Widget -function StarcraftStreamPage._teamDisplay(opponent) +function StarcraftStreamPage._opponentDisplay(opponent) return HtmlWidgets.Div{ classes = {'match-bm-players-team'}, children = WidgetUtil.collect( @@ -75,24 +76,21 @@ function StarcraftStreamPage._teamDisplay(opponent) classes = {'match-bm-players-team-header'}, children = OpponentDisplay.InlineOpponent{opponent = opponent, teamStyle = 'icon'} }, - Array.map(opponent.players, StarcraftStreamPage._playerDisplay) + Array.map(opponent.players, FnUtil.curry(StarcraftStreamPage._playerDisplay, opponent.type)) ) } end +---@param opponentType OpponentType ---@param player standardPlayer ---@return Widget -function StarcraftStreamPage._playerDisplay(player) +function StarcraftStreamPage._playerDisplay(opponentType, player) local lpdbData = mw.ext.LiquipediaDB.lpdb('player', { conditions = '[[pagename::' .. (Page.pageifyLink(player.pageName) or '') .. ']]', limit = 1 - })[1] or {} - - local image = lpdbData.image - if String.isEmpty(image) then - image = Logic.emptyOr((lpdbData.extradata or {}).image, 'Blank Player Image.png') --[[@as string]] - end + })[1] + local image = Logic.nilIfEmpty(lpdbData.image) or 'Blank Player Image.png' local imageDisplay = Image.display(image, nil, {class = 'img-fluid', size = '600px'}) local nameDisplay = PlayerDisplay.InlinePlayer{ @@ -114,7 +112,14 @@ function StarcraftStreamPage._playerDisplay(player) HtmlWidgets.B{children = 'Name: '}, lpdbData.name }} or nil, - lpdbData.birthdate ~= DateExt.defaultDate and HtmlWidgets.Span{children = { + Opponent.typeIsParty(opponentType) and Logic.isNotEmpty(lpdbData.team) and HtmlWidgets.Span{children = { + HtmlWidgets.B{children = 'Team: '}, + OpponentDisplay.InlineTeamContainer{ + template = lpdbData.team, + style = 'standard' + } + }} or nil, + not DateExt.isDefaultTimestamp(lpdbData.birthdate) and HtmlWidgets.Span{children = { HtmlWidgets.B{children = 'Birth: '}, mw.getContentLanguage():formatDate('F j, Y', lpdbData.birthdate), ' (' .. DateExt.calculateAge(DateExt.getCurrentTimestamp(), lpdbData.birthdate) .. ')' From e33e985e190247184ae15fc30aa2f090621f81a9 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Thu, 19 Feb 2026 18:30:10 +0900 Subject: [PATCH 19/35] remove unused import --- lua/wikis/commons/StreamPage/Starcraft.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/lua/wikis/commons/StreamPage/Starcraft.lua b/lua/wikis/commons/StreamPage/Starcraft.lua index 7e82e6f2d14..8879d1a3fa4 100644 --- a/lua/wikis/commons/StreamPage/Starcraft.lua +++ b/lua/wikis/commons/StreamPage/Starcraft.lua @@ -24,7 +24,6 @@ local Opponent = Lua.import('Module:Opponent/Custom') local OpponentDisplay = Lua.import('Module:OpponentDisplay/Custom') local Page = Lua.import('Module:Page') local PlayerDisplay = Lua.import('Module:Player/Display/Custom') -local String = Lua.import('Module:StringUtils') local Table = Lua.import('Module:Table') local DataTable = Lua.import('Module:Widget/Basic/DataTable') From df87f4cf4979c313e58f8ac6223802c5fd1c37ee Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Thu, 19 Feb 2026 18:48:41 +0900 Subject: [PATCH 20/35] add gap --- stylesheets/commons/BigMatch.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/stylesheets/commons/BigMatch.scss b/stylesheets/commons/BigMatch.scss index c4d36717bfb..4bb9bb1cc08 100644 --- a/stylesheets/commons/BigMatch.scss +++ b/stylesheets/commons/BigMatch.scss @@ -39,6 +39,7 @@ span.slash { flex-direction: row; align-items: center; padding: 16px 0; + gap: 0.5rem; } .match-bm-match-header-opponent { From 5ae5057a9b0d7a30e2c6e8424c6c071099b64965 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Thu, 19 Feb 2026 19:13:58 +0900 Subject: [PATCH 21/35] use table2 --- lua/wikis/commons/StreamPage/Starcraft.lua | 68 +++++++++++----------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/lua/wikis/commons/StreamPage/Starcraft.lua b/lua/wikis/commons/StreamPage/Starcraft.lua index 8879d1a3fa4..cd0f7888bdc 100644 --- a/lua/wikis/commons/StreamPage/Starcraft.lua +++ b/lua/wikis/commons/StreamPage/Starcraft.lua @@ -29,6 +29,7 @@ local Table = Lua.import('Module:Table') local DataTable = Lua.import('Module:Widget/Basic/DataTable') local HtmlWidgets = Lua.import('Module:Widget/Html/All') local Link = Lua.import('Module:Widget/Basic/Link') +local TableWidgets = Lua.import('Module:Widget/Table2/All') local WidgetUtil = Lua.import('Module:Widget/Util') local RANDOM_RACE = 'r' @@ -169,16 +170,16 @@ function StarcraftStreamPage:_mapPool() or race1 ~= race2 local currentMap = self:_getCurrentMap() - local matchup = skipMapWinRate and '' or race1 .. race2 + local matchup = skipMapWinRate and '' or (race1 .. race2) ---@param map 'TBA'|{link: string, displayname: string} ---@return Widget local function createMapRow(map) if map == 'TBA' then - return HtmlWidgets.Tr{ + return TableWidgets.Row{ classes = {'stats-row'}, - children = HtmlWidgets.Td{ - attributes = {colspan = 2}, + children = TableWidgets.Cell{ + colspan = 2, children = HtmlWidgets.Span{ css = { ['text-align'] = 'center', @@ -189,48 +190,49 @@ function StarcraftStreamPage:_mapPool() } } end - return HtmlWidgets.Tr{ + return TableWidgets.Row{ classes = { 'stats-row', map.link == currentMap and 'tournament-highlighted-bg' or nil }, children = WidgetUtil.collect( - HtmlWidgets.Td{children = Link{link = map.link, children = map.displayname}}, - not skipMapWinRate and HtmlWidgets.Td{ + TableWidgets.Cell{children = Link{link = map.link, children = map.displayname}}, + not skipMapWinRate and TableWidgets.Cell{ children = StarcraftStreamPage._queryMapWinrate(map.link, matchup) } or nil ), } end - return DataTable{ - tableCss = { - ['text-align'] = 'center', - margin = '0 0 10px 0', + return TableWidgets.Table{ + variant = 'themed', + css = { + ['font-size'] = '130%', }, - children = WidgetUtil.collect( - HtmlWidgets.Tr{ - classes = {'wiki-color-dark', 'wiki-backgroundcolor-light'}, - css = { - ['font-size'] = '130%', - padding = '5px 10px', - }, - children = {HtmlWidgets.Th{ - attributes = {colspan = 2}, - css = {padding = '5px'}, - children = 'Map Pool' - }} - }, - not skipMapWinRate and HtmlWidgets.Tr{children = { - HtmlWidgets.Th{children = 'Map'}, - HtmlWidgets.Th{children = { - race1:upper(), - 'v', - race2:upper() + columns = { + {align = 'center'}, + not skipMapWinRate and {align = 'center'} or nil, + }, + children = { + TableWidgets.TableHeader{children = { + TableWidgets.Row{children = { + TableWidgets.CellHeader{ + colspan = 2, + unsortable = true, + children = 'Map Pool' + } + }}, + TableWidgets.Row{children = { + TableWidgets.CellHeader{children = 'Map'}, + TableWidgets.CellHeader{children = { + race1:upper(), + 'v', + race2:upper() + }} }} - }} or nil, - Array.map(maps, createMapRow) - ) + }}, + TableWidgets.TableBody{children = Array.map(maps, createMapRow)} + } } end From 334dd77ea491e217f5365930b16d6e156a3dbd84 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Thu, 19 Feb 2026 19:15:32 +0900 Subject: [PATCH 22/35] remove unused import --- lua/wikis/commons/StreamPage/Starcraft.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/lua/wikis/commons/StreamPage/Starcraft.lua b/lua/wikis/commons/StreamPage/Starcraft.lua index cd0f7888bdc..44aa319fc9a 100644 --- a/lua/wikis/commons/StreamPage/Starcraft.lua +++ b/lua/wikis/commons/StreamPage/Starcraft.lua @@ -26,7 +26,6 @@ local Page = Lua.import('Module:Page') local PlayerDisplay = Lua.import('Module:Player/Display/Custom') local Table = Lua.import('Module:Table') -local DataTable = Lua.import('Module:Widget/Basic/DataTable') local HtmlWidgets = Lua.import('Module:Widget/Html/All') local Link = Lua.import('Module:Widget/Basic/Link') local TableWidgets = Lua.import('Module:Widget/Table2/All') From 33c05cdbab98a5199528b6f0f62c53728321a589 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Fri, 20 Feb 2026 14:41:03 +0900 Subject: [PATCH 23/35] feedback from review Co-authored-by: hjpalpha <75081997+hjpalpha@users.noreply.github.com> --- lua/wikis/commons/StreamPage/Starcraft.lua | 26 +++++++++++----------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/lua/wikis/commons/StreamPage/Starcraft.lua b/lua/wikis/commons/StreamPage/Starcraft.lua index 44aa319fc9a..44e0a179c28 100644 --- a/lua/wikis/commons/StreamPage/Starcraft.lua +++ b/lua/wikis/commons/StreamPage/Starcraft.lua @@ -166,7 +166,8 @@ function StarcraftStreamPage:_mapPool() or race1 == Faction.defaultFaction or race1 == RANDOM_RACE or race2 == Faction.defaultFaction - or race1 ~= race2 + or race2 == RANDOM_RACE + or race1 == race2 local currentMap = self:_getCurrentMap() local matchup = skipMapWinRate and '' or (race1 .. race2) @@ -178,7 +179,7 @@ function StarcraftStreamPage:_mapPool() return TableWidgets.Row{ classes = {'stats-row'}, children = TableWidgets.Cell{ - colspan = 2, + colspan = skipMapWinRate and 1 or 2, children = HtmlWidgets.Span{ css = { ['text-align'] = 'center', @@ -216,18 +217,18 @@ function StarcraftStreamPage:_mapPool() TableWidgets.TableHeader{children = { TableWidgets.Row{children = { TableWidgets.CellHeader{ - colspan = 2, + colspan = skipMapWinRate and 1 or 2, unsortable = true, children = 'Map Pool' } }}, TableWidgets.Row{children = { TableWidgets.CellHeader{children = 'Map'}, - TableWidgets.CellHeader{children = { + not skipMapWinRate and TableWidgets.CellHeader{children = { race1:upper(), 'v', race2:upper() - }} + }} or nil }} }}, TableWidgets.TableBody{children = Array.map(maps, createMapRow)} @@ -243,17 +244,16 @@ function StarcraftStreamPage._queryMapWinrate(map, matchup) local LPDBoutput = mw.ext.LiquipediaDB.lpdb('datapoint', { conditions = conditions, query = 'extradata', - }) + })[1] - if type(LPDBoutput[1]) == 'table' then - if LPDBoutput[1]['extradata'][matchup] == '-' then - return TBD - else - return math.floor(LPDBoutput[1]['extradata'][matchup]*100 + 0.5) .. '%' - end - else + if type(LPDBoutput) ~= 'table' or Logic.isEmpty(LPDBoutput) then + return TBD + end + local data = (LPDBoutput.extradata or {})[matchup] + if Logic.isEmpty(data) or data == '-' then return TBD end + return math.floor(data * 100 + 0.5) .. '%' end ---@private From 5946589e790fa7673abc04bde30055d43d91735e Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Fri, 20 Feb 2026 14:46:47 +0900 Subject: [PATCH 24/35] simplify faction check --- lua/wikis/commons/StreamPage/Starcraft.lua | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/lua/wikis/commons/StreamPage/Starcraft.lua b/lua/wikis/commons/StreamPage/Starcraft.lua index 44e0a179c28..e7302d51d78 100644 --- a/lua/wikis/commons/StreamPage/Starcraft.lua +++ b/lua/wikis/commons/StreamPage/Starcraft.lua @@ -31,7 +31,6 @@ local Link = Lua.import('Module:Widget/Basic/Link') local TableWidgets = Lua.import('Module:Widget/Table2/All') local WidgetUtil = Lua.import('Module:Widget/Util') -local RANDOM_RACE = 'r' local TBD = Abbreviation.make{title = 'To be determined (or to be decided)', text = 'TBD'} ---@class StarcraftStreamPage: BaseStreamPage @@ -161,13 +160,8 @@ function StarcraftStreamPage:_mapPool() local race2 = ((match.opponents[2].players)[1] or {}).faction --[[ @as string ]] local skipMapWinRate = not Array.all(match.opponents, function (opponent) return opponent.type == Opponent.solo end) - or not race1 - or not race2 - or race1 == Faction.defaultFaction - or race1 == RANDOM_RACE - or race2 == Faction.defaultFaction - or race2 == RANDOM_RACE - or race1 == race2 + or not Table.includes(Faction.coreFactions, race1) + or not Table.includes(Faction.coreFactions, race2) local currentMap = self:_getCurrentMap() local matchup = skipMapWinRate and '' or (race1 .. race2) From 0da922b0c4914df2009b21a2eef64facfe26ddbf Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Fri, 20 Feb 2026 14:49:14 +0900 Subject: [PATCH 25/35] use formatPercentage --- lua/wikis/commons/StreamPage/Starcraft.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lua/wikis/commons/StreamPage/Starcraft.lua b/lua/wikis/commons/StreamPage/Starcraft.lua index e7302d51d78..4eb4758adad 100644 --- a/lua/wikis/commons/StreamPage/Starcraft.lua +++ b/lua/wikis/commons/StreamPage/Starcraft.lua @@ -20,6 +20,7 @@ local Image = Lua.import('Module:Image') local Json = Lua.import('Module:Json') local Links = Lua.import('Module:Links') local Logic = Lua.import('Module:Logic') +local MathUtil = Lua.import('Module:MathUtil') local Opponent = Lua.import('Module:Opponent/Custom') local OpponentDisplay = Lua.import('Module:OpponentDisplay/Custom') local Page = Lua.import('Module:Page') @@ -247,7 +248,7 @@ function StarcraftStreamPage._queryMapWinrate(map, matchup) if Logic.isEmpty(data) or data == '-' then return TBD end - return math.floor(data * 100 + 0.5) .. '%' + return MathUtil.formatPercentage(data) end ---@private From 93c83daf31ebfcddb95d1ac58476f0a6066510ed Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Fri, 20 Feb 2026 14:50:29 +0900 Subject: [PATCH 26/35] use title param --- lua/wikis/commons/StreamPage/Starcraft.lua | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/lua/wikis/commons/StreamPage/Starcraft.lua b/lua/wikis/commons/StreamPage/Starcraft.lua index 4eb4758adad..b90013a9713 100644 --- a/lua/wikis/commons/StreamPage/Starcraft.lua +++ b/lua/wikis/commons/StreamPage/Starcraft.lua @@ -208,15 +208,9 @@ function StarcraftStreamPage:_mapPool() {align = 'center'}, not skipMapWinRate and {align = 'center'} or nil, }, + title = 'Map Pool', children = { TableWidgets.TableHeader{children = { - TableWidgets.Row{children = { - TableWidgets.CellHeader{ - colspan = skipMapWinRate and 1 or 2, - unsortable = true, - children = 'Map Pool' - } - }}, TableWidgets.Row{children = { TableWidgets.CellHeader{children = 'Map'}, not skipMapWinRate and TableWidgets.CellHeader{children = { From 3a5709085949f08dc2c0db313dbb2c68758958b6 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Fri, 20 Feb 2026 14:51:29 +0900 Subject: [PATCH 27/35] remove font-size css --- lua/wikis/commons/StreamPage/Starcraft.lua | 3 --- 1 file changed, 3 deletions(-) diff --git a/lua/wikis/commons/StreamPage/Starcraft.lua b/lua/wikis/commons/StreamPage/Starcraft.lua index b90013a9713..34b6e1f6c90 100644 --- a/lua/wikis/commons/StreamPage/Starcraft.lua +++ b/lua/wikis/commons/StreamPage/Starcraft.lua @@ -201,9 +201,6 @@ function StarcraftStreamPage:_mapPool() return TableWidgets.Table{ variant = 'themed', - css = { - ['font-size'] = '130%', - }, columns = { {align = 'center'}, not skipMapWinRate and {align = 'center'} or nil, From 701573a6f0fc5a018c95031a1798c4ef4424d6ed Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Fri, 20 Feb 2026 14:52:55 +0900 Subject: [PATCH 28/35] suppress name display for solo opponent Co-authored-by: hjpalpha <75081997+hjpalpha@users.noreply.github.com> --- lua/wikis/commons/StreamPage/Starcraft.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lua/wikis/commons/StreamPage/Starcraft.lua b/lua/wikis/commons/StreamPage/Starcraft.lua index 34b6e1f6c90..714a0060d1d 100644 --- a/lua/wikis/commons/StreamPage/Starcraft.lua +++ b/lua/wikis/commons/StreamPage/Starcraft.lua @@ -92,9 +92,9 @@ function StarcraftStreamPage._playerDisplay(opponentType, player) local image = Logic.nilIfEmpty(lpdbData.image) or 'Blank Player Image.png' local imageDisplay = Image.display(image, nil, {class = 'img-fluid', size = '600px'}) - local nameDisplay = PlayerDisplay.InlinePlayer{ + local nameDisplay = opponentType ~= Opponent.solo and PlayerDisplay.InlinePlayer{ player = player - } + } or nil return HtmlWidgets.Div{ classes = {'match-bm-players-player', 'match-bm-players-player--col-2'}, From 72d1809e82a3ed3fd5f4be397c3cd382b7a02851 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Fri, 20 Feb 2026 15:03:43 +0900 Subject: [PATCH 29/35] add option for suppressing map table --- lua/wikis/commons/StreamPage/Starcraft.lua | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/lua/wikis/commons/StreamPage/Starcraft.lua b/lua/wikis/commons/StreamPage/Starcraft.lua index 714a0060d1d..16d36259a5b 100644 --- a/lua/wikis/commons/StreamPage/Starcraft.lua +++ b/lua/wikis/commons/StreamPage/Starcraft.lua @@ -36,7 +36,10 @@ local TBD = Abbreviation.make{title = 'To be determined (or to be decided)', tex ---@class StarcraftStreamPage: BaseStreamPage ---@operator call(table): StarcraftStreamPage -local StarcraftStreamPage = Class.new(BaseStreamPage) +---@field suppressMapTable boolean +local StarcraftStreamPage = Class.new(BaseStreamPage, function (self, args) + self.suppressMapTable = Logic.readBool(args.suppressMapTable) +end) ---@param frame Frame ---@return Widget? @@ -45,13 +48,13 @@ function StarcraftStreamPage.run(frame) return StarcraftStreamPage(args):create() end ----@return Widget|Widget[]? +---@return Widget[] function StarcraftStreamPage:render() - return { + return WidgetUtil.collect( HtmlWidgets.H3{children = 'Player Information'}, self:renderPlayerInformation(), self:_mapPool() - } + ) end ---@protected @@ -144,8 +147,12 @@ function StarcraftStreamPage._playerDisplay(opponentType, player) end ---@private ----@return Html +---@return Widget? function StarcraftStreamPage:_mapPool() + if self.suppressMapTable then + return + end + local match = self.matches[1] local maps = Logic.emptyOr( From b757cf9949ee766bb6d424cb0052ccd415b4a372 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Fri, 20 Feb 2026 15:05:02 +0900 Subject: [PATCH 30/35] rename as suggested --- .../StreamPage/{Starcraft.lua => Faction.lua} | 34 +++++++++---------- lua/wikis/starcraft/StreamPage/Custom.lua | 2 +- lua/wikis/starcraft2/StreamPage/Custom.lua | 2 +- 3 files changed, 19 insertions(+), 19 deletions(-) rename lua/wikis/commons/StreamPage/{Starcraft.lua => Faction.lua} (88%) diff --git a/lua/wikis/commons/StreamPage/Starcraft.lua b/lua/wikis/commons/StreamPage/Faction.lua similarity index 88% rename from lua/wikis/commons/StreamPage/Starcraft.lua rename to lua/wikis/commons/StreamPage/Faction.lua index 16d36259a5b..2e3a8416e2a 100644 --- a/lua/wikis/commons/StreamPage/Starcraft.lua +++ b/lua/wikis/commons/StreamPage/Faction.lua @@ -1,6 +1,6 @@ --- -- @Liquipedia --- page=Module:StreamPage/Starcraft +-- page=Module:StreamPage/Faction -- -- Please see https://github.com/Liquipedia/Lua-Modules to contribute -- @@ -34,22 +34,22 @@ local WidgetUtil = Lua.import('Module:Widget/Util') local TBD = Abbreviation.make{title = 'To be determined (or to be decided)', text = 'TBD'} ----@class StarcraftStreamPage: BaseStreamPage ----@operator call(table): StarcraftStreamPage +---@class FactionStreamPage: BaseStreamPage +---@operator call(table): FactionStreamPage ---@field suppressMapTable boolean -local StarcraftStreamPage = Class.new(BaseStreamPage, function (self, args) +local FactionStreamPage = Class.new(BaseStreamPage, function (self, args) self.suppressMapTable = Logic.readBool(args.suppressMapTable) end) ---@param frame Frame ---@return Widget? -function StarcraftStreamPage.run(frame) +function FactionStreamPage.run(frame) local args = Arguments.getArgs(frame) - return StarcraftStreamPage(args):create() + return FactionStreamPage(args):create() end ---@return Widget[] -function StarcraftStreamPage:render() +function FactionStreamPage:render() return WidgetUtil.collect( HtmlWidgets.H3{children = 'Player Information'}, self:renderPlayerInformation(), @@ -59,18 +59,18 @@ end ---@protected ---@return Widget -function StarcraftStreamPage:renderPlayerInformation() +function FactionStreamPage:renderPlayerInformation() return HtmlWidgets.Div{ classes = {'match-bm-players-wrapper'}, css = {width = '100%'}, - children = Array.map(self.matches[1].opponents, StarcraftStreamPage._opponentDisplay) + children = Array.map(self.matches[1].opponents, FactionStreamPage._opponentDisplay) } end ---@private ---@param opponent standardOpponent ---@return Widget -function StarcraftStreamPage._opponentDisplay(opponent) +function FactionStreamPage._opponentDisplay(opponent) return HtmlWidgets.Div{ classes = {'match-bm-players-team'}, children = WidgetUtil.collect( @@ -78,7 +78,7 @@ function StarcraftStreamPage._opponentDisplay(opponent) classes = {'match-bm-players-team-header'}, children = OpponentDisplay.InlineOpponent{opponent = opponent, teamStyle = 'icon'} }, - Array.map(opponent.players, FnUtil.curry(StarcraftStreamPage._playerDisplay, opponent.type)) + Array.map(opponent.players, FnUtil.curry(FactionStreamPage._playerDisplay, opponent.type)) ) } end @@ -86,7 +86,7 @@ end ---@param opponentType OpponentType ---@param player standardPlayer ---@return Widget -function StarcraftStreamPage._playerDisplay(opponentType, player) +function FactionStreamPage._playerDisplay(opponentType, player) local lpdbData = mw.ext.LiquipediaDB.lpdb('player', { conditions = '[[pagename::' .. (Page.pageifyLink(player.pageName) or '') .. ']]', limit = 1 @@ -148,7 +148,7 @@ end ---@private ---@return Widget? -function StarcraftStreamPage:_mapPool() +function FactionStreamPage:_mapPool() if self.suppressMapTable then return end @@ -200,7 +200,7 @@ function StarcraftStreamPage:_mapPool() children = WidgetUtil.collect( TableWidgets.Cell{children = Link{link = map.link, children = map.displayname}}, not skipMapWinRate and TableWidgets.Cell{ - children = StarcraftStreamPage._queryMapWinrate(map.link, matchup) + children = FactionStreamPage._queryMapWinrate(map.link, matchup) } or nil ), } @@ -232,7 +232,7 @@ end ---@param map string ---@param matchup string ---@return string? -function StarcraftStreamPage._queryMapWinrate(map, matchup) +function FactionStreamPage._queryMapWinrate(map, matchup) local conditions = '[[pagename::' .. string.gsub(map, ' ', '_') .. ']] AND [[type::map_winrates]]' local LPDBoutput = mw.ext.LiquipediaDB.lpdb('datapoint', { conditions = conditions, @@ -251,7 +251,7 @@ end ---@private ---@return string? -function StarcraftStreamPage:_getCurrentMap() +function FactionStreamPage:_getCurrentMap() local games = self.matches[1].games for _, game in ipairs(games) do if Logic.isEmpty(game.winner) then @@ -260,4 +260,4 @@ function StarcraftStreamPage:_getCurrentMap() end end -return StarcraftStreamPage +return FactionStreamPage diff --git a/lua/wikis/starcraft/StreamPage/Custom.lua b/lua/wikis/starcraft/StreamPage/Custom.lua index b9c13b053c0..e9f6f554b8b 100644 --- a/lua/wikis/starcraft/StreamPage/Custom.lua +++ b/lua/wikis/starcraft/StreamPage/Custom.lua @@ -7,4 +7,4 @@ local Lua = require('Module:Lua') -return Lua.import('Module:StreamPage/Starcraft') +return Lua.import('Module:StreamPage/Faction') diff --git a/lua/wikis/starcraft2/StreamPage/Custom.lua b/lua/wikis/starcraft2/StreamPage/Custom.lua index b9c13b053c0..e9f6f554b8b 100644 --- a/lua/wikis/starcraft2/StreamPage/Custom.lua +++ b/lua/wikis/starcraft2/StreamPage/Custom.lua @@ -7,4 +7,4 @@ local Lua = require('Module:Lua') -return Lua.import('Module:StreamPage/Starcraft') +return Lua.import('Module:StreamPage/Faction') From fba0b30fbf143d388e14aa44794e8d02636f53cd Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Fri, 20 Feb 2026 15:28:59 +0900 Subject: [PATCH 31/35] tabs --- lua/wikis/commons/MatchGroup/Util.lua | 2 ++ lua/wikis/commons/StreamPage/Faction.lua | 33 +++++++++++++++++++----- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/lua/wikis/commons/MatchGroup/Util.lua b/lua/wikis/commons/MatchGroup/Util.lua index a877ca27db7..0d0879851c4 100644 --- a/lua/wikis/commons/MatchGroup/Util.lua +++ b/lua/wikis/commons/MatchGroup/Util.lua @@ -256,6 +256,7 @@ MatchGroupUtil.types.Game = TypeUtil.struct({ ---@class MatchGroupUtilMatch ---@field bracketData MatchGroupUtilBracketData +---@field bracketId string ---@field comment string? ---@field date string ---@field dateIsExact boolean @@ -565,6 +566,7 @@ function MatchGroupUtil.matchFromRecord(record) local match = { bestof = tonumber(record.bestof) or 0, bracketData = bracketData, + bracketId = record.match2bracketid, comment = nilIfEmpty(Table.extract(extradata, 'comment')), extradata = extradata, date = record.date, diff --git a/lua/wikis/commons/StreamPage/Faction.lua b/lua/wikis/commons/StreamPage/Faction.lua index 2e3a8416e2a..36b08182490 100644 --- a/lua/wikis/commons/StreamPage/Faction.lua +++ b/lua/wikis/commons/StreamPage/Faction.lua @@ -21,10 +21,12 @@ local Json = Lua.import('Module:Json') local Links = Lua.import('Module:Links') local Logic = Lua.import('Module:Logic') local MathUtil = Lua.import('Module:MathUtil') +local MatchGroup = Lua.import('Module:MatchGroup') local Opponent = Lua.import('Module:Opponent/Custom') local OpponentDisplay = Lua.import('Module:OpponentDisplay/Custom') local Page = Lua.import('Module:Page') local PlayerDisplay = Lua.import('Module:Player/Display/Custom') +local Tabs = Lua.import('Module:Tabs') local Table = Lua.import('Module:Table') local HtmlWidgets = Lua.import('Module:Widget/Html/All') @@ -48,13 +50,14 @@ function FactionStreamPage.run(frame) return FactionStreamPage(args):create() end ----@return Widget[] +---@return string|Widget? function FactionStreamPage:render() - return WidgetUtil.collect( - HtmlWidgets.H3{children = 'Player Information'}, - self:renderPlayerInformation(), - self:_mapPool() - ) + return Tabs.dynamic{ + name1 = 'Player Information', + content1 = self:renderPlayerInformation(), + name2 = 'Tournament Information', + content2 = self:renderTournamentInformation() + } end ---@protected @@ -146,6 +149,24 @@ function FactionStreamPage._playerDisplay(opponentType, player) } end +---@param props table +---@return Widget +local function createTemplateBox(props) + return HtmlWidgets.Div{ + classes = Array.extend('template-box', props.classes), + css = {['padding-right'] = Logic.emptyOr(props.padding, '2em')}, + children = props.children + } +end + +function FactionStreamPage:renderTournamentInformation() + local match = self.matches[1] + return HtmlWidgets.Div{children = { + createTemplateBox{children = self:_mapPool()}, + createTemplateBox{children = MatchGroup.MatchGroupById{id = match.bracketId}} + }} +end + ---@private ---@return Widget? function FactionStreamPage:_mapPool() From 5f95ab87f2ab9dafe77408047dc3c5828639ce71 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Tue, 24 Feb 2026 15:10:30 +0900 Subject: [PATCH 32/35] adjust display Co-authored-by: hjpalpha <75081997+hjpalpha@users.noreply.github.com> --- lua/wikis/commons/StreamPage/Base.lua | 9 ++++++--- lua/wikis/commons/StreamPage/Faction.lua | 14 +++++++++----- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/lua/wikis/commons/StreamPage/Base.lua b/lua/wikis/commons/StreamPage/Base.lua index 6b75d6fd004..0b1b6f14d64 100644 --- a/lua/wikis/commons/StreamPage/Base.lua +++ b/lua/wikis/commons/StreamPage/Base.lua @@ -43,10 +43,12 @@ local WidgetUtil = Lua.import('Module:Widget/Util') ---@operator call(table): BaseStreamPage ---@field channel string ---@field provider string +---@field suppressBottomContent boolean ---@field matches MatchGroupUtilMatch[] local StreamPage = Class.new(function (self, args) self.channel = assert(Logic.nilIfEmpty(args.channel)) self.provider = assert(Logic.nilIfEmpty(args.provider)) + self.suppressBottomContent = Logic.readBool(args.suppressBottomContent) self.matches = {} self:_fetchMatches() @@ -128,6 +130,7 @@ function StreamPage:_header() opponent.teamTemplateData = TeamTemplate.getRaw(opponent.template) opponent.seriesDots = {} end) + return MatchPageHeader{ countdownBlock = countdownBlock, isBestOfOne = match.bestof == 1, @@ -136,7 +139,7 @@ function StreamPage:_header() opponent2 = match.opponents[2], parent = tournament.pageName, phase = MatchGroupUtil.computeMatchPhase(match), - stream = match.stream, + stream = {}, tournamentName = tournament.fullName, highlighted = HighlightConditions.tournament(tournament) } @@ -181,7 +184,7 @@ function StreamPage:create() xxxl = 3, } }}, - self:createBottomContent() + not self.suppressBottomContent and self:createBottomContent() or nil )} end @@ -214,7 +217,7 @@ function StreamPage:createBottomContent() local headToHead = self:_buildHeadToHeadMatchTable() return WidgetUtil.collect( - HtmlWidgets.H3{children = 'Match History'}, + not self.suppressBottomContent and HtmlWidgets.H3{children = 'Match History'} or nil, HtmlWidgets.Div{ classes = {'match-bm-match-additional'}, children = WidgetUtil.collect( diff --git a/lua/wikis/commons/StreamPage/Faction.lua b/lua/wikis/commons/StreamPage/Faction.lua index 36b08182490..b1802e16121 100644 --- a/lua/wikis/commons/StreamPage/Faction.lua +++ b/lua/wikis/commons/StreamPage/Faction.lua @@ -47,16 +47,20 @@ end) ---@return Widget? function FactionStreamPage.run(frame) local args = Arguments.getArgs(frame) - return FactionStreamPage(args):create() + args.suppressBottomContent = true + local factionStreamPage = FactionStreamPage(args) + return factionStreamPage:create() end ---@return string|Widget? function FactionStreamPage:render() return Tabs.dynamic{ - name1 = 'Player Information', + name1 = 'Players', content1 = self:renderPlayerInformation(), - name2 = 'Tournament Information', - content2 = self:renderTournamentInformation() + name2 = 'Head to Head', + content2 = self:createBottomContent(), + name3 = 'Tournament Stage', + content3 = self:renderTournamentInformation() } end @@ -163,6 +167,7 @@ function FactionStreamPage:renderTournamentInformation() local match = self.matches[1] return HtmlWidgets.Div{children = { createTemplateBox{children = self:_mapPool()}, + -- todo after we have standardized standings: make it display the group table where applicable too createTemplateBox{children = MatchGroup.MatchGroupById{id = match.bracketId}} }} end @@ -228,7 +233,6 @@ function FactionStreamPage:_mapPool() end return TableWidgets.Table{ - variant = 'themed', columns = { {align = 'center'}, not skipMapWinRate and {align = 'center'} or nil, From 8de7534e3046bd4156efbb7a54d94fdd15304dd3 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Tue, 24 Feb 2026 15:12:42 +0900 Subject: [PATCH 33/35] use compact date --- lua/wikis/commons/StreamPage/Base.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/lua/wikis/commons/StreamPage/Base.lua b/lua/wikis/commons/StreamPage/Base.lua index 0b1b6f14d64..584018bb3ce 100644 --- a/lua/wikis/commons/StreamPage/Base.lua +++ b/lua/wikis/commons/StreamPage/Base.lua @@ -251,6 +251,7 @@ function StreamPage:_createMatchTable(props) local match = self.matches[1] return MatchTable(Table.mergeInto({ addCategory = false, + dateFormat = 'compact', edate = match.timestamp - DateExt.daysToSeconds(1) --[[ MatchTable adds 1-day offset to make edate inclusive, and we don't want that here ]], limit = 5, From 58c2176abe3cb58fd71615b63fa7b7b57031ccd3 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Tue, 24 Feb 2026 18:48:38 +0900 Subject: [PATCH 34/35] code conciseness Co-authored-by: hjpalpha <75081997+hjpalpha@users.noreply.github.com> --- lua/wikis/commons/StreamPage/Team.lua | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lua/wikis/commons/StreamPage/Team.lua b/lua/wikis/commons/StreamPage/Team.lua index 1e75ed0cf24..3af4ca5a5ce 100644 --- a/lua/wikis/commons/StreamPage/Team.lua +++ b/lua/wikis/commons/StreamPage/Team.lua @@ -74,10 +74,7 @@ function TeamStreamPage._playerDisplay(player) limit = 1 })[1] - local image = lpdbData.image - if String.isEmpty(image) then - image = Logic.emptyOr((lpdbData.extradata or {}).image, 'Blank Player Image.png') --[[@as string]] - end + local image = Logic.emptyOr(lpdbData.image, (lpdbData.extradata or {}).image, 'Blank Player Image.png')--[[@as string]] local imageDisplay = Image.display(image, nil, {class = 'img-fluid', size = '600px'}) From 32e63130a2162120d08a4fed3b90e326c91e2a27 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Tue, 24 Feb 2026 18:50:46 +0900 Subject: [PATCH 35/35] remove unused import --- lua/wikis/commons/StreamPage/Team.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/lua/wikis/commons/StreamPage/Team.lua b/lua/wikis/commons/StreamPage/Team.lua index 3af4ca5a5ce..348cc670b07 100644 --- a/lua/wikis/commons/StreamPage/Team.lua +++ b/lua/wikis/commons/StreamPage/Team.lua @@ -16,7 +16,6 @@ local Logic = Lua.import('Module:Logic') local OpponentDisplay = Lua.import('Module:OpponentDisplay/Custom') local Page = Lua.import('Module:Page') local PlayerDisplay = Lua.import('Module:Player/Display/Custom') -local String = Lua.import('Module:StringUtils') local HtmlWidgets = Lua.import('Module:Widget/Html/All') local WidgetUtil = Lua.import('Module:Widget/Util')