Compare commits
	
		
			47 Commits
		
	
	
		
			lf94/emerg
			...
			jtran/y-co
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| b4fb903bd0 | |||
| 1b8688f274 | |||
| 397839da84 | |||
| ac120838e5 | |||
| e6a2ac9c4a | |||
| 6e7e6e96cf | |||
| 73e155d79b | |||
| a782f26ec2 | |||
| 01076c3aed | |||
| fe512611ac | |||
| cba953c245 | |||
| 54ca6ea0b2 | |||
| 6a01608c3a | |||
| 530f15e04a | |||
| 725e59d987 | |||
| 54313c9b03 | |||
| 890d96496c | |||
| 35999366a7 | |||
| 2affc7271d | |||
| d30fbf8b4b | |||
| 3f7e776464 | |||
| 79cff57f43 | |||
| 1cd2cd82b2 | |||
| 60e187bd3e | |||
| c64175425b | |||
| 36464e6984 | |||
| 2f0002e53c | |||
| 482833c88f | |||
| d9d0a72306 | |||
| 65cd9fab64 | |||
| 5e41e382ce | |||
| 1e3cb00092 | |||
| d1a2bd01ca | |||
| aca13d087b | |||
| fcdde3e482 | |||
| a1df3d0ffc | |||
| 1852e6167b | |||
| 29bf77bb82 | |||
| e81b614523 | |||
| 5a5fe3bb95 | |||
| 0710f6e5f2 | |||
| c9d5633647 | |||
| f9419a98b5 | |||
| 999f72bccf | |||
| 9dbe74e008 | |||
| 88d9cdc52b | |||
| 2dd1f0f213 | 
							
								
								
									
										37
									
								
								.github/ISSUE_TEMPLATE/cryptic_error.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,37 @@ | ||||
| name: Cryptic KCL Error | ||||
| description: File a bug report for source code that produces a confusing error | ||||
| title: "[CRYPTIC]: " | ||||
| labels: ["cryptic-error"] | ||||
| assignees: [] | ||||
| body: | ||||
|   - type: markdown | ||||
|     attributes: | ||||
|       value: "Thank you for taking the time to report a confusing error. Please provide as much information as possible to help us resolve it." | ||||
|  | ||||
|   - type: textarea | ||||
|     id: kcl | ||||
|     attributes: | ||||
|       label: Paste minimal KCL source that produces a cryptic error | ||||
|       description: Minimal KCL reproducer that produces a cryptic error | ||||
|       placeholder: "const ..." | ||||
|       render: javascript | ||||
|     validations: | ||||
|       required: true | ||||
|  | ||||
|   - type: textarea | ||||
|     id: expected-behavior | ||||
|     attributes: | ||||
|       label: Expected Behavior | ||||
|       description: Description of what you expected to happen (if you know). | ||||
|       placeholder: "I expected that..." | ||||
|     validations: | ||||
|       required: false | ||||
|  | ||||
|   - type: textarea | ||||
|     id: additional-context | ||||
|     attributes: | ||||
|       label: Additional Context | ||||
|       description: Add any other context about the problem here. | ||||
|       placeholder: "Anything else you want to add..." | ||||
|     validations: | ||||
|       required: false | ||||
							
								
								
									
										38
									
								
								README.md
									
									
									
									
									
								
							
							
						
						| @ -124,20 +124,40 @@ Before you submit a contribution PR to this repo, please ensure that: | ||||
