From a3f69c384b8442c34a16519c87089de8fb669f26 Mon Sep 17 00:00:00 2001 From: GODrums Date: Fri, 5 Jun 2026 01:10:44 +0200 Subject: [PATCH 1/7] encapsulate legacy steam market logic and deprecate jquery --- src/lib/components/decorators.ts | 27 +++++++------------ src/lib/components/injectors.ts | 26 +++++++++++++----- src/lib/components/market/item_row_wrapper.ts | 7 ++++- src/lib/components/market/mode.ts | 27 +++++++++++++++++++ src/lib/components/market/utility_belt.ts | 3 ++- src/lib/page_scripts/utils.ts | 14 ++++++---- 6 files changed, 73 insertions(+), 31 deletions(-) create mode 100644 src/lib/components/market/mode.ts diff --git a/src/lib/components/decorators.ts b/src/lib/components/decorators.ts index daa48b6f..7e858a08 100644 --- a/src/lib/components/decorators.ts +++ b/src/lib/components/decorators.ts @@ -10,9 +10,7 @@ export enum ConflictingMode { CONTINUOUS, // Continuously check and hide conflicting elements } -type CSSProperties = JQuery.PlainObject< - string | number | ((this: HTMLElement, index: number, value: string) => string | number | void | undefined) ->; +type CSSProperties = Record; /** * Decorator that applies CSS to DOM elements from conflicting extensions @@ -31,23 +29,18 @@ export function StyleConflictingElement( return; } - const styleElements = () => { - $J(selector).each(function () { - $J(this).css(cssProps); - }); - }; - - const checkAndStyle = async () => { - const found = $J(selector).length > 0; - if (found) { - styleElements(); + const checkAndStyle = () => { + const elements = document.querySelectorAll(selector); + for (const el of elements) { + for (const [prop, value] of Object.entries(cssProps)) { + el.style.setProperty(prop, String(value)); + } } - return found; + return elements.length > 0; }; - const interval = setInterval(async () => { - const result = await checkAndStyle(); - if (result && mode === ConflictingMode.ONCE) { + const interval = setInterval(() => { + if (checkAndStyle() && mode === ConflictingMode.ONCE) { clearInterval(interval); } }, 250); diff --git a/src/lib/components/injectors.ts b/src/lib/components/injectors.ts index ab6cc03b..c2cafda2 100644 --- a/src/lib/components/injectors.ts +++ b/src/lib/components/injectors.ts @@ -23,6 +23,8 @@ interface InjectionConfig { op: (ctx: JQuery, target: typeof FloatElement) => void; } +type InjectionGuard = () => boolean; + const InjectionConfigs: {[key in InjectionType]: InjectionConfig} = { [InjectionType.Append]: { exists: (ctx, selector) => !!ctx.children(selector).length, @@ -53,11 +55,17 @@ export function CustomElement(): any { }; } -function Inject(selector: string, mode: InjectionMode, type: InjectionType): any { +const canInject = (guard?: InjectionGuard) => (guard ? guard() : true) + +function Inject(selector: string, mode: InjectionMode, type: InjectionType, guard?: InjectionGuard): any { return function (target: typeof FloatElement, propertyKey: string, descriptor: PropertyDescriptor) { if (!inPageContext()) { return; } + if (!canInject(guard)) { + return; + } + switch (mode) { case InjectionMode.ONCE: $J(selector).each(function () { @@ -66,6 +74,10 @@ function Inject(selector: string, mode: InjectionMode, type: InjectionType): any break; case InjectionMode.CONTINUOUS: setInterval(() => { + if (!canInject(guard)) { + return; + } + $J(selector).each(function () { // Don't add the item again if we already have if (InjectionConfigs[type].exists($J(this), target.tag())) return; @@ -78,14 +90,14 @@ function Inject(selector: string, mode: InjectionMode, type: InjectionType): any }; } -export function InjectAppend(selector: string, mode: InjectionMode = InjectionMode.ONCE): any { - return Inject(selector, mode, InjectionType.Append); +export function InjectAppend(selector: string, mode: InjectionMode = InjectionMode.ONCE, guard?: InjectionGuard): any { + return Inject(selector, mode, InjectionType.Append, guard); } -export function InjectBefore(selector: string, mode: InjectionMode = InjectionMode.ONCE): any { - return Inject(selector, mode, InjectionType.Before); +export function InjectBefore(selector: string, mode: InjectionMode = InjectionMode.ONCE, guard?: InjectionGuard): any { + return Inject(selector, mode, InjectionType.Before, guard); } -export function InjectAfter(selector: string, mode: InjectionMode = InjectionMode.ONCE): any { - return Inject(selector, mode, InjectionType.After); +export function InjectAfter(selector: string, mode: InjectionMode = InjectionMode.ONCE, guard?: InjectionGuard): any { + return Inject(selector, mode, InjectionType.After, guard); } diff --git a/src/lib/components/market/item_row_wrapper.ts b/src/lib/components/market/item_row_wrapper.ts index da8f6bef..c78a87aa 100644 --- a/src/lib/components/market/item_row_wrapper.ts +++ b/src/lib/components/market/item_row_wrapper.ts @@ -28,9 +28,14 @@ import './sticker_display'; import {FetchBluegem, FetchBluegemResponse} from '../../bridge/handlers/fetch_bluegem'; import {ClientSend} from '../../bridge/client'; import {ConflictingExtension, ConflictingMode, HideConflictingElement, StyleConflictingElement} from '../decorators'; +import {isLegacySteamMarket} from './mode'; @CustomElement() -@InjectAppend('#searchResultsRows .market_listing_row .market_listing_item_name_block', InjectionMode.CONTINUOUS) +@InjectAppend( + '#searchResultsRows .market_listing_row .market_listing_item_name_block', + InjectionMode.CONTINUOUS, + isLegacySteamMarket +) @HideConflictingElement( ConflictingExtension.CS2_TRADER, '#searchResultsRows .market_listing_row .stickerHolderMarket, #searchResultsRows .market_listing_row .stickersTotal, #searchResultsRows .market_listing_row .floatBarMarket', diff --git a/src/lib/components/market/mode.ts b/src/lib/components/market/mode.ts new file mode 100644 index 00000000..65549185 --- /dev/null +++ b/src/lib/components/market/mode.ts @@ -0,0 +1,27 @@ +export enum SteamMarketMode { + REACT = 'react', + LEGACY = 'legacy', +} + +/** Determine if the Steam Market Beta or the legacy version is being used */ +export function getSteamMarketMode(): SteamMarketMode { + if ( + typeof $J === 'function' && + typeof g_rgListingInfo === 'object' && + g_rgListingInfo !== null && + typeof g_rgAssets === 'object' && + g_rgAssets !== null + ) { + return SteamMarketMode.LEGACY; + } + + return SteamMarketMode.REACT; +} + +export function isSteamMarketMode(mode: SteamMarketMode): boolean { + return getSteamMarketMode() === mode; +} + +export function isLegacySteamMarket(): boolean { + return isSteamMarketMode(SteamMarketMode.LEGACY); +} diff --git a/src/lib/components/market/utility_belt.ts b/src/lib/components/market/utility_belt.ts index 4fe38b83..e316709f 100644 --- a/src/lib/components/market/utility_belt.ts +++ b/src/lib/components/market/utility_belt.ts @@ -9,9 +9,10 @@ import '../filter/filter_container'; import {Observe} from '../../utils/observers'; import {isBuggedSkin} from '../../utils/skin'; import {AppId, ContextId} from '../../types/steam_constants'; +import {isLegacySteamMarket} from './mode'; @CustomElement() -@InjectBefore('#searchResultsRows', InjectionMode.ONCE) +@InjectBefore('#searchResultsRows', InjectionMode.ONCE, isLegacySteamMarket) export class UtilityBelt extends FloatElement { @state() private buggedSkinCount = 0; diff --git a/src/lib/page_scripts/utils.ts b/src/lib/page_scripts/utils.ts index 7c9b7630..0068c839 100644 --- a/src/lib/page_scripts/utils.ts +++ b/src/lib/page_scripts/utils.ts @@ -5,6 +5,7 @@ import {ExecuteCssOnPage} from '../bridge/handlers/execute_css'; import {FetchExtensionFile} from '../bridge/handlers/fetch_extension_file'; import {isFirefox} from '../utils/detect'; import {g_PostMessageBus} from '../bus/post_message_bus'; +import {isLegacySteamMarket} from '../components/market/mode'; async function initiateChromium(scriptPath: string) { ClientSend(ExecuteCssOnPage, { @@ -63,11 +64,14 @@ export async function init(scriptPath: string, ifPage: () => any) { // @ts-ignore Deprecated name window.csgofloat = true; - // Add Roboto font in the page context - const fontLink = document.createElement('link'); - fontLink.href = 'https://fonts.googleapis.com/css2?family=Roboto:wght@500;700&display=swap'; - fontLink.rel = 'stylesheet'; - document.head.appendChild(fontLink); + // React sets default-src 'self' in the CSP, so we can't load external fonts in the beta + if (isLegacySteamMarket()) { + // Add Roboto font in the page context + const fontLink = document.createElement('link'); + fontLink.href = 'https://fonts.googleapis.com/css2?family=Roboto:wght@500;700&display=swap'; + fontLink.rel = 'stylesheet'; + document.head.appendChild(fontLink); + } ifPage(); return; From b02c63f48e0ce28f1d294d712f4edca26004df75 Mon Sep 17 00:00:00 2001 From: GODrums Date: Fri, 5 Jun 2026 01:19:13 +0200 Subject: [PATCH 2/7] chore: run format --- src/lib/components/injectors.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/components/injectors.ts b/src/lib/components/injectors.ts index c2cafda2..2d2a0dee 100644 --- a/src/lib/components/injectors.ts +++ b/src/lib/components/injectors.ts @@ -55,7 +55,7 @@ export function CustomElement(): any { }; } -const canInject = (guard?: InjectionGuard) => (guard ? guard() : true) +const canInject = (guard?: InjectionGuard) => (guard ? guard() : true); function Inject(selector: string, mode: InjectionMode, type: InjectionType, guard?: InjectionGuard): any { return function (target: typeof FloatElement, propertyKey: string, descriptor: PropertyDescriptor) { From b34a34d25884d00b5361bc080bbfb32a2835381d Mon Sep 17 00:00:00 2001 From: GODrums Date: Fri, 5 Jun 2026 02:23:26 +0200 Subject: [PATCH 3/7] replace jquery in injectors --- src/lib/components/injectors.ts | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/lib/components/injectors.ts b/src/lib/components/injectors.ts index 2d2a0dee..4de620cc 100644 --- a/src/lib/components/injectors.ts +++ b/src/lib/components/injectors.ts @@ -19,24 +19,24 @@ enum InjectionType { } interface InjectionConfig { - exists: (ctx: JQuery, selector: string) => boolean; - op: (ctx: JQuery, target: typeof FloatElement) => void; + exists: (ctx: HTMLElement, selector: string) => boolean; + op: (ctx: HTMLElement, target: typeof FloatElement) => void; } type InjectionGuard = () => boolean; const InjectionConfigs: {[key in InjectionType]: InjectionConfig} = { [InjectionType.Append]: { - exists: (ctx, selector) => !!ctx.children(selector).length, - op: (ctx, target) => ctx.append(target.elem()), + exists: (anchor, selector) => anchor.lastElementChild?.matches(selector) ?? false, + op: (anchor, target) => anchor.appendChild(target.elem()), }, [InjectionType.Before]: { - exists: (ctx, selector) => !!ctx.parent().children(selector).length, - op: (ctx, target) => ctx.before(target.elem()), + exists: (anchor, selector) => anchor.previousElementSibling?.matches(selector) ?? false, + op: (anchor, target) => anchor.parentElement?.insertBefore(target.elem(), anchor), }, [InjectionType.After]: { - exists: (ctx, selector) => !!ctx.parent().children(selector).length, - op: (ctx, target) => ctx.after(target.elem()), + exists: (anchor, selector) => anchor.nextElementSibling?.matches(selector) ?? false, + op: (anchor, target) => anchor.parentElement?.insertBefore(target.elem(), anchor.nextSibling), }, }; @@ -68,8 +68,8 @@ function Inject(selector: string, mode: InjectionMode, type: InjectionType, guar switch (mode) { case InjectionMode.ONCE: - $J(selector).each(function () { - InjectionConfigs[type].op($J(this), target); + document.querySelectorAll(selector).forEach((el) => { + InjectionConfigs[type].op(el, target); }); break; case InjectionMode.CONTINUOUS: @@ -78,11 +78,11 @@ function Inject(selector: string, mode: InjectionMode, type: InjectionType, guar return; } - $J(selector).each(function () { + document.querySelectorAll(selector).forEach((el) => { // Don't add the item again if we already have - if (InjectionConfigs[type].exists($J(this), target.tag())) return; + if (InjectionConfigs[type].exists(el, target.tag())) return; - InjectionConfigs[type].op($J(this), target); + InjectionConfigs[type].op(el, target); }); }, 250); break; From 09fb8dfc196fd5f1172eb0e7b2506ca1e30f512b Mon Sep 17 00:00:00 2001 From: GODrums Date: Fri, 5 Jun 2026 19:28:39 +0200 Subject: [PATCH 4/7] fix: match any sibling in detection --- src/lib/components/injectors.ts | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/lib/components/injectors.ts b/src/lib/components/injectors.ts index 4de620cc..5c7623c1 100644 --- a/src/lib/components/injectors.ts +++ b/src/lib/components/injectors.ts @@ -27,19 +27,31 @@ type InjectionGuard = () => boolean; const InjectionConfigs: {[key in InjectionType]: InjectionConfig} = { [InjectionType.Append]: { - exists: (anchor, selector) => anchor.lastElementChild?.matches(selector) ?? false, + exists: (anchor, selector) => Array.from(anchor.children).some((child) => child.matches(selector)), op: (anchor, target) => anchor.appendChild(target.elem()), }, [InjectionType.Before]: { - exists: (anchor, selector) => anchor.previousElementSibling?.matches(selector) ?? false, + exists: (anchor, selector) => hasSiblingMatching(anchor, 'previousElementSibling', selector), op: (anchor, target) => anchor.parentElement?.insertBefore(target.elem(), anchor), }, [InjectionType.After]: { - exists: (anchor, selector) => anchor.nextElementSibling?.matches(selector) ?? false, + exists: (anchor, selector) => hasSiblingMatching(anchor, 'nextElementSibling', selector), op: (anchor, target) => anchor.parentElement?.insertBefore(target.elem(), anchor.nextSibling), }, }; +/** Checks if any sibling of `anchor` in the given direction matches the selector. */ +function hasSiblingMatching( + anchor: HTMLElement, + direction: keyof Pick, + selector: string +): boolean { + for (let el = anchor[direction]; el; el = el[direction]) { + if (el.matches(selector)) return true; + } + return false; +} + export function CustomElement(): any { return function (target: typeof FloatElement, propertyKey: string, descriptor: PropertyDescriptor) { if (!inPageContext()) { From 5a5982008b68e8c64c4a6227d6a65d7d3d20c03e Mon Sep 17 00:00:00 2001 From: GODrums Date: Fri, 5 Jun 2026 20:52:08 +0200 Subject: [PATCH 5/7] remove robot font completely --- .../inventory/list_item_modal_styles.ts | 1 - src/lib/components/market/mode.ts | 20 ++++++------------- src/lib/page_scripts/utils.ts | 10 ---------- 3 files changed, 6 insertions(+), 25 deletions(-) diff --git a/src/lib/components/inventory/list_item_modal_styles.ts b/src/lib/components/inventory/list_item_modal_styles.ts index 7d804835..2bdfe7ed 100644 --- a/src/lib/components/inventory/list_item_modal_styles.ts +++ b/src/lib/components/inventory/list_item_modal_styles.ts @@ -96,7 +96,6 @@ export const listItemModalStyles = [ padding: 20px; width: 500px; max-width: 90%; - font-family: Roboto, 'Helvetica Neue', sans-serif; border-width: 2px; border-style: solid; border-color: rgba(193, 206, 255, 0.07); diff --git a/src/lib/components/market/mode.ts b/src/lib/components/market/mode.ts index 65549185..0a8887d1 100644 --- a/src/lib/components/market/mode.ts +++ b/src/lib/components/market/mode.ts @@ -3,25 +3,17 @@ export enum SteamMarketMode { LEGACY = 'legacy', } -/** Determine if the Steam Market Beta or the legacy version is being used */ -export function getSteamMarketMode(): SteamMarketMode { - if ( +export function isLegacySteamMarket(): boolean { + return ( typeof $J === 'function' && typeof g_rgListingInfo === 'object' && g_rgListingInfo !== null && typeof g_rgAssets === 'object' && g_rgAssets !== null - ) { - return SteamMarketMode.LEGACY; - } - - return SteamMarketMode.REACT; + ); } -export function isSteamMarketMode(mode: SteamMarketMode): boolean { - return getSteamMarketMode() === mode; -} - -export function isLegacySteamMarket(): boolean { - return isSteamMarketMode(SteamMarketMode.LEGACY); +/** True only if current page is part of the Steam Market AND the beta is being used */ +export function isReactSteamMarket(): boolean { + return (window as any).SSR?.reactRoot !== undefined; } diff --git a/src/lib/page_scripts/utils.ts b/src/lib/page_scripts/utils.ts index 0068c839..1c1a1280 100644 --- a/src/lib/page_scripts/utils.ts +++ b/src/lib/page_scripts/utils.ts @@ -5,7 +5,6 @@ import {ExecuteCssOnPage} from '../bridge/handlers/execute_css'; import {FetchExtensionFile} from '../bridge/handlers/fetch_extension_file'; import {isFirefox} from '../utils/detect'; import {g_PostMessageBus} from '../bus/post_message_bus'; -import {isLegacySteamMarket} from '../components/market/mode'; async function initiateChromium(scriptPath: string) { ClientSend(ExecuteCssOnPage, { @@ -64,15 +63,6 @@ export async function init(scriptPath: string, ifPage: () => any) { // @ts-ignore Deprecated name window.csgofloat = true; - // React sets default-src 'self' in the CSP, so we can't load external fonts in the beta - if (isLegacySteamMarket()) { - // Add Roboto font in the page context - const fontLink = document.createElement('link'); - fontLink.href = 'https://fonts.googleapis.com/css2?family=Roboto:wght@500;700&display=swap'; - fontLink.rel = 'stylesheet'; - document.head.appendChild(fontLink); - } - ifPage(); return; } From 3712e16674003f9e6b6d487f12d932579df89072 Mon Sep 17 00:00:00 2001 From: GODrums Date: Fri, 5 Jun 2026 21:16:09 +0200 Subject: [PATCH 6/7] remove unused mode --- src/lib/components/market/mode.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/lib/components/market/mode.ts b/src/lib/components/market/mode.ts index 0a8887d1..02775183 100644 --- a/src/lib/components/market/mode.ts +++ b/src/lib/components/market/mode.ts @@ -1,8 +1,3 @@ -export enum SteamMarketMode { - REACT = 'react', - LEGACY = 'legacy', -} - export function isLegacySteamMarket(): boolean { return ( typeof $J === 'function' && From 77fa8cb46e1b995001c27008553cbc31db71561b Mon Sep 17 00:00:00 2001 From: GODrums Date: Wed, 10 Jun 2026 00:42:25 +0200 Subject: [PATCH 7/7] use native before/after --- src/lib/components/injectors.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/components/injectors.ts b/src/lib/components/injectors.ts index 5c7623c1..80f16320 100644 --- a/src/lib/components/injectors.ts +++ b/src/lib/components/injectors.ts @@ -32,11 +32,11 @@ const InjectionConfigs: {[key in InjectionType]: InjectionConfig} = { }, [InjectionType.Before]: { exists: (anchor, selector) => hasSiblingMatching(anchor, 'previousElementSibling', selector), - op: (anchor, target) => anchor.parentElement?.insertBefore(target.elem(), anchor), + op: (anchor, target) => anchor.before(target.elem()), }, [InjectionType.After]: { exists: (anchor, selector) => hasSiblingMatching(anchor, 'nextElementSibling', selector), - op: (anchor, target) => anchor.parentElement?.insertBefore(target.elem(), anchor.nextSibling), + op: (anchor, target) => anchor.after(target.elem()), }, };