Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion apps/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
"test": "cd supabase/functions/api && deno test",
"status": "supabase status -o json"
},
"dependencies": {},
"devDependencies": {
"@onlook/typescript": "*",
"@types/bun": "latest",
Expand Down
2 changes: 1 addition & 1 deletion apps/web/client/next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially useful
* for Docker builds.
*/
import { NextConfig } from 'next';
import type { NextConfig } from 'next';
import createNextIntlPlugin from 'next-intl/plugin';
import path from 'node:path';
import './src/env';
Expand Down
64 changes: 50 additions & 14 deletions apps/web/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,27 @@
"dependencies": {
"@ai-sdk/provider-utils": "^3.0.10",
"@ai-sdk/react": "2.0.60",
"@codemirror/autocomplete": "^6.19.0",
"@codemirror/lang-css": "^6.2.0",
"@codemirror/lang-html": "^6.4.7",
"@codemirror/lang-javascript": "^6.2.3",
"@codemirror/lang-json": "^6.0.1",
"@codemirror/lang-markdown": "^6.1.7",
"@codemirror/language": "^6.11.3",
"@codemirror/lint": "^6.9.0",
"@codemirror/search": "^6.5.11",
"@codemirror/state": "^6.5.2",
"@codemirror/view": "^6.38.6",
"@codesandbox/sdk": "^1.1.6",
"@lezer/highlight": "^1.2.1",
"@mendable/firecrawl-js": "^1.29.1",
"@onlook/code-provider": "*",
"@onlook/constants": "*",
"@onlook/db": "*",
"@onlook/email": "*",
"@onlook/file-system": "*",
"@onlook/fonts": "*",
"@onlook/git": "*",
"@onlook/github": "*",
"@onlook/growth": "*",
"@onlook/image-server": "*",
Expand All @@ -50,10 +61,13 @@
"@onlook/stripe": "*",
"@onlook/ui": "*",
"@onlook/utility": "*",
"@onlook/web-preload": "*",
"@openrouter/ai-sdk-provider": "^1.2.0",
"@opentelemetry/api-logs": "0.57.2",
"@opentelemetry/instrumentation": "0.57.2",
"@opentelemetry/sdk-logs": "0.57.2",
"@supabase/ssr": "^0.6.1",
"@supabase/supabase-js": "^2.75.0",
"@t3-oss/env-nextjs": "^0.12.0",
"@tanstack/react-query": "^5.69.0",
"@trpc/client": "^11.0.0",
Expand All @@ -66,68 +80,90 @@
"@xterm/xterm": "^5.5.0",
"@zenfs/core": "2.4.0",
"@zenfs/dom": "1.2.5",
"ai": "5.0.26",
"ai": "5.0.60",
"blob-util": "^2.0.2",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"culori": "^4.0.1",
"date-fns": "^4.1.0",
"drizzle-orm": "^0.44.6",
"exa-js": "^1.8.26",
"flexsearch": "^0.8.160",
"framer-motion": "^12.23.24",
"freestyle-sandboxes": "^0.0.78",
"gleap": "^14.8.8",
"gpt-tokenizer": "^3.0.1",
"langfuse-vercel": "^3.38.4",
"localforage": "^1.10.0",
"lodash": "^4.17.21",
"lru-cache": "^11.2.1",
"lucide-react": "^0.486.0",
"lucide-react": "^0.544.0",
"marked": "^15.0.7",
"mime-lite": "^1.0.3",
"mobx": "^6.15.0",
"mobx-react-lite": "^4.1.0",
"motion": "^12.23.19",
"nanoid": "^5.1.6",
"next": ">=15.5.3",
"next-intl": "^4.0.2",
"next-themes": "^0.4.6",
"octokit": "^5.0.3",
"openai": "^4.103.0",
"penpal": "^7.0.4",
"pg": "^8.16.3",
"postgres": "^3.4.7",
"posthog-js": "^1.246.0",
"posthog-node": "^4.17.2",
"prettier": "^3.3.3",
"prosemirror-commands": "^1.7.1",
"prosemirror-history": "^1.4.1",
"prosemirror-keymap": "^1.2.2",
"react": "^19.0.0",
"prosemirror-model": "^1.25.3",
"prosemirror-state": "^1.4.3",
"prosemirror-view": "^1.34.2",
"react": "^19.1.13",
"react-arborist": "^3.4.3",
"react-codemirror-merge": "4.23.10",
"react-dom": "^19.0.0",
"react-dnd": "^16.0.1",
"react-dnd-html5-backend": "^16.0.1",
"react-dom": "^19.1.13",
"react-hotkeys-hook": "^5.0.1",
"react-markdown": "^10.1.0",
"remark-gfm": "^4.0.1",
"server-only": "^0.0.1",
"sharp": "^0.33.5",
"shiki": "^3.2.2",
"sonner": "^2.0.4",
"strip-ansi": "^7.1.0",
"superjson": "^2.2.1",
"tailwind-merge": "^3.2.0",
"superjson": "^2.2.2",
"tailwind-merge": "^3.3.1",
"tldts": "^7.0.12",
"tw-animate-css": "^1.2.5",
"unicornstudio-react": "^1.4.33",
"use-resize-observer": "^9.1.0",
"use-stick-to-bottom": "^1.1.1",
"uuid": "^11.1.0",
"webfontloader": "^1.6.28",
"zod": "^4.1.3"
"zod": "^4.1.8"
},
"devDependencies": {
"@eslint/eslintrc": "^3.3.1",
"@onlook/eslint": "*",
"@onlook/typescript": "*",
"@tailwindcss/postcss": "^4.0.15",
"@tailwindcss/postcss": "^4.1.5",
"@types/culori": "^4.0.0",
"@types/node": "^20.14.10",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"@types/lodash": "^4.17.16",
"@types/node": "^24.7.2",
"@types/react": "^19.1.13",
"@types/react-dom": "^19.1.13",
Comment on lines +157 to +158
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Align React type packages with the supported React major

Once we stay on React 18, the @types/react and @types/react-dom packages should also remain on the 18.x line; the 19.x typings include breaking type-level changes meant for the unreleased runtime.

Apply:

-        "@types/react": "^19.1.13",
-        "@types/react-dom": "^19.1.13",
+        "@types/react": "^18.2.49",
+        "@types/react-dom": "^18.2.19",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"@types/react": "^19.1.13",
"@types/react-dom": "^19.1.13",
"@types/react": "^18.2.49",
"@types/react-dom": "^18.2.19",
🤖 Prompt for AI Agents
In apps/web/client/package.json around lines 153-154, the project currently
depends on @types/react and @types/react-dom v19.x which are incompatible with
our React 18 runtime; change both entries to the 18.x type definitions (e.g.,
^18.2.0 or the latest 18.x patch), update the lockfile by running your package
manager install (npm/yarn/pnpm) and verify types compile locally.

"@types/webfontloader": "^1.6.38",
"eslint": "^9.0.0",
"postcss": "^8.5.3",
"tailwindcss": "^4.0.15",
"tailwindcss": "^4.1.5",
"type-fest": "^4.41.0",
"typescript": "^5.5.4"
"typescript": "^5.8.2"
},
"ct3aMetadata": {
"initVersion": "7.39.2"
}
}
}
28 changes: 14 additions & 14 deletions apps/web/client/public/onlook-preload-script.js

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions apps/web/client/src/app/api/chat/route.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { api } from '@/trpc/server';
import { trackEvent } from '@/utils/analytics/server';
import { createRootAgentStream } from '@onlook/ai';
import { toDbMessage } from '@onlook/db';
import { ChatType, type ChatMessage, type ChatMetadata } from '@onlook/models';
import { createRootAgentStream } from '@/lib/ai';
import { toDbMessage } from '@/lib/ai/chat/mappers/message';
import { ChatType, type ChatMessage, type ChatMetadata } from '@/lib/ai/models/chat';
import { type NextRequest } from 'next/server';
import { v4 as uuidv4 } from 'uuid';
import { checkMessageLimit, decrementUsage, errorHandler, getSupabaseUser, incrementUsage } from './helpers';
Expand Down
2 changes: 2 additions & 0 deletions apps/web/client/src/app/callback/github/install/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ export default function GitHubInstallCallbackPage() {
const handleInstallationCallback = api.github.handleInstallationCallbackUrl.useMutation();

useEffect(() => {
if (!searchParams) return;

const installationId = searchParams.get('installation_id');
const setupAction = searchParams.get('setup_action');
const stateParam = searchParams.get('state');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const HandleAuth = () => {
const searchParams = useSearchParams();

const handleLogin = () => {
const currentUrl = `${pathname}${searchParams.toString() ? `?${searchParams.toString()}` : ''}`;
const currentUrl = `${pathname}${searchParams && searchParams.toString() ? `?${searchParams.toString()}` : ''}`;
router.push(`${Routes.LOGIN}?${getReturnUrlQueryParam(currentUrl)}`);
}

Expand Down
4 changes: 2 additions & 2 deletions apps/web/client/src/app/invitation/[id]/_components/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export function Main({ invitationId }: { invitationId: string }) {
const router = useRouter();
const pathname = usePathname();
const searchParams = useSearchParams();
const token = useSearchParams().get('token');
const token = searchParams?.get('token') ?? null;
const { data: invitation, isLoading: loadingInvitation, error: getInvitationError } = api.invitation.getWithoutToken.useQuery({
id: invitationId,
});
Expand All @@ -35,7 +35,7 @@ export function Main({ invitationId }: { invitationId: string }) {
// Clear analytics/feedback identities before signing out
void resetTelemetry();
await supabase.auth.signOut();
const currentUrl = `${pathname}${searchParams.toString() ? `?${searchParams.toString()}` : ''}`;
const currentUrl = `${pathname}${searchParams && searchParams.toString() ? `?${searchParams.toString()}` : ''}`;
router.push(`${Routes.LOGIN}?${getReturnUrlQueryParam(currentUrl)}`);
}

Expand Down
3 changes: 2 additions & 1 deletion apps/web/client/src/app/login/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ export default function LoginPage() {
const isDev = process.env.NODE_ENV === 'development';
const t = useTranslations();
const backgroundUrl = useGetBackground('login');
const returnUrl = useSearchParams().get(LocalForageKeys.RETURN_URL);
const searchParams = useSearchParams();
const returnUrl = searchParams?.get(LocalForageKeys.RETURN_URL) ?? null;

return (
<div className="flex h-screen w-screen justify-center">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,16 @@ import { useEditorEngine } from '@/components/store/editor';
import type { Frame } from '@onlook/models';
import {
PENPAL_PARENT_CHANNEL,
type PenpalChildMethods,
type PenpalParentMethods,
type PromisifiedPendpalChildMethods,
} from '@onlook/penpal';
import type { PenpalChildMethods } from '@onlook/web-preload/script/api';

// Promisified version of PenpalChildMethods for async RPC calls
type PromisifiedPendpalChildMethods = {
[K in keyof PenpalChildMethods]: (
...args: Parameters<PenpalChildMethods[K]>
) => Promise<ReturnType<PenpalChildMethods[K]>>;
};
import { WebPreview, WebPreviewBody } from '@onlook/ui/ai-elements';
import { cn } from '@onlook/ui/utils';
import { observer } from 'mobx-react-lite';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useEditorEngine } from '@/components/store/editor';
import { transKeys } from '@/i18n/keys';
import { ChatType } from '@onlook/models';
import { ChatType } from '@/lib/ai/models/chat'
import { Button } from '@onlook/ui/button';
import { Icons } from '@onlook/ui/icons';
import { toast } from '@onlook/ui/sonner';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { EditorView, keymap, ViewUpdate } from '@codemirror/view';
import type { CodeNavigationTarget } from '@onlook/models';
import { convertToBase64DataUrl, getMimeType } from '@onlook/utility/src/file';
import { convertToBase64DataUrl, getMimeType, isVideoFile } from '@onlook/utility';
import CodeMirror from '@uiw/react-codemirror';
import { type RefObject, useEffect, useMemo, useRef, useState } from 'react';
import type { BinaryEditorFile, EditorFile } from '../shared/types';
Expand Down Expand Up @@ -170,11 +170,23 @@ export const CodeEditor = ({
}}
>
{file.type === 'binary' && (
<img
src={getFileUrl(file as BinaryEditorFile)}
alt={file.path}
className="w-full h-full object-contain p-5"
/>
<>
{isVideoFile(file.path) ? (
<video
src={getFileUrl(file as BinaryEditorFile)}
controls
className="w-full h-full object-contain p-5"
>
Your browser does not support the video tag.
</video>
) : (
<img
src={getFileUrl(file as BinaryEditorFile)}
alt={file.path}
className="w-full h-full object-contain p-5"
/>
)}
</>
)}
{file.type === 'text' && typeof file.content === 'string' && (
<>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ interface CodeControlsProps {
currentPath: string;
onSave: () => Promise<void>;
onRefresh: () => void;
onCreateFile: (filePath: string, content?: string) => Promise<void>;
onCreateFile: (filePath: string, content?: string | Uint8Array) => Promise<void>;
onCreateFolder: (folderPath: string) => Promise<void>;
isSidebarOpen: boolean;
setIsSidebarOpen: (isSidebarOpen: boolean) => void;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export interface CodeTabRef {
getCurrentPath: () => string;
handleSaveFile: () => Promise<void>;
refreshFileTree: () => void;
handleCreateFile: (filePath: string, content?: string) => Promise<void>;
handleCreateFile: (filePath: string, content?: string | Uint8Array) => Promise<void>;
handleCreateFolder: (folderPath: string) => Promise<void>;
}

Expand Down Expand Up @@ -415,7 +415,7 @@ export const CodeTab = memo(forwardRef<CodeTabRef, CodeTabProps>(({ projectId, b
);
};

const handleCreateFile = async (filePath: string, content: string = '') => {
const handleCreateFile = async (filePath: string, content: string | Uint8Array = '') => {
if (!branchData) {
throw new Error('Branch data not found');
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { Input } from '@onlook/ui/input';
import { Label } from '@onlook/ui/label';
import { toast } from '@onlook/ui/sonner';
import { cn } from '@onlook/ui/utils';
import { isBinaryFile } from '@onlook/utility';
import path from 'path';
import { useCallback, useEffect, useState } from 'react';

Expand All @@ -19,7 +20,7 @@ interface UploadModalProps {
show: boolean;
setShow: (show: boolean) => void;
onSuccess?: () => void;
onCreateFile: (filePath: string, content?: string) => Promise<void>;
onCreateFile: (filePath: string, content?: string | Uint8Array) => Promise<void>;
}

export const UploadModal = ({
Expand Down Expand Up @@ -75,13 +76,27 @@ export const UploadModal = ({

const fileName = file.name;
const fullPath = path.join(currentPath, fileName).replace(/\\/g, '/');
const isBinary = isBinaryFile(fileName);

// Read file content
const content = await new Promise<string>((resolve, reject) => {
const content = await new Promise<string | Uint8Array>((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result as string);
reader.onload = () => {
if (isBinary) {
// For binary files, convert ArrayBuffer to Uint8Array
resolve(new Uint8Array(reader.result as ArrayBuffer));
} else {
// For text files, use string result
resolve(reader.result as string);
}
};
reader.onerror = () => reject(reader.error);
reader.readAsText(file);

// Use appropriate read method
if (isBinary) {
reader.readAsArrayBuffer(file);
} else {
reader.readAsText(file);
}
});

await onCreateFile(fullPath, content);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ export const FileTree = ({
onRefresh={onRefresh}
onKeyDown={handleKeyDown}
/>
<div ref={treeContainerRef} className="w-full text-xs px-2 flex-1 min-h-0">
<div ref={treeContainerRef as React.Ref<HTMLDivElement>} className="w-full text-xs px-2 flex-1 min-h-0">
{isLoading ? (
<div className="flex flex-col justify-start items-center h-full text-sm text-foreground/50 pt-4">
<div className="animate-spin h-6 w-6 border-2 border-foreground-hover rounded-full border-t-transparent mb-2"></div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export const ImageGrid = ({ images, projectId, branchId, search, onUpload, onRen
<div className="flex flex-col items-center justify-center py-8 text-foreground-secondary">
<Icons.Image className="w-8 h-8 mb-2" />
<div className="text-sm">
{search ? 'No images match your search' : 'No images in this folder'}
{search ? 'No images or videos match your search' : 'No images or videos in this folder'}
</div>
</div>
)}
Expand Down
Loading