Hide workspace and agent content when the native sidebar rail is collapsed. Add persistent whole-composer collapse controls and a temporary Zen mode that removes side panels, chat header, and composer while keeping an in-chat exit control available. Cover state transitions, Escape handling, React rerender idempotence, and cleanup with a dependency-free DOM regression test.
459 lines
21 KiB
JavaScript
459 lines
21 KiB
JavaScript
/**
|
|
* dsh-ui-tweaks — browser half.
|
|
*
|
|
* A single client-only plugin that bundles six independent UI tweaks, each
|
|
* toggleable in the CONFIG block below:
|
|
*
|
|
* 0) BASE_BG — soften the near-black base background token
|
|
* (`--dsw-alias-bg-base`, #151517 by default). Drives the main content and,
|
|
* via feature (1), the sidebar, so both tone together.
|
|
*
|
|
* 1) MATCH_SIDEBAR_BG — the left sidebar root paints
|
|
* `background: var(--dsw-specific-sidebar-fill)` while the main content
|
|
* paints `var(--dsw-alias-bg-base)`. We alias the former token to the
|
|
* latter so the sidebar shares the transcript background (theme-agnostic).
|
|
*
|
|
* 2) SIDEBAR_BRAND_TEXT / HIDE_NEW_SESSION — keep the sidebar's logo row so
|
|
* its NATIVE collapse/expand toggle stays available (that toggle is what
|
|
* the compact-sidebar tweak used to remove), but replace the DeepSeek brand
|
|
* logo with plain text (e.g. "Harness"), and optionally hide the New
|
|
* Session button. Sidebar CSS-module classes are content-hashed, so we
|
|
* match the stable suffix; the brand button is pinpointed with
|
|
* `[class*="_brand"]:has([class*="_brandIdentity"])`.
|
|
*
|
|
* 3) COLLAPSE_HEADER — collapse the conversation-header utility pills
|
|
* (Session log, Stop All, Resume…, Git) behind one `⋯` dropdown. We never
|
|
* move or synthetically click the real buttons — their React handlers stay
|
|
* intact; we only restyle the `.headerUtilities` container into a panel and
|
|
* add a vanilla trigger. A MutationObserver re-applies after React
|
|
* re-renders. Open/close state lives in `data-*` attrs React won't clobber.
|
|
*
|
|
* 4) PANEL_BG — tone the elevated surfaces so they sit close to the base
|
|
* background: the "Message the agent" composer (`--dsw-specific-input-major`),
|
|
* code blocks (`--dsw-alias-markdown-code-block` + `…-banner`), the Ongoing
|
|
* Goal bar (`--dsw-specific-tip`), and generic panels/dropdowns
|
|
* (`--dsw-alias-bg-layer-1/2`). All are overridden to one shade.
|
|
*
|
|
* 5) STYLE_PROCESSED_LIKE_PROMPT — restyle the smooth-stream "Processed <time>"
|
|
* collapsible (stable class `dshss-processed`) to match the prompt composer:
|
|
* full-width rounded pill, thin border, panel background, soft shadow.
|
|
*
|
|
* All static CSS shares one <style>; feature 3 also adds dynamic wiring.
|
|
* Everything is HMR-safe via a single combined cleanup.
|
|
* @module dsh-ui-tweaks/client
|
|
*/
|
|
window.__ModuleLoader__.load({
|
|
id: "dsh-ui-tweaks",
|
|
factory: (require) => {
|
|
var module = { exports: {} };
|
|
var exports = module.exports;
|
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
|
// ---- CONFIG (edit here) -----------------------------------------------
|
|
// (0) Background tone — the app's base background (harness default
|
|
// #151517). This drives the main content AND, via feature (1), the
|
|
// sidebar, so both change together. Set to "" to keep the harness default.
|
|
const BASE_BG = "#121317";
|
|
|
|
// (1) Sidebar background — paint the sidebar with the main content fill.
|
|
const MATCH_SIDEBAR_BG = true;
|
|
// Fill the sidebar takes. Default = main content bg (seamless). Use any
|
|
// color or var, e.g. "var(--dsw-alias-bg-layer-2)" for a subtle tint.
|
|
const SIDEBAR_FILL = "var(--dsw-alias-bg-base)";
|
|
|
|
// (2) Sidebar header — keep the logo row (and its native collapse/expand
|
|
// toggle button), but replace the DeepSeek brand logo with plain text.
|
|
// Set to "" to show no brand text at all (leaving only the toggle).
|
|
const SIDEBAR_BRAND_TEXT = "Harness";
|
|
// Remove the New Session button.
|
|
const HIDE_NEW_SESSION = true;
|
|
|
|
// (3) Header menu — collapse header utility buttons into one dropdown.
|
|
const COLLAPSE_HEADER = true;
|
|
// Label shown on the collapsed trigger button.
|
|
const TRIGGER_LABEL = "⋯";
|
|
// Close the dropdown after clicking one of its items.
|
|
const CLOSE_ON_ITEM_CLICK = true;
|
|
|
|
// (4) Panel + prompt surfaces — tone the elevated panels (the composer,
|
|
// code blocks, the Ongoing Goal bar, and generic dropdowns/inputs) to sit
|
|
// a touch off the base background. Default = slightly lighter than BASE_BG.
|
|
// Set to "" to leave the harness defaults.
|
|
const PANEL_BG = "#24262b";
|
|
|
|
// (5) Processed bar — restyle the smooth-stream "Processed <time>"
|
|
// collapsible (class `dshss-processed`, its own plugin's FOLD_CSS) to look
|
|
// like the prompt composer: full-width rounded pill with a thin border,
|
|
// the panel background, and a soft shadow.
|
|
const STYLE_PROCESSED_LIKE_PROMPT = true;
|
|
// -----------------------------------------------------------------------
|
|
|
|
const STYLE_ID = "dsh-ui-tweaks-style";
|
|
const COMPOSER_STORAGE_KEY = "dsh-ui-tweaks:composer-collapsed";
|
|
let composerCollapsed = readStoredComposerState();
|
|
let zenActive = false;
|
|
|
|
/** Assemble the combined static CSS for whichever features are enabled. */
|
|
function buildCss() {
|
|
const rules = [];
|
|
|
|
// (0) base background tone (main content + sidebar via feature 1)
|
|
if (BASE_BG) {
|
|
rules.push("*{--dsw-alias-bg-base:" + BASE_BG + " !important;}");
|
|
}
|
|
|
|
// (4) elevated panel + prompt surfaces (composer, code blocks, goal bar,
|
|
// generic panels) — one shade so they sit close to the base background.
|
|
if (PANEL_BG) {
|
|
rules.push(
|
|
"*{" +
|
|
"--dsw-specific-input-major:" + PANEL_BG + " !important;" + // composer card
|
|
"--dsw-alias-markdown-code-block:" + PANEL_BG + " !important;" + // code block body
|
|
"--dsw-alias-markdown-code-block-banner:" + PANEL_BG + " !important;" +// code block copy bar
|
|
"--dsw-specific-tip:" + PANEL_BG + " !important;" + // Ongoing Goal bar
|
|
"--dsw-alias-bg-layer-1:" + PANEL_BG + " !important;" + // generic panels/inputs
|
|
"--dsw-alias-bg-layer-2:" + PANEL_BG + " !important;" + // generic elevated
|
|
"}"
|
|
);
|
|
}
|
|
|
|
// (1) sidebar fill
|
|
if (MATCH_SIDEBAR_BG) {
|
|
rules.push("*{--dsw-specific-sidebar-fill:" + SIDEBAR_FILL + " !important;}");
|
|
}
|
|
|
|
// (2) sidebar header: keep the native collapse toggle, restyle the brand.
|
|
// Hide the original brand contents (logo mark + wordmark, text or svg).
|
|
rules.push('[class*="_brandIdentity"]{display:none !important;}');
|
|
if (SIDEBAR_BRAND_TEXT) {
|
|
// JSON.stringify safely double-quotes/escapes the label for CSS content.
|
|
const label = JSON.stringify(String(SIDEBAR_BRAND_TEXT));
|
|
rules.push(
|
|
// The brand BUTTON is the only element that *contains* the identity,
|
|
// so `:has()` pinpoints it regardless of class hashing or nesting.
|
|
'[class*="_brand"]:has([class*="_brandIdentity"]){overflow:visible !important;}',
|
|
'[class*="_brand"]:has([class*="_brandIdentity"])::after{content:' + label +
|
|
";font-size:18px;font-weight:600;letter-spacing:.04em;" +
|
|
"color:var(--dsw-alias-label-primary);white-space:nowrap;}",
|
|
// In the collapsed rail, hide the brand so only the toggle remains.
|
|
'[class*="_collapsed"] [class*="_brand"]:has([class*="_brandIdentity"]){display:none !important;}'
|
|
);
|
|
} else {
|
|
rules.push('[class*="_brand"]:has([class*="_brandIdentity"]){display:none !important;}');
|
|
}
|
|
if (HIDE_NEW_SESSION) {
|
|
rules.push('[class*="_root"] [class*="_newSession"]{display:none !important;}');
|
|
}
|
|
|
|
// (3) header menu (static parts)
|
|
if (COLLAPSE_HEADER) {
|
|
rules.push(
|
|
// host anchors the absolutely-positioned panel
|
|
'[data-dsh-hdr-menu="host"]{position:relative}',
|
|
// the utility row becomes a dropdown panel
|
|
'[data-dsh-hdr-menu="panel"]{' +
|
|
"position:absolute;top:calc(100% + 8px);right:0;z-index:2147483000;" +
|
|
"margin-left:0 !important;flex-direction:column !important;" +
|
|
"align-items:stretch !important;gap:6px !important;padding:8px;min-width:210px;" +
|
|
"background:var(--dsw-alias-bg-elevated, var(--dsw-specific-sidebar-fill, var(--dsw-alias-bg-base,#1f1f1f)));" +
|
|
"border:1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.12));" +
|
|
"border-radius:12px;box-shadow:0 12px 32px rgba(0,0,0,.45)}",
|
|
// hidden unless the host is marked open
|
|
'[data-dsh-hdr-menu="host"]:not([data-open="1"]) [data-dsh-hdr-menu="panel"]{display:none !important}',
|
|
// stack items full width, left aligned
|
|
'[data-dsh-hdr-menu="panel"] > *{width:100%;box-sizing:border-box;justify-content:flex-start !important}',
|
|
// known grouped entry (fleet-control) stacks vertically too
|
|
'[data-dsh-hdr-menu="panel"] .dsh-fleet-control{flex-direction:column;align-items:stretch;gap:6px;width:100%}',
|
|
'[data-dsh-hdr-menu="panel"] .dsh-fleet-control > *{width:100%;justify-content:flex-start}',
|
|
// the trigger pill
|
|
".dsh-hdr-menu-trigger{border:1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.12));" +
|
|
"height:32px;min-width:32px;color:var(--dsw-alias-label-primary);background:transparent;" +
|
|
"border-radius:18px;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;" +
|
|
"gap:5px;padding:6px 12px;font-size:15px;line-height:20px;white-space:nowrap;" +
|
|
"font-family:var(--dsw-font-family)}",
|
|
".dsh-hdr-menu-trigger:hover{background:var(--dsw-alias-interactive-bg-hover)}",
|
|
'.dsh-hdr-menu-trigger[aria-expanded="true"]{background:var(--dsw-alias-interactive-bg-hover)}'
|
|
);
|
|
}
|
|
|
|
// (5) Processed bar restyled like the prompt composer (.uV2eYG_card:
|
|
// 1px border, input-major bg, 22px radius, shadow-lv2, full width). The
|
|
// `dshss-processed` class is stable (its own plugin's FOLD_CSS), so we
|
|
// override those exact declarations with !important.
|
|
if (STYLE_PROCESSED_LIKE_PROMPT) {
|
|
rules.push(
|
|
// Collapsed pill — looks like the prompt composer. Label + chevron
|
|
// stay grouped on the left (flex-start) so the arrow sits right next
|
|
// to the text; a tiny spacer keeps the box full width.
|
|
".dshss-processed{" +
|
|
"box-sizing:border-box !important;" +
|
|
"width:100% !important;max-width:var(--dsh-chat-content-width) !important;" +
|
|
"align-self:center !important;" +
|
|
"justify-content:flex-start !important;gap:8px !important;" +
|
|
"min-height:0 !important;padding:5px 14px !important;" +
|
|
"border:1px solid var(--dsw-alias-border-l2-darkmode-thin, var(--dsw-alias-border-l2, rgba(255,255,255,.12))) !important;" +
|
|
"border-radius:22px !important;" +
|
|
"background:var(--dsw-specific-input-major) !important;" +
|
|
"box-shadow:var(--dsw-shadow-lv2) !important;" +
|
|
"color:var(--dsw-alias-label-secondary) !important;}",
|
|
// Keep the chevron aligned to the text baseline, not stretched.
|
|
".dshss-processed .dshss-processed-chevron{align-self:center !important;margin:0 !important;}",
|
|
".dshss-processed:hover{" +
|
|
"background:color-mix(in srgb, var(--dsw-specific-input-major), #ffffff 6%) !important;" +
|
|
"border-color:var(--dsw-alias-border-l2, rgba(255,255,255,.18)) !important;" +
|
|
"color:var(--dsw-alias-label-primary) !important;}",
|
|
// Expanded — drop the pill and blend into the page background: use the
|
|
// base bg, no border, no shadow, so the opened section reads as inline
|
|
// content rather than a floating capsule.
|
|
'.dshss-processed[aria-expanded="true"]{' +
|
|
"background:var(--dsw-alias-bg-base) !important;" +
|
|
"border-color:transparent !important;" +
|
|
"box-shadow:none !important;" +
|
|
"color:var(--dsw-alias-label-primary) !important;}",
|
|
// Expanded hover — subtle highlight since it otherwise blends into bg.
|
|
'.dshss-processed[aria-expanded="true"]:hover{' +
|
|
"background:color-mix(in srgb, var(--dsw-alias-bg-base), #ffffff 6%) !important;}"
|
|
);
|
|
}
|
|
|
|
rules.push(
|
|
'[data-sidebar-collapsed] [class*="_sidebarCol"] [class*="_regionArea"]{display:none !important;}',
|
|
'[data-composer-seat]{position:relative}',
|
|
'.dsh-chat-controls{position:absolute;right:24px;bottom:calc(100% + 6px);z-index:12;display:flex;align-items:center;gap:6px}',
|
|
'.dsh-chat-control{height:28px;min-width:28px;padding:0 9px;border:1px solid var(--dsw-alias-border-l2,rgba(255,255,255,.12));border-radius:14px;background:var(--dsw-specific-input-major);color:var(--dsw-alias-label-secondary);font:var(--dsw-font-xs-13);cursor:pointer;box-shadow:var(--dsw-shadow-lv1)}',
|
|
'.dsh-chat-control:hover{color:var(--dsw-alias-label-primary);background:color-mix(in srgb,var(--dsw-specific-input-major),#fff 6%)}',
|
|
'[data-composer-seat][data-dsh-composer-collapsed="1"]{min-height:38px;justify-content:center}',
|
|
'[data-composer-seat][data-dsh-composer-collapsed="1"]>:not(.dsh-chat-controls){display:none !important}',
|
|
'[data-composer-seat][data-dsh-composer-collapsed="1"]>.dsh-chat-controls{position:static;align-self:flex-end;margin:4px 24px 6px 0}',
|
|
'html[data-dsh-zen="1"] [class*="_frame"][style*="grid-template-columns"]{grid-template-columns:0 minmax(0,1fr) 0 !important}',
|
|
'html[data-dsh-zen="1"] [class*="_sidebarCol"],html[data-dsh-zen="1"] [class*="_detailsCol"],html[data-dsh-zen="1"] [class*="_handle"]{display:none !important}',
|
|
'html[data-dsh-zen="1"] [data-phase]>[class*="_header"]{display:none !important}',
|
|
'html[data-dsh-zen="1"] [data-composer-seat]{min-height:38px;justify-content:center}',
|
|
'html[data-dsh-zen="1"] [data-composer-seat]>:not(.dsh-chat-controls){display:none !important}',
|
|
'html[data-dsh-zen="1"] [data-composer-seat]>.dsh-chat-controls{position:static;align-self:flex-end;margin:4px 24px 6px 0}',
|
|
'html[data-dsh-zen="1"] .dsh-composer-collapse-toggle{display:none !important}'
|
|
);
|
|
|
|
return rules.join("");
|
|
}
|
|
|
|
/** Insert (or update) the shared override <style>. */
|
|
function installStyle() {
|
|
let el = document.getElementById(STYLE_ID);
|
|
if (!el) {
|
|
el = document.createElement("style");
|
|
el.id = STYLE_ID;
|
|
el.dataset.plugin = "dsh-ui-tweaks";
|
|
(document.head || document.documentElement).appendChild(el);
|
|
}
|
|
el.textContent = buildCss();
|
|
}
|
|
|
|
// ---- (3) header-menu dynamic behavior ---------------------------------
|
|
|
|
function readStoredComposerState() {
|
|
try {
|
|
return window.localStorage.getItem(COMPOSER_STORAGE_KEY) === "1";
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function storeComposerState() {
|
|
try {
|
|
window.localStorage.setItem(COMPOSER_STORAGE_KEY, composerCollapsed ? "1" : "0");
|
|
} catch {}
|
|
}
|
|
|
|
function updateChatControls() {
|
|
document.querySelectorAll("[data-composer-seat]").forEach((seat) => {
|
|
if (composerCollapsed) seat.setAttribute("data-dsh-composer-collapsed", "1");
|
|
else seat.removeAttribute("data-dsh-composer-collapsed");
|
|
const collapse = seat.querySelector(".dsh-composer-collapse-toggle");
|
|
if (collapse) {
|
|
const collapseLabel = composerCollapsed ? "⌃ Chat" : "⌄ Chat";
|
|
if (collapse.textContent !== collapseLabel) collapse.textContent = collapseLabel;
|
|
collapse.title = composerCollapsed ? "Expand bottom chat bar" : "Collapse bottom chat bar";
|
|
collapse.setAttribute("aria-expanded", composerCollapsed ? "false" : "true");
|
|
}
|
|
const zen = seat.querySelector(".dsh-zen-toggle");
|
|
if (zen) {
|
|
const zenLabel = zenActive ? "Exit Zen" : "Zen";
|
|
if (zen.textContent !== zenLabel) zen.textContent = zenLabel;
|
|
zen.title = zenActive ? "Exit Zen mode" : "Enter Zen mode";
|
|
zen.setAttribute("aria-pressed", zenActive ? "true" : "false");
|
|
}
|
|
});
|
|
}
|
|
|
|
function setComposerCollapsed(collapsed) {
|
|
composerCollapsed = collapsed;
|
|
storeComposerState();
|
|
updateChatControls();
|
|
}
|
|
|
|
function setZenActive(active) {
|
|
zenActive = active;
|
|
if (active) document.documentElement.setAttribute("data-dsh-zen", "1");
|
|
else document.documentElement.removeAttribute("data-dsh-zen");
|
|
updateChatControls();
|
|
}
|
|
|
|
function makeChatControls() {
|
|
const controls = document.createElement("div");
|
|
controls.className = "dsh-chat-controls";
|
|
|
|
const collapse = document.createElement("button");
|
|
collapse.type = "button";
|
|
collapse.className = "dsh-chat-control dsh-composer-collapse-toggle";
|
|
collapse.setAttribute("aria-label", "Toggle bottom chat bar");
|
|
collapse.addEventListener("click", () => setComposerCollapsed(!composerCollapsed));
|
|
|
|
const zen = document.createElement("button");
|
|
zen.type = "button";
|
|
zen.className = "dsh-chat-control dsh-zen-toggle";
|
|
zen.setAttribute("aria-label", "Toggle Zen mode");
|
|
zen.addEventListener("click", () => setZenActive(!zenActive));
|
|
|
|
controls.append(collapse, zen);
|
|
return controls;
|
|
}
|
|
|
|
function enhanceChatControls() {
|
|
document.querySelectorAll("[data-composer-seat]").forEach((seat) => {
|
|
if (!seat.querySelector(":scope > .dsh-chat-controls")) seat.appendChild(makeChatControls());
|
|
});
|
|
updateChatControls();
|
|
}
|
|
|
|
function removeChatControls() {
|
|
setZenActive(false);
|
|
document.querySelectorAll(".dsh-chat-controls").forEach((controls) => controls.remove());
|
|
document.querySelectorAll("[data-composer-seat]").forEach((seat) => seat.removeAttribute("data-dsh-composer-collapsed"));
|
|
}
|
|
|
|
/** Close every open header menu (optionally sparing one host). */
|
|
function closeAll(except) {
|
|
document.querySelectorAll('[data-dsh-hdr-menu="host"][data-open="1"]').forEach((host) => {
|
|
if (host === except) return;
|
|
host.removeAttribute("data-open");
|
|
const t = host.querySelector(".dsh-hdr-menu-trigger");
|
|
if (t) t.setAttribute("aria-expanded", "false");
|
|
});
|
|
}
|
|
|
|
/** Build the trigger button for a given host (the .headerActions element). */
|
|
function makeTrigger(host) {
|
|
const btn = document.createElement("button");
|
|
btn.type = "button";
|
|
btn.className = "dsh-hdr-menu-trigger";
|
|
btn.textContent = TRIGGER_LABEL;
|
|
btn.title = "Session actions";
|
|
btn.setAttribute("aria-label", "Session actions menu");
|
|
btn.setAttribute("aria-expanded", "false");
|
|
btn.addEventListener("click", (e) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
const open = host.getAttribute("data-open") === "1";
|
|
closeAll(host);
|
|
if (open) {
|
|
host.removeAttribute("data-open");
|
|
btn.setAttribute("aria-expanded", "false");
|
|
} else {
|
|
host.setAttribute("data-open", "1");
|
|
btn.setAttribute("aria-expanded", "true");
|
|
}
|
|
});
|
|
return btn;
|
|
}
|
|
|
|
/** Enhance every header-utilities region currently in the DOM (idempotent). */
|
|
function enhance() {
|
|
enhanceChatControls();
|
|
if (!COLLAPSE_HEADER) return;
|
|
const panels = document.querySelectorAll('[class*="_headerUtilities"]');
|
|
panels.forEach((panel) => {
|
|
const host = panel.parentElement;
|
|
if (!host) return;
|
|
panel.setAttribute("data-dsh-hdr-menu", "panel");
|
|
host.setAttribute("data-dsh-hdr-menu", "host");
|
|
// close-on-item-click wiring (once)
|
|
if (CLOSE_ON_ITEM_CLICK && panel.getAttribute("data-dsh-hdr-wired") !== "1") {
|
|
panel.setAttribute("data-dsh-hdr-wired", "1");
|
|
panel.addEventListener("click", (e) => {
|
|
if (e.target && e.target.closest("button,a,[role='menuitem'],[role='button']")) {
|
|
// let the real handler run, then close on next tick
|
|
setTimeout(() => closeAll(), 0);
|
|
}
|
|
});
|
|
}
|
|
// ensure exactly one trigger, placed right after the panel
|
|
if (!host.querySelector(".dsh-hdr-menu-trigger")) {
|
|
const trigger = makeTrigger(host);
|
|
if (panel.nextSibling) host.insertBefore(trigger, panel.nextSibling);
|
|
else host.appendChild(trigger);
|
|
}
|
|
});
|
|
}
|
|
|
|
let scheduled = false;
|
|
function scheduleEnhance() {
|
|
if (scheduled) return;
|
|
scheduled = true;
|
|
requestAnimationFrame(() => {
|
|
scheduled = false;
|
|
enhance();
|
|
});
|
|
}
|
|
|
|
/** No client services required — pure DOM/CSS. */
|
|
const inject = [];
|
|
|
|
/** Client plugin body: install CSS, then wire the header dropdown if on. */
|
|
function apply(ctx) {
|
|
const start = () => {
|
|
installStyle();
|
|
|
|
enhance();
|
|
|
|
const observer = new MutationObserver(scheduleEnhance);
|
|
observer.observe(document.body, { childList: true, subtree: true });
|
|
|
|
const onDocClick = (e) => {
|
|
const host = e.target && e.target.closest && e.target.closest('[data-dsh-hdr-menu="host"]');
|
|
if (!host) closeAll();
|
|
};
|
|
const onKey = (e) => {
|
|
if (e.key !== "Escape") return;
|
|
closeAll();
|
|
if (zenActive) setZenActive(false);
|
|
};
|
|
document.addEventListener("click", onDocClick, true);
|
|
document.addEventListener("keydown", onKey, true);
|
|
|
|
return () => {
|
|
observer.disconnect();
|
|
document.removeEventListener("click", onDocClick, true);
|
|
document.removeEventListener("keydown", onKey, true);
|
|
closeAll();
|
|
removeChatControls();
|
|
const style = document.getElementById(STYLE_ID);
|
|
if (style) style.remove();
|
|
document.querySelectorAll(".dsh-hdr-menu-trigger").forEach((n) => n.remove());
|
|
document.querySelectorAll('[data-dsh-hdr-menu]').forEach((n) => {
|
|
n.removeAttribute("data-dsh-hdr-menu");
|
|
n.removeAttribute("data-open");
|
|
n.removeAttribute("data-dsh-hdr-wired");
|
|
});
|
|
};
|
|
};
|
|
|
|
if (ctx && typeof ctx.effect === "function") ctx.effect(start, "dsh-ui-tweaks: sidebar + header tweaks");
|
|
else start();
|
|
}
|
|
|
|
exports.apply = apply;
|
|
exports.inject = inject;
|
|
return module.exports;
|
|
},
|
|
});
|