├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── help_wanted.md ├── dependabot.yml ├── pull_request_template.md └── workflows │ └── supabase-migrate-staging.yml ├── .gitignore ├── .husky └── pre-commit ├── .prettierignore ├── .prettierrc ├── .vscode ├── .debug.script.mjs ├── extensions.json ├── launch.json └── tasks.json ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── SECURITY.md ├── apps ├── backend │ ├── .env.example │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── supabase │ │ ├── .gitignore │ │ ├── config.toml │ │ ├── functions │ │ │ ├── api │ │ │ │ ├── .npmrc │ │ │ │ ├── deno.json │ │ │ │ └── index.ts │ │ │ ├── shared │ │ │ │ ├── .npmrc │ │ │ │ ├── auth.ts │ │ │ │ ├── deno.json │ │ │ │ └── error.ts │ │ │ └── stripe-webhook │ │ │ │ ├── .npmrc │ │ │ │ ├── deno.json │ │ │ │ ├── deno.lock │ │ │ │ └── index.ts │ │ └── migrations │ │ │ ├── 0000_same_human_robot.sql │ │ │ ├── 0001_graceful_exodus.sql │ │ │ ├── 0002_red_crusher_hogan.sql │ │ │ ├── 0003_loud_ozymandias.sql │ │ │ ├── 0004_pink_expediter.sql │ │ │ ├── 0005_short_lila_cheney.sql │ │ │ ├── 0006_rls.sql │ │ │ ├── 0007_realtime_rls.sql │ │ │ └── meta │ │ │ ├── 0000_snapshot.json │ │ │ ├── 0001_snapshot.json │ │ │ ├── 0002_snapshot.json │ │ │ ├── 0003_snapshot.json │ │ │ ├── 0004_snapshot.json │ │ │ ├── 0005_snapshot.json │ │ │ ├── 0006_snapshot.json │ │ │ ├── 0007_snapshot.json │ │ │ └── _journal.json │ └── tsconfig.json └── web │ ├── .gitignore │ ├── README.md │ ├── client │ ├── .env.example │ ├── .gitignore │ ├── .prettierrc │ ├── README.md │ ├── components.json │ ├── eslint.config.js │ ├── messages │ │ ├── en.json │ │ ├── ja.json │ │ ├── ko.json │ │ └── zh.json │ ├── next.config.ts │ ├── package.json │ ├── postcss.config.js │ ├── prettier.config.js │ ├── public │ │ ├── assets │ │ │ ├── dunes-create-dark.png │ │ │ ├── dunes-create-light.png │ │ │ ├── dunes-login-dark.png │ │ │ ├── dunes-login-light.png │ │ │ ├── mountains.png │ │ │ └── new-yt-thumbnail.png │ │ └── favicon.ico │ ├── src │ │ ├── app │ │ │ ├── _components │ │ │ │ ├── auth-modal.tsx │ │ │ │ ├── button-link.tsx │ │ │ │ ├── hero │ │ │ │ │ ├── create-error.tsx │ │ │ │ │ ├── create.tsx │ │ │ │ │ ├── high-demand.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── unicorn-background.tsx │ │ │ │ ├── landing-page │ │ │ │ │ ├── code-one-to-one-section.tsx │ │ │ │ │ ├── contributor-section.tsx │ │ │ │ │ ├── cta-section.tsx │ │ │ │ │ ├── faq-dropdown.tsx │ │ │ │ │ ├── faq-section.tsx │ │ │ │ │ ├── features-section.tsx │ │ │ │ │ ├── mock-layers-tab.tsx │ │ │ │ │ ├── page-footer.tsx │ │ │ │ │ ├── testimonial-card.tsx │ │ │ │ │ ├── testimonials-section.tsx │ │ │ │ │ └── what-can-onlook-do-section.tsx │ │ │ │ ├── login-button.tsx │ │ │ │ ├── theme.tsx │ │ │ │ └── top-bar │ │ │ │ │ ├── github.tsx │ │ │ │ │ └── index.tsx │ │ │ ├── api │ │ │ │ ├── chat │ │ │ │ │ └── route.ts │ │ │ │ └── trpc │ │ │ │ │ └── [trpc] │ │ │ │ │ └── route.ts │ │ │ ├── auth │ │ │ │ ├── auth-context.tsx │ │ │ │ ├── callback │ │ │ │ │ └── route.ts │ │ │ │ └── redirect │ │ │ │ │ └── page.tsx │ │ │ ├── faq │ │ │ │ └── page.tsx │ │ │ ├── fonts.ts │ │ │ ├── invitation │ │ │ │ └── [id] │ │ │ │ │ ├── _components │ │ │ │ │ └── main.tsx │ │ │ │ │ ├── layout.tsx │ │ │ │ │ └── page.tsx │ │ │ ├── layout.tsx │ │ │ ├── login │ │ │ │ ├── actions.tsx │ │ │ │ ├── error.tsx │ │ │ │ └── page.tsx │ │ │ ├── not-found.tsx │ │ │ ├── page.tsx │ │ │ ├── privacy-policy │ │ │ │ └── page.tsx │ │ │ ├── project │ │ │ │ └── [id] │ │ │ │ │ ├── _components │ │ │ │ │ ├── bottom-bar │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ ├── terminal-area.tsx │ │ │ │ │ │ └── terminal.tsx │ │ │ │ │ ├── canvas │ │ │ │ │ │ ├── frame │ │ │ │ │ │ │ ├── gesture.tsx │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ ├── resize-handles.tsx │ │ │ │ │ │ │ ├── right-click.tsx │ │ │ │ │ │ │ ├── top-bar.tsx │ │ │ │ │ │ │ └── web-frame.tsx │ │ │ │ │ │ ├── frames.tsx │ │ │ │ │ │ ├── hotkeys │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── overlay │ │ │ │ │ │ │ ├── elements │ │ │ │ │ │ │ ├── chat.tsx │ │ │ │ │ │ │ ├── measurement.tsx │ │ │ │ │ │ │ ├── rect │ │ │ │ │ │ │ │ ├── base.tsx │ │ │ │ │ │ │ │ ├── click.tsx │ │ │ │ │ │ │ │ ├── hover.tsx │ │ │ │ │ │ │ │ ├── insert.tsx │ │ │ │ │ │ │ │ └── resize.tsx │ │ │ │ │ │ │ └── text.tsx │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ └── pan.tsx │ │ │ │ │ ├── editor-bar │ │ │ │ │ │ ├── div-selected.tsx │ │ │ │ │ │ ├── dropdowns │ │ │ │ │ │ │ ├── border-color.tsx │ │ │ │ │ │ │ ├── border.tsx │ │ │ │ │ │ │ ├── color-background.tsx │ │ │ │ │ │ │ ├── display │ │ │ │ │ │ │ │ ├── align.tsx │ │ │ │ │ │ │ │ ├── direction.tsx │ │ │ │ │ │ │ │ ├── gap.tsx │ │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ │ └── type.tsx │ │ │ │ │ │ │ ├── height.tsx │ │ │ │ │ │ │ ├── img-background.tsx │ │ │ │ │ │ │ ├── img-fit.tsx │ │ │ │ │ │ │ ├── margin.tsx │ │ │ │ │ │ │ ├── opacity.tsx │ │ │ │ │ │ │ ├── padding.tsx │ │ │ │ │ │ │ ├── radius.tsx │ │ │ │ │ │ │ ├── state-dropdown.tsx │ │ │ │ │ │ │ └── width.tsx │ │ │ │ │ │ ├── hooks │ │ │ │ │ │ │ ├── use-box-control.ts │ │ │ │ │ │ │ ├── use-color-update.ts │ │ │ │ │ │ │ ├── use-dimension-control.ts │ │ │ │ │ │ │ ├── use-dropdown-manager.tsx │ │ │ │ │ │ │ ├── use-input-control.ts │ │ │ │ │ │ │ ├── use-measure-group.ts │ │ │ │ │ │ │ └── use-text-control.ts │ │ │ │ │ │ ├── hover-tooltip.tsx │ │ │ │ │ │ ├── img-selected.tsx │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ ├── inputs │ │ │ │ │ │ │ ├── color-picker.tsx │ │ │ │ │ │ │ ├── input-color.tsx │ │ │ │ │ │ │ ├── input-dropdown.tsx │ │ │ │ │ │ │ ├── input-icon.tsx │ │ │ │ │ │ │ ├── input-radio.tsx │ │ │ │ │ │ │ ├── input-range.tsx │ │ │ │ │ │ │ └── spacing-inputs.tsx │ │ │ │ │ │ ├── overflow-menu.tsx │ │ │ │ │ │ ├── panels │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ ├── layout-position.tsx │ │ │ │ │ │ │ ├── panel-bar │ │ │ │ │ │ │ │ ├── bar.tsx │ │ │ │ │ │ │ │ └── button.tsx │ │ │ │ │ │ │ └── typography.tsx │ │ │ │ │ │ ├── separator.tsx │ │ │ │ │ │ ├── text-inputs │ │ │ │ │ │ │ ├── advanced-typography.tsx │ │ │ │ │ │ │ ├── font │ │ │ │ │ │ │ │ ├── font-family-selector.tsx │ │ │ │ │ │ │ │ ├── font-family.tsx │ │ │ │ │ │ │ │ ├── font-size.tsx │ │ │ │ │ │ │ │ └── font-weight.tsx │ │ │ │ │ │ │ ├── text-align.tsx │ │ │ │ │ │ │ └── text-color.tsx │ │ │ │ │ │ └── text-selected.tsx │ │ │ │ │ ├── left-panel │ │ │ │ │ │ ├── brand-tab │ │ │ │ │ │ │ ├── color-panel │ │ │ │ │ │ │ │ ├── color-name-input.tsx │ │ │ │ │ │ │ │ ├── color-pallet-group.tsx │ │ │ │ │ │ │ │ ├── color-popover.tsx │ │ │ │ │ │ │ │ ├── color-row.tsx │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ ├── font-panel │ │ │ │ │ │ │ │ ├── font-family.tsx │ │ │ │ │ │ │ │ ├── font-files.tsx │ │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ │ ├── system-font.tsx │ │ │ │ │ │ │ │ └── upload-modal.tsx │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ ├── help-dropdown │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ ├── image-tab │ │ │ │ │ │ │ ├── data.json │ │ │ │ │ │ │ ├── delete-modal.tsx │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ └── rename-modal.tsx │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ ├── layers-tab │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ └── tree │ │ │ │ │ │ │ │ ├── node-icon.tsx │ │ │ │ │ │ │ │ ├── page-tree-node.tsx │ │ │ │ │ │ │ │ ├── page-tree-row.tsx │ │ │ │ │ │ │ │ ├── tree-node.tsx │ │ │ │ │ │ │ │ └── tree-row.tsx │ │ │ │ │ │ ├── page-tab │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ └── page-modal.tsx │ │ │ │ │ │ ├── windows-tab │ │ │ │ │ │ │ ├── device-settings.tsx │ │ │ │ │ │ │ ├── frame-dimensions.tsx │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ └── zoom-controls │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── main.tsx │ │ │ │ │ ├── members │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ ├── invitation-row.tsx │ │ │ │ │ │ ├── invite-member-input.tsx │ │ │ │ │ │ ├── member-row.tsx │ │ │ │ │ │ ├── members-content.tsx │ │ │ │ │ │ └── suggested-teammates.tsx │ │ │ │ │ ├── right-click-menu │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── right-panel │ │ │ │ │ │ ├── chat-tab │ │ │ │ │ │ │ ├── chat-input │ │ │ │ │ │ │ │ ├── action-buttons.tsx │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ ├── chat-messages │ │ │ │ │ │ │ │ ├── assistant-message.tsx │ │ │ │ │ │ │ │ ├── error-message.tsx │ │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ │ ├── markdown-renderer.tsx │ │ │ │ │ │ │ │ ├── message-content │ │ │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ │ │ └── tool-call-display.tsx │ │ │ │ │ │ │ │ ├── stream-message.tsx │ │ │ │ │ │ │ │ └── user-message.tsx │ │ │ │ │ │ │ ├── code-change-display │ │ │ │ │ │ │ │ ├── bash-code-display.tsx │ │ │ │ │ │ │ │ ├── code-block.tsx │ │ │ │ │ │ │ │ ├── code-diff.tsx │ │ │ │ │ │ │ │ ├── code-modal.tsx │ │ │ │ │ │ │ │ └── collapsible-code-block.tsx │ │ │ │ │ │ │ ├── context-pills │ │ │ │ │ │ │ │ ├── draft-context-pill.tsx │ │ │ │ │ │ │ │ ├── draft-image-pill.tsx │ │ │ │ │ │ │ │ ├── helpers.tsx │ │ │ │ │ │ │ │ ├── input-context-pills.tsx │ │ │ │ │ │ │ │ └── sent-context-pill.tsx │ │ │ │ │ │ │ ├── controls.tsx │ │ │ │ │ │ │ ├── error.tsx │ │ │ │ │ │ │ ├── history.tsx │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ ├── panel-dropdown.tsx │ │ │ │ │ │ │ └── suggestions.tsx │ │ │ │ │ │ ├── dev-tab │ │ │ │ │ │ │ ├── code-mirror-config.ts │ │ │ │ │ │ │ ├── file-tab.tsx │ │ │ │ │ │ │ ├── file-tree-node.tsx │ │ │ │ │ │ │ ├── file-tree-row.tsx │ │ │ │ │ │ │ ├── file-tree.tsx │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ └── top-bar │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ ├── mode-toggle.tsx │ │ │ │ │ │ └── project-breadcrumb.tsx │ │ │ │ │ ├── _hooks │ │ │ │ │ ├── use-chat.tsx │ │ │ │ │ ├── use-panel-measure.tsx │ │ │ │ │ └── use-tab-active.tsx │ │ │ │ │ ├── layout.tsx │ │ │ │ │ └── page.tsx │ │ │ ├── projects │ │ │ │ ├── _components │ │ │ │ │ ├── carousel.tsx │ │ │ │ │ ├── edit-app.tsx │ │ │ │ │ ├── info.tsx │ │ │ │ │ ├── select.tsx │ │ │ │ │ ├── settings.tsx │ │ │ │ │ └── top-bar.tsx │ │ │ │ ├── layout.tsx │ │ │ │ └── page.tsx │ │ │ ├── sitemap │ │ │ │ └── page.tsx │ │ │ └── terms-of-service │ │ │ │ └── page.tsx │ │ ├── components │ │ │ ├── hotkey.ts │ │ │ ├── ide.ts │ │ │ ├── posthog-provider.tsx │ │ │ ├── store │ │ │ │ ├── create │ │ │ │ │ ├── index.ts │ │ │ │ │ └── manager.ts │ │ │ │ ├── editor │ │ │ │ │ ├── action │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── ast │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── layers.ts │ │ │ │ │ ├── canvas │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── chat │ │ │ │ │ │ ├── code.ts │ │ │ │ │ │ ├── context.ts │ │ │ │ │ │ ├── conversation │ │ │ │ │ │ │ ├── conversation.ts │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── helpers.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── message │ │ │ │ │ │ │ ├── assistant.ts │ │ │ │ │ │ │ └── user.ts │ │ │ │ │ │ ├── mockData.ts │ │ │ │ │ │ └── suggestions.ts │ │ │ │ │ ├── code │ │ │ │ │ │ ├── helpers.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── insert.ts │ │ │ │ │ │ ├── requests.ts │ │ │ │ │ │ └── tailwind.ts │ │ │ │ │ ├── copy │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── element │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── engine.ts │ │ │ │ │ ├── error │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── font │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── frames │ │ │ │ │ │ ├── frame.ts │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── group │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── history │ │ │ │ │ │ ├── helpers.ts │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── image │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── insert │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── move │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── overlay │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── prosemirror │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── state.ts │ │ │ │ │ │ └── utils.ts │ │ │ │ │ ├── pages │ │ │ │ │ │ ├── helper.ts │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── sandbox │ │ │ │ │ │ ├── file-event-bus.ts │ │ │ │ │ │ ├── file-sync.ts │ │ │ │ │ │ ├── file-watcher.ts │ │ │ │ │ │ ├── helpers.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── mapping.ts │ │ │ │ │ │ ├── session.ts │ │ │ │ │ │ └── terminal.ts │ │ │ │ │ ├── state │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── style │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── text │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── theme │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── util.ts │ │ │ │ ├── project │ │ │ │ │ ├── index.ts │ │ │ │ │ └── manager.ts │ │ │ │ ├── projects │ │ │ │ │ ├── index.ts │ │ │ │ │ └── manager.ts │ │ │ │ └── user │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── language.ts │ │ │ │ │ ├── manager.ts │ │ │ │ │ ├── settings.ts │ │ │ │ │ └── subscription.ts │ │ │ └── ui │ │ │ │ ├── avatar-dropdown.tsx │ │ │ │ └── dunes.tsx │ │ ├── env.ts │ │ ├── hooks │ │ │ └── use-feature-flags.tsx │ │ ├── i18n │ │ │ └── request.ts │ │ ├── middleware.ts │ │ ├── pages │ │ │ └── _error.tsx │ │ ├── server │ │ │ └── api │ │ │ │ ├── root.ts │ │ │ │ ├── routers │ │ │ │ ├── canvas.ts │ │ │ │ ├── chat.ts │ │ │ │ ├── code.ts │ │ │ │ ├── forward │ │ │ │ │ ├── editor.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── frame.ts │ │ │ │ ├── index.ts │ │ │ │ ├── invitation.ts │ │ │ │ ├── member.ts │ │ │ │ ├── project.ts │ │ │ │ ├── sandbox.ts │ │ │ │ ├── user-canvas.ts │ │ │ │ └── user.ts │ │ │ │ └── trpc.ts │ │ ├── styles │ │ │ └── globals.css │ │ ├── trpc │ │ │ ├── client.ts │ │ │ ├── helpers.ts │ │ │ ├── query-client.ts │ │ │ ├── react.tsx │ │ │ └── server.ts │ │ └── utils │ │ │ ├── analytics │ │ │ ├── index.ts │ │ │ └── server.ts │ │ │ ├── constants │ │ │ └── index.ts │ │ │ └── supabase │ │ │ ├── client.ts │ │ │ ├── middleware.ts │ │ │ └── server.ts │ ├── test │ │ └── sandbox │ │ │ ├── file-event-bus.test.ts │ │ │ ├── file-sync.test.ts │ │ │ ├── helpers.test.ts │ │ │ ├── mapping.test.ts │ │ │ ├── sandbox.test.ts │ │ │ └── subdirectory.test.ts │ └── tsconfig.json │ ├── docker-compose.yml │ ├── package.json │ ├── preload │ ├── .gitignore │ ├── Dockerfile │ ├── README.md │ ├── dist │ │ └── index.js │ ├── package.json │ ├── script │ │ ├── api │ │ │ ├── dom.ts │ │ │ ├── elements │ │ │ │ ├── dom │ │ │ │ │ ├── group.ts │ │ │ │ │ ├── helpers.ts │ │ │ │ │ ├── image.ts │ │ │ │ │ ├── insert.ts │ │ │ │ │ └── remove.ts │ │ │ │ ├── helpers.ts │ │ │ │ ├── index.ts │ │ │ │ ├── move │ │ │ │ │ ├── drag.ts │ │ │ │ │ ├── helpers.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── stub.ts │ │ │ │ ├── style.ts │ │ │ │ └── text.ts │ │ │ ├── events │ │ │ │ ├── dom.ts │ │ │ │ ├── index.ts │ │ │ │ └── publish.ts │ │ │ ├── index.ts │ │ │ ├── ready.ts │ │ │ ├── state.ts │ │ │ ├── style │ │ │ │ ├── css-manager.ts │ │ │ │ ├── index.ts │ │ │ │ └── update.ts │ │ │ └── theme │ │ │ │ └── index.ts │ │ ├── helpers │ │ │ ├── assert.ts │ │ │ ├── clone.ts │ │ │ ├── dom.ts │ │ │ ├── ids.ts │ │ │ └── index.ts │ │ └── index.ts │ ├── server │ │ └── index.ts │ └── tsconfig.json │ ├── server │ ├── .gitignore │ ├── Dockerfile │ ├── README.md │ ├── bun.lock │ ├── package.json │ ├── src │ │ ├── index.ts │ │ ├── router │ │ │ ├── context.ts │ │ │ ├── index.ts │ │ │ ├── routes │ │ │ │ └── sandbox.ts │ │ │ └── trpc.ts │ │ ├── sandbox │ │ │ └── index.ts │ │ └── server.ts │ ├── tsconfig.json │ └── tsup.config.ts │ └── template │ ├── .codesandbox │ └── tasks.json │ ├── .devcontainer │ ├── Dockerfile │ └── devcontainer.json │ ├── .eslintrc.json │ ├── .gitignore │ ├── .prettierrc │ ├── Dockerfile │ ├── README.md │ ├── app │ ├── favicon.ico │ ├── globals.css │ ├── layout.tsx │ └── page.tsx │ ├── components.json │ ├── lib │ └── utils.ts │ ├── next.config.mjs │ ├── package.json │ ├── postcss.config.mjs │ ├── public │ └── images │ │ └── logo-text.png │ ├── tailwind.config.ts │ └── tsconfig.json ├── assets ├── architecture.png ├── brand.png ├── code-connect.png ├── contribute.png ├── favicon.ico ├── fork.png ├── hld.png ├── insert-div.png ├── logo.svg ├── text-styling.png └── web-preview.png ├── bun.lock ├── bun.lockb ├── docs ├── .eslintrc.json ├── .gitignore ├── README.md ├── bun.lock ├── content │ ├── docs │ │ ├── developer │ │ │ ├── appendix │ │ │ │ └── index.mdx │ │ │ ├── architecture.mdx │ │ │ ├── contributing.mdx │ │ │ ├── electron-to-web-migration.mdx │ │ │ ├── index.mdx │ │ │ └── running-locally.mdx │ │ ├── faq │ │ │ └── index.mdx │ │ ├── features │ │ │ ├── figma-to-onlook │ │ │ │ └── index.mdx │ │ │ ├── index.mdx │ │ │ └── modes │ │ │ │ └── index.mdx │ │ ├── index.mdx │ │ ├── tutorials │ │ │ ├── design-to-code.mdx │ │ │ ├── first-project.mdx │ │ │ └── index.mdx │ │ └── user-guide │ │ │ ├── index.mdx │ │ │ ├── quickstart │ │ │ └── index.mdx │ │ │ └── ui-overview.mdx │ └── index.mdx ├── next.config.ts ├── package.json ├── postcss.config.mjs ├── public │ ├── favicon.ico │ └── images │ │ ├── ai-assistance.png │ │ ├── ai-mode.png │ │ ├── canvas-architecture.png │ │ ├── code-integration.png │ │ ├── code-mode.png │ │ ├── contribute-allow-edits.png │ │ ├── contribute-fork.png │ │ ├── contribute-open-pr.png │ │ ├── create-project.png │ │ ├── edit-loop.png │ │ ├── electron-to-web-after.png │ │ ├── electron-to-web-before.png │ │ ├── faq-banner.png │ │ ├── features-overview.png │ │ ├── figma-to-onlook-banner.png │ │ ├── figma-to-onlook.png │ │ ├── full-architecture.png │ │ ├── hero-banner.png │ │ ├── layer-naming.png │ │ ├── modes-overview.png │ │ ├── onlook-interface.png │ │ ├── quickstart-banner.png │ │ ├── visual-edit-mode.png │ │ └── visual-editor.png ├── source.config.ts ├── src │ ├── app │ │ ├── (home) │ │ │ ├── layout.tsx │ │ │ └── page.tsx │ │ ├── api │ │ │ └── search │ │ │ │ └── route.ts │ │ ├── docs │ │ │ ├── [[...slug]] │ │ │ │ ├── edit-button.tsx │ │ │ │ ├── edit-gh.tsx │ │ │ │ └── page.tsx │ │ │ └── layout.tsx │ │ ├── global.css │ │ ├── layout.config.tsx │ │ ├── layout.tsx │ │ └── not-found.tsx │ ├── lib │ │ └── source.ts │ └── mdx-components.tsx └── tsconfig.json ├── package.json ├── packages ├── ai │ ├── package.json │ ├── src │ │ ├── apply │ │ │ ├── client.ts │ │ │ └── index.ts │ │ ├── chat │ │ │ ├── index.ts │ │ │ └── providers.ts │ │ ├── coder │ │ │ ├── block.ts │ │ │ ├── helpers.ts │ │ │ ├── index.ts │ │ │ └── search-replace.ts │ │ ├── index.ts │ │ ├── prompt │ │ │ ├── base.ts │ │ │ ├── context.ts │ │ │ ├── create │ │ │ │ ├── base.ts │ │ │ │ ├── example.ts │ │ │ │ └── index.ts │ │ │ ├── edit │ │ │ │ ├── edit.ts │ │ │ │ ├── example.ts │ │ │ │ └── index.ts │ │ │ ├── format.ts │ │ │ ├── helpers.ts │ │ │ ├── index.ts │ │ │ ├── onlook.ts │ │ │ ├── provider.ts │ │ │ ├── shell.ts │ │ │ ├── signatures.ts │ │ │ └── summary.ts │ │ └── tools │ │ │ ├── helpers.ts │ │ │ └── index.ts │ ├── test │ │ ├── apply.test.ts │ │ ├── coder │ │ │ ├── block │ │ │ │ ├── block.test.ts │ │ │ │ └── data │ │ │ │ │ └── multiple.txt │ │ │ └── extract.test.ts │ │ ├── list-files.test.ts │ │ └── prompt │ │ │ ├── data │ │ │ ├── create-page-system.txt │ │ │ ├── examples.txt │ │ │ ├── file.txt │ │ │ ├── highlights.txt │ │ │ ├── response.txt │ │ │ ├── summary.txt │ │ │ ├── system.txt │ │ │ ├── user-empty.txt │ │ │ └── user.txt │ │ │ └── prompt.test.ts │ └── tsconfig.json ├── constants │ ├── package.json │ ├── src │ │ ├── csb.ts │ │ ├── dom.ts │ │ ├── editor.ts │ │ ├── files.ts │ │ ├── frame.ts │ │ ├── freestyle.ts │ │ ├── index.ts │ │ ├── language.ts │ │ └── links.ts │ └── tsconfig.json ├── db │ ├── .env.example │ ├── drizzle.config.ts │ ├── package.json │ ├── src │ │ ├── client.ts │ │ ├── dto │ │ │ ├── canvas.ts │ │ │ ├── conversation.ts │ │ │ ├── frame.ts │ │ │ ├── index.ts │ │ │ ├── message.ts │ │ │ ├── project.ts │ │ │ └── user.ts │ │ ├── index.ts │ │ ├── schema │ │ │ ├── index.ts │ │ │ ├── project │ │ │ │ ├── canvas │ │ │ │ │ ├── canvas.ts │ │ │ │ │ ├── frame.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── chat │ │ │ │ │ ├── conversation.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── message.ts │ │ │ │ ├── index.ts │ │ │ │ ├── invitation.ts │ │ │ │ └── project.ts │ │ │ ├── supabase │ │ │ │ ├── index.ts │ │ │ │ └── user.ts │ │ │ └── user │ │ │ │ ├── index.ts │ │ │ │ ├── settings.ts │ │ │ │ ├── user-canvas.ts │ │ │ │ ├── user-project.ts │ │ │ │ └── user.ts │ │ └── seed │ │ │ ├── constants.ts │ │ │ ├── db.ts │ │ │ ├── seed.ts │ │ │ └── supabase.ts │ └── tsconfig.json ├── email │ ├── package.json │ ├── src │ │ ├── client.ts │ │ ├── index.ts │ │ ├── invitation.ts │ │ ├── templates │ │ │ ├── index.ts │ │ │ └── invite-user.tsx │ │ └── types │ │ │ └── send-email.ts │ └── tsconfig.json ├── fonts │ ├── package.json │ ├── src │ │ ├── default.ts │ │ ├── family.ts │ │ ├── helper.ts │ │ ├── index.ts │ │ ├── utils.ts │ │ └── variants.ts │ └── tsconfig.json ├── git │ ├── package.json │ ├── src │ │ ├── git.ts │ │ └── index.ts │ ├── test │ │ └── git.test.ts │ └── tsconfig.json ├── growth │ ├── package.json │ ├── src │ │ ├── index.ts │ │ ├── inject.ts │ │ ├── remove.ts │ │ └── script.ts │ ├── tests │ │ └── inject.test.ts │ └── tsconfig.json ├── mastra │ ├── package.json │ ├── src │ │ ├── agents │ │ │ └── index.ts │ │ ├── index.ts │ │ ├── tools │ │ │ └── index.ts │ │ └── workflows │ │ │ └── index.ts │ └── tsconfig.json ├── models │ ├── package.json │ ├── src │ │ ├── actions │ │ │ ├── action.ts │ │ │ ├── code.ts │ │ │ ├── index.ts │ │ │ ├── location.ts │ │ │ └── target.ts │ │ ├── assets │ │ │ └── index.ts │ │ ├── auth │ │ │ └── index.ts │ │ ├── chat │ │ │ ├── conversation │ │ │ │ └── index.ts │ │ │ ├── index.ts │ │ │ ├── message │ │ │ │ ├── code.ts │ │ │ │ ├── context.ts │ │ │ │ ├── index.ts │ │ │ │ └── message.ts │ │ │ ├── request.ts │ │ │ ├── response.ts │ │ │ ├── stream.ts │ │ │ ├── suggestion.ts │ │ │ └── summary.ts │ │ ├── code │ │ │ └── index.ts │ │ ├── create │ │ │ └── index.ts │ │ ├── editor │ │ │ └── index.ts │ │ ├── element │ │ │ ├── classes.ts │ │ │ ├── element.ts │ │ │ ├── index.ts │ │ │ ├── layers.ts │ │ │ ├── props.ts │ │ │ └── templateNode.ts │ │ ├── hosting │ │ │ └── index.ts │ │ ├── ide │ │ │ └── index.ts │ │ ├── index.ts │ │ ├── llm │ │ │ └── index.ts │ │ ├── pages │ │ │ ├── index.ts │ │ │ └── opengraph.ts │ │ ├── project │ │ │ ├── canvas.ts │ │ │ ├── command.ts │ │ │ ├── domain.ts │ │ │ ├── frame.ts │ │ │ ├── index.ts │ │ │ ├── invitation.ts │ │ │ ├── project.ts │ │ │ ├── rect.ts │ │ │ └── role.ts │ │ ├── run │ │ │ └── index.ts │ │ ├── style │ │ │ └── index.ts │ │ ├── supabase │ │ │ └── db.ts │ │ ├── usage │ │ │ └── index.ts │ │ └── user │ │ │ ├── index.ts │ │ │ ├── settings.ts │ │ │ └── user.ts │ └── tsconfig.json ├── parser │ ├── package.json │ ├── src │ │ ├── code-edit │ │ │ ├── group.ts │ │ │ ├── helpers.ts │ │ │ ├── image.ts │ │ │ ├── index.ts │ │ │ ├── insert.ts │ │ │ ├── move.ts │ │ │ ├── remove.ts │ │ │ ├── style.ts │ │ │ ├── text.ts │ │ │ └── transform.ts │ │ ├── helpers.ts │ │ ├── ids.ts │ │ ├── index.ts │ │ ├── packages.ts │ │ ├── parse.ts │ │ └── template-node │ │ │ ├── helpers.ts │ │ │ ├── index.ts │ │ │ └── map.ts │ ├── test │ │ ├── helpers.test.ts │ │ ├── ids.test.ts │ │ ├── parse.test.ts │ │ └── template.test.ts │ └── tsconfig.json ├── penpal │ ├── package.json │ ├── src │ │ ├── child.ts │ │ ├── index.ts │ │ ├── parent.ts │ │ └── utils.ts │ └── tsconfig.json ├── rpc │ ├── package.json │ ├── src │ │ ├── index.ts │ │ └── trpc │ │ │ ├── config.ts │ │ │ ├── index.ts │ │ │ └── types.ts │ └── tsconfig.json ├── scripts │ ├── package.json │ ├── src │ │ ├── api-keys.ts │ │ ├── backend.ts │ │ ├── helpers.ts │ │ └── index.ts │ └── tsconfig.json ├── types │ ├── package.json │ ├── src │ │ ├── adapters │ │ │ ├── index.ts │ │ │ └── props.ts │ │ ├── design-tokens │ │ │ └── index.ts │ │ └── index.ts │ └── tsconfig.json ├── ui │ ├── README.md │ ├── components.json │ ├── package.json │ ├── postcss.config.js │ ├── src │ │ ├── components │ │ │ ├── accordion.tsx │ │ │ ├── alert-dialog.tsx │ │ │ ├── alert.tsx │ │ │ ├── aspect-ratio.tsx │ │ │ ├── avatar.tsx │ │ │ ├── badge.tsx │ │ │ ├── breadcrumb.tsx │ │ │ ├── button.tsx │ │ │ ├── calendar.tsx │ │ │ ├── card.tsx │ │ │ ├── carousel.tsx │ │ │ ├── chart.tsx │ │ │ ├── chat │ │ │ │ ├── chat-message-list.tsx │ │ │ │ └── hooks │ │ │ │ │ └── use-auto-scroll.tsx │ │ │ ├── checkbox.tsx │ │ │ ├── collapsible.tsx │ │ │ ├── color-picker │ │ │ │ ├── ColorPicker.tsx │ │ │ │ ├── ColorSlider.tsx │ │ │ │ ├── EyeDropperButton.tsx │ │ │ │ ├── SVPicker.tsx │ │ │ │ ├── checkPattern.ts │ │ │ │ └── index.tsx │ │ │ ├── command.tsx │ │ │ ├── context-menu.tsx │ │ │ ├── dialog.tsx │ │ │ ├── draftable-input.tsx │ │ │ ├── drawer.tsx │ │ │ ├── dropdown-menu.tsx │ │ │ ├── form.tsx │ │ │ ├── hotkey-label.tsx │ │ │ ├── hover-card.tsx │ │ │ ├── icons │ │ │ │ ├── header-level-icons │ │ │ │ │ ├── h1Icon.tsx │ │ │ │ │ ├── h2Icon.tsx │ │ │ │ │ ├── h3Icon.tsx │ │ │ │ │ ├── h4Icon.tsx │ │ │ │ │ ├── h5Icon.tsx │ │ │ │ │ └── h6Icon.tsx │ │ │ │ └── index.tsx │ │ │ ├── index.tsx │ │ │ ├── input-group.tsx │ │ │ ├── input-otp.tsx │ │ │ ├── input.tsx │ │ │ ├── kbd.tsx │ │ │ ├── label.tsx │ │ │ ├── menubar.tsx │ │ │ ├── motion-card.tsx │ │ │ ├── navigation-menu.tsx │ │ │ ├── pagination.tsx │ │ │ ├── popover.tsx │ │ │ ├── progress.tsx │ │ │ ├── radio-group.tsx │ │ │ ├── resizable.tsx │ │ │ ├── scroll-area.tsx │ │ │ ├── select.tsx │ │ │ ├── separator.tsx │ │ │ ├── sheet.tsx │ │ │ ├── shine-border.tsx │ │ │ ├── sidebar.tsx │ │ │ ├── skeleton.tsx │ │ │ ├── slider.tsx │ │ │ ├── sonner.tsx │ │ │ ├── switch.tsx │ │ │ ├── table.tsx │ │ │ ├── tabs.tsx │ │ │ ├── textarea.tsx │ │ │ ├── toggle-group.tsx │ │ │ ├── toggle.tsx │ │ │ └── tooltip.tsx │ │ ├── globals.css │ │ ├── hooks │ │ │ ├── index.ts │ │ │ ├── use-enter-submit.ts │ │ │ ├── use-media-query.ts │ │ │ ├── use-mobile.ts │ │ │ ├── use-pointer-stroke.tsx │ │ │ └── use-resize-observer.ts │ │ ├── index.ts │ │ └── utils │ │ │ ├── cn.ts │ │ │ ├── index.ts │ │ │ └── truncate.ts │ ├── tailwind.config.ts │ ├── tokens.ts │ └── tsconfig.json └── utility │ ├── package.json │ ├── src │ ├── assert.ts │ ├── autolayout.ts │ ├── clone.ts │ ├── color.ts │ ├── defaults │ │ ├── canvas.ts │ │ ├── conversation.ts │ │ ├── frame.ts │ │ ├── index.ts │ │ ├── user-canvas.ts │ │ └── user-settings.ts │ ├── email.ts │ ├── errors.ts │ ├── font.ts │ ├── id.ts │ ├── image.ts │ ├── index.ts │ ├── initials.ts │ ├── math.ts │ ├── null.ts │ ├── path.ts │ ├── string.ts │ ├── tailwind.ts │ ├── time.ts │ ├── unit.ts │ ├── urls.ts │ └── window-metadata.ts │ ├── test │ ├── colors.test.ts │ ├── errors.test.ts │ ├── id.test.ts │ └── path.test.ts │ └── tsconfig.json ├── scripts ├── increment_tag.sh ├── publish_tag.sh └── remove_tag.sh └── tooling └── typescript ├── base.json ├── next-react.json ├── package.json ├── react-library.json └── vite-react.json /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | name: 🐞 Bug report 4 | about: Create a report to help us improve 5 | title: "[bug] the title of bug report" 6 | labels: bug 7 | assignees: '' 8 | 9 | --- 10 | 11 | #### Describe the bug 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | name: ✨ Feature request 4 | about: Create a feature request 5 | title: "[feat] the title of the request" 6 | labels: enhancement 7 | assignees: '' 8 | 9 | --- 10 | 11 | #### Describe the feature 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/help_wanted.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🥺 Help wanted 3 | about: Confuse about the use of Onlook 4 | title: "[Help] the title of help wanted report" 5 | labels: help wanted 6 | assignees: '' 7 | 8 | --- 9 | 10 | #### Describe the problem you confuse 11 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "npm" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "monthly" 12 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | 4 | 5 | ## Related Issues 6 | 7 | 8 | 9 | ## Type of Change 10 | 11 | 12 | 13 | - [ ] Bug fix 14 | - [ ] New feature 15 | - [ ] Documentation update 16 | - [ ] Release 17 | - [ ] Refactor 18 | - [ ] Other (please describe): 19 | 20 | ## Testing 21 | 22 | 23 | 24 | ## Screenshots (if applicable) 25 | 26 | 27 | 28 | ## Additional Notes 29 | 30 | 31 | -------------------------------------------------------------------------------- /.github/workflows/supabase-migrate-staging.yml: -------------------------------------------------------------------------------- 1 | name: Deploy Supabase Migration to Staging Environment 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | migrate: 8 | runs-on: ubuntu-latest 9 | env: 10 | SUPABASE_DATABASE_URL: ${{ secrets.STAGING_SUPABASE_DATABASE_URL }} 11 | 12 | steps: 13 | - uses: actions/checkout@v4 14 | 15 | - uses: oven-sh/setup-bun@v1 16 | with: 17 | bun-version: latest 18 | 19 | - name: Install dependencies 20 | run: bun install 21 | 22 | - name: Run migrations 23 | run: bun run db:migrate -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | dist-electron 14 | release 15 | *.local 16 | 17 | # Editor directories and files 18 | .vscode/.debug.env 19 | .vscode/settings.json 20 | .idea 21 | .DS_Store 22 | *.suo 23 | *.ntvs* 24 | *.njsproj 25 | *.sln 26 | *.sw? 27 | 28 | #lockfile 29 | package-lock.json 30 | pnpm-lock.yaml 31 | yarn.lock 32 | /test-results/ 33 | /playwright-report/ 34 | /playwright/.cache/ 35 | 36 | # Env variables 37 | .env 38 | .env.production 39 | .env.development 40 | .env.test 41 | .env.local 42 | .env.development.local 43 | .env.test.local 44 | .env.production.local 45 | 46 | mise.toml -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | bun run format && git add . 3 | # bun run lint && bun run format:write && git add . -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist-electron 2 | dist 3 | node_modules 4 | release 5 | demos -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "printWidth": 100, 4 | "tabWidth": 4, 5 | "useTabs": false, 6 | "semi": true, 7 | "jsxSingleQuote": false, 8 | "bracketSpacing": true, 9 | "arrowParens": "always", 10 | "endOfLine": "lf" 11 | } 12 | -------------------------------------------------------------------------------- /.vscode/.debug.script.mjs: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs' 2 | import path from 'node:path' 3 | import { fileURLToPath } from 'node:url' 4 | import { createRequire } from 'node:module' 5 | import { spawn } from 'node:child_process' 6 | 7 | const pkg = createRequire(import.meta.url)('../apps/studio/package.json') 8 | const __dirname = path.dirname(fileURLToPath(import.meta.url)) 9 | 10 | // write .debug.env 11 | const envContent = Object.entries(pkg.debug.env).map(([key, val]) => `${key}=${val}`) 12 | fs.writeFileSync(path.join(__dirname, '.debug.env'), envContent.join('\n')) 13 | 14 | // bootstrap 15 | spawn( 16 | process.platform === 'win32' ? 'npm.cmd' : 'npm', 17 | ['run', 'dev'], 18 | { 19 | stdio: 'inherit', 20 | env: Object.assign(process.env, { VSCODE_DEBUG: 'true' }), 21 | }, 22 | ) 23 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "mrmlnc.vscode-json5" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "Before Debug", 8 | "type": "shell", 9 | "command": "node .vscode/.debug.script.mjs", 10 | "isBackground": true, 11 | "problemMatcher": { 12 | "owner": "typescript", 13 | "fileLocation": "relative", 14 | "pattern": { 15 | "regexp": "^([a-zA-Z]\\:\/?([\\w\\-]\/?)+\\.\\w+):(\\d+):(\\d+): (ERROR|WARNING)\\: (.*)$", 16 | "file": 1, 17 | "line": 3, 18 | "column": 4, 19 | "code": 5, 20 | "message": 6 21 | }, 22 | "background": { 23 | "activeOnStart": true, 24 | "beginsPattern": "^.*VITE v.* ready in \\d* ms.*$", 25 | "endsPattern": "^.*\\[startup\\] Electron App.*$" 26 | } 27 | } 28 | } 29 | ] 30 | } -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | Please contact us at [contact@onlook.com](mailto:contact@onlook.com) with any security issues. -------------------------------------------------------------------------------- /apps/backend/.env.example: -------------------------------------------------------------------------------- 1 | # Auth 2 | GOOGLE_CLIENT_ID= 3 | GOOGLE_SECRET= 4 | GITHUB_CLIENT_ID= 5 | GITHUB_SECRET= 6 | 7 | # Payment 8 | STRIPE_API_KEY= 9 | STRIPE_WEBHOOK_SIGNING_SECRET= 10 | STRIPE_PRO_PRICE_ID= 11 | -------------------------------------------------------------------------------- /apps/backend/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | dist-electron 14 | release 15 | *.local 16 | 17 | # Editor directories and files 18 | .vscode/.debug.env 19 | .vscode/settings.json 20 | .idea 21 | .DS_Store 22 | *.suo 23 | *.ntvs* 24 | *.njsproj 25 | *.sln 26 | *.sw? 27 | 28 | #lockfile 29 | package-lock.json 30 | pnpm-lock.yaml 31 | yarn.lock 32 | /test-results/ 33 | /playwright-report/ 34 | /playwright/.cache/ 35 | 36 | # Env variables 37 | .env 38 | -------------------------------------------------------------------------------- /apps/backend/README.md: -------------------------------------------------------------------------------- 1 | ## Why a backend stack? 2 | 3 | This is our server stack built in Supabase which you can also run locally or self-host. 4 | 5 | Used to enable online capabilities such as managing users, collaborating, persisting data, etc. 6 | 7 | We will offer this as a hosted instance at some point. Ideally, the product should still work offline with no backend connection. 8 | 9 | ## Usage 10 | 11 | ### Running locally 12 | 13 | 1. Make sure you have [Docker] installed 14 | 2. Install necessary packages 15 | 16 | ```bash 17 | npm install 18 | ``` 19 | 20 | 3. Run the supabase instance locally 21 | 22 | ```bash 23 | npm run start 24 | ``` 25 | 26 | 4. Set up the latest snapshot of the database 27 | 28 | ```bash 29 | npm run reset 30 | ``` 31 | -------------------------------------------------------------------------------- /apps/backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@onlook/backend", 3 | "private": true, 4 | "scripts": { 5 | "start": "supabase start", 6 | "stop": "supabase stop", 7 | "push": "supabase db push", 8 | "reset": "supabase db reset", 9 | "db:gen": "supabase gen types --lang=typescript --local --schema public > ../../packages/supabase/src/types/db.ts", 10 | "test": "cd supabase/functions/api && deno test" 11 | }, 12 | "dependencies": {}, 13 | "devDependencies": { 14 | "@onlook/typescript": "*", 15 | "@types/bun": "latest", 16 | "supabase": "^2.6.8" 17 | }, 18 | "peerDependencies": { 19 | "typescript": "^5.0.0" 20 | } 21 | } -------------------------------------------------------------------------------- /apps/backend/supabase/.gitignore: -------------------------------------------------------------------------------- 1 | # Supabase 2 | .branches 3 | .temp 4 | .env 5 | -------------------------------------------------------------------------------- /apps/backend/supabase/config.toml: -------------------------------------------------------------------------------- 1 | project_id = "onlook-web" 2 | 3 | [api] 4 | enabled = true 5 | port = 54321 6 | schemas = ["public", "storage"] 7 | extra_search_path = ["public"] 8 | max_rows = 100 9 | 10 | [auth] 11 | site_url = "https://onlook.com" 12 | additional_redirect_urls = [ 13 | "http://localhost:3000", 14 | "http://localhost:3000/auth/callback", 15 | ] 16 | jwt_expiry = 36000 17 | 18 | [db] 19 | port = 54322 20 | 21 | [studio] 22 | port = 54323 23 | 24 | [auth.external.github] 25 | enabled = true 26 | client_id = "env(GITHUB_CLIENT_ID)" 27 | secret = "env(GITHUB_SECRET)" 28 | redirect_uri = "http://127.0.0.1:54321/auth/v1/callback" 29 | 30 | [auth.external.google] 31 | enabled = true 32 | client_id = "env(GOOGLE_CLIENT_ID)" 33 | secret = "env(GOOGLE_SECRET)" 34 | redirect_uri = "http://127.0.0.1:54321/auth/v1/callback" 35 | 36 | [analytics] 37 | enabled = true 38 | port = 54327 39 | vector_port = 54328 40 | backend = "postgres" 41 | 42 | [functions.stripe-webhook] 43 | verify_jwt = false 44 | 45 | [storage.buckets.preview_images] 46 | public = true 47 | file_size_limit = "10MiB" 48 | -------------------------------------------------------------------------------- /apps/backend/supabase/functions/api/.npmrc: -------------------------------------------------------------------------------- 1 | # Configuration for private npm package dependencies 2 | # For more information on using private registries with Edge Functions, see: 3 | # https://supabase.com/docs/guides/functions/import-maps#importing-from-private-registries 4 | -------------------------------------------------------------------------------- /apps/backend/supabase/functions/api/deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": { 3 | "@onlook/models/": "../../../../../packages/models/src/", 4 | "zod": "npm:zod" 5 | } 6 | } -------------------------------------------------------------------------------- /apps/backend/supabase/functions/api/index.ts: -------------------------------------------------------------------------------- 1 | import { Hono } from 'jsr:@hono/hono'; 2 | import "jsr:@supabase/functions-js/edge-runtime.d.ts"; 3 | 4 | const app = new Hono(); 5 | 6 | Deno.serve(app.fetch); -------------------------------------------------------------------------------- /apps/backend/supabase/functions/shared/.npmrc: -------------------------------------------------------------------------------- 1 | # Configuration for private npm package dependencies 2 | # For more information on using private registries with Edge Functions, see: 3 | # https://supabase.com/docs/guides/functions/import-maps#importing-from-private-registries 4 | -------------------------------------------------------------------------------- /apps/backend/supabase/functions/shared/deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": { 3 | "@onlook/models/": "../../../../../packages/models/src/" 4 | } 5 | } -------------------------------------------------------------------------------- /apps/backend/supabase/functions/shared/error.ts: -------------------------------------------------------------------------------- 1 | export function getErrorMessage(error: unknown): string { 2 | if (error instanceof Error) { 3 | return error.message; 4 | } 5 | if (typeof error === 'string') { 6 | return error; 7 | } 8 | if (error && typeof error === 'object' && 'message' in error) { 9 | return String(error.message); 10 | } 11 | return 'An unknown error occurred'; 12 | } 13 | 14 | export function createErrorObject(error: unknown): { 15 | error: { 16 | message: string; 17 | }; 18 | } { 19 | return { 20 | error: { 21 | message: error instanceof Error ? error.message : JSON.stringify(error), 22 | }, 23 | }; 24 | } -------------------------------------------------------------------------------- /apps/backend/supabase/functions/stripe-webhook/.npmrc: -------------------------------------------------------------------------------- 1 | # Configuration for private npm package dependencies 2 | # For more information on using private registries with Edge Functions, see: 3 | # https://supabase.com/docs/guides/functions/import-maps#importing-from-private-registries 4 | -------------------------------------------------------------------------------- /apps/backend/supabase/functions/stripe-webhook/deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": {} 3 | } -------------------------------------------------------------------------------- /apps/backend/supabase/migrations/0001_graceful_exodus.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "conversations" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint 2 | ALTER TABLE "messages" ENABLE ROW LEVEL SECURITY; -------------------------------------------------------------------------------- /apps/backend/supabase/migrations/0002_red_crusher_hogan.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS "user_settings" ( 2 | "id" uuid PRIMARY KEY NOT NULL, 3 | "user_id" uuid NOT NULL, 4 | "auto_apply_code" boolean DEFAULT true NOT NULL, 5 | "expand_code_blocks" boolean DEFAULT true NOT NULL, 6 | "show_suggestions" boolean DEFAULT true NOT NULL, 7 | "show_mini_chat" boolean DEFAULT true NOT NULL, 8 | CONSTRAINT "user_settings_user_id_unique" UNIQUE("user_id") 9 | ); 10 | --> statement-breakpoint 11 | ALTER TABLE "user_settings" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint 12 | ALTER TABLE "user_settings" DROP CONSTRAINT IF EXISTS "user_settings_user_id_users_id_fk"; 13 | --> statement-breakpoint 14 | ALTER TABLE "user_settings" ADD CONSTRAINT "user_settings_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE cascade; -------------------------------------------------------------------------------- /apps/backend/supabase/migrations/0005_short_lila_cheney.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "project_invitations" DROP COLUMN IF EXISTS "status";--> statement-breakpoint 2 | ALTER TABLE "project_invitations" DROP CONSTRAINT IF EXISTS "project_invitations_invitee_email_project_id_unique"; 3 | ALTER TABLE "project_invitations" ADD CONSTRAINT "project_invitations_invitee_email_project_id_unique" UNIQUE("invitee_email","project_id");--> statement-breakpoint 4 | DROP TYPE IF EXISTS "public"."invitation_status"; -------------------------------------------------------------------------------- /apps/backend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | // Enable latest features 4 | "lib": ["ESNext", "DOM"], 5 | "target": "ESNext", 6 | "module": "ESNext", 7 | "moduleDetection": "force", 8 | "jsx": "react-jsx", 9 | "allowJs": true, 10 | 11 | // Bundler mode 12 | "moduleResolution": "bundler", 13 | "allowImportingTsExtensions": true, 14 | "verbatimModuleSyntax": true, 15 | "noEmit": true, 16 | 17 | // Best practices 18 | "strict": true, 19 | "skipLibCheck": true, 20 | "noFallthroughCasesInSwitch": true, 21 | 22 | // Some stricter flags (disabled by default) 23 | "noUnusedLocals": false, 24 | "noUnusedParameters": false, 25 | "noPropertyAccessFromIndexSignature": false 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /apps/web/.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies (bun install) 2 | node_modules 3 | 4 | # output 5 | out 6 | dist 7 | *.tgz 8 | 9 | # code coverage 10 | coverage 11 | *.lcov 12 | 13 | # logs 14 | logs 15 | _.log 16 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json 17 | 18 | # dotenv environment variable files 19 | .env 20 | .env.development.local 21 | .env.test.local 22 | .env.production.local 23 | .env.local 24 | 25 | # caches 26 | .eslintcache 27 | .cache 28 | *.tsbuildinfo 29 | 30 | # IntelliJ based IDEs 31 | .idea 32 | 33 | # Finder (MacOS) folder config 34 | .DS_Store 35 | -------------------------------------------------------------------------------- /apps/web/README.md: -------------------------------------------------------------------------------- 1 | # Web project 2 | 3 | Structure 4 | 5 | - Client - Next.js client that is served as app front-end 6 | - Server - The control server that will be used to interact with the template 7 | app 8 | - Template - The example app that will be editable 9 | - Shared - All the shared packages for the web project 10 | - Preload - The script that gets injected into Template app. This allows 11 | communnicating directly with the app DOM through iframe 12 | -------------------------------------------------------------------------------- /apps/web/client/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # database 12 | /prisma/db.sqlite 13 | /prisma/db.sqlite-journal 14 | db.sqlite 15 | 16 | # next.js 17 | /.next/ 18 | /out/ 19 | next-env.d.ts 20 | 21 | # production 22 | /build 23 | 24 | # misc 25 | .DS_Store 26 | *.pem 27 | 28 | # debug 29 | npm-debug.log* 30 | yarn-debug.log* 31 | yarn-error.log* 32 | .pnpm-debug.log* 33 | 34 | # local env files 35 | # do not commit any .env files to git, except for the .env.example file. https://create.t3.gg/en/usage/env-variables#using-environment-variables 36 | .env 37 | .env*.local 38 | 39 | # vercel 40 | .vercel 41 | 42 | # typescript 43 | *.tsbuildinfo 44 | 45 | # idea files 46 | .idea -------------------------------------------------------------------------------- /apps/web/client/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "printWidth": 100, 4 | "tabWidth": 4, 5 | "useTabs": false, 6 | "semi": true, 7 | "jsxSingleQuote": false, 8 | "bracketSpacing": true, 9 | "arrowParens": "always", 10 | "endOfLine": "lf" 11 | } 12 | -------------------------------------------------------------------------------- /apps/web/client/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "", 8 | "css": "src/styles/globals.css", 9 | "baseColor": "slate", 10 | "cssVariables": false, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils", 16 | "ui": "@/components/ui", 17 | "lib": "@/lib", 18 | "hooks": "@/hooks" 19 | }, 20 | "iconLibrary": "lucide" 21 | } -------------------------------------------------------------------------------- /apps/web/client/next.config.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially useful 3 | * for Docker builds. 4 | */ 5 | import { NextConfig } from 'next'; 6 | import createNextIntlPlugin from 'next-intl/plugin'; 7 | import path from 'node:path'; 8 | import './src/env'; 9 | 10 | const nextConfig: NextConfig = { 11 | devIndicators: false, 12 | // TODO: Remove this once we have a proper ESLint and TypeScript config 13 | eslint: { 14 | ignoreDuringBuilds: true, 15 | }, 16 | typescript: { 17 | ignoreBuildErrors: true, 18 | }, 19 | }; 20 | 21 | if (process.env.NODE_ENV === 'development') { 22 | nextConfig.outputFileTracingRoot = path.join(__dirname, '../../..'); 23 | } 24 | 25 | const withNextIntl = createNextIntlPlugin(); 26 | export default withNextIntl(nextConfig); 27 | -------------------------------------------------------------------------------- /apps/web/client/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | '@tailwindcss/postcss': {}, 4 | }, 5 | }; 6 | -------------------------------------------------------------------------------- /apps/web/client/prettier.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('prettier').Config & import('prettier-plugin-tailwindcss').PluginOptions} */ 2 | export default { 3 | plugins: ['prettier-plugin-tailwindcss'], 4 | }; 5 | -------------------------------------------------------------------------------- /apps/web/client/public/assets/dunes-create-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlook-dev/onlook/430860fdd6ebd403e21eb2bc340925edef3ed158/apps/web/client/public/assets/dunes-create-dark.png -------------------------------------------------------------------------------- /apps/web/client/public/assets/dunes-create-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlook-dev/onlook/430860fdd6ebd403e21eb2bc340925edef3ed158/apps/web/client/public/assets/dunes-create-light.png -------------------------------------------------------------------------------- /apps/web/client/public/assets/dunes-login-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlook-dev/onlook/430860fdd6ebd403e21eb2bc340925edef3ed158/apps/web/client/public/assets/dunes-login-dark.png -------------------------------------------------------------------------------- /apps/web/client/public/assets/dunes-login-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlook-dev/onlook/430860fdd6ebd403e21eb2bc340925edef3ed158/apps/web/client/public/assets/dunes-login-light.png -------------------------------------------------------------------------------- /apps/web/client/public/assets/mountains.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlook-dev/onlook/430860fdd6ebd403e21eb2bc340925edef3ed158/apps/web/client/public/assets/mountains.png -------------------------------------------------------------------------------- /apps/web/client/public/assets/new-yt-thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlook-dev/onlook/430860fdd6ebd403e21eb2bc340925edef3ed158/apps/web/client/public/assets/new-yt-thumbnail.png -------------------------------------------------------------------------------- /apps/web/client/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlook-dev/onlook/430860fdd6ebd403e21eb2bc340925edef3ed158/apps/web/client/public/favicon.ico -------------------------------------------------------------------------------- /apps/web/client/src/app/_components/button-link.tsx: -------------------------------------------------------------------------------- 1 | // Reusable button-link component 2 | export function ButtonLink({ href, children, rightIcon }: { href: string; children: React.ReactNode; rightIcon?: React.ReactNode }) { 3 | return ( 4 | 9 | {children} 10 | {rightIcon && {rightIcon}} 11 | 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /apps/web/client/src/app/_components/hero/create-error.tsx: -------------------------------------------------------------------------------- 1 | import { useCreateManager } from '@/components/store/create'; 2 | import { observer } from 'mobx-react-lite'; 3 | import { motion } from 'motion/react'; 4 | 5 | export const CreateError = observer(() => { 6 | const createManager = useCreateManager(); 7 | const error = createManager.error; 8 | 9 | return ( 10 | 17 | {error} 18 | 19 | ); 20 | }); -------------------------------------------------------------------------------- /apps/web/client/src/app/_components/hero/high-demand.tsx: -------------------------------------------------------------------------------- 1 | import { motion } from 'motion/react'; 2 | 3 | export function HighDemand({ isMounted }: { isMounted: boolean }) { 4 | const isHighDemand = false; 5 | 6 | return ( 7 | 14 | {"We're currently experiencing high demand. Project may fail to create."} 15 | 16 | ); 17 | } 18 | 19 | -------------------------------------------------------------------------------- /apps/web/client/src/app/_components/theme.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { ThemeProvider as NextThemesProvider } from 'next-themes'; 4 | import * as React from 'react'; 5 | 6 | export function ThemeProvider({ 7 | children, 8 | ...props 9 | }: React.ComponentProps) { 10 | return {children}; 11 | } 12 | -------------------------------------------------------------------------------- /apps/web/client/src/app/auth/redirect/page.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { useRouter } from 'next/navigation'; 4 | import { useEffect } from 'react'; 5 | 6 | export default function AuthRedirect() { 7 | const router = useRouter(); 8 | 9 | useEffect(() => { 10 | const returnUrl = localStorage.getItem('returnUrl') || '/'; 11 | localStorage.removeItem('returnUrl'); 12 | router.push(returnUrl); 13 | }, [router]); 14 | 15 | return ( 16 |
17 |
18 |

