mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-29 02:58:12 +08:00
Merge branch 'master' into merge_point
This commit is contained in:
@@ -23,7 +23,6 @@
|
|||||||
"streetsidesoftware.code-spell-checker",
|
"streetsidesoftware.code-spell-checker",
|
||||||
// Helpful
|
// Helpful
|
||||||
"mhutchie.git-graph",
|
"mhutchie.git-graph",
|
||||||
"waderyan.gitblame",
|
|
||||||
"qezhu.gitlink",
|
"qezhu.gitlink",
|
||||||
"wmaurer.change-case"
|
"wmaurer.change-case"
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
name: Profiling Changes
|
name: Profiling Changes
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request: {}
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CARGO_TERM_COLOR: always
|
CARGO_TERM_COLOR: always
|
||||||
@@ -9,6 +9,7 @@ env:
|
|||||||
jobs:
|
jobs:
|
||||||
profile:
|
profile:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
continue-on-error: true
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
@@ -33,12 +34,12 @@ jobs:
|
|||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ~/.cargo/bin/iai-callgrind-runner
|
path: ~/.cargo/bin/iai-callgrind-runner
|
||||||
key: ${{ runner.os }}-iai-callgrind-runner-0.12.3
|
key: ${{ runner.os }}-iai-callgrind-runner-0.16.1
|
||||||
|
|
||||||
- name: Install iai-callgrind
|
- name: Install iai-callgrind
|
||||||
if: steps.cache-iai.outputs.cache-hit != 'true'
|
if: steps.cache-iai.outputs.cache-hit != 'true'
|
||||||
run: |
|
run: |
|
||||||
cargo install iai-callgrind-runner@0.12.3
|
cargo install iai-callgrind-runner@0.16.1
|
||||||
|
|
||||||
- name: Checkout master branch
|
- name: Checkout master branch
|
||||||
run: |
|
run: |
|
||||||
@@ -63,7 +64,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
# Compile benchmarks
|
# Compile benchmarks
|
||||||
cargo bench --bench compile_demo_art_iai -- --save-baseline=master
|
cargo bench --bench compile_demo_art_iai -- --save-baseline=master
|
||||||
|
|
||||||
# Runtime benchmarks
|
# Runtime benchmarks
|
||||||
cargo bench --bench update_executor_iai -- --save-baseline=master
|
cargo bench --bench update_executor_iai -- --save-baseline=master
|
||||||
cargo bench --bench run_once_iai -- --save-baseline=master
|
cargo bench --bench run_once_iai -- --save-baseline=master
|
||||||
@@ -74,32 +75,14 @@ jobs:
|
|||||||
git checkout ${{ github.event.pull_request.head.sha }}
|
git checkout ${{ github.event.pull_request.head.sha }}
|
||||||
|
|
||||||
- name: Run PR benchmarks
|
- name: Run PR benchmarks
|
||||||
id: benchmark
|
|
||||||
run: |
|
run: |
|
||||||
# Compile benchmarks
|
# Compile benchmarks
|
||||||
COMPILE_OUTPUT=$(cargo bench --bench compile_demo_art_iai -- --baseline=master --output-format=json | jq -sc | sed 's/\\"//g')
|
cargo bench --bench compile_demo_art_iai -- --baseline=master --output-format=json | jq -sc > /tmp/compile_output.json
|
||||||
|
|
||||||
# Runtime benchmarks
|
# Runtime benchmarks
|
||||||
UPDATE_OUTPUT=$(cargo bench --bench update_executor_iai -- --baseline=master --output-format=json | jq -sc | sed 's/\\"//g')
|
cargo bench --bench update_executor_iai -- --baseline=master --output-format=json | jq -sc > /tmp/update_output.json
|
||||||
RUN_ONCE_OUTPUT=$(cargo bench --bench run_once_iai -- --baseline=master --output-format=json | jq -sc | sed 's/\\"//g')
|
cargo bench --bench run_once_iai -- --baseline=master --output-format=json | jq -sc > /tmp/run_once_output.json
|
||||||
RUN_CACHED_OUTPUT=$(cargo bench --bench run_cached_iai -- --baseline=master --output-format=json | jq -sc | sed 's/\\"//g')
|
cargo bench --bench run_cached_iai -- --baseline=master --output-format=json | jq -sc > /tmp/run_cached_output.json
|
||||||
|
|
||||||
# Store outputs
|
|
||||||
echo "COMPILE_OUTPUT<<EOF" >> $GITHUB_OUTPUT
|
|
||||||
echo "$COMPILE_OUTPUT" >> $GITHUB_OUTPUT
|
|
||||||
echo "EOF" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
echo "UPDATE_OUTPUT<<EOF" >> $GITHUB_OUTPUT
|
|
||||||
echo "$UPDATE_OUTPUT" >> $GITHUB_OUTPUT
|
|
||||||
echo "EOF" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
echo "RUN_ONCE_OUTPUT<<EOF" >> $GITHUB_OUTPUT
|
|
||||||
echo "$RUN_ONCE_OUTPUT" >> $GITHUB_OUTPUT
|
|
||||||
echo "EOF" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
echo "RUN_CACHED_OUTPUT<<EOF" >> $GITHUB_OUTPUT
|
|
||||||
echo "$RUN_CACHED_OUTPUT" >> $GITHUB_OUTPUT
|
|
||||||
echo "EOF" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- name: Make old comments collapsed by default
|
- name: Make old comments collapsed by default
|
||||||
uses: actions/github-script@v7
|
uses: actions/github-script@v7
|
||||||
@@ -131,11 +114,13 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
github-token: ${{secrets.GITHUB_TOKEN}}
|
github-token: ${{secrets.GITHUB_TOKEN}}
|
||||||
script: |
|
script: |
|
||||||
const compileOutput = JSON.parse(`${{ steps.benchmark.outputs.COMPILE_OUTPUT }}`);
|
const fs = require('fs');
|
||||||
const updateOutput = JSON.parse(`${{ steps.benchmark.outputs.UPDATE_OUTPUT }}`);
|
|
||||||
const runOnceOutput = JSON.parse(`${{ steps.benchmark.outputs.RUN_ONCE_OUTPUT }}`);
|
const compileOutput = JSON.parse(fs.readFileSync('/tmp/compile_output.json', 'utf8'));
|
||||||
const runCachedOutput = JSON.parse(`${{ steps.benchmark.outputs.RUN_CACHED_OUTPUT }}`);
|
const updateOutput = JSON.parse(fs.readFileSync('/tmp/update_output.json', 'utf8'));
|
||||||
|
const runOnceOutput = JSON.parse(fs.readFileSync('/tmp/run_once_output.json', 'utf8'));
|
||||||
|
const runCachedOutput = JSON.parse(fs.readFileSync('/tmp/run_cached_output.json', 'utf8'));
|
||||||
|
|
||||||
let significantChanges = false;
|
let significantChanges = false;
|
||||||
let commentBody = "";
|
let commentBody = "";
|
||||||
|
|
||||||
|
|||||||
Vendored
-1
@@ -13,7 +13,6 @@
|
|||||||
"streetsidesoftware.code-spell-checker",
|
"streetsidesoftware.code-spell-checker",
|
||||||
// Helpful
|
// Helpful
|
||||||
"mhutchie.git-graph",
|
"mhutchie.git-graph",
|
||||||
"waderyan.gitblame",
|
|
||||||
"qezhu.gitlink",
|
"qezhu.gitlink",
|
||||||
"wmaurer.change-case"
|
"wmaurer.change-case"
|
||||||
]
|
]
|
||||||
|
|||||||
Generated
+390
-6
@@ -637,6 +637,75 @@ dependencies = [
|
|||||||
"wayland-client",
|
"wayland-client",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "camino"
|
||||||
|
version = "1.1.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dd0b03af37dad7a14518b7691d81acb0f8222604ad3d1b02f6b4bed5188c0cd5"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cargo-gpu"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "git+https://github.com/rust-gpu/cargo-gpu?rev=f969528e87baa17a7d48eecf4a6fcfdcaaf30566#f969528e87baa17a7d48eecf4a6fcfdcaaf30566"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"cargo_metadata",
|
||||||
|
"clap",
|
||||||
|
"crossterm",
|
||||||
|
"directories",
|
||||||
|
"env_logger",
|
||||||
|
"log",
|
||||||
|
"relative-path",
|
||||||
|
"rustc_codegen_spirv-target-specs 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"semver",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"spirv-builder",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cargo-platform"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "84982c6c0ae343635a3a4ee6dedef965513735c8b183caa7289fa6e27399ebd4"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cargo-util-schemas"
|
||||||
|
version = "0.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7dc1a6f7b5651af85774ae5a34b4e8be397d9cf4bc063b7e6dbd99a841837830"
|
||||||
|
dependencies = [
|
||||||
|
"semver",
|
||||||
|
"serde",
|
||||||
|
"serde-untagged",
|
||||||
|
"serde-value",
|
||||||
|
"thiserror 2.0.16",
|
||||||
|
"toml",
|
||||||
|
"unicode-xid",
|
||||||
|
"url",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cargo_metadata"
|
||||||
|
version = "0.21.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5cfca2aaa699835ba88faf58a06342a314a950d2b9686165e038286c30316868"
|
||||||
|
dependencies = [
|
||||||
|
"camino",
|
||||||
|
"cargo-platform",
|
||||||
|
"cargo-util-schemas",
|
||||||
|
"semver",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror 2.0.16",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cast"
|
name = "cast"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
@@ -656,9 +725,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cef"
|
name = "cef"
|
||||||
version = "139.6.0+139.0.37"
|
version = "139.0.1+139.0.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "92479592f7519e5843f711b07aa2194021259a5e8301ddc1ce1ac2bee5cf1575"
|
checksum = "39b749cfc4124f9505b3fbe32279c0e93f30831f1ecf3c2cf85863179319cd7b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cef-dll-sys",
|
"cef-dll-sys",
|
||||||
"libloading",
|
"libloading",
|
||||||
@@ -667,9 +736,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cef-dll-sys"
|
name = "cef-dll-sys"
|
||||||
version = "139.6.0+139.0.37"
|
version = "139.0.1+139.0.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "de4c379eb16bafc1fe3f40f9635827d2c5dcef44bb703245ce08c2086dfaa0f3"
|
checksum = "fc42adb0adc477860b705e967d9f899be05ba3775fe4d6dc4d844a1391ffa3dd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"cmake",
|
"cmake",
|
||||||
@@ -866,6 +935,15 @@ dependencies = [
|
|||||||
"windows-sys 0.60.2",
|
"windows-sys 0.60.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "convert_case"
|
||||||
|
version = "0.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-segmentation",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "convert_case"
|
name = "convert_case"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
@@ -1029,6 +1107,33 @@ version = "0.8.21"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossterm"
|
||||||
|
version = "0.29.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.9.3",
|
||||||
|
"crossterm_winapi",
|
||||||
|
"derive_more",
|
||||||
|
"document-features",
|
||||||
|
"mio",
|
||||||
|
"parking_lot",
|
||||||
|
"rustix 1.0.8",
|
||||||
|
"signal-hook",
|
||||||
|
"signal-hook-mio",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossterm_winapi"
|
||||||
|
version = "0.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crunchy"
|
name = "crunchy"
|
||||||
version = "0.2.4"
|
version = "0.2.4"
|
||||||
@@ -1105,6 +1210,7 @@ version = "2.0.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3"
|
checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"convert_case 0.7.1",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.106",
|
"syn 2.0.106",
|
||||||
@@ -1126,6 +1232,15 @@ dependencies = [
|
|||||||
"crypto-common",
|
"crypto-common",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "directories"
|
||||||
|
version = "6.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "16f5094c54661b38d03bd7e50df373292118db60b585c08a411c6d840017fe7d"
|
||||||
|
dependencies = [
|
||||||
|
"dirs-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dirs"
|
name = "dirs"
|
||||||
version = "6.0.0"
|
version = "6.0.0"
|
||||||
@@ -1344,6 +1459,16 @@ version = "1.0.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "erased-serde"
|
||||||
|
version = "0.4.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e004d887f51fcb9fef17317a2f3525c887d8aa3f4f50fed920816a688284a5b7"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"typeid",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "errno"
|
name = "errno"
|
||||||
version = "0.3.13"
|
version = "0.3.13"
|
||||||
@@ -1609,6 +1734,15 @@ dependencies = [
|
|||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fsevent-sys"
|
||||||
|
version = "4.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures"
|
name = "futures"
|
||||||
version = "0.3.31"
|
version = "0.3.31"
|
||||||
@@ -2045,6 +2179,7 @@ dependencies = [
|
|||||||
"glam",
|
"glam",
|
||||||
"graphene-core",
|
"graphene-core",
|
||||||
"graphene-core-shaders",
|
"graphene-core-shaders",
|
||||||
|
"graphene-raster-nodes-shaders",
|
||||||
"image",
|
"image",
|
||||||
"kurbo",
|
"kurbo",
|
||||||
"ndarray",
|
"ndarray",
|
||||||
@@ -2054,9 +2189,18 @@ dependencies = [
|
|||||||
"rand_chacha 0.9.0",
|
"rand_chacha 0.9.0",
|
||||||
"serde",
|
"serde",
|
||||||
"specta",
|
"specta",
|
||||||
|
"spirv-std",
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "graphene-raster-nodes-shaders"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"cargo-gpu",
|
||||||
|
"env_logger",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "graphene-std"
|
name = "graphene-std"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -2107,6 +2251,7 @@ dependencies = [
|
|||||||
"ash",
|
"ash",
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"cef",
|
"cef",
|
||||||
|
"cef-dll-sys",
|
||||||
"core-foundation",
|
"core-foundation",
|
||||||
"derivative",
|
"derivative",
|
||||||
"dirs",
|
"dirs",
|
||||||
@@ -2698,6 +2843,26 @@ version = "2.0.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd"
|
checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "inotify"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.9.3",
|
||||||
|
"inotify-sys",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "inotify-sys"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "interpolate_name"
|
name = "interpolate_name"
|
||||||
version = "0.2.4"
|
version = "0.2.4"
|
||||||
@@ -2893,6 +3058,26 @@ version = "3.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
|
checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "kqueue"
|
||||||
|
version = "1.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eac30106d7dce88daf4a3fcb4879ea939476d5074a9b7ddd0fb97fa4bed5596a"
|
||||||
|
dependencies = [
|
||||||
|
"kqueue-sys",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "kqueue-sys"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kurbo"
|
name = "kurbo"
|
||||||
version = "0.11.3"
|
version = "0.11.3"
|
||||||
@@ -3156,6 +3341,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
|
checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
|
"log",
|
||||||
"wasi 0.11.1+wasi-snapshot-preview1",
|
"wasi 0.11.1+wasi-snapshot-preview1",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
@@ -3280,7 +3466,7 @@ dependencies = [
|
|||||||
name = "node-macro"
|
name = "node-macro"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"convert_case",
|
"convert_case 0.8.0",
|
||||||
"graphene-core",
|
"graphene-core",
|
||||||
"indoc",
|
"indoc",
|
||||||
"proc-macro-crate",
|
"proc-macro-crate",
|
||||||
@@ -3307,6 +3493,30 @@ version = "0.3.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8"
|
checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "notify"
|
||||||
|
version = "8.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.9.3",
|
||||||
|
"fsevent-sys",
|
||||||
|
"inotify",
|
||||||
|
"kqueue",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"mio",
|
||||||
|
"notify-types",
|
||||||
|
"walkdir",
|
||||||
|
"windows-sys 0.60.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "notify-types"
|
||||||
|
version = "2.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5e0826a989adedc2a244799e823aece04662b66609d96af8dff7ac6df9a8925d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-ansi-term"
|
name = "nu-ansi-term"
|
||||||
version = "0.50.1"
|
version = "0.50.1"
|
||||||
@@ -3793,6 +4003,15 @@ dependencies = [
|
|||||||
"libredox",
|
"libredox",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ordered-float"
|
||||||
|
version = "2.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ordered-float"
|
name = "ordered-float"
|
||||||
version = "4.6.0"
|
version = "4.6.0"
|
||||||
@@ -4434,6 +4653,12 @@ dependencies = [
|
|||||||
"rgb",
|
"rgb",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "raw-string"
|
||||||
|
version = "0.3.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e0501e134c6905fee1f10fed25b0a7e1261bf676cffac9543a7d0730dec01af2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "raw-window-handle"
|
name = "raw-window-handle"
|
||||||
version = "0.6.2"
|
version = "0.6.2"
|
||||||
@@ -4544,6 +4769,15 @@ version = "0.8.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001"
|
checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "relative-path"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bca40a312222d8ba74837cb474edef44b37f561da5f773981007a10bbaa992b0"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "renderdoc-sys"
|
name = "renderdoc-sys"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
@@ -4685,6 +4919,16 @@ version = "0.20.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97"
|
checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rspirv"
|
||||||
|
version = "0.12.0+sdk-1.3.268.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "69cf3a93856b6e5946537278df0d3075596371b1950ccff012f02b0f7eafec8d"
|
||||||
|
dependencies = [
|
||||||
|
"rustc-hash 1.1.0",
|
||||||
|
"spirv",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-demangle"
|
name = "rustc-demangle"
|
||||||
version = "0.1.26"
|
version = "0.1.26"
|
||||||
@@ -4703,6 +4947,33 @@ version = "2.1.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
|
checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc_codegen_spirv-target-specs"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6c89eaf493b3dfc730cda42a77014aad65e03213992c7afe0dff60a9f7d3dd94"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc_codegen_spirv-target-specs"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "git+https://github.com/rust-gpu/rust-gpu?rev=c12f216121820580731440ee79ebc7403d6ea04f#c12f216121820580731440ee79ebc7403d6ea04f"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"strum 0.27.2",
|
||||||
|
"thiserror 2.0.16",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc_codegen_spirv-types"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "git+https://github.com/rust-gpu/rust-gpu?rev=c12f216121820580731440ee79ebc7403d6ea04f#c12f216121820580731440ee79ebc7403d6ea04f"
|
||||||
|
dependencies = [
|
||||||
|
"rspirv",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"spirv",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "0.38.44"
|
version = "0.38.44"
|
||||||
@@ -4893,6 +5164,9 @@ name = "semver"
|
|||||||
version = "1.0.26"
|
version = "1.0.26"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0"
|
checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
@@ -4903,6 +5177,27 @@ dependencies = [
|
|||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde-untagged"
|
||||||
|
version = "0.1.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "34836a629bcbc6f1afdf0907a744870039b1e14c0561cb26094fa683b158eff3"
|
||||||
|
dependencies = [
|
||||||
|
"erased-serde",
|
||||||
|
"serde",
|
||||||
|
"typeid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde-value"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c"
|
||||||
|
dependencies = [
|
||||||
|
"ordered-float 2.10.1",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde-wasm-bindgen"
|
name = "serde-wasm-bindgen"
|
||||||
version = "0.6.5"
|
version = "0.6.5"
|
||||||
@@ -5001,6 +5296,27 @@ version = "1.3.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signal-hook"
|
||||||
|
version = "0.3.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"signal-hook-registry",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signal-hook-mio"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"mio",
|
||||||
|
"signal-hook",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "signal-hook-registry"
|
name = "signal-hook-registry"
|
||||||
version = "1.4.6"
|
version = "1.4.6"
|
||||||
@@ -5178,8 +5494,57 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844"
|
checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.9.3",
|
"bitflags 2.9.3",
|
||||||
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "spirv-builder"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "git+https://github.com/rust-gpu/rust-gpu?rev=c12f216121820580731440ee79ebc7403d6ea04f#c12f216121820580731440ee79ebc7403d6ea04f"
|
||||||
|
dependencies = [
|
||||||
|
"cargo_metadata",
|
||||||
|
"clap",
|
||||||
|
"log",
|
||||||
|
"memchr",
|
||||||
|
"notify",
|
||||||
|
"raw-string",
|
||||||
|
"rustc_codegen_spirv-target-specs 0.9.0 (git+https://github.com/rust-gpu/rust-gpu?rev=c12f216121820580731440ee79ebc7403d6ea04f)",
|
||||||
|
"rustc_codegen_spirv-types",
|
||||||
|
"semver",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror 2.0.16",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "spirv-std"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "git+https://github.com/rust-gpu/rust-gpu?rev=c12f216121820580731440ee79ebc7403d6ea04f#c12f216121820580731440ee79ebc7403d6ea04f"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"glam",
|
||||||
|
"libm",
|
||||||
|
"num-traits",
|
||||||
|
"spirv-std-macros",
|
||||||
|
"spirv-std-types",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "spirv-std-macros"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "git+https://github.com/rust-gpu/rust-gpu?rev=c12f216121820580731440ee79ebc7403d6ea04f#c12f216121820580731440ee79ebc7403d6ea04f"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"spirv-std-types",
|
||||||
|
"syn 2.0.106",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "spirv-std-types"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "git+https://github.com/rust-gpu/rust-gpu?rev=c12f216121820580731440ee79ebc7403d6ea04f#c12f216121820580731440ee79ebc7403d6ea04f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stable_deref_trait"
|
name = "stable_deref_trait"
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
@@ -5658,9 +6023,16 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_spanned",
|
"serde_spanned",
|
||||||
"toml_datetime",
|
"toml_datetime",
|
||||||
|
"toml_write",
|
||||||
"winnow",
|
"winnow",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_write"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tower"
|
name = "tower"
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
@@ -5791,6 +6163,12 @@ dependencies = [
|
|||||||
"core_maths",
|
"core_maths",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typeid"
|
||||||
|
version = "1.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typenum"
|
name = "typenum"
|
||||||
version = "1.18.0"
|
version = "1.18.0"
|
||||||
@@ -5880,6 +6258,12 @@ version = "0.2.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c"
|
checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-xid"
|
||||||
|
version = "0.2.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unit-prefix"
|
name = "unit-prefix"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
@@ -6479,7 +6863,7 @@ dependencies = [
|
|||||||
"naga",
|
"naga",
|
||||||
"ndk-sys 0.5.0+25.2.9519653",
|
"ndk-sys 0.5.0+25.2.9519653",
|
||||||
"objc",
|
"objc",
|
||||||
"ordered-float",
|
"ordered-float 4.6.0",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"portable-atomic",
|
"portable-atomic",
|
||||||
"profiling",
|
"profiling",
|
||||||
|
|||||||
+9
-1
@@ -17,6 +17,7 @@ members = [
|
|||||||
"node-graph/graph-craft",
|
"node-graph/graph-craft",
|
||||||
"node-graph/graphene-cli",
|
"node-graph/graphene-cli",
|
||||||
"node-graph/graster-nodes",
|
"node-graph/graster-nodes",
|
||||||
|
"node-graph/graster-nodes/shaders",
|
||||||
"node-graph/gstd",
|
"node-graph/gstd",
|
||||||
"node-graph/gsvg-renderer",
|
"node-graph/gsvg-renderer",
|
||||||
"node-graph/interpreted-executor",
|
"node-graph/interpreted-executor",
|
||||||
@@ -37,6 +38,7 @@ default-members = [
|
|||||||
"node-graph/gbrush",
|
"node-graph/gbrush",
|
||||||
"node-graph/gcore",
|
"node-graph/gcore",
|
||||||
"node-graph/gcore-shaders",
|
"node-graph/gcore-shaders",
|
||||||
|
"node-graph/graster-nodes/shaders",
|
||||||
"node-graph/gmath-nodes",
|
"node-graph/gmath-nodes",
|
||||||
"node-graph/gpath-bool",
|
"node-graph/gpath-bool",
|
||||||
"node-graph/graph-craft",
|
"node-graph/graph-craft",
|
||||||
@@ -181,7 +183,8 @@ iai-callgrind = { version = "0.16" }
|
|||||||
ndarray = "0.16"
|
ndarray = "0.16"
|
||||||
strum = { version = "0.27", features = ["derive"] }
|
strum = { version = "0.27", features = ["derive"] }
|
||||||
dirs = "6.0"
|
dirs = "6.0"
|
||||||
cef = "139.0"
|
cef = "=139.0.1"
|
||||||
|
cef-dll-sys = "=139.0.1"
|
||||||
include_dir = "0.7"
|
include_dir = "0.7"
|
||||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
@@ -190,6 +193,11 @@ open = "5.3"
|
|||||||
poly-cool = "0.3"
|
poly-cool = "0.3"
|
||||||
spin = "0.10"
|
spin = "0.10"
|
||||||
clap = "4.5"
|
clap = "4.5"
|
||||||
|
spirv-std = { git = "https://github.com/rust-gpu/rust-gpu", rev = "c12f216121820580731440ee79ebc7403d6ea04f" }
|
||||||
|
cargo-gpu = { git = "https://github.com/rust-gpu/cargo-gpu", rev = "f969528e87baa17a7d48eecf4a6fcfdcaaf30566" }
|
||||||
|
|
||||||
|
[workspace.lints.rust]
|
||||||
|
unexpected_cfgs = { level = "allow", check-cfg = ['cfg(target_arch, values("spirv"))'] }
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
opt-level = 1
|
opt-level = 1
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ winit = { workspace = true, features = ["serde"] }
|
|||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
futures = { workspace = true }
|
futures = { workspace = true }
|
||||||
cef = { workspace = true }
|
cef = { workspace = true }
|
||||||
|
cef-dll-sys = { workspace = true }
|
||||||
tracing-subscriber = { workspace = true }
|
tracing-subscriber = { workspace = true }
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
dirs = { workspace = true }
|
dirs = { workspace = true }
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use super::utility_types::{FrontendDocumentDetails, MouseCursorIcon};
|
use super::utility_types::{DocumentDetails, MouseCursorIcon, OpenDocument};
|
||||||
use crate::messages::app_window::app_window_message_handler::AppWindowPlatform;
|
use crate::messages::app_window::app_window_message_handler::AppWindowPlatform;
|
||||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||||
use crate::messages::portfolio::document::node_graph::utility_types::{
|
use crate::messages::portfolio::document::node_graph::utility_types::{
|
||||||
@@ -90,13 +90,15 @@ pub enum FrontendMessage {
|
|||||||
font: Font,
|
font: Font,
|
||||||
},
|
},
|
||||||
TriggerImport,
|
TriggerImport,
|
||||||
TriggerIndexedDbRemoveDocument {
|
TriggerPersistenceRemoveDocument {
|
||||||
#[serde(rename = "documentId")]
|
#[serde(rename = "documentId")]
|
||||||
document_id: DocumentId,
|
document_id: DocumentId,
|
||||||
},
|
},
|
||||||
TriggerIndexedDbWriteDocument {
|
TriggerPersistenceWriteDocument {
|
||||||
|
#[serde(rename = "documentId")]
|
||||||
|
document_id: DocumentId,
|
||||||
document: String,
|
document: String,
|
||||||
details: FrontendDocumentDetails,
|
details: DocumentDetails,
|
||||||
},
|
},
|
||||||
TriggerLoadFirstAutoSaveDocument,
|
TriggerLoadFirstAutoSaveDocument,
|
||||||
TriggerLoadRestAutoSaveDocuments,
|
TriggerLoadRestAutoSaveDocuments,
|
||||||
@@ -308,7 +310,7 @@ pub enum FrontendMessage {
|
|||||||
},
|
},
|
||||||
UpdateOpenDocumentsList {
|
UpdateOpenDocumentsList {
|
||||||
#[serde(rename = "openDocuments")]
|
#[serde(rename = "openDocuments")]
|
||||||
open_documents: Vec<FrontendDocumentDetails>,
|
open_documents: Vec<OpenDocument>,
|
||||||
},
|
},
|
||||||
UpdatePropertiesPanelLayout {
|
UpdatePropertiesPanelLayout {
|
||||||
#[serde(rename = "layoutTarget")]
|
#[serde(rename = "layoutTarget")]
|
||||||
|
|||||||
@@ -2,13 +2,18 @@ use crate::messages::portfolio::document::utility_types::document_metadata::Laye
|
|||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
|
#[derive(PartialEq, Eq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||||
pub struct FrontendDocumentDetails {
|
pub struct OpenDocument {
|
||||||
#[serde(rename = "isAutoSaved")]
|
pub id: DocumentId,
|
||||||
pub is_auto_saved: bool,
|
pub details: DocumentDetails,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||||
|
pub struct DocumentDetails {
|
||||||
|
pub name: String,
|
||||||
#[serde(rename = "isSaved")]
|
#[serde(rename = "isSaved")]
|
||||||
pub is_saved: bool,
|
pub is_saved: bool,
|
||||||
pub name: String,
|
#[serde(rename = "isAutoSaved")]
|
||||||
pub id: DocumentId,
|
pub is_auto_saved: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||||
|
|||||||
@@ -952,6 +952,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
|
|||||||
|
|
||||||
self.path = None;
|
self.path = None;
|
||||||
self.set_save_state(false);
|
self.set_save_state(false);
|
||||||
|
self.set_auto_save_state(false);
|
||||||
|
|
||||||
responses.add(PortfolioMessage::UpdateOpenDocumentsList);
|
responses.add(PortfolioMessage::UpdateOpenDocumentsList);
|
||||||
responses.add(NodeGraphMessage::UpdateNewNodeGraph);
|
responses.add(NodeGraphMessage::UpdateNewNodeGraph);
|
||||||
@@ -1301,6 +1302,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
|
|||||||
}
|
}
|
||||||
self.network_interface.finish_transaction();
|
self.network_interface.finish_transaction();
|
||||||
self.document_redo_history.clear();
|
self.document_redo_history.clear();
|
||||||
|
responses.add(PortfolioMessage::UpdateOpenDocumentsList);
|
||||||
}
|
}
|
||||||
DocumentMessage::AbortTransaction => {
|
DocumentMessage::AbortTransaction => {
|
||||||
responses.add(DocumentMessage::RepeatedAbortTransaction { undo_count: 1 });
|
responses.add(DocumentMessage::RepeatedAbortTransaction { undo_count: 1 });
|
||||||
@@ -1316,6 +1318,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
|
|||||||
|
|
||||||
self.network_interface.finish_transaction();
|
self.network_interface.finish_transaction();
|
||||||
responses.add(OverlaysMessage::Draw);
|
responses.add(OverlaysMessage::Draw);
|
||||||
|
responses.add(PortfolioMessage::UpdateOpenDocumentsList);
|
||||||
}
|
}
|
||||||
DocumentMessage::ToggleLayerExpansion { id, recursive } => {
|
DocumentMessage::ToggleLayerExpansion { id, recursive } => {
|
||||||
let layer = LayerNodeIdentifier::new(id, &self.network_interface);
|
let layer = LayerNodeIdentifier::new(id, &self.network_interface);
|
||||||
@@ -1975,16 +1978,16 @@ impl DocumentMessageHandler {
|
|||||||
Some(previous_network)
|
Some(previous_network)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn current_hash(&self) -> Option<u64> {
|
pub fn current_hash(&self) -> u64 {
|
||||||
self.document_undo_history.iter().last().map(|network| network.document_network().current_hash())
|
self.network_interface.document_network().current_hash()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_auto_saved(&self) -> bool {
|
pub fn is_auto_saved(&self) -> bool {
|
||||||
self.current_hash() == self.auto_saved_hash
|
Some(self.current_hash()) == self.auto_saved_hash
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_saved(&self) -> bool {
|
pub fn is_saved(&self) -> bool {
|
||||||
self.current_hash() == self.saved_hash
|
Some(self.current_hash()) == self.saved_hash
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_graph_overlay_open(&self) -> bool {
|
pub fn is_graph_overlay_open(&self) -> bool {
|
||||||
@@ -1993,7 +1996,7 @@ impl DocumentMessageHandler {
|
|||||||
|
|
||||||
pub fn set_auto_save_state(&mut self, is_saved: bool) {
|
pub fn set_auto_save_state(&mut self, is_saved: bool) {
|
||||||
if is_saved {
|
if is_saved {
|
||||||
self.auto_saved_hash = self.current_hash();
|
self.auto_saved_hash = Some(self.current_hash());
|
||||||
} else {
|
} else {
|
||||||
self.auto_saved_hash = None;
|
self.auto_saved_hash = None;
|
||||||
}
|
}
|
||||||
@@ -2001,7 +2004,7 @@ impl DocumentMessageHandler {
|
|||||||
|
|
||||||
pub fn set_save_state(&mut self, is_saved: bool) {
|
pub fn set_save_state(&mut self, is_saved: bool) {
|
||||||
if is_saved {
|
if is_saved {
|
||||||
self.saved_hash = self.current_hash();
|
self.saved_hash = Some(self.current_hash());
|
||||||
} else {
|
} else {
|
||||||
self.saved_hash = None;
|
self.saved_hash = None;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use crate::consts::{DEFAULT_DOCUMENT_NAME, DEFAULT_STROKE_WIDTH, FILE_EXTENSION}
|
|||||||
use crate::messages::animation::TimingInformation;
|
use crate::messages::animation::TimingInformation;
|
||||||
use crate::messages::debug::utility_types::MessageLoggingVerbosity;
|
use crate::messages::debug::utility_types::MessageLoggingVerbosity;
|
||||||
use crate::messages::dialog::simple_dialogs;
|
use crate::messages::dialog::simple_dialogs;
|
||||||
use crate::messages::frontend::utility_types::FrontendDocumentDetails;
|
use crate::messages::frontend::utility_types::{DocumentDetails, OpenDocument};
|
||||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||||
use crate::messages::portfolio::document::DocumentMessageContext;
|
use crate::messages::portfolio::document::DocumentMessageContext;
|
||||||
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
|
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
|
||||||
@@ -187,13 +187,13 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
|||||||
}
|
}
|
||||||
PortfolioMessage::AutoSaveDocument { document_id } => {
|
PortfolioMessage::AutoSaveDocument { document_id } => {
|
||||||
let document = self.documents.get(&document_id).unwrap();
|
let document = self.documents.get(&document_id).unwrap();
|
||||||
responses.add(FrontendMessage::TriggerIndexedDbWriteDocument {
|
responses.add(FrontendMessage::TriggerPersistenceWriteDocument {
|
||||||
|
document_id,
|
||||||
document: document.serialize_document(),
|
document: document.serialize_document(),
|
||||||
details: FrontendDocumentDetails {
|
details: DocumentDetails {
|
||||||
is_auto_saved: document.is_auto_saved(),
|
|
||||||
is_saved: document.is_saved(),
|
|
||||||
id: document_id,
|
|
||||||
name: document.name.clone(),
|
name: document.name.clone(),
|
||||||
|
is_saved: document.is_saved(),
|
||||||
|
is_auto_saved: document.is_auto_saved(),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -216,7 +216,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
|||||||
}
|
}
|
||||||
|
|
||||||
for document_id in &self.document_ids {
|
for document_id in &self.document_ids {
|
||||||
responses.add(FrontendMessage::TriggerIndexedDbRemoveDocument { document_id: *document_id });
|
responses.add(FrontendMessage::TriggerPersistenceRemoveDocument { document_id: *document_id });
|
||||||
}
|
}
|
||||||
|
|
||||||
responses.add(PortfolioMessage::DestroyAllDocuments);
|
responses.add(PortfolioMessage::DestroyAllDocuments);
|
||||||
@@ -242,7 +242,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
|||||||
|
|
||||||
// Actually delete the document (delay to delete document is required to let the document and properties panel messages above get processed)
|
// Actually delete the document (delay to delete document is required to let the document and properties panel messages above get processed)
|
||||||
responses.add(PortfolioMessage::DeleteDocument { document_id });
|
responses.add(PortfolioMessage::DeleteDocument { document_id });
|
||||||
responses.add(FrontendMessage::TriggerIndexedDbRemoveDocument { document_id });
|
responses.add(FrontendMessage::TriggerPersistenceRemoveDocument { document_id });
|
||||||
|
|
||||||
// Send the new list of document tab names
|
// Send the new list of document tab names
|
||||||
responses.add(PortfolioMessage::UpdateOpenDocumentsList);
|
responses.add(PortfolioMessage::UpdateOpenDocumentsList);
|
||||||
@@ -1044,11 +1044,13 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
|||||||
.document_ids
|
.document_ids
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|id| {
|
.filter_map(|id| {
|
||||||
self.documents.get(id).map(|document| FrontendDocumentDetails {
|
self.documents.get(id).map(|document| OpenDocument {
|
||||||
is_auto_saved: document.is_auto_saved(),
|
|
||||||
is_saved: document.is_saved(),
|
|
||||||
id: *id,
|
id: *id,
|
||||||
name: document.name.clone(),
|
details: DocumentDetails {
|
||||||
|
is_auto_saved: document.is_auto_saved(),
|
||||||
|
is_saved: document.is_saved(),
|
||||||
|
name: document.name.clone(),
|
||||||
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|||||||
@@ -1,109 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
root: true,
|
|
||||||
env: { browser: true, node: true },
|
|
||||||
extends: [
|
|
||||||
"eslint:recommended",
|
|
||||||
"plugin:import/recommended",
|
|
||||||
"plugin:@typescript-eslint/recommended",
|
|
||||||
"plugin:import/typescript",
|
|
||||||
"plugin:svelte/recommended",
|
|
||||||
"plugin:svelte/prettier",
|
|
||||||
"prettier",
|
|
||||||
],
|
|
||||||
plugins: ["import", "@typescript-eslint", "prettier"],
|
|
||||||
settings: {
|
|
||||||
"import/parsers": { "@typescript-eslint/parser": [".ts"] },
|
|
||||||
"import/resolver": { typescript: true, node: true },
|
|
||||||
},
|
|
||||||
parser: "@typescript-eslint/parser",
|
|
||||||
parserOptions: {
|
|
||||||
ecmaVersion: "latest",
|
|
||||||
project: "./tsconfig.json",
|
|
||||||
extraFileExtensions: [".svelte"],
|
|
||||||
},
|
|
||||||
ignorePatterns: [
|
|
||||||
// Ignore generated directories
|
|
||||||
"node_modules/",
|
|
||||||
"dist/",
|
|
||||||
"pkg/",
|
|
||||||
"wasm/pkg/",
|
|
||||||
// Don't ignore JS and TS dotfiles in this folder
|
|
||||||
"!.*.js",
|
|
||||||
"!.*.ts",
|
|
||||||
],
|
|
||||||
overrides: [
|
|
||||||
{
|
|
||||||
files: ["*.svelte"],
|
|
||||||
parser: "svelte-eslint-parser",
|
|
||||||
// Parse the `<script>` in `.svelte` as TypeScript by adding the following configuration.
|
|
||||||
parserOptions: { parser: "@typescript-eslint/parser" },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
extends: ["plugin:@typescript-eslint/disable-type-checked"],
|
|
||||||
files: ["./*.js", "./*.cjs"],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
rules: {
|
|
||||||
// Standard ESLint config (for ordinary JS syntax linting)
|
|
||||||
indent: "off",
|
|
||||||
quotes: ["error", "double", { allowTemplateLiterals: true }],
|
|
||||||
camelcase: ["error", { properties: "always" }],
|
|
||||||
"linebreak-style": ["error", "unix"],
|
|
||||||
"eol-last": ["error", "always"],
|
|
||||||
"max-len": ["error", { code: 200, tabWidth: 4, ignorePattern: `d="([\\s\\S]*?)"` }],
|
|
||||||
"prefer-destructuring": "off",
|
|
||||||
"no-console": "warn",
|
|
||||||
"no-debugger": "warn",
|
|
||||||
"no-param-reassign": ["error", { props: false }],
|
|
||||||
"no-bitwise": "off",
|
|
||||||
"no-shadow": "off",
|
|
||||||
"no-use-before-define": "off",
|
|
||||||
"no-restricted-imports": ["error", { patterns: [".*", "!@graphite/*"] }],
|
|
||||||
|
|
||||||
// TypeScript plugin config (for TS-specific linting)
|
|
||||||
"@typescript-eslint/indent": "off",
|
|
||||||
"@typescript-eslint/camelcase": "off",
|
|
||||||
"@typescript-eslint/no-use-before-define": "off",
|
|
||||||
"@typescript-eslint/no-unused-vars": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
args: "all",
|
|
||||||
argsIgnorePattern: "^_",
|
|
||||||
caughtErrors: "all",
|
|
||||||
caughtErrorsIgnorePattern: "^_",
|
|
||||||
destructuredArrayIgnorePattern: "^_",
|
|
||||||
varsIgnorePattern: "^_",
|
|
||||||
ignoreRestSiblings: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"@typescript-eslint/consistent-type-imports": "error",
|
|
||||||
"@typescript-eslint/consistent-type-definitions": ["error", "type"],
|
|
||||||
"@typescript-eslint/consistent-type-assertions": ["error", { assertionStyle: "as", objectLiteralTypeAssertions: "never" }],
|
|
||||||
"@typescript-eslint/consistent-indexed-object-style": ["error", "record"],
|
|
||||||
"@typescript-eslint/consistent-generic-constructors": ["error", "constructor"],
|
|
||||||
"@typescript-eslint/no-restricted-types": ["error", { types: { null: "Use `undefined` instead." } }],
|
|
||||||
|
|
||||||
// Prettier plugin config (for validating and fixing formatting)
|
|
||||||
"prettier/prettier": "error",
|
|
||||||
|
|
||||||
// Svelte plugin config (for validating Svelte-specific syntax)
|
|
||||||
"svelte/no-at-html-tags": "off",
|
|
||||||
"svelte/valid-compile": ["error", { ignoreWarnings: true }],
|
|
||||||
|
|
||||||
// Import plugin config (for intelligently validating module import statements)
|
|
||||||
"import/no-unresolved": "error",
|
|
||||||
// `no-duplicates` disabled due to <https://github.com/import-js/eslint-plugin-import/issues/1479#issuecomment-1789527447>. Reenable if that issue gets fixed.
|
|
||||||
"import/no-duplicates": "off",
|
|
||||||
"import/prefer-default-export": "off",
|
|
||||||
"import/no-relative-packages": "error",
|
|
||||||
"import/order": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
alphabetize: { order: "asc", caseInsensitive: true },
|
|
||||||
pathGroups: [{ pattern: "**/*.svelte", group: "unknown", position: "after" }],
|
|
||||||
"newlines-between": "always-and-inside-groups",
|
|
||||||
warnOnUnassignedImports: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
import js from "@eslint/js";
|
||||||
|
import { defineConfig, globalIgnores } from "eslint/config";
|
||||||
|
import * as pluginImport from "eslint-plugin-import";
|
||||||
|
import pluginPrettier from "eslint-plugin-prettier";
|
||||||
|
import pluginSvelte from "eslint-plugin-svelte";
|
||||||
|
import globals from "globals";
|
||||||
|
import ts from "typescript-eslint";
|
||||||
|
|
||||||
|
export default defineConfig([
|
||||||
|
js.configs.recommended,
|
||||||
|
ts.configs.recommended,
|
||||||
|
pluginImport.flatConfigs.recommended,
|
||||||
|
pluginImport.flatConfigs.typescript,
|
||||||
|
pluginSvelte.configs["flat/recommended"],
|
||||||
|
pluginSvelte.configs["flat/prettier"],
|
||||||
|
globalIgnores([
|
||||||
|
// Ignore generated directories
|
||||||
|
"**/node_modules/",
|
||||||
|
"**/dist/",
|
||||||
|
"**/pkg/",
|
||||||
|
"wasm/pkg/",
|
||||||
|
// Don't ignore JS and TS dotfiles in this folder
|
||||||
|
"!**/.*.js",
|
||||||
|
"!**/.*.ts",
|
||||||
|
]),
|
||||||
|
{
|
||||||
|
plugins: {
|
||||||
|
prettier: pluginPrettier,
|
||||||
|
},
|
||||||
|
settings: {
|
||||||
|
"import/parsers": { "@typescript-eslint/parser": [".ts"] },
|
||||||
|
"import/resolver": { typescript: true, node: true },
|
||||||
|
},
|
||||||
|
languageOptions: {
|
||||||
|
parserOptions: {
|
||||||
|
project: "./tsconfig.json",
|
||||||
|
},
|
||||||
|
globals: {
|
||||||
|
...globals.browser,
|
||||||
|
...globals.node,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
// Standard ESLint config (for ordinary JS syntax linting)
|
||||||
|
indent: "off",
|
||||||
|
quotes: ["error", "double", { allowTemplateLiterals: true }],
|
||||||
|
camelcase: ["error", { properties: "always" }],
|
||||||
|
"linebreak-style": ["error", "unix"],
|
||||||
|
"eol-last": ["error", "always"],
|
||||||
|
"max-len": ["error", { code: 200, tabWidth: 4, ignorePattern: `d="([\\s\\S]*?)"` }],
|
||||||
|
"prefer-destructuring": "off",
|
||||||
|
"no-console": "warn",
|
||||||
|
"no-debugger": "warn",
|
||||||
|
"no-param-reassign": ["error", { props: false }],
|
||||||
|
"no-bitwise": "off",
|
||||||
|
"no-shadow": "off",
|
||||||
|
"no-use-before-define": "off",
|
||||||
|
"no-restricted-imports": ["error", { patterns: [".*", "!@graphite/*"] }],
|
||||||
|
|
||||||
|
// TypeScript plugin config (for TS-specific linting)
|
||||||
|
"@typescript-eslint/indent": "off",
|
||||||
|
"@typescript-eslint/camelcase": "off",
|
||||||
|
"@typescript-eslint/no-use-before-define": "off",
|
||||||
|
"@typescript-eslint/no-unused-vars": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
args: "all",
|
||||||
|
argsIgnorePattern: "^_",
|
||||||
|
caughtErrors: "all",
|
||||||
|
caughtErrorsIgnorePattern: "^_",
|
||||||
|
destructuredArrayIgnorePattern: "^_",
|
||||||
|
varsIgnorePattern: "^_",
|
||||||
|
ignoreRestSiblings: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"@typescript-eslint/consistent-type-imports": "error",
|
||||||
|
"@typescript-eslint/consistent-type-definitions": ["error", "type"],
|
||||||
|
"@typescript-eslint/consistent-type-assertions": ["error", { assertionStyle: "as", objectLiteralTypeAssertions: "never" }],
|
||||||
|
"@typescript-eslint/consistent-indexed-object-style": ["error", "record"],
|
||||||
|
"@typescript-eslint/consistent-generic-constructors": ["error", "constructor"],
|
||||||
|
"@typescript-eslint/no-restricted-types": ["error", { types: { null: "Use `undefined` instead." } }],
|
||||||
|
|
||||||
|
// Prettier plugin config (for validating and fixing formatting)
|
||||||
|
"prettier/prettier": "error",
|
||||||
|
|
||||||
|
// Svelte plugin config (for validating Svelte-specific syntax)
|
||||||
|
"svelte/no-at-html-tags": "off",
|
||||||
|
"svelte/no-useless-mustaches": "off",
|
||||||
|
"svelte/valid-compile": ["error", { ignoreWarnings: true }],
|
||||||
|
"svelte/require-each-key": "off", // TODO: Remove this rule and fix the places where it's violated
|
||||||
|
|
||||||
|
// Import plugin config (for intelligently validating module import statements)
|
||||||
|
"import/no-unresolved": "error",
|
||||||
|
// `no-duplicates` disabled due to <https://github.com/import-js/eslint-plugin-import/issues/1479#issuecomment-1789527447>. Reenable if that issue gets fixed.
|
||||||
|
"import/no-duplicates": "off",
|
||||||
|
"import/prefer-default-export": "off",
|
||||||
|
"import/no-relative-packages": "error",
|
||||||
|
"import/no-named-as-default-member": "off",
|
||||||
|
"import/order": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
alphabetize: { order: "asc", caseInsensitive: true },
|
||||||
|
pathGroups: [{ pattern: "**/*.svelte", group: "unknown", position: "after" }],
|
||||||
|
"newlines-between": "always-and-inside-groups",
|
||||||
|
warnOnUnassignedImports: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ["**/*.svelte"],
|
||||||
|
languageOptions: {
|
||||||
|
// Parse the `<script>` in `.svelte` as TypeScript by adding the following configuration.
|
||||||
|
parserOptions: {
|
||||||
|
projectService: true,
|
||||||
|
parser: ts.parser,
|
||||||
|
extraFileExtensions: [".svelte"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ["**/*.js"],
|
||||||
|
extends: [ts.configs.disableTypeChecked],
|
||||||
|
},
|
||||||
|
]);
|
||||||
Generated
+2437
-1327
File diff suppressed because it is too large
Load Diff
+21
-21
@@ -32,38 +32,38 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
"idb-keyval": "^6.2.1",
|
"idb-keyval": "^6.2.2",
|
||||||
"reflect-metadata": "^0.2.2",
|
"reflect-metadata": "^0.2.2",
|
||||||
"source-code-pro": "github:adobe-fonts/source-code-pro#2.042R-u/1.062R-i/1.026R-vf",
|
"source-code-pro": "github:adobe-fonts/source-code-pro#2.042R-u/1.062R-i/1.026R-vf",
|
||||||
"source-sans": "github:adobe-fonts/source-sans#2.045R-ro%2F1.095R-it"
|
"source-sans": "github:adobe-fonts/source-sans#2.045R-ro%2F1.095R-it"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@eslint/compat": "^1.3.2",
|
||||||
|
"@eslint/eslintrc": "^3.3.1",
|
||||||
|
"@eslint/js": "^9.34.0",
|
||||||
"@sveltejs/vite-plugin-svelte": "^3.1.2",
|
"@sveltejs/vite-plugin-svelte": "^3.1.2",
|
||||||
"@types/node": "^22.6.1",
|
"@types/node": "^24.3.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.7.0",
|
|
||||||
"@typescript-eslint/parser": "^8.7.0",
|
|
||||||
"buffer": "^6.0.3",
|
"buffer": "^6.0.3",
|
||||||
"concurrently": "^9.0.1",
|
"concurrently": "^9.2.1",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-import-resolver-typescript": "^4.4.4",
|
||||||
"eslint-import-resolver-typescript": "^3.6.3",
|
"eslint-plugin-import": "^2.32.0",
|
||||||
"eslint-plugin-import": "^2.30.0",
|
"eslint-plugin-prettier": "^5.5.4",
|
||||||
"eslint-plugin-prettier": "^5.2.1",
|
"eslint-plugin-svelte": "^3.11.0",
|
||||||
"eslint-plugin-svelte": "^2.44.0",
|
"globals": "^16.3.0",
|
||||||
"postcss": "^8.4.47",
|
"postcss": "^8.5.6",
|
||||||
"prettier": "^3.3.3",
|
"prettier-plugin-svelte": "^3.4.0",
|
||||||
"prettier-plugin-svelte": "^3.2.6",
|
"prettier": "^3.6.2",
|
||||||
"process": "^0.11.10",
|
"process": "^0.11.10",
|
||||||
"rollup-plugin-license": "^3.6.0",
|
"rollup-plugin-license": "^3.6.0",
|
||||||
"sass": "1.78.0",
|
"sass": "^1.91.0",
|
||||||
"svelte": "^4.2.19",
|
"svelte-preprocess": "^6.0.3",
|
||||||
"svelte-preprocess": "^6.0.2",
|
"svelte": "4.2.20",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"typescript": "^5.6.2",
|
"typescript-eslint": "^8.41.0",
|
||||||
"vite": "^5.4.14",
|
"typescript": "^5.9.2",
|
||||||
"vite-multiple-assets": "1.3.1"
|
"vite-multiple-assets": "2.2.5",
|
||||||
|
"vite": "^5.4.19"
|
||||||
},
|
},
|
||||||
"//": "The dev dependency for `sass` can be removed once <https://github.com/sveltejs/svelte-preprocess/issues/656> is fixed, but meanwhile we have to",
|
|
||||||
"///": "list it here and pin it to 1.78.0 so `vite` and `svelte-preprocess` use it instead of 1.79+ which spams the console with deprecation warnings.",
|
|
||||||
"homepage": "https://graphite.rs",
|
"homepage": "https://graphite.rs",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@@ -146,8 +146,9 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 80 32"><line stroke="red" stroke-width="4px" x1="0" y1="36" x2="80" y2="-4" /></svg>\
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 80 32"><line stroke="red" stroke-width="4px" x1="0" y1="36" x2="80" y2="-4" /></svg>\
|
||||||
');
|
');
|
||||||
|
|
||||||
--color-transparent-checkered-background: linear-gradient(45deg, #cccccc 25%, transparent 25%, transparent 75%, #cccccc 75%),
|
--color-transparent-checkered-background:
|
||||||
linear-gradient(45deg, #cccccc 25%, transparent 25%, transparent 75%, #cccccc 75%), linear-gradient(#ffffff, #ffffff);
|
linear-gradient(45deg, #cccccc 25%, transparent 25%, transparent 75%, #cccccc 75%), linear-gradient(45deg, #cccccc 25%, transparent 25%, transparent 75%, #cccccc 75%),
|
||||||
|
linear-gradient(#ffffff, #ffffff);
|
||||||
--color-transparent-checkered-background-size: 16px 16px, 16px 16px, 16px 16px;
|
--color-transparent-checkered-background-size: 16px 16px, 16px 16px, 16px 16px;
|
||||||
--color-transparent-checkered-background-position: 0 0, 8px 8px, 8px 8px;
|
--color-transparent-checkered-background-position: 0 0, 8px 8px, 8px 8px;
|
||||||
--color-transparent-checkered-background-position-plus-one: 1px 1px, 9px 9px, 9px 9px;
|
--color-transparent-checkered-background-position-plus-one: 1px 1px, 9px 9px, 9px 9px;
|
||||||
|
|||||||
@@ -282,7 +282,6 @@
|
|||||||
const stop = gradientSpectrumInputWidget && activeIndex !== undefined && gradient?.atIndex(activeIndex);
|
const stop = gradientSpectrumInputWidget && activeIndex !== undefined && gradient?.atIndex(activeIndex);
|
||||||
if (stop && gradientSpectrumInputWidget instanceof SpectrumInput) {
|
if (stop && gradientSpectrumInputWidget instanceof SpectrumInput) {
|
||||||
stop.color = colorToEmit;
|
stop.color = colorToEmit;
|
||||||
gradient = gradient;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch("colorOrGradient", gradient || colorToEmit);
|
dispatch("colorOrGradient", gradient || colorToEmit);
|
||||||
@@ -452,7 +451,6 @@
|
|||||||
{gradient}
|
{gradient}
|
||||||
{disabled}
|
{disabled}
|
||||||
on:gradient={() => {
|
on:gradient={() => {
|
||||||
gradient = gradient;
|
|
||||||
if (gradient) dispatch("colorOrGradient", gradient);
|
if (gradient) dispatch("colorOrGradient", gradient);
|
||||||
}}
|
}}
|
||||||
on:activeMarkerIndexChange={gradientActiveMarkerIndexChange}
|
on:activeMarkerIndexChange={gradientActiveMarkerIndexChange}
|
||||||
@@ -596,7 +594,7 @@
|
|||||||
unit="%"
|
unit="%"
|
||||||
mode="Range"
|
mode="Range"
|
||||||
displayDecimalPlaces={1}
|
displayDecimalPlaces={1}
|
||||||
tooltip={`Scale of translucency, from transparent (0%) to opaque (100%), for the color's alpha channel`}
|
tooltip="Scale of translucency, from transparent (0%) to opaque (100%), for the color's alpha channel"
|
||||||
/>
|
/>
|
||||||
</LayoutRow>
|
</LayoutRow>
|
||||||
<LayoutRow class="leftover-space" />
|
<LayoutRow class="leftover-space" />
|
||||||
|
|||||||
@@ -329,11 +329,11 @@
|
|||||||
|
|
||||||
await tick();
|
await tick();
|
||||||
|
|
||||||
if (!textInput) {
|
if (!textInput) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// eslint-disable-next-line svelte/no-dom-manipulating
|
||||||
if (displayEditableTextbox.text === "") textInput.textContent = "";
|
if (displayEditableTextbox.text === "") textInput.textContent = "";
|
||||||
|
// eslint-disable-next-line svelte/no-dom-manipulating
|
||||||
else textInput.textContent = `${displayEditableTextbox.text}\n`;
|
else textInput.textContent = `${displayEditableTextbox.text}\n`;
|
||||||
|
|
||||||
// Make it so `maxHeight` is a multiple of `lineHeight`
|
// Make it so `maxHeight` is a multiple of `lineHeight`
|
||||||
@@ -508,7 +508,7 @@
|
|||||||
<LayoutRow class="spacer" />
|
<LayoutRow class="spacer" />
|
||||||
{/if}
|
{/if}
|
||||||
<LayoutCol class="tool-shelf-bottom-widgets">
|
<LayoutCol class="tool-shelf-bottom-widgets">
|
||||||
<WidgetLayout class={"working-colors-input-area"} layout={$document.workingColorsLayout} />
|
<WidgetLayout class="working-colors-input-area" layout={$document.workingColorsLayout} />
|
||||||
</LayoutCol>
|
</LayoutCol>
|
||||||
</LayoutCol>
|
</LayoutCol>
|
||||||
<LayoutCol class="viewport-container">
|
<LayoutCol class="viewport-container">
|
||||||
|
|||||||
@@ -547,7 +547,7 @@
|
|||||||
<div class="expand-arrow-none"></div>
|
<div class="expand-arrow-none"></div>
|
||||||
{/if}
|
{/if}
|
||||||
{#if listing.entry.clipped}
|
{#if listing.entry.clipped}
|
||||||
<IconLabel icon="Clipped" class="clipped-arrow" tooltip={"Clipping mask is active (Alt-click border to release)"} />
|
<IconLabel icon="Clipped" class="clipped-arrow" tooltip="Clipping mask is active (Alt-click border to release)" />
|
||||||
{/if}
|
{/if}
|
||||||
<div class="thumbnail">
|
<div class="thumbnail">
|
||||||
{#if $nodeGraph.thumbnails.has(listing.entry.id)}
|
{#if $nodeGraph.thumbnails.has(listing.entry.id)}
|
||||||
@@ -555,7 +555,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{#if listing.entry.name === "Artboard"}
|
{#if listing.entry.name === "Artboard"}
|
||||||
<IconLabel icon="Artboard" class={"layer-type-icon"} />
|
<IconLabel icon="Artboard" class="layer-type-icon" />
|
||||||
{/if}
|
{/if}
|
||||||
<LayoutRow class="layer-name" on:dblclick={() => onEditLayerName(listing)}>
|
<LayoutRow class="layer-name" on:dblclick={() => onEditLayerName(listing)}>
|
||||||
<input
|
<input
|
||||||
@@ -572,7 +572,7 @@
|
|||||||
</LayoutRow>
|
</LayoutRow>
|
||||||
{#if !listing.entry.unlocked || !listing.entry.parentsUnlocked}
|
{#if !listing.entry.unlocked || !listing.entry.parentsUnlocked}
|
||||||
<IconButton
|
<IconButton
|
||||||
class={"status-toggle"}
|
class="status-toggle"
|
||||||
classes={{ inherited: !listing.entry.parentsUnlocked }}
|
classes={{ inherited: !listing.entry.parentsUnlocked }}
|
||||||
action={(e) => (toggleLayerLock(listing.entry.id), e?.stopPropagation())}
|
action={(e) => (toggleLayerLock(listing.entry.id), e?.stopPropagation())}
|
||||||
size={24}
|
size={24}
|
||||||
@@ -582,7 +582,7 @@
|
|||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
<IconButton
|
<IconButton
|
||||||
class={"status-toggle"}
|
class="status-toggle"
|
||||||
classes={{ inherited: !listing.entry.parentsVisible }}
|
classes={{ inherited: !listing.entry.parentsVisible }}
|
||||||
action={(e) => (toggleNodeVisibilityLayerPanel(listing.entry.id), e?.stopPropagation())}
|
action={(e) => (toggleNodeVisibilityLayerPanel(listing.entry.id), e?.stopPropagation())}
|
||||||
size={24}
|
size={24}
|
||||||
|
|||||||
@@ -259,7 +259,7 @@
|
|||||||
|
|
||||||
<!-- Click target debug visualizations -->
|
<!-- Click target debug visualizations -->
|
||||||
{#if $nodeGraph.clickTargets}
|
{#if $nodeGraph.clickTargets}
|
||||||
<div class="click-targets" style:transform-origin={`0 0`} style:transform={`translate(${$nodeGraph.transform.x}px, ${$nodeGraph.transform.y}px) scale(${$nodeGraph.transform.scale})`}>
|
<div class="click-targets" style:transform-origin="0 0" style:transform={`translate(${$nodeGraph.transform.x}px, ${$nodeGraph.transform.y}px) scale(${$nodeGraph.transform.scale})`}>
|
||||||
<svg>
|
<svg>
|
||||||
{#each $nodeGraph.clickTargets.nodeClickTargets as pathString}
|
{#each $nodeGraph.clickTargets.nodeClickTargets as pathString}
|
||||||
<path class="node" d={pathString} />
|
<path class="node" d={pathString} />
|
||||||
@@ -283,14 +283,14 @@
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<!-- Thick vertical layer connection wires -->
|
<!-- Thick vertical layer connection wires -->
|
||||||
<div class="wires" style:transform-origin={`0 0`} style:transform={`translate(${$nodeGraph.transform.x}px, ${$nodeGraph.transform.y}px) scale(${$nodeGraph.transform.scale})`}>
|
<div class="wires" style:transform-origin="0 0" style:transform={`translate(${$nodeGraph.transform.x}px, ${$nodeGraph.transform.y}px) scale(${$nodeGraph.transform.scale})`}>
|
||||||
<svg>
|
<svg>
|
||||||
{#each $nodeGraph.wires.values() as map}
|
{#each $nodeGraph.wires.values() as map}
|
||||||
{#each map.values() as { pathString, dataType, thick, dashed }}
|
{#each map.values() as { pathString, dataType, thick, dashed }}
|
||||||
{#if thick}
|
{#if thick}
|
||||||
<path
|
<path
|
||||||
d={pathString}
|
d={pathString}
|
||||||
style:--data-line-width={"8px"}
|
style:--data-line-width="8px"
|
||||||
style:--data-color={`var(--color-data-${dataType.toLowerCase()})`}
|
style:--data-color={`var(--color-data-${dataType.toLowerCase()})`}
|
||||||
style:--data-color-dim={`var(--color-data-${dataType.toLowerCase()}-dim)`}
|
style:--data-color-dim={`var(--color-data-${dataType.toLowerCase()}-dim)`}
|
||||||
style:--data-dasharray={`3,${dashed ? 2 : 0}`}
|
style:--data-dasharray={`3,${dashed ? 2 : 0}`}
|
||||||
@@ -302,7 +302,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Import and Export connectors -->
|
<!-- Import and Export connectors -->
|
||||||
<div class="imports-and-exports" style:transform-origin={`0 0`} style:transform={`translate(${$nodeGraph.transform.x}px, ${$nodeGraph.transform.y}px) scale(${$nodeGraph.transform.scale})`}>
|
<div class="imports-and-exports" style:transform-origin="0 0" style:transform={`translate(${$nodeGraph.transform.x}px, ${$nodeGraph.transform.y}px) scale(${$nodeGraph.transform.scale})`}>
|
||||||
{#if $nodeGraph.updateImportsExports}
|
{#if $nodeGraph.updateImportsExports}
|
||||||
{#each $nodeGraph.updateImportsExports.imports as frontendOutput, index}
|
{#each $nodeGraph.updateImportsExports.imports as frontendOutput, index}
|
||||||
{#if frontendOutput}
|
{#if frontendOutput}
|
||||||
@@ -352,7 +352,7 @@
|
|||||||
{#if (hoveringImportIndex === index || editingNameImportIndex === index) && $nodeGraph.updateImportsExports.addImportExport}
|
{#if (hoveringImportIndex === index || editingNameImportIndex === index) && $nodeGraph.updateImportsExports.addImportExport}
|
||||||
<IconButton
|
<IconButton
|
||||||
size={16}
|
size={16}
|
||||||
icon={"Remove"}
|
icon="Remove"
|
||||||
class="remove-button-import"
|
class="remove-button-import"
|
||||||
data-index={index}
|
data-index={index}
|
||||||
data-import-text-edge
|
data-import-text-edge
|
||||||
@@ -411,7 +411,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
<IconButton
|
<IconButton
|
||||||
size={16}
|
size={16}
|
||||||
icon={"Remove"}
|
icon="Remove"
|
||||||
class="remove-button-export"
|
class="remove-button-export"
|
||||||
data-index={index}
|
data-index={index}
|
||||||
data-export-text-edge
|
data-export-text-edge
|
||||||
@@ -459,7 +459,7 @@
|
|||||||
style:--offset-left={($nodeGraph.updateImportsExports.exportPosition.x - 12) / 24}
|
style:--offset-left={($nodeGraph.updateImportsExports.exportPosition.x - 12) / 24}
|
||||||
style:--offset-top={($nodeGraph.updateImportsExports.exportPosition.y - 12) / 24 + $nodeGraph.updateImportsExports.exports.length}
|
style:--offset-top={($nodeGraph.updateImportsExports.exportPosition.y - 12) / 24 + $nodeGraph.updateImportsExports.exports.length}
|
||||||
>
|
>
|
||||||
<IconButton size={24} icon={"Add"} action={() => editor.handle.addSecondaryExport()} />
|
<IconButton size={24} icon="Add" action={() => editor.handle.addSecondaryExport()} />
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
@@ -482,7 +482,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Layers and nodes -->
|
<!-- Layers and nodes -->
|
||||||
<div class="layers-and-nodes" style:transform-origin={`0 0`} style:transform={`translate(${$nodeGraph.transform.x}px, ${$nodeGraph.transform.y}px) scale(${$nodeGraph.transform.scale})`}>
|
<div class="layers-and-nodes" style:transform-origin="0 0" style:transform={`translate(${$nodeGraph.transform.x}px, ${$nodeGraph.transform.y}px) scale(${$nodeGraph.transform.scale})`}>
|
||||||
<!-- Layers -->
|
<!-- Layers -->
|
||||||
{#each Array.from($nodeGraph.nodes)
|
{#each Array.from($nodeGraph.nodes)
|
||||||
.filter(([nodeId, node]) => node.isLayer && $nodeGraph.visibleNodes.has(nodeId))
|
.filter(([nodeId, node]) => node.isLayer && $nodeGraph.visibleNodes.has(nodeId))
|
||||||
@@ -589,7 +589,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="solo-drag-grip" title="Drag only this layer without pushing others outside the stack"></div>
|
<div class="solo-drag-grip" title="Drag only this layer without pushing others outside the stack"></div>
|
||||||
<IconButton
|
<IconButton
|
||||||
class={"visibility"}
|
class="visibility"
|
||||||
data-visibility-button
|
data-visibility-button
|
||||||
size={24}
|
size={24}
|
||||||
icon={node.visible ? "EyeVisible" : "EyeHidden"}
|
icon={node.visible ? "EyeVisible" : "EyeHidden"}
|
||||||
@@ -618,7 +618,7 @@
|
|||||||
{#if !thick}
|
{#if !thick}
|
||||||
<path
|
<path
|
||||||
d={pathString}
|
d={pathString}
|
||||||
style:--data-line-width={"2px"}
|
style:--data-line-width="2px"
|
||||||
style:--data-color={`var(--color-data-${dataType.toLowerCase()})`}
|
style:--data-color={`var(--color-data-${dataType.toLowerCase()})`}
|
||||||
style:--data-color-dim={`var(--color-data-${dataType.toLowerCase()}-dim)`}
|
style:--data-color-dim={`var(--color-data-${dataType.toLowerCase()}-dim)`}
|
||||||
style:--data-dasharray={`3,${dashed ? 2 : 0}`}
|
style:--data-dasharray={`3,${dashed ? 2 : 0}`}
|
||||||
|
|||||||
@@ -35,17 +35,17 @@
|
|||||||
editor.handle.setNodePinned(widgetData.id, !widgetData.pinned);
|
editor.handle.setNodePinned(widgetData.id, !widgetData.pinned);
|
||||||
e?.stopPropagation();
|
e?.stopPropagation();
|
||||||
}}
|
}}
|
||||||
class={"show-only-on-hover"}
|
class="show-only-on-hover"
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
icon={"Trash"}
|
icon="Trash"
|
||||||
tooltip={"Delete this node from the layer chain"}
|
tooltip="Delete this node from the layer chain"
|
||||||
size={24}
|
size={24}
|
||||||
action={(e) => {
|
action={(e) => {
|
||||||
editor.handle.deleteNode(widgetData.id);
|
editor.handle.deleteNode(widgetData.id);
|
||||||
e?.stopPropagation();
|
e?.stopPropagation();
|
||||||
}}
|
}}
|
||||||
class={"show-only-on-hover"}
|
class="show-only-on-hover"
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
icon={widgetData.visible ? "EyeVisible" : "EyeHidden"}
|
icon={widgetData.visible ? "EyeVisible" : "EyeHidden"}
|
||||||
|
|||||||
@@ -9,15 +9,17 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
{#each widgetData.tableWidgets as row}
|
<tbody>
|
||||||
<tr>
|
{#each widgetData.tableWidgets as row}
|
||||||
{#each row as cell}
|
<tr>
|
||||||
<td>
|
{#each row as cell}
|
||||||
<WidgetSpan widgetData={{ rowWidgets: [cell] }} {layoutTarget} />
|
<td>
|
||||||
</td>
|
<WidgetSpan widgetData={{ rowWidgets: [cell] }} {layoutTarget} />
|
||||||
{/each}
|
</td>
|
||||||
</tr>
|
{/each}
|
||||||
{/each}
|
</tr>
|
||||||
|
{/each}
|
||||||
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|||||||
@@ -188,7 +188,7 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<LayoutRow class={"curve-input"} classes={{ disabled, ...classes }} style={styleName} {styles} {tooltip}>
|
<LayoutRow class="curve-input" classes={{ disabled, ...classes }} style={styleName} {styles} {tooltip}>
|
||||||
<svg viewBox="0 0 1 1" on:pointermove={handlePointerMove} on:pointerup={handlePointerUp}>
|
<svg viewBox="0 0 1 1" on:pointermove={handlePointerMove} on:pointerup={handlePointerUp}>
|
||||||
{#each { length: GRID_SIZE - 1 } as _, i}
|
{#each { length: GRID_SIZE - 1 } as _, i}
|
||||||
<path class="grid" d={`M 0 ${(i + 1) / GRID_SIZE} L 1 ${(i + 1) / GRID_SIZE}`} />
|
<path class="grid" d={`M 0 ${(i + 1) / GRID_SIZE} L 1 ${(i + 1) / GRID_SIZE}`} />
|
||||||
|
|||||||
@@ -32,9 +32,7 @@
|
|||||||
let id = String(Math.random()).substring(2);
|
let id = String(Math.random()).substring(2);
|
||||||
let macKeyboardLayout = platformIsMac();
|
let macKeyboardLayout = platformIsMac();
|
||||||
|
|
||||||
$: inputValue = value;
|
$: dispatch("value", value);
|
||||||
|
|
||||||
$: dispatch("value", inputValue);
|
|
||||||
|
|
||||||
// Select (highlight) all the text. For technical reasons, it is necessary to pass the current text.
|
// Select (highlight) all the text. For technical reasons, it is necessary to pass the current text.
|
||||||
export function selectAllText(currentText: string) {
|
export function selectAllText(currentText: string) {
|
||||||
@@ -85,7 +83,7 @@
|
|||||||
{disabled}
|
{disabled}
|
||||||
{placeholder}
|
{placeholder}
|
||||||
bind:this={inputOrTextarea}
|
bind:this={inputOrTextarea}
|
||||||
bind:value={inputValue}
|
bind:value
|
||||||
on:focus={() => dispatch("textFocused")}
|
on:focus={() => dispatch("textFocused")}
|
||||||
on:blur={() => dispatch("textChanged")}
|
on:blur={() => dispatch("textChanged")}
|
||||||
on:change={() => dispatch("textChanged")}
|
on:change={() => dispatch("textChanged")}
|
||||||
@@ -104,7 +102,7 @@
|
|||||||
{spellcheck}
|
{spellcheck}
|
||||||
{disabled}
|
{disabled}
|
||||||
bind:this={inputOrTextarea}
|
bind:this={inputOrTextarea}
|
||||||
bind:value={inputValue}
|
bind:value
|
||||||
on:focus={() => dispatch("textFocused")}
|
on:focus={() => dispatch("textFocused")}
|
||||||
on:blur={() => dispatch("textChanged")}
|
on:blur={() => dispatch("textChanged")}
|
||||||
on:change={() => dispatch("textChanged")}
|
on:change={() => dispatch("textChanged")}
|
||||||
|
|||||||
@@ -640,7 +640,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<FieldInput
|
<FieldInput
|
||||||
class={"number-input"}
|
class="number-input"
|
||||||
classes={{
|
classes={{
|
||||||
increment: mode === "Increment",
|
increment: mode === "Increment",
|
||||||
range: mode === "Range",
|
range: mode === "Range",
|
||||||
|
|||||||
@@ -39,7 +39,7 @@
|
|||||||
export let requiresLock = false;
|
export let requiresLock = false;
|
||||||
export let textOnly = false;
|
export let textOnly = false;
|
||||||
|
|
||||||
$: keyboardLockInfoMessage = watchKeyboardLockInfoMessage(fullscreen.keyboardLockApiSupported);
|
$: keyboardLockInfoMessage = watchKeyboardLockInfoMessage($fullscreen.keyboardLockApiSupported);
|
||||||
|
|
||||||
$: displayKeyboardLockNotice = requiresLock && !$fullscreen.keyboardLocked;
|
$: displayKeyboardLockNotice = requiresLock && !$fullscreen.keyboardLocked;
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
<LayoutRow class="hint-groups">
|
<LayoutRow class="hint-groups">
|
||||||
{#each hintData as hintGroup, index (hintGroup)}
|
{#each hintData as hintGroup, index (hintGroup)}
|
||||||
{#if index !== 0}
|
{#if index !== 0}
|
||||||
<Separator type={"Section"} />
|
<Separator type="Section" />
|
||||||
{/if}
|
{/if}
|
||||||
{#each hintGroup as hint (hint)}
|
{#each hintGroup as hint (hint)}
|
||||||
{#if hint.plus}
|
{#if hint.plus}
|
||||||
|
|||||||
@@ -12,13 +12,13 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<LayoutRow class="window-button linux" tooltip="Minimize" on:click={() => editor.handle.appWindowMinimize()}>
|
<LayoutRow class="window-button linux" tooltip="Minimize" on:click={() => editor.handle.appWindowMinimize()}>
|
||||||
<IconLabel icon={"WindowButtonWinMinimize"} />
|
<IconLabel icon="WindowButtonWinMinimize" />
|
||||||
</LayoutRow>
|
</LayoutRow>
|
||||||
<LayoutRow class="window-button linux" tooltip={maximized ? "Unmaximize" : "Maximize"} on:click={() => editor.handle.appWindowMaximize()}>
|
<LayoutRow class="window-button linux" tooltip={maximized ? "Unmaximize" : "Maximize"} on:click={() => editor.handle.appWindowMaximize()}>
|
||||||
<IconLabel icon={maximized ? "WindowButtonWinRestoreDown" : "WindowButtonWinMaximize"} />
|
<IconLabel icon={maximized ? "WindowButtonWinRestoreDown" : "WindowButtonWinMaximize"} />
|
||||||
</LayoutRow>
|
</LayoutRow>
|
||||||
<LayoutRow class="window-button linux" tooltip="Close" on:click={() => editor.handle.appWindowClose()}>
|
<LayoutRow class="window-button linux" tooltip="Close" on:click={() => editor.handle.appWindowClose()}>
|
||||||
<IconLabel icon={"WindowButtonWinClose"} />
|
<IconLabel icon="WindowButtonWinClose" />
|
||||||
</LayoutRow>
|
</LayoutRow>
|
||||||
|
|
||||||
<style lang="scss" global>
|
<style lang="scss" global>
|
||||||
|
|||||||
@@ -12,13 +12,13 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<LayoutRow class="window-button windows" tooltip="Minimize" on:click={() => editor.handle.appWindowMinimize()}>
|
<LayoutRow class="window-button windows" tooltip="Minimize" on:click={() => editor.handle.appWindowMinimize()}>
|
||||||
<IconLabel icon={"WindowButtonWinMinimize"} />
|
<IconLabel icon="WindowButtonWinMinimize" />
|
||||||
</LayoutRow>
|
</LayoutRow>
|
||||||
<LayoutRow class="window-button windows" tooltip={maximized ? "Restore Down" : "Maximize"} on:click={() => editor.handle.appWindowMaximize()}>
|
<LayoutRow class="window-button windows" tooltip={maximized ? "Restore Down" : "Maximize"} on:click={() => editor.handle.appWindowMaximize()}>
|
||||||
<IconLabel icon={maximized ? "WindowButtonWinRestoreDown" : "WindowButtonWinMaximize"} />
|
<IconLabel icon={maximized ? "WindowButtonWinRestoreDown" : "WindowButtonWinMaximize"} />
|
||||||
</LayoutRow>
|
</LayoutRow>
|
||||||
<LayoutRow class="window-button windows" tooltip="Close" on:click={() => editor.handle.appWindowClose()}>
|
<LayoutRow class="window-button windows" tooltip="Close" on:click={() => editor.handle.appWindowClose()}>
|
||||||
<IconLabel icon={"WindowButtonWinClose"} />
|
<IconLabel icon="WindowButtonWinClose" />
|
||||||
</LayoutRow>
|
</LayoutRow>
|
||||||
|
|
||||||
<style lang="scss" global>
|
<style lang="scss" global>
|
||||||
|
|||||||
@@ -160,32 +160,34 @@
|
|||||||
</LayoutRow>
|
</LayoutRow>
|
||||||
<LayoutRow class="actions">
|
<LayoutRow class="actions">
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tbody>
|
||||||
<td>
|
<tr>
|
||||||
<TextButton label="New Document" icon="File" flush={true} action={() => editor.handle.newDocumentDialog()} />
|
<td>
|
||||||
</td>
|
<TextButton label="New Document" icon="File" flush={true} action={() => editor.handle.newDocumentDialog()} />
|
||||||
<td>
|
</td>
|
||||||
<UserInputLabel keysWithLabelsGroups={[[...platformModifiers(true), { key: "KeyN", label: "N" }]]} />
|
<td>
|
||||||
</td>
|
<UserInputLabel keysWithLabelsGroups={[[...platformModifiers(true), { key: "KeyN", label: "N" }]]} />
|
||||||
</tr>
|
</td>
|
||||||
<tr>
|
</tr>
|
||||||
<td>
|
<tr>
|
||||||
<TextButton label="Open Document" icon="Folder" flush={true} action={() => editor.handle.openDocument()} />
|
<td>
|
||||||
</td>
|
<TextButton label="Open Document" icon="Folder" flush={true} action={() => editor.handle.openDocument()} />
|
||||||
<td>
|
</td>
|
||||||
<UserInputLabel keysWithLabelsGroups={[[...platformModifiers(false), { key: "KeyO", label: "O" }]]} />
|
<td>
|
||||||
</td>
|
<UserInputLabel keysWithLabelsGroups={[[...platformModifiers(false), { key: "KeyO", label: "O" }]]} />
|
||||||
</tr>
|
</td>
|
||||||
<tr>
|
</tr>
|
||||||
<td colspan="2">
|
<tr>
|
||||||
<TextButton label="Open Demo Artwork" icon="Image" flush={true} action={() => editor.handle.demoArtworkDialog()} />
|
<td colspan="2">
|
||||||
</td>
|
<TextButton label="Open Demo Artwork" icon="Image" flush={true} action={() => editor.handle.demoArtworkDialog()} />
|
||||||
</tr>
|
</td>
|
||||||
<tr>
|
</tr>
|
||||||
<td colspan="2">
|
<tr>
|
||||||
<TextButton label="Support the Development Fund" icon="Heart" flush={true} action={() => editor.handle.visitUrl("https://graphite.rs/donate/")} />
|
<td colspan="2">
|
||||||
</td>
|
<TextButton label="Support the Development Fund" icon="Heart" flush={true} action={() => editor.handle.visitUrl("https://graphite.rs/donate/")} />
|
||||||
</tr>
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</LayoutRow>
|
</LayoutRow>
|
||||||
</LayoutCol>
|
</LayoutCol>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import { getContext } from "svelte";
|
import { getContext } from "svelte";
|
||||||
|
|
||||||
import type { Editor } from "@graphite/editor";
|
import type { Editor } from "@graphite/editor";
|
||||||
import type { FrontendDocumentDetails } from "@graphite/messages";
|
import type { OpenDocument } from "@graphite/messages";
|
||||||
import type { DialogState } from "@graphite/state-providers/dialog";
|
import type { DialogState } from "@graphite/state-providers/dialog";
|
||||||
import type { PortfolioState } from "@graphite/state-providers/portfolio";
|
import type { PortfolioState } from "@graphite/state-providers/portfolio";
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
|
|
||||||
$: documentPanel?.scrollTabIntoView($portfolio.activeDocumentIndex);
|
$: documentPanel?.scrollTabIntoView($portfolio.activeDocumentIndex);
|
||||||
|
|
||||||
$: documentTabLabels = $portfolio.documents.map((doc: FrontendDocumentDetails) => {
|
$: documentTabLabels = $portfolio.documents.map((doc: OpenDocument) => {
|
||||||
const name = doc.displayName;
|
const name = doc.displayName;
|
||||||
|
|
||||||
if (!editor.handle.inDevelopmentMode()) return { name };
|
if (!editor.handle.inDevelopmentMode()) return { name };
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ export async function initWasm() {
|
|||||||
if (wasmImport !== undefined) return;
|
if (wasmImport !== undefined) return;
|
||||||
|
|
||||||
// Import the WASM module JS bindings and wrap them in the panic proxy
|
// Import the WASM module JS bindings and wrap them in the panic proxy
|
||||||
// eslint-disable-next-line import/no-cycle
|
|
||||||
const wasm = await init();
|
const wasm = await init();
|
||||||
for (const [name, f] of Object.entries(wasm)) {
|
for (const [name, f] of Object.entries(wasm)) {
|
||||||
if (name.startsWith("__node_registry")) f();
|
if (name.startsWith("__node_registry")) f();
|
||||||
@@ -58,7 +57,7 @@ export function createEditor(): Editor {
|
|||||||
if (!demoArtwork) return;
|
if (!demoArtwork) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const url = new URL(`/${demoArtwork}.graphite`, document.location.href);
|
const url = new URL(`/demo-artwork/${demoArtwork}.graphite`, document.location.href);
|
||||||
const data = await fetch(url);
|
const data = await fetch(url);
|
||||||
if (!data.ok) throw new Error();
|
if (!data.ok) throw new Error();
|
||||||
|
|
||||||
|
|||||||
@@ -283,7 +283,7 @@ export function createInputManager(editor: Editor, dialog: DialogState, portfoli
|
|||||||
|
|
||||||
async function onBeforeUnload(e: BeforeUnloadEvent) {
|
async function onBeforeUnload(e: BeforeUnloadEvent) {
|
||||||
const activeDocument = get(portfolio).documents[get(portfolio).activeDocumentIndex];
|
const activeDocument = get(portfolio).documents[get(portfolio).activeDocumentIndex];
|
||||||
if (activeDocument && !activeDocument.isAutoSaved) editor.handle.triggerAutoSave(activeDocument.id);
|
if (activeDocument && !activeDocument.details.isAutoSaved) editor.handle.triggerAutoSave(activeDocument.id);
|
||||||
|
|
||||||
// Skip the message if the editor crashed, since work is already lost
|
// Skip the message if the editor crashed, since work is already lost
|
||||||
if (await editor.handle.hasCrashed()) return;
|
if (await editor.handle.hasCrashed()) return;
|
||||||
@@ -291,7 +291,7 @@ export function createInputManager(editor: Editor, dialog: DialogState, portfoli
|
|||||||
// Skip the message during development, since it's annoying when testing
|
// Skip the message during development, since it's annoying when testing
|
||||||
if (await editor.handle.inDevelopmentMode()) return;
|
if (await editor.handle.inDevelopmentMode()) return;
|
||||||
|
|
||||||
const allDocumentsSaved = get(portfolio).documents.reduce((acc, doc) => acc && doc.isSaved, true);
|
const allDocumentsSaved = get(portfolio).documents.reduce((acc, doc) => acc && doc.details.isSaved, true);
|
||||||
if (!allDocumentsSaved) {
|
if (!allDocumentsSaved) {
|
||||||
e.returnValue = "Unsaved work will be lost if the web browser tab is closed. Close anyway?";
|
e.returnValue = "Unsaved work will be lost if the web browser tab is closed. Close anyway?";
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import { get as getFromStore } from "svelte/store";
|
|||||||
|
|
||||||
import { type Editor } from "@graphite/editor";
|
import { type Editor } from "@graphite/editor";
|
||||||
import {
|
import {
|
||||||
TriggerIndexedDbWriteDocument,
|
TriggerPersistenceWriteDocument,
|
||||||
TriggerIndexedDbRemoveDocument,
|
TriggerPersistenceRemoveDocument,
|
||||||
TriggerSavePreferences,
|
TriggerSavePreferences,
|
||||||
TriggerLoadPreferences,
|
TriggerLoadPreferences,
|
||||||
TriggerLoadFirstAutoSaveDocument,
|
TriggerLoadFirstAutoSaveDocument,
|
||||||
@@ -27,23 +27,23 @@ export function createPersistenceManager(editor: Editor, portfolio: PortfolioSta
|
|||||||
await set("current_document_id", String(documentId), graphiteStore);
|
await set("current_document_id", String(documentId), graphiteStore);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function storeDocument(autoSaveDocument: TriggerIndexedDbWriteDocument) {
|
async function storeDocument(autoSaveDocument: TriggerPersistenceWriteDocument) {
|
||||||
await update<Record<string, TriggerIndexedDbWriteDocument>>(
|
await update<Record<string, TriggerPersistenceWriteDocument>>(
|
||||||
"documents",
|
"documents",
|
||||||
(old) => {
|
(old) => {
|
||||||
const documents = old || {};
|
const documents = old || {};
|
||||||
documents[autoSaveDocument.details.id] = autoSaveDocument;
|
documents[autoSaveDocument.documentId] = autoSaveDocument;
|
||||||
return documents;
|
return documents;
|
||||||
},
|
},
|
||||||
graphiteStore,
|
graphiteStore,
|
||||||
);
|
);
|
||||||
|
|
||||||
await storeDocumentOrder();
|
await storeDocumentOrder();
|
||||||
await storeCurrentDocumentId(autoSaveDocument.details.id);
|
await storeCurrentDocumentId(autoSaveDocument.documentId);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function removeDocument(id: string) {
|
async function removeDocument(id: string) {
|
||||||
await update<Record<string, TriggerIndexedDbWriteDocument>>(
|
await update<Record<string, TriggerPersistenceWriteDocument>>(
|
||||||
"documents",
|
"documents",
|
||||||
(old) => {
|
(old) => {
|
||||||
const documents = old || {};
|
const documents = old || {};
|
||||||
@@ -77,7 +77,7 @@ export function createPersistenceManager(editor: Editor, portfolio: PortfolioSta
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function loadFirstDocument() {
|
async function loadFirstDocument() {
|
||||||
const previouslySavedDocuments = await get<Record<string, TriggerIndexedDbWriteDocument>>("documents", graphiteStore);
|
const previouslySavedDocuments = await get<Record<string, TriggerPersistenceWriteDocument>>("documents", graphiteStore);
|
||||||
const documentOrder = await get<string[]>("documents_tab_order", graphiteStore);
|
const documentOrder = await get<string[]>("documents_tab_order", graphiteStore);
|
||||||
const currentDocumentId = await get<string>("current_document_id", graphiteStore);
|
const currentDocumentId = await get<string>("current_document_id", graphiteStore);
|
||||||
if (!previouslySavedDocuments || !documentOrder) return;
|
if (!previouslySavedDocuments || !documentOrder) return;
|
||||||
@@ -86,20 +86,20 @@ export function createPersistenceManager(editor: Editor, portfolio: PortfolioSta
|
|||||||
|
|
||||||
if (currentDocumentId && currentDocumentId in previouslySavedDocuments) {
|
if (currentDocumentId && currentDocumentId in previouslySavedDocuments) {
|
||||||
const doc = previouslySavedDocuments[currentDocumentId];
|
const doc = previouslySavedDocuments[currentDocumentId];
|
||||||
editor.handle.openAutoSavedDocument(BigInt(doc.details.id), doc.details.name, doc.details.isSaved, doc.document, false);
|
editor.handle.openAutoSavedDocument(BigInt(doc.documentId), doc.details.name, doc.details.isSaved, doc.document, false);
|
||||||
editor.handle.selectDocument(BigInt(currentDocumentId));
|
editor.handle.selectDocument(BigInt(currentDocumentId));
|
||||||
} else {
|
} else {
|
||||||
const len = orderedSavedDocuments.length;
|
const len = orderedSavedDocuments.length;
|
||||||
if (len > 0) {
|
if (len > 0) {
|
||||||
const doc = orderedSavedDocuments[len - 1];
|
const doc = orderedSavedDocuments[len - 1];
|
||||||
editor.handle.openAutoSavedDocument(BigInt(doc.details.id), doc.details.name, doc.details.isSaved, doc.document, false);
|
editor.handle.openAutoSavedDocument(BigInt(doc.documentId), doc.details.name, doc.details.isSaved, doc.document, false);
|
||||||
editor.handle.selectDocument(BigInt(doc.details.id));
|
editor.handle.selectDocument(BigInt(doc.documentId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadRestDocuments() {
|
async function loadRestDocuments() {
|
||||||
const previouslySavedDocuments = await get<Record<string, TriggerIndexedDbWriteDocument>>("documents", graphiteStore);
|
const previouslySavedDocuments = await get<Record<string, TriggerPersistenceWriteDocument>>("documents", graphiteStore);
|
||||||
const documentOrder = await get<string[]>("documents_tab_order", graphiteStore);
|
const documentOrder = await get<string[]>("documents_tab_order", graphiteStore);
|
||||||
const currentDocumentId = await get<string>("current_document_id", graphiteStore);
|
const currentDocumentId = await get<string>("current_document_id", graphiteStore);
|
||||||
if (!previouslySavedDocuments || !documentOrder) return;
|
if (!previouslySavedDocuments || !documentOrder) return;
|
||||||
@@ -107,19 +107,19 @@ export function createPersistenceManager(editor: Editor, portfolio: PortfolioSta
|
|||||||
const orderedSavedDocuments = documentOrder.flatMap((id) => (previouslySavedDocuments[id] ? [previouslySavedDocuments[id]] : []));
|
const orderedSavedDocuments = documentOrder.flatMap((id) => (previouslySavedDocuments[id] ? [previouslySavedDocuments[id]] : []));
|
||||||
|
|
||||||
if (currentDocumentId) {
|
if (currentDocumentId) {
|
||||||
const currentIndex = orderedSavedDocuments.findIndex((doc) => doc.details.id === currentDocumentId);
|
const currentIndex = orderedSavedDocuments.findIndex((doc) => doc.documentId === currentDocumentId);
|
||||||
const beforeCurrentIndex = currentIndex - 1;
|
const beforeCurrentIndex = currentIndex - 1;
|
||||||
const afterCurrentIndex = currentIndex + 1;
|
const afterCurrentIndex = currentIndex + 1;
|
||||||
|
|
||||||
for (let i = beforeCurrentIndex; i >= 0; i--) {
|
for (let i = beforeCurrentIndex; i >= 0; i--) {
|
||||||
const { document, details } = orderedSavedDocuments[i];
|
const { documentId, document, details } = orderedSavedDocuments[i];
|
||||||
const { id, name, isSaved } = details;
|
const { name, isSaved } = details;
|
||||||
editor.handle.openAutoSavedDocument(BigInt(id), name, isSaved, document, true);
|
editor.handle.openAutoSavedDocument(BigInt(documentId), name, isSaved, document, true);
|
||||||
}
|
}
|
||||||
for (let i = afterCurrentIndex; i < orderedSavedDocuments.length; i++) {
|
for (let i = afterCurrentIndex; i < orderedSavedDocuments.length; i++) {
|
||||||
const { document, details } = orderedSavedDocuments[i];
|
const { documentId, document, details } = orderedSavedDocuments[i];
|
||||||
const { id, name, isSaved } = details;
|
const { name, isSaved } = details;
|
||||||
editor.handle.openAutoSavedDocument(BigInt(id), name, isSaved, document, false);
|
editor.handle.openAutoSavedDocument(BigInt(documentId), name, isSaved, document, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
editor.handle.selectDocument(BigInt(currentDocumentId));
|
editor.handle.selectDocument(BigInt(currentDocumentId));
|
||||||
@@ -127,13 +127,13 @@ export function createPersistenceManager(editor: Editor, portfolio: PortfolioSta
|
|||||||
const length = orderedSavedDocuments.length;
|
const length = orderedSavedDocuments.length;
|
||||||
|
|
||||||
for (let i = length - 2; i >= 0; i--) {
|
for (let i = length - 2; i >= 0; i--) {
|
||||||
const { document, details } = orderedSavedDocuments[i];
|
const { documentId, document, details } = orderedSavedDocuments[i];
|
||||||
const { id, name, isSaved } = details;
|
const { name, isSaved } = details;
|
||||||
editor.handle.openAutoSavedDocument(BigInt(id), name, isSaved, document, true);
|
editor.handle.openAutoSavedDocument(BigInt(documentId), name, isSaved, document, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (length > 0) {
|
if (length > 0) {
|
||||||
const id = orderedSavedDocuments[length - 1].details.id;
|
const id = orderedSavedDocuments[length - 1].documentId;
|
||||||
editor.handle.selectDocument(BigInt(id));
|
editor.handle.selectDocument(BigInt(id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -161,10 +161,10 @@ export function createPersistenceManager(editor: Editor, portfolio: PortfolioSta
|
|||||||
editor.subscriptions.subscribeJsMessage(TriggerLoadPreferences, async () => {
|
editor.subscriptions.subscribeJsMessage(TriggerLoadPreferences, async () => {
|
||||||
await loadPreferences();
|
await loadPreferences();
|
||||||
});
|
});
|
||||||
editor.subscriptions.subscribeJsMessage(TriggerIndexedDbWriteDocument, async (autoSaveDocument) => {
|
editor.subscriptions.subscribeJsMessage(TriggerPersistenceWriteDocument, async (autoSaveDocument) => {
|
||||||
await storeDocument(autoSaveDocument);
|
await storeDocument(autoSaveDocument);
|
||||||
});
|
});
|
||||||
editor.subscriptions.subscribeJsMessage(TriggerIndexedDbRemoveDocument, async (removeAutoSaveDocument) => {
|
editor.subscriptions.subscribeJsMessage(TriggerPersistenceRemoveDocument, async (removeAutoSaveDocument) => {
|
||||||
await removeDocument(removeAutoSaveDocument.documentId);
|
await removeDocument(removeAutoSaveDocument.documentId);
|
||||||
});
|
});
|
||||||
editor.subscriptions.subscribeJsMessage(TriggerLoadFirstAutoSaveDocument, async () => {
|
editor.subscriptions.subscribeJsMessage(TriggerLoadFirstAutoSaveDocument, async () => {
|
||||||
@@ -175,7 +175,7 @@ export function createPersistenceManager(editor: Editor, portfolio: PortfolioSta
|
|||||||
});
|
});
|
||||||
editor.subscriptions.subscribeJsMessage(TriggerSaveActiveDocument, async (triggerSaveActiveDocument) => {
|
editor.subscriptions.subscribeJsMessage(TriggerSaveActiveDocument, async (triggerSaveActiveDocument) => {
|
||||||
const documentId = String(triggerSaveActiveDocument.documentId);
|
const documentId = String(triggerSaveActiveDocument.documentId);
|
||||||
const previouslySavedDocuments = await get<Record<string, TriggerIndexedDbWriteDocument>>("documents", graphiteStore);
|
const previouslySavedDocuments = await get<Record<string, TriggerPersistenceWriteDocument>>("documents", graphiteStore);
|
||||||
if (!previouslySavedDocuments) return;
|
if (!previouslySavedDocuments) return;
|
||||||
if (documentId in previouslySavedDocuments) {
|
if (documentId in previouslySavedDocuments) {
|
||||||
await storeCurrentDocumentId(documentId);
|
await storeCurrentDocumentId(documentId);
|
||||||
|
|||||||
+21
-35
@@ -1,5 +1,4 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
/* eslint-disable max-classes-per-file */
|
|
||||||
|
|
||||||
import { Transform, Type, plainToClass } from "class-transformer";
|
import { Transform, Type, plainToClass } from "class-transformer";
|
||||||
|
|
||||||
@@ -130,37 +129,36 @@ export class UpdateNodeGraphSelection extends JsMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class UpdateOpenDocumentsList extends JsMessage {
|
export class UpdateOpenDocumentsList extends JsMessage {
|
||||||
@Type(() => FrontendDocumentDetails)
|
@Type(() => OpenDocument)
|
||||||
readonly openDocuments!: FrontendDocumentDetails[];
|
readonly openDocuments!: OpenDocument[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UpdateWirePathInProgress extends JsMessage {
|
export class UpdateWirePathInProgress extends JsMessage {
|
||||||
readonly wirePath!: WirePath | undefined;
|
readonly wirePath!: WirePath | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allows the auto save system to use a string for the id rather than a BigInt.
|
export class OpenDocument {
|
||||||
// IndexedDb does not allow for BigInts as primary keys.
|
readonly id!: bigint;
|
||||||
// TypeScript does not allow subclasses to change the type of class variables in subclasses.
|
@Type(() => DocumentDetails)
|
||||||
// It is an abstract class to point out that it should not be instantiated directly.
|
readonly details!: DocumentDetails;
|
||||||
export abstract class DocumentDetails {
|
|
||||||
|
get displayName(): string {
|
||||||
|
return this.details.displayName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DocumentDetails {
|
||||||
readonly name!: string;
|
readonly name!: string;
|
||||||
|
|
||||||
readonly isAutoSaved!: boolean;
|
readonly isAutoSaved!: boolean;
|
||||||
|
|
||||||
readonly isSaved!: boolean;
|
readonly isSaved!: boolean;
|
||||||
|
|
||||||
// This field must be provided by the subclass implementation
|
|
||||||
// readonly id!: bigint | string;
|
|
||||||
|
|
||||||
get displayName(): string {
|
get displayName(): string {
|
||||||
return `${this.name}${this.isSaved ? "" : "*"}`;
|
return `${this.name}${this.isSaved ? "" : "*"}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class FrontendDocumentDetails extends DocumentDetails {
|
|
||||||
readonly id!: bigint;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Box {
|
export class Box {
|
||||||
readonly startX!: number;
|
readonly startX!: number;
|
||||||
|
|
||||||
@@ -278,21 +276,20 @@ export class WireUpdate {
|
|||||||
readonly wirePathUpdate!: WirePath | undefined;
|
readonly wirePathUpdate!: WirePath | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class IndexedDbDocumentDetails extends DocumentDetails {
|
export class TriggerPersistenceWriteDocument extends JsMessage {
|
||||||
|
// Use a string since IndexedDB can not use BigInts for keys
|
||||||
@Transform(({ value }: { value: bigint }) => value.toString())
|
@Transform(({ value }: { value: bigint }) => value.toString())
|
||||||
id!: string;
|
documentId!: string;
|
||||||
}
|
|
||||||
|
|
||||||
export class TriggerIndexedDbWriteDocument extends JsMessage {
|
|
||||||
document!: string;
|
document!: string;
|
||||||
|
|
||||||
@Type(() => IndexedDbDocumentDetails)
|
@Type(() => DocumentDetails)
|
||||||
details!: IndexedDbDocumentDetails;
|
details!: DocumentDetails;
|
||||||
|
|
||||||
version!: string;
|
version!: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TriggerIndexedDbRemoveDocument extends JsMessage {
|
export class TriggerPersistenceRemoveDocument extends JsMessage {
|
||||||
// Use a string since IndexedDB can not use BigInts for keys
|
// Use a string since IndexedDB can not use BigInts for keys
|
||||||
@Transform(({ value }: { value: bigint }) => value.toString())
|
@Transform(({ value }: { value: bigint }) => value.toString())
|
||||||
documentId!: string;
|
documentId!: string;
|
||||||
@@ -1400,7 +1397,6 @@ export class Widget {
|
|||||||
widgetId!: bigint;
|
widgetId!: bigint;
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
function hoistWidgetHolder(widgetHolder: any): Widget {
|
function hoistWidgetHolder(widgetHolder: any): Widget {
|
||||||
const kind = Object.keys(widgetHolder.widget)[0];
|
const kind = Object.keys(widgetHolder.widget)[0];
|
||||||
const props = widgetHolder.widget[kind];
|
const props = widgetHolder.widget[kind];
|
||||||
@@ -1415,7 +1411,6 @@ function hoistWidgetHolder(widgetHolder: any): Widget {
|
|||||||
return plainToClass(Widget, { props, widgetId });
|
return plainToClass(Widget, { props, widgetId });
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
function hoistWidgetHolders(widgetHolders: any[]): Widget[] {
|
function hoistWidgetHolders(widgetHolders: any[]): Widget[] {
|
||||||
return widgetHolders.map(hoistWidgetHolder);
|
return widgetHolders.map(hoistWidgetHolder);
|
||||||
}
|
}
|
||||||
@@ -1431,7 +1426,6 @@ export class WidgetDiffUpdate extends JsMessage {
|
|||||||
layoutTarget!: unknown;
|
layoutTarget!: unknown;
|
||||||
|
|
||||||
// TODO: Replace `any` with correct typing
|
// TODO: Replace `any` with correct typing
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
@Transform(({ value }: { value: any }) => createWidgetDiff(value))
|
@Transform(({ value }: { value: any }) => createWidgetDiff(value))
|
||||||
diff!: WidgetDiff[];
|
diff!: WidgetDiff[];
|
||||||
}
|
}
|
||||||
@@ -1466,7 +1460,6 @@ export function patchWidgetLayout(layout: /* &mut */ WidgetLayout, updates: Widg
|
|||||||
return targetLayout;
|
return targetLayout;
|
||||||
}
|
}
|
||||||
// This is a path traversal so we can assume from the backend that it exists
|
// This is a path traversal so we can assume from the backend that it exists
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
||||||
if (targetLayout && "action" in targetLayout) return targetLayout.children![index];
|
if (targetLayout && "action" in targetLayout) return targetLayout.children![index];
|
||||||
|
|
||||||
return targetLayout?.[index];
|
return targetLayout?.[index];
|
||||||
@@ -1487,7 +1480,6 @@ export function patchWidgetLayout(layout: /* &mut */ WidgetLayout, updates: Widg
|
|||||||
diffObject.length = 0;
|
diffObject.length = 0;
|
||||||
}
|
}
|
||||||
// Remove all of the keys from the old object
|
// Remove all of the keys from the old object
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
Object.keys(diffObject).forEach((key) => delete (diffObject as any)[key]);
|
Object.keys(diffObject).forEach((key) => delete (diffObject as any)[key]);
|
||||||
|
|
||||||
// Assign keys to the new object
|
// Assign keys to the new object
|
||||||
@@ -1520,7 +1512,6 @@ export function isWidgetSection(layoutRow: LayoutGroup): layoutRow is WidgetSect
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Unpacking rust types to more usable type in the frontend
|
// Unpacking rust types to more usable type in the frontend
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
function createWidgetDiff(diffs: any[]): WidgetDiff[] {
|
function createWidgetDiff(diffs: any[]): WidgetDiff[] {
|
||||||
return diffs.map((diff) => {
|
return diffs.map((diff) => {
|
||||||
const { widgetPath, newValue } = diff;
|
const { widgetPath, newValue } = diff;
|
||||||
@@ -1539,7 +1530,6 @@ function createWidgetDiff(diffs: any[]): WidgetDiff[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Unpacking a layout group
|
// Unpacking a layout group
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
function createLayoutGroup(layoutGroup: any): LayoutGroup {
|
function createLayoutGroup(layoutGroup: any): LayoutGroup {
|
||||||
if (layoutGroup.column) {
|
if (layoutGroup.column) {
|
||||||
const columnWidgets = hoistWidgetHolders(layoutGroup.column.columnWidgets);
|
const columnWidgets = hoistWidgetHolders(layoutGroup.column.columnWidgets);
|
||||||
@@ -1597,7 +1587,6 @@ export class UpdateMenuBarLayout extends JsMessage {
|
|||||||
layoutTarget!: unknown;
|
layoutTarget!: unknown;
|
||||||
|
|
||||||
// TODO: Replace `any` with correct typing
|
// TODO: Replace `any` with correct typing
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
@Transform(({ value }: { value: any }) => createMenuLayout(value))
|
@Transform(({ value }: { value: any }) => createMenuLayout(value))
|
||||||
layout!: MenuBarEntry[];
|
layout!: MenuBarEntry[];
|
||||||
}
|
}
|
||||||
@@ -1614,14 +1603,12 @@ export class UpdateToolShelfLayout extends WidgetDiffUpdate {}
|
|||||||
|
|
||||||
export class UpdateWorkingColorsLayout extends WidgetDiffUpdate {}
|
export class UpdateWorkingColorsLayout extends WidgetDiffUpdate {}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
function createMenuLayout(menuBarEntry: any[]): MenuBarEntry[] {
|
function createMenuLayout(menuBarEntry: any[]): MenuBarEntry[] {
|
||||||
return menuBarEntry.map((entry) => ({
|
return menuBarEntry.map((entry) => ({
|
||||||
...entry,
|
...entry,
|
||||||
children: createMenuLayoutRecursive(entry.children),
|
children: createMenuLayoutRecursive(entry.children),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
function createMenuLayoutRecursive(children: any[][]): MenuBarEntry[][] {
|
function createMenuLayoutRecursive(children: any[][]): MenuBarEntry[][] {
|
||||||
return children.map((groups) =>
|
return children.map((groups) =>
|
||||||
groups.map((entry) => ({
|
groups.map((entry) => ({
|
||||||
@@ -1634,7 +1621,6 @@ function createMenuLayoutRecursive(children: any[][]): MenuBarEntry[][] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// `any` is used since the type of the object should be known from the Rust side
|
// `any` is used since the type of the object should be known from the Rust side
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
type JSMessageFactory = (data: any, wasm: WebAssembly.Memory, handle: EditorHandle) => JsMessage;
|
type JSMessageFactory = (data: any, wasm: WebAssembly.Memory, handle: EditorHandle) => JsMessage;
|
||||||
type MessageMaker = typeof JsMessage | JSMessageFactory;
|
type MessageMaker = typeof JsMessage | JSMessageFactory;
|
||||||
|
|
||||||
@@ -1655,8 +1641,8 @@ export const messageMakers: Record<string, MessageMaker> = {
|
|||||||
TriggerFetchAndOpenDocument,
|
TriggerFetchAndOpenDocument,
|
||||||
TriggerFontLoad,
|
TriggerFontLoad,
|
||||||
TriggerImport,
|
TriggerImport,
|
||||||
TriggerIndexedDbRemoveDocument,
|
TriggerPersistenceRemoveDocument,
|
||||||
TriggerIndexedDbWriteDocument,
|
TriggerPersistenceWriteDocument,
|
||||||
TriggerLoadFirstAutoSaveDocument,
|
TriggerLoadFirstAutoSaveDocument,
|
||||||
TriggerLoadPreferences,
|
TriggerLoadPreferences,
|
||||||
TriggerLoadRestAutoSaveDocuments,
|
TriggerLoadRestAutoSaveDocuments,
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
/* eslint-disable max-classes-per-file */
|
|
||||||
|
|
||||||
import { writable } from "svelte/store";
|
import { writable } from "svelte/store";
|
||||||
|
|
||||||
import { type Editor } from "@graphite/editor";
|
import { type Editor } from "@graphite/editor";
|
||||||
import { type AppWindowPlatform, UpdatePlatform, UpdateViewportHolePunch, UpdateWindowState } from "@graphite/messages";
|
import { type AppWindowPlatform, UpdatePlatform, UpdateViewportHolePunch, UpdateWindowState } from "@graphite/messages";
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
||||||
export function createAppWindowState(editor: Editor) {
|
export function createAppWindowState(editor: Editor) {
|
||||||
const { subscribe, update } = writable({
|
const { subscribe, update } = writable({
|
||||||
platform: "Web" as AppWindowPlatform,
|
platform: "Web" as AppWindowPlatform,
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import {
|
|||||||
} from "@graphite/messages";
|
} from "@graphite/messages";
|
||||||
import { type IconName } from "@graphite/utility-functions/icons";
|
import { type IconName } from "@graphite/utility-functions/icons";
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
||||||
export function createDialogState(editor: Editor) {
|
export function createDialogState(editor: Editor) {
|
||||||
const { subscribe, update } = writable({
|
const { subscribe, update } = writable({
|
||||||
visible: false,
|
visible: false,
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import {
|
|||||||
UpdateGraphFadeArtwork,
|
UpdateGraphFadeArtwork,
|
||||||
} from "@graphite/messages";
|
} from "@graphite/messages";
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
||||||
export function createDocumentState(editor: Editor) {
|
export function createDocumentState(editor: Editor) {
|
||||||
const state = writable({
|
const state = writable({
|
||||||
// Layouts
|
// Layouts
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { writable } from "svelte/store";
|
|||||||
import { type Editor } from "@graphite/editor";
|
import { type Editor } from "@graphite/editor";
|
||||||
import { TriggerFontLoad } from "@graphite/messages";
|
import { TriggerFontLoad } from "@graphite/messages";
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
||||||
export function createFontsState(editor: Editor) {
|
export function createFontsState(editor: Editor) {
|
||||||
// TODO: Do some code cleanup to remove the need for this empty store
|
// TODO: Do some code cleanup to remove the need for this empty store
|
||||||
const { subscribe } = writable({});
|
const { subscribe } = writable({});
|
||||||
|
|||||||
@@ -2,11 +2,15 @@ import { writable } from "svelte/store";
|
|||||||
|
|
||||||
import { type Editor } from "@graphite/editor";
|
import { type Editor } from "@graphite/editor";
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
||||||
export function createFullscreenState(_: Editor) {
|
export function createFullscreenState(_: Editor) {
|
||||||
|
// Experimental Keyboard API: https://developer.mozilla.org/en-US/docs/Web/API/Navigator/keyboard
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const keyboardLockApiSupported: Readonly<boolean> = "keyboard" in navigator && (navigator as any).keyboard && "lock" in (navigator as any).keyboard;
|
||||||
|
|
||||||
const { subscribe, update } = writable({
|
const { subscribe, update } = writable({
|
||||||
windowFullscreen: false,
|
windowFullscreen: false,
|
||||||
keyboardLocked: false,
|
keyboardLocked: false,
|
||||||
|
keyboardLockApiSupported,
|
||||||
});
|
});
|
||||||
|
|
||||||
function fullscreenModeChanged() {
|
function fullscreenModeChanged() {
|
||||||
@@ -46,17 +50,12 @@ export function createFullscreenState(_: Editor) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Experimental Keyboard API: https://developer.mozilla.org/en-US/docs/Web/API/Navigator/keyboard
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
const keyboardLockApiSupported: Readonly<boolean> = "keyboard" in navigator && (navigator as any).keyboard && "lock" in (navigator as any).keyboard;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
subscribe,
|
subscribe,
|
||||||
fullscreenModeChanged,
|
fullscreenModeChanged,
|
||||||
enterFullscreen,
|
enterFullscreen,
|
||||||
exitFullscreen,
|
exitFullscreen,
|
||||||
toggleFullscreen,
|
toggleFullscreen,
|
||||||
keyboardLockApiSupported,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
export type FullscreenState = ReturnType<typeof createFullscreenState>;
|
export type FullscreenState = ReturnType<typeof createFullscreenState>;
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ import {
|
|||||||
UpdateWirePathInProgress,
|
UpdateWirePathInProgress,
|
||||||
} from "@graphite/messages";
|
} from "@graphite/messages";
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
||||||
export function createNodeGraphState(editor: Editor) {
|
export function createNodeGraphState(editor: Editor) {
|
||||||
const { subscribe, update } = writable({
|
const { subscribe, update } = writable({
|
||||||
box: undefined as Box | undefined,
|
box: undefined as Box | undefined,
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
/* eslint-disable max-classes-per-file */
|
|
||||||
|
|
||||||
import { writable } from "svelte/store";
|
import { writable } from "svelte/store";
|
||||||
|
|
||||||
import { type Editor } from "@graphite/editor";
|
import { type Editor } from "@graphite/editor";
|
||||||
|
import type { OpenDocument } from "@graphite/messages";
|
||||||
import {
|
import {
|
||||||
type FrontendDocumentDetails,
|
|
||||||
TriggerFetchAndOpenDocument,
|
TriggerFetchAndOpenDocument,
|
||||||
TriggerSaveDocument,
|
TriggerSaveDocument,
|
||||||
TriggerExportImage,
|
TriggerExportImage,
|
||||||
@@ -20,11 +18,10 @@ import {
|
|||||||
import { downloadFile, downloadFileBlob, upload } from "@graphite/utility-functions/files";
|
import { downloadFile, downloadFileBlob, upload } from "@graphite/utility-functions/files";
|
||||||
import { extractPixelData, rasterizeSVG } from "@graphite/utility-functions/rasterization";
|
import { extractPixelData, rasterizeSVG } from "@graphite/utility-functions/rasterization";
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
||||||
export function createPortfolioState(editor: Editor) {
|
export function createPortfolioState(editor: Editor) {
|
||||||
const { subscribe, update } = writable({
|
const { subscribe, update } = writable({
|
||||||
unsaved: false,
|
unsaved: false,
|
||||||
documents: [] as FrontendDocumentDetails[],
|
documents: [] as OpenDocument[],
|
||||||
activeDocumentIndex: 0,
|
activeDocumentIndex: 0,
|
||||||
dataPanelOpen: false,
|
dataPanelOpen: false,
|
||||||
propertiesPanelOpen: true,
|
propertiesPanelOpen: true,
|
||||||
@@ -49,7 +46,7 @@ export function createPortfolioState(editor: Editor) {
|
|||||||
editor.subscriptions.subscribeJsMessage(TriggerFetchAndOpenDocument, async (triggerFetchAndOpenDocument) => {
|
editor.subscriptions.subscribeJsMessage(TriggerFetchAndOpenDocument, async (triggerFetchAndOpenDocument) => {
|
||||||
try {
|
try {
|
||||||
const { name, filename } = triggerFetchAndOpenDocument;
|
const { name, filename } = triggerFetchAndOpenDocument;
|
||||||
const url = new URL(filename, document.location.href);
|
const url = new URL(`demo-artwork/${filename}`, document.location.href);
|
||||||
const data = await fetch(url);
|
const data = await fetch(url);
|
||||||
const content = await data.text();
|
const content = await data.text();
|
||||||
|
|
||||||
@@ -90,7 +87,7 @@ export function createPortfolioState(editor: Editor) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const imageData = await extractPixelData(new Blob([data.content.data], { type: data.type }));
|
const imageData = await extractPixelData(new Blob([new Uint8Array(data.content.data)], { type: data.type }));
|
||||||
editor.handle.pasteImage(data.filename, new Uint8Array(imageData.data), imageData.width, imageData.height);
|
editor.handle.pasteImage(data.filename, new Uint8Array(imageData.data), imageData.width, imageData.height);
|
||||||
});
|
});
|
||||||
editor.subscriptions.subscribeJsMessage(TriggerSaveDocument, (triggerSaveDocument) => {
|
editor.subscriptions.subscribeJsMessage(TriggerSaveDocument, (triggerSaveDocument) => {
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ type JsMessageCallback<T extends JsMessage> = (messageData: T) => void;
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
type JsMessageCallbackMap = Record<string, JsMessageCallback<any> | undefined>;
|
type JsMessageCallbackMap = Record<string, JsMessageCallback<any> | undefined>;
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
||||||
export function createSubscriptionRouter() {
|
export function createSubscriptionRouter() {
|
||||||
const subscriptions: JsMessageCallbackMap = {};
|
const subscriptions: JsMessageCallbackMap = {};
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ export type DebouncerOptions = {
|
|||||||
debounceTime: number;
|
debounceTime: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
||||||
export function debouncer<T>(callFn: (value: T) => unknown, { debounceTime = 60 }: Partial<DebouncerOptions> = {}) {
|
export function debouncer<T>(callFn: (value: T) => unknown, { debounceTime = 60 }: Partial<DebouncerOptions> = {}) {
|
||||||
let currentValue: T | undefined;
|
let currentValue: T | undefined;
|
||||||
let recentlyUpdated: boolean = false;
|
let recentlyUpdated: boolean = false;
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
/* eslint-disable import/first */
|
|
||||||
|
|
||||||
// Graphics
|
// Graphics
|
||||||
import GraphiteLogotypeSolid from "@graphite-frontend/assets/graphics/graphite-logotype-solid.svg";
|
import GraphiteLogotypeSolid from "@graphite-frontend/assets/graphics/graphite-logotype-solid.svg";
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
/* eslint-disable import/first */
|
|
||||||
|
|
||||||
// Demo artwork
|
// Demo artwork
|
||||||
import ThumbnailChangingSeasons from "@graphite-frontend/assets/images/demo-artwork/thumbnail-changing-seasons.png";
|
import ThumbnailChangingSeasons from "@graphite-frontend/assets/images/demo-artwork/thumbnail-changing-seasons.png";
|
||||||
import ThumbnailIsometricFountain from "@graphite-frontend/assets/images/demo-artwork/thumbnail-isometric-fountain.png";
|
import ThumbnailIsometricFountain from "@graphite-frontend/assets/images/demo-artwork/thumbnail-isometric-fountain.png";
|
||||||
|
|||||||
@@ -12,12 +12,10 @@ export function panicProxy<T extends object>(module: T): T {
|
|||||||
// Special handling to wrap the return of a constructor in the proxy
|
// Special handling to wrap the return of a constructor in the proxy
|
||||||
const isClass = isFunction && /^\s*class\s+/.test(targetValue.toString());
|
const isClass = isFunction && /^\s*class\s+/.test(targetValue.toString());
|
||||||
if (isClass) {
|
if (isClass) {
|
||||||
// eslint-disable-next-line func-names
|
|
||||||
return function (...args: unknown[]): unknown {
|
return function (...args: unknown[]): unknown {
|
||||||
// All three of these comment lines are necessary to suppress errors at both compile time and while editing this file (@ts-expect-error doesn't work here while editing the file)
|
// All three of these comment lines are necessary to suppress errors at both compile time and while editing this file (@ts-expect-error doesn't work here while editing the file)
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
// eslint-disable-next-line new-cap
|
|
||||||
const result = new targetValue(...args);
|
const result = new targetValue(...args);
|
||||||
|
|
||||||
return panicProxy(result);
|
return panicProxy(result);
|
||||||
@@ -25,7 +23,6 @@ export function panicProxy<T extends object>(module: T): T {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Replace the original function with a wrapper function that runs the original in a try-catch block
|
// Replace the original function with a wrapper function that runs the original in a try-catch block
|
||||||
// eslint-disable-next-line func-names
|
|
||||||
return function (...args: unknown[]): unknown {
|
return function (...args: unknown[]): unknown {
|
||||||
let result;
|
let result;
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ESNext",
|
"target": "ESNext",
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "node",
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"importHelpers": true,
|
"importHelpers": true,
|
||||||
"moduleResolution": "node",
|
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
@@ -20,11 +20,9 @@
|
|||||||
},
|
},
|
||||||
"lib": ["ESNext", "DOM", "DOM.Iterable", "ScriptHost"]
|
"lib": ["ESNext", "DOM", "DOM.Iterable", "ScriptHost"]
|
||||||
},
|
},
|
||||||
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.svelte", "*.ts", "*.js", "*.cjs"],
|
"include": ["src/**/*.ts", "src/**/*.svelte", "*.ts"],
|
||||||
"exclude": ["node_modules"],
|
|
||||||
"ts-node": {
|
"ts-node": {
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"module": "commonjs",
|
|
||||||
"useDefineForClassFields": false,
|
"useDefineForClassFields": false,
|
||||||
"noImplicitOverride": true
|
"noImplicitOverride": true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
/* eslint-disable no-console */
|
/* eslint-disable no-console */
|
||||||
|
|
||||||
import { spawnSync } from "child_process";
|
import { spawnSync } from "child_process";
|
||||||
|
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
|
||||||
@@ -31,7 +30,12 @@ export default defineConfig({
|
|||||||
defaultHandler?.(warning);
|
defaultHandler?.(warning);
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
viteMultipleAssets(["../demo-artwork"]),
|
viteMultipleAssets(
|
||||||
|
// Additional static asset directories besides `public/`
|
||||||
|
[{ input: "../demo-artwork/**", output: "demo-artwork" }],
|
||||||
|
// Options where we set custom MIME types
|
||||||
|
{ mimeTypes: { ".graphite": "application/json" } },
|
||||||
|
),
|
||||||
],
|
],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: [
|
alias: [
|
||||||
@@ -65,11 +69,6 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
output: {
|
|
||||||
// Inject `.min` into the filename of minified CSS files to tell Cloudflare not to minify it again.
|
|
||||||
// Cloudflare's minifier breaks the CSS due to a bug where it removes whitespace around calc() plus operators.
|
|
||||||
assetFileNames: (info) => `assets/[name]-[hash]${info.name?.endsWith(".css") ? ".min" : ""}[extname]`,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -361,11 +360,9 @@ function htmlDecode(input: string): string {
|
|||||||
if (maybeEntity) return maybeEntity[1];
|
if (maybeEntity) return maybeEntity[1];
|
||||||
|
|
||||||
let match;
|
let match;
|
||||||
// eslint-disable-next-line no-cond-assign
|
|
||||||
if ((match = entityCode.match(/^#x([\da-fA-F]+)$/))) {
|
if ((match = entityCode.match(/^#x([\da-fA-F]+)$/))) {
|
||||||
return String.fromCharCode(parseInt(match[1], 16));
|
return String.fromCharCode(parseInt(match[1], 16));
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line no-cond-assign
|
|
||||||
if ((match = entityCode.match(/^#(\d+)$/))) {
|
if ((match = entityCode.match(/^#(\d+)$/))) {
|
||||||
return String.fromCharCode(~~match[1]);
|
return String.fromCharCode(~~match[1]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,9 +11,10 @@ repository = "https://github.com/GraphiteEditor/Graphite"
|
|||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["gpu"]
|
default = ["gpu", "shader-nodes"]
|
||||||
gpu = ["editor/gpu"]
|
gpu = ["editor/gpu"]
|
||||||
native = []
|
native = []
|
||||||
|
shader-nodes = ["graphene-std/shader-nodes", "gpu"]
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
crate-type = ["cdylib", "rlib"]
|
crate-type = ["cdylib", "rlib"]
|
||||||
|
|||||||
@@ -7,7 +7,23 @@ authors = ["Graphite Authors <contact@graphite.rs>"]
|
|||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
std = ["dep:dyn-any", "dep:serde", "dep:specta", "dep:log", "glam/debug-glam-assert", "glam/std", "glam/serde", "half/std", "half/serde", "num-traits/std"]
|
# any feature that
|
||||||
|
# * must be usable in shaders
|
||||||
|
# * but requires std
|
||||||
|
# * and should be on by default
|
||||||
|
# should be in this list instead of `[workspace.dependency]`
|
||||||
|
std = [
|
||||||
|
"dep:dyn-any",
|
||||||
|
"dep:serde",
|
||||||
|
"dep:specta",
|
||||||
|
"dep:log",
|
||||||
|
"glam/debug-glam-assert",
|
||||||
|
"glam/std",
|
||||||
|
"glam/serde",
|
||||||
|
"half/std",
|
||||||
|
"half/serde",
|
||||||
|
"num-traits/std"
|
||||||
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Local std dependencies
|
# Local std dependencies
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ use super::discrete_srgb::{float_to_srgb_u8, srgb_u8_to_float};
|
|||||||
use bytemuck::{Pod, Zeroable};
|
use bytemuck::{Pod, Zeroable};
|
||||||
use core::fmt::Debug;
|
use core::fmt::Debug;
|
||||||
use core::hash::Hash;
|
use core::hash::Hash;
|
||||||
|
use glam::Vec4;
|
||||||
use half::f16;
|
use half::f16;
|
||||||
#[cfg(not(feature = "std"))]
|
#[cfg(not(feature = "std"))]
|
||||||
use num_traits::Euclid;
|
use num_traits::Euclid;
|
||||||
@@ -1075,6 +1076,21 @@ impl Color {
|
|||||||
..*self
|
..*self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn from_vec4(vec: Vec4) -> Self {
|
||||||
|
Self {
|
||||||
|
red: vec.x,
|
||||||
|
green: vec.y,
|
||||||
|
blue: vec.z,
|
||||||
|
alpha: vec.w,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn to_vec4(&self) -> Vec4 {
|
||||||
|
Vec4::new(self.red, self.green, self.blue, self.alpha)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
use graph_craft::document::NodeNetwork;
|
use graph_craft::document::NodeNetwork;
|
||||||
use graph_craft::util::*;
|
use graph_craft::util::*;
|
||||||
use iai_callgrind::{black_box, library_benchmark, library_benchmark_group, main};
|
use iai_callgrind::{library_benchmark, library_benchmark_group, main};
|
||||||
|
|
||||||
#[library_benchmark]
|
#[library_benchmark]
|
||||||
#[benches::with_setup(args = ["isometric-fountain", "painted-dreams", "procedural-string-lights", "parametric-dunescape", "red-dress", "valley-of-spires"], setup = load_from_name)]
|
#[benches::with_setup(args = ["isometric-fountain", "painted-dreams", "procedural-string-lights", "parametric-dunescape", "red-dress", "valley-of-spires"], setup = load_from_name)]
|
||||||
pub fn compile_to_proto(_input: NodeNetwork) {
|
pub fn compile_to_proto(_input: NodeNetwork) {
|
||||||
black_box(compile(_input));
|
std::hint::black_box(compile(_input));
|
||||||
}
|
}
|
||||||
|
|
||||||
library_benchmark_group!(name = compile_group; benchmarks = compile_to_proto);
|
library_benchmark_group!(name = compile_group; benchmarks = compile_to_proto);
|
||||||
|
|||||||
@@ -6,8 +6,18 @@ description = "graphene raster data format"
|
|||||||
authors = ["Graphite Authors <contact@graphite.rs>"]
|
authors = ["Graphite Authors <contact@graphite.rs>"]
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["rlib", "dylib"]
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
|
shader-nodes = [
|
||||||
|
"std",
|
||||||
|
"dep:graphene-raster-nodes-shaders",
|
||||||
|
]
|
||||||
std = [
|
std = [
|
||||||
"dep:graphene-core",
|
"dep:graphene-core",
|
||||||
"dep:dyn-any",
|
"dep:dyn-any",
|
||||||
@@ -19,8 +29,6 @@ std = [
|
|||||||
"dep:serde",
|
"dep:serde",
|
||||||
"dep:specta",
|
"dep:specta",
|
||||||
"dep:kurbo",
|
"dep:kurbo",
|
||||||
"glam/debug-glam-assert",
|
|
||||||
"glam/serde",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
@@ -31,10 +39,12 @@ node-macro = { workspace = true }
|
|||||||
# Local std dependencies
|
# Local std dependencies
|
||||||
dyn-any = { workspace = true, optional = true }
|
dyn-any = { workspace = true, optional = true }
|
||||||
graphene-core = { workspace = true, optional = true }
|
graphene-core = { workspace = true, optional = true }
|
||||||
|
graphene-raster-nodes-shaders = { path = "./shaders", optional = true }
|
||||||
|
|
||||||
# Workspace dependencies
|
# Workspace dependencies
|
||||||
bytemuck = { workspace = true }
|
bytemuck = { workspace = true }
|
||||||
glam = { workspace = true }
|
glam = { workspace = true }
|
||||||
|
spirv-std = { workspace = true }
|
||||||
num-traits = { workspace = true }
|
num-traits = { workspace = true }
|
||||||
|
|
||||||
# Workspace std dependencies
|
# Workspace std dependencies
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
name = "graphene-raster-nodes-shaders"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
description = "graphene raster data format"
|
||||||
|
authors = ["Graphite Authors <contact@graphite.rs>"]
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
cargo-gpu = { workspace = true }
|
||||||
|
env_logger = { workspace = true }
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
use cargo_gpu::InstalledBackend;
|
||||||
|
use cargo_gpu::spirv_builder::{MetadataPrintout, SpirvMetadata};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
env_logger::builder().init();
|
||||||
|
|
||||||
|
// Skip building the shader if they are provided externally
|
||||||
|
println!("cargo:rerun-if-env-changed=GRAPHENE_RASTER_NODES_SHADER_PATH");
|
||||||
|
if !std::env::var("GRAPHENE_RASTER_NODES_SHADER_PATH").unwrap_or_default().is_empty() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allows overriding the PATH to inject the rust-gpu rust toolchain when building the rest of the project with stable rustc.
|
||||||
|
// Used in nix shell. Do not remove without checking with developers using nix.
|
||||||
|
println!("cargo:rerun-if-env-changed=RUST_GPU_PATH_OVERRIDE");
|
||||||
|
if let Ok(path_override) = std::env::var("RUST_GPU_PATH_OVERRIDE") {
|
||||||
|
let current_path = std::env::var("PATH").unwrap_or_default();
|
||||||
|
let new_path = format!("{path_override}:{current_path}");
|
||||||
|
// SAFETY: Build script is single-threaded therefore this cannot lead to undefined behavior.
|
||||||
|
unsafe {
|
||||||
|
std::env::set_var("PATH", &new_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let shader_crate = PathBuf::from(concat!(env!("CARGO_MANIFEST_DIR"), "/.."));
|
||||||
|
|
||||||
|
println!("cargo:rerun-if-env-changed=RUSTC_CODEGEN_SPIRV_PATH");
|
||||||
|
let rustc_codegen_spirv_path = std::env::var("RUSTC_CODEGEN_SPIRV_PATH").unwrap_or_default();
|
||||||
|
let backend = if rustc_codegen_spirv_path.is_empty() {
|
||||||
|
// install the toolchain and build the `rustc_codegen_spirv` codegen backend with it
|
||||||
|
cargo_gpu::Install::from_shader_crate(shader_crate.clone()).run()?
|
||||||
|
} else {
|
||||||
|
// use the `RUSTC_CODEGEN_SPIRV` environment variable to find the codegen backend
|
||||||
|
let mut backend = InstalledBackend::default();
|
||||||
|
backend.rustc_codegen_spirv_location = PathBuf::from(rustc_codegen_spirv_path);
|
||||||
|
backend.toolchain_channel = "nightly".to_string();
|
||||||
|
backend.target_spec_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||||
|
backend
|
||||||
|
};
|
||||||
|
|
||||||
|
// build the shader crate
|
||||||
|
let mut builder = backend.to_spirv_builder(shader_crate, "spirv-unknown-naga-wgsl");
|
||||||
|
builder.print_metadata = MetadataPrintout::DependencyOnly;
|
||||||
|
builder.spirv_metadata = SpirvMetadata::Full;
|
||||||
|
builder.shader_crate_features.default_features = false;
|
||||||
|
let wgsl_result = builder.build()?;
|
||||||
|
let path_to_spv = wgsl_result.module.unwrap_single();
|
||||||
|
|
||||||
|
// needs to be fixed upstream
|
||||||
|
let path_to_wgsl = path_to_spv.with_extension("wgsl");
|
||||||
|
|
||||||
|
println!("cargo::rustc-env=GRAPHENE_RASTER_NODES_SHADER_PATH={}", path_to_wgsl.display());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"allows-weak-linkage": false,
|
||||||
|
"arch": "spirv",
|
||||||
|
"crt-objects-fallback": "false",
|
||||||
|
"crt-static-allows-dylibs": true,
|
||||||
|
"crt-static-respected": true,
|
||||||
|
"data-layout": "e-m:e-p:32:32:32-i64:64-n8:16:32:64",
|
||||||
|
"dll-prefix": "",
|
||||||
|
"dll-suffix": ".spv.json",
|
||||||
|
"dynamic-linking": true,
|
||||||
|
"emit-debug-gdb-scripts": false,
|
||||||
|
"env": "naga-wgsl",
|
||||||
|
"linker-flavor": "unix",
|
||||||
|
"linker-is-gnu": false,
|
||||||
|
"llvm-target": "spirv-unknown-naga-wgsl",
|
||||||
|
"main-needs-argc-argv": false,
|
||||||
|
"metadata": {
|
||||||
|
"description": null,
|
||||||
|
"host_tools": null,
|
||||||
|
"std": null,
|
||||||
|
"tier": null
|
||||||
|
},
|
||||||
|
"panic-strategy": "abort",
|
||||||
|
"simd-types-indirect": false,
|
||||||
|
"target-pointer-width": "32"
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
pub const WGSL_SHADER: &str = include_str!(env!("GRAPHENE_RASTER_NODES_SHADER_PATH"));
|
||||||
@@ -33,6 +33,7 @@ use num_traits::float::Float;
|
|||||||
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, node_macro::ChoiceType)]
|
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, node_macro::ChoiceType)]
|
||||||
#[cfg_attr(feature = "std", derive(dyn_any::DynAny, specta::Type, serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "std", derive(dyn_any::DynAny, specta::Type, serde::Serialize, serde::Deserialize))]
|
||||||
#[widget(Dropdown)]
|
#[widget(Dropdown)]
|
||||||
|
#[repr(u32)]
|
||||||
pub enum LuminanceCalculation {
|
pub enum LuminanceCalculation {
|
||||||
#[default]
|
#[default]
|
||||||
#[label("sRGB")]
|
#[label("sRGB")]
|
||||||
@@ -52,6 +53,7 @@ fn luminance<T: Adjust<Color>>(
|
|||||||
Table<GradientStops>,
|
Table<GradientStops>,
|
||||||
GradientStops,
|
GradientStops,
|
||||||
)]
|
)]
|
||||||
|
#[gpu_image]
|
||||||
mut input: T,
|
mut input: T,
|
||||||
luminance_calc: LuminanceCalculation,
|
luminance_calc: LuminanceCalculation,
|
||||||
) -> T {
|
) -> T {
|
||||||
@@ -68,7 +70,7 @@ fn luminance<T: Adjust<Color>>(
|
|||||||
input
|
input
|
||||||
}
|
}
|
||||||
|
|
||||||
#[node_macro::node(category("Raster"), shader_node(PerPixelAdjust))]
|
#[node_macro::node(category("Raster"), cfg(feature = "std"))]
|
||||||
fn gamma_correction<T: Adjust<Color>>(
|
fn gamma_correction<T: Adjust<Color>>(
|
||||||
_: impl Ctx,
|
_: impl Ctx,
|
||||||
#[implementations(
|
#[implementations(
|
||||||
@@ -77,6 +79,7 @@ fn gamma_correction<T: Adjust<Color>>(
|
|||||||
Table<GradientStops>,
|
Table<GradientStops>,
|
||||||
GradientStops,
|
GradientStops,
|
||||||
)]
|
)]
|
||||||
|
#[gpu_image]
|
||||||
mut input: T,
|
mut input: T,
|
||||||
#[default(2.2)]
|
#[default(2.2)]
|
||||||
#[range((0.01, 10.))]
|
#[range((0.01, 10.))]
|
||||||
@@ -98,6 +101,7 @@ fn extract_channel<T: Adjust<Color>>(
|
|||||||
Table<GradientStops>,
|
Table<GradientStops>,
|
||||||
GradientStops,
|
GradientStops,
|
||||||
)]
|
)]
|
||||||
|
#[gpu_image]
|
||||||
mut input: T,
|
mut input: T,
|
||||||
channel: RedGreenBlueAlpha,
|
channel: RedGreenBlueAlpha,
|
||||||
) -> T {
|
) -> T {
|
||||||
@@ -122,6 +126,7 @@ fn make_opaque<T: Adjust<Color>>(
|
|||||||
Table<GradientStops>,
|
Table<GradientStops>,
|
||||||
GradientStops,
|
GradientStops,
|
||||||
)]
|
)]
|
||||||
|
#[gpu_image]
|
||||||
mut input: T,
|
mut input: T,
|
||||||
) -> T {
|
) -> T {
|
||||||
input.adjust(|color| {
|
input.adjust(|color| {
|
||||||
@@ -139,7 +144,7 @@ fn make_opaque<T: Adjust<Color>>(
|
|||||||
//
|
//
|
||||||
// Some further analysis available at:
|
// Some further analysis available at:
|
||||||
// https://geraldbakker.nl/psnumbers/brightness-contrast.html
|
// https://geraldbakker.nl/psnumbers/brightness-contrast.html
|
||||||
#[node_macro::node(name("Brightness/Contrast"), category("Raster: Adjustment"), properties("brightness_contrast_properties"), shader_node(PerPixelAdjust))]
|
#[node_macro::node(name("Brightness/Contrast"), category("Raster: Adjustment"), properties("brightness_contrast_properties"), cfg(feature = "std"))]
|
||||||
fn brightness_contrast<T: Adjust<Color>>(
|
fn brightness_contrast<T: Adjust<Color>>(
|
||||||
_: impl Ctx,
|
_: impl Ctx,
|
||||||
#[implementations(
|
#[implementations(
|
||||||
@@ -148,6 +153,7 @@ fn brightness_contrast<T: Adjust<Color>>(
|
|||||||
Table<GradientStops>,
|
Table<GradientStops>,
|
||||||
GradientStops,
|
GradientStops,
|
||||||
)]
|
)]
|
||||||
|
#[gpu_image]
|
||||||
mut input: T,
|
mut input: T,
|
||||||
brightness: SignedPercentageF32,
|
brightness: SignedPercentageF32,
|
||||||
contrast: SignedPercentageF32,
|
contrast: SignedPercentageF32,
|
||||||
@@ -238,6 +244,7 @@ fn levels<T: Adjust<Color>>(
|
|||||||
Table<GradientStops>,
|
Table<GradientStops>,
|
||||||
GradientStops,
|
GradientStops,
|
||||||
)]
|
)]
|
||||||
|
#[gpu_image]
|
||||||
mut image: T,
|
mut image: T,
|
||||||
#[default(0.)] shadows: PercentageF32,
|
#[default(0.)] shadows: PercentageF32,
|
||||||
#[default(50.)] midtones: PercentageF32,
|
#[default(50.)] midtones: PercentageF32,
|
||||||
@@ -306,6 +313,7 @@ fn black_and_white<T: Adjust<Color>>(
|
|||||||
Table<GradientStops>,
|
Table<GradientStops>,
|
||||||
GradientStops,
|
GradientStops,
|
||||||
)]
|
)]
|
||||||
|
#[gpu_image]
|
||||||
mut image: T,
|
mut image: T,
|
||||||
#[default(Color::BLACK)] tint: Color,
|
#[default(Color::BLACK)] tint: Color,
|
||||||
#[default(40.)]
|
#[default(40.)]
|
||||||
@@ -379,6 +387,7 @@ fn hue_saturation<T: Adjust<Color>>(
|
|||||||
Table<GradientStops>,
|
Table<GradientStops>,
|
||||||
GradientStops,
|
GradientStops,
|
||||||
)]
|
)]
|
||||||
|
#[gpu_image]
|
||||||
mut input: T,
|
mut input: T,
|
||||||
hue_shift: AngleF32,
|
hue_shift: AngleF32,
|
||||||
saturation_shift: SignedPercentageF32,
|
saturation_shift: SignedPercentageF32,
|
||||||
@@ -414,6 +423,7 @@ fn invert<T: Adjust<Color>>(
|
|||||||
Table<GradientStops>,
|
Table<GradientStops>,
|
||||||
GradientStops,
|
GradientStops,
|
||||||
)]
|
)]
|
||||||
|
#[gpu_image]
|
||||||
mut input: T,
|
mut input: T,
|
||||||
) -> T {
|
) -> T {
|
||||||
input.adjust(|color| {
|
input.adjust(|color| {
|
||||||
@@ -437,6 +447,7 @@ fn threshold<T: Adjust<Color>>(
|
|||||||
Table<GradientStops>,
|
Table<GradientStops>,
|
||||||
GradientStops,
|
GradientStops,
|
||||||
)]
|
)]
|
||||||
|
#[gpu_image]
|
||||||
mut image: T,
|
mut image: T,
|
||||||
#[default(50.)] min_luminance: PercentageF32,
|
#[default(50.)] min_luminance: PercentageF32,
|
||||||
#[default(100.)] max_luminance: PercentageF32,
|
#[default(100.)] max_luminance: PercentageF32,
|
||||||
@@ -483,6 +494,7 @@ fn vibrance<T: Adjust<Color>>(
|
|||||||
Table<GradientStops>,
|
Table<GradientStops>,
|
||||||
GradientStops,
|
GradientStops,
|
||||||
)]
|
)]
|
||||||
|
#[gpu_image]
|
||||||
mut image: T,
|
mut image: T,
|
||||||
vibrance: SignedPercentageF32,
|
vibrance: SignedPercentageF32,
|
||||||
) -> T {
|
) -> T {
|
||||||
@@ -551,6 +563,7 @@ pub enum RedGreenBlue {
|
|||||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, node_macro::ChoiceType)]
|
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, node_macro::ChoiceType)]
|
||||||
#[cfg_attr(feature = "std", derive(dyn_any::DynAny, specta::Type, serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "std", derive(dyn_any::DynAny, specta::Type, serde::Serialize, serde::Deserialize))]
|
||||||
#[widget(Radio)]
|
#[widget(Radio)]
|
||||||
|
#[repr(u32)]
|
||||||
pub enum RedGreenBlueAlpha {
|
pub enum RedGreenBlueAlpha {
|
||||||
#[default]
|
#[default]
|
||||||
Red,
|
Red,
|
||||||
@@ -640,7 +653,7 @@ pub enum DomainWarpType {
|
|||||||
// Aims for interoperable compatibility with:
|
// Aims for interoperable compatibility with:
|
||||||
// https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#:~:text=%27mixr%27%20%3D%20Channel%20Mixer
|
// https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#:~:text=%27mixr%27%20%3D%20Channel%20Mixer
|
||||||
// https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#:~:text=Lab%20color%20only-,Channel%20Mixer,-Key%20is%20%27mixr
|
// https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#:~:text=Lab%20color%20only-,Channel%20Mixer,-Key%20is%20%27mixr
|
||||||
#[node_macro::node(category("Raster: Adjustment"), properties("channel_mixer_properties"), shader_node(PerPixelAdjust))]
|
#[node_macro::node(category("Raster: Adjustment"), properties("channel_mixer_properties"), cfg(feature = "std"))]
|
||||||
fn channel_mixer<T: Adjust<Color>>(
|
fn channel_mixer<T: Adjust<Color>>(
|
||||||
_: impl Ctx,
|
_: impl Ctx,
|
||||||
#[implementations(
|
#[implementations(
|
||||||
@@ -649,6 +662,7 @@ fn channel_mixer<T: Adjust<Color>>(
|
|||||||
Table<GradientStops>,
|
Table<GradientStops>,
|
||||||
GradientStops,
|
GradientStops,
|
||||||
)]
|
)]
|
||||||
|
#[gpu_image]
|
||||||
mut image: T,
|
mut image: T,
|
||||||
|
|
||||||
monochrome: bool,
|
monochrome: bool,
|
||||||
@@ -769,7 +783,7 @@ pub enum SelectiveColorChoice {
|
|||||||
//
|
//
|
||||||
// Algorithm based on:
|
// Algorithm based on:
|
||||||
// https://blog.pkh.me/p/22-understanding-selective-coloring-in-adobe-photoshop.html
|
// https://blog.pkh.me/p/22-understanding-selective-coloring-in-adobe-photoshop.html
|
||||||
#[node_macro::node(category("Raster: Adjustment"), properties("selective_color_properties"), shader_node(PerPixelAdjust))]
|
#[node_macro::node(category("Raster: Adjustment"), properties("selective_color_properties"), cfg(feature = "std"))]
|
||||||
fn selective_color<T: Adjust<Color>>(
|
fn selective_color<T: Adjust<Color>>(
|
||||||
_: impl Ctx,
|
_: impl Ctx,
|
||||||
#[implementations(
|
#[implementations(
|
||||||
@@ -778,6 +792,7 @@ fn selective_color<T: Adjust<Color>>(
|
|||||||
Table<GradientStops>,
|
Table<GradientStops>,
|
||||||
GradientStops,
|
GradientStops,
|
||||||
)]
|
)]
|
||||||
|
#[gpu_image]
|
||||||
mut image: T,
|
mut image: T,
|
||||||
|
|
||||||
mode: RelativeAbsolute,
|
mode: RelativeAbsolute,
|
||||||
@@ -921,6 +936,7 @@ fn posterize<T: Adjust<Color>>(
|
|||||||
Table<GradientStops>,
|
Table<GradientStops>,
|
||||||
GradientStops,
|
GradientStops,
|
||||||
)]
|
)]
|
||||||
|
#[gpu_image]
|
||||||
mut input: T,
|
mut input: T,
|
||||||
#[default(4)]
|
#[default(4)]
|
||||||
#[hard_min(2.)]
|
#[hard_min(2.)]
|
||||||
@@ -955,6 +971,7 @@ fn exposure<T: Adjust<Color>>(
|
|||||||
Table<GradientStops>,
|
Table<GradientStops>,
|
||||||
GradientStops,
|
GradientStops,
|
||||||
)]
|
)]
|
||||||
|
#[gpu_image]
|
||||||
mut input: T,
|
mut input: T,
|
||||||
exposure: f32,
|
exposure: f32,
|
||||||
offset: f32,
|
offset: f32,
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ fn blend<T: Blend<Color> + Send>(
|
|||||||
Table<GradientStops>,
|
Table<GradientStops>,
|
||||||
GradientStops,
|
GradientStops,
|
||||||
)]
|
)]
|
||||||
|
#[gpu_image]
|
||||||
over: T,
|
over: T,
|
||||||
#[expose]
|
#[expose]
|
||||||
#[implementations(
|
#[implementations(
|
||||||
@@ -149,6 +150,7 @@ fn blend<T: Blend<Color> + Send>(
|
|||||||
Table<GradientStops>,
|
Table<GradientStops>,
|
||||||
GradientStops,
|
GradientStops,
|
||||||
)]
|
)]
|
||||||
|
#[gpu_image]
|
||||||
under: T,
|
under: T,
|
||||||
blend_mode: BlendMode,
|
blend_mode: BlendMode,
|
||||||
#[default(100.)] opacity: PercentageF32,
|
#[default(100.)] opacity: PercentageF32,
|
||||||
@@ -165,6 +167,7 @@ fn color_overlay<T: Adjust<Color>>(
|
|||||||
Table<GradientStops>,
|
Table<GradientStops>,
|
||||||
GradientStops,
|
GradientStops,
|
||||||
)]
|
)]
|
||||||
|
#[gpu_image]
|
||||||
mut image: T,
|
mut image: T,
|
||||||
#[default(Color::BLACK)] color: Color,
|
#[default(Color::BLACK)] color: Color,
|
||||||
blend_mode: BlendMode,
|
blend_mode: BlendMode,
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ image-compare = []
|
|||||||
vello = ["dep:vello", "gpu"]
|
vello = ["dep:vello", "gpu"]
|
||||||
resvg = []
|
resvg = []
|
||||||
wayland = ["graph-craft/wayland"]
|
wayland = ["graph-craft/wayland"]
|
||||||
|
shader-nodes = ["graphene-raster-nodes/shader-nodes"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Local dependencies
|
# Local dependencies
|
||||||
|
|||||||
@@ -2,8 +2,9 @@ mod benchmark_util;
|
|||||||
|
|
||||||
use benchmark_util::setup_network;
|
use benchmark_util::setup_network;
|
||||||
use graphene_std::application_io::RenderConfig;
|
use graphene_std::application_io::RenderConfig;
|
||||||
use iai_callgrind::{black_box, library_benchmark, library_benchmark_group, main};
|
use iai_callgrind::{library_benchmark, library_benchmark_group, main};
|
||||||
use interpreted_executor::dynamic_executor::DynamicExecutor;
|
use interpreted_executor::dynamic_executor::DynamicExecutor;
|
||||||
|
use std::hint::black_box;
|
||||||
|
|
||||||
fn setup_run_cached(name: &str) -> DynamicExecutor {
|
fn setup_run_cached(name: &str) -> DynamicExecutor {
|
||||||
let (executor, _) = setup_network(name);
|
let (executor, _) = setup_network(name);
|
||||||
|
|||||||
@@ -2,8 +2,9 @@ mod benchmark_util;
|
|||||||
|
|
||||||
use benchmark_util::setup_network;
|
use benchmark_util::setup_network;
|
||||||
use graphene_std::application_io;
|
use graphene_std::application_io;
|
||||||
use iai_callgrind::{black_box, library_benchmark, library_benchmark_group, main};
|
use iai_callgrind::{library_benchmark, library_benchmark_group, main};
|
||||||
use interpreted_executor::dynamic_executor::DynamicExecutor;
|
use interpreted_executor::dynamic_executor::DynamicExecutor;
|
||||||
|
use std::hint::black_box;
|
||||||
|
|
||||||
fn setup_run_once(name: &str) -> DynamicExecutor {
|
fn setup_run_once(name: &str) -> DynamicExecutor {
|
||||||
let (executor, _) = setup_network(name);
|
let (executor, _) = setup_network(name);
|
||||||
|
|||||||
@@ -2,8 +2,9 @@ mod benchmark_util;
|
|||||||
|
|
||||||
use benchmark_util::setup_network;
|
use benchmark_util::setup_network;
|
||||||
use graph_craft::proto::ProtoNetwork;
|
use graph_craft::proto::ProtoNetwork;
|
||||||
use iai_callgrind::{black_box, library_benchmark, library_benchmark_group, main};
|
use iai_callgrind::{library_benchmark, library_benchmark_group, main};
|
||||||
use interpreted_executor::dynamic_executor::DynamicExecutor;
|
use interpreted_executor::dynamic_executor::DynamicExecutor;
|
||||||
|
use std::hint::black_box;
|
||||||
|
|
||||||
fn setup_update_executor(name: &str) -> (DynamicExecutor, ProtoNetwork) {
|
fn setup_update_executor(name: &str) -> (DynamicExecutor, ProtoNetwork) {
|
||||||
let (_, proto_network) = setup_network(name);
|
let (_, proto_network) = setup_network(name);
|
||||||
|
|||||||
@@ -295,6 +295,7 @@ pub(crate) fn generate_node_code(parsed: &ParsedNodeFn) -> syn::Result<TokenStre
|
|||||||
|
|
||||||
let cfg = crate::shader_nodes::modify_cfg(attributes);
|
let cfg = crate::shader_nodes::modify_cfg(attributes);
|
||||||
let node_input_accessor = generate_node_input_references(parsed, fn_generics, &field_idents, &graphene_core, &identifier, &cfg);
|
let node_input_accessor = generate_node_input_references(parsed, fn_generics, &field_idents, &graphene_core, &identifier, &cfg);
|
||||||
|
let shader_entry_point = attributes.shader_node.as_ref().map(|n| n.codegen_shader_entry_point(parsed)).unwrap_or(Ok(TokenStream2::new()))?;
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
/// Underlying implementation for [#struct_name]
|
/// Underlying implementation for [#struct_name]
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -384,6 +385,8 @@ pub(crate) fn generate_node_code(parsed: &ParsedNodeFn) -> syn::Result<TokenStre
|
|||||||
NODE_METADATA.lock().unwrap().insert(#identifier(), metadata);
|
NODE_METADATA.lock().unwrap().insert(#identifier(), metadata);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#shader_entry_point
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -586,6 +589,7 @@ fn generate_register_node_impl(parsed: &ParsedNodeFn, field_names: &[&Ident], st
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use crate::shader_nodes::CodegenShaderEntryPoint;
|
||||||
use syn::visit_mut::VisitMut;
|
use syn::visit_mut::VisitMut;
|
||||||
use syn::{GenericArgument, Lifetime, Type};
|
use syn::{GenericArgument, Lifetime, Type};
|
||||||
|
|
||||||
|
|||||||
@@ -120,6 +120,8 @@ pub enum ParsedFieldType {
|
|||||||
Node(NodeParsedField),
|
Node(NodeParsedField),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// a param of any kind, either a concrete type or a generic type with a set of possible types specified via
|
||||||
|
/// `#[implementation(type)]`
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct RegularParsedField {
|
pub struct RegularParsedField {
|
||||||
pub ty: Type,
|
pub ty: Type,
|
||||||
@@ -131,8 +133,10 @@ pub struct RegularParsedField {
|
|||||||
pub number_hard_max: Option<LitFloat>,
|
pub number_hard_max: Option<LitFloat>,
|
||||||
pub number_mode_range: Option<ExprTuple>,
|
pub number_mode_range: Option<ExprTuple>,
|
||||||
pub implementations: Punctuated<Type, Comma>,
|
pub implementations: Punctuated<Type, Comma>,
|
||||||
|
pub gpu_image: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// a param of `impl Node` with `#[implementation(in -> out)]`
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct NodeParsedField {
|
pub struct NodeParsedField {
|
||||||
pub input_type: Type,
|
pub input_type: Type,
|
||||||
@@ -529,6 +533,7 @@ fn parse_field(pat_ident: PatIdent, ty: Type, attrs: &[Attribute]) -> syn::Resul
|
|||||||
.map_err(|e| Error::new_spanned(attr, format!("Invalid `step` for argument '{ident}': {e}\nUSAGE EXAMPLE: #[step(2.)]")))
|
.map_err(|e| Error::new_spanned(attr, format!("Invalid `step` for argument '{ident}': {e}\nUSAGE EXAMPLE: #[step(2.)]")))
|
||||||
})
|
})
|
||||||
.transpose()?;
|
.transpose()?;
|
||||||
|
let gpu_image = extract_attribute(attrs, "gpu_image").is_some();
|
||||||
|
|
||||||
let (is_node, node_input_type, node_output_type) = parse_node_type(&ty);
|
let (is_node, node_input_type, node_output_type) = parse_node_type(&ty);
|
||||||
let description = attrs
|
let description = attrs
|
||||||
@@ -590,6 +595,7 @@ fn parse_field(pat_ident: PatIdent, ty: Type, attrs: &[Attribute]) -> syn::Resul
|
|||||||
ty,
|
ty,
|
||||||
value_source,
|
value_source,
|
||||||
implementations,
|
implementations,
|
||||||
|
gpu_image,
|
||||||
}),
|
}),
|
||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
@@ -829,6 +835,7 @@ mod tests {
|
|||||||
number_hard_max: None,
|
number_hard_max: None,
|
||||||
number_mode_range: None,
|
number_mode_range: None,
|
||||||
implementations: Punctuated::new(),
|
implementations: Punctuated::new(),
|
||||||
|
gpu_image: false,
|
||||||
}),
|
}),
|
||||||
number_display_decimal_places: None,
|
number_display_decimal_places: None,
|
||||||
number_step: None,
|
number_step: None,
|
||||||
@@ -909,6 +916,7 @@ mod tests {
|
|||||||
number_hard_max: None,
|
number_hard_max: None,
|
||||||
number_mode_range: None,
|
number_mode_range: None,
|
||||||
implementations: Punctuated::new(),
|
implementations: Punctuated::new(),
|
||||||
|
gpu_image: false,
|
||||||
}),
|
}),
|
||||||
number_display_decimal_places: None,
|
number_display_decimal_places: None,
|
||||||
number_step: None,
|
number_step: None,
|
||||||
@@ -972,6 +980,7 @@ mod tests {
|
|||||||
number_hard_max: None,
|
number_hard_max: None,
|
||||||
number_mode_range: None,
|
number_mode_range: None,
|
||||||
implementations: Punctuated::new(),
|
implementations: Punctuated::new(),
|
||||||
|
gpu_image: false,
|
||||||
}),
|
}),
|
||||||
number_display_decimal_places: None,
|
number_display_decimal_places: None,
|
||||||
number_step: None,
|
number_step: None,
|
||||||
@@ -1038,6 +1047,7 @@ mod tests {
|
|||||||
p.push(parse_quote!(f64));
|
p.push(parse_quote!(f64));
|
||||||
p
|
p
|
||||||
},
|
},
|
||||||
|
gpu_image: false,
|
||||||
}),
|
}),
|
||||||
number_display_decimal_places: None,
|
number_display_decimal_places: None,
|
||||||
number_step: None,
|
number_step: None,
|
||||||
@@ -1106,6 +1116,7 @@ mod tests {
|
|||||||
number_hard_max: None,
|
number_hard_max: None,
|
||||||
number_mode_range: Some(parse_quote!((0., 100.))),
|
number_mode_range: Some(parse_quote!((0., 100.))),
|
||||||
implementations: Punctuated::new(),
|
implementations: Punctuated::new(),
|
||||||
|
gpu_image: false,
|
||||||
}),
|
}),
|
||||||
number_display_decimal_places: None,
|
number_display_decimal_places: None,
|
||||||
number_step: None,
|
number_step: None,
|
||||||
@@ -1167,6 +1178,7 @@ mod tests {
|
|||||||
number_hard_max: None,
|
number_hard_max: None,
|
||||||
number_mode_range: None,
|
number_mode_range: None,
|
||||||
implementations: Punctuated::new(),
|
implementations: Punctuated::new(),
|
||||||
|
gpu_image: false,
|
||||||
}),
|
}),
|
||||||
number_display_decimal_places: None,
|
number_display_decimal_places: None,
|
||||||
number_step: None,
|
number_step: None,
|
||||||
|
|||||||
+24
-5
@@ -1,10 +1,13 @@
|
|||||||
use crate::parsing::NodeFnAttributes;
|
use crate::parsing::{NodeFnAttributes, ParsedNodeFn};
|
||||||
|
use crate::shader_nodes::per_pixel_adjust::PerPixelAdjust;
|
||||||
use proc_macro2::{Ident, TokenStream};
|
use proc_macro2::{Ident, TokenStream};
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use strum::{EnumString, VariantNames};
|
use strum::VariantNames;
|
||||||
use syn::Error;
|
use syn::Error;
|
||||||
use syn::parse::{Parse, ParseStream};
|
use syn::parse::{Parse, ParseStream};
|
||||||
|
|
||||||
|
pub mod per_pixel_adjust;
|
||||||
|
|
||||||
pub const STD_FEATURE_GATE: &str = "std";
|
pub const STD_FEATURE_GATE: &str = "std";
|
||||||
|
|
||||||
pub fn modify_cfg(attributes: &NodeFnAttributes) -> TokenStream {
|
pub fn modify_cfg(attributes: &NodeFnAttributes) -> TokenStream {
|
||||||
@@ -16,17 +19,33 @@ pub fn modify_cfg(attributes: &NodeFnAttributes) -> TokenStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, EnumString, VariantNames)]
|
#[derive(Debug, VariantNames)]
|
||||||
pub(crate) enum ShaderNodeType {
|
pub(crate) enum ShaderNodeType {
|
||||||
PerPixelAdjust,
|
PerPixelAdjust(PerPixelAdjust),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parse for ShaderNodeType {
|
impl Parse for ShaderNodeType {
|
||||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||||
let ident: Ident = input.parse()?;
|
let ident: Ident = input.parse()?;
|
||||||
Ok(match ident.to_string().as_str() {
|
Ok(match ident.to_string().as_str() {
|
||||||
"PerPixelAdjust" => ShaderNodeType::PerPixelAdjust,
|
"PerPixelAdjust" => ShaderNodeType::PerPixelAdjust(PerPixelAdjust::parse(input)?),
|
||||||
_ => return Err(Error::new_spanned(&ident, format!("attr 'shader_node' must be one of {:?}", Self::VARIANTS))),
|
_ => return Err(Error::new_spanned(&ident, format!("attr 'shader_node' must be one of {:?}", Self::VARIANTS))),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait CodegenShaderEntryPoint {
|
||||||
|
fn codegen_shader_entry_point(&self, parsed: &ParsedNodeFn) -> syn::Result<TokenStream>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CodegenShaderEntryPoint for ShaderNodeType {
|
||||||
|
fn codegen_shader_entry_point(&self, parsed: &ParsedNodeFn) -> syn::Result<TokenStream> {
|
||||||
|
if parsed.is_async {
|
||||||
|
return Err(Error::new_spanned(&parsed.fn_name, "Shader nodes must not be async"));
|
||||||
|
}
|
||||||
|
|
||||||
|
match self {
|
||||||
|
ShaderNodeType::PerPixelAdjust(x) => x.codegen_shader_entry_point(parsed),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
use crate::parsing::{ParsedFieldType, ParsedNodeFn, RegularParsedField};
|
||||||
|
use crate::shader_nodes::CodegenShaderEntryPoint;
|
||||||
|
use proc_macro2::{Ident, TokenStream};
|
||||||
|
use quote::{ToTokens, format_ident, quote};
|
||||||
|
use std::borrow::Cow;
|
||||||
|
use syn::parse::{Parse, ParseStream};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct PerPixelAdjust {}
|
||||||
|
|
||||||
|
impl Parse for PerPixelAdjust {
|
||||||
|
fn parse(_input: ParseStream) -> syn::Result<Self> {
|
||||||
|
Ok(Self {})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CodegenShaderEntryPoint for PerPixelAdjust {
|
||||||
|
fn codegen_shader_entry_point(&self, parsed: &ParsedNodeFn) -> syn::Result<TokenStream> {
|
||||||
|
let fn_name = &parsed.fn_name;
|
||||||
|
let gpu_mod = format_ident!("{}_gpu", parsed.fn_name);
|
||||||
|
let spirv_image_ty = quote!(Image2d);
|
||||||
|
|
||||||
|
// bindings for images start at 1
|
||||||
|
let mut binding_cnt = 0;
|
||||||
|
let params = parsed
|
||||||
|
.fields
|
||||||
|
.iter()
|
||||||
|
.map(|f| {
|
||||||
|
let ident = &f.pat_ident;
|
||||||
|
match &f.ty {
|
||||||
|
ParsedFieldType::Node { .. } => Err(syn::Error::new_spanned(ident, "PerPixelAdjust shader nodes cannot accept other nodes as generics")),
|
||||||
|
ParsedFieldType::Regular(RegularParsedField { gpu_image: false, ty, .. }) => Ok(Param {
|
||||||
|
ident: Cow::Borrowed(&ident.ident),
|
||||||
|
ty: Cow::Owned(ty.to_token_stream()),
|
||||||
|
param_type: ParamType::Uniform,
|
||||||
|
}),
|
||||||
|
ParsedFieldType::Regular(RegularParsedField { gpu_image: true, .. }) => {
|
||||||
|
binding_cnt += 1;
|
||||||
|
Ok(Param {
|
||||||
|
ident: Cow::Owned(format_ident!("image_{}", &ident.ident)),
|
||||||
|
ty: Cow::Borrowed(&spirv_image_ty),
|
||||||
|
param_type: ParamType::Image { binding: binding_cnt },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<syn::Result<Vec<_>>>()?;
|
||||||
|
|
||||||
|
let uniform_members = params
|
||||||
|
.iter()
|
||||||
|
.filter_map(|Param { ident, ty, param_type }| match param_type {
|
||||||
|
ParamType::Image { .. } => None,
|
||||||
|
ParamType::Uniform => Some(quote! {#ident: #ty}),
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let image_params = params
|
||||||
|
.iter()
|
||||||
|
.filter_map(|Param { ident, ty, param_type }| match param_type {
|
||||||
|
ParamType::Image { binding } => Some(quote! {#[spirv(descriptor_set = 0, binding = #binding)] #ident: &#ty}),
|
||||||
|
ParamType::Uniform => None,
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let call_args = params
|
||||||
|
.iter()
|
||||||
|
.map(|Param { ident, param_type, .. }| match param_type {
|
||||||
|
ParamType::Image { .. } => quote!(Color::from_vec4(#ident.fetch_with(texel_coord, lod(0)))),
|
||||||
|
ParamType::Uniform => quote!(uniform.#ident),
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let context = quote!(());
|
||||||
|
|
||||||
|
Ok(quote! {
|
||||||
|
pub mod #gpu_mod {
|
||||||
|
use super::*;
|
||||||
|
use graphene_core_shaders::color::Color;
|
||||||
|
use spirv_std::spirv;
|
||||||
|
use spirv_std::glam::{Vec4, Vec4Swizzles};
|
||||||
|
use spirv_std::image::{Image2d, ImageWithMethods};
|
||||||
|
use spirv_std::image::sample_with::lod;
|
||||||
|
|
||||||
|
pub struct Uniform {
|
||||||
|
#(#uniform_members),*
|
||||||
|
}
|
||||||
|
|
||||||
|
#[spirv(fragment)]
|
||||||
|
pub fn entry_point(
|
||||||
|
#[spirv(frag_coord)] frag_coord: Vec4,
|
||||||
|
color_out: &mut Vec4,
|
||||||
|
#[spirv(descriptor_set = 0, binding = 0, storage_buffer)] uniform: &Uniform,
|
||||||
|
#(#image_params),*
|
||||||
|
) {
|
||||||
|
let texel_coord = frag_coord.xy().as_uvec2();
|
||||||
|
let color: Color = #fn_name(#context, #(#call_args),*);
|
||||||
|
*color_out = color.to_vec4();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Param<'a> {
|
||||||
|
ident: Cow<'a, Ident>,
|
||||||
|
ty: Cow<'a, TokenStream>,
|
||||||
|
param_type: ParamType,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ParamType {
|
||||||
|
Image { binding: u32 },
|
||||||
|
Uniform,
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user