|  | ||||
| ## Release a new version | ||||
|  | ||||
| 1. Bump the versions by running `./make-realease.sh` while on a fresh pull of main | ||||
| #### 1. Bump the versions by running `./make-release.sh` and create a Cut Release PR | ||||
|  | ||||
| That will create the branch with the updated json files for you. | ||||
| run `./make-release.sh` for a patch update | ||||
| run `./make-release.sh "minor"` for minor | ||||
| run `./make-release.sh "major"` for major | ||||
| That will create the branch with the updated json files for you: | ||||
| - run `./make-release.sh` or `./make-release.sh patch` for a patch update; | ||||
| - run `./make-release.sh minor` for minor; or | ||||
| - run `./make-release.sh major` for major. | ||||
|  | ||||
| After it runs you should just need to push the push the branch and open a PR (it will suggest a changelog for you too, delete any that are not user facing) | ||||
| After it runs you should just need the push the branch and open a PR. | ||||
|  | ||||
| The PR may serve as a place to discuss the human-readable changelog and extra QA.  | ||||
| **Important:** It needs to be prefixed with `Cut release v` to build in release mode and a few other things to test in the best context possible, the intent would be for instance to have `Cut release v1.2.3` for the `v1.2.3` release candidate. | ||||
|  | ||||
| 2. Merge the PR | ||||
| The PR may then serve as a place to discuss the human-readable changelog and extra QA. The `make-release.sh` tool suggests a changelog for you too to be used as PR description, just make sure to delete lines that are not user facing. | ||||
|  | ||||
| #### 2. Smoke test artifacts from the Cut Release PR | ||||
|  | ||||
| The release builds can be find under the `artifact` zip, at the very bottom of the `ci` action page for each commit on this branch. | ||||
|  | ||||
| We don't have a strict process, but click around and check for anything obvious, posting results as comments in the Cut Release PR. | ||||
|  | ||||
| The other `ci` output in Cut Release PRs is `updater-test`, because we don't have a way to test this fully automated, we have a semi-automated process. Download updater-test zip file, install the app, run it, expect an updater prompt to a dummy v0.99.99, install it and check that the app comes back at that version (on both macOS and Windows). | ||||
|  | ||||
| #### 3. Merge the Cut Release PR | ||||
|  | ||||
| This will kick the `create-release` action, that creates a _Draft_ release out of this Cut Release PR merge after less than a minute, with the new version as title and Cut Release PR as description. | ||||
|  | ||||
|  | ||||
| #### 4. Publish the release | ||||
|  | ||||
| Head over to https://github.com/KittyCAD/modeling-app/releases, the draft release corresponding to the merged Cut Release PR should show up at the top as _Draft_. Click on it, verify the content, and hit _Publish_. | ||||
|  | ||||
| #### 5. Profit | ||||
|  | ||||
| A new Action kicks in at https://github.com/KittyCAD/modeling-app/actions, which can be found under `release` event filter. | ||||
|  | ||||
| 3. Profit (A new Action kicks in at https://github.com/KittyCAD/modeling-app/actions if the PR was correctly named) | ||||
|  | ||||
| ## Fuzzing the parser | ||||
|  | ||||
|  | ||||
| Before Width: | Height: | Size: 249 KiB After Width: | Height: | Size: 249 KiB | 
| Before Width: | Height: | Size: 249 KiB After Width: | Height: | Size: 249 KiB | 
| Before Width: | Height: | Size: 249 KiB After Width: | Height: | Size: 249 KiB | 
| Before Width: | Height: | Size: 171 KiB After Width: | Height: | Size: 171 KiB | 
| Before Width: | Height: | Size: 171 KiB After Width: | Height: | Size: 171 KiB | 
| Before Width: | Height: | Size: 171 KiB After Width: | Height: | Size: 171 KiB | 
| Before Width: | Height: | Size: 171 KiB After Width: | Height: | Size: 171 KiB | 
| Before Width: | Height: | Size: 249 KiB After Width: | Height: | Size: 249 KiB | 
| Before Width: | Height: | Size: 171 KiB After Width: | Height: | Size: 171 KiB | 
| Before Width: | Height: | Size: 171 KiB After Width: | Height: | Size: 171 KiB | 
| @ -3099,6 +3099,49 @@ const sketch002 = startSketchOn(extrude001, $seg01) | ||||
|     ).not.toBeDisabled() | ||||
|   }) | ||||
|  | ||||
|   test('Fillet button states test', async ({ page }) => { | ||||
|     const u = await getUtils(page) | ||||
|     await page.addInitScript(async () => { | ||||
|       localStorage.setItem( | ||||
|         'persistCode', | ||||
|         `const sketch001 = startSketchOn('XZ') | ||||
|   |> startProfileAt([-5, -5], %) | ||||
|   |> line([0, 10], %) | ||||
|   |> line([10, 0], %) | ||||
|   |> line([0, -10], %) | ||||
|   |> lineTo([profileStartX(%), profileStartY(%)], %) | ||||
|   |> close(%)` | ||||
|       ) | ||||
|     }) | ||||
|  | ||||
|     await page.setViewportSize({ width: 1000, height: 500 }) | ||||
|     await u.waitForAuthSkipAppStart() | ||||
|     await u.openDebugPanel() | ||||
|     await u.expectCmdLog('[data-message-type="execution-done"]') | ||||
|     await u.closeDebugPanel() | ||||
|  | ||||
|     const selectSegment = () => page.getByText(`line([10, 0], %)`).click() | ||||
|     const selectClose = () => page.getByText(`close(%)`).click() | ||||
|     const clickEmpty = () => page.mouse.click(950, 100) | ||||
|  | ||||
|     // expect fillet button without any bodies in the scene | ||||
|     await selectSegment() | ||||
|     await expect(page.getByRole('button', { name: 'Fillet' })).toBeDisabled() | ||||
|     await clickEmpty() | ||||
|     await expect(page.getByRole('button', { name: 'Fillet' })).toBeDisabled() | ||||
|  | ||||
|     // test fillet button with the body in the scene | ||||
|     const codeToAdd = `${await u.codeLocator.allInnerTexts()} | ||||
| const extrude001 = extrude(10, sketch001)` | ||||
|     await u.codeLocator.fill(codeToAdd) | ||||
|     await selectSegment() | ||||
|     await expect(page.getByRole('button', { name: 'Fillet' })).toBeEnabled() | ||||
|     await selectClose() | ||||
|     await expect(page.getByRole('button', { name: 'Fillet' })).toBeDisabled() | ||||
|     await clickEmpty() | ||||
|     await expect(page.getByRole('button', { name: 'Fillet' })).toBeEnabled() | ||||
|   }) | ||||
|  | ||||
|   const removeAfterFirstParenthesis = (inputString: string) => { | ||||
|     const index = inputString.indexOf('(') | ||||
|     if (index !== -1) { | ||||
| @ -3500,11 +3543,62 @@ test.describe('Command bar tests', () => { | ||||
|       `const extrude001 = extrude(${KCL_DEFAULT_LENGTH}, sketch001)` | ||||
|     ) | ||||
|   }) | ||||
|   test('Command bar works and can change a setting', async ({ page }) => { | ||||
|  | ||||
|   test('Fillet from command bar', async ({ page }) => { | ||||
|     await page.addInitScript(async () => { | ||||
|       localStorage.setItem( | ||||
|         'persistCode', | ||||
|         `const sketch001 = startSketchOn('XY') | ||||
|   |> startProfileAt([-5, -5], %) | ||||
|   |> line([0, 10], %) | ||||
|   |> line([10, 0], %) | ||||
|   |> line([0, -10], %) | ||||
|   |> lineTo([profileStartX(%), profileStartY(%)], %) | ||||
|   |> close(%) | ||||
| const extrude001 = extrude(-10, sketch001)` | ||||
|       ) | ||||
|     }) | ||||
|  | ||||
|     const u = await getUtils(page) | ||||
|     await page.setViewportSize({ width: 1000, height: 500 }) | ||||
|     await u.waitForAuthSkipAppStart() | ||||
|     await u.openDebugPanel() | ||||
|     await u.expectCmdLog('[data-message-type="execution-done"]') | ||||
|     await u.closeDebugPanel() | ||||
|  | ||||
|     const selectSegment = () => page.getByText(`line([0, -10], %)`).click() | ||||
|  | ||||
|     await selectSegment() | ||||
|     await page.waitForTimeout(100) | ||||
|     await page.getByRole('button', { name: 'Fillet' }).click() | ||||
|     await page.waitForTimeout(100) | ||||
|     await page.keyboard.press('Enter') | ||||
|     await page.waitForTimeout(100) | ||||
|     await page.keyboard.press('Enter') | ||||
|     await page.waitForTimeout(100) | ||||
|     await expect(page.locator('.cm-activeLine')).toContainText( | ||||
|       `fillet({ radius: ${KCL_DEFAULT_LENGTH}, tags: [seg01] }, %)` | ||||
|     ) | ||||
|   }) | ||||
|  | ||||
|   test('Command bar can change a setting, and switch back and forth between arguments', async ({ | ||||
|     page, | ||||
|   }) => { | ||||
|     const u = await getUtils(page) | ||||
|     await page.setViewportSize({ width: 1200, height: 500 }) | ||||
|     await u.waitForAuthSkipAppStart() | ||||
|  | ||||
|     const commandBarButton = page.getByRole('button', { name: 'Commands' }) | ||||
|     const cmdSearchBar = page.getByPlaceholder('Search commands') | ||||
|     const themeOption = page.getByRole('option', { | ||||
|       name: 'theme', | ||||
|       exact: false, | ||||
|     }) | ||||
|     const commandLevelArgButton = page.getByRole('button', { name: 'level' }) | ||||
|     const commandThemeArgButton = page.getByRole('button', { name: 'value' }) | ||||
|     // This selector changes after we set the setting | ||||
|     let commandOptionInput = page.getByPlaceholder('Select an option') | ||||
|  | ||||
|     await expect( | ||||
|       page.getByRole('button', { name: 'Start Sketch' }) | ||||
|     ).not.toBeDisabled() | ||||
| @ -3515,23 +3609,17 @@ test.describe('Command bar tests', () => { | ||||
|       .or(page.getByRole('button', { name: '⌘K' })) | ||||
|       .click() | ||||
|  | ||||
|     let cmdSearchBar = page.getByPlaceholder('Search commands') | ||||
|     await expect(cmdSearchBar).toBeVisible() | ||||
|     await page.keyboard.press('Escape') | ||||
|     cmdSearchBar = page.getByPlaceholder('Search commands') | ||||
|     await expect(cmdSearchBar).not.toBeVisible() | ||||
|  | ||||
|     // Now try the same, but with the keyboard shortcut, check focus | ||||
|     await page.keyboard.press('Meta+K') | ||||
|     cmdSearchBar = page.getByPlaceholder('Search commands') | ||||
|     await expect(cmdSearchBar).toBeVisible() | ||||
|     await expect(cmdSearchBar).toBeFocused() | ||||
|  | ||||
|     // Try typing in the command bar | ||||
|     await page.keyboard.type('theme') | ||||
|     const themeOption = page.getByRole('option', { | ||||
|       name: 'Settings · app · theme', | ||||
|     }) | ||||
|     await cmdSearchBar.fill('theme') | ||||
|     await expect(themeOption).toBeVisible() | ||||
|     await themeOption.click() | ||||
|     const themeInput = page.getByPlaceholder('Select an option') | ||||
| @ -3553,6 +3641,24 @@ test.describe('Command bar tests', () => { | ||||
|     ).toBeVisible() | ||||
|     // Check that the theme changed | ||||
|     await expect(page.locator('body')).not.toHaveClass(`body-bg dark`) | ||||
|  | ||||
|     commandOptionInput = page.getByPlaceholder('system') | ||||
|  | ||||
|     // Test case for https://github.com/KittyCAD/modeling-app/issues/2882 | ||||
|     await commandBarButton.click() | ||||
|     await cmdSearchBar.focus() | ||||
|     await cmdSearchBar.fill('theme') | ||||
|     await themeOption.click() | ||||
|     await expect(commandThemeArgButton).toBeDisabled() | ||||
|     await commandOptionInput.focus() | ||||
|     await commandOptionInput.fill('lig') | ||||
|     await commandLevelArgButton.click() | ||||
|     await expect(commandLevelArgButton).toBeDisabled() | ||||
|  | ||||
|     // Test case for https://github.com/KittyCAD/modeling-app/issues/2881 | ||||
|     await commandThemeArgButton.click() | ||||
|     await expect(commandThemeArgButton).toBeDisabled() | ||||
|     await expect(commandLevelArgButton).toHaveText('level: project') | ||||
|   }) | ||||
|  | ||||
|   test('Command bar keybinding works from code editor and can change a setting', async ({ | ||||
| @ -3577,7 +3683,7 @@ test.describe('Command bar tests', () => { | ||||
|     await expect(cmdSearchBar).toBeFocused() | ||||
|  | ||||
|     // Try typing in the command bar | ||||
|     await page.keyboard.type('theme') | ||||
|     await cmdSearchBar.fill('theme') | ||||
|     const themeOption = page.getByRole('option', { | ||||
|       name: 'Settings · app · theme', | ||||
|     }) | ||||
| @ -3648,7 +3754,9 @@ test.describe('Command bar tests', () => { | ||||
|     await page.mouse.click(700, 200) | ||||
|  | ||||
|     // Assert that we're on the distance step | ||||
|     await expect(page.getByRole('button', { name: 'distance' })).toBeDisabled() | ||||
|     await expect( | ||||
|       page.getByRole('button', { name: 'distance', exact: false }) | ||||
|     ).toBeDisabled() | ||||
|  | ||||
|     // Assert that the an alternative variable name is chosen, | ||||
|     // since the default variable name is already in use (distance) | ||||
| @ -3663,11 +3771,12 @@ test.describe('Command bar tests', () => { | ||||
|  | ||||
|     // Review step and argument hotkeys | ||||
|     await expect(submitButton).toBeEnabled() | ||||
|     await page.keyboard.press('Backspace') | ||||
|     await expect(submitButton).toBeFocused() | ||||
|     await submitButton.press('Backspace') | ||||
|  | ||||
|     // Assert we're back on the distance step | ||||
|     await expect( | ||||
|       page.getByRole('button', { name: 'Distance 5', exact: false }) | ||||
|       page.getByRole('button', { name: 'distance', exact: false }) | ||||
|     ).toBeDisabled() | ||||
|  | ||||
|     await continueButton.click() | ||||
| @ -3691,6 +3800,47 @@ const extrude001 = extrude(distance001, sketch001)`.replace( | ||||
|       ) // remove newlines | ||||
|     ) | ||||
|   }) | ||||
|  | ||||
|   test('Can switch between sketch tools via command bar', async ({ page }) => { | ||||
|     const u = await getUtils(page) | ||||
|     await page.setViewportSize({ width: 1200, height: 500 }) | ||||
|     await u.waitForAuthSkipAppStart() | ||||
|  | ||||
|     const sketchButton = page.getByRole('button', { name: 'Start Sketch' }) | ||||
|     const cmdBarButton = page.getByRole('button', { name: 'Commands' }) | ||||
|     const rectangleToolCommand = page.getByRole('option', { | ||||
|       name: 'Rectangle', | ||||
|     }) | ||||
|     const rectangleToolButton = page.getByRole('button', { name: 'Rectangle' }) | ||||
|     const lineToolCommand = page.getByRole('option', { name: 'Line' }) | ||||
|     const lineToolButton = page.getByRole('button', { name: 'Line' }) | ||||
|     const arcToolCommand = page.getByRole('option', { name: 'Tangential Arc' }) | ||||
|     const arcToolButton = page.getByRole('button', { name: 'Tangential Arc' }) | ||||
|  | ||||
|     // Start a sketch | ||||
|     await sketchButton.click() | ||||
|     await page.mouse.click(700, 200) | ||||
|  | ||||
|     // Switch between sketch tools via the command bar | ||||
|     await expect(lineToolButton).toHaveAttribute('aria-pressed', 'true') | ||||
|     await cmdBarButton.click() | ||||
|     await rectangleToolCommand.click() | ||||
|     await expect(rectangleToolButton).toHaveAttribute('aria-pressed', 'true') | ||||
|     await cmdBarButton.click() | ||||
|     await lineToolCommand.click() | ||||
|     await expect(lineToolButton).toHaveAttribute('aria-pressed', 'true') | ||||
|  | ||||
|     // Click in the scene a couple times to draw a line | ||||
|     // so tangential arc is valid | ||||
|     await page.mouse.click(700, 200) | ||||
|     await page.mouse.move(700, 300, { steps: 5 }) | ||||
|     await page.mouse.click(700, 300) | ||||
|  | ||||
|     // switch to tangential arc via command bar | ||||
|     await cmdBarButton.click() | ||||
|     await arcToolCommand.click() | ||||
|     await expect(arcToolButton).toHaveAttribute('aria-pressed', 'true') | ||||
|   }) | ||||
| }) | ||||
|  | ||||
| test.describe('Regression tests', () => { | ||||
| @ -4642,10 +4792,10 @@ test.describe('Sketch tests', () => { | ||||
|     // click extrude | ||||
|     await page.getByRole('button', { name: 'Extrude' }).click() | ||||
|  | ||||
|     // sketch selection should already have been made. "Selection 1 face" only show up when the selection has been made already | ||||
|     // sketch selection should already have been made. "Selection: 1 face" only show up when the selection has been made already | ||||
|     // otherwise the cmdbar would be waiting for a selection. | ||||
|     await expect( | ||||
|       page.getByRole('button', { name: 'Selection 1 face' }) | ||||
|       page.getByRole('button', { name: 'selection : 1 face', exact: false }) | ||||
|     ).toBeVisible() | ||||
|   }) | ||||
|   test("Existing sketch with bad code delete user's code", async ({ page }) => { | ||||
| @ -7071,6 +7221,7 @@ test.describe('Test network and connection issues', () => { | ||||
|  | ||||
|     // Expect the network to be up | ||||
|     await expect(page.getByText('Network Health (Connected)')).toBeVisible() | ||||
|     await expect(page.getByTestId('loading-stream')).not.toBeAttached() | ||||
|  | ||||
|     // Click off the code pane. | ||||
|     await page.mouse.click(100, 100) | ||||
|  | ||||
| Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 45 KiB | 
| Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 45 KiB | 
| Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 51 KiB | 
| Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 46 KiB | 
| Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB | 
| Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 37 KiB | 
| Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 40 KiB | 
| Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB | 
| Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB | 
| Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 72 KiB | 
| Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 45 KiB | 
| Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 62 KiB | 
| Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 30 KiB | 
| Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 33 KiB | 
| Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 33 KiB | 
| Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 32 KiB | 
| Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 36 KiB | 
| Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 30 KiB | 
| @ -16,14 +16,14 @@ export const TEST_COLORS = { | ||||
| } as const | ||||
|  | ||||
| async function waitForPageLoad(page: Page) { | ||||
|   // wait for 'Loading stream...' spinner | ||||
|   await page.getByTestId('loading-stream').waitFor() | ||||
|   // wait for all spinners to be gone | ||||
|   await page | ||||
|     .getByTestId('loading') | ||||
|     .waitFor({ state: 'detached', timeout: 20_000 }) | ||||
|   await expect(page.getByTestId('loading')).not.toBeAttached({ | ||||
|     timeout: 20_000, | ||||
|   }) | ||||
|  | ||||
|   await page.getByTestId('start-sketch').waitFor() | ||||
|   await expect(page.getByTestId('start-sketch')).toBeEnabled({ | ||||
|     timeout: 20_000, | ||||
|   }) | ||||
| } | ||||
|  | ||||
| async function removeCurrentCode(page: Page) { | ||||
| @ -471,8 +471,10 @@ export const doExport = async ( | ||||
|   page: Page | ||||
| ): Promise<Paths> => { | ||||
|   await page.getByRole('button', { name: APP_NAME }).click() | ||||
|   await expect(page.getByRole('button', { name: 'Export Part' })).toBeVisible() | ||||
|   await page.getByRole('button', { name: 'Export Part' }).click() | ||||
|   await expect( | ||||
|     page.getByRole('button', { name: 'Export', exact: false }) | ||||
|   ).toBeVisible() | ||||
|   await page.getByRole('button', { name: 'Export', exact: false }).click() | ||||
|   await expect(page.getByTestId('command-bar')).toBeVisible() | ||||
|  | ||||
|   // Go through export via command bar | ||||
|  | ||||
| @ -77,7 +77,7 @@ describe('ZMA authorized user flows', () => { | ||||
|     const menuButton = await $('[data-testid="user-sidebar-toggle"]') | ||||
|     await click(menuButton) | ||||
|  | ||||
|     const settingsButton = await $('[data-testid="settings-button"]') | ||||
|     const settingsButton = await $('[data-testid="user-settings"]') | ||||
|     await click(settingsButton) | ||||
|  | ||||
|     const projectDirInput = await $('[data-testid="project-directory-input"]') | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "untitled-app", | ||||
|   "version": "0.24.0", | ||||
|   "version": "0.24.3", | ||||
|   "private": true, | ||||
|   "dependencies": { | ||||
|     "@codemirror/autocomplete": "^6.17.0", | ||||
|  | ||||
| @ -30,7 +30,7 @@ import { URI } from 'vscode-uri' | ||||
| import { LanguageServerClient } from '../client' | ||||
| import { CompletionItemKindMap } from './autocomplete' | ||||
| import { addToken, SemanticToken } from './semantic-tokens' | ||||
| import { deferExecution, posToOffset, formatMarkdownContents } from './util' | ||||
| import { posToOffset, formatMarkdownContents } from './util' | ||||
| import lspAutocompleteExt from './autocomplete' | ||||
| import lspHoverExt from './hover' | ||||
| import lspFormatExt from './format' | ||||
| @ -93,23 +93,10 @@ export class LanguageServerPlugin implements PluginValue { | ||||
|   private doSemanticTokens: boolean = false | ||||
|   private doFoldingRanges: boolean = false | ||||
|  | ||||
|   private _defferer = deferExecution((code: string) => { | ||||
|     try { | ||||
|       // Update the state (not the editor) with the new code. | ||||
|       this.client.textDocumentDidChange({ | ||||
|         textDocument: { | ||||
|           uri: this.getDocUri(), | ||||
|           version: this.documentVersion++, | ||||
|         }, | ||||
|         contentChanges: [{ text: code }], | ||||
|       }) | ||||
|  | ||||
|       this.requestSemanticTokens() | ||||
|       this.updateFoldingRanges() | ||||
|     } catch (e) { | ||||
|       console.error(e) | ||||
|     } | ||||
|   }, this.changesDelay) | ||||
|   // When a doc update needs to be sent to the server, this holds the | ||||
|   // timeout handle for it. When null, the server has the up-to-date | ||||
|   // document. | ||||
|   private sendScheduled: number | null = null | ||||
|  | ||||
|   constructor(options: LanguageServerOptions, private view: EditorView) { | ||||
|     this.client = options.client | ||||
| @ -152,14 +139,9 @@ export class LanguageServerPlugin implements PluginValue { | ||||
|   } | ||||
|  | ||||
|   update(viewUpdate: ViewUpdate) { | ||||
|     // If the doc didn't change we can return early. | ||||
|     if (!viewUpdate.docChanged) { | ||||
|       return | ||||
|     if (viewUpdate.docChanged) { | ||||
|       this.scheduleSendDoc() | ||||
|     } | ||||
|  | ||||
|     this.sendChange({ | ||||
|       documentText: viewUpdate.state.doc.toString(), | ||||
|     }) | ||||
|   } | ||||
|  | ||||
|   destroy() { | ||||
| @ -184,16 +166,6 @@ export class LanguageServerPlugin implements PluginValue { | ||||
|     this.updateFoldingRanges() | ||||
|   } | ||||
|  | ||||
|   async sendChange({ documentText }: { documentText: string }) { | ||||
|     if (!this.client.ready) return | ||||
|  | ||||
|     this._defferer(documentText) | ||||
|   } | ||||
|  | ||||
|   requestDiagnostics() { | ||||
|     this.sendChange({ documentText: this.getDocText() }) | ||||
|   } | ||||
|  | ||||
|   async requestHoverTooltip( | ||||
|     view: EditorView, | ||||
|     { line, character }: { line: number; character: number } | ||||
| @ -204,7 +176,7 @@ export class LanguageServerPlugin implements PluginValue { | ||||
|     ) | ||||
|       return null | ||||
|  | ||||
|     this.sendChange({ documentText: this.getDocText() }) | ||||
|     this.ensureDocSent() | ||||
|     const result = await this.client.textDocumentHover({ | ||||
|       textDocument: { uri: this.getDocUri() }, | ||||
|       position: { line, character }, | ||||
| @ -227,6 +199,42 @@ export class LanguageServerPlugin implements PluginValue { | ||||
|     return { pos, end, create: (view) => ({ dom }), above: true } | ||||
|   } | ||||
|  | ||||
|   scheduleSendDoc() { | ||||
|     if (this.sendScheduled != null) window.clearTimeout(this.sendScheduled) | ||||
|     this.sendScheduled = window.setTimeout( | ||||
|       () => this.sendDoc(), | ||||
|       this.changesDelay | ||||
|     ) | ||||
|   } | ||||
|  | ||||
|   sendDoc() { | ||||
|     if (this.sendScheduled != null) { | ||||
|       window.clearTimeout(this.sendScheduled) | ||||
|       this.sendScheduled = null | ||||
|     } | ||||
|  | ||||
|     if (!this.client.ready) return | ||||
|     try { | ||||
|       // Update the state (not the editor) with the new code. | ||||
|       this.client.textDocumentDidChange({ | ||||
|         textDocument: { | ||||
|           uri: this.getDocUri(), | ||||
|           version: this.documentVersion++, | ||||
|         }, | ||||
|         contentChanges: [{ text: this.view.state.doc.toString() }], | ||||
|       }) | ||||
|  | ||||
|       this.requestSemanticTokens() | ||||
|       this.updateFoldingRanges() | ||||
|     } catch (e) { | ||||
|       console.error(e) | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   ensureDocSent() { | ||||
|     if (this.sendScheduled != null) this.sendDoc() | ||||
|   } | ||||
|  | ||||
|   async getFoldingRanges(): Promise<LSP.FoldingRange[] | null> { | ||||
|     if ( | ||||
|       !this.doFoldingRanges || | ||||
| @ -284,13 +292,7 @@ export class LanguageServerPlugin implements PluginValue { | ||||
|     ) | ||||
|       return null | ||||
|  | ||||
|     this.client.textDocumentDidChange({ | ||||
|       textDocument: { | ||||
|         uri: this.getDocUri(), | ||||
|         version: this.documentVersion++, | ||||
|       }, | ||||
|       contentChanges: [{ text: this.getDocText() }], | ||||
|     }) | ||||
|     this.ensureDocSent() | ||||
|  | ||||
|     const result = await this.client.textDocumentFormatting({ | ||||
|       textDocument: { uri: this.getDocUri() }, | ||||
| @ -330,9 +332,7 @@ export class LanguageServerPlugin implements PluginValue { | ||||
|     ) | ||||
|       return null | ||||
|  | ||||
|     this.sendChange({ | ||||
|       documentText: context.state.doc.toString(), | ||||
|     }) | ||||
|     this.ensureDocSent() | ||||
|  | ||||
|     const result = await this.client.textDocumentCompletion({ | ||||
|       textDocument: { uri: this.getDocUri() }, | ||||
|  | ||||
							
								
								
									
										138
									
								
								src-tauri/Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						| @ -332,7 +332,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.68", | ||||
|  "syn 2.0.71", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @ -361,13 +361,13 @@ checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799" | ||||
|  | ||||
| [[package]] | ||||
| name = "async-trait" | ||||
| version = "0.1.80" | ||||
| version = "0.1.81" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" | ||||
| checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.68", | ||||
|  "syn 2.0.71", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @ -407,7 +407,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.68", | ||||
|  "syn 2.0.71", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @ -550,7 +550,7 @@ dependencies = [ | ||||
|  "proc-macro-crate 3.1.0", | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.68", | ||||
|  "syn 2.0.71", | ||||
|  "syn_derive", | ||||
| ] | ||||
|  | ||||
| @ -792,9 +792,9 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "clap" | ||||
| version = "4.5.7" | ||||
| version = "4.5.9" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f" | ||||
| checksum = "64acc1846d54c1fe936a78dc189c34e28d3f5afc348403f28ecf53660b9b8462" | ||||
| dependencies = [ | ||||
|  "clap_builder", | ||||
|  "clap_derive", | ||||
| @ -802,9 +802,9 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "clap_builder" | ||||
| version = "4.5.7" | ||||
| version = "4.5.9" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f" | ||||
| checksum = "6fb8393d67ba2e7bfaf28a23458e4e2b543cc73a99595511eb207fdb8aede942" | ||||
| dependencies = [ | ||||
|  "anstream", | ||||
|  "anstyle", | ||||
| @ -816,14 +816,14 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "clap_derive" | ||||
| version = "4.5.5" | ||||
| version = "4.5.8" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6" | ||||
| checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085" | ||||
| dependencies = [ | ||||
|  "heck 0.5.0", | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.68", | ||||
|  "syn 2.0.71", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @ -1073,7 +1073,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" | ||||
| dependencies = [ | ||||
|  "quote", | ||||
|  "syn 2.0.68", | ||||
|  "syn 2.0.71", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @ -1083,7 +1083,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f" | ||||
| dependencies = [ | ||||
|  "quote", | ||||
|  "syn 2.0.68", | ||||
|  "syn 2.0.71", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @ -1107,7 +1107,7 @@ dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "strsim 0.10.0", | ||||
|  "syn 2.0.68", | ||||
|  "syn 2.0.71", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @ -1118,7 +1118,7 @@ checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" | ||||
| dependencies = [ | ||||
|  "darling_core", | ||||
|  "quote", | ||||
|  "syn 2.0.68", | ||||
|  "syn 2.0.71", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @ -1179,7 +1179,7 @@ checksum = "4078275de501a61ceb9e759d37bdd3d7210e654dbc167ac1a3678ef4435ed57b" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.68", | ||||
|  "syn 2.0.71", | ||||
|  "synstructure", | ||||
| ] | ||||
|  | ||||
| @ -1216,7 +1216,7 @@ dependencies = [ | ||||
|  "regex", | ||||
|  "serde", | ||||
|  "serde_tokenstream", | ||||
|  "syn 2.0.68", | ||||
|  "syn 2.0.71", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @ -1227,7 +1227,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.68", | ||||
|  "syn 2.0.71", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @ -1288,7 +1288,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.68", | ||||
|  "syn 2.0.71", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @ -1320,7 +1320,7 @@ checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.68", | ||||
|  "syn 2.0.71", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @ -1427,7 +1427,7 @@ checksum = "5c785274071b1b420972453b306eeca06acf4633829db4223b58a2a8c5953bc4" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.68", | ||||
|  "syn 2.0.71", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @ -1588,7 +1588,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.68", | ||||
|  "syn 2.0.71", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @ -1704,7 +1704,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.68", | ||||
|  "syn 2.0.71", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @ -1980,7 +1980,7 @@ dependencies = [ | ||||
|  "proc-macro-error", | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.68", | ||||
|  "syn 2.0.71", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @ -2008,7 +2008,7 @@ dependencies = [ | ||||
|  "inflections", | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.68", | ||||
|  "syn 2.0.71", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @ -2083,7 +2083,7 @@ dependencies = [ | ||||
|  "proc-macro-error", | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.68", | ||||
|  "syn 2.0.71", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @ -2571,7 +2571,7 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "kcl-lib" | ||||
| version = "0.1.70" | ||||
| version = "0.1.72" | ||||
| dependencies = [ | ||||
|  "anyhow", | ||||
|  "approx", | ||||
| @ -3377,7 +3377,7 @@ dependencies = [ | ||||
|  "regex", | ||||
|  "regex-syntax 0.8.3", | ||||
|  "structmeta", | ||||
|  "syn 2.0.68", | ||||
|  "syn 2.0.71", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @ -3496,7 +3496,7 @@ dependencies = [ | ||||
|  "phf_shared 0.11.2", | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.68", | ||||
|  "syn 2.0.71", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @ -3564,7 +3564,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.68", | ||||
|  "syn 2.0.71", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @ -4438,7 +4438,7 @@ dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "serde_derive_internals", | ||||
|  "syn 2.0.68", | ||||
|  "syn 2.0.71", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @ -4523,9 +4523,9 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "serde" | ||||
| version = "1.0.203" | ||||
| version = "1.0.204" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" | ||||
| checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" | ||||
| dependencies = [ | ||||
|  "serde_derive", | ||||
| ] | ||||
| @ -4552,13 +4552,13 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "serde_derive" | ||||
| version = "1.0.203" | ||||
| version = "1.0.204" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" | ||||
| checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.68", | ||||
|  "syn 2.0.71", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @ -4569,7 +4569,7 @@ checksum = "330f01ce65a3a5fe59a60c82f3c9a024b573b8a6e875bd233fe5f934e71d54e3" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.68", | ||||
|  "syn 2.0.71", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @ -4602,7 +4602,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.68", | ||||
|  "syn 2.0.71", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @ -4623,7 +4623,7 @@ dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "serde", | ||||
|  "syn 2.0.68", | ||||
|  "syn 2.0.71", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @ -4665,7 +4665,7 @@ dependencies = [ | ||||
|  "darling", | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.68", | ||||
|  "syn 2.0.71", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @ -4933,7 +4933,7 @@ dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "structmeta-derive", | ||||
|  "syn 2.0.68", | ||||
|  "syn 2.0.71", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @ -4944,7 +4944,7 @@ checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.68", | ||||
|  "syn 2.0.71", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @ -4966,7 +4966,7 @@ dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "rustversion", | ||||
|  "syn 2.0.68", | ||||
|  "syn 2.0.71", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @ -4999,9 +4999,9 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "syn" | ||||
| version = "2.0.68" | ||||
| version = "2.0.71" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" | ||||
| checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
| @ -5017,7 +5017,7 @@ dependencies = [ | ||||
|  "proc-macro-error", | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.68", | ||||
|  "syn 2.0.71", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @ -5034,7 +5034,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.68", | ||||
|  "syn 2.0.71", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @ -5251,7 +5251,7 @@ dependencies = [ | ||||
|  "serde", | ||||
|  "serde_json", | ||||
|  "sha2", | ||||
|  "syn 2.0.68", | ||||
|  "syn 2.0.71", | ||||
|  "tauri-utils", | ||||
|  "thiserror", | ||||
|  "time", | ||||
| @ -5269,7 +5269,7 @@ dependencies = [ | ||||
|  "heck 0.5.0", | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.68", | ||||
|  "syn 2.0.71", | ||||
|  "tauri-codegen", | ||||
|  "tauri-utils", | ||||
| ] | ||||
| @ -5627,22 +5627,22 @@ checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" | ||||
|  | ||||
| [[package]] | ||||
| name = "thiserror" | ||||
| version = "1.0.61" | ||||
| version = "1.0.62" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" | ||||
| checksum = "f2675633b1499176c2dff06b0856a27976a8f9d436737b4cf4f312d4d91d8bbb" | ||||
| dependencies = [ | ||||
|  "thiserror-impl", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| name = "thiserror-impl" | ||||
| version = "1.0.61" | ||||
| version = "1.0.62" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" | ||||
| checksum = "d20468752b09f49e909e55a5d338caa8bedf615594e9d80bc4c565d30faf798c" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.68", | ||||
|  "syn 2.0.71", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @ -5740,7 +5740,7 @@ checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.68", | ||||
|  "syn 2.0.71", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @ -5940,7 +5940,7 @@ checksum = "84fd902d4e0b9a4b27f2f440108dc034e1758628a9b702f8ec61ad66355422fa" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.68", | ||||
|  "syn 2.0.71", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @ -5969,7 +5969,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.68", | ||||
|  "syn 2.0.71", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @ -6099,7 +6099,7 @@ checksum = "c88cc88fd23b5a04528f3a8436024f20010a16ec18eb23c164b1242f65860130" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.68", | ||||
|  "syn 2.0.71", | ||||
|  "termcolor", | ||||
| ] | ||||
|  | ||||
| @ -6280,9 +6280,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" | ||||
|  | ||||
| [[package]] | ||||
| name = "uuid" | ||||
| version = "1.9.1" | ||||
| version = "1.10.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "5de17fd2f7da591098415cff336e12965a28061ddace43b59cb3c430179c9439" | ||||
| checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" | ||||
| dependencies = [ | ||||
|  "getrandom 0.2.14", | ||||
|  "serde", | ||||
| @ -6316,7 +6316,7 @@ dependencies = [ | ||||
|  "proc-macro-error", | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.68", | ||||
|  "syn 2.0.71", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @ -6415,7 +6415,7 @@ dependencies = [ | ||||
|  "once_cell", | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.68", | ||||
|  "syn 2.0.71", | ||||
|  "wasm-bindgen-shared", | ||||
| ] | ||||
|  | ||||
| @ -6449,7 +6449,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.68", | ||||
|  "syn 2.0.71", | ||||
|  "wasm-bindgen-backend", | ||||
|  "wasm-bindgen-shared", | ||||
| ] | ||||
| @ -6590,7 +6590,7 @@ checksum = "ac1345798ecd8122468840bcdf1b95e5dc6d2206c5e4b0eafa078d061f59c9bc" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.68", | ||||
|  "syn 2.0.71", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @ -6696,7 +6696,7 @@ checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.68", | ||||
|  "syn 2.0.71", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @ -6707,7 +6707,7 @@ checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.68", | ||||
|  "syn 2.0.71", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @ -7159,7 +7159,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.68", | ||||
|  "syn 2.0.71", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
|  | ||||
| @ -80,5 +80,5 @@ | ||||
|     } | ||||
|   }, | ||||
|   "productName": "Zoo Modeling App", | ||||
|   "version": "0.24.0" | ||||
|   "version": "0.24.3" | ||||
| } | ||||
|  | ||||
| @ -44,7 +44,7 @@ export function App() { | ||||
|   }, [projectName, projectPath]) | ||||
|  | ||||
|   useHotKeyListener() | ||||
|   const { context } = useModelingContext() | ||||
|   const { context, state } = useModelingContext() | ||||
|  | ||||
|   const { auth, settings } = useSettingsAuthContext() | ||||
|   const token = auth?.context?.token | ||||
| @ -57,7 +57,6 @@ export function App() { | ||||
|   const { | ||||
|     app: { onboardingStatus }, | ||||
|   } = settings.context | ||||
|   const { state } = useModelingContext() | ||||
|  | ||||
|   useHotkeys('backspace', (e) => { | ||||
|     e.preventDefault() | ||||
|  | ||||
| @ -39,3 +39,32 @@ export const AppStateProvider = ({ children }: { children: ReactNode }) => { | ||||
|     </AppStateContext.Provider> | ||||
|   ) | ||||
| } | ||||
|  | ||||
| interface AppStream { | ||||
|   mediaStream: MediaStream | ||||
|   setMediaStream: (mediaStream: MediaStream) => void | ||||
| } | ||||
|  | ||||
| const AppStreamContext = createContext<AppStream>({ | ||||
|   mediaStream: undefined as unknown as MediaStream, | ||||
|   setMediaStream: () => {}, | ||||
| }) | ||||
|  | ||||
| export const useAppStream = () => useContext(AppStreamContext) | ||||
|  | ||||
| export const AppStreamProvider = ({ children }: { children: ReactNode }) => { | ||||
|   const [mediaStream, setMediaStream] = useState<MediaStream>( | ||||
|     undefined as unknown as MediaStream | ||||
|   ) | ||||
|  | ||||
|   return ( | ||||
|     <AppStreamContext.Provider | ||||
|       value={{ | ||||
|         mediaStream, | ||||
|         setMediaStream, | ||||
|       }} | ||||
|     > | ||||
|       {children} | ||||
|     </AppStreamContext.Provider> | ||||
|   ) | ||||
| } | ||||
|  | ||||
| @ -16,6 +16,7 @@ import { | ||||
|   canRectangleTool, | ||||
|   isEditingExistingSketch, | ||||
| } from 'machines/modelingMachine' | ||||
| import { DEV } from 'env' | ||||
|  | ||||
| export function Toolbar({ | ||||
|   className = '', | ||||
| @ -60,7 +61,7 @@ export function Toolbar({ | ||||
|         ? send('CancelSketch') | ||||
|         : send({ | ||||
|             type: 'change tool', | ||||
|             data: 'line', | ||||
|             data: { tool: 'line' }, | ||||
|           }), | ||||
|     { enabled: !disableLineButton, scopes: ['sketch'] } | ||||
|   ) | ||||
| @ -75,7 +76,7 @@ export function Toolbar({ | ||||
|         ? send('CancelSketch') | ||||
|         : send({ | ||||
|             type: 'change tool', | ||||
|             data: 'tangentialArc', | ||||
|             data: { tool: 'tangentialArc' }, | ||||
|           }), | ||||
|     { enabled: !disableTangentialArc, scopes: ['sketch'] } | ||||
|   ) | ||||
| @ -89,7 +90,7 @@ export function Toolbar({ | ||||
|         ? send('CancelSketch') | ||||
|         : send({ | ||||
|             type: 'change tool', | ||||
|             data: 'rectangle', | ||||
|             data: { tool: 'rectangle' }, | ||||
|           }), | ||||
|     { enabled: !disableRectangle, scopes: ['sketch'] } | ||||
|   ) | ||||
| @ -114,10 +115,20 @@ export function Toolbar({ | ||||
|     () => | ||||
|       commandBarSend({ | ||||
|         type: 'Find and select command', | ||||
|         data: { name: 'Extrude', ownerMachine: 'modeling' }, | ||||
|         data: { name: 'Extrude', groupId: 'modeling' }, | ||||
|       }), | ||||
|     { enabled: !disableAllButtons, scopes: ['modeling'] } | ||||
|   ) | ||||
|   const disableFillet = !state.can('Fillet') || disableAllButtons | ||||
|   useHotkeys( | ||||
|     'f', | ||||
|     () => | ||||
|       commandBarSend({ | ||||
|         type: 'Find and select command', | ||||
|         data: { name: 'Fillet', groupId: 'modeling' }, | ||||
|       }), | ||||
|     { enabled: !disableFillet, scopes: ['modeling'] } | ||||
|   ) | ||||
|  | ||||
|   function handleToolbarButtonsWheelEvent(ev: WheelEvent<HTMLSpanElement>) { | ||||
|     const span = toolbarButtonsRef.current | ||||
| @ -263,7 +274,7 @@ export function Toolbar({ | ||||
|                     ? send('CancelSketch') | ||||
|                     : send({ | ||||
|                         type: 'change tool', | ||||
|                         data: 'line', | ||||
|                         data: { tool: 'line' }, | ||||
|                       }) | ||||
|                 } | ||||
|                 aria-pressed={state?.matches('Sketch.Line tool')} | ||||
| @ -293,7 +304,7 @@ export function Toolbar({ | ||||
|                     ? send('CancelSketch') | ||||
|                     : send({ | ||||
|                         type: 'change tool', | ||||
|                         data: 'tangentialArc', | ||||
|                         data: { tool: 'tangentialArc' }, | ||||
|                       }) | ||||
|                 } | ||||
|                 aria-pressed={state.matches('Sketch.Tangential arc to')} | ||||
| @ -323,7 +334,7 @@ export function Toolbar({ | ||||
|                     ? send('CancelSketch') | ||||
|                     : send({ | ||||
|                         type: 'change tool', | ||||
|                         data: 'rectangle', | ||||
|                         data: { tool: 'rectangle' }, | ||||
|                       }) | ||||
|                 } | ||||
|                 aria-pressed={state.matches('Sketch.Rectangle tool')} | ||||
| @ -378,7 +389,7 @@ export function Toolbar({ | ||||
|               onClick={() => | ||||
|                 commandBarSend({ | ||||
|                   type: 'Find and select command', | ||||
|                   data: { name: 'Extrude', ownerMachine: 'modeling' }, | ||||
|                   data: { name: 'Extrude', groupId: 'modeling' }, | ||||
|                 }) | ||||
|               } | ||||
|               disabled={!state.can('Extrude') || disableAllButtons} | ||||
| @ -404,6 +415,36 @@ export function Toolbar({ | ||||
|             </ActionButton> | ||||
|           </li> | ||||
|         )} | ||||
|         {state.matches('idle') && (DEV || (window as any)._enableFillet) && ( | ||||
|           <li className="contents"> | ||||
|             <ActionButton | ||||
|               className={buttonClassName} | ||||
|               Element="button" | ||||
|               onClick={() => | ||||
|                 commandBarSend({ | ||||
|                   type: 'Find and select command', | ||||
|                   data: { name: 'Fillet', groupId: 'modeling' }, | ||||
|                 }) | ||||
|               } | ||||
|               disabled={disableFillet} | ||||
|               title={disableFillet ? 'fillet' : "edge can't be filleted"} | ||||
|               iconStart={{ | ||||
|                 icon: 'fillet', // todo: add fillet icon | ||||
|                 iconClassName, | ||||
|                 bgClassName, | ||||
|               }} | ||||
|             > | ||||
|               Fillet | ||||
|               <Tooltip | ||||
|                 delay={1250} | ||||
|                 position="bottom" | ||||
|                 className="!px-2 !text-xs" | ||||
|               > | ||||
|                 Shortcut: F | ||||
|               </Tooltip> | ||||
|             </ActionButton> | ||||
|           </li> | ||||
|         )} | ||||
|       </ul> | ||||
|     </menu> | ||||
|   ) | ||||
|  | ||||
| @ -47,7 +47,6 @@ import { | ||||
|   PipeExpression, | ||||
|   Program, | ||||
|   ProgramMemory, | ||||
|   programMemoryInit, | ||||
|   recast, | ||||
|   SketchGroup, | ||||
|   ExtrudeGroup, | ||||
| @ -130,7 +129,7 @@ export const HIDE_HOVER_SEGMENT_LENGTH = 60 // in pixels | ||||
| export class SceneEntities { | ||||
|   engineCommandManager: EngineCommandManager | ||||
|   scene: Scene | ||||
|   sceneProgramMemory: ProgramMemory = { root: {}, return: null } | ||||
|   sceneProgramMemory: ProgramMemory = ProgramMemory.empty() | ||||
|   activeSegments: { [key: string]: Group } = {} | ||||
|   intersectionPlane: Mesh | null = null | ||||
|   axisGroup: Group | null = null | ||||
| @ -550,9 +549,9 @@ export class SceneEntities { | ||||
|     const variableDeclarationName = | ||||
|       _node1.node?.declarations?.[0]?.id?.name || '' | ||||
|  | ||||
|     const sg = kclManager.programMemory.root[ | ||||
|     const sg = kclManager.programMemory.get( | ||||
|       variableDeclarationName | ||||
|     ] as SketchGroup | ||||
|     ) as SketchGroup | ||||
|     const lastSeg = sg.value.slice(-1)[0] || sg.start | ||||
|  | ||||
|     const index = sg.value.length // because we've added a new segment that's not in the memory yet, no need for `-1` | ||||
| @ -768,9 +767,9 @@ export class SceneEntities { | ||||
|           programMemoryOverride, | ||||
|         }) | ||||
|         this.sceneProgramMemory = programMemory | ||||
|         const sketchGroup = programMemory.root[ | ||||
|         const sketchGroup = programMemory.get( | ||||
|           variableDeclarationName | ||||
|         ] as SketchGroup | ||||
|         ) as SketchGroup | ||||
|         const sgPaths = sketchGroup.value | ||||
|         const orthoFactor = orthoScale(sceneInfra.camControls.camera) | ||||
|  | ||||
| @ -820,9 +819,9 @@ export class SceneEntities { | ||||
|  | ||||
|           // Prepare to update the THREEjs scene | ||||
|           this.sceneProgramMemory = programMemory | ||||
|           const sketchGroup = programMemory.root[ | ||||
|           const sketchGroup = programMemory.get( | ||||
|             variableDeclarationName | ||||
|           ] as SketchGroup | ||||
|           ) as SketchGroup | ||||
|           const sgPaths = sketchGroup.value | ||||
|           const orthoFactor = orthoScale(sceneInfra.camControls.camera) | ||||
|  | ||||
| @ -1081,9 +1080,9 @@ export class SceneEntities { | ||||
|       }) | ||||
|       this.sceneProgramMemory = programMemory | ||||
|  | ||||
|       const maybeSketchGroup = programMemory.root[variableDeclarationName] | ||||
|       const maybeSketchGroup = programMemory.get(variableDeclarationName) | ||||
|       let sketchGroup = undefined | ||||
|       if (maybeSketchGroup.type === 'SketchGroup') { | ||||
|       if (maybeSketchGroup?.type === 'SketchGroup') { | ||||
|         sketchGroup = maybeSketchGroup | ||||
|       } else if ((maybeSketchGroup as ExtrudeGroup).sketchGroup) { | ||||
|         sketchGroup = (maybeSketchGroup as ExtrudeGroup).sketchGroup | ||||
| @ -1773,7 +1772,7 @@ function prepareTruncatedMemoryAndAst( | ||||
|   if (err(_node)) return _node | ||||
|   const variableDeclarationName = _node.node?.declarations?.[0]?.id?.name || '' | ||||
|   const lastSeg = ( | ||||
|     programMemory.root[variableDeclarationName] as SketchGroup | ||||
|     programMemory.get(variableDeclarationName) as SketchGroup | ||||
|   ).value.slice(-1)[0] | ||||
|   if (draftSegment) { | ||||
|     // truncatedAst needs to setup with another segment at the end | ||||
| @ -1824,33 +1823,27 @@ function prepareTruncatedMemoryAndAst( | ||||
|     ..._ast, | ||||
|     body: [JSON.parse(JSON.stringify(_ast.body[bodyIndex]))], | ||||
|   } | ||||
|   const programMemoryOverride = programMemoryInit() | ||||
|   if (err(programMemoryOverride)) return programMemoryOverride | ||||
|  | ||||
|   // Grab all the TagDeclarators and TagIdentifiers from memory. | ||||
|   let start = _node.node.start | ||||
|   for (const key in programMemory.root) { | ||||
|     const value = programMemory.root[key] | ||||
|     if (!('__meta' in value)) { | ||||
|       continue | ||||
|     } | ||||
|   const programMemoryOverride = programMemory.filterVariables(true, (value) => { | ||||
|     if ( | ||||
|       !('__meta' in value) || | ||||
|       value.__meta === undefined || | ||||
|       value.__meta.length === 0 || | ||||
|       value.__meta[0].sourceRange === undefined | ||||
|     ) { | ||||
|       continue | ||||
|       return false | ||||
|     } | ||||
|  | ||||
|     if (value.__meta[0].sourceRange[0] >= start) { | ||||
|       // We only want things before our start point. | ||||
|       continue | ||||
|       return false | ||||
|     } | ||||
|  | ||||
|     if (value.type === 'TagIdentifier') { | ||||
|       programMemoryOverride.root[key] = JSON.parse(JSON.stringify(value)) | ||||
|     } | ||||
|   } | ||||
|     return value.type === 'TagIdentifier' | ||||
|   }) | ||||
|   if (err(programMemoryOverride)) return programMemoryOverride | ||||
|  | ||||
|   for (let i = 0; i < bodyIndex; i++) { | ||||
|     const node = _ast.body[i] | ||||
| @ -1858,12 +1851,15 @@ function prepareTruncatedMemoryAndAst( | ||||
|       continue | ||||
|     } | ||||
|     const name = node.declarations[0].id.name | ||||
|     // const memoryItem = kclManager.programMemory.root[name] | ||||
|     const memoryItem = programMemory.root[name] | ||||
|     const memoryItem = programMemory.get(name) | ||||
|     if (!memoryItem) { | ||||
|       continue | ||||
|     } | ||||
|     programMemoryOverride.root[name] = JSON.parse(JSON.stringify(memoryItem)) | ||||
|     const error = programMemoryOverride.set( | ||||
|       name, | ||||
|       JSON.parse(JSON.stringify(memoryItem)) | ||||
|     ) | ||||
|     if (err(error)) return error | ||||
|   } | ||||
|   return { | ||||
|     truncatedAst, | ||||
| @ -1900,7 +1896,7 @@ export function sketchGroupFromPathToNode({ | ||||
|   ) | ||||
|   if (err(_varDec)) return _varDec | ||||
|   const varDec = _varDec.node | ||||
|   const result = programMemory.root[varDec?.id?.name || ''] | ||||
|   const result = programMemory.get(varDec?.id?.name || '') | ||||
|   if (result?.type === 'ExtrudeGroup') { | ||||
|     return result.sketchGroup | ||||
|   } | ||||
|  | ||||
| @ -49,9 +49,9 @@ export const AppHeader = ({ | ||||
|           <> | ||||
|             <CommandBarOpenButton /> | ||||
|             <RefreshButton /> | ||||
|             <UserSidebarMenu user={user} /> | ||||
|           </> | ||||
|         )} | ||||
|         <UserSidebarMenu user={user} /> | ||||
|       </div> | ||||
|     </header> | ||||
|   ) | ||||
|  | ||||
