Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
150 commits
Select commit Hold shift + click to select a range
9667f2b
move reconnct
Kitenite Sep 26, 2025
5bdf732
wip: integrate new fs system
Kitenite Sep 26, 2025
a1e9a3f
save sandbox setup
Kitenite Sep 26, 2025
ad812f8
saving
Kitenite Sep 26, 2025
c15304a
refactor code tab
Kitenite Sep 26, 2025
c9cc777
restore file tree functionality
Kitenite Sep 26, 2025
7967d5f
file tree update
Kitenite Sep 26, 2025
5cd47ae
selecting file
Kitenite Sep 26, 2025
e6669c8
better hover
Kitenite Sep 26, 2025
fdd56dc
refactor
Kitenite Sep 26, 2025
8287347
more refactor and cleanup
Kitenite Sep 26, 2025
71f4683
update viewing
Kitenite Sep 26, 2025
22c3783
working file tree
Kitenite Sep 26, 2025
c44beaf
flatten file tree
Kitenite Sep 26, 2025
282cc83
remove tree node
Kitenite Sep 26, 2025
25726e6
fix icons
Kitenite Sep 26, 2025
07248c3
use local fs
Kitenite Sep 26, 2025
b569aeb
clean up file node
Kitenite Sep 26, 2025
796a5bc
more cleanup
Kitenite Sep 26, 2025
a89b5e5
remove load modal
Kitenite Sep 26, 2025
bc6da3c
save
Kitenite Sep 26, 2025
2dcc8ee
working file writing
Kitenite Sep 26, 2025
aae7228
discard changes
Kitenite Sep 26, 2025
9f9d70c
use file paths
Kitenite Sep 26, 2025
79d0b79
better pathing
Kitenite Sep 26, 2025
1669910
clean rerender
Kitenite Sep 26, 2025
11b50d2
merge main
Kitenite Sep 27, 2025
3a4eb51
fix: remove duplicate condition check in handleToolCall (#2929)
anand-106 Sep 27, 2025
a8364cc
merge main
Kitenite Sep 27, 2025
e45e941
remove dead code
Kitenite Sep 27, 2025
898d104
remove more dead code
Kitenite Sep 27, 2025
2c9c76e
clean up ide manager
Kitenite Sep 28, 2025
bd882ac
reduce ide manager to nothing
Kitenite Sep 28, 2025
a869bb9
slightly unfuck the image tab
Kitenite Sep 28, 2025
3794efb
use fs
Kitenite Sep 28, 2025
a11e57d
restore styling
Kitenite Sep 28, 2025
d519aba
simplify image tab
Kitenite Sep 28, 2025
e2509c8
simplified image
Kitenite Sep 28, 2025
c9e2294
add image viewer
Kitenite Sep 28, 2025
2ce9005
handle svg
Kitenite Sep 28, 2025
87ef4a9
handle filtering
Kitenite Sep 28, 2025
30c9fdb
refactor
Kitenite Sep 29, 2025
f759b3e
refactor
Kitenite Sep 29, 2025
7cdb8f7
add kinda dragging
Kitenite Sep 29, 2025
8f8503e
working image transfer
Kitenite Sep 29, 2025
f39e645
file url instead
Kitenite Sep 29, 2025
eb3b63d
animate sidebar
Kitenite Sep 29, 2025
b5e2276
styling
Kitenite Sep 29, 2025
4edd7b6
add ide delete rename
Kitenite Sep 29, 2025
2791932
working rename and delete
Kitenite Sep 29, 2025
b98f10d
working not rerender
Kitenite Sep 29, 2025
acfc83c
input focus
Kitenite Sep 29, 2025
adbc0d8
small ui fix
Kitenite Sep 29, 2025
7bc2cc0
update
Kitenite Sep 29, 2025
3269ac3
code controls
Kitenite Sep 29, 2025
245fd5e
update controls
Kitenite Sep 29, 2025
7a965ab
update controls
Kitenite Sep 29, 2025
676a005
working create and upload
Kitenite Sep 29, 2025
3719653
warn before delete
Kitenite Sep 29, 2025
114e496
file icon
Kitenite Sep 29, 2025
bcd9197
update tools
Kitenite Sep 30, 2025
4c610d8
fixing fonts
Kitenite Sep 30, 2025
94033f2
read file tool
Kitenite Sep 30, 2025
f2cd7da
fix typechecks
Kitenite Sep 30, 2025
0d770c7
Merge branch 'main' into feat/integrate-sync-eng
Kitenite Sep 30, 2025
9f5016e
mock root layout path
Kitenite Sep 30, 2025
10a7c79
working pages
Kitenite Sep 30, 2025
17e0a1f
using layouts
Kitenite Sep 30, 2025
b4f0fc9
scan pages
Kitenite Sep 30, 2025
12a4720
working pages tab
Kitenite Sep 30, 2025
5661778
working fonts setup
Kitenite Sep 30, 2025
ed62540
working fonts
Kitenite Sep 30, 2025
14cebae
cleaning up
Kitenite Sep 30, 2025
10d5b76
Push plan for CodeEditorApi
saddlepaddle Sep 29, 2025
3816d2f
WIP
saddlepaddle Sep 30, 2025
0d50998
WIP
saddlepaddle Oct 1, 2025
c4ad1b4
WIP - forking working
saddlepaddle Oct 1, 2025
52c0f85
WIP - kinda working
saddlepaddle Oct 1, 2025
ae9996a
WIP - trying to fix sidepanel
saddlepaddle Oct 1, 2025
79e4ff0
WIP - at least it works
saddlepaddle Oct 1, 2025
ad4efa9
WIP - need to find why new project flow is broken
saddlepaddle Oct 1, 2025
c5aee59
WIP
saddlepaddle Oct 1, 2025
5b6d2b6
WIP
saddlepaddle Oct 1, 2025
1755fce
Few more issues
saddlepaddle Oct 1, 2025
97f5b22
Ensure injection only ran once
saddlepaddle Oct 1, 2025
ee0690a
Kinda working - saw some sandboxes not load but clicking is now immed…
saddlepaddle Oct 1, 2025
31bad4b
Updated a bunch to include branchId for operations
saddlepaddle Oct 1, 2025
ebfff2c
Handle paths correctly in fs, start fixing downstream consumers to fi…
saddlepaddle Oct 1, 2025
46c5211
Ensure code highlighting still works
saddlepaddle Oct 1, 2025
a435fb0
Fix fonts by ensuring we write first to local state
saddlepaddle Oct 1, 2025
656d134
Ensure file names are sanitized when users upload files
saddlepaddle Oct 2, 2025
905b611
Found issue - need to use code-editor-api not fs
saddlepaddle Oct 2, 2025
dfce627
Replace usages of fs
saddlepaddle Oct 2, 2025
7a5620b
WIP - fixes for the missing page.tsx + not being able to click on ele…
saddlepaddle Oct 2, 2025
7bf1fbf
merge main
Kitenite Oct 3, 2025
8716eb4
highlight styling
Kitenite Oct 3, 2025
61601a6
highlight on correct file
Kitenite Oct 3, 2025
cc30779
fix frame loading
Kitenite Oct 3, 2025
8acb3fa
Merge branch 'main' into feat/integrate-sync-eng
Kitenite Oct 3, 2025
b7e7426
refactor preload script
Kitenite Oct 3, 2025
7beb5ad
font scan
Kitenite Oct 3, 2025
fb270ac
handle code controls
Kitenite Oct 3, 2025
ba406a3
use correct file system
Kitenite Oct 3, 2025
0d91716
use router config and better init
Kitenite Oct 3, 2025
4105a34
refactor create branch
Kitenite Oct 3, 2025
505a6c6
only use code fs
Kitenite Oct 3, 2025
52b4c77
clean up
Kitenite Oct 4, 2025
03879ef
fix dnd backend crash react-arbonist
Kitenite Oct 4, 2025
13eb3af
save file
Kitenite Oct 4, 2025
b04152e
handle reconnect
Kitenite Oct 4, 2025
b0ba790
handle reconnect
Kitenite Oct 4, 2025
b46bba8
clean up
Kitenite Oct 4, 2025
c8ddcb7
update tests
Kitenite Oct 4, 2025
680c59c
update ui
Kitenite Oct 4, 2025
b83e7e6
fix tests
Kitenite Oct 4, 2025
d92dae5
fix tests
Kitenite Oct 4, 2025
e520ad8
remove tests
Kitenite Oct 4, 2025
50f1e55
remove test routes
Kitenite Oct 4, 2025
abb4e4c
clean up
Kitenite Oct 4, 2025
b839148
clean up
Kitenite Oct 4, 2025
1323252
clamp highlight
Kitenite Oct 4, 2025
1726923
remove unused functions
Kitenite Oct 4, 2025
dd32ecd
update file icon extension
Kitenite Oct 4, 2025
9c8c941
clean up
Kitenite Oct 4, 2025
120bb4e
copy dir
Kitenite Oct 5, 2025
cb6e5a6
copy dir
Kitenite Oct 5, 2025
bf0da9e
bulk write files
Kitenite Oct 5, 2025
4efd3dc
fix preload script location
Kitenite Oct 5, 2025
91debaa
fix tests
Kitenite Oct 5, 2025
59d6053
fix type
Kitenite Oct 5, 2025
cc89adc
fix type
Kitenite Oct 5, 2025
3053de1
keep index in memory
Kitenite Oct 5, 2025
13e6d0a
working component detection
Kitenite Oct 5, 2025
c9b1c57
needs fixing idemanager
Kitenite Oct 5, 2025
098e25d
needs fixing idemanager
Kitenite Oct 5, 2025
5d106d8
allow overriding
Kitenite Oct 5, 2025
1aa23e8
allow overrides
Kitenite Oct 5, 2025
032bb8d
update
Kitenite Oct 5, 2025
336dcc7
document the ast algorithm
Kitenite Oct 5, 2025
a4fc99f
update void
Kitenite Oct 5, 2025
f67c75b
update use-file
Kitenite Oct 5, 2025
e38d38f
clean up
Kitenite Oct 5, 2025
d1034cb
rebuild index
Kitenite Oct 5, 2025
c220663
get preload script
Kitenite Oct 5, 2025
355299f
add snappiness
Kitenite Oct 5, 2025
b11b001
snapshot commit preload script
Kitenite Oct 5, 2025
29a3ba8
deprecate main cdn
Kitenite Oct 5, 2025
c2a0f36
update template for new preload url
Kitenite Oct 6, 2025
5012335
fix test and login button
Kitenite Oct 6, 2025
6148c01
DRY
Kitenite Oct 6, 2025
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
18 changes: 9 additions & 9 deletions apps/web/client/public/onlook-preload-script.js

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -110,20 +110,21 @@ export const BrandTab = observer(() => {
}
};

// Listen for file changes in the sandbox
const unsubscribe = editorEngine.activeSandbox.fileEventBus.subscribe('*', (event) => {
// Check if any of the changed files are Tailwind config files
const isTailwindConfigChange = event.paths.some(path =>
path.includes('tailwind.config') || path.includes('globals.css')
);

if (isTailwindConfigChange && event.paths[0]) {
handleFileChange(event.paths[0]);
}
});
// TODO: use fs hook
// // Listen for file changes in the sandbox
// const unsubscribe = editorEngine.activeSandbox.fileEventBus.subscribe('*', (event) => {
// // Check if any of the changed files are Tailwind config files
// const isTailwindConfigChange = event.paths.some(path =>
// path.includes('tailwind.config') || path.includes('globals.css')
// );

// if (isTailwindConfigChange && event.paths[0]) {
// handleFileChange(event.paths[0]);
// }
// });

return () => {
unsubscribe();
// unsubscribe();
};
}, [editorEngine.theme, editorEngine.activeSandbox]);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getExtensions } from '@/app/project/[id]/_components/right-panel/code-tab/code-mirror-config';
import { getExtensions } from '@/app/project/[id]/_components/right-panel/code-tab/file-content/code-mirror-config';
import { SystemTheme } from '@onlook/models';
import { cn } from '@onlook/ui/utils';
import { basicSetup } from '@uiw/codemirror-extensions-basic-setup';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { SystemTheme } from '@onlook/models';
import { useTheme } from 'next-themes';
import CodeMirrorMerge from 'react-codemirror-merge';
import { getExtensions } from '../../code-tab/code-mirror-config';
import { getExtensions } from '../../code-tab/file-content/code-mirror-config';
interface CodeDiffProps {
originalCode: string;
modifiedCode: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
import { Icons } from '@onlook/ui/icons';
import { Tooltip, TooltipContent, TooltipTrigger } from '@onlook/ui/tooltip';
import { cn } from '@onlook/ui/utils';
import { TooltipArrow } from '@radix-ui/react-tooltip';
import { observer } from 'mobx-react-lite';
import { FileModal } from './modals/file-modal';
import { FolderModal } from './modals/folder-modal';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { useEditorEngine } from '@/components/store/editor';
import type { EditorFile } from '@/components/store/editor/ide';
import { EditorView } from '@codemirror/view';
import CodeMirror from '@uiw/react-codemirror';
import { observer } from 'mobx-react-lite';
import { getBasicSetup, getExtensions } from './code-mirror-config';

interface CodeEditorProps {
file: EditorFile;
isActive: boolean;
editorViewsRef: React.MutableRefObject<Map<string, EditorView>>;
onSaveFile: () => Promise<void>;
onUpdateFileContent: (fileId: string, content: string) => void;
onGetFileUrl: (file: EditorFile) => string;
}

export const CodeEditor = observer(({
file,
isActive,
editorViewsRef,
onSaveFile,
onUpdateFileContent,
onGetFileUrl
}: CodeEditorProps) => {
const editorEngine = useEditorEngine();
const ide = editorEngine.ide;

return (
<div
className="h-full"
style={{
display: isActive ? 'block' : 'none',
}}
>
{file.isBinary ? (
<img
src={onGetFileUrl(file)}
alt={file.filename}
className="w-full h-full object-contain p-5"
/>
) : (
<CodeMirror
key={file.id}
value={file.content}
height="100%"
theme="dark"
extensions={[
...getBasicSetup(onSaveFile),
...getExtensions(file.language),
]}
onChange={(value) => {
if (ide.highlightRange) {
ide.setHighlightRange(null);
}
onUpdateFileContent(file.id, value);
}}
className="h-full overflow-hidden"
onCreateEditor={(editor) => {
editorViewsRef.current.set(file.id, editor);

editor.dom.addEventListener('mousedown', () => {
if (ide.highlightRange) {
ide.setHighlightRange(null);
}
});

// If this file is the active file and we have a highlight range,
// trigger the highlight effect again
if (
ide.activeFile &&
ide.activeFile.id === file.id &&
ide.highlightRange
) {
setTimeout(() => {
if (ide.highlightRange) {
ide.setHighlightRange(ide.highlightRange);
}
}, 300);
}
}}
/>
)}
</div>
);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { useEditorEngine } from '@/components/store/editor';
import type { EditorFile } from '@/components/store/editor/ide';
import { EditorView } from '@codemirror/view';
import { observer } from 'mobx-react-lite';
import { useRef } from 'react';
import { CodeEditor } from './code-editor';
import { UnsavedChangesDialog } from './unsaved-changes-dialog';

interface CodeEditorAreaProps {
editorViewsRef: React.MutableRefObject<Map<string, EditorView>>;
onSaveFile: () => Promise<void>;
onUpdateFileContent: (fileId: string, content: string) => void;
onDiscardChanges: (fileId: string) => Promise<void>;
onGetFileUrl: (file: EditorFile) => string;
}

export const CodeEditorArea = observer(({
editorViewsRef,
onSaveFile,
onUpdateFileContent,
onDiscardChanges,
onGetFileUrl
}: CodeEditorAreaProps) => {
const editorEngine = useEditorEngine();
const ide = editorEngine.ide;
const editorContainer = useRef<HTMLDivElement | null>(null);

return (
<div className="flex-1 relative overflow-hidden">
{ide.isLoading && (
<div className="absolute inset-0 bg-background/50 flex items-center justify-center z-10">
<div className="flex flex-col items-center">
<div className="animate-spin h-8 w-8 border-2 border-foreground-hover rounded-full border-t-transparent"></div>
<span className="mt-2 text-sm">Loading file...</span>
</div>
</div>
)}

<div ref={editorContainer} className="h-full">
{/* Empty state when no file is selected */}
{ide.openedFiles.length === 0 || !ide.activeFile ? (
<div className="absolute inset-0 flex items-center justify-center z-10">
<div className="text-center text-muted-foreground text-base">
Open a file or select an element on the page.
</div>
</div>
) : (
ide.openedFiles.map((file) => (
<CodeEditor
key={file.id}
file={file}
isActive={ide.activeFile?.id === file.id}
editorViewsRef={editorViewsRef}
onSaveFile={onSaveFile}
onUpdateFileContent={onUpdateFileContent}
onGetFileUrl={onGetFileUrl}
/>
))
)}
</div>

{/* Unsaved Changes Dialog */}
{ide.activeFile?.isDirty && ide.showUnsavedDialog && (
<UnsavedChangesDialog
onSave={onSaveFile}
onDiscard={() => onDiscardChanges(ide.activeFile!.id)}
onCancel={() => {
ide.showUnsavedDialog = false;
ide.pendingCloseAll = false;
}}
/>
)}
</div>
);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Button } from '@onlook/ui/button';

interface UnsavedChangesDialogProps {
onSave: () => Promise<void>;
onDiscard: () => Promise<void>;
onCancel: () => void;
}

export function UnsavedChangesDialog({ onSave, onDiscard, onCancel }: UnsavedChangesDialogProps) {
return (
<div className="absolute top-4 left-1/2 z-50 -translate-x-1/2 bg-white dark:bg-zinc-800 border dark:border-zinc-700 shadow-lg rounded-lg p-4 w-[320px]">
<div className="text-sm text-gray-800 dark:text-gray-100 mb-4">
You have unsaved changes. Are you sure you want to close this file?
</div>
<div className="flex justify-end gap-1">
<Button
onClick={onDiscard}
variant="ghost"
className="text-red hover:text-red"
>
Discard
</Button>
<Button
onClick={onSave}
variant="ghost"
className="text-sm text-blue-500 hover:text-blue-500"
>
Save
</Button>
<Button
variant="ghost"
onClick={onCancel}
>
Cancel
</Button>
</div>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Icons } from '@onlook/ui/icons';
import { cn } from '@onlook/ui/utils';
import React from 'react';

export interface FileTabProps {
filename: string;
Expand All @@ -11,14 +10,14 @@ export interface FileTabProps {
'data-active'?: boolean;
}

export const FileTab: React.FC<FileTabProps> = ({
export const FileTab = ({
filename,
isActive = false,
isDirty = false,
onClick,
onClose,
'data-active': dataActive,
}) => {
}: FileTabProps) => {
return (
<div className="h-full pl-3 pr-3 relative group" data-active={dataActive}>
<div className="absolute right-0 h-[50%] w-[0.5px] bg-foreground/10 top-1/2 -translate-y-1/2"></div>
Expand All @@ -27,7 +26,7 @@ export const FileTab: React.FC<FileTabProps> = ({
className={cn(
'text-sm h-full flex items-center focus:outline-none max-w-[150px]',
isActive
? isDirty
? isDirty
? 'text-teal-300'
: 'text-foreground'
: isDirty
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
'use client'

import type { EditorFile } from '@/components/store/editor/ide';
import { Button } from '@onlook/ui/button';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger
} from '@onlook/ui/dropdown-menu';
import { Icons } from '@onlook/ui/icons';
import { Tooltip, TooltipContent, TooltipTrigger } from '@onlook/ui/tooltip';
import { observer } from 'mobx-react-lite';
import { forwardRef } from 'react';
import { FileTab } from './file-tab';

interface FileTabsProps {
openedFiles: EditorFile[];
activeFile: EditorFile | null;
isFilesVisible: boolean;
onToggleFilesVisible: () => void;
onFileSelect: (file: EditorFile) => void;
onCloseFile: (fileId: string) => void;
onCloseAllFiles: () => void;
}

export const FileTabs = observer(forwardRef<HTMLDivElement, FileTabsProps>(({
openedFiles,
activeFile,
isFilesVisible,
onToggleFilesVisible,
onFileSelect,
onCloseFile,
onCloseAllFiles,
}, ref) => {
return (
<div className="flex items-center justify-between h-11 pl-0 border-b-[0.5px] flex-shrink-0 relative">
<div className="absolute left-0 top-0 bottom-0 z-20 border-r-[0.5px] h-full flex items-center p-1 bg-background">
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
onClick={onToggleFilesVisible}
className="text-muted-foreground hover:text-foreground"
>
{isFilesVisible ? <Icons.SidebarLeftCollapse /> : <Icons.SidebarLeftExpand />}
</Button>
</TooltipTrigger>
<TooltipContent side="bottom" className="mt-1" hideArrow>
{isFilesVisible ? 'Collapse sidebar' : 'Expand sidebar'}
</TooltipContent>
</Tooltip>
</div>
<div className="absolute right-0 top-0 bottom-0 z-20 flex items-center h-full border-l-[0.5px] p-1 bg-background w-11">
<DropdownMenu>
<DropdownMenuTrigger className="text-muted-foreground hover:text-foreground hover:bg-foreground/5 p-1 rounded h-full w-full flex items-center justify-center px-2.5">
<Icons.DotsHorizontal className="h-4 w-4" />
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="-mt-1">
<DropdownMenuItem
onClick={() => activeFile && onCloseFile(activeFile.id)}
disabled={!activeFile}
className="cursor-pointer"
>
Close file
</DropdownMenuItem>
<DropdownMenuItem
onClick={onCloseAllFiles}
disabled={openedFiles.length === 0}
className="cursor-pointer"
>
Close all
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
<div className="flex items-center h-full overflow-x-auto w-full ml-11 mr-10.5" ref={ref}>
{openedFiles.map((file) => (
<FileTab
key={file.id}
filename={file.filename}
isActive={activeFile?.id === file.id}
isDirty={file.isDirty}
onClick={() => onFileSelect(file)}
onClose={() => onCloseFile(file.id)}
data-active={activeFile?.id === file.id}
/>
))}
</div>
</div>
);
}));

FileTabs.displayName = 'FileTabs';
Loading
Loading