mirror of
https://github.com/k1tbyte/Wand-Enhancer.git
synced 2026-08-28 17:01:04 +00:00
13759b1db6
- reduce ASAR IO overhead with streamed archive reads, buffered copies, faster relative-path handling and placeholder integrity records - fix in-place app.asar.unpacked packing/extraction self-copy cases that caused locked-file failures - tighten JS patch discovery with candidate bundle filters and search hints - require prebuilt remote-panel dist artifacts and clean up embedded bridge/script packaging - add unified build entrypoints for PowerShell, cmd and bash and move native CMake output under .tmp - add release metadata validation, changelog section extraction, pre-commit hook and GitHub Actions validation/release pipelines - make CHANGELOG the source of truth for release notes and document the tag-driven release flow - add updater release notes UI with latest/full changelog loading and localize the new update strings - modularize bridge renderer scripts, add installed apps and game status sync, and support remote launch/stop commands - centralize bridge protocol, IPC and WebSocket constants and improve LAN IP selection for QR pairing - refactor remote panel controls/state enums, persist accent color, polish library/session UI and refresh assets
256 lines
6.2 KiB
JavaScript
256 lines
6.2 KiB
JavaScript
import {
|
|
DATA_IMAGE_URL_PREFIX,
|
|
IMAGE_FIELD_NAMES,
|
|
MAX_IMAGE_URL_SEARCH_DEPTH,
|
|
PROTOCOL_RELATIVE_IMAGE_URL_PATTERN,
|
|
REMOTE_IMAGE_URL_PATTERN,
|
|
SIDEBAR_GAME_ROW_CONTAINER_SELECTOR,
|
|
SIDEBAR_GAME_ROW_IMAGE_SELECTOR,
|
|
SIDEBAR_GAME_ROW_MORE_SELECTOR,
|
|
SIDEBAR_GAME_ROW_TITLE_SELECTOR,
|
|
SIDEBAR_GAME_ROW_TOOLTIP_ID_PATTERN,
|
|
STEAM_APP_ID_FIELD_NAMES,
|
|
STEAM_APP_ID_PATTERN,
|
|
STEAM_CONTAINER_FIELD_PATTERN,
|
|
STEAM_CONTAINER_ID_FIELD_NAMES,
|
|
STEAM_PLATFORM,
|
|
WEMOD_STEAM_COMMUNITY_CDN_BASE_URL,
|
|
} from "./constants.js"
|
|
import { isRecord, safeString, toStringId } from "./runtime.js"
|
|
|
|
export function pickImageUrl(...values) {
|
|
for (const value of values) {
|
|
const imageUrl = normalizeImageUrl(value)
|
|
if (imageUrl) {
|
|
return imageUrl
|
|
}
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
export function normalizeImageUrl(value, depth = 0) {
|
|
if (typeof value === "string") {
|
|
const trimmed = value.trim()
|
|
if (
|
|
REMOTE_IMAGE_URL_PATTERN.test(trimmed) ||
|
|
trimmed.startsWith(DATA_IMAGE_URL_PREFIX)
|
|
) {
|
|
return trimmed
|
|
}
|
|
|
|
if (PROTOCOL_RELATIVE_IMAGE_URL_PATTERN.test(trimmed)) {
|
|
return `https:${trimmed}`
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
if (depth > MAX_IMAGE_URL_SEARCH_DEPTH || !value) {
|
|
return null
|
|
}
|
|
|
|
if (Array.isArray(value)) {
|
|
for (const entry of value) {
|
|
const imageUrl = normalizeImageUrl(entry, depth + 1)
|
|
if (imageUrl) {
|
|
return imageUrl
|
|
}
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
if (!isRecord(value)) {
|
|
return null
|
|
}
|
|
|
|
for (const key of IMAGE_FIELD_NAMES) {
|
|
const imageUrl = normalizeImageUrl(value[key], depth + 1)
|
|
if (imageUrl) {
|
|
return imageUrl
|
|
}
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
export function getSteamClientIconUrl(...values) {
|
|
for (const value of values) {
|
|
const steamAppId = toStringId(value)
|
|
if (steamAppId && STEAM_APP_ID_PATTERN.test(steamAppId)) {
|
|
return `${WEMOD_STEAM_COMMUNITY_CDN_BASE_URL}/${steamAppId}/client_icon/96.webp`
|
|
}
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
export function getSidebarGameRowClientIcons() {
|
|
const byTitleId = new Map()
|
|
const byTitleName = new Map()
|
|
if (typeof document === "undefined") {
|
|
return { byTitleId, byTitleName }
|
|
}
|
|
|
|
const containers = document.querySelectorAll(
|
|
SIDEBAR_GAME_ROW_CONTAINER_SELECTOR
|
|
)
|
|
for (const container of containers) {
|
|
const imageElement = container.querySelector(
|
|
SIDEBAR_GAME_ROW_IMAGE_SELECTOR
|
|
)
|
|
const titleElement = container.querySelector(
|
|
SIDEBAR_GAME_ROW_TITLE_SELECTOR
|
|
)
|
|
const moreButton = container.querySelector(SIDEBAR_GAME_ROW_MORE_SELECTOR)
|
|
if (!imageElement || !titleElement) {
|
|
continue
|
|
}
|
|
|
|
const backgroundImageUrl = getCssBackgroundImageUrl(
|
|
safeString(imageElement.style?.backgroundImage) ||
|
|
getComputedStyle(imageElement).backgroundImage
|
|
)
|
|
if (!backgroundImageUrl) {
|
|
continue
|
|
}
|
|
|
|
const tooltipId = safeString(
|
|
moreButton?.getAttribute?.("data-tooltip-trigger-for")
|
|
)
|
|
const titleId =
|
|
tooltipId.match(SIDEBAR_GAME_ROW_TOOLTIP_ID_PATTERN)?.[1] ?? null
|
|
const titleName = normalizeTitleMatchKey(titleElement.textContent)
|
|
|
|
if (titleId) {
|
|
byTitleId.set(titleId, backgroundImageUrl)
|
|
}
|
|
|
|
if (titleName) {
|
|
byTitleName.set(titleName, backgroundImageUrl)
|
|
}
|
|
}
|
|
|
|
return { byTitleId, byTitleName }
|
|
}
|
|
|
|
export function getSidebarGameRowClientIconUrl(
|
|
sidebarIcons,
|
|
titleId,
|
|
...names
|
|
) {
|
|
const normalizedTitleId = toStringId(titleId)
|
|
if (normalizedTitleId && sidebarIcons.byTitleId.has(normalizedTitleId)) {
|
|
return sidebarIcons.byTitleId.get(normalizedTitleId) ?? null
|
|
}
|
|
|
|
for (const name of names) {
|
|
const normalizedName = normalizeTitleMatchKey(name)
|
|
if (normalizedName && sidebarIcons.byTitleName.has(normalizedName)) {
|
|
return sidebarIcons.byTitleName.get(normalizedName) ?? null
|
|
}
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
export function findSteamAppId(...roots) {
|
|
const seen = new Set()
|
|
const queue = roots.map((value) => ({ value, depth: 0, steamContext: false }))
|
|
|
|
while (queue.length > 0) {
|
|
const current = queue.shift()
|
|
const value = current?.value
|
|
const depth = current?.depth ?? 0
|
|
const steamContext = current?.steamContext ?? false
|
|
|
|
if (!value || depth > MAX_IMAGE_URL_SEARCH_DEPTH || seen.has(value)) {
|
|
continue
|
|
}
|
|
|
|
if (typeof value === "string" || typeof value === "number") {
|
|
const steamAppId = steamContext ? toStringId(value) : null
|
|
if (steamAppId && STEAM_APP_ID_PATTERN.test(steamAppId)) {
|
|
return steamAppId
|
|
}
|
|
continue
|
|
}
|
|
|
|
seen.add(value)
|
|
|
|
if (Array.isArray(value)) {
|
|
for (const entry of value) {
|
|
queue.push({ value: entry, depth: depth + 1, steamContext })
|
|
}
|
|
continue
|
|
}
|
|
|
|
if (!isRecord(value)) {
|
|
continue
|
|
}
|
|
|
|
for (const [key, entry] of Object.entries(value)) {
|
|
const steamAppId = getSteamAppIdFromEntry(key, entry, steamContext)
|
|
if (steamAppId) {
|
|
return steamAppId
|
|
}
|
|
|
|
if (isRecord(entry) || Array.isArray(entry)) {
|
|
queue.push({
|
|
value: entry,
|
|
depth: depth + 1,
|
|
steamContext: steamContext || STEAM_CONTAINER_FIELD_PATTERN.test(key),
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
export function getInstalledAppSteamAppId(platform, sku) {
|
|
if (safeString(platform).toLowerCase() !== STEAM_PLATFORM) {
|
|
return null
|
|
}
|
|
|
|
return sku
|
|
}
|
|
|
|
function normalizeTitleMatchKey(value) {
|
|
const normalized = safeString(value).trim().toLowerCase().replace(/\s+/g, " ")
|
|
return normalized || null
|
|
}
|
|
|
|
function getCssBackgroundImageUrl(value) {
|
|
const backgroundImage = safeString(value)
|
|
if (!backgroundImage || backgroundImage === "none") {
|
|
return null
|
|
}
|
|
|
|
const match = backgroundImage.match(/url\((['"]?)(.*?)\1\)/i)
|
|
if (!match?.[2]) {
|
|
return null
|
|
}
|
|
|
|
return normalizeImageUrl(match[2])
|
|
}
|
|
|
|
function getSteamAppIdFromEntry(key, entry, steamContext) {
|
|
if (STEAM_APP_ID_FIELD_NAMES.has(key)) {
|
|
const steamAppId = toStringId(entry)
|
|
if (steamAppId && STEAM_APP_ID_PATTERN.test(steamAppId)) {
|
|
return steamAppId
|
|
}
|
|
}
|
|
|
|
if (steamContext && STEAM_CONTAINER_ID_FIELD_NAMES.has(key)) {
|
|
const steamAppId = toStringId(entry)
|
|
if (steamAppId && STEAM_APP_ID_PATTERN.test(steamAppId)) {
|
|
return steamAppId
|
|
}
|
|
}
|
|
|
|
return null
|
|
}
|