feat: v1 — persistent shell, model gateway, artifact improvements (#1462)
This commit is contained in:
parent
3651670fb9
commit
f9652b452a
161 changed files with 5166 additions and 8009 deletions
|
|
@ -8,7 +8,7 @@ import { renderToString } from "react-dom/server";
|
|||
import { MessageResponse } from "@/components/ai-elements/message";
|
||||
|
||||
import { documentSchema } from "./config";
|
||||
import { createSuggestionWidget, type UISuggestion } from "./suggestions";
|
||||
import type { UISuggestion } from "./suggestions";
|
||||
|
||||
export const buildDocumentFromContent = (content: string) => {
|
||||
const parser = DOMParser.fromSchema(documentSchema);
|
||||
|
|
@ -26,7 +26,7 @@ export const buildContentFromDocument = (document: Node) => {
|
|||
|
||||
export const createDecorations = (
|
||||
suggestions: UISuggestion[],
|
||||
view: EditorView
|
||||
_view: EditorView
|
||||
) => {
|
||||
const decorations: Decoration[] = [];
|
||||
|
||||
|
|
@ -37,6 +37,7 @@ export const createDecorations = (
|
|||
suggestion.selectionEnd,
|
||||
{
|
||||
class: "suggestion-highlight",
|
||||
"data-suggestion-id": suggestion.id,
|
||||
},
|
||||
{
|
||||
suggestionId: suggestion.id,
|
||||
|
|
@ -44,21 +45,7 @@ export const createDecorations = (
|
|||
}
|
||||
)
|
||||
);
|
||||
|
||||
decorations.push(
|
||||
Decoration.widget(
|
||||
suggestion.selectionStart,
|
||||
(currentView) => {
|
||||
const { dom } = createSuggestionWidget(suggestion, currentView);
|
||||
return dom;
|
||||
},
|
||||
{
|
||||
suggestionId: suggestion.id,
|
||||
type: "widget",
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return DecorationSet.create(view.state.doc, decorations);
|
||||
return DecorationSet.create(_view.state.doc, decorations);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import { createRoot } from "react-dom/client";
|
||||
|
||||
// biome-ignore lint/complexity/noStaticOnlyClass: "Needs to be static"
|
||||
export class ReactRenderer {
|
||||
static render(component: React.ReactElement, dom: HTMLElement) {
|
||||
const root = createRoot(dom);
|
||||
root.render(component);
|
||||
export function renderReactComponent(
|
||||
component: React.ReactElement,
|
||||
dom: HTMLElement
|
||||
) {
|
||||
const root = createRoot(dom);
|
||||
root.render(component);
|
||||
|
||||
return {
|
||||
destroy: () => root.unmount(),
|
||||
};
|
||||
}
|
||||
return {
|
||||
destroy: () => root.unmount(),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,6 @@
|
|||
import type { Node } from "prosemirror-model";
|
||||
import { Plugin, PluginKey } from "prosemirror-state";
|
||||
import {
|
||||
type Decoration,
|
||||
DecorationSet,
|
||||
type EditorView,
|
||||
} from "prosemirror-view";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import type { ArtifactKind } from "@/components/artifact";
|
||||
import { Suggestion as PreviewSuggestion } from "@/components/suggestion";
|
||||
import { DecorationSet } from "prosemirror-view";
|
||||
import type { Suggestion } from "@/lib/db/schema";
|
||||
|
||||
export interface UISuggestion extends Suggestion {
|
||||
|
|
@ -66,71 +59,6 @@ export function projectWithPositions(
|
|||
});
|
||||
}
|
||||
|
||||
export function createSuggestionWidget(
|
||||
suggestion: UISuggestion,
|
||||
view: EditorView,
|
||||
artifactKind: ArtifactKind = "text"
|
||||
): { dom: HTMLElement; destroy: () => void } {
|
||||
const dom = document.createElement("span");
|
||||
const root = createRoot(dom);
|
||||
|
||||
dom.addEventListener("mousedown", (event) => {
|
||||
event.preventDefault();
|
||||
view.dom.blur();
|
||||
});
|
||||
|
||||
const onApply = () => {
|
||||
const { state, dispatch } = view;
|
||||
|
||||
const decorationTransaction = state.tr;
|
||||
const currentState = suggestionsPluginKey.getState(state);
|
||||
const currentDecorations = currentState?.decorations;
|
||||
|
||||
if (currentDecorations) {
|
||||
const newDecorations = DecorationSet.create(
|
||||
state.doc,
|
||||
currentDecorations.find().filter((decoration: Decoration) => {
|
||||
return decoration.spec.suggestionId !== suggestion.id;
|
||||
})
|
||||
);
|
||||
|
||||
decorationTransaction.setMeta(suggestionsPluginKey, {
|
||||
decorations: newDecorations,
|
||||
selected: null,
|
||||
});
|
||||
dispatch(decorationTransaction);
|
||||
}
|
||||
|
||||
const textTransaction = view.state.tr.replaceWith(
|
||||
suggestion.selectionStart,
|
||||
suggestion.selectionEnd,
|
||||
state.schema.text(suggestion.suggestedText)
|
||||
);
|
||||
|
||||
textTransaction.setMeta("no-debounce", true);
|
||||
|
||||
dispatch(textTransaction);
|
||||
};
|
||||
|
||||
root.render(
|
||||
<PreviewSuggestion
|
||||
artifactKind={artifactKind}
|
||||
onApply={onApply}
|
||||
suggestion={suggestion}
|
||||
/>
|
||||
);
|
||||
|
||||
return {
|
||||
dom,
|
||||
destroy: () => {
|
||||
// Wrapping unmount in setTimeout to avoid synchronous unmounting during render
|
||||
setTimeout(() => {
|
||||
root.unmount();
|
||||
}, 0);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export const suggestionsPluginKey = new PluginKey("suggestions");
|
||||
export const suggestionsPlugin = new Plugin({
|
||||
key: suggestionsPluginKey,
|
||||
|
|
@ -154,5 +82,15 @@ export const suggestionsPlugin = new Plugin({
|
|||
decorations(state) {
|
||||
return this.getState(state)?.decorations ?? DecorationSet.empty;
|
||||
},
|
||||
handleDOMEvents: {
|
||||
mousedown(_view, event) {
|
||||
const target = event.target as HTMLElement;
|
||||
if (target.closest(".suggestion-highlight")) {
|
||||
event.preventDefault();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue