Remove experimental chat and Zen controls

Roll back the Chat collapse, Zen mode, composer reflow repair, and related regression fixture after they interfered with the native prompt. Keep only the collapsed-sidebar agent-tree hiding selector.
This commit is contained in:
Шурупов Илья Викторович 2026-09-14 18:29:06 +03:00
parent 35f26fa32b
commit 9db94f8aa7
4 changed files with 14 additions and 389 deletions

View file

@ -89,7 +89,6 @@ window.__ModuleLoader__.load({
// -----------------------------------------------------------------------
const STYLE_ID = "dsh-ui-tweaks-style";
let zenActive = false;
/** Assemble the combined static CSS for whichever features are enabled. */
function buildCss() {
@ -214,19 +213,7 @@ window.__ModuleLoader__.load({
);
}
rules.push(
'[data-sidebar-collapsed] [class*="_sidebarCol"] [class*="_regionArea"]{display:none !important;}',
'[data-phase]{position:relative}',
'.dsh-chat-controls{z-index:12;display:flex;flex:none;align-items:center;gap:6px;margin-left:8px}',
'.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%)}',
'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"]{visibility:hidden !important;pointer-events:none !important;border:0 !important}',
'html[data-dsh-zen="1"] [class*="_handle"]{display:none !important}',
'html[data-dsh-zen="1"] [data-phase] header[class*="_header"]{display:none !important}',
'html[data-dsh-zen="1"] [data-composer-seat]{position:absolute !important;left:0;right:0;bottom:0;visibility:hidden !important;pointer-events:none !important}',
'.dsh-chat-controls[data-floating="1"]{position:absolute;right:24px;top:12px;margin-left:0}'
);
rules.push('[data-sidebar-collapsed] [class*="_sidebarCol"] [class*="_regionArea"]{display:none !important;}');
return rules.join("");
}
@ -245,133 +232,6 @@ window.__ModuleLoader__.load({
// ---- (3) header-menu dynamic behavior ---------------------------------
function restoreNativeComposer() {
try {
window.localStorage.removeItem("dsh-ui-tweaks:composer-collapsed");
} catch {}
document.querySelectorAll("[data-composer-seat]").forEach((seat) => seat.removeAttribute("data-dsh-composer-collapsed"));
}
function repairNativeComposerLayouts() {
document.querySelectorAll("[data-input-scroll]").forEach((scrollport) => {
if (scrollport.getAttribute("data-dsh-composer-repaired") === "1") return;
scrollport.setAttribute("data-dsh-composer-repaired", "1");
requestAnimationFrame(() => requestAnimationFrame(() => {
if (!scrollport.isConnected) return;
const mirror = scrollport.querySelector("[data-input-mirror]");
const input = scrollport.querySelector("textarea");
if (!mirror || !input) return;
const mirrorPosition = mirror.style.position;
mirror.style.position = "absolute";
mirror.offsetHeight;
scrollport.offsetHeight;
mirror.style.position = mirrorPosition;
mirror.offsetHeight;
const inputHeight = input.style.height;
input.style.height = `${String(input.clientHeight + 1)}px`;
input.offsetHeight;
input.style.height = inputHeight;
input.offsetHeight;
const seat = scrollport.closest("[data-composer-seat]");
seat?.parentElement?.style.setProperty("--dsh-composer-height", `${String(seat.offsetHeight)}px`);
}));
});
}
function updateChatControls() {
document.querySelectorAll(".dsh-chat-controls").forEach((controls) => {
const zen = controls.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 conversationRoots() {
const roots = [];
document.querySelectorAll("[data-composer-seat]").forEach((seat) => {
const conversation = seat.closest("[data-phase]");
if (conversation && !roots.includes(conversation)) roots.push(conversation);
});
return roots;
}
function normalizeChatControls() {
const roots = conversationRoots();
const validRoots = new Set(roots);
const owners = new Map();
document.querySelectorAll(".dsh-chat-controls").forEach((controls) => {
const owner = controls.closest("[data-phase]");
if (!validRoots.has(owner) || owners.has(owner)) {
controls.remove();
return;
}
owners.set(owner, controls);
});
roots.forEach((conversation) => {
if (owners.has(conversation)) return;
const controls = makeChatControls();
conversation.appendChild(controls);
owners.set(conversation, controls);
});
return roots;
}
function placeChatControls(roots = conversationRoots()) {
roots.forEach((conversation) => {
const controls = Array.from(conversation.querySelectorAll(".dsh-chat-controls")).find((candidate) => candidate.closest("[data-phase]") === conversation);
if (!controls) return;
const git = conversation.querySelector(".dsh-git-working-dirs");
const utilities = conversation.querySelector('header [class*="_headerUtilities"]');
const anchor = git ?? utilities;
if (!zenActive && anchor?.parentElement) {
controls.removeAttribute("data-floating");
if (controls.parentElement !== anchor.parentElement) anchor.parentElement.insertBefore(controls, anchor);
return;
}
controls.setAttribute("data-floating", "1");
if (controls.parentElement !== conversation) conversation.appendChild(controls);
});
}
function setZenActive(active) {
zenActive = active;
if (active) document.documentElement.setAttribute("data-dsh-zen", "1");
else document.documentElement.removeAttribute("data-dsh-zen");
placeChatControls(normalizeChatControls());
updateChatControls();
}
function makeChatControls() {
const controls = document.createElement("div");
controls.className = "dsh-chat-controls";
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(zen);
return controls;
}
function enhanceChatControls() {
const roots = normalizeChatControls();
placeChatControls(roots);
updateChatControls();
}
function removeChatControls() {
setZenActive(false);
document.querySelectorAll(".dsh-chat-controls").forEach((controls) => controls.remove());
document.querySelectorAll('[data-dsh-composer-repaired="1"]').forEach((scrollport) => scrollport.removeAttribute("data-dsh-composer-repaired"));
restoreNativeComposer();
}
/** Close every open header menu (optionally sparing one host). */
function closeAll(except) {
document.querySelectorAll('[data-dsh-hdr-menu="host"][data-open="1"]').forEach((host) => {
@ -409,9 +269,6 @@ window.__ModuleLoader__.load({
/** Enhance every header-utilities region currently in the DOM (idempotent). */
function enhance() {
repairNativeComposerLayouts();
enhanceChatControls();
if (!COLLAPSE_HEADER) return;
const panels = document.querySelectorAll('[class*="_headerUtilities"]');
panels.forEach((panel) => {
const host = panel.parentElement;
@ -454,7 +311,16 @@ window.__ModuleLoader__.load({
function apply(ctx) {
const start = () => {
installStyle();
restoreNativeComposer();
// Features 1 & 2 are pure CSS — nothing more to wire.
if (!COLLAPSE_HEADER) {
return () => {
const style = document.getElementById(STYLE_ID);
if (style) style.remove();
};
}
// Feature 3 — dynamic header dropdown.
enhance();
const observer = new MutationObserver(scheduleEnhance);
@ -465,9 +331,7 @@ window.__ModuleLoader__.load({
if (!host) closeAll();
};
const onKey = (e) => {
if (e.key !== "Escape") return;
closeAll();
if (zenActive) setZenActive(false);
if (e.key === "Escape") closeAll();
};
document.addEventListener("click", onDocClick, true);
document.addEventListener("keydown", onKey, true);
@ -477,7 +341,6 @@ window.__ModuleLoader__.load({
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());

View file

@ -1,7 +1,7 @@
{
"name": "dsh-ui-tweaks",
"description": "dsh web plugin: cohesive sidebar, header, and Zen-mode controls for the DSH Web chat interface. Client-only.",
"version": "0.3.1",
"description": "dsh web plugin: sidebar appearance, collapsed-tree hiding, and conversation-header utility menu tweaks without modifying the chat composer. Client-only.",
"version": "0.4.0",
"private": true,
"type": "module",
"engines": {

View file

@ -7,7 +7,6 @@ cd "$repository_directory"
bash -n install.sh uninstall.sh bin/dsh-app bin/free-port-3080.sh test/*.sh
node --check scripts/apply-compatibility-patches.mjs
node test/profile-manifest.test.mjs
node test/ui-controls.test.mjs
python3 test/editor-artifacts.test.py
python3 test/editor-runtime.test.py
python3 test/cloudflared-artifact.test.py

View file

@ -1,237 +0,0 @@
import assert from 'node:assert/strict'
import { readFile } from 'node:fs/promises'
import vm from 'node:vm'
class Element {
constructor(tagName) {
this.tagName = tagName
this.attributes = new Map()
this.children = []
this.listeners = new Map()
this.dataset = {}
this.parentElement = null
this.className = ''
this.textContent = ''
this.title = ''
this.type = ''
this.clientHeight = 32
this.offsetHeight = 32
this.isConnected = true
const properties = new Map()
this.style = {
position: '',
height: '',
setProperty: (name, value) => properties.set(name, String(value)),
getPropertyValue: (name) => properties.get(name) ?? '',
}
}
append(...children) {
for (const child of children) this.appendChild(child)
}
appendChild(child) {
if (child.parentElement) child.parentElement.children = child.parentElement.children.filter((candidate) => candidate !== child)
child.parentElement = this
this.children.push(child)
return child
}
insertBefore(child, reference) {
if (child.parentElement) child.parentElement.children = child.parentElement.children.filter((candidate) => candidate !== child)
const index = this.children.indexOf(reference)
child.parentElement = this
this.children.splice(index < 0 ? this.children.length : index, 0, child)
return child
}
addEventListener(name, listener) {
this.listeners.set(name, listener)
}
click() {
this.listeners.get('click')?.({ preventDefault() {}, stopPropagation() {}, target: this })
}
setAttribute(name, value) {
this.attributes.set(name, String(value))
}
getAttribute(name) {
return this.attributes.get(name) ?? null
}
removeAttribute(name) {
this.attributes.delete(name)
}
querySelector(selector) {
if (selector === ':scope > .dsh-chat-controls') return this.children.find((child) => child.hasClass('dsh-chat-controls')) ?? null
if (selector === 'header [class*="_headerUtilities"]') return this.descendants().find((child) => child.tagName === 'div' && child.className.includes('_headerUtilities')) ?? null
if (selector === '[data-input-mirror]') return this.descendants().find((child) => child.attributes.has('data-input-mirror')) ?? null
if (selector === 'textarea') return this.descendants().find((child) => child.tagName === 'textarea') ?? null
if (selector.startsWith('.')) return this.descendants().find((child) => child.hasClass(selector.slice(1))) ?? null
return null
}
querySelectorAll(selector) {
if (selector.startsWith('.')) return this.descendants().filter((child) => child.hasClass(selector.slice(1)))
return []
}
closest(selector) {
let current = this
while (current) {
if (selector === '[data-phase]' && current.attributes.has('data-phase')) return current
if (selector === '[data-composer-seat]' && current.attributes.has('data-composer-seat')) return current
current = current.parentElement
}
return null
}
hasClass(name) {
return this.className.split(/\s+/).includes(name)
}
descendants() {
return this.children.flatMap((child) => [child, ...child.descendants()])
}
remove() {
if (!this.parentElement) return
this.parentElement.children = this.parentElement.children.filter((child) => child !== this)
this.parentElement = null
}
}
const documentElement = new Element('html')
const head = new Element('head')
const body = new Element('body')
const conversation = new Element('div')
conversation.setAttribute('data-phase', 'active')
const header = new Element('header')
const titleRow = new Element('div')
const headerActions = new Element('div')
const git = new Element('div')
git.className = 'dsh-git-working-dirs'
const utilities = new Element('div')
utilities.className = 'fixture_headerUtilities'
headerActions.appendChild(git)
titleRow.append(headerActions, utilities)
header.appendChild(titleRow)
const seat = new Element('div')
seat.setAttribute('data-composer-seat', '')
seat.setAttribute('data-dsh-composer-collapsed', '1')
const inputScroll = new Element('div')
inputScroll.setAttribute('data-input-scroll', '')
const input = new Element('textarea')
const mirror = new Element('div')
mirror.setAttribute('data-input-mirror', '')
inputScroll.append(input, mirror)
seat.appendChild(inputScroll)
const nestedPhase = new Element('div')
nestedPhase.setAttribute('data-phase', 'streaming')
const strayControls = new Element('div')
strayControls.className = 'dsh-chat-controls'
nestedPhase.appendChild(strayControls)
conversation.append(header, nestedPhase, seat)
body.appendChild(conversation)
const documentListeners = new Map()
const allElements = () => [documentElement, head, body, ...head.descendants(), ...body.descendants()]
const document = {
documentElement,
head,
body,
createElement: (tagName) => new Element(tagName),
getElementById: (id) => allElements().find((element) => element.id === id) ?? null,
querySelectorAll(selector) {
if (selector === '[data-phase]') return allElements().filter((element) => element.attributes.has('data-phase'))
if (selector === '[data-composer-seat]') return allElements().filter((element) => element.attributes.has('data-composer-seat'))
if (selector === '[data-input-scroll]') return allElements().filter((element) => element.attributes.has('data-input-scroll'))
if (selector === '[data-dsh-composer-repaired="1"]') return allElements().filter((element) => element.getAttribute('data-dsh-composer-repaired') === '1')
if (selector === '.dsh-chat-controls') return allElements().filter((element) => element.hasClass('dsh-chat-controls'))
if (selector === '.dsh-hdr-menu-trigger' || selector === '[data-dsh-hdr-menu]' || selector === '[class*="_headerUtilities"]') return []
return []
},
addEventListener: (name, listener) => documentListeners.set(name, listener),
removeEventListener: (name) => documentListeners.delete(name),
}
const storage = new Map([['dsh-ui-tweaks:composer-collapsed', '1']])
const window = {
document,
localStorage: {
getItem: (key) => storage.get(key) ?? null,
setItem: (key, value) => storage.set(key, String(value)),
removeItem: (key) => storage.delete(key),
},
}
window.window = window
let definition
window.__ModuleLoader__ = { load: (candidate) => { definition = candidate } }
class MutationObserver {
observe() {}
disconnect() {}
}
const source = await readFile(new URL('../plugins/dsh-ui-tweaks/lib/client.js', import.meta.url), 'utf8')
vm.runInNewContext(source, {
window,
document,
MutationObserver,
requestAnimationFrame: (callback) => callback(),
setTimeout,
})
const plugin = definition.factory(() => {})
let cleanup
plugin.apply({ effect: (start) => { cleanup = start() } })
const controls = conversation.querySelector('.dsh-chat-controls')
assert(controls)
assert.equal(document.querySelectorAll('.dsh-chat-controls').length, 1)
assert.equal(strayControls.parentElement, null)
assert.equal(controls.parentElement, headerActions)
assert(controls.parentElement.children.indexOf(controls) < controls.parentElement.children.indexOf(git))
const zen = controls.querySelector('.dsh-zen-toggle')
assert.equal(controls.querySelector('.dsh-composer-collapse-toggle'), null)
assert.equal(zen.textContent, 'Zen')
assert.equal(seat.getAttribute('data-dsh-composer-collapsed'), null)
assert.equal(storage.has('dsh-ui-tweaks:composer-collapsed'), false)
assert.equal(inputScroll.getAttribute('data-dsh-composer-repaired'), '1')
assert.equal(mirror.style.position, '')
assert.equal(input.style.height, '')
assert.equal(conversation.style.getPropertyValue('--dsh-composer-height'), '32px')
zen.click()
assert.equal(documentElement.getAttribute('data-dsh-zen'), '1')
assert.equal(zen.textContent, 'Exit Zen')
assert.equal(seat.getAttribute('data-dsh-composer-collapsed'), null)
assert.equal(controls.parentElement, conversation)
assert.equal(controls.getAttribute('data-floating'), '1')
documentListeners.get('keydown')?.({ key: 'Escape' })
assert.equal(documentElement.getAttribute('data-dsh-zen'), null)
assert.equal(zen.textContent, 'Zen')
assert.equal(controls.parentElement, headerActions)
assert(controls.parentElement.children.indexOf(controls) < controls.parentElement.children.indexOf(git))
assert.equal(controls.getAttribute('data-floating'), null)
cleanup()
assert.equal(conversation.querySelector('.dsh-chat-controls'), null)
assert.equal(seat.getAttribute('data-dsh-composer-collapsed'), null)
assert.equal(inputScroll.getAttribute('data-dsh-composer-repaired'), null)
assert.equal(documentElement.getAttribute('data-dsh-zen'), null)
assert(source.includes('[data-sidebar-collapsed] [class*="_sidebarCol"] [class*="_regionArea"]'))
assert(source.includes('[class*="_detailsCol"]{visibility:hidden !important;pointer-events:none !important;border:0 !important}'))
assert(!source.includes('[class*="_detailsCol"],html[data-dsh-zen="1"] [class*="_handle"]{display:none'))
assert(source.includes('.dsh-chat-controls{z-index:12;display:flex;flex:none'))
assert(source.includes('.dsh-chat-controls[data-floating="1"]{position:absolute;right:24px;top:12px'))
assert(!source.includes('dsh-composer-collapse-toggle'))
assert(!source.includes('[data-composer-seat][data-dsh-composer-collapsed="1"]'))
assert(source.includes('[data-composer-seat]{position:absolute !important;left:0;right:0;bottom:0;visibility:hidden'))
assert(source.includes('[data-phase] header[class*="_header"]{display:none !important}'))
console.log('UI controls test passed')