Compare commits
25 Commits
pierremtb/
...
nightly-v2
Author | SHA1 | Date | |
---|---|---|---|
61807e7629 | |||
357bbffce5 | |||
6ac9c49773 | |||
020497cde2 | |||
688852a5df | |||
6c635bd70d | |||
1c0a38a1e2 | |||
019cb815f9 | |||
c8653beae7 | |||
9ea3cb51ba | |||
e0de0493ab | |||
11cac0c30e | |||
4de50edf5a | |||
bebace193d | |||
f4d5578faf | |||
fed922cfb8 | |||
2b7325655b | |||
d3b02b8c5b | |||
9008fb636f | |||
eb4048cd16 | |||
c30b161e95 | |||
0e68d03612 | |||
95fd14eedc | |||
3c367426c6 | |||
a277cce636 |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -9,7 +9,7 @@ Just like patternTransform, but works on 2D sketches not 3D solids.
|
|||||||
|
|
||||||
|
|
||||||
```js
|
```js
|
||||||
patternTransform2d(total_instances: integer, transform_function: FunctionParam, solid_set: SketchSet) -> [Sketch]
|
patternTransform2d(total_instances: integer, transform_function: FunctionParam, solid_set: SketchSet, use_original?: bool) -> [Sketch]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@ -20,6 +20,7 @@ patternTransform2d(total_instances: integer, transform_function: FunctionParam,
|
|||||||
| `total_instances` | `integer` | | Yes |
|
| `total_instances` | `integer` | | Yes |
|
||||||
| `transform_function` | `FunctionParam` | | Yes |
|
| `transform_function` | `FunctionParam` | | Yes |
|
||||||
| `solid_set` | [`SketchSet`](/docs/kcl/types/SketchSet) | A sketch or a group of sketches. | Yes |
|
| `solid_set` | [`SketchSet`](/docs/kcl/types/SketchSet) | A sketch or a group of sketches. | Yes |
|
||||||
|
| `use_original` | `bool` | | No |
|
||||||
|
|
||||||
### Returns
|
### Returns
|
||||||
|
|
||||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
19651
docs/kcl/std.json
19651
docs/kcl/std.json
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
15
docs/kcl/types/ArtifactId.md
Normal file
15
docs/kcl/types/ArtifactId.md
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
title: "ArtifactId"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
**Type:** `string` (`uuid`)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -20,5 +20,6 @@ Data for a circular pattern on a 2D sketch.
|
|||||||
| `center` |`[number, number]`| The center about which to make the pattern. This is a 2D vector. | No |
|
| `center` |`[number, number]`| The center about which to make the pattern. This is a 2D vector. | No |
|
||||||
| `arcDegrees` |`number`| The arc angle (in degrees) to place the repetitions. Must be greater than 0. | No |
|
| `arcDegrees` |`number`| The arc angle (in degrees) to place the repetitions. Must be greater than 0. | No |
|
||||||
| `rotateDuplicates` |`boolean`| Whether or not to rotate the duplicates as they are copied. | No |
|
| `rotateDuplicates` |`boolean`| Whether or not to rotate the duplicates as they are copied. | No |
|
||||||
|
| `useOriginal` |`boolean`| If the target being patterned is itself a pattern, then, should you use the original solid, or the pattern? | No |
|
||||||
|
|
||||||
|
|
||||||
|
@ -21,5 +21,6 @@ Data for a circular pattern on a 3D model.
|
|||||||
| `center` |`[number, number, number]`| The center about which to make the pattern. This is a 3D vector. | No |
|
| `center` |`[number, number, number]`| The center about which to make the pattern. This is a 3D vector. | No |
|
||||||
| `arcDegrees` |`number`| The arc angle (in degrees) to place the repetitions. Must be greater than 0. | No |
|
| `arcDegrees` |`number`| The arc angle (in degrees) to place the repetitions. Must be greater than 0. | No |
|
||||||
| `rotateDuplicates` |`boolean`| Whether or not to rotate the duplicates as they are copied. | No |
|
| `rotateDuplicates` |`boolean`| Whether or not to rotate the duplicates as they are copied. | No |
|
||||||
|
| `useOriginal` |`boolean`| If the target being patterned is itself a pattern, then, should you use the original solid, or the pattern? | No |
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ A face.
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `id` |`string`| The id of the face. | No |
|
| `id` |`string`| The id of the face. | No |
|
||||||
|
| `artifactId` |[`ArtifactId`](/docs/kcl/types/ArtifactId)| The artifact ID. | No |
|
||||||
| `value` |`string`| The tag of the face. | No |
|
| `value` |`string`| The tag of the face. | No |
|
||||||
| `xAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the face’s X axis be? | No |
|
| `xAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the face’s X axis be? | No |
|
||||||
| `yAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the face’s Y axis be? | No |
|
| `yAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the face’s Y axis be? | No |
|
||||||
|
@ -17,6 +17,7 @@ A helix.
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `value` |`string`| The id of the helix. | No |
|
| `value` |`string`| The id of the helix. | No |
|
||||||
|
| `artifactId` |[`ArtifactId`](/docs/kcl/types/ArtifactId)| The artifact ID. | No |
|
||||||
| `revolutions` |`number`| Number of revolutions. | No |
|
| `revolutions` |`number`| Number of revolutions. | No |
|
||||||
| `angleStart` |`number`| Start angle (in degrees). | No |
|
| `angleStart` |`number`| Start angle (in degrees). | No |
|
||||||
| `ccw` |`boolean`| Is the helix rotation counter clockwise? | No |
|
| `ccw` |`boolean`| Is the helix rotation counter clockwise? | No |
|
||||||
|
@ -1,26 +0,0 @@
|
|||||||
---
|
|
||||||
title: "HelixData"
|
|
||||||
excerpt: "Data for a helix."
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
Data for a helix.
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `revolutions` |`number`| Number of revolutions. | No |
|
|
||||||
| `angleStart` |`number`| Start angle (in degrees). | No |
|
|
||||||
| `ccw` |`boolean`| Is the helix rotation counter clockwise? The default is `false`. | No |
|
|
||||||
| `length` |`number`| Length of the helix. This is not necessary if the helix is created around an edge. If not given the length of the edge is used. | No |
|
|
||||||
| `radius` |`number`| Radius of the helix. | No |
|
|
||||||
| `axis` |[`Axis3dOrEdgeReference`](/docs/kcl/types/Axis3dOrEdgeReference)| Axis to use as mirror. | No |
|
|
||||||
|
|
||||||
|
|
@ -17,6 +17,7 @@ A helix.
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `value` |`string`| The id of the helix. | No |
|
| `value` |`string`| The id of the helix. | No |
|
||||||
|
| `artifactId` |[`ArtifactId`](/docs/kcl/types/ArtifactId)| The artifact ID. | No |
|
||||||
| `revolutions` |`number`| Number of revolutions. | No |
|
| `revolutions` |`number`| Number of revolutions. | No |
|
||||||
| `angleStart` |`number`| Start angle (in degrees). | No |
|
| `angleStart` |`number`| Start angle (in degrees). | No |
|
||||||
| `ccw` |`boolean`| Is the helix rotation counter clockwise? | No |
|
| `ccw` |`boolean`| Is the helix rotation counter clockwise? | No |
|
||||||
|
@ -17,6 +17,7 @@ A plane.
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `id` |`string`| The id of the plane. | No |
|
| `id` |`string`| The id of the plane. | No |
|
||||||
|
| `artifactId` |[`ArtifactId`](/docs/kcl/types/ArtifactId)| The artifact ID. | No |
|
||||||
| `value` |[`PlaneType`](/docs/kcl/types/PlaneType)| A plane. | No |
|
| `value` |[`PlaneType`](/docs/kcl/types/PlaneType)| A plane. | No |
|
||||||
| `origin` |[`Point3d`](/docs/kcl/types/Point3d)| Origin of the plane. | No |
|
| `origin` |[`Point3d`](/docs/kcl/types/Point3d)| Origin of the plane. | No |
|
||||||
| `xAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the plane’s X axis be? | No |
|
| `xAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the plane’s X axis be? | No |
|
||||||
|
@ -21,6 +21,8 @@ A sketch is a collection of paths.
|
|||||||
| `on` |[`SketchSurface`](/docs/kcl/types/SketchSurface)| What the sketch is on (can be a plane or a face). | No |
|
| `on` |[`SketchSurface`](/docs/kcl/types/SketchSurface)| What the sketch is on (can be a plane or a face). | No |
|
||||||
| `start` |[`BasePath`](/docs/kcl/types/BasePath)| The starting path. | No |
|
| `start` |[`BasePath`](/docs/kcl/types/BasePath)| The starting path. | No |
|
||||||
| `tags` |`object`| Tag identifiers that have been declared in this sketch. | No |
|
| `tags` |`object`| Tag identifiers that have been declared in this sketch. | No |
|
||||||
|
| `artifactId` |[`ArtifactId`](/docs/kcl/types/ArtifactId)| The original id of the sketch. This stays the same even if the sketch is is sketched on face etc. | No |
|
||||||
|
| `originalId` |`string`| | No |
|
||||||
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A sketch is a collection of paths. | No |
|
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A sketch is a collection of paths. | No |
|
||||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| Metadata. | No |
|
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| Metadata. | No |
|
||||||
|
|
||||||
|
@ -30,6 +30,8 @@ A sketch is a collection of paths.
|
|||||||
| `on` |[`SketchSurface`](/docs/kcl/types/SketchSurface)| What the sketch is on (can be a plane or a face). | No |
|
| `on` |[`SketchSurface`](/docs/kcl/types/SketchSurface)| What the sketch is on (can be a plane or a face). | No |
|
||||||
| `start` |[`BasePath`](/docs/kcl/types/BasePath)| The starting path. | No |
|
| `start` |[`BasePath`](/docs/kcl/types/BasePath)| The starting path. | No |
|
||||||
| `tags` |`object`| Tag identifiers that have been declared in this sketch. | No |
|
| `tags` |`object`| Tag identifiers that have been declared in this sketch. | No |
|
||||||
|
| `artifactId` |[`ArtifactId`](/docs/kcl/types/ArtifactId)| The original id of the sketch. This stays the same even if the sketch is is sketched on face etc. | No |
|
||||||
|
| `originalId` |`string`| | No |
|
||||||
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A sketch or a group of sketches. | No |
|
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A sketch or a group of sketches. | No |
|
||||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| Metadata. | No |
|
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| Metadata. | No |
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ A plane.
|
|||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `type` |enum: `plane`| | No |
|
| `type` |enum: `plane`| | No |
|
||||||
| `id` |`string`| The id of the plane. | No |
|
| `id` |`string`| The id of the plane. | No |
|
||||||
|
| `artifactId` |[`ArtifactId`](/docs/kcl/types/ArtifactId)| The artifact ID. | No |
|
||||||
| `value` |[`PlaneType`](/docs/kcl/types/PlaneType)| A sketch type. | No |
|
| `value` |[`PlaneType`](/docs/kcl/types/PlaneType)| A sketch type. | No |
|
||||||
| `origin` |[`Point3d`](/docs/kcl/types/Point3d)| Origin of the plane. | No |
|
| `origin` |[`Point3d`](/docs/kcl/types/Point3d)| Origin of the plane. | No |
|
||||||
| `xAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the plane’s X axis be? | No |
|
| `xAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the plane’s X axis be? | No |
|
||||||
@ -50,6 +51,7 @@ A face.
|
|||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `type` |enum: `face`| | No |
|
| `type` |enum: `face`| | No |
|
||||||
| `id` |`string`| The id of the face. | No |
|
| `id` |`string`| The id of the face. | No |
|
||||||
|
| `artifactId` |[`ArtifactId`](/docs/kcl/types/ArtifactId)| The artifact ID. | No |
|
||||||
| `value` |`string`| The tag of the face. | No |
|
| `value` |`string`| The tag of the face. | No |
|
||||||
| `xAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the face’s X axis be? | No |
|
| `xAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the face’s X axis be? | No |
|
||||||
| `yAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the face’s Y axis be? | No |
|
| `yAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the face’s Y axis be? | No |
|
||||||
|
@ -17,6 +17,7 @@ An solid is a collection of extrude surfaces.
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `id` |`string`| The id of the solid. | No |
|
| `id` |`string`| The id of the solid. | No |
|
||||||
|
| `artifactId` |[`ArtifactId`](/docs/kcl/types/ArtifactId)| The artifact ID of the solid. Unlike `id`, this doesn't change. | No |
|
||||||
| `value` |`[` [`ExtrudeSurface`](/docs/kcl/types/ExtrudeSurface) `]`| The extrude surfaces. | No |
|
| `value` |`[` [`ExtrudeSurface`](/docs/kcl/types/ExtrudeSurface) `]`| The extrude surfaces. | No |
|
||||||
| `sketch` |[`Sketch`](/docs/kcl/types/Sketch)| The sketch. | No |
|
| `sketch` |[`Sketch`](/docs/kcl/types/Sketch)| The sketch. | No |
|
||||||
| `height` |`number`| The height of the solid. | No |
|
| `height` |`number`| The height of the solid. | No |
|
||||||
|
@ -26,6 +26,7 @@ An solid is a collection of extrude surfaces.
|
|||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `type` |enum: `solid`| | No |
|
| `type` |enum: `solid`| | No |
|
||||||
| `id` |`string`| The id of the solid. | No |
|
| `id` |`string`| The id of the solid. | No |
|
||||||
|
| `artifactId` |[`ArtifactId`](/docs/kcl/types/ArtifactId)| The artifact ID of the solid. Unlike `id`, this doesn't change. | No |
|
||||||
| `value` |`[` [`ExtrudeSurface`](/docs/kcl/types/ExtrudeSurface) `]`| The extrude surfaces. | No |
|
| `value` |`[` [`ExtrudeSurface`](/docs/kcl/types/ExtrudeSurface) `]`| The extrude surfaces. | No |
|
||||||
| `sketch` |[`Sketch`](/docs/kcl/types/Sketch)| The sketch. | No |
|
| `sketch` |[`Sketch`](/docs/kcl/types/Sketch)| The sketch. | No |
|
||||||
| `height` |`number`| The height of the solid. | No |
|
| `height` |`number`| The height of the solid. | No |
|
||||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -35,6 +35,30 @@ sketch002 = startSketchOn(plane001)
|
|||||||
extrude001 = extrude(sketch002, length = 10)
|
extrude001 = extrude(sketch002, length = 10)
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const FEAUTRE_TREE_SKETCH_CODE = `sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([0, 0], %)
|
||||||
|
|> angledLine([0, 4], %, $rectangleSegmentA001)
|
||||||
|
|> angledLine([
|
||||||
|
segAng(rectangleSegmentA001) - 90,
|
||||||
|
2
|
||||||
|
], %, $rectangleSegmentB001)
|
||||||
|
|> angledLine([
|
||||||
|
segAng(rectangleSegmentA001),
|
||||||
|
-segLen(rectangleSegmentA001)
|
||||||
|
], %, $rectangleSegmentC001)
|
||||||
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||||
|
|> close(%)
|
||||||
|
extrude001 = extrude(sketch001, length = 10)
|
||||||
|
sketch002 = startSketchOn(extrude001, rectangleSegmentB001)
|
||||||
|
|> circle({
|
||||||
|
center = [-1, 2],
|
||||||
|
radius = .5
|
||||||
|
}, %)
|
||||||
|
plane001 = offsetPlane('XZ', -5)
|
||||||
|
sketch003 = startSketchOn(plane001)
|
||||||
|
|> circle({ center = [0, 0], radius = 5 }, %)
|
||||||
|
`
|
||||||
|
|
||||||
test.describe('Feature Tree pane', () => {
|
test.describe('Feature Tree pane', () => {
|
||||||
test(
|
test(
|
||||||
'User can go to definition and go to function definition',
|
'User can go to definition and go to function definition',
|
||||||
@ -124,4 +148,267 @@ test.describe('Feature Tree pane', () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
test(
|
||||||
|
`User can edit sketch (but not on offset plane yet) from the feature tree`,
|
||||||
|
{ tag: '@electron' },
|
||||||
|
async ({ context, homePage, scene, editor, toolbar, page }) => {
|
||||||
|
const unavailableToastMessage = page.getByText(
|
||||||
|
'Editing sketches on faces or offset planes through the feature tree is not yet supported'
|
||||||
|
)
|
||||||
|
|
||||||
|
await context.folderSetupFn(async (dir) => {
|
||||||
|
const bracketDir = join(dir, 'test-sample')
|
||||||
|
await fsp.mkdir(bracketDir, { recursive: true })
|
||||||
|
await fsp.writeFile(
|
||||||
|
join(bracketDir, 'main.kcl'),
|
||||||
|
FEAUTRE_TREE_SKETCH_CODE,
|
||||||
|
'utf-8'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('setup test', async () => {
|
||||||
|
await homePage.expectState({
|
||||||
|
projectCards: [
|
||||||
|
{
|
||||||
|
title: 'test-sample',
|
||||||
|
fileCount: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
sortBy: 'last-modified-desc',
|
||||||
|
})
|
||||||
|
await homePage.openProject('test-sample')
|
||||||
|
await scene.waitForExecutionDone()
|
||||||
|
await toolbar.openFeatureTreePane()
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('On a default plane should work', async () => {
|
||||||
|
await (await toolbar.getFeatureTreeOperation('Sketch', 0)).dblclick()
|
||||||
|
await expect(
|
||||||
|
toolbar.exitSketchBtn,
|
||||||
|
'We should be in sketch mode now'
|
||||||
|
).toBeVisible()
|
||||||
|
await editor.expectState({
|
||||||
|
highlightedCode: '',
|
||||||
|
diagnostics: [],
|
||||||
|
activeLines: ["sketch001 = startSketchOn('XZ')"],
|
||||||
|
})
|
||||||
|
await toolbar.exitSketchBtn.click()
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('On an extrude face should *not* work', async () => {
|
||||||
|
// Tooltip is getting in the way of clicking, so I'm first closing the pane
|
||||||
|
await toolbar.closeFeatureTreePane()
|
||||||
|
await (await toolbar.getFeatureTreeOperation('Sketch', 1)).dblclick()
|
||||||
|
await expect(
|
||||||
|
unavailableToastMessage,
|
||||||
|
'We should see a toast message about this'
|
||||||
|
).toBeVisible()
|
||||||
|
await unavailableToastMessage.waitFor({ state: 'detached' })
|
||||||
|
// TODO - turn on once we update the artifactGraph in Rust
|
||||||
|
// to include the proper source location for the extrude face
|
||||||
|
// await expect(
|
||||||
|
// toolbar.exitSketchBtn,
|
||||||
|
// 'We should be in sketch mode now'
|
||||||
|
// ).toBeVisible()
|
||||||
|
// await editor.expectState({
|
||||||
|
// highlightedCode: '',
|
||||||
|
// diagnostics: [],
|
||||||
|
// activeLines: ['|>circle({center=[-1,2],radius=.5},%)'],
|
||||||
|
// })
|
||||||
|
// await toolbar.exitSketchBtn.click()
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('On an offset plane should *not* work', async () => {
|
||||||
|
// Tooltip is getting in the way of clicking, so I'm first closing the pane
|
||||||
|
await toolbar.closeFeatureTreePane()
|
||||||
|
await (await toolbar.getFeatureTreeOperation('Sketch', 2)).dblclick()
|
||||||
|
await editor.expectState({
|
||||||
|
highlightedCode: '',
|
||||||
|
diagnostics: [],
|
||||||
|
activeLines: ['|>circle({center=[0,0],radius=5},%)'],
|
||||||
|
})
|
||||||
|
await expect(
|
||||||
|
toolbar.exitSketchBtn,
|
||||||
|
'We should not be in sketch mode now'
|
||||||
|
).not.toBeVisible()
|
||||||
|
await expect(
|
||||||
|
page.getByText(
|
||||||
|
'Editing sketches on faces or offset planes through the feature tree is not yet supported'
|
||||||
|
),
|
||||||
|
'We should see a toast message about this'
|
||||||
|
).toBeVisible()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
test(`User can edit an extrude operation from the feature tree`, async ({
|
||||||
|
context,
|
||||||
|
homePage,
|
||||||
|
scene,
|
||||||
|
editor,
|
||||||
|
toolbar,
|
||||||
|
cmdBar,
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
const initialInput = '23'
|
||||||
|
const initialCode = `sketch001 = startSketchOn('XZ')
|
||||||
|
|> circle({ center = [0, 0], radius = 5 }, %)
|
||||||
|
renamedExtrude = extrude(sketch001, length = ${initialInput})`
|
||||||
|
const newConstantName = 'distance001'
|
||||||
|
const expectedCode = `sketch001 = startSketchOn('XZ')
|
||||||
|
|> circle({ center = [0, 0], radius = 5 }, %)
|
||||||
|
${newConstantName} = 23
|
||||||
|
renamedExtrude = extrude(sketch001, length = ${newConstantName})`
|
||||||
|
|
||||||
|
await context.folderSetupFn(async (dir) => {
|
||||||
|
const testDir = join(dir, 'test-sample')
|
||||||
|
await fsp.mkdir(testDir, { recursive: true })
|
||||||
|
await fsp.writeFile(join(testDir, 'main.kcl'), initialCode, 'utf-8')
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('setup test', async () => {
|
||||||
|
await homePage.expectState({
|
||||||
|
projectCards: [
|
||||||
|
{
|
||||||
|
title: 'test-sample',
|
||||||
|
fileCount: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
sortBy: 'last-modified-desc',
|
||||||
|
})
|
||||||
|
await homePage.openProject('test-sample')
|
||||||
|
await scene.waitForExecutionDone()
|
||||||
|
await toolbar.openFeatureTreePane()
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('Double click on the extrude operation', async () => {
|
||||||
|
await (await toolbar.getFeatureTreeOperation('Extrude', 0))
|
||||||
|
.first()
|
||||||
|
.dblclick()
|
||||||
|
await editor.expectState({
|
||||||
|
highlightedCode: '',
|
||||||
|
diagnostics: [],
|
||||||
|
activeLines: [
|
||||||
|
`renamedExtrude = extrude(sketch001, length = ${initialInput})`,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
await cmdBar.expectState({
|
||||||
|
commandName: 'Extrude',
|
||||||
|
stage: 'arguments',
|
||||||
|
currentArgKey: 'distance',
|
||||||
|
currentArgValue: initialInput,
|
||||||
|
headerArguments: {
|
||||||
|
Selection: '1 face',
|
||||||
|
Distance: initialInput,
|
||||||
|
},
|
||||||
|
highlightedHeaderArg: 'distance',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('Add a named constant for distance argument and submit', async () => {
|
||||||
|
await expect(cmdBar.currentArgumentInput).toBeVisible()
|
||||||
|
const addVariableButton = page.getByRole('button', {
|
||||||
|
name: 'Create new variable',
|
||||||
|
})
|
||||||
|
await addVariableButton.click()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'review',
|
||||||
|
headerArguments: {
|
||||||
|
Selection: '1 face',
|
||||||
|
// The calculated value is shown in the argument summary
|
||||||
|
Distance: initialInput,
|
||||||
|
},
|
||||||
|
commandName: 'Extrude',
|
||||||
|
})
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await editor.expectState({
|
||||||
|
highlightedCode: '',
|
||||||
|
diagnostics: [],
|
||||||
|
activeLines: [
|
||||||
|
`renamedExtrude = extrude(sketch001, length = ${newConstantName})`,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
await editor.expectEditor.toContain(expectedCode, {
|
||||||
|
shouldNormalise: true,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
test(`User can edit an offset plane operation from the feature tree`, async ({
|
||||||
|
context,
|
||||||
|
homePage,
|
||||||
|
scene,
|
||||||
|
editor,
|
||||||
|
toolbar,
|
||||||
|
cmdBar,
|
||||||
|
}) => {
|
||||||
|
const testCode = (value: string) => `p = offsetPlane('XY', ${value})`
|
||||||
|
const initialInput = '10'
|
||||||
|
const initialCode = testCode(initialInput)
|
||||||
|
const newInput = '5 + 10'
|
||||||
|
const expectedCode = testCode(newInput)
|
||||||
|
await context.folderSetupFn(async (dir) => {
|
||||||
|
const testDir = join(dir, 'test-sample')
|
||||||
|
await fsp.mkdir(testDir, { recursive: true })
|
||||||
|
await fsp.writeFile(join(testDir, 'main.kcl'), initialCode, 'utf-8')
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('setup test', async () => {
|
||||||
|
await homePage.expectState({
|
||||||
|
projectCards: [
|
||||||
|
{
|
||||||
|
title: 'test-sample',
|
||||||
|
fileCount: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
sortBy: 'last-modified-desc',
|
||||||
|
})
|
||||||
|
await homePage.openProject('test-sample')
|
||||||
|
await scene.waitForExecutionDone()
|
||||||
|
await toolbar.openFeatureTreePane()
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('Double click on the offset plane operation', async () => {
|
||||||
|
await (await toolbar.getFeatureTreeOperation('Offset Plane', 0))
|
||||||
|
.first()
|
||||||
|
.dblclick()
|
||||||
|
await editor.expectState({
|
||||||
|
highlightedCode: '',
|
||||||
|
diagnostics: [],
|
||||||
|
activeLines: [initialCode],
|
||||||
|
})
|
||||||
|
await cmdBar.expectState({
|
||||||
|
commandName: 'Offset plane',
|
||||||
|
stage: 'arguments',
|
||||||
|
currentArgKey: 'distance',
|
||||||
|
currentArgValue: initialInput,
|
||||||
|
headerArguments: {
|
||||||
|
Plane: '1 plane',
|
||||||
|
Distance: initialInput,
|
||||||
|
},
|
||||||
|
highlightedHeaderArg: 'distance',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('Edit the distance argument and submit', async () => {
|
||||||
|
await expect(cmdBar.currentArgumentInput).toBeVisible()
|
||||||
|
await cmdBar.currentArgumentInput.locator('.cm-content').fill(newInput)
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'review',
|
||||||
|
headerArguments: {
|
||||||
|
Plane: '1 plane',
|
||||||
|
// We show the calculated value in the argument summary
|
||||||
|
Distance: '15',
|
||||||
|
},
|
||||||
|
commandName: 'Offset plane',
|
||||||
|
})
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await editor.expectState({
|
||||||
|
highlightedCode: '',
|
||||||
|
diagnostics: [],
|
||||||
|
activeLines: [expectedCode],
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -26,11 +26,18 @@ type CmdBarSerialised =
|
|||||||
export class CmdBarFixture {
|
export class CmdBarFixture {
|
||||||
public page: Page
|
public page: Page
|
||||||
cmdBarOpenBtn!: Locator
|
cmdBarOpenBtn!: Locator
|
||||||
|
cmdBarElement!: Locator
|
||||||
|
|
||||||
constructor(page: Page) {
|
constructor(page: Page) {
|
||||||
this.page = page
|
this.page = page
|
||||||
this.cmdBarOpenBtn = page.getByTestId('command-bar-open-button')
|
this.cmdBarOpenBtn = page.getByTestId('command-bar-open-button')
|
||||||
|
this.cmdBarElement = page.getByTestId('command-bar')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get currentArgumentInput() {
|
||||||
|
return this.page.getByTestId('cmd-bar-arg-value')
|
||||||
|
}
|
||||||
|
|
||||||
reConstruct = (page: Page) => {
|
reConstruct = (page: Page) => {
|
||||||
this.page = page
|
this.page = page
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,10 @@ export class SceneFixture {
|
|||||||
public page: Page
|
public page: Page
|
||||||
public streamWrapper!: Locator
|
public streamWrapper!: Locator
|
||||||
public loadingIndicator!: Locator
|
public loadingIndicator!: Locator
|
||||||
private exeIndicator!: Locator
|
|
||||||
|
get exeIndicator() {
|
||||||
|
return this.page.getByTestId('model-state-indicator-execution-done')
|
||||||
|
}
|
||||||
|
|
||||||
constructor(page: Page) {
|
constructor(page: Page) {
|
||||||
this.page = page
|
this.page = page
|
||||||
@ -64,7 +67,6 @@ export class SceneFixture {
|
|||||||
reConstruct = (page: Page) => {
|
reConstruct = (page: Page) => {
|
||||||
this.page = page
|
this.page = page
|
||||||
|
|
||||||
this.exeIndicator = page.getByTestId('model-state-indicator-execution-done')
|
|
||||||
this.streamWrapper = page.getByTestId('stream')
|
this.streamWrapper = page.getByTestId('stream')
|
||||||
this.loadingIndicator = this.streamWrapper.getByTestId('loading')
|
this.loadingIndicator = this.streamWrapper.getByTestId('loading')
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,6 @@ export class ToolbarFixture {
|
|||||||
createFileBtn!: Locator
|
createFileBtn!: Locator
|
||||||
fileCreateToast!: Locator
|
fileCreateToast!: Locator
|
||||||
filePane!: Locator
|
filePane!: Locator
|
||||||
exeIndicator!: Locator
|
|
||||||
treeInputField!: Locator
|
treeInputField!: Locator
|
||||||
/** The sidebar button for the Feature Tree pane */
|
/** The sidebar button for the Feature Tree pane */
|
||||||
featureTreeId = 'feature-tree' as const
|
featureTreeId = 'feature-tree' as const
|
||||||
@ -62,15 +61,16 @@ export class ToolbarFixture {
|
|||||||
this.filePane = page.locator('#files-pane')
|
this.filePane = page.locator('#files-pane')
|
||||||
this.featureTreePane = page.locator('#feature-tree-pane')
|
this.featureTreePane = page.locator('#feature-tree-pane')
|
||||||
this.fileCreateToast = page.getByText('Successfully created')
|
this.fileCreateToast = page.getByText('Successfully created')
|
||||||
this.exeIndicator = page.getByTestId(
|
|
||||||
'model-state-indicator-receive-reliable'
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get logoLink() {
|
get logoLink() {
|
||||||
return this.page.getByTestId('app-logo')
|
return this.page.getByTestId('app-logo')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get exeIndicator() {
|
||||||
|
return this.page.getByTestId('model-state-indicator-receive-reliable')
|
||||||
|
}
|
||||||
|
|
||||||
startSketchPlaneSelection = async () =>
|
startSketchPlaneSelection = async () =>
|
||||||
doAndWaitForImageDiff(this.page, () => this.startSketchBtn.click(), 500)
|
doAndWaitForImageDiff(this.page, () => this.startSketchBtn.click(), 500)
|
||||||
|
|
||||||
@ -139,7 +139,8 @@ export class ToolbarFixture {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a specific operation button from the Feature Tree pane
|
* Get a specific operation button from the Feature Tree pane.
|
||||||
|
* Index is 0-based.
|
||||||
*/
|
*/
|
||||||
async getFeatureTreeOperation(operationName: string, operationIndex: number) {
|
async getFeatureTreeOperation(operationName: string, operationIndex: number) {
|
||||||
await this.openFeatureTreePane()
|
await this.openFeatureTreePane()
|
||||||
|
@ -1890,7 +1890,7 @@ chamfer04 = chamfer({ length = 5, tags = [getOppositeEdge(seg02)]}, extrude001
|
|||||||
const testPoint = { x: 575, y: 200 }
|
const testPoint = { x: 575, y: 200 }
|
||||||
const [clickOnCap] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
const [clickOnCap] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
||||||
const shellDeclaration =
|
const shellDeclaration =
|
||||||
"shell001 = shell({ faces = ['end'], thickness = 5 }, extrude001)"
|
"shell001 = shell(extrude001, faces = ['end'], thickness = 5)"
|
||||||
|
|
||||||
await test.step(`Look for the grey of the shape`, async () => {
|
await test.step(`Look for the grey of the shape`, async () => {
|
||||||
await scene.expectPixelColor([127, 127, 127], testPoint, 15)
|
await scene.expectPixelColor([127, 127, 127], testPoint, 15)
|
||||||
@ -1990,8 +1990,7 @@ extrude001 = extrude(sketch001, length = 40)
|
|||||||
const [clickOnWall] = scene.makeMouseHelpers(testPoint.x, testPoint.y + 70)
|
const [clickOnWall] = scene.makeMouseHelpers(testPoint.x, testPoint.y + 70)
|
||||||
const mutatedCode = 'xLine(-40, %, $seg01)'
|
const mutatedCode = 'xLine(-40, %, $seg01)'
|
||||||
const shellDeclaration =
|
const shellDeclaration =
|
||||||
"shell001 = shell({ faces = ['end', seg01], thickness = 5}, extrude001)"
|
"shell001 = shell(extrude001, faces = ['end', seg01], thickness = 5)"
|
||||||
const formattedOutLastLine = '}, extrude001)'
|
|
||||||
|
|
||||||
await test.step(`Look for the grey of the shape`, async () => {
|
await test.step(`Look for the grey of the shape`, async () => {
|
||||||
await scene.expectPixelColor([99, 99, 99], testPoint, 15)
|
await scene.expectPixelColor([99, 99, 99], testPoint, 15)
|
||||||
@ -2034,7 +2033,7 @@ extrude001 = extrude(sketch001, length = 40)
|
|||||||
await editor.expectEditor.toContain(shellDeclaration)
|
await editor.expectEditor.toContain(shellDeclaration)
|
||||||
await editor.expectState({
|
await editor.expectState({
|
||||||
diagnostics: [],
|
diagnostics: [],
|
||||||
activeLines: [formattedOutLastLine],
|
activeLines: [shellDeclaration],
|
||||||
highlightedCode: '',
|
highlightedCode: '',
|
||||||
})
|
})
|
||||||
await scene.expectPixelColor([49, 49, 49], testPoint, 15)
|
await scene.expectPixelColor([49, 49, 49], testPoint, 15)
|
||||||
@ -2088,9 +2087,8 @@ extrude002 = extrude(sketch002, length = 50)
|
|||||||
// One dumb hardcoded screen pixel value
|
// One dumb hardcoded screen pixel value
|
||||||
const testPoint = { x: 550, y: 295 }
|
const testPoint = { x: 550, y: 295 }
|
||||||
const [clickOnCap] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
const [clickOnCap] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
||||||
const shellDeclaration = `shell001 = shell({ faces = ['end'], thickness = 5 }, ${
|
const shellTarget = hasExtrudesInPipe ? 'sketch002' : 'extrude002'
|
||||||
hasExtrudesInPipe ? 'sketch002' : 'extrude002'
|
const shellDeclaration = `shell001 = shell(${shellTarget}, faces = ['end'], thickness = 5)`
|
||||||
})`
|
|
||||||
|
|
||||||
await test.step(`Look for the grey of the shape`, async () => {
|
await test.step(`Look for the grey of the shape`, async () => {
|
||||||
await toolbar.closePane('code')
|
await toolbar.closePane('code')
|
||||||
|
@ -35,7 +35,8 @@ sketch003 = startSketchOn('XY')
|
|||||||
extrude003 = extrude(sketch003, length = 20)
|
extrude003 = extrude(sketch003, length = 20)
|
||||||
`
|
`
|
||||||
|
|
||||||
test.fixme('Check the happy path, for basic changing color', () => {
|
test.describe('Prompt-to-edit tests', { tag: '@skipWin' }, () => {
|
||||||
|
test.describe('Check the happy path, for basic changing color', () => {
|
||||||
const cases = [
|
const cases = [
|
||||||
{
|
{
|
||||||
desc: 'User accepts change',
|
desc: 'User accepts change',
|
||||||
@ -71,7 +72,9 @@ test.fixme('Check the happy path, for basic changing color', () => {
|
|||||||
const green: [number, number, number] = [108, 152, 75]
|
const green: [number, number, number] = [108, 152, 75]
|
||||||
const notGreen: [number, number, number] = [132, 132, 132]
|
const notGreen: [number, number, number] = [132, 132, 132]
|
||||||
const body2NotGreen: [number, number, number] = [88, 88, 88]
|
const body2NotGreen: [number, number, number] = [88, 88, 88]
|
||||||
const submittingToast = page.getByText('Submitting to Text-to-CAD API...')
|
const submittingToast = page.getByText(
|
||||||
|
'Submitting to Text-to-CAD API...'
|
||||||
|
)
|
||||||
const successToast = page.getByText('Prompt to edit successful')
|
const successToast = page.getByText('Prompt to edit successful')
|
||||||
const acceptBtn = page.getByRole('button', { name: 'checkmark Accept' })
|
const acceptBtn = page.getByRole('button', { name: 'checkmark Accept' })
|
||||||
const rejectBtn = page.getByRole('button', { name: 'close Reject' })
|
const rejectBtn = page.getByRole('button', { name: 'close Reject' })
|
||||||
@ -132,9 +135,8 @@ test.fixme('Check the happy path, for basic changing color', () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('bad path', { tag: ['@skipWin'] }, () => {
|
|
||||||
test(`bad edit prompt`, async ({
|
test(`bad edit prompt`, async ({
|
||||||
context,
|
context,
|
||||||
homePage,
|
homePage,
|
||||||
|
@ -253,7 +253,7 @@ extrude001 = extrude(sketch001, length = 50)
|
|||||||
|>
|
|>
|
||||||
|
|
||||||
example = extrude(exampleSketch, length = 5)
|
example = extrude(exampleSketch, length = 5)
|
||||||
shell({ faces: ['end'], thickness: 0.25 }, exampleSketch)`
|
shell(exampleSketch, faces = ['end'], thickness = 0.25)`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -314,6 +314,7 @@ extrude001 = extrude(sketch001, length = 50)
|
|||||||
)
|
)
|
||||||
|
|
||||||
test('when engine fails export we handle the failure and alert the user', async ({
|
test('when engine fails export we handle the failure and alert the user', async ({
|
||||||
|
scene,
|
||||||
page,
|
page,
|
||||||
homePage,
|
homePage,
|
||||||
}) => {
|
}) => {
|
||||||
@ -383,10 +384,7 @@ extrude001 = extrude(sketch001, length = 50)
|
|||||||
await page.keyboard.press('End')
|
await page.keyboard.press('End')
|
||||||
await page.keyboard.press('Enter')
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
// wait for execution done
|
await scene.waitForExecutionDone()
|
||||||
await u.openDebugPanel()
|
|
||||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
|
||||||
await u.closeDebugPanel()
|
|
||||||
|
|
||||||
// Now try exporting
|
// Now try exporting
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ test.describe('Sketch tests', { tag: ['@skipWin'] }, () => {
|
|||||||
page,
|
page,
|
||||||
context,
|
context,
|
||||||
homePage,
|
homePage,
|
||||||
|
scene,
|
||||||
}) => {
|
}) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
const selectionsSnippets = {
|
const selectionsSnippets = {
|
||||||
@ -75,6 +76,7 @@ test.describe('Sketch tests', { tag: ['@skipWin'] }, () => {
|
|||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
|
await scene.waitForExecutionDone()
|
||||||
|
|
||||||
// wait for execution done
|
// wait for execution done
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user