Skip to content

Commit

Permalink
feat: lift EditComposer to runtime layer (#900)
Browse files Browse the repository at this point in the history
  • Loading branch information
Yonom authored Sep 26, 2024
1 parent c423b88 commit 70720ba
Show file tree
Hide file tree
Showing 26 changed files with 481 additions and 335 deletions.
6 changes: 6 additions & 0 deletions .changeset/ninety-lions-move.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@assistant-ui/react-playground": patch
"@assistant-ui/react": patch
---

feat: lift EditComposer to runtime layer
12 changes: 10 additions & 2 deletions packages/react-playground/src/lib/playground-runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const {
BaseAssistantRuntimeCore,
ProxyConfigProvider,
generateId,
BaseThreadComposerRuntimeCore,
DefaultThreadComposerRuntimeCore,
AssistantRuntime,
ThreadRuntime,
} = INTERNAL;
Expand Down Expand Up @@ -113,7 +113,15 @@ export class PlaygroundThreadRuntimeCore

private configProvider = new ProxyConfigProvider();

public readonly composer = new BaseThreadComposerRuntimeCore(this);
public readonly composer = new DefaultThreadComposerRuntimeCore(this);

public getEditComposer() {
return undefined;
}

public beginEdit() {
throw new Error("Playground does not support edit mode.");
}

constructor(
configProvider: ModelConfigProvider,
Expand Down
206 changes: 206 additions & 0 deletions packages/react/src/api/ComposerRuntime.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
import { ThreadComposerAttachment } from "../context/stores/Attachment";
import { ComposerRuntimeCore } from "../runtimes/core/ComposerRuntimeCore";
import { SubscribableWithState } from "./subscribable/Subscribable";

export type ThreadComposerRuntimeCoreBinding = SubscribableWithState<
ComposerRuntimeCore | undefined
>;

export type EditComposerState = Readonly<{
type: "edit" | "thread";

/** @deprecated Use `text` instead. This will be removed in 0.6.0. */
value: string;
/** @deprecated Use `useComposerRuntime().setText()` instead. This will be removed in 0.6.0. */
setValue: (value: string) => void;

text: string;
/**
* @deprecated Use `useComposerRuntime().setText()` instead. This will be removed in 0.6.0.
*/
setText: (value: string) => void;

canCancel: boolean;
isEditing: boolean;
isEmpty: boolean;

/**
* @deprecated Use `useComposerRuntime().beginEdit()` instead. This will be removed in 0.6.0.
*/
edit: () => void;
/**
* @deprecated Use `useComposerRuntime().send()` instead. This will be removed in 0.6.0.
*/
send: () => void;
/**
* @deprecated Use `useComposerRuntime().cancel()` instead. This will be removed in 0.6.0.
*/
cancel: () => void;
}>;

export type UnstableThreadComposerStateV2 = EditComposerState &
Readonly<{
attachments: readonly ThreadComposerAttachment[];
attachmentAccept: string;
}>;

const METHOD_NOT_SUPPORTED = () => {
throw new Error("Composer is not available");
};
const EMPTY_ARRAY = Object.freeze([]);
const getThreadComposerState = (
type: "edit" | "thread",
runtime: ComposerRuntimeCore | undefined,
beginEdit: () => void,
): UnstableThreadComposerStateV2 => {
return Object.freeze({
type,

isEditing: runtime?.isEditing ?? false,
canCancel: runtime?.canCancel ?? false,
isEmpty: runtime?.isEmpty ?? true,
text: runtime?.text ?? "",
attachments: runtime?.attachments ?? EMPTY_ARRAY,
attachmentAccept: runtime?.attachmentAccept ?? "*",

value: runtime?.text ?? "",
setValue: runtime?.setText.bind(runtime) ?? METHOD_NOT_SUPPORTED,
setText: runtime?.setText.bind(runtime) ?? METHOD_NOT_SUPPORTED,
edit: beginEdit,
send: runtime?.send.bind(runtime) ?? METHOD_NOT_SUPPORTED,
cancel: runtime?.cancel.bind(runtime) ?? METHOD_NOT_SUPPORTED,
});
};

export class ComposerRuntime implements ComposerRuntimeCore {
public get type() {
return this._beginEdit ? "edit" : "thread";
}

constructor(
private _core: ThreadComposerRuntimeCoreBinding,
private _beginEdit?: () => void,
) {}

/**
* @deprecated Use `getState().isEditing` instead. This will be removed in 0.6.0.
*/
public get isEditing() {
return this.getState().isEditing;
}

/**
* @deprecated Use `getState().isEmpty` instead. This will be removed in 0.6.0.
*/
public get isEmpty() {
return this.getState().isEmpty;
}

/**
* @deprecated Use `getState().canCancel` instead. This will be removed in 0.6.0.
*/
public get canCancel() {
return this.getState().canCancel;
}

/**
* @deprecated Use `getState().text` instead. This will be removed in 0.6.0.
*/
public get text() {
return this.getState().text;
}

/**
* @deprecated Use `getState().attachmentAccept` instead. This will be removed in 0.6.0.
*/
public get attachmentAccept() {
return this.getState().attachmentAccept;
}

// TODO should this instead return getAttachmentByIndex([idx]) instead?
/**
* @deprecated Use `getState().attachments` instead. This will be removed in 0.6.0.
*/
public get attachments() {
return this.getState().attachments;
}

/**
* @deprecated Use `getState().text` instead. This will be removed in 0.6.0.
*/
public get value() {
return this.text;
}

public getState() {
return getThreadComposerState(
this.type,
this._core.getState(),
this._beginEdit?.bind(this) ?? METHOD_NOT_SUPPORTED,
);
}

public setText(text: string) {
const core = this._core.getState();
if (!core) throw new Error("Composer is not available");
core.setText(text);
}

public setValue(text: string) {
this.setText(text);
}

public addAttachment(file: File) {
const core = this._core.getState();
if (!core) throw new Error("Composer is not available");
return core.addAttachment(file);
}

// /**
// * @deprecated Use `getAttachmentById(id).removeAttachment` instead. This will be removed in 0.6.0.
// */
public removeAttachment(attachmentId: string) {
const core = this._core.getState();
if (!core) throw new Error("Composer is not available");
return core.removeAttachment(attachmentId);
}

/**
* @deprecated This method will be removed in 0.6.0. Submit feedback if you need this functionality.
*/
public reset() {
const core = this._core.getState();
if (!core) throw new Error("Composer is not available");
core.reset();
}

public send() {
const core = this._core.getState();
if (!core) throw new Error("Composer is not available");
core.send();
}

public cancel() {
const core = this._core.getState();
if (!core) throw new Error("Composer is not available");
core.cancel();
}

public beginEdit() {
const core = this._core.getState();
if (!core) throw new Error("Composer is not available");

this._beginEdit?.();
}

/**
* @deprecated Use `beginEdit()` instead. This will be removed in 0.6.0.
*/
public edit() {
this.beginEdit();
}

public subscribe(callback: () => void) {
return this._core.subscribe(callback);
}
}
15 changes: 14 additions & 1 deletion packages/react/src/api/MessageRuntime.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { toContentPartStatus } from "../context/providers/ContentPartProvider";
import { ThreadMessage, AppendMessage } from "../types";
import { ComposerRuntime } from "./ComposerRuntime";
import { ContentPartRuntime } from "./ContentPartRuntime";
import { ThreadRuntimeCoreBinding } from "./ThreadRuntime";
import { NestedSubscriptionSubject } from "./subscribable/NestedSubscriptionSubject";
import { ShallowMemoizeSubject } from "./subscribable/ShallowMemoizeSubject";
import { SubscribableWithState } from "./subscribable/Subscribable";

Expand Down Expand Up @@ -29,12 +31,23 @@ export class MessageRuntime {
private _threadBinding: ThreadRuntimeCoreBinding,
) {}

public composer = new ComposerRuntime(
new NestedSubscriptionSubject({
getState: () =>
this._threadBinding
.getState()
.getEditComposer(this._core.getState().id),
subscribe: (callback) => this._threadBinding.subscribe(callback),
}),
() => this._threadBinding.getState().beginEdit(this._core.getState().id),
) as ComposerRuntime & { type: "edit" };

public getState() {
return this._core.getState();
}

// TODO improve type
public edit(message: Omit<AppendMessage, "parentId">) {
public unstable_edit(message: Omit<AppendMessage, "parentId">) {
const state = this._core.getState();
if (!state) throw new Error("Message is not available");

Expand Down
104 changes: 0 additions & 104 deletions packages/react/src/api/ThreadComposerRuntime.ts

This file was deleted.

Loading

0 comments on commit 70720ba

Please sign in to comment.