Compare commits
	
		
			6 Commits
		
	
	
		
			nightly-v2
			...
			franknoiro
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 7d6427ab64 | |||
| 4abbe0d57a | |||
| a631ff689f | |||
| e1d401adfe | |||
| 6f49c88382 | |||
| 374d07b995 | 
| @ -9,10 +9,11 @@ VITE_KC_SITE_BASE_URL=https://dev.zoo.dev | ||||
| VITE_KC_SITE_APP_URL=https://app.dev.zoo.dev | ||||
| VITE_KC_SKIP_AUTH=false | ||||
| VITE_KC_CONNECTION_TIMEOUT_MS=5000 | ||||
| #VITE_KC_DEV_TOKEN="optional token from dev.zoo.dev to skip auth in the app" | ||||
| #VITE_KC_DEV_TOKEN="optional token to skip auth in the app" | ||||
| #token="required token for playwright. TODO: clean up env vars in #3973" | ||||
|  | ||||
| RUST_BACKTRACE=1 | ||||
| PYO3_PYTHON=/usr/local/bin/python3 | ||||
| #KITTYCAD_API_TOKEN="required token from dev.zoo.dev for engine testing" | ||||
| #KITTYCAD_API_TOKEN="required token for engine testing" | ||||
|  | ||||
| FAIL_ON_CONSOLE_ERRORS=true | ||||
|  | ||||
							
								
								
									
										1
									
								
								.github/workflows/e2e-tests.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @ -229,7 +229,6 @@ jobs: | ||||
