70 lines
2.8 KiB
TypeScript
70 lines
2.8 KiB
TypeScript
import type { AssistantBridge, BrowserStateEvent, RevealEvent, ViewMode, ViewportMessage } from '../electron/shared';
|
||
|
||
declare global {
|
||
interface Window {
|
||
assistant?: AssistantBridge;
|
||
}
|
||
}
|
||
|
||
// 没有 Electron(在普通浏览器里调试界面)时退回到直接请求后端
|
||
const FALLBACK_PORT = 8788;
|
||
const FALLBACK_BASE = `http://127.0.0.1:${FALLBACK_PORT}/api`;
|
||
|
||
async function fetchJson<T>(method: string, path: string, body?: unknown): Promise<T> {
|
||
const response = await fetch(FALLBACK_BASE + path, {
|
||
method,
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: body === undefined ? undefined : JSON.stringify(body),
|
||
});
|
||
const data = (await response.json().catch(() => ({}))) as Record<string, unknown>;
|
||
if (!response.ok) {
|
||
const detail = typeof data.detail === 'string' ? data.detail : response.statusText;
|
||
throw new Error(detail);
|
||
}
|
||
return data as T;
|
||
}
|
||
|
||
const noop = (): (() => void) => () => undefined;
|
||
|
||
const fallback: AssistantBridge = {
|
||
get: path => fetchJson('GET', path),
|
||
post: (path, body) => fetchJson('POST', path, body ?? {}),
|
||
put: (path, body) => fetchJson('PUT', path, body ?? {}),
|
||
del: path => fetchJson('DELETE', path),
|
||
openPath: async () => '当前为浏览器预览,请在桌面端打开本地文件。',
|
||
meta: async () => ({ apiPort: FALLBACK_PORT, cdpPort: 0, controlPort: 0, version: 'browser-dev' }),
|
||
browser: {
|
||
viewport: async () => undefined,
|
||
mode: async () => undefined,
|
||
onState: noop,
|
||
onReveal: noop,
|
||
onHide: noop,
|
||
onUserInput: noop,
|
||
},
|
||
};
|
||
|
||
const bridge: AssistantBridge = window.assistant ?? fallback;
|
||
|
||
export const isElectron = Boolean(window.assistant);
|
||
|
||
export const api = {
|
||
get: <T>(path: string): Promise<T> => bridge.get(path) as Promise<T>,
|
||
post: <T>(path: string, body?: unknown): Promise<T> => bridge.post(path, body) as Promise<T>,
|
||
put: <T>(path: string, body?: unknown): Promise<T> => bridge.put(path, body) as Promise<T>,
|
||
del: <T>(path: string): Promise<T> => bridge.del(path) as Promise<T>,
|
||
openPath: (filePath: string): Promise<string> => bridge.openPath(filePath),
|
||
appMeta: () => bridge.meta(),
|
||
browser: {
|
||
viewport: (message: ViewportMessage): Promise<void> => bridge.browser.viewport(message),
|
||
mode: (taskId: string, mode: ViewMode): Promise<void> => bridge.browser.mode(taskId, mode),
|
||
onState: (handler: (event: BrowserStateEvent) => void): (() => void) => bridge.browser.onState(handler),
|
||
onReveal: (handler: (event: RevealEvent) => void): (() => void) => bridge.browser.onReveal(handler),
|
||
onHide: (handler: (event: RevealEvent) => void): (() => void) => bridge.browser.onHide(handler),
|
||
onUserInput: (handler: (event: RevealEvent) => void): (() => void) => bridge.browser.onUserInput(handler),
|
||
},
|
||
};
|
||
|
||
export function errorText(error: unknown): string {
|
||
return error instanceof Error ? error.message : String(error);
|
||
}
|