diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/api/lsfgApi.ts | 1 | ||||
| -rw-r--r-- | src/components/CollapsibleItemGroup.tsx | 4 | ||||
| -rw-r--r-- | src/components/GameConfigurationSelector.tsx | 6 | ||||
| -rw-r--r-- | src/utils/steamLaunchOptions.ts | 41 |
4 files changed, 42 insertions, 10 deletions
diff --git a/src/api/lsfgApi.ts b/src/api/lsfgApi.ts index 883f56e..44b72b5 100644 --- a/src/api/lsfgApi.ts +++ b/src/api/lsfgApi.ts @@ -41,6 +41,7 @@ export interface InstalledGame { appid: string; name: string; nonSteam: boolean; + isFlatpakShortcut?: boolean; } export interface GlobalConfig { diff --git a/src/components/CollapsibleItemGroup.tsx b/src/components/CollapsibleItemGroup.tsx index 29fe945..a4e4092 100644 --- a/src/components/CollapsibleItemGroup.tsx +++ b/src/components/CollapsibleItemGroup.tsx @@ -6,6 +6,7 @@ export interface CollapsibleItem { id: string; label: string; description: string; + disabled?: boolean; } export const collapsibleItemGroupStyles = ` @@ -91,7 +92,8 @@ export function CollapsibleItemGroup({ <Field label={item.label} description={item.description} - onActivate={() => onSelect(item.id)} + disabled={item.disabled} + onActivate={item.disabled ? undefined : () => onSelect(item.id)} highlightOnFocus /> </PanelSectionRow> diff --git a/src/components/GameConfigurationSelector.tsx b/src/components/GameConfigurationSelector.tsx index ad3537d..91dc3df 100644 --- a/src/components/GameConfigurationSelector.tsx +++ b/src/components/GameConfigurationSelector.tsx @@ -20,6 +20,7 @@ const ENABLED_COLLAPSED_KEY = "lsfg-enabled-games-collapsed-v4"; const AVAILABLE_COLLAPSED_KEY = "lsfg-available-games-collapsed-v3"; function targetDescription(game: GameTarget): string { + if (game.isFlatpakShortcut) return "Non-Steam | Use Flatpak Tab"; return game.source === "unknown" ? "Unknown source ยท excluded from bulk actions" : sourceLabel(game.source); @@ -43,8 +44,8 @@ export function GameConfigurationSelector({ }); const enabledGames = sortGames(targets.filter((game) => game.configured)); const availableGames = sortGames(targets.filter((game) => !game.configured)); - const enableableGames = availableGames.filter((game) => game.source === source); - const removableGames = enabledGames.filter((game) => game.source === source); + const enableableGames = availableGames.filter((game) => game.source === source && !game.isFlatpakShortcut); + const removableGames = enabledGames.filter((game) => game.source === source && !game.isFlatpakShortcut); const sourceName = source === "nonSteam" ? "non-Steam shortcuts" : "Steam games"; const emptyDescription = source === "nonSteam" ? "Steam has not reported any eligible non-Steam shortcuts" @@ -53,6 +54,7 @@ export function GameConfigurationSelector({ id: game.appid, label: game.name, description: targetDescription(game), + disabled: game.isFlatpakShortcut, }); const [enabledCollapsed, toggleEnabled] = usePersistentCollapsed(`${ENABLED_COLLAPSED_KEY}-${source}`); const [availableCollapsed, toggleAvailable] = usePersistentCollapsed(`${AVAILABLE_COLLAPSED_KEY}-${source}`); diff --git a/src/utils/steamLaunchOptions.ts b/src/utils/steamLaunchOptions.ts index f03eeab..83c46f2 100644 --- a/src/utils/steamLaunchOptions.ts +++ b/src/utils/steamLaunchOptions.ts @@ -153,7 +153,25 @@ function tokenize(options: string): LaunchToken[] { } const serialize = (tokens: readonly LaunchToken[]) => tokens.map(({ raw }) => raw).join(" "); -const commandIndex = (tokens: readonly LaunchToken[]) => tokens.findIndex((token) => token.raw.toLowerCase() === COMMAND_TOKEN); +function isCommandToken(token: LaunchToken): boolean { + return token.value.toLowerCase() === COMMAND_TOKEN; +} + +function isMalformedCommandToken(token: LaunchToken): boolean { + const value = token.value.toLowerCase(); + return value === "%command" || value === "command%"; +} + +function normalizeCommandTokens(tokens: LaunchToken[]): void { + for (const token of tokens) { + if (isCommandToken(token) || isMalformedCommandToken(token)) { + token.raw = COMMAND_TOKEN; + token.value = COMMAND_TOKEN; + } + } +} + +const commandIndex = (tokens: readonly LaunchToken[]) => tokens.findIndex(isCommandToken); const isAssignment = (token: LaunchToken) => /^[A-Za-z_][A-Za-z0-9_]*=/.test(token.value); const isLegacyToken = (value: string) => LEGACY_WRAPPER_TOKENS.has(value) || LEGACY_ABSOLUTE_WRAPPER.test(value); const isWrapperToken = (value: string, wrapperPath: string) => decodeToken(value) === wrapperPath || isLegacyWrapperToken(value); @@ -173,9 +191,9 @@ function removeMatchingWrappers(tokens: LaunchToken[], predicate: (value: string function installLaunchOption( options: string, wrapperPath = DEFAULT_WRAPPER_PATH, - shortcutLaunchOptions = false, ) { const tokens = tokenize(options); + normalizeCommandTokens(tokens); removeMatchingWrappers(tokens, isLegacyToken); let command = commandIndex(tokens); if (command >= 0) { @@ -185,11 +203,15 @@ function installLaunchOption( tokens.splice(command, 0, { raw: wrapperPath, value: wrapperPath }); return { options: serialize(tokens), commandTokenAdded: false }; } + + const existingWrapper = tokens.findIndex((token) => decodeToken(token.value) === wrapperPath); + if (existingWrapper >= 0) { + tokens.splice(existingWrapper + 1, 0, { raw: COMMAND_TOKEN, value: COMMAND_TOKEN }); + return { options: serialize(tokens), commandTokenAdded: true }; + } + let insertion = 0; while (insertion < tokens.length && isAssignment(tokens[insertion])) insertion++; - if (!shortcutLaunchOptions && insertion < tokens.length && !tokens[insertion].value.startsWith("-")) { - throw new Error("Launch options do not contain %command%; refusing to guess a launcher command"); - } tokens.splice(insertion, 0, { raw: wrapperPath, value: wrapperPath }, { raw: COMMAND_TOKEN, value: COMMAND_TOKEN }, @@ -207,6 +229,7 @@ export function removeWrapperLaunchOption( commandTokenAdded = false, ): string { const tokens = tokenize(options); + normalizeCommandTokens(tokens); if (removeMatchingWrappers(tokens, (value) => isWrapperToken(value, wrapperPath)) && commandTokenAdded) { const command = commandIndex(tokens); if (command >= 0) tokens.splice(command, 1); @@ -345,14 +368,18 @@ export function installWrapperIntegration( const current = await readSteamLaunchOptions(appId, nonSteam); const cleaned = cleanupPluginAssignments(cleanupLegacyLaunchOptions(current.options)); const alreadyInstalled = hasWrapperLaunchIntegration(current.options, wrapperPath); - const rewrite = installLaunchOption(cleaned, wrapperPath, nonSteam); + const rewrite = installLaunchOption(cleaned, wrapperPath); if (rewrite.options === current.options) return { snapshot: current, commandTokenAdded, changed: false }; const value = await writeVerified( appId, nonSteam, current.options, rewrite.options, (options) => writeOptions(appId, nonSteam, options), "Steam did not accept the launch options", ); - return { snapshot: value, commandTokenAdded: alreadyInstalled ? commandTokenAdded : rewrite.commandTokenAdded, changed: true }; + return { + snapshot: value, + commandTokenAdded: alreadyInstalled ? commandTokenAdded : commandTokenAdded || rewrite.commandTokenAdded, + changed: true, + }; }); } |
