Skip to content

Commit

Permalink
Improve UI for viewing and changing problems (#15)
Browse files Browse the repository at this point in the history
  • Loading branch information
bhackett1024 authored Feb 11, 2025
1 parent b7b6020 commit bb82c56
Show file tree
Hide file tree
Showing 12 changed files with 570 additions and 114 deletions.
8 changes: 8 additions & 0 deletions app/components/chat/Chat.client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,12 @@ setInterval(async () => {
}
}, 1000);

let gLastUserPrompt: string | undefined = "app goes blank getting directions";

export function getLastUserPrompt(): string | undefined {
return gLastUserPrompt;
}

export function Chat() {
renderLogger.trace('Chat');

Expand Down Expand Up @@ -345,6 +351,8 @@ export const ChatImpl = memo(
return;
}

gLastUserPrompt = _input;

const anthropicApiKey = Cookies.get(anthropicApiKeyCookieName);
if (!anthropicApiKey) {
const numFreeUses = +(Cookies.get(anthropicNumFreeUsesCookieName) || 0);
Expand Down
38 changes: 19 additions & 19 deletions app/components/chat/LoadProblemButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,40 @@ import { toast } from 'react-toastify';
import { createChatFromFolder, type FileArtifact } from '~/utils/folderImport';
import { logStore } from '~/lib/stores/logs'; // Assuming logStore is imported from this location
import { assert, sendCommandDedicatedClient } from '~/lib/replay/ReplayProtocolClient';
import type { BoltProblem } from '~/components/sidebar/SaveProblem';
import type { BoltProblem } from '~/lib/replay/Problems';
import { getProblem } from '~/lib/replay/Problems';
import JSZip from 'jszip';

interface LoadProblemButtonProps {
className?: string;
importChat?: (description: string, messages: Message[]) => Promise<void>;
}

export async function loadProblem(problemId: string, importChat: (description: string, messages: Message[]) => Promise<void>) {
let problem: BoltProblem | null = null;
try {
const rv = await sendCommandDedicatedClient({
method: "Recording.globalExperimentalCommand",
params: {
name: "fetchBoltProblem",
params: { problemId },
},
});
console.log("FetchProblemRval", rv);
problem = (rv as { rval: BoltProblem }).rval;
} catch (error) {
console.error("Error fetching problem", error);
toast.error("Failed to fetch problem");
export function setLastLoadedProblem(problem: BoltProblem) {
localStorage.setItem('loadedProblem', JSON.stringify(problem));
}

export function getLastLoadedProblem(): BoltProblem | undefined {
const problemJSON = localStorage.getItem('loadedProblem');
if (!problemJSON) {
return undefined;
}
return JSON.parse(problemJSON);
}

export async function loadProblem(problemId: string, importChat: (description: string, messages: Message[]) => Promise<void>) {
const problem = await getProblem(problemId);

if (!problem) {
return;
}

const problemContents = problem.prompt.content;
const problemTitle = problem.title;
setLastLoadedProblem(problem);

const { repositoryContents, title: problemTitle } = problem;

const zip = new JSZip();
await zip.loadAsync(problemContents, { base64: true });
await zip.loadAsync(repositoryContents, { base64: true });

const fileArtifacts: FileArtifact[] = [];
for (const [key, object] of Object.entries(zip.files)) {
Expand Down
2 changes: 1 addition & 1 deletion app/components/settings/SettingsWindow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export const SettingsWindow = ({ open, onClose }: SettingsProps) => {

const tabs: { id: TabType; label: string; icon: string; component?: ReactElement }[] = [
{ id: 'data', label: 'Data', icon: 'i-ph:database', component: <DataTab /> },
{ id: 'apiKeys', label: 'API Keys', icon: 'i-ph:key', component: <APIKeysTab /> },
{ id: 'apiKeys', label: 'User Info', icon: 'i-ph:key', component: <APIKeysTab /> },
{ id: 'connection', label: 'Connection', icon: 'i-ph:link', component: <ConnectionsTab /> },
{ id: 'features', label: 'Features', icon: 'i-ph:star', component: <FeaturesTab /> },
...(debug
Expand Down
35 changes: 35 additions & 0 deletions app/components/settings/providers/APIKeysTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ import { useState } from 'react';
import { toast } from 'react-toastify';
import Cookies from 'js-cookie';
import { anthropicNumFreeUsesCookieName, anthropicApiKeyCookieName, MaxFreeUses } from '~/utils/freeUses';
import { setNutAdminKey, setProblemsUsername, getNutAdminKey, getProblemsUsername } from '~/lib/replay/Problems';

export default function ConnectionsTab() {
const [apiKey, setApiKey] = useState(Cookies.get(anthropicApiKeyCookieName) || '');
const [username, setUsername] = useState(getProblemsUsername() || '');
const [adminKey, setAdminKey] = useState(getNutAdminKey() || '');
const numFreeUses = +(Cookies.get(anthropicNumFreeUsesCookieName) || 0);

const handleSaveAPIKey = async (key: string) => {
Expand All @@ -17,6 +20,16 @@ export default function ConnectionsTab() {
setApiKey(key);
};

const handleSaveUsername = async (username: string) => {
setProblemsUsername(username);
setUsername(username);
};

const handleSaveAdminKey = async (key: string) => {
setNutAdminKey(key);
setAdminKey(key);
};

return (
<div className="p-4 mb-4 border border-bolt-elements-borderColor rounded-lg bg-bolt-elements-background-depth-3">
<h3 className="text-lg font-medium text-bolt-elements-textPrimary mb-4">Anthropic API Key</h3>
Expand All @@ -37,6 +50,28 @@ export default function ConnectionsTab() {
</div>
</div>
)}
<h3 className="text-lg font-medium text-bolt-elements-textPrimary mb-4">Problems User Name</h3>
<div className="flex mb-4">
<div className="flex-1 mr-2">
<input
type="text"
value={username}
onChange={(e) => handleSaveUsername(e.target.value)}
className="w-full bg-white dark:bg-bolt-elements-background-depth-4 relative px-2 py-1.5 rounded-md focus:outline-none placeholder-bolt-elements-textTertiary text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary border border-bolt-elements-borderColor disabled:opacity-50"
/>
</div>
</div>
<h3 className="text-lg font-medium text-bolt-elements-textPrimary mb-4">Nut Admin Key</h3>
<div className="flex mb-4">
<div className="flex-1 mr-2">
<input
type="text"
value={adminKey}
onChange={(e) => handleSaveAdminKey(e.target.value)}
className="w-full bg-white dark:bg-bolt-elements-background-depth-4 relative px-2 py-1.5 rounded-md focus:outline-none placeholder-bolt-elements-textTertiary text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary border border-bolt-elements-borderColor disabled:opacity-50"
/>
</div>
</div>
</div>
);
}
3 changes: 3 additions & 0 deletions app/components/sidebar/Menu.client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import { binDates } from './date-binning';
import { useSearchFilter } from '~/lib/hooks/useSearchFilter';
import ReactModal from 'react-modal';
import { SaveProblem } from './SaveProblem';
import { SaveSolution } from './SaveSolution';
import { hasNutAdminKey } from '~/lib/replay/Problems';

const menuVariants = {
closed: {
Expand Down Expand Up @@ -138,6 +140,7 @@ export const Menu = () => {
Problems
</a>
<SaveProblem />
{hasNutAdminKey() && <SaveSolution />}
<a
href="/about"
className="flex gap-2 bg-bolt-elements-sidebar-buttonBackgroundDefault text-bolt-elements-sidebar-buttonText hover:bg-bolt-elements-sidebar-buttonBackgroundHover rounded-md p-2 transition-theme"
Expand Down
77 changes: 18 additions & 59 deletions app/components/sidebar/SaveProblem.tsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,18 @@
import { toast } from "react-toastify";
import ReactModal from 'react-modal';
import { assert, sendCommandDedicatedClient } from "~/lib/replay/ReplayProtocolClient";
import { useState } from "react";
import { workbenchStore } from "~/lib/stores/workbench";
import { getProblemsUsername, submitProblem } from "~/lib/replay/Problems";
import type { BoltProblemInput } from "~/lib/replay/Problems";

ReactModal.setAppElement('#root');

// Combines information about the contents of a project along with a prompt
// from the user and any associated Replay data to accomplish a task. Together
// this information is enough that the model should be able to generate a
// suitable fix.
//
// Must be JSON serializable.
interface ProjectPrompt {
content: string; // base64 encoded zip file
}

export interface BoltProblem {
version: number;
title: string;
description: string;
name: string;
email: string;
prompt: ProjectPrompt;
}

export function SaveProblem() {
const [isModalOpen, setIsModalOpen] = useState(false);
const [formData, setFormData] = useState({
title: '',
description: '',
name: '',
email: ''
name: ''
});
const [problemId, setProblemId] = useState<string | null>(null);

Expand All @@ -40,8 +21,7 @@ export function SaveProblem() {
setFormData({
title: '',
description: '',
name: '',
email: '',
name: ''
});
setProblemId(null);
};
Expand All @@ -61,36 +41,31 @@ export function SaveProblem() {
return;
}

const username = getProblemsUsername();

if (!username) {
toast.error('Please fill in username field');
return;
}

toast.info("Submitting problem...");

console.log("SubmitProblem", formData);

await workbenchStore.saveAllFiles();
const { contentBase64 } = await workbenchStore.generateZipBase64();
const prompt: ProjectPrompt = { content: contentBase64 };

const problem: BoltProblem = {
version: 1,
const problem: BoltProblemInput = {
version: 2,
title: formData.title,
description: formData.description,
name: formData.name,
email: formData.email,
prompt,
username,
repositoryContents: contentBase64,
};

try {
const rv = await sendCommandDedicatedClient({
method: "Recording.globalExperimentalCommand",
params: {
name: "submitBoltProblem",
params: { problem },
},
});
console.log("SubmitProblemRval", rv);
setProblemId((rv as any).rval.problemId);
} catch (error) {
console.error("Error submitting problem", error);
toast.error("Failed to submit problem");
const problemId = await submitProblem(problem);
if (problemId) {
setProblemId(problemId);
}
}

Expand Down Expand Up @@ -140,22 +115,6 @@ export function SaveProblem() {
value={formData.description}
onChange={handleInputChange}
/>

<div className="flex items-center">Name (optional):</div>
<input type="text"
name="name"
className="bg-bolt-elements-background-depth-1 text-bolt-elements-textPrimary rounded px-2 w-full border border-gray-300"
value={formData.name}
onChange={handleInputChange}
/>

<div className="flex items-center">Email (optional):</div>
<input type="text"
name="email"
className="bg-bolt-elements-background-depth-1 text-bolt-elements-textPrimary rounded px-2 w-full border border-gray-300"
value={formData.email}
onChange={handleInputChange}
/>
</div>
<div className="flex justify-center gap-2 mt-4">
<button onClick={handleSubmitProblem} className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600">Submit</button>
Expand Down
Loading

0 comments on commit bb82c56

Please sign in to comment.