diff options
Diffstat (limited to 'src/pages/options_src')
-rw-r--r-- | src/pages/options_src/App.svelte | 118 | ||||
-rw-r--r-- | src/pages/options_src/General/Exceptions.svelte | 108 | ||||
-rw-r--r-- | src/pages/options_src/General/General.svelte | 70 | ||||
-rw-r--r-- | src/pages/options_src/General/SettingsButtons.svelte | 118 | ||||
-rw-r--r-- | src/pages/options_src/Services/FrontendIcon.svelte | 43 | ||||
-rw-r--r-- | src/pages/options_src/Services/Instances.svelte | 233 | ||||
-rw-r--r-- | src/pages/options_src/Services/RedirectType.svelte | 99 | ||||
-rw-r--r-- | src/pages/options_src/Services/ServiceIcon.svelte | 40 | ||||
-rw-r--r-- | src/pages/options_src/Services/Services.svelte | 188 | ||||
-rw-r--r-- | src/pages/options_src/Sidebar.svelte | 43 | ||||
-rw-r--r-- | src/pages/options_src/main.js | 7 | ||||
-rw-r--r-- | src/pages/options_src/stores.js | 5 |
12 files changed, 1072 insertions, 0 deletions
diff --git a/src/pages/options_src/App.svelte b/src/pages/options_src/App.svelte new file mode 100644 index 00000000..6bbacb9b --- /dev/null +++ b/src/pages/options_src/App.svelte @@ -0,0 +1,118 @@ +<script> + let browser = window.browser || window.chrome + + import General from "./General/General.svelte" + import utils from "../../assets/javascripts/utils.js" + import { onDestroy } from "svelte" + import servicesHelper from "../../assets/javascripts/services.js" + import { onMount } from "svelte" + import Sidebar from "./Sidebar.svelte" + import { options, config, page } from "./stores" + import Services from "./Services/Services.svelte" + + let _options + const unsubscribeOptions = options.subscribe(val => { + if (val) { + _options = val + browser.storage.local.set({ options: val }) + } + }) + + let _config + const unsubscribeConfig = config.subscribe(val => (_config = val)) + + onDestroy(() => { + unsubscribeOptions() + unsubscribeConfig() + }) + + onMount(async () => { + let opts = await utils.getOptions() + if (!opts) { + await servicesHelper.initDefaults() + opts = await utils.getOptions() + } + options.set(opts) + config.set(await utils.getConfig()) + }) + + let _page + page.subscribe(val => (_page = val)) + + const dark = { + text: "#fff", + bgMain: "#121212", + bgSecondary: "#202020", + active: "#fbc117", + danger: "#f04141", + lightGrey: "#c3c3c3", + } + const light = { + text: "black", + bgMain: "white", + bgSecondary: "#e4e4e4", + active: "#fb9817", + danger: "#f04141", + lightGrey: "#c3c3c3", + } + let cssVariables + $: if (_options) { + if (_options.theme == "dark") { + cssVariables = dark + } else if (_options.theme == "light") { + cssVariables = light + } else if (window.matchMedia("(prefers-color-scheme: dark)").matches) { + cssVariables = dark + } else { + cssVariables = light + } + } +</script> + +{#if _options && _config} + <div + class="main" + dir="auto" + style=" + --text: {cssVariables.text}; + --bg-main: {cssVariables.bgMain}; + --bg-secondary: {cssVariables.bgSecondary}; + --active: {cssVariables.active}; + --danger: {cssVariables.danger}; + --light-grey: {cssVariables.lightGrey};" + > + <Sidebar /> + {#if _page == "general"} + <General /> + {:else if _page == "services"} + <Services /> + {/if} + </div> +{:else} + <p>Loading...</p> +{/if} + +<style> + + :global(body) { + width: 100vw; + height: 100vh; + margin: 0; + padding: 0; + } + + div { + height: 100%; + display: grid; + grid-template-columns: min-content 800px; + margin: 0; + padding-top: 50px; + justify-content: center; + font-family: "Inter"; + box-sizing: border-box; + font-size: 16px; + background-color: var(--bg-main); + color: var(--text); + overflow: scroll; + } +</style> diff --git a/src/pages/options_src/General/Exceptions.svelte b/src/pages/options_src/General/Exceptions.svelte new file mode 100644 index 00000000..4ef5591b --- /dev/null +++ b/src/pages/options_src/General/Exceptions.svelte @@ -0,0 +1,108 @@ +<script> + import Row from "../../components/Row.svelte" + import Select from "../../components/Select.svelte" + import AddIcon from "../../icons/AddIcon.svelte" + import CloseIcon from "../../icons/CloseIcon.svelte" + import Input from "../../components/Input.svelte" + import Label from "../../components/Label.svelte" + import { options, config } from "../stores" + import { onDestroy } from "svelte" + + let _options + let _config + + const unsubscribeOptions = options.subscribe(val => (_options = val)) + const unsubscribeConfig = config.subscribe(val => (_config = val)) + onDestroy(() => { + unsubscribeOptions() + unsubscribeConfig() + }) + let inputType = "url" + let inputValue = "" + + $: inputPlaceholder = inputType == "url" ? "https://www.google.com" : "https?://(www.|)youtube.com/" + + function removeException(exception) { + let index + index = _options.exceptions.url.indexOf(exception) + if (index > -1) { + _options.exceptions.url.splice(index, 1) + } else { + index = _options.exceptions.regex.indexOf(exception) + if (index > -1) _options.exceptions.regex.splice(index, 1) + } + options.set(_options) + } + + function addException() { + let valid = false + if (inputType == "url" && /^(ftp|http|https):\/\/[^ "]+$/.test(inputValue)) { + valid = true + if (!_options.exceptions.url.includes(inputValue)) { + _options.exceptions.url.push(inputValue) + } + } else if (inputType == "regex") { + valid = true + if (!_options.exceptions.regex.includes(inputValue)) { + _options.exceptions.regex.push(inputValue) + } + } + if (valid) { + options.set(_options) + inputValue = "" + } + } +</script> + +<div class="block block-option"> + <Row> + <Label>Excluded from redirecting</Label> + </Row> + <Row> + <div> + <Input + placeholder={inputPlaceholder} + aria-label="Add url exception input" + bind:value={inputValue} + on:keydown={e => { + if (e.key === "Enter") addException() + }} + /> + <Select + bind:value={inputType} + values={[ + { value: "url", name: "URL" }, + { value: "regex", name: "Regex" }, + ]} + /> + </div> + <button class="add" on:click={addException} aria-label="Add the url exception"> + <AddIcon /> + </button> + </Row> + <hr /> + <div class="checklist"> + {#each [..._options.exceptions.url, ..._options.exceptions.regex] as exception} + <Row> + {exception} + <button class="add" on:click={() => removeException(exception)}> + <CloseIcon /> + </button> + </Row> + <hr /> + {/each} + </div> +</div> + +<style> + .add { + background-color: transparent; + border: none; + color: var(--text); + padding: 0; + margin: 0; + text-decoration: none; + display: inline-block; + cursor: pointer; + } +</style> diff --git a/src/pages/options_src/General/General.svelte b/src/pages/options_src/General/General.svelte new file mode 100644 index 00000000..d5b2dd59 --- /dev/null +++ b/src/pages/options_src/General/General.svelte @@ -0,0 +1,70 @@ +<script> + let browser = window.browser || window.chrome + + import Exceptions from "./Exceptions.svelte" + import SettingsButtons from "./SettingsButtons.svelte" + import RowSelect from "../../components/RowSelect.svelte" + import Checkbox from "../../components/RowCheckbox.svelte" + import { options } from "../stores" + import { onDestroy } from "svelte" + + let _options + const unsubscribe = options.subscribe(val => (_options = val)) + onDestroy(unsubscribe) + + let bookmarksPermission + browser.permissions.contains({ permissions: ["bookmarks"] }, r => (bookmarksPermission = r)) + $: if (bookmarksPermission) { + browser.permissions.request({ permissions: ["bookmarks"] }, r => (bookmarksPermission = r)) + } else { + browser.permissions.remove({ permissions: ["bookmarks"] }) + bookmarksPermission = false + } +</script> + +<div> + <RowSelect + label="Theme" + values={[ + { value: "detect", name: "Auto" }, + { value: "light", name: "Light" }, + { value: "dark", name: "Dark" }, + ]} + value={_options.theme} + onChange={e => { + _options["theme"] = e.target.options[e.target.options.selectedIndex].value + options.set(_options) + }} + ariaLabel="select theme" + /> + + <RowSelect + label="Fetch public instances" + value={_options.fetchInstances} + onChange={e => { + _options["fetchInstances"] = e.target.options[e.target.options.selectedIndex].value + options.set(_options) + }} + ariaLabel="Select fetch public instances" + values={[ + { value: "github", name: "GitHub" }, + { value: "codeberg", name: "Codeberg" }, + { value: "disable", name: "Disable" }, + ]} + /> + + <Checkbox + label="Redirect Only in Incognito" + checked={_options.redirectOnlyInIncognito} + onChange={e => { + _options["redirectOnlyInIncognito"] = e.target.checked + options.set(_options) + }} + /> + + <Checkbox label="Bookmarks menu" bind:checked={bookmarksPermission} /> + + <Exceptions opts={_options} /> + + <SettingsButtons opts={_options} /> +</div> diff --git a/src/pages/options_src/General/SettingsButtons.svelte b/src/pages/options_src/General/SettingsButtons.svelte new file mode 100644 index 00000000..c37a3702 --- /dev/null +++ b/src/pages/options_src/General/SettingsButtons.svelte @@ -0,0 +1,118 @@ +<script> + let browser = window.browser || window.chrome + + import { onDestroy } from "svelte" + import Button from "../../components/Button.svelte" + import ExportIcon from "../../icons/ExportIcon.svelte" + import ImportIcon from "../../icons/ImportIcon.svelte" + import ResetIcon from "../../icons/ResetIcon.svelte" + import { options } from "../stores" + import servicesHelper from "../../../assets/javascripts/services.js" + import utils from "../../../assets/javascripts/utils.js" + + let _options + const unsubscribe = options.subscribe(val => (_options = val)) + onDestroy(unsubscribe) + + let disableButtons = false + + let importSettingsInput + let importSettingsFiles + $: if (importSettingsFiles) { + disableButtons = true + const reader = new FileReader() + reader.readAsText(importSettingsFiles[0]) + reader.onload = async () => { + const data = JSON.parse(reader.result) + if ("theme" in data && data.version == browser.runtime.getManifest().version) { + browser.storage.local.clear(async () => { + console.log("clearing") + options.set(data) + disableButtons = false + }) + } else { + console.log("incompatible settings") + alert("Incompatible settings") + } + } + reader.onerror = error => { + console.log("error", error) + alert("Error!") + } + } + + async function exportSettings() { + disableButtons = true + _options.version = browser.runtime.getManifest().version + const resultString = JSON.stringify(_options, null, " ") + const anchor = document.createElement("a") + anchor.href = "data:application/json;base64," + btoa(resultString) + anchor.download = `libredirect-settings-v${_options.version}.json` + anchor.click() + disableButtons = false + } + + async function exportSettingsSync() { + disableButtons = true + _options.version = browser.runtime.getManifest().version + await servicesHelper.initDefaults() + browser.storage.sync.set({ options: _options }) + disableButtons = false + } + + async function importSettingsSync() { + disableButtons = true + browser.storage.sync.get({ options }, r => { + const optionsSync = r.options + if (optionsSync.version == browser.runtime.getManifest().version) { + options.set(optionsSync) + } else { + alert("Error") + } + disableButtons = false + }) + } + + async function resetSettings() { + disableButtons = true + browser.storage.local.clear(async () => { + await servicesHelper.initDefaults() + options.set(await utils.getOptions()) + disableButtons = false + }) + } +</script> + +<div class="buttons"> + <Button on:click={() => importSettingsInput.click()} disabled={disableButtons}> + <ImportIcon /> + <x data-localise="__MSG_importSettings__">Import Settings</x> + </Button> + <input + type="file" + accept=".json" + style="display: none" + bind:this={importSettingsInput} + bind:files={importSettingsFiles} + /> + + <Button on:click={exportSettings} disabled={disableButtons}> + <ExportIcon /> + <x data-localise="__MSG_exportSettings__">Export Settings</x> + </Button> + + <Button on:click={exportSettingsSync} disabled={disableButtons}> + <ExportIcon /> + <x>Export Settings to Sync</x> + </Button> + + <Button on:click={importSettingsSync} disabled={disableButtons}> + <ImportIcon /> + <x>Import Settings from Sync</x> + </Button> + + <Button on:click={resetSettings} disabled={disableButtons}> + <ResetIcon /> + <x>Reset Settings</x> + </Button> +</div> diff --git a/src/pages/options_src/Services/FrontendIcon.svelte b/src/pages/options_src/Services/FrontendIcon.svelte new file mode 100644 index 00000000..24942fd6 --- /dev/null +++ b/src/pages/options_src/Services/FrontendIcon.svelte @@ -0,0 +1,43 @@ +<script> + import { onDestroy } from "svelte" + export let details + import { config, options } from "../stores" + let _options + let _config + + const unsubscribeOptions = options.subscribe(val => (_options = val)) + const unsubscribeConfig = config.subscribe(val => (_config = val)) + onDestroy(() => { + unsubscribeOptions() + unsubscribeConfig() + }) + + let theme + $: if (_options) { + if (_options.theme == "dark") { + theme = "dark" + } else if (_options.theme == "light") { + theme = "light" + } else if (window.matchMedia("(prefers-color-scheme: dark)").matches) { + theme = "dark" + } else { + theme = "light" + } + } + + export let selectedService + + $: imageType = _config.services[selectedService].frontends[details.value].imageType +</script> + +{#if imageType} + {#if imageType == "svgMono"} + {#if theme == "dark"} + <img src={`/assets/images/${details.value}-icon-light.svg`} alt={details.label} /> + {:else} + <img src={`/assets/images/${details.value}-icon.svg`} alt={details.label} /> + {/if} + {:else} + <img src={`/assets/images/${details.value}-icon.${imageType}`} alt={details.label} /> + {/if} +{/if} diff --git a/src/pages/options_src/Services/Instances.svelte b/src/pages/options_src/Services/Instances.svelte new file mode 100644 index 00000000..892e395b --- /dev/null +++ b/src/pages/options_src/Services/Instances.svelte @@ -0,0 +1,233 @@ +<script> + let browser = window.browser || window.chrome + + import Button from "../../components/Button.svelte" + import AddIcon from "../../icons/AddIcon.svelte" + import { options, config } from "../stores" + import PingIcon from "../../icons/PingIcon.svelte" + import Row from "../../components/Row.svelte" + import Input from "../../components/Input.svelte" + import Label from "../../components/Label.svelte" + import CloseIcon from "../../icons/CloseIcon.svelte" + import { onDestroy, onMount } from "svelte" + import utils from "../../../assets/javascripts/utils" + + export let selectedService + export let selectedFrontend + + let _options + let _config + + const unsubscribeOptions = options.subscribe(val => (_options = val)) + const unsubscribeConfig = config.subscribe(val => (_config = val)) + onDestroy(() => { + unsubscribeOptions() + unsubscribeConfig() + }) + + let blacklist + let redirects + + $: serviceOptions = _options[selectedService] + $: serviceConf = _config.services[selectedService] + + let allInstances = [] + + $: { + allInstances = [] + if (_options[selectedFrontend]) allInstances.push(..._options[selectedFrontend]) + if (redirects && redirects[selectedFrontend]) { + for (const network in redirects[selectedFrontend]) { + allInstances.push(...redirects[selectedFrontend][network]) + } + } + } + + let pingCache + $: { + if (pingCache) browser.storage.local.set({ pingCache }) + } + + function isCustomInstance(instance) { + if (redirects[selectedFrontend]) { + for (const network in redirects[selectedFrontend]) { + if (redirects[selectedFrontend][network].includes(instance)) return false + } + } + return true + } + + async function pingInstances() { + pingCache = {} + for (const instance of allInstances) { + console.log("pinging...", instance) + pingCache[instance] = { color: "lightblue", value: "pinging..." } + const time = await utils.ping(instance) + pingCache[instance] = processTime(time) + } + } + function processTime(time) { + let value + let color + if (time < 5000) { + value = `${time}ms` + if (time <= 1000) color = "green" + else if (time <= 2000) color = "orange" + } else if (time >= 5000) { + color = "red" + if (time == 5000) value = "5000ms+" + if (time > 5000) value = `Error: ${time - 5000}` + } else { + color = "red" + value = "Server not found" + } + return { color, value } + } + + onMount(async () => { + blacklist = await utils.getBlacklist(_options) + redirects = await utils.getList(_options) + pingCache = await utils.getPingCache() + }) + + let addInstanceValue + function addInstance() { + const instance = utils.protocolHost(new URL(addInstanceValue)) + if (!_options[selectedFrontend].includes(instance)) { + _options[selectedFrontend].push(instance) + addInstanceValue = "" + options.set(_options) + } + } +</script> + +{#if serviceConf.frontends[selectedFrontend].instanceList && redirects && blacklist} + <hr /> + <div dir="ltr"> + <div class="ping"> + <Button on:click={pingInstances}> + <PingIcon /> + Ping Instances + </Button> + </div> + + <Row> + <Label>Add your favorite instances</Label> + </Row> + + <Row> + <Input + bind:value={addInstanceValue} + type="url" + placeholder="https://instance.com" + aria-label="Add instance input" + on:keydown={e => e.key === "Enter" && addInstance()} + /> + <button on:click={addInstance} class="add" aria-label="Add the instance"> + <AddIcon /> + </button> + </Row> + + {#each _options[selectedFrontend] as instance} + <Row> + <span> + <a href={instance} target="_blank" rel="noopener noreferrer">{instance}</a> + {#if isCustomInstance(instance)} + <span style="color:grey">custom</span> + {/if} + {#if pingCache && pingCache[instance]} + <span style="color:{pingCache[instance].color}">{pingCache[instance].value}</span> + {/if} + </span> + <button + class="add" + aria-label="Remove Instance" + on:click={() => { + const index = _options[selectedFrontend].indexOf(instance) + if (index > -1) { + _options[selectedFrontend].splice(index, 1) + options.set(_options) + } + }} + > + <CloseIcon /> + </button> + </Row> + <hr /> + {/each} + + {#if redirects !== "disabled" && blacklist !== "disabled"} + {#if redirects[selectedFrontend] && redirects[selectedFrontend]["clearnet"]} + {#each Object.entries(_config.networks) as [networkName, network]} + {#if redirects[selectedFrontend] && redirects[selectedFrontend][networkName] && redirects[selectedFrontend][networkName].length > 0} + <Row></Row> + <Row><Label>{network.name}</Label></Row> + <hr /> + {#each redirects[selectedFrontend][networkName] as instance} + <Row> + <span> + <a href={instance} target="_blank" rel="noopener noreferrer">{instance}</a> + {#if blacklist.cloudflare.includes(instance)} + <a + href="https://libredirect.github.io/docs.html#instances" + target="_blank" + rel="noopener noreferrer" + style="color:red;" + > + cloudflare + </a> + {/if} + {#if _options[selectedFrontend].includes(instance)} + <span style="color:grey">chosen</span> + {/if} + {#if pingCache && pingCache[instance]} + <span style="color:{pingCache[instance].color}">{pingCache[instance].value}</span> + {/if} + </span> + <button + class="add" + aria-label="Add instance" + on:click={() => { + if (_options[selectedFrontend]) { + if (!_options[selectedFrontend].includes(instance)) { + _options[selectedFrontend].push(instance) + options.set(_options) + } + } + }} + > + <AddIcon /> + </button> + </Row> + <hr /> + {/each} + {/if} + {/each} + {:else} + <Row><Label>No instances found.</Label></Row> + {/if} + {/if} + </div> +{/if} + +<style> + .add { + background-color: transparent; + border: none; + color: var(--text); + padding: 0; + margin: 0; + text-decoration: none; + display: inline-block; + cursor: pointer; + } + + a { + color: var(--text); + text-decoration: none; + } + + a:hover { + text-decoration: underline; + } +</style> diff --git a/src/pages/options_src/Services/RedirectType.svelte b/src/pages/options_src/Services/RedirectType.svelte new file mode 100644 index 00000000..7f7a2843 --- /dev/null +++ b/src/pages/options_src/Services/RedirectType.svelte @@ -0,0 +1,99 @@ +<script> + import { onDestroy } from "svelte" + + import RowSelect from "../../components/RowSelect.svelte" + import SvelteSelect from "svelte-select" + import { options, config } from "../stores" + import Row from "../../components/Row.svelte" + import Label from "../../components/Label.svelte" + import FrontendIcon from "./FrontendIcon.svelte" + + let _options + let _config + + const unsubscribeOptions = options.subscribe(val => (_options = val)) + const unsubscribeConfig = config.subscribe(val => (_config = val)) + onDestroy(() => { + unsubscribeOptions() + unsubscribeConfig() + }) + + export let selectedService + + $: serviceConf = _config.services[selectedService] + $: serviceOptions = _options[selectedService] + $: frontendName = _options[selectedService].frontend + + let values + $: if (serviceConf.frontends[frontendName].embeddable) { + values = [ + { value: "both", name: "Both" }, + { value: "sub_frame", name: "Only Embedded" }, + { value: "main_frame", name: "Only Not Embedded" }, + ] + } else if ( + serviceConf.frontends[frontendName].desktopApp && + Object.values(serviceConf.frontends).some(frontend => frontend.embeddable) + ) { + values = [ + { value: "both", name: "both" }, + { value: "main_frame", name: "Only Not Embedded" }, + ] + if (serviceOptions.redirectType == "sub_frame") { + serviceOptions.redirectType = "main_frame" + options.set(_options) + } + } else { + values = [{ value: "main_frame", name: "Only Not Embedded" }] + serviceOptions.redirectType = "main_frame" + options.set(_options) + } + + let embeddableFrontends = [] + $: if (serviceConf) { + embeddableFrontends = [] + for (const [frontendId, frontendConf] of Object.entries(serviceConf.frontends)) { + if (frontendConf.embeddable && frontendConf.instanceList) { + embeddableFrontends.push({ + value: frontendId, + label: frontendConf.name, + }) + } + } + } +</script> + +<RowSelect + label="Redirect Type" + value={serviceOptions.redirectType} + onChange={e => { + serviceOptions.redirectType = e.target.options[e.target.options.selectedIndex].value + options.set(_options) + }} + {values} +/> + +{#if serviceConf.frontends[frontendName].desktopApp && serviceOptions.redirectType != "main_frame"} + <Row> + <Label>Embed Frontend</Label> + <SvelteSelect + clearable={false} + class="svelte_select" + value={serviceOptions.embedFrontend} + on:change={e => { + serviceOptions.embedFrontend = e.detail.value + options.set(_options) + }} + items={embeddableFrontends} + > + <div class="slot" slot="item" let:item> + <FrontendIcon details={item} {selectedService} /> + {item.label} + </div> + <div class="slot" slot="selection" let:selection> + <FrontendIcon details={selection} {selectedService} /> + {selection.label} + </div> + </SvelteSelect> + </Row> +{/if} diff --git a/src/pages/options_src/Services/ServiceIcon.svelte b/src/pages/options_src/Services/ServiceIcon.svelte new file mode 100644 index 00000000..89393cf6 --- /dev/null +++ b/src/pages/options_src/Services/ServiceIcon.svelte @@ -0,0 +1,40 @@ +<script> + import { onDestroy } from "svelte" + export let details + import { config, options } from "../stores" + let _options + let _config + + const unsubscribeOptions = options.subscribe(val => (_options = val)) + const unsubscribeConfig = config.subscribe(val => (_config = val)) + onDestroy(() => { + unsubscribeOptions() + unsubscribeConfig() + }) + + let theme + $: if (_options) { + if (_options.theme == "dark") { + theme = "dark" + } else if (_options.theme == "light") { + theme = "light" + } else if (window.matchMedia("(prefers-color-scheme: dark)").matches) { + theme = "dark" + } else { + theme = "light" + } + } + $: imageType = _config.services[details.value].imageType +</script> + +{#if imageType} + {#if imageType == "svgMono"} + {#if theme == "dark"} + <img src={`/assets/images/${details.value}-icon-light.svg`} alt={details.label} /> + {:else} + <img src={`/assets/images/${details.value}-icon.svg`} alt={details.label} /> + {/if} + {:else} + <img src={`/assets/images/${details.value}-icon.${imageType}`} alt={details.label} /> + {/if} +{/if} diff --git a/src/pages/options_src/Services/Services.svelte b/src/pages/options_src/Services/Services.svelte new file mode 100644 index 00000000..cb1efacb --- /dev/null +++ b/src/pages/options_src/Services/Services.svelte @@ -0,0 +1,188 @@ +<script> + import Checkbox from "../../components/RowCheckbox.svelte" + import RowSelect from "../../components/RowSelect.svelte" + import Row from "../../components/Row.svelte" + import Label from "../../components/Label.svelte" + import Select from "../../components/Select.svelte" + import { options, config } from "../stores" + import RedirectType from "./RedirectType.svelte" + import { onDestroy } from "svelte" + import Instances from "./Instances.svelte" + import SvelteSelect from "svelte-select" + import ServiceIcon from "./ServiceIcon.svelte" + import FrontendIcon from "./FrontendIcon.svelte" + + let _options + let _config + + const unsubscribeOptions = options.subscribe(val => (_options = val)) + const unsubscribeConfig = config.subscribe(val => (_config = val)) + onDestroy(() => { + unsubscribeOptions() + unsubscribeConfig() + }) + + let selectedService = "youtube" + $: serviceConf = _config.services[selectedService] + $: serviceOptions = _options[selectedService] + $: frontendWebsite = serviceConf.frontends[serviceOptions.frontend].url +</script> + +<div> + <Row> + <Label> + Service: + <a href={serviceConf.url} target="_blank" rel="noopener noreferrer">{serviceConf.url}</a> + </Label> + <SvelteSelect + clearable={false} + class="svelte_select" + value={selectedService} + on:change={e => (selectedService = e.detail.value)} + items={[ + ...Object.entries(_config.services).map(([serviceKey, service]) => { + return { value: serviceKey, label: service.name } + }), + ]} + > + <div class="slot" slot="item" let:item> + <ServiceIcon details={item} /> + {item.label} + </div> + <div class="slot" slot="selection" let:selection> + <ServiceIcon details={selection} /> + {selection.label} + </div> + </SvelteSelect> + </Row> + + <hr /> + + <Checkbox + label="Enable" + checked={serviceOptions.enabled} + onChange={e => { + serviceOptions.enabled = e.target.checked + options.set(_options) + }} + /> + + <div style={!serviceOptions.enabled && "pointer-events: none;opacity: 0.4;user-select: none;"}> + <Checkbox + label="Show in popup" + checked={_options.popupServices.includes(selectedService)} + onChange={e => { + if (e.target.checked && !_options.popupServices.includes(selectedService)) { + _options.popupServices.push(selectedService) + } else if (_options.popupServices.includes(selectedService)) { + const index = _options.popupServices.indexOf(selectedService) + if (index !== -1) _options.popupServices.splice(index, 1) + } + options.set(_options) + }} + /> + + <Row> + <Label> + Frontend: + <a href={frontendWebsite} target="_blank" rel="noopener noreferrer"> + {frontendWebsite} + </a> + </Label> + + <SvelteSelect + clearable={false} + class="svelte_select" + value={serviceOptions.frontend} + on:change={e => { + serviceOptions.frontend = e.detail.value + options.set(_options) + }} + items={[ + ...Object.entries(serviceConf.frontends).map(([frontendId, frontend]) => ({ + value: frontendId, + label: frontend.name, + })), + ]} + > + <div class="slot" slot="item" let:item> + <FrontendIcon details={item} {selectedService} /> + {item.label} + </div> + <div class="slot" slot="selection" let:selection> + <FrontendIcon details={selection} {selectedService} /> + {selection.label} + </div> + </SvelteSelect> + </Row> + + <RedirectType {selectedService} /> + + <RowSelect + label="Unsupported embeds handling" + value={serviceOptions.unsupportedUrls} + onChange={e => { + serviceOptions.unsupportedUrls = e.target.options[e.target.options.selectedIndex].value + options.set(_options) + }} + values={[ + { value: "bypass", name: "Bypass" }, + { value: "block", name: "Block" }, + ]} + /> + + {#if selectedService == "search"} + <Row> + <Label> + Set LibRedirect as Default Search Engine. For how to do in chromium browsers, click + <a + href="https://libredirect.github.io/docs.html#search_engine_chromium" + target="_blank" + rel="noopener noreferrer" + >here + </a>. + </Label> + </Row> + {/if} + + <Instances + {selectedService} + selectedFrontend={!serviceConf.frontends[serviceOptions.frontend].desktopApp || + serviceOptions.redirectType == "main_frame" + ? serviceOptions.frontend + : serviceOptions.embedFrontend} + /> + + <Row></Row> + </div> +</div> + +<style> + :global(.svelte_select) { + font-weight: bold; + --item-padding: 0 10px; + --border: none; + --border-hover: none; + --width: 210px; + --background: var(--bg-secondary); + --list-background: var(--bg-secondary); + --item-active-background: red; + --item-is-active-bg: grey; + --item-hover-bg: grey; + --item-is-active-color: var(--text); + --padding: 0 0 0 10px; + --item-color: var(--text); + } + :global(.svelte_select .slot) { + display: flex; + justify-content: start; + align-items: center; + } + + :global(.svelte_select img, .svelte_select svg) { + margin-right: 10px; + height: 26px; + width: 26px; + color: var(--text); + } +</style> diff --git a/src/pages/options_src/Sidebar.svelte b/src/pages/options_src/Sidebar.svelte new file mode 100644 index 00000000..ff44995f --- /dev/null +++ b/src/pages/options_src/Sidebar.svelte @@ -0,0 +1,43 @@ +<script> + import { page } from "./stores" + import GeneralIcon from "../icons/GeneralIcon.svelte" + import ServicesIcon from "../icons/ServicesIcon.svelte" + import AboutIcon from "../icons/AboutIcon.svelte" +</script> + +<div> + <a href="#general" on:click={() => page.set("general")} style={$page == "general" && "color: var(--active);"}> + <GeneralIcon style="margin-right: 5px" /> + <span data-localise="__MSG_general__">General</span> + </a> + <a href="#services" on:click={() => page.set("services")} style={$page == "services" && "color: var(--active);"}> + <ServicesIcon style="margin-right: 5px" /> + <span data-localise="__MSG_services__">Services</span> + </a> + <a href="https://libredirect.github.io" target="_blank" rel="noopener noreferrer"> + <AboutIcon style="margin-right: 5px" /> + <span data-localise="__MSG_about__">About</span> + </a> +</div> + +<style> + div { + display: flex; + flex-direction: column; + margin: 0 20px; + } + + a { + display: flex; + align-items: center; + font-size: 18px; + text-decoration: none; + color: var(--text); + transition: 0.1s; + margin: 10px; + } + + a:hover { + color: var(--active); + } +</style> diff --git a/src/pages/options_src/main.js b/src/pages/options_src/main.js new file mode 100644 index 00000000..c4012f4a --- /dev/null +++ b/src/pages/options_src/main.js @@ -0,0 +1,7 @@ +import App from "./App.svelte" + +const app = new App({ + target: document.body, +}) + +export default app diff --git a/src/pages/options_src/stores.js b/src/pages/options_src/stores.js new file mode 100644 index 00000000..782f6064 --- /dev/null +++ b/src/pages/options_src/stores.js @@ -0,0 +1,5 @@ +import { writable } from "svelte/store" + +export const options = writable(null) +export const config = writable(null) +export const page = writable("general") |