diff --git a/assets/app/css/darwinTitlebar.css b/assets/app/css/darwinTitlebar.css index a9aeb11d..f3555554 100644 --- a/assets/app/css/darwinTitlebar.css +++ b/assets/app/css/darwinTitlebar.css @@ -13,7 +13,7 @@ display: block; line-height: 45px; left: 0; - transform: translate(-82px, 4px); + transform: translate(-82px, 0px); } [legcord-platform="darwin"] #window-controls-container:hover #minimize #minimize-icon, @@ -36,7 +36,7 @@ no-repeat 50% 50%; mask: url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTIiIGhlaWdodD0iMTIiIHZpZXdCb3g9IjAgMCAxMiAxMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNNS4yOTI4OSA2TDIuODE4MDEgMy41MjUxM0wzLjUyNTEyIDIuODE4MDJMNS45OTk5OSA1LjI5Mjg5TDguNDc0ODcgMi44MTgwMkw5LjE4MTk3IDMuNTI1MTNMNi43MDcxIDZMOS4xODE5NyA4LjQ3NDg3TDguNDc0ODcgOS4xODE5OEw1Ljk5OTk5IDYuNzA3MTFMMy41MjUxMiA5LjE4MTk4TDIuODE4MDEgOC40NzQ4N0w1LjI5Mjg5IDZaIiBmaWxsPSJyZ2JhKDEyOCwgNiwgMCwgMSkiLz48L3N2Zz4=") no-repeat 50% 50%; - transform: translate(-0.3px, -9px); + transform: translate(-0.4px, -7px); } [legcord-platform="darwin"]:not([unFocused]) #window-controls-container #minimize #minimize-icon { background-color: #7d631b; @@ -44,7 +44,7 @@ no-repeat 50% 50%; mask: url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTIiIGhlaWdodD0iMTIiIHZpZXdCb3g9IjAgMCAxMiAxMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNMTAgNS4zOTk5OUgyVjYuNTk5OTlIMTBWNS4zOTk5OVoiIGZpbGw9IiM5ODY4MDEiLz48L3N2Zz4=") no-repeat 50% 50%; - transform: translate(-0px, -9px); + transform: translate(-0px, -7px); } [legcord-platform="darwin"]:not([unFocused]) #window-controls-container #maximize #maximize-icon { background-color: #1d7525; @@ -52,7 +52,7 @@ no-repeat 50% 50%; mask: url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTIiIGhlaWdodD0iMTIiIHZpZXdCb3g9IjAgMCAxMiAxMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNOC41ODgyMyA2Ljk5MDE1TDUuMDA5NzkgMy40MTE3QzQuODU1ODMgMy4yNTc3NCA0Ljk1ODYgMi45OTQyMiA1LjE3NjE0IDIuOTg1MTRMOC45MTA0MiAyLjgyOTMxQzkuMDU2NTggMi44MjMyMSA5LjE3NjczIDIuOTQzMzUgOS4xNzA2MyAzLjA4OTUyTDkuMDE0NzkgNi44MjM4QzkuMDA1NzEgNy4wNDEzNCA4Ljc0MjE5IDcuMTQ0MTEgOC41ODgyMyA2Ljk5MDE1WiIgZmlsbD0iIzEyNUUxRSIvPjxwYXRoIGQ9Ik0zLjQxMTc3IDUuMDA5ODJMNi45OTAyMSA4LjU4ODI3QzcuMTQ0MTcgOC43NDIyMyA3LjA0MTQgOS4wMDU3NSA2LjgyMzg2IDkuMDE0ODNMMy4wODk1OCA5LjE3MDY2QzIuOTQzNDIgOS4xNzY3NiAyLjgyMzI3IDkuMDU2NjEgMi44MjkzNyA4LjkxMDQ1TDIuOTg1MjEgNS4xNzYxN0MyLjk5NDI5IDQuOTU4NjMgMy4yNTc4MSA0Ljg1NTg2IDMuNDExNzcgNS4wMDk4MloiIGZpbGw9IiMxMjVFMUUiLz48L3N2Zz4=") no-repeat 50% 50%; - transform: translate(0.1px, -9px); + transform: translate(0px, -7px); } [legcord-platform="darwin"] #window-controls-container #minimize { diff --git a/assets/app/css/discord.css b/assets/app/css/discord.css index 17126776..7c17ba25 100644 --- a/assets/app/css/discord.css +++ b/assets/app/css/discord.css @@ -10,7 +10,7 @@ color: var(--text-muted); } -div[class^="listItem__"]:has([data-list-item-id="guildsnav___app-download-button"]) { +div:has(> span [data-list-item-id="guildsnav___app-download-button"]) { display: none !important; } diff --git a/assets/app/css/linuxTitlebar.css b/assets/app/css/linuxTitlebar.css index 2d2a3699..7935faa0 100644 --- a/assets/app/css/linuxTitlebar.css +++ b/assets/app/css/linuxTitlebar.css @@ -1,4 +1,4 @@ -[legcord-platform="linux"] div[class^="trailing_"] { +[legcord-platform="linux"] div[class*="title"] + div[class*="trailing"] { margin-right: 150px; } diff --git a/assets/app/css/winTitlebar.css b/assets/app/css/winTitlebar.css index 793a6ed5..1586911c 100644 --- a/assets/app/css/winTitlebar.css +++ b/assets/app/css/winTitlebar.css @@ -1,6 +1,6 @@ /* Legcord on Windows */ -[legcord-platform="win32"] div[class^="trailing_"] { +[legcord-platform="win32"] div[class*="title"] + div[class*="trailing"] { margin-right: 150px; } diff --git a/biome.jsonc b/biome.jsonc index 21a09f55..da9bc498 100644 --- a/biome.jsonc +++ b/biome.jsonc @@ -27,6 +27,9 @@ "style": { "noNonNullAssertion": "off", "noUselessElse": "off" // NOTE - This rule seems broken + }, + "a11y": { + "useKeyWithClickEvents": "off" } } } diff --git a/package.json b/package.json index 5666351b..f8ad0f5d 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "@uwu/lune": "^1.5.1", "@uwu/shelter-defs": "^1.4.1", "babel-preset-solid": "^1.9.3", - "electron": "39.2.6", + "electron": "39.2.7", "electron-builder": "^26.3.5", "lucide-solid": "^0.475.0", "rolldown": "1.0.0-beta.31", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9e14e27e..53dac3a9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,10 +23,6 @@ importers: ws: specifier: ^8.18.0 version: 8.18.0 - optionalDependencies: - '@vencord/venmic': - specifier: ^6.1.0 - version: 6.1.0 devDependencies: '@babel/preset-flow': specifier: ^7.25.9 @@ -62,8 +58,8 @@ importers: specifier: ^1.9.3 version: 1.9.3(@babel/core@7.26.7) electron: - specifier: 39.2.6 - version: 39.2.6 + specifier: 39.2.7 + version: 39.2.7 electron-builder: specifier: ^26.3.5 version: 26.3.5(electron-builder-squirrel-windows@25.1.8) @@ -91,6 +87,10 @@ importers: xml-formatter: specifier: ^3.6.6 version: 3.6.6 + optionalDependencies: + '@vencord/venmic': + specifier: ^6.1.0 + version: 6.1.0 packages: @@ -1755,8 +1755,8 @@ packages: electron-updater@6.6.2: resolution: {integrity: sha512-Cr4GDOkbAUqRHP5/oeOmH/L2Bn6+FQPxVLZtPbcmKZC63a1F3uu5EefYOssgZXG3u/zBlubbJ5PJdITdMVggbw==} - electron@39.2.6: - resolution: {integrity: sha512-dHBgTodWBZd+tL1Dt0PSh/CFLHeDkFCTKCTXu1dgPhlE9Z3k2zzlBQ9B2oW55CFsKanBDHiUomHJNw0XaSdQpA==} + electron@39.2.7: + resolution: {integrity: sha512-KU0uFS6LSTh4aOIC3miolcbizOFP7N1M46VTYVfqIgFiuA2ilfNaOHLDS9tCMvwwHRowAsvqBrh9NgMXcTOHCQ==} engines: {node: '>= 12.20.55'} hasBin: true @@ -3580,7 +3580,7 @@ snapshots: node-gyp: 9.4.1 ora: 5.4.1 read-binary-file-arch: 1.0.6 - semver: 7.7.2 + semver: 7.7.3 tar: 6.2.1 yargs: 17.7.2 transitivePeerDependencies: @@ -3935,7 +3935,7 @@ snapshots: '@npmcli/fs@2.1.2': dependencies: '@gar/promisify': 1.1.3 - semver: 7.7.2 + semver: 7.7.3 '@npmcli/fs@4.0.0': dependencies: @@ -4400,7 +4400,7 @@ snapshots: minimatch: 10.0.3 resedit: 1.7.2 sanitize-filename: 1.6.3 - semver: 7.7.2 + semver: 7.7.3 tar: 6.2.1 temp-file: 3.4.0 transitivePeerDependencies: @@ -5024,7 +5024,7 @@ snapshots: transitivePeerDependencies: - supports-color - electron@39.2.6: + electron@39.2.7: dependencies: '@electron/get': 2.0.3 '@types/node': 22.10.1 @@ -5311,7 +5311,7 @@ snapshots: es6-error: 4.1.1 matcher: 3.0.0 roarr: 2.15.4 - semver: 7.7.2 + semver: 7.7.3 serialize-error: 7.0.1 optional: true @@ -5801,7 +5801,7 @@ snapshots: node-abi@3.71.0: dependencies: - semver: 7.7.2 + semver: 7.7.3 node-abi@4.17.0: dependencies: @@ -5848,7 +5848,7 @@ snapshots: nopt: 6.0.0 npmlog: 6.0.2 rimraf: 3.0.2 - semver: 7.7.2 + semver: 7.7.3 tar: 6.2.1 which: 2.0.2 transitivePeerDependencies: diff --git a/src/discord/window.ts b/src/discord/window.ts index 5f4a620c..a60ba9dc 100644 --- a/src/discord/window.ts +++ b/src/discord/window.ts @@ -184,8 +184,8 @@ function doAfterDefiningTheWindow(passedWindow: BrowserWindow): void { // fix UMG video playback passedWindow.webContents.session.webRequest.onBeforeSendHeaders( { urls: ["https://www.youtube.com/embed/*"] }, - ({ requestHeaders, url }, callback) => { - requestHeaders.Referer = url; + ({ requestHeaders }, callback) => { + requestHeaders.Referer = "https://google.com"; callback({ requestHeaders }); }, ); diff --git a/src/shelter/screenshare/components/ScreensharePicker.module.css b/src/shelter/screenshare/components/ScreensharePicker.module.css index 746a356f..14bf8a5a 100644 --- a/src/shelter/screenshare/components/ScreensharePicker.module.css +++ b/src/shelter/screenshare/components/ScreensharePicker.module.css @@ -1,6 +1,6 @@ .sources { grid-template-columns: auto auto; - max-height: 300px; + max-height: 260px; overflow: hidden; display: grid; overflow-y: scroll; @@ -12,10 +12,15 @@ padding: 10px; } .qualityBox { - gap: 10%; - display: flex; + display: grid; + grid-template-columns: auto auto; + gap: 10px 10%; } .checkbox { padding-top: 10px; } +.header { + margin-bottom: 5px; + margin-left: 5px; +} diff --git a/src/shelter/screenshare/components/ScreensharePicker.tsx b/src/shelter/screenshare/components/ScreensharePicker.tsx index db3b6868..08b673a4 100644 --- a/src/shelter/screenshare/components/ScreensharePicker.tsx +++ b/src/shelter/screenshare/components/ScreensharePicker.tsx @@ -1,8 +1,10 @@ import type { Node } from "@vencord/venmic"; import { For, Show, createSignal } from "solid-js"; import { Dropdown } from "../../settings/components/Dropdown.jsx"; +import { SegmentedControl } from "../../settings/components/SegmentedControl.jsx"; import classes from "./ScreensharePicker.module.css"; import { type IPCSources, SourceCard } from "./SourceCard.jsx"; + const { ui: { ModalRoot, @@ -87,7 +89,7 @@ export const ScreensharePicker = (props: { props.close(); } return ( - + Screenshare
@@ -110,42 +112,50 @@ export const ScreensharePicker = (props: {
-
Resolution
- + Resolution + + { - store.resolution = Number(e.currentTarget.value); - }} - > - - - - - - -
-
-
FPS
- { - store.fps = Number(e.currentTarget.value); + onChange={(v) => { + store.resolution = Number(v); }} - > - - - - - + options={[ + { label: "480p", value: "480" }, + { label: "720p", value: "720" }, + { label: "1080p", value: "1080" }, + { label: "1440p", value: "1440" }, + { label: "2160p", value: "2160" }, + ]} + />
-
Audio
+
+ Audio +
+
+
+ FPS +
+ { + store.fps = Number(v); + }} + options={[ + { label: "5", value: "5" }, + { label: "15", value: "15" }, + { label: "30", value: "30" }, + { label: "60", value: "60" }, + ]} + /> +
@@ -153,19 +163,20 @@ export const ScreensharePicker = (props: {
Venmic
{ - const source = props.audioSources!.find( - (node) => node["node.name"] === e.currentTarget.value, - ); + onChange={(v) => { + const source = props.audioSources!.find((node) => node["node.name"] === v); if (!source) return; setAudioSource(source); }} - > - - - {(source: Node) => } - - + limitHeight + options={[ + { label: "Venmic disabled", value: "Venmic disabled" }, + ...(props.audioSources?.map((s) => ({ + label: s["node.name"], + value: s["node.name"], + })) ?? []), + ]} + />
diff --git a/src/shelter/settings/components/Dropdown.module.css b/src/shelter/settings/components/Dropdown.module.css index 03b0d701..d5b13db8 100644 --- a/src/shelter/settings/components/Dropdown.module.css +++ b/src/shelter/settings/components/Dropdown.module.css @@ -1,27 +1,68 @@ -.acDropdown option { - color: var(--text-secondary); - font-weight: 400; - font-style: normal; +.container { + margin-top: 8px; + margin-bottom: 24px; + position: relative; } -.acDropdown { - margin-top: 10px; - margin-bottom: 20px; - padding-top: 5px; - padding-bottom: 5px; - width: 100%; - font-size: 25px; - -webkit-appearance: button; - -moz-appearance: button; - appearance: button; - background-color: var(--background-secondary-alt); - background-position: center right; - background-repeat: no-repeat; - border: 1px solid var(--background-floating); - border-radius: 2px; +.valuewrapper { + display: flex; + justify-content: space-between; + align-items: center; + cursor: pointer; + padding: 8px; + color: var(--text-default); - font-size: 1.2em; + font-weight: var(--font-weight-medium); + background-color: var(--input-background-default); + border: 1px solid var(--input-border-default); + border-radius: var(--radius-sm); + color: var(--input-text-default); + transition: border-color .1s ease; + box-sizing: border-box; + min-height: var(--control-input-height-md); + + &::hover { + border-color: var(--input-border-hover); + transition: border-color .1s ease; + } +} +.value { + align-items: center; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; - outline: none !important; + color: var(--text-default); + font-family: var(--font-primary); + font-size: 16px; + font-weight: 500; + line-height: 1.25; +} +.list { + min-height: 0; + margin: 0; + margin-top: 8px; + background-color: var(--background-surface-higher); + border: 1px solid var(--border-subtle); + border-radius: 8px; + position: absolute; + width: 100%; + z-index: 100; + overflow-y: auto; +} +option { + color: var(--text-subtle); + font-size: 16px; + font-weight: 400; + line-height: 20px; + align-items: center; + padding: 12px; + cursor: pointer; + + &:hover { + background-color: var(--interactive-background-hover); + color: var(--interactive-text-hover); + } + &[aria-selected="true"] { + background-color: var(--interactive-background-selected); + color: var(--interactive-text-active); + } } diff --git a/src/shelter/settings/components/Dropdown.tsx b/src/shelter/settings/components/Dropdown.tsx index ab74ad75..af61f233 100644 --- a/src/shelter/settings/components/Dropdown.tsx +++ b/src/shelter/settings/components/Dropdown.tsx @@ -1,14 +1,74 @@ -import type { JSX, JSXElement } from "solid-js"; +import { For, createEffect, createMemo, createSignal, onCleanup, onMount } from "solid-js"; import classes from "./Dropdown.module.css"; export const Dropdown = (props: { - value: string | number | string[]; - onChange: JSX.EventHandler; - children: JSXElement[]; + value: string | number; + onChange: (val: string) => void; + options: { label: string; value: string | number }[]; + limitHeight?: boolean | undefined; }) => { + const [open, set] = createSignal(false); + const [maxHeight, setMaxHeight] = createSignal(""); + let container: HTMLDivElement | undefined; + + const handler = (e: MouseEvent) => !container?.contains(e.target as Node) && set(false); + + onMount(() => document.addEventListener("click", handler)); + onCleanup(() => document.removeEventListener("click", handler)); + + createEffect(() => { + if (!open() || !props.limitHeight) return; + + const rect = container?.parentElement?.getBoundingClientRect(); + if (!rect) return; + + setMaxHeight(`${rect.bottom - container!.getBoundingClientRect().bottom - 20}px`); + }); + + const text = createMemo(() => props.options.find((o) => o.value === props.value)?.label ?? props.value); + return ( - + // biome-ignore lint/a11y/useSemanticElements: FIX-ME +
+
set(!open())}> +
+ {text()} +
+
+ +
+
+ {open() && ( +
+ + {(opt) => ( + + )} + +
+ )} +
); }; diff --git a/src/shelter/settings/components/DropdownItem.module.css b/src/shelter/settings/components/DropdownItem.module.css index 78207cb0..89f7ad7e 100644 --- a/src/shelter/settings/components/DropdownItem.module.css +++ b/src/shelter/settings/components/DropdownItem.module.css @@ -1,6 +1,6 @@ .note { margin-top: 8px; - color: var(--header-secondary); + color: var(--text-subtle); font-size: 14px; line-height: 20px; font-weight: 400; @@ -11,7 +11,7 @@ display: block; overflow: hidden; margin: 0; - color: var(--header-primary); + color: var(--text-strong); line-height: 24px; font-weight: 500; word-wrap: break-word; diff --git a/src/shelter/settings/components/DropdownItem.tsx b/src/shelter/settings/components/DropdownItem.tsx index f4384977..7bae47fc 100644 --- a/src/shelter/settings/components/DropdownItem.tsx +++ b/src/shelter/settings/components/DropdownItem.tsx @@ -1,4 +1,4 @@ -import { type JSX, type JSXElement, Show } from "solid-js"; +import { type JSXElement, Show } from "solid-js"; import { Dropdown } from "./Dropdown.jsx"; import classes from "./DropdownItem.module.css"; const { @@ -10,8 +10,9 @@ export const DropdownItem = (props: { note: string; link?: string; value: string; - onChange: JSX.EventHandler; - children: JSXElement[]; + onChange: (val: string) => void; + options: { label: string; value: string | number }[]; + limitHeight?: boolean | undefined; extraItems?: JSXElement; }) => { return ( @@ -19,12 +20,18 @@ export const DropdownItem = (props: {
{props.title}
-
{props.note}
- - Learn more here. - - {/* biome-ignore lint/correctness/noChildrenProp: FIX-ME, couldn't figure out proper types */} - +
+ {props.note} + + Learn more here. + +
+ {props.extraItems} diff --git a/src/shelter/settings/components/KeybindMaker.module.css b/src/shelter/settings/components/KeybindMaker.module.css index 1ae2dd7b..40d8c5d9 100644 --- a/src/shelter/settings/components/KeybindMaker.module.css +++ b/src/shelter/settings/components/KeybindMaker.module.css @@ -1,5 +1,22 @@ .grabBox { + position: relative; flex-direction: row; flex: 1; display: flex; } +.recBtn { + position: absolute; + width: auto; + top: 50%; + right: 0; + transform: translate(-7px, -50%); +} +.error { + color: var(--control-critical-secondary-text-default); + font-size: 14px; + line-height: 20px; + font-weight: 400; + + margin: 0; + margin-left: 5px; +} diff --git a/src/shelter/settings/components/KeybindMaker.tsx b/src/shelter/settings/components/KeybindMaker.tsx index e85de177..d541cbb6 100644 --- a/src/shelter/settings/components/KeybindMaker.tsx +++ b/src/shelter/settings/components/KeybindMaker.tsx @@ -1,7 +1,8 @@ -import { Show, createSignal } from "solid-js"; +import { Show, createSignal, onCleanup } from "solid-js"; import type { KeybindActions } from "../../../@types/keybind.js"; import { Dropdown } from "./Dropdown.jsx"; import classes from "./KeybindMaker.module.css"; + const { ui: { ModalRoot, @@ -12,57 +13,64 @@ const { TextBox, Button, ButtonSizes, + ButtonColors, Header, HeaderTags, Divider, SwitchItem, genId, - showToast, }, plugin: { store }, } = shelter; + export const KeybindMaker = (props: { close: () => void }) => { + const [recording, setRecording] = createSignal(false); const [accelerator, setAccelerator] = createSignal(""); const [global, setGlobal] = createSignal(true); const [action, setAction] = createSignal("mute"); const [javascriptCode, setJavascriptCode] = createSignal(""); const [enabled, setEnabled] = createSignal(true); + let logged: string[] = []; - let lock = false; - function grabKeys() { - if (lock) return; - lock = true; + let containsNonModifier = false; + let timeout: NodeJS.Timeout | null = null; + function log(event: KeyboardEvent) { + const key = event.key.replace(" ", "Space"); + if (logged.includes(key) || logged.length > 3) { + console.log("already in array"); + } else { + console.log(key); + logged.unshift(key); + if (event.location === 0) containsNonModifier = true; + setAccelerator(logged.join("+")); + } + if (timeout) clearTimeout(timeout); + timeout = setTimeout(stopRecording, 3000); + } + function stopRecording() { + if (!recording()) return; + setRecording(false); + if (timeout) { + clearTimeout(timeout); + timeout = null; + } + + document.body.removeEventListener("keyup", log); + console.log("Recording stop"); + } + onCleanup(() => recording() && stopRecording()); + + function startRecording() { + if (recording()) return; + setRecording(true); + logged = []; + containsNonModifier = false; setAccelerator(""); console.log("Recording start"); - document.body.addEventListener("keyup", function log(event) { - const key = event.key; - if (logged.includes(key) || logged.length > 3) { - console.log("already in array"); - } else { - key.replace(" ", "Space"); - console.log(key); - logged.push(key); - setAccelerator(`${key}+${accelerator()}`); - } - setTimeout(() => { - if (lock) { - lock = false; - document.body.removeEventListener("keyup", log); - console.log("Recording stop"); - setAccelerator(accelerator().slice(0, -1)); - } - }, 3000); - }); + document.body.addEventListener("keyup", log); } function save() { - if (lock) - return showToast({ - title: "Slow down!", - content: "Pause for a few seconds after recording a keybind before saving it.", - duration: 3000, - }); - if (accelerator() === "") return; const current = store.settings.keybinds; const keybind = { accelerator: accelerator(), @@ -78,33 +86,52 @@ export const KeybindMaker = (props: { close: () => void }) => { console.log(store.settings.keybinds); window.legcord.settings.addKeybind(keybind); } + return ( Add a keybind -
Accelerator
+ +
Accelerator
+ +

Modifier-only shortcuts are not supported.

+
+
{/* FIXME - I have no idea what this `disabled` tag is, its not in the typedefs // @ts-expect-error*/} - + {recording() ? ( + + ) : ( + + )}
Action
setAction((e.target as HTMLInputElement).value as KeybindActions)} - > - - - - - - - - + onChange={(v) => setAction(v as KeybindActions)} + limitHeight + options={[ + { label: "Mute", value: "mute" }, + { label: "Deafen", value: "deafen" }, + { label: "Leave call", value: "leaveCall" }, + { label: "Navigate forward", value: "navigateForward" }, + { label: "Navigate back", value: "navigateBack" }, + { label: "Run Javascript", value: "runJavascript" }, + { label: "Open Quick CSS", value: "openQuickCss" }, + ]} + /> void }) => {
- +
); }; diff --git a/src/shelter/settings/components/SegmentedControl.module.css b/src/shelter/settings/components/SegmentedControl.module.css new file mode 100644 index 00000000..dbc0b3b1 --- /dev/null +++ b/src/shelter/settings/components/SegmentedControl.module.css @@ -0,0 +1,32 @@ +.container { + min-height: var(--control-input-height-md); + background-color: var(--input-background-default); + border: 1px solid var(--input-border-default); + border-radius: var(--radius-sm); + display: flex; + overflow: hidden; +} +.item { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + padding: 0 10px; + cursor: pointer; + position: relative; + color: var(--text-default); + font-weight: var(--font-weight-medium); +} +.item:not(:first-child)::before { + content: ""; + position: absolute; + left: 0; + top: 25%; + height: 50%; + width: 1px; + background-color: var(--border-subtle); +} +.item[aria-selected="true"] { + background-color: var(--interactive-background-selected); + color: var(--interactive-text-active); +} diff --git a/src/shelter/settings/components/SegmentedControl.tsx b/src/shelter/settings/components/SegmentedControl.tsx new file mode 100644 index 00000000..b5fff3c8 --- /dev/null +++ b/src/shelter/settings/components/SegmentedControl.tsx @@ -0,0 +1,24 @@ +import { For } from "solid-js"; +import classes from "./SegmentedControl.module.css"; + +export const SegmentedControl = (props: { + value: string | number; + onChange: (val: string) => void; + options: { label: string; value: string | number }[]; +}) => { + return ( +
+ + {(opt) => ( +
props.onChange(String(opt.value))} + > + {opt.label} +
+ )} +
+
+ ); +}; diff --git a/src/shelter/settings/pages/RegisteredGamesPage.tsx b/src/shelter/settings/pages/RegisteredGamesPage.tsx index a624d937..1bc1e83b 100644 --- a/src/shelter/settings/pages/RegisteredGamesPage.tsx +++ b/src/shelter/settings/pages/RegisteredGamesPage.tsx @@ -1,5 +1,5 @@ import type { ProcessInfo } from "arrpc"; -import { For, createSignal } from "solid-js"; +import { createSignal } from "solid-js"; import { sleep } from "../../../common/sleep.js"; import { Dropdown } from "../components/Dropdown.jsx"; import classes from "./RegisteredGames.module.css"; @@ -9,7 +9,7 @@ const { export function RegisteredGamesPage() { const [detectables, setDetectables] = createSignal(); - const [selectedDetectable, setSelectedDetectable] = createSignal(""); + const [selectedDetectable, setSelectedDetectable] = createSignal("refresh"); function getDetectables() { window.legcord.rpc.refreshProcessList(); sleep(500).then(() => { @@ -28,23 +28,21 @@ export function RegisteredGamesPage() {
{ - const detectable = e.currentTarget.value; - if (detectable === "refresh") { + onChange={(v) => { + if (v === "refresh") { getDetectables(); setSelectedDetectable(""); console.log("Detectables refreshed"); } else { - console.log("Selected detectable:", detectable); - setSelectedDetectable(e.currentTarget.value); + console.log("Selected detectable:", v); + setSelectedDetectable(v); } }} - > - - {(process: ProcessInfo) => } - - - + options={[ + ...(detectables()?.map((p) => ({ label: p[1], value: p[1] })) ?? []), + { label: "Refresh list", value: "refresh" }, + ]} + /> diff --git a/src/shelter/settings/pages/SettingsPage.tsx b/src/shelter/settings/pages/SettingsPage.tsx index e3685427..670660cb 100644 --- a/src/shelter/settings/pages/SettingsPage.tsx +++ b/src/shelter/settings/pages/SettingsPage.tsx @@ -48,28 +48,22 @@ export function SettingsPage() { > {store.i18n["settings-invitewebsocket"]} -
- {store.i18n["settings-category-lookAndFeel"]} -
- setConfig("windowStyle", (e.target as HTMLInputElement).value as Settings["windowStyle"], true) - } + onChange={(v) => setConfig("windowStyle", v as Settings["windowStyle"], true)} title={store.i18n["settings-theme"]} note={store.i18n["settings-theme-desc"]} link="https://github.com/Legcord/Legcord/wiki/Settings-%5Bwip%5D#legcord-theme" - > - - - - - + options={[ + { label: store.i18n["settings-theme-default"], value: "default" }, + { label: store.i18n["settings-theme-native"], value: "native" }, + { label: store.i18n["settings-theme-overlay"], value: "overlay" }, + { label: "Legacy", value: "legacy" }, + ]} + /> - setConfig("transparency", (e.target as HTMLInputElement).value as Settings["transparency"], true) - } + onChange={(v) => setConfig("transparency", v as Settings["transparency"], true)} title={store.i18n["settings-transparency"]} note={store.i18n["settings-transparency-desc"]} link="https://github.com/Legcord/Legcord/wiki/Transparency-options" @@ -93,13 +87,14 @@ export function SettingsPage() {
} - > - - - - - - + options={[ + { label: store.i18n["settings-transparency-universal"], value: "universal" }, + ...(window.legcord.platform === "win32" || window.legcord.platform === "darwin" + ? [{ label: store.i18n["settings-transparency-modern"], value: "modern" }] + : []), + { label: store.i18n["settings-none"], value: "none" }, + ]} + /> setConfig("tray", (e.target as HTMLInputElement).value as Settings["tray"], true)} + onChange={(v) => setConfig("tray", v as Settings["tray"], true)} title={store.i18n["settings-trayIcon"]} note={store.i18n["settings-trayIcon-desc"]} - > - - - - - - - - - - + options={[ + { label: store.i18n["settings-trayIcon-dynamic"], value: "dynamic" }, + { label: store.i18n["settings-trayIcon-disabled"], value: "disabled" }, + { label: store.i18n["settings-trayIcon-normal"], value: "dsc-tray" }, + { label: store.i18n["settings-trayIcon-classic"], value: "clsc-dsc-tray" }, + { label: store.i18n["settings-trayIcon-colored-plug"], value: "ac_plug_colored" }, + { label: store.i18n["settings-trayIcon-white-plug"], value: "ac_white_plug" }, + { label: store.i18n["settings-trayIcon-white-plug-alt"], value: "ac_white_plug_hollow" }, + { label: store.i18n["settings-trayIcon-black-plug"], value: "ac_black_plug" }, + { label: store.i18n["settings-trayIcon-black-plug-alt"], value: "ac_black_plug_hollow" }, + ]} + /> - setConfig("channel", (e.target as HTMLInputElement).value as Settings["channel"], true) - } + onChange={(v) => setConfig("channel", v as Settings["channel"], true)} title={store.i18n["settings-channel"]} note={store.i18n["settings-channel-desc"]} link="https://support.discord.com/hc/en-us/articles/360035675191-Discord-Testing-Clients" - > - - - - + options={[ + { label: "Stable", value: "stable" }, + { label: "Canary", value: "canary" }, + { label: "PTB", value: "ptb" }, + ]} + /> - setConfig( - "performanceMode", - (e.target as HTMLInputElement).value as Settings["performanceMode"], - true, - ) - } + onChange={(v) => setConfig("performanceMode", v as Settings["performanceMode"], true)} title={store.i18n["settings-prfmMode"]} note={store.i18n["settings-prfmMode-desc"]} link="https://github.com/Legcord/Legcord/blob/dev/src/common/flags.ts" - > - - - - - - + options={[ + { label: store.i18n["settings-prfmMode-dynamic"], value: "dynamic" }, + { label: store.i18n["settings-prfmMode-performance"], value: "performance" }, + { label: store.i18n["settings-prfmMode-battery"], value: "battery" }, + { label: store.i18n["settings-prfmMode-vaapi"], value: "vaapi" }, + { label: store.i18n["settings-none"], value: "none" }, + ]} + /> { + onChange={(v) => { const audioSettings = structuredClone({ ...settings.audio }); - audioSettings.loopbackType = (e.target as HTMLInputElement) - .value as Settings["audio"]["loopbackType"]; + audioSettings.loopbackType = v as Settings["audio"]["loopbackType"]; setConfig("audio", audioSettings); }} title={store.i18n["settings-audio"]} note={store.i18n["settings-audio-desc"]} link="https://www.electronjs.org/docs/latest/api/session#sessetdisplaymediarequesthandlerhandler-opts" - > - - - + options={[ + { label: "Loopback", value: "loopback" }, + { label: "Loopback with mute", value: "loopbackWithMute" }, + ]} + />