mirror of
https://github.com/BetterSEQTA/BetterSEQTA-Plus.git
synced 2026-08-28 09:11:06 +00:00
feat: cleanup and improve detection delay on kitten plugin
This commit is contained in:
@@ -14,8 +14,6 @@ export default {
|
|||||||
'/node_modules/(?!(color|color-string|color-convert|color-name)/)',
|
'/node_modules/(?!(color|color-string|color-convert|color-name)/)',
|
||||||
],
|
],
|
||||||
moduleNameMapper: {
|
moduleNameMapper: {
|
||||||
'\\.png$': '<rootDir>/src/test/mocks/assetStub.ts',
|
|
||||||
'\\?inline$': '<rootDir>/src/test/mocks/inlineStub.ts',
|
|
||||||
'^@/(.*)$': '<rootDir>/src/$1',
|
'^@/(.*)$': '<rootDir>/src/$1',
|
||||||
'^color$': '<rootDir>/src/test/mocks/color.ts',
|
'^color$': '<rootDir>/src/test/mocks/color.ts',
|
||||||
'^webextension-polyfill$': '<rootDir>/src/test/mocks/webextension-polyfill.ts',
|
'^webextension-polyfill$': '<rootDir>/src/test/mocks/webextension-polyfill.ts',
|
||||||
|
|||||||
+49
-47
@@ -53,9 +53,8 @@ if (document.childNodes[1]) {
|
|||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
// The 404 page is a bare document with no second childNode, so it must be
|
// Standalone 404 documents never pass the SEQTA SPA gate above.
|
||||||
// booted unconditionally (outside the gate above). It self-guards on title.
|
void bootErrorPage();
|
||||||
bootErrorPage();
|
|
||||||
|
|
||||||
if (import.meta.env.DEV) {
|
if (import.meta.env.DEV) {
|
||||||
window.addEventListener("unhandledrejection", (event) => {
|
window.addEventListener("unhandledrejection", (event) => {
|
||||||
@@ -142,65 +141,68 @@ async function init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The 404 page is a standalone document that never passes the SPA gate above,
|
function shouldBootKitten404(): boolean {
|
||||||
// so the normal plugin path never boots there; start just the classic kitten
|
if (IsSEQTAPage || document.getElementById("container")) return false;
|
||||||
// 404 plugin so it renders without any SPA work.
|
return /404|not found/i.test(document.title);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Hide the rebranded 404 before the kitten card mounts (removed after boot). */
|
||||||
|
function inject404FlashHide(): void {
|
||||||
|
if (document.getElementById("bsplus-404-flash-hide")) return;
|
||||||
|
const style = document.createElement("style");
|
||||||
|
style.id = "bsplus-404-flash-hide";
|
||||||
|
style.textContent =
|
||||||
|
"html,body{background:#333!important}.message{display:none!important}";
|
||||||
|
(document.head ?? document.documentElement).appendChild(style);
|
||||||
|
}
|
||||||
|
|
||||||
async function bootErrorPage() {
|
async function bootErrorPage() {
|
||||||
if (IsSEQTAPage) return;
|
if (IsSEQTAPage) return;
|
||||||
|
|
||||||
// Runs at document_start, so <title> and body are not available yet; wait
|
const storagePromise = browser.storage.local.get([
|
||||||
// for the DOM before testing the 404 title.
|
|
||||||
await new Promise<void>((resolve) => {
|
|
||||||
if (document.readyState !== "loading") {
|
|
||||||
resolve();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
document.addEventListener("DOMContentLoaded", () => resolve(), {
|
|
||||||
once: true,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const is404Page =
|
|
||||||
/404/.test(document.title) || /not found/i.test(document.title);
|
|
||||||
if (!is404Page) return;
|
|
||||||
|
|
||||||
const stored = await browser.storage.local.get([
|
|
||||||
"onoff",
|
"onoff",
|
||||||
"plugin.error-page-kitten.settings",
|
"plugin.error-page-kitten.settings",
|
||||||
]);
|
]);
|
||||||
if ((stored.onoff ?? true) === false) return;
|
|
||||||
const kittenEnabled = (
|
|
||||||
stored["plugin.error-page-kitten.settings"] as
|
|
||||||
| { enabled?: boolean }
|
|
||||||
| undefined
|
|
||||||
)?.enabled;
|
|
||||||
if (kittenEnabled === false) return;
|
|
||||||
|
|
||||||
hideRebranded404();
|
const watchTitle = () => {
|
||||||
|
if (shouldBootKitten404()) inject404FlashHide();
|
||||||
|
};
|
||||||
|
watchTitle();
|
||||||
|
if (document.readyState === "loading") {
|
||||||
|
document.addEventListener("readystatechange", watchTitle);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.readyState === "loading") {
|
||||||
|
await new Promise<void>((resolve) => {
|
||||||
|
document.addEventListener("DOMContentLoaded", () => resolve(), { once: true });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
document.removeEventListener("readystatechange", watchTitle);
|
||||||
|
|
||||||
|
if (!shouldBootKitten404()) return;
|
||||||
|
|
||||||
|
const stored = await storagePromise;
|
||||||
|
if ((stored.onoff ?? true) === false) return;
|
||||||
|
const kittenSettings = stored["plugin.error-page-kitten.settings"] as
|
||||||
|
| { enabled?: boolean }
|
||||||
|
| undefined;
|
||||||
|
if (kittenSettings?.enabled === false) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { pluginManager } = await import("@/plugins/index");
|
const { mountErrorPageKitten } = await import(
|
||||||
await pluginManager.startPlugin("error-page-kitten");
|
"@/plugins/built-in/errorPageKitten"
|
||||||
|
);
|
||||||
|
mountErrorPageKitten();
|
||||||
|
document.getElementById("bsplus-404-flash-hide")?.remove();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Restore the rebranded page so the user is not left with a blank document.
|
document.getElementById("bsplus-404-flash-hide")?.remove();
|
||||||
document
|
document
|
||||||
.querySelectorAll<HTMLElement>(".bsplus-kitten-404-hidden")
|
.querySelectorAll(".bsplus-kitten-404-hidden")
|
||||||
.forEach((el) => {
|
.forEach((el) => el.classList.remove("bsplus-kitten-404-hidden"));
|
||||||
el.classList.remove("bsplus-kitten-404-hidden");
|
|
||||||
el.style.display = "";
|
|
||||||
});
|
|
||||||
console.error("[BetterSEQTA+] Failed to boot 404 page:", error);
|
console.error("[BetterSEQTA+] Failed to boot 404 page:", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function hideRebranded404() {
|
|
||||||
const message = document.querySelector<HTMLElement>(".message");
|
|
||||||
if (message) {
|
|
||||||
message.classList.add("bsplus-kitten-404-hidden");
|
|
||||||
message.style.display = "none";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function replaceIcons() {
|
function replaceIcons() {
|
||||||
document
|
document
|
||||||
.querySelectorAll<HTMLLinkElement>('link[rel*="icon"]')
|
.querySelectorAll<HTMLLinkElement>('link[rel*="icon"]')
|
||||||
|
|||||||
@@ -3,65 +3,33 @@
|
|||||||
*/
|
*/
|
||||||
/// <reference types="jest" />
|
/// <reference types="jest" />
|
||||||
import type { PluginAPI } from "@/plugins/core/types";
|
import type { PluginAPI } from "@/plugins/core/types";
|
||||||
import errorPageKittenPlugin from "./index";
|
|
||||||
|
jest.mock("@/lib/extensionAssetUrl", () => ({
|
||||||
|
resolveExtensionAssetUrl: (url: string) =>
|
||||||
|
`chrome-extension://test/${String(url).replace(/^\/+/, "")}`,
|
||||||
|
}));
|
||||||
|
jest.mock("@/resources/error-page/kitten.png", () => ({
|
||||||
|
__esModule: true,
|
||||||
|
default: "/resources/error-page/kitten.png",
|
||||||
|
}));
|
||||||
|
jest.mock("./styles.css?inline", () => ({
|
||||||
|
__esModule: true,
|
||||||
|
default: "/* test */",
|
||||||
|
}));
|
||||||
|
|
||||||
|
import { mountErrorPageKitten } from "./index";
|
||||||
|
|
||||||
const REBRANDED_404_BODY = `
|
const REBRANDED_404_BODY = `
|
||||||
<div class="message">
|
<div class="message">
|
||||||
<h1>Page not found</h1>
|
<h1>Page not found</h1>
|
||||||
<p>We can't find the page you're looking for.</p>
|
<p>We can't find the page you're looking for.</p>
|
||||||
<p>It may have been moved, deleted or the link might be out of date.</p>
|
|
||||||
<p class="error-ref">Ref: 404</p>
|
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const api = {
|
|
||||||
settings: { loaded: Promise.resolve() },
|
|
||||||
} as unknown as PluginAPI<{}>;
|
|
||||||
|
|
||||||
async function startPlugin(): Promise<() => void> {
|
|
||||||
const cleanup = (await errorPageKittenPlugin.run(api)) ?? (() => {});
|
|
||||||
return cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCard(): HTMLElement | null {
|
function getCard(): HTMLElement | null {
|
||||||
// The card container shares its class with <html>, so scope to body.
|
|
||||||
return document.body.querySelector<HTMLElement>(".bsplus-kitten-404");
|
return document.body.querySelector<HTMLElement>(".bsplus-kitten-404");
|
||||||
}
|
}
|
||||||
|
|
||||||
function expectKittenRendered(): void {
|
|
||||||
const card = getCard();
|
|
||||||
expect(card).not.toBeNull();
|
|
||||||
expect(document.documentElement.classList).toContain("bsplus-kitten-404");
|
|
||||||
expect(card?.querySelector("h1")?.textContent).toBe("404 Not Found");
|
|
||||||
// The original document's title.
|
|
||||||
expect(document.title).toBe("404 Not Found");
|
|
||||||
// The reference page's exact vendor-prefixed banner CSS is shipped at runtime.
|
|
||||||
const stripeStyle = Array.from(
|
|
||||||
document.head.querySelectorAll("style"),
|
|
||||||
).find((s) => s.textContent?.includes("-webkit-repeating-linear-gradient"));
|
|
||||||
expect(stripeStyle?.textContent).toContain(
|
|
||||||
"repeating-linear-gradient(45deg",
|
|
||||||
);
|
|
||||||
const img = card?.querySelector(".kitten img") as HTMLImageElement | null;
|
|
||||||
expect(img?.getAttribute("alt")).toBeNull();
|
|
||||||
expect(img?.src).toContain("kitten");
|
|
||||||
const flickrLink = card?.querySelector(
|
|
||||||
".attrib a",
|
|
||||||
) as HTMLAnchorElement | null;
|
|
||||||
expect(flickrLink?.textContent).toBe("by storyvillegirl");
|
|
||||||
expect(flickrLink?.href).toBe(
|
|
||||||
"http://www.flickr.com/photos/bibbit/2756165489/",
|
|
||||||
);
|
|
||||||
const seqtaLink = card?.querySelector("a[href='http://www.seqta.com.au']");
|
|
||||||
expect(seqtaLink?.textContent).toBe("SEQTA");
|
|
||||||
// Original rebranded message is hidden.
|
|
||||||
expect(
|
|
||||||
document.querySelector(".message")?.classList.contains(
|
|
||||||
"bsplus-kitten-404-hidden",
|
|
||||||
),
|
|
||||||
).toBe(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
describe("errorPageKitten", () => {
|
describe("errorPageKitten", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
document.body.innerHTML = "";
|
document.body.innerHTML = "";
|
||||||
@@ -69,47 +37,24 @@ describe("errorPageKitten", () => {
|
|||||||
document.documentElement.classList.remove("bsplus-kitten-404");
|
document.documentElement.classList.remove("bsplus-kitten-404");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders the classic kitten 404 card over the rebranded page", async () => {
|
it("mounts the classic kitten 404 card", () => {
|
||||||
document.title = "Page not found";
|
document.title = "Page not found";
|
||||||
document.body.innerHTML = REBRANDED_404_BODY;
|
document.body.innerHTML = REBRANDED_404_BODY;
|
||||||
|
|
||||||
const cleanup = await startPlugin();
|
const cleanup = mountErrorPageKitten();
|
||||||
|
|
||||||
expectKittenRendered();
|
const card = getCard();
|
||||||
|
expect(card).not.toBeNull();
|
||||||
|
expect(document.documentElement.classList).toContain("bsplus-kitten-404");
|
||||||
|
expect(card?.querySelector("h1")?.textContent).toBe("404 Not Found");
|
||||||
|
expect(document.title).toBe("404 Not Found");
|
||||||
|
expect(card?.querySelector(".kitten img")?.getAttribute("src")).toContain("kitten");
|
||||||
|
expect(
|
||||||
|
document.querySelector(".message")?.classList.contains("bsplus-kitten-404-hidden"),
|
||||||
|
).toBe(true);
|
||||||
|
|
||||||
cleanup();
|
cleanup();
|
||||||
expect(getCard()).toBeNull();
|
expect(getCard()).toBeNull();
|
||||||
expect(document.title).toBe("Page not found");
|
expect(document.title).toBe("Page not found");
|
||||||
expect(
|
|
||||||
Array.from(document.head.querySelectorAll("style")).some((s) =>
|
|
||||||
s.textContent?.includes("-webkit-repeating-linear-gradient"),
|
|
||||||
),
|
|
||||||
).toBe(false);
|
|
||||||
expect(
|
|
||||||
document.documentElement.classList.contains("bsplus-kitten-404"),
|
|
||||||
).toBe(false);
|
|
||||||
expect(
|
|
||||||
document
|
|
||||||
.querySelector(".message")
|
|
||||||
?.classList.contains("bsplus-kitten-404-hidden"),
|
|
||||||
).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("does not render inside the SPA (no .message, has #container)", async () => {
|
|
||||||
document.title = "SEQTA";
|
|
||||||
document.body.innerHTML = '<div id="container"></div>';
|
|
||||||
|
|
||||||
await startPlugin();
|
|
||||||
|
|
||||||
expect(getCard()).toBeNull();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("does not render on an unrelated page", async () => {
|
|
||||||
document.title = "Home";
|
|
||||||
document.body.innerHTML = '<div id="app"></div>';
|
|
||||||
|
|
||||||
await startPlugin();
|
|
||||||
|
|
||||||
expect(getCard()).toBeNull();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,72 +4,43 @@ import kittenPng from "@/resources/error-page/kitten.png";
|
|||||||
import styles from "./styles.css?inline";
|
import styles from "./styles.css?inline";
|
||||||
|
|
||||||
const KITTEN_IMG = resolveExtensionAssetUrl(kittenPng);
|
const KITTEN_IMG = resolveExtensionAssetUrl(kittenPng);
|
||||||
const KITTEN_CLASS = "bsplus-kitten-404";
|
const ROOT_CLASS = "bsplus-kitten-404";
|
||||||
|
|
||||||
// The reference page's exact h1 background declarations, in the original
|
const CARD_HTML = `<h1>404 Not Found</h1>
|
||||||
// order. Each engine keeps the last declaration it understands (-webkit in
|
<p>Sorry — the resource you are looking for could not be found.</p>
|
||||||
// Blink, -moz in Gecko, the legacy standard otherwise), so shipping them
|
<p>If you believe you are seeing this message in error, please contact your IT department.</p>
|
||||||
// byte-for-byte reproduces the reference banner on every engine. Lives in a
|
<div class="kitten">
|
||||||
// JS string because the CSS minifier drops vendor-prefixed declarations.
|
<p>I did find this picture of a kitten for you, though!</p>
|
||||||
const H1_STRIPE_CSS =
|
<img src="${KITTEN_IMG}" alt="">
|
||||||
"html.bsplus-kitten-404 .bsplus-kitten-404>h1{background-image:-moz-repeating-linear-gradient(135deg, rgba(0,0,0,0), rgba(0,0,0,0) 8px, rgba(0,0,0,0.05) 8px, rgba(0,0,0,0.05) 16px), -moz-linear-gradient(top, rgba(0,0,0,0), rgba(0,0,0,0.1));background-image:-webkit-repeating-linear-gradient(135deg, rgba(0,0,0,0), rgba(0,0,0,0) 8px, rgba(0,0,0,0.05) 8px, rgba(0,0,0,0.05) 16px), -webkit-linear-gradient(top, rgba(0,0,0,0), rgba(0,0,0,0.1));background-image:-o-repeating-linear-gradient(135deg, rgba(0,0,0,0), rgba(0,0,0,0) 8px, rgba(0,0,0,0.05) 8px, rgba(0,0,0,0.05) 16px), -o-linear-gradient(top, rgba(0,0,0,0), rgba(0,0,0,0.1));background-image:repeating-linear-gradient(45deg, rgba(0,0,0,0), rgba(0,0,0,0) 8px, rgba(0,0,0,0.05) 8px, rgba(0,0,0,0.05) 16px), linear-gradient(top, rgba(0,0,0,0), rgba(0,0,0,0.1))}";
|
<div class="attrib">CC-BY-SA <a href="http://www.flickr.com/photos/bibbit/2756165489/">by storyvillegirl</a></div>
|
||||||
|
</div>
|
||||||
|
<a href="http://www.seqta.com.au">SEQTA</a>`;
|
||||||
|
|
||||||
// The 404 page (legacy and rebranded) is a standalone document built around a
|
/** Boot path in SEQTA.ts gates when this runs; keep render-only here. */
|
||||||
// single `.message` block; the real app mounts a `#container`. Require the
|
export function mountErrorPageKitten(): () => void {
|
||||||
// former and rule out the latter so the plugin never renders inside the app.
|
const styleEl = document.createElement("style");
|
||||||
function is404Page(): boolean {
|
styleEl.textContent = styles;
|
||||||
if (document.getElementById("container")) return false;
|
document.head.appendChild(styleEl);
|
||||||
const heading = document.querySelector(".message > h1");
|
|
||||||
const text = heading?.textContent ?? document.title;
|
const originalTitle = document.title;
|
||||||
return /not found/i.test(text) || /404/.test(document.title);
|
document.querySelector(".message")?.classList.add("bsplus-kitten-404-hidden");
|
||||||
}
|
document.documentElement.classList.add(ROOT_CLASS);
|
||||||
|
document.title = "404 Not Found";
|
||||||
|
|
||||||
function buildCard(): HTMLElement {
|
|
||||||
const card = document.createElement("div");
|
const card = document.createElement("div");
|
||||||
card.className = KITTEN_CLASS;
|
card.className = ROOT_CLASS;
|
||||||
|
card.innerHTML = CARD_HTML;
|
||||||
|
document.body.appendChild(card);
|
||||||
|
|
||||||
const h1 = document.createElement("h1");
|
return () => {
|
||||||
h1.textContent = "404 Not Found";
|
styleEl.remove();
|
||||||
card.appendChild(h1);
|
document.documentElement.classList.remove(ROOT_CLASS);
|
||||||
|
document
|
||||||
const p1 = document.createElement("p");
|
.querySelectorAll(".bsplus-kitten-404-hidden")
|
||||||
p1.textContent =
|
.forEach((el) => el.classList.remove("bsplus-kitten-404-hidden"));
|
||||||
"Sorry — the resource you are looking for could not be found.";
|
document.title = originalTitle;
|
||||||
card.appendChild(p1);
|
card.remove();
|
||||||
|
};
|
||||||
const p2 = document.createElement("p");
|
|
||||||
p2.textContent =
|
|
||||||
"If you believe you are seeing this message in error, please contact your IT department.";
|
|
||||||
card.appendChild(p2);
|
|
||||||
|
|
||||||
const kitten = document.createElement("div");
|
|
||||||
kitten.className = "kitten";
|
|
||||||
|
|
||||||
const kittenLine = document.createElement("p");
|
|
||||||
kittenLine.textContent = "I did find this picture of a kitten for you, though!";
|
|
||||||
kitten.appendChild(kittenLine);
|
|
||||||
|
|
||||||
const img = document.createElement("img");
|
|
||||||
img.src = KITTEN_IMG;
|
|
||||||
kitten.appendChild(img);
|
|
||||||
|
|
||||||
const attrib = document.createElement("div");
|
|
||||||
attrib.className = "attrib";
|
|
||||||
attrib.append("CC-BY-SA ");
|
|
||||||
const link = document.createElement("a");
|
|
||||||
link.href = "http://www.flickr.com/photos/bibbit/2756165489/";
|
|
||||||
link.textContent = "by storyvillegirl";
|
|
||||||
attrib.appendChild(link);
|
|
||||||
kitten.appendChild(attrib);
|
|
||||||
|
|
||||||
card.appendChild(kitten);
|
|
||||||
|
|
||||||
const seqtaLink = document.createElement("a");
|
|
||||||
seqtaLink.href = "http://www.seqta.com.au";
|
|
||||||
seqtaLink.textContent = "SEQTA";
|
|
||||||
card.appendChild(seqtaLink);
|
|
||||||
|
|
||||||
return card;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const errorPageKittenPlugin: Plugin = {
|
const errorPageKittenPlugin: Plugin = {
|
||||||
@@ -81,52 +52,7 @@ const errorPageKittenPlugin: Plugin = {
|
|||||||
disableToggle: true,
|
disableToggle: true,
|
||||||
defaultEnabled: true,
|
defaultEnabled: true,
|
||||||
styles,
|
styles,
|
||||||
|
run: async () => mountErrorPageKitten(),
|
||||||
run: async (_api) => {
|
|
||||||
let card: HTMLElement | null = null;
|
|
||||||
let hasBuilt = false;
|
|
||||||
let originalTitle = document.title;
|
|
||||||
let stripeStyle: HTMLStyleElement | null = null;
|
|
||||||
|
|
||||||
const render = () => {
|
|
||||||
if (!is404Page()) return;
|
|
||||||
|
|
||||||
document.querySelector(".message")?.classList.add("bsplus-kitten-404-hidden");
|
|
||||||
|
|
||||||
if (hasBuilt) return;
|
|
||||||
const body = document.body;
|
|
||||||
if (!body) return;
|
|
||||||
|
|
||||||
hasBuilt = true;
|
|
||||||
document.documentElement.classList.add(KITTEN_CLASS);
|
|
||||||
document.title = "404 Not Found";
|
|
||||||
// Appended after the manager-injected styles so these same-specificity
|
|
||||||
// declarations win over the compiled baseline.
|
|
||||||
stripeStyle = document.createElement("style");
|
|
||||||
stripeStyle.textContent = H1_STRIPE_CSS;
|
|
||||||
document.head.appendChild(stripeStyle);
|
|
||||||
card = buildCard();
|
|
||||||
body.appendChild(card);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Body may not be parsed yet at document_start; render now and again on
|
|
||||||
// DOM ready as a safety net.
|
|
||||||
render();
|
|
||||||
document.addEventListener("DOMContentLoaded", render, { once: true });
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
document.removeEventListener("DOMContentLoaded", render);
|
|
||||||
document.documentElement.classList.remove(KITTEN_CLASS);
|
|
||||||
document
|
|
||||||
.querySelectorAll(".bsplus-kitten-404-hidden")
|
|
||||||
.forEach((el) => el.classList.remove("bsplus-kitten-404-hidden"));
|
|
||||||
document.title = originalTitle;
|
|
||||||
stripeStyle?.remove();
|
|
||||||
stripeStyle = null;
|
|
||||||
card?.remove();
|
|
||||||
card = null;
|
|
||||||
};
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default errorPageKittenPlugin;
|
export default errorPageKittenPlugin;
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
import { defineLazyPlugin } from "../../core/dynamicLoader";
|
|
||||||
import styles from "./styles.css?inline";
|
|
||||||
|
|
||||||
export default defineLazyPlugin({
|
|
||||||
id: "error-page-kitten",
|
|
||||||
name: "Classic 404 Page",
|
|
||||||
description: "Brings back SEQTA's old kitten 404 page",
|
|
||||||
version: "1.0.0",
|
|
||||||
settings: {},
|
|
||||||
disableToggle: true,
|
|
||||||
defaultEnabled: true,
|
|
||||||
styles,
|
|
||||||
loader: () => import("./index"),
|
|
||||||
});
|
|
||||||
@@ -1,10 +1,5 @@
|
|||||||
/* Classic SEQTA 404 page, restored one-to-one over the rebranded document.
|
/* Classic SEQTA 404 page — scoped under html.bsplus-kitten-404 so it never leaks into the SPA. */
|
||||||
Scoped under html.bsplus-kitten-404 so it can never leak into the SPA;
|
|
||||||
the card element itself carries the .bsplus-kitten-404 class. */
|
|
||||||
|
|
||||||
/* The rebranded page styles <body> (Arial / flex / 25vh top / line-height
|
|
||||||
1.5 / letter-spacing). line-height and letter-spacing are reset because
|
|
||||||
the rebranded body sets them and the classic page uses browser defaults. */
|
|
||||||
html.bsplus-kitten-404 {
|
html.bsplus-kitten-404 {
|
||||||
background: #333;
|
background: #333;
|
||||||
}
|
}
|
||||||
@@ -20,7 +15,6 @@ html.bsplus-kitten-404 body {
|
|||||||
letter-spacing: normal;
|
letter-spacing: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Hide the rebranded page content while the classic card is shown. */
|
|
||||||
html.bsplus-kitten-404 .bsplus-kitten-404-hidden {
|
html.bsplus-kitten-404 .bsplus-kitten-404-hidden {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
@@ -38,10 +32,6 @@ html.bsplus-kitten-404 .bsplus-kitten-404 {
|
|||||||
|
|
||||||
html.bsplus-kitten-404 .bsplus-kitten-404 > h1 {
|
html.bsplus-kitten-404 .bsplus-kitten-404 > h1 {
|
||||||
background: #612;
|
background: #612;
|
||||||
/* Valid modern baseline; the reference's exact vendor-prefixed declarations
|
|
||||||
are injected at runtime (H1_STRIPE_CSS in index.ts), which win over this.
|
|
||||||
The vendor-prefixed lines can't live in this file because the CSS
|
|
||||||
minifier drops them. */
|
|
||||||
background-image: repeating-linear-gradient(
|
background-image: repeating-linear-gradient(
|
||||||
135deg,
|
135deg,
|
||||||
rgba(0, 0, 0, 0),
|
rgba(0, 0, 0, 0),
|
||||||
|
|||||||
@@ -26,9 +26,6 @@ interface StorageChange<T = any> {
|
|||||||
|
|
||||||
/** Phased plugin startup: critical UI first, light DOM next, heavy plugins last. */
|
/** Phased plugin startup: critical UI first, light DOM next, heavy plugins last. */
|
||||||
const PLUGIN_START_PHASES: readonly string[][] = [
|
const PLUGIN_START_PHASES: readonly string[][] = [
|
||||||
// 404 page boot is a tiny standalone path; start it first so the kitten
|
|
||||||
// card renders before any SPA work. A no-op on normal SEQTA pages.
|
|
||||||
["error-page-kitten"],
|
|
||||||
["themes", "animated-background"],
|
["themes", "animated-background"],
|
||||||
[
|
[
|
||||||
"timetable",
|
"timetable",
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import messageFoldersPluginLazy from "./built-in/messageFolders/lazy";
|
|||||||
import enhancedNavigationPluginLazy from "./built-in/enhancedNavigation/lazy";
|
import enhancedNavigationPluginLazy from "./built-in/enhancedNavigation/lazy";
|
||||||
import globalSearchPluginLazy from "./built-in/globalSearch/lazy";
|
import globalSearchPluginLazy from "./built-in/globalSearch/lazy";
|
||||||
import gradeAnalyticsPluginLazy from "./built-in/gradeAnalytics/lazy";
|
import gradeAnalyticsPluginLazy from "./built-in/gradeAnalytics/lazy";
|
||||||
import errorPageKittenPluginLazy from "./built-in/errorPageKitten/lazy";
|
import errorPageKittenPlugin from "./built-in/errorPageKitten";
|
||||||
|
|
||||||
// Initialize plugin manager
|
// Initialize plugin manager
|
||||||
const pluginManager = PluginManager.getInstance();
|
const pluginManager = PluginManager.getInstance();
|
||||||
@@ -37,7 +37,7 @@ pluginManager.registerPlugin(messageFoldersPluginLazy);
|
|||||||
pluginManager.registerPlugin(enhancedNavigationPluginLazy);
|
pluginManager.registerPlugin(enhancedNavigationPluginLazy);
|
||||||
pluginManager.registerPlugin(globalSearchPluginLazy);
|
pluginManager.registerPlugin(globalSearchPluginLazy);
|
||||||
pluginManager.registerPlugin(gradeAnalyticsPluginLazy);
|
pluginManager.registerPlugin(gradeAnalyticsPluginLazy);
|
||||||
pluginManager.registerPlugin(errorPageKittenPluginLazy);
|
pluginManager.registerPlugin(errorPageKittenPlugin);
|
||||||
|
|
||||||
export async function initializePlugins(): Promise<void> {
|
export async function initializePlugins(): Promise<void> {
|
||||||
await pluginManager.startAllPlugins();
|
await pluginManager.startAllPlugins();
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
// Jest stub for Vite asset imports (png, etc.): Vite returns a URL string.
|
|
||||||
export default "/resources/error-page/kitten.png";
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
// Jest stub for Vite's `?inline` css imports: returns a CSS string.
|
|
||||||
export default "/* test inline css */";
|
|
||||||
@@ -32,9 +32,6 @@ export default {
|
|||||||
storage: { local, onChanged },
|
storage: { local, onChanged },
|
||||||
runtime: {
|
runtime: {
|
||||||
sendMessage: jest.fn(async () => undefined),
|
sendMessage: jest.fn(async () => undefined),
|
||||||
getURL: jest.fn(
|
|
||||||
(path: string) => `chrome-extension://test/${String(path).replace(/^\/+/, "")}`,
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user