Merge branch 'pierremtb/issue7657-Allow-all-sweeps-to-work-on-variable-less-profiles' into pierremtb/issue7615-Expose-global
-optional-arg-for-all-point-and-click-transforms
This commit is contained in:
1
interface.d.ts
vendored
1
interface.d.ts
vendored
@ -72,7 +72,6 @@ export interface IElectronAPI {
|
|||||||
}
|
}
|
||||||
process: {
|
process: {
|
||||||
env: {
|
env: {
|
||||||
BASE_URL: string
|
|
||||||
IS_PLAYWRIGHT: string
|
IS_PLAYWRIGHT: string
|
||||||
VITE_KC_DEV_TOKEN: string
|
VITE_KC_DEV_TOKEN: string
|
||||||
VITE_KC_API_WS_MODELING_URL: string
|
VITE_KC_API_WS_MODELING_URL: string
|
||||||
|
19
package-lock.json
generated
19
package-lock.json
generated
@ -26640,10 +26640,27 @@
|
|||||||
"vscode-uri": "^3.1.0"
|
"vscode-uri": "^3.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^22.14.1",
|
"@types/node": "^24.0.7",
|
||||||
"ts-node": "^10.9.2"
|
"ts-node": "^10.9.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"packages/codemirror-lsp-client/node_modules/@types/node": {
|
||||||
|
"version": "24.0.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.10.tgz",
|
||||||
|
"integrity": "sha512-ENHwaH+JIRTDIEEbDK6QSQntAYGtbvdDXnMXnZaZ6k13Du1dPMmprkEHIL7ok2Wl2aZevetwTAb5S+7yIF+enA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": "~7.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"packages/codemirror-lsp-client/node_modules/undici-types": {
|
||||||
|
"version": "7.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz",
|
||||||
|
"integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"rust/kcl-language-server": {
|
"rust/kcl-language-server": {
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
@ -10,71 +10,76 @@ DATA;
|
|||||||
NAMED_UNIT(*)
|
NAMED_UNIT(*)
|
||||||
SI_UNIT($, .METRE.)
|
SI_UNIT($, .METRE.)
|
||||||
);
|
);
|
||||||
#2 = UNCERTAINTY_MEASURE_WITH_UNIT(0.00001, #1, 'DISTANCE_ACCURACY_VALUE', $);
|
#2 = (
|
||||||
#3 = (
|
NAMED_UNIT(*)
|
||||||
|
PLANE_ANGLE_UNIT()
|
||||||
|
SI_UNIT($, .RADIAN.)
|
||||||
|
);
|
||||||
|
#3 = UNCERTAINTY_MEASURE_WITH_UNIT(0.00001, #1, 'DISTANCE_ACCURACY_VALUE', $);
|
||||||
|
#4 = (
|
||||||
GEOMETRIC_REPRESENTATION_CONTEXT(3)
|
GEOMETRIC_REPRESENTATION_CONTEXT(3)
|
||||||
GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#2))
|
GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#3))
|
||||||
GLOBAL_UNIT_ASSIGNED_CONTEXT((#1))
|
GLOBAL_UNIT_ASSIGNED_CONTEXT((#1, #2))
|
||||||
REPRESENTATION_CONTEXT('', '3D')
|
REPRESENTATION_CONTEXT('', '3D')
|
||||||
);
|
);
|
||||||
#4 = CARTESIAN_POINT('NONE', (0.015, -0.01, -0.005));
|
#5 = CARTESIAN_POINT('NONE', (0.015, -0.01, -0.005));
|
||||||
#5 = VERTEX_POINT('NONE', #4);
|
#6 = VERTEX_POINT('NONE', #5);
|
||||||
#6 = CARTESIAN_POINT('NONE', (0.015, 0, -0.005));
|
#7 = CARTESIAN_POINT('NONE', (0.015, 0, -0.005));
|
||||||
#7 = VERTEX_POINT('NONE', #6);
|
#8 = VERTEX_POINT('NONE', #7);
|
||||||
#8 = DIRECTION('NONE', (1, 0, -0));
|
#9 = DIRECTION('NONE', (1, 0, -0));
|
||||||
#9 = DIRECTION('NONE', (0, 1, 0));
|
#10 = DIRECTION('NONE', (0, 1, 0));
|
||||||
#10 = CARTESIAN_POINT('NONE', (0.005, -0.01, -0.005));
|
#11 = CARTESIAN_POINT('NONE', (0.005, -0.01, -0.005));
|
||||||
#11 = AXIS2_PLACEMENT_3D('NONE', #10, #9, #8);
|
#12 = AXIS2_PLACEMENT_3D('NONE', #11, #10, #9);
|
||||||
#12 = CIRCLE('NONE', #11, 0.01);
|
#13 = CIRCLE('NONE', #12, 0.01);
|
||||||
#13 = DIRECTION('NONE', (0, 1, 0));
|
#14 = DIRECTION('NONE', (0, 1, 0));
|
||||||
#14 = VECTOR('NONE', #13, 1);
|
#15 = VECTOR('NONE', #14, 1);
|
||||||
#15 = CARTESIAN_POINT('NONE', (0.015, -0.01, -0.005));
|
#16 = CARTESIAN_POINT('NONE', (0.015, -0.01, -0.005));
|
||||||
#16 = LINE('NONE', #15, #14);
|
#17 = LINE('NONE', #16, #15);
|
||||||
#17 = DIRECTION('NONE', (1, 0, -0));
|
#18 = DIRECTION('NONE', (1, 0, -0));
|
||||||
#18 = DIRECTION('NONE', (0, 1, 0));
|
#19 = DIRECTION('NONE', (0, 1, 0));
|
||||||
#19 = CARTESIAN_POINT('NONE', (0.005, 0, -0.005));
|
#20 = CARTESIAN_POINT('NONE', (0.005, 0, -0.005));
|
||||||
#20 = AXIS2_PLACEMENT_3D('NONE', #19, #18, #17);
|
#21 = AXIS2_PLACEMENT_3D('NONE', #20, #19, #18);
|
||||||
#21 = CIRCLE('NONE', #20, 0.01);
|
#22 = CIRCLE('NONE', #21, 0.01);
|
||||||
#22 = EDGE_CURVE('NONE', #5, #5, #12, .T.);
|
#23 = EDGE_CURVE('NONE', #6, #6, #13, .T.);
|
||||||
#23 = EDGE_CURVE('NONE', #5, #7, #16, .T.);
|
#24 = EDGE_CURVE('NONE', #6, #8, #17, .T.);
|
||||||
#24 = EDGE_CURVE('NONE', #7, #7, #21, .T.);
|
#25 = EDGE_CURVE('NONE', #8, #8, #22, .T.);
|
||||||
#25 = CARTESIAN_POINT('NONE', (0.005, -0.005, -0.005));
|
#26 = CARTESIAN_POINT('NONE', (0.005, -0.005, -0.005));
|
||||||
#26 = DIRECTION('NONE', (0, 1, 0));
|
#27 = DIRECTION('NONE', (0, 1, 0));
|
||||||
#27 = DIRECTION('NONE', (1, 0, -0));
|
#28 = DIRECTION('NONE', (1, 0, -0));
|
||||||
#28 = AXIS2_PLACEMENT_3D('NONE', #25, #26, #27);
|
#29 = AXIS2_PLACEMENT_3D('NONE', #26, #27, #28);
|
||||||
#29 = CYLINDRICAL_SURFACE('NONE', #28, 0.01);
|
#30 = CYLINDRICAL_SURFACE('NONE', #29, 0.01);
|
||||||
#30 = CARTESIAN_POINT('NONE', (0, -0.01, -0));
|
#31 = CARTESIAN_POINT('NONE', (0, -0.01, -0));
|
||||||
#31 = DIRECTION('NONE', (0, 1, 0));
|
#32 = DIRECTION('NONE', (0, 1, 0));
|
||||||
#32 = AXIS2_PLACEMENT_3D('NONE', #30, #31, $);
|
#33 = AXIS2_PLACEMENT_3D('NONE', #31, #32, $);
|
||||||
#33 = PLANE('NONE', #32);
|
#34 = PLANE('NONE', #33);
|
||||||
#34 = CARTESIAN_POINT('NONE', (0, 0, -0));
|
#35 = CARTESIAN_POINT('NONE', (0, 0, -0));
|
||||||
#35 = DIRECTION('NONE', (0, 1, 0));
|
#36 = DIRECTION('NONE', (0, 1, 0));
|
||||||
#36 = AXIS2_PLACEMENT_3D('NONE', #34, #35, $);
|
#37 = AXIS2_PLACEMENT_3D('NONE', #35, #36, $);
|
||||||
#37 = PLANE('NONE', #36);
|
#38 = PLANE('NONE', #37);
|
||||||
#38 = ORIENTED_EDGE('NONE', *, *, #22, .T.);
|
#39 = ORIENTED_EDGE('NONE', *, *, #23, .T.);
|
||||||
#39 = ORIENTED_EDGE('NONE', *, *, #24, .F.);
|
#40 = ORIENTED_EDGE('NONE', *, *, #25, .F.);
|
||||||
#40 = EDGE_LOOP('NONE', (#38));
|
#41 = EDGE_LOOP('NONE', (#39));
|
||||||
#41 = FACE_BOUND('NONE', #40, .T.);
|
#42 = FACE_BOUND('NONE', #41, .T.);
|
||||||
#42 = EDGE_LOOP('NONE', (#39));
|
#43 = EDGE_LOOP('NONE', (#40));
|
||||||
#43 = FACE_BOUND('NONE', #42, .T.);
|
#44 = FACE_BOUND('NONE', #43, .T.);
|
||||||
#44 = ADVANCED_FACE('NONE', (#41, #43), #29, .T.);
|
#45 = ADVANCED_FACE('NONE', (#42, #44), #30, .T.);
|
||||||
#45 = ORIENTED_EDGE('NONE', *, *, #22, .F.);
|
#46 = ORIENTED_EDGE('NONE', *, *, #23, .F.);
|
||||||
#46 = EDGE_LOOP('NONE', (#45));
|
#47 = EDGE_LOOP('NONE', (#46));
|
||||||
#47 = FACE_BOUND('NONE', #46, .T.);
|
#48 = FACE_BOUND('NONE', #47, .T.);
|
||||||
#48 = ADVANCED_FACE('NONE', (#47), #33, .F.);
|
#49 = ADVANCED_FACE('NONE', (#48), #34, .F.);
|
||||||
#49 = ORIENTED_EDGE('NONE', *, *, #24, .T.);
|
#50 = ORIENTED_EDGE('NONE', *, *, #25, .T.);
|
||||||
#50 = EDGE_LOOP('NONE', (#49));
|
#51 = EDGE_LOOP('NONE', (#50));
|
||||||
#51 = FACE_BOUND('NONE', #50, .T.);
|
#52 = FACE_BOUND('NONE', #51, .T.);
|
||||||
#52 = ADVANCED_FACE('NONE', (#51), #37, .T.);
|
#53 = ADVANCED_FACE('NONE', (#52), #38, .T.);
|
||||||
#53 = CLOSED_SHELL('NONE', (#44, #48, #52));
|
#54 = CLOSED_SHELL('NONE', (#45, #49, #53));
|
||||||
#54 = MANIFOLD_SOLID_BREP('NONE', #53);
|
#55 = MANIFOLD_SOLID_BREP('NONE', #54);
|
||||||
#55 = APPLICATION_CONTEXT('configuration controlled 3D design of mechanical parts and assemblies');
|
#56 = APPLICATION_CONTEXT('configuration controlled 3D design of mechanical parts and assemblies');
|
||||||
#56 = PRODUCT_DEFINITION_CONTEXT('part definition', #55, 'design');
|
#57 = PRODUCT_DEFINITION_CONTEXT('part definition', #56, 'design');
|
||||||
#57 = PRODUCT('UNIDENTIFIED_PRODUCT', 'NONE', $, ());
|
#58 = PRODUCT('UNIDENTIFIED_PRODUCT', 'NONE', $, ());
|
||||||
#58 = PRODUCT_DEFINITION_FORMATION('', $, #57);
|
#59 = PRODUCT_DEFINITION_FORMATION('', $, #58);
|
||||||
#59 = PRODUCT_DEFINITION('design', $, #58, #56);
|
#60 = PRODUCT_DEFINITION('design', $, #59, #57);
|
||||||
#60 = PRODUCT_DEFINITION_SHAPE('NONE', $, #59);
|
#61 = PRODUCT_DEFINITION_SHAPE('NONE', $, #60);
|
||||||
#61 = ADVANCED_BREP_SHAPE_REPRESENTATION('NONE', (#54), #3);
|
#62 = ADVANCED_BREP_SHAPE_REPRESENTATION('NONE', (#55), #4);
|
||||||
#62 = SHAPE_DEFINITION_REPRESENTATION(#60, #61);
|
#63 = SHAPE_DEFINITION_REPRESENTATION(#61, #62);
|
||||||
ENDSEC;
|
ENDSEC;
|
||||||
END-ISO-10303-21;
|
END-ISO-10303-21;
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
URL STATUS
|
URL STATUS
|
||||||
000 https://${BASE_URL}
|
000 https://${BASE_URL}
|
||||||
|
405 https://api.dev.zoo.dev/oauth2/token/revoke
|
||||||
|
401 https://api.dev.zoo.dev/users
|
||||||
301 https://discord.gg/JQEpHR7Nt2
|
301 https://discord.gg/JQEpHR7Nt2
|
||||||
404 https://github.com/KittyCAD/engine/issues/3528
|
404 https://github.com/KittyCAD/engine/issues/3528
|
||||||
404 https://github.com/KittyCAD/modeling-app/commit/${ref}
|
404 https://github.com/KittyCAD/modeling-app/commit/${ref}
|
||||||
|
@ -7,7 +7,7 @@ import {
|
|||||||
LanguageServerClient,
|
LanguageServerClient,
|
||||||
LspWorkerEventType,
|
LspWorkerEventType,
|
||||||
} from '@kittycad/codemirror-lsp-client'
|
} from '@kittycad/codemirror-lsp-client'
|
||||||
import { TEST, VITE_KC_API_BASE_URL } from '@src/env'
|
import { TEST } from '@src/env'
|
||||||
import React, { createContext, useContext, useMemo, useState } from 'react'
|
import React, { createContext, useContext, useMemo, useState } from 'react'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import type * as LSP from 'vscode-languageserver-protocol'
|
import type * as LSP from 'vscode-languageserver-protocol'
|
||||||
@ -28,6 +28,7 @@ import type { FileEntry } from '@src/lib/project'
|
|||||||
import { codeManager } from '@src/lib/singletons'
|
import { codeManager } from '@src/lib/singletons'
|
||||||
import { err } from '@src/lib/trap'
|
import { err } from '@src/lib/trap'
|
||||||
import { useToken } from '@src/lib/singletons'
|
import { useToken } from '@src/lib/singletons'
|
||||||
|
import { withAPIBaseURL } from '@src/lib/withBaseURL'
|
||||||
|
|
||||||
function getWorkspaceFolders(): LSP.WorkspaceFolder[] {
|
function getWorkspaceFolders(): LSP.WorkspaceFolder[] {
|
||||||
return []
|
return []
|
||||||
@ -85,7 +86,7 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
|
|||||||
const initEvent: KclWorkerOptions = {
|
const initEvent: KclWorkerOptions = {
|
||||||
wasmUrl: wasmUrl(),
|
wasmUrl: wasmUrl(),
|
||||||
token: token,
|
token: token,
|
||||||
apiBaseUrl: VITE_KC_API_BASE_URL,
|
apiBaseUrl: withAPIBaseURL(''),
|
||||||
}
|
}
|
||||||
lspWorker.postMessage({
|
lspWorker.postMessage({
|
||||||
worker: LspWorker.Kcl,
|
worker: LspWorker.Kcl,
|
||||||
@ -178,7 +179,7 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
|
|||||||
const initEvent: CopilotWorkerOptions = {
|
const initEvent: CopilotWorkerOptions = {
|
||||||
wasmUrl: wasmUrl(),
|
wasmUrl: wasmUrl(),
|
||||||
token: token,
|
token: token,
|
||||||
apiBaseUrl: VITE_KC_API_BASE_URL,
|
apiBaseUrl: withAPIBaseURL(''),
|
||||||
}
|
}
|
||||||
lspWorker.postMessage({
|
lspWorker.postMessage({
|
||||||
worker: LspWorker.Copilot,
|
worker: LspWorker.Copilot,
|
||||||
|
@ -2,9 +2,12 @@ import type { Node } from '@rust/kcl-lib/bindings/Node'
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
createArrayExpression,
|
createArrayExpression,
|
||||||
|
createCallExpressionStdLibKw,
|
||||||
createIdentifier,
|
createIdentifier,
|
||||||
|
createLabeledArg,
|
||||||
createLiteral,
|
createLiteral,
|
||||||
createLiteralMaybeSuffix,
|
createLiteralMaybeSuffix,
|
||||||
|
createLocalName,
|
||||||
createObjectExpression,
|
createObjectExpression,
|
||||||
createPipeExpression,
|
createPipeExpression,
|
||||||
createPipeSubstitution,
|
createPipeSubstitution,
|
||||||
@ -14,12 +17,19 @@ import {
|
|||||||
} from '@src/lang/create'
|
} from '@src/lang/create'
|
||||||
import {
|
import {
|
||||||
addSketchTo,
|
addSketchTo,
|
||||||
|
createPathToNodeForLastVariable,
|
||||||
|
createVariableExpressionsArray,
|
||||||
deleteSegmentFromPipeExpression,
|
deleteSegmentFromPipeExpression,
|
||||||
moveValueIntoNewVariable,
|
moveValueIntoNewVariable,
|
||||||
|
setCallInAst,
|
||||||
sketchOnExtrudedFace,
|
sketchOnExtrudedFace,
|
||||||
splitPipedProfile,
|
splitPipedProfile,
|
||||||
} from '@src/lang/modifyAst'
|
} from '@src/lang/modifyAst'
|
||||||
import { findUsesOfTagInPipe } from '@src/lang/queryAst'
|
import {
|
||||||
|
findUsesOfTagInPipe,
|
||||||
|
getNodeFromPath,
|
||||||
|
getVariableExprsFromSelection,
|
||||||
|
} from '@src/lang/queryAst'
|
||||||
import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils'
|
import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils'
|
||||||
import type { Artifact } from '@src/lang/std/artifactGraph'
|
import type { Artifact } from '@src/lang/std/artifactGraph'
|
||||||
import { codeRefFromRange } from '@src/lang/std/artifactGraph'
|
import { codeRefFromRange } from '@src/lang/std/artifactGraph'
|
||||||
@ -31,6 +41,7 @@ import { enginelessExecutor } from '@src/lib/testHelpers'
|
|||||||
import { err } from '@src/lib/trap'
|
import { err } from '@src/lib/trap'
|
||||||
import { deleteFromSelection } from '@src/lang/modifyAst/deleteFromSelection'
|
import { deleteFromSelection } from '@src/lang/modifyAst/deleteFromSelection'
|
||||||
import { assertNotErr } from '@src/unitTestUtils'
|
import { assertNotErr } from '@src/unitTestUtils'
|
||||||
|
import type { Selections } from '@src/lib/selections'
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await initPromise
|
await initPromise
|
||||||
@ -917,3 +928,212 @@ extrude001 = extrude(part001, length = 5)
|
|||||||
expect(result instanceof Error).toBe(true)
|
expect(result instanceof Error).toBe(true)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('Testing createVariableExpressionsArray', () => {
|
||||||
|
it('should return null for any number of pipe substitutions', () => {
|
||||||
|
const onePipe = [createPipeSubstitution()]
|
||||||
|
const twoPipes = [createPipeSubstitution(), createPipeSubstitution()]
|
||||||
|
const threePipes = [
|
||||||
|
createPipeSubstitution(),
|
||||||
|
createPipeSubstitution(),
|
||||||
|
createPipeSubstitution(),
|
||||||
|
]
|
||||||
|
expect(createVariableExpressionsArray(onePipe)).toBeNull()
|
||||||
|
expect(createVariableExpressionsArray(twoPipes)).toBeNull()
|
||||||
|
expect(createVariableExpressionsArray(threePipes)).toBeNull()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should create a variable expressions for one variable', () => {
|
||||||
|
const oneVariableName = [createLocalName('var1')]
|
||||||
|
const expr = createVariableExpressionsArray(oneVariableName)
|
||||||
|
if (expr?.type !== 'Name') {
|
||||||
|
throw new Error(`Expected Literal type, got ${expr?.type}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(expr.name.name).toBe('var1')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should create an array of variable expressions for two variables', () => {
|
||||||
|
const twoVariableNames = [createLocalName('var1'), createLocalName('var2')]
|
||||||
|
const exprs = createVariableExpressionsArray(twoVariableNames)
|
||||||
|
if (exprs?.type !== 'ArrayExpression') {
|
||||||
|
throw new Error('Expected ArrayExpression type')
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(exprs.elements).toHaveLength(2)
|
||||||
|
if (
|
||||||
|
exprs.elements[0].type !== 'Name' ||
|
||||||
|
exprs.elements[1].type !== 'Name'
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
`Expected elements to be of type Name, got ${exprs.elements[0].type} and ${exprs.elements[1].type}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
expect(exprs.elements[0].name.name).toBe('var1')
|
||||||
|
expect(exprs.elements[1].name.name).toBe('var2')
|
||||||
|
})
|
||||||
|
|
||||||
|
// This would catch the issue at https://github.com/KittyCAD/modeling-app/issues/7669
|
||||||
|
// TODO: fix function to get this test to pass
|
||||||
|
// it('should create one expr if the array of variable names are the same', () => {
|
||||||
|
// const twoVariableNames = [createLocalName('var1'), createLocalName('var1')]
|
||||||
|
// const expr = createVariableExpressionsArray(twoVariableNames)
|
||||||
|
// if (expr?.type !== 'Name') {
|
||||||
|
// throw new Error(`Expected Literal type, got ${expr?.type}`)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// expect(expr.name.name).toBe('var1')
|
||||||
|
// })
|
||||||
|
|
||||||
|
it('should create an array of variable expressions for one variable and a pipe', () => {
|
||||||
|
const oneVarOnePipe = [createPipeSubstitution(), createLocalName('var1')]
|
||||||
|
const exprs = createVariableExpressionsArray(oneVarOnePipe)
|
||||||
|
if (exprs?.type !== 'ArrayExpression') {
|
||||||
|
throw new Error('Expected ArrayExpression type')
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(exprs.elements).toHaveLength(2)
|
||||||
|
expect(exprs.elements[0].type).toBe('PipeSubstitution')
|
||||||
|
if (exprs.elements[1].type !== 'Name') {
|
||||||
|
throw new Error(
|
||||||
|
`Expected elements[1] to be of type Name, got ${exprs.elements[1].type}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(exprs.elements[1].name.name).toBe('var1')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Testing createPathToNodeForLastVariable', () => {
|
||||||
|
it('should create a path to the last variable in the array', () => {
|
||||||
|
const circleProfileInVar = `sketch001 = startSketchOn(XY)
|
||||||
|
profile001 = circle(sketch001, center = [0, 0], radius = 1)
|
||||||
|
extrude001 = extrude(profile001, length = 5)
|
||||||
|
`
|
||||||
|
const ast = assertParse(circleProfileInVar)
|
||||||
|
const path = createPathToNodeForLastVariable(ast, false)
|
||||||
|
expect(path.length).toEqual(4)
|
||||||
|
|
||||||
|
// Verify we can get the right node
|
||||||
|
const node = getNodeFromPath<any>(ast, path)
|
||||||
|
if (err(node)) {
|
||||||
|
throw node
|
||||||
|
}
|
||||||
|
// With the expected range
|
||||||
|
const startOfExtrudeIndex = circleProfileInVar.indexOf('extrude(')
|
||||||
|
expect(node.node.start).toEqual(startOfExtrudeIndex)
|
||||||
|
expect(node.node.end).toEqual(circleProfileInVar.length - 1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should create a path to the first kwarg in the last expression', () => {
|
||||||
|
const circleProfileInVar = `sketch001 = startSketchOn(XY)
|
||||||
|
profile001 = circle(sketch001, center = [0, 0], radius = 1)
|
||||||
|
extrude001 = extrude(profile001, length = 123)
|
||||||
|
`
|
||||||
|
const ast = assertParse(circleProfileInVar)
|
||||||
|
const path = createPathToNodeForLastVariable(ast, true)
|
||||||
|
expect(path.length).toEqual(7)
|
||||||
|
|
||||||
|
// Verify we can get the right node
|
||||||
|
const node = getNodeFromPath<any>(ast, path)
|
||||||
|
if (err(node)) {
|
||||||
|
throw node
|
||||||
|
}
|
||||||
|
// With the expected range
|
||||||
|
const startOfKwargIndex = circleProfileInVar.indexOf('123')
|
||||||
|
expect(node.node.start).toEqual(startOfKwargIndex)
|
||||||
|
expect(node.node.end).toEqual(startOfKwargIndex + 3)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Testing setCallInAst', () => {
|
||||||
|
it('should push an extrude call with variable on variable profile', () => {
|
||||||
|
const code = `sketch001 = startSketchOn(XY)
|
||||||
|
profile001 = circle(sketch001, center = [0, 0], radius = 1)
|
||||||
|
`
|
||||||
|
const ast = assertParse(code)
|
||||||
|
const exprs = createVariableExpressionsArray([
|
||||||
|
createLocalName('profile001'),
|
||||||
|
])
|
||||||
|
const call = createCallExpressionStdLibKw('extrude', exprs, [
|
||||||
|
createLabeledArg('length', createLiteral(5)),
|
||||||
|
])
|
||||||
|
const pathToNode = setCallInAst(ast, call)
|
||||||
|
if (err(pathToNode)) {
|
||||||
|
throw pathToNode
|
||||||
|
}
|
||||||
|
const newCode = recast(ast)
|
||||||
|
expect(newCode).toContain(code)
|
||||||
|
expect(newCode).toContain(`extrude001 = extrude(profile001, length = 5)`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should push an extrude call in pipe is selection was in variable-less pipe', async () => {
|
||||||
|
const code = `startSketchOn(XY)
|
||||||
|
|> circle(center = [0, 0], radius = 1)
|
||||||
|
`
|
||||||
|
const ast = assertParse(code)
|
||||||
|
const { artifactGraph } = await enginelessExecutor(ast)
|
||||||
|
const artifact = artifactGraph.values().find((a) => a.type === 'path')
|
||||||
|
if (!artifact) {
|
||||||
|
throw new Error('Artifact not found in the graph')
|
||||||
|
}
|
||||||
|
const selections: Selections = {
|
||||||
|
graphSelections: [
|
||||||
|
{
|
||||||
|
codeRef: artifact.codeRef,
|
||||||
|
artifact,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
otherSelections: [],
|
||||||
|
}
|
||||||
|
const variableExprs = getVariableExprsFromSelection(selections, ast)
|
||||||
|
if (err(variableExprs)) throw variableExprs
|
||||||
|
const exprs = createVariableExpressionsArray(variableExprs.exprs)
|
||||||
|
const call = createCallExpressionStdLibKw('extrude', exprs, [
|
||||||
|
createLabeledArg('length', createLiteral(5)),
|
||||||
|
])
|
||||||
|
const lastPathToNode = variableExprs.paths.pop()
|
||||||
|
const pathToNode = setCallInAst(ast, call, undefined, lastPathToNode)
|
||||||
|
if (err(pathToNode)) {
|
||||||
|
throw pathToNode
|
||||||
|
}
|
||||||
|
const newCode = recast(ast)
|
||||||
|
expect(newCode).toContain(code)
|
||||||
|
expect(newCode).toContain(`|> extrude(length = 5)`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should push an extrude call with variable if selection was in variable pipe', async () => {
|
||||||
|
const code = `profile001 = startSketchOn(XY)
|
||||||
|
|> circle(center = [0, 0], radius = 1)
|
||||||
|
`
|
||||||
|
const ast = assertParse(code)
|
||||||
|
const { artifactGraph } = await enginelessExecutor(ast)
|
||||||
|
const artifact = artifactGraph.values().find((a) => a.type === 'path')
|
||||||
|
if (!artifact) {
|
||||||
|
throw new Error('Artifact not found in the graph')
|
||||||
|
}
|
||||||
|
const selections: Selections = {
|
||||||
|
graphSelections: [
|
||||||
|
{
|
||||||
|
codeRef: artifact.codeRef,
|
||||||
|
artifact,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
otherSelections: [],
|
||||||
|
}
|
||||||
|
const variableExprs = getVariableExprsFromSelection(selections, ast)
|
||||||
|
if (err(variableExprs)) throw variableExprs
|
||||||
|
const exprs = createVariableExpressionsArray(variableExprs.exprs)
|
||||||
|
const call = createCallExpressionStdLibKw('extrude', exprs, [
|
||||||
|
createLabeledArg('length', createLiteral(5)),
|
||||||
|
])
|
||||||
|
const lastPathToNode = variableExprs.paths.pop()
|
||||||
|
const pathToNode = setCallInAst(ast, call, undefined, lastPathToNode)
|
||||||
|
if (err(pathToNode)) {
|
||||||
|
throw pathToNode
|
||||||
|
}
|
||||||
|
const newCode = recast(ast)
|
||||||
|
expect(newCode).toContain(code)
|
||||||
|
expect(newCode).toContain(`extrude001 = extrude(profile001, length = 5)`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
@ -1209,3 +1209,83 @@ export function insertVariableAndOffsetPathToNode(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create an array expression for variables,
|
||||||
|
// or keep it null if all are PipeSubstitutions
|
||||||
|
export function createVariableExpressionsArray(sketches: Expr[]): Expr | null {
|
||||||
|
let exprs: Expr | null = null
|
||||||
|
if (sketches.every((s) => s.type === 'PipeSubstitution')) {
|
||||||
|
// Keeping null so we don't even put it the % sign
|
||||||
|
} else if (sketches.length === 1) {
|
||||||
|
exprs = sketches[0]
|
||||||
|
} else {
|
||||||
|
exprs = createArrayExpression(sketches)
|
||||||
|
}
|
||||||
|
return exprs
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a path to node to the last variable declaroator of an ast
|
||||||
|
// Optionally, can point to the first kwarg of the CallExpressionKw
|
||||||
|
export function createPathToNodeForLastVariable(
|
||||||
|
ast: Node<Program>,
|
||||||
|
toFirstKwarg = true
|
||||||
|
): PathToNode {
|
||||||
|
const argIndex = 0 // first kwarg for all sweeps here
|
||||||
|
const pathToCall: PathToNode = [
|
||||||
|
['body', ''],
|
||||||
|
[ast.body.length - 1, 'index'],
|
||||||
|
['declaration', 'VariableDeclaration'],
|
||||||
|
['init', 'VariableDeclarator'],
|
||||||
|
]
|
||||||
|
if (toFirstKwarg) {
|
||||||
|
pathToCall.push(
|
||||||
|
['arguments', 'CallExpressionKw'],
|
||||||
|
[argIndex, ARG_INDEX_FIELD],
|
||||||
|
['arg', LABELED_ARG_FIELD]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return pathToCall
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setCallInAst(
|
||||||
|
ast: Node<Program>,
|
||||||
|
call: Node<CallExpressionKw>,
|
||||||
|
nodeToEdit?: PathToNode,
|
||||||
|
lastPathToNode?: PathToNode
|
||||||
|
): Error | PathToNode {
|
||||||
|
let pathToNode: PathToNode | undefined
|
||||||
|
if (nodeToEdit) {
|
||||||
|
const result = getNodeFromPath<CallExpressionKw>(
|
||||||
|
ast,
|
||||||
|
nodeToEdit,
|
||||||
|
'CallExpressionKw'
|
||||||
|
)
|
||||||
|
if (err(result)) {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(result.node, call)
|
||||||
|
pathToNode = nodeToEdit
|
||||||
|
} else {
|
||||||
|
if (!call.unlabeled && lastPathToNode) {
|
||||||
|
const pipe = getNodeFromPath<PipeExpression>(
|
||||||
|
ast,
|
||||||
|
lastPathToNode,
|
||||||
|
'PipeExpression'
|
||||||
|
)
|
||||||
|
if (err(pipe)) {
|
||||||
|
return pipe
|
||||||
|
}
|
||||||
|
pipe.node.body.push(call)
|
||||||
|
pathToNode = lastPathToNode
|
||||||
|
} else {
|
||||||
|
const name = findUniqueName(ast, call.callee.name.name)
|
||||||
|
const declaration = createVariableDeclaration(name, call)
|
||||||
|
ast.body.push(declaration)
|
||||||
|
pathToNode = createPathToNodeForLastVariable(ast)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pathToNode
|
||||||
|
}
|
||||||
|
252
src/lang/modifyAst/sweeps.test.ts
Normal file
252
src/lang/modifyAst/sweeps.test.ts
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
import {
|
||||||
|
type Artifact,
|
||||||
|
assertParse,
|
||||||
|
type CodeRef,
|
||||||
|
type Program,
|
||||||
|
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'
|
||||||
|
import {
|
||||||
|
addExtrude,
|
||||||
|
addLoft,
|
||||||
|
addRevolve,
|
||||||
|
addSweep,
|
||||||
|
} from '@src/lang/modifyAst/sweeps'
|
||||||
|
import { stringToKclExpression } from '@src/lib/kclHelpers'
|
||||||
|
import type { Node } from '@rust/kcl-lib/bindings/Node'
|
||||||
|
|
||||||
|
async function getAstAndArtifactGraph(code: string) {
|
||||||
|
const ast = assertParse(code)
|
||||||
|
if (err(ast)) throw ast
|
||||||
|
|
||||||
|
const { artifactGraph } = await enginelessExecutor(ast)
|
||||||
|
return { ast, artifactGraph }
|
||||||
|
}
|
||||||
|
|
||||||
|
function createSelectionFromPathArtifact(
|
||||||
|
artifacts: (Artifact & { codeRef: CodeRef })[]
|
||||||
|
): Selections {
|
||||||
|
const graphSelections = artifacts.map(
|
||||||
|
(artifact) =>
|
||||||
|
({
|
||||||
|
codeRef: artifact.codeRef,
|
||||||
|
artifact,
|
||||||
|
}) as Selection
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
graphSelections,
|
||||||
|
otherSelections: [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getAstAndSketchSelections(code: string) {
|
||||||
|
const { ast, artifactGraph } = await getAstAndArtifactGraph(code)
|
||||||
|
const artifacts = [...artifactGraph.values()].filter((a) => a.type === 'path')
|
||||||
|
if (artifacts.length === 0) {
|
||||||
|
throw new Error('Artifact not found in the graph')
|
||||||
|
}
|
||||||
|
const sketches = createSelectionFromPathArtifact(artifacts)
|
||||||
|
return { ast, sketches }
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getKclCommandValue(value: string) {
|
||||||
|
const result = await stringToKclExpression(value)
|
||||||
|
if (err(result) || 'errors' in result) {
|
||||||
|
throw new Error(`Couldn't create kcl expression`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runNewAstAndCheckForSweep(ast: Node<Program>) {
|
||||||
|
const { artifactGraph } = await enginelessExecutor(ast)
|
||||||
|
console.log('artifactGraph', artifactGraph)
|
||||||
|
const sweepArtifact = artifactGraph.values().find((a) => a.type === 'sweep')
|
||||||
|
expect(sweepArtifact).toBeDefined()
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('Testing addExtrude', () => {
|
||||||
|
it('should push a call in pipe if selection was in variable-less pipe', async () => {
|
||||||
|
const code = `startSketchOn(XY)
|
||||||
|
|> circle(center = [0, 0], radius = 1)
|
||||||
|
`
|
||||||
|
const { ast, sketches } = await getAstAndSketchSelections(code)
|
||||||
|
const length = await getKclCommandValue('1')
|
||||||
|
const result = addExtrude({ ast, sketches, length })
|
||||||
|
if (err(result)) throw result
|
||||||
|
const newCode = recast(result.modifiedAst)
|
||||||
|
expect(newCode).toContain(code)
|
||||||
|
expect(newCode).toContain(`|> extrude(length = 1)`)
|
||||||
|
await runNewAstAndCheckForSweep(result.modifiedAst)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should push a call with variable if selection was in variable profile', async () => {
|
||||||
|
const code = `sketch001 = startSketchOn(XY)
|
||||||
|
profile001 = circle(sketch001, center = [0, 0], radius = 1)
|
||||||
|
`
|
||||||
|
const { ast, sketches } = await getAstAndSketchSelections(code)
|
||||||
|
const length = await getKclCommandValue('2')
|
||||||
|
const result = addExtrude({ ast, sketches, length })
|
||||||
|
if (err(result)) throw result
|
||||||
|
const newCode = recast(result.modifiedAst)
|
||||||
|
expect(newCode).toContain(code)
|
||||||
|
expect(newCode).toContain(`extrude001 = extrude(profile001, length = 2)`)
|
||||||
|
await runNewAstAndCheckForSweep(result.modifiedAst)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should push a call with variable if selection was in variable pipe', async () => {
|
||||||
|
const code = `profile001 = startSketchOn(XY)
|
||||||
|
|> circle(center = [0, 0], radius = 1)
|
||||||
|
`
|
||||||
|
const { ast, sketches } = await getAstAndSketchSelections(code)
|
||||||
|
const length = await getKclCommandValue('3')
|
||||||
|
const result = addExtrude({ ast, sketches, length })
|
||||||
|
if (err(result)) throw result
|
||||||
|
await runNewAstAndCheckForSweep(result.modifiedAst)
|
||||||
|
const newCode = recast(result.modifiedAst)
|
||||||
|
expect(newCode).toContain(code)
|
||||||
|
expect(newCode).toContain(`extrude001 = extrude(profile001, length = 3)`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should push a call with many compatible optional args if asked', async () => {
|
||||||
|
const code = `sketch001 = startSketchOn(XY)
|
||||||
|
profile001 = circle(sketch001, center = [0, 0], radius = 1)
|
||||||
|
`
|
||||||
|
const { ast, sketches } = await getAstAndSketchSelections(code)
|
||||||
|
const length = await getKclCommandValue('10')
|
||||||
|
const bidirectionalLength = await getKclCommandValue('20')
|
||||||
|
const twistAngle = await getKclCommandValue('30')
|
||||||
|
const result = addExtrude({
|
||||||
|
ast,
|
||||||
|
sketches,
|
||||||
|
length,
|
||||||
|
bidirectionalLength,
|
||||||
|
twistAngle,
|
||||||
|
})
|
||||||
|
if (err(result)) throw result
|
||||||
|
await runNewAstAndCheckForSweep(result.modifiedAst)
|
||||||
|
const newCode = recast(result.modifiedAst)
|
||||||
|
expect(newCode).toContain(code)
|
||||||
|
expect(newCode).toContain(`extrude001 = extrude(
|
||||||
|
profile001,
|
||||||
|
length = 10,
|
||||||
|
bidirectionalLength = 20,
|
||||||
|
twistAngle = 30,
|
||||||
|
)`)
|
||||||
|
})
|
||||||
|
|
||||||
|
// TODO: missing edit flow test
|
||||||
|
|
||||||
|
// TODO: missing multi-profile test
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Testing addSweep', () => {
|
||||||
|
it('should push a call with variable and all compatible optional args', async () => {
|
||||||
|
const code = `sketch001 = startSketchOn(XY)
|
||||||
|
profile001 = circle(sketch001, center = [0, 0], radius = 1)
|
||||||
|
sketch002 = startSketchOn(XZ)
|
||||||
|
profile002 = startProfile(sketch002, at = [0, 0])
|
||||||
|
|> xLine(length = -5)
|
||||||
|
|> tangentialArc(endAbsolute = [-20, 5])
|
||||||
|
`
|
||||||
|
const { ast, artifactGraph } = await getAstAndArtifactGraph(code)
|
||||||
|
const artifact1 = artifactGraph.values().find((a) => a.type === 'path')
|
||||||
|
const artifact2 = [...artifactGraph.values()].findLast(
|
||||||
|
(a) => a.type === 'path'
|
||||||
|
)
|
||||||
|
if (!artifact1 || !artifact2) {
|
||||||
|
throw new Error('Artifact not found in the graph')
|
||||||
|
}
|
||||||
|
|
||||||
|
const sketches = createSelectionFromPathArtifact([artifact1])
|
||||||
|
const path = createSelectionFromPathArtifact([artifact2])
|
||||||
|
const sectional = true
|
||||||
|
const relativeTo = 'sketchPlane'
|
||||||
|
const result = addSweep({
|
||||||
|
ast,
|
||||||
|
sketches,
|
||||||
|
path,
|
||||||
|
sectional,
|
||||||
|
relativeTo,
|
||||||
|
})
|
||||||
|
if (err(result)) throw result
|
||||||
|
await runNewAstAndCheckForSweep(result.modifiedAst)
|
||||||
|
const newCode = recast(result.modifiedAst)
|
||||||
|
expect(newCode).toContain(code)
|
||||||
|
expect(newCode).toContain(`sweep001 = sweep(
|
||||||
|
profile001,
|
||||||
|
path = profile002,
|
||||||
|
sectional = true,
|
||||||
|
relativeTo = 'sketchPlane',
|
||||||
|
)`)
|
||||||
|
})
|
||||||
|
|
||||||
|
// TODO: missing edit flow test
|
||||||
|
|
||||||
|
// TODO: missing multi-profile test
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Testing addLoft', () => {
|
||||||
|
it('should push a call with variable and all optional args if asked', async () => {
|
||||||
|
const code = `sketch001 = startSketchOn(XZ)
|
||||||
|
profile001 = circle(sketch001, center = [0, 0], radius = 30)
|
||||||
|
plane001 = offsetPlane(XZ, offset = 50)
|
||||||
|
sketch002 = startSketchOn(plane001)
|
||||||
|
profile002 = circle(sketch002, center = [0, 0], radius = 20)
|
||||||
|
`
|
||||||
|
const { ast, sketches } = await getAstAndSketchSelections(code)
|
||||||
|
expect(sketches.graphSelections).toHaveLength(2)
|
||||||
|
const vDegree = await getKclCommandValue('3')
|
||||||
|
const result = addLoft({
|
||||||
|
ast,
|
||||||
|
sketches,
|
||||||
|
vDegree,
|
||||||
|
})
|
||||||
|
if (err(result)) throw result
|
||||||
|
const newCode = recast(result.modifiedAst)
|
||||||
|
expect(newCode).toContain(code)
|
||||||
|
expect(newCode).toContain(
|
||||||
|
`loft001 = loft([profile001, profile002], vDegree = 3)`
|
||||||
|
)
|
||||||
|
// Don't think we can find the artifact here for loft?
|
||||||
|
})
|
||||||
|
|
||||||
|
// TODO: missing edit flow test
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Testing addRevolve', () => {
|
||||||
|
it('should push a call with variable and compatible optional args if asked', async () => {
|
||||||
|
const code = `sketch001 = startSketchOn(XZ)
|
||||||
|
profile001 = circle(sketch001, center = [3, 0], radius = 1)
|
||||||
|
`
|
||||||
|
const { ast, sketches } = await getAstAndSketchSelections(code)
|
||||||
|
expect(sketches.graphSelections).toHaveLength(1)
|
||||||
|
const result = addRevolve({
|
||||||
|
ast,
|
||||||
|
sketches,
|
||||||
|
angle: await getKclCommandValue('1'),
|
||||||
|
axisOrEdge: 'Axis',
|
||||||
|
axis: 'X',
|
||||||
|
edge: undefined,
|
||||||
|
symmetric: false,
|
||||||
|
bidirectionalAngle: await getKclCommandValue('2'),
|
||||||
|
})
|
||||||
|
if (err(result)) throw result
|
||||||
|
await runNewAstAndCheckForSweep(result.modifiedAst)
|
||||||
|
const newCode = recast(result.modifiedAst)
|
||||||
|
console.log(newCode)
|
||||||
|
expect(newCode).toContain(code)
|
||||||
|
expect(newCode).toContain(`revolve001 = revolve(
|
||||||
|
profile001,
|
||||||
|
angle = 1,
|
||||||
|
axis = X,
|
||||||
|
bidirectionalAngle = 2,
|
||||||
|
)`)
|
||||||
|
})
|
||||||
|
|
||||||
|
// TODO: missing edit flow test
|
||||||
|
|
||||||
|
// TODO: missing multi-profile test
|
||||||
|
})
|
@ -5,10 +5,12 @@ import {
|
|||||||
createLabeledArg,
|
createLabeledArg,
|
||||||
createLiteral,
|
createLiteral,
|
||||||
createLocalName,
|
createLocalName,
|
||||||
createVariableDeclaration,
|
|
||||||
findUniqueName,
|
|
||||||
} from '@src/lang/create'
|
} from '@src/lang/create'
|
||||||
import { insertVariableAndOffsetPathToNode } from '@src/lang/modifyAst'
|
import {
|
||||||
|
createVariableExpressionsArray,
|
||||||
|
insertVariableAndOffsetPathToNode,
|
||||||
|
setCallInAst,
|
||||||
|
} from '@src/lang/modifyAst'
|
||||||
import {
|
import {
|
||||||
getEdgeTagCall,
|
getEdgeTagCall,
|
||||||
mutateAstWithTagForSketchSegment,
|
mutateAstWithTagForSketchSegment,
|
||||||
@ -16,20 +18,11 @@ import {
|
|||||||
import {
|
import {
|
||||||
getNodeFromPath,
|
getNodeFromPath,
|
||||||
getVariableExprsFromSelection,
|
getVariableExprsFromSelection,
|
||||||
createVariableExpressionsArray,
|
|
||||||
createPathToNodeForLastVariable,
|
|
||||||
valueOrVariable,
|
valueOrVariable,
|
||||||
} from '@src/lang/queryAst'
|
} from '@src/lang/queryAst'
|
||||||
import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils'
|
import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils'
|
||||||
import type {
|
import type { PathToNode, Program, VariableDeclaration } from '@src/lang/wasm'
|
||||||
CallExpressionKw,
|
|
||||||
PathToNode,
|
|
||||||
PipeExpression,
|
|
||||||
Program,
|
|
||||||
VariableDeclaration,
|
|
||||||
} from '@src/lang/wasm'
|
|
||||||
import type { KclCommandValue } from '@src/lib/commandTypes'
|
import type { KclCommandValue } from '@src/lib/commandTypes'
|
||||||
import { KCL_DEFAULT_CONSTANT_PREFIXES } from '@src/lib/constants'
|
|
||||||
import type { Selections } from '@src/lib/selections'
|
import type { Selections } from '@src/lib/selections'
|
||||||
import { err } from '@src/lib/trap'
|
import { err } from '@src/lib/trap'
|
||||||
|
|
||||||
@ -114,42 +107,10 @@ export function addExtrude({
|
|||||||
|
|
||||||
// 3. If edit, we assign the new function call declaration to the existing node,
|
// 3. If edit, we assign the new function call declaration to the existing node,
|
||||||
// otherwise just push to the end
|
// otherwise just push to the end
|
||||||
let pathToNode: PathToNode | undefined
|
const lastPath = variableExpressions.paths.pop() // TODO: check if this is correct
|
||||||
if (nodeToEdit) {
|
const pathToNode = setCallInAst(modifiedAst, call, nodeToEdit, lastPath)
|
||||||
const result = getNodeFromPath<CallExpressionKw>(
|
if (err(pathToNode)) {
|
||||||
modifiedAst,
|
return pathToNode
|
||||||
nodeToEdit,
|
|
||||||
'CallExpressionKw'
|
|
||||||
)
|
|
||||||
if (err(result)) {
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.assign(result.node, call)
|
|
||||||
pathToNode = nodeToEdit
|
|
||||||
} else {
|
|
||||||
const lastPathToNode: PathToNode | undefined =
|
|
||||||
variableExpressions.paths.pop()
|
|
||||||
if (sketchesExpr === null && lastPathToNode) {
|
|
||||||
const pipe = getNodeFromPath<PipeExpression>(
|
|
||||||
modifiedAst,
|
|
||||||
lastPathToNode,
|
|
||||||
'PipeExpression'
|
|
||||||
)
|
|
||||||
if (err(pipe)) {
|
|
||||||
return pipe
|
|
||||||
}
|
|
||||||
pipe.node.body.push(call)
|
|
||||||
pathToNode = lastPathToNode
|
|
||||||
} else {
|
|
||||||
const name = findUniqueName(
|
|
||||||
modifiedAst,
|
|
||||||
KCL_DEFAULT_CONSTANT_PREFIXES.EXTRUDE
|
|
||||||
)
|
|
||||||
const declaration = createVariableDeclaration(name, call)
|
|
||||||
modifiedAst.body.push(declaration)
|
|
||||||
pathToNode = createPathToNodeForLastVariable(modifiedAst)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -220,41 +181,10 @@ export function addSweep({
|
|||||||
|
|
||||||
// 3. If edit, we assign the new function call declaration to the existing node,
|
// 3. If edit, we assign the new function call declaration to the existing node,
|
||||||
// otherwise just push to the end
|
// otherwise just push to the end
|
||||||
let pathToNode: PathToNode | undefined
|
const lastPath = variableExprs.paths.pop() // TODO: check if this is correct
|
||||||
if (nodeToEdit) {
|
const pathToNode = setCallInAst(modifiedAst, call, nodeToEdit, lastPath)
|
||||||
const result = getNodeFromPath<CallExpressionKw>(
|
if (err(pathToNode)) {
|
||||||
modifiedAst,
|
return pathToNode
|
||||||
nodeToEdit,
|
|
||||||
'CallExpressionKw'
|
|
||||||
)
|
|
||||||
if (err(result)) {
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.assign(result.node, call)
|
|
||||||
pathToNode = nodeToEdit
|
|
||||||
} else {
|
|
||||||
const lastPathToNode: PathToNode | undefined = variableExprs.paths.pop()
|
|
||||||
if (sketchesExpr === null && lastPathToNode) {
|
|
||||||
const pipe = getNodeFromPath<PipeExpression>(
|
|
||||||
modifiedAst,
|
|
||||||
lastPathToNode,
|
|
||||||
'PipeExpression'
|
|
||||||
)
|
|
||||||
if (err(pipe)) {
|
|
||||||
return pipe
|
|
||||||
}
|
|
||||||
pipe.node.body.push(call)
|
|
||||||
pathToNode = lastPathToNode
|
|
||||||
} else {
|
|
||||||
const name = findUniqueName(
|
|
||||||
modifiedAst,
|
|
||||||
KCL_DEFAULT_CONSTANT_PREFIXES.SWEEP
|
|
||||||
)
|
|
||||||
const declaration = createVariableDeclaration(name, call)
|
|
||||||
modifiedAst.body.push(declaration)
|
|
||||||
pathToNode = createPathToNodeForLastVariable(modifiedAst)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -310,42 +240,10 @@ export function addLoft({
|
|||||||
|
|
||||||
// 3. If edit, we assign the new function call declaration to the existing node,
|
// 3. If edit, we assign the new function call declaration to the existing node,
|
||||||
// otherwise just push to the end
|
// otherwise just push to the end
|
||||||
let pathToNode: PathToNode | undefined
|
const lastPath = variableExprs.paths.pop() // TODO: check if this is correct
|
||||||
if (nodeToEdit) {
|
const pathToNode = setCallInAst(modifiedAst, call, nodeToEdit, lastPath)
|
||||||
const result = getNodeFromPath<CallExpressionKw>(
|
if (err(pathToNode)) {
|
||||||
modifiedAst,
|
return pathToNode
|
||||||
nodeToEdit,
|
|
||||||
'CallExpressionKw'
|
|
||||||
)
|
|
||||||
if (err(result)) {
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.assign(result.node, call)
|
|
||||||
pathToNode = nodeToEdit
|
|
||||||
} else {
|
|
||||||
const lastPathToNode: PathToNode | undefined = variableExprs.paths.pop()
|
|
||||||
if (sketchesExpr === null && lastPathToNode) {
|
|
||||||
const pipe = getNodeFromPath<PipeExpression>(
|
|
||||||
modifiedAst,
|
|
||||||
lastPathToNode,
|
|
||||||
'PipeExpression'
|
|
||||||
)
|
|
||||||
if (err(pipe)) {
|
|
||||||
return pipe
|
|
||||||
}
|
|
||||||
pipe.node.body.push(call)
|
|
||||||
pathToNode = lastPathToNode
|
|
||||||
} else {
|
|
||||||
const name = findUniqueName(
|
|
||||||
modifiedAst,
|
|
||||||
KCL_DEFAULT_CONSTANT_PREFIXES.LOFT
|
|
||||||
)
|
|
||||||
const declaration = createVariableDeclaration(name, call)
|
|
||||||
modifiedAst.body.push(declaration)
|
|
||||||
const toFirstKwarg = !!vDegree
|
|
||||||
pathToNode = createPathToNodeForLastVariable(modifiedAst, toFirstKwarg)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -445,41 +343,10 @@ export function addRevolve({
|
|||||||
|
|
||||||
// 3. If edit, we assign the new function call declaration to the existing node,
|
// 3. If edit, we assign the new function call declaration to the existing node,
|
||||||
// otherwise just push to the end
|
// otherwise just push to the end
|
||||||
let pathToNode: PathToNode | undefined
|
const lastPath = variableExprs.paths.pop() // TODO: check if this is correct
|
||||||
if (nodeToEdit) {
|
const pathToNode = setCallInAst(modifiedAst, call, nodeToEdit, lastPath)
|
||||||
const result = getNodeFromPath<CallExpressionKw>(
|
if (err(pathToNode)) {
|
||||||
modifiedAst,
|
return pathToNode
|
||||||
nodeToEdit,
|
|
||||||
'CallExpressionKw'
|
|
||||||
)
|
|
||||||
if (err(result)) {
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.assign(result.node, call)
|
|
||||||
pathToNode = nodeToEdit
|
|
||||||
} else {
|
|
||||||
const lastPathToNode: PathToNode | undefined = variableExprs.paths.pop()
|
|
||||||
if (sketchesExpr === null && lastPathToNode) {
|
|
||||||
const pipe = getNodeFromPath<PipeExpression>(
|
|
||||||
modifiedAst,
|
|
||||||
lastPathToNode,
|
|
||||||
'PipeExpression'
|
|
||||||
)
|
|
||||||
if (err(pipe)) {
|
|
||||||
return pipe
|
|
||||||
}
|
|
||||||
pipe.node.body.push(call)
|
|
||||||
pathToNode = lastPathToNode
|
|
||||||
} else {
|
|
||||||
const name = findUniqueName(
|
|
||||||
modifiedAst,
|
|
||||||
KCL_DEFAULT_CONSTANT_PREFIXES.REVOLVE
|
|
||||||
)
|
|
||||||
const declaration = createVariableDeclaration(name, call)
|
|
||||||
modifiedAst.body.push(declaration)
|
|
||||||
pathToNode = createPathToNodeForLastVariable(modifiedAst)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
@ -15,6 +15,7 @@ import {
|
|||||||
findAllPreviousVariables,
|
findAllPreviousVariables,
|
||||||
findUsesOfTagInPipe,
|
findUsesOfTagInPipe,
|
||||||
getNodeFromPath,
|
getNodeFromPath,
|
||||||
|
getVariableExprsFromSelection,
|
||||||
hasSketchPipeBeenExtruded,
|
hasSketchPipeBeenExtruded,
|
||||||
isCursorInFunctionDefinition,
|
isCursorInFunctionDefinition,
|
||||||
isNodeSafeToReplace,
|
isNodeSafeToReplace,
|
||||||
@ -27,7 +28,7 @@ import { topLevelRange } from '@src/lang/util'
|
|||||||
import type { Identifier, PathToNode } from '@src/lang/wasm'
|
import type { Identifier, PathToNode } from '@src/lang/wasm'
|
||||||
import { assertParse, recast } from '@src/lang/wasm'
|
import { assertParse, recast } from '@src/lang/wasm'
|
||||||
import { initPromise } from '@src/lang/wasmUtils'
|
import { initPromise } from '@src/lang/wasmUtils'
|
||||||
import { type Selection } from '@src/lib/selections'
|
import type { Selections, Selection } from '@src/lib/selections'
|
||||||
import { enginelessExecutor } from '@src/lib/testHelpers'
|
import { enginelessExecutor } from '@src/lib/testHelpers'
|
||||||
import { err } from '@src/lib/trap'
|
import { err } from '@src/lib/trap'
|
||||||
|
|
||||||
@ -778,3 +779,184 @@ describe('Testing specific sketch getNodeFromPath workflow', () => {
|
|||||||
expect(result).toEqual(false)
|
expect(result).toEqual(false)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('Testing getVariableExprsFromSelection', () => {
|
||||||
|
it('should find the variable expr in a simple profile selection', async () => {
|
||||||
|
const circleProfileInVar = `sketch001 = startSketchOn(XY)
|
||||||
|
profile001 = circle(sketch001, center = [0, 0], radius = 1)
|
||||||
|
`
|
||||||
|
const ast = assertParse(circleProfileInVar)
|
||||||
|
const { artifactGraph } = await enginelessExecutor(ast)
|
||||||
|
const artifact = artifactGraph.values().find((a) => a.type === 'path')
|
||||||
|
if (!artifact) {
|
||||||
|
throw new Error('Artifact not found in the graph')
|
||||||
|
}
|
||||||
|
const selections: Selections = {
|
||||||
|
graphSelections: [
|
||||||
|
{
|
||||||
|
codeRef: artifact.codeRef,
|
||||||
|
artifact,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
otherSelections: [],
|
||||||
|
}
|
||||||
|
const variableExprs = getVariableExprsFromSelection(selections, ast)
|
||||||
|
if (err(variableExprs)) throw variableExprs
|
||||||
|
|
||||||
|
expect(variableExprs.exprs).toHaveLength(1)
|
||||||
|
if (variableExprs.exprs[0].type !== 'Name') {
|
||||||
|
throw new Error(`Expected Name, got ${variableExprs.exprs[0].type}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(variableExprs.exprs[0].name.name).toEqual('profile001')
|
||||||
|
|
||||||
|
expect(variableExprs.paths).toHaveLength(1)
|
||||||
|
expect(variableExprs.paths[0]).toEqual([
|
||||||
|
['body', ''],
|
||||||
|
[1, 'index'],
|
||||||
|
['declaration', 'VariableDeclaration'],
|
||||||
|
['init', ''],
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return the pipe substitution symbol in a variable-less simple profile selection', async () => {
|
||||||
|
const circleProfileInVar = `startSketchOn(XY)
|
||||||
|
|> circle(center = [0, 0], radius = 1)
|
||||||
|
`
|
||||||
|
const ast = assertParse(circleProfileInVar)
|
||||||
|
const { artifactGraph } = await enginelessExecutor(ast)
|
||||||
|
const artifact = artifactGraph.values().find((a) => a.type === 'path')
|
||||||
|
if (!artifact) {
|
||||||
|
throw new Error('Artifact not found in the graph')
|
||||||
|
}
|
||||||
|
const selections: Selections = {
|
||||||
|
graphSelections: [
|
||||||
|
{
|
||||||
|
codeRef: artifact.codeRef,
|
||||||
|
artifact,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
otherSelections: [],
|
||||||
|
}
|
||||||
|
const variableExprs = getVariableExprsFromSelection(selections, ast)
|
||||||
|
if (err(variableExprs)) throw variableExprs
|
||||||
|
|
||||||
|
expect(variableExprs.exprs).toHaveLength(1)
|
||||||
|
expect(variableExprs.exprs[0].type).toEqual('PipeSubstitution')
|
||||||
|
|
||||||
|
expect(variableExprs.paths).toHaveLength(1)
|
||||||
|
expect(variableExprs.paths[0]).toEqual([
|
||||||
|
['body', ''],
|
||||||
|
[0, 'index'],
|
||||||
|
['expression', 'ExpressionStatement'],
|
||||||
|
['body', 'PipeExpression'],
|
||||||
|
[1, 'index'],
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should find the variable exprs in a multi profile selection ', async () => {
|
||||||
|
const circleProfileInVar = `sketch001 = startSketchOn(XY)
|
||||||
|
profile001 = circle(sketch001, center = [0, 0], radius = 1)
|
||||||
|
profile002 = circle(sketch001, center = [2, 2], radius = 1)
|
||||||
|
`
|
||||||
|
const ast = assertParse(circleProfileInVar)
|
||||||
|
const { artifactGraph } = await enginelessExecutor(ast)
|
||||||
|
const artifacts = [...artifactGraph.values()].filter(
|
||||||
|
(a) => a.type === 'path'
|
||||||
|
)
|
||||||
|
if (!artifacts || artifacts.length !== 2) {
|
||||||
|
throw new Error('Artifact not found in the graph')
|
||||||
|
}
|
||||||
|
const selections: Selections = {
|
||||||
|
graphSelections: artifacts.map((artifact) => {
|
||||||
|
return {
|
||||||
|
codeRef: artifact.codeRef,
|
||||||
|
artifact,
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
otherSelections: [],
|
||||||
|
}
|
||||||
|
const variableExprs = getVariableExprsFromSelection(selections, ast)
|
||||||
|
if (err(variableExprs)) throw variableExprs
|
||||||
|
|
||||||
|
expect(variableExprs.exprs).toHaveLength(2)
|
||||||
|
if (variableExprs.exprs[0].type !== 'Name') {
|
||||||
|
throw new Error(`Expected Name, got ${variableExprs.exprs[0].type}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (variableExprs.exprs[1].type !== 'Name') {
|
||||||
|
throw new Error(`Expected Name, got ${variableExprs.exprs[1].type}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(variableExprs.exprs[0].name.name).toEqual('profile001')
|
||||||
|
expect(variableExprs.exprs[1].name.name).toEqual('profile002')
|
||||||
|
|
||||||
|
expect(variableExprs.paths).toHaveLength(2)
|
||||||
|
expect(variableExprs.paths[0]).toEqual([
|
||||||
|
['body', ''],
|
||||||
|
[1, 'index'],
|
||||||
|
['declaration', 'VariableDeclaration'],
|
||||||
|
['init', ''],
|
||||||
|
])
|
||||||
|
expect(variableExprs.paths[1]).toEqual([
|
||||||
|
['body', ''],
|
||||||
|
[2, 'index'],
|
||||||
|
['declaration', 'VariableDeclaration'],
|
||||||
|
['init', ''],
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return the pipe substitution symbol and a variable name in a complex multi profile selection', async () => {
|
||||||
|
const circleProfileInVar = `startSketchOn(XY)
|
||||||
|
|> circle(center = [0, 0], radius = 1)
|
||||||
|
profile002 = circle(sketch001, center = [2, 2], radius = 1)
|
||||||
|
`
|
||||||
|
const ast = assertParse(circleProfileInVar)
|
||||||
|
const { artifactGraph } = await enginelessExecutor(ast)
|
||||||
|
const artifacts = [...artifactGraph.values()].filter(
|
||||||
|
(a) => a.type === 'path'
|
||||||
|
)
|
||||||
|
if (!artifacts || artifacts.length !== 2) {
|
||||||
|
throw new Error('Artifact not found in the graph')
|
||||||
|
}
|
||||||
|
const selections: Selections = {
|
||||||
|
graphSelections: artifacts.map((artifact) => {
|
||||||
|
return {
|
||||||
|
codeRef: artifact.codeRef,
|
||||||
|
artifact,
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
otherSelections: [],
|
||||||
|
}
|
||||||
|
const variableExprs = getVariableExprsFromSelection(selections, ast)
|
||||||
|
if (err(variableExprs)) throw variableExprs
|
||||||
|
|
||||||
|
expect(variableExprs.exprs).toHaveLength(2)
|
||||||
|
if (variableExprs.exprs[0].type !== 'PipeSubstitution') {
|
||||||
|
throw new Error(
|
||||||
|
`Expected PipeSubstitution, got ${variableExprs.exprs[0].type}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (variableExprs.exprs[1].type !== 'Name') {
|
||||||
|
throw new Error(`Expected Name, got ${variableExprs.exprs[1].type}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(variableExprs.exprs[1].name.name).toEqual('profile002')
|
||||||
|
|
||||||
|
expect(variableExprs.paths).toHaveLength(2)
|
||||||
|
expect(variableExprs.paths[0]).toEqual([
|
||||||
|
['body', ''],
|
||||||
|
[0, 'index'],
|
||||||
|
['expression', 'ExpressionStatement'],
|
||||||
|
['body', 'PipeExpression'],
|
||||||
|
[1, 'index'],
|
||||||
|
])
|
||||||
|
expect(variableExprs.paths[1]).toEqual([
|
||||||
|
['body', ''],
|
||||||
|
[1, 'index'],
|
||||||
|
['declaration', 'VariableDeclaration'],
|
||||||
|
['init', ''],
|
||||||
|
])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
@ -2,11 +2,7 @@ import type { FunctionExpression } from '@rust/kcl-lib/bindings/FunctionExpressi
|
|||||||
import type { ImportStatement } from '@rust/kcl-lib/bindings/ImportStatement'
|
import type { ImportStatement } from '@rust/kcl-lib/bindings/ImportStatement'
|
||||||
import type { Node } from '@rust/kcl-lib/bindings/Node'
|
import type { Node } from '@rust/kcl-lib/bindings/Node'
|
||||||
import type { TypeDeclaration } from '@rust/kcl-lib/bindings/TypeDeclaration'
|
import type { TypeDeclaration } from '@rust/kcl-lib/bindings/TypeDeclaration'
|
||||||
import {
|
import { createLocalName, createPipeSubstitution } from '@src/lang/create'
|
||||||
createLocalName,
|
|
||||||
createPipeSubstitution,
|
|
||||||
createArrayExpression,
|
|
||||||
} from '@src/lang/create'
|
|
||||||
import type { ToolTip } from '@src/lang/langHelpers'
|
import type { ToolTip } from '@src/lang/langHelpers'
|
||||||
import { splitPathAtLastIndex } from '@src/lang/modifyAst'
|
import { splitPathAtLastIndex } from '@src/lang/modifyAst'
|
||||||
import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils'
|
import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils'
|
||||||
@ -1079,17 +1075,19 @@ export function getVariableExprsFromSelection(
|
|||||||
// Pointing to same variable case
|
// Pointing to same variable case
|
||||||
paths.push(nodeToEdit)
|
paths.push(nodeToEdit)
|
||||||
exprs.push(createPipeSubstitution())
|
exprs.push(createPipeSubstitution())
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Pointing to different variable case
|
// Pointing to different variable case
|
||||||
paths.push(sketchVariable.deepPath)
|
paths.push(sketchVariable.deepPath)
|
||||||
exprs.push(createLocalName(name))
|
exprs.push(createLocalName(name))
|
||||||
} else {
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// No variable case
|
// No variable case
|
||||||
paths.push(sketchVariable.deepPath)
|
paths.push(sketchVariable.deepPath)
|
||||||
exprs.push(createPipeSubstitution())
|
exprs.push(createPipeSubstitution())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (exprs.length === 0) {
|
if (exprs.length === 0) {
|
||||||
return new Error("Couldn't map selections to program references")
|
return new Error("Couldn't map selections to program references")
|
||||||
@ -1098,44 +1096,6 @@ export function getVariableExprsFromSelection(
|
|||||||
return { exprs, paths }
|
return { exprs, paths }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create an array expression for variables,
|
|
||||||
// or keep it null if all are PipeSubstitutions
|
|
||||||
export function createVariableExpressionsArray(sketches: Expr[]) {
|
|
||||||
let sketchesExpr: Expr | null = null
|
|
||||||
if (sketches.every((s) => s.type === 'PipeSubstitution')) {
|
|
||||||
// Keeping null so we don't even put it the % sign
|
|
||||||
} else if (sketches.length === 1) {
|
|
||||||
sketchesExpr = sketches[0]
|
|
||||||
} else {
|
|
||||||
sketchesExpr = createArrayExpression(sketches)
|
|
||||||
}
|
|
||||||
return sketchesExpr
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a path to node to the last variable declaroator of an ast
|
|
||||||
// Optionally, can point to the first kwarg of the CallExpressionKw
|
|
||||||
export function createPathToNodeForLastVariable(
|
|
||||||
ast: Node<Program>,
|
|
||||||
toFirstKwarg = true
|
|
||||||
): PathToNode {
|
|
||||||
const argIndex = 0 // first kwarg for all sweeps here
|
|
||||||
const pathToCall: PathToNode = [
|
|
||||||
['body', ''],
|
|
||||||
[ast.body.length - 1, 'index'],
|
|
||||||
['declaration', 'VariableDeclaration'],
|
|
||||||
['init', 'VariableDeclarator'],
|
|
||||||
]
|
|
||||||
if (toFirstKwarg) {
|
|
||||||
pathToCall.push(
|
|
||||||
['arguments', 'CallExpressionKw'],
|
|
||||||
[argIndex, ARG_INDEX_FIELD],
|
|
||||||
['arg', LABELED_ARG_FIELD]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return pathToCall
|
|
||||||
}
|
|
||||||
|
|
||||||
// Go from the sketches argument in a KCL sweep call declaration
|
// Go from the sketches argument in a KCL sweep call declaration
|
||||||
// to a list of graph selections, useful for edit flows.
|
// to a list of graph selections, useful for edit flows.
|
||||||
// Somewhat of an inverse of getSketchExprsFromSelection.
|
// Somewhat of an inverse of getSketchExprsFromSelection.
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import { VITE_KC_API_BASE_URL } from '@src/env'
|
|
||||||
import { UAParser } from 'ua-parser-js'
|
import { UAParser } from 'ua-parser-js'
|
||||||
|
|
||||||
import type { OsInfo } from '@rust/kcl-lib/bindings/OsInfo'
|
import type { OsInfo } from '@rust/kcl-lib/bindings/OsInfo'
|
||||||
@ -11,6 +10,7 @@ import { isDesktop } from '@src/lib/isDesktop'
|
|||||||
import type RustContext from '@src/lib/rustContext'
|
import type RustContext from '@src/lib/rustContext'
|
||||||
import screenshot from '@src/lib/screenshot'
|
import screenshot from '@src/lib/screenshot'
|
||||||
import { APP_VERSION } from '@src/routes/utils'
|
import { APP_VERSION } from '@src/routes/utils'
|
||||||
|
import { withAPIBaseURL } from '@src/lib/withBaseURL'
|
||||||
|
|
||||||
/* eslint-disable suggest-no-throw/suggest-no-throw --
|
/* eslint-disable suggest-no-throw/suggest-no-throw --
|
||||||
* All the throws in CoreDumpManager are intentional and should be caught and handled properly
|
* All the throws in CoreDumpManager are intentional and should be caught and handled properly
|
||||||
@ -35,7 +35,7 @@ export class CoreDumpManager {
|
|||||||
codeManager: CodeManager
|
codeManager: CodeManager
|
||||||
rustContext: RustContext
|
rustContext: RustContext
|
||||||
token: string | undefined
|
token: string | undefined
|
||||||
baseUrl: string = VITE_KC_API_BASE_URL
|
baseUrl: string = withAPIBaseURL('')
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
engineCommandManager: EngineCommandManager,
|
engineCommandManager: EngineCommandManager,
|
||||||
|
@ -26,6 +26,7 @@ import { err } from '@src/lib/trap'
|
|||||||
import type { DeepPartial } from '@src/lib/types'
|
import type { DeepPartial } from '@src/lib/types'
|
||||||
import { getInVariableCase } from '@src/lib/utils'
|
import { getInVariableCase } from '@src/lib/utils'
|
||||||
import { IS_STAGING } from '@src/routes/utils'
|
import { IS_STAGING } from '@src/routes/utils'
|
||||||
|
import { withAPIBaseURL } from '@src/lib/withBaseURL'
|
||||||
|
|
||||||
export async function renameProjectDirectory(
|
export async function renameProjectDirectory(
|
||||||
projectPath: string,
|
projectPath: string,
|
||||||
@ -697,7 +698,9 @@ export const readTokenFile = async () => {
|
|||||||
export const writeTokenFile = async (token: string) => {
|
export const writeTokenFile = async (token: string) => {
|
||||||
const tokenFilePath = await getTokenFilePath()
|
const tokenFilePath = await getTokenFilePath()
|
||||||
if (err(token)) return Promise.reject(token)
|
if (err(token)) return Promise.reject(token)
|
||||||
return window.electron.writeFile(tokenFilePath, token)
|
const result = window.electron.writeFile(tokenFilePath, token)
|
||||||
|
console.log('token written to disk')
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
export const writeTelemetryFile = async (content: string) => {
|
export const writeTelemetryFile = async (content: string) => {
|
||||||
@ -722,12 +725,9 @@ export const setState = async (state: Project | undefined): Promise<void> => {
|
|||||||
appStateStore = state
|
appStateStore = state
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getUser = async (
|
export const getUser = async (token: string): Promise<Models['User_type']> => {
|
||||||
token: string,
|
|
||||||
hostname: string
|
|
||||||
): Promise<Models['User_type']> => {
|
|
||||||
try {
|
try {
|
||||||
const user = await fetch(`${hostname}/users/me`, {
|
const user = await fetch(withAPIBaseURL('/users/me'), {
|
||||||
headers: new Headers({
|
headers: new Headers({
|
||||||
Authorization: `Bearer ${token}`,
|
Authorization: `Bearer ${token}`,
|
||||||
}),
|
}),
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { VITE_KC_API_BASE_URL, VITE_KC_SITE_APP_URL } from '@src/env'
|
import { VITE_KC_SITE_APP_URL } from '@src/env'
|
||||||
import toast from 'react-hot-toast'
|
import toast from 'react-hot-toast'
|
||||||
|
|
||||||
import { stringToBase64 } from '@src/lib/base64'
|
import { stringToBase64 } from '@src/lib/base64'
|
||||||
@ -7,6 +7,7 @@ import {
|
|||||||
CREATE_FILE_URL_PARAM,
|
CREATE_FILE_URL_PARAM,
|
||||||
} from '@src/lib/constants'
|
} from '@src/lib/constants'
|
||||||
import { err } from '@src/lib/trap'
|
import { err } from '@src/lib/trap'
|
||||||
|
import { withAPIBaseURL } from '@src/lib/withBaseURL'
|
||||||
|
|
||||||
export interface FileLinkParams {
|
export interface FileLinkParams {
|
||||||
code: string
|
code: string
|
||||||
@ -96,7 +97,7 @@ export async function createShortlink(
|
|||||||
if (password) {
|
if (password) {
|
||||||
body.password = password
|
body.password = password
|
||||||
}
|
}
|
||||||
const response = await fetch(`${VITE_KC_API_BASE_URL}/user/shortlinks`, {
|
const response = await fetch(withAPIBaseURL('/user/shortlinks'), {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-type': 'application/json',
|
'Content-type': 'application/json',
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import type { SelectionRange } from '@codemirror/state'
|
import type { SelectionRange } from '@codemirror/state'
|
||||||
import { EditorSelection, Transaction } from '@codemirror/state'
|
import { EditorSelection, Transaction } from '@codemirror/state'
|
||||||
import type { Models } from '@kittycad/lib'
|
import type { Models } from '@kittycad/lib'
|
||||||
import { VITE_KC_API_BASE_URL, VITE_KC_SITE_BASE_URL } from '@src/env'
|
import { VITE_KC_SITE_BASE_URL } from '@src/env'
|
||||||
import { diffLines } from 'diff'
|
import { diffLines } from 'diff'
|
||||||
import toast from 'react-hot-toast'
|
import toast from 'react-hot-toast'
|
||||||
import type { TextToCadMultiFileIteration_type } from '@kittycad/lib/dist/types/src/models'
|
import type { TextToCadMultiFileIteration_type } from '@kittycad/lib/dist/types/src/models'
|
||||||
@ -28,6 +28,7 @@ import { uuidv4 } from '@src/lib/utils'
|
|||||||
import type { File as KittyCadLibFile } from '@kittycad/lib/dist/types/src/models'
|
import type { File as KittyCadLibFile } from '@kittycad/lib/dist/types/src/models'
|
||||||
import type { FileMeta } from '@src/lib/types'
|
import type { FileMeta } from '@src/lib/types'
|
||||||
import type { RequestedKCLFile } from '@src/machines/systemIO/utils'
|
import type { RequestedKCLFile } from '@src/machines/systemIO/utils'
|
||||||
|
import { withAPIBaseURL } from '@src/lib/withBaseURL'
|
||||||
|
|
||||||
type KclFileMetaMap = {
|
type KclFileMetaMap = {
|
||||||
[execStateFileNamesIndex: number]: Extract<FileMeta, { type: 'kcl' }>
|
[execStateFileNamesIndex: number]: Extract<FileMeta, { type: 'kcl' }>
|
||||||
@ -77,7 +78,7 @@ async function submitTextToCadRequest(
|
|||||||
})
|
})
|
||||||
|
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`${VITE_KC_API_BASE_URL}/ml/text-to-cad/multi-file/iteration`,
|
withAPIBaseURL('/ml/text-to-cad/multi-file/iteration'),
|
||||||
{
|
{
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
@ -304,7 +305,7 @@ export async function getPromptToEditResult(
|
|||||||
id: string,
|
id: string,
|
||||||
token?: string
|
token?: string
|
||||||
): Promise<Models['TextToCadMultiFileIteration_type'] | Error> {
|
): Promise<Models['TextToCadMultiFileIteration_type'] | Error> {
|
||||||
const url = VITE_KC_API_BASE_URL + '/async/operations/' + id
|
const url = withAPIBaseURL(`/async/operations/${id}`)
|
||||||
const data: Models['TextToCadMultiFileIteration_type'] | Error =
|
const data: Models['TextToCadMultiFileIteration_type'] | Error =
|
||||||
await crossPlatformFetch(
|
await crossPlatformFetch(
|
||||||
url,
|
url,
|
||||||
@ -340,7 +341,7 @@ export async function doPromptEdit({
|
|||||||
;(window as any).process = {
|
;(window as any).process = {
|
||||||
env: {
|
env: {
|
||||||
ZOO_API_TOKEN: token,
|
ZOO_API_TOKEN: token,
|
||||||
ZOO_HOST: VITE_KC_API_BASE_URL,
|
ZOO_HOST: withAPIBaseURL(''),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { VITE_KC_API_BASE_URL } from '@src/env'
|
import { withAPIBaseURL } from '@src/lib/withBaseURL'
|
||||||
|
|
||||||
import EditorManager from '@src/editor/manager'
|
import EditorManager from '@src/editor/manager'
|
||||||
import { KclManager } from '@src/lang/KclSingleton'
|
import { KclManager } from '@src/lang/KclSingleton'
|
||||||
@ -171,7 +171,7 @@ const appMachine = setup({
|
|||||||
systemId: BILLING,
|
systemId: BILLING,
|
||||||
input: {
|
input: {
|
||||||
...BILLING_CONTEXT_DEFAULTS,
|
...BILLING_CONTEXT_DEFAULTS,
|
||||||
urlUserService: VITE_KC_API_BASE_URL,
|
urlUserService: withAPIBaseURL(''),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import type { 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 toast from 'react-hot-toast'
|
||||||
import type { NavigateFunction } from 'react-router-dom'
|
import type { NavigateFunction } from 'react-router-dom'
|
||||||
import {
|
import {
|
||||||
@ -19,6 +18,7 @@ import { err, reportRejection } from '@src/lib/trap'
|
|||||||
import { toSync } from '@src/lib/utils'
|
import { toSync } from '@src/lib/utils'
|
||||||
import { getAllSubDirectoriesAtProjectRoot } from '@src/machines/systemIO/snapshotContext'
|
import { getAllSubDirectoriesAtProjectRoot } from '@src/machines/systemIO/snapshotContext'
|
||||||
import { joinOSPaths } from '@src/lib/paths'
|
import { joinOSPaths } from '@src/lib/paths'
|
||||||
|
import { withAPIBaseURL } from '@src/lib/withBaseURL'
|
||||||
|
|
||||||
export async function submitTextToCadPrompt(
|
export async function submitTextToCadPrompt(
|
||||||
prompt: string,
|
prompt: string,
|
||||||
@ -32,7 +32,7 @@ export async function submitTextToCadPrompt(
|
|||||||
kcl_version: kclManager.kclVersion,
|
kcl_version: kclManager.kclVersion,
|
||||||
}
|
}
|
||||||
// Glb has a smaller footprint than gltf, should we want to render it.
|
// Glb has a smaller footprint than gltf, should we want to render it.
|
||||||
const url = VITE_KC_API_BASE_URL + '/ai/text-to-cad/glb?kcl=true'
|
const url = withAPIBaseURL('/ai/text-to-cad/glb?kcl=true')
|
||||||
const data: Models['TextToCad_type'] | Error = await crossPlatformFetch(
|
const data: Models['TextToCad_type'] | Error = await crossPlatformFetch(
|
||||||
url,
|
url,
|
||||||
{
|
{
|
||||||
@ -58,7 +58,7 @@ export async function getTextToCadResult(
|
|||||||
id: string,
|
id: string,
|
||||||
token?: string
|
token?: string
|
||||||
): Promise<Models['TextToCad_type'] | Error> {
|
): Promise<Models['TextToCad_type'] | Error> {
|
||||||
const url = VITE_KC_API_BASE_URL + '/user/text-to-cad/' + id
|
const url = withAPIBaseURL(`/user/text-to-cad/${id}`)
|
||||||
const data: Models['TextToCad_type'] | Error = await crossPlatformFetch(
|
const data: Models['TextToCad_type'] | Error = await crossPlatformFetch(
|
||||||
url,
|
url,
|
||||||
{
|
{
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
import type { Models } from '@kittycad/lib/dist/types/src'
|
import type { Models } from '@kittycad/lib/dist/types/src'
|
||||||
import { VITE_KC_API_BASE_URL } from '@src/env'
|
|
||||||
import crossPlatformFetch from '@src/lib/crossPlatformFetch'
|
import crossPlatformFetch from '@src/lib/crossPlatformFetch'
|
||||||
|
import { withAPIBaseURL } from '@src/lib/withBaseURL'
|
||||||
|
|
||||||
export async function sendTelemetry(
|
export async function sendTelemetry(
|
||||||
id: string,
|
id: string,
|
||||||
feedback: Models['MlFeedback_type'],
|
feedback: Models['MlFeedback_type'],
|
||||||
token?: string
|
token?: string
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const url =
|
const url = withAPIBaseURL(`/user/text-to-cad/${id}?feedback=${feedback}`)
|
||||||
VITE_KC_API_BASE_URL + '/user/text-to-cad/' + id + '?feedback=' + feedback
|
|
||||||
await crossPlatformFetch(
|
await crossPlatformFetch(
|
||||||
url,
|
url,
|
||||||
{
|
{
|
||||||
|
34
src/lib/withBaseURL.test.ts
Normal file
34
src/lib/withBaseURL.test.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { withAPIBaseURL } from '@src/lib/withBaseURL'
|
||||||
|
|
||||||
|
describe('withBaseURL', () => {
|
||||||
|
/**
|
||||||
|
* running in the development environment
|
||||||
|
* the .env.development should load
|
||||||
|
*/
|
||||||
|
describe('withAPIBaseUrl', () => {
|
||||||
|
it('should return base url', () => {
|
||||||
|
const expected = 'https://api.dev.zoo.dev'
|
||||||
|
const actual = withAPIBaseURL('')
|
||||||
|
expect(actual).toBe(expected)
|
||||||
|
})
|
||||||
|
it('should return base url with /users', () => {
|
||||||
|
const expected = 'https://api.dev.zoo.dev/users'
|
||||||
|
const actual = withAPIBaseURL('/users')
|
||||||
|
expect(actual).toBe(expected)
|
||||||
|
})
|
||||||
|
it('should return a longer base url with /oauth2/token/revoke', () => {
|
||||||
|
const expected = 'https://api.dev.zoo.dev/oauth2/token/revoke'
|
||||||
|
const actual = withAPIBaseURL('/oauth2/token/revoke')
|
||||||
|
expect(actual).toBe(expected)
|
||||||
|
})
|
||||||
|
it('should ensure base url does not have ending slash', () => {
|
||||||
|
const expected = 'https://api.dev.zoo.dev'
|
||||||
|
const actual = withAPIBaseURL('')
|
||||||
|
expect(actual).toBe(expected)
|
||||||
|
const expectedEndsWith = expected[expected.length - 1]
|
||||||
|
const actualEndsWith = actual[actual.length - 1]
|
||||||
|
expect(actual).toBe(expected)
|
||||||
|
expect(actualEndsWith).toBe(expectedEndsWith)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
@ -1,5 +1,5 @@
|
|||||||
import { VITE_KC_API_BASE_URL } from '@src/env'
|
import { VITE_KC_API_BASE_URL } from '@src/env'
|
||||||
|
|
||||||
export default function withBaseUrl(path: string): string {
|
export function withAPIBaseURL(path: string): string {
|
||||||
return VITE_KC_API_BASE_URL + path
|
return VITE_KC_API_BASE_URL + path
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import type { Models } from '@kittycad/lib'
|
import type { Models } from '@kittycad/lib'
|
||||||
import { VITE_KC_API_BASE_URL, VITE_KC_DEV_TOKEN } from '@src/env'
|
import { VITE_KC_DEV_TOKEN } from '@src/env'
|
||||||
import { assign, fromPromise, setup } from 'xstate'
|
import { assign, fromPromise, setup } from 'xstate'
|
||||||
|
|
||||||
import { COOKIE_NAME, OAUTH2_DEVICE_CLIENT_ID } from '@src/lib/constants'
|
import { COOKIE_NAME, OAUTH2_DEVICE_CLIENT_ID } from '@src/lib/constants'
|
||||||
@ -10,10 +10,7 @@ import {
|
|||||||
} from '@src/lib/desktop'
|
} from '@src/lib/desktop'
|
||||||
import { isDesktop } from '@src/lib/isDesktop'
|
import { isDesktop } from '@src/lib/isDesktop'
|
||||||
import { markOnce } from '@src/lib/performance'
|
import { markOnce } from '@src/lib/performance'
|
||||||
import {
|
import { withAPIBaseURL } from '@src/lib/withBaseURL'
|
||||||
default as withBaseURL,
|
|
||||||
default as withBaseUrl,
|
|
||||||
} from '@src/lib/withBaseURL'
|
|
||||||
import { ACTOR_IDS } from '@src/machines/machineConstants'
|
import { ACTOR_IDS } from '@src/machines/machineConstants'
|
||||||
|
|
||||||
export interface UserContext {
|
export interface UserContext {
|
||||||
@ -31,11 +28,21 @@ export type Events =
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const TOKEN_PERSIST_KEY = 'TOKEN_PERSIST_KEY'
|
export const TOKEN_PERSIST_KEY = 'TOKEN_PERSIST_KEY'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine which token do we have persisted to initialize the auth machine
|
||||||
|
*/
|
||||||
|
const persistedCookie = getCookie(COOKIE_NAME)
|
||||||
|
const persistedLocalStorage = localStorage?.getItem(TOKEN_PERSIST_KEY) || ''
|
||||||
|
const persistedDevToken = VITE_KC_DEV_TOKEN
|
||||||
export const persistedToken =
|
export const persistedToken =
|
||||||
VITE_KC_DEV_TOKEN ||
|
persistedDevToken || persistedCookie || persistedLocalStorage
|
||||||
getCookie(COOKIE_NAME) ||
|
console.log('Initial persisted token')
|
||||||
localStorage?.getItem(TOKEN_PERSIST_KEY) ||
|
console.table([
|
||||||
''
|
['cookie', !!persistedCookie],
|
||||||
|
['local storage', !!persistedLocalStorage],
|
||||||
|
['api token', !!persistedDevToken],
|
||||||
|
])
|
||||||
|
|
||||||
export const authMachine = setup({
|
export const authMachine = setup({
|
||||||
types: {} as {
|
types: {} as {
|
||||||
@ -132,7 +139,7 @@ export const authMachine = setup({
|
|||||||
|
|
||||||
async function getUser(input: { token?: string }) {
|
async function getUser(input: { token?: string }) {
|
||||||
const token = await getAndSyncStoredToken(input)
|
const token = await getAndSyncStoredToken(input)
|
||||||
const url = withBaseURL('/user')
|
const url = withAPIBaseURL('/user')
|
||||||
const headers: { [key: string]: string } = {
|
const headers: { [key: string]: string } = {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
}
|
}
|
||||||
@ -141,7 +148,7 @@ async function getUser(input: { token?: string }) {
|
|||||||
if (token) headers['Authorization'] = `Bearer ${token}`
|
if (token) headers['Authorization'] = `Bearer ${token}`
|
||||||
|
|
||||||
const userPromise = isDesktop()
|
const userPromise = isDesktop()
|
||||||
? getUserDesktop(token, VITE_KC_API_BASE_URL)
|
? getUserDesktop(token)
|
||||||
: fetch(url, {
|
: fetch(url, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
@ -190,12 +197,24 @@ async function getAndSyncStoredToken(input: {
|
|||||||
token?: string
|
token?: string
|
||||||
}): Promise<string> {
|
}): Promise<string> {
|
||||||
// dev mode
|
// dev mode
|
||||||
if (VITE_KC_DEV_TOKEN) return VITE_KC_DEV_TOKEN
|
if (VITE_KC_DEV_TOKEN) {
|
||||||
|
console.log('Token used for authentication')
|
||||||
|
console.table([['api token', !!VITE_KC_DEV_TOKEN]])
|
||||||
|
return VITE_KC_DEV_TOKEN
|
||||||
|
}
|
||||||
|
|
||||||
const token =
|
const inputToken = input.token && input.token !== '' ? input.token : ''
|
||||||
input.token && input.token !== ''
|
const cookieToken = getCookie(COOKIE_NAME)
|
||||||
? input.token
|
const localStorageToken = localStorage?.getItem(TOKEN_PERSIST_KEY) || ''
|
||||||
: getCookie(COOKIE_NAME) || localStorage?.getItem(TOKEN_PERSIST_KEY) || ''
|
const token = inputToken || cookieToken || localStorageToken
|
||||||
|
|
||||||
|
console.log('Token used for authentication')
|
||||||
|
console.table([
|
||||||
|
['persisted token', !!inputToken],
|
||||||
|
['cookie', !!cookieToken],
|
||||||
|
['local storage', !!localStorageToken],
|
||||||
|
['api token', !!VITE_KC_DEV_TOKEN],
|
||||||
|
])
|
||||||
if (token) {
|
if (token) {
|
||||||
// has just logged in, update storage
|
// has just logged in, update storage
|
||||||
localStorage.setItem(TOKEN_PERSIST_KEY, token)
|
localStorage.setItem(TOKEN_PERSIST_KEY, token)
|
||||||
@ -221,7 +240,7 @@ async function logout() {
|
|||||||
|
|
||||||
if (token) {
|
if (token) {
|
||||||
try {
|
try {
|
||||||
await fetch(withBaseUrl('/oauth2/token/revoke'), {
|
await fetch(withAPIBaseURL('/oauth2/token/revoke'), {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
headers: {
|
headers: {
|
||||||
@ -244,7 +263,7 @@ async function logout() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return fetch(withBaseUrl('/logout'), {
|
return fetch(withAPIBaseURL('/logout'), {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
})
|
})
|
||||||
|
@ -72,7 +72,7 @@ import {
|
|||||||
addRevolve,
|
addRevolve,
|
||||||
addSweep,
|
addSweep,
|
||||||
getAxisExpressionAndIndex,
|
getAxisExpressionAndIndex,
|
||||||
} from '@src/lang/modifyAst/addSweep'
|
} from '@src/lang/modifyAst/sweeps'
|
||||||
import {
|
import {
|
||||||
applyIntersectFromTargetOperatorSelections,
|
applyIntersectFromTargetOperatorSelections,
|
||||||
applySubtractFromTargetOperatorSelections,
|
applySubtractFromTargetOperatorSelections,
|
||||||
|
@ -70,7 +70,6 @@ dotenv.config({ path: [`.env.${NODE_ENV}.local`, `.env.${NODE_ENV}`] })
|
|||||||
|
|
||||||
// default vite values based on mode
|
// default vite values based on mode
|
||||||
process.env.NODE_ENV ??= viteEnv.MODE
|
process.env.NODE_ENV ??= viteEnv.MODE
|
||||||
process.env.BASE_URL ??= viteEnv.VITE_KC_API_BASE_URL
|
|
||||||
process.env.VITE_KC_API_WS_MODELING_URL ??= viteEnv.VITE_KC_API_WS_MODELING_URL
|
process.env.VITE_KC_API_WS_MODELING_URL ??= viteEnv.VITE_KC_API_WS_MODELING_URL
|
||||||
process.env.VITE_KC_API_BASE_URL ??= viteEnv.VITE_KC_API_BASE_URL
|
process.env.VITE_KC_API_BASE_URL ??= viteEnv.VITE_KC_API_BASE_URL
|
||||||
process.env.VITE_KC_SITE_BASE_URL ??= viteEnv.VITE_KC_SITE_BASE_URL
|
process.env.VITE_KC_SITE_BASE_URL ??= viteEnv.VITE_KC_SITE_BASE_URL
|
||||||
|
@ -6,7 +6,7 @@ import { Link } from 'react-router-dom'
|
|||||||
import { ActionButton } from '@src/components/ActionButton'
|
import { ActionButton } from '@src/components/ActionButton'
|
||||||
import { CustomIcon } from '@src/components/CustomIcon'
|
import { CustomIcon } from '@src/components/CustomIcon'
|
||||||
import { Logo } from '@src/components/Logo'
|
import { Logo } from '@src/components/Logo'
|
||||||
import { VITE_KC_API_BASE_URL, VITE_KC_SITE_BASE_URL } from '@src/env'
|
import { VITE_KC_SITE_BASE_URL } from '@src/env'
|
||||||
import { APP_NAME } from '@src/lib/constants'
|
import { APP_NAME } from '@src/lib/constants'
|
||||||
import { isDesktop } from '@src/lib/isDesktop'
|
import { isDesktop } from '@src/lib/isDesktop'
|
||||||
import { openExternalBrowserIfDesktop } from '@src/lib/openWindow'
|
import { openExternalBrowserIfDesktop } from '@src/lib/openWindow'
|
||||||
@ -15,6 +15,7 @@ import { reportRejection } from '@src/lib/trap'
|
|||||||
import { toSync } from '@src/lib/utils'
|
import { toSync } from '@src/lib/utils'
|
||||||
import { authActor, useSettings } from '@src/lib/singletons'
|
import { authActor, useSettings } from '@src/lib/singletons'
|
||||||
import { APP_VERSION, generateSignInUrl } from '@src/routes/utils'
|
import { APP_VERSION, generateSignInUrl } from '@src/routes/utils'
|
||||||
|
import { withAPIBaseURL } from '@src/lib/withBaseURL'
|
||||||
|
|
||||||
const subtleBorder =
|
const subtleBorder =
|
||||||
'border border-solid border-chalkboard-30 dark:border-chalkboard-80'
|
'border border-solid border-chalkboard-30 dark:border-chalkboard-80'
|
||||||
@ -54,7 +55,7 @@ const SignIn = () => {
|
|||||||
const signInDesktop = async () => {
|
const signInDesktop = async () => {
|
||||||
// We want to invoke our command to login via device auth.
|
// We want to invoke our command to login via device auth.
|
||||||
const userCodeToDisplay = await window.electron
|
const userCodeToDisplay = await window.electron
|
||||||
.startDeviceFlow(VITE_KC_API_BASE_URL + location.search)
|
.startDeviceFlow(withAPIBaseURL(location.search))
|
||||||
.catch(reportError)
|
.catch(reportError)
|
||||||
if (!userCodeToDisplay) {
|
if (!userCodeToDisplay) {
|
||||||
console.error('No user code received while trying to log in')
|
console.error('No user code received while trying to log in')
|
||||||
|
Reference in New Issue
Block a user