manu-sapiens's picture
copy of omnitool_latest - should be working
b39afbe
raw
history blame
13.3 kB
/**
* Copyright (c) 2023 MERCENARIES.AI PTE. LTD.
* All rights reserved.
*/
import {
type ChatClientService,
Client,
ClientExtensionManager,
type JobControllerClientService,
type MessagingClientService
} from 'omni-client-services';
import { Workbench } from './workbench';
import { OmniSDKHost, MarkdownEngine } from 'omni-sdk';
import {
OmniSSEMessages,
Manager,
type IOmniSSEMessageCustomExtensionEvent,
type IOmniSSEMessageClientToast,
type IOmniSSEMessageShowExtensionEvent,
insane
} from 'omni-shared';
import CodeRenderer from './renderers/chat/CodeRenderer';
import MarkdownRenderer from './renderers/chat/MarkdownRenderer';
import OmniComponentRenderer from './renderers/chat/OmniComponentRenderer';
import OmniComponentMetaRenderer from './renderers/chat/OmniComponentMetaRenderer';
import OmniJobRenderer from './renderers/chat/OmniJobRenderer';
import OmniRestErrorRenderer from './renderers/chat/OmniRestErrorRenderer';
import PlainTextRenderer from './renderers/chat/PlainTextRenderer';
import OmniBillingTabRenderer from './renderers/chat/OmniBillingTabRenderer';
import OmniSettingsRenderer from './renderers/chat/OmniSettingsRenderer';
import OmniExtensionListRenderer from './renderers/chat/OmniExtensionListRenderer';
import { OAIComponent31, type OAIBaseComponent, type OmniComponentFormat } from 'omni-sockets';
//@ts-expect-error
import WinBox from '@winbox/src/js/winbox.js';
import '@winbox/src/css/winbox.css';
enum ReteCurveType {
CurveBasis = 'curveBasis',
CurveBasisClosed = 'curveBasisClosed',
CurveBasisOpen = 'curveBasisOpen',
CurveBump = 'curveBump',
CurveBundle = 'curveBundle',
CurveCardinal = 'curveCardinal',
CurveCardinalClosed = 'curveCardinalClosed',
CurveCardinalOpen = 'curveCardinalOpen',
CurveCatmullRom = 'curveCatmullRom',
CurveCatmullRomOpen = 'curveCatmullRomOpen',
CurveLinear = 'curveLinear',
CurveLinearClosed = 'curveLinearClosed',
CurveMonotone = 'curveMonotone',
CurveNatural = 'curveNatural',
CurveRadial = 'curveRadial',
CurveStep = 'curveStep'
}
enum ReteSettings {
curve = 'curve'
}
class ClientBlockManager extends Manager {
private readonly factories: Map<string, Function>;
private readonly componentCache: Map<string, OAIBaseComponent>;
constructor(app: OmnitoolClient) {
super(app);
this.factories = new Map();
this.componentCache = new Map();
this.registerType('OAIComponent31', OAIComponent31.fromJSON);
}
public registerType(key: string, Factory: Function): void {
if (!key) throw new Error('registerType(): key cannot be undefined');
if (!Factory || typeof Factory !== 'function') throw new Error(`Factory ${key} must be a function`);
if (this.factories.has(key)) throw new Error(`Block type ${key} already registered`);
this.factories.set(key, Factory);
}
get client(): OmnitoolClient {
return this.app as OmnitoolClient;
}
private async _rpc(fName: string, args: any): Promise<any> {
return await this.client.runScript('blockManager', { command: fName, args }, { fromChatWindow: false });
}
public async getInstance(key: string): Promise<OAIBaseComponent | undefined> {
const ret = await this.getInstances([key]);
return ret ? ret[0] : undefined;
}
// return a composed block. If the key responds to a patch, the patch is applied to the underlying block
public async getInstances(keys: string[]): Promise<OAIBaseComponent[]> {
if (!keys) throw new Error('getInstances(): key cannot be undefined');
// Check the cache first
let found = (keys.map((k) => this.componentCache.get(k)) as OAIBaseComponent[]) || [];
const missing = keys.filter((k) => !this.componentCache.has(k));
// Retrieve any missing
if (missing.length > 0) {
const userId = undefined;
const blocks = (await this._rpc('getInstances', [missing, userId, 'missing_block'])) as OmniComponentFormat[];
const results = await Promise.all(
blocks.map(async (block: OmniComponentFormat) => {
const Factory = this.factories.get(block.type || 'OAIComponent31');
if (!Factory) {
throw new Error(`Internal error, Block type ${block.type} not registered`);
}
const instance = Factory(block); // return the composed block
this.componentCache.set(instance.name, instance);
return instance;
})
);
found.push(...results);
}
found = found.filter((r) => r !== undefined);
return found;
}
}
// Custom client
class OmnitoolClient extends Client {
clipboard: any = {};
workbench: Workbench;
uiSettings: any = {};
reteSettings: {
curve: ReteCurveType;
curvature: number;
arrow: boolean;
};
windows: Map<string, WinBox>;
sdkHost: OmniSDKHost<OmnitoolClient>;
public readonly markdownEngine: MarkdownEngine;
private readonly _blocks: ClientBlockManager;
constructor(id: string, options: any) {
super(id, options);
this.markdownEngine = new MarkdownEngine();
this.windows = new Map<string, WinBox>();
this.sdkHost = new OmniSDKHost<OmnitoolClient>(this).init();
// @ts-ignore
this.workbench = new window.Alpine.reactive(new Workbench(this));
const client = this;
this._blocks = new ClientBlockManager(this);
//@ts-expect-error
this.reteSettings = new window.Alpine.reactive({
curve: window.localStorage.getItem('settings.rete.curve') ?? ReteCurveType.CurveBasis,
arrow: window.localStorage.getItem('settings.rete.arrow') === 'true',
curvature: parseFloat(window.localStorage.getItem('settings.rete.curvature') ?? '0.3')
});
for (const key in ReteSettings) {
const value = window.localStorage.getItem(`settings.rete.${key}`);
if (value) {
//@ts-ignore
this.reteSettings[key] = value;
}
}
// @ts-ignore
this.uiSettings = new window.Alpine.reactive({
minimalChat: window.localStorage.getItem('settings.minimalChat') === 'true',
chatSide: window.localStorage.getItem('settings.chatSide') ?? 'left',
tabBarStyle: window.localStorage.getItem('settings.tabBarStyle') ?? 'tabs',
chatMinimized: window.localStorage.getItem('settings.chatMinimized') === 'true' ?? false,
async toggleMinimal() {
this.minimalChat = !this.minimalChat;
window.localStorage.setItem('settings.minimalChat', this.minimalChat);
await client.emit('request_editor_resize', {});
},
async toggleSide() {
this.chatSide = this.chatSide === 'left' ? 'right' : 'left';
window.localStorage.setItem('settings.chatSide', this.chatSide);
await client.emit('request_editor_resize', {});
},
async toggleMinimized(val?: boolean) {
this.chatMinimized = val !== undefined ? val : !this.chatMinimized;
window.localStorage.setItem('settings.chatMinimized', this.chatMinimized ? 'true' : 'false');
await client.emit('request_editor_resize', {});
}
});
// @ts-ignore
this.extensions = new window.Alpine.reactive(new ClientExtensionManager(this));
}
showToast(
message: string,
options: {
description?: string;
type?: 'default' | 'danger' | 'success' | 'warning' | 'info';
position?: string;
html?: string;
} = { description: '', type: 'default', position: 'top-right', html: '' }
) {
const description = options.description || '';
const type = options.type || 'default';
const position = options.position || 'top-right';
const html = options.html || '';
window.dispatchEvent(new CustomEvent('toast-show', { detail: { type, message, description, position, html } }));
}
showTopBanner(
bannerTitle: string,
bannerDescription: string,
options: {
link?: string;
}
) {
const link = options.link ?? '';
window.dispatchEvent(new CustomEvent('top-banner-show', { detail: { bannerTitle, bannerDescription, link } }));
}
closeWindow(singletonHash?: string): boolean {
const win = this.getWindow(singletonHash);
if (win) {
win.close();
}
return !!win;
}
public getWindow(singletonHash?: string): WinBox | undefined {
return singletonHash ? this.windows.get(singletonHash) : undefined;
}
public toggleWindow(url: string, singletonHash: string | undefined, opts: any): WinBox | undefined {
opts ??= {};
opts.minwidth ??= '600px';
opts.minheight ??= '500px';
opts.x ??= 'center';
opts.y ??= 'center';
if (singletonHash)
{
this.closeWindow(singletonHash)
url += `&omniHash=${singletonHash}`;
}
const template = document.createElement('div');
template.innerHTML = `
<div class="wb-header">
<div class="wb-control">
<span class="wb-custom"></span>
<span class="wb-close"></span>
</div>
<div class="wb-drag">
<div class="wb-title">
</div>
</div>
</div>
<div class="wb-body"></div>
`;
const window = new WinBox(
Object.assign(
{},
{
title: opts.title,
url,
class: [],
autosize: true
},
opts,
{
onclose: () => {
if (singletonHash) {
this.windows.delete(singletonHash);
this.sdkHost.deregister(singletonHash);
}
this.workbench.refreshUI();
}
}
)
);
if (singletonHash) {
this.windows.set(singletonHash, window);
}
this.workbench.refreshUI();
return window;
}
get blocks(): ClientBlockManager {
return this._blocks;
}
async onConfigure(): Promise<boolean> {
this.success('onConfigure');
return true;
}
async onLoad(): Promise<boolean> {
this.success('onLoad');
return true;
}
async onStart(): Promise<boolean> {
const renderInitTasks: Promise<void>[] = [];
renderInitTasks.push(this.chat.registerRenderer(new PlainTextRenderer()));
renderInitTasks.push(this.chat.registerRenderer(new MarkdownRenderer()));
renderInitTasks.push(this.chat.registerRenderer(new OmniJobRenderer()));
renderInitTasks.push(this.chat.registerRenderer(new OmniComponentRenderer()));
renderInitTasks.push(this.chat.registerRenderer(new OmniComponentMetaRenderer()));
renderInitTasks.push(this.chat.registerRenderer(new OmniRestErrorRenderer()));
renderInitTasks.push(this.chat.registerRenderer(new OmniBillingTabRenderer()));
renderInitTasks.push(this.chat.registerRenderer(new OmniSettingsRenderer()));
renderInitTasks.push(this.chat.registerRenderer(new OmniExtensionListRenderer()));
renderInitTasks.push(this.chat.registerRenderer(new CodeRenderer()));
await Promise.all(renderInitTasks);
this.io.registerMessageHandler({ type: 'chat', handler: this.chat.onChatMessage.bind(this.chat) });
this.io.registerMessageHandler({
type: OmniSSEMessages.CLIENT_TOAST,
handler: (message: IOmniSSEMessageClientToast): any => {
this.showToast(message.body.message, message.body.options);
}
});
this.io.registerMessageHandler({ type: 'chat:system', handler: this.chat.onChatMessage.bind(this.chat) });
// Extension messages
this.io.registerMessageHandler({
type: OmniSSEMessages.SHOW_EXTENSION,
handler: (message: IOmniSSEMessageShowExtensionEvent): any => {
this.workbench.showExtension(message.body.extensionId, message.body.args, message.body.page, message.body.opts);
}
})
this.io.registerMessageHandler({
type: OmniSSEMessages.CUSTOM_EXTENSION_EVENT,
handler: (message: IOmniSSEMessageCustomExtensionEvent) => {
const { extensionId, eventId, eventArgs } = message.body;
this.sdkHost.signalCustomEvent(extensionId, eventId, eventArgs);
}
});
// Subscribe to messages from the server
this.io.registerMessageHandler({ type: 'job:status', handler: this.jobs.handleMessages.bind(this.jobs) });
this.io.registerMessageHandler({ type: 'job:error', handler: this.jobs.handleMessages.bind(this.jobs) });
this.io.registerMessageHandler({ type: 'job:update', handler: this.jobs.handleMessages.bind(this.jobs) });
this.events.on('chat_message_added', async (eventData: any) => {
const [message] = eventData;
console.log(message);
this.sdkHost.signalChatMessageReceived(message);
});
await this.io.startSSE(''); // Start the SSE connection to receive real time messages from the server.
await this.workbench.load(); // Initialize the workbench
return true;
}
get io(): MessagingClientService {
return this.services.get('messaging') as unknown as MessagingClientService;
}
get jobs(): JobControllerClientService {
return this.services.get('jobs') as unknown as JobControllerClientService;
}
get chat(): ChatClientService {
return this.services.get('chat') as unknown as ChatClientService;
}
addImageToClipboard(images: any) {
if (!images) {
return;
}
if (!Array.isArray(images)) {
images = [images];
}
this.clipboard ??= {};
this.clipboard.images ??= [];
this.clipboard.images = this.clipboard.images.concat(images);
}
}
export { OmnitoolClient };