mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-29 18:46:59 +00:00
refactor(desktop): drop leva from the chat backdrop
The backdrop shipped a leva control panel — a Shift+Y dev tweak surface for opacity, blend mode, filters and a radius scalar — that has never been touched since the app landed. Its hooks ran in every build, dev and packaged alike: only the panel's visibility was gated on `import.meta.env.DEV`, not the two `useControls` calls or the zustand store behind them. Inline the values it was already rendering with and delete the dependency; leva was the only thing importing it in the tree. One of those controls was doing real damage. `--radius-scalar` shipped as 0.6 in styles.css, but the slider's 0.2 default was written onto the root element on mount, so every window that mounts the backdrop rendered at 0.2 and every window that doesn't kept 0.6. The token now declares 0.2 — the value the chat has actually rendered at all along. The remaining static values (0.025 opacity, difference blend, 160dvh, top left, invert) become classes; saturate(1)/brightness(1) were identity and are dropped.
This commit is contained in:
parent
84858d76ba
commit
058c4376d2
4 changed files with 16 additions and 108 deletions
|
|
@ -106,7 +106,6 @@
|
|||
"hast-util-to-text": "^4.0.2",
|
||||
"ignore": "^7.0.5",
|
||||
"katex": "^0.16.45",
|
||||
"leva": "^0.10.1",
|
||||
"mermaid": "^11.15.0",
|
||||
"motion": "^12.38.0",
|
||||
"nanostores": "^1.3.0",
|
||||
|
|
|
|||
|
|
@ -1,118 +1,24 @@
|
|||
import { useStore } from '@nanostores/react'
|
||||
import { Leva, useControls } from 'leva'
|
||||
import { type CSSProperties, useEffect, useState } from 'react'
|
||||
|
||||
import { $backdrop } from '@/store/backdrop'
|
||||
|
||||
const BLEND_MODES = [
|
||||
'normal',
|
||||
'multiply',
|
||||
'screen',
|
||||
'overlay',
|
||||
'darken',
|
||||
'lighten',
|
||||
'color-dodge',
|
||||
'color-burn',
|
||||
'hard-light',
|
||||
'soft-light',
|
||||
'difference',
|
||||
'exclusion',
|
||||
'hue',
|
||||
'saturation',
|
||||
'color',
|
||||
'luminosity'
|
||||
] as const
|
||||
|
||||
type BlendMode = (typeof BLEND_MODES)[number]
|
||||
const assetPath = (path: string) => `${import.meta.env.BASE_URL}${path.replace(/^\/+/, '')}`
|
||||
|
||||
export function Backdrop() {
|
||||
const [controlsOpen, setControlsOpen] = useState(false)
|
||||
const on = useStore($backdrop)
|
||||
|
||||
useEffect(() => {
|
||||
if (!import.meta.env.DEV) {
|
||||
return
|
||||
}
|
||||
|
||||
const onKeyDown = (event: KeyboardEvent) => {
|
||||
const target = event.target as HTMLElement | null
|
||||
|
||||
const editing =
|
||||
target?.isContentEditable ||
|
||||
target instanceof HTMLInputElement ||
|
||||
target instanceof HTMLTextAreaElement ||
|
||||
target instanceof HTMLSelectElement
|
||||
|
||||
if (editing || event.repeat || event.altKey || event.ctrlKey || event.metaKey) {
|
||||
return
|
||||
}
|
||||
|
||||
if (event.shiftKey && event.code === 'KeyY') {
|
||||
setControlsOpen(open => !open)
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('keydown', onKeyDown)
|
||||
|
||||
return () => window.removeEventListener('keydown', onKeyDown)
|
||||
}, [])
|
||||
|
||||
const shape = useControls(
|
||||
'UI / Shape',
|
||||
{ radiusScalar: { value: 0.2, min: 0, max: 2, step: 0.1, label: 'radius scalar' } },
|
||||
{ collapsed: true }
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
document.documentElement.style.setProperty('--radius-scalar', String(shape.radiusScalar))
|
||||
}, [shape.radiusScalar])
|
||||
|
||||
const statue = useControls(
|
||||
'Backdrop / Statue',
|
||||
{
|
||||
enabled: { value: true, label: 'on' },
|
||||
opacity: { value: 0.025, min: 0, max: 1, step: 0.005 },
|
||||
blendMode: { value: 'difference' as BlendMode, options: BLEND_MODES, label: 'blend' },
|
||||
invert: { value: true, label: 'invert color' },
|
||||
saturate: { value: 1, min: 0, max: 3, step: 0.05, label: 'saturate' },
|
||||
brightness: { value: 1, min: 0, max: 2, step: 0.05, label: 'brightness' },
|
||||
objectPosition: {
|
||||
value: 'top left',
|
||||
options: ['top left', 'top right', 'bottom left', 'bottom right', 'center', 'top', 'bottom', 'left', 'right'],
|
||||
label: 'position'
|
||||
},
|
||||
scale: { value: 160, min: 100, max: 300, step: 5, label: 'height (dvh)' }
|
||||
},
|
||||
{ collapsed: true }
|
||||
)
|
||||
if (!on) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Leva collapsed hidden={!import.meta.env.DEV || !controlsOpen} titleBar={{ title: 'backdrop', drag: true }} />
|
||||
|
||||
{on && statue.enabled && (
|
||||
<div
|
||||
aria-hidden
|
||||
className="pointer-events-none absolute inset-0 z-2"
|
||||
style={{
|
||||
mixBlendMode: statue.blendMode as CSSProperties['mixBlendMode'],
|
||||
opacity: statue.opacity
|
||||
}}
|
||||
>
|
||||
<img
|
||||
alt=""
|
||||
className="w-auto min-w-dvw object-cover"
|
||||
fetchPriority="low"
|
||||
src={assetPath('ds-assets/filler-bg0.jpg')}
|
||||
style={{
|
||||
height: `${statue.scale}dvh`,
|
||||
objectPosition: statue.objectPosition,
|
||||
filter: `invert(calc(${statue.invert ? 1 : 0} * var(--backdrop-invert-mul, 1))) saturate(${statue.saturate}) brightness(${statue.brightness})`
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
<div aria-hidden className="pointer-events-none absolute inset-0 z-2 opacity-[0.025] mix-blend-difference">
|
||||
<img
|
||||
alt=""
|
||||
className="h-[160dvh] w-auto min-w-dvw object-cover object-left-top [filter:invert(var(--backdrop-invert-mul,1))]"
|
||||
fetchPriority="low"
|
||||
src={assetPath('ds-assets/filler-bg0.jpg')}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -387,7 +387,11 @@
|
|||
--dt-spacing-mul: 1;
|
||||
|
||||
--radius: 0.75rem;
|
||||
--radius-scalar: 0.6;
|
||||
/* Effective value everywhere: the chat backdrop used to force 0.2 onto the
|
||||
root element at runtime (an ungated leva slider default), so this 0.6
|
||||
only ever applied to the windows that don't mount the backdrop. Pinned
|
||||
to the value the app actually renders at. */
|
||||
--radius-scalar: 0.2;
|
||||
|
||||
/* Space under last message vs overlay composer — driven by the measured composer height (see composer/index.tsx)
|
||||
plus the out-of-flow status stack's measured height (see status-stack/index.tsx) when one is showing.
|
||||
|
|
|
|||
1
package-lock.json
generated
1
package-lock.json
generated
|
|
@ -125,7 +125,6 @@
|
|||
"hast-util-to-text": "^4.0.2",
|
||||
"ignore": "^7.0.5",
|
||||
"katex": "^0.16.45",
|
||||
"leva": "^0.10.1",
|
||||
"mermaid": "^11.15.0",
|
||||
"motion": "^12.38.0",
|
||||
"nanostores": "^1.3.0",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue