From cc411d654a9e8dbff5e0d490a1199584e791c297 Mon Sep 17 00:00:00 2001 From: David Trattnig <david@subsquare.at> Date: Fri, 16 Dec 2022 20:36:45 +0100 Subject: [PATCH] refact: initial context config implementation --- src/cards/CategoryCard.svelte | 8 +++++--- src/cards/EpisodeCardBig.svelte | 5 ++++- src/cards/EpisodeCardSmall.svelte | 6 ++++-- src/cards/HostCardAvatar.svelte | 5 ++++- src/cards/HostCardBig.svelte | 10 ++++++---- src/cards/NowPlayingCard.svelte | 5 ++++- src/cards/ShowCardBig.svelte | 10 +++++----- src/cards/ShowCardMedium.svelte | 6 ++++-- src/cards/ShowCardSmall.svelte | 7 ++++--- src/cards/TimeslotCard.svelte | 6 ++++-- src/components/CategoryList.svelte | 12 ++++++++---- src/components/EpisodeDetail.svelte | 13 ++++++++----- src/components/HostDetail.svelte | 15 +++++++++++---- src/components/HostList.svelte | 14 ++++++-------- src/components/MediaPlayer.svelte | 9 ++++++--- src/components/NowPlayingWidget.svelte | 14 +++++++------- src/components/Player.svelte | 12 ++++++++---- src/components/Programme.svelte | 16 +++++++++------- src/components/ShowDetail.svelte | 20 +++++++++++++------- src/components/ShowList.svelte | 20 +++++++++++--------- src/elements/CategoryHeading.svelte | 5 ++++- 21 files changed, 135 insertions(+), 83 deletions(-) diff --git a/src/cards/CategoryCard.svelte b/src/cards/CategoryCard.svelte index 1b9c1f2..80a8e61 100644 --- a/src/cards/CategoryCard.svelte +++ b/src/cards/CategoryCard.svelte @@ -1,11 +1,13 @@ <script lang="ts"> + import { getContext } from 'svelte' import Ripple from '@smui/ripple' import Card, { Content } from '@smui/card' import CategoryHeading from '../elements/CategoryHeading.svelte' export let category: any - export let urlShowList: string - export let renderHtml: boolean // Attention: to avoid XSS attack vectors use with trusted API sources only + + const context: { [name: string]: any } = getContext('category') + const urlShowList = context.urlShowList </script> <style lang="scss"> @@ -20,7 +22,7 @@ <Card class="aura-card category cat-{category.id}"> <div use:Ripple={{ surface: true }}> <Content class="category-content"> - <CategoryHeading {category} {renderHtml} headingLevel={2} /> + <CategoryHeading headingLevel={2} {category} /> </Content> </div> </Card> diff --git a/src/cards/EpisodeCardBig.svelte b/src/cards/EpisodeCardBig.svelte index 1f1c663..ddfd12f 100644 --- a/src/cards/EpisodeCardBig.svelte +++ b/src/cards/EpisodeCardBig.svelte @@ -1,4 +1,5 @@ <script> + import { getContext } from 'svelte' import Spinner from '../common/Spinner.svelte' import Card, { Content } from '@smui/card' import LayoutGrid, { Cell } from '@smui/layout-grid' @@ -8,12 +9,14 @@ import EpisodePlayMedia from '../elements/EpisodePlayMedia.svelte' export let embedCbaPlayer = true - export let renderHtml = false // Attention: to avoid XSS attack vectors use with trusted API sources only export let show = undefined export let episode = undefined export let urlShowDetail = undefined export let labelPlay = undefined export let labelLinkArchive = undefined + + const context = getContext('episode') + const renderHtml = context.renderHtml </script> <style lang="scss"> diff --git a/src/cards/EpisodeCardSmall.svelte b/src/cards/EpisodeCardSmall.svelte index a196d25..6551580 100644 --- a/src/cards/EpisodeCardSmall.svelte +++ b/src/cards/EpisodeCardSmall.svelte @@ -1,5 +1,5 @@ <script> - import { onMount } from 'svelte' + import { onMount, getContext } from 'svelte' import Spinner from '../common/Spinner.svelte' import Ripple from '@smui/ripple' import Card, { Content } from '@smui/card' @@ -9,7 +9,9 @@ export let episode = undefined export let urlEpisodeDetail = undefined - export let renderHtml = false // Attention: to avoid XSS attack vectors use with trusted API sources only + + const context = getContext('episode') + const renderHtml = context.renderHtml /* Initialize the component */ onMount(() => {}) diff --git a/src/cards/HostCardAvatar.svelte b/src/cards/HostCardAvatar.svelte index 905fa79..c541739 100644 --- a/src/cards/HostCardAvatar.svelte +++ b/src/cards/HostCardAvatar.svelte @@ -1,12 +1,15 @@ <script> + import { getContext } from 'svelte' import Ripple from '@smui/ripple' import Card, { Media } from '@smui/card' import Display from '../common/Display.svelte' export let host = undefined export let urlHostDetail = undefined - export let renderHtml = false // Attention: to avoid XSS attack vectors use with trusted API sources only export let displayOverlay = false + + const context = getContext('host') + const renderHtml = context.renderHtml </script> <style lang="scss"> diff --git a/src/cards/HostCardBig.svelte b/src/cards/HostCardBig.svelte index 322851b..6ef3c29 100644 --- a/src/cards/HostCardBig.svelte +++ b/src/cards/HostCardBig.svelte @@ -1,5 +1,5 @@ <script> - import { onMount } from 'svelte' + import { onMount, getContext } from 'svelte' import Spinner from '../common/Spinner.svelte' import Card, { Content } from '@smui/card' import LayoutGrid, { Cell } from '@smui/layout-grid' @@ -8,12 +8,14 @@ import HostCardAvatar from './HostCardAvatar.svelte' import ShowCardSmall from './ShowCardSmall.svelte' - export let renderHtml = false // Attention: to avoid XSS attack vectors use with trusted API sources only export let labelShows = 'Shows' export let host = undefined export let shows = undefined export let urlShowDetail = undefined + const context = getContext('host') + const renderHtml = context.renderHtml + /* Initialize the component */ onMount(() => {}) </script> @@ -98,7 +100,7 @@ <Cell span={4}> <div class="card-cell cell-right"> <div class="card-container detail"> - <HostCardAvatar {host} {renderHtml} /> + <HostCardAvatar {host} /> </div> </div> </Cell> @@ -110,7 +112,7 @@ <h2>{labelShows}</h2> <div class="show-list-small card-container list contained"> {#each shows as show} - <ShowCardSmall {show} {urlShowDetail} {renderHtml} /> + <ShowCardSmall {show} {urlShowDetail} /> {/each} </div> </div> diff --git a/src/cards/NowPlayingCard.svelte b/src/cards/NowPlayingCard.svelte index d6e9b86..9db8180 100644 --- a/src/cards/NowPlayingCard.svelte +++ b/src/cards/NowPlayingCard.svelte @@ -1,4 +1,5 @@ <script lang="ts"> + import { getContext } from 'svelte' import Card, { Content } from '@smui/card' import Button, { Icon, Label } from '@smui/button' import { hasEpisodeTitle } from '../common/Common.svelte' @@ -7,8 +8,10 @@ export let episode: { [id: string]: string } = {} export let prefixOnAir: string - export let renderHtml: boolean // Attention: to avoid XSS attack vectors use with trusted API sources only export let callbackButton: Function + + const context: { [name: string]: any } = getContext('player') + const renderHtml: boolean = context.renderHtml </script> <style lang="scss"> diff --git a/src/cards/ShowCardBig.svelte b/src/cards/ShowCardBig.svelte index d07741e..740bc72 100644 --- a/src/cards/ShowCardBig.svelte +++ b/src/cards/ShowCardBig.svelte @@ -1,5 +1,5 @@ <script> - import { onMount } from 'svelte' + import { onMount, getContext } from 'svelte' import Spinner from '../common/Spinner.svelte' import LayoutGrid, { Cell } from '@smui/layout-grid' import Card, { Content } from '@smui/card' @@ -11,7 +11,6 @@ import { createEventDispatcher } from 'svelte' const dispatch = createEventDispatcher() - export let renderHtml = false // Attention: to avoid XSS attack vectors use with trusted API sources only export let show = undefined export let episodes = undefined export let urlHostDetail = undefined @@ -25,6 +24,9 @@ export let displayCatIcons = undefined export let displayLangIcons = undefined + const context = getContext('show') + const renderHtml = context.renderHtml + function eventShowMore() { dispatch('showMore') } @@ -178,7 +180,6 @@ <HostCardAvatar {host} {urlHostDetail} - {renderHtml} displayOverlay={true} /> {/each} </div> @@ -201,8 +202,7 @@ <span class={episode.isCurrent ? 'highlight' : ''}> <EpisodeCardSmall {episode} - urlEpisodeDetail={urlEpisodeDetail + episode.id} - {renderHtml} /> + urlEpisodeDetail={urlEpisodeDetail + episode.id} /> </span> {/each} {:else} diff --git a/src/cards/ShowCardMedium.svelte b/src/cards/ShowCardMedium.svelte index 6fdc219..0e34920 100644 --- a/src/cards/ShowCardMedium.svelte +++ b/src/cards/ShowCardMedium.svelte @@ -1,12 +1,14 @@ <script> - import { onMount } from 'svelte' + import { onMount, getContext } from 'svelte' import Spinner from '../common/Spinner.svelte' import Card, { Content, PrimaryAction, Media } from '@smui/card' import Display from '../common/Display.svelte' export let show = undefined export let urlShowDetail = undefined - export let renderHtml = false // Attention: to avoid XSS attack vectors use with trusted API sources only + + const context = getContext('show') + const renderHtml = context.renderHtml /* Initialize the component */ onMount(() => {}) diff --git a/src/cards/ShowCardSmall.svelte b/src/cards/ShowCardSmall.svelte index fd1bb23..34c7a08 100644 --- a/src/cards/ShowCardSmall.svelte +++ b/src/cards/ShowCardSmall.svelte @@ -1,15 +1,16 @@ <script> - import { onMount } from 'svelte' + import { onMount, getContext } from 'svelte' import Spinner from '../common/Spinner.svelte' import Ripple from '@smui/ripple' import Card, { Content, Media } from '@smui/card' import LayoutGrid, { Cell } from '@smui/layout-grid' - import Display from '../common/Display.svelte' export let show = undefined export let urlShowDetail = undefined - export let renderHtml = false // Attention: to avoid XSS attack vectors use with trusted API sources only + + const context = getContext('show') + const renderHtml = context.renderHtml /* Initialize the component */ onMount(() => {}) diff --git a/src/cards/TimeslotCard.svelte b/src/cards/TimeslotCard.svelte index affc775..f94e502 100644 --- a/src/cards/TimeslotCard.svelte +++ b/src/cards/TimeslotCard.svelte @@ -1,5 +1,5 @@ <script> - import { onMount } from 'svelte' + import { onMount, getContext } from 'svelte' import Ripple from '@smui/ripple' import Card from '@smui/card' import LayoutGrid, { Cell } from '@smui/layout-grid' @@ -19,7 +19,9 @@ export let displayCatTitle = undefined export let displayCatIcons = undefined export let displayLangIcons = undefined - export let renderHtml = false // Attention: to avoid XSS attack vectors use with trusted API sources only + + const context = getContext('programme') + const renderHtml = context.renderHtml let isActive = false diff --git a/src/components/CategoryList.svelte b/src/components/CategoryList.svelte index 8dd5b24..d6343e3 100644 --- a/src/components/CategoryList.svelte +++ b/src/components/CategoryList.svelte @@ -1,14 +1,18 @@ <script lang="ts"> - import { onMount } from 'svelte' + import { onMount, setContext } from 'svelte' import Spinner from '../common/Spinner.svelte' import { settings, fetchApi, shuffle } from '../common/Common.svelte' import CategoryCard from '../cards/CategoryCard.svelte' export let categoriesToDisplay: [] // Default: Display all categories export let shuffleCategories: boolean = false - /* Nested props */ export let urlShowList: string - export let renderHtml: boolean = false // Attention: to avoid XSS attack vectors use with trusted API sources only + export let renderHtml: boolean = false + + setContext('category', { + renderHtml: renderHtml, + urlShowList: urlShowList, + }) let categories: any[] @@ -54,7 +58,7 @@ <div class="card-container list"> {#if categories} {#each categories as category} - <CategoryCard {category} {urlShowList} {renderHtml} /> + <CategoryCard {category} /> {/each} {:else} <Spinner /> diff --git a/src/components/EpisodeDetail.svelte b/src/components/EpisodeDetail.svelte index 3b0c4c5..4dc21ed 100644 --- a/src/components/EpisodeDetail.svelte +++ b/src/components/EpisodeDetail.svelte @@ -1,16 +1,20 @@ <script> - import { onMount } from 'svelte' + import { onMount, setContext } from 'svelte' import Spinner from '../common/Spinner.svelte' import { settings, fetchApi } from '../common/Common.svelte' import EpisodeCardBig from '../cards/EpisodeCardBig.svelte' export let episodeId = undefined - /* Nested Component Props */ + export let labelPlay = undefined export let labelLinkArchive = undefined export let urlShowDetail = undefined export let embedCbaPlayer = true - export let renderHtml = false // Attention: to avoid XSS attack vectors use with trusted API sources only + export let renderHtml = false + + setContext('episode', { + renderHtml: renderHtml, + }) let episode let show @@ -64,8 +68,7 @@ {urlShowDetail} {labelPlay} {labelLinkArchive} - {embedCbaPlayer} - {renderHtml} /> + {embedCbaPlayer} /> {:else} <Spinner /> {/if} diff --git a/src/components/HostDetail.svelte b/src/components/HostDetail.svelte index 62a3597..caa2249 100644 --- a/src/components/HostDetail.svelte +++ b/src/components/HostDetail.svelte @@ -1,15 +1,22 @@ <script> - import { onMount } from 'svelte' + import { onMount, setContext } from 'svelte' import Spinner from '../common/Spinner.svelte' import { settings, fetchApi } from '../common/Common.svelte' import HostCardBig from '../cards/HostCardBig.svelte' export let hostid = undefined - /* Nested Component Props */ - export let renderHtml = false // Attention: to avoid XSS attack vectors use with trusted API sources only + + export let renderHtml = false export let labelShows = undefined export let urlShowDetail = undefined + setContext('host', { + renderHtml: renderHtml, + }) + setContext('show', { + renderHtml: renderHtml, + }) + let host let shows @@ -46,7 +53,7 @@ <div class="card-container detail"> {#if host} - <HostCardBig {host} {shows} {urlShowDetail} {labelShows} {renderHtml} /> + <HostCardBig {host} {shows} {urlShowDetail} {labelShows} /> {:else} <Spinner /> {/if} diff --git a/src/components/HostList.svelte b/src/components/HostList.svelte index f04b2e3..bfa6993 100644 --- a/src/components/HostList.svelte +++ b/src/components/HostList.svelte @@ -1,13 +1,11 @@ <script> - import { onMount } from 'svelte' + import { onMount, setContext } from 'svelte' import Spinner from '../common/Spinner.svelte' import Button, { Label } from '@smui/button' import { settings, fetchApi } from '../common/Common.svelte' import HostCardAvatar from '../cards/HostCardAvatar.svelte' // import Pagination from '../Pagination.svelte' - // export let api = 'https://prog-info.o94.at/api.php' - // export let endpoint = 'hosts' export let urlHostDetail = undefined export let page = 0 export let limit = 300 @@ -21,6 +19,10 @@ // export let pageprev = "Previous page"; // export let pagenext = "Next page"; + setContext('host', { + renderHtml: renderHtml, + }) + let hosts = [] let count let active_hosts @@ -101,11 +103,7 @@ {#if hosts && hosts.length > 0} <div class="card-container list"> {#each hosts as host} - <HostCardAvatar - {urlHostDetail} - {host} - {renderHtml} - displayOverlay={true} /> + <HostCardAvatar {urlHostDetail} {host} displayOverlay={true} /> {/each} </div> {:else} diff --git a/src/components/MediaPlayer.svelte b/src/components/MediaPlayer.svelte index 9c0b42d..a3027d1 100644 --- a/src/components/MediaPlayer.svelte +++ b/src/components/MediaPlayer.svelte @@ -1,5 +1,5 @@ <script lang="ts"> - import { onMount, onDestroy } from 'svelte' + import { onMount, onDestroy, setContext } from 'svelte' import { addTrackStore } from '../stores.js' import Spinner from '../common/Spinner.svelte' import MediaPlayerCenter from '../elements/MediaPlayerCenter.svelte' @@ -20,6 +20,10 @@ export var urlShowDetail: string = '/show-detail.html?slug=' export var urlShowList: string = '/show-list.html' + setContext('player', { + renderHtml: renderHtml, + }) + const TABS: Array<{ [id: string]: string }> = [ { id: 'now', icon: 'aura-icon-small icon-now', label: 'Now' }, { @@ -335,8 +339,7 @@ renderLinks={renderProgrammeLinks} {urlEpisodeDetail} {urlShowDetail} - {urlShowList} - {renderHtml} /> + {urlShowList} /> </div> {:else if activeTab && activeTab.id === 'playlist'} <div id="aura-media-player-tab-playlist" class="tab-content"> diff --git a/src/components/NowPlayingWidget.svelte b/src/components/NowPlayingWidget.svelte index 0092c9f..e616b56 100644 --- a/src/components/NowPlayingWidget.svelte +++ b/src/components/NowPlayingWidget.svelte @@ -16,7 +16,7 @@ </script> <script lang="ts"> - import { onMount } from 'svelte' + import { onMount, setContext } from 'svelte' import Spinner from '../common/Spinner.svelte' import NowPlayingCard from '../cards/NowPlayingCard.svelte' import { settings, continuousFetch } from '../common/Common.svelte' @@ -26,7 +26,11 @@ 'location=no,height=632,width=344,scrollbars=no,status=no' export let refreshtime: number = 60 export let prefixOnAir: string = 'LIVE' - export let renderHtml = false // Attention: to avoid XSS attack vectors use with trusted API sources only + export let renderHtml = false + + setContext('player', { + renderHtml: renderHtml, + }) let episode: { [id: string]: string } = {} @@ -61,11 +65,7 @@ <template> {#if episode} - <NowPlayingCard - {episode} - {prefixOnAir} - {renderHtml} - callbackButton={() => popup()} /> + <NowPlayingCard {episode} {prefixOnAir} callbackButton={() => popup()} /> {:else} <Spinner /> {/if} diff --git a/src/components/Player.svelte b/src/components/Player.svelte index dad3b67..b39ca5f 100644 --- a/src/components/Player.svelte +++ b/src/components/Player.svelte @@ -1,5 +1,5 @@ <script> - import { onMount, onDestroy } from 'svelte' + import { onMount, onDestroy, setContext } from 'svelte' import Spinner from '../common/Spinner.svelte' import { customUrl } from '../stores.js' import Display from '../common/Display.svelte' @@ -13,7 +13,11 @@ export let playerwindowsettings = 'location=no,height=350,width=670,scrollbars=no,status=no' export let refreshtime = '60' - export let renderHtml = false // Attention: to avoid XSS attack vectors use with trusted API sources only + export let renderHtml = false + + setContext('player', { + renderHtml: renderHtml, + }) let currentShow @@ -155,10 +159,10 @@ aria-describedby="player-description"> <div class="info-wrapper"> <span class="episode-title"> - <Display value={currentShow.name} {renderHtml} /> + <Display value={currentShow.name} /> </span> <span class="episode-info"> - <Display value={currentShow.note_title} {renderHtml} /> + <Display value={currentShow.note_title} /> </span> <span class="track-service"> <span class="current-track">{displayTrack(currentShow)}</span> diff --git a/src/components/Programme.svelte b/src/components/Programme.svelte index d188e47..7141249 100644 --- a/src/components/Programme.svelte +++ b/src/components/Programme.svelte @@ -1,5 +1,5 @@ <script> - import { onMount } from 'svelte' + import { onMount, setContext } from 'svelte' import Spinner from '../common/Spinner.svelte' import { settings, @@ -16,9 +16,9 @@ export let startActive = false export let refreshTime = '60' export let view = 'card' // 'card' or 'paper' - /* Nested Component Props */ + export let renderLinks = true - export let renderHtml = false // Attention: to avoid XSS attack vectors use with trusted API sources only + export let renderHtml = false export let displayCatTitle = false export let displayCatIcons = false export let displayLangIcons = false @@ -28,6 +28,10 @@ export let urlDefaultFallbackShow = 'https://o94.at/programm/Musiktracker_in-random-order' + setContext('programme', { + renderHtml: renderHtml, + }) + let programme /* Initialize the component */ @@ -121,8 +125,7 @@ {urlShowList} {displayCatTitle} {displayCatIcons} - {displayLangIcons} - {renderHtml} /> + {displayLangIcons} /> {/each} </div> {:else if view == 'paper'} @@ -131,8 +134,7 @@ {programme} {urlEpisodeDetail} {urlShowDetail} - {renderLinks} - {renderHtml} /> + {renderLinks} /> </div> {:else} <div class="error">Invalid programme view type.</div> diff --git a/src/components/ShowDetail.svelte b/src/components/ShowDetail.svelte index 4ba1f58..4812311 100644 --- a/src/components/ShowDetail.svelte +++ b/src/components/ShowDetail.svelte @@ -1,19 +1,16 @@ <script> - import { onMount } from 'svelte' + import { onMount, setContext } from 'svelte' import Spinner from '../common/Spinner.svelte' import { settings, fetchApi, formatApiDate } from '../common/Common.svelte' import ShowCardBig from '../cards/ShowCardBig.svelte' - // export let api = 'https://prog-info.o94.at/api.php' - // export let endpointShows = 'shows' - // export let endpointEpisodes = 'timeslots' export let showSlug = undefined export let episodeDaysFuture = 360 export let episodeDaysPast = 180 export let maxFutureEpisodes = 8 export let defaultPastEpisodes = 7 - /* Nested Component Props */ - export let renderHtml = false // Attention: to avoid XSS attack vectors use with trusted API sources only + + export let renderHtml = false export let displayCatTitle = false export let displayCatIcons = false export let displayLangIcons = false @@ -26,6 +23,16 @@ export let urlShowList = undefined export let radioEpoch = new Date('1998-08-17') + setContext('show', { + renderHtml: renderHtml, + }) + setContext('host', { + renderHtml: renderHtml, + }) + setContext('episode', { + renderHtml: renderHtml, + }) + let endpointShow = settings.api.endpoints.show let endpointEpisode = settings.api.endpoints.episode let tmp_show // FIXME Workaround for current API limitations: It's not possible to fetch everything in one request (see below) @@ -165,7 +172,6 @@ {labelEpisodes} {labelCurrentNone} {labelShowMore} - {renderHtml} {hasMoreEpisodes} {displayCatTitle} {displayCatIcons} diff --git a/src/components/ShowList.svelte b/src/components/ShowList.svelte index 523905f..9c7da13 100644 --- a/src/components/ShowList.svelte +++ b/src/components/ShowList.svelte @@ -1,5 +1,5 @@ <script> - import { onMount } from 'svelte' + import { onMount, setContext } from 'svelte' import Spinner from '../common/Spinner.svelte' import Select, { Option } from '@smui/select' import { settings, fetchApi } from '../common/Common.svelte' @@ -13,8 +13,14 @@ export let language = undefined export let labelAllShows = 'All shows' export let enableCategoryDropdown = false - /* Nested Component Props */ - export let renderHtml = false // Attention: to avoid XSS attack vectors use with trusted API sources only + export let renderHtml = false + + setContext('show', { + renderHtml: renderHtml, + }) + setContext('category', { + renderHtml: renderHtml, + }) let query let shows @@ -134,11 +140,7 @@ <template> <div class="show-overview"> {#if selectedCat} - <CategoryHeading - category={selectedCat} - {language} - {renderHtml} - headingLevel={1} /> + <CategoryHeading category={selectedCat} {language} headingLevel={1} /> {/if} {#if allCategories && enableCategoryDropdown} @@ -156,7 +158,7 @@ <div class="card-container list"> {#if shows} {#each shows as show} - <ShowCardMedium {show} {urlShowDetail} {renderHtml} /> + <ShowCardMedium {show} {urlShowDetail} /> {/each} {:else} <Spinner /> diff --git a/src/elements/CategoryHeading.svelte b/src/elements/CategoryHeading.svelte index 536d001..dcdb2cc 100644 --- a/src/elements/CategoryHeading.svelte +++ b/src/elements/CategoryHeading.svelte @@ -1,11 +1,14 @@ <script lang="ts"> + import { getContext } from 'svelte' import Display from '../common/Display.svelte' // import HeadingTag from './HeadingTag.svelte' export let category: { [name: string]: any } export let language: string = '' export let headingLevel: number = 1 - export let renderHtml: boolean + + const categoryContext: { [name: string]: any } = getContext('category') + const renderHtml: boolean = categoryContext.renderHtml </script> <style lang="scss"> -- GitLab