Run prettier on everything (#104)

This commit is contained in:
Will Chen
2025-05-06 23:02:28 -07:00
committed by GitHub
parent 744ea68ac8
commit 0d56651220
168 changed files with 1980 additions and 1907 deletions

View File

@@ -27,13 +27,13 @@ Add these commands to your `package.json`:
When mocking the `node:fs` module, use a default export in the mock:
```typescript
vi.mock('node:fs', async () => {
vi.mock("node:fs", async () => {
return {
default: {
mkdirSync: vi.fn(),
writeFileSync: vi.fn(),
// Add other fs methods as needed
}
},
};
});
```
@@ -43,12 +43,12 @@ vi.mock('node:fs', async () => {
When mocking isomorphic-git, provide a default export:
```typescript
vi.mock('isomorphic-git', () => ({
vi.mock("isomorphic-git", () => ({
default: {
add: vi.fn().mockResolvedValue(undefined),
commit: vi.fn().mockResolvedValue(undefined),
// Add other git methods as needed
}
},
}));
```
@@ -57,7 +57,7 @@ vi.mock('isomorphic-git', () => ({
When testing IPC handlers, mock the Electron IPC system:
```typescript
vi.mock('electron', () => ({
vi.mock("electron", () => ({
ipcMain: {
handle: vi.fn(),
on: vi.fn(),
@@ -74,4 +74,4 @@ vi.mock('electron', () => ({
## Example
See `chat_stream_handlers.test.ts` for an example of testing IPC handlers with proper mocking.
See `chat_stream_handlers.test.ts` for an example of testing IPC handlers with proper mocking.

View File

@@ -58,28 +58,28 @@ describe("getDyadAddDependencyTags", () => {
it("should return an array of dyad-add-dependency tags", () => {
const result = getDyadAddDependencyTags(
`<dyad-add-dependency packages="uuid"></dyad-add-dependency>`
`<dyad-add-dependency packages="uuid"></dyad-add-dependency>`,
);
expect(result).toEqual(["uuid"]);
});
it("should return all the packages in the dyad-add-dependency tags", () => {
const result = getDyadAddDependencyTags(
`<dyad-add-dependency packages="pkg1 pkg2"></dyad-add-dependency>`
`<dyad-add-dependency packages="pkg1 pkg2"></dyad-add-dependency>`,
);
expect(result).toEqual(["pkg1", "pkg2"]);
});
it("should return all the packages in the dyad-add-dependency tags", () => {
const result = getDyadAddDependencyTags(
`txt before<dyad-add-dependency packages="pkg1 pkg2"></dyad-add-dependency>text after`
`txt before<dyad-add-dependency packages="pkg1 pkg2"></dyad-add-dependency>text after`,
);
expect(result).toEqual(["pkg1", "pkg2"]);
});
it("should return all the packages in multiple dyad-add-dependency tags", () => {
const result = getDyadAddDependencyTags(
`txt before<dyad-add-dependency packages="pkg1 pkg2"></dyad-add-dependency>txt between<dyad-add-dependency packages="pkg3"></dyad-add-dependency>text after`
`txt before<dyad-add-dependency packages="pkg1 pkg2"></dyad-add-dependency>txt between<dyad-add-dependency packages="pkg3"></dyad-add-dependency>text after`,
);
expect(result).toEqual(["pkg1", "pkg2", "pkg3"]);
});
@@ -450,7 +450,7 @@ declare module 'uuid' {
}
</dyad-write>
I've created a complete todo list application with the ability to add, complete, and delete tasks. The app includes statistics and uses local storage to persist data.`
I've created a complete todo list application with the ability to add, complete, and delete tasks. The app includes statistics and uses local storage to persist data.`,
);
expect(result.length).toEqual(7);
});
@@ -465,7 +465,7 @@ describe("getDyadRenameTags", () => {
it("should return an array of dyad-rename tags", () => {
const result = getDyadRenameTags(
`<dyad-rename from="src/components/UserProfile.jsx" to="src/components/ProfileCard.jsx"></dyad-rename>
<dyad-rename from="src/utils/helpers.js" to="src/utils/utils.js"></dyad-rename>`
<dyad-rename from="src/utils/helpers.js" to="src/utils/utils.js"></dyad-rename>`,
);
expect(result).toEqual([
{
@@ -486,7 +486,7 @@ describe("getDyadDeleteTags", () => {
it("should return an array of dyad-delete paths", () => {
const result = getDyadDeleteTags(
`<dyad-delete path="src/components/Analytics.jsx"></dyad-delete>
<dyad-delete path="src/utils/unused.js"></dyad-delete>`
<dyad-delete path="src/utils/unused.js"></dyad-delete>`,
);
expect(result).toEqual([
"src/components/Analytics.jsx",
@@ -526,7 +526,7 @@ describe("processFullResponse", () => {
{
chatSummary: undefined,
messageId: 1,
}
},
);
expect(result).toEqual({});
expect(fs.mkdirSync).not.toHaveBeenCalled();
@@ -547,16 +547,16 @@ describe("processFullResponse", () => {
expect(fs.mkdirSync).toHaveBeenCalledWith(
"/mock/user/data/path/mock-app-path/src",
{ recursive: true }
{ recursive: true },
);
expect(fs.writeFileSync).toHaveBeenCalledWith(
"/mock/user/data/path/mock-app-path/src/file1.js",
"console.log('Hello');"
"console.log('Hello');",
);
expect(git.add).toHaveBeenCalledWith(
expect.objectContaining({
filepath: "src/file1.js",
})
}),
);
expect(git.commit).toHaveBeenCalled();
expect(result).toEqual({ updatedFiles: true });
@@ -604,49 +604,49 @@ describe("processFullResponse", () => {
// Check that directories were created for each file path
expect(fs.mkdirSync).toHaveBeenCalledWith(
"/mock/user/data/path/mock-app-path/src",
{ recursive: true }
{ recursive: true },
);
expect(fs.mkdirSync).toHaveBeenCalledWith(
"/mock/user/data/path/mock-app-path/src/utils",
{ recursive: true }
{ recursive: true },
);
expect(fs.mkdirSync).toHaveBeenCalledWith(
"/mock/user/data/path/mock-app-path/src/components",
{ recursive: true }
{ recursive: true },
);
// Using toHaveBeenNthCalledWith to check each specific call
expect(fs.writeFileSync).toHaveBeenNthCalledWith(
1,
"/mock/user/data/path/mock-app-path/src/file1.js",
"console.log('First file');"
"console.log('First file');",
);
expect(fs.writeFileSync).toHaveBeenNthCalledWith(
2,
"/mock/user/data/path/mock-app-path/src/utils/file2.js",
"export const add = (a, b) => a + b;"
"export const add = (a, b) => a + b;",
);
expect(fs.writeFileSync).toHaveBeenNthCalledWith(
3,
"/mock/user/data/path/mock-app-path/src/components/Button.tsx",
"import React from 'react';\n export const Button = ({ children }) => <button>{children}</button>;"
"import React from 'react';\n export const Button = ({ children }) => <button>{children}</button>;",
);
// Verify git operations were called for each file
expect(git.add).toHaveBeenCalledWith(
expect.objectContaining({
filepath: "src/file1.js",
})
}),
);
expect(git.add).toHaveBeenCalledWith(
expect.objectContaining({
filepath: "src/utils/file2.js",
})
}),
);
expect(git.add).toHaveBeenCalledWith(
expect.objectContaining({
filepath: "src/components/Button.tsx",
})
}),
);
// Verify commit was called once after all files were added
@@ -669,21 +669,21 @@ describe("processFullResponse", () => {
expect(fs.mkdirSync).toHaveBeenCalledWith(
"/mock/user/data/path/mock-app-path/src/components",
{ recursive: true }
{ recursive: true },
);
expect(fs.renameSync).toHaveBeenCalledWith(
"/mock/user/data/path/mock-app-path/src/components/OldComponent.jsx",
"/mock/user/data/path/mock-app-path/src/components/NewComponent.jsx"
"/mock/user/data/path/mock-app-path/src/components/NewComponent.jsx",
);
expect(git.add).toHaveBeenCalledWith(
expect.objectContaining({
filepath: "src/components/NewComponent.jsx",
})
}),
);
expect(git.remove).toHaveBeenCalledWith(
expect.objectContaining({
filepath: "src/components/OldComponent.jsx",
})
}),
);
expect(git.commit).toHaveBeenCalled();
expect(result).toEqual({ updatedFiles: true });
@@ -719,12 +719,12 @@ describe("processFullResponse", () => {
});
expect(fs.unlinkSync).toHaveBeenCalledWith(
"/mock/user/data/path/mock-app-path/src/components/Unused.jsx"
"/mock/user/data/path/mock-app-path/src/components/Unused.jsx",
);
expect(git.remove).toHaveBeenCalledWith(
expect.objectContaining({
filepath: "src/components/Unused.jsx",
})
}),
);
expect(git.commit).toHaveBeenCalled();
expect(result).toEqual({ updatedFiles: true });
@@ -769,18 +769,18 @@ describe("processFullResponse", () => {
// Check write operation happened
expect(fs.writeFileSync).toHaveBeenCalledWith(
"/mock/user/data/path/mock-app-path/src/components/NewComponent.jsx",
"import React from 'react'; export default () => <div>New</div>;"
"import React from 'react'; export default () => <div>New</div>;",
);
// Check rename operation happened
expect(fs.renameSync).toHaveBeenCalledWith(
"/mock/user/data/path/mock-app-path/src/components/OldComponent.jsx",
"/mock/user/data/path/mock-app-path/src/components/RenamedComponent.jsx"
"/mock/user/data/path/mock-app-path/src/components/RenamedComponent.jsx",
);
// Check delete operation happened
expect(fs.unlinkSync).toHaveBeenCalledWith(
"/mock/user/data/path/mock-app-path/src/components/Unused.jsx"
"/mock/user/data/path/mock-app-path/src/components/Unused.jsx",
);
// Check git operations
@@ -791,9 +791,9 @@ describe("processFullResponse", () => {
expect(git.commit).toHaveBeenCalledWith(
expect.objectContaining({
message: expect.stringContaining(
"wrote 1 file(s), renamed 1 file(s), deleted 1 file(s)"
"wrote 1 file(s), renamed 1 file(s), deleted 1 file(s)",
),
})
}),
);
expect(result).toEqual({ updatedFiles: true });

View File

@@ -92,7 +92,7 @@ export const TitleBar = () => {
variant="outline"
className={cn(
"ml-4 no-app-region-drag h-7 bg-indigo-600 text-white dark:bg-indigo-600 dark:text-white",
!isDyadProEnabled && "bg-zinc-600 dark:bg-zinc-600"
!isDyadProEnabled && "bg-zinc-600 dark:bg-zinc-600",
)}
size="sm"
>

View File

@@ -63,7 +63,7 @@ ${debugInfo.logs.slice(-3_500) || "No logs available"}
// Create the GitHub issue URL with the pre-filled body
const encodedBody = encodeURIComponent(issueBody);
const encodedTitle = encodeURIComponent(
"[bug] Error in Dyad application"
"[bug] Error in Dyad application",
);
const githubIssueUrl = `https://github.com/dyad-sh/dyad/issues/new?title=${encodedTitle}&labels=bug,filed-from-app,client-error&body=${encodedBody}`;
@@ -73,7 +73,7 @@ ${debugInfo.logs.slice(-3_500) || "No logs available"}
console.error("Failed to prepare bug report:", err);
// Fallback to opening the regular GitHub issue page
IpcClient.getInstance().openExternalUrl(
"https://github.com/dyad-sh/dyad/issues/new"
"https://github.com/dyad-sh/dyad/issues/new",
);
} finally {
setIsLoading(false);

View File

@@ -21,7 +21,7 @@ export function GitHubConnector({ appId, folderName }: GitHubConnectorProps) {
const [githubError, setGithubError] = useState<string | null>(null);
const [isConnectingToGithub, setIsConnectingToGithub] = useState(false);
const [githubStatusMessage, setGithubStatusMessage] = useState<string | null>(
null
null,
);
const [codeCopied, setCodeCopied] = useState(false);
// --- ---
@@ -91,7 +91,7 @@ export function GitHubConnector({ appId, folderName }: GitHubConnectorProps) {
setGithubUserCode(null);
setGithubVerificationUri(null);
setIsConnectingToGithub(false);
}
},
);
cleanupFunctions.push(removeErrorListener);
@@ -134,7 +134,7 @@ export function GitHubConnector({ appId, folderName }: GitHubConnectorProps) {
try {
const result = await IpcClient.getInstance().checkGithubRepoAvailable(
githubOrg,
repoName
repoName,
);
setRepoAvailable(result.available);
if (!result.available) {
@@ -156,7 +156,7 @@ export function GitHubConnector({ appId, folderName }: GitHubConnectorProps) {
const result = await IpcClient.getInstance().createGithubRepo(
githubOrg,
repoName,
appId!
appId!,
);
if (result.success) {
setCreateRepoSuccess(true);
@@ -247,7 +247,7 @@ export function GitHubConnector({ appId, folderName }: GitHubConnectorProps) {
onClick={(e) => {
e.preventDefault();
IpcClient.getInstance().openExternalUrl(
githubVerificationUri
githubVerificationUri,
);
}}
target="_blank"
@@ -273,7 +273,7 @@ export function GitHubConnector({ appId, folderName }: GitHubConnectorProps) {
setTimeout(() => setCodeCopied(false), 2000);
})
.catch((err) =>
console.error("Failed to copy code:", err)
console.error("Failed to copy code:", err),
);
}
}}
@@ -325,7 +325,7 @@ export function GitHubConnector({ appId, folderName }: GitHubConnectorProps) {
onClick={(e) => {
e.preventDefault();
IpcClient.getInstance().openExternalUrl(
`https://github.com/${app.githubOrg}/${app.githubRepo}`
`https://github.com/${app.githubOrg}/${app.githubRepo}`,
);
}}
className="cursor-pointer text-blue-600 hover:underline dark:text-blue-400"

View File

@@ -21,7 +21,7 @@ export function GitHubIntegration() {
}
} catch (err: any) {
showError(
err.message || "An error occurred while disconnecting from GitHub"
err.message || "An error occurred while disconnecting from GitHub",
);
} finally {
setIsDisconnecting(false);

View File

@@ -105,7 +105,7 @@ ${debugInfo.logs.slice(-3_500) || "No logs available"}
console.error("Failed to prepare bug report:", error);
// Fallback to opening the regular GitHub issue page
IpcClient.getInstance().openExternalUrl(
"https://github.com/dyad-sh/dyad/issues/new"
"https://github.com/dyad-sh/dyad/issues/new",
);
} finally {
setIsLoading(false);
@@ -121,9 +121,8 @@ ${debugInfo.logs.slice(-3_500) || "No logs available"}
setIsUploading(true);
try {
// Get chat logs (includes debug info, chat data, and codebase)
const chatLogs = await IpcClient.getInstance().getChatLogs(
selectedChatId
);
const chatLogs =
await IpcClient.getInstance().getChatLogs(selectedChatId);
// Store data for review and switch to review mode
setChatLogsData(chatLogs);
@@ -131,7 +130,7 @@ ${debugInfo.logs.slice(-3_500) || "No logs available"}
} catch (error) {
console.error("Failed to upload chat session:", error);
alert(
"Failed to upload chat session. Please try again or report manually."
"Failed to upload chat session. Please try again or report manually.",
);
} finally {
setIsUploading(false);
@@ -162,7 +161,7 @@ ${debugInfo.logs.slice(-3_500) || "No logs available"}
extension: "json",
contentType: "application/json",
}),
}
},
);
if (!response.ok) {
@@ -176,7 +175,7 @@ ${debugInfo.logs.slice(-3_500) || "No logs available"}
const uploadResult = await IpcClient.getInstance().uploadToSignedUrl(
uploadUrl,
"application/json",
chatLogsJson
chatLogsJson,
);
if (!uploadResult.success) {
@@ -374,7 +373,7 @@ Session ID: ${sessionId}
variant="outline"
onClick={() => {
IpcClient.getInstance().openExternalUrl(
"https://www.dyad.sh/docs"
"https://www.dyad.sh/docs",
);
}}
className="w-full py-6 bg-(--background-lightest)"

View File

@@ -62,14 +62,14 @@ export function ModelPicker({
if (selectedModel.provider === "ollama") {
return (
ollamaModels.find(
(model: LocalModel) => model.modelName === selectedModel.name
(model: LocalModel) => model.modelName === selectedModel.name,
)?.displayName || selectedModel.name
);
}
if (selectedModel.provider === "lmstudio") {
return (
lmStudioModels.find(
(model: LocalModel) => model.modelName === selectedModel.name
(model: LocalModel) => model.modelName === selectedModel.name,
)?.displayName || selectedModel.name // Fallback to path if not found
);
}
@@ -77,7 +77,7 @@ export function ModelPicker({
// Fallback for cloud models
return (
MODEL_OPTIONS[selectedModel.provider]?.find(
(model) => model.name === selectedModel.name
(model) => model.name === selectedModel.name,
)?.displayName || selectedModel.name
);
};
@@ -90,7 +90,7 @@ export function ModelPicker({
models.map((model) => ({
...model,
provider: provider as ModelProvider,
}))
})),
);
// Determine availability of local models

View File

@@ -34,7 +34,7 @@ export function ProviderSettingsGrid({
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{Object.entries(PROVIDERS).map(([key, provider]) => {
const isConfigured = configuredProviders.includes(
key as ModelProvider
key as ModelProvider,
);
return (

View File

@@ -35,7 +35,7 @@ export function SetupBanner() {
const navigate = useNavigate();
const { isAnyProviderSetup, loading } = useSettings();
const [nodeSystemInfo, setNodeSystemInfo] = useState<NodeSystemInfo | null>(
null
null,
);
const [nodeCheckError, setNodeCheckError] = useState<boolean>(false);
const [nodeInstallStep, setNodeInstallStep] =
@@ -106,7 +106,7 @@ export function SetupBanner() {
const bannerClasses = cn(
"w-full mb-6 border rounded-xl shadow-sm overflow-hidden",
"border-zinc-200 dark:border-zinc-700"
"border-zinc-200 dark:border-zinc-700",
);
const getStatusIcon = (isComplete: boolean, hasError: boolean = false) => {
@@ -137,8 +137,8 @@ export function SetupBanner() {
nodeCheckError
? "bg-red-50 dark:bg-red-900/30"
: isNodeSetupComplete
? "bg-green-50 dark:bg-green-900/30"
: "bg-yellow-50 dark:bg-yellow-900/30"
? "bg-green-50 dark:bg-green-900/30"
: "bg-yellow-50 dark:bg-yellow-900/30",
)}
>
<AccordionTrigger className="px-4 py-3 transition-colors w-full hover:no-underline">
@@ -178,7 +178,7 @@ export function SetupBanner() {
className="text-blue-500 dark:text-blue-400 hover:underline"
onClick={() => {
IpcClient.getInstance().openExternalUrl(
"https://nodejs.org/en/download"
"https://nodejs.org/en/download",
);
}}
>
@@ -203,12 +203,12 @@ export function SetupBanner() {
className={cn(
isAnyProviderSetup()
? "bg-green-50 dark:bg-green-900/30"
: "bg-yellow-50 dark:bg-yellow-900/30"
: "bg-yellow-50 dark:bg-yellow-900/30",
)}
>
<AccordionTrigger
className={cn(
"px-4 py-3 transition-colors w-full hover:no-underline"
"px-4 py-3 transition-colors w-full hover:no-underline",
)}
>
<div className="flex items-center justify-between w-full">
@@ -288,7 +288,7 @@ function NodeJsHelpCallout() {
<a
onClick={() => {
IpcClient.getInstance().openExternalUrl(
"https://www.dyad.sh/docs/help/nodejs"
"https://www.dyad.sh/docs/help/nodejs",
);
}}
className="text-blue-600 dark:text-blue-400 hover:underline font-medium"

View File

@@ -100,7 +100,7 @@ export function SupabaseConnector({ appId }: { appId: number }) {
variant="outline"
onClick={() => {
IpcClient.getInstance().openExternalUrl(
`https://supabase.com/dashboard/project/${app.supabaseProjectId}`
`https://supabase.com/dashboard/project/${app.supabaseProjectId}`,
);
}}
className="ml-2 px-2 py-1"
@@ -204,7 +204,7 @@ export function SupabaseConnector({ appId }: { appId: number }) {
<img
onClick={() => {
IpcClient.getInstance().openExternalUrl(
"https://supabase-oauth.dyad.sh/api/connect-supabase/login"
"https://supabase-oauth.dyad.sh/api/connect-supabase/login",
);
}}
src={isDarkMode ? connectSupabaseDark : connectSupabaseLight}

View File

@@ -24,7 +24,7 @@ export function SupabaseIntegration() {
}
} catch (err: any) {
showError(
err.message || "An error occurred while disconnecting from Supabase"
err.message || "An error occurred while disconnecting from Supabase",
);
} finally {
setIsDisconnecting(false);

View File

@@ -33,7 +33,7 @@ export function PrivacyBanner() {
<a
onClick={() => {
IpcClient.getInstance().openExternalUrl(
"https://dyad.sh/docs/telemetry"
"https://dyad.sh/docs/telemetry",
);
}}
className="cursor-pointer text-sm text-blue-600 dark:text-blue-400 hover:underline"

View File

@@ -1,4 +1,3 @@
import { XCircle, AlertTriangle } from "lucide-react"; // Assuming lucide-react is used
interface ChatErrorProps {

View File

@@ -176,7 +176,7 @@ export function ChatInput({ chatId }: { chatId?: number }) {
if (!chatId || !messageId || isApproving || isRejecting || isStreaming)
return;
console.log(
`Approving proposal for chatId: ${chatId}, messageId: ${messageId}`
`Approving proposal for chatId: ${chatId}, messageId: ${messageId}`,
);
setIsApproving(true);
posthog.capture("chat:approve");
@@ -213,7 +213,7 @@ export function ChatInput({ chatId }: { chatId?: number }) {
if (!chatId || !messageId || isApproving || isRejecting || isStreaming)
return;
console.log(
`Rejecting proposal for chatId: ${chatId}, messageId: ${messageId}`
`Rejecting proposal for chatId: ${chatId}, messageId: ${messageId}`,
);
setIsRejecting(true);
posthog.capture("chat:reject");
@@ -771,7 +771,7 @@ function ChatInputActions({
className="flex items-center space-x-2"
onClick={() => {
IpcClient.getInstance().openExternalUrl(
`https://www.npmjs.com/package/${pkg}`
`https://www.npmjs.com/package/${pkg}`,
);
}}
>
@@ -884,7 +884,7 @@ function ProposalSummary({
parts.push(
`${sqlQueries.length} SQL ${
sqlQueries.length === 1 ? "query" : "queries"
}`
}`,
);
}
@@ -892,7 +892,7 @@ function ProposalSummary({
parts.push(
`${serverFunctions.length} Server ${
serverFunctions.length === 1 ? "Function" : "Functions"
}`
}`,
);
}
@@ -900,13 +900,13 @@ function ProposalSummary({
parts.push(
`${packagesAdded.length} ${
packagesAdded.length === 1 ? "package" : "packages"
}`
}`,
);
}
if (filesChanged.length) {
parts.push(
`${filesChanged.length} ${filesChanged.length === 1 ? "file" : "files"}`
`${filesChanged.length} ${filesChanged.length === 1 ? "file" : "files"}`,
);
}

View File

@@ -29,7 +29,7 @@ export const CodeHighlight = memo(
isDarkMode ? githubDark : github,
{
delay: 150,
}
},
);
// Update the cache whenever we get a new highlighted code
@@ -64,5 +64,5 @@ export const CodeHighlight = memo(
},
(prevProps, nextProps) => {
return prevProps.children === nextProps.children;
}
},
);

View File

@@ -56,7 +56,7 @@ export const DyadAddDependency: React.FC<DyadAddDependencyProps> = ({
key={p}
onClick={() => {
IpcClient.getInstance().openExternalUrl(
`https://www.npmjs.com/package/${p}`
`https://www.npmjs.com/package/${p}`,
);
}}
>

View File

@@ -34,8 +34,8 @@ export const DyadExecuteSql: React.FC<DyadExecuteSqlProps> = ({
inProgress
? "border-amber-500"
: aborted
? "border-red-500"
: "border-border"
? "border-red-500"
: "border-border"
}`}
onClick={() => setIsContentVisible(!isContentVisible)}
>

View File

@@ -177,7 +177,7 @@ function parseCustomTags(content: string): ContentPiece[] {
const tagPattern = new RegExp(
`<(${customTagNames.join("|")})\\s*([^>]*)>(.*?)<\\/\\1>`,
"gs"
"gs",
);
const contentPieces: ContentPiece[] = [];
@@ -253,7 +253,7 @@ function getState({
*/
function renderCustomTag(
tagInfo: CustomTagInfo,
{ isStreaming }: { isStreaming: boolean }
{ isStreaming }: { isStreaming: boolean },
): React.ReactNode {
const { tag, attributes, content, inProgress } = tagInfo;

View File

@@ -42,8 +42,8 @@ export const DyadWrite: React.FC<DyadWriteProps> = ({
inProgress
? "border-amber-500"
: aborted
? "border-red-500"
: "border-border"
? "border-red-500"
: "border-border"
}`}
onClick={() => setIsContentVisible(!isContentVisible)}
>

View File

@@ -71,27 +71,27 @@ export const MessagesList = forwardRef<HTMLDivElement, MessagesListProps>(
previousAssistantMessage?.commitHash
) {
console.debug(
"Reverting to previous assistant version"
"Reverting to previous assistant version",
);
await revertVersion({
versionId: previousAssistantMessage.commitHash,
});
const chat = await IpcClient.getInstance().getChat(
selectedChatId
);
const chat =
await IpcClient.getInstance().getChat(
selectedChatId,
);
setMessages(chat.messages);
}
} else {
const chat = await IpcClient.getInstance().getChat(
selectedChatId
);
const chat =
await IpcClient.getInstance().getChat(selectedChatId);
if (chat.initialCommitHash) {
await revertVersion({
versionId: chat.initialCommitHash,
});
const result =
await IpcClient.getInstance().deleteMessages(
selectedChatId
selectedChatId,
);
if (result.success) {
setMessages([]);
@@ -100,7 +100,7 @@ export const MessagesList = forwardRef<HTMLDivElement, MessagesListProps>(
}
} else {
showWarning(
"No initial commit hash found for chat. Need to manually undo code changes"
"No initial commit hash found for chat. Need to manually undo code changes",
);
}
}
@@ -148,27 +148,26 @@ export const MessagesList = forwardRef<HTMLDivElement, MessagesListProps>(
previousAssistantMessage?.commitHash
) {
console.debug(
"Reverting to previous assistant version"
"Reverting to previous assistant version",
);
await revertVersion({
versionId: previousAssistantMessage.commitHash,
});
shouldRedo = false;
} else {
const chat = await IpcClient.getInstance().getChat(
selectedChatId
);
const chat =
await IpcClient.getInstance().getChat(selectedChatId);
if (chat.initialCommitHash) {
console.debug(
"Reverting to initial commit hash",
chat.initialCommitHash
chat.initialCommitHash,
);
await revertVersion({
versionId: chat.initialCommitHash,
});
} else {
showWarning(
"No initial commit hash found for chat. Need to manually undo code changes"
"No initial commit hash found for chat. Need to manually undo code changes",
);
}
}
@@ -212,5 +211,5 @@ export const MessagesList = forwardRef<HTMLDivElement, MessagesListProps>(
<div ref={messagesEndRef} />
</div>
);
}
},
);

View File

@@ -18,7 +18,7 @@ export function VersionPane({ isVisible, onClose }: VersionPaneProps) {
const { versions, loading, refreshVersions, revertVersion } =
useVersions(appId);
const [selectedVersionId, setSelectedVersionId] = useAtom(
selectedVersionIdAtom
selectedVersionIdAtom,
);
useEffect(() => {
// Refresh versions in case the user updated versions outside of the app
@@ -86,20 +86,20 @@ export function VersionPane({ isVisible, onClose }: VersionPaneProps) {
{version.message && (
<p className="mt-1 text-sm">
{version.message.startsWith(
"Reverted all changes back to version "
"Reverted all changes back to version ",
)
? version.message.replace(
/Reverted all changes back to version ([a-f0-9]+)/,
(_, hash) => {
const targetIndex = versions.findIndex(
(v) => v.oid === hash
(v) => v.oid === hash,
);
return targetIndex !== -1
? `Reverted all changes back to version ${
versions.length - targetIndex
}`
: version.message;
}
},
)
: version.message}
</p>
@@ -115,7 +115,7 @@ export function VersionPane({ isVisible, onClose }: VersionPaneProps) {
}}
className={cn(
"invisible mt-1 flex items-center gap-1 px-2 py-0.5 text-sm font-medium bg-(--primary) text-(--primary-foreground) hover:bg-background-lightest rounded-md transition-colors",
selectedVersionId === version.oid && "visible"
selectedVersionId === version.oid && "visible",
)}
aria-label="Undo to latest version"
>

View File

@@ -122,7 +122,7 @@ export const PreviewIframe = ({ loading }: { loading: boolean }) => {
// Load router related files to extract routes
const { content: routerContent } = useLoadAppFile(
selectedAppId,
"src/App.tsx"
"src/App.tsx",
);
// Effect to parse routes from the router file
@@ -287,7 +287,7 @@ export const PreviewIframe = ({ loading }: { loading: boolean }) => {
type: "navigate",
payload: { direction: "backward" },
},
"*"
"*",
);
// Update our local state
@@ -305,14 +305,14 @@ export const PreviewIframe = ({ loading }: { loading: boolean }) => {
type: "navigate",
payload: { direction: "forward" },
},
"*"
"*",
);
// Update our local state
setCurrentHistoryPosition((prev) => prev + 1);
setCanGoBack(true);
setCanGoForward(
currentHistoryPosition + 1 < navigationHistory.length - 1
currentHistoryPosition + 1 < navigationHistory.length - 1,
);
}
};

View File

@@ -200,7 +200,7 @@ export function PreviewPanel() {
if (currentRunningApp !== null) {
console.debug(
"Component unmounting or selectedAppId changing, stopping app",
currentRunningApp
currentRunningApp,
);
stopApp(currentRunningApp);
runningAppIdRef.current = null; // Clear ref on stop

View File

@@ -78,8 +78,8 @@ export function ProviderSettingsPage({ provider }: ProviderSettingsPageProps) {
const activeKeySource = isValidUserKey
? "settings"
: hasEnvKey
? "env"
: "none";
? "env"
: "none";
// --- Accordion Logic ---
const defaultAccordionValue = [];
@@ -202,8 +202,8 @@ export function ProviderSettingsPage({ provider }: ProviderSettingsPageProps) {
{settingsLoading
? "Loading..."
: isConfigured
? "Setup Complete"
: "Not Setup"}
? "Setup Complete"
: "Not Setup"}
</span>
</div>
{!settingsLoading && hasFreeTier && (

View File

@@ -1,13 +1,13 @@
import type * as React from "react"
import * as AccordionPrimitive from "@radix-ui/react-accordion"
import { ChevronDownIcon } from "lucide-react"
import type * as React from "react";
import * as AccordionPrimitive from "@radix-ui/react-accordion";
import { ChevronDownIcon } from "lucide-react";
import { cn } from "@/lib/utils"
import { cn } from "@/lib/utils";
function Accordion({
...props
}: React.ComponentProps<typeof AccordionPrimitive.Root>) {
return <AccordionPrimitive.Root data-slot="accordion" {...props} />
return <AccordionPrimitive.Root data-slot="accordion" {...props} />;
}
function AccordionItem({
@@ -20,7 +20,7 @@ function AccordionItem({
className={cn("border-b last:border-b-0", className)}
{...props}
/>
)
);
}
function AccordionTrigger({
@@ -34,7 +34,7 @@ function AccordionTrigger({
data-slot="accordion-trigger"
className={cn(
"focus-visible:border-ring focus-visible:ring-ring/50 flex flex-1 items-start justify-between gap-4 rounded-md py-4 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&[data-state=open]>svg]:rotate-180",
className
className,
)}
{...props}
>
@@ -42,7 +42,7 @@ function AccordionTrigger({
<ChevronDownIcon className="text-muted-foreground pointer-events-none size-4 shrink-0 translate-y-0.5 transition-transform duration-200" />
</AccordionPrimitive.Trigger>
</AccordionPrimitive.Header>
)
);
}
function AccordionContent({
@@ -58,7 +58,7 @@ function AccordionContent({
>
<div className={cn("pt-0 pb-4", className)}>{children}</div>
</AccordionPrimitive.Content>
)
);
}
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };

View File

@@ -1,7 +1,7 @@
import type * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"
import type * as React from "react";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils"
import { cn } from "@/lib/utils";
const alertVariants = cva(
"relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current",
@@ -16,8 +16,8 @@ const alertVariants = cva(
defaultVariants: {
variant: "default",
},
}
)
},
);
function Alert({
className,
@@ -31,7 +31,7 @@ function Alert({
className={cn(alertVariants({ variant }), className)}
{...props}
/>
)
);
}
function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
@@ -40,11 +40,11 @@ function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
data-slot="alert-title"
className={cn(
"col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight",
className
className,
)}
{...props}
/>
)
);
}
function AlertDescription({
@@ -56,11 +56,11 @@ function AlertDescription({
data-slot="alert-description"
className={cn(
"text-muted-foreground col-start-2 grid justify-items-start gap-1 text-sm [&_p]:leading-relaxed",
className
className,
)}
{...props}
/>
)
);
}
export { Alert, AlertTitle, AlertDescription }
export { Alert, AlertTitle, AlertDescription };

View File

@@ -33,7 +33,7 @@ const buttonVariants = cva(
variant: "default",
size: "default",
},
}
},
);
function Button({

View File

@@ -9,7 +9,7 @@ const Card = React.forwardRef<
ref={ref}
className={cn(
"rounded-lg border bg-card text-card-foreground shadow-sm",
className
className,
)}
{...props}
/>
@@ -36,7 +36,7 @@ const CardTitle = React.forwardRef<
ref={ref}
className={cn(
"text-2xl font-semibold leading-none tracking-tight",
className
className,
)}
{...props}
/>

View File

@@ -1,31 +1,31 @@
import * as React from "react"
import * as DialogPrimitive from "@radix-ui/react-dialog"
import { XIcon } from "lucide-react"
import * as React from "react";
import * as DialogPrimitive from "@radix-ui/react-dialog";
import { XIcon } from "lucide-react";
import { cn } from "@/lib/utils"
import { cn } from "@/lib/utils";
function Dialog({
...props
}: React.ComponentProps<typeof DialogPrimitive.Root>) {
return <DialogPrimitive.Root data-slot="dialog" {...props} />
return <DialogPrimitive.Root data-slot="dialog" {...props} />;
}
function DialogTrigger({
...props
}: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
}
function DialogPortal({
...props
}: React.ComponentProps<typeof DialogPrimitive.Portal>) {
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;
}
function DialogClose({
...props
}: React.ComponentProps<typeof DialogPrimitive.Close>) {
return <DialogPrimitive.Close data-slot="dialog-close" {...props} />
return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
}
function DialogOverlay({
@@ -37,11 +37,11 @@ function DialogOverlay({
data-slot="dialog-overlay"
className={cn(
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
className
className,
)}
{...props}
/>
)
);
}
function DialogContent({
@@ -56,7 +56,7 @@ function DialogContent({
data-slot="dialog-content"
className={cn(
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
className
className,
)}
{...props}
>
@@ -67,7 +67,7 @@ function DialogContent({
</DialogPrimitive.Close>
</DialogPrimitive.Content>
</DialogPortal>
)
);
}
function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
@@ -77,7 +77,7 @@ function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
{...props}
/>
)
);
}
function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
@@ -86,11 +86,11 @@ function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
data-slot="dialog-footer"
className={cn(
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
className
className,
)}
{...props}
/>
)
);
}
function DialogTitle({
@@ -103,7 +103,7 @@ function DialogTitle({
className={cn("text-lg leading-none font-semibold", className)}
{...props}
/>
)
);
}
function DialogDescription({
@@ -116,7 +116,7 @@ function DialogDescription({
className={cn("text-muted-foreground text-sm", className)}
{...props}
/>
)
);
}
export {
@@ -130,4 +130,4 @@ export {
DialogPortal,
DialogTitle,
DialogTrigger,
}
};

View File

@@ -41,7 +41,7 @@ function DropdownMenuContent({
sideOffset={sideOffset}
className={cn(
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",
className
className,
)}
{...props}
/>
@@ -73,7 +73,7 @@ function DropdownMenuItem({
data-variant={variant}
className={cn(
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-1 rounded-sm px-2 py-1 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className
className,
)}
{...props}
/>
@@ -91,7 +91,7 @@ function DropdownMenuCheckboxItem({
data-slot="dropdown-menu-checkbox-item"
className={cn(
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className
className,
)}
checked={checked}
{...props}
@@ -127,7 +127,7 @@ function DropdownMenuRadioItem({
data-slot="dropdown-menu-radio-item"
className={cn(
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className
className,
)}
{...props}
>
@@ -154,7 +154,7 @@ function DropdownMenuLabel({
data-inset={inset}
className={cn(
"px-2 py-1.5 text-sm font-medium data-[inset]:pl-8",
className
className,
)}
{...props}
/>
@@ -183,7 +183,7 @@ function DropdownMenuShortcut({
data-slot="dropdown-menu-shortcut"
className={cn(
"text-muted-foreground ml-auto text-xs tracking-widest",
className
className,
)}
{...props}
/>
@@ -210,7 +210,7 @@ function DropdownMenuSubTrigger({
data-inset={inset}
className={cn(
"focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8",
className
className,
)}
{...props}
>
@@ -229,7 +229,7 @@ function DropdownMenuSubContent({
data-slot="dropdown-menu-sub-content"
className={cn(
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg",
className
className,
)}
{...props}
/>

View File

@@ -1,6 +1,6 @@
import type * as React from "react"
import type * as React from "react";
import { cn } from "@/lib/utils"
import { cn } from "@/lib/utils";
function Input({ className, type, ...props }: React.ComponentProps<"input">) {
return (
@@ -11,11 +11,11 @@ function Input({ className, type, ...props }: React.ComponentProps<"input">) {
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
className
className,
)}
{...props}
/>
)
);
}
export { Input }
export { Input };

View File

@@ -1,7 +1,7 @@
import * as React from "react"
import * as LabelPrimitive from "@radix-ui/react-label"
import * as React from "react";
import * as LabelPrimitive from "@radix-ui/react-label";
import { cn } from "@/lib/utils"
import { cn } from "@/lib/utils";
function Label({
className,
@@ -12,11 +12,11 @@ function Label({
data-slot="label"
className={cn(
"flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
className
className,
)}
{...props}
/>
)
);
}
export { Label }
export { Label };

View File

@@ -18,7 +18,7 @@ const PopoverContent = React.forwardRef<
sideOffset={sideOffset}
className={cn(
"z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className
className,
)}
{...props}
/>

View File

@@ -1,25 +1,25 @@
import * as React from "react"
import * as SelectPrimitive from "@radix-ui/react-select"
import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react"
import * as React from "react";
import * as SelectPrimitive from "@radix-ui/react-select";
import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react";
import { cn } from "@/lib/utils"
import { cn } from "@/lib/utils";
function Select({
...props
}: React.ComponentProps<typeof SelectPrimitive.Root>) {
return <SelectPrimitive.Root data-slot="select" {...props} />
return <SelectPrimitive.Root data-slot="select" {...props} />;
}
function SelectGroup({
...props
}: React.ComponentProps<typeof SelectPrimitive.Group>) {
return <SelectPrimitive.Group data-slot="select-group" {...props} />
return <SelectPrimitive.Group data-slot="select-group" {...props} />;
}
function SelectValue({
...props
}: React.ComponentProps<typeof SelectPrimitive.Value>) {
return <SelectPrimitive.Value data-slot="select-value" {...props} />
return <SelectPrimitive.Value data-slot="select-value" {...props} />;
}
function SelectTrigger({
@@ -28,7 +28,7 @@ function SelectTrigger({
children,
...props
}: React.ComponentProps<typeof SelectPrimitive.Trigger> & {
size?: "sm" | "default"
size?: "sm" | "default";
}) {
return (
<SelectPrimitive.Trigger
@@ -36,7 +36,7 @@ function SelectTrigger({
data-size={size}
className={cn(
"border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className
className,
)}
{...props}
>
@@ -45,7 +45,7 @@ function SelectTrigger({
<ChevronDownIcon className="size-4 opacity-50" />
</SelectPrimitive.Icon>
</SelectPrimitive.Trigger>
)
);
}
function SelectContent({
@@ -62,7 +62,7 @@ function SelectContent({
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md",
position === "popper" &&
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
className
className,
)}
position={position}
{...props}
@@ -72,7 +72,7 @@ function SelectContent({
className={cn(
"p-1",
position === "popper" &&
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1",
)}
>
{children}
@@ -80,7 +80,7 @@ function SelectContent({
<SelectScrollDownButton />
</SelectPrimitive.Content>
</SelectPrimitive.Portal>
)
);
}
function SelectLabel({
@@ -93,7 +93,7 @@ function SelectLabel({
className={cn("text-muted-foreground px-2 py-1.5 text-xs", className)}
{...props}
/>
)
);
}
function SelectItem({
@@ -106,7 +106,7 @@ function SelectItem({
data-slot="select-item"
className={cn(
"focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
className
className,
)}
{...props}
>
@@ -117,7 +117,7 @@ function SelectItem({
</span>
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
</SelectPrimitive.Item>
)
);
}
function SelectSeparator({
@@ -130,7 +130,7 @@ function SelectSeparator({
className={cn("bg-border pointer-events-none -mx-1 my-1 h-px", className)}
{...props}
/>
)
);
}
function SelectScrollUpButton({
@@ -142,13 +142,13 @@ function SelectScrollUpButton({
data-slot="select-scroll-up-button"
className={cn(
"flex cursor-default items-center justify-center py-1",
className
className,
)}
{...props}
>
<ChevronUpIcon className="size-4" />
</SelectPrimitive.ScrollUpButton>
)
);
}
function SelectScrollDownButton({
@@ -160,13 +160,13 @@ function SelectScrollDownButton({
data-slot="select-scroll-down-button"
className={cn(
"flex cursor-default items-center justify-center py-1",
className
className,
)}
{...props}
>
<ChevronDownIcon className="size-4" />
</SelectPrimitive.ScrollDownButton>
)
);
}
export {
@@ -180,4 +180,4 @@ export {
SelectSeparator,
SelectTrigger,
SelectValue,
}
};

View File

@@ -1,9 +1,9 @@
"use client"
"use client";
import type * as React from "react"
import * as SeparatorPrimitive from "@radix-ui/react-separator"
import type * as React from "react";
import * as SeparatorPrimitive from "@radix-ui/react-separator";
import { cn } from "@/lib/utils"
import { cn } from "@/lib/utils";
function Separator({
className,
@@ -18,11 +18,11 @@ function Separator({
orientation={orientation}
className={cn(
"bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px",
className
className,
)}
{...props}
/>
)
);
}
export { Separator }
export { Separator };

View File

@@ -1,29 +1,29 @@
import type * as React from "react"
import * as SheetPrimitive from "@radix-ui/react-dialog"
import { XIcon } from "lucide-react"
import type * as React from "react";
import * as SheetPrimitive from "@radix-ui/react-dialog";
import { XIcon } from "lucide-react";
import { cn } from "@/lib/utils"
import { cn } from "@/lib/utils";
function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive.Root>) {
return <SheetPrimitive.Root data-slot="sheet" {...props} />
return <SheetPrimitive.Root data-slot="sheet" {...props} />;
}
function SheetTrigger({
...props
}: React.ComponentProps<typeof SheetPrimitive.Trigger>) {
return <SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} />
return <SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} />;
}
function SheetClose({
...props
}: React.ComponentProps<typeof SheetPrimitive.Close>) {
return <SheetPrimitive.Close data-slot="sheet-close" {...props} />
return <SheetPrimitive.Close data-slot="sheet-close" {...props} />;
}
function SheetPortal({
...props
}: React.ComponentProps<typeof SheetPrimitive.Portal>) {
return <SheetPrimitive.Portal data-slot="sheet-portal" {...props} />
return <SheetPrimitive.Portal data-slot="sheet-portal" {...props} />;
}
function SheetOverlay({
@@ -35,11 +35,11 @@ function SheetOverlay({
data-slot="sheet-overlay"
className={cn(
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
className
className,
)}
{...props}
/>
)
);
}
function SheetContent({
@@ -48,7 +48,7 @@ function SheetContent({
side = "right",
...props
}: React.ComponentProps<typeof SheetPrimitive.Content> & {
side?: "top" | "right" | "bottom" | "left"
side?: "top" | "right" | "bottom" | "left";
}) {
return (
<SheetPortal>
@@ -65,7 +65,7 @@ function SheetContent({
"data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 h-auto border-b",
side === "bottom" &&
"data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 h-auto border-t",
className
className,
)}
{...props}
>
@@ -76,7 +76,7 @@ function SheetContent({
</SheetPrimitive.Close>
</SheetPrimitive.Content>
</SheetPortal>
)
);
}
function SheetHeader({ className, ...props }: React.ComponentProps<"div">) {
@@ -86,7 +86,7 @@ function SheetHeader({ className, ...props }: React.ComponentProps<"div">) {
className={cn("flex flex-col gap-1.5 p-4", className)}
{...props}
/>
)
);
}
function SheetFooter({ className, ...props }: React.ComponentProps<"div">) {
@@ -96,7 +96,7 @@ function SheetFooter({ className, ...props }: React.ComponentProps<"div">) {
className={cn("mt-auto flex flex-col gap-2 p-4", className)}
{...props}
/>
)
);
}
function SheetTitle({
@@ -109,7 +109,7 @@ function SheetTitle({
className={cn("text-foreground font-semibold", className)}
{...props}
/>
)
);
}
function SheetDescription({
@@ -122,7 +122,7 @@ function SheetDescription({
className={cn("text-muted-foreground text-sm", className)}
{...props}
/>
)
);
}
export {
@@ -134,4 +134,4 @@ export {
SheetFooter,
SheetTitle,
SheetDescription,
}
};

View File

@@ -67,7 +67,7 @@ function SidebarProvider({
// This sets the cookie to keep the sidebar state.
document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`;
},
[setOpenProp, open]
[setOpenProp, open],
);
// Helper to toggle the sidebar.
@@ -117,7 +117,7 @@ function SidebarProvider({
setOpen,
toggleSidebar,
}),
[state, open, setOpen, toggleSidebar]
[state, open, setOpen, toggleSidebar],
);
return (
@@ -135,7 +135,7 @@ function SidebarProvider({
className={cn(
"bg-sidebar",
"group/sidebar-wrapper has-data-[variant=inset]:bg-sidebar flex min-h-svh w-full",
className
className,
)}
{...props}
>
@@ -166,7 +166,7 @@ function Sidebar({
data-slot="sidebar"
className={cn(
"bg-sidebar text-sidebar-foreground flex h-full w-(--sidebar-width) flex-col",
className
className,
)}
{...props}
>
@@ -193,7 +193,7 @@ function Sidebar({
"group-data-[side=right]:rotate-180",
variant === "floating" || variant === "inset"
? "group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]"
: "group-data-[collapsible=icon]:w-(--sidebar-width-icon)"
: "group-data-[collapsible=icon]:w-(--sidebar-width-icon)",
)}
/>
<div
@@ -207,7 +207,7 @@ function Sidebar({
variant === "floating" || variant === "inset"
? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]"
: "group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l border-sidebar-border",
className
className,
)}
{...props}
>
@@ -274,7 +274,7 @@ function SidebarRail({ className, ...props }: React.ComponentProps<"button">) {
"hover:group-data-[collapsible=offcanvas]:bg-sidebar group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full",
"[[data-side=left][data-collapsible=offcanvas]_&]:-right-2",
"[[data-side=right][data-collapsible=offcanvas]_&]:-left-2",
className
className,
)}
{...props}
/>
@@ -288,7 +288,7 @@ function SidebarInset({ className, ...props }: React.ComponentProps<"main">) {
className={cn(
"bg-background relative flex w-full flex-1 flex-col",
"md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow-sm md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2",
className
className,
)}
{...props}
/>
@@ -352,7 +352,7 @@ function SidebarContent({ className, ...props }: React.ComponentProps<"div">) {
data-sidebar="content"
className={cn(
"flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden",
className
className,
)}
{...props}
/>
@@ -384,7 +384,7 @@ function SidebarGroupLabel({
className={cn(
"text-sidebar-foreground/70 ring-sidebar-ring flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium outline-hidden transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
"group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0",
className
className,
)}
{...props}
/>
@@ -407,7 +407,7 @@ function SidebarGroupAction({
// Increases the hit area of the button on mobile.
"after:absolute after:-inset-2 md:after:hidden",
"group-data-[collapsible=icon]:hidden",
className
className,
)}
{...props}
/>
@@ -474,7 +474,7 @@ const sidebarMenuButtonVariants = cva(
variant: "default",
size: "default",
},
}
},
);
function SidebarMenuButton({
@@ -552,7 +552,7 @@ function SidebarMenuAction({
"group-data-[collapsible=icon]:hidden",
showOnHover &&
"peer-data-[active=true]/menu-button:text-sidebar-accent-foreground group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 md:opacity-0",
className
className,
)}
{...props}
/>
@@ -574,7 +574,7 @@ function SidebarMenuBadge({
"peer-data-[size=default]/menu-button:top-1.5",
"peer-data-[size=lg]/menu-button:top-2.5",
"group-data-[collapsible=icon]:hidden",
className
className,
)}
{...props}
/>
@@ -627,7 +627,7 @@ function SidebarMenuSub({ className, ...props }: React.ComponentProps<"ul">) {
className={cn(
"border-sidebar-border mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-l px-2.5 py-0.5",
"group-data-[collapsible=icon]:hidden",
className
className,
)}
{...props}
/>
@@ -673,7 +673,7 @@ function SidebarMenuSubButton({
size === "sm" && "text-xs",
size === "md" && "text-sm",
"group-data-[collapsible=icon]:hidden",
className
className,
)}
{...props}
/>

View File

@@ -1,4 +1,4 @@
import { cn } from "@/lib/utils"
import { cn } from "@/lib/utils";
function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
return (
@@ -7,7 +7,7 @@ function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
className={cn("bg-accent animate-pulse rounded-md", className)}
{...props}
/>
)
);
}
export { Skeleton }
export { Skeleton };

View File

@@ -1,7 +1,7 @@
import * as React from "react"
import * as SwitchPrimitive from "@radix-ui/react-switch"
import * as React from "react";
import * as SwitchPrimitive from "@radix-ui/react-switch";
import { cn } from "@/lib/utils"
import { cn } from "@/lib/utils";
function Switch({
className,
@@ -12,18 +12,18 @@ function Switch({
data-slot="switch"
className={cn(
"peer data-[state=checked]:bg-primary data-[state=unchecked]:bg-input focus-visible:border-ring focus-visible:ring-ring/50 dark:data-[state=unchecked]:bg-input/80 inline-flex h-[1.15rem] w-8 shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
className
className,
)}
{...props}
>
<SwitchPrimitive.Thumb
data-slot="switch-thumb"
className={cn(
"bg-background dark:data-[state=unchecked]:bg-foreground dark:data-[state=checked]:bg-primary-foreground pointer-events-none block size-4 rounded-full ring-0 transition-transform data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0"
"bg-background dark:data-[state=unchecked]:bg-foreground dark:data-[state=checked]:bg-primary-foreground pointer-events-none block size-4 rounded-full ring-0 transition-transform data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0",
)}
/>
</SwitchPrimitive.Root>
)
);
}
export { Switch }
export { Switch };

View File

@@ -1,16 +1,16 @@
import * as React from "react"
import * as ToggleGroupPrimitive from "@radix-ui/react-toggle-group"
import { type VariantProps } from "class-variance-authority"
import * as React from "react";
import * as ToggleGroupPrimitive from "@radix-ui/react-toggle-group";
import { type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils"
import { toggleVariants } from "@/components/ui/toggle"
import { cn } from "@/lib/utils";
import { toggleVariants } from "@/components/ui/toggle";
const ToggleGroupContext = React.createContext<
VariantProps<typeof toggleVariants>
>({
size: "default",
variant: "default",
})
});
function ToggleGroup({
className,
@@ -27,7 +27,7 @@ function ToggleGroup({
data-size={size}
className={cn(
"group/toggle-group flex w-fit items-center rounded-md data-[variant=outline]:shadow-xs",
className
className,
)}
{...props}
>
@@ -35,7 +35,7 @@ function ToggleGroup({
{children}
</ToggleGroupContext.Provider>
</ToggleGroupPrimitive.Root>
)
);
}
function ToggleGroupItem({
@@ -46,7 +46,7 @@ function ToggleGroupItem({
...props
}: React.ComponentProps<typeof ToggleGroupPrimitive.Item> &
VariantProps<typeof toggleVariants>) {
const context = React.useContext(ToggleGroupContext)
const context = React.useContext(ToggleGroupContext);
return (
<ToggleGroupPrimitive.Item
@@ -59,13 +59,13 @@ function ToggleGroupItem({
size: context.size || size,
}),
"min-w-0 flex-1 shrink-0 rounded-none shadow-none first:rounded-l-md last:rounded-r-md focus:z-10 focus-visible:z-10 data-[variant=outline]:border-l-0 data-[variant=outline]:first:border-l",
className
className,
)}
{...props}
>
{children}
</ToggleGroupPrimitive.Item>
)
);
}
export { ToggleGroup, ToggleGroupItem }
export { ToggleGroup, ToggleGroupItem };

View File

@@ -1,10 +1,10 @@
"use client"
"use client";
import * as React from "react"
import * as TogglePrimitive from "@radix-ui/react-toggle"
import { cva, type VariantProps } from "class-variance-authority"
import * as React from "react";
import * as TogglePrimitive from "@radix-ui/react-toggle";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils"
import { cn } from "@/lib/utils";
const toggleVariants = cva(
"inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium hover:bg-muted hover:text-muted-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] outline-none transition-[color,box-shadow] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive whitespace-nowrap",
@@ -25,8 +25,8 @@ const toggleVariants = cva(
variant: "default",
size: "default",
},
}
)
},
);
function Toggle({
className,
@@ -41,7 +41,7 @@ function Toggle({
className={cn(toggleVariants({ variant, size, className }))}
{...props}
/>
)
);
}
export { Toggle, toggleVariants }
export { Toggle, toggleVariants };

View File

@@ -1,9 +1,9 @@
"use client"
"use client";
import type * as React from "react"
import * as TooltipPrimitive from "@radix-ui/react-tooltip"
import type * as React from "react";
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
import { cn } from "@/lib/utils"
import { cn } from "@/lib/utils";
function TooltipProvider({
delayDuration = 0,
@@ -15,7 +15,7 @@ function TooltipProvider({
delayDuration={delayDuration}
{...props}
/>
)
);
}
function Tooltip({
@@ -25,13 +25,13 @@ function Tooltip({
<TooltipProvider>
<TooltipPrimitive.Root data-slot="tooltip" {...props} />
</TooltipProvider>
)
);
}
function TooltipTrigger({
...props
}: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />
return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />;
}
function TooltipContent({
@@ -47,7 +47,7 @@ function TooltipContent({
sideOffset={sideOffset}
className={cn(
"bg-primary text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance",
className
className,
)}
{...props}
>
@@ -55,7 +55,7 @@ function TooltipContent({
<TooltipPrimitive.Arrow className="bg-primary fill-primary z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" />
</TooltipPrimitive.Content>
</TooltipPrimitive.Portal>
)
);
}
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };

View File

@@ -135,7 +135,7 @@ export const PROVIDER_TO_ENV_VAR: Record<string, string> = {
};
export const ALLOWED_ENV_VARS = Object.keys(PROVIDER_TO_ENV_VAR).map(
(provider) => PROVIDER_TO_ENV_VAR[provider]
(provider) => PROVIDER_TO_ENV_VAR[provider],
);
export const AUTO_MODELS = [

View File

@@ -61,7 +61,7 @@ export function useTheme() {
const darkModeQuery = window.matchMedia("(prefers-color-scheme: dark)");
const updateTheme = () => {
setIsDarkMode(
theme === "dark" || (theme === "system" && darkModeQuery.matches)
theme === "dark" || (theme === "system" && darkModeQuery.matches),
);
};

View File

@@ -91,7 +91,7 @@ export const db = _db as any as BetterSQLite3Database<typeof schema> & {
export async function updateAppGithubRepo(
appId: number,
org: string,
repo: string
repo: string,
): Promise<void> {
await db
.update(schema.apps)

View File

@@ -1,5 +1,3 @@
export function useIsMobile() {
// Always return false to force desktop behavior
return false;

View File

@@ -31,7 +31,7 @@ export function useCountTokens() {
setLoading(false);
}
},
[setLoading, setError, setResult]
[setLoading, setError, setResult],
);
return {

View File

@@ -24,7 +24,7 @@ export function useLoadAppFile(appId: number | null, filePath: string | null) {
} catch (error) {
console.error(
`Error loading file ${filePath} for app ${appId}:`,
error
error,
);
setError(error instanceof Error ? error : new Error(String(error)));
setContent(null);
@@ -50,7 +50,7 @@ export function useLoadAppFile(appId: number | null, filePath: string | null) {
} catch (error) {
console.error(
`Error refreshing file ${filePath} for app ${appId}:`,
error
error,
);
setError(error instanceof Error ? error : new Error(String(error)));
} finally {

View File

@@ -21,7 +21,7 @@ export function useProposal(chatId?: number | undefined) {
try {
// Type assertion might be needed depending on how IpcClient is typed
const result = (await IpcClient.getInstance().getProposal(
chatId
chatId,
)) as ProposalResult | null;
if (result) {
@@ -37,7 +37,7 @@ export function useProposal(chatId?: number | undefined) {
setIsLoading(false);
}
},
[chatId] // Only depend on chatId, setProposalResult is stable
[chatId], // Only depend on chatId, setProposalResult is stable
); // Depend on chatId
useEffect(() => {

View File

@@ -52,7 +52,7 @@ export function useRunApp() {
} catch (error) {
console.error(`Error running app ${appId}:`, error);
setPreviewErrorMessage(
error instanceof Error ? error.message : error?.toString()
error instanceof Error ? error.message : error?.toString(),
);
} finally {
setLoading(false);
@@ -73,7 +73,7 @@ export function useRunApp() {
} catch (error) {
console.error(`Error stopping app ${appId}:`, error);
setPreviewErrorMessage(
error instanceof Error ? error.message : error?.toString()
error instanceof Error ? error.message : error?.toString(),
);
} finally {
setLoading(false);
@@ -97,7 +97,7 @@ export function useRunApp() {
console.debug(
"Restarting app",
appId,
removeNodeModules ? "with node_modules cleanup" : ""
removeNodeModules ? "with node_modules cleanup" : "",
);
// Clear the URL and add restart message
@@ -127,25 +127,25 @@ export function useRunApp() {
}
// Check if the output contains a localhost URL
const urlMatch = output.message.match(
/(https?:\/\/localhost:\d+\/?)/
/(https?:\/\/localhost:\d+\/?)/,
);
if (urlMatch) {
setAppUrlObj({ appUrl: urlMatch[1], appId });
}
},
removeNodeModules
removeNodeModules,
);
} catch (error) {
console.error(`Error restarting app ${appId}:`, error);
setPreviewErrorMessage(
error instanceof Error ? error.message : error?.toString()
error instanceof Error ? error.message : error?.toString(),
);
} finally {
setPreviewPanelKey((prevKey) => prevKey + 1);
setLoading(false);
}
},
[appId, setApp, setAppOutput, setAppUrlObj, setPreviewPanelKey]
[appId, setApp, setAppOutput, setAppUrlObj, setPreviewPanelKey],
);
const refreshAppIframe = useCallback(async () => {

View File

@@ -119,7 +119,7 @@ function processSettingsForTelemetry(settings: UserSettings) {
if (settings.telemetryConsent) {
window.localStorage.setItem(
TELEMETRY_CONSENT_KEY,
settings.telemetryConsent
settings.telemetryConsent,
);
} else {
window.localStorage.removeItem(TELEMETRY_CONSENT_KEY);
@@ -127,7 +127,7 @@ function processSettingsForTelemetry(settings: UserSettings) {
if (settings.telemetryUserId) {
window.localStorage.setItem(
TELEMETRY_USER_ID_KEY,
settings.telemetryUserId
settings.telemetryUserId,
);
} else {
window.localStorage.removeItem(TELEMETRY_USER_ID_KEY);

View File

@@ -117,7 +117,7 @@ export function useStreamChat({
setError(error instanceof Error ? error.message : String(error));
}
},
[setMessages, setIsStreaming, setIsPreviewOpen]
[setMessages, setIsStreaming, setIsPreviewOpen],
);
return {

View File

@@ -13,7 +13,7 @@ export function useSupabase() {
const [loading, setLoading] = useAtom(supabaseLoadingAtom);
const [error, setError] = useAtom(supabaseErrorAtom);
const [selectedProject, setSelectedProject] = useAtom(
selectedSupabaseProjectAtom
selectedSupabaseProjectAtom,
);
const ipcClient = IpcClient.getInstance();
@@ -58,7 +58,7 @@ export function useSupabase() {
setLoading(false);
}
},
[ipcClient, setError, setLoading]
[ipcClient, setError, setLoading],
);
/**
@@ -84,7 +84,7 @@ export function useSupabase() {
setLoading(false);
}
},
[ipcClient, setError, setLoading]
[ipcClient, setError, setLoading],
);
/**
@@ -94,7 +94,7 @@ export function useSupabase() {
(projectId: string | null) => {
setSelectedProject(projectId);
},
[setSelectedProject]
[setSelectedProject],
);
return {

View File

@@ -71,7 +71,7 @@ export function useVersions(appId: number | null) {
showError(error);
}
},
[appId, setVersions, setError, selectedChatId, setMessages]
[appId, setVersions, setError, selectedChatId, setMessages],
);
return { versions, loading, error, refreshVersions, revertVersion };

View File

@@ -74,7 +74,7 @@ async function executeAppLocalNode({
shell: true,
stdio: "pipe", // Ensure stdio is piped so we can capture output/errors and detect close
detached: false, // Ensure child process is attached to the main process lifecycle unless explicitly backgrounded
}
},
);
// Check if process spawned correctly
@@ -86,7 +86,7 @@ async function executeAppLocalNode({
throw new Error(
`Failed to spawn process for app ${appId}. Error: ${
errorOutput || "Unknown spawn error"
}`
}`,
);
}
@@ -118,7 +118,7 @@ async function executeAppLocalNode({
// Handle process exit/close
process.on("close", (code, signal) => {
logger.log(
`App ${appId} (PID: ${process.pid}) process closed with code ${code}, signal ${signal}.`
`App ${appId} (PID: ${process.pid}) process closed with code ${code}, signal ${signal}.`,
);
removeAppIfCurrentProcess(appId, process);
});
@@ -126,7 +126,7 @@ async function executeAppLocalNode({
// Handle errors during process lifecycle (e.g., command not found)
process.on("error", (err) => {
logger.error(
`Error in app ${appId} (PID: ${process.pid}) process: ${err.message}`
`Error in app ${appId} (PID: ${process.pid}) process: ${err.message}`,
);
removeAppIfCurrentProcess(appId, process);
// Note: We don't throw here as the error is asynchronous. The caller got a success response already.
@@ -173,7 +173,7 @@ export function registerAppHandlers() {
// Copy scaffold asynchronously
await copyDirectoryRecursive(
path.join(__dirname, "..", "..", "scaffold"),
fullAppPath
fullAppPath,
);
// Initialize git repo and create first commit
await git.init({
@@ -285,7 +285,7 @@ export function registerAppHandlers() {
logger.error(`Error reading file ${filePath} for app ${appId}:`, error);
throw new Error("Failed to read file");
}
}
},
);
ipcMain.handle("get-env-vars", async () => {
@@ -300,7 +300,7 @@ export function registerAppHandlers() {
"run-app",
async (
event: Electron.IpcMainInvokeEvent,
{ appId }: { appId: number }
{ appId }: { appId: number },
) => {
return withLock(appId, async () => {
// Check if app is already running
@@ -337,19 +337,19 @@ export function registerAppHandlers() {
throw new Error(`Failed to run app ${appId}: ${error.message}`);
}
});
}
},
);
ipcMain.handle("stop-app", async (_, { appId }: { appId: number }) => {
logger.log(
`Attempting to stop app ${appId}. Current running apps: ${runningApps.size}`
`Attempting to stop app ${appId}. Current running apps: ${runningApps.size}`,
);
return withLock(appId, async () => {
const appInfo = runningApps.get(appId);
if (!appInfo) {
logger.log(
`App ${appId} not found in running apps map. Assuming already stopped.`
`App ${appId} not found in running apps map. Assuming already stopped.`,
);
return {
success: true,
@@ -359,13 +359,13 @@ export function registerAppHandlers() {
const { process, processId } = appInfo;
logger.log(
`Found running app ${appId} with processId ${processId} (PID: ${process.pid}). Attempting to stop.`
`Found running app ${appId} with processId ${processId} (PID: ${process.pid}). Attempting to stop.`,
);
// Check if the process is already exited or closed
if (process.exitCode !== null || process.signalCode !== null) {
logger.log(
`Process for app ${appId} (PID: ${process.pid}) already exited (code: ${process.exitCode}, signal: ${process.signalCode}). Cleaning up map.`
`Process for app ${appId} (PID: ${process.pid}) already exited (code: ${process.exitCode}, signal: ${process.signalCode}). Cleaning up map.`,
);
runningApps.delete(appId); // Ensure cleanup if somehow missed
return { success: true, message: "Process already exited." };
@@ -382,7 +382,7 @@ export function registerAppHandlers() {
} catch (error: any) {
logger.error(
`Error stopping app ${appId} (PID: ${process.pid}, processId: ${processId}):`,
error
error,
);
// Attempt cleanup even if an error occurred during the stop process
removeAppIfCurrentProcess(appId, process);
@@ -398,7 +398,7 @@ export function registerAppHandlers() {
{
appId,
removeNodeModules,
}: { appId: number; removeNodeModules?: boolean }
}: { appId: number; removeNodeModules?: boolean },
) => {
logger.log(`Restarting app ${appId}`);
return withLock(appId, async () => {
@@ -408,7 +408,7 @@ export function registerAppHandlers() {
if (appInfo) {
const { process, processId } = appInfo;
logger.log(
`Stopping app ${appId} (processId ${processId}) before restart`
`Stopping app ${appId} (processId ${processId}) before restart`,
);
await killProcess(process);
@@ -435,7 +435,7 @@ export function registerAppHandlers() {
if (removeNodeModules) {
const nodeModulesPath = path.join(appPath, "node_modules");
logger.log(
`Removing node_modules for app ${appId} at ${nodeModulesPath}`
`Removing node_modules for app ${appId} at ${nodeModulesPath}`,
);
if (fs.existsSync(nodeModulesPath)) {
await fsPromises.rm(nodeModulesPath, {
@@ -449,7 +449,7 @@ export function registerAppHandlers() {
}
logger.debug(
`Executing app ${appId} in path ${app.path} after restart request`
`Executing app ${appId} in path ${app.path} after restart request`,
); // Adjusted log
await executeApp({ appPath, appId, event }); // This will handle starting either mode
@@ -460,7 +460,7 @@ export function registerAppHandlers() {
throw error;
}
});
}
},
);
ipcMain.handle(
@@ -471,7 +471,7 @@ export function registerAppHandlers() {
appId,
filePath,
content,
}: { appId: number; filePath: string; content: string }
}: { appId: number; filePath: string; content: string },
) => {
const app = await db.query.apps.findFirst({
where: eq(apps.id, appId),
@@ -517,7 +517,7 @@ export function registerAppHandlers() {
logger.error(`Error writing file ${filePath} for app ${appId}:`, error);
throw new Error(`Failed to write file: ${error.message}`);
}
}
},
);
ipcMain.handle("delete-app", async (_, { appId }: { appId: number }) => {
@@ -575,7 +575,7 @@ export function registerAppHandlers() {
appId,
appName,
appPath,
}: { appId: number; appName: string; appPath: string }
}: { appId: number; appName: string; appPath: string },
) => {
return withLock(appId, async () => {
// Check if app exists
@@ -613,7 +613,7 @@ export function registerAppHandlers() {
} catch (error: any) {
logger.error(`Error stopping app ${appId} before renaming:`, error);
throw new Error(
`Failed to stop app before renaming: ${error.message}`
`Failed to stop app before renaming: ${error.message}`,
);
}
}
@@ -627,7 +627,7 @@ export function registerAppHandlers() {
// Check if destination directory already exists
if (fs.existsSync(newAppPath)) {
throw new Error(
`Destination path '${newAppPath}' already exists`
`Destination path '${newAppPath}' already exists`,
);
}
@@ -641,7 +641,7 @@ export function registerAppHandlers() {
} catch (error: any) {
logger.error(
`Error moving app files from ${oldAppPath} to ${newAppPath}:`,
error
error,
);
throw new Error(`Failed to move app files: ${error.message}`);
}
@@ -667,7 +667,7 @@ export function registerAppHandlers() {
} catch (rollbackError) {
logger.error(
`Failed to rollback file move during rename error:`,
rollbackError
rollbackError,
);
}
}
@@ -676,7 +676,7 @@ export function registerAppHandlers() {
throw new Error(`Failed to update app in database: ${error.message}`);
}
});
}
},
);
ipcMain.handle("reset-all", async () => {

View File

@@ -52,7 +52,7 @@ export function registerChatHandlers() {
"for app:",
appId,
"with initial commit hash:",
initialCommitHash
initialCommitHash,
);
return chat.id;
});
@@ -101,7 +101,7 @@ export function registerChatHandlers() {
const allChats = await query;
return allChats;
}
},
);
ipcMain.handle("delete-chat", async (_, chatId: number) => {

View File

@@ -124,7 +124,7 @@ export function registerChatStreamHandlers() {
await db
.delete(messages)
.where(
eq(messages.id, chatMessages[lastUserMessageIndex + 1].id)
eq(messages.id, chatMessages[lastUserMessageIndex + 1].id),
);
}
}
@@ -220,7 +220,7 @@ export function registerChatStreamHandlers() {
req.chatId,
testResponse,
abortController,
updatedChat
updatedChat,
);
} else {
// Normal AI processing for non-test prompts
@@ -242,7 +242,7 @@ export function registerChatStreamHandlers() {
"codebaseInfo: length",
codebaseInfo.length,
"estimated tokens",
codebaseInfo.length / 4
codebaseInfo.length / 4,
);
// Prepare message history for the AI
@@ -266,7 +266,7 @@ export function registerChatStreamHandlers() {
systemPrompt += "\n\n" + SUPABASE_NOT_AVAILABLE_SYSTEM_PROMPT;
}
const isSummarizeIntent = req.prompt.startsWith(
"Summarize from chat-id="
"Summarize from chat-id=",
);
if (isSummarizeIntent) {
systemPrompt = SUMMARIZE_CHAT_SYSTEM_PROMPT;
@@ -276,7 +276,7 @@ export function registerChatStreamHandlers() {
const hasImageAttachments =
req.attachments &&
req.attachments.some((attachment) =>
attachment.type.startsWith("image/")
attachment.type.startsWith("image/"),
);
if (hasImageAttachments) {
@@ -316,7 +316,7 @@ This conversation includes one or more image attachments. When the user uploads
// Replace the last message with one that includes attachments
chatMessages[lastUserIndex] = await prepareMessageWithAttachments(
lastUserMessage,
attachmentPaths
attachmentPaths,
);
}
}
@@ -353,7 +353,7 @@ This conversation includes one or more image attachments. When the user uploads
(error as any)?.error?.message || JSON.stringify(error);
event.sender.send(
"chat:response:error",
`Sorry, there was an error from the AI: ${message}`
`Sorry, there was an error from the AI: ${message}`,
);
// Clean up the abort controller
activeStreams.delete(req.chatId);
@@ -374,7 +374,7 @@ This conversation includes one or more image attachments. When the user uploads
});
fullResponse = fullResponse.replace(
"$$SUPABASE_CLIENT_CODE$$",
supabaseClientCode
supabaseClientCode,
);
}
// Store the current partial response
@@ -421,13 +421,13 @@ This conversation includes one or more image attachments. When the user uploads
.where(eq(messages.id, placeholderAssistantMessage.id));
logger.log(
`Updated cancelled response for placeholder message ${placeholderAssistantMessage.id} in chat ${chatId}`
`Updated cancelled response for placeholder message ${placeholderAssistantMessage.id} in chat ${chatId}`,
);
partialResponses.delete(req.chatId);
} catch (error) {
logger.error(
`Error saving partial response for chat ${chatId}:`,
error
error,
);
}
}
@@ -441,7 +441,7 @@ This conversation includes one or more image attachments. When the user uploads
if (!abortController.signal.aborted && fullResponse) {
// Scrape from: <dyad-chat-summary>Renaming profile file</dyad-chat-title>
const chatTitle = fullResponse.match(
/<dyad-chat-summary>(.*?)<\/dyad-chat-summary>/
/<dyad-chat-summary>(.*?)<\/dyad-chat-summary>/,
);
if (chatTitle) {
await db
@@ -461,7 +461,7 @@ This conversation includes one or more image attachments. When the user uploads
const status = await processFullResponseActions(
fullResponse,
req.chatId,
{ chatSummary, messageId: placeholderAssistantMessage.id } // Use placeholder ID
{ chatSummary, messageId: placeholderAssistantMessage.id }, // Use placeholder ID
);
const chat = await db.query.chats.findFirst({
@@ -481,7 +481,7 @@ This conversation includes one or more image attachments. When the user uploads
if (status.error) {
event.sender.send(
"chat:response:error",
`Sorry, there was an error applying the AI's changes: ${status.error}`
`Sorry, there was an error applying the AI's changes: ${status.error}`,
);
}
@@ -505,12 +505,15 @@ This conversation includes one or more image attachments. When the user uploads
try {
// We don't immediately delete files because they might be needed for reference
// Instead, schedule them for deletion after some time
setTimeout(async () => {
if (fs.existsSync(filePath)) {
await unlink(filePath);
logger.log(`Deleted temporary file: ${filePath}`);
}
}, 30 * 60 * 1000); // Delete after 30 minutes
setTimeout(
async () => {
if (fs.existsSync(filePath)) {
await unlink(filePath);
logger.log(`Deleted temporary file: ${filePath}`);
}
},
30 * 60 * 1000,
); // Delete after 30 minutes
} catch (error) {
logger.error(`Error scheduling file deletion: ${error}`);
}
@@ -523,7 +526,7 @@ This conversation includes one or more image attachments. When the user uploads
logger.error("Error calling LLM:", error);
event.sender.send(
"chat:response:error",
`Sorry, there was an error processing your request: ${error}`
`Sorry, there was an error processing your request: ${error}`,
);
// Clean up the abort controller
activeStreams.delete(req.chatId);
@@ -555,7 +558,7 @@ This conversation includes one or more image attachments. When the user uploads
}
export function formatMessages(
messages: { role: string; content: string | undefined }[]
messages: { role: string; content: string | undefined }[],
) {
return messages
.map((m) => `<message role="${m.role}">${m.content}</message>`)
@@ -566,7 +569,7 @@ export function formatMessages(
async function replaceTextAttachmentWithContent(
text: string,
filePath: string,
fileName: string
fileName: string,
): Promise<string> {
try {
if (await isTextFile(filePath)) {
@@ -577,16 +580,16 @@ async function replaceTextAttachmentWithContent(
const escapedPath = filePath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
const tagPattern = new RegExp(
`<dyad-text-attachment filename="[^"]*" type="[^"]*" path="${escapedPath}">\\s*<\\/dyad-text-attachment>`,
"g"
"g",
);
const replacedText = text.replace(
tagPattern,
`Full content of ${fileName}:\n\`\`\`\n${fullContent}\n\`\`\``
`Full content of ${fileName}:\n\`\`\`\n${fullContent}\n\`\`\``,
);
logger.log(
`Replaced text attachment content for: ${fileName} - length before: ${text.length} - length after: ${replacedText.length}`
`Replaced text attachment content for: ${fileName} - length before: ${text.length} - length after: ${replacedText.length}`,
);
return replacedText;
}
@@ -600,13 +603,13 @@ async function replaceTextAttachmentWithContent(
// Helper function to convert traditional message to one with proper image attachments
async function prepareMessageWithAttachments(
message: CoreMessage,
attachmentPaths: string[]
attachmentPaths: string[],
): Promise<CoreMessage> {
let textContent = message.content;
// Get the original text content
if (typeof textContent !== "string") {
logger.warn(
"Message content is not a string - shouldn't happen but using message as-is"
"Message content is not a string - shouldn't happen but using message as-is",
);
return message;
}
@@ -617,7 +620,7 @@ async function prepareMessageWithAttachments(
textContent = await replaceTextAttachmentWithContent(
textContent,
filePath,
fileName
fileName,
);
}

View File

@@ -91,7 +91,7 @@ export function registerDebugHandlers() {
async (): Promise<SystemDebugInfo> => {
console.log("IPC: get-system-debug-info called");
return getSystemDebugInfo();
}
},
);
ipcMain.handle(
@@ -151,7 +151,7 @@ export function registerDebugHandlers() {
console.error(`Error in get-chat-logs:`, error);
throw error;
}
}
},
);
console.log("Registered debug IPC handlers");

View File

@@ -10,7 +10,7 @@ export function registerDependencyHandlers() {
"chat:add-dep",
async (
_event,
{ chatId, packages }: { chatId: number; packages: string[] }
{ chatId, packages }: { chatId: number; packages: string[] },
) => {
// Find the message from the database
const foundMessages = await db.query.messages.findMany({
@@ -39,13 +39,13 @@ export function registerDependencyHandlers() {
.reverse()
.find((m) =>
m.content.includes(
`<dyad-add-dependency packages="${packages.join(" ")}">`
)
`<dyad-add-dependency packages="${packages.join(" ")}">`,
),
);
if (!message) {
throw new Error(
`Message with packages ${packages.join(", ")} not found`
`Message with packages ${packages.join(", ")} not found`,
);
}
@@ -54,6 +54,6 @@ export function registerDependencyHandlers() {
message,
appPath: getDyadAppPath(app.path),
});
}
},
);
}

View File

@@ -131,7 +131,7 @@ async function pollForAccessToken(event: IpcMainInvokeEvent) {
// Schedule next poll
currentFlowState.timeoutId = setTimeout(
() => pollForAccessToken(event),
interval * 1000
interval * 1000,
);
break;
case "slow_down":
@@ -143,7 +143,7 @@ async function pollForAccessToken(event: IpcMainInvokeEvent) {
});
currentFlowState.timeoutId = setTimeout(
() => pollForAccessToken(event),
newInterval * 1000
newInterval * 1000,
);
break;
case "expired_token":
@@ -162,7 +162,7 @@ async function pollForAccessToken(event: IpcMainInvokeEvent) {
break;
default:
logger.error(
`Unknown GitHub error: ${data.error_description || data.error}`
`Unknown GitHub error: ${data.error_description || data.error}`,
);
event.sender.send("github:flow-error", {
error: `GitHub authorization error: ${
@@ -202,7 +202,7 @@ function stopPolling() {
function handleStartGithubFlow(
event: IpcMainInvokeEvent,
args: { appId: number | null }
args: { appId: number | null },
) {
logger.debug(`Received github:start-flow for appId: ${args.appId}`);
@@ -249,7 +249,7 @@ function handleStartGithubFlow(
if (!res.ok) {
return res.json().then((errData) => {
throw new Error(
`GitHub API Error: ${errData.error_description || res.statusText}`
`GitHub API Error: ${errData.error_description || res.statusText}`,
);
});
}
@@ -273,7 +273,7 @@ function handleStartGithubFlow(
// Start polling after the initial interval
currentFlowState.timeoutId = setTimeout(
() => pollForAccessToken(event),
currentFlowState.interval * 1000
currentFlowState.interval * 1000,
);
})
.catch((error) => {
@@ -289,7 +289,7 @@ function handleStartGithubFlow(
// --- GitHub Repo Availability Handler ---
async function handleIsRepoAvailable(
event: IpcMainInvokeEvent,
{ org, repo }: { org: string; repo: string }
{ org, repo }: { org: string; repo: string },
) {
try {
// Get access token from settings
@@ -327,7 +327,7 @@ async function handleIsRepoAvailable(
// --- GitHub Create Repo Handler ---
async function handleCreateRepo(
event: IpcMainInvokeEvent,
{ org, repo, appId }: { org: string; repo: string; appId: number }
{ org, repo, appId }: { org: string; repo: string; appId: number },
) {
try {
// Get access token from settings
@@ -376,7 +376,7 @@ async function handleCreateRepo(
// --- GitHub Push Handler ---
async function handlePushToGithub(
event: IpcMainInvokeEvent,
{ appId }: { appId: number }
{ appId }: { appId: number },
) {
try {
// Get access token from settings
@@ -424,7 +424,7 @@ async function handlePushToGithub(
async function handleDisconnectGithubRepo(
event: IpcMainInvokeEvent,
{ appId }: { appId: number }
{ appId }: { appId: number },
) {
try {
logger.log(`Disconnecting GitHub repo for appId: ${appId}`);
@@ -464,6 +464,6 @@ export function registerGithubHandlers() {
ipcMain.handle("github:create-repo", handleCreateRepo);
ipcMain.handle("github:push", handlePushToGithub);
ipcMain.handle("github:disconnect", (event, args: { appId: number }) =>
handleDisconnectGithubRepo(event, args)
handleDisconnectGithubRepo(event, args),
);
}

View File

@@ -11,15 +11,17 @@ export interface LMStudioModel {
publisher: string;
state: "loaded" | "not-loaded";
max_context_length: number;
quantization: string
compatibility_type: string
quantization: string;
compatibility_type: string;
arch: string;
[key: string]: any;
}
export async function fetchLMStudioModels(): Promise<LocalModelListResponse> {
try {
const modelsResponse: Response = await fetch("http://localhost:1234/api/v0/models");
const modelsResponse: Response = await fetch(
"http://localhost:1234/api/v0/models",
);
if (!modelsResponse.ok) {
throw new Error("Failed to fetch models from LM Studio");
}
@@ -30,7 +32,7 @@ export async function fetchLMStudioModels(): Promise<LocalModelListResponse> {
.map((model: any) => ({
modelName: model.id,
displayName: model.id,
provider: "lmstudio"
provider: "lmstudio",
}));
logger.info(`Successfully fetched ${models.length} models from LM Studio`);
@@ -41,7 +43,10 @@ export async function fetchLMStudioModels(): Promise<LocalModelListResponse> {
}
export function registerLMStudioHandlers() {
ipcMain.handle('local-models:list-lmstudio', async (): Promise<LocalModelListResponse> => {
return fetchLMStudioModels();
});
}
ipcMain.handle(
"local-models:list-lmstudio",
async (): Promise<LocalModelListResponse> => {
return fetchLMStudioModels();
},
);
}

View File

@@ -31,12 +31,13 @@ export async function fetchOllamaModels(): Promise<LocalModelListResponse> {
const ollamaModels: OllamaModel[] = data.models || [];
const models: LocalModel[] = ollamaModels.map((model: OllamaModel) => {
const displayName = model.name.split(':')[0]
.replace(/-/g, ' ')
.replace(/(\d+)/, ' $1 ')
.split(' ')
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ')
const displayName = model.name
.split(":")[0]
.replace(/-/g, " ")
.replace(/(\d+)/, " $1 ")
.split(" ")
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join(" ")
.trim();
return {
@@ -45,14 +46,18 @@ export async function fetchOllamaModels(): Promise<LocalModelListResponse> {
provider: "ollama",
};
});
logger.info(`Successfully fetched ${models.length} models from Ollama`);
return { models, error: null };
logger.info(`Successfully fetched ${models.length} models from Ollama`);
return { models, error: null };
} catch (error) {
if (error instanceof TypeError && (error as Error).message.includes('fetch failed')) {
if (
error instanceof TypeError &&
(error as Error).message.includes("fetch failed")
) {
logger.error("Could not connect to Ollama");
return {
models: [],
error: "Could not connect to Ollama. Make sure it's running at http://localhost:11434"
error:
"Could not connect to Ollama. Make sure it's running at http://localhost:11434",
};
}
return { models: [], error: "Failed to fetch models from Ollama" };
@@ -60,7 +65,10 @@ export async function fetchOllamaModels(): Promise<LocalModelListResponse> {
}
export function registerOllamaHandlers() {
ipcMain.handle('local-models:list-ollama', async (): Promise<LocalModelListResponse> => {
return fetchOllamaModels();
});
}
ipcMain.handle(
"local-models:list-ollama",
async (): Promise<LocalModelListResponse> => {
return fetchOllamaModels();
},
);
}

View File

@@ -14,7 +14,7 @@ export function registerNodeHandlers() {
"handling ipc: nodejs-status for platform:",
platform(),
"and arch:",
arch()
arch(),
);
// Run checks in parallel
const [nodeVersion, pnpmVersion] = await Promise.all([
@@ -23,7 +23,7 @@ export function registerNodeHandlers() {
// If not, try to install it using corepack.
// If both fail, then pnpm is not available.
runShellCommand(
"pnpm --version || (corepack enable pnpm && pnpm --version) || (npm install -g pnpm@latest-10 && pnpm --version)"
"pnpm --version || (corepack enable pnpm && pnpm --version) || (npm install -g pnpm@latest-10 && pnpm --version)",
),
]);
// Default to mac download url.

View File

@@ -80,7 +80,7 @@ function cleanupExpiredCacheEntries() {
if (expiredCount > 0) {
logger.log(
`Cleaned up ${expiredCount} expired codebase token cache entries`
`Cleaned up ${expiredCount} expired codebase token cache entries`,
);
}
}
@@ -90,7 +90,7 @@ async function getCodebaseTokenCount(
chatId: number,
messageId: number,
messageContent: string,
appPath: string
appPath: string,
): Promise<number> {
// Clean up expired cache entries first
cleanupExpiredCacheEntries();
@@ -128,7 +128,7 @@ async function getCodebaseTokenCount(
const getProposalHandler = async (
_event: IpcMainInvokeEvent,
{ chatId }: { chatId: number }
{ chatId }: { chatId: number },
): Promise<ProposalResult | null> => {
return withLock("get-proposal:" + chatId, async () => {
logger.log(`IPC: get-proposal called for chatId: ${chatId}`);
@@ -152,7 +152,7 @@ const getProposalHandler = async (
) {
const messageId = latestAssistantMessage.id; // Get the message ID
logger.log(
`Found latest assistant message (ID: ${messageId}), parsing content...`
`Found latest assistant message (ID: ${messageId}), parsing content...`,
);
const messageContent = latestAssistantMessage.content;
@@ -211,7 +211,7 @@ const getProposalHandler = async (
"files=",
proposal.filesChanged.length,
"packages=",
proposal.packagesAdded.length
proposal.packagesAdded.length,
);
return {
@@ -221,19 +221,23 @@ const getProposalHandler = async (
};
} else {
logger.log(
"No relevant tags found in the latest assistant message content."
"No relevant tags found in the latest assistant message content.",
);
}
}
const actions: ActionProposal["actions"] = [];
if (latestAssistantMessage?.content) {
const writeTags = getDyadWriteTags(latestAssistantMessage.content);
const refactorTarget = writeTags.reduce((largest, tag) => {
const lineCount = tag.content.split("\n").length;
return lineCount > 500 && (!largest || lineCount > largest.lineCount)
? { path: tag.path, lineCount }
: largest;
}, null as { path: string; lineCount: number } | null);
const refactorTarget = writeTags.reduce(
(largest, tag) => {
const lineCount = tag.content.split("\n").length;
return lineCount > 500 &&
(!largest || lineCount > largest.lineCount)
? { path: tag.path, lineCount }
: largest;
},
null as { path: string; lineCount: number } | null,
);
if (refactorTarget) {
actions.push({
id: "refactor-file",
@@ -288,7 +292,7 @@ const getProposalHandler = async (
chatId,
latestAssistantMessage.id,
latestAssistantMessage.content || "",
chat.app.path
chat.app.path,
);
const totalTokens = messagesTokenCount + codebaseTokenCount;
@@ -296,13 +300,13 @@ const getProposalHandler = async (
logger.log(
`Token usage: ${totalTokens}/${contextWindow} (${
(totalTokens / contextWindow) * 100
}%)`
}%)`,
);
// If we're using more than 80% of the context window, suggest summarizing
if (totalTokens > contextWindow * 0.8) {
logger.log(
`Token usage high (${totalTokens}/${contextWindow}), suggesting summarize action`
`Token usage high (${totalTokens}/${contextWindow}), suggesting summarize action`,
);
actions.push({
id: "summarize-in-new-chat",
@@ -330,14 +334,14 @@ const getProposalHandler = async (
// Handler to approve a proposal (process actions and update message)
const approveProposalHandler = async (
_event: IpcMainInvokeEvent,
{ chatId, messageId }: { chatId: number; messageId: number }
{ chatId, messageId }: { chatId: number; messageId: number },
): Promise<{
success: boolean;
error?: string;
uncommittedFiles?: string[];
}> => {
logger.log(
`IPC: approve-proposal called for chatId: ${chatId}, messageId: ${messageId}`
`IPC: approve-proposal called for chatId: ${chatId}, messageId: ${messageId}`,
);
try {
@@ -346,7 +350,7 @@ const approveProposalHandler = async (
where: and(
eq(messages.id, messageId),
eq(messages.chatId, chatId),
eq(messages.role, "assistant")
eq(messages.role, "assistant"),
),
columns: {
content: true,
@@ -355,7 +359,7 @@ const approveProposalHandler = async (
if (!messageToApprove?.content) {
logger.error(
`Assistant message not found for chatId: ${chatId}, messageId: ${messageId}`
`Assistant message not found for chatId: ${chatId}, messageId: ${messageId}`,
);
return { success: false, error: "Assistant message not found." };
}
@@ -368,13 +372,13 @@ const approveProposalHandler = async (
{
chatSummary: chatSummary ?? undefined,
messageId,
} // Pass summary if found
}, // Pass summary if found
);
if (processResult.error) {
logger.error(
`Error processing actions for message ${messageId}:`,
processResult.error
processResult.error,
);
// Optionally: Update message state to 'error' or similar?
// For now, just return error to frontend
@@ -397,10 +401,10 @@ const approveProposalHandler = async (
// Handler to reject a proposal (just update message state)
const rejectProposalHandler = async (
_event: IpcMainInvokeEvent,
{ chatId, messageId }: { chatId: number; messageId: number }
{ chatId, messageId }: { chatId: number; messageId: number },
): Promise<{ success: boolean; error?: string }> => {
logger.log(
`IPC: reject-proposal called for chatId: ${chatId}, messageId: ${messageId}`
`IPC: reject-proposal called for chatId: ${chatId}, messageId: ${messageId}`,
);
try {
@@ -409,14 +413,14 @@ const rejectProposalHandler = async (
where: and(
eq(messages.id, messageId),
eq(messages.chatId, chatId),
eq(messages.role, "assistant")
eq(messages.role, "assistant"),
),
columns: { id: true },
});
if (!messageToReject) {
logger.error(
`Assistant message not found for chatId: ${chatId}, messageId: ${messageId}`
`Assistant message not found for chatId: ${chatId}, messageId: ${messageId}`,
);
return { success: false, error: "Assistant message not found." };
}

View File

@@ -15,7 +15,7 @@ export function registerSettingsHandlers() {
if (
Object.prototype.hasOwnProperty.call(
settings.providerSettings,
providerKey
providerKey,
)
) {
const providerSetting = settings.providerSettings[providerKey];
@@ -35,6 +35,6 @@ export function registerSettingsHandlers() {
async (_, settings: Partial<UserSettings>) => {
writeSettings(settings);
return readSettings();
}
},
);
}

View File

@@ -39,7 +39,7 @@ export function registerSupabaseHandlers() {
logger.error("Error setting Supabase project for app:", error);
throw error;
}
}
},
);
// Unset app project - removes the link between a Dyad app and a Supabase project
@@ -58,6 +58,6 @@ export function registerSupabaseHandlers() {
logger.error("Error unsetting Supabase project for app:", error);
throw error;
}
}
},
);
}

View File

@@ -46,7 +46,7 @@ export async function streamTestResponse(
chatId: number,
testResponse: string,
abortController: AbortController,
updatedChat: any
updatedChat: any,
): Promise<string> {
console.log(`Using canned response for test prompt`);

View File

@@ -62,7 +62,7 @@ export function registerTokenCountHandlers() {
}
const systemPromptTokens = estimateTokens(
systemPrompt + supabaseContext
systemPrompt + supabaseContext,
);
// Extract codebase information if app is associated with the chat
@@ -75,7 +75,7 @@ export function registerTokenCountHandlers() {
codebaseInfo = await extractCodebase(appPath);
codebaseTokens = estimateTokens(codebaseInfo);
logger.log(
`Extracted codebase information from ${appPath}, tokens: ${codebaseTokens}`
`Extracted codebase information from ${appPath}, tokens: ${codebaseTokens}`,
);
} catch (error) {
logger.error("Error extracting codebase:", error);
@@ -101,6 +101,6 @@ export function registerTokenCountHandlers() {
logger.error("Error counting tokens:", error);
throw error;
}
}
},
);
}

View File

@@ -39,7 +39,7 @@ export function registerUploadHandlers() {
if (!response.ok) {
throw new Error(
`Upload failed with status ${response.status}: ${response.statusText}`
`Upload failed with status ${response.status}: ${response.statusText}`,
);
}
@@ -52,7 +52,7 @@ export function registerUploadHandlers() {
error: error instanceof Error ? error.message : String(error),
};
}
}
},
);
logger.debug("Registered upload IPC handlers");

View File

@@ -95,14 +95,17 @@ export function registerVersionHandlers() {
errorMessage: `Failed to get current branch: ${error.message}`,
};
}
}
},
);
ipcMain.handle(
"revert-version",
async (
_,
{ appId, previousVersionId }: { appId: number; previousVersionId: string }
{
appId,
previousVersionId,
}: { appId: number; previousVersionId: string },
) => {
return withLock(appId, async () => {
const app = await db.query.apps.findFirst({
@@ -198,13 +201,13 @@ export function registerVersionHandlers() {
const messagesToDelete = await db.query.messages.findMany({
where: and(
eq(messages.chatId, chatId),
gt(messages.id, messageWithCommit.id)
gt(messages.id, messageWithCommit.id),
),
orderBy: desc(messages.id),
});
logger.log(
`Deleting ${messagesToDelete.length} messages after commit ${previousVersionId} from chat ${chatId}`
`Deleting ${messagesToDelete.length} messages after commit ${previousVersionId} from chat ${chatId}`,
);
// Delete the messages
@@ -214,8 +217,8 @@ export function registerVersionHandlers() {
.where(
and(
eq(messages.chatId, chatId),
gt(messages.id, messageWithCommit.id)
)
gt(messages.id, messageWithCommit.id),
),
);
}
}
@@ -224,12 +227,12 @@ export function registerVersionHandlers() {
} catch (error: any) {
logger.error(
`Error reverting to version ${previousVersionId} for app ${appId}:`,
error
error,
);
throw new Error(`Failed to revert version: ${error.message}`);
}
});
}
},
);
ipcMain.handle(
@@ -259,11 +262,11 @@ export function registerVersionHandlers() {
} catch (error: any) {
logger.error(
`Error checking out version ${versionId} for app ${appId}:`,
error
error,
);
throw new Error(`Failed to checkout version: ${error.message}`);
}
});
}
},
);
}

View File

@@ -84,7 +84,7 @@ export class IpcClient {
} else {
console.warn(
`[IPC] No callbacks found for chat ${chatId}`,
this.chatStreams
this.chatStreams,
);
}
} else {
@@ -119,7 +119,9 @@ export class IpcClient {
this.chatStreams.delete(chatId);
} else {
console.error(
new Error(`[IPC] No callbacks found for chat ${chatId} on stream end`)
new Error(
`[IPC] No callbacks found for chat ${chatId} on stream end`,
),
);
}
});
@@ -220,7 +222,7 @@ export class IpcClient {
public async editAppFile(
appId: number,
filePath: string,
content: string
content: string,
): Promise<{ success: boolean }> {
try {
const result = await this.ipcRenderer.invoke("edit-app-file", {
@@ -245,7 +247,7 @@ export class IpcClient {
onUpdate: (messages: Message[]) => void;
onEnd: (response: ChatResponseEnd) => void;
onError: (error: string) => void;
}
},
): void {
const { chatId, redo, attachments, onUpdate, onEnd, onError } = options;
this.chatStreams.set(chatId, { onUpdate, onEnd, onError });
@@ -268,9 +270,9 @@ export class IpcClient {
reader.onerror = () =>
reject(new Error(`Failed to read file: ${file.name}`));
reader.readAsDataURL(file);
}
},
);
})
}),
)
.then((fileDataArray) => {
// Use invoke to start the stream and pass the chatId and attachments
@@ -331,7 +333,7 @@ export class IpcClient {
}
public async deleteChat(
chatId: number
chatId: number,
): Promise<{ success: boolean; error?: string }> {
try {
const result = await this.ipcRenderer.invoke("delete-chat", chatId);
@@ -343,7 +345,7 @@ export class IpcClient {
}
public async deleteMessages(
chatId: number
chatId: number,
): Promise<{ success: boolean; error?: string }> {
try {
const result = await this.ipcRenderer.invoke("delete-messages", chatId);
@@ -356,7 +358,7 @@ export class IpcClient {
// Open an external URL using the default browser
public async openExternalUrl(
url: string
url: string,
): Promise<{ success: boolean; error?: string }> {
try {
const result = await this.ipcRenderer.invoke("open-external-url", url);
@@ -368,12 +370,12 @@ export class IpcClient {
}
public async showItemInFolder(
fullPath: string
fullPath: string,
): Promise<{ success: boolean; error?: string }> {
try {
const result = await this.ipcRenderer.invoke(
"show-item-in-folder",
fullPath
fullPath,
);
return result as { success: boolean; error?: string };
} catch (error) {
@@ -385,7 +387,7 @@ export class IpcClient {
// Run an app
public async runApp(
appId: number,
onOutput: (output: AppOutput) => void
onOutput: (output: AppOutput) => void,
): Promise<{ success: boolean }> {
try {
const result = await this.ipcRenderer.invoke("run-app", { appId });
@@ -412,7 +414,7 @@ export class IpcClient {
public async restartApp(
appId: number,
onOutput: (output: AppOutput) => void,
removeNodeModules?: boolean
removeNodeModules?: boolean,
): Promise<{ success: boolean }> {
try {
const result = await this.ipcRenderer.invoke("restart-app", {
@@ -512,12 +514,12 @@ export class IpcClient {
// Update user settings
public async setUserSettings(
settings: Partial<UserSettings>
settings: Partial<UserSettings>,
): Promise<UserSettings> {
try {
const updatedSettings = await this.ipcRenderer.invoke(
"set-user-settings",
settings
settings,
);
return updatedSettings;
} catch (error) {
@@ -606,7 +608,7 @@ export class IpcClient {
}
public onGithubDeviceFlowUpdate(
callback: (data: GitHubDeviceFlowUpdateData) => void
callback: (data: GitHubDeviceFlowUpdateData) => void,
): () => void {
const listener = (data: any) => {
console.log("github:flow-update", data);
@@ -620,7 +622,7 @@ export class IpcClient {
}
public onGithubDeviceFlowSuccess(
callback: (data: GitHubDeviceFlowSuccessData) => void
callback: (data: GitHubDeviceFlowSuccessData) => void,
): () => void {
const listener = (data: any) => {
console.log("github:flow-success", data);
@@ -633,7 +635,7 @@ export class IpcClient {
}
public onGithubDeviceFlowError(
callback: (data: GitHubDeviceFlowErrorData) => void
callback: (data: GitHubDeviceFlowErrorData) => void,
): () => void {
const listener = (data: any) => {
console.log("github:flow-error", data);
@@ -654,7 +656,7 @@ export class IpcClient {
// --- GitHub Repo Management ---
public async checkGithubRepoAvailable(
org: string,
repo: string
repo: string,
): Promise<{ available: boolean; error?: string }> {
try {
const result = await this.ipcRenderer.invoke("github:is-repo-available", {
@@ -670,7 +672,7 @@ export class IpcClient {
public async createGithubRepo(
org: string,
repo: string,
appId: number
appId: number,
): Promise<{ success: boolean; error?: string }> {
try {
const result = await this.ipcRenderer.invoke("github:create-repo", {
@@ -686,7 +688,7 @@ export class IpcClient {
// Sync (push) local repo to GitHub
public async syncGithubRepo(
appId: number
appId: number,
): Promise<{ success: boolean; error?: string }> {
try {
const result = await this.ipcRenderer.invoke("github:push", { appId });
@@ -698,7 +700,7 @@ export class IpcClient {
}
public async disconnectGithubRepo(
appId: number
appId: number,
): Promise<{ success: boolean; error?: string }> {
try {
const result = await this.ipcRenderer.invoke("github:disconnect", {
@@ -800,7 +802,7 @@ export class IpcClient {
public async setSupabaseAppProject(
project: string,
app: number
app: number,
): Promise<{ success: boolean; appId: number; projectId: string }> {
try {
const result = await this.ipcRenderer.invoke("supabase:set-app-project", {
@@ -815,14 +817,14 @@ export class IpcClient {
}
public async unsetSupabaseAppProject(
app: number
app: number,
): Promise<{ success: boolean; appId: number }> {
try {
const result = await this.ipcRenderer.invoke(
"supabase:unset-app-project",
{
app,
}
},
);
return result;
} catch (error) {
@@ -856,7 +858,7 @@ export class IpcClient {
public async uploadToSignedUrl(
url: string,
contentType: string,
data: any
data: any,
): Promise<{ success: boolean; error?: string }> {
try {
const result = await this.ipcRenderer.invoke("upload-to-signed-url", {
@@ -874,7 +876,7 @@ export class IpcClient {
public async listLocalOllamaModels(): Promise<LocalModel[]> {
try {
const response = await this.ipcRenderer.invoke(
"local-models:list-ollama"
"local-models:list-ollama",
);
return response?.models || [];
} catch (error) {
@@ -888,7 +890,7 @@ export class IpcClient {
public async listLocalLMStudioModels(): Promise<LocalModel[]> {
try {
const response = await this.ipcRenderer.invoke(
"local-models:list-lmstudio"
"local-models:list-lmstudio",
);
return response?.models || [];
} catch (error) {
@@ -896,14 +898,14 @@ export class IpcClient {
throw new Error(`Failed to fetch LM Studio models: ${error.message}`);
}
throw new Error(
"Failed to fetch LM Studio models: Unknown error occurred"
"Failed to fetch LM Studio models: Unknown error occurred",
);
}
}
// Listen for deep link events
public onDeepLinkReceived(
callback: (data: DeepLinkData) => void
callback: (data: DeepLinkData) => void,
): () => void {
const listener = (data: any) => {
callback(data as DeepLinkData);
@@ -916,7 +918,7 @@ export class IpcClient {
// Count tokens for a chat and input
public async countTokens(
params: TokenCountParams
params: TokenCountParams,
): Promise<TokenCountResult> {
try {
const result = await this.ipcRenderer.invoke("chat:count-tokens", params);

View File

@@ -22,7 +22,7 @@ export async function executeAddDependency({
`(pnpm add ${packageStr}) || (npm install ${packageStr})`,
{
cwd: appPath,
}
},
);
const installResults = stdout + (stderr ? `\n${stderr}` : "");
@@ -30,13 +30,13 @@ export async function executeAddDependency({
const updatedContent = message.content.replace(
new RegExp(
`<dyad-add-dependency packages="${packages.join(
" "
" ",
)}">[^<]*</dyad-add-dependency>`,
"g"
"g",
),
`<dyad-add-dependency packages="${packages.join(
" "
)}">${installResults}</dyad-add-dependency>`
" ",
)}">${installResults}</dyad-add-dependency>`,
);
// Save the updated message back to the database

View File

@@ -56,7 +56,7 @@ export function getDyadWriteTags(fullResponse: string): {
} else {
logger.warn(
"Found <dyad-write> tag without a valid 'path' attribute:",
match[0]
match[0],
);
}
}
@@ -174,7 +174,7 @@ export async function processFullResponseActions(
{
chatSummary,
messageId,
}: { chatSummary: string | undefined; messageId: number }
}: { chatSummary: string | undefined; messageId: number },
): Promise<{
updatedFiles?: boolean;
error?: string;
@@ -216,7 +216,7 @@ export async function processFullResponseActions(
where: and(
eq(messages.id, messageId),
eq(messages.role, "assistant"),
eq(messages.chatId, chatId)
eq(messages.chatId, chatId),
),
});
@@ -254,7 +254,7 @@ export async function processFullResponseActions(
} catch (error) {
errors.push({
message: `Failed to add dependencies: ${dyadAddDependencyPackages.join(
", "
", ",
)}`,
error: error,
});
@@ -433,7 +433,7 @@ export async function processFullResponseActions(
changes.push(`deleted ${deletedFiles.length} file(s)`);
if (dyadAddDependencyPackages.length > 0)
changes.push(
`added ${dyadAddDependencyPackages.join(", ")} package(s)`
`added ${dyadAddDependencyPackages.join(", ")} package(s)`,
);
if (dyadExecuteSqlQueries.length > 0)
changes.push(`executed ${dyadExecuteSqlQueries.length} SQL queries`);
@@ -486,13 +486,13 @@ export async function processFullResponseActions(
${warnings
.map(
(warning) =>
`<dyad-output type="warning" message="${warning.message}">${warning.error}</dyad-output>`
`<dyad-output type="warning" message="${warning.message}">${warning.error}</dyad-output>`,
)
.join("\n")}
${errors
.map(
(error) =>
`<dyad-output type="error" message="${error.message}">${error.error}</dyad-output>`
`<dyad-output type="error" message="${error.message}">${error.error}</dyad-output>`,
)
.join("\n")}
`;

View File

@@ -35,7 +35,7 @@ export function getFilesRecursively(dir: string, baseDir: string): string[] {
export async function copyDirectoryRecursive(
source: string,
destination: string
destination: string,
) {
await fsPromises.mkdir(destination, { recursive: true });
const entries = await fsPromises.readdir(source, { withFileTypes: true });

View File

@@ -17,7 +17,7 @@ import log from "electron-log";
const logger = log.scope("getModelClient");
export function getModelClient(
model: LargeLanguageModel,
settings: UserSettings
settings: UserSettings,
) {
const dyadApiKey = settings.providerSettings?.auto?.apiKey?.value;
// Handle 'auto' provider by trying each model in AUTO_MODELS until one works
@@ -31,7 +31,7 @@ export function getModelClient(
if (apiKey) {
logger.log(
`Using provider: ${autoModel.provider} model: ${autoModel.name}`
`Using provider: ${autoModel.provider} model: ${autoModel.name}`,
);
// Use the first model that has an API key
return getModelClient(
@@ -39,7 +39,7 @@ export function getModelClient(
provider: autoModel.provider,
name: autoModel.name,
} as LargeLanguageModel,
settings
settings,
);
}
}
@@ -104,7 +104,7 @@ const DEFAULT_MAX_TOKENS = 8_000;
export function getMaxTokens(model: LargeLanguageModel) {
if (!MODEL_OPTIONS[model.provider as keyof typeof MODEL_OPTIONS]) {
logger.warn(
`Model provider ${model.provider} not found in MODEL_OPTIONS. Using default max tokens.`
`Model provider ${model.provider} not found in MODEL_OPTIONS. Using default max tokens.`,
);
return DEFAULT_MAX_TOKENS;
}

View File

@@ -30,7 +30,7 @@ export function acquireLock(lockId: number | string): {
*/
export async function withLock<T>(
lockId: number | string,
fn: () => Promise<T>
fn: () => Promise<T>,
): Promise<T> {
// Wait for any existing operation to complete
const existingLock = locks.get(lockId);

View File

@@ -36,7 +36,7 @@ export function killProcess(process: ChildProcess): Promise<void> {
// Add timeout to prevent hanging
const timeout = setTimeout(() => {
console.warn(
`Timeout waiting for process (PID: ${process.pid}) to close. Force killing may be needed.`
`Timeout waiting for process (PID: ${process.pid}) to close. Force killing may be needed.`,
);
resolve();
}, 5000); // 5-second timeout
@@ -44,7 +44,7 @@ export function killProcess(process: ChildProcess): Promise<void> {
process.on("close", (code, signal) => {
clearTimeout(timeout);
console.log(
`Received 'close' event for process (PID: ${process.pid}) with code ${code}, signal ${signal}.`
`Received 'close' event for process (PID: ${process.pid}) with code ${code}, signal ${signal}.`,
);
resolve();
});
@@ -53,7 +53,7 @@ export function killProcess(process: ChildProcess): Promise<void> {
process.on("error", (err) => {
clearTimeout(timeout);
console.error(
`Error during stop sequence for process (PID: ${process.pid}): ${err.message}`
`Error during stop sequence for process (PID: ${process.pid}): ${err.message}`,
);
resolve();
});
@@ -62,16 +62,16 @@ export function killProcess(process: ChildProcess): Promise<void> {
if (process.pid) {
// Use tree-kill to terminate the entire process tree
console.log(
`Attempting to tree-kill process tree starting at PID ${process.pid}.`
`Attempting to tree-kill process tree starting at PID ${process.pid}.`,
);
treeKill(process.pid, "SIGTERM", (err: Error | undefined) => {
if (err) {
console.warn(
`tree-kill error for PID ${process.pid}: ${err.message}`
`tree-kill error for PID ${process.pid}: ${err.message}`,
);
} else {
console.log(
`tree-kill signal sent successfully to PID ${process.pid}.`
`tree-kill signal sent successfully to PID ${process.pid}.`,
);
}
});
@@ -88,17 +88,17 @@ export function killProcess(process: ChildProcess): Promise<void> {
*/
export function removeAppIfCurrentProcess(
appId: number,
process: ChildProcess
process: ChildProcess,
): void {
const currentAppInfo = runningApps.get(appId);
if (currentAppInfo && currentAppInfo.process === process) {
runningApps.delete(appId);
console.log(
`Removed app ${appId} (processId ${currentAppInfo.processId}) from running map. Current size: ${runningApps.size}`
`Removed app ${appId} (processId ${currentAppInfo.processId}) from running map. Current size: ${runningApps.size}`,
);
} else {
console.log(
`App ${appId} process was already removed or replaced in running map. Ignoring.`
`App ${appId} process was already removed or replaced in running map. Ignoring.`,
);
}
}

View File

@@ -29,7 +29,7 @@ export function runShellCommand(command: string): Promise<string | null> {
process.on("close", (code) => {
if (code === 0) {
logger.debug(
`Command "${command}" succeeded with code ${code}: ${output.trim()}`
`Command "${command}" succeeded with code ${code}: ${output.trim()}`,
);
resolve(output.trim()); // Command succeeded, return trimmed output
} else {

View File

@@ -13,7 +13,7 @@ export const estimateTokens = (text: string): number => {
export const estimateMessagesTokens = (messages: Message[]): number => {
return messages.reduce(
(acc, message) => acc + estimateTokens(message.content),
0
0,
);
};
@@ -24,7 +24,7 @@ export function getContextWindow() {
const model = settings.selectedModel;
if (!MODEL_OPTIONS[model.provider as keyof typeof MODEL_OPTIONS]) {
logger.warn(
`Model provider ${model.provider} not found in MODEL_OPTIONS. Using default max tokens.`
`Model provider ${model.provider} not found in MODEL_OPTIONS. Using default max tokens.`,
);
return DEFAULT_CONTEXT_WINDOW;
}

View File

@@ -9,7 +9,7 @@ import type { CreateAppParams, CreateAppResult } from "../ipc/ipc_types";
* @returns The created app and chatId
*/
export async function createApp(
params: CreateAppParams
params: CreateAppParams,
): Promise<CreateAppResult> {
try {
return await IpcClient.getInstance().createApp(params);

View File

@@ -41,7 +41,7 @@ const providers = [
export const ModelProviderSchema = z.enum(providers);
export const cloudProviders = providers.filter(
(provider) => provider !== "ollama" && provider !== "lmstudio"
(provider) => provider !== "ollama" && provider !== "lmstudio",
);
/**

View File

@@ -49,7 +49,7 @@ export const showLoading = <T>(
loadingMessage: string,
promise: Promise<T>,
successMessage?: string,
errorMessage?: string
errorMessage?: string,
) => {
return toast.promise(promise, {
loading: loadingMessage,
@@ -61,7 +61,7 @@ export const showLoading = <T>(
export const showUncommittedFilesWarning = (files: string[]) => {
showWarning(
`Some changed files were not committed. Please use git to manually commit them.
\n\n${files.join("\n")}`
\n\n${files.join("\n")}`,
);
};

View File

@@ -115,7 +115,7 @@ const createWindow = () => {
mainWindow.loadURL(MAIN_WINDOW_VITE_DEV_SERVER_URL);
} else {
mainWindow.loadFile(
path.join(__dirname, "../renderer/main_window/index.html")
path.join(__dirname, "../renderer/main_window/index.html"),
);
}
if (process.env.NODE_ENV === "development") {
@@ -158,12 +158,12 @@ function handleDeepLinkReturn(url: string) {
"Handling deep link: protocol",
parsed.protocol,
"hostname",
parsed.hostname
parsed.hostname,
);
if (parsed.protocol !== "dyad:") {
dialog.showErrorBox(
"Invalid Protocol",
`Expected dyad://, got ${parsed.protocol}. Full URL: ${url}`
`Expected dyad://, got ${parsed.protocol}. Full URL: ${url}`,
);
return;
}
@@ -174,7 +174,7 @@ function handleDeepLinkReturn(url: string) {
if (!token || !refreshToken || !expiresIn) {
dialog.showErrorBox(
"Invalid URL",
"Expected token, refreshToken, and expiresIn"
"Expected token, refreshToken, and expiresIn",
);
return;
}
@@ -196,7 +196,7 @@ function handleDeepLinkReturn(url: string) {
if (!apiKey) {
dialog.showErrorBox(
"Invalid URL",
"Expected key, budget_reset_at, and max_budget"
"Expected key, budget_reset_at, and max_budget",
);
return;
}

View File

@@ -93,25 +93,25 @@ export function writeSettings(settings: Partial<UserSettings>): void {
const newSettings = { ...currentSettings, ...settings };
if (newSettings.githubAccessToken) {
newSettings.githubAccessToken = encrypt(
newSettings.githubAccessToken.value
newSettings.githubAccessToken.value,
);
}
if (newSettings.supabase) {
if (newSettings.supabase.accessToken) {
newSettings.supabase.accessToken = encrypt(
newSettings.supabase.accessToken.value
newSettings.supabase.accessToken.value,
);
}
if (newSettings.supabase.refreshToken) {
newSettings.supabase.refreshToken = encrypt(
newSettings.supabase.refreshToken.value
newSettings.supabase.refreshToken.value,
);
}
}
for (const provider in newSettings.providerSettings) {
if (newSettings.providerSettings[provider].apiKey) {
newSettings.providerSettings[provider].apiKey = encrypt(
newSettings.providerSettings[provider].apiKey.value
newSettings.providerSettings[provider].apiKey.value,
);
}
}

View File

@@ -108,7 +108,7 @@ export default function AppDetailsPage() {
alert(
`Error renaming app: ${
error instanceof Error ? error.message : String(error)
}`
}`,
);
} finally {
setIsRenaming(false);
@@ -134,7 +134,7 @@ export default function AppDetailsPage() {
alert(
`Error renaming folder: ${
error instanceof Error ? error.message : String(error)
}`
}`,
);
} finally {
setIsRenamingFolder(false);

View File

@@ -69,7 +69,7 @@ export default function ChatPage() {
id="preview-panel"
minSize={20}
className={cn(
!isResizing && "transition-all duration-100 ease-in-out"
!isResizing && "transition-all duration-100 ease-in-out",
)}
>
<PreviewPanel />

View File

@@ -228,7 +228,7 @@ export default function HomePage() {
onClick={() =>
window.open(
releaseUrl.replace("?hideHeader=true&theme=" + theme, ""),
"_blank"
"_blank",
)
}
>

View File

@@ -36,7 +36,7 @@ export default function SettingsPage() {
} catch (error) {
console.error("Error resetting:", error);
showError(
error instanceof Error ? error.message : "An unknown error occurred"
error instanceof Error ? error.message : "An unknown error occurred",
);
} finally {
setIsResetting(false);

View File

@@ -89,7 +89,7 @@ contextBridge.exposeInMainWorld("electron", {
},
on: (
channel: ValidReceiveChannel,
listener: (...args: unknown[]) => void
listener: (...args: unknown[]) => void,
) => {
if (validReceiveChannels.includes(channel)) {
const subscription = (
@@ -110,7 +110,7 @@ contextBridge.exposeInMainWorld("electron", {
},
removeListener: (
channel: ValidReceiveChannel,
listener: (...args: unknown[]) => void
listener: (...args: unknown[]) => void,
) => {
if (validReceiveChannels.includes(channel)) {
ipcRenderer.removeListener(channel, listener);

View File

@@ -36,12 +36,12 @@ const posthogClient = posthog.init(
"Telemetry opted in - UUID:",
telemetryUserId,
"sending event",
event
event,
);
return event;
},
persistence: "localStorage",
}
},
);
function App() {
@@ -74,5 +74,5 @@ createRoot(document.getElementById("root")!).render(
<PostHogProvider client={posthogClient}>
<App />
</PostHogProvider>
</StrictMode>
</StrictMode>,
);

View File

@@ -5,48 +5,54 @@
/* Load Geist Font */
@font-face {
font-family: 'Geist Mono';
src: url('/node_modules/geist/dist/fonts/geist-mono/GeistMono-Regular.ttf') format('truetype');
font-family: "Geist Mono";
src: url("/node_modules/geist/dist/fonts/geist-mono/GeistMono-Regular.ttf")
format("truetype");
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Geist Mono';
src: url('/node_modules/geist/dist/fonts/geist-mono/GeistMono-Medium.ttf') format('truetype');
font-family: "Geist Mono";
src: url("/node_modules/geist/dist/fonts/geist-mono/GeistMono-Medium.ttf")
format("truetype");
font-weight: 500;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Geist Mono';
src: url('/node_modules/geist/dist/fonts/geist-mono/GeistMono-Bold.ttf') format('truetype');
font-family: "Geist Mono";
src: url("/node_modules/geist/dist/fonts/geist-mono/GeistMono-Bold.ttf")
format("truetype");
font-weight: 700;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Geist';
src: url('/node_modules/geist/dist/fonts/geist-sans/Geist-Regular.ttf') format('truetype');
font-family: "Geist";
src: url("/node_modules/geist/dist/fonts/geist-sans/Geist-Regular.ttf")
format("truetype");
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Geist';
src: url('/node_modules/geist/dist/fonts/geist-sans/Geist-Medium.ttf') format('truetype');
font-family: "Geist";
src: url("/node_modules/geist/dist/fonts/geist-sans/Geist-Medium.ttf")
format("truetype");
font-weight: 500;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Geist';
src: url('/node_modules/geist/dist/fonts/geist-sans/Geist-Bold.ttf') format('truetype');
font-family: "Geist";
src: url("/node_modules/geist/dist/fonts/geist-sans/Geist-Bold.ttf")
format("truetype");
font-weight: 700;
font-style: normal;
font-display: swap;
@@ -68,8 +74,8 @@
:root {
--docs-bg: #f5f5f5;
--default-font-family: 'Geist', sans-serif;
--default-mono-font-family: 'Geist Mono', monospace;
--default-font-family: "Geist", sans-serif;
--default-mono-font-family: "Geist Mono", monospace;
/* --background: oklch(1 0 0); */
--background-lightest: #fff;
--background-lighter: oklch(0.995 0.0188 292.61);
@@ -122,7 +128,6 @@
.dark {
--docs-bg: #121212;
--background-lightest: oklch(0.285 0 0);
--background-lighter: oklch(0.185 0 0);
--background: oklch(0.145 0 0);
@@ -214,65 +219,64 @@
}
@layer base {
:root {
--sidebar-background: 0 0% 98%;
--sidebar-foreground: 240 5.3% 26.1%;
--sidebar-primary: 240 5.9% 10%;
--sidebar-primary-foreground: 0 0% 98%;
--sidebar-accent: 240 4.8% 95.9%;
--sidebar-accent-foreground: 240 5.9% 10%;
--sidebar-border: 220 13% 91%;
--sidebar-ring: 217.2 91.2% 59.8%;
}
.dark {
--sidebar-background: 240 5.9% 10%;
--sidebar-foreground: 240 4.8% 95.9%;
--sidebar-primary: 224.3 76.3% 48%;
--sidebar-primary-foreground: 0 0% 100%;
--sidebar-accent: 240 3.7% 15.9%;
--sidebar-accent-foreground: 240 4.8% 95.9%;
--sidebar-border: 240 3.7% 15.9%;
--sidebar-ring: 217.2 91.2% 59.8%;
:root {
--sidebar-background: 0 0% 98%;
--sidebar-foreground: 240 5.3% 26.1%;
--sidebar-primary: 240 5.9% 10%;
--sidebar-primary-foreground: 0 0% 98%;
--sidebar-accent: 240 4.8% 95.9%;
--sidebar-accent-foreground: 240 5.9% 10%;
--sidebar-border: 220 13% 91%;
--sidebar-ring: 217.2 91.2% 59.8%;
}
.dark {
--sidebar-background: 240 5.9% 10%;
--sidebar-foreground: 240 4.8% 95.9%;
--sidebar-primary: 224.3 76.3% 48%;
--sidebar-primary-foreground: 0 0% 100%;
--sidebar-accent: 240 3.7% 15.9%;
--sidebar-accent-foreground: 240 4.8% 95.9%;
--sidebar-border: 240 3.7% 15.9%;
--sidebar-ring: 217.2 91.2% 59.8%;
}
}
@media (pointer: fine) {
@supports not (selector(::-webkit-scrollbar)) {
* {
scrollbar-color: #dadce0 transparent;
scrollbar-gutter: auto;
scrollbar-width: thin;
}
}
@media (pointer: fine) {
@supports not (selector(::-webkit-scrollbar)) {
* {
scrollbar-color: #dadce0 transparent;
scrollbar-gutter: auto;
scrollbar-width: thin;
}
}
::-webkit-scrollbar,
::-webkit-scrollbar-corner {
background: transparent;
height: 12px;
width: 12px;
}
::-webkit-scrollbar-thumb {
background: content-box currentColor;
border: 2px solid transparent;
border-radius: 8px;
color: #dadce0;
min-height: 48px;
min-width: 48px;
}
:hover::-webkit-scrollbar-thumb {
color: #80868b;
}
::-webkit-scrollbar-thumb:active {
color: #5f6368;
}
::-webkit-scrollbar-button {
height: 0;
width: 0;
}
::-webkit-scrollbar,
::-webkit-scrollbar-corner {
background: transparent;
height: 12px;
width: 12px;
}
::-webkit-scrollbar-thumb {
background: content-box currentColor;
border: 2px solid transparent;
border-radius: 8px;
color: #dadce0;
min-height: 48px;
min-width: 48px;
}
:hover::-webkit-scrollbar-thumb {
color: #80868b;
}
::-webkit-scrollbar-thumb:active {
color: #5f6368;
}
::-webkit-scrollbar-button {
height: 0;
width: 0;
}
}

View File

@@ -9,7 +9,7 @@ async function getPublishableKey({ projectId }: { projectId: string }) {
}
const publishableKey = keys.find(
(key) =>
(key as any)["name"] === "anon" || (key as any)["type"] === "publishable"
(key as any)["name"] === "anon" || (key as any)["type"] === "publishable",
);
if (!publishableKey) {
@@ -47,7 +47,7 @@ export async function getSupabaseContext({
});
const schema = await supabase.runQuery(
supabaseProjectId,
SUPABASE_SCHEMA_QUERY
SUPABASE_SCHEMA_QUERY,
);
const secrets = await supabase.getSecrets(supabaseProjectId);

View File

@@ -38,7 +38,7 @@ export async function refreshSupabaseToken(): Promise<void> {
if (!refreshToken) {
throw new Error(
"Supabase refresh token not found. Please authenticate first."
"Supabase refresh token not found. Please authenticate first.",
);
}
@@ -52,7 +52,7 @@ export async function refreshSupabaseToken(): Promise<void> {
"Content-Type": "application/json",
},
body: JSON.stringify({ refreshToken }),
}
},
);
if (!response.ok) {
@@ -94,7 +94,7 @@ export async function getSupabaseClient(): Promise<SupabaseManagementAPI> {
if (!supabaseAccessToken) {
throw new Error(
"Supabase access token not found. Please authenticate first."
"Supabase access token not found. Please authenticate first.",
);
}
@@ -120,7 +120,7 @@ export async function getSupabaseClient(): Promise<SupabaseManagementAPI> {
}
export async function getSupabaseProjectName(
projectId: string
projectId: string,
): Promise<string> {
const supabase = await getSupabaseClient();
const projects = await supabase.getProjects();
@@ -148,12 +148,12 @@ export async function deleteSupabaseFunction({
functionName: string;
}): Promise<void> {
logger.info(
`Deleting Supabase function: ${functionName} from project: ${supabaseProjectId}`
`Deleting Supabase function: ${functionName} from project: ${supabaseProjectId}`,
);
const supabase = await getSupabaseClient();
await supabase.deleteFunction(supabaseProjectId, functionName);
logger.info(
`Deleted Supabase function: ${functionName} from project: ${supabaseProjectId}`
`Deleted Supabase function: ${functionName} from project: ${supabaseProjectId}`,
);
}
@@ -167,7 +167,7 @@ export async function deploySupabaseFunctions({
content: string;
}): Promise<void> {
logger.info(
`Deploying Supabase function: ${functionName} to project: ${supabaseProjectId}`
`Deploying Supabase function: ${functionName} to project: ${supabaseProjectId}`,
);
const supabase = await getSupabaseClient();
const formData = new FormData();
@@ -176,7 +176,7 @@ export async function deploySupabaseFunctions({
JSON.stringify({
entrypoint_path: "index.ts",
name: functionName,
})
}),
);
formData.append("file", new Blob([content]), "index.ts");
@@ -188,7 +188,7 @@ export async function deploySupabaseFunctions({
Authorization: `Bearer ${(supabase as any).options.accessToken}`,
},
body: formData,
}
},
);
if (response.status !== 201) {
@@ -196,7 +196,7 @@ export async function deploySupabaseFunctions({
}
logger.info(
`Deployed Supabase function: ${functionName} to project: ${supabaseProjectId}`
`Deployed Supabase function: ${functionName} to project: ${supabaseProjectId}`,
);
return response.json();
}
@@ -208,12 +208,12 @@ async function createResponseError(response: Response, action: string) {
`Failed to ${action}: ${response.statusText} (${response.status})${
errorBody ? `: ${errorBody.message}` : ""
}`,
response
response,
);
}
async function safeParseErrorResponseBody(
response: Response
response: Response,
): Promise<{ message: string } | undefined> {
try {
const body = await response.json();

View File

@@ -52,7 +52,7 @@ const gitIgnoreMtimes = new Map<string, number>();
*/
async function isGitIgnored(
filePath: string,
baseDir: string
baseDir: string,
): Promise<boolean> {
try {
// Check if any relevant .gitignore has been modified
@@ -318,7 +318,7 @@ async function sortFilesByModificationTime(files: string[]): Promise<string[]> {
logger.error(`Error getting file stats for ${file}:`, error);
return { file, mtime: Date.now() };
}
})
}),
);
// Sort by modification time (oldest first)
@@ -346,12 +346,12 @@ function sortFilesByImportance(files: string[], baseDir: string): string[] {
// Check if file A matches any high priority pattern
const aIsHighPriority = highPriorityPatterns.some((pattern) =>
pattern.test(relativeA)
pattern.test(relativeA),
);
// Check if file B matches any high priority pattern
const bIsHighPriority = highPriorityPatterns.some((pattern) =>
pattern.test(relativeB)
pattern.test(relativeB),
);
// Sort by priority first