diff --git a/apps/studio/electron/main/events/hosting.ts b/apps/studio/electron/main/events/hosting.ts index 90c321642..d00ac63a3 100644 --- a/apps/studio/electron/main/events/hosting.ts +++ b/apps/studio/electron/main/events/hosting.ts @@ -8,8 +8,11 @@ export function listenForHostingMessages() { return await hostingManager.deploy(folderPath, buildScript, url); }); - ipcMain.handle(MainChannels.UNPUBLISH_HOSTING_ENV, (e: Electron.IpcMainInvokeEvent, args) => { - const { url } = args; - return hostingManager.unpublish(url); - }); + ipcMain.handle( + MainChannels.UNPUBLISH_HOSTING_ENV, + async (e: Electron.IpcMainInvokeEvent, args) => { + const { url } = args; + return await hostingManager.unpublish(url); + }, + ); } diff --git a/apps/studio/electron/main/hosting/helpers.ts b/apps/studio/electron/main/hosting/helpers.ts index 726f9e5aa..6b9225703 100644 --- a/apps/studio/electron/main/hosting/helpers.ts +++ b/apps/studio/electron/main/hosting/helpers.ts @@ -1,4 +1,4 @@ -import { addStandaloneConfig } from '@onlook/foundation'; +import { addNextBuildConfig } from '@onlook/foundation'; import { copyFileSync, existsSync, mkdirSync, readdirSync, readFileSync, statSync } from 'fs'; import { isBinary } from 'istextorbinary'; import { exec } from 'node:child_process'; @@ -46,10 +46,31 @@ export function serializeFiles(currentDir: string, basePath: string = ''): FileR return files; } -export async function prepareNextProject(projectDir: string) { - const res = await addStandaloneConfig(projectDir); +export async function preprocessNextBuild(projectDir: string): Promise<{ + success: boolean; + error?: string; +}> { + const res = await addNextBuildConfig(projectDir); if (!res) { - return false; + return { + success: false, + error: 'Failed to add standalone config to Next.js project. Make sure project is Next.js and next.config.{js|ts|mjs|cjs} is present', + }; + } + + return { success: true }; +} + +export async function postprocessNextBuild(projectDir: string): Promise<{ + success: boolean; + error?: string; +}> { + const entrypointExists = await checkEntrypointExists(projectDir); + if (!entrypointExists) { + return { + success: false, + error: 'Failed to find entrypoint server.js in .next/standalone', + }; } copyDir(projectDir + '/public', projectDir + '/.next/standalone/public'); @@ -58,11 +79,18 @@ export async function prepareNextProject(projectDir: string) { for (const lockFile of SUPPORTED_LOCK_FILES) { if (existsSync(projectDir + '/' + lockFile)) { copyFileSync(projectDir + '/' + lockFile, projectDir + '/.next/standalone/' + lockFile); - return true; + return { success: true }; } } - return false; + return { + success: false, + error: 'Failed to find lock file. Supported lock files: ' + SUPPORTED_LOCK_FILES.join(', '), + }; +} + +async function checkEntrypointExists(projectDir: string) { + return existsSync(join(projectDir, '/.next/standalone/server.js')); } export function copyDir(src: string, dest: string) { diff --git a/apps/studio/electron/main/hosting/index.ts b/apps/studio/electron/main/hosting/index.ts index 83379dac5..33d7a8cad 100644 --- a/apps/studio/electron/main/hosting/index.ts +++ b/apps/studio/electron/main/hosting/index.ts @@ -3,17 +3,19 @@ import { HostingStatus } from '@onlook/models/hosting'; import { FreestyleSandboxes, type FreestyleDeployWebSuccessResponse } from 'freestyle-sandboxes'; import { mainWindow } from '..'; import analytics from '../analytics'; -import { PersistentStorage } from '../storage'; -import { prepareNextProject, runBuildScript, serializeFiles } from './helpers'; +import { + postprocessNextBuild, + preprocessNextBuild, + runBuildScript, + serializeFiles, +} from './helpers'; import { LogTimer } from '/common/helpers/timer'; class HostingManager { private static instance: HostingManager; private freestyle: FreestyleSandboxes | null = null; - private userId: string | null = null; private constructor() { - this.restoreSettings(); this.freestyle = this.initFreestyleClient(); } @@ -34,11 +36,6 @@ class HostingManager { return HostingManager.instance; } - private restoreSettings() { - const settings = PersistentStorage.USER_SETTINGS.read() || {}; - this.userId = settings.id || null; - } - async deploy( folderPath: string, buildScript: string, @@ -55,44 +52,61 @@ class HostingManager { return { state: HostingStatus.ERROR, message: 'Hosting client not initialized' }; } - // TODO: Check if project is a Next.js project + try { + this.emitState(HostingStatus.DEPLOYING, 'Preparing project...'); - const BUILD_OUTPUT_PATH = folderPath + '/.next'; - const BUILD_SCRIPT_NO_LINT = buildScript + ' -- --no-lint'; + const { success: preprocessSuccess, error: preprocessError } = + await preprocessNextBuild(folderPath); + + if (!preprocessSuccess) { + this.emitState( + HostingStatus.ERROR, + 'Failed to prepare project for deployment, error: ' + preprocessError, + ); + return { + state: HostingStatus.ERROR, + message: 'Failed to prepare project for deployment, error: ' + preprocessError, + }; + } - try { this.emitState(HostingStatus.DEPLOYING, 'Creating optimized build...'); timer.log('Starting build'); - const STANDALONE_PATH = BUILD_OUTPUT_PATH + '/standalone'; - const { success, error } = await runBuildScript(folderPath, BUILD_SCRIPT_NO_LINT); + const BUILD_SCRIPT_NO_LINT = buildScript + ' -- --no-lint'; + const { success: buildSuccess, error: buildError } = await runBuildScript( + folderPath, + BUILD_SCRIPT_NO_LINT, + ); timer.log('Build completed'); - if (!success) { - this.emitState(HostingStatus.ERROR, `Build failed with error: ${error}`); + if (!buildSuccess) { + this.emitState(HostingStatus.ERROR, `Build failed with error: ${buildError}`); return { state: HostingStatus.ERROR, - message: `Build failed with error: ${error}`, + message: `Build failed with error: ${buildError}`, }; } - this.emitState(HostingStatus.DEPLOYING, 'Preparing project...'); + this.emitState(HostingStatus.DEPLOYING, 'Preparing project for deployment...'); - const preparedResult = await prepareNextProject(folderPath); + const { success: postprocessSuccess, error: postprocessError } = + await postprocessNextBuild(folderPath); timer.log('Project preparation completed'); - if (!preparedResult) { + if (!postprocessSuccess) { this.emitState( HostingStatus.ERROR, - 'Failed to prepare project for deployment, no lock file found', + 'Failed to postprocess project for deployment, error: ' + postprocessError, ); return { state: HostingStatus.ERROR, - message: 'Failed to prepare project for deployment, no lock file found', + message: + 'Failed to postprocess project for deployment, error: ' + postprocessError, }; } - const files = serializeFiles(STANDALONE_PATH); + const NEXT_BUILD_OUTPUT_PATH = folderPath + '/.next/standalone'; + const files = serializeFiles(NEXT_BUILD_OUTPUT_PATH); timer.log('Files serialized'); const config = { @@ -151,10 +165,16 @@ class HostingManager { }); } - async unpublish(url: string) { + async unpublish(url: string): Promise<{ + success: boolean; + message?: string; + }> { if (!this.freestyle) { console.error('Freestyle client not initialized'); - return; + return { + success: false, + message: 'Freestyle client not initialized', + }; } const config = { @@ -169,7 +189,10 @@ class HostingManager { if (!res.projectId) { console.error('Failed to delete deployment', res); - return false; + return { + success: false, + message: 'Failed to delete deployment. ' + res, + }; } this.emitState(HostingStatus.NO_ENV, 'Deployment deleted'); @@ -178,14 +201,20 @@ class HostingManager { state: HostingStatus.NO_ENV, message: 'Deployment deleted', }); - return true; + return { + success: true, + message: 'Deployment deleted', + }; } catch (error) { console.error('Failed to delete deployment', error); this.emitState(HostingStatus.ERROR, 'Failed to delete deployment'); analytics.trackError('Failed to delete deployment', { error, }); - return false; + return { + success: false, + message: 'Failed to delete deployment. ' + error, + }; } } } diff --git a/apps/studio/package.json b/apps/studio/package.json index 97e5683fd..c39952382 100644 --- a/apps/studio/package.json +++ b/apps/studio/package.json @@ -62,7 +62,7 @@ "embla-carousel-wheel-gestures": "^8.0.1", "fflate": "^0.8.2", "fix-path": "^4.0.0", - "freestyle-sandboxes": "^0.0.7", + "freestyle-sandboxes": "^0.0.11", "istextorbinary": "^9.5.0", "js-string-escape": "^1.0.1", "langfuse-vercel": "^3.29.1", diff --git a/apps/studio/src/lib/projects/hosting.ts b/apps/studio/src/lib/projects/hosting.ts index de1022da6..c642d91f2 100644 --- a/apps/studio/src/lib/projects/hosting.ts +++ b/apps/studio/src/lib/projects/hosting.ts @@ -1,4 +1,4 @@ -import { MainChannels } from '@onlook/models/constants'; +import { HOSTING_DOMAIN, MainChannels } from '@onlook/models/constants'; import { HostingStatus } from '@onlook/models/hosting'; import type { Project } from '@onlook/models/projects'; import { makeAutoObservable } from 'mobx'; @@ -72,21 +72,30 @@ export class HostingManager { .replace(/^-|-$/g, ''); } - createLink() { - const newUrl = `${this.createProjectSubdomain(this.project.id)}.onlook.live`; + async createLink(): Promise { + const newUrl = `${this.createProjectSubdomain(this.project.id)}.${HOSTING_DOMAIN}`; + sendAnalytics('hosting create link', { + url: newUrl, + }); this.updateProject({ hosting: { url: newUrl, }, }); - this.updateState({ url: newUrl, status: HostingStatus.READY }); - sendAnalytics('hosting create link', { - url: newUrl, - }); - this.publish(); + const success = await this.publish(); + if (!success) { + this.updateProject({ + hosting: { + url: null, + }, + }); + this.updateState({ url: null, status: HostingStatus.NO_ENV }); + return false; + } + return true; } - async publish() { + async publish(): Promise { sendAnalytics('hosting publish'); const folderPath = this.project.folderPath; if (!folderPath) { @@ -94,7 +103,7 @@ export class HostingManager { sendAnalyticsError('Failed to publish', { message: 'Failed to publish hosting environment, missing folder path', }); - return; + return false; } const buildScript: string = this.project.commands?.build || 'npm run build'; @@ -103,7 +112,7 @@ export class HostingManager { sendAnalyticsError('Failed to publish', { message: 'Failed to publish hosting environment, missing build script', }); - return; + return false; } const url = this.project.hosting?.url; @@ -112,7 +121,7 @@ export class HostingManager { sendAnalyticsError('Failed to publish', { message: 'Failed to publish hosting environment, missing url', }); - return; + return false; } this.updateState({ status: HostingStatus.DEPLOYING, message: 'Creating deployment...' }); @@ -126,7 +135,7 @@ export class HostingManager { url, }); - if (!res) { + if (!res || res.state === HostingStatus.ERROR) { console.error('Failed to publish hosting environment'); this.updateState({ status: HostingStatus.ERROR, @@ -135,7 +144,7 @@ export class HostingManager { sendAnalyticsError('Failed to publish', { message: 'Failed to publish hosting environment, no response from client', }); - return; + return false; } sendAnalytics('hosting publish success', { @@ -144,23 +153,27 @@ export class HostingManager { }); this.updateState({ status: res.state, message: res.message }); + return true; } async unpublish() { this.updateState({ status: HostingStatus.DELETING, message: 'Deleting deployment...' }); sendAnalytics('hosting unpublish'); - const res: boolean = await invokeMainChannel(MainChannels.UNPUBLISH_HOSTING_ENV, { + const res: { + success: boolean; + message?: string; + } = await invokeMainChannel(MainChannels.UNPUBLISH_HOSTING_ENV, { url: this.state.url, }); - if (!res) { - console.error('Failed to unpublish hosting environment'); + if (!res.success) { + console.error('Failed to unpublish hosting environment', res); this.updateState({ status: HostingStatus.ERROR, - message: 'Failed to unpublish hosting environment', + message: res.message || 'Failed to unpublish hosting environment', }); sendAnalyticsError('Failed to unpublish', { - message: 'Failed to unpublish hosting environment', + message: res.message || 'Failed to unpublish hosting environment', }); return; } @@ -181,6 +194,10 @@ export class HostingManager { } refresh() { - this.updateState({ status: HostingStatus.READY, message: null }); + if (this.state.url) { + this.updateState({ status: HostingStatus.READY, message: null }); + } else { + this.updateState({ status: HostingStatus.NO_ENV, message: null, url: null }); + } } } diff --git a/apps/studio/src/routes/editor/EditPanel/index.tsx b/apps/studio/src/routes/editor/EditPanel/index.tsx index 0c35427f5..fdc1c8479 100644 --- a/apps/studio/src/routes/editor/EditPanel/index.tsx +++ b/apps/studio/src/routes/editor/EditPanel/index.tsx @@ -71,7 +71,7 @@ const EditPanel = observer(() => { value={EditorTabValue.CHAT} > - {'Chat (beta)'} + {'Chat'} {selectedTab === EditorTabValue.CHAT && } diff --git a/apps/studio/src/routes/editor/TopBar/ShareProject/index.tsx b/apps/studio/src/routes/editor/TopBar/ShareProject/index.tsx index 5313bfb32..9ebe0a4dc 100644 --- a/apps/studio/src/routes/editor/TopBar/ShareProject/index.tsx +++ b/apps/studio/src/routes/editor/TopBar/ShareProject/index.tsx @@ -79,11 +79,7 @@ const ShareProject = observer(() => { }; const renderHeader = () => { - if (!projectsManager.hosting?.state.url) { - return 'Share public link'; - } - - return HostingStateMessages[projectsManager.hosting?.state.status]; + return HostingStateMessages[projectsManager.hosting?.state.status || HostingStatus.NO_ENV]; }; const renderNoEnv = () => { @@ -96,7 +92,8 @@ const ShareProject = observer(() => { className="space-y-4" >

- Share your app with the world and update it at any time in Onlook. + Share your app with the world and update it at any time in Onlook. We currently + only support Next.js projects.

- ); + const buttonClasses = + 'px-3 flex items-center border-[0.5px] text-xs justify-center shadow-sm h-8 rounded-md transition-all duration-300 ease-in-out'; + let colorClasses = 'border-input bg-background hover:bg-background-onlook text-foreground'; + + switch (projectsManager.hosting?.state.status) { + case HostingStatus.READY: + colorClasses = 'border-teal-300 bg-teal-700 hover:bg-teal-500/20 text-teal-100'; + return ( + + ); + case HostingStatus.ERROR: + colorClasses = 'border-red-500/30 bg-red-500/10 hover:bg-red-500/20 text-red-500'; + return ( + + ); + case HostingStatus.DEPLOYING: + case HostingStatus.DELETING: + return ( + + ); + case HostingStatus.NO_ENV: + default: + return ( + + ); + } }; const renderReady = () => { @@ -238,7 +273,19 @@ const ShareProject = observer(() => {
{renderLink()} - {renderUnpublish()} + {renderPublishControls()} +

+ Want to host on your own domain? + +

); diff --git a/apps/studio/src/routes/editor/TopBar/index.tsx b/apps/studio/src/routes/editor/TopBar/index.tsx index 81385478d..23f78ff6a 100644 --- a/apps/studio/src/routes/editor/TopBar/index.tsx +++ b/apps/studio/src/routes/editor/TopBar/index.tsx @@ -77,7 +77,7 @@ const EditorTopBar = observer(() => { )} -
+
{/* */} diff --git a/bun.lockb b/bun.lockb index f72110c72..2221e1b5a 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/packages/foundation/src/frameworks/next.ts b/packages/foundation/src/frameworks/next.ts index 2b99edeae..a4414522e 100644 --- a/packages/foundation/src/frameworks/next.ts +++ b/packages/foundation/src/frameworks/next.ts @@ -203,7 +203,76 @@ export const removeNextCache = (): void => { } }; -export const addStandaloneConfig = (projectDir: string): Promise => { +const addConfigProperty = ( + ast: t.File, + propertyName: string, + propertyValue: t.Expression, +): boolean => { + let propertyExists = false; + + traverse(ast, { + ObjectExpression(path) { + const properties = path.node.properties; + let hasProperty = false; + + // Check if property already exists + properties.forEach((prop) => { + if (t.isObjectProperty(prop) && t.isIdentifier(prop.key, { name: propertyName })) { + hasProperty = true; + propertyExists = true; + + // If the property value is an object expression, merge properties + if (t.isObjectExpression(prop.value) && t.isObjectExpression(propertyValue)) { + const existingProps = new Map( + prop.value.properties + .filter( + (p): p is t.ObjectProperty => + t.isObjectProperty(p) && t.isIdentifier(p.key), + ) + .map((p) => [(p.key as t.Identifier).name, p]), + ); + + // Add or update properties from propertyValue + propertyValue.properties.forEach((newProp) => { + if (t.isObjectProperty(newProp) && t.isIdentifier(newProp.key)) { + existingProps.set(newProp.key.name, newProp); + } + }); + + // Update the property value with merged properties + prop.value.properties = Array.from(existingProps.values()); + } else { + // For non-object properties, just replace the value + prop.value = propertyValue; + } + } + }); + + if (!hasProperty) { + // Add the new property if it doesn't exist + properties.push(t.objectProperty(t.identifier(propertyName), propertyValue)); + propertyExists = true; + } + + // Stop traversing after the modification + path.stop(); + }, + }); + + return propertyExists; +}; + +const addTypescriptConfig = (ast: t.File): boolean => { + return addConfigProperty( + ast, + 'typescript', + t.objectExpression([ + t.objectProperty(t.identifier('ignoreBuildErrors'), t.booleanLiteral(true)), + ]), + ); +}; + +export const addNextBuildConfig = (projectDir: string): Promise => { return new Promise((resolve) => { // Find any config file const possibleExtensions = ['.js', '.ts', '.mjs', '.cjs']; @@ -238,41 +307,14 @@ export const addStandaloneConfig = (projectDir: string): Promise => { const astParserOption = genASTParserOptionsByFileExtension(configFileExtension); const ast = parse(data, astParserOption); - let outputExists = false; - - traverse(ast, { - ObjectExpression(path) { - const properties = path.node.properties; - let hasOutputProperty = false; - - // Check if output property already exists - properties.forEach((prop) => { - if ( - t.isObjectProperty(prop) && - t.isIdentifier(prop.key, { name: 'output' }) - ) { - hasOutputProperty = true; - outputExists = true; - } - }); - - if (!hasOutputProperty) { - // Add output: 'standalone' property - properties.push( - t.objectProperty(t.identifier('output'), t.stringLiteral('standalone')), - ); - outputExists = true; - } - // Stop traversing after the modification - path.stop(); - }, - }); + // Add both configurations + const outputExists = addConfigProperty(ast, 'output', t.stringLiteral('standalone')); + const typescriptExists = addTypescriptConfig(ast); // Generate the modified code from the AST const updatedCode = generate(ast, {}, data).code; - // Write the updated content back to next.config.* file fs.writeFile(configPath, updatedCode, 'utf8', (err) => { if (err) { console.error(`Error writing ${configPath}:`, err); @@ -281,9 +323,9 @@ export const addStandaloneConfig = (projectDir: string): Promise => { } console.log( - `Successfully updated ${configPath} with standalone output configuration`, + `Successfully updated ${configPath} with standalone output and typescript configuration`, ); - resolve(outputExists); + resolve(outputExists && typescriptExists); }); }); }); diff --git a/packages/foundation/src/index.ts b/packages/foundation/src/index.ts index c3bdc5254..ebb57796f 100644 --- a/packages/foundation/src/index.ts +++ b/packages/foundation/src/index.ts @@ -1,5 +1,5 @@ export { createProject } from './create'; -export { addStandaloneConfig } from './frameworks/next'; +export { addNextBuildConfig } from './frameworks/next'; export { revertLegacyOnlook } from './revert'; export { setupProject } from './setup'; export { verifyProject } from './verify'; diff --git a/packages/foundation/tests/config.test.ts b/packages/foundation/tests/config.test.ts index 138a45284..1091c504b 100644 --- a/packages/foundation/tests/config.test.ts +++ b/packages/foundation/tests/config.test.ts @@ -1,7 +1,7 @@ import { afterEach, describe, expect, test } from 'bun:test'; import fs from 'fs'; import path from 'path'; -import { addStandaloneConfig } from '../src/frameworks/next'; +import { addNextBuildConfig } from '../src/frameworks/next'; describe('Next.js Config Modifications', () => { const configFiles = ['next.config.js', 'next.config.ts', 'next.config.mjs']; @@ -33,8 +33,8 @@ module.exports = nextConfig; fs.writeFileSync(configPath, initialConfig, 'utf8'); - // Apply the standalone config modification - addStandaloneConfig(process.cwd()); + // Apply the config modifications + addNextBuildConfig(process.cwd()); // Wait a bit for the file operation to complete await new Promise((resolve) => setTimeout(resolve, 100)); @@ -42,8 +42,10 @@ module.exports = nextConfig; // Read the modified config const modifiedConfig = fs.readFileSync(configPath, 'utf8'); - // Verify the output configuration was added + // Verify both configurations were added expect(modifiedConfig).toContain('output: "standalone"'); + expect(modifiedConfig).toContain('typescript: {'); + expect(modifiedConfig).toContain('ignoreBuildErrors: true'); expect(modifiedConfig).toContain('reactStrictMode: true'); // Clean up this config file @@ -51,24 +53,27 @@ module.exports = nextConfig; } }); - test('addStandaloneConfig does not duplicate output property', async () => { + test('addStandaloneConfig does not duplicate properties', async () => { const configPath = path.resolve(process.cwd(), 'next.config.js'); - // Create config with existing output property using CommonJS syntax - const configWithOutput = ` + // Create config with existing properties using CommonJS syntax + const configWithExisting = ` /** @type {import('next').NextConfig} */ const nextConfig = { reactStrictMode: true, - output: "standalone" + output: "standalone", + typescript: { + ignoreBuildErrors: true + } }; module.exports = nextConfig; `.trim(); - fs.writeFileSync(configPath, configWithOutput, 'utf8'); + fs.writeFileSync(configPath, configWithExisting, 'utf8'); - // Apply the standalone config modification - addStandaloneConfig(process.cwd()); + // Apply the config modifications + addNextBuildConfig(process.cwd()); // Wait a bit for the file operation to complete await new Promise((resolve) => setTimeout(resolve, 100)); @@ -76,11 +81,50 @@ module.exports = nextConfig; // Read the modified config const modifiedConfig = fs.readFileSync(configPath, 'utf8'); - // Count occurrences of 'output' + // Count occurrences of properties const outputCount = (modifiedConfig.match(/output:/g) || []).length; + const typescriptCount = (modifiedConfig.match(/typescript:/g) || []).length; - // Verify there's only one output property + // Verify there's only one instance of each property expect(outputCount).toBe(1); + expect(typescriptCount).toBe(1); expect(modifiedConfig).toContain('output: "standalone"'); + expect(modifiedConfig).toContain('typescript: {'); + expect(modifiedConfig).toContain('ignoreBuildErrors: true'); + }); + + test('addStandaloneConfig preserves existing typescript attributes', async () => { + const configPath = path.resolve(process.cwd(), 'next.config.js'); + + // Create config with existing typescript properties + const configWithExisting = ` +/** @type {import('next').NextConfig} */ +const nextConfig = { + reactStrictMode: true, + typescript: { + tsconfigPath: "./custom-tsconfig.json", + ignoreBuildErrors: false + } +}; + +module.exports = nextConfig; + `.trim(); + + fs.writeFileSync(configPath, configWithExisting, 'utf8'); + + // Apply the config modifications + addNextBuildConfig(process.cwd()); + + // Wait a bit for the file operation to complete + await new Promise((resolve) => setTimeout(resolve, 100)); + + // Read the modified config + const modifiedConfig = fs.readFileSync(configPath, 'utf8'); + + // Verify typescript configuration + expect(modifiedConfig).toContain('typescript: {'); + expect(modifiedConfig).toContain('ignoreBuildErrors: true'); // Should be updated to true + expect(modifiedConfig).toContain('tsconfigPath: "./custom-tsconfig.json"'); // Should be preserved + expect((modifiedConfig.match(/typescript:/g) || []).length).toBe(1); // Should still only have one typescript block }); }); diff --git a/packages/models/src/constants/index.ts b/packages/models/src/constants/index.ts index 54cb36de6..5cc800b93 100644 --- a/packages/models/src/constants/index.ts +++ b/packages/models/src/constants/index.ts @@ -166,6 +166,7 @@ export enum Links { export const APP_NAME = 'Onlook'; export const APP_SCHEMA = 'onlook'; +export const HOSTING_DOMAIN = 'onlook.live'; export const MAX_NAME_LENGTH = 50; export const DefaultSettings = { SCALE: 0.6, diff --git a/packages/models/src/hosting/index.ts b/packages/models/src/hosting/index.ts index f82915a49..fed8f18f9 100644 --- a/packages/models/src/hosting/index.ts +++ b/packages/models/src/hosting/index.ts @@ -7,7 +7,7 @@ export enum HostingStatus { } export const HostingStateMessages = { - [HostingStatus.NO_ENV]: 'Share public link', + [HostingStatus.NO_ENV]: 'Share public link (beta)', [HostingStatus.READY]: 'Public link', [HostingStatus.DEPLOYING]: 'Deploying', [HostingStatus.ERROR]: 'Error',