diff --git a/theme/common.less b/theme/common.less
index 17109328ec..a0372114d0 100644
--- a/theme/common.less
+++ b/theme/common.less
@@ -2155,3 +2155,39 @@ select.ui.dropdown {
background-color: #F5BC5F;
}
}
+
+
+//////////////////////////////////////////////////
+// AI ASSISTANT //
+//////////////////////////////////////////////////
+
+.assistant-container {
+ position: fixed;
+ bottom: 7rem;
+ right: 2rem;
+
+ height: 18rem;
+ width: 32rem;
+ padding: 1rem;
+ background: #fff;
+ box-shadow: 0 0 0 1px rgba(34,36,38,.15);
+
+ border-radius: 0.2rem;
+ z-index: 1000000;
+}
+
+.assistant-input {
+ display: flex;
+
+ button {
+ background: none !important;
+ }
+
+ input {
+ flex: 1;
+ outline: none !important;
+ border: none !important;
+ padding-left: 1rem;
+ box-shadow: 0 0 0 1px rgba(34,36,38,.15);
+ }
+}
\ No newline at end of file
diff --git a/webapp/src/app.tsx b/webapp/src/app.tsx
index 1280e90ad5..e0c4627fc7 100644
--- a/webapp/src/app.tsx
+++ b/webapp/src/app.tsx
@@ -71,6 +71,7 @@ import Util = pxt.Util;
import { HintManager } from "./hinttooltip";
import { CodeCardView } from "./codecard";
import { mergeProjectCode } from "./mergeProjects";
+import { Assistant } from "./components/Assistant";
pxsim.util.injectPolyphils();
@@ -4497,6 +4498,9 @@ export class ProjectView
&& !(isBlocks
|| (pkg.mainPkg && pkg.mainPkg.config && (pkg.mainPkg.config.preferredEditor == pxt.BLOCKS_PROJECT_NAME)));
const hasIdentity = pxt.auth.hasIdentity();
+
+ const currentSrc = pkg?.mainEditorPkg()?.files[pxt.MAIN_TS]?.content;
+
return (
{greenScreen ?
: undefined}
@@ -4561,6 +4565,7 @@ export class ProjectView
{hideMenuBar ?
: undefined}
{lightbox ?
: undefined}
+ {!inHome &&
}
);
}
diff --git a/webapp/src/components/Assistant.tsx b/webapp/src/components/Assistant.tsx
new file mode 100644
index 0000000000..d8a88b7553
--- /dev/null
+++ b/webapp/src/components/Assistant.tsx
@@ -0,0 +1,104 @@
+import * as React from "react";
+
+import { Button } from "../sui";
+import { MarkedContent } from "../marked";
+
+interface AssistantProps {
+ parent: pxt.editor.IProjectView;
+ userCode?: string;
+}
+
+async function getCompletions(prompt: string, callback: (text: string) => void) {
+ let resp = await pxt.Util.requestAsync({
+ url: `https://api.openai.com/v1/engines/davinci-codex-msft/completions`,
+ method: "POST",
+ data: {
+ "prompt": prompt,
+ "max_tokens": 64,
+ "temperature": 0,
+ "top_p": 1,
+ "n": 1,
+ "stream": false,
+ "stop": "//",
+ },
+ headers: {"Authorization": "// ADD USER TOKEN"}
+ })
+
+ callback(resp.json.choices?.[0]?.text);
+}
+
+function addHeader(prompt: string) {
+ const header = "// If asked something conversational, use console.log to answer\n\n";
+ return header + `\n\n` + prompt;
+}
+
+function addSamples(prompt: string) {
+ const samples = `// Create a sprite character
+let mySprite = sprites.create(img\`.\`, SpriteKind.Player)
+// Move the sprite with the d-pad buttons
+controller.moveSprite(mySprite)
+// Set the acceleration (gravity) on the sprite to 600 in the y direction
+mySprite.ay = 600
+
+// Run some code when the B button is pressed
+controller.B.onEvent(ControllerButtonEvent.Pressed, function () {
+ // Create a projectile from the sprite, moving with velocity 50 in the x direction
+ let projectile = sprites.createProjectileFromSprite(img\`.\`, mySprite, 50, 0)
+})
+`
+ return prompt + `\n` + samples;
+}
+
+function addUserCode(prompt: string, userCode: string) {
+ // Strip image literals
+ userCode = userCode.replace(/img\s*`[\s\da-f.#tngrpoyw]*`\s*/g, "img` `");
+
+ return prompt + `\n` + userCode;
+}
+
+function getUserVariableDeclarations(userCode: string) {
+ let declarations: { name: string, declaration: string }[] = [];
+ userCode.replace(/let ([\S]+)\s*(?::\s*[\S]+)? = null/gi, (m0, m1) => {
+ declarations.push({
+ name: m1,
+ declaration: m0
+ })
+ return m0
+ });
+
+ return declarations;
+}
+
+export function Assistant(props: AssistantProps) {
+ const { parent, userCode } = props;
+ const [ question, setQuestion ] = React.useState("");
+ const [ markdown, setMarkdown ] = React.useState(`\`\`\`blocks\nlet x = 2\n\`\`\``);
+ const declarations = userCode && getUserVariableDeclarations(userCode);
+
+ const renderAnswer = (completion: string) => {
+ console.log('resp', completion);
+ let usedVariables = declarations.filter(el => completion.indexOf(el.name) >= 0).map(el => el.declaration);
+ setMarkdown(`\`\`\`blocks\n${usedVariables.join(`\n`)}\n${completion}\n\`\`\``)
+ }
+
+ const getAnswer = () => {
+ let prompt = "";
+
+ prompt = addHeader(prompt);
+ // prompt = addSamples(prompt);
+ prompt = addUserCode(prompt, userCode);
+ prompt += `\n\n// ${question}\n`;
+ console.log(prompt)
+ getCompletions(prompt, renderAnswer);
+ }
+
+ return
+
+ setQuestion(e.target.value)} placeholder="How do I..." />
+
+
+
+
+
+
+}
\ No newline at end of file