mirror of
https://github.com/BetterSEQTA/BetterSEQTA-Plus.git
synced 2026-08-28 17:21:05 +00:00
feat: sidebar customisaitons and settings fixes
This commit is contained in:
@@ -0,0 +1,577 @@
|
||||
<script lang="ts">
|
||||
import Select from "@/interface/components/Select.svelte";
|
||||
import Slider from "@/interface/components/Slider.svelte";
|
||||
import { settingsState } from "@/seqta/utils/listeners/SettingsState";
|
||||
import {
|
||||
DEFAULT_SIDEBAR_BLUR,
|
||||
DEFAULT_SIDEBAR_RADIUS,
|
||||
getSidebarStyle,
|
||||
normalizeSidebarBlur,
|
||||
normalizeSidebarDensity,
|
||||
normalizeSidebarIndicator,
|
||||
normalizeSidebarRadius,
|
||||
normalizeSidebarStyleId,
|
||||
normalizeSidebarWidth,
|
||||
SIDEBAR_STYLES,
|
||||
type SidebarStyleId,
|
||||
} from "@/seqta/ui/sidebar/sidebarStyles";
|
||||
|
||||
const PREVIEW_ITEMS = [
|
||||
{ label: "Home", active: true },
|
||||
{ label: "Timetable", active: false },
|
||||
{ label: "Assessments", active: false },
|
||||
{ label: "Messages", active: false },
|
||||
{ label: "Documents", active: false },
|
||||
] as const;
|
||||
|
||||
const selectedId = $derived(
|
||||
normalizeSidebarStyleId($settingsState.sidebarStyle),
|
||||
);
|
||||
const selected = $derived(getSidebarStyle(selectedId));
|
||||
const density = $derived(
|
||||
normalizeSidebarDensity($settingsState.sidebarDensity),
|
||||
);
|
||||
const indicator = $derived(
|
||||
normalizeSidebarIndicator($settingsState.sidebarActiveIndicator),
|
||||
);
|
||||
const width = $derived(normalizeSidebarWidth($settingsState.sidebarWidth));
|
||||
const radius = $derived(
|
||||
normalizeSidebarRadius(
|
||||
$settingsState.sidebarCornerRadius ?? DEFAULT_SIDEBAR_RADIUS,
|
||||
),
|
||||
);
|
||||
const blur = $derived(
|
||||
normalizeSidebarBlur($settingsState.sidebarBlur ?? DEFAULT_SIDEBAR_BLUR),
|
||||
);
|
||||
const transparencyOn = $derived($settingsState.transparencyEffects === true);
|
||||
|
||||
function selectStyle(id: SidebarStyleId) {
|
||||
if (selectedId !== id) settingsState.sidebarStyle = id;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="card">
|
||||
<header class="card-header split">
|
||||
<div>
|
||||
<h2 class="title">Sidebar Style</h2>
|
||||
<p class="subtitle">Choose how the navigation menu looks</p>
|
||||
</div>
|
||||
<div class="selected-meta" aria-live="polite">
|
||||
<span class="selected-label">{selected.label}</span>
|
||||
<span class="selected-desc">{selected.description}</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="picker-body">
|
||||
<div class="preview-pane" aria-hidden="true">
|
||||
<div class={`preview style-${selectedId}`}>
|
||||
<div class="preview-chrome">
|
||||
<div class="preview-logo"></div>
|
||||
{#each PREVIEW_ITEMS as item (item.label)}
|
||||
<div class="preview-item" class:active={item.active}>
|
||||
<span class="preview-dot"></span>
|
||||
<span class="preview-label">{item.label}</span>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="options"
|
||||
role="listbox"
|
||||
tabindex="0"
|
||||
aria-label="Sidebar styles"
|
||||
aria-activedescendant={`sidebar-style-${selectedId}`}
|
||||
>
|
||||
{#each SIDEBAR_STYLES as style (style.id)}
|
||||
{@const active = style.id === selectedId}
|
||||
<button
|
||||
type="button"
|
||||
id={`sidebar-style-${style.id}`}
|
||||
class="option"
|
||||
class:active
|
||||
role="option"
|
||||
aria-selected={active}
|
||||
onclick={() => selectStyle(style.id)}
|
||||
>
|
||||
<div class={`thumb style-${style.id}`}>
|
||||
<div class="thumb-chrome">
|
||||
{#each PREVIEW_ITEMS.slice(0, 3) as item (item.label)}
|
||||
<div class="thumb-item" class:active={item.active}></div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
<span class="option-label">{style.label}</span>
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<h2 class="title">Sidebar Look</h2>
|
||||
<p class="subtitle">Density, size, and active-state details</p>
|
||||
</header>
|
||||
|
||||
<div class="rows">
|
||||
<div class="row">
|
||||
<div class="copy">
|
||||
<h3 class="row-title">Item Size</h3>
|
||||
<p class="row-desc">Spacing and type size for menu rows</p>
|
||||
</div>
|
||||
<div class="control">
|
||||
<Select
|
||||
value={density}
|
||||
onChange={(value) => (settingsState.sidebarDensity = value)}
|
||||
options={[
|
||||
{ value: "compact", label: "Compact" },
|
||||
{ value: "comfortable", label: "Comfortable" },
|
||||
{ value: "large", label: "Large" },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="copy">
|
||||
<h3 class="row-title">Corner Radius</h3>
|
||||
<p class="row-desc">Roundness of menu items ({radius}px)</p>
|
||||
</div>
|
||||
<div class="control slider">
|
||||
<Slider
|
||||
state={radius}
|
||||
min={0}
|
||||
max={24}
|
||||
step={1}
|
||||
onChange={(value) => (settingsState.sidebarCornerRadius = value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="copy">
|
||||
<h3 class="row-title">Active Indicator</h3>
|
||||
<p class="row-desc">How the current page is highlighted</p>
|
||||
</div>
|
||||
<div class="control">
|
||||
<Select
|
||||
value={indicator}
|
||||
onChange={(value) => (settingsState.sidebarActiveIndicator = value)}
|
||||
options={[
|
||||
{ value: "fill", label: "Fill" },
|
||||
{ value: "bar", label: "Left bar" },
|
||||
{ value: "outline", label: "Outline" },
|
||||
{ value: "underline", label: "Underline" },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="copy">
|
||||
<h3 class="row-title">Sidebar Width</h3>
|
||||
<p class="row-desc">Overall navigation column width</p>
|
||||
</div>
|
||||
<div class="control">
|
||||
<Select
|
||||
value={width}
|
||||
onChange={(value) => (settingsState.sidebarWidth = value)}
|
||||
options={[
|
||||
{ value: "narrow", label: "Narrow" },
|
||||
{ value: "default", label: "Default" },
|
||||
{ value: "wide", label: "Wide" },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" class:disabled={!transparencyOn}>
|
||||
<div class="copy">
|
||||
<h3 class="row-title">Blur Strength</h3>
|
||||
<p class="row-desc">
|
||||
{#if transparencyOn}
|
||||
Glass blur on the sidebar ({blur}px)
|
||||
{:else}
|
||||
Enable Transparency Effects to use blur
|
||||
{/if}
|
||||
</p>
|
||||
</div>
|
||||
<div class="control slider">
|
||||
<Slider
|
||||
state={blur}
|
||||
min={0}
|
||||
max={80}
|
||||
step={1}
|
||||
onChange={(value) => (settingsState.sidebarBlur = value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.card {
|
||||
margin: 4px 0;
|
||||
padding: 4px;
|
||||
border-radius: 0.75rem;
|
||||
border: 1px solid rgb(228 228 231 / 0.5);
|
||||
background: linear-gradient(to bottom right, white, rgb(244 244 245));
|
||||
box-shadow: 0 1px 2px rgb(0 0 0 / 0.05);
|
||||
}
|
||||
|
||||
:global(.dark) .card {
|
||||
border-color: rgb(63 63 70 / 0.4);
|
||||
background: linear-gradient(
|
||||
to bottom right,
|
||||
rgb(24 24 27 / 0.4),
|
||||
rgb(24 24 27 / 0.5)
|
||||
);
|
||||
}
|
||||
|
||||
.card-header {
|
||||
padding: 1rem 1.25rem 0.5rem;
|
||||
}
|
||||
|
||||
.card-header.split {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
margin-top: 0.15rem;
|
||||
font-size: 1rem;
|
||||
color: rgb(82 82 91);
|
||||
}
|
||||
|
||||
:global(.dark) .subtitle {
|
||||
color: rgb(212 212 216);
|
||||
}
|
||||
|
||||
.selected-meta {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
text-align: right;
|
||||
max-width: 14rem;
|
||||
gap: 0.15rem;
|
||||
}
|
||||
|
||||
.selected-label {
|
||||
font-size: 0.95rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.selected-desc {
|
||||
font-size: 0.8rem;
|
||||
line-height: 1.35;
|
||||
color: rgb(113 113 122);
|
||||
}
|
||||
|
||||
:global(.dark) .selected-desc {
|
||||
color: rgb(161 161 170);
|
||||
}
|
||||
|
||||
.picker-body {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(160px, 210px) 1fr;
|
||||
gap: 1rem;
|
||||
padding: 0.5rem 1rem 1rem;
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.picker-body {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.preview-pane {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.preview {
|
||||
width: 100%;
|
||||
max-width: 196px;
|
||||
height: 280px;
|
||||
border-radius: 18px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 14px 32px rgb(0 0 0 / 0.22);
|
||||
background: linear-gradient(160deg, #aa053a, #141414 70%);
|
||||
}
|
||||
|
||||
.preview-chrome {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
padding: 14px 0 10px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.preview-logo {
|
||||
width: 42%;
|
||||
height: 10px;
|
||||
margin: 0 16px 14px;
|
||||
border-radius: 999px;
|
||||
background: rgb(255 255 255 / 0.35);
|
||||
}
|
||||
|
||||
.preview-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin: 0 8px 6px;
|
||||
padding: 8px 10px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.preview-item.active {
|
||||
background: rgba(0, 0, 0, 0.35);
|
||||
}
|
||||
|
||||
.preview.style-soft .preview-item {
|
||||
margin: 0 10px 8px;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.18);
|
||||
}
|
||||
|
||||
.preview.style-pill .preview-item {
|
||||
margin: 0 12px 7px;
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
.preview.style-glass .preview-item {
|
||||
margin: 0 10px 7px;
|
||||
background: rgba(255, 255, 255, 0.12);
|
||||
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.16);
|
||||
}
|
||||
|
||||
.preview.style-glass .preview-item.active {
|
||||
background: rgba(255, 255, 255, 0.22);
|
||||
}
|
||||
|
||||
.preview.style-sharp .preview-item {
|
||||
margin: 0 6px 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.preview.style-strip .preview-item {
|
||||
margin: 0;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.preview.style-strip .preview-item.active {
|
||||
position: relative;
|
||||
background: rgba(255, 255, 255, 0.12);
|
||||
}
|
||||
|
||||
.preview.style-strip .preview-item.active::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 6px;
|
||||
bottom: 6px;
|
||||
width: 3px;
|
||||
border-radius: 2px;
|
||||
background: #fff;
|
||||
box-shadow: 0 0 8px rgba(255, 255, 255, 0.55);
|
||||
}
|
||||
|
||||
.preview.style-neon .preview-item.active {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(255, 255, 255, 0.55),
|
||||
0 0 12px rgba(255, 255, 255, 0.4),
|
||||
0 0 22px rgba(255, 255, 255, 0.22);
|
||||
}
|
||||
|
||||
.preview-dot {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 999px;
|
||||
background: rgb(255 255 255 / 0.75);
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.preview-label {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.options {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
|
||||
gap: 0.65rem;
|
||||
align-content: start;
|
||||
}
|
||||
|
||||
.option {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.4rem;
|
||||
padding: 0.45rem;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 0.75rem;
|
||||
background: rgb(255 255 255 / 0.55);
|
||||
color: inherit;
|
||||
cursor: pointer;
|
||||
transition:
|
||||
transform 0.2s ease,
|
||||
border-color 0.2s ease,
|
||||
box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
:global(.dark) .option {
|
||||
background: rgb(39 39 42 / 0.55);
|
||||
}
|
||||
|
||||
.option:hover {
|
||||
transform: scale(1.02);
|
||||
}
|
||||
|
||||
.option:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.option.active {
|
||||
border-color: var(--theme-primary, #f43f5e);
|
||||
box-shadow: 0 0 0 1px var(--theme-primary, #f43f5e);
|
||||
}
|
||||
|
||||
.option:focus-visible {
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 2px var(--theme-primary, #f43f5e);
|
||||
}
|
||||
|
||||
.thumb {
|
||||
width: 100%;
|
||||
aspect-ratio: 3 / 4;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
background: linear-gradient(160deg, #aa053a, #141414 70%);
|
||||
}
|
||||
|
||||
.thumb-chrome {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
height: 100%;
|
||||
padding: 8px 6px;
|
||||
}
|
||||
|
||||
.thumb-item {
|
||||
height: 10px;
|
||||
border-radius: 6px;
|
||||
background: rgba(255, 255, 255, 0.18);
|
||||
}
|
||||
|
||||
.thumb-item.active {
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
.thumb.style-pill .thumb-item {
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
.thumb.style-sharp .thumb-item {
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.thumb.style-strip .thumb-item {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.thumb.style-strip .thumb-item.active {
|
||||
background: rgba(255, 255, 255, 0.22);
|
||||
box-shadow: inset 3px 0 0 #fff;
|
||||
}
|
||||
|
||||
.thumb.style-glass .thumb-item {
|
||||
background: rgba(255, 255, 255, 0.22);
|
||||
}
|
||||
|
||||
.thumb.style-neon .thumb-item.active {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(255, 255, 255, 0.5),
|
||||
0 0 8px rgba(255, 255, 255, 0.55);
|
||||
}
|
||||
|
||||
.option-label {
|
||||
font-size: 0.8rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.rows {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0.25rem 0.5rem 0.75rem;
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
padding: 0.85rem 0.75rem;
|
||||
border-top: 1px solid rgb(244 244 245);
|
||||
}
|
||||
|
||||
:global(.dark) .row {
|
||||
border-top-color: rgb(63 63 70 / 0.5);
|
||||
}
|
||||
|
||||
.row:first-child {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.row.disabled {
|
||||
opacity: 0.55;
|
||||
}
|
||||
|
||||
.copy {
|
||||
min-width: 0;
|
||||
flex: 1 1 auto;
|
||||
padding-right: 0.5rem;
|
||||
}
|
||||
|
||||
.row-title {
|
||||
font-size: 1.125rem;
|
||||
font-weight: 700;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.row-desc {
|
||||
margin-top: 0.1rem;
|
||||
font-size: 0.95rem;
|
||||
color: rgb(82 82 91);
|
||||
}
|
||||
|
||||
:global(.dark) .row-desc {
|
||||
color: rgb(212 212 216);
|
||||
}
|
||||
|
||||
.control {
|
||||
flex: 0 0 auto;
|
||||
min-width: 9.5rem;
|
||||
max-width: 12rem;
|
||||
}
|
||||
|
||||
.control.slider {
|
||||
min-width: 10rem;
|
||||
width: 11rem;
|
||||
}
|
||||
</style>
|
||||
@@ -24,9 +24,6 @@
|
||||
isGhReleaseUpdateCheckEnabled,
|
||||
type GhReleaseUpdateInfo,
|
||||
} from "@/utils/githubReleaseUpdate";
|
||||
import { getAllPluginSettings } from "@/plugins";
|
||||
import { isSeqtaEngageExperience } from "@/seqta/utils/isSeqtaEngage";
|
||||
|
||||
type PageId = "settings" | "shortcuts" | "themes";
|
||||
|
||||
type NavItem = {
|
||||
@@ -37,6 +34,37 @@
|
||||
let devModeSequence = "";
|
||||
let activePage = $state<PageId>("settings");
|
||||
let activeSection = $state("general");
|
||||
let navTrackEl = $state<HTMLElement | null>(null);
|
||||
let indicatorY = $state(0);
|
||||
let indicatorH = $state(40);
|
||||
let indicatorReady = $state(false);
|
||||
|
||||
const updateNavIndicator = () => {
|
||||
if (!navTrackEl || activePage !== "settings") {
|
||||
indicatorReady = false;
|
||||
return;
|
||||
}
|
||||
const btn = navTrackEl.querySelector<HTMLElement>(
|
||||
`[data-nav-section="${activeSection}"]`,
|
||||
);
|
||||
if (!btn) {
|
||||
indicatorReady = false;
|
||||
return;
|
||||
}
|
||||
const trackRect = navTrackEl.getBoundingClientRect();
|
||||
const btnRect = btn.getBoundingClientRect();
|
||||
indicatorY = btnRect.top - trackRect.top + navTrackEl.scrollTop;
|
||||
indicatorH = btnRect.height;
|
||||
indicatorReady = true;
|
||||
};
|
||||
|
||||
$effect(() => {
|
||||
activeSection;
|
||||
activePage;
|
||||
navTrackEl;
|
||||
queueMicrotask(updateNavIndicator);
|
||||
});
|
||||
|
||||
let showDisclaimerModal = $state(false);
|
||||
let disclaimerCallbacks = $state<{ onConfirm: () => void; onCancel: () => void } | null>(null);
|
||||
let disclaimerTitle = $state("Confirm");
|
||||
@@ -51,26 +79,18 @@
|
||||
{ id: "themes", title: "Themes" },
|
||||
];
|
||||
|
||||
const pluginNavItems = getAllPluginSettings()
|
||||
.filter((plugin) => !(isSeqtaEngageExperience() && plugin.pluginId === "global-search"))
|
||||
.filter(
|
||||
(plugin) =>
|
||||
(plugin as { disableToggle?: boolean }).disableToggle ||
|
||||
Object.keys(plugin.settings ?? {}).length > 0,
|
||||
)
|
||||
.map((plugin) => ({
|
||||
id: `plugin:${plugin.pluginId}`,
|
||||
label: plugin.name,
|
||||
}));
|
||||
|
||||
const userNav: NavItem[] = [
|
||||
{ id: "account", label: "My Account" },
|
||||
{ id: "general", label: "General" },
|
||||
{ id: "appearance", label: "Appearance" },
|
||||
{ id: "home", label: "Home" },
|
||||
];
|
||||
|
||||
const appNav: NavItem[] = [...pluginNavItems, { id: "advanced", label: "Advanced" }];
|
||||
const appNav: NavItem[] = [
|
||||
{ id: "timetable", label: "Timetable" },
|
||||
{ id: "assessments", label: "Assessments" },
|
||||
{ id: "features", label: "Features" },
|
||||
{ id: "advanced", label: "Advanced" },
|
||||
];
|
||||
|
||||
const sectionTitle = $derived.by(() => {
|
||||
if (activePage === "shortcuts") return "Shortcuts";
|
||||
@@ -209,11 +229,12 @@
|
||||
{#snippet navButton(item: NavItem)}
|
||||
<button
|
||||
type="button"
|
||||
data-nav-section={item.id}
|
||||
onclick={() => selectSection(item.id)}
|
||||
class="w-full px-3 py-2 text-left text-base rounded-lg transition-all duration-200
|
||||
class="relative z-10 w-full px-3 py-2.5 text-left text-lg rounded-lg transition-colors duration-200
|
||||
{activePage === 'settings' && activeSection === item.id
|
||||
? 'bg-zinc-200/80 dark:bg-zinc-700/80 text-zinc-900 dark:text-white font-medium'
|
||||
: 'text-zinc-600 dark:text-zinc-300 hover:bg-zinc-200/50 dark:hover:bg-zinc-700/40 hover:text-zinc-900 dark:hover:text-white'}"
|
||||
? 'text-zinc-900 dark:text-white font-medium'
|
||||
: 'text-zinc-600 dark:text-zinc-300 hover:text-zinc-900 dark:hover:text-white'}"
|
||||
>
|
||||
{item.label}
|
||||
</button>
|
||||
@@ -221,7 +242,7 @@
|
||||
|
||||
{#snippet settingsShell()}
|
||||
<div
|
||||
class="flex flex-col h-full min-h-0 overflow-hidden bg-white dark:bg-zinc-800 dark:text-white {standalone
|
||||
class="flex flex-col h-full min-h-0 overflow-hidden bg-white dark:bg-zinc-800 dark:text-white text-[18px] {standalone
|
||||
? ''
|
||||
: 'rounded-xl shadow-2xl border border-zinc-200/60 dark:border-zinc-700/60'}"
|
||||
>
|
||||
@@ -257,7 +278,7 @@
|
||||
role="tab"
|
||||
aria-selected={activePage === page.id}
|
||||
onclick={() => selectPage(page.id)}
|
||||
class="flex-1 px-4 py-2 text-base rounded-full transition-all duration-200
|
||||
class="flex-1 px-4 py-2.5 text-lg rounded-full transition-all duration-200
|
||||
{activePage === page.id
|
||||
? 'bg-white dark:bg-zinc-700 text-zinc-900 dark:text-white font-semibold shadow-sm'
|
||||
: 'text-zinc-500 dark:text-zinc-400 hover:text-zinc-800 dark:hover:text-zinc-200'}"
|
||||
@@ -334,61 +355,72 @@
|
||||
: 'w-[260px] px-4 py-5'}"
|
||||
aria-label="Settings categories"
|
||||
>
|
||||
{#if activePage === "settings"}
|
||||
<div class="flex flex-col gap-1">
|
||||
<p
|
||||
class="px-3 mb-1.5 text-xs font-semibold tracking-wider uppercase text-zinc-400 dark:text-zinc-500"
|
||||
>
|
||||
User Settings
|
||||
</p>
|
||||
{#each userNav as item (item.id)}
|
||||
{@render navButton(item)}
|
||||
{/each}
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<p
|
||||
class="px-3 mb-1.5 text-xs font-semibold tracking-wider uppercase text-zinc-400 dark:text-zinc-500"
|
||||
>
|
||||
App Settings
|
||||
</p>
|
||||
{#each appNav as item (item.id)}
|
||||
{@render navButton(item)}
|
||||
{/each}
|
||||
</div>
|
||||
{:else if activePage === "shortcuts"}
|
||||
<div class="flex flex-col gap-1">
|
||||
<p
|
||||
class="px-3 mb-1.5 text-xs font-semibold tracking-wider uppercase text-zinc-400 dark:text-zinc-500"
|
||||
>
|
||||
Shortcuts
|
||||
</p>
|
||||
<button
|
||||
type="button"
|
||||
class="w-full px-3 py-2 text-left text-base rounded-lg font-medium bg-zinc-200/80 dark:bg-zinc-700/80 text-zinc-900 dark:text-white"
|
||||
>
|
||||
Shortcuts
|
||||
</button>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex flex-col gap-1">
|
||||
<p
|
||||
class="px-3 mb-1.5 text-xs font-semibold tracking-wider uppercase text-zinc-400 dark:text-zinc-500"
|
||||
>
|
||||
Themes
|
||||
</p>
|
||||
<button
|
||||
type="button"
|
||||
class="w-full px-3 py-2 text-left text-base rounded-lg font-medium bg-zinc-200/80 dark:bg-zinc-700/80 text-zinc-900 dark:text-white"
|
||||
>
|
||||
Themes
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="relative flex flex-col gap-5" bind:this={navTrackEl}>
|
||||
{#if activePage === "settings" && indicatorReady}
|
||||
<div
|
||||
class="absolute left-0 right-0 top-0 z-0 rounded-lg bg-zinc-200/90 dark:bg-zinc-700/90 pointer-events-none"
|
||||
style="transform: translateY({indicatorY}px); height: {indicatorH}px; transition: {$settingsState.animations
|
||||
? 'transform 0.28s cubic-bezier(0.22, 1, 0.36, 1), height 0.28s cubic-bezier(0.22, 1, 0.36, 1)'
|
||||
: 'none'};"
|
||||
></div>
|
||||
{/if}
|
||||
|
||||
{#if activePage === "settings"}
|
||||
<div class="flex flex-col gap-1">
|
||||
<p
|
||||
class="relative z-10 px-3 mb-1.5 text-sm font-semibold tracking-wider uppercase text-zinc-400 dark:text-zinc-500"
|
||||
>
|
||||
User Settings
|
||||
</p>
|
||||
{#each userNav as item (item.id)}
|
||||
{@render navButton(item)}
|
||||
{/each}
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<p
|
||||
class="relative z-10 px-3 mb-1.5 text-sm font-semibold tracking-wider uppercase text-zinc-400 dark:text-zinc-500"
|
||||
>
|
||||
App Settings
|
||||
</p>
|
||||
{#each appNav as item (item.id)}
|
||||
{@render navButton(item)}
|
||||
{/each}
|
||||
</div>
|
||||
{:else if activePage === "shortcuts"}
|
||||
<div class="flex flex-col gap-1">
|
||||
<p
|
||||
class="px-3 mb-1.5 text-sm font-semibold tracking-wider uppercase text-zinc-400 dark:text-zinc-500"
|
||||
>
|
||||
Shortcuts
|
||||
</p>
|
||||
<button
|
||||
type="button"
|
||||
class="w-full px-3 py-2.5 text-left text-lg rounded-lg font-medium bg-zinc-200/80 dark:bg-zinc-700/80 text-zinc-900 dark:text-white"
|
||||
>
|
||||
Shortcuts
|
||||
</button>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex flex-col gap-1">
|
||||
<p
|
||||
class="px-3 mb-1.5 text-sm font-semibold tracking-wider uppercase text-zinc-400 dark:text-zinc-500"
|
||||
>
|
||||
Themes
|
||||
</p>
|
||||
<button
|
||||
type="button"
|
||||
class="w-full px-3 py-2.5 text-left text-lg rounded-lg font-medium bg-zinc-200/80 dark:bg-zinc-700/80 text-zinc-900 dark:text-white"
|
||||
>
|
||||
Themes
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="flex flex-col flex-1 min-w-0 min-h-0">
|
||||
<div class="shrink-0 px-6 pt-5 pb-3">
|
||||
<h1 class="text-2xl font-semibold tracking-tight text-zinc-900 dark:text-white">
|
||||
<h1 class="text-3xl font-semibold tracking-tight text-zinc-900 dark:text-white">
|
||||
{sectionTitle}
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
import type { SettingsList } from "@/interface/types/SettingsProps"
|
||||
import { settingsState } from "@/seqta/utils/listeners/SettingsState.ts"
|
||||
import PickerSwatch from "@/interface/components/PickerSwatch.svelte"
|
||||
import SidebarAppearance from "@/interface/components/SidebarAppearance.svelte"
|
||||
import ConnectMobileApp from "@/interface/components/ConnectMobileApp.svelte"
|
||||
import CloudSettingsSync from "@/interface/components/CloudSettingsSync.svelte"
|
||||
import CloudHeader from "@/interface/components/store/CloudHeader.svelte"
|
||||
@@ -143,11 +144,25 @@
|
||||
activeSection?: string;
|
||||
}>();
|
||||
|
||||
const activePluginId = $derived(
|
||||
activeSection.startsWith("plugin:")
|
||||
? activeSection.slice("plugin:".length)
|
||||
: null,
|
||||
);
|
||||
/** Map each plugin into the settings sidebar category that fits its purpose. */
|
||||
const pluginSectionById: Record<string, string> = {
|
||||
"profile-picture": "account",
|
||||
"animated-background": "appearance",
|
||||
"background-music": "appearance",
|
||||
timetable: "timetable",
|
||||
timetableEdit: "timetable",
|
||||
"assessments-overview": "assessments",
|
||||
"assessments-average": "assessments",
|
||||
"grade-analytics": "assessments",
|
||||
"global-search": "features",
|
||||
"enhanced-navigation": "features",
|
||||
messageFolders: "features",
|
||||
notificationCollector: "features",
|
||||
};
|
||||
|
||||
const pluginBelongsInSection = (pluginId: string) =>
|
||||
(pluginSectionById[pluginId] ?? "features") === activeSection;
|
||||
|
||||
|
||||
async function exportCloudSettingsJsonToFile() {
|
||||
const payload = await getSnapshotForUpload();
|
||||
@@ -164,10 +179,10 @@
|
||||
</script>
|
||||
|
||||
{#snippet Setting({ title, description, Component, props }: SettingsList) }
|
||||
<div class="flex justify-between items-center px-5 py-4">
|
||||
<div class="flex justify-between items-center px-5 py-5">
|
||||
<div class="pr-5">
|
||||
<h2 class="text-base font-bold">{title}</h2>
|
||||
<p class="text-sm text-zinc-600 dark:text-zinc-300">{description}</p>
|
||||
<h2 class="text-xl font-bold">{title}</h2>
|
||||
<p class="text-base text-zinc-600 dark:text-zinc-300">{description}</p>
|
||||
</div>
|
||||
<div class="shrink-0">
|
||||
<Component {...props} />
|
||||
@@ -189,8 +204,8 @@
|
||||
<div class="p-1 my-1 from-white to-zinc-100 bg-gradient-to-br rounded-xl border shadow-sm border-zinc-200/50 dark:border-zinc-700/40 dark:to-zinc-900/50 dark:from-zinc-900/40">
|
||||
<div class="flex justify-between items-center px-5 py-4">
|
||||
<div class="pr-4">
|
||||
<h2 class="text-base font-bold">BetterSEQTA Cloud</h2>
|
||||
<p class="text-sm text-zinc-600 dark:text-zinc-300">Account & sync</p>
|
||||
<h2 class="text-xl font-bold">BetterSEQTA Cloud</h2>
|
||||
<p class="text-base text-zinc-600 dark:text-zinc-300">Account & sync</p>
|
||||
</div>
|
||||
<div>
|
||||
<CloudHeader alwaysShowUserName onClick={showCloudPanel} />
|
||||
@@ -345,12 +360,18 @@
|
||||
{@render Setting(option)}
|
||||
{/each}
|
||||
|
||||
{#if !isEngage}
|
||||
<div class="border-none">
|
||||
<SidebarAppearance />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="border-none">
|
||||
<div class="p-1 my-1 from-white to-zinc-100 bg-gradient-to-br rounded-xl border shadow-sm border-zinc-200/50 dark:border-zinc-700/40 dark:to-zinc-900/50 dark:from-zinc-900/40">
|
||||
<div class="flex justify-between items-center px-5 py-4">
|
||||
<div class="pr-4">
|
||||
<h2 class="text-base font-bold">Adaptive Theme Colour</h2>
|
||||
<p class="text-sm text-zinc-600 dark:text-zinc-300">Change the theme colour based on the current class (e.g. when viewing a course or assessments page)</p>
|
||||
<h2 class="text-xl font-bold">Adaptive Theme Colour</h2>
|
||||
<p class="text-base text-zinc-600 dark:text-zinc-300">Change the theme colour based on the current class (e.g. when viewing a course or assessments page)</p>
|
||||
</div>
|
||||
<div>
|
||||
<Switch
|
||||
@@ -362,8 +383,8 @@
|
||||
{#if $settingsState.adaptiveThemeColour}
|
||||
<div class="flex justify-between items-center px-5 py-4 pl-7 border-t border-zinc-100 dark:border-zinc-700/50">
|
||||
<div class="pr-4">
|
||||
<h2 class="text-base font-bold">Soft Gradient</h2>
|
||||
<p class="text-sm text-zinc-600 dark:text-zinc-300">Use a soft gradient instead of a solid colour when viewing a class</p>
|
||||
<h2 class="text-xl font-bold">Soft Gradient</h2>
|
||||
<p class="text-base text-zinc-600 dark:text-zinc-300">Use a soft gradient instead of a solid colour when viewing a class</p>
|
||||
</div>
|
||||
<div>
|
||||
<Switch
|
||||
@@ -374,8 +395,8 @@
|
||||
</div>
|
||||
<div class="flex justify-between items-center px-5 py-4 pl-7 border-t border-zinc-100 dark:border-zinc-700/50">
|
||||
<div class="pr-4">
|
||||
<h2 class="text-base font-bold">Smooth colour transition</h2>
|
||||
<p class="text-sm text-zinc-600 dark:text-zinc-300">Ease between class/subject colours when navigating instead of switching instantly</p>
|
||||
<h2 class="text-xl font-bold">Smooth colour transition</h2>
|
||||
<p class="text-base text-zinc-600 dark:text-zinc-300">Ease between class/subject colours when navigating instead of switching instantly</p>
|
||||
</div>
|
||||
<div>
|
||||
<Switch
|
||||
@@ -389,19 +410,19 @@
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if activeSection === "home"}
|
||||
{#if activeSection === "general"}
|
||||
<div class="border-none">
|
||||
<div class="p-1 my-1 from-white to-zinc-100 bg-gradient-to-br rounded-xl border shadow-sm border-zinc-200/50 dark:border-zinc-700/40 dark:to-zinc-900/50 dark:from-zinc-900/40">
|
||||
<div class="flex justify-between items-center px-5 py-4">
|
||||
<div class="pr-4">
|
||||
<h2 class="text-base font-bold">Home Page Assessments</h2>
|
||||
<p class="text-sm text-zinc-600 dark:text-zinc-300">Limit upcoming assessments shown on the home page by subject</p>
|
||||
<h2 class="text-xl font-bold">Home Page Assessments</h2>
|
||||
<p class="text-base text-zinc-600 dark:text-zinc-300">Limit upcoming assessments shown on the home page by subject</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-between items-center px-5 py-4 pl-7 border-t border-zinc-100 dark:border-zinc-700/50">
|
||||
<div class="pr-4">
|
||||
<h2 class="text-base font-bold">Include Past Assessments</h2>
|
||||
<p class="text-sm text-zinc-600 dark:text-zinc-300">Show past-due assessments from the upcoming list, matching the Assessments page</p>
|
||||
<h2 class="text-xl font-bold">Include Past Assessments</h2>
|
||||
<p class="text-base text-zinc-600 dark:text-zinc-300">Show past-due assessments from the upcoming list, matching the Assessments page</p>
|
||||
</div>
|
||||
<div>
|
||||
<Switch
|
||||
@@ -412,8 +433,8 @@
|
||||
</div>
|
||||
<div class="flex justify-between items-center px-5 py-4 pl-7 border-t border-zinc-100 dark:border-zinc-700/50">
|
||||
<div class="pr-4">
|
||||
<h2 class="text-base font-bold">Maximum Subjects</h2>
|
||||
<p class="text-sm text-zinc-600 dark:text-zinc-300">Number of subjects to include, ordered by soonest due date</p>
|
||||
<h2 class="text-xl font-bold">Maximum Subjects</h2>
|
||||
<p class="text-base text-zinc-600 dark:text-zinc-300">Number of subjects to include, ordered by soonest due date</p>
|
||||
</div>
|
||||
<Select
|
||||
value={String($settingsState.homeUpcomingSubjectsMax ?? 5)}
|
||||
@@ -430,8 +451,8 @@
|
||||
</div>
|
||||
<div class="flex justify-between items-center px-5 py-4 pl-7 border-t border-zinc-100 dark:border-zinc-700/50">
|
||||
<div class="pr-4">
|
||||
<h2 class="text-base font-bold">Maximum Assessments per Subject</h2>
|
||||
<p class="text-sm text-zinc-600 dark:text-zinc-300">Assessments shown for each included subject</p>
|
||||
<h2 class="text-xl font-bold">Maximum Assessments per Subject</h2>
|
||||
<p class="text-base text-zinc-600 dark:text-zinc-300">Assessments shown for each included subject</p>
|
||||
</div>
|
||||
<Select
|
||||
value={String($settingsState.homeUpcomingAssessmentsPerSubjectMax ?? 0)}
|
||||
@@ -451,14 +472,14 @@
|
||||
{/if}
|
||||
|
||||
{#each pluginSettings as plugin (plugin.pluginId)}
|
||||
{#if activePluginId === plugin.pluginId}
|
||||
{#if pluginBelongsInSection(plugin.pluginId)}
|
||||
<div class="border-none">
|
||||
<div class="p-1 my-1 from-white to-zinc-100 bg-gradient-to-br rounded-xl border shadow-sm border-zinc-200/50 dark:border-zinc-700/40 dark:to-zinc-900/50 dark:from-zinc-900/40 {!(plugin as any).disableToggle && Object.keys(plugin.settings).length === 0 ? 'hidden' : ''}">
|
||||
<!-- Always show enable toggle if disableToggle is true -->
|
||||
{#if (plugin as any).disableToggle}
|
||||
<div class="flex justify-between items-center px-5 py-4">
|
||||
<div class="pr-4">
|
||||
<h2 class="flex gap-2 items-center text-base font-bold">
|
||||
<h2 class="flex gap-2 items-center text-xl font-bold">
|
||||
Enable {plugin.name}
|
||||
{#if plugin.beta}
|
||||
<span class="px-2 py-0.5 text-xs font-medium text-orange-800 bg-orange-100 rounded-full border border-orange-300/30 dark:bg-orange-900/30 dark:text-orange-300 dark:border-orange-900/30">
|
||||
@@ -466,7 +487,7 @@
|
||||
</span>
|
||||
{/if}
|
||||
</h2>
|
||||
<p class="text-sm text-zinc-600 dark:text-zinc-300">{plugin.description}</p>
|
||||
<p class="text-base text-zinc-600 dark:text-zinc-300">{plugin.description}</p>
|
||||
</div>
|
||||
<div>
|
||||
<Switch
|
||||
@@ -496,8 +517,8 @@
|
||||
{#if key !== 'enabled' && !(key === 'useCloudPfp' && !cloudState.isLoggedIn)}
|
||||
<div class="flex justify-between items-center px-5 py-4">
|
||||
<div class="pr-4">
|
||||
<h2 class="text-base font-bold">{setting.title || key}</h2>
|
||||
<p class="text-sm text-zinc-600 dark:text-zinc-300">{setting.description || ''}</p>
|
||||
<h2 class="text-xl font-bold">{setting.title || key}</h2>
|
||||
<p class="text-base text-zinc-600 dark:text-zinc-300">{setting.description || ''}</p>
|
||||
</div>
|
||||
<div>
|
||||
{#if setting.type === 'boolean'}
|
||||
@@ -585,8 +606,8 @@
|
||||
<div class="flex-col p-1 my-1 bg-gradient-to-br from-white rounded-xl border shadow-sm to-zinc-100 border-zinc-200/50 dark:border-zinc-700/40 dark:to-zinc-900/50 dark:from-zinc-900/40">
|
||||
<div class="flex justify-between items-center px-5 py-4">
|
||||
<div class="pr-4">
|
||||
<h2 class="text-base font-bold">Developer Mode</h2>
|
||||
<p class="text-sm text-zinc-600 dark:text-zinc-300">Enables developer mode, allowing you to test new features and changes.</p>
|
||||
<h2 class="text-xl font-bold">Developer Mode</h2>
|
||||
<p class="text-base text-zinc-600 dark:text-zinc-300">Enables developer mode, allowing you to test new features and changes.</p>
|
||||
</div>
|
||||
<div>
|
||||
<Switch state={$settingsState.devMode} onChange={(isOn: boolean) => settingsState.devMode = isOn} />
|
||||
@@ -594,8 +615,8 @@
|
||||
</div>
|
||||
<div class="flex justify-between items-center px-5 py-4">
|
||||
<div class="pr-4">
|
||||
<h2 class="text-base font-bold">Verbose logging</h2>
|
||||
<p class="text-sm text-zinc-600 dark:text-zinc-300">Show diagnostic console output (indexer, theme manager, timetable colour patch, etc.)</p>
|
||||
<h2 class="text-xl font-bold">Verbose logging</h2>
|
||||
<p class="text-base text-zinc-600 dark:text-zinc-300">Show diagnostic console output (indexer, theme manager, timetable colour patch, etc.)</p>
|
||||
</div>
|
||||
<div>
|
||||
<Switch
|
||||
@@ -606,8 +627,8 @@
|
||||
</div>
|
||||
<div class="flex justify-between items-center px-5 py-4">
|
||||
<div class="pr-4">
|
||||
<h2 class="text-base font-bold">Sensitive Hider</h2>
|
||||
<p class="text-sm text-zinc-600 dark:text-zinc-300">Replace sensitive content with mock data</p>
|
||||
<h2 class="text-xl font-bold">Sensitive Hider</h2>
|
||||
<p class="text-base text-zinc-600 dark:text-zinc-300">Replace sensitive content with mock data</p>
|
||||
</div>
|
||||
<div>
|
||||
<Switch
|
||||
@@ -618,8 +639,8 @@
|
||||
</div>
|
||||
<div class="flex justify-between items-center px-5 py-4">
|
||||
<div class="pr-4">
|
||||
<h2 class="text-base font-bold">Mock Notices</h2>
|
||||
<p class="text-sm text-zinc-600 dark:text-zinc-300">Use fake notice data on homepage instead of real data</p>
|
||||
<h2 class="text-xl font-bold">Mock Notices</h2>
|
||||
<p class="text-base text-zinc-600 dark:text-zinc-300">Use fake notice data on homepage instead of real data</p>
|
||||
</div>
|
||||
<div>
|
||||
<Switch
|
||||
@@ -630,8 +651,8 @@
|
||||
</div>
|
||||
<div class="flex justify-between items-center px-5 py-4">
|
||||
<div class="pr-4">
|
||||
<h2 class="text-base font-bold">Show Privacy Notification</h2>
|
||||
<p class="text-sm text-zinc-600 dark:text-zinc-300">Show the privacy notification popup on next page load</p>
|
||||
<h2 class="text-xl font-bold">Show Privacy Notification</h2>
|
||||
<p class="text-base text-zinc-600 dark:text-zinc-300">Show the privacy notification popup on next page load</p>
|
||||
</div>
|
||||
<div>
|
||||
<Button
|
||||
@@ -649,8 +670,8 @@
|
||||
</div>
|
||||
<div class="flex justify-between items-center px-5 py-4">
|
||||
<div class="pr-4">
|
||||
<h2 class="text-base font-bold">Show Theme of the Month</h2>
|
||||
<p class="text-sm text-zinc-600 dark:text-zinc-300">Fetch and show the current month's popup now (ignores dismissed state)</p>
|
||||
<h2 class="text-xl font-bold">Show Theme of the Month</h2>
|
||||
<p class="text-base text-zinc-600 dark:text-zinc-300">Fetch and show the current month's popup now (ignores dismissed state)</p>
|
||||
</div>
|
||||
<div>
|
||||
<Button
|
||||
@@ -665,8 +686,8 @@
|
||||
</div>
|
||||
<div class="flex justify-between items-center px-5 py-4">
|
||||
<div class="pr-4">
|
||||
<h2 class="text-base font-bold">Export cloud settings JSON</h2>
|
||||
<p class="text-sm text-zinc-600 dark:text-zinc-300">Download the same payload as cloud sync (OAuth tokens stripped). For debugging and server testing.</p>
|
||||
<h2 class="text-xl font-bold">Export cloud settings JSON</h2>
|
||||
<p class="text-base text-zinc-600 dark:text-zinc-300">Download the same payload as cloud sync (OAuth tokens stripped). For debugging and server testing.</p>
|
||||
</div>
|
||||
<div>
|
||||
<Button onClick={exportCloudSettingsJsonToFile} text="Export to file" />
|
||||
@@ -675,8 +696,8 @@
|
||||
<div class="flex flex-col gap-2 px-4 py-3">
|
||||
<div class="flex justify-between items-start gap-3">
|
||||
<div class="pr-4">
|
||||
<h2 class="text-base font-bold">API Base URL (session only)</h2>
|
||||
<p class="text-sm text-zinc-600 dark:text-zinc-300">Override the content API host for this browser session. Cleared on restart. Affects themes, theme of the month, and other server-driven content.</p>
|
||||
<h2 class="text-xl font-bold">API Base URL (session only)</h2>
|
||||
<p class="text-base text-zinc-600 dark:text-zinc-300">Override the content API host for this browser session. Cleared on restart. Affects themes, theme of the month, and other server-driven content.</p>
|
||||
{#if devApiBaseActive}
|
||||
<p class="text-xs mt-1 text-amber-600 dark:text-amber-400">
|
||||
Override active: <span class="font-mono">{devApiBaseActive}</span>
|
||||
@@ -699,8 +720,8 @@
|
||||
</div>
|
||||
<div class="flex flex-col gap-2 px-4 py-3">
|
||||
<div>
|
||||
<h2 class="text-base font-bold">GitHub latest version override</h2>
|
||||
<p class="text-sm text-zinc-600 dark:text-zinc-300">Pretend a newer GitHub release exists to test the update badge. Only applies when dev mode is on.</p>
|
||||
<h2 class="text-xl font-bold">GitHub latest version override</h2>
|
||||
<p class="text-base text-zinc-600 dark:text-zinc-300">Pretend a newer GitHub release exists to test the update badge. Only applies when dev mode is on.</p>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
|
||||
@@ -196,7 +196,7 @@
|
||||
transition={{ duration: 0.3 }}
|
||||
>
|
||||
<button
|
||||
class="w-full px-4 py-2 mb-4 text-[13px] dark:text-white transition rounded-xl bg-zinc-200 dark:bg-zinc-700/50"
|
||||
class="w-full px-4 py-3 mb-4 text-base dark:text-white transition rounded-xl bg-zinc-200 dark:bg-zinc-700/50"
|
||||
onclick={isFormVisible ? addNewCustomShortcut : toggleForm}
|
||||
>
|
||||
{#if isFormVisible}
|
||||
@@ -224,7 +224,7 @@
|
||||
<div class="flex justify-between items-center px-4 py-3">
|
||||
<div class="pr-4">
|
||||
<!-- Use DisplayName if it exists, otherwise use the key (shortcut[0]) as a fallback -->
|
||||
<h2 class="text-sm">{shortcut[1].DisplayName || shortcut[0]}</h2>
|
||||
<h2 class="text-lg font-medium">{shortcut[1].DisplayName || shortcut[0]}</h2>
|
||||
</div>
|
||||
<Switch state={$settingsState.shortcuts.find(s => s.name === shortcut[0])?.enabled ?? false} onChange={() => switchChange(shortcut[0])} />
|
||||
</div>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
{#if !standalone.standalone}
|
||||
<button
|
||||
onclick={() => selectNoBackground()}
|
||||
class="w-full px-4 py-2 mb-4 text-[13px] dark:text-white transition rounded-xl bg-zinc-200 dark:bg-zinc-700/50">
|
||||
class="w-full px-4 py-3 mb-4 text-base dark:text-white transition rounded-xl bg-zinc-200 dark:bg-zinc-700/50">
|
||||
{ clearTheme ? 'Clear Theme' : 'Select a Theme' }
|
||||
</button>
|
||||
<div class="relative w-full">
|
||||
|
||||
Reference in New Issue
Block a user