feat(web): NetX workbench, UME-only UI, i18n, and header status

- Replace sidebar app with workbench home and module tabs (openOrFocusModule)

- Remove legacy pages; add module registry, shared tab channel, WEB.md

- Add zh/en i18n, global ToastProvider, restore oclaw bridge pill in header

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
oliver 2026-05-27 11:18:30 +08:00
parent 664775927b
commit 282dbe75af
33 changed files with 1631 additions and 1677 deletions

View file

@ -0,0 +1,46 @@
import { useEffect, useRef, useState } from "react";
type Props = {
text: string;
ariaLabel: string;
};
export function HelpHint({ text, ariaLabel }: Props) {
const [open, setOpen] = useState(false);
const rootRef = useRef<HTMLSpanElement>(null);
useEffect(() => {
if (!open) return;
const onDocClick = (ev: MouseEvent) => {
if (!rootRef.current?.contains(ev.target as Node)) setOpen(false);
};
const onKey = (ev: KeyboardEvent) => {
if (ev.key === "Escape") setOpen(false);
};
document.addEventListener("mousedown", onDocClick);
document.addEventListener("keydown", onKey);
return () => {
document.removeEventListener("mousedown", onDocClick);
document.removeEventListener("keydown", onKey);
};
}, [open]);
return (
<span className="help-hint" ref={rootRef}>
<button
type="button"
className="help-hint__trigger"
aria-label={ariaLabel}
aria-expanded={open}
onClick={() => setOpen((v) => !v)}
>
?
</button>
{open ? (
<div className="help-hint__popover" role="tooltip">
{text}
</div>
) : null}
</span>
);
}