diff --git a/ui/src/app/native-bridge.test.ts b/ui/src/app/native-bridge.test.ts index 6e33af51db66..46b69391c6cb 100644 --- a/ui/src/app/native-bridge.test.ts +++ b/ui/src/app/native-bridge.test.ts @@ -4,7 +4,7 @@ import { navigateChatInputHistory, type ChatInputHistoryState, } from "../pages/chat/input-history.ts"; -import { createNativeChatDrafts, isWebView2, sendToNative } from "./native-bridge.ts"; +import { createNativeChatDrafts } from "./native-bridge.ts"; type FakeBridge = { postMessage: ReturnType; @@ -46,14 +46,6 @@ afterEach(() => { }); describe("native chat drafts", () => { - it("detects WebView2 and sends native messages", () => { - expect(isWebView2()).toBe(false); - const bridge = makeBridge(); - expect(isWebView2()).toBe(true); - sendToNative({ type: "ready" }); - expect(bridge.posted).toEqual([{ type: "ready" }]); - }); - it("registers the listener before the ready handshake", () => { const callOrder: string[] = []; vi.stubGlobal("chrome", { diff --git a/ui/src/app/native-bridge.ts b/ui/src/app/native-bridge.ts index c23092b3ebfd..8df889409b76 100644 --- a/ui/src/app/native-bridge.ts +++ b/ui/src/app/native-bridge.ts @@ -5,10 +5,6 @@ type WebView2Bridge = { removeEventListener(type: "message", listener: (event: MessageEvent) => void): void; }; -type NativeBridgeMessage = - | { type: "draft-text"; payload: { text: string } } - | { type: "ready"; payload?: Record }; - export type NativeChatDrafts = { subscribe: (listener: (draft: string) => void) => () => void; dispose: () => void; @@ -19,12 +15,9 @@ function getWebview(): WebView2Bridge | undefined { return webview; } -export function isWebView2(): boolean { - return getWebview() !== undefined; -} - -export function sendToNative(msg: NativeBridgeMessage): void { - getWebview()?.postMessage(msg); +// Keep WebView2 messaging distinct from the DOM Window.postMessage contract. +function sendToNative(message: unknown): void { + getWebview()?.postMessage(message); } function readNativeDraft(raw: unknown): string | null { diff --git a/ui/src/app/settings.node.test.ts b/ui/src/app/settings.node.test.ts index 7294cd0bc1ff..b11e7b47c53a 100644 --- a/ui/src/app/settings.node.test.ts +++ b/ui/src/app/settings.node.test.ts @@ -6,7 +6,6 @@ import { loadGatewaySessionSelection, loadLocalUserIdentity, loadSettings, - saveLocalUserIdentity, saveSettings, type UiSettings, } from "./settings.ts"; @@ -837,14 +836,16 @@ describe("loadSettings default gateway URL derivation", () => { }); }); - it("persists local user identity separately from gateway settings", () => { + it("loads local user identity separately from gateway settings", () => { setTestLocation({ protocol: "https:", host: "gateway.example:8443", pathname: "/", }); - - saveLocalUserIdentity({ name: "Buns", avatar: "🦞" }); + localStorage.setItem( + "openclaw.control.user.v1", + JSON.stringify({ name: "Buns", avatar: "🦞" }), + ); expect(loadLocalUserIdentity()).toEqual({ name: "Buns", @@ -870,15 +871,4 @@ describe("loadSettings default gateway URL derivation", () => { avatar: null, }); }); - - it("removes the persisted local user identity when cleared", () => { - saveLocalUserIdentity({ name: "Buns", avatar: "data:image/png;base64,AAA" }); - saveLocalUserIdentity({ name: null, avatar: null }); - - expect(loadLocalUserIdentity()).toEqual({ - name: null, - avatar: null, - }); - expect(localStorage.getItem("openclaw.control.user.v1")).toBeNull(); - }); }); diff --git a/ui/src/app/settings.ts b/ui/src/app/settings.ts index c4d339b47477..b96bbce1b6ab 100644 --- a/ui/src/app/settings.ts +++ b/ui/src/app/settings.ts @@ -45,11 +45,7 @@ import { normalizeChatSplitLayout, type ChatSplitLayout } from "../pages/chat/sp import { parseImportedCustomTheme, type ImportedCustomTheme } from "./custom-theme.ts"; import { normalizeGatewayTokenScope } from "./gateway-scope.ts"; import { parseThemeSelection, type ThemeMode, type ThemeName } from "./theme.ts"; -import { - hasLocalUserIdentity, - normalizeLocalUserIdentity, - type LocalUserIdentity, -} from "./user-identity.ts"; +import { normalizeLocalUserIdentity, type LocalUserIdentity } from "./user-identity.ts"; export const BORDER_RADIUS_STOPS = [0, 25, 50, 75, 100] as const; export type BorderRadiusStop = (typeof BORDER_RADIUS_STOPS)[number]; @@ -129,8 +125,6 @@ export type UiSettings = { locale?: string; }; -export type { LocalUserIdentity } from "./user-identity.ts"; - type LastActiveSessionHost = { settings: UiSettings; applySettings(next: UiSettings): void; @@ -668,21 +662,6 @@ export function loadLocalUserIdentity(): LocalUserIdentity { } } -export function saveLocalUserIdentity(next: LocalUserIdentity) { - const storage = getSafeLocalStorage(); - const normalized = normalizeLocalUserIdentity(next); - try { - if (!hasLocalUserIdentity(normalized)) { - storage?.removeItem(LOCAL_USER_IDENTITY_KEY); - return; - } - storage?.setItem(LOCAL_USER_IDENTITY_KEY, JSON.stringify(normalized)); - } catch { - // best-effort — quota exceeded or security restrictions should not - // prevent in-memory identity updates from being applied - } -} - function persistSettings(next: UiSettings, options: { selectGateway?: boolean } = {}) { persistSessionToken(next.gatewayUrl, next.token); const storage = getSafeLocalStorage(); diff --git a/ui/src/lib/sessions/drag.ts b/ui/src/lib/sessions/drag.ts index da606d184401..688bed84ee4d 100644 --- a/ui/src/lib/sessions/drag.ts +++ b/ui/src/lib/sessions/drag.ts @@ -1,3 +1,4 @@ +// Private MIME keeps stray text and file drags from becoming session actions. export const SESSION_DRAG_MIME = "application/x-openclaw-session-key"; export function writeSessionDragData(dataTransfer: DataTransfer, sessionKey: string): void { diff --git a/ui/src/pages/sessions/view.ts b/ui/src/pages/sessions/view.ts index 87f235409dbc..8b0c141a9cd4 100644 --- a/ui/src/pages/sessions/view.ts +++ b/ui/src/pages/sessions/view.ts @@ -24,6 +24,7 @@ import { formatSessionTokens } from "../../lib/presenter.ts"; import { formatGoalDetail, formatGoalSummary } from "../../lib/session-goal.ts"; import { sessionModelMatchesDefaults } from "../../lib/session-model-defaults.ts"; import { isSessionRunActive } from "../../lib/session-run-state.ts"; +import { SESSION_DRAG_MIME } from "../../lib/sessions/drag.ts"; import { groupSessionRows, SESSION_GROUP_MODES, @@ -484,8 +485,6 @@ function sessionDetailItems(params: { } const NEW_GROUP_OPTION = "__new-group__"; -// Private MIME so stray text/file drags never become sessions.patch calls. -const SESSION_DRAG_MIME = "application/x-openclaw-session-key"; function sessionsTableColumnCount(props: SessionsProps): number { return props.groupBy === "category" ? 9 : 8;