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/Base.lua b/lua/wikis/commons/StreamPage/Base.lua new file mode 100644 index 00000000000..584018bb3ce --- /dev/null +++ b/lua/wikis/commons/StreamPage/Base.lua @@ -0,0 +1,306 @@ +--- +-- @Liquipedia +-- page=Module:StreamPage/Base +-- +-- 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 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 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') +local MatchTicker = Lua.import('Module:MatchTicker') +local Opponent = Lua.import('Module:Opponent/Custom') +local OpponentDisplay = Lua.import('Module:OpponentDisplay/Custom') +local Table = Lua.import('Module:Table') +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 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') + +---@class BaseStreamPage: BaseClass +---@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() +end) + +---@param frame Frame +---@return Widget? +function StreamPage.run(frame) + local args = Arguments.getArgs(frame) + return StreamPage(args):create() +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 = {}, + 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 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{ + cellContent = self:render(), + xs = 'ignore', + sm = 'ignore', + lg = 8, + xl = 8, + xxl = 9, + xxxl = 9, + }, + GridWidgets.Cell{ + cellContent = MatchPageAdditionalSection{ + header = 'Channel Schedule', + children = self:_createMatchTicker():query():create():css('width', '100%') + }, + xs = 'ignore', + sm = 'ignore', + lg = 4, + xl = 4, + xxl = 3, + xxxl = 3, + } + }}, + not self.suppressBottomContent and self:createBottomContent() or nil + )} +end + +---@private +---@return MatchTicker +function StreamPage:_createMatchTicker() + return MatchTicker{ + additionalConditions = 'AND (' .. tostring(self:_createMatchQueryCondition()) .. ')', + limit = 5, + newStyle = true, + ongoing = true, + upcoming = true, + } +end + +---@protected +---@return string|Widget|Html|(string|Widget|Html)[]? +function StreamPage:render() +end + +---@protected +---@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( + not self.suppressBottomContent and HtmlWidgets.H3{children = 'Match History'} or nil, + 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, + 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, + 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 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') diff --git a/lua/wikis/commons/StreamPage/Faction.lua b/lua/wikis/commons/StreamPage/Faction.lua new file mode 100644 index 00000000000..b1802e16121 --- /dev/null +++ b/lua/wikis/commons/StreamPage/Faction.lua @@ -0,0 +1,288 @@ +--- +-- @Liquipedia +-- page=Module:StreamPage/Faction +-- +-- Please see https://github.com/Liquipedia/Lua-Modules to contribute +-- + +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 FnUtil = Lua.import('Module:FnUtil') +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 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') +local Link = Lua.import('Module:Widget/Basic/Link') +local TableWidgets = Lua.import('Module:Widget/Table2/All') +local WidgetUtil = Lua.import('Module:Widget/Util') + +local TBD = Abbreviation.make{title = 'To be determined (or to be decided)', text = 'TBD'} + +---@class FactionStreamPage: BaseStreamPage +---@operator call(table): FactionStreamPage +---@field suppressMapTable boolean +local FactionStreamPage = Class.new(BaseStreamPage, function (self, args) + self.suppressMapTable = Logic.readBool(args.suppressMapTable) +end) + +---@param frame Frame +---@return Widget? +function FactionStreamPage.run(frame) + local args = Arguments.getArgs(frame) + args.suppressBottomContent = true + local factionStreamPage = FactionStreamPage(args) + return factionStreamPage:create() +end + +---@return string|Widget? +function FactionStreamPage:render() + return Tabs.dynamic{ + name1 = 'Players', + content1 = self:renderPlayerInformation(), + name2 = 'Head to Head', + content2 = self:createBottomContent(), + name3 = 'Tournament Stage', + content3 = self:renderTournamentInformation() + } +end + +---@protected +---@return Widget +function FactionStreamPage:renderPlayerInformation() + return HtmlWidgets.Div{ + classes = {'match-bm-players-wrapper'}, + css = {width = '100%'}, + children = Array.map(self.matches[1].opponents, FactionStreamPage._opponentDisplay) + } +end + +---@private +---@param opponent standardOpponent +---@return Widget +function FactionStreamPage._opponentDisplay(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, FnUtil.curry(FactionStreamPage._playerDisplay, opponent.type)) + ) + } +end + +---@param opponentType OpponentType +---@param player standardPlayer +---@return Widget +function FactionStreamPage._playerDisplay(opponentType, player) + local lpdbData = mw.ext.LiquipediaDB.lpdb('player', { + conditions = '[[pagename::' .. (Page.pageifyLink(player.pageName) or '') .. ']]', + limit = 1 + })[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 = opponentType ~= Opponent.solo and PlayerDisplay.InlinePlayer{ + player = player + } or nil + + 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, + 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) .. ')' + }} 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 + +---@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()}, + -- todo after we have standardized standings: make it display the group table where applicable too + createTemplateBox{children = MatchGroup.MatchGroupById{id = match.bracketId}} + }} +end + +---@private +---@return Widget? +function FactionStreamPage:_mapPool() + if self.suppressMapTable then + return + end + + 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 --[[ @as string ]] + 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 Table.includes(Faction.coreFactions, race1) + or not Table.includes(Faction.coreFactions, race2) + + local currentMap = self:_getCurrentMap() + 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 TableWidgets.Row{ + classes = {'stats-row'}, + children = TableWidgets.Cell{ + colspan = skipMapWinRate and 1 or 2, + children = HtmlWidgets.Span{ + css = { + ['text-align'] = 'center', + ['font-style'] = 'italic', + }, + children = 'To be announced' + } + } + } + end + return TableWidgets.Row{ + classes = { + 'stats-row', + map.link == currentMap and 'tournament-highlighted-bg' or nil + }, + children = WidgetUtil.collect( + TableWidgets.Cell{children = Link{link = map.link, children = map.displayname}}, + not skipMapWinRate and TableWidgets.Cell{ + children = FactionStreamPage._queryMapWinrate(map.link, matchup) + } or nil + ), + } + end + + return TableWidgets.Table{ + columns = { + {align = 'center'}, + not skipMapWinRate and {align = 'center'} or nil, + }, + title = 'Map Pool', + children = { + TableWidgets.TableHeader{children = { + TableWidgets.Row{children = { + TableWidgets.CellHeader{children = 'Map'}, + not skipMapWinRate and TableWidgets.CellHeader{children = { + race1:upper(), + 'v', + race2:upper() + }} or nil + }} + }}, + TableWidgets.TableBody{children = Array.map(maps, createMapRow)} + } + } +end + +---@param map string +---@param matchup string +---@return string? +function FactionStreamPage._queryMapWinrate(map, matchup) + local conditions = '[[pagename::' .. string.gsub(map, ' ', '_') .. ']] AND [[type::map_winrates]]' + local LPDBoutput = mw.ext.LiquipediaDB.lpdb('datapoint', { + conditions = conditions, + query = 'extradata', + })[1] + + 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 MathUtil.formatPercentage(data) +end + +---@private +---@return string? +function FactionStreamPage:_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 FactionStreamPage diff --git a/lua/wikis/commons/StreamPage/Team.lua b/lua/wikis/commons/StreamPage/Team.lua new file mode 100644 index 00000000000..348cc670b07 --- /dev/null +++ b/lua/wikis/commons/StreamPage/Team.lua @@ -0,0 +1,90 @@ +--- +-- @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 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 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 + +---@protected +---@return Widget +function TeamStreamPage:renderPlayerInformation() + return HtmlWidgets.Div{ + classes = {'match-bm-players-wrapper'}, + css = {width = '100%'}, + 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 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'}) + + 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 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') diff --git a/lua/wikis/starcraft/StreamPage/Custom.lua b/lua/wikis/starcraft/StreamPage/Custom.lua new file mode 100644 index 00000000000..e9f6f554b8b --- /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/Faction') diff --git a/lua/wikis/starcraft2/StreamPage/Custom.lua b/lua/wikis/starcraft2/StreamPage/Custom.lua new file mode 100644 index 00000000000..e9f6f554b8b --- /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/Faction') 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 {