diff --git a/e2e/playwright/testing-samples-loading.spec.ts b/e2e/playwright/testing-samples-loading.spec.ts index 401f6958f..ddc2080c6 100644 --- a/e2e/playwright/testing-samples-loading.spec.ts +++ b/e2e/playwright/testing-samples-loading.spec.ts @@ -4,6 +4,7 @@ import { bracket } from 'lib/exampleKcl' import * as fsp from 'fs/promises' import { join } from 'path' import { FILE_EXT } from 'lib/constants' +import { UnitLength_type } from '@kittycad/lib/dist/types/src/models' test.beforeEach(async ({ context, page }, testInfo) => { await setup(context, page, testInfo) @@ -15,8 +16,8 @@ test.afterEach(async ({ page }, testInfo) => { test.describe('Testing in-app sample loading', () => { /** - * Note this test implicitly depends on the KCL sample "flange-with-patterns.kcl" - * and its title. https://github.com/KittyCAD/kcl-samples/blob/main/flange-with-patterns/flange-with-patterns.kcl + * Note this test implicitly depends on the KCL sample "car-wheel.kcl", + * its title, and its units settings. https://github.com/KittyCAD/kcl-samples/blob/main/car-wheel/car-wheel.kcl */ test('Web: should overwrite current code, cannot create new file', async ({ page, @@ -33,8 +34,8 @@ test.describe('Testing in-app sample loading', () => { // Locators and constants const newSample = { - file: 'flange-with-patterns' + FILE_EXT, - title: 'Flange', + file: 'car-wheel' + FILE_EXT, + title: 'Car Wheel', } const commandBarButton = page.getByRole('button', { name: 'Commands' }) const samplesCommandOption = page.getByRole('option', { @@ -51,9 +52,11 @@ test.describe('Testing in-app sample loading', () => { page.getByRole('option', { name, }) - const warningText = page.getByText('Overwrite current file?') + const warningText = page.getByText('Overwrite current file and units?') const confirmButton = page.getByRole('button', { name: 'Submit command' }) const codeLocator = page.locator('.cm-content') + const unitsToast = (unit: UnitLength_type) => + page.getByText(`Set default unit to "${unit}" for this project`) await test.step(`Precondition: check the initial code`, async () => { await u.openKclCodePanel() @@ -71,12 +74,13 @@ test.describe('Testing in-app sample loading', () => { await confirmButton.click() await expect(codeLocator).toContainText('// ' + newSample.title) + await expect(unitsToast('in')).toBeVisible() }) }) /** * Note this test implicitly depends on the KCL samples: - * "flange-with-patterns.kcl": https://github.com/KittyCAD/kcl-samples/blob/main/flange-with-patterns/flange-with-patterns.kcl + * "car-wheel.kcl": https://github.com/KittyCAD/kcl-samples/blob/main/car-wheel/car-wheel.kcl * "gear-rack.kcl": https://github.com/KittyCAD/kcl-samples/blob/main/gear-rack/gear-rack.kcl */ test( @@ -97,8 +101,8 @@ test.describe('Testing in-app sample loading', () => { // Locators and constants const sampleOne = { - file: 'flange-with-patterns' + FILE_EXT, - title: 'Flange', + file: 'car-wheel' + FILE_EXT, + title: 'Car Wheel', } const sampleTwo = { file: 'gear-rack' + FILE_EXT, @@ -119,9 +123,11 @@ test.describe('Testing in-app sample loading', () => { name: 'Overwrite', }) const newFileWarning = page.getByText( - 'Create a new file with the example code?' + 'Create a new file, overwrite project units?' + ) + const overwriteWarning = page.getByText( + 'Overwrite current file and units?' ) - const overwriteWarning = page.getByText('Overwrite current file?') const confirmButton = page.getByRole('button', { name: 'Submit command' }) const projectMenuButton = page.getByTestId('project-sidebar-toggle') const newlyCreatedFile = (name: string) => @@ -129,6 +135,8 @@ test.describe('Testing in-app sample loading', () => { has: page.getByRole('button', { name }), }) const codeLocator = page.locator('.cm-content') + const unitsToast = (unit: UnitLength_type) => + page.getByText(`Set default unit to "${unit}" for this project`) await test.step(`Test setup`, async () => { await page.setViewportSize({ width: 1200, height: 500 }) @@ -158,6 +166,7 @@ test.describe('Testing in-app sample loading', () => { await expect(codeLocator).toContainText('// ' + sampleOne.title) await expect(newlyCreatedFile(sampleOne.file)).toBeVisible() await expect(projectMenuButton).toContainText(sampleOne.file) + await expect(unitsToast('in')).toBeVisible() }) await test.step(`Now overwrite the current file`, async () => { @@ -187,6 +196,7 @@ test.describe('Testing in-app sample loading', () => { await expect(newlyCreatedFile(sampleOne.file)).toBeVisible() await expect(newlyCreatedFile(sampleTwo.file)).not.toBeVisible() await expect(projectMenuButton).toContainText(sampleOne.file) + await expect(unitsToast('mm')).toBeVisible() }) await electronApp.close() diff --git a/src/components/CommandBarOverwriteWarning.tsx b/src/components/CommandBarOverwriteWarning.tsx index 08398e777..9e9dd239f 100644 --- a/src/components/CommandBarOverwriteWarning.tsx +++ b/src/components/CommandBarOverwriteWarning.tsx @@ -4,8 +4,8 @@ interface CommandBarOverwriteWarningProps { } export function CommandBarOverwriteWarning({ - heading = 'Overwrite current file?', - message = 'This will permanently replace the current code in the editor.', + heading = 'Overwrite current file and units?', + message = 'This will permanently replace the current code in the editor, and overwrite your current units.', }: CommandBarOverwriteWarningProps) { return ( <> diff --git a/src/components/FileMachineProvider.tsx b/src/components/FileMachineProvider.tsx index 2e7394f00..9c6788e89 100644 --- a/src/components/FileMachineProvider.tsx +++ b/src/components/FileMachineProvider.tsx @@ -28,6 +28,7 @@ import { getKclSamplesManifest, KclSamplesManifestItem, } from 'lib/getKclSamplesManifest' +import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext' type MachineContext = { state: StateFrom @@ -46,6 +47,7 @@ export const FileMachineProvider = ({ }) => { const navigate = useNavigate() const { commandBarSend } = useCommandsContext() + const { settings } = useSettingsAuthContext() const { project, file } = useRouteLoaderData(PATHS.FILE) as IndexLoaderData const [kclSamples, setKclSamples] = React.useState( [] @@ -306,6 +308,18 @@ export const FileMachineProvider = ({ }, }) } + + // Either way, we want to overwrite the defaultUnit project setting + // with the sample's setting. + if (data.sampleUnits) { + settings.send({ + type: 'set.modeling.defaultUnit', + data: { + level: 'project', + value: data.sampleUnits, + }, + }) + } }, kclSamples.map((sample) => ({ value: sample.file, diff --git a/src/lib/kclCommands.ts b/src/lib/kclCommands.ts index 3d60f6c64..f83392b44 100644 --- a/src/lib/kclCommands.ts +++ b/src/lib/kclCommands.ts @@ -2,11 +2,16 @@ import { CommandBarOverwriteWarning } from 'components/CommandBarOverwriteWarnin import { Command, CommandArgumentOption } from './commandTypes' import { kclManager } from './singletons' import { isDesktop } from './isDesktop' -import { FILE_EXT } from './constants' +import { FILE_EXT, PROJECT_SETTINGS_FILE_NAME } from './constants' +import { UnitLength_type } from '@kittycad/lib/dist/types/src/models' +import { parseProjectSettings } from 'lang/wasm' +import { err } from './trap' +import { projectConfigurationToSettingsPayload } from './settings/settingsUtils' interface OnSubmitProps { sampleName: string code: string + sampleUnits?: UnitLength_type method: 'overwrite' | 'newFile' } @@ -34,7 +39,10 @@ export function kclCommands( icon: 'code', reviewMessage: ({ argumentsToSubmit }) => argumentsToSubmit.method === 'newFile' - ? 'Create a new file with the example code?' + ? CommandBarOverwriteWarning({ + heading: 'Create a new file, overwrite project units?', + message: `This will add the sample as a new file to your project, and replace your current project units with the sample's units.`, + }) : CommandBarOverwriteWarning({}), groupId: 'code', onSubmit(data) { @@ -44,20 +52,45 @@ export function kclCommands( const sampleCodeUrl = `https://raw.githubusercontent.com/KittyCAD/kcl-samples/main/${encodeURIComponent( data.sample.replace(FILE_EXT, '') )}/${encodeURIComponent(data.sample)}` - fetch(sampleCodeUrl) - .then(async (response) => { - if (!response.ok) { - console.error('Failed to fetch sample code:', response.statusText) - return - } - const code = await response.text() + const sampleSettingsFileUrl = `https://raw.githubusercontent.com/KittyCAD/kcl-samples/main/${encodeURIComponent( + data.sample.replace(FILE_EXT, '') + )}/${PROJECT_SETTINGS_FILE_NAME}` - return { - sampleName: data.sample, - code, - method: data.method, + Promise.all([fetch(sampleCodeUrl), fetch(sampleSettingsFileUrl)]) + .then( + async ([ + codeResponse, + settingsResponse, + ]): Promise => { + if (!(codeResponse.ok && settingsResponse.ok)) { + console.error( + 'Failed to fetch sample code:', + codeResponse.statusText + ) + return Promise.reject(new Error('Failed to fetch sample code')) + } + const code = await codeResponse.text() + const parsedProjectSettings = parseProjectSettings( + await settingsResponse.text() + ) + let projectSettingsPayload: ReturnType< + typeof projectConfigurationToSettingsPayload + > = {} + if (!err(parsedProjectSettings)) { + projectSettingsPayload = projectConfigurationToSettingsPayload( + parsedProjectSettings + ) + } + + return { + sampleName: data.sample, + code, + method: data.method, + sampleUnits: + projectSettingsPayload.modeling?.defaultUnit || 'mm', + } } - }) + ) .then((props) => { if (props?.code) { onSubmit(props).catch(reportError)