feat(scenes): rich scene descriptions and control viewer window

Add TipTap editing in the editor and an Electron window on the control panel to read the current scene description.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Ivan Fontosh
2026-07-15 09:49:40 +08:00
parent 35a6e979eb
commit cfa3959fb3
26 changed files with 1795 additions and 60 deletions
@@ -13,6 +13,12 @@
opacity: 0.45;
}
.disabledTipHost {
display: inline-flex;
vertical-align: middle;
cursor: not-allowed;
}
.buttonPrimary {
border: 1px solid var(--accent-border);
background: var(--accent-fill-solid);
@@ -12,4 +12,5 @@ void test('Button: тултип через портал (title), не тольк
assert.ok(src.includes('role="tooltip"'));
assert.ok(src.includes('onMouseEnter={showTip}'));
assert.ok(src.includes('document.body'));
assert.ok(src.includes('disabledTipHost'), 'disabled + title → хост для hover-тултипа');
});
+33 -16
View File
@@ -28,11 +28,12 @@ export function Button({
tooltipPlacement = 'top',
}: ButtonProps) {
const btnRef = useRef<HTMLButtonElement | null>(null);
const hostRef = useRef<HTMLSpanElement | null>(null);
const [tipPos, setTipPos] = useState<{ x: number; y: number } | null>(null);
const showTip = useCallback(() => {
if (disabled || !title) return;
const el = btnRef.current;
if (!title) return;
const el = disabled ? hostRef.current : btnRef.current;
if (!el) return;
const r = el.getBoundingClientRect();
if (tooltipPlacement === 'bottom-left') {
@@ -66,22 +67,38 @@ export function Button({
)
: null;
const button = (
<button
ref={btnRef}
type="button"
className={btnClass}
disabled={disabled}
aria-label={ariaLabel}
onClick={disabled ? undefined : onClick}
onMouseEnter={disabled ? undefined : showTip}
onMouseLeave={disabled ? undefined : hideTip}
onFocus={showTip}
onBlur={hideTip}
>
{children}
</button>
);
// Disabled buttons don't receive mouse events — host span keeps tooltip usable.
if (disabled && title) {
return (
<>
<span ref={hostRef} className={styles.disabledTipHost} onMouseEnter={showTip} onMouseLeave={hideTip}>
{button}
</span>
{tip}
</>
);
}
return (
<>
<button
ref={btnRef}
type="button"
className={btnClass}
disabled={disabled}
aria-label={ariaLabel}
onClick={disabled ? undefined : onClick}
onMouseEnter={showTip}
onMouseLeave={hideTip}
onFocus={showTip}
onBlur={hideTip}
>
{children}
</button>
{button}
{tip}
</>
);