mirror of
https://github.com/BetterSEQTA/BetterSEQTA-Plus.git
synced 2026-06-13 23:24:40 +00:00
chore: update changelog
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
import { settingsState } from "@/seqta/utils/listeners/SettingsState";
|
||||
import { FONT_PRESETS, getFontPreset, type FontPreset } from "./presets";
|
||||
|
||||
const FONT_STYLE_ID = "betterseqta-font-override";
|
||||
const FONT_PICKER_BATCH_ID = "betterseqta-font-picker-preview";
|
||||
const loadedFontIds = new Set<string>();
|
||||
let pickerFontsPromise: Promise<void> | null = null;
|
||||
|
||||
/** Elements that show per-font previews must stay outside the global override. */
|
||||
export const FONT_PICKER_ROOT_CLASS = "bsplus-font-picker-root";
|
||||
|
||||
function googleFamilyParam(preset: FontPreset): string | null {
|
||||
if (!preset.googleUrl) return null;
|
||||
const name =
|
||||
preset.stack.split(",")[0]?.trim().replace(/^"|"$/g, "") ?? "";
|
||||
if (!name) return null;
|
||||
return `family=${encodeURIComponent(name)}:wght@400;500;600;700`;
|
||||
}
|
||||
|
||||
function injectStylesheet(id: string, href: string): Promise<void> {
|
||||
if (document.getElementById(id)) return Promise.resolve();
|
||||
|
||||
return new Promise((resolve) => {
|
||||
const link = document.createElement("link");
|
||||
link.id = id;
|
||||
link.rel = "stylesheet";
|
||||
link.href = href;
|
||||
link.setAttribute("data-betterseqta-font-picker-batch", "true");
|
||||
link.onload = () => resolve();
|
||||
link.onerror = () => resolve();
|
||||
document.head.appendChild(link);
|
||||
});
|
||||
}
|
||||
|
||||
/** Load all Google Fonts for picker previews (batched, awaited). */
|
||||
export function ensureFontPickerFontsLoaded(): Promise<void> {
|
||||
if (!pickerFontsPromise) {
|
||||
pickerFontsPromise = (async () => {
|
||||
const params = FONT_PRESETS.map(googleFamilyParam).filter(
|
||||
(param): param is string => param !== null,
|
||||
);
|
||||
|
||||
const chunkSize = 10;
|
||||
for (let i = 0; i < params.length; i += chunkSize) {
|
||||
const chunk = params.slice(i, i + chunkSize);
|
||||
const url = `https://fonts.googleapis.com/css2?${chunk.join("&")}&display=swap`;
|
||||
await injectStylesheet(`${FONT_PICKER_BATCH_ID}-${i / chunkSize}`, url);
|
||||
}
|
||||
|
||||
try {
|
||||
await document.fonts.ready;
|
||||
} catch {
|
||||
/* FontFaceSet unsupported or blocked */
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
return pickerFontsPromise;
|
||||
}
|
||||
|
||||
export function ensureFontLoaded(preset: FontPreset): void {
|
||||
if (!preset.googleUrl || loadedFontIds.has(preset.id)) return;
|
||||
|
||||
if (document.querySelector(`link[data-betterseqta-font="${preset.id}"]`)) {
|
||||
loadedFontIds.add(preset.id);
|
||||
return;
|
||||
}
|
||||
|
||||
const link = document.createElement("link");
|
||||
link.rel = "stylesheet";
|
||||
link.href = preset.googleUrl;
|
||||
link.setAttribute("data-betterseqta-font", preset.id);
|
||||
document.head.appendChild(link);
|
||||
loadedFontIds.add(preset.id);
|
||||
}
|
||||
|
||||
export function buildFontPreviewCss(): string {
|
||||
return FONT_PRESETS.map(
|
||||
(preset) => `
|
||||
.bsplus-font-picker-option[data-font-id="${preset.id}"] .bsplus-font-picker-option-name {
|
||||
font-family: ${preset.stack} !important;
|
||||
}`,
|
||||
).join("\n");
|
||||
}
|
||||
|
||||
const SEQTA_FONT_SCOPE = `
|
||||
.legacy-root,
|
||||
.legacy-root input,
|
||||
.legacy-root textarea,
|
||||
.legacy-root button,
|
||||
.legacy-root select,
|
||||
.legacy-root option,
|
||||
.legacy-root .input,
|
||||
.legacy-root *,
|
||||
#container,
|
||||
#container *
|
||||
`;
|
||||
|
||||
function buildFontOverrideCss(family: string): string {
|
||||
const rule = `font-family: ${family} !important;`;
|
||||
|
||||
return `
|
||||
${SEQTA_FONT_SCOPE} {
|
||||
${rule}
|
||||
}
|
||||
|
||||
.iconFamily,
|
||||
.iconFamily *,
|
||||
button.uiButton.timetable-zoom.iconFamily,
|
||||
[class~="iconFamily"],
|
||||
[class~="iconFamily"] * {
|
||||
font-family: "IconFamily" !important;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
export function applySelectedFont(fontId?: string | null): void {
|
||||
if (typeof document === "undefined") return;
|
||||
|
||||
const preset = getFontPreset(fontId ?? settingsState.selectedFont);
|
||||
ensureFontLoaded(preset);
|
||||
|
||||
document.documentElement.style.setProperty(
|
||||
"--betterseqta-font-family",
|
||||
preset.stack.split(",")[0]?.trim().replace(/^"|"$/g, "") ?? "Rubik",
|
||||
);
|
||||
|
||||
let style = document.getElementById(FONT_STYLE_ID) as HTMLStyleElement | null;
|
||||
if (!style) {
|
||||
style = document.createElement("style");
|
||||
style.id = FONT_STYLE_ID;
|
||||
document.head.appendChild(style);
|
||||
}
|
||||
|
||||
style.textContent = buildFontOverrideCss(preset.stack);
|
||||
}
|
||||
@@ -0,0 +1,265 @@
|
||||
export const DEFAULT_FONT_ID = "rubik";
|
||||
|
||||
export interface FontPreset {
|
||||
id: string;
|
||||
name: string;
|
||||
stack: string;
|
||||
googleUrl?: string;
|
||||
sample: string;
|
||||
}
|
||||
|
||||
export const FONT_PRESETS: FontPreset[] = [
|
||||
{
|
||||
id: "rubik",
|
||||
name: "Rubik",
|
||||
stack: "Rubik, sans-serif",
|
||||
googleUrl:
|
||||
"https://fonts.googleapis.com/css2?family=Rubik:wght@400;500;600;700&display=swap",
|
||||
sample: "Assessment due tomorrow at 3:30pm",
|
||||
},
|
||||
{
|
||||
id: "inter",
|
||||
name: "Inter",
|
||||
stack: "Inter, sans-serif",
|
||||
googleUrl:
|
||||
"https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap",
|
||||
sample: "Assessment due tomorrow at 3:30pm",
|
||||
},
|
||||
{
|
||||
id: "poppins",
|
||||
name: "Poppins",
|
||||
stack: "Poppins, sans-serif",
|
||||
googleUrl:
|
||||
"https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap",
|
||||
sample: "Assessment due tomorrow at 3:30pm",
|
||||
},
|
||||
{
|
||||
id: "nunito",
|
||||
name: "Nunito",
|
||||
stack: "Nunito, sans-serif",
|
||||
googleUrl:
|
||||
"https://fonts.googleapis.com/css2?family=Nunito:wght@400;500;600;700&display=swap",
|
||||
sample: "Assessment due tomorrow at 3:30pm",
|
||||
},
|
||||
{
|
||||
id: "montserrat",
|
||||
name: "Montserrat",
|
||||
stack: "Montserrat, sans-serif",
|
||||
googleUrl:
|
||||
"https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600;700&display=swap",
|
||||
sample: "Assessment due tomorrow at 3:30pm",
|
||||
},
|
||||
{
|
||||
id: "open-sans",
|
||||
name: "Open Sans",
|
||||
stack: '"Open Sans", sans-serif',
|
||||
googleUrl:
|
||||
"https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;500;600;700&display=swap",
|
||||
sample: "Assessment due tomorrow at 3:30pm",
|
||||
},
|
||||
{
|
||||
id: "lato",
|
||||
name: "Lato",
|
||||
stack: "Lato, sans-serif",
|
||||
googleUrl:
|
||||
"https://fonts.googleapis.com/css2?family=Lato:wght@400;700&display=swap",
|
||||
sample: "Assessment due tomorrow at 3:30pm",
|
||||
},
|
||||
{
|
||||
id: "source-sans-3",
|
||||
name: "Source Sans 3",
|
||||
stack: '"Source Sans 3", sans-serif',
|
||||
googleUrl:
|
||||
"https://fonts.googleapis.com/css2?family=Source+Sans+3:wght@400;500;600;700&display=swap",
|
||||
sample: "Assessment due tomorrow at 3:30pm",
|
||||
},
|
||||
{
|
||||
id: "raleway",
|
||||
name: "Raleway",
|
||||
stack: "Raleway, sans-serif",
|
||||
googleUrl:
|
||||
"https://fonts.googleapis.com/css2?family=Raleway:wght@400;500;600;700&display=swap",
|
||||
sample: "Assessment due tomorrow at 3:30pm",
|
||||
},
|
||||
{
|
||||
id: "dm-sans",
|
||||
name: "DM Sans",
|
||||
stack: '"DM Sans", sans-serif',
|
||||
googleUrl:
|
||||
"https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&display=swap",
|
||||
sample: "Assessment due tomorrow at 3:30pm",
|
||||
},
|
||||
{
|
||||
id: "plus-jakarta-sans",
|
||||
name: "Plus Jakarta Sans",
|
||||
stack: '"Plus Jakarta Sans", sans-serif',
|
||||
googleUrl:
|
||||
"https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap",
|
||||
sample: "Assessment due tomorrow at 3:30pm",
|
||||
},
|
||||
{
|
||||
id: "outfit",
|
||||
name: "Outfit",
|
||||
stack: "Outfit, sans-serif",
|
||||
googleUrl:
|
||||
"https://fonts.googleapis.com/css2?family=Outfit:wght@400;500;600;700&display=swap",
|
||||
sample: "Assessment due tomorrow at 3:30pm",
|
||||
},
|
||||
{
|
||||
id: "roboto",
|
||||
name: "Roboto",
|
||||
stack: "Roboto, sans-serif",
|
||||
googleUrl:
|
||||
"https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap",
|
||||
sample: "Assessment due tomorrow at 3:30pm",
|
||||
},
|
||||
{
|
||||
id: "work-sans",
|
||||
name: "Work Sans",
|
||||
stack: '"Work Sans", sans-serif',
|
||||
googleUrl:
|
||||
"https://fonts.googleapis.com/css2?family=Work+Sans:wght@400;500;600;700&display=swap",
|
||||
sample: "Assessment due tomorrow at 3:30pm",
|
||||
},
|
||||
{
|
||||
id: "manrope",
|
||||
name: "Manrope",
|
||||
stack: "Manrope, sans-serif",
|
||||
googleUrl:
|
||||
"https://fonts.googleapis.com/css2?family=Manrope:wght@400;500;600;700&display=swap",
|
||||
sample: "Assessment due tomorrow at 3:30pm",
|
||||
},
|
||||
{
|
||||
id: "figtree",
|
||||
name: "Figtree",
|
||||
stack: "Figtree, sans-serif",
|
||||
googleUrl:
|
||||
"https://fonts.googleapis.com/css2?family=Figtree:wght@400;500;600;700&display=swap",
|
||||
sample: "Assessment due tomorrow at 3:30pm",
|
||||
},
|
||||
{
|
||||
id: "lexend",
|
||||
name: "Lexend",
|
||||
stack: "Lexend, sans-serif",
|
||||
googleUrl:
|
||||
"https://fonts.googleapis.com/css2?family=Lexend:wght@400;500;600;700&display=swap",
|
||||
sample: "Assessment due tomorrow at 3:30pm",
|
||||
},
|
||||
{
|
||||
id: "ubuntu",
|
||||
name: "Ubuntu",
|
||||
stack: "Ubuntu, sans-serif",
|
||||
googleUrl:
|
||||
"https://fonts.googleapis.com/css2?family=Ubuntu:wght@400;500;700&display=swap",
|
||||
sample: "Assessment due tomorrow at 3:30pm",
|
||||
},
|
||||
{
|
||||
id: "karla",
|
||||
name: "Karla",
|
||||
stack: "Karla, sans-serif",
|
||||
googleUrl:
|
||||
"https://fonts.googleapis.com/css2?family=Karla:wght@400;500;600;700&display=swap",
|
||||
sample: "Assessment due tomorrow at 3:30pm",
|
||||
},
|
||||
{
|
||||
id: "quicksand",
|
||||
name: "Quicksand",
|
||||
stack: "Quicksand, sans-serif",
|
||||
googleUrl:
|
||||
"https://fonts.googleapis.com/css2?family=Quicksand:wght@400;500;600;700&display=swap",
|
||||
sample: "Assessment due tomorrow at 3:30pm",
|
||||
},
|
||||
{
|
||||
id: "ibm-plex-sans",
|
||||
name: "IBM Plex Sans",
|
||||
stack: '"IBM Plex Sans", sans-serif',
|
||||
googleUrl:
|
||||
"https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500;600;700&display=swap",
|
||||
sample: "Assessment due tomorrow at 3:30pm",
|
||||
},
|
||||
{
|
||||
id: "space-grotesk",
|
||||
name: "Space Grotesk",
|
||||
stack: '"Space Grotesk", sans-serif',
|
||||
googleUrl:
|
||||
"https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&display=swap",
|
||||
sample: "Assessment due tomorrow at 3:30pm",
|
||||
},
|
||||
{
|
||||
id: "mulish",
|
||||
name: "Mulish",
|
||||
stack: "Mulish, sans-serif",
|
||||
googleUrl:
|
||||
"https://fonts.googleapis.com/css2?family=Mulish:wght@400;500;600;700&display=swap",
|
||||
sample: "Assessment due tomorrow at 3:30pm",
|
||||
},
|
||||
{
|
||||
id: "cabin",
|
||||
name: "Cabin",
|
||||
stack: "Cabin, sans-serif",
|
||||
googleUrl:
|
||||
"https://fonts.googleapis.com/css2?family=Cabin:wght@400;500;600;700&display=swap",
|
||||
sample: "Assessment due tomorrow at 3:30pm",
|
||||
},
|
||||
{
|
||||
id: "oswald",
|
||||
name: "Oswald",
|
||||
stack: "Oswald, sans-serif",
|
||||
googleUrl:
|
||||
"https://fonts.googleapis.com/css2?family=Oswald:wght@400;500;600;700&display=swap",
|
||||
sample: "Assessment due tomorrow at 3:30pm",
|
||||
},
|
||||
{
|
||||
id: "merriweather",
|
||||
name: "Merriweather",
|
||||
stack: "Merriweather, serif",
|
||||
googleUrl:
|
||||
"https://fonts.googleapis.com/css2?family=Merriweather:wght@400;700&display=swap",
|
||||
sample: "Assessment due tomorrow at 3:30pm",
|
||||
},
|
||||
{
|
||||
id: "playfair-display",
|
||||
name: "Playfair Display",
|
||||
stack: '"Playfair Display", serif',
|
||||
googleUrl:
|
||||
"https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;600;700&display=swap",
|
||||
sample: "Assessment due tomorrow at 3:30pm",
|
||||
},
|
||||
{
|
||||
id: "lora",
|
||||
name: "Lora",
|
||||
stack: "Lora, serif",
|
||||
googleUrl:
|
||||
"https://fonts.googleapis.com/css2?family=Lora:wght@400;500;600;700&display=swap",
|
||||
sample: "Assessment due tomorrow at 3:30pm",
|
||||
},
|
||||
{
|
||||
id: "crimson-pro",
|
||||
name: "Crimson Pro",
|
||||
stack: '"Crimson Pro", serif',
|
||||
googleUrl:
|
||||
"https://fonts.googleapis.com/css2?family=Crimson+Pro:wght@400;500;600;700&display=swap",
|
||||
sample: "Assessment due tomorrow at 3:30pm",
|
||||
},
|
||||
{
|
||||
id: "libre-baskerville",
|
||||
name: "Libre Baskerville",
|
||||
stack: '"Libre Baskerville", serif',
|
||||
googleUrl:
|
||||
"https://fonts.googleapis.com/css2?family=Libre+Baskerville:wght@400;700&display=swap",
|
||||
sample: "Assessment due tomorrow at 3:30pm",
|
||||
},
|
||||
{
|
||||
id: "system",
|
||||
name: "System Default",
|
||||
stack: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
|
||||
sample: "Assessment due tomorrow at 3:30pm",
|
||||
},
|
||||
];
|
||||
|
||||
export function getFontPreset(id?: string | null): FontPreset {
|
||||
return (
|
||||
FONT_PRESETS.find((preset) => preset.id === id) ??
|
||||
FONT_PRESETS.find((preset) => preset.id === DEFAULT_FONT_ID)!
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { settingsState } from "./SettingsState";
|
||||
import { updateAllColors } from "@/seqta/ui/colors/Manager";
|
||||
import { applySelectedFont } from "@/seqta/ui/fonts/Manager";
|
||||
|
||||
// Shortcuts rendering
|
||||
import { renderShortcuts } from "@/seqta/utils/Render/renderShortcuts";
|
||||
@@ -40,6 +41,7 @@ export class StorageChangeHandler {
|
||||
"iconOnlySidebar",
|
||||
this.handleIconOnlySidebarChange.bind(this),
|
||||
);
|
||||
settingsState.register("selectedFont", () => applySelectedFont());
|
||||
}
|
||||
|
||||
private handleIconOnlySidebarChange(newValue: boolean | undefined) {
|
||||
|
||||
Reference in New Issue
Block a user