mirror of
https://github.com/BetterSEQTA/BetterSEQTA-Plus.git
synced 2026-06-06 11:44:40 +00:00
feat(settings): add working shortcuts and custom shortcuts
This commit is contained in:
+3
-2
@@ -70,7 +70,7 @@
|
|||||||
"@uiw/codemirror-extensions-color": "^4.21.25",
|
"@uiw/codemirror-extensions-color": "^4.21.25",
|
||||||
"@uiw/codemirror-theme-github": "^4.21.25",
|
"@uiw/codemirror-theme-github": "^4.21.25",
|
||||||
"@uiw/react-codemirror": "^4.21.25",
|
"@uiw/react-codemirror": "^4.21.25",
|
||||||
"autoprefixer": "^10.4.17",
|
"autoprefixer": "^10.4.20",
|
||||||
"classnames": "^2.5.1",
|
"classnames": "^2.5.1",
|
||||||
"color": "^4.2.3",
|
"color": "^4.2.3",
|
||||||
"dompurify": "^3.0.8",
|
"dompurify": "^3.0.8",
|
||||||
@@ -80,6 +80,7 @@
|
|||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"million": "latest",
|
"million": "latest",
|
||||||
"motion": "^10.18.0",
|
"motion": "^10.18.0",
|
||||||
|
"postcss": "^8.4.45",
|
||||||
"publish-browser-extension": "^2.2.1",
|
"publish-browser-extension": "^2.2.1",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-best-gradient-color-picker": "3.0.5",
|
"react-best-gradient-color-picker": "3.0.5",
|
||||||
@@ -93,7 +94,7 @@
|
|||||||
"svelte-hash-router": "^1.0.1",
|
"svelte-hash-router": "^1.0.1",
|
||||||
"svelte-motion": "^0.12.2",
|
"svelte-motion": "^0.12.2",
|
||||||
"swiper": "latest",
|
"swiper": "latest",
|
||||||
"tailwindcss": "^3.4.1",
|
"tailwindcss": "^3.4.10",
|
||||||
"ts-loader": "^9.5.1",
|
"ts-loader": "^9.5.1",
|
||||||
"typescript": "^5.5.4",
|
"typescript": "^5.5.4",
|
||||||
"uuid": "^9.0.1",
|
"uuid": "^9.0.1",
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
export default {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
autoprefixer: {},
|
||||||
|
},
|
||||||
|
}
|
||||||
+7
-3
@@ -48,7 +48,6 @@ var IsSEQTAPage = false
|
|||||||
const hasSEQTAText = document.childNodes[1].textContent?.includes('Copyright (c) SEQTA Software')
|
const hasSEQTAText = document.childNodes[1].textContent?.includes('Copyright (c) SEQTA Software')
|
||||||
init()
|
init()
|
||||||
|
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
CheckForMenuList()
|
CheckForMenuList()
|
||||||
const hasSEQTATitle = document.title.includes('SEQTA Learn')
|
const hasSEQTATitle = document.title.includes('SEQTA Learn')
|
||||||
@@ -67,6 +66,8 @@ async function init() {
|
|||||||
// wait until settingsState has been loaded from storage
|
// wait until settingsState has been loaded from storage
|
||||||
await initializeSettingsState();
|
await initializeSettingsState();
|
||||||
|
|
||||||
|
console.log(settingsState.onoff)
|
||||||
|
|
||||||
if (settingsState.onoff) {
|
if (settingsState.onoff) {
|
||||||
enableCurrentTheme()
|
enableCurrentTheme()
|
||||||
|
|
||||||
@@ -979,9 +980,12 @@ export function addExtensionSettings() {
|
|||||||
extensionPopup.appendChild(extensionIframe) */
|
extensionPopup.appendChild(extensionIframe) */
|
||||||
|
|
||||||
// create shadow dom and render svelte app
|
// create shadow dom and render svelte app
|
||||||
|
try {
|
||||||
const shadow = extensionPopup.attachShadow({ mode: 'open' });
|
const shadow = extensionPopup.attachShadow({ mode: 'open' });
|
||||||
const svelteApp = initSvelteInterface(shadow);
|
initSvelteInterface(shadow);
|
||||||
console.log(svelteApp)
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
}
|
||||||
|
|
||||||
const container = document.getElementById('container')
|
const container = document.getElementById('container')
|
||||||
|
|
||||||
|
|||||||
@@ -46,6 +46,7 @@
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<div class="flex flex-col h-full">
|
||||||
<div bind:this={containerRef} class="top-0 z-10 text-[0.875rem] pb-0.5 mx-4 tab-width-container">
|
<div bind:this={containerRef} class="top-0 z-10 text-[0.875rem] pb-0.5 mx-4 tab-width-container">
|
||||||
<div class="relative flex">
|
<div class="relative flex">
|
||||||
<MotionDiv
|
<MotionDiv
|
||||||
@@ -67,12 +68,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="h-full px-4 overflow-y-scroll overflow-x-clip">
|
<div class="h-full px-4 overflow-y-scroll overflow-x-clip">
|
||||||
<MotionDiv
|
<MotionDiv
|
||||||
|
class="h-full"
|
||||||
animate={{ x: `${-activeTab * 100}%` }}
|
animate={{ x: `${-activeTab * 100}%` }}
|
||||||
transition={springTransition}
|
transition={springTransition}
|
||||||
>
|
>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
{#each tabs as { Content }, index}
|
{#each tabs as { Content }, index}
|
||||||
<div class="absolute w-full transition-opacity duration-300 pb-4 {activeTab === index ? 'opacity-100' : 'opacity-0'}"
|
<div class="absolute w-full transition-opacity duration-300 overflow-y-scroll {activeTab === index ? 'opacity-100' : 'opacity-0'}"
|
||||||
style="left: {index * 100}%;">
|
style="left: {index * 100}%;">
|
||||||
<Content />
|
<Content />
|
||||||
</div>
|
</div>
|
||||||
@@ -80,3 +82,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</MotionDiv>
|
</MotionDiv>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
@@ -19,3 +19,9 @@
|
|||||||
.tab-width {
|
.tab-width {
|
||||||
width: var(--tab-width);
|
width: var(--tab-width);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
&:focus {
|
||||||
|
box-shadow: unset !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
import { createStandalone } from '../utils/standalone.svelte';
|
import { createStandalone } from '../utils/standalone.svelte';
|
||||||
import { onMount } from 'svelte'
|
import { onMount } from 'svelte'
|
||||||
|
import { settingsState } from '@/seqta/utils/listeners/SettingsState'
|
||||||
|
|
||||||
const openChangelog = () => {
|
const openChangelog = () => {
|
||||||
browser.runtime.sendMessage({ type: 'currentTab', info: 'OpenChangelog' });
|
browser.runtime.sendMessage({ type: 'currentTab', info: 'OpenChangelog' });
|
||||||
@@ -21,7 +22,8 @@
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="relative flex flex-col w-[384px] shadow-2xl gap-2 bg-white { standalone ? 'h-[600px]' : 'h-[100vh] rounded-xl' } overflow-clip dark:bg-zinc-800 dark:text-white">
|
<div class="w-[384px] shadow-2xl {$settingsState.DarkMode ? 'dark' : ''} { standalone ? 'h-[600px]' : 'h-full rounded-xl' } overflow-clip">
|
||||||
|
<div class="relative flex flex-col h-full gap-2 bg-white overflow-clip dark:bg-zinc-800 dark:text-white">
|
||||||
<div class="grid border-b border-b-zinc-200/40 place-items-center">
|
<div class="grid border-b border-b-zinc-200/40 place-items-center">
|
||||||
<img src={browser.runtime.getURL('resources/icons/betterseqta-dark-full.png')} class="w-4/5 dark:hidden" alt="Light logo" />
|
<img src={browser.runtime.getURL('resources/icons/betterseqta-dark-full.png')} class="w-4/5 dark:hidden" alt="Light logo" />
|
||||||
<img src={browser.runtime.getURL('resources/icons/betterseqta-light-full.png')} class="hidden w-4/5 dark:block" alt="Dark logo" />
|
<img src={browser.runtime.getURL('resources/icons/betterseqta-light-full.png')} class="hidden w-4/5 dark:block" alt="Dark logo" />
|
||||||
@@ -34,3 +36,4 @@
|
|||||||
{ title: 'Themes', Content: Theme },
|
{ title: 'Themes', Content: Theme },
|
||||||
]} />
|
]} />
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
|
|
||||||
<div class="flex flex-col -mt-4 overflow-y-scroll divide-y divide-zinc-100 dark:divide-zinc-700">
|
<div class="flex flex-col divide-y divide-zinc-100 dark:divide-zinc-700">
|
||||||
{#each [
|
{#each [
|
||||||
{
|
{
|
||||||
title: "Transparency Effects",
|
title: "Transparency Effects",
|
||||||
|
|||||||
@@ -1,25 +1,130 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
// @ts-expect-error umm idk
|
||||||
|
import { MotionDiv } from 'svelte-motion';
|
||||||
import { settingsState } from "@/seqta/utils/listeners/SettingsState.ts"
|
import { settingsState } from "@/seqta/utils/listeners/SettingsState.ts"
|
||||||
|
import Switch from "@/svelte-interface/components/Switch.svelte"
|
||||||
|
|
||||||
|
const switchChange = (index: number) => {
|
||||||
|
const updatedShortcuts = [...settingsState.shortcuts];
|
||||||
|
updatedShortcuts[index].enabled = !updatedShortcuts[index].enabled;
|
||||||
|
settingsState.shortcuts = updatedShortcuts;
|
||||||
|
}
|
||||||
|
|
||||||
|
let isFormVisible = $state(false);
|
||||||
|
let newTitle = $state("");
|
||||||
|
let newURL = $state("");
|
||||||
|
|
||||||
|
const toggleForm = () => {
|
||||||
|
isFormVisible = !isFormVisible;
|
||||||
|
};
|
||||||
|
|
||||||
|
const formatUrl = (inputUrl: string) => {
|
||||||
|
const protocolRegex = /^(http:\/\/|https:\/\/|ftp:\/\/)/;
|
||||||
|
return protocolRegex.test(inputUrl) ? inputUrl : `https://${inputUrl}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const isValidTitle = (title: string) => title.trim() !== "";
|
||||||
|
|
||||||
|
const isValidURL = (url: string) => {
|
||||||
|
const pattern = new RegExp("^(https?:\\/\\/)?[\\w.-]+(?:\\.[\\w\\-]+)*(?::\\d+)?(/[\\w\\-./]*)*$", "i");
|
||||||
|
return pattern.test(url);
|
||||||
|
};
|
||||||
|
|
||||||
|
const addNewCustomShortcut = () => {
|
||||||
|
if (isValidTitle(newTitle) && isValidURL(newURL)) {
|
||||||
|
const newShortcut = { name: newTitle.trim(), url: formatUrl(newURL).trim(), icon: newTitle[0] };
|
||||||
|
settingsState.customshortcuts = [...settingsState.customshortcuts, newShortcut];
|
||||||
|
|
||||||
|
newTitle = "";
|
||||||
|
newURL = "";
|
||||||
|
isFormVisible = false;
|
||||||
|
} else {
|
||||||
|
alert("Please enter a valid title and URL.");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteCustomShortcut = (index: number) => {
|
||||||
|
settingsState.customshortcuts = settingsState.customshortcuts.filter((_, i) => i !== index);
|
||||||
|
};
|
||||||
|
|
||||||
|
const springTransition = { type: 'spring', damping: 20 };
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#snippet Shortcuts([string, Shortcut]) }
|
{#snippet Shortcuts([index, Shortcut]: [string, { name: string, enabled: boolean }]) }
|
||||||
<div class="flex items-center justify-between px-4 py-3">
|
<div class="flex items-center justify-between px-4 py-3">
|
||||||
<div class="pr-4">
|
<div class="pr-4">
|
||||||
<h2 class="text-sm font-bold">{Shortcut.name}</h2>
|
<h2 class="text-sm">{Shortcut.name}</h2>
|
||||||
<p class="text-xs">{Shortcut.enabled}</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
<Switch state={Shortcut.enabled} onChange={() => switchChange(parseInt(index))} />
|
||||||
</div>
|
</div>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
|
|
||||||
|
<div class="flex flex-col pt-4 divide-y divide-zinc-100 dark:divide-zinc-700">
|
||||||
|
<div>
|
||||||
|
<MotionDiv
|
||||||
|
initial={{ opacity: 0, height: 0 }}
|
||||||
|
animate={isFormVisible ? { opacity: 1, height: "auto" } : { opacity: 0, height: 0 }}
|
||||||
|
exit={{ opacity: 0, height: 0 }}
|
||||||
|
transition={springTransition}
|
||||||
|
>
|
||||||
|
{#if isFormVisible}
|
||||||
|
<div class="flex flex-col items-center">
|
||||||
|
<MotionDiv
|
||||||
|
initial={{ opacity: 0, y: -10 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
transition={{ delay: 0.1, duration: 0.4 }}
|
||||||
|
class="w-full"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
class="w-full p-2 transition border-0 rounded-lg placeholder-zinc-300 bg-zinc-100 dark:bg-zinc-700 focus:bg-zinc-200/50 dark:focus:bg-zinc-600"
|
||||||
|
type="text"
|
||||||
|
placeholder="Shortcut Name"
|
||||||
|
bind:value={newTitle}
|
||||||
|
/>
|
||||||
|
</MotionDiv>
|
||||||
|
<MotionDiv
|
||||||
|
initial={{ opacity: 0, y: -10 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
transition={{ delay: 0.2, duration: 0.4 }}
|
||||||
|
class="w-full"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
class="w-full p-2 my-2 transition border-0 rounded-lg placeholder-zinc-300 bg-zinc-100 dark:bg-zinc-700 focus:bg-zinc-200/50 dark:focus:bg-zinc-600"
|
||||||
|
type="text"
|
||||||
|
placeholder="URL eg. https://google.com"
|
||||||
|
bind:value={newURL}
|
||||||
|
/>
|
||||||
|
</MotionDiv>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</MotionDiv>
|
||||||
|
|
||||||
<div class="text-xl">shortcuts tab</div>
|
<MotionDiv
|
||||||
|
animate={isFormVisible ? { y: 0 } : { y: 0 }}
|
||||||
|
transition={springTransition}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="w-full px-4 py-2 mb-4 text-white transition rounded-xl bg-zinc-700/50"
|
||||||
|
onclick={isFormVisible ? addNewCustomShortcut : toggleForm}
|
||||||
|
>
|
||||||
|
{isFormVisible ? 'Add' : 'Add Custom Shortcut'}
|
||||||
|
</button>
|
||||||
|
</MotionDiv>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-col -mt-4 overflow-y-scroll divide-y divide-zinc-100 dark:divide-zinc-700">
|
{#each Object.entries($settingsState.shortcuts) as shortcut}
|
||||||
{#each Object.entries(settingsState.shortcuts) as shortcut}
|
|
||||||
<!-- do processing stuff here -->
|
|
||||||
{@render Shortcuts(shortcut)}
|
{@render Shortcuts(shortcut)}
|
||||||
{/each}
|
{/each}
|
||||||
|
|
||||||
|
<!-- Custom Shortcuts Section -->
|
||||||
|
{#each $settingsState.customshortcuts as shortcut, index}
|
||||||
|
<div class="flex items-center justify-between px-4 py-3">
|
||||||
|
{shortcut.name}
|
||||||
|
<button onclick={() => deleteCustomShortcut(index)}>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width={1.5} stroke="currentColor" class="w-6 h-6">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
+6
-1
@@ -5,7 +5,12 @@ const {
|
|||||||
/** @type {import('tailwindcss').Config} */
|
/** @type {import('tailwindcss').Config} */
|
||||||
export default {
|
export default {
|
||||||
content: [
|
content: [
|
||||||
"./src/**/*.{js,ts,jsx,tsx,html}",
|
"./src/**/*.{js,ts,jsx,tsx,html,svelte}",
|
||||||
|
],
|
||||||
|
safelist: [
|
||||||
|
{
|
||||||
|
pattern: / */,
|
||||||
|
}
|
||||||
],
|
],
|
||||||
darkMode: "class",
|
darkMode: "class",
|
||||||
theme: {
|
theme: {
|
||||||
|
|||||||
Reference in New Issue
Block a user