Compare commits
14 Commits
pierremtb/
...
nadro/adho
Author | SHA1 | Date | |
---|---|---|---|
53a646d12a | |||
85c721fb49 | |||
27af2d08a3 | |||
fb8b975b5e | |||
62d8d45a58 | |||
ae3440df0a | |||
af658c909d | |||
7ec11d23c8 | |||
30000a1eac | |||
e35bcf2f11 | |||
939d5ef3f7 | |||
8d19a955af | |||
625394d587 | |||
e6dd628736 |
2
.github/workflows/build-apps.yml
vendored
2
.github/workflows/build-apps.yml
vendored
@ -43,7 +43,7 @@ jobs:
|
||||
- name: Download Wasm Cache
|
||||
id: download-wasm
|
||||
if: ${{ github.event_name == 'pull_request' && steps.filter.outputs.rust == 'false' }}
|
||||
uses: dawidd6/action-download-artifact@v7
|
||||
uses: dawidd6/action-download-artifact@v11
|
||||
continue-on-error: true
|
||||
with:
|
||||
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||
|
12
.github/workflows/cargo-test.yml
vendored
12
.github/workflows/cargo-test.yml
vendored
@ -25,8 +25,8 @@ jobs:
|
||||
- runner=8cpu-linux-x64
|
||||
- extras=s3-cache
|
||||
steps:
|
||||
- uses: runs-on/action@v1
|
||||
- uses: actions/create-github-app-token@v1
|
||||
- uses: runs-on/action@v2
|
||||
- uses: actions/create-github-app-token@v2
|
||||
id: app-token
|
||||
with:
|
||||
app-id: ${{ secrets.MODELING_APP_GH_APP_ID }}
|
||||
@ -149,8 +149,8 @@ jobs:
|
||||
partitionIndex: [1, 2, 3, 4, 5, 6]
|
||||
partitionTotal: [6]
|
||||
steps:
|
||||
- uses: runs-on/action@v1
|
||||
- uses: actions/create-github-app-token@v1
|
||||
- uses: runs-on/action@v2
|
||||
- uses: actions/create-github-app-token@v2
|
||||
id: app-token
|
||||
with:
|
||||
app-id: ${{ secrets.MODELING_APP_GH_APP_ID }}
|
||||
@ -207,8 +207,8 @@ jobs:
|
||||
- runner=32cpu-linux-x64
|
||||
- extras=s3-cache
|
||||
steps:
|
||||
- uses: runs-on/action@v1
|
||||
- uses: actions/create-github-app-token@v1
|
||||
- uses: runs-on/action@v2
|
||||
- uses: actions/create-github-app-token@v2
|
||||
id: app-token
|
||||
with:
|
||||
app-id: ${{ secrets.MODELING_APP_GH_APP_ID }}
|
||||
|
6
.github/workflows/e2e-tests.yml
vendored
6
.github/workflows/e2e-tests.yml
vendored
@ -46,7 +46,7 @@ jobs:
|
||||
- name: Download Wasm cache
|
||||
id: download-wasm
|
||||
if: ${{ github.event_name != 'schedule' && steps.filter.outputs.rust == 'false' }}
|
||||
uses: dawidd6/action-download-artifact@v7
|
||||
uses: dawidd6/action-download-artifact@v11
|
||||
continue-on-error: true
|
||||
with:
|
||||
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||
@ -110,7 +110,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
|
||||
- uses: actions/create-github-app-token@v1
|
||||
- uses: actions/create-github-app-token@v2
|
||||
id: app-token
|
||||
with:
|
||||
app-id: ${{ secrets.MODELING_APP_GH_APP_ID }}
|
||||
@ -230,7 +230,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
|
||||
- uses: actions/create-github-app-token@v1
|
||||
- uses: actions/create-github-app-token@v2
|
||||
id: app-token
|
||||
with:
|
||||
app-id: ${{ secrets.MODELING_APP_GH_APP_ID }}
|
||||
|
2
.github/workflows/generate-website-docs.yml
vendored
2
.github/workflows/generate-website-docs.yml
vendored
@ -20,7 +20,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/create-github-app-token@v1
|
||||
- uses: actions/create-github-app-token@v2
|
||||
id: app-token
|
||||
with:
|
||||
# required
|
||||
|
6
.github/workflows/kcl-python-bindings.yml
vendored
6
.github/workflows/kcl-python-bindings.yml
vendored
@ -113,7 +113,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v5
|
||||
uses: astral-sh/setup-uv@v6
|
||||
- uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
- uses: taiki-e/install-action@just
|
||||
- name: Run tests
|
||||
@ -130,7 +130,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install the latest version of uv
|
||||
uses: astral-sh/setup-uv@v5
|
||||
uses: astral-sh/setup-uv@v6
|
||||
- name: Install codespell
|
||||
run: |
|
||||
uv venv .venv
|
||||
@ -161,7 +161,7 @@ jobs:
|
||||
with:
|
||||
path: rust/kcl-python-bindings
|
||||
- name: Install the latest version of uv
|
||||
uses: astral-sh/setup-uv@v5
|
||||
uses: astral-sh/setup-uv@v6
|
||||
- name: do uv things
|
||||
run: |
|
||||
cd rust/kcl-python-bindings
|
||||
|
@ -525,7 +525,9 @@ test.describe('Command bar tests', () => {
|
||||
const projectName = 'test'
|
||||
const beforeKclCode = `a = 5
|
||||
b = a * a
|
||||
c = 3 + a`
|
||||
c = 3 + a
|
||||
theta = 45deg
|
||||
`
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
const testProject = join(dir, projectName)
|
||||
await fsp.mkdir(testProject, { recursive: true })
|
||||
@ -615,9 +617,45 @@ c = 3 + a`
|
||||
stage: 'commandBarClosed',
|
||||
})
|
||||
})
|
||||
await test.step(`Edit a parameter with explicit units via command bar`, async () => {
|
||||
await cmdBar.cmdBarOpenBtn.click()
|
||||
await cmdBar.chooseCommand('edit parameter')
|
||||
await cmdBar
|
||||
.selectOption({
|
||||
name: 'theta',
|
||||
})
|
||||
.click()
|
||||
await cmdBar.expectState({
|
||||
stage: 'arguments',
|
||||
commandName: 'Edit parameter',
|
||||
currentArgKey: 'value',
|
||||
currentArgValue: '45deg',
|
||||
headerArguments: {
|
||||
Name: 'theta',
|
||||
Value: '',
|
||||
},
|
||||
highlightedHeaderArg: 'value',
|
||||
})
|
||||
await cmdBar.argumentInput
|
||||
.locator('[contenteditable]')
|
||||
.fill('45deg + 1deg')
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.expectState({
|
||||
stage: 'review',
|
||||
commandName: 'Edit parameter',
|
||||
headerArguments: {
|
||||
Name: 'theta',
|
||||
Value: '46deg',
|
||||
},
|
||||
})
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.expectState({
|
||||
stage: 'commandBarClosed',
|
||||
})
|
||||
})
|
||||
|
||||
await editor.expectEditor.toContain(
|
||||
`a = 5b = a * amyParameter001 = ${newValue}c = 3 + a`
|
||||
`a = 5b = a * amyParameter001 = ${newValue}c = 3 + atheta = 45deg + 1deg`
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -136,17 +136,17 @@ test.describe('Point-and-click tests', () => {
|
||||
highlightedHeaderArg: 'length',
|
||||
commandName: 'Extrude',
|
||||
})
|
||||
await page.keyboard.insertText('width - 0.001')
|
||||
await page.keyboard.insertText('width - 0.001in')
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.expectState({
|
||||
stage: 'review',
|
||||
headerArguments: {
|
||||
Length: '4.999',
|
||||
Length: '4.999in',
|
||||
},
|
||||
commandName: 'Extrude',
|
||||
})
|
||||
await cmdBar.progressCmdBar()
|
||||
await editor.expectEditor.toContain('extrude(length = width - 0.001)')
|
||||
await editor.expectEditor.toContain('extrude(length = width - 0.001in)')
|
||||
})
|
||||
|
||||
await test.step(`Edit second extrude via feature tree`, async () => {
|
||||
|
@ -125,18 +125,57 @@ test('Shows a loading spinner when uninitialized credit count', async () => {
|
||||
await expect(queryByTestId('spinner')).toBeVisible()
|
||||
})
|
||||
|
||||
test('Shows the total credits for Unknown subscription', async () => {
|
||||
const data = {
|
||||
balance: {
|
||||
monthlyApiCreditsRemaining: 10,
|
||||
stableApiCreditsRemaining: 25,
|
||||
},
|
||||
subscriptions: {
|
||||
monthlyPayAsYouGoApiCreditsTotal: 20,
|
||||
name: "unknown",
|
||||
}
|
||||
const unKnownTierData = {
|
||||
balance: {
|
||||
monthlyApiCreditsRemaining: 10,
|
||||
stableApiCreditsRemaining: 25,
|
||||
},
|
||||
subscriptions: {
|
||||
monthlyPayAsYouGoApiCreditsTotal: 20,
|
||||
name: "unknown",
|
||||
}
|
||||
}
|
||||
|
||||
const freeTierData = {
|
||||
balance: {
|
||||
monthlyApiCreditsRemaining: 10,
|
||||
stableApiCreditsRemaining: 0,
|
||||
},
|
||||
subscriptions: {
|
||||
monthlyPayAsYouGoApiCreditsTotal: 20,
|
||||
name: "free",
|
||||
}
|
||||
}
|
||||
|
||||
const proTierData = {
|
||||
// These are all ignored
|
||||
balance: {
|
||||
monthlyApiCreditsRemaining: 10,
|
||||
stableApiCreditsRemaining: 0,
|
||||
},
|
||||
subscriptions: {
|
||||
// This should be ignored because it's Pro tier.
|
||||
monthlyPayAsYouGoApiCreditsTotal: 20,
|
||||
name: "pro",
|
||||
}
|
||||
}
|
||||
|
||||
const enterpriseTierData = {
|
||||
// These are all ignored, user is part of an org.
|
||||
balance: {
|
||||
monthlyApiCreditsRemaining: 10,
|
||||
stableApiCreditsRemaining: 0,
|
||||
},
|
||||
subscriptions: {
|
||||
// This should be ignored because it's Pro tier.
|
||||
monthlyPayAsYouGoApiCreditsTotal: 20,
|
||||
// This should be ignored because the user is part of an Org.
|
||||
name: "free",
|
||||
}
|
||||
}
|
||||
|
||||
test('Shows the total credits for Unknown subscription', async () => {
|
||||
const data = unKnownTierData
|
||||
server.use(
|
||||
http.get('*/user/payment/balance', (req, res, ctx) => {
|
||||
return HttpResponse.json(createUserPaymentBalanceResponse(data.balance))
|
||||
@ -166,17 +205,7 @@ test('Shows the total credits for Unknown subscription', async () => {
|
||||
})
|
||||
|
||||
test('Progress bar reflects ratio left of Free subscription', async () => {
|
||||
const data = {
|
||||
balance: {
|
||||
monthlyApiCreditsRemaining: 10,
|
||||
stableApiCreditsRemaining: 0,
|
||||
},
|
||||
subscriptions: {
|
||||
monthlyPayAsYouGoApiCreditsTotal: 20,
|
||||
name: "free",
|
||||
}
|
||||
}
|
||||
|
||||
const data = freeTierData
|
||||
server.use(
|
||||
http.get('*/user/payment/balance', (req, res, ctx) => {
|
||||
return HttpResponse.json(createUserPaymentBalanceResponse(data.balance))
|
||||
@ -212,19 +241,7 @@ test('Progress bar reflects ratio left of Free subscription', async () => {
|
||||
})
|
||||
})
|
||||
test('Shows infinite credits for Pro subscription', async () => {
|
||||
const data = {
|
||||
// These are all ignored
|
||||
balance: {
|
||||
monthlyApiCreditsRemaining: 10,
|
||||
stableApiCreditsRemaining: 0,
|
||||
},
|
||||
subscriptions: {
|
||||
// This should be ignored because it's Pro tier.
|
||||
monthlyPayAsYouGoApiCreditsTotal: 20,
|
||||
name: "pro",
|
||||
}
|
||||
}
|
||||
|
||||
const data = proTierData
|
||||
server.use(
|
||||
http.get('*/user/payment/balance', (req, res, ctx) => {
|
||||
return HttpResponse.json(createUserPaymentBalanceResponse(data.balance))
|
||||
@ -255,19 +272,7 @@ test('Shows infinite credits for Pro subscription', async () => {
|
||||
await expect(queryByTestId('billing-remaining-progress-bar-inline')).toBe(null)
|
||||
})
|
||||
test('Shows infinite credits for Enterprise subscription', async () => {
|
||||
const data = {
|
||||
// These are all ignored, user is part of an org.
|
||||
balance: {
|
||||
monthlyApiCreditsRemaining: 10,
|
||||
stableApiCreditsRemaining: 0,
|
||||
},
|
||||
subscriptions: {
|
||||
// This should be ignored because it's Pro tier.
|
||||
monthlyPayAsYouGoApiCreditsTotal: 20,
|
||||
// This should be ignored because the user is part of an Org.
|
||||
name: "free",
|
||||
}
|
||||
}
|
||||
const data = enterpriseTierData
|
||||
|
||||
server.use(
|
||||
http.get('*/user/payment/balance', (req, res, ctx) => {
|
||||
@ -297,3 +302,58 @@ test('Shows infinite credits for Enterprise subscription', async () => {
|
||||
await expect(queryByTestId('infinity')).toBeVisible()
|
||||
await expect(queryByTestId('billing-remaining-progress-bar-inline')).toBe(null)
|
||||
})
|
||||
|
||||
test('Show upgrade button if credits are not infinite', async () => {
|
||||
const data = freeTierData
|
||||
server.use(
|
||||
http.get('*/user/payment/balance', (req, res, ctx) => {
|
||||
return HttpResponse.json(createUserPaymentBalanceResponse(data.balance))
|
||||
}),
|
||||
http.get('*/user/payment/subscriptions', (req, res, ctx) => {
|
||||
return HttpResponse.json(createUserPaymentSubscriptionsResponse(data.subscriptions))
|
||||
}),
|
||||
http.get('*/org', (req, res, ctx) => {
|
||||
return new HttpResponse(403)
|
||||
}),
|
||||
)
|
||||
|
||||
const billingActor = createActor(billingMachine, { input: BILLING_CONTEXT_DEFAULTS }).start()
|
||||
|
||||
const { queryByTestId } = render(<BillingDialog
|
||||
billingActor={billingActor}
|
||||
/>)
|
||||
|
||||
await act(() => {
|
||||
billingActor.send({ type: BillingTransition.Update, apiToken: "it doesn't matter wtf this is :)" })
|
||||
})
|
||||
|
||||
await expect(queryByTestId('billing-upgrade-button')).toBeVisible()
|
||||
})
|
||||
|
||||
test('Hide upgrade button if credits are infinite', async () => {
|
||||
const data = enterpriseTierData
|
||||
server.use(
|
||||
http.get('*/user/payment/balance', (req, res, ctx) => {
|
||||
return HttpResponse.json(createUserPaymentBalanceResponse(data.balance))
|
||||
}),
|
||||
http.get('*/user/payment/subscriptions', (req, res, ctx) => {
|
||||
return HttpResponse.json(createUserPaymentSubscriptionsResponse(data.subscriptions))
|
||||
}),
|
||||
// Ok finally the first use of an org lol
|
||||
http.get('*/org', (req, res, ctx) => {
|
||||
return HttpResponse.json(createOrgResponse())
|
||||
}),
|
||||
)
|
||||
|
||||
const billingActor = createActor(billingMachine, { input: BILLING_CONTEXT_DEFAULTS }).start()
|
||||
|
||||
const { queryByTestId } = render(<BillingDialog
|
||||
billingActor={billingActor}
|
||||
/>)
|
||||
|
||||
await act(() => {
|
||||
billingActor.send({ type: BillingTransition.Update, apiToken: "it doesn't matter wtf this is :)" })
|
||||
})
|
||||
|
||||
await expect(queryByTestId('billing-upgrade-button')).toBe(null)
|
||||
})
|
||||
|
1118
package-lock.json
generated
1118
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
120
rust/Cargo.lock
generated
120
rust/Cargo.lock
generated
@ -178,7 +178,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -189,7 +189,7 @@ checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -211,7 +211,7 @@ checksum = "e12882f59de5360c748c4cbf569a042d5fb0eb515f7bea9c1f470b47f6ffbd73"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -514,7 +514,7 @@ dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -740,7 +740,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -751,7 +751,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -810,7 +810,7 @@ checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -831,7 +831,7 @@ dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -841,7 +841,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c"
|
||||
dependencies = [
|
||||
"derive_builder_core",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -906,7 +906,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -944,7 +944,7 @@ checksum = "a1ab991c1362ac86c61ab6f556cff143daa22e5a15e4e189df818b2fd19fe65b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1119,7 +1119,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1223,7 +1223,7 @@ dependencies = [
|
||||
"inflections",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1599,7 +1599,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1829,7 +1829,7 @@ version = "0.1.83"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1839,7 +1839,7 @@ dependencies = [
|
||||
"convert_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2104,7 +2104,7 @@ dependencies = [
|
||||
"kittycad-modeling-cmds-macros-impl",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2115,7 +2115,7 @@ checksum = "fdb4ee23cc996aa2dca7584d410e8826e08161e1ac4335bb646d5ede33f37cb3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2311,7 +2311,7 @@ checksum = "db5b29714e950dbb20d5e6f74f9dcec4edbcc1067bb7f8ed198c097b8c1a818b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2640,7 +2640,7 @@ dependencies = [
|
||||
"regex",
|
||||
"regex-syntax 0.8.5",
|
||||
"structmeta",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2654,7 +2654,7 @@ dependencies = [
|
||||
"regex",
|
||||
"regex-syntax 0.8.5",
|
||||
"structmeta",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2710,7 +2710,7 @@ dependencies = [
|
||||
"pest_meta",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2754,7 +2754,7 @@ dependencies = [
|
||||
"phf_shared",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2809,7 +2809,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2921,7 +2921,7 @@ dependencies = [
|
||||
"proc-macro-error-attr2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2981,7 +2981,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"pyo3-macros-backend",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2994,7 +2994,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"pyo3-build-config",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3492,7 +3492,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde_derive_internals",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3556,7 +3556,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3567,7 +3567,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3591,14 +3591,14 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "0.6.8"
|
||||
version = "0.6.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
|
||||
checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@ -3815,7 +3815,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"structmeta-derive",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3826,7 +3826,7 @@ checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3848,7 +3848,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3891,9 +3891,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.103"
|
||||
version = "2.0.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4307e30089d6fd6aff212f2da3a1f9e32f3223b1f010fb09b7c95f90f3ca1e8"
|
||||
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -3917,7 +3917,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3941,7 +3941,7 @@ dependencies = [
|
||||
"proc-macro-error2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4050,7 +4050,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4061,7 +4061,7 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4173,7 +4173,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4217,9 +4217,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.22"
|
||||
version = "0.8.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae"
|
||||
checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
@ -4238,9 +4238,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.22.26"
|
||||
version = "0.22.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e"
|
||||
checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
|
||||
dependencies = [
|
||||
"indexmap 2.9.0",
|
||||
"serde",
|
||||
@ -4341,7 +4341,7 @@ checksum = "84fd902d4e0b9a4b27f2f440108dc034e1758628a9b702f8ec61ad66355422fa"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4369,7 +4369,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4449,7 +4449,7 @@ checksum = "e9d4ed7b4c18cc150a6a0a1e9ea1ecfa688791220781af6e119f9599a8502a0a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
@ -4635,7 +4635,7 @@ dependencies = [
|
||||
"proc-macro-error2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4706,7 +4706,7 @@ dependencies = [
|
||||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@ -4742,7 +4742,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
@ -4777,7 +4777,7 @@ checksum = "17d5042cc5fa009658f9a7333ef24291b1291a25b6382dd68862a7f3b969f69b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5067,7 +5067,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
@ -5112,7 +5112,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5123,7 +5123,7 @@ checksum = "6352c01d0edd5db859a63e2605f4ea3183ddbd15e2c4a9e7d32184df75e4f154"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5143,7 +5143,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
@ -5164,7 +5164,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5186,7 +5186,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.103",
|
||||
"syn 2.0.104",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -19,7 +19,7 @@ anyhow = { workspace = true }
|
||||
clap = { workspace = true, features = ["derive"] }
|
||||
semver = "1.0.25"
|
||||
serde = { workspace = true }
|
||||
toml_edit = "0.22.26"
|
||||
toml_edit = "0.22.27"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
@ -14,7 +14,7 @@ bench = false
|
||||
[dependencies]
|
||||
proc-macro2 = "1"
|
||||
quote = "1"
|
||||
syn = { version = "2.0.103", features = ["full"] }
|
||||
syn = { version = "2.0.104", features = ["full"] }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
@ -14,7 +14,7 @@ bench = false
|
||||
convert_case = "0.8.0"
|
||||
proc-macro2 = "1"
|
||||
quote = "1"
|
||||
syn = { version = "2.0.103", features = ["full"] }
|
||||
syn = { version = "2.0.104", features = ["full"] }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
@ -123,7 +123,7 @@
|
||||
"@vscode/test-electron": "^2.4.1",
|
||||
"@vscode/vsce": "^3.3.2",
|
||||
"cross-env": "^7.0.3",
|
||||
"esbuild": "^0.25.2",
|
||||
"esbuild": "^0.25.3",
|
||||
"glob": "^11.0.1",
|
||||
"mocha": "^11.1.0",
|
||||
"typescript": "^5.8.3"
|
||||
|
@ -74,7 +74,7 @@ sha2 = "0.10.9"
|
||||
tabled = { version = "0.20.0", optional = true }
|
||||
tempfile = "3.20"
|
||||
thiserror = "2.0.0"
|
||||
toml = "0.8.22"
|
||||
toml = "0.8.23"
|
||||
ts-rs = { version = "11.0.1", features = [
|
||||
"uuid-impl",
|
||||
"url-impl",
|
||||
|
@ -401,7 +401,7 @@ impl FunctionDefinition<'_> {
|
||||
impl FunctionBody<'_> {
|
||||
fn prep_mem(&self, exec_state: &mut ExecState) {
|
||||
match self {
|
||||
FunctionBody::Rust(_) => exec_state.mut_stack().push_new_env_for_rust_call(),
|
||||
FunctionBody::Rust(_) => exec_state.mut_stack().push_new_root_env(true),
|
||||
FunctionBody::Kcl(_, memory) => exec_state.mut_stack().push_new_env_for_call(*memory),
|
||||
}
|
||||
}
|
||||
|
@ -541,22 +541,6 @@ impl Stack {
|
||||
self.push_new_env_for_call(snapshot);
|
||||
}
|
||||
|
||||
/// Push a new stack frame on to the call stack for callees which should not read or write
|
||||
/// from memory.
|
||||
///
|
||||
/// This is suitable for calling standard library functions or other functions written in Rust
|
||||
/// which will use 'Rust memory' rather than KCL's memory and cannot reach into the wider
|
||||
/// environment.
|
||||
///
|
||||
/// Trying to read or write from this environment will panic with an index out of bounds.
|
||||
pub fn push_new_env_for_rust_call(&mut self) {
|
||||
self.call_stack.push(self.current_env);
|
||||
// Rust functions shouldn't try to set or access anything in their environment, so don't
|
||||
// waste time and space on a new env. Using usize::MAX means we'll get an overflow if we
|
||||
// try to access anything rather than a silent error.
|
||||
self.current_env = EnvironmentRef(usize::MAX, 0);
|
||||
}
|
||||
|
||||
/// Push a new stack frame on to the call stack with no connection to a parent environment.
|
||||
///
|
||||
/// Suitable for executing a separate module.
|
||||
@ -683,7 +667,7 @@ impl Stack {
|
||||
env.contains_key(var)
|
||||
}
|
||||
|
||||
/// Get a key from the first KCL (i.e., non-Rust) stack frame on the call stack.
|
||||
/// Get a key from the first stack frame on the call stack.
|
||||
pub fn get_from_call_stack(&self, key: &str, source_range: SourceRange) -> Result<(usize, &KclValue), KclError> {
|
||||
if !self.current_env.skip_env() {
|
||||
return Ok((self.current_env.1, self.get(key, source_range)?));
|
||||
@ -695,7 +679,7 @@ impl Stack {
|
||||
}
|
||||
}
|
||||
|
||||
unreachable!("It can't be Rust frames all the way down");
|
||||
unreachable!("No frames on the stack?");
|
||||
}
|
||||
|
||||
/// Iterate over all keys in the current environment which satisfy the provided predicate.
|
||||
@ -1217,24 +1201,6 @@ mod test {
|
||||
assert_get_from(mem, "c", 5, callee);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rust_env() {
|
||||
let mem = &mut Stack::new_for_tests();
|
||||
mem.add("a".to_owned(), val(1), sr()).unwrap();
|
||||
mem.add("b".to_owned(), val(3), sr()).unwrap();
|
||||
let sn = mem.snapshot();
|
||||
|
||||
mem.push_new_env_for_rust_call();
|
||||
mem.push_new_env_for_call(sn);
|
||||
assert_get(mem, "b", 3);
|
||||
mem.add("b".to_owned(), val(4), sr()).unwrap();
|
||||
assert_get(mem, "b", 4);
|
||||
|
||||
mem.pop_env();
|
||||
mem.pop_env();
|
||||
assert_get(mem, "b", 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deep_call_env() {
|
||||
let mem = &mut Stack::new_for_tests();
|
||||
|
@ -1920,6 +1920,22 @@ shape = layer() |> patternTransform(instances = 10, transform = transform)
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn pass_std_to_std() {
|
||||
let ast = r#"sketch001 = startSketchOn(XY)
|
||||
profile001 = circle(sketch001, center = [0, 0], radius = 2)
|
||||
extrude001 = extrude(profile001, length = 5)
|
||||
extrudes = patternLinear3d(
|
||||
extrude001,
|
||||
instances = 3,
|
||||
distance = 5,
|
||||
axis = [1, 1, 0],
|
||||
)
|
||||
clone001 = map(extrudes, f = clone)
|
||||
"#;
|
||||
parse_execute(ast).await.unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_zero_param_fn() {
|
||||
let ast = r#"sigmaAllow = 35000 // psi
|
||||
|
@ -840,6 +840,18 @@ pub enum UnitType {
|
||||
Angle(UnitAngle),
|
||||
}
|
||||
|
||||
impl UnitType {
|
||||
pub(crate) fn to_suffix(self) -> Option<String> {
|
||||
match self {
|
||||
UnitType::Count => Some("_".to_owned()),
|
||||
UnitType::Length(UnitLen::Unknown) => None,
|
||||
UnitType::Angle(UnitAngle::Unknown) => None,
|
||||
UnitType::Length(l) => Some(l.to_string()),
|
||||
UnitType::Angle(a) => Some(a.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for UnitType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
|
@ -45,6 +45,31 @@ pub fn format_number_literal(value: f64, suffix: NumericSuffix) -> Result<String
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, thiserror::Error)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum FormatNumericTypeError {
|
||||
#[error("Invalid numeric type: {0:?}")]
|
||||
Invalid(NumericType),
|
||||
}
|
||||
|
||||
/// For UI code generation, format a number value with a suffix such that the
|
||||
/// result can parse as a literal. If it can't be done, returns an error.
|
||||
///
|
||||
/// This is used by TS.
|
||||
pub fn format_number_value(value: f64, ty: NumericType) -> Result<String, FormatNumericTypeError> {
|
||||
match ty {
|
||||
NumericType::Default { .. } => Ok(value.to_string()),
|
||||
// There isn't a syntactic suffix for these. For unknown, we don't want
|
||||
// to ever generate the unknown suffix. We currently warn on it, and we
|
||||
// may remove it in the future.
|
||||
NumericType::Unknown | NumericType::Any => Err(FormatNumericTypeError::Invalid(ty)),
|
||||
NumericType::Known(unit_type) => unit_type
|
||||
.to_suffix()
|
||||
.map(|suffix| format!("{value}{suffix}"))
|
||||
.ok_or(FormatNumericTypeError::Invalid(ty)),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use pretty_assertions::assert_eq;
|
||||
@ -134,4 +159,74 @@ mod tests {
|
||||
Err(FormatNumericSuffixError::Invalid(NumericSuffix::Unknown))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_number_value() {
|
||||
assert_eq!(
|
||||
format_number_value(
|
||||
1.0,
|
||||
NumericType::Default {
|
||||
len: Default::default(),
|
||||
angle: Default::default()
|
||||
}
|
||||
),
|
||||
Ok("1".to_owned())
|
||||
);
|
||||
assert_eq!(
|
||||
format_number_value(1.0, NumericType::Known(UnitType::Length(UnitLen::Unknown))),
|
||||
Err(FormatNumericTypeError::Invalid(NumericType::Known(UnitType::Length(
|
||||
UnitLen::Unknown
|
||||
))))
|
||||
);
|
||||
assert_eq!(
|
||||
format_number_value(1.0, NumericType::Known(UnitType::Angle(UnitAngle::Unknown))),
|
||||
Err(FormatNumericTypeError::Invalid(NumericType::Known(UnitType::Angle(
|
||||
UnitAngle::Unknown
|
||||
))))
|
||||
);
|
||||
assert_eq!(
|
||||
format_number_value(1.0, NumericType::Known(UnitType::Count)),
|
||||
Ok("1_".to_owned())
|
||||
);
|
||||
assert_eq!(
|
||||
format_number_value(1.0, NumericType::Known(UnitType::Length(UnitLen::Mm))),
|
||||
Ok("1mm".to_owned())
|
||||
);
|
||||
assert_eq!(
|
||||
format_number_value(1.0, NumericType::Known(UnitType::Length(UnitLen::Cm))),
|
||||
Ok("1cm".to_owned())
|
||||
);
|
||||
assert_eq!(
|
||||
format_number_value(1.0, NumericType::Known(UnitType::Length(UnitLen::M))),
|
||||
Ok("1m".to_owned())
|
||||
);
|
||||
assert_eq!(
|
||||
format_number_value(1.0, NumericType::Known(UnitType::Length(UnitLen::Inches))),
|
||||
Ok("1in".to_owned())
|
||||
);
|
||||
assert_eq!(
|
||||
format_number_value(1.0, NumericType::Known(UnitType::Length(UnitLen::Feet))),
|
||||
Ok("1ft".to_owned())
|
||||
);
|
||||
assert_eq!(
|
||||
format_number_value(1.0, NumericType::Known(UnitType::Length(UnitLen::Yards))),
|
||||
Ok("1yd".to_owned())
|
||||
);
|
||||
assert_eq!(
|
||||
format_number_value(1.0, NumericType::Known(UnitType::Angle(UnitAngle::Degrees))),
|
||||
Ok("1deg".to_owned())
|
||||
);
|
||||
assert_eq!(
|
||||
format_number_value(1.0, NumericType::Known(UnitType::Angle(UnitAngle::Radians))),
|
||||
Ok("1rad".to_owned())
|
||||
);
|
||||
assert_eq!(
|
||||
format_number_value(1.0, NumericType::Unknown),
|
||||
Err(FormatNumericTypeError::Invalid(NumericType::Unknown))
|
||||
);
|
||||
assert_eq!(
|
||||
format_number_value(1.0, NumericType::Any),
|
||||
Err(FormatNumericTypeError::Invalid(NumericType::Any))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -140,7 +140,7 @@ pub mod std_utils {
|
||||
|
||||
pub mod pretty {
|
||||
pub use crate::{
|
||||
fmt::{format_number_literal, human_display_number},
|
||||
fmt::{format_number_literal, format_number_value, human_display_number},
|
||||
parsing::token::NumericSuffix,
|
||||
};
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ kittycad = { workspace = true }
|
||||
kittycad-modeling-cmds = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
tokio = { workspace = true, features = ["sync", "rt"] }
|
||||
toml = "0.8.22"
|
||||
toml = "0.8.23"
|
||||
tower-lsp = { workspace = true, features = ["runtime-agnostic"] }
|
||||
uuid = { workspace = true, features = ["v4", "js", "serde"] }
|
||||
wasm-bindgen = "0.2.99"
|
||||
|
@ -61,6 +61,37 @@ pub fn format_number_literal(value: f64, suffix_json: &str) -> Result<String, Js
|
||||
kcl_lib::pretty::format_number_literal(value, suffix).map_err(JsError::from)
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn format_number_value(value: f64, numeric_type_json: &str) -> Result<String, String> {
|
||||
console_error_panic_hook::set_once();
|
||||
|
||||
// ts-rs can't handle tuple types, so it mashes all of these types together.
|
||||
if let Ok(ty) = serde_json::from_str::<NumericType>(numeric_type_json) {
|
||||
if let Ok(formatted) = kcl_lib::pretty::format_number_value(value, ty) {
|
||||
return Ok(formatted);
|
||||
}
|
||||
}
|
||||
if let Ok(unit_type) = serde_json::from_str::<UnitType>(numeric_type_json) {
|
||||
let ty = NumericType::Known(unit_type);
|
||||
if let Ok(formatted) = kcl_lib::pretty::format_number_value(value, ty) {
|
||||
return Ok(formatted);
|
||||
}
|
||||
}
|
||||
if let Ok(unit_len) = serde_json::from_str::<UnitLen>(numeric_type_json) {
|
||||
let ty = NumericType::Known(UnitType::Length(unit_len));
|
||||
if let Ok(formatted) = kcl_lib::pretty::format_number_value(value, ty) {
|
||||
return Ok(formatted);
|
||||
}
|
||||
}
|
||||
if let Ok(unit_angle) = serde_json::from_str::<UnitAngle>(numeric_type_json) {
|
||||
let ty = NumericType::Known(UnitType::Angle(unit_angle));
|
||||
if let Ok(formatted) = kcl_lib::pretty::format_number_value(value, ty) {
|
||||
return Ok(formatted);
|
||||
}
|
||||
}
|
||||
Err(format!("Invalid type: {numeric_type_json}"))
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn human_display_number(value: f64, ty_json: &str) -> Result<String, String> {
|
||||
console_error_panic_hook::set_once();
|
||||
|
@ -394,7 +394,7 @@ export function Toolbar({
|
||||
<div className="flex flex-col items-center absolute top-full left-1/2 -translate-x-1/2">
|
||||
{isInTemporaryWorkspace && (
|
||||
<div className="flex flex-row gap-2 justify-center">
|
||||
<div className="mt-2 py-1 animate-pulse w-fit uppercase text-xs rounded-full ml-2 px-2 py-1 border border-chalkboard-40 dark:text-chalkboard-40 bg-chalkboard-10 dark:bg-chalkboard-90 shadow-lg">
|
||||
<div className="mt-2 animate-pulse w-fit uppercase text-xs rounded-full ml-2 px-2 py-1 border border-chalkboard-40 dark:text-chalkboard-40 bg-chalkboard-10 dark:bg-chalkboard-90 shadow-lg flex items-center">
|
||||
Temporary workspace
|
||||
</div>
|
||||
<button
|
||||
|
@ -6,7 +6,7 @@ import {
|
||||
BillingRemainingMode,
|
||||
} from '@src/components/BillingRemaining'
|
||||
|
||||
import type { BillingActor } from '@src/machines/billingMachine'
|
||||
import { type BillingActor } from '@src/machines/billingMachine'
|
||||
|
||||
export const BillingDialog = (props: { billingActor: BillingActor }) => {
|
||||
const billingContext = useSelector(
|
||||
@ -39,15 +39,18 @@ export const BillingDialog = (props: { billingActor: BillingActor }) => {
|
||||
mode={BillingRemainingMode.ProgressBarStretch}
|
||||
billingActor={props.billingActor}
|
||||
/>
|
||||
<a
|
||||
className="bg-ml-black text-ml-white rounded-lg text-center p-1 cursor-pointer"
|
||||
href="https://zoo.dev/design-studio-pricing"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
onClick={openExternalBrowserIfDesktop()}
|
||||
>
|
||||
Upgrade
|
||||
</a>
|
||||
{!hasUnlimited && (
|
||||
<a
|
||||
className="bg-ml-black text-ml-white rounded-lg text-center p-1 cursor-pointer"
|
||||
href="https://zoo.dev/design-studio-pricing"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
data-testid="billing-upgrade-button"
|
||||
onClick={openExternalBrowserIfDesktop()}
|
||||
>
|
||||
Upgrade
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
@ -12,7 +12,7 @@ import type {
|
||||
} from '@src/lib/commandTypes'
|
||||
import type { Selections } from '@src/lib/selections'
|
||||
import { getSelectionTypeDisplayText } from '@src/lib/selections'
|
||||
import { roundOff } from '@src/lib/utils'
|
||||
import { roundOffWithUnits } from '@src/lib/utils'
|
||||
import { commandBarActor, useCommandBarState } from '@src/lib/singletons'
|
||||
|
||||
function CommandBarHeaderFooter({
|
||||
@ -163,10 +163,8 @@ function CommandBarHeaderFooter({
|
||||
arg.inputType === 'selectionMixed' ? (
|
||||
getSelectionTypeDisplayText(argValue as Selections)
|
||||
) : arg.inputType === 'kcl' ? (
|
||||
roundOff(
|
||||
Number(
|
||||
(argValue as KclCommandValue).valueCalculated
|
||||
),
|
||||
roundOffWithUnits(
|
||||
(argValue as KclCommandValue).valueCalculated,
|
||||
4
|
||||
)
|
||||
) : arg.inputType === 'text' &&
|
||||
|
@ -21,13 +21,13 @@ import { Spinner } from '@src/components/Spinner'
|
||||
import { createLocalName, createVariableDeclaration } from '@src/lang/create'
|
||||
import { getNodeFromPath } from '@src/lang/queryAst'
|
||||
import type { SourceRange, VariableDeclarator } from '@src/lang/wasm'
|
||||
import { isPathToNode } from '@src/lang/wasm'
|
||||
import { formatNumberValue, isPathToNode } from '@src/lang/wasm'
|
||||
import type { CommandArgument, KclCommandValue } from '@src/lib/commandTypes'
|
||||
import { kclManager } from '@src/lib/singletons'
|
||||
import { getSystemTheme } from '@src/lib/theme'
|
||||
import { err } from '@src/lib/trap'
|
||||
import { useCalculateKclExpression } from '@src/lib/useCalculateKclExpression'
|
||||
import { roundOff } from '@src/lib/utils'
|
||||
import { roundOff, roundOffWithUnits } from '@src/lib/utils'
|
||||
import { varMentions } from '@src/lib/varCompletionExtension'
|
||||
import { useSettings } from '@src/lib/singletons'
|
||||
import { commandBarActor, useCommandBarState } from '@src/lib/singletons'
|
||||
@ -128,10 +128,22 @@ function CommandBarKclInput({
|
||||
sourceRange: sourceRangeForPrevVariables,
|
||||
})
|
||||
|
||||
const varMentionData: Completion[] = prevVariables.map((v) => ({
|
||||
label: v.key,
|
||||
detail: String(roundOff(Number(v.value))),
|
||||
}))
|
||||
const varMentionData: Completion[] = prevVariables.map((v) => {
|
||||
const roundedWithUnits = (() => {
|
||||
if (typeof v.value !== 'number' || !v.ty) {
|
||||
return undefined
|
||||
}
|
||||
const numWithUnits = formatNumberValue(v.value, v.ty)
|
||||
if (err(numWithUnits)) {
|
||||
return undefined
|
||||
}
|
||||
return roundOffWithUnits(numWithUnits)
|
||||
})()
|
||||
return {
|
||||
label: v.key,
|
||||
detail: roundedWithUnits ?? String(roundOff(Number(v.value))),
|
||||
}
|
||||
})
|
||||
const varMentionsExtension = varMentions(varMentionData)
|
||||
|
||||
const { setContainer, view } = useCodeMirror({
|
||||
@ -282,7 +294,7 @@ function CommandBarKclInput({
|
||||
) : calcResult === 'NAN' ? (
|
||||
"Can't calculate"
|
||||
) : (
|
||||
roundOff(Number(calcResult), 4)
|
||||
roundOffWithUnits(calcResult, 4)
|
||||
)}
|
||||
</span>
|
||||
</label>
|
||||
|
@ -110,6 +110,7 @@ export const FeatureTreePane = () => {
|
||||
// devTools: true,
|
||||
}
|
||||
)
|
||||
|
||||
// If there are parse errors we show the last successful operations
|
||||
// and overlay a message on top of the pane
|
||||
const parseErrors = kclManager.errors.filter((e) => e.kind !== 'engine')
|
||||
@ -163,12 +164,13 @@ export const FeatureTreePane = () => {
|
||||
{!modelingState.matches('Sketch') && <DefaultPlanes />}
|
||||
{parseErrors.length > 0 && (
|
||||
<div
|
||||
className={`absolute inset-0 rounded-lg p-2 ${
|
||||
operationList.length &&
|
||||
`bg-destroy-10/40 dark:bg-destroy-80/40`
|
||||
className={`absolute inset-0 p-2 ${
|
||||
operationList.length ||
|
||||
(parseErrors.length > 0 &&
|
||||
`bg-destroy-10/40 dark:bg-destroy-80/40`)
|
||||
}`}
|
||||
>
|
||||
<div className="text-sm bg-destroy-80 text-chalkboard-10 py-1 px-2 rounded flex gap-2 items-center">
|
||||
<div className="text-base font-sans font-normal text-destroy-80 dark:text-destroy-10 bg-destroy-10 dark:bg-destroy-80 py-1 px-2 rounded flex gap-2 items-center">
|
||||
<p className="flex-1">
|
||||
Errors found in KCL code.
|
||||
<br />
|
||||
@ -176,7 +178,7 @@ export const FeatureTreePane = () => {
|
||||
</p>
|
||||
<button
|
||||
onClick={goToError}
|
||||
className="bg-chalkboard-10 text-destroy-80 p-1 rounded-sm flex-none hover:bg-chalkboard-10 hover:border-destroy-70 hover:text-destroy-80 border-transparent"
|
||||
className="border bg-chalkboard-10 text-destroy-80 p-1 rounded flex-none hover:bg-chalkboard-10 hover:border-destroy-70 hover:text-destroy-80 border-transparent"
|
||||
>
|
||||
View error
|
||||
</button>
|
||||
|
@ -63,11 +63,36 @@ variableBelowShouldNotBeIncluded = 3
|
||||
execState.variables,
|
||||
topLevelRange(rangeStart, rangeStart)
|
||||
)
|
||||
const defaultTy = {
|
||||
type: 'Default',
|
||||
angle: {
|
||||
type: 'Degrees',
|
||||
},
|
||||
len: {
|
||||
type: 'Mm',
|
||||
},
|
||||
}
|
||||
expect(variables).toEqual([
|
||||
{ key: 'baseThick', value: 1 },
|
||||
{ key: 'armAngle', value: 60 },
|
||||
{ key: 'baseThickHalf', value: 0.5 },
|
||||
{ key: 'halfArmAngle', value: 30 },
|
||||
{
|
||||
key: 'baseThick',
|
||||
value: 1,
|
||||
ty: defaultTy,
|
||||
},
|
||||
{
|
||||
key: 'armAngle',
|
||||
value: 60,
|
||||
ty: defaultTy,
|
||||
},
|
||||
{
|
||||
key: 'baseThickHalf',
|
||||
value: 0.5,
|
||||
ty: defaultTy,
|
||||
},
|
||||
{
|
||||
key: 'halfArmAngle',
|
||||
value: 30,
|
||||
ty: defaultTy,
|
||||
},
|
||||
// no arrExpShouldNotBeIncluded, variableBelowShouldNotBeIncluded etc
|
||||
])
|
||||
// there are 4 number variables and 2 non-number variables before the sketch var
|
||||
|
@ -55,6 +55,7 @@ import type { OpKclValue, Operation } from '@rust/kcl-lib/bindings/Operation'
|
||||
import { ARG_INDEX_FIELD, LABELED_ARG_FIELD } from '@src/lang/queryAstConstants'
|
||||
import type { KclCommandValue } from '@src/lib/commandTypes'
|
||||
import type { UnaryExpression } from 'typescript'
|
||||
import type { NumericType } from '@rust/kcl-lib/bindings/NumericType'
|
||||
|
||||
/**
|
||||
* Retrieves a node from a given path within a Program node structure, optionally stopping at a specified node type.
|
||||
@ -306,6 +307,7 @@ export function traverse(
|
||||
export interface PrevVariable<T> {
|
||||
key: string
|
||||
value: T
|
||||
ty: NumericType | undefined
|
||||
}
|
||||
|
||||
export function findAllPreviousVariablesPath(
|
||||
@ -353,6 +355,7 @@ export function findAllPreviousVariablesPath(
|
||||
variables.push({
|
||||
key: varName,
|
||||
value: varValue.value,
|
||||
ty: varValue.type === 'Number' ? varValue.ty : undefined,
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -45,6 +45,7 @@ import {
|
||||
default_app_settings,
|
||||
default_project_settings,
|
||||
format_number_literal,
|
||||
format_number_value,
|
||||
get_kcl_version,
|
||||
get_tangential_arc_to_info,
|
||||
human_display_number,
|
||||
@ -448,6 +449,23 @@ export function formatNumberLiteral(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a number from a KclValue such that it could be parsed as KCL.
|
||||
*/
|
||||
export function formatNumberValue(
|
||||
value: number,
|
||||
numericType: NumericType
|
||||
): string | Error {
|
||||
try {
|
||||
return format_number_value(value, JSON.stringify(numericType))
|
||||
} catch (e) {
|
||||
return new Error(
|
||||
`Error formatting number value: value=${value}, numericType=${numericType}`,
|
||||
{ cause: e }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Debug display a number with suffix, for human consumption only.
|
||||
*/
|
||||
|
@ -2,13 +2,20 @@ import type { ParseResult } from '@src/lang/wasm'
|
||||
import { getCalculatedKclExpressionValue } from '@src/lib/kclHelpers'
|
||||
|
||||
describe('KCL expression calculations', () => {
|
||||
it('calculates a simple expression', async () => {
|
||||
it('calculates a simple expression without units', async () => {
|
||||
const actual = await getCalculatedKclExpressionValue('1 + 2')
|
||||
const coercedActual = actual as Exclude<typeof actual, Error | ParseResult>
|
||||
expect(coercedActual).not.toHaveProperty('errors')
|
||||
expect(coercedActual.valueAsString).toEqual('3')
|
||||
expect(coercedActual?.astNode).toBeDefined()
|
||||
})
|
||||
it('calculates a simple expression with units', async () => {
|
||||
const actual = await getCalculatedKclExpressionValue('1deg + 30deg')
|
||||
const coercedActual = actual as Exclude<typeof actual, Error | ParseResult>
|
||||
expect(coercedActual).not.toHaveProperty('errors')
|
||||
expect(coercedActual.valueAsString).toEqual('31deg')
|
||||
expect(coercedActual?.astNode).toBeDefined()
|
||||
})
|
||||
it('returns NAN for an invalid expression', async () => {
|
||||
const actual = await getCalculatedKclExpressionValue('1 + x')
|
||||
const coercedActual = actual as Exclude<typeof actual, Error | ParseResult>
|
||||
|
@ -1,5 +1,10 @@
|
||||
import { executeAstMock } from '@src/lang/langHelpers'
|
||||
import { type CallExpressionKw, parse, resultIsOk } from '@src/lang/wasm'
|
||||
import {
|
||||
type CallExpressionKw,
|
||||
formatNumberValue,
|
||||
parse,
|
||||
resultIsOk,
|
||||
} from '@src/lang/wasm'
|
||||
import type { KclCommandValue, KclExpression } from '@src/lib/commandTypes'
|
||||
import { rustContext } from '@src/lib/singletons'
|
||||
import { err } from '@src/lib/trap'
|
||||
@ -32,12 +37,27 @@ export async function getCalculatedKclExpressionValue(value: string) {
|
||||
const variableDeclaratorAstNode =
|
||||
resultDeclaration?.type === 'VariableDeclaration' &&
|
||||
resultDeclaration?.declaration.init
|
||||
const resultRawValue = execState.variables[DUMMY_VARIABLE_NAME]?.value
|
||||
const varValue = execState.variables[DUMMY_VARIABLE_NAME]
|
||||
// If the value is a number, attempt to format it with units.
|
||||
const resultValueWithUnits = (() => {
|
||||
if (!varValue || varValue.type !== 'Number') {
|
||||
return undefined
|
||||
}
|
||||
const formatted = formatNumberValue(varValue.value, varValue.ty)
|
||||
if (err(formatted)) return undefined
|
||||
return formatted
|
||||
})()
|
||||
// Prefer the formatted value with units. Fallback to the raw value.
|
||||
const resultRawValue = varValue?.value
|
||||
const valueAsString = resultValueWithUnits
|
||||
? resultValueWithUnits
|
||||
: typeof resultRawValue === 'number'
|
||||
? String(resultRawValue)
|
||||
: 'NAN'
|
||||
|
||||
return {
|
||||
astNode: variableDeclaratorAstNode,
|
||||
valueAsString:
|
||||
typeof resultRawValue === 'number' ? String(resultRawValue) : 'NAN',
|
||||
valueAsString,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,7 +79,7 @@ export function useCalculateKclExpression({
|
||||
isValueParsable = false
|
||||
}
|
||||
const initialCalcResult: number | string =
|
||||
Number.isNaN(Number(value)) || !isValueParsable ? 'NAN' : value
|
||||
Number.isNaN(parseFloat(value)) || !isValueParsable ? 'NAN' : value
|
||||
const [calcResult, setCalcResult] = useState(initialCalcResult)
|
||||
const [newVariableName, _setNewVariableName] = useState('')
|
||||
const [isNewVariableNameUnique, setIsNewVariableNameUnique] = useState(true)
|
||||
|
@ -8,6 +8,7 @@ import {
|
||||
isOverlap,
|
||||
onDragNumberCalculation,
|
||||
roundOff,
|
||||
roundOffWithUnits,
|
||||
simulateOnMouseDragMatch,
|
||||
} from '@src/lib/utils'
|
||||
|
||||
@ -43,6 +44,48 @@ describe('testing roundOff', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('roundOffWithUnits', () => {
|
||||
it('works with no units', () => {
|
||||
expect(roundOffWithUnits('1.23456789')).toBe('1.23')
|
||||
expect(roundOffWithUnits('1.23456789', 3)).toBe('1.235')
|
||||
expect(roundOffWithUnits('1.', 3)).toBe('1')
|
||||
expect(roundOffWithUnits('-1.23456789')).toBe('-1.23')
|
||||
expect(roundOffWithUnits('-1.23456789', 3)).toBe('-1.235')
|
||||
expect(roundOffWithUnits('-1.', 3)).toBe('-1')
|
||||
})
|
||||
it('works with standard units', () => {
|
||||
expect(roundOffWithUnits('1.23456789mm', 3)).toBe('1.235mm')
|
||||
expect(roundOffWithUnits('1.23456789m', 3)).toBe('1.235m')
|
||||
expect(roundOffWithUnits('1.23456789in', 3)).toBe('1.235in')
|
||||
expect(roundOffWithUnits('1.23456789_', 3)).toBe('1.235_')
|
||||
expect(roundOffWithUnits('1._', 3)).toBe('1_')
|
||||
expect(roundOffWithUnits('-1.23456789mm', 3)).toBe('-1.235mm')
|
||||
expect(roundOffWithUnits('-1.23456789m', 3)).toBe('-1.235m')
|
||||
expect(roundOffWithUnits('-1.23456789in', 3)).toBe('-1.235in')
|
||||
expect(roundOffWithUnits('-1.23456789_', 3)).toBe('-1.235_')
|
||||
expect(roundOffWithUnits('-1._', 3)).toBe('-1_')
|
||||
expect(roundOffWithUnits('1.23456789e3mm', 3)).toBe('1234.568mm')
|
||||
expect(roundOffWithUnits('1.23456789e3m', 3)).toBe('1234.568m')
|
||||
expect(roundOffWithUnits('1.23456789e3in', 3)).toBe('1234.568in')
|
||||
expect(roundOffWithUnits('1.23456789e3_', 3)).toBe('1234.568_')
|
||||
expect(roundOffWithUnits('1.e3_', 3)).toBe('1000_')
|
||||
expect(roundOffWithUnits('1e3_', 3)).toBe('1000_')
|
||||
expect(roundOffWithUnits('1.23456789e-3mm', 3)).toBe('0.001mm')
|
||||
expect(roundOffWithUnits('1.23456789e-3m', 3)).toBe('0.001m')
|
||||
expect(roundOffWithUnits('1.23456789e-3in', 3)).toBe('0.001in')
|
||||
expect(roundOffWithUnits('1.23456789e-3_', 3)).toBe('0.001_')
|
||||
expect(roundOffWithUnits('1.e-3_', 3)).toBe('0.001_')
|
||||
expect(roundOffWithUnits('1e-3_', 3)).toBe('0.001_')
|
||||
})
|
||||
it('works with weird units', () => {
|
||||
expect(roundOffWithUnits('1.23456789_?', 3)).toBe('1.235_?')
|
||||
expect(roundOffWithUnits('-1.23456789_?', 3)).toBe('-1.235_?')
|
||||
})
|
||||
it('returns the original string when used with something not parsable as a number', () => {
|
||||
expect(roundOffWithUnits('foo', 3)).toBe('foo')
|
||||
})
|
||||
})
|
||||
|
||||
describe('testing hasLeadingZero', () => {
|
||||
it('.1 should have no leading zero', () => {
|
||||
const actual = hasLeadingZero('.1')
|
||||
|
@ -328,6 +328,32 @@ export function roundOff(num: number, precision: number = 2): number {
|
||||
return Math.round(num * x) / x
|
||||
}
|
||||
|
||||
export function roundOffWithUnits(
|
||||
numWithUnits: string,
|
||||
precision: number = 2
|
||||
): string {
|
||||
const match = numWithUnits.match(
|
||||
/^([+-]?[\d.]+(?:[eE][+-]?\d+)?)([a-zA-Z_?]+)$/
|
||||
)
|
||||
let num: string
|
||||
let suffix: string
|
||||
if (match) {
|
||||
num = match[1]
|
||||
suffix = match[2] ?? ''
|
||||
} else {
|
||||
// If no match, assume it's just a number with no units.
|
||||
num = numWithUnits
|
||||
suffix = ''
|
||||
}
|
||||
const parsedNum = parseFloat(num)
|
||||
if (Number.isNaN(parsedNum)) {
|
||||
// If parsing fails, return the original string.
|
||||
return numWithUnits
|
||||
}
|
||||
const roundedNum = roundOff(parsedNum, precision)
|
||||
return `${roundedNum}${suffix}`
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the number as a string has any precision in the decimal places
|
||||
* '1' -> 0
|
||||
|
@ -13,6 +13,7 @@ import type {
|
||||
default_app_settings as DefaultAppSettings,
|
||||
default_project_settings as DefaultProjectSettings,
|
||||
format_number_literal as FormatNumberLiteral,
|
||||
format_number_value as FormatNumberValue,
|
||||
human_display_number as HumanDisplayNumber,
|
||||
get_kcl_version as GetKclVersion,
|
||||
get_tangential_arc_to_info as GetTangentialArcToInfo,
|
||||
@ -59,6 +60,9 @@ export const recast_wasm: typeof RecastWasm = (...args) => {
|
||||
export const format_number_literal: typeof FormatNumberLiteral = (...args) => {
|
||||
return getModule().format_number_literal(...args)
|
||||
}
|
||||
export const format_number_value: typeof FormatNumberValue = (...args) => {
|
||||
return getModule().format_number_value(...args)
|
||||
}
|
||||
export const human_display_number: typeof HumanDisplayNumber = (...args) => {
|
||||
return getModule().human_display_number(...args)
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ export const modelingDesignRole = (
|
||||
label: 'Design',
|
||||
submenu: [
|
||||
{
|
||||
label: 'Start sketch',
|
||||
label: 'Start Sketch',
|
||||
id: 'Design.Start sketch',
|
||||
click: () => {
|
||||
typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', {
|
||||
@ -19,7 +19,7 @@ export const modelingDesignRole = (
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Create an offset plane',
|
||||
label: 'Create an Offset Plane',
|
||||
id: 'Design.Create an offset plane',
|
||||
click: () => {
|
||||
typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', {
|
||||
@ -28,7 +28,7 @@ export const modelingDesignRole = (
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Create a helix',
|
||||
label: 'Create a Helix',
|
||||
id: 'Design.Create a helix',
|
||||
click: () => {
|
||||
typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', {
|
||||
@ -37,7 +37,7 @@ export const modelingDesignRole = (
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Create a parameter',
|
||||
label: 'Create a Parameter',
|
||||
id: 'Design.Create a parameter',
|
||||
click: () => {
|
||||
typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', {
|
||||
@ -47,7 +47,7 @@ export const modelingDesignRole = (
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Create an additive feature',
|
||||
label: 'Create an Additive Feature',
|
||||
id: 'Design.Create an additive feature',
|
||||
submenu: [
|
||||
{
|
||||
@ -89,7 +89,7 @@ export const modelingDesignRole = (
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Apply modification feature',
|
||||
label: 'Apply Modification Feature',
|
||||
id: 'Design.Apply modification feature',
|
||||
submenu: [
|
||||
{
|
||||
@ -123,7 +123,7 @@ export const modelingDesignRole = (
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Insert from project file',
|
||||
label: 'Insert from Project File',
|
||||
id: 'Design.Insert from project file',
|
||||
click: () => {
|
||||
typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', {
|
||||
|
@ -30,7 +30,7 @@ export const projectEditRole = (
|
||||
label: 'Edit',
|
||||
submenu: [
|
||||
{
|
||||
label: 'Rename project',
|
||||
label: 'Rename Project',
|
||||
id: 'Edit.Rename project',
|
||||
click: () => {
|
||||
typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', {
|
||||
@ -39,7 +39,7 @@ export const projectEditRole = (
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Delete project',
|
||||
label: 'Delete Project',
|
||||
id: 'Edit.Delete project',
|
||||
click: () => {
|
||||
typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', {
|
||||
@ -49,7 +49,7 @@ export const projectEditRole = (
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Change project directory',
|
||||
label: 'Change Project Directory',
|
||||
id: 'Edit.Change project directory',
|
||||
click: () => {
|
||||
typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', {
|
||||
@ -102,7 +102,7 @@ export const modelingEditRole = (
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Edit parameter',
|
||||
label: 'Edit Parameter',
|
||||
id: 'Edit.Edit parameter',
|
||||
click: () => {
|
||||
typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', {
|
||||
@ -111,7 +111,7 @@ export const modelingEditRole = (
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Format code',
|
||||
label: 'Format Code',
|
||||
id: 'Edit.Format code',
|
||||
click: () => {
|
||||
typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', {
|
||||
@ -121,7 +121,7 @@ export const modelingEditRole = (
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Rename project',
|
||||
label: 'Rename Project',
|
||||
id: 'Edit.Rename project',
|
||||
click: () => {
|
||||
typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', {
|
||||
@ -130,7 +130,7 @@ export const modelingEditRole = (
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Delete project',
|
||||
label: 'Delete Project',
|
||||
id: 'Edit.Delete project',
|
||||
click: () => {
|
||||
typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', {
|
||||
@ -140,7 +140,7 @@ export const modelingEditRole = (
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Change project directory',
|
||||
label: 'Change Project Directory',
|
||||
id: 'Edit.Change project directory',
|
||||
click: () => {
|
||||
typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', {
|
||||
|
@ -13,7 +13,7 @@ export const projectFileRole = (
|
||||
label: 'File',
|
||||
submenu: [
|
||||
{
|
||||
label: 'Create project',
|
||||
label: 'Create Project',
|
||||
id: 'File.Create project',
|
||||
accelerator: 'CommandOrControl+N',
|
||||
click: () => {
|
||||
@ -23,7 +23,7 @@ export const projectFileRole = (
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Open project',
|
||||
label: 'Open Project',
|
||||
id: 'File.Open project',
|
||||
accelerator: 'CommandOrControl+P',
|
||||
click: () => {
|
||||
@ -36,7 +36,7 @@ export const projectFileRole = (
|
||||
// Appears to be only Windows and Mac OS specific. Linux does not have support
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Add file to project',
|
||||
label: 'Add File to Project',
|
||||
id: 'File.Add file to project',
|
||||
click: () => {
|
||||
typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', {
|
||||
@ -58,7 +58,7 @@ export const projectFileRole = (
|
||||
label: 'Preferences',
|
||||
submenu: [
|
||||
{
|
||||
label: 'User settings',
|
||||
label: 'User Settings',
|
||||
id: 'File.Preferences.User settings',
|
||||
click: () => {
|
||||
typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', {
|
||||
@ -76,7 +76,7 @@ export const projectFileRole = (
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'User default units',
|
||||
label: 'User Default Units',
|
||||
id: 'File.Preferences.User default units',
|
||||
click: () => {
|
||||
typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', {
|
||||
@ -94,7 +94,7 @@ export const projectFileRole = (
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Theme color',
|
||||
label: 'Theme Color',
|
||||
id: 'File.Preferences.Theme color',
|
||||
click: () => {
|
||||
typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', {
|
||||
@ -107,7 +107,7 @@ export const projectFileRole = (
|
||||
{ type: 'separator' },
|
||||
// Last in list
|
||||
{
|
||||
label: 'Sign out',
|
||||
label: 'Sign Out',
|
||||
id: 'File.Sign out',
|
||||
click: () => {
|
||||
typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', {
|
||||
@ -128,7 +128,7 @@ export const modelingFileRole = (
|
||||
submenu: [
|
||||
// TODO: Once a safe command bar create new file and folder is implemented we can turn these on
|
||||
// {
|
||||
// label: 'Create new file',
|
||||
// label: 'Create New File',
|
||||
// click: () => {
|
||||
// typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', {
|
||||
// menuLabel: 'File.Create new file',
|
||||
@ -136,7 +136,7 @@ export const modelingFileRole = (
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// label: 'Create new folder',
|
||||
// label: 'Create New Folder',
|
||||
// click: () => {
|
||||
// typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', {
|
||||
// menuLabel: 'File.Create new folder',
|
||||
@ -144,7 +144,7 @@ export const modelingFileRole = (
|
||||
// },
|
||||
// },
|
||||
{
|
||||
label: 'Create project',
|
||||
label: 'Create Project',
|
||||
id: 'File.Create project',
|
||||
accelerator: 'CommandOrControl+N',
|
||||
click: () => {
|
||||
@ -154,7 +154,7 @@ export const modelingFileRole = (
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Open project',
|
||||
label: 'Open Project',
|
||||
id: 'File.Open project',
|
||||
accelerator: 'CommandOrControl+P',
|
||||
click: () => {
|
||||
@ -167,7 +167,7 @@ export const modelingFileRole = (
|
||||
// Appears to be only Windows and Mac OS specific. Linux does not have support
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Add file to project',
|
||||
label: 'Add File to Project',
|
||||
id: 'File.Add file to project',
|
||||
click: () => {
|
||||
typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', {
|
||||
@ -176,7 +176,7 @@ export const modelingFileRole = (
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Export current part',
|
||||
label: 'Export Current Part',
|
||||
id: 'File.Export current part',
|
||||
click: () => {
|
||||
typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', {
|
||||
@ -189,7 +189,7 @@ export const modelingFileRole = (
|
||||
label: 'Preferences',
|
||||
submenu: [
|
||||
{
|
||||
label: 'Project settings',
|
||||
label: 'Project Settings',
|
||||
id: 'File.Preferences.Project settings',
|
||||
click: () => {
|
||||
typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', {
|
||||
@ -198,7 +198,7 @@ export const modelingFileRole = (
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'User settings',
|
||||
label: 'User Settings',
|
||||
id: 'File.Preferences.User settings',
|
||||
click: () => {
|
||||
typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', {
|
||||
@ -216,7 +216,7 @@ export const modelingFileRole = (
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'User default units',
|
||||
label: 'User Default Units',
|
||||
id: 'File.Preferences.User default units',
|
||||
click: () => {
|
||||
typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', {
|
||||
@ -234,7 +234,7 @@ export const modelingFileRole = (
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Theme color',
|
||||
label: 'Theme Color',
|
||||
id: 'File.Preferences.Theme color',
|
||||
click: () => {
|
||||
typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', {
|
||||
@ -247,7 +247,7 @@ export const modelingFileRole = (
|
||||
{ type: 'separator' },
|
||||
// Last in list
|
||||
{
|
||||
label: 'Sign out',
|
||||
label: 'Sign Out',
|
||||
id: 'File.Sign out',
|
||||
click: () => {
|
||||
typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', {
|
||||
|
@ -14,7 +14,7 @@ export const helpRole = (
|
||||
submenu: [
|
||||
{
|
||||
id: 'Help.Show all commands',
|
||||
label: 'Show all commands',
|
||||
label: 'Show All Commands',
|
||||
click: () => {
|
||||
typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', {
|
||||
menuLabel: 'Help.Command Palette...',
|
||||
@ -22,7 +22,7 @@ export const helpRole = (
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'KCL code samples',
|
||||
label: 'KCL Code Samples',
|
||||
id: 'Help.KCL code samples',
|
||||
click: () => {
|
||||
shell
|
||||
@ -31,13 +31,13 @@ export const helpRole = (
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'KCL docs',
|
||||
label: 'KCL Docs',
|
||||
click: () => {
|
||||
shell.openExternal('https://zoo.dev/docs/kcl').catch(reportRejection)
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Get started with Text-to-CAD',
|
||||
label: 'Get Started with Text-to-CAD',
|
||||
click: () => {
|
||||
shell
|
||||
.openExternal('https://text-to-cad.zoo.dev/dashboard')
|
||||
@ -46,7 +46,7 @@ export const helpRole = (
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Ask the community discord',
|
||||
label: 'Ask the Community Discord',
|
||||
click: () => {
|
||||
shell
|
||||
.openExternal('https://discord.gg/JQEpHR7Nt2')
|
||||
@ -54,7 +54,7 @@ export const helpRole = (
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Ask the community discourse',
|
||||
label: 'Ask the Community Discourse',
|
||||
click: () => {
|
||||
shell
|
||||
.openExternal('https://community.zoo.dev/')
|
||||
@ -63,7 +63,7 @@ export const helpRole = (
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Report a bug',
|
||||
label: 'Report a Bug',
|
||||
id: 'Help.Report a bug',
|
||||
click: () => {
|
||||
shell
|
||||
@ -74,7 +74,7 @@ export const helpRole = (
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Request a feature',
|
||||
label: 'Request a Feature',
|
||||
click: () => {
|
||||
shell
|
||||
.openExternal(
|
||||
@ -86,7 +86,7 @@ export const helpRole = (
|
||||
{ type: 'separator' },
|
||||
{
|
||||
id: 'Help.Replay onboarding tutorial',
|
||||
label: 'Replay onboarding tutorial',
|
||||
label: 'Replay Onboarding Tutorial',
|
||||
click: () => {
|
||||
typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', {
|
||||
menuLabel: 'Help.Replay onboarding tutorial',
|
||||
@ -99,7 +99,7 @@ export const helpRole = (
|
||||
{ role: 'forceReload' },
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Show release notes',
|
||||
label: 'Show Release Notes',
|
||||
click: () => {
|
||||
shell
|
||||
.openExternal('https://github.com/KittyCAD/modeling-app/releases')
|
||||
@ -107,14 +107,14 @@ export const helpRole = (
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Check for updates',
|
||||
label: 'Check for Updates',
|
||||
click: () => {
|
||||
getAutoUpdater().checkForUpdates().catch(reportRejection)
|
||||
},
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Manage account',
|
||||
label: 'Manage Account',
|
||||
click: () => {
|
||||
shell.openExternal('https://zoo.dev/account').catch(reportRejection)
|
||||
},
|
||||
|
@ -12,47 +12,47 @@ type HeaderLabel =
|
||||
| 'View'
|
||||
|
||||
type FileRoleLabel =
|
||||
| 'Open project'
|
||||
| 'Create project'
|
||||
| 'Import file from URL'
|
||||
| 'Open Project'
|
||||
| 'Create Project'
|
||||
| 'Import File from URL'
|
||||
| 'Preferences'
|
||||
| 'User settings'
|
||||
| 'User Settings'
|
||||
| 'Keybindings'
|
||||
| 'Sign out'
|
||||
| 'Sign Out'
|
||||
| 'Theme'
|
||||
| 'Theme color'
|
||||
| 'Export current part'
|
||||
| 'Create new file'
|
||||
| 'Create new folder'
|
||||
| 'Share part via Zoo link'
|
||||
| 'Project settings'
|
||||
| 'Add file to project'
|
||||
| 'User default units'
|
||||
| 'Theme Color'
|
||||
| 'Export Current Part'
|
||||
| 'Create New File'
|
||||
| 'Create New Folder'
|
||||
| 'Share Part via Zoo Link'
|
||||
| 'Project Settings'
|
||||
| 'Add File to Project'
|
||||
| 'User Default Units'
|
||||
|
||||
type EditRoleLabel =
|
||||
| 'Rename project'
|
||||
| 'Delete project'
|
||||
| 'Change project directory'
|
||||
| 'Rename Project'
|
||||
| 'Delete Project'
|
||||
| 'Change Project Directory'
|
||||
| 'Undo'
|
||||
| 'Redo'
|
||||
| 'Speech'
|
||||
| 'Edit parameter'
|
||||
| 'Edit Parameter'
|
||||
| 'Modify with Zoo Text-To-CAD'
|
||||
| 'Format code'
|
||||
| 'Format Code'
|
||||
|
||||
type HelpRoleLabel =
|
||||
| 'Report a bug'
|
||||
| 'Request a feature'
|
||||
| 'Ask the community discord'
|
||||
| 'Ask the community discourse'
|
||||
| 'KCL code samples'
|
||||
| 'KCL docs'
|
||||
| 'Replay onboarding tutorial'
|
||||
| 'Show release notes'
|
||||
| 'Check for updates'
|
||||
| 'Manage account'
|
||||
| 'Get started with Text-to-CAD'
|
||||
| 'Show all commands'
|
||||
| 'Report a Bug'
|
||||
| 'Request a Feature'
|
||||
| 'Ask the Community Discord'
|
||||
| 'Ask the Community Discourse'
|
||||
| 'KCL Code Samples'
|
||||
| 'KCL Docs'
|
||||
| 'Replay Onboarding Tutorial'
|
||||
| 'Show Release Notes'
|
||||
| 'Check for Updates'
|
||||
| 'Manage Account'
|
||||
| 'Get Started with Text-to-CAD'
|
||||
| 'Show All Commands'
|
||||
|
||||
type ViewRoleLabel =
|
||||
| 'Command Palette...'
|
||||
@ -64,37 +64,37 @@ type ViewRoleLabel =
|
||||
| 'Variables'
|
||||
| 'Logs'
|
||||
| 'Debug'
|
||||
| 'Standard views'
|
||||
| 'Orthographic view'
|
||||
| 'Perspective view'
|
||||
| 'Right view'
|
||||
| 'Back view'
|
||||
| 'Top view'
|
||||
| 'Left view'
|
||||
| 'Front view'
|
||||
| 'Bottom view'
|
||||
| 'Reset view'
|
||||
| 'Center view on selection'
|
||||
| 'Standard Views'
|
||||
| 'Orthographic View'
|
||||
| 'Perspective View'
|
||||
| 'Right View'
|
||||
| 'Back View'
|
||||
| 'Top View'
|
||||
| 'Left View'
|
||||
| 'Front View'
|
||||
| 'Bottom View'
|
||||
| 'Reset View'
|
||||
| 'Center View on Selection'
|
||||
| 'Refresh'
|
||||
| 'Named views'
|
||||
| 'Create named view'
|
||||
| 'Load named view'
|
||||
| 'Delete named view'
|
||||
| 'Named Views'
|
||||
| 'Create Named View'
|
||||
| 'Load Named View'
|
||||
| 'Delete Named View'
|
||||
|
||||
type DesignRoleLabel =
|
||||
| 'Design'
|
||||
| 'Create a parameter'
|
||||
| 'Insert from project file'
|
||||
| 'Create a Parameter'
|
||||
| 'Insert from Project File'
|
||||
| 'Create with Zoo Text-To-CAD'
|
||||
| 'Start sketch'
|
||||
| 'Create an offset plane'
|
||||
| 'Create a helix'
|
||||
| 'Create an additive feature'
|
||||
| 'Start Sketch'
|
||||
| 'Create an Offset Plane'
|
||||
| 'Create a Helix'
|
||||
| 'Create an Additive Feature'
|
||||
| 'Extrude'
|
||||
| 'Revolve'
|
||||
| 'Sweep'
|
||||
| 'Loft'
|
||||
| 'Apply modification feature'
|
||||
| 'Apply Modification Feature'
|
||||
| 'Fillet'
|
||||
| 'Chamfer'
|
||||
| 'Shell'
|
||||
|
@ -74,7 +74,7 @@ export const modelingViewRole = (
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Orthographic view',
|
||||
label: 'Orthographic View',
|
||||
id: 'View.Orthographic view',
|
||||
click: () => {
|
||||
typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', {
|
||||
@ -83,7 +83,7 @@ export const modelingViewRole = (
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Perspective view',
|
||||
label: 'Perspective View',
|
||||
id: 'View.Perspective view',
|
||||
click: () => {
|
||||
typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', {
|
||||
@ -93,11 +93,11 @@ export const modelingViewRole = (
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Standard views',
|
||||
label: 'Standard Views',
|
||||
id: 'View.Standard views',
|
||||
submenu: [
|
||||
{
|
||||
label: 'Right view',
|
||||
label: 'Right View',
|
||||
id: 'View.Standard views.Right view',
|
||||
click: () => {
|
||||
typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', {
|
||||
@ -106,7 +106,7 @@ export const modelingViewRole = (
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Back view',
|
||||
label: 'Back View',
|
||||
id: 'View.Standard views.Back view',
|
||||
click: () => {
|
||||
typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', {
|
||||
@ -114,9 +114,8 @@ export const modelingViewRole = (
|
||||
})
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
label: 'Top view',
|
||||
label: 'Top View',
|
||||
id: 'View.Standard views.Top view',
|
||||
click: () => {
|
||||
typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', {
|
||||
@ -124,9 +123,8 @@ export const modelingViewRole = (
|
||||
})
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
label: 'Left view',
|
||||
label: 'Left View',
|
||||
id: 'View.Standard views.Left view',
|
||||
click: () => {
|
||||
typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', {
|
||||
@ -134,9 +132,8 @@ export const modelingViewRole = (
|
||||
})
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
label: 'Front view',
|
||||
label: 'Front View',
|
||||
id: 'View.Standard views.Front view',
|
||||
click: () => {
|
||||
typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', {
|
||||
@ -144,9 +141,8 @@ export const modelingViewRole = (
|
||||
})
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
label: 'Bottom view',
|
||||
label: 'Bottom View',
|
||||
id: 'View.Standard views.Bottom view',
|
||||
click: () => {
|
||||
typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', {
|
||||
@ -156,7 +152,7 @@ export const modelingViewRole = (
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Reset view',
|
||||
label: 'Reset View',
|
||||
id: 'View.Standard views.Reset view',
|
||||
click: () => {
|
||||
typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', {
|
||||
@ -164,9 +160,8 @@ export const modelingViewRole = (
|
||||
})
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
label: 'Center view on selection',
|
||||
label: 'Center View on Selection',
|
||||
id: 'View.Standard views.Center view on selection',
|
||||
click: () => {
|
||||
typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', {
|
||||
@ -186,11 +181,11 @@ export const modelingViewRole = (
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Named views',
|
||||
label: 'Named Views',
|
||||
id: 'View.Named views',
|
||||
submenu: [
|
||||
{
|
||||
label: 'Create named view',
|
||||
label: 'Create Named View',
|
||||
id: 'View.Named views.Create named view',
|
||||
click: () => {
|
||||
typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', {
|
||||
@ -198,9 +193,8 @@ export const modelingViewRole = (
|
||||
})
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
label: 'Load named view',
|
||||
label: 'Load Named View',
|
||||
id: 'View.Named views.Load named view',
|
||||
click: () => {
|
||||
typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', {
|
||||
@ -208,9 +202,8 @@ export const modelingViewRole = (
|
||||
})
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
label: 'Delete named view',
|
||||
label: 'Delete Named View',
|
||||
id: 'View.Named views.Delete named view',
|
||||
click: () => {
|
||||
typeSafeWebContentsSend(mainWindow, 'menu-action-clicked', {
|
||||
|
@ -66,6 +66,7 @@ import {
|
||||
defaultLocalStatusBarItems,
|
||||
defaultGlobalStatusBarItems,
|
||||
} from '@src/components/StatusBar/defaultStatusBarItems'
|
||||
import { useSelector } from '@xstate/react'
|
||||
|
||||
type ReadWriteProjectState = {
|
||||
value: boolean
|
||||
@ -81,6 +82,8 @@ const Home = () => {
|
||||
const [nativeFileMenuCreated, setNativeFileMenuCreated] = useState(false)
|
||||
const apiToken = useToken()
|
||||
const networkMachineStatus = useNetworkMachineStatus()
|
||||
const billingContext = useSelector(billingActor, ({ context }) => context)
|
||||
const hasUnlimitedCredits = billingContext.credits === Infinity
|
||||
|
||||
// Only create the native file menus on desktop
|
||||
useEffect(() => {
|
||||
@ -354,11 +357,13 @@ const Home = () => {
|
||||
</li>
|
||||
</ul>
|
||||
<ul className="flex flex-col">
|
||||
<li className="contents">
|
||||
<div className="my-2">
|
||||
<BillingDialog billingActor={billingActor} />
|
||||
</div>
|
||||
</li>
|
||||
{!hasUnlimitedCredits && (
|
||||
<li className="contents">
|
||||
<div className="my-2">
|
||||
<BillingDialog billingActor={billingActor} />
|
||||
</div>
|
||||
</li>
|
||||
)}
|
||||
<li className="contents">
|
||||
<ActionButton
|
||||
Element="externalLink"
|
||||
|
Reference in New Issue
Block a user