refactor progress

This commit is contained in:
Hampus Kraft
2026-02-17 12:22:36 +00:00
parent cb31608523
commit d5abd1a7e4
8257 changed files with 1190207 additions and 761040 deletions

View File

@@ -2,12 +2,174 @@
# It is not intended for manual editing.
version = 4
[[package]]
name = "adler2"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "aligned"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee4508988c62edf04abd8d92897fca0c2995d907ce1dfeaf369dac3716a40685"
dependencies = [
"as-slice",
]
[[package]]
name = "aligned-vec"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b"
dependencies = [
"equator",
]
[[package]]
name = "anyhow"
version = "1.0.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
[[package]]
name = "arbitrary"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
[[package]]
name = "arg_enum_proc_macro"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "arrayvec"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
[[package]]
name = "as-slice"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516"
dependencies = [
"stable_deref_trait",
]
[[package]]
name = "autocfg"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "av-scenechange"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f321d77c20e19b92c39e7471cf986812cbb46659d2af674adc4331ef3f18394"
dependencies = [
"aligned",
"anyhow",
"arg_enum_proc_macro",
"arrayvec",
"log",
"num-rational",
"num-traits",
"pastey",
"rayon",
"thiserror",
"v_frame",
"y4m",
]
[[package]]
name = "av1-grain"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8cfddb07216410377231960af4fcab838eaa12e013417781b78bd95ee22077f8"
dependencies = [
"anyhow",
"arrayvec",
"log",
"nom",
"num-rational",
"v_frame",
]
[[package]]
name = "avif-serialize"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47c8fbc0f831f4519fe8b810b6a7a91410ec83031b8233f730a0480029f6a23f"
dependencies = [
"arrayvec",
]
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
[[package]]
name = "bitstream-io"
version = "4.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60d4bd9d1db2c6bdf285e223a7fa369d5ce98ec767dec949c6ca62863ce61757"
dependencies = [
"core2",
]
[[package]]
name = "built"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4ad8f11f288f48ca24471bbd51ac257aaeaaa07adae295591266b792902ae64"
[[package]]
name = "bumpalo"
version = "3.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
[[package]]
name = "bytemuck"
version = "1.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4"
[[package]]
name = "byteorder-lite"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
[[package]]
name = "cc"
version = "1.2.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd4932aefd12402b36c60956a4fe0035421f544799057659ff86f923657aada3"
dependencies = [
"find-msvc-tools",
"jobserver",
"libc",
"shlex",
]
[[package]]
name = "cfg-if"
version = "1.0.4"
@@ -20,6 +182,112 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
[[package]]
name = "core2"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505"
dependencies = [
"memchr",
]
[[package]]
name = "crc32fast"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
dependencies = [
"cfg-if",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
dependencies = [
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "either"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]]
name = "equator"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc"
dependencies = [
"equator-macro",
]
[[package]]
name = "equator-macro"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "fdeflate"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c"
dependencies = [
"simd-adler32",
]
[[package]]
name = "find-msvc-tools"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f449e6c6c08c865631d4890cfacf252b3d396c9bcc83adb6623cdb02a8336c41"
[[package]]
name = "flate2"
version = "1.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b375d6465b98090a5f25b1c7703f3859783755aa9a80433b36e0379a3ec2f369"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]]
name = "getrandom"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
dependencies = [
"cfg-if",
"libc",
"r-efi",
"wasip2",
]
[[package]]
name = "gif"
version = "0.13.3"
@@ -30,21 +298,272 @@ dependencies = [
"weezl",
]
[[package]]
name = "image"
version = "0.25.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6506c6c10786659413faa717ceebcb8f70731c0a60cbae39795fdf114519c1a"
dependencies = [
"bytemuck",
"byteorder-lite",
"image-webp",
"moxcms",
"num-traits",
"png 0.18.0",
"ravif",
"rgb",
"zune-core",
"zune-jpeg",
]
[[package]]
name = "image-webp"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3"
dependencies = [
"byteorder-lite",
"quick-error",
]
[[package]]
name = "imgref"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7c5cedc30da3a610cac6b4ba17597bdf7152cf974e8aab3afb3d54455e371c8"
[[package]]
name = "interpolate_name"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "itertools"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
dependencies = [
"either",
]
[[package]]
name = "jobserver"
version = "0.1.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
dependencies = [
"getrandom",
"libc",
]
[[package]]
name = "libc"
version = "0.2.180"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc"
[[package]]
name = "libfluxcore"
version = "0.1.0"
dependencies = [
"gif",
"image",
"png 0.17.16",
"ruzstd",
"serde",
"wasm-bindgen",
]
[[package]]
name = "libfuzzer-sys"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5037190e1f70cbeef565bd267599242926f724d3b8a9f510fd7e0b540cfa4404"
dependencies = [
"arbitrary",
"cc",
]
[[package]]
name = "log"
version = "0.4.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
[[package]]
name = "loop9"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062"
dependencies = [
"imgref",
]
[[package]]
name = "maybe-rayon"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519"
dependencies = [
"cfg-if",
]
[[package]]
name = "memchr"
version = "2.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
[[package]]
name = "miniz_oxide"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
dependencies = [
"adler2",
"simd-adler32",
]
[[package]]
name = "moxcms"
version = "0.7.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac9557c559cd6fc9867e122e20d2cbefc9ca29d80d027a8e39310920ed2f0a97"
dependencies = [
"num-traits",
"pxfm",
]
[[package]]
name = "new_debug_unreachable"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
[[package]]
name = "nom"
version = "8.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405"
dependencies = [
"memchr",
]
[[package]]
name = "noop_proc_macro"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8"
[[package]]
name = "num-bigint"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
dependencies = [
"num-integer",
"num-traits",
]
[[package]]
name = "num-derive"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "num-integer"
version = "0.1.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
dependencies = [
"num-traits",
]
[[package]]
name = "num-rational"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
dependencies = [
"num-bigint",
"num-integer",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "paste"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "pastey"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec"
[[package]]
name = "png"
version = "0.17.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526"
dependencies = [
"bitflags 1.3.2",
"crc32fast",
"fdeflate",
"flate2",
"miniz_oxide",
]
[[package]]
name = "png"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97baced388464909d42d89643fe4361939af9b7ce7a31ee32a168f832a70f2a0"
dependencies = [
"bitflags 2.10.0",
"crc32fast",
"fdeflate",
"flate2",
"miniz_oxide",
]
[[package]]
name = "ppv-lite86"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
dependencies = [
"zerocopy",
]
[[package]]
name = "proc-macro2"
version = "1.0.103"
@@ -54,6 +573,40 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "profiling"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773"
dependencies = [
"profiling-procmacros",
]
[[package]]
name = "profiling-procmacros"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b"
dependencies = [
"quote",
"syn",
]
[[package]]
name = "pxfm"
version = "0.1.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7186d3822593aa4393561d186d1393b3923e9d6163d3fbfd6e825e3e6cf3e6a8"
dependencies = [
"num-traits",
]
[[package]]
name = "quick-error"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
[[package]]
name = "quote"
version = "1.0.42"
@@ -63,6 +616,116 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "r-efi"
version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
name = "rand"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
dependencies = [
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
dependencies = [
"getrandom",
]
[[package]]
name = "rav1e"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43b6dd56e85d9483277cde964fd1bdb0428de4fec5ebba7540995639a21cb32b"
dependencies = [
"aligned-vec",
"arbitrary",
"arg_enum_proc_macro",
"arrayvec",
"av-scenechange",
"av1-grain",
"bitstream-io",
"built",
"cfg-if",
"interpolate_name",
"itertools",
"libc",
"libfuzzer-sys",
"log",
"maybe-rayon",
"new_debug_unreachable",
"noop_proc_macro",
"num-derive",
"num-traits",
"paste",
"profiling",
"rand",
"rand_chacha",
"simd_helpers",
"thiserror",
"v_frame",
"wasm-bindgen",
]
[[package]]
name = "ravif"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef69c1990ceef18a116855938e74793a5f7496ee907562bd0857b6ac734ab285"
dependencies = [
"avif-serialize",
"imgref",
"loop9",
"quick-error",
"rav1e",
"rgb",
]
[[package]]
name = "rayon"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f"
dependencies = [
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
dependencies = [
"crossbeam-deque",
"crossbeam-utils",
]
[[package]]
name = "rgb"
version = "0.8.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce"
[[package]]
name = "rustversion"
version = "1.0.22"
@@ -75,6 +738,63 @@ version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fad02996bfc73da3e301efe90b1837be9ed8f4a462b6ed410aa35d00381de89f"
[[package]]
name = "serde"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
"serde_derive",
]
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "simd-adler32"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2"
[[package]]
name = "simd_helpers"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6"
dependencies = [
"quote",
]
[[package]]
name = "stable_deref_trait"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
[[package]]
name = "syn"
version = "2.0.111"
@@ -86,12 +806,52 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "2.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "unicode-ident"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
[[package]]
name = "v_frame"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "666b7727c8875d6ab5db9533418d7c764233ac9c0cff1d469aec8fa127597be2"
dependencies = [
"aligned-vec",
"num-traits",
"wasm-bindgen",
]
[[package]]
name = "wasip2"
version = "1.0.1+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
dependencies = [
"wit-bindgen",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.106"
@@ -142,3 +902,50 @@ name = "weezl"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88"
[[package]]
name = "wit-bindgen"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
[[package]]
name = "y4m"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5a4b21e1a62b67a2970e6831bc091d7b87e119e7f9791aef9702e3bef04448"
[[package]]
name = "zerocopy"
version = "0.8.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "zune-core"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "111f7d9820f05fd715df3144e254d6fc02ee4088b0644c0ffd0efc9e6d9d2773"
[[package]]
name = "zune-jpeg"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e35aee689668bf9bd6f6f3a6c60bb29ba1244b3b43adfd50edd554a371da37d5"
dependencies = [
"zune-core",
]

View File

@@ -1,12 +1,15 @@
[package]
name = "libfluxcore"
version = "0.1.0"
edition = "2021"
edition = "2024"
[lib]
crate-type = ["cdylib"]
[dependencies]
gif = "0.13"
image = { version = "0.25.9", default-features = false, features = ["jpeg", "png", "webp", "avif"] }
png = "0.17"
ruzstd = { version = "0.7", default-features = false, features = ["std"] }
wasm-bindgen = { version = "0.2", features = ["std"] }
serde = { version = "1.0", features = ["derive"] }

View File

@@ -0,0 +1,159 @@
/*
* Copyright (C) 2026 Fluxer Contributors
*
* This file is part of Fluxer.
*
* Fluxer is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Fluxer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
*/
use gif::{ColorOutput, DecodeOptions};
use std::io::Cursor;
use wasm_bindgen::prelude::*;
const PNG_SIGNATURE: [u8; 8] = [137, 80, 78, 71, 13, 10, 26, 10];
enum ImageFormat {
Gif,
Png,
Webp,
Avif,
Unknown,
}
fn detect_format(input: &[u8]) -> ImageFormat {
if input.len() >= 6 && &input[..6] == b"GIF89a" {
return ImageFormat::Gif;
}
if input.len() >= 6 && &input[..6] == b"GIF87a" {
return ImageFormat::Gif;
}
if input.len() >= PNG_SIGNATURE.len() && input[..PNG_SIGNATURE.len()] == PNG_SIGNATURE {
return ImageFormat::Png;
}
if input.len() >= 12 && &input[..4] == b"RIFF" && &input[8..12] == b"WEBP" {
return ImageFormat::Webp;
}
if is_avif_file(input) {
return ImageFormat::Avif;
}
ImageFormat::Unknown
}
fn is_animated_gif(input: &[u8]) -> bool {
let mut options = DecodeOptions::new();
options.set_color_output(ColorOutput::RGBA);
let cursor = Cursor::new(input);
let mut reader = match options.read_info(cursor) {
Ok(reader) => reader,
Err(_) => return false,
};
let mut frame_count = 0;
loop {
match reader.read_next_frame() {
Ok(Some(_frame)) => {
frame_count += 1;
if frame_count > 1 {
return true;
}
}
Ok(None) => break,
Err(_) => return false,
}
}
false
}
fn has_apng_actl(input: &[u8]) -> bool {
if input.len() < PNG_SIGNATURE.len() || input[..PNG_SIGNATURE.len()] != PNG_SIGNATURE {
return false;
}
let mut offset = PNG_SIGNATURE.len();
while offset + 12 <= input.len() {
let length_bytes = &input[offset..offset + 4];
let length = u32::from_be_bytes(length_bytes.try_into().unwrap()) as usize;
let chunk_type = &input[offset + 4..offset + 8];
if chunk_type == b"acTL" {
return true;
}
offset = offset
.saturating_add(8)
.saturating_add(length)
.saturating_add(4);
}
false
}
fn has_webp_anim(input: &[u8]) -> bool {
if input.len() < 12 || &input[..4] != b"RIFF" || &input[8..12] != b"WEBP" {
return false;
}
let mut offset = 12;
while offset + 8 <= input.len() {
let chunk_id = &input[offset..offset + 4];
let size_bytes = &input[offset + 4..offset + 8];
let size = u32::from_le_bytes(size_bytes.try_into().unwrap()) as usize;
if chunk_id == b"ANIM" {
return true;
}
let advance = 8 + size + (size % 2);
offset = offset.saturating_add(advance);
}
false
}
fn is_avif_file(input: &[u8]) -> bool {
if input.len() < 12 {
return false;
}
let box_type = &input[4..8];
if box_type != b"ftyp" {
return false;
}
let brand = &input[8..12];
brand == b"avif" || brand == b"avis"
}
fn has_avif_anim(input: &[u8]) -> bool {
if !is_avif_file(input) {
return false;
}
if input.len() < 12 {
return false;
}
let brand = &input[8..12];
brand == b"avis"
}
#[wasm_bindgen]
pub fn is_animated_image(input: &[u8]) -> bool {
match detect_format(input) {
ImageFormat::Gif => is_animated_gif(input),
ImageFormat::Png => has_apng_actl(input),
ImageFormat::Webp => has_webp_anim(input),
ImageFormat::Avif => has_avif_anim(input),
ImageFormat::Unknown => false,
}
}

View File

@@ -0,0 +1,411 @@
/*
* Copyright (C) 2026 Fluxer Contributors
*
* This file is part of Fluxer.
*
* Fluxer is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Fluxer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
*/
use png::{BlendOp, DisposeOp};
use std::io::Cursor;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
#[allow(clippy::too_many_arguments)]
pub fn crop_and_rotate_apng(
input: &[u8],
x: u32,
y: u32,
width: u32,
height: u32,
rotation_deg: u32,
resize_width: Option<u32>,
resize_height: Option<u32>,
) -> Result<Box<[u8]>, JsValue> {
process_apng(
input,
x,
y,
width,
height,
rotation_deg,
resize_width,
resize_height,
)
}
#[allow(clippy::too_many_arguments)]
fn process_apng(
input: &[u8],
x: u32,
y: u32,
width: u32,
height: u32,
rotation_deg: u32,
resize_width: Option<u32>,
resize_height: Option<u32>,
) -> Result<Box<[u8]>, JsValue> {
let cursor = Cursor::new(input);
let mut decoder = png::Decoder::new(cursor);
decoder.set_transformations(png::Transformations::EXPAND | png::Transformations::STRIP_16);
let mut reader = decoder
.read_info()
.map_err(|e| JsValue::from_str(&format!("png read_info: {e}")))?;
let info = reader.info();
let animation_control = info
.animation_control()
.ok_or_else(|| JsValue::from_str("Not an animated PNG"))?;
let screen_width = info.width;
let screen_height = info.height;
let crop_x = x.min(screen_width);
let crop_y = y.min(screen_height);
let crop_w = width.min(screen_width - crop_x);
let crop_h = height.min(screen_height - crop_y);
if crop_w == 0 || crop_h == 0 {
return Err(JsValue::from_str("Crop area is empty"));
}
let rotation = rotation_deg.rem_euclid(360);
let (base_w, base_h) = match rotation {
90 | 270 => (crop_h, crop_w),
_ => (crop_w, crop_h),
};
let (target_w, target_h) = match (
resize_width.filter(|w| *w > 0),
resize_height.filter(|h| *h > 0),
) {
(Some(w), Some(h)) => (w, h),
_ => (base_w, base_h),
};
if target_w == 0 || target_h == 0 {
return Err(JsValue::from_str("Target dimensions are empty"));
}
if crop_x == 0
&& crop_y == 0
&& crop_w == screen_width
&& crop_h == screen_height
&& rotation == 0
&& target_w == screen_width
&& target_h == screen_height
{
return Ok(input.to_vec().into_boxed_slice());
}
let mut output = Cursor::new(Vec::new());
let mut encoder = png::Encoder::new(&mut output, target_w, target_h);
encoder.set_color(png::ColorType::Rgba);
encoder.set_depth(png::BitDepth::Eight);
encoder
.set_animated(animation_control.num_frames, animation_control.num_plays)
.map_err(|e| JsValue::from_str(&format!("png set_animated: {e}")))?;
encoder.validate_sequence(true);
let mut writer = encoder
.write_header()
.map_err(|e| JsValue::from_str(&format!("png write_header: {e}")))?;
let mut canvas = vec![0u8; (screen_width * screen_height * 4) as usize];
let mut previous_canvas: Option<Vec<u8>> = None;
let mut processed_any = false;
const MAX_TOTAL_PIXELS: u64 = 200_000_000;
let mut processed_pixels: u64 = 0;
let mut frame_buffer = vec![0u8; (screen_width * screen_height * 4) as usize];
while let Ok(frame_info) = reader.next_frame_info() {
processed_any = true;
let dispose_op = frame_info.dispose_op;
let blend_op = frame_info.blend_op;
let delay_num = frame_info.delay_num;
let delay_den = frame_info.delay_den;
let fx = frame_info.x_offset as usize;
let fy = frame_info.y_offset as usize;
let fw = frame_info.width as usize;
let fh = frame_info.height as usize;
let rect_x = frame_info.x_offset;
let rect_y = frame_info.y_offset;
let rect_w = frame_info.width;
let rect_h = frame_info.height;
if dispose_op == DisposeOp::Previous {
previous_canvas = Some(canvas.clone());
}
reader
.next_frame(&mut frame_buffer)
.map_err(|e| JsValue::from_str(&format!("png next_frame: {e}")))?;
if blend_op == BlendOp::Source {
draw_frame_on_canvas_source(&mut canvas, screen_width, fx, fy, fw, fh, &frame_buffer);
} else {
draw_frame_on_canvas_over(&mut canvas, screen_width, fx, fy, fw, fh, &frame_buffer);
}
let (cw, ch) = (crop_w as usize, crop_h as usize);
let cropped = crop_rgba(
&canvas,
screen_width as usize,
screen_height as usize,
crop_x as usize,
crop_y as usize,
cw,
ch,
)?;
let (rotated, rw, rh) = match rotation {
90 => rotate_rgba_90(&cropped, cw, ch),
180 => rotate_rgba_180(&cropped, cw, ch),
270 => rotate_rgba_270(&cropped, cw, ch),
_ => (cropped, cw, ch),
};
let (final_rgba, _fw, _fh) = if target_w as usize != rw || target_h as usize != rh {
let resized =
resize_rgba_nearest(&rotated, rw, rh, target_w as usize, target_h as usize);
(resized, target_w as usize, target_h as usize)
} else {
(rotated, rw, rh)
};
processed_pixels += (final_rgba.len() / 4) as u64;
if processed_pixels > MAX_TOTAL_PIXELS {
return Err(JsValue::from_str(
"Animated PNG is too large to crop. Try reducing its dimensions or number of frames.",
));
}
writer
.set_frame_delay(delay_num, delay_den)
.map_err(|e| JsValue::from_str(&format!("png set_frame_delay: {e}")))?;
writer
.set_dispose_op(dispose_op)
.map_err(|e| JsValue::from_str(&format!("png set_dispose_op: {e}")))?;
writer
.set_blend_op(blend_op)
.map_err(|e| JsValue::from_str(&format!("png set_blend_op: {e}")))?;
writer
.write_image_data(&final_rgba)
.map_err(|e| JsValue::from_str(&format!("png write_image_data: {e}")))?;
match dispose_op {
DisposeOp::Background => {
clear_rect(&mut canvas, screen_width, rect_x, rect_y, rect_w, rect_h);
}
DisposeOp::Previous => {
if let Some(prev) = previous_canvas.take() {
canvas = prev;
}
}
_ => {}
}
}
if !processed_any {
return Err(JsValue::from_str("APNG has no frames"));
}
writer
.finish()
.map_err(|e| JsValue::from_str(&format!("png finish: {e}")))?;
Ok(output.into_inner().into_boxed_slice())
}
fn draw_frame_on_canvas_source(
canvas: &mut [u8],
canvas_width: u32,
fx: usize,
fy: usize,
fw: usize,
fh: usize,
buffer: &[u8],
) {
let cw = canvas_width as usize;
for row in 0..fh {
let canvas_y = fy + row;
let canvas_offset = (canvas_y * cw + fx) * 4;
let frame_offset = row * fw * 4;
if canvas_offset + fw * 4 <= canvas.len() && frame_offset + fw * 4 <= buffer.len() {
canvas[canvas_offset..canvas_offset + fw * 4]
.copy_from_slice(&buffer[frame_offset..frame_offset + fw * 4]);
}
}
}
fn draw_frame_on_canvas_over(
canvas: &mut [u8],
canvas_width: u32,
fx: usize,
fy: usize,
fw: usize,
fh: usize,
buffer: &[u8],
) {
let cw = canvas_width as usize;
for row in 0..fh {
let canvas_y = fy + row;
let canvas_offset = (canvas_y * cw + fx) * 4;
let frame_offset = row * fw * 4;
if canvas_offset + fw * 4 <= canvas.len() && frame_offset + fw * 4 <= buffer.len() {
let frame_row = &buffer[frame_offset..frame_offset + fw * 4];
let canvas_row = &mut canvas[canvas_offset..canvas_offset + fw * 4];
for i in 0..fw {
let pixel_idx = i * 4;
let alpha = frame_row[pixel_idx + 3];
if alpha > 0 {
canvas_row[pixel_idx] = frame_row[pixel_idx];
canvas_row[pixel_idx + 1] = frame_row[pixel_idx + 1];
canvas_row[pixel_idx + 2] = frame_row[pixel_idx + 2];
canvas_row[pixel_idx + 3] = frame_row[pixel_idx + 3];
}
}
}
}
}
fn clear_rect(canvas: &mut [u8], canvas_width: u32, x: u32, y: u32, w: u32, h: u32) {
let cw = canvas_width as usize;
let x = x as usize;
let y = y as usize;
let w = w as usize;
let h = h as usize;
for row in 0..h {
let canvas_y = y + row;
let offset = (canvas_y * cw + x) * 4;
if offset + w * 4 <= canvas.len() {
for i in 0..w {
let idx = offset + i * 4;
canvas[idx] = 0;
canvas[idx + 1] = 0;
canvas[idx + 2] = 0;
canvas[idx + 3] = 0;
}
}
}
}
fn crop_rgba(
src: &[u8],
src_w: usize,
src_h: usize,
x: usize,
y: usize,
w: usize,
h: usize,
) -> Result<Vec<u8>, JsValue> {
if x + w > src_w || y + h > src_h {
return Err(JsValue::from_str("Crop rect out of bounds"));
}
let mut dst = vec![0u8; w * h * 4];
for row in 0..h {
let src_y = y + row;
let src_offset = (src_y * src_w + x) * 4;
let dst_offset = row * w * 4;
dst[dst_offset..dst_offset + w * 4].copy_from_slice(&src[src_offset..src_offset + w * 4]);
}
Ok(dst)
}
fn rotate_rgba_90(src: &[u8], src_w: usize, src_h: usize) -> (Vec<u8>, usize, usize) {
let dst_w = src_h;
let dst_h = src_w;
let mut dst = vec![0u8; dst_w * dst_h * 4];
for y in 0..src_h {
for x in 0..src_w {
let src_idx = (y * src_w + x) * 4;
let dst_x = src_h - 1 - y;
let dst_y = x;
let dst_idx = (dst_y * dst_w + dst_x) * 4;
dst[dst_idx..dst_idx + 4].copy_from_slice(&src[src_idx..src_idx + 4]);
}
}
(dst, dst_w, dst_h)
}
fn rotate_rgba_180(src: &[u8], src_w: usize, src_h: usize) -> (Vec<u8>, usize, usize) {
let mut dst = vec![0u8; src.len()];
for y in 0..src_h {
for x in 0..src_w {
let src_idx = (y * src_w + x) * 4;
let dst_x = src_w - 1 - x;
let dst_y = src_h - 1 - y;
let dst_idx = (dst_y * src_w + dst_x) * 4;
dst[dst_idx..dst_idx + 4].copy_from_slice(&src[src_idx..src_idx + 4]);
}
}
(dst, src_w, src_h)
}
fn rotate_rgba_270(src: &[u8], src_w: usize, src_h: usize) -> (Vec<u8>, usize, usize) {
let dst_w = src_h;
let dst_h = src_w;
let mut dst = vec![0u8; dst_w * dst_h * 4];
for y in 0..src_h {
for x in 0..src_w {
let src_idx = (y * src_w + x) * 4;
let dst_x = y;
let dst_y = dst_h - 1 - x;
let dst_idx = (dst_y * dst_w + dst_x) * 4;
dst[dst_idx..dst_idx + 4].copy_from_slice(&src[src_idx..src_idx + 4]);
}
}
(dst, dst_w, dst_h)
}
fn resize_rgba_nearest(
src: &[u8],
src_w: usize,
src_h: usize,
dst_w: usize,
dst_h: usize,
) -> Vec<u8> {
let mut dst = vec![0u8; dst_w * dst_h * 4];
for dy in 0..dst_h {
let sy = dy * src_h / dst_h;
for dx in 0..dst_w {
let sx = dx * src_w / dst_w;
let src_idx = (sy * src_w + sx) * 4;
let dst_idx = (dy * dst_w + dx) * 4;
dst[dst_idx..dst_idx + 4].copy_from_slice(&src[src_idx..src_idx + 4]);
}
}
dst
}

View File

@@ -35,6 +35,7 @@ impl From<JsValue> for EncodeError {
}
#[wasm_bindgen]
#[allow(clippy::too_many_arguments)]
pub fn crop_and_rotate_gif(
input: &[u8],
x: u32,
@@ -83,6 +84,7 @@ enum EncoderMode {
Quantized,
}
#[allow(clippy::too_many_arguments)]
fn process_gif(
input: &[u8],
x: u32,
@@ -451,13 +453,15 @@ impl PaletteFrameEncoder {
transparent_index,
} = PaletteFrameData::from_rgba(&rgba)?;
let mut frame = Frame::default();
frame.width = self.width;
frame.height = self.height;
frame.delay = delay;
frame.buffer = Cow::Owned(indices);
frame.palette = Some(palette);
frame.transparent = transparent_index;
let frame = Frame {
width: self.width,
height: self.height,
delay,
buffer: Cow::Owned(indices),
palette: Some(palette),
transparent: transparent_index,
..Frame::default()
};
self.encoder.write_frame(&frame).map_err(map_encoding_error)
}

View File

@@ -17,8 +17,14 @@
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
*/
pub mod gif;
pub mod animation;
pub mod apng;
pub mod gateway;
pub mod gif;
pub mod static_image;
pub use gif::crop_and_rotate_gif;
pub use animation::is_animated_image;
pub use apng::crop_and_rotate_apng;
pub use gateway::decompress_zstd_frame;
pub use gif::crop_and_rotate_gif;
pub use static_image::crop_and_rotate_image;

View File

@@ -0,0 +1,98 @@
/*
* Copyright (C) 2026 Fluxer Contributors
*
* This file is part of Fluxer.
*
* Fluxer is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Fluxer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
*/
use image::imageops::FilterType;
use image::{DynamicImage, GenericImageView, ImageFormat, RgbaImage, imageops};
use std::io::Cursor;
use wasm_bindgen::prelude::*;
fn normalize_rotation(rotation_deg: u32) -> u32 {
rotation_deg % 360
}
fn map_format_hint(hint: &str) -> Result<ImageFormat, JsValue> {
match hint.trim().to_lowercase().as_str() {
"png" | "apng" => Ok(ImageFormat::Png),
"jpeg" | "jpg" => Ok(ImageFormat::Jpeg),
"webp" => Ok(ImageFormat::WebP),
"avif" => Ok(ImageFormat::Avif),
"gif" => Ok(ImageFormat::Gif),
_ => Err(JsValue::from_str("Unsupported static format")),
}
}
#[wasm_bindgen]
#[allow(clippy::too_many_arguments)]
pub fn crop_and_rotate_image(
input: &[u8],
format_hint: &str,
x: u32,
y: u32,
width: u32,
height: u32,
rotation_deg: u32,
resize_width: Option<u32>,
resize_height: Option<u32>,
) -> Result<Box<[u8]>, JsValue> {
let format = map_format_hint(format_hint)?;
let dynamic_image = image::load_from_memory_with_format(input, format)
.map_err(|err| JsValue::from_str(&format!("Failed to decode {format_hint}: {err}")))?;
let (img_w, img_h) = dynamic_image.dimensions();
let crop_x = x.min(img_w);
let crop_y = y.min(img_h);
let crop_w = width.min(img_w.saturating_sub(crop_x));
let crop_h = height.min(img_h.saturating_sub(crop_y));
if crop_w == 0 || crop_h == 0 {
return Err(JsValue::from_str("Crop area is empty"));
}
let cropped: RgbaImage = dynamic_image
.crop_imm(crop_x, crop_y, crop_w, crop_h)
.to_rgba8();
let rotated = match normalize_rotation(rotation_deg) {
90 => imageops::rotate90(&cropped),
180 => imageops::rotate180(&cropped),
270 => imageops::rotate270(&cropped),
_ => cropped.clone(),
};
let target_w = resize_width.filter(|w| *w > 0).unwrap_or(rotated.width());
let target_h = resize_height.filter(|h| *h > 0).unwrap_or(rotated.height());
if target_w == 0 || target_h == 0 {
return Err(JsValue::from_str("Target dimensions are empty"));
}
let final_buffer = if target_w == rotated.width() && target_h == rotated.height() {
rotated
} else {
imageops::resize(&rotated, target_w, target_h, FilterType::Lanczos3)
};
let final_frame = DynamicImage::ImageRgba8(final_buffer);
let mut output = Cursor::new(Vec::new());
final_frame
.write_to(&mut output, format)
.map_err(|err| JsValue::from_str(&format!("Failed to encode {format_hint}: {err}")))?;
Ok(output.into_inner().into_boxed_slice())
}

View File

@@ -1,38 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- Required for Electron on arm64 -->
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
<!-- Microphone: need BOTH audio-input (hardened runtime) AND microphone (sandbox) -->
<key>com.apple.security.device.audio-input</key>
<true/>
<key>com.apple.security.device.microphone</key>
<true/>
<!-- Camera -->
<key>com.apple.security.device.camera</key>
<true/>
<!-- Network -->
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
<!-- Passkeys: application identifier and associated domains (restricted entitlements) -->
<key>com.apple.application-identifier</key>
<string>3G5837T29K.app.fluxer.canary</string>
<key>com.apple.developer.associated-domains</key>
<array>
<string>webcredentials:web.canary.fluxer.app</string>
</array>
</dict>
</plist>

View File

@@ -1,32 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- Required for Electron on arm64 -->
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
<!-- Microphone: need BOTH audio-input (hardened runtime) AND microphone (sandbox) -->
<key>com.apple.security.device.audio-input</key>
<true/>
<key>com.apple.security.device.microphone</key>
<true/>
<!-- Camera -->
<key>com.apple.security.device.camera</key>
<true/>
<!-- Network -->
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
<!-- NOTE: No restricted entitlements (associated-domains, application-identifier) here.
This file is for helper apps/frameworks which don't need passkey capabilities. -->
</dict>
</plist>

View File

@@ -1,39 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- Required for Electron on arm64 -->
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
<!-- Microphone: need BOTH audio-input (hardened runtime) AND microphone (sandbox) -->
<key>com.apple.security.device.audio-input</key>
<true/>
<key>com.apple.security.device.microphone</key>
<true/>
<!-- Camera -->
<key>com.apple.security.device.camera</key>
<true/>
<!-- Network -->
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
<!-- Passkeys: application identifier and associated domains (restricted entitlements) -->
<key>com.apple.application-identifier</key>
<string>3G5837T29K.app.fluxer</string>
<key>com.apple.developer.associated-domains</key>
<array>
<string>webcredentials:fluxer.app</string>
<string>webcredentials:web.fluxer.app</string>
</array>
</dict>
</plist>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 557 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 865 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -1,3 +0,0 @@
<svg width="633" height="512" viewBox="0 0 633 512" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M46.3631 210.286C19.4771 210.286 -2.98429 188.554 0.324514 161.87C3.90111 133.034 11.3051 108.486 22.5371 88.228C39.9071 57.448 62.4611 35.048 90.1931 21.028C118.231 7.01002 147.793 0 178.879 0C209.965 0 238.155 7.01002 263.451 21.028C289.051 34.742 318.765 55.772 352.593 84.114C375.451 103.314 393.431 117.028 406.537 125.258C419.945 133.18 434.879 137.142 451.337 137.142C477.851 137.142 498.727 127.696 513.965 108.8C521.231 99.79 526.661 88.77 530.255 75.74C537.627 49.008 558.305 25.6 586.033 25.6C612.817 25.6 635.309 47.078 632.223 73.684C628.861 102.664 621.441 127.322 609.965 147.658C592.593 178.438 569.889 200.838 541.851 214.858C513.813 228.876 484.251 235.886 453.165 235.886C422.079 235.886 393.737 229.334 368.137 216.228C342.537 202.82 312.975 181.334 279.451 151.772C257.507 132.572 239.679 119.01 225.965 111.086C212.251 102.858 197.165 98.742 180.707 98.742C156.021 98.742 135.603 107.276 119.451 124.342C111.043 133.226 104.947 145.246 101.165 160.404C94.4911 187.138 73.9191 210.286 46.3631 210.286ZM46.3631 486.4C19.4771 486.4 -2.98429 464.668 0.324514 437.986C3.90111 409.148 11.3051 384.6 22.5371 364.342C39.9071 333.562 62.4611 311.162 90.1931 297.142C118.231 283.124 147.793 276.114 178.879 276.114C209.965 276.114 238.155 283.124 263.451 297.142C289.051 310.858 318.765 331.886 352.593 360.228C375.451 379.428 393.431 393.142 406.537 401.372C419.945 409.296 434.879 413.258 451.337 413.258C477.851 413.258 498.727 403.81 513.965 384.914C521.231 375.904 526.661 364.884 530.255 351.854C537.627 325.122 558.305 301.714 586.033 301.714C612.817 301.714 635.309 323.194 632.223 349.798C628.861 378.778 621.441 403.436 609.965 423.772C592.593 454.552 569.889 476.952 541.851 490.972C513.813 504.99 484.251 512 453.165 512C422.079 512 393.737 505.448 368.137 492.342C342.537 478.934 312.975 457.448 279.451 427.886C257.507 408.686 239.679 395.124 225.965 387.2C212.251 378.972 197.165 374.858 180.707 374.858C156.021 374.858 135.603 383.39 119.451 400.458C111.043 409.34 104.947 421.36 101.165 436.518C94.4911 463.252 73.9191 486.4 46.3631 486.4Z" fill="white"/>
</svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -1,27 +0,0 @@
{
"fill": {
"solid": "display-p3:0.95215,0.66553,0.23462,1.00000"
},
"groups": [
{
"layers": [
{
"image-name": "Vector.svg",
"name": "Vector"
}
],
"shadow": {
"kind": "neutral",
"opacity": 0.5
},
"translucency": {
"enabled": true,
"value": 0.5
}
}
],
"supported-platforms": {
"circles": ["watchOS"],
"squares": "shared"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 521 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 701 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 883 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 805 B

View File

@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict/>
</plist>

View File

@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleIconFile</key>
<string>AppIcon</string>
<key>CFBundleIconName</key>
<string>AppIcon</string>
</dict>
</plist>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 676 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -1,3 +0,0 @@
<svg width="633" height="512" viewBox="0 0 633 512" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M46.3631 210.286C19.4771 210.286 -2.98429 188.554 0.324514 161.87C3.90111 133.034 11.3051 108.486 22.5371 88.228C39.9071 57.448 62.4611 35.048 90.1931 21.028C118.231 7.01002 147.793 0 178.879 0C209.965 0 238.155 7.01002 263.451 21.028C289.051 34.742 318.765 55.772 352.593 84.114C375.451 103.314 393.431 117.028 406.537 125.258C419.945 133.18 434.879 137.142 451.337 137.142C477.851 137.142 498.727 127.696 513.965 108.8C521.231 99.79 526.661 88.77 530.255 75.74C537.627 49.008 558.305 25.6 586.033 25.6C612.817 25.6 635.309 47.078 632.223 73.684C628.861 102.664 621.441 127.322 609.965 147.658C592.593 178.438 569.889 200.838 541.851 214.858C513.813 228.876 484.251 235.886 453.165 235.886C422.079 235.886 393.737 229.334 368.137 216.228C342.537 202.82 312.975 181.334 279.451 151.772C257.507 132.572 239.679 119.01 225.965 111.086C212.251 102.858 197.165 98.742 180.707 98.742C156.021 98.742 135.603 107.276 119.451 124.342C111.043 133.226 104.947 145.246 101.165 160.404C94.4911 187.138 73.9191 210.286 46.3631 210.286ZM46.3631 486.4C19.4771 486.4 -2.98429 464.668 0.324514 437.986C3.90111 409.148 11.3051 384.6 22.5371 364.342C39.9071 333.562 62.4611 311.162 90.1931 297.142C118.231 283.124 147.793 276.114 178.879 276.114C209.965 276.114 238.155 283.124 263.451 297.142C289.051 310.858 318.765 331.886 352.593 360.228C375.451 379.428 393.431 393.142 406.537 401.372C419.945 409.296 434.879 413.258 451.337 413.258C477.851 413.258 498.727 403.81 513.965 384.914C521.231 375.904 526.661 364.884 530.255 351.854C537.627 325.122 558.305 301.714 586.033 301.714C612.817 301.714 635.309 323.194 632.223 349.798C628.861 378.778 621.441 403.436 609.965 423.772C592.593 454.552 569.889 476.952 541.851 490.972C513.813 504.99 484.251 512 453.165 512C422.079 512 393.737 505.448 368.137 492.342C342.537 478.934 312.975 457.448 279.451 427.886C257.507 408.686 239.679 395.124 225.965 387.2C212.251 378.972 197.165 374.858 180.707 374.858C156.021 374.858 135.603 383.39 119.451 400.458C111.043 409.34 104.947 421.36 101.165 436.518C94.4911 463.252 73.9191 486.4 46.3631 486.4Z" fill="white"/>
</svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -1,28 +0,0 @@
{
"fill": {
"solid": "display-p3:0.27100,0.25562,0.81934,1.00000"
},
"groups": [
{
"layers": [
{
"fill": "automatic",
"image-name": "Vector.svg",
"name": "Vector"
}
],
"shadow": {
"kind": "neutral",
"opacity": 0.5
},
"translucency": {
"enabled": true,
"value": 0.5
}
}
],
"supported-platforms": {
"circles": ["watchOS"],
"squares": "shared"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 624 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 807 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 930 B

View File

@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict/>
</plist>

View File

@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleIconFile</key>
<string>AppIcon</string>
<key>CFBundleIconName</key>
<string>AppIcon</string>
</dict>
</plist>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

View File

@@ -1,52 +0,0 @@
/*
* Copyright (C) 2026 Fluxer Contributors
*
* This file is part of Fluxer.
*
* Fluxer is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Fluxer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
*/
const {notarize} = require('@electron/notarize');
exports.default = async function notarizing(context) {
const {electronPlatformName, appOutDir} = context;
if (electronPlatformName !== 'darwin') {
return;
}
if (!process.env.APPLE_ID || !process.env.APPLE_APP_SPECIFIC_PASSWORD || !process.env.APPLE_TEAM_ID) {
console.log('Skipping notarization: Apple credentials not set');
return;
}
const appName = context.packager.appInfo.productFilename;
const appPath = `${appOutDir}/${appName}.app`;
console.log(`Notarizing ${appPath}...`);
try {
await notarize({
tool: 'notarytool',
appPath,
appleId: process.env.APPLE_ID,
appleIdPassword: process.env.APPLE_APP_SPECIFIC_PASSWORD,
teamId: process.env.APPLE_TEAM_ID,
});
console.log('Notarization complete');
} catch (error) {
console.error('Notarization failed:', error);
throw error;
}
};

View File

@@ -1,153 +0,0 @@
/*
* Copyright (C) 2026 Fluxer Contributors
*
* This file is part of Fluxer.
*
* Fluxer is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Fluxer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
*/
/** @type {import('electron-builder').Configuration} */
const config = (() => {
const channel = process.env.BUILD_CHANNEL === 'canary' ? 'canary' : 'stable';
const isCanary = channel === 'canary';
const appId = isCanary ? 'app.fluxer.canary' : 'app.fluxer';
const productName = isCanary ? 'Fluxer Canary' : 'Fluxer';
const iconsDir = isCanary ? 'electron-build-resources/icons-canary' : 'electron-build-resources/icons-stable';
const macEntitlements = isCanary
? 'electron-build-resources/entitlements.mac.canary.plist'
: 'electron-build-resources/entitlements.mac.stable.plist';
const macProfile = isCanary
? 'electron-build-resources/profiles/Fluxer_Canary.provisionprofile'
: 'electron-build-resources/profiles/Fluxer.provisionprofile';
const winIconUrl = isCanary
? 'https://fluxerstatic.com/web/icons/desktop/canary/icon.ico'
: 'https://fluxerstatic.com/web/icons/desktop/stable/icon.ico';
const linuxExecutableName = isCanary ? 'fluxercanary' : 'fluxer';
const linuxSynopsis = productName;
const linuxDescription = productName;
return {
appId,
productName,
copyright: 'Copyright (C) 2026 Fluxer Contributors',
artifactName: `fluxer-${channel}-\${version}-\${arch}.\${ext}`,
directories: {
output: 'dist-electron',
buildResources: 'electron-build-resources',
},
files: [
'src-electron/dist/**/*',
'!**/*.map',
'!**/*.md',
'!**/README*',
'!**/readme*',
'!**/CHANGELOG*',
'!**/LICENSE*',
'!**/.github/**',
'!**/docs/**',
'!**/doc/**',
'!**/example/**',
'!**/examples/**',
'!**/test/**',
'!**/tests/**',
'!**/__tests__/**',
'!**/*.ts',
'!**/tsconfig*.json',
],
extraMetadata: {
main: 'src-electron/dist/main/index.js',
type: 'module',
},
asar: true,
compression: 'normal',
asarUnpack: [
'**/*.node',
'**/node_modules/uiohook-napi/**',
'**/node_modules/input-monitoring-check/**',
'**/src-electron/dist/preload/**',
],
extraResources: [
{from: `${iconsDir}/512x512.png`, to: '512x512.png'},
{from: `${iconsDir}/badges`, to: 'badges'},
{from: `${iconsDir}/_compiled/Assets.car`, to: 'Assets.car'},
],
mac: {
category: 'public.app-category.social-networking',
icon: `${iconsDir}/_compiled/AppIcon.icns`,
hardenedRuntime: true,
gatekeeperAssess: false,
entitlements: macEntitlements,
entitlementsInherit: 'electron-build-resources/entitlements.mac.inherit.plist',
provisioningProfile: macProfile,
extendInfo: {
CFBundleIconName: 'AppIcon',
NSMicrophoneUsageDescription: 'Fluxer needs access to your microphone for voice chat.',
NSCameraUsageDescription: 'Fluxer needs access to your camera for video chat.',
NSInputMonitoringUsageDescription: 'Fluxer needs Input Monitoring access for global shortcuts and hotkeys.',
},
notarize: true,
target: [
{target: 'dmg', arch: ['x64', 'arm64']},
{target: 'zip', arch: ['x64', 'arm64']},
],
},
dmg: {
sign: false,
icon: `${iconsDir}/_compiled/AppIcon.icns`,
format: 'UDZO',
contents: [
{x: 130, y: 220},
{x: 410, y: 220, type: 'link', path: '/Applications'},
],
},
win: {
icon: `${iconsDir}/icon.ico`,
target: [{target: 'squirrel'}],
},
squirrelWindows: {
iconUrl: winIconUrl,
},
linux: {
icon: iconsDir,
category: 'Network',
maintainer: 'Fluxer Contributors',
synopsis: linuxSynopsis,
description: linuxDescription,
executableName: linuxExecutableName,
target: ['dir', 'AppImage', 'deb', 'rpm', 'tar.gz'],
mimeTypes: ['x-scheme-handler/fluxer'],
},
protocols: [{name: 'Fluxer', schemes: ['fluxer']}],
};
})();
module.exports = config;

View File

@@ -3,8 +3,8 @@
<head>
<meta charset="utf-8">
<title>Fluxer</title>
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover, maximum-scale=1, user-scalable=no">
<meta name="description" content="Fluxer is an open-source, independent instant messaging and VoIP platform. Built for friends, groups, and communities.">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover, interactive-widget=resizes-content, maximum-scale=1, user-scalable=no">
<meta name="description" content="Fluxer is a free and open source instant messaging and VoIP platform built for friends, groups, and communities.">
<link rel="preconnect" href="https://fluxerstatic.com">
<link rel="stylesheet" href="https://fluxerstatic.com/fonts/ibm-plex.css">
<link rel="manifest" href="/manifest.json">

View File

@@ -1,28 +0,0 @@
{
"$schema": "https://unpkg.com/knip@5/schema.json",
"entry": ["src-electron/main/index.ts", "scripts/*.mjs"],
"ignore": [
"lingui.config.js",
"postcss.config.js",
"src/sw/worker.ts",
"pkgs/libfluxcore/libfluxcore_bg.wasm.d.ts",
"pkgs/libfluxcore/libfluxcore.d.ts",
"src/types/browser.d.ts"
],
"ignoreDependencies": [
"@electron-webauthn/native",
"electron-webauthn-mac",
"fs-extra",
"undici",
"update-electron-app",
"@types/ws",
"electron-builder-squirrel-windows"
],
"ignoreBinaries": ["go"],
"ignoreExportsUsedInFile": true,
"project": ["src/**/*.{ts,tsx}"],
"rules": {
"exports": "off",
"types": "off"
}
}

View File

@@ -1,159 +1,42 @@
{
"name": "fluxer_app",
"version": "1.0.0",
"type": "module",
"version": "0.0.0",
"private": true,
"description": "Fluxer is an open-source, independent instant messaging and VoIP platform. Built for friends, groups, and communities.",
"description": "Fluxer is a free and open source instant messaging and VoIP platform built for friends, groups, and communities.",
"homepage": "https://fluxer.app",
"author": "Fluxer Contributors <developers@fluxer.app>",
"sideEffects": [
"*.css",
"**/*.css"
],
"type": "module",
"exports": {
".": "./src/index.tsx",
"./src/*": "./src/*"
},
"scripts": {
"build": "pnpm wasm:codegen && pnpm generate:colors && pnpm generate:masks && pnpm generate:css-types && tsc --noEmit && pnpm lingui:compile && rm -rf dist && rspack build --mode production && npx tsx scripts/build-sw.mjs",
"dev": "pnpm wasm:codegen && pnpm generate:colors && pnpm generate:masks && pnpm generate:css-types && pnpm lingui:compile && rm -rf dist && rspack serve",
"build": "pnpm wasm:codegen && pnpm generate:colors && pnpm generate:masks && pnpm generate:css-types && tsgo --noEmit && pnpm lingui:compile && rm -rf dist && rspack build --mode production && pnpm tsx scripts/build-sw.mjs",
"dev": "pnpm tsx scripts/DevServer.tsx",
"typecheck": "pnpm wasm:codegen && pnpm generate:masks && pnpm generate:css-types && tsgo --noEmit",
"typecheck:only": "tsgo --noEmit",
"generate:colors": "pnpm tsx scripts/GenerateColorSystem.tsx",
"generate:css-types": "tcm src --pattern '**/*.module.css'",
"electron:build:canary": "BUILD_CHANNEL=canary pnpm electron:compile && BUILD_CHANNEL=canary electron-builder --config electron-builder.canary.yaml",
"electron:build:stable": "pnpm electron:compile && electron-builder --config electron-builder.yaml",
"electron:compile": "node scripts/build-electron.mjs",
"electron:dev": "pnpm electron:compile && electron .",
"generate:colors": "cd scripts && go run ./cmd/generate-color-system",
"generate:emoji-sprites": "cd scripts && go run ./cmd/generate-emoji-sprites",
"generate:masks": "npx tsx scripts/generate-avatar-masks.ts",
"knip": "knip",
"lingui": "pnpm lingui:extract && cd scripts && pnpm translate && pnpm lingui:compile",
"generate:css-types:watch": "tcm src --pattern '**/*.module.css' --watch",
"generate:emoji-sprites": "pnpm tsx scripts/GenerateEmojiSprites.tsx",
"generate:masks": "pnpm tsx scripts/GenerateAvatarMasks.tsx",
"i18n:auto": "node scripts/auto-i18n.mjs",
"i18n:compile": "pnpm lingui:compile",
"i18n:extract": "pnpm lingui:extract",
"lingui:compile": "lingui compile --strict",
"lingui:extract": "lingui extract --clean",
"translate": "cd scripts/cmd/locales-pending && go run . --locales-dir=../../../src/locales",
"translate:locale": "cd scripts/cmd/locales-pending && go run . --locales-dir=../../../src/locales --locale",
"translate:qc": "cd scripts/cmd/locales-pending && go run . --locales-dir=../../../src/locales --qc-only --qc-passes 2",
"translate:reset": "cd scripts/cmd/locales-pending/reset && go run . --locales-dir=../../../../src/locales",
"set:build-channel": "node scripts/set-build-channel.mjs",
"test": "vitest run",
"test": "pnpm i18n:compile && vitest run",
"test:coverage": "vitest run --coverage",
"test:debug": "vitest run --no-coverage --inspect-brk --threads=false",
"test:ui": "vitest --ui",
"test:watch": "vitest",
"tsc:all": "pnpm wasm:codegen && pnpm tsc -p tsconfig.json && pnpm tsc -p tsconfig.electron.json",
"tsc:all": "pnpm wasm:codegen && pnpm tsgo -p tsconfig.json",
"wasm:codegen": "cd crates/libfluxcore && wasm-pack build --target web --out-dir ../../pkgs/libfluxcore --release"
},
"dependencies": {
"@dnd-kit/core": "6.3.1",
"@dnd-kit/modifiers": "9.0.0",
"@dnd-kit/sortable": "10.0.0",
"@dnd-kit/utilities": "3.2.2",
"@electron-webauthn/native": "^0.0.6",
"@floating-ui/react": "0.27.16",
"@floating-ui/react-dom": "2.1.6",
"@hcaptcha/react-hcaptcha": "1.17.2",
"@lingui/core": "5.7.0",
"@lingui/react": "5.7.0",
"@livekit/components-react": "2.9.17",
"@livekit/track-processors": "0.7.0",
"@marsidev/react-turnstile": "1.4.0",
"@mediapipe/tasks-vision": "0.10.14",
"@phosphor-icons/react": "2.1.10",
"@radix-ui/react-checkbox": "^1.3.3",
"@radix-ui/react-radio-group": "1.3.8",
"@radix-ui/react-switch": "^1.2.6",
"@sentry/react": "10.32.1",
"@simplewebauthn/browser": "13.2.2",
"bowser": "2.13.1",
"clsx": "2.1.1",
"colorjs.io": "0.6.0",
"combokeys": "3.0.1",
"electron-squirrel-startup": "^1.0.1",
"electron-webauthn-mac": "^1.0.0",
"emoji-regex": "10.6.0",
"eventemitter3": "5.0.1",
"favico.js": "0.3.10",
"framer-motion": "12.23.26",
"fs-extra": "11.3.3",
"highlight.js": "11.11.1",
"idna-uts46-hx": "6.1.0",
"katex": "0.16.27",
"livekit-client": "2.16.1",
"lodash": "4.17.21",
"lru-cache": "11.2.4",
"luxon": "3.7.2",
"match-sorter": "8.2.0",
"mobx": "6.15.0",
"mobx-persist-store": "1.1.8",
"mobx-react-lite": "4.1.1",
"motion": "12.23.26",
"node-mac-permissions": "^2.5.0",
"qrcode": "1.5.4",
"react": "19.2.3",
"react-aria-components": "1.14.0",
"react-dnd": "16.0.1",
"react-dnd-accessible-backend": "2.1.0",
"react-dnd-html5-backend": "16.0.1",
"react-dnd-multi-backend": "9.0.0",
"react-dom": "19.2.3",
"react-hook-form": "7.69.0",
"react-hotkeys-hook": "5.2.1",
"react-modal-sheet": "5.2.1",
"react-select": "5.10.2",
"react-zoom-pan-pinch": "3.7.0",
"rxjs": "7.8.2",
"thumbhash": "0.1.1",
"undici": "7.16.0",
"unique-names-generator": "4.7.1",
"update-electron-app": "3.1.2",
"urlpattern-polyfill": "10.1.0",
"valibot": "1.2.0",
"ws": "8.18.3"
},
"devDependencies": {
"@lingui/cli": "5.7.0",
"@lingui/swc-plugin": "5.9.0",
"@rspack/cli": "1.6.2",
"@rspack/core": "1.6.2",
"@rspack/plugin-react-refresh": "1.5.1",
"@svgr/core": "^8.1.0",
"@svgr/plugin-jsx": "^8.1.0",
"@svgr/plugin-svgo": "^8.1.0",
"@svgr/webpack": "^8.1.0",
"@types/combokeys": "2.4.9",
"@types/electron-squirrel-startup": "^1.0.2",
"@types/jsdom": "27.0.0",
"@types/lodash": "4.17.21",
"@types/luxon": "3.7.1",
"@types/node": "25.0.3",
"@types/qrcode": "1.5.6",
"@types/react": "19.2.7",
"@types/react-dom": "19.2.3",
"@types/ws": "^8.18.1",
"@vitest/coverage-v8": "4.0.16",
"autoprefixer": "^10.4.23",
"browserslist": "^4.28.1",
"chokidar": "^4.0.0",
"electron": "39.2.7",
"electron-builder": "26.0.12",
"electron-builder-squirrel-windows": "^26.0.12",
"esbuild": "^0.27.2",
"happy-dom": "20.0.11",
"jsdom": "27.4.0",
"knip": "5.78.0",
"lightningcss": "^1.30.2",
"node-addon-api": "^8.5.0",
"postcss": "^8.4.49",
"postcss-discard-comments": "^7.0.5",
"postcss-loader": "^8.2.0",
"postcss-modules": "^6.0.1",
"postcss-preset-env": "^10.6.0",
"react-refresh": "^0.14.2",
"tsx": "4.21.0",
"typed-css-modules": "^0.9.1",
"typescript": "5.9.3",
"vitest": "4.0.16",
"wasm-pack": "0.13.1"
},
"optionalDependencies": {
"electron-log": "5.4.3",
"uiohook-napi": "1.5.4"
},
"browserslist": [
"chrome >= 47",
"edge >= 12",
@@ -165,30 +48,108 @@
"> 0.2%",
"not op_mini all"
],
"packageManager": "pnpm@10.26.0",
"pnpm": {
"peerDependencyRules": {
"allowedVersions": {
"react": "*"
},
"ignoreMissing": [
"webpack",
"babel-plugin-macros"
]
},
"overrides": {
"@rspack/core": "1.6.2",
"@rspack/cli": "1.6.2",
"@lingui/swc-plugin": "5.9.0"
},
"onlyBuiltDependencies": [
"core-js",
"core-js-pure",
"electron",
"node-mac-permissions",
"oxc-resolver",
"protobufjs",
"uiohook-napi"
]
}
"dependencies": {
"@floating-ui/react": "catalog:",
"@floating-ui/react-dom": "catalog:",
"@fluxer/constants": "workspace:*",
"@fluxer/date_utils": "workspace:*",
"@fluxer/geo_utils": "workspace:*",
"@fluxer/limits": "workspace:*",
"@fluxer/list_utils": "workspace:*",
"@fluxer/markdown_parser": "workspace:*",
"@fluxer/number_utils": "workspace:*",
"@fluxer/schema": "workspace:*",
"@fluxer/snowflake": "workspace:*",
"@fluxer/ui": "workspace:*",
"@hcaptcha/react-hcaptcha": "catalog:",
"@lingui/core": "catalog:",
"@lingui/react": "catalog:",
"@livekit/components-react": "catalog:",
"@livekit/track-processors": "catalog:",
"@marsidev/react-turnstile": "catalog:",
"@phosphor-icons/react": "catalog:",
"@radix-ui/react-checkbox": "catalog:",
"@radix-ui/react-radio-group": "catalog:",
"@radix-ui/react-switch": "catalog:",
"@sentry/react": "catalog:",
"@simplewebauthn/browser": "catalog:",
"bowser": "catalog:",
"clsx": "catalog:",
"colorjs.io": "catalog:",
"combokeys": "catalog:",
"eventemitter3": "catalog:",
"favico.js": "catalog:",
"framer-motion": "catalog:",
"fs-extra": "catalog:",
"highlight.js": "catalog:",
"katex": "catalog:",
"livekit-client": "catalog:",
"lodash": "catalog:",
"lru-cache": "catalog:",
"luxon": "catalog:",
"match-sorter": "catalog:",
"mobx": "catalog:",
"mobx-persist-store": "catalog:",
"mobx-react-lite": "catalog:",
"motion": "catalog:",
"qrcode": "catalog:",
"react": "catalog:",
"react-aria-components": "catalog:",
"react-day-picker": "catalog:",
"react-dnd": "catalog:",
"react-dnd-accessible-backend": "catalog:",
"react-dnd-html5-backend": "catalog:",
"react-dnd-multi-backend": "catalog:",
"react-dom": "catalog:",
"react-hook-form": "catalog:",
"react-hotkeys-hook": "catalog:",
"react-modal-sheet": "catalog:",
"react-select": "catalog:",
"react-zoom-pan-pinch": "catalog:",
"rxjs": "catalog:",
"thumbhash": "catalog:",
"unique-names-generator": "catalog:",
"urlpattern-polyfill": "catalog:",
"valibot": "catalog:"
},
"devDependencies": {
"@lingui/cli": "catalog:",
"@lingui/swc-plugin": "catalog:",
"@rspack/cli": "catalog:",
"@rspack/core": "catalog:",
"@svgr/core": "catalog:",
"@svgr/plugin-jsx": "catalog:",
"@svgr/plugin-svgo": "catalog:",
"@svgr/webpack": "catalog:",
"@types/combokeys": "catalog:",
"@types/jsdom": "catalog:",
"@types/lodash": "catalog:",
"@types/luxon": "catalog:",
"@types/node": "catalog:",
"@types/qrcode": "catalog:",
"@types/react": "catalog:",
"@types/react-dom": "catalog:",
"@typescript/native-preview": "catalog:",
"@vitest/coverage-v8": "catalog:",
"autoprefixer": "catalog:",
"browserslist": "catalog:",
"chokidar": "catalog:",
"happy-dom": "catalog:",
"jsdom": "catalog:",
"lightningcss": "catalog:",
"node-addon-api": "catalog:",
"postcss": "catalog:",
"postcss-discard-comments": "catalog:",
"postcss-loader": "catalog:",
"postcss-modules": "catalog:",
"postcss-preset-env": "catalog:",
"sharp": "catalog:",
"esbuild": "catalog:",
"tsx": "catalog:",
"typed-css-modules": "catalog:",
"vite-tsconfig-paths": "catalog:",
"vitest": "catalog:",
"wasm-pack": "catalog:"
},
"packageManager": "pnpm@10.29.3"
}

4367
fluxer_app/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,33 +0,0 @@
FROM golang:1.25.5-alpine AS build
WORKDIR /build
COPY fluxer_app/proxy/go.mod ./
COPY fluxer_app/proxy/go.sum* ./
RUN go mod download
COPY fluxer_app/proxy/ .
COPY fluxer_app/dist/index.html assets/index.html
COPY fluxer_app/dist/manifest.json assets/manifest.json
COPY fluxer_app/dist/sw.js* assets/
COPY fluxer_app/dist/version.json assets/version.json
ARG TARGETOS=linux
ARG TARGETARCH=amd64
RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o fluxer-app-proxy
FROM alpine:3.19
RUN apk add --no-cache ca-certificates curl
WORKDIR /app
COPY --from=build /build/fluxer-app-proxy /app/fluxer-app-proxy
USER nobody
EXPOSE 8080
ENV PORT=8080
ENTRYPOINT ["/app/fluxer-app-proxy"]

View File

@@ -1,2 +0,0 @@
*
!.gitignore

View File

@@ -1,5 +0,0 @@
module github.com/fluxerapp/fluxer/proxy
go 1.25.5
require golang.org/x/time v0.14.0

View File

@@ -1,2 +0,0 @@
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=

Some files were not shown because too many files have changed in this diff Show More