Redirecting...

19 |

Please wait while we redirect you back.

20 |
21 |
22 | ); 23 | } -------------------------------------------------------------------------------- /apps/web/client/src/app/fonts.ts: -------------------------------------------------------------------------------- 1 | import { Vujahday_Script } from 'next/font/google'; 2 | 3 | export const vujahdayScript = Vujahday_Script({ 4 | weight: '400', 5 | subsets: ['latin'], 6 | }); -------------------------------------------------------------------------------- /apps/web/client/src/app/invitation/[id]/layout.tsx: -------------------------------------------------------------------------------- 1 | import { type Metadata } from 'next'; 2 | 3 | export const metadata: Metadata = { 4 | title: 'Onlook', 5 | description: 'Onlook – Invitation', 6 | }; 7 | 8 | export default async function Layout({ children }: Readonly<{ children: React.ReactNode }>) { 9 | return ( 10 |
11 | {children} 12 |
13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /apps/web/client/src/app/invitation/[id]/page.tsx: -------------------------------------------------------------------------------- 1 | import { Main } from './_components/main'; 2 | 3 | export default async function Page({ params }: { params: Promise<{ id: string }> }) { 4 | const id = (await params).id; 5 | 6 | return
; 7 | } 8 | -------------------------------------------------------------------------------- /apps/web/client/src/app/login/error.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | export default function ErrorPage() { 3 | return

Sorry, something went wrong

; 4 | } 5 | -------------------------------------------------------------------------------- /apps/web/client/src/app/project/[id]/_components/canvas/frames.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { useEditorEngine } from '@/components/store/editor'; 4 | import type { FrameImpl } from '@/components/store/editor/frames/frame'; 5 | import { observer } from 'mobx-react-lite'; 6 | import { FrameView } from './frame'; 7 | 8 | export const Frames = observer(() => { 9 | const editorEngine = useEditorEngine(); 10 | const frames = editorEngine.frames.frames; 11 | 12 | return ( 13 |
14 | {frames.map((frame: FrameImpl) => ( 15 | 16 | ))} 17 |
18 | ); 19 | }); 20 | -------------------------------------------------------------------------------- /apps/web/client/src/app/project/[id]/_components/canvas/overlay/elements/rect/hover.tsx: -------------------------------------------------------------------------------- 1 | import type { RectDimensions } from '@onlook/models'; 2 | import React from 'react'; 3 | import { BaseRect } from './base'; 4 | 5 | interface HoverRectProps { 6 | rect: RectDimensions | null; 7 | isComponent?: boolean; 8 | } 9 | 10 | export const HoverRect: React.FC = ({ rect, isComponent }) => { 11 | if (!rect) { 12 | return null; 13 | } 14 | return ; 15 | }; 16 | -------------------------------------------------------------------------------- /apps/web/client/src/app/project/[id]/_components/canvas/overlay/elements/rect/insert.tsx: -------------------------------------------------------------------------------- 1 | import type { RectDimensions } from '@onlook/models'; 2 | import React from 'react'; 3 | import { BaseRect } from './base'; 4 | 5 | interface InsertRectProps { 6 | rect: RectDimensions | null; 7 | } 8 | 9 | export const InsertRect: React.FC = ({ rect }) => { 10 | if (!rect) { 11 | return null; 12 | } 13 | return ; 14 | }; 15 | -------------------------------------------------------------------------------- /apps/web/client/src/app/project/[id]/_components/editor-bar/panels/index.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { motion } from 'motion/react'; 4 | import { LayoutPosition } from './layout-position'; 5 | import { Typography } from './typography'; 6 | 7 | export const Panels = ({ selectedElement }: { selectedElement: string }) => { 8 | return ( 9 | 22 |
23 | 24 | 25 |
26 |
27 | ); 28 | }; 29 | -------------------------------------------------------------------------------- /apps/web/client/src/app/project/[id]/_components/editor-bar/panels/panel-bar/bar.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { Icons } from '@onlook/ui/icons'; 4 | import * as React from 'react'; 5 | import { PanelButton } from './button'; 6 | 7 | export const ViewButtons = () => { 8 | const [activeView, setActiveView] = React.useState<'text' | 'grid'>('text'); 9 | 10 | return ( 11 |
12 | setActiveView('text')} 16 | /> 17 | setActiveView('grid')} 21 | /> 22 |
23 | ); 24 | }; 25 | -------------------------------------------------------------------------------- /apps/web/client/src/app/project/[id]/_components/editor-bar/separator.tsx: -------------------------------------------------------------------------------- 1 | export const InputSeparator = () => { 2 | return
; 3 | }; 4 | -------------------------------------------------------------------------------- /apps/web/client/src/app/project/[id]/_components/left-panel/brand-tab/color-panel/color-row.tsx: -------------------------------------------------------------------------------- 1 | interface ColorRowProps { 2 | label: string; 3 | colors: string[]; 4 | } 5 | 6 | export const ColorRow = ({ label, colors }: ColorRowProps) => ( 7 |
8 | {label} 9 |
10 | {colors.map((color, index) => ( 11 |
16 | ))} 17 |
18 |
19 | ); 20 | -------------------------------------------------------------------------------- /apps/web/client/src/app/project/[id]/_components/left-panel/layers-tab/tree/tree-row.tsx: -------------------------------------------------------------------------------- 1 | import type { LayerNode } from '@onlook/models/element'; 2 | import type { RefObject } from 'react'; 3 | import type { NodeApi } from 'react-arborist'; 4 | 5 | interface TreeRowProps { 6 | node: NodeApi; 7 | innerRef: RefObject; 8 | attrs: Record; 9 | children: React.ReactNode; 10 | } 11 | 12 | export const TreeRow = ({ innerRef, attrs, children }: TreeRowProps) => { 13 | return ( 14 |
15 | {children} 16 |
17 | ); 18 | }; 19 | -------------------------------------------------------------------------------- /apps/web/client/src/app/project/[id]/_components/members/index.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { Button } from '@onlook/ui/button'; 4 | import { Icons } from '@onlook/ui/icons/index'; 5 | import { Popover, PopoverContent, PopoverTrigger } from '@onlook/ui/popover'; 6 | import { useState } from 'react'; 7 | import { MembersContent } from './members-content'; 8 | 9 | export const Members = ({ projectId }: { projectId: string }) => { 10 | const [isOpen, setIsOpen] = useState(false); 11 | 12 | return ( 13 | 14 | 15 | 18 | 19 | 20 | 21 | 22 | 23 | ); 24 | }; 25 | -------------------------------------------------------------------------------- /apps/web/client/src/app/project/[id]/_components/members/member-row.tsx: -------------------------------------------------------------------------------- 1 | import type { ProjectRole, UserMetadata } from '@onlook/models'; 2 | import { Avatar, AvatarFallback, AvatarImage } from '@onlook/ui/avatar'; 3 | import { getInitials } from '@onlook/utility'; 4 | 5 | interface MemberRowProps { 6 | user: UserMetadata; 7 | role: ProjectRole; 8 | } 9 | 10 | export const MemberRow = ({ user, role }: MemberRowProps) => { 11 | const initials = getInitials(user.name ?? ''); 12 | 13 | return ( 14 |
15 | 16 | {user?.avatarUrl && } 17 | {initials} 18 | 19 |
20 |
{user.name}
21 |
{user.email}
22 |
23 |
24 | ); 25 | }; 26 | -------------------------------------------------------------------------------- /apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/chat-messages/assistant-message.tsx: -------------------------------------------------------------------------------- 1 | import type { AssistantChatMessage } from '@onlook/models'; 2 | import { MessageContent } from './message-content'; 3 | 4 | export const AssistantMessage = ({ message }: { message: AssistantChatMessage }) => { 5 | return ( 6 |
7 |
8 | 14 |
15 |
16 | ); 17 | }; 18 | -------------------------------------------------------------------------------- /apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/context-pills/sent-context-pill.tsx: -------------------------------------------------------------------------------- 1 | import { type ChatMessageContext } from '@onlook/models/chat'; 2 | import { getContextIcon, getTruncatedName } from './helpers'; 3 | 4 | export function SentContextPill({ context }: { context: ChatMessageContext }) { 5 | return ( 6 | 10 | {getContextIcon(context)} 11 | {getTruncatedName(context)} 12 | 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /apps/web/client/src/app/project/[id]/_components/right-panel/chat-tab/index.tsx: -------------------------------------------------------------------------------- 1 | import { ChatInput } from './chat-input'; 2 | import { ChatMessages } from './chat-messages'; 3 | import { ErrorSection } from './error'; 4 | 5 | export const ChatTab = () => { 6 | return ( 7 |
8 |
9 | 10 |
11 | 12 | 13 |
14 | ); 15 | }; 16 | -------------------------------------------------------------------------------- /apps/web/client/src/app/project/[id]/_hooks/use-tab-active.tsx: -------------------------------------------------------------------------------- 1 | import { useCallback, useEffect, useState } from 'react'; 2 | 3 | type TabActivityState = 'active' | 'inactive' | 'reactivated'; 4 | 5 | export function useTabActive() { 6 | const [tabState, setTabState] = useState('active'); 7 | 8 | const handleVisibilityChange = useCallback(() => { 9 | const nowActive = document.visibilityState === 'visible'; 10 | if (!nowActive) { 11 | setTabState('inactive'); 12 | } else if (tabState === 'inactive') { 13 | setTabState('reactivated'); 14 | } else { 15 | setTabState('active'); 16 | } 17 | }, [tabState]); 18 | 19 | useEffect(() => { 20 | document.addEventListener('visibilitychange', handleVisibilityChange); 21 | return () => { 22 | document.removeEventListener('visibilitychange', handleVisibilityChange); 23 | }; 24 | }, [handleVisibilityChange]); 25 | 26 | return { tabState }; 27 | } 28 | -------------------------------------------------------------------------------- /apps/web/client/src/app/project/[id]/layout.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { useEditorEngine } from "@/components/store/editor"; 4 | import { useProjectManager } from "@/components/store/project"; 5 | import { useEffect } from "react"; 6 | import { ChatProvider } from './_hooks/use-chat'; 7 | 8 | export default function ProjectLayout({ children }: { children: React.ReactNode }) { 9 | const editorEngine = useEditorEngine(); 10 | const projectManager = useProjectManager(); 11 | 12 | useEffect(() => { 13 | return () => { 14 | projectManager.clear() 15 | editorEngine.clear(); 16 | }; 17 | }, []); 18 | 19 | return {children}; 20 | } -------------------------------------------------------------------------------- /apps/web/client/src/app/project/[id]/page.tsx: -------------------------------------------------------------------------------- 1 | import { Main } from './_components/main'; 2 | 3 | export default async function Page({ params }: { params: Promise<{ id: string }> }) { 4 | const projectId = (await params).id; 5 | if (!projectId) { 6 | return
Invalid project ID
; 7 | } 8 | return ( 9 |
10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /apps/web/client/src/app/projects/layout.tsx: -------------------------------------------------------------------------------- 1 | import { Routes } from '@/utils/constants'; 2 | import { createClient } from '@/utils/supabase/server'; 3 | import { type Metadata } from 'next'; 4 | import { redirect } from 'next/navigation'; 5 | 6 | export const metadata: Metadata = { 7 | title: 'Onlook', 8 | description: 'Onlook – Projects', 9 | }; 10 | 11 | export default async function Layout({ children }: Readonly<{ children: React.ReactNode }>) { 12 | const supabase = await createClient(); 13 | const { 14 | data: { session }, 15 | } = await supabase.auth.getSession(); 16 | if (!session) { 17 | redirect(Routes.LOGIN); 18 | } 19 | return <>{children}; 20 | } 21 | -------------------------------------------------------------------------------- /apps/web/client/src/components/store/create/index.ts: -------------------------------------------------------------------------------- 1 | import { createContext, useContext } from 'react'; 2 | import { CreateManager } from './manager'; 3 | 4 | const createManager = new CreateManager(); 5 | const CreateContext = createContext(createManager); 6 | export const useCreateManager = () => useContext(CreateContext); 7 | -------------------------------------------------------------------------------- /apps/web/client/src/components/store/editor/chat/helpers.ts: -------------------------------------------------------------------------------- 1 | export const PROMPT_TOO_LONG_ERROR = `Our conversation memory is full. Please start a new chat to continue.`; 2 | 3 | export function isPromptTooLongError(content: string) { 4 | return ( 5 | content.includes('invalid_request_error') && 6 | (content.includes('prompt is too long') || content.includes('exceed context limit')) 7 | ); 8 | } 9 | -------------------------------------------------------------------------------- /apps/web/client/src/components/store/editor/code/helpers.ts: -------------------------------------------------------------------------------- 1 | import type { CodeDiffRequest } from '@onlook/models/code'; 2 | 3 | export async function getOrCreateCodeDiffRequest( 4 | oid: string, 5 | oidToCodeChange: Map, 6 | ): Promise { 7 | let diffRequest = oidToCodeChange.get(oid); 8 | if (!diffRequest) { 9 | diffRequest = { 10 | oid, 11 | structureChanges: [], 12 | attributes: {}, 13 | textContent: null, 14 | overrideClasses: null, 15 | }; 16 | oidToCodeChange.set(oid, diffRequest); 17 | } 18 | return diffRequest; 19 | } 20 | -------------------------------------------------------------------------------- /apps/web/client/src/components/store/editor/index.ts: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { createContext, useContext } from 'react'; 4 | import { projectManager } from '../project'; 5 | import { userManager } from '../user'; 6 | import { EditorEngine } from './engine'; 7 | 8 | const editorEngine = new EditorEngine(projectManager, userManager); 9 | const EditorEngineContext = createContext(editorEngine); 10 | export const useEditorEngine = () => useContext(EditorEngineContext); 11 | -------------------------------------------------------------------------------- /apps/web/client/src/components/store/editor/sandbox/helpers.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import parserEstree from 'prettier/plugins/estree'; 3 | import parserTypescript from 'prettier/plugins/typescript'; 4 | import prettier from 'prettier/standalone'; 5 | 6 | const SANDBOX_ROOT = '/project/sandbox'; 7 | 8 | export function normalizePath(p: string): string { 9 | let abs = path.isAbsolute(p) ? p : path.join(SANDBOX_ROOT, p); 10 | let relative = path.relative(SANDBOX_ROOT, abs); 11 | return relative.replace(/\\/g, '/'); // Always POSIX style 12 | } 13 | 14 | export async function formatContent(filePath: string, content: string): Promise { 15 | try { 16 | // Use browser standalone version with necessary plugins 17 | const formattedContent = await prettier.format(content, { 18 | filepath: filePath, 19 | plugins: [parserEstree, parserTypescript], 20 | parser: 'typescript', 21 | }); 22 | return formattedContent; 23 | } catch (error: any) { 24 | console.error('Error formatting file:', error); 25 | return content; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /apps/web/client/src/components/store/project/index.ts: -------------------------------------------------------------------------------- 1 | import { createContext, useContext } from 'react'; 2 | import { ProjectManager } from './manager'; 3 | 4 | export const projectManager = new ProjectManager(); 5 | const ProjectContext = createContext(projectManager); 6 | export const useProjectManager = () => useContext(ProjectContext); 7 | -------------------------------------------------------------------------------- /apps/web/client/src/components/store/projects/index.ts: -------------------------------------------------------------------------------- 1 | import { createContext, useContext } from 'react'; 2 | import { userManager } from '../user'; 3 | import { ProjectsManager } from './manager'; 4 | 5 | const projectsManager = new ProjectsManager(userManager); 6 | const ProjectsContext = createContext(projectsManager); 7 | export const useProjectsManager = () => useContext(ProjectsContext); 8 | -------------------------------------------------------------------------------- /apps/web/client/src/components/store/user/index.ts: -------------------------------------------------------------------------------- 1 | import { createContext, useContext } from 'react'; 2 | import { UserManager } from './manager'; 3 | 4 | export const userManager = new UserManager(); 5 | const UserContext = createContext(userManager); 6 | export const useUserManager = () => useContext(UserContext); 7 | -------------------------------------------------------------------------------- /apps/web/client/src/components/store/user/language.ts: -------------------------------------------------------------------------------- 1 | import { Language } from '@onlook/constants'; 2 | import localforage from 'localforage'; 3 | 4 | export class LanguageManager { 5 | constructor() {} 6 | 7 | update(language: Language) { 8 | localforage.setItem('app-language', language); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /apps/web/client/src/components/ui/dunes.tsx: -------------------------------------------------------------------------------- 1 | import Image from 'next/image'; 2 | 3 | export function Dunes() { 4 | return ( 5 |
6 | Onlook dunes dark 13 | Onlook dunes light 20 |
21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /apps/web/client/src/middleware.ts: -------------------------------------------------------------------------------- 1 | import { updateSession } from '@/utils/supabase/middleware'; 2 | import { type NextRequest } from 'next/server'; 3 | 4 | export async function middleware(request: NextRequest) { 5 | // update user's auth session 6 | return await updateSession(request); 7 | } 8 | 9 | export const config = { 10 | matcher: [ 11 | /* 12 | * Match all request paths except for the ones starting with: 13 | * - _next/static (static files) 14 | * - _next/image (image optimization files) 15 | * - favicon.ico (favicon file) 16 | * Feel free to modify this pattern to include more paths. 17 | */ 18 | '/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)', 19 | ], 20 | }; 21 | -------------------------------------------------------------------------------- /apps/web/client/src/pages/_error.tsx: -------------------------------------------------------------------------------- 1 | function Error({ statusCode }: { statusCode: number }) { 2 | return ( 3 |

4 | {statusCode 5 | ? `An error ${statusCode} occurred on server` 6 | : 'An error occurred on client'} 7 |

8 | ); 9 | } 10 | 11 | Error.getInitialProps = ({ res, err }: { res: any; err: any }) => { 12 | const statusCode = res ? res.statusCode : err ? err.statusCode : 404; 13 | return { statusCode }; 14 | }; 15 | 16 | export default Error; 17 | -------------------------------------------------------------------------------- /apps/web/client/src/server/api/routers/forward/index.ts: -------------------------------------------------------------------------------- 1 | export * from './editor'; 2 | -------------------------------------------------------------------------------- /apps/web/client/src/server/api/routers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './forward'; 2 | export * from './invitation'; 3 | export * from './member'; 4 | export * from './project'; 5 | export * from './sandbox'; 6 | export * from './user'; 7 | -------------------------------------------------------------------------------- /apps/web/client/src/styles/globals.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | overscroll-behavior: none; 4 | overflow-x: hidden; 5 | } -------------------------------------------------------------------------------- /apps/web/client/src/trpc/client.ts: -------------------------------------------------------------------------------- 1 | import { createTRPCClient } from '@trpc/client'; 2 | import { type inferRouterInputs, type inferRouterOutputs } from '@trpc/server'; 3 | import { type AppRouter } from '~/server/api/root'; 4 | import { links } from './helpers'; 5 | 6 | /** 7 | * Inference helper for inputs. 8 | * 9 | * @example type HelloInput = RouterInputs['example']['hello'] 10 | */ 11 | export type RouterInputs = inferRouterInputs; 12 | 13 | /** 14 | * Inference helper for outputs. 15 | * 16 | * @example type HelloOutput = RouterOutputs['example']['hello'] 17 | */ 18 | export type RouterOutputs = inferRouterOutputs; 19 | 20 | export const api = createTRPCClient({ 21 | links, 22 | }); 23 | -------------------------------------------------------------------------------- /apps/web/client/src/trpc/helpers.ts: -------------------------------------------------------------------------------- 1 | import { httpBatchStreamLink, loggerLink } from '@trpc/client'; 2 | import SuperJSON from 'superjson'; 3 | 4 | export function getBaseUrl() { 5 | if (typeof window !== 'undefined') return window.location.origin; 6 | if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`; 7 | return `http://localhost:${process.env.PORT ?? 3000}`; 8 | } 9 | 10 | export const links = [ 11 | loggerLink({ 12 | enabled: (op) => 13 | process.env.NODE_ENV === 'development' || 14 | (op.direction === 'down' && op.result instanceof Error), 15 | }), 16 | httpBatchStreamLink({ 17 | transformer: SuperJSON, 18 | url: getBaseUrl() + '/api/trpc', 19 | headers: () => { 20 | const headers = new Headers(); 21 | headers.set('x-trpc-source', 'vanilla-client'); 22 | return headers; 23 | }, 24 | }), 25 | ]; 26 | -------------------------------------------------------------------------------- /apps/web/client/src/trpc/query-client.ts: -------------------------------------------------------------------------------- 1 | import { defaultShouldDehydrateQuery, QueryClient } from '@tanstack/react-query'; 2 | import SuperJSON from 'superjson'; 3 | 4 | export const createQueryClient = () => 5 | new QueryClient({ 6 | defaultOptions: { 7 | queries: { 8 | // With SSR, we usually want to set some default staleTime 9 | // above 0 to avoid refetching immediately on the client 10 | staleTime: 30 * 1000, 11 | }, 12 | dehydrate: { 13 | serializeData: SuperJSON.serialize, 14 | shouldDehydrateQuery: (query) => 15 | defaultShouldDehydrateQuery(query) || query.state.status === 'pending', 16 | }, 17 | hydrate: { 18 | deserializeData: SuperJSON.deserialize, 19 | }, 20 | }, 21 | }); 22 | -------------------------------------------------------------------------------- /apps/web/client/src/trpc/server.ts: -------------------------------------------------------------------------------- 1 | import 'server-only'; 2 | 3 | import { createHydrationHelpers } from '@trpc/react-query/rsc'; 4 | import { headers } from 'next/headers'; 5 | import { cache } from 'react'; 6 | import { createCaller, type AppRouter } from '~/server/api/root'; 7 | import { createTRPCContext } from '~/server/api/trpc'; 8 | import { createQueryClient } from './query-client'; 9 | 10 | /** 11 | * This wraps the `createTRPCContext` helper and provides the required context for the tRPC API when 12 | * handling a tRPC call from a React Server Component. 13 | */ 14 | const createContext = cache(async () => { 15 | const heads = new Headers(await headers()); 16 | heads.set('x-trpc-source', 'rsc'); 17 | 18 | return createTRPCContext({ 19 | headers: heads, 20 | }); 21 | }); 22 | 23 | const getQueryClient = cache(createQueryClient); 24 | const caller = createCaller(createContext); 25 | 26 | export const { trpc: api, HydrateClient } = createHydrationHelpers( 27 | caller, 28 | getQueryClient, 29 | ); 30 | -------------------------------------------------------------------------------- /apps/web/client/src/utils/analytics/index.ts: -------------------------------------------------------------------------------- 1 | 2 | export function sendAnalytics(event: string, properties?: Record) { 3 | console.log('sendAnalytics', event, properties); 4 | } 5 | -------------------------------------------------------------------------------- /apps/web/client/src/utils/analytics/server.ts: -------------------------------------------------------------------------------- 1 | import { env } from "@/env"; 2 | import { PostHog } from "posthog-node"; 3 | 4 | class PostHogSingleton { 5 | private static instance: PostHog | null = null; 6 | private constructor() { } 7 | 8 | public static getInstance(): PostHog | null { 9 | if (!env.NEXT_PUBLIC_POSTHOG_KEY) { 10 | console.warn('PostHog key not found'); 11 | return null; 12 | } 13 | if (!PostHogSingleton.instance) { 14 | PostHogSingleton.instance = new PostHog(env.NEXT_PUBLIC_POSTHOG_KEY, { 15 | host: env.NEXT_PUBLIC_POSTHOG_HOST, 16 | flushAt: 1, 17 | flushInterval: 0, 18 | }); 19 | } 20 | return PostHogSingleton.instance; 21 | } 22 | } 23 | 24 | export const client = PostHogSingleton.getInstance(); -------------------------------------------------------------------------------- /apps/web/client/src/utils/constants/index.ts: -------------------------------------------------------------------------------- 1 | export const Routes = { 2 | HOME: '/', 3 | LOGIN: '/login', 4 | PROJECTS: '/projects', 5 | PROJECT: '/project', 6 | }; 7 | -------------------------------------------------------------------------------- /apps/web/client/src/utils/supabase/client.ts: -------------------------------------------------------------------------------- 1 | import { createBrowserClient } from '@supabase/ssr'; 2 | 3 | export function createClient() { 4 | // Create a supabase client on the browser with project's credentials 5 | return createBrowserClient( 6 | process.env.NEXT_PUBLIC_SUPABASE_URL!, 7 | process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, 8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /apps/web/client/test/sandbox/subdirectory.test.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlook-dev/onlook/430860fdd6ebd403e21eb2bc340925edef3ed158/apps/web/client/test/sandbox/subdirectory.test.ts -------------------------------------------------------------------------------- /apps/web/client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@onlook/typescript/next-react.json", 3 | "compilerOptions": { 4 | "baseUrl": ".", 5 | "paths": { 6 | "@/*": [ 7 | "./src/*" 8 | ], 9 | "/*": [ 10 | "./*" 11 | ], 12 | "~/*": [ 13 | "./src/*" 14 | ] 15 | } 16 | }, 17 | "include": [ 18 | "src", 19 | "tests", 20 | ".next/types/**/*.ts" 21 | ], 22 | "exclude": [ 23 | "node_modules", 24 | ".next" 25 | ] 26 | } -------------------------------------------------------------------------------- /apps/web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@onlook/web", 3 | "module": "index.ts", 4 | "type": "module", 5 | "private": true, 6 | "scripts": { 7 | "dev": "bun --filter '@onlook/web-client' --filter '@onlook/web-server' --filter '@onlook/web-preload' --filter '@onlook/web-template' dev", 8 | "dev:client": "bun --filter @onlook/web-client dev", 9 | "dev:server": "bun --filter @onlook/web-server dev", 10 | "dev:preload": "bun --filter @onlook/web-preload dev", 11 | "build:client": "bun --filter @onlook/web-client build", 12 | "start:client": "bun --filter @onlook/web-client start", 13 | "docker:up": "docker compose up", 14 | "docker:build": "docker compose build", 15 | "docker:down": "docker compose down", 16 | "docker:restart": "docker compose down && docker compose up" 17 | }, 18 | "devDependencies": { 19 | "@types/bun": "latest" 20 | }, 21 | "peerDependencies": { 22 | "typescript": "^5" 23 | }, 24 | "dependencies": {} 25 | } -------------------------------------------------------------------------------- /apps/web/preload/.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies (bun install) 2 | node_modules 3 | 4 | # output 5 | out 6 | *.tgz 7 | 8 | # code coverage 9 | coverage 10 | *.lcov 11 | 12 | # logs 13 | logs 14 | _.log 15 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json 16 | 17 | # dotenv environment variable files 18 | .env 19 | .env.development.local 20 | .env.test.local 21 | .env.production.local 22 | .env.local 23 | 24 | # caches 25 | .eslintcache 26 | .cache 27 | *.tsbuildinfo 28 | 29 | # IntelliJ based IDEs 30 | .idea 31 | 32 | # Finder (MacOS) folder config 33 | .DS_Store 34 | 35 | # CDN 36 | !dist/index.js -------------------------------------------------------------------------------- /apps/web/preload/Dockerfile: -------------------------------------------------------------------------------- 1 | # Use the official Bun image 2 | FROM oven/bun:slim 3 | 4 | # Set working directory 5 | WORKDIR /app 6 | 7 | # Copy package.json and bun.lockb (if they exist) 8 | COPY package.json bun.lock ./ 9 | 10 | # Install dependencies 11 | RUN bun install 12 | 13 | # Copy the rest of the application 14 | COPY . . 15 | 16 | # Expose port 8083 17 | EXPOSE 8083 18 | 19 | # Start the server 20 | CMD ["bun", "server/index.ts"] -------------------------------------------------------------------------------- /apps/web/preload/README.md: -------------------------------------------------------------------------------- 1 | # What is this? 2 | 3 | This is the preload script that will be injected into template project. 4 | Dependencies to this package is REQUIRED to be browser compatible in order to 5 | prevent runtime errors. Please keep external deps to a minimal and DO NOT IMPORT 6 | node dependencies. 7 | -------------------------------------------------------------------------------- /apps/web/preload/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@onlook/web-preload", 3 | "module": "script/index.ts", 4 | "type": "module", 5 | "scripts": { 6 | "dev": "concurrently \"bun run build:watch\" \"bun run serve\"", 7 | "serve": "bun --watch server/index.ts", 8 | "build": "bun build script/index.ts --outdir=dist --target=browser --sourcemap --external @onlook/utility", 9 | "build:watch": "bun run build --watch" 10 | }, 11 | "devDependencies": { 12 | "@types/bun": "latest", 13 | "@types/css-tree": "^2.3.10", 14 | "@types/lodash.debounce": "^4.0.9", 15 | "concurrently": "^9.1.2" 16 | }, 17 | "peerDependencies": { 18 | "typescript": "^5" 19 | }, 20 | "dependencies": { 21 | "@onlook/constants": "*", 22 | "@onlook/models": "*", 23 | "@onlook/penpal": "*", 24 | "css-tree": "^3.1.0", 25 | "lodash-es": "^4.17.21", 26 | "lodash.debounce": "^4.0.8", 27 | "nanoid": "^5.1.5", 28 | "penpal": "^7.0.4" 29 | } 30 | } -------------------------------------------------------------------------------- /apps/web/preload/script/api/elements/dom/image.ts: -------------------------------------------------------------------------------- 1 | import { StyleChangeType } from '@onlook/models/style'; 2 | import { cssManager } from '../../style'; 3 | 4 | export function insertImage(domId: string, image: string) { 5 | cssManager.updateStyle(domId, { 6 | backgroundImage: { value: `url(${image})`, type: StyleChangeType.Value }, 7 | }); 8 | } 9 | 10 | export function removeImage(domId: string) { 11 | cssManager.updateStyle(domId, { 12 | backgroundImage: { value: 'none', type: StyleChangeType.Value }, 13 | }); 14 | } 15 | -------------------------------------------------------------------------------- /apps/web/preload/script/api/events/index.ts: -------------------------------------------------------------------------------- 1 | import { listenForDomMutation } from './dom'; 2 | 3 | export function listenForEvents() { 4 | listenForWindowEvents(); 5 | listenForDomMutation(); 6 | } 7 | 8 | function listenForWindowEvents() { 9 | window.addEventListener('resize', () => { 10 | // ipcRenderer.sendToHost(WebviewChannels.WINDOW_RESIZED); 11 | }); 12 | } 13 | -------------------------------------------------------------------------------- /apps/web/preload/script/api/state.ts: -------------------------------------------------------------------------------- 1 | import { penpalParent } from ".."; 2 | 3 | export function setFrameId(frameId: string) { 4 | (window as any)._onlookFrameId = frameId; 5 | } 6 | 7 | export function getFrameId(): string { 8 | const frameId = (window as any)._onlookFrameId; 9 | if (!frameId) { 10 | console.warn('Frame id not found'); 11 | penpalParent?.getFrameId().then((id) => { 12 | setFrameId(id); 13 | }); 14 | return ''; 15 | } 16 | return frameId; 17 | } 18 | -------------------------------------------------------------------------------- /apps/web/preload/script/api/style/index.ts: -------------------------------------------------------------------------------- 1 | export * from './css-manager'; 2 | export * from './update'; 3 | 4 | -------------------------------------------------------------------------------- /apps/web/preload/script/api/style/update.ts: -------------------------------------------------------------------------------- 1 | import type { Change, DomElement, StyleChange } from "@onlook/models"; 2 | import { cssManager } from "."; 3 | import { getElementByDomId } from "../elements"; 4 | 5 | export function updateStyle(domId: string, change: Change>): DomElement | null { 6 | cssManager.updateStyle(domId, change.updated); 7 | return getElementByDomId(domId, true); 8 | } 9 | -------------------------------------------------------------------------------- /apps/web/preload/script/api/theme/index.ts: -------------------------------------------------------------------------------- 1 | import { SystemTheme } from '@onlook/models'; 2 | 3 | export function getTheme(): SystemTheme { 4 | try { 5 | return (window?.localStorage.getItem('theme') as SystemTheme) || SystemTheme.LIGHT; 6 | } catch (error) { 7 | console.warn('Failed to get theme', error); 8 | return SystemTheme.LIGHT; 9 | } 10 | } 11 | 12 | export function setTheme(theme: SystemTheme) { 13 | try { 14 | if (theme === SystemTheme.DARK) { 15 | document.documentElement.classList.add('dark'); 16 | window?.localStorage.setItem('theme', SystemTheme.DARK); 17 | } else { 18 | document.documentElement.classList.remove('dark'); 19 | window?.localStorage.setItem('theme', SystemTheme.LIGHT); 20 | } 21 | return true; 22 | } catch (error) { 23 | console.warn('Failed to set theme', error); 24 | return false; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /apps/web/preload/script/helpers/assert.ts: -------------------------------------------------------------------------------- 1 | export function assertNever(n: never): never { 2 | throw new Error(`Expected \`never\`, found: ${JSON.stringify(n)}`); 3 | } 4 | -------------------------------------------------------------------------------- /apps/web/preload/script/helpers/clone.ts: -------------------------------------------------------------------------------- 1 | export const jsonClone = (obj: T): T => JSON.parse(JSON.stringify(obj)); -------------------------------------------------------------------------------- /apps/web/preload/script/helpers/ids.ts: -------------------------------------------------------------------------------- 1 | import { EditorAttributes } from '@onlook/constants'; 2 | import { nanoid } from 'nanoid/non-secure'; 3 | 4 | export function getOrAssignDomId(node: HTMLElement): string { 5 | let domId = node.getAttribute(EditorAttributes.DATA_ONLOOK_DOM_ID) as string; 6 | if (!domId) { 7 | domId = `odid-${nanoid()}`; 8 | node.setAttribute(EditorAttributes.DATA_ONLOOK_DOM_ID, domId); 9 | } 10 | return domId; 11 | } 12 | 13 | export const VALID_DATA_ATTR_CHARS = 'abcdefghijklmnopqrstuvwxyz0123456789-._:'; 14 | 15 | export function getOid(node: HTMLElement): string | undefined { 16 | return node.getAttribute(EditorAttributes.DATA_ONLOOK_ID) as string; 17 | } 18 | 19 | export function getInstanceId(node: HTMLElement): string | undefined { 20 | return node.getAttribute(EditorAttributes.DATA_ONLOOK_INSTANCE_ID) as string; 21 | } 22 | -------------------------------------------------------------------------------- /apps/web/preload/script/helpers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './assert'; 2 | export * from './clone'; 3 | export * from './dom'; 4 | -------------------------------------------------------------------------------- /apps/web/preload/server/index.ts: -------------------------------------------------------------------------------- 1 | import { serve } from "bun"; 2 | import path from "path"; 3 | 4 | const server = serve({ 5 | port: 8083, 6 | async fetch(req) { 7 | const url = new URL(req.url); 8 | if (url.pathname === "/") { 9 | try { 10 | const resolvedPath = path.resolve(import.meta.dir + "/../dist/index.js") 11 | 12 | const file = Bun.file(resolvedPath); 13 | return new Response(file, { 14 | headers: { 15 | "Content-Type": "application/javascript", 16 | "Cache-Control": "public, max-age=31536000", 17 | "Access-Control-Allow-Origin": "*", 18 | }, 19 | }); 20 | } catch (error) { 21 | return new Response("Script not found", { status: 404 }); 22 | } 23 | } 24 | 25 | return new Response("Not found", { status: 404 }); 26 | }, 27 | }); 28 | 29 | console.log(`CDN server listening on http://localhost:${server.port}`); 30 | -------------------------------------------------------------------------------- /apps/web/preload/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@onlook/typescript/base.json", 3 | "compilerOptions": { 4 | "paths": { 5 | "@/*": [ 6 | "./src/*" 7 | ] 8 | } 9 | }, 10 | "include": [ 11 | "src", 12 | "test" 13 | ], 14 | "exclude": [ 15 | "node_modules" 16 | ] 17 | } -------------------------------------------------------------------------------- /apps/web/server/.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies (bun install) 2 | node_modules 3 | 4 | # output 5 | out 6 | dist 7 | *.tgz 8 | 9 | # code coverage 10 | coverage 11 | *.lcov 12 | 13 | # logs 14 | logs 15 | _.log 16 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json 17 | 18 | # dotenv environment variable files 19 | .env 20 | .env.development.local 21 | .env.test.local 22 | .env.production.local 23 | .env.local 24 | 25 | # caches 26 | .eslintcache 27 | .cache 28 | *.tsbuildinfo 29 | 30 | # IntelliJ based IDEs 31 | .idea 32 | 33 | # Finder (MacOS) folder config 34 | .DS_Store 35 | -------------------------------------------------------------------------------- /apps/web/server/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM oven/bun:slim 2 | 3 | WORKDIR /app 4 | 5 | COPY package.json bun.lock ./ 6 | RUN bun install 7 | 8 | COPY . . 9 | 10 | EXPOSE 8081 11 | 12 | CMD ["bun", "run", "dev"] -------------------------------------------------------------------------------- /apps/web/server/README.md: -------------------------------------------------------------------------------- 1 | # server 2 | 3 | To install dependencies: 4 | 5 | ```bash 6 | bun install 7 | ``` 8 | 9 | To run: 10 | 11 | ```bash 12 | bun run index.ts 13 | ``` 14 | 15 | This project was created using `bun init` in bun v1.2.9. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime. 16 | -------------------------------------------------------------------------------- /apps/web/server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@onlook/web-server", 3 | "module": "index.ts", 4 | "type": "module", 5 | "private": true, 6 | "scripts": { 7 | "dev": "bun run --watch src/index.ts", 8 | "build": "bun build src/index.ts --outdir dist --target bun", 9 | "start": "bun dist/index.js", 10 | "preview": "bun run build && bun run start" 11 | }, 12 | "devDependencies": { 13 | "@types/bun": "latest" 14 | }, 15 | "peerDependencies": { 16 | "typescript": "^5" 17 | }, 18 | "dependencies": { 19 | "@fastify/websocket": "^11.0.2", 20 | "@trpc/server": "^11.0.0", 21 | "fastify": "^5.2.2", 22 | "zod": "^3.24.2", 23 | "@onlook/rpc": "*" 24 | } 25 | } -------------------------------------------------------------------------------- /apps/web/server/src/index.ts: -------------------------------------------------------------------------------- 1 | import { editorServerConfig } from '@onlook/rpc'; 2 | import { createServer } from './server'; 3 | 4 | const server = createServer(editorServerConfig); 5 | 6 | server.start(); -------------------------------------------------------------------------------- /apps/web/server/src/router/context.ts: -------------------------------------------------------------------------------- 1 | import type { CreateFastifyContextOptions } from '@trpc/server/adapters/fastify'; 2 | 3 | export interface User { 4 | name: string[] | string; 5 | } 6 | 7 | export function createContext({ req, res }: CreateFastifyContextOptions) { 8 | const user: User = { name: req.headers.username ?? 'anonymous' }; 9 | 10 | return { req, res, user }; 11 | } 12 | 13 | export type Context = Awaited>; -------------------------------------------------------------------------------- /apps/web/server/src/router/index.ts: -------------------------------------------------------------------------------- 1 | import { sandboxRouter } from './routes/sandbox'; 2 | import { router } from './trpc'; 3 | 4 | export const appRouter = router({ 5 | sandbox: sandboxRouter, 6 | }); 7 | 8 | export type AppRouter = typeof appRouter; -------------------------------------------------------------------------------- /apps/web/server/src/router/routes/sandbox.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | import { publicProcedure, router } from '../trpc'; 3 | 4 | export const sandboxRouter = router({ 5 | create: publicProcedure 6 | .input(z.string()) 7 | .mutation(({ input }) => { 8 | return `hi ${input}`; 9 | }), 10 | 11 | start: publicProcedure 12 | .input(z.string()) 13 | .mutation(({ input }) => { 14 | return `hi ${input}`; 15 | }), 16 | 17 | stop: publicProcedure 18 | .input(z.string()) 19 | .mutation(({ input }) => { 20 | return { 21 | success: true, 22 | message: `Sandbox ${input} stopped`, 23 | timestamp: new Date().toISOString(), 24 | }; 25 | }), 26 | 27 | status: publicProcedure 28 | .input(z.string()) 29 | .query(({ input }) => { 30 | return { 31 | id: input, 32 | status: 'running', 33 | details: { cpu: '5%', memory: '120MB' }, 34 | uptime: 1200, 35 | }; 36 | }), 37 | 38 | }); 39 | -------------------------------------------------------------------------------- /apps/web/server/src/router/trpc.ts: -------------------------------------------------------------------------------- 1 | import { initTRPC } from '@trpc/server'; 2 | import superjson from 'superjson'; 3 | import type { Context } from './context'; 4 | 5 | const t = initTRPC.context().create({ 6 | transformer: superjson, 7 | errorFormatter({ shape }) { 8 | return shape; 9 | }, 10 | }); 11 | 12 | export const router = t.router; 13 | export const publicProcedure = t.procedure; -------------------------------------------------------------------------------- /apps/web/server/src/sandbox/index.ts: -------------------------------------------------------------------------------- 1 | export const start = async (sandboxId: string): Promise<{ 2 | previewUrl: string, 3 | editorUrl: string 4 | }> => { 5 | return { 6 | previewUrl: `http://localhost:8084`, 7 | editorUrl: `http://localhost:8080`, 8 | }; 9 | }; 10 | 11 | export const stop = async (sandboxId: string) => { 12 | return { 13 | previewUrl: `http://localhost:8084`, 14 | editorUrl: `http://localhost:8080`, 15 | }; 16 | }; 17 | 18 | export const status = async (sandboxId: string) => { 19 | return { 20 | previewUrl: `http://localhost:8084`, 21 | editorUrl: `http://localhost:8080`, 22 | }; 23 | }; -------------------------------------------------------------------------------- /apps/web/server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | // Environment setup & latest features 4 | "lib": ["ESNext"], 5 | "target": "ESNext", 6 | "module": "ESNext", 7 | "moduleDetection": "force", 8 | "jsx": "react-jsx", 9 | "allowJs": true, 10 | 11 | // Bundler mode 12 | "moduleResolution": "bundler", 13 | "allowImportingTsExtensions": true, 14 | "verbatimModuleSyntax": true, 15 | "noEmit": true, 16 | 17 | // Best practices 18 | "strict": true, 19 | "skipLibCheck": true, 20 | "noFallthroughCasesInSwitch": true, 21 | "noUncheckedIndexedAccess": true, 22 | 23 | // Some stricter flags (disabled by default) 24 | "noUnusedLocals": false, 25 | "noUnusedParameters": false, 26 | "noPropertyAccessFromIndexSignature": false 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /apps/web/server/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tsup' 2 | 3 | export default defineConfig({ 4 | entry: ['src/index.ts'], 5 | outDir: 'dist', 6 | target: 'node18', 7 | format: ['cjs'], 8 | splitting: false, 9 | clean: true, 10 | minify: true, 11 | sourcemap: false, 12 | shims: false, 13 | dts: false, 14 | external: [], 15 | esbuildOptions(options) { 16 | options.platform = 'node' 17 | } 18 | }) 19 | -------------------------------------------------------------------------------- /apps/web/template/.codesandbox/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "setupTasks": ["bun install"], 3 | "tasks": { 4 | "dev": { 5 | "name": "Dev Server", 6 | "command": "bun run dev", 7 | "preview": { 8 | "port": 3000 9 | }, 10 | "runAtStart": true 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /apps/web/template/.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM oven/bun:slim 2 | 3 | EXPOSE 3000 4 | -------------------------------------------------------------------------------- /apps/web/template/.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "build": { 3 | "dockerfile": "./Dockerfile" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /apps/web/template/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /apps/web/template/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | next-env.d.ts 16 | 17 | # production 18 | /build 19 | 20 | # misc 21 | .DS_Store 22 | *.pem 23 | 24 | # debug 25 | npm-debug.log* 26 | yarn-debug.log* 27 | yarn-error.log* 28 | 29 | # local env files 30 | .env*.local 31 | 32 | # vercel 33 | .vercel 34 | 35 | # typescript 36 | *.tsbuildinfo 37 | 38 | # vscode 39 | .vscode -------------------------------------------------------------------------------- /apps/web/template/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "printWidth": 100, 4 | "tabWidth": 4, 5 | "useTabs": false, 6 | "semi": true, 7 | "jsxSingleQuote": false, 8 | "bracketSpacing": true, 9 | "arrowParens": "always", 10 | "endOfLine": "lf" 11 | } 12 | -------------------------------------------------------------------------------- /apps/web/template/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM oven/bun:slim 2 | 3 | WORKDIR /app 4 | 5 | COPY package.json bun.lock ./ 6 | RUN bun install 7 | 8 | COPY . . 9 | 10 | EXPOSE 3000 11 | 12 | CMD ["bun", "run", "dev"] -------------------------------------------------------------------------------- /apps/web/template/README.md: -------------------------------------------------------------------------------- 1 | # Onlook Starter Template 2 | 3 |

4 | 5 |

6 | 7 | This is an [Onlook](https://onlook.com/) project set up with 8 | [Next.js](https://nextjs.org/), [TailwindCSS](https://tailwindcss.com/) and 9 | [ShadCN](https://ui.shadcn.com). 10 | 11 | ## Getting Started 12 | 13 | First, run the development server: 14 | 15 | ```bash 16 | npm run dev 17 | # or 18 | yarn dev 19 | # or 20 | pnpm dev 21 | # or 22 | bun dev 23 | ``` 24 | 25 | Open [http://localhost:3000](http://localhost:3000) in Onlook to see the result. 26 | -------------------------------------------------------------------------------- /apps/web/template/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlook-dev/onlook/430860fdd6ebd403e21eb2bc340925edef3ed158/apps/web/template/app/favicon.ico -------------------------------------------------------------------------------- /apps/web/template/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.ts", 8 | "css": "app/globals.css", 9 | "baseColor": "neutral", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils", 16 | "ui": "@/components/ui", 17 | "lib": "@/lib", 18 | "hooks": "@/hooks" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /apps/web/template/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { clsx, type ClassValue } from 'clsx'; 2 | import { twMerge } from 'tailwind-merge'; 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)); 6 | } 7 | -------------------------------------------------------------------------------- /apps/web/template/next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | output: 'standalone', 4 | devIndicators: false, 5 | }; 6 | export default nextConfig; 7 | -------------------------------------------------------------------------------- /apps/web/template/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@onlook/web-template", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "PORT=8084 next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint", 10 | "format": "prettier --write ." 11 | }, 12 | "dependencies": { 13 | "@radix-ui/react-slot": "^1.1.0", 14 | "class-variance-authority": "^0.7.0", 15 | "clsx": "^2.1.1", 16 | "lucide-react": "^0.438.0", 17 | "next": "14.2.26", 18 | "react": "^18", 19 | "react-dom": "^18", 20 | "tailwind-merge": "^2.5.2", 21 | "tailwindcss-animate": "^1.0.7" 22 | }, 23 | "devDependencies": { 24 | "@types/node": "^20", 25 | "@types/react": "^18", 26 | "@types/react-dom": "^18", 27 | "eslint": "^8", 28 | "eslint-config-next": "^15.1.6", 29 | "postcss": "^8", 30 | "prettier": "^3.3.3", 31 | "tailwindcss": "^3.4.1", 32 | "typescript": "^5" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /apps/web/template/postcss.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('postcss-load-config').Config} */ 2 | const config = { 3 | plugins: { 4 | tailwindcss: {}, 5 | }, 6 | }; 7 | 8 | export default config; 9 | -------------------------------------------------------------------------------- /apps/web/template/public/images/logo-text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlook-dev/onlook/430860fdd6ebd403e21eb2bc340925edef3ed158/apps/web/template/public/images/logo-text.png -------------------------------------------------------------------------------- /apps/web/template/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["dom", "dom.iterable", "esnext"], 4 | "allowJs": true, 5 | "skipLibCheck": true, 6 | "strict": true, 7 | "noEmit": true, 8 | "esModuleInterop": true, 9 | "module": "esnext", 10 | "moduleResolution": "bundler", 11 | "resolveJsonModule": true, 12 | "isolatedModules": true, 13 | "jsx": "preserve", 14 | "incremental": true, 15 | "plugins": [ 16 | { 17 | "name": "next" 18 | } 19 | ], 20 | "paths": { 21 | "@/*": ["./*"] 22 | } 23 | }, 24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 25 | "exclude": ["node_modules"] 26 | } 27 | -------------------------------------------------------------------------------- /assets/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlook-dev/onlook/430860fdd6ebd403e21eb2bc340925edef3ed158/assets/architecture.png -------------------------------------------------------------------------------- /assets/brand.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlook-dev/onlook/430860fdd6ebd403e21eb2bc340925edef3ed158/assets/brand.png -------------------------------------------------------------------------------- /assets/code-connect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlook-dev/onlook/430860fdd6ebd403e21eb2bc340925edef3ed158/assets/code-connect.png -------------------------------------------------------------------------------- /assets/contribute.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlook-dev/onlook/430860fdd6ebd403e21eb2bc340925edef3ed158/assets/contribute.png -------------------------------------------------------------------------------- /assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlook-dev/onlook/430860fdd6ebd403e21eb2bc340925edef3ed158/assets/favicon.ico -------------------------------------------------------------------------------- /assets/fork.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlook-dev/onlook/430860fdd6ebd403e21eb2bc340925edef3ed158/assets/fork.png -------------------------------------------------------------------------------- /assets/hld.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlook-dev/onlook/430860fdd6ebd403e21eb2bc340925edef3ed158/assets/hld.png -------------------------------------------------------------------------------- /assets/insert-div.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlook-dev/onlook/430860fdd6ebd403e21eb2bc340925edef3ed158/assets/insert-div.png -------------------------------------------------------------------------------- /assets/text-styling.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlook-dev/onlook/430860fdd6ebd403e21eb2bc340925edef3ed158/assets/text-styling.png -------------------------------------------------------------------------------- /assets/web-preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlook-dev/onlook/430860fdd6ebd403e21eb2bc340925edef3ed158/assets/web-preview.png -------------------------------------------------------------------------------- /bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlook-dev/onlook/430860fdd6ebd403e21eb2bc340925edef3ed158/bun.lockb -------------------------------------------------------------------------------- /docs/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["next/core-web-vitals", "next/typescript"] 3 | } 4 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | # deps 2 | /node_modules 3 | 4 | # generated content 5 | .contentlayer 6 | .content-collections 7 | .source 8 | 9 | # test & build 10 | /coverage 11 | /.next/ 12 | /out/ 13 | /build 14 | *.tsbuildinfo 15 | 16 | # misc 17 | .DS_Store 18 | *.pem 19 | /.pnp 20 | .pnp.js 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | # others 26 | .env*.local 27 | .vercel 28 | next-env.d.ts -------------------------------------------------------------------------------- /docs/content/docs/developer/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Developers 3 | description: Technical documentation for Onlook 4 | --- 5 | 6 | # Developer Documentation 7 | 8 | This section provides technical documentation for developers who want to understand Onlook's architecture, contribute to the project, or extend its functionality. 9 | 10 | ## Overview 11 | 12 | Onlook is structured as a monorepo with several interconnected apps and packages, primarily built using Next.js, React, TypeScript, and Vite. 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /docs/content/docs/tutorials/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Tutorials 3 | description: Step-by-step guides for using Onlook 4 | --- 5 | 6 | # Tutorials 7 | 8 | These tutorials will guide you through common tasks in Onlook. 9 | 10 | 11 | 12 | 13 | 14 | 15 | ## Creating Your First Project 16 | 17 | Learn how to create your first project in Onlook, including: 18 | 19 | - Setting up a new project 20 | - Adding components 21 | - Styling your components 22 | - Exporting your project 23 | 24 | ## Design to Code Workflow 25 | 26 | Learn how to convert designs to code using Onlook, including: 27 | 28 | - Importing designs 29 | - Converting designs to components 30 | - Customizing generated code 31 | - Integrating with your existing workflow 32 | -------------------------------------------------------------------------------- /docs/next.config.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially useful 3 | * for Docker builds. 4 | */ 5 | import { createMDX } from 'fumadocs-mdx/next'; 6 | import { NextConfig } from 'next'; 7 | import path from 'node:path'; 8 | 9 | const withMDX = createMDX(); 10 | 11 | const nextConfig: NextConfig = { 12 | reactStrictMode: true, 13 | }; 14 | 15 | if (process.env.NODE_ENV === 'development') { 16 | nextConfig.outputFileTracingRoot = path.join(__dirname, '../../..'); 17 | } 18 | 19 | export default withMDX(nextConfig); 20 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@onlook/docs", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "build": "next build", 7 | "dev": "next dev --turbo", 8 | "start": "next start", 9 | "postinstall": "fumadocs-mdx" 10 | }, 11 | "dependencies": { 12 | "next": "15.3.1", 13 | "react": "^19.1.0", 14 | "react-dom": "^19.1.0", 15 | "fumadocs-ui": "15.3.1", 16 | "fumadocs-core": "15.5.0", 17 | "fumadocs-mdx": "11.6.3", 18 | "@onlook/ui": "*" 19 | }, 20 | "devDependencies": { 21 | "@types/node": "22.15.12", 22 | "@types/react": "^19.1.3", 23 | "@types/react-dom": "^19.1.3", 24 | "typescript": "^5.8.3", 25 | "@types/mdx": "^2.0.13", 26 | "@tailwindcss/postcss": "^4.1.5", 27 | "tailwindcss": "^4.1.5", 28 | "postcss": "^8.5.3", 29 | "eslint": "^8", 30 | "eslint-config-next": "15.3.1" 31 | } 32 | } -------------------------------------------------------------------------------- /docs/postcss.config.mjs: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | '@tailwindcss/postcss': {}, 4 | }, 5 | }; 6 | -------------------------------------------------------------------------------- /docs/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlook-dev/onlook/430860fdd6ebd403e21eb2bc340925edef3ed158/docs/public/favicon.ico -------------------------------------------------------------------------------- /docs/public/images/ai-assistance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlook-dev/onlook/430860fdd6ebd403e21eb2bc340925edef3ed158/docs/public/images/ai-assistance.png -------------------------------------------------------------------------------- /docs/public/images/ai-mode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlook-dev/onlook/430860fdd6ebd403e21eb2bc340925edef3ed158/docs/public/images/ai-mode.png -------------------------------------------------------------------------------- /docs/public/images/canvas-architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlook-dev/onlook/430860fdd6ebd403e21eb2bc340925edef3ed158/docs/public/images/canvas-architecture.png -------------------------------------------------------------------------------- /docs/public/images/code-integration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlook-dev/onlook/430860fdd6ebd403e21eb2bc340925edef3ed158/docs/public/images/code-integration.png -------------------------------------------------------------------------------- /docs/public/images/code-mode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlook-dev/onlook/430860fdd6ebd403e21eb2bc340925edef3ed158/docs/public/images/code-mode.png -------------------------------------------------------------------------------- /docs/public/images/contribute-allow-edits.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlook-dev/onlook/430860fdd6ebd403e21eb2bc340925edef3ed158/docs/public/images/contribute-allow-edits.png -------------------------------------------------------------------------------- /docs/public/images/contribute-fork.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlook-dev/onlook/430860fdd6ebd403e21eb2bc340925edef3ed158/docs/public/images/contribute-fork.png -------------------------------------------------------------------------------- /docs/public/images/contribute-open-pr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlook-dev/onlook/430860fdd6ebd403e21eb2bc340925edef3ed158/docs/public/images/contribute-open-pr.png -------------------------------------------------------------------------------- /docs/public/images/create-project.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlook-dev/onlook/430860fdd6ebd403e21eb2bc340925edef3ed158/docs/public/images/create-project.png -------------------------------------------------------------------------------- /docs/public/images/edit-loop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlook-dev/onlook/430860fdd6ebd403e21eb2bc340925edef3ed158/docs/public/images/edit-loop.png -------------------------------------------------------------------------------- /docs/public/images/electron-to-web-after.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlook-dev/onlook/430860fdd6ebd403e21eb2bc340925edef3ed158/docs/public/images/electron-to-web-after.png -------------------------------------------------------------------------------- /docs/public/images/electron-to-web-before.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlook-dev/onlook/430860fdd6ebd403e21eb2bc340925edef3ed158/docs/public/images/electron-to-web-before.png -------------------------------------------------------------------------------- /docs/public/images/faq-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlook-dev/onlook/430860fdd6ebd403e21eb2bc340925edef3ed158/docs/public/images/faq-banner.png -------------------------------------------------------------------------------- /docs/public/images/features-overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlook-dev/onlook/430860fdd6ebd403e21eb2bc340925edef3ed158/docs/public/images/features-overview.png -------------------------------------------------------------------------------- /docs/public/images/figma-to-onlook-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlook-dev/onlook/430860fdd6ebd403e21eb2bc340925edef3ed158/docs/public/images/figma-to-onlook-banner.png -------------------------------------------------------------------------------- /docs/public/images/figma-to-onlook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlook-dev/onlook/430860fdd6ebd403e21eb2bc340925edef3ed158/docs/public/images/figma-to-onlook.png -------------------------------------------------------------------------------- /docs/public/images/full-architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlook-dev/onlook/430860fdd6ebd403e21eb2bc340925edef3ed158/docs/public/images/full-architecture.png -------------------------------------------------------------------------------- /docs/public/images/hero-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlook-dev/onlook/430860fdd6ebd403e21eb2bc340925edef3ed158/docs/public/images/hero-banner.png -------------------------------------------------------------------------------- /docs/public/images/layer-naming.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlook-dev/onlook/430860fdd6ebd403e21eb2bc340925edef3ed158/docs/public/images/layer-naming.png -------------------------------------------------------------------------------- /docs/public/images/modes-overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlook-dev/onlook/430860fdd6ebd403e21eb2bc340925edef3ed158/docs/public/images/modes-overview.png -------------------------------------------------------------------------------- /docs/public/images/onlook-interface.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlook-dev/onlook/430860fdd6ebd403e21eb2bc340925edef3ed158/docs/public/images/onlook-interface.png -------------------------------------------------------------------------------- /docs/public/images/quickstart-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlook-dev/onlook/430860fdd6ebd403e21eb2bc340925edef3ed158/docs/public/images/quickstart-banner.png -------------------------------------------------------------------------------- /docs/public/images/visual-edit-mode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlook-dev/onlook/430860fdd6ebd403e21eb2bc340925edef3ed158/docs/public/images/visual-edit-mode.png -------------------------------------------------------------------------------- /docs/public/images/visual-editor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlook-dev/onlook/430860fdd6ebd403e21eb2bc340925edef3ed158/docs/public/images/visual-editor.png -------------------------------------------------------------------------------- /docs/source.config.ts: -------------------------------------------------------------------------------- 1 | import { 2 | defineConfig, 3 | defineDocs, 4 | frontmatterSchema, 5 | metaSchema, 6 | } from 'fumadocs-mdx/config'; 7 | 8 | // You can customise Zod schemas for frontmatter and `meta.json` here 9 | // see https://fumadocs.vercel.app/docs/mdx/collections#define-docs 10 | export const docs = defineDocs({ 11 | docs: { 12 | schema: frontmatterSchema, 13 | }, 14 | meta: { 15 | schema: metaSchema, 16 | }, 17 | }); 18 | 19 | export default defineConfig({ 20 | mdxOptions: { 21 | // MDX options 22 | }, 23 | }); 24 | -------------------------------------------------------------------------------- /docs/src/app/(home)/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { ReactNode } from 'react'; 2 | import { HomeLayout } from 'fumadocs-ui/layouts/home'; 3 | import { baseOptions } from '@/app/layout.config'; 4 | 5 | export default function Layout({ children }: { children: ReactNode }) { 6 | return ( 7 | 16 | {children} 17 | 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /docs/src/app/(home)/page.tsx: -------------------------------------------------------------------------------- 1 | import { redirect } from 'next/navigation'; 2 | 3 | export default function HomePage() { 4 | redirect('/docs'); 5 | 6 | return ( 7 |
8 |

Welcome to Onlook Docs

9 |
10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /docs/src/app/api/search/route.ts: -------------------------------------------------------------------------------- 1 | import { source } from '@/lib/source'; 2 | import { createFromSource } from 'fumadocs-core/search/server'; 3 | 4 | export const { GET } = createFromSource(source); 5 | -------------------------------------------------------------------------------- /docs/src/app/docs/[[...slug]]/edit-button.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { Button } from '@onlook/ui/button'; 4 | import { ReactNode } from 'react'; 5 | 6 | interface EditButtonProps { 7 | href: string; 8 | className?: string; 9 | children: ReactNode; 10 | } 11 | 12 | export function EditButton({ href, className, children }: EditButtonProps) { 13 | return ( 14 | 24 | ); 25 | } -------------------------------------------------------------------------------- /docs/src/app/docs/[[...slug]]/edit-gh.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { Button } from '@onlook/ui/button'; 4 | import { Icons } from '@onlook/ui/icons/index'; 5 | 6 | export function EditGitHub({ filePath }: { filePath: string }) { 7 | return ( 8 | 18 | ); 19 | } -------------------------------------------------------------------------------- /docs/src/app/docs/layout.tsx: -------------------------------------------------------------------------------- 1 | import { baseOptions } from '@/app/layout.config'; 2 | import { source } from '@/lib/source'; 3 | import { DocsLayout } from 'fumadocs-ui/layouts/docs'; 4 | import type { ReactNode } from 'react'; 5 | 6 | const docsOptions = { 7 | ...baseOptions, 8 | nav: { 9 | ...baseOptions.nav, 10 | component: null 11 | } 12 | }; 13 | 14 | export default function Layout({ children }: { children: ReactNode }) { 15 | return ( 16 | 20 | {children} 21 | 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /docs/src/app/global.css: -------------------------------------------------------------------------------- 1 | @import 'tailwindcss'; 2 | @import 'fumadocs-ui/css/black.css'; 3 | @import 'fumadocs-ui/css/preset.css'; 4 | 5 | html, 6 | body { 7 | overscroll-behavior: none; 8 | } 9 | 10 | /* Improve header link button visibility and clickability */ 11 | [data-hash-link] { 12 | opacity: 0; 13 | transition: opacity 0.2s; 14 | } 15 | 16 | [data-hash-link]:hover, 17 | h1:hover [data-hash-link], 18 | h2:hover [data-hash-link], 19 | h3:hover [data-hash-link], 20 | h4:hover [data-hash-link], 21 | h5:hover [data-hash-link], 22 | h6:hover [data-hash-link] { 23 | opacity: 1; 24 | cursor: pointer; 25 | } 26 | -------------------------------------------------------------------------------- /docs/src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import '@onlook/ui/globals.css'; 2 | import './global.css'; 3 | 4 | import { RootProvider } from 'fumadocs-ui/provider'; 5 | import { type Metadata } from 'next'; 6 | import { Inter } from 'next/font/google'; 7 | import type { ReactNode } from 'react'; 8 | 9 | export const metadata: Metadata = { 10 | title: 'Onlook', 11 | description: 'Onlook Documentation', 12 | icons: [{ rel: 'icon', url: '/favicon.ico' }], 13 | }; 14 | 15 | const inter = Inter({ 16 | subsets: ['latin'], 17 | variable: '--font-inter', 18 | }); 19 | 20 | export default function Layout({ children }: { children: ReactNode }) { 21 | return ( 22 | 23 | 24 | {children} 25 | 26 | 27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /docs/src/lib/source.ts: -------------------------------------------------------------------------------- 1 | import { docs } from '@/.source'; 2 | import { loader } from 'fumadocs-core/source'; 3 | 4 | // See https://fumadocs.vercel.app/docs/headless/source-api for more info 5 | export const source = loader({ 6 | // it assigns a URL to your pages 7 | baseUrl: '/docs', 8 | source: docs.toFumadocsSource(), 9 | }); 10 | -------------------------------------------------------------------------------- /docs/src/mdx-components.tsx: -------------------------------------------------------------------------------- 1 | import defaultMdxComponents from 'fumadocs-ui/mdx'; 2 | import type { MDXComponents } from 'mdx/types'; 3 | 4 | // use this function to get MDX components, you will need it for rendering MDX 5 | export function getMDXComponents(components?: MDXComponents): MDXComponents { 6 | return { 7 | ...defaultMdxComponents, 8 | ...components, 9 | }; 10 | } 11 | -------------------------------------------------------------------------------- /docs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "target": "ESNext", 5 | "lib": [ 6 | "dom", 7 | "dom.iterable", 8 | "esnext" 9 | ], 10 | "allowJs": true, 11 | "skipLibCheck": true, 12 | "strict": true, 13 | "forceConsistentCasingInFileNames": true, 14 | "noEmit": true, 15 | "esModuleInterop": true, 16 | "module": "esnext", 17 | "moduleResolution": "bundler", 18 | "resolveJsonModule": true, 19 | "isolatedModules": true, 20 | "jsx": "preserve", 21 | "incremental": true, 22 | "paths": { 23 | "@/.source": [ 24 | "./.source/index.ts" 25 | ], 26 | "@/*": [ 27 | "./src/*" 28 | ] 29 | }, 30 | "plugins": [ 31 | { 32 | "name": "next" 33 | } 34 | ] 35 | }, 36 | "include": [ 37 | "next-env.d.ts", 38 | "**/*.ts", 39 | "**/*.tsx", 40 | ".next/types/**/*.ts" 41 | ], 42 | "exclude": [ 43 | "node_modules" 44 | ] 45 | } -------------------------------------------------------------------------------- /packages/ai/src/apply/client.ts: -------------------------------------------------------------------------------- 1 | import OpenAI from 'openai'; 2 | 3 | const createPrompt = (originalCode: string, updateSnippet: string) => `${originalCode} 4 | ${updateSnippet}`; 5 | 6 | export class FastApplyClient { 7 | private readonly client: OpenAI; 8 | 9 | constructor(apiKey: string) { 10 | this.client = new OpenAI({ 11 | apiKey, 12 | baseURL: 'https://api.morphllm.com/v1', 13 | }); 14 | } 15 | 16 | async applyCodeChange(originalCode: string, updateSnippet: string): Promise { 17 | const response = await this.client.chat.completions.create({ 18 | model: 'morph-v2', 19 | messages: [ 20 | { 21 | role: 'user', 22 | content: createPrompt(originalCode, updateSnippet), 23 | }, 24 | ], 25 | }); 26 | return response.choices[0]?.message.content || null; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/ai/src/apply/index.ts: -------------------------------------------------------------------------------- 1 | export * from './client'; 2 | -------------------------------------------------------------------------------- /packages/ai/src/chat/index.ts: -------------------------------------------------------------------------------- 1 | export * from './providers'; 2 | -------------------------------------------------------------------------------- /packages/ai/src/chat/providers.ts: -------------------------------------------------------------------------------- 1 | import { createAnthropic } from '@ai-sdk/anthropic'; 2 | import { CLAUDE_MODELS, LLMProvider } from '@onlook/models'; 3 | import { assertNever } from '@onlook/utility'; 4 | import { type LanguageModelV1 } from 'ai'; 5 | 6 | export async function initModel( 7 | provider: LLMProvider, 8 | model: CLAUDE_MODELS, 9 | ): Promise { 10 | switch (provider) { 11 | case LLMProvider.ANTHROPIC: 12 | return await getAnthropicProvider(model); 13 | default: 14 | assertNever(provider); 15 | } 16 | } 17 | 18 | async function getAnthropicProvider(model: CLAUDE_MODELS): Promise { 19 | const anthropic = createAnthropic(); 20 | return anthropic(model, { 21 | cacheControl: true, 22 | }); 23 | } 24 | -------------------------------------------------------------------------------- /packages/ai/src/coder/block.ts: -------------------------------------------------------------------------------- 1 | import { type CodeBlock } from '@onlook/models'; 2 | 3 | export class CodeBlockProcessor { 4 | /** 5 | * Extracts multiple code blocks from a string, including optional file names and languages 6 | * @param text String containing zero or more code blocks 7 | * @returns Array of code blocks with metadata 8 | */ 9 | extractCodeBlocks(text: string): CodeBlock[] { 10 | // Matches: optional filename on previous line, fence start with optional language, content, fence end 11 | const blockRegex = /(?:([^\n]+)\n)?```(\w+)?\n([\s\S]*?)```/g; 12 | const matches = text.matchAll(blockRegex); 13 | 14 | return Array.from(matches).map((match) => ({ 15 | ...(match[1] && { fileName: match[1].trim() }), 16 | ...(match[2] && { language: match[2] }), 17 | content: match[3]?.trim() ?? '', 18 | })); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/ai/src/coder/helpers.ts: -------------------------------------------------------------------------------- 1 | import { marked } from 'marked'; 2 | 3 | /** 4 | * Extracts code from markdown code blocks. If no code blocks are found, returns the original text. 5 | * @param text The markdown text containing code blocks 6 | * @returns The extracted code or original text if no code blocks found 7 | */ 8 | export function extractCodeBlocks(text: string): string { 9 | const tokens = marked.lexer(text); 10 | const codeBlocks = tokens 11 | .filter((token: any) => token.type === 'code') 12 | .map((token: any) => token.text); 13 | return codeBlocks.length ? codeBlocks.join('\n\n') : text; 14 | } 15 | -------------------------------------------------------------------------------- /packages/ai/src/coder/index.ts: -------------------------------------------------------------------------------- 1 | export * from './block'; 2 | export * from './helpers'; 3 | -------------------------------------------------------------------------------- /packages/ai/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './apply'; 2 | export * from './chat'; 3 | export * from './coder'; 4 | export * from './prompt'; 5 | export * from './tools'; 6 | -------------------------------------------------------------------------------- /packages/ai/src/prompt/base.ts: -------------------------------------------------------------------------------- 1 | import { PROJECT_ROOT_SIGNATURE } from './signatures'; 2 | 3 | const language = 'the same language they are using'; 4 | 5 | const projectRootPrefix = `The project root is: ${PROJECT_ROOT_SIGNATURE}`; 6 | 7 | const BASE_PROMPTS = { 8 | language, 9 | projectRootPrefix, 10 | }; 11 | 12 | export { BASE_PROMPTS }; 13 | -------------------------------------------------------------------------------- /packages/ai/src/prompt/create/base.ts: -------------------------------------------------------------------------------- 1 | export const CREATE_NEW_PAGE_SYSTEM_PROMPT = `IMPORTANT: 2 | - The following is the first user message meant to set up the project from a blank slate. 3 | - You will be given a prompt and optional images. You need to update a Next.js project that matches the prompt. 4 | - Try to use a distinct style and infer it from the prompt. For example, if the prompt is for something artistic, you should make this look distinct based on the intent.`; 5 | -------------------------------------------------------------------------------- /packages/ai/src/prompt/create/index.ts: -------------------------------------------------------------------------------- 1 | export * from './base'; 2 | export * from './example'; 3 | -------------------------------------------------------------------------------- /packages/ai/src/prompt/edit/index.ts: -------------------------------------------------------------------------------- 1 | export * from './edit'; 2 | export * from './example'; 3 | -------------------------------------------------------------------------------- /packages/ai/src/prompt/format.ts: -------------------------------------------------------------------------------- 1 | export const CODE_FENCE = { 2 | start: '```', 3 | end: '```', 4 | }; 5 | -------------------------------------------------------------------------------- /packages/ai/src/prompt/helpers.ts: -------------------------------------------------------------------------------- 1 | export const wrapXml = (name: string, content: string) => { 2 | return `<${name}>${content}`; 3 | }; 4 | -------------------------------------------------------------------------------- /packages/ai/src/prompt/index.ts: -------------------------------------------------------------------------------- 1 | export * from './create'; 2 | export * from './onlook'; 3 | export * from './provider'; 4 | export * from './summary'; 5 | -------------------------------------------------------------------------------- /packages/ai/src/prompt/shell.ts: -------------------------------------------------------------------------------- 1 | export const SHELL_PROMPT = `Using tools, you can suggest UNIX shell commands for users to run. Only suggest complete shell commands that are ready to execute, without placeholders. 2 | Only suggest at most a few shell commands at a time, not more than 3. 3 | Do not suggest shell commands for running the project, such as npm run dev. The project will already be running. 4 | 5 | Examples of when to suggest shell commands: 6 | - If you changed a CLI program, suggest the command to run it to see the new behavior. 7 | - If you added a test, suggest how to run it with the testing tool used by the project. 8 | - If your code changes add new dependencies, suggest the command to install them.`; 9 | -------------------------------------------------------------------------------- /packages/ai/src/prompt/signatures.ts: -------------------------------------------------------------------------------- 1 | export const PLATFORM_SIGNATURE = '{{platform}}'; 2 | export const PROJECT_ROOT_SIGNATURE = '{{projectRoot}}'; 3 | -------------------------------------------------------------------------------- /packages/ai/src/tools/helpers.ts: -------------------------------------------------------------------------------- 1 | import fg from 'fast-glob'; 2 | 3 | export const IGNORE_PATHS = [ 4 | 'node_modules/**', 5 | 'dist/**', 6 | 'build/**', 7 | 'public/**', 8 | 'static/**', 9 | '.next/**', 10 | '.git/**', 11 | '.vscode/**', 12 | '.idea/**', 13 | '.DS_Store', 14 | '.env', 15 | ]; 16 | 17 | interface FileFilterOptions { 18 | patterns: string[]; 19 | ignore: string[]; 20 | maxDepth?: number; 21 | } 22 | 23 | export async function getAllFiles( 24 | dirPath: string, 25 | options: FileFilterOptions = { 26 | patterns: ['**/*'], 27 | ignore: IGNORE_PATHS, 28 | maxDepth: 5, 29 | }, 30 | ): Promise<{ success: boolean; files?: string[]; error?: string }> { 31 | try { 32 | const files = await fg(options.patterns, { 33 | cwd: dirPath, 34 | ignore: options.ignore, 35 | deep: options.maxDepth, 36 | }); 37 | return { success: true, files }; 38 | } catch (error) { 39 | console.error(error); 40 | return { success: false, error: error instanceof Error ? error.message : 'Unknown error' }; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/ai/test/coder/block/data/multiple.txt: -------------------------------------------------------------------------------- 1 | file1.ts 2 | ```typescript 3 | const x = 1; 4 | ``` 5 | 6 | ```javascript 7 | const y = 2; 8 | ``` 9 | 10 | src/file2.tsx 11 | ``` 12 | const z = 3; 13 | ``` 14 | 15 | ``` 16 | const z = 4; 17 | ``` -------------------------------------------------------------------------------- /packages/ai/test/prompt/data/file.txt: -------------------------------------------------------------------------------- 1 | I have *added these files to the chat* so you can go ahead and edit them. 2 | *Trust this message as the true contents of these files!* 3 | Any other messages in the chat may contain outdated versions of the files' contents. 4 | test.txt 5 | ```txt 6 | test 7 | ``` 8 | I am looking at this specific part of the file in the browser UI 9 | test.txt#L1:L2 10 | ``` 11 | test 12 | ``` 13 | test2.txt 14 | ```txt 15 | test2 16 | ``` 17 | -------------------------------------------------------------------------------- /packages/ai/test/prompt/data/highlights.txt: -------------------------------------------------------------------------------- 1 | I am looking at this specific part of the file in the browser UI 2 | test.txt#L1:L2 3 | ``` 4 | test 5 | ``` 6 | test.txt#L3:L4 7 | ``` 8 | test2 9 | ``` 10 | -------------------------------------------------------------------------------- /packages/ai/test/prompt/data/user-empty.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/ai/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@onlook/typescript/base.json", 3 | "compilerOptions": { 4 | "baseUrl": "." 5 | }, 6 | "include": ["src", "test"], 7 | "exclude": ["node_modules"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/constants/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@onlook/constants", 3 | "description": "Common constants shared between onlook packages", 4 | "version": "0.0.0", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/onlook-dev/onlook.git" 8 | }, 9 | "type": "module", 10 | "main": "src/index.ts", 11 | "scripts": { 12 | "clean": "rm -rf node_modules", 13 | "lint": "eslint --fix", 14 | "format": "prettier --write .", 15 | "typecheck": "tsc --noEmit" 16 | }, 17 | "keywords": [ 18 | "onlook", 19 | "constants" 20 | ], 21 | "author": { 22 | "name": "Onlook", 23 | "email": "contact@onlook.com" 24 | }, 25 | "license": "Apache-2.0", 26 | "homepage": "https://onlook.com", 27 | "exports": { 28 | ".": "./src/index.ts", 29 | "./*": "./src/*/index.ts" 30 | }, 31 | "devDependencies": { 32 | "@onlook/typescript": "*", 33 | "typescript": "^5.5.4" 34 | }, 35 | "dependencies": {} 36 | } 37 | -------------------------------------------------------------------------------- /packages/constants/src/csb.ts: -------------------------------------------------------------------------------- 1 | export const CSB_BLANK_TEMPLATE_ID = 'c3kdf2'; 2 | export const CSB_PREVIEW_TASK_NAME = 'dev'; 3 | -------------------------------------------------------------------------------- /packages/constants/src/files.ts: -------------------------------------------------------------------------------- 1 | import { CUSTOM_OUTPUT_DIR } from './editor'; 2 | 3 | export const IGNORED_DIRECTORIES = [ 4 | 'node_modules', 5 | 'dist', 6 | 'build', 7 | 'public', 8 | 'static', 9 | '.git', 10 | '.next', 11 | CUSTOM_OUTPUT_DIR, 12 | ]; 13 | 14 | export const JSX_FILE_EXTENSIONS = ['jsx', 'tsx']; 15 | 16 | export const JS_FILE_EXTENSIONS = ['js', 'ts']; 17 | -------------------------------------------------------------------------------- /packages/constants/src/freestyle.ts: -------------------------------------------------------------------------------- 1 | export const FRESTYLE_CUSTOM_HOSTNAME = '_freestyle_custom_hostname'; 2 | export const FREESTYLE_IP_ADDRESS = '35.235.84.134'; 3 | -------------------------------------------------------------------------------- /packages/constants/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './csb'; 2 | export * from './dom'; 3 | export * from './editor'; 4 | export * from './files'; 5 | export * from './freestyle'; 6 | export * from './language'; 7 | export * from './links'; 8 | export * from './frame'; 9 | -------------------------------------------------------------------------------- /packages/constants/src/language.ts: -------------------------------------------------------------------------------- 1 | export enum Language { 2 | English = 'en', 3 | Japanese = 'ja', 4 | Chinese = 'zh', 5 | Korean = 'ko', 6 | } 7 | 8 | export const LANGUAGE_DISPLAY_NAMES: Record = { 9 | [Language.English]: 'English', 10 | [Language.Japanese]: '日本語', 11 | [Language.Chinese]: '中文', 12 | [Language.Korean]: '한국어', 13 | } as const; 14 | -------------------------------------------------------------------------------- /packages/constants/src/links.ts: -------------------------------------------------------------------------------- 1 | export enum Links { 2 | DISCORD = 'https://discord.gg/hERDfFZCsH', 3 | GITHUB = 'https://github.com/onlook-dev/onlook', 4 | USAGE_DOCS = 'https://github.com/onlook-dev/onlook/wiki/How-to-set-up-my-project%3F', 5 | WIKI = 'https://github.com/onlook-dev/onlook/wiki', 6 | OPEN_ISSUE = 'https://github.com/onlook-dev/onlook/issues/new/choose', 7 | } 8 | -------------------------------------------------------------------------------- /packages/constants/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@onlook/typescript/base.json", 3 | "compilerOptions": { 4 | "baseUrl": "." 5 | }, 6 | "include": ["src"], 7 | "exclude": ["node_modules"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/db/.env.example: -------------------------------------------------------------------------------- 1 | # Hint: Run supabase start 2 | SUPABASE_URL=http://127.0.0.1:54321 3 | SUPABASE_SERVICE_ROLE_KEY= 4 | SUPABASE_DATABASE_URL=postgresql://postgres:postgres@127.0.0.1:54322/postgres -------------------------------------------------------------------------------- /packages/db/drizzle.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'drizzle-kit'; 2 | 3 | const DEFAULT_DATABASE_URL = 'postgresql://postgres:postgres@127.0.0.1:54322/postgres'; 4 | export default defineConfig({ 5 | schema: './src/schema', 6 | out: '../../apps/backend/supabase/migrations', 7 | dialect: "postgresql", 8 | schemaFilter: ["public"], 9 | dbCredentials: { 10 | url: process.env.SUPABASE_DATABASE_URL ?? DEFAULT_DATABASE_URL, 11 | }, 12 | }); -------------------------------------------------------------------------------- /packages/db/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@onlook/db", 3 | "description": "Drizzle database schema", 4 | "version": "0.0.0", 5 | "private": true, 6 | "main": "./src/index.ts", 7 | "types": "./src/index.ts", 8 | "scripts": { 9 | "lint": "eslint .", 10 | "typecheck": "tsc --noEmit", 11 | "db:gen": "drizzle-kit generate", 12 | "db:push": "drizzle-kit push", 13 | "db:studio": "drizzle-kit studio", 14 | "db:migrate": "drizzle-kit migrate", 15 | "db:seed": "bun src/seed/seed.ts" 16 | }, 17 | "dependencies": { 18 | "dotenv": "^16.5.0", 19 | "drizzle-orm": "^0.43.1", 20 | "drizzle-zod": "^0.7.1", 21 | "pg": "^8.11.3", 22 | "postgres": "^3.4.5", 23 | "uuid": "^11.1.0" 24 | }, 25 | "devDependencies": { 26 | "drizzle-kit": "^0.31.1", 27 | "typescript": "^5.8.2" 28 | } 29 | } -------------------------------------------------------------------------------- /packages/db/src/client.ts: -------------------------------------------------------------------------------- 1 | import * as schema from '@onlook/db/src/schema'; 2 | import { drizzle } from 'drizzle-orm/postgres-js'; 3 | import postgres from 'postgres'; 4 | 5 | /** 6 | * Cache the database connection in development. This avoids creating a new connection on every HMR 7 | * update. 8 | */ 9 | const globalForDb = globalThis as unknown as { 10 | conn: postgres.Sql | undefined; 11 | }; 12 | 13 | const conn = globalForDb.conn ?? postgres(process.env.SUPABASE_DATABASE_URL!, { prepare: false }); 14 | if (process.env.NODE_ENV !== 'production') globalForDb.conn = conn; 15 | 16 | export const db = drizzle(conn, { schema }); 17 | -------------------------------------------------------------------------------- /packages/db/src/dto/canvas.ts: -------------------------------------------------------------------------------- 1 | import type { Canvas } from '@onlook/models'; 2 | import type { UserCanvas as DbUserCanvas } from '../schema'; 3 | 4 | export const toCanvas = (dbUserCanvas: DbUserCanvas): Canvas => { 5 | return { 6 | id: dbUserCanvas.canvasId, 7 | scale: Number(dbUserCanvas.scale), 8 | position: { 9 | x: Number(dbUserCanvas.x), 10 | y: Number(dbUserCanvas.y), 11 | }, 12 | }; 13 | }; 14 | 15 | export const fromCanvas = (canvas: Canvas): Omit => { 16 | return { 17 | scale: canvas.scale.toString(), 18 | x: canvas.position.x.toString(), 19 | y: canvas.position.y.toString(), 20 | canvasId: canvas.id, 21 | }; 22 | }; 23 | -------------------------------------------------------------------------------- /packages/db/src/dto/conversation.ts: -------------------------------------------------------------------------------- 1 | import { type ChatConversation } from "@onlook/models"; 2 | import type { Conversation as DbConversation } from "../schema"; 3 | 4 | export const toConversation = (dbConversation: DbConversation): ChatConversation => { 5 | return { 6 | id: dbConversation.id, 7 | displayName: dbConversation.displayName, 8 | createdAt: dbConversation.createdAt.toISOString(), 9 | updatedAt: dbConversation.updatedAt.toISOString(), 10 | projectId: dbConversation.projectId, 11 | } 12 | } 13 | 14 | export const fromConversation = (conversation: ChatConversation): DbConversation => { 15 | return { 16 | id: conversation.id, 17 | displayName: conversation.displayName, 18 | createdAt: new Date(conversation.createdAt), 19 | updatedAt: new Date(conversation.updatedAt), 20 | projectId: conversation.projectId, 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/db/src/dto/frame.ts: -------------------------------------------------------------------------------- 1 | import { FrameType, type WebFrame } from '@onlook/models'; 2 | import type { Frame as DbFrame } from '../schema'; 3 | 4 | export const toFrame = (dbFrame: DbFrame): WebFrame => { 5 | return { 6 | id: dbFrame.id, 7 | url: dbFrame.url, 8 | type: dbFrame.type as FrameType, 9 | position: { 10 | x: Number(dbFrame.x), 11 | y: Number(dbFrame.y), 12 | }, 13 | dimension: { 14 | width: Number(dbFrame.width), 15 | height: Number(dbFrame.height), 16 | }, 17 | }; 18 | }; 19 | 20 | export const fromFrame = (canvasId: string, frame: WebFrame): DbFrame => { 21 | return { 22 | id: frame.id, 23 | url: frame.url, 24 | type: frame.type as FrameType, 25 | x: frame.position.x.toString(), 26 | y: frame.position.y.toString(), 27 | canvasId: canvasId, 28 | width: frame.dimension.width.toString(), 29 | height: frame.dimension.height.toString(), 30 | }; 31 | }; 32 | -------------------------------------------------------------------------------- /packages/db/src/dto/index.ts: -------------------------------------------------------------------------------- 1 | export * from './canvas'; 2 | export * from './conversation'; 3 | export * from './frame'; 4 | export * from './message'; 5 | export * from './project'; 6 | export * from './user'; 7 | -------------------------------------------------------------------------------- /packages/db/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './dto'; 2 | export * from './schema'; 3 | export * from './seed/constants'; 4 | -------------------------------------------------------------------------------- /packages/db/src/schema/index.ts: -------------------------------------------------------------------------------- 1 | export * from './project'; 2 | export * from './supabase'; 3 | export * from './user'; 4 | -------------------------------------------------------------------------------- /packages/db/src/schema/project/canvas/canvas.ts: -------------------------------------------------------------------------------- 1 | import { relations } from 'drizzle-orm'; 2 | import { pgTable, uuid } from 'drizzle-orm/pg-core'; 3 | import { projects } from '../project'; 4 | import { frames } from './frame'; 5 | import { createUpdateSchema } from 'drizzle-zod'; 6 | import { userCanvases } from '../../user'; 7 | 8 | export const canvases = pgTable('canvas', { 9 | id: uuid('id').primaryKey().defaultRandom(), 10 | projectId: uuid('project_id') 11 | .notNull() 12 | .references(() => projects.id, { onDelete: 'cascade', onUpdate: 'cascade' }), 13 | }).enableRLS(); 14 | 15 | export const canvasUpdateSchema = createUpdateSchema(canvases); 16 | 17 | export type Canvas = typeof canvases.$inferSelect; 18 | export type NewCanvas = typeof canvases.$inferInsert; 19 | 20 | export const canvasRelations = relations(canvases, ({ one, many }) => ({ 21 | frames: many(frames), 22 | userCanvases: many(userCanvases), 23 | project: one(projects, { 24 | fields: [canvases.projectId], 25 | references: [projects.id], 26 | }), 27 | })); 28 | -------------------------------------------------------------------------------- /packages/db/src/schema/project/canvas/index.ts: -------------------------------------------------------------------------------- 1 | export * from './canvas'; 2 | export * from './frame'; 3 | 4 | -------------------------------------------------------------------------------- /packages/db/src/schema/project/chat/index.ts: -------------------------------------------------------------------------------- 1 | export * from './conversation'; 2 | export * from './message'; 3 | -------------------------------------------------------------------------------- /packages/db/src/schema/project/index.ts: -------------------------------------------------------------------------------- 1 | export * from './canvas'; 2 | export * from './chat'; 3 | export * from './invitation'; 4 | export * from './project'; 5 | -------------------------------------------------------------------------------- /packages/db/src/schema/supabase/index.ts: -------------------------------------------------------------------------------- 1 | export * from './user'; 2 | -------------------------------------------------------------------------------- /packages/db/src/schema/supabase/user.ts: -------------------------------------------------------------------------------- 1 | import { jsonb, pgSchema, text, timestamp, uuid } from 'drizzle-orm/pg-core'; 2 | 3 | const authSchema = pgSchema('auth'); 4 | 5 | export const authUsers = authSchema.table('users', { 6 | id: uuid('id').primaryKey(), 7 | email: text('email').notNull(), 8 | emailConfirmedAt: timestamp('email_confirmed_at'), 9 | rawUserMetaData: jsonb('raw_user_meta_data'), 10 | }); 11 | 12 | export type AuthUser = typeof authUsers.$inferSelect; 13 | -------------------------------------------------------------------------------- /packages/db/src/schema/user/index.ts: -------------------------------------------------------------------------------- 1 | export * from './settings'; 2 | export * from './user'; 3 | export * from './user-canvas'; 4 | export * from './user-project'; 5 | -------------------------------------------------------------------------------- /packages/db/src/schema/user/user.ts: -------------------------------------------------------------------------------- 1 | import { relations } from 'drizzle-orm'; 2 | import { pgTable, uuid } from 'drizzle-orm/pg-core'; 3 | import { createInsertSchema } from 'drizzle-zod'; 4 | import { authUsers } from '../supabase'; 5 | import { userSettings } from './settings'; 6 | import { userCanvases } from './user-canvas'; 7 | import { userProjects } from './user-project'; 8 | 9 | export const users = pgTable('users', { 10 | id: uuid('id') 11 | .primaryKey() 12 | .references(() => authUsers.id, { onDelete: 'cascade', onUpdate: 'cascade' }), 13 | }).enableRLS(); 14 | 15 | export const usersRelations = relations(users, ({ many, one }) => ({ 16 | userCanvases: many(userCanvases), 17 | userProjects: many(userProjects), 18 | userSettings: one(userSettings), 19 | authUser: one(authUsers), 20 | })); 21 | 22 | export const userInsertSchema = createInsertSchema(users); 23 | export type User = typeof users.$inferSelect; 24 | export type NewUser = typeof users.$inferInsert; 25 | -------------------------------------------------------------------------------- /packages/db/src/seed/constants.ts: -------------------------------------------------------------------------------- 1 | export const SEED_USER = { 2 | EMAIL: 'test@test.com', 3 | PASSWORD: 'test', 4 | ID: '2585ea6b-6303-4f21-977c-62af2f5a21f5', 5 | } -------------------------------------------------------------------------------- /packages/db/src/seed/seed.ts: -------------------------------------------------------------------------------- 1 | import { config } from 'dotenv'; 2 | import { resetDb, seedDb } from './db'; 3 | import { seedUser } from './supabase'; 4 | 5 | // Load .env file 6 | config({ path: '../../.env' }); 7 | 8 | (async () => { 9 | try { 10 | if (!process.env.SUPABASE_DATABASE_URL || !process.env.SUPABASE_URL || !process.env.SUPABASE_SERVICE_ROLE_KEY) { 11 | const missingVars = []; 12 | if (!process.env.SUPABASE_DATABASE_URL) missingVars.push('SUPABASE_DATABASE_URL'); 13 | if (!process.env.SUPABASE_URL) missingVars.push('SUPABASE_URL'); 14 | if (!process.env.SUPABASE_SERVICE_ROLE_KEY) missingVars.push('SUPABASE_SERVICE_ROLE_KEY'); 15 | throw new Error(`Missing environment variables: ${missingVars.join(', ')}`); 16 | } 17 | 18 | await seedUser(); 19 | await resetDb(); 20 | await seedDb(); 21 | process.exit(0); 22 | } catch (error) { 23 | console.error('Error seeding database:', error); 24 | process.exit(1); 25 | } 26 | })(); -------------------------------------------------------------------------------- /packages/db/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@onlook/typescript/base.json", 3 | "compilerOptions": { 4 | "paths": { 5 | "@/*": [ 6 | "./src/*" 7 | ] 8 | } 9 | }, 10 | "include": [ 11 | "src", 12 | "test" 13 | ], 14 | "exclude": [ 15 | "node_modules" 16 | ] 17 | } -------------------------------------------------------------------------------- /packages/email/src/client.ts: -------------------------------------------------------------------------------- 1 | import { Resend } from 'resend'; 2 | 3 | export const getResendClient = ({ apiKey }: { apiKey: string }) => { 4 | return new Resend(apiKey); 5 | }; 6 | -------------------------------------------------------------------------------- /packages/email/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './client'; 2 | export * from './invitation'; 3 | export * from './templates'; 4 | -------------------------------------------------------------------------------- /packages/email/src/invitation.ts: -------------------------------------------------------------------------------- 1 | import { render } from '@react-email/components'; 2 | import { type InviteUserEmailProps, InviteUserEmail } from './templates'; 3 | import type { SendEmailParams } from './types/send-email'; 4 | 5 | export const sendInvitationEmail = async (...params: SendEmailParams) => { 6 | const [client, { invitedByEmail, inviteLink }, { dryRun = false } = {}] = params; 7 | 8 | if (dryRun) { 9 | const rendered = await render(InviteUserEmail({ invitedByEmail, inviteLink })); 10 | console.log(rendered); 11 | return; 12 | } 13 | 14 | return await client.emails.send({ 15 | from: 'Onlook ', 16 | to: invitedByEmail, 17 | subject: 'You have been invited to Onlook', 18 | react: InviteUserEmail({ invitedByEmail, inviteLink }), 19 | }); 20 | }; 21 | -------------------------------------------------------------------------------- /packages/email/src/templates/index.ts: -------------------------------------------------------------------------------- 1 | export * from './invite-user'; 2 | -------------------------------------------------------------------------------- /packages/email/src/types/send-email.ts: -------------------------------------------------------------------------------- 1 | import type { Resend } from 'resend'; 2 | 3 | export interface SendEmailOptions { 4 | dryRun?: boolean; 5 | } 6 | 7 | export type SendEmailParams

= [client: Resend, props: P, options?: Partial]; 8 | -------------------------------------------------------------------------------- /packages/email/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@onlook/typescript/react-library.json", 3 | "compilerOptions": { 4 | "baseUrl": "." 5 | }, 6 | "include": ["src"], 7 | "exclude": ["node_modules"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/fonts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@onlook/fonts", 3 | "description": "Onlook supported fonts", 4 | "version": "0.0.0", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/onlook-dev/onlook.git" 8 | }, 9 | "type": "module", 10 | "main": "src/index.ts", 11 | "scripts": { 12 | "clean": "rm -rf node_modules", 13 | "lint": "eslint --fix", 14 | "format": "prettier --write .", 15 | "typecheck": "tsc --noEmit" 16 | }, 17 | "keywords": [ 18 | "onlook", 19 | "fonts" 20 | ], 21 | "author": { 22 | "name": "Onlook", 23 | "email": "contact@onlook.com" 24 | }, 25 | "license": "Apache-2.0", 26 | "homepage": "https://onlook.com", 27 | "exports": { 28 | ".": "./src/index.ts", 29 | "./*": "./src/*/index.ts" 30 | }, 31 | "devDependencies": { 32 | "@onlook/typescript": "*", 33 | "typescript": "^5.5.4" 34 | }, 35 | "dependencies": {} 36 | } 37 | -------------------------------------------------------------------------------- /packages/fonts/src/default.ts: -------------------------------------------------------------------------------- 1 | export const DEFAULT = { 2 | WEIGHT: '400', 3 | STYLE: 'normal', 4 | }; 5 | -------------------------------------------------------------------------------- /packages/fonts/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './default'; 2 | export * from './family'; 3 | export * from './variants'; 4 | export * from './helper'; 5 | export * from './utils'; 6 | -------------------------------------------------------------------------------- /packages/fonts/src/variants.ts: -------------------------------------------------------------------------------- 1 | export enum WEIGHT { 2 | THIN = 'Thin', 3 | EXTRA_LIGHT = 'Extra Light', 4 | LIGHT = 'Light', 5 | REGULAR = 'Regular', 6 | MEDIUM = 'Medium', 7 | SEMI_BOLD = 'Semi Bold', 8 | BOLD = 'Bold', 9 | EXTRA_BOLD = 'Extra Bold', 10 | BLACK = 'Black', 11 | } 12 | 13 | export const VARIANTS: { name: WEIGHT; value: string }[] = [ 14 | { 15 | name: WEIGHT.THIN, 16 | value: '100', 17 | }, 18 | { 19 | name: WEIGHT.EXTRA_LIGHT, 20 | value: '200', 21 | }, 22 | { 23 | name: WEIGHT.LIGHT, 24 | value: '300', 25 | }, 26 | { 27 | name: WEIGHT.REGULAR, 28 | value: '400', 29 | }, 30 | { 31 | name: WEIGHT.MEDIUM, 32 | value: '500', 33 | }, 34 | { 35 | name: WEIGHT.SEMI_BOLD, 36 | value: '600', 37 | }, 38 | { 39 | name: WEIGHT.BOLD, 40 | value: '700', 41 | }, 42 | { 43 | name: WEIGHT.EXTRA_BOLD, 44 | value: '800', 45 | }, 46 | { 47 | name: WEIGHT.BLACK, 48 | value: '900', 49 | }, 50 | ]; 51 | -------------------------------------------------------------------------------- /packages/fonts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@onlook/typescript/base.json", 3 | "compilerOptions": { 4 | "baseUrl": "." 5 | }, 6 | "include": ["src"], 7 | "exclude": ["node_modules"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/git/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@onlook/git", 3 | "description": "A git library for Onlook", 4 | "main": "./src/index.ts", 5 | "type": "module", 6 | "module": "src/index.ts", 7 | "types": "src/index.ts", 8 | "version": "0.0.0", 9 | "private": true, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/onlook-dev/onlook.git" 13 | }, 14 | "scripts": { 15 | "clean": "rm -rf node_modules", 16 | "lint": "eslint --fix .", 17 | "format": "prettier --write .", 18 | "typecheck": "tsc --noEmit" 19 | }, 20 | "keywords": [ 21 | "onlook", 22 | "git" 23 | ], 24 | "author": { 25 | "name": "Onlook", 26 | "email": "contact@onlook.com" 27 | }, 28 | "license": "Apache-2.0", 29 | "homepage": "https://onlook.com", 30 | "devDependencies": { 31 | "@onlook/typescript": "*" 32 | }, 33 | "dependencies": { 34 | "globby": "^14.1.0", 35 | "isomorphic-git": "^1.29.0" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /packages/git/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './git'; 2 | -------------------------------------------------------------------------------- /packages/git/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@onlook/typescript/base.json", 3 | "compilerOptions": { 4 | "baseUrl": "." 5 | }, 6 | "include": ["src", "test"], 7 | "exclude": ["node_modules"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/growth/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@onlook/growth", 3 | "description": "Growth helpers", 4 | "main": "./src/index.ts", 5 | "type": "module", 6 | "module": "src/index.ts", 7 | "types": "src/index.ts", 8 | "version": "0.0.0", 9 | "private": true, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/onlook-dev/onlook.git" 13 | }, 14 | "scripts": { 15 | "clean": "rm -rf node_modules", 16 | "lint": "eslint --fix .", 17 | "format": "prettier --write .", 18 | "typecheck": "tsc --noEmit" 19 | }, 20 | "keywords": [ 21 | "onlook", 22 | "growth" 23 | ], 24 | "author": { 25 | "name": "Onlook", 26 | "email": "contact@onlook.com" 27 | }, 28 | "license": "Apache-2.0", 29 | "homepage": "https://onlook.com", 30 | "devDependencies": { 31 | "@onlook/typescript": "*" 32 | }, 33 | "dependencies": { 34 | "@babel/types": "^7.27.0", 35 | "@onlook/parser": "*" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /packages/growth/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './inject'; 2 | export * from './remove'; 3 | -------------------------------------------------------------------------------- /packages/growth/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@onlook/typescript/base.json", 3 | "compilerOptions": { 4 | "baseUrl": "." 5 | }, 6 | "include": ["src", "test"], 7 | "exclude": ["node_modules"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/mastra/src/agents/index.ts: -------------------------------------------------------------------------------- 1 | import { anthropic } from '@ai-sdk/anthropic'; 2 | import { Agent } from '@mastra/core/agent'; 3 | import { weatherTool } from '../tools'; 4 | 5 | export const weatherAgent = new Agent({ 6 | name: 'Weather Agent', 7 | instructions: ` 8 | You are a helpful weather assistant that provides accurate weather information. 9 | 10 | Your primary function is to help users get weather details for specific locations. When responding: 11 | - Always ask for a location if none is provided 12 | - If the location name isn’t in English, please translate it 13 | - If giving a location with multiple parts (e.g. "New York, NY"), use the most relevant part (e.g. "New York") 14 | - Include relevant details like humidity, wind conditions, and precipitation 15 | - Keep responses concise but informative 16 | 17 | Use the weatherTool to fetch current weather data. 18 | `, 19 | model: anthropic('claude-3-5-sonnet-20241022'), 20 | tools: { weatherTool }, 21 | }); 22 | -------------------------------------------------------------------------------- /packages/mastra/src/index.ts: -------------------------------------------------------------------------------- 1 | import { Mastra } from '@mastra/core/mastra'; 2 | import { createLogger } from '@mastra/core/logger'; 3 | import { weatherWorkflow } from './workflows'; 4 | import { weatherAgent } from './agents'; 5 | 6 | export const mastra = new Mastra({ 7 | workflows: { weatherWorkflow }, 8 | agents: { weatherAgent }, 9 | logger: createLogger({ 10 | name: 'Mastra', 11 | level: 'info', 12 | }), 13 | }); 14 | -------------------------------------------------------------------------------- /packages/mastra/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@onlook/typescript/base.json", 3 | "compilerOptions": { 4 | "baseUrl": "." 5 | }, 6 | "include": ["src", "test"], 7 | "exclude": ["node_modules"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/models/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@onlook/models", 3 | "description": "Common models shared between onlook packages", 4 | "version": "0.0.0", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/onlook-dev/onlook.git" 8 | }, 9 | "type": "module", 10 | "main": "src/index.ts", 11 | "scripts": { 12 | "clean": "rm -rf node_modules", 13 | "lint": "eslint --fix", 14 | "format": "prettier --write .", 15 | "typecheck": "tsc --noEmit" 16 | }, 17 | "keywords": [ 18 | "onlook", 19 | "models" 20 | ], 21 | "author": { 22 | "name": "Onlook", 23 | "email": "contact@onlook.com" 24 | }, 25 | "license": "Apache-2.0", 26 | "homepage": "https://onlook.com", 27 | "exports": { 28 | ".": "./src/index.ts", 29 | "./*": "./src/*/index.ts" 30 | }, 31 | "devDependencies": { 32 | "@onlook/typescript": "*", 33 | "typescript": "^5.5.4", 34 | "ai": "^4.2.6" 35 | }, 36 | "dependencies": { 37 | "zod": "^3.23.8" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/models/src/actions/index.ts: -------------------------------------------------------------------------------- 1 | export * from './action.ts'; 2 | export * from './code.ts'; 3 | export * from './location.ts'; 4 | export * from './target.ts'; 5 | -------------------------------------------------------------------------------- /packages/models/src/actions/location.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | const BaseActionLocationSchema = z.object({ 4 | type: z.enum(['prepend', 'append']), 5 | targetDomId: z.string(), 6 | targetOid: z.string().nullable(), 7 | }); 8 | 9 | export const IndexActionLocationSchema = BaseActionLocationSchema.extend({ 10 | type: z.literal('index'), 11 | index: z.number(), 12 | originalIndex: z.number(), 13 | }); 14 | 15 | export const ActionLocationSchema = z.discriminatedUnion('type', [ 16 | IndexActionLocationSchema, 17 | BaseActionLocationSchema, 18 | ]); 19 | 20 | export type ActionLocation = z.infer; 21 | export type IndexActionLocation = z.infer; 22 | -------------------------------------------------------------------------------- /packages/models/src/actions/target.ts: -------------------------------------------------------------------------------- 1 | import type { StyleChange } from '../style'; 2 | 3 | export type Change = { 4 | updated: T; 5 | original: T; 6 | }; 7 | 8 | export interface ActionTarget { 9 | domId: string; 10 | oid: string | null; 11 | frameId: string; 12 | } 13 | 14 | export interface StyleActionTarget extends ActionTarget { 15 | change: Change>; 16 | } 17 | -------------------------------------------------------------------------------- /packages/models/src/auth/index.ts: -------------------------------------------------------------------------------- 1 | export enum SignInMethod { 2 | GITHUB = 'github', 3 | GOOGLE = 'google', 4 | } 5 | -------------------------------------------------------------------------------- /packages/models/src/chat/conversation/index.ts: -------------------------------------------------------------------------------- 1 | export type ChatConversation = { 2 | id: string; 3 | projectId: string; 4 | displayName: string | null; 5 | createdAt: string; 6 | updatedAt: string; 7 | }; 8 | -------------------------------------------------------------------------------- /packages/models/src/chat/index.ts: -------------------------------------------------------------------------------- 1 | export * from './conversation/'; 2 | export * from './message/'; 3 | export * from './request.ts'; 4 | export * from './response.ts'; 5 | export * from './stream.ts'; 6 | export * from './suggestion.ts'; 7 | export * from './summary.ts'; 8 | -------------------------------------------------------------------------------- /packages/models/src/chat/message/code.ts: -------------------------------------------------------------------------------- 1 | export interface CodeBlock { 2 | fileName?: string; 3 | language?: string; 4 | content: string; 5 | } 6 | 7 | export interface CodeSearchReplace { 8 | search: string; 9 | replace: string; 10 | } 11 | -------------------------------------------------------------------------------- /packages/models/src/chat/message/index.ts: -------------------------------------------------------------------------------- 1 | export * from '../response.ts'; 2 | export * from './code.ts'; 3 | export * from './context.ts'; 4 | export * from './message.ts'; 5 | -------------------------------------------------------------------------------- /packages/models/src/chat/message/message.ts: -------------------------------------------------------------------------------- 1 | import type { Message } from '@ai-sdk/react'; 2 | import type { TextPart } from 'ai'; 3 | import type { CodeDiff } from '../../code/index.ts'; 4 | import { type ChatMessageContext } from './context.ts'; 5 | 6 | export enum ChatMessageRole { 7 | USER = 'user', 8 | ASSISTANT = 'assistant', 9 | SYSTEM = 'system', 10 | } 11 | 12 | export interface UserChatMessage extends Message { 13 | role: ChatMessageRole.USER; 14 | context: ChatMessageContext[]; 15 | parts: TextPart[]; 16 | content: string; 17 | } 18 | 19 | export interface AssistantChatMessage extends Message { 20 | role: ChatMessageRole.ASSISTANT; 21 | applied: boolean; 22 | snapshots: ChatSnapshot; 23 | parts: Message['parts']; 24 | content: string; 25 | } 26 | 27 | export type ChatSnapshot = Record; 28 | 29 | export interface SystemChatMessage extends Message { 30 | role: ChatMessageRole.SYSTEM; 31 | } 32 | 33 | export type ChatMessage = UserChatMessage | AssistantChatMessage | SystemChatMessage; 34 | -------------------------------------------------------------------------------- /packages/models/src/chat/request.ts: -------------------------------------------------------------------------------- 1 | import type { CoreMessage } from 'ai'; 2 | 3 | export enum StreamRequestType { 4 | CHAT = 'chat', 5 | CREATE = 'create', 6 | ERROR_FIX = 'error-fix', 7 | SUGGESTIONS = 'suggestions', 8 | SUMMARY = 'summary', 9 | } 10 | 11 | export type StreamRequest = { 12 | messages: CoreMessage[]; 13 | systemPrompt: string; 14 | requestType: StreamRequestType; 15 | useAnalytics: boolean; 16 | }; 17 | 18 | export type StreamRequestV2 = { 19 | messages: CoreMessage[]; 20 | requestType: StreamRequestType; 21 | useAnalytics: boolean; 22 | }; 23 | -------------------------------------------------------------------------------- /packages/models/src/chat/response.ts: -------------------------------------------------------------------------------- 1 | export interface UsageCheckResult { 2 | exceeded: boolean; 3 | reason: 'none' | 'daily' | 'monthly'; 4 | daily_requests_count: number; 5 | monthly_requests_count: number; 6 | daily_requests_limit: number; 7 | monthly_requests_limit: number; 8 | } 9 | -------------------------------------------------------------------------------- /packages/models/src/chat/stream.ts: -------------------------------------------------------------------------------- 1 | import type { CoreMessage, LanguageModelUsage, TextStreamPart, ToolSet } from 'ai'; 2 | import type { UsageCheckResult } from './response'; 3 | 4 | export interface StreamResponse { 5 | type: 'partial' | 'full' | 'error' | 'rate-limited'; 6 | } 7 | 8 | export interface PartialStreamResponse extends StreamResponse { 9 | payload: TextStreamPart; 10 | type: 'partial'; 11 | } 12 | 13 | export interface FullStreamResponse extends StreamResponse { 14 | payload: CoreMessage[]; 15 | type: 'full'; 16 | usage?: LanguageModelUsage; 17 | text: string; 18 | } 19 | 20 | export interface ErrorStreamResponse extends StreamResponse { 21 | type: 'error'; 22 | message: string; 23 | } 24 | 25 | export interface RateLimitedStreamResponse extends StreamResponse { 26 | type: 'rate-limited'; 27 | rateLimitResult: UsageCheckResult; 28 | } 29 | 30 | export type CompletedStreamResponse = 31 | | FullStreamResponse 32 | | RateLimitedStreamResponse 33 | | ErrorStreamResponse; 34 | -------------------------------------------------------------------------------- /packages/models/src/chat/suggestion.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export interface ProjectSuggestions { 4 | id: string; 5 | projectId: string; 6 | suggestions: ChatSuggestion[]; 7 | } 8 | 9 | export interface ChatSuggestion { 10 | title: string; 11 | prompt: string; 12 | } 13 | 14 | export const ChatSuggestionSchema = z.object({ 15 | title: z 16 | .string() 17 | .describe( 18 | 'The display title of the suggestion. This will be shown to the user. Keep it concise but descriptive.', 19 | ), 20 | prompt: z 21 | .string() 22 | .describe( 23 | 'The prompt for the suggestion. This will be used to generate the suggestion. Make this as detailed and specific as possible.', 24 | ), 25 | }); 26 | -------------------------------------------------------------------------------- /packages/models/src/chat/summary.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const ChatSummarySchema = z.object({ 4 | filesDiscussed: z 5 | .array(z.string()) 6 | .describe('List of file paths mentioned in the conversation'), 7 | projectContext: z 8 | .string() 9 | .describe('Summary of what the user is building and their overall goals'), 10 | implementationDetails: z 11 | .string() 12 | .describe('Summary of key code decisions, patterns, and important implementation details'), 13 | userPreferences: z 14 | .string() 15 | .describe('Specific preferences the user has expressed about implementation, design, etc.'), 16 | currentStatus: z.string().describe('Current state of the project and any pending work'), 17 | }); 18 | -------------------------------------------------------------------------------- /packages/models/src/code/index.ts: -------------------------------------------------------------------------------- 1 | import { type CodeAction } from '../actions/code'; 2 | 3 | export interface CodeDiffRequest { 4 | oid: string; 5 | attributes: Record; 6 | textContent: string | null; 7 | overrideClasses: boolean | null; 8 | structureChanges: CodeAction[]; 9 | } 10 | 11 | export interface CodeDiff { 12 | original: string; 13 | generated: string; 14 | path: string; 15 | } 16 | 17 | export type FileToRequests = Map< 18 | string, 19 | { 20 | oidToRequest: Map; 21 | content: string; 22 | } 23 | >; 24 | 25 | export interface FileNode { 26 | id: string; 27 | name: string; 28 | path: string; 29 | isDirectory: boolean; 30 | children?: FileNode[]; 31 | extension?: string; 32 | } 33 | -------------------------------------------------------------------------------- /packages/models/src/create/index.ts: -------------------------------------------------------------------------------- 1 | export enum CreateStage { 2 | CLONING = 'cloning', 3 | GIT_INIT = 'git_init', 4 | INSTALLING = 'installing', 5 | COMPLETE = 'complete', 6 | ERROR = 'error', 7 | } 8 | 9 | export enum VerifyStage { 10 | CHECKING = 'checking', 11 | NOT_INSTALLED = 'not_installed', 12 | INSTALLED = 'installed', 13 | ERROR = 'error', 14 | } 15 | 16 | export enum SetupStage { 17 | INSTALLING = 'installing', 18 | CONFIGURING = 'configuring', 19 | COMPLETE = 'complete', 20 | ERROR = 'error', 21 | } 22 | 23 | export interface CreateProjectResponse { 24 | success: boolean; 25 | error?: string; 26 | response?: { 27 | projectPath: string; 28 | content: string; 29 | }; 30 | cancelled?: boolean; 31 | } 32 | 33 | export type CreateCallback = (stage: CreateStage, message: string) => void; 34 | export type VerifyCallback = (stage: VerifyStage, message: string) => void; 35 | export type SetupCallback = (stage: SetupStage, message: string) => void; 36 | -------------------------------------------------------------------------------- /packages/models/src/editor/index.ts: -------------------------------------------------------------------------------- 1 | export interface WebviewMetadata { 2 | id: string; 3 | title: string; 4 | src: string; 5 | } 6 | 7 | export enum EditorMode { 8 | DESIGN = 'design', 9 | PREVIEW = 'preview', 10 | PAN = 'pan', 11 | INSERT_TEXT = 'insert-text', 12 | INSERT_DIV = 'insert-div', 13 | INSERT_IMAGE = 'insert-image', 14 | } 15 | 16 | export enum EditorTabValue { 17 | CHAT = 'chat', 18 | DEV = 'dev', 19 | } 20 | 21 | export enum SettingsTabValue { 22 | SITE = 'site', 23 | DOMAIN = 'domain', 24 | PROJECT = 'project', 25 | PREFERENCES = 'preferences', 26 | VERSIONS = 'versions', 27 | ADVANCED = 'advanced', 28 | } 29 | 30 | export enum LeftPanelTabValue { 31 | PAGES = 'pages', 32 | LAYERS = 'layers', 33 | COMPONENTS = 'components', 34 | IMAGES = 'images', 35 | WINDOWS = 'windows', 36 | BRAND = 'brand', 37 | APPS = 'apps', 38 | } 39 | 40 | export enum BrandTabValue { 41 | COLORS = 'colors', 42 | FONTS = 'fonts', 43 | } 44 | 45 | export enum MouseAction { 46 | MOVE = 'move', 47 | MOUSE_DOWN = 'click', 48 | DOUBLE_CLICK = 'double-click', 49 | } 50 | -------------------------------------------------------------------------------- /packages/models/src/element/classes.ts: -------------------------------------------------------------------------------- 1 | interface ParsedClasses { 2 | type: 'classes'; 3 | value: string[]; 4 | } 5 | 6 | interface ClassParsingError { 7 | type: 'error'; 8 | reason: string; 9 | } 10 | 11 | export type ClassParsingResult = ParsedClasses | ClassParsingError; 12 | -------------------------------------------------------------------------------- /packages/models/src/element/element.ts: -------------------------------------------------------------------------------- 1 | interface BaseDomElement { 2 | domId: string; 3 | frameId: string; 4 | oid: string | null; 5 | instanceId: string | null; 6 | rect: DOMRect; 7 | } 8 | 9 | export interface ParentDomElement extends BaseDomElement {} 10 | 11 | export interface DomElement extends BaseDomElement { 12 | tagName: string; 13 | styles: DomElementStyles | null; 14 | parent: ParentDomElement | null; 15 | } 16 | 17 | export interface DomElementStyles { 18 | defined: Record; // Styles from stylesheets or inline 19 | computed: Record; // Browser computed styles 20 | } 21 | 22 | export interface ElementPosition { 23 | x: number; 24 | y: number; 25 | } 26 | 27 | export interface DropElementProperties { 28 | tagName: string; 29 | styles: Record; 30 | textContent: string | null; 31 | } 32 | 33 | export interface RectDimensions { 34 | width: number; 35 | height: number; 36 | top: number; 37 | left: number; 38 | } 39 | -------------------------------------------------------------------------------- /packages/models/src/element/index.ts: -------------------------------------------------------------------------------- 1 | export * from './classes'; 2 | export * from './element'; 3 | export * from './layers'; 4 | export * from './templateNode'; 5 | export * from './props'; 6 | -------------------------------------------------------------------------------- /packages/models/src/element/layers.ts: -------------------------------------------------------------------------------- 1 | export enum DynamicType { 2 | ARRAY = 'array', 3 | CONDITIONAL = 'conditional', 4 | UNKNOWN = 'unknown', 5 | } 6 | 7 | export enum CoreElementType { 8 | COMPONENT_ROOT = 'component-root', 9 | BODY_TAG = 'body-tag', 10 | } 11 | 12 | export interface LayerNode { 13 | domId: string; 14 | frameId: string; 15 | instanceId: string | null; 16 | oid: string | null; 17 | textContent: string; 18 | tagName: string; 19 | isVisible: boolean; 20 | dynamicType: DynamicType | null; 21 | coreElementType: CoreElementType | null; 22 | component: string | null; 23 | children: string[] | null; 24 | parent: string | null; 25 | } 26 | -------------------------------------------------------------------------------- /packages/models/src/element/props.ts: -------------------------------------------------------------------------------- 1 | interface ParsedProps { 2 | type: 'props'; 3 | props: NodeProps[]; 4 | } 5 | 6 | export enum PropsType { 7 | String = 'string', 8 | Number = 'number', 9 | Boolean = 'boolean', 10 | Object = 'object', 11 | Array = 'array', 12 | Code = 'code', 13 | } 14 | 15 | export interface NodeProps { 16 | key: any; 17 | value: any; 18 | type: PropsType; 19 | } 20 | 21 | interface PropsParsingError { 22 | type: 'error'; 23 | reason: string; 24 | } 25 | 26 | export type PropsParsingResult = ParsedProps | PropsParsingError; 27 | -------------------------------------------------------------------------------- /packages/models/src/element/templateNode.ts: -------------------------------------------------------------------------------- 1 | import { type CoreElementType, type DynamicType } from './layers'; 2 | 3 | export interface TemplateNode { 4 | path: string; 5 | startTag: TemplateTag; 6 | endTag: TemplateTag | null; 7 | component: string | null; 8 | dynamicType: DynamicType | null; 9 | coreElementType: CoreElementType | null; 10 | } 11 | 12 | export interface TemplateTag { 13 | start: TemplateTagPosition; 14 | end: TemplateTagPosition; 15 | } 16 | 17 | export interface TemplateTagPosition { 18 | line: number; 19 | column: number; 20 | } 21 | -------------------------------------------------------------------------------- /packages/models/src/ide/index.ts: -------------------------------------------------------------------------------- 1 | export enum IdeType { 2 | VS_CODE = 'VSCode', 3 | CURSOR = 'Cursor', 4 | ZED = 'Zed', 5 | WINDSURF = 'Windsurf', 6 | ONLOOK = 'Onlook', 7 | } 8 | 9 | export const DEFAULT_IDE = IdeType.ONLOOK; 10 | -------------------------------------------------------------------------------- /packages/models/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './actions/'; 2 | export * from './assets/'; 3 | export * from './auth/'; 4 | export * from './chat/'; 5 | export * from './code/'; 6 | export * from './create/'; 7 | export * from './editor/'; 8 | export * from './element/'; 9 | export * from './ide/'; 10 | export * from './llm/'; 11 | export * from './pages/'; 12 | export * from './project/'; 13 | export * from './run/'; 14 | export * from './style/'; 15 | export * from './user/'; 16 | -------------------------------------------------------------------------------- /packages/models/src/llm/index.ts: -------------------------------------------------------------------------------- 1 | export enum LLMProvider { 2 | ANTHROPIC = 'anthropic', 3 | } 4 | 5 | export enum CLAUDE_MODELS { 6 | SONNET_4 = 'claude-sonnet-4-20250514', 7 | SONNET_3_7 = 'claude-3-7-sonnet-20250219', 8 | HAIKU = 'claude-3-5-haiku-20241022', 9 | } 10 | -------------------------------------------------------------------------------- /packages/models/src/project/canvas.ts: -------------------------------------------------------------------------------- 1 | import type { RectPosition } from './rect'; 2 | 3 | export interface Canvas { 4 | id: string; 5 | scale: number; 6 | position: RectPosition; 7 | } 8 | -------------------------------------------------------------------------------- /packages/models/src/project/command.ts: -------------------------------------------------------------------------------- 1 | export interface Commands { 2 | build?: string; 3 | run?: string; 4 | install?: string; 5 | } 6 | -------------------------------------------------------------------------------- /packages/models/src/project/domain.ts: -------------------------------------------------------------------------------- 1 | export enum DomainType { 2 | BASE = 'base', 3 | CUSTOM = 'custom', 4 | } 5 | 6 | export interface ProjectDomain { 7 | base: DomainSettings | null; 8 | custom: DomainSettings | null; 9 | } 10 | 11 | export interface DomainSettings { 12 | url: string; 13 | type: DomainType; 14 | publishedAt?: string; 15 | } 16 | -------------------------------------------------------------------------------- /packages/models/src/project/frame.ts: -------------------------------------------------------------------------------- 1 | import { Orientation, Theme } from '@onlook/constants'; 2 | import type { RectDimension, RectPosition } from './rect'; 3 | 4 | export enum FrameType { 5 | WEB = 'web', 6 | } 7 | 8 | export interface Frame { 9 | id: string; 10 | position: RectPosition; 11 | type: FrameType; 12 | dimension: RectDimension; 13 | } 14 | 15 | export interface WebFrame extends Frame { 16 | url: string; 17 | type: FrameType.WEB; 18 | } 19 | 20 | export interface WindowMetadata { 21 | orientation: Orientation; 22 | aspectRatioLocked: boolean; 23 | device: string; 24 | theme: Theme; 25 | width: number; 26 | height: number; 27 | } 28 | -------------------------------------------------------------------------------- /packages/models/src/project/index.ts: -------------------------------------------------------------------------------- 1 | export * from './canvas'; 2 | export * from './command'; 3 | export * from './domain'; 4 | export * from './frame'; 5 | export * from './invitation'; 6 | export * from './project'; 7 | export * from './rect'; 8 | export * from './role'; 9 | -------------------------------------------------------------------------------- /packages/models/src/project/invitation.ts: -------------------------------------------------------------------------------- 1 | import type { ProjectRole } from './role'; 2 | 3 | export interface Invitation { 4 | id: string; 5 | inviteeEmail: string; 6 | role: ProjectRole; 7 | expiresAt: Date; 8 | } 9 | -------------------------------------------------------------------------------- /packages/models/src/project/project.ts: -------------------------------------------------------------------------------- 1 | export interface Project { 2 | id: string; 3 | name: string; 4 | metadata: { 5 | createdAt: string; 6 | updatedAt: string; 7 | previewImg: string | null; 8 | description: string | null; 9 | }; 10 | sandbox: { 11 | id: string; 12 | url: string; 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /packages/models/src/project/rect.ts: -------------------------------------------------------------------------------- 1 | export interface RectPosition { 2 | x: number; 3 | y: number; 4 | } 5 | 6 | export interface RectDimension { 7 | width: number; 8 | height: number; 9 | } 10 | -------------------------------------------------------------------------------- /packages/models/src/project/role.ts: -------------------------------------------------------------------------------- 1 | export enum ProjectRole { 2 | OWNER = 'owner', 3 | ADMIN = 'admin', 4 | } 5 | -------------------------------------------------------------------------------- /packages/models/src/run/index.ts: -------------------------------------------------------------------------------- 1 | export enum RunState { 2 | STOPPED = 'stopped', 3 | SETTING_UP = 'setting-up', 4 | RUNNING = 'running', 5 | STOPPING = 'stopping', 6 | ERROR = 'error', 7 | } 8 | -------------------------------------------------------------------------------- /packages/models/src/style/index.ts: -------------------------------------------------------------------------------- 1 | export interface StyleChange { 2 | value: string; 3 | type: StyleChangeType; 4 | } 5 | 6 | export enum StyleChangeType { 7 | Value = 'value', 8 | Custom = 'custom', 9 | Remove = 'remove', 10 | } 11 | 12 | export interface TailwindColor { 13 | name: string; 14 | originalKey: string; 15 | lightColor: string; 16 | darkColor?: string; 17 | line?: { 18 | config?: number; 19 | css?: { 20 | lightMode?: number; 21 | darkMode?: number; 22 | }; 23 | }; 24 | override?: boolean; 25 | } 26 | -------------------------------------------------------------------------------- /packages/models/src/usage/index.ts: -------------------------------------------------------------------------------- 1 | export enum UsagePlanType { 2 | BASIC = 'basic', 3 | PRO = 'pro', 4 | } 5 | 6 | export interface UserUsage { 7 | id: number; 8 | user_id: string; 9 | stripe_customer_id?: string; 10 | stripe_subscription_id?: string; 11 | daily_requests_count: number; 12 | monthly_requests_count: number; 13 | plan_id: number; 14 | created_at: string; 15 | updated_at: string; 16 | cancelled: boolean; 17 | } 18 | 19 | export interface UsagePlan { 20 | id: number; 21 | name: UsagePlanType; 22 | daily_requests_limit: number; 23 | monthly_requests_limit: number; 24 | stripe_price_id: string; 25 | stripe_product_id: string; 26 | is_free: boolean; 27 | } 28 | -------------------------------------------------------------------------------- /packages/models/src/user/index.ts: -------------------------------------------------------------------------------- 1 | export * from './settings'; 2 | export * from './user'; 3 | -------------------------------------------------------------------------------- /packages/models/src/user/settings.ts: -------------------------------------------------------------------------------- 1 | export interface UserSettings { 2 | id: string; 3 | chat: ChatSettings; 4 | } 5 | 6 | export interface ChatSettings { 7 | showSuggestions: boolean; 8 | autoApplyCode: boolean; 9 | expandCodeBlocks: boolean; 10 | showMiniChat: boolean; 11 | } 12 | -------------------------------------------------------------------------------- /packages/models/src/user/user.ts: -------------------------------------------------------------------------------- 1 | export interface UserMetadata { 2 | id: string; 3 | name?: string; 4 | avatarUrl?: string | null; 5 | email?: string; 6 | } 7 | -------------------------------------------------------------------------------- /packages/models/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@onlook/typescript/base.json", 3 | "compilerOptions": { 4 | "baseUrl": "." 5 | }, 6 | "include": ["src"], 7 | "exclude": ["node_modules"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/parser/src/code-edit/image.ts: -------------------------------------------------------------------------------- 1 | import { DefaultSettings } from '@onlook/constants'; 2 | import { type CodeInsertImage, type CodeRemoveImage } from '@onlook/models/actions'; 3 | import { type NodePath, type t as T } from '../packages'; 4 | import { addClassToNode } from './style'; 5 | 6 | export function insertImageToNode(path: NodePath, action: CodeInsertImage): void { 7 | const imageName = writeImageToFile(action); 8 | if (!imageName) { 9 | console.error('Failed to write image to file'); 10 | return; 11 | } 12 | const prefix = DefaultSettings.IMAGE_FOLDER.replace(/^public\//, ''); 13 | const backgroundClass = `bg-[url(/${prefix}/${imageName})]`; 14 | addClassToNode(path.node, backgroundClass); 15 | } 16 | 17 | function writeImageToFile(action: CodeInsertImage): string | null { 18 | // TODO: Implement 19 | return null; 20 | } 21 | 22 | export function removeImageFromNode(path: NodePath, action: CodeRemoveImage): void {} 23 | -------------------------------------------------------------------------------- /packages/parser/src/code-edit/index.ts: -------------------------------------------------------------------------------- 1 | export * from './group'; 2 | export * from './image'; 3 | export * from './insert'; 4 | export * from './move'; 5 | export * from './remove'; 6 | export * from './style'; 7 | export * from './text'; 8 | export * from './transform'; 9 | -------------------------------------------------------------------------------- /packages/parser/src/code-edit/text.ts: -------------------------------------------------------------------------------- 1 | import { type t as T, types as t } from '../packages'; 2 | 3 | export function updateNodeTextContent(node: T.JSXElement, textContent: string): void { 4 | const textNode = node.children.find((child) => t.isJSXText(child)) as T.JSXText | undefined; 5 | if (textNode) { 6 | textNode.value = textContent; 7 | } else { 8 | node.children.unshift(t.jsxText(textContent)); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/parser/src/helpers.ts: -------------------------------------------------------------------------------- 1 | import { type t as T, types as t } from './packages'; 2 | 3 | export function isReactFragment(openingElement: T.JSXOpeningElement): boolean { 4 | const name = openingElement.name; 5 | 6 | if (t.isJSXIdentifier(name)) { 7 | return name.name === 'Fragment'; 8 | } 9 | 10 | if (t.isJSXMemberExpression(name)) { 11 | return ( 12 | t.isJSXIdentifier(name.object) && 13 | name.object.name === 'React' && 14 | t.isJSXIdentifier(name.property) && 15 | name.property.name === 'Fragment' 16 | ); 17 | } 18 | 19 | return false; 20 | } 21 | 22 | export function isColorsObjectProperty(path: any): boolean { 23 | return ( 24 | path.parent.type === 'ObjectExpression' && 25 | path.node.key.type === 'Identifier' && 26 | path.node.key.name === 'colors' && 27 | path.node.value.type === 'ObjectExpression' 28 | ); 29 | } 30 | 31 | export function isObjectExpression(node: any): node is T.ObjectExpression { 32 | return node.type === 'ObjectExpression'; 33 | } 34 | -------------------------------------------------------------------------------- /packages/parser/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './code-edit'; 2 | export * from './helpers'; 3 | export * from './ids'; 4 | export * from './packages'; 5 | export * from './parse'; 6 | export * from './template-node'; 7 | -------------------------------------------------------------------------------- /packages/parser/src/packages.ts: -------------------------------------------------------------------------------- 1 | import { packages } from '@babel/standalone'; 2 | 3 | import type * as t from '@babel/types'; 4 | import type { NodePath } from '@babel/traverse'; 5 | import type { GeneratorOptions } from '@babel/generator'; 6 | 7 | export const { parse } = packages.parser; 8 | export const { generate } = packages.generator; 9 | export const traverse = packages.traverse.default; 10 | export const types = packages.types; 11 | 12 | export type { t, NodePath, GeneratorOptions }; 13 | -------------------------------------------------------------------------------- /packages/parser/src/template-node/index.ts: -------------------------------------------------------------------------------- 1 | export * from './helpers'; 2 | export * from './map'; 3 | -------------------------------------------------------------------------------- /packages/parser/test/parse.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, test } from 'bun:test'; 2 | import { getAstFromContent, getContentFromAst } from 'src'; 3 | 4 | describe('Parse Tests', () => { 5 | test('should parse and serialize a simple component', async () => { 6 | const code = `export default function App() {\n return (\n

Hello, world!
);\n\n}`; 7 | const ast = getAstFromContent(code); 8 | const serialized = await getContentFromAst(ast); 9 | expect(serialized).toEqual(code); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /packages/parser/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@onlook/typescript/base.json", 3 | "compilerOptions": { 4 | "baseUrl": "." 5 | }, 6 | "include": ["src", "test"], 7 | "exclude": ["node_modules"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/penpal/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@onlook/penpal", 3 | "description": "A utility library to facilitates rpc-style calls with penpal between preload and iframe", 4 | "main": "./src/index.ts", 5 | "type": "module", 6 | "module": "src/index.ts", 7 | "types": "src/index.ts", 8 | "version": "0.0.0", 9 | "private": true, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/onlook-dev/onlook.git" 13 | }, 14 | "scripts": { 15 | "clean": "rm -rf node_modules", 16 | "lint": "eslint --fix .", 17 | "format": "prettier --write .", 18 | "typecheck": "tsc --noEmit" 19 | }, 20 | "keywords": [ 21 | "onlook", 22 | "penpal", 23 | "preload", 24 | "iframe" 25 | ], 26 | "author": { 27 | "name": "Onlook", 28 | "email": "contact@onlook.com" 29 | }, 30 | "license": "Apache-2.0", 31 | "homepage": "https://onlook.com", 32 | "devDependencies": { 33 | "@onlook/typescript": "*" 34 | }, 35 | "dependencies": {} 36 | } 37 | -------------------------------------------------------------------------------- /packages/penpal/src/child.ts: -------------------------------------------------------------------------------- 1 | import type { PenpalChildMethods as PenpalChildMethodsType } from '@onlook/web-preload/script/api'; 2 | 3 | // Preload methods should be treated as promises 4 | export type PromisifiedPendpalChildMethods = { 5 | [K in keyof PenpalChildMethods]: ( 6 | ...args: Parameters 7 | ) => Promise>; 8 | }; 9 | 10 | export type PenpalChildMethods = PenpalChildMethodsType; 11 | 12 | export const PENPAL_CHILD_CHANNEL = 'PENPAL_CHILD'; 13 | -------------------------------------------------------------------------------- /packages/penpal/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './child'; 2 | export * from './parent'; 3 | export * from './utils'; 4 | -------------------------------------------------------------------------------- /packages/penpal/src/parent.ts: -------------------------------------------------------------------------------- 1 | export type PenpalParentMethods = { 2 | getFrameId: () => string; 3 | }; 4 | 5 | // Parent methods should be treated as promises 6 | export type PromisifiedPenpalParentMethods = { 7 | [K in keyof PenpalParentMethods]: ( 8 | ...args: Parameters 9 | ) => Promise>; 10 | }; 11 | 12 | export const PENPAL_PARENT_CHANNEL = 'PENPAL_PARENT'; 13 | -------------------------------------------------------------------------------- /packages/penpal/src/utils.ts: -------------------------------------------------------------------------------- 1 | export const promisifyMethod = any>( 2 | method: T | undefined, 3 | ): ((...args: Parameters) => Promise>) => { 4 | return async (...args: Parameters) => { 5 | if (!method) throw new Error('Method not initialized'); 6 | return method(...args); 7 | }; 8 | }; 9 | -------------------------------------------------------------------------------- /packages/penpal/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@onlook/typescript/base.json", 3 | "compilerOptions": { 4 | "baseUrl": "." 5 | }, 6 | "include": ["src", "test"], 7 | "exclude": ["node_modules"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/rpc/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@onlook/rpc", 3 | "description": "An rpc library for Onlook web", 4 | "main": "./src/index.ts", 5 | "type": "module", 6 | "module": "src/index.ts", 7 | "types": "src/index.ts", 8 | "version": "0.0.0", 9 | "private": true, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/onlook-dev/onlook.git" 13 | }, 14 | "scripts": { 15 | "clean": "rm -rf node_modules", 16 | "lint": "eslint --fix .", 17 | "format": "prettier --write .", 18 | "typecheck": "tsc --noEmit" 19 | }, 20 | "keywords": [ 21 | "onlook", 22 | "rpc" 23 | ], 24 | "author": { 25 | "name": "Onlook", 26 | "email": "contact@onlook.com" 27 | }, 28 | "license": "Apache-2.0", 29 | "homepage": "https://onlook.com", 30 | "devDependencies": { 31 | "@onlook/typescript": "*" 32 | }, 33 | "dependencies": { 34 | "@onlook/web-server": "*" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /packages/rpc/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './trpc'; 2 | -------------------------------------------------------------------------------- /packages/rpc/src/trpc/config.ts: -------------------------------------------------------------------------------- 1 | export interface EditorServerOptions { 2 | dev?: boolean; 3 | port?: number; 4 | prefix?: string; 5 | } 6 | 7 | export const editorServerConfig: EditorServerOptions = { 8 | dev: true, 9 | port: 8080, 10 | prefix: '/trpc', 11 | }; 12 | -------------------------------------------------------------------------------- /packages/rpc/src/trpc/index.ts: -------------------------------------------------------------------------------- 1 | export * from './config'; 2 | export * from './types'; 3 | -------------------------------------------------------------------------------- /packages/rpc/src/trpc/types.ts: -------------------------------------------------------------------------------- 1 | import type { AppRouter } from '@onlook/web-server/src/router'; 2 | 3 | export type EditorRouter = AppRouter; 4 | -------------------------------------------------------------------------------- /packages/rpc/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@onlook/typescript/base.json", 3 | "compilerOptions": { 4 | "baseUrl": "." 5 | }, 6 | "include": ["src", "test"], 7 | "exclude": ["node_modules"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/scripts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@onlook/scripts", 3 | "version": "0.0.1", 4 | "private": true, 5 | "type": "module", 6 | "scripts": { 7 | "build": "bun build src/index.ts --outdir=dist --target=node", 8 | "dev": "tsc --watch", 9 | "start": "bun run build && bun dist/index.js", 10 | "typecheck": "tsc --noEmit", 11 | "lint": "eslint . --ext .ts", 12 | "format": "prettier --write ." 13 | }, 14 | "bin": { 15 | "env": "dist/index.js" 16 | }, 17 | "dependencies": { 18 | "chalk": "^4.1.2", 19 | "commander": "^4.1.1", 20 | "prompts": "^2.4.2", 21 | "ora": "^5.4.1" 22 | }, 23 | "devDependencies": { 24 | "@onlook/typescript": "*", 25 | "@types/node": "^20.10.5", 26 | "@types/prompts": "^2.4.9", 27 | "typescript": "^5.3.3" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/scripts/src/helpers.ts: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs'; 2 | import ora from 'ora'; 3 | 4 | export const writeEnvFile = (filePath: string, content: string, label: string) => { 5 | const spinner = ora(`Writing ${label} .env to ${filePath}`).start(); 6 | try { 7 | fs.appendFileSync(filePath, content); 8 | spinner.succeed(`${label} .env written to ${filePath}`); 9 | } catch (err) { 10 | spinner.fail(`Failed writing ${label} .env`); 11 | throw err; 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /packages/scripts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@onlook/typescript/base.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "rootDir": "src", 6 | "module": "ESNext", 7 | "moduleResolution": "node", 8 | "target": "ES2020", 9 | "allowSyntheticDefaultImports": true, 10 | "esModuleInterop": true 11 | }, 12 | "include": ["src/**/*"] 13 | } 14 | -------------------------------------------------------------------------------- /packages/types/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@onlook/types", 3 | "description": "Common types shared between onlook packages", 4 | "version": "0.0.0", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/onlook-dev/onlook.git" 8 | }, 9 | "type": "module", 10 | "main": "src/index.ts", 11 | "scripts": { 12 | "clean": "rm -rf node_modules", 13 | "lint": "eslint --fix .", 14 | "format": "prettier --write .", 15 | "typecheck": "tsc --noEmit" 16 | }, 17 | "keywords": [ 18 | "onlook", 19 | "types" 20 | ], 21 | "author": { 22 | "name": "Onlook", 23 | "email": "contact@onlook.com" 24 | }, 25 | "license": "Apache-2.0", 26 | "homepage": "https://onlook.com", 27 | "exports": { 28 | "./*": "./src/*/index.ts" 29 | }, 30 | "devDependencies": { 31 | "@onlook/typescript": "*", 32 | "typescript": "^5.5.4" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /packages/types/src/adapters/index.ts: -------------------------------------------------------------------------------- 1 | import * as PropType from './props'; 2 | 3 | export { PropType }; 4 | 5 | export interface Prop { 6 | name: string; 7 | type: PropType.StringType | PropType.BooleanType | PropType.EnumType; 8 | } 9 | 10 | export interface ComponentDescriptor { 11 | framework: 'react'; // TODO: support other frameworks 12 | name: string; // export name; e.g. "Button" ("default" for default export) 13 | sourceFilePath: string; // relative path to component e.g. "src/Button.tsx" 14 | props: Prop[]; 15 | createRenderer: (element: HTMLElement) => ComponentRenderer; 16 | } 17 | 18 | export interface ComponentRenderer { 19 | render(props: Record): Promise; 20 | dispose(): void; 21 | } 22 | -------------------------------------------------------------------------------- /packages/types/src/design-tokens/index.ts: -------------------------------------------------------------------------------- 1 | // W3C design tokens https://design-tokens.github.io/community-group/format 2 | 3 | export interface ColorToken { 4 | $value: string; 5 | $type: 'color'; 6 | } 7 | 8 | export type DesignToken = ColorToken; 9 | 10 | export interface DesignTokens { 11 | [key: string]: DesignToken | DesignTokens; 12 | } 13 | -------------------------------------------------------------------------------- /packages/types/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './adapters'; 2 | export * from './design-tokens'; 3 | -------------------------------------------------------------------------------- /packages/types/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@onlook/typescript/base.json", 3 | "compilerOptions": { 4 | "baseUrl": "." 5 | }, 6 | "include": ["src"], 7 | "exclude": ["node_modules"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/ui/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlook-dev/onlook/430860fdd6ebd403e21eb2bc340925edef3ed158/packages/ui/README.md -------------------------------------------------------------------------------- /packages/ui/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 4 | "rsc": false, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.ts", 8 | "css": "src/globals.css", 9 | "baseColor": "stone", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "hooks": "@/hooks", 16 | "lib": "@/lib", 17 | "ui": "@/components", 18 | "utils": "@/utils" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/ui/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | '@tailwindcss/postcss': {}, 4 | }, 5 | }; 6 | -------------------------------------------------------------------------------- /packages/ui/src/components/aspect-ratio.tsx: -------------------------------------------------------------------------------- 1 | import * as AspectRatioPrimitive from '@radix-ui/react-aspect-ratio'; 2 | 3 | function AspectRatio({ ...props }: React.ComponentProps) { 4 | return ; 5 | } 6 | 7 | export { AspectRatio }; 8 | -------------------------------------------------------------------------------- /packages/ui/src/components/collapsible.tsx: -------------------------------------------------------------------------------- 1 | import * as CollapsiblePrimitive from '@radix-ui/react-collapsible'; 2 | 3 | function Collapsible({ ...props }: React.ComponentProps) { 4 | return ; 5 | } 6 | 7 | function CollapsibleTrigger({ 8 | ...props 9 | }: React.ComponentProps) { 10 | return ; 11 | } 12 | 13 | function CollapsibleContent({ 14 | ...props 15 | }: React.ComponentProps) { 16 | return ; 17 | } 18 | 19 | export { Collapsible, CollapsibleTrigger, CollapsibleContent }; 20 | -------------------------------------------------------------------------------- /packages/ui/src/components/color-picker/checkPattern.ts: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/react'; 2 | 3 | export function checkPattern( 4 | color0: string, 5 | color1: string, 6 | size: string, 7 | offsetX = '0px', 8 | offsetY = '0px', 9 | ) { 10 | return css` 11 | background-color: ${color0}; 12 | background-image: 13 | linear-gradient( 14 | 45deg, 15 | ${color1} 25%, 16 | transparent 25%, 17 | transparent 75%, 18 | ${color1} 75%, 19 | ${color1} 20 | ), 21 | linear-gradient( 22 | 45deg, 23 | ${color1} 25%, 24 | transparent 25%, 25 | transparent 75%, 26 | ${color1} 75%, 27 | ${color1} 28 | ); 29 | background-position: 30 | ${offsetX} ${offsetY}, 31 | calc(${size} / 2 + ${offsetX}) calc(${size} / 2 + ${offsetY}); 32 | background-size: ${size} ${size}; 33 | `; 34 | } 35 | -------------------------------------------------------------------------------- /packages/ui/src/components/color-picker/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './checkPattern'; 2 | export * from './ColorPicker'; 3 | export * from './ColorSlider'; 4 | export * from './SVPicker'; 5 | -------------------------------------------------------------------------------- /packages/ui/src/components/hotkey-label.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from '../utils'; 2 | import { Kbd } from './kbd'; 3 | 4 | export type Hotkey = { 5 | command: string; 6 | description: string; 7 | readableCommand: string; 8 | }; 9 | 10 | export function HotkeyLabel({ hotkey, className }: { hotkey: Hotkey; className?: string }) { 11 | return ( 12 | 13 | {hotkey.description} 14 | 15 | 16 | 20 | 21 | 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /packages/ui/src/components/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './'; 2 | -------------------------------------------------------------------------------- /packages/ui/src/components/input.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | import { cn } from '../utils'; 4 | 5 | function Input({ className, type, ...props }: React.ComponentProps<'input'>) { 6 | return ( 7 | 18 | ); 19 | } 20 | 21 | export { Input }; 22 | -------------------------------------------------------------------------------- /packages/ui/src/components/kbd.tsx: -------------------------------------------------------------------------------- 1 | import type React from 'react'; 2 | import { cn } from '../utils'; 3 | 4 | export function Kbd({ children, className }: { children: React.ReactNode; className?: string }) { 5 | return ( 6 | 12 | {children} 13 | 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /packages/ui/src/components/label.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import * as LabelPrimitive from '@radix-ui/react-label'; 4 | import * as React from 'react'; 5 | 6 | import { cn } from '../utils'; 7 | 8 | function Label({ className, ...props }: React.ComponentProps) { 9 | return ( 10 | 18 | ); 19 | } 20 | 21 | export { Label }; 22 | -------------------------------------------------------------------------------- /packages/ui/src/components/progress.tsx: -------------------------------------------------------------------------------- 1 | import * as ProgressPrimitive from '@radix-ui/react-progress'; 2 | import * as React from 'react'; 3 | 4 | import { cn } from '../utils'; 5 | 6 | function Progress({ 7 | className, 8 | value, 9 | ...props 10 | }: React.ComponentProps) { 11 | return ( 12 | 20 | 25 | 26 | ); 27 | } 28 | 29 | export { Progress }; 30 | -------------------------------------------------------------------------------- /packages/ui/src/components/separator.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import * as SeparatorPrimitive from '@radix-ui/react-separator'; 4 | import * as React from 'react'; 5 | 6 | import { cn } from '../utils'; 7 | 8 | function Separator({ 9 | className, 10 | orientation = 'horizontal', 11 | decorative = true, 12 | ...props 13 | }: React.ComponentProps) { 14 | return ( 15 | 25 | ); 26 | } 27 | 28 | export { Separator }; 29 | -------------------------------------------------------------------------------- /packages/ui/src/components/skeleton.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from '../utils'; 2 | 3 | function Skeleton({ className, ...props }: React.ComponentProps<'div'>) { 4 | return ( 5 |
10 | ); 11 | } 12 | 13 | export { Skeleton }; 14 | -------------------------------------------------------------------------------- /packages/ui/src/components/sonner.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { useTheme } from 'next-themes'; 4 | import { Toaster as Sonner, type ToasterProps, toast } from 'sonner'; 5 | 6 | const Toaster = ({ ...props }: ToasterProps) => { 7 | const { theme = 'system' } = useTheme(); 8 | 9 | return ( 10 | 22 | ); 23 | }; 24 | 25 | export { Toaster, toast }; 26 | -------------------------------------------------------------------------------- /packages/ui/src/components/textarea.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | import { cn } from '../utils'; 4 | 5 | function Textarea({ className, ...props }: React.ComponentProps<'textarea'>) { 6 | return ( 7 |