Merge branch 'main' into feat/image-select

This commit is contained in:
Ed McConnell 2024-12-14 09:56:29 -05:00
commit 7a31db904d
5 changed files with 64 additions and 5 deletions

View File

@ -1 +1 @@
{ "commit": "a936c5a99036e0ea65c976e19b3bdb39f8d7cd40" } { "commit": "6ffcdd8b3c54e2560b327ca1b4f4eb33ba54c0db" }

View File

@ -436,12 +436,15 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
} }
event.preventDefault(); event.preventDefault();
if (isStreaming) { if (isStreaming) {
handleStop?.(); handleStop?.();
return; return;
} }
// ignore if using input method engine
if (event.nativeEvent.isComposing) {
return
}
handleSendMessage?.(event); handleSendMessage?.(event);
} }
}} }}

View File

@ -15,6 +15,7 @@ export default function ConnectionsTab() {
hasToken: !!githubToken, hasToken: !!githubToken,
}); });
toast.success('GitHub credentials saved successfully!'); toast.success('GitHub credentials saved successfully!');
Cookies.set('git:github.com', JSON.stringify({ username: githubToken, password: 'x-oauth-basic' }));
}; };
return ( return (

View File

@ -16,6 +16,7 @@ import * as nodePath from 'node:path';
import { extractRelativePath } from '~/utils/diff'; import { extractRelativePath } from '~/utils/diff';
import { description } from '~/lib/persistence'; import { description } from '~/lib/persistence';
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import { createSampler } from '~/utils/sampler';
export interface ArtifactState { export interface ArtifactState {
id: string; id: string;
@ -280,7 +281,7 @@ export class WorkbenchStore {
runAction(data: ActionCallbackData, isStreaming: boolean = false) { runAction(data: ActionCallbackData, isStreaming: boolean = false) {
if (isStreaming) { if (isStreaming) {
this._runAction(data, isStreaming); this.actionStreamSampler(data, isStreaming);
} else { } else {
this.addToExecutionQueue(() => this._runAction(data, isStreaming)); this.addToExecutionQueue(() => this._runAction(data, isStreaming));
} }
@ -296,7 +297,8 @@ export class WorkbenchStore {
const action = artifact.runner.actions.get()[data.actionId]; const action = artifact.runner.actions.get()[data.actionId];
if (action.executed) {
if (!action || action.executed) {
return; return;
} }
@ -329,6 +331,10 @@ export class WorkbenchStore {
} }
} }
actionStreamSampler = createSampler(async (data: ActionCallbackData, isStreaming: boolean = false) => {
return await this._runAction(data, isStreaming);
}, 100); // TODO: remove this magic number to have it configurable
#getArtifact(id: string) { #getArtifact(id: string) {
const artifacts = this.artifacts.get(); const artifacts = this.artifacts.get();
return artifacts[id]; return artifacts[id];

49
app/utils/sampler.ts Normal file
View File

@ -0,0 +1,49 @@
/**
* Creates a function that samples calls at regular intervals and captures trailing calls.
* - Drops calls that occur between sampling intervals
* - Takes one call per sampling interval if available
* - Captures the last call if no call was made during the interval
*
* @param fn The function to sample
* @param sampleInterval How often to sample calls (in ms)
* @returns The sampled function
*/
export function createSampler<T extends (...args: any[]) => any>(fn: T, sampleInterval: number): T {
let lastArgs: Parameters<T> | null = null;
let lastTime = 0;
let timeout: NodeJS.Timeout | null = null;
// Create a function with the same type as the input function
const sampled = function (this: any, ...args: Parameters<T>) {
const now = Date.now();
lastArgs = args;
// If we're within the sample interval, just store the args
if (now - lastTime < sampleInterval) {
// Set up trailing call if not already set
if (!timeout) {
timeout = setTimeout(
() => {
timeout = null;
lastTime = Date.now();
if (lastArgs) {
fn.apply(this, lastArgs);
lastArgs = null;
}
},
sampleInterval - (now - lastTime),
);
}
return;
}
// If we're outside the interval, execute immediately
lastTime = now;
fn.apply(this, args);
lastArgs = null;
} as T;
return sampled;
}