diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index 51ff3abbc..000000000 --- a/.eslintrc +++ /dev/null @@ -1,103 +0,0 @@ -{ - "parser": "@typescript-eslint/parser", - "parserOptions": { - "project": "./tsconfig.json" - }, - "plugins": [ - "react-perf", - "css-modules", - "jest", - "jsx-a11y", - "react", - "react-hooks", - "suggest-no-throw", - "testing-library", - "@typescript-eslint" - ], - "extends": [ - "plugin:css-modules/recommended", - "plugin:jsx-a11y/recommended", - "plugin:react-hooks/recommended" - ], - "rules": { - "no-array-constructor": "off", // This is wrong; use the @typescript-eslint one instead. - "@typescript-eslint/no-array-constructor": "error", - "@typescript-eslint/no-array-delete": "error", - "@typescript-eslint/no-duplicate-enum-values": "error", - "@typescript-eslint/no-duplicate-type-constituents": "error", - "@typescript-eslint/no-empty-object-type": "error", - "@typescript-eslint/no-extra-non-null-assertion": "error", - "@typescript-eslint/no-floating-promises": "error", - "@typescript-eslint/no-for-in-array": "error", - "no-implied-eval": "off", // This is wrong; use the @typescript-eslint one instead. - "@typescript-eslint/no-implied-eval": "error", - "@typescript-eslint/no-misused-new": "error", - "@typescript-eslint/no-misused-promises": "error", - "@typescript-eslint/no-namespace": "error", - "@typescript-eslint/no-non-null-asserted-optional-chain": "error", - "@typescript-eslint/no-redundant-type-constituents": "error", - "@typescript-eslint/no-this-alias": "warn", - "@typescript-eslint/no-unnecessary-type-assertion": "error", - "@typescript-eslint/no-unnecessary-type-constraint": "error", - "no-unused-vars": "off", // This is wrong; use the @typescript-eslint one instead. - "@typescript-eslint/no-unused-vars": ["error", { - "varsIgnorePattern": "^_", - "argsIgnorePattern": "^_", - "ignoreRestSiblings": true, - "vars": "all", - "args": "none" - }], - "@typescript-eslint/no-unsafe-unary-minus": "error", - "@typescript-eslint/no-wrapper-object-types": "error", - "no-throw-literal": "off", // Use @typescript-eslint/only-throw-error instead. - "@typescript-eslint/only-throw-error": "error", - "@typescript-eslint/prefer-as-const": "warn", - "@typescript-eslint/prefer-namespace-keyword": "error", - "@typescript-eslint/restrict-plus-operands": "error", - "jsx-a11y/click-events-have-key-events": "off", - "jsx-a11y/no-autofocus": "off", - "jsx-a11y/no-noninteractive-element-interactions": "off", - "no-restricted-globals": [ - "error", - { - "name": "isNaN", - "message": "Use Number.isNaN() instead." - } - ], - "no-restricted-syntax": [ - "error", - { - "selector": "CallExpression[callee.object.name='Array'][callee.property.name='isArray']", - "message": "Use isArray() in lib/utils.ts instead of Array.isArray()." - } - ], - "semi": [ - "error", - "never" - ], - "react-hooks/exhaustive-deps": "off", - "suggest-no-throw/suggest-no-throw": "error" - }, - "overrides": [ - { - "files": ["e2e/**/*.ts"], // Update the pattern based on your file structure - "extends": [ - "plugin:testing-library/react" - ], - "rules": { - "suggest-no-throw/suggest-no-throw": "off", - "testing-library/prefer-screen-queries": "off", - "jest/valid-expect": "off" - } - }, - { - "files": ["src/**/*.test.ts"], - "extends": [ - "plugin:testing-library/react" - ], - "rules": { - "suggest-no-throw/suggest-no-throw": "off" - } - } - ] -} diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 000000000..e60b0800e --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,127 @@ +{ + "parser": "@typescript-eslint/parser", + "parserOptions": { + "project": "./tsconfig.json" + }, + "plugins": [ + "react-perf", + "css-modules", + "jest", + "jsx-a11y", + "react", + "react-hooks", + "suggest-no-throw", + "testing-library", + "@typescript-eslint" + ], + "extends": [ + "plugin:css-modules/recommended", + "plugin:jsx-a11y/recommended", + "plugin:react-hooks/recommended" + ], + "rules": { + "no-array-constructor": "off", // This is wrong; use the @typescript-eslint one instead. + "@typescript-eslint/no-array-constructor": "error", + "@typescript-eslint/no-array-delete": "error", + "@typescript-eslint/no-duplicate-enum-values": "error", + "@typescript-eslint/no-duplicate-type-constituents": "error", + "@typescript-eslint/no-empty-object-type": "error", + "@typescript-eslint/no-extra-non-null-assertion": "error", + "@typescript-eslint/no-floating-promises": "error", + "@typescript-eslint/no-for-in-array": "error", + "no-implied-eval": "off", // This is wrong; use the @typescript-eslint one instead. + "@typescript-eslint/no-implied-eval": "error", + "@typescript-eslint/no-misused-new": "error", + "@typescript-eslint/no-misused-promises": "error", + "@typescript-eslint/no-namespace": "error", + "@typescript-eslint/no-non-null-asserted-optional-chain": "error", + "@typescript-eslint/no-redundant-type-constituents": "error", + "@typescript-eslint/no-this-alias": "warn", + "@typescript-eslint/no-unnecessary-type-assertion": "error", + "@typescript-eslint/no-unnecessary-type-constraint": "error", + "no-unused-vars": "off", // This is wrong; use the @typescript-eslint one instead. + "@typescript-eslint/no-unused-vars": [ + "error", + { + "varsIgnorePattern": "^_", + "argsIgnorePattern": "^_", + "ignoreRestSiblings": true, + "vars": "all", + "args": "none" + } + ], + "@typescript-eslint/no-unsafe-unary-minus": "error", + "@typescript-eslint/no-wrapper-object-types": "error", + "no-throw-literal": "off", // Use @typescript-eslint/only-throw-error instead. + "@typescript-eslint/only-throw-error": "error", + "@typescript-eslint/prefer-as-const": "warn", + "@typescript-eslint/prefer-namespace-keyword": "error", + "@typescript-eslint/consistent-type-imports": "error", + "@typescript-eslint/restrict-plus-operands": "error", + "jsx-a11y/click-events-have-key-events": "off", + "jsx-a11y/no-autofocus": "off", + "jsx-a11y/no-noninteractive-element-interactions": "off", + "no-restricted-globals": [ + "error", + { + "name": "isNaN", + "message": "Use Number.isNaN() instead." + } + ], + "no-restricted-syntax": [ + "error", + { + "selector": "CallExpression[callee.object.name='Array'][callee.property.name='isArray']", + "message": "Use isArray() in lib/utils.ts instead of Array.isArray()." + }, + { + "selector": "CallExpression[callee.object.name='TOML'][callee.property.name='stringify']", + "message": "Do not use TOML.stringify directly. Use the wrappers in test-utils instead like settingsToToml." + }, + { + "selector": "CallExpression[callee.object.name='TOML'][callee.property.name='parse']", + "message": "Do not use TOML.parse directly. Use the wrappers in test-utils instead like tomlToSettings." + } + ], + "no-restricted-imports": [ + "error", + { + "patterns": [ + // Restrict all relative imports except for .css files. + { + "group": ["./*", "../*", "!./*.css", "!../*.css"], + "message": "Use absolute imports instead." + } + ] + } + ], + "semi": ["error", "never"], + "react-hooks/exhaustive-deps": "off", + "suggest-no-throw/suggest-no-throw": "error" + }, + "overrides": [ + { + "files": ["e2e/**/*.ts"], // Update the pattern based on your file structure + "extends": ["plugin:testing-library/react"], + "rules": { + "suggest-no-throw/suggest-no-throw": "off", + "testing-library/prefer-screen-queries": "off", + "jest/valid-expect": "off" + } + }, + { + "files": ["src/**/*.test.ts"], + "extends": ["plugin:testing-library/react"], + "rules": { + "suggest-no-throw/suggest-no-throw": "off" + } + }, + { + "files": ["packages/**/*.ts", "rust/**/*.ts"], + "extends": [], + "rules": { + "no-restricted-imports": "off" + } + } + ] +} diff --git a/docs/kcl/scale.md b/docs/kcl/scale.md index c3d477fac..1f5b21f9f 100644 --- a/docs/kcl/scale.md +++ b/docs/kcl/scale.md @@ -17,9 +17,9 @@ If you want to apply the transform in global space, set `global` to `true`. The ```js scale( objects: SolidOrSketchOrImportedGeometry, - x: number, - y: number, - z: number, + x?: number, + y?: number, + z?: number, global?: bool, ): SolidOrSketchOrImportedGeometry ``` @@ -30,9 +30,9 @@ scale( | Name | Type | Description | Required | |----------|------|-------------|----------| | `objects` | [`SolidOrSketchOrImportedGeometry`](/docs/kcl/types/SolidOrSketchOrImportedGeometry) | The solid, sketch, or set of solids or sketches to scale. | Yes | -| `x` | [`number`](/docs/kcl/types/number) | The scale factor for the x axis. | Yes | -| `y` | [`number`](/docs/kcl/types/number) | The scale factor for the y axis. | Yes | -| `z` | [`number`](/docs/kcl/types/number) | The scale factor for the z axis. | Yes | +| `x` | [`number`](/docs/kcl/types/number) | The scale factor for the x axis. Default is 1 if not provided. | No | +| `y` | [`number`](/docs/kcl/types/number) | The scale factor for the y axis. Default is 1 if not provided. | No | +| `z` | [`number`](/docs/kcl/types/number) | The scale factor for the z axis. Default is 1 if not provided. | No | | `global` | [`bool`](/docs/kcl/types/bool) | If true, the transform is applied in global space. The origin of the model will move. By default, the transform is applied in local sketch axis, therefore the origin will not move. | No | ### Returns @@ -62,7 +62,7 @@ sweepSketch = startSketchOn(XY) |> circle(center = [0, 0], radius = 2) |> hole(pipeHole, %) |> sweep(path = sweepPath) - |> scale(x = 1.0, y = 1.0, z = 2.5) + |> scale(z = 2.5) ``` ![Rendered example of scale 0](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABQAAAALQCAYAAADPfd1WAADhgUlEQVR4Ae3AA6AkWZbG8f937o3IzKdyS2Oubdu2bdu2bdu2bWmMnpZKr54yMyLu+Xa3anqmhztr1a8+6EEP4qqrrrrqqquuuuqqq6666qqrrrrqqquu+j+JylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV/0nu+aaax585syZBwNcc801Dz5z5syDeKZrrrnmwTzTNddc82Cey5kzZx589uzZW8+cOfNg/hXOnj17Kw9w33333coz3XfffbeePXv2GQD33XffrQBnz5699b777ruVq6666qqrrrrqqquu+r8FPehBD+Kqq6666qqrrrrqP8qLvdiLvfbrvM7rvNc111zz4DNnzjz4mmuueTD/gvvuu4/73Xfffdzv7Nmz/FucOXOGB7rmmmu45ppreGHuu+++W3mmr//6r3+ff/iHf/htrrrqqquuuuqqq6666n8/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V1111VX/SWzzb2Eb2wDYxja2sQ2AbQBsY5vnRxL3k8QLIonnxzZXXXXVVVddddVVV131fwDBVVddddVVV1111X+CiLgVIDOxzQPZxjYAtvnXsA2Abf4zSQLgmmuueTBXXXXVVVddddVVV131vxeVq6666qqrrrrqqv8kmcn9bGObF8Q2tnkg29jGNraxjW1s80C2eUEk8aKQxFVXXXXVVVddddVVV/0fROWqq6666qqrrrrqP0nXddjmgWzzwtgGwDb3sw2Abe5nG9s8P5IAkMS/lSSuuuqqq6666qqrrrrq/wCCq6666qqrrrrqqv8kR0dHt9rGNs+PbZ4f2wDYxjYAtgGwjW3uZxsA2wBIAkASDySJB5LEc5MEgCQAzp49y1VXXXXVVVddddVVV/0vR+Wqq6666qqrrrrqP0mtFdsA2OYFsc0LYxvb2MY2trGNbV4UkgCQhCSemyTuJwkASUQEV1111VVXXXXVVVdd9b8cwVVXXXXVVVddddV/ktVqdSuAbQBsA2Ab27wobHM/2zw/tnl+JCGJf48zZ848mKuuuuqqq6666qqrrvrfi+Cqq6666qqrrrrqP5FtAGzzL7HN/WxjGwDb2AbANraxjW1sc9VVV1111VVXXXXVVVe9QFSuuuqqq6666qqr/pNEBLaxzf1s80C2AbANgG1scz/bANjGNraxjW1eEElI4gWRBIAkrrrqqquuuuqqq6666v84KlddddVVV1111VX/ScZxvNU2/162AbDNA9nmP4MkJHHhwgWuuuqqq6666qqrrrrqfzkqV1111VVXXXXVVf9JbGMb29jGNg9kmxfENrYBsI1tbGMb29gGwDYviCQkIQlJ/GtFBFddddVVV1111VVXXfW/HMFVV1111VVXXXXVf5LMvNU2tnkg29jmfrZ5QWwDYBsA29jmudnmgSTxH+Gaa655MFddddVVV1111VVXXfW/F8FVV1111VVXXXXVfxLb2OaBbPOC2AbANgC2AbANgG3uZxvb2ObfQxIAkgCQBIAkrrrqqquuuuqqq6666v8AKlddddVVV1111VX/STIT29jGNrb5l9jmgWwDYBvb2MY2L4wknh9JvDCSeCBJXHXVVVddddVVV1111f9yVK666qqrrrrqqqv+k9h+hm1eGNsA2OaBbHM/29gGwDa2sY1tAGzz/EhCEg8kiX+JJAAuXLjAVVddddVVV1111VVX/S9HcNVVV1111VVXXfWfxDa2sY1t7mebF4VtbHM/29gGwDa2sc2/RBLPTRL/kojgqquuuuqqq6666qqr/pejctVVV1111VVXXfWfJCJuzUweyDb3s83zY5sHso1tbANgm+dmmweSxANJ4gWRxHOTBMCZM2cexFVXXXXVVVddddVVV/3vReWqq6666qqrrrrqP0lmYhvbANjmfra5n22em20AbPNAtrGNbWxjG9tcddVVV1111VVXXXXVVc8Xlauuuuqqq6666qr/RJmJbWzzL7GNbe5nGwDb2MY2tgGwzXOzzQNJ4vmRxItCElddddVVV1111VVXXfW/HJWrrrrqqquuuuqq/yQRcSv/Atu8KGxjG9vYBsA2trHNCyKJfw1JAEhid3eXq6666qqrrrrqqquu+l+OylVXXXXVVVddddV/otYatrGNbWxjm+dmm/vZxjYAtrENgG0AbGObB7LNCyIJAEm8KCRx1VVXXXXVVVddddVV/0dQueqqq6666qqrrvpPlJn8W9nmfrYBsA2AbWxjm38rSbwwkrjmmmsezFVXXXXVVVddddVVV/3vReWqq6666qqrrrrqP0lE3CqJzMQ2D2Qb2wDY5n62eSDb2MY2trGNbR7INs9NEg8kiX+JJO4niauuuuqqq6666qqrrvo/gOCqq6666qqrrrrqP1Fmcj/b2OYFsc0LYxsA29jGNrYBsM1zk8Rzk8TzI4nnJomrrrrqqquuuuqqq676X47gqquuuuqqq6666j9R13XY5oFs88LYxja2sY1tbANgG9vYxjYAtgGwDYAk7ieJF0YSL8ju7i5XXXXVVVddddVVV131vxzBVVddddVVV1111X+io6OjW21jm+fHNgC2+ZfYxjYPZJv/aJIAkMRVV1111VVXXXXVVVf9L0flqquuuuqqq6666j9RrRXbANjmfrZ5UdnGNraxjW1sY5v72eYFkcS/liQArrnmmgdz1VVXXXXVVVddddVV/3sRXHXVVVddddVVV/0nWq1WtwLYBsA297PNc7PN/Wxjm+dmG9sA2MY2/1EkcT9JXHXVVVddddVVV1111f9yVK666qqrrrrqqqv+k9kGwDYAtnkg2wDYBsA2trmfbWxjG9vYBsA2D2SbF0YS95PEv+TSpUtcddVVV1111VVXXXXV/3JUrrrqqquuuuqqq/4TRQS2sc2/hW0AbGMb2wDYxja2sc3zIwlJAEjifpJ4IElcddVVV1111VVXXXXV/1FUrrrqqquuuuqqq/4TjeN4q21eENu8ILYBsM39bGMb2wDY5oFsc9VVV1111VVXXXXVVVc9C8FVV1111VVXXXXVf6LMBMA2trENgG0eyDYPZJsHso1tAGwDYBsA2zyQbf69JCGJM2fOPJirrrrqqquuuuqqq67634vKVVddddVVV1111X8i27dmJra5n20AbPPcbPNAtrENgG1sYxvb2MY2ALaxzf0kcT9J/GtIAkASV1111VVXXXXVVVdd9b8cwVVXXXXVVVddddV/ItvY5oWxzQPZBsA297MNgG0AbHM/27yoJPGCSOKqq6666qqrrrrqqqv+j6Fy1VVXXXXVVVdd9Z8oM7GNbWxjmxfENs/NNraxjW0AbGMb29gGwDb/0S5dusRVV1111VVXXXXVVVf9L0dw1VVXXXXVVVdd9Z/I9jNs869lm+dmG9vYBsA2ALYBsI1tXlSSuJ8kHkgSkrjqqquuuuqqq6666qr/5ahcddVVV1111VVX/SeyjW1sYxsA29gGwDYAtnlutgGwjW0AbGMb2wDYBsA2z48k/jUk8UDXXHPNg7nqqquuuuqqq6666qr/vQiuuuqqq6666qqr/hNFxK2Zyf1scz/bPDfb2OZ+trmfbWwDYBvb2MY2/xJJSOJfQxJXXXXVVVddddVVV131vxyVq6666qqrrrrqqv9EmYltbGObfwvb2MY2ALaxjW0eyDYPJImrrrrqqquuuuqqq676f47KVVddddVVV1111X+yzMQ2L4htAGxzP9s8P7axDYBtbGObfytJPDdJAOzv73PVVVddddVVV1111VX/yxFcddVVV1111VVX/SeKiFt5ANvYxjb/EtvYBsA2trGNbWwDYBvb3M82z00S/1qSsM1VV1111VVXXXXVVVf9L0dw1VVXXXXVVVdd9Z+stYZtbPPcbPPcbPNAtrmfbQBsY5v72cY2/5EkAXDNNdc8mKuuuuqqq6666qqrrvrfieCqq6666qqrrrrqP1lmYpvnZpv72QbANs+PbWwDYBvb2MY2tnkg27wwkpDEv0QSV1111VVXXXXVVVdd9b8clauuuuqqq6666qr/RBFxK89kG9u8KGxjG9vYBsA2trHN/WwDYBsA21x11VVXXXXVVVddddVVz0Jw1VVXXXXVVVdd9Z/MNra5n21scz/bANjmBbENgG1sYxvbANgGwDb/FpIAkMRzO3v2LFddddVVV1111VVXXfW/GJWrrrrqqquuuuqq/2Rd12Eb2/xb2AbANvezjW1sYxvbPJBtnh9JvCgkARARXHXVVVddddVVV1111f9iBFddddVVV1111VX/yY6Ojm61DYBt7mcb2zw32wDYxjYAtrGNbWxjG9vY5n62eW6SAJDEi0IS95MEwJkzZx7MVVddddVVV1111VVX/e9E5aqrrrrqqquuuuo/Wa0V29gGwDbPzTYAtgGwzf1scz/b2OaBbGMbANv8e0niqquuuuqqq6666qqr/o8guOqqq6666qqrrvpPtlqtbuW52OZFYRsA29gGwDa2sY1tAGxjGwDbPD+S+Nc6d+4cV1111VVXXXXVVVdd9b8YwVVXXXXVVVddddV/AdvYxjbPzTYAtvmX2AbANraxjW3+M0hCElddddVVV1111VVXXfW/GJWrrrrqqquuuuqq/2SSyEyem21eGNsA2MY2trGNbWxjG9vYxja2sc1zk8S/RBIPJAmAUgpXXXXVVVddddVVV131vxjBVVddddVVV1111X+yaZpuBbCNbWxzP9s8N9vYBsA297MNgG1s81/lmmuueTBXXXXVVVddddVVV131vxOVq6666qqrrrrqqv9kmYltHsg2D2QbANs8N9vYBsA2tgGwjW1sYxvb2MY2LwpJXHXVVVddddVVV1111f9xVK666qqrrrrqqqv+k9m+1Ta2+dewzQPZxjYAtrGNbWzzn0ESkrjqqquuuuqqq6666qr/xahcddVVV1111VVX/SezjW0AbGOb58c2z802ALa5n21sY5v72cY2/9EuXLjAVVddddVVV1111VVX/S9G5aqrrrrqqquuuuo/WWZiG9s8kG0AbPNAtrHN/WwDYBvb2MY2ALaxjW0AbANgm38rSdwvIrjqqquuuuqqq6666qr/xahcddVVV1111VVX/Sez/YzMxDa2AbANgG1eFLYBsI1tAGxjG9sA2AbANv+Rzpw58yCuuuqqq6666qqrrrrqfycqV1111VVXXXXVVf/JbPNAtnlutgGwzf1sYxvbANjmfraxjW0AbANgm3+JJP4lkpDEVVddddVVV1111VVX/S9H5aqrrrrqqquuuuo/WUTcmpnY5t/KNraxjW1sYxsA2wDY5gWRxPMjiQeSxAPt7u5y1VVXXXXVVVddddVV/4tRueqqq6666qqrrvpPlpnYBsA2ALaxDYBtAGxzP9s8P7axjW1sYxvb2OZ+tnlBJPGCSOK5SeKqq6666qqrrrrqqqv+F6Ny1VVXXXXVVVdd9V8gM7ENgG3uZ5vnZpv72cY2trHNc7MNgG0AbHM/27wgkviXSOKqq6666qqrrrrqqqv+l6Ny1VVXXXXVVVdd9Z8sIm61jW1s86KyzXOzjW1sYxvb2Oa52ea5SeJfSxLXXHPNg7nqqquuuuqqq6666qr/nahcddVVV1111VVX/RfITF4Y2wDY5vmxjW1sYxvb2AbANrYBsM1VV1111VVXXXXVVVdd9SwEV1111VVXXXXVVf8FMhPb2MY2trHNC2Kb+9nmgWxjG9vYxja2uZ9t/jUk8dwkASCJq6666qqrrrrqqquu+l+M4Kqrrrrqqquuuuo/WUTcCmCb58c2ALYBsA2AbWwDYBvb2OaBbHM/29jmP4okdnd3ueqqq6666qqrrrrqqv/FqFx11VVXXXXVVVf9F7CNbWzzQLb5l9jmfraxjW1sYxvb2OZ+trHNv5UkHuiaa655MFddddVVV1111VVXXfW/E8FVV1111VVXXXXVf4FSCvezjW3+tWwDYBvb2AbANrYBsM1/JElcddVVV1111VVXXXXV/2IEV1111VVXXXXVVf8F1uv1rbaxzf1scz/bANjmgWwDYBsA2wDYxja2uZ9trrrqqquuuuqqq6666qrnQHDVVVddddVVV131X6CUgm0AbPMvsY1tAGwDYBsA29zPNraxzf1s8x9lb2+Pq6666qqrrrrqqquu+l+M4Kqrrrrqqquuuuq/wGq1utU2z802tgGwDYBtnpttAGxjG9vYxja2sY1tbANgG9u8qCTx3CRx1VVXXXXVVVddddVV/8tRueqqq6666qqrrvovZBsA27wobHM/29gGwDa2AbDNVVddddVVV1111VVXXfV8Ubnqqquuuuqqq676LxARZCa2sQ2AbV4UtrHN/WxjG9vYBsA2ALaxzb+XJAAkcebMmQdz1VVXXXXVVVddddVV/ztRueqqq6666qqrrvovMI7jrbwQtgGwDYBtbPNAtrGNbe5nG9vYBsA2D2Sbq6666qqrrrrqqquu+n+MylVXXXXVVVddddV/gczENrYBsA2AbWzzgtjm+bGNbWxjmweyjW3+LSTxQHt7e1x11VVXXXXVVVddddX/YlSuuuqqq6666qqr/gvYvjUzAbANgG0eyDYAtnkg29jGNraxjW1sA2Ab2wDY5j+KJK666qqrrrrqqquuuup/OYKrrrrqqquuuuqq/wK2AbDNv5dtbGMb2wDY5rnZ5qqrrrrqqquuuuqqq/6fo3LVVVddddVVV131XyAzsQ2AbWzz/NgGwDa2uZ9tbGOb+9nGNrYBsI1tbPPvIYn7SeKaa655MFddddVVV1111VVXXfW/E5Wrrrrqqquuuuqq/wK2n5GZ2OaBbANgm+fHNra5n21sYxsA29jGNg9km6uuuuqqq6666qqrrroKgquuuuqqq6666qr/AraxzQPZBsA2D2Sb58c2ALaxjW3uZxsA29jm30ISz00SV1111VVXXXXVVVdd9b8Ylauuuuqqq6666qr/ApJutY1tbGOb52abF8Q2ALa5n21sYxsA2/xHkYQk9vb2uOqqq6666qqrrrrqqv/FqFx11VVXXXXVVVf9F7CNbWzzorKNbWwDYBsA29jGNraxjW0AbGMb29jmhZHEVVddddVVV1111VVX/R9HcNVVV1111VVXXfVfJDMBsA2AbWwDYBsA2wDY5oFsA2Ab29gGwDa2sc1/pmuuuebBXHXVVVddddVVV1111f8+VK666qqrrrrqqqv+C0TErbaxDYBt7mebF4VtHsg2D2Qb2zyQbf49JHHVVVddddVVV1111VX/i1G56qqrrrrqqquu+i+SmdjGNi+MbR7INvezjW1sYxvb2MY297ONbf6jnD17lquuuuqqq6666qqrrvpfiuCqq6666qqrrrrqv0hmYpsXxDYPZBvbANjGNvezDYBtbGMbANvczzb/XpKICK666qqrrrrqqquuuup/KYKrrrrqqquuuuqq/wIRcaskAGxjG9vY5vmxzfNjG9vYxja2sY1tbGMb2/xHkIRtAM6cOfNgrrrqqquuuuqqq6666n8fKlddddVVV1111VX/RTIT2zw329gGwDbPzTYPZBsA27wgtnlBJPGiksRVV1111VVXXXXVVVf9L0blqquuuuqqq6666r9IKQXb2OaFsc1zs41tbANgG9vYxja2sY1tbPNAtrnqqquuuuqqq6666qr/pwiuuuqqq6666qqr/ousVqtbbQNgG9u8MLaxzXOzjW0AbGMb29jmfraxzX+Ec+fOcdVVV1111VVXXXXVVf9LUbnqqquuuuqqq676L1Jr5bnZ5n62uZ9t7mcb29jGNvezjW0eyDa2uZ9t/i0kASCJiOCqq6666qqrrrrqqqv+lyK46qqrrrrqqquu+i+yWq1utY1tbHM/29gGwDb/EtvYxjYAtrGNbf4zRARXXXXVVVddddVVV131vxTBVVddddVVV1111X8h29jmRWWb+9nGNgC2AbCNbWxjG9vYxjb/VpJ4btdcc82Dueqqq6666qqrrrrqqv99qFx11VVXXXXVVVf9F8lMbHM/29jmudkGwDYAtrHN/WwDYBvb2OZ+trmfbf49JHHVVVddddVVV1111VX/y1G56qqrrrrqqquu+i+Smbfaxja2eW62+ZfYxja2sY1tAGxjGwDb/FtJ4oEkIYmrrrrqqquuuuqqq676X4rKVVddddVVV1111X8R29jmudnmudnmudnmgWwDYBvb2OZ+tgGwzb/XxYsXueqqq6666qqrrrrqqv+lqFx11VVXXXXVVVf9F7F9q21sYxvb3M82ALZ5INsA2AbANraxDYBtbPNAtvmPdubMmQdx1VVXXXXVVVddddVV//tQueqqq6666qqrrvovYhvbPJBtnpttAGwDYBsA29zPNraxjW1sY5v72cY297PNv5Ukrrrqqquuuuqqq6666n8pKlddddVVV1111VX/RTIT29jm38o2tgGwzf1scz/bPJBt/q0kcdVVV1111VVXXXXVVf+LUbnqqquuuuqqq676L2L7GbaxjW1sA2AbANvczzYPZJsHsg2AbWxjG9v8Z9jd3eWqq6666qqrrrrqqqv+l6Jy1VVXXXXVVVdd9V/ENrZ5INsA2AbANg9kG9sA2MY2trGNbWxjGwDbANjGNraxzb+VJK666qqrrrrqqquuuup/OSpXXXXVVVddddVV/0Uk3Wob29jmX2KbF8Y297PNfxZJXHXVVVddddVVV1111f9SVK666qqrrrrqqqv+i9gmM7mfbZ4f2zw329jGNraxDYBtbGMb2wDYxjb/HpIAkATANddc82Cuuuqqq6666qqrrrrqfx+Cq6666qqrrrrqqv9CtrGNbe5nGwDb3M8297PNA9kGwDa2sQ2AbR7INv8RJHHVVVddddVVV1111VX/S1G56qqrrrrqqquu+i8SEbdmJrYBsI1tAGzz/NjmgWwDYBvb2MY2tgGwjW1eGNtcddVVV1111VVXXXXV/xMEV1111VVXXXXVVf+FbPPC2OZ+trmfbWwDYJvnZhvb2AbANgC2sc2/x6VLl7jqqquuuuqqq6666qr/pQiuuuqqq6666qqr/gtlJraxzQtim3+JbWxjG9sA2AbANv/Rrrnmmgdz1VVXXXXVVVddddVV//tQueqqq6666qqrrvovEhG3SsI2ALaxjW0AbPPcbGOb+9nGNraxjW1sYxsA2wDYxjb/XpKQxFVXXXXVVVddddVVV/0vRXDVVVddddVVV131XygzAbDNA9nmgWzzQLaxzQtiG9sA2Ob5sc1VV1111VVXXXXVVVf9P0Plqquuuuqqq6666r9QKQXbvCC2uZ9tnpttbGMb29jGNgC2uZ9tAGwDYJt/LUkAXLp0iauuuuqqq6666qqrrvpfiuCqq6666qqrrrrqv9BqtbrVNraxjW1s88LYBsA2z802trHN/WwDYJurrrrqqquuuuqqq676f47KVVddddVVV1111X+hWiu2eX5sA2Cb+9kGwDYAtrGNbWxzP9vY5j+DJM6cOfNgrrrqqquuuuqqq6666n8fgquuuuqqq6666qr/QqvV6lbb2MY297PNA9nmX2Ib29jmfraxjW3uZ5t/K0lcddVVV1111VVXXXXV/2IEV1111VVXXXXVVf+FbPNAtnlR2QbANrYBsI1tbGObB7KNbf61JHHVVVddddVVV1111VX/R1C56qqrrrrqqquu+i8UEdjGNvezzf1scz/bANjGNgC2AbCNbe5nG9sA2MY2/1H29va46qqrrrrqqquuuuqq/6WoXHXVVVddddVVV/0XGsfxVtsA2Ob5sc39bPPC2MY2V1111VVXXXXVVVddddXzReWqq6666qqrrrrqv5BtbGObB7KNbe5nm+dmG9vYxja2sY1tbGMbANvYxjZXXXXVVVddddVVV131/xzBVVddddVVV1111X8h27faxja2sY1t/iW2eW62uZ9tbPPcbPNvIQkASUjimmuueTBXXXXVVVddddVVV131vw+Vq6666qqrrrrqqv9CtslMXhDb3M82ALZ5INvYBsA2trmfbWwDYBsA29jmqquuuuqqq6666qqr/h+ictVVV1111VVXXfVfqLUGgG0AbANgm+fHNvezjW3uZxsA29jGNvezzb+XJAD29/e56qqrrrrqqquuuuqq/6UIrrrqqquuuuqqq/5rPSMzAbDNA9kGwDYvjG1sA2AbANvYBsA2/5Fsc9VVV1111VVXXXXVVf9LUbnqqquuuuqqq676L2QbANvczzbPzTb3s41tbGOb+9nGNra5n20AbGMb2/x7SeKqq6666qqrrrrqqqv+lyK46qqrrrrqqquu+i8k6dbMxDa2sc0D2eZ+tnl+bGMb2wDYxja2AbDN82Mb2/xbXXPNNQ/mqquuuuqqq6666qqr/nehctVVV1111VVXXfVfyDa2eW62sc1zs80LY5sHsg2Abe5nm6uuuuqqq6666qqrrvp/iuCqq6666qqrrrrqv1hmYhvbANjmgWzz3GxjG9vYxja2sY1tbGMbANsA2ObfSxKSOHv2LFddddVVV1111VVXXfW/EJWrrrrqqquuuuqq/0IRcSsPYJvnxzb3s82/xDa2sc0D2QbANs/NNi+qiOCqq6666qqrrrrqqqv+FyK46qqrrrrqqquu+i+WmdjGNs/NNvezzXOzDYBtbGMb29zPNraxzX+0M2fOPJirrrrqqquuuuqqq67634Xgqquuuuqqq6666r9YZmKb+9nGNrZ5brYBsI1tAGzzQLaxjW0eyDZXXXXVVVddddVVV131/xyVq6666qqrrrrqqv9CEXErgG0AbPPcbPMvsY1tbANgGwDbANgGwDa2+feQxFVXXXXVVVddddVVV/0vRXDVVVddddVVV131XywzAbDNC2Kb+9nmfraxzQPZBsA2/1nOnz/PVVddddVVV1111VVX/S9EcNVVV1111VVXXfVfrLV2q20eyDYAtrmfbV4Q29jGNraxjW1sYxvb2OY/SkRw1VVXXXXVVVddddVV/wtRueqqq6666qqrrvovlpnYxja2sQ2AbZ6bbQBsYxsA29zPNi8q2/xrSQJAElddddVVV1111VVXXfW/EMFVV1111VVXXXXVf7FaK7Z5QWzzQLa5n20AbGMbANvYxja2sY1tbGMb29jmX0sSAJIAuOaaax7MVVddddVVV1111VVX/e9CcNVVV1111VVXXfVfbL1e3wpgG9v8e9gGwDYAtvmX2Oaqq6666qqrrrrqqqv+nyC46qqrrrrqqquu+i9mG9vczza2eSDb2OZ+trENgG0AbANgGwDbANjmP9qFCxe46qqrrrrqqquuuuqq/4UIrrrqqquuuuqqq/6L2cY2tnkg29jmgWzzQLYBsA2AbQBsYxvbANjGNrZ5INv8W0QEV1111VVXXXXVVVdd9b8Qlauuuuqqq6666qr/Ypl5q20AbPMvsc0D2QbANgC2eSDb/Gc4c+bMg7jqqquuuuqqq6666qr/XQiuuuqqq6666qqr/ovZBsA2ALZ5INs8P7axDYBtAGxjG9vYxjYAtrmfbWzzQLZ5UUlCElddddVVV1111VVXXfW/EJWrrrrqqquuuuqq/2K2b81MAGwDYJsHss0D2ea52eZ+trmfbQBsY5urrrrqqquuuuqqq676f4zgqquuuuqqq6666r+YbWxjmweyjW3uZxvbPDfb2AbANrYBsI1tAGzz3Gzzb3Xx4kWuuuqqq6666qqrrrrqfyEqV1111VVXXXXVVf/FbPNAtnlhbHM/29zPNvezzf1sA2Cb/yiSuOqqq6666qqrrrrqqv+FCK666qqrrrrqqqv+i7XWyExsYxsA29zPNs+Pbe5nGwDb2AbANrYBsM39bPPvIQmAa6655sFcddVVV1111VVXXXXV/y5Urrrqqquuuuqqq/7rPcM2tnlBbPNAtrmfbQBscz/b2AbANvezzb+HJAAkcdVVV1111VVXXXXVVf8LEVx11VVXXXXVVVf9N7ANgG1sA2Ab29zPNs/NNg9kG9vczzb3s839bHPVVVddddVVV1111VX/D1G56qqrrrrqqquu+i8m6Vbb2OZ+tnl+bGMbANsA2MY2trENgG1sA2Ab2/x7SOKqq6666qqrrrrqqqv+DyC46qqrrrrqqquu+i9mG9sA2Oa52ea52eYFsY1tAGzzQLaxzb/XpUuXuOaaax7MVVddddVVV1111VVX/e9C5aqrrrrqqquuuuq/WETcmpnYBsA2ALaxDYBtnh/bANjGNraxDYBtbANgG9s8N9v8a0niqquuuuqqq6666qqr/peictVVV1111VVXXfXfIDOxzQtjmweyDYBtnptt/iW2AbDNv5Ykrrrqqquuuuqqq6666n8hKlddddVVV1111VX/DWxjG9vYxjbPj21s8/zYxja2AbCNbf6jSOJ+ly5d4qqrrrrqqquuuuqqq/4XonLVVVddddVVV131XywibrWNbWxzP9sA2Oa52QbANgC2eSDbPJBtbGMbANv8W0jifmfOnHkwV1111VVXXXXVVVdd9b8LwVVXXXXVVVddddV/g2EYbrUNgG1sA2AbANs8N9sA2AbANraxDYBtbGObB7LNv4ckJHHVVVddddVVV1111VX/C1G56qqrrrrqqquu+m8gCdvY5gWxjW1s89xs80C2eSDb/Efa29vjmmuueTBXXXXVVVddddVVV131vwvBVVddddVVV1111X+DaZputY1tbGMb2wDY5rnZBsA2tgGwjW1sA2Ab2/x7SOK5SUISV1111VVXXXXVVVdd9b8QwVVXXXXVVVddddV/A9vY5rnZBsA2z80297PNA9nmfrYBsM2/hSSe2/7+PgDXXHPNg7nqqquuuuqqq6666qr/PQiuuuqqq6666qqr/htEBLaxjW2eH9vY5rnZBsA2trGNbWxzP9sA2Oa5SeJFIQlJSOJ+Z86ceTBXXXXVVVddddVVV131vweVq6666qqrrrrqqv8GwzDcmpnYBsA2tgGwzQPZxjYAtgGwzf1sA2Ab2/xbSeJ+knggSVy4cIGrrrrqqquuuuqqq676X4bgqquuuuqqq6666r9BZt5qG9vY5n62eUFs80C2sQ2Abf6zSEISFy5c4JprrnkwV1111VVXXXXVVVdd9b8Hlauuuuqqq6666qr/Bq01bHM/29jmfraxzXOzjW1scz/bANjGNgCSsM2LShLPjyQAJBERXHXVVVddddVVV1111f8yVK666qqrrrrqqqv+G9h+RmZiG9vczza2uZ9tbGOb52Yb2wDY5oFs8/xI4kUlCQBJSGJ3d5czZ848iKuuuuqqq6666qqrrvrfg+Cqq6666qqrrrrqv4GkWzMT29jGNv8S2wDYxjYAtrGNbWxjmxdEEveTxP0k8UCSuJ8kACRx6dIlrrnmmgdz1VVXXXXVVVddddVV/3sQXHXVVVddddVVV/03sE1mYpv72eZ+trHNc7PN/WwDYJv72cY2LwpJSOKBJAEgiftJAuDSpUtcc801D+aqq6666qqrrrrqqqv+96By1VVXXXXVVVdd9d8gIm7NTGxjG9sA2MY297ONbWxjGwDb2AbANgC2sc39bPOvJYkHksT9IoK9vT3OnDnzYK666qqrrrrqqquuuup/D4Krrrrqqquuuuqq/yaZiW1sYxvb3M82z49t7mcbANsA2MY2tgGwjW2emyReVJKQBMD+/j5XXXXVVVddddVVV131vwzBVVddddVVV1111X+DiLjVNpmJ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V1111VVXXfU/2Ww2+61rr732tTc3N5nNZnRdR9d1lFKotVJKISIopRARRASlFCKCUgoRQSmFiKCUQkQQEZRSiAgiAklEBJIAkASAbQBsYxvb2MY2trGNbTIT29jGNraxjW1sYxvb2AbANraxjW1sYxvbZCa2sU1mYhvb2MY2/1PYxja22dzc5Prrr8c2N9xwA9vb28xms1v/4R/+4bd/67d+63v+4R/+4be56qqrrrrqqquuuuq/A3rQgx7EVVddddVVV1111f9ktdbfuuaaa157e3ub2WxG3/d0XUcphVorpRQiglIKEUEphYggIiilEBGUUogISilEBBFBKYWIICKICCQhCUkASALANgC2sY1tbGMb29jGNraxjW1sYxvb2MY2ALaxDYBtAGxjG9vYxja2AchMAGxjm/vZ5kUliX8v2wBIAsA2tgGwjW0yk8zENq01NjY2uO6663jMYx7DbDa79bd+67e++7d/+7e/57777ruVq6666qqrrrrqqqv+q6AHPehBXHXVVVddddVVV/1Plpmvfe211/7WsWPHWCwW9H1PrZVaK7VWSimUUogISilEBBFBKYWIICIopRARlFKICCKCUgoRQUQQEUgiIpAEgCQAbHM/29jGNraxjW1sYxvb2AbANraxjW3uZxsA29zPNgC2AbANgG0AbANgGwDbPDdJ/FexDYBtbJOZ2CYzsU1m0lqjtUZrjY2NDR7xiEfwqq/6qtx33323fv3Xf/37/MM//MNvc9VVV1111VVXXXXVfzb0oAc9iKuuuuqqq6666qr/yTLzwSdPnnz6iRMn2NjYoO97uq6j1kqtlVIKpRQiglIKEUFEUEohIogISilEBKUUIoKIoJRCRBARRAQRgSQkIQkASQDYBsA2ALaxjW1sA2Ab29jGNgC2sc39bPNvYRsA2/xPYBvb2MY2mYltMpPMJDNprZGZtNZorTFNE4vFgkc+8pE89rGPZRiGWz/rsz7rde67775bueqqq6666qqrrrrqPwt60IMexFVXXXXVVVddddX/dLPZ7Leuueaa197c3GQ2m9H3PbVWaq2UUiilEBGUUogISilEBBFBKYWIICIopRARRASlFCKCiCAiiAgkEREASAJAEgC2AbANgG1sA2Ab29gGwDa2uZ9tHkgS95MEgCQAJCEJSUhCEvfLTABs89wk8V/FNpmJbTKTzKS1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rrrqqv8lCK666qqrrrrqqqv+D/vFX/zFX+Cq/xGOjo6+Z3d397eHYWCaJlprZCa2sY1tbGMb29jGNraxjW1s80C2sY1tbPNvJYn7SUISz00SkpDEpUuXeOxjH/taXHXVVVddddVVV/3PR3DVVVddddVVV131f9zbvd3biQd4u7d7O3HVf4dbd3d332e5XDJNE9M00VojM8lMMpPMxDa2sQ2AbWxjGwDb2MY2tnlutrENgG1eGEk8kCTuJwkASUjifhHB7u4uV1111VVXXXXVVf9LEFx11VVXXXXVVVf9P/AjP/Ijnw3wIz/yI5/NVf9tbN963333vc96vWaaJjKT1hqZiW1sYxvb2MY2trmfbWxjm/vZxja2sc1zsw2Abf61JHE/SUhCEru7u7z4i7/463DVVVddddVVV131Px/BVVddddVVV1111f8D11xzzYMBXud1Xue9ueq/1TRN333hwoXfHseRaZrITDKTzMQ2trGNbWxjG9vYxjb3s41tbPPcbGOb+9nmBZEEgCReEEkASEISEcE111zzYK666qqrrrrqqqv+5yO46qqrrrrqqquu+n/gxV7sxV4b4Jprrnnwi73Yi702V/23Ojg4eJ/1es00TbTWyEwyk8wkM8lMbGMb29jGNgC2sY1t7mcb29jGNvezzb+FJCQBIAkASQBI4tKlS9x3331cddVVV1111VVX/S9AcNVVV1111VVXXfV/3Iu92Iu91jXXXPNgnunFXuzFXour/rvdeu7cue8ehoFpmshMMpPMxDa2sY1tAGwDYBvb3M82tnl+bPOiksT9JHE/STyQJAAkIelB11xzzYO56qqrrrrqqquu+p+N4Kqrrrrqqquuuur/uGuuuebBPMDrvM7rvDdX/bdbr9efs16vmaaJ1hqZiW0yE9vYxja2sY1tbANgG9vczza2sY1tbHM/27yoJPH8SOJ+kpCEJM6cOfNgrrrqqquuuuqqq/5nI7jqqquuuuqqq676P+7FXuzFXpsHuOaaax784i/+4q/NVf+tJN169uzZ9xmGgWmayEwyE9vYxja2sY1tbANgG9sA2MY2tvnPIgkASUhCEpJ48pOfzDXXXPNgrrrqqquuuuqqq/5nI7jqqquuuuqqq676P+7FXuzFXpvn8tjHPva1uOq/3TRNvz0MA9M00VojM8lMMhPb2MY2ALaxjW0AbGOb+9nG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H00S/10k8YLYxjZnz559BlddddVVV1111VX/cxFcddVVV1111VVX/T/xdm/3dvqQD/mQhwB3AtNnfuZnvg5X/Y9nm38L2zw/tnlukpAEgCQkIYl/yYkTJ7jqqquuuuqqq676H47gqquuuuqqq6666v+fG4H64R/+4d/FVf/jSeLfyzYvCklcddVVV1111VVX/R9DcNVVV1111VVXXfX/yH333XfrP/zDP/w2wDXXXPPg13md13lvrvofTRIvKtv8e0niRXXixAmuuuqqq6666qqr/ocjuOqqq6666qqrrvp/7B3f8R0/i6v+R+v7/sH8J5GEJAAkIQkASbwobHPffffdylVXXXXVVVddddX/XARXXXXVVVddddVV/49dc801D37Hd3zHz+Kq/7EyE0m8MLa5n23+tSRxP0kASEISz49tAGxz1VVXXXXVVVdd9T8cwVVXXXXVVVddddX/c6/zOq/z3tdcc82Duer/DNs8P7Z5QSQhCQBJ/Etsc/LkSc6ePXsrV1111VVXXXXVVf9zEVx11VVXXXXVVVf9P3PmzJkH8wDXXHPNg9/xHd/xs7jqfyTb/HvY5rlJ4oEk8YJI4gWxzVVXXXXVVVddddX/cARXXXXVVVddddVVV/FiL/Zir/3iL/7ir81V/+NI4j+TJO4nCUm8qE6dOsVVV1111VVXXXXV/3AEV1111VVXXXXVVVdxzTXXPPgd3/EdP5ur/kfJzAfzn0gS95PE/SQhiRfFfffddytXXXXVVVddddVV/3MRXHXVVVddddVVV1112Yu92Iu91ou92Iu9Nlf9jySJ+9nmfrYBsM1zs82LQhL3k8TzI4kHso1trrrqqquuuuqqq/6HI7jqqquuuuqqq676f+aaa655MC/Ah3/4h38XV/2/IAkASQBIQhIPJIn72eaBJHHq1Cmuuuqqq6666qqr/oejctVVV1111VVXXXUV9913363/8A//8Nv33XffrVz1f4JtJHE/20ji+ZHEA0nigSTx3Gxz33333cpVV1111VVXXXXV/2xUrrrqqquuuuqqq/6f+ZEf+ZHPPnv27K333XffM97pnd7ps17sxV7stf/hH/7ht7/+67/+fbjqfxxJvCC2kcT9bCOJfwtJSOJ+knh+bAMgiauuuuqqq6666qr/BahcddVVV1111VVX/T/zoz/6o5/DM33913/9rd/0Td/09Bd7sRd77WuuuebB9913361c9T9GZj6YF8A2knhutgGQxL9EEpL4t9jZ2eHs2bO3ctVVV1111VVXXfU/G8FVV1111VVXXXXV/2P33Xffrb/1W7/13ddcc82D3/Ed3/GzuOp/NNv8Z5CEJCQhiauuuuqqq6666qr/Qwiuuuqqq6666qqr/p/70R/90c8BeJ3XeZ33frEXe7HX5qr/MSTxorDNi0oS/xJJvDC2OX78OPfdd9+tXHXVVVddddVVV/3PRnDVVVddddVVV131/9x9991369d//de/D8CHf/iHfxdX/b8gCUlIQhKSkMSLyjZXXXXVVVddddVV/wsQXHXVVVddddVVV13FP/zDP/z23//93//2Nddc8+AP//AP/y6u+h8hMx8MIIn72eZfyzYvKkkASOK52eaqq6666qqrrrrqfxmCq6666qqrrrrqqqu47777bv36r//69wZ4ndd5nfd+ndd5nffmqv92kpDEfzRJ3E8SkpCEJAAkASCJ+0nigY4fP8599913K1ddddVVV1111VX/sxFcddVVV1111VVXXXXZ2bNnn/H1X//173Pffffd+o7v+I6f9WIv9mKvzVX/rSTxorLNfwRJvChsc9VVV1111VVXXfW/AMFVV1111VVXXXXVVc/yW7/1W9/9W7/1W999zTXXPPjDP/zDv+uaa655MFf9t4kI/iW2eWFs84JIAkASkpDEc5PEVVddddVVV1111f9iBFddddVVV1111VVXPYff/u3f/p5/+Id/+O1rrrnmwZ/7uZ/721z1v4ZtXlSSAJAEgCQAJCEJSbwwJ06c4OzZs8/gqquuuuqqq6666n82gquuuuqqq6666qqrnsN9991369d//de/z3333XfrmTNnHvRN3/RNT+eq/xaS+Neyzb+WJAAk8aKyzVVXXXXVVVddddX/AgRXXXXVVVddddVVVz2P++6779bP+qzPep377rvv1muuuebB3/RN3/R0rvovl5kP4t/ANraxzQsjCUlIQhKSkMSL4sSJE1x11VVXXXXVVVf9L0Bw1VVXXXXVVVddddXzdd999936mZ/5ma/9D//wD799zTXXPPibvumbnn7NNdc8mKv+y5RSkMR/NUk8P5K4n23uu+++W7nqqquuuuqqq676n43gqquuuuqqq6666qoX6OzZs8/4+q//+ve57777br3mmmse/Dmf8zm/dc011zyYq/5HsM2/hSQkIQlJSEISkpAEgCQk8YLY5qqrrrrqqquuuup/AYKrrrrqqquuuuqqq16o++6779bP+qzPep1/+Id/+O1rrrnmwZ/7uZ/72+/4ju/4WVz1n04Sz49tHsg2/16SuJ8kXhjbnDhxgrNnz97KVVddddVVV1111f9sBFddddVVV1111VVX/Yvuu+++W7/+67/+fX7kR37ks237nd7pnT77Hd/xHT+Lq/5TSeI/kiSemyQkASAJSbwobHPVVVddddVVV131vwDBVVddddVVV1111VUvkvvuu+/WH/3RH/2c3/qt3/pugHd6p3f67G/6pm96+ou92Iu9Nlf9p5D0YF4A2/x7SEISkpCEJCTxojp58iRXXXXVVVddddVV/wsQXHXVVVddddVVV131r/KjP/qjn/MhH/IhD/mHf/iH377mmmse/OEf/uHf9U7v9E6fzVX/4STxorLNi0ISz48kACQhCUn8S+67775bueqqq6666qqrrvqfjeCqq6666qqrrrrqqn+1++6779av//qvf58f+ZEf+exrrrnmwe/4ju/4Wd/0Td/09Hd8x3f8LK76DyWJF5VtbPOikoQkJAEgiecmiauuuuqqq6666qr/xQiuuuqqq6666qqrrvo3ue+++2790R/90c/5kA/5kIf8wz/8w29fc801D36nd3qnz/6mb/qmp7/O67zOe3PVv5sk/jNIQhL3k4QkJCEJSQBI4gU5efIkV1111VVXXXXVVf8LUI4fP85VV1111VVXXXXVVf92h4eHu7/1W7/1PWfPnn3Ggx/84Je+5pprHvyKr/iKb/26r/u673N4eLh76623/jVX/ZvUWt96sVi8dCmFUgoRQUQgCUlIAkASkpCEJCQhCUlIQhKSiAgigoiglEKtlVorpRRKKdyvtUZrjdYarTUyk8wkM8lMWms85jGPecbP//zPfzVXXXXVVVddddVV/7NRjh8/zlVXXXXVVVddddVV/3633nrrX//Zn/3ZzxweHu5ec801D77mmmse/Iqv+Ipv/Tqv8zrvvbm5eRzQ2bNnb+WqF9lisfjo2Wz24FIKpRQigohAEpKQhCQkIQlJSEISkpCEJCQhiYggIogISimUUqi1UkqhlIIkbJOZtNZordFaIzPJTDKTzCQzebmXe7lLP//zP//VXHXVVVddddVVV/3PRuWqq6666qqrrrrqqv8w9913360/+qM/+jm//du//T1nzpx58Du90zt91ou92Iu99ju90zt9NsDZs2ef8Zu/+Zvf9du//dvfc999993KVS9Ua43/LJKQBIAkJAEgiX/Jzs4O9913361cddVVV1111VVX/c9HOX78OFddddVVV1111VVX/cc6PDzcPXv27K2/9Vu/9T2//du//T2bm5vHH/KQh7z05ubm8Rd/8Rd/7Td/8zf/6Nd5ndd5783NzeOAzp49eyv/Cm/7tm/7bq/8yq/89n/913/92/wXed/3fd/Pv+WWW0498YlPfBz/Rfq+f+/5fP7gUgqlFCKCiEASkpCEJCQhCUlIQhKSkIQkJCGJiCAiKKVQSqGUQq2VWisRgSQyk9YarTVaa7TWyEwyk8wkM+m6joc85CHP+K3f+q3v5qqrrrrqqquuuup/NvSgBz2Iq6666qqrrrrqqqv+811zzTUPfrEXe7HXerEXe7HXfp3XeZ335gHuu+++WwH+4R/+4bf/4R/+4Xfuu+++W8+ePXvrfffddyvP5Sd+4ifMA3zAB3xAvXDhQuMBrrnmmgefOXPmwS/2Yi/2Wi/+4i/+2vfdd98zvv7rv/69+Td4yEMesvjyL//yIx7g7d7u7cR/gY2Njd86duzYa/d9T9d1lFIopRARRAQRgSQkERFIQhKSKKUQEUQEEUFEUEqh1kqtla7rmM1mzGYzuq6j1gpAa41hGFiv16zXa9brNeM4Mo4j0zQxjiPXXXcdN9100/d8/dd//Xtz1VVXXXXVVVdd9T8betCDHsRVV1111VVXXXXVVf+1rrnmmgefOXPmwS/2Yi/2Wi/+4i/+2i/2Yi/22rwA9913361nz569FdCLvdiLvRbPx2/91m99N8A111zz4Bd7sRd7bZ7L2bNnn/H3f//3v8VzEmCekwDzAK/zOq/z3jyXs2fPPuM3f/M3v+vs2bPPALjvvvtuPXv27K333Xffrddcc82DAc6cOfNggGuuuebBAGfOnHkQwG//9m9/z3333XcrL4Ljx48/fbFYPLjve7quo5RCKYWIICKICCQhiYhAEpKQRCmFiCAiiAgiglIKtVZqrfR9T9/3zGYz+r6n1gpAa431es16vWa9XrNerxnHkXEcmaaJcRy57rrruOmmm77n67/+69+bq6666qqrrrrqqv/Z0IMe9CCuuuqqq6666qqrrvrvdc011zz4zJkzD77mmmsefObMmQe9+Iu/+GufOXPmwddcc82DAe67775br7nmmgfzf8CHfMiHPOS+++67lRfB9vb207e2th48m82otVJKoZRCRBARSCIikIQkIgJJSKKUQkQQEUQEEUEphVortVb6vmc2mzGbzej7nlorANM0MQwDq9WK9XrNMAwMw8A4jkzTxDiOXHfdddx0003f8/Vf//XvzVVXXXXVVVddddX/bFSuuuqqq6666qqrrvpvd999991633333foP//APAPzoj/7o5/BM11xzzYPPnDnzYMAf/uEf/t3XXHPNg3kuX//1X/8+AC/2Yi/2Wtdcc82DX+zFXuy1eYB/+Id/+O3f+q3f+h6ekwHxnAyIB/jwD//w7+L5+Id/+IfffrEXe7HX5l/hH/7hH377vvvuu5V/BUk8kG1eENsASMI2L4gk7icJSUgCQBIAknhBjh07xn333fd0rrrqqquuuuqqq/7nQw960IO46qqrrrrqqquuuup/j5/4iZ8wD/B2b/d24rlcc801DwZ4sRd7sdd+sRd7sdc6e/bsM37kR37ks/k3+omf+AnzAG/3dm8ngGuuuebBr/3ar/1er/M6r/Pe11xzzYN5EZw9e/YZv/mbv/ld//AP//A7//AP//DbvBDb29tP397efnDXdXRdRymFiKCUQkQgiYhAEpKQhCQigoiglEJEEBFEBKUUaq10XUff98xmM2azGbPZjFortpmmiWEYWK1WrFYrhmFgGAbGcWSaJsZx5JGPfCSr1eqzf/RHf/RzuOqqq6666qqrrvqfDT3oQQ/iqquuuuqqq6666qqr/j2uueaaB7/Yi73Ya73Yi73Ya7/O67zOe/Miuu+++2790R/90c/5rd/6re/m+djZ2Xn61tbWg/u+p9ZKKYWIoJRCRCCJiEASkpCEJCKCiKCUQkQQEZRSKKVQSqHrOvq+Zz6fM5vNmM1mlFIAmKaJ9XrNarVitVoxDAPDMDCOI9M0MY4jj3zkI1mtVp/9oz/6o5/DVVddddVVV1111f9slOPHj3PVVVddddVVV1111VX/HoeHh7u33nrr3/zpn/7pz/z2b//299x6661/s7m5efyaa655MA/wmZ/5ma/zW7/1W98N8JCHPOSlNzc3j7/iK77iW7/u677u+2xsbBz7h3/4h9/hAWaz2Uf3fX+8lEJEEBFIIiIAkIQkJCEJSUhCEpKICCQhiYggIiilUEqh1kqtla7rqLUSEUjCNq01pmmitUZrjdYatslMMpOHPexhPP3pT/+eW2+99a+56qqrrrrqqquu+p8NPehBD+Kqq6666qqrrrrqqqv+M5w5c+ZBr/M6r/Per/M6r/Pe11xzzYPf7u3eTjzTNddc8+AXe7EXe+3XeZ3Xea8Xe7EXe22A++6779bf+q3f+u4f/dEf/RyAEydOeLFY0Pc9tVZKKUQEEUFEEBFEBJKQhCQkERFEBKUUIoKIoJRCKYVaK13X0fc98/mc+XzObDajlALAOI6s12tWqxWr1Yr1es0wDEzTxDiOjOPIK7zCK/D3f//37/Nbv/Vb381VV1111VVXXXXV/2zoQQ96EFddddVVV1111VVXXfWf6ZprrnkwwH333Xcrz8eZM2ce9BEf8RHf/WIv9mKvDXDffffd+tu//dvf82u/9mufNZ/P6fueWiulFCKCiCAiiAgkERFIQhKSiAgigoiglEJEUEqhlEKtla7rmM1mzOdz5vM5fd9TawVgHEfW6zXL5ZLVasV6vWYcR8ZxZBxHxnHkDd7gDfj93//99/mt3/qt7+aqq6666qqrrrrqfzaCq6666qqrrrrqqquu+k9233333Xrffffdygtw9uzZZ3zmZ37m63zIh3zIQ/7hH/7ht6+55poHv+M7vuNn2eY/gyQeSBKSAJDEi+Ls2bO3ctVVV1111VVXXfU/H8FVV1111VVXXXXVVVf9D3Hffffd+pmf+Zmv8/Vf//Xvc999993KA9jmBbHNi0oSAJKQhCQkIQlJ3E8SkpDEc8tMrrrqqquuuuqqq/6XoHLVVVddddVVV1111VX/w/zWb/3Wd//93//9b0m6lX8l27yoJCEJSUgCQBKSeGFOnDjBfffddytXXXXVVVddddVV//MRXHXVVVddddVVV1111f9A9957L8/NNi+IbWzzwkgCQBKSAJCEJCQhCUn8S2xz1VVXXXXVVVdd9b8Elauuuuqqq6666qqrrvp/ShKSkIQk7icJSbwgJ06c4Kqrrrrqqquuuup/CSpXXXXVVVddddVVV131v5RtJPGvIQkASUhCEpKQBIAkXhT33XffrVx11VVXXXXVVVf9z0flqquuuuqqq6666qqr/geTxH80SUhCEpKQhCQAJCEJAEk8kCSuuuqqq6666qqr/pehctVVV1111VVXXXXVVf9DSeI/myQiAkk8kCRekBMnTnDVVVddddVVV131vwSVq6666qqrrrrqqquu+h/I9oP5DySJ+0lCEpKQhCQkASAJSdxPEpJ4oPvuu+9Wrrrqqquuuuqqq/53oHLVVVddddVVV1111VX/x0kCQBKSuJ8kJCEJSQBI4vmRBIAkrrrqqquuuuqqq/4XIbjqqquuuuqqq6666qr/hWwDYJvnZpsXRhKSkIQkJBERSEISkpDE87O9vc3Zs2dv5aqrrrrqqquuuup/BypXXXXVVVddddVVV131/5AkJCEJSUhCEg8kiecmiauuuuqqq6666qr/RahcddVVV1111VVXXXXV/0CZ+WBeRLa5nyReGElIAkASkpCEJAAkIYkXZGdnh/vuu+9Wrrrqqquuuuqqq/53ILjqqquuuuqqq6666qr/gSQhiedmG9v8e0lCEpKQhCQkIQlJSOKqq6666qqrrrrq/wAqV1111VVXXXXVVVdd9T+QJP6jSQJAEpKQhCQiAklIQhIPJImrrrrqqquuuuqq/8WoXHXVVVddddVVV1111f9AkvjPIAkASQBIQhKSkASAJAAk8dx2dna46qqrrrrqqquu+l+EylVXXXXVVVddddVVV/0PJIn/SJK4nyQAJBERSEISkpAEgCTuJ4kHuu+++27lqquuuuqqq6666n8Hgquuuuqqq6666qqrrvpfyjb/VpKQhCQkERFIQhKSuOqqq6666qqrrvo/guCqq6666qqrrrrqqqv+Z3ow/8EkIQlJSEISkogIJCEJSTw/kgDY2dnh7Nmzz+Cqq6666qqrrrrqfwcqV1111VVXXXXVVVdd9T+QJP4zSUISkpCEJCQhCUlI4vmxzVVXXXXVVVddddX/IlSuuuqqq6666qqrrrrqfyBJSOJfyzb3k8TzIwmAiEASkpBERPBAkpDE/SRx/Phxrrrqqquuuuqqq/4XIbjqqquuuuqqq6666qr/gSTxorDN/Wzz/EjifpIAkIQkIgJJSEISkpDEC2Kb++6771auuuqqq6666qqr/ncguOqqq6666qqrrrrqqv+BIuLBvIhs8y+RhCQAJCEJSUgiIogIJCEJAElcddVVV1111VVX/R9AcNVVV1111VVXXXXVVf8D2QZAEs+PbR7INs+PJB5IEpKQhCQkIQlJSEISknggSdxvZ2eHs2fP3spVV1111VVXXXXV/w5Urrrqqquuuuqqq6666n8gSfxnkoQkJBERSEISDySJq6666qqrrrrqqv/lqFx11VVXXXXVVVddddX/E5KQhCQkIYmIQBKSkIQkJCGJ5+f48eNcddVVV1111VVX/S9C5aqrrrrqqquuuuqqq/4HksR/FklIQhKSkEREIAlJPJAkACRxv/vuu+9Wrrrqqquuuuqqq/53oHLVVVddddVVV1111VX/Q0niP4oknpskJBERSEISkpCEJJ4f21x11VVXXXXVVVf9L0Jw1VVXXXXVVVddddVV/wNFxIN5LrYBsM2/lSQkIYmIICKQhCQkIYn7SeK5nThxgquuuuqqq6666qr/RQiuuuqqq6666qqrrrrqfyBJ/Ets86KQBIAkACQhCUlIQhIRgSQAJPFAkrjffffddytXXXXVVVddddVV/3tQueqqq6666qqrrrrqqv8jbPMvkQSAJCQREUQEkpCEJCQhiecmiauuuuqqq6666qr/ZQiuuuqqq6666qqrrrrqfxHb/FtIQhIAkpCEJCQhCUlIQhIvyPb2NmfPnr2Vq6666qqrrrrqqv89CK666qqrrrrqqquuuup/oL7vH8yLwDYAtnlRSEISkpBERBARRASSkIQkACQhCQBJXHXVVVddddVVV/0vRHDVVVddddVVV1111VX/A7XWeFHZ5gWRxANJAkASEYEkJCEJSUgCQBLPbWdnh/vuu+9Wrrrqqquuuuqqq/73oHLVVVddddVVV1111VX/T0gCQBKSkIQkIgJJSAJAEg8kiauuuuqqq6666qr/pahcddVVV1111VVXXXXV/3GSkASAJCQhCUlEBJKQhCQkcdVVV1111VVXXfV/CJWrrrrqqquuuuqqq676H0oSL4htJPFvIQlJRASSiAgkERFIQhKSeG7b29tcddVVV1111VVX/S9D5aqrrrrqqquuuuqqq/4HksS/lyQeSBKSkIQkJBERSEISkpDE/SQhiQe67777buWqq6666qqrrrrqfw8qV1111VVXXXXVVVdd9T9QKeXB/CeKCCQhiYgAQBKSkMRzk8RVV1111VVXXXXV/0IEV1111VVXXXXVVVdd9X+QJB5IEpKQhCQkERFEBJKICCRxP0k8t52dHc6ePfsMrrrqqquuuuqqq/73oHLVVVddddVVV1111VX/x0nifpKQhCQkIQlJSEISkpDEA0kCwDZXXXXVVVddddVV/8tQueqqq6666qqrrrrqqv+lbCOJF0YSz00SkogIIoKIAEASkrifJCRxv52dHa666qqrrrrqqqv+lyG46qqrrrrqqquuuuqq/wVs8/zYBsA295PEc5OEJCQhCUlIQhKSiAgkIQlJSOK52ea+++67lauuuuqqq6666qr/PQiuuuqqq6666qqrrrrqf5jMfDAvhG0eyDb/GpKQhCQigohAEpK46qqrrrrqqquu+j+GylVXXXXVVVddddVVV/0fJwkASUhCEhFBRCAJSQBIQhKSeH62t7c5e/bsrVx11VVXXXXVVVf970Hlqquuuuqqq6666qqr/h+QBIAkJCEJSUQEEYFtJCEJAElIAkASkrjqqquuuuqqq676X4jKVVddddVVV1111VVX/Q8kiX8vSTyQJAAkIYmIQBKSkIQkJCGJ+0nifseOHeOqq6666qqrrrrqfxkqV1111VVXXXXVVVdd9T9MZj6Y/yCSeCBJAEhCEhFBRGAbSfxL7rvvvlu56qqrrrrqqquu+t+DylVXXXXVVVddddVVV/0vYhtJ/GtIQhKSkIQkJBERSEISAJKQhCSeH9tcddVVV1111VVX/S9DcNVVV1111VVXXXXVVf/DSAJAEg9km/vZ5t9KEhGBJCICSUhCEpIAkIQkHuj48eNcddVVV1111VVX/S9D5aqrrrrqqquuuuqqq/4PkcQLIglJSEISkpBERGAbSQBI4n6SkIQk7rvvvlu56qqrrrrqqquu+t+FylVXXXXVVVddddVVV/0fYJsHksQDSQJAEpKQREQQEUgCQBKSeH4kcdVVV1111VVXXfW/EMFVV1111VVXXXXVVVf9D5OZD5bEv8Q2/xJJSAJAEpKQhCQkIYmIQBKSkIQkJPFAm5ubnD179lauuuqqq6666qqr/nehctVVV1111VVXXXXVVf/DSOKBbPOC2AZAEs9NEs+PJCKCiCAikIQkJCGJ+0niqquuuuqqq6666n85KlddddVVV1111VVXXfU/jCT+o0lCEpKQhCQkIQlJSEISz48ktre3ue+++27lqquuuuqqq6666n8XKlddddVVV1111VVXXfU/jCT+s0hCEpKICCKCiABAEpKQhCQAJHHVVVddddVVV131vxiVq6666qqrrrrqqquu+j9OEpKQhCQkERFIQhKSkIQkJHHVVVddddVVV131fwjBVVddddVVV1111VVX/Q8jiRfGNi+MJO4niecmCUlEBBGBJCQhCQBJAEjifltbW1x11VVXXXXVVVf9L0Tlqquuuuqqq6666qqr/oex/SD+E0hCEpKQhCQkIQlJSEISAJK4nyQA7rvvvlu56qqrrrrqqquu+t+FylVXXXXVVVddddVVV/0PIwlJ/HtIQhIAkpCEJCQREUQEEUFEEBHY5vmRxFVXXXXVVVddddX/YgRXXXXVVVddddVVV131P4wk/rVs86KQhCQkIQlJSEISkpCEJJ7b9vY2Z8+efQZXXXXVVVddddVV/7tQueqqq6666qqrrrrqqv9hJPEfRRIAkpCEJCQhiYhAEpKQhCQkcT9J3M82V1111VVXXXXVVf8LEVx11VVXXXXVVVddddX/MBHxYP4FtnlRSeKBJCEJSUQEkpCEJAAkIYkH2tnZ4aqrrrrqqquuuup/IYKrrrrqqquuuuqqq676H8Y2/xq2+ZdIQhKSkEREIAlJSEISkpDEA0lCEpnJfffddytXXXXVVVddddVV/7sQXHXVVVddddVVV1111f8wknhBbHM/29jmBZHEA0lCEpKQREQgCUlIQhJXXXXVVVddddVV/8cQXHXVVVddddVVV1111f8RkpDEA0lCEpIAkIQkIgJJRAQRgSQAJCEJAEncb2tri7Nnz97KVVddddVVV1111f8uVK666qqrrrrqqquuuup/GElI4t9KEpJ4bpKQhCQkERFIQhKSkMT9JPFAtrnqqquuuuqqq676X4jKVVddddVVV1111VVX/T8gCUlIQhKSkIQkJCEJSUjigSQBcOzYMa666qqrrrrqqqv+FyK46qqrrrrqqquuuuqq/2EkPZjnYhvb/GtJQhIPJImIQBKSkIQkACQhiQeShG3uu+++W7nqqquuuuqqq67634XKVVddddVVV1111VVX/Q8jif8IknggSUhCEpKICCQhCQBJvCC2ueqqq6666qqrrvpfiMpVV1111VVXXXXVVVf9HycJSQBIIiKQhCQkIQlJSEISAJKQxP2OHTvGVVddddVVV1111f9CBFddddVVV1111VVXXfW/jG3+JZJ4bpKQhCQkIYmIAEAS95PE/SQhifvuu+9Wrrrqqquuuuqqq/73oXLVVVddddVVV1111VX/w5RSHsx/EEkASEISkpBERCAJSUhCEpKQxHOTxFVXXXXVVVddddX/UgRXXXXVVVddddVVV131v5xtHkgSAJIAkMT9JCEJSUQEkpAEgCSen42NDc6ePXsrV1111VVXXXXVVf/7EFx11VVXXXXVVVddddX/UJJ4QWzzQJIAkMTzIwlJSEISkpCEJCQhiftJQhKSkMRVV1111VVXXXXV/2JUrrrqqquuuuqqq6666n8gSfxLbAMgiRdEEpIAkIQkJBERSEISkpCEJCTx3DY3N7nvvvtu5aqrrrrqqquuuup/HypXXXXVVVddddVVV131f5AkHkgS95OEJCQhCUlI4qqrrrrqqquuuur/ICpXXXXVVVddddVVV131P0zXdQ/mP4Ak7icJSUhCEpKQhCTuJ4kHkgSAJK666qqrrrrqqqv+lyK46qqrrrrqqquuuuqq/2Eyk38LSQBI4n6SkASAJCQREUgiIgCQhCQkIQlJ3E8SGxsbXHXVVVddddVVV/0vRXDVVVddddVVV1111VX/w0jiBbHNv4UkJCEJSUhCEpKQxHOTxP0kcd99993KVVddddVVV1111f8+BFddddVVV1111VVXXfU/yOu8zuu8t23+vSQhCQBJ3E8SkogIJCEJAElcddVVV1111VVX/R9EcNVVV1111VVXXXXVVf+DvOM7vuNn8e8giQeSBIAkJCEJSUhCEpKQBIAkJPHcNjc3OXv27DO46qqrrrrqqquu+t+HylVXXXXVVVddddVVV/0PI4n/DJKQhCQkIQlJSEISz00SkrDNVVddddVVV1111f9SVK666qqrrrrqqquuuup/kHvvvRfb/GvY5oEk8UCSkASAJCQhCUncTxKSAJDEA21tbXHVVVddddVVV131vxTBVVddddVVV1111VVX/Q9xzTXXPPglXuIlHiyJF8Y2/xJJSEIS95OEJCQhCUlIQhLPTRL3s8199913K1ddddVVV1111VX/+xBcddVVV1111VVXXXXV/xD33XffrQCPfvSjeVHZ5l8iCUlIQhKSkASAJB5IEveThCSuuuqqq6666qqr/hcjuOqqq6666qqrrrrqqv9B/uEf/uG3X/3VX53nxzYviCQk8dwkASCJ+0lCEpIAkIQkJPH8XHPNNZw9e/ZWrrrqqquuuuqqq/73Ibjqqquuuuqqq6666qr/QX7rt37rex796Edz6tQpXhjb2Oa5SeJ+kgCQBIAkJCEJSUhCEv+S5XL5jPvuu+9Wrrrqqquuuuqqq/73Ibjqqquuuuqqq6666qr/QX7rt37ruzPz1rd4i7fg30oSknggSUhCEpKQhCQkIQlJPD+PetSjuO+++57OVVddddVVV1111f9OBFddddVVV1111VVXXfU/zI/+6I9+zsMe9rBb3/RN3xQA2/xbSUISAJKQhCQkIYkXRBKLxYJHPepR/NZv/db3cNVVV1111VVXXfW/E5Wrrrrqqquuuuqqq676H+a3fuu3vvvMmTMPeqd3eqfPlsSv/uqv8m8hiftJAkASkpCEJCRxP0kASGI2m/HIRz6Sf/iHf/jt3/qt3/purrrqqquuuuqqq/53Irjqqquuuuqqq6666qr/gX7rt37ru//hH/7ht9/kTd6Ez/iMz+DEiRP8W0hCEgCSkIQkJHE/STxQrZWHPvShnDhxgh/5kR/5HK666qqrrrrqqqv+96IcP36cq6666qqrrrrqqquu+p/m6Ojo0j/8wz/8zq233vo3r/Var/XWL/7iL87GxgZPe9rTkIQkJCEJSZRSqLVSSqGUQimFUgq1Vmqt9H3PfD5nc3OT7e1ttre32djYoO97JJGZrFYrVqsVJ0+e5ClPecpvf/3Xf/37/MM//MNvc9VVV1111VVXXfW/F+X48eNcddVVV1111VVXXXXV/0SHh4e7R0dHu7feeuvfvNZrvdZbP+xhD+PlX/7lWSwWPP3pT0cSkgAopVBKoZRCKYVSCrVWSil0XUfXdcznczY2Ntje3mZ7e5uNjQ26rkMS586dY29vj4jgvvvuu/W7vuu7PuYf/uEffpurrrrqqquuuuqq/90ox48f56qrrrrqqquuuuqqq/6nOjw83L311lv/+rd/+7e/5yEPechL33LLLQ9+6EMfysu93Mtxww03sFqt2N3dpdZKKYVSCqUUSimUUqi1Umul73vm8zmbm5tsb2+zvb1N13UcHBxw++23s1qtuPfee2+99dZb//pLv/RL3+bWW2/9a6666qqrrrrqqqv+90MPetCDuOqqq6666qqrrrrqqv8tXuzFXuy13+md3umzXuzFXuy1eaaLFy9y2223cfvtt3N4eEitleVyyWq1YjabcerUKTY2Njh9+jTXX389W1tbzOdzbHO/++6779Yf/dEf/Zzf+q3f+m6uuuqqq6666qqr/u9AD3rQg7jqqquuuuqqq6666qr/ba655poHv/Zrv/Z7vfiLv/hrv9iLvdhr829w33333fpbv/Vb33327Nln/NZv/dZ3c9VVV1111VVXXfV/D3rQgx7EVVddddVVV1111VVX/W92zTXXPPjFXuzFXvvFXuzFXuuaa655MMCZM2cefM011zwY4L777rsV4OzZs7fed999t9533323/sM//MPv/MM//MNvc9VVV1111VVXXfV/G3rQgx7EVVddddVVV1111VVXXXXVVVddddVVV131fxKVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1X8I75L/M2Yj6jAAAAAAElFTkSuQmCC) @@ -74,7 +74,7 @@ sweepSketch = startSketchOn(XY) import "tests/inputs/cube.sldprt" as cube cube - |> scale(x = 1.0, y = 1.0, z = 2.5) + |> scale(z = 2.5) ``` ![Rendered example of scale 1](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABQAAAALQCAYAAADPfd1WAAEj5UlEQVR4Ae3AA6AkWZbG8f937o3IzKdyS2Oubdu2bdu2bdu2bWmMnpZKr54yMyLu+Xa3anqmhztr1a8+6EEP4qqrrrrqqquuuuqqq6666qqrrrrqqquu+j+JylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrvoXvNiLvdhrc9VVV/2PdPbs2Vvvu+++W7nqqquuuuqqq656/qhcddVVV1111VVXvQAv9mIv9tof/uEf/l3XXHPNg7nqqqv+R/qHf/iH3/77v//73/7RH/3Rz+Gqq6666qqrrrrqeaEHPehBXHXVVVddddVVVz23d3zHd/ysd3qnd/rsv/iLv+DHfuzHuOr/D9tc9b/H67/+6/MGb/AG/MM//MNvf+ZnfubrcNVVV1111VVXXfWc0IMe9CCuuuqqq6666qqr7nfNNdc8+MM//MO/68Ve7MVe+9d//df59V//da666v8j2/xvceLECT7ogz6IcRxv/azP+qzXue+++27lqquuuuqqq6666gr0oAc9iKuuuuqqq6666iqAa6655sGf8zmf81td1z34x3/8x3na057GVf+32eaq/xtOnDjBB37gBzKO462/9Vu/9d0/+qM/+jlcddVVV1111VVXAeX48eNcddVVV1111VVXvc7rvM57f+7nfu5v3Xvvvce/7uu+josXL3LV/32SkIQkJCEJSUhCEpKQhCQkIQlJSEISkpCEJCQhCUlIQhKSkIQkJCEJSUhCEpKQhCQk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IEn8R7LNCyKJq/53sM3/FLZ5bhsbGwDcd999t3LVVVddddVVV/1vR+Wqq6666qqrrvo/ZWNjg8PDQyTxorANgCReFLYBkMSLwjb3k8SLyjYAkvjXsM39JPFvYZv7SeLfwzbPTRL/GWzzwkjiqv9atvmfyDYvzMbGBlddddVVV1111f8ZBFddddVVV1111f8J99133608gG1s86KyjW1eVLaxzb+GbWzzr2Eb29jmX8s2trGNbf4tbGMb29jmP4JtbGMb29jmv4JtbGMb29jGNraxjW2uetHZxja2sY1tbGMb29jmfwrb2MY2tnlRnT179lauuuqqq6666qr/7Qiuuuqqq6666qr/E86ePXsrwMbGBg9kG9u8qGxjmxeVbWxjmxeVbWxjm38N29jGNv8WtrGNbf6tbGMb29jmP4ptbGMb29jGNv/VbGMb29jGNraxjW1sYxvb2MY2/5fYxja2sY1tbGMb29jGNrb5n8w2trGNba666qqrrrrqqv/XqFx11VVXXXXVVf8v2AZAEi8K2wBI4kVlGwBJvKhscz9JvKhscz9J/GvZ5oEk8W9hm+cmif8otnl+JPE/hW2u+u9lm/9IGxsbANx33323ctVVV1111VVX/W9HcNVVV1111VVX/Z9w33333QqwubnJC2Mb27yobGMb27yobGMb2/xr2MY2/1q2sY1t/q1sYxvb/HvZxja2sc1/BtvYxja2sY1trvq/zza2sY1tbHPVVVddddVVV131QlC56qqrrrrqqqv+T9nY2OBFYZv7SeJFYRsASbyobAMgiReVbe4niX8N2zyQJP61bPNAkvj3sM1zk8R/Btu8MJK46n8H2/x32NjYAOC+++67lauuuuqqq6666v8CKlddddVVV1111f8J99133608k20AJPGisA2AJF4UtgGQxIvKNveTxIvKNveTxL+Wbe4niX8L2zyQJP69bPPcJPGfzTYvjCSu+q9lm/9JNjY2ADh79uytXHXVVVddddVV/xdQueqqq6666qqr/s+yDYAkXhS2AZDEi8I295PEi8o2AJL417DN/STxr2Wb+0ni38o2DySJ/wi2eW6S+K9kmxeVJK564Wzzv83GxgYA9913361cddVVV1111VX/F1C56qqrrrrqqqv+Tzh79uwzADY3N3lutgGQxIvCNgCSeFHZBkASLyrb3E8S/xq2uZ8k/rVs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sI1t/j1sY5t/D9vY5t/LNrb597LNfxTb/HvZxjb/Xrb597DNv4dt/ivZ5j+bJP6zSOKqq6666qqrrvp/jeCqq6666qqrrroKsI1t/j1s8+9lm38v29jm38s2/162sc1/BNvY5t/LNv9etvn3sM2/h23+LWzzX8E2/9NtbGwAcN999z2dq6666qqrrrrq/zKCq6666qqrrrrq/7SzZ8/eCrC9vc2Lwjb/Hraxzb+HbWzz72Ub2/x72MY2/162sc1/BNvY5t/DNrb597CNbf6tbHPVfy5JvCCbm5tcddVVV1111VX/LxBcddVVV1111VVXPRfb2Obfwza2+fewjW3+vWzz72Ub2/x72cY2/xFs8+9lm38v2/xb2ebfyjb/Frb517LN/xSS+I909uzZZ3DVVVddddVVV/1fRnDVVVddddVVV131AtjGNv8etrHNv4dtbPPvYRvb/HvZxjb/Xrb5j2Ab2/x72MY2/x62+beyzb+Vba56/iRx1VVXXXXVVVddBRBcddVVV1111VX/p9133323AmxtbfFvZZt/L9v8e9nGNv8etrHNv5dt/r1sY5v/CLb597LNv4dt/q1s81/JNv/fbWxsAHD27Nlbueqqq6666qqr/i8juOqqq6666qqr/s+77777bgXY2tri38o2tvn3sI1t/r1s8+9lG9v8e9jGNv9etvmPYBvb/HvY5t/DNv9Wtvm3sM3/F5L4j7KxsQHAfffddytXXXXVVVddddX/ZQRXXXXVVVddddX/G1tbW9jm38M2tvn3sI1t/j1sY5t/L9v8e9nGNv8etrHNfwTb/HvY5t/DNv9Wtvm3sM1/Ntv8byGJq6666qqrrrrqqmciuOqqq6666qqr/s87e/bsrTyAbWzz72Eb2/x72MY2/x62sc2/h21s8+9lm38v29jm38s2/x62sc2/lW3+rWzzX8E2/59tbGwAcN99993KVVddddVVV131fxnBVVddddVVV131/5ZtbPPvYZt/L9v8e9nm38s2tvn3sI1t/r1s8+9lG9v8e9jm38o2/5VscxVI4qqrrrrqqquuuuoBCK666qqrrrrqqv/z7rvvvlsBtra2eH5s8+9hG9v8e9jGNv8etrHNv5dt/r1sY5t/D9v8R7DNv4dt/q1s829hm/8KtvnvJokXhST+o2xubgJw33333cpVV1111VVXXfV/HcFVV1111VVXXfV/3n333XcrwNbWFi+IbWzz72Eb2/x72MY2/x62sc2/h21s8+9lm38P29jm38s2/x62+beyzb+Fbf61bPP/mSReFBsbGwCcPXv2Vq666qqrrrrqqv/rCK666qqrrrrqqqsewDa2+fewzb+Xbf69bPPvZRvb/HvYxjb/Hrb597LNv4dt/q1sc9X/DJK46qqrrrrqqqv+XyG46qqrrrrqqqv+zzt79uwzALa3t3lR2ebfwza2+fewjW3+PWxjm38v2/x72ebfwza2+fewjW3+rWzzX8k2/1q2+c9im/8OkviPIAmAjY0NAO67775bueqqq6666qqr/q8juOqqq6666qqr/s+77777bgXY2triX8M2tvn3sI1t/j1sY5t/D9vY5t/DNrb597CNbf49bPPvZZt/K9v8W9jmfyLb/F8giauuuuqqq6666qrng+Cqq6666qqrrrrqX2Cbfy/b/HvZ5t/LNv9etvn3ss2/h23+vWzzb2Wbfwvb/GvZ5qp/P0ncb2NjA4D77rvvVq666qqrrrrqqv/rCK666qqrrrrqqv83tre3+beyjW3+PWxjm38P29jm38M2tvn3sI1t/j1s8+9hm38v2/xb2ebfwjb/Wrb517DN/waS+I8giauuuuqqq6666qoXgOCqq6666qqrrvo/7+zZs7cC2MY2/x62sc2/h21s8+9hm38v2/x72ebfwza2+beyjW3+PWzzb2Wbq/5zSeLfSxIPtLGxAcDZs2efwVVXXXXVVVdd9X8dwVVXXXXVVVdd9f+ObWzz72Gbfy/b/HvYxjb/Hraxzb+HbWzz72Gbfw/b/HvY5r+Sbf61bPOfxTZXXXXVVVddddVV/0cRXHXVVVddddVV/+fdd999twJsb2/zQLb597CNbf49bGObfw/b2Obfwzb/Xrb597DNv4dt/j1s829hm38L2/xnss3/B5J4YSTx3DY2NgC47777buWqq6666qqrrvq/juCqq6666qqrrvp/4b777rsVYGtriweyjW3+PWzz72Wbfy/b/HvYxjb/Hrb597CNbf6tbPPvYZt/C9v8V7DN/yeS+M+wsbEBwNmzZ2/lqquuuuqqq676v47gqquuuuqqq666CrDNv4dtbPPvYRvb/HvYxjb/Hrb597CNbf49bPNvZZt/D9v8W9jmX8s2V/3bSeKFkcRVV1111VVXXfX/HsFV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M9nmP4NtXhS2ueo/hiT+PSTxbyGJ/0iS+LeQxL+GJO63WCwAuO+++27lqquuuuqqq676/47gqquuuuqqq6666t/BNv9atvnXsM1V//tJ4vmRxP9kkvjXkMSLShIvCkncTxL3k8TzM5/PATh79uytXHXVVVddddVV/98RXHXVVVddddVVVwH33XffrQDb29v8a9nmfxLbvKhs89/BNlf995PEv5YkHkgSLypJvCCSuOqqq6666qqrrvpPQHDVVVddddVVV10F3HfffbcCbG9v81/BNv8atvnvZpv/Sra56jlJ4v8DSfxbSWKxWHDVVVddddVVV131TARXXXXVVVddddVVz8U2/1q2+Z/ENi8q2/xHss1V//NI4l8iif9Ikvj3ksT9JHE/SdxPEs/PfffddytXXXXVVVddddX/dwRXXXXVVVddddVVz4dt/rVs869hm38N21z1P5sk/qtJ4rlJ4j+CJP41JPGiksRVV1111VVXXXXVfwGCq6666qqrrrrqKuDs2bO3Amxvb/NfyTb/WWzzH802V/3/JYkHksSLShIviCT+I0hisVgAcPbs2Wdw1VVXXXXVVVddBQRXXXXVVVddddVVwH333XcrwM7ODvezzb+Wbf4z2eY/g23+q9nm38s2/xdJ4j+bJP4zSeLfQhL3k8T9JHE/SdxPEg80n88BuO+++27lqquuuuqqq666Cgiuuuqqq6666qqrXgjb/GvZ5l/DNv9ZbPPfwTZX/e8iif8qkvjPslgsALjvvvtu5aqrrrrqqquuugoIrrrqqquuuuqqq4CzZ8/eCrC9vc1zs83/JLb5/8I2/x62+b9CEv9WkvjXksQLI4kHksQLIokXRBL/ESRx1VVXXXXVVVdd9XwQXHXVVVddddVVVwH33XffrQA7Ozv8R7DNv4Zt/rvZ5kVhm6v+Y0ji30MSz00S/xkk8Z9NEveTxP0kcT9J3E8SV1111VVXXXXVVf8Cgquuuuqqq6666qoXgW3+J7HNi8o2V/33ksT/JJL4zySJ/y6LxQKA++677+lcddVVV1111VVXAcFVV1111VVXXXUVcN99990KsL29zQtim38N2/xr2Ob/Ettc9f+DJF5UkviPJon7zedzAM6ePfsMrrrqqquuuuqqq4Dgqquuuuqqq6666j+Rbf41bPOiss2LyjYvCtu8KGzzH8E2V/3nksS/RBIP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XXXVsxFcddVVV1111VVXPcDZs2dvBdje3uZfYpt/Ddtc9T+fJK4CSbwgknhBJHE/SfxrSOLfaj6fA3D27NlncNVVV1111VVXXfVsBFddddVVV1111VX/A9nmRWGbq/7vk8S/RBL/HpL4t5DEi0IS95PE/STxopLEc5PEVVddddVVV1111QtBcNVVV1111VVXXfXvYJv/DWzzL7HNv5dtXhjbXPVfQxIPJIkHksSLShL/XSTxopjP5wDcd999t3LVVVddddVVV131bARXXXXVVVddddVVD3DffffdCrCzs8N/Btv8b2abq/5lkvi3ksT/VpL4t5LE/STxwkjiuUniqquuuuqqq6666vkguOqqq6666qqrrnou9913360AOzs7vChs85/BNi8K21z1P58knpsk/i0k8Z9JEi+IJF4UkrifJO4niftJ4kUliecmifvN53MAzp49eytXXXXVVVddddVVz0Zw1VVXXXXVVVdd9QJsb2/zn8E2V/3fIIn/CSTx7yGJfwtJ/GeSxFVXXXXVVVddddW/E8FVV1111VVXXXXVczl79uyt/CvZ5j+Dbf6j2OZfYpur/meRxL+WJB5IEg8kiReVJP4rSeKFkcRzk8R8Pgfgvvvuu5WrrrrqqquuuuqqZyO46qqrrrrqqquu+m9gm/9Itvm/zjZX/eeTxAsiiRdEEveTxL9EEveTxAsjiauuuuqqq6666qp/I4Krrrrqqquuuuqq53LffffdCrC9vY1tXlS2uQps829lm//PJPEvkcT/JpK4nyT+o0jifvP5HID77rvvVq666qqrrrrqqqueE8FVV1111VVXXXXVc7nvvvtuBdjZ2eE/k21eFLb5j2Kbfy/bXPV/hyReEEm8IJL4zySJ5yaJ5yaJ+XwOwNmzZ2/lqquuuuqqq6666jkRXHXVVVddddVVV/0LbPOiss1/F9v8R7DNVc9JEi8qSTw3STw3SfxHkMQDSeKBJPFAkviPJol/K0ncTxL/FvP5HID77rvvVq666qqrrrrqqqueE5Wrrrrqqquuuuqq53L27NlnAOzs7HDVVf+dJPFfSRIviCReFJK4nyTuJ4n7SeJFJYnnJomrrrrqqquuuuqqFxHBVVddddVVV1111XO57777bgXY2dnhfrZ5UdnmRWWbF4Vt/q+wzVX/u0niP5Mkrrrqqquuuuqqq/6DEFx11VVXXXXVVVf9H2Kbf4ltrnr+JPGiksR/NUn8R5LECyKJ/0qSeGEk8dwkATCfzwG47777buWqq6666qqrrrrqORFcddVVV1111VVXvQDb29s8kG1eVLZ5UdnmRWGb/ylsc9V/LEn8a0nigSTxQJJ4IEn8R5PE/STxL5HE/STxwkjiuUniuS0WCwDOnj37DK666qqrrrrqqqueE8FVV1111VVXXXXVczl79uyt/D9nm6v+f5HECyKJfy1J3E8SLypJXHXVVVddddVVV/0HIrjqqquuuuqqq676V7DNi8o2/1/Z5n8CSTw/kvjvJIl/iST+M0ni30IS/9Ek8cJI4rlJ4n7z+ZyrrrrqqquuuuqqF4Dgqquuuuqqq6666rncd999twLs7OzwX8U2Lwrb/Ets8y+xzVX/8STx3CTxv40kXhSS+LeSxAsjiReFJO5333333cpVV1111VVXXXXVcyK46qqrrrrqqquuej7uu+++WwF2dnZ4brZ5Udnmqqv+I0jiX0MSDySJfwtJvCgkcT9J3E8S95PEv4cknp/5fA7A2bNnb+Wqq6666qqrrrrqORFcddVVV1111VVX/Q9hm6v+55HEfzZJ/GtJ4oEk8W8lif8JJPHcJPHcJHHVVVddddVVV131IiK46qqrrrrqqquuej7Onj17K8D29jbPj23+u9jmX2Kbfy/bXPV/kyT+LSTxH0ES95PEv9d8Pgfgvvvuu5Wrrrrqqquuuuqq50Rw1VVXXXXVVVdd9Z/MNv8T2ebfwzZX/etJ4l8iif8uknhRSOJ+knh+JHE/SbyoJPHcJPHcJHHVVVddddVVV131LyC46qqrrrrqqquuej7uu+++WwF2dnZ4QWzzH802V/3PJ4n/TSTxbyGJfy1J/FtI4t9iPp8DcN99993KVVddddVVV1111fMiuOqqq6666qqrrno+7rvvvlsBdnZ2+I9gm/9ItvnfzDb/H0jiP4IkHkgSDySJB5LEi0oSLwpJ/EeTxAsjiecmiQeaz+cAnD179lauuuqqq6666qqrnhfBVVddddVVV1111b+Dbf6j2eY/gm2u+q8jiX8LSfxXksQLIon/bJJ4YSTx3CTx3CQBMJ/Pueqqq6666qqrrnohCK666qqrrrrqqquej7Nnzz4DYGdnh/8otvnfxjb/Vra56v8mSdxPEveTxP0kcT9JvDCS+LeSBMB99913K1ddddVVV1111VXPi+Cqq6666qqrrrrqhdje3uZfYpv/rWxz1RWS+J9AEv+RJPFvIYn/TJJ4YSTx3CRx1VVXXXXVVVdd9a9EcNVVV1111VVXXfV83Hfffbfy38g2/xLb/Ets85/JNv/RbPN/lST+tSTxryGJF5UkXhSS+I8gif8IkrjfbDYD4L777ruVq6666qqrrrrqqudFcNVVV1111VVXXfUfwDYvCttcddW/hyQeSBIvKkm8IJJ4UUjifpJ4fiRxP0m8qCTx3CTxwszncwDOnj37DK666qqrrrrqqqueF5Wrrrrqqquuuuqq5+Ps2bO3Auzs7HDV/1+SuOpFI4l/C0k8N0k8N0k8N0lcddVVV1111VVX/QsIrrrqqquuuuqqq/6D2OZFYZsXhW3+Jbb597LNVS86SbwoJPEvkcR/Jkm8IJJ4QSRxP0n8R5PEf5T5fM5VV1111VVXXXXVC0Fw1VVXXXXVVVdd9Xzcd999twLs7Oxgm//NbPPvYZurXjhJ/EeQxL+HJP4nkcT9JPHCSOK5SeK5SeL5ue+++27lqquuuuqqq6666nkRXHXVVVddddVVV70A9913360AOzs7vKhsc9VV/1Ek8UCSeCBJvKgk8YJI4kUhiftJ4n6SuJ8kXlSS+LeSBMB8Pgfg7Nmzt3LVVVddddVVV131vAiuuuqqq6666qqrXgS2+Y9kmxeFbf4ltrnqRSeJ/w6S+N9AEv+ZJPHCSOK5SeKqq6666qqrrrrq34jgqquuuuqqq6666gW47777bgXY2dnhqn8b21z130MSL4gkXhSS+I8giRdGEi8KSTy3+XwOwH333XcrV1111VVXXXXVVc+L4KqrrrrqqquuuupFZJsXhW1eFLb5r2KbF8Y2/1a2uepfTxL/kSTxn0kS/xJJ3E8S/x6SeEEkcdVVV1111VVXXfUiIrjqqquuuuqqq656Ac6ePXsrwM7ODv+dbHPV/y6S+J9EEi+IJP61JHE/SfxbSOK5SeK5SeIFWSwWAJw9e/YZXHXVVVddddVVVz1/BFddddVVV1111VUvwH333XcrwPb2NvezzYvCNv+VbPOfyTZX/eeSxANJ4oEk8UCS+I8mif9Mkvi3ksRzm81mANx33323ctVVV1111VVXXfX8EVx11VVXXXXVVVf9N7LN/1e2+Z9CEi8KSfxvIIkXRBIvCkn8W0nifpJ4YSTx3CRx1VVXXXXVVVdd9R+E4KqrrrrqqquuuuoFOHv27K0AOzs7PJBtXhS2+Y9im38v21z1opPEi0IS/xJJ/G8miftJ4n6SuJ8kXlSSeFFI4rlJ4n7z+RyA++6771auuuqqq6666qqrnj+Cq6666qqrrrrqqv8jbPPvYZur/neSxL+FJP6rSOKFkcS/hiSuuuqqq6666qqrXgQEV1111VVXXXXVVS/AfffddyvAzs4Oz802/1Fsc9X/L5L495DEi0oSLwpJ/EeTxAsjiecmiecmiednPp8DcN999z2dq6666qqrrrrqqueP4Kqrrrrqqquuuuo/kW3+o9jmqv+bJPFAknggSfxHkMSLQhL/GpK4nyReGEn8W0nigebzOQBnz559BlddddVVV1111VXPH8FVV1111VVXXXXVC3DffffdCrCzs8N/Ntv8T2ebF8Q2/9NI4r+DJP47SeI/kiTuJ4n7SeI/iiSemyReEElcddVVV1111VVXvYgIrrrqqquuuuqqq/6NbPOisM1/Fdu8MLa56v8WSfxbSOK/iiSemyReFJK46qqrrrrqqquu+ncguOqqq6666qqrrnoBzp49+wyAnZ0d/qewzX8m2/xHs81V/z0k8aKQxP0k8W8liftJ4l9LEi+IJJ7bfD4H4OzZs7dy1VVXXXXVVVdd9fwRXHXVVVddddVVV70Q9913360AOzs7PD+2+Y9im6v+75HEfyRJ/HeTxP0k8aKSxHOTxHOTxAsiifvNZjMA7rvvvlu56qqrrrrqqquuev4Irrrqqquuuuqqq/4L2Oaq/90k8dwk8a8liX8NSbyoJPGCSOJfSxL3k8S/hST+M0jiqquuuuqqq6666kVAcNVVV1111VVXXfVCnD179laAnZ0dXhDb/E9imxfGNlf9zyGJB5LEfzZJ/GeSxAsjiecmiecmiRdmPp8DcN99993KVVddddVVV1111fNHcNVVV1111VVXXfVfxDb/Etv8S2xz1VUAknhBJPGikMT9JPFvJYkXRhL/VpK46qqrrrrqqquu+jciuOqqq6666qqrrnoh7rvvvlsBdnZ2eGFs8/+Bbf43k8SLQhL/FpL4v0IS95PE/SRxP0n8e0jiuUniBZHE/ebzOQD33XffrVx11VVXXXXVVVe9YARXXXXVVVddddVVL8R99913K8D29jb/X9jm38I2/xtJ4n8LSfxbSOK/gySemyReFJJ4QSQxm80AOHv27K1cddVVV1111VVXvWAEV1111VVXXXXVVf+FbPMvsc2/xDYvjG2u+t9PEi8qSbwoJPGfSRL/WpJ4QSRx1VVXXXXVVVdd9e9AcNVVV1111VVXXfUi2NnZ4V9im/8tbHPVfz5J/GtI4r+SJO4niX8NSdxPEi+MJJ6bJJ6bJF4QSTzQfD4H4L777ruVq6666qqrrrrqqheM4KqrrrrqqquuuuqFOHv27DP4D2abq66wzfNjm/+LJPFAkvi3ksQLIol/D0ncTxL3k8SLShL/VpJ4QSRx1VVXXXXVVVdd9a9AcNVVV1111VVXXfVC3HfffbcC7Ozs8KKwzX8E2/xLbHPVfx9J/G8gif8qknhhJPHcJPFvMZ/PAbjvvvtu5aqrrrrqqquuuuoFI7jqqquuuuqqq656EdnmfxPb/FvZ5j+Sba7695HECyKJF4Uk/qNJ4oWRxL+VJF6Q2WwGwNmzZ5/BVVddddVVV1111QtGcNVVV1111VVXXfVCnD179laAnZ0dXlS2+ZfY5n8r21z1v58k7ieJfw1J/HtI4rlJ4gWRxFVXXXXVVVddddW/EcFVV1111VVXXXXVv4Jt/qvY5j+Tba560UjiXyKJ/0iS+LeQxL+HJO4niftJ4kUliReFJJ6bJF4QSVx11VVXXXXVVVf9KxFcddVVV1111VVXvRD33XffrQA7Ozv8T2Sbq/5vkMSLShL/00jihZHEv4YkXhBJzOdzAO67775bueqqq6666qqrrnrBCK666qqrrrrqqqv+Bffdd9+tADs7O7yobPMvsc1/Ntv8V7LNVVdI4oEk8UCS+M8mif9MknhhJPHcJPHcJPGCSOL5mc1mAJw9e/ZWrrrqqquuuuqqq14wgquuuuqqq6666qp/Jdv8V7HNVVdJ4gWRxItCEveTxL+VJP6zSOIFkcRVV1111VVXXXXVvwHBVVddddVVV1111b/g7NmztwLs7Ozwr2Gb/+ls84LY5qr/+yTx/EjifpJ4UUniuUniuUni30ISAPP5HID77rvvVq666qqrrrrqqqteMIKrrrrqqquuuuqqfwPb/Eewzb+Xba76jyOJ/26S+LeQxL+HJP4tJPEfSRJXXXXVVVddddVV/0EIrrrqqquuuuqqq/4F9913360AOzs7/Hewzb+Hbf6j2eaq/x6SeFFI4j+TJF4YSTw3STw3Sbwgknh+ZrMZAPfdd9+tXHXVVVddddVVV71wBFddddVVV1111VUvop2dHR7INv8S2/xLbHPV/y6S+I8kif9MkviPIIkXRhL/VpJ4QSTxQPP5HICzZ8/eylVXXXXVVVddddULR3DVVVddddVVV131L7jvvvtu5X8421z1/48k/rUkcT9JPD+SuJ8k/j0k8dwk8YJI4gWRBIAkrrrqqquuuuqqq15EBFddddVVV1111VX/Drb5l9jm38s2/1ls81/BNv9etvmfSBL/GpJ4UUniv4Ik/i0k8W8liRdEEi/IbDYD4L777ruVq6666qqrrrrqqheO4KqrrrrqqquuuupfcPbs2WcA7Ozs8J/FNv+ZbPNvYZt/Ldv8fyeJB5LEfzZJ/FeRxAsjiecmiX8LSVx11VVXXXXVVVf9OxBcddVVV1111VVX/Qvuu+++WwF2dna46j+ebf67SOL/Ikn8R5PECyOJF4Uknpsk/iWSuN9sNgPgvvvuu5WrrrrqqquuuuqqF47gqquuuuqqq6666t/JNv8S2/xns81V//Ek8V9JEi+IJP61JPFvJYl/D0n8a0jiXyKJ+XwOwNmzZ5/BVVddddVVV1111QtHcNVVV1111VVXXfUvOHv27K0AOzs7/GeyzQtjm/9JbHPV/y6SuJ8k7ieJ+0niRSWJfytJvCCSuOqqq6666qqrrvoPQnDVVVddddVVV131H8A2/xLb/HeyzQtim6v+e0ji30IS/x0k8cJI4rlJ4gWRxAsiiauuuuqqq6666qp/JypXXXXVVVddddVV/4L77rvvVoCdnR2uuuq/kiT+J5DECyOJF4Uk/i0k8UCz2QyA++6771auuuqqq6666qqrXjiCq6666qqrrrrqqn+FnZ0dXhDb/HvZ5oWxzQtjm/9otrnqP44k/qNJ4j+aJP49JPGvIYl/iSQA5vM5AGfPnr2Vq6666qqrrrrqqheO4KqrrrrqqquuuupFcN99993KfwDbXHXVfyZJ3E8S/xqSeFFJ4t9KEi+IJAAkcdVVV1111VVXXfUfgOCqq6666qqrrrrqRXD27NlbAXZ2drjqv4Zt/i+QxItKEi+IJP49JHE/SdxPEv9RJPHcJPGCSOLfYjabAXDffffdylVXXXXVVVddddULR3DVVVddddVVV131H8g2/162eWFs88LY5gWxzQtim38t2zw/tvm/ShL/X0ni30oSL4gkXhBJAEjiqquuuuqqq6666t+A4KqrrrrqqquuuupFcN99990KsLOzw7+Xbf6/ss3/NZJ4IEn8Z5PEfxVJvDCSeG6S+LeQxL9kPp8DcN99993KVVddddVVV1111b+M4KqrrrrqqquuuupfwTb/Etv8X2Obq/5nksR/NEm8MJL4t5LEv0QSL8hsNgPg7Nmzt3LVVVddddVVV131LyO46qqrrrrqqquuehHcd999twLs7OzwH8E2L4xtrvq/TxIviCT+tSTxbyWJfw9JPDdJvCCSuOqqq6666qqrrvovQHDVVVddddVVV131r2Sbf4lt/jPZ5oWxzQtim6v+75PE/SRxP0ncTxIvKkn8W0niBZHECyIJAEncbz6fA3DffffdylVXXXXVVVddddW/jOCqq6666qqrrrrqRXD27NlnABw7doz/y2zz38E2/xNI4qoXnSSemyT+LSTxL5HEVVddddVVV1111b8SwVVXXXXVVVddddWL4L777rsVYGdnBwDb/HvZ5oWxzVX/t0ji30IS/x0k8R9JEv8SSbwws9kMgPvuu+9Wrrrqqquuuuqqq/5lBFddddVVV1111VX/SWzzn8k2/xPY5vmxzVUvOkn8TyCJF0YSz00Sz00SL4gk/q1msxlXXXXVVVddddVV/woEV1111VVXXXXVVS+Cs2fP3gqws7PD/xa2eUFs869lm6v++0niP5ok/jtI4gWRBIAknp+zZ88+g6uuuuqqq6666qp/GcFVV1111VVXXXXVv5Ft/iW2eWFs88LY5qqrXhBJ/GtI4t9CEv9WkviPIAkASVx11VVXXXXVVVf9KxBcddVVV1111VVXvQjuu+++WwF2dnb4n8Q2/9Fs85/JNv9fSOK/iiTuJ4n7SeL5kcS/hySemyReEEm8IJIAkMS/ZDabAXDffffdylVXXXXVVVddddW/jOCqq6666qqrrrrq38E2/162ueqqB5LEfzdJ/E80m80AOHv27K1cddVVV1111VVX/csIrrrqqquuuuqqq15E9913360AOzs7/GvY5t/DNv9WtnlBbHPVCyeJ/yyS+N9GEs9NEs9NEi+IJP4lkgCQxFVXXXXVVVddddW/E8FVV1111VVXXXXVi+js2bO3Auzs7PBAtvn/wjbPj22u+t9PEv+RJPGCSOJFJQkAScxmMwDuu+++W7nqqquuuuqqq676lxFcddVVV1111VVX/RewzQtjm38r2/xfZZvnZpv/qSTxH00S/5kk8cJI4kUhiX8LSVx11VVXXXXVVVf9JyK46qqrrrrqqquuehHdd999twLs7Ozw3Gzzn8k2/1a2+deyzX8E21z1H0cS/xNI4l9DEv8SSfxLZrMZAPfdd9+tXHXVVVddddVVV71oCK666qqrrrrqqquu+h/BNlf9x5LE/SRxP0m8MJL4zyCJf4kkXpDZbAbA2bNnb+Wqq6666qqrrrrqRUNw1VVXXXXVVVdd9SK67777bgXY2dnh38I2L4xt/q1s829hm/8LbPO/mST+tSRxP0ncTxL/GSTx3CTx3CTxH0ESAJIAkMRVV1111VVXXXXVvwHBVVddddVVV1111X8Q2/xnss3/Fbb5v0ASLypJ/H8iiRdEEv8Ws9kMgPvuu+9Wrrrqqquuuuqqq140BFddddVVV1111VUvorNnzz4D4NixY/xb2eaFsc1/Btv8a9nm+bHN82Obq/73ksR/NUlcddVVV1111VVX/ScjuOqqq6666qqrrnoR3XfffbcC7Ozs8ILY5r+Lba666j+SJJ6bJJ6bJF4QSfxLJPGimM1mANx33323ctVVV1111VVXXfWiIbjqqquuuuqqq676X8Q2/9Fsc9X/HJL4ryKJ/yqS+JdIAkASV1111VVXXXXVVf9BCK666qqrrrrqqqteRGfPnr0VYGdnhxfGNi+MbV4Y2/xfYpur/n+QxH8kSQBIAmA+nwNw9uzZZ3DVVVddddVVV131oiG46qqrrrrqqquu+n/CNlf9/ySJ+0nihZHEi0IS/xaSuOqqq6666qqrrvovRHDVVVddddVVV131IrrvvvtuBdjZ2eFfYpsXxjb/VrZ5QWzzH8k2z49t/qezzf92kvi3ksR/FEn8a0jiXyKJf63ZbAbAfffddytXXXXVVVddddVVLxqCq6666qqrrrrqqn8D2/xnss1/Jdv8R7DNv4dtnpttrvq/SxIvqtlsBsDZs2dv5aqrrrrqqquuuupFQ3DVVVddddVVV131r3DffffdCrCzs8NV/z62uepFJ4n7SeJ+krifJP4tJPFvJYkXRBIvKklcddVVV1111VVX/ScguOqqq6666qqrrvpXuO+++24F2NnZ4V9imxfGNv9WtnlBbPOC2Ob/Atv8byaJ/+kk8dwk8W8hiReVJAAkASAJAEnMZjMA7rvvvlu56qqrrrrqqquuetFQueqqq6666qqrrvo3eJVXeRXuuOMO/iWS+PeQxL+FJP61JPGvIYl/DUm8KCTxopDEv0QSL4wkXhhJvDCSeGEk8fxI4gWRxPMjiedHEs+PJJ4fSTw3STw3STw3STw3STw3STyQJJ6bJB5IEg8kiQeaz+dcddVVV1111VVX/RtQueqqq6666qqrrvpX+NEf/dHPueaaax588803P/jmm2/mqquu+q/19V//9e/DVVddddVVV1111YsOPehBD+Kqq6666qqrrrrqX+PFXuzFXvvFXuzFXourrrrqv9TZs2ef8Vu/9VvfzVVXXXXVVVddddWLDj3oQQ/iqquuuuqqq6666qqrrrrqqquuuuqqq676P4nKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8V/wjJ/RBQDxGi4QAAAABJRU5ErkJggg==) @@ -108,12 +108,7 @@ sweepPath = startProfileAt([0, 0], sketch002) parts = sweep([rectangleSketch, circleSketch], path = sweepPath) // Scale the sweep. -scale( - parts, - x = 1.0, - y = 1.0, - z = 0.5, -) +scale(parts, z = 0.5) ``` ![Rendered example of scale 2](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABQAAAALQCAYAAADPfd1WAAEgoUlEQVR4Ae3AA6AkWZbG8f937o3IzKdyS2Oubdu2bdu2bdu2bWmMnpZKr54yMyLu+Xa3anqmhztr1a8+6EEP4qqrrrrqqquuuuqqq6666qqrrrrqqquu+j+JylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVS/AT/zET5hnWq/X973ru77rtVx11VVXXXXVVVdd9b8JwVVXXXXVVVddddVVVz0fP/ETP2EeYDabXfPiL/7i13DVVVddddVVV1111f8mBFddddVVV1111VVXXfUiesd3fMcf4aqrrrrqqquuuuqq/02oXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqhfRZ37mZ74OV1111VVXXXXVVVf9b0Jw1VVXXXXVVVddddVVz8fbvd3b6R/+4R9+kGd6u7d7O3HVVVddddVVV1111f82BFddddVVV1111VVXXfUCfOZnfua7cdVVV1111VVXXXXV/2YEV1111VVXXXXVVVdd9ULcd999twJcc801D+aqq6666qqrrrrqqv9tqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XEVx11VVXXXXVVVddddULcfbs2VsBzpw582Cuuuqqq6666qqrrvrfhspVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KoKrrrrqqquuuuqqq656Ie67775bAa655poHc9VVV1111VVXXXXV/zZUrrrqqquuuuqqq6666oW47777bgU4c+bMg7jqqquuuuqqq6666n8bKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddULcfbs2WcAXHPNNQ/mqquuuuqqq6666qr/bahcddVVV1111VVXXXXVC3HffffdCnDNNdc8mKuuuuqqq6666qqr/rehctVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV70QZ8+evRXgzJkzD+aqq6666qqrrrrqqv9tqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qoX4r777rsV4JprrnkwV1111VVXXXXVVVf9b0Plqquuuuqqq6666qqrrrrqqquuuuqqq676v4rgqquuuuqqq6666qqr/gX33XffrQDXXHPNg7nqqquuuuqqq6666n8TKlddddVVV1111VVXXXXVVVddddVVV1111f9VBFddddVVV1111VVXXfUvOHv27K0AZ86ceTBXXXXVVVddddVVV/1vQuWqq6666qqrrrrqqquuuuqqq6666qqrrvq/iuCqq6666qqrrrrqqqv+Bffdd9+tANdcc82Dueqqq6666qqrrrrqfxMqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666l9w33333Qpw5syZB3HVVVddddVVV1111f8mVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddW/4OzZs88AuOaaax7MVVddddVVV1111VX/m1C56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVV/4L77rvvVoBrrrnmwVx11VVXXXXVVVdd9b8Jlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXfUvOHv27K0AZ86ceTBXXXXVVVddddVVV/1vQuWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq676F9x33323AlxzzTUP5qqrrrrqqquuuuqq/02oXHXVVVddddVVV1111VVXXXXVVVddddVV/1cRXHXVVVddddVVV1111YvgvvvuuxXgmmuueTBXXXXVVVddddVVV/1vQXDVVVddddVVV1111VUvgrNnz94KcObMmQdz1VVXXXXVVVddddX/FlSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/VxFcddVVV1111VVXXXXVi+C+++67FeCaa655MFddddVVV1111VVX/W9B5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV70I7rvvvlsBzpw58yCuuuqqq6666qqrrvrfgspVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq656EZw9e/YZANdcc82Dueqqq6666qqrrrrqfwsqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666kVw33333QpwzTXXPJirrrrqqquuuuqqq/63oHLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqqteBGfPnr0V4MyZMw/mqquuuuqqq6666qr/LahcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVL4L77rvvVoBrrrnmwVx11VVXXXXVVVdd9b8FwVVXXXXVVVddddVVV72I7rvvvlsBrrnmmgdz1VVXXXXVVVddddX/BlSuuuqqq6666qqrrrrqqquuuuqqq6666qr/qwiuuuqqq6666qqrrrrqRXT27NlbAc6cOfNgrrrqqquuuuqqq67634DKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yqCq6666qqrrrrqqquuehHdd999twJcc801D+aqq6666qqrrrrqqv8NqFx11VVXXXXVVVddddWL6L777rsV4MyZMw/iqquuuuqqq6666qr/DahcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVL6KzZ88+A+Caa655MFddddVVV1111VVX/W9A5aqrrrrqqquuuuqqq15E9913360A11xzzYO56qqrrrrqqquuuup/AypXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVi+js2bO3Apw5c+bBXHXVVVddddVVV131vwGVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVdd9SK67777bgW45pprHsxVV1111VVXXXXVVf8bULnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vIrjqqquuuuqqq6666qp/hfvuu+9WgGuuuebBXHXVVVddddVVV131Px2Vq6666qqrrrrqqquuuuqqq6666qqrrrrq/yqCq6666qqrrrrqqquu+lc4e/bsrQBnzpx5MFddddVVV1111VVX/U9H5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+K4Kqrrrrqqquuuuqqq/4V7rvvvlsBrrnmmgdz1VVXXXXVVVddddX/dFSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVv8J99913K8CZM2cexFVXXXXVVVddddVV/9NRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVf8KZ8+efQbANddc82Cuuuqqq6666qqrrvqfjspVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq676V7jvvvtuBbjmmmsezFVXXXXVVVddddVV/9NRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVf8KZ8+evRXgzJkzD+aqq6666qqrrrrqqv/pqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VX/Cvfdd9+tANdcc82Dueqqq6666qqrrrrqfzoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1UEV1111VVXXXXVVVdd9a9033333QpwzTXXPJirrrrqqquuuuqqq/4nI7jqqquuuuqqq6666qp/pbNnz94KcObMmQdz1VVXXXXVVVddddX/ZFSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/VxFcddVVV1111VVXXXXVv9J99913K8A111zzYK666qqrrrrqqquu+p+MylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8V5fjx41x11VVXXXXVVVddddW/xid90if9NMArvuIrvvXGxsZf/fVf//UTueqqq6666qqrrrrqfyKCq6666qqrrrrqqquu+lf4iZ/4CfMAb/EWb/EzXHXVVVddddVVV131PxXBVVddddVVV1111VVX/Tt96Id+6Ldy1VVXXXXVVVddddX/RFSuuuqqq6666qqrrrrq3+mee+75Xa666qqrrrrqqquu+p+IylVXXXXVVVddddVVV/07/eRP/uT3c9VVV1111VVXXXXV/0QEV1111VVXXXXVVVdd9a/wdm/3drrnnnuezDO93du9nbjqqquuuuqqq6666n8qgquuuuqqq6666qqrrvpX+sZv/MYPBPiHf/iH3+aqq6666qqrrrrqqv/JqFx11VVXXXXVVVddddW/0tmzZ28FOHPmzIO56qqrrrrqqquuuup/MipXXXXVVVddddVVV131b3T27Nlbueqqq6666qqrrrrqfzIqV1111VVXXXXVVVdd9W905syZB3PVVVddddVVV1111f9kVK666qqrrrrqqquuuupf6b777rsV4JprrnkwV1111VVXXXXVVVf9T0blqquuuuqqq6666qqrrvof5XVe53Xe+x3f8R0/i/+FJMm2eQBJsu2zZ8/e+vVf//Xvc999993KVVddddVVV131X4XKVVddddVVV1111VVXXfU/xod/+Id/1+u8zuu8N8Af/dEf8W9lm/9pXvVVX/XBn/M5n/Nbn/VZn/U69913361cddVVV1111VX/FahcddVVV1111VVXXXXVVf/jnD9/nu/+7u/mv4Nt/jOcOnWKRz3qUQ9+x3d8x8/6+q//+vfhqquuuuqqq676r0Bw1VVXXXXVVVddddVV/wb33XffrQDXXHPNg7nqP9ypU6d45CMfyX8HSUhCEpKQhCQkIQlJSEISkpDEi+K7v/u7AXixF3ux177mmmsezFVXXXXVVVdd9V+B4Kqrrrrqqquuuuqqq676H+Oaa655MM/0Fm/xFvxvIQlJSEISkpCEJCQhiQsXLvCkJz2Ja6655sHv+I7v+FlcddVVV1111VX/FQiuuuqqq6666qqrrrrqqv9RfvzHfxyAU6dOIQlJSEISkvjf7Lu/+7sBePEXf/HXueaaax7MVVddddVVV131n43gqquuuuqqq6666qqrrvof48yZMw8+d+4cj3vc4zh16hSPfOQjeW6SkIQkJCEJSUhCEpKQhCQkIQlJSOK/2/nz53nSk57EmTNnHvSO7/iOn8VVV1111VVXXfWfjcpVV1111VVXXfW/2jXXXPPgM2fOPPiaa655MA9w33333XrNNdc8GOC+++67FeDs2bO33nfffbdy1VX/Ac6ePXvrNddc8+AzZ848+L777ruVq/5DPe5xj+Oxj30sb/7mb85XfuVX8h9FEv8WtvmP8t3f/d184Rd+IS/2Yi/22tdcc82D77vvvlu56qqrrrrqqqv+s1C56qqrrrrqqqv+V7jmmmsefObMmQe/2Iu92Gu9+Iu/+Gu/2Iu92GvzXO67775br7nmmgfzL7jvvvtuPXv27K333Xffrf/wD//wO7/1W7/13Vx11VX/I1xzzTUPPnv2LI973ON4+7d/e06dOsX/BJL417DNC3L+/Hme+MQn8qhHPerB7/iO7/hZX//1X/8+XHXVVVddddVV/1moXHXVVVddddVV/2Ndc801D36xF3ux136d13md93qxF3ux1+aZzp8/zxOf+ESe9KQnAXD+/HnOnTuHpAfzXCRx6tQpAB75yEcCcOrUqQe/2Iu92INf7MVejNd5ndd57w//8A//rvvuu+/W3/qt3/ruf/iHf/idf/iHf/htrrrqqv9WZ8+e5XGPexyPfexjeeQjH8mTnvQk/jeRxAvzvd/7vXzBF3wBL/ZiL/ba11xzzYPvu+++W7nqqquuuuqqq/4zULnqqquuuuqqq/7Hecd3fMfPep3XeZ33vuaaax4McO7cOX72Z3+WJz7xiTzpSU8CQBIPJIkHksRz+6M/+iPud+rUKQAe+chH8shHPpJXeZVXefA7vdM7fTbAfffdd+tv/dZvffeP/uiPfg5XXXXVf7mzZ88C8LjHPY7HPvaxvPmbvzlf9VVfxb/ENv9bnD9/nic96Uk88pGPfPA7vuM7ftbXf/3Xvw9XXXXVVVddddV/BipXXXXVVVddddX/GC/2Yi/22h/+4R/+Xddcc82Dz507x8/8zM/wsz/7s0gCQBIAkrifJJ6bJF4QSQBcuHABgD/+4z/mj//4j/mFX/gFAN7szd6MV3mVV3nwO73TO33267zO67z3b/3Wb333j/7oj34OV1111X+6a6655sE8wO/+7u/yWq/1Wpw6dYoXhST+rWzzX+17vud7+IIv+AJe7MVe7LWvueaaB9933323ctVVV1111VVX/UejctVVV1111VVX/be75pprHvzhH/7h3/ViL/Zir/2EJzyBL/uyL+P8+fNIQhIAkgCQxP0k8UCSeEEk8cKcP38egO/93u/lF37hF3jkIx/JK7/yKz/4nd7pnT77dV7ndd77R3/0Rz/nt37rt76bq6666j/d2bNnATh79ixnz57lsY99LI985CN50pOexH8WSfxr2ebf4/z58zzpSU/ikY985IPf8R3f8bO+/uu//n246qqrrrrqqqv+oxFc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uuqqq6666qqr/oPcd999t/JMkpCEJGwjCQDbANjGNgC2sY1tbHM/29jGNraxjW1sA2Ab29jGNraxjW0AbGMb29jGNraxDYBtbGMb29jGNrYBsI1tbGMbANvYxjb3s41tbANgG9sA2MY297MNgG0AbGMbANtcddVVV1111X8wKlddddVVV1111VVXXXXVVf9Bzp49+4x/+Id/+O0Xe7EXe21JlFK46l/vH/7hH36bq6666qqrrvqPgR70oAdx1VVXXXXVVVddddVVV131H+kd3/EdP+t1Xud13pur/tX+4R/+4be//uu//n246qqrrrrqqv8Y6EEPehBXXXXVVVddddVVV1111VVXXXXVVVddddX/SVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/FZWrrrrqqquuuuqqq6666qqrrrrqqquuuur/KipXXXXVVVddddVVV1111VVXXXXVVVddddX/VVSuuuqqq6666qqrrrrqqquuuuqqq6666qr/q6hcddVVV1111VVXXXXVVVddddVVV1111VX/V1G56qqrrrrqqquuuuqqq6666qqrrrrqqqv+r6Jy1VVXXXXVVVddddVVV1111VVXXXXVVVf9X0Xlqquuuuqqq6666qqrrrrqqquuuuqqq676v4rKVVddddVVV1111VVXXXXVVVddddVVV131fxWVq6666qqrrrrqqquuuuqqq6666qqrrrrq/yoqV1111VVXXXXVVVddddVVV1111VVXXXXV/1VUrrrqqquuuuqqq6666qqrrrrqqquuuuqq/6uoXHXVVVddddVVV1111VVXXXXVVVddddVV/1dRueqqq6666qqrrrrqqquuuuqqq6666qqr/q+ictVVV1111VVXXXXVVVddddVVV1111VVX/V9F5aqrrrrqqquuuuqqq6666qqrrrrqqquu+r+KylVXXXXVVVddddVVV1111VVXXXXVVVdd9X8Vlauuuuqqq6666qqrrrrqqquuuuqqq6666v8qKlddddVVV1111VVXXXXVVVddddVVV1111f9VVK666qqrrrrqqquuuuqqq6666qqrrrrqqv+rqFx11VVXXXXVVVddddVVV1111VVXXXXVVf9XUbnqqquuuuqqq6666qqrrrrqqquuuuqqq/6vonLVVVddddVVV1111VVXXXXVVVddddVVV/1fReWqq6666qqrrrrqqquuuuqqq6666qqrrvq/ispVV1111VVXXXXVVVddddVVV1111VVXXfV/Ff8Iv8z1O0aqcs4AAAAASUVORK5CYII=) diff --git a/docs/kcl/std.json b/docs/kcl/std.json index 082aafe04..b837e2325 100644 --- a/docs/kcl/std.json +++ b/docs/kcl/std.json @@ -267784,9 +267784,10 @@ "type": "number", "schema": { "$schema": "https://spec.openapis.org/oas/3.0/schema/2019-04-02#/definitions/Schema", - "title": "double", + "title": "Nullable_double", "type": "number", "format": "double", + "nullable": true, "definitions": { "Solid": { "type": "object", @@ -269371,9 +269372,9 @@ } } }, - "required": true, + "required": false, "includeInSnippet": true, - "description": "The scale factor for the x axis.", + "description": "The scale factor for the x axis. Default is 1 if not provided.", "labelRequired": true }, { @@ -269381,9 +269382,10 @@ "type": "number", "schema": { "$schema": "https://spec.openapis.org/oas/3.0/schema/2019-04-02#/definitions/Schema", - "title": "double", + "title": "Nullable_double", "type": "number", "format": "double", + "nullable": true, "definitions": { "Solid": { "type": "object", @@ -270968,9 +270970,9 @@ } } }, - "required": true, + "required": false, "includeInSnippet": true, - "description": "The scale factor for the y axis.", + "description": "The scale factor for the y axis. Default is 1 if not provided.", "labelRequired": true }, { @@ -270978,9 +270980,10 @@ "type": "number", "schema": { "$schema": "https://spec.openapis.org/oas/3.0/schema/2019-04-02#/definitions/Schema", - "title": "double", + "title": "Nullable_double", "type": "number", "format": "double", + "nullable": true, "definitions": { "Solid": { "type": "object", @@ -272565,9 +272568,9 @@ } } }, - "required": true, + "required": false, "includeInSnippet": true, - "description": "The scale factor for the z axis.", + "description": "The scale factor for the z axis. Default is 1 if not provided.", "labelRequired": true }, { @@ -275836,9 +275839,9 @@ "unpublished": false, "deprecated": false, "examples": [ - "// Scale a pipe.\n\n// Create a path for the sweep.\nsweepPath = startSketchOn(XZ)\n |> startProfileAt([0.05, 0.05], %)\n |> line(end = [0, 7])\n |> tangentialArc({ offset = 90, radius = 5 }, %)\n |> line(end = [-3, 0])\n |> tangentialArc({ offset = -90, radius = 5 }, %)\n |> line(end = [0, 7])\n\n// Create a hole for the pipe.\npipeHole = startSketchOn(XY)\n |> circle(center = [0, 0], radius = 1.5)\n\nsweepSketch = startSketchOn(XY)\n |> circle(center = [0, 0], radius = 2)\n |> hole(pipeHole, %)\n |> sweep(path = sweepPath)\n |> scale(x = 1.0, y = 1.0, z = 2.5)", - "// Scale an imported model.\n\n\nimport \"tests/inputs/cube.sldprt\" as cube\n\ncube\n |> scale(x = 1.0, y = 1.0, z = 2.5)", - "// Sweep two sketches along the same path.\n\n\nsketch001 = startSketchOn(XY)\nrectangleSketch = startProfileAt([-200, 23.86], sketch001)\n |> angledLine([0, 73.47], %, $rectangleSegmentA001)\n |> angledLine([\n segAng(rectangleSegmentA001) - 90,\n 50.61\n ], %)\n |> angledLine([\n segAng(rectangleSegmentA001),\n -segLen(rectangleSegmentA001)\n ], %)\n |> line(endAbsolute = [profileStartX(%), profileStartY(%)])\n |> close()\n\ncircleSketch = circle(sketch001, center = [200, -30.29], radius = 32.63)\n\nsketch002 = startSketchOn(YZ)\nsweepPath = startProfileAt([0, 0], sketch002)\n |> yLine(length = 231.81)\n |> tangentialArc({ radius = 80, offset = -90 }, %)\n |> xLine(length = 384.93)\n\nparts = sweep([rectangleSketch, circleSketch], path = sweepPath)\n\n// Scale the sweep.\nscale(\n parts,\n x = 1.0,\n y = 1.0,\n z = 0.5,\n)" + "// Scale a pipe.\n\n// Create a path for the sweep.\nsweepPath = startSketchOn(XZ)\n |> startProfileAt([0.05, 0.05], %)\n |> line(end = [0, 7])\n |> tangentialArc({ offset = 90, radius = 5 }, %)\n |> line(end = [-3, 0])\n |> tangentialArc({ offset = -90, radius = 5 }, %)\n |> line(end = [0, 7])\n\n// Create a hole for the pipe.\npipeHole = startSketchOn(XY)\n |> circle(center = [0, 0], radius = 1.5)\n\nsweepSketch = startSketchOn(XY)\n |> circle(center = [0, 0], radius = 2)\n |> hole(pipeHole, %)\n |> sweep(path = sweepPath)\n |> scale(z = 2.5)", + "// Scale an imported model.\n\n\nimport \"tests/inputs/cube.sldprt\" as cube\n\ncube\n |> scale(z = 2.5)", + "// Sweep two sketches along the same path.\n\n\nsketch001 = startSketchOn(XY)\nrectangleSketch = startProfileAt([-200, 23.86], sketch001)\n |> angledLine([0, 73.47], %, $rectangleSegmentA001)\n |> angledLine([\n segAng(rectangleSegmentA001) - 90,\n 50.61\n ], %)\n |> angledLine([\n segAng(rectangleSegmentA001),\n -segLen(rectangleSegmentA001)\n ], %)\n |> line(endAbsolute = [profileStartX(%), profileStartY(%)])\n |> close()\n\ncircleSketch = circle(sketch001, center = [200, -30.29], radius = 32.63)\n\nsketch002 = startSketchOn(YZ)\nsweepPath = startProfileAt([0, 0], sketch002)\n |> yLine(length = 231.81)\n |> tangentialArc({ radius = 80, offset = -90 }, %)\n |> xLine(length = 384.93)\n\nparts = sweep([rectangleSketch, circleSketch], path = sweepPath)\n\n// Scale the sweep.\nscale(parts, z = 0.5)" ] }, { @@ -326051,9 +326054,10 @@ "type": "number", "schema": { "$schema": "https://spec.openapis.org/oas/3.0/schema/2019-04-02#/definitions/Schema", - "title": "double", + "title": "Nullable_double", "type": "number", "format": "double", + "nullable": true, "definitions": { "Solid": { "type": "object", @@ -327638,9 +327642,9 @@ } } }, - "required": true, + "required": false, "includeInSnippet": true, - "description": "The amount to move the solid or sketch along the x axis.", + "description": "The amount to move the solid or sketch along the x axis. Defaults to 0 if not provided.", "labelRequired": true }, { @@ -327648,9 +327652,10 @@ "type": "number", "schema": { "$schema": "https://spec.openapis.org/oas/3.0/schema/2019-04-02#/definitions/Schema", - "title": "double", + "title": "Nullable_double", "type": "number", "format": "double", + "nullable": true, "definitions": { "Solid": { "type": "object", @@ -329235,9 +329240,9 @@ } } }, - "required": true, + "required": false, "includeInSnippet": true, - "description": "The amount to move the solid or sketch along the y axis.", + "description": "The amount to move the solid or sketch along the y axis. Defaults to 0 if not provided.", "labelRequired": true }, { @@ -329245,9 +329250,10 @@ "type": "number", "schema": { "$schema": "https://spec.openapis.org/oas/3.0/schema/2019-04-02#/definitions/Schema", - "title": "double", + "title": "Nullable_double", "type": "number", "format": "double", + "nullable": true, "definitions": { "Solid": { "type": "object", @@ -330832,9 +330838,9 @@ } } }, - "required": true, + "required": false, "includeInSnippet": true, - "description": "The amount to move the solid or sketch along the z axis.", + "description": "The amount to move the solid or sketch along the z axis. Defaults to 0 if not provided.", "labelRequired": true }, { @@ -334106,8 +334112,8 @@ "// Move a pipe.\n\n// Create a path for the sweep.\nsweepPath = startSketchOn(XZ)\n |> startProfileAt([0.05, 0.05], %)\n |> line(end = [0, 7])\n |> tangentialArc({ offset = 90, radius = 5 }, %)\n |> line(end = [-3, 0])\n |> tangentialArc({ offset = -90, radius = 5 }, %)\n |> line(end = [0, 7])\n\n// Create a hole for the pipe.\npipeHole = startSketchOn(XY)\n |> circle(center = [0, 0], radius = 1.5)\n\nsweepSketch = startSketchOn(XY)\n |> circle(center = [0, 0], radius = 2)\n |> hole(pipeHole, %)\n |> sweep(path = sweepPath)\n |> translate(x = 1.0, y = 1.0, z = 2.5)", "// Move an imported model.\n\n\nimport \"tests/inputs/cube.sldprt\" as cube\n\ncube\n |> translate(x = 1.0, y = 1.0, z = 2.5)", "// Sweep two sketches along the same path.\n\n\nsketch001 = startSketchOn(XY)\nrectangleSketch = startProfileAt([-200, 23.86], sketch001)\n |> angledLine([0, 73.47], %, $rectangleSegmentA001)\n |> angledLine([\n segAng(rectangleSegmentA001) - 90,\n 50.61\n ], %)\n |> angledLine([\n segAng(rectangleSegmentA001),\n -segLen(rectangleSegmentA001)\n ], %)\n |> line(endAbsolute = [profileStartX(%), profileStartY(%)])\n |> close()\n\ncircleSketch = circle(sketch001, center = [200, -30.29], radius = 32.63)\n\nsketch002 = startSketchOn(YZ)\nsweepPath = startProfileAt([0, 0], sketch002)\n |> yLine(length = 231.81)\n |> tangentialArc({ radius = 80, offset = -90 }, %)\n |> xLine(length = 384.93)\n\nparts = sweep([rectangleSketch, circleSketch], path = sweepPath)\n\n// Move the sweeps.\ntranslate(\n parts,\n x = 1.0,\n y = 1.0,\n z = 2.5,\n)", - "// Move a sketch.\n\n\nfn square(length) {\n l = length / 2\n p0 = [-l, -l]\n p1 = [-l, l]\n p2 = [l, l]\n p3 = [l, -l]\n\n return startSketchOn(XY)\n |> startProfileAt(p0, %)\n |> line(endAbsolute = p1)\n |> line(endAbsolute = p2)\n |> line(endAbsolute = p3)\n |> close()\n}\n\nsquare(10)\n |> translate(x = 5, y = 5, z = 0)\n |> extrude(length = 10)", - "// Translate and rotate a sketch to create a loft.\nsketch001 = startSketchOn(XY)\n\nfn square() {\n return startProfileAt([-10, 10], sketch001)\n |> xLine(length = 20)\n |> yLine(length = -20)\n |> xLine(length = -20)\n |> line(endAbsolute = [profileStartX(%), profileStartY(%)])\n |> close()\n}\n\nprofile001 = square()\n\nprofile002 = square()\n |> translate(x = 0, y = 0, z = 20)\n |> rotate(axis = [0, 0, 1.0], angle = 45)\n\nloft([profile001, profile002])" + "// Move a sketch.\n\n\nfn square(length) {\n l = length / 2\n p0 = [-l, -l]\n p1 = [-l, l]\n p2 = [l, l]\n p3 = [l, -l]\n\n return startSketchOn(XY)\n |> startProfileAt(p0, %)\n |> line(endAbsolute = p1)\n |> line(endAbsolute = p2)\n |> line(endAbsolute = p3)\n |> close()\n}\n\nsquare(10)\n |> translate(x = 5, y = 5)\n |> extrude(length = 10)", + "// Translate and rotate a sketch to create a loft.\nsketch001 = startSketchOn(XY)\n\nfn square() {\n return startProfileAt([-10, 10], sketch001)\n |> xLine(length = 20)\n |> yLine(length = -20)\n |> xLine(length = -20)\n |> line(endAbsolute = [profileStartX(%), profileStartY(%)])\n |> close()\n}\n\nprofile001 = square()\n\nprofile002 = square()\n |> translate(z = 20)\n |> rotate(axis = [0, 0, 1.0], angle = 45)\n\nloft([profile001, profile002])" ] }, { diff --git a/docs/kcl/translate.md b/docs/kcl/translate.md index bcba20fc5..95fb2f978 100644 --- a/docs/kcl/translate.md +++ b/docs/kcl/translate.md @@ -13,9 +13,9 @@ Translate is really useful for sketches if you want to move a sketch and then ro ```js translate( objects: SolidOrSketchOrImportedGeometry, - x: number, - y: number, - z: number, + x?: number, + y?: number, + z?: number, global?: bool, ): SolidOrSketchOrImportedGeometry ``` @@ -26,9 +26,9 @@ translate( | Name | Type | Description | Required | |----------|------|-------------|----------| | `objects` | [`SolidOrSketchOrImportedGeometry`](/docs/kcl/types/SolidOrSketchOrImportedGeometry) | The solid, sketch, or set of solids or sketches to move. | Yes | -| `x` | [`number`](/docs/kcl/types/number) | The amount to move the solid or sketch along the x axis. | Yes | -| `y` | [`number`](/docs/kcl/types/number) | The amount to move the solid or sketch along the y axis. | Yes | -| `z` | [`number`](/docs/kcl/types/number) | The amount to move the solid or sketch along the z axis. | Yes | +| `x` | [`number`](/docs/kcl/types/number) | The amount to move the solid or sketch along the x axis. Defaults to 0 if not provided. | No | +| `y` | [`number`](/docs/kcl/types/number) | The amount to move the solid or sketch along the y axis. Defaults to 0 if not provided. | No | +| `z` | [`number`](/docs/kcl/types/number) | The amount to move the solid or sketch along the z axis. Defaults to 0 if not provided. | No | | `global` | [`bool`](/docs/kcl/types/bool) | If true, the transform is applied in global space. The origin of the model will move. By default, the transform is applied in local sketch axis, therefore the origin will not move. | No | ### Returns @@ -134,7 +134,7 @@ fn square(length) { } square(10) - |> translate(x = 5, y = 5, z = 0) + |> translate(x = 5, y = 5) |> extrude(length = 10) ``` @@ -156,7 +156,7 @@ fn square() { profile001 = square() profile002 = square() - |> translate(x = 0, y = 0, z = 20) + |> translate(z = 20) |> rotate(axis = [0, 0, 1.0], angle = 45) loft([profile001, profile002]) diff --git a/e2e/playwright/app-header-tests.spec.ts b/e2e/playwright/app-header-tests.spec.ts index c663c83a8..4ec2a4ff1 100644 --- a/e2e/playwright/app-header-tests.spec.ts +++ b/e2e/playwright/app-header-tests.spec.ts @@ -1,4 +1,4 @@ -import { test, expect } from './zoo-test' +import { expect, test } from '@e2e/playwright/zoo-test' test.describe('Electron app header tests', () => { test( diff --git a/e2e/playwright/basic-sketch.spec.ts b/e2e/playwright/basic-sketch.spec.ts index 3cb1e78fb..adf2270bf 100644 --- a/e2e/playwright/basic-sketch.spec.ts +++ b/e2e/playwright/basic-sketch.spec.ts @@ -1,13 +1,14 @@ -import { Page } from '@playwright/test' -import { test, expect } from './zoo-test' +import type { Page } from '@playwright/test' + +import type { HomePageFixture } from '@e2e/playwright/fixtures/homePageFixture' import { - getUtils, + PERSIST_MODELING_CONTEXT, TEST_COLORS, commonPoints, - PERSIST_MODELING_CONTEXT, + getUtils, orRunWhenFullSuiteEnabled, -} from './test-utils' -import { HomePageFixture } from './fixtures/homePageFixture' +} from '@e2e/playwright/test-utils' +import { expect, test } from '@e2e/playwright/zoo-test' test.setTimeout(120000) diff --git a/e2e/playwright/boolean.spec.ts b/e2e/playwright/boolean.spec.ts index 67127ef68..d8b5f62ad 100644 --- a/e2e/playwright/boolean.spec.ts +++ b/e2e/playwright/boolean.spec.ts @@ -1,7 +1,8 @@ -import { test, expect } from './zoo-test' import fs from 'node:fs/promises' import path from 'node:path' +import { expect, test } from '@e2e/playwright/zoo-test' + test.describe('Point and click for boolean workflows', () => { // Boolean operations to test const booleanOperations = [ diff --git a/e2e/playwright/can-create-sketches-on-all-planes-and-their-back-sides.spec.ts b/e2e/playwright/can-create-sketches-on-all-planes-and-their-back-sides.spec.ts index b8e1b9ff0..e317701a2 100644 --- a/e2e/playwright/can-create-sketches-on-all-planes-and-their-back-sides.spec.ts +++ b/e2e/playwright/can-create-sketches-on-all-planes-and-their-back-sides.spec.ts @@ -1,10 +1,11 @@ -import { Page } from '@playwright/test' -import { test, expect } from './zoo-test' -import { HomePageFixture } from './fixtures/homePageFixture' -import { getUtils } from './test-utils' -import { EngineCommand } from 'lang/std/artifactGraph' -import { uuidv4 } from 'lib/utils' -import { SceneFixture } from './fixtures/sceneFixture' +import type { Page } from '@playwright/test' +import type { EngineCommand } from '@src/lang/std/artifactGraph' +import { uuidv4 } from '@src/lib/utils' + +import type { HomePageFixture } from '@e2e/playwright/fixtures/homePageFixture' +import type { SceneFixture } from '@e2e/playwright/fixtures/sceneFixture' +import { getUtils } from '@e2e/playwright/test-utils' +import { expect, test } from '@e2e/playwright/zoo-test' test.describe( 'Can create sketches on all planes and their back sides', diff --git a/e2e/playwright/code-pane-and-errors.spec.ts b/e2e/playwright/code-pane-and-errors.spec.ts index 1908ab38d..89ca3e48f 100644 --- a/e2e/playwright/code-pane-and-errors.spec.ts +++ b/e2e/playwright/code-pane-and-errors.spec.ts @@ -1,13 +1,14 @@ -import { test, expect } from './zoo-test' -import { - orRunWhenFullSuiteEnabled, - getUtils, - executorInputPath, -} from './test-utils' -import { join } from 'path' -import { bracket } from 'lib/exampleKcl' -import { TEST_CODE_LONG_WITH_ERROR_OUT_OF_VIEW } from './storageStates' +import { bracket } from '@src/lib/exampleKcl' import fsp from 'fs/promises' +import { join } from 'path' + +import { TEST_CODE_LONG_WITH_ERROR_OUT_OF_VIEW } from '@e2e/playwright/storageStates' +import { + executorInputPath, + getUtils, + orRunWhenFullSuiteEnabled, +} from '@e2e/playwright/test-utils' +import { expect, test } from '@e2e/playwright/zoo-test' test.describe('Code pane and errors', { tag: ['@skipWin'] }, () => { test('Typing KCL errors induces a badge on the code pane button', async ({ diff --git a/e2e/playwright/command-bar-tests.spec.ts b/e2e/playwright/command-bar-tests.spec.ts index a162a70b7..69a767e62 100644 --- a/e2e/playwright/command-bar-tests.spec.ts +++ b/e2e/playwright/command-bar-tests.spec.ts @@ -1,12 +1,13 @@ -import { test, expect } from './zoo-test' +import { KCL_DEFAULT_LENGTH } from '@src/lib/constants' import * as fsp from 'fs/promises' +import path, { join } from 'path' + import { executorInputPath, getUtils, orRunWhenFullSuiteEnabled, -} from './test-utils' -import { KCL_DEFAULT_LENGTH } from 'lib/constants' -import path, { join } from 'path' +} from '@e2e/playwright/test-utils' +import { expect, test } from '@e2e/playwright/zoo-test' test.describe('Command bar tests', { tag: ['@skipWin'] }, () => { test('Extrude from command bar selects extrude line after', async ({ diff --git a/e2e/playwright/copilot-ghost-test.spec.ts b/e2e/playwright/copilot-ghost-test.spec.ts index c7f552258..c507176d4 100644 --- a/e2e/playwright/copilot-ghost-test.spec.ts +++ b/e2e/playwright/copilot-ghost-test.spec.ts @@ -1,5 +1,5 @@ -import { test, expect } from './zoo-test' -import { getUtils } from './test-utils' +import { getUtils } from '@e2e/playwright/test-utils' +import { expect, test } from '@e2e/playwright/zoo-test' test.describe('Copilot ghost text', () => { // eslint-disable-next-line jest/valid-title diff --git a/e2e/playwright/debug-pane.spec.ts b/e2e/playwright/debug-pane.spec.ts index 59ff83605..81514761f 100644 --- a/e2e/playwright/debug-pane.spec.ts +++ b/e2e/playwright/debug-pane.spec.ts @@ -1,6 +1,5 @@ -import { test, expect } from './zoo-test' - -import { getUtils } from './test-utils' +import { getUtils } from '@e2e/playwright/test-utils' +import { expect, test } from '@e2e/playwright/zoo-test' function countNewlines(input: string): number { let count = 0 diff --git a/e2e/playwright/desktop-export.spec.ts b/e2e/playwright/desktop-export.spec.ts index c4d32ccd1..f491d5d6a 100644 --- a/e2e/playwright/desktop-export.spec.ts +++ b/e2e/playwright/desktop-export.spec.ts @@ -1,11 +1,12 @@ -import { test, expect } from './zoo-test' +import fsp from 'fs/promises' import path from 'path' + import { - getUtils, executorInputPath, getPlaywrightDownloadDir, -} from './test-utils' -import fsp from 'fs/promises' + getUtils, +} from '@e2e/playwright/test-utils' +import { expect, test } from '@e2e/playwright/zoo-test' test( 'export works on the first try', diff --git a/e2e/playwright/editor-tests.spec.ts b/e2e/playwright/editor-tests.spec.ts index 739236857..02801d296 100644 --- a/e2e/playwright/editor-tests.spec.ts +++ b/e2e/playwright/editor-tests.spec.ts @@ -1,14 +1,14 @@ -import { test, expect } from './zoo-test' +import { uuidv4 } from '@src/lib/utils' import fsp from 'fs/promises' -import { uuidv4 } from 'lib/utils' +import { join } from 'path' + import { + TEST_COLORS, executorInputPath, getUtils, orRunWhenFullSuiteEnabled, - TEST_COLORS, -} from './test-utils' - -import { join } from 'path' +} from '@e2e/playwright/test-utils' +import { expect, test } from '@e2e/playwright/zoo-test' test.describe('Editor tests', { tag: ['@skipWin'] }, () => { test('can comment out code with ctrl+/', async ({ page, homePage }) => { diff --git a/e2e/playwright/feature-tree-pane.spec.ts b/e2e/playwright/feature-tree-pane.spec.ts index 54e4ed0b6..beb5fe6c6 100644 --- a/e2e/playwright/feature-tree-pane.spec.ts +++ b/e2e/playwright/feature-tree-pane.spec.ts @@ -1,7 +1,8 @@ -import { test, expect } from './zoo-test' import * as fsp from 'fs/promises' import { join } from 'path' +import { expect, test } from '@e2e/playwright/zoo-test' + const FEATURE_TREE_EXAMPLE_CODE = `export fn timesFive(x) { return 5 * x } diff --git a/e2e/playwright/file-tree.spec.ts b/e2e/playwright/file-tree.spec.ts index 88ed2d403..422754f26 100644 --- a/e2e/playwright/file-tree.spec.ts +++ b/e2e/playwright/file-tree.spec.ts @@ -1,15 +1,16 @@ -import { test, expect } from './zoo-test' -import * as fsp from 'fs/promises' +import { FILE_EXT } from '@src/lib/constants' import * as fs from 'fs' +import * as fsp from 'fs/promises' +import { join } from 'path' + import { createProject, executorInputPath, getUtils, orRunWhenFullSuiteEnabled, runningOnWindows, -} from './test-utils' -import { join } from 'path' -import { FILE_EXT } from 'lib/constants' +} from '@e2e/playwright/test-utils' +import { expect, test } from '@e2e/playwright/zoo-test' test.describe('integrations tests', () => { test( diff --git a/e2e/playwright/fixtures/cmdBarFixture.ts b/e2e/playwright/fixtures/cmdBarFixture.ts index 18800ef9e..e73194eba 100644 --- a/e2e/playwright/fixtures/cmdBarFixture.ts +++ b/e2e/playwright/fixtures/cmdBarFixture.ts @@ -1,5 +1,5 @@ -import type { Page, Locator, Route, Request } from '@playwright/test' -import { expect, TestInfo } from '@playwright/test' +import type { Locator, Page, Request, Route, TestInfo } from '@playwright/test' +import { expect } from '@playwright/test' import * as fs from 'fs' import * as path from 'path' diff --git a/e2e/playwright/fixtures/editorFixture.ts b/e2e/playwright/fixtures/editorFixture.ts index 3304a8a66..266a30137 100644 --- a/e2e/playwright/fixtures/editorFixture.ts +++ b/e2e/playwright/fixtures/editorFixture.ts @@ -1,11 +1,12 @@ -import type { Page, Locator } from '@playwright/test' +import type { Locator, Page } from '@playwright/test' import { expect } from '@playwright/test' + import { - closePane, checkIfPaneIsOpen, + closePane, openPane, sansWhitespace, -} from '../test-utils' +} from '@e2e/playwright/test-utils' interface EditorState { activeLines: Array diff --git a/e2e/playwright/fixtures/fixtureSetup.ts b/e2e/playwright/fixtures/fixtureSetup.ts index e051917ab..1c370bae7 100644 --- a/e2e/playwright/fixtures/fixtureSetup.ts +++ b/e2e/playwright/fixtures/fixtureSetup.ts @@ -1,28 +1,28 @@ /* eslint-disable react-hooks/rules-of-hooks */ - import type { BrowserContext, ElectronApplication, - TestInfo, Page, + TestInfo, } from '@playwright/test' - import { _electron as electron } from '@playwright/test' -import * as TOML from '@iarna/toml' -import { TEST_SETTINGS } from '../storageStates' -import { SETTINGS_FILE_NAME } from 'lib/constants' -import { getUtils, setup } from '../test-utils' +import { SETTINGS_FILE_NAME } from '@src/lib/constants' +import type { DeepPartial } from '@src/lib/types' import fsp from 'fs/promises' import fs from 'node:fs' import path from 'path' -import { CmdBarFixture } from './cmdBarFixture' -import { EditorFixture } from './editorFixture' -import { ToolbarFixture } from './toolbarFixture' -import { SceneFixture } from './sceneFixture' -import { HomePageFixture } from './homePageFixture' -import { DeepPartial } from 'lib/types' -import { Settings } from '@rust/kcl-lib/bindings/Settings' + +import type { Settings } from '@rust/kcl-lib/bindings/Settings' + +import { CmdBarFixture } from '@e2e/playwright/fixtures/cmdBarFixture' +import { EditorFixture } from '@e2e/playwright/fixtures/editorFixture' +import { HomePageFixture } from '@e2e/playwright/fixtures/homePageFixture' +import { SceneFixture } from '@e2e/playwright/fixtures/sceneFixture' +import { ToolbarFixture } from '@e2e/playwright/fixtures/toolbarFixture' + +import { TEST_SETTINGS } from '@e2e/playwright/storageStates' +import { getUtils, settingsToToml, setup } from '@e2e/playwright/test-utils' export class AuthenticatedApp { public readonly page: Page @@ -287,26 +287,30 @@ export class ElectronZoo { let settingsOverridesToml = '' if (appSettings) { - settingsOverridesToml = TOML.stringify({ - // @ts-expect-error + settingsOverridesToml = settingsToToml({ settings: { ...TEST_SETTINGS, ...appSettings, app: { ...TEST_SETTINGS.app, - project_directory: this.projectDirName, ...appSettings.app, }, + project: { + ...TEST_SETTINGS.project, + directory: this.projectDirName, + }, }, }) } else { - settingsOverridesToml = TOML.stringify({ - // @ts-expect-error + settingsOverridesToml = settingsToToml({ settings: { ...TEST_SETTINGS, app: { ...TEST_SETTINGS.app, - project_directory: this.projectDirName, + }, + project: { + ...TEST_SETTINGS.project, + directory: this.projectDirName, }, }, }) diff --git a/e2e/playwright/fixtures/homePageFixture.ts b/e2e/playwright/fixtures/homePageFixture.ts index e6caf43a8..891c66a84 100644 --- a/e2e/playwright/fixtures/homePageFixture.ts +++ b/e2e/playwright/fixtures/homePageFixture.ts @@ -1,4 +1,4 @@ -import type { Page, Locator } from '@playwright/test' +import type { Locator, Page } from '@playwright/test' import { expect } from '@playwright/test' interface ProjectCardState { diff --git a/e2e/playwright/fixtures/sceneFixture.ts b/e2e/playwright/fixtures/sceneFixture.ts index b47538345..a1b9c891e 100644 --- a/e2e/playwright/fixtures/sceneFixture.ts +++ b/e2e/playwright/fixtures/sceneFixture.ts @@ -1,15 +1,17 @@ -import type { Page, Locator } from '@playwright/test' -import { expect } from '../zoo-test' -import { isArray, uuidv4 } from 'lib/utils' -import { CmdBarFixture } from './cmdBarFixture' +import type { Locator, Page } from '@playwright/test' +import { isArray, uuidv4 } from '@src/lib/utils' + +import type { CmdBarFixture } from '@e2e/playwright/fixtures/cmdBarFixture' + import { closeDebugPanel, doAndWaitForImageDiff, getPixelRGBs, + getUtils, openAndClearDebugPanel, sendCustomCmd, - getUtils, -} from '../test-utils' +} from '@e2e/playwright/test-utils' +import { expect } from '@e2e/playwright/zoo-test' type MouseParams = { pixelDiff?: number diff --git a/e2e/playwright/fixtures/toolbarFixture.ts b/e2e/playwright/fixtures/toolbarFixture.ts index 09dd54d96..29eef26cb 100644 --- a/e2e/playwright/fixtures/toolbarFixture.ts +++ b/e2e/playwright/fixtures/toolbarFixture.ts @@ -1,14 +1,15 @@ -import { type Page, type Locator, test } from '@playwright/test' -import { expect } from '../zoo-test' +import { type Locator, type Page, test } from '@playwright/test' +import type { SidebarType } from '@src/components/ModelingSidebar/ModelingPanes' +import { SIDEBAR_BUTTON_SUFFIX } from '@src/lib/constants' +import type { ToolbarModeName } from '@src/lib/toolbar' + import { checkIfPaneIsOpen, closePane, doAndWaitForImageDiff, openPane, -} from '../test-utils' -import { SidebarType } from 'components/ModelingSidebar/ModelingPanes' -import { SIDEBAR_BUTTON_SUFFIX } from 'lib/constants' -import { ToolbarModeName } from 'lib/toolbar' +} from '@e2e/playwright/test-utils' +import { expect } from '@e2e/playwright/zoo-test' export class ToolbarFixture { public page: Page diff --git a/e2e/playwright/machines.spec.ts b/e2e/playwright/machines.spec.ts index 113f59ffc..9f4c3cc4d 100644 --- a/e2e/playwright/machines.spec.ts +++ b/e2e/playwright/machines.spec.ts @@ -1,7 +1,8 @@ -import { test, expect } from './zoo-test' -import { executorInputPath } from './test-utils' -import { join } from 'path' import fsp from 'fs/promises' +import { join } from 'path' + +import { executorInputPath } from '@e2e/playwright/test-utils' +import { expect, test } from '@e2e/playwright/zoo-test' test( 'When machine-api server not found butt is disabled and shows the reason', diff --git a/e2e/playwright/named-views.spec.ts b/e2e/playwright/named-views.spec.ts index c24ea6de9..3eca7adbd 100644 --- a/e2e/playwright/named-views.spec.ts +++ b/e2e/playwright/named-views.spec.ts @@ -1,13 +1,15 @@ -import { test, expect } from './zoo-test' -import { PROJECT_SETTINGS_FILE_NAME } from 'lib/constants' +import { PROJECT_SETTINGS_FILE_NAME } from '@src/lib/constants' import * as fsp from 'fs/promises' import { join } from 'path' + +import type { NamedView } from '@rust/kcl-lib/bindings/NamedView' + import { createProject, - tomlToPerProjectSettings, perProjectsettingsToToml, -} from './test-utils' -import { NamedView } from '@rust/kcl-lib/bindings/NamedView' + tomlToPerProjectSettings, +} from '@e2e/playwright/test-utils' +import { expect, test } from '@e2e/playwright/zoo-test' // Helper function to determine if the file path on disk exists // Specifically this is used to check if project.toml exists on disk diff --git a/e2e/playwright/native-file-menu.spec.ts b/e2e/playwright/native-file-menu.spec.ts index 798013e24..affb8d6ae 100644 --- a/e2e/playwright/native-file-menu.spec.ts +++ b/e2e/playwright/native-file-menu.spec.ts @@ -1,4 +1,4 @@ -import { test, expect } from './zoo-test' +import { expect, test } from '@e2e/playwright/zoo-test' /** * Not all menu actions are tested. Some are default electron menu actions. @@ -10,6 +10,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => { test('File.Create project', async ({ tronApp, cmdBar, page }) => { if (!tronApp) fail() // Run electron snippet to find the Menu! + await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await tronApp.electron.evaluate(async ({ app }) => { if (!app || !app.applicationMenu) fail() const newProject = @@ -29,6 +30,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => { test('File.Open project', async ({ tronApp, cmdBar, page }) => { if (!tronApp) fail() // Run electron snippet to find the Menu! + await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await tronApp.electron.evaluate(async ({ app }) => { if (!app || !app.applicationMenu) fail() const openProject = @@ -52,6 +54,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => { }) => { if (!tronApp) fail() // Run electron snippet to find the Menu! + await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await tronApp.electron.evaluate(async ({ app }) => { if (!app || !app.applicationMenu) fail() const userSettings = app.applicationMenu.getMenuItemById( @@ -75,6 +78,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => { }) => { if (!tronApp) fail() // Run electron snippet to find the Menu! + await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await tronApp.electron.evaluate(async ({ app }) => { if (!app || !app.applicationMenu) fail() const keybindings = app.applicationMenu.getMenuItemById( @@ -96,6 +100,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => { }) => { if (!tronApp) fail() // Run electron snippet to find the Menu! + await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await tronApp.electron.evaluate(async ({ app }) => { if (!app || !app.applicationMenu) fail() const menu = app.applicationMenu.getMenuItemById( @@ -112,6 +117,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => { test('File.Preferences.Theme', async ({ tronApp, cmdBar, page }) => { if (!tronApp) fail() // Run electron snippet to find the Menu! + await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await tronApp.electron.evaluate(async ({ app }) => { if (!app || !app.applicationMenu) fail() const menu = app.applicationMenu.getMenuItemById( @@ -136,6 +142,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => { }) => { if (!tronApp) fail() // Run electron snippet to find the Menu! + await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await tronApp.electron.evaluate(async ({ app }) => { if (!app || !app.applicationMenu) fail() const menu = app.applicationMenu.getMenuItemById( @@ -152,6 +159,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => { test('File.Preferences.Sign out', async ({ tronApp, cmdBar, page }) => { if (!tronApp) fail() // Run electron snippet to find the Menu! + await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await tronApp.electron.evaluate(async ({ app }) => { if (!app || !app.applicationMenu) fail() const menu = app.applicationMenu.getMenuItemById('File.Sign out') @@ -170,6 +178,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => { test('Edit.Rename project', async ({ tronApp, cmdBar, page }) => { if (!tronApp) fail() // Run electron snippet to find the Menu! + await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await tronApp.electron.evaluate(async ({ app }) => { if (!app || !app.applicationMenu) fail() const menu = app.applicationMenu.getMenuItemById( @@ -188,6 +197,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => { test('Edit.Delete project', async ({ tronApp, cmdBar, page }) => { if (!tronApp) fail() // Run electron snippet to find the Menu! + await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await tronApp.electron.evaluate(async ({ app }) => { if (!app || !app.applicationMenu) fail() const menu = app.applicationMenu.getMenuItemById( @@ -210,6 +220,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => { }) => { if (!tronApp) fail() // Run electron snippet to find the Menu! + await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await tronApp.electron.evaluate(async ({ app }) => { if (!app || !app.applicationMenu) fail() const menu = app.applicationMenu.getMenuItemById( @@ -228,6 +239,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => { test('View.Command Palette...', async ({ tronApp, cmdBar, page }) => { if (!tronApp) fail() // Run electron snippet to find the Menu! + await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await tronApp.electron.evaluate(async ({ app }) => { if (!app || !app.applicationMenu) fail() const menu = app.applicationMenu.getMenuItemById( @@ -245,6 +257,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => { test('Help.Show all commands', async ({ tronApp, cmdBar, page }) => { if (!tronApp) fail() // Run electron snippet to find the Menu! + await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await tronApp.electron.evaluate(async ({ app }) => { if (!app || !app.applicationMenu) fail() const menu = app.applicationMenu.getMenuItemById( @@ -260,6 +273,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => { test('Help.KCL code samples', async ({ tronApp, cmdBar, page }) => { if (!tronApp) fail() // Run electron snippet to find the Menu! + await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await tronApp.electron.evaluate(async ({ app }) => { if (!app || !app.applicationMenu) fail() const menu = app.applicationMenu.getMenuItemById( @@ -275,6 +289,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => { }) => { if (!tronApp) fail() // Run electron snippet to find the Menu! + await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await tronApp.electron.evaluate(async ({ app }) => { if (!app || !app.applicationMenu) fail() const menu = app.applicationMenu.getMenuItemById( @@ -293,6 +308,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => { test('Help.Reset onboarding', async ({ tronApp, cmdBar, page }) => { if (!tronApp) fail() // Run electron snippet to find the Menu! + await page.waitForTimeout(100) // wait for createModelingPageMenu() to run await tronApp.electron.evaluate(async ({ app }) => { if (!app || !app.applicationMenu) fail() const menu = app.applicationMenu.getMenuItemById( diff --git a/e2e/playwright/null.spec.ts b/e2e/playwright/null.spec.ts index dcb213a9f..3e05f7a3b 100644 --- a/e2e/playwright/null.spec.ts +++ b/e2e/playwright/null.spec.ts @@ -2,8 +2,7 @@ // application, check it can make it to the project pane, and nothing more. // It also tests our test wrappers are working. // Additionally this serves as a nice minimal example. - -import { test, expect } from './zoo-test' +import { expect, test } from '@e2e/playwright/zoo-test' test.describe('Open the application', () => { test('see the project view', async ({ page, context }) => { diff --git a/e2e/playwright/onboarding-tests.spec.ts b/e2e/playwright/onboarding-tests.spec.ts index 4ecac34e3..05944297b 100644 --- a/e2e/playwright/onboarding-tests.spec.ts +++ b/e2e/playwright/onboarding-tests.spec.ts @@ -1,22 +1,23 @@ -import { test, expect } from './zoo-test' -import { join } from 'path' +import { bracket } from '@src/lib/exampleKcl' +import { onboardingPaths } from '@src/routes/Onboarding/paths' import fsp from 'fs/promises' -import { - getUtils, - executorInputPath, - createProject, - settingsToToml, - orRunWhenFullSuiteEnabled, -} from './test-utils' -import { bracket } from 'lib/exampleKcl' -import { onboardingPaths } from 'routes/Onboarding/paths' +import { join } from 'path' + +import { expectPixelColor } from '@e2e/playwright/fixtures/sceneFixture' import { TEST_SETTINGS_KEY, - TEST_SETTINGS_ONBOARDING_START, TEST_SETTINGS_ONBOARDING_EXPORT, + TEST_SETTINGS_ONBOARDING_START, TEST_SETTINGS_ONBOARDING_USER_MENU, -} from './storageStates' -import { expectPixelColor } from './fixtures/sceneFixture' +} from '@e2e/playwright/storageStates' +import { + createProject, + executorInputPath, + getUtils, + orRunWhenFullSuiteEnabled, + settingsToToml, +} from '@e2e/playwright/test-utils' +import { expect, test } from '@e2e/playwright/zoo-test' // Because our default test settings have the onboardingStatus set to 'dismissed', // we must set it to empty for the tests where we want to see the onboarding immediately. diff --git a/e2e/playwright/playwright-deprecated.ts b/e2e/playwright/playwright-deprecated.ts index b92a52dfc..2c25b4f57 100644 --- a/e2e/playwright/playwright-deprecated.ts +++ b/e2e/playwright/playwright-deprecated.ts @@ -1,4 +1,4 @@ -import { test, expect } from '@playwright/test' +import { expect, test } from '@playwright/test' /** @deprecated, import from ./fixtureSetup.ts instead */ export const _test = test diff --git a/e2e/playwright/point-click.spec.ts b/e2e/playwright/point-click.spec.ts index 7c18108a5..ebeb47513 100644 --- a/e2e/playwright/point-click.spec.ts +++ b/e2e/playwright/point-click.spec.ts @@ -1,12 +1,12 @@ -import { Page } from '@playwright/test' -import { test, expect } from './zoo-test' -import { EditorFixture } from './fixtures/editorFixture' -import { SceneFixture } from './fixtures/sceneFixture' -import { ToolbarFixture } from './fixtures/toolbarFixture' +import type { Locator, Page } from '@playwright/test' import fs from 'node:fs/promises' import path from 'node:path' -import { getUtils, orRunWhenFullSuiteEnabled } from './test-utils' -import { Locator } from '@playwright/test' + +import type { EditorFixture } from '@e2e/playwright/fixtures/editorFixture' +import type { SceneFixture } from '@e2e/playwright/fixtures/sceneFixture' +import type { ToolbarFixture } from '@e2e/playwright/fixtures/toolbarFixture' +import { getUtils, orRunWhenFullSuiteEnabled } from '@e2e/playwright/test-utils' +import { expect, test } from '@e2e/playwright/zoo-test' // test file is for testing point an click code gen functionality that's not sketch mode related diff --git a/e2e/playwright/projects.spec.ts b/e2e/playwright/projects.spec.ts index 425fd9d2b..efba3c04e 100644 --- a/e2e/playwright/projects.spec.ts +++ b/e2e/playwright/projects.spec.ts @@ -1,19 +1,20 @@ -import { test, expect } from './zoo-test' +import { DEFAULT_PROJECT_KCL_FILE } from '@src/lib/constants' +import fs from 'fs' +import fsp from 'fs/promises' +import path from 'path' + +import type { Paths } from '@e2e/playwright/test-utils' import { + createProject, doExport, executorInputPath, + getPlaywrightDownloadDir, getUtils, isOutOfViewInScrollContainer, - Paths, - createProject, - getPlaywrightDownloadDir, orRunWhenFullSuiteEnabled, runningOnWindows, -} from './test-utils' -import fsp from 'fs/promises' -import fs from 'fs' -import path from 'path' -import { DEFAULT_PROJECT_KCL_FILE } from 'lib/constants' +} from '@e2e/playwright/test-utils' +import { expect, test } from '@e2e/playwright/zoo-test' test( 'projects reload if a new one is created, deleted, or renamed externally', diff --git a/e2e/playwright/prompt-to-edit-snapshot-tests.spec.ts b/e2e/playwright/prompt-to-edit-snapshot-tests.spec.ts index 083ea8406..86f6c3804 100644 --- a/e2e/playwright/prompt-to-edit-snapshot-tests.spec.ts +++ b/e2e/playwright/prompt-to-edit-snapshot-tests.spec.ts @@ -1,4 +1,5 @@ -import { test, expect } from './zoo-test' +import { expect, test } from '@e2e/playwright/zoo-test' + /* eslint-disable jest/no-conditional-expect */ /** diff --git a/e2e/playwright/prompt-to-edit.spec.ts b/e2e/playwright/prompt-to-edit.spec.ts index 0121a1d32..e79db800c 100644 --- a/e2e/playwright/prompt-to-edit.spec.ts +++ b/e2e/playwright/prompt-to-edit.spec.ts @@ -1,5 +1,5 @@ -import { test, expect } from './zoo-test' -import { orRunWhenFullSuiteEnabled } from './test-utils' +import { orRunWhenFullSuiteEnabled } from '@e2e/playwright/test-utils' +import { expect, test } from '@e2e/playwright/zoo-test' /* eslint-disable jest/no-conditional-expect */ diff --git a/e2e/playwright/regression-tests.spec.ts b/e2e/playwright/regression-tests.spec.ts index c261120c6..d17c321dd 100644 --- a/e2e/playwright/regression-tests.spec.ts +++ b/e2e/playwright/regression-tests.spec.ts @@ -1,17 +1,18 @@ -import { Page } from '@playwright/test' -import { test, expect } from './zoo-test' -import path from 'path' +import type { Page } from '@playwright/test' +import { bracket } from '@src/lib/exampleKcl' +import { reportRejection } from '@src/lib/trap' import * as fsp from 'fs/promises' +import path from 'path' + +import { TEST_CODE_TRIGGER_ENGINE_EXPORT_ERROR } from '@e2e/playwright/storageStates' +import type { TestColor } from '@e2e/playwright/test-utils' import { - getUtils, TEST_COLORS, - TestColor, executorInputPath, + getUtils, orRunWhenFullSuiteEnabled, -} from './test-utils' -import { TEST_CODE_TRIGGER_ENGINE_EXPORT_ERROR } from './storageStates' -import { bracket } from 'lib/exampleKcl' -import { reportRejection } from 'lib/trap' +} from '@e2e/playwright/test-utils' +import { expect, test } from '@e2e/playwright/zoo-test' test.describe('Regression tests', { tag: ['@skipWin'] }, () => { // bugs we found that don't fit neatly into other categories diff --git a/e2e/playwright/sketch-tests.spec.ts b/e2e/playwright/sketch-tests.spec.ts index 628f55bca..39379c5b7 100644 --- a/e2e/playwright/sketch-tests.spec.ts +++ b/e2e/playwright/sketch-tests.spec.ts @@ -1,20 +1,20 @@ -import { Page } from '@playwright/test' -import { test, expect } from './zoo-test' +import type { Page } from '@playwright/test' +import { roundOff, uuidv4 } from '@src/lib/utils' import fs from 'node:fs/promises' import path from 'node:path' -import { HomePageFixture } from './fixtures/homePageFixture' +import type { CmdBarFixture } from '@e2e/playwright/fixtures/cmdBarFixture' +import type { HomePageFixture } from '@e2e/playwright/fixtures/homePageFixture' +import type { SceneFixture } from '@e2e/playwright/fixtures/sceneFixture' +import type { ToolbarFixture } from '@e2e/playwright/fixtures/toolbarFixture' import { - getMovementUtils, - getUtils, PERSIST_MODELING_CONTEXT, TEST_COLORS, + getMovementUtils, + getUtils, orRunWhenFullSuiteEnabled, -} from './test-utils' -import { uuidv4, roundOff } from 'lib/utils' -import { SceneFixture } from './fixtures/sceneFixture' -import { ToolbarFixture } from './fixtures/toolbarFixture' -import { CmdBarFixture } from './fixtures/cmdBarFixture' +} from '@e2e/playwright/test-utils' +import { expect, test } from '@e2e/playwright/zoo-test' test.describe('Sketch tests', { tag: ['@skipWin'] }, () => { test('multi-sketch file shows multiple Edit Sketch buttons', async ({ diff --git a/e2e/playwright/snapshot-tests.spec.ts b/e2e/playwright/snapshot-tests.spec.ts index 67d40bb6a..e12239241 100644 --- a/e2e/playwright/snapshot-tests.spec.ts +++ b/e2e/playwright/snapshot-tests.spec.ts @@ -1,21 +1,22 @@ -import { test, expect } from './zoo-test' -import { secrets } from './secrets' -import { - Paths, - doExport, - getUtils, - settingsToToml, - orRunWhenFullSuiteEnabled, -} from './test-utils' -import { Models } from '@kittycad/lib' -import fsp from 'fs/promises' +import type { Models } from '@kittycad/lib' +import { KCL_DEFAULT_LENGTH } from '@src/lib/constants' import { spawn } from 'child_process' -import { KCL_DEFAULT_LENGTH } from 'lib/constants' +import fsp from 'fs/promises' import JSZip from 'jszip' import path from 'path' -import { TEST_SETTINGS, TEST_SETTINGS_KEY } from './storageStates' -import { SceneFixture } from './fixtures/sceneFixture' -import { CmdBarFixture } from './fixtures/cmdBarFixture' + +import type { CmdBarFixture } from '@e2e/playwright/fixtures/cmdBarFixture' +import type { SceneFixture } from '@e2e/playwright/fixtures/sceneFixture' +import { secrets } from '@e2e/playwright/secrets' +import { TEST_SETTINGS, TEST_SETTINGS_KEY } from '@e2e/playwright/storageStates' +import type { Paths } from '@e2e/playwright/test-utils' +import { + doExport, + getUtils, + orRunWhenFullSuiteEnabled, + settingsToToml, +} from '@e2e/playwright/test-utils' +import { expect, test } from '@e2e/playwright/zoo-test' test.beforeEach(async ({ page, context }) => { // Make the user avatar image always 404 @@ -345,7 +346,9 @@ const extrudeDefaultPlane = async ( app: { onboarding_status: 'dismissed', show_debug_panel: true, - theme: 'dark', + appearance: { + theme: 'dark', + }, }, project: { default_project_name: 'project-$nnn', diff --git a/e2e/playwright/storageStates.ts b/e2e/playwright/storageStates.ts index 4b2cc30c6..f09a53002 100644 --- a/e2e/playwright/storageStates.ts +++ b/e2e/playwright/storageStates.ts @@ -1,17 +1,19 @@ -import { Settings } from '@rust/kcl-lib/bindings/Settings' -import { SaveSettingsPayload } from 'lib/settings/settingsTypes' -import { Themes } from 'lib/theme' -import { DeepPartial } from 'lib/types' -import { onboardingPaths } from 'routes/Onboarding/paths' +import type { SaveSettingsPayload } from '@src/lib/settings/settingsTypes' +import { Themes } from '@src/lib/theme' +import type { DeepPartial } from '@src/lib/types' +import { onboardingPaths } from '@src/routes/Onboarding/paths' + +import type { Settings } from '@rust/kcl-lib/bindings/Settings' export const IS_PLAYWRIGHT_KEY = 'playwright' export const TEST_SETTINGS_KEY = '/settings.toml' export const TEST_SETTINGS: DeepPartial = { app: { - theme: Themes.Dark, + appearance: { + theme: Themes.Dark, + }, onboarding_status: 'dismissed', - project_directory: '', show_debug_panel: true, }, modeling: { @@ -22,6 +24,7 @@ export const TEST_SETTINGS: DeepPartial = { }, project: { default_project_name: 'project-$nnn', + directory: '', }, text_editor: { text_wrapping: true, @@ -54,7 +57,7 @@ export const TEST_SETTINGS_ONBOARDING_START: DeepPartial = { export const TEST_SETTINGS_DEFAULT_THEME: DeepPartial = { ...TEST_SETTINGS, - app: { ...TEST_SETTINGS.app, theme: Themes.System }, + app: { ...TEST_SETTINGS.app, appearance: { theme: Themes.System } }, } export const TEST_SETTINGS_CORRUPTED = { diff --git a/e2e/playwright/test-network-and-connection-issues.spec.ts b/e2e/playwright/test-network-and-connection-issues.spec.ts index 50bf53b64..e8cf451a4 100644 --- a/e2e/playwright/test-network-and-connection-issues.spec.ts +++ b/e2e/playwright/test-network-and-connection-issues.spec.ts @@ -1,7 +1,12 @@ -import { test, expect } from './zoo-test' -import { commonPoints, getUtils, orRunWhenFullSuiteEnabled } from './test-utils' -import { EngineCommand } from 'lang/std/artifactGraph' -import { uuidv4 } from 'lib/utils' +import type { EngineCommand } from '@src/lang/std/artifactGraph' +import { uuidv4 } from '@src/lib/utils' + +import { + commonPoints, + getUtils, + orRunWhenFullSuiteEnabled, +} from '@e2e/playwright/test-utils' +import { expect, test } from '@e2e/playwright/zoo-test' test.describe('Test network and connection issues', () => { test( diff --git a/e2e/playwright/test-utils.test.ts b/e2e/playwright/test-utils.test.ts index e8dc1491b..9d717b8fa 100644 --- a/e2e/playwright/test-utils.test.ts +++ b/e2e/playwright/test-utils.test.ts @@ -1,9 +1,9 @@ import { + orRunWhenFullSuiteEnabled, runningOnLinux, runningOnMac, runningOnWindows, - orRunWhenFullSuiteEnabled, -} from './test-utils' +} from '@e2e/playwright/test-utils' describe('platform detection utilities', () => { const originalPlatform = process.platform diff --git a/e2e/playwright/test-utils.ts b/e2e/playwright/test-utils.ts index aae8a922c..1e8914046 100644 --- a/e2e/playwright/test-utils.ts +++ b/e2e/playwright/test-utils.ts @@ -1,32 +1,29 @@ -import { - expect, - BrowserContext, - TestInfo, - Locator, - Page, -} from '@playwright/test' -import { test } from './zoo-test' -import { EngineCommand } from 'lang/std/artifactGraph' +import * as TOML from '@iarna/toml' +import type { Models } from '@kittycad/lib' +import type { BrowserContext, Locator, Page, TestInfo } from '@playwright/test' +import { expect } from '@playwright/test' +import type { EngineCommand } from '@src/lang/std/artifactGraph' +import type { Configuration } from '@src/lang/wasm' +import { COOKIE_NAME } from '@src/lib/constants' +import { reportRejection } from '@src/lib/trap' +import type { DeepPartial } from '@src/lib/types' +import { isArray } from '@src/lib/utils' import fsp from 'fs/promises' import path from 'path' import pixelMatch from 'pixelmatch' +import type { Protocol } from 'playwright-core/types/protocol' import { PNG } from 'pngjs' -import { Protocol } from 'playwright-core/types/protocol' -import type { Models } from '@kittycad/lib' -import { COOKIE_NAME } from 'lib/constants' -import { secrets } from './secrets' + +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_KEY, - TEST_SETTINGS, IS_PLAYWRIGHT_KEY, -} from './storageStates' -import * as TOML from '@iarna/toml' -import { isErrorWhitelisted } from './lib/console-error-whitelist' -import { isArray } from 'lib/utils' -import { reportRejection } from 'lib/trap' -import { DeepPartial } from 'lib/types' -import { Configuration } from 'lang/wasm' -import { ProjectConfiguration } from '@rust/kcl-lib/bindings/ProjectConfiguration' + TEST_SETTINGS, + TEST_SETTINGS_KEY, +} from '@e2e/playwright/storageStates' +import { test } from '@e2e/playwright/zoo-test' const toNormalizedCode = (text: string) => { return text.replace(/\s+/g, '') @@ -683,8 +680,8 @@ const _makeTemplate = ( isArray(currentOptions) ? currentOptions[i] : typeof currentOptions === 'number' - ? currentOptions - : '' + ? currentOptions + : '' ) ) }) @@ -903,15 +900,21 @@ export async function setup( settings: { ...TEST_SETTINGS, app: { + appearance: { + ...TEST_SETTINGS.app?.appearance, + theme: 'dark', + }, ...TEST_SETTINGS.project, - project_directory: TEST_SETTINGS.app?.project_directory, onboarding_status: 'dismissed', - theme: 'dark', + }, + project: { + ...TEST_SETTINGS.project, + directory: TEST_SETTINGS.project?.directory, }, }, }), IS_PLAYWRIGHT_KEY, - PLAYWRIGHT_TEST_DIR: TEST_SETTINGS.app?.project_directory || '', + PLAYWRIGHT_TEST_DIR: TEST_SETTINGS.project?.directory || '', PERSIST_MODELING_CONTEXT, } ) @@ -1112,21 +1115,25 @@ export async function pollEditorLinesSelectedLength(page: Page, lines: number) { } export function settingsToToml(settings: DeepPartial) { + // eslint-disable-next-line no-restricted-syntax return TOML.stringify(settings as any) } export function tomlToSettings(toml: string): DeepPartial { + // eslint-disable-next-line no-restricted-syntax return TOML.parse(toml) } export function tomlToPerProjectSettings( toml: string ): DeepPartial { + // eslint-disable-next-line no-restricted-syntax return TOML.parse(toml) } export function perProjectsettingsToToml( settings: DeepPartial ) { + // eslint-disable-next-line no-restricted-syntax return TOML.stringify(settings as any) } diff --git a/e2e/playwright/testing-camera-movement.spec.ts b/e2e/playwright/testing-camera-movement.spec.ts index c900f3fc6..9b649d81c 100644 --- a/e2e/playwright/testing-camera-movement.spec.ts +++ b/e2e/playwright/testing-camera-movement.spec.ts @@ -1,7 +1,8 @@ -import { test, expect } from './zoo-test' -import { EngineCommand } from 'lang/std/artifactGraph' -import { uuidv4 } from 'lib/utils' -import { getUtils, orRunWhenFullSuiteEnabled } from './test-utils' +import type { EngineCommand } from '@src/lang/std/artifactGraph' +import { uuidv4 } from '@src/lib/utils' + +import { getUtils, orRunWhenFullSuiteEnabled } from '@e2e/playwright/test-utils' +import { expect, test } from '@e2e/playwright/zoo-test' test.describe('Testing Camera Movement', { tag: ['@skipWin'] }, () => { test('Can move camera reliably', async ({ page, context, homePage }) => { diff --git a/e2e/playwright/testing-constraints.spec.ts b/e2e/playwright/testing-constraints.spec.ts index da190a058..5b8e4031e 100644 --- a/e2e/playwright/testing-constraints.spec.ts +++ b/e2e/playwright/testing-constraints.spec.ts @@ -1,14 +1,15 @@ -import { test, expect } from './zoo-test' +import { XOR } from '@src/lib/utils' import * as fsp from 'fs/promises' -import { - getUtils, - TEST_COLORS, - pollEditorLinesSelectedLength, - orRunWhenFullSuiteEnabled, -} from './test-utils' -import { XOR } from 'lib/utils' import path from 'node:path' +import { + TEST_COLORS, + getUtils, + orRunWhenFullSuiteEnabled, + pollEditorLinesSelectedLength, +} from '@e2e/playwright/test-utils' +import { expect, test } from '@e2e/playwright/zoo-test' + test.describe('Testing constraints', { tag: ['@skipWin'] }, () => { test('Can constrain line length', async ({ page, homePage }) => { await page.addInitScript(async () => { @@ -1097,7 +1098,7 @@ test.describe('Electron constraint tests', () => { test( 'Able to double click label to set constraint', { tag: '@electron' }, - async ({ page, context, homePage, scene, editor, toolbar }) => { + async ({ page, context, homePage, scene, editor, toolbar, cmdBar }) => { await context.folderSetupFn(async (dir) => { const bracketDir = path.join(dir, 'test-sample') await fsp.mkdir(bracketDir, { recursive: true }) @@ -1131,6 +1132,14 @@ test.describe('Electron constraint tests', () => { await scene.waitForExecutionDone() }) + async function clickOnFirstSegmentLabel() { + const child = page + .locator('.segment-length-label-text') + .first() + .locator('xpath=..') + await child.dblclick() + } + await test.step('Double click to constrain', async () => { // Enter sketch edit mode via feature tree await toolbar.openPane('feature-tree') @@ -1138,21 +1147,19 @@ test.describe('Electron constraint tests', () => { await op.dblclick() await toolbar.closePane('feature-tree') - const child = page - .locator('.segment-length-label-text') - .first() - .locator('xpath=..') - await child.dblclick() - const cmdBarSubmitButton = page.getByRole('button', { - name: 'arrow right Continue', - }) - await cmdBarSubmitButton.click() - await expect(page.locator('.cm-content')).toContainText( - 'length001 = 15.3' - ) - await expect(page.locator('.cm-content')).toContainText( - '|> angledLine([9, length001], %)' - ) + await clickOnFirstSegmentLabel() + await cmdBar.progressCmdBar() + await editor.expectEditor.toContain('length001 = 15.3') + await editor.expectEditor.toContain('|> angledLine([9, length001], %)') + }) + + await test.step('Double click again and expect failure', async () => { + await clickOnFirstSegmentLabel() + + await expect( + page.getByText('Unable to constrain the length of this segment') + ).toBeVisible() + await page.getByRole('button', { name: 'Exit Sketch' }).click() }) } diff --git a/e2e/playwright/testing-gizmo.spec.ts b/e2e/playwright/testing-gizmo.spec.ts index 75b222b32..62422f8fe 100644 --- a/e2e/playwright/testing-gizmo.spec.ts +++ b/e2e/playwright/testing-gizmo.spec.ts @@ -1,7 +1,8 @@ -import { test, expect } from './zoo-test' -import { getUtils } from './test-utils' -import { uuidv4 } from 'lib/utils' -import { TEST_CODE_GIZMO } from './storageStates' +import { uuidv4 } from '@src/lib/utils' + +import { TEST_CODE_GIZMO } from '@e2e/playwright/storageStates' +import { getUtils } from '@e2e/playwright/test-utils' +import { expect, test } from '@e2e/playwright/zoo-test' test.describe('Testing Gizmo', { tag: ['@skipWin'] }, () => { const cases = [ diff --git a/e2e/playwright/testing-perspective-toggle.spec.ts b/e2e/playwright/testing-perspective-toggle.spec.ts index 78c83e7b0..eb1306e98 100644 --- a/e2e/playwright/testing-perspective-toggle.spec.ts +++ b/e2e/playwright/testing-perspective-toggle.spec.ts @@ -1,5 +1,5 @@ -import { test, expect } from './zoo-test' -import { getUtils, orRunWhenFullSuiteEnabled } from './test-utils' +import { getUtils, orRunWhenFullSuiteEnabled } from '@e2e/playwright/test-utils' +import { expect, test } from '@e2e/playwright/zoo-test' test.describe('Test toggling perspective', () => { test('via command palette and toggle', async ({ page, homePage }) => { diff --git a/e2e/playwright/testing-samples-loading.spec.ts b/e2e/playwright/testing-samples-loading.spec.ts index 182925fe9..0e033e89b 100644 --- a/e2e/playwright/testing-samples-loading.spec.ts +++ b/e2e/playwright/testing-samples-loading.spec.ts @@ -1,9 +1,10 @@ -import { test, expect } from './zoo-test' -import { getUtils } from './test-utils' -import { bracket } from 'lib/exampleKcl' +import { FILE_EXT } from '@src/lib/constants' +import { bracket } from '@src/lib/exampleKcl' import * as fsp from 'fs/promises' import { join } from 'path' -import { FILE_EXT } from 'lib/constants' + +import { getUtils } from '@e2e/playwright/test-utils' +import { expect, test } from '@e2e/playwright/zoo-test' test.describe('Testing in-app sample loading', () => { /** diff --git a/e2e/playwright/testing-segment-overlays.spec.ts b/e2e/playwright/testing-segment-overlays.spec.ts index 809ad55bb..e761d9ff6 100644 --- a/e2e/playwright/testing-segment-overlays.spec.ts +++ b/e2e/playwright/testing-segment-overlays.spec.ts @@ -1,15 +1,15 @@ -import { Page } from '@playwright/test' -import { test, expect } from './zoo-test' +import type { Page } from '@playwright/test' +import type { LineInputsType } from '@src/lang/std/sketchcombos' +import { uuidv4 } from '@src/lib/utils' +import type { EditorFixture } from '@e2e/playwright/fixtures/editorFixture' import { deg, getUtils, - wiggleMove, orRunWhenFullSuiteEnabled, -} from './test-utils' -import { LineInputsType } from 'lang/std/sketchcombos' -import { uuidv4 } from 'lib/utils' -import { EditorFixture } from './fixtures/editorFixture' + wiggleMove, +} from '@e2e/playwright/test-utils' +import { expect, test } from '@e2e/playwright/zoo-test' test.describe('Testing segment overlays', { tag: ['@skipWin'] }, () => { test('Hover over a segment should show its overlay, hovering over the input overlays should show its popover, clicking the input overlay should constrain/unconstrain it:\nfor the following segments', () => { diff --git a/e2e/playwright/testing-selections.spec.ts b/e2e/playwright/testing-selections.spec.ts index 0b1ded979..756375f0c 100644 --- a/e2e/playwright/testing-selections.spec.ts +++ b/e2e/playwright/testing-selections.spec.ts @@ -1,9 +1,13 @@ -import { test, expect } from './zoo-test' +import type { Coords2d } from '@src/lang/std/sketch' +import { KCL_DEFAULT_LENGTH } from '@src/lib/constants' +import { uuidv4 } from '@src/lib/utils' -import { commonPoints, getUtils, orRunWhenFullSuiteEnabled } from './test-utils' -import { Coords2d } from 'lang/std/sketch' -import { KCL_DEFAULT_LENGTH } from 'lib/constants' -import { uuidv4 } from 'lib/utils' +import { + commonPoints, + getUtils, + orRunWhenFullSuiteEnabled, +} from '@e2e/playwright/test-utils' +import { expect, test } from '@e2e/playwright/zoo-test' test.describe('Testing selections', { tag: ['@skipWin'] }, () => { test.setTimeout(90_000) diff --git a/e2e/playwright/testing-settings.spec.ts b/e2e/playwright/testing-settings.spec.ts index c57eccf23..8cd4241e0 100644 --- a/e2e/playwright/testing-settings.spec.ts +++ b/e2e/playwright/testing-settings.spec.ts @@ -1,24 +1,29 @@ -import { test, expect } from './zoo-test' +import { + PROJECT_SETTINGS_FILE_NAME, + SETTINGS_FILE_NAME, +} from '@src/lib/constants' +import type { SettingsLevel } from '@src/lib/settings/settingsTypes' +import type { DeepPartial } from '@src/lib/types' import * as fsp from 'fs/promises' import { join } from 'path' + +import type { Settings } from '@rust/kcl-lib/bindings/Settings' + import { - getUtils, - executorInputPath, - createProject, - tomlToSettings, - TEST_COLORS, - orRunWhenFullSuiteEnabled, -} from './test-utils' -import { SettingsLevel } from 'lib/settings/settingsTypes' -import { SETTINGS_FILE_NAME, PROJECT_SETTINGS_FILE_NAME } from 'lib/constants' -import { - TEST_SETTINGS_KEY, - TEST_SETTINGS_CORRUPTED, TEST_SETTINGS, + TEST_SETTINGS_CORRUPTED, TEST_SETTINGS_DEFAULT_THEME, -} from './storageStates' -import { DeepPartial } from 'lib/types' -import { Settings } from '@rust/kcl-lib/bindings/Settings' + TEST_SETTINGS_KEY, +} from '@e2e/playwright/storageStates' +import { + TEST_COLORS, + createProject, + executorInputPath, + getUtils, + orRunWhenFullSuiteEnabled, + tomlToSettings, +} from '@e2e/playwright/test-utils' +import { expect, test } from '@e2e/playwright/zoo-test' test.describe('Testing settings', () => { test('Stored settings are validated and fall back to defaults', async ({ @@ -45,12 +50,12 @@ test.describe('Testing settings', () => { ) ) - expect(storedSettings.settings?.app?.theme).toBe('dark') + expect(storedSettings.settings?.app?.appearance?.theme).toBe('dark') // Check that the invalid settings were changed to good defaults expect(storedSettings.settings?.modeling?.base_unit).toBe('in') expect(storedSettings.settings?.modeling?.mouse_controls).toBe('zoo') - expect(storedSettings.settings?.app?.project_directory).toBe('') + expect(storedSettings.settings?.project?.directory).toBe('') expect(storedSettings.settings?.project?.default_project_name).toBe( 'project-$nnn' ) @@ -381,7 +386,9 @@ test.describe('Testing settings', () => { } await tronApp.cleanProjectDir({ app: { - theme_color: '259', + appearance: { + color: 259, + }, }, }) @@ -413,9 +420,12 @@ test.describe('Testing settings', () => { await tronApp.cleanProjectDir({ app: { - // Doesn't matter what you set it to. It will - // default to 264.5 - theme_color: '0', + appearance: { + // Doesn't matter what you set it to. It will + // default to 264.5 + + color: 0, + }, }, }) diff --git a/e2e/playwright/text-to-cad-tests.spec.ts b/e2e/playwright/text-to-cad-tests.spec.ts index 8dddbcb33..da2406141 100644 --- a/e2e/playwright/text-to-cad-tests.spec.ts +++ b/e2e/playwright/text-to-cad-tests.spec.ts @@ -1,12 +1,13 @@ -import { Page } from '@playwright/test' -import { test, expect } from './zoo-test' -import { - getUtils, - createProject, - orRunWhenFullSuiteEnabled, -} from './test-utils' -import { join } from 'path' +import type { Page } from '@playwright/test' import fs from 'fs' +import { join } from 'path' + +import { + createProject, + getUtils, + orRunWhenFullSuiteEnabled, +} from '@e2e/playwright/test-utils' +import { expect, test } from '@e2e/playwright/zoo-test' test.describe('Text-to-CAD tests', { tag: ['@skipWin'] }, () => { test('basic lego happy case', async ({ page, homePage }) => { diff --git a/e2e/playwright/various.spec.ts b/e2e/playwright/various.spec.ts index d49ff9514..2488404b4 100644 --- a/e2e/playwright/various.spec.ts +++ b/e2e/playwright/various.spec.ts @@ -1,11 +1,10 @@ -import { test, expect } from './zoo-test' - import { doExport, getUtils, makeTemplate, orRunWhenFullSuiteEnabled, -} from './test-utils' +} from '@e2e/playwright/test-utils' +import { expect, test } from '@e2e/playwright/zoo-test' test('Units menu', async ({ page, homePage }) => { test.fixme(orRunWhenFullSuiteEnabled()) diff --git a/e2e/playwright/zoo-test.ts b/e2e/playwright/zoo-test.ts index 5096686a4..63010757b 100644 --- a/e2e/playwright/zoo-test.ts +++ b/e2e/playwright/zoo-test.ts @@ -1,12 +1,11 @@ /* eslint-disable react-hooks/rules-of-hooks */ - import { test as playwrightTestFn } from '@playwright/test' +import type { Fixtures } from '@e2e/playwright/fixtures/fixtureSetup' import { - fixturesBasedOnProcessEnvPlatform, - Fixtures, ElectronZoo, -} from './fixtures/fixtureSetup' + fixturesBasedOnProcessEnvPlatform, +} from '@e2e/playwright/fixtures/fixtureSetup' export { expect } from '@playwright/test' diff --git a/forge.config.ts b/forge.config.ts index 5a079660e..ace957e8b 100644 --- a/forge.config.ts +++ b/forge.config.ts @@ -1,6 +1,6 @@ -import type { ForgeConfig } from '@electron-forge/shared-types' -import { VitePlugin } from '@electron-forge/plugin-vite' import { FusesPlugin } from '@electron-forge/plugin-fuses' +import { VitePlugin } from '@electron-forge/plugin-vite' +import type { ForgeConfig } from '@electron-forge/shared-types' import { FuseV1Options, FuseVersion } from '@electron/fuses' import path from 'path' diff --git a/forge.env.d.ts b/forge.env.d.ts index eb0a5a87e..4408b1e64 100644 --- a/forge.env.d.ts +++ b/forge.env.d.ts @@ -26,7 +26,7 @@ declare global { declare module 'vite' { interface ConfigEnv< - K extends keyof VitePluginConfig = keyof VitePluginConfig + K extends keyof VitePluginConfig = keyof VitePluginConfig, > { root: string forgeConfig: VitePluginConfig diff --git a/interface.d.ts b/interface.d.ts index 27c97f3ac..81f789ea7 100644 --- a/interface.d.ts +++ b/interface.d.ts @@ -1,13 +1,10 @@ -import fs from 'node:fs/promises' -import fsSync from 'node:fs' -import path from 'path' -import { dialog, shell } from 'electron' import { MachinesListing } from 'components/MachineManagerProvider' -import type { Channel } from 'src/menu/channels' -import { Menu, WebContents } from 'electron' -import { ZooLabel, ZooMenuEvents } from 'menu/roles' -import type { MenuActionIPC } from 'menu/rules' +import 'electron' +import { dialog, shell } from 'electron' import type { WebContentSendPayload } from 'menu/channels' +import { ZooLabel } from 'menu/roles' +import fs from 'node:fs/promises' +import path from 'path' // Extend the interface with additional custom properties declare module 'electron' { diff --git a/package.json b/package.json index fe5c1a0ad..641aa5f70 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "@fortawesome/react-fontawesome": "^0.2.0", "@headlessui/react": "^1.7.19", "@headlessui/tailwindcss": "^0.2.2", - "@kittycad/lib": "2.0.23", + "@kittycad/lib": "2.0.25", "@lezer/highlight": "^1.2.1", "@lezer/lr": "^1.4.1", "@react-hook/resize-observer": "^2.0.1", @@ -88,9 +88,9 @@ "simpleserver:ci": "yarn pretest && http-server ./public --cors -p 3000 &", "simpleserver:bg": "yarn pretest && http-server ./public --cors -p 3000 &", "simpleserver:stop": "kill-port 3000", - "fmt": "prettier --write ./src *.ts *.json *.js ./e2e ./packages ./rust/kcl-language-server ./rust/kcl-lib/bindings ./rust/kcl-wasm-lib/pkg", - "fmt:generated": "prettier --write *.ts *.json *.js ./rust/kcl-lib/bindings ./rust/kcl-wasm-lib/pkg", - "fmt-check": "prettier --check ./src *.ts *.json *.js ./e2e ./packages ./rust/kcl-language-server", + "fmt": "prettier --write .eslintrc.json ./src *.ts *.json *.js ./e2e ./packages ./rust/kcl-language-server ./rust/kcl-lib/bindings ./rust/kcl-wasm-lib/pkg", + "fmt:generated": "prettier --write .eslintrc.json *.ts *.json *.js ./rust/kcl-lib/bindings ./rust/kcl-wasm-lib/pkg", + "fmt-check": "prettier --check .eslintrc.json ./src *.ts *.json *.js ./e2e ./packages ./rust/kcl-language-server", "fetch:wasm": "./scripts/get-latest-wasm-bundle.sh", "fetch:wasm:windows": "./scripts/get-latest-wasm-bundle.ps1", "fetch:samples": "rm -rf public/kcl-samples* && curl -L -o public/kcl-samples.zip https://github.com/KittyCAD/kcl-samples/archive/refs/heads/achalmers/kw-args-xylineto.zip && unzip -o public/kcl-samples.zip -d public && mv public/kcl-samples-* public/kcl-samples", @@ -102,6 +102,7 @@ "remove-importmeta": "sed -i 's/import.meta.url/window.location.origin/g' \"./rust/kcl-wasm-lib/pkg/kcl_wasm_lib.js\"; sed -i '' 's/import.meta.url/window.location.origin/g' \"./rust/kcl-wasm-lib/pkg/kcl_wasm_lib.js\" || echo \"sed for both mac and linux\"", "lint-fix": "eslint --fix --ext .ts --ext .tsx src e2e packages/codemirror-lsp-client/src rust/kcl-language-server/client/src", "lint": "eslint --max-warnings 0 --ext .ts --ext .tsx src e2e packages/codemirror-lsp-client/src rust/kcl-language-server/client/src", + "circular-deps": "dpdm --no-warning --no-tree -T --skip-dynamic-imports=circular src/index.tsx", "files:set-version": "echo \"$(jq --arg v \"$VERSION\" '.version=$v' package.json --indent 2)\" > package.json", "files:set-notes": "./scripts/set-files-notes.sh", "files:flip-to-nightly": "./scripts/flip-files-to-nightly.sh", @@ -139,7 +140,20 @@ "trailingComma": "es5", "tabWidth": 2, "semi": false, - "singleQuote": true + "singleQuote": true, + "importOrder": [ + "", + "^@rust/(.*)$", + "^@e2e/(.*)$", + "^@src/(.*)$", + "^[./]" + ], + "importOrderSeparation": true, + "importOrderSortSpecifiers": true, + "plugins": [ + "@trivago/prettier-plugin-sort-imports", + "prettier-plugin-organize-imports" + ] }, "browserslist": { "production": [ @@ -167,6 +181,7 @@ "@playwright/test": "^1.51.1", "@testing-library/jest-dom": "^5.14.1", "@testing-library/react": "^15.0.2", + "@trivago/prettier-plugin-sort-imports": "^5.2.2", "@types/diff": "^7.0.2", "@types/electron": "^1.6.10", "@types/isomorphic-fetch": "^0.0.39", @@ -187,6 +202,7 @@ "@vitest/web-worker": "^1.5.0", "@xstate/cli": "^0.5.17", "autoprefixer": "^10.4.21", + "dpdm": "^3.14.0", "electron": "^34.1.1", "electron-builder": "^26.0.12", "eslint": "^8.0.1", @@ -208,7 +224,8 @@ "pngjs": "^7.0.0", "postcss": "^8.4.43", "postinstall-postinstall": "^2.1.0", - "prettier": "^2.8.8", + "prettier": "^3.5.3", + "prettier-plugin-organize-imports": "^4.1.0", "setimmediate": "^1.0.5", "tailwindcss": "^3.4.17", "ts-node": "^10.0.0", diff --git a/packages/codemirror-lang-kcl/rollup.config.js b/packages/codemirror-lang-kcl/rollup.config.js index d558688be..deadb2519 100644 --- a/packages/codemirror-lang-kcl/rollup.config.js +++ b/packages/codemirror-lang-kcl/rollup.config.js @@ -1,6 +1,6 @@ -import dts from 'rollup-plugin-dts' import { lezer } from '@lezer/generator/rollup' import typescript from '@rollup/plugin-typescript' +import dts from 'rollup-plugin-dts' export default [ { diff --git a/packages/codemirror-lang-kcl/src/index.ts b/packages/codemirror-lang-kcl/src/index.ts index 7d77f1375..238340459 100644 --- a/packages/codemirror-lang-kcl/src/index.ts +++ b/packages/codemirror-lang-kcl/src/index.ts @@ -1,14 +1,14 @@ // Base CodeMirror language support for kcl. - import { LRLanguage, LanguageSupport, - indentNodeProp, continuedIndent, delimitedIndent, - foldNodeProp, foldInside, + foldNodeProp, + indentNodeProp, } from '@codemirror/language' + // @ts-ignore: No types available import { parser } from './kcl.grammar' diff --git a/packages/codemirror-lang-kcl/test/all.test.ts b/packages/codemirror-lang-kcl/test/all.test.ts index dba913ceb..5d03f09f3 100644 --- a/packages/codemirror-lang-kcl/test/all.test.ts +++ b/packages/codemirror-lang-kcl/test/all.test.ts @@ -1,9 +1,9 @@ -import { KclLanguage } from '../src/index' import { fileTests } from '@lezer/generator/dist/test' - import * as fs from 'fs' import * as path from 'path' +import { KclLanguage } from '../src/index' + let caseDir = path.dirname(__filename) for (let file of fs.readdirSync(caseDir)) { diff --git a/packages/codemirror-lang-kcl/vitest.main.config.ts b/packages/codemirror-lang-kcl/vitest.main.config.ts index 814142a18..e16fa0092 100644 --- a/packages/codemirror-lang-kcl/vitest.main.config.ts +++ b/packages/codemirror-lang-kcl/vitest.main.config.ts @@ -1,9 +1,8 @@ // Overrides the test options from the modeling-app config. - -import viteTsconfigPaths from 'vite-tsconfig-paths' -import { defineConfig, configDefaults } from 'vitest/config' // @ts-ignore: No types available import { lezer } from '@lezer/generator/rollup' +import viteTsconfigPaths from 'vite-tsconfig-paths' +import { defineConfig } from 'vitest/config' const config = defineConfig({ test: { diff --git a/packages/codemirror-lsp-client/src/client/codec/bytes.ts b/packages/codemirror-lsp-client/src/client/codec/bytes.ts index 73d778752..f90c8994f 100644 --- a/packages/codemirror-lsp-client/src/client/codec/bytes.ts +++ b/packages/codemirror-lsp-client/src/client/codec/bytes.ts @@ -1,4 +1,4 @@ -import { encoder, decoder } from '../codec' +import { decoder, encoder } from './encode-decode' export default class Bytes { static encode(input: string): Uint8Array { @@ -10,7 +10,7 @@ export default class Bytes { } static append< - T extends { length: number; set(arr: T, offset: number): void } + T extends { length: number; set(arr: T, offset: number): void }, >(constructor: { new (length: number): T }, ...arrays: T[]) { let totalLength = 0 for (const arr of arrays) { diff --git a/packages/codemirror-lsp-client/src/client/codec/demuxer.ts b/packages/codemirror-lsp-client/src/client/codec/demuxer.ts index dff846d6a..c4690d0a1 100644 --- a/packages/codemirror-lsp-client/src/client/codec/demuxer.ts +++ b/packages/codemirror-lsp-client/src/client/codec/demuxer.ts @@ -1,10 +1,10 @@ import * as vsrpc from 'vscode-jsonrpc' -import { Codec } from '.' import Bytes from './bytes' +import PromiseMap from './map' import Queue from './queue' import Tracer from './tracer' -import PromiseMap from './map' +import { Codec } from './utils' export default class StreamDemuxer extends Queue { readonly responses: PromiseMap = diff --git a/packages/codemirror-lsp-client/src/client/codec/encode-decode.ts b/packages/codemirror-lsp-client/src/client/codec/encode-decode.ts new file mode 100644 index 000000000..30325efc4 --- /dev/null +++ b/packages/codemirror-lsp-client/src/client/codec/encode-decode.ts @@ -0,0 +1,2 @@ +export const encoder = new TextEncoder() +export const decoder = new TextDecoder() diff --git a/packages/codemirror-lsp-client/src/client/codec/index.ts b/packages/codemirror-lsp-client/src/client/codec/index.ts index db50b1392..4c1ea80ab 100644 --- a/packages/codemirror-lsp-client/src/client/codec/index.ts +++ b/packages/codemirror-lsp-client/src/client/codec/index.ts @@ -1,8 +1,7 @@ -import * as jsrpc from 'json-rpc-2.0' -import * as vsrpc from 'vscode-jsonrpc' +import type * as vsrpc from 'vscode-jsonrpc' -import Bytes from './bytes' import StreamDemuxer from './demuxer' +import { decoder } from './encode-decode' import Headers from './headers' import Queue from './queue' import Tracer from './tracer' @@ -12,25 +11,6 @@ export enum LspWorkerEventType { Call = 'call', } -export const encoder = new TextEncoder() -export const decoder = new TextDecoder() - -export class Codec { - static encode( - json: jsrpc.JSONRPCRequest | jsrpc.JSONRPCResponse - ): Uint8Array { - const message = JSON.stringify(json) - const delimited = Headers.add(message) - return Bytes.encode(delimited) - } - - static decode(data: Uint8Array): T { - const delimited = Bytes.decode(data) - const message = Headers.remove(delimited) - return JSON.parse(message) as T - } -} - // FIXME: tracing efficiency export class IntoServer extends Queue diff --git a/packages/codemirror-lsp-client/src/client/codec/tracer.ts b/packages/codemirror-lsp-client/src/client/codec/tracer.ts index 816540c8b..3bde2fcb2 100644 --- a/packages/codemirror-lsp-client/src/client/codec/tracer.ts +++ b/packages/codemirror-lsp-client/src/client/codec/tracer.ts @@ -1,4 +1,4 @@ -import { Message } from 'vscode-languageserver-protocol' +import type { Message } from 'vscode-languageserver-protocol' export default class Tracer { static client(message: string): void { diff --git a/packages/codemirror-lsp-client/src/client/codec/utils.ts b/packages/codemirror-lsp-client/src/client/codec/utils.ts new file mode 100644 index 000000000..bb27e60bf --- /dev/null +++ b/packages/codemirror-lsp-client/src/client/codec/utils.ts @@ -0,0 +1,20 @@ +import type * as jsrpc from 'json-rpc-2.0' + +import Bytes from './bytes' +import Headers from './headers' + +export class Codec { + static encode( + json: jsrpc.JSONRPCRequest | jsrpc.JSONRPCResponse + ): Uint8Array { + const message = JSON.stringify(json) + const delimited = Headers.add(message) + return Bytes.encode(delimited) + } + + static decode(data: Uint8Array): T { + const delimited = Bytes.decode(data) + const message = Headers.remove(delimited) + return JSON.parse(message) as T + } +} diff --git a/packages/codemirror-lsp-client/src/client/index.ts b/packages/codemirror-lsp-client/src/client/index.ts index 822957482..63ffe744b 100644 --- a/packages/codemirror-lsp-client/src/client/index.ts +++ b/packages/codemirror-lsp-client/src/client/index.ts @@ -1,8 +1,8 @@ import type * as LSP from 'vscode-languageserver-protocol' -import { FromServer, IntoServer } from './codec' +import type { LanguageServerPlugin } from '../plugin/lsp' +import type { FromServer, IntoServer } from './codec' import Client from './jsonrpc' -import { LanguageServerPlugin } from '../plugin/lsp' // https://microsoft.github.io/language-server-protocol/specifications/specification-current/ @@ -12,15 +12,15 @@ interface LSPRequestMap { 'textDocument/hover': [LSP.HoverParams, LSP.Hover] 'textDocument/completion': [ LSP.CompletionParams, - LSP.CompletionItem[] | LSP.CompletionList | null + LSP.CompletionItem[] | LSP.CompletionList | null, ] 'textDocument/semanticTokens/full': [ LSP.SemanticTokensParams, - LSP.SemanticTokens + LSP.SemanticTokens, ] 'textDocument/formatting': [ LSP.DocumentFormattingParams, - LSP.TextEdit[] | null + LSP.TextEdit[] | null, ] 'textDocument/foldingRange': [LSP.FoldingRangeParams, LSP.FoldingRange[]] } diff --git a/packages/codemirror-lsp-client/src/client/jsonrpc.ts b/packages/codemirror-lsp-client/src/client/jsonrpc.ts index 0a18bf9d0..9bb873684 100644 --- a/packages/codemirror-lsp-client/src/client/jsonrpc.ts +++ b/packages/codemirror-lsp-client/src/client/jsonrpc.ts @@ -1,11 +1,12 @@ import * as jsrpc from 'json-rpc-2.0' import * as LSP from 'vscode-languageserver-protocol' +import type { FromServer, IntoServer } from './codec' +import { Codec } from './codec/utils' import { registerServerCapability, unregisterServerCapability, } from './server-capability-registration' -import { Codec, FromServer, IntoServer } from './codec' const client_capabilities: LSP.ClientCapabilities = { textDocument: { diff --git a/packages/codemirror-lsp-client/src/client/server-capability-registration.ts b/packages/codemirror-lsp-client/src/client/server-capability-registration.ts index 36b21566c..6dc9304a1 100644 --- a/packages/codemirror-lsp-client/src/client/server-capability-registration.ts +++ b/packages/codemirror-lsp-client/src/client/server-capability-registration.ts @@ -1,4 +1,4 @@ -import { +import type { Registration, ServerCapabilities, Unregistration, diff --git a/packages/codemirror-lsp-client/src/index.ts b/packages/codemirror-lsp-client/src/index.ts index 708aca942..3bcb983d9 100644 --- a/packages/codemirror-lsp-client/src/index.ts +++ b/packages/codemirror-lsp-client/src/index.ts @@ -1,36 +1,34 @@ import { foldService } from '@codemirror/language' -import { Extension, EditorState } from '@codemirror/state' +import type { EditorState, Extension } from '@codemirror/state' import { ViewPlugin } from '@codemirror/view' +import type { LanguageServerOptions } from './plugin/lsp' import { - docPathFacet, LanguageServerPlugin, LanguageServerPluginSpec, + docPathFacet, languageId, workspaceFolders, - LanguageServerOptions, } from './plugin/lsp' -export type { LanguageServerClientOptions } from './client' export { LanguageServerClient } from './client' +export type { LanguageServerClientOptions } from './client' +export { FromServer, IntoServer, LspWorkerEventType } from './client/codec' +export { Codec } from './client/codec/utils' export { - Codec, - FromServer, - IntoServer, - LspWorkerEventType, -} from './client/codec' -export type { LanguageServerOptions } from './plugin/lsp' + lspDiagnosticsEvent, + lspFormatCodeEvent, + lspSemanticTokensEvent, +} from './plugin/annotation' export { LanguageServerPlugin, LanguageServerPluginSpec, docPathFacet, languageId, workspaceFolders, - lspSemanticTokensEvent, - lspDiagnosticsEvent, - lspFormatCodeEvent, } from './plugin/lsp' -export { posToOffset, offsetToPos } from './plugin/util' +export type { LanguageServerOptions } from './plugin/lsp' +export { offsetToPos, posToOffset } from './plugin/util' export function lspPlugin(options: LanguageServerOptions): Extension { let plugin: LanguageServerPlugin | null = null diff --git a/packages/codemirror-lsp-client/src/plugin/annotation.ts b/packages/codemirror-lsp-client/src/plugin/annotation.ts new file mode 100644 index 000000000..10feb8135 --- /dev/null +++ b/packages/codemirror-lsp-client/src/plugin/annotation.ts @@ -0,0 +1,12 @@ +import { Annotation } from '@codemirror/state' + +export enum LspAnnotation { + SemanticTokens = 'semantic-tokens', + FormatCode = 'format-code', + Diagnostics = 'diagnostics', +} + +const lspEvent = Annotation.define() +export const lspSemanticTokensEvent = lspEvent.of(LspAnnotation.SemanticTokens) +export const lspFormatCodeEvent = lspEvent.of(LspAnnotation.FormatCode) +export const lspDiagnosticsEvent = lspEvent.of(LspAnnotation.Diagnostics) diff --git a/packages/codemirror-lsp-client/src/plugin/autocomplete.ts b/packages/codemirror-lsp-client/src/plugin/autocomplete.ts index a1352c71c..9d2baebf7 100644 --- a/packages/codemirror-lsp-client/src/plugin/autocomplete.ts +++ b/packages/codemirror-lsp-client/src/plugin/autocomplete.ts @@ -9,17 +9,18 @@ import { prevSnippetField, startCompletion, } from '@codemirror/autocomplete' -import { Prec, Extension } from '@codemirror/state' -import { EditorView, keymap, KeyBinding, ViewPlugin } from '@codemirror/view' - +import { syntaxTree } from '@codemirror/language' +import type { Extension } from '@codemirror/state' +import { Prec } from '@codemirror/state' +import type { EditorView, KeyBinding, ViewPlugin } from '@codemirror/view' +import { keymap } from '@codemirror/view' import { CompletionItemKind, CompletionTriggerKind, } from 'vscode-languageserver-protocol' -import { LanguageServerPlugin } from './lsp' +import type { LanguageServerPlugin } from './lsp' import { offsetToPos } from './util' -import { syntaxTree } from '@codemirror/language' export const CompletionItemKindMap = Object.fromEntries( Object.entries(CompletionItemKind).map(([key, value]) => [value, key]) diff --git a/packages/codemirror-lsp-client/src/plugin/format.ts b/packages/codemirror-lsp-client/src/plugin/format.ts index d7cedc1a4..4a33a35d6 100644 --- a/packages/codemirror-lsp-client/src/plugin/format.ts +++ b/packages/codemirror-lsp-client/src/plugin/format.ts @@ -1,7 +1,9 @@ -import { Extension, Prec } from '@codemirror/state' -import { EditorView, keymap, KeyBinding, ViewPlugin } from '@codemirror/view' +import type { Extension } from '@codemirror/state' +import { Prec } from '@codemirror/state' +import type { EditorView, KeyBinding, ViewPlugin } from '@codemirror/view' +import { keymap } from '@codemirror/view' -import { LanguageServerPlugin } from './lsp' +import type { LanguageServerPlugin } from './lsp' export default function lspFormatExt( plugin: ViewPlugin diff --git a/packages/codemirror-lsp-client/src/plugin/hover.ts b/packages/codemirror-lsp-client/src/plugin/hover.ts index ec2a843d7..02b1d566d 100644 --- a/packages/codemirror-lsp-client/src/plugin/hover.ts +++ b/packages/codemirror-lsp-client/src/plugin/hover.ts @@ -1,12 +1,8 @@ -import { Extension } from '@codemirror/state' -import { - hoverTooltip, - tooltips, - ViewPlugin, - EditorView, -} from '@codemirror/view' +import type { Extension } from '@codemirror/state' +import type { ViewPlugin } from '@codemirror/view' +import { EditorView, hoverTooltip, tooltips } from '@codemirror/view' -import { LanguageServerPlugin } from './lsp' +import type { LanguageServerPlugin } from './lsp' import { offsetToPos } from './util' export default function lspHoverExt( diff --git a/packages/codemirror-lsp-client/src/plugin/indent.ts b/packages/codemirror-lsp-client/src/plugin/indent.ts index 323301b0e..c835736b1 100644 --- a/packages/codemirror-lsp-client/src/plugin/indent.ts +++ b/packages/codemirror-lsp-client/src/plugin/indent.ts @@ -1,5 +1,5 @@ import { indentService } from '@codemirror/language' -import { Extension } from '@codemirror/state' +import type { Extension } from '@codemirror/state' export default function lspIndentExt(): Extension { // Match the indentation of the previous line (if present). diff --git a/packages/codemirror-lsp-client/src/plugin/lsp.ts b/packages/codemirror-lsp-client/src/plugin/lsp.ts index c8c7e0fb6..b68c8a783 100644 --- a/packages/codemirror-lsp-client/src/plugin/lsp.ts +++ b/packages/codemirror-lsp-client/src/plugin/lsp.ts @@ -4,39 +4,34 @@ import type { CompletionResult, } from '@codemirror/autocomplete' import { completeFromList, snippetCompletion } from '@codemirror/autocomplete' -import { - Facet, - StateEffect, - Extension, - Transaction, - Annotation, -} from '@codemirror/state' -import type { - ViewUpdate, - PluginValue, - PluginSpec, - ViewPlugin, -} from '@codemirror/view' -import { EditorView, Tooltip } from '@codemirror/view' import { linter } from '@codemirror/lint' - -import type { PublishDiagnosticsParams } from 'vscode-languageserver-protocol' +import type { Extension, StateEffect } from '@codemirror/state' +import { Facet, Transaction } from '@codemirror/state' +import type { + EditorView, + PluginSpec, + PluginValue, + Tooltip, + ViewPlugin, + ViewUpdate, +} from '@codemirror/view' import type * as LSP from 'vscode-languageserver-protocol' -import { - DiagnosticSeverity, +import type { CompletionTriggerKind, + PublishDiagnosticsParams, } from 'vscode-languageserver-protocol' +import { DiagnosticSeverity } from 'vscode-languageserver-protocol' import { URI } from 'vscode-uri' -import { LanguageServerClient } from '../client' -import { CompletionItemKindMap } from './autocomplete' -import { addToken, SemanticToken } from './semantic-tokens' -import { posToOffset, formatMarkdownContents } from './util' -import lspAutocompleteExt from './autocomplete' -import lspHoverExt from './hover' +import type { LanguageServerClient } from '../client' +import { lspFormatCodeEvent, lspSemanticTokensEvent } from './annotation' +import lspAutocompleteExt, { CompletionItemKindMap } from './autocomplete' import lspFormatExt from './format' +import lspHoverExt from './hover' import lspIndentExt from './indent' -import lspSemanticTokensExt from './semantic-tokens' +import type { SemanticToken } from './semantic-tokens' +import lspSemanticTokensExt, { addToken } from './semantic-tokens' +import { formatMarkdownContents, posToOffset } from './util' const useLast = (values: readonly any[]) => values.reduce((_, v) => v, '') export const docPathFacet = Facet.define({ @@ -48,17 +43,6 @@ export const workspaceFolders = Facet.define< LSP.WorkspaceFolder[] >({ combine: useLast }) -export enum LspAnnotation { - SemanticTokens = 'semantic-tokens', - FormatCode = 'format-code', - Diagnostics = 'diagnostics', -} - -const lspEvent = Annotation.define() -export const lspSemanticTokensEvent = lspEvent.of(LspAnnotation.SemanticTokens) -export const lspFormatCodeEvent = lspEvent.of(LspAnnotation.FormatCode) -export const lspDiagnosticsEvent = lspEvent.of(LspAnnotation.Diagnostics) - export interface LanguageServerOptions { // We assume this is the main project directory, we are currently working in. workspaceFolders: LSP.WorkspaceFolder[] @@ -98,7 +82,10 @@ export class LanguageServerPlugin implements PluginValue { // document. private sendScheduled: number | null = null - constructor(options: LanguageServerOptions, private view: EditorView) { + constructor( + options: LanguageServerOptions, + private view: EditorView + ) { this.client = options.client this.documentVersion = 0 diff --git a/packages/codemirror-lsp-client/src/plugin/semantic-tokens.ts b/packages/codemirror-lsp-client/src/plugin/semantic-tokens.ts index 02af94d33..4ed17b0e4 100644 --- a/packages/codemirror-lsp-client/src/plugin/semantic-tokens.ts +++ b/packages/codemirror-lsp-client/src/plugin/semantic-tokens.ts @@ -1,10 +1,12 @@ import { highlightingFor } from '@codemirror/language' -import { StateEffect, StateField, Extension } from '@codemirror/state' -import { EditorView, Decoration, DecorationSet } from '@codemirror/view' +import type { Extension } from '@codemirror/state' +import { StateEffect, StateField } from '@codemirror/state' +import type { DecorationSet } from '@codemirror/view' +import { Decoration, EditorView } from '@codemirror/view' +import type { Tag } from '@lezer/highlight' +import { tags } from '@lezer/highlight' -import { Tag, tags } from '@lezer/highlight' - -import { lspSemanticTokensEvent } from './lsp' +import { lspSemanticTokensEvent } from './annotation' export interface SemanticToken { from: number diff --git a/packages/codemirror-lsp-client/src/plugin/util.ts b/packages/codemirror-lsp-client/src/plugin/util.ts index f70e429f3..53fa1fbe3 100644 --- a/packages/codemirror-lsp-client/src/plugin/util.ts +++ b/packages/codemirror-lsp-client/src/plugin/util.ts @@ -1,7 +1,8 @@ -import { Text } from '@codemirror/state' -import { Marked, MarkedOptions } from '@ts-stack/markdown' - +import type { Text } from '@codemirror/state' +import type { MarkedOptions } from '@ts-stack/markdown' +import { Marked } from '@ts-stack/markdown' import type * as LSP from 'vscode-languageserver-protocol' + import { isArray } from '../lib/utils' // takes a function and executes it after the wait time, if the function is called again before the wait time is up, the timer is reset diff --git a/rust/kcl-language-server/client/src/bootstrap.ts b/rust/kcl-language-server/client/src/bootstrap.ts index 77d5e0f04..fdda77e8f 100644 --- a/rust/kcl-language-server/client/src/bootstrap.ts +++ b/rust/kcl-language-server/client/src/bootstrap.ts @@ -1,10 +1,11 @@ /* eslint suggest-no-throw/suggest-no-throw: 0 */ -import * as vscode from 'vscode' -import * as os from 'os' -import type { Config } from './config' -import { log, isValidExecutable } from './util' -import type { PersistentState } from './persistent_state' import { exec } from 'child_process' +import * as os from 'os' +import * as vscode from 'vscode' + +import type { Config } from './config' +import type { PersistentState } from './persistent_state' +import { isValidExecutable, log } from './util' export async function bootstrap( context: vscode.ExtensionContext, diff --git a/rust/kcl-language-server/client/src/client.ts b/rust/kcl-language-server/client/src/client.ts index 968fe7d65..772103c34 100644 --- a/rust/kcl-language-server/client/src/client.ts +++ b/rust/kcl-language-server/client/src/client.ts @@ -1,6 +1,6 @@ /* eslint suggest-no-throw/suggest-no-throw: 0 */ -import * as lc from 'vscode-languageclient/node' import type * as vscode from 'vscode' +import * as lc from 'vscode-languageclient/node' export async function createClient( traceOutputChannel: vscode.OutputChannel, diff --git a/rust/kcl-language-server/client/src/commands.ts b/rust/kcl-language-server/client/src/commands.ts index b51c8ec53..31b629399 100644 --- a/rust/kcl-language-server/client/src/commands.ts +++ b/rust/kcl-language-server/client/src/commands.ts @@ -1,8 +1,8 @@ /* eslint suggest-no-throw/suggest-no-throw: 0 */ +import { spawnSync } from 'child_process' import * as vscode from 'vscode' import type { Cmd, CtxInit } from './ctx' -import { spawnSync } from 'child_process' export function serverVersion(ctx: CtxInit): Cmd { return async () => { diff --git a/rust/kcl-language-server/client/src/config.ts b/rust/kcl-language-server/client/src/config.ts index 49ad5addd..7a96d42e7 100644 --- a/rust/kcl-language-server/client/src/config.ts +++ b/rust/kcl-language-server/client/src/config.ts @@ -1,10 +1,11 @@ /* eslint suggest-no-throw/suggest-no-throw: 0 */ -import * as Is from 'vscode-languageclient/lib/common/utils/is' import * as os from 'os' import * as path from 'path' import * as vscode from 'vscode' -import { log, type Env } from './util' +import * as Is from 'vscode-languageclient/lib/common/utils/is' + import { expectNotUndefined, unwrapUndefinable } from './undefinable' +import { type Env, log } from './util' export type RunnableEnvCfgItem = { mask?: string diff --git a/rust/kcl-language-server/client/src/ctx.ts b/rust/kcl-language-server/client/src/ctx.ts index 7ebe3f7af..ffe4100a5 100644 --- a/rust/kcl-language-server/client/src/ctx.ts +++ b/rust/kcl-language-server/client/src/ctx.ts @@ -1,20 +1,20 @@ /* eslint suggest-no-throw/suggest-no-throw: 0 */ import * as vscode from 'vscode' import type * as lc from 'vscode-languageclient/node' +import { TransportKind } from 'vscode-languageclient/node' -import { Config, prepareVSCodeConfig } from './config' +import { bootstrap } from './bootstrap' import { createClient } from './client' -import { - isKclDocument, - isKclEditor, - LazyOutputChannel, - log, - type KclEditor, -} from './util' +import { Config, prepareVSCodeConfig } from './config' import type { ServerStatusParams } from './lsp_ext' import { PersistentState } from './persistent_state' -import { bootstrap } from './bootstrap' -import { TransportKind } from 'vscode-languageclient/node' +import { + type KclEditor, + LazyOutputChannel, + isKclDocument, + isKclEditor, + log, +} from './util' // We only support local folders, not eg. Live Share (`vlsl:` scheme), so don't activate if // only those are in use. We use "Empty" to represent these scenarios diff --git a/rust/kcl-language-server/client/src/persistent_state.ts b/rust/kcl-language-server/client/src/persistent_state.ts index ae9e60204..14c02e1d1 100644 --- a/rust/kcl-language-server/client/src/persistent_state.ts +++ b/rust/kcl-language-server/client/src/persistent_state.ts @@ -1,5 +1,6 @@ /* eslint suggest-no-throw/suggest-no-throw: 0 */ import type * as vscode from 'vscode' + import { log } from './util' export class PersistentState { diff --git a/rust/kcl-language-server/client/src/test/runTest.ts b/rust/kcl-language-server/client/src/test/runTest.ts index 542d5903e..7467c59a4 100644 --- a/rust/kcl-language-server/client/src/test/runTest.ts +++ b/rust/kcl-language-server/client/src/test/runTest.ts @@ -1,6 +1,5 @@ -import * as path from 'path' - import { runTests } from '@vscode/test-electron' +import * as path from 'path' async function main() { try { diff --git a/rust/kcl-language-server/client/src/test/suite/extension.test.ts b/rust/kcl-language-server/client/src/test/suite/extension.test.ts index d28ece25b..42b002018 100644 --- a/rust/kcl-language-server/client/src/test/suite/extension.test.ts +++ b/rust/kcl-language-server/client/src/test/suite/extension.test.ts @@ -1,8 +1,8 @@ import * as assert from 'assert' - // You can import and use all API from the 'vscode' module // as well as import your extension to test it import * as vscode from 'vscode' + // import * as myExtension from '../../extension'; suite('Extension Test Suite', () => { diff --git a/rust/kcl-language-server/client/src/test/suite/index.ts b/rust/kcl-language-server/client/src/test/suite/index.ts index d6681067c..c1c17b03b 100644 --- a/rust/kcl-language-server/client/src/test/suite/index.ts +++ b/rust/kcl-language-server/client/src/test/suite/index.ts @@ -1,4 +1,5 @@ import * as path from 'path' + const Mocha = require('mocha') const { glob } = require('glob') diff --git a/rust/kcl-language-server/client/src/util.ts b/rust/kcl-language-server/client/src/util.ts index 04ad5a1fd..b8e5fa89b 100644 --- a/rust/kcl-language-server/client/src/util.ts +++ b/rust/kcl-language-server/client/src/util.ts @@ -1,8 +1,8 @@ /* eslint suggest-no-throw/suggest-no-throw: 0 */ -import * as vscode from 'vscode' import { strict as nativeAssert } from 'assert' -import { exec, type ExecOptions, spawnSync } from 'child_process' +import { type ExecOptions, exec, spawnSync } from 'child_process' import { inspect } from 'util' +import * as vscode from 'vscode' export interface Env { [name: string]: string diff --git a/rust/kcl-lib/src/execution/exec_ast.rs b/rust/kcl-lib/src/execution/exec_ast.rs index 2ed45f842..e3295b958 100644 --- a/rust/kcl-lib/src/execution/exec_ast.rs +++ b/rust/kcl-lib/src/execution/exec_ast.rs @@ -1227,7 +1227,7 @@ impl Node { )); } - let op = if func.feature_tree_operation() { + let op = if func.feature_tree_operation() && !ctx.is_isolated_execution().await { let op_labeled_args = args .kw_args .labeled @@ -1297,24 +1297,26 @@ impl Node { // exec_state. let func = fn_name.get_result(exec_state, ctx).await?.clone(); - // Track call operation. - let op_labeled_args = args - .kw_args - .labeled - .iter() - .map(|(k, arg)| (k.clone(), OpArg::new(OpKclValue::from(&arg.value), arg.source_range))) - .collect(); - exec_state.global.operations.push(Operation::UserDefinedFunctionCall { - name: Some(fn_name.to_string()), - function_source_range: func.function_def_source_range().unwrap_or_default(), - unlabeled_arg: args + if !ctx.is_isolated_execution().await { + // Track call operation. + let op_labeled_args = args .kw_args - .unlabeled - .as_ref() - .map(|arg| OpArg::new(OpKclValue::from(&arg.value), arg.source_range)), - labeled_args: op_labeled_args, - source_range: callsite, - }); + .labeled + .iter() + .map(|(k, arg)| (k.clone(), OpArg::new(OpKclValue::from(&arg.value), arg.source_range))) + .collect(); + exec_state.global.operations.push(Operation::UserDefinedFunctionCall { + name: Some(fn_name.to_string()), + function_source_range: func.function_def_source_range().unwrap_or_default(), + unlabeled_arg: args + .kw_args + .unlabeled + .as_ref() + .map(|arg| OpArg::new(OpKclValue::from(&arg.value), arg.source_range)), + labeled_args: op_labeled_args, + source_range: callsite, + }); + } let Some(fn_src) = func.as_fn() else { return Err(KclError::Semantic(KclErrorDetails { @@ -1341,8 +1343,10 @@ impl Node { }) })?; - // Track return operation. - exec_state.global.operations.push(Operation::UserDefinedFunctionReturn); + if !ctx.is_isolated_execution().await { + // Track return operation. + exec_state.global.operations.push(Operation::UserDefinedFunctionReturn); + } Ok(result) } @@ -1379,7 +1383,7 @@ impl Node { )); } - let op = if func.feature_tree_operation() { + let op = if func.feature_tree_operation() && !ctx.is_isolated_execution().await { let op_labeled_args = func .args(false) .iter() @@ -1437,15 +1441,17 @@ impl Node { // exec_state. let func = fn_name.get_result(exec_state, ctx).await?.clone(); - // Track call operation. - exec_state.global.operations.push(Operation::UserDefinedFunctionCall { - name: Some(fn_name.to_string()), - function_source_range: func.function_def_source_range().unwrap_or_default(), - unlabeled_arg: None, - // TODO: Add the arguments for legacy positional parameters. - labeled_args: Default::default(), - source_range: callsite, - }); + if !ctx.is_isolated_execution().await { + // Track call operation. + exec_state.global.operations.push(Operation::UserDefinedFunctionCall { + name: Some(fn_name.to_string()), + function_source_range: func.function_def_source_range().unwrap_or_default(), + unlabeled_arg: None, + // TODO: Add the arguments for legacy positional parameters. + labeled_args: Default::default(), + source_range: callsite, + }); + } let Some(fn_src) = func.as_fn() else { return Err(KclError::Semantic(KclErrorDetails { @@ -1471,8 +1477,10 @@ impl Node { }) })?; - // Track return operation. - exec_state.global.operations.push(Operation::UserDefinedFunctionReturn); + if !ctx.is_isolated_execution().await { + // Track return operation. + exec_state.global.operations.push(Operation::UserDefinedFunctionReturn); + } Ok(result) } diff --git a/rust/kcl-lib/src/execution/mod.rs b/rust/kcl-lib/src/execution/mod.rs index c6c7a355c..7c0d53557 100644 --- a/rust/kcl-lib/src/execution/mod.rs +++ b/rust/kcl-lib/src/execution/mod.rs @@ -498,9 +498,13 @@ impl ExecutorContext { self.context_type == ContextType::Mock || self.context_type == ContextType::MockCustomForwarded } + pub async fn is_isolated_execution(&self) -> bool { + self.engine.execution_kind().await.is_isolated() + } + /// Returns true if we should not send engine commands for any reason. pub async fn no_engine_commands(&self) -> bool { - self.is_mock() || self.engine.execution_kind().await.is_isolated() + self.is_mock() || self.is_isolated_execution().await } pub async fn send_clear_scene( diff --git a/rust/kcl-lib/src/settings/types/mod.rs b/rust/kcl-lib/src/settings/types/mod.rs index 7da6579b1..4077cccc8 100644 --- a/rust/kcl-lib/src/settings/types/mod.rs +++ b/rust/kcl-lib/src/settings/types/mod.rs @@ -113,15 +113,19 @@ pub struct AppSettings { pub onboarding_status: OnboardingStatus, /// Backwards compatible project directory setting. #[serde(default, alias = "projectDirectory", skip_serializing_if = "Option::is_none")] + #[ts(skip)] pub project_directory: Option, /// Backwards compatible theme setting. #[serde(default, skip_serializing_if = "Option::is_none")] + #[ts(skip)] pub theme: Option, /// The hue of the primary theme color for the app. #[serde(default, skip_serializing_if = "Option::is_none", alias = "themeColor")] + #[ts(skip)] pub theme_color: Option, /// Whether or not Screen Space Ambient Occlusion (SSAO) is enabled. #[serde(default, alias = "enableSSAO", skip_serializing_if = "Option::is_none")] + #[ts(skip)] pub enable_ssao: Option, /// Permanently dismiss the banner warning to download the desktop app. /// This setting only applies to the web app. And is temporary until we have Linux support. @@ -285,6 +289,7 @@ pub struct ModelingSettings { /// of the app to aid in development. /// Remove this when we remove backwards compatibility with the old settings file. #[serde(default, alias = "showDebugPanel", skip_serializing_if = "is_default")] + #[ts(skip)] pub show_debug_panel: bool, /// Whether or not Screen Space Ambient Occlusion (SSAO) is enabled. #[serde(default, skip_serializing_if = "is_default")] diff --git a/rust/kcl-lib/src/settings/types/project.rs b/rust/kcl-lib/src/settings/types/project.rs index d8293114f..954c243a8 100644 --- a/rust/kcl-lib/src/settings/types/project.rs +++ b/rust/kcl-lib/src/settings/types/project.rs @@ -94,9 +94,11 @@ pub struct ProjectAppSettings { pub onboarding_status: OnboardingStatus, /// The hue of the primary theme color for the app. #[serde(default, skip_serializing_if = "Option::is_none", alias = "themeColor")] + #[ts(skip)] pub theme_color: Option, /// Whether or not Screen Space Ambient Occlusion (SSAO) is enabled. #[serde(default, alias = "enableSSAO", skip_serializing_if = "Option::is_none")] + #[ts(skip)] pub enable_ssao: Option, /// Permanently dismiss the banner warning to download the desktop app. /// This setting only applies to the web app. And is temporary until we have Linux support. @@ -143,6 +145,7 @@ pub struct ProjectModelingSettings { /// of the app to aid in development. /// Remove this when we remove backwards compatibility with the old settings file. #[serde(default, alias = "showDebugPanel", skip_serializing_if = "is_default")] + #[ts(skip)] pub show_debug_panel: bool, /// Whether or not Screen Space Ambient Occlusion (SSAO) is enabled. #[serde(default, skip_serializing_if = "is_default")] diff --git a/rust/kcl-lib/src/std/transform.rs b/rust/kcl-lib/src/std/transform.rs index f4bccbe5f..b7f75244f 100644 --- a/rust/kcl-lib/src/std/transform.rs +++ b/rust/kcl-lib/src/std/transform.rs @@ -28,11 +28,19 @@ pub async fn scale(exec_state: &mut ExecState, args: Args) -> Result Result hole(pipeHole, %) /// |> sweep(path = sweepPath) /// |> scale( -/// x = 1.0, -/// y = 1.0, /// z = 2.5, /// ) /// ``` @@ -98,8 +104,6 @@ pub async fn scale(exec_state: &mut ExecState, args: Args) -> Result scale( -/// x = 1.0, -/// y = 1.0, /// z = 2.5, /// ) /// ``` @@ -135,7 +139,7 @@ pub async fn scale(exec_state: &mut ExecState, args: Args) -> Result Result, + y: Option, + z: Option, global: Option, exec_state: &mut ExecState, args: Args, @@ -174,7 +178,11 @@ async fn inner_scale( object_id, transforms: vec![shared::ComponentTransform { scale: Some(shared::TransformBy::> { - property: Point3d { x, y, z }, + property: Point3d { + x: x.unwrap_or(1.0), + y: y.unwrap_or(1.0), + z: z.unwrap_or(1.0), + }, set: false, is_local: !global.unwrap_or(false), }), @@ -201,11 +209,19 @@ pub async fn translate(exec_state: &mut ExecState, args: Args) -> Result Result translate( /// x = 5, /// y = 5, -/// z = 0, /// ) /// |> extrude( /// length = 10, @@ -349,7 +364,7 @@ pub async fn translate(exec_state: &mut ExecState, args: Args) -> Result translate(x = 0, y = 0, z = 20) +/// |> translate(z = 20) /// |> rotate(axis = [0, 0, 1.0], angle = 45) /// /// loft([profile001, profile002]) @@ -361,17 +376,17 @@ pub async fn translate(exec_state: &mut ExecState, args: Args) -> Result, + y: Option, + z: Option, global: Option, exec_state: &mut ExecState, args: Args, @@ -392,9 +407,9 @@ async fn inner_translate( transforms: vec![shared::ComponentTransform { translate: Some(shared::TransformBy::> { property: shared::Point3d { - x: LengthUnit(x), - y: LengthUnit(y), - z: LengthUnit(z), + x: LengthUnit(x.unwrap_or_default()), + y: LengthUnit(y.unwrap_or_default()), + z: LengthUnit(z.unwrap_or_default()), }, set: false, is_local: !global.unwrap_or(false), @@ -1001,4 +1016,34 @@ sweepSketch = startSketchOn('XY') .to_string() ); } + + #[tokio::test(flavor = "multi_thread")] + async fn test_translate_no_args() { + let ast = PIPE.to_string() + + r#" + |> translate( + ) +"#; + let result = parse_execute(&ast).await; + assert!(result.is_err()); + assert_eq!( + result.unwrap_err().message(), + r#"Expected `x`, `y`, or `z` to be provided."#.to_string() + ); + } + + #[tokio::test(flavor = "multi_thread")] + async fn test_scale_no_args() { + let ast = PIPE.to_string() + + r#" + |> scale( + ) +"#; + let result = parse_execute(&ast).await; + assert!(result.is_err()); + assert_eq!( + result.unwrap_err().message(), + r#"Expected `x`, `y`, or `z` to be provided."#.to_string() + ); + } } diff --git a/rust/kcl-lib/tests/import_function_not_sketch/ops.snap b/rust/kcl-lib/tests/import_function_not_sketch/ops.snap index ce0263167..45ff1fa72 100644 --- a/rust/kcl-lib/tests/import_function_not_sketch/ops.snap +++ b/rust/kcl-lib/tests/import_function_not_sketch/ops.snap @@ -2,43 +2,4 @@ source: kcl-lib/src/simulation_tests.rs description: Operations executed import_function_not_sketch.kcl --- -[ - { - "labeledArgs": { - "data": { - "value": { - "type": "String", - "value": "XY" - }, - "sourceRange": [] - } - }, - "name": "startSketchOn", - "sourceRange": [], - "type": "StdLibCall", - "unlabeledArg": null - }, - { - "labeledArgs": { - "axis": { - "value": { - "type": "String", - "value": "y" - }, - "sourceRange": [] - } - }, - "name": "revolve", - "sourceRange": [], - "type": "StdLibCall", - "unlabeledArg": { - "value": { - "type": "Sketch", - "value": { - "artifactId": "[uuid]" - } - }, - "sourceRange": [] - } - } -] +[] diff --git a/rust/kcl-lib/tests/import_side_effect/ops.snap b/rust/kcl-lib/tests/import_side_effect/ops.snap index 6bde63843..5be330e7e 100644 --- a/rust/kcl-lib/tests/import_side_effect/ops.snap +++ b/rust/kcl-lib/tests/import_side_effect/ops.snap @@ -2,86 +2,4 @@ source: kcl-lib/src/simulation_tests.rs description: Operations executed import_side_effect.kcl --- -[ - { - "labeledArgs": { - "data": { - "value": { - "type": "String", - "value": "XY" - }, - "sourceRange": [] - } - }, - "name": "startSketchOn", - "sourceRange": [], - "type": "StdLibCall", - "unlabeledArg": null - }, - { - "type": "UserDefinedFunctionCall", - "name": "circle", - "functionSourceRange": [ - 0, - 0, - 0 - ], - "unlabeledArg": null, - "labeledArgs": { - "center": { - "value": { - "type": "Array", - "value": [ - { - "type": "Number", - "value": 0.0, - "ty": { - "type": "Default", - "len": { - "type": "Mm" - }, - "angle": { - "type": "Degrees" - } - } - }, - { - "type": "Number", - "value": 0.0, - "ty": { - "type": "Default", - "len": { - "type": "Mm" - }, - "angle": { - "type": "Degrees" - } - } - } - ] - }, - "sourceRange": [] - }, - "radius": { - "value": { - "type": "Number", - "value": 10.0, - "ty": { - "type": "Default", - "len": { - "type": "Mm" - }, - "angle": { - "type": "Degrees" - } - } - }, - "sourceRange": [] - } - }, - "sourceRange": [] - }, - { - "type": "UserDefinedFunctionReturn" - } -] +[] diff --git a/rust/kcl-lib/tests/kcl_samples/multi-axis-robot/ops.snap b/rust/kcl-lib/tests/kcl_samples/multi-axis-robot/ops.snap index 04c8dfa4e..032fda565 100644 --- a/rust/kcl-lib/tests/kcl_samples/multi-axis-robot/ops.snap +++ b/rust/kcl-lib/tests/kcl_samples/multi-axis-robot/ops.snap @@ -3,66 +3,6 @@ source: kcl-lib/src/simulation_tests.rs description: Operations executed multi-axis-robot.kcl --- [ - { - "type": "UserDefinedFunctionCall", - "name": "sin", - "functionSourceRange": [ - 0, - 0, - 0 - ], - "unlabeledArg": null, - "labeledArgs": {}, - "sourceRange": [] - }, - { - "type": "UserDefinedFunctionReturn" - }, - { - "type": "UserDefinedFunctionCall", - "name": "cos", - "functionSourceRange": [ - 0, - 0, - 0 - ], - "unlabeledArg": null, - "labeledArgs": {}, - "sourceRange": [] - }, - { - "type": "UserDefinedFunctionReturn" - }, - { - "type": "UserDefinedFunctionCall", - "name": "sin", - "functionSourceRange": [ - 0, - 0, - 0 - ], - "unlabeledArg": null, - "labeledArgs": {}, - "sourceRange": [] - }, - { - "type": "UserDefinedFunctionReturn" - }, - { - "type": "UserDefinedFunctionCall", - "name": "cos", - "functionSourceRange": [ - 0, - 0, - 0 - ], - "unlabeledArg": null, - "labeledArgs": {}, - "sourceRange": [] - }, - { - "type": "UserDefinedFunctionReturn" - }, { "labeledArgs": { "data": { diff --git a/rust/kcl-lib/tests/kcl_samples/pipe-flange-assembly/ops.snap b/rust/kcl-lib/tests/kcl_samples/pipe-flange-assembly/ops.snap index e17bbd3b9..e31de7504 100644 --- a/rust/kcl-lib/tests/kcl_samples/pipe-flange-assembly/ops.snap +++ b/rust/kcl-lib/tests/kcl_samples/pipe-flange-assembly/ops.snap @@ -3,36 +3,6 @@ source: kcl-lib/src/simulation_tests.rs description: Operations executed pipe-flange-assembly.kcl --- [ - { - "type": "UserDefinedFunctionCall", - "name": "cos", - "functionSourceRange": [ - 0, - 0, - 0 - ], - "unlabeledArg": null, - "labeledArgs": {}, - "sourceRange": [] - }, - { - "type": "UserDefinedFunctionReturn" - }, - { - "type": "UserDefinedFunctionCall", - "name": "cos", - "functionSourceRange": [ - 0, - 0, - 0 - ], - "unlabeledArg": null, - "labeledArgs": {}, - "sourceRange": [] - }, - { - "type": "UserDefinedFunctionReturn" - }, { "type": "UserDefinedFunctionCall", "name": "flange", diff --git a/rust/kcl-lib/tests/kcl_samples/walkie-talkie/ops.snap b/rust/kcl-lib/tests/kcl_samples/walkie-talkie/ops.snap index 959b9886b..466c960c4 100644 --- a/rust/kcl-lib/tests/kcl_samples/walkie-talkie/ops.snap +++ b/rust/kcl-lib/tests/kcl_samples/walkie-talkie/ops.snap @@ -349,352 +349,6 @@ description: Operations executed walkie-talkie.kcl { "type": "UserDefinedFunctionReturn" }, - { - "type": "UserDefinedFunctionCall", - "name": "body", - "functionSourceRange": [ - 359, - 2786, - 6 - ], - "unlabeledArg": null, - "labeledArgs": {}, - "sourceRange": [] - }, - { - "labeledArgs": { - "data": { - "value": { - "type": "Plane", - "artifact_id": "[uuid]" - }, - "sourceRange": [] - } - }, - "name": "startSketchOn", - "sourceRange": [], - "type": "StdLibCall", - "unlabeledArg": null - }, - { - "labeledArgs": { - "length": { - "value": { - "type": "Number", - "value": 1.0, - "ty": { - "type": "Default", - "len": { - "type": "Inches" - }, - "angle": { - "type": "Degrees" - } - } - }, - "sourceRange": [] - } - }, - "name": "extrude", - "sourceRange": [], - "type": "StdLibCall", - "unlabeledArg": { - "value": { - "type": "Sketch", - "value": { - "artifactId": "[uuid]" - } - }, - "sourceRange": [] - } - }, - { - "labeledArgs": { - "length": { - "value": { - "type": "Number", - "value": 0.325, - "ty": { - "type": "Default", - "len": { - "type": "Inches" - }, - "angle": { - "type": "Degrees" - } - } - }, - "sourceRange": [] - }, - "tags": { - "value": { - "type": "Array", - "value": [ - { - "type": "Uuid", - "value": "[uuid]" - }, - { - "type": "Uuid", - "value": "[uuid]" - }, - { - "type": "Uuid", - "value": "[uuid]" - }, - { - "type": "Uuid", - "value": "[uuid]" - } - ] - }, - "sourceRange": [] - } - }, - "name": "chamfer", - "sourceRange": [], - "type": "StdLibCall", - "unlabeledArg": { - "value": { - "type": "Solid", - "value": { - "artifactId": "[uuid]" - } - }, - "sourceRange": [] - } - }, - { - "labeledArgs": { - "data": { - "value": { - "type": "Solid", - "value": { - "artifactId": "[uuid]" - } - }, - "sourceRange": [] - }, - "tag": { - "value": { - "type": "String", - "value": "END" - }, - "sourceRange": [] - } - }, - "name": "startSketchOn", - "sourceRange": [], - "type": "StdLibCall", - "unlabeledArg": null - }, - { - "type": "UserDefinedFunctionCall", - "name": "cos", - "functionSourceRange": [ - 0, - 0, - 0 - ], - "unlabeledArg": null, - "labeledArgs": {}, - "sourceRange": [] - }, - { - "type": "UserDefinedFunctionReturn" - }, - { - "type": "UserDefinedFunctionCall", - "name": "cos", - "functionSourceRange": [ - 0, - 0, - 0 - ], - "unlabeledArg": null, - "labeledArgs": {}, - "sourceRange": [] - }, - { - "type": "UserDefinedFunctionReturn" - }, - { - "type": "UserDefinedFunctionCall", - "name": "cos", - "functionSourceRange": [ - 0, - 0, - 0 - ], - "unlabeledArg": null, - "labeledArgs": {}, - "sourceRange": [] - }, - { - "type": "UserDefinedFunctionReturn" - }, - { - "type": "UserDefinedFunctionCall", - "name": "cos", - "functionSourceRange": [ - 0, - 0, - 0 - ], - "unlabeledArg": null, - "labeledArgs": {}, - "sourceRange": [] - }, - { - "type": "UserDefinedFunctionReturn" - }, - { - "labeledArgs": { - "length": { - "value": { - "type": "Number", - "value": -0.0625, - "ty": { - "type": "Default", - "len": { - "type": "Inches" - }, - "angle": { - "type": "Degrees" - } - } - }, - "sourceRange": [] - } - }, - "name": "extrude", - "sourceRange": [], - "type": "StdLibCall", - "unlabeledArg": { - "value": { - "type": "Sketch", - "value": { - "artifactId": "[uuid]" - } - }, - "sourceRange": [] - } - }, - { - "labeledArgs": { - "data": { - "value": { - "type": "Solid", - "value": { - "artifactId": "[uuid]" - } - }, - "sourceRange": [] - }, - "tag": { - "value": { - "type": "String", - "value": "start" - }, - "sourceRange": [] - } - }, - "name": "startSketchOn", - "sourceRange": [], - "type": "StdLibCall", - "unlabeledArg": null - }, - { - "labeledArgs": { - "length": { - "value": { - "type": "Number", - "value": -0.0625, - "ty": { - "type": "Default", - "len": { - "type": "Inches" - }, - "angle": { - "type": "Degrees" - } - } - }, - "sourceRange": [] - } - }, - "name": "extrude", - "sourceRange": [], - "type": "StdLibCall", - "unlabeledArg": { - "value": { - "type": "Sketch", - "value": { - "artifactId": "[uuid]" - } - }, - "sourceRange": [] - } - }, - { - "labeledArgs": { - "data": { - "value": { - "type": "Solid", - "value": { - "artifactId": "[uuid]" - } - }, - "sourceRange": [] - }, - "tag": { - "value": { - "type": "String", - "value": "start" - }, - "sourceRange": [] - } - }, - "name": "startSketchOn", - "sourceRange": [], - "type": "StdLibCall", - "unlabeledArg": null - }, - { - "labeledArgs": { - "length": { - "value": { - "type": "Number", - "value": -0.5, - "ty": { - "type": "Default", - "len": { - "type": "Inches" - }, - "angle": { - "type": "Degrees" - } - } - }, - "sourceRange": [] - } - }, - "name": "extrude", - "sourceRange": [], - "type": "StdLibCall", - "unlabeledArg": { - "value": { - "type": "Sketch", - "value": { - "artifactId": "[uuid]" - } - }, - "sourceRange": [] - } - }, - { - "type": "UserDefinedFunctionReturn" - }, { "type": "UserDefinedFunctionCall", "name": "antenna", diff --git a/rust/kcl-lib/tests/pattern_circular_in_module/ops.snap b/rust/kcl-lib/tests/pattern_circular_in_module/ops.snap index 38aa7bb3b..d0f99c008 100644 --- a/rust/kcl-lib/tests/pattern_circular_in_module/ops.snap +++ b/rust/kcl-lib/tests/pattern_circular_in_module/ops.snap @@ -3,91 +3,6 @@ source: kcl-lib/src/simulation_tests.rs description: Operations executed pattern_circular_in_module.kcl --- [ - { - "type": "UserDefinedFunctionCall", - "name": "thing", - "functionSourceRange": [ - 15, - 378, - 5 - ], - "unlabeledArg": null, - "labeledArgs": {}, - "sourceRange": [] - }, - { - "labeledArgs": { - "data": { - "value": { - "type": "Plane", - "artifact_id": "[uuid]" - }, - "sourceRange": [] - } - }, - "name": "startSketchOn", - "sourceRange": [], - "type": "StdLibCall", - "unlabeledArg": null - }, - { - "labeledArgs": { - "length": { - "value": { - "type": "Number", - "value": 1.0, - "ty": { - "type": "Default", - "len": { - "type": "Mm" - }, - "angle": { - "type": "Degrees" - } - } - }, - "sourceRange": [] - } - }, - "name": "extrude", - "sourceRange": [], - "type": "StdLibCall", - "unlabeledArg": { - "value": { - "type": "Array", - "value": [ - { - "type": "Sketch", - "value": { - "artifactId": "[uuid]" - } - }, - { - "type": "Sketch", - "value": { - "artifactId": "[uuid]" - } - }, - { - "type": "Sketch", - "value": { - "artifactId": "[uuid]" - } - }, - { - "type": "Sketch", - "value": { - "artifactId": "[uuid]" - } - } - ] - }, - "sourceRange": [] - } - }, - { - "type": "UserDefinedFunctionReturn" - }, { "type": "UserDefinedFunctionCall", "name": "thing", diff --git a/rust/kcl-lib/tests/pattern_linear_in_module/ops.snap b/rust/kcl-lib/tests/pattern_linear_in_module/ops.snap index 589583cb2..85e2f2a01 100644 --- a/rust/kcl-lib/tests/pattern_linear_in_module/ops.snap +++ b/rust/kcl-lib/tests/pattern_linear_in_module/ops.snap @@ -169,175 +169,6 @@ description: Operations executed pattern_linear_in_module.kcl "sourceRange": [] } }, - { - "type": "UserDefinedFunctionReturn" - }, - { - "type": "UserDefinedFunctionCall", - "name": "thing", - "functionSourceRange": [ - 15, - 221, - 5 - ], - "unlabeledArg": null, - "labeledArgs": {}, - "sourceRange": [] - }, - { - "labeledArgs": { - "data": { - "value": { - "type": "Plane", - "artifact_id": "[uuid]" - }, - "sourceRange": [] - } - }, - "name": "startSketchOn", - "sourceRange": [], - "type": "StdLibCall", - "unlabeledArg": null - }, - { - "type": "UserDefinedFunctionCall", - "name": "circle", - "functionSourceRange": [ - 0, - 0, - 0 - ], - "unlabeledArg": null, - "labeledArgs": { - "center": { - "value": { - "type": "Array", - "value": [ - { - "type": "Number", - "value": 0.0, - "ty": { - "type": "Default", - "len": { - "type": "Mm" - }, - "angle": { - "type": "Degrees" - } - } - }, - { - "type": "Number", - "value": 0.0, - "ty": { - "type": "Default", - "len": { - "type": "Mm" - }, - "angle": { - "type": "Degrees" - } - } - } - ] - }, - "sourceRange": [] - }, - "radius": { - "value": { - "type": "Number", - "value": 1.0, - "ty": { - "type": "Default", - "len": { - "type": "Mm" - }, - "angle": { - "type": "Degrees" - } - } - }, - "sourceRange": [] - } - }, - "sourceRange": [] - }, - { - "type": "UserDefinedFunctionReturn" - }, - { - "labeledArgs": { - "length": { - "value": { - "type": "Number", - "value": 1.0, - "ty": { - "type": "Default", - "len": { - "type": "Mm" - }, - "angle": { - "type": "Degrees" - } - } - }, - "sourceRange": [] - } - }, - "name": "extrude", - "sourceRange": [], - "type": "StdLibCall", - "unlabeledArg": { - "value": { - "type": "Array", - "value": [ - { - "type": "Sketch", - "value": { - "artifactId": "[uuid]" - } - }, - { - "type": "Sketch", - "value": { - "artifactId": "[uuid]" - } - }, - { - "type": "Sketch", - "value": { - "artifactId": "[uuid]" - } - }, - { - "type": "Sketch", - "value": { - "artifactId": "[uuid]" - } - }, - { - "type": "Sketch", - "value": { - "artifactId": "[uuid]" - } - }, - { - "type": "Sketch", - "value": { - "artifactId": "[uuid]" - } - }, - { - "type": "Sketch", - "value": { - "artifactId": "[uuid]" - } - } - ] - }, - "sourceRange": [] - } - }, { "type": "UserDefinedFunctionReturn" } diff --git a/rust/kcl-python-bindings/Cargo.toml b/rust/kcl-python-bindings/Cargo.toml index 7ffa5bcbd..1018963a6 100644 --- a/rust/kcl-python-bindings/Cargo.toml +++ b/rust/kcl-python-bindings/Cargo.toml @@ -3,6 +3,7 @@ name = "kcl-python-bindings" version = "0.3.57" edition = "2021" repository = "https://github.com/kittycad/modeling-app" +exclude = ["tests/*", "files/*", "venv/*"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] diff --git a/rust/kcl-python-bindings/pyproject.toml b/rust/kcl-python-bindings/pyproject.toml index a206d035f..b9f9ecdb9 100644 --- a/rust/kcl-python-bindings/pyproject.toml +++ b/rust/kcl-python-bindings/pyproject.toml @@ -20,3 +20,10 @@ test = [ [tool.maturin] features = ["pyo3/extension-module"] + +[tool.setuptools] +include-package-data = false + +[tool.setuptools.packages.find] +include = ["src*"] +exclude = ["files*", "tests*", "venv*", "target*"] diff --git a/rust/kcl-wasm-lib/src/wasm.rs b/rust/kcl-wasm-lib/src/wasm.rs index e9171e4fa..eb15df6e2 100644 --- a/rust/kcl-wasm-lib/src/wasm.rs +++ b/rust/kcl-wasm-lib/src/wasm.rs @@ -176,6 +176,8 @@ pub fn serialize_configuration(val: JsValue) -> Result { let config: kcl_lib::Configuration = val.into_serde().map_err(|e| e.to_string())?; let toml_str = toml::to_string_pretty(&config).map_err(|e| e.to_string())?; + let settings = kcl_lib::Configuration::backwards_compatible_toml_parse(&toml_str).map_err(|e| e.to_string())?; + let toml_str = toml::to_string_pretty(&settings).map_err(|e| e.to_string())?; // The serde-wasm-bindgen does not work here because of weird HashMap issues so we use the // gloo-serialize crate instead. @@ -190,6 +192,9 @@ pub fn serialize_project_configuration(val: JsValue) -> Result let config: kcl_lib::ProjectConfiguration = val.into_serde().map_err(|e| e.to_string())?; let toml_str = toml::to_string_pretty(&config).map_err(|e| e.to_string())?; + let settings = + kcl_lib::ProjectConfiguration::backwards_compatible_toml_parse(&toml_str).map_err(|e| e.to_string())?; + let toml_str = toml::to_string_pretty(&settings).map_err(|e| e.to_string())?; // The serde-wasm-bindgen does not work here because of weird HashMap issues so we use the // gloo-serialize crate instead. diff --git a/scripts/semantic-release.sh b/scripts/semantic-release.sh index 085989ef9..36859b001 100755 --- a/scripts/semantic-release.sh +++ b/scripts/semantic-release.sh @@ -1,9 +1,9 @@ -# Requires access to an environment variable GH_TOKEN -# If you are in the path of the git repository the gh release list will automatically point to that git repo -# aka cd /some/path/modeling-app -# $ gh release list -# Get the latest semver tag from git -latest_tag=$(gh release list --json name,isLatest --jq '.[] | select(.isLatest)|.name') +#!/bin/bash +set -euo pipefail + +# Fetch the latest release tag +git fetch --all --tags +latest_tag=$(git tag --sort=-v:refname | grep "^v[0-9]" | head -n 1) # Function to bump version numbers bump_version() { diff --git a/src/App.tsx b/src/App.tsx index d6a63dd45..4786ed4c5 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,36 +1,43 @@ import { useEffect, useMemo, useRef } from 'react' -import { useHotKeyListener } from './hooks/useHotKeyListener' -import { Stream } from './components/Stream' -import { AppHeader } from './components/AppHeader' -import { useHotkeys } from 'react-hotkeys-hook' -import { useLoaderData, useNavigate } from 'react-router-dom' -import { type IndexLoaderData } from 'lib/types' -import { PATHS } from 'lib/paths' -import { onboardingPaths } from 'routes/Onboarding/paths' -import { useEngineConnectionSubscriptions } from 'hooks/useEngineConnectionSubscriptions' -import { codeManager, engineCommandManager } from 'lib/singletons' -import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath' -import { isDesktop } from 'lib/isDesktop' -import { useLspContext } from 'components/LspProvider' -import { ModelingSidebar } from 'components/ModelingSidebar/ModelingSidebar' -import { LowerRightControls } from 'components/LowerRightControls' -import ModalContainer from 'react-modal-promise' -import useHotkeyWrapper from 'lib/hotkeyWrapper' -import Gizmo from 'components/Gizmo' -import { CoreDumpManager } from 'lib/coredump' -import { UnitsMenu } from 'components/UnitsMenu' -import { CameraProjectionToggle } from 'components/CameraProjectionToggle' -import { useCreateFileLinkQuery } from 'hooks/useCreateFileLinkQueryWatcher' -import { maybeWriteToDisk } from 'lib/telemetry' -import { takeScreenshotOfVideoStreamCanvas } from 'lib/screenshot' -import { writeProjectThumbnailFile } from 'lib/desktop' -import { useRouteLoaderData } from 'react-router-dom' -import { useEngineCommands } from 'components/EngineCommands' -import { commandBarActor } from 'machines/commandBarMachine' -import { useToken } from 'machines/appMachine' -import { useSettings } from 'machines/appMachine' -import { rustContext } from 'lib/singletons' import toast from 'react-hot-toast' +import { useHotkeys } from 'react-hotkeys-hook' +import ModalContainer from 'react-modal-promise' +import { + useLoaderData, + useNavigate, + useRouteLoaderData, +} from 'react-router-dom' + +import { AppHeader } from '@src/components/AppHeader' +import { CameraProjectionToggle } from '@src/components/CameraProjectionToggle' +import { useEngineCommands } from '@src/components/EngineCommands' +import Gizmo from '@src/components/Gizmo' +import { LowerRightControls } from '@src/components/LowerRightControls' +import { useLspContext } from '@src/components/LspProvider' +import { ModelingSidebar } from '@src/components/ModelingSidebar/ModelingSidebar' +import { Stream } from '@src/components/Stream' +import { UnitsMenu } from '@src/components/UnitsMenu' +import { useAbsoluteFilePath } from '@src/hooks/useAbsoluteFilePath' +import { useCreateFileLinkQuery } from '@src/hooks/useCreateFileLinkQueryWatcher' +import { useEngineConnectionSubscriptions } from '@src/hooks/useEngineConnectionSubscriptions' +import { useHotKeyListener } from '@src/hooks/useHotKeyListener' +import { CoreDumpManager } from '@src/lib/coredump' +import { writeProjectThumbnailFile } from '@src/lib/desktop' +import useHotkeyWrapper from '@src/lib/hotkeyWrapper' +import { isDesktop } from '@src/lib/isDesktop' +import { PATHS } from '@src/lib/paths' +import { takeScreenshotOfVideoStreamCanvas } from '@src/lib/screenshot' +import { + codeManager, + engineCommandManager, + rustContext, +} from '@src/lib/singletons' +import { maybeWriteToDisk } from '@src/lib/telemetry' +import { type IndexLoaderData } from '@src/lib/types' +import { useSettings, useToken } from '@src/machines/appMachine' +import { commandBarActor } from '@src/machines/commandBarMachine' +import { onboardingPaths } from '@src/routes/Onboarding/paths' + maybeWriteToDisk() .then(() => {}) .catch(() => {}) diff --git a/src/AppState.tsx b/src/AppState.tsx index 7ecb30fdc..61e6d0fff 100644 --- a/src/AppState.tsx +++ b/src/AppState.tsx @@ -1,4 +1,5 @@ -import { createContext, useContext, useState, ReactNode } from 'react' +import type { ReactNode } from 'react' +import { createContext, useContext, useState } from 'react' /* diff --git a/src/Auth.tsx b/src/Auth.tsx index 7281471c2..19fa736a9 100644 --- a/src/Auth.tsx +++ b/src/Auth.tsx @@ -1,5 +1,5 @@ -import { useAuthState } from 'machines/appMachine' -import Loading from './components/Loading' +import Loading from '@src/components/Loading' +import { useAuthState } from '@src/machines/appMachine' // Wrapper around protected routes, used in src/Router.tsx export const Auth = ({ children }: React.PropsWithChildren) => { diff --git a/src/Router.tsx b/src/Router.tsx index b13e97301..16e0a720f 100644 --- a/src/Router.tsx +++ b/src/Router.tsx @@ -1,46 +1,53 @@ -import { App } from './App' +import { useMemo } from 'react' +import toast from 'react-hot-toast' import { + Outlet, + RouterProvider, createBrowserRouter, createHashRouter, - Outlet, redirect, - RouterProvider, } from 'react-router-dom' -import { ErrorPage } from './components/ErrorPage' -import { Settings } from './routes/Settings' -import { Telemetry } from './routes/Telemetry' -import Onboarding, { onboardingRoutes } from './routes/Onboarding' -import SignIn from './routes/SignIn' -import { Auth } from './Auth' -import { isDesktop } from './lib/isDesktop' -import Home from './routes/Home' -import { NetworkContext } from './hooks/useNetworkContext' -import { useNetworkStatus } from './hooks/useNetworkStatus' -import makeUrlPathRelative from './lib/makeUrlPathRelative' -import DownloadAppBanner from 'components/DownloadAppBanner' -import { WasmErrBanner } from 'components/WasmErrBanner' -import { CommandBar } from 'components/CommandBar/CommandBar' -import ModelingMachineProvider from 'components/ModelingMachineProvider' -import FileMachineProvider from 'components/FileMachineProvider' -import { MachineManagerProvider } from 'components/MachineManagerProvider' -import { PATHS } from 'lib/paths' -import { fileLoader, homeLoader, telemetryLoader } from 'lib/routeLoaders' -import LspProvider from 'components/LspProvider' -import { KclContextProvider } from 'lang/KclProvider' -import { ASK_TO_OPEN_QUERY_PARAM, BROWSER_PROJECT_NAME } from 'lib/constants' -import { CoreDumpManager } from 'lib/coredump' -import { codeManager, engineCommandManager } from 'lib/singletons' -import useHotkeyWrapper from 'lib/hotkeyWrapper' -import toast from 'react-hot-toast' -import { coreDump } from 'lang/wasm' -import { useMemo } from 'react' -import { AppStateProvider } from 'AppState' -import { reportRejection } from 'lib/trap' -import { RouteProvider } from 'components/RouteProvider' -import { ProjectsContextProvider } from 'components/ProjectsContextProvider' -import { useToken } from 'machines/appMachine' -import { OpenInDesktopAppHandler } from 'components/OpenInDesktopAppHandler' -import { rustContext } from 'lib/singletons' + +import { App } from '@src/App' +import { AppStateProvider } from '@src/AppState' +import { Auth } from '@src/Auth' +import { CommandBar } from '@src/components/CommandBar/CommandBar' +import DownloadAppBanner from '@src/components/DownloadAppBanner' +import { ErrorPage } from '@src/components/ErrorPage' +import FileMachineProvider from '@src/components/FileMachineProvider' +import LspProvider from '@src/components/LspProvider' +import { MachineManagerProvider } from '@src/components/MachineManagerProvider' +import ModelingMachineProvider from '@src/components/ModelingMachineProvider' +import { OpenInDesktopAppHandler } from '@src/components/OpenInDesktopAppHandler' +import { ProjectsContextProvider } from '@src/components/ProjectsContextProvider' +import { RouteProvider } from '@src/components/RouteProvider' +import { WasmErrBanner } from '@src/components/WasmErrBanner' +import { NetworkContext } from '@src/hooks/useNetworkContext' +import { useNetworkStatus } from '@src/hooks/useNetworkStatus' +import { KclContextProvider } from '@src/lang/KclProvider' +import { coreDump } from '@src/lang/wasm' +import { + ASK_TO_OPEN_QUERY_PARAM, + BROWSER_PROJECT_NAME, +} from '@src/lib/constants' +import { CoreDumpManager } from '@src/lib/coredump' +import useHotkeyWrapper from '@src/lib/hotkeyWrapper' +import { isDesktop } from '@src/lib/isDesktop' +import makeUrlPathRelative from '@src/lib/makeUrlPathRelative' +import { PATHS } from '@src/lib/paths' +import { fileLoader, homeLoader, telemetryLoader } from '@src/lib/routeLoaders' +import { + codeManager, + engineCommandManager, + rustContext, +} from '@src/lib/singletons' +import { reportRejection } from '@src/lib/trap' +import { useToken } from '@src/machines/appMachine' +import Home from '@src/routes/Home' +import Onboarding, { onboardingRoutes } from '@src/routes/Onboarding' +import { Settings } from '@src/routes/Settings' +import SignIn from '@src/routes/SignIn' +import { Telemetry } from '@src/routes/Telemetry' const createRouter = isDesktop() ? createHashRouter : createBrowserRouter diff --git a/src/Toolbar.tsx b/src/Toolbar.tsx index 7a0aa1348..e327a8b8b 100644 --- a/src/Toolbar.tsx +++ b/src/Toolbar.tsx @@ -1,28 +1,29 @@ -import { useRef, useMemo, memo, useCallback, useState } from 'react' -import { isCursorInSketchCommandRange } from 'lang/util' -import { editorManager, kclManager } from 'lib/singletons' -import { useModelingContext } from 'hooks/useModelingContext' -import { useNetworkContext } from 'hooks/useNetworkContext' -import { NetworkHealthState } from 'hooks/useNetworkStatus' -import { ActionButton } from 'components/ActionButton' -import { useKclContext } from 'lang/KclProvider' -import { ActionButtonDropdown } from 'components/ActionButtonDropdown' +import { memo, useCallback, useMemo, useRef, useState } from 'react' import { useHotkeys } from 'react-hotkeys-hook' -import Tooltip from 'components/Tooltip' -import { useAppState } from 'AppState' -import { CustomIcon } from 'components/CustomIcon' -import { - toolbarConfig, + +import { useAppState } from '@src/AppState' +import { ActionButton } from '@src/components/ActionButton' +import { ActionButtonDropdown } from '@src/components/ActionButtonDropdown' +import { CustomIcon } from '@src/components/CustomIcon' +import Tooltip from '@src/components/Tooltip' +import { useModelingContext } from '@src/hooks/useModelingContext' +import { useNetworkContext } from '@src/hooks/useNetworkContext' +import { NetworkHealthState } from '@src/hooks/useNetworkStatus' +import { useKclContext } from '@src/lang/KclProvider' +import { isCursorInFunctionDefinition } from '@src/lang/queryAst' +import { isCursorInSketchCommandRange } from '@src/lang/util' +import { isDesktop } from '@src/lib/isDesktop' +import { openExternalBrowserIfDesktop } from '@src/lib/openWindow' +import { editorManager, kclManager } from '@src/lib/singletons' +import type { ToolbarItem, ToolbarItemCallbackProps, ToolbarItemResolved, ToolbarModeName, -} from 'lib/toolbar' -import { isDesktop } from 'lib/isDesktop' -import { openExternalBrowserIfDesktop } from 'lib/openWindow' -import { isCursorInFunctionDefinition } from 'lang/queryAst' -import { commandBarActor } from 'machines/commandBarMachine' -import { isArray } from 'lib/utils' +} from '@src/lib/toolbar' +import { toolbarConfig } from '@src/lib/toolbar' +import { isArray } from '@src/lib/utils' +import { commandBarActor } from '@src/machines/commandBarMachine' export function Toolbar({ className = '', diff --git a/src/clientSideScene/CameraControls.ts b/src/clientSideScene/CameraControls.ts index 92ae7196d..a25d3bf99 100644 --- a/src/clientSideScene/CameraControls.ts +++ b/src/clientSideScene/CameraControls.ts @@ -1,4 +1,5 @@ -import { cameraMouseDragGuards, MouseGuard } from 'lib/cameraControls' +import type { CameraDragInteractionType_type } from '@kittycad/lib/dist/types/src/models' +import * as TWEEN from '@tweenjs/tween.js' import { Euler, MathUtils, @@ -10,26 +11,34 @@ import { Vector2, Vector3, } from 'three' + +import type { CameraProjectionType } from '@rust/kcl-lib/bindings/CameraProjectionType' + +import { isQuaternionVertical } from '@src/clientSideScene/helpers' import { DEBUG_SHOW_INTERSECTION_PLANE, INTERSECTION_PLANE_LAYER, SKETCH_LAYER, ZOOM_MAGIC_NUMBER, -} from './sceneInfra' -import { - Subscription, +} from '@src/clientSideScene/sceneInfra' +import type { EngineCommand } from '@src/lang/std/artifactGraph' +import type { EngineCommandManager, + Subscription, UnreliableSubscription, -} from 'lang/std/engineConnection' -import { EngineCommand } from 'lang/std/artifactGraph' -import { toSync, uuidv4, getNormalisedCoordinates } from 'lib/utils' -import { deg2Rad } from 'lib/utils2d' -import { isReducedMotion, roundOff, throttle } from 'lib/utils' -import * as TWEEN from '@tweenjs/tween.js' -import { isQuaternionVertical } from './helpers' -import { reportRejection } from 'lib/trap' -import { CameraProjectionType } from '@rust/kcl-lib/bindings/CameraProjectionType' -import { CameraDragInteractionType_type } from '@kittycad/lib/dist/types/src/models' +} from '@src/lang/std/engineConnection' +import type { MouseGuard } from '@src/lib/cameraControls' +import { cameraMouseDragGuards } from '@src/lib/cameraControls' +import { reportRejection } from '@src/lib/trap' +import { + getNormalisedCoordinates, + isReducedMotion, + roundOff, + throttle, + toSync, + uuidv4, +} from '@src/lib/utils' +import { deg2Rad } from '@src/lib/utils2d' const ORTHOGRAPHIC_CAMERA_SIZE = 20 const FRAMES_TO_ANIMATE_IN = 30 diff --git a/src/clientSideScene/ClientSideSceneComp.tsx b/src/clientSideScene/ClientSideSceneComp.tsx index 06c9a70e5..106734c5c 100644 --- a/src/clientSideScene/ClientSideSceneComp.tsx +++ b/src/clientSideScene/ClientSideSceneComp.tsx @@ -1,54 +1,60 @@ -import { useRef, useEffect, useState, useMemo, Fragment } from 'react' -import { useModelingContext } from 'hooks/useModelingContext' +import { Dialog, Popover, Transition } from '@headlessui/react' +import { Fragment, useEffect, useMemo, useRef, useState } from 'react' +import toast from 'react-hot-toast' +import type { InstanceProps } from 'react-modal-promise' +import { create } from 'react-modal-promise' -import { cameraMouseDragGuards } from 'lib/cameraControls' -import { ARROWHEAD, DEBUG_SHOW_BOTH_SCENES } from './sceneInfra' -import { ReactCameraProperties } from './CameraControls' -import { throttle, toSync } from 'lib/utils' -import { - sceneInfra, - kclManager, - codeManager, - editorManager, - sceneEntitiesManager, - engineCommandManager, - rustContext, -} from 'lib/singletons' +import type { Node } from '@rust/kcl-lib/bindings/Node' + +import type { ReactCameraProperties } from '@src/clientSideScene/CameraControls' import { EXTRA_SEGMENT_HANDLE, PROFILE_START, getParentGroup, -} from './sceneEntities' -import { SegmentOverlay, SketchDetails } from 'machines/modelingMachine' -import { findUsesOfTagInPipe, getNodeFromPath } from 'lang/queryAst' +} from '@src/clientSideScene/sceneConstants' import { - CallExpression, - CallExpressionKw, - PathToNode, - Program, - Expr, - parse, - recast, - defaultSourceRange, - resultIsOk, - topLevelRange, -} from 'lang/wasm' -import { CustomIcon, CustomIconName } from 'components/CustomIcon' -import { ConstrainInfo } from 'lang/std/stdTypes' -import { getConstraintInfo, getConstraintInfoKw } from 'lang/std/sketch' -import { Dialog, Popover, Transition } from '@headlessui/react' -import toast from 'react-hot-toast' -import { InstanceProps, create } from 'react-modal-promise' -import { executeAstMock } from 'lang/langHelpers' + ARROWHEAD, + DEBUG_SHOW_BOTH_SCENES, +} from '@src/clientSideScene/sceneInfra' +import { ActionButton } from '@src/components/ActionButton' +import type { CustomIconName } from '@src/components/CustomIcon' +import { CustomIcon } from '@src/components/CustomIcon' +import { useModelingContext } from '@src/hooks/useModelingContext' +import { executeAstMock } from '@src/lang/langHelpers' import { deleteSegmentFromPipeExpression, removeSingleConstraintInfo, -} from 'lang/modifyAst' -import { ActionButton } from 'components/ActionButton' -import { err, reportRejection, trap } from 'lib/trap' -import { Node } from '@rust/kcl-lib/bindings/Node' -import { commandBarActor } from 'machines/commandBarMachine' -import { useSettings } from 'machines/appMachine' +} from '@src/lang/modifyAst' +import { findUsesOfTagInPipe, getNodeFromPath } from '@src/lang/queryAst' +import { getConstraintInfo, getConstraintInfoKw } from '@src/lang/std/sketch' +import type { ConstrainInfo } from '@src/lang/std/stdTypes' +import { topLevelRange } from '@src/lang/util' +import type { + CallExpression, + CallExpressionKw, + Expr, + PathToNode, + Program, +} from '@src/lang/wasm' +import { defaultSourceRange, parse, recast, resultIsOk } from '@src/lang/wasm' +import { cameraMouseDragGuards } from '@src/lib/cameraControls' +import { + codeManager, + editorManager, + engineCommandManager, + kclManager, + rustContext, + sceneEntitiesManager, + sceneInfra, +} from '@src/lib/singletons' +import { err, reportRejection, trap } from '@src/lib/trap' +import { throttle, toSync } from '@src/lib/utils' +import type { useSettings } from '@src/machines/appMachine' +import { commandBarActor } from '@src/machines/commandBarMachine' +import type { + SegmentOverlay, + SketchDetails, +} from '@src/machines/modelingMachine' function useShouldHideScene(): { hideClient: boolean; hideServer: boolean } { const [isCamMoving, setIsCamMoving] = useState(false) @@ -635,8 +641,8 @@ const ConstraintSymbol = ({ implicitDesc ? 'bg-chalkboard-10 dark:bg-chalkboard-100 border-transparent border-0 rounded' : isConstrained - ? 'bg-chalkboard-10 dark:bg-chalkboard-90 dark:hover:bg-chalkboard-80 border-chalkboard-40 dark:border-chalkboard-70 rounded-sm' - : 'bg-primary/30 dark:bg-primary text-primary dark:text-chalkboard-10 dark:border-transparent group-hover:bg-primary/40 group-hover:border-primary/50 group-hover:brightness-125' + ? 'bg-chalkboard-10 dark:bg-chalkboard-90 dark:hover:bg-chalkboard-80 border-chalkboard-40 dark:border-chalkboard-70 rounded-sm' + : 'bg-primary/30 dark:bg-primary text-primary dark:text-chalkboard-10 dark:border-transparent group-hover:bg-primary/40 group-hover:border-primary/50 group-hover:brightness-125' } h-[26px] w-[26px] rounded-sm relative m-0 p-0`} onMouseEnter={() => { editorManager.setHighlightRange([range]) diff --git a/src/clientSideScene/helpers.test.ts b/src/clientSideScene/helpers.test.ts index 1b6a68309..e20f28f42 100644 --- a/src/clientSideScene/helpers.test.ts +++ b/src/clientSideScene/helpers.test.ts @@ -1,5 +1,6 @@ import { Quaternion } from 'three' -import { isQuaternionVertical } from './helpers' + +import { isQuaternionVertical } from '@src/clientSideScene/helpers' describe('isQuaternionVertical', () => { it('should identify vertical quaternions', () => { diff --git a/src/clientSideScene/helpers.ts b/src/clientSideScene/helpers.ts index e938fa2c5..b99c3e9ed 100644 --- a/src/clientSideScene/helpers.ts +++ b/src/clientSideScene/helpers.ts @@ -1,15 +1,13 @@ -import { compareVec2Epsilon2 } from 'lang/std/sketch' +import type { Group, Mesh, OrthographicCamera, Quaternion } from 'three' import { GridHelper, LineBasicMaterial, - OrthographicCamera, PerspectiveCamera, - Group, - Mesh, - Quaternion, Vector3, } from 'three' +import { compareVec2Epsilon2 } from '@src/lang/std/sketch' + export function createGridHelper({ size, divisions, diff --git a/src/clientSideScene/sceneConstants.ts b/src/clientSideScene/sceneConstants.ts new file mode 100644 index 000000000..4e1dead89 --- /dev/null +++ b/src/clientSideScene/sceneConstants.ts @@ -0,0 +1,76 @@ +// Constants shared between sceneEntities.ts and segments.ts +// This file helps break circular dependencies +import type { Group } from 'three' + +// Segment types +export const ARC_SEGMENT = 'arc-segment' +export const ARC_SEGMENT_BODY = 'arc-segment-body' +export const ARC_SEGMENT_DASH = 'arc-segment-dash' +export const STRAIGHT_SEGMENT = 'straight-segment' +export const STRAIGHT_SEGMENT_BODY = 'straight-segment-body' +export const STRAIGHT_SEGMENT_DASH = 'straight-segment-body-dashed' +export const CIRCLE_SEGMENT = 'circle-segment' +export const CIRCLE_SEGMENT_BODY = 'circle-segment-body' +export const CIRCLE_SEGMENT_DASH = 'circle-segment-body-dashed' +export const TANGENTIAL_ARC_TO_SEGMENT = 'tangential-arc-to-segment' +export const TANGENTIAL_ARC_TO_SEGMENT_BODY = 'tangential-arc-to-segment-body' +export const TANGENTIAL_ARC_TO__SEGMENT_DASH = + 'tangential-arc-to-segment-body-dashed' +export const THREE_POINT_ARC_SEGMENT = 'three-point-arc-segment' +export const THREE_POINT_ARC_SEGMENT_BODY = 'three-point-arc-segment-body' +export const THREE_POINT_ARC_SEGMENT_DASH = 'three-point-arc-segment-dash' +export const CIRCLE_THREE_POINT_SEGMENT = 'circle-three-point-segment' +export const CIRCLE_THREE_POINT_SEGMENT_BODY = 'circle-segment-body' +export const CIRCLE_THREE_POINT_SEGMENT_DASH = + 'circle-three-point-segment-body-dashed' + +// Handle names +export const ARC_ANGLE_END = 'arc-angle-end' +export const ARC_ANGLE_REFERENCE_LINE = 'arc-angle-reference-line' +export const ARC_CENTER_TO_FROM = 'arc-center-to-from' +export const ARC_CENTER_TO_TO = 'arc-center-to-to' +export const CIRCLE_CENTER_HANDLE = 'circle-center-handle' +export const CIRCLE_THREE_POINT_HANDLE1 = 'circle-three-point-handle1' +export const CIRCLE_THREE_POINT_HANDLE2 = 'circle-three-point-handle2' +export const CIRCLE_THREE_POINT_HANDLE3 = 'circle-three-point-handle3' +export const THREE_POINT_ARC_HANDLE2 = 'three-point-arc-handle2' +export const THREE_POINT_ARC_HANDLE3 = 'three-point-arc-handle3' +export const EXTRA_SEGMENT_HANDLE = 'extraSegmentHandle' +export const PROFILE_START = 'profile-start' + +// Additional types +export const DRAFT_DASHED_LINE = 'draft-dashed-line' + +// Measurements +export const EXTRA_SEGMENT_OFFSET_PX = 8 +export const SEGMENT_WIDTH_PX = 1.6 +export const HIDE_SEGMENT_LENGTH = 75 +export const HIDE_HOVER_SEGMENT_LENGTH = 60 + +// Segment groups +export const SEGMENT_BODIES = [ + STRAIGHT_SEGMENT, + TANGENTIAL_ARC_TO_SEGMENT, + CIRCLE_SEGMENT, + CIRCLE_THREE_POINT_SEGMENT, + ARC_SEGMENT, + THREE_POINT_ARC_SEGMENT, +] + +export const SEGMENT_BODIES_PLUS_PROFILE_START = [ + ...SEGMENT_BODIES, + PROFILE_START, +] + +// Helper functions +export function getParentGroup( + object: any, + stopAt: string[] = SEGMENT_BODIES +): Group | null { + if (stopAt.includes(object?.userData?.type)) { + return object + } else if (object?.parent) { + return getParentGroup(object.parent, stopAt) + } + return null +} diff --git a/src/clientSideScene/sceneEntities.ts b/src/clientSideScene/sceneEntities.ts index e2181ed94..697bfc773 100644 --- a/src/clientSideScene/sceneEntities.ts +++ b/src/clientSideScene/sceneEntities.ts @@ -1,189 +1,171 @@ +import toast from 'react-hot-toast' +import type { + Intersection, + Object3D, + Object3DEventMap, + Quaternion, +} from 'three' import { BoxGeometry, DoubleSide, + ExtrudeGeometry, Group, - Intersection, + LineCurve3, Mesh, MeshBasicMaterial, - Object3D, - Object3DEventMap, OrthographicCamera, PerspectiveCamera, PlaneGeometry, Points, - Quaternion, + Shape, Vector2, Vector3, - Shape, - LineCurve3, - ExtrudeGeometry, } from 'three' +import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer' +import { radToDeg } from 'three/src/math/MathUtils' + +import type { Models } from '@kittycad/lib/dist/types/src' +import type { CallExpression } from '@rust/kcl-lib/bindings/CallExpression' +import type { CallExpressionKw } from '@rust/kcl-lib/bindings/CallExpressionKw' +import type { Node } from '@rust/kcl-lib/bindings/Node' +import type { Path } from '@rust/kcl-lib/bindings/Path' +import type { PipeExpression } from '@rust/kcl-lib/bindings/PipeExpression' +import type { Point3d } from '@rust/kcl-lib/bindings/Point3d' +import type { Program } from '@rust/kcl-lib/bindings/Program' +import type { Sketch } from '@rust/kcl-lib/bindings/Sketch' +import type { SourceRange } from '@rust/kcl-lib/bindings/SourceRange' +import type { VariableDeclaration } from '@rust/kcl-lib/bindings/VariableDeclaration' +import type { VariableDeclarator } from '@rust/kcl-lib/bindings/VariableDeclarator' +import { uuidv4 } from '@src/lib/utils' + +import { + createGridHelper, + isQuaternionVertical, + orthoScale, + perspScale, + quaternionFromUpNForward, +} from '@src/clientSideScene/helpers' +import { + ARC_ANGLE_END, + ARC_SEGMENT, + CIRCLE_CENTER_HANDLE, + CIRCLE_SEGMENT, + CIRCLE_THREE_POINT_HANDLE1, + CIRCLE_THREE_POINT_HANDLE2, + CIRCLE_THREE_POINT_HANDLE3, + CIRCLE_THREE_POINT_SEGMENT, + DRAFT_DASHED_LINE, + EXTRA_SEGMENT_HANDLE, + PROFILE_START, + SEGMENT_BODIES, + SEGMENT_BODIES_PLUS_PROFILE_START, + SEGMENT_WIDTH_PX, + STRAIGHT_SEGMENT, + STRAIGHT_SEGMENT_DASH, + TANGENTIAL_ARC_TO_SEGMENT, + THREE_POINT_ARC_HANDLE2, + THREE_POINT_ARC_HANDLE3, + THREE_POINT_ARC_SEGMENT, + getParentGroup, +} from '@src/clientSideScene/sceneConstants' +import type { + OnClickCallbackArgs, + OnMouseEnterLeaveArgs, + SceneInfra, +} from '@src/clientSideScene/sceneInfra' import { ANGLE_SNAP_THRESHOLD_DEGREES, ARROWHEAD, AXIS_GROUP, DRAFT_POINT, DRAFT_POINT_GROUP, - getSceneScale, INTERSECTION_PLANE_LAYER, - OnClickCallbackArgs, - OnMouseEnterLeaveArgs, RAYCASTABLE_PLANE, SKETCH_GROUP_SEGMENTS, SKETCH_LAYER, X_AXIS, Y_AXIS, -} from './sceneInfra' -import { isQuaternionVertical, quaternionFromUpNForward } from './helpers' -import { - CallExpression, - parse, - Path, - PathToNode, - PipeExpression, - Program, - recast, - Sketch, - VariableDeclaration, - VariableDeclarator, - sketchFromKclValue, - defaultSourceRange, - sourceRangeFromRust, - resultIsOk, - SourceRange, - topLevelRange, - CallExpressionKw, - VariableMap, -} from 'lang/wasm' -import { - engineCommandManager, - kclManager, - sceneInfra, - codeManager, - editorManager, - rustContext, -} from 'lib/singletons' -import { getNodeFromPath } from 'lang/queryAst' -import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils' -import { executeAstMock, ToolTip } from 'lang/langHelpers' + getSceneScale, +} from '@src/clientSideScene/sceneInfra' +import type { SegmentUtils } from '@src/clientSideScene/segments' import { createProfileStartHandle, dashedStraight, - SegmentUtils, segmentUtils, -} from './segments' -import { - addCallExpressionsToPipe, - addCloseToPipe, - addNewSketchLn, - ARG_END_ABSOLUTE, - changeSketchArguments, - Coords2d, - updateStartProfileAtArgs, -} from 'lang/std/sketch' -import { isArray, isOverlap, roundOff } from 'lib/utils' +} from '@src/clientSideScene/segments' +import type EditorManager from '@src/editor/manager' +import type CodeManager from '@src/lang/codeManager' +import { ARG_END_ABSOLUTE } from '@src/lang/constants' import { createArrayExpression, createCallExpressionStdLib, - createLocalName, createCallExpressionStdLibKw, createLabeledArg, createLiteral, - createNodeFromExprSnippet, + createLocalName, createPipeExpression, createPipeSubstitution, createVariableDeclaration, findUniqueName, +} from '@src/lang/create' +import type { KclManager } from '@src/lang/KclSingleton' +import type { ToolTip } from '@src/lang/langHelpers' +import { executeAstMock } from '@src/lang/langHelpers' +import { updateModelingState } from '@src/lang/modelingWorkflows' +import { + createNodeFromExprSnippet, getInsertIndex, insertNewStartProfileAt, updateSketchNodePathsWithInsertIndex, -} from 'lang/modifyAst' -import { Selections, getEventForSegmentSelection } from 'lib/selections' -import { createGridHelper, orthoScale, perspScale } from './helpers' -import { Models } from '@kittycad/lib' -import { uuidv4 } from 'lib/utils' +} from '@src/lang/modifyAst' +import { getNodeFromPath } from '@src/lang/queryAst' +import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils' import { + codeRefFromRange, + getArtifactFromRange, +} from '@src/lang/std/artifactGraph' +import type { EngineCommandManager } from '@src/lang/std/engineConnection' +import type { Coords2d } from '@src/lang/std/sketch' +import { + addCallExpressionsToPipe, + addCloseToPipe, + addNewSketchLn, + changeSketchArguments, + updateStartProfileAtArgs, +} from '@src/lang/std/sketch' +import type { SegmentInputs } from '@src/lang/std/stdTypes' +import { topLevelRange } from '@src/lang/util' +import type { PathToNode, VariableMap } from '@src/lang/wasm' +import { + defaultSourceRange, + parse, + recast, + resultIsOk, + sketchFromKclValue, + sourceRangeFromRust, +} from '@src/lang/wasm' +import { EXECUTION_TYPE_MOCK } from '@src/lib/constants' +import { + getRectangleCallExpressions, + updateCenterRectangleSketch, + updateRectangleSketch, +} from '@src/lib/rectangleTool' +import type RustContext from '@src/lib/rustContext' +import type { Selections } from '@src/lib/selections' +import { getEventForSegmentSelection } from '@src/lib/selections' +import type { Themes } from '@src/lib/theme' +import { getThemeColorForThreeJs } from '@src/lib/theme' +import { err, reportRejection, trap } from '@src/lib/trap' +import { isArray, isOverlap, roundOff } from '@src/lib/utils' +import type { SegmentOverlayPayload, SketchDetails, SketchDetailsUpdate, SketchTool, -} from 'machines/modelingMachine' -import { EngineCommandManager } from 'lang/std/engineConnection' -import { - getRectangleCallExpressions, - updateRectangleSketch, - updateCenterRectangleSketch, -} from 'lib/rectangleTool' -import { getThemeColorForThreeJs, Themes } from 'lib/theme' -import { err, reportRejection, trap } from 'lib/trap' -import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer' -import { Point3d } from '@rust/kcl-lib/bindings/Point3d' -import { SegmentInputs } from 'lang/std/stdTypes' -import { Node } from '@rust/kcl-lib/bindings/Node' -import { radToDeg } from 'three/src/math/MathUtils' -import toast from 'react-hot-toast' -import { getArtifactFromRange, codeRefFromRange } from 'lang/std/artifactGraph' -import { updateModelingState } from 'lang/modelingWorkflows' -import { EXECUTION_TYPE_MOCK } from 'lib/constants' +} from '@src/machines/modelingMachine' type DraftSegment = 'line' | 'tangentialArcTo' -export const EXTRA_SEGMENT_HANDLE = 'extraSegmentHandle' -export const EXTRA_SEGMENT_OFFSET_PX = 8 -export const PROFILE_START = 'profile-start' - -export const DRAFT_DASHED_LINE = 'draft-dashed-line' - -export const STRAIGHT_SEGMENT = 'straight-segment' -export const STRAIGHT_SEGMENT_BODY = 'straight-segment-body' -export const STRAIGHT_SEGMENT_DASH = 'straight-segment-body-dashed' -export const TANGENTIAL_ARC_TO__SEGMENT_DASH = - 'tangential-arc-to-segment-body-dashed' -export const TANGENTIAL_ARC_TO_SEGMENT = 'tangential-arc-to-segment' -export const TANGENTIAL_ARC_TO_SEGMENT_BODY = 'tangential-arc-to-segment-body' -export const CIRCLE_THREE_POINT_SEGMENT = 'circle-three-point-segment' -export const CIRCLE_THREE_POINT_SEGMENT_BODY = 'circle-segment-body' -export const CIRCLE_THREE_POINT_SEGMENT_DASH = - 'circle-three-point-segment-body-dashed' -export const CIRCLE_THREE_POINT_HANDLE1 = 'circle-three-point-handle1' -export const CIRCLE_THREE_POINT_HANDLE2 = 'circle-three-point-handle2' -export const CIRCLE_THREE_POINT_HANDLE3 = 'circle-three-point-handle3' -export const CIRCLE_SEGMENT = 'circle-segment' -export const CIRCLE_SEGMENT_BODY = 'circle-segment-body' -export const CIRCLE_SEGMENT_DASH = 'circle-segment-body-dashed' -export const CIRCLE_CENTER_HANDLE = 'circle-center-handle' -export const SEGMENT_WIDTH_PX = 1.6 - -// Arc segment constants -export const ARC_SEGMENT = 'arc-segment' -export const ARC_SEGMENT_BODY = 'arc-segment-body' -export const ARC_SEGMENT_DASH = 'arc-segment-dash' -export const ARC_ANGLE_END = 'arc-angle-end' -export const ARC_CENTER_TO_FROM = 'arc-center-to-from' -export const ARC_CENTER_TO_TO = 'arc-center-to-to' -export const ARC_ANGLE_REFERENCE_LINE = 'arc-angle-reference-line' - -export const THREE_POINT_ARC_SEGMENT = 'three-point-arc-segment' -export const THREE_POINT_ARC_SEGMENT_BODY = 'three-point-arc-segment-body' -export const THREE_POINT_ARC_SEGMENT_DASH = 'three-point-arc-segment-dash' -export const THREE_POINT_ARC_HANDLE2 = 'three-point-arc-handle2' -export const THREE_POINT_ARC_HANDLE3 = 'three-point-arc-handle3' - -export const HIDE_SEGMENT_LENGTH = 75 // in pixels -export const HIDE_HOVER_SEGMENT_LENGTH = 60 // in pixels -export const SEGMENT_BODIES = [ - STRAIGHT_SEGMENT, - TANGENTIAL_ARC_TO_SEGMENT, - CIRCLE_SEGMENT, - CIRCLE_THREE_POINT_SEGMENT, - ARC_SEGMENT, - THREE_POINT_ARC_SEGMENT, -] -export const SEGMENT_BODIES_PLUS_PROFILE_START = [ - ...SEGMENT_BODIES, - PROFILE_START, -] - type Vec3Array = [number, number, number] // This singleton Class is responsible for all of the things the user sees and interacts with. @@ -191,15 +173,34 @@ type Vec3Array = [number, number, number] // Cameras, controls, raycasters, etc are handled by sceneInfra export class SceneEntities { readonly engineCommandManager: EngineCommandManager + readonly sceneInfra: SceneInfra + readonly editorManager: EditorManager + readonly codeManager: CodeManager + readonly kclManager: KclManager + readonly rustContext: RustContext activeSegments: { [key: string]: Group } = {} readonly intersectionPlane: Mesh axisGroup: Group | null = null draftPointGroups: Group[] = [] currentSketchQuaternion: Quaternion | null = null - constructor(engineCommandManager: EngineCommandManager) { + constructor( + engineCommandManager: EngineCommandManager, + sceneInfra: SceneInfra, + editorManager: EditorManager, + codeManager: CodeManager, + kclManager: KclManager, + rustContext: RustContext + ) { this.engineCommandManager = engineCommandManager - this.intersectionPlane = SceneEntities.createIntersectionPlane() - sceneInfra.camControls.subscribeToCamChange(this.onCamChange) + this.sceneInfra = sceneInfra + this.editorManager = editorManager + this.codeManager = codeManager + this.kclManager = kclManager + this.rustContext = rustContext + this.intersectionPlane = SceneEntities.createIntersectionPlane( + this.sceneInfra + ) + this.sceneInfra.camControls.subscribeToCamChange(this.onCamChange) window.addEventListener('resize', this.onWindowResize) } @@ -207,14 +208,14 @@ export class SceneEntities { this.onCamChange() } onCamChange = () => { - const orthoFactor = orthoScale(sceneInfra.camControls.camera) + const orthoFactor = orthoScale(this.sceneInfra.camControls.camera) const callbacks: (() => SegmentOverlayPayload | null)[] = [] Object.values(this.activeSegments).forEach((segment, index) => { const factor = - (sceneInfra.camControls.camera instanceof OrthographicCamera + (this.sceneInfra.camControls.camera instanceof OrthographicCamera ? orthoFactor - : perspScale(sceneInfra.camControls.camera, segment)) / - sceneInfra._baseUnitMultiplier + : perspScale(this.sceneInfra.camControls.camera, segment)) / + this.sceneInfra._baseUnitMultiplier let input: SegmentInputs = { type: 'straight-segment', from: segment.userData.from, @@ -307,7 +308,7 @@ export class SceneEntities { input, group: segment, scale: factor, - sceneInfra, + sceneInfra: this.sceneInfra, }) callBack && !err(callBack) && callbacks.push(callBack) if (segment.name === PROFILE_START) { @@ -316,18 +317,18 @@ export class SceneEntities { }) if (this.axisGroup) { const factor = - sceneInfra.camControls.camera instanceof OrthographicCamera + this.sceneInfra.camControls.camera instanceof OrthographicCamera ? orthoFactor - : perspScale(sceneInfra.camControls.camera, this.axisGroup) + : perspScale(this.sceneInfra.camControls.camera, this.axisGroup) const x = this.axisGroup.getObjectByName(X_AXIS) - x?.scale.set(1, factor / sceneInfra._baseUnitMultiplier, 1) + x?.scale.set(1, factor / this.sceneInfra._baseUnitMultiplier, 1) const y = this.axisGroup.getObjectByName(Y_AXIS) - y?.scale.set(factor / sceneInfra._baseUnitMultiplier, 1, 1) + y?.scale.set(factor / this.sceneInfra._baseUnitMultiplier, 1, 1) } - sceneInfra.overlayCallbacks(callbacks) + this.sceneInfra.overlayCallbacks(callbacks) } - private static createIntersectionPlane() { + private static createIntersectionPlane(sceneInfra: SceneInfra) { const hundredM = 100_0000 const planeGeometry = new PlaneGeometry(hundredM, hundredM) const planeMaterial = new MeshBasicMaterial({ @@ -349,7 +350,7 @@ export class SceneEntities { up: [number, number, number], sketchPosition?: [number, number, number] ) { - const orthoFactor = orthoScale(sceneInfra.camControls.camera) + const orthoFactor = orthoScale(this.sceneInfra.camControls.camera) const baseXColor = 0x000055 const baseYColor = 0x550000 const axisPixelWidth = 1.6 @@ -391,17 +392,17 @@ export class SceneEntities { gridHelper.renderOrder = -3 // is this working? gridHelper.name = 'gridHelper' const sceneScale = getSceneScale( - sceneInfra.camControls.camera, - sceneInfra.camControls.target + this.sceneInfra.camControls.camera, + this.sceneInfra.camControls.target ) gridHelper.scale.set(sceneScale, sceneScale, sceneScale) const factor = - sceneInfra.camControls.camera instanceof OrthographicCamera + this.sceneInfra.camControls.camera instanceof OrthographicCamera ? orthoFactor - : perspScale(sceneInfra.camControls.camera, this.axisGroup) - xAxisMesh?.scale.set(1, factor / sceneInfra._baseUnitMultiplier, 1) - yAxisMesh?.scale.set(factor / sceneInfra._baseUnitMultiplier, 1, 1) + : perspScale(this.sceneInfra.camControls.camera, this.axisGroup) + xAxisMesh?.scale.set(1, factor / this.sceneInfra._baseUnitMultiplier, 1) + yAxisMesh?.scale.set(factor / this.sceneInfra._baseUnitMultiplier, 1, 1) this.axisGroup.add(xAxisMesh, yAxisMesh, gridHelper) this.currentSketchQuaternion && @@ -420,10 +421,10 @@ export class SceneEntities { ) this.axisGroup.setRotationFromQuaternion(quat) sketchPosition && this.axisGroup.position.set(...sketchPosition) - sceneInfra.scene.add(this.axisGroup) + this.sceneInfra.scene.add(this.axisGroup) } getDraftPoint() { - return sceneInfra.scene.getObjectByName(DRAFT_POINT) + return this.sceneInfra.scene.getObjectByName(DRAFT_POINT) } createDraftPoint({ point, @@ -449,16 +450,16 @@ export class SceneEntities { new Vector3(...zAxis) ) draftPointGroup.setRotationFromQuaternion(currentSketchQuaternion) - sceneInfra.scene.add(draftPointGroup) + this.sceneInfra.scene.add(draftPointGroup) const dummy = new Mesh() dummy.position.set(0, 0, 0) - const scale = sceneInfra.getClientSceneScaleFactor(dummy) + const scale = this.sceneInfra.getClientSceneScaleFactor(dummy) const draftPoint = createProfileStartHandle({ isDraft: true, from: [point.x, point.y], scale, - theme: sceneInfra._theme, + theme: this.sceneInfra._theme, // default is 12, this makes the draft point pop a bit more, // especially when snapping to the startProfileAt handle as it's it was the exact same size size: 16, @@ -505,7 +506,7 @@ export class SceneEntities { this.intersectionPlane.position.copy( new Vector3(...(sketchDetails?.origin || [0, 0, 0])) ) - sceneInfra.setCallbacks({ + this.sceneInfra.setCallbacks({ onMove: (args) => { if (!args.intersects.length) return const axisIntersection = args.intersects.find( @@ -570,7 +571,7 @@ export class SceneEntities { this.removeDraftPoint() if (!args) return // If there is a valid camera interaction that matches, do that instead - const interaction = sceneInfra.camControls.getInteractionType( + const interaction = this.sceneInfra.camControls.getInteractionType( args.mouseEvent ) if (interaction !== 'none') return @@ -610,7 +611,7 @@ export class SceneEntities { } const inserted = insertNewStartProfileAt( - kclManager.ast, + this.kclManager.ast, sketchDetails.sketchEntryNodePath || [], sketchDetails.sketchNodePaths, sketchDetails.planeNodePath, @@ -621,7 +622,7 @@ export class SceneEntities { if (trap(inserted)) return const { modifiedAst } = inserted - await kclManager.updateAst(modifiedAst, false) + await this.kclManager.updateAst(modifiedAst, false) // Now perform the caller-specified action afterClick(args, { @@ -660,12 +661,13 @@ export class SceneEntities { const { execState } = await executeAstMock({ ast: truncatedAst, - rustContext, + rustContext: this.rustContext, }) const sketchesInfo = getSketchesInfo({ sketchNodePaths, ast: maybeModdedAst, variables: execState.variables, + kclManager: this.kclManager, }) const group = new Group() @@ -677,7 +679,7 @@ export class SceneEntities { const dummy = new Mesh() // TODO: When we actually have sketch positions and rotations we can use them here. dummy.position.set(0, 0, 0) - const scale = sceneInfra.getClientSceneScaleFactor(dummy) + const scale = this.sceneInfra.getClientSceneScaleFactor(dummy) const callbacks: (() => SegmentOverlayPayload | null)[] = [] @@ -696,7 +698,7 @@ export class SceneEntities { id: sketch.start.__geoMeta.id, pathToNode: segPathToNode, scale, - theme: sceneInfra._theme, + theme: this.sceneInfra._theme, isDraft: false, }) _profileStart.layers.set(SKETCH_LAYER) @@ -758,14 +760,14 @@ export class SceneEntities { segment.type === 'TangentialArcTo' ? segmentUtils.tangentialArcTo.init : segment.type === 'Circle' - ? segmentUtils.circle.init - : segment.type === 'Arc' - ? segmentUtils.arc.init - : segment.type === 'CircleThreePoint' - ? segmentUtils.circleThreePoint.init - : segment.type === 'ArcThreePoint' - ? segmentUtils.threePointArc.init - : segmentUtils.straight.init + ? segmentUtils.circle.init + : segment.type === 'Arc' + ? segmentUtils.arc.init + : segment.type === 'CircleThreePoint' + ? segmentUtils.circleThreePoint.init + : segment.type === 'ArcThreePoint' + ? segmentUtils.threePointArc.init + : segmentUtils.straight.init const input: SegmentInputs = segment.type === 'Circle' ? { @@ -777,33 +779,34 @@ export class SceneEntities { radius: segment.radius, } : segment.type === 'CircleThreePoint' || - segment.type === 'ArcThreePoint' - ? { - type: 'circle-three-point-segment', - p1: segment.p1, - p2: segment.p2, - p3: segment.p3, - } - : segment.type === 'Arc' - ? { - type: 'arc-segment', - from: segment.from, - center: segment.center, - to: segment.to, - ccw: segment.ccw, - radius: segment.radius, - } - : { - type: 'straight-segment', - from: segment.from, - to: segment.to, - } + segment.type === 'ArcThreePoint' + ? { + type: 'circle-three-point-segment', + p1: segment.p1, + p2: segment.p2, + p3: segment.p3, + } + : segment.type === 'Arc' + ? { + type: 'arc-segment', + from: segment.from, + center: segment.center, + to: segment.to, + ccw: segment.ccw, + radius: segment.radius, + } + : { + type: 'straight-segment', + from: segment.from, + to: segment.to, + } const startRange = _node1.node.start const endRange = _node1.node.end const sourceRange: SourceRange = [startRange, endRange, 0] const selection: Selections = computeSelectionFromSourceRangeAndAST( sourceRange, - maybeModdedAst + maybeModdedAst, + this.kclManager ) const result = initSegment({ prevSegment: sketch.paths[index - 1], @@ -813,10 +816,10 @@ export class SceneEntities { pathToNode: segPathToNode, isDraftSegment, scale, - texture: sceneInfra.extraSegmentTexture, - theme: sceneInfra._theme, + texture: this.sceneInfra.extraSegmentTexture, + theme: this.sceneInfra._theme, isSelected, - sceneInfra, + sceneInfra: this.sceneInfra, selection, }) if (err(result)) return @@ -845,9 +848,9 @@ export class SceneEntities { this.currentSketchQuaternion ) position && this.intersectionPlane.position.set(...position) - sceneInfra.scene.add(group) - sceneInfra.camControls.enableRotate = false - sceneInfra.overlayCallbacks(callbacks) + this.sceneInfra.scene.add(group) + this.sceneInfra.camControls.enableRotate = false + this.sceneInfra.overlayCallbacks(callbacks) return { truncatedAst, @@ -864,9 +867,9 @@ export class SceneEntities { origin: [number, number, number] ) => { if (trap(modifiedAst)) return Promise.reject(modifiedAst) - const nextAst = await kclManager.updateAst(modifiedAst, false) + const nextAst = await this.kclManager.updateAst(modifiedAst, false) this.tearDownSketch({ removeAxis: false }) - sceneInfra.resetMouseListeners() + this.sceneInfra.resetMouseListeners() await this.setupSketch({ sketchEntryNodePath, sketchNodePaths, @@ -903,7 +906,7 @@ export class SceneEntities { segmentName: 'line' | 'tangentialArcTo' = 'line', shouldTearDown = true ) => { - const _ast = structuredClone(kclManager.ast) + const _ast = structuredClone(this.kclManager.ast) const _node1 = getNodeFromPath( _ast, @@ -914,7 +917,7 @@ export class SceneEntities { const variableDeclarationName = _node1.node?.declaration.id?.name || '' const sg = sketchFromKclValue( - kclManager.variables[variableDeclarationName], + this.kclManager.variables[variableDeclarationName], variableDeclarationName ) if (err(sg)) return Promise.reject(sg) @@ -923,7 +926,7 @@ export class SceneEntities { const index = sg.paths.length // because we've added a new segment that's not in the memory yet, no need for `.length -1` const mod = addNewSketchLn({ node: _ast, - variables: kclManager.variables, + variables: this.kclManager.variables, input: { type: 'straight-segment', to: lastSeg.to, @@ -940,7 +943,7 @@ export class SceneEntities { const draftExpressionsIndices = { start: index, end: index } if (shouldTearDown) this.tearDownSketch({ removeAxis: false }) - sceneInfra.resetMouseListeners() + this.sceneInfra.resetMouseListeners() const { truncatedAst } = await this.setupSketch({ sketchEntryNodePath, @@ -951,11 +954,11 @@ export class SceneEntities { maybeModdedAst: modifiedAst, draftExpressionsIndices, }) - sceneInfra.setCallbacks({ + this.sceneInfra.setCallbacks({ onClick: async (args) => { if (!args) return // If there is a valid camera interaction that matches, do that instead - const interaction = sceneInfra.camControls.getInteractionType( + const interaction = this.sceneInfra.camControls.getInteractionType( args.mouseEvent ) if (interaction !== 'none') return @@ -968,12 +971,15 @@ export class SceneEntities { sketchEntryNodePath ) - let modifiedAst: Node | Error = structuredClone(kclManager.ast) + let modifiedAst: Node | Error = structuredClone( + this.kclManager.ast + ) const sketch = sketchFromPathToNode({ pathToNode: sketchEntryNodePath, - ast: kclManager.ast, - variables: kclManager.variables, + ast: this.kclManager.ast, + variables: this.kclManager.variables, + kclManager: this.kclManager, }) if (err(sketch)) return Promise.reject(sketch) if (!sketch) return Promise.reject(new Error('No sketch found')) @@ -989,8 +995,8 @@ export class SceneEntities { ]), ]) modifiedAst = addCallExpressionsToPipe({ - node: kclManager.ast, - variables: kclManager.variables, + node: this.kclManager.ast, + variables: this.kclManager.variables, pathToNode: sketchEntryNodePath, expressions: [ segmentName === 'tangentialArcTo' @@ -1006,7 +1012,7 @@ export class SceneEntities { if (trap(modifiedAst)) return Promise.reject(modifiedAst) modifiedAst = addCloseToPipe({ node: modifiedAst, - variables: kclManager.variables, + variables: this.kclManager.variables, pathToNode: sketchEntryNodePath, }) if (trap(modifiedAst)) return Promise.reject(modifiedAst) @@ -1059,8 +1065,8 @@ export class SceneEntities { } const tmp = addNewSketchLn({ - node: kclManager.ast, - variables: kclManager.variables, + node: this.kclManager.ast, + variables: this.kclManager.variables, input: { type: 'straight-segment', from: [lastSegment.to[0], lastSegment.to[1]], @@ -1078,13 +1084,13 @@ export class SceneEntities { } await updateModelingState(modifiedAst, EXECUTION_TYPE_MOCK, { - kclManager, - editorManager, - codeManager, + kclManager: this.kclManager, + editorManager: this.editorManager, + codeManager: this.codeManager, }) if (intersectsProfileStart) { - sceneInfra.modelingSend({ type: 'Close sketch' }) + this.sceneInfra.modelingSend({ type: 'Close sketch' }) } else { await this.setupDraftSegment( sketchEntryNodePath, @@ -1132,7 +1138,7 @@ export class SceneEntities { sketchOrigin: [number, number, number], rectangleOrigin: [x: number, y: number] ): Promise => { - let _ast = structuredClone(kclManager.ast) + let _ast = structuredClone(this.kclManager.ast) const varDec = getNodeFromPath( _ast, @@ -1174,7 +1180,7 @@ export class SceneEntities { _ast = pResult.program // do a quick mock execution to get the program memory up-to-date - await kclManager.executeAstMock(_ast) + await this.kclManager.executeAstMock(_ast) const justCreatedNode = getNodeFromPath( _ast, @@ -1207,7 +1213,7 @@ export class SceneEntities { draftExpressionsIndices: { start: 0, end: 3 }, }) - sceneInfra.setCallbacks({ + this.sceneInfra.setCallbacks({ onMove: async (args) => { // Update the width and height of the draft rectangle @@ -1235,12 +1241,12 @@ export class SceneEntities { const { execState } = await executeAstMock({ ast: truncatedAst, - rustContext, + rustContext: this.rustContext, }) const sketch = sketchFromKclValue(execState.variables[varName], varName) if (err(sketch)) return Promise.reject(sketch) const sgPaths = sketch.paths - const orthoFactor = orthoScale(sceneInfra.camControls.camera) + const orthoFactor = orthoScale(this.sceneInfra.camControls.camera) const varDecIndex = Number(updatedEntryNodePath[1][0]) @@ -1275,7 +1281,7 @@ export class SceneEntities { }, onClick: async (args) => { // If there is a valid camera interaction that matches, do that instead - const interaction = sceneInfra.camControls.getInteractionType( + const interaction = this.sceneInfra.camControls.getInteractionType( args.mouseEvent ) if (interaction !== 'none') return @@ -1312,11 +1318,11 @@ export class SceneEntities { // possible sketchFromKclValue "fails" when sketching on a face, // and this couldn't wouldn't run. await updateModelingState(_ast, EXECUTION_TYPE_MOCK, { - kclManager, - editorManager, - codeManager, + kclManager: this.kclManager, + editorManager: this.editorManager, + codeManager: this.codeManager, }) - sceneInfra.modelingSend({ type: 'Finish rectangle' }) + this.sceneInfra.modelingSend({ type: 'Finish rectangle' }) }, }) return { @@ -1334,7 +1340,7 @@ export class SceneEntities { sketchOrigin: [number, number, number], rectangleOrigin: [x: number, y: number] ): Promise => { - let _ast = structuredClone(kclManager.ast) + let _ast = structuredClone(this.kclManager.ast) const varDec = getNodeFromPath( _ast, @@ -1375,7 +1381,7 @@ export class SceneEntities { _ast = __recastAst.program // do a quick mock execution to get the program memory up-to-date - await kclManager.executeAstMock(_ast) + await this.kclManager.executeAstMock(_ast) const justCreatedNode = getNodeFromPath( _ast, @@ -1407,7 +1413,7 @@ export class SceneEntities { draftExpressionsIndices: { start: 0, end: 3 }, }) - sceneInfra.setCallbacks({ + this.sceneInfra.setCallbacks({ onMove: async (args) => { // Update the width and height of the draft rectangle @@ -1442,12 +1448,12 @@ export class SceneEntities { const { execState } = await executeAstMock({ ast: truncatedAst, - rustContext, + rustContext: this.rustContext, }) const sketch = sketchFromKclValue(execState.variables[varName], varName) if (err(sketch)) return Promise.reject(sketch) const sgPaths = sketch.paths - const orthoFactor = orthoScale(sceneInfra.camControls.camera) + const orthoFactor = orthoScale(this.sceneInfra.camControls.camera) const varDecIndex = Number(updatedEntryNodePath[1][0]) @@ -1465,7 +1471,7 @@ export class SceneEntities { }, onClick: async (args) => { // If there is a valid camera interaction that matches, do that instead - const interaction = sceneInfra.camControls.getInteractionType( + const interaction = this.sceneInfra.camControls.getInteractionType( args.mouseEvent ) if (interaction !== 'none') return @@ -1505,11 +1511,11 @@ export class SceneEntities { // possible sketchFromKclValue "fails" when sketching on a face, // and this couldn't wouldn't run. await updateModelingState(_ast, EXECUTION_TYPE_MOCK, { - kclManager, - editorManager, - codeManager, + kclManager: this.kclManager, + editorManager: this.editorManager, + codeManager: this.codeManager, }) - sceneInfra.modelingSend({ type: 'Finish center rectangle' }) + this.sceneInfra.modelingSend({ type: 'Finish center rectangle' }) } }, }) @@ -1529,7 +1535,7 @@ export class SceneEntities { point1: [x: number, y: number], point2: [x: number, y: number] ): Promise => { - let _ast = structuredClone(kclManager.ast) + let _ast = structuredClone(this.kclManager.ast) const varDec = getNodeFromPath( _ast, @@ -1568,7 +1574,7 @@ export class SceneEntities { _ast = pResult.program // do a quick mock execution to get the program memory up-to-date - await kclManager.executeAstMock(_ast) + await this.kclManager.executeAstMock(_ast) const { truncatedAst } = await this.setupSketch({ sketchEntryNodePath: updatedEntryNodePath, @@ -1580,7 +1586,7 @@ export class SceneEntities { draftExpressionsIndices: { start: 0, end: 0 }, }) - sceneInfra.setCallbacks({ + this.sceneInfra.setCallbacks({ onMove: async (args) => { const firstProfileIndex = Number(updatedSketchNodePaths[0][1][0]) const nodePathWithCorrectedIndexForTruncatedAst = @@ -1601,7 +1607,7 @@ export class SceneEntities { if (sketchInit.type === 'CallExpressionKw') { const moddedResult = changeSketchArguments( modded, - kclManager.variables, + this.kclManager.variables, { type: 'path', pathToNode: nodePathWithCorrectedIndexForTruncatedAst, @@ -1622,12 +1628,12 @@ export class SceneEntities { const { execState } = await executeAstMock({ ast: modded, - rustContext, + rustContext: this.rustContext, }) const sketch = sketchFromKclValue(execState.variables[varName], varName) if (err(sketch)) return const sgPaths = sketch.paths - const orthoFactor = orthoScale(sceneInfra.camControls.camera) + const orthoFactor = orthoScale(this.sceneInfra.camControls.camera) const varDecIndex = Number(updatedEntryNodePath[1][0]) @@ -1645,7 +1651,7 @@ export class SceneEntities { }, onClick: async (args) => { // If there is a valid camera interaction that matches, do that instead - const interaction = sceneInfra.camControls.getInteractionType( + const interaction = this.sceneInfra.camControls.getInteractionType( args.mouseEvent ) if (interaction !== 'none') return @@ -1665,7 +1671,7 @@ export class SceneEntities { if (sketchInit.type === 'CallExpressionKw') { const moddedResult = changeSketchArguments( modded, - kclManager.variables, + this.kclManager.variables, { type: 'path', pathToNode: updatedEntryNodePath, @@ -1689,11 +1695,11 @@ export class SceneEntities { // Update the primary AST and unequip the rectangle tool await updateModelingState(_ast, EXECUTION_TYPE_MOCK, { - kclManager, - editorManager, - codeManager, + kclManager: this.kclManager, + editorManager: this.editorManager, + codeManager: this.codeManager, }) - sceneInfra.modelingSend({ type: 'Finish circle three point' }) + this.sceneInfra.modelingSend({ type: 'Finish circle three point' }) } }, }) @@ -1712,7 +1718,7 @@ export class SceneEntities { sketchOrigin: [number, number, number], center: [x: number, y: number] ): Promise => { - let _ast = structuredClone(kclManager.ast) + let _ast = structuredClone(this.kclManager.ast) const _node1 = getNodeFromPath( _ast, @@ -1723,7 +1729,7 @@ export class SceneEntities { const variableDeclarationName = _node1.node?.declaration.id?.name || '' const sg = sketchFromKclValue( - kclManager.variables[variableDeclarationName], + this.kclManager.variables[variableDeclarationName], variableDeclarationName ) if (err(sg)) return Promise.reject(sg) @@ -1744,7 +1750,7 @@ export class SceneEntities { // Use addNewSketchLn to append an arc to the existing sketch const mod = addNewSketchLn({ node: _ast, - variables: kclManager.variables, + variables: this.kclManager.variables, input: { type: 'arc-segment', from, @@ -1763,13 +1769,13 @@ export class SceneEntities { _ast = pResult.program // do a quick mock execution to get the program memory up-to-date - await kclManager.executeAstMock(_ast) + await this.kclManager.executeAstMock(_ast) const index = sg.paths.length // because we've added a new segment that's not in the memory yet const draftExpressionsIndices = { start: index, end: index } this.tearDownSketch({ removeAxis: false }) - sceneInfra.resetMouseListeners() + this.sceneInfra.resetMouseListeners() const { truncatedAst } = await this.setupSketch({ sketchEntryNodePath, @@ -1781,7 +1787,7 @@ export class SceneEntities { draftExpressionsIndices, }) - sceneInfra.setCallbacks({ + this.sceneInfra.setCallbacks({ onMove: async (args) => { const firstProfileIndex = Number(sketchNodePaths[0][1][0]) const nodePathWithCorrectedIndexForTruncatedAst = structuredClone( @@ -1815,7 +1821,7 @@ export class SceneEntities { const moddedResult = changeSketchArguments( modded, - kclManager.variables, + this.kclManager.variables, { type: 'path', pathToNode: nodePathWithCorrectedIndexForTruncatedAst, @@ -1834,7 +1840,7 @@ export class SceneEntities { } const { execState } = await executeAstMock({ ast: modded, - rustContext, + rustContext: this.rustContext, }) const sketch = sketchFromKclValue( execState.variables[variableDeclarationName], @@ -1842,7 +1848,7 @@ export class SceneEntities { ) if (err(sketch)) return const sgPaths = sketch.paths - const orthoFactor = orthoScale(sceneInfra.camControls.camera) + const orthoFactor = orthoScale(this.sceneInfra.camControls.camera) const varDecIndex = Number(sketchEntryNodePath[1][0]) @@ -1868,7 +1874,7 @@ export class SceneEntities { Number(nodePathWithCorrectedIndexForTruncatedAst[1][0]) - firstProfileIndex // If there is a valid camera interaction that matches, do that instead - const interaction = sceneInfra.camControls.getInteractionType( + const interaction = this.sceneInfra.camControls.getInteractionType( args.mouseEvent ) if (interaction !== 'none') return @@ -1900,7 +1906,7 @@ export class SceneEntities { const moddedResult = changeSketchArguments( modded, - kclManager.variables, + this.kclManager.variables, { type: 'path', pathToNode: mod.pathToNode, @@ -1926,11 +1932,11 @@ export class SceneEntities { // Update the primary AST and unequip the arc tool await updateModelingState(_ast, EXECUTION_TYPE_MOCK, { - kclManager, - editorManager, - codeManager, + kclManager: this.kclManager, + editorManager: this.editorManager, + codeManager: this.codeManager, }) - sceneInfra.modelingSend({ type: 'Finish arc' }) + this.sceneInfra.modelingSend({ type: 'Finish arc' }) } }, }) @@ -1949,7 +1955,7 @@ export class SceneEntities { sketchOrigin: [number, number, number], p2: [x: number, y: number] ): Promise => { - let _ast = structuredClone(kclManager.ast) + let _ast = structuredClone(this.kclManager.ast) const _node1 = getNodeFromPath( _ast, @@ -1960,7 +1966,7 @@ export class SceneEntities { const variableDeclarationName = _node1.node?.declaration.id?.name || '' const sg = sketchFromKclValue( - kclManager.variables[variableDeclarationName], + this.kclManager.variables[variableDeclarationName], variableDeclarationName ) if (err(sg)) return Promise.reject(sg) @@ -1973,7 +1979,7 @@ export class SceneEntities { // Use addNewSketchLn to append an arc to the existing sketch const mod = addNewSketchLn({ node: _ast, - variables: kclManager.variables, + variables: this.kclManager.variables, input: { type: 'circle-three-point-segment', p1, @@ -1990,7 +1996,7 @@ export class SceneEntities { _ast = pResult.program // do a quick mock execution to get the program memory up-to-date - await kclManager.executeAstMock(_ast) + await this.kclManager.executeAstMock(_ast) const index = sg.paths.length // because we've added a new segment that's not in the memory yet const draftExpressionsIndices = { start: index, end: index } @@ -1999,7 +2005,7 @@ export class SceneEntities { const insertIndex = Number(mod.pathToNode[1][0]) this.tearDownSketch({ removeAxis: false }) - sceneInfra.resetMouseListeners() + this.sceneInfra.resetMouseListeners() const { truncatedAst } = await this.setupSketch({ sketchEntryNodePath, @@ -2013,7 +2019,7 @@ export class SceneEntities { const doNotSnapAsThreePointArcIsTheOnlySegment = sg.paths.length === 0 - sceneInfra.setCallbacks({ + this.sceneInfra.setCallbacks({ onMove: async (args) => { const firstProfileIndex = Number(sketchNodePaths[0][1][0]) const nodePathWithCorrectedIndexForTruncatedAst = structuredClone( @@ -2048,7 +2054,7 @@ export class SceneEntities { if (sketchInit.type === 'PipeExpression') { const moddedResult = changeSketchArguments( modded, - kclManager.variables, + this.kclManager.variables, { type: 'path', pathToNode: nodePathWithCorrectedIndexForTruncatedAst, @@ -2065,7 +2071,7 @@ export class SceneEntities { } const { execState } = await executeAstMock({ ast: modded, - rustContext, + rustContext: this.rustContext, }) const sketch = sketchFromKclValue( execState.variables[variableDeclarationName], @@ -2073,7 +2079,7 @@ export class SceneEntities { ) if (err(sketch)) return const sgPaths = sketch.paths - const orthoFactor = orthoScale(sceneInfra.camControls.camera) + const orthoFactor = orthoScale(this.sceneInfra.camControls.camera) const varDecIndex = Number(sketchEntryNodePath[1][0]) @@ -2099,7 +2105,7 @@ export class SceneEntities { Number(nodePathWithCorrectedIndexForTruncatedAst[1][0]) - firstProfileIndex // If there is a valid camera interaction that matches, do that instead - const interaction = sceneInfra.camControls.getInteractionType( + const interaction = this.sceneInfra.camControls.getInteractionType( args.mouseEvent ) if (interaction !== 'none') return @@ -2125,7 +2131,7 @@ export class SceneEntities { const moddedResult = changeSketchArguments( modded, - kclManager.variables, + this.kclManager.variables, { type: 'path', pathToNode: mod.pathToNode, @@ -2167,7 +2173,7 @@ export class SceneEntities { const moddedResult = addCloseToPipe({ node: modded, - variables: kclManager.variables, + variables: this.kclManager.variables, pathToNode: sketchEntryNodePath, }) if (err(moddedResult)) return @@ -2183,14 +2189,14 @@ export class SceneEntities { // Update the primary AST and unequip the arc tool await updateModelingState(_ast, EXECUTION_TYPE_MOCK, { - kclManager, - editorManager, - codeManager, + kclManager: this.kclManager, + editorManager: this.editorManager, + codeManager: this.codeManager, }) if (intersectsProfileStart) { - sceneInfra.modelingSend({ type: 'Close sketch' }) + this.sceneInfra.modelingSend({ type: 'Close sketch' }) } else { - sceneInfra.modelingSend({ type: 'Finish arc' }) + this.sceneInfra.modelingSend({ type: 'Finish arc' }) } } }, @@ -2210,7 +2216,7 @@ export class SceneEntities { sketchOrigin: [number, number, number], circleCenter: [x: number, y: number] ): Promise => { - let _ast = structuredClone(kclManager.ast) + let _ast = structuredClone(this.kclManager.ast) const varDec = getNodeFromPath( _ast, @@ -2255,7 +2261,7 @@ export class SceneEntities { _ast = pResult.program // do a quick mock execution to get the program memory up-to-date - await kclManager.executeAstMock(_ast) + await this.kclManager.executeAstMock(_ast) const { truncatedAst } = await this.setupSketch({ sketchEntryNodePath: updatedEntryNodePath, @@ -2267,7 +2273,7 @@ export class SceneEntities { draftExpressionsIndices: { start: 0, end: 0 }, }) - sceneInfra.setCallbacks({ + this.sceneInfra.setCallbacks({ onMove: async (args) => { const nodePathWithCorrectedIndexForTruncatedAst = structuredClone(updatedEntryNodePath) @@ -2290,7 +2296,7 @@ export class SceneEntities { if (sketchInit.type === 'CallExpressionKw') { const moddedResult = changeSketchArguments( modded, - kclManager.variables, + this.kclManager.variables, { type: 'path', pathToNode: nodePathWithCorrectedIndexForTruncatedAst, @@ -2312,12 +2318,12 @@ export class SceneEntities { const { execState } = await executeAstMock({ ast: modded, - rustContext, + rustContext: this.rustContext, }) const sketch = sketchFromKclValue(execState.variables[varName], varName) if (err(sketch)) return const sgPaths = sketch.paths - const orthoFactor = orthoScale(sceneInfra.camControls.camera) + const orthoFactor = orthoScale(this.sceneInfra.camControls.camera) const varDecIndex = Number(updatedEntryNodePath[1][0]) @@ -2335,7 +2341,7 @@ export class SceneEntities { }, onClick: async (args) => { // If there is a valid camera interaction that matches, do that instead - const interaction = sceneInfra.camControls.getInteractionType( + const interaction = this.sceneInfra.camControls.getInteractionType( args.mouseEvent ) if (interaction !== 'none') return @@ -2358,7 +2364,7 @@ export class SceneEntities { if (sketchInit.type === 'CallExpressionKw') { const moddedResult = changeSketchArguments( modded, - kclManager.variables, + this.kclManager.variables, { type: 'path', pathToNode: updatedEntryNodePath, @@ -2384,11 +2390,11 @@ export class SceneEntities { // Update the primary AST and unequip the rectangle tool await updateModelingState(_ast, EXECUTION_TYPE_MOCK, { - kclManager, - editorManager, - codeManager, + kclManager: this.kclManager, + editorManager: this.editorManager, + codeManager: this.codeManager, }) - sceneInfra.modelingSend({ type: 'Finish circle' }) + this.sceneInfra.modelingSend({ type: 'Finish circle' }) } }, }) @@ -2414,7 +2420,7 @@ export class SceneEntities { position?: [number, number, number] }) => { let addingNewSegmentStatus: 'nothing' | 'pending' | 'added' = 'nothing' - sceneInfra.setCallbacks({ + this.sceneInfra.setCallbacks({ onDragEnd: async () => { if (addingNewSegmentStatus !== 'nothing') { this.tearDownSketch({ removeAxis: false }) @@ -2422,7 +2428,7 @@ export class SceneEntities { this.setupSketch({ sketchEntryNodePath, sketchNodePaths, - maybeModdedAst: kclManager.ast, + maybeModdedAst: this.kclManager.ast, up, forward, position, @@ -2436,7 +2442,7 @@ export class SceneEntities { forward, position, }) - await codeManager.writeToFile() + await this.codeManager.writeToFile() } }, onDrag: async ({ @@ -2457,8 +2463,9 @@ export class SceneEntities { const sketch = sketchFromPathToNode({ pathToNode, - ast: kclManager.ast, - variables: kclManager.variables, + ast: this.kclManager.ast, + variables: this.kclManager.variables, + kclManager: this.kclManager, }) if (trap(sketch)) return if (!sketch) { @@ -2470,8 +2477,8 @@ export class SceneEntities { if (addingNewSegmentStatus === 'nothing') { const prevSegment = sketch.paths[pipeIndex - 2] const mod = addNewSketchLn({ - node: kclManager.ast, - variables: kclManager.variables, + node: this.kclManager.ast, + variables: this.kclManager.variables, input: { type: 'straight-segment', to: [intersectionPoint.twoD.x, intersectionPoint.twoD.y], @@ -2487,13 +2494,13 @@ export class SceneEntities { addingNewSegmentStatus = 'pending' if (trap(mod)) return - await kclManager.executeAstMock(mod.modifiedAst) + await this.kclManager.executeAstMock(mod.modifiedAst) this.tearDownSketch({ removeAxis: false }) // eslint-disable-next-line @typescript-eslint/no-floating-promises this.setupSketch({ sketchEntryNodePath: pathToNode, sketchNodePaths, - maybeModdedAst: kclManager.ast, + maybeModdedAst: this.kclManager.ast, up, forward, position, @@ -2526,13 +2533,13 @@ export class SceneEntities { onMove: () => {}, onClick: (args) => { // If there is a valid camera interaction that matches, do that instead - const interaction = sceneInfra.camControls.getInteractionType( + const interaction = this.sceneInfra.camControls.getInteractionType( args.mouseEvent ) if (interaction !== 'none') return if (args?.mouseEvent.which !== 1) return if (!args || !args.selected) { - sceneInfra.modelingSend({ + this.sceneInfra.modelingSend({ type: 'Set selection', data: { selectionType: 'singleCodeCursor', @@ -2543,7 +2550,7 @@ export class SceneEntities { const { selected } = args const event = getEventForSegmentSelection(selected) if (!event) return - sceneInfra.modelingSend(event) + this.sceneInfra.modelingSend(event) }, ...this.mouseEnterLeaveCallbacks(), }) @@ -2555,8 +2562,8 @@ export class SceneEntities { ) => prepareTruncatedAst( sketchNodePaths, - ast || kclManager.ast, - kclManager.lastSuccessfulVariables, + ast || this.kclManager.ast, + this.kclManager.lastSuccessfulVariables, draftSegment ) getSnappedDragPoint({ @@ -2690,7 +2697,9 @@ export class SceneEntities { intersects, intersection2d, }).snappedPoint - let modifiedAst = draftInfo ? draftInfo.truncatedAst : { ...kclManager.ast } + let modifiedAst = draftInfo + ? draftInfo.truncatedAst + : { ...this.kclManager.ast } const nodePathWithCorrectedIndexForTruncatedAst = structuredClone(pathToNode) @@ -2862,12 +2871,12 @@ export class SceneEntities { to: dragTo, from, }, - variables: kclManager.variables, + variables: this.kclManager.variables, }) } else { modded = changeSketchArguments( modifiedAst, - kclManager.variables, + this.kclManager.variables, { type: 'sourceRange', sourceRange: topLevelRange(node.start, node.end), @@ -2889,16 +2898,17 @@ export class SceneEntities { if (!draftInfo) // don't want to mod the user's code yet as they have't committed to the change yet // plus this would be the truncated ast being recast, it would be wrong - codeManager.updateCodeEditor(code) + this.codeManager.updateCodeEditor(code) const { execState } = await executeAstMock({ ast: truncatedAst, - rustContext, + rustContext: this.rustContext, }) const variables = execState.variables const sketchesInfo = getSketchesInfo({ sketchNodePaths, ast: truncatedAst, variables, + kclManager: this.kclManager, }) const callBacks: (() => SegmentOverlayPayload | null)[] = [] for (const sketchInfo of sketchesInfo) { @@ -2908,7 +2918,7 @@ export class SceneEntities { if (!sketch) return const sgPaths = sketch.paths - const orthoFactor = orthoScale(sceneInfra.camControls.camera) + const orthoFactor = orthoScale(this.sceneInfra.camControls.camera) this.updateSegment( sketch.start, @@ -2932,7 +2942,7 @@ export class SceneEntities { ) ) } - sceneInfra.overlayCallbacks(callBacks) + this.sceneInfra.overlayCallbacks(callBacks) })().catch(reportRejection) } @@ -2968,10 +2978,10 @@ export class SceneEntities { this.activeSegments[originalPathToNodeStr] const type = group?.userData?.type const factor = - (sceneInfra.camControls.camera instanceof OrthographicCamera + (this.sceneInfra.camControls.camera instanceof OrthographicCamera ? orthoFactor - : perspScale(sceneInfra.camControls.camera, group)) / - sceneInfra._baseUnitMultiplier + : perspScale(this.sceneInfra.camControls.camera, group)) / + this.sceneInfra._baseUnitMultiplier let input: SegmentInputs = { type: 'straight-segment', from: segment.from, @@ -3043,7 +3053,7 @@ export class SceneEntities { group, scale: factor, prevSegment: sgPaths[index - 1], - sceneInfra, + sceneInfra: this.sceneInfra, }) if (callBack && !err(callBack)) return callBack @@ -3074,18 +3084,19 @@ export class SceneEntities { }) } removeSketchGrid() { - if (this.axisGroup) sceneInfra.scene.remove(this.axisGroup) + if (this.axisGroup) this.sceneInfra.scene.remove(this.axisGroup) } tearDownSketch({ removeAxis = true }: { removeAxis?: boolean }) { // Remove all draft groups this.draftPointGroups.forEach((draftPointGroup) => { - sceneInfra.scene.remove(draftPointGroup) + this.sceneInfra.scene.remove(draftPointGroup) }) // Remove all sketch tools - if (this.axisGroup && removeAxis) sceneInfra.scene.remove(this.axisGroup) - const sketchSegments = sceneInfra.scene.children.find( + if (this.axisGroup && removeAxis) + this.sceneInfra.scene.remove(this.axisGroup) + const sketchSegments = this.sceneInfra.scene.children.find( ({ userData }) => userData?.type === SKETCH_GROUP_SEGMENTS ) if (sketchSegments) { @@ -3097,9 +3108,9 @@ export class SceneEntities { object.remove() } }) - sceneInfra.scene.remove(sketchSegments) + this.sceneInfra.scene.remove(sketchSegments) } - sceneInfra.camControls.enableRotate = true + this.sceneInfra.camControls.enableRotate = true this.activeSegments = {} } mouseEnterLeaveCallbacks() { @@ -3116,7 +3127,7 @@ export class SceneEntities { SEGMENT_BODIES_PLUS_PROFILE_START ) if (parent?.userData?.pathToNode) { - const pResult = parse(recast(kclManager.ast)) + const pResult = parse(recast(this.kclManager.ast)) if (trap(pResult) || !resultIsOk(pResult)) return Promise.reject(pResult) const updatedAst = pResult.program @@ -3128,7 +3139,9 @@ export class SceneEntities { ]) if (trap(_node, { suppress: true })) return const node = _node.node - editorManager.setHighlightRange([topLevelRange(node.start, node.end)]) + this.editorManager.setHighlightRange([ + topLevelRange(node.start, node.end), + ]) const yellow = 0xffff00 colorSegment(selected, yellow) const extraSegmentGroup = parent.getObjectByName(EXTRA_SEGMENT_HANDLE) @@ -3139,7 +3152,7 @@ export class SceneEntities { } }) } - const orthoFactor = orthoScale(sceneInfra.camControls.camera) + const orthoFactor = orthoScale(this.sceneInfra.camControls.camera) let input: SegmentInputs = { type: 'straight-segment', @@ -3147,10 +3160,10 @@ export class SceneEntities { to: parent.userData.to, } const factor = - (sceneInfra.camControls.camera instanceof OrthographicCamera + (this.sceneInfra.camControls.camera instanceof OrthographicCamera ? orthoFactor - : perspScale(sceneInfra.camControls.camera, parent)) / - sceneInfra._baseUnitMultiplier + : perspScale(this.sceneInfra.camControls.camera, parent)) / + this.sceneInfra._baseUnitMultiplier let update: SegmentUtils['update'] | null = null if (parent.name === STRAIGHT_SEGMENT) { update = segmentUtils.straight.update @@ -3203,20 +3216,20 @@ export class SceneEntities { input, group: parent, scale: factor, - sceneInfra, + sceneInfra: this.sceneInfra, }) return } - editorManager.setHighlightRange([defaultSourceRange()]) + this.editorManager.setHighlightRange([defaultSourceRange()]) }, onMouseLeave: ({ selected, ...rest }: OnMouseEnterLeaveArgs) => { - editorManager.setHighlightRange([defaultSourceRange()]) + this.editorManager.setHighlightRange([defaultSourceRange()]) const parent = getParentGroup( selected, SEGMENT_BODIES_PLUS_PROFILE_START ) if (parent) { - const orthoFactor = orthoScale(sceneInfra.camControls.camera) + const orthoFactor = orthoScale(this.sceneInfra.camControls.camera) let input: SegmentInputs = { type: 'straight-segment', @@ -3224,10 +3237,10 @@ export class SceneEntities { to: parent.userData.to, } const factor = - (sceneInfra.camControls.camera instanceof OrthographicCamera + (this.sceneInfra.camControls.camera instanceof OrthographicCamera ? orthoFactor - : perspScale(sceneInfra.camControls.camera, parent)) / - sceneInfra._baseUnitMultiplier + : perspScale(this.sceneInfra.camControls.camera, parent)) / + this.sceneInfra._baseUnitMultiplier let update: SegmentUtils['update'] | null = null if (parent.name === STRAIGHT_SEGMENT) { update = segmentUtils.straight.update @@ -3280,7 +3293,7 @@ export class SceneEntities { input, group: parent, scale: factor, - sceneInfra, + sceneInfra: this.sceneInfra, }) } const isSelected = parent?.userData?.isSelected @@ -3289,7 +3302,7 @@ export class SceneEntities { isSelected ? 0x0000ff : parent?.userData?.baseColor || - getThemeColorForThreeJs(sceneInfra._theme) + getThemeColorForThreeJs(this.sceneInfra._theme) ) const extraSegmentGroup = parent?.getObjectByName(EXTRA_SEGMENT_HANDLE) if (extraSegmentGroup) { @@ -3309,7 +3322,7 @@ export class SceneEntities { } } resetOverlays() { - sceneInfra.modelingSend({ + this.sceneInfra.modelingSend({ type: 'Set Segment Overlays', data: { type: 'clear', @@ -3317,8 +3330,90 @@ export class SceneEntities { }) } + async getSketchOrientationDetails(sketch: Sketch): Promise<{ + quat: Quaternion + sketchDetails: Omit< + SketchDetails & { faceId?: string }, + 'sketchNodePaths' | 'sketchEntryNodePath' | 'planeNodePath' + > + }> { + if (sketch.on.type === 'plane') { + const zAxis = sketch?.on.zAxis + return { + quat: getQuaternionFromZAxis(massageFormats(zAxis)), + sketchDetails: { + zAxis: [zAxis.x, zAxis.y, zAxis.z], + yAxis: [sketch.on.yAxis.x, sketch.on.yAxis.y, sketch.on.yAxis.z], + origin: [0, 0, 0], + faceId: sketch.on.id, + }, + } + } + const faceInfo = await this.getFaceDetails(sketch.on.id) + + if (!faceInfo?.origin || !faceInfo?.z_axis || !faceInfo?.y_axis) + return Promise.reject('face info') + const { z_axis, y_axis, origin } = faceInfo + const quaternion = quaternionFromUpNForward( + new Vector3(y_axis.x, y_axis.y, y_axis.z), + new Vector3(z_axis.x, z_axis.y, z_axis.z) + ) + return { + quat: quaternion, + sketchDetails: { + zAxis: [z_axis.x, z_axis.y, z_axis.z], + yAxis: [y_axis.x, y_axis.y, y_axis.z], + origin: [origin.x, origin.y, origin.z], + faceId: sketch.on.id, + }, + } + } + + /** + * Retrieves orientation details for a given entity representing a face (brep face or default plane). + * This function asynchronously fetches and returns the origin, x-axis, y-axis, and z-axis details + * for a specified entity ID. It is primarily used to obtain the orientation of a face in the scene, + * which is essential for calculating the correct positioning and alignment of the client side sketch. + * + * @param entityId - The ID of the entity for which orientation details are being fetched. + * @returns A promise that resolves with the orientation details of the face. + */ + async getFaceDetails( + entityId: string + ): Promise { + // TODO mode engine connection to allow batching returns and batch the following + await this.engineCommandManager.sendSceneCommand({ + type: 'modeling_cmd_req', + cmd_id: uuidv4(), + cmd: { + type: 'enable_sketch_mode', + adjust_camera: false, + animated: false, + ortho: false, + entity_id: entityId, + }, + }) + const resp = await this.engineCommandManager.sendSceneCommand({ + type: 'modeling_cmd_req', + cmd_id: uuidv4(), + cmd: { type: 'get_sketch_mode_plane' }, + }) + const faceInfo = + resp?.success && + resp?.resp.type === 'modeling' && + resp?.resp?.data?.modeling_response?.type === 'get_sketch_mode_plane' + ? resp?.resp?.data?.modeling_response.data + : ({} as Models['GetSketchModePlane_type']) + await this.engineCommandManager.sendSceneCommand({ + type: 'modeling_cmd_req', + cmd_id: uuidv4(), + cmd: { type: 'sketch_mode_disable' }, + }) + return faceInfo + } + drawDashedLine({ from, to }: { from: Coords2d; to: Coords2d }) { - const baseColor = getThemeColorForThreeJs(sceneInfra._theme) + const baseColor = getThemeColorForThreeJs(this.sceneInfra._theme) const color = baseColor const meshType = STRAIGHT_SEGMENT_DASH @@ -3359,10 +3454,10 @@ export class SceneEntities { group: segmentGroup, updater: (group: Group, to: Coords2d, orthoFactor: number) => { const scale = - (sceneInfra.camControls.camera instanceof OrthographicCamera + (this.sceneInfra.camControls.camera instanceof OrthographicCamera ? orthoFactor - : perspScale(sceneInfra.camControls.camera, group)) / - sceneInfra._baseUnitMultiplier + : perspScale(this.sceneInfra.camControls.camera, group)) / + this.sceneInfra._baseUnitMultiplier const from = group.userData.from const shape = new Shape() @@ -3478,26 +3573,16 @@ function prepareTruncatedAst( } } -export function getParentGroup( - object: any, - stopAt: string[] = SEGMENT_BODIES -): Group | null { - if (stopAt.includes(object?.userData?.type)) { - return object - } else if (object?.parent) { - return getParentGroup(object.parent, stopAt) - } - return null -} - function sketchFromPathToNode({ pathToNode, ast, variables, + kclManager, }: { pathToNode: PathToNode ast: Program variables: VariableMap + kclManager: KclManager }): Sketch | null | Error { const _varDec = getNodeFromPath( kclManager.ast, @@ -3540,12 +3625,14 @@ function colorSegment(object: any, color: number) { export function getSketchQuaternion( sketchPathToNode: PathToNode, - sketchNormalBackUp: [number, number, number] | null + sketchNormalBackUp: [number, number, number] | null, + kclManager: KclManager ): Quaternion | Error { const sketch = sketchFromPathToNode({ pathToNode: sketchPathToNode, ast: kclManager.ast, variables: kclManager.variables, + kclManager, }) if (err(sketch)) return sketch const zAxis = sketch?.on.zAxis || sketchNormalBackUp @@ -3553,87 +3640,6 @@ export function getSketchQuaternion( return getQuaternionFromZAxis(massageFormats(zAxis)) } -export async function getSketchOrientationDetails(sketch: Sketch): Promise<{ - quat: Quaternion - sketchDetails: Omit< - SketchDetails & { faceId?: string }, - 'sketchNodePaths' | 'sketchEntryNodePath' | 'planeNodePath' - > -}> { - if (sketch.on.type === 'plane') { - const zAxis = sketch?.on.zAxis - return { - quat: getQuaternionFromZAxis(massageFormats(zAxis)), - sketchDetails: { - zAxis: [zAxis.x, zAxis.y, zAxis.z], - yAxis: [sketch.on.yAxis.x, sketch.on.yAxis.y, sketch.on.yAxis.z], - origin: [0, 0, 0], - faceId: sketch.on.id, - }, - } - } - const faceInfo = await getFaceDetails(sketch.on.id) - - if (!faceInfo?.origin || !faceInfo?.z_axis || !faceInfo?.y_axis) - return Promise.reject('face info') - const { z_axis, y_axis, origin } = faceInfo - const quaternion = quaternionFromUpNForward( - new Vector3(y_axis.x, y_axis.y, y_axis.z), - new Vector3(z_axis.x, z_axis.y, z_axis.z) - ) - return { - quat: quaternion, - sketchDetails: { - zAxis: [z_axis.x, z_axis.y, z_axis.z], - yAxis: [y_axis.x, y_axis.y, y_axis.z], - origin: [origin.x, origin.y, origin.z], - faceId: sketch.on.id, - }, - } -} - -/** - * Retrieves orientation details for a given entity representing a face (brep face or default plane). - * This function asynchronously fetches and returns the origin, x-axis, y-axis, and z-axis details - * for a specified entity ID. It is primarily used to obtain the orientation of a face in the scene, - * which is essential for calculating the correct positioning and alignment of the client side sketch. - * - * @param entityId - The ID of the entity for which orientation details are being fetched. - * @returns A promise that resolves with the orientation details of the face. - */ -export async function getFaceDetails( - entityId: string -): Promise { - // TODO mode engine connection to allow batching returns and batch the following - await engineCommandManager.sendSceneCommand({ - type: 'modeling_cmd_req', - cmd_id: uuidv4(), - cmd: { - type: 'enable_sketch_mode', - adjust_camera: false, - animated: false, - ortho: false, - entity_id: entityId, - }, - }) - const resp = await engineCommandManager.sendSceneCommand({ - type: 'modeling_cmd_req', - cmd_id: uuidv4(), - cmd: { type: 'get_sketch_mode_plane' }, - }) - const faceInfo = - resp?.success && - resp?.resp.type === 'modeling' && - resp?.resp?.data?.modeling_response?.type === 'get_sketch_mode_plane' - ? resp?.resp?.data?.modeling_response.data - : ({} as Models['GetSketchModePlane_type']) - await engineCommandManager.sendSceneCommand({ - type: 'modeling_cmd_req', - cmd_id: uuidv4(), - cmd: { type: 'sketch_mode_disable' }, - }) - return faceInfo -} export function getQuaternionFromZAxis(zAxis: Vector3): Quaternion { const dummyCam = new PerspectiveCamera() @@ -3663,10 +3669,12 @@ function getSketchesInfo({ sketchNodePaths, ast, variables, + kclManager, }: { sketchNodePaths: PathToNode[] ast: Node variables: VariableMap + kclManager: KclManager }): { sketch: Sketch pathToNode: PathToNode @@ -3680,6 +3688,7 @@ function getSketchesInfo({ pathToNode: path, ast, variables, + kclManager, }) if (err(sketch)) continue if (!sketch) continue @@ -3698,7 +3707,8 @@ function getSketchesInfo({ */ function computeSelectionFromSourceRangeAndAST( sourceRange: SourceRange, - ast: Node + ast: Node, + kclManager: KclManager ): Selections { const artifactGraph = kclManager.artifactGraph const artifact = getArtifactFromRange(sourceRange, artifactGraph) || undefined diff --git a/src/clientSideScene/sceneInfra.ts b/src/clientSideScene/sceneInfra.ts index 40d9dbbbc..34254119a 100644 --- a/src/clientSideScene/sceneInfra.ts +++ b/src/clientSideScene/sceneInfra.ts @@ -1,3 +1,13 @@ +import * as TWEEN from '@tweenjs/tween.js' +import type { + Group, + Intersection, + Mesh, + MeshBasicMaterial, + Object3D, + Object3DEventMap, + Texture, +} from 'three' import { AmbientLight, Color, @@ -5,32 +15,29 @@ import { LineBasicMaterial, OrthographicCamera, PerspectiveCamera, + Raycaster, Scene, + TextureLoader, + Vector2, Vector3, WebGLRenderer, - Raycaster, - Vector2, - Group, - MeshBasicMaterial, - Mesh, - Intersection, - Object3D, - Object3DEventMap, - TextureLoader, - Texture, } from 'three' -import { Coords2d, compareVec2Epsilon2 } from 'lang/std/sketch' -import { useModelingContext } from 'hooks/useModelingContext' -import * as TWEEN from '@tweenjs/tween.js' -import { Axis, NonCodeSelection } from 'lib/selections' -import { type BaseUnit } from 'lib/settings/settingsTypes' -import { CameraControls } from './CameraControls' -import { EngineCommandManager } from 'lang/std/engineConnection' -import { MouseState, SegmentOverlayPayload } from 'machines/modelingMachine' -import { getAngle, throttle } from 'lib/utils' -import { Themes } from 'lib/theme' import { CSS2DRenderer } from 'three/examples/jsm/renderers/CSS2DRenderer' -import { orthoScale, perspScale } from './helpers' + +import { CameraControls } from '@src/clientSideScene/CameraControls' +import { orthoScale, perspScale } from '@src/clientSideScene/helpers' +import type { useModelingContext } from '@src/hooks/useModelingContext' +import type { EngineCommandManager } from '@src/lang/std/engineConnection' +import type { Coords2d } from '@src/lang/std/sketch' +import { compareVec2Epsilon2 } from '@src/lang/std/sketch' +import type { Axis, NonCodeSelection } from '@src/lib/selections' +import { type BaseUnit } from '@src/lib/settings/settingsTypes' +import { Themes } from '@src/lib/theme' +import { getAngle, throttle } from '@src/lib/utils' +import type { + MouseState, + SegmentOverlayPayload, +} from '@src/machines/modelingMachine' type SendType = ReturnType['send'] diff --git a/src/clientSideScene/sceneUtils.ts b/src/clientSideScene/sceneUtils.ts new file mode 100644 index 000000000..e69de29bb diff --git a/src/clientSideScene/segments.ts b/src/clientSideScene/segments.ts index bcb70ba0d..f23fa0841 100644 --- a/src/clientSideScene/segments.ts +++ b/src/clientSideScene/segments.ts @@ -1,4 +1,4 @@ -import { Coords2d } from 'lang/std/sketch' +import type { NormalBufferAttributes, Texture } from 'three' import { BoxGeometry, BufferGeometry, @@ -8,25 +8,33 @@ import { EllipseCurve, ExtrudeGeometry, Group, - LineCurve3, - LineBasicMaterial, - LineDashedMaterial, Line, + LineBasicMaterial, + LineCurve3, + LineDashedMaterial, Mesh, MeshBasicMaterial, - NormalBufferAttributes, Points, PointsMaterial, Shape, SphereGeometry, - Texture, Vector2, Vector3, } from 'three' -import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js' import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer' -import { PathToNode, Sketch, getTangentialArcToInfo } from 'lang/wasm' +import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js' + +import { calculate_circle_from_3_points } from '@rust/kcl-wasm-lib/pkg/kcl_wasm_lib' + +import type { Sketch } from '@rust/kcl-lib/bindings/Sketch' import { + ARC_ANGLE_END, + ARC_ANGLE_REFERENCE_LINE, + ARC_CENTER_TO_FROM, + ARC_CENTER_TO_TO, + ARC_SEGMENT, + ARC_SEGMENT_BODY, + ARC_SEGMENT_DASH, CIRCLE_CENTER_HANDLE, CIRCLE_SEGMENT, CIRCLE_SEGMENT_BODY, @@ -49,42 +57,39 @@ import { TANGENTIAL_ARC_TO_SEGMENT, TANGENTIAL_ARC_TO_SEGMENT_BODY, TANGENTIAL_ARC_TO__SEGMENT_DASH, - ARC_SEGMENT, - ARC_SEGMENT_BODY, - ARC_SEGMENT_DASH, - ARC_ANGLE_END, - getParentGroup, - ARC_CENTER_TO_FROM, - ARC_CENTER_TO_TO, - ARC_ANGLE_REFERENCE_LINE, + THREE_POINT_ARC_HANDLE2, + THREE_POINT_ARC_HANDLE3, THREE_POINT_ARC_SEGMENT, THREE_POINT_ARC_SEGMENT_BODY, THREE_POINT_ARC_SEGMENT_DASH, - THREE_POINT_ARC_HANDLE2, - THREE_POINT_ARC_HANDLE3, -} from './sceneEntities' -import { getTangentPointFromPreviousArc } from 'lib/utils2d' + getParentGroup, +} from '@src/clientSideScene/sceneConstants' +import type { SceneInfra } from '@src/clientSideScene/sceneInfra' import { ARROWHEAD, DRAFT_POINT, - SceneInfra, SEGMENT_LENGTH_LABEL, SEGMENT_LENGTH_LABEL_OFFSET_PX, SEGMENT_LENGTH_LABEL_TEXT, -} from './sceneInfra' -import { Themes, getThemeColorForThreeJs } from 'lib/theme' -import { isClockwise, normaliseAngle, roundOff } from 'lib/utils' -import { +} from '@src/clientSideScene/sceneInfra' +import { angleLengthInfo } from '@src/components/Toolbar/angleLengthInfo' +import type { Coords2d } from '@src/lang/std/sketch' +import type { SegmentInputs } from '@src/lang/std/stdTypes' +import type { PathToNode } from '@src/lang/wasm' +import { getTangentialArcToInfo } from '@src/lang/wasm' +import type { Selections } from '@src/lib/selections' +import type { Themes } from '@src/lib/theme' +import { getThemeColorForThreeJs } from '@src/lib/theme' +import { err } from '@src/lib/trap' +import { isClockwise, normaliseAngle, roundOff } from '@src/lib/utils' +import { getTangentPointFromPreviousArc } from '@src/lib/utils2d' +import { commandBarActor } from '@src/machines/commandBarMachine' +import type { SegmentOverlay, SegmentOverlayPayload, SegmentOverlays, -} from 'machines/modelingMachine' -import { SegmentInputs } from 'lang/std/stdTypes' -import { err } from 'lib/trap' -import { sceneInfra } from 'lib/singletons' -import { Selections } from 'lib/selections' -import { calculate_circle_from_3_points } from '@rust/kcl-wasm-lib/pkg/kcl_wasm_lib' -import { commandBarActor } from 'machines/commandBarMachine' +} from '@src/machines/modelingMachine' +import toast from 'react-hot-toast' const ANGLE_INDICATOR_RADIUS = 30 // in px interface CreateSegmentArgs { @@ -205,6 +210,7 @@ class StraightSegment implements SegmentUtils { from, to, scale, + sceneInfra, }) segmentGroup.add(arrowGroup) segmentGroup.add(lengthIndicatorGroup) @@ -572,6 +578,7 @@ class CircleSegment implements SegmentUtils { from: center, to: [center[0] + radius, center[1]], scale, + sceneInfra, }) arcMesh.userData.type = meshType @@ -1002,6 +1009,7 @@ class ArcSegment implements SegmentUtils { from: center, to: from, scale, + sceneInfra, }) const grey = 0xaaaaaa @@ -1059,6 +1067,7 @@ class ArcSegment implements SegmentUtils { center[1] + Math.sin(endAngle) * radius, ], scale, + sceneInfra, }) endAngleLengthIndicator.name = 'endAngleLengthIndicator' @@ -1679,11 +1688,13 @@ function createLengthIndicator({ to, scale, length = 0.1, + sceneInfra, }: { from: Coords2d to: Coords2d scale: number length?: number + sceneInfra: SceneInfra }) { const lengthIndicatorGroup = new Group() lengthIndicatorGroup.name = SEGMENT_LENGTH_LABEL @@ -1701,6 +1712,7 @@ function createLengthIndicator({ console.error('Unable to dimension segment when clicking the label.') return } + sceneInfra.modelingSend({ type: 'Set selection', data: { @@ -1709,6 +1721,20 @@ function createLengthIndicator({ }, }) + const canConstrainLength = angleLengthInfo({ + selectionRanges: { + ...selection, + graphSelections: [selection.graphSelections[0]], + }, + angleOrLength: 'setLength', + }) + if (err(canConstrainLength) || !canConstrainLength.enabled) { + toast.error( + 'Unable to constrain the length of this segment. Check the KCL code' + ) + return + } + // Command Bar commandBarActor.send({ type: 'Find and select command', diff --git a/src/commandLineArgs.test.ts b/src/commandLineArgs.test.ts index 6fec7cdad..af383faa0 100644 --- a/src/commandLineArgs.test.ts +++ b/src/commandLineArgs.test.ts @@ -1,4 +1,4 @@ -import { getPathOrUrlFromArgs, parseCLIArgs } from 'commandLineArgs' +import { getPathOrUrlFromArgs, parseCLIArgs } from '@src/commandLineArgs' const linuxDeepLinkArgv = [ '/tmp/.mount_Zoo Movq3t0x/zoo-modeling-app', diff --git a/src/components/ActionButton.test.tsx b/src/components/ActionButton.test.tsx index fd649fa67..c900da372 100644 --- a/src/components/ActionButton.test.tsx +++ b/src/components/ActionButton.test.tsx @@ -1,6 +1,7 @@ import { render, screen } from '@testing-library/react' import { describe, expect, it } from 'vitest' -import { ActionButton } from './ActionButton' + +import { ActionButton } from '@src/components/ActionButton' describe('ActionButton tests', () => { it('ActionButton with no iconStart or iconEnd should have even left and right padding', () => { diff --git a/src/components/ActionButton.tsx b/src/components/ActionButton.tsx index 2905f8ae0..766d19d93 100644 --- a/src/components/ActionButton.tsx +++ b/src/components/ActionButton.tsx @@ -1,9 +1,12 @@ -import { ActionIcon, ActionIconProps } from './ActionIcon' -import { openExternalBrowserIfDesktop } from 'lib/openWindow' -import React, { ForwardedRef, forwardRef } from 'react' -import { PATHS } from 'lib/paths' -import { Link } from 'react-router-dom' +import type { ForwardedRef } from 'react' +import React, { forwardRef } from 'react' import type { LinkProps } from 'react-router-dom' +import { Link } from 'react-router-dom' + +import type { ActionIconProps } from '@src/components/ActionIcon' +import { ActionIcon } from '@src/components/ActionIcon' +import { openExternalBrowserIfDesktop } from '@src/lib/openWindow' +import { PATHS } from '@src/lib/paths' interface BaseActionButtonProps { iconStart?: ActionIconProps @@ -47,8 +50,8 @@ export const ActionButton = forwardRef((props: ActionButtonProps, ref) => { ? 'px-0' // No padding if both icons are present : 'pr-2' // Padding on the right if only the start icon is present : props.iconEnd - ? 'pl-2' // Padding on the left if only the end icon is present - : 'px-2' // Padding on both sides if no icons are present + ? 'pl-2' // Padding on the left if only the end icon is present + : 'px-2' // Padding on both sides if no icons are present } ${props.className ? props.className : ''}` switch (props.Element) { diff --git a/src/components/ActionButtonDropdown.tsx b/src/components/ActionButtonDropdown.tsx index 32c55db98..37e037aa1 100644 --- a/src/components/ActionButtonDropdown.tsx +++ b/src/components/ActionButtonDropdown.tsx @@ -1,7 +1,8 @@ import { Popover } from '@headlessui/react' -import { ActionButtonProps } from './ActionButton' -import { CustomIcon } from './CustomIcon' -import Tooltip from './Tooltip' + +import type { ActionButtonProps } from '@src/components/ActionButton' +import { CustomIcon } from '@src/components/CustomIcon' +import Tooltip from '@src/components/Tooltip' type ActionButtonSplitProps = ActionButtonProps & { Element: 'button' } & { name?: string diff --git a/src/components/ActionIcon.tsx b/src/components/ActionIcon.tsx index 1609360bc..0a8447240 100644 --- a/src/components/ActionIcon.tsx +++ b/src/components/ActionIcon.tsx @@ -1,9 +1,9 @@ -import { - IconDefinition, - faCircleExclamation, -} from '@fortawesome/free-solid-svg-icons' +import type { IconDefinition } from '@fortawesome/free-solid-svg-icons' +import { faCircleExclamation } from '@fortawesome/free-solid-svg-icons' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { CustomIcon, CustomIconName } from './CustomIcon' + +import type { CustomIconName } from '@src/components/CustomIcon' +import { CustomIcon } from '@src/components/CustomIcon' const iconSizes = { xs: 12, diff --git a/src/components/AppHeader.tsx b/src/components/AppHeader.tsx index 58dd37b20..81f537648 100644 --- a/src/components/AppHeader.tsx +++ b/src/components/AppHeader.tsx @@ -1,12 +1,13 @@ -import { Toolbar } from '../Toolbar' -import UserSidebarMenu from 'components/UserSidebarMenu' -import { type IndexLoaderData } from 'lib/types' -import ProjectSidebarMenu from './ProjectSidebarMenu' +import { Toolbar } from '@src/Toolbar' +import { CommandBarOpenButton } from '@src/components/CommandBarOpenButton' +import ProjectSidebarMenu from '@src/components/ProjectSidebarMenu' +import { RefreshButton } from '@src/components/RefreshButton' +import UserSidebarMenu from '@src/components/UserSidebarMenu' +import { isDesktop } from '@src/lib/isDesktop' +import { type IndexLoaderData } from '@src/lib/types' +import { useUser } from '@src/machines/appMachine' + import styles from './AppHeader.module.css' -import { RefreshButton } from 'components/RefreshButton' -import { CommandBarOpenButton } from './CommandBarOpenButton' -import { isDesktop } from 'lib/isDesktop' -import { useUser } from 'machines/appMachine' interface AppHeaderProps extends React.PropsWithChildren { showToolbar?: boolean diff --git a/src/components/AstExplorer.tsx b/src/components/AstExplorer.tsx index 5d85ab7b4..4e0fba201 100644 --- a/src/components/AstExplorer.tsx +++ b/src/components/AstExplorer.tsx @@ -1,13 +1,15 @@ -import { useModelingContext } from 'hooks/useModelingContext' -import { editorManager, kclManager } from 'lib/singletons' -import { getNodeFromPath } from 'lang/queryAst' -import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils' import { useEffect, useRef, useState } from 'react' -import { trap } from 'lib/trap' -import { codeToIdSelections } from 'lib/selections' -import { codeRefFromRange } from 'lang/std/artifactGraph' -import { defaultSourceRange, topLevelRange } from 'lang/wasm' -import { isArray } from 'lib/utils' + +import { useModelingContext } from '@src/hooks/useModelingContext' +import { getNodeFromPath } from '@src/lang/queryAst' +import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils' +import { codeRefFromRange } from '@src/lang/std/artifactGraph' +import { topLevelRange } from '@src/lang/util' +import { defaultSourceRange } from '@src/lang/wasm' +import { codeToIdSelections } from '@src/lib/selections' +import { editorManager, kclManager } from '@src/lib/singletons' +import { trap } from '@src/lib/trap' +import { isArray } from '@src/lib/utils' export function AstExplorer() { const { context } = useModelingContext() diff --git a/src/components/AvailableVarsHelpers.tsx b/src/components/AvailableVarsHelpers.tsx index 40f624d35..c880069fb 100644 --- a/src/components/AvailableVarsHelpers.tsx +++ b/src/components/AvailableVarsHelpers.tsx @@ -1,10 +1,10 @@ -import { BinaryPart } from '../lang/wasm' import { - createLocalName, createLiteral, + createLocalName, createUnaryExpression, -} from '../lang/modifyAst' -import { PrevVariable } from '../lang/queryAst' +} from '@src/lang/create' +import type { PrevVariable } from '@src/lang/queryAst' +import type { BinaryPart } from '@src/lang/wasm' export const AvailableVars = ({ onVarClick, diff --git a/src/components/CamToggle.tsx b/src/components/CamToggle.tsx index 4e1b3d48a..d80ce652f 100644 --- a/src/components/CamToggle.tsx +++ b/src/components/CamToggle.tsx @@ -1,8 +1,9 @@ -import { useState, useEffect } from 'react' -import { EngineCommandManagerEvents } from 'lang/std/engineConnection' -import { engineCommandManager, sceneInfra } from 'lib/singletons' -import { throttle, isReducedMotion } from 'lib/utils' -import { reportRejection } from 'lib/trap' +import { useEffect, useState } from 'react' + +import { EngineCommandManagerEvents } from '@src/lang/std/engineConnection' +import { engineCommandManager, sceneInfra } from '@src/lib/singletons' +import { reportRejection } from '@src/lib/trap' +import { isReducedMotion, throttle } from '@src/lib/utils' const updateDollyZoom = throttle( (newFov: number) => sceneInfra.camControls.dollyZoom(newFov), diff --git a/src/components/CameraProjectionToggle.tsx b/src/components/CameraProjectionToggle.tsx index 907351dea..314fc56f5 100644 --- a/src/components/CameraProjectionToggle.tsx +++ b/src/components/CameraProjectionToggle.tsx @@ -1,7 +1,8 @@ import { Switch } from '@headlessui/react' -import { settingsActor, useSettings } from 'machines/appMachine' import { useEffect, useState } from 'react' +import { settingsActor, useSettings } from '@src/machines/appMachine' + export function CameraProjectionToggle() { const settings = useSettings() const isCameraProjectionPerspective = diff --git a/src/components/CommandBar/CommandArgOptionInput.tsx b/src/components/CommandBar/CommandArgOptionInput.tsx index 427fcfbf6..ac4195f0f 100644 --- a/src/components/CommandBar/CommandArgOptionInput.tsx +++ b/src/components/CommandBar/CommandArgOptionInput.tsx @@ -1,10 +1,17 @@ import { Combobox } from '@headlessui/react' import { useSelector } from '@xstate/react' import Fuse from 'fuse.js' -import { CommandArgument, CommandArgumentOption } from 'lib/commandTypes' -import { commandBarActor, useCommandBarState } from 'machines/commandBarMachine' import { useEffect, useMemo, useRef, useState } from 'react' -import { AnyStateMachine, StateFrom } from 'xstate' +import type { AnyStateMachine, StateFrom } from 'xstate' + +import type { + CommandArgument, + CommandArgumentOption, +} from '@src/lib/commandTypes' +import { + commandBarActor, + useCommandBarState, +} from '@src/machines/commandBarMachine' const contextSelector = (snapshot: StateFrom | undefined) => snapshot?.context diff --git a/src/components/CommandBar/CommandBar.tsx b/src/components/CommandBar/CommandBar.tsx index 07ca73b44..30a834d49 100644 --- a/src/components/CommandBar/CommandBar.tsx +++ b/src/components/CommandBar/CommandBar.tsx @@ -1,13 +1,17 @@ import { Dialog, Popover, Transition } from '@headlessui/react' import { Fragment, useEffect } from 'react' -import CommandBarArgument from './CommandBarArgument' -import CommandComboBox from '../CommandComboBox' -import CommandBarReview from './CommandBarReview' import { useLocation } from 'react-router-dom' -import useHotkeyWrapper from 'lib/hotkeyWrapper' -import { CustomIcon } from 'components/CustomIcon' -import Tooltip from 'components/Tooltip' -import { commandBarActor, useCommandBarState } from 'machines/commandBarMachine' + +import CommandBarArgument from '@src/components/CommandBar/CommandBarArgument' +import CommandBarReview from '@src/components/CommandBar/CommandBarReview' +import CommandComboBox from '@src/components/CommandComboBox' +import { CustomIcon } from '@src/components/CustomIcon' +import Tooltip from '@src/components/Tooltip' +import useHotkeyWrapper from '@src/lib/hotkeyWrapper' +import { + commandBarActor, + useCommandBarState, +} from '@src/machines/commandBarMachine' export const COMMAND_PALETTE_HOTKEY = 'mod+k' diff --git a/src/components/CommandBar/CommandBarArgument.tsx b/src/components/CommandBar/CommandBarArgument.tsx index 93d4fa345..0418c3c95 100644 --- a/src/components/CommandBar/CommandBarArgument.tsx +++ b/src/components/CommandBar/CommandBarArgument.tsx @@ -1,12 +1,15 @@ -import CommandArgOptionInput from './CommandArgOptionInput' -import CommandBarBasicInput from './CommandBarBasicInput' -import CommandBarSelectionInput from './CommandBarSelectionInput' -import CommandBarSelectionMixedInput from './CommandBarSelectionMixedInput' -import { CommandArgument } from 'lib/commandTypes' -import CommandBarHeader from './CommandBarHeader' -import CommandBarKclInput from './CommandBarKclInput' -import CommandBarTextareaInput from './CommandBarTextareaInput' -import { commandBarActor, useCommandBarState } from 'machines/commandBarMachine' +import CommandArgOptionInput from '@src/components/CommandBar/CommandArgOptionInput' +import CommandBarBasicInput from '@src/components/CommandBar/CommandBarBasicInput' +import CommandBarHeader from '@src/components/CommandBar/CommandBarHeader' +import CommandBarKclInput from '@src/components/CommandBar/CommandBarKclInput' +import CommandBarSelectionInput from '@src/components/CommandBar/CommandBarSelectionInput' +import CommandBarSelectionMixedInput from '@src/components/CommandBar/CommandBarSelectionMixedInput' +import CommandBarTextareaInput from '@src/components/CommandBar/CommandBarTextareaInput' +import type { CommandArgument } from '@src/lib/commandTypes' +import { + commandBarActor, + useCommandBarState, +} from '@src/machines/commandBarMachine' function CommandBarArgument({ stepBack }: { stepBack: () => void }) { const commandBarState = useCommandBarState() diff --git a/src/components/CommandBar/CommandBarBasicInput.tsx b/src/components/CommandBar/CommandBarBasicInput.tsx index 7398c2a8c..b079af0d2 100644 --- a/src/components/CommandBar/CommandBarBasicInput.tsx +++ b/src/components/CommandBar/CommandBarBasicInput.tsx @@ -1,8 +1,12 @@ -import { CommandArgument } from 'lib/commandTypes' -import { commandBarActor, useCommandBarState } from 'machines/commandBarMachine' import { useEffect, useRef } from 'react' import { useHotkeys } from 'react-hotkeys-hook' +import type { CommandArgument } from '@src/lib/commandTypes' +import { + commandBarActor, + useCommandBarState, +} from '@src/machines/commandBarMachine' + function CommandBarBasicInput({ arg, stepBack, diff --git a/src/components/CommandBar/CommandBarHeader.tsx b/src/components/CommandBar/CommandBarHeader.tsx index 64bda41a4..7d72e31dd 100644 --- a/src/components/CommandBar/CommandBarHeader.tsx +++ b/src/components/CommandBar/CommandBarHeader.tsx @@ -1,12 +1,20 @@ -import { CustomIcon } from '../CustomIcon' import React, { useMemo, useState } from 'react' -import { ActionButton } from '../ActionButton' -import { Selections, getSelectionTypeDisplayText } from 'lib/selections' import { useHotkeys } from 'react-hotkeys-hook' -import { KclCommandValue, KclExpressionWithVariable } from 'lib/commandTypes' -import Tooltip from 'components/Tooltip' -import { roundOff } from 'lib/utils' -import { commandBarActor, useCommandBarState } from 'machines/commandBarMachine' + +import { ActionButton } from '@src/components/ActionButton' +import { CustomIcon } from '@src/components/CustomIcon' +import Tooltip from '@src/components/Tooltip' +import type { + KclCommandValue, + KclExpressionWithVariable, +} from '@src/lib/commandTypes' +import type { Selections } from '@src/lib/selections' +import { getSelectionTypeDisplayText } from '@src/lib/selections' +import { roundOff } from '@src/lib/utils' +import { + commandBarActor, + useCommandBarState, +} from '@src/machines/commandBarMachine' function CommandBarHeader({ children }: React.PropsWithChildren) { const commandBarState = useCommandBarState() diff --git a/src/components/CommandBar/CommandBarKclInput.tsx b/src/components/CommandBar/CommandBarKclInput.tsx index 1014e38f1..68204a2a7 100644 --- a/src/components/CommandBar/CommandBarKclInput.tsx +++ b/src/components/CommandBar/CommandBarKclInput.tsx @@ -1,33 +1,41 @@ +import type { Completion } from '@codemirror/autocomplete' import { closeBrackets, closeBracketsKeymap, - Completion, completionKeymap, completionStatus, } from '@codemirror/autocomplete' -import { EditorView, keymap, ViewUpdate } from '@codemirror/view' -import { CustomIcon } from 'components/CustomIcon' -import { CommandArgument, KclCommandValue } from 'lib/commandTypes' -import { getSystemTheme } from 'lib/theme' -import { useCalculateKclExpression } from 'lib/useCalculateKclExpression' -import { roundOff } from 'lib/utils' -import { varMentions } from 'lib/varCompletionExtension' -import { useEffect, useMemo, useRef, useState } from 'react' -import { useHotkeys } from 'react-hotkeys-hook' -import styles from './CommandBarKclInput.module.css' -import { createLocalName, createVariableDeclaration } from 'lang/modifyAst' -import { useCodeMirror } from 'components/ModelingSidebar/ModelingPanes/CodeEditor' +import type { ViewUpdate } from '@codemirror/view' +import { EditorView, keymap } from '@codemirror/view' import { useSelector } from '@xstate/react' -import { commandBarActor, useCommandBarState } from 'machines/commandBarMachine' -import { useSettings } from 'machines/appMachine' +import { useEffect, useMemo, useRef, useState } from 'react' import toast from 'react-hot-toast' -import { AnyStateMachine, SnapshotFrom } from 'xstate' -import { kclManager } from 'lib/singletons' -import { getNodeFromPath } from 'lang/queryAst' -import { isPathToNode, SourceRange, VariableDeclarator } from 'lang/wasm' -import { Node } from '@rust/kcl-lib/bindings/Node' -import { err } from 'lib/trap' -import { Spinner } from 'components/Spinner' +import { useHotkeys } from 'react-hotkeys-hook' +import type { AnyStateMachine, SnapshotFrom } from 'xstate' + +import type { Node } from '@rust/kcl-lib/bindings/Node' + +import { CustomIcon } from '@src/components/CustomIcon' +import { useCodeMirror } from '@src/components/ModelingSidebar/ModelingPanes/CodeEditor' +import { Spinner } from '@src/components/Spinner' +import { createLocalName, createVariableDeclaration } from '@src/lang/create' +import { getNodeFromPath } from '@src/lang/queryAst' +import type { SourceRange, VariableDeclarator } from '@src/lang/wasm' +import { isPathToNode } from '@src/lang/wasm' +import type { CommandArgument, KclCommandValue } from '@src/lib/commandTypes' +import { kclManager } from '@src/lib/singletons' +import { getSystemTheme } from '@src/lib/theme' +import { err } from '@src/lib/trap' +import { useCalculateKclExpression } from '@src/lib/useCalculateKclExpression' +import { roundOff } from '@src/lib/utils' +import { varMentions } from '@src/lib/varCompletionExtension' +import { useSettings } from '@src/machines/appMachine' +import { + commandBarActor, + useCommandBarState, +} from '@src/machines/commandBarMachine' + +import styles from './CommandBarKclInput.module.css' // TODO: remove the need for this selector once we decouple all actors from React const machineContextSelector = (snapshot?: SnapshotFrom) => diff --git a/src/components/CommandBar/CommandBarReview.tsx b/src/components/CommandBar/CommandBarReview.tsx index a944560ba..8eb52e9ad 100644 --- a/src/components/CommandBar/CommandBarReview.tsx +++ b/src/components/CommandBar/CommandBarReview.tsx @@ -1,7 +1,11 @@ -import { commandBarActor, useCommandBarState } from 'machines/commandBarMachine' -import CommandBarHeader from './CommandBarHeader' import { useHotkeys } from 'react-hotkeys-hook' +import CommandBarHeader from '@src/components/CommandBar/CommandBarHeader' +import { + commandBarActor, + useCommandBarState, +} from '@src/machines/commandBarMachine' + function CommandBarReview({ stepBack }: { stepBack: () => void }) { const commandBarState = useCommandBarState() const { diff --git a/src/components/CommandBar/CommandBarSelectionInput.tsx b/src/components/CommandBar/CommandBarSelectionInput.tsx index 454c22cf5..db06d6473 100644 --- a/src/components/CommandBar/CommandBarSelectionInput.tsx +++ b/src/components/CommandBar/CommandBarSelectionInput.tsx @@ -1,18 +1,22 @@ import { useSelector } from '@xstate/react' -import { Artifact } from 'lang/std/artifactGraph' -import { CommandArgument } from 'lib/commandTypes' +import { useEffect, useMemo, useRef, useState } from 'react' +import type { StateFrom } from 'xstate' + +import type { Artifact } from '@src/lang/std/artifactGraph' +import type { CommandArgument } from '@src/lib/commandTypes' import { canSubmitSelectionArg, getSelectionCountByType, getSelectionTypeDisplayText, -} from 'lib/selections' -import { engineCommandManager, kclManager } from 'lib/singletons' -import { reportRejection } from 'lib/trap' -import { toSync } from 'lib/utils' -import { commandBarActor, useCommandBarState } from 'machines/commandBarMachine' -import { modelingMachine } from 'machines/modelingMachine' -import { useEffect, useMemo, useRef, useState } from 'react' -import { StateFrom } from 'xstate' +} from '@src/lib/selections' +import { engineCommandManager, kclManager } from '@src/lib/singletons' +import { reportRejection } from '@src/lib/trap' +import { toSync } from '@src/lib/utils' +import { + commandBarActor, + useCommandBarState, +} from '@src/machines/commandBarMachine' +import type { modelingMachine } from '@src/machines/modelingMachine' const semanticEntityNames: { [key: string]: Array diff --git a/src/components/CommandBar/CommandBarSelectionMixedInput.tsx b/src/components/CommandBar/CommandBarSelectionMixedInput.tsx index fbee6d5ae..1db4dd3be 100644 --- a/src/components/CommandBar/CommandBarSelectionMixedInput.tsx +++ b/src/components/CommandBar/CommandBarSelectionMixedInput.tsx @@ -1,13 +1,17 @@ +import { useSelector } from '@xstate/react' import { useEffect, useMemo, useRef, useState } from 'react' -import { CommandArgument } from 'lib/commandTypes' + +import type { CommandArgument } from '@src/lib/commandTypes' +import type { Selections } from '@src/lib/selections' import { - Selections, canSubmitSelectionArg, getSelectionCountByType, -} from 'lib/selections' -import { useSelector } from '@xstate/react' -import { commandBarActor, useCommandBarState } from 'machines/commandBarMachine' -import { kclManager } from 'lib/singletons' +} from '@src/lib/selections' +import { kclManager } from '@src/lib/singletons' +import { + commandBarActor, + useCommandBarState, +} from '@src/machines/commandBarMachine' const selectionSelector = (snapshot: any) => snapshot?.context.selectionRanges diff --git a/src/components/CommandBar/CommandBarTextareaInput.tsx b/src/components/CommandBar/CommandBarTextareaInput.tsx index 589cf24c3..78c9cb885 100644 --- a/src/components/CommandBar/CommandBarTextareaInput.tsx +++ b/src/components/CommandBar/CommandBarTextareaInput.tsx @@ -1,8 +1,13 @@ -import { CommandArgument } from 'lib/commandTypes' -import { commandBarActor, useCommandBarState } from 'machines/commandBarMachine' -import { RefObject, useEffect, useRef } from 'react' +import type { RefObject } from 'react' +import { useEffect, useRef } from 'react' import { useHotkeys } from 'react-hotkeys-hook' +import type { CommandArgument } from '@src/lib/commandTypes' +import { + commandBarActor, + useCommandBarState, +} from '@src/machines/commandBarMachine' + function CommandBarTextareaInput({ arg, stepBack, diff --git a/src/components/CommandBarOpenButton.tsx b/src/components/CommandBarOpenButton.tsx index a0b3d466e..0d1cf26a0 100644 --- a/src/components/CommandBarOpenButton.tsx +++ b/src/components/CommandBarOpenButton.tsx @@ -1,7 +1,7 @@ -import usePlatform from 'hooks/usePlatform' -import { hotkeyDisplay } from 'lib/hotkeyWrapper' -import { COMMAND_PALETTE_HOTKEY } from './CommandBar/CommandBar' -import { commandBarActor } from 'machines/commandBarMachine' +import { COMMAND_PALETTE_HOTKEY } from '@src/components/CommandBar/CommandBar' +import usePlatform from '@src/hooks/usePlatform' +import { hotkeyDisplay } from '@src/lib/hotkeyWrapper' +import { commandBarActor } from '@src/machines/commandBarMachine' export function CommandBarOpenButton() { const platform = usePlatform() diff --git a/src/components/CommandComboBox.tsx b/src/components/CommandComboBox.tsx index c589d8a2b..983dfea61 100644 --- a/src/components/CommandComboBox.tsx +++ b/src/components/CommandComboBox.tsx @@ -1,11 +1,12 @@ import { Combobox } from '@headlessui/react' import Fuse from 'fuse.js' -import { Command } from 'lib/commandTypes' import { useEffect, useState } from 'react' -import { CustomIcon } from './CustomIcon' -import { getActorNextEvents } from 'lib/utils' -import { sortCommands } from 'lib/commandUtils' -import { commandBarActor } from 'machines/commandBarMachine' + +import { CustomIcon } from '@src/components/CustomIcon' +import type { Command } from '@src/lib/commandTypes' +import { sortCommands } from '@src/lib/commandUtils' +import { getActorNextEvents } from '@src/lib/utils' +import { commandBarActor } from '@src/machines/commandBarMachine' function CommandComboBox({ options, diff --git a/src/components/ContextMenu.tsx b/src/components/ContextMenu.tsx index b263e29c2..106567929 100644 --- a/src/components/ContextMenu.tsx +++ b/src/components/ContextMenu.tsx @@ -1,15 +1,11 @@ -import toast from 'react-hot-toast' -import { ActionIcon, ActionIconProps } from './ActionIcon' -import { - RefObject, - useCallback, - useEffect, - useMemo, - useRef, - useState, -} from 'react' -import { useHotkeys } from 'react-hotkeys-hook' import { Dialog } from '@headlessui/react' +import type { RefObject } from 'react' +import { useCallback, useEffect, useMemo, useRef, useState } from 'react' +import toast from 'react-hot-toast' +import { useHotkeys } from 'react-hotkeys-hook' + +import type { ActionIconProps } from '@src/components/ActionIcon' +import { ActionIcon } from '@src/components/ActionIcon' export interface ContextMenuProps extends Omit, 'children'> { diff --git a/src/components/DebugArtifactGraph.tsx b/src/components/DebugArtifactGraph.tsx index 5436ab237..32a432334 100644 --- a/src/components/DebugArtifactGraph.tsx +++ b/src/components/DebugArtifactGraph.tsx @@ -1,8 +1,11 @@ import { useMemo } from 'react' -import { kclManager } from 'lib/singletons' -import { expandPlane, PlaneArtifactRich } from 'lang/std/artifactGraph' -import { ArtifactGraph } from 'lang/wasm' -import { DebugDisplayArray, GenericObj } from './DebugDisplayObj' + +import type { GenericObj } from '@src/components/DebugDisplayObj' +import { DebugDisplayArray } from '@src/components/DebugDisplayObj' +import type { PlaneArtifactRich } from '@src/lang/std/artifactGraph' +import { expandPlane } from '@src/lang/std/artifactGraph' +import type { ArtifactGraph } from '@src/lang/wasm' +import { kclManager } from '@src/lib/singletons' export function DebugArtifactGraph() { const artifactGraphTree = useMemo(() => { diff --git a/src/components/DebugDisplayObj.tsx b/src/components/DebugDisplayObj.tsx index 55b6ec244..ee3c54ef6 100644 --- a/src/components/DebugDisplayObj.tsx +++ b/src/components/DebugDisplayObj.tsx @@ -1,6 +1,7 @@ -import { isArray, isNonNullable } from 'lib/utils' import { useRef, useState } from 'react' +import { isArray, isNonNullable } from '@src/lib/utils' + type Primitive = string | number | bigint | boolean | symbol | null | undefined export type GenericObj = { diff --git a/src/components/DownloadAppBanner.tsx b/src/components/DownloadAppBanner.tsx index cbc278d75..702cee99b 100644 --- a/src/components/DownloadAppBanner.tsx +++ b/src/components/DownloadAppBanner.tsx @@ -1,7 +1,8 @@ import { Dialog } from '@headlessui/react' -import { ActionButton } from './ActionButton' import { useState } from 'react' -import { useSettings } from 'machines/appMachine' + +import { ActionButton } from '@src/components/ActionButton' +import { useSettings } from '@src/machines/appMachine' const DownloadAppBanner = () => { const settings = useSettings() diff --git a/src/components/DragWarningToast.tsx b/src/components/DragWarningToast.tsx index 1f4c2a98d..a7d72abcd 100644 --- a/src/components/DragWarningToast.tsx +++ b/src/components/DragWarningToast.tsx @@ -1,4 +1,4 @@ -import { MoveDesc } from 'machines/modelingMachine' +import type { MoveDesc } from '@src/machines/modelingMachine' export const DragWarningToast = (moveDescs: MoveDesc[]) => { if (moveDescs.length === 1) { diff --git a/src/components/EngineCommands.tsx b/src/components/EngineCommands.tsx index ace639518..6bf3cfd20 100644 --- a/src/components/EngineCommands.tsx +++ b/src/components/EngineCommands.tsx @@ -1,7 +1,8 @@ -import { CommandLog } from 'lang/std/engineConnection' -import { engineCommandManager } from 'lib/singletons' -import { reportRejection } from 'lib/trap' -import { useState, useEffect } from 'react' +import { useEffect, useState } from 'react' + +import type { CommandLog } from '@src/lang/std/engineConnection' +import { engineCommandManager } from '@src/lib/singletons' +import { reportRejection } from '@src/lib/trap' export function useEngineCommands(): [CommandLog[], () => void] { const [engineCommands, setEngineCommands] = useState( diff --git a/src/components/ErrorPage.tsx b/src/components/ErrorPage.tsx index 0705074be..73467e226 100644 --- a/src/components/ErrorPage.tsx +++ b/src/components/ErrorPage.tsx @@ -1,12 +1,13 @@ -import { isDesktop } from 'lib/isDesktop' -import { useRouteError, isRouteErrorResponse } from 'react-router-dom' -import { ActionButton } from './ActionButton' import { faBug, faHome, faRefresh, faTrash, } from '@fortawesome/free-solid-svg-icons' +import { isRouteErrorResponse, useRouteError } from 'react-router-dom' + +import { ActionButton } from '@src/components/ActionButton' +import { isDesktop } from '@src/lib/isDesktop' /** Type narrowing function of unknown error to a string */ function errorMessage(error: unknown): string { diff --git a/src/components/FileMachineProvider.tsx b/src/components/FileMachineProvider.tsx index dcfd2fb6a..d65cb5145 100644 --- a/src/components/FileMachineProvider.tsx +++ b/src/components/FileMachineProvider.tsx @@ -1,41 +1,39 @@ import { useMachine } from '@xstate/react' -import { useLocation, useNavigate, useRouteLoaderData } from 'react-router-dom' -import { type IndexLoaderData } from 'lib/types' -import { BROWSER_PATH, PATHS } from 'lib/paths' import React, { createContext, useEffect, useMemo } from 'react' import { toast } from 'react-hot-toast' -import { +import { useLocation, useNavigate, useRouteLoaderData } from 'react-router-dom' +import type { Actor, AnyStateMachine, ContextFrom, Prop, StateFrom, - fromPromise, } from 'xstate' -import { fileMachine } from 'machines/fileMachine' -import { isDesktop } from 'lib/isDesktop' +import { fromPromise } from 'xstate' + +import { newKclFile } from '@src/lang/project' +import { createNamedViewsCommand } from '@src/lib/commandBarConfigs/namedViewsConfig' +import { createRouteCommands } from '@src/lib/commandBarConfigs/routeCommandConfig' import { DEFAULT_DEFAULT_LENGTH_UNIT, DEFAULT_FILE_NAME, DEFAULT_PROJECT_KCL_FILE, FILE_EXT, -} from 'lib/constants' -import { getProjectInfo } from 'lib/desktop' -import { getNextDirName, getNextFileName } from 'lib/desktopFS' -import { kclCommands } from 'lib/kclCommands' -import { codeManager, kclManager } from 'lib/singletons' -import { - getKclSamplesManifest, - KclSamplesManifestItem, -} from 'lib/getKclSamplesManifest' -import { markOnce } from 'lib/performance' -import { commandBarActor } from 'machines/commandBarMachine' -import { useSettings } from 'machines/appMachine' -import { createRouteCommands } from 'lib/commandBarConfigs/routeCommandConfig' -import { useToken } from 'machines/appMachine' -import { createNamedViewsCommand } from 'lib/commandBarConfigs/namedViewsConfig' -import { err, reportRejection } from 'lib/trap' -import { newKclFile } from 'lang/project' +} from '@src/lib/constants' +import { getProjectInfo } from '@src/lib/desktop' +import { getNextDirName, getNextFileName } from '@src/lib/desktopFS' +import type { KclSamplesManifestItem } from '@src/lib/getKclSamplesManifest' +import { getKclSamplesManifest } from '@src/lib/getKclSamplesManifest' +import { isDesktop } from '@src/lib/isDesktop' +import { kclCommands } from '@src/lib/kclCommands' +import { BROWSER_PATH, PATHS } from '@src/lib/paths' +import { markOnce } from '@src/lib/performance' +import { codeManager, kclManager } from '@src/lib/singletons' +import { err, reportRejection } from '@src/lib/trap' +import { type IndexLoaderData } from '@src/lib/types' +import { useSettings, useToken } from '@src/machines/appMachine' +import { commandBarActor } from '@src/machines/commandBarMachine' +import { fileMachine } from '@src/machines/fileMachine' type MachineContext = { state: StateFrom diff --git a/src/components/FileTree.tsx b/src/components/FileTree.tsx index 27d5f5f21..35ee4b7d9 100644 --- a/src/components/FileTree.tsx +++ b/src/components/FileTree.tsx @@ -1,30 +1,34 @@ -import type { IndexLoaderData } from 'lib/types' -import { PATHS } from 'lib/paths' -import { ActionButton } from './ActionButton' -import Tooltip from './Tooltip' -import { Dispatch, useCallback, useRef, useState } from 'react' -import { useNavigate, useRouteLoaderData } from 'react-router-dom' -import { Disclosure } from '@headlessui/react' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faChevronRight, faPencil } from '@fortawesome/free-solid-svg-icons' -import { useFileContext } from 'hooks/useFileContext' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { Disclosure } from '@headlessui/react' +import type { Dispatch } from 'react' +import { useCallback, useRef, useState } from 'react' +import { useNavigate, useRouteLoaderData } from 'react-router-dom' + +import { ActionButton } from '@src/components/ActionButton' +import { ContextMenu, ContextMenuItem } from '@src/components/ContextMenu' +import { CustomIcon } from '@src/components/CustomIcon' +import { useLspContext } from '@src/components/LspProvider' +import { DeleteConfirmationDialog } from '@src/components/ProjectCard/DeleteProjectDialog' +import Tooltip from '@src/components/Tooltip' +import { useFileContext } from '@src/hooks/useFileContext' +import { useFileSystemWatcher } from '@src/hooks/useFileSystemWatcher' +import { useModelingContext } from '@src/hooks/useModelingContext' +import usePlatform from '@src/hooks/usePlatform' +import { useKclContext } from '@src/lang/KclProvider' +import type { KCLError } from '@src/lang/errors' +import { kclErrorsByFilename } from '@src/lang/errors' +import { normalizeLineEndings } from '@src/lib/codeEditor' +import { FILE_EXT } from '@src/lib/constants' +import { sortFilesAndDirectories } from '@src/lib/desktopFS' +import useHotkeyWrapper from '@src/lib/hotkeyWrapper' +import { PATHS } from '@src/lib/paths' +import type { FileEntry } from '@src/lib/project' +import { codeManager, kclManager } from '@src/lib/singletons' +import { reportRejection } from '@src/lib/trap' +import type { IndexLoaderData } from '@src/lib/types' + import styles from './FileTree.module.css' -import { sortFilesAndDirectories } from 'lib/desktopFS' -import { FILE_EXT } from 'lib/constants' -import { CustomIcon } from './CustomIcon' -import { codeManager, kclManager } from 'lib/singletons' -import { useLspContext } from './LspProvider' -import useHotkeyWrapper from 'lib/hotkeyWrapper' -import { useModelingContext } from 'hooks/useModelingContext' -import { DeleteConfirmationDialog } from './ProjectCard/DeleteProjectDialog' -import { ContextMenu, ContextMenuItem } from './ContextMenu' -import usePlatform from 'hooks/usePlatform' -import { FileEntry } from 'lib/project' -import { useFileSystemWatcher } from 'hooks/useFileSystemWatcher' -import { normalizeLineEndings } from 'lib/codeEditor' -import { reportRejection } from 'lib/trap' -import { useKclContext } from 'lang/KclProvider' -import { kclErrorsByFilename, KCLError } from 'lang/errors' function getIndentationCSS(level: number) { return `calc(1rem * ${level + 1})` diff --git a/src/components/Gizmo.tsx b/src/components/Gizmo.tsx index 06c681bb6..b05dddd88 100644 --- a/src/components/Gizmo.tsx +++ b/src/components/Gizmo.tsx @@ -1,34 +1,33 @@ -import { SceneInfra } from 'clientSideScene/sceneInfra' -import { sceneInfra } from 'lib/singletons' -import { MutableRefObject, useEffect, useRef } from 'react' +import { Popover } from '@headlessui/react' +import type { MutableRefObject } from 'react' +import { useEffect, useRef } from 'react' +import type { Camera, ColorRepresentation, Intersection, Object3D } from 'three' import { - WebGLRenderer, - Scene, - OrthographicCamera, BoxGeometry, - SphereGeometry, - MeshBasicMaterial, + Clock, Color, Mesh, - Clock, + MeshBasicMaterial, + OrthographicCamera, Quaternion, - ColorRepresentation, - Vector2, Raycaster, - Camera, - Intersection, - Object3D, + Scene, + SphereGeometry, + Vector2, + WebGLRenderer, } from 'three' -import { Popover } from '@headlessui/react' -import { CustomIcon } from './CustomIcon' -import { reportRejection } from 'lib/trap' + +import type { SceneInfra } from '@src/clientSideScene/sceneInfra' +import { CustomIcon } from '@src/components/CustomIcon' import { - useViewControlMenuItems, ViewControlContextMenu, -} from './ViewControlMenu' -import { AxisNames } from 'lib/constants' -import { useModelingContext } from 'hooks/useModelingContext' -import { useSettings } from 'machines/appMachine' + useViewControlMenuItems, +} from '@src/components/ViewControlMenu' +import { useModelingContext } from '@src/hooks/useModelingContext' +import { AxisNames } from '@src/lib/constants' +import { sceneInfra } from '@src/lib/singletons' +import { reportRejection } from '@src/lib/trap' +import { useSettings } from '@src/machines/appMachine' const CANVAS_SIZE = 80 const FRUSTUM_SIZE = 0.5 diff --git a/src/components/HelpMenu.tsx b/src/components/HelpMenu.tsx index 56a186f22..64c83063d 100644 --- a/src/components/HelpMenu.tsx +++ b/src/components/HelpMenu.tsx @@ -1,16 +1,17 @@ import { Popover } from '@headlessui/react' -import Tooltip from './Tooltip' -import { CustomIcon } from './CustomIcon' import { useLocation, useNavigate } from 'react-router-dom' -import { PATHS } from 'lib/paths' -import { createAndOpenNewTutorialProject } from 'lib/desktopFS' -import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath' -import { useLspContext } from './LspProvider' -import { openExternalBrowserIfDesktop } from 'lib/openWindow' -import { reportRejection } from 'lib/trap' -import { settingsActor } from 'machines/appMachine' -import type { WebContentSendPayload } from '../menu/channels' -import { useMenuListener } from 'hooks/useMenu' + +import { CustomIcon } from '@src/components/CustomIcon' +import { useLspContext } from '@src/components/LspProvider' +import Tooltip from '@src/components/Tooltip' +import { useAbsoluteFilePath } from '@src/hooks/useAbsoluteFilePath' +import { useMenuListener } from '@src/hooks/useMenu' +import { createAndOpenNewTutorialProject } from '@src/lib/desktopFS' +import { openExternalBrowserIfDesktop } from '@src/lib/openWindow' +import { PATHS } from '@src/lib/paths' +import { reportRejection } from '@src/lib/trap' +import { settingsActor } from '@src/machines/appMachine' +import type { WebContentSendPayload } from '@src/menu/channels' const HelpMenuDivider = () => (
diff --git a/src/components/Loading.tsx b/src/components/Loading.tsx index 7d2972340..b9950069f 100644 --- a/src/components/Loading.tsx +++ b/src/components/Loading.tsx @@ -1,17 +1,15 @@ import { useEffect, useState } from 'react' +import { Spinner } from '@src/components/Spinner' import { - EngineConnectionStateType, + CONNECTION_ERROR_TEXT, + ConnectionError, DisconnectingType, EngineCommandManagerEvents, EngineConnectionEvents, - ConnectionError, - CONNECTION_ERROR_TEXT, -} from '../lang/std/engineConnection' - -import { engineCommandManager } from '../lib/singletons' - -import { Spinner } from './Spinner' + EngineConnectionStateType, +} from '@src/lang/std/engineConnection' +import { engineCommandManager } from '@src/lib/singletons' interface LoadingProps extends React.PropsWithChildren { className?: string diff --git a/src/components/LowerRightControls.tsx b/src/components/LowerRightControls.tsx index ac942204c..5666422aa 100644 --- a/src/components/LowerRightControls.tsx +++ b/src/components/LowerRightControls.tsx @@ -1,18 +1,19 @@ -import { APP_VERSION, getReleaseUrl } from 'routes/Settings' -import { CustomIcon } from 'components/CustomIcon' -import Tooltip from 'components/Tooltip' -import { PATHS } from 'lib/paths' -import { NetworkHealthIndicator } from 'components/NetworkHealthIndicator' -import { HelpMenu } from './HelpMenu' -import { Link, useLocation } from 'react-router-dom' -import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath' -import { coreDump } from 'lang/wasm' import toast from 'react-hot-toast' -import { CoreDumpManager } from 'lib/coredump' -import openWindow, { openExternalBrowserIfDesktop } from 'lib/openWindow' -import { NetworkMachineIndicator } from './NetworkMachineIndicator' -import { ModelStateIndicator } from './ModelStateIndicator' -import { reportRejection } from 'lib/trap' +import { Link, useLocation } from 'react-router-dom' + +import { CustomIcon } from '@src/components/CustomIcon' +import { HelpMenu } from '@src/components/HelpMenu' +import { ModelStateIndicator } from '@src/components/ModelStateIndicator' +import { NetworkHealthIndicator } from '@src/components/NetworkHealthIndicator' +import { NetworkMachineIndicator } from '@src/components/NetworkMachineIndicator' +import Tooltip from '@src/components/Tooltip' +import { useAbsoluteFilePath } from '@src/hooks/useAbsoluteFilePath' +import { coreDump } from '@src/lang/wasm' +import type { CoreDumpManager } from '@src/lib/coredump' +import openWindow, { openExternalBrowserIfDesktop } from '@src/lib/openWindow' +import { PATHS } from '@src/lib/paths' +import { reportRejection } from '@src/lib/trap' +import { APP_VERSION, getReleaseUrl } from '@src/routes/Settings' export function LowerRightControls({ children, diff --git a/src/components/LspProvider.tsx b/src/components/LspProvider.tsx index 71f158eef..355531341 100644 --- a/src/components/LspProvider.tsx +++ b/src/components/LspProvider.tsx @@ -1,32 +1,33 @@ -import type * as LSP from 'vscode-languageserver-protocol' -import React, { createContext, useMemo, useContext, useState } from 'react' +import type { LanguageSupport } from '@codemirror/language' +import type { Extension } from '@codemirror/state' +import type { LanguageServerPlugin } from '@kittycad/codemirror-lsp-client' import { - LanguageServerClient, FromServer, IntoServer, + LanguageServerClient, LspWorkerEventType, - LanguageServerPlugin, } from '@kittycad/codemirror-lsp-client' -import { TEST, VITE_KC_API_BASE_URL } from 'env' -import { kcl } from 'editor/plugins/lsp/kcl/language' -import { copilotPlugin } from 'editor/plugins/lsp/copilot' -import { Extension } from '@codemirror/state' -import { LanguageSupport } from '@codemirror/language' +import { TEST, VITE_KC_API_BASE_URL } from '@src/env' +import React, { createContext, useContext, useMemo, useState } from 'react' import { useNavigate } from 'react-router-dom' -import { PATHS } from 'lib/paths' -import { FileEntry } from 'lib/project' -import Worker from 'editor/plugins/lsp/worker.ts?worker' -import { - KclWorkerOptions, +import type * as LSP from 'vscode-languageserver-protocol' + +import { copilotPlugin } from '@src/editor/plugins/lsp/copilot' +import { kcl } from '@src/editor/plugins/lsp/kcl/language' +import type { CopilotWorkerOptions, - LspWorker, -} from 'editor/plugins/lsp/types' -import { wasmUrl } from 'lang/wasm' -import { PROJECT_ENTRYPOINT } from 'lib/constants' -import { err } from 'lib/trap' -import { isDesktop } from 'lib/isDesktop' -import { codeManager } from 'lib/singletons' -import { useToken } from 'machines/appMachine' + KclWorkerOptions, +} from '@src/editor/plugins/lsp/types' +import { LspWorker } from '@src/editor/plugins/lsp/types' +import Worker from '@src/editor/plugins/lsp/worker.ts?worker' +import { wasmUrl } from '@src/lang/wasm' +import { PROJECT_ENTRYPOINT } from '@src/lib/constants' +import { isDesktop } from '@src/lib/isDesktop' +import { PATHS } from '@src/lib/paths' +import type { FileEntry } from '@src/lib/project' +import { codeManager } from '@src/lib/singletons' +import { err } from '@src/lib/trap' +import { useToken } from '@src/machines/appMachine' function getWorkspaceFolders(): LSP.WorkspaceFolder[] { return [] diff --git a/src/components/MachineManagerProvider.tsx b/src/components/MachineManagerProvider.tsx index 8e0605bae..8a4ca2e3c 100644 --- a/src/components/MachineManagerProvider.tsx +++ b/src/components/MachineManagerProvider.tsx @@ -1,11 +1,11 @@ import { createContext, useEffect, useState } from 'react' -import { engineCommandManager } from 'lib/singletons' -import { isDesktop } from 'lib/isDesktop' -import { components } from 'lib/machine-api' -import { reportRejection } from 'lib/trap' -import { toSync } from 'lib/utils' -import { commandBarActor } from 'machines/commandBarMachine' +import { isDesktop } from '@src/lib/isDesktop' +import type { components } from '@src/lib/machine-api' +import { engineCommandManager } from '@src/lib/singletons' +import { reportRejection } from '@src/lib/trap' +import { toSync } from '@src/lib/utils' +import { commandBarActor } from '@src/machines/commandBarMachine' export type MachinesListing = Array< components['schemas']['MachineInfoResponse'] diff --git a/src/components/ModelStateIndicator.tsx b/src/components/ModelStateIndicator.tsx index 660d7e99c..54e5b52c8 100644 --- a/src/components/ModelStateIndicator.tsx +++ b/src/components/ModelStateIndicator.tsx @@ -1,6 +1,7 @@ -import { useEngineCommands } from './EngineCommands' -import { Spinner } from './Spinner' -import { CustomIcon } from './CustomIcon' +import { CustomIcon } from '@src/components/CustomIcon' +import { useEngineCommands } from '@src/components/EngineCommands' +import { Spinner } from '@src/components/Spinner' + export const ModelStateIndicator = () => { const [commands] = useEngineCommands() const lastCommandType = commands[commands.length - 1]?.type diff --git a/src/components/ModelingMachineProvider.tsx b/src/components/ModelingMachineProvider.tsx index e26f67d23..33dcd4893 100644 --- a/src/components/ModelingMachineProvider.tsx +++ b/src/components/ModelingMachineProvider.tsx @@ -1,70 +1,46 @@ import { useMachine, useSelector } from '@xstate/react' import React, { createContext, + useContext, useEffect, useMemo, useRef, - useContext, } from 'react' -import { - Actor, - ContextFrom, - Prop, - SnapshotFrom, - StateFrom, - assign, - fromPromise, -} from 'xstate' -import { - getPersistedContext, - modelingMachine, - modelingMachineDefaultContext, -} from 'machines/modelingMachine' -import { useSetupEngineManager } from 'hooks/useSetupEngineManager' -import { - isCursorInSketchCommandRange, - updateSketchDetailsNodePaths, -} from 'lang/util' -import { - kclManager, - sceneInfra, - engineCommandManager, - codeManager, - editorManager, - sceneEntitiesManager, - rustContext, -} from 'lib/singletons' -import { - MachineManager, - MachineManagerContext, -} from 'components/MachineManagerProvider' +import toast from 'react-hot-toast' import { useHotkeys } from 'react-hotkeys-hook' -import { applyConstraintHorzVertDistance } from './Toolbar/SetHorzVertDistance' -import { - angleBetweenInfo, - applyConstraintAngleBetween, -} from './Toolbar/SetAngleBetween' -import { - applyConstraintAngleLength, - applyConstraintLength, -} from './Toolbar/setAngleLength' -import { - handleSelectionBatch, - Selections, - updateSelections, -} from 'lib/selections' -import { applyConstraintIntersect } from './Toolbar/Intersect' -import { applyConstraintAbsDistance } from './Toolbar/SetAbsDistance' -import useStateMachineCommands from 'hooks/useStateMachineCommands' -import { - ModelingCommandSchema, - modelingMachineCommandConfig, -} from 'lib/commandBarConfigs/modelingCommandConfig' +import { useLoaderData, useNavigate, useSearchParams } from 'react-router-dom' +import type { Actor, ContextFrom, Prop, SnapshotFrom, StateFrom } from 'xstate' +import { assign, fromPromise } from 'xstate' + +import type { + OutputFormat3d, + Point3d, +} from '@rust/kcl-lib/bindings/ModelingCmd' +import type { Node } from '@rust/kcl-lib/bindings/Node' +import type { Plane } from '@rust/kcl-lib/bindings/Plane' + +import { letEngineAnimateAndSyncCamAfter } from '@src/clientSideScene/CameraControls' import { SEGMENT_BODIES, getParentGroup, - getSketchOrientationDetails, -} from 'clientSideScene/sceneEntities' +} from '@src/clientSideScene/sceneConstants' +import type { MachineManager } from '@src/components/MachineManagerProvider' +import { MachineManagerContext } from '@src/components/MachineManagerProvider' +import { applyConstraintIntersect } from '@src/components/Toolbar/Intersect' +import { applyConstraintAbsDistance } from '@src/components/Toolbar/SetAbsDistance' +import { + angleBetweenInfo, + applyConstraintAngleBetween, +} from '@src/components/Toolbar/SetAngleBetween' +import { applyConstraintHorzVertDistance } from '@src/components/Toolbar/SetHorzVertDistance' +import { + applyConstraintAngleLength, + applyConstraintLength, +} from '@src/components/Toolbar/setAngleLength' +import { useFileContext } from '@src/hooks/useFileContext' +import { useSetupEngineManager } from '@src/hooks/useSetupEngineManager' +import useStateMachineCommands from '@src/hooks/useStateMachineCommands' +import { updateModelingState } from '@src/lang/modelingWorkflows' import { insertNamedConstant, replaceValueAtNodePath, @@ -72,55 +48,69 @@ import { sketchOnOffsetPlane, splitPipedProfile, startSketchOnDefault, -} from 'lang/modifyAst' -import { - KclValue, - PathToNode, - PipeExpression, - Program, - VariableDeclaration, - parse, - recast, - resultIsOk, -} from 'lang/wasm' +} from '@src/lang/modifyAst' import { artifactIsPlaneWithPaths, doesSketchPipeNeedSplitting, getNodeFromPath, isCursorInFunctionDefinition, traverse, -} from 'lang/queryAst' -import toast from 'react-hot-toast' -import { useLoaderData, useNavigate, useSearchParams } from 'react-router-dom' -import { letEngineAnimateAndSyncCamAfter } from 'clientSideScene/CameraControls' -import { err, reportRejection, trap, reject } from 'lib/trap' -import { - EngineConnectionStateType, - EngineConnectionEvents, -} from 'lang/std/engineConnection' -import { submitAndAwaitTextToKcl } from 'lib/textToCad' -import { useFileContext } from 'hooks/useFileContext' -import { platform, uuidv4 } from 'lib/utils' -import { Node } from '@rust/kcl-lib/bindings/Node' +} from '@src/lang/queryAst' +import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils' import { getFaceCodeRef, getPathsFromArtifact, getPlaneFromArtifact, -} from 'lang/std/artifactGraph' -import { promptToEditFlow } from 'lib/promptToEdit' -import { kclEditorActor } from 'machines/kclEditorMachine' -import { commandBarActor } from 'machines/commandBarMachine' -import { useToken } from 'machines/appMachine' -import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils' -import { useSettings } from 'machines/appMachine' -import { IndexLoaderData } from 'lib/types' -import { OutputFormat3d, Point3d } from '@rust/kcl-lib/bindings/ModelingCmd' -import { EXPORT_TOAST_MESSAGES, MAKE_TOAST_MESSAGES } from 'lib/constants' -import { exportMake } from 'lib/exportMake' -import { exportSave } from 'lib/exportSave' -import { Plane } from '@rust/kcl-lib/bindings/Plane' -import { updateModelingState } from 'lang/modelingWorkflows' -import { EXECUTION_TYPE_MOCK } from 'lib/constants' +} from '@src/lang/std/artifactGraph' +import { + EngineConnectionEvents, + EngineConnectionStateType, +} from '@src/lang/std/engineConnection' +import { + isCursorInSketchCommandRange, + updateSketchDetailsNodePaths, +} from '@src/lang/util' +import type { + KclValue, + PathToNode, + PipeExpression, + Program, + VariableDeclaration, +} from '@src/lang/wasm' +import { parse, recast, resultIsOk } from '@src/lang/wasm' +import type { ModelingCommandSchema } from '@src/lib/commandBarConfigs/modelingCommandConfig' +import { modelingMachineCommandConfig } from '@src/lib/commandBarConfigs/modelingCommandConfig' +import { + EXECUTION_TYPE_MOCK, + EXPORT_TOAST_MESSAGES, + MAKE_TOAST_MESSAGES, +} from '@src/lib/constants' +import { exportMake } from '@src/lib/exportMake' +import { exportSave } from '@src/lib/exportSave' +import { promptToEditFlow } from '@src/lib/promptToEdit' +import type { Selections } from '@src/lib/selections' +import { handleSelectionBatch, updateSelections } from '@src/lib/selections' +import { + codeManager, + editorManager, + engineCommandManager, + kclManager, + rustContext, + sceneEntitiesManager, + sceneInfra, +} from '@src/lib/singletons' +import { submitAndAwaitTextToKcl } from '@src/lib/textToCad' +import { err, reject, reportRejection, trap } from '@src/lib/trap' +import type { IndexLoaderData } from '@src/lib/types' +import { platform, uuidv4 } from '@src/lib/utils' +import { useSettings, useToken } from '@src/machines/appMachine' +import { commandBarActor } from '@src/machines/commandBarMachine' +import { kclEditorActor } from '@src/machines/kclEditorMachine' +import { + getPersistedContext, + modelingMachine, + modelingMachineDefaultContext, +} from '@src/machines/modelingMachine' export const ModelingMachineContext = createContext( {} as { @@ -911,7 +901,9 @@ export const ModelingMachineProvider = ({ } return Promise.reject(new Error('No sketch')) } - const info = await getSketchOrientationDetails(sketch.value) + const info = await sceneEntitiesManager.getSketchOrientationDetails( + sketch.value + ) await letEngineAnimateAndSyncCamAfter( engineCommandManager, info?.sketchDetails?.faceId || '' diff --git a/src/components/ModelingSidebar/ModelingPane.tsx b/src/components/ModelingSidebar/ModelingPane.tsx index b39c0e05d..734b8dbd6 100644 --- a/src/components/ModelingSidebar/ModelingPane.tsx +++ b/src/components/ModelingSidebar/ModelingPane.tsx @@ -1,12 +1,14 @@ -import { ReactNode } from 'react' +import type { IconDefinition } from '@fortawesome/free-solid-svg-icons' +import type { ReactNode } from 'react' + +import { ActionButton } from '@src/components/ActionButton' +import { ActionIcon } from '@src/components/ActionIcon' +import type { CustomIconName } from '@src/components/CustomIcon' +import Tooltip from '@src/components/Tooltip' +import { useSettings } from '@src/machines/appMachine' +import { onboardingPaths } from '@src/routes/Onboarding/paths' + import styles from './ModelingPane.module.css' -import { ActionButton } from 'components/ActionButton' -import Tooltip from 'components/Tooltip' -import { CustomIconName } from 'components/CustomIcon' -import { IconDefinition } from '@fortawesome/free-solid-svg-icons' -import { ActionIcon } from 'components/ActionIcon' -import { onboardingPaths } from 'routes/Onboarding/paths' -import { useSettings } from 'machines/appMachine' export interface ModelingPaneProps { id: string diff --git a/src/components/ModelingSidebar/ModelingPanes/CodeEditor.tsx b/src/components/ModelingSidebar/ModelingPanes/CodeEditor.tsx index 050d9c098..87d4d3fbe 100644 --- a/src/components/ModelingSidebar/ModelingPanes/CodeEditor.tsx +++ b/src/components/ModelingSidebar/ModelingPanes/CodeEditor.tsx @@ -1,20 +1,17 @@ -import React, { +import type { EditorStateConfig, Extension } from '@codemirror/state' +import { EditorState, StateEffect } from '@codemirror/state' +import { oneDark } from '@codemirror/theme-one-dark' +import { EditorView } from '@codemirror/view' +import { + forwardRef, useEffect, + useImperativeHandle, useMemo, useRef, useState, - forwardRef, - useImperativeHandle, } from 'react' -import { - EditorState, - EditorStateConfig, - Extension, - StateEffect, -} from '@codemirror/state' -import { EditorView } from '@codemirror/view' -import { oneDark } from '@codemirror/theme-one-dark' -import { isArray } from 'lib/utils' + +import { isArray } from '@src/lib/utils' //reference: https://github.com/sachinraja/rodemirror/blob/main/src/use-first-render.ts const useFirstRender = () => { diff --git a/src/components/ModelingSidebar/ModelingPanes/DebugPane.tsx b/src/components/ModelingSidebar/ModelingPanes/DebugPane.tsx index 99c4a0166..aa847900c 100644 --- a/src/components/ModelingSidebar/ModelingPanes/DebugPane.tsx +++ b/src/components/ModelingSidebar/ModelingPanes/DebugPane.tsx @@ -1,7 +1,7 @@ -import { DebugArtifactGraph } from 'components/DebugArtifactGraph' -import { AstExplorer } from '../../AstExplorer' -import { EngineCommands } from '../../EngineCommands' -import { CamDebugSettings } from 'clientSideScene/ClientSideSceneComp' +import { CamDebugSettings } from '@src/clientSideScene/ClientSideSceneComp' +import { AstExplorer } from '@src/components/AstExplorer' +import { DebugArtifactGraph } from '@src/components/DebugArtifactGraph' +import { EngineCommands } from '@src/components/EngineCommands' export const DebugPane = () => { return ( diff --git a/src/components/ModelingSidebar/ModelingPanes/FeatureTreeMenu.tsx b/src/components/ModelingSidebar/ModelingPanes/FeatureTreeMenu.tsx index d0cfb1f9e..964c950f3 100644 --- a/src/components/ModelingSidebar/ModelingPanes/FeatureTreeMenu.tsx +++ b/src/components/ModelingSidebar/ModelingPanes/FeatureTreeMenu.tsx @@ -1,8 +1,10 @@ import { Menu } from '@headlessui/react' -import { PropsWithChildren } from 'react' -import { ActionIcon } from 'components/ActionIcon' +import type { PropsWithChildren } from 'react' + +import { ActionIcon } from '@src/components/ActionIcon' +import { commandBarActor } from '@src/machines/commandBarMachine' + import styles from './KclEditorMenu.module.css' -import { commandBarActor } from 'machines/commandBarMachine' export const FeatureTreeMenu = ({ children }: PropsWithChildren) => { return ( diff --git a/src/components/ModelingSidebar/ModelingPanes/FeatureTreePane.tsx b/src/components/ModelingSidebar/ModelingPanes/FeatureTreePane.tsx index ed0b90747..035120e19 100644 --- a/src/components/ModelingSidebar/ModelingPanes/FeatureTreePane.tsx +++ b/src/components/ModelingSidebar/ModelingPanes/FeatureTreePane.tsx @@ -1,28 +1,35 @@ -import { Diagnostic } from '@codemirror/lint' +import type { Diagnostic } from '@codemirror/lint' import { useMachine, useSelector } from '@xstate/react' -import { ContextMenu, ContextMenuItem } from 'components/ContextMenu' -import { CustomIcon, CustomIconName } from 'components/CustomIcon' -import Loading from 'components/Loading' -import { useModelingContext } from 'hooks/useModelingContext' -import { useKclContext } from 'lang/KclProvider' -import { codeRefFromRange, getArtifactFromRange } from 'lang/std/artifactGraph' -import { sourceRangeFromRust } from 'lang/wasm' +import type { ComponentProps } from 'react' +import { useEffect, useMemo, useRef, useState } from 'react' +import type { Actor, Prop } from 'xstate' + +import type { Operation } from '@rust/kcl-lib/bindings/Operation' + +import { ContextMenu, ContextMenuItem } from '@src/components/ContextMenu' +import type { CustomIconName } from '@src/components/CustomIcon' +import { CustomIcon } from '@src/components/CustomIcon' +import Loading from '@src/components/Loading' +import { useModelingContext } from '@src/hooks/useModelingContext' +import { useKclContext } from '@src/lang/KclProvider' +import { + codeRefFromRange, + getArtifactFromRange, +} from '@src/lang/std/artifactGraph' +import { sourceRangeFromRust } from '@src/lang/wasm' import { filterOperations, getOperationIcon, getOperationLabel, stdLibMap, -} from 'lib/operations' -import { editorManager, kclManager } from 'lib/singletons' -import { ComponentProps, useEffect, useMemo, useRef, useState } from 'react' -import { Operation } from '@rust/kcl-lib/bindings/Operation' -import { Actor, Prop } from 'xstate' -import { featureTreeMachine } from 'machines/featureTreeMachine' +} from '@src/lib/operations' +import { editorManager, kclManager } from '@src/lib/singletons' +import { featureTreeMachine } from '@src/machines/featureTreeMachine' import { editorIsMountedSelector, kclEditorActor, selectionEventSelector, -} from 'machines/kclEditorMachine' +} from '@src/machines/kclEditorMachine' export const FeatureTreePane = () => { const isEditorMounted = useSelector(kclEditorActor, editorIsMountedSelector) diff --git a/src/components/ModelingSidebar/ModelingPanes/KclEditorMenu.tsx b/src/components/ModelingSidebar/ModelingPanes/KclEditorMenu.tsx index ba462d5d4..d713d7d3d 100644 --- a/src/components/ModelingSidebar/ModelingPanes/KclEditorMenu.tsx +++ b/src/components/ModelingSidebar/ModelingPanes/KclEditorMenu.tsx @@ -1,15 +1,17 @@ -import { Menu } from '@headlessui/react' -import { PropsWithChildren } from 'react' import { faArrowUpRightFromSquare } from '@fortawesome/free-solid-svg-icons' -import { ActionIcon } from 'components/ActionIcon' -import styles from './KclEditorMenu.module.css' -import { useConvertToVariable } from 'hooks/useToolbarGuards' -import { editorShortcutMeta } from './KclEditorPane' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { kclManager } from 'lib/singletons' -import { openExternalBrowserIfDesktop } from 'lib/openWindow' -import { reportRejection } from 'lib/trap' -import { commandBarActor } from 'machines/commandBarMachine' +import { Menu } from '@headlessui/react' +import type { PropsWithChildren } from 'react' + +import { ActionIcon } from '@src/components/ActionIcon' +import { editorShortcutMeta } from '@src/components/ModelingSidebar/ModelingPanes/KclEditorPane' +import { useConvertToVariable } from '@src/hooks/useToolbarGuards' +import { openExternalBrowserIfDesktop } from '@src/lib/openWindow' +import { kclManager } from '@src/lib/singletons' +import { reportRejection } from '@src/lib/trap' +import { commandBarActor } from '@src/machines/commandBarMachine' + +import styles from './KclEditorMenu.module.css' export const KclEditorMenu = ({ children }: PropsWithChildren) => { const { enable: convertToVarEnabled, handleClick: handleConvertToVarClick } = diff --git a/src/components/ModelingSidebar/ModelingPanes/KclEditorPane.tsx b/src/components/ModelingSidebar/ModelingPanes/KclEditorPane.tsx index 5094d0fc5..25084aedf 100644 --- a/src/components/ModelingSidebar/ModelingPanes/KclEditorPane.tsx +++ b/src/components/ModelingSidebar/ModelingPanes/KclEditorPane.tsx @@ -1,56 +1,58 @@ -import { TEST } from 'env' -import { Themes, getSystemTheme } from 'lib/theme' -import { useEffect, useMemo, useRef } from 'react' -import { highlightSelectionMatches, searchKeymap } from '@codemirror/search' -import { lineHighlightField } from 'editor/highlightextension' -import { onMouseDragMakeANewNumber, onMouseDragRegex } from 'lib/utils' -import { - lineNumbers, - rectangularSelection, - highlightActiveLineGutter, - highlightSpecialChars, - highlightActiveLine, - keymap, - EditorView, - dropCursor, - drawSelection, -} from '@codemirror/view' -import { - indentWithTab, - defaultKeymap, - historyKeymap, - history, -} from '@codemirror/commands' -import { diagnosticCount, lintGutter, lintKeymap } from '@codemirror/lint' -import { - foldGutter, - foldKeymap, - bracketMatching, - indentOnInput, - codeFolding, - syntaxHighlighting, - defaultHighlightStyle, -} from '@codemirror/language' -import interact from '@replit/codemirror-interact' -import { kclManager, editorManager, codeManager } from 'lib/singletons' -import { useHotkeys } from 'react-hotkeys-hook' -import { useLspContext } from 'components/LspProvider' -import { Prec, EditorState, Extension, Transaction } from '@codemirror/state' import { closeBrackets, closeBracketsKeymap, completionKeymap, } from '@codemirror/autocomplete' -import CodeEditor from './CodeEditor' -import { codeManagerHistoryCompartment } from 'lang/codeManager' +import { + defaultKeymap, + history, + historyKeymap, + indentWithTab, +} from '@codemirror/commands' +import { + bracketMatching, + codeFolding, + defaultHighlightStyle, + foldGutter, + foldKeymap, + indentOnInput, + syntaxHighlighting, +} from '@codemirror/language' +import { diagnosticCount, lintGutter, lintKeymap } from '@codemirror/lint' +import { highlightSelectionMatches, searchKeymap } from '@codemirror/search' +import type { Extension } from '@codemirror/state' +import { EditorState, Prec, Transaction } from '@codemirror/state' +import { + EditorView, + drawSelection, + dropCursor, + highlightActiveLine, + highlightActiveLineGutter, + highlightSpecialChars, + keymap, + lineNumbers, + rectangularSelection, +} from '@codemirror/view' +import interact from '@replit/codemirror-interact' +import { TEST } from '@src/env' +import { useSelector } from '@xstate/react' +import { useEffect, useMemo, useRef } from 'react' +import { useHotkeys } from 'react-hotkeys-hook' + +import { useLspContext } from '@src/components/LspProvider' +import CodeEditor from '@src/components/ModelingSidebar/ModelingPanes/CodeEditor' +import { lineHighlightField } from '@src/editor/highlightextension' +import { modelingMachineEvent } from '@src/editor/manager' +import { codeManagerHistoryCompartment } from '@src/lang/codeManager' +import { codeManager, editorManager, kclManager } from '@src/lib/singletons' +import { Themes, getSystemTheme } from '@src/lib/theme' +import { onMouseDragMakeANewNumber, onMouseDragRegex } from '@src/lib/utils' +import { useSettings } from '@src/machines/appMachine' import { editorIsMountedSelector, kclEditorActor, selectionEventSelector, -} from 'machines/kclEditorMachine' -import { useSelector } from '@xstate/react' -import { modelingMachineEvent } from 'editor/manager' -import { useSettings } from 'machines/appMachine' +} from '@src/machines/kclEditorMachine' export const editorShortcutMeta = { formatCode: { diff --git a/src/components/ModelingSidebar/ModelingPanes/LoggingPanes.tsx b/src/components/ModelingSidebar/ModelingPanes/LoggingPanes.tsx index 91b7d628b..718530145 100644 --- a/src/components/ModelingSidebar/ModelingPanes/LoggingPanes.tsx +++ b/src/components/ModelingSidebar/ModelingPanes/LoggingPanes.tsx @@ -1,6 +1,7 @@ import ReactJson from 'react-json-view' -import { useKclContext } from 'lang/KclProvider' -import { useResolvedTheme } from 'hooks/useResolvedTheme' + +import { useResolvedTheme } from '@src/hooks/useResolvedTheme' +import { useKclContext } from '@src/lang/KclProvider' const ReactJsonTypeHack = ReactJson as any diff --git a/src/components/ModelingSidebar/ModelingPanes/MemoryPane.test.tsx b/src/components/ModelingSidebar/ModelingPanes/MemoryPane.test.tsx index 1e598ad60..d19b78a0c 100644 --- a/src/components/ModelingSidebar/ModelingPanes/MemoryPane.test.tsx +++ b/src/components/ModelingSidebar/ModelingPanes/MemoryPane.test.tsx @@ -1,6 +1,6 @@ -import { processMemory } from './MemoryPane' -import { enginelessExecutor } from '../../../lib/testHelpers' -import { assertParse, initPromise } from '../../../lang/wasm' +import { processMemory } from '@src/components/ModelingSidebar/ModelingPanes/MemoryPane' +import { assertParse, initPromise } from '@src/lang/wasm' +import { enginelessExecutor } from '@src/lib/testHelpers' beforeAll(async () => { await initPromise diff --git a/src/components/ModelingSidebar/ModelingPanes/MemoryPane.tsx b/src/components/ModelingSidebar/ModelingPanes/MemoryPane.tsx index 7b11fd6b1..7e5f4d001 100644 --- a/src/components/ModelingSidebar/ModelingPanes/MemoryPane.tsx +++ b/src/components/ModelingSidebar/ModelingPanes/MemoryPane.tsx @@ -1,18 +1,18 @@ +import { useMemo } from 'react' import toast from 'react-hot-toast' import ReactJson from 'react-json-view' -import { useMemo } from 'react' -import { - Path, - ExtrudeSurface, - sketchFromKclValueOptional, - VariableMap, -} from 'lang/wasm' -import { useKclContext } from 'lang/KclProvider' -import { useResolvedTheme } from 'hooks/useResolvedTheme' -import { ActionButton } from 'components/ActionButton' -import { Reason, trap } from 'lib/trap' -import Tooltip from 'components/Tooltip' -import { useModelingContext } from 'hooks/useModelingContext' + +import type { ExtrudeSurface } from '@rust/kcl-lib/bindings/ExtrudeSurface' +import type { Path } from '@rust/kcl-lib/bindings/Path' + +import { ActionButton } from '@src/components/ActionButton' +import Tooltip from '@src/components/Tooltip' +import { useModelingContext } from '@src/hooks/useModelingContext' +import { useResolvedTheme } from '@src/hooks/useResolvedTheme' +import { useKclContext } from '@src/lang/KclProvider' +import type { VariableMap } from '@src/lang/wasm' +import { sketchFromKclValueOptional } from '@src/lang/wasm' +import { Reason, trap } from '@src/lib/trap' export const MemoryPaneMenu = () => { const { variables } = useKclContext() diff --git a/src/components/ModelingSidebar/ModelingPanes/index.tsx b/src/components/ModelingSidebar/ModelingPanes/index.tsx index 6ec850748..00ef6224e 100644 --- a/src/components/ModelingSidebar/ModelingPanes/index.tsx +++ b/src/components/ModelingSidebar/ModelingPanes/index.tsx @@ -1,25 +1,30 @@ -import { IconDefinition, faBugSlash } from '@fortawesome/free-solid-svg-icons' -import { KclEditorMenu } from 'components/ModelingSidebar/ModelingPanes/KclEditorMenu' -import { CustomIconName } from 'components/CustomIcon' -import { KclEditorPane } from 'components/ModelingSidebar/ModelingPanes/KclEditorPane' -import { ModelingPaneHeader } from 'components/ModelingSidebar/ModelingPane' -import { MouseEventHandler, ReactNode } from 'react' -import { MemoryPane, MemoryPaneMenu } from './MemoryPane' -import { LogsPane } from './LoggingPanes' -import { DebugPane } from './DebugPane' +import type { IconDefinition } from '@fortawesome/free-solid-svg-icons' +import { faBugSlash } from '@fortawesome/free-solid-svg-icons' +import type { MouseEventHandler, ReactNode } from 'react' +import type { ContextFrom } from 'xstate' + +import type { CustomIconName } from '@src/components/CustomIcon' import { FileTreeInner, FileTreeMenu, FileTreeRoot, useFileTreeOperations, -} from 'components/FileTree' -import { useKclContext } from 'lang/KclProvider' -import { editorManager } from 'lib/singletons' -import { ContextFrom } from 'xstate' -import { settingsMachine } from 'machines/settingsMachine' -import { FeatureTreePane } from './FeatureTreePane' -import { kclErrorsByFilename } from 'lang/errors' -import { FeatureTreeMenu } from './FeatureTreeMenu' +} from '@src/components/FileTree' +import { ModelingPaneHeader } from '@src/components/ModelingSidebar/ModelingPane' +import { DebugPane } from '@src/components/ModelingSidebar/ModelingPanes/DebugPane' +import { FeatureTreeMenu } from '@src/components/ModelingSidebar/ModelingPanes/FeatureTreeMenu' +import { FeatureTreePane } from '@src/components/ModelingSidebar/ModelingPanes/FeatureTreePane' +import { KclEditorMenu } from '@src/components/ModelingSidebar/ModelingPanes/KclEditorMenu' +import { KclEditorPane } from '@src/components/ModelingSidebar/ModelingPanes/KclEditorPane' +import { LogsPane } from '@src/components/ModelingSidebar/ModelingPanes/LoggingPanes' +import { + MemoryPane, + MemoryPaneMenu, +} from '@src/components/ModelingSidebar/ModelingPanes/MemoryPane' +import type { useKclContext } from '@src/lang/KclProvider' +import { kclErrorsByFilename } from '@src/lang/errors' +import { editorManager } from '@src/lib/singletons' +import type { settingsMachine } from '@src/machines/settingsMachine' export type SidebarType = | 'code' diff --git a/src/components/ModelingSidebar/ModelingSidebar.tsx b/src/components/ModelingSidebar/ModelingSidebar.tsx index 38146b8a8..43d4ab205 100644 --- a/src/components/ModelingSidebar/ModelingSidebar.tsx +++ b/src/components/ModelingSidebar/ModelingSidebar.tsx @@ -1,26 +1,26 @@ +import type { IconDefinition } from '@fortawesome/free-solid-svg-icons' import { Resizable } from 're-resizable' -import { - MouseEventHandler, - useCallback, - useEffect, - useMemo, - useContext, -} from 'react' +import type { MouseEventHandler } from 'react' +import { useCallback, useContext, useEffect, useMemo } from 'react' import { useHotkeys } from 'react-hotkeys-hook' -import { SidebarAction, SidebarType, sidebarPanes } from './ModelingPanes' -import Tooltip from 'components/Tooltip' -import { ActionIcon } from 'components/ActionIcon' -import { ModelingPane } from './ModelingPane' -import { isDesktop } from 'lib/isDesktop' -import { useModelingContext } from 'hooks/useModelingContext' -import { CustomIconName } from 'components/CustomIcon' -import { IconDefinition } from '@fortawesome/free-solid-svg-icons' -import { useKclContext } from 'lang/KclProvider' -import { MachineManagerContext } from 'components/MachineManagerProvider' -import { onboardingPaths } from 'routes/Onboarding/paths' -import { SIDEBAR_BUTTON_SUFFIX } from 'lib/constants' -import { commandBarActor } from 'machines/commandBarMachine' -import { useSettings } from 'machines/appMachine' + +import { ActionIcon } from '@src/components/ActionIcon' +import type { CustomIconName } from '@src/components/CustomIcon' +import { MachineManagerContext } from '@src/components/MachineManagerProvider' +import { ModelingPane } from '@src/components/ModelingSidebar/ModelingPane' +import type { + SidebarAction, + SidebarType, +} from '@src/components/ModelingSidebar/ModelingPanes' +import { sidebarPanes } from '@src/components/ModelingSidebar/ModelingPanes' +import Tooltip from '@src/components/Tooltip' +import { useModelingContext } from '@src/hooks/useModelingContext' +import { useKclContext } from '@src/lang/KclProvider' +import { SIDEBAR_BUTTON_SUFFIX } from '@src/lib/constants' +import { isDesktop } from '@src/lib/isDesktop' +import { useSettings } from '@src/machines/appMachine' +import { commandBarActor } from '@src/machines/commandBarMachine' +import { onboardingPaths } from '@src/routes/Onboarding/paths' interface ModelingSidebarProps { paneOpacity: '' | 'opacity-20' | 'opacity-40' @@ -113,17 +113,20 @@ export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) { ) const paneBadgeMap: Record = useMemo(() => { - return filteredPanes.reduce((acc, pane) => { - if (pane.showBadge) { - acc[pane.id] = { - value: pane.showBadge.value(paneCallbackProps), - onClick: pane.showBadge.onClick, - className: pane.showBadge.className, - title: pane.showBadge.title, + return filteredPanes.reduce( + (acc, pane) => { + if (pane.showBadge) { + acc[pane.id] = { + value: pane.showBadge.value(paneCallbackProps), + onClick: pane.showBadge.onClick, + className: pane.showBadge.className, + title: pane.showBadge.title, + } } - } - return acc - }, {} as Record) + return acc + }, + {} as Record + ) }, [paneCallbackProps]) // Clear any hidden panes from the `openPanes` array diff --git a/src/components/NetworkHealthIndicator.test.tsx b/src/components/NetworkHealthIndicator.test.tsx index 84e2ef978..db5659391 100644 --- a/src/components/NetworkHealthIndicator.test.tsx +++ b/src/components/NetworkHealthIndicator.test.tsx @@ -1,10 +1,11 @@ import { fireEvent, render, screen } from '@testing-library/react' import { BrowserRouter } from 'react-router-dom' + import { NETWORK_HEALTH_TEXT, NetworkHealthIndicator, -} from './NetworkHealthIndicator' -import { NetworkHealthState } from 'hooks/useNetworkStatus' +} from '@src/components/NetworkHealthIndicator' +import { NetworkHealthState } from '@src/hooks/useNetworkStatus' function TestWrap({ children }: { children: React.ReactNode }) { // wrap in router and xState context diff --git a/src/components/NetworkHealthIndicator.tsx b/src/components/NetworkHealthIndicator.tsx index 45381dc5f..3380154fa 100644 --- a/src/components/NetworkHealthIndicator.tsx +++ b/src/components/NetworkHealthIndicator.tsx @@ -1,11 +1,13 @@ import { Popover } from '@headlessui/react' -import { ActionIcon, ActionIconProps } from './ActionIcon' -import Tooltip from './Tooltip' -import { ConnectingTypeGroup } from '../lang/std/engineConnection' -import { useNetworkContext } from '../hooks/useNetworkContext' -import { NetworkHealthState } from '../hooks/useNetworkStatus' -import { toSync } from 'lib/utils' -import { reportRejection } from 'lib/trap' + +import type { ActionIconProps } from '@src/components/ActionIcon' +import { ActionIcon } from '@src/components/ActionIcon' +import Tooltip from '@src/components/Tooltip' +import { useNetworkContext } from '@src/hooks/useNetworkContext' +import { NetworkHealthState } from '@src/hooks/useNetworkStatus' +import type { ConnectingTypeGroup } from '@src/lang/std/engineConnection' +import { reportRejection } from '@src/lib/trap' +import { toSync } from '@src/lib/utils' export const NETWORK_HEALTH_TEXT: Record = { [NetworkHealthState.Ok]: 'Connected', diff --git a/src/components/NetworkMachineIndicator.tsx b/src/components/NetworkMachineIndicator.tsx index 6d07fd946..2d3867216 100644 --- a/src/components/NetworkMachineIndicator.tsx +++ b/src/components/NetworkMachineIndicator.tsx @@ -1,10 +1,11 @@ import { Popover } from '@headlessui/react' import { useContext } from 'react' -import Tooltip from './Tooltip' -import { isDesktop } from 'lib/isDesktop' -import { components } from 'lib/machine-api' -import { MachineManagerContext } from 'components/MachineManagerProvider' -import { CustomIcon } from './CustomIcon' + +import { CustomIcon } from '@src/components/CustomIcon' +import { MachineManagerContext } from '@src/components/MachineManagerProvider' +import Tooltip from '@src/components/Tooltip' +import { isDesktop } from '@src/lib/isDesktop' +import type { components } from '@src/lib/machine-api' export const NetworkMachineIndicator = ({ className, diff --git a/src/components/OpenInDesktopAppHandler.test.tsx b/src/components/OpenInDesktopAppHandler.test.tsx index 9cac84fee..a68e02147 100644 --- a/src/components/OpenInDesktopAppHandler.test.tsx +++ b/src/components/OpenInDesktopAppHandler.test.tsx @@ -1,6 +1,7 @@ import { fireEvent, render, screen } from '@testing-library/react' import { BrowserRouter, Route, Routes } from 'react-router-dom' -import { OpenInDesktopAppHandler } from './OpenInDesktopAppHandler' + +import { OpenInDesktopAppHandler } from '@src/components/OpenInDesktopAppHandler' /** * The behavior under test requires a router, diff --git a/src/components/OpenInDesktopAppHandler.tsx b/src/components/OpenInDesktopAppHandler.tsx index 3c34dad1e..fbd81c399 100644 --- a/src/components/OpenInDesktopAppHandler.tsx +++ b/src/components/OpenInDesktopAppHandler.tsx @@ -1,11 +1,14 @@ -import { getSystemTheme, Themes } from 'lib/theme' -import { ZOO_STUDIO_PROTOCOL } from 'lib/constants' -import { isDesktop } from 'lib/isDesktop' -import { useSearchParams } from 'react-router-dom' -import { ASK_TO_OPEN_QUERY_PARAM } from 'lib/constants' -import { VITE_KC_SITE_BASE_URL } from 'env' -import { ActionButton } from './ActionButton' import { Transition } from '@headlessui/react' +import { VITE_KC_SITE_BASE_URL } from '@src/env' +import { useSearchParams } from 'react-router-dom' + +import { ActionButton } from '@src/components/ActionButton' +import { + ASK_TO_OPEN_QUERY_PARAM, + ZOO_STUDIO_PROTOCOL, +} from '@src/lib/constants' +import { isDesktop } from '@src/lib/isDesktop' +import { Themes, getSystemTheme } from '@src/lib/theme' /** * This component is a handler that checks if a certain query parameter diff --git a/src/components/ProjectCard/DeleteProjectDialog.tsx b/src/components/ProjectCard/DeleteProjectDialog.tsx index 0fd43e848..74d9ed102 100644 --- a/src/components/ProjectCard/DeleteProjectDialog.tsx +++ b/src/components/ProjectCard/DeleteProjectDialog.tsx @@ -1,5 +1,6 @@ import { Dialog } from '@headlessui/react' -import { ActionButton } from 'components/ActionButton' + +import { ActionButton } from '@src/components/ActionButton' type DeleteConfirmationDialogProps = React.PropsWithChildren<{ title: string diff --git a/src/components/ProjectCard/ProjectCard.tsx b/src/components/ProjectCard/ProjectCard.tsx index da6a20313..5a7908d01 100644 --- a/src/components/ProjectCard/ProjectCard.tsx +++ b/src/components/ProjectCard/ProjectCard.tsx @@ -1,15 +1,17 @@ -import { FormEvent, useEffect, useRef, useState } from 'react' -import { PATHS } from 'lib/paths' -import { Link } from 'react-router-dom' -import { ActionButton } from '../ActionButton' -import { FILE_EXT, PROJECT_IMAGE_NAME } from 'lib/constants' +import type { FormEvent } from 'react' +import { useEffect, useRef, useState } from 'react' import { useHotkeys } from 'react-hotkeys-hook' -import Tooltip from '../Tooltip' -import { DeleteConfirmationDialog } from './DeleteProjectDialog' -import { ProjectCardRenameForm } from './ProjectCardRenameForm' -import { Project } from 'lib/project' -import { toSync } from 'lib/utils' -import { reportRejection } from 'lib/trap' +import { Link } from 'react-router-dom' + +import { ActionButton } from '@src/components/ActionButton' +import { DeleteConfirmationDialog } from '@src/components/ProjectCard/DeleteProjectDialog' +import { ProjectCardRenameForm } from '@src/components/ProjectCard/ProjectCardRenameForm' +import Tooltip from '@src/components/Tooltip' +import { FILE_EXT, PROJECT_IMAGE_NAME } from '@src/lib/constants' +import { PATHS } from '@src/lib/paths' +import type { Project } from '@src/lib/project' +import { reportRejection } from '@src/lib/trap' +import { toSync } from '@src/lib/utils' function ProjectCard({ project, diff --git a/src/components/ProjectCard/ProjectCardRenameForm.tsx b/src/components/ProjectCard/ProjectCardRenameForm.tsx index 91addd69c..af578f3f6 100644 --- a/src/components/ProjectCard/ProjectCardRenameForm.tsx +++ b/src/components/ProjectCard/ProjectCardRenameForm.tsx @@ -1,7 +1,9 @@ -import { ActionButton } from 'components/ActionButton' -import Tooltip from 'components/Tooltip' -import { HTMLProps, forwardRef } from 'react' -import { Project } from 'lib/project' +import type { HTMLProps } from 'react' +import { forwardRef } from 'react' + +import { ActionButton } from '@src/components/ActionButton' +import Tooltip from '@src/components/Tooltip' +import type { Project } from '@src/lib/project' interface ProjectCardRenameFormProps extends HTMLProps { project: Project diff --git a/src/components/ProjectSearchBar.tsx b/src/components/ProjectSearchBar.tsx index 2eabd3035..4637700fd 100644 --- a/src/components/ProjectSearchBar.tsx +++ b/src/components/ProjectSearchBar.tsx @@ -1,8 +1,9 @@ -import { Project } from 'lib/project' -import { CustomIcon } from './CustomIcon' +import Fuse from 'fuse.js' import { useEffect, useRef, useState } from 'react' import { useHotkeys } from 'react-hotkeys-hook' -import Fuse from 'fuse.js' + +import { CustomIcon } from '@src/components/CustomIcon' +import type { Project } from '@src/lib/project' export function useProjectSearch(projects: Project[]) { const [query, setQuery] = useState('') diff --git a/src/components/ProjectSidebarMenu.test.tsx b/src/components/ProjectSidebarMenu.test.tsx index 60c43184c..7ebd3450e 100644 --- a/src/components/ProjectSidebarMenu.test.tsx +++ b/src/components/ProjectSidebarMenu.test.tsx @@ -1,7 +1,8 @@ import { render, screen } from '@testing-library/react' import { BrowserRouter } from 'react-router-dom' -import ProjectSidebarMenu from './ProjectSidebarMenu' -import { Project } from 'lib/project' + +import ProjectSidebarMenu from '@src/components/ProjectSidebarMenu' +import type { Project } from '@src/lib/project' const now = new Date() const projectWellFormed = { diff --git a/src/components/ProjectSidebarMenu.tsx b/src/components/ProjectSidebarMenu.tsx index 5e125a829..d8726c5a7 100644 --- a/src/components/ProjectSidebarMenu.tsx +++ b/src/components/ProjectSidebarMenu.tsx @@ -1,24 +1,30 @@ import { Popover, Transition } from '@headlessui/react' -import { ActionButton, ActionButtonProps } from './ActionButton' -import { type IndexLoaderData } from 'lib/types' -import { PATHS } from 'lib/paths' -import { isDesktop } from '../lib/isDesktop' -import { Link, useLocation, useNavigate } from 'react-router-dom' -import { Fragment, useMemo, useContext } from 'react' -import { Logo } from './Logo' -import { APP_NAME } from 'lib/constants' -import { CustomIcon } from './CustomIcon' -import { useLspContext } from './LspProvider' -import { codeManager, engineCommandManager, kclManager } from 'lib/singletons' -import { MachineManagerContext } from 'components/MachineManagerProvider' -import usePlatform from 'hooks/usePlatform' -import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath' -import Tooltip from './Tooltip' -import { SnapshotFrom } from 'xstate' -import { commandBarActor } from 'machines/commandBarMachine' import { useSelector } from '@xstate/react' -import { copyFileShareLink } from 'lib/links' -import { useToken } from 'machines/appMachine' +import { Fragment, useContext, useMemo } from 'react' +import { Link, useLocation, useNavigate } from 'react-router-dom' +import type { SnapshotFrom } from 'xstate' + +import type { ActionButtonProps } from '@src/components/ActionButton' +import { ActionButton } from '@src/components/ActionButton' +import { CustomIcon } from '@src/components/CustomIcon' +import { Logo } from '@src/components/Logo' +import { useLspContext } from '@src/components/LspProvider' +import { MachineManagerContext } from '@src/components/MachineManagerProvider' +import Tooltip from '@src/components/Tooltip' +import { useAbsoluteFilePath } from '@src/hooks/useAbsoluteFilePath' +import usePlatform from '@src/hooks/usePlatform' +import { APP_NAME } from '@src/lib/constants' +import { isDesktop } from '@src/lib/isDesktop' +import { copyFileShareLink } from '@src/lib/links' +import { PATHS } from '@src/lib/paths' +import { + codeManager, + engineCommandManager, + kclManager, +} from '@src/lib/singletons' +import { type IndexLoaderData } from '@src/lib/types' +import { useToken } from '@src/machines/appMachine' +import { commandBarActor } from '@src/machines/commandBarMachine' const ProjectSidebarMenu = ({ project, diff --git a/src/components/ProjectsContextProvider.tsx b/src/components/ProjectsContextProvider.tsx index 0c6559f98..74ef8fc35 100644 --- a/src/components/ProjectsContextProvider.tsx +++ b/src/components/ProjectsContextProvider.tsx @@ -1,39 +1,41 @@ import { useMachine } from '@xstate/react' -import { useFileSystemWatcher } from 'hooks/useFileSystemWatcher' -import { useProjectsLoader } from 'hooks/useProjectsLoader' -import { projectsMachine } from 'machines/projectsMachine' import { createContext, useCallback, useEffect, useState } from 'react' -import { Actor, AnyStateMachine, fromPromise, Prop, StateFrom } from 'xstate' -import { useLspContext } from './LspProvider' import toast from 'react-hot-toast' import { useLocation, useNavigate, useSearchParams } from 'react-router-dom' -import { PATHS } from 'lib/paths' -import { - createNewProjectDirectory, - listProjects, - renameProjectDirectory, -} from 'lib/desktop' -import { - getNextProjectIndex, - interpolateProjectNameWithIndex, - doesProjectNameNeedInterpolated, - getUniqueProjectName, - getNextFileName, -} from 'lib/desktopFS' -import useStateMachineCommands from 'hooks/useStateMachineCommands' -import { projectsCommandBarConfig } from 'lib/commandBarConfigs/projectsCommandConfig' -import { isDesktop } from 'lib/isDesktop' -import { commandBarActor } from 'machines/commandBarMachine' -import { useSettings } from 'machines/appMachine' +import type { Actor, AnyStateMachine, Prop, StateFrom } from 'xstate' +import { fromPromise } from 'xstate' + +import { useLspContext } from '@src/components/LspProvider' +import { useFileSystemWatcher } from '@src/hooks/useFileSystemWatcher' +import { useProjectsLoader } from '@src/hooks/useProjectsLoader' +import useStateMachineCommands from '@src/hooks/useStateMachineCommands' +import { newKclFile } from '@src/lang/project' +import { projectsCommandBarConfig } from '@src/lib/commandBarConfigs/projectsCommandConfig' import { CREATE_FILE_URL_PARAM, FILE_EXT, PROJECT_ENTRYPOINT, -} from 'lib/constants' -import { codeManager, kclManager } from 'lib/singletons' -import { Project } from 'lib/project' -import { newKclFile } from 'lang/project' -import { err } from 'lib/trap' +} from '@src/lib/constants' +import { + createNewProjectDirectory, + listProjects, + renameProjectDirectory, +} from '@src/lib/desktop' +import { + doesProjectNameNeedInterpolated, + getNextFileName, + getNextProjectIndex, + getUniqueProjectName, + interpolateProjectNameWithIndex, +} from '@src/lib/desktopFS' +import { isDesktop } from '@src/lib/isDesktop' +import { PATHS } from '@src/lib/paths' +import type { Project } from '@src/lib/project' +import { codeManager, kclManager } from '@src/lib/singletons' +import { err } from '@src/lib/trap' +import { useSettings } from '@src/machines/appMachine' +import { commandBarActor } from '@src/machines/commandBarMachine' +import { projectsMachine } from '@src/machines/projectsMachine' type MachineContext = { state?: StateFrom @@ -382,8 +384,8 @@ const ProjectsContextDesktop = ({ input.method === 'newProject' ? PROJECT_ENTRYPOINT : input.name.endsWith(FILE_EXT) - ? input.name - : input.name + FILE_EXT + ? input.name + : input.name + FILE_EXT let message = 'File created successfully' const needsInterpolated = doesProjectNameNeedInterpolated(projectName) diff --git a/src/components/RefreshButton.tsx b/src/components/RefreshButton.tsx index 65aefb741..12fe4bb8e 100644 --- a/src/components/RefreshButton.tsx +++ b/src/components/RefreshButton.tsx @@ -1,16 +1,20 @@ -import { coreDump } from 'lang/wasm' -import { CoreDumpManager } from 'lib/coredump' -import { CustomIcon } from './CustomIcon' -import { codeManager, engineCommandManager } from 'lib/singletons' import React, { useMemo } from 'react' import toast from 'react-hot-toast' -import Tooltip from './Tooltip' -import { reportRejection } from 'lib/trap' -import { toSync } from 'lib/utils' -import { useToken } from 'machines/appMachine' -import { rustContext } from 'lib/singletons' -import type { WebContentSendPayload } from '../menu/channels' -import { useMenuListener } from 'hooks/useMenu' + +import { CustomIcon } from '@src/components/CustomIcon' +import Tooltip from '@src/components/Tooltip' +import { useMenuListener } from '@src/hooks/useMenu' +import { coreDump } from '@src/lang/wasm' +import { CoreDumpManager } from '@src/lib/coredump' +import { + codeManager, + engineCommandManager, + rustContext, +} from '@src/lib/singletons' +import { reportRejection } from '@src/lib/trap' +import { toSync } from '@src/lib/utils' +import { useToken } from '@src/machines/appMachine' +import type { WebContentSendPayload } from '@src/menu/channels' export const RefreshButton = ({ children }: React.PropsWithChildren) => { const token = useToken() diff --git a/src/components/RouteProvider.tsx b/src/components/RouteProvider.tsx index 6662f8702..c118d20f5 100644 --- a/src/components/RouteProvider.tsx +++ b/src/components/RouteProvider.tsx @@ -1,22 +1,24 @@ -import { useEffect, useState, createContext, ReactNode } from 'react' +import type { ReactNode } from 'react' +import { createContext, useEffect, useState } from 'react' import { - useNavigation, useLocation, useNavigate, + useNavigation, useRouteLoaderData, } from 'react-router-dom' -import { PATHS } from 'lib/paths' -import { markOnce } from 'lib/performance' -import { useAuthNavigation } from 'hooks/useAuthNavigation' -import { useSettings } from 'machines/appMachine' -import { IndexLoaderData } from 'lib/types' -import { getAppSettingsFilePath } from 'lib/desktop' -import { isDesktop } from 'lib/isDesktop' -import { trap } from 'lib/trap' -import { useFileSystemWatcher } from 'hooks/useFileSystemWatcher' -import { loadAndValidateSettings } from 'lib/settings/settingsUtils' -import { settingsActor } from 'machines/appMachine' -import { OnboardingStatus } from '@rust/kcl-lib/bindings/OnboardingStatus' + +import type { OnboardingStatus } from '@rust/kcl-lib/bindings/OnboardingStatus' + +import { useAuthNavigation } from '@src/hooks/useAuthNavigation' +import { useFileSystemWatcher } from '@src/hooks/useFileSystemWatcher' +import { getAppSettingsFilePath } from '@src/lib/desktop' +import { isDesktop } from '@src/lib/isDesktop' +import { PATHS } from '@src/lib/paths' +import { markOnce } from '@src/lib/performance' +import { loadAndValidateSettings } from '@src/lib/settings/settingsUtils' +import { trap } from '@src/lib/trap' +import type { IndexLoaderData } from '@src/lib/types' +import { settingsActor, useSettings } from '@src/machines/appMachine' export const RouteProviderContext = createContext({}) diff --git a/src/components/SetAngleLengthModal.tsx b/src/components/SetAngleLengthModal.tsx index 982f98bfb..e45518b36 100644 --- a/src/components/SetAngleLengthModal.tsx +++ b/src/components/SetAngleLengthModal.tsx @@ -1,14 +1,15 @@ import { Dialog, Transition } from '@headlessui/react' import { Fragment, useState } from 'react' import { type InstanceProps, create } from 'react-modal-promise' -import { Expr } from '../lang/wasm' + import { AvailableVars, - addToInputHelper, CalcResult, CreateNewVariable, -} from './AvailableVarsHelpers' -import { useCalculateKclExpression } from 'lib/useCalculateKclExpression' + addToInputHelper, +} from '@src/components/AvailableVarsHelpers' +import type { Expr } from '@src/lang/wasm' +import { useCalculateKclExpression } from '@src/lib/useCalculateKclExpression' type ModalResolve = { value: string diff --git a/src/components/SetHorVertDistanceModal.tsx b/src/components/SetHorVertDistanceModal.tsx index 46c1f2ff6..dd9fd406e 100644 --- a/src/components/SetHorVertDistanceModal.tsx +++ b/src/components/SetHorVertDistanceModal.tsx @@ -1,14 +1,15 @@ import { Dialog, Transition } from '@headlessui/react' import { Fragment, useState } from 'react' import { type InstanceProps, create } from 'react-modal-promise' -import { Expr } from '../lang/wasm' + import { AvailableVars, - addToInputHelper, CalcResult, CreateNewVariable, -} from './AvailableVarsHelpers' -import { useCalculateKclExpression } from 'lib/useCalculateKclExpression' + addToInputHelper, +} from '@src/components/AvailableVarsHelpers' +import type { Expr } from '@src/lang/wasm' +import { useCalculateKclExpression } from '@src/lib/useCalculateKclExpression' type ModalResolve = { value: string diff --git a/src/components/SetVarNameModal.tsx b/src/components/SetVarNameModal.tsx index e69f1f2de..7a1a02177 100644 --- a/src/components/SetVarNameModal.tsx +++ b/src/components/SetVarNameModal.tsx @@ -1,10 +1,11 @@ import { Dialog, Transition } from '@headlessui/react' import { Fragment } from 'react' -import { CreateNewVariable } from './AvailableVarsHelpers' -import { ActionButton } from './ActionButton' import { toast } from 'react-hot-toast' import { type InstanceProps, create } from 'react-modal-promise' -import { useCalculateKclExpression } from 'lib/useCalculateKclExpression' + +import { ActionButton } from '@src/components/ActionButton' +import { CreateNewVariable } from '@src/components/AvailableVarsHelpers' +import { useCalculateKclExpression } from '@src/lib/useCalculateKclExpression' type ModalResolve = { variableName: string } type ModalReject = boolean diff --git a/src/components/Settings/AllKeybindingsFields.tsx b/src/components/Settings/AllKeybindingsFields.tsx index 356ec7746..004bc437f 100644 --- a/src/components/Settings/AllKeybindingsFields.tsx +++ b/src/components/Settings/AllKeybindingsFields.tsx @@ -1,10 +1,12 @@ +import type { ForwardedRef } from 'react' +import { forwardRef } from 'react' +import { useLocation } from 'react-router-dom' + +import type { InteractionMapItem } from '@src/lib/settings/initialKeybindings' import { - InteractionMapItem, interactionMap, sortInteractionMapByCategory, -} from 'lib/settings/initialKeybindings' -import { ForwardedRef, forwardRef } from 'react' -import { useLocation } from 'react-router-dom' +} from '@src/lib/settings/initialKeybindings' type AllKeybindingsFieldsProps = object diff --git a/src/components/Settings/AllSettingsFields.tsx b/src/components/Settings/AllSettingsFields.tsx index 6b9ae8da6..167f0440c 100644 --- a/src/components/Settings/AllSettingsFields.tsx +++ b/src/components/Settings/AllSettingsFields.tsx @@ -1,31 +1,36 @@ +import { useSelector } from '@xstate/react' import decamelize from 'decamelize' -import { Setting } from 'lib/settings/initialSettings' -import { SetEventTypes, SettingsLevel } from 'lib/settings/settingsTypes' -import { - shouldHideSetting, - shouldShowSettingInput, -} from 'lib/settings/settingsUtils' -import { Fragment } from 'react/jsx-runtime' -import { SettingsSection } from './SettingsSection' -import { useLocation, useNavigate } from 'react-router-dom' -import { isDesktop } from 'lib/isDesktop' -import { ActionButton } from 'components/ActionButton' -import { SettingsFieldInput } from './SettingsFieldInput' +import type { ForwardedRef } from 'react' +import { forwardRef, useEffect, useMemo } from 'react' import toast from 'react-hot-toast' -import { APP_VERSION, IS_NIGHTLY, getReleaseUrl } from 'routes/Settings' -import { PATHS } from 'lib/paths' +import { useLocation, useNavigate } from 'react-router-dom' +import { Fragment } from 'react/jsx-runtime' + +import { ActionButton } from '@src/components/ActionButton' +import { useLspContext } from '@src/components/LspProvider' +import { SettingsFieldInput } from '@src/components/Settings/SettingsFieldInput' +import { SettingsSection } from '@src/components/Settings/SettingsSection' +import { useDotDotSlash } from '@src/hooks/useDotDotSlash' import { createAndOpenNewTutorialProject, getSettingsFolderPaths, -} from 'lib/desktopFS' -import { useDotDotSlash } from 'hooks/useDotDotSlash' -import { ForwardedRef, forwardRef, useEffect, useMemo } from 'react' -import { useLspContext } from 'components/LspProvider' -import { toSync } from 'lib/utils' -import { reportRejection } from 'lib/trap' -import { openExternalBrowserIfDesktop } from 'lib/openWindow' -import { settingsActor, useSettings } from 'machines/appMachine' -import { useSelector } from '@xstate/react' +} from '@src/lib/desktopFS' +import { isDesktop } from '@src/lib/isDesktop' +import { openExternalBrowserIfDesktop } from '@src/lib/openWindow' +import { PATHS } from '@src/lib/paths' +import type { Setting } from '@src/lib/settings/initialSettings' +import type { + SetEventTypes, + SettingsLevel, +} from '@src/lib/settings/settingsTypes' +import { + shouldHideSetting, + shouldShowSettingInput, +} from '@src/lib/settings/settingsUtils' +import { reportRejection } from '@src/lib/trap' +import { toSync } from '@src/lib/utils' +import { settingsActor, useSettings } from '@src/machines/appMachine' +import { APP_VERSION, IS_NIGHTLY, getReleaseUrl } from '@src/routes/Settings' interface AllSettingsFieldsProps { searchParamTab: SettingsLevel diff --git a/src/components/Settings/KeybindingsSectionsList.tsx b/src/components/Settings/KeybindingsSectionsList.tsx index 11a9ecdad..16ea15b48 100644 --- a/src/components/Settings/KeybindingsSectionsList.tsx +++ b/src/components/Settings/KeybindingsSectionsList.tsx @@ -1,7 +1,7 @@ import { interactionMap, sortInteractionMapByCategory, -} from 'lib/settings/initialKeybindings' +} from '@src/lib/settings/initialKeybindings' interface KeybindingSectionsListProps { scrollRef: React.RefObject diff --git a/src/components/Settings/SettingsFieldInput.tsx b/src/components/Settings/SettingsFieldInput.tsx index 83ae6166c..a70ae59ed 100644 --- a/src/components/Settings/SettingsFieldInput.tsx +++ b/src/components/Settings/SettingsFieldInput.tsx @@ -1,14 +1,15 @@ -import { Toggle } from 'components/Toggle/Toggle' -import { Setting } from 'lib/settings/initialSettings' -import { +import { useMemo } from 'react' +import type { EventFrom } from 'xstate' + +import { Toggle } from '@src/components/Toggle/Toggle' +import type { Setting } from '@src/lib/settings/initialSettings' +import type { SetEventTypes, SettingsLevel, WildcardSetEvent, -} from 'lib/settings/settingsTypes' -import { getSettingInputType } from 'lib/settings/settingsUtils' -import { settingsActor, useSettings } from 'machines/appMachine' -import { useMemo } from 'react' -import { EventFrom } from 'xstate' +} from '@src/lib/settings/settingsTypes' +import { getSettingInputType } from '@src/lib/settings/settingsUtils' +import { settingsActor, useSettings } from '@src/machines/appMachine' interface SettingsFieldInputProps { // We don't need the fancy types here, diff --git a/src/components/Settings/SettingsSearchBar.tsx b/src/components/Settings/SettingsSearchBar.tsx index def38320e..b36429fb3 100644 --- a/src/components/Settings/SettingsSearchBar.tsx +++ b/src/components/Settings/SettingsSearchBar.tsx @@ -1,14 +1,15 @@ import { Combobox } from '@headlessui/react' -import { CustomIcon } from 'components/CustomIcon' import decamelize from 'decamelize' import Fuse from 'fuse.js' -import { interactionMap } from 'lib/settings/initialKeybindings' -import { SettingsLevel } from 'lib/settings/settingsTypes' -import { useSettings } from 'machines/appMachine' import { useEffect, useMemo, useRef, useState } from 'react' import { useHotkeys } from 'react-hotkeys-hook' import { useNavigate } from 'react-router-dom' +import { CustomIcon } from '@src/components/CustomIcon' +import { interactionMap } from '@src/lib/settings/initialKeybindings' +import type { SettingsLevel } from '@src/lib/settings/settingsTypes' +import { useSettings } from '@src/machines/appMachine' + type ExtendedSettingsLevel = SettingsLevel | 'keybindings' export type SettingsSearchItem = { diff --git a/src/components/Settings/SettingsSection.tsx b/src/components/Settings/SettingsSection.tsx index d40ea1c1c..099bd7920 100644 --- a/src/components/Settings/SettingsSection.tsx +++ b/src/components/Settings/SettingsSection.tsx @@ -1,6 +1,6 @@ -import { CustomIcon } from 'components/CustomIcon' -import Tooltip from 'components/Tooltip' -import { SettingsLevel } from 'lib/settings/settingsTypes' +import { CustomIcon } from '@src/components/CustomIcon' +import Tooltip from '@src/components/Tooltip' +import type { SettingsLevel } from '@src/lib/settings/settingsTypes' interface SettingsSectionProps extends React.HTMLProps { title: string diff --git a/src/components/Settings/SettingsSectionsList.tsx b/src/components/Settings/SettingsSectionsList.tsx index 2ce5d750c..f1fbfcfd5 100644 --- a/src/components/Settings/SettingsSectionsList.tsx +++ b/src/components/Settings/SettingsSectionsList.tsx @@ -1,8 +1,9 @@ import decamelize from 'decamelize' -import { Setting } from 'lib/settings/initialSettings' -import { SettingsLevel } from 'lib/settings/settingsTypes' -import { shouldHideSetting } from 'lib/settings/settingsUtils' -import { useSettings } from 'machines/appMachine' + +import type { Setting } from '@src/lib/settings/initialSettings' +import type { SettingsLevel } from '@src/lib/settings/settingsTypes' +import { shouldHideSetting } from '@src/lib/settings/settingsUtils' +import { useSettings } from '@src/machines/appMachine' interface SettingsSectionsListProps { searchParamTab: SettingsLevel diff --git a/src/components/Settings/SettingsTabButton.tsx b/src/components/Settings/SettingsTabButton.tsx index 43cab40f0..f873ab499 100644 --- a/src/components/Settings/SettingsTabButton.tsx +++ b/src/components/Settings/SettingsTabButton.tsx @@ -1,4 +1,5 @@ -import { CustomIcon, CustomIconName } from 'components/CustomIcon' +import type { CustomIconName } from '@src/components/CustomIcon' +import { CustomIcon } from '@src/components/CustomIcon' interface SettingsTabButtonProps { checked: boolean diff --git a/src/components/Settings/SettingsTabs.tsx b/src/components/Settings/SettingsTabs.tsx index 2b93cb40b..3f372d8ac 100644 --- a/src/components/Settings/SettingsTabs.tsx +++ b/src/components/Settings/SettingsTabs.tsx @@ -1,5 +1,6 @@ import { RadioGroup } from '@headlessui/react' -import { SettingsTabButton } from './SettingsTabButton' + +import { SettingsTabButton } from '@src/components/Settings/SettingsTabButton' interface SettingsTabButtonProps { value: string diff --git a/src/components/Spinner.tsx b/src/components/Spinner.tsx index 63a5b356f..539511f0c 100644 --- a/src/components/Spinner.tsx +++ b/src/components/Spinner.tsx @@ -1,4 +1,4 @@ -import { SVGProps } from 'react' +import type { SVGProps } from 'react' export const Spinner = (props: SVGProps) => { return ( diff --git a/src/components/Stream.tsx b/src/components/Stream.tsx index f64b45431..500c4e9b7 100644 --- a/src/components/Stream.tsx +++ b/src/components/Stream.tsx @@ -1,26 +1,32 @@ -import { MouseEventHandler, useEffect, useRef, useState } from 'react' -import Loading from './Loading' -import { useModelingContext } from 'hooks/useModelingContext' -import { useNetworkContext } from 'hooks/useNetworkContext' -import { NetworkHealthState } from 'hooks/useNetworkStatus' -import { ClientSideScene } from 'clientSideScene/ClientSideSceneComp' -import { btnName } from 'lib/cameraControls' -import { sendSelectEventToEngine } from 'lib/selections' -import { kclManager, engineCommandManager, sceneInfra } from 'lib/singletons' -import { useAppStream } from 'AppState' +import { useAppStream } from '@src/AppState' +import type { MouseEventHandler } from 'react' +import { useEffect, useRef, useState } from 'react' +import { useRouteLoaderData } from 'react-router-dom' + +import { ClientSideScene } from '@src/clientSideScene/ClientSideSceneComp' +import Loading from '@src/components/Loading' +import { ViewControlContextMenu } from '@src/components/ViewControlMenu' +import { useModelingContext } from '@src/hooks/useModelingContext' +import { useNetworkContext } from '@src/hooks/useNetworkContext' +import { NetworkHealthState } from '@src/hooks/useNetworkStatus' +import { getArtifactOfTypes } from '@src/lang/std/artifactGraph' import { + DisconnectingType, EngineCommandManagerEvents, EngineConnectionStateType, - DisconnectingType, -} from 'lang/std/engineConnection' -import { useRouteLoaderData } from 'react-router-dom' -import { PATHS } from 'lib/paths' -import { IndexLoaderData } from 'lib/types' -import { err, reportRejection } from 'lib/trap' -import { getArtifactOfTypes } from 'lang/std/artifactGraph' -import { ViewControlContextMenu } from './ViewControlMenu' -import { useCommandBarState } from 'machines/commandBarMachine' -import { useSettings } from 'machines/appMachine' +} from '@src/lang/std/engineConnection' +import { btnName } from '@src/lib/cameraControls' +import { PATHS } from '@src/lib/paths' +import { sendSelectEventToEngine } from '@src/lib/selections' +import { + engineCommandManager, + kclManager, + sceneInfra, +} from '@src/lib/singletons' +import { err, reportRejection } from '@src/lib/trap' +import type { IndexLoaderData } from '@src/lib/types' +import { useSettings } from '@src/machines/appMachine' +import { useCommandBarState } from '@src/machines/commandBarMachine' enum StreamState { Playing = 'playing', diff --git a/src/components/TelemetryExplorer.tsx b/src/components/TelemetryExplorer.tsx index 24c0d85a2..1ff2c085c 100644 --- a/src/components/TelemetryExplorer.tsx +++ b/src/components/TelemetryExplorer.tsx @@ -1,11 +1,10 @@ -import { getMarks } from 'lib/performance' - +import { getMarks } from '@src/lib/performance' import { printDeltaTotal, printInvocationCount, printMarkDownTable, printRawMarks, -} from 'lib/telemetry' +} from '@src/lib/telemetry' export function TelemetryExplorer() { const marks = getMarks() diff --git a/src/components/ToastTextToCad.tsx b/src/components/ToastTextToCad.tsx index 899e60261..56875a123 100644 --- a/src/components/ToastTextToCad.tsx +++ b/src/components/ToastTextToCad.tsx @@ -1,14 +1,10 @@ -import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader' -import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls' -import { useFileContext } from 'hooks/useFileContext' -import { isDesktop } from 'lib/isDesktop' -import { PATHS } from 'lib/paths' -import toast from 'react-hot-toast' -import { - TextToCad_type, +import type { TextToCadIteration_type, + TextToCad_type, } from '@kittycad/lib/dist/types/src/models' import { useCallback, useEffect, useRef, useState } from 'react' +import toast from 'react-hot-toast' +import type { Mesh } from 'three' import { Box3, Color, @@ -16,24 +12,29 @@ import { EdgesGeometry, LineBasicMaterial, LineSegments, - Mesh, MeshBasicMaterial, OrthographicCamera, Scene, Vector3, WebGLRenderer, } from 'three' +import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls' import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader' -import { base64Decode } from 'lang/wasm' -import { sendTelemetry } from 'lib/textToCad' -import { Themes } from 'lib/theme' -import { ActionButton } from './ActionButton' -import { commandBarActor } from 'machines/commandBarMachine' -import { EventFrom } from 'xstate' -import { fileMachine } from 'machines/fileMachine' -import { reportRejection } from 'lib/trap' -import { codeManager, kclManager } from 'lib/singletons' -import { openExternalBrowserIfDesktop } from 'lib/openWindow' +import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader' +import type { EventFrom } from 'xstate' + +import { ActionButton } from '@src/components/ActionButton' +import type { useFileContext } from '@src/hooks/useFileContext' +import { base64Decode } from '@src/lang/wasm' +import { isDesktop } from '@src/lib/isDesktop' +import { openExternalBrowserIfDesktop } from '@src/lib/openWindow' +import { PATHS } from '@src/lib/paths' +import { codeManager, kclManager } from '@src/lib/singletons' +import { sendTelemetry } from '@src/lib/textToCad' +import type { Themes } from '@src/lib/theme' +import { reportRejection } from '@src/lib/trap' +import { commandBarActor } from '@src/machines/commandBarMachine' +import type { fileMachine } from '@src/machines/fileMachine' const CANVAS_SIZE = 128 const PROMPT_TRUNCATE_LENGTH = 128 diff --git a/src/components/ToastUpdate.test.tsx b/src/components/ToastUpdate.test.tsx index 8b141e701..f372528a1 100644 --- a/src/components/ToastUpdate.test.tsx +++ b/src/components/ToastUpdate.test.tsx @@ -1,6 +1,7 @@ import { fireEvent, render, screen } from '@testing-library/react' import { vi } from 'vitest' -import { ToastUpdate } from './ToastUpdate' + +import { ToastUpdate } from '@src/components/ToastUpdate' describe('ToastUpdate tests', () => { const testData = { diff --git a/src/components/ToastUpdate.tsx b/src/components/ToastUpdate.tsx index fbedc03fd..208ade576 100644 --- a/src/components/ToastUpdate.tsx +++ b/src/components/ToastUpdate.tsx @@ -1,9 +1,11 @@ +import type { MarkedOptions } from '@ts-stack/markdown' +import { Marked, escape, unescape } from '@ts-stack/markdown' import toast from 'react-hot-toast' -import { ActionButton } from './ActionButton' -import { openExternalBrowserIfDesktop } from 'lib/openWindow' -import { escape, Marked, MarkedOptions, unescape } from '@ts-stack/markdown' -import { getReleaseUrl } from 'routes/Settings' -import { SafeRenderer } from 'lib/markdown' + +import { ActionButton } from '@src/components/ActionButton' +import { SafeRenderer } from '@src/lib/markdown' +import { openExternalBrowserIfDesktop } from '@src/lib/openWindow' +import { getReleaseUrl } from '@src/routes/Settings' export function ToastUpdate({ version, diff --git a/src/components/Toolbar/EqualAngle.tsx b/src/components/Toolbar/EqualAngle.tsx index c2ba55c46..190042688 100644 --- a/src/components/Toolbar/EqualAngle.tsx +++ b/src/components/Toolbar/EqualAngle.tsx @@ -1,17 +1,17 @@ -import { toolTips } from 'lang/langHelpers' -import { Selections } from 'lib/selections' -import { Program, Expr, VariableDeclarator } from '../../lang/wasm' -import { getNodeFromPath } from '../../lang/queryAst' -import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils' -import { isSketchVariablesLinked } from '../../lang/std/sketchConstraints' +import { toolTips } from '@src/lang/langHelpers' +import { getNodeFromPath } from '@src/lang/queryAst' +import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils' +import { isSketchVariablesLinked } from '@src/lang/std/sketchConstraints' +import type { PathToNodeMap } from '@src/lang/std/sketchcombos' import { - transformSecondarySketchLinesTagFirst, getTransformInfos, - PathToNodeMap, -} from '../../lang/std/sketchcombos' -import { kclManager } from 'lib/singletons' -import { err } from 'lib/trap' -import { TransformInfo } from 'lang/std/stdTypes' + transformSecondarySketchLinesTagFirst, +} from '@src/lang/std/sketchcombos' +import type { TransformInfo } from '@src/lang/std/stdTypes' +import type { Expr, Program, VariableDeclarator } from '@src/lang/wasm' +import type { Selections } from '@src/lib/selections' +import { kclManager } from '@src/lib/singletons' +import { err } from '@src/lib/trap' export function equalAngleInfo({ selectionRanges, diff --git a/src/components/Toolbar/EqualLength.tsx b/src/components/Toolbar/EqualLength.tsx index 9d41df1f0..0460f7b16 100644 --- a/src/components/Toolbar/EqualLength.tsx +++ b/src/components/Toolbar/EqualLength.tsx @@ -1,17 +1,18 @@ -import { toolTips } from 'lang/langHelpers' -import { Selections } from 'lib/selections' -import { Program, Expr, VariableDeclarator } from '../../lang/wasm' -import { getNodeFromPath } from '../../lang/queryAst' -import { isSketchVariablesLinked } from '../../lang/std/sketchConstraints' +import type { Node } from '@rust/kcl-lib/bindings/Node' + +import { toolTips } from '@src/lang/langHelpers' +import { getNodeFromPath } from '@src/lang/queryAst' +import { isSketchVariablesLinked } from '@src/lang/std/sketchConstraints' +import type { PathToNodeMap } from '@src/lang/std/sketchcombos' import { - transformSecondarySketchLinesTagFirst, getTransformInfos, - PathToNodeMap, -} from '../../lang/std/sketchcombos' -import { TransformInfo } from 'lang/std/stdTypes' -import { kclManager } from 'lib/singletons' -import { err } from 'lib/trap' -import { Node } from '@rust/kcl-lib/bindings/Node' + transformSecondarySketchLinesTagFirst, +} from '@src/lang/std/sketchcombos' +import type { TransformInfo } from '@src/lang/std/stdTypes' +import type { Expr, Program, VariableDeclarator } from '@src/lang/wasm' +import type { Selections } from '@src/lib/selections' +import { kclManager } from '@src/lib/singletons' +import { err } from '@src/lib/trap' export function setEqualLengthInfo({ selectionRanges, diff --git a/src/components/Toolbar/HorzVert.tsx b/src/components/Toolbar/HorzVert.tsx index 46593d009..8c744a7b9 100644 --- a/src/components/Toolbar/HorzVert.tsx +++ b/src/components/Toolbar/HorzVert.tsx @@ -1,16 +1,17 @@ -import { toolTips } from 'lang/langHelpers' -import { Selections } from 'lib/selections' -import { Program, Expr, VariableMap } from '../../lang/wasm' -import { getNodeFromPath } from '../../lang/queryAst' +import type { Node } from '@rust/kcl-lib/bindings/Node' + +import { toolTips } from '@src/lang/langHelpers' +import { getNodeFromPath } from '@src/lang/queryAst' +import type { PathToNodeMap } from '@src/lang/std/sketchcombos' import { - PathToNodeMap, getTransformInfos, transformAstSketchLines, -} from '../../lang/std/sketchcombos' -import { TransformInfo } from 'lang/std/stdTypes' -import { kclManager } from 'lib/singletons' -import { err } from 'lib/trap' -import { Node } from '@rust/kcl-lib/bindings/Node' +} from '@src/lang/std/sketchcombos' +import type { TransformInfo } from '@src/lang/std/stdTypes' +import type { Expr, Program, VariableMap } from '@src/lang/wasm' +import type { Selections } from '@src/lib/selections' +import { kclManager } from '@src/lib/singletons' +import { err } from '@src/lib/trap' export function horzVertInfo( selectionRanges: Selections, diff --git a/src/components/Toolbar/Intersect.tsx b/src/components/Toolbar/Intersect.tsx index 762fd38cd..de0b6f764 100644 --- a/src/components/Toolbar/Intersect.tsx +++ b/src/components/Toolbar/Intersect.tsx @@ -1,24 +1,28 @@ -import { toolTips } from 'lang/langHelpers' -import { Program, Expr, VariableDeclarator } from '../../lang/wasm' -import { Selections } from 'lib/selections' +import type { Node } from '@rust/kcl-lib/bindings/Node' + +import { removeDoubleNegatives } from '@src/components/AvailableVarsHelpers' +import { + GetInfoModal, + createInfoModal, +} from '@src/components/SetHorVertDistanceModal' +import { createVariableDeclaration } from '@src/lang/create' +import { toolTips } from '@src/lang/langHelpers' import { getNodeFromPath, isLinesParallelAndConstrained, -} from '../../lang/queryAst' -import { isSketchVariablesLinked } from '../../lang/std/sketchConstraints' +} from '@src/lang/queryAst' +import { isSketchVariablesLinked } from '@src/lang/std/sketchConstraints' +import type { PathToNodeMap } from '@src/lang/std/sketchcombos' import { - transformSecondarySketchLinesTagFirst, getTransformInfos, - PathToNodeMap, isExprBinaryPart, -} from '../../lang/std/sketchcombos' -import { TransformInfo } from 'lang/std/stdTypes' -import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal' -import { createVariableDeclaration } from '../../lang/modifyAst' -import { removeDoubleNegatives } from '../AvailableVarsHelpers' -import { kclManager } from 'lib/singletons' -import { err } from 'lib/trap' -import { Node } from '@rust/kcl-lib/bindings/Node' + transformSecondarySketchLinesTagFirst, +} from '@src/lang/std/sketchcombos' +import type { TransformInfo } from '@src/lang/std/stdTypes' +import type { Expr, Program, VariableDeclarator } from '@src/lang/wasm' +import type { Selections } from '@src/lib/selections' +import { kclManager } from '@src/lib/singletons' +import { err } from '@src/lib/trap' const getModalInfo = createInfoModal(GetInfoModal) diff --git a/src/components/Toolbar/RemoveConstrainingValues.tsx b/src/components/Toolbar/RemoveConstrainingValues.tsx index 94fd626c2..b2d99f004 100644 --- a/src/components/Toolbar/RemoveConstrainingValues.tsx +++ b/src/components/Toolbar/RemoveConstrainingValues.tsx @@ -1,17 +1,19 @@ -import { toolTips } from 'lang/langHelpers' -import { Selection, Selections } from 'lib/selections' -import { PathToNode, Program, Expr, topLevelRange } from '../../lang/wasm' -import { getNodeFromPath } from '../../lang/queryAst' +import type { Node } from '@rust/kcl-lib/bindings/Node' + +import { toolTips } from '@src/lang/langHelpers' +import { getNodeFromPath } from '@src/lang/queryAst' +import { codeRefFromRange } from '@src/lang/std/artifactGraph' +import type { PathToNodeMap } from '@src/lang/std/sketchcombos' import { - PathToNodeMap, getRemoveConstraintsTransforms, transformAstSketchLines, -} from '../../lang/std/sketchcombos' -import { TransformInfo } from 'lang/std/stdTypes' -import { kclManager } from 'lib/singletons' -import { err } from 'lib/trap' -import { Node } from '@rust/kcl-lib/bindings/Node' -import { codeRefFromRange } from 'lang/std/artifactGraph' +} from '@src/lang/std/sketchcombos' +import type { TransformInfo } from '@src/lang/std/stdTypes' +import { topLevelRange } from '@src/lang/util' +import type { Expr, PathToNode, Program } from '@src/lang/wasm' +import type { Selection, Selections } from '@src/lib/selections' +import { kclManager } from '@src/lib/singletons' +import { err } from '@src/lib/trap' export function removeConstrainingValuesInfo({ selectionRanges, diff --git a/src/components/Toolbar/SetAbsDistance.tsx b/src/components/Toolbar/SetAbsDistance.tsx index 4efeb7fff..3aa97dbb3 100644 --- a/src/components/Toolbar/SetAbsDistance.tsx +++ b/src/components/Toolbar/SetAbsDistance.tsx @@ -1,23 +1,24 @@ -import { toolTips } from 'lang/langHelpers' -import { Program, Expr } from '../../lang/wasm' -import { Selections } from 'lib/selections' -import { getNodeFromPath } from '../../lang/queryAst' -import { - getTransformInfos, - transformAstSketchLines, - PathToNodeMap, - isExprBinaryPart, -} from '../../lang/std/sketchcombos' -import { TransformInfo } from 'lang/std/stdTypes' +import type { Node } from '@rust/kcl-lib/bindings/Node' + +import { removeDoubleNegatives } from '@src/components/AvailableVarsHelpers' import { SetAngleLengthModal, createSetAngleLengthModal, -} from '../SetAngleLengthModal' -import { createName, createVariableDeclaration } from '../../lang/modifyAst' -import { removeDoubleNegatives } from '../AvailableVarsHelpers' -import { kclManager } from 'lib/singletons' -import { err } from 'lib/trap' -import { Node } from '@rust/kcl-lib/bindings/Node' +} from '@src/components/SetAngleLengthModal' +import { createName, createVariableDeclaration } from '@src/lang/create' +import { toolTips } from '@src/lang/langHelpers' +import { getNodeFromPath } from '@src/lang/queryAst' +import type { PathToNodeMap } from '@src/lang/std/sketchcombos' +import { + getTransformInfos, + isExprBinaryPart, + transformAstSketchLines, +} from '@src/lang/std/sketchcombos' +import type { TransformInfo } from '@src/lang/std/stdTypes' +import type { Expr, Program } from '@src/lang/wasm' +import type { Selections } from '@src/lib/selections' +import { kclManager } from '@src/lib/singletons' +import { err } from '@src/lib/trap' const getModalInfo = createSetAngleLengthModal(SetAngleLengthModal) @@ -39,8 +40,8 @@ export function absDistanceInfo({ constraint === 'xAbs' || constraint === 'yAbs' ? constraint : constraint === 'snapToYAxis' - ? 'xAbs' - : 'yAbs' + ? 'xAbs' + : 'yAbs' const _nodes = selectionRanges.graphSelections.map(({ codeRef }) => { const tmp = getNodeFromPath(kclManager.ast, codeRef.pathToNode, [ 'CallExpression', diff --git a/src/components/Toolbar/SetAngleBetween.tsx b/src/components/Toolbar/SetAngleBetween.tsx index 7bd192889..1046cc1ba 100644 --- a/src/components/Toolbar/SetAngleBetween.tsx +++ b/src/components/Toolbar/SetAngleBetween.tsx @@ -1,20 +1,23 @@ -import { toolTips } from 'lang/langHelpers' -import { Program, Expr, VariableDeclarator } from '../../lang/wasm' -import { Selections } from 'lib/selections' -import { getNodeFromPath } from '../../lang/queryAst' -import { isSketchVariablesLinked } from '../../lang/std/sketchConstraints' +import { removeDoubleNegatives } from '@src/components/AvailableVarsHelpers' +import { + GetInfoModal, + createInfoModal, +} from '@src/components/SetHorVertDistanceModal' +import { createVariableDeclaration } from '@src/lang/create' +import { toolTips } from '@src/lang/langHelpers' +import { getNodeFromPath } from '@src/lang/queryAst' +import { isSketchVariablesLinked } from '@src/lang/std/sketchConstraints' +import type { PathToNodeMap } from '@src/lang/std/sketchcombos' import { - transformSecondarySketchLinesTagFirst, getTransformInfos, - PathToNodeMap, isExprBinaryPart, -} from '../../lang/std/sketchcombos' -import { TransformInfo } from 'lang/std/stdTypes' -import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal' -import { createVariableDeclaration } from '../../lang/modifyAst' -import { removeDoubleNegatives } from '../AvailableVarsHelpers' -import { kclManager } from 'lib/singletons' -import { err } from 'lib/trap' + transformSecondarySketchLinesTagFirst, +} from '@src/lang/std/sketchcombos' +import type { TransformInfo } from '@src/lang/std/stdTypes' +import type { Expr, Program, VariableDeclarator } from '@src/lang/wasm' +import type { Selections } from '@src/lib/selections' +import { kclManager } from '@src/lib/singletons' +import { err } from '@src/lib/trap' const getModalInfo = createInfoModal(GetInfoModal) diff --git a/src/components/Toolbar/SetHorzVertDistance.tsx b/src/components/Toolbar/SetHorzVertDistance.tsx index 7a85abb49..d18e023ee 100644 --- a/src/components/Toolbar/SetHorzVertDistance.tsx +++ b/src/components/Toolbar/SetHorzVertDistance.tsx @@ -1,21 +1,25 @@ -import { toolTips } from 'lang/langHelpers' -import { Program, Expr, VariableDeclarator } from '../../lang/wasm' -import { getNodeFromPath } from '../../lang/queryAst' -import { isSketchVariablesLinked } from '../../lang/std/sketchConstraints' +import type { Node } from '@rust/kcl-lib/bindings/Node' + +import { removeDoubleNegatives } from '@src/components/AvailableVarsHelpers' +import { + GetInfoModal, + createInfoModal, +} from '@src/components/SetHorVertDistanceModal' +import { createLiteral, createVariableDeclaration } from '@src/lang/create' +import { toolTips } from '@src/lang/langHelpers' +import { getNodeFromPath } from '@src/lang/queryAst' +import { isSketchVariablesLinked } from '@src/lang/std/sketchConstraints' +import type { PathToNodeMap } from '@src/lang/std/sketchcombos' import { - transformSecondarySketchLinesTagFirst, getTransformInfos, - PathToNodeMap, isExprBinaryPart, -} from '../../lang/std/sketchcombos' -import { TransformInfo } from 'lang/std/stdTypes' -import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal' -import { createLiteral, createVariableDeclaration } from '../../lang/modifyAst' -import { removeDoubleNegatives } from '../AvailableVarsHelpers' -import { kclManager } from 'lib/singletons' -import { Selections } from 'lib/selections' -import { cleanErrs, err } from 'lib/trap' -import { Node } from '@rust/kcl-lib/bindings/Node' + transformSecondarySketchLinesTagFirst, +} from '@src/lang/std/sketchcombos' +import type { TransformInfo } from '@src/lang/std/stdTypes' +import type { Expr, Program, VariableDeclarator } from '@src/lang/wasm' +import type { Selections } from '@src/lib/selections' +import { kclManager } from '@src/lib/singletons' +import { cleanErrs, err } from '@src/lib/trap' const getModalInfo = createInfoModal(GetInfoModal) diff --git a/src/components/Toolbar/angleLengthInfo.ts b/src/components/Toolbar/angleLengthInfo.ts new file mode 100644 index 000000000..a1d52cdb2 --- /dev/null +++ b/src/components/Toolbar/angleLengthInfo.ts @@ -0,0 +1,57 @@ +import { toolTips } from '@src/lang/langHelpers' +import { getNodeFromPath } from '@src/lang/queryAst' +import { getTransformInfos } from '@src/lang/std/sketchcombos' +import type { TransformInfo } from '@src/lang/std/stdTypes' +import type { Expr } from '@src/lang/wasm' +import type { Selections } from '@src/lib/selections' +import { kclManager } from '@src/lib/singletons' +import { err } from '@src/lib/trap' + +export function angleLengthInfo({ + selectionRanges, + angleOrLength = 'setLength', +}: { + selectionRanges: Selections + angleOrLength?: 'setLength' | 'setAngle' +}): + | { + transforms: TransformInfo[] + enabled: boolean + } + | Error { + const nodes = selectionRanges.graphSelections.map(({ codeRef }) => + getNodeFromPath(kclManager.ast, codeRef.pathToNode, [ + 'CallExpression', + 'CallExpressionKw', + ]) + ) + const _err1 = nodes.find(err) + if (_err1 instanceof Error) return _err1 + + const isAllTooltips = nodes.every((meta) => { + if (err(meta)) return false + return ( + (meta.node?.type === 'CallExpressionKw' || + meta.node?.type === 'CallExpression') && + toolTips.includes(meta.node.callee.name.name as any) + ) + }) + + const transforms = getTransformInfos( + selectionRanges, + kclManager.ast, + angleOrLength + ) + const enabled = + selectionRanges.graphSelections.length <= 1 && + isAllTooltips && + transforms.every(Boolean) + console.log( + 'enabled', + enabled, + selectionRanges.graphSelections.length, + isAllTooltips, + transforms.every(Boolean) + ) + return { enabled, transforms } +} diff --git a/src/components/Toolbar/setAngleLength.tsx b/src/components/Toolbar/setAngleLength.tsx index ac04f093c..5111b9362 100644 --- a/src/components/Toolbar/setAngleLength.tsx +++ b/src/components/Toolbar/setAngleLength.tsx @@ -1,74 +1,29 @@ -import { toolTips } from 'lang/langHelpers' -import { Program, Expr } from '../../lang/wasm' -import { Selections } from 'lib/selections' -import { getNodeFromPath } from '../../lang/queryAst' -import { - PathToNodeMap, - getTransformInfos, - isExprBinaryPart, - transformAstSketchLines, -} from '../../lang/std/sketchcombos' -import { TransformInfo } from 'lang/std/stdTypes' +import { removeDoubleNegatives } from '@src/components/AvailableVarsHelpers' import { SetAngleLengthModal, createSetAngleLengthModal, -} from '../SetAngleLengthModal' +} from '@src/components/SetAngleLengthModal' +import { angleLengthInfo } from '@src/components/Toolbar/angleLengthInfo' import { createBinaryExpressionWithUnary, createLocalName, createName, createVariableDeclaration, -} from '../../lang/modifyAst' -import { removeDoubleNegatives } from '../AvailableVarsHelpers' -import { normaliseAngle } from '../../lib/utils' -import { kclManager } from 'lib/singletons' -import { err } from 'lib/trap' -import { KclCommandValue } from 'lib/commandTypes' +} from '@src/lang/create' +import type { PathToNodeMap } from '@src/lang/std/sketchcombos' +import { + isExprBinaryPart, + transformAstSketchLines, +} from '@src/lang/std/sketchcombos' +import type { Expr, Program } from '@src/lang/wasm' +import type { KclCommandValue } from '@src/lib/commandTypes' +import type { Selections } from '@src/lib/selections' +import { kclManager } from '@src/lib/singletons' +import { err } from '@src/lib/trap' +import { normaliseAngle } from '@src/lib/utils' const getModalInfo = createSetAngleLengthModal(SetAngleLengthModal) -export function angleLengthInfo({ - selectionRanges, - angleOrLength = 'setLength', -}: { - selectionRanges: Selections - angleOrLength?: 'setLength' | 'setAngle' -}): - | { - transforms: TransformInfo[] - enabled: boolean - } - | Error { - const nodes = selectionRanges.graphSelections.map(({ codeRef }) => - getNodeFromPath(kclManager.ast, codeRef.pathToNode, [ - 'CallExpression', - 'CallExpressionKw', - ]) - ) - const _err1 = nodes.find(err) - if (_err1 instanceof Error) return _err1 - - const isAllTooltips = nodes.every((meta) => { - if (err(meta)) return false - return ( - (meta.node?.type === 'CallExpressionKw' || - meta.node?.type === 'CallExpression') && - toolTips.includes(meta.node.callee.name.name as any) - ) - }) - - const transforms = getTransformInfos( - selectionRanges, - kclManager.ast, - angleOrLength - ) - const enabled = - selectionRanges.graphSelections.length <= 1 && - isAllTooltips && - transforms.every(Boolean) - return { enabled, transforms } -} - export async function applyConstraintLength({ length, selectionRanges, diff --git a/src/components/Tooltip.module.css b/src/components/Tooltip.module.css index 033f2c7a5..896f9adc7 100644 --- a/src/components/Tooltip.module.css +++ b/src/components/Tooltip.module.css @@ -20,7 +20,9 @@ /* The parts that will be transitioned */ opacity: 0; transform: translate(var(--_x, 0), var(--_y, 0)); - transition: transform 0.15s ease-out, opacity 0.11s ease-out; + transition: + transform 0.15s ease-out, + opacity 0.11s ease-out; } .tooltip { diff --git a/src/components/UnitsMenu.tsx b/src/components/UnitsMenu.tsx index 6be30d721..28addb6f2 100644 --- a/src/components/UnitsMenu.tsx +++ b/src/components/UnitsMenu.tsx @@ -1,16 +1,19 @@ import { Popover } from '@headlessui/react' +import { useEffect, useState } from 'react' +import toast from 'react-hot-toast' + import { changeKclSettings, unitAngleToUnitAng, unitLengthToUnitLen, -} from 'lang/wasm' -import { DEFAULT_DEFAULT_ANGLE_UNIT } from 'lib/constants' -import { DEFAULT_DEFAULT_LENGTH_UNIT } from 'lib/constants' -import { baseUnitLabels, baseUnitsUnion } from 'lib/settings/settingsTypes' -import { codeManager, kclManager } from 'lib/singletons' -import { err, reportRejection } from 'lib/trap' -import { useEffect, useState } from 'react' -import toast from 'react-hot-toast' +} from '@src/lang/wasm' +import { + DEFAULT_DEFAULT_ANGLE_UNIT, + DEFAULT_DEFAULT_LENGTH_UNIT, +} from '@src/lib/constants' +import { baseUnitLabels, baseUnitsUnion } from '@src/lib/settings/settingsTypes' +import { codeManager, kclManager } from '@src/lib/singletons' +import { err, reportRejection } from '@src/lib/trap' export function UnitsMenu() { const [fileSettings, setFileSettings] = useState(kclManager.fileSettings) diff --git a/src/components/UpdaterRestartModal.test.tsx b/src/components/UpdaterRestartModal.test.tsx index c177bbab8..a97b9cc7d 100644 --- a/src/components/UpdaterRestartModal.test.tsx +++ b/src/components/UpdaterRestartModal.test.tsx @@ -1,6 +1,7 @@ import { fireEvent, render, screen } from '@testing-library/react' import { vi } from 'vitest' -import { UpdaterRestartModal } from './UpdaterRestartModal' + +import { UpdaterRestartModal } from '@src/components/UpdaterRestartModal' describe('UpdaterRestartModal tests', () => { test('Renders the modal', () => { diff --git a/src/components/UpdaterRestartModal.tsx b/src/components/UpdaterRestartModal.tsx index 0be9f82f8..7901c080b 100644 --- a/src/components/UpdaterRestartModal.tsx +++ b/src/components/UpdaterRestartModal.tsx @@ -1,5 +1,7 @@ -import { create, InstanceProps } from 'react-modal-promise' -import { ActionButton } from './ActionButton' +import type { InstanceProps } from 'react-modal-promise' +import { create } from 'react-modal-promise' + +import { ActionButton } from '@src/components/ActionButton' type ModalResolve = { wantRestart: boolean diff --git a/src/components/UserSidebarMenu.test.tsx b/src/components/UserSidebarMenu.test.tsx index afa3ebd94..e0870d14d 100644 --- a/src/components/UserSidebarMenu.test.tsx +++ b/src/components/UserSidebarMenu.test.tsx @@ -1,12 +1,13 @@ +import type { Models } from '@kittycad/lib' import { fireEvent, render, screen, waitFor } from '@testing-library/react' -import UserSidebarMenu from './UserSidebarMenu' import { Route, RouterProvider, createMemoryRouter, createRoutesFromElements, } from 'react-router-dom' -import { Models } from '@kittycad/lib' + +import UserSidebarMenu from '@src/components/UserSidebarMenu' type User = Models['User_type'] diff --git a/src/components/UserSidebarMenu.tsx b/src/components/UserSidebarMenu.tsx index 8f76a9f4f..685bf21f7 100644 --- a/src/components/UserSidebarMenu.tsx +++ b/src/components/UserSidebarMenu.tsx @@ -1,15 +1,17 @@ import { Popover, Transition } from '@headlessui/react' -import { ActionButton, ActionButtonProps } from './ActionButton' -import { useLocation, useNavigate } from 'react-router-dom' +import type { Models } from '@kittycad/lib' import { Fragment, useMemo, useState } from 'react' -import { PATHS } from 'lib/paths' -import { Models } from '@kittycad/lib' -import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath' -import Tooltip from './Tooltip' -import usePlatform from 'hooks/usePlatform' -import { isDesktop } from 'lib/isDesktop' -import { CustomIcon } from './CustomIcon' -import { authActor } from 'machines/appMachine' +import { useLocation, useNavigate } from 'react-router-dom' + +import type { ActionButtonProps } from '@src/components/ActionButton' +import { ActionButton } from '@src/components/ActionButton' +import { CustomIcon } from '@src/components/CustomIcon' +import Tooltip from '@src/components/Tooltip' +import { useAbsoluteFilePath } from '@src/hooks/useAbsoluteFilePath' +import usePlatform from '@src/hooks/usePlatform' +import { isDesktop } from '@src/lib/isDesktop' +import { PATHS } from '@src/lib/paths' +import { authActor } from '@src/machines/appMachine' type User = Models['User_type'] diff --git a/src/components/ViewControlMenu.tsx b/src/components/ViewControlMenu.tsx index 4e9c8c5bd..123131beb 100644 --- a/src/components/ViewControlMenu.tsx +++ b/src/components/ViewControlMenu.tsx @@ -1,16 +1,18 @@ -import { reportRejection } from 'lib/trap' +import { useMemo } from 'react' + +import type { ContextMenuProps } from '@src/components/ContextMenu' import { ContextMenu, ContextMenuDivider, ContextMenuItem, ContextMenuItemRefresh, - ContextMenuProps, -} from './ContextMenu' -import { AxisNames, VIEW_NAMES_SEMANTIC } from 'lib/constants' -import { useModelingContext } from 'hooks/useModelingContext' -import { useMemo } from 'react' -import { sceneInfra } from 'lib/singletons' -import { useSettings } from 'machines/appMachine' +} from '@src/components/ContextMenu' +import { useModelingContext } from '@src/hooks/useModelingContext' +import type { AxisNames } from '@src/lib/constants' +import { VIEW_NAMES_SEMANTIC } from '@src/lib/constants' +import { sceneInfra } from '@src/lib/singletons' +import { reportRejection } from '@src/lib/trap' +import { useSettings } from '@src/machines/appMachine' export function useViewControlMenuItems() { const { state: modelingState, send: modelingSend } = useModelingContext() diff --git a/src/components/WasmErrBanner.tsx b/src/components/WasmErrBanner.tsx index 67bc3fdd3..ff36422ff 100644 --- a/src/components/WasmErrBanner.tsx +++ b/src/components/WasmErrBanner.tsx @@ -1,7 +1,8 @@ import { Dialog } from '@headlessui/react' import { useState } from 'react' -import { ActionButton } from './ActionButton' -import { useKclContext } from 'lang/KclProvider' + +import { ActionButton } from '@src/components/ActionButton' +import { useKclContext } from '@src/lang/KclProvider' export function WasmErrBanner() { const [isBannerDismissed, setBannerDismissed] = useState(false) diff --git a/src/editor/highlightextension.ts b/src/editor/highlightextension.ts index 90df5deb5..c6b6220e9 100644 --- a/src/editor/highlightextension.ts +++ b/src/editor/highlightextension.ts @@ -1,5 +1,5 @@ -import { StateField, StateEffect, Annotation } from '@codemirror/state' -import { EditorView, Decoration } from '@codemirror/view' +import { Annotation, StateEffect, StateField } from '@codemirror/state' +import { Decoration, EditorView } from '@codemirror/view' export { EditorView } diff --git a/src/editor/manager.test.ts b/src/editor/manager.test.ts index d214ba5ec..471481ed0 100644 --- a/src/editor/manager.test.ts +++ b/src/editor/manager.test.ts @@ -1,5 +1,6 @@ -import { editorManager } from 'lib/singletons' -import { Diagnostic } from '@codemirror/lint' +import type { Diagnostic } from '@codemirror/lint' + +import { editorManager } from '@src/lib/singletons' describe('EditorManager Class', () => { describe('makeUniqueDiagnostics', () => { diff --git a/src/editor/manager.ts b/src/editor/manager.ts index 8b97d97cf..b6492bf53 100644 --- a/src/editor/manager.ts +++ b/src/editor/manager.ts @@ -1,19 +1,25 @@ -import { EditorView, ViewUpdate } from '@codemirror/view' +import { redo, undo } from '@codemirror/commands' import { syntaxTree } from '@codemirror/language' -import { EditorSelection, Annotation, Transaction } from '@codemirror/state' -import { engineCommandManager, kclManager } from 'lib/singletons' -import { modelingMachine, ModelingMachineEvent } from 'machines/modelingMachine' -import { Selections, Selection, processCodeMirrorRanges } from 'lib/selections' -import { undo, redo } from '@codemirror/commands' -import { addLineHighlight, addLineHighlightEvent } from './highlightextension' +import type { Diagnostic } from '@codemirror/lint' +import { forEachDiagnostic, setDiagnosticsEffect } from '@codemirror/lint' +import { Annotation, EditorSelection, Transaction } from '@codemirror/state' +import type { ViewUpdate } from '@codemirror/view' +import { EditorView } from '@codemirror/view' +import type { StateFrom } from 'xstate' + import { - Diagnostic, - forEachDiagnostic, - setDiagnosticsEffect, -} from '@codemirror/lint' -import { StateFrom } from 'xstate' -import { markOnce } from 'lib/performance' -import { kclEditorActor } from 'machines/kclEditorMachine' + addLineHighlight, + addLineHighlightEvent, +} from '@src/editor/highlightextension' +import { markOnce } from '@src/lib/performance' +import type { Selection, Selections } from '@src/lib/selections' +import { processCodeMirrorRanges } from '@src/lib/selections' +import { engineCommandManager, kclManager } from '@src/lib/singletons' +import { kclEditorActor } from '@src/machines/kclEditorMachine' +import type { + ModelingMachineEvent, + modelingMachine, +} from '@src/machines/modelingMachine' declare global { interface Window { diff --git a/src/editor/plugins/lsp/copilot/index.ts b/src/editor/plugins/lsp/copilot/index.ts index 85982daaa..729531e06 100644 --- a/src/editor/plugins/lsp/copilot/index.ts +++ b/src/editor/plugins/lsp/copilot/index.ts @@ -1,42 +1,42 @@ /// Thanks to the Cursor folks for their heavy lifting here. /// This has been heavily modified from their original implementation but we are /// still grateful. +import { completionStatus } from '@codemirror/autocomplete' import { indentUnit } from '@codemirror/language' -import { - Decoration, - DecorationSet, - EditorView, - KeyBinding, - PluginValue, - ViewPlugin, - ViewUpdate, - keymap, -} from '@codemirror/view' +import type { Extension } from '@codemirror/state' import { Annotation, EditorState, - Extension, Prec, StateEffect, StateField, Transaction, } from '@codemirror/state' -import { completionStatus } from '@codemirror/autocomplete' -import { - offsetToPos, - posToOffset, - LanguageServerOptions, +import type { + DecorationSet, + KeyBinding, + PluginValue, + ViewUpdate, +} from '@codemirror/view' +import { Decoration, EditorView, ViewPlugin, keymap } from '@codemirror/view' +import type { LanguageServerClient, + LanguageServerOptions, +} from '@kittycad/codemirror-lsp-client' +import { docPathFacet, languageId, + offsetToPos, + posToOffset, } from '@kittycad/codemirror-lsp-client' -import { deferExecution } from 'lib/utils' -import { CopilotLspCompletionParams } from '@rust/kcl-lib/bindings/CopilotLspCompletionParams' -import { CopilotCompletionResponse } from '@rust/kcl-lib/bindings/CopilotCompletionResponse' -import { CopilotAcceptCompletionParams } from '@rust/kcl-lib/bindings/CopilotAcceptCompletionParams' -import { CopilotRejectCompletionParams } from '@rust/kcl-lib/bindings/CopilotRejectCompletionParams' -import { editorManager } from 'lib/singletons' -import { reportRejection } from 'lib/trap' +import { editorManager } from '@src/lib/singletons' +import { reportRejection } from '@src/lib/trap' +import { deferExecution } from '@src/lib/utils' + +import type { CopilotAcceptCompletionParams } from '@rust/kcl-lib/bindings/CopilotAcceptCompletionParams' +import type { CopilotCompletionResponse } from '@rust/kcl-lib/bindings/CopilotCompletionResponse' +import type { CopilotLspCompletionParams } from '@rust/kcl-lib/bindings/CopilotLspCompletionParams' +import type { CopilotRejectCompletionParams } from '@rust/kcl-lib/bindings/CopilotRejectCompletionParams' const copilotPluginAnnotation = Annotation.define() export const copilotPluginEvent = copilotPluginAnnotation.of(true) @@ -205,7 +205,10 @@ export class CompletionRequester implements PluginValue { // document. private sendScheduledInput: number | null = null - constructor(readonly view: EditorView, client: LanguageServerClient) { + constructor( + readonly view: EditorView, + client: LanguageServerClient + ) { this.client = client } diff --git a/src/editor/plugins/lsp/kcl/colors.ts b/src/editor/plugins/lsp/kcl/colors.ts index 9fc11b129..4587505db 100644 --- a/src/editor/plugins/lsp/kcl/colors.ts +++ b/src/editor/plugins/lsp/kcl/colors.ts @@ -1,15 +1,15 @@ -import { - EditorView, - WidgetType, - ViewUpdate, - ViewPlugin, - DecorationSet, - Decoration, -} from '@codemirror/view' -import { Range, Extension, Text } from '@codemirror/state' -import { NodeProp, Tree } from '@lezer/common' import { language, syntaxTree } from '@codemirror/language' -import { isArray } from 'lib/utils' +import type { Extension, Range, Text } from '@codemirror/state' +import type { DecorationSet, ViewUpdate } from '@codemirror/view' +import { + Decoration, + EditorView, + ViewPlugin, + WidgetType, +} from '@codemirror/view' +import type { Tree } from '@lezer/common' +import { NodeProp } from '@lezer/common' +import { isArray } from '@src/lib/utils' interface PickerState { from: number diff --git a/src/editor/plugins/lsp/kcl/index.ts b/src/editor/plugins/lsp/kcl/index.ts index 2c1543a2b..c12e9544d 100644 --- a/src/editor/plugins/lsp/kcl/index.ts +++ b/src/editor/plugins/lsp/kcl/index.ts @@ -1,20 +1,22 @@ -import { Extension } from '@codemirror/state' -import { ViewPlugin, PluginValue, ViewUpdate } from '@codemirror/view' -import { - LanguageServerOptions, +import type { Extension } from '@codemirror/state' +import type { PluginValue, ViewUpdate } from '@codemirror/view' +import { ViewPlugin } from '@codemirror/view' +import type { LanguageServerClient, - lspPlugin, - lspFormatCodeEvent, + LanguageServerOptions, } from '@kittycad/codemirror-lsp-client' -import { deferExecution } from 'lib/utils' -import { codeManager, editorManager, kclManager } from 'lib/singletons' -import { UpdateUnitsParams } from '@rust/kcl-lib/bindings/UpdateUnitsParams' -import { UpdateCanExecuteParams } from '@rust/kcl-lib/bindings/UpdateCanExecuteParams' -import { UpdateUnitsResponse } from '@rust/kcl-lib/bindings/UpdateUnitsResponse' -import { UpdateCanExecuteResponse } from '@rust/kcl-lib/bindings/UpdateCanExecuteResponse' -import { codeManagerUpdateEvent } from 'lang/codeManager' -import { copilotPluginEvent } from '../copilot' -import { updateOutsideEditorEvent } from 'editor/manager' +import { lspFormatCodeEvent, lspPlugin } from '@kittycad/codemirror-lsp-client' +import { updateOutsideEditorEvent } from '@src/editor/manager' +import { codeManagerUpdateEvent } from '@src/lang/codeManager' +import { codeManager, editorManager, kclManager } from '@src/lib/singletons' +import { deferExecution } from '@src/lib/utils' + +import type { UpdateCanExecuteParams } from '@rust/kcl-lib/bindings/UpdateCanExecuteParams' +import type { UpdateCanExecuteResponse } from '@rust/kcl-lib/bindings/UpdateCanExecuteResponse' +import type { UpdateUnitsParams } from '@rust/kcl-lib/bindings/UpdateUnitsParams' +import type { UpdateUnitsResponse } from '@rust/kcl-lib/bindings/UpdateUnitsResponse' + +import { copilotPluginEvent } from '@src/editor/plugins/lsp/copilot' const changesDelay = 600 diff --git a/src/editor/plugins/lsp/kcl/language.ts b/src/editor/plugins/lsp/kcl/language.ts index d1884b498..a9a7ac4ab 100644 --- a/src/editor/plugins/lsp/kcl/language.ts +++ b/src/editor/plugins/lsp/kcl/language.ts @@ -1,14 +1,14 @@ // Code mirror language implementation for kcl. - import { LanguageSupport } from '@codemirror/language' -import { +import { KclLanguage } from '@kittycad/codemirror-lang-kcl' +import type { LanguageServerClient, LanguageServerPlugin, } from '@kittycad/codemirror-lsp-client' -import { kclPlugin } from '.' import type * as LSP from 'vscode-languageserver-protocol' -import { colorPicker } from './colors' -import { KclLanguage } from '@kittycad/codemirror-lang-kcl' + +import { kclPlugin } from '@src/editor/plugins/lsp/kcl' +import { colorPicker } from '@src/editor/plugins/lsp/kcl/colors' export interface LanguageOptions { workspaceFolders: LSP.WorkspaceFolder[] diff --git a/src/editor/plugins/lsp/types.ts b/src/editor/plugins/lsp/types.ts index b512de30f..11ef70fc8 100644 --- a/src/editor/plugins/lsp/types.ts +++ b/src/editor/plugins/lsp/types.ts @@ -1,4 +1,4 @@ -import { LspWorkerEventType } from '@kittycad/codemirror-lsp-client' +import type { LspWorkerEventType } from '@kittycad/codemirror-lsp-client' export enum LspWorker { Kcl = 'kcl', diff --git a/src/editor/plugins/lsp/worker.ts b/src/editor/plugins/lsp/worker.ts index 05cdfd2cc..ec80718d0 100644 --- a/src/editor/plugins/lsp/worker.ts +++ b/src/editor/plugins/lsp/worker.ts @@ -4,20 +4,22 @@ import { IntoServer, LspWorkerEventType, } from '@kittycad/codemirror-lsp-client' -import { fileSystemManager } from 'lang/std/fileSystemManager' +import type * as jsrpc from 'json-rpc-2.0' + import init, { LspServerConfig, lsp_run_copilot, lsp_run_kcl, } from '@rust/kcl-wasm-lib/pkg/kcl_wasm_lib' -import * as jsrpc from 'json-rpc-2.0' -import { - LspWorkerEvent, - LspWorker, - KclWorkerOptions, + +import type { CopilotWorkerOptions, -} from 'editor/plugins/lsp/types' -import { err, reportRejection } from 'lib/trap' + KclWorkerOptions, + LspWorkerEvent, +} from '@src/editor/plugins/lsp/types' +import { LspWorker } from '@src/editor/plugins/lsp/types' +import { fileSystemManager } from '@src/lang/std/fileSystemManager' +import { err, reportRejection } from '@src/lib/trap' const intoServer: IntoServer = new IntoServer() const fromServer: FromServer | Error = FromServer.create() diff --git a/src/hooks/useAbsoluteFilePath.ts b/src/hooks/useAbsoluteFilePath.ts index 4e9c9dc37..06c5672d7 100644 --- a/src/hooks/useAbsoluteFilePath.ts +++ b/src/hooks/useAbsoluteFilePath.ts @@ -1,7 +1,8 @@ -import { type IndexLoaderData } from 'lib/types' -import { BROWSER_PATH, PATHS } from 'lib/paths' import { useRouteLoaderData } from 'react-router-dom' +import { BROWSER_PATH, PATHS } from '@src/lib/paths' +import { type IndexLoaderData } from '@src/lib/types' + export function useAbsoluteFilePath() { const routeData = useRouteLoaderData(PATHS.FILE) as IndexLoaderData diff --git a/src/hooks/useAuthNavigation.tsx b/src/hooks/useAuthNavigation.tsx index b58bc99c0..116f8e1d8 100644 --- a/src/hooks/useAuthNavigation.tsx +++ b/src/hooks/useAuthNavigation.tsx @@ -1,8 +1,9 @@ -import { PATHS } from 'lib/paths' -import { useAuthState } from 'machines/appMachine' import { useEffect } from 'react' import { useLocation, useNavigate } from 'react-router-dom' +import { PATHS } from '@src/lib/paths' +import { useAuthState } from '@src/machines/appMachine' + /** * A simple hook that listens to the auth state of the app and navigates * accordingly. diff --git a/src/hooks/useCreateFileLinkQueryWatcher.ts b/src/hooks/useCreateFileLinkQueryWatcher.ts index 04237aadf..d02594a6f 100644 --- a/src/hooks/useCreateFileLinkQueryWatcher.ts +++ b/src/hooks/useCreateFileLinkQueryWatcher.ts @@ -1,11 +1,12 @@ -import { base64ToString } from 'lib/base64' -import { CREATE_FILE_URL_PARAM, DEFAULT_FILE_NAME } from 'lib/constants' import { useEffect } from 'react' import { useSearchParams } from 'react-router-dom' -import { isDesktop } from 'lib/isDesktop' -import { FileLinkParams } from 'lib/links' -import { ProjectsCommandSchema } from 'lib/commandBarConfigs/projectsCommandConfig' -import { useSettings } from 'machines/appMachine' + +import { base64ToString } from '@src/lib/base64' +import type { ProjectsCommandSchema } from '@src/lib/commandBarConfigs/projectsCommandConfig' +import { CREATE_FILE_URL_PARAM, DEFAULT_FILE_NAME } from '@src/lib/constants' +import { isDesktop } from '@src/lib/isDesktop' +import type { FileLinkParams } from '@src/lib/links' +import { useSettings } from '@src/machines/appMachine' // For initializing the command arguments, we actually want `method` to be undefined // so that we don't skip it in the command palette. @@ -45,8 +46,8 @@ export function useCreateFileLinkQuery( ? params.name.replace('.kcl', '') : params.name : isDesktop() - ? settings.projects.defaultProjectName.current - : DEFAULT_FILE_NAME, + ? settings.projects.defaultProjectName.current + : DEFAULT_FILE_NAME, code: params.code || '', method: isDesktop() ? undefined : 'existingProject', } diff --git a/src/hooks/useDocumentHasFocus.ts b/src/hooks/useDocumentHasFocus.ts index fab729ab6..f97045bad 100644 --- a/src/hooks/useDocumentHasFocus.ts +++ b/src/hooks/useDocumentHasFocus.ts @@ -1,5 +1,5 @@ // Based on https://learnersbucket.com/examples/interview/usehasfocus-hook-in-react/ -import { useState, useEffect } from 'react' +import { useEffect, useState } from 'react' export const useDocumentHasFocus = () => { // get the initial state diff --git a/src/hooks/useEngineConnectionSubscriptions.ts b/src/hooks/useEngineConnectionSubscriptions.ts index ff7c94108..ee11b6491 100644 --- a/src/hooks/useEngineConnectionSubscriptions.ts +++ b/src/hooks/useEngineConnectionSubscriptions.ts @@ -1,28 +1,33 @@ import { useEffect, useRef } from 'react' + +import { useModelingContext } from '@src/hooks/useModelingContext' +import { getNodeFromPath } from '@src/lang/queryAst' +import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils' +import type { SegmentArtifact } from '@src/lang/std/artifactGraph' +import { + getArtifactOfTypes, + getCapCodeRef, + getCodeRefsByArtifactId, + getSweepFromSuspectedSweepSurface, + getWallCodeRef, +} from '@src/lang/std/artifactGraph' +import type { CallExpression, CallExpressionKw } from '@src/lang/wasm' +import { defaultSourceRange } from '@src/lang/wasm' +import type { DefaultPlaneStr } from '@src/lib/planes' +import { getEventForSelectWithPoint } from '@src/lib/selections' import { editorManager, engineCommandManager, kclManager, + rustContext, + sceneEntitiesManager, sceneInfra, -} from 'lib/singletons' -import { useModelingContext } from './useModelingContext' -import { getEventForSelectWithPoint } from 'lib/selections' -import { - getCapCodeRef, - getSweepFromSuspectedSweepSurface, - getWallCodeRef, - getCodeRefsByArtifactId, - getArtifactOfTypes, - SegmentArtifact, -} from 'lang/std/artifactGraph' -import { err, reportRejection } from 'lib/trap' -import { getFaceDetails } from 'clientSideScene/sceneEntities' -import { DefaultPlaneStr } from 'lib/planes' -import { getNodeFromPath } from 'lang/queryAst' -import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils' -import { CallExpression, CallExpressionKw, defaultSourceRange } from 'lang/wasm' -import { EdgeCutInfo, ExtrudeFacePlane } from 'machines/modelingMachine' -import { rustContext } from 'lib/singletons' +} from '@src/lib/singletons' +import { err, reportRejection } from '@src/lib/trap' +import type { + EdgeCutInfo, + ExtrudeFacePlane, +} from '@src/machines/modelingMachine' export function useEngineConnectionSubscriptions() { const { send, context, state } = useModelingContext() @@ -143,7 +148,8 @@ export function useEngineConnectionSubscriptions() { const artifact = kclManager.artifactGraph.get(planeOrFaceId) if (artifact?.type === 'plane') { - const planeInfo = await getFaceDetails(planeOrFaceId) + const planeInfo = + await sceneEntitiesManager.getFaceDetails(planeOrFaceId) sceneInfra.modelingSend({ type: 'Select default plane', data: { @@ -165,7 +171,7 @@ export function useEngineConnectionSubscriptions() { ].map((num) => num / sceneInfra._baseUnitMultiplier) as [ number, number, - number + number, ], planeId: planeOrFaceId, pathToNode: artifact.codeRef.pathToNode, @@ -194,10 +200,10 @@ export function useEngineConnectionSubscriptions() { artifact.type === 'cap' ? getCapCodeRef(artifact, kclManager.artifactGraph) : artifact.type === 'wall' - ? getWallCodeRef(artifact, kclManager.artifactGraph) - : artifact.codeRef + ? getWallCodeRef(artifact, kclManager.artifactGraph) + : artifact.codeRef - const faceInfo = await getFaceDetails(faceId) + const faceInfo = await sceneEntitiesManager.getFaceDetails(faceId) if (!faceInfo?.origin || !faceInfo?.z_axis || !faceInfo?.y_axis) return const { z_axis, y_axis, origin } = faceInfo @@ -275,11 +281,11 @@ export function useEngineConnectionSubscriptions() { const _faceInfo: ExtrudeFacePlane['faceInfo'] = edgeCutMeta ? edgeCutMeta : artifact.type === 'cap' - ? { - type: 'cap', - subType: artifact.subType, - } - : { type: 'wall' } + ? { + type: 'cap', + subType: artifact.subType, + } + : { type: 'wall' } const extrudePathToNode = !err(extrusion) ? getNodePathFromSourceRange( diff --git a/src/hooks/useFileContext.ts b/src/hooks/useFileContext.ts index a41135d06..4e66a60fd 100644 --- a/src/hooks/useFileContext.ts +++ b/src/hooks/useFileContext.ts @@ -1,6 +1,7 @@ -import { FileContext } from 'components/FileMachineProvider' import { useContext } from 'react' +import { FileContext } from '@src/components/FileMachineProvider' + export const useFileContext = () => { return useContext(FileContext) } diff --git a/src/hooks/useFileSystemWatcher.tsx b/src/hooks/useFileSystemWatcher.tsx index 210b31e11..c6f5cc78f 100644 --- a/src/hooks/useFileSystemWatcher.tsx +++ b/src/hooks/useFileSystemWatcher.tsx @@ -1,6 +1,7 @@ -import { isDesktop } from 'lib/isDesktop' -import { reportRejection } from 'lib/trap' -import { useEffect, useState, useRef } from 'react' +import { useEffect, useRef, useState } from 'react' + +import { isDesktop } from '@src/lib/isDesktop' +import { reportRejection } from '@src/lib/trap' type Path = string diff --git a/src/hooks/useHotKeyListener.ts b/src/hooks/useHotKeyListener.ts index 10d6a3eed..999907144 100644 --- a/src/hooks/useHotKeyListener.ts +++ b/src/hooks/useHotKeyListener.ts @@ -1,6 +1,7 @@ -import { editorManager } from 'lib/singletons' import { useEffect } from 'react' +import { editorManager } from '@src/lib/singletons' + // Kurt's note: codeMirror styling overrides were needed to make this work // namely, the cursor needs to still be shown when the editor is not focused // search for code-mirror-override in the repo to find the relevant styles diff --git a/src/hooks/useMenu.ts b/src/hooks/useMenu.ts index f99e2d8b9..4842ae81e 100644 --- a/src/hooks/useMenu.ts +++ b/src/hooks/useMenu.ts @@ -1,6 +1,8 @@ import { useEffect } from 'react' -import type { WebContentSendPayload } from '../menu/channels' -import { isDesktop } from 'lib/isDesktop' + +import { isDesktop } from '@src/lib/isDesktop' +import type { WebContentSendPayload } from '@src/menu/channels' + export function useMenuListener( callback: (data: WebContentSendPayload) => void ) { diff --git a/src/hooks/useModelingContext.ts b/src/hooks/useModelingContext.ts index 3af3c5d79..9aa202789 100644 --- a/src/hooks/useModelingContext.ts +++ b/src/hooks/useModelingContext.ts @@ -1,6 +1,7 @@ -import { ModelingMachineContext } from 'components/ModelingMachineProvider' import { useContext } from 'react' +import { ModelingMachineContext } from '@src/components/ModelingMachineProvider' + export const useModelingContext = () => { return useContext(ModelingMachineContext) } diff --git a/src/hooks/useNetworkContext.tsx b/src/hooks/useNetworkContext.tsx index 16da16a11..35ce44412 100644 --- a/src/hooks/useNetworkContext.tsx +++ b/src/hooks/useNetworkContext.tsx @@ -1,11 +1,13 @@ import { createContext, useContext } from 'react' + +import type { NetworkStatus } from '@src/hooks/useNetworkStatus' +import { NetworkHealthState } from '@src/hooks/useNetworkStatus' +import type { EngineConnectionState } from '@src/lang/std/engineConnection' import { ConnectingTypeGroup, EngineConnectionStateType, - EngineConnectionState, initialConnectingTypeGroupState, -} from '../lang/std/engineConnection' -import { NetworkStatus, NetworkHealthState } from './useNetworkStatus' +} from '@src/lang/std/engineConnection' export const NetworkContext = createContext({ immediateState: { diff --git a/src/hooks/useNetworkStatus.tsx b/src/hooks/useNetworkStatus.tsx index 30c4ccf2c..d01dcdd4d 100644 --- a/src/hooks/useNetworkStatus.tsx +++ b/src/hooks/useNetworkStatus.tsx @@ -1,16 +1,19 @@ import { useEffect, useState } from 'react' -import { + +import type { ConnectingType, + EngineConnectionState, + ErrorType, +} from '@src/lang/std/engineConnection' +import { ConnectingTypeGroup, DisconnectingType, EngineCommandManagerEvents, EngineConnectionEvents, EngineConnectionStateType, - EngineConnectionState, - ErrorType, initialConnectingTypeGroupState, -} from '../lang/std/engineConnection' -import { engineCommandManager } from '../lib/singletons' +} from '@src/lang/std/engineConnection' +import { engineCommandManager } from '@src/lib/singletons' export enum NetworkHealthState { Ok, @@ -69,10 +72,10 @@ export function useNetworkStatus() { !internetConnected ? NetworkHealthState.Disconnected : hasIssues || hasIssues === undefined - ? NetworkHealthState.Issue - : pingPongHealth === 'TIMEOUT' - ? NetworkHealthState.Weak - : NetworkHealthState.Ok + ? NetworkHealthState.Issue + : pingPongHealth === 'TIMEOUT' + ? NetworkHealthState.Weak + : NetworkHealthState.Ok ) }, [hasIssues, internetConnected, pingPongHealth]) diff --git a/src/hooks/usePlatform.ts b/src/hooks/usePlatform.ts index d74944745..3d5ef847b 100644 --- a/src/hooks/usePlatform.ts +++ b/src/hooks/usePlatform.ts @@ -1,6 +1,8 @@ -import { Platform, platform } from 'lib/utils' import { useEffect, useState } from 'react' +import type { Platform } from '@src/lib/utils' +import { platform } from '@src/lib/utils' + export default function usePlatform() { const [platformName, setPlatformName] = useState('') diff --git a/src/hooks/useProjectsContext.ts b/src/hooks/useProjectsContext.ts index 2cc3551be..2e68535a7 100644 --- a/src/hooks/useProjectsContext.ts +++ b/src/hooks/useProjectsContext.ts @@ -1,6 +1,7 @@ -import { ProjectsMachineContext } from 'components/ProjectsContextProvider' import { useContext } from 'react' +import { ProjectsMachineContext } from '@src/components/ProjectsContextProvider' + export const useProjectsContext = () => { return useContext(ProjectsMachineContext) } diff --git a/src/hooks/useProjectsLoader.tsx b/src/hooks/useProjectsLoader.tsx index 8df3bab8a..d7d158a31 100644 --- a/src/hooks/useProjectsLoader.tsx +++ b/src/hooks/useProjectsLoader.tsx @@ -1,9 +1,10 @@ -import { trap } from 'lib/trap' -import { useState, useEffect } from 'react' -import { ensureProjectDirectoryExists, listProjects } from 'lib/desktop' -import { loadAndValidateSettings } from 'lib/settings/settingsUtils' -import { Project } from 'lib/project' -import { isDesktop } from 'lib/isDesktop' +import { useEffect, useState } from 'react' + +import { ensureProjectDirectoryExists, listProjects } from '@src/lib/desktop' +import { isDesktop } from '@src/lib/isDesktop' +import type { Project } from '@src/lib/project' +import { loadAndValidateSettings } from '@src/lib/settings/settingsUtils' +import { trap } from '@src/lib/trap' // Gotcha: This should be ported to the ProjectMachine and keep track of // projectDirs and projectPaths in the context when it internally calls listProjects diff --git a/src/hooks/useResolvedTheme.ts b/src/hooks/useResolvedTheme.ts index 325dcf0f4..4f13c94cc 100644 --- a/src/hooks/useResolvedTheme.ts +++ b/src/hooks/useResolvedTheme.ts @@ -1,5 +1,5 @@ -import { Themes, getSystemTheme } from 'lib/theme' -import { useSettings } from 'machines/appMachine' +import { Themes, getSystemTheme } from '@src/lib/theme' +import { useSettings } from '@src/machines/appMachine' /** * Resolves the current theme based on the theme setting diff --git a/src/hooks/useSetupEngineManager.ts b/src/hooks/useSetupEngineManager.ts index 5092bc8bf..689a3c624 100644 --- a/src/hooks/useSetupEngineManager.ts +++ b/src/hooks/useSetupEngineManager.ts @@ -1,15 +1,16 @@ -import { useLayoutEffect, useEffect, useRef } from 'react' -import { engineCommandManager } from 'lib/singletons' -import { deferExecution } from 'lib/utils' -import { Themes } from 'lib/theme' -import { useModelingContext } from './useModelingContext' -import { useNetworkContext } from 'hooks/useNetworkContext' -import { useAppState, useAppStream } from 'AppState' -import { SettingsViaQueryString } from 'lib/settings/settingsTypes' +import { useAppState, useAppStream } from '@src/AppState' +import { useEffect, useLayoutEffect, useRef } from 'react' + +import type { useModelingContext } from '@src/hooks/useModelingContext' +import { useNetworkContext } from '@src/hooks/useNetworkContext' import { - EngineConnectionStateType, DisconnectingType, -} from 'lang/std/engineConnection' + EngineConnectionStateType, +} from '@src/lang/std/engineConnection' +import type { SettingsViaQueryString } from '@src/lib/settings/settingsTypes' +import { engineCommandManager } from '@src/lib/singletons' +import { Themes } from '@src/lib/theme' +import { deferExecution } from '@src/lib/utils' export function useSetupEngineManager( streamRef: React.RefObject, diff --git a/src/hooks/useStateMachineCommands.ts b/src/hooks/useStateMachineCommands.ts index 923d8ae79..47c110092 100644 --- a/src/hooks/useStateMachineCommands.ts +++ b/src/hooks/useStateMachineCommands.ts @@ -1,20 +1,21 @@ +import { useAppState } from '@src/AppState' import { useEffect } from 'react' -import { AnyStateMachine, Actor, StateFrom, EventFrom } from 'xstate' -import { createMachineCommand } from '../lib/createMachineCommand' -import { modelingMachine } from 'machines/modelingMachine' -import { authMachine } from 'machines/authMachine' -import { settingsMachine } from 'machines/settingsMachine' -import { projectsMachine } from 'machines/projectsMachine' -import { +import type { Actor, AnyStateMachine, EventFrom, StateFrom } from 'xstate' + +import { useNetworkContext } from '@src/hooks/useNetworkContext' +import { NetworkHealthState } from '@src/hooks/useNetworkStatus' +import { useKclContext } from '@src/lang/KclProvider' +import type { Command, StateMachineCommandSetConfig, StateMachineCommandSetSchema, -} from 'lib/commandTypes' -import { useKclContext } from 'lang/KclProvider' -import { useNetworkContext } from 'hooks/useNetworkContext' -import { NetworkHealthState } from 'hooks/useNetworkStatus' -import { useAppState } from 'AppState' -import { commandBarActor } from 'machines/commandBarMachine' +} from '@src/lib/commandTypes' +import { createMachineCommand } from '@src/lib/createMachineCommand' +import type { authMachine } from '@src/machines/authMachine' +import { commandBarActor } from '@src/machines/commandBarMachine' +import type { modelingMachine } from '@src/machines/modelingMachine' +import type { projectsMachine } from '@src/machines/projectsMachine' +import type { settingsMachine } from '@src/machines/settingsMachine' // This might not be necessary, AnyStateMachine from xstate is working export type AllMachines = @@ -25,7 +26,7 @@ export type AllMachines = interface UseStateMachineCommandsArgs< T extends AllMachines, - S extends StateMachineCommandSetSchema + S extends StateMachineCommandSetSchema, > { machineId: T['id'] state: StateFrom @@ -38,7 +39,7 @@ interface UseStateMachineCommandsArgs< export default function useStateMachineCommands< T extends AnyStateMachine, - S extends StateMachineCommandSetSchema + S extends StateMachineCommandSetSchema, >({ machineId, state, diff --git a/src/hooks/useToolbarGuards.ts b/src/hooks/useToolbarGuards.ts index 0319a0ef1..36b73244d 100644 --- a/src/hooks/useToolbarGuards.ts +++ b/src/hooks/useToolbarGuards.ts @@ -1,16 +1,18 @@ +import { useEffect, useState } from 'react' + import { SetVarNameModal, createSetVarNameModal, -} from 'components/SetVarNameModal' -import { editorManager, kclManager, codeManager } from 'lib/singletons' -import { reportRejection, trap, err } from 'lib/trap' -import { moveValueIntoNewVariable } from 'lang/modifyAst' -import { isNodeSafeToReplace } from 'lang/queryAst' -import { useEffect, useState } from 'react' -import { useModelingContext } from './useModelingContext' -import { PathToNode, SourceRange, recast } from 'lang/wasm' -import { useKclContext } from 'lang/KclProvider' -import { toSync } from 'lib/utils' +} from '@src/components/SetVarNameModal' +import { useModelingContext } from '@src/hooks/useModelingContext' +import { useKclContext } from '@src/lang/KclProvider' +import { moveValueIntoNewVariable } from '@src/lang/modifyAst' +import { isNodeSafeToReplace } from '@src/lang/queryAst' +import type { PathToNode, SourceRange } from '@src/lang/wasm' +import { recast } from '@src/lang/wasm' +import { codeManager, editorManager, kclManager } from '@src/lib/singletons' +import { err, reportRejection, trap } from '@src/lib/trap' +import { toSync } from '@src/lib/utils' export const getVarNameModal = createSetVarNameModal(SetVarNameModal) diff --git a/src/index.css b/src/index.css index 90e9e9a84..fb216014c 100644 --- a/src/index.css +++ b/src/index.css @@ -131,13 +131,13 @@ input { } .mono { - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', - monospace; + font-family: + source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace; } code { - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', - monospace; + font-family: + source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace; } /* diff --git a/src/index.tsx b/src/index.tsx index b7abe9b9f..45f9b4d11 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,19 +1,20 @@ +import { AppStreamProvider } from '@src/AppState' import ReactDOM from 'react-dom/client' -import './index.css' -import reportWebVitals from './reportWebVitals' import toast, { Toaster } from 'react-hot-toast' -import { Router } from './Router' import { HotkeysProvider } from 'react-hotkeys-hook' import ModalContainer from 'react-modal-promise' -import { isDesktop } from 'lib/isDesktop' -import { AppStreamProvider } from 'AppState' -import { ToastUpdate } from 'components/ToastUpdate' -import { markOnce } from 'lib/performance' -import { AUTO_UPDATER_TOAST_ID } from 'lib/constants' -import { initializeWindowExceptionHandler } from 'lib/exceptions' -import { initPromise } from 'lang/wasm' -import { appActor } from 'machines/appMachine' -import { reportRejection } from 'lib/trap' + +import { Router } from '@src/Router' +import { ToastUpdate } from '@src/components/ToastUpdate' +import '@src/index.css' +import { initPromise } from '@src/lang/wasm' +import { AUTO_UPDATER_TOAST_ID } from '@src/lib/constants' +import { initializeWindowExceptionHandler } from '@src/lib/exceptions' +import { isDesktop } from '@src/lib/isDesktop' +import { markOnce } from '@src/lib/performance' +import { reportRejection } from '@src/lib/trap' +import { appActor } from '@src/machines/appMachine' +import reportWebVitals from '@src/reportWebVitals' markOnce('code/willAuth') initializeWindowExceptionHandler() diff --git a/src/lang/KclProvider.tsx b/src/lang/KclProvider.tsx index 61dc05f39..5d174a4b7 100644 --- a/src/lang/KclProvider.tsx +++ b/src/lang/KclProvider.tsx @@ -1,10 +1,12 @@ +import type { Diagnostic } from '@codemirror/lint' import { createContext, useContext, useEffect, useState } from 'react' -import { type IndexLoaderData } from 'lib/types' import { useRouteLoaderData } from 'react-router-dom' -import { codeManager, kclManager } from 'lib/singletons' -import { Diagnostic } from '@codemirror/lint' -import { KCLError } from './errors' -import { PATHS } from 'lib/paths' + +import { PATHS } from '@src/lib/paths' +import { codeManager, kclManager } from '@src/lib/singletons' +import { type IndexLoaderData } from '@src/lib/types' + +import type { KCLError } from '@src/lang/errors' const KclContext = createContext({ code: codeManager?.code || '', diff --git a/src/lang/KclSingleton.ts b/src/lang/KclSingleton.ts index 10d518866..7ef452477 100644 --- a/src/lang/KclSingleton.ts +++ b/src/lang/KclSingleton.ts @@ -1,49 +1,53 @@ -import { executeAst, executeAstMock, lintAst } from 'lang/langHelpers' -import { handleSelectionBatch, Selections } from 'lib/selections' -import { - KCLError, - compilationErrorsToDiagnostics, - kclErrorsToDiagnostics, -} from './errors' -import { uuidv4, isOverlap, deferExecution } from 'lib/utils' -import { EngineCommandManager } from './std/engineConnection' -import { err, reportRejection } from 'lib/trap' -import { EXECUTE_AST_INTERRUPT_ERROR_MESSAGE } from 'lib/constants' -import { buildArtifactIndex } from 'lib/artifactIndex' -import { ArtifactIndex } from 'lib/artifactIndex' - -import { - emptyExecState, - ExecState, - getKclVersion, - initPromise, - jsAppSettings, - KclValue, - parse, - PathToNode, - Program, - recast, - SourceRange, - topLevelRange, - VariableMap, - ArtifactGraph, -} from 'lang/wasm' -import { getNodeFromPath, getSettingsAnnotation } from './queryAst' -import { - codeManager, - editorManager, - sceneInfra, - rustContext, -} from 'lib/singletons' -import { Diagnostic } from '@codemirror/lint' -import { markOnce } from 'lib/performance' -import { Node } from '@rust/kcl-lib/bindings/Node' -import { +import type { Diagnostic } from '@codemirror/lint' +import type { EntityType_type, ModelingCmdReq_type, } from '@kittycad/lib/dist/types/src/models' -import { Operation } from '@rust/kcl-lib/bindings/Operation' -import { KclSettingsAnnotation } from 'lib/settings/settingsTypes' + +import type { Node } from '@rust/kcl-lib/bindings/Node' +import type { Operation } from '@rust/kcl-lib/bindings/Operation' + +import type { KCLError } from '@src/lang/errors' +import { + compilationErrorsToDiagnostics, + kclErrorsToDiagnostics, +} from '@src/lang/errors' +import { executeAst, executeAstMock, lintAst } from '@src/lang/langHelpers' +import { getNodeFromPath, getSettingsAnnotation } from '@src/lang/queryAst' +import type { EngineCommandManager } from '@src/lang/std/engineConnection' +import { topLevelRange } from '@src/lang/util' +import type { + ArtifactGraph, + ExecState, + KclValue, + PathToNode, + Program, + SourceRange, + VariableMap, +} from '@src/lang/wasm' +import { + emptyExecState, + getKclVersion, + initPromise, + parse, + recast, +} from '@src/lang/wasm' +import type { ArtifactIndex } from '@src/lib/artifactIndex' +import { buildArtifactIndex } from '@src/lib/artifactIndex' +import { EXECUTE_AST_INTERRUPT_ERROR_MESSAGE } from '@src/lib/constants' +import { markOnce } from '@src/lib/performance' +import type { Selections } from '@src/lib/selections' +import { handleSelectionBatch } from '@src/lib/selections' +import type { KclSettingsAnnotation } from '@src/lib/settings/settingsTypes' +import { jsAppSettings } from '@src/lib/settings/settingsUtils' +import { + codeManager, + editorManager, + rustContext, + sceneInfra, +} from '@src/lib/singletons' +import { err, reportRejection } from '@src/lib/trap' +import { deferExecution, isOverlap, uuidv4 } from '@src/lib/utils' interface ExecuteArgs { ast?: Node diff --git a/src/lang/artifact.test.ts b/src/lang/artifact.test.ts index 3ac0a67af..b480bd6cf 100644 --- a/src/lang/artifact.test.ts +++ b/src/lang/artifact.test.ts @@ -1,5 +1,5 @@ -import { assertParse, initPromise } from './wasm' -import { enginelessExecutor } from '../lib/testHelpers' +import { assertParse, initPromise } from '@src/lang/wasm' +import { enginelessExecutor } from '@src/lib/testHelpers' beforeAll(async () => { await initPromise diff --git a/src/lang/codeManager.ts b/src/lang/codeManager.ts index 96ea4fa2c..f8bf489df 100644 --- a/src/lang/codeManager.ts +++ b/src/lang/codeManager.ts @@ -1,16 +1,17 @@ // A little class for updating the code state when we need to and explicitly // NOT updating the code state when we don't need to. // This prevents re-renders of the codemirror editor, when typing. -import { bracket } from 'lib/exampleKcl' -import { isDesktop } from 'lib/isDesktop' -import toast from 'react-hot-toast' -import { editorManager } from 'lib/singletons' -import { Annotation, Transaction } from '@codemirror/state' -import { EditorView, KeyBinding } from '@codemirror/view' -import { recast, Program, parse } from 'lang/wasm' -import { err, reportRejection } from 'lib/trap' -import { Compartment } from '@codemirror/state' import { history } from '@codemirror/commands' +import { Annotation, Compartment, Transaction } from '@codemirror/state' +import type { EditorView, KeyBinding } from '@codemirror/view' +import toast from 'react-hot-toast' + +import type { Program } from '@src/lang/wasm' +import { parse, recast } from '@src/lang/wasm' +import { bracket } from '@src/lib/exampleKcl' +import { isDesktop } from '@src/lib/isDesktop' +import { editorManager } from '@src/lib/singletons' +import { err, reportRejection } from '@src/lib/trap' const PERSIST_CODE_KEY = 'persistCode' diff --git a/src/lang/constants.ts b/src/lang/constants.ts new file mode 100644 index 000000000..00032417d --- /dev/null +++ b/src/lang/constants.ts @@ -0,0 +1,7 @@ +export const ARG_TAG = 'tag' +export const ARG_END = 'end' +export const ARG_LENGTH = 'length' +export const ARG_END_ABSOLUTE = 'endAbsolute' +export const ARG_CIRCLE_CENTER = 'center' +export const ARG_CIRCLE_RADIUS = 'radius' +export const DETERMINING_ARGS = [ARG_LENGTH, ARG_END, ARG_END_ABSOLUTE] diff --git a/src/lang/create.ts b/src/lang/create.ts new file mode 100644 index 000000000..1a630d11a --- /dev/null +++ b/src/lang/create.ts @@ -0,0 +1,439 @@ +import type { Name } from '@rust/kcl-lib/bindings/Name' +import type { Node } from '@rust/kcl-lib/bindings/Node' +import type { TagDeclarator } from '@rust/kcl-lib/bindings/TagDeclarator' + +import { ARG_TAG } from '@src/lang/constants' +import { getNodeFromPath } from '@src/lang/queryAst' +import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils' +import { findKwArg } from '@src/lang/util' +import type { + ArrayExpression, + BinaryExpression, + CallExpression, + CallExpressionKw, + Expr, + Identifier, + LabeledArg, + Literal, + LiteralValue, + ObjectExpression, + PathToNode, + PipeExpression, + PipeSubstitution, + Program, + SourceRange, + UnaryExpression, + VariableDeclaration, + VariableDeclarator, +} from '@src/lang/wasm' +import { formatNumber } from '@src/lang/wasm' +import { err } from '@src/lib/trap' + +/** + * Note: This depends on WASM, but it's not async. Callers are responsible for + * awaiting init of the WASM module. + */ +export function createLiteral(value: LiteralValue | number): Node { + if (typeof value === 'number') { + value = { value, suffix: 'None' } + } + let raw: string + if (typeof value === 'string') { + // TODO: Should we handle escape sequences? + raw = `${value}` + } else if (typeof value === 'boolean') { + raw = `${value}` + } else if (typeof value.value === 'number' && value.suffix === 'None') { + // Fast path for numbers when there are no units. + raw = `${value.value}` + } else { + raw = formatNumber(value.value, value.suffix) + } + return { + type: 'Literal', + start: 0, + end: 0, + moduleId: 0, + value, + raw, + outerAttrs: [], + preComments: [], + commentStart: 0, + } +} + +export function createTagDeclarator(value: string): Node { + return { + type: 'TagDeclarator', + start: 0, + end: 0, + moduleId: 0, + outerAttrs: [], + preComments: [], + commentStart: 0, + + value, + } +} + +export function createIdentifier(name: string): Node { + return { + type: 'Identifier', + start: 0, + end: 0, + moduleId: 0, + outerAttrs: [], + preComments: [], + commentStart: 0, + + name, + } +} + +export function createLocalName(name: string): Node { + return { + type: 'Name', + start: 0, + end: 0, + moduleId: 0, + outerAttrs: [], + preComments: [], + commentStart: 0, + + abs_path: false, + path: [], + name: createIdentifier(name), + } +} + +export function createName(path: [string], name: string): Node { + return { + type: 'Name', + start: 0, + end: 0, + moduleId: 0, + outerAttrs: [], + preComments: [], + commentStart: 0, + + abs_path: false, + path: path.map(createIdentifier), + name: createIdentifier(name), + } +} + +export function createPipeSubstitution(): Node { + return { + type: 'PipeSubstitution', + start: 0, + end: 0, + moduleId: 0, + outerAttrs: [], + preComments: [], + commentStart: 0, + } +} + +export function createCallExpressionStdLib( + name: string, + args: CallExpression['arguments'] +): Node { + return { + type: 'CallExpression', + start: 0, + end: 0, + moduleId: 0, + outerAttrs: [], + preComments: [], + commentStart: 0, + callee: createLocalName(name), + arguments: args, + } +} + +export const nonCodeMetaEmpty = () => { + return { nonCodeNodes: {}, startNodes: [], start: 0, end: 0 } +} + +export function createCallExpressionStdLibKw( + name: string, + unlabeled: CallExpressionKw['unlabeled'], + args: CallExpressionKw['arguments'] +): Node { + return { + type: 'CallExpressionKw', + start: 0, + end: 0, + moduleId: 0, + outerAttrs: [], + preComments: [], + commentStart: 0, + nonCodeMeta: nonCodeMetaEmpty(), + callee: createLocalName(name), + unlabeled, + arguments: args, + } +} + +export function createCallExpression( + name: string, + args: CallExpression['arguments'] +): Node { + return { + type: 'CallExpression', + start: 0, + end: 0, + moduleId: 0, + outerAttrs: [], + preComments: [], + commentStart: 0, + callee: createLocalName(name), + arguments: args, + } +} + +export function createArrayExpression( + elements: ArrayExpression['elements'] +): Node { + return { + type: 'ArrayExpression', + start: 0, + end: 0, + moduleId: 0, + outerAttrs: [], + preComments: [], + commentStart: 0, + + nonCodeMeta: nonCodeMetaEmpty(), + elements, + } +} + +export function createPipeExpression( + body: PipeExpression['body'] +): Node { + return { + type: 'PipeExpression', + start: 0, + end: 0, + moduleId: 0, + outerAttrs: [], + preComments: [], + commentStart: 0, + + body, + nonCodeMeta: nonCodeMetaEmpty(), + } +} + +export function createVariableDeclaration( + varName: string, + init: VariableDeclarator['init'], + visibility: VariableDeclaration['visibility'] = 'default', + kind: VariableDeclaration['kind'] = 'const' +): Node { + return { + type: 'VariableDeclaration', + start: 0, + end: 0, + moduleId: 0, + outerAttrs: [], + preComments: [], + commentStart: 0, + + declaration: { + type: 'VariableDeclarator', + start: 0, + end: 0, + moduleId: 0, + outerAttrs: [], + preComments: [], + commentStart: 0, + + id: createIdentifier(varName), + init, + }, + visibility, + kind, + } +} + +export function createObjectExpression(properties: { + [key: string]: Expr +}): Node { + return { + type: 'ObjectExpression', + start: 0, + end: 0, + moduleId: 0, + outerAttrs: [], + preComments: [], + commentStart: 0, + + nonCodeMeta: nonCodeMetaEmpty(), + properties: Object.entries(properties).map(([key, value]) => ({ + type: 'ObjectProperty', + start: 0, + end: 0, + moduleId: 0, + outerAttrs: [], + preComments: [], + commentStart: 0, + key: createIdentifier(key), + + value, + })), + } +} + +export function createUnaryExpression( + argument: UnaryExpression['argument'], + operator: UnaryExpression['operator'] = '-' +): Node { + return { + type: 'UnaryExpression', + start: 0, + end: 0, + moduleId: 0, + outerAttrs: [], + preComments: [], + commentStart: 0, + + operator, + argument, + } +} + +export function createBinaryExpression([left, operator, right]: [ + BinaryExpression['left'], + BinaryExpression['operator'], + BinaryExpression['right'], +]): Node { + return { + type: 'BinaryExpression', + start: 0, + end: 0, + moduleId: 0, + outerAttrs: [], + preComments: [], + commentStart: 0, + + operator, + left, + right, + } +} + +export function createBinaryExpressionWithUnary([left, right]: [ + BinaryExpression['left'], + BinaryExpression['right'], +]): Node { + if (right.type === 'UnaryExpression' && right.operator === '-') + return createBinaryExpression([left, '-', right.argument]) + return createBinaryExpression([left, '+', right]) +} + +export function findUniqueName( + ast: Program | string, + name: string, + pad = 3, + index = 1 +): string { + let searchStr: string = typeof ast === 'string' ? ast : JSON.stringify(ast) + const indexStr = String(index).padStart(pad, '0') + + const endingDigitsMatcher = /\d+$/ + const nameEndsInDigits = name.match(endingDigitsMatcher) + let nameIsInString = searchStr.includes(`:"${name}"`) + + if (nameEndsInDigits !== null) { + // base case: name is unique and ends in digits + if (!nameIsInString) return name + + // recursive case: name is not unique and ends in digits + const newPad = nameEndsInDigits[0].length + const newIndex = parseInt(nameEndsInDigits[0]) + 1 + const nameWithoutDigits = name.replace(endingDigitsMatcher, '') + + return findUniqueName(searchStr, nameWithoutDigits, newPad, newIndex) + } + + const newName = `${name}${indexStr}` + nameIsInString = searchStr.includes(`:"${newName}"`) + + // base case: name is unique and does not end in digits + if (!nameIsInString) return newName + + // recursive case: name is not unique and does not end in digits + return findUniqueName(searchStr, name, pad, index + 1) +} + +export function giveSketchFnCallTag( + ast: Node, + range: SourceRange, + tag?: string +): + | { + modifiedAst: Node + tag: string + isTagExisting: boolean + pathToNode: PathToNode + } + | Error { + const path = getNodePathFromSourceRange(ast, range) + const maybeTag = (() => { + const callNode = getNodeFromPath( + ast, + path, + ['CallExpression', 'CallExpressionKw'] + ) + if (!err(callNode) && callNode.node.type === 'CallExpressionKw') { + const { node: primaryCallExp } = callNode + const existingTag = findKwArg(ARG_TAG, primaryCallExp) + const tagDeclarator = + existingTag || createTagDeclarator(tag || findUniqueName(ast, 'seg', 2)) + const isTagExisting = !!existingTag + if (!isTagExisting) { + callNode.node.arguments.push(createLabeledArg(ARG_TAG, tagDeclarator)) + } + return { tagDeclarator, isTagExisting } + } + + // We've handled CallExpressionKw above, so this has to be positional. + const _node1 = getNodeFromPath(ast, path, 'CallExpression') + if (err(_node1)) return _node1 + const { node: primaryCallExp } = _node1 + + // Tag is always 3rd expression now, using arg index feels brittle + // but we can come up with a better way to identify tag later. + const thirdArg = primaryCallExp.arguments?.[2] + const tagDeclarator = + thirdArg || + (createTagDeclarator( + tag || findUniqueName(ast, 'seg', 2) + ) as TagDeclarator) + const isTagExisting = !!thirdArg + if (!isTagExisting) { + primaryCallExp.arguments[2] = tagDeclarator + } + return { tagDeclarator, isTagExisting } + })() + + if (err(maybeTag)) return maybeTag + const { tagDeclarator, isTagExisting } = maybeTag + if ('value' in tagDeclarator) { + // Now TypeScript knows tagDeclarator has a value property + return { + modifiedAst: ast, + tag: String(tagDeclarator.value), + isTagExisting, + pathToNode: path, + } + } else { + return new Error('Unable to assign tag without value') + } +} + +export const createLabeledArg = (label: string, arg: Expr): LabeledArg => { + return { label: createIdentifier(label), arg, type: 'LabeledArg' } +} diff --git a/src/lang/errors.test.ts b/src/lang/errors.test.ts index f115eb0da..41393830b 100644 --- a/src/lang/errors.test.ts +++ b/src/lang/errors.test.ts @@ -1,5 +1,7 @@ -import { kclErrorsToDiagnostics, KCLError } from './errors' -import { defaultArtifactGraph, topLevelRange } from 'lang/wasm' +import type { KCLError } from '@src/lang/errors' +import { kclErrorsToDiagnostics } from '@src/lang/errors' +import { defaultArtifactGraph } from '@src/lang/std/artifactGraph' +import { topLevelRange } from '@src/lang/util' describe('test kclErrToDiagnostic', () => { it('converts KCL errors to CodeMirror diagnostics', () => { diff --git a/src/lang/errors.ts b/src/lang/errors.ts index efb35ca67..2612c779d 100644 --- a/src/lang/errors.ts +++ b/src/lang/errors.ts @@ -1,20 +1,20 @@ -import { KclError as RustKclError } from '@rust/kcl-lib/bindings/KclError' -import { CompilationError } from '@rust/kcl-lib/bindings/CompilationError' -import { Diagnostic as CodeMirrorDiagnostic } from '@codemirror/lint' +import type { Diagnostic as CodeMirrorDiagnostic } from '@codemirror/lint' +import type { Text } from '@codemirror/state' import { posToOffset } from '@kittycad/codemirror-lsp-client' -import { Diagnostic as LspDiagnostic } from 'vscode-languageserver-protocol' -import { Text } from '@codemirror/state' -import { EditorView } from 'codemirror' -import { - ArtifactCommand, - ArtifactGraph, - defaultArtifactGraph, - isTopLevelModule, - SourceRange, -} from 'lang/wasm' -import { Operation } from '@rust/kcl-lib/bindings/Operation' -import { ModulePath } from '@rust/kcl-lib/bindings/ModulePath' -import { DefaultPlanes } from '@rust/kcl-lib/bindings/DefaultPlanes' +import type { EditorView } from 'codemirror' +import type { Diagnostic as LspDiagnostic } from 'vscode-languageserver-protocol' + +import type { CompilationError } from '@rust/kcl-lib/bindings/CompilationError' +import type { DefaultPlanes } from '@rust/kcl-lib/bindings/DefaultPlanes' +import type { KclError as RustKclError } from '@rust/kcl-lib/bindings/KclError' +import type { ModulePath } from '@rust/kcl-lib/bindings/ModulePath' +import type { Operation } from '@rust/kcl-lib/bindings/Operation' + +import type { ArtifactCommand } from '@rust/kcl-lib/bindings/Artifact' +import type { SourceRange } from '@rust/kcl-lib/bindings/SourceRange' +import { defaultArtifactGraph } from '@src/lang/std/artifactGraph' +import { isTopLevelModule } from '@src/lang/util' +import type { ArtifactGraph } from '@src/lang/wasm' type ExtractKind = T extends { kind: infer K } ? K : never export class KCLError extends Error { diff --git a/src/lang/executor.test.ts b/src/lang/executor.test.ts index 1ca8e7c8c..682f493ea 100644 --- a/src/lang/executor.test.ts +++ b/src/lang/executor.test.ts @@ -1,15 +1,11 @@ import fs from 'node:fs' -import { - assertParse, - Sketch, - initPromise, - sketchFromKclValue, - defaultArtifactGraph, - topLevelRange, -} from './wasm' -import { enginelessExecutor } from '../lib/testHelpers' -import { KCLError } from './errors' +import { KCLError } from '@src/lang/errors' +import { defaultArtifactGraph } from '@src/lang/std/artifactGraph' +import { topLevelRange } from '@src/lang/util' +import type { Sketch } from '@src/lang/wasm' +import { assertParse, initPromise, sketchFromKclValue } from '@src/lang/wasm' +import { enginelessExecutor } from '@src/lib/testHelpers' beforeAll(async () => { await initPromise diff --git a/src/lang/getNodePathFromSourceRange.test.ts b/src/lang/getNodePathFromSourceRange.test.ts index acec223af..d7fb0a74a 100644 --- a/src/lang/getNodePathFromSourceRange.test.ts +++ b/src/lang/getNodePathFromSourceRange.test.ts @@ -1,8 +1,12 @@ -import { getNodeFromPath, LABELED_ARG_FIELD, ARG_INDEX_FIELD } from './queryAst' -import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils' -import { assertParse, initPromise, Parameter, topLevelRange } from './wasm' -import { err } from 'lib/trap' -import { Name } from '@rust/kcl-lib/bindings/Name' +import type { Name } from '@rust/kcl-lib/bindings/Name' + +import { getNodeFromPath } from '@src/lang/queryAst' +import { ARG_INDEX_FIELD, LABELED_ARG_FIELD } from '@src/lang/queryAstConstants' +import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils' +import { topLevelRange } from '@src/lang/util' +import type { Parameter } from '@src/lang/wasm' +import { assertParse, initPromise } from '@src/lang/wasm' +import { err } from '@src/lib/trap' beforeAll(async () => { await initPromise diff --git a/src/lang/kclSamples.test.ts b/src/lang/kclSamples.test.ts index 6bb9720b4..e9914960d 100644 --- a/src/lang/kclSamples.test.ts +++ b/src/lang/kclSamples.test.ts @@ -1,8 +1,8 @@ -import { assertParse, initPromise } from './wasm' -import { enginelessExecutor } from '../lib/testHelpers' - -import path from 'node:path' import fs from 'node:fs/promises' +import path from 'node:path' + +import { assertParse, initPromise } from '@src/lang/wasm' +import { enginelessExecutor } from '@src/lib/testHelpers' // The purpose of these tests is to act as a first line of defense // if something gets real screwy with our KCL ecosystem. diff --git a/src/lang/langHelpers.ts b/src/lang/langHelpers.ts index a8fd7bd1e..b76ed1f22 100644 --- a/src/lang/langHelpers.ts +++ b/src/lang/langHelpers.ts @@ -1,15 +1,13 @@ -import { - Program, - kclLint, - emptyExecState, - ExecState, - jsAppSettings, -} from 'lang/wasm' -import { KCLError } from 'lang/errors' -import { Diagnostic } from '@codemirror/lint' -import { Node } from '@rust/kcl-lib/bindings/Node' -import RustContext from 'lib/rustContext' -import { EXECUTE_AST_INTERRUPT_ERROR_STRING } from 'lib/constants' +import type { Diagnostic } from '@codemirror/lint' + +import type { Node } from '@rust/kcl-lib/bindings/Node' + +import { KCLError } from '@src/lang/errors' +import type { ExecState, Program } from '@src/lang/wasm' +import { emptyExecState, kclLint } from '@src/lang/wasm' +import { EXECUTE_AST_INTERRUPT_ERROR_STRING } from '@src/lib/constants' +import type RustContext from '@src/lib/rustContext' +import { jsAppSettings } from '@src/lib/settings/settingsUtils' export type ToolTip = | 'lineTo' diff --git a/src/lang/modelingWorkflows.ts b/src/lang/modelingWorkflows.ts index d77bcf4b3..a422a5eb5 100644 --- a/src/lang/modelingWorkflows.ts +++ b/src/lang/modelingWorkflows.ts @@ -5,18 +5,18 @@ * coordinate between different subsystems in the modeling app: * AST, code editor, file system and 3D engine. */ +import type { Node } from '@rust/kcl-lib/bindings/Node' -import { Node } from '@rust/kcl-lib/bindings/Node' -import { KclManager } from 'lang/KclSingleton' -import { ExecutionType } from 'lib/constants' +import type EditorManager from '@src/editor/manager' +import type { KclManager } from '@src/lang/KclSingleton' +import type CodeManager from '@src/lang/codeManager' +import type { PathToNode, Program, SourceRange } from '@src/lang/wasm' +import type { ExecutionType } from '@src/lib/constants' import { EXECUTION_TYPE_MOCK, EXECUTION_TYPE_NONE, EXECUTION_TYPE_REAL, -} from 'lib/constants' -import { PathToNode, Program, SourceRange } from 'lang/wasm' -import EditorManager from 'editor/manager' -import CodeManager from 'lang/codeManager' +} from '@src/lib/constants' /** * Updates the complete modeling state: diff --git a/src/lang/modifyAst.test.ts b/src/lang/modifyAst.test.ts index f647640fb..7fb23708e 100644 --- a/src/lang/modifyAst.test.ts +++ b/src/lang/modifyAst.test.ts @@ -1,38 +1,36 @@ +import type { Node } from '@rust/kcl-lib/bindings/Node' + import { - assertParse, - recast, - initPromise, - Identifier, - topLevelRange, - LiteralValue, - Literal, -} from './wasm' -import { - createLiteral, - createIdentifier, - createCallExpression, - createObjectExpression, createArrayExpression, + createCallExpression, + createIdentifier, + createLiteral, + createObjectExpression, + createPipeExpression, createPipeSubstitution, createVariableDeclaration, - createPipeExpression, findUniqueName, - addSketchTo, giveSketchFnCallTag, - moveValueIntoNewVariable, - sketchOnExtrudedFace, - deleteSegmentFromPipeExpression, - removeSingleConstraintInfo, +} from '@src/lang/create' +import { + addSketchTo, deleteFromSelection, + deleteSegmentFromPipeExpression, + moveValueIntoNewVariable, + removeSingleConstraintInfo, + sketchOnExtrudedFace, splitPipedProfile, -} from './modifyAst' -import { enginelessExecutor } from '../lib/testHelpers' -import { findUsesOfTagInPipe } from './queryAst' -import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils' -import { err } from 'lib/trap' -import { SimplifiedArgDetails } from './std/stdTypes' -import { Node } from '@rust/kcl-lib/bindings/Node' -import { Artifact, codeRefFromRange } from './std/artifactGraph' +} from '@src/lang/modifyAst' +import { findUsesOfTagInPipe } from '@src/lang/queryAst' +import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils' +import type { Artifact } from '@src/lang/std/artifactGraph' +import { codeRefFromRange } from '@src/lang/std/artifactGraph' +import type { SimplifiedArgDetails } from '@src/lang/std/stdTypes' +import { topLevelRange } from '@src/lang/util' +import type { Identifier, Literal, LiteralValue } from '@src/lang/wasm' +import { assertParse, initPromise, recast } from '@src/lang/wasm' +import { enginelessExecutor } from '@src/lib/testHelpers' +import { err } from '@src/lib/trap' beforeAll(async () => { await initPromise diff --git a/src/lang/modifyAst.ts b/src/lang/modifyAst.ts index f7423c95a..f23ad90b5 100644 --- a/src/lang/modifyAst.ts +++ b/src/lang/modifyAst.ts @@ -1,68 +1,46 @@ -import { err, reportRejection, trap } from 'lib/trap' -import { Selection } from 'lib/selections' +import type { Models } from '@kittycad/lib' + +import type { BodyItem } from '@rust/kcl-lib/bindings/BodyItem' +import type { Name } from '@rust/kcl-lib/bindings/Name' +import type { Node } from '@rust/kcl-lib/bindings/Node' + import { - Program, - CallExpression, - LabeledArg, - CallExpressionKw, - PipeExpression, - VariableDeclaration, - VariableDeclarator, - Expr, - Literal, - LiteralValue, - PipeSubstitution, - Identifier, - ArrayExpression, - ObjectExpression, - UnaryExpression, - BinaryExpression, - PathToNode, - SourceRange, - isPathToNodeNumber, - parse, - formatNumber, - ArtifactGraph, - VariableMap, - KclValue, -} from './wasm' + createArrayExpression, + createCallExpression, + createCallExpressionStdLib, + createCallExpressionStdLibKw, + createIdentifier, + createLabeledArg, + createLiteral, + createLocalName, + createObjectExpression, + createPipeExpression, + createPipeSubstitution, + createVariableDeclaration, + findUniqueName, +} from '@src/lang/create' +import { + deleteEdgeTreatment, + locateExtrudeDeclarator, +} from '@src/lang/modifyAst/addEdgeTreatment' import { - isNodeSafeToReplacePath, findAllPreviousVariables, findAllPreviousVariablesPath, - getNodeFromPath, - isNodeSafeToReplace, - traverse, getBodyIndex, + getNodeFromPath, isCallExprWithName, + isNodeSafeToReplace, + isNodeSafeToReplacePath, + traverse, +} from '@src/lang/queryAst' +import { ARG_INDEX_FIELD, LABELED_ARG_FIELD, UNLABELED_ARG, -} from './queryAst' +} from '@src/lang/queryAstConstants' +import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils' +import type { Artifact } from '@src/lang/std/artifactGraph' import { - addTagForSketchOnFace, - ARG_TAG, - getConstraintInfo, - getConstraintInfoKw, -} from './std/sketch' -import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils' -import { - PathToNodeMap, - isLiteralArrayOrStatic, - removeSingleConstraint, - transformAstSketchLines, -} from './std/sketchcombos' -import { DefaultPlaneStr } from 'lib/planes' -import { isArray, isOverlap, roundOff } from 'lib/utils' -import { KCL_DEFAULT_CONSTANT_PREFIXES } from 'lib/constants' -import { SimplifiedArgDetails } from './std/stdTypes' -import { TagDeclarator } from '@rust/kcl-lib/bindings/TagDeclarator' -import { Models } from '@kittycad/lib' -import { ExtrudeFacePlane } from 'machines/modelingMachine' -import { Node } from '@rust/kcl-lib/bindings/Node' -import { KclExpressionWithVariable } from 'lib/commandTypes' -import { - Artifact, expandCap, expandPlane, expandWall, @@ -70,14 +48,43 @@ import { getArtifactsOfTypes, getFaceCodeRef, getPathsFromArtifact, -} from './std/artifactGraph' -import { BodyItem } from '@rust/kcl-lib/bindings/BodyItem' -import { findKwArg } from './util' +} from '@src/lang/std/artifactGraph' import { - deleteEdgeTreatment, - locateExtrudeDeclarator, -} from './modifyAst/addEdgeTreatment' -import { Name } from '@rust/kcl-lib/bindings/Name' + addTagForSketchOnFace, + getConstraintInfo, + getConstraintInfoKw, +} from '@src/lang/std/sketch' +import type { PathToNodeMap } from '@src/lang/std/sketchcombos' +import { + isLiteralArrayOrStatic, + removeSingleConstraint, + transformAstSketchLines, +} from '@src/lang/std/sketchcombos' +import type { SimplifiedArgDetails } from '@src/lang/std/stdTypes' +import type { + ArrayExpression, + ArtifactGraph, + CallExpression, + CallExpressionKw, + Expr, + KclValue, + Literal, + PathToNode, + PipeExpression, + Program, + SourceRange, + VariableDeclaration, + VariableDeclarator, + VariableMap, +} from '@src/lang/wasm' +import { isPathToNodeNumber, parse } from '@src/lang/wasm' +import type { KclExpressionWithVariable } from '@src/lib/commandTypes' +import { KCL_DEFAULT_CONSTANT_PREFIXES } from '@src/lib/constants' +import type { DefaultPlaneStr } from '@src/lib/planes' +import type { Selection } from '@src/lib/selections' +import { err, reportRejection, trap } from '@src/lib/trap' +import { isArray, isOverlap, roundOff } from '@src/lib/utils' +import type { ExtrudeFacePlane } from '@src/machines/modelingMachine' export function startSketchOnDefault( node: Node, @@ -209,41 +216,6 @@ export function addSketchTo( } } -export function findUniqueName( - ast: Program | string, - name: string, - pad = 3, - index = 1 -): string { - let searchStr: string = typeof ast === 'string' ? ast : JSON.stringify(ast) - const indexStr = String(index).padStart(pad, '0') - - const endingDigitsMatcher = /\d+$/ - const nameEndsInDigits = name.match(endingDigitsMatcher) - let nameIsInString = searchStr.includes(`:"${name}"`) - - if (nameEndsInDigits !== null) { - // base case: name is unique and ends in digits - if (!nameIsInString) return name - - // recursive case: name is not unique and ends in digits - const newPad = nameEndsInDigits[0].length - const newIndex = parseInt(nameEndsInDigits[0]) + 1 - const nameWithoutDigits = name.replace(endingDigitsMatcher, '') - - return findUniqueName(searchStr, nameWithoutDigits, newPad, newIndex) - } - - const newName = `${name}${indexStr}` - nameIsInString = searchStr.includes(`:"${newName}"`) - - // base case: name is unique and does not end in digits - if (!nameIsInString) return newName - - // recursive case: name is not unique and does not end in digits - return findUniqueName(searchStr, name, pad, index + 1) -} - /** Set the keyword argument to the given value. Returns true if it overwrote an existing argument. @@ -493,8 +465,8 @@ export function addShell({ insertIndex !== undefined ? insertIndex : modifiedAst.body.length - ? modifiedAst.body.length - : 0 + ? modifiedAst.body.length + : 0 if (modifiedAst.body.length) { modifiedAst.body.splice(insertAt, 0, variable) @@ -553,8 +525,8 @@ export function addSweep({ insertIndex !== undefined ? insertIndex : modifiedAst.body.length - ? modifiedAst.body.length - : 0 + ? modifiedAst.body.length + : 0 modifiedAst.body.length ? modifiedAst.body.splice(insertAt, 0, variable) @@ -787,8 +759,8 @@ export function addOffsetPlane({ insertIndex !== undefined ? insertIndex : modifiedAst.body.length - ? modifiedAst.body.length - : 0 + ? modifiedAst.body.length + : 0 modifiedAst.body.length ? modifiedAst.body.splice(insertAt, 0, newPlane) @@ -866,8 +838,8 @@ export function addHelix({ insertIndex !== undefined ? insertIndex : modifiedAst.body.length - ? modifiedAst.body.length - : 0 + ? modifiedAst.body.length + : 0 modifiedAst.body.length ? modifiedAst.body.splice(insertAt, 0, variable) @@ -1010,372 +982,6 @@ export function splitPathAtPipeExpression(pathToNode: PathToNode): { return splitPathAtPipeExpression(pathToNode.slice(0, -1)) } -/** - * Note: This depends on WASM, but it's not async. Callers are responsible for - * awaiting init of the WASM module. - */ -export function createLiteral(value: LiteralValue | number): Node { - if (typeof value === 'number') { - value = { value, suffix: 'None' } - } - let raw: string - if (typeof value === 'string') { - // TODO: Should we handle escape sequences? - raw = `${value}` - } else if (typeof value === 'boolean') { - raw = `${value}` - } else if (typeof value.value === 'number' && value.suffix === 'None') { - // Fast path for numbers when there are no units. - raw = `${value.value}` - } else { - raw = formatNumber(value.value, value.suffix) - } - return { - type: 'Literal', - start: 0, - end: 0, - moduleId: 0, - value, - raw, - outerAttrs: [], - preComments: [], - commentStart: 0, - } -} - -export function createTagDeclarator(value: string): Node { - return { - type: 'TagDeclarator', - start: 0, - end: 0, - moduleId: 0, - outerAttrs: [], - preComments: [], - commentStart: 0, - - value, - } -} - -export function createIdentifier(name: string): Node { - return { - type: 'Identifier', - start: 0, - end: 0, - moduleId: 0, - outerAttrs: [], - preComments: [], - commentStart: 0, - - name, - } -} - -export function createLocalName(name: string): Node { - return { - type: 'Name', - start: 0, - end: 0, - moduleId: 0, - outerAttrs: [], - preComments: [], - commentStart: 0, - - abs_path: false, - path: [], - name: createIdentifier(name), - } -} - -export function createName(path: [string], name: string): Node { - return { - type: 'Name', - start: 0, - end: 0, - moduleId: 0, - outerAttrs: [], - preComments: [], - commentStart: 0, - - abs_path: false, - path: path.map(createIdentifier), - name: createIdentifier(name), - } -} - -export function createPipeSubstitution(): Node { - return { - type: 'PipeSubstitution', - start: 0, - end: 0, - moduleId: 0, - outerAttrs: [], - preComments: [], - commentStart: 0, - } -} - -export function createCallExpressionStdLib( - name: string, - args: CallExpression['arguments'] -): Node { - return { - type: 'CallExpression', - start: 0, - end: 0, - moduleId: 0, - outerAttrs: [], - preComments: [], - commentStart: 0, - callee: createLocalName(name), - arguments: args, - } -} - -export function createCallExpressionStdLibKw( - name: string, - unlabeled: CallExpressionKw['unlabeled'], - args: CallExpressionKw['arguments'] -): Node { - return { - type: 'CallExpressionKw', - start: 0, - end: 0, - moduleId: 0, - outerAttrs: [], - preComments: [], - commentStart: 0, - nonCodeMeta: nonCodeMetaEmpty(), - callee: createLocalName(name), - unlabeled, - arguments: args, - } -} - -export function createCallExpression( - name: string, - args: CallExpression['arguments'] -): Node { - return { - type: 'CallExpression', - start: 0, - end: 0, - moduleId: 0, - outerAttrs: [], - preComments: [], - commentStart: 0, - callee: createLocalName(name), - arguments: args, - } -} - -export function createArrayExpression( - elements: ArrayExpression['elements'] -): Node { - return { - type: 'ArrayExpression', - start: 0, - end: 0, - moduleId: 0, - outerAttrs: [], - preComments: [], - commentStart: 0, - - nonCodeMeta: nonCodeMetaEmpty(), - elements, - } -} - -export function createPipeExpression( - body: PipeExpression['body'] -): Node { - return { - type: 'PipeExpression', - start: 0, - end: 0, - moduleId: 0, - outerAttrs: [], - preComments: [], - commentStart: 0, - - body, - nonCodeMeta: nonCodeMetaEmpty(), - } -} - -export function createVariableDeclaration( - varName: string, - init: VariableDeclarator['init'], - visibility: VariableDeclaration['visibility'] = 'default', - kind: VariableDeclaration['kind'] = 'const' -): Node { - return { - type: 'VariableDeclaration', - start: 0, - end: 0, - moduleId: 0, - outerAttrs: [], - preComments: [], - commentStart: 0, - - declaration: { - type: 'VariableDeclarator', - start: 0, - end: 0, - moduleId: 0, - outerAttrs: [], - preComments: [], - commentStart: 0, - - id: createIdentifier(varName), - init, - }, - visibility, - kind, - } -} - -export function createObjectExpression(properties: { - [key: string]: Expr -}): Node { - return { - type: 'ObjectExpression', - start: 0, - end: 0, - moduleId: 0, - outerAttrs: [], - preComments: [], - commentStart: 0, - - nonCodeMeta: nonCodeMetaEmpty(), - properties: Object.entries(properties).map(([key, value]) => ({ - type: 'ObjectProperty', - start: 0, - end: 0, - moduleId: 0, - outerAttrs: [], - preComments: [], - commentStart: 0, - key: createIdentifier(key), - - value, - })), - } -} - -export function createUnaryExpression( - argument: UnaryExpression['argument'], - operator: UnaryExpression['operator'] = '-' -): Node { - return { - type: 'UnaryExpression', - start: 0, - end: 0, - moduleId: 0, - outerAttrs: [], - preComments: [], - commentStart: 0, - - operator, - argument, - } -} - -export function createBinaryExpression([left, operator, right]: [ - BinaryExpression['left'], - BinaryExpression['operator'], - BinaryExpression['right'] -]): Node { - return { - type: 'BinaryExpression', - start: 0, - end: 0, - moduleId: 0, - outerAttrs: [], - preComments: [], - commentStart: 0, - - operator, - left, - right, - } -} - -export function createBinaryExpressionWithUnary([left, right]: [ - BinaryExpression['left'], - BinaryExpression['right'] -]): Node { - if (right.type === 'UnaryExpression' && right.operator === '-') - return createBinaryExpression([left, '-', right.argument]) - return createBinaryExpression([left, '+', right]) -} - -export function giveSketchFnCallTag( - ast: Node, - range: SourceRange, - tag?: string -): - | { - modifiedAst: Node - tag: string - isTagExisting: boolean - pathToNode: PathToNode - } - | Error { - const path = getNodePathFromSourceRange(ast, range) - const maybeTag = (() => { - const callNode = getNodeFromPath( - ast, - path, - ['CallExpression', 'CallExpressionKw'] - ) - if (!err(callNode) && callNode.node.type === 'CallExpressionKw') { - const { node: primaryCallExp } = callNode - const existingTag = findKwArg(ARG_TAG, primaryCallExp) - const tagDeclarator = - existingTag || createTagDeclarator(tag || findUniqueName(ast, 'seg', 2)) - const isTagExisting = !!existingTag - if (!isTagExisting) { - callNode.node.arguments.push(createLabeledArg(ARG_TAG, tagDeclarator)) - } - return { tagDeclarator, isTagExisting } - } - - // We've handled CallExpressionKw above, so this has to be positional. - const _node1 = getNodeFromPath(ast, path, 'CallExpression') - if (err(_node1)) return _node1 - const { node: primaryCallExp } = _node1 - - // Tag is always 3rd expression now, using arg index feels brittle - // but we can come up with a better way to identify tag later. - const thirdArg = primaryCallExp.arguments?.[2] - const tagDeclarator = - thirdArg || - (createTagDeclarator( - tag || findUniqueName(ast, 'seg', 2) - ) as TagDeclarator) - const isTagExisting = !!thirdArg - if (!isTagExisting) { - primaryCallExp.arguments[2] = tagDeclarator - } - return { tagDeclarator, isTagExisting } - })() - - if (err(maybeTag)) return maybeTag - const { tagDeclarator, isTagExisting } = maybeTag - if ('value' in tagDeclarator) { - // Now TypeScript knows tagDeclarator has a value property - return { - modifiedAst: ast, - tag: String(tagDeclarator.value), - isTagExisting, - pathToNode: path, - } - } else { - return new Error('Unable to assign tag without value') - } -} - /** * Replace a */ @@ -1557,7 +1163,7 @@ export async function deleteFromSelection( variables: VariableMap, artifactGraph: ArtifactGraph, getFaceDetails: (id: string) => Promise = () => - ({} as any) + ({}) as any ): Promise | Error> { const astClone = structuredClone(ast) let deletionArtifact = selection.artifact @@ -1591,8 +1197,8 @@ export async function deleteFromSelection( deletionArtifact.type === 'plane' ? expandPlane(deletionArtifact, artifactGraph) : deletionArtifact.type === 'wall' - ? expandWall(deletionArtifact, artifactGraph) - : expandCap(deletionArtifact, artifactGraph) + ? expandWall(deletionArtifact, artifactGraph) + : expandCap(deletionArtifact, artifactGraph) for (const path of plane.paths.sort( (a, b) => b.codeRef.range?.[0] - a.codeRef.range?.[0] )) { @@ -1732,12 +1338,12 @@ export async function deleteFromSelection( selection.artifact?.type === 'wall' ? selection.artifact : selection.artifact?.type === 'segment' && - selection.artifact.surfaceId - ? getArtifactOfTypes( - { key: selection.artifact.surfaceId, types: ['wall'] }, - artifactGraph - ) - : null + selection.artifact.surfaceId + ? getArtifactOfTypes( + { key: selection.artifact.surfaceId, types: ['wall'] }, + artifactGraph + ) + : null if (err(wallArtifact)) return if (wallArtifact) { const sweep = getArtifactOfTypes( @@ -1802,15 +1408,15 @@ export async function deleteFromSelection( variable.type === 'Sketch' ? variable.value.artifactId : variable.type === 'Face' - ? variable.value.artifactId - : '' + ? variable.value.artifactId + : '' if (!artifactId) return new Error('Sketch not on anything') const onId = variable.type === 'Sketch' ? variable.value.on.id : variable.type === 'Face' - ? variable.value.id - : '' + ? variable.value.id + : '' if (!onId) return new Error('Sketch not on anything') // Can't kick off multiple requests at once as getFaceDetails // is three engine calls in one and they conflict @@ -1943,10 +1549,6 @@ export function deleteNodeInExtrudePipe( lookup.extrudeDeclarator.init.body.splice(node[pipeIndex][0], 1) } -export const nonCodeMetaEmpty = () => { - return { nonCodeNodes: {}, startNodes: [], start: 0, end: 0 } -} - export function getInsertIndex( sketchNodePaths: PathToNode[], planeNodePath: PathToNode, @@ -1963,8 +1565,8 @@ export function getInsertIndex( const insertIndex = !sketchNodePaths.length ? Number(planeNodePath[1][0]) + 1 : insertType === 'start' - ? minIndex - : maxIndex + 1 + ? minIndex + : maxIndex + 1 return insertIndex } @@ -2107,7 +1709,3 @@ export function createNodeFromExprSnippet( if (!node) return new Error('No node found') return node } - -export const createLabeledArg = (label: string, arg: Expr): LabeledArg => { - return { label: createIdentifier(label), arg, type: 'LabeledArg' } -} diff --git a/src/lang/modifyAst/addEdgeTreatment.test.ts b/src/lang/modifyAst/addEdgeTreatment.test.ts index aa91f9389..35576ace8 100644 --- a/src/lang/modifyAst/addEdgeTreatment.test.ts +++ b/src/lang/modifyAst/addEdgeTreatment.test.ts @@ -1,41 +1,42 @@ -import { - assertParse, - recast, - initPromise, - PathToNode, - Program, - CallExpression, - PipeExpression, - VariableDeclarator, - SourceRange, - topLevelRange, - CallExpressionKw, -} from '../wasm' +import { VITE_KC_DEV_TOKEN } from '@src/env' + +import { createLiteral } from '@src/lang/create' +import type { + ChamferParameters, + EdgeTreatmentParameters, + FilletParameters, +} from '@src/lang/modifyAst/addEdgeTreatment' import { EdgeTreatmentType, + deleteEdgeTreatment, getPathToExtrudeForSegmentSelection, hasValidEdgeTreatmentSelection, isTagUsedInEdgeTreatment, modifyAstWithEdgeTreatmentAndTag, - FilletParameters, - ChamferParameters, - EdgeTreatmentParameters, - deleteEdgeTreatment, -} from './addEdgeTreatment' -import { getNodeFromPath } from '../queryAst' -import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils' -import { createLiteral } from 'lang/modifyAst' -import { err } from 'lib/trap' -import { Selection, Selections } from 'lib/selections' +} from '@src/lang/modifyAst/addEdgeTreatment' +import { getNodeFromPath } from '@src/lang/queryAst' +import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils' +import { codeRefFromRange } from '@src/lang/std/artifactGraph' +import { topLevelRange } from '@src/lang/util' +import type { + CallExpression, + CallExpressionKw, + PathToNode, + PipeExpression, + Program, + SourceRange, + VariableDeclarator, +} from '@src/lang/wasm' +import { assertParse, initPromise, recast } from '@src/lang/wasm' +import type { Selection, Selections } from '@src/lib/selections' import { codeManager, editorManager, engineCommandManager, kclManager, -} from 'lib/singletons' -import { VITE_KC_DEV_TOKEN } from 'env' -import { isOverlap } from 'lib/utils' -import { codeRefFromRange } from 'lang/std/artifactGraph' +} from '@src/lib/singletons' +import { err } from '@src/lib/trap' +import { isOverlap } from '@src/lib/utils' beforeAll(async () => { await initPromise diff --git a/src/lang/modifyAst/addEdgeTreatment.ts b/src/lang/modifyAst/addEdgeTreatment.ts index db6a285ad..01d4a174f 100644 --- a/src/lang/modifyAst/addEdgeTreatment.ts +++ b/src/lang/modifyAst/addEdgeTreatment.ts @@ -1,4 +1,36 @@ +import type { Name } from '@rust/kcl-lib/bindings/Name' +import type { Node } from '@rust/kcl-lib/bindings/Node' + +import type EditorManager from '@src/editor/manager' +import type { KclManager } from '@src/lang/KclSingleton' +import type CodeManager from '@src/lang/codeManager' +import { ARG_TAG } from '@src/lang/constants' import { + createArrayExpression, + createCallExpressionStdLib, + createCallExpressionStdLibKw, + createLabeledArg, + createLocalName, + createPipeExpression, +} from '@src/lang/create' +import { updateModelingState } from '@src/lang/modelingWorkflows' +import { + getNodeFromPath, + hasSketchPipeBeenExtruded, + traverse, +} from '@src/lang/queryAst' +import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils' +import type { Artifact } from '@src/lang/std/artifactGraph' +import { getSweepArtifactFromSelection } from '@src/lang/std/artifactGraph' +import type { EngineCommandManager } from '@src/lang/std/engineConnection' +import { + addTagForSketchOnFace, + getTagFromCallExpression, + sketchLineHelperMap, + sketchLineHelperMapKw, +} from '@src/lang/std/sketch' +import { findKwArg } from '@src/lang/util' +import type { ArtifactGraph, CallExpression, CallExpressionKw, @@ -9,42 +41,12 @@ import { Program, VariableDeclaration, VariableDeclarator, -} from '../wasm' -import { - createCallExpressionStdLib, - createArrayExpression, - createLocalName, - createPipeExpression, - createCallExpressionStdLibKw, - createLabeledArg, -} from '../modifyAst' -import { - getNodeFromPath, - hasSketchPipeBeenExtruded, - traverse, -} from '../queryAst' -import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils' -import { - addTagForSketchOnFace, - ARG_TAG, - getTagFromCallExpression, - sketchLineHelperMap, - sketchLineHelperMapKw, -} from '../std/sketch' -import { err } from 'lib/trap' -import { Selection, Selections } from 'lib/selections' -import { KclCommandValue } from 'lib/commandTypes' -import { isArray } from 'lib/utils' -import { Artifact, getSweepArtifactFromSelection } from 'lang/std/artifactGraph' -import { Node } from '@rust/kcl-lib/bindings/Node' -import { findKwArg } from 'lang/util' -import { KclManager } from 'lang/KclSingleton' -import { EXECUTION_TYPE_REAL } from 'lib/constants' -import { EngineCommandManager } from 'lang/std/engineConnection' -import EditorManager from 'editor/manager' -import CodeManager from 'lang/codeManager' -import { updateModelingState } from 'lang/modelingWorkflows' -import { Name } from '@rust/kcl-lib/bindings/Name' +} from '@src/lang/wasm' +import type { KclCommandValue } from '@src/lib/commandTypes' +import { EXECUTION_TYPE_REAL } from '@src/lib/constants' +import type { Selection, Selections } from '@src/lib/selections' +import { err } from '@src/lib/trap' +import { isArray } from '@src/lib/utils' // Edge Treatment Types export enum EdgeTreatmentType { diff --git a/src/lang/modifyAst/addRevolve.ts b/src/lang/modifyAst/addRevolve.ts index 3841a7f7b..bfd0460f3 100644 --- a/src/lang/modifyAst/addRevolve.ts +++ b/src/lang/modifyAst/addRevolve.ts @@ -1,27 +1,30 @@ -import { err } from 'lib/trap' -import { KCL_DEFAULT_CONSTANT_PREFIXES } from 'lib/constants' -import { Program, PathToNode, Expr, VariableDeclarator } from 'lang/wasm' -import { Selections } from 'lib/selections' -import { Node } from '@rust/kcl-lib/bindings/Node' +import type { Node } from '@rust/kcl-lib/bindings/Node' + import { - createLiteral, - createLocalName, - findUniqueName, - createVariableDeclaration, createCallExpressionStdLibKw, createLabeledArg, -} from 'lang/modifyAst' + createLiteral, + createLocalName, + createVariableDeclaration, + findUniqueName, +} from '@src/lang/create' import { - ARG_INDEX_FIELD, - getNodeFromPath, - LABELED_ARG_FIELD, -} from 'lang/queryAst' -import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils' -import { - mutateAstWithTagForSketchSegment, getEdgeTagCall, -} from 'lang/modifyAst/addEdgeTreatment' -import { getSafeInsertIndex } from 'lang/queryAst/getSafeInsertIndex' + mutateAstWithTagForSketchSegment, +} from '@src/lang/modifyAst/addEdgeTreatment' +import { getNodeFromPath } from '@src/lang/queryAst' +import { getSafeInsertIndex } from '@src/lang/queryAst/getSafeInsertIndex' +import { ARG_INDEX_FIELD, LABELED_ARG_FIELD } from '@src/lang/queryAstConstants' +import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils' +import type { + Expr, + PathToNode, + Program, + VariableDeclarator, +} from '@src/lang/wasm' +import { KCL_DEFAULT_CONSTANT_PREFIXES } from '@src/lib/constants' +import type { Selections } from '@src/lib/selections' +import { err } from '@src/lib/trap' export function getAxisExpressionAndIndex( axisOrEdge: 'Axis' | 'Edge', diff --git a/src/lang/modifyAst/boolean.ts b/src/lang/modifyAst/boolean.ts index 322907cc2..a1f9cca21 100644 --- a/src/lang/modifyAst/boolean.ts +++ b/src/lang/modifyAst/boolean.ts @@ -1,8 +1,8 @@ -import { Node } from '@rust/kcl-lib/bindings/Node' -import EditorManager from 'editor/manager' -import CodeManager from 'lang/codeManager' -import { KclManager } from 'lang/KclSingleton' -import { updateModelingState } from 'lang/modelingWorkflows' +import type { Node } from '@rust/kcl-lib/bindings/Node' + +import type EditorManager from '@src/editor/manager' +import type { KclManager } from '@src/lang/KclSingleton' +import type CodeManager from '@src/lang/codeManager' import { createArrayExpression, createCallExpressionStdLibKw, @@ -10,21 +10,22 @@ import { createLocalName, createVariableDeclaration, findUniqueName, -} from 'lang/modifyAst' -import { getNodeFromPath } from 'lang/queryAst' -import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils' -import { getFaceCodeRef } from 'lang/std/artifactGraph' -import { EngineCommandManager } from 'lang/std/engineConnection' -import { +} from '@src/lang/create' +import { updateModelingState } from '@src/lang/modelingWorkflows' +import { getNodeFromPath } from '@src/lang/queryAst' +import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils' +import { getFaceCodeRef } from '@src/lang/std/artifactGraph' +import type { EngineCommandManager } from '@src/lang/std/engineConnection' +import type { Artifact, ArtifactGraph, Program, VariableDeclaration, -} from 'lang/wasm' -import { EXECUTION_TYPE_REAL } from 'lib/constants' -import { Selection, Selections } from 'lib/selections' -import { err } from 'lib/trap' -import { isArray } from 'lib/utils' +} from '@src/lang/wasm' +import { EXECUTION_TYPE_REAL } from '@src/lib/constants' +import type { Selection, Selections } from '@src/lib/selections' +import { err } from '@src/lib/trap' +import { isArray } from '@src/lib/utils' export async function applySubtractFromTargetOperatorSelections( target: Selection, diff --git a/src/lang/modifyAst/deleteSelection.ts b/src/lang/modifyAst/deleteSelection.ts index 95b46921b..4dc654ab1 100644 --- a/src/lang/modifyAst/deleteSelection.ts +++ b/src/lang/modifyAst/deleteSelection.ts @@ -1,16 +1,16 @@ -import { Selection } from 'lib/selections' -import { getFaceDetails } from 'clientSideScene/sceneEntities' -import { deleteFromSelection } from 'lang/modifyAst' +import { executeAstMock } from '@src/lang/langHelpers' +import { updateModelingState } from '@src/lang/modelingWorkflows' +import { deleteFromSelection } from '@src/lang/modifyAst' +import { EXECUTION_TYPE_REAL } from '@src/lib/constants' +import type { Selection } from '@src/lib/selections' import { codeManager, editorManager, kclManager, rustContext, -} from 'lib/singletons' -import { err } from 'lib/trap' -import { executeAstMock } from 'lang/langHelpers' -import { updateModelingState } from 'lang/modelingWorkflows' -import { EXECUTION_TYPE_REAL } from 'lib/constants' + sceneEntitiesManager, +} from '@src/lib/singletons' +import { err } from '@src/lib/trap' export const deletionErrorMessage = 'Unable to delete selection. Please edit manually in code pane.' @@ -25,7 +25,7 @@ export async function deleteSelectionPromise( selection, kclManager.variables, kclManager.artifactGraph, - getFaceDetails + sceneEntitiesManager.getFaceDetails ) if (err(modifiedAst)) { return new Error(deletionErrorMessage) diff --git a/src/lang/modifyAst/setAppearance.ts b/src/lang/modifyAst/setAppearance.ts index 1775569eb..b1cc397ea 100644 --- a/src/lang/modifyAst/setAppearance.ts +++ b/src/lang/modifyAst/setAppearance.ts @@ -1,15 +1,16 @@ -import { PathToNode, Program } from 'lang/wasm' -import { Node } from '@rust/kcl-lib/bindings/Node' -import { locateExtrudeDeclarator } from './addEdgeTreatment' -import { err } from 'lib/trap' +import type { Node } from '@rust/kcl-lib/bindings/Node' + import { createCallExpressionStdLibKw, createLabeledArg, createLiteral, createPipeExpression, -} from 'lang/modifyAst' -import { createPipeSubstitution } from 'lang/modifyAst' -import { COMMAND_APPEARANCE_COLOR_DEFAULT } from 'lib/commandBarConfigs/modelingCommandConfig' + createPipeSubstitution, +} from '@src/lang/create' +import { locateExtrudeDeclarator } from '@src/lang/modifyAst/addEdgeTreatment' +import type { PathToNode, Program } from '@src/lang/wasm' +import { COMMAND_APPEARANCE_COLOR_DEFAULT } from '@src/lib/commandBarConfigs/modelingCommandConfig' +import { err } from '@src/lib/trap' export function setAppearance({ ast, diff --git a/src/lang/project.ts b/src/lang/project.ts index 17141a034..aada9a51b 100644 --- a/src/lang/project.ts +++ b/src/lang/project.ts @@ -1,11 +1,14 @@ -import { UnitLength } from '@rust/kcl-lib/bindings/ModelingCmd' +import type { UnitLength } from '@rust/kcl-lib/bindings/ModelingCmd' + import { changeKclSettings, unitAngleToUnitAng, unitLengthToUnitLen, -} from './wasm' -import { DEFAULT_DEFAULT_ANGLE_UNIT } from 'lib/constants' -import { DEFAULT_DEFAULT_LENGTH_UNIT } from 'lib/constants' +} from '@src/lang/wasm' +import { + DEFAULT_DEFAULT_ANGLE_UNIT, + DEFAULT_DEFAULT_LENGTH_UNIT, +} from '@src/lib/constants' /** * Create a new KCL file with the given initial content and default length unit. diff --git a/src/lang/queryAst.test.ts b/src/lang/queryAst.test.ts index 7a1261c37..eb314c1b4 100644 --- a/src/lang/queryAst.test.ts +++ b/src/lang/queryAst.test.ts @@ -1,36 +1,32 @@ -import { - assertParse, - recast, - initPromise, - PathToNode, - Identifier, - topLevelRange, -} from './wasm' -import { - findAllPreviousVariables, - isNodeSafeToReplace, - isTypeInValue, - hasExtrudeSketch, - findUsesOfTagInPipe, - hasSketchPipeBeenExtruded, - doesSceneHaveSweepableSketch, - traverse, - getNodeFromPath, - doesSceneHaveExtrudedSketch, -} from './queryAst' -import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils' -import { enginelessExecutor } from '../lib/testHelpers' +import type { Name } from '@rust/kcl-lib/bindings/Name' + import { createArrayExpression, createCallExpression, + createCallExpressionStdLib, createLiteral, createPipeSubstitution, - createCallExpressionStdLib, -} from './modifyAst' -import { err } from 'lib/trap' -import { codeRefFromRange } from './std/artifactGraph' -import { addCallExpressionsToPipe, addCloseToPipe } from 'lang/std/sketch' -import { Name } from '@rust/kcl-lib/bindings/Name' +} from '@src/lang/create' +import { + doesSceneHaveExtrudedSketch, + doesSceneHaveSweepableSketch, + findAllPreviousVariables, + findUsesOfTagInPipe, + getNodeFromPath, + hasExtrudeSketch, + hasSketchPipeBeenExtruded, + isNodeSafeToReplace, + isTypeInValue, + traverse, +} from '@src/lang/queryAst' +import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils' +import { codeRefFromRange } from '@src/lang/std/artifactGraph' +import { addCallExpressionsToPipe, addCloseToPipe } from '@src/lang/std/sketch' +import { topLevelRange } from '@src/lang/util' +import type { Identifier, PathToNode } from '@src/lang/wasm' +import { assertParse, initPromise, recast } from '@src/lang/wasm' +import { enginelessExecutor } from '@src/lib/testHelpers' +import { err } from '@src/lib/trap' beforeAll(async () => { await initPromise diff --git a/src/lang/queryAst.ts b/src/lang/queryAst.ts index d2d16251d..b741ab14c 100644 --- a/src/lang/queryAst.ts +++ b/src/lang/queryAst.ts @@ -1,54 +1,56 @@ -import { ToolTip } from 'lang/langHelpers' -import { Selection, Selections } from 'lib/selections' +import type { FunctionExpression } from '@rust/kcl-lib/bindings/FunctionExpression' +import type { ImportStatement } from '@rust/kcl-lib/bindings/ImportStatement' +import type { Node } from '@rust/kcl-lib/bindings/Node' +import type { TypeDeclaration } from '@rust/kcl-lib/bindings/TypeDeclaration' + +import { ARG_TAG } from '@src/lang/constants' +import { createLocalName } from '@src/lang/create' +import type { ToolTip } from '@src/lang/langHelpers' +import { splitPathAtLastIndex } from '@src/lang/modifyAst' +import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils' +import { codeRefFromRange } from '@src/lang/std/artifactGraph' +import { getArgForEnd, getFirstArg } from '@src/lang/std/sketch' +import { getSketchSegmentFromSourceRange } from '@src/lang/std/sketchConstraints' import { + getConstraintLevelFromSourceRange, + getConstraintType, +} from '@src/lang/std/sketchcombos' +import { findKwArg, topLevelRange } from '@src/lang/util' +import type { ArrayExpression, + ArtifactGraph, BinaryExpression, CallExpression, CallExpressionKw, Expr, ExpressionStatement, + Identifier, ObjectExpression, ObjectProperty, PathToNode, PipeExpression, Program, ReturnStatement, - sketchFromKclValue, - sketchFromKclValueOptional, SourceRange, SyntaxType, - topLevelRange, VariableDeclaration, VariableDeclarator, - recast, - ArtifactGraph, - kclSettings, - unitLenToUnitLength, - unitAngToUnitAngle, VariableMap, - Identifier, -} from './wasm' -import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils' -import { createLocalName, splitPathAtLastIndex } from './modifyAst' -import { getSketchSegmentFromSourceRange } from './std/sketchConstraints' -import { getAngle, isArray } from '../lib/utils' -import { ARG_TAG, getArgForEnd, getFirstArg } from './std/sketch' +} from '@src/lang/wasm' import { - getConstraintLevelFromSourceRange, - getConstraintType, -} from './std/sketchcombos' -import { err, Reason } from 'lib/trap' -import { Node } from '@rust/kcl-lib/bindings/Node' -import { findKwArg } from './util' -import { codeRefFromRange } from './std/artifactGraph' -import { FunctionExpression } from '@rust/kcl-lib/bindings/FunctionExpression' -import { ImportStatement } from '@rust/kcl-lib/bindings/ImportStatement' -import { KclSettingsAnnotation } from 'lib/settings/settingsTypes' -import { TypeDeclaration } from '@rust/kcl-lib/bindings/TypeDeclaration' + kclSettings, + recast, + sketchFromKclValue, + sketchFromKclValueOptional, + unitAngToUnitAngle, + unitLenToUnitLength, +} from '@src/lang/wasm' +import type { Selection, Selections } from '@src/lib/selections' +import type { KclSettingsAnnotation } from '@src/lib/settings/settingsTypes' +import { Reason, err } from '@src/lib/trap' +import { getAngle, isArray } from '@src/lib/utils' -export const LABELED_ARG_FIELD = 'LabeledArg -> Arg' -export const UNLABELED_ARG = 'unlabeled first arg' -export const ARG_INDEX_FIELD = 'arg index' +import { ARG_INDEX_FIELD, LABELED_ARG_FIELD } from '@src/lang/queryAstConstants' /** * Retrieves a node from a given path within a Program node structure, optionally stopping at a specified node type. diff --git a/src/lang/queryAst/getIdentifiersInProgram.test.ts b/src/lang/queryAst/getIdentifiersInProgram.test.ts index c2a2cf557..42f1f55a0 100644 --- a/src/lang/queryAst/getIdentifiersInProgram.test.ts +++ b/src/lang/queryAst/getIdentifiersInProgram.test.ts @@ -1,5 +1,5 @@ -import { assertParse, initPromise } from 'lang/wasm' -import { getIdentifiersInProgram } from './getIndentifiersInProgram' +import { getIdentifiersInProgram } from '@src/lang/queryAst/getIndentifiersInProgram' +import { assertParse, initPromise } from '@src/lang/wasm' function identifier(name: string, start: number, end: number) { return { diff --git a/src/lang/queryAst/getIndentifiersInProgram.ts b/src/lang/queryAst/getIndentifiersInProgram.ts index 4a06d8b4d..2189baa29 100644 --- a/src/lang/queryAst/getIndentifiersInProgram.ts +++ b/src/lang/queryAst/getIndentifiersInProgram.ts @@ -1,6 +1,7 @@ -import { traverse } from 'lang/queryAst' -import { Expr, Name, Program } from 'lang/wasm' -import { Node } from '@rust/kcl-lib/bindings/Node' +import type { Node } from '@rust/kcl-lib/bindings/Node' + +import { traverse } from '@src/lang/queryAst' +import type { Expr, Name, Program } from '@src/lang/wasm' /** * Given an AST `Program`, return an array of diff --git a/src/lang/queryAst/getSafeInsertIndex.test.ts b/src/lang/queryAst/getSafeInsertIndex.test.ts index af62d6bd1..23d574b80 100644 --- a/src/lang/queryAst/getSafeInsertIndex.test.ts +++ b/src/lang/queryAst/getSafeInsertIndex.test.ts @@ -1,5 +1,5 @@ -import { assertParse, initPromise } from 'lang/wasm' -import { getSafeInsertIndex } from './getSafeInsertIndex' +import { getSafeInsertIndex } from '@src/lang/queryAst/getSafeInsertIndex' +import { assertParse, initPromise } from '@src/lang/wasm' beforeAll(async () => { await initPromise diff --git a/src/lang/queryAst/getSafeInsertIndex.ts b/src/lang/queryAst/getSafeInsertIndex.ts index 37c6587b1..fd01e68e0 100644 --- a/src/lang/queryAst/getSafeInsertIndex.ts +++ b/src/lang/queryAst/getSafeInsertIndex.ts @@ -1,7 +1,8 @@ -import { getIdentifiersInProgram } from './getIndentifiersInProgram' -import { Program, Expr } from 'lang/wasm' -import { Node } from '@rust/kcl-lib/bindings/Node' -import { getTagDeclaratorsInProgram } from './getTagDeclaratorsInProgram' +import type { Node } from '@rust/kcl-lib/bindings/Node' + +import { getIdentifiersInProgram } from '@src/lang/queryAst/getIndentifiersInProgram' +import { getTagDeclaratorsInProgram } from '@src/lang/queryAst/getTagDeclaratorsInProgram' +import type { Expr, Program } from '@src/lang/wasm' /** * Given a target expression, return the body index of the last-used variable diff --git a/src/lang/queryAst/getTagDeclaratorsInProgram.test.ts b/src/lang/queryAst/getTagDeclaratorsInProgram.test.ts index d8a78ebb2..883078415 100644 --- a/src/lang/queryAst/getTagDeclaratorsInProgram.test.ts +++ b/src/lang/queryAst/getTagDeclaratorsInProgram.test.ts @@ -1,5 +1,5 @@ -import { assertParse, initPromise } from 'lang/wasm' -import { getTagDeclaratorsInProgram } from './getTagDeclaratorsInProgram' +import { getTagDeclaratorsInProgram } from '@src/lang/queryAst/getTagDeclaratorsInProgram' +import { assertParse, initPromise } from '@src/lang/wasm' function tagDeclaratorWithIndex( value: string, diff --git a/src/lang/queryAst/getTagDeclaratorsInProgram.ts b/src/lang/queryAst/getTagDeclaratorsInProgram.ts index 31e96132e..e1291c8e8 100644 --- a/src/lang/queryAst/getTagDeclaratorsInProgram.ts +++ b/src/lang/queryAst/getTagDeclaratorsInProgram.ts @@ -1,8 +1,9 @@ -import { getBodyIndex, traverse } from 'lang/queryAst' -import { Expr, Program } from 'lang/wasm' -import { Node } from '@rust/kcl-lib/bindings/Node' -import { TagDeclarator } from '@rust/kcl-lib/bindings/TagDeclarator' -import { err } from 'lib/trap' +import type { Node } from '@rust/kcl-lib/bindings/Node' +import type { TagDeclarator } from '@rust/kcl-lib/bindings/TagDeclarator' + +import { getBodyIndex, traverse } from '@src/lang/queryAst' +import type { Expr, Program } from '@src/lang/wasm' +import { err } from '@src/lib/trap' type TagWithBodyIndex = { tag: TagDeclarator; bodyIndex: number } diff --git a/src/lang/queryAst/getVariableDeclaration.ts b/src/lang/queryAst/getVariableDeclaration.ts index 277952293..036c345fe 100644 --- a/src/lang/queryAst/getVariableDeclaration.ts +++ b/src/lang/queryAst/getVariableDeclaration.ts @@ -1,5 +1,6 @@ -import { Node } from '@rust/kcl-lib/bindings/Node' -import { Program } from 'lang/wasm' +import type { Node } from '@rust/kcl-lib/bindings/Node' + +import type { Program } from '@src/lang/wasm' /** * Given a program and a variable name, return the variable declaration diff --git a/src/lang/queryAst/getVariableDeclarationIndex.ts b/src/lang/queryAst/getVariableDeclarationIndex.ts index d3960771b..976d362e9 100644 --- a/src/lang/queryAst/getVariableDeclarationIndex.ts +++ b/src/lang/queryAst/getVariableDeclarationIndex.ts @@ -1,5 +1,6 @@ -import { Node } from '@rust/kcl-lib/bindings/Node' -import { Program } from 'lang/wasm' +import type { Node } from '@rust/kcl-lib/bindings/Node' + +import type { Program } from '@src/lang/wasm' /** * Given a program and a variable name, return the index of the variable declaration diff --git a/src/lang/queryAstConstants.ts b/src/lang/queryAstConstants.ts new file mode 100644 index 000000000..452829ba9 --- /dev/null +++ b/src/lang/queryAstConstants.ts @@ -0,0 +1,3 @@ +export const LABELED_ARG_FIELD = 'LabeledArg -> Arg' +export const UNLABELED_ARG = 'unlabeled first arg' +export const ARG_INDEX_FIELD = 'arg index' diff --git a/src/lang/queryAstNodePathUtils.ts b/src/lang/queryAstNodePathUtils.ts index 2f00457fb..6c1b9000d 100644 --- a/src/lang/queryAstNodePathUtils.ts +++ b/src/lang/queryAstNodePathUtils.ts @@ -1,17 +1,18 @@ -import { +import type { ImportStatement } from '@rust/kcl-lib/bindings/ImportStatement' +import type { Node } from '@rust/kcl-lib/bindings/Node' +import type { TypeDeclaration } from '@rust/kcl-lib/bindings/TypeDeclaration' + +import { ARG_INDEX_FIELD, LABELED_ARG_FIELD } from '@src/lang/queryAstConstants' +import type { Expr, ExpressionStatement, - VariableDeclaration, - ReturnStatement, - SourceRange, + Identifier, PathToNode, Program, - Identifier, -} from './wasm' -import { ImportStatement } from '@rust/kcl-lib/bindings/ImportStatement' -import { Node } from '@rust/kcl-lib/bindings/Node' -import { ARG_INDEX_FIELD, LABELED_ARG_FIELD } from './queryAst' -import { TypeDeclaration } from '@rust/kcl-lib/bindings/TypeDeclaration' + ReturnStatement, + SourceRange, + VariableDeclaration, +} from '@src/lang/wasm' function moreNodePathFromSourceRange( node: Node< diff --git a/src/lang/recast.test.ts b/src/lang/recast.test.ts index 38110f71b..5ae49d83b 100644 --- a/src/lang/recast.test.ts +++ b/src/lang/recast.test.ts @@ -1,6 +1,8 @@ -import { assertParse, Program, recast, initPromise } from './wasm' import fs from 'node:fs' -import { err } from 'lib/trap' + +import type { Program } from '@src/lang/wasm' +import { assertParse, initPromise, recast } from '@src/lang/wasm' +import { err } from '@src/lib/trap' beforeAll(async () => { await initPromise diff --git a/src/lang/std/artifactGraph.ts b/src/lang/std/artifactGraph.ts index b13b2c81c..05adb0647 100644 --- a/src/lang/std/artifactGraph.ts +++ b/src/lang/std/artifactGraph.ts @@ -1,35 +1,41 @@ -import { - Expr, - Artifact, - ArtifactGraph, - ArtifactId, - PathToNode, - Program, - SourceRange, - PathArtifact, - PlaneArtifact, - WallArtifact, - SegmentArtifact, - Solid2dArtifact as Solid2D, - SweepArtifact, - SweepEdge, - CapArtifact, - EdgeCut, -} from 'lang/wasm' -import { Models } from '@kittycad/lib' -import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils' -import { Selection } from 'lib/selections' -import { err } from 'lib/trap' -import { +import type { Models } from '@kittycad/lib' + +import type { Cap, + CapSubType, Plane, StartSketchOnFace, StartSketchOnPlane, Wall, } from '@rust/kcl-lib/bindings/Artifact' -import { CapSubType } from '@rust/kcl-lib/bindings/Artifact' -export type { Artifact, ArtifactId, SegmentArtifact } from 'lang/wasm' +import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils' +import type { + Artifact, + ArtifactGraph, + ArtifactId, + CapArtifact, + EdgeCut, + Expr, + PathArtifact, + PathToNode, + PlaneArtifact, + Program, + SegmentArtifact, + Solid2dArtifact as Solid2D, + SourceRange, + SweepArtifact, + SweepEdge, + WallArtifact, +} from '@src/lang/wasm' +import type { Selection } from '@src/lib/selections' +import { err } from '@src/lib/trap' + +export type { Artifact, ArtifactId, SegmentArtifact } from '@src/lang/wasm' + +export function defaultArtifactGraph(): ArtifactGraph { + return new Map() +} interface BaseArtifact { id: ArtifactId diff --git a/src/lang/std/engineConnection.ts b/src/lang/std/engineConnection.ts index 855510a34..f2bd71603 100644 --- a/src/lang/std/engineConnection.ts +++ b/src/lang/std/engineConnection.ts @@ -1,22 +1,24 @@ -import { defaultSourceRange, SourceRange } from 'lang/wasm' -import { VITE_KC_API_WS_MODELING_URL, VITE_KC_DEV_TOKEN } from 'env' -import { Models } from '@kittycad/lib' -import { uuidv4, binaryToUuid } from 'lib/utils' +import type { Models } from '@kittycad/lib' +import { VITE_KC_API_WS_MODELING_URL, VITE_KC_DEV_TOKEN } from '@src/env' import { BSON } from 'bson' + +import type { MachineManager } from '@src/components/MachineManagerProvider' +import type { useModelingContext } from '@src/hooks/useModelingContext' +import type { KclManager } from '@src/lang/KclSingleton' +import type { EngineCommand, ResponseMap } from '@src/lang/std/artifactGraph' +import type { SourceRange } from '@src/lang/wasm' +import { defaultSourceRange } from '@src/lang/wasm' +import { EXECUTE_AST_INTERRUPT_ERROR_MESSAGE } from '@src/lib/constants' +import { markOnce } from '@src/lib/performance' +import type { SettingsViaQueryString } from '@src/lib/settings/settingsTypes' import { Themes, - getThemeColorForEngine, - getOppositeTheme, darkModeMatcher, -} from 'lib/theme' -import { EngineCommand, ResponseMap } from 'lang/std/artifactGraph' -import { useModelingContext } from 'hooks/useModelingContext' -import { SettingsViaQueryString } from 'lib/settings/settingsTypes' -import { EXECUTE_AST_INTERRUPT_ERROR_MESSAGE } from 'lib/constants' -import { KclManager } from 'lang/KclSingleton' -import { reportRejection } from 'lib/trap' -import { markOnce } from 'lib/performance' -import { MachineManager } from 'components/MachineManagerProvider' + getOppositeTheme, + getThemeColorForEngine, +} from '@src/lib/theme' +import { reportRejection } from '@src/lib/trap' +import { binaryToUuid, uuidv4 } from '@src/lib/utils' // TODO(paultag): This ought to be tweakable. const pingIntervalMs = 5_000 @@ -48,8 +50,8 @@ interface WebRTCClientMetrics extends ClientMetrics { type Value = U extends undefined ? { type: T; value: U } : U extends void - ? { type: T } - : { type: T; value: U } + ? { type: T } + : { type: T; value: U } type State = Value @@ -309,8 +311,10 @@ class EngineConnection extends EventTarget { private engineCommandManager: EngineCommandManager private pingPongSpan: { ping?: Date; pong?: Date } - private pingIntervalId: ReturnType = setInterval(() => {}, - 60_000) + private pingIntervalId: ReturnType = setInterval( + () => {}, + 60_000 + ) isUsingConnectionLite: boolean = false constructor({ diff --git a/src/lang/std/sketch.test.ts b/src/lang/std/sketch.test.ts index 0a47f2f86..77b5335de 100644 --- a/src/lang/std/sketch.test.ts +++ b/src/lang/std/sketch.test.ts @@ -1,26 +1,22 @@ +import type { Node } from '@rust/kcl-lib/bindings/Node' + +import { getNodeFromPath } from '@src/lang/queryAst' +import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils' import { - changeSketchArguments, - addTagForSketchOnFace, - addNewSketchLn, - getYComponent, - getXComponent, addCloseToPipe, + addNewSketchLn, + addTagForSketchOnFace, + changeSketchArguments, getConstraintInfo, getConstraintInfoKw, -} from './sketch' -import { - assertParse, - recast, - initPromise, - CallExpression, - topLevelRange, - CallExpressionKw, -} from '../wasm' -import { getNodeFromPath } from '../queryAst' -import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils' -import { enginelessExecutor } from '../../lib/testHelpers' -import { err } from 'lib/trap' -import { Node } from '@rust/kcl-lib/bindings/Node' + getXComponent, + getYComponent, +} from '@src/lang/std/sketch' +import { topLevelRange } from '@src/lang/util' +import type { CallExpression, CallExpressionKw } from '@src/lang/wasm' +import { assertParse, initPromise, recast } from '@src/lang/wasm' +import { enginelessExecutor } from '@src/lib/testHelpers' +import { err } from '@src/lib/trap' const eachQuad: [number, [number, number]][] = [ [-315, [1, 1]], diff --git a/src/lang/std/sketch.ts b/src/lang/std/sketch.ts index 1076ef19d..514062c17 100644 --- a/src/lang/std/sketch.ts +++ b/src/lang/std/sketch.ts @@ -1,88 +1,86 @@ +import { perpendicularDistance } from 'sketch-helpers' + +import type { Name } from '@rust/kcl-lib/bindings/Name' +import type { Node } from '@rust/kcl-lib/bindings/Node' +import type { TagDeclarator } from '@rust/kcl-lib/bindings/TagDeclarator' + import { - Path, - Sketch, - SourceRange, - PathToNode, - Program, - PipeExpression, - CallExpression, - CallExpressionKw, - VariableDeclarator, - Expr, - VariableDeclaration, - sketchFromKclValue, - topLevelRange, - VariableMap, -} from 'lang/wasm' + ARG_CIRCLE_CENTER, + ARG_CIRCLE_RADIUS, + ARG_END, + ARG_END_ABSOLUTE, + ARG_LENGTH, + ARG_TAG, + DETERMINING_ARGS, +} from '@src/lang/constants' import { - ARG_INDEX_FIELD, - getNodeFromPath, - getNodeFromPathCurry, - LABELED_ARG_FIELD, -} from 'lang/queryAst' -import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils' + createArrayExpression, + createCallExpression, + createCallExpressionStdLibKw, + createLabeledArg, + createLiteral, + createObjectExpression, + createPipeExpression, + createPipeSubstitution, + createTagDeclarator, + findUniqueName, + nonCodeMetaEmpty, +} from '@src/lang/create' +import type { ToolTip } from '@src/lang/langHelpers' +import { toolTips } from '@src/lang/langHelpers' +import { + mutateArrExp, + mutateKwArg, + mutateObjExpProp, + removeKwArgs, + splitPathAtPipeExpression, +} from '@src/lang/modifyAst' +import { getNodeFromPath, getNodeFromPathCurry } from '@src/lang/queryAst' +import { ARG_INDEX_FIELD, LABELED_ARG_FIELD } from '@src/lang/queryAstConstants' +import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils' import { isLiteralArrayOrStatic, isNotLiteralArrayOrStatic, -} from 'lang/std/sketchcombos' -import { toolTips, ToolTip } from 'lang/langHelpers' -import { - createPipeExpression, - mutateKwArg, - nonCodeMetaEmpty, - removeKwArgs, - splitPathAtPipeExpression, -} from '../modifyAst' - -import { - SketchLineHelper, - ConstrainInfo, - ArrayItemInput, - ObjectPropertyInput, - SingleValueInput, +} from '@src/lang/std/sketchcombos' +import type { AddTagInfo, + ArrayItemInput, + ConstrainInfo, + CreatedSketchExprResult, + InputArgKeys, + ObjectPropertyInput, + RawArgs, SegmentInputs, SimplifiedArgDetails, - RawArgs, - CreatedSketchExprResult, + SingleValueInput, + SketchLineHelper, SketchLineHelperKw, - InputArgKeys, -} from 'lang/std/stdTypes' - -import { - createLiteral, - createTagDeclarator, - createCallExpression, - createCallExpressionStdLibKw, - createArrayExpression, - createLabeledArg, - createPipeSubstitution, - createObjectExpression, - mutateArrExp, - mutateObjExpProp, - findUniqueName, -} from 'lang/modifyAst' -import { roundOff, getLength, getAngle, isArray } from 'lib/utils' -import { err } from 'lib/trap' -import { perpendicularDistance } from 'sketch-helpers' -import { TagDeclarator } from '@rust/kcl-lib/bindings/TagDeclarator' -import { EdgeCutInfo } from 'machines/modelingMachine' -import { Node } from '@rust/kcl-lib/bindings/Node' +} from '@src/lang/std/stdTypes' import { findKwArg, - findKwArgWithIndex, findKwArgAny, findKwArgAnyIndex, -} from 'lang/util' -import { Name } from '@rust/kcl-lib/bindings/Name' - -export const ARG_TAG = 'tag' -export const ARG_END = 'end' -export const ARG_LENGTH = 'length' -export const ARG_END_ABSOLUTE = 'endAbsolute' -export const ARG_CIRCLE_CENTER = 'center' -export const ARG_CIRCLE_RADIUS = 'radius' -export const DETERMINING_ARGS = [ARG_LENGTH, ARG_END, ARG_END_ABSOLUTE] + findKwArgWithIndex, + topLevelRange, +} from '@src/lang/util' +import type { + CallExpression, + CallExpressionKw, + Expr, + Path, + PathToNode, + PipeExpression, + Program, + Sketch, + SourceRange, + VariableDeclaration, + VariableDeclarator, + VariableMap, +} from '@src/lang/wasm' +import { sketchFromKclValue } from '@src/lang/wasm' +import { err } from '@src/lib/trap' +import { getAngle, getLength, isArray, roundOff } from '@src/lib/utils' +import type { EdgeCutInfo } from '@src/machines/modelingMachine' const STRAIGHT_SEGMENT_ERR = new Error( 'Invalid input, expected "straight-segment"' @@ -158,10 +156,10 @@ const constrainInfo = ( g === 'singleValue' ? { type: 'singleValue' } : typeof g === 'number' - ? { type: 'arrayItem', index: g } - : typeof g === 'string' - ? { type: 'objectProperty', key: g } - : undefined, + ? { type: 'arrayItem', index: g } + : typeof g === 'string' + ? { type: 'objectProperty', key: g } + : undefined, pathToNode: e, stdLibFnName: f, }) @@ -178,7 +176,7 @@ const commonConstraintInfoHelper = ( { arrayInput?: 0 | 1 objInput?: ObjectPropertyInput['key'] - } + }, ], code: string, pathToNode: PathToNode, @@ -3459,10 +3457,11 @@ function isAngleLiteral(lineArugement: Expr): boolean { return lineArugement?.type === 'ArrayExpression' ? isLiteralArrayOrStatic(lineArugement.elements[0]) : lineArugement?.type === 'ObjectExpression' - ? isLiteralArrayOrStatic( - lineArugement.properties.find(({ key }) => key.name === 'angle')?.value - ) - : false + ? isLiteralArrayOrStatic( + lineArugement.properties.find(({ key }) => key.name === 'angle') + ?.value + ) + : false } type addTagFn = ( diff --git a/src/lang/std/sketchConstraints.test.ts b/src/lang/std/sketchConstraints.test.ts index 67f091556..2c51751be 100644 --- a/src/lang/std/sketchConstraints.test.ts +++ b/src/lang/std/sketchConstraints.test.ts @@ -1,21 +1,20 @@ +import { codeRefFromRange } from '@src/lang/std/artifactGraph' +import { getSketchSegmentFromSourceRange } from '@src/lang/std/sketchConstraints' +import type { ConstraintType } from '@src/lang/std/sketchcombos' import { - assertParse, - Sketch, - recast, - initPromise, - sketchFromKclValue, - SourceRange, - topLevelRange, -} from '../wasm' -import { - ConstraintType, getTransformInfos, transformAstSketchLines, -} from './sketchcombos' -import { getSketchSegmentFromSourceRange } from './sketchConstraints' -import { enginelessExecutor } from '../../lib/testHelpers' -import { err } from 'lib/trap' -import { codeRefFromRange } from './artifactGraph' +} from '@src/lang/std/sketchcombos' +import { topLevelRange } from '@src/lang/util' +import type { Sketch, SourceRange } from '@src/lang/wasm' +import { + assertParse, + initPromise, + recast, + sketchFromKclValue, +} from '@src/lang/wasm' +import { enginelessExecutor } from '@src/lib/testHelpers' +import { err } from '@src/lib/trap' beforeAll(async () => { await initPromise diff --git a/src/lang/std/sketchConstraints.ts b/src/lang/std/sketchConstraints.ts index f9c57e490..09f280d83 100644 --- a/src/lang/std/sketchConstraints.ts +++ b/src/lang/std/sketchConstraints.ts @@ -1,22 +1,23 @@ -import { getNodeFromPath } from 'lang/queryAst' -import { ToolTip, toolTips } from 'lang/langHelpers' -import { Node } from '@rust/kcl-lib/bindings/Node' -import { - Program, - VariableDeclarator, +import type { Node } from '@rust/kcl-lib/bindings/Node' + +import { DETERMINING_ARGS } from '@src/lang/constants' +import type { ToolTip } from '@src/lang/langHelpers' +import { toolTips } from '@src/lang/langHelpers' +import { getNodeFromPath } from '@src/lang/queryAst' +import { findKwArgAny, topLevelRange } from '@src/lang/util' +import type { CallExpression, - Sketch, - SourceRange, + CallExpressionKw, + Expr, + LabeledArg, Path, PathToNode, - Expr, - topLevelRange, - LabeledArg, - CallExpressionKw, -} from '../wasm' -import { err } from 'lib/trap' -import { findKwArgAny } from 'lang/util' -import { DETERMINING_ARGS } from './sketch' + Program, + Sketch, + SourceRange, + VariableDeclarator, +} from '@src/lang/wasm' +import { err } from '@src/lib/trap' export function getSketchSegmentFromPathToNode( sketch: Sketch, diff --git a/src/lang/std/sketchcombos.test.ts b/src/lang/std/sketchcombos.test.ts index 13c1dc6ec..e27e97756 100644 --- a/src/lang/std/sketchcombos.test.ts +++ b/src/lang/std/sketchcombos.test.ts @@ -1,32 +1,24 @@ +import { ARG_END, ARG_END_ABSOLUTE } from '@src/lang/constants' +import type { ToolTip } from '@src/lang/langHelpers' +import { codeRefFromRange } from '@src/lang/std/artifactGraph' +import { getArgForEnd, isAbsoluteLine } from '@src/lang/std/sketch' +import type { + ConstraintLevel, + ConstraintType, +} from '@src/lang/std/sketchcombos' import { - assertParse, - Expr, - recast, - initPromise, - Program, - topLevelRange, -} from '../wasm' -import { + getConstraintLevelFromSourceRange, getConstraintType, getTransformInfos, transformAstSketchLines, transformSecondarySketchLinesTagFirst, - ConstraintType, - ConstraintLevel, - getConstraintLevelFromSourceRange, -} from './sketchcombos' -import { ToolTip } from 'lang/langHelpers' -import { Selections, Selection } from 'lib/selections' -import { err } from 'lib/trap' -import { enginelessExecutor } from '../../lib/testHelpers' -import { codeRefFromRange } from './artifactGraph' -import { findKwArg } from 'lang/util' -import { - ARG_END, - ARG_END_ABSOLUTE, - getArgForEnd, - isAbsoluteLine, -} from './sketch' +} from '@src/lang/std/sketchcombos' +import { findKwArg, topLevelRange } from '@src/lang/util' +import type { Expr, Program } from '@src/lang/wasm' +import { assertParse, initPromise, recast } from '@src/lang/wasm' +import type { Selection, Selections } from '@src/lib/selections' +import { enginelessExecutor } from '@src/lib/testHelpers' +import { err } from '@src/lib/trap' beforeAll(async () => { await initPromise diff --git a/src/lang/std/sketchcombos.ts b/src/lang/std/sketchcombos.ts index a5ba6029a..17aa4cbba 100644 --- a/src/lang/std/sketchcombos.ts +++ b/src/lang/std/sketchcombos.ts @@ -1,31 +1,12 @@ +import type { Node } from '@rust/kcl-lib/bindings/Node' + import { - CreatedSketchExprResult, - CreateStdLibSketchCallExpr, - InputArg, - InputArgs, - SimplifiedArgDetails, - TransformInfo, -} from './stdTypes' -import { ToolTip, toolTips } from 'lang/langHelpers' -import { Selections } from 'lib/selections' -import { cleanErrs, err } from 'lib/trap' -import { - CallExpression, - CallExpressionKw, - Program, - Expr, - BinaryPart, - VariableDeclarator, - PathToNode, - sketchFromKclValue, - Literal, - SourceRange, - LiteralValue, - LabeledArg, - VariableMap, -} from '../wasm' -import { getNodeFromPath, getNodeFromPathCurry } from '../queryAst' -import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils' + ARG_END, + ARG_END_ABSOLUTE, + ARG_LENGTH, + ARG_TAG, + DETERMINING_ARGS, +} from '@src/lang/constants' import { createArrayExpression, createBinaryExpression, @@ -40,31 +21,54 @@ import { createPipeSubstitution, createUnaryExpression, giveSketchFnCallTag, -} from '../modifyAst' +} from '@src/lang/create' +import type { ToolTip } from '@src/lang/langHelpers' +import { toolTips } from '@src/lang/langHelpers' +import { getNodeFromPath, getNodeFromPathCurry } from '@src/lang/queryAst' +import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils' import { createFirstArg, - getConstraintInfo, - getFirstArg, - getArgForEnd, - replaceSketchLine, - ARG_TAG, - ARG_END, - ARG_END_ABSOLUTE, - getConstraintInfoKw, - isAbsoluteLine, - getCircle, - ARG_LENGTH, - tooltipToFnName, fnNameToTooltip, - DETERMINING_ARGS, -} from './sketch' + getArgForEnd, + getCircle, + getConstraintInfo, + getConstraintInfoKw, + getFirstArg, + isAbsoluteLine, + replaceSketchLine, + tooltipToFnName, +} from '@src/lang/std/sketch' import { getSketchSegmentFromPathToNode, getSketchSegmentFromSourceRange, -} from './sketchConstraints' -import { getAngle, roundOff, normaliseAngle, isArray } from '../../lib/utils' -import { Node } from '@rust/kcl-lib/bindings/Node' -import { findKwArg, findKwArgAny } from 'lang/util' +} from '@src/lang/std/sketchConstraints' +import type { + CreateStdLibSketchCallExpr, + CreatedSketchExprResult, + InputArg, + InputArgs, + SimplifiedArgDetails, + TransformInfo, +} from '@src/lang/std/stdTypes' +import { findKwArg, findKwArgAny } from '@src/lang/util' +import type { + BinaryPart, + CallExpression, + CallExpressionKw, + Expr, + LabeledArg, + Literal, + LiteralValue, + PathToNode, + Program, + SourceRange, + VariableDeclarator, + VariableMap, +} from '@src/lang/wasm' +import { sketchFromKclValue } from '@src/lang/wasm' +import type { Selections } from '@src/lib/selections' +import { cleanErrs, err } from '@src/lib/trap' +import { getAngle, isArray, normaliseAngle, roundOff } from '@src/lib/utils' export type LineInputsType = | 'xAbsolute' @@ -322,8 +326,8 @@ const xyLineSetLength = const lineVal = forceValueUsedInTransform ? forceValueUsedInTransform : referenceSeg - ? segRef - : args[0].expr + ? segRef + : args[0].expr const literalArg = asNum(args[0].expr.value) if (err(literalArg)) return literalArg return createCallWrapper(xOrY, lineVal, tag, literalArg) @@ -351,18 +355,18 @@ const basicAngledLineCreateNode = varValToUse === 'ang' ? inputs[0].expr : referenceSeg === 'ang' - ? getClosesAngleDirection( - argValue, - refAng, - createSegAngle(referenceSegName) - ) - : args[0].expr + ? getClosesAngleDirection( + argValue, + refAng, + createSegAngle(referenceSegName) + ) + : args[0].expr const nonForcedLen = varValToUse === 'len' ? inputs[1].expr : referenceSeg === 'len' - ? createSegLen(referenceSegName) - : args[1].expr + ? createSegLen(referenceSegName) + : args[1].expr const shouldForceAng = valToForce === 'ang' && forceValueUsedInTransform const shouldForceLen = valToForce === 'len' && forceValueUsedInTransform const literalArg = asNum( @@ -678,13 +682,13 @@ const setAngleBetweenCreateNode = tranformToType === 'none' ? 'angledLine' : tranformToType === 'xAbs' - ? 'angledLineToX' - : 'angledLineToY', + ? 'angledLineToX' + : 'angledLineToY', tranformToType === 'none' ? [binExp, args[1].expr] : tranformToType === 'xAbs' - ? [binExp, inputs[0].expr] - : [binExp, inputs[1].expr], + ? [binExp, inputs[0].expr] + : [binExp, inputs[1].expr], tag, valueUsedInTransform ) @@ -2104,17 +2108,17 @@ export function transformAstSketchLines({ ccw: true, // Default to counter-clockwise for circles } : seg.type === 'CircleThreePoint' || seg.type === 'ArcThreePoint' - ? { - type: 'circle-three-point-segment', - p1: seg.p1, - p2: seg.p2, - p3: seg.p3, - } - : { - type: 'straight-segment', - to, - from, - }, + ? { + type: 'circle-three-point-segment', + p1: seg.p1, + p2: seg.p2, + p3: seg.p3, + } + : { + type: 'straight-segment', + to, + from, + }, replaceExistingCallback: (rawArgs) => callBack({ diff --git a/src/lang/std/std.test.ts b/src/lang/std/std.test.ts index f18183ab2..cb33c6506 100644 --- a/src/lang/std/std.test.ts +++ b/src/lang/std/std.test.ts @@ -1,5 +1,5 @@ -import { assertParse, initPromise } from '../wasm' -import { enginelessExecutor } from '../../lib/testHelpers' +import { assertParse, initPromise } from '@src/lang/wasm' +import { enginelessExecutor } from '@src/lib/testHelpers' beforeAll(async () => { await initPromise diff --git a/src/lang/std/stdTypes.ts b/src/lang/std/stdTypes.ts index c29c5e677..157d42b2c 100644 --- a/src/lang/std/stdTypes.ts +++ b/src/lang/std/stdTypes.ts @@ -1,18 +1,19 @@ -import { ToolTip } from 'lang/langHelpers' -import { - Path, - SourceRange, - Program, - Expr, - PathToNode, - CallExpression, - Literal, +import type { Node } from '@rust/kcl-lib/bindings/Node' + +import type { ToolTip } from '@src/lang/langHelpers' +import type { LineInputsType } from '@src/lang/std/sketchcombos' +import type { BinaryPart, + CallExpression, CallExpressionKw, + Expr, + Literal, + Path, + PathToNode, + Program, + SourceRange, VariableMap, -} from '../wasm' -import { LineInputsType } from './sketchcombos' -import { Node } from '@rust/kcl-lib/bindings/Node' +} from '@src/lang/wasm' export interface ModifyAstBase { node: Node diff --git a/src/lang/util.ts b/src/lang/util.ts index aa91956c6..16033f019 100644 --- a/src/lang/util.ts +++ b/src/lang/util.ts @@ -1,18 +1,35 @@ -import { Selections } from 'lib/selections' -import { - PathToNode, - CallExpression, - Literal, +import { filterArtifacts, getFaceCodeRef } from '@src/lang/std/artifactGraph' +import type { ArrayExpression, - BinaryExpression, ArtifactGraph, + BinaryExpression, + CallExpression, CallExpressionKw, Expr, + Literal, LiteralValue, NumericSuffix, -} from './wasm' -import { filterArtifacts, getFaceCodeRef } from 'lang/std/artifactGraph' -import { isArray, isOverlap } from 'lib/utils' + PathToNode, +} from '@src/lang/wasm' +import type { Selections } from '@src/lib/selections' +import { isArray, isOverlap } from '@src/lib/utils' + +import type { SourceRange } from '@rust/kcl-lib/bindings/SourceRange' + +/** + * Create a SourceRange for the top-level module. + */ +export function topLevelRange(start: number, end: number): SourceRange { + return [start, end, 0] +} + +/** + * Returns true if this source range is from the file being executed. Returns + * false if it's from a file that was imported. + */ +export function isTopLevelModule(range: SourceRange): boolean { + return range[2] === 0 +} /** * Updates pathToNode body indices to account for the insertion of an expression @@ -81,12 +98,12 @@ export function isCursorInSketchCommandRange( firstEntry?.type === 'segment' ? firstEntry.pathId : ((firstEntry?.type === 'plane' || - firstEntry?.type === 'cap' || - firstEntry?.type === 'wall') && - firstEntry.pathIds?.length) || - false - ? firstEntry.pathIds[0] - : false + firstEntry?.type === 'cap' || + firstEntry?.type === 'wall') && + firstEntry.pathIds?.length) || + false + ? firstEntry.pathIds[0] + : false return parentId ? parentId diff --git a/src/lang/wasm.test.ts b/src/lang/wasm.test.ts index e792085c5..313cb1c61 100644 --- a/src/lang/wasm.test.ts +++ b/src/lang/wasm.test.ts @@ -1,8 +1,10 @@ -import { err } from 'lib/trap' -import { formatNumber, initPromise, parse, ParseResult } from './wasm' -import { enginelessExecutor } from 'lib/testHelpers' -import { Node } from '@rust/kcl-lib/bindings/Node' -import { Program } from '@rust/kcl-lib/bindings/Program' +import type { Node } from '@rust/kcl-lib/bindings/Node' +import type { Program } from '@rust/kcl-lib/bindings/Program' + +import type { ParseResult } from '@src/lang/wasm' +import { formatNumber, initPromise, parse } from '@src/lang/wasm' +import { enginelessExecutor } from '@src/lib/testHelpers' +import { err } from '@src/lib/trap' beforeEach(async () => { await initPromise diff --git a/src/lang/wasm.ts b/src/lang/wasm.ts index 861264234..42235a69d 100644 --- a/src/lang/wasm.ts +++ b/src/lang/wasm.ts @@ -1,105 +1,110 @@ -import { - init, - parse_wasm, - recast_wasm, - format_number, - kcl_lint, - is_points_ccw, - get_tangential_arc_to_info, - get_kcl_version, - coredump, - default_app_settings, - parse_app_settings, - parse_project_settings, - default_project_settings, - base64_decode, - kcl_settings, - change_kcl_settings, - serialize_project_configuration, - serialize_configuration, - reloadModule, - is_kcl_empty_or_only_settings, -} from 'lib/wasm_lib_wrapper' - -import { KCLError } from './errors' -import { KclError as RustKclError } from '@rust/kcl-lib/bindings/KclError' -import { Discovered } from '@rust/kcl-lib/bindings/Discovered' -import { KclValue } from '@rust/kcl-lib/bindings/KclValue' +import type { + ArtifactCommand, + ArtifactId, + ArtifactGraph as RustArtifactGraph, +} from '@rust/kcl-lib/bindings/Artifact' +import type { CompilationError } from '@rust/kcl-lib/bindings/CompilationError' +import type { Configuration } from '@rust/kcl-lib/bindings/Configuration' +import type { CoreDumpInfo } from '@rust/kcl-lib/bindings/CoreDumpInfo' +import type { DefaultPlanes } from '@rust/kcl-lib/bindings/DefaultPlanes' +import type { Discovered } from '@rust/kcl-lib/bindings/Discovered' +import type { ExecOutcome as RustExecOutcome } from '@rust/kcl-lib/bindings/ExecOutcome' +import type { KclError as RustKclError } from '@rust/kcl-lib/bindings/KclError' +import type { KclErrorWithOutputs } from '@rust/kcl-lib/bindings/KclErrorWithOutputs' +import type { KclValue } from '@rust/kcl-lib/bindings/KclValue' +import type { MetaSettings } from '@rust/kcl-lib/bindings/MetaSettings' +import type { UnitAngle, UnitLength } from '@rust/kcl-lib/bindings/ModelingCmd' +import type { ModulePath } from '@rust/kcl-lib/bindings/ModulePath' +import type { Node } from '@rust/kcl-lib/bindings/Node' +import type { NumericSuffix } from '@rust/kcl-lib/bindings/NumericSuffix' +import type { Operation } from '@rust/kcl-lib/bindings/Operation' import type { Program } from '@rust/kcl-lib/bindings/Program' -import { Coords2d } from './std/sketch' -import { CoreDumpInfo } from '@rust/kcl-lib/bindings/CoreDumpInfo' -import { CoreDumpManager } from 'lib/coredump' -import openWindow from 'lib/openWindow' -import { TEST } from 'env' -import { err, Reason } from 'lib/trap' -import { Configuration } from '@rust/kcl-lib/bindings/Configuration' -import { DeepPartial } from 'lib/types' -import { ProjectConfiguration } from '@rust/kcl-lib/bindings/ProjectConfiguration' -import { Sketch } from '@rust/kcl-lib/bindings/Sketch' -import { ExecOutcome as RustExecOutcome } from '@rust/kcl-lib/bindings/ExecOutcome' -import { Node } from '@rust/kcl-lib/bindings/Node' -import { CompilationError } from '@rust/kcl-lib/bindings/CompilationError' -import { SourceRange } from '@rust/kcl-lib/bindings/SourceRange' -import { getAllCurrentSettings } from 'lib/settings/settingsUtils' -import { Operation } from '@rust/kcl-lib/bindings/Operation' -import { KclErrorWithOutputs } from '@rust/kcl-lib/bindings/KclErrorWithOutputs' -import { ArtifactId } from '@rust/kcl-lib/bindings/Artifact' -import { ArtifactCommand } from '@rust/kcl-lib/bindings/Artifact' -import { ArtifactGraph as RustArtifactGraph } from '@rust/kcl-lib/bindings/Artifact' -import { Artifact } from './std/artifactGraph' -import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils' -import { NumericSuffix } from '@rust/kcl-lib/bindings/NumericSuffix' -import { MetaSettings } from '@rust/kcl-lib/bindings/MetaSettings' -import { UnitAngle, UnitLength } from '@rust/kcl-lib/bindings/ModelingCmd' -import { UnitLen } from '@rust/kcl-lib/bindings/UnitLen' -import { UnitAngle as UnitAng } from '@rust/kcl-lib/bindings/UnitAngle' -import { ModulePath } from '@rust/kcl-lib/bindings/ModulePath' -import { DefaultPlanes } from '@rust/kcl-lib/bindings/DefaultPlanes' -import { isArray } from 'lib/utils' +import type { ProjectConfiguration } from '@rust/kcl-lib/bindings/ProjectConfiguration' +import type { Sketch } from '@rust/kcl-lib/bindings/Sketch' +import type { SourceRange } from '@rust/kcl-lib/bindings/SourceRange' +import type { UnitAngle as UnitAng } from '@rust/kcl-lib/bindings/UnitAngle' +import type { UnitLen } from '@rust/kcl-lib/bindings/UnitLen' + +import { KCLError } from '@src/lang/errors' +import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils' +import { + defaultArtifactGraph, + type Artifact, +} from '@src/lang/std/artifactGraph' +import type { Coords2d } from '@src/lang/std/sketch' import { DEFAULT_DEFAULT_ANGLE_UNIT, DEFAULT_DEFAULT_LENGTH_UNIT, -} from 'lib/constants' +} from '@src/lib/constants' +import type { CoreDumpManager } from '@src/lib/coredump' +import openWindow from '@src/lib/openWindow' +import { Reason, err } from '@src/lib/trap' +import type { DeepPartial } from '@src/lib/types' +import { isArray } from '@src/lib/utils' +import { + base64_decode, + change_kcl_settings, + coredump, + default_app_settings, + default_project_settings, + format_number, + get_kcl_version, + get_tangential_arc_to_info, + init, + is_kcl_empty_or_only_settings, + is_points_ccw, + kcl_lint, + kcl_settings, + parse_app_settings, + parse_project_settings, + parse_wasm, + recast_wasm, + reloadModule, + serialize_configuration, + serialize_project_configuration, +} from '@src/lib/wasm_lib_wrapper' -export type { Artifact } from '@rust/kcl-lib/bindings/Artifact' -export type { ArtifactCommand } from '@rust/kcl-lib/bindings/Artifact' -export type { ArtifactId } from '@rust/kcl-lib/bindings/Artifact' -export type { Cap as CapArtifact } from '@rust/kcl-lib/bindings/Artifact' -export type { CodeRef } from '@rust/kcl-lib/bindings/Artifact' -export type { EdgeCut } from '@rust/kcl-lib/bindings/Artifact' -export type { Path as PathArtifact } from '@rust/kcl-lib/bindings/Artifact' -export type { Plane as PlaneArtifact } from '@rust/kcl-lib/bindings/Artifact' -export type { Segment as SegmentArtifact } from '@rust/kcl-lib/bindings/Artifact' -export type { Solid2d as Solid2dArtifact } from '@rust/kcl-lib/bindings/Artifact' -export type { Sweep as SweepArtifact } from '@rust/kcl-lib/bindings/Artifact' -export type { SweepEdge } from '@rust/kcl-lib/bindings/Artifact' -export type { Wall as WallArtifact } from '@rust/kcl-lib/bindings/Artifact' -export type { Configuration } from '@rust/kcl-lib/bindings/Configuration' -export type { Program } from '@rust/kcl-lib/bindings/Program' -export type { Expr } from '@rust/kcl-lib/bindings/Expr' -export type { ObjectExpression } from '@rust/kcl-lib/bindings/ObjectExpression' -export type { ObjectProperty } from '@rust/kcl-lib/bindings/ObjectProperty' -export type { MemberExpression } from '@rust/kcl-lib/bindings/MemberExpression' -export type { PipeExpression } from '@rust/kcl-lib/bindings/PipeExpression' -export type { VariableDeclaration } from '@rust/kcl-lib/bindings/VariableDeclaration' -export type { Parameter } from '@rust/kcl-lib/bindings/Parameter' -export type { PipeSubstitution } from '@rust/kcl-lib/bindings/PipeSubstitution' -export type { Identifier } from '@rust/kcl-lib/bindings/Identifier' -export type { Name } from '@rust/kcl-lib/bindings/Name' -export type { UnaryExpression } from '@rust/kcl-lib/bindings/UnaryExpression' +export type { ArrayExpression } from '@rust/kcl-lib/bindings/ArrayExpression' +export type { + Artifact, + ArtifactCommand, + ArtifactId, + Cap as CapArtifact, + CodeRef, + EdgeCut, + Path as PathArtifact, + Plane as PlaneArtifact, + Segment as SegmentArtifact, + Solid2d as Solid2dArtifact, + Sweep as SweepArtifact, + SweepEdge, + Wall as WallArtifact, +} from '@rust/kcl-lib/bindings/Artifact' export type { BinaryExpression } from '@rust/kcl-lib/bindings/BinaryExpression' -export type { ReturnStatement } from '@rust/kcl-lib/bindings/ReturnStatement' -export type { ExpressionStatement } from '@rust/kcl-lib/bindings/ExpressionStatement' +export type { BinaryPart } from '@rust/kcl-lib/bindings/BinaryPart' export type { CallExpression } from '@rust/kcl-lib/bindings/CallExpression' export type { CallExpressionKw } from '@rust/kcl-lib/bindings/CallExpressionKw' +export type { Configuration } from '@rust/kcl-lib/bindings/Configuration' +export type { Expr } from '@rust/kcl-lib/bindings/Expr' +export type { ExpressionStatement } from '@rust/kcl-lib/bindings/ExpressionStatement' +export type { Identifier } from '@rust/kcl-lib/bindings/Identifier' export type { LabeledArg } from '@rust/kcl-lib/bindings/LabeledArg' -export type { VariableDeclarator } from '@rust/kcl-lib/bindings/VariableDeclarator' -export type { BinaryPart } from '@rust/kcl-lib/bindings/BinaryPart' export type { Literal } from '@rust/kcl-lib/bindings/Literal' export type { LiteralValue } from '@rust/kcl-lib/bindings/LiteralValue' -export type { ArrayExpression } from '@rust/kcl-lib/bindings/ArrayExpression' -export type { SourceRange } from '@rust/kcl-lib/bindings/SourceRange' +export type { MemberExpression } from '@rust/kcl-lib/bindings/MemberExpression' +export type { Name } from '@rust/kcl-lib/bindings/Name' export type { NumericSuffix } from '@rust/kcl-lib/bindings/NumericSuffix' +export type { ObjectExpression } from '@rust/kcl-lib/bindings/ObjectExpression' +export type { ObjectProperty } from '@rust/kcl-lib/bindings/ObjectProperty' +export type { Parameter } from '@rust/kcl-lib/bindings/Parameter' +export type { PipeExpression } from '@rust/kcl-lib/bindings/PipeExpression' +export type { PipeSubstitution } from '@rust/kcl-lib/bindings/PipeSubstitution' +export type { Program } from '@rust/kcl-lib/bindings/Program' +export type { ReturnStatement } from '@rust/kcl-lib/bindings/ReturnStatement' +export type { SourceRange } from '@rust/kcl-lib/bindings/SourceRange' +export type { UnaryExpression } from '@rust/kcl-lib/bindings/UnaryExpression' +export type { VariableDeclaration } from '@rust/kcl-lib/bindings/VariableDeclaration' +export type { VariableDeclarator } from '@rust/kcl-lib/bindings/VariableDeclarator' export type SyntaxType = | 'Program' @@ -123,11 +128,11 @@ export type SyntaxType = | 'NonCodeNode' | 'UnaryExpression' +export type { ExtrudeSurface } from '@rust/kcl-lib/bindings/ExtrudeSurface' +export type { KclValue } from '@rust/kcl-lib/bindings/KclValue' export type { Path } from '@rust/kcl-lib/bindings/Path' export type { Sketch } from '@rust/kcl-lib/bindings/Sketch' export type { Solid } from '@rust/kcl-lib/bindings/Solid' -export type { KclValue } from '@rust/kcl-lib/bindings/KclValue' -export type { ExtrudeSurface } from '@rust/kcl-lib/bindings/ExtrudeSurface' /** * Convert a SourceRange as used inside the KCL interpreter into the above one for use in the @@ -146,21 +151,6 @@ export function defaultSourceRange(): SourceRange { return [0, 0, 0] } -/** - * Create a SourceRange for the top-level module. - */ -export function topLevelRange(start: number, end: number): SourceRange { - return [start, end, 0] -} - -/** - * Returns true if this source range is from the file being executed. Returns - * false if it's from a file that was imported. - */ -export function isTopLevelModule(range: SourceRange): boolean { - return range[2] === 0 -} - function firstSourceRange(error: RustKclError): SourceRange { return error.sourceRanges.length > 0 ? sourceRangeFromRust(error.sourceRanges[0]) @@ -384,10 +374,6 @@ function rustArtifactGraphToMap( return map } -export function defaultArtifactGraph(): ArtifactGraph { - return new Map() -} - // TODO: In the future, make the parameter be a KclValue. export function sketchFromKclValueOptional( obj: any, @@ -422,19 +408,6 @@ export function sketchFromKclValue( return result } -export const jsAppSettings = async () => { - let jsAppSettings = default_app_settings() - if (!TEST) { - const settings = await import('machines/appMachine').then((module) => - module.getSettings() - ) - if (settings) { - jsAppSettings = getAllCurrentSettings(settings) - } - } - return jsAppSettings -} - export const errFromErrWithOutputs = (e: any): KCLError => { const parsed: KclErrorWithOutputs = JSON.parse(e.toString()) return new KCLError( diff --git a/src/lib/artifactIndex.ts b/src/lib/artifactIndex.ts index 19cb92b8a..e9d582e8b 100644 --- a/src/lib/artifactIndex.ts +++ b/src/lib/artifactIndex.ts @@ -1,5 +1,10 @@ -import { ArtifactGraph, ArtifactId, SourceRange, Artifact } from 'lang/wasm' -import { getFaceCodeRef } from 'lang/std/artifactGraph' +import { getFaceCodeRef } from '@src/lang/std/artifactGraph' +import type { + Artifact, + ArtifactGraph, + ArtifactId, + SourceRange, +} from '@src/lang/wasm' // Index artifacts in an ordered list for binary search export type ArtifactEntry = { artifact: Artifact; id: ArtifactId } diff --git a/src/lib/base64.test.ts b/src/lib/base64.test.ts index edb7db9d2..d33281931 100644 --- a/src/lib/base64.test.ts +++ b/src/lib/base64.test.ts @@ -1,5 +1,6 @@ import { expect } from 'vitest' -import { base64ToString, stringToBase64 } from './base64' + +import { base64ToString, stringToBase64 } from '@src/lib/base64' describe('base64 encoding', () => { test('to base64, simple code', async () => { diff --git a/src/lib/browserSaveFile.ts b/src/lib/browserSaveFile.ts index 73004d695..33a347656 100644 --- a/src/lib/browserSaveFile.ts +++ b/src/lib/browserSaveFile.ts @@ -1,9 +1,9 @@ /// The method below uses the File System Access API when it's supported and // else falls back to the classic approach. In both cases the function saves // the file, but in case of where the File System Access API is supported, the - import toast from 'react-hot-toast' -import { EXPORT_TOAST_MESSAGES } from './constants' + +import { EXPORT_TOAST_MESSAGES } from '@src/lib/constants' // user will get a file save dialog where they can choose where the file should be saved. export const browserSaveFile = async ( diff --git a/src/lib/cameraControls.ts b/src/lib/cameraControls.ts index 00fe4ccd5..1a8f7fc53 100644 --- a/src/lib/cameraControls.ts +++ b/src/lib/cameraControls.ts @@ -1,5 +1,6 @@ -import { MouseControlType } from '@rust/kcl-lib/bindings/MouseControlType' -import { platform } from './utils' +import type { MouseControlType } from '@rust/kcl-lib/bindings/MouseControlType' + +import { platform } from '@src/lib/utils' const PLATFORM = platform() const META = @@ -39,7 +40,6 @@ export function mouseControlsToCameraSystem( // TODO: understand why the values come back without underscores and fix the root cause // @ts-ignore: TS2678 case 'onshape': - case 'on_shape': return 'OnShape' case 'trackpad_friendly': return 'Trackpad Friendly' @@ -52,7 +52,6 @@ export function mouseControlsToCameraSystem( // TODO: understand why the values come back without underscores and fix the root cause // @ts-ignore: TS2678 case 'autocad': - case 'auto_cad': return 'AutoCAD' default: return undefined diff --git a/src/lib/commandBarConfigs/authCommandConfig.ts b/src/lib/commandBarConfigs/authCommandConfig.ts index 1f77434ae..0417d973d 100644 --- a/src/lib/commandBarConfigs/authCommandConfig.ts +++ b/src/lib/commandBarConfigs/authCommandConfig.ts @@ -1,6 +1,6 @@ -import { Command } from 'lib/commandTypes' -import { authActor } from 'machines/appMachine' -import { ACTOR_IDS } from 'machines/machineConstants' +import type { Command } from '@src/lib/commandTypes' +import { authActor } from '@src/machines/appMachine' +import { ACTOR_IDS } from '@src/machines/machineConstants' export const authCommands: Command[] = [ { diff --git a/src/lib/commandBarConfigs/modelingCommandConfig.ts b/src/lib/commandBarConfigs/modelingCommandConfig.ts index 4e8cebf8d..00f115981 100644 --- a/src/lib/commandBarConfigs/modelingCommandConfig.ts +++ b/src/lib/commandBarConfigs/modelingCommandConfig.ts @@ -1,30 +1,34 @@ -import { Models } from '@kittycad/lib' -import { angleLengthInfo } from 'components/Toolbar/setAngleLength' -import { transformAstSketchLines } from 'lang/std/sketchcombos' -import { - isPathToNode, +import type { Models } from '@kittycad/lib' +import { DEV } from '@src/env' + +import { angleLengthInfo } from '@src/components/Toolbar/angleLengthInfo' +import { getNodeFromPath } from '@src/lang/queryAst' +import { getVariableDeclaration } from '@src/lang/queryAst/getVariableDeclaration' +import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils' +import { transformAstSketchLines } from '@src/lang/std/sketchcombos' +import type { PathToNode, SourceRange, VariableDeclarator, -} from 'lang/wasm' -import { StateMachineCommandSetConfig, KclCommandValue } from 'lib/commandTypes' -import { KCL_DEFAULT_LENGTH, KCL_DEFAULT_DEGREE } from 'lib/constants' -import { components } from 'lib/machine-api' -import { Selections } from 'lib/selections' -import { codeManager, kclManager } from 'lib/singletons' -import { err } from 'lib/trap' -import { modelingMachine, SketchTool } from 'machines/modelingMachine' +} from '@src/lang/wasm' +import { isPathToNode } from '@src/lang/wasm' import { loftValidator, revolveAxisValidator, shellValidator, sweepValidator, -} from './validators' -import { getVariableDeclaration } from 'lang/queryAst/getVariableDeclaration' -import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils' -import { getNodeFromPath } from 'lang/queryAst' -import { IS_NIGHTLY_OR_DEBUG } from 'routes/Settings' -import { DEV } from 'env' +} from '@src/lib/commandBarConfigs/validators' +import type { + KclCommandValue, + StateMachineCommandSetConfig, +} from '@src/lib/commandTypes' +import { KCL_DEFAULT_DEGREE, KCL_DEFAULT_LENGTH } from '@src/lib/constants' +import type { components } from '@src/lib/machine-api' +import type { Selections } from '@src/lib/selections' +import { codeManager, kclManager } from '@src/lib/singletons' +import { err } from '@src/lib/trap' +import type { SketchTool, modelingMachine } from '@src/machines/modelingMachine' +import { IS_NIGHTLY_OR_DEBUG } from '@src/routes/Settings' type OutputFormat = Models['OutputFormat3d_type'] type OutputTypeKey = OutputFormat['type'] diff --git a/src/lib/commandBarConfigs/namedViewsConfig.ts b/src/lib/commandBarConfigs/namedViewsConfig.ts index a22313366..de0822067 100644 --- a/src/lib/commandBarConfigs/namedViewsConfig.ts +++ b/src/lib/commandBarConfigs/namedViewsConfig.ts @@ -1,14 +1,18 @@ -import { NamedView } from '@rust/kcl-lib/bindings/NamedView' -import { Command, CommandArgumentOption } from '../commandTypes' -import toast from 'react-hot-toast' -import { engineCommandManager } from 'lib/singletons' -import { uuidv4 } from 'lib/utils' -import { settingsActor, getSettings } from 'machines/appMachine' -import { err, reportRejection } from 'lib/trap' -import { +import type { CameraViewState_type, + Point3d_type, + Point4d_type, WorldCoordinateSystem_type, } from '@kittycad/lib/dist/types/src/models' +import toast from 'react-hot-toast' + +import type { NamedView } from '@rust/kcl-lib/bindings/NamedView' + +import type { Command, CommandArgumentOption } from '@src/lib/commandTypes' +import { engineCommandManager } from '@src/lib/singletons' +import { err, reportRejection } from '@src/lib/trap' +import { uuidv4 } from '@src/lib/utils' +import { getSettings, settingsActor } from '@src/machines/appMachine' function isWorldCoordinateSystemType( x: string @@ -16,6 +20,31 @@ function isWorldCoordinateSystemType( return x === 'right_handed_up_z' || x === 'right_handed_up_y' } +type Tuple3 = [number, number, number] +type Tuple4 = [number, number, number, number] + +function point3DToNumberArray(value: Point3d_type): Tuple3 { + return [value.x, value.y, value.z] +} +function numberArrayToPoint3D(value: Tuple3): Point3d_type { + return { + x: value[0], + y: value[1], + z: value[2], + } +} +function point4DToNumberArray(value: Point4d_type): Tuple4 { + return [value.x, value.y, value.z, value.w] +} +function numberArrayToPoint4D(value: Tuple4): Point4d_type { + return { + x: value[0], + y: value[1], + z: value[2], + w: value[3], + } +} + function namedViewToCameraViewState( namedView: NamedView ): CameraViewState_type | Error { @@ -32,8 +61,8 @@ function namedViewToCameraViewState( ortho_scale_factor: namedView.ortho_scale_factor, world_coord_system: worldCoordinateSystem, is_ortho: namedView.is_ortho, - pivot_position: namedView.pivot_position, - pivot_rotation: namedView.pivot_rotation, + pivot_position: numberArrayToPoint3D(namedView.pivot_position), + pivot_rotation: numberArrayToPoint4D(namedView.pivot_rotation), } return cameraViewState @@ -43,29 +72,11 @@ function cameraViewStateToNamedView( name: string, cameraViewState: CameraViewState_type ): NamedView | Error { - let pivot_position: [number, number, number] | null = null - let pivot_rotation: [number, number, number, number] | null = null + let pivot_position: Tuple3 | null = null + let pivot_rotation: Tuple4 | null = null - if (cameraViewState.pivot_position.length === 3) { - pivot_position = [ - cameraViewState.pivot_position[0], - cameraViewState.pivot_position[1], - cameraViewState.pivot_position[2], - ] - } else { - return new Error(`invalid pivot position ${cameraViewState.pivot_position}`) - } - - if (cameraViewState.pivot_rotation.length === 4) { - pivot_rotation = [ - cameraViewState.pivot_rotation[0], - cameraViewState.pivot_rotation[1], - cameraViewState.pivot_rotation[2], - cameraViewState.pivot_rotation[3], - ] - } else { - return new Error(`invalid pivot rotation ${cameraViewState.pivot_rotation}`) - } + pivot_position = point3DToNumberArray(cameraViewState.pivot_position) + pivot_rotation = point4DToNumberArray(cameraViewState.pivot_rotation) // Create a new named view const requestedView: NamedView = { diff --git a/src/lib/commandBarConfigs/projectsCommandConfig.ts b/src/lib/commandBarConfigs/projectsCommandConfig.ts index 3b82ddc1f..bc6b4a7bb 100644 --- a/src/lib/commandBarConfigs/projectsCommandConfig.ts +++ b/src/lib/commandBarConfigs/projectsCommandConfig.ts @@ -1,7 +1,7 @@ -import { CommandBarOverwriteWarning } from 'components/CommandBarOverwriteWarning' -import { StateMachineCommandSetConfig } from 'lib/commandTypes' -import { isDesktop } from 'lib/isDesktop' -import { projectsMachine } from 'machines/projectsMachine' +import { CommandBarOverwriteWarning } from '@src/components/CommandBarOverwriteWarning' +import type { StateMachineCommandSetConfig } from '@src/lib/commandTypes' +import { isDesktop } from '@src/lib/isDesktop' +import type { projectsMachine } from '@src/machines/projectsMachine' export type ProjectsCommandSchema = { 'Read projects': Record diff --git a/src/lib/commandBarConfigs/routeCommandConfig.ts b/src/lib/commandBarConfigs/routeCommandConfig.ts index 4c058137d..0dbc38d72 100644 --- a/src/lib/commandBarConfigs/routeCommandConfig.ts +++ b/src/lib/commandBarConfigs/routeCommandConfig.ts @@ -1,6 +1,8 @@ -import { Command } from '../commandTypes' -import { PATHS } from 'lib/paths' -import { NavigateFunction, Location } from 'react-router-dom' +import type { Location, NavigateFunction } from 'react-router-dom' + +import type { Command } from '@src/lib/commandTypes' +import { PATHS } from '@src/lib/paths' + export function createRouteCommands( navigate: NavigateFunction, location: Location, diff --git a/src/lib/commandBarConfigs/settingsCommandConfig.ts b/src/lib/commandBarConfigs/settingsCommandConfig.ts index a79b4a29f..4b2adb99d 100644 --- a/src/lib/commandBarConfigs/settingsCommandConfig.ts +++ b/src/lib/commandBarConfigs/settingsCommandConfig.ts @@ -1,26 +1,27 @@ -import { +import decamelize from 'decamelize' +import type { ActorRefFrom, AnyStateMachine } from 'xstate' + +import type { Command, CommandArgument, CommandArgumentConfig, -} from '../commandTypes' -import { - SettingsPaths, - SettingsLevel, - SettingProps, - SetEventTypes, -} from 'lib/settings/settingsTypes' -import { settingsMachine } from 'machines/settingsMachine' -import { PathValue } from 'lib/types' -import { ActorRefFrom, AnyStateMachine } from 'xstate' -import { getPropertyByPath } from 'lib/objectPropertyByPath' -import { buildCommandArgument } from 'lib/createMachineCommand' -import decamelize from 'decamelize' -import { isDesktop } from 'lib/isDesktop' -import { - createSettings, +} from '@src/lib/commandTypes' +import { buildCommandArgument } from '@src/lib/createMachineCommand' +import { isDesktop } from '@src/lib/isDesktop' +import { getPropertyByPath } from '@src/lib/objectPropertyByPath' +import type { Setting, SettingsType, -} from 'lib/settings/initialSettings' + createSettings, +} from '@src/lib/settings/initialSettings' +import type { + SetEventTypes, + SettingProps, + SettingsLevel, + SettingsPaths, +} from '@src/lib/settings/settingsTypes' +import type { PathValue } from '@src/lib/types' +import type { settingsMachine } from '@src/machines/settingsMachine' // An array of the paths to all of the settings that have commandConfigs export const settingsWithCommandConfigs = (s: SettingsType) => diff --git a/src/lib/commandBarConfigs/validators.test.ts b/src/lib/commandBarConfigs/validators.test.ts index 4862ab5a6..977a3aca4 100644 --- a/src/lib/commandBarConfigs/validators.test.ts +++ b/src/lib/commandBarConfigs/validators.test.ts @@ -1,4 +1,4 @@ -import { parseEngineErrorMessage } from './validators' +import { parseEngineErrorMessage } from '@src/lib/commandBarConfigs/validators' describe('parseEngineErrorMessage', () => { it('takes an engine error string and parses its json message', () => { diff --git a/src/lib/commandBarConfigs/validators.ts b/src/lib/commandBarConfigs/validators.ts index b199ced47..87be89757 100644 --- a/src/lib/commandBarConfigs/validators.ts +++ b/src/lib/commandBarConfigs/validators.ts @@ -1,9 +1,10 @@ -import { Models } from '@kittycad/lib' -import { engineCommandManager, kclManager } from 'lib/singletons' -import { uuidv4 } from 'lib/utils' -import { CommandBarContext } from 'machines/commandBarMachine' -import { Selections } from 'lib/selections' -import { ApiError_type } from '@kittycad/lib/dist/types/src/models' +import type { Models } from '@kittycad/lib' +import type { ApiError_type } from '@kittycad/lib/dist/types/src/models' + +import type { Selections } from '@src/lib/selections' +import { engineCommandManager, kclManager } from '@src/lib/singletons' +import { uuidv4 } from '@src/lib/utils' +import type { CommandBarContext } from '@src/machines/commandBarMachine' export const disableDryRunWithRetry = async (numberOfRetries = 3) => { for (let tries = 0; tries < numberOfRetries; tries++) { diff --git a/src/lib/commandTypes.ts b/src/lib/commandTypes.ts index 026b00865..61dd6d691 100644 --- a/src/lib/commandTypes.ts +++ b/src/lib/commandTypes.ts @@ -1,14 +1,18 @@ -import { CustomIconName } from 'components/CustomIcon' -import { AllMachines } from 'hooks/useStateMachineCommands' -import { Actor, AnyStateMachine, ContextFrom, EventFrom } from 'xstate' -import { Expr, Name, VariableDeclaration } from 'lang/wasm' -import { commandBarMachine } from 'machines/commandBarMachine' -import { ReactNode } from 'react' -import { MachineManager } from 'components/MachineManagerProvider' -import { Node } from '@rust/kcl-lib/bindings/Node' -import { Artifact } from 'lang/std/artifactGraph' -import { CommandBarContext } from 'machines/commandBarMachine' -import { EntityType_type } from '@kittycad/lib/dist/types/src/models' +import type { EntityType_type } from '@kittycad/lib/dist/types/src/models' +import type { ReactNode } from 'react' +import type { Actor, AnyStateMachine, ContextFrom, EventFrom } from 'xstate' + +import type { Node } from '@rust/kcl-lib/bindings/Node' + +import type { CustomIconName } from '@src/components/CustomIcon' +import type { MachineManager } from '@src/components/MachineManagerProvider' +import type { AllMachines } from '@src/hooks/useStateMachineCommands' +import type { Artifact } from '@src/lang/std/artifactGraph' +import type { Expr, Name, VariableDeclaration } from '@src/lang/wasm' +import type { + CommandBarContext, + commandBarMachine, +} from '@src/machines/commandBarMachine' type Icon = CustomIconName const _PLATFORMS = ['both', 'web', 'desktop'] as const @@ -43,7 +47,7 @@ export type StateMachineCommandSetSchema = Partial<{ export type StateMachineCommandSet< T extends AllMachines, - Schema extends StateMachineCommandSetSchema + Schema extends StateMachineCommandSetSchema, > = Partial<{ [EventType in EventFrom['type']]: Command< T, @@ -60,7 +64,7 @@ export type StateMachineCommandSet< */ export type StateMachineCommandSetConfig< T extends AllMachines, - Schema extends StateMachineCommandSetSchema + Schema extends StateMachineCommandSetSchema, > = Partial<{ [EventType in EventFrom['type']]: | CommandConfig['type'], Schema[EventType]> @@ -70,7 +74,8 @@ export type StateMachineCommandSetConfig< export type Command< T extends AnyStateMachine = AnyStateMachine, CommandName extends EventFrom['type'] = EventFrom['type'], - CommandSchema extends StateMachineCommandSetSchema[CommandName] = StateMachineCommandSetSchema[CommandName] + CommandSchema extends + StateMachineCommandSetSchema[CommandName] = StateMachineCommandSetSchema[CommandName], > = { name: CommandName groupId: T['id'] @@ -96,7 +101,8 @@ export type Command< export type CommandConfig< T extends AnyStateMachine = AnyStateMachine, CommandName extends EventFrom['type'] = EventFrom['type'], - CommandSchema extends StateMachineCommandSetSchema[CommandName] = StateMachineCommandSetSchema[CommandName] + CommandSchema extends + StateMachineCommandSetSchema[CommandName] = StateMachineCommandSetSchema[CommandName], > = Omit< Command, 'name' | 'groupId' | 'onSubmit' | 'onCancel' | 'args' | 'needsReview' @@ -113,7 +119,7 @@ export type CommandConfig< export type CommandArgumentConfig< OutputType, - C = ContextFrom + C = ContextFrom, > = { displayName?: string description?: string @@ -239,7 +245,7 @@ export type CommandArgumentConfig< export type CommandArgument< OutputType, - T extends AnyStateMachine = AnyStateMachine + T extends AnyStateMachine = AnyStateMachine, > = { displayName?: string description?: string @@ -360,7 +366,7 @@ export type CommandArgument< export type CommandArgumentWithName< OutputType, - T extends AnyStateMachine = AnyStateMachine + T extends AnyStateMachine = AnyStateMachine, > = CommandArgument & { name: string } diff --git a/src/lib/commandUtils.test.ts b/src/lib/commandUtils.test.ts index 85d45bbad..abb7ef8c4 100644 --- a/src/lib/commandUtils.test.ts +++ b/src/lib/commandUtils.test.ts @@ -1,4 +1,5 @@ -import { CommandWithDisabledState, sortCommands } from './commandUtils' +import type { CommandWithDisabledState } from '@src/lib/commandUtils' +import { sortCommands } from '@src/lib/commandUtils' function commandWithDisabled( name: string, diff --git a/src/lib/commandUtils.ts b/src/lib/commandUtils.ts index ec7bdb59d..7c75f9d45 100644 --- a/src/lib/commandUtils.ts +++ b/src/lib/commandUtils.ts @@ -2,8 +2,7 @@ // That object also contains some metadata about what to do with the KCL expression, // such as whether we need to create a new variable for it. // This function extracts the value field from those arg payloads and returns - -import { Command } from './commandTypes' +import type { Command } from '@src/lib/commandTypes' // The arg object with all its field as natural values that the command to be executed will expect. export function getCommandArgumentKclValuesOnly(args: Record) { diff --git a/src/lib/constants.ts b/src/lib/constants.ts index 3731990b0..d6c3b1940 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -1,5 +1,6 @@ -import { Models } from '@kittycad/lib/dist/types/src' -import { UnitAngle, UnitLength } from '@rust/kcl-lib/bindings/ModelingCmd' +import type { Models } from '@kittycad/lib/dist/types/src' + +import type { UnitAngle, UnitLength } from '@rust/kcl-lib/bindings/ModelingCmd' export const APP_NAME = 'Modeling App' /** Search string in new project names to increment as an index */ diff --git a/src/lib/coredump.ts b/src/lib/coredump.ts index 326cd4871..384aa53b4 100644 --- a/src/lib/coredump.ts +++ b/src/lib/coredump.ts @@ -1,13 +1,18 @@ -import { CommandLog, EngineCommandManager } from 'lang/std/engineConnection' -import { WebrtcStats } from '@rust/kcl-lib/bindings/WebrtcStats' -import { OsInfo } from '@rust/kcl-lib/bindings/OsInfo' -import { isDesktop } from 'lib/isDesktop' -import { APP_VERSION } from 'routes/Settings' +import { VITE_KC_API_BASE_URL } from '@src/env' import { UAParser } from 'ua-parser-js' -import screenshot from 'lib/screenshot' -import { VITE_KC_API_BASE_URL } from 'env' -import CodeManager from 'lang/codeManager' -import RustContext from 'lib/rustContext' + +import type { OsInfo } from '@rust/kcl-lib/bindings/OsInfo' +import type { WebrtcStats } from '@rust/kcl-lib/bindings/WebrtcStats' + +import type CodeManager from '@src/lang/codeManager' +import type { + CommandLog, + EngineCommandManager, +} from '@src/lang/std/engineConnection' +import { isDesktop } from '@src/lib/isDesktop' +import type RustContext from '@src/lib/rustContext' +import screenshot from '@src/lib/screenshot' +import { APP_VERSION } from '@src/routes/Settings' /* eslint-disable suggest-no-throw/suggest-no-throw -- * All the throws in CoreDumpManager are intentional and should be caught and handled properly diff --git a/src/lib/createMachineCommand.ts b/src/lib/createMachineCommand.ts index 3e0595980..501ffd5ab 100644 --- a/src/lib/createMachineCommand.ts +++ b/src/lib/createMachineCommand.ts @@ -1,25 +1,26 @@ -import { +import { DEV } from '@src/env' +import type { + Actor, AnyStateMachine, ContextFrom, EventFrom, - Actor, StateFrom, } from 'xstate' -import { isDesktop } from './isDesktop' -import { + +import type { Command, CommandArgument, CommandArgumentConfig, CommandConfig, StateMachineCommandSetConfig, StateMachineCommandSetSchema, -} from './commandTypes' -import { DEV } from 'env' -import { IS_NIGHTLY_OR_DEBUG } from 'routes/Settings' +} from '@src/lib/commandTypes' +import { isDesktop } from '@src/lib/isDesktop' +import { IS_NIGHTLY_OR_DEBUG } from '@src/routes/Settings' interface CreateMachineCommandProps< T extends AnyStateMachine, - S extends StateMachineCommandSetSchema + S extends StateMachineCommandSetSchema, > { type: EventFrom['type'] groupId: T['id'] @@ -34,7 +35,7 @@ interface CreateMachineCommandProps< // from a more terse Command Bar Meta definition. export function createMachineCommand< T extends AnyStateMachine, - S extends StateMachineCommandSetSchema + S extends StateMachineCommandSetSchema, >({ groupId, type, @@ -132,7 +133,7 @@ export function createMachineCommand< function buildCommandArguments< T extends AnyStateMachine, S extends StateMachineCommandSetSchema, - CommandName extends EventFrom['type'] = EventFrom['type'] + CommandName extends EventFrom['type'] = EventFrom['type'], >( state: StateFrom, args: CommandConfig['args'], @@ -151,7 +152,7 @@ function buildCommandArguments< export function buildCommandArgument< T extends AnyStateMachine, - O extends StateMachineCommandSetSchema = StateMachineCommandSetSchema + O extends StateMachineCommandSetSchema = StateMachineCommandSetSchema, >( arg: CommandArgumentConfig, context: ContextFrom, diff --git a/src/lib/crossPlatformFetch.ts b/src/lib/crossPlatformFetch.ts index 7baaa63bc..28247442d 100644 --- a/src/lib/crossPlatformFetch.ts +++ b/src/lib/crossPlatformFetch.ts @@ -1,7 +1,8 @@ -import { DEV } from 'env' -import { isDesktop } from 'lib/isDesktop' +import { DEV } from '@src/env' import isomorphicFetch from 'isomorphic-fetch' +import { isDesktop } from '@src/lib/isDesktop' + // TODO I not sure this file should exist const headers = (token?: string): HeadersInit => ({ diff --git a/src/lib/desktop.test.ts b/src/lib/desktop.test.ts index 2868ceb10..bcb415f9a 100644 --- a/src/lib/desktop.test.ts +++ b/src/lib/desktop.test.ts @@ -1,7 +1,9 @@ -import { vi, describe, it, expect, beforeEach } from 'vitest' -import { listProjects } from './desktop' -import { DeepPartial } from './types' -import { Configuration } from '@rust/kcl-lib/bindings/Configuration' +import { beforeEach, describe, expect, it, vi } from 'vitest' + +import type { Configuration } from '@rust/kcl-lib/bindings/Configuration' + +import { listProjects } from '@src/lib/desktop' +import type { DeepPartial } from '@src/lib/types' // Mock the electron window global const mockElectron = { diff --git a/src/lib/desktop.ts b/src/lib/desktop.ts index 0b591d117..bb69f170d 100644 --- a/src/lib/desktop.ts +++ b/src/lib/desktop.ts @@ -1,13 +1,15 @@ -import { err } from 'lib/trap' -import { Models } from '@kittycad/lib' -import { Project, FileEntry } from 'lib/project' +import type { Models } from '@kittycad/lib' +import type { Configuration } from '@rust/kcl-lib/bindings/Configuration' +import type { ProjectConfiguration } from '@rust/kcl-lib/bindings/ProjectConfiguration' + +import { newKclFile } from '@src/lang/project' import { defaultAppSettings, initPromise, parseAppSettings, parseProjectSettings, -} from 'lang/wasm' +} from '@src/lang/wasm' import { DEFAULT_DEFAULT_LENGTH_UNIT, PROJECT_ENTRYPOINT, @@ -18,11 +20,10 @@ import { TELEMETRY_FILE_NAME, TELEMETRY_RAW_FILE_NAME, TOKEN_FILE_NAME, -} from './constants' -import { DeepPartial } from './types' -import { ProjectConfiguration } from '@rust/kcl-lib/bindings/ProjectConfiguration' -import { Configuration } from '@rust/kcl-lib/bindings/Configuration' -import { newKclFile } from 'lang/project' +} from '@src/lib/constants' +import type { FileEntry, Project } from '@src/lib/project' +import { err } from '@src/lib/trap' +import type { DeepPartial } from '@src/lib/types' export async function renameProjectDirectory( projectPath: string, @@ -67,9 +68,7 @@ export async function renameProjectDirectory( export async function ensureProjectDirectoryExists( config: DeepPartial ): Promise { - const projectDir = - config.settings?.app?.project_directory || - config.settings?.project?.directory + const projectDir = config.settings?.project?.directory if (!projectDir) { console.error('projectDir is falsey', config) return Promise.reject(new Error('projectDir is falsey')) @@ -588,8 +587,7 @@ export const readAppSettingsFile = async () => { } const hasProjectDirectorySetting = - parsedAppConfig.settings?.project?.directory || - parsedAppConfig.settings?.app?.project_directory + parsedAppConfig.settings?.project?.directory if (hasProjectDirectorySetting) { return parsedAppConfig diff --git a/src/lib/desktopFS.test.ts b/src/lib/desktopFS.test.ts index a590c64f5..d8032e81f 100644 --- a/src/lib/desktopFS.test.ts +++ b/src/lib/desktopFS.test.ts @@ -1,5 +1,5 @@ -import { getUniqueProjectName } from './desktopFS' -import { FileEntry } from './project' +import { getUniqueProjectName } from '@src/lib/desktopFS' +import type { FileEntry } from '@src/lib/project' /** Create a dummy project */ function project(name: string, children?: FileEntry[]): FileEntry { diff --git a/src/lib/desktopFS.ts b/src/lib/desktopFS.ts index e7b9bf1df..1a4a13b54 100644 --- a/src/lib/desktopFS.ts +++ b/src/lib/desktopFS.ts @@ -1,18 +1,18 @@ -import { isDesktop } from './isDesktop' -import type { FileEntry } from 'lib/project' import { FILE_EXT, INDEX_IDENTIFIER, MAX_PADDING, ONBOARDING_PROJECT_NAME, -} from 'lib/constants' -import { bracket } from './exampleKcl' -import { PATHS } from './paths' +} from '@src/lib/constants' import { createNewProjectDirectory, listProjects, readAppSettingsFile, -} from './desktop' +} from '@src/lib/desktop' +import { bracket } from '@src/lib/exampleKcl' +import { isDesktop } from '@src/lib/isDesktop' +import { PATHS } from '@src/lib/paths' +import type { FileEntry } from '@src/lib/project' export const isHidden = (fileOrDir: FileEntry) => !!fileOrDir.name?.startsWith('.') diff --git a/src/lib/exceptions.ts b/src/lib/exceptions.ts index d72075221..e80f7c0ce 100644 --- a/src/lib/exceptions.ts +++ b/src/lib/exceptions.ts @@ -1,7 +1,8 @@ -import { kclManager } from 'lib/singletons' -import { reloadModule, getModule } from 'lib/wasm_lib_wrapper' import toast from 'react-hot-toast' -import { reportRejection } from './trap' + +import { kclManager } from '@src/lib/singletons' +import { reportRejection } from '@src/lib/trap' +import { getModule, reloadModule } from '@src/lib/wasm_lib_wrapper' let initialized = false diff --git a/src/lib/exportMake.ts b/src/lib/exportMake.ts index 14ba4fe46..46b366c10 100644 --- a/src/lib/exportMake.ts +++ b/src/lib/exportMake.ts @@ -1,8 +1,9 @@ -import { MachineManager } from 'components/MachineManagerProvider' import toast from 'react-hot-toast' -import { components } from './machine-api' -import ModelingAppFile from './modelingAppFile' -import { MAKE_TOAST_MESSAGES } from './constants' + +import type { MachineManager } from '@src/components/MachineManagerProvider' +import { MAKE_TOAST_MESSAGES } from '@src/lib/constants' +import type { components } from '@src/lib/machine-api' +import type ModelingAppFile from '@src/lib/modelingAppFile' // Make files locally from an export call. export async function exportMake({ diff --git a/src/lib/exportSave.ts b/src/lib/exportSave.ts index 43683e6da..a239395f5 100644 --- a/src/lib/exportSave.ts +++ b/src/lib/exportSave.ts @@ -1,10 +1,10 @@ -import { isDesktop } from './isDesktop' -import { browserSaveFile } from './browserSaveFile' - import JSZip from 'jszip' -import ModelingAppFile from './modelingAppFile' import toast from 'react-hot-toast' -import { EXPORT_TOAST_MESSAGES } from './constants' + +import { browserSaveFile } from '@src/lib/browserSaveFile' +import { EXPORT_TOAST_MESSAGES } from '@src/lib/constants' +import { isDesktop } from '@src/lib/isDesktop' +import type ModelingAppFile from '@src/lib/modelingAppFile' const save_ = async (file: ModelingAppFile, toastId: string) => { try { diff --git a/src/lib/getCurrentProjectFile.test.ts b/src/lib/getCurrentProjectFile.test.ts index 78b0cea52..10221bf41 100644 --- a/src/lib/getCurrentProjectFile.test.ts +++ b/src/lib/getCurrentProjectFile.test.ts @@ -1,8 +1,9 @@ import { promises as fs } from 'fs' -import path from 'path' import os from 'os' +import path from 'path' import { v4 as uuidv4 } from 'uuid' -import getCurrentProjectFile from './getCurrentProjectFile' + +import getCurrentProjectFile from '@src/lib/getCurrentProjectFile' describe('getCurrentProjectFile', () => { test('with explicit open file with space (URL encoded)', async () => { diff --git a/src/lib/getCurrentProjectFile.ts b/src/lib/getCurrentProjectFile.ts index d3638cb26..10235d802 100644 --- a/src/lib/getCurrentProjectFile.ts +++ b/src/lib/getCurrentProjectFile.ts @@ -1,8 +1,9 @@ -import * as path from 'path' +import type { Models } from '@kittycad/lib/dist/types/src' +import type { Stats } from 'fs' import * as fs from 'fs/promises' -import { Stats } from 'fs' -import { Models } from '@kittycad/lib/dist/types/src' -import { PROJECT_ENTRYPOINT } from './constants' +import * as path from 'path' + +import { PROJECT_ENTRYPOINT } from '@src/lib/constants' // Create a const object with the values const FILE_IMPORT_FORMATS = { diff --git a/src/lib/getKclSamplesManifest.ts b/src/lib/getKclSamplesManifest.ts index b03e922a2..3c7347ddf 100644 --- a/src/lib/getKclSamplesManifest.ts +++ b/src/lib/getKclSamplesManifest.ts @@ -1,5 +1,5 @@ -import { KCL_SAMPLES_MANIFEST_URL } from './constants' -import { isDesktop } from './isDesktop' +import { KCL_SAMPLES_MANIFEST_URL } from '@src/lib/constants' +import { isDesktop } from '@src/lib/isDesktop' export type KclSamplesManifestItem = { file: string diff --git a/src/lib/hotkeyWrapper.test.ts b/src/lib/hotkeyWrapper.test.ts index df15f8af5..a3e082df3 100644 --- a/src/lib/hotkeyWrapper.test.ts +++ b/src/lib/hotkeyWrapper.test.ts @@ -1,4 +1,4 @@ -import { hotkeyDisplay } from './hotkeyWrapper' +import { hotkeyDisplay } from '@src/lib/hotkeyWrapper' describe('hotkeyDisplay', () => { it('displays mod', async () => { diff --git a/src/lib/hotkeyWrapper.ts b/src/lib/hotkeyWrapper.ts index ce5bf747e..a270246d2 100644 --- a/src/lib/hotkeyWrapper.ts +++ b/src/lib/hotkeyWrapper.ts @@ -1,7 +1,9 @@ -import { Options, useHotkeys } from 'react-hotkeys-hook' import { useEffect } from 'react' -import { codeManager } from './singletons' -import { Platform } from './utils' +import type { Options } from 'react-hotkeys-hook' +import { useHotkeys } from 'react-hotkeys-hook' + +import { codeManager } from '@src/lib/singletons' +import type { Platform } from '@src/lib/utils' // Hotkey wrapper wraps hotkeys for the app (outside of the editor) // with hotkeys inside the editor. diff --git a/src/lib/kclCommands.ts b/src/lib/kclCommands.ts index 923c24202..41afa5524 100644 --- a/src/lib/kclCommands.ts +++ b/src/lib/kclCommands.ts @@ -1,23 +1,24 @@ -import { CommandBarOverwriteWarning } from 'components/CommandBarOverwriteWarning' -import { Command, CommandArgumentOption } from './commandTypes' -import { codeManager, kclManager } from './singletons' -import { isDesktop } from './isDesktop' +import type { UnitLength_type } from '@kittycad/lib/dist/types/src/models' +import toast from 'react-hot-toast' + +import { CommandBarOverwriteWarning } from '@src/components/CommandBarOverwriteWarning' +import { + changeKclSettings, + unitAngleToUnitAng, + unitLengthToUnitLen, +} from '@src/lang/wasm' +import type { Command, CommandArgumentOption } from '@src/lib/commandTypes' import { DEFAULT_DEFAULT_ANGLE_UNIT, DEFAULT_DEFAULT_LENGTH_UNIT, FILE_EXT, -} from './constants' -import { UnitLength_type } from '@kittycad/lib/dist/types/src/models' -import { err, reportRejection } from './trap' -import { IndexLoaderData } from './types' -import { copyFileShareLink } from './links' -import { baseUnitsUnion } from './settings/settingsTypes' -import toast from 'react-hot-toast' -import { - changeKclSettings, - unitLengthToUnitLen, - unitAngleToUnitAng, -} from 'lang/wasm' +} from '@src/lib/constants' +import { isDesktop } from '@src/lib/isDesktop' +import { copyFileShareLink } from '@src/lib/links' +import { baseUnitsUnion } from '@src/lib/settings/settingsTypes' +import { codeManager, kclManager } from '@src/lib/singletons' +import { err, reportRejection } from '@src/lib/trap' +import type { IndexLoaderData } from '@src/lib/types' interface OnSubmitProps { sampleName: string diff --git a/src/lib/kclHelpers.test.ts b/src/lib/kclHelpers.test.ts index 18179ed36..c3b79a50e 100644 --- a/src/lib/kclHelpers.test.ts +++ b/src/lib/kclHelpers.test.ts @@ -1,5 +1,5 @@ -import { ParseResult } from 'lang/wasm' -import { getCalculatedKclExpressionValue } from './kclHelpers' +import type { ParseResult } from '@src/lang/wasm' +import { getCalculatedKclExpressionValue } from '@src/lib/kclHelpers' describe('KCL expression calculations', () => { it('calculates a simple expression', async () => { diff --git a/src/lib/kclHelpers.ts b/src/lib/kclHelpers.ts index 34a65bbf6..2427a183a 100644 --- a/src/lib/kclHelpers.ts +++ b/src/lib/kclHelpers.ts @@ -1,8 +1,8 @@ -import { err } from './trap' -import { parse, resultIsOk } from 'lang/wasm' -import { executeAstMock } from 'lang/langHelpers' -import { KclExpression } from './commandTypes' -import { rustContext } from './singletons' +import { executeAstMock } from '@src/lang/langHelpers' +import { parse, resultIsOk } from '@src/lang/wasm' +import type { KclExpression } from '@src/lib/commandTypes' +import { rustContext } from '@src/lib/singletons' +import { err } from '@src/lib/trap' const DUMMY_VARIABLE_NAME = '__result__' diff --git a/src/lib/links.test.ts b/src/lib/links.test.ts index a65dec09e..32d2c6dff 100644 --- a/src/lib/links.test.ts +++ b/src/lib/links.test.ts @@ -1,5 +1,6 @@ -import { VITE_KC_SITE_APP_URL } from 'env' -import { createCreateFileUrl } from './links' +import { VITE_KC_SITE_APP_URL } from '@src/env' + +import { createCreateFileUrl } from '@src/lib/links' describe(`link creation tests`, () => { test(`createCreateFileUrl happy path`, async () => { diff --git a/src/lib/links.ts b/src/lib/links.ts index 2b31f46eb..f03613273 100644 --- a/src/lib/links.ts +++ b/src/lib/links.ts @@ -1,8 +1,13 @@ -import { ASK_TO_OPEN_QUERY_PARAM, CREATE_FILE_URL_PARAM } from './constants' -import { stringToBase64 } from './base64' -import { VITE_KC_API_BASE_URL, VITE_KC_SITE_APP_URL } from 'env' +import { VITE_KC_API_BASE_URL, VITE_KC_SITE_APP_URL } from '@src/env' import toast from 'react-hot-toast' -import { err } from './trap' + +import { stringToBase64 } from '@src/lib/base64' +import { + ASK_TO_OPEN_QUERY_PARAM, + CREATE_FILE_URL_PARAM, +} from '@src/lib/constants' +import { err } from '@src/lib/trap' + export interface FileLinkParams { code: string name: string diff --git a/src/lib/markdown.ts b/src/lib/markdown.ts index d4afcf746..b31069138 100644 --- a/src/lib/markdown.ts +++ b/src/lib/markdown.ts @@ -1,5 +1,7 @@ -import { MarkedOptions, Renderer, unescape } from '@ts-stack/markdown' -import { openExternalBrowserIfDesktop } from './openWindow' +import type { MarkedOptions } from '@ts-stack/markdown' +import { Renderer, unescape } from '@ts-stack/markdown' + +import { openExternalBrowserIfDesktop } from '@src/lib/openWindow' /** * Main goal of this custom renderer is to prevent links from changing the current location diff --git a/src/lib/objectPropertyByPath.ts b/src/lib/objectPropertyByPath.ts index 3a9e1ad61..6e6ea98d5 100644 --- a/src/lib/objectPropertyByPath.ts +++ b/src/lib/objectPropertyByPath.ts @@ -1,8 +1,8 @@ -import { PathValue, Paths } from './types' +import type { PathValue, Paths } from '@src/lib/types' export function setPropertyByPath< T extends { [key: string]: any }, - P extends Paths + P extends Paths, >(obj: T, path: P, value: PathValue) { if (typeof path === 'string') { const pList = path.split('.') @@ -25,7 +25,7 @@ export function setPropertyByPath< export function getPropertyByPath< T extends { [key: string]: any }, - P extends Paths + P extends Paths, >(obj: T, path: P): unknown { if (typeof path === 'string') { return path.split('.').reduce((accumulator, currentValue) => { diff --git a/src/lib/openWindow.ts b/src/lib/openWindow.ts index cc7d8b023..e48a2e1ea 100644 --- a/src/lib/openWindow.ts +++ b/src/lib/openWindow.ts @@ -1,6 +1,7 @@ -import { MouseEventHandler } from 'react' -import { isDesktop } from 'lib/isDesktop' -import { reportRejection } from './trap' +import type { MouseEventHandler } from 'react' + +import { isDesktop } from '@src/lib/isDesktop' +import { reportRejection } from '@src/lib/trap' export const openExternalBrowserIfDesktop = (to?: string) => function (e) { diff --git a/src/lib/operations.test.ts b/src/lib/operations.test.ts index 98f409bda..2ce781b86 100644 --- a/src/lib/operations.test.ts +++ b/src/lib/operations.test.ts @@ -1,6 +1,7 @@ -import { defaultSourceRange } from 'lang/wasm' -import { filterOperations } from './operations' -import { Operation } from '@rust/kcl-lib/bindings/Operation' +import type { Operation } from '@rust/kcl-lib/bindings/Operation' + +import { defaultSourceRange } from '@src/lang/wasm' +import { filterOperations } from '@src/lib/operations' function stdlib(name: string): Operation { return { diff --git a/src/lib/operations.ts b/src/lib/operations.ts index 7109488d5..239fd9280 100644 --- a/src/lib/operations.ts +++ b/src/lib/operations.ts @@ -1,26 +1,27 @@ -import { CustomIconName } from 'components/CustomIcon' +import type { Operation } from '@rust/kcl-lib/bindings/Operation' + +import type { CustomIconName } from '@src/components/CustomIcon' +import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils' +import type { Artifact } from '@src/lang/std/artifactGraph' import { - Artifact, getArtifactOfTypes, getCapCodeRef, getEdgeCutConsumedCodeRef, getSweepEdgeCodeRef, getWallCodeRef, -} from 'lang/std/artifactGraph' -import { Operation } from '@rust/kcl-lib/bindings/Operation' -import { codeManager, kclManager, rustContext } from './singletons' -import { err } from './trap' -import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils' -import { sourceRangeFromRust } from 'lang/wasm' -import { CommandBarMachineEvent } from 'machines/commandBarMachine' -import { stringToKclExpression } from './kclHelpers' -import { +} from '@src/lang/std/artifactGraph' +import { sourceRangeFromRust } from '@src/lang/wasm' +import type { HelixModes, ModelingCommandSchema, -} from './commandBarConfigs/modelingCommandConfig' -import { isDefaultPlaneStr } from './planes' -import { Selection, Selections } from './selections' -import { KclExpression } from './commandTypes' +} from '@src/lib/commandBarConfigs/modelingCommandConfig' +import type { KclExpression } from '@src/lib/commandTypes' +import { stringToKclExpression } from '@src/lib/kclHelpers' +import { isDefaultPlaneStr } from '@src/lib/planes' +import type { Selection, Selections } from '@src/lib/selections' +import { codeManager, kclManager, rustContext } from '@src/lib/singletons' +import { err } from '@src/lib/trap' +import type { CommandBarMachineEvent } from '@src/machines/commandBarMachine' type ExecuteCommandEvent = CommandBarMachineEvent & { type: 'Find and select command' diff --git a/src/lib/paths.test.ts b/src/lib/paths.test.ts index a6d8cd7ea..e61e367a9 100644 --- a/src/lib/paths.test.ts +++ b/src/lib/paths.test.ts @@ -1,6 +1,7 @@ -import { parseProjectRoute } from './paths' import * as path from 'path' +import { parseProjectRoute } from '@src/lib/paths' + describe('testing parseProjectRoute', () => { it('should parse a project as a subpath of project dir', async () => { let config = { diff --git a/src/lib/paths.ts b/src/lib/paths.ts index 7320918f1..4ad514f19 100644 --- a/src/lib/paths.ts +++ b/src/lib/paths.ts @@ -1,13 +1,20 @@ -import { onboardingPaths } from 'routes/Onboarding/paths' -import { BROWSER_FILE_NAME, BROWSER_PROJECT_NAME, FILE_EXT } from './constants' -import { isDesktop } from './isDesktop' -import { readAppSettingsFile } from './desktop' -import { readLocalStorageAppSettingsFile } from './settings/settingsUtils' -import { err } from 'lib/trap' -import { IS_PLAYWRIGHT_KEY } from '../../e2e/playwright/storageStates' -import { DeepPartial } from './types' -import { Configuration } from '@rust/kcl-lib/bindings/Configuration' -import { PlatformPath } from 'path' +import type { PlatformPath } from 'path' + +import type { Configuration } from '@rust/kcl-lib/bindings/Configuration' + +import { IS_PLAYWRIGHT_KEY } from '@e2e/playwright/storageStates' + +import { + BROWSER_FILE_NAME, + BROWSER_PROJECT_NAME, + FILE_EXT, +} from '@src/lib/constants' +import { readAppSettingsFile } from '@src/lib/desktop' +import { isDesktop } from '@src/lib/isDesktop' +import { readLocalStorageAppSettingsFile } from '@src/lib/settings/settingsUtils' +import { err } from '@src/lib/trap' +import type { DeepPartial } from '@src/lib/types' +import { onboardingPaths } from '@src/routes/Onboarding/paths' const prependRoutes = (routesObject: Record) => (prepend: string) => { diff --git a/src/lib/performance.ts b/src/lib/performance.ts index c9b359bf0..b40185d78 100644 --- a/src/lib/performance.ts +++ b/src/lib/performance.ts @@ -1,4 +1,4 @@ -import { isDesktop } from 'lib/isDesktop' +import { isDesktop } from '@src/lib/isDesktop' function isWeb(): boolean { // Identify browser environment when following property is not present diff --git a/src/lib/planes.ts b/src/lib/planes.ts index 162c06721..bef4c487b 100644 --- a/src/lib/planes.ts +++ b/src/lib/planes.ts @@ -1,4 +1,4 @@ -import { DefaultPlanes } from '@rust/kcl-lib/bindings/DefaultPlanes' +import type { DefaultPlanes } from '@rust/kcl-lib/bindings/DefaultPlanes' // KCL string representation of default planes export type DefaultPlaneStr = 'XY' | 'XZ' | 'YZ' | '-XY' | '-XZ' | '-YZ' diff --git a/src/lib/promptToEdit.ts b/src/lib/promptToEdit.ts index 1578a428e..c8289f2df 100644 --- a/src/lib/promptToEdit.ts +++ b/src/lib/promptToEdit.ts @@ -1,17 +1,20 @@ -import { Models } from '@kittycad/lib' -import { VITE_KC_API_BASE_URL } from 'env' -import crossPlatformFetch from './crossPlatformFetch' -import { err, reportRejection } from './trap' -import { Selections } from './selections' -import { getArtifactOfTypes } from 'lang/std/artifactGraph' -import { ArtifactGraph, SourceRange, topLevelRange } from 'lang/wasm' -import toast from 'react-hot-toast' -import { codeManager, editorManager, kclManager } from './singletons' -import { ToastPromptToEditCadSuccess } from 'components/ToastTextToCad' -import { uuidv4 } from './utils' +import type { SelectionRange } from '@codemirror/state' +import { EditorSelection, Transaction } from '@codemirror/state' +import type { Models } from '@kittycad/lib' +import { VITE_KC_API_BASE_URL } from '@src/env' import { diffLines } from 'diff' -import { Transaction, EditorSelection, SelectionRange } from '@codemirror/state' -import { modelingMachineEvent } from 'editor/manager' +import toast from 'react-hot-toast' + +import { ToastPromptToEditCadSuccess } from '@src/components/ToastTextToCad' +import { modelingMachineEvent } from '@src/editor/manager' +import { getArtifactOfTypes } from '@src/lang/std/artifactGraph' +import { topLevelRange } from '@src/lang/util' +import type { ArtifactGraph, SourceRange } from '@src/lang/wasm' +import crossPlatformFetch from '@src/lib/crossPlatformFetch' +import type { Selections } from '@src/lib/selections' +import { codeManager, editorManager, kclManager } from '@src/lib/singletons' +import { err, reportRejection } from '@src/lib/trap' +import { uuidv4 } from '@src/lib/utils' function sourceIndexToLineColumn( code: string, diff --git a/src/lib/rectangleTool.test.ts b/src/lib/rectangleTool.test.ts index 33010c130..6b60341a4 100644 --- a/src/lib/rectangleTool.test.ts +++ b/src/lib/rectangleTool.test.ts @@ -1,15 +1,12 @@ import { expect } from 'vitest' -import { - recast, - assertParse, - topLevelRange, - VariableDeclaration, - initPromise, -} from 'lang/wasm' -import { updateCenterRectangleSketch } from './rectangleTool' -import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils' -import { getNodeFromPath } from 'lang/queryAst' -import { trap } from './trap' + +import { getNodeFromPath } from '@src/lang/queryAst' +import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils' +import { topLevelRange } from '@src/lang/util' +import type { VariableDeclaration } from '@src/lang/wasm' +import { assertParse, initPromise, recast } from '@src/lang/wasm' +import { updateCenterRectangleSketch } from '@src/lib/rectangleTool' +import { trap } from '@src/lib/trap' beforeAll(async () => { await initPromise diff --git a/src/lib/rectangleTool.ts b/src/lib/rectangleTool.ts index bb8509e94..5fb44d8b3 100644 --- a/src/lib/rectangleTool.ts +++ b/src/lib/rectangleTool.ts @@ -1,3 +1,4 @@ +import { ARG_END_ABSOLUTE } from '@src/lang/constants' import { createArrayExpression, createBinaryExpression, @@ -9,17 +10,20 @@ import { createPipeSubstitution, createTagDeclarator, createUnaryExpression, -} from 'lang/modifyAst' -import { ArrayExpression, CallExpression, PipeExpression } from 'lang/wasm' -import { roundOff } from 'lib/utils' +} from '@src/lang/create' import { - isCallExpression, isArrayExpression, - isLiteral, isBinaryExpression, + isCallExpression, + isLiteral, isLiteralValueNumber, -} from 'lang/util' -import { ARG_END_ABSOLUTE } from 'lang/std/sketch' +} from '@src/lang/util' +import type { + ArrayExpression, + CallExpression, + PipeExpression, +} from '@src/lang/wasm' +import { roundOff } from '@src/lib/utils' /** * It does not create the startSketchOn and it does not create the startProfileAt. diff --git a/src/lib/routeLoaders.ts b/src/lib/routeLoaders.ts index 7da9a221a..8d547e366 100644 --- a/src/lib/routeLoaders.ts +++ b/src/lib/routeLoaders.ts @@ -1,21 +1,26 @@ -import { LoaderFunction, redirect } from 'react-router-dom' -import { FileLoaderData, HomeLoaderData, IndexLoaderData } from './types' -import { getProjectMetaByRouteId, PATHS } from './paths' -import { isDesktop } from './isDesktop' -import { BROWSER_PATH } from 'lib/paths' +import type { LoaderFunction } from 'react-router-dom' +import { redirect } from 'react-router-dom' +import { waitFor } from 'xstate' + +import { fileSystemManager } from '@src/lang/std/fileSystemManager' +import { normalizeLineEndings } from '@src/lib/codeEditor' import { BROWSER_FILE_NAME, BROWSER_PROJECT_NAME, FILE_EXT, PROJECT_ENTRYPOINT, -} from 'lib/constants' -import { loadAndValidateSettings } from './settings/settingsUtils' -import { codeManager } from 'lib/singletons' -import { fileSystemManager } from 'lang/std/fileSystemManager' -import { getProjectInfo } from './desktop' -import { normalizeLineEndings } from 'lib/codeEditor' -import { settingsActor } from 'machines/appMachine' -import { waitFor } from 'xstate' +} from '@src/lib/constants' +import { getProjectInfo } from '@src/lib/desktop' +import { isDesktop } from '@src/lib/isDesktop' +import { BROWSER_PATH, PATHS, getProjectMetaByRouteId } from '@src/lib/paths' +import { loadAndValidateSettings } from '@src/lib/settings/settingsUtils' +import { codeManager } from '@src/lib/singletons' +import type { + FileLoaderData, + HomeLoaderData, + IndexLoaderData, +} from '@src/lib/types' +import { settingsActor } from '@src/machines/appMachine' export const telemetryLoader: LoaderFunction = async ({ params, diff --git a/src/lib/rustContext.ts b/src/lib/rustContext.ts index 4f29bc3cf..2dc549108 100644 --- a/src/lib/rustContext.ts +++ b/src/lib/rustContext.ts @@ -1,25 +1,29 @@ +import toast from 'react-hot-toast' + +import type { Configuration } from '@rust/kcl-lib/bindings/Configuration' +import type { DefaultPlanes } from '@rust/kcl-lib/bindings/DefaultPlanes' +import type { KclError as RustKclError } from '@rust/kcl-lib/bindings/KclError' +import type { OutputFormat3d } from '@rust/kcl-lib/bindings/ModelingCmd' +import type { Node } from '@rust/kcl-lib/bindings/Node' +import type { Program } from '@rust/kcl-lib/bindings/Program' +import type { Context } from '@rust/kcl-wasm-lib/pkg/kcl_wasm_lib' + +import type { EngineCommandManager } from '@src/lang/std/engineConnection' +import { fileSystemManager } from '@src/lang/std/fileSystemManager' +import type { ExecState } from '@src/lang/wasm' import { errFromErrWithOutputs, - ExecState, execStateFromRust, initPromise, mockExecStateFromRust, -} from 'lang/wasm' -import { getModule, ModuleType } from 'lib/wasm_lib_wrapper' -import { fileSystemManager } from 'lang/std/fileSystemManager' -import type { Configuration } from '@rust/kcl-lib/bindings/Configuration' -import { DeepPartial } from 'lib/types' -import { Node } from '@rust/kcl-lib/bindings/Node' -import type { Program } from '@rust/kcl-lib/bindings/Program' -import { Context } from '@rust/kcl-wasm-lib/pkg/kcl_wasm_lib' -import { DefaultPlanes } from '@rust/kcl-lib/bindings/DefaultPlanes' -import { DefaultPlaneStr, defaultPlaneStrToKey } from 'lib/planes' -import { err } from 'lib/trap' -import { EngineCommandManager } from 'lang/std/engineConnection' -import { OutputFormat3d } from '@rust/kcl-lib/bindings/ModelingCmd' -import ModelingAppFile from './modelingAppFile' -import toast from 'react-hot-toast' -import { KclError as RustKclError } from '@rust/kcl-lib/bindings/KclError' +} from '@src/lang/wasm' +import type ModelingAppFile from '@src/lib/modelingAppFile' +import type { DefaultPlaneStr } from '@src/lib/planes' +import { defaultPlaneStrToKey } from '@src/lib/planes' +import { err } from '@src/lib/trap' +import type { DeepPartial } from '@src/lib/types' +import type { ModuleType } from '@src/lib/wasm_lib_wrapper' +import { getModule } from '@src/lib/wasm_lib_wrapper' export default class RustContext { private wasmInitFailed: boolean = true diff --git a/src/lib/selections.test.ts b/src/lib/selections.test.ts index 545dfd2d5..955737411 100644 --- a/src/lib/selections.test.ts +++ b/src/lib/selections.test.ts @@ -1,13 +1,16 @@ import { expect } from 'vitest' -import { assertParse, initPromise, ArtifactGraph, SourceRange } from 'lang/wasm' -import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils' + +import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils' +import type { Artifact } from '@src/lang/std/artifactGraph' +import type { ArtifactGraph, SourceRange } from '@src/lang/wasm' +import { assertParse, initPromise } from '@src/lang/wasm' +import type { ArtifactIndex } from '@src/lib/artifactIndex' +import { buildArtifactIndex } from '@src/lib/artifactIndex' +import type { Selection } from '@src/lib/selections' import { codeToIdSelections, - Selection, findLastRangeStartingBefore, -} from './selections' -import { buildArtifactIndex, ArtifactIndex } from './artifactIndex' -import { Artifact } from 'lang/std/artifactGraph' +} from '@src/lib/selections' beforeAll(async () => { await initPromise diff --git a/src/lib/selections.ts b/src/lib/selections.ts index 0fb624f17..ff548d54c 100644 --- a/src/lib/selections.ts +++ b/src/lib/selections.ts @@ -1,46 +1,49 @@ -import { Models } from '@kittycad/lib' +import type { SelectionRange } from '@codemirror/state' +import { EditorSelection } from '@codemirror/state' +import type { Models } from '@kittycad/lib' +import type { Object3D, Object3DEventMap } from 'three' +import { Mesh } from 'three' + +import type { Node } from '@rust/kcl-lib/bindings/Node' + +import { + SEGMENT_BODIES_PLUS_PROFILE_START, + getParentGroup, +} from '@src/clientSideScene/sceneConstants' +import { AXIS_GROUP, X_AXIS } from '@src/clientSideScene/sceneInfra' +import { getNodeFromPath, isSingleCursorInPipe } from '@src/lang/queryAst' +import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils' +import type { Artifact, ArtifactId, CodeRef } from '@src/lang/std/artifactGraph' +import { getCodeRefsByArtifactId } from '@src/lang/std/artifactGraph' +import type { PathToNodeMap } from '@src/lang/std/sketchcombos' +import { isCursorInSketchCommandRange, topLevelRange } from '@src/lang/util' +import type { + ArtifactGraph, + CallExpression, + CallExpressionKw, + Expr, + Program, + SourceRange, +} from '@src/lang/wasm' +import { defaultSourceRange } from '@src/lang/wasm' +import type { ArtifactEntry, ArtifactIndex } from '@src/lib/artifactIndex' +import type { CommandArgument } from '@src/lib/commandTypes' +import type { DefaultPlaneStr } from '@src/lib/planes' import { codeManager, engineCommandManager, kclManager, + rustContext, sceneEntitiesManager, -} from 'lib/singletons' +} from '@src/lib/singletons' +import { err } from '@src/lib/trap' import { - CallExpression, - SourceRange, - Expr, - defaultSourceRange, - topLevelRange, - ArtifactGraph, - CallExpressionKw, -} from 'lang/wasm' -import { ModelingMachineEvent } from 'machines/modelingMachine' -import { isNonNullable, uuidv4 } from 'lib/utils' -import { EditorSelection, SelectionRange } from '@codemirror/state' -import { getNormalisedCoordinates, isOverlap } from 'lib/utils' -import { isCursorInSketchCommandRange } from 'lang/util' -import { Program } from 'lang/wasm' -import { getNodeFromPath, isSingleCursorInPipe } from 'lang/queryAst' -import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils' -import { CommandArgument } from './commandTypes' -import { - getParentGroup, - SEGMENT_BODIES_PLUS_PROFILE_START, -} from 'clientSideScene/sceneEntities' -import { Mesh, Object3D, Object3DEventMap } from 'three' -import { AXIS_GROUP, X_AXIS } from 'clientSideScene/sceneInfra' -import { PathToNodeMap } from 'lang/std/sketchcombos' -import { err } from 'lib/trap' -import { - Artifact, - CodeRef, - getCodeRefsByArtifactId, - ArtifactId, -} from 'lang/std/artifactGraph' -import { Node } from '@rust/kcl-lib/bindings/Node' -import { DefaultPlaneStr } from './planes' -import { ArtifactEntry, ArtifactIndex } from './artifactIndex' -import { rustContext } from './singletons' + getNormalisedCoordinates, + isNonNullable, + isOverlap, + uuidv4, +} from '@src/lib/utils' +import type { ModelingMachineEvent } from '@src/machines/modelingMachine' export const X_AXIS_UUID = 'ad792545-7fd3-482a-a602-a93924e3055b' export const Y_AXIS_UUID = '680fd157-266f-4b8a-984f-cdf46b8bdf01' diff --git a/src/lib/settings/initialKeybindings.ts b/src/lib/settings/initialKeybindings.ts index 2071068e1..b49bd1462 100644 --- a/src/lib/settings/initialKeybindings.ts +++ b/src/lib/settings/initialKeybindings.ts @@ -1,5 +1,5 @@ -import { isDesktop } from 'lib/isDesktop' -import { platform } from 'lib/utils' +import { isDesktop } from '@src/lib/isDesktop' +import { platform } from '@src/lib/utils' export type InteractionMapItem = { name: string diff --git a/src/lib/settings/initialSettings.tsx b/src/lib/settings/initialSettings.tsx index 21bccdb79..a82a99a9c 100644 --- a/src/lib/settings/initialSettings.tsx +++ b/src/lib/settings/initialSettings.tsx @@ -1,28 +1,29 @@ -import { DEFAULT_PROJECT_NAME } from 'lib/constants' +import { useRef } from 'react' + +import type { CameraOrbitType } from '@rust/kcl-lib/bindings/CameraOrbitType' +import type { CameraProjectionType } from '@rust/kcl-lib/bindings/CameraProjectionType' +import type { NamedView } from '@rust/kcl-lib/bindings/NamedView' +import type { OnboardingStatus } from '@rust/kcl-lib/bindings/OnboardingStatus' + +import { CustomIcon } from '@src/components/CustomIcon' +import Tooltip from '@src/components/Tooltip' +import type { CameraSystem } from '@src/lib/cameraControls' +import { cameraMouseDragGuards, cameraSystems } from '@src/lib/cameraControls' import { + DEFAULT_DEFAULT_LENGTH_UNIT, + DEFAULT_PROJECT_NAME, +} from '@src/lib/constants' +import { isDesktop } from '@src/lib/isDesktop' +import type { BaseUnit, SettingProps, SettingsLevel, - baseUnitsUnion, -} from 'lib/settings/settingsTypes' -import { Themes } from 'lib/theme' -import { isEnumMember } from 'lib/types' -import { - CameraSystem, - cameraMouseDragGuards, - cameraSystems, -} from 'lib/cameraControls' -import { isDesktop } from 'lib/isDesktop' -import { useRef } from 'react' -import { CustomIcon } from 'components/CustomIcon' -import Tooltip from 'components/Tooltip' -import { isArray, toSync } from 'lib/utils' -import { reportRejection } from 'lib/trap' -import { CameraProjectionType } from '@rust/kcl-lib/bindings/CameraProjectionType' -import { OnboardingStatus } from '@rust/kcl-lib/bindings/OnboardingStatus' -import { NamedView } from '@rust/kcl-lib/bindings/NamedView' -import { CameraOrbitType } from '@rust/kcl-lib/bindings/CameraOrbitType' -import { DEFAULT_DEFAULT_LENGTH_UNIT } from 'lib/constants' +} from '@src/lib/settings/settingsTypes' +import { baseUnitsUnion } from '@src/lib/settings/settingsTypes' +import { Themes } from '@src/lib/theme' +import { reportRejection } from '@src/lib/trap' +import { isEnumMember } from '@src/lib/types' +import { isArray, toSync } from '@src/lib/utils' /** * A setting that can be set at the user or project level @@ -91,8 +92,8 @@ export class Setting { return this._project !== undefined ? this._project : this._user !== undefined - ? this._user - : this._default + ? this._user + : this._default } /** * @param {SettingsLevel} level - The level to get the fallback for diff --git a/src/lib/settings/settingsTypes.ts b/src/lib/settings/settingsTypes.ts index bbf17ec4a..3df094095 100644 --- a/src/lib/settings/settingsTypes.ts +++ b/src/lib/settings/settingsTypes.ts @@ -1,14 +1,16 @@ import { type Models } from '@kittycad/lib' -import { Setting, settings } from './initialSettings' -import { AtLeast, PathValue, Paths } from 'lib/types' -import { CommandArgumentConfig } from 'lib/commandTypes' -import { Themes } from 'lib/theme' -import { CameraProjectionType } from '@rust/kcl-lib/bindings/CameraProjectionType' -import { +import type { UnitAngle_type, UnitLength_type, } from '@kittycad/lib/dist/types/src/models' -import { CameraOrbitType } from '@rust/kcl-lib/bindings/CameraOrbitType' + +import type { CameraOrbitType } from '@rust/kcl-lib/bindings/CameraOrbitType' +import type { CameraProjectionType } from '@rust/kcl-lib/bindings/CameraProjectionType' + +import type { CommandArgumentConfig } from '@src/lib/commandTypes' +import type { Setting, settings } from '@src/lib/settings/initialSettings' +import type { Themes } from '@src/lib/theme' +import type { AtLeast, PathValue, Paths } from '@src/lib/types' export interface SettingsViaQueryString { pool: string | null diff --git a/src/lib/settings/settingsUtils.test.ts b/src/lib/settings/settingsUtils.test.ts index 3994662f7..f8840d165 100644 --- a/src/lib/settings/settingsUtils.test.ts +++ b/src/lib/settings/settingsUtils.test.ts @@ -1,12 +1,13 @@ -import { DeepPartial } from 'lib/types' -import { Configuration } from '@rust/kcl-lib/bindings/Configuration' +import type { Configuration } from '@rust/kcl-lib/bindings/Configuration' + +import { createSettings } from '@src/lib/settings/initialSettings' import { configurationToSettingsPayload, getAllCurrentSettings, projectConfigurationToSettingsPayload, setSettingsAtLevel, -} from './settingsUtils' -import { createSettings } from './initialSettings' +} from '@src/lib/settings/settingsUtils' +import type { DeepPartial } from '@src/lib/types' describe(`testing settings initialization`, () => { it(`sets settings at the 'user' level`, () => { diff --git a/src/lib/settings/settingsUtils.ts b/src/lib/settings/settingsUtils.ts index a3cf9f6f6..deb34baeb 100644 --- a/src/lib/settings/settingsUtils.ts +++ b/src/lib/settings/settingsUtils.ts @@ -1,3 +1,9 @@ +import type { Configuration } from '@rust/kcl-lib/bindings/Configuration' +import type { NamedView } from '@rust/kcl-lib/bindings/NamedView' +import type { ProjectConfiguration } from '@rust/kcl-lib/bindings/ProjectConfiguration' +import { default_app_settings } from '@rust/kcl-wasm-lib/pkg/kcl_wasm_lib' +import { TEST } from '@src/env' + import { defaultAppSettings, defaultProjectSettings, @@ -6,25 +12,26 @@ import { parseProjectSettings, serializeConfiguration, serializeProjectConfiguration, -} from 'lang/wasm' -import { mouseControlsToCameraSystem } from 'lib/cameraControls' -import { BROWSER_PROJECT_NAME } from 'lib/constants' +} from '@src/lang/wasm' +import { mouseControlsToCameraSystem } from '@src/lib/cameraControls' +import { BROWSER_PROJECT_NAME } from '@src/lib/constants' import { getInitialDefaultDir, readAppSettingsFile, readProjectSettingsFile, writeAppSettingsFile, writeProjectSettingsFile, -} from 'lib/desktop' -import { isDesktop } from 'lib/isDesktop' -import { Setting, createSettings, settings } from 'lib/settings/initialSettings' -import { appThemeToTheme } from 'lib/theme' -import { err } from 'lib/trap' -import { DeepPartial } from 'lib/types' -import { Configuration } from '@rust/kcl-lib/bindings/Configuration' -import { ProjectConfiguration } from '@rust/kcl-lib/bindings/ProjectConfiguration' -import { NamedView } from '@rust/kcl-lib/bindings/NamedView' -import { SaveSettingsPayload, SettingsLevel } from './settingsTypes' +} from '@src/lib/desktop' +import { isDesktop } from '@src/lib/isDesktop' +import type { Setting } from '@src/lib/settings/initialSettings' +import { createSettings, settings } from '@src/lib/settings/initialSettings' +import type { + SaveSettingsPayload, + SettingsLevel, +} from '@src/lib/settings/settingsTypes' +import { appThemeToTheme } from '@src/lib/theme' +import { err } from '@src/lib/trap' +import type { DeepPartial } from '@src/lib/types' /** * Convert from a rust settings struct into the JS settings struct. @@ -440,3 +447,16 @@ export function getSettingInputType(setting: Setting) { return setting.commandConfig.inputType as 'string' | 'options' | 'boolean' return typeof setting.default as 'string' | 'boolean' } + +export const jsAppSettings = async () => { + let jsAppSettings = default_app_settings() + if (!TEST) { + const settings = await import('@src/machines/appMachine').then((module) => + module.getSettings() + ) + if (settings) { + jsAppSettings = getAllCurrentSettings(settings) + } + } + return jsAppSettings +} diff --git a/src/lib/singletons.ts b/src/lib/singletons.ts index c54c5d662..d027a3719 100644 --- a/src/lib/singletons.ts +++ b/src/lib/singletons.ts @@ -1,11 +1,12 @@ -import { SceneEntities } from 'clientSideScene/sceneEntities' -import { SceneInfra } from 'clientSideScene/sceneInfra' -import EditorManager from 'editor/manager' -import { KclManager } from 'lang/KclSingleton' -import CodeManager from 'lang/codeManager' -import { EngineCommandManager } from 'lang/std/engineConnection' -import { uuidv4 } from './utils' -import RustContext from 'lib/rustContext' +import EditorManager from '@src/editor/manager' +import { KclManager } from '@src/lang/KclSingleton' +import CodeManager from '@src/lang/codeManager' +import { EngineCommandManager } from '@src/lang/std/engineConnection' +import RustContext from '@src/lib/rustContext' +import { uuidv4 } from '@src/lib/utils' + +import { SceneEntities } from '@src/clientSideScene/sceneEntities' +import { SceneInfra } from '@src/clientSideScene/sceneInfra' export const codeManager = new CodeManager() @@ -28,13 +29,20 @@ engineCommandManager.kclManager = kclManager export const sceneInfra = new SceneInfra(engineCommandManager) engineCommandManager.camControlsCameraChange = sceneInfra.onCameraChange -export const sceneEntitiesManager = new SceneEntities(engineCommandManager) - // This needs to be after sceneInfra and engineCommandManager are is created. export const editorManager = new EditorManager() export const rustContext = new RustContext(engineCommandManager) +export const sceneEntitiesManager = new SceneEntities( + engineCommandManager, + sceneInfra, + editorManager, + codeManager, + kclManager, + rustContext +) + if (typeof window !== 'undefined') { ;(window as any).engineCommandManager = engineCommandManager ;(window as any).kclManager = kclManager diff --git a/src/lib/sorting.ts b/src/lib/sorting.ts index 63306f11e..02af6d09b 100644 --- a/src/lib/sorting.ts +++ b/src/lib/sorting.ts @@ -1,5 +1,5 @@ -import { CustomIconName } from 'components/CustomIcon' -import { Project } from 'lib/project' +import type { CustomIconName } from '@src/components/CustomIcon' +import type { Project } from '@src/lib/project' const DESC = ':desc' diff --git a/src/lib/tauriFS.test.ts b/src/lib/tauriFS.test.ts index b5f7c7640..daf5bd200 100644 --- a/src/lib/tauriFS.test.ts +++ b/src/lib/tauriFS.test.ts @@ -1,8 +1,8 @@ +import { MAX_PADDING } from '@src/lib/constants' import { getNextProjectIndex, interpolateProjectNameWithIndex, -} from './desktopFS' -import { MAX_PADDING } from './constants' +} from '@src/lib/desktopFS' describe('Test project name utility functions', () => { it('interpolates a project name without an index', () => { diff --git a/src/lib/telemetry.test.ts b/src/lib/telemetry.test.ts index 9c1d6b45e..f52eaf01f 100644 --- a/src/lib/telemetry.test.ts +++ b/src/lib/telemetry.test.ts @@ -1,10 +1,10 @@ +import type { MaxWidth } from '@src/lib/telemetry' import { columnWidth, - MaxWidth, - printHeader, printDivider, + printHeader, printRow, -} from 'lib/telemetry' +} from '@src/lib/telemetry' describe('Telemetry', () => { describe('columnWidth', () => { diff --git a/src/lib/telemetry.ts b/src/lib/telemetry.ts index 6258d8f89..ac159714b 100644 --- a/src/lib/telemetry.ts +++ b/src/lib/telemetry.ts @@ -1,5 +1,7 @@ -import { PerformanceMark, getMarks } from 'lib/performance' -import { writeTelemetryFile, writeRawTelemetryFile } from 'lib/desktop' +import { writeRawTelemetryFile, writeTelemetryFile } from '@src/lib/desktop' +import type { PerformanceMark } from '@src/lib/performance' +import { getMarks } from '@src/lib/performance' + let args: any = null // Get the longest width of values or column name diff --git a/src/lib/testHelpers.ts b/src/lib/testHelpers.ts index b9d70faf9..b7de7206b 100644 --- a/src/lib/testHelpers.ts +++ b/src/lib/testHelpers.ts @@ -1,6 +1,8 @@ -import { Program, ExecState, jsAppSettings } from '../lang/wasm' -import { Node } from '@rust/kcl-lib/bindings/Node' -import { rustContext } from './singletons' +import type { Node } from '@rust/kcl-lib/bindings/Node' + +import type { ExecState, Program } from '@src/lang/wasm' +import { jsAppSettings } from '@src/lib/settings/settingsUtils' +import { rustContext } from '@src/lib/singletons' export async function enginelessExecutor( ast: Node, diff --git a/src/lib/textToCad.ts b/src/lib/textToCad.ts index b9aaef243..d4b3db734 100644 --- a/src/lib/textToCad.ts +++ b/src/lib/textToCad.ts @@ -1,21 +1,22 @@ -import { Models } from '@kittycad/lib' +import type { Models } from '@kittycad/lib' +import { VITE_KC_API_BASE_URL } from '@src/env' +import toast from 'react-hot-toast' +import type { NavigateFunction } from 'react-router-dom' +import type { ContextFrom, EventFrom } from 'xstate' + import { ToastTextToCadError, ToastTextToCadSuccess, -} from 'components/ToastTextToCad' -import { VITE_KC_API_BASE_URL } from 'env' -import toast from 'react-hot-toast' -import { FILE_EXT } from './constants' -import { ContextFrom, EventFrom } from 'xstate' -import { fileMachine } from 'machines/fileMachine' -import { NavigateFunction } from 'react-router-dom' -import crossPlatformFetch from './crossPlatformFetch' -import { isDesktop } from 'lib/isDesktop' -import { Themes } from './theme' -import { getNextFileName } from './desktopFS' -import { reportRejection } from './trap' -import { toSync } from './utils' -import { kclManager } from './singletons' +} from '@src/components/ToastTextToCad' +import { FILE_EXT } from '@src/lib/constants' +import crossPlatformFetch from '@src/lib/crossPlatformFetch' +import { getNextFileName } from '@src/lib/desktopFS' +import { isDesktop } from '@src/lib/isDesktop' +import { kclManager } from '@src/lib/singletons' +import type { Themes } from '@src/lib/theme' +import { reportRejection } from '@src/lib/trap' +import { toSync } from '@src/lib/utils' +import type { fileMachine } from '@src/machines/fileMachine' async function submitTextToCadPrompt( prompt: string, diff --git a/src/lib/theme.ts b/src/lib/theme.ts index fdfe6bd73..a99ef53c0 100644 --- a/src/lib/theme.ts +++ b/src/lib/theme.ts @@ -1,4 +1,4 @@ -import { AppTheme } from '@rust/kcl-lib/bindings/AppTheme' +import type { AppTheme } from '@rust/kcl-lib/bindings/AppTheme' /** A media query matcher for dark mode */ export const darkModeMatcher = diff --git a/src/lib/toolbar.ts b/src/lib/toolbar.ts index b1cc024ab..edc60cd2c 100644 --- a/src/lib/toolbar.ts +++ b/src/lib/toolbar.ts @@ -1,13 +1,15 @@ -import { CustomIconName } from 'components/CustomIcon' -import { DEV } from 'env' -import { commandBarActor } from 'machines/commandBarMachine' +import { DEV } from '@src/env' +import type { EventFrom, StateFrom } from 'xstate' + +import type { CustomIconName } from '@src/components/CustomIcon' +import { createLiteral } from '@src/lang/create' +import { commandBarActor } from '@src/machines/commandBarMachine' +import type { modelingMachine } from '@src/machines/modelingMachine' import { isEditingExistingSketch, - modelingMachine, pipeHasCircle, -} from 'machines/modelingMachine' -import { IS_NIGHTLY_OR_DEBUG } from 'routes/Settings' -import { EventFrom, StateFrom } from 'xstate' +} from '@src/machines/modelingMachine' +import { IS_NIGHTLY_OR_DEBUG } from '@src/routes/Settings' export type ToolbarModeName = 'modeling' | 'sketching' @@ -620,7 +622,22 @@ export const toolbarConfig: Record = { [ { id: 'constraint-length', - disabled: (state) => !state.matches({ Sketch: 'SketchIdle' }), + disabled: (state) => + !( + state.matches({ Sketch: 'SketchIdle' }) && + state.can({ + type: 'Constrain length', + data: { + selection: state.context.selectionRanges, + // dummy data is okay for checking if the constrain is possible + length: { + valueAst: createLiteral(1), + valueText: '1', + valueCalculated: '1', + }, + }, + }) + ), onClick: () => commandBarActor.send({ type: 'Find and select command', diff --git a/src/lib/types.ts b/src/lib/types.ts index b76a5c8f2..230a09381 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -1,4 +1,4 @@ -import { Project, FileEntry } from 'lib/project' +import type { FileEntry, Project } from '@src/lib/project' export type IndexLoaderData = { code: string | null @@ -44,30 +44,30 @@ type Prev = [ 18, 19, 20, - ...0[] + ...0[], ] export type Paths = [D] extends [never] ? never : T extends object - ? { - [K in keyof T]-?: K extends string | number - ? `${K}` | Join> - : never - }[keyof T] - : '' + ? { + [K in keyof T]-?: K extends string | number + ? `${K}` | Join> + : never + }[keyof T] + : '' type Idx = K extends keyof T ? T[K] : number extends keyof T - ? K extends `${number}` - ? T[number] + ? K extends `${number}` + ? T[number] + : never : never - : never export type PathValue< T, - P extends Paths + P extends Paths, > = P extends `${infer Key}.${infer Rest}` ? Rest extends Paths, 1> ? PathValue, Rest> @@ -77,8 +77,8 @@ export type PathValue< export type Leaves = [D] extends [never] ? never : T extends object - ? { [K in keyof T]-?: Join> }[keyof T] - : '' + ? { [K in keyof T]-?: Join> }[keyof T] + : '' // Thanks to @micfan on StackOverflow for this utility type: // https://stackoverflow.com/a/57390160/22753272 diff --git a/src/lib/useCalculateKclExpression.ts b/src/lib/useCalculateKclExpression.ts index cc79afafc..ead3acf4e 100644 --- a/src/lib/useCalculateKclExpression.ts +++ b/src/lib/useCalculateKclExpression.ts @@ -1,14 +1,16 @@ -import { useModelingContext } from 'hooks/useModelingContext' -import { kclManager } from 'lib/singletons' -import { useKclContext } from 'lang/KclProvider' -import { findUniqueName } from 'lang/modifyAst' -import { PrevVariable, findAllPreviousVariables } from 'lang/queryAst' -import { Expr, SourceRange } from 'lang/wasm' import { useEffect, useMemo, useRef, useState } from 'react' -import { getCalculatedKclExpressionValue } from './kclHelpers' -import { parse, resultIsOk } from 'lang/wasm' -import { err } from 'lib/trap' -import { getSafeInsertIndex } from 'lang/queryAst/getSafeInsertIndex' + +import { useModelingContext } from '@src/hooks/useModelingContext' +import { useKclContext } from '@src/lang/KclProvider' +import { findUniqueName } from '@src/lang/create' +import type { PrevVariable } from '@src/lang/queryAst' +import { findAllPreviousVariables } from '@src/lang/queryAst' +import { getSafeInsertIndex } from '@src/lang/queryAst/getSafeInsertIndex' +import type { Expr, SourceRange } from '@src/lang/wasm' +import { parse, resultIsOk } from '@src/lang/wasm' +import { getCalculatedKclExpressionValue } from '@src/lib/kclHelpers' +import { kclManager } from '@src/lib/singletons' +import { err } from '@src/lib/trap' const isValidVariableName = (name: string) => /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name) diff --git a/src/lib/usePreviousVarMentions.ts b/src/lib/usePreviousVarMentions.ts index 66787eaac..2a7f43356 100644 --- a/src/lib/usePreviousVarMentions.ts +++ b/src/lib/usePreviousVarMentions.ts @@ -1,5 +1,6 @@ -import { CompletionContext } from '@codemirror/autocomplete' -import { usePreviousVariables } from './usePreviousVariables' +import type { CompletionContext } from '@codemirror/autocomplete' + +import { usePreviousVariables } from '@src/lib/usePreviousVariables' /// Basically a fork of the `mentions` extension https://github.com/uiwjs/react-codemirror/blob/master/extensions/mentions/src/index.ts /// But it matches on any word, not just the `@` symbol diff --git a/src/lib/usePreviousVariables.ts b/src/lib/usePreviousVariables.ts index c6ec4be93..f0fc816d4 100644 --- a/src/lib/usePreviousVariables.ts +++ b/src/lib/usePreviousVariables.ts @@ -1,9 +1,10 @@ -import { useModelingContext } from 'hooks/useModelingContext' -import { kclManager } from 'lib/singletons' -import { useKclContext } from 'lang/KclProvider' -import { findAllPreviousVariables } from 'lang/queryAst' import { useEffect, useState } from 'react' +import { useModelingContext } from '@src/hooks/useModelingContext' +import { useKclContext } from '@src/lang/KclProvider' +import { findAllPreviousVariables } from '@src/lang/queryAst' +import { kclManager } from '@src/lib/singletons' + export function usePreviousVariables() { const { variables, code } = useKclContext() const { context } = useModelingContext() diff --git a/src/lib/utils.test.ts b/src/lib/utils.test.ts index 9f95000a5..cd792403f 100644 --- a/src/lib/utils.test.ts +++ b/src/lib/utils.test.ts @@ -1,13 +1,14 @@ +import type { SourceRange } from '@rust/kcl-lib/bindings/SourceRange' +import { topLevelRange } from '@src/lang/util' import { + hasDigitsLeftOfDecimal, + hasLeadingZero, + isClockwise, isOverlap, + onDragNumberCalculation, roundOff, simulateOnMouseDragMatch, - onDragNumberCalculation, - hasLeadingZero, - hasDigitsLeftOfDecimal, - isClockwise, -} from './utils' -import { SourceRange, topLevelRange } from '../lang/wasm' +} from '@src/lib/utils' describe('testing isOverlapping', () => { testBothOrders(topLevelRange(0, 3), topLevelRange(3, 10)) diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 94ec96d5d..30ff5576a 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -1,10 +1,10 @@ -import { SourceRange } from '../lang/wasm' - +import type { Binary as BSONBinary } from 'bson' import { v4 } from 'uuid' -import { isDesktop } from './isDesktop' -import { AnyMachineSnapshot } from 'xstate' -import { AsyncFn } from './types' -import { Binary as BSONBinary } from 'bson' +import type { AnyMachineSnapshot } from 'xstate' + +import type { SourceRange } from '@src/lang/wasm' +import { isDesktop } from '@src/lib/isDesktop' +import type { AsyncFn } from '@src/lib/types' export const uuidv4 = v4 diff --git a/src/lib/utils2d.test.ts b/src/lib/utils2d.test.ts index d64b0ca92..aa77cc96b 100644 --- a/src/lib/utils2d.test.ts +++ b/src/lib/utils2d.test.ts @@ -1,5 +1,5 @@ -import { Coords2d } from 'lang/std/sketch' -import { isPointsCCW, initPromise } from 'lang/wasm' +import type { Coords2d } from '@src/lang/std/sketch' +import { initPromise, isPointsCCW } from '@src/lang/wasm' beforeAll(async () => { await initPromise diff --git a/src/lib/utils2d.ts b/src/lib/utils2d.ts index d0e59844c..e0fe46125 100644 --- a/src/lib/utils2d.ts +++ b/src/lib/utils2d.ts @@ -1,5 +1,5 @@ -import { Coords2d } from 'lang/std/sketch' -import { getAngle } from './utils' +import type { Coords2d } from '@src/lang/std/sketch' +import { getAngle } from '@src/lib/utils' export function deg2Rad(deg: number): number { return (deg * Math.PI) / 180 diff --git a/src/lib/varCompletionExtension.ts b/src/lib/varCompletionExtension.ts index d2a5f08f1..91894e39a 100644 --- a/src/lib/varCompletionExtension.ts +++ b/src/lib/varCompletionExtension.ts @@ -1,9 +1,6 @@ -import { Extension } from '@codemirror/state' -import { - CompletionContext, - autocompletion, - Completion, -} from '@codemirror/autocomplete' +import type { Completion, CompletionContext } from '@codemirror/autocomplete' +import { autocompletion } from '@codemirror/autocomplete' +import type { Extension } from '@codemirror/state' /// Basically a fork of the `mentions` extension https://github.com/uiwjs/react-codemirror/blob/master/extensions/mentions/src/index.ts /// But it matches on any word, not just the `@` symbol diff --git a/src/lib/wasm_lib_wrapper.ts b/src/lib/wasm_lib_wrapper.ts index 8dbddd847..6d1e3d427 100644 --- a/src/lib/wasm_lib_wrapper.ts +++ b/src/lib/wasm_lib_wrapper.ts @@ -6,28 +6,28 @@ * A way to bypass this is by reloading the entire .js file so the global wasm variable * gets reinitialized and we do not use that old reference */ - -import { - parse_wasm as ParseWasm, - recast_wasm as RecastWasm, - format_number as FormatNumber, - kcl_lint as KclLint, - is_points_ccw as IsPointsCcw, - get_tangential_arc_to_info as GetTangentialArcToInfo, +import type { + base64_decode as Base64Decode, + change_kcl_settings as ChangeKclSettings, coredump as CoreDump, default_app_settings as DefaultAppSettings, + default_project_settings as DefaultProjectSettings, + format_number as FormatNumber, + get_kcl_version as GetKclVersion, + get_tangential_arc_to_info as GetTangentialArcToInfo, + is_kcl_empty_or_only_settings as IsKclEmptyOrOnlySettings, + is_points_ccw as IsPointsCcw, + kcl_lint as KclLint, + kcl_settings as KclSettings, parse_app_settings as ParseAppSettings, parse_project_settings as ParseProjectSettings, - default_project_settings as DefaultProjectSettings, - base64_decode as Base64Decode, - kcl_settings as KclSettings, - change_kcl_settings as ChangeKclSettings, - is_kcl_empty_or_only_settings as IsKclEmptyOrOnlySettings, - get_kcl_version as GetKclVersion, + parse_wasm as ParseWasm, + recast_wasm as RecastWasm, serialize_configuration as SerializeConfiguration, serialize_project_configuration as SerializeProjectConfiguration, } from '@rust/kcl-wasm-lib/pkg/kcl_wasm_lib' +// eslint-disable-next-line @typescript-eslint/consistent-type-imports export type ModuleType = typeof import('@rust/kcl-wasm-lib/pkg/kcl_wasm_lib') // Stores the result of the import of the wasm_lib file diff --git a/src/lib/withBaseURL.ts b/src/lib/withBaseURL.ts index 4ea2d3986..e23436bd3 100644 --- a/src/lib/withBaseURL.ts +++ b/src/lib/withBaseURL.ts @@ -1,4 +1,4 @@ -import { VITE_KC_API_BASE_URL } from '../env' +import { VITE_KC_API_BASE_URL } from '@src/env' export default function withBaseUrl(path: string): string { return VITE_KC_API_BASE_URL + path diff --git a/src/machines/appMachine.ts b/src/machines/appMachine.ts index 531d78d91..1938af55d 100644 --- a/src/machines/appMachine.ts +++ b/src/machines/appMachine.ts @@ -1,9 +1,11 @@ -import { ActorRefFrom, createActor, setup, spawnChild } from 'xstate' -import { authMachine } from './authMachine' import { useSelector } from '@xstate/react' -import { ACTOR_IDS } from './machineConstants' -import { settingsMachine } from './settingsMachine' -import { createSettings } from 'lib/settings/initialSettings' +import type { ActorRefFrom } from 'xstate' +import { createActor, setup, spawnChild } from 'xstate' + +import { createSettings } from '@src/lib/settings/initialSettings' +import { authMachine } from '@src/machines/authMachine' +import { ACTOR_IDS } from '@src/machines/machineConstants' +import { settingsMachine } from '@src/machines/settingsMachine' const { AUTH, SETTINGS } = ACTOR_IDS const appMachineActors = { diff --git a/src/machines/authMachine.ts b/src/machines/authMachine.ts index 8e76ba838..66507e7f7 100644 --- a/src/machines/authMachine.ts +++ b/src/machines/authMachine.ts @@ -1,22 +1,25 @@ -import { assign, setup, fromPromise } from 'xstate' -import { Models } from '@kittycad/lib' -import withBaseURL from '../lib/withBaseURL' -import { isDesktop } from 'lib/isDesktop' +import type { Models } from '@kittycad/lib' import { + DEV, VITE_KC_API_BASE_URL, VITE_KC_DEV_TOKEN, VITE_KC_SKIP_AUTH, - DEV, -} from 'env' +} from '@src/env' +import { assign, fromPromise, setup } from 'xstate' + +import { COOKIE_NAME } from '@src/lib/constants' import { getUser as getUserDesktop, readTokenFile, writeTokenFile, -} from 'lib/desktop' -import { COOKIE_NAME } from 'lib/constants' -import { markOnce } from 'lib/performance' -import { ACTOR_IDS } from './machineConstants' -import withBaseUrl from '../lib/withBaseURL' +} from '@src/lib/desktop' +import { isDesktop } from '@src/lib/isDesktop' +import { markOnce } from '@src/lib/performance' +import { + default as withBaseURL, + default as withBaseUrl, +} from '@src/lib/withBaseURL' +import { ACTOR_IDS } from '@src/machines/machineConstants' const SKIP_AUTH = VITE_KC_SKIP_AUTH === 'true' && DEV diff --git a/src/machines/commandBarMachine.ts b/src/machines/commandBarMachine.ts index b8e1a0eb0..fb45c2f81 100644 --- a/src/machines/commandBarMachine.ts +++ b/src/machines/commandBarMachine.ts @@ -1,15 +1,17 @@ -import { assign, createActor, fromPromise, setup, SnapshotFrom } from 'xstate' -import { +import { useSelector } from '@xstate/react' +import toast from 'react-hot-toast' +import type { SnapshotFrom } from 'xstate' +import { assign, createActor, fromPromise, setup } from 'xstate' + +import type { MachineManager } from '@src/components/MachineManagerProvider' +import { authCommands } from '@src/lib/commandBarConfigs/authCommandConfig' +import type { Command, CommandArgument, CommandArgumentWithName, KclCommandValue, -} from 'lib/commandTypes' -import { getCommandArgumentKclValuesOnly } from 'lib/commandUtils' -import { MachineManager } from 'components/MachineManagerProvider' -import toast from 'react-hot-toast' -import { useSelector } from '@xstate/react' -import { authCommands } from 'lib/commandBarConfigs/authCommandConfig' +} from '@src/lib/commandTypes' +import { getCommandArgumentKclValuesOnly } from '@src/lib/commandUtils' export type CommandBarContext = { commands: Command[] @@ -134,10 +136,11 @@ export const commandBarMachine = setup({ // that is, the first argument that is not already in the argumentsToSubmit // or hidden, or that is not undefined, or that is not marked as "skippable". // TODO validate the type of the existing arguments - const nonHiddenArgs = Object.entries(selectedCommand.args).filter((a) => - a[1].hidden && typeof a[1].hidden === 'function' - ? !a[1].hidden(context) - : !a[1].hidden + const nonHiddenArgs = Object.entries(selectedCommand.args).filter( + (a) => + a[1].hidden && typeof a[1].hidden === 'function' + ? !a[1].hidden(context) + : !a[1].hidden ) let argIndex = 0 @@ -241,8 +244,8 @@ export const commandBarMachine = setup({ argName in event.data.argDefaultValues ? event.data.argDefaultValues[argName] : arg.skip && 'defaultValue' in arg - ? arg.defaultValue - : undefined + ? arg.defaultValue + : undefined } return args }, diff --git a/src/machines/featureTreeMachine.ts b/src/machines/featureTreeMachine.ts index b436a2a38..5f86651d7 100644 --- a/src/machines/featureTreeMachine.ts +++ b/src/machines/featureTreeMachine.ts @@ -1,21 +1,21 @@ -import { Artifact, getArtifactFromRange } from 'lang/std/artifactGraph' -import { SourceRange } from 'lang/wasm' -import { - enterAppearanceFlow, - enterEditFlow, - EnterEditFlowProps, -} from 'lib/operations' -import { kclManager } from 'lib/singletons' -import { err } from 'lib/trap' import toast from 'react-hot-toast' -import { Operation } from '@rust/kcl-lib/bindings/Operation' import { assign, fromPromise, setup } from 'xstate' -import { commandBarActor } from './commandBarMachine' -import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils' + +import type { Operation } from '@rust/kcl-lib/bindings/Operation' + import { deleteSelectionPromise, deletionErrorMessage, -} from 'lang/modifyAst/deleteSelection' +} from '@src/lang/modifyAst/deleteSelection' +import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils' +import type { Artifact } from '@src/lang/std/artifactGraph' +import { getArtifactFromRange } from '@src/lang/std/artifactGraph' +import type { SourceRange } from '@src/lang/wasm' +import type { EnterEditFlowProps } from '@src/lib/operations' +import { enterAppearanceFlow, enterEditFlow } from '@src/lib/operations' +import { kclManager } from '@src/lib/singletons' +import { err } from '@src/lib/trap' +import { commandBarActor } from '@src/machines/commandBarMachine' type FeatureTreeEvent = | { @@ -273,10 +273,10 @@ export const featureTreeMachine = setup({ src: 'prepareEditCommand', input: ({ context }) => { const artifact = context.targetSourceRange - ? getArtifactFromRange( + ? (getArtifactFromRange( context.targetSourceRange, kclManager.artifactGraph - ) ?? undefined + ) ?? undefined) : undefined return { // currentOperation is guaranteed to be defined here @@ -327,10 +327,10 @@ export const featureTreeMachine = setup({ src: 'prepareAppearanceCommand', input: ({ context }) => { const artifact = context.targetSourceRange - ? getArtifactFromRange( + ? (getArtifactFromRange( context.targetSourceRange, kclManager.artifactGraph - ) ?? undefined + ) ?? undefined) : undefined return { // currentOperation is guaranteed to be defined here @@ -381,10 +381,10 @@ export const featureTreeMachine = setup({ src: 'sendDeleteCommand', input: ({ context }) => { const artifact = context.targetSourceRange - ? getArtifactFromRange( + ? (getArtifactFromRange( context.targetSourceRange, kclManager.artifactGraph - ) ?? undefined + ) ?? undefined) : undefined return { artifact, diff --git a/src/machines/fileMachine.ts b/src/machines/fileMachine.ts index f628ed643..a200226c5 100644 --- a/src/machines/fileMachine.ts +++ b/src/machines/fileMachine.ts @@ -1,5 +1,6 @@ import { assign, fromPromise, setup } from 'xstate' -import { Project, FileEntry } from 'lib/project' + +import type { FileEntry, Project } from '@src/lib/project' type FileMachineContext = { project: Project diff --git a/src/machines/kclEditorMachine.ts b/src/machines/kclEditorMachine.ts index 86c16efe6..a23e32a0c 100644 --- a/src/machines/kclEditorMachine.ts +++ b/src/machines/kclEditorMachine.ts @@ -1,5 +1,6 @@ -import { assign, createActor, setup, StateFrom } from 'xstate' -import { EditorSelection } from '@codemirror/state' +import type { EditorSelection } from '@codemirror/state' +import type { StateFrom } from 'xstate' +import { assign, createActor, setup } from 'xstate' type SelectionEvent = { codeMirrorSelection: EditorSelection diff --git a/src/machines/modelingMachine.ts b/src/machines/modelingMachine.ts index f4b9da995..bc9ac3bc9 100644 --- a/src/machines/modelingMachine.ts +++ b/src/machines/modelingMachine.ts @@ -1,4 +1,92 @@ +import toast from 'react-hot-toast' +import { Mesh, Vector2, Vector3 } from 'three' +import { assign, fromPromise, setup } from 'xstate' + +import type { Node } from '@rust/kcl-lib/bindings/Node' + +import { deleteSegment } from '@src/clientSideScene/ClientSideSceneComp' import { + orthoScale, + quaternionFromUpNForward, +} from '@src/clientSideScene/helpers' +import { DRAFT_DASHED_LINE } from '@src/clientSideScene/sceneConstants' +import { DRAFT_POINT } from '@src/clientSideScene/sceneInfra' +import { createProfileStartHandle } from '@src/clientSideScene/segments' +import type { MachineManager } from '@src/components/MachineManagerProvider' +import type { ModelingMachineContext } from '@src/components/ModelingMachineProvider' +import type { SidebarType } from '@src/components/ModelingSidebar/ModelingPanes' +import { angleLengthInfo } from '@src/components/Toolbar/angleLengthInfo' +import { + applyConstraintEqualAngle, + equalAngleInfo, +} from '@src/components/Toolbar/EqualAngle' +import { + applyConstraintEqualLength, + setEqualLengthInfo, +} from '@src/components/Toolbar/EqualLength' +import { + applyConstraintHorzVert, + horzVertInfo, +} from '@src/components/Toolbar/HorzVert' +import { intersectInfo } from '@src/components/Toolbar/Intersect' +import { + applyRemoveConstrainingValues, + removeConstrainingValuesInfo, +} from '@src/components/Toolbar/RemoveConstrainingValues' +import { + absDistanceInfo, + applyConstraintAxisAlign, +} from '@src/components/Toolbar/SetAbsDistance' +import { angleBetweenInfo } from '@src/components/Toolbar/SetAngleBetween' +import { + applyConstraintHorzVertAlign, + horzVertDistanceInfo, +} from '@src/components/Toolbar/SetHorzVertDistance' +import { createLiteral, createLocalName } from '@src/lang/create' +import { updateModelingState } from '@src/lang/modelingWorkflows' +import { + addHelix, + addOffsetPlane, + addShell, + addSweep, + deleteNodeInExtrudePipe, + extrudeSketch, + insertNamedConstant, + loftSketches, +} from '@src/lang/modifyAst' +import type { + ChamferParameters, + FilletParameters, +} from '@src/lang/modifyAst/addEdgeTreatment' +import { + EdgeTreatmentType, + applyEdgeTreatmentToSelection, + getPathToExtrudeForSegmentSelection, + mutateAstWithTagForSketchSegment, +} from '@src/lang/modifyAst/addEdgeTreatment' +import { + getAxisExpressionAndIndex, + revolveSketch, +} from '@src/lang/modifyAst/addRevolve' +import { + applyIntersectFromTargetOperatorSelections, + applySubtractFromTargetOperatorSelections, + applyUnionFromTargetOperatorSelections, +} from '@src/lang/modifyAst/boolean' +import { + deleteSelectionPromise, + deletionErrorMessage, +} from '@src/lang/modifyAst/deleteSelection' +import { setAppearance } from '@src/lang/modifyAst/setAppearance' +import { + getNodeFromPath, + isNodeSafeToReplacePath, + stringifyPathToNode, +} from '@src/lang/queryAst' +import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils' +import { getPathsFromPlaneArtifact } from '@src/lang/std/artifactGraph' +import type { Coords2d } from '@src/lang/std/sketch' +import type { CallExpression, CallExpressionKw, Expr, @@ -7,112 +95,30 @@ import { PathToNode, VariableDeclaration, VariableDeclarator, - parse, - recast, - resultIsOk, - sketchFromKclValue, -} from 'lang/wasm' -import { +} from '@src/lang/wasm' +import { parse, recast, resultIsOk, sketchFromKclValue } from '@src/lang/wasm' +import type { ModelingCommandSchema } from '@src/lib/commandBarConfigs/modelingCommandConfig' +import type { KclCommandValue } from '@src/lib/commandTypes' +import { EXECUTION_TYPE_REAL } from '@src/lib/constants' +import type { DefaultPlaneStr } from '@src/lib/planes' +import type { Axis, DefaultPlaneSelection, - Selections, Selection, - updateSelections, -} from 'lib/selections' -import { assign, fromPromise, setup } from 'xstate' -import { SidebarType } from 'components/ModelingSidebar/ModelingPanes' -import { isNodeSafeToReplacePath, stringifyPathToNode } from 'lang/queryAst' -import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils' + Selections, +} from '@src/lib/selections' +import { updateSelections } from '@src/lib/selections' import { - kclManager, - sceneInfra, - sceneEntitiesManager, - engineCommandManager, - editorManager, codeManager, -} from 'lib/singletons' -import { - horzVertInfo, - applyConstraintHorzVert, -} from 'components/Toolbar/HorzVert' -import { - applyConstraintHorzVertAlign, - horzVertDistanceInfo, -} from 'components/Toolbar/SetHorzVertDistance' -import { angleBetweenInfo } from 'components/Toolbar/SetAngleBetween' -import { angleLengthInfo } from 'components/Toolbar/setAngleLength' -import { - applyConstraintEqualLength, - setEqualLengthInfo, -} from 'components/Toolbar/EqualLength' -import { - getAxisExpressionAndIndex, - revolveSketch, -} from 'lang/modifyAst/addRevolve' -import { - addHelix, - addOffsetPlane, - addShell, - addSweep, - createLiteral, - createLocalName, - deleteNodeInExtrudePipe, - extrudeSketch, - insertNamedConstant, - loftSketches, -} from 'lang/modifyAst' -import { - applyEdgeTreatmentToSelection, - ChamferParameters, - EdgeTreatmentType, - FilletParameters, - getPathToExtrudeForSegmentSelection, - mutateAstWithTagForSketchSegment, -} from 'lang/modifyAst/addEdgeTreatment' -import { getNodeFromPath } from '../lang/queryAst' -import { - applyConstraintEqualAngle, - equalAngleInfo, -} from 'components/Toolbar/EqualAngle' -import { - applyRemoveConstrainingValues, - removeConstrainingValuesInfo, -} from 'components/Toolbar/RemoveConstrainingValues' -import { intersectInfo } from 'components/Toolbar/Intersect' -import { - absDistanceInfo, - applyConstraintAxisAlign, -} from 'components/Toolbar/SetAbsDistance' -import { ModelingCommandSchema } from 'lib/commandBarConfigs/modelingCommandConfig' -import { err, reportRejection, trap } from 'lib/trap' -import { DefaultPlaneStr } from 'lib/planes' -import { isArray, uuidv4 } from 'lib/utils' -import { Coords2d } from 'lang/std/sketch' -import { deleteSegment } from 'clientSideScene/ClientSideSceneComp' -import toast from 'react-hot-toast' -import { ToolbarModeName } from 'lib/toolbar' -import { orthoScale, quaternionFromUpNForward } from 'clientSideScene/helpers' -import { Mesh, Vector2, Vector3 } from 'three' -import { MachineManager } from 'components/MachineManagerProvider' -import { KclCommandValue } from 'lib/commandTypes' -import { ModelingMachineContext } from 'components/ModelingMachineProvider' -import { - deleteSelectionPromise, - deletionErrorMessage, -} from 'lang/modifyAst/deleteSelection' -import { getPathsFromPlaneArtifact } from 'lang/std/artifactGraph' -import { createProfileStartHandle } from 'clientSideScene/segments' -import { DRAFT_POINT } from 'clientSideScene/sceneInfra' -import { setAppearance } from 'lang/modifyAst/setAppearance' -import { DRAFT_DASHED_LINE } from 'clientSideScene/sceneEntities' -import { Node } from '@rust/kcl-lib/bindings/Node' -import { updateModelingState } from 'lang/modelingWorkflows' -import { EXECUTION_TYPE_REAL } from 'lib/constants' -import { - applyIntersectFromTargetOperatorSelections, - applySubtractFromTargetOperatorSelections, - applyUnionFromTargetOperatorSelections, -} from 'lang/modifyAst/boolean' + editorManager, + engineCommandManager, + kclManager, + sceneEntitiesManager, + sceneInfra, +} from '@src/lib/singletons' +import type { ToolbarModeName } from '@src/lib/toolbar' +import { err, reportRejection, trap } from '@src/lib/trap' +import { isArray, uuidv4 } from '@src/lib/utils' export const MODELING_PERSIST_KEY = 'MODELING_PERSIST_KEY' @@ -1107,8 +1113,8 @@ export const modelingMachine = setup({ currentTool === 'tangentialArc' ? { type: 'Continue existing profile', data } : currentTool === 'arc' - ? { type: 'Add start point', data } - : { type: 'Add start point', data } + ? { type: 'Add start point', data } + : { type: 'Add start point', data } ), }) }, diff --git a/src/machines/projectsMachine.ts b/src/machines/projectsMachine.ts index 7344259f6..1be6a2d71 100644 --- a/src/machines/projectsMachine.ts +++ b/src/machines/projectsMachine.ts @@ -1,7 +1,8 @@ import { assign, fromPromise, setup } from 'xstate' -import { ProjectsCommandSchema } from 'lib/commandBarConfigs/projectsCommandConfig' -import { Project } from 'lib/project' -import { isArray } from 'lib/utils' + +import type { ProjectsCommandSchema } from '@src/lib/commandBarConfigs/projectsCommandConfig' +import type { Project } from '@src/lib/project' +import { isArray } from '@src/lib/utils' export const projectsMachine = setup({ types: { diff --git a/src/machines/settingsMachine.ts b/src/machines/settingsMachine.ts index 758602ebd..270dce448 100644 --- a/src/machines/settingsMachine.ts +++ b/src/machines/settingsMachine.ts @@ -1,5 +1,7 @@ +import decamelize from 'decamelize' +import toast from 'react-hot-toast' +import type { AnyActorRef } from 'xstate' import { - AnyActorRef, assign, enqueueActions, fromCallback, @@ -7,25 +9,24 @@ import { sendTo, setup, } from 'xstate' + +import type { NamedView } from '@rust/kcl-lib/bindings/NamedView' + import { - Themes, - darkModeMatcher, - getOppositeTheme, - getSystemTheme, - setThemeClass, -} from 'lib/theme' -import { - createSettings, - settings, - SettingsType, -} from 'lib/settings/initialSettings' -import { + createSettingsCommand, + settingsWithCommandConfigs, +} from '@src/lib/commandBarConfigs/settingsCommandConfig' +import type { Command } from '@src/lib/commandTypes' +import type { Project } from '@src/lib/project' +import type { SettingsType } from '@src/lib/settings/initialSettings' +import { createSettings, settings } from '@src/lib/settings/initialSettings' +import type { BaseUnit, SetEventTypes, SettingsLevel, SettingsPaths, WildcardSetEvent, -} from 'lib/settings/settingsTypes' +} from '@src/lib/settings/settingsTypes' import { clearSettingsAtLevel, configurationToSettingsPayload, @@ -33,25 +34,23 @@ import { projectConfigurationToSettingsPayload, saveSettings, setSettingsAtLevel, -} from 'lib/settings/settingsUtils' -import { NamedView } from '@rust/kcl-lib/bindings/NamedView' +} from '@src/lib/settings/settingsUtils' import { codeManager, engineCommandManager, kclManager, sceneEntitiesManager, sceneInfra, -} from 'lib/singletons' -import toast from 'react-hot-toast' -import decamelize from 'decamelize' -import { reportRejection } from 'lib/trap' -import { Project } from 'lib/project' +} from '@src/lib/singletons' import { - createSettingsCommand, - settingsWithCommandConfigs, -} from 'lib/commandBarConfigs/settingsCommandConfig' -import { Command } from 'lib/commandTypes' -import { commandBarActor } from './commandBarMachine' + Themes, + darkModeMatcher, + getOppositeTheme, + getSystemTheme, + setThemeClass, +} from '@src/lib/theme' +import { reportRejection } from '@src/lib/trap' +import { commandBarActor } from '@src/machines/commandBarMachine' type SettingsMachineContext = SettingsType & { currentProject?: Project @@ -206,7 +205,7 @@ export const settingsMachine = setup({ if (!('data' in event)) return const eventParts = event.type.replace(/^set./, '').split('.') as [ keyof typeof settings, - string + string, ] const truncatedNewValue = event.data.value?.toString().slice(0, 28) const message = diff --git a/src/main.ts b/src/main.ts index aa0697f47..849938526 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,41 +1,43 @@ // Some of the following was taken from bits and pieces of the vite-typescript // template that ElectronJS provides. -import dotenv from 'dotenv' -import { - app, - BrowserWindow, - ipcMain, - dialog, - shell, - nativeTheme, - desktopCapturer, - systemPreferences, - Menu, - screen, -} from 'electron' -import path from 'path' -import { Issuer } from 'openid-client' -import { Bonjour, Service } from 'bonjour-service' // @ts-ignore: TS1343 import * as kittycad from '@kittycad/lib/import' +import * as packageJSON from '@root/package.json' +import type { Service } from 'bonjour-service' +import { Bonjour } from 'bonjour-service' +import dotenv from 'dotenv' +import { + BrowserWindow, + Menu, + app, + desktopCapturer, + dialog, + ipcMain, + nativeTheme, + screen, + shell, + systemPreferences, +} from 'electron' import electronUpdater, { type AppUpdater } from 'electron-updater' -import getCurrentProjectFile from 'lib/getCurrentProjectFile' import os from 'node:os' -import { reportRejection } from 'lib/trap' -import { ZOO_STUDIO_PROTOCOL } from 'lib/constants' +import { Issuer } from 'openid-client' +import path from 'path' + import { argvFromYargs, getPathOrUrlFromArgs, parseCLIArgs, -} from './commandLineArgs' -import * as packageJSON from '../package.json' +} from '@src/commandLineArgs' +import { ZOO_STUDIO_PROTOCOL } from '@src/lib/constants' +import getCurrentProjectFile from '@src/lib/getCurrentProjectFile' +import { reportRejection } from '@src/lib/trap' import { buildAndSetMenuForFallback, buildAndSetMenuForModelingPage, buildAndSetMenuForProjectPage, - enableMenu, disableMenu, -} from './menu' + enableMenu, +} from '@src/menu' let mainWindow: BrowserWindow | null = null @@ -369,10 +371,7 @@ ipcMain.handle('startDeviceFlow', async (_, host: string) => { ipcMain.handle('kittycad', (event, data) => { return data.access .split('.') - .reduce( - (obj: any, prop: any) => obj[prop], - kittycad - )(data.args) + .reduce((obj: any, prop: any) => obj[prop], kittycad)(data.args) }) // Used to find other devices on the local network, e.g. 3D printers, CNC machines, etc. diff --git a/src/menu.ts b/src/menu.ts index 8384916de..c3a47557a 100644 --- a/src/menu.ts +++ b/src/menu.ts @@ -1,11 +1,13 @@ -import { app, Menu, BrowserWindow } from 'electron' -import { projectFileRole } from 'menu/fileRole' -import { projectEditRole } from 'menu/editRole' -import { helpRole } from 'menu/helpRole' -import { projectViewRole } from 'menu/viewRole' - +import type { BrowserWindow } from 'electron' +import { Menu, app } from 'electron' import os from 'node:os' -import { ZooMenuItemConstructorOptions } from 'menu/roles' + +import { projectEditRole } from '@src/menu/editRole' +import { projectFileRole } from '@src/menu/fileRole' +import { helpRole } from '@src/menu/helpRole' +import type { ZooMenuItemConstructorOptions } from '@src/menu/roles' +import { projectViewRole } from '@src/menu/viewRole' + const isMac = os.platform() === 'darwin' // Default electron menu. diff --git a/src/menu/channels.ts b/src/menu/channels.ts index b01dde3bd..84a96d394 100644 --- a/src/menu/channels.ts +++ b/src/menu/channels.ts @@ -1,5 +1,6 @@ -import { BrowserWindow } from 'electron' -import type { Channel } from '../channels' +import type { BrowserWindow } from 'electron' + +import type { Channel } from '@src/channels' // types for knowing what menu sends what webContent payload export type MenuLabels = diff --git a/src/menu/editRole.ts b/src/menu/editRole.ts index b19b59dda..1d2f42059 100644 --- a/src/menu/editRole.ts +++ b/src/menu/editRole.ts @@ -1,7 +1,9 @@ -import { BrowserWindow } from 'electron' -import { typeSafeWebContentsSend } from './channels' +import type { BrowserWindow } from 'electron' import os from 'node:os' -import { ZooMenuItemConstructorOptions } from './roles' + +import { typeSafeWebContentsSend } from '@src/menu/channels' +import type { ZooMenuItemConstructorOptions } from '@src/menu/roles' + const isMac = os.platform() === 'darwin' export const projectEditRole = ( diff --git a/src/menu/fileRole.ts b/src/menu/fileRole.ts index 295661301..4c2e0e63b 100644 --- a/src/menu/fileRole.ts +++ b/src/menu/fileRole.ts @@ -1,7 +1,9 @@ -import { BrowserWindow } from 'electron' -import { typeSafeWebContentsSend } from './channels' -import { ZooMenuItemConstructorOptions } from './roles' +import type { BrowserWindow } from 'electron' import os from 'node:os' + +import { typeSafeWebContentsSend } from '@src/menu/channels' +import type { ZooMenuItemConstructorOptions } from '@src/menu/roles' + const isMac = os.platform() === 'darwin' export const projectFileRole = ( diff --git a/src/menu/helpRole.ts b/src/menu/helpRole.ts index ff4d84325..5c10687f6 100644 --- a/src/menu/helpRole.ts +++ b/src/menu/helpRole.ts @@ -1,7 +1,9 @@ -import { shell, BrowserWindow } from 'electron' -import { ZooMenuItemConstructorOptions } from './roles' -import { reportRejection } from 'lib/trap' -import { typeSafeWebContentsSend } from './channels' +import type { BrowserWindow } from 'electron' +import { shell } from 'electron' + +import { reportRejection } from '@src/lib/trap' +import { typeSafeWebContentsSend } from '@src/menu/channels' +import type { ZooMenuItemConstructorOptions } from '@src/menu/roles' export const helpRole = ( mainWindow: BrowserWindow diff --git a/src/menu/roles.ts b/src/menu/roles.ts index e2a843a70..b04daa4d7 100644 --- a/src/menu/roles.ts +++ b/src/menu/roles.ts @@ -1,6 +1,6 @@ // Does not matter what labels belong to what type. I only split these into some internal types to easily parse // what labels should belong to what grouping -import { Menu, MenuItemConstructorOptions } from 'electron' +import type { Menu, MenuItemConstructorOptions } from 'electron' type HeaderLabel = | 'File' diff --git a/src/menu/viewRole.ts b/src/menu/viewRole.ts index 5e16746d9..ea901b716 100644 --- a/src/menu/viewRole.ts +++ b/src/menu/viewRole.ts @@ -1,7 +1,9 @@ -import { BrowserWindow } from 'electron' -import { ZooMenuItemConstructorOptions } from './roles' -import { typeSafeWebContentsSend } from './channels' +import type { BrowserWindow } from 'electron' import os from 'node:os' + +import { typeSafeWebContentsSend } from '@src/menu/channels' +import type { ZooMenuItemConstructorOptions } from '@src/menu/roles' + const isMac = os.platform() === 'darwin' export const projectViewRole = ( diff --git a/src/preload.ts b/src/preload.ts index abcbd425c..0cf65d788 100644 --- a/src/preload.ts +++ b/src/preload.ts @@ -1,13 +1,15 @@ -import { ipcRenderer, contextBridge, IpcRendererEvent } from 'electron' -import path from 'path' +import packageJson from '@root/package.json' +import type { MachinesListing } from '@src/components/MachineManagerProvider' +import chokidar from 'chokidar' +import type { IpcRendererEvent } from 'electron' +import { contextBridge, ipcRenderer } from 'electron' +import fsSync from 'node:fs' import fs from 'node:fs/promises' import os from 'node:os' -import fsSync from 'node:fs' -import packageJson from '../package.json' -import { MachinesListing } from 'components/MachineManagerProvider' -import chokidar from 'chokidar' -import type { Channel } from './channels' -import type { WebContentSendPayload } from './menu/channels' +import path from 'path' + +import type { Channel } from '@src/channels' +import type { WebContentSendPayload } from '@src/menu/channels' const typeSafeIpcRendererOn = ( channel: Channel, diff --git a/src/reportWebVitals.ts b/src/reportWebVitals.ts index 918338364..af6501be0 100644 --- a/src/reportWebVitals.ts +++ b/src/reportWebVitals.ts @@ -1,5 +1,5 @@ -import { reportRejection } from 'lib/trap' -import { ReportHandler } from 'web-vitals' +import { reportRejection } from '@src/lib/trap' +import type { ReportHandler } from 'web-vitals' const reportWebVitals = (onPerfEntry?: ReportHandler) => { if (onPerfEntry && onPerfEntry instanceof Function) { diff --git a/src/routes/Home.tsx b/src/routes/Home.tsx index c3bed4479..385251f7f 100644 --- a/src/routes/Home.tsx +++ b/src/routes/Home.tsx @@ -1,32 +1,35 @@ -import { FormEvent, useEffect, useRef, useState } from 'react' -import { ActionButton } from 'components/ActionButton' -import { AppHeader } from 'components/AppHeader' -import ProjectCard from 'components/ProjectCard/ProjectCard' -import { useNavigate, useSearchParams } from 'react-router-dom' -import { Link } from 'react-router-dom' +import type { FormEvent } from 'react' +import { useEffect, useRef, useState } from 'react' import { toast } from 'react-hot-toast' -import Loading from 'components/Loading' -import { PATHS } from 'lib/paths' +import { useHotkeys } from 'react-hotkeys-hook' +import { Link, useNavigate, useSearchParams } from 'react-router-dom' + +import { ActionButton } from '@src/components/ActionButton' +import { AppHeader } from '@src/components/AppHeader' +import Loading from '@src/components/Loading' +import { LowerRightControls } from '@src/components/LowerRightControls' +import ProjectCard from '@src/components/ProjectCard/ProjectCard' +import { + ProjectSearchBar, + useProjectSearch, +} from '@src/components/ProjectSearchBar' +import { useCreateFileLinkQuery } from '@src/hooks/useCreateFileLinkQueryWatcher' +import { useMenuListener } from '@src/hooks/useMenu' +import { useProjectsContext } from '@src/hooks/useProjectsContext' +import { isDesktop } from '@src/lib/isDesktop' +import { PATHS } from '@src/lib/paths' +import { markOnce } from '@src/lib/performance' +import type { Project } from '@src/lib/project' +import { kclManager } from '@src/lib/singletons' import { getNextSearchParams, getSortFunction, getSortIcon, -} from '../lib/sorting' -import { useHotkeys } from 'react-hotkeys-hook' -import { isDesktop } from 'lib/isDesktop' -import { kclManager } from 'lib/singletons' -import { LowerRightControls } from 'components/LowerRightControls' -import { ProjectSearchBar, useProjectSearch } from 'components/ProjectSearchBar' -import { Project } from 'lib/project' -import { markOnce } from 'lib/performance' -import { useProjectsContext } from 'hooks/useProjectsContext' -import { commandBarActor } from 'machines/commandBarMachine' -import { useCreateFileLinkQuery } from 'hooks/useCreateFileLinkQueryWatcher' -import { useSettings } from 'machines/appMachine' -import { reportRejection } from 'lib/trap' -import { authActor } from 'machines/appMachine' -import type { WebContentSendPayload } from '../menu/channels' -import { useMenuListener } from 'hooks/useMenu' +} from '@src/lib/sorting' +import { reportRejection } from '@src/lib/trap' +import { authActor, useSettings } from '@src/machines/appMachine' +import { commandBarActor } from '@src/machines/commandBarMachine' +import type { WebContentSendPayload } from '@src/menu/channels' // This route only opens in the desktop context for now, // as defined in Router.tsx, so we can use the desktop APIs and types. diff --git a/src/routes/Onboarding/Camera.tsx b/src/routes/Onboarding/Camera.tsx index 19aa6a19a..04e7b50fd 100644 --- a/src/routes/Onboarding/Camera.tsx +++ b/src/routes/Onboarding/Camera.tsx @@ -1,12 +1,14 @@ -import { OnboardingButtons, useDismiss, useNextClick } from '.' -import { onboardingPaths } from 'routes/Onboarding/paths' +import { SettingsSection } from '@src/components/Settings/SettingsSection' +import type { CameraSystem } from '@src/lib/cameraControls' +import { cameraMouseDragGuards, cameraSystems } from '@src/lib/cameraControls' +import { settingsActor, useSettings } from '@src/machines/appMachine' +import { onboardingPaths } from '@src/routes/Onboarding/paths' + import { - CameraSystem, - cameraMouseDragGuards, - cameraSystems, -} from 'lib/cameraControls' -import { SettingsSection } from 'components/Settings/SettingsSection' -import { settingsActor, useSettings } from 'machines/appMachine' + OnboardingButtons, + useDismiss, + useNextClick, +} from '@src/routes/Onboarding/utils' export default function Units() { useDismiss() diff --git a/src/routes/Onboarding/CmdK.tsx b/src/routes/Onboarding/CmdK.tsx index 1ed836eb0..29610c9ac 100644 --- a/src/routes/Onboarding/CmdK.tsx +++ b/src/routes/Onboarding/CmdK.tsx @@ -1,8 +1,9 @@ -import usePlatform from 'hooks/usePlatform' -import { OnboardingButtons, kbdClasses } from '.' -import { onboardingPaths } from 'routes/Onboarding/paths' -import { hotkeyDisplay } from 'lib/hotkeyWrapper' -import { COMMAND_PALETTE_HOTKEY } from 'components/CommandBar/CommandBar' +import { COMMAND_PALETTE_HOTKEY } from '@src/components/CommandBar/CommandBar' +import usePlatform from '@src/hooks/usePlatform' +import { hotkeyDisplay } from '@src/lib/hotkeyWrapper' +import { onboardingPaths } from '@src/routes/Onboarding/paths' + +import { OnboardingButtons, kbdClasses } from '@src/routes/Onboarding/utils' export default function CmdK() { const platformName = usePlatform() diff --git a/src/routes/Onboarding/CodeEditor.tsx b/src/routes/Onboarding/CodeEditor.tsx index 9af30860c..e0e4e47fb 100644 --- a/src/routes/Onboarding/CodeEditor.tsx +++ b/src/routes/Onboarding/CodeEditor.tsx @@ -1,5 +1,10 @@ -import { kbdClasses, OnboardingButtons, useDemoCode } from '.' -import { onboardingPaths } from 'routes/Onboarding/paths' +import { onboardingPaths } from '@src/routes/Onboarding/paths' + +import { + OnboardingButtons, + kbdClasses, + useDemoCode, +} from '@src/routes/Onboarding/utils' export default function OnboardingCodeEditor() { useDemoCode() diff --git a/src/routes/Onboarding/Export.tsx b/src/routes/Onboarding/Export.tsx index c71455080..5d14b4d87 100644 --- a/src/routes/Onboarding/Export.tsx +++ b/src/routes/Onboarding/Export.tsx @@ -1,6 +1,7 @@ -import { APP_NAME } from 'lib/constants' -import { OnboardingButtons } from '.' -import { onboardingPaths } from 'routes/Onboarding/paths' +import { APP_NAME } from '@src/lib/constants' +import { onboardingPaths } from '@src/routes/Onboarding/paths' + +import { OnboardingButtons } from '@src/routes/Onboarding/utils' export default function Export() { return ( diff --git a/src/routes/Onboarding/FutureWork.tsx b/src/routes/Onboarding/FutureWork.tsx index 4be5e0fce..fa2756adf 100644 --- a/src/routes/Onboarding/FutureWork.tsx +++ b/src/routes/Onboarding/FutureWork.tsx @@ -1,9 +1,11 @@ -import { OnboardingButtons, useDemoCode } from '.' import { useEffect } from 'react' -import { useModelingContext } from 'hooks/useModelingContext' -import { APP_NAME } from 'lib/constants' -import { onboardingPaths } from './paths' -import { sceneInfra } from 'lib/singletons' + +import { useModelingContext } from '@src/hooks/useModelingContext' +import { APP_NAME } from '@src/lib/constants' +import { sceneInfra } from '@src/lib/singletons' +import { onboardingPaths } from '@src/routes/Onboarding/paths' + +import { OnboardingButtons, useDemoCode } from '@src/routes/Onboarding/utils' export default function FutureWork() { const { send } = useModelingContext() diff --git a/src/routes/Onboarding/InteractiveNumbers.tsx b/src/routes/Onboarding/InteractiveNumbers.tsx index d8a6cb0c5..0b9017f8d 100644 --- a/src/routes/Onboarding/InteractiveNumbers.tsx +++ b/src/routes/Onboarding/InteractiveNumbers.tsx @@ -1,6 +1,11 @@ -import { OnboardingButtons, kbdClasses, useDemoCode } from '.' -import { onboardingPaths } from 'routes/Onboarding/paths' -import { bracketWidthConstantLine } from 'lib/exampleKcl' +import { bracketWidthConstantLine } from '@src/lib/exampleKcl' +import { onboardingPaths } from '@src/routes/Onboarding/paths' + +import { + kbdClasses, + OnboardingButtons, + useDemoCode, +} from '@src/routes/Onboarding/utils' export default function OnboardingInteractiveNumbers() { useDemoCode() diff --git a/src/routes/Onboarding/Introduction.tsx b/src/routes/Onboarding/Introduction.tsx index c3103d12c..860a0ca13 100644 --- a/src/routes/Onboarding/Introduction.tsx +++ b/src/routes/Onboarding/Introduction.tsx @@ -1,20 +1,22 @@ -import { OnboardingButtons, useDemoCode } from '.' -import { onboardingPaths } from 'routes/Onboarding/paths' -import { Themes, getSystemTheme } from 'lib/theme' -import { bracket } from 'lib/exampleKcl' -import { createAndOpenNewTutorialProject } from 'lib/desktopFS' -import { isDesktop } from 'lib/isDesktop' -import { useNavigate, useRouteLoaderData } from 'react-router-dom' -import { codeManager, kclManager } from 'lib/singletons' -import { APP_NAME } from 'lib/constants' import { useEffect, useState } from 'react' -import { IndexLoaderData } from 'lib/types' -import { PATHS } from 'lib/paths' -import { useFileContext } from 'hooks/useFileContext' -import { useLspContext } from 'components/LspProvider' -import { reportRejection } from 'lib/trap' -import { useSettings } from 'machines/appMachine' -import { isKclEmptyOrOnlySettings } from 'lang/wasm' +import { useNavigate, useRouteLoaderData } from 'react-router-dom' + +import { useLspContext } from '@src/components/LspProvider' +import { useFileContext } from '@src/hooks/useFileContext' +import { isKclEmptyOrOnlySettings } from '@src/lang/wasm' +import { APP_NAME } from '@src/lib/constants' +import { createAndOpenNewTutorialProject } from '@src/lib/desktopFS' +import { bracket } from '@src/lib/exampleKcl' +import { isDesktop } from '@src/lib/isDesktop' +import { PATHS } from '@src/lib/paths' +import { codeManager, kclManager } from '@src/lib/singletons' +import { Themes, getSystemTheme } from '@src/lib/theme' +import { reportRejection } from '@src/lib/trap' +import type { IndexLoaderData } from '@src/lib/types' +import { useSettings } from '@src/machines/appMachine' +import { onboardingPaths } from '@src/routes/Onboarding/paths' + +import { OnboardingButtons, useDemoCode } from '@src/routes/Onboarding/utils' /** * Show either a welcome screen or a warning screen diff --git a/src/routes/Onboarding/ParametricModeling.tsx b/src/routes/Onboarding/ParametricModeling.tsx index ae13f1ef1..4360b5067 100644 --- a/src/routes/Onboarding/ParametricModeling.tsx +++ b/src/routes/Onboarding/ParametricModeling.tsx @@ -1,9 +1,10 @@ -import { OnboardingButtons, useDemoCode } from '.' -import { onboardingPaths } from 'routes/Onboarding/paths' -import { Themes, getSystemTheme } from 'lib/theme' -import { bracketThicknessCalculationLine } from 'lib/exampleKcl' -import { isDesktop } from 'lib/isDesktop' -import { useSettings } from 'machines/appMachine' +import { bracketThicknessCalculationLine } from '@src/lib/exampleKcl' +import { isDesktop } from '@src/lib/isDesktop' +import { Themes, getSystemTheme } from '@src/lib/theme' +import { useSettings } from '@src/machines/appMachine' +import { onboardingPaths } from '@src/routes/Onboarding/paths' + +import { OnboardingButtons, useDemoCode } from '@src/routes/Onboarding/utils' export default function OnboardingParametricModeling() { useDemoCode() diff --git a/src/routes/Onboarding/ProjectMenu.tsx b/src/routes/Onboarding/ProjectMenu.tsx index db02afc26..6e2ebe976 100644 --- a/src/routes/Onboarding/ProjectMenu.tsx +++ b/src/routes/Onboarding/ProjectMenu.tsx @@ -1,6 +1,7 @@ -import { OnboardingButtons } from '.' -import { onboardingPaths } from 'routes/Onboarding/paths' -import { isDesktop } from 'lib/isDesktop' +import { isDesktop } from '@src/lib/isDesktop' +import { onboardingPaths } from '@src/routes/Onboarding/paths' + +import { OnboardingButtons } from '@src/routes/Onboarding/utils' export default function ProjectMenu() { const onDesktop = isDesktop() diff --git a/src/routes/Onboarding/Sketching.tsx b/src/routes/Onboarding/Sketching.tsx index 7129ee196..b3cf1bc12 100644 --- a/src/routes/Onboarding/Sketching.tsx +++ b/src/routes/Onboarding/Sketching.tsx @@ -1,7 +1,9 @@ -import { OnboardingButtons } from '.' -import { onboardingPaths } from 'routes/Onboarding/paths' import { useEffect } from 'react' -import { codeManager, kclManager } from 'lib/singletons' + +import { codeManager, kclManager } from '@src/lib/singletons' +import { onboardingPaths } from '@src/routes/Onboarding/paths' + +import { OnboardingButtons } from '@src/routes/Onboarding/utils' export default function Sketching() { useEffect(() => { diff --git a/src/routes/Onboarding/Streaming.tsx b/src/routes/Onboarding/Streaming.tsx index bd0028152..4dfec2e72 100644 --- a/src/routes/Onboarding/Streaming.tsx +++ b/src/routes/Onboarding/Streaming.tsx @@ -1,5 +1,6 @@ -import { OnboardingButtons } from '.' -import { onboardingPaths } from 'routes/Onboarding/paths' +import { onboardingPaths } from '@src/routes/Onboarding/paths' + +import { OnboardingButtons } from '@src/routes/Onboarding/utils' export default function Streaming() { return ( diff --git a/src/routes/Onboarding/Units.tsx b/src/routes/Onboarding/Units.tsx index 7ec94ac34..82e16235b 100644 --- a/src/routes/Onboarding/Units.tsx +++ b/src/routes/Onboarding/Units.tsx @@ -1,10 +1,12 @@ import { faArrowRight, faXmark } from '@fortawesome/free-solid-svg-icons' -import { type BaseUnit, baseUnitsUnion } from 'lib/settings/settingsTypes' -import { ActionButton } from 'components/ActionButton' -import { SettingsSection } from 'components/Settings/SettingsSection' -import { useDismiss, useNextClick } from '.' -import { onboardingPaths } from 'routes/Onboarding/paths' -import { settingsActor, useSettings } from 'machines/appMachine' + +import { ActionButton } from '@src/components/ActionButton' +import { SettingsSection } from '@src/components/Settings/SettingsSection' +import { type BaseUnit, baseUnitsUnion } from '@src/lib/settings/settingsTypes' +import { settingsActor, useSettings } from '@src/machines/appMachine' +import { onboardingPaths } from '@src/routes/Onboarding/paths' + +import { useDismiss, useNextClick } from '@src/routes/Onboarding/utils' export default function Units() { const dismiss = useDismiss() diff --git a/src/routes/Onboarding/UserMenu.tsx b/src/routes/Onboarding/UserMenu.tsx index ca2448c1b..30849812a 100644 --- a/src/routes/Onboarding/UserMenu.tsx +++ b/src/routes/Onboarding/UserMenu.tsx @@ -1,7 +1,9 @@ -import { OnboardingButtons } from '.' -import { onboardingPaths } from 'routes/Onboarding/paths' import { useEffect, useState } from 'react' -import { useUser } from 'machines/appMachine' + +import { useUser } from '@src/machines/appMachine' +import { onboardingPaths } from '@src/routes/Onboarding/paths' + +import { OnboardingButtons } from '@src/routes/Onboarding/utils' export default function UserMenu() { const user = useUser() diff --git a/src/routes/Onboarding/index.tsx b/src/routes/Onboarding/index.tsx index c867e74fc..1652d70ac 100644 --- a/src/routes/Onboarding/index.tsx +++ b/src/routes/Onboarding/index.tsx @@ -1,37 +1,21 @@ import { useHotkeys } from 'react-hotkeys-hook' -import { Outlet, useNavigate } from 'react-router-dom' -import Introduction from './Introduction' -import Camera from './Camera' -import Sketching from './Sketching' -import { useCallback, useEffect } from 'react' -import makeUrlPathRelative from '../../lib/makeUrlPathRelative' -import Streaming from './Streaming' -import CodeEditor from './CodeEditor' -import ParametricModeling from './ParametricModeling' -import InteractiveNumbers from './InteractiveNumbers' -import CmdK from './CmdK' -import UserMenu from './UserMenu' -import ProjectMenu from './ProjectMenu' -import Export from './Export' -import FutureWork from './FutureWork' -import { PATHS } from 'lib/paths' -import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath' -import { ActionButton } from 'components/ActionButton' -import { onboardingPaths } from 'routes/Onboarding/paths' -import { codeManager, editorManager, kclManager } from 'lib/singletons' -import { bracket } from 'lib/exampleKcl' -import { toSync } from 'lib/utils' -import { reportRejection } from 'lib/trap' -import { useNetworkContext } from 'hooks/useNetworkContext' -import { NetworkHealthState } from 'hooks/useNetworkStatus' -import { EngineConnectionStateType } from 'lang/std/engineConnection' -import { settingsActor } from 'machines/appMachine' -import { CustomIcon } from 'components/CustomIcon' -import Tooltip from 'components/Tooltip' -import { waitFor } from 'xstate' +import { Outlet } from 'react-router-dom' -export const kbdClasses = - 'py-0.5 px-1 text-sm rounded bg-chalkboard-10 dark:bg-chalkboard-100 border border-chalkboard-50 border-b-2' +import makeUrlPathRelative from '@src/lib/makeUrlPathRelative' +import Camera from '@src/routes/Onboarding/Camera' +import CmdK from '@src/routes/Onboarding/CmdK' +import CodeEditor from '@src/routes/Onboarding/CodeEditor' +import Export from '@src/routes/Onboarding/Export' +import FutureWork from '@src/routes/Onboarding/FutureWork' +import InteractiveNumbers from '@src/routes/Onboarding/InteractiveNumbers' +import Introduction from '@src/routes/Onboarding/Introduction' +import ParametricModeling from '@src/routes/Onboarding/ParametricModeling' +import ProjectMenu from '@src/routes/Onboarding/ProjectMenu' +import Sketching from '@src/routes/Onboarding/Sketching' +import Streaming from '@src/routes/Onboarding/Streaming' +import UserMenu from '@src/routes/Onboarding/UserMenu' +import { onboardingPaths } from '@src/routes/Onboarding/paths' +import { useDismiss } from '@src/routes/Onboarding/utils' export const onboardingRoutes = [ { @@ -85,168 +69,6 @@ export const onboardingRoutes = [ }, ] -export function useDemoCode() { - const { overallState, immediateState } = useNetworkContext() - - useEffect(() => { - // Don't run if the editor isn't loaded or the code is already the bracket - if (!editorManager.editorView || codeManager.code === bracket) { - return - } - // Don't run if the network isn't healthy or the connection isn't established - if ( - overallState !== NetworkHealthState.Ok || - immediateState.type !== EngineConnectionStateType.ConnectionEstablished - ) { - return - } - setTimeout( - toSync(async () => { - codeManager.updateCodeStateEditor(bracket) - await kclManager.executeCode(true) - await codeManager.writeToFile() - }, reportRejection) - ) - }, [editorManager.editorView, immediateState, overallState]) -} - -export function useNextClick(newStatus: string) { - const filePath = useAbsoluteFilePath() - const navigate = useNavigate() - - return useCallback(() => { - settingsActor.send({ - type: 'set.app.onboardingStatus', - data: { level: 'user', value: newStatus }, - }) - navigate(filePath + PATHS.ONBOARDING.INDEX.slice(0, -1) + newStatus) - }, [filePath, newStatus, settingsActor.send, navigate]) -} - -export function useDismiss() { - const filePath = useAbsoluteFilePath() - const send = settingsActor.send - const navigate = useNavigate() - - const settingsCallback = useCallback(() => { - send({ - type: 'set.app.onboardingStatus', - data: { level: 'user', value: 'dismissed' }, - }) - waitFor(settingsActor, (state) => state.matches('idle')) - .then(() => navigate(filePath)) - .catch(reportRejection) - }, [send]) - - return settingsCallback -} - -// Get the 1-indexed step number of the current onboarding step -export function useStepNumber( - slug?: (typeof onboardingPaths)[keyof typeof onboardingPaths] -) { - return slug - ? slug === onboardingPaths.INDEX - ? 1 - : onboardingRoutes.findIndex( - (r) => r.path === makeUrlPathRelative(slug) - ) + 1 - : 1 -} - -export function OnboardingButtons({ - currentSlug, - className, - dismissClassName, - onNextOverride, - ...props -}: { - currentSlug?: (typeof onboardingPaths)[keyof typeof onboardingPaths] - className?: string - dismissClassName?: string - onNextOverride?: () => void -} & React.HTMLAttributes) { - const dismiss = useDismiss() - const stepNumber = useStepNumber(currentSlug) - const previousStep = - !stepNumber || stepNumber === 0 ? null : onboardingRoutes[stepNumber - 2] - const goToPrevious = useNextClick( - onboardingPaths.INDEX + (previousStep?.path ?? '') - ) - const nextStep = - !stepNumber || stepNumber === onboardingRoutes.length - ? null - : onboardingRoutes[stepNumber] - const goToNext = useNextClick(onboardingPaths.INDEX + (nextStep?.path ?? '')) - - return ( - <> - -
- - previousStep?.path || previousStep?.index - ? goToPrevious() - : dismiss() - } - iconStart={{ - icon: previousStep ? 'arrowLeft' : 'close', - className: 'text-chalkboard-10', - bgClassName: 'bg-destroy-80 group-hover:bg-destroy-80', - }} - className="hover:border-destroy-40 hover:bg-destroy-10/50 dark:hover:bg-destroy-80/50" - data-testid="onboarding-prev" - > - {previousStep ? `Back` : 'Dismiss'} - - {stepNumber !== undefined && ( -

- {stepNumber} / {onboardingRoutes.length} -

- )} - { - if (nextStep?.path) { - onNextOverride ? onNextOverride() : goToNext() - } else { - dismiss() - } - }} - iconStart={{ - icon: nextStep ? 'arrowRight' : 'checkmark', - bgClassName: 'dark:bg-chalkboard-80', - }} - className="dark:hover:bg-chalkboard-80/50" - data-testid="onboarding-next" - > - {nextStep ? `Next` : 'Finish'} - -
- - ) -} - const Onboarding = () => { const dismiss = useDismiss() useHotkeys('esc', dismiss) diff --git a/src/routes/Onboarding/paths.ts b/src/routes/Onboarding/paths.ts index 8e92107a6..f8a069331 100644 --- a/src/routes/Onboarding/paths.ts +++ b/src/routes/Onboarding/paths.ts @@ -1,4 +1,4 @@ -import { OnboardingStatus } from '@rust/kcl-lib/bindings/OnboardingStatus' +import type { OnboardingStatus } from '@rust/kcl-lib/bindings/OnboardingStatus' export const onboardingPaths: Record = { INDEX: '/', diff --git a/src/routes/Onboarding/utils.tsx b/src/routes/Onboarding/utils.tsx new file mode 100644 index 000000000..1536390cf --- /dev/null +++ b/src/routes/Onboarding/utils.tsx @@ -0,0 +1,184 @@ +import { useCallback, useEffect } from 'react' +import { useNavigate } from 'react-router-dom' +import { waitFor } from 'xstate' + +import { ActionButton } from '@src/components/ActionButton' +import { CustomIcon } from '@src/components/CustomIcon' +import Tooltip from '@src/components/Tooltip' +import { useAbsoluteFilePath } from '@src/hooks/useAbsoluteFilePath' +import { useNetworkContext } from '@src/hooks/useNetworkContext' +import { NetworkHealthState } from '@src/hooks/useNetworkStatus' +import { EngineConnectionStateType } from '@src/lang/std/engineConnection' +import { bracket } from '@src/lib/exampleKcl' +import makeUrlPathRelative from '@src/lib/makeUrlPathRelative' +import { PATHS } from '@src/lib/paths' +import { codeManager, editorManager, kclManager } from '@src/lib/singletons' +import { reportRejection } from '@src/lib/trap' +import { toSync } from '@src/lib/utils' +import { settingsActor } from '@src/machines/appMachine' +import { onboardingRoutes } from '@src/routes/Onboarding' +import { onboardingPaths } from '@src/routes/Onboarding/paths' + +export const kbdClasses = + 'py-0.5 px-1 text-sm rounded bg-chalkboard-10 dark:bg-chalkboard-100 border border-chalkboard-50 border-b-2' + +// Get the 1-indexed step number of the current onboarding step +function useStepNumber( + slug?: (typeof onboardingPaths)[keyof typeof onboardingPaths] +) { + return slug + ? slug === onboardingPaths.INDEX + ? 1 + : onboardingRoutes.findIndex( + (r) => r.path === makeUrlPathRelative(slug) + ) + 1 + : 1 +} + +export function useDemoCode() { + const { overallState, immediateState } = useNetworkContext() + + useEffect(() => { + // Don't run if the editor isn't loaded or the code is already the bracket + if (!editorManager.editorView || codeManager.code === bracket) { + return + } + // Don't run if the network isn't healthy or the connection isn't established + if ( + overallState !== NetworkHealthState.Ok || + immediateState.type !== EngineConnectionStateType.ConnectionEstablished + ) { + return + } + setTimeout( + toSync(async () => { + codeManager.updateCodeStateEditor(bracket) + await kclManager.executeCode(true) + await codeManager.writeToFile() + }, reportRejection) + ) + }, [editorManager.editorView, immediateState, overallState]) +} + +export function useNextClick(newStatus: string) { + const filePath = useAbsoluteFilePath() + const navigate = useNavigate() + + return useCallback(() => { + settingsActor.send({ + type: 'set.app.onboardingStatus', + data: { level: 'user', value: newStatus }, + }) + navigate(filePath + PATHS.ONBOARDING.INDEX.slice(0, -1) + newStatus) + }, [filePath, newStatus, settingsActor.send, navigate]) +} + +export function useDismiss() { + const filePath = useAbsoluteFilePath() + const send = settingsActor.send + const navigate = useNavigate() + + const settingsCallback = useCallback(() => { + send({ + type: 'set.app.onboardingStatus', + data: { level: 'user', value: 'dismissed' }, + }) + waitFor(settingsActor, (state) => state.matches('idle')) + .then(() => navigate(filePath)) + .catch(reportRejection) + }, [send]) + + return settingsCallback +} +export function OnboardingButtons({ + currentSlug, + className, + dismissClassName, + onNextOverride, + ...props +}: { + currentSlug?: (typeof onboardingPaths)[keyof typeof onboardingPaths] + className?: string + dismissClassName?: string + onNextOverride?: () => void +} & React.HTMLAttributes) { + const dismiss = useDismiss() + const stepNumber = useStepNumber(currentSlug) + const previousStep = + !stepNumber || stepNumber === 0 ? null : onboardingRoutes[stepNumber - 2] + const goToPrevious = useNextClick( + onboardingPaths.INDEX + (previousStep?.path ?? '') + ) + const nextStep = + !stepNumber || stepNumber === onboardingRoutes.length + ? null + : onboardingRoutes[stepNumber] + const goToNext = useNextClick(onboardingPaths.INDEX + (nextStep?.path ?? '')) + + return ( + <> + +
+ + previousStep?.path || previousStep?.index + ? goToPrevious() + : dismiss() + } + iconStart={{ + icon: previousStep ? 'arrowLeft' : 'close', + className: 'text-chalkboard-10', + bgClassName: 'bg-destroy-80 group-hover:bg-destroy-80', + }} + className="hover:border-destroy-40 hover:bg-destroy-10/50 dark:hover:bg-destroy-80/50" + data-testid="onboarding-prev" + > + {previousStep ? `Back` : 'Dismiss'} + + {stepNumber !== undefined && ( +

+ {stepNumber} / {onboardingRoutes.length} +

+ )} + { + if (nextStep?.path) { + onNextOverride ? onNextOverride() : goToNext() + } else { + dismiss() + } + }} + iconStart={{ + icon: nextStep ? 'arrowRight' : 'checkmark', + bgClassName: 'dark:bg-chalkboard-80', + }} + className="dark:hover:bg-chalkboard-80/50" + data-testid="onboarding-next" + > + {nextStep ? `Next` : 'Finish'} + +
+ + ) +} diff --git a/src/routes/Settings.tsx b/src/routes/Settings.tsx index 9ce3af3c1..287f8340f 100644 --- a/src/routes/Settings.tsx +++ b/src/routes/Settings.tsx @@ -1,20 +1,22 @@ -import { SettingsLevel } from 'lib/settings/settingsTypes' -import { useLocation, useNavigate, useSearchParams } from 'react-router-dom' -import { useHotkeys } from 'react-hotkeys-hook' -import { PATHS } from 'lib/paths' -import { useDotDotSlash } from 'hooks/useDotDotSlash' -import { Fragment, useEffect, useRef } from 'react' import { Dialog, Transition } from '@headlessui/react' -import { CustomIcon } from 'components/CustomIcon' -import { SettingsSearchBar } from 'components/Settings/SettingsSearchBar' -import { SettingsTabs } from 'components/Settings/SettingsTabs' -import { SettingsSectionsList } from 'components/Settings/SettingsSectionsList' -import { AllSettingsFields } from 'components/Settings/AllSettingsFields' -import { AllKeybindingsFields } from 'components/Settings/AllKeybindingsFields' -import { KeybindingsSectionsList } from 'components/Settings/KeybindingsSectionsList' -import { isDesktop } from 'lib/isDesktop' -import { IS_PLAYWRIGHT_KEY } from '../../e2e/playwright/storageStates' -import { NODE_ENV } from 'env' +import { NODE_ENV } from '@src/env' +import { Fragment, useEffect, useRef } from 'react' +import { useHotkeys } from 'react-hotkeys-hook' +import { useLocation, useNavigate, useSearchParams } from 'react-router-dom' + +import { IS_PLAYWRIGHT_KEY } from '@e2e/playwright/storageStates' + +import { CustomIcon } from '@src/components/CustomIcon' +import { AllKeybindingsFields } from '@src/components/Settings/AllKeybindingsFields' +import { AllSettingsFields } from '@src/components/Settings/AllSettingsFields' +import { KeybindingsSectionsList } from '@src/components/Settings/KeybindingsSectionsList' +import { SettingsSearchBar } from '@src/components/Settings/SettingsSearchBar' +import { SettingsSectionsList } from '@src/components/Settings/SettingsSectionsList' +import { SettingsTabs } from '@src/components/Settings/SettingsTabs' +import { useDotDotSlash } from '@src/hooks/useDotDotSlash' +import { isDesktop } from '@src/lib/isDesktop' +import { PATHS } from '@src/lib/paths' +import type { SettingsLevel } from '@src/lib/settings/settingsTypes' const isTestEnv = window?.localStorage.getItem(IS_PLAYWRIGHT_KEY) === 'true' @@ -22,9 +24,9 @@ export const APP_VERSION = isTestEnv && NODE_ENV === 'development' ? '11.22.33' : isDesktop() - ? // @ts-ignore - window.electron.packageJson.version - : 'main' + ? // @ts-ignore + window.electron.packageJson.version + : 'main' export const PACKAGE_NAME = isDesktop() ? window.electron.packageJson.name diff --git a/src/routes/SignIn.tsx b/src/routes/SignIn.tsx index 45c711d12..68f9e5047 100644 --- a/src/routes/SignIn.tsx +++ b/src/routes/SignIn.tsx @@ -1,20 +1,21 @@ -import { ActionButton } from '../components/ActionButton' -import { isDesktop } from '../lib/isDesktop' -import { VITE_KC_SITE_BASE_URL, VITE_KC_API_BASE_URL } from '../env' -import { Themes, getSystemTheme } from '../lib/theme' -import { PATHS } from 'lib/paths' -import { APP_NAME } from 'lib/constants' -import { CSSProperties, useCallback, useState } from 'react' -import { Logo } from 'components/Logo' -import { CustomIcon } from 'components/CustomIcon' -import { Link } from 'react-router-dom' -import { APP_VERSION } from './Settings' -import { openExternalBrowserIfDesktop } from 'lib/openWindow' -import { toSync } from 'lib/utils' -import { reportRejection } from 'lib/trap' +import type { CSSProperties } from 'react' +import { useCallback, useState } from 'react' import toast from 'react-hot-toast' -import { authActor } from 'machines/appMachine' -import { useSettings } from 'machines/appMachine' +import { Link } from 'react-router-dom' + +import { ActionButton } from '@src/components/ActionButton' +import { CustomIcon } from '@src/components/CustomIcon' +import { Logo } from '@src/components/Logo' +import { VITE_KC_API_BASE_URL, VITE_KC_SITE_BASE_URL } from '@src/env' +import { APP_NAME } from '@src/lib/constants' +import { isDesktop } from '@src/lib/isDesktop' +import { openExternalBrowserIfDesktop } from '@src/lib/openWindow' +import { PATHS } from '@src/lib/paths' +import { Themes, getSystemTheme } from '@src/lib/theme' +import { reportRejection } from '@src/lib/trap' +import { toSync } from '@src/lib/utils' +import { authActor, useSettings } from '@src/machines/appMachine' +import { APP_VERSION } from '@src/routes/Settings' const subtleBorder = 'border border-solid border-chalkboard-30 dark:border-chalkboard-80' @@ -48,8 +49,8 @@ const SignIn = () => { ? '-dark' : '' : shouldContrast - ? '' - : '-dark', + ? '' + : '-dark', [theme.current] ) diff --git a/src/routes/Telemetry.tsx b/src/routes/Telemetry.tsx index a5cd91b0d..a0e5aed96 100644 --- a/src/routes/Telemetry.tsx +++ b/src/routes/Telemetry.tsx @@ -1,11 +1,12 @@ -import { useLocation, useNavigate } from 'react-router-dom' -import { useHotkeys } from 'react-hotkeys-hook' -import { PATHS } from 'lib/paths' -import { useDotDotSlash } from 'hooks/useDotDotSlash' -import { Fragment } from 'react' import { Dialog, Transition } from '@headlessui/react' -import { CustomIcon } from 'components/CustomIcon' -import { TelemetryExplorer } from 'components/TelemetryExplorer' +import { Fragment } from 'react' +import { useHotkeys } from 'react-hotkeys-hook' +import { useLocation, useNavigate } from 'react-router-dom' + +import { CustomIcon } from '@src/components/CustomIcon' +import { TelemetryExplorer } from '@src/components/TelemetryExplorer' +import { useDotDotSlash } from '@src/hooks/useDotDotSlash' +import { PATHS } from '@src/lib/paths' export const Telemetry = () => { const navigate = useNavigate() diff --git a/src/setupTests.ts b/src/setupTests.ts index 2c9bd3b0f..bccb50d2d 100644 --- a/src/setupTests.ts +++ b/src/setupTests.ts @@ -1,9 +1,8 @@ import '@testing-library/jest-dom' -import { WebSocket } from 'ws' +import fetch from 'node-fetch' import { vi } from 'vitest' import 'vitest-webgl-canvas-mock' - -import fetch from 'node-fetch' +import { WebSocket } from 'ws' // @ts-ignore globalThis.fetch = fetch diff --git a/src/test-utils.test.ts b/src/test-utils.test.ts index 9f0266e9c..245ee24f5 100644 --- a/src/test-utils.test.ts +++ b/src/test-utils.test.ts @@ -1,4 +1,4 @@ -import { normaliseKclNumbers } from '../e2e/playwright/test-utils' +import { normaliseKclNumbers } from '@e2e/playwright/test-utils' test('normaliseKclNumbers', () => { expect( diff --git a/tsconfig.json b/tsconfig.json index 4dd68fb00..d379580d9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,16 +1,17 @@ { "compilerOptions": { - "baseUrl": "src", "noErrorTruncation": true, "paths": { "@kittycad/codemirror-lsp-client": [ - "../packages/codemirror-lsp-client/src/index.ts" + "./packages/codemirror-lsp-client/src/index.ts" ], "@kittycad/codemirror-lang-kcl": [ - "../packages/codemirror-lang-kcl/src/index.ts" + "./packages/codemirror-lang-kcl/src/index.ts" ], - "@rust/*": ["../rust/*"], - "/*": ["src/*"] + "@rust/*": ["./rust/*"], + "@e2e/*": ["./e2e/*"], + "@src/*": ["./src/*"], + "@root/*": ["./*"] }, "types": [ "vite/client", diff --git a/vite.base.config.ts b/vite.base.config.ts index fd213f2df..515e959ee 100644 --- a/vite.base.config.ts +++ b/vite.base.config.ts @@ -1,6 +1,7 @@ import { builtinModules } from 'node:module' import type { AddressInfo } from 'node:net' import type { ConfigEnv, Plugin, UserConfig } from 'vite' + import pkg from './package.json' export const builtins = [ @@ -59,17 +60,20 @@ export function getBuildDefine(env: ConfigEnv<'build'>) { .filter(({ name }) => name != null) .map(({ name }) => name!) const defineKeys = getDefineKeys(names) - const define = Object.entries(defineKeys).reduce((acc, [name, keys]) => { - const { VITE_DEV_SERVER_URL, VITE_NAME } = keys - const def = { - [VITE_DEV_SERVER_URL]: - command === 'serve' - ? JSON.stringify(process.env[VITE_DEV_SERVER_URL]) - : undefined, - [VITE_NAME]: JSON.stringify(name), - } - return { ...acc, ...def } - }, {} as Record) + const define = Object.entries(defineKeys).reduce( + (acc, [name, keys]) => { + const { VITE_DEV_SERVER_URL, VITE_NAME } = keys + const def = { + [VITE_DEV_SERVER_URL]: + command === 'serve' + ? JSON.stringify(process.env[VITE_DEV_SERVER_URL]) + : undefined, + [VITE_NAME]: JSON.stringify(name), + } + return { ...acc, ...def } + }, + {} as Record + ) return define } @@ -87,9 +91,8 @@ export function pluginExposeRenderer(name: string): Plugin { server.httpServer?.once('listening', () => { const addressInfo = server.httpServer!.address() as AddressInfo // Expose env constant for main process use. - process.env[ - VITE_DEV_SERVER_URL - ] = `http://localhost:${addressInfo?.port}` + process.env[VITE_DEV_SERVER_URL] = + `http://localhost:${addressInfo?.port}` }) }, } diff --git a/vite.config.ts b/vite.config.ts index d4f918055..beef0bbb4 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,11 +1,11 @@ -import react from '@vitejs/plugin-react' -import viteTsconfigPaths from 'vite-tsconfig-paths' -import eslint from '@nabla/vite-plugin-eslint' -import { defineConfig, configDefaults } from 'vitest/config' -import version from 'vite-plugin-package-version' -import topLevelAwait from 'vite-plugin-top-level-await' // @ts-ignore: No types available import { lezer } from '@lezer/generator/rollup' +import eslint from '@nabla/vite-plugin-eslint' +import react from '@vitejs/plugin-react' +import version from 'vite-plugin-package-version' +import topLevelAwait from 'vite-plugin-top-level-await' +import viteTsconfigPaths from 'vite-tsconfig-paths' +import { configDefaults, defineConfig } from 'vitest/config' const config = defineConfig({ server: { @@ -63,6 +63,9 @@ const config = defineConfig({ '@kittycad/codemirror-lsp-client': '/packages/codemirror-lsp-client/src', '@kittycad/codemirror-lang-kcl': '/packages/codemirror-lang-kcl/src', '@rust': '/rust', + '@e2e': '/e2e', + '@src': '/src', + '@root': '/', }, }, plugins: [ diff --git a/vite.main.config.ts b/vite.main.config.ts index 89645e874..9890813e6 100644 --- a/vite.main.config.ts +++ b/vite.main.config.ts @@ -1,18 +1,19 @@ -import type { ConfigEnv, UserConfig } from 'vite' -import { defineConfig, mergeConfig } from 'vite' -import { configDefaults } from 'vitest/config' -import viteTsconfigPaths from 'vite-tsconfig-paths' -import vitePluginEslint from '@nabla/vite-plugin-eslint' -import vitePluginPackageVersion from 'vite-plugin-package-version' -import { - getBuildConfig, - getBuildDefine, - external, - pluginHotRestart, -} from './vite.base.config' -import viteJsPluginReact from '@vitejs/plugin-react' // @ts-ignore: No types available import { lezer } from '@lezer/generator/rollup' +import vitePluginEslint from '@nabla/vite-plugin-eslint' +import viteJsPluginReact from '@vitejs/plugin-react' +import type { ConfigEnv, UserConfig } from 'vite' +import { defineConfig, mergeConfig } from 'vite' +import vitePluginPackageVersion from 'vite-plugin-package-version' +import viteTsconfigPaths from 'vite-tsconfig-paths' +import { configDefaults } from 'vitest/config' + +import { + external, + getBuildConfig, + getBuildDefine, + pluginHotRestart, +} from './vite.base.config' // https://vitejs.dev/config export default defineConfig((env) => { diff --git a/vite.preload.config.ts b/vite.preload.config.ts index c3a78a01a..fa58bbcfc 100644 --- a/vite.preload.config.ts +++ b/vite.preload.config.ts @@ -1,11 +1,11 @@ import type { ConfigEnv, UserConfig } from 'vite' import { defineConfig, mergeConfig } from 'vite' -import { configDefaults } from 'vitest/config' import viteTsconfigPaths from 'vite-tsconfig-paths' + import { + external, getBuildConfig, getBuildDefine, - external, pluginHotRestart, } from './vite.base.config' diff --git a/vite.renderer.config.ts b/vite.renderer.config.ts index eaf0eb049..6c6d3c1ea 100644 --- a/vite.renderer.config.ts +++ b/vite.renderer.config.ts @@ -1,10 +1,11 @@ -import type { ConfigEnv, UserConfig } from 'vite' -import { defineConfig } from 'vite' -import { pluginExposeRenderer } from './vite.base.config' -import viteTsconfigPaths from 'vite-tsconfig-paths' -import topLevelAwait from 'vite-plugin-top-level-await' // @ts-ignore: No types available import { lezer } from '@lezer/generator/rollup' +import type { ConfigEnv, UserConfig } from 'vite' +import { defineConfig } from 'vite' +import topLevelAwait from 'vite-plugin-top-level-await' +import viteTsconfigPaths from 'vite-tsconfig-paths' + +import { pluginExposeRenderer } from './vite.base.config' // https://vitejs.dev/config export default defineConfig((env) => { diff --git a/yarn.lock b/yarn.lock index 102b37a20..4a1d47041 100644 --- a/yarn.lock +++ b/yarn.lock @@ -89,7 +89,7 @@ "@jridgewell/trace-mapping" "^0.3.25" jsesc "^3.0.2" -"@babel/generator@^7.27.0": +"@babel/generator@^7.26.5", "@babel/generator@^7.27.0": version "7.27.0" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.27.0.tgz#764382b5392e5b9aff93cadb190d0745866cbc2c" integrity sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw== @@ -352,7 +352,7 @@ dependencies: "@babel/types" "^7.26.10" -"@babel/parser@^7.27.0": +"@babel/parser@^7.26.7", "@babel/parser@^7.27.0": version "7.27.0" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.27.0.tgz#3d7d6ee268e41d2600091cbd4e145ffee85a44ec" integrity sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg== @@ -992,7 +992,7 @@ debug "^4.3.1" globals "^11.1.0" -"@babel/traverse@^7.26.10": +"@babel/traverse@^7.26.10", "@babel/traverse@^7.26.7": version "7.27.0" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.27.0.tgz#11d7e644779e166c0442f9a07274d02cd91d4a70" integrity sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA== @@ -1022,7 +1022,7 @@ "@babel/helper-string-parser" "^7.25.9" "@babel/helper-validator-identifier" "^7.25.9" -"@babel/types@^7.27.0": +"@babel/types@^7.26.7", "@babel/types@^7.27.0": version "7.27.0" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.27.0.tgz#ef9acb6b06c3173f6632d993ecb6d4ae470b4559" integrity sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg== @@ -1866,10 +1866,10 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" -"@kittycad/lib@2.0.23": - version "2.0.23" - resolved "https://registry.yarnpkg.com/@kittycad/lib/-/lib-2.0.23.tgz#0d215d458b35f6d207eeb90443889fa77b21b913" - integrity sha512-5T7+gHB21RX5bE7ILp3TnLzp0rA7CP1BucNctHynANG/sXV44tD7U8YEcQsi+/ZmMkvrxmZ/3r/UQjgzhQUh7w== +"@kittycad/lib@2.0.25": + version "2.0.25" + resolved "https://registry.yarnpkg.com/@kittycad/lib/-/lib-2.0.25.tgz#ad3e9a548752440b18102ca9b83e10811c2c08ab" + integrity sha512-Qw5veBEX37lOfdg93OiSKFcTC+3y5q3hcfjML53BbRwE7bzwE/PlPFAouqqnts4a9PEETHxeO1CsKe3YUW+ysA== dependencies: openapi-types "^12.0.0" ts-node "^10.9.1" @@ -2275,6 +2275,18 @@ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== +"@trivago/prettier-plugin-sort-imports@^5.2.2": + version "5.2.2" + resolved "https://registry.yarnpkg.com/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-5.2.2.tgz#38983f0b83490a0a7d974a6f1e409fb4bf678d02" + integrity sha512-fYDQA9e6yTNmA13TLVSA+WMQRc5Bn/c0EUBditUHNfMMxN7M82c38b1kEggVE3pLpZ0FwkwJkUEKMiOi52JXFA== + dependencies: + "@babel/generator" "^7.26.5" + "@babel/parser" "^7.26.7" + "@babel/traverse" "^7.26.7" + "@babel/types" "^7.26.7" + javascript-natural-sort "^0.7.1" + lodash "^4.17.21" + "@ts-stack/markdown@^1.5.0": version "1.5.0" resolved "https://registry.yarnpkg.com/@ts-stack/markdown/-/markdown-1.5.0.tgz#5dc298a20dc3dc040143c5a5948201eb6bf5419d" @@ -4203,6 +4215,19 @@ dotenv@^16.4.5: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.7.tgz#0e20c5b82950140aa99be360a8a5f52335f53c26" integrity sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ== +dpdm@^3.14.0: + version "3.14.0" + resolved "https://registry.yarnpkg.com/dpdm/-/dpdm-3.14.0.tgz#12a60a2d88b23981c91239b86e7462a5c203e5e9" + integrity sha512-YJzsFSyEtj88q5eTELg3UWU7TVZkG1dpbF4JDQ3t1b07xuzXmdoGeSz9TKOke1mUuOpWlk4q+pBh+aHzD6GBTg== + dependencies: + chalk "^4.1.2" + fs-extra "^11.1.1" + glob "^10.3.4" + ora "^5.4.1" + tslib "^2.6.2" + typescript "^5.2.2" + yargs "^17.7.2" + dunder-proto@^1.0.0, dunder-proto@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" @@ -5413,7 +5438,7 @@ glob-parent@^6.0.2: dependencies: is-glob "^4.0.3" -glob@^10.3.10, glob@^10.3.12: +glob@^10.3.10, glob@^10.3.12, glob@^10.3.4: version "10.4.5" resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== @@ -6253,6 +6278,11 @@ jake@^10.8.5: filelist "^1.0.4" minimatch "^3.1.2" +javascript-natural-sort@^0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz#f9e2303d4507f6d74355a73664d1440fb5a0ef59" + integrity sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw== + jest-diff@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" @@ -6572,7 +6602,7 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.2, lodash@^4.17.20: +lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.2, lodash@^4.17.20, lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -7203,7 +7233,7 @@ optionator@^0.9.3: type-check "^0.4.0" word-wrap "^1.2.5" -ora@^5.1.0: +ora@^5.1.0, ora@^5.4.1: version "5.4.1" resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== @@ -7570,11 +7600,21 @@ prelude-ls@^1.2.1: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== +prettier-plugin-organize-imports@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-4.1.0.tgz#f3d3764046a8e7ba6491431158b9be6ffd83b90f" + integrity sha512-5aWRdCgv645xaa58X8lOxzZoiHAldAPChljr/MT0crXVOWTZ+Svl4hIWlz+niYSlO6ikE5UXkN1JrRvIP2ut0A== + prettier@^2.8.8: version "2.8.8" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== +prettier@^3.5.3: + version "3.5.3" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.5.3.tgz#4fc2ce0d657e7a02e602549f053b239cb7dfe1b5" + integrity sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw== + pretty-format@^27.0.2: version "27.5.1" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" @@ -9016,6 +9056,11 @@ tslib@^2.0.1, tslib@^2.3.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0" integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ== +tslib@^2.6.2: + version "2.8.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" + integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== + tslib@~2.4: version "2.4.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" @@ -9154,7 +9199,7 @@ typescript-eslint@^8.26.1: "@typescript-eslint/parser" "8.26.1" "@typescript-eslint/utils" "8.26.1" -typescript@^5.4.3, typescript@^5.8.2: +typescript@^5.2.2, typescript@^5.4.3, typescript@^5.8.2: version "5.8.2" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.8.2.tgz#8170b3702f74b79db2e5a96207c15e65807999e4" integrity sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==