Compare commits
12 Commits
jtran/fix-
...
v0.24.1
Author | SHA1 | Date | |
---|---|---|---|
f9419a98b5 | |||
999f72bccf | |||
9dbe74e008 | |||
88d9cdc52b | |||
2dd1f0f213 | |||
b971f3ecf4 | |||
2198bd7580 | |||
5fa1497b75 | |||
ff86e41283 | |||
08e4c03ca7 | |||
c654582137 | |||
6c2fa95a32 |
33
.github/workflows/ci.yml
vendored
@ -13,6 +13,7 @@ on:
|
||||
# Will checkout the last commit from the default branch (main as of 2023-10-04)
|
||||
|
||||
env:
|
||||
CUT_RELEASE_PR: ${{ github.event_name == 'pull_request' && (contains(github.event.pull_request.title, 'Cut release v')) }}
|
||||
BUILD_RELEASE: ${{ github.event_name == 'release' || github.event_name == 'schedule' || github.event_name == 'pull_request' && (contains(github.event.pull_request.title, 'Cut release v')) }}
|
||||
|
||||
concurrency:
|
||||
@ -110,8 +111,14 @@ jobs:
|
||||
echo "$(jq --arg name 'Zoo Modeling App (Nightly)' \
|
||||
'.productName=$name' src-tauri/tauri.release.conf.json --indent 2)" > src-tauri/tauri.release.conf.json
|
||||
|
||||
- name: Set updater test version
|
||||
if: ${{ env.CUT_RELEASE_PR == 'true' }}
|
||||
run: |
|
||||
echo "$(jq --arg url 'https://dl.zoo.dev/releases/modeling-app/test/last_update.json' \
|
||||
'.plugins.updater.endpoints[]=$url' src-tauri/tauri.release.conf.json --indent 2)" > src-tauri/tauri.release.conf.json
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: github.event_name == 'schedule'
|
||||
if: ${{ github.event_name == 'schedule' || env.CUT_RELEASE_PR == 'true' }}
|
||||
with:
|
||||
path: |
|
||||
package.json
|
||||
@ -377,6 +384,30 @@ jobs:
|
||||
E2E_TAURI_ENABLED: true
|
||||
TS_NODE_COMPILER_OPTIONS: '{"module": "commonjs"}'
|
||||
|
||||
- uses: actions/download-artifact@v3
|
||||
if: ${{ env.CUT_RELEASE_PR == 'true' }}
|
||||
|
||||
- name: Copy updated .json file for updater test
|
||||
if: ${{ env.CUT_RELEASE_PR == 'true' }}
|
||||
run: |
|
||||
ls -l artifact
|
||||
cp artifact/src-tauri/tauri.release.conf.json src-tauri/tauri.release.conf.json
|
||||
cat src-tauri/tauri.release.conf.json
|
||||
|
||||
- name: Build the app (release, updater test)
|
||||
if: ${{ env.CUT_RELEASE_PR == 'true' && matrix.os != 'ubuntu-latest' }}
|
||||
env:
|
||||
TAURI_CONF_ARGS: "-c ${{ matrix.os == 'windows-latest' && 'src-tauri\\tauri.release.conf.json' || 'src-tauri/tauri.release.conf.json' }}"
|
||||
TAURI_BUNDLE_ARGS: "-b ${{ matrix.os == 'windows-latest' && 'msi' || 'dmg' }}"
|
||||
run: "yarn tauri build ${{ env.TAURI_CONF_ARGS }} ${{ env.TAURI_BUNDLE_ARGS }} ${{ env.TAURI_ARGS_MACOS }}"
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: ${{ env.CUT_RELEASE_PR == 'true' && matrix.os != 'ubuntu-latest' }}
|
||||
with:
|
||||
path: "${{ matrix.os == 'macos-14' && 'src-tauri/target/universal-apple-darwin/release/bundle/dmg/*.dmg' || 'src-tauri/target/release/bundle/msi/*.msi' }}"
|
||||
name: updater-test
|
||||
|
||||
|
||||
publish-apps-release:
|
||||
permissions:
|
||||
contents: write
|
||||
|
151
.github/workflows/playwright.yml
vendored
@ -83,6 +83,20 @@ jobs:
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: './src/wasm-lib'
|
||||
- name: Install vector
|
||||
run: |
|
||||
curl --proto '=https' --tlsv1.2 -sSfL https://sh.vector.dev > /tmp/vector.sh
|
||||
chmod +x /tmp/vector.sh
|
||||
/tmp/vector.sh -y -no-modify-path
|
||||
mkdir -p /tmp/vector
|
||||
cp .github/workflows/vector.toml /tmp/vector.toml
|
||||
sed -i "s#GITHUB_WORKFLOW#${GITHUB_WORKFLOW}#g" /tmp/vector.toml
|
||||
sed -i "s#GITHUB_REPOSITORY#${GITHUB_REPOSITORY}#g" /tmp/vector.toml
|
||||
sed -i "s#GITHUB_SHA#${GITHUB_SHA}#g" /tmp/vector.toml
|
||||
sed -i "s#GITHUB_REF_NAME#${GITHUB_REF_NAME}#g" /tmp/vector.toml
|
||||
sed -i "s#GH_ACTIONS_AXIOM_TOKEN#${{secrets.GH_ACTIONS_AXIOM_TOKEN}}#g" /tmp/vector.toml
|
||||
cat /tmp/vector.toml
|
||||
${HOME}/.vector/bin/vector --config /tmp/vector.toml &
|
||||
- name: Build Wasm (because rust diff)
|
||||
if: needs.check-rust-changes.outputs.rust-changed == 'true'
|
||||
run: yarn build:wasm
|
||||
@ -139,27 +153,60 @@ jobs:
|
||||
with:
|
||||
name: test-results-ubuntu-${{ github.sha }}
|
||||
path: test-results/
|
||||
- name: Run ubuntu/chrome flow retry failures
|
||||
- name: Run ubuntu/chrome flow (with retries)
|
||||
id: retry
|
||||
if: always()
|
||||
run: |
|
||||
if [[ -d "test-results" ]];
|
||||
then if [[ $(ls -1 "test-results" | wc -l) != "0" ]];
|
||||
then echo "retried=true" >> $GITHUB_OUTPUT;
|
||||
else echo "retried=false" >> $GITHUB_OUTPUT; exit 0;
|
||||
fi;
|
||||
else echo "retried=false" >> $GITHUB_OUTPUT; exit 0;
|
||||
fi;
|
||||
yarn playwright test --project="Google Chrome" --last-failed e2e/playwright/flow-tests.spec.ts
|
||||
env:
|
||||
CI: true
|
||||
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
- name: Run ubuntu/chrome flow
|
||||
if: steps.retry.outputs.retried == 'false'
|
||||
run: yarn playwright test --project="Google Chrome" e2e/playwright/flow-tests.spec.ts
|
||||
if [[ ! -f "test-results/.last-run.json" ]]; then
|
||||
# if no last run artifact, than run plawright normally
|
||||
echo "run playwright normally"
|
||||
yarn playwright test --project="Google Chrome" e2e/playwright/flow-tests.spec.ts || true
|
||||
# # send to axiom
|
||||
node playwrightProcess.mjs | tee /tmp/github-actions.log > /dev/null 2>&1
|
||||
fi
|
||||
|
||||
retry=1
|
||||
max_retrys=4
|
||||
|
||||
# retry failed tests, doing our own retries because using inbuilt playwright retries causes connection issues
|
||||
while [[ $retry -le $max_retrys ]]; do
|
||||
if [[ -f "test-results/.last-run.json" ]]; then
|
||||
failed_tests=$(jq '.failedTests | length' test-results/.last-run.json)
|
||||
if [[ $failed_tests -gt 0 ]]; then
|
||||
echo "retried=true" >>$GITHUB_OUTPUT
|
||||
echo "run playwright with last failed tests and retry $retry"
|
||||
yarn playwright test --project="Google Chrome" --last-failed e2e/playwright/flow-tests.spec.ts || true
|
||||
# send to axiom
|
||||
node playwrightProcess.mjs | tee /tmp/github-actions.log > /dev/null 2>&1
|
||||
retry=$((retry + 1))
|
||||
else
|
||||
echo "retried=false" >>$GITHUB_OUTPUT
|
||||
exit 0
|
||||
fi
|
||||
else
|
||||
echo "retried=false" >>$GITHUB_OUTPUT
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
|
||||
echo "retried=false" >>$GITHUB_OUTPUT
|
||||
|
||||
if [[ -f "test-results/.last-run.json" ]]; then
|
||||
failed_tests=$(jq '.failedTests | length' test-results/.last-run.json)
|
||||
if [[ $failed_tests -gt 0 ]]; then
|
||||
# if it still fails after 3 retrys, then fail the job
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
exit 0
|
||||
env:
|
||||
CI: true
|
||||
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
- name: send to axiom
|
||||
if: always()
|
||||
shell: bash
|
||||
run: |
|
||||
node playwrightProcess.mjs | tee /tmp/github-actions.log
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
@ -226,6 +273,20 @@ jobs:
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: './src/wasm-lib'
|
||||
- name: Install vector
|
||||
run: |
|
||||
curl --proto '=https' --tlsv1.2 -sSfL https://sh.vector.dev > /tmp/vector.sh
|
||||
chmod +x /tmp/vector.sh
|
||||
/tmp/vector.sh -y -no-modify-path
|
||||
mkdir -p /tmp/vector
|
||||
cp .github/workflows/vector.toml /tmp/vector.toml
|
||||
sed -i "" "s#GITHUB_WORKFLOW#${GITHUB_WORKFLOW}#g" /tmp/vector.toml
|
||||
sed -i "" "s#GITHUB_REPOSITORY#${GITHUB_REPOSITORY}#g" /tmp/vector.toml
|
||||
sed -i "" "s#GITHUB_SHA#${GITHUB_SHA}#g" /tmp/vector.toml
|
||||
sed -i "" "s#GITHUB_REF_NAME#${GITHUB_REF_NAME}#g" /tmp/vector.toml
|
||||
sed -i "" "s#GH_ACTIONS_AXIOM_TOKEN#${{secrets.GH_ACTIONS_AXIOM_TOKEN}}#g" /tmp/vector.toml
|
||||
cat /tmp/vector.toml
|
||||
${HOME}/.vector/bin/vector --config /tmp/vector.toml &
|
||||
- name: Build Wasm (because rust diff)
|
||||
if: needs.check-rust-changes.outputs.rust-changed == 'true'
|
||||
run: yarn build:wasm
|
||||
@ -241,26 +302,52 @@ jobs:
|
||||
with:
|
||||
name: test-results-macos-${{ github.sha }}
|
||||
path: test-results/
|
||||
- name: Run macos/safari flow retry failures
|
||||
- name: Run macos/safari flow (with retries)
|
||||
id: retry
|
||||
if: always()
|
||||
run: |
|
||||
if [[ -d "test-results" ]];
|
||||
then if [[ $(ls -1 "test-results" | wc -l) != "0" ]];
|
||||
then echo "retried=true" >> $GITHUB_OUTPUT;
|
||||
else echo "retried=false" >> $GITHUB_OUTPUT; exit 0;
|
||||
fi;
|
||||
else echo "retried=false" >> $GITHUB_OUTPUT; exit 0;
|
||||
fi;
|
||||
yarn playwright test --project="webkit" --last-failed e2e/playwright/flow-tests.spec.ts
|
||||
env:
|
||||
CI: true
|
||||
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
- name: Run macos/safari flow
|
||||
if: steps.retry.outputs.retried == 'false'
|
||||
# webkit doesn't work on Ubuntu because of the same reason tauri doesn't (webRTC issues)
|
||||
# TODO remove this and the matrix and run all tests on ubuntu when this is fixed
|
||||
run: yarn playwright test --project="webkit" e2e/playwright/flow-tests.spec.ts
|
||||
if [[ ! -f "test-results/.last-run.json" ]]; then
|
||||
# if no last run artifact, than run plawright normally
|
||||
echo "run playwright normally"
|
||||
yarn playwright test --project="webkit" e2e/playwright/flow-tests.spec.ts || true
|
||||
# # send to axiom
|
||||
node playwrightProcess.mjs | tee /tmp/github-actions.log > /dev/null 2>&1
|
||||
fi
|
||||
|
||||
retry=1
|
||||
max_retrys=4
|
||||
|
||||
# retry failed tests, doing our own retries because using inbuilt playwright retries causes connection issues
|
||||
while [[ $retry -le $max_retrys ]]; do
|
||||
if [[ -f "test-results/.last-run.json" ]]; then
|
||||
failed_tests=$(jq '.failedTests | length' test-results/.last-run.json)
|
||||
if [[ $failed_tests -gt 0 ]]; then
|
||||
echo "retried=true" >>$GITHUB_OUTPUT
|
||||
echo "run playwright with last failed tests and retry $retry"
|
||||
yarn playwright test --project="webkit" --last-failed e2e/playwright/flow-tests.spec.ts || true
|
||||
# send to axiom
|
||||
node playwrightProcess.mjs | tee /tmp/github-actions.log > /dev/null 2>&1
|
||||
retry=$((retry + 1))
|
||||
else
|
||||
echo "retried=false" >>$GITHUB_OUTPUT
|
||||
exit 0
|
||||
fi
|
||||
else
|
||||
echo "retried=false" >>$GITHUB_OUTPUT
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
|
||||
echo "retried=false" >>$GITHUB_OUTPUT
|
||||
|
||||
if [[ -f "test-results/.last-run.json" ]]; then
|
||||
failed_tests=$(jq '.failedTests | length' test-results/.last-run.json)
|
||||
if [[ $failed_tests -gt 0 ]]; then
|
||||
# if it still fails after 3 retrys, then fail the job
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
exit 0
|
||||
env:
|
||||
CI: true
|
||||
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
|
@ -7726,6 +7726,31 @@ test('Basic default modeling and sketch hotkeys work', async ({ page }) => {
|
||||
await expect(page.locator('.cm-content')).toContainText('extrude(')
|
||||
})
|
||||
|
||||
test('Delete key does not navigate back', async ({ page }) => {
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.goto('/')
|
||||
await page.waitForURL('**/file/**', { waitUntil: 'domcontentloaded' })
|
||||
|
||||
const settingsButton = page.getByRole('link', {
|
||||
name: 'Settings',
|
||||
exact: false,
|
||||
})
|
||||
const settingsCloseButton = page.getByTestId('settings-close-button')
|
||||
|
||||
await settingsButton.click()
|
||||
await expect(page.url()).toContain('/settings')
|
||||
|
||||
// Make sure that delete doesn't go back from settings
|
||||
await page.keyboard.press('Delete')
|
||||
await expect(page.url()).toContain('/settings')
|
||||
|
||||
// Now close the settings and try delete again,
|
||||
// make sure it doesn't go back to settings
|
||||
await settingsCloseButton.click()
|
||||
await page.keyboard.press('Delete')
|
||||
await expect(page.url()).not.toContain('/settings')
|
||||
})
|
||||
|
||||
test('Sketch on face', async ({ page }) => {
|
||||
test.setTimeout(90_000)
|
||||
const u = await getUtils(page)
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "untitled-app",
|
||||
"version": "0.24.0",
|
||||
"version": "0.24.1",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.17.0",
|
||||
|
@ -93,23 +93,10 @@ export class LanguageServerPlugin implements PluginValue {
|
||||
private doSemanticTokens: boolean = false
|
||||
private doFoldingRanges: boolean = false
|
||||
|
||||
private _defferer = deferExecution((code: string) => {
|
||||
try {
|
||||
// Update the state (not the editor) with the new code.
|
||||
this.client.textDocumentDidChange({
|
||||
textDocument: {
|
||||
uri: this.getDocUri(),
|
||||
version: this.documentVersion++,
|
||||
},
|
||||
contentChanges: [{ text: code }],
|
||||
})
|
||||
|
||||
this.requestSemanticTokens()
|
||||
this.updateFoldingRanges()
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}, this.changesDelay)
|
||||
// When a doc update needs to be sent to the server, this holds the
|
||||
// timeout handle for it. When null, the server has the up-to-date
|
||||
// document.
|
||||
private sendScheduled: number | null = null
|
||||
|
||||
constructor(options: LanguageServerOptions, private view: EditorView) {
|
||||
this.client = options.client
|
||||
@ -152,14 +139,9 @@ export class LanguageServerPlugin implements PluginValue {
|
||||
}
|
||||
|
||||
update(viewUpdate: ViewUpdate) {
|
||||
// If the doc didn't change we can return early.
|
||||
if (!viewUpdate.docChanged) {
|
||||
return
|
||||
if (viewUpdate.docChanged) {
|
||||
this.scheduleSendDoc()
|
||||
}
|
||||
|
||||
this.sendChange({
|
||||
documentText: viewUpdate.state.doc.toString(),
|
||||
})
|
||||
}
|
||||
|
||||
destroy() {
|
||||
@ -184,16 +166,6 @@ export class LanguageServerPlugin implements PluginValue {
|
||||
this.updateFoldingRanges()
|
||||
}
|
||||
|
||||
async sendChange({ documentText }: { documentText: string }) {
|
||||
if (!this.client.ready) return
|
||||
|
||||
this._defferer(documentText)
|
||||
}
|
||||
|
||||
requestDiagnostics() {
|
||||
this.sendChange({ documentText: this.getDocText() })
|
||||
}
|
||||
|
||||
async requestHoverTooltip(
|
||||
view: EditorView,
|
||||
{ line, character }: { line: number; character: number }
|
||||
@ -204,7 +176,7 @@ export class LanguageServerPlugin implements PluginValue {
|
||||
)
|
||||
return null
|
||||
|
||||
this.sendChange({ documentText: this.getDocText() })
|
||||
this.ensureDocSent()
|
||||
const result = await this.client.textDocumentHover({
|
||||
textDocument: { uri: this.getDocUri() },
|
||||
position: { line, character },
|
||||
@ -227,6 +199,42 @@ export class LanguageServerPlugin implements PluginValue {
|
||||
return { pos, end, create: (view) => ({ dom }), above: true }
|
||||
}
|
||||
|
||||
scheduleSendDoc() {
|
||||
if (this.sendScheduled != null) window.clearTimeout(this.sendScheduled)
|
||||
this.sendScheduled = window.setTimeout(
|
||||
() => this.sendDoc(),
|
||||
this.changesDelay
|
||||
)
|
||||
}
|
||||
|
||||
sendDoc() {
|
||||
if (this.sendScheduled != null) {
|
||||
window.clearTimeout(this.sendScheduled)
|
||||
this.sendScheduled = null
|
||||
}
|
||||
|
||||
if (!this.client.ready) return
|
||||
try {
|
||||
// Update the state (not the editor) with the new code.
|
||||
this.client.textDocumentDidChange({
|
||||
textDocument: {
|
||||
uri: this.getDocUri(),
|
||||
version: this.documentVersion++,
|
||||
},
|
||||
contentChanges: [{ text: this.view.state.doc.toString() }],
|
||||
})
|
||||
|
||||
this.requestSemanticTokens()
|
||||
this.updateFoldingRanges()
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
|
||||
ensureDocSent() {
|
||||
if (this.sendScheduled != null) this.sendDoc()
|
||||
}
|
||||
|
||||
async getFoldingRanges(): Promise<LSP.FoldingRange[] | null> {
|
||||
if (
|
||||
!this.doFoldingRanges ||
|
||||
@ -284,13 +292,7 @@ export class LanguageServerPlugin implements PluginValue {
|
||||
)
|
||||
return null
|
||||
|
||||
this.client.textDocumentDidChange({
|
||||
textDocument: {
|
||||
uri: this.getDocUri(),
|
||||
version: this.documentVersion++,
|
||||
},
|
||||
contentChanges: [{ text: this.getDocText() }],
|
||||
})
|
||||
this.ensureDocSent()
|
||||
|
||||
const result = await this.client.textDocumentFormatting({
|
||||
textDocument: { uri: this.getDocUri() },
|
||||
@ -330,9 +332,7 @@ export class LanguageServerPlugin implements PluginValue {
|
||||
)
|
||||
return null
|
||||
|
||||
this.sendChange({
|
||||
documentText: context.state.doc.toString(),
|
||||
})
|
||||
this.ensureDocSent()
|
||||
|
||||
const result = await this.client.textDocumentCompletion({
|
||||
textDocument: { uri: this.getDocUri() },
|
||||
|
@ -20,7 +20,10 @@ export default defineConfig({
|
||||
/* Different amount of parallelism on CI and local. */
|
||||
workers: process.env.CI ? 4 : 4,
|
||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||
reporter: 'html',
|
||||
reporter: [
|
||||
[process.env.CI ? 'dot' : 'list'],
|
||||
['json', { outputFile: './test-results/report.json' }],
|
||||
],
|
||||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||
use: {
|
||||
/* Base URL to use in actions like `await page.goto('/')`. */
|
||||
|
65
playwrightProcess.mjs
Normal file
@ -0,0 +1,65 @@
|
||||
import { readFileSync } from 'fs'
|
||||
|
||||
const data = readFileSync('./test-results/report.json', 'utf8')
|
||||
|
||||
// types, but was easier to store and run as normal js
|
||||
// interface FailedTest {
|
||||
// name: string;
|
||||
// projectName: string;
|
||||
// error: string;
|
||||
// }
|
||||
|
||||
// interface Spec {
|
||||
// title: string;
|
||||
// tests: Test[];
|
||||
// }
|
||||
|
||||
// interface Test {
|
||||
// expectedStatus: 'passed' | 'failed' | 'pending';
|
||||
// projectName: string;
|
||||
// title: string;
|
||||
// results: {
|
||||
// status: 'passed' | 'failed' | 'pending';
|
||||
// error: {stack: string}
|
||||
// }[]
|
||||
// }
|
||||
|
||||
// interface Suite {
|
||||
// title: string
|
||||
// suites: Suite[];
|
||||
// specs: Spec[];
|
||||
// }
|
||||
|
||||
// const processReport = (suites: Suite[]): FailedTest[] => {
|
||||
// const failedTests: FailedTest[] = []
|
||||
// const loopSuites = (suites: Suite[], previousName = '') => {
|
||||
const processReport = (suites) => {
|
||||
const failedTests = []
|
||||
const loopSuites = (suites, previousName = '') => {
|
||||
if (!suites) return
|
||||
for (const suite of suites) {
|
||||
const name = (previousName ? `${previousName} -- ` : '') + suite.title
|
||||
for (const spec of suite.specs) {
|
||||
for (const test of spec.tests) {
|
||||
for (const result of test.results) {
|
||||
if ((result.status !== 'passed') && test.expectedStatus === 'passed') {
|
||||
failedTests.push({
|
||||
name: (name + ' -- ' + spec.title) + (test.title ? ` -- ${test.title}` : ''),
|
||||
status: result.status,
|
||||
projectName: test.projectName,
|
||||
error: result.error?.stack,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
loopSuites(suite.suites, name)
|
||||
}
|
||||
}
|
||||
loopSuites(suites)
|
||||
return failedTests.map(line => JSON.stringify(line)).join('\n')
|
||||
}
|
||||
const failedTests = processReport(JSON.parse(data).suites)
|
||||
// log to stdout to be piped to axiom
|
||||
console.log(failedTests)
|
||||
|
130
src-tauri/Cargo.lock
generated
@ -332,7 +332,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -361,13 +361,13 @@ checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799"
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.80"
|
||||
version = "0.1.81"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca"
|
||||
checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -407,7 +407,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -550,7 +550,7 @@ dependencies = [
|
||||
"proc-macro-crate 3.1.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.70",
|
||||
"syn_derive",
|
||||
]
|
||||
|
||||
@ -792,9 +792,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.7"
|
||||
version = "4.5.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f"
|
||||
checksum = "64acc1846d54c1fe936a78dc189c34e28d3f5afc348403f28ecf53660b9b8462"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@ -802,9 +802,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.7"
|
||||
version = "4.5.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f"
|
||||
checksum = "6fb8393d67ba2e7bfaf28a23458e4e2b543cc73a99595511eb207fdb8aede942"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@ -816,14 +816,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.5"
|
||||
version = "4.5.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6"
|
||||
checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085"
|
||||
dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1073,7 +1073,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1083,7 +1083,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1107,7 +1107,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim 0.10.0",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1118,7 +1118,7 @@ checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1179,7 +1179,7 @@ checksum = "4078275de501a61ceb9e759d37bdd3d7210e654dbc167ac1a3678ef4435ed57b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.70",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
@ -1216,7 +1216,7 @@ dependencies = [
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_tokenstream",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1227,7 +1227,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1288,7 +1288,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1320,7 +1320,7 @@ checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1427,7 +1427,7 @@ checksum = "5c785274071b1b420972453b306eeca06acf4633829db4223b58a2a8c5953bc4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1588,7 +1588,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1704,7 +1704,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1980,7 +1980,7 @@ dependencies = [
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2008,7 +2008,7 @@ dependencies = [
|
||||
"inflections",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2083,7 +2083,7 @@ dependencies = [
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2571,7 +2571,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-lib"
|
||||
version = "0.1.70"
|
||||
version = "0.1.72"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"approx",
|
||||
@ -3377,7 +3377,7 @@ dependencies = [
|
||||
"regex",
|
||||
"regex-syntax 0.8.3",
|
||||
"structmeta",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3496,7 +3496,7 @@ dependencies = [
|
||||
"phf_shared 0.11.2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3564,7 +3564,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4438,7 +4438,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde_derive_internals",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4523,9 +4523,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.203"
|
||||
version = "1.0.204"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094"
|
||||
checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
@ -4552,13 +4552,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.203"
|
||||
version = "1.0.204"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
|
||||
checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4569,7 +4569,7 @@ checksum = "330f01ce65a3a5fe59a60c82f3c9a024b573b8a6e875bd233fe5f934e71d54e3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4602,7 +4602,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4623,7 +4623,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4665,7 +4665,7 @@ dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4933,7 +4933,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"structmeta-derive",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4944,7 +4944,7 @@ checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4966,7 +4966,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4999,9 +4999,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.68"
|
||||
version = "2.0.70"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9"
|
||||
checksum = "2f0209b68b3613b093e0ec905354eccaedcfe83b8cb37cbdeae64026c3064c16"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -5017,7 +5017,7 @@ dependencies = [
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5034,7 +5034,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5251,7 +5251,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.70",
|
||||
"tauri-utils",
|
||||
"thiserror",
|
||||
"time",
|
||||
@ -5269,7 +5269,7 @@ dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.70",
|
||||
"tauri-codegen",
|
||||
"tauri-utils",
|
||||
]
|
||||
@ -5642,7 +5642,7 @@ checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5740,7 +5740,7 @@ checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5940,7 +5940,7 @@ checksum = "84fd902d4e0b9a4b27f2f440108dc034e1758628a9b702f8ec61ad66355422fa"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5969,7 +5969,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -6099,7 +6099,7 @@ checksum = "c88cc88fd23b5a04528f3a8436024f20010a16ec18eb23c164b1242f65860130"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.70",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
@ -6280,9 +6280,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.9.1"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5de17fd2f7da591098415cff336e12965a28061ddace43b59cb3c430179c9439"
|
||||
checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314"
|
||||
dependencies = [
|
||||
"getrandom 0.2.14",
|
||||
"serde",
|
||||
@ -6316,7 +6316,7 @@ dependencies = [
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -6415,7 +6415,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.70",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@ -6449,7 +6449,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.70",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
@ -6590,7 +6590,7 @@ checksum = "ac1345798ecd8122468840bcdf1b95e5dc6d2206c5e4b0eafa078d061f59c9bc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -6696,7 +6696,7 @@ checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -6707,7 +6707,7 @@ checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -7159,7 +7159,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -80,5 +80,5 @@
|
||||
}
|
||||
},
|
||||
"productName": "Zoo Modeling App",
|
||||
"version": "0.24.0"
|
||||
"version": "0.24.1"
|
||||
}
|
||||
|
@ -39,3 +39,32 @@ export const AppStateProvider = ({ children }: { children: ReactNode }) => {
|
||||
</AppStateContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
interface AppStream {
|
||||
mediaStream: MediaStream
|
||||
setMediaStream: (mediaStream: MediaStream) => void
|
||||
}
|
||||
|
||||
const AppStreamContext = createContext<AppStream>({
|
||||
mediaStream: undefined as unknown as MediaStream,
|
||||
setMediaStream: () => {},
|
||||
})
|
||||
|
||||
export const useAppStream = () => useContext(AppStreamContext)
|
||||
|
||||
export const AppStreamProvider = ({ children }: { children: ReactNode }) => {
|
||||
const [mediaStream, setMediaStream] = useState<MediaStream>(
|
||||
undefined as unknown as MediaStream
|
||||
)
|
||||
|
||||
return (
|
||||
<AppStreamContext.Provider
|
||||
value={{
|
||||
mediaStream,
|
||||
setMediaStream,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</AppStreamContext.Provider>
|
||||
)
|
||||
}
|
||||
|
@ -114,7 +114,7 @@ export function Toolbar({
|
||||
() =>
|
||||
commandBarSend({
|
||||
type: 'Find and select command',
|
||||
data: { name: 'Extrude', ownerMachine: 'modeling' },
|
||||
data: { name: 'Extrude', groupId: 'modeling' },
|
||||
}),
|
||||
{ enabled: !disableAllButtons, scopes: ['modeling'] }
|
||||
)
|
||||
@ -378,7 +378,7 @@ export function Toolbar({
|
||||
onClick={() =>
|
||||
commandBarSend({
|
||||
type: 'Find and select command',
|
||||
data: { name: 'Extrude', ownerMachine: 'modeling' },
|
||||
data: { name: 'Extrude', groupId: 'modeling' },
|
||||
})
|
||||
}
|
||||
disabled={!state.can('Extrude') || disableAllButtons}
|
||||
|
@ -518,9 +518,9 @@ export class CameraControls {
|
||||
direction.normalize()
|
||||
this.camera.position.copy(this.target).addScaledVector(direction, distance)
|
||||
}
|
||||
usePerspectiveCamera = async () => {
|
||||
usePerspectiveCamera = async (forceSend = false) => {
|
||||
this._usePerspectiveCamera()
|
||||
if (this.syncDirection === 'clientToEngine') {
|
||||
if (forceSend || this.syncDirection === 'clientToEngine') {
|
||||
await this.engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
|
@ -717,7 +717,7 @@ export const CamDebugSettings = () => {
|
||||
if (camSettings.type === 'perspective') {
|
||||
sceneInfra.camControls.useOrthographicCamera()
|
||||
} else {
|
||||
sceneInfra.camControls.usePerspectiveCamera()
|
||||
sceneInfra.camControls.usePerspectiveCamera(true)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
@ -82,11 +82,11 @@ function ProjectMenuPopover({
|
||||
}) {
|
||||
const { commandBarState, commandBarSend } = useCommandsContext()
|
||||
const { onProjectClose } = useLspContext()
|
||||
const exportCommandInfo = { name: 'Export', ownerMachine: 'modeling' }
|
||||
const findCommand = (obj: { name: string; ownerMachine: string }) =>
|
||||
const exportCommandInfo = { name: 'Export', groupId: 'modeling' }
|
||||
const findCommand = (obj: { name: string; groupId: string }) =>
|
||||
Boolean(
|
||||
commandBarState.context.commands.find(
|
||||
(c) => c.name === obj.name && c.ownerMachine === obj.ownerMachine
|
||||
(c) => c.name === obj.name && c.groupId === obj.groupId
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -6,9 +6,10 @@ import { useModelingContext } from 'hooks/useModelingContext'
|
||||
import { useNetworkContext } from 'hooks/useNetworkContext'
|
||||
import { NetworkHealthState } from 'hooks/useNetworkStatus'
|
||||
import { ClientSideScene } from 'clientSideScene/ClientSideSceneComp'
|
||||
import { butName } from 'lib/cameraControls'
|
||||
import { btnName } from 'lib/cameraControls'
|
||||
import { sendSelectEventToEngine } from 'lib/selections'
|
||||
import { kclManager, engineCommandManager, sceneInfra } from 'lib/singletons'
|
||||
import { useAppStream } from 'AppState'
|
||||
|
||||
export const Stream = () => {
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
@ -17,6 +18,7 @@ export const Stream = () => {
|
||||
const videoRef = useRef<HTMLVideoElement>(null)
|
||||
const { settings } = useSettingsAuthContext()
|
||||
const { state, send, context } = useModelingContext()
|
||||
const { mediaStream } = useAppStream()
|
||||
const { overallState } = useNetworkContext()
|
||||
const [isFreezeFrame, setIsFreezeFrame] = useState(false)
|
||||
|
||||
@ -124,10 +126,10 @@ export const Stream = () => {
|
||||
)
|
||||
return
|
||||
if (!videoRef.current) return
|
||||
if (!context.store?.mediaStream) return
|
||||
if (!mediaStream) return
|
||||
|
||||
// Do not immediately play the stream!
|
||||
videoRef.current.srcObject = context.store.mediaStream
|
||||
videoRef.current.srcObject = mediaStream
|
||||
videoRef.current.pause()
|
||||
|
||||
send({
|
||||
@ -136,7 +138,7 @@ export const Stream = () => {
|
||||
videoElement: videoRef.current,
|
||||
},
|
||||
})
|
||||
}, [context.store?.mediaStream])
|
||||
}, [mediaStream])
|
||||
|
||||
const handleMouseDown: MouseEventHandler<HTMLDivElement> = (e) => {
|
||||
if (!isNetworkOkay) return
|
||||
@ -172,7 +174,7 @@ export const Stream = () => {
|
||||
if (state.matches('Sketch')) return
|
||||
if (state.matches('Sketch no face')) return
|
||||
|
||||
if (!context.store?.didDragInStream && butName(e).left) {
|
||||
if (!context.store?.didDragInStream && btnName(e).left) {
|
||||
sendSelectEventToEngine(
|
||||
e,
|
||||
videoRef.current,
|
||||
|
@ -195,14 +195,15 @@ export class CompletionRequester implements PluginValue {
|
||||
|
||||
private queuedUids: string[] = []
|
||||
|
||||
private _deffererCodeUpdate = deferExecution(() => {
|
||||
this.requestCompletions()
|
||||
}, changesDelay)
|
||||
|
||||
private _deffererUserSelect = deferExecution(() => {
|
||||
this.rejectSuggestionCommand()
|
||||
}, changesDelay)
|
||||
|
||||
// When a doc update needs to be sent to the server, this holds the
|
||||
// timeout handle for it. When null, the server has the up-to-date
|
||||
// document.
|
||||
private sendScheduledInput: number | null = null
|
||||
|
||||
constructor(readonly view: EditorView, client: LanguageServerClient) {
|
||||
this.client = client
|
||||
}
|
||||
@ -245,7 +246,34 @@ export class CompletionRequester implements PluginValue {
|
||||
}
|
||||
|
||||
this.lastPos = this.view.state.selection.main.head
|
||||
if (viewUpdate.docChanged) this._deffererCodeUpdate(true)
|
||||
if (viewUpdate.docChanged) this.scheduleUpdateDoc()
|
||||
}
|
||||
|
||||
scheduleUpdateDoc() {
|
||||
if (this.sendScheduledInput != null)
|
||||
window.clearTimeout(this.sendScheduledInput)
|
||||
this.sendScheduledInput = window.setTimeout(
|
||||
() => this.updateDoc(),
|
||||
changesDelay
|
||||
)
|
||||
}
|
||||
|
||||
updateDoc() {
|
||||
if (this.sendScheduledInput != null) {
|
||||
window.clearTimeout(this.sendScheduledInput)
|
||||
this.sendScheduledInput = null
|
||||
}
|
||||
|
||||
if (!this.client.ready) return
|
||||
try {
|
||||
this.requestCompletions()
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
|
||||
ensureDocUpdated() {
|
||||
if (this.sendScheduledInput != null) this.updateDoc()
|
||||
}
|
||||
|
||||
ghostText(): GhostText | null {
|
||||
|
@ -27,13 +27,10 @@ export class KclPlugin implements PluginValue {
|
||||
this.client = client
|
||||
}
|
||||
|
||||
private _deffererCodeUpdate = deferExecution(() => {
|
||||
if (this.viewUpdate === null) {
|
||||
return
|
||||
}
|
||||
|
||||
kclManager.executeCode()
|
||||
}, changesDelay)
|
||||
// When a doc update needs to be sent to the server, this holds the
|
||||
// timeout handle for it. When null, the server has the up-to-date
|
||||
// document.
|
||||
private sendScheduledInput: number | null = null
|
||||
|
||||
private _deffererUserSelect = deferExecution(() => {
|
||||
if (this.viewUpdate === null) {
|
||||
@ -101,7 +98,34 @@ export class KclPlugin implements PluginValue {
|
||||
codeManager.code = newCode
|
||||
codeManager.writeToFile()
|
||||
|
||||
this._deffererCodeUpdate(true)
|
||||
this.scheduleUpdateDoc()
|
||||
}
|
||||
|
||||
scheduleUpdateDoc() {
|
||||
if (this.sendScheduledInput != null)
|
||||
window.clearTimeout(this.sendScheduledInput)
|
||||
this.sendScheduledInput = window.setTimeout(
|
||||
() => this.updateDoc(),
|
||||
changesDelay
|
||||
)
|
||||
}
|
||||
|
||||
updateDoc() {
|
||||
if (this.sendScheduledInput != null) {
|
||||
window.clearTimeout(this.sendScheduledInput)
|
||||
this.sendScheduledInput = null
|
||||
}
|
||||
|
||||
if (!this.client.ready) return
|
||||
try {
|
||||
kclManager.executeCode()
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
|
||||
ensureDocUpdated() {
|
||||
if (this.sendScheduledInput != null) this.updateDoc()
|
||||
}
|
||||
|
||||
async updateUnits(
|
||||
|
@ -4,7 +4,7 @@ import { deferExecution } from 'lib/utils'
|
||||
import { Themes } from 'lib/theme'
|
||||
import { makeDefaultPlanes, modifyGrid } from 'lang/wasm'
|
||||
import { useModelingContext } from './useModelingContext'
|
||||
import { useAppState } from 'AppState'
|
||||
import { useAppState, useAppStream } from 'AppState'
|
||||
|
||||
export function useSetupEngineManager(
|
||||
streamRef: React.RefObject<HTMLDivElement>,
|
||||
@ -28,6 +28,7 @@ export function useSetupEngineManager(
|
||||
}
|
||||
) {
|
||||
const { setAppState } = useAppState()
|
||||
const { setMediaStream } = useAppStream()
|
||||
|
||||
const streamWidth = streamRef?.current?.offsetWidth
|
||||
const streamHeight = streamRef?.current?.offsetHeight
|
||||
@ -54,11 +55,7 @@ export function useSetupEngineManager(
|
||||
settings.modelingSend
|
||||
) {
|
||||
engineCommandManager.start({
|
||||
setMediaStream: (mediaStream) =>
|
||||
settings.modelingSend({
|
||||
type: 'Set context',
|
||||
data: { mediaStream },
|
||||
}),
|
||||
setMediaStream: setMediaStream,
|
||||
setIsStreamReady: (isStreamReady) => setAppState({ isStreamReady }),
|
||||
width: quadWidth,
|
||||
height: quadHeight,
|
||||
|
@ -60,7 +60,8 @@ export default function useStateMachineCommands<
|
||||
.filter((e) => !['done.', 'error.'].some((n) => e.includes(n)))
|
||||
.map((type) =>
|
||||
createMachineCommand<T, S>({
|
||||
ownerMachine: machineId,
|
||||
// The group is the owner machine's ID.
|
||||
groupId: machineId,
|
||||
type,
|
||||
state,
|
||||
send,
|
||||
|
@ -13,6 +13,7 @@ import {
|
||||
UpdaterRestartModal,
|
||||
createUpdaterRestartModal,
|
||||
} from 'components/UpdaterRestartModal'
|
||||
import { AppStreamProvider } from 'AppState'
|
||||
|
||||
// uncomment for xstate inspector
|
||||
// import { DEV } from 'env'
|
||||
@ -26,28 +27,30 @@ const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement)
|
||||
|
||||
root.render(
|
||||
<HotkeysProvider>
|
||||
<Router />
|
||||
<Toaster
|
||||
position="bottom-center"
|
||||
toastOptions={{
|
||||
style: {
|
||||
borderRadius: '3px',
|
||||
},
|
||||
className:
|
||||
'bg-chalkboard-10 dark:bg-chalkboard-90 text-chalkboard-110 dark:text-chalkboard-10 rounded-sm border-chalkboard-20/50 dark:border-chalkboard-80/50',
|
||||
success: {
|
||||
iconTheme: {
|
||||
primary: 'oklch(89% 0.16 143.4deg)',
|
||||
secondary: 'oklch(48.62% 0.1654 142.5deg)',
|
||||
<AppStreamProvider>
|
||||
<Router />
|
||||
<Toaster
|
||||
position="bottom-center"
|
||||
toastOptions={{
|
||||
style: {
|
||||
borderRadius: '3px',
|
||||
},
|
||||
duration:
|
||||
window?.localStorage.getItem('playwright') === 'true'
|
||||
? 10 // speed up e2e tests
|
||||
: 1500,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<ModalContainer />
|
||||
className:
|
||||
'bg-chalkboard-10 dark:bg-chalkboard-90 text-chalkboard-110 dark:text-chalkboard-10 rounded-sm border-chalkboard-20/50 dark:border-chalkboard-80/50',
|
||||
success: {
|
||||
iconTheme: {
|
||||
primary: 'oklch(89% 0.16 143.4deg)',
|
||||
secondary: 'oklch(48.62% 0.1654 142.5deg)',
|
||||
},
|
||||
duration:
|
||||
window?.localStorage.getItem('playwright') === 'true'
|
||||
? 10 // speed up e2e tests
|
||||
: 1500,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<ModalContainer />
|
||||
</AppStreamProvider>
|
||||
</HotkeysProvider>
|
||||
)
|
||||
|
||||
|
@ -64,7 +64,7 @@ export interface MouseGuard {
|
||||
rotate: MouseGuardHandler
|
||||
}
|
||||
|
||||
export const butName = (e: React.MouseEvent) => ({
|
||||
export const btnName = (e: React.MouseEvent) => ({
|
||||
middle: !!(e.buttons & 4) || e.button === 1,
|
||||
right: !!(e.buttons & 2) || e.button === 2,
|
||||
left: !!(e.buttons & 1) || e.button === 0,
|
||||
@ -75,8 +75,8 @@ export const cameraMouseDragGuards: Record<CameraSystem, MouseGuard> = {
|
||||
pan: {
|
||||
description: 'Right click + Shift + drag or middle click + drag',
|
||||
callback: (e) =>
|
||||
(butName(e).middle && noModifiersPressed(e)) ||
|
||||
(butName(e).right && e.shiftKey),
|
||||
(btnName(e).middle && noModifiersPressed(e)) ||
|
||||
(btnName(e).right && e.shiftKey),
|
||||
},
|
||||
zoom: {
|
||||
description: 'Scroll wheel or Right click + Ctrl + drag',
|
||||
@ -85,15 +85,15 @@ export const cameraMouseDragGuards: Record<CameraSystem, MouseGuard> = {
|
||||
},
|
||||
rotate: {
|
||||
description: 'Right click + drag',
|
||||
callback: (e) => butName(e).right && noModifiersPressed(e),
|
||||
callback: (e) => btnName(e).right && noModifiersPressed(e),
|
||||
},
|
||||
},
|
||||
OnShape: {
|
||||
pan: {
|
||||
description: 'Right click + Ctrl + drag or middle click + drag',
|
||||
callback: (e) =>
|
||||
(butName(e).right && e.ctrlKey) ||
|
||||
(butName(e).middle && noModifiersPressed(e)),
|
||||
(btnName(e).right && e.ctrlKey) ||
|
||||
(btnName(e).middle && noModifiersPressed(e)),
|
||||
},
|
||||
zoom: {
|
||||
description: 'Scroll wheel',
|
||||
@ -102,72 +102,72 @@ export const cameraMouseDragGuards: Record<CameraSystem, MouseGuard> = {
|
||||
},
|
||||
rotate: {
|
||||
description: 'Right click + drag',
|
||||
callback: (e) => butName(e).right && noModifiersPressed(e),
|
||||
callback: (e) => btnName(e).right && noModifiersPressed(e),
|
||||
},
|
||||
},
|
||||
'Trackpad Friendly': {
|
||||
pan: {
|
||||
description: 'Left click + Alt + Shift + drag or middle click + drag',
|
||||
callback: (e) =>
|
||||
(butName(e).left && e.altKey && e.shiftKey && !e.metaKey) ||
|
||||
(butName(e).middle && noModifiersPressed(e)),
|
||||
(btnName(e).left && e.altKey && e.shiftKey && !e.metaKey) ||
|
||||
(btnName(e).middle && noModifiersPressed(e)),
|
||||
},
|
||||
zoom: {
|
||||
description: 'Scroll wheel or Left click + Alt + OS + drag',
|
||||
dragCallback: (e) => butName(e).left && e.altKey && e.metaKey,
|
||||
dragCallback: (e) => btnName(e).left && e.altKey && e.metaKey,
|
||||
scrollCallback: () => true,
|
||||
},
|
||||
rotate: {
|
||||
description: 'Left click + Alt + drag',
|
||||
callback: (e) => butName(e).left && e.altKey && !e.shiftKey && !e.metaKey,
|
||||
callback: (e) => btnName(e).left && e.altKey && !e.shiftKey && !e.metaKey,
|
||||
lenientDragStartButton: 0,
|
||||
},
|
||||
},
|
||||
Solidworks: {
|
||||
pan: {
|
||||
description: 'Right click + Ctrl + drag',
|
||||
callback: (e) => butName(e).right && e.ctrlKey,
|
||||
callback: (e) => btnName(e).right && e.ctrlKey,
|
||||
lenientDragStartButton: 2,
|
||||
},
|
||||
zoom: {
|
||||
description: 'Scroll wheel or Middle click + Shift + drag',
|
||||
dragCallback: (e) => butName(e).middle && e.shiftKey,
|
||||
dragCallback: (e) => btnName(e).middle && e.shiftKey,
|
||||
scrollCallback: () => true,
|
||||
},
|
||||
rotate: {
|
||||
description: 'Middle click + drag',
|
||||
callback: (e) => butName(e).middle && noModifiersPressed(e),
|
||||
callback: (e) => btnName(e).middle && noModifiersPressed(e),
|
||||
},
|
||||
},
|
||||
NX: {
|
||||
pan: {
|
||||
description: 'Middle click + Shift + drag',
|
||||
callback: (e) => butName(e).middle && e.shiftKey,
|
||||
callback: (e) => btnName(e).middle && e.shiftKey,
|
||||
},
|
||||
zoom: {
|
||||
description: 'Scroll wheel or Middle click + Ctrl + drag',
|
||||
dragCallback: (e) => butName(e).middle && e.ctrlKey,
|
||||
dragCallback: (e) => btnName(e).middle && e.ctrlKey,
|
||||
scrollCallback: () => true,
|
||||
},
|
||||
rotate: {
|
||||
description: 'Middle click + drag',
|
||||
callback: (e) => butName(e).middle && noModifiersPressed(e),
|
||||
callback: (e) => btnName(e).middle && noModifiersPressed(e),
|
||||
},
|
||||
},
|
||||
Creo: {
|
||||
pan: {
|
||||
description: 'Left click + Ctrl + drag',
|
||||
callback: (e) => butName(e).left && !butName(e).right && e.ctrlKey,
|
||||
callback: (e) => btnName(e).left && !btnName(e).right && e.ctrlKey,
|
||||
},
|
||||
zoom: {
|
||||
description: 'Scroll wheel or Right click + Ctrl + drag',
|
||||
dragCallback: (e) => butName(e).right && !butName(e).left && e.ctrlKey,
|
||||
dragCallback: (e) => btnName(e).right && !btnName(e).left && e.ctrlKey,
|
||||
scrollCallback: () => true,
|
||||
},
|
||||
rotate: {
|
||||
description: 'Middle (or Left + Right) click + Ctrl + drag',
|
||||
callback: (e) => {
|
||||
const b = butName(e)
|
||||
const b = btnName(e)
|
||||
return (b.middle || (b.left && b.right)) && e.ctrlKey
|
||||
},
|
||||
},
|
||||
@ -175,7 +175,7 @@ export const cameraMouseDragGuards: Record<CameraSystem, MouseGuard> = {
|
||||
AutoCAD: {
|
||||
pan: {
|
||||
description: 'Middle click + drag',
|
||||
callback: (e) => butName(e).middle && noModifiersPressed(e),
|
||||
callback: (e) => btnName(e).middle && noModifiersPressed(e),
|
||||
},
|
||||
zoom: {
|
||||
description: 'Scroll wheel',
|
||||
@ -184,7 +184,7 @@ export const cameraMouseDragGuards: Record<CameraSystem, MouseGuard> = {
|
||||
},
|
||||
rotate: {
|
||||
description: 'Middle click + Shift + drag',
|
||||
callback: (e) => butName(e).middle && e.shiftKey,
|
||||
callback: (e) => btnName(e).middle && e.shiftKey,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -124,7 +124,7 @@ export function createSettingsCommand({
|
||||
displayName: `Settings · ${decamelize(type.replaceAll('.', ' · '), {
|
||||
separator: ' ',
|
||||
})}`,
|
||||
ownerMachine: 'settings',
|
||||
groupId: 'settings',
|
||||
icon: 'settings',
|
||||
needsReview: false,
|
||||
onSubmit: (data) => {
|
||||
|
@ -65,7 +65,7 @@ export type Command<
|
||||
CommandSchema extends CommandSetSchema<T>[CommandName] = CommandSetSchema<T>[CommandName]
|
||||
> = {
|
||||
name: CommandName
|
||||
ownerMachine: T['id']
|
||||
groupId: T['id']
|
||||
needsReview: boolean
|
||||
onSubmit: (data?: CommandSchema) => void
|
||||
onCancel?: () => void
|
||||
@ -84,7 +84,7 @@ export type CommandConfig<
|
||||
CommandSchema extends CommandSetSchema<T>[CommandName] = CommandSetSchema<T>[CommandName]
|
||||
> = Omit<
|
||||
Command<T, CommandName, CommandSchema>,
|
||||
'name' | 'ownerMachine' | 'onSubmit' | 'onCancel' | 'args' | 'needsReview'
|
||||
'name' | 'groupId' | 'onSubmit' | 'onCancel' | 'args' | 'needsReview'
|
||||
> & {
|
||||
needsReview?: true
|
||||
args?: {
|
||||
|
@ -20,7 +20,7 @@ interface CreateMachineCommandProps<
|
||||
S extends CommandSetSchema<T>
|
||||
> {
|
||||
type: EventFrom<T>['type']
|
||||
ownerMachine: T['id']
|
||||
groupId: T['id']
|
||||
state: StateFrom<T>
|
||||
send: Function
|
||||
actor: InterpreterFrom<T>
|
||||
@ -34,7 +34,7 @@ export function createMachineCommand<
|
||||
T extends AnyStateMachine,
|
||||
S extends CommandSetSchema<T>
|
||||
>({
|
||||
ownerMachine,
|
||||
groupId,
|
||||
type,
|
||||
state,
|
||||
send,
|
||||
@ -62,7 +62,7 @@ export function createMachineCommand<
|
||||
|
||||
const command: Command<T, typeof type, S[typeof type]> = {
|
||||
name: type,
|
||||
ownerMachine: ownerMachine,
|
||||
groupId,
|
||||
icon,
|
||||
needsReview: commandConfig.needsReview || false,
|
||||
onSubmit: (data?: S[typeof type]) => {
|
||||
|
@ -57,7 +57,7 @@ export type CommandBarMachineEvent =
|
||||
}
|
||||
| {
|
||||
type: 'Find and select command'
|
||||
data: { name: string; ownerMachine: string }
|
||||
data: { name: string; groupId: string }
|
||||
}
|
||||
| {
|
||||
type: 'Change current argument'
|
||||
@ -120,9 +120,7 @@ export const commandBarMachine = createMachine(
|
||||
context.commands.filter(
|
||||
(c) =>
|
||||
!event.data.commands.some(
|
||||
(c2) =>
|
||||
c2.name === c.name &&
|
||||
c2.ownerMachine === c.ownerMachine
|
||||
(c2) => c2.name === c.name && c2.groupId === c.groupId
|
||||
)
|
||||
),
|
||||
}),
|
||||
@ -393,9 +391,7 @@ export const commandBarMachine = createMachine(
|
||||
selectedCommand: (c, e) => {
|
||||
if (e.type !== 'Find and select command') return c.selectedCommand
|
||||
const found = c.commands.find(
|
||||
(cmd) =>
|
||||
cmd.name === e.data.name &&
|
||||
cmd.ownerMachine === e.data.ownerMachine
|
||||
(cmd) => cmd.name === e.data.name && cmd.groupId === e.data.groupId
|
||||
)
|
||||
|
||||
return !!found ? found : c.selectedCommand
|
||||
@ -514,7 +510,7 @@ export const commandBarMachine = createMachine(
|
||||
)
|
||||
|
||||
function sortCommands(a: Command, b: Command) {
|
||||
if (b.ownerMachine === 'auth') return -1
|
||||
if (a.ownerMachine === 'auth') return 1
|
||||
if (b.groupId === 'auth') return -1
|
||||
if (a.groupId === 'auth') return 1
|
||||
return a.name.localeCompare(b.name)
|
||||
}
|
||||
|
@ -138,7 +138,6 @@ export type SegmentOverlayPayload =
|
||||
}
|
||||
|
||||
interface Store {
|
||||
mediaStream?: MediaStream
|
||||
videoElement?: HTMLVideoElement
|
||||
buttonDownInStream: number | undefined
|
||||
didDragInStream: boolean
|
||||
|
@ -57,6 +57,9 @@ const Home = () => {
|
||||
kclManager.cancelAllExecutions()
|
||||
}, [])
|
||||
|
||||
useHotkeys('backspace', (e) => {
|
||||
e.preventDefault()
|
||||
})
|
||||
useHotkeys(
|
||||
isTauri() ? 'mod+,' : 'shift+mod+,',
|
||||
() => navigate(paths.HOME + paths.SETTINGS),
|
||||
|
Before Width: | Height: | Size: 333 KiB After Width: | Height: | Size: 333 KiB |
Before Width: | Height: | Size: 91 KiB After Width: | Height: | Size: 91 KiB |
Before Width: | Height: | Size: 147 KiB After Width: | Height: | Size: 147 KiB |
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 92 KiB |
Before Width: | Height: | Size: 333 KiB After Width: | Height: | Size: 333 KiB |
Before Width: | Height: | Size: 120 KiB After Width: | Height: | Size: 120 KiB |
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 95 KiB |
Before Width: | Height: | Size: 117 KiB After Width: | Height: | Size: 117 KiB |