diff --git a/app/components/chat/Artifact.tsx b/app/components/chat/Artifact.tsx index 5f0c99106..cc2ce21c8 100644 --- a/app/components/chat/Artifact.tsx +++ b/app/components/chat/Artifact.tsx @@ -8,17 +8,24 @@ import { workbenchStore } from '~/lib/stores/workbench'; import { classNames } from '~/utils/classNames'; import { cubicEasingFn } from '~/utils/easings'; import { WORK_DIR } from '~/utils/constants'; +import { createAsyncSuspenseValue } from '~/lib/asyncSuspenseValue'; const highlighterOptions = { langs: ['shell'], themes: ['light-plus', 'dark-plus'], }; -const shellHighlighter: HighlighterGeneric = - import.meta.hot?.data.shellHighlighter ?? (await createHighlighter(highlighterOptions)); +const shellHighlighter = createAsyncSuspenseValue(async () => { + const shellHighlighterPromise: Promise> = + import.meta.hot?.data.shellHighlighterPromise ?? createHighlighter(highlighterOptions); + if (import.meta.hot) { + import.meta.hot.data.shellHighlighterPromise = shellHighlighterPromise; + } + return shellHighlighterPromise; +}); -if (import.meta.hot) { - import.meta.hot.data.shellHighlighter = shellHighlighter; +if (typeof document !== 'undefined') { + shellHighlighter.preload(); } interface ArtifactProps { @@ -134,7 +141,7 @@ function ShellCodeBlock({ classsName, code }: ShellCodeBlockProps) {
((props: 'mt-4': !isFirst, })} > - {isUserMessage && ( -
-
-
- )} -
- {isUserMessage ? ( - - ) : ( - +
+ } + > + {isUserMessage && ( +
+
+
)} -
- {!isUserMessage && messageId && getLastMessageProjectContents(index) && EnableRewindButton && ( -
- -
+ )} + ); }) diff --git a/app/lib/asyncSuspenseValue.ts b/app/lib/asyncSuspenseValue.ts new file mode 100644 index 000000000..22185fd33 --- /dev/null +++ b/app/lib/asyncSuspenseValue.ts @@ -0,0 +1,58 @@ +type SuspenseRecord = + | { + status: 'resolved'; + value: T; + } + | { + status: 'rejected'; + error: Error; + } + | { + status: 'pending'; + promise: Promise; + }; + +export function createAsyncSuspenseValue(getValue: () => Promise) { + let record: SuspenseRecord | undefined; + + const load = () => { + const promise = getValue().then( + (value) => { + record = { status: 'resolved', value }; + return value; + }, + (error) => { + record = { status: 'rejected', error }; + throw error; + }, + ); + + record = { status: 'pending', promise }; + return promise; + }; + + const asyncValue = { + read() { + if (!record) { + throw load(); + } + + switch (record.status) { + case 'pending': + throw record.promise; + case 'resolved': + return record.value; + case 'rejected': + throw record.error; + } + }, + preload() { + if (record) { + return; + } + load().catch(() => {}); + }, + }; + + return asyncValue; +}