diff --git a/clients/tabby-chat-panel/package.json b/clients/tabby-chat-panel/package.json index ee4608375f5d..b107bf01035d 100644 --- a/clients/tabby-chat-panel/package.json +++ b/clients/tabby-chat-panel/package.json @@ -1,7 +1,7 @@ { "name": "tabby-chat-panel", "type": "module", - "version": "0.5.0", + "version": "0.6.0", "keywords": [], "sideEffects": false, "exports": { diff --git a/clients/tabby-chat-panel/src/index.ts b/clients/tabby-chat-panel/src/index.ts index b2d4bdbe3345..5acf1600a3fc 100644 --- a/clients/tabby-chat-panel/src/index.ts +++ b/clients/tabby-chat-panel/src/index.ts @@ -362,8 +362,8 @@ export interface ClientApi extends ClientApiMethods { hasCapability: (method: keyof ClientApiMethods) => Promise } -export function createClient(target: HTMLIFrameElement, api: ClientApiMethods): ServerApi { - return createThreadFromIframe(target, { +export async function createClient(target: HTMLIFrameElement, api: ClientApiMethods): Promise { + const thread = createThreadFromIframe(target, { expose: { refresh: api.refresh, onApplyInEditor: api.onApplyInEditor, @@ -382,10 +382,18 @@ export function createClient(target: HTMLIFrameElement, api: ClientApiMethods): readFileContent: api.readFileContent, }, }) + + const serverMethods = await thread._requestMethods() as (keyof ServerApi)[] + const serverApi = {} as ServerApi + for (const method of serverMethods) { + serverApi[method] = thread[method] + } + + return serverApi } -export function createServer(api: ServerApi): ClientApi { - return createThreadFromInsideIframe({ +export async function createServer(api: ServerApi): Promise { + const thread = createThreadFromInsideIframe({ expose: { init: api.init, executeCommand: api.executeCommand, @@ -396,4 +404,13 @@ export function createServer(api: ServerApi): ClientApi { updateActiveSelection: api.updateActiveSelection, }, }) + const clientMethods = await thread._requestMethods() as (keyof ClientApi)[] + const clientApi = {} as ClientApi + for (const method of clientMethods) { + clientApi[method] = thread[method] + } + // hasCapability is not exposed from client + clientApi.hasCapability = thread.hasCapability + + return clientApi } diff --git a/clients/tabby-chat-panel/src/react.ts b/clients/tabby-chat-panel/src/react.ts index e6a57edeb295..9b938e1116e7 100644 --- a/clients/tabby-chat-panel/src/react.ts +++ b/clients/tabby-chat-panel/src/react.ts @@ -11,7 +11,7 @@ function useClient(iframeRef: RefObject, api: ClientApiMethod useEffect(() => { if (iframeRef.current && !isCreated) { isCreated = true - setClient(createClient(iframeRef.current!, api)) + createClient(iframeRef.current!, api).then(setClient) } }, [iframeRef.current]) @@ -26,7 +26,7 @@ function useServer(api: ServerApi) { const isInIframe = window.self !== window.top if (isInIframe && !isCreated) { isCreated = true - setServer(createServer(api)) + createServer(api).then(setServer) } }, []) diff --git a/clients/tabby-threads/source/targets/target.ts b/clients/tabby-threads/source/targets/target.ts index b4b4504edad8..639367428474 100644 --- a/clients/tabby-threads/source/targets/target.ts +++ b/clients/tabby-threads/source/targets/target.ts @@ -69,6 +69,7 @@ const RELEASE = 3; const FUNCTION_APPLY = 5; const FUNCTION_RESULT = 6; const CHECK_CAPABILITY = 7; +const EXPOSE_LIST = 8; interface MessageMap { [CALL]: [string, string | number, any]; @@ -78,6 +79,7 @@ interface MessageMap { [FUNCTION_APPLY]: [string, string, any]; [FUNCTION_RESULT]: [string, Error?, any?]; [CHECK_CAPABILITY]: [string, string]; + [EXPOSE_LIST]: [string]; } type MessageData = { @@ -121,7 +123,9 @@ export function createThread< ) => void >(); - const call = createCallable>(handlerForCall, callable); + const call = createCallable>(handlerForCall, callable, { + _requestMethods, + }); const encoderApi: ThreadEncoderApi = { functions: { @@ -333,6 +337,18 @@ export function createThread< send(RESULT, [id, undefined, encoder.encode(hasMethod, encoderApi)[0]]); break; } + + case EXPOSE_LIST: { + // return our list of exposed methods + const [id] = data[1]; + const exposedMethods = Array.from(activeApi.keys()); + send(RESULT, [ + id, + undefined, + encoder.encode(exposedMethods, encoderApi)[0], + ]); + break; + } } } @@ -410,6 +426,13 @@ export function createThread< callIdsToResolver.delete(callId); } } + + async function _requestMethods() { + const id = uuid(); + const done = waitForResult(id); + send(EXPOSE_LIST, [id]); + return done; + } } class ThreadTerminatedError extends Error { @@ -430,7 +453,10 @@ function createCallable( handlerForCall: ( property: string | number | symbol ) => AnyFunction | undefined, - callable?: (keyof T)[] + callable?: (keyof T)[], + methods?: { + _requestMethods(): Promise; + } ): T { let call: any; @@ -447,6 +473,12 @@ function createCallable( {}, { get(_target, property) { + if (property === "then") { + return undefined; + } + if (property === "_requestMethods") { + return methods?._requestMethods; + } if (cache.has(property)) { return cache.get(property); } diff --git a/clients/tabby-threads/source/types.ts b/clients/tabby-threads/source/types.ts index 61e4b1803aa8..1cc5005fc8cc 100644 --- a/clients/tabby-threads/source/types.ts +++ b/clients/tabby-threads/source/types.ts @@ -3,7 +3,7 @@ import type { RETAIN_METHOD, ENCODE_METHOD, RETAINED_BY, -} from './constants.ts'; +} from "./constants.ts"; /** * A thread represents a target JavaScript environment that exposes a set @@ -18,6 +18,12 @@ export type Thread = { ? Target[K] : never : never; +} & { + + /** + * A method that used to get all exposed methods of the opposite thread. + */ + _requestMethods(): Promise; }; /**