Compare commits
16 Commits
v0.17.2
...
derive-doc
Author | SHA1 | Date | |
---|---|---|---|
f5ee346408 | |||
544a7565e3 | |||
979046f7e6 | |||
07ae5106b9 | |||
e9ae484332 | |||
2a86ffc09a | |||
93903a8a47 | |||
45e85a1f81 | |||
c187989d18 | |||
47b5fa1459 | |||
d85781ef99 | |||
233f81a879 | |||
8ac0bf4953 | |||
24caeece65 | |||
f493cf11a0 | |||
594e888c12 |
@ -1,3 +1,3 @@
|
|||||||
[codespell]
|
[codespell]
|
||||||
ignore-words-list: crate,everytime,inout,co-ordinate,ot,nwo,absolutey,atleast
|
ignore-words-list: crate,everytime,inout,co-ordinate,ot,nwo,absolutey,atleast
|
||||||
skip: **/target,node_modules,build,**/Cargo.lock,./docs/kcl/*.md
|
skip: **/target,node_modules,build,**/Cargo.lock,./docs/kcl/*.md,./src-tauri/gen/schemas
|
||||||
|
@ -7,23 +7,23 @@ on:
|
|||||||
- '**/Cargo.toml'
|
- '**/Cargo.toml'
|
||||||
- '**/Cargo.lock'
|
- '**/Cargo.lock'
|
||||||
- '**/rust-toolchain.toml'
|
- '**/rust-toolchain.toml'
|
||||||
- .github/workflows/cargo-criterion.yml
|
- .github/workflows/cargo-bench.yml
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- '**.rs'
|
- '**.rs'
|
||||||
- '**/Cargo.toml'
|
- '**/Cargo.toml'
|
||||||
- '**/Cargo.lock'
|
- '**/Cargo.lock'
|
||||||
- '**/rust-toolchain.toml'
|
- '**/rust-toolchain.toml'
|
||||||
- .github/workflows/cargo-criterion.yml
|
- .github/workflows/cargo-bench.yml
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
permissions: read-all
|
permissions: read-all
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
name: cargo criterion
|
name: cargo bench
|
||||||
jobs:
|
jobs:
|
||||||
cargocriterion:
|
cargo-bench:
|
||||||
name: cargo criterion
|
name: Benchmark with iai
|
||||||
runs-on: ubuntu-latest-8-cores
|
runs-on: ubuntu-latest-8-cores
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
@ -31,10 +31,12 @@ jobs:
|
|||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
cargo install cargo-criterion
|
cargo install cargo-criterion
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install -y valgrind
|
||||||
- name: Rust Cache
|
- name: Rust Cache
|
||||||
uses: Swatinem/rust-cache@v2.6.1
|
uses: Swatinem/rust-cache@v2.6.1
|
||||||
- name: Benchmark kcl library
|
- name: Benchmark kcl library
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |-
|
run: |-
|
||||||
cd src/wasm-lib/kcl; cargo criterion
|
cd src/wasm-lib/kcl; cargo bench -- iai
|
||||||
|
|
26
.github/workflows/ci.yml
vendored
@ -125,6 +125,9 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [macos-14, ubuntu-latest, windows-latest]
|
os: [macos-14, ubuntu-latest, windows-latest]
|
||||||
|
env:
|
||||||
|
TAURI_ARGS_MACOS: ${{ matrix.os == 'macos-14' && '--target universal-apple-darwin' || '' }}
|
||||||
|
TAURI_ARGS_UBUNTU: ${{ matrix.os == 'ubuntu-latest' && '--bundles' || '' }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
@ -144,10 +147,12 @@ jobs:
|
|||||||
sudo apt-get update &&
|
sudo apt-get update &&
|
||||||
sudo apt-get install -y
|
sudo apt-get install -y
|
||||||
libgtk-3-dev
|
libgtk-3-dev
|
||||||
libgtksourceview-3.0-dev
|
libayatana-appindicator3-dev
|
||||||
webkit2gtk-4.0
|
|
||||||
libappindicator3-dev
|
|
||||||
webkit2gtk-driver
|
webkit2gtk-driver
|
||||||
|
libsoup-3.0-dev
|
||||||
|
libjavascriptcoregtk-4.1-dev
|
||||||
|
libwebkit2gtk-4.1-dev
|
||||||
|
at-spi2-core
|
||||||
xvfb
|
xvfb
|
||||||
|
|
||||||
- name: Sync node version and setup cache
|
- name: Sync node version and setup cache
|
||||||
@ -161,7 +166,9 @@ jobs:
|
|||||||
- name: Setup Rust
|
- name: Setup Rust
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
|
||||||
|
# TODO: re-enable for Windows builds, see https://github.com/tauri-apps/tauri/issues/9045
|
||||||
- name: Setup Rust cache
|
- name: Setup Rust cache
|
||||||
|
if: matrix.os != 'windows-latest'
|
||||||
uses: swatinem/rust-cache@v2
|
uses: swatinem/rust-cache@v2
|
||||||
with:
|
with:
|
||||||
workspaces: './src-tauri -> target'
|
workspaces: './src-tauri -> target'
|
||||||
@ -224,14 +231,14 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
includeRelease: false
|
includeRelease: false
|
||||||
includeDebug: true
|
includeDebug: true
|
||||||
args: ${{ matrix.os == 'macos-14' && '--target universal-apple-darwin' || '' }}
|
args: "${{ env.TAURI_ARGS_MACOS }} ${{ env.TAURI_ARGS_UBUNTU }}"
|
||||||
|
|
||||||
- name: Build the app (release) and sign
|
- name: Build the app (release) and sign
|
||||||
uses: tauri-apps/tauri-action@v0
|
uses: tauri-apps/tauri-action@v0
|
||||||
if: ${{ env.BUILD_RELEASE == 'true' }}
|
if: ${{ env.BUILD_RELEASE == 'true' }}
|
||||||
env:
|
env:
|
||||||
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
||||||
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
||||||
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
|
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
|
||||||
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
||||||
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
|
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
|
||||||
@ -240,7 +247,7 @@ jobs:
|
|||||||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||||
TAURI_CONF_ARGS: "--config ${{ matrix.os == 'windows-latest' && 'src-tauri\\tauri.release.conf.json' || 'src-tauri/tauri.release.conf.json' }}"
|
TAURI_CONF_ARGS: "--config ${{ matrix.os == 'windows-latest' && 'src-tauri\\tauri.release.conf.json' || 'src-tauri/tauri.release.conf.json' }}"
|
||||||
with:
|
with:
|
||||||
args: "${{ matrix.os == 'macos-14' && '--target universal-apple-darwin' || '' }} ${{ env.TAURI_CONF_ARGS }}"
|
args: "${{ env.TAURI_CONF_ARGS }} ${{ env.TAURI_ARGS_MACOS }} ${{ env.TAURI_ARGS_UBUNTU }}"
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v3
|
- uses: actions/upload-artifact@v3
|
||||||
if: matrix.os != 'ubuntu-latest'
|
if: matrix.os != 'ubuntu-latest'
|
||||||
@ -250,10 +257,11 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
path: "${{ env.PREFIX }}/${{ env.MODE }}/bundle/*/*"
|
path: "${{ env.PREFIX }}/${{ env.MODE }}/bundle/*/*"
|
||||||
|
|
||||||
|
# TODO: re-enable linux e2e tests when possible
|
||||||
- name: Run e2e tests (linux only)
|
- name: Run e2e tests (linux only)
|
||||||
if: matrix.os == 'ubuntu-latest'
|
if: false
|
||||||
run: |
|
run: |
|
||||||
cargo install tauri-driver@0.1.3
|
cargo install tauri-driver
|
||||||
source .env.${{ env.BUILD_RELEASE == 'true' && 'production' || 'development' }}
|
source .env.${{ env.BUILD_RELEASE == 'true' && 'production' || 'development' }}
|
||||||
export VITE_KC_API_BASE_URL
|
export VITE_KC_API_BASE_URL
|
||||||
xvfb-run yarn test:e2e:tauri
|
xvfb-run yarn test:e2e:tauri
|
||||||
|
1
.gitignore
vendored
@ -51,5 +51,6 @@ e2e/playwright/export-snapshots/*
|
|||||||
|
|
||||||
## generated files
|
## generated files
|
||||||
src/**/*.typegen.ts
|
src/**/*.typegen.ts
|
||||||
|
src-tauri/gen
|
||||||
|
|
||||||
src/wasm-lib/grackle/stdlib_cube_partial.json
|
src/wasm-lib/grackle/stdlib_cube_partial.json
|
||||||
|
@ -281,7 +281,7 @@ https://github.com/KittyCAD/modeling-app/assets/29681384/6f5e8e85-1003-4fd9-be7f
|
|||||||
<details>
|
<details>
|
||||||
|
|
||||||
<summary>
|
<summary>
|
||||||
Ps for the debug panel, the following JSON is useful for snapping the camera
|
PS: for the debug panel, the following JSON is useful for snapping the camera
|
||||||
</summary>
|
</summary>
|
||||||
|
|
||||||
```JSON
|
```JSON
|
||||||
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 79 KiB |
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 48 KiB |
@ -1,7 +1,10 @@
|
|||||||
import { browser, $, expect } from '@wdio/globals'
|
import { browser, $, expect } from '@wdio/globals'
|
||||||
import fs from 'fs/promises'
|
import fs from 'fs/promises'
|
||||||
|
|
||||||
const defaultDir = `${process.env.HOME}/Documents/zoo-modeling-app-projects`
|
const documentsDir = `${process.env.HOME}/Documents`
|
||||||
|
const userSettingsFile = `${process.env.HOME}/.config/dev.zoo.modeling-app/user.toml`
|
||||||
|
const defaultProjectDir = `${documentsDir}/zoo-modeling-app-projects`
|
||||||
|
const newProjectDir = `${documentsDir}/a-different-directory`
|
||||||
const userCodeDir = '/tmp/kittycad_user_code'
|
const userCodeDir = '/tmp/kittycad_user_code'
|
||||||
|
|
||||||
async function click(element: WebdriverIO.Element): Promise<void> {
|
async function click(element: WebdriverIO.Element): Promise<void> {
|
||||||
@ -10,12 +13,25 @@ async function click(element: WebdriverIO.Element): Promise<void> {
|
|||||||
await browser.execute('arguments[0].click();', element)
|
await browser.execute('arguments[0].click();', element)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Shoutout to @Sheap on Github for a great workaround utility:
|
||||||
|
* https://github.com/tauri-apps/tauri/issues/6541#issue-1638944060
|
||||||
|
*/
|
||||||
|
async function setDatasetValue(
|
||||||
|
field: WebdriverIO.Element,
|
||||||
|
property: string,
|
||||||
|
value: string
|
||||||
|
) {
|
||||||
|
await browser.execute(`arguments[0].dataset.${property} = "${value}"`, field)
|
||||||
|
}
|
||||||
|
|
||||||
describe('ZMA (Tauri, Linux)', () => {
|
describe('ZMA (Tauri, Linux)', () => {
|
||||||
it('opens the auth page and signs in', async () => {
|
it('opens the auth page and signs in', async () => {
|
||||||
// Clean up filesystem from previous tests
|
// Clean up filesystem from previous tests
|
||||||
await new Promise((resolve) => setTimeout(resolve, 100))
|
await new Promise((resolve) => setTimeout(resolve, 100))
|
||||||
await fs.rm(defaultDir, { force: true, recursive: true })
|
await fs.rm(defaultProjectDir, { force: true, recursive: true })
|
||||||
await fs.rm(userCodeDir, { force: true })
|
await fs.rm(userCodeDir, { force: true })
|
||||||
|
await fs.rm(userSettingsFile, { force: true })
|
||||||
|
await fs.mkdir(newProjectDir, { recursive: true })
|
||||||
|
|
||||||
const signInButton = await $('[data-testid="sign-in-button"]')
|
const signInButton = await $('[data-testid="sign-in-button"]')
|
||||||
expect(await signInButton.getText()).toEqual('Sign in')
|
expect(await signInButton.getText()).toEqual('Sign in')
|
||||||
@ -65,8 +81,20 @@ describe('ZMA (Tauri, Linux)', () => {
|
|||||||
const settingsButton = await $('[data-testid="settings-button"]')
|
const settingsButton = await $('[data-testid="settings-button"]')
|
||||||
await click(settingsButton)
|
await click(settingsButton)
|
||||||
|
|
||||||
const defaultDirInput = await $('[data-testid="default-directory-input"]')
|
const projectDirInput = await $('[data-testid="project-directory-input"]')
|
||||||
expect(await defaultDirInput.getValue()).toEqual(defaultDir)
|
expect(await projectDirInput.getValue()).toEqual(defaultProjectDir)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We've set up the project directory input (in initialSettings.tsx)
|
||||||
|
* to be able to skip the folder selection dialog if data-testValue
|
||||||
|
* has a value, allowing us to test the input otherwise works.
|
||||||
|
*/
|
||||||
|
await setDatasetValue(projectDirInput, 'testValue', newProjectDir)
|
||||||
|
const projectDirButton = await $('[data-testid="project-directory-button"]')
|
||||||
|
await click(projectDirButton)
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 500))
|
||||||
|
// This line is broken. I need a different way to grab the toast
|
||||||
|
await expect(await $('div*=Set project directory to')).toBeDisplayed()
|
||||||
|
|
||||||
const nameInput = await $('[data-testid="projects-defaultProjectName"]')
|
const nameInput = await $('[data-testid="projects-defaultProjectName"]')
|
||||||
expect(await nameInput.getValue()).toEqual('project-$nnn')
|
expect(await nameInput.getValue()).toEqual('project-$nnn')
|
||||||
|
14
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "untitled-app",
|
"name": "untitled-app",
|
||||||
"version": "0.17.2",
|
"version": "0.17.3",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/autocomplete": "^6.15.0",
|
"@codemirror/autocomplete": "^6.15.0",
|
||||||
@ -16,7 +16,12 @@
|
|||||||
"@open-rpc/client-js": "^1.8.1",
|
"@open-rpc/client-js": "^1.8.1",
|
||||||
"@react-hook/resize-observer": "^1.2.6",
|
"@react-hook/resize-observer": "^1.2.6",
|
||||||
"@replit/codemirror-interact": "^6.3.0",
|
"@replit/codemirror-interact": "^6.3.0",
|
||||||
"@tauri-apps/api": "^1.5.3",
|
"@tauri-apps/api": "^2.0.0-beta.7",
|
||||||
|
"@tauri-apps/plugin-dialog": "^2.0.0-beta.2",
|
||||||
|
"@tauri-apps/plugin-fs": "^2.0.0-beta.2",
|
||||||
|
"@tauri-apps/plugin-http": "^2.0.0-beta.2",
|
||||||
|
"@tauri-apps/plugin-os": "^2.0.0-beta.2",
|
||||||
|
"@tauri-apps/plugin-shell": "^2.0.0-beta.2",
|
||||||
"@testing-library/jest-dom": "^5.14.1",
|
"@testing-library/jest-dom": "^5.14.1",
|
||||||
"@testing-library/react": "^14.0.0",
|
"@testing-library/react": "^14.0.0",
|
||||||
"@testing-library/user-event": "^14.5.2",
|
"@testing-library/user-event": "^14.5.2",
|
||||||
@ -48,7 +53,6 @@
|
|||||||
"react-router-dom": "^6.22.3",
|
"react-router-dom": "^6.22.3",
|
||||||
"sketch-helpers": "^0.0.4",
|
"sketch-helpers": "^0.0.4",
|
||||||
"swr": "^2.2.2",
|
"swr": "^2.2.2",
|
||||||
"tauri-plugin-fs-extra-api": "https://github.com/tauri-apps/tauri-plugin-fs-extra#v1",
|
|
||||||
"three": "^0.160.0",
|
"three": "^0.160.0",
|
||||||
"toml": "^3.0.0",
|
"toml": "^3.0.0",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
@ -86,7 +90,7 @@
|
|||||||
"remove-importmeta": "sed -i 's/import.meta.url/window.location.origin/g' \"./src/wasm-lib/pkg/wasm_lib.js\"; sed -i '' 's/import.meta.url/window.location.origin/g' \"./src/wasm-lib/pkg/wasm_lib.js\" || echo \"sed for both mac and linux\"",
|
"remove-importmeta": "sed -i 's/import.meta.url/window.location.origin/g' \"./src/wasm-lib/pkg/wasm_lib.js\"; sed -i '' 's/import.meta.url/window.location.origin/g' \"./src/wasm-lib/pkg/wasm_lib.js\" || echo \"sed for both mac and linux\"",
|
||||||
"wasm-prep": "rm -rf src/wasm-lib/pkg && mkdir src/wasm-lib/pkg && rm -rf src/wasm-lib/kcl/bindings",
|
"wasm-prep": "rm -rf src/wasm-lib/pkg && mkdir src/wasm-lib/pkg && rm -rf src/wasm-lib/kcl/bindings",
|
||||||
"lint": "eslint --fix src",
|
"lint": "eslint --fix src",
|
||||||
"bump-jsons": "echo \"$(jq --arg v \"$VERSION\" '.version=$v' package.json --indent 2)\" > package.json && echo \"$(jq --arg v \"$VERSION\" '.package.version=$v' src-tauri/tauri.conf.json --indent 2)\" > src-tauri/tauri.conf.json",
|
"bump-jsons": "echo \"$(jq --arg v \"$VERSION\" '.version=$v' package.json --indent 2)\" > package.json && echo \"$(jq --arg v \"$VERSION\" '.version=$v' src-tauri/tauri.conf.json --indent 2)\" > src-tauri/tauri.conf.json",
|
||||||
"postinstall": "yarn xstate:typegen",
|
"postinstall": "yarn xstate:typegen",
|
||||||
"xstate:typegen": "yarn xstate typegen \"src/**/*.ts?(x)\""
|
"xstate:typegen": "yarn xstate typegen \"src/**/*.ts?(x)\""
|
||||||
},
|
},
|
||||||
@ -112,7 +116,7 @@
|
|||||||
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
|
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
|
||||||
"@babel/preset-env": "^7.24.3",
|
"@babel/preset-env": "^7.24.3",
|
||||||
"@playwright/test": "^1.39.0",
|
"@playwright/test": "^1.39.0",
|
||||||
"@tauri-apps/cli": "^1.5.11",
|
"@tauri-apps/cli": "^2.0.0-beta.12",
|
||||||
"@types/crypto-js": "^4.2.2",
|
"@types/crypto-js": "^4.2.2",
|
||||||
"@types/debounce-promise": "^3.1.9",
|
"@types/debounce-promise": "^3.1.9",
|
||||||
"@types/pixelmatch": "^5.2.6",
|
"@types/pixelmatch": "^5.2.6",
|
||||||
|
2545
src-tauri/Cargo.lock
generated
@ -7,12 +7,12 @@ license = ""
|
|||||||
repository = "https://github.com/KittyCAD/modeling-app"
|
repository = "https://github.com/KittyCAD/modeling-app"
|
||||||
default-run = "app"
|
default-run = "app"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.60"
|
rust-version = "1.70"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tauri-build = { version = "1.5.1", features = [] }
|
tauri-build = { version = "2.0.0-beta", features = [] }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
@ -20,8 +20,13 @@ kittycad = "0.2.63"
|
|||||||
oauth2 = "4.4.2"
|
oauth2 = "4.4.2"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
tauri = { version = "1.6.1", features = [ "os-all", "dialog-all", "fs-all", "http-request", "path-all", "shell-open", "shell-open-api", "devtools"] }
|
tauri = { version = "2.0.0-beta", features = [ "devtools", "unstable"] }
|
||||||
tauri-plugin-fs-extra = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
|
tauri-plugin-dialog = { version = "2.0.0-beta.0" }
|
||||||
|
tauri-plugin-fs = { version = "2.0.0-beta.0" }
|
||||||
|
tauri-plugin-http = { version = "2.0.0-beta.0" }
|
||||||
|
tauri-plugin-os = { version = "2.0.0-beta.0" }
|
||||||
|
tauri-plugin-shell = { version = "2.0.0-beta.0" }
|
||||||
|
tauri-plugin-updater = { version = "2.0.0-beta.0" }
|
||||||
tokio = { version = "1.37.0", features = ["time"] }
|
tokio = { version = "1.37.0", features = ["time"] }
|
||||||
toml = "0.8.2"
|
toml = "0.8.2"
|
||||||
|
|
||||||
|
87
src-tauri/capabilities/desktop.json
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
{
|
||||||
|
"$schema": "../gen/schemas/desktop-schema.json",
|
||||||
|
"identifier": "main-capability",
|
||||||
|
"description": "Capability for the main window",
|
||||||
|
"context": "local",
|
||||||
|
"windows": [
|
||||||
|
"main"
|
||||||
|
],
|
||||||
|
"permissions": [
|
||||||
|
"path:default",
|
||||||
|
"event:default",
|
||||||
|
"window:default",
|
||||||
|
"app:default",
|
||||||
|
"resources:default",
|
||||||
|
"menu:default",
|
||||||
|
"tray:default",
|
||||||
|
"fs:allow-create",
|
||||||
|
"fs:allow-read-file",
|
||||||
|
"fs:allow-read-text-file",
|
||||||
|
"fs:allow-write-file",
|
||||||
|
"fs:allow-write-text-file",
|
||||||
|
"fs:allow-read-dir",
|
||||||
|
"fs:allow-copy-file",
|
||||||
|
"fs:allow-mkdir",
|
||||||
|
"fs:allow-remove",
|
||||||
|
"fs:allow-remove",
|
||||||
|
"fs:allow-rename",
|
||||||
|
"fs:allow-exists",
|
||||||
|
"fs:allow-stat",
|
||||||
|
{
|
||||||
|
"identifier": "fs:scope",
|
||||||
|
"allow": [
|
||||||
|
{
|
||||||
|
"path": "$HOME/**/*"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "$HOME/.config"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "$HOME/.config/**/*"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "$APPCONFIG"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "$APPCONFIG/**/*"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "$DOCUMENT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "$DOCUMENT/**/*"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"shell:allow-open",
|
||||||
|
"dialog:allow-open",
|
||||||
|
"dialog:allow-save",
|
||||||
|
"dialog:allow-message",
|
||||||
|
"dialog:allow-ask",
|
||||||
|
"dialog:allow-confirm",
|
||||||
|
{
|
||||||
|
"identifier": "http:default",
|
||||||
|
"allow": [
|
||||||
|
"https://dev.kittycad.io/*",
|
||||||
|
"https://dev.zoo.dev/*",
|
||||||
|
"https://kittycad.io/*",
|
||||||
|
"https://zoo.dev/*",
|
||||||
|
"https://api.dev.kittycad.io/*",
|
||||||
|
"https://api.dev.zoo.dev/*"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"os:allow-platform",
|
||||||
|
"os:allow-version",
|
||||||
|
"os:allow-os-type",
|
||||||
|
"os:allow-family",
|
||||||
|
"os:allow-arch",
|
||||||
|
"os:allow-exe-extension",
|
||||||
|
"os:allow-locale",
|
||||||
|
"os:allow-hostname"
|
||||||
|
],
|
||||||
|
"platforms": [
|
||||||
|
"linux",
|
||||||
|
"macOS",
|
||||||
|
"windows"
|
||||||
|
]
|
||||||
|
}
|
@ -4,11 +4,15 @@
|
|||||||
use std::env;
|
use std::env;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use oauth2::TokenResponse;
|
use oauth2::TokenResponse;
|
||||||
|
use serde::Serialize;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
use tauri::{InvokeError, Manager};
|
use tauri::ipc::InvokeError;
|
||||||
|
use tauri_plugin_shell::ShellExt;
|
||||||
const DEFAULT_HOST: &str = "https://api.kittycad.io";
|
const DEFAULT_HOST: &str = "https://api.kittycad.io";
|
||||||
|
|
||||||
/// This command returns the a json string parse from a toml file at the path.
|
/// This command returns the a json string parse from a toml file at the path.
|
||||||
@ -24,6 +28,56 @@ fn read_toml(path: &str) -> Result<String, InvokeError> {
|
|||||||
Ok(value)
|
Ok(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// From https://github.com/tauri-apps/tauri/blob/1.x/core/tauri/src/api/dir.rs#L51
|
||||||
|
/// Removed from tauri v2
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct DiskEntry {
|
||||||
|
/// The path to the entry.
|
||||||
|
pub path: PathBuf,
|
||||||
|
/// The name of the entry (file name with extension or directory name).
|
||||||
|
pub name: Option<String>,
|
||||||
|
/// The children of this entry if it's a directory.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub children: Option<Vec<DiskEntry>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// From https://github.com/tauri-apps/tauri/blob/1.x/core/tauri/src/api/dir.rs#L51
|
||||||
|
/// Removed from tauri v2
|
||||||
|
fn is_dir<P: AsRef<Path>>(path: P) -> Result<bool> {
|
||||||
|
std::fs::metadata(path)
|
||||||
|
.map(|md| md.is_dir())
|
||||||
|
.map_err(Into::into)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// From https://github.com/tauri-apps/tauri/blob/1.x/core/tauri/src/api/dir.rs#L51
|
||||||
|
/// Removed from tauri v2
|
||||||
|
#[tauri::command]
|
||||||
|
fn read_dir_recursive(path: &str) -> Result<Vec<DiskEntry>, InvokeError> {
|
||||||
|
let mut files_and_dirs: Vec<DiskEntry> = vec![];
|
||||||
|
// let path = path.as_ref();
|
||||||
|
for entry in fs::read_dir(path).map_err(|e| InvokeError::from_anyhow(e.into()))? {
|
||||||
|
let path = entry
|
||||||
|
.map_err(|e| InvokeError::from_anyhow(e.into()))?
|
||||||
|
.path();
|
||||||
|
|
||||||
|
if let Ok(flag) = is_dir(&path) {
|
||||||
|
files_and_dirs.push(DiskEntry {
|
||||||
|
path: path.clone(),
|
||||||
|
children: if flag {
|
||||||
|
Some(read_dir_recursive(path.to_str().expect("No path"))?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
name: path
|
||||||
|
.file_name()
|
||||||
|
.map(|name| name.to_string_lossy())
|
||||||
|
.map(|name| name.to_string()),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(files_and_dirs)
|
||||||
|
}
|
||||||
|
|
||||||
/// This command returns a string that is the contents of a file at the path.
|
/// This command returns a string that is the contents of a file at the path.
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
fn read_txt_file(path: &str) -> Result<String, InvokeError> {
|
fn read_txt_file(path: &str) -> Result<String, InvokeError> {
|
||||||
@ -85,7 +139,8 @@ async fn login(app: tauri::AppHandle, host: &str) -> Result<String, InvokeError>
|
|||||||
fs::write("/tmp/kittycad_user_code", details.user_code().secret())
|
fs::write("/tmp/kittycad_user_code", details.user_code().secret())
|
||||||
.expect("Unable to write /tmp/kittycad_user_code file");
|
.expect("Unable to write /tmp/kittycad_user_code file");
|
||||||
} else {
|
} else {
|
||||||
tauri::api::shell::open(&app.shell_scope(), auth_uri.secret(), None)
|
app.shell()
|
||||||
|
.open(auth_uri.secret(), None)
|
||||||
.map_err(|e| InvokeError::from_anyhow(e.into()))?;
|
.map_err(|e| InvokeError::from_anyhow(e.into()))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,12 +220,15 @@ fn show_in_folder(path: String) {
|
|||||||
fn main() {
|
fn main() {
|
||||||
tauri::Builder::default()
|
tauri::Builder::default()
|
||||||
.setup(|_app| {
|
.setup(|_app| {
|
||||||
#[cfg(debug_assertions)] // only include this code on debug builds
|
#[cfg(debug_assertions)]
|
||||||
{
|
{
|
||||||
let window = _app.get_window("main").unwrap();
|
use tauri::Manager;
|
||||||
// comment out the below if you don't devtools to open everytime.
|
_app.get_webview("main").unwrap().open_devtools();
|
||||||
// it's useful because otherwise devtools shuts everytime rust code changes.
|
}
|
||||||
window.open_devtools();
|
#[cfg(not(debug_assertions))]
|
||||||
|
{
|
||||||
|
_app.handle()
|
||||||
|
.plugin(tauri_plugin_updater::Builder::new().build())?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
@ -179,9 +237,14 @@ fn main() {
|
|||||||
login,
|
login,
|
||||||
read_toml,
|
read_toml,
|
||||||
read_txt_file,
|
read_txt_file,
|
||||||
|
read_dir_recursive,
|
||||||
show_in_folder,
|
show_in_folder,
|
||||||
])
|
])
|
||||||
.plugin(tauri_plugin_fs_extra::init())
|
.plugin(tauri_plugin_dialog::init())
|
||||||
|
.plugin(tauri_plugin_fs::init())
|
||||||
|
.plugin(tauri_plugin_http::init())
|
||||||
|
.plugin(tauri_plugin_os::init())
|
||||||
|
.plugin(tauri_plugin_shell::init())
|
||||||
.run(tauri::generate_context!())
|
.run(tauri::generate_context!())
|
||||||
.expect("error while running tauri application");
|
.expect("error while running tauri application");
|
||||||
}
|
}
|
||||||
|
@ -1,63 +1,28 @@
|
|||||||
{
|
{
|
||||||
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
|
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
|
||||||
"build": {
|
"app": {
|
||||||
"beforeDevCommand": "yarn start",
|
"security": {
|
||||||
"devPath": "http://localhost:3000",
|
"csp": null
|
||||||
"distDir": "../build"
|
|
||||||
},
|
},
|
||||||
"package": {
|
"windows": [
|
||||||
"productName": "zoo-modeling-app",
|
{
|
||||||
"version": "0.17.2"
|
"fullscreen": false,
|
||||||
},
|
"height": 1200,
|
||||||
"tauri": {
|
"resizable": true,
|
||||||
"allowlist": {
|
"title": "Zoo Modeling App",
|
||||||
"all": false,
|
"width": 1800
|
||||||
"dialog": {
|
}
|
||||||
"all": true,
|
|
||||||
"ask": true,
|
|
||||||
"confirm": true,
|
|
||||||
"message": true,
|
|
||||||
"open": true,
|
|
||||||
"save": true
|
|
||||||
},
|
|
||||||
"fs": {
|
|
||||||
"scope": [
|
|
||||||
"$HOME/**/*",
|
|
||||||
"$APPCONFIG",
|
|
||||||
"$APPCONFIG/**/*",
|
|
||||||
"$DOCUMENT",
|
|
||||||
"$DOCUMENT/**/*"
|
|
||||||
],
|
|
||||||
"all": true
|
|
||||||
},
|
|
||||||
"http": {
|
|
||||||
"request": true,
|
|
||||||
"scope": [
|
|
||||||
"https://dev.kittycad.io/*",
|
|
||||||
"https://dev.zoo.dev/*",
|
|
||||||
"https://kittycad.io/*",
|
|
||||||
"https://zoo.dev/*",
|
|
||||||
"https://api.dev.kittycad.io/*",
|
|
||||||
"https://api.dev.zoo.dev/*"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"os": {
|
"build": {
|
||||||
"all": true
|
"beforeDevCommand": "yarn start",
|
||||||
},
|
"devUrl": "http://localhost:3000",
|
||||||
"shell": {
|
"frontendDist": "../build"
|
||||||
"open": true
|
|
||||||
},
|
|
||||||
"path": {
|
|
||||||
"all": true
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"bundle": {
|
"bundle": {
|
||||||
"active": true,
|
"active": true,
|
||||||
"category": "DeveloperTool",
|
"category": "DeveloperTool",
|
||||||
"copyright": "",
|
"copyright": "",
|
||||||
"deb": {
|
|
||||||
"depends": []
|
|
||||||
},
|
|
||||||
"externalBin": [],
|
"externalBin": [],
|
||||||
"icon": [
|
"icon": [
|
||||||
"icons/32x32.png",
|
"icons/32x32.png",
|
||||||
@ -66,7 +31,11 @@
|
|||||||
"icons/icon.icns",
|
"icons/icon.icns",
|
||||||
"icons/icon.ico"
|
"icons/icon.ico"
|
||||||
],
|
],
|
||||||
"identifier": "dev.zoo.modeling-app",
|
"linux": {
|
||||||
|
"deb": {
|
||||||
|
"depends": []
|
||||||
|
}
|
||||||
|
},
|
||||||
"longDescription": "",
|
"longDescription": "",
|
||||||
"macOS": {
|
"macOS": {
|
||||||
"entitlements": null,
|
"entitlements": null,
|
||||||
@ -79,20 +48,12 @@
|
|||||||
"shortDescription": "",
|
"shortDescription": "",
|
||||||
"targets": "all"
|
"targets": "all"
|
||||||
},
|
},
|
||||||
"security": {
|
"identifier": "dev.zoo.modeling-app",
|
||||||
"csp": null
|
"plugins": {
|
||||||
|
"shell": {
|
||||||
|
"open": true
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"updater": {
|
"productName": "Zoo Modeling App",
|
||||||
"active": false
|
"version": "0.17.3"
|
||||||
},
|
|
||||||
"windows": [
|
|
||||||
{
|
|
||||||
"fullscreen": false,
|
|
||||||
"height": 1200,
|
|
||||||
"resizable": true,
|
|
||||||
"title": "Zoo Modeling App",
|
|
||||||
"width": 1800
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
|
|
||||||
"package": {
|
|
||||||
"productName": "Zoo Modeling App"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,13 @@
|
|||||||
{
|
{
|
||||||
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
|
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
|
||||||
"tauri": {
|
"bundle": {
|
||||||
|
"windows": {
|
||||||
|
"certificateThumbprint": "F4C9A52FF7BC26EE5E054946F6B11DEEA94C748D",
|
||||||
|
"digestAlgorithm": "sha256",
|
||||||
|
"timestampUrl": "http://timestamp.digicert.com"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"plugins": {
|
||||||
"updater": {
|
"updater": {
|
||||||
"active": true,
|
"active": true,
|
||||||
"endpoints": [
|
"endpoints": [
|
||||||
@ -8,14 +15,6 @@
|
|||||||
],
|
],
|
||||||
"dialog": true,
|
"dialog": true,
|
||||||
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEUzNzA4MjBEQjFBRTY4NzYKUldSMmFLNnhEWUp3NCtsT21Jd05wQktOaGVkOVp6MUFma0hNTDRDSnI2RkJJTEZOWG1ncFhqcU8K"
|
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEUzNzA4MjBEQjFBRTY4NzYKUldSMmFLNnhEWUp3NCtsT21Jd05wQktOaGVkOVp6MUFma0hNTDRDSnI2RkJJTEZOWG1ncFhqcU8K"
|
||||||
},
|
|
||||||
"bundle": {
|
|
||||||
"identifier": "io.kittycad.modeling-app",
|
|
||||||
"windows": {
|
|
||||||
"certificateThumbprint": "F4C9A52FF7BC26EE5E054946F6B11DEEA94C748D",
|
|
||||||
"digestAlgorithm": "sha256",
|
|
||||||
"timestampUrl": "http://timestamp.digicert.com"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
|
|
||||||
"package": {
|
|
||||||
"productName": "Zoo Modeling App"
|
|
||||||
}
|
|
||||||
}
|
|
@ -153,7 +153,7 @@ export function App() {
|
|||||||
<ModalContainer />
|
<ModalContainer />
|
||||||
<Resizable
|
<Resizable
|
||||||
className={
|
className={
|
||||||
'pointer-events-none h-full flex flex-col flex-1 z-10 my-5 ml-5 pr-1 transition-opacity transition-duration-75 ' +
|
'pointer-events-none h-full flex flex-col flex-1 z-10 my-2 ml-2 pr-1 transition-opacity transition-duration-75 ' +
|
||||||
+paneOpacity
|
+paneOpacity
|
||||||
}
|
}
|
||||||
defaultSize={{
|
defaultSize={{
|
||||||
@ -166,7 +166,7 @@ export function App() {
|
|||||||
maxHeight={'auto'}
|
maxHeight={'auto'}
|
||||||
handleClasses={{
|
handleClasses={{
|
||||||
right:
|
right:
|
||||||
'hover:bg-chalkboard-10/50 bg-transparent transition-colors duration-75 transition-ease-out delay-100 ' +
|
'hover:bg-chalkboard-10 hover:dark:bg-chalkboard-110 bg-transparent transition-colors duration-75 transition-ease-out delay-100 ' +
|
||||||
(buttonDownInStream || onboardingStatus.current === 'camera'
|
(buttonDownInStream || onboardingStatus.current === 'camera'
|
||||||
? 'pointer-events-none '
|
? 'pointer-events-none '
|
||||||
: 'pointer-events-auto'),
|
: 'pointer-events-auto'),
|
||||||
@ -202,7 +202,7 @@ export function App() {
|
|||||||
theme={editorTheme}
|
theme={editorTheme}
|
||||||
open={openPanes.includes('kclErrors')}
|
open={openPanes.includes('kclErrors')}
|
||||||
title="KCL Errors"
|
title="KCL Errors"
|
||||||
iconClassNames={{ icon: 'group-open:text-destroy-30' }}
|
iconClassNames={{ bg: 'group-open:bg-destroy-70' }}
|
||||||
/>
|
/>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
@ -66,10 +66,10 @@ const router = createBrowserRouter([
|
|||||||
<Outlet />
|
<Outlet />
|
||||||
<App />
|
<App />
|
||||||
<CommandBar />
|
<CommandBar />
|
||||||
|
{!isTauri() && import.meta.env.PROD && <DownloadAppBanner />}
|
||||||
</ModelingMachineProvider>
|
</ModelingMachineProvider>
|
||||||
<WasmErrBanner />
|
<WasmErrBanner />
|
||||||
</FileMachineProvider>
|
</FileMachineProvider>
|
||||||
{!isTauri() && import.meta.env.PROD && <DownloadAppBanner />}
|
|
||||||
</Auth>
|
</Auth>
|
||||||
),
|
),
|
||||||
children: [
|
children: [
|
||||||
|
@ -18,8 +18,12 @@ export const Toolbar = () => {
|
|||||||
const { commandBarSend } = useCommandsContext()
|
const { commandBarSend } = useCommandsContext()
|
||||||
const { state, send, context } = useModelingContext()
|
const { state, send, context } = useModelingContext()
|
||||||
const toolbarButtonsRef = useRef<HTMLUListElement>(null)
|
const toolbarButtonsRef = useRef<HTMLUListElement>(null)
|
||||||
|
const iconClassName =
|
||||||
|
'group-disabled:text-chalkboard-50 group-enabled:group-hover:!text-chalkboard-10 group-pressed:!text-chalkboard-10'
|
||||||
const bgClassName =
|
const bgClassName =
|
||||||
'group-enabled:group-hover:bg-energy-10 group-pressed:bg-energy-10 dark:group-enabled:group-hover:bg-chalkboard-80 dark:group-pressed:bg-chalkboard-80'
|
'group-disabled:!bg-transparent group-enabled:group-hover:bg-primary group-pressed:bg-primary'
|
||||||
|
const buttonClassName =
|
||||||
|
'bg-chalkboard-10 dark:bg-chalkboard-100 hover:bg-chalkboard-10 dark:hover:bg-chalkboard-100'
|
||||||
const pathId = useMemo(() => {
|
const pathId = useMemo(() => {
|
||||||
if (!isSingleCursorInPipe(context.selectionRanges, kclManager.ast)) {
|
if (!isSingleCursorInPipe(context.selectionRanges, kclManager.ast)) {
|
||||||
return false
|
return false
|
||||||
@ -64,12 +68,14 @@ export const Toolbar = () => {
|
|||||||
{state.nextEvents.includes('Enter sketch') && (
|
{state.nextEvents.includes('Enter sketch') && (
|
||||||
<li className="contents">
|
<li className="contents">
|
||||||
<ActionButton
|
<ActionButton
|
||||||
|
className={buttonClassName}
|
||||||
Element="button"
|
Element="button"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
send({ type: 'Enter sketch', data: { forceNewSketch: true } })
|
send({ type: 'Enter sketch', data: { forceNewSketch: true } })
|
||||||
}
|
}
|
||||||
icon={{
|
icon={{
|
||||||
icon: 'sketch',
|
icon: 'sketch',
|
||||||
|
iconClassName,
|
||||||
bgClassName,
|
bgClassName,
|
||||||
}}
|
}}
|
||||||
disabled={disableAllButtons}
|
disabled={disableAllButtons}
|
||||||
@ -81,10 +87,12 @@ export const Toolbar = () => {
|
|||||||
{state.nextEvents.includes('Enter sketch') && pathId && (
|
{state.nextEvents.includes('Enter sketch') && pathId && (
|
||||||
<li className="contents">
|
<li className="contents">
|
||||||
<ActionButton
|
<ActionButton
|
||||||
|
className={buttonClassName}
|
||||||
Element="button"
|
Element="button"
|
||||||
onClick={() => send({ type: 'Enter sketch' })}
|
onClick={() => send({ type: 'Enter sketch' })}
|
||||||
icon={{
|
icon={{
|
||||||
icon: 'sketch',
|
icon: 'sketch',
|
||||||
|
iconClassName,
|
||||||
bgClassName,
|
bgClassName,
|
||||||
}}
|
}}
|
||||||
disabled={disableAllButtons}
|
disabled={disableAllButtons}
|
||||||
@ -96,10 +104,12 @@ export const Toolbar = () => {
|
|||||||
{state.nextEvents.includes('Cancel') && !state.matches('idle') && (
|
{state.nextEvents.includes('Cancel') && !state.matches('idle') && (
|
||||||
<li className="contents">
|
<li className="contents">
|
||||||
<ActionButton
|
<ActionButton
|
||||||
|
className={buttonClassName}
|
||||||
Element="button"
|
Element="button"
|
||||||
onClick={() => send({ type: 'Cancel' })}
|
onClick={() => send({ type: 'Cancel' })}
|
||||||
icon={{
|
icon={{
|
||||||
icon: 'arrowLeft',
|
icon: 'arrowLeft',
|
||||||
|
iconClassName,
|
||||||
bgClassName,
|
bgClassName,
|
||||||
}}
|
}}
|
||||||
disabled={disableAllButtons}
|
disabled={disableAllButtons}
|
||||||
@ -112,6 +122,7 @@ export const Toolbar = () => {
|
|||||||
<>
|
<>
|
||||||
<li className="contents" key="line-button">
|
<li className="contents" key="line-button">
|
||||||
<ActionButton
|
<ActionButton
|
||||||
|
className={buttonClassName}
|
||||||
Element="button"
|
Element="button"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
state?.matches('Sketch.Line tool')
|
state?.matches('Sketch.Line tool')
|
||||||
@ -119,9 +130,9 @@ export const Toolbar = () => {
|
|||||||
: send('Equip Line tool')
|
: send('Equip Line tool')
|
||||||
}
|
}
|
||||||
aria-pressed={state?.matches('Sketch.Line tool')}
|
aria-pressed={state?.matches('Sketch.Line tool')}
|
||||||
className="pressed:bg-energy-10/20 dark:pressed:bg-energy-80"
|
|
||||||
icon={{
|
icon={{
|
||||||
icon: 'line',
|
icon: 'line',
|
||||||
|
iconClassName,
|
||||||
bgClassName,
|
bgClassName,
|
||||||
}}
|
}}
|
||||||
disabled={disableAllButtons}
|
disabled={disableAllButtons}
|
||||||
@ -131,6 +142,7 @@ export const Toolbar = () => {
|
|||||||
</li>
|
</li>
|
||||||
<li className="contents" key="tangential-arc-button">
|
<li className="contents" key="tangential-arc-button">
|
||||||
<ActionButton
|
<ActionButton
|
||||||
|
className={buttonClassName}
|
||||||
Element="button"
|
Element="button"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
state.matches('Sketch.Tangential arc to')
|
state.matches('Sketch.Tangential arc to')
|
||||||
@ -138,9 +150,9 @@ export const Toolbar = () => {
|
|||||||
: send('Equip tangential arc to')
|
: send('Equip tangential arc to')
|
||||||
}
|
}
|
||||||
aria-pressed={state.matches('Sketch.Tangential arc to')}
|
aria-pressed={state.matches('Sketch.Tangential arc to')}
|
||||||
className="pressed:bg-energy-10/20 dark:pressed:bg-energy-80"
|
|
||||||
icon={{
|
icon={{
|
||||||
icon: 'arc',
|
icon: 'arc',
|
||||||
|
iconClassName,
|
||||||
bgClassName,
|
bgClassName,
|
||||||
}}
|
}}
|
||||||
disabled={
|
disabled={
|
||||||
@ -179,8 +191,8 @@ export const Toolbar = () => {
|
|||||||
.map((eventName) => (
|
.map((eventName) => (
|
||||||
<li className="contents" key={eventName}>
|
<li className="contents" key={eventName}>
|
||||||
<ActionButton
|
<ActionButton
|
||||||
|
className={buttonClassName}
|
||||||
Element="button"
|
Element="button"
|
||||||
className="text-sm"
|
|
||||||
key={eventName}
|
key={eventName}
|
||||||
onClick={() => send(eventName)}
|
onClick={() => send(eventName)}
|
||||||
disabled={
|
disabled={
|
||||||
@ -191,6 +203,7 @@ export const Toolbar = () => {
|
|||||||
title={eventName}
|
title={eventName}
|
||||||
icon={{
|
icon={{
|
||||||
icon: 'line',
|
icon: 'line',
|
||||||
|
iconClassName,
|
||||||
bgClassName,
|
bgClassName,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -203,8 +216,8 @@ export const Toolbar = () => {
|
|||||||
{state.matches('idle') && (
|
{state.matches('idle') && (
|
||||||
<li className="contents">
|
<li className="contents">
|
||||||
<ActionButton
|
<ActionButton
|
||||||
|
className={buttonClassName}
|
||||||
Element="button"
|
Element="button"
|
||||||
className="text-sm"
|
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
commandBarSend({
|
commandBarSend({
|
||||||
type: 'Find and select command',
|
type: 'Find and select command',
|
||||||
@ -219,6 +232,7 @@ export const Toolbar = () => {
|
|||||||
}
|
}
|
||||||
icon={{
|
icon={{
|
||||||
icon: 'extrude',
|
icon: 'extrude',
|
||||||
|
iconClassName,
|
||||||
bgClassName,
|
bgClassName,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -231,16 +245,16 @@ export const Toolbar = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="max-w-full flex items-stretch rounded-l-sm rounded-r-full bg-chalkboard-10 dark:bg-chalkboard-100 relative">
|
<div className="max-w-full flex items-stretch rounded-l-sm rounded-r-full bg-chalkboard-10/80 dark:bg-chalkboard-110/70 relative">
|
||||||
<menu className="flex-1 pl-1 pr-2 py-0 overflow-hidden rounded-l-sm whitespace-nowrap bg-chalkboard-10 dark:bg-chalkboard-100 border-solid border border-energy-10 dark:border-chalkboard-90 border-r-0">
|
<menu className="flex-1 pl-1 pr-2 py-0 overflow-hidden rounded-l-sm whitespace-nowrap border-solid border border-primary/30 dark:border-chalkboard-90 border-r-0">
|
||||||
<ToolbarButtons />
|
<ToolbarButtons />
|
||||||
</menu>
|
</menu>
|
||||||
<ActionButton
|
<ActionButton
|
||||||
Element="button"
|
Element="button"
|
||||||
onClick={() => commandBarSend({ type: 'Open' })}
|
onClick={() => commandBarSend({ type: 'Open' })}
|
||||||
className="rounded-r-full pr-4 self-stretch border-energy-10 hover:border-energy-10 dark:border-chalkboard-80 bg-energy-10/50 hover:bg-energy-10 dark:bg-chalkboard-80 dark:text-energy-10"
|
className="rounded-r-full pr-4 self-stretch border-primary/30 hover:border-primary dark:border-chalkboard-80 dark:bg-chalkboard-80 text-primary"
|
||||||
>
|
>
|
||||||
{platform === 'darwin' ? '⌘K' : 'Ctrl+/'}
|
{platform === 'macos' ? '⌘K' : 'Ctrl+/'}
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -1,11 +1,21 @@
|
|||||||
:root {
|
:root {
|
||||||
|
--primary-hue: 264.48;
|
||||||
|
--primary-chroma: 0.2167;
|
||||||
|
--primary-lightness: 60%;
|
||||||
|
--_primary: var(--primary-lightness) var(--primary-chroma)
|
||||||
|
var(--primary-hue, 264.48);
|
||||||
|
--primary: oklch(
|
||||||
|
var(--primary-lightness) var(--primary-chroma) var(--primary-hue, 264.48) /
|
||||||
|
var(--opacity, 1)
|
||||||
|
);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Generated using Catmosphere Theme Builder
|
Generated using Catmosphere Theme Builder
|
||||||
by KittyCAD
|
by KittyCAD
|
||||||
https://catmosphere-theme-builder.vercel.app/?colors=%5B%7B%22from%22:%7B%22l%22:1,%22c%22:0.01,%22h%22:78%7D,%22to%22:%7B%22l%22:0.065,%22c%22:0.05,%22h%22:182.6%7D,%22stops%22:10,%22steps%22:12%7D,%7B%22from%22:%7B%22l%22:1,%22c%22:0.45,%22h%22:122.4%7D,%22to%22:%7B%22l%22:0.13,%22c%22:0.031,%22h%22:137.2%7D,%22stops%22:10,%22steps%22:12%7D,%7B%22from%22:%7B%22l%22:1,%22c%22:0.13,%22h%22:176%7D,%22to%22:%7B%22l%22:0.116,%22c%22:0.097,%22h%22:213.1%7D,%22stops%22:10,%22steps%22:12%7D,%7B%22from%22:%7B%22l%22:1,%22c%22:0.169,%22h%22:144.4%7D,%22to%22:%7B%22l%22:0.12,%22c%22:0.45,%22h%22:132.7%7D,%22steps%22:12%7D,%7B%22from%22:%7B%22l%22:1,%22c%22:0.087,%22h%22:261.6%7D,%22to%22:%7B%22l%22:0.22,%22c%22:0.084,%22h%22:275.5%7D,%22steps%22:12,%22uuid%22:%227tpx9pf1zd6%22%7D,%7B%22from%22:%7B%22l%22:0.954,%22c%22:0.108,%22h%22:280.6%7D,%22to%22:%7B%22l%22:0.166,%22c%22:0.188,%22h%22:263.8%7D,%22steps%22:12,%22uuid%22:%22vu652mebd3%22%7D,%7B%22from%22:%7B%22l%22:1,%22c%22:0.115,%22h%22:0%7D,%22to%22:%7B%22l%22:0.096,%22c%22:0.261,%22h%22:302%7D,%22steps%22:12%7D,%7B%22from%22:%7B%22l%22:1,%22c%22:0.185,%22h%22:19.8%7D,%22to%22:%7B%22l%22:0.368,%22c%22:0.45,%22h%22:9.4%7D,%22steps%22:8,%22uuid%22:%22g05inkd34l%22%7D,%7B%22from%22:%7B%22l%22:0.912,%22c%22:0.139,%22h%22:87%7D,%22to%22:%7B%22l%22:0.502,%22c%22:0.45,%22h%22:97.7%7D,%22steps%22:8,%22uuid%22:%22l892hcw4ef%22%7D,%7B%22from%22:%7B%22l%22:0.89,%22c%22:0.16,%22h%22:143.4%7D,%22to%22:%7B%22l%22:0.466,%22c%22:0.208,%22h%22:147.7%7D,%22steps%22:8,%22uuid%22:%22hkd09y9ov4h%22%7D%5D
|
https://catmosphere-theme-builder.vercel.app/?colors=%5B%7B%22from%22:%7B%22l%22:1,%22c%22:0.01,%22h%22:78%7D,%22to%22:%7B%22l%22:0.065,%22c%22:0.05,%22h%22:182.6%7D,%22stops%22:10,%22steps%22:12%7D,%7B%22from%22:%7B%22l%22:1,%22c%22:0.45,%22h%22:122.4%7D,%22to%22:%7B%22l%22:0.13,%22c%22:0.031,%22h%22:137.2%7D,%22stops%22:10,%22steps%22:12%7D,%7B%22from%22:%7B%22l%22:1,%22c%22:0.13,%22h%22:176%7D,%22to%22:%7B%22l%22:0.116,%22c%22:0.097,%22h%22:213.1%7D,%22stops%22:10,%22steps%22:12%7D,%7B%22from%22:%7B%22l%22:1,%22c%22:0.169,%22h%22:144.4%7D,%22to%22:%7B%22l%22:0.12,%22c%22:0.45,%22h%22:132.7%7D,%22steps%22:12%7D,%7B%22from%22:%7B%22l%22:1,%22c%22:0.087,%22h%22:261.6%7D,%22to%22:%7B%22l%22:0.22,%22c%22:0.084,%22h%22:275.5%7D,%22steps%22:12,%22uuid%22:%227tpx9pf1zd6%22%7D,%7B%22from%22:%7B%22l%22:0.954,%22c%22:0.108,%22h%22:280.6%7D,%22to%22:%7B%22l%22:0.166,%22c%22:0.188,%22h%22:263.8%7D,%22steps%22:12,%22uuid%22:%22vu652mebd3%22%7D,%7B%22from%22:%7B%22l%22:1,%22c%22:0.115,%22h%22:0%7D,%22to%22:%7B%22l%22:0.096,%22c%22:0.261,%22h%22:302%7D,%22steps%22:12%7D,%7B%22from%22:%7B%22l%22:1,%22c%22:0.185,%22h%22:19.8%7D,%22to%22:%7B%22l%22:0.368,%22c%22:0.45,%22h%22:9.4%7D,%22steps%22:8,%22uuid%22:%22g05inkd34l%22%7D,%7B%22from%22:%7B%22l%22:0.912,%22c%22:0.139,%22h%22:87%7D,%22to%22:%7B%22l%22:0.502,%22c%22:0.45,%22h%22:97.7%7D,%22steps%22:8,%22uuid%22:%22l892hcw4ef%22%7D,%7B%22from%22:%7B%22l%22:0.89,%22c%22:0.16,%22h%22:143.4%7D,%22to%22:%7B%22l%22:0.466,%22c%22:0.208,%22h%22:147.7%7D,%22steps%22:8,%22uuid%22:%22hkd09y9ov4h%22%7D%5D
|
||||||
*/
|
*/
|
||||||
/* Chalkboard */
|
/* Chalkboard */
|
||||||
--chalkboard-10: oklch(99.7% 0.008766 102.8deg);
|
--chalkboard-10: oklch(99.9% 0.003766 102.8deg);
|
||||||
--chalkboard-20: oklch(91.34% 0.009353 109deg);
|
--chalkboard-20: oklch(91.34% 0.009353 109deg);
|
||||||
--chalkboard-30: oklch(82.99% 0.00994 115.2deg);
|
--chalkboard-30: oklch(82.99% 0.00994 115.2deg);
|
||||||
--chalkboard-40: oklch(74.63% 0.01053 121.4deg);
|
--chalkboard-40: oklch(74.63% 0.01053 121.4deg);
|
||||||
|
@ -30,9 +30,9 @@ export const ActionIcon = ({
|
|||||||
children,
|
children,
|
||||||
}: ActionIconProps) => {
|
}: ActionIconProps) => {
|
||||||
// By default, we reverse the icon color and background color in dark mode
|
// By default, we reverse the icon color and background color in dark mode
|
||||||
const computedIconClassName = `h-auto dark:text-energy-10 !group-disabled:text-chalkboard-60 !group-disabled:text-chalkboard-60 ${iconClassName}`
|
const computedIconClassName = `h-auto text-primary dark:text-current !group-disabled:text-chalkboard-60 !group-disabled:text-chalkboard-60 ${iconClassName}`
|
||||||
|
|
||||||
const computedBgClassName = `bg-chalkboard-20 dark:bg-chalkboard-90 !group-disabled:bg-chalkboard-30 !dark:group-disabled:bg-chalkboard-80 ${bgClassName}`
|
const computedBgClassName = `bg-primary/10 dark:bg-chalkboard-90 !group-disabled:bg-chalkboard-30 !dark:group-disabled:bg-chalkboard-80 ${bgClassName}`
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
@ -33,7 +33,7 @@ export const AppHeader = ({
|
|||||||
className={
|
className={
|
||||||
'w-full grid ' +
|
'w-full grid ' +
|
||||||
styles.header +
|
styles.header +
|
||||||
' overlaid-panes sticky top-0 z-20 py-1 px-2 bg-chalkboard-10/70 dark:bg-chalkboard-100/50 border-b dark:border-b-2 border-chalkboard-30 dark:border-chalkboard-90 items-center ' +
|
' overlaid-panes sticky top-0 z-20 px-2 items-center ' +
|
||||||
className
|
className
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
@ -53,13 +53,13 @@ export const AppHeader = ({
|
|||||||
className="text-sm self-center flex items-center w-fit gap-3"
|
className="text-sm self-center flex items-center w-fit gap-3"
|
||||||
>
|
>
|
||||||
Command Palette{' '}
|
Command Palette{' '}
|
||||||
<kbd className="bg-energy-10/50 dark:bg-chalkboard-100 dark:text-energy-10 inline-block px-1 py-0.5 border-energy-10 dark:border-chalkboard-90">
|
<kbd className="bg-primary/10 dark:bg-chalkboard-100 dark:text-primary inline-block px-1 py-0.5 border-primary dark:border-chalkboard-90">
|
||||||
{platform === 'darwin' ? '⌘K' : 'Ctrl+/'}
|
{platform === 'macos' ? '⌘K' : 'Ctrl+/'}
|
||||||
</kbd>
|
</kbd>
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-1 ml-auto">
|
<div className="flex items-center gap-1 py-1 ml-auto">
|
||||||
{/* If there are children, show them, otherwise show User menu */}
|
{/* If there are children, show them, otherwise show User menu */}
|
||||||
{children || (
|
{children || (
|
||||||
<>
|
<>
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
.button {
|
.button {
|
||||||
@apply flex justify-between items-center gap-2 px-2 py-1 text-left border-none rounded-sm;
|
@apply flex justify-between items-center gap-2 px-2 py-1 text-left border-none rounded-sm;
|
||||||
@apply font-mono !no-underline text-xs font-bold select-none text-chalkboard-90;
|
@apply font-mono !no-underline text-xs font-bold select-none text-chalkboard-90;
|
||||||
@apply ui-active:bg-energy-10/50 ui-active:text-inherit;
|
@apply ui-active:bg-primary/10 ui-active:text-primary ui-active:text-inherit;
|
||||||
@apply transition-colors ease-out;
|
@apply transition-colors ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(.dark) .button {
|
:global(.dark) .button {
|
||||||
@apply text-chalkboard-30;
|
@apply !text-chalkboard-30;
|
||||||
@apply ui-active:bg-chalkboard-80 ui-active:text-energy-10;
|
@apply ui-active:bg-chalkboard-90;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button small {
|
.button small {
|
||||||
|
@ -30,12 +30,12 @@ export const CodeMenu = ({ children }: PropsWithChildren) => {
|
|||||||
className="p-1"
|
className="p-1"
|
||||||
size="sm"
|
size="sm"
|
||||||
bgClassName={
|
bgClassName={
|
||||||
'bg-chalkboard-20 dark:bg-chalkboard-110 hover:bg-energy-10/50 hover:dark:bg-chalkboard-90 ui-active:bg-chalkboard-80 ui-active:dark:bg-chalkboard-90 rounded-sm'
|
'!bg-transparent hover:!bg-primary/10 hover:dark:!bg-chalkboard-100 ui-active:!bg-primary/10 dark:ui-active:!bg-chalkboard-100 rounded-sm'
|
||||||
}
|
}
|
||||||
iconClassName={'text-chalkboard-90 dark:text-chalkboard-40'}
|
iconClassName={'!text-chalkboard-90 dark:!text-chalkboard-40'}
|
||||||
/>
|
/>
|
||||||
</Menu.Button>
|
</Menu.Button>
|
||||||
<Menu.Items className="absolute right-0 left-auto w-72 flex flex-col gap-1 divide-y divide-chalkboard-20 dark:divide-chalkboard-70 align-stretch px-0 py-1 bg-chalkboard-10 dark:bg-chalkboard-90 rounded-sm shadow-lg border border-solid border-chalkboard-20/50 dark:border-chalkboard-80/50">
|
<Menu.Items className="absolute right-0 left-auto w-72 flex flex-col gap-1 divide-y divide-chalkboard-20 dark:divide-chalkboard-70 align-stretch px-0 py-1 bg-chalkboard-10 dark:bg-chalkboard-100 rounded-sm shadow-lg border border-solid border-chalkboard-20/50 dark:border-chalkboard-80/50">
|
||||||
<Menu.Item>
|
<Menu.Item>
|
||||||
<button
|
<button
|
||||||
onClick={() => kclManager.format()}
|
onClick={() => kclManager.format()}
|
||||||
|
@ -3,6 +3,11 @@
|
|||||||
@apply bg-chalkboard-10/70 backdrop-blur-sm;
|
@apply bg-chalkboard-10/70 backdrop-blur-sm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.header::before,
|
||||||
|
.header::-webkit-details-marker {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
:global(.dark) .panel {
|
:global(.dark) .panel {
|
||||||
@apply bg-chalkboard-110/50 backdrop-blur-0;
|
@apply bg-chalkboard-110/50 backdrop-blur-0;
|
||||||
}
|
}
|
||||||
@ -11,7 +16,7 @@
|
|||||||
@apply sticky top-0 z-10 cursor-pointer;
|
@apply sticky top-0 z-10 cursor-pointer;
|
||||||
@apply flex items-center justify-between gap-2 w-full p-2;
|
@apply flex items-center justify-between gap-2 w-full p-2;
|
||||||
@apply font-mono text-xs font-bold select-none text-chalkboard-90;
|
@apply font-mono text-xs font-bold select-none text-chalkboard-90;
|
||||||
@apply bg-chalkboard-20;
|
@apply bg-chalkboard-10;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header:not(:last-of-type) {
|
.header:not(:last-of-type) {
|
||||||
|
@ -30,11 +30,11 @@ export const PanelHeader = ({
|
|||||||
className="p-1"
|
className="p-1"
|
||||||
size="sm"
|
size="sm"
|
||||||
bgClassName={
|
bgClassName={
|
||||||
'dark:!bg-chalkboard-100 group-open:bg-chalkboard-80 dark:group-open:!bg-chalkboard-90 border border-transparent dark:group-open:border-chalkboard-60 rounded-sm ' +
|
'dark:!bg-transparent group-open:bg-primary dark:group-open:!bg-primary rounded-sm ' +
|
||||||
(iconClassNames?.bg || '')
|
(iconClassNames?.bg || '')
|
||||||
}
|
}
|
||||||
iconClassName={
|
iconClassName={
|
||||||
'group-open:text-energy-10 ' + (iconClassNames?.icon || '')
|
'group-open:text-chalkboard-10 ' + (iconClassNames?.icon || '')
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
{title}
|
{title}
|
||||||
|
@ -141,7 +141,7 @@ function CommandArgOptionInput({
|
|||||||
<Combobox.Option
|
<Combobox.Option
|
||||||
key={option.name}
|
key={option.name}
|
||||||
value={option}
|
value={option}
|
||||||
className="flex items-center gap-2 px-4 py-1 first:mt-2 last:mb-2 ui-active:bg-energy-10/50 dark:ui-active:bg-chalkboard-90"
|
className="flex items-center gap-2 px-4 py-1 first:mt-2 last:mb-2 ui-active:bg-primary/10 dark:ui-active:bg-chalkboard-90"
|
||||||
>
|
>
|
||||||
<p className="flex-grow">{option.name} </p>
|
<p className="flex-grow">{option.name} </p>
|
||||||
{option.value === currentOption?.value && (
|
{option.value === currentOption?.value && (
|
||||||
|
@ -104,7 +104,7 @@ function CommandBarHeader({ children }: React.PropsWithChildren<{}>) {
|
|||||||
key={argName}
|
key={argName}
|
||||||
className={`relative w-fit px-2 py-1 rounded-sm flex gap-2 items-center border ${
|
className={`relative w-fit px-2 py-1 rounded-sm flex gap-2 items-center border ${
|
||||||
argName === currentArgument?.name
|
argName === currentArgument?.name
|
||||||
? 'disabled:bg-energy-10/50 dark:disabled:bg-energy-10/20 disabled:border-energy-10 dark:disabled:border-energy-10 disabled:text-chalkboard-100 dark:disabled:text-chalkboard-10'
|
? 'disabled:bg-primary/10 dark:disabled:bg-primary/20 disabled:border-primary dark:disabled:border-primary disabled:text-chalkboard-100 dark:disabled:text-chalkboard-10'
|
||||||
: 'bg-chalkboard-20/50 dark:bg-chalkboard-80/50 border-chalkboard-20 dark:border-chalkboard-80'
|
: 'bg-chalkboard-20/50 dark:bg-chalkboard-80/50 border-chalkboard-20 dark:border-chalkboard-80'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
@ -129,7 +129,7 @@ function CommandBarHeader({ children }: React.PropsWithChildren<{}>) {
|
|||||||
)
|
)
|
||||||
) : null}
|
) : null}
|
||||||
{showShortcuts && (
|
{showShortcuts && (
|
||||||
<small className="absolute -top-[1px] right-full translate-x-1/2 px-0.5 rounded-sm bg-chalkboard-80 text-chalkboard-10 dark:bg-energy-10 dark:text-chalkboard-100">
|
<small className="absolute -top-[1px] right-full translate-x-1/2 px-0.5 rounded-sm bg-chalkboard-80 text-chalkboard-10 dark:bg-primary dark:text-chalkboard-100">
|
||||||
<span className="sr-only">Hotkey: </span>
|
<span className="sr-only">Hotkey: </span>
|
||||||
{i + 1}
|
{i + 1}
|
||||||
</small>
|
</small>
|
||||||
@ -174,12 +174,11 @@ function ReviewingButton() {
|
|||||||
autoFocus
|
autoFocus
|
||||||
type="submit"
|
type="submit"
|
||||||
form="review-form"
|
form="review-form"
|
||||||
className="w-fit !p-0 rounded-sm border !border-chalkboard-100 dark:!border-energy-10 hover:shadow"
|
className="w-fit !p-0 rounded-sm border !border-primary hover:shadow"
|
||||||
icon={{
|
icon={{
|
||||||
icon: 'checkmark',
|
icon: 'checkmark',
|
||||||
bgClassName:
|
bgClassName: 'p-1 rounded-sm !bg-primary hover:brightness-110',
|
||||||
'p-1 rounded-sm !bg-chalkboard-100 hover:!bg-chalkboard-110 dark:!bg-energy-20 dark:hover:!bg-energy-10',
|
iconClassName: '!text-chalkboard-10',
|
||||||
iconClassName: '!text-energy-10 dark:!text-chalkboard-100',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span className="sr-only">Submit command</span>
|
<span className="sr-only">Submit command</span>
|
||||||
@ -193,10 +192,11 @@ function GatheringArgsButton() {
|
|||||||
Element="button"
|
Element="button"
|
||||||
type="submit"
|
type="submit"
|
||||||
form="arg-form"
|
form="arg-form"
|
||||||
className="w-fit !p-0 rounded-sm"
|
className="w-fit !p-0 rounded-sm border !border-primary hover:shadow"
|
||||||
icon={{
|
icon={{
|
||||||
icon: 'arrowRight',
|
icon: 'arrowRight',
|
||||||
bgClassName: 'p-1 rounded-sm',
|
bgClassName: 'p-1 rounded-sm !bg-primary hover:brightness-110',
|
||||||
|
iconClassName: '!text-chalkboard-10',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span className="sr-only">Continue</span>
|
<span className="sr-only">Continue</span>
|
||||||
|
@ -9,9 +9,9 @@
|
|||||||
.editor :global(.cm-line)::selection {
|
.editor :global(.cm-line)::selection {
|
||||||
@apply px-1;
|
@apply px-1;
|
||||||
@apply text-chalkboard-100;
|
@apply text-chalkboard-100;
|
||||||
@apply bg-energy-10/50;
|
@apply bg-primary/40;
|
||||||
}
|
}
|
||||||
:global(.dark) .editor :global(.cm-line)::selection {
|
:global(.dark) .editor :global(.cm-line)::selection {
|
||||||
@apply text-energy-10;
|
@apply text-chalkboard-10;
|
||||||
@apply bg-energy-10/20;
|
@apply bg-primary/40;
|
||||||
}
|
}
|
||||||
|
@ -153,7 +153,7 @@ function CommandBarKclInput({
|
|||||||
className={
|
className={
|
||||||
calcResult === 'NAN'
|
calcResult === 'NAN'
|
||||||
? 'text-destroy-80 dark:text-destroy-40'
|
? 'text-destroy-80 dark:text-destroy-40'
|
||||||
: 'text-energy-60 dark:text-energy-20'
|
: 'text-succeed-80 dark:text-succeed-40'
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{calcResult === 'NAN'
|
{calcResult === 'NAN'
|
||||||
@ -173,7 +173,7 @@ function CommandBarKclInput({
|
|||||||
type="text"
|
type="text"
|
||||||
id="variable-name"
|
id="variable-name"
|
||||||
name="variable-name"
|
name="variable-name"
|
||||||
className="flex-1 border-none bg-transparent"
|
className="flex-1 border-none bg-transparent focus:outline-none"
|
||||||
placeholder="Variable name"
|
placeholder="Variable name"
|
||||||
value={newVariableName}
|
value={newVariableName}
|
||||||
autoCapitalize="off"
|
autoCapitalize="off"
|
||||||
@ -196,7 +196,7 @@ function CommandBarKclInput({
|
|||||||
<span
|
<span
|
||||||
className={
|
className={
|
||||||
isNewVariableNameUnique
|
isNewVariableNameUnique
|
||||||
? 'text-energy-60 dark:text-energy-20'
|
? 'text-succeed-60 dark:text-succeed-40'
|
||||||
: 'text-destroy-60 dark:text-destroy-40'
|
: 'text-destroy-60 dark:text-destroy-40'
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
@ -38,11 +38,11 @@ function CommandComboBox({
|
|||||||
<div className="flex items-center gap-2 px-4 pb-2 border-solid border-0 border-b border-b-chalkboard-20 dark:border-b-chalkboard-80">
|
<div className="flex items-center gap-2 px-4 pb-2 border-solid border-0 border-b border-b-chalkboard-20 dark:border-b-chalkboard-80">
|
||||||
<CustomIcon
|
<CustomIcon
|
||||||
name="search"
|
name="search"
|
||||||
className="w-5 h-5 bg-energy-10/50 dark:bg-chalkboard-90 dark:text-energy-10"
|
className="w-5 h-5 bg-primary/10 text-primary"
|
||||||
/>
|
/>
|
||||||
<Combobox.Input
|
<Combobox.Input
|
||||||
onChange={(event) => setQuery(event.target.value)}
|
onChange={(event) => setQuery(event.target.value)}
|
||||||
className="w-full bg-transparent focus:outline-none selection:bg-energy-10/50 dark:selection:bg-energy-10/20 dark:focus:outline-none"
|
className="w-full bg-transparent focus:outline-none selection:bg-primary/20 dark:selection:bg-primary/40 dark:focus:outline-none"
|
||||||
onKeyDown={(event) => {
|
onKeyDown={(event) => {
|
||||||
if (
|
if (
|
||||||
(event.metaKey && event.key === 'k') ||
|
(event.metaKey && event.key === 'k') ||
|
||||||
@ -72,13 +72,10 @@ function CommandComboBox({
|
|||||||
<Combobox.Option
|
<Combobox.Option
|
||||||
key={option.name}
|
key={option.name}
|
||||||
value={option}
|
value={option}
|
||||||
className="flex items-center gap-2 px-4 py-1 first:mt-2 last:mb-2 ui-active:bg-energy-10/50 dark:ui-active:bg-chalkboard-90"
|
className="flex items-center gap-2 px-4 py-1 first:mt-2 last:mb-2 ui-active:bg-primary/10 dark:ui-active:bg-chalkboard-90"
|
||||||
>
|
>
|
||||||
{'icon' in option && option.icon && (
|
{'icon' in option && option.icon && (
|
||||||
<CustomIcon
|
<CustomIcon name={option.icon} className="w-5 h-5" />
|
||||||
name={option.icon}
|
|
||||||
className="w-5 h-5 dark:text-energy-10"
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
<p className="flex-grow">{option.displayName || option.name} </p>
|
<p className="flex-grow">{option.displayName || option.name} </p>
|
||||||
{option.description && (
|
{option.description && (
|
||||||
|
@ -1,54 +1,8 @@
|
|||||||
export type CustomIconName =
|
import { cloneElement } from 'react'
|
||||||
| 'arc'
|
|
||||||
| 'arrowDown'
|
|
||||||
| 'arrowLeft'
|
|
||||||
| 'arrowRight'
|
|
||||||
| 'arrowUp'
|
|
||||||
| 'checkmark'
|
|
||||||
| 'clipboardPlus'
|
|
||||||
| 'clipboardCheckmark'
|
|
||||||
| 'close'
|
|
||||||
| 'equal'
|
|
||||||
| 'exportFile'
|
|
||||||
| 'extrude'
|
|
||||||
| 'file'
|
|
||||||
| 'filePlus'
|
|
||||||
| 'folder'
|
|
||||||
| 'folderPlus'
|
|
||||||
| 'gear'
|
|
||||||
| 'horizontal'
|
|
||||||
| 'horizontalDash'
|
|
||||||
| 'kcl'
|
|
||||||
| 'line'
|
|
||||||
| 'make-variable'
|
|
||||||
| 'move'
|
|
||||||
| 'network'
|
|
||||||
| 'networkCrossedOut'
|
|
||||||
| 'parallel'
|
|
||||||
| 'person'
|
|
||||||
| 'plus'
|
|
||||||
| 'refresh'
|
|
||||||
| 'search'
|
|
||||||
| 'settings'
|
|
||||||
| 'sketch'
|
|
||||||
| 'three-dots'
|
|
||||||
| 'vertical'
|
|
||||||
|
|
||||||
export const CustomIcon = ({
|
const CustomIconMap = {
|
||||||
name,
|
arc: (
|
||||||
...props
|
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
}: {
|
|
||||||
name: CustomIconName
|
|
||||||
} & React.SVGProps<SVGSVGElement>) => {
|
|
||||||
switch (name) {
|
|
||||||
case 'arc':
|
|
||||||
return (
|
|
||||||
<svg
|
|
||||||
{...props}
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
fillRule="evenodd"
|
fillRule="evenodd"
|
||||||
clipRule="evenodd"
|
clipRule="evenodd"
|
||||||
@ -56,15 +10,9 @@ export const CustomIcon = ({
|
|||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
)
|
),
|
||||||
case 'arrowDown':
|
arrowDown: (
|
||||||
return (
|
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<svg
|
|
||||||
{...props}
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
fillRule="evenodd"
|
fillRule="evenodd"
|
||||||
clipRule="evenodd"
|
clipRule="evenodd"
|
||||||
@ -72,15 +20,9 @@ export const CustomIcon = ({
|
|||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
)
|
),
|
||||||
case 'arrowLeft':
|
arrowLeft: (
|
||||||
return (
|
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<svg
|
|
||||||
{...props}
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
fillRule="evenodd"
|
fillRule="evenodd"
|
||||||
clipRule="evenodd"
|
clipRule="evenodd"
|
||||||
@ -88,15 +30,9 @@ export const CustomIcon = ({
|
|||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
)
|
),
|
||||||
case 'arrowRight':
|
arrowRight: (
|
||||||
return (
|
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<svg
|
|
||||||
{...props}
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
fillRule="evenodd"
|
fillRule="evenodd"
|
||||||
clipRule="evenodd"
|
clipRule="evenodd"
|
||||||
@ -104,15 +40,9 @@ export const CustomIcon = ({
|
|||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
)
|
),
|
||||||
case 'arrowUp':
|
arrowUp: (
|
||||||
return (
|
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<svg
|
|
||||||
{...props}
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
fillRule="evenodd"
|
fillRule="evenodd"
|
||||||
clipRule="evenodd"
|
clipRule="evenodd"
|
||||||
@ -120,15 +50,9 @@ export const CustomIcon = ({
|
|||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
)
|
),
|
||||||
case 'checkmark':
|
checkmark: (
|
||||||
return (
|
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<svg
|
|
||||||
{...props}
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
fillRule="evenodd"
|
fillRule="evenodd"
|
||||||
clipRule="evenodd"
|
clipRule="evenodd"
|
||||||
@ -136,15 +60,9 @@ export const CustomIcon = ({
|
|||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
)
|
),
|
||||||
case 'clipboardCheckmark':
|
clipboardCheckmark: (
|
||||||
return (
|
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<svg
|
|
||||||
{...props}
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
fillRule="evenodd"
|
fillRule="evenodd"
|
||||||
clipRule="evenodd"
|
clipRule="evenodd"
|
||||||
@ -152,15 +70,9 @@ export const CustomIcon = ({
|
|||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
)
|
),
|
||||||
case 'clipboardPlus':
|
clipboardPlus: (
|
||||||
return (
|
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<svg
|
|
||||||
{...props}
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
fillRule="evenodd"
|
fillRule="evenodd"
|
||||||
clipRule="evenodd"
|
clipRule="evenodd"
|
||||||
@ -168,15 +80,9 @@ export const CustomIcon = ({
|
|||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
)
|
),
|
||||||
case 'close':
|
close: (
|
||||||
return (
|
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<svg
|
|
||||||
{...props}
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
fillRule="evenodd"
|
fillRule="evenodd"
|
||||||
clipRule="evenodd"
|
clipRule="evenodd"
|
||||||
@ -184,29 +90,17 @@ export const CustomIcon = ({
|
|||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
)
|
),
|
||||||
case 'equal':
|
equal: (
|
||||||
return (
|
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<svg
|
|
||||||
{...props}
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
d="M5 8.78V7H14.52V8.78H5ZM5 13.02V11.24H14.52V13.02H5Z"
|
d="M5 8.78V7H14.52V8.78H5ZM5 13.02V11.24H14.52V13.02H5Z"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
)
|
),
|
||||||
case 'exportFile':
|
exportFile: (
|
||||||
return (
|
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<svg
|
|
||||||
{...props}
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
fillRule="evenodd"
|
fillRule="evenodd"
|
||||||
clipRule="evenodd"
|
clipRule="evenodd"
|
||||||
@ -214,15 +108,9 @@ export const CustomIcon = ({
|
|||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
)
|
),
|
||||||
case 'extrude':
|
extrude: (
|
||||||
return (
|
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<svg
|
|
||||||
{...props}
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
fillRule="evenodd"
|
fillRule="evenodd"
|
||||||
clipRule="evenodd"
|
clipRule="evenodd"
|
||||||
@ -230,15 +118,9 @@ export const CustomIcon = ({
|
|||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
)
|
),
|
||||||
case 'file':
|
file: (
|
||||||
return (
|
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<svg
|
|
||||||
{...props}
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
fillRule="evenodd"
|
fillRule="evenodd"
|
||||||
clipRule="evenodd"
|
clipRule="evenodd"
|
||||||
@ -246,15 +128,9 @@ export const CustomIcon = ({
|
|||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
)
|
),
|
||||||
case 'filePlus':
|
filePlus: (
|
||||||
return (
|
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<svg
|
|
||||||
{...props}
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
fillRule="evenodd"
|
fillRule="evenodd"
|
||||||
clipRule="evenodd"
|
clipRule="evenodd"
|
||||||
@ -262,15 +138,9 @@ export const CustomIcon = ({
|
|||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
)
|
),
|
||||||
case 'folder':
|
folder: (
|
||||||
return (
|
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<svg
|
|
||||||
{...props}
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
fillRule="evenodd"
|
fillRule="evenodd"
|
||||||
clipRule="evenodd"
|
clipRule="evenodd"
|
||||||
@ -278,15 +148,9 @@ export const CustomIcon = ({
|
|||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
)
|
),
|
||||||
case 'folderPlus':
|
folderPlus: (
|
||||||
return (
|
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<svg
|
|
||||||
{...props}
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
fillRule="evenodd"
|
fillRule="evenodd"
|
||||||
clipRule="evenodd"
|
clipRule="evenodd"
|
||||||
@ -294,15 +158,9 @@ export const CustomIcon = ({
|
|||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
)
|
),
|
||||||
case 'gear':
|
gear: (
|
||||||
return (
|
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<svg
|
|
||||||
{...props}
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
fillRule="evenodd"
|
fillRule="evenodd"
|
||||||
clipRule="evenodd"
|
clipRule="evenodd"
|
||||||
@ -310,15 +168,9 @@ export const CustomIcon = ({
|
|||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
)
|
),
|
||||||
case 'horizontal':
|
horizontal: (
|
||||||
return (
|
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<svg
|
|
||||||
{...props}
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
fillRule="evenodd"
|
fillRule="evenodd"
|
||||||
clipRule="evenodd"
|
clipRule="evenodd"
|
||||||
@ -326,15 +178,9 @@ export const CustomIcon = ({
|
|||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
)
|
),
|
||||||
case 'horizontalDash':
|
horizontalDash: (
|
||||||
return (
|
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<svg
|
|
||||||
{...props}
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
fillRule="evenodd"
|
fillRule="evenodd"
|
||||||
clipRule="evenodd"
|
clipRule="evenodd"
|
||||||
@ -342,15 +188,9 @@ export const CustomIcon = ({
|
|||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
)
|
),
|
||||||
case 'kcl':
|
kcl: (
|
||||||
return (
|
<svg viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<svg
|
|
||||||
{...props}
|
|
||||||
viewBox="0 0 40 40"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
fillRule="evenodd"
|
fillRule="evenodd"
|
||||||
clipRule="evenodd"
|
clipRule="evenodd"
|
||||||
@ -358,15 +198,9 @@ export const CustomIcon = ({
|
|||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
)
|
),
|
||||||
case 'line':
|
line: (
|
||||||
return (
|
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<svg
|
|
||||||
{...props}
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
fillRule="evenodd"
|
fillRule="evenodd"
|
||||||
clipRule="evenodd"
|
clipRule="evenodd"
|
||||||
@ -374,15 +208,9 @@ export const CustomIcon = ({
|
|||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
)
|
),
|
||||||
case 'make-variable':
|
'make-variable': (
|
||||||
return (
|
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<svg
|
|
||||||
{...props}
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
fillRule="evenodd"
|
fillRule="evenodd"
|
||||||
clipRule="evenodd"
|
clipRule="evenodd"
|
||||||
@ -390,15 +218,9 @@ export const CustomIcon = ({
|
|||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
)
|
),
|
||||||
case 'move':
|
move: (
|
||||||
return (
|
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<svg
|
|
||||||
{...props}
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
fillRule="evenodd"
|
fillRule="evenodd"
|
||||||
clipRule="evenodd"
|
clipRule="evenodd"
|
||||||
@ -406,15 +228,9 @@ export const CustomIcon = ({
|
|||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
)
|
),
|
||||||
case 'network':
|
network: (
|
||||||
return (
|
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<svg
|
|
||||||
{...props}
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
fillRule="evenodd"
|
fillRule="evenodd"
|
||||||
clipRule="evenodd"
|
clipRule="evenodd"
|
||||||
@ -422,15 +238,9 @@ export const CustomIcon = ({
|
|||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
)
|
),
|
||||||
case 'networkCrossedOut':
|
networkCrossedOut: (
|
||||||
return (
|
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<svg
|
|
||||||
{...props}
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
fillRule="evenodd"
|
fillRule="evenodd"
|
||||||
clipRule="evenodd"
|
clipRule="evenodd"
|
||||||
@ -438,15 +248,9 @@ export const CustomIcon = ({
|
|||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
)
|
),
|
||||||
case 'parallel':
|
parallel: (
|
||||||
return (
|
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<svg
|
|
||||||
{...props}
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
fillRule="evenodd"
|
fillRule="evenodd"
|
||||||
clipRule="evenodd"
|
clipRule="evenodd"
|
||||||
@ -454,15 +258,9 @@ export const CustomIcon = ({
|
|||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
)
|
),
|
||||||
case 'person':
|
person: (
|
||||||
return (
|
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<svg
|
|
||||||
{...props}
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
fillRule="evenodd"
|
fillRule="evenodd"
|
||||||
clipRule="evenodd"
|
clipRule="evenodd"
|
||||||
@ -470,15 +268,9 @@ export const CustomIcon = ({
|
|||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
)
|
),
|
||||||
case 'plus':
|
plus: (
|
||||||
return (
|
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<svg
|
|
||||||
{...props}
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
fillRule="evenodd"
|
fillRule="evenodd"
|
||||||
clipRule="evenodd"
|
clipRule="evenodd"
|
||||||
@ -486,15 +278,9 @@ export const CustomIcon = ({
|
|||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
)
|
),
|
||||||
case 'refresh':
|
refresh: (
|
||||||
return (
|
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<svg
|
|
||||||
{...props}
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
fillRule="evenodd"
|
fillRule="evenodd"
|
||||||
clipRule="evenodd"
|
clipRule="evenodd"
|
||||||
@ -502,15 +288,9 @@ export const CustomIcon = ({
|
|||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
)
|
),
|
||||||
case 'search':
|
search: (
|
||||||
return (
|
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<svg
|
|
||||||
{...props}
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
fillRule="evenodd"
|
fillRule="evenodd"
|
||||||
clipRule="evenodd"
|
clipRule="evenodd"
|
||||||
@ -518,15 +298,9 @@ export const CustomIcon = ({
|
|||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
)
|
),
|
||||||
case 'settings':
|
settings: (
|
||||||
return (
|
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<svg
|
|
||||||
{...props}
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
fillRule="evenodd"
|
fillRule="evenodd"
|
||||||
clipRule="evenodd"
|
clipRule="evenodd"
|
||||||
@ -534,15 +308,9 @@ export const CustomIcon = ({
|
|||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
)
|
),
|
||||||
case 'sketch':
|
sketch: (
|
||||||
return (
|
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<svg
|
|
||||||
{...props}
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
fillRule="evenodd"
|
fillRule="evenodd"
|
||||||
clipRule="evenodd"
|
clipRule="evenodd"
|
||||||
@ -550,15 +318,9 @@ export const CustomIcon = ({
|
|||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
)
|
),
|
||||||
case 'three-dots':
|
'three-dots': (
|
||||||
return (
|
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<svg
|
|
||||||
{...props}
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
fillRule="evenodd"
|
fillRule="evenodd"
|
||||||
clipRule="evenodd"
|
clipRule="evenodd"
|
||||||
@ -566,15 +328,9 @@ export const CustomIcon = ({
|
|||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
)
|
),
|
||||||
case 'vertical':
|
vertical: (
|
||||||
return (
|
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<svg
|
|
||||||
{...props}
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
fillRule="evenodd"
|
fillRule="evenodd"
|
||||||
clipRule="evenodd"
|
clipRule="evenodd"
|
||||||
@ -582,6 +338,13 @@ export const CustomIcon = ({
|
|||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
)
|
),
|
||||||
}
|
} as const
|
||||||
}
|
|
||||||
|
export type CustomIconName = keyof typeof CustomIconMap
|
||||||
|
|
||||||
|
export const CustomIcon = ({
|
||||||
|
name,
|
||||||
|
...props
|
||||||
|
}: { name: CustomIconName } & React.SVGProps<SVGSVGElement>) =>
|
||||||
|
cloneElement(CustomIconMap[name], props)
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import { Dialog } from '@headlessui/react'
|
import { Dialog } from '@headlessui/react'
|
||||||
import { useStore } from '../useStore'
|
|
||||||
import { ActionButton } from './ActionButton'
|
import { ActionButton } from './ActionButton'
|
||||||
|
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||||
|
import { useState } from 'react'
|
||||||
|
|
||||||
const DownloadAppBanner = () => {
|
const DownloadAppBanner = () => {
|
||||||
const { isBannerDismissed, setBannerDismissed } = useStore((s) => ({
|
const { settings } = useSettingsAuthContext()
|
||||||
isBannerDismissed: s.isBannerDismissed,
|
const [isBannerDismissed, setIsBannerDismissed] = useState(
|
||||||
setBannerDismissed: s.setBannerDismissed,
|
settings.context.app.dismissWebBanner.current
|
||||||
}))
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
@ -23,7 +24,7 @@ const DownloadAppBanner = () => {
|
|||||||
</h2>
|
</h2>
|
||||||
<ActionButton
|
<ActionButton
|
||||||
Element="button"
|
Element="button"
|
||||||
onClick={() => setBannerDismissed(true)}
|
onClick={() => setIsBannerDismissed(true)}
|
||||||
icon={{
|
icon={{
|
||||||
icon: 'close',
|
icon: 'close',
|
||||||
className: 'p-1',
|
className: 'p-1',
|
||||||
@ -51,6 +52,24 @@ const DownloadAppBanner = () => {
|
|||||||
</a>{' '}
|
</a>{' '}
|
||||||
to download the app for the best experience.
|
to download the app for the best experience.
|
||||||
</p>
|
</p>
|
||||||
|
<p className="mt-6">
|
||||||
|
If you're on Linux and the browser is your only way to use the app,
|
||||||
|
you can permanently dismiss this banner by{' '}
|
||||||
|
<a
|
||||||
|
onClick={() => {
|
||||||
|
setIsBannerDismissed(true)
|
||||||
|
settings.send({
|
||||||
|
type: 'set.app.dismissWebBanner',
|
||||||
|
data: { level: 'user', value: true },
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
href="/"
|
||||||
|
className="!text-warn-80 dark:!text-warn-80 dark:hover:!text-warn-70 underline"
|
||||||
|
>
|
||||||
|
toggling the App > Dismiss Web Banner setting
|
||||||
|
</a>
|
||||||
|
.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</Dialog.Panel>
|
</Dialog.Panel>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
@ -14,16 +14,10 @@ import {
|
|||||||
} from 'xstate'
|
} from 'xstate'
|
||||||
import { useCommandsContext } from 'hooks/useCommandsContext'
|
import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||||
import { fileMachine } from 'machines/fileMachine'
|
import { fileMachine } from 'machines/fileMachine'
|
||||||
import {
|
import { mkdir, remove, rename, create } from '@tauri-apps/plugin-fs'
|
||||||
createDir,
|
|
||||||
removeDir,
|
|
||||||
removeFile,
|
|
||||||
renameFile,
|
|
||||||
writeFile,
|
|
||||||
} from '@tauri-apps/api/fs'
|
|
||||||
import { readProject } from 'lib/tauriFS'
|
import { readProject } from 'lib/tauriFS'
|
||||||
import { isTauri } from 'lib/isTauri'
|
import { isTauri } from 'lib/isTauri'
|
||||||
import { sep } from '@tauri-apps/api/path'
|
import { join, sep } from '@tauri-apps/api/path'
|
||||||
import { DEFAULT_FILE_NAME, FILE_EXT } from 'lib/constants'
|
import { DEFAULT_FILE_NAME, FILE_EXT } from 'lib/constants'
|
||||||
|
|
||||||
type MachineContext<T extends AnyStateMachine> = {
|
type MachineContext<T extends AnyStateMachine> = {
|
||||||
@ -56,7 +50,7 @@ export const FileMachineProvider = ({
|
|||||||
commandBarSend({ type: 'Close' })
|
commandBarSend({ type: 'Close' })
|
||||||
navigate(
|
navigate(
|
||||||
`${paths.FILE}/${encodeURIComponent(
|
`${paths.FILE}/${encodeURIComponent(
|
||||||
context.selectedDirectory + sep + event.data.name
|
context.selectedDirectory + sep() + event.data.name
|
||||||
)}`
|
)}`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -79,14 +73,13 @@ export const FileMachineProvider = ({
|
|||||||
let name = event.data.name.trim() || DEFAULT_FILE_NAME
|
let name = event.data.name.trim() || DEFAULT_FILE_NAME
|
||||||
|
|
||||||
if (event.data.makeDir) {
|
if (event.data.makeDir) {
|
||||||
await createDir(context.selectedDirectory.path + sep + name)
|
await mkdir(await join(context.selectedDirectory.path, name))
|
||||||
} else {
|
} else {
|
||||||
await writeFile(
|
await create(
|
||||||
context.selectedDirectory.path +
|
context.selectedDirectory.path +
|
||||||
sep +
|
sep() +
|
||||||
name +
|
name +
|
||||||
(name.endsWith(FILE_EXT) ? '' : FILE_EXT),
|
(name.endsWith(FILE_EXT) ? '' : FILE_EXT)
|
||||||
''
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,12 +92,11 @@ export const FileMachineProvider = ({
|
|||||||
const { oldName, newName, isDir } = event.data
|
const { oldName, newName, isDir } = event.data
|
||||||
let name = newName ? newName : DEFAULT_FILE_NAME
|
let name = newName ? newName : DEFAULT_FILE_NAME
|
||||||
|
|
||||||
await renameFile(
|
await rename(
|
||||||
context.selectedDirectory.path + sep + oldName,
|
await join(context.selectedDirectory.path, oldName),
|
||||||
context.selectedDirectory.path +
|
(await join(context.selectedDirectory.path, name)) +
|
||||||
sep +
|
(name.endsWith(FILE_EXT) || isDir ? '' : FILE_EXT),
|
||||||
name +
|
{}
|
||||||
(name.endsWith(FILE_EXT) || isDir ? '' : FILE_EXT)
|
|
||||||
)
|
)
|
||||||
return (
|
return (
|
||||||
oldName !== name && `Successfully renamed "${oldName}" to "${name}"`
|
oldName !== name && `Successfully renamed "${oldName}" to "${name}"`
|
||||||
@ -117,11 +109,11 @@ export const FileMachineProvider = ({
|
|||||||
const isDir = !!event.data.children
|
const isDir = !!event.data.children
|
||||||
|
|
||||||
if (isDir) {
|
if (isDir) {
|
||||||
await removeDir(event.data.path, {
|
await remove(event.data.path, {
|
||||||
recursive: true,
|
recursive: true,
|
||||||
}).catch((e) => console.error('Error deleting directory', e))
|
}).catch((e) => console.error('Error deleting directory', e))
|
||||||
} else {
|
} else {
|
||||||
await removeFile(event.data.path).catch((e) =>
|
await remove(event.data.path).catch((e) =>
|
||||||
console.error('Error deleting file', e)
|
console.error('Error deleting file', e)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import { type IndexLoaderData } from 'lib/types'
|
import type { FileEntry, IndexLoaderData } from 'lib/types'
|
||||||
import { paths } from 'lib/paths'
|
import { paths } from 'lib/paths'
|
||||||
import { ActionButton } from './ActionButton'
|
import { ActionButton } from './ActionButton'
|
||||||
import Tooltip from './Tooltip'
|
import Tooltip from './Tooltip'
|
||||||
import { FileEntry } from '@tauri-apps/api/fs'
|
|
||||||
import { Dispatch, useEffect, useRef, useState } from 'react'
|
import { Dispatch, useEffect, useRef, useState } from 'react'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import { Dialog, Disclosure } from '@headlessui/react'
|
import { Dialog, Disclosure } from '@headlessui/react'
|
||||||
@ -192,8 +191,8 @@ const FileTreeItem = ({
|
|||||||
{fileOrDir.children === undefined ? (
|
{fileOrDir.children === undefined ? (
|
||||||
<li
|
<li
|
||||||
className={
|
className={
|
||||||
'group m-0 p-0 border-solid border-0 text-energy-100 hover:text-energy-70 hover:bg-energy-10/50 dark:text-energy-30 dark:hover:!text-energy-20 dark:hover:bg-energy-90/50 focus-within:bg-energy-10/80 dark:focus-within:bg-energy-80/50 hover:focus-within:bg-energy-10/80 dark:hover:focus-within:bg-energy-80/50 ' +
|
'group m-0 p-0 border-solid border-0 hover:text-primary hover:bg-primary/5 focus-within:bg-primary/5 ' +
|
||||||
(isCurrentFile ? 'bg-energy-10/50 dark:bg-energy-90/50' : '')
|
(isCurrentFile ? '!bg-primary/10 !text-primary' : '')
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{!isRenaming ? (
|
{!isRenaming ? (
|
||||||
@ -206,12 +205,7 @@ const FileTreeItem = ({
|
|||||||
>
|
>
|
||||||
<CustomIcon
|
<CustomIcon
|
||||||
name={fileOrDir.name?.endsWith(FILE_EXT) ? 'kcl' : 'file'}
|
name={fileOrDir.name?.endsWith(FILE_EXT) ? 'kcl' : 'file'}
|
||||||
className={
|
className="inline-block w-3 text-current"
|
||||||
'inline-block w-3 ' +
|
|
||||||
(isCurrentFile
|
|
||||||
? 'text-energy-90 dark:text-energy-10'
|
|
||||||
: 'text-energy-50 dark:text-energy-50')
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
{fileOrDir.name}
|
{fileOrDir.name}
|
||||||
</button>
|
</button>
|
||||||
@ -230,9 +224,9 @@ const FileTreeItem = ({
|
|||||||
{!isRenaming ? (
|
{!isRenaming ? (
|
||||||
<Disclosure.Button
|
<Disclosure.Button
|
||||||
className={
|
className={
|
||||||
' group border-none text-sm rounded-none p-0 m-0 flex items-center justify-start w-full py-0.5 text-chalkboard-70 dark:text-chalkboard-30 hover:bg-energy-10/50 dark:hover:bg-energy-90/50' +
|
' group border-none text-sm rounded-none p-0 m-0 flex items-center justify-start w-full py-0.5 hover:text-primary hover:bg-primary/5' +
|
||||||
(context.selectedDirectory.path.includes(fileOrDir.path)
|
(context.selectedDirectory.path.includes(fileOrDir.path)
|
||||||
? ' group-focus-within:bg-chalkboard-20/50 dark:group-focus-within:bg-chalkboard-80/20 hover:group-focus-within:bg-chalkboard-20 dark:hover:group-focus-within:bg-chalkboard-80/20 group-active:bg-chalkboard-20/50 dark:group-active:bg-chalkboard-80/20 hover:group-active:bg-chalkboard-20/50 dark:hover:group-active:bg-chalkboard-80/20'
|
? ' ui-open:text-primary'
|
||||||
: '')
|
: '')
|
||||||
}
|
}
|
||||||
style={{ paddingInlineStart: getIndentationCSS(level) }}
|
style={{ paddingInlineStart: getIndentationCSS(level) }}
|
||||||
@ -353,17 +347,16 @@ export const FileTree = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={className}>
|
<div className={className}>
|
||||||
<div className="flex items-center gap-1 px-4 py-1 bg-chalkboard-20/50 dark:bg-chalkboard-80/50 border-b border-b-chalkboard-30 dark:border-b-chalkboard-80">
|
<div className="flex items-center gap-1 px-4 py-1 bg-chalkboard-20/40 dark:bg-chalkboard-80/50 border-b border-b-chalkboard-30 dark:border-b-chalkboard-80">
|
||||||
<h2 className="flex-1 m-0 p-0 text-sm mono">Files</h2>
|
<h2 className="flex-1 m-0 p-0 text-sm mono">Files</h2>
|
||||||
<ActionButton
|
<ActionButton
|
||||||
Element="button"
|
Element="button"
|
||||||
icon={{
|
icon={{
|
||||||
icon: 'filePlus',
|
icon: 'filePlus',
|
||||||
iconClassName: '!text-energy-80 dark:!text-energy-20',
|
iconClassName: '!text-current',
|
||||||
bgClassName:
|
bgClassName: 'bg-transparent',
|
||||||
'bg-chalkboard-20/50 hover:bg-energy-10/50 dark:hover:bg-transparent',
|
|
||||||
}}
|
}}
|
||||||
className="!p-0 bg-transparent !outline-none"
|
className="!p-0 !bg-transparent hover:text-primary border-transparent hover:border-primary !outline-none"
|
||||||
onClick={createFile}
|
onClick={createFile}
|
||||||
>
|
>
|
||||||
<Tooltip position="inlineStart" delay={750}>
|
<Tooltip position="inlineStart" delay={750}>
|
||||||
@ -375,11 +368,10 @@ export const FileTree = ({
|
|||||||
Element="button"
|
Element="button"
|
||||||
icon={{
|
icon={{
|
||||||
icon: 'folderPlus',
|
icon: 'folderPlus',
|
||||||
iconClassName: '!text-energy-80 dark:!text-energy-20',
|
iconClassName: '!text-current',
|
||||||
bgClassName:
|
bgClassName: 'bg-transparent',
|
||||||
'bg-chalkboard-20/50 hover:bg-energy-10/50 dark:hover:bg-transparent',
|
|
||||||
}}
|
}}
|
||||||
className="!p-0 bg-transparent !outline-none"
|
className="!p-0 !bg-transparent hover:text-primary border-transparent hover:border-primary !outline-none"
|
||||||
onClick={createFolder}
|
onClick={createFolder}
|
||||||
>
|
>
|
||||||
<Tooltip position="inlineStart" delay={750}>
|
<Tooltip position="inlineStart" delay={750}>
|
||||||
|
@ -15,23 +15,20 @@ const Loading = ({ children }: React.PropsWithChildren) => {
|
|||||||
data-testid="loading"
|
data-testid="loading"
|
||||||
>
|
>
|
||||||
<svg viewBox="0 0 10 10" className="w-8 h-8">
|
<svg viewBox="0 0 10 10" className="w-8 h-8">
|
||||||
<circle cx="5" cy="5" r="4" stroke="var(--energy-50)" fill="none" />
|
|
||||||
<circle
|
<circle
|
||||||
cx="5"
|
cx="5"
|
||||||
cy="5"
|
cy="5"
|
||||||
r="4"
|
r="4"
|
||||||
stroke="var(--energy-10)"
|
stroke="var(--primary)"
|
||||||
fill="none"
|
fill="none"
|
||||||
strokeDasharray="4, 4"
|
strokeDasharray="4, 4"
|
||||||
className="animate-spin origin-center"
|
className="animate-spin origin-center"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
<p className="text-base mt-4 text-energy-80 dark:text-energy-30">
|
<p className="text-base mt-4 text-primary">{children || 'Loading'}</p>
|
||||||
{children || 'Loading'}
|
|
||||||
</p>
|
|
||||||
<p
|
<p
|
||||||
className={
|
className={
|
||||||
'text-sm mt-4 text-energy-70 dark:text-energy-50 transition-opacity duration-500' +
|
'text-sm mt-4 text-primary/60 transition-opacity duration-500' +
|
||||||
(hasLongLoadTime ? ' opacity-100' : ' opacity-0')
|
(hasLongLoadTime ? ' opacity-100' : ' opacity-0')
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
@ -13,7 +13,7 @@ import { Extension } from '@codemirror/state'
|
|||||||
import { LanguageSupport } from '@codemirror/language'
|
import { LanguageSupport } from '@codemirror/language'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import { paths } from 'lib/paths'
|
import { paths } from 'lib/paths'
|
||||||
import { FileEntry } from '@tauri-apps/api/fs'
|
import { FileEntry } from 'lib/types'
|
||||||
|
|
||||||
const DEFAULT_FILE_NAME: string = 'main.kcl'
|
const DEFAULT_FILE_NAME: string = 'main.kcl'
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ const hasIssueToIconColors: Record<string | number | symbol, IconColorConfig> =
|
|||||||
bg: 'bg-chalkboard-30 dark:bg-chalkboard-70',
|
bg: 'bg-chalkboard-30 dark:bg-chalkboard-70',
|
||||||
},
|
},
|
||||||
false: {
|
false: {
|
||||||
icon: 'text-chalkboard-110 dark:!text-chalkboard-10',
|
icon: '!text-chalkboard-110 dark:!text-chalkboard-10',
|
||||||
bg: 'bg-transparent dark:bg-transparent',
|
bg: 'bg-transparent dark:bg-transparent',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -58,8 +58,8 @@ const hasIssueToIconColors: Record<string | number | symbol, IconColorConfig> =
|
|||||||
const overallConnectionStateColor: Record<NetworkHealthState, IconColorConfig> =
|
const overallConnectionStateColor: Record<NetworkHealthState, IconColorConfig> =
|
||||||
{
|
{
|
||||||
[NetworkHealthState.Ok]: {
|
[NetworkHealthState.Ok]: {
|
||||||
icon: 'text-energy-80 dark:text-energy-10',
|
icon: 'text-succeed-80 dark:text-succeed-10',
|
||||||
bg: 'bg-energy-10/30 dark:bg-energy-80/50',
|
bg: 'bg-succeed-10/30 dark:bg-succeed-80/50',
|
||||||
},
|
},
|
||||||
[NetworkHealthState.Issue]: {
|
[NetworkHealthState.Issue]: {
|
||||||
icon: 'text-destroy-80 dark:text-destroy-10',
|
icon: 'text-destroy-80 dark:text-destroy-10',
|
||||||
@ -214,7 +214,7 @@ export const NetworkHealthIndicator = () => {
|
|||||||
'p-0 border-none bg-transparent dark:bg-transparent relative ' +
|
'p-0 border-none bg-transparent dark:bg-transparent relative ' +
|
||||||
(hasIssues
|
(hasIssues
|
||||||
? 'focus-visible:outline-destroy-80'
|
? 'focus-visible:outline-destroy-80'
|
||||||
: 'focus-visible:outline-energy-80')
|
: 'focus-visible:outline-succeed-80')
|
||||||
}
|
}
|
||||||
data-testid="network-toggle"
|
data-testid="network-toggle"
|
||||||
>
|
>
|
||||||
@ -227,7 +227,7 @@ export const NetworkHealthIndicator = () => {
|
|||||||
'rounded-sm ' + overallConnectionStateColor[overallState].bg
|
'rounded-sm ' + overallConnectionStateColor[overallState].bg
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Tooltip position="blockEnd" delay={750} className="ui-open:hidden">
|
<Tooltip position="left" delay={750} className="ui-open:hidden">
|
||||||
Network Health ({NETWORK_HEALTH_TEXT[overallState]})
|
Network Health ({NETWORK_HEALTH_TEXT[overallState]})
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Popover.Button>
|
</Popover.Button>
|
||||||
|
@ -13,6 +13,7 @@ import { getPartsCount, readProject } from '../lib/tauriFS'
|
|||||||
import { FILE_EXT } from 'lib/constants'
|
import { FILE_EXT } from 'lib/constants'
|
||||||
import { Dialog } from '@headlessui/react'
|
import { Dialog } from '@headlessui/react'
|
||||||
import { useHotkeys } from 'react-hotkeys-hook'
|
import { useHotkeys } from 'react-hotkeys-hook'
|
||||||
|
import Tooltip from './Tooltip'
|
||||||
|
|
||||||
function ProjectCard({
|
function ProjectCard({
|
||||||
project,
|
project,
|
||||||
@ -64,17 +65,17 @@ function ProjectCard({
|
|||||||
inputRef.current.focus()
|
inputRef.current.focus()
|
||||||
inputRef.current.select()
|
inputRef.current.select()
|
||||||
}
|
}
|
||||||
}, [inputRef])
|
}, [inputRef.current])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li
|
<li
|
||||||
{...props}
|
{...props}
|
||||||
className="group relative min-h-[5em] p-1 rounded-sm border border-chalkboard-20 dark:border-chalkboard-90 hover:border-energy-10 dark:hover:border-chalkboard-70 hover:bg-energy-10/20 dark:hover:bg-chalkboard-90"
|
className="group relative min-h-[5em] p-1 rounded-sm border border-chalkboard-20 dark:border-chalkboard-80 hover:!border-primary"
|
||||||
>
|
>
|
||||||
{isEditing ? (
|
{isEditing ? (
|
||||||
<form onSubmit={handleSave} className="flex gap-2 items-center">
|
<form onSubmit={handleSave} className="flex gap-2 items-center">
|
||||||
<input
|
<input
|
||||||
className="dark:bg-chalkboard-80 dark:border-chalkboard-40 min-w-0 p-1 selection:bg-energy-10/20 focus:outline-none"
|
className="dark:bg-chalkboard-80 dark:border-chalkboard-40 min-w-0 p-1 focus:outline-none"
|
||||||
type="text"
|
type="text"
|
||||||
id="newProjectName"
|
id="newProjectName"
|
||||||
name="newProjectName"
|
name="newProjectName"
|
||||||
@ -87,27 +88,41 @@ function ProjectCard({
|
|||||||
<ActionButton
|
<ActionButton
|
||||||
Element="button"
|
Element="button"
|
||||||
type="submit"
|
type="submit"
|
||||||
icon={{ icon: faCheck, size: 'sm', className: 'p-1' }}
|
icon={{
|
||||||
|
icon: faCheck,
|
||||||
|
size: 'sm',
|
||||||
|
className: 'p-1',
|
||||||
|
bgClassName: '!bg-transparent',
|
||||||
|
}}
|
||||||
className="!p-0"
|
className="!p-0"
|
||||||
></ActionButton>
|
>
|
||||||
|
<Tooltip position="left" delay={1000}>
|
||||||
|
Rename project
|
||||||
|
</Tooltip>
|
||||||
|
</ActionButton>
|
||||||
<ActionButton
|
<ActionButton
|
||||||
Element="button"
|
Element="button"
|
||||||
icon={{
|
icon={{
|
||||||
icon: faX,
|
icon: faX,
|
||||||
size: 'sm',
|
size: 'sm',
|
||||||
iconClassName: 'dark:!text-chalkboard-20',
|
iconClassName: 'dark:!text-chalkboard-20',
|
||||||
|
bgClassName: '!bg-transparent',
|
||||||
className: 'p-1',
|
className: 'p-1',
|
||||||
}}
|
}}
|
||||||
className="!p-0"
|
className="!p-0"
|
||||||
onClick={() => setIsEditing(false)}
|
onClick={() => setIsEditing(false)}
|
||||||
/>
|
>
|
||||||
|
<Tooltip position="left" delay={1000}>
|
||||||
|
Cancel
|
||||||
|
</Tooltip>
|
||||||
|
</ActionButton>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<div className="p-1 flex flex-col h-full gap-2">
|
<div className="p-1 flex flex-col h-full gap-2">
|
||||||
<Link
|
<Link
|
||||||
className="flex-1 !no-underline text-liquid-100 after:content-[''] after:absolute after:inset-0"
|
className="flex-1 !no-underline !text-chalkboard-110 dark:!text-chalkboard-10 after:content-[''] after:absolute after:inset-0"
|
||||||
to={`${paths.FILE}/${encodeURIComponent(project.path)}`}
|
to={`${paths.FILE}/${encodeURIComponent(project.path)}`}
|
||||||
data-testid="project-link"
|
data-testid="project-link"
|
||||||
>
|
>
|
||||||
@ -121,7 +136,10 @@ function ProjectCard({
|
|||||||
}`}
|
}`}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-chalkboard-60 text-xs">
|
<span className="text-chalkboard-60 text-xs">
|
||||||
Edited {getDisplayedTime(project.entrypointMetadata.modifiedAt)}
|
Edited{' '}
|
||||||
|
{project.entrypointMetadata.mtime
|
||||||
|
? getDisplayedTime(project.entrypointMetadata.mtime)
|
||||||
|
: 'never'}
|
||||||
</span>
|
</span>
|
||||||
<div className="absolute z-10 bottom-2 right-2 flex gap-1 items-center opacity-0 group-hover:opacity-100 group-focus-within:opacity-100">
|
<div className="absolute z-10 bottom-2 right-2 flex gap-1 items-center opacity-0 group-hover:opacity-100 group-focus-within:opacity-100">
|
||||||
<ActionButton
|
<ActionButton
|
||||||
@ -130,6 +148,7 @@ function ProjectCard({
|
|||||||
icon: faPenAlt,
|
icon: faPenAlt,
|
||||||
className: 'p-1',
|
className: 'p-1',
|
||||||
iconClassName: 'dark:!text-chalkboard-20',
|
iconClassName: 'dark:!text-chalkboard-20',
|
||||||
|
bgClassName: '!bg-transparent',
|
||||||
size: 'xs',
|
size: 'xs',
|
||||||
}}
|
}}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
@ -138,15 +157,19 @@ function ProjectCard({
|
|||||||
setIsEditing(true)
|
setIsEditing(true)
|
||||||
}}
|
}}
|
||||||
className="!p-0"
|
className="!p-0"
|
||||||
/>
|
>
|
||||||
|
<Tooltip position="left" delay={1000}>
|
||||||
|
Rename project
|
||||||
|
</Tooltip>
|
||||||
|
</ActionButton>
|
||||||
<ActionButton
|
<ActionButton
|
||||||
Element="button"
|
Element="button"
|
||||||
icon={{
|
icon={{
|
||||||
icon: faTrashAlt,
|
icon: faTrashAlt,
|
||||||
className: 'p-1',
|
className: 'p-1',
|
||||||
size: 'xs',
|
size: 'xs',
|
||||||
bgClassName: 'bg-destroy-80',
|
bgClassName: '!bg-transparent',
|
||||||
iconClassName: '!text-destroy-20 dark:!text-destroy-40',
|
iconClassName: '!text-destroy-70',
|
||||||
}}
|
}}
|
||||||
className="!p-0 hover:border-destroy-40 dark:hover:border-destroy-40"
|
className="!p-0 hover:border-destroy-40 dark:hover:border-destroy-40"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
@ -154,7 +177,11 @@ function ProjectCard({
|
|||||||
e.nativeEvent.stopPropagation()
|
e.nativeEvent.stopPropagation()
|
||||||
setIsConfirmingDelete(true)
|
setIsConfirmingDelete(true)
|
||||||
}}
|
}}
|
||||||
/>
|
>
|
||||||
|
<Tooltip position="left" delay={1000}>
|
||||||
|
Delete project
|
||||||
|
</Tooltip>
|
||||||
|
</ActionButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Dialog
|
<Dialog
|
||||||
|
@ -17,23 +17,24 @@ const projectWellFormed = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
entrypointMetadata: {
|
entrypointMetadata: {
|
||||||
accessedAt: now,
|
atime: now,
|
||||||
blksize: 32,
|
blksize: 32,
|
||||||
blocks: 32,
|
blocks: 32,
|
||||||
createdAt: now,
|
birthtime: now,
|
||||||
dev: 1,
|
dev: 1,
|
||||||
gid: 1,
|
gid: 1,
|
||||||
ino: 1,
|
ino: 1,
|
||||||
isDir: false,
|
isDirectory: false,
|
||||||
isFile: true,
|
isFile: true,
|
||||||
isSymlink: false,
|
isSymlink: false,
|
||||||
mode: 1,
|
mode: 1,
|
||||||
modifiedAt: now,
|
mtime: now,
|
||||||
nlink: 1,
|
nlink: 1,
|
||||||
permissions: { readonly: false, mode: 1 },
|
readonly: false,
|
||||||
rdev: 1,
|
rdev: 1,
|
||||||
size: 32,
|
size: 32,
|
||||||
uid: 1,
|
uid: 1,
|
||||||
|
fileAttributes: null,
|
||||||
},
|
},
|
||||||
} satisfies ProjectWithEntryPointMetadata
|
} satisfies ProjectWithEntryPointMetadata
|
||||||
|
|
||||||
|
@ -25,15 +25,15 @@ const ProjectSidebarMenu = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const { onProjectClose } = useLspContext()
|
const { onProjectClose } = useLspContext()
|
||||||
return (
|
return (
|
||||||
<div className="rounded-sm !no-underline h-9 mr-auto max-h-min min-w-max border-0 py-1 px-2 flex items-center gap-2 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-energy-50 dark:hover:bg-chalkboard-90">
|
<div className="!no-underline h-full mr-auto max-h-min min-w-max flex items-center gap-2">
|
||||||
<Link
|
<Link
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onProjectClose(file || null, project?.path || null, false)
|
onProjectClose(file || null, project?.path || null, false)
|
||||||
}}
|
}}
|
||||||
to={paths.HOME}
|
to={paths.HOME}
|
||||||
className="group"
|
className="relative h-full grid place-content-center group p-1.5 before:block before:content-[''] before:absolute before:inset-0 before:bottom-2.5 before:z-[-1] before:bg-primary hover:before:brightness-110 before:rounded-b-sm"
|
||||||
>
|
>
|
||||||
<Logo className="w-auto h-5 text-chalkboard-120 dark:text-chalkboard-10 group-hover:text-energy-10" />
|
<Logo className="w-auto h-4 text-chalkboard-10" />
|
||||||
</Link>
|
</Link>
|
||||||
{renderAsLink ? (
|
{renderAsLink ? (
|
||||||
<>
|
<>
|
||||||
@ -67,20 +67,27 @@ function ProjectMenuPopover({
|
|||||||
project?: IndexLoaderData['project']
|
project?: IndexLoaderData['project']
|
||||||
file?: IndexLoaderData['file']
|
file?: IndexLoaderData['file']
|
||||||
}) {
|
}) {
|
||||||
const { commandBarSend } = useCommandsContext()
|
const { commandBarState, commandBarSend } = useCommandsContext()
|
||||||
const { onProjectClose } = useLspContext()
|
const { onProjectClose } = useLspContext()
|
||||||
|
const exportCommandInfo = { name: 'Export', ownerMachine: 'modeling' }
|
||||||
|
const findCommand = (obj: { name: string; ownerMachine: string }) =>
|
||||||
|
Boolean(
|
||||||
|
commandBarState.context.commands.find(
|
||||||
|
(c) => c.name === obj.name && c.ownerMachine === obj.ownerMachine
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Popover className="relative">
|
<Popover className="relative">
|
||||||
<Popover.Button
|
<Popover.Button
|
||||||
className="rounded-sm h-9 mr-auto max-h-min min-w-max border-0 py-1 pl-0 pr-2 flex items-center focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-energy-50 dark:hover:bg-chalkboard-90"
|
className="rounded-sm h-9 mr-auto max-h-min min-w-max border-0 py-1 pl-0 pr-2 flex items-center focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary dark:hover:bg-chalkboard-90"
|
||||||
data-testid="project-sidebar-toggle"
|
data-testid="project-sidebar-toggle"
|
||||||
>
|
>
|
||||||
<CustomIcon name="three-dots" className="w-5 h-5 rotate-90" />
|
<CustomIcon name="three-dots" className="w-5 h-5 rotate-90" />
|
||||||
<div className="flex flex-col items-start py-0.5">
|
<div className="flex flex-col items-start py-0.5">
|
||||||
<span className="hidden text-sm text-chalkboard-110 dark:text-chalkboard-20 whitespace-nowrap lg:block">
|
<span className="hidden text-sm text-chalkboard-110 dark:text-chalkboard-20 whitespace-nowrap lg:block">
|
||||||
{isTauri() && file?.name
|
{isTauri() && file?.name
|
||||||
? file.name.slice(file.name.lastIndexOf(sep) + 1)
|
? file.name.slice(file.name.lastIndexOf(sep()) + 1)
|
||||||
: APP_NAME}
|
: APP_NAME}
|
||||||
</span>
|
</span>
|
||||||
{isTauri() && project?.name && (
|
{isTauri() && project?.name && (
|
||||||
@ -119,19 +126,16 @@ function ProjectMenuPopover({
|
|||||||
<>
|
<>
|
||||||
<div className="flex items-center gap-4 px-4 py-3">
|
<div className="flex items-center gap-4 px-4 py-3">
|
||||||
<div>
|
<div>
|
||||||
<p
|
<p className="m-0 text-mono" data-testid="projectName">
|
||||||
className="m-0 text-chalkboard-100 dark:text-energy-10 text-mono"
|
|
||||||
data-testid="projectName"
|
|
||||||
>
|
|
||||||
{project?.name ? project.name : APP_NAME}
|
{project?.name ? project.name : APP_NAME}
|
||||||
</p>
|
</p>
|
||||||
{project?.entrypointMetadata && (
|
{project?.entrypointMetadata && (
|
||||||
<p
|
<p
|
||||||
className="m-0 text-xs text-chalkboard-100 dark:text-energy-40"
|
className="m-0 text-xs text-chalkboard-80 dark:text-chalkboard-40"
|
||||||
data-testid="createdAt"
|
data-testid="createdAt"
|
||||||
>
|
>
|
||||||
Created{' '}
|
Created{' '}
|
||||||
{project.entrypointMetadata.createdAt.toLocaleDateString()}
|
{project.entrypointMetadata.birthtime?.toLocaleDateString()}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@ -143,17 +147,27 @@ function ProjectMenuPopover({
|
|||||||
closePanel={close}
|
closePanel={close}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex-1 overflow-hidden" />
|
<div className="flex-1 p-4 text-sm overflow-hidden">
|
||||||
|
<p>
|
||||||
|
In the browser version of Modeling App you can only have one
|
||||||
|
part, and the code is stored in your browser's storage.
|
||||||
|
</p>
|
||||||
|
<p className="my-6">
|
||||||
|
Please save any code you want to keep more permanently, as
|
||||||
|
your browser's storage is not guaranteed to be permanent.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="flex flex-col gap-2 p-4 dark:bg-chalkboard-90">
|
<div className="flex flex-col gap-2 p-4 dark:bg-chalkboard-90">
|
||||||
<ActionButton
|
<ActionButton
|
||||||
Element="button"
|
Element="button"
|
||||||
icon={{ icon: 'exportFile', className: 'p-1' }}
|
icon={{ icon: 'exportFile', className: 'p-1' }}
|
||||||
className="border-transparent dark:border-transparent"
|
className="border-transparent dark:border-transparent"
|
||||||
|
disabled={!findCommand(exportCommandInfo)}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
commandBarSend({
|
commandBarSend({
|
||||||
type: 'Find and select command',
|
type: 'Find and select command',
|
||||||
data: { name: 'Export', ownerMachine: 'modeling' },
|
data: exportCommandInfo,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
@ -166,11 +180,10 @@ function ProjectMenuPopover({
|
|||||||
onProjectClose(file || null, project?.path || null, true)
|
onProjectClose(file || null, project?.path || null, true)
|
||||||
}}
|
}}
|
||||||
icon={{
|
icon={{
|
||||||
icon: faHome,
|
icon: 'arrowLeft',
|
||||||
className: 'p-1',
|
className: 'p-1',
|
||||||
size: 'sm',
|
|
||||||
}}
|
}}
|
||||||
className="border-transparent dark:border-transparent hover:bg-energy-10/20 dark:hover:bg-chalkboard-90"
|
className="border-transparent dark:border-transparent"
|
||||||
>
|
>
|
||||||
Go to Home
|
Go to Home
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
|
@ -135,6 +135,7 @@ export const SettingsAuthProviderBase = ({
|
|||||||
: '')
|
: '')
|
||||||
toast.success(message, {
|
toast.success(message, {
|
||||||
duration: message.split(' ').length * 100 + 1500,
|
duration: message.split(' ').length * 100 + 1500,
|
||||||
|
id: `${event.type}.success`,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
'Execute AST': () => kclManager.executeAst(),
|
'Execute AST': () => kclManager.executeAst(),
|
||||||
@ -199,6 +200,17 @@ export const SettingsAuthProviderBase = ({
|
|||||||
return () => matcher.removeEventListener('change', listener)
|
return () => matcher.removeEventListener('change', listener)
|
||||||
}, [settingsState.context])
|
}, [settingsState.context])
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the --primary-hue CSS variable
|
||||||
|
* to match the setting app.themeColor.current
|
||||||
|
*/
|
||||||
|
useEffect(() => {
|
||||||
|
document.documentElement.style.setProperty(
|
||||||
|
`--primary-hue`,
|
||||||
|
settingsState.context.app.themeColor.current
|
||||||
|
)
|
||||||
|
}, [settingsState.context.app.themeColor.current])
|
||||||
|
|
||||||
// Auth machine setup
|
// Auth machine setup
|
||||||
const [authState, authSend, authActor] = useMachine(authMachine, {
|
const [authState, authSend, authActor] = useMachine(authMachine, {
|
||||||
actions: {
|
actions: {
|
||||||
|
@ -154,10 +154,9 @@ const UserSidebarMenu = ({ user }: { user?: User }) => {
|
|||||||
icon={{
|
icon={{
|
||||||
icon: faSignOutAlt,
|
icon: faSignOutAlt,
|
||||||
className: 'p-1',
|
className: 'p-1',
|
||||||
bgClassName: 'bg-destroy-80',
|
bgClassName: '!bg-transparent',
|
||||||
size: 'sm',
|
size: 'sm',
|
||||||
iconClassName:
|
iconClassName: '!text-destroy-70',
|
||||||
'text-destroy-20 group-hover:text-destroy-10 hover:text-destroy-10',
|
|
||||||
}}
|
}}
|
||||||
className="border-transparent dark:border-transparent hover:border-destroy-40 dark:hover:border-destroy-60 hover:bg-destroy-10/20 dark:hover:bg-destroy-80/20"
|
className="border-transparent dark:border-transparent hover:border-destroy-40 dark:hover:border-destroy-60 hover:bg-destroy-10/20 dark:hover:bg-destroy-80/20"
|
||||||
data-testid="user-sidebar-sign-out"
|
data-testid="user-sidebar-sign-out"
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Platform, platform } from '@tauri-apps/api/os'
|
import { Platform, platform } from '@tauri-apps/plugin-os'
|
||||||
import { isTauri } from 'lib/isTauri'
|
import { isTauri } from 'lib/isTauri'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
|
|
||||||
@ -14,9 +14,9 @@ export default function usePlatform() {
|
|||||||
void getPlatform()
|
void getPlatform()
|
||||||
} else {
|
} else {
|
||||||
if (navigator.userAgent.indexOf('Mac') !== -1) {
|
if (navigator.userAgent.indexOf('Mac') !== -1) {
|
||||||
setPlatformName('darwin')
|
setPlatformName('macos')
|
||||||
} else if (navigator.userAgent.indexOf('Win') !== -1) {
|
} else if (navigator.userAgent.indexOf('Win') !== -1) {
|
||||||
setPlatformName('win32')
|
setPlatformName('windows')
|
||||||
} else if (navigator.userAgent.indexOf('Linux') !== -1) {
|
} else if (navigator.userAgent.indexOf('Linux') !== -1) {
|
||||||
setPlatformName('linux')
|
setPlatformName('linux')
|
||||||
}
|
}
|
||||||
|
@ -64,15 +64,15 @@ select {
|
|||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
@apply border border-chalkboard-30 m-0.5 px-3 rounded text-xs focus-visible:ring-energy-10;
|
@apply border border-chalkboard-30 m-0.5 px-3 rounded text-xs focus-visible:ring-primary;
|
||||||
}
|
}
|
||||||
|
|
||||||
button:hover {
|
button:hover {
|
||||||
@apply border-chalkboard-40 bg-energy-10/20;
|
@apply border-chalkboard-40 bg-primary/5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark button {
|
.dark button {
|
||||||
@apply border-chalkboard-70 focus-visible:ring-energy-10/50;
|
@apply border-chalkboard-70 focus-visible:ring-primary/50;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark button:hover {
|
.dark button:hover {
|
||||||
@ -80,7 +80,7 @@ button:hover {
|
|||||||
}
|
}
|
||||||
|
|
||||||
button:disabled {
|
button:disabled {
|
||||||
@apply cursor-not-allowed bg-chalkboard-20 text-chalkboard-60 border-chalkboard-20;
|
@apply cursor-not-allowed bg-chalkboard-20/50 text-chalkboard-60 border-chalkboard-20;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark button:disabled {
|
.dark button:disabled {
|
||||||
@ -88,19 +88,19 @@ button:disabled {
|
|||||||
}
|
}
|
||||||
|
|
||||||
a:not(.action-button) {
|
a:not(.action-button) {
|
||||||
@apply text-energy-70 hover:text-energy-60 underline;
|
@apply text-primary underline hover:hue-rotate-15;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark a:not(.action-button) {
|
.dark a:not(.action-button) {
|
||||||
@apply text-energy-20 hover:text-energy-10;
|
@apply hover:brightness-110 hover:hue-rotate-0;
|
||||||
}
|
}
|
||||||
|
|
||||||
input {
|
input {
|
||||||
@apply selection:bg-energy-10/50;
|
@apply selection:bg-primary/50;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark input {
|
.dark input {
|
||||||
@apply selection:bg-energy-10/40;
|
@apply selection:bg-primary/40;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mono {
|
.mono {
|
||||||
|