diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..bac83fe --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,6 @@ +# clipboard api is still unstable, so web-sys requires the below flag to be passed for copy (ctrl + c) to work +# https://rustwasm.github.io/docs/wasm-bindgen/web-sys/unstable-apis.html +# check status at https://developer.mozilla.org/en-US/docs/Web/API/Clipboard#browser_compatibility +# we don't use `[build]` because of rust analyzer's build cache invalidation https://github.com/emilk/eframe_template/issues/93 +[target.wasm32-unknown-unknown] +rustflags = ["--cfg=web_sys_unstable_apis"] \ No newline at end of file diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml new file mode 100644 index 0000000..034d84b --- /dev/null +++ b/.github/workflows/pages.yml @@ -0,0 +1,44 @@ +name: Github Pages + +on: + push: + branches: + - main + +permissions: + contents: write # for committing to gh-pages branch. + +jobs: + build-github-pages: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 # repo checkout + - uses: actions-rs/toolchain@v1 # get rust toolchain for wasm + with: + profile: minimal + toolchain: stable + target: wasm32-unknown-unknown + override: true + - name: Rust Cache # cache the rust build artefacts + uses: Swatinem/rust-cache@v1 + - name: Download and install Trunk binary + run: wget -qO- https://github.com/thedodd/trunk/releases/latest/download/trunk-x86_64-unknown-linux-gnu.tar.gz | tar -xzf- + - name: Build # build + # Environment $public_url resolves to the github project page. + # If using a user/organization page, remove the `${{ github.event.repository.name }}` part. + # using --public-url something will allow trunk to modify all the href paths like from favicon.ico to repo_name/favicon.ico . + # this is necessary for github pages where the site is deployed to username.github.io/repo_name and all files must be requested + # relatively as eframe_template/favicon.ico. if we skip public-url option, the href paths will instead request username.github.io/favicon.ico which + # will obviously return error 404 not found. + run: ./trunk build --release --public-url $public_url + env: + # public_url: "https://${{ github.repository_owner }}.github.io" + # public_url: "/${{ github.event.repository.name }}" + public_url: "https://rubenk.dev" + - name: Deploy + uses: JamesIves/github-pages-deploy-action@v4 + with: + folder: dist + # this option will not maintain any history of your previous pages deployment + # set to false if you want all page build to be committed to your gh-pages branch history + single-commit: true diff --git a/.github/workflows/rust.yml.backup b/.github/workflows/rust.yml.backup new file mode 100644 index 0000000..930f464 --- /dev/null +++ b/.github/workflows/rust.yml.backup @@ -0,0 +1,167 @@ +on: [push, pull_request, workflow_dispatch] + +name: CI + +env: + # --cfg=web_sys_unstable_apis is required to enable the web_sys clipboard API which egui_web uses + # https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Clipboard.html + # https://rustwasm.github.io/docs/wasm-bindgen/web-sys/unstable-apis.html + RUSTFLAGS: -D warnings --cfg=web_sys_unstable_apis + RUSTDOCFLAGS: -D warnings + +jobs: + check: + name: Check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + - uses: actions-rs/cargo@v1 + with: + command: check + args: --all-features + + check_wasm: + name: Check wasm32 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + target: wasm32-unknown-unknown + override: true + - uses: actions-rs/cargo@v1 + with: + command: check + args: --all-features --lib --target wasm32-unknown-unknown + + test: + name: Test Suite + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + - run: sudo apt-get install libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev libxkbcommon-dev libssl-dev + - uses: actions-rs/cargo@v1 + with: + command: test + args: --lib + + fmt: + name: Rustfmt + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + components: rustfmt + - uses: actions-rs/cargo@v1 + with: + command: fmt + args: --all -- --check + + clippy: + name: Clippy + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + components: clippy + - uses: actions-rs/cargo@v1 + with: + command: clippy + args: -- -D warnings + + trunk: + name: trunk + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: 1.76.0 + target: wasm32-unknown-unknown + override: true + - name: Download and install Trunk binary + run: wget -qO- https://github.com/thedodd/trunk/releases/latest/download/trunk-x86_64-unknown-linux-gnu.tar.gz | tar -xzf- + - name: Build + run: ./trunk build + + build: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + - os: macos-latest + TARGET: aarch64-apple-darwin + + - os: macos-latest + TARGET: x86_64-apple-darwin + + - os: ubuntu-latest + TARGET: arm-unknown-linux-musleabihf + + - os: ubuntu-latest + TARGET: armv7-unknown-linux-musleabihf + + - os: ubuntu-latest + TARGET: x86_64-unknown-linux-musl + + - os: windows-latest + TARGET: x86_64-pc-windows-msvc + EXTENSION: .exe + + steps: + - name: Building ${{ matrix.TARGET }} + run: echo "${{ matrix.TARGET }}" + + - uses: actions/checkout@master + - uses: actions-rs/toolchain@v1.0.1 + with: + toolchain: stable + target: ${{ matrix.TARGET }} + override: true + + - uses: actions-rs/cargo@v1 + with: + use-cross: true + command: build + args: --verbose --release --target=${{ matrix.TARGET }} + + - name: Rename + run: cp target/${{ matrix.TARGET }}/release/eframe_template${{ matrix.EXTENSION }} eframe_template-${{ matrix.TARGET }}${{ matrix.EXTENSION }} + + - uses: actions/upload-artifact@master + with: + name: eframe_template-${{ matrix.TARGET }}${{ matrix.EXTENSION }} + path: eframe_template-${{ matrix.TARGET }}${{ matrix.EXTENSION }} + + - uses: svenstaro/upload-release-action@v2 + name: Upload binaries to release + if: ${{ github.event_name == 'push' }} + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: eframe_template-${{ matrix.TARGET }}${{ matrix.EXTENSION }} + asset_name: eframe_template-${{ matrix.TARGET }}${{ matrix.EXTENSION }} + tag: ${{ github.ref }} + prerelease: ${{ !startsWith(github.ref, 'refs/tags/') }} + overwrite: true diff --git a/.github/workflows/typos.yml.backup b/.github/workflows/typos.yml.backup new file mode 100644 index 0000000..3055f87 --- /dev/null +++ b/.github/workflows/typos.yml.backup @@ -0,0 +1,19 @@ +# Copied from https://github.com/rerun-io/rerun_template + +# https://github.com/crate-ci/typos +# Add exceptions to `.typos.toml` +# install and run locally: cargo install typos-cli && typos + +name: Spell Check +on: [pull_request] + +jobs: + run: + name: Spell Check + runs-on: ubuntu-latest + steps: + - name: Checkout Actions Repository + uses: actions/checkout@v4 + + - name: Check spelling of entire workspace + uses: crate-ci/typos@master diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..54ace48 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +# Mac stuff: +.DS_Store + +# trunk output folder +dist + +# Rust compile target directories: +target +target_ra +target_wasm + +# https://github.com/lycheeverse/lychee +.lycheecache diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/.nojekyll @@ -0,0 +1 @@ + diff --git a/.typos.toml b/.typos.toml new file mode 100644 index 0000000..0e66a4e --- /dev/null +++ b/.typos.toml @@ -0,0 +1,6 @@ +# https://github.com/crate-ci/typos +# install: cargo install typos-cli +# run: typos + +[default.extend-words] +egui = "egui" # Example for how to ignore a false positive diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..e166e71 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "gitConfigUser.selectedProfile": "personal" +} \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..2441614 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,2424 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ab_glyph" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f90148830dac590fac7ccfe78ec4a8ea404c60f75a24e16407a71f0f40de775" +dependencies = [ + "ab_glyph_rasterizer", + "owned_ttf_parser", +] + +[[package]] +name = "ab_glyph_rasterizer" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" + +[[package]] +name = "accesskit" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74a4b14f3d99c1255dcba8f45621ab1a2e7540a0009652d33989005a4d0bfc6b" +dependencies = [ + "enumn", + "serde", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "serde", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "android-activity" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee91c0c2905bae44f84bfa4e044536541df26b7703fd0888deeb9060fcc44289" +dependencies = [ + "android-properties", + "bitflags 2.5.0", + "cc", + "cesu8", + "jni", + "jni-sys", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "num_enum", + "thiserror", +] + +[[package]] +name = "android-properties" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" + +[[package]] +name = "arboard" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb4009533e8ff8f1450a5bcbc30f4242a1d34442221f72314bea1f5dc9c7f89" +dependencies = [ + "clipboard-win", + "log", + "objc2 0.5.1", + "objc2-app-kit", + "objc2-foundation", + "parking_lot", + "x11rb", +] + +[[package]] +name = "as-raw-xcb-connection" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +dependencies = [ + "serde", +] + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "block-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae85a0696e7ea3b835a453750bf002770776609115e6d25c6d2ff28a8200f7e7" +dependencies = [ + "objc-sys", +] + +[[package]] +name = "block2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15b55663a85f33501257357e6421bb33e769d5c9ffb5ba0921c975a123e35e68" +dependencies = [ + "block-sys", + "objc2 0.4.1", +] + +[[package]] +name = "block2" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43ff7d91d3c1d568065b06c899777d1e48dcf76103a672a0adbc238a7f247f1e" +dependencies = [ + "objc2 0.5.1", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "bytemuck" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4da9a32f3fed317401fa3c862968128267c3106685286e15d5aaa3d7389c2f60" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" + +[[package]] +name = "calloop" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fba7adb4dd5aa98e5553510223000e7148f621165ec5f9acd7113f6ca4995298" +dependencies = [ + "bitflags 2.5.0", + "log", + "polling", + "rustix", + "slab", + "thiserror", +] + +[[package]] +name = "calloop-wayland-source" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f0ea9b9476c7fad82841a8dbb380e2eae480c21910feba80725b46931ed8f02" +dependencies = [ + "calloop", + "rustix", + "wayland-backend", + "wayland-client", +] + +[[package]] +name = "cc" +version = "1.0.96" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "065a29261d53ba54260972629f9ca6bffa69bac13cd1fed61420f7fa68b9f8bd" +dependencies = [ + "jobserver", + "libc", + "once_cell", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + +[[package]] +name = "cgl" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ced0551234e87afee12411d535648dd89d2e7f34c78b753395567aff3d447ff" +dependencies = [ + "libc", +] + +[[package]] +name = "clipboard-win" +version = "5.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79f4473f5144e20d9aceaf2972478f06ddf687831eafeeb434fbaf0acc4144ad" +dependencies = [ + "error-code", +] + +[[package]] +name = "cocoa" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c" +dependencies = [ + "bitflags 1.3.2", + "block", + "cocoa-foundation", + "core-foundation", + "core-graphics", + "foreign-types", + "libc", + "objc", +] + +[[package]] +name = "cocoa-foundation" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7" +dependencies = [ + "bitflags 1.3.2", + "block", + "core-foundation", + "core-graphics-types", + "libc", + "objc", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "core-graphics" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" + +[[package]] +name = "cursor-icon" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" + +[[package]] +name = "directories-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + +[[package]] +name = "dlib" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" +dependencies = [ + "libloading", +] + +[[package]] +name = "document-features" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef5282ad69563b5fc40319526ba27e0e7363d552a896f0297d54f767717f9b95" +dependencies = [ + "litrs", +] + +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "ecolor" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20930a432bbd57a6d55e07976089708d4893f3d556cf42a0d79e9e321fa73b10" +dependencies = [ + "bytemuck", + "serde", +] + +[[package]] +name = "eframe" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "020e2ccef6bbcec71dbc542f7eed64a5846fc3076727f5746da8fd307c91bab2" +dependencies = [ + "bytemuck", + "cocoa", + "directories-next", + "document-features", + "egui", + "egui-winit", + "egui_glow", + "glow", + "glutin", + "glutin-winit", + "image", + "js-sys", + "log", + "objc", + "parking_lot", + "percent-encoding", + "raw-window-handle 0.5.2", + "raw-window-handle 0.6.1", + "ron", + "serde", + "static_assertions", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "web-time", + "winapi", + "winit", +] + +[[package]] +name = "egui" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "584c5d1bf9a67b25778a3323af222dbe1a1feb532190e103901187f92c7fe29a" +dependencies = [ + "accesskit", + "ahash", + "epaint", + "log", + "nohash-hasher", + "ron", + "serde", +] + +[[package]] +name = "egui-winit" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e3da0cbe020f341450c599b35b92de4af7b00abde85624fd16f09c885573609" +dependencies = [ + "arboard", + "egui", + "log", + "raw-window-handle 0.6.1", + "serde", + "smithay-clipboard", + "web-time", + "webbrowser", + "winit", +] + +[[package]] +name = "egui_commonmark" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "013480797931a2649e03069613ed35514569372d6f79df70fc3653ae18a75c6c" +dependencies = [ + "egui", + "egui_extras", + "pulldown-cmark", +] + +[[package]] +name = "egui_extras" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b78779f35ded1a853786c9ce0b43fe1053e10a21ea3b23ebea411805ce41593" +dependencies = [ + "egui", + "enum-map", + "image", + "log", + "mime_guess2", + "serde", +] + +[[package]] +name = "egui_glow" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0e5d975f3c86edc3d35b1db88bb27c15dde7c55d3b5af164968ab5ede3f44ca" +dependencies = [ + "bytemuck", + "egui", + "glow", + "log", + "memoffset", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "emath" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4c3a552cfca14630702449d35f41c84a0d15963273771c6059175a803620f3f" +dependencies = [ + "bytemuck", + "serde", +] + +[[package]] +name = "enum-map" +version = "2.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6866f3bfdf8207509a033af1a75a7b08abda06bbaaeae6669323fd5a097df2e9" +dependencies = [ + "enum-map-derive", + "serde", +] + +[[package]] +name = "enum-map-derive" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "enumn" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fd000fd6988e73bbe993ea3db9b1aa64906ab88766d654973924340c8cddb42" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "env_logger" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "epaint" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b381f8b149657a4acf837095351839f32cd5c4aec1817fc4df84e18d76334176" +dependencies = [ + "ab_glyph", + "ahash", + "bytemuck", + "ecolor", + "emath", + "log", + "nohash-hasher", + "parking_lot", + "serde", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "error-code" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0474425d51df81997e2f90a21591180b38eccf27292d755f3e30750225c175b" + +[[package]] +name = "fdeflate" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "flate2" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "gethostname" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" +dependencies = [ + "libc", + "windows-targets 0.48.5", +] + +[[package]] +name = "getrandom" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gl_generator" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" +dependencies = [ + "khronos_api", + "log", + "xml-rs", +] + +[[package]] +name = "glow" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd348e04c43b32574f2de31c8bb397d96c9fcfa1371bd4ca6d8bdc464ab121b1" +dependencies = [ + "js-sys", + "slotmap", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "glutin" +version = "0.31.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18fcd4ae4e86d991ad1300b8f57166e5be0c95ef1f63f3f5b827f8a164548746" +dependencies = [ + "bitflags 2.5.0", + "cfg_aliases", + "cgl", + "core-foundation", + "dispatch", + "glutin_egl_sys", + "glutin_glx_sys", + "glutin_wgl_sys", + "icrate", + "libloading", + "objc2 0.4.1", + "once_cell", + "raw-window-handle 0.5.2", + "wayland-sys", + "windows-sys 0.48.0", + "x11-dl", +] + +[[package]] +name = "glutin-winit" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebcdfba24f73b8412c5181e56f092b5eff16671c514ce896b258a0a64bd7735" +dependencies = [ + "cfg_aliases", + "glutin", + "raw-window-handle 0.5.2", + "winit", +] + +[[package]] +name = "glutin_egl_sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77cc5623f5309ef433c3dd4ca1223195347fe62c413da8e2fdd0eb76db2d9bcd" +dependencies = [ + "gl_generator", + "windows-sys 0.48.0", +] + +[[package]] +name = "glutin_glx_sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a165fd686c10dcc2d45380b35796e577eacfd43d4660ee741ec8ebe2201b3b4f" +dependencies = [ + "gl_generator", + "x11-dl", +] + +[[package]] +name = "glutin_wgl_sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8098adac955faa2d31079b65dc48841251f69efd3ac25477903fc424362ead" +dependencies = [ + "gl_generator", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "icrate" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d3aaff8a54577104bafdf686ff18565c3b6903ca5782a2026ef06e2c7aa319" +dependencies = [ + "block2 0.3.0", + "dispatch", + "objc2 0.4.1", +] + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "image" +version = "0.24.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "num-traits", + "png", +] + +[[package]] +name = "include_dir" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18762faeff7122e89e0857b02f7ce6fcc0d101d5e9ad2ad7846cc01d61b7f19e" +dependencies = [ + "include_dir_macros", +] + +[[package]] +name = "include_dir_macros" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b139284b5cf57ecfa712bcc66950bb635b31aff41c188e8a4cfc758eca374a3f" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "indexmap" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "is-terminal" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "jobserver" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "khronos_api" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" + +[[package]] +name = "libc" +version = "0.2.154" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" + +[[package]] +name = "libloading" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" +dependencies = [ + "cfg-if", + "windows-targets 0.52.5", +] + +[[package]] +name = "libredox" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3af92c55d7d839293953fcd0fda5ecfe93297cfde6ffbdec13b41d99c0ba6607" +dependencies = [ + "bitflags 2.5.0", + "libc", + "redox_syscall 0.4.1", +] + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.5.0", + "libc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + +[[package]] +name = "litrs" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "memchr" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + +[[package]] +name = "memmap2" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess2" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a3333bb1609500601edc766a39b4c1772874a4ce26022f4d866854dc020c41" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "miniz_oxide" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +dependencies = [ + "adler", + "simd-adler32", +] + +[[package]] +name = "ndk" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7" +dependencies = [ + "bitflags 2.5.0", + "jni-sys", + "log", + "ndk-sys", + "num_enum", + "raw-window-handle 0.5.2", + "raw-window-handle 0.6.1", + "thiserror", +] + +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + +[[package]] +name = "ndk-sys" +version = "0.5.0+25.2.9519653" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_enum" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + +[[package]] +name = "objc-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da284c198fb9b7b0603f8635185e85fbd5b64ee154b1ed406d489077de2d6d60" + +[[package]] +name = "objc2" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "559c5a40fdd30eb5e344fbceacf7595a81e242529fb4e21cf5f43fb4f11ff98d" +dependencies = [ + "objc-sys", + "objc2-encode 3.0.0", +] + +[[package]] +name = "objc2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4b25e1034d0e636cd84707ccdaa9f81243d399196b8a773946dcffec0401659" +dependencies = [ + "objc-sys", + "objc2-encode 4.0.1", +] + +[[package]] +name = "objc2-app-kit" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb79768a710a9a1798848179edb186d1af7e8a8679f369e4b8d201dd2a034047" +dependencies = [ + "block2 0.5.0", + "objc2 0.5.1", + "objc2-core-data", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-data" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e092bc42eaf30a08844e6a076938c60751225ec81431ab89f5d1ccd9f958d6c" +dependencies = [ + "block2 0.5.0", + "objc2 0.5.1", + "objc2-foundation", +] + +[[package]] +name = "objc2-encode" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d079845b37af429bfe5dfa76e6d087d788031045b25cfc6fd898486fd9847666" + +[[package]] +name = "objc2-encode" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88658da63e4cc2c8adb1262902cd6af51094df0488b760d6fd27194269c0950a" + +[[package]] +name = "objc2-foundation" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfaefe14254871ea16c7d88968c0ff14ba554712a20d76421eec52f0a7fb8904" +dependencies = [ + "block2 0.5.0", + "objc2 0.5.1", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "orbclient" +version = "0.3.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52f0d54bde9774d3a51dcf281a5def240c71996bc6ca05d2c847ec8b2b216166" +dependencies = [ + "libredox 0.0.2", +] + +[[package]] +name = "owned_ttf_parser" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4586edfe4c648c71797a74c84bacb32b52b212eff5dfe2bb9f2c599844023e7" +dependencies = [ + "ttf-parser", +] + +[[package]] +name = "parking_lot" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.5.1", + "smallvec", + "windows-targets 0.52.5", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "png" +version = "0.17.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "polling" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645493cf344456ef24219d02a768cf1fb92ddf8c92161679ae3d91b91a637be3" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix", + "tracing", + "windows-sys 0.52.0", +] + +[[package]] +name = "proc-macro-crate" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +dependencies = [ + "toml_edit 0.21.1", +] + +[[package]] +name = "proc-macro2" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "pulldown-cmark" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76979bea66e7875e7509c4ec5300112b316af87fa7a252ca91c448b32dfe3993" +dependencies = [ + "bitflags 2.5.0", + "memchr", + "unicase", +] + +[[package]] +name = "quick-xml" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "raw-window-handle" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" + +[[package]] +name = "raw-window-handle" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cc3bcbdb1ddfc11e700e62968e6b4cc9c75bb466464ad28fb61c5b2c964418b" + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +dependencies = [ + "bitflags 2.5.0", +] + +[[package]] +name = "redox_users" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" +dependencies = [ + "getrandom", + "libredox 0.1.3", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" + +[[package]] +name = "rk_playground" +version = "0.1.0" +dependencies = [ + "eframe", + "egui", + "egui_commonmark", + "egui_extras", + "env_logger", + "include_dir", + "log", + "serde", + "toml", + "wasm-bindgen-futures", +] + +[[package]] +name = "ron" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" +dependencies = [ + "base64", + "bitflags 2.5.0", + "serde", + "serde_derive", +] + +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags 2.5.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serde" +version = "1.0.200" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.200" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_spanned" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +dependencies = [ + "serde", +] + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "slotmap" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" +dependencies = [ + "version_check", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "smithay-client-toolkit" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "922fd3eeab3bd820d76537ce8f582b1cf951eceb5475c28500c7457d9d17f53a" +dependencies = [ + "bitflags 2.5.0", + "calloop", + "calloop-wayland-source", + "cursor-icon", + "libc", + "log", + "memmap2", + "rustix", + "thiserror", + "wayland-backend", + "wayland-client", + "wayland-csd-frame", + "wayland-cursor", + "wayland-protocols", + "wayland-protocols-wlr", + "wayland-scanner", + "xkeysym", +] + +[[package]] +name = "smithay-clipboard" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c091e7354ea8059d6ad99eace06dd13ddeedbb0ac72d40a9a6e7ff790525882d" +dependencies = [ + "libc", + "smithay-client-toolkit", + "wayland-backend", +] + +[[package]] +name = "smol_str" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6845563ada680337a52d43bb0b29f396f2d911616f6573012645b9e3d048a49" +dependencies = [ + "serde", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "syn" +version = "2.0.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "toml" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.22.12", +] + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.22.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow 0.6.8", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" + +[[package]] +name = "ttf-parser" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4" + +[[package]] +name = "unicase" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "wayland-backend" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d50fa61ce90d76474c87f5fc002828d81b32677340112b4ef08079a9d459a40" +dependencies = [ + "cc", + "downcast-rs", + "rustix", + "scoped-tls", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-client" +version = "0.31.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82fb96ee935c2cea6668ccb470fb7771f6215d1691746c2d896b447a00ad3f1f" +dependencies = [ + "bitflags 2.5.0", + "rustix", + "wayland-backend", + "wayland-scanner", +] + +[[package]] +name = "wayland-csd-frame" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" +dependencies = [ + "bitflags 2.5.0", + "cursor-icon", + "wayland-backend", +] + +[[package]] +name = "wayland-cursor" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71ce5fa868dd13d11a0d04c5e2e65726d0897be8de247c0c5a65886e283231ba" +dependencies = [ + "rustix", + "wayland-client", + "xcursor", +] + +[[package]] +name = "wayland-protocols" +version = "0.31.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f81f365b8b4a97f422ac0e8737c438024b5951734506b0e1d775c73030561f4" +dependencies = [ + "bitflags 2.5.0", + "wayland-backend", + "wayland-client", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-plasma" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23803551115ff9ea9bce586860c5c5a971e360825a0309264102a9495a5ff479" +dependencies = [ + "bitflags 2.5.0", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-wlr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6" +dependencies = [ + "bitflags 2.5.0", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b3a62929287001986fb58c789dce9b67604a397c15c611ad9f747300b6c283" +dependencies = [ + "proc-macro2", + "quick-xml", + "quote", +] + +[[package]] +name = "wayland-sys" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15a0c8eaff5216d07f226cb7a549159267f3467b289d9a2e52fd3ef5aae2b7af" +dependencies = [ + "dlib", + "log", + "once_cell", + "pkg-config", +] + +[[package]] +name = "web-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa30049b1c872b72c89866d458eae9f20380ab280ffd1b1e18df2d3e2d98cfe0" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webbrowser" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db67ae75a9405634f5882791678772c94ff5f16a66535aae186e26aa0841fc8b" +dependencies = [ + "core-foundation", + "home", + "jni", + "log", + "ndk-context", + "objc", + "raw-window-handle 0.5.2", + "url", + "web-sys", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.5", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" + +[[package]] +name = "winit" +version = "0.29.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d59ad965a635657faf09c8f062badd885748428933dad8e8bdd64064d92e5ca" +dependencies = [ + "ahash", + "android-activity", + "atomic-waker", + "bitflags 2.5.0", + "bytemuck", + "calloop", + "cfg_aliases", + "core-foundation", + "core-graphics", + "cursor-icon", + "icrate", + "js-sys", + "libc", + "log", + "memmap2", + "ndk", + "ndk-sys", + "objc2 0.4.1", + "once_cell", + "orbclient", + "percent-encoding", + "raw-window-handle 0.5.2", + "raw-window-handle 0.6.1", + "redox_syscall 0.3.5", + "rustix", + "smithay-client-toolkit", + "smol_str", + "unicode-segmentation", + "wasm-bindgen", + "wasm-bindgen-futures", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-protocols-plasma", + "web-sys", + "web-time", + "windows-sys 0.48.0", + "x11-dl", + "x11rb", + "xkbcommon-dl", +] + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d" +dependencies = [ + "memchr", +] + +[[package]] +name = "x11-dl" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +dependencies = [ + "libc", + "once_cell", + "pkg-config", +] + +[[package]] +name = "x11rb" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d91ffca73ee7f68ce055750bf9f6eca0780b8c85eff9bc046a3b0da41755e12" +dependencies = [ + "as-raw-xcb-connection", + "gethostname", + "libc", + "libloading", + "once_cell", + "rustix", + "x11rb-protocol", +] + +[[package]] +name = "x11rb-protocol" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d" + +[[package]] +name = "xcursor" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a0ccd7b4a5345edfcd0c3535718a4e9ff7798ffc536bb5b5a0e26ff84732911" + +[[package]] +name = "xkbcommon-dl" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" +dependencies = [ + "bitflags 2.5.0", + "dlib", + "log", + "once_cell", + "xkeysym", +] + +[[package]] +name = "xkeysym" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "054a8e68b76250b253f671d1268cb7f1ae089ec35e195b2efb2a4e9a836d0621" + +[[package]] +name = "xml-rs" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791978798f0597cfc70478424c2b4fdc2b7a8024aaff78497ef00f24ef674193" + +[[package]] +name = "zerocopy" +version = "0.7.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "087eca3c1eaf8c47b94d02790dd086cd594b912d2043d4de4bfdd466b3befb7c" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f4b6c273f496d8fd4eaf18853e6b448760225dc030ff2c485a786859aea6393" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..13b635c --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,57 @@ +# cargo-features = ["edition2024"] + +[package] +name = "rk_playground" +version = "0.1.0" +authors = ["Ruben Kharel "] +edition = "2021" +include = ["LICENSE-APACHE", "LICENSE-MIT", "**/*.rs", "Cargo.toml"] +rust-version = "1.76" + +[package.metadata.docs.rs] +all-features = true +targets = ["x86_64-unknown-linux-gnu", "wasm32-unknown-unknown"] + +[dependencies] +egui = "0.27.0" +eframe = { version = "0.27.0", default-features = false, features = [ +# "accesskit", # Make egui comptaible with screen readers. NOTE: adds a lot of dependencies. + "default_fonts", # Embed the default egui fonts. + "glow", # Use the glow rendering backend. Alternative: "wgpu". + "persistence", # Enable restoring app state when restarting the app. +] } +log = "0.4" + +# You only need serde if you want app persistence: +serde = { version = "1", features = ["derive"] } +egui_extras = "0.27.2" +toml = "0.8.12" +egui_commonmark = "0.15.0" +include_dir = "0.7.3" + +# native: +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +env_logger = "0.10" + +# web: +[target.'cfg(target_arch = "wasm32")'.dependencies] +wasm-bindgen-futures = "0.4" + + +[profile.release] +opt-level = 2 # fast and small wasm + +# Optimize all dependencies even in debug builds: +[profile.dev.package."*"] +opt-level = 2 + + +[patch.crates-io] + +# for bleeding edge version of egui and eframe: +# egui = { git = "https://github.com/emilk/egui", branch = "master" } +# eframe = { git = "https://github.com/emilk/egui", branch = "master" } + +# If you fork https://github.com/emilk/egui you can test with: +# egui = { path = "../egui/crates/egui" } +# eframe = { path = "../egui/crates/eframe" } diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..9b739e3 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +## Blog + Resume (sofar) + +- Rewriting in rust & wasm diff --git a/Trunk.toml b/Trunk.toml new file mode 100644 index 0000000..a7b19c9 --- /dev/null +++ b/Trunk.toml @@ -0,0 +1 @@ +[build] diff --git a/_headers b/_headers new file mode 100644 index 0000000..7040a1c --- /dev/null +++ b/_headers @@ -0,0 +1,5 @@ +/* +Cache-Control: no-cache +Hello: world +Access-Control-Allow-Origin: * +Access-Control-Allow-Methods: GET \ No newline at end of file diff --git a/assets/favicon.ico b/assets/favicon.ico new file mode 100755 index 0000000..61ad031 Binary files /dev/null and b/assets/favicon.ico differ diff --git a/assets/icon-1024.png b/assets/icon-1024.png new file mode 100644 index 0000000..1b5868a Binary files /dev/null and b/assets/icon-1024.png differ diff --git a/assets/icon-256.png b/assets/icon-256.png new file mode 100644 index 0000000..ae72287 Binary files /dev/null and b/assets/icon-256.png differ diff --git a/assets/icon_ios_touch_192.png b/assets/icon_ios_touch_192.png new file mode 100644 index 0000000..8472802 Binary files /dev/null and b/assets/icon_ios_touch_192.png differ diff --git a/assets/manifest.json b/assets/manifest.json new file mode 100644 index 0000000..2c78fbb --- /dev/null +++ b/assets/manifest.json @@ -0,0 +1,28 @@ +{ + "name": "rk_playground", + "short_name": "rk_playground", + "icons": [ + { + "src": "icon-256.png", + "sizes": "256x256", + "type": "image/png" + }, + { + "src": "maskable_icon_x512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "any maskable" + }, + { + "src": "icon-1024.png", + "sizes": "1024x1024", + "type": "image/png" + } + ], + "lang": "en-US", + "id": "index.html", + "start_url": "index.html", + "display": "standalone", + "background_color": "white", + "theme_color": "white" +} diff --git a/assets/maskable_icon_x512.png b/assets/maskable_icon_x512.png new file mode 100644 index 0000000..db8df3e Binary files /dev/null and b/assets/maskable_icon_x512.png differ diff --git a/assets/sw.js b/assets/sw.js new file mode 100644 index 0000000..69e6f43 --- /dev/null +++ b/assets/sw.js @@ -0,0 +1,25 @@ +var cacheName = 'rk_playground_cache'; +var filesToCache = [ + './', + './index.html', + './rk_playground.js', + './rk_playground_bg.wasm', +]; + +/* Start the service worker and cache all of the app's content */ +self.addEventListener('install', function (e) { + e.waitUntil( + caches.open(cacheName).then(function (cache) { + return cache.addAll(filesToCache); + }) + ); +}); + +/* Serve cached content when offline */ +self.addEventListener('fetch', function (e) { + e.respondWith( + caches.match(e.request).then(function (response) { + return response || fetch(e.request); + }) + ); +}); diff --git a/check.sh b/check.sh new file mode 100755 index 0000000..522dab1 --- /dev/null +++ b/check.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +# This scripts runs various CI-like checks in a convenient way. +set -eux + +cargo check --quiet --workspace --all-targets +cargo check --quiet --workspace --all-features --lib --target wasm32-unknown-unknown +cargo fmt --all -- --check +cargo clippy --quiet --workspace --all-targets --all-features -- -D warnings -W clippy::all +cargo test --quiet --workspace --all-targets --all-features +cargo test --quiet --workspace --doc +trunk build diff --git a/index.html b/index.html new file mode 100644 index 0000000..de4c330 --- /dev/null +++ b/index.html @@ -0,0 +1,157 @@ + + + + + + + + + + Ruben Kharel + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/rust-toolchain b/rust-toolchain new file mode 100644 index 0000000..031b4cc --- /dev/null +++ b/rust-toolchain @@ -0,0 +1,10 @@ +# If you see this, run "rustup self update" to get rustup 1.23 or newer. + +# NOTE: above comment is for older `rustup` (before TOML support was added), +# which will treat the first line as the toolchain name, and therefore show it +# to the user in the error, instead of "error: invalid channel name '[toolchain]'". + +[toolchain] +channel = "1.76.0" +components = [ "rustfmt", "clippy" ] +targets = [ "wasm32-unknown-unknown" ] diff --git a/src/_data/education.toml b/src/_data/education.toml new file mode 100644 index 0000000..fa29f3a --- /dev/null +++ b/src/_data/education.toml @@ -0,0 +1,11 @@ +[[education]] +degree = "Bsc. IT (Hons) Networking & IT Security" +uni = "London Metropolitan University" +year = "2017 — 2021" +summary = "Overall, this course gave me a strong foundation of knowledge in computer networks, network security, and cyber security. It has given me the tools and understanding to work in this field and be able to design, implement, and manage secure web and networks." + +[[education]] +degree = "Mobile Solution Development" +uni = "Conestoga College" +year = "2024 — Present" +summary = "Learning Android / iOS and more..." \ No newline at end of file diff --git a/src/_data/experience.toml b/src/_data/experience.toml new file mode 100644 index 0000000..e03b22f --- /dev/null +++ b/src/_data/experience.toml @@ -0,0 +1,56 @@ +[[experience]] +company = "Nassec" +summary = "A young security enthusiast driven company filled with security researchers to provide ultimate security solution for websites and mobile applications." +logo = "/img/companylogos/nassec.jpg" +url = "https://www.reconwithme.com/" + +[[experience.positions]] +title = "Security Researcher & Scripting" +startdate = "2020-10-14" +enddate = "2021-01-01" +summary = "Worked on scrapping some security related stuff, developed few mini tools for scanning the web. Strongify my knowledge on OSWAP Top 10" +projects = ["scrappers", "pentesting", "OSWAP top 10"] + +[[experience.positions]] +title = "Backend Developer" +startdate = "2021-01-02" +enddate = "2021-11-31" +summary = "As a backend developer of a vulnerability scanning tool, I developed RESTful api which scans various tools and technelogy and gather vulnerable informations of the target." +projects = [ + "Built API for vulnerability scanning tool, focusing on web app security.", + "Managed AWS infrastructure, implemented CI/CD via GitHub Actions and Docker.", + "Designed and implemented few projects, exploring different approaches." +] + +[[experience.positions]] +title = "Project Manager" +startdate = "2021-12-01" +enddate = "2022-07-31" +summary = "Managed technical aspect of the project called ReconwithMe, which was an automated vulnerability scanning and penetration testing tool, developed by security researchers for IT security teams." +projects = [ + "Guided the technical team throughout the development process.", + "Conducted training sessions for interns, focusing on RESTful API development using Express.js." +] + +[[experience]] +company = "InnovateTech" +logo = "/img/companylogos/innovatetech.png" +url = "https://www.innovatetech.io/" + +[[experience.positions]] +title = "DevOps Intern" +startdate = "2022-10-01" +enddate = "2022-12-01" +summary = "Internship where I gained hands-on experience with Kubernetes, Helm, Docker, and various AWS services. Deployed ASP Core .NET app and an open-source tool with GitLab CI/CD using Docker on AWS EKS with Helm. Explored multiple AWS services." + +[[experience]] +company = "Dharahara Clothings" +logo = "/img/companylogos/dharahara_clothings.jpg" +url = "https://www.instagram.com/dharahara_clothings/?hl=en" + +[[experience.positions]] +title = "FullStack Developer" +startdate = "2023-01-01" +enddate = "2023-11-01" +summary = "Led Node.js/MongoDB API development with JWT auth for high-traffic shopping platform. Utilized AWS, CI/CD, and led Next.js/Tailwind CSS for client-side app." + diff --git a/src/_data/interests.toml b/src/_data/interests.toml new file mode 100644 index 0000000..eb085c8 --- /dev/null +++ b/src/_data/interests.toml @@ -0,0 +1,5 @@ +[interest] +description = [ + "Curious about tech, also non-geeky side of me loves wondering about the universe.", + "Blogging, a recent new interest of mine." +] \ No newline at end of file diff --git a/src/_data/links.toml b/src/_data/links.toml new file mode 100644 index 0000000..e5ebb7b --- /dev/null +++ b/src/_data/links.toml @@ -0,0 +1,9 @@ +[[link]] +name = "LinkedIn" +url = "https://linkedin.com/in/rubenkharel" +label = "rubenkharel" + +[[link]] +name = "GitHub" +url = "https://github.com/rukh-debug" +label = "rukh-debug" diff --git a/src/_data/projects.toml b/src/_data/projects.toml new file mode 100644 index 0000000..b11dff4 --- /dev/null +++ b/src/_data/projects.toml @@ -0,0 +1,27 @@ +[[project]] +name = "eReKon" +role = "Developer, Open-Source Contributor" +duration = "2021 — Present" +url = "https://github.com/rukh-debug/erekon" +description = """ +eReKon is a web recon tool that helps search and scan information related to a website or domain. It mines domain info, gathers subdomains, scrapes and indexes public information, finds website technologies, and retrieves DNS and WHOIS information. In a nutshell, it is a single solution to quickly identify public information, find hidden targets, and monitor changes. +""" + +[[project]] +name = "talking-pdf" +role = "React Dev, Open-Source Contributor" +duration = "2023 — Present" +url = "https://rubenk.dev/talking-pdf" +description = """ +A simple web app that takes PDF as input and lets you interact with it through a chat interface. It is built using React, Next.js, and SCSS. Powered by OpenAI's GPT-3 API. +""" + +[[project]] +name = "vstatus" +role = "Developer" +duration = "2023 — Present" +url = "https://github.com/rukh-debug/vstatus" +description = """ +vstatus is a Visual Studio Code extension that tracks your workspace and file durations, generates insightful activity visualizations, and provides a live status image via an HTTP server. Initially intended to be used for a GitHub readme page. +""" + diff --git a/src/_data/recognitions.toml b/src/_data/recognitions.toml new file mode 100644 index 0000000..55e2e34 --- /dev/null +++ b/src/_data/recognitions.toml @@ -0,0 +1,29 @@ +[[recognition]] +name = "Introduction to Cybersecurity" +organization = "Cisco" +year = "Certified 2020" +summary = """ +
+
+
+
+ +""" + +[[recognition]] +name = "Docker Essentials: A Developer Introduction" +organization = "IBM" +year = "Certified 2022" +summary = """ +
+
+
+
+ +""" diff --git a/src/_data/resume.toml b/src/_data/resume.toml new file mode 100644 index 0000000..1cddb29 --- /dev/null +++ b/src/_data/resume.toml @@ -0,0 +1,22 @@ +[header] +name = "Ruben kharel" +display_contact_info = true +current_title = "Fullstack Dev && DevOps" +intro = "I am a developer and devops guy actively developing webs and also a learner, passively learning various nerdy stuff." + +[contact] +email = "resume@rubenk.dev" + +[sections] +experience = true +education = true +projects = true +skills = true +recognition = true +interests = true +links = true + +[social] +website = "http://rubenk.dev" +github = "https://github.com/rukh-debug" +linkedin = "https://linkedin.com/in/rubenkharel" diff --git a/src/_data/skills.toml b/src/_data/skills.toml new file mode 100644 index 0000000..1dee5ce --- /dev/null +++ b/src/_data/skills.toml @@ -0,0 +1,16 @@ +[skill] +name = "fullstack Dev && DevOps" +description = "3+ years expertise in full stack development. experienced in DevOps practices for end-to-end delivery of scalable and secure applications." + +[skill.ToolsTech] +languages = ["rust", "c#", "nodejs/ts", "python", "kotlin", "swift"] +databases = ["sql", "nosql"] +cloud_services = ["AWS Services"] +containers = ["docker", "Kubernetes"] +version_control = ["git/github/gitlab", "actions", "pipelines"] +api_testing = ["RESTful API", "Postman API testing"] +scripting = ["bash"] +editors = ["Vim", "vsCode"] +os = ["linux (Arch, Debian)"] +tools = ["tmux"] +security_tools = ["nmap", "nc", "metasploit", "frida", "burp"] diff --git a/src/_post/hyprland_productivity.toml b/src/_post/hyprland_productivity.toml new file mode 100644 index 0000000..58c2c57 --- /dev/null +++ b/src/_post/hyprland_productivity.toml @@ -0,0 +1,118 @@ +[Blog] +layout = "post" +title = "For the love of Tiling Window Managers (Hyprland)" +description = "Say goodbye to cluttered screens and hello to smooth sailing" +date = "2022-01-15T12:30:00+05:45" +tags = ["Linux"] +image = "img/postbanners/oss.png" +markdown = """ +` +The joy and struggle of linux is found in its freedom. The joy of using what you like. The struggle of finding what that is.` + +- u/Own-Cupcake7586 + +### Journey + +I used to jump from one Linux distribution to another, always looking for the perfect desktop. I didn't know much about window managers or desktop environments back then. I tried popular options like GNOME and KDE, but nothing felt quite right. + +Then I discovered i3, my first tiling window manager. At first, it was strange. Everything was controlled by keyboard shortcuts, and I had to learn a whole new way of managing windows. It was tough at the beginning, but after a few days, something clicked. + +I felt the need of mouse to move around was little to none. Windows automatically organized themselves on the screen, and I could do everything with just the keyboard. It was so much faster and more efficient. + +I was amazed by how simple and elegant it was. I was hooked on tiling window managers, and I knew I wouldn't go back. (and i cannot atm, because i find other WM unusable now) + +### Tiling Window Manager + +https://en.wikipedia.org/wiki/Tiling_window_manager + +Wikipedia defines a tiling window manager as a system that organizes windows into non-overlapping frames, unlike the usual way of stacking windows on top of each other. That's exactly what it is, But let's break it down into simpler terms for the less geeky peps. + +Let's look at the below screenshot to give a gist of what it is. + + +![Dwm-screenshot](https://en.wikipedia.org/wiki/Tiling_window_manager#/media/File:Dwm-screenshot.png) + + + +### Journy continues on Tiling WM + +For many, the journey into the world of tiling window managers begins with the renowned [i3wm](https://i3wm.org/). It's a fantastic option, known for its stability and robust features, especially for those using the [X11 display server](https://www.x.org/wiki/). However, as I got into [Wayland](https://wayland.freedesktop.org/), the search for the perfect tiling companion continued. While [Sway](https://swaywm.org/) presented itself as a compelling alternative with its i3-compatible configuration, my exploration ultimately led me to [Hyprland](https://hyprland.org/). + +### ...and now Hyprland + +Hyprland caught my eye because it's a really attractive window manager, even without any extra setup. It's exactly what I was looking for: a customizable yet beautiful window manager. + +Hyprland is special because it lets you personalize almost everything while still being easy to use and **looking great**. It's similar to other window managers in what it can do, but it stands out because it runs so smoothly. ([not sure if you have nvidia](https://www.youtube.com/watch?v=xPh-5P4XH6o)) + + +#### Setup hyprland + +[official documentation](https://wiki.hyprland.org/Getting-Started/Installation/) to setup hyprland is here if you happen to try it out. + +##### Opening window + +To open a new window, press `mod+enter`. This opens a terminal by default, but you can customize it to open any application you want. It's a quick and easy way to launch programs without using the mouse. + +##### Moving windows + +To move a window, press `mod+shift+arrow keys`. This allows you to quickly rearrange windows on the screen. It's a simple but powerful feature that makes multitasking a breeze. + +##### Resizing windows + +To resize a window, press `mod+r` to enter resize mode, then use the arrow keys to adjust the size. This is a great way to customize your workspace and make the most of your screen real estate. + +##### Changing workspaces + +To switch between workspaces, press `mod+number`. This lets you organize your windows into different groups, so you can keep your workflow organized and efficient. + +##### Secret workspace + +Hyprland has a secret workspace feature that lets you hide windows from view. To activate it, press `mod+shift+s`. This is a handy way to keep your desktop clutter-free and focus on the task at hand. + +##### Bindings + +Hyprland lets you create custom key bindings to suit your workflow. You can remap keys, create new shortcuts, and customize the window manager to your heart's content. This level of customization is what sets Hyprland apart from other window managers. + +### Beyond the Basics + +While the basic features of Hyprland are powerful on their own, there's so much more you can do with this window manager. Here are a few advanced features that can take your productivity to the next level: + +- **Scripting**: Hyprland supports scripting languages like Python and Lua, allowing you to automate tasks and create custom workflows. +- **Custom layouts**: You can create custom layouts to organize your windows in unique ways. This is great for multitasking and keeping your workspace tidy. +- **Integrations**: Hyprland integrates with other tools and applications, allowing you to create a seamless workflow across different programs. + +### Conclusion + +Hyprland has been a game-changer for me. It's fast, efficient, and beautiful, and it has completely transformed the way I work on my computer. If you're looking to supercharge your productivity and streamline your workflow, I highly recommend giving Hyprland (Or any other tiling window manager) a try. + +I encourage you to explore the world of tiling window managers and find what works best for you. It may take some time to + +==== Outline ==== +- opening window +- moving windows +- resizing windows +- changing workspaces +- secret workspace +- bindings + + +--- + +Provide a basic overview of the installation process (depending on the user's distro). +Mention any dependencies or configuration files needed. +Point to resources like the official documentation or community guides. +Hyprland in Action: + +Showcase your personal Hyprland setup with screenshots or even a short video demonstration. +Highlight your favorite features and customizations. +Explain how Hyprland improves your workflow and productivity. +Beyond the Basics: + +Discuss more advanced Hyprland features like scripting, custom layouts, and integrations with other tools. +Offer tips and tricks for optimizing your Hyprland experience. +Conclusion: + +Summarize your overall experience with Hyprland and why you recommend it. +Encourage readers to explore the world of tiling window managers and find what works best for them. +End with a call to action, inviting readers to share their own Hyprland setups or ask questions. +""" \ No newline at end of file diff --git a/src/_post/published.toml b/src/_post/published.toml new file mode 100644 index 0000000..1089737 --- /dev/null +++ b/src/_post/published.toml @@ -0,0 +1,2 @@ +[published] +blogs = ["aws_ses", "py_publish", "sane_apps", "spf_dkim"] \ No newline at end of file diff --git a/src/_post/published/aws_ses.toml b/src/_post/published/aws_ses.toml new file mode 100644 index 0000000..5d8df46 --- /dev/null +++ b/src/_post/published/aws_ses.toml @@ -0,0 +1,71 @@ +[Blog] +title = "Tips to get out of AWS SES sandbox environment." +description = "If you are struggling with getting access to production environment for AWS SES, then this post might be helpful for you." +date = "2023-01-24T01:04:03+05:45" +tags = ["AWS"] +image = "img/postbanners/awsses.png" +markdown = """ +# Getting out of AWS SES sandbox environment + +If you're working with Amazon Web Services (AWS) Simple Email Service (SES), you may have encountered the limitations of the sandbox environment. This environment restricts your ability to send emails to unverified addresses and limits your sending quota. In this technical guide, we'll walk through the process of requesting access to move out of the sandbox environment and highlight key configurations to ensure a smooth transition. + +## Prerequisites + +Before we dive in, let's assume you're already familiar with the limited features of AWS SES in the sandbox environment and understand the process of requesting a move out of the sandbox through the AWS support team. The AWS user interface is intuitive, making it easy to navigate and understand. + +If you've previously attempted to request access but were rejected, it might be due to insufficient information or missing details in your request. In such cases, you may have received a response from the support team asking for clarification or additional information. A common reply from the support team might look like this: + +``` +We reviewed your request and determined that your use of Amazon SES could have a negative impact on our service. We are denying this request to prevent other Amazon SES customers from experiencing interruptions in service. +``` + +Our goal is to provide you with the necessary information to make a clear and effective request to the AWS support team, enabling you to access the full features of AWS SES. + +## Key Configurations + +Before submitting your request, ensure that the following configurations are in place: + +1. **DomainKeys Identified Mail (DKIM) configuration**: DKIM is an email authentication technique that allows recipients to verify that an email was indeed sent and authorized by the owner of the domain. It involves adding a digital signature to the email headers. To learn more about DKIM and other related concepts like SPF and DMARC, check out this in-depth article: [What is SPF, DKIM and DMARC?](/2023/05/02/what-is-spf-dkim-dmarc.html) + +2. **Custom MAIL FROM domain**: Configure a custom MAIL FROM domain to enhance your email deliverability and branding. This domain is used in the "From" address of your emails and helps establish trust with recipients' email servers. + +3. **Email feedback forwarding**: Set up email feedback forwarding using Amazon Simple Notification Service (SNS). This allows you to handle bounce, complaint, and delivery notifications programmatically. Create SNS topics for each type of feedback and configure SES to publish notifications to these topics. + +## Crafting Your Request + +When you're ready to submit your request to the AWS support team, you'll be asked to provide detailed information about your use case and email sending practices. Here are some sample responses to common questions: + +### Use Case Description + +``` +Our email subscription and system-notification services are designed to provide users with timely and relevant information. The process begins when a user creates an account on our platform, opts in to receive our email newsletter, and chooses to receive notifications. + +We use the service to send important updates and notifications related to the user's account, such as account verification emails, password reset emails, and other critical account-related information. These emails are triggered by specific actions on the platform and are automatically sent to the user's registered email address. + +Emails are only sent to users who have explicitly opted in, and each email includes an unsubscribe link that allows users to opt out of future communications if desired. We monitor the delivery and open rates of the sent emails and have implemented HTTP endpoints in our API server to receive bounce and complaint notifications via SNS. All events are stored in our database for further analysis and auditing. +``` + +### Recipient Opt-In Process + +``` +We take great care to send emails only to individuals who have explicitly requested to receive communications from us. Users can indicate their interest in receiving emails and provide their contact information by completing opt-in forms on our website. Every email we send includes a clear and prominent unsubscribe option, allowing recipients to easily opt out of future emails. Additionally, we comply with all laws and regulations related to sending commercial emails, such as the CAN-SPAM Act in the United States. We respect individuals' privacy and take measures to avoid sending unsolicited emails. +``` + +### Bounce and Complaint Handling + +``` +We have a well-defined process in place to handle bounce and complaint notifications. If we receive a bounce notification indicating that an email we sent was undeliverable, we immediately flag the corresponding email address as invalid in our contact list and take precautions to avoid sending any further emails to that address. Similarly, if we receive a complaint notification indicating that one of our emails was marked as spam by a recipient, we promptly investigate the issue and make necessary adjustments to ensure our emails adhere to industry best practices and guidelines. + +We have implemented HTTP endpoints in our server to capture bounce and complaint notifications, and all events are logged in our database. This allows us to track and monitor these notifications effectively and also alerts our team of administrators to take appropriate actions. Our primary goal is to maintain high deliverability rates and ensure that our communications are well-received by our recipients. We value their feedback and continuously strive to improve the quality and relevance of our emails. +``` + +## Conclusion + +By providing detailed and technical information about your email sending practices, configurations, and handling of bounce and complaint notifications, you increase the likelihood of a successful request to move out of the AWS SES sandbox environment. + +If your request is rejected without a specific reason, don't hesitate to reopen the case and ask for clarification on what additional steps you need to take. Be concise and direct in your communication. In some cases, a senior support member may review your application and find it satisfactory, granting you access to the full features of AWS SES. + +Remember to regularly monitor your email sending metrics, maintain proper email hygiene, and promptly address any issues that arise to ensure ongoing compliance with AWS SES guidelines. + +We hope this technical guide helps you navigate the process of moving out of the AWS SES sandbox environment. Happy emailing! +""" \ No newline at end of file diff --git a/src/_post/published/py_publish.toml b/src/_post/published/py_publish.toml new file mode 100644 index 0000000..966103f --- /dev/null +++ b/src/_post/published/py_publish.toml @@ -0,0 +1,193 @@ +[Blog] +title = "Python Packaging and Publishing CLI Package with pyproject.toml" +description = "This blog is focused on step to step guide to package and publish python CLI tool/package with pyproject.toml." +date = "2023-04-07 11:12:03 +0545" +tags = ["Python"] +image = "img/postbanners/python_plus_toml.png" +markdown = """ + +# The Plot + +Recently I was packaging a CLI tool build on python and I was using `setup.py` to package and publish the package. But I was not happy with the way `setup.py` works. So I started looking for alternatives and I found `pyproject.toml` which is a new standard for packaging python packages. So I decided to give it a try and I was amazed by the simplicity and ease of use of `pyproject.toml`. So I decided to write a blog on it. + +# What is pyproject.toml? + +`pyproject.toml` is a new standard for packaging python packages. It is a configuration file for python projects. It is a replacement for `setup.py` and `setup.cfg`. It is a standard defined by [PEP 518](https://www.python.org/dev/peps/pep-0518/). + +Although `pyproject.toml` is a new standard but it is supported by all the major python packaging tools like `pip`, `setuptools`, `poetry`, `flit`, `build`, etc. So you can use any of the packaging tools to package and publish your python package. But I also want to mention few features are still in beta though. + +# Pre-requisites + +Before we start, make sure you have the following installed on your system: + +- Python ( I have used python 3.10.9 in this blog ) +- pip ( I have used pip 23.0.1 in this blog ) +- ofcourse a python package to package and publish + +# Setting up your project + +Here is what my project structure looks like: + + . + ├── LICENSE + ├── README.md + ├── pyproject.toml + ├── src + │ └── mypackage + │ ├── __init__.py + │ └── cli.py + └── tests + └── test_mypackage.py + +In __init__.py I have added the following code: + +```python +def hello(): + print("Hello World") +``` + +In cli.py I have added the following code: + +```python +import click +from mypackage import hello + +@click.command() +def main(): + hello() +``` + +in pyproject.toml I have added the following code: + +```toml +[build-system] +requires = ["setuptools>=61.0", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "helloworld-cli" # name of your package +version = "0.0.1" +authors = [ + { name="Your Name", email="someemail@example.com" }, +] +description = "A minimal cli printing hello world" +readme = "README.md" +license = {file = "LICENSE"} +keywords = ["helloworld", "cli", "python"] + +dependencies = [ + "click>=8.0.0", +] + +[project.urls] +Homepage = "https://github.com/YOUR_GITHUB_USERNAME/YOUR_PROJECT_NAME" # +"Bug Tracker" = "https://github.com/YOUR_GITHUB_USERNAME/YOUR_PROJECT_NAME/issues" + +[project.scripts] +helloworld-cli = "mypackage.cli:main" +``` + +Here build-system is used to specify the build backend and the build requirements. In this case I am using setuptools as the build backend and wheel as the build requirement. In project section I have specified the name, version, authors, description, readme, license and keywords of my package. The name of the package should be unique on [pypi](https://pypi.org/). + +The main part of the pyproject.toml is the scripts section. In this section we specify the entry point of our package. In this case I have specified the entry point as `mypackage.cli:main`. This means that the entry point of my package is `main` function in `cli.py` file in `mypackage` module. + +> NOTE: After the package is installed the `main` function will be available as `helloworld-cli` command in the terminal. + +Other files are self explanatory and not necessary for this blog. + +# Packaging and Publishing + +Now that we have our project ready, we can package and publish our package. For this we will use `build` tool and `twine`. `build` tool is used to build the package and `twine` tool is used to publish the package. + +## Installing build and twine + +To install `build` and `twine` run the following command: + +```bash +pip install build twine +``` + +## Building the package + +To build the package run the following command: + +```bash +python -m build +``` + +This will create a `dist` folder in the root of your project. This folder will contain the built package. In my case it is `helloworld_cli-0.0.1-py3-none-any.whl` and `helloworld-cli-0.0.1.tar.gz`. + +### Testing the package locally before publishing + +To test the package locally before publishing it, we can install the package using `pip`. To install the package run the following command: + +```bash +pip install dist/helloworld_cli-0.0.1-py3-none-any.whl +``` + +or + +```bash +pip install dist/helloworld-cli-0.0.1.tar.gz +``` + +Here whl is the wheel package and tar.gz is the source package. You can use either of them to install the package. The reason behind having both the package is that the wheel package is faster to install and the source package is platform independent. + +After installing the package, you can run the following command to test the package: + +```bash +helloworld-cli +``` + +This should print `Hello World!` in the terminal. + +Here is my output + +``` +rukh@ √ helloworld-cli +Hello World! + +rukh@ √ which helloworld-cli +/home/rukh/.local/bin/helloworld-cli + +rukh@ √ cat /home/rukh/.local/bin/helloworld-cli +#!/sbin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from mypackage.cli import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\\.pyw|\\.exe)?$', '', sys.argv[0]) + sys.exit(main()) + +rukh@ √ +``` + +As you can see the package is installed and the `main` function is available as `helloworld-cli` command in the terminal. + +## Publishing the package + +After successfully building the package, we can publish the package. To publish the package we will use `twine`. To publish the package run the following command: + +```bash +twine upload dist/* +``` + +This will ask for your pypi username and password. After entering the username and password, the package will be published. Or alternatively you can setup pypirc config file and use that to publish the package. To setup pypirc config file follow the steps mentioned in [this](https://packaging.python.org/en/latest/specifications/pypirc/) link. My `~/.pypirc` file looks like this: + +```bash +[distutils] +index-servers=pypi +[pypi] +repository = https://upload.pypi.org/legacy/ +username =__token__ +password = pypi-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +``` + +Here token is the token generated from [pypi](https://pypi.org/). To generate the token follow the steps mentioned in [this](https://pypi.org/help/#apitoken) link. I reccommend using token instead of password for security reasons. + +# Conclusion + +In this blog we have seen how to package and publish a python package using `pyproject.toml`. We have also seen how to test the package locally before publishing it. pyproject.toml offers a simpler and more intuitive way to package and publish python projects. It's easy to use and is becoming the standard for python packaging. So if you're looking to package and publish your python projects, pyproject.toml is definitely worth considering. + +""" \ No newline at end of file diff --git a/src/_post/published/sane_apps.toml b/src/_post/published/sane_apps.toml new file mode 100644 index 0000000..abba68f --- /dev/null +++ b/src/_post/published/sane_apps.toml @@ -0,0 +1,97 @@ +[Blog] +title = "Apps that keeps me sane" +date = "2022-01-15T12:30:00+05:45" +image = "img/postbanners/oss.png" +description = "The list of apps and softwares I use." +tags = ["Opensource"] +markdown = """ +I am a huge believer and supporter of opensource community. As it offers the freedom of customization of the software, access to a wide range of resource and support from the community, the opportunity to collaborate with other users, and the chance to contribute to the development of something great. The opensource community also allows users to experiment with their software without the fear of being locked into expensive licensing fees. + +I used opensource tool as much as I can. So here are various day to day opensource softwares and applications I use on my day to day life. + +## On Linux + +--- + +| name | Desc | type | Licence | +| ----------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | +| [Arch os](https://archlinux.org/) | Stopped distro hopping after installing Arch | Operating System | [GNU/GPL](https://en.wikipedia.org/wiki/GNU_General_Public_License) | +| [Foot](https://codeberg.org/dnkl/foot) | Blazing fast terminal | Terminal Emulator | [MIT](https://codeberg.org/dnkl/foot/src/branch/master/LICENSE) | +| [qutebrowser](https://qutebrowser.org/) | qutebrowser is a keyboard-focused browser with a minimal GUI. It’s based on Python and Qt. | Web Browser | [GNU/GPL](https://github.com/qutebrowser/qutebrowser/blob/main/LICENSE) | +| [zsh](https://www.zsh.org/) | Bashlike shell | Shell | [Zsh Licence](https://github.com/zsh-users/zsh/blob/master/LICENCE) | +| [ripcord](https://cancel.fm/ripcord/) | Never using slack app unless i need it. slack is too fancy for me to use, a boomer. | Communication | [Shareware](https://cancel.fm/ripcord/) | +| [rustDesk](https://rustdesk.com/) | Control other system Remotely or let your be controlled. | Remote Control | [AGPL](https://github.com/rustdesk/rustdesk/blob/master/LICENCE) | +| [cht.sh](https://cht.sh) | Cheatsheet | Cheat Sheet | [MIT](https://github.com/chubin/cheat.sh/blob/master/LICENSE) | +| [wayland](https://wayland.freedesktop.org/) | Wayland is a replacement for the X11. | Display Server | [MIT](https://en.wikipedia.org/wiki/MIT_License) | +| [hyprland](https://github.com/hyprwm/Hyprland) | Hyprland is a highly customizable dynamic tiling Wayland compositor that doesn't sacrifice on its looks. | Window Manager | [BDS-3-Clause](https://github.com/hyprwm/Hyprland/blob/main/LICENSE) | +| [SpaceVim](https://spacevim.org/) | SpaceVim is a modular configuration of Vim and Neovim. | Text Editor | [GNU/GPL](https://en.wikipedia.org/wiki/GNU_General_Public_License) | +| [Waybar](https://github.com/Alexays/Waybar) | Highly customizable Wayland bar for Sway and Wlroots based compositors. | Status Bar | [MIT](https://github.com/Alexays/Waybar/blob/master/LICENSE) | +| [dunst](https://github.com/dunst-project/dunst/) | Lightweight and customizable notification daemon. yeah, never going back. | Notification Daemon | [dunstLicence](https://github.com/dunst-project/dunst/blob/master/LICENSE) | +| [Pavucontrol](https://freedesktop.org/software/pulseaudio/pavucontrol/) | PulseAudio Volume Control is a graphical volume control tool for PulseAudio. | Audio Control | [GNU/GPL](https://github.com/pulseaudio/pavucontrol/blob/master/LICENSE) | +| [mpd](https://www.musicpd.org/) | Music Player Daemon. | Music Player Daemon | [GNU/GPL](https://github.com/MusicPlayerDaemon/MPD/blob/master/COPYING) | +| [ncmpcpp](https://rybczak.net/ncmpcpp/) | featureful ncurses based MPD client inspired by ncmpc | MPD Client | [GNU/GPL](https://github.com/ncmpcpp/ncmpcpp/blob/master/COPYING) | +| [mpv](https://mpv.io/) | Mpv is a free, open source, and cross-platform media player. | Media Player | [GNU/GPL](https://github.com/mpv-player/mpv/blob/master/LICENSE.GPL) & [GNU/LGPL](https://github.com/mpv-player/mpv/blob/master/LICENSE.LGPL) | +| [talecast](https://github.com/TBS1996/TaleCast) | Simple CLI podcatcher | Podcast Manager | [MIT](https://github.com/TBS1996/TaleCast/blob/main/LICENSE) | +| [asciinema](https://asciinema.org/) | Record and share your terminal sessions, the right way. | Terminal Recorder | [GNU/GPL](https://github.com/asciinema/asciinema/blob/develop/LICENSE) | +| [authy](https://authy.com/download/) | Syncable, ~~crossplatform~~ 2fa auth app | Authencation | Closed Source | +| [tofi](https://github.com/philj56/tofi) | Tiny dynamic menu for Wayland | Menu | [MIT](https://github.com/philj56/tofi/blob/master/LICENSE) | +| [yt-dlp](https://github.com/yt-dlp/yt-dlp) | A youtube-dl fork with additional features and fixes. | Media Downloader | [Unlicense Licence](https://github.com/yt-dlp/yt-dlp/blob/master/LICENSE) | +| [nerdfont](https://www.nerdfonts.com/) | Iconic font aggregator, collection, and patcher. | Font | [Nerd Font License](https://github.com/ryanoasis/nerd-fonts/blob/master/LICENSE) | +| [networkmanager](https://wiki.archlinux.org/index.php/NetworkManager) | NetworkManager is a set of co-operative tools that make networking simple and straightforward. | Network Manager | [GNU/LGPL](https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/main/COPYING) | +| [paru](https://github.com/Morganamilo/paru) | AUR helper written in rust. | Package Manager | [GNU/GPL](https://github.com/Morganamilo/paru/blob/master/LICENSE) | +| [tmux](https://github.com/tmux/tmux/wiki) | Terminal multiplexer. | Terminal Multiplexer | [Permissive Open Source License](https://github.com/tmux/tmux/blob/master/COPYING) | +| [vscode](https://code.visualstudio.com/) | Code editing. Redefined. | Code Editor | [MIT](https://github.com/microsoft/vscode/blob/main/LICENSE.txt) | +| [wl-clipboard-rs](https://github.com/YaLTeR/wl-clipboard-rs) | A safe Rust crate for working with the Wayland clipboard. | Clipboard Manager | [MIT](https://github.com/YaLTeR/wl-clipboard-rs/blob/master/LICENSE-MIT) | +| [wlr-randr](https://sr.ht/~emersion/wlr-randr/) | Utility to manage outputs of a Wayland compositor. | Display Management | [MIT](https://git.sr.ht/~emersion/wlr-randr/tree/master/item/LICENSE) | +| [waydroid](https://github.com/waydroid/waydroid) | Waydroid uses a container-based approach to boot a full Android system | Android Container | [GNU/GPL](https://github.com/waydroid/waydroid/blob/main/LICENSE) | +| [cloudflared](https://github.com/cloudflare/cloudflared) | Cloudflare Tunnel. | Tunneling Tool | [Apache 2.0](https://github.com/cloudflare/cloudflared/blob/master/LICENSE) | +| [cointop](https://github.com/cointop-sh/cointop) | Fast, Lightweight & interactive terminal based UI application for tracking cryptocurrencies | Crypto Tracker | [Apache 2.0](https://github.com/cointop-sh/cointop/blob/master/LICENSE) | +| [gruvbox](https://github.com/morhetz/gruvbox) | Gruvbox theme for neovim, but that's the colorscheme I also have everywhere else | Color Scheme | [Unlicensed](https://en.wikipedia.org/wiki/Unlicense) | +| [scrcpy](https://github.com/Genymobile/scrcpy) | Android Screen Mirroring. | Screen Mirroring | [Apache 2.0](https://github.com/Genymobile/scrcpy/blob/master/LICENSE) | +| [localsend](https://github.com/localsend/localsend) | An open-source cross-platform alternative to AirDrop | File Transfer | [MIT](https://github.com/localsend/localsend/blob/main/LICENSE) | +| [ly](https://github.com/fairyglade/ly) | display manager with console UI | Display Manager | [WTFPL](https://github.com/fairyglade/ly/blob/master/license.md) | +| [qbittorrent](https://github.com/qbittorrent/qBittorrent) | BitTorrent client. | Torrent Client | [GPLv3](https://github.com/qbittorrent/qBittorrent/blob/master/COPYING.GPLv3) | +| [ranger](https://github.com/ranger/ranger) | A VIM-inspired filemanager for the console | File Manager | [GNU/GPL](https://github.com/ranger/ranger/blob/master/LICENSE) | +| [rdesktop](https://github.com/rdesktop/rdesktop) | rdesktop is an open source client for Microsoft's RDP protocol | Remote Desktop | [GNU/GPL](https://github.com/rdesktop/rdesktop/blob/master/COPYING) | +| [wf-recorder](https://github.com/ammen99/wf-recorder) | utility program for screen recording of wlroots-based compositors | Screen Recorder | [MIT](https://github.com/ammen99/wf-recorder/blob/master/LICENSE) | +| [zathura-pdf-mupdf](https://github.com/pwmt/zathura-pdf-mupdf) | PDF support for zathura (mupdf) | PDF Viewer | [zlib](https://github.com/pwmt/zathura-pdf-mupdf/blob/develop/LICENSE) | +| [plz](https://github.com/rukh-debug/plz-cli/tree/main) | Copilot for your terminal | Copilot | [MIT](https://github.com/rukh-debug/plz-cli/blob/main/LICENSE) | + +## On Android + +| name | Desc | type | +| --------------------------------------------------------------------------------------------- | --------------------------------------------------------------- | ----------------- | +| [Termux](https://f-droid.org/en/packages/com.termux/) | Quickly helps me to ssh into servers | Terminal Emulator | +| [Amaze](https://f-droid.org/en/packages/com.amaze.filemanager/) | File manager for android | File Manager | +| [Material File](https://f-droid.org/en/packages/me.zhanghai.android.files/) | File manager for android | File manager | +| [ReVanced](https://github.com/revanced/revanced-manager) | ReVanced Manager. | Patch Manager | +| [xManager](https://github.com/Team-xManager/xManager) | Spotify Ad-free, New Feature & Freedom | Music Player | +| [Shelter](https://github.com/PeterCxy/Shelter) | Clone app and isolate spaces | Utility | +| [Cloud Stream](https://github.com/recloudstream/cloudstream/releases) | Free Stream movies, anime, TV series, Documentaries and more | Streaming service | +| [Unexpected Keyboard](https://f-droid.org/en/packages/juloo.keyboard2/) | Awesome keyboard for android | Keyboard | +| [Librera Reader](https://f-droid.org/packages/com.foobnix.pro.pdf.reader/) | Awesome pdf, ebook reader for android | Reader | +| [AN2Linux](https://f-droid.org/en/packages/kiwi.root.an2linuxclient/) | Shows notification of your android device to your linux machine | Utility | +| [ArchWiki Viewer](https://f-droid.org/en/packages/com.jtmcn.archwiki.viewer/) | Read Arch wiki anytime anywhere | Reader | +| [Aria2App](https://f-droid.org/packages/com.gianlu.aria2app/) | Slow download speed? Use your full bandwidth with this | Utility | +| [localsend](https://github.com/localsend/localsend/) | An open-source cross-platform alternative to AirDrop | File Transfer | +| [Aurora](https://f-droid.org/en/packages/com.funkymuse.aurora/) | Non-official Library Genesis mobile client. | Downloader | +| [Authy](https://authy.com/download/) | Syncable, ~~crossplatform~~ 2fa auth app | 2fa app | +| [MacroDroid](https://play.google.com/store/apps/details?id=com.arlosoft.macrodroid) | Task automation on adroid device | Utility | +| [Draw](https://f-droid.org/en/packages/com.simplemobiletools.draw.pro/) | Draw whatever you want on your android device | Drawing app | +| [Neo Store](https://f-droid.org/en/packages/com.machiav3lli.fdroid/) | Download apps from f-droid repo | Utility | +| [Signal](https://signal.org/download/) | Signal is a secure, open source, encrypted, communication app | Communication app | +| [Notion](https://www.notion.so/mobile) | Notion is a simple, fast, and free note-taking app | Note-taking app | +| [Aurora Store](https://f-droid.org/en/packages/com.aurora.store/) | An awesome Google Playstore Client | Playstore Client | +| [phyphox](https://f-droid.org/en/packages/de.rwth_aachen.phyphox/) | Perform physics experiments with your phone. | Utility | +| [Easy Xkcd](https://f-droid.org/en/packages/de.tap.easy_xkcd/) | Easy xkcd is an Open Source reader for xkcd | Utility | +| [Notification Dictionary](https://f-droid.org/en/packages/com.xtreak.notificationdictionary/) | An Android app to display meaning of the word as notification. | Utility | +| [Finder](https://f-droid.org/en/packages/ru.seva.finder/) | Remote mobile phone searching via SMS requests. | Utility | +| [Infinity](https://f-droid.org/en/packages/ml.docilealligator.infinityforreddit/) | A beautiful, feature-rich Reddit client. | Reddit Client | +| [vlc](https://f-droid.org/packages/org.videolan.vlc/) | Listen to music and watch video | Media Player | +| [SpotiFlyer](https://f-droid.org/en/packages/com.shabinder.spotiflyer/) | Download All your songs from Spotify and more | Downloader | +| [ServeIt](https://f-droid.org/en/packages/com.example.flutter_http_server/) | ServeIt is a simple http-server on android device. | Utility | +| [DuckDuckGo](https://f-droid.org/en/packages/com.duckduckgo.mobile.android/) | Privacy, simplified | Browser | +| [Paxful](https://play.google.com/store/apps/details?id=com.paxful.wallet) | Trade crypto | Finance | +| [Seal](https://f-droid.org/en/packages/com.junkfood.seal/) | yt-dlp in an app | Utilities | + +""" \ No newline at end of file diff --git a/src/_post/published/spf_dkim.toml b/src/_post/published/spf_dkim.toml new file mode 100644 index 0000000..d26bf4d --- /dev/null +++ b/src/_post/published/spf_dkim.toml @@ -0,0 +1,139 @@ +[Blog] +title = "What is SPF, DKIM and DMARC?" +description = "Let's understand the ABC of email security." +date = "2023-05-03 04:03:03 +0545" +tags = ["EmailSecurity"] +image = "img/postbanners/secure-email.jpg" +markdown = """ +## Intro + +Hey there! Setting up your email service and thinking, "Gee, email authentication sounds like a real snooze fest"? Well, fear not my friend, because I'm here to make it simpler to understand. But before we dive in, let me just say that email authentication is super important for keeping our online communications secure and legit. + +There are three key technologies you need to know about (although there are more ways): SPF, DKIM, and DMARC. Don't worry if those acronyms sound like alphabet soup right now - I'll break it down for you. Basically, these tools work together to verify that the emails you send and receive are authentic and not some phishing scam from a cybercriminal. + +So, let's cut the chit-chat and get started! + +## What the Heck is SPF? + +First things first, SPF stands for Sender Policy Framework. And no, it's not about sunscreen, although it does provide protection against harmful rays of a different kind. Specifically, SPF is a way to prevent email spoofing and phishing. + +Here's how it works: when an email is sent, the recipient's email server checks the "envelope from" address to see if it matches the "from" address in the message header. If they don't match, it could be a sign of spoofing. To prevent this, the "envelope from" address can be checked against a list of authorized senders stored in the sender's DNS record. This list is called an SPF record, and it contains a list of IP addresses and domain names that are allowed to send email on behalf of the domain in question. + +So, let's say you have a domain called example.com, and you want to make sure that only authorized senders can send email from that domain. You would create an SPF record that lists the IP addresses and domain names of your email servers, as well as any third-party services that you use to send email. When an email is received from example.com, the recipient's email server will check the SPF record to make sure that the "envelope from" address matches one of the authorized senders listed in the record. If it doesn't, the email will be flagged as suspicious or rejected altogether. + +One thing to keep in mind is that SPF records are an "all or nothing" proposition. In other words, if you have an SPF record that lists only your email servers, any email sent from an IP address or domain name not listed in the record will be rejected. This can be a problem if you use third-party services to send email on your behalf, as those services may have different IP addresses or domain names than your own servers. To prevent this, you'll need to add those third-party services to your SPF record. + +### SPF Record Breakdown + +Let's take a quick look at an example of an SPF record and a breakdown of its components: + +``` +v=spf1 include:_spf.google.com ~all +``` + +Let's take a closer look at each part of this record: + +- `v=spf1`: This indicates the version of SPF being used. The current version is SPFv1. +- `include:_spf.google.com`: This specifies that all IP addresses and domain names authorized to send email on behalf of the domain should be obtained from the _spf.google.com record. In other words, if Google is authorized to send email on behalf of the domain, the receiving server will check the _spf.google.com record to verify this. +- `~all`: This is the result of the SPF check. The tilde (~) indicates that the result is a "soft fail," which means that the receiving server should accept the email, but mark it as potentially suspicious. The `all` keyword indicates the default result if there are no other mechanisms or modifiers in the record. In this case, it means that any IP address or domain name not listed in the record should be treated as a soft fail. + +Other possible result options include: + +- `-all`: This is a "hard fail," which means that the email should be rejected if the SPF check fails. +- `+all`: This is a "pass," which means that any IP address or domain name should be allowed to send email on behalf of the domain. +- `?all`: This is a "neutral," which means that the receiving server should neither accept nor reject the email based on the SPF check. + +It's important to note that the order of the mechanisms and modifiers in an SPF record is significant. In general, mechanisms are evaluated in the order they appear, and the result is determined by the first matching mechanism. Modifiers are applied to the result of the last mechanism that matched. + +I hope this explanation, example, and breakdown help clarify what SPF is and how to implement it. For more info, check out the references below. + +#### References & More + +- [Cloudflare explains A-Z of SPF](https://www.cloudflare.com/learning/dns/dns-records/dns-spf-record/) + +## DKIM? What's That? + +DKIM stands for DomainKeys Identified Mail. It is a method used to add a digital signature to emails, which helps to ensure their integrity and authenticity. + +Here's how it works in simple terms: + +When you send an email, your email server adds a unique digital signature to the email's header using your domain's private key. This signature can then be verified by the recipient's email server using your domain's public key, which is stored in your domain's DNS records. + +If the signature is valid and the email passes other checks (such as SPF and DMARC), the recipient's email server can be confident that the email was actually sent from your domain and that it hasn't been modified in transit. + +This helps prevent email spoofing and phishing attacks, where someone pretends to be you and sends emails from your domain in order to trick people into giving up sensitive information. + +So, to sum up: DKIM adds a digital signature to emails that helps ensure their authenticity and integrity, preventing email spoofing and phishing attacks. + +### DKIM Breakdown + +Let's say you're the owner of the domain "example.com" and you want to send an email to someone at "example.org". Here's how DKIM might work in this scenario: + +Your email server adds a unique digital signature to the email's header using your domain's private key. The signature might look something like this: + +``` +DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; + d=example.com; s=2022; t=1651565280; + bh=mbO26Ycqy8AvcLqopewVz+NvN0nZ/kXKHVLqlu/7i0A=; + h=From:To:Subject:Date; + b=Lp/YnI+7XZoysbgLd7BhIY1gV7Q2C5ALxV5RL2r5AaJ... +``` + +Let's ignore all the stuff in here for the sake of simplicity except `b=Lp/YnI+7XZoysbgLd7BhIY1gV7Q2C5ALxV5RL2r5AaJ...` This is the actual signature value, which is a digital signature of the headers and body of the email. + +When the email is received by the recipient's email server, the server looks up your domain's public key in your domain's DNS records. The public key might look something like this: + +``` +v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4... +``` + +Here's a breakdown of each component of the public key: + +- `v=DKIM1`: This specifies the version of the DKIM protocol being used. It should match the version specified in the email's DKIM signature. +- `k=rsa`: This specifies the type of public key being used. In this case, an RSA key is being used. +- `p=MIGfMA0GCSqGSIb3DQEBAQUAA4...`: This is the actual public key value, which is used to verify the digital signature on the email. + +The recipient's email server uses the public key to verify the digital signature on the email. It does this by recalculating the hash of the email's headers and body using the same hashing algorithm specified in the DKIM signature (in this case, RSA-SHA256), and then comparing the calculated hash to the hash value included in the DKIM signature (the `bh` value). + +If the recalculated hash matches the hash value in the DKIM signature, and the signature passes other checks like SPF and DMARC, the recipient's email server can be confident that the email was actually sent from your domain and hasn't been modified in transit. + +That's the basic process for how DKIM works to help ensure email authenticity and integrity. By adding a digital signature to your emails using your domain's private key, and then verifying the signature using your domain's public key, you can help prevent email spoofing and phishing attacks. + +#### References & More + +- [What and how of DKIM?](https://www.mimecast.com/content/dkim) +- [Why DKIM and each components explained](https://mailtrap.io/blog/dkim/) + +## Finally, DMARC + +DMARC stands for Domain-based Message Authentication, Reporting and Conformance. Yes, it does sound like a government agency, but it's actually a protocol that helps protect your email from spam and phishing attacks. + +In simple terms, DMARC is a way to bring SPF and DKIM together. It checks whether an email message is actually coming from the domain it claims to be from, and whether it has been tampered with in transit. + +DMARC works by checking the SPF and DKIM records of an email to ensure they match the sending domain. If they don't match, the email may be rejected or marked as suspicious. This helps prevent spammers from spoofing your domain and sending fraudulent emails to your customers. + +DMARC has different policy levels, ranging from "none" to "reject". The "none" policy allows email providers to send reports without actually taking any action. The "quarantine" policy allows email providers to send suspicious emails to the spam folder. And the "reject" policy outright blocks suspicious emails. + +### DMARC Record Breakdown + +Here's an example DMARC record: + +``` +v=DMARC1; p=reject; rua=mailto:dmarc@example.com; fo=1; +``` + +Now let's break down each component: + +- `v=DMARC1`: This is the DMARC version. At the time of writing, DMARC version 1 is the latest and most widely used version. +- `p=reject`: This is the DMARC policy. It tells email receivers what to do if a message fails DMARC checks. In this case, the policy is set to "reject", which means that if a message fails DMARC checks, it should be rejected and not delivered. +- `rua=mailto:dmarc@example.com`: This is the DMARC report URI. It specifies the email address where aggregate reports should be sent. In this case, reports will be sent to "dmarc@example.com" via email. +- `fo=1`: This is the DMARC failure options. It specifies how failed messages should be handled by the receiving email server. In this case, the option is set to "1", which means that the message should be treated as failed if any of the authentication mechanisms (DKIM or SPF) fail. + +There are more components in a DMARC record, which I didn't talk about here just for the sake of simplicity. But you may explore more in detail from the links below. + +#### References & More + +- [DMARC.org](https://dmarc.org/) +- [How to implement DMARC](https://docs.sendgrid.com/ui/sending-email/how-to-implement-dmarc) +- [DMARC rua and ruf](https://dmarcian.com/rua-vs-ruf/) +""" \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..7d42a82 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,12 @@ +#![warn(clippy::all)] +mod pages; +pub use pages::*; + +mod wrap_pages; +pub use wrap_pages::WrapPages; + +#[cfg(target_arch = "wasm32")] +mod web; + +#[cfg(target_arch = "wasm32")] +pub use web::*; \ No newline at end of file diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..16f293c --- /dev/null +++ b/src/main.rs @@ -0,0 +1,46 @@ +#![warn(clippy::all, rust_2018_idioms)] +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release + +// When compiling natively: +#[cfg(not(target_arch = "wasm32"))] +fn main() -> eframe::Result<()> { + env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`). + + let native_options = eframe::NativeOptions { + viewport: egui::ViewportBuilder::default() + .with_inner_size([400.0, 300.0]) + .with_min_inner_size([300.0, 220.0]) + .with_icon( + // NOTE: Adding an icon is optional + eframe::icon_data::from_png_bytes(&include_bytes!("../assets/icon-256.png")[..]) + .expect("Failed to load icon"), + ), + ..Default::default() + }; + eframe::run_native( + "rk_playground", + native_options, + Box::new(|cc| Box::new(rk_playground::WrapPages::new(cc))), + ) +} + +// When compiling to web using trunk: + +#[cfg(target_arch = "wasm32")] +fn main() { + // Redirect `log` message to `console.log` and friends: + eframe::WebLogger::init(log::LevelFilter::Debug).ok(); + + let web_options = eframe::WebOptions::default(); + + wasm_bindgen_futures::spawn_local(async { + eframe::WebRunner::new() + .start( + "rk_playground", // hardcode it + web_options, + Box::new(|cc| Box::new(rk_playground::WrapPages::new(cc))), + ) + .await + .expect("failed to start eframe"); + }); +} diff --git a/src/pages/app.rs b/src/pages/app.rs new file mode 100644 index 0000000..9afa0e6 --- /dev/null +++ b/src/pages/app.rs @@ -0,0 +1,71 @@ +use egui_extras::install_image_loaders; + +/// We derive Deserialize/Serialize so we can persist app state on shutdown. +#[derive(serde::Deserialize, serde::Serialize)] +#[serde(default)] // if we add new fields, give them default values when deserializing old state +pub struct TemplateApp { + // Example stuff: + label: String, + + #[serde(skip)] // This how you opt-out of serialization of a field + value: f32, +} + +impl Default for TemplateApp { + fn default() -> Self { + Self { + // Example stuff: + label: "Hello World!".to_owned(), + value: 2.7, + } + } +} + +impl TemplateApp { + /// Called once before the first frame. + pub fn new(cc: &eframe::CreationContext<'_>) -> Self { + // This is also where you can customize the look and feel of egui using + // `cc.egui_ctx.set_visuals` and `cc.egui_ctx.set_fonts`. + + install_image_loaders(&cc.egui_ctx); + // Load previous app state (if any). + // Note that you must enable the `persistence` feature for this to work. + if let Some(storage) = cc.storage { + return eframe::get_value(storage, eframe::APP_KEY).unwrap_or_default(); + } + Default::default() + } +} + +impl eframe::App for TemplateApp { + /// Called by the frame work to save state before shutdown. + fn save(&mut self, storage: &mut dyn eframe::Storage) { + eframe::set_value(storage, eframe::APP_KEY, self); + } + + /// Called each time the UI needs repainting, which may be many times per second. + fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { + // Put your widgets into a `SidePanel`, `TopBottomPanel`, `CentralPanel`, `Window` or `Area`. + // For inspiration and more examples, go to https://emilk.github.io/egui + + egui::TopBottomPanel::top("top_panel").show(ctx, |ui| { + // The top panel is often a good place for a menu bar: + + egui::menu::bar(ui, |ui| { + // NOTE: no File->Quit on web pages! + let is_web = cfg!(target_arch = "wasm32"); + if !is_web { + ui.menu_button("Home", |ui| { + if ui.button("Quit").clicked() { + ctx.send_viewport_cmd(egui::ViewportCommand::Close); + } + }); + ui.add_space(16.0); + } + egui::widgets::global_dark_light_mode_switch(ui); + // egui::widgets::global_dark_light_mode_buttons(ui); + }); + }); + + } +} diff --git a/src/pages/blog.rs b/src/pages/blog.rs new file mode 100644 index 0000000..d518476 --- /dev/null +++ b/src/pages/blog.rs @@ -0,0 +1,176 @@ +use egui_commonmark::{CommonMarkCache, CommonMarkViewer}; +use include_dir::{include_dir, Dir}; + +#[derive(Debug, serde::Deserialize, serde::Serialize)] +pub struct Blogs { + pub all_blogs: Vec, +} + +#[derive(Debug, serde::Deserialize, serde::Serialize)] +pub struct Blog { + pub title: String, + pub date: String, + pub image: String, + pub description: String, + pub tags: Vec, + pub markdown: String, + pub show_full: bool, +} + +impl Blog { + fn new( + title: String, + date: String, + image: String, + description: String, + tags: Vec, + markdown: String, + ) -> Self { + Blog { + title, + date, + image, + description, + tags, + markdown, + show_full: false, + } + } +} + +impl Default for Blogs { + fn default() -> Self { + // get all the published blog filenames from published.toml + let mut blogs = Blogs { all_blogs: vec![] }; + + // let published_toml_str: &str = std::include_str!("../_post/published.toml"); + + // let published_toml: toml::Value = published_toml_str.parse().unwrap(); + // let published_blogs = published_toml["blogs"].as_array().unwrap(); + + static PROJECT_DIR: Dir = include_dir!("src/_post/published"); + + PROJECT_DIR.files().for_each(|file| { + let file_content = file.contents_utf8().unwrap(); + + let blog_toml: toml::Value = match file_content.parse() { + Ok(value) => value, + Err(err) => { + log::error!("Failed to parse blog TOML: {:?}", err); + return; + } + }; + + let blog_item = blog_toml["Blog"].as_table().unwrap(); + let title = blog_item["title"].as_str().unwrap().to_string(); + let date = blog_item["date"].as_str().unwrap().to_string(); + let image = blog_item["image"].as_str().unwrap().to_string(); + let description = blog_item["description"].as_str().unwrap().to_string(); + let tags = blog_item["tags"] + .as_array() + .unwrap() + .iter() + .map(|tag| tag.as_str().unwrap().to_string()) + .collect(); + let markdown = blog_item["markdown"].as_str().unwrap().to_string(); + + let blog = Blog::new(title, date, image, description, tags, markdown); + blogs.all_blogs.push(blog); + }); + blogs + } +} + +#[derive(serde::Deserialize, serde::Serialize)] +#[serde(default)] +#[derive(Debug)] +pub struct Cache { + #[serde(skip)] + pub markdown_cache: CommonMarkCache, +} + +impl Default for Cache { + fn default() -> Self { + Cache { + markdown_cache: CommonMarkCache::default(), + } + } +} + +#[derive(Debug, serde::Deserialize, serde::Serialize)] +#[serde(default)] +pub struct BlogPage { + pub blogs: Blogs, + pub cache: Cache, +} + +impl Default for BlogPage { + fn default() -> Self { + BlogPage { + blogs: Blogs::default(), + cache: Cache::default(), + } + } +} + +impl BlogPage { + pub fn new(cc: &eframe::CreationContext<'_>) -> Self { + if let Some(storage) = cc.storage { + return eframe::get_value(storage, eframe::APP_KEY).unwrap_or_default(); + } + + Default::default() + } +} + +impl eframe::App for BlogPage { + fn save(&mut self, storage: &mut dyn eframe::Storage) { + eframe::set_value(storage, eframe::APP_KEY, self); + } + + fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { + egui::CentralPanel::default().show(ctx, |ui| { + egui::CollapsingHeader::new("Info").show(ui, |ui| { + ui.heading(format!( + "Blog Count: {}", + &self.blogs.all_blogs.len().to_string() + )); + }); + + // let mut cache = CommonMarkCache::default(); + for blog in &mut self.blogs.all_blogs { + egui::Window::new(blog.title.clone()) + .enabled(true) + .collapsible(true) + .resizable(true) + // .hscroll(true) + .show(ctx, |ui| { + ui.horizontal(|ui| { + ui.weak(blog.date.clone()); + ui.separator(); + ui.weak(blog.tags.join(", ")); + }); + // ui.image(egui::TextureId::from_image_source(egui::ImageSource::from_data(blog.image.clone().into_bytes()))); + ui.label(blog.description.clone()); + + if &blog.show_full == &true { + if ui.button("Collapse").clicked() { + blog.show_full = false; + } + egui::ScrollArea::vertical().show(ui, |ui| { + CommonMarkViewer::new(blog.title.clone()).show( + ui, + &mut self.cache.markdown_cache, + &blog.markdown, + ); + }); + } else { + if ui.button("Read More").clicked() { + blog.show_full = true; + } + } + }); + } + }); + } +} diff --git a/src/pages/mod.rs b/src/pages/mod.rs new file mode 100644 index 0000000..c7aaab4 --- /dev/null +++ b/src/pages/mod.rs @@ -0,0 +1,8 @@ +mod resume; +pub use resume::ResumePage; + +mod blog; +pub use blog::BlogPage; + +mod app; +pub use app::TemplateApp; diff --git a/src/pages/resume.rs b/src/pages/resume.rs new file mode 100644 index 0000000..2f2637d --- /dev/null +++ b/src/pages/resume.rs @@ -0,0 +1,428 @@ +use egui::Ui; +use toml::Value; + +pub use self::skills::Skills; +mod skills; +pub use self::education::Educations; +mod education; +pub use self::recognitions::Recognitions; +mod recognitions; +pub use self::projects::Projects; +mod projects; +pub use self::experience::Experiences; +mod experience; +pub use self::interests::Interests; +mod interests; +pub use self::links::Links; +mod links; + +#[derive(Debug, serde::Deserialize, serde::Serialize)] +struct Resume { + header: Header, + contact: Contact, + sections: Sections, + social: Social, + skills: Skills, + education: Educations, + recognitions: Recognitions, + projects: Projects, + experience: Experiences, + interests: Interests, + links: Links, +} +// ----------------------------------------------------- +#[derive(Debug, serde::Deserialize, serde::Serialize)] +struct Header { + name: String, + display_contact_info: bool, + current_title: String, + intro: String, +} + +// ----------------------------------------------------- + +#[derive(Debug, serde::Deserialize, serde::Serialize)] +struct Contact { + email: String, +} + +// ----------------------------------------------------- +#[derive(Debug, serde::Deserialize, serde::Serialize)] +struct Sections { + experience: bool, + education: bool, + projects: bool, + skills: bool, + recognition: bool, + interests: bool, + links: bool, +} + +// ----------------------------------------------------- +#[derive(Debug, serde::Deserialize, serde::Serialize)] +struct Social { + website: String, + github: String, + linkedin: String, +} + +// ----------------------------------------------------- +impl Default for Resume { + fn default() -> Self { + let resume_toml_str = std::include_str!("../_data/resume.toml"); + let resume_toml_value: Value = toml::from_str(&resume_toml_str).expect("Invalid TOML"); + + let default_header = Header { + name: resume_toml_value["header"]["name"].as_str().unwrap_or("Hello World").to_string(), + display_contact_info: resume_toml_value["header"]["display_contact_info"].as_bool().unwrap_or(true), + current_title: resume_toml_value["header"]["current_title"].as_str().unwrap_or("fullstack dev && devops").to_string(), + intro: resume_toml_value["header"]["intro"].as_str().unwrap_or("i am a developer and devops guy actively developing webs and also a learner, passively learning various nerdy stuff.").to_string(), + }; + + let default_contact = Contact { + email: resume_toml_value["contact"]["email"] + .as_str() + .unwrap_or("resume@rubenk.dev") + .to_string(), + }; + + let default_sections = Sections { + experience: resume_toml_value["sections"]["experience"] + .as_bool() + .unwrap_or(true), + education: resume_toml_value["sections"]["education"] + .as_bool() + .unwrap_or(true), + projects: resume_toml_value["sections"]["projects"] + .as_bool() + .unwrap_or(true), + skills: resume_toml_value["sections"]["skills"] + .as_bool() + .unwrap_or(true), + recognition: resume_toml_value["sections"]["recognition"] + .as_bool() + .unwrap_or(true), + interests: resume_toml_value["sections"]["interests"] + .as_bool() + .unwrap_or(true), + links: resume_toml_value["sections"]["links"] + .as_bool() + .unwrap_or(true), + }; + + let default_social = Social { + website: resume_toml_value["social"]["website"] + .as_str() + .unwrap_or("http://rubenk.dev") + .to_string(), + github: resume_toml_value["social"]["github"] + .as_str() + .unwrap_or("https://github.com/rukh-debug") + .to_string(), + linkedin: resume_toml_value["social"]["linkedin"] + .as_str() + .unwrap_or("https://linkedin.com/in/rubenkharel") + .to_string(), + }; + + Resume { + header: default_header, + contact: default_contact, + sections: default_sections, + social: default_social, + skills: Skills::default(), + education: Educations::default(), + recognitions: Recognitions::default(), + projects: Projects::default(), + experience: Experiences::default(), + interests: Interests::default(), + links: Links::default(), + } + } +} + +// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +/// We derive Deserialize/Serialize so we can persist app state on shutdown. +#[derive(serde::Deserialize, serde::Serialize)] +#[serde(default)] // if we add new fields, give them default values when deserializing old state +pub struct ResumePage { + resume: Resume, +} + +impl Default for ResumePage { + fn default() -> Self { + ResumePage { + resume: Resume::default(), + } + } +} + +impl ResumePage { + pub fn new(cc: &eframe::CreationContext<'_>) -> Self { + if let Some(storage) = cc.storage { + return eframe::get_value(storage, eframe::APP_KEY).unwrap_or_default(); + } + + Default::default() + } +} + +impl eframe::App for ResumePage { + fn save(&mut self, storage: &mut dyn eframe::Storage) { + eframe::set_value(storage, eframe::APP_KEY, self); + } + + fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { + egui::CentralPanel::default().show(ctx, |_ui| { + // insert everything inside drag and drop + + egui::Window::new(format!( + "{} | {}", + self.resume.header.name, self.resume.header.current_title + )) + .default_open(true) + .enabled(true) + .show(ctx, |ui| { + // render ui for resume here + ui.label(format!("{}", self.resume.header.intro)); + ui.hyperlink_to("Contact", format!("mailto:{}", self.resume.contact.email)); + }); + + if self.resume.sections.experience { + egui::Window::new("Experience") + .enabled(true) + .default_open(false) + .show(ctx, |ui| { + for experience in &self.resume.experience.experiences { + ui.monospace(experience.company.clone()); + ui.horizontal(|ui| { + ui.label(experience.summary.clone()); + ui.separator(); + ui.label(experience.url.clone()); + }); + ui.spacing(); + for role in &experience.roles { + ui.monospace(role.title.clone()); + ui.horizontal(|ui| { + ui.label(role.start_date.clone()); + ui.separator(); + ui.label(role.end_date.clone()); + }); + ui.spacing(); + ui.label(role.summary.clone()); + for highlight in &role.highlights { + ui.label(highlight); + } + // if this is the last iteration dont show the separator + if !std::ptr::eq(role, experience.roles.last().unwrap()) { + ui.separator(); + } + } + // if this is the last iteration dont show the separator + if !std::ptr::eq( + experience, + self.resume.experience.experiences.last().unwrap(), + ) { + ui.separator(); + } + } + }); + } + + if self.resume.sections.education { + egui::Window::new("Education") + .enabled(true) + .default_open(false) + .show(ctx, |ui| { + for education in &self.resume.education.educations { + ui.monospace(format!("{}", education.degree)); + ui.horizontal(|ui| { + ui.label(format!("{}", education.uni)); + ui.separator(); + ui.label(format!("{}", education.year)); + }); + ui.spacing(); + ui.label(format!("{}", education.summary)); + // if this is the last iteration dont show the separator + if !std::ptr::eq( + education, + self.resume.education.educations.last().unwrap(), + ) { + ui.separator(); + } + } + }); + } + + if self.resume.sections.projects { + egui::Window::new("Projects") + .default_open(false) + .enabled(true) + .show(ctx, |ui| { + for project in &self.resume.projects.projects { + ui.monospace(format!("{}", project.name)); + ui.horizontal(|ui| { + ui.label(format!("{}", project.role)); + ui.separator(); + ui.label(format!("{}", project.duration)); + }); + ui.spacing(); + ui.label(format!("{}", project.description)); + // if this is the last iteration dont show the separator + if !std::ptr::eq(project, self.resume.projects.projects.last().unwrap()) + { + ui.separator(); + } + } + }); + } + + if self.resume.sections.skills { + egui::Window::new("Skills") + .default_open(false) + .enabled(true) + .show(ctx, |ui| { + ui.monospace(format!("{}", self.resume.skills.name)); + ui.label(format!("{}", self.resume.skills.description)); + ui.separator(); + + ui.horizontal(|ui| { + ui.monospace("Languages"); + ui.separator(); + for language in &self.resume.skills.tools_tech.languages { + ui.label(language); + } + }); + ui.horizontal(|ui| { + ui.monospace("Databases"); + ui.separator(); + for database in &self.resume.skills.tools_tech.databases { + ui.label(database); + } + }); + ui.horizontal(|ui| { + ui.monospace("Cloud Services"); + ui.separator(); + for cloud_service in &self.resume.skills.tools_tech.cloud_services { + ui.label(cloud_service); + } + }); + ui.horizontal(|ui| { + ui.monospace("Containers"); + ui.separator(); + for container in &self.resume.skills.tools_tech.containers { + ui.label(container); + } + }); + ui.horizontal(|ui| { + ui.monospace("Version Control"); + ui.separator(); + for version_control in &self.resume.skills.tools_tech.version_control { + ui.label(version_control); + } + }); + ui.horizontal(|ui| { + ui.monospace("API Testing"); + ui.separator(); + for api_testing in &self.resume.skills.tools_tech.api_testing { + ui.label(api_testing); + } + }); + ui.horizontal(|ui| { + ui.monospace("Scripting"); + ui.separator(); + for scripting in &self.resume.skills.tools_tech.scripting { + ui.label(scripting); + } + }); + ui.horizontal(|ui| { + ui.monospace("Editors"); + ui.separator(); + for editor in &self.resume.skills.tools_tech.editors { + ui.label(editor); + } + }); + ui.horizontal(|ui| { + ui.monospace("OS"); + ui.separator(); + for os in &self.resume.skills.tools_tech.os { + ui.label(os); + } + }); + ui.horizontal(|ui| { + ui.monospace("Tools"); + ui.separator(); + for tool in &self.resume.skills.tools_tech.tools { + ui.label(tool); + } + }); + ui.horizontal(|ui| { + ui.monospace("Security Tools"); + ui.separator(); + for security_tool in &self.resume.skills.tools_tech.security_tools { + ui.label(security_tool); + } + }); + }); + } + + if self.resume.sections.recognition { + egui::Window::new("Recognition") + .enabled(true) + .default_open(false) + .show(ctx, |ui| { + for recognition in &self.resume.recognitions.recognitions { + ui.monospace(format!("{}", recognition.name)); + ui.horizontal(|ui| { + ui.label(format!("{}", recognition.organization)); + ui.separator(); + ui.label(format!("{}", recognition.year)); + }); + ui.spacing(); + ui.label(format!("{}", recognition.summary)); + // if this is the last iteration dont show the separator + if !std::ptr::eq( + recognition, + self.resume.recognitions.recognitions.last().unwrap(), + ) { + ui.separator(); + } + } + }); + } + + if self.resume.sections.interests { + egui::Window::new("Interests") + .enabled(true) + .default_open(false) + .show(ctx, |ui| { + for interest in &self.resume.interests.interests { + ui.label(format!("{}", interest.description)); + // if this is the last iteration dont show the separator + if !std::ptr::eq( + interest, + self.resume.interests.interests.last().unwrap(), + ) { + ui.separator(); + } + } + }); + } + + if self.resume.sections.links { + egui::Window::new("Links") + .default_open(false) + .enabled(true) + .show(ctx, |ui| { + for link in &self.resume.links.links { + ui.hyperlink_to(link.name.clone(), link.url.clone()); + // if this is the last iteration dont show the separator + if !std::ptr::eq(link, self.resume.links.links.last().unwrap()) { + ui.separator(); + } + } + }); + } + }); + } +} diff --git a/src/pages/resume/education.rs b/src/pages/resume/education.rs new file mode 100644 index 0000000..a892394 --- /dev/null +++ b/src/pages/resume/education.rs @@ -0,0 +1,48 @@ +#[derive(Debug, serde::Deserialize, serde::Serialize)] + +pub struct Educations { + pub educations: Vec, +} + +#[derive(Debug, serde::Deserialize, serde::Serialize)] + +pub struct Education { + pub degree: String, + pub uni: String, + pub year: String, + pub summary: String, +} + +impl Education { + fn new(degree: String, uni: String, year: String, summary: String) -> Self { + Education { + degree, + uni, + year, + summary, + } + } +} + +impl Default for Educations { + fn default() -> Self { + let educations_toml_str: &str = std::include_str!("../../_data/education.toml"); + let educations_toml: toml::Value = educations_toml_str.parse().unwrap(); + + let educations = educations_toml["education"] + .as_array() + .unwrap() + .iter() + .map(|x| { + let degree = x.get("degree").and_then(|s| s.as_str()).unwrap_or("").to_string(); + let uni = x.get("uni").and_then(|s| s.as_str()).unwrap_or("").to_string(); + let year = x.get("year").and_then(|s| s.as_str()).unwrap_or("").to_string(); + let summary = x.get("summary").and_then(|s| s.as_str()).unwrap_or("").to_string(); + + Education::new(degree, uni, year, summary) + }) + .collect(); + + Educations { educations } + } +} \ No newline at end of file diff --git a/src/pages/resume/experience.rs b/src/pages/resume/experience.rs new file mode 100644 index 0000000..05d235a --- /dev/null +++ b/src/pages/resume/experience.rs @@ -0,0 +1,125 @@ +#[derive(Debug, serde::Deserialize, serde::Serialize)] + +pub struct Experiences { + pub experiences: Vec, +} + +#[derive(Debug, serde::Deserialize, serde::Serialize)] +pub struct Role { + pub title: String, + pub start_date: String, + pub end_date: String, + pub summary: String, + pub highlights: Vec, +} + +#[derive(Debug, serde::Deserialize, serde::Serialize)] +pub struct Experience { + pub company: String, + pub summary: String, + pub logo: String, + pub url: String, + pub roles: Vec, +} + +impl Role { + fn new( + title: String, + start_date: String, + end_date: String, + summary: String, + highlights: Vec, + ) -> Self { + Role { + title, + start_date, + end_date, + summary, + highlights, + } + } +} + +impl Default for Experiences { + fn default() -> Self { + let experiences_toml_str: &str = std::include_str!("../../_data/experience.toml"); + let experiences_toml: toml::Value = experiences_toml_str.parse().unwrap(); + + let experiences = experiences_toml["experience"] + .as_array() + .unwrap() + .iter() + .map(|x| { + let company = x + .get("company") + .and_then(|s| s.as_str()) + .unwrap_or("") + .to_string(); + let summary = x + .get("summary") + .and_then(|s| s.as_str()) + .unwrap_or("") + .to_string(); + let logo = x + .get("logo") + .and_then(|s| s.as_str()) + .unwrap_or("") + .to_string(); + let url = x + .get("url") + .and_then(|s| s.as_str()) + .unwrap_or("") + .to_string(); + + let roles = x + .get("positions") + .and_then(|s| s.as_array()) + .unwrap() + .iter() + .map(|y| { + let title = y + .get("title") + .and_then(|s| s.as_str()) + .unwrap_or("") + .to_string(); + let start_date = y + .get("startdate") + .and_then(|s| s.as_str()) + .unwrap_or("") + .to_string(); + let end_date = y + .get("enddate") + .and_then(|s| s.as_str()) + .unwrap_or("") + .to_string(); + let summary = y + .get("summary") + .and_then(|s| s.as_str()) + .unwrap_or("") + .to_string(); + let highlights = y + .get("projects") + .and_then(|s| s.as_array()) + .unwrap_or(&vec![]) + .iter() + .map(|z| z.as_str().unwrap().to_string()) + .collect(); + + Role::new(title, start_date, end_date, summary, highlights) + }) + .collect(); + + Experience { + company, + summary, + logo, + url, + roles, + } + }) + .collect(); + + Experiences { experiences } + } +} + diff --git a/src/pages/resume/interests.rs b/src/pages/resume/interests.rs new file mode 100644 index 0000000..1eb690c --- /dev/null +++ b/src/pages/resume/interests.rs @@ -0,0 +1,34 @@ +#[derive(Debug, serde::Deserialize, serde::Serialize)] +pub struct Interests { + pub interests: Vec, +} + +#[derive(Debug, serde::Deserialize, serde::Serialize)] +pub struct Interest { + pub description: String, +} + +impl Interest { + fn new(description: String) -> Self { + Interest { + description, + } + } +} + +impl Default for Interests { + fn default() -> Self { + let interests_toml_str: &str = std::include_str!("../../_data/interests.toml"); + let interests_toml: toml::Value = interests_toml_str.parse().unwrap(); + + let interest = interests_toml["interest"].as_table().unwrap(); + let interests = interest["description"] + .as_array() + .unwrap() + .iter() + .map(|x| Interest::new(x.as_str().unwrap().to_string())) + .collect::>(); + + Interests { interests: interests } + } +} \ No newline at end of file diff --git a/src/pages/resume/links.rs b/src/pages/resume/links.rs new file mode 100644 index 0000000..d012c27 --- /dev/null +++ b/src/pages/resume/links.rs @@ -0,0 +1,44 @@ +#[derive(Debug, serde::Deserialize, serde::Serialize)] + +pub struct Links { + pub links: Vec, +} + +#[derive(Debug, serde::Deserialize, serde::Serialize)] +pub struct Link { + pub name: String, + pub url: String, + pub label : String, +} + +impl Link { + fn new(name: String, url: String, label: String) -> Self { + Link { + name, + url, + label, + } + } +} + +impl Default for Links { + fn default() -> Self { + let links_toml_str: &str = std::include_str!("../../_data/links.toml"); + let links_toml: toml::Value = links_toml_str.parse().unwrap(); + + let links = links_toml["link"] + .as_array() + .unwrap() + .iter() + .map(|x| { + let name = x.get("name").and_then(|s| s.as_str()).unwrap_or("").to_string(); + let url = x.get("url").and_then(|s| s.as_str()).unwrap_or("").to_string(); + let label = x.get("label").and_then(|s| s.as_str()).unwrap_or("").to_string(); + + Link::new(name, url, label) + }) + .collect(); + + Links { links: links } + } +} \ No newline at end of file diff --git a/src/pages/resume/projects.rs b/src/pages/resume/projects.rs new file mode 100644 index 0000000..846bd89 --- /dev/null +++ b/src/pages/resume/projects.rs @@ -0,0 +1,70 @@ +#[derive(Debug, serde::Deserialize, serde::Serialize)] +pub struct Projects { + pub projects: Vec, +} + +#[derive(Debug, serde::Deserialize, serde::Serialize)] +pub struct Project { + pub name: String, + pub role: String, + pub duration: String, + pub url: String, + pub description: String, +} + +impl Project { + fn new(name: String, role: String, duration: String, url: String, description: String) -> Self { + Project { + name, + role, + duration, + url, + description, + } + } +} + +impl Default for Projects { + fn default() -> Self { + let projects_toml_str: &str = std::include_str!("../../_data/projects.toml"); + let projects_toml: toml::Value = projects_toml_str.parse().unwrap(); + + let projects = projects_toml["project"] + .as_array() + .unwrap() + .iter() + .map(|x| { + let name = x + .get("name") + .and_then(|s| s.as_str()) + .unwrap_or("") + .to_string(); + let role = x + .get("role") + .and_then(|s| s.as_str()) + .unwrap_or("") + .to_string(); + let duration = x + .get("duration") + .and_then(|s| s.as_str()) + .unwrap_or("") + .to_string(); + let url = x + .get("url") + .and_then(|s| s.as_str()) + .unwrap_or("") + .to_string(); + let description = x + .get("description") + .and_then(|s| s.as_str()) + .unwrap_or("") + .to_string(); + + Project::new(name, role, duration, url, description) + }) + .collect(); + + Projects { projects } + } +} + diff --git a/src/pages/resume/recognitions.rs b/src/pages/resume/recognitions.rs new file mode 100644 index 0000000..249dd68 --- /dev/null +++ b/src/pages/resume/recognitions.rs @@ -0,0 +1,63 @@ +#[derive(Debug, serde::Deserialize, serde::Serialize)] +pub struct Recognitions { + pub recognitions: Vec, +} + +#[derive(Debug, serde::Deserialize, serde::Serialize)] +pub struct Recognition { + pub name: String, + pub organization: String, + pub year: String, + pub summary: String, +} + +impl Recognition { + fn new(name: String, organization: String, year: String, summary: String) -> Self { + Recognition { + name, + organization, + year, + summary, + } + } +} + +impl Default for Recognitions { + fn default() -> Self { + let recognitions_toml_str: &str = std::include_str!("../../_data/recognitions.toml"); + let recognitions_toml: toml::Value = recognitions_toml_str.parse().unwrap(); + + let recognitions = recognitions_toml["recognition"] + .as_array() + .unwrap() + .iter() + .map(|x| { + let name = x + .get("name") + .and_then(|s| s.as_str()) + .unwrap_or("") + .to_string(); + let organization = x + .get("organization") + .and_then(|s| s.as_str()) + .unwrap_or("") + .to_string(); + let year = x + .get("year") + .and_then(|s| s.as_str()) + .unwrap_or("") + .to_string(); + let summary = x + .get("summary") + .and_then(|s| s.as_str()) + .unwrap_or("") + .to_string(); + + Recognition::new(name, organization, year, summary) + }) + .collect(); + + Recognitions { recognitions } + } +} + diff --git a/src/pages/resume/skills.rs b/src/pages/resume/skills.rs new file mode 100644 index 0000000..821e815 --- /dev/null +++ b/src/pages/resume/skills.rs @@ -0,0 +1,118 @@ +#[derive(Debug, serde::Deserialize, serde::Serialize)] +pub struct Skills { + pub name: String, + pub description: String, + pub tools_tech: ToolsTech, +} + +#[derive(Debug, serde::Deserialize, serde::Serialize)] +pub struct ToolsTech { + pub languages: Vec, + pub databases: Vec, + pub cloud_services: Vec, + pub containers: Vec, + pub version_control: Vec, + pub api_testing: Vec, + pub scripting: Vec, + pub editors: Vec, + pub os: Vec, + pub tools: Vec, + pub security_tools: Vec, +} + +impl Default for Skills { + fn default() -> Self { + let skills_toml_str: &str = std::include_str!("../../_data/skills.toml"); + let skills_toml: toml::Value = skills_toml_str.parse().unwrap(); + + let skill = skills_toml["skill"].as_table().unwrap(); + let name = skill["name"].as_str().unwrap().to_string(); + let description = skill["description"].as_str().unwrap().to_string(); + + let tools_tech = skills_toml["skill"]["ToolsTech"].as_table().unwrap(); + let languages = tools_tech["languages"] + .as_array() + .unwrap() + .iter() + .map(|x| x.as_str().unwrap().to_string()) + .collect(); + let databases = tools_tech["databases"] + .as_array() + .unwrap() + .iter() + .map(|x| x.as_str().unwrap().to_string()) + .collect(); + let cloud_services = tools_tech["cloud_services"] + .as_array() + .unwrap() + .iter() + .map(|x| x.as_str().unwrap().to_string()) + .collect(); + let containers = tools_tech["containers"] + .as_array() + .unwrap() + .iter() + .map(|x| x.as_str().unwrap().to_string()) + .collect(); + let version_control = tools_tech["version_control"] + .as_array() + .unwrap() + .iter() + .map(|x| x.as_str().unwrap().to_string()) + .collect(); + let api_testing = tools_tech["api_testing"] + .as_array() + .unwrap() + .iter() + .map(|x| x.as_str().unwrap().to_string()) + .collect(); + let scripting = tools_tech["scripting"] + .as_array() + .unwrap() + .iter() + .map(|x| x.as_str().unwrap().to_string()) + .collect(); + let editors = tools_tech["editors"] + .as_array() + .unwrap() + .iter() + .map(|x| x.as_str().unwrap().to_string()) + .collect(); + let os = tools_tech["os"] + .as_array() + .unwrap() + .iter() + .map(|x| x.as_str().unwrap().to_string()) + .collect(); + let tools = tools_tech["tools"] + .as_array() + .unwrap() + .iter() + .map(|x| x.as_str().unwrap().to_string()) + .collect(); + let security_tools = tools_tech["security_tools"] + .as_array() + .unwrap() + .iter() + .map(|x| x.as_str().unwrap().to_string()) + .collect(); + + Self { + name, + description, + tools_tech: ToolsTech { + languages, + databases, + cloud_services, + containers, + version_control, + api_testing, + scripting, + editors, + os, + tools, + security_tools, + }, + } + } +} diff --git a/src/web.rs b/src/web.rs new file mode 100644 index 0000000..3a87063 --- /dev/null +++ b/src/web.rs @@ -0,0 +1,68 @@ +#![allow(clippy::mem_forget)] // False positives from #[wasm_bindgen] macro +use eframe::wasm_bindgen::{self, prelude::*}; + +use crate::WrapPages as WrapApp; + +/// Our handle to the web app from JavaScript. +#[derive(Clone)] +#[wasm_bindgen] +pub struct WebHandle { + runner: eframe::WebRunner, +} + +#[wasm_bindgen] +impl WebHandle { + /// Installs a panic hook, then returns. + #[allow(clippy::new_without_default)] + #[wasm_bindgen(constructor)] + pub fn new() -> Self { + // Redirect [`log`] message to `console.log` and friends: + eframe::WebLogger::init(log::LevelFilter::Debug).ok(); + + Self { + runner: eframe::WebRunner::new(), + } + } + + /// Call this once from JavaScript to start your app. + #[wasm_bindgen] + pub async fn start(&self, canvas_id: &str) -> Result<(), wasm_bindgen::JsValue> { + self.runner + .start( + canvas_id, + eframe::WebOptions::default(), + Box::new(|cc| Box::new(WrapApp::new(cc))), + ) + .await + } + + #[wasm_bindgen] + pub fn destroy(&self) { + self.runner.destroy(); + } + + /// Example on how to call into your app from JavaScript. + // #[wasm_bindgen] + // pub fn example(&self) { + // if let Some(_app) = self.runner.app_mut::() { + // // _app.example(); + // } + // } + + /// The JavaScript can check whether or not your app has crashed: + #[wasm_bindgen] + pub fn has_panicked(&self) -> bool { + self.runner.has_panicked() + } + + #[wasm_bindgen] + pub fn panic_message(&self) -> Option { + self.runner.panic_summary().map(|s| s.message()) + } + + #[wasm_bindgen] + pub fn panic_callstack(&self) -> Option { + self.runner.panic_summary().map(|s| s.callstack()) + } +} + diff --git a/src/wrap_pages.rs b/src/wrap_pages.rs new file mode 100644 index 0000000..e126ab8 --- /dev/null +++ b/src/wrap_pages.rs @@ -0,0 +1,162 @@ +use std::fmt; + +#[derive(Default)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "serde", serde(default))] +pub struct State { + resume_page: crate::pages::ResumePage, + blog_page: crate::pages::BlogPage, + + selected_anchor: Anchor, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +enum Anchor { + Blog, + Resume, + // Clear, +} + +#[derive(Clone, Copy, Debug)] +#[must_use] +enum Command { + Nothing, +} + +impl Anchor { + #[cfg(target_arch = "wasm32")] + fn all() -> Vec { + vec![Self::Blog, Self::Resume] + } +} + +impl fmt::Display for Anchor { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self) + } +} + +impl From for egui::WidgetText { + fn from(value: Anchor) -> Self { + Self::RichText(egui::RichText::new(value.to_string())) + } +} + +impl Default for Anchor { + fn default() -> Self { + Self::Blog + } +} + +pub struct WrapPages { + state: State, +} + +impl WrapPages { + pub fn new(_cc: &eframe::CreationContext<'_>) -> Self { + #[allow(unused_mut)] + let mut slf = Self { + state: State::default(), + }; + + #[cfg(feature = "persistence")] + if let Some(storage) = cc.storage { + if let Some(state) = eframe::get_value(storage, eframe::APP_KEY) { + slf.state = state; + } + } + slf + } + + fn apps_iter_mut(&mut self) -> impl Iterator { + let vec = vec![ + ( + "Blog", + Anchor::Blog, + &mut self.state.blog_page as &mut dyn eframe::App, + ), + ( + "Resume", + Anchor::Resume, + &mut self.state.resume_page as &mut dyn eframe::App, + ), + ]; + + vec.into_iter() + } + + fn show_selected_app(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) { + let selected_anchor = self.state.selected_anchor; + for (_name, anchor, app) in self.apps_iter_mut() { + if anchor == selected_anchor || ctx.memory(|mem| mem.everything_is_visible()) { + app.update(ctx, frame); + } + } + } + + fn bar_contents(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame, _cmd: &mut Command) { + egui::widgets::global_dark_light_mode_switch(ui); + + ui.separator(); + + let mut selected_anchor = self.state.selected_anchor; + for (name, anchor, _app) in self.apps_iter_mut() { + if ui + .selectable_label(selected_anchor == anchor, name) + .clicked() + { + selected_anchor = anchor; + if frame.is_web() { + ui.ctx() + .open_url(egui::OpenUrl::same_tab(format!("#{anchor}"))); + } + } + } + self.state.selected_anchor = selected_anchor; + } +} + +impl eframe::App for WrapPages { + fn save(&mut self, storage: &mut dyn eframe::Storage) { + eframe::set_value(storage, eframe::APP_KEY, &self.state.blog_page); + eframe::set_value(storage, eframe::APP_KEY, &self.state.resume_page); + } + + fn clear_color(&self, visuals: &egui::Visuals) -> [f32; 4] { + // Giving the area behind the floating windows a different color, because it looks better: + let color = egui::lerp( + egui::Rgba::from(visuals.panel_fill)..=egui::Rgba::from(visuals.extreme_bg_color), + 0.5, + ); + let color = egui::Color32::from(color); + color.to_normalized_gamma_f32() + } + + fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) { + #[cfg(target_arch = "wasm32")] + if let Some(anchor) = frame.info().web_info.location.hash.strip_prefix('#') { + let anchor = Anchor::all().into_iter().find(|x| x.to_string() == anchor); + if let Some(v) = anchor { + self.state.selected_anchor = v; + } + } + + #[cfg(not(target_arch = "wasm32"))] + if ctx.input_mut(|i| i.consume_key(egui::Modifiers::NONE, egui::Key::F11)) { + let fullscreen = ctx.input(|i| i.viewport().fullscreen.unwrap_or(false)); + ctx.send_viewport_cmd(egui::ViewportCommand::Fullscreen(!fullscreen)); + } + + let mut cmd = Command::Nothing; + egui::TopBottomPanel::top("wrap_app_top_bar").show(ctx, |ui| { + ui.horizontal_wrapped(|ui| { + ui.visuals_mut().button_frame = false; + self.bar_contents(ui, frame, &mut cmd); + }); + }); + + + self.show_selected_app(ctx, frame); + } +} \ No newline at end of file