Skip to content

Commit

Permalink
refactor(apps): protocol (#171)
Browse files Browse the repository at this point in the history
  • Loading branch information
JanPokorny authored Jan 8, 2025
1 parent d0b3892 commit ed7727b
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 93 deletions.
4 changes: 2 additions & 2 deletions src/modules/apps/builder/AppBuilder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ function AppBuilderContent() {
totalCount,
]);

const handleReportError = useCallback(
const handleFixError = useCallback(
async (errorText: string) => {
await sendMessage(
`I have encountered the following error:\n\n\`\`\`error\n${errorText}\n\`\`\`\n\nFix this error please.`,
Expand Down Expand Up @@ -364,7 +364,7 @@ function AppBuilderContent() {
<TabPanel key={TabsKeys.Preview}>
<ArtifactSharedIframe
sourceCode={code}
onReportError={handleReportError}
onFixError={handleFixError}
/>
</TabPanel>
<TabPanel key={TabsKeys.SourceCode}>
Expand Down
147 changes: 56 additions & 91 deletions src/modules/apps/builder/ArtifactSharedIframe.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { createChatCompletion, modulesToPackages } from '@/app/api/apps';
import { ChatCompletionCreateBody } from '@/app/api/apps/types';
import { ApiError } from '@/app/api/errors';
import { useProjectContext } from '@/layout/providers/ProjectProvider';
import { Theme, useTheme } from '@/layout/providers/ThemeProvider';
import { useTheme } from '@/layout/providers/ThemeProvider';
import { USERCONTENT_SITE_URL } from '@/utils/constants';
import { removeTrailingSlash } from '@/utils/helpers';
import { Loading } from '@carbon/react';
Expand All @@ -29,7 +29,7 @@ import AppPlaceholder from './Placeholder.svg';

interface Props {
sourceCode: string | null;
onReportError?: (errorText: string) => void;
onFixError?: (errorText: string) => void;
}

function getErrorMessage(error: unknown) {
Expand All @@ -42,39 +42,35 @@ function getErrorMessage(error: unknown) {
return 'Unknown error when calling LLM function.';
}

export function ArtifactSharedIframe({ sourceCode, onReportError }: Props) {
export function ArtifactSharedIframe({ sourceCode, onFixError }: Props) {
const iframeRef = useRef<HTMLIFrameElement>(null);
const [state, setState] = useState<State>(State.LOADING);
const { appliedTheme: theme } = useTheme();
const { project, organization } = useProjectContext();
const [iframeLoadCount, setIframeLoadCount] = useState<number>(0);

const postMessage = (message: PostMessage) => {
const postMessage = useCallback((message: PostMessage) => {
if(iframeLoadCount === 0) return;
iframeRef.current?.contentWindow?.postMessage(
message,
USERCONTENT_SITE_URL,
);
};
}, [iframeLoadCount])

const updateTheme = useCallback((theme: Theme) => {
postMessage({ type: PostMessageType.UPDATE_THEME, theme });
}, []);

const updateCode = useCallback(
(code: string | null) => {
if (!code) {
return;
}

postMessage({
type: PostMessageType.UPDATE_CODE,
useEffect(() => {
postMessage({
type: PostMessageType.UPDATE_STATE,
stateChange: {
code: sourceCode ?? undefined,
config: {
can_fix_error: Boolean(onReportError),
canFixError: Boolean(onFixError)
},
code,
});
},
[onReportError],
);
theme: theme ?? 'system',
fullscreen: false,
ancestorOrigin: window.location.origin,
}
})
}, [sourceCode, onFixError, theme, postMessage]);

const handleMessage = useCallback(
async (event: MessageEvent<StliteMessage>) => {
Expand All @@ -84,15 +80,19 @@ export function ArtifactSharedIframe({ sourceCode, onReportError }: Props) {
return;
}

if (
data.type === RecieveMessageType.SCRIPT_RUN_STATE_CHANGED &&
data.scriptRunState === ScriptRunState.RUNNING
) {
if (data.type === RecieveMessageType.READY) {
setState(State.READY);
return;
}

if (data.type === RecieveMessageType.REQUEST) {
const respond = (payload: unknown = undefined) =>
postMessage({
type: PostMessageType.RESPONSE,
request_id: data.request_id,
payload,
});

try {
switch (data.request_type) {
case 'modules_to_packages':
Expand All @@ -101,12 +101,9 @@ export function ArtifactSharedIframe({ sourceCode, onReportError }: Props) {
project.id,
data.payload.modules,
);
postMessage({
type: PostMessageType.RESPONSE,
request_id: data.request_id,
payload: packagesResponse,
});
respond(packagesResponse);
break;

case 'chat_completion':
const response = await createChatCompletion(
organization.id,
Expand All @@ -115,49 +112,23 @@ export function ArtifactSharedIframe({ sourceCode, onReportError }: Props) {
);
const message = response?.choices[0]?.message?.content;
if (!message) throw new Error(); // missing completion
postMessage({
type: PostMessageType.RESPONSE,
request_id: data.request_id,
payload: { message },
});
respond({ message });
break;

case 'fix_error':
onFixError?.(data.payload.errorText);
respond();
break;
}
} catch (err) {
postMessage({
type: PostMessageType.RESPONSE,
request_id: data.request_id,
payload: { error: getErrorMessage(err) },
});
respond({ error: getErrorMessage(err) });
}
return;
}

if (data.type === RecieveMessageType.REPORT_ERROR) {
onReportError?.(data.errorText);
return;
}
},
[project, organization, onReportError],
[project, organization, onFixError, postMessage],
);

const handleIframeLoad = useCallback(() => {
if (theme) {
updateTheme(theme);
}
}, [theme, updateTheme]);

useEffect(() => {
if (theme) {
updateTheme(theme);
}
}, [theme, updateTheme]);

useEffect(() => {
if (state === State.READY) {
updateCode(sourceCode);
}
}, [state, theme, sourceCode, updateCode]);

useEffect(() => {
window.addEventListener('message', handleMessage);

Expand All @@ -180,31 +151,32 @@ export function ArtifactSharedIframe({ sourceCode, onReportError }: Props) {
'allow-popups-to-escape-sandbox',
].join(' ')}
className={classes.app}
onLoad={handleIframeLoad}
onLoad={() => setIframeLoadCount(i => i + 1)}
/>

{!sourceCode ? (
<div className={classes.placeholder}>
<AppPlaceholder />
</div>
) : (
state === State.LOADING && sourceCode && <Loading />
state === State.LOADING && <Loading />
)}
</div>
);
}

type PostMessage =
| {
type: PostMessageType.UPDATE_CODE;
code: string;
config: {
can_fix_error?: boolean;
};
}
| {
type: PostMessageType.UPDATE_THEME;
theme: Theme;
type: PostMessageType.UPDATE_STATE;
stateChange: Partial<{
fullscreen: boolean,
theme: 'light' | 'dark' | 'system',
code: string,
config: {
canFixError: boolean
},
ancestorOrigin: string,
}>;
}
| {
type: PostMessageType.RESPONSE;
Expand All @@ -213,15 +185,8 @@ type PostMessage =
};

enum PostMessageType {
UPDATE_CODE = 'bee:updateCode',
UPDATE_THEME = 'bee:updateTheme',
RESPONSE = 'bee:response',
}

enum ScriptRunState {
INITIAL = 'initial',
NOT_RUNNING = 'notRunning',
RUNNING = 'running',
UPDATE_STATE = 'bee:updateState'
}

enum State {
Expand All @@ -230,15 +195,13 @@ enum State {
}

enum RecieveMessageType {
SCRIPT_RUN_STATE_CHANGED = 'SCRIPT_RUN_STATE_CHANGED',
READY = 'bee:ready',
REQUEST = 'bee:request',
REPORT_ERROR = 'bee:reportError',
}

export type StliteMessage =
| {
type: RecieveMessageType.SCRIPT_RUN_STATE_CHANGED;
scriptRunState: ScriptRunState;
type: RecieveMessageType.READY;
}
| {
type: RecieveMessageType.REQUEST;
Expand All @@ -253,6 +216,8 @@ export type StliteMessage =
payload: ChatCompletionCreateBody;
}
| {
type: RecieveMessageType.REPORT_ERROR;
errorText: string;
type: RecieveMessageType.REQUEST;
request_type: 'fix_error';
request_id: string;
payload: { errorText: string };
};

0 comments on commit ed7727b

Please sign in to comment.