mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
cef071582e
* feat(gateway): add live device scope upgrades * feat(ui): add limited-access upgrade flow * fix(protocol): refresh Swift scope upgrade models * perf(ui): lazy-load device scope upgrades * fix(ci): complete scope upgrade generated surfaces * perf(ui): lazy-load GitHub link hovercards * fix(ui): keep admin repair guidance focusable * fix(ui): gate and refresh scope upgrade banner * refactor(ui): keep gateway client within line budget * fix(ci): align rebased scope upgrade checks * fix(ui): resolve scope upgrade in browser tests * fix(gateway): honor refreshed scope upgrade deadline * fix(gateway): honor refreshed scope upgrade deadline * fix(gateway): coalesce scope upgrade waiters * fix(ui): gate scope upgrade actions * chore(plugin-sdk): refresh rebased API baseline * fix(scope-upgrade): return canonical request ids * fix(ui): preserve gateway event type binding * fix(protocol): generate scope upgrade result models * fix(ui): preserve scope upgrade recovery guidance * chore(plugin-sdk): refresh rebased API baseline * test(ui): avoid scope upgrade navigation race * docs(control-ui): clarify scope upgrade approver * test(gateway): align appended method counts * chore(plugin-sdk): refresh rebased API baseline * refactor(ui): keep place picker within line budget * chore(plugin-sdk): refresh rebased API baseline * chore(plugin-sdk): refresh rebased API baseline * chore(plugin-sdk): refresh rebased API baseline * chore(plugin-sdk): refresh rebased API baseline * chore(plugin-sdk): refresh rebased API baseline * chore(plugin-sdk): refresh rebased API baseline * chore(plugin-sdk): refresh rebased API baseline * chore(plugin-sdk): refresh rebased API baseline * chore(plugin-sdk): refresh rebased API baseline * chore(plugin-sdk): refresh rebased API baseline * chore(plugin-sdk): refresh rebased API baseline * fix(gateway): preserve scope-upgrade browser origin
76 lines
2.6 KiB
TypeScript
76 lines
2.6 KiB
TypeScript
import type { GatewayBrowserClient } from "../api/gateway.ts";
|
|
import { ensureCustomElementDefined } from "../app/lazy-custom-element.ts";
|
|
import type { GitHubLinkHovercardProvider } from "./github-link-hovercard.runtime.ts";
|
|
import {
|
|
GITHUB_HOVERCARD_OPEN_DELAY_MS,
|
|
githubLinkAnchorFromEvent,
|
|
parseGitHubLinkTarget,
|
|
} from "./github-link-target.ts";
|
|
|
|
const HOVERCARD_TAG = "openclaw-github-link-hovercard-provider";
|
|
|
|
type HovercardProviderElement = HTMLElement & {
|
|
client: GatewayBrowserClient | null;
|
|
};
|
|
|
|
function providerForAnchor(anchor: HTMLAnchorElement): GitHubLinkHovercardProvider | null {
|
|
return anchor.closest<GitHubLinkHovercardProvider>(HOVERCARD_TAG);
|
|
}
|
|
|
|
function removeBootstrapListeners(): void {
|
|
document.removeEventListener("pointerover", handleBootstrapPointerOver, true);
|
|
document.removeEventListener("focusin", handleBootstrapFocusIn, true);
|
|
}
|
|
|
|
async function activateHovercard(event: Event, trigger: "focus" | "pointer"): Promise<void> {
|
|
if (trigger === "pointer" && (event as PointerEvent).pointerType === "touch") {
|
|
return;
|
|
}
|
|
const anchor = githubLinkAnchorFromEvent(event);
|
|
const target = anchor ? parseGitHubLinkTarget(anchor.href) : null;
|
|
if (!anchor || !target || !providerForAnchor(anchor)) {
|
|
return;
|
|
}
|
|
const startedAt = performance.now();
|
|
const pendingClients = new Map(
|
|
[...document.querySelectorAll<HovercardProviderElement>(HOVERCARD_TAG)].map((provider) => [
|
|
provider,
|
|
provider.client,
|
|
]),
|
|
);
|
|
await ensureCustomElementDefined(HOVERCARD_TAG, async () => {
|
|
const runtime = await import("./github-link-hovercard.runtime.ts");
|
|
customElements.define(HOVERCARD_TAG, runtime.GitHubLinkHovercardProvider);
|
|
for (const [provider, client] of pendingClients) {
|
|
provider.client = client;
|
|
}
|
|
});
|
|
removeBootstrapListeners();
|
|
const provider = providerForAnchor(anchor);
|
|
const stillActive =
|
|
trigger === "pointer" ? anchor.matches(":hover") : document.activeElement === anchor;
|
|
if (!provider || !anchor.isConnected || !stillActive) {
|
|
return;
|
|
}
|
|
const delay =
|
|
trigger === "pointer"
|
|
? Math.max(0, GITHUB_HOVERCARD_OPEN_DELAY_MS - (performance.now() - startedAt))
|
|
: 0;
|
|
provider.activateFromBootstrap(anchor, target, trigger, delay);
|
|
}
|
|
|
|
function handleBootstrapPointerOver(event: Event): void {
|
|
void activateHovercard(event, "pointer");
|
|
}
|
|
|
|
function handleBootstrapFocusIn(event: Event): void {
|
|
void activateHovercard(event, "focus");
|
|
}
|
|
|
|
if (customElements.get(HOVERCARD_TAG)) {
|
|
removeBootstrapListeners();
|
|
} else {
|
|
document.addEventListener("pointerover", handleBootstrapPointerOver, true);
|
|
document.addEventListener("focusin", handleBootstrapFocusIn, true);
|
|
}
|