|           max_attempts: 5 | ||||
|         env: | ||||
|           token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }} | ||||
|           snapshottoken: ${{ secrets.KITTYCAD_API_TOKEN }} | ||||
|           TAB_API_URL: ${{ secrets.TAB_API_URL }} | ||||
|           TAB_API_KEY: ${{ secrets.TAB_API_KEY }} | ||||
|           CI_COMMIT_SHA: ${{ github.event.pull_request.head.sha }} | ||||
|  | ||||
| @ -63,7 +63,7 @@ If you're not a Zoo employee you won't be able to access the dev environment, yo | ||||
|  | ||||
| ### Development environment variables | ||||
|  | ||||
| The Copilot LSP plugin in the editor requires a Zoo API token to run. In production, we authenticate this with a token via cookie in the browser and device auth token in the desktop environment, but this token is inaccessible in the dev browser version because the cookie is considered "cross-site" (from `localhost` to `dev.zoo.dev`). There is an optional environment variable called `VITE_KC_DEV_TOKEN` that you can populate with a dev token in a `.env.development.local` file to not check it into Git, which will use that token instead of other methods for the LSP service. | ||||
| The Copilot LSP plugin in the editor requires a Zoo API token to run. In production, we authenticate this with a token via cookie in the browser and device auth token in the desktop environment, but this token is inaccessible in the dev browser version because the cookie is considered "cross-site" (from `localhost` to `zoo.dev`). There is an optional environment variable called `VITE_KC_DEV_TOKEN` that you can populate with a dev token in a `.env.development.local` file to not check it into Git, which will use that token instead of other methods for the LSP service. | ||||
|  | ||||
| ### Developing in Chrome | ||||
|  | ||||
| @ -198,15 +198,9 @@ For more information on fuzzing you can check out | ||||
|  | ||||
| ### Playwright tests | ||||
|  | ||||
| You will need a `./e2e/playwright/playwright-secrets.env` file: | ||||
| Prepare these system dependencies: | ||||
|  | ||||
| ```bash | ||||
| $ touch ./e2e/playwright/playwright-secrets.env | ||||
| $ cat ./e2e/playwright/playwright-secrets.env | ||||
| token=<dev.zoo.dev/account/api-tokens> | ||||
| snapshottoken=<zoo.dev/account/api-tokens> | ||||
| ``` | ||||
| or use `export` to set the environment variables `token` and `snapshottoken`. | ||||
| - Set $token from https://zoo.dev/account/api-tokens | ||||
|  | ||||
| #### Snapshot tests (Google Chrome on Ubuntu only) | ||||
|  | ||||
| @ -302,7 +296,7 @@ Which will run our suite of [Vitest unit](https://vitest.dev/) and [React Testin | ||||
|  | ||||
| Prepare these system dependencies: | ||||
|  | ||||
| - Set `$KITTYCAD_API_TOKEN` from https://dev.zoo.dev/account/api-tokens | ||||
| - Set `$KITTYCAD_API_TOKEN` from https://zoo.dev/account/api-tokens | ||||
| - Install `just` following [these instructions](https://just.systems/man/en/packages.html) | ||||
|  | ||||
| then run tests that target the KCL language: | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| import type { Locator, Page } from '@playwright/test' | ||||
| import { secrets } from '@e2e/playwright/secrets' | ||||
| import { token } from '@e2e/playwright/test-utils' | ||||
|  | ||||
| export class SignInPageFixture { | ||||
|   public page: Page | ||||
| @ -25,7 +25,7 @@ export class SignInPageFixture { | ||||
|     // Device flow: stolen from the tauri days | ||||
|     // https://github.com/KittyCAD/modeling-app/blob/d916c7987452e480719004e6d11fd2e595c7d0eb/e2e/tauri/specs/app.spec.ts#L19 | ||||
|     const headers = { | ||||
|       Authorization: `Bearer ${secrets.token}`, | ||||
|       Authorization: `Bearer ${token}`, | ||||
|       Accept: 'application/json', | ||||
|       'Content-Type': 'application/json', | ||||
|     } | ||||
|  | ||||
| @ -63,7 +63,6 @@ test.describe('Onboarding tests', () => { | ||||
|         shouldNormalise: true, | ||||
|       }) | ||||
|       await scene.connectionEstablished() | ||||
|       await expect(toolbar.startSketchBtn).toBeEnabled({ timeout: 15_000 }) | ||||
|     }) | ||||
|  | ||||
|     await test.step('Go home and verify we still see the tutorial button, then begin it.', async () => { | ||||
| @ -91,8 +90,6 @@ test.describe('Onboarding tests', () => { | ||||
|     await test.step('Ensure we see the welcome screen in a new project', async () => { | ||||
|       await expect(toolbar.projectName).toContainText('tutorial-project') | ||||
|       await expect(tutorialWelcomeHeading).toBeVisible() | ||||
|       await scene.connectionEstablished() | ||||
|       await expect(toolbar.startSketchBtn).toBeEnabled({ timeout: 15_000 }) | ||||
|     }) | ||||
|  | ||||
|     await test.step('Test the clicking through the onboarding flow', async () => { | ||||
| @ -132,8 +129,6 @@ test.describe('Onboarding tests', () => { | ||||
|       await test.step('Gets to the onboarding start', async () => { | ||||
|         await expect(toolbar.projectName).toContainText('tutorial-project') | ||||
|         await expect(tutorialWelcomeHeading).toBeVisible() | ||||
|         await scene.connectionEstablished() | ||||
|         await expect(toolbar.startSketchBtn).toBeEnabled({ timeout: 15_000 }) | ||||
|       }) | ||||
|  | ||||
|       await test.step('Dismiss the onboarding', async () => { | ||||
| @ -168,7 +163,6 @@ test.describe('Onboarding tests', () => { | ||||
|         await expect(toolbar.projectName).toContainText('tutorial-project') | ||||
|         await expect(tutorialWelcomeHeading).toBeVisible() | ||||
|         await scene.connectionEstablished() | ||||
|         await expect(toolbar.startSketchBtn).toBeEnabled({ timeout: 15_000 }) | ||||
|       }) | ||||
|  | ||||
|       await test.step('Dismiss the onboarding', async () => { | ||||
|  | ||||
| @ -1,28 +0,0 @@ | ||||
| import { readFileSync } from 'fs' | ||||
|  | ||||
| const secrets: Record<string, string> = {} | ||||
| const secretsPath = './e2e/playwright/playwright-secrets.env' | ||||
| try { | ||||
|   const file = readFileSync(secretsPath, 'utf8') | ||||
|   file | ||||
|     .split('\n') | ||||
|     .filter((line) => line && line.length > 1) | ||||
|     .forEach((line) => { | ||||
|       // Allow line comments. | ||||
|       if (line.trimStart().startsWith('#')) return | ||||
|       const [key, value] = line.split('=') | ||||
|       // prefer env vars over secrets file | ||||
|       secrets[key] = process.env[key] || (value as any).replaceAll('"', '') | ||||
|     }) | ||||
| } catch (error: unknown) { | ||||
|   void error | ||||
|   // probably running in CI | ||||
|   console.warn( | ||||
|     `Error reading ${secretsPath}; environment variables will be used` | ||||
|   ) | ||||
| } | ||||
| secrets.token = secrets.token || process.env.token || '' | ||||
| secrets.snapshottoken = secrets.snapshottoken || process.env.snapshottoken || '' | ||||
| // add more env vars here to make them available in CI | ||||
|  | ||||
| export { secrets } | ||||
| @ -5,7 +5,7 @@ import { TEST_SETTINGS, TEST_SETTINGS_KEY } from '@e2e/playwright/storageStates' | ||||
| import { | ||||
|   getUtils, | ||||
|   headerMasks, | ||||
|   networkingMasks, | ||||
|   lowerRightMasks, | ||||
|   settingsToToml, | ||||
| } from '@e2e/playwright/test-utils' | ||||
| import { expect, test } from '@e2e/playwright/zoo-test' | ||||
| @ -88,7 +88,7 @@ const extrudeDefaultPlane = async ( | ||||
|  | ||||
|   await expect(page).toHaveScreenshot({ | ||||
|     maxDiffPixels: 100, | ||||
|     mask: networkingMasks(page), | ||||
|     mask: lowerRightMasks(page), | ||||
|   }) | ||||
|   await u.openKclCodePanel() | ||||
| } | ||||
| @ -173,7 +173,7 @@ test( | ||||
|     await page.waitForTimeout(500) | ||||
|     await expect(page).toHaveScreenshot({ | ||||
|       maxDiffPixels: 100, | ||||
|       mask: networkingMasks(page), | ||||
|       mask: lowerRightMasks(page), | ||||
|     }) | ||||
|  | ||||
|     const lineEndClick = () => | ||||
| @ -200,7 +200,7 @@ test( | ||||
|  | ||||
|     await expect(page).toHaveScreenshot({ | ||||
|       maxDiffPixels: 100, | ||||
|       mask: networkingMasks(page), | ||||
|       mask: lowerRightMasks(page), | ||||
|     }) | ||||
|     await endOfTangentClk() | ||||
|  | ||||
| @ -210,7 +210,7 @@ test( | ||||
|     await threePointArcMidPointMv() | ||||
|     await expect(page).toHaveScreenshot({ | ||||
|       maxDiffPixels: 100, | ||||
|       mask: networkingMasks(page), | ||||
|       mask: lowerRightMasks(page), | ||||
|     }) | ||||
|     await threePointArcMidPointClk() | ||||
|     await page.waitForTimeout(100) | ||||
| @ -219,7 +219,7 @@ test( | ||||
|     await page.waitForTimeout(500) | ||||
|     await expect(page).toHaveScreenshot({ | ||||
|       maxDiffPixels: 100, | ||||
|       mask: networkingMasks(page), | ||||
|       mask: lowerRightMasks(page), | ||||
|     }) | ||||
|  | ||||
|     await threePointArcEndPointClk() | ||||
| @ -239,7 +239,7 @@ test( | ||||
|     await page.waitForTimeout(500) | ||||
|     await expect(page).toHaveScreenshot({ | ||||
|       maxDiffPixels: 100, | ||||
|       mask: networkingMasks(page), | ||||
|       mask: lowerRightMasks(page), | ||||
|     }) | ||||
|     await arcEndClk() | ||||
|   } | ||||
| @ -286,7 +286,7 @@ test( | ||||
|     // Ensure the draft rectangle looks the same as it usually does | ||||
|     await expect(page).toHaveScreenshot({ | ||||
|       maxDiffPixels: 100, | ||||
|       mask: networkingMasks(page), | ||||
|       mask: lowerRightMasks(page), | ||||
|     }) | ||||
|   } | ||||
| ) | ||||
| @ -328,7 +328,7 @@ test( | ||||
|     // Ensure the draft rectangle looks the same as it usually does | ||||
|     await expect(page).toHaveScreenshot({ | ||||
|       maxDiffPixels: 100, | ||||
|       mask: networkingMasks(page), | ||||
|       mask: lowerRightMasks(page), | ||||
|     }) | ||||
|     await expect(page.locator('.cm-content')).toHaveText( | ||||
|       `sketch001 = startSketchOn(XZ)profile001 = circle(sketch001, center = [366.89, -62.01], radius = 1)` | ||||
| @ -395,7 +395,7 @@ test.describe( | ||||
|       // screen shot should show the sketch | ||||
|       await expect(page).toHaveScreenshot({ | ||||
|         maxDiffPixels: 100, | ||||
|         mask: networkingMasks(page), | ||||
|         mask: lowerRightMasks(page), | ||||
|       }) | ||||
|  | ||||
|       await u.doAndWaitForImageDiff( | ||||
| @ -408,7 +408,7 @@ test.describe( | ||||
|       // second screen shot should look almost identical, i.e. scale should be the same. | ||||
|       await expect(page).toHaveScreenshot({ | ||||
|         maxDiffPixels: 100, | ||||
|         mask: networkingMasks(page), | ||||
|         mask: lowerRightMasks(page), | ||||
|       }) | ||||
|     }) | ||||
|  | ||||
| @ -490,7 +490,7 @@ test.describe( | ||||
|       // screen shot should show the sketch | ||||
|       await expect(page).toHaveScreenshot({ | ||||
|         maxDiffPixels: 100, | ||||
|         mask: networkingMasks(page), | ||||
|         mask: lowerRightMasks(page), | ||||
|       }) | ||||
|  | ||||
|       // exit sketch | ||||
| @ -504,7 +504,7 @@ test.describe( | ||||
|       // second screen shot should look almost identical, i.e. scale should be the same. | ||||
|       await expect(page).toHaveScreenshot({ | ||||
|         maxDiffPixels: 100, | ||||
|         mask: networkingMasks(page), | ||||
|         mask: lowerRightMasks(page), | ||||
|       }) | ||||
|     }) | ||||
|   } | ||||
| @ -563,7 +563,7 @@ part002 = startSketchOn(part001, face = seg01) | ||||
|  | ||||
|     await expect(page).toHaveScreenshot({ | ||||
|       maxDiffPixels: 100, | ||||
|       mask: networkingMasks(page), | ||||
|       mask: lowerRightMasks(page), | ||||
|     }) | ||||
|   } | ||||
| ) | ||||
| @ -599,7 +599,7 @@ test( | ||||
|  | ||||
|     await expect(page).toHaveScreenshot({ | ||||
|       maxDiffPixels: 100, | ||||
|       mask: networkingMasks(page), | ||||
|       mask: lowerRightMasks(page), | ||||
|     }) | ||||
|   } | ||||
| ) | ||||
| @ -636,7 +636,7 @@ test( | ||||
|  | ||||
|     await expect(page).toHaveScreenshot({ | ||||
|       maxDiffPixels: 100, | ||||
|       mask: networkingMasks(page), | ||||
|       mask: lowerRightMasks(page), | ||||
|     }) | ||||
|   } | ||||
| ) | ||||
| @ -701,7 +701,7 @@ test.describe('Grid visibility', { tag: '@snapshot' }, () => { | ||||
|  | ||||
|     await expect(stream).toHaveScreenshot({ | ||||
|       maxDiffPixels: 100, | ||||
|       mask: [...headerMasks(page), ...networkingMasks(page)], | ||||
|       mask: [...headerMasks(page), ...lowerRightMasks(page)], | ||||
|     }) | ||||
|   }) | ||||
|  | ||||
| @ -722,7 +722,7 @@ test.describe('Grid visibility', { tag: '@snapshot' }, () => { | ||||
|  | ||||
|     await expect(stream).toHaveScreenshot({ | ||||
|       maxDiffPixels: 100, | ||||
|       mask: [...headerMasks(page), ...networkingMasks(page)], | ||||
|       mask: [...headerMasks(page), ...lowerRightMasks(page)], | ||||
|     }) | ||||
|   }) | ||||
|  | ||||
| @ -761,7 +761,7 @@ test.describe('Grid visibility', { tag: '@snapshot' }, () => { | ||||
|  | ||||
|     await expect(stream).toHaveScreenshot({ | ||||
|       maxDiffPixels: 100, | ||||
|       mask: [...headerMasks(page), ...networkingMasks(page)], | ||||
|       mask: [...headerMasks(page), ...lowerRightMasks(page)], | ||||
|     }) | ||||
|   }) | ||||
| }) | ||||
| @ -829,7 +829,7 @@ test('theme persists', async ({ page, context }) => { | ||||
|  | ||||
|   await expect(page, 'expect screenshot to have light theme').toHaveScreenshot({ | ||||
|     maxDiffPixels: 100, | ||||
|     mask: networkingMasks(page), | ||||
|     mask: lowerRightMasks(page), | ||||
|   }) | ||||
| }) | ||||
|  | ||||
| @ -870,7 +870,7 @@ sweepSketch = startSketchOn(XY) | ||||
|  | ||||
|     await expect(page, 'expect small color widget').toHaveScreenshot({ | ||||
|       maxDiffPixels: 100, | ||||
|       mask: networkingMasks(page), | ||||
|       mask: lowerRightMasks(page), | ||||
|     }) | ||||
|   }) | ||||
|  | ||||
| @ -923,7 +923,7 @@ sweepSketch = startSketchOn(XY) | ||||
|       'expect small color widget to have window open' | ||||
|     ).toHaveScreenshot({ | ||||
|       maxDiffPixels: 100, | ||||
|       mask: networkingMasks(page), | ||||
|       mask: lowerRightMasks(page), | ||||
|     }) | ||||
|   }) | ||||
| }) | ||||
|  | ||||
| Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB | 
| Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB | 
| Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB | 
| Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB | 
| Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB | 
| Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB | 
| Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB | 
| Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB | 
| Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB | 
| Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB | 
| Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB | 
| Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 50 KiB | 
| Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB | 
| Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 74 KiB | 
| Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB | 
| Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB | 
| Before Width: | Height: | Size: 132 KiB After Width: | Height: | Size: 132 KiB | 
| Before Width: | Height: | Size: 116 KiB After Width: | Height: | Size: 116 KiB | 
| Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 65 KiB | 
| Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 68 KiB | 
| Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 64 KiB | 
| Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 67 KiB | 
| Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 67 KiB | 
| Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 63 KiB | 
| @ -13,11 +13,15 @@ import fsp from 'fs/promises' | ||||
| import pixelMatch from 'pixelmatch' | ||||
| import type { Protocol } from 'playwright-core/types/protocol' | ||||
| import { PNG } from 'pngjs' | ||||
| import dotenv from 'dotenv' | ||||
|  | ||||
| const NODE_ENV = process.env.NODE_ENV || 'development' | ||||
| dotenv.config({ path: [`.env.${NODE_ENV}.local`, `.env.${NODE_ENV}`] }) | ||||
| export const token = process.env.token || '' | ||||
|  | ||||
| import type { ProjectConfiguration } from '@rust/kcl-lib/bindings/ProjectConfiguration' | ||||
|  | ||||
| import { isErrorWhitelisted } from '@e2e/playwright/lib/console-error-whitelist' | ||||
| import { secrets } from '@e2e/playwright/secrets' | ||||
| import { TEST_SETTINGS, TEST_SETTINGS_KEY } from '@e2e/playwright/storageStates' | ||||
| import { test } from '@e2e/playwright/zoo-test' | ||||
|  | ||||
| @ -31,8 +35,9 @@ export const headerMasks = (page: Page) => [ | ||||
|   page.locator('#sidebar-bottom-ribbon'), | ||||
| ] | ||||
|  | ||||
| export const networkingMasks = (page: Page) => [ | ||||
| export const lowerRightMasks = (page: Page) => [ | ||||
|   page.getByTestId('network-toggle'), | ||||
|   page.getByTestId('billing-remaining-bar'), | ||||
| ] | ||||
|  | ||||
| export type TestColor = [number, number, number] | ||||
| @ -890,7 +895,7 @@ export async function setup( | ||||
|       localStorage.setItem('PLAYWRIGHT_TEST_DIR', PLAYWRIGHT_TEST_DIR) | ||||
|     }, | ||||
|     { | ||||
|       token: secrets.token, | ||||
|       token, | ||||
|       settingsKey: TEST_SETTINGS_KEY, | ||||
|       settings: settingsToToml({ | ||||
|         settings: { | ||||
| @ -918,7 +923,7 @@ export async function setup( | ||||
|   await context.addCookies([ | ||||
|     { | ||||
|       name: COOKIE_NAME, | ||||
|       value: secrets.token, | ||||
|       value: token, | ||||
|       path: '/', | ||||
|       domain: 'localhost', | ||||
|       secure: true, | ||||
|  | ||||
| @ -20,7 +20,7 @@ import { | ||||
|   createProject, | ||||
|   executorInputPath, | ||||
|   getUtils, | ||||
|   networkingMasks, | ||||
|   lowerRightMasks, | ||||
|   settingsToToml, | ||||
|   tomlToSettings, | ||||
| } from '@e2e/playwright/test-utils' | ||||
| @ -1061,7 +1061,7 @@ fn cube` | ||||
|         'toggle-settings-initial.png', | ||||
|         { | ||||
|           maxDiffPixels: 15, | ||||
|           mask: networkingMasks(page), | ||||
|           mask: lowerRightMasks(page), | ||||
|         } | ||||
|       ) | ||||
|  | ||||
| @ -1078,7 +1078,7 @@ fn cube` | ||||
|         'toggle-settings-initial.png', | ||||
|         { | ||||
|           maxDiffPixels: 15, | ||||
|           mask: networkingMasks(page), | ||||
|           mask: lowerRightMasks(page), | ||||
|         } | ||||
|       ) | ||||
|     }) | ||||
|  | ||||
							
								
								
									
										1
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						| @ -2492,7 +2492,6 @@ | ||||
|     }, | ||||
|     "node_modules/@clack/prompts/node_modules/is-unicode-supported": { | ||||
|       "version": "1.3.0", | ||||
|       "extraneous": true, | ||||
|       "inBundle": true, | ||||
|       "license": "MIT", | ||||
|       "engines": { | ||||
|  | ||||
| @ -137,8 +137,8 @@ | ||||
|     "test:unit:components": "jest -c jest-component-unit-tests/jest.config.ts --rootDir jest-component-unit-tests/", | ||||
|     "test:unit:kcl-samples": "vitest run --mode development ./src/lang/kclSamples.test.ts", | ||||
|     "test:playwright:electron": "playwright test --config=playwright.electron.config.ts --grep-invert=@snapshot", | ||||
|     "test:playwright:electron:local": "npm run tronb:vite:dev && NODE_ENV=development playwright test --config=playwright.electron.config.ts --grep-invert=@snapshot --grep-invert=\"$(curl --silent https://test-analysis-bot.hawk-dinosaur.ts.net/projects/KittyCAD/modeling-app/tests/disabled/regex)\"", | ||||
|     "test:playwright:electron:local-engine": "npm run tronb:vite:dev && NODE_ENV=development playwright test --config=playwright.electron.config.ts --grep-invert='@snapshot|@skipLocalEngine' --grep-invert=\"$(curl --silent https://test-analysis-bot.hawk-dinosaur.ts.net/projects/KittyCAD/modeling-app/tests/disabled/regex)\"", | ||||
|     "test:playwright:electron:local": "npm run tronb:vite:dev && playwright test --config=playwright.electron.config.ts --grep-invert=@snapshot --grep-invert=\"$(curl --silent https://test-analysis-bot.hawk-dinosaur.ts.net/projects/KittyCAD/modeling-app/tests/disabled/regex)\"", | ||||
|     "test:playwright:electron:local-engine": "npm run tronb:vite:dev && playwright test --config=playwright.electron.config.ts --grep-invert='@snapshot|@skipLocalEngine' --grep-invert=\"$(curl --silent https://test-analysis-bot.hawk-dinosaur.ts.net/projects/KittyCAD/modeling-app/tests/disabled/regex)\"", | ||||
|     "test:unit:local": "npm run simpleserver:bg && npm run test:unit; kill-port 3000", | ||||
|     "test:unit:kcl-samples:local": "npm run simpleserver:bg && npm run test:unit:kcl-samples; kill-port 3000" | ||||
|   }, | ||||
|  | ||||
| @ -14,11 +14,7 @@ import Tooltip from '@src/components/Tooltip' | ||||
| import { useAbsoluteFilePath } from '@src/hooks/useAbsoluteFilePath' | ||||
| import { openExternalBrowserIfDesktop } from '@src/lib/openWindow' | ||||
| import { PATHS } from '@src/lib/paths' | ||||
| import { | ||||
|   APP_VERSION, | ||||
|   IS_NIGHTLY_OR_DEBUG, | ||||
|   getReleaseUrl, | ||||
| } from '@src/routes/utils' | ||||
| import { APP_VERSION, getReleaseUrl } from '@src/routes/utils' | ||||
|  | ||||
| import { billingActor } from '@src/lib/singletons' | ||||
|  | ||||
| @ -39,22 +35,23 @@ export function LowerRightControls({ | ||||
|     <section className="fixed bottom-2 right-2 flex flex-col items-end gap-3 pointer-events-none"> | ||||
|       {children} | ||||
|       <menu className="flex items-center justify-end gap-3 pointer-events-auto"> | ||||
|         {IS_NIGHTLY_OR_DEBUG && ( | ||||
|           <Popover className="relative"> | ||||
|             <Popover.Button className="p-0 !border-transparent"> | ||||
|               <BillingRemaining | ||||
|                 mode={BillingRemainingMode.ProgressBarFixed} | ||||
|                 billingActor={billingActor} | ||||
|               /> | ||||
|               <Tooltip position="top" contentClassName="text-xs"> | ||||
|                 Text-to-CAD credits | ||||
|               </Tooltip> | ||||
|             </Popover.Button> | ||||
|             <Popover.Panel className="absolute right-0 left-auto bottom-full mb-1 w-64 flex flex-col gap-1 align-stretch rounded-lg shadow-lg text-sm"> | ||||
|               <BillingDialog billingActor={billingActor} /> | ||||
|             </Popover.Panel> | ||||
|           </Popover> | ||||
|         )} | ||||
|         <Popover className="relative"> | ||||
|           <Popover.Button | ||||
|             className="p-0 !border-transparent" | ||||
|             data-testid="billing-remaining-bar" | ||||
|           > | ||||
|             <BillingRemaining | ||||
|               mode={BillingRemainingMode.ProgressBarFixed} | ||||
|               billingActor={billingActor} | ||||
|             /> | ||||
|             <Tooltip position="top" contentClassName="text-xs"> | ||||
|               Text-to-CAD credits | ||||
|             </Tooltip> | ||||
|           </Popover.Button> | ||||
|           <Popover.Panel className="absolute right-0 left-auto bottom-full mb-1 w-64 flex flex-col gap-1 align-stretch rounded-lg shadow-lg text-sm"> | ||||
|             <BillingDialog billingActor={billingActor} /> | ||||
|           </Popover.Panel> | ||||
|         </Popover> | ||||
|         <a | ||||
|           onClick={openExternalBrowserIfDesktop(getReleaseUrl())} | ||||
|           href={getReleaseUrl()} | ||||
|  | ||||
| @ -1,4 +1,3 @@ | ||||
| import { IS_NIGHTLY_OR_DEBUG } from '@src/routes/utils' | ||||
| import type { FormEvent, HTMLProps } from 'react' | ||||
| import { useEffect } from 'react' | ||||
| import { toast } from 'react-hot-toast' | ||||
| @ -351,13 +350,11 @@ const Home = () => { | ||||
|             </li> | ||||
|           </ul> | ||||
|           <ul className="flex flex-col"> | ||||
|             {IS_NIGHTLY_OR_DEBUG && ( | ||||
|               <li className="contents"> | ||||
|                 <div className="my-2"> | ||||
|                   <BillingDialog billingActor={billingActor} /> | ||||
|                 </div> | ||||
|               </li> | ||||
|             )} | ||||
|             <li className="contents"> | ||||
|               <div className="my-2"> | ||||
|                 <BillingDialog billingActor={billingActor} /> | ||||
|               </div> | ||||
|             </li> | ||||
|             <li className="contents"> | ||||
|               <ActionButton | ||||
|                 Element="externalLink" | ||||
|  | ||||
| @ -79,7 +79,7 @@ function Welcome() { | ||||
|   }, []) | ||||
|  | ||||
|   return ( | ||||
|     <div className="fixed inset-0 z-50 grid items-end justify-center p-2"> | ||||
|     <div className="cursor-not-allowed fixed inset-0 z-50 grid items-end justify-center p-2"> | ||||
|       <OnboardingCard> | ||||
|         <h1 className="text-xl font-bold">Welcome to Zoo Design Studio</h1> | ||||
|         <p className="my-4"> | ||||
| @ -149,7 +149,7 @@ function Toolbar() { | ||||
|   useOnboardingPanes() | ||||
|  | ||||
|   return ( | ||||
|     <div className="fixed inset-0 z-[99] grid items-start justify-center p-16"> | ||||
|     <div className="cursor-not-allowed fixed inset-0 z-[99] grid items-start justify-center p-16"> | ||||
|       <OnboardingCard> | ||||
|         <h1 className="text-xl font-bold">This is the toolbar</h1> | ||||
|         <p className="my-4"> | ||||
| @ -170,7 +170,7 @@ function TextToCad() { | ||||
|   useOnboardingPanes() | ||||
|  | ||||
|   return ( | ||||
|     <div className="fixed inset-0 z-50 grid items-start justify-center p-16"> | ||||
|     <div className="cursor-not-allowed fixed inset-0 z-50 grid items-start justify-center p-16"> | ||||
|       <OnboardingCard> | ||||
|         <h1 className="text-xl font-bold">Text-to-CAD</h1> | ||||
|         <p className="my-4"> | ||||
| @ -229,7 +229,7 @@ function TextToCadPrompt() { | ||||
|   useAdvanceOnboardingOnFormSubmit(thisOnboardingStatus) | ||||
|  | ||||
|   return ( | ||||
|     <div className="fixed inset-0 z-[99] grid items-center justify-center"> | ||||
|     <div className="cursor-not-allowed fixed inset-0 z-[99] grid items-center justify-center"> | ||||
|       <OnboardingCard> | ||||
|         <h1 className="text-xl font-bold">Text-to-CAD prompt</h1> | ||||
|         <p className="my-4"> | ||||
| @ -325,7 +325,7 @@ function PromptToEdit() { | ||||
|   useAdvanceOnboardingOnFormSubmit(thisOnboardingStatus) | ||||
|  | ||||
|   return ( | ||||
|     <div className="fixed inset-0 z-50 grid items-center justify-center p-16"> | ||||
|     <div className="cursor-not-allowed fixed inset-0 z-50 grid items-center justify-center p-16"> | ||||
|       <OnboardingCard className="col-start-3 col-span-2"> | ||||
|         <h1 className="text-xl font-bold">Modify with Zoo Text-to-CAD</h1> | ||||
|         <p className="my-4"> | ||||
| @ -381,7 +381,7 @@ function PromptToEditPrompt() { | ||||
|   useAdvanceOnboardingOnFormSubmit(thisOnboardingStatus) | ||||
|  | ||||
|   return ( | ||||
|     <div className="fixed inset-0 z-[99] grid items-center justify-center"> | ||||
|     <div className="cursor-not-allowed fixed inset-0 z-[99] grid items-center justify-center"> | ||||
|       <OnboardingCard className="pointer-events-auto"> | ||||
|         <h1 className="text-xl font-bold">Modify with Text-to-CAD prompt</h1> | ||||
|         {!isReady && ( | ||||
| @ -429,7 +429,7 @@ function PromptToEditResult() { | ||||
|   }, []) | ||||
|  | ||||
|   return ( | ||||
|     <div className="fixed inset-0 z-[99] p-8 grid justify-center items-end"> | ||||
|     <div className="cursor-not-allowed fixed inset-0 z-[99] p-8 grid justify-center items-end"> | ||||
|       <OnboardingCard className="col-start-3 col-span-2"> | ||||
|         <h1 className="text-xl font-bold">Result</h1> | ||||
|         <p className="my-4"> | ||||
| @ -462,7 +462,7 @@ function OnboardingConclusion() { | ||||
|   useOnboardingPanes() | ||||
|  | ||||
|   return ( | ||||
|     <div className="fixed inset-0 z-50 p-16 grid justify-center items-center"> | ||||
|     <div className="cursor-not-allowed fixed inset-0 z-50 p-16 grid justify-center items-center"> | ||||
|       <OnboardingCard> | ||||
|         <h1 className="text-xl font-bold">Download the desktop app</h1> | ||||
|         <p className="my-4"> | ||||
|  | ||||
| @ -78,7 +78,7 @@ function Welcome() { | ||||
|   }, [loaderData?.project?.name]) | ||||
|  | ||||
|   return ( | ||||
|     <div className="fixed inset-0 z-50 grid items-end justify-center p-2"> | ||||
|     <div className="cursor-not-allowed fixed inset-0 z-50 grid items-end justify-center p-2"> | ||||
|       <OnboardingCard> | ||||
|         <h1 className="text-xl font-bold">Welcome to Zoo Design Studio</h1> | ||||
|         <p className="my-4"> | ||||
| @ -140,7 +140,7 @@ function Toolbar() { | ||||
|   useOnboardingPanes() | ||||
|  | ||||
|   return ( | ||||
|     <div className="fixed inset-0 z-[99] grid items-start justify-center p-16"> | ||||
|     <div className="cursor-not-allowed fixed inset-0 z-[99] grid items-start justify-center p-16"> | ||||
|       <OnboardingCard> | ||||
|         <h1 className="text-xl font-bold">This is the toolbar</h1> | ||||
|         <p className="my-4"> | ||||
| @ -161,7 +161,7 @@ function TextToCad() { | ||||
|   useOnboardingPanes() | ||||
|  | ||||
|   return ( | ||||
|     <div className="fixed inset-0 z-50 grid items-start justify-center p-16"> | ||||
|     <div className="cursor-not-allowed fixed inset-0 z-50 grid items-start justify-center p-16"> | ||||
|       <OnboardingCard> | ||||
|         <h1 className="text-xl font-bold">Text-to-CAD</h1> | ||||
|         <p className="my-4"> | ||||
| @ -220,7 +220,7 @@ function TextToCadPrompt() { | ||||
|   useAdvanceOnboardingOnFormSubmit(thisOnboardingStatus, 'desktop') | ||||
|  | ||||
|   return ( | ||||
|     <div className="fixed inset-0 z-[99] grid items-center justify-center"> | ||||
|     <div className="cursor-not-allowed fixed inset-0 z-[99] grid items-center justify-center"> | ||||
|       <OnboardingCard className="pointer-events-auto"> | ||||
|         <h1 className="text-xl font-bold">Text-to-CAD prompt</h1> | ||||
|         <p className="my-4"> | ||||
| @ -266,7 +266,7 @@ function FeatureTreePane() { | ||||
|   }, []) | ||||
|  | ||||
|   return ( | ||||
|     <div className="fixed inset-0 z-[99] p-8 grid justify-center items-end"> | ||||
|     <div className="cursor-not-allowed fixed inset-0 z-[99] p-8 grid justify-center items-end"> | ||||
|       <OnboardingCard className="col-start-3 col-span-2"> | ||||
|         <h1 className="text-xl font-bold">CPU Fan Housing</h1> | ||||
|         <p className="my-4"> | ||||
| @ -297,7 +297,7 @@ function CodePane() { | ||||
|   useOnboardingPanes(['code']) | ||||
|  | ||||
|   return ( | ||||
|     <div className="fixed inset-0 z-50 p-8 grid justify-center items-end"> | ||||
|     <div className="cursor-not-allowed fixed inset-0 z-50 p-8 grid justify-center items-end"> | ||||
|       <OnboardingCard className="col-start-3 col-span-2"> | ||||
|         <h1 className="text-xl font-bold">KCL Code</h1> | ||||
|         <p className="my-4"> | ||||
| @ -328,7 +328,7 @@ function ProjectPane() { | ||||
|   useOnboardingPanes(['files']) | ||||
|  | ||||
|   return ( | ||||
|     <div className="fixed inset-0 z-50 p-8 grid justify-center items-end"> | ||||
|     <div className="cursor-not-allowed fixed inset-0 z-50 p-8 grid justify-center items-end"> | ||||
|       <OnboardingCard className="col-start-3 col-span-2"> | ||||
|         <h1 className="text-xl font-bold">Files Pane</h1> | ||||
|         <p className="my-4"> | ||||
| @ -355,7 +355,7 @@ function OtherPanes() { | ||||
|   useOnboardingPanes(['logs', 'variables']) | ||||
|  | ||||
|   return ( | ||||
|     <div className="fixed inset-0 z-50 p-8 grid justify-center items-end"> | ||||
|     <div className="cursor-not-allowed fixed inset-0 z-50 p-8 grid justify-center items-end"> | ||||
|       <OnboardingCard className="col-start-3 col-span-2"> | ||||
|         <h1 className="text-xl font-bold">Other panes</h1> | ||||
|         <p className="my-4"> | ||||
| @ -400,7 +400,7 @@ function PromptToEdit() { | ||||
|   useAdvanceOnboardingOnFormSubmit(thisOnboardingStatus, 'desktop') | ||||
|  | ||||
|   return ( | ||||
|     <div className="fixed inset-0 z-50 p-8 grid justify-center items-center"> | ||||
|     <div className="cursor-not-allowed fixed inset-0 z-50 p-8 grid justify-center items-center"> | ||||
|       <OnboardingCard className="col-start-3 col-span-2"> | ||||
|         <h1 className="text-xl font-bold">Modify with Zoo Text-to-CAD</h1> | ||||
|         <p className="my-4"> | ||||
| @ -456,7 +456,7 @@ function PromptToEditPrompt() { | ||||
|   useAdvanceOnboardingOnFormSubmit(thisOnboardingStatus, 'desktop') | ||||
|  | ||||
|   return ( | ||||
|     <div className="fixed inset-0 z-[99] grid items-center justify-center"> | ||||
|     <div className="cursor-not-allowed fixed inset-0 z-[99] grid items-center justify-center"> | ||||
|       <OnboardingCard className="pointer-events-auto"> | ||||
|         <h1 className="text-xl font-bold">Modify with Text-to-CAD prompt</h1> | ||||
|         {!isReady && ( | ||||
| @ -510,7 +510,7 @@ function PromptToEditResult() { | ||||
|   }, []) | ||||
|  | ||||
|   return ( | ||||
|     <div className="fixed inset-0 z-[99] p-8 grid justify-center items-end"> | ||||
|     <div className="cursor-not-allowed fixed inset-0 z-[99] p-8 grid justify-center items-end"> | ||||
|       <OnboardingCard className="col-start-3 col-span-2"> | ||||
|         <h1 className="text-xl font-bold">Result</h1> | ||||
|         <p className="my-4"> | ||||
| @ -548,7 +548,7 @@ function Imports() { | ||||
|   useOnboardingPanes() | ||||
|  | ||||
|   return ( | ||||
|     <div className="fixed inset-0 z-50 p-16 flex flex-col gap-8 items-center"> | ||||
|     <div className="cursor-not-allowed fixed inset-0 z-50 p-16 flex flex-col gap-8 items-center"> | ||||
|       <OnboardingCard> | ||||
|         <h1 className="text-xl font-bold">Add file(s) to project</h1> | ||||
|         <p className="my-4"> | ||||
| @ -579,7 +579,7 @@ function Exports() { | ||||
|   useOnboardingPanes() | ||||
|  | ||||
|   return ( | ||||
|     <div className="fixed inset-0 z-50 p-16 grid justify-start items-center"> | ||||
|     <div className="cursor-not-allowed fixed inset-0 z-50 p-16 grid justify-start items-center"> | ||||
|       <OnboardingCard> | ||||
|         <h1 className="text-xl font-bold">Exporting</h1> | ||||
|         <p className="my-4"> | ||||
| @ -607,7 +607,7 @@ function OnboardingConclusion() { | ||||
|   ) | ||||
|  | ||||
|   return ( | ||||
|     <div className="fixed inset-0 z-50 p-16 grid justify-center items-center"> | ||||
|     <div className="cursor-not-allowed fixed inset-0 z-50 p-16 grid justify-center items-center"> | ||||
|       <OnboardingCard> | ||||
|         <h1 className="text-xl font-bold">Time to start building</h1> | ||||
|         <p className="my-4"> | ||||
|  | ||||
| @ -54,7 +54,7 @@ export const OnboardingCard = ({ | ||||
|   ...props | ||||
| }: React.HTMLAttributes<HTMLDivElement>) => ( | ||||
|   <div | ||||
|     className={`relative max-w-3xl min-w-80 bg-chalkboard-10 dark:bg-chalkboard-90 py-6 px-8 rounded border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg ${className || ''}`} | ||||
|     className={`relative max-w-3xl min-w-80 cursor-auto bg-chalkboard-10 dark:bg-chalkboard-90 py-6 px-8 rounded border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg ${className || ''}`} | ||||
|     {...props} | ||||
|   > | ||||
|     {children} | ||||
|  | ||||
