diff --git a/e2e/playwright/projects.spec.ts b/e2e/playwright/projects.spec.ts
index 72c4411ba..fece88f91 100644
--- a/e2e/playwright/projects.spec.ts
+++ b/e2e/playwright/projects.spec.ts
@@ -136,6 +136,335 @@ test(
}
)
+test(
+ 'open a file in a project works and renders, open another file in different project with errors, it should clear the scene',
+ { tag: '@electron' },
+ async ({ browserName }, testInfo) => {
+ const { electronApp, page } = await setupElectron({
+ testInfo,
+ folderSetupFn: async (dir) => {
+ const bracketDir = join(dir, 'bracket')
+ await fsp.mkdir(bracketDir, { recursive: true })
+ await fsp.copyFile(
+ executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
+ join(bracketDir, 'main.kcl')
+ )
+ const errorDir = join(dir, 'broken-code')
+ await fsp.mkdir(errorDir, { recursive: true })
+ await fsp.copyFile(
+ executorInputPath('broken-code-test.kcl'),
+ join(errorDir, 'main.kcl')
+ )
+ },
+ })
+
+ await page.setViewportSize({ width: 1200, height: 500 })
+ const u = await getUtils(page)
+
+ page.on('console', console.log)
+
+ const pointOnModel = { x: 630, y: 280 }
+
+ await test.step('Opening the bracket project should load the stream', async () => {
+ // expect to see the text bracket
+ await expect(page.getByText('bracket')).toBeVisible()
+
+ await page.getByText('bracket').click()
+
+ await expect(page.getByTestId('loading')).toBeAttached()
+ await expect(page.getByTestId('loading')).not.toBeAttached({
+ timeout: 20_000,
+ })
+
+ await expect(
+ page.getByRole('button', { name: 'Start Sketch' })
+ ).toBeEnabled({
+ timeout: 20_000,
+ })
+
+ // gray at this pixel means the stream has loaded in the most
+ // user way we can verify it (pixel color)
+ await expect
+ .poll(() => u.getGreatestPixDiff(pointOnModel, [85, 85, 85]), {
+ timeout: 10_000,
+ })
+ .toBeLessThan(15)
+ })
+
+ await test.step('Clicking the logo takes us back to the projects page / home', async () => {
+ await page.getByTestId('app-logo').click()
+
+ await expect(page.getByRole('link', { name: 'bracket' })).toBeVisible()
+ await expect(page.getByText('broken-code')).toBeVisible()
+ await expect(page.getByText('bracket')).toBeVisible()
+ await expect(page.getByText('New Project')).toBeVisible()
+ })
+ await test.step('opening broken code project should clear the scene and show the error', async () => {
+ // Go back home.
+ await expect(page.getByText('broken-code')).toBeVisible()
+
+ await page.getByText('broken-code').click()
+
+ // error in guter
+ await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
+
+ // error text on hover
+ await page.hover('.cm-lint-marker-error')
+ const crypticErrorText = `Expected a tag declarator`
+ await expect(page.getByText(crypticErrorText).first()).toBeVisible()
+
+ // black pixel means the scene has been cleared.
+ await expect
+ .poll(() => u.getGreatestPixDiff(pointOnModel, [30, 30, 30]), {
+ timeout: 10_000,
+ })
+ .toBeLessThan(15)
+ })
+
+ await electronApp.close()
+ }
+)
+
+test(
+ 'open a file in a project works and renders, open another file in different project that is empty, it should clear the scene',
+ { tag: '@electron' },
+ async ({ browserName }, testInfo) => {
+ const { electronApp, page } = await setupElectron({
+ testInfo,
+ folderSetupFn: async (dir) => {
+ const bracketDir = join(dir, 'bracket')
+ await fsp.mkdir(bracketDir, { recursive: true })
+ await fsp.copyFile(
+ executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
+ join(bracketDir, 'main.kcl')
+ )
+ const emptyDir = join(dir, 'empty')
+ await fsp.mkdir(emptyDir, { recursive: true })
+ await fsp.writeFile(join(emptyDir, 'main.kcl'), '')
+ },
+ })
+
+ await page.setViewportSize({ width: 1200, height: 500 })
+ const u = await getUtils(page)
+
+ page.on('console', console.log)
+
+ const pointOnModel = { x: 630, y: 280 }
+
+ await test.step('Opening the bracket project should load the stream', async () => {
+ // expect to see the text bracket
+ await expect(page.getByText('bracket')).toBeVisible()
+
+ await page.getByText('bracket').click()
+
+ await expect(page.getByTestId('loading')).toBeAttached()
+ await expect(page.getByTestId('loading')).not.toBeAttached({
+ timeout: 20_000,
+ })
+
+ await expect(
+ page.getByRole('button', { name: 'Start Sketch' })
+ ).toBeEnabled({
+ timeout: 20_000,
+ })
+
+ // gray at this pixel means the stream has loaded in the most
+ // user way we can verify it (pixel color)
+ await expect
+ .poll(() => u.getGreatestPixDiff(pointOnModel, [85, 85, 85]), {
+ timeout: 10_000,
+ })
+ .toBeLessThan(15)
+ })
+
+ await test.step('Clicking the logo takes us back to the projects page / home', async () => {
+ await page.getByTestId('app-logo').click()
+
+ await expect(page.getByRole('link', { name: 'bracket' })).toBeVisible()
+ await expect(page.getByText('empty')).toBeVisible()
+ await expect(page.getByText('bracket')).toBeVisible()
+ await expect(page.getByText('New Project')).toBeVisible()
+ })
+ await test.step('opening empty code project should clear the scene', async () => {
+ // Go back home.
+ await expect(page.getByText('empty')).toBeVisible()
+
+ await page.getByText('empty').click()
+
+ // Ensure the code is empty.
+ await expect(u.codeLocator).toContainText('')
+ expect(u.codeLocator.innerHTML.length).toBeLessThan(2)
+
+ // planes colors means the scene has been cleared.
+ await expect
+ .poll(() => u.getGreatestPixDiff(pointOnModel, [92, 53, 53]), {
+ timeout: 10_000,
+ })
+ .toBeLessThan(15)
+ })
+
+ await electronApp.close()
+ }
+)
+
+test(
+ 'open a file in a project works and renders, open empty file, it should clear the scene',
+ { tag: '@electron' },
+ async ({ browserName }, testInfo) => {
+ const { electronApp, page } = await setupElectron({
+ testInfo,
+ folderSetupFn: async (dir) => {
+ const bracketDir = join(dir, 'bracket')
+ await fsp.mkdir(bracketDir, { recursive: true })
+ await fsp.copyFile(
+ executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
+ join(bracketDir, 'main.kcl')
+ )
+
+ await fsp.writeFile(join(bracketDir, 'empty.kcl'), '')
+ },
+ })
+
+ await page.setViewportSize({ width: 1200, height: 500 })
+ const u = await getUtils(page)
+
+ page.on('console', console.log)
+
+ const pointOnModel = { x: 630, y: 280 }
+
+ await test.step('Opening the bracket project should load the stream', async () => {
+ // expect to see the text bracket
+ await expect(page.getByText('bracket')).toBeVisible()
+
+ await page.getByText('bracket').click()
+
+ await expect(page.getByTestId('loading')).toBeAttached()
+ await expect(page.getByTestId('loading')).not.toBeAttached({
+ timeout: 20_000,
+ })
+
+ await expect(
+ page.getByRole('button', { name: 'Start Sketch' })
+ ).toBeEnabled({
+ timeout: 20_000,
+ })
+
+ // gray at this pixel means the stream has loaded in the most
+ // user way we can verify it (pixel color)
+ await expect
+ .poll(() => u.getGreatestPixDiff(pointOnModel, [85, 85, 85]), {
+ timeout: 10_000,
+ })
+ .toBeLessThan(15)
+ })
+ await test.step('creating a empty file should clear the scene', async () => {
+ // open the file pane.
+ await page.getByTestId('files-pane-button').click()
+
+ // OPen the other file.
+ const file = page.getByRole('button', { name: 'empty.kcl' })
+ await expect(file).toBeVisible()
+
+ await file.click()
+
+ // planes colors means the scene has been cleared.
+ await expect
+ .poll(() => u.getGreatestPixDiff(pointOnModel, [92, 53, 53]), {
+ timeout: 10_000,
+ })
+ .toBeLessThan(15)
+
+ // Ensure the code is empty.
+ await expect(u.codeLocator).toContainText('')
+ expect(u.codeLocator.innerHTML.length).toBeLessThan(2)
+ })
+
+ await electronApp.close()
+ }
+)
+
+test(
+ 'open a file in a project works and renders, open another file in the same project with errors, it should clear the scene',
+ { tag: '@electron' },
+ async ({ browserName }, testInfo) => {
+ const { electronApp, page } = await setupElectron({
+ testInfo,
+ folderSetupFn: async (dir) => {
+ const bracketDir = join(dir, 'bracket')
+ await fsp.mkdir(bracketDir, { recursive: true })
+ await fsp.copyFile(
+ executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
+ join(bracketDir, 'main.kcl')
+ )
+ await fsp.copyFile(
+ executorInputPath('broken-code-test.kcl'),
+ join(bracketDir, 'broken-code-test.kcl')
+ )
+ },
+ })
+
+ await page.setViewportSize({ width: 1200, height: 500 })
+ const u = await getUtils(page)
+
+ page.on('console', console.log)
+
+ const pointOnModel = { x: 630, y: 280 }
+
+ await test.step('Opening the bracket project should load the stream', async () => {
+ // expect to see the text bracket
+ await expect(page.getByText('bracket')).toBeVisible()
+
+ await page.getByText('bracket').click()
+
+ await expect(page.getByTestId('loading')).toBeAttached()
+ await expect(page.getByTestId('loading')).not.toBeAttached({
+ timeout: 20_000,
+ })
+
+ await expect(
+ page.getByRole('button', { name: 'Start Sketch' })
+ ).toBeEnabled({
+ timeout: 20_000,
+ })
+
+ // gray at this pixel means the stream has loaded in the most
+ // user way we can verify it (pixel color)
+ await expect
+ .poll(() => u.getGreatestPixDiff(pointOnModel, [85, 85, 85]), {
+ timeout: 10_000,
+ })
+ .toBeLessThan(15)
+ })
+ await test.step('opening broken code file should clear the scene and show the error', async () => {
+ // open the file pane.
+ await page.getByTestId('files-pane-button').click()
+
+ // OPen the other file.
+ const file = page.getByRole('button', { name: 'broken-code-test.kcl' })
+ await expect(file).toBeVisible()
+
+ await file.click()
+
+ // error in guter
+ await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
+
+ // error text on hover
+ await page.hover('.cm-lint-marker-error')
+ const crypticErrorText = `Expected a tag declarator`
+ await expect(page.getByText(crypticErrorText).first()).toBeVisible()
+
+ // black pixel means the scene has been cleared.
+ await expect
+ .poll(() => u.getGreatestPixDiff(pointOnModel, [30, 30, 30]), {
+ timeout: 10_000,
+ })
+ .toBeLessThan(15)
+ })
+
+ await electronApp.close()
+ }
+)
+
test(
'when code with error first loads you get errors in console',
{ tag: '@electron' },
diff --git a/src/components/FileTree.tsx b/src/components/FileTree.tsx
index de7a3f77a..bc6a9bc78 100644
--- a/src/components/FileTree.tsx
+++ b/src/components/FileTree.tsx
@@ -266,6 +266,7 @@ const FileTreeItem = ({
// Let the lsp servers know we closed a file.
onFileClose(currentFile?.path || null, project?.path || null)
onFileOpen(fileOrDir.path, project?.path || null)
+ kclManager.switchedFiles = true
// Open kcl files
navigate(`${PATHS.FILE}/${encodeURIComponent(fileOrDir.path)}`)
diff --git a/src/components/ModelingSidebar/ModelingPanes/KclEditorMenu.tsx b/src/components/ModelingSidebar/ModelingPanes/KclEditorMenu.tsx
index 44a6dd4cf..b395f5f3a 100644
--- a/src/components/ModelingSidebar/ModelingPanes/KclEditorMenu.tsx
+++ b/src/components/ModelingSidebar/ModelingPanes/KclEditorMenu.tsx
@@ -40,7 +40,9 @@ export const KclEditorMenu = ({ children }: PropsWithChildren) => {