diff --git a/app/components/chat/Chat.client.tsx b/app/components/chat/Chat.client.tsx
index 94b682f7..0b4952f0 100644
--- a/app/components/chat/Chat.client.tsx
+++ b/app/components/chat/Chat.client.tsx
@@ -29,6 +29,7 @@ import { getCurrentMouseData } from '../workbench/PointSelector';
import { anthropicNumFreeUsesCookieName, anthropicApiKeyCookieName, MaxFreeUses } from '~/utils/freeUses';
import type { FileMap } from '~/lib/stores/files';
import { shouldIncludeFile } from '~/utils/fileUtils';
+import { getNutLoginKey } from '~/lib/replay/Problems';
const toastAnimation = cssTransition({
enter: 'animated fadeInRight',
@@ -334,11 +335,18 @@ export const ChatImpl = memo(
return;
}
+ const loginKey = getNutLoginKey();
+ if (!loginKey) {
+ toast.error('Please set a login key in the "User Info" settings.');
+ return;
+ }
+
const anthropicApiKey = Cookies.get(anthropicApiKeyCookieName);
- if (!anthropicApiKey) {
+
+ if (!loginKey && !anthropicApiKey) {
const numFreeUses = +(Cookies.get(anthropicNumFreeUsesCookieName) || 0);
if (numFreeUses >= MaxFreeUses) {
- toast.error('All free uses consumed. Please set an Anthropic API key in the settings.');
+ toast.error('All free uses consumed. Please set a login key or Anthropic API key in the "User Info" settings.');
return;
}
@@ -412,7 +420,7 @@ export const ChatImpl = memo(
image: imageData,
})),
] as any, // Type assertion to bypass compiler check
- }, { body: { simulationEnhancedPrompt, anthropicApiKey } });
+ }, { body: { simulationEnhancedPrompt, anthropicApiKey, loginKey } });
if (fileModifications !== undefined) {
/**
diff --git a/app/components/settings/providers/APIKeysTab.tsx b/app/components/settings/providers/APIKeysTab.tsx
index a7bbe115..b8981f5f 100644
--- a/app/components/settings/providers/APIKeysTab.tsx
+++ b/app/components/settings/providers/APIKeysTab.tsx
@@ -2,16 +2,16 @@ 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';
+import { saveNutLoginKey, saveProblemsUsername, getNutLoginKey, 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 [loginKey, setLoginKey] = useState(getNutLoginKey() || '');
const numFreeUses = +(Cookies.get(anthropicNumFreeUsesCookieName) || 0);
const handleSaveAPIKey = async (key: string) => {
- if (!key || !key.startsWith('sk-ant-')) {
+ if (key && !key.startsWith('sk-ant-')) {
toast.error('Please provide a valid Anthropic API key');
return;
}
@@ -21,13 +21,19 @@ export default function ConnectionsTab() {
};
const handleSaveUsername = async (username: string) => {
- setProblemsUsername(username);
+ saveProblemsUsername(username);
setUsername(username);
};
- const handleSaveAdminKey = async (key: string) => {
- setNutAdminKey(key);
- setAdminKey(key);
+ const handleSaveLoginKey = async (key: string) => {
+ setLoginKey(key);
+
+ try {
+ await saveNutLoginKey(key);
+ toast.success('Login key saved');
+ } catch (error) {
+ toast.error('Failed to save login key');
+ }
};
return (
@@ -61,13 +67,13 @@ export default function ConnectionsTab() {
/>
-
Nut Admin Key
+ Nut Login Key
handleSaveAdminKey(e.target.value)}
+ value={loginKey}
+ onChange={(e) => handleSaveLoginKey(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"
/>
diff --git a/app/components/sidebar/Menu.client.tsx b/app/components/sidebar/Menu.client.tsx
index 5549e236..745c5de8 100644
--- a/app/components/sidebar/Menu.client.tsx
+++ b/app/components/sidebar/Menu.client.tsx
@@ -13,7 +13,7 @@ import { binDates } from './date-binning';
import { useSearchFilter } from '~/lib/hooks/useSearchFilter';
import { SaveProblem } from './SaveProblem';
import { SaveSolution } from './SaveSolution';
-import { hasNutAdminKey } from '~/lib/replay/Problems';
+import { getNutIsAdmin } from '~/lib/replay/Problems';
const menuVariants = {
closed: {
@@ -140,7 +140,7 @@ export const Menu = () => {
Problems
- {hasNutAdminKey() &&
}
+ {getNutIsAdmin() &&
}
();
let finished: (v?: any) => void;
@@ -49,6 +50,7 @@ async function chatAction({ context, request }: ActionFunctionArgs) {
const anthropicApiKey: AnthropicApiKey = {
key: apiKey,
isUser: !!clientAnthropicApiKey,
+ userLoginKey: loginKey,
};
const resultStream = new ReadableStream({
diff --git a/app/routes/problem.$id.tsx b/app/routes/problem.$id.tsx
index 5af7210f..126f9c57 100644
--- a/app/routes/problem.$id.tsx
+++ b/app/routes/problem.$id.tsx
@@ -5,11 +5,10 @@ import BackgroundRays from '~/components/ui/BackgroundRays';
import { TooltipProvider } from '@radix-ui/react-tooltip';
import { ToastContainerWrapper, Status, Keywords } from './problems';
import { toast } from 'react-toastify';
-import { useCallback, useEffect } from 'react';
-import { useState } from 'react';
+import { Suspense, useCallback, useEffect, useState } from 'react';
import { useParams } from '@remix-run/react';
-import { getProblem, updateProblem as backendUpdateProblem, getProblemsUsername, BoltProblemStatus, hasNutAdminKey } from '~/lib/replay/Problems';
-import type { BoltProblem, BoltProblemComment, BoltProblemInput } from '~/lib/replay/Problems';
+import { getProblem, updateProblem as backendUpdateProblem, getProblemsUsername, BoltProblemStatus, getNutIsAdmin } from '~/lib/replay/Problems';
+import type { BoltProblem, BoltProblemComment } from '~/lib/replay/Problems';
function Comments({ comments }: { comments: BoltProblemComment[] }) {
return (
@@ -152,6 +151,8 @@ function UpdateProblemForms({ updateProblem }: { updateProblem: UpdateProblemCal
)
}
+const Nothing = () => null;
+
function ViewProblemPage() {
const params = useParams();
const problemId = params.id;
@@ -177,6 +178,7 @@ function ViewProblemPage() {
}, [problemId]);
return (
+ }>
@@ -190,12 +192,13 @@ function ViewProblemPage() {
)
: }
- {hasNutAdminKey() && problemData && (
+ {getNutIsAdmin() && problemData && (
)}
+
);
}
diff --git a/app/routes/problems.tsx b/app/routes/problems.tsx
index 78259f7c..bde969ac 100644
--- a/app/routes/problems.tsx
+++ b/app/routes/problems.tsx
@@ -4,8 +4,7 @@ import { Menu } from '~/components/sidebar/Menu.client';
import BackgroundRays from '~/components/ui/BackgroundRays';
import { TooltipProvider } from '@radix-ui/react-tooltip';
import { cssTransition, ToastContainer } from 'react-toastify';
-import { useEffect } from 'react';
-import { useState } from 'react';
+import { Suspense, useEffect, useState } from 'react';
import { BoltProblemStatus, listAllProblems } from '~/lib/replay/Problems';
import type { BoltProblemDescription } from '~/lib/replay/Problems';
@@ -91,6 +90,8 @@ function getProblemStatus(problem: BoltProblemDescription): BoltProblemStatus {
return problem.status ?? BoltProblemStatus.Pending;
}
+const Nothing = () => null;
+
function ProblemsPage() {
const [problems, setProblems] = useState(null);
const [statusFilter, setStatusFilter] = useState(BoltProblemStatus.Solved);
@@ -104,6 +105,7 @@ function ProblemsPage() {
});
return (
+ }>
@@ -164,6 +166,7 @@ function ProblemsPage() {
+
);
}