creates video voice memos from audio clips; with bluesky integration. trill.ptr.pet

feat: add audio transcription

ptr.pet f04d69d1 cb193787

verified
+443 -4
deno.lock
··· 10 10 "npm:@atcute/lexicons@^1.2.3": "1.2.3", 11 11 "npm:@atcute/microcosm@1": "1.0.0", 12 12 "npm:@atcute/oauth-browser-client@^2.0.1": "2.0.1_@atcute+identity@1.1.2", 13 + "npm:@huggingface/transformers@^3.8.0": "3.8.0", 13 14 "npm:@pandacss/dev@^1.5.1": "1.5.1_typescript@5.9.3", 14 15 "npm:@pandacss/preset-base@^1.5.1": "1.5.1", 15 16 "npm:@park-ui/panda-preset@~0.43.1": "0.43.1_@pandacss+dev@1.5.1__typescript@5.9.3_typescript@5.9.3", ··· 243 244 "debug", 244 245 "gensync", 245 246 "json5", 246 - "semver" 247 + "semver@6.3.1" 247 248 ] 248 249 }, 249 250 "@babel/generator@7.28.5": { ··· 263 264 "@babel/helper-validator-option", 264 265 "browserslist@4.28.0", 265 266 "lru-cache", 266 - "semver" 267 + "semver@6.3.1" 267 268 ] 268 269 }, 269 270 "@babel/helper-globals@7.28.0": { ··· 390 391 "postcss-selector-parser" 391 392 ] 392 393 }, 394 + "@emnapi/runtime@1.7.1": { 395 + "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", 396 + "dependencies": [ 397 + "tslib" 398 + ] 399 + }, 393 400 "@esbuild/aix-ppc64@0.25.12": { 394 401 "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", 395 402 "os": ["aix"], ··· 543 550 "@floating-ui/utils@0.2.10": { 544 551 "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==" 545 552 }, 553 + "@huggingface/jinja@0.5.1": { 554 + "integrity": "sha512-yUZLld4lrM9iFxHCwFQ7D1HW2MWMwSbeB7WzWqFYDWK+rEb+WldkLdAJxUPOmgICMHZLzZGVcVjFh3w/YGubng==" 555 + }, 556 + "@huggingface/transformers@3.8.0": { 557 + "integrity": "sha512-bEvx9k/fnhjKtekc1pDYLGDhVwxW5mO3k4UkP/mJQtQI5dC34daCQ28O1B19lkyM0QYFwAj+RGl0qOjE9Upt/w==", 558 + "dependencies": [ 559 + "@huggingface/jinja", 560 + "onnxruntime-node", 561 + "onnxruntime-web", 562 + "sharp" 563 + ] 564 + }, 565 + "@img/colour@1.0.0": { 566 + "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==" 567 + }, 568 + "@img/sharp-darwin-arm64@0.34.5": { 569 + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", 570 + "optionalDependencies": [ 571 + "@img/sharp-libvips-darwin-arm64" 572 + ], 573 + "os": ["darwin"], 574 + "cpu": ["arm64"] 575 + }, 576 + "@img/sharp-darwin-x64@0.34.5": { 577 + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", 578 + "optionalDependencies": [ 579 + "@img/sharp-libvips-darwin-x64" 580 + ], 581 + "os": ["darwin"], 582 + "cpu": ["x64"] 583 + }, 584 + "@img/sharp-libvips-darwin-arm64@1.2.4": { 585 + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", 586 + "os": ["darwin"], 587 + "cpu": ["arm64"] 588 + }, 589 + "@img/sharp-libvips-darwin-x64@1.2.4": { 590 + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", 591 + "os": ["darwin"], 592 + "cpu": ["x64"] 593 + }, 594 + "@img/sharp-libvips-linux-arm64@1.2.4": { 595 + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", 596 + "os": ["linux"], 597 + "cpu": ["arm64"] 598 + }, 599 + "@img/sharp-libvips-linux-arm@1.2.4": { 600 + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", 601 + "os": ["linux"], 602 + "cpu": ["arm"] 603 + }, 604 + "@img/sharp-libvips-linux-ppc64@1.2.4": { 605 + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", 606 + "os": ["linux"], 607 + "cpu": ["ppc64"] 608 + }, 609 + "@img/sharp-libvips-linux-riscv64@1.2.4": { 610 + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", 611 + "os": ["linux"], 612 + "cpu": ["riscv64"] 613 + }, 614 + "@img/sharp-libvips-linux-s390x@1.2.4": { 615 + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", 616 + "os": ["linux"], 617 + "cpu": ["s390x"] 618 + }, 619 + "@img/sharp-libvips-linux-x64@1.2.4": { 620 + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", 621 + "os": ["linux"], 622 + "cpu": ["x64"] 623 + }, 624 + "@img/sharp-libvips-linuxmusl-arm64@1.2.4": { 625 + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", 626 + "os": ["linux"], 627 + "cpu": ["arm64"] 628 + }, 629 + "@img/sharp-libvips-linuxmusl-x64@1.2.4": { 630 + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", 631 + "os": ["linux"], 632 + "cpu": ["x64"] 633 + }, 634 + "@img/sharp-linux-arm64@0.34.5": { 635 + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", 636 + "optionalDependencies": [ 637 + "@img/sharp-libvips-linux-arm64" 638 + ], 639 + "os": ["linux"], 640 + "cpu": ["arm64"] 641 + }, 642 + "@img/sharp-linux-arm@0.34.5": { 643 + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", 644 + "optionalDependencies": [ 645 + "@img/sharp-libvips-linux-arm" 646 + ], 647 + "os": ["linux"], 648 + "cpu": ["arm"] 649 + }, 650 + "@img/sharp-linux-ppc64@0.34.5": { 651 + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", 652 + "optionalDependencies": [ 653 + "@img/sharp-libvips-linux-ppc64" 654 + ], 655 + "os": ["linux"], 656 + "cpu": ["ppc64"] 657 + }, 658 + "@img/sharp-linux-riscv64@0.34.5": { 659 + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", 660 + "optionalDependencies": [ 661 + "@img/sharp-libvips-linux-riscv64" 662 + ], 663 + "os": ["linux"], 664 + "cpu": ["riscv64"] 665 + }, 666 + "@img/sharp-linux-s390x@0.34.5": { 667 + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", 668 + "optionalDependencies": [ 669 + "@img/sharp-libvips-linux-s390x" 670 + ], 671 + "os": ["linux"], 672 + "cpu": ["s390x"] 673 + }, 674 + "@img/sharp-linux-x64@0.34.5": { 675 + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", 676 + "optionalDependencies": [ 677 + "@img/sharp-libvips-linux-x64" 678 + ], 679 + "os": ["linux"], 680 + "cpu": ["x64"] 681 + }, 682 + "@img/sharp-linuxmusl-arm64@0.34.5": { 683 + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", 684 + "optionalDependencies": [ 685 + "@img/sharp-libvips-linuxmusl-arm64" 686 + ], 687 + "os": ["linux"], 688 + "cpu": ["arm64"] 689 + }, 690 + "@img/sharp-linuxmusl-x64@0.34.5": { 691 + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", 692 + "optionalDependencies": [ 693 + "@img/sharp-libvips-linuxmusl-x64" 694 + ], 695 + "os": ["linux"], 696 + "cpu": ["x64"] 697 + }, 698 + "@img/sharp-wasm32@0.34.5": { 699 + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", 700 + "dependencies": [ 701 + "@emnapi/runtime" 702 + ], 703 + "cpu": ["wasm32"] 704 + }, 705 + "@img/sharp-win32-arm64@0.34.5": { 706 + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", 707 + "os": ["win32"], 708 + "cpu": ["arm64"] 709 + }, 710 + "@img/sharp-win32-ia32@0.34.5": { 711 + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", 712 + "os": ["win32"], 713 + "cpu": ["ia32"] 714 + }, 715 + "@img/sharp-win32-x64@0.34.5": { 716 + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", 717 + "os": ["win32"], 718 + "cpu": ["x64"] 719 + }, 546 720 "@internationalized/date@3.10.0": { 547 721 "integrity": "sha512-oxDR/NTEJ1k+UFVQElaNIk65E/Z83HK1z1WI3lQyhTtnNg4R5oVXaPzK3jcpKG8UHKDVuDQHzn+wsxSz8RP3aw==", 548 722 "dependencies": [ ··· 574 748 "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", 575 749 "dependencies": [ 576 750 "@isaacs/balanced-match" 751 + ] 752 + }, 753 + "@isaacs/fs-minipass@4.0.1": { 754 + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", 755 + "dependencies": [ 756 + "minipass" 577 757 ] 578 758 }, 579 759 "@jridgewell/gen-mapping@0.3.13": { ··· 817 997 "effect" 818 998 ], 819 999 "scripts": true 1000 + }, 1001 + "@protobufjs/aspromise@1.1.2": { 1002 + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" 1003 + }, 1004 + "@protobufjs/base64@1.1.2": { 1005 + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" 1006 + }, 1007 + "@protobufjs/codegen@2.0.4": { 1008 + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" 1009 + }, 1010 + "@protobufjs/eventemitter@1.1.0": { 1011 + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" 1012 + }, 1013 + "@protobufjs/fetch@1.1.0": { 1014 + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", 1015 + "dependencies": [ 1016 + "@protobufjs/aspromise", 1017 + "@protobufjs/inquire" 1018 + ] 1019 + }, 1020 + "@protobufjs/float@1.0.2": { 1021 + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" 1022 + }, 1023 + "@protobufjs/inquire@1.1.0": { 1024 + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" 1025 + }, 1026 + "@protobufjs/path@1.1.2": { 1027 + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" 1028 + }, 1029 + "@protobufjs/pool@1.1.0": { 1030 + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" 1031 + }, 1032 + "@protobufjs/utf8@1.1.0": { 1033 + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" 820 1034 }, 821 1035 "@rollup/rollup-android-arm-eabi@4.52.5": { 822 1036 "integrity": "sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==", ··· 2484 2698 "integrity": "sha512-gYjt7OIqdM0PcttNYP2aVrr2G0bMALkBaoehD4BuRGjAOtipg0b6wHg1yNL+s5zSnLZZrGHOw4IrND8CD+3oIQ==", 2485 2699 "bin": true 2486 2700 }, 2701 + "boolean@3.2.0": { 2702 + "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", 2703 + "deprecated": true 2704 + }, 2487 2705 "braces@3.0.3": { 2488 2706 "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", 2489 2707 "dependencies": [ ··· 2539 2757 "readdirp" 2540 2758 ] 2541 2759 }, 2760 + "chownr@3.0.0": { 2761 + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==" 2762 + }, 2542 2763 "code-block-writer@13.0.3": { 2543 2764 "integrity": "sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg==" 2544 2765 }, ··· 2582 2803 "ms" 2583 2804 ] 2584 2805 }, 2806 + "define-data-property@1.1.4": { 2807 + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", 2808 + "dependencies": [ 2809 + "es-define-property", 2810 + "es-errors", 2811 + "gopd" 2812 + ] 2813 + }, 2814 + "define-properties@1.2.1": { 2815 + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", 2816 + "dependencies": [ 2817 + "define-data-property", 2818 + "has-property-descriptors", 2819 + "object-keys" 2820 + ] 2821 + }, 2585 2822 "detect-libc@1.0.3": { 2586 2823 "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", 2587 2824 "bin": true 2825 + }, 2826 + "detect-libc@2.1.2": { 2827 + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==" 2828 + }, 2829 + "detect-node@2.1.0": { 2830 + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==" 2588 2831 }, 2589 2832 "effect@3.10.15": { 2590 2833 "integrity": "sha512-LdczPAFbtij3xGr9i+8PyDtuWdlXjSY5UJ8PKrYrr0DClKfR/OW3j8sxtambWYljzJAYD865KFhv7LdbWdG7VQ==", ··· 2604 2847 "entities@6.0.1": { 2605 2848 "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==" 2606 2849 }, 2850 + "es-define-property@1.0.1": { 2851 + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==" 2852 + }, 2853 + "es-errors@1.3.0": { 2854 + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==" 2855 + }, 2856 + "es6-error@4.1.1": { 2857 + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==" 2858 + }, 2607 2859 "esbuild@0.25.12": { 2608 2860 "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", 2609 2861 "optionalDependencies": [ ··· 2642 2894 }, 2643 2895 "escalade@3.2.0": { 2644 2896 "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==" 2897 + }, 2898 + "escape-string-regexp@4.0.0": { 2899 + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" 2645 2900 }, 2646 2901 "esm-env@1.2.2": { 2647 2902 "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==" ··· 2698 2953 "to-regex-range" 2699 2954 ] 2700 2955 }, 2956 + "flatbuffers@25.9.23": { 2957 + "integrity": "sha512-MI1qs7Lo4Syw0EOzUl0xjs2lsoeqFku44KpngfIduHBYvzm8h2+7K8YMQh1JtVVVrUvhLpNwqVi4DERegUJhPQ==" 2958 + }, 2701 2959 "focus-trap@7.5.4": { 2702 2960 "integrity": "sha512-N7kHdlgsO/v+iD/dMoJKtsSqs5Dz/dXZVebRgJw23LDk+jMi/974zyiOYDziY2JPp8xivq9BmUGwIJMiuSBi7w==", 2703 2961 "dependencies": [ ··· 2730 2988 "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", 2731 2989 "dependencies": [ 2732 2990 "is-glob" 2991 + ] 2992 + }, 2993 + "global-agent@3.0.0": { 2994 + "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", 2995 + "dependencies": [ 2996 + "boolean", 2997 + "es6-error", 2998 + "matcher", 2999 + "roarr", 3000 + "semver@7.7.3", 3001 + "serialize-error" 3002 + ] 3003 + }, 3004 + "globalthis@1.0.4": { 3005 + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", 3006 + "dependencies": [ 3007 + "define-properties", 3008 + "gopd" 2733 3009 ] 2734 3010 }, 2735 3011 "globrex@0.1.2": { 2736 3012 "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==" 2737 3013 }, 3014 + "gopd@1.2.0": { 3015 + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==" 3016 + }, 2738 3017 "graceful-fs@4.2.11": { 2739 3018 "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" 3019 + }, 3020 + "guid-typescript@1.0.9": { 3021 + "integrity": "sha512-Y8T4vYhEfwJOTbouREvG+3XDsjr8E3kIr7uf+JZ0BYloFsttiHU0WfvANVsR7TxNUJa/WpCnw/Ino/p+DeBhBQ==" 3022 + }, 3023 + "has-property-descriptors@1.0.2": { 3024 + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", 3025 + "dependencies": [ 3026 + "es-define-property" 3027 + ] 2740 3028 }, 2741 3029 "hookable@5.5.3": { 2742 3030 "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==" ··· 2775 3063 "json-schema-traverse@1.0.0": { 2776 3064 "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" 2777 3065 }, 3066 + "json-stringify-safe@5.0.1": { 3067 + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" 3068 + }, 2778 3069 "json5@2.2.3": { 2779 3070 "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", 2780 3071 "bin": true ··· 2842 3133 "lightningcss@1.25.1": { 2843 3134 "integrity": "sha512-V0RMVZzK1+rCHpymRv4URK2lNhIRyO8g7U7zOFwVAhJuat74HtkjIQpQRKNCwFEYkRGpafOpmXXLoaoBcyVtBg==", 2844 3135 "dependencies": [ 2845 - "detect-libc" 3136 + "detect-libc@1.0.3" 2846 3137 ], 2847 3138 "optionalDependencies": [ 2848 3139 "lightningcss-darwin-arm64", ··· 2868 3159 "lodash.uniq@4.5.0": { 2869 3160 "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==" 2870 3161 }, 3162 + "long@5.3.2": { 3163 + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==" 3164 + }, 2871 3165 "look-it-up@2.1.0": { 2872 3166 "integrity": "sha512-nMoGWW2HurtuJf6XAL56FWTDCWLOTSsanrgwOyaR5Y4e3zfG5N/0cU5xWZSEU3tBxhQugRbV1xL9jb+ug7yZww==" 2873 3167 }, 2874 3168 "lru-cache@5.1.1": { 2875 3169 "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", 2876 3170 "dependencies": [ 2877 - "yallist" 3171 + "yallist@3.1.1" 2878 3172 ] 2879 3173 }, 2880 3174 "lucide-solid@0.553.0_solid-js@1.9.10__seroval@1.3.2": { ··· 2887 3181 "integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==", 2888 3182 "dependencies": [ 2889 3183 "@jridgewell/sourcemap-codec" 3184 + ] 3185 + }, 3186 + "matcher@3.0.0": { 3187 + "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", 3188 + "dependencies": [ 3189 + "escape-string-regexp" 2890 3190 ] 2891 3191 }, 2892 3192 "mediabunny@1.25.0": { ··· 2921 3221 "@isaacs/brace-expansion" 2922 3222 ] 2923 3223 }, 3224 + "minipass@7.1.2": { 3225 + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==" 3226 + }, 3227 + "minizlib@3.1.0": { 3228 + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", 3229 + "dependencies": [ 3230 + "minipass" 3231 + ] 3232 + }, 2924 3233 "ms@2.1.3": { 2925 3234 "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" 2926 3235 }, ··· 2941 3250 "node-releases@2.0.27": { 2942 3251 "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==" 2943 3252 }, 3253 + "object-keys@1.1.1": { 3254 + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" 3255 + }, 2944 3256 "object-path@0.11.8": { 2945 3257 "integrity": "sha512-YJjNZrlXJFM42wTBn6zgOJVar9KFJvzx6sTWDte8sWZF//cnjl0BxHNpfZx+ZffXX63A9q0b1zsFiBX4g4X5KA==" 2946 3258 }, 3259 + "onnxruntime-common@1.21.0": { 3260 + "integrity": "sha512-Q632iLLrtCAVOTO65dh2+mNbQir/QNTVBG3h/QdZBpns7mZ0RYbLRBgGABPbpU9351AgYy7SJf1WaeVwMrBFPQ==" 3261 + }, 3262 + "onnxruntime-common@1.22.0-dev.20250409-89f8206ba4": { 3263 + "integrity": "sha512-vDJMkfCfb0b1A836rgHj+ORuZf4B4+cc2bASQtpeoJLueuFc5DuYwjIZUBrSvx/fO5IrLjLz+oTrB3pcGlhovQ==" 3264 + }, 3265 + "onnxruntime-node@1.21.0": { 3266 + "integrity": "sha512-NeaCX6WW2L8cRCSqy3bInlo5ojjQqu2fD3D+9W5qb5irwxhEyWKXeH2vZ8W9r6VxaMPUan+4/7NDwZMtouZxEw==", 3267 + "dependencies": [ 3268 + "global-agent", 3269 + "onnxruntime-common@1.21.0", 3270 + "tar" 3271 + ], 3272 + "os": ["win32", "darwin", "linux"], 3273 + "scripts": true 3274 + }, 3275 + "onnxruntime-web@1.22.0-dev.20250409-89f8206ba4": { 3276 + "integrity": "sha512-0uS76OPgH0hWCPrFKlL8kYVV7ckM7t/36HfbgoFw6Nd0CZVVbQC4PkrR8mBX8LtNUFZO25IQBqV2Hx2ho3FlbQ==", 3277 + "dependencies": [ 3278 + "flatbuffers", 3279 + "guid-typescript", 3280 + "long", 3281 + "onnxruntime-common@1.22.0-dev.20250409-89f8206ba4", 3282 + "platform", 3283 + "protobufjs" 3284 + ] 3285 + }, 2947 3286 "outdent@0.8.0": { 2948 3287 "integrity": "sha512-KiOAIsdpUTcAXuykya5fnVVT+/5uS0Q1mrkRHcF89tpieSmY33O/tmc54CqwA+bfhbtEfZUNLHaPUiB9X3jt1A==" 2949 3288 }, ··· 2987 3326 "exsolve", 2988 3327 "pathe" 2989 3328 ] 3329 + }, 3330 + "platform@1.3.6": { 3331 + "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==" 2990 3332 }, 2991 3333 "pluralize@8.0.0": { 2992 3334 "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==" ··· 3057 3399 "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", 3058 3400 "bin": true 3059 3401 }, 3402 + "protobufjs@7.5.4": { 3403 + "integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==", 3404 + "dependencies": [ 3405 + "@protobufjs/aspromise", 3406 + "@protobufjs/base64", 3407 + "@protobufjs/codegen", 3408 + "@protobufjs/eventemitter", 3409 + "@protobufjs/fetch", 3410 + "@protobufjs/float", 3411 + "@protobufjs/inquire", 3412 + "@protobufjs/path", 3413 + "@protobufjs/pool", 3414 + "@protobufjs/utf8", 3415 + "@types/node", 3416 + "long" 3417 + ], 3418 + "scripts": true 3419 + }, 3060 3420 "proxy-compare@3.0.0": { 3061 3421 "integrity": "sha512-y44MCkgtZUCT9tZGuE278fB7PWVf7fRYy0vbRXAts2o5F0EfC4fIQrvQQGBJo1WJbFcVLXzApOscyJuZqHQc1w==" 3062 3422 }, ··· 3084 3444 "reusify@1.1.0": { 3085 3445 "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==" 3086 3446 }, 3447 + "roarr@2.15.4": { 3448 + "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", 3449 + "dependencies": [ 3450 + "boolean", 3451 + "detect-node", 3452 + "globalthis", 3453 + "json-stringify-safe", 3454 + "semver-compare", 3455 + "sprintf-js" 3456 + ] 3457 + }, 3087 3458 "rollup@4.52.5": { 3088 3459 "integrity": "sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==", 3089 3460 "dependencies": [ ··· 3122 3493 "queue-microtask" 3123 3494 ] 3124 3495 }, 3496 + "semver-compare@1.0.0": { 3497 + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==" 3498 + }, 3125 3499 "semver@6.3.1": { 3126 3500 "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", 3127 3501 "bin": true 3502 + }, 3503 + "semver@7.7.3": { 3504 + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", 3505 + "bin": true 3506 + }, 3507 + "serialize-error@7.0.1": { 3508 + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", 3509 + "dependencies": [ 3510 + "type-fest" 3511 + ] 3128 3512 }, 3129 3513 "seroval-plugins@1.3.3_seroval@1.3.2": { 3130 3514 "integrity": "sha512-16OL3NnUBw8JG1jBLUoZJsLnQq0n5Ua6aHalhJK4fMQkz1lqR7Osz1sA30trBtd9VUDc2NgkuRCn8+/pBwqZ+w==", ··· 3135 3519 "seroval@1.3.2": { 3136 3520 "integrity": "sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ==" 3137 3521 }, 3522 + "sharp@0.34.5": { 3523 + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", 3524 + "dependencies": [ 3525 + "@img/colour", 3526 + "detect-libc@2.1.2", 3527 + "semver@7.7.3" 3528 + ], 3529 + "optionalDependencies": [ 3530 + "@img/sharp-darwin-arm64", 3531 + "@img/sharp-darwin-x64", 3532 + "@img/sharp-libvips-darwin-arm64", 3533 + "@img/sharp-libvips-darwin-x64", 3534 + "@img/sharp-libvips-linux-arm", 3535 + "@img/sharp-libvips-linux-arm64", 3536 + "@img/sharp-libvips-linux-ppc64", 3537 + "@img/sharp-libvips-linux-riscv64", 3538 + "@img/sharp-libvips-linux-s390x", 3539 + "@img/sharp-libvips-linux-x64", 3540 + "@img/sharp-libvips-linuxmusl-arm64", 3541 + "@img/sharp-libvips-linuxmusl-x64", 3542 + "@img/sharp-linux-arm", 3543 + "@img/sharp-linux-arm64", 3544 + "@img/sharp-linux-ppc64", 3545 + "@img/sharp-linux-riscv64", 3546 + "@img/sharp-linux-s390x", 3547 + "@img/sharp-linux-x64", 3548 + "@img/sharp-linuxmusl-arm64", 3549 + "@img/sharp-linuxmusl-x64", 3550 + "@img/sharp-wasm32", 3551 + "@img/sharp-win32-arm64", 3552 + "@img/sharp-win32-ia32", 3553 + "@img/sharp-win32-x64" 3554 + ], 3555 + "scripts": true 3556 + }, 3138 3557 "sisteransi@1.0.5": { 3139 3558 "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" 3140 3559 }, ··· 3181 3600 "source-map-js@1.2.1": { 3182 3601 "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==" 3183 3602 }, 3603 + "sprintf-js@1.1.3": { 3604 + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==" 3605 + }, 3184 3606 "string-width@4.2.3": { 3185 3607 "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 3186 3608 "dependencies": [ ··· 3208 3630 "strip-ansi" 3209 3631 ] 3210 3632 }, 3633 + "tar@7.5.1": { 3634 + "integrity": "sha512-nlGpxf+hv0v7GkWBK2V9spgactGOp0qvfWRxUMjqHyzrt3SgwE48DIv/FhqPHJYLHpgW1opq3nERbz5Anq7n1g==", 3635 + "dependencies": [ 3636 + "@isaacs/fs-minipass", 3637 + "chownr", 3638 + "minipass", 3639 + "minizlib", 3640 + "yallist@5.0.0" 3641 + ] 3642 + }, 3211 3643 "tinyglobby@0.2.15_picomatch@4.0.3": { 3212 3644 "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", 3213 3645 "dependencies": [ ··· 3252 3684 }, 3253 3685 "tslib@2.8.1": { 3254 3686 "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" 3687 + }, 3688 + "type-fest@0.13.1": { 3689 + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==" 3255 3690 }, 3256 3691 "typescript@5.9.3": { 3257 3692 "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", ··· 3338 3773 }, 3339 3774 "yallist@3.1.1": { 3340 3775 "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" 3776 + }, 3777 + "yallist@5.0.0": { 3778 + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==" 3341 3779 } 3342 3780 }, 3343 3781 "workspace": { ··· 3352 3790 "npm:@atcute/lexicons@^1.2.3", 3353 3791 "npm:@atcute/microcosm@1", 3354 3792 "npm:@atcute/oauth-browser-client@^2.0.1", 3793 + "npm:@huggingface/transformers@^3.8.0", 3355 3794 "npm:@pandacss/dev@^1.5.1", 3356 3795 "npm:@pandacss/preset-base@^1.5.1", 3357 3796 "npm:@park-ui/panda-preset@~0.43.1",
+1
package.json
··· 31 31 "@atcute/lexicons": "^1.2.3", 32 32 "@atcute/microcosm": "^1.0.0", 33 33 "@atcute/oauth-browser-client": "^2.0.1", 34 + "@huggingface/transformers": "^3.8.0", 34 35 "@solid-primitives/date": "^2.1.4", 35 36 "@solid-primitives/map": "^0.7.2", 36 37 "fast-average-color": "^9.5.0",
+50 -22
src/components/FileTask.tsx
··· 1 1 import { 2 + CaptionsIcon, 2 3 CircleAlertIcon, 3 4 DownloadIcon, 4 5 EllipsisVerticalIcon, ··· 14 15 15 16 import { TaskState } from "~/lib/task"; 16 17 import PostDialog from "./PostDialog"; 17 - import { Button } from "./ui/button"; 18 + import { Button, ButtonProps } from "./ui/button"; 18 19 import { Menu } from "./ui/menu"; 19 20 import { createSignal } from "solid-js"; 21 + import { toaster } from "./Toaster"; 20 22 21 23 const downloadFile = (blob: Blob, fileName: string) => { 22 24 const url = URL.createObjectURL(blob); ··· 53 55 </Popover.Positioner> 54 56 </Popover.Root> 55 57 ); 56 - const statusSuccess = (result: Blob) => { 58 + const statusSuccess = (result: Blob, altText?: string) => { 59 + const [menuOpen, setMenuOpen] = createSignal(false); 60 + const MenuButton = (props: ButtonProps) => ( 61 + <Button 62 + color={{ _hover: "colorPalette.emphasized" }} 63 + variant="ghost" 64 + display="flex" 65 + justifyContent="space-between" 66 + alignItems="center" 67 + {...props} 68 + onClick={(e) => { 69 + if (typeof props.onClick === "function") props.onClick(e); 70 + setMenuOpen(false); 71 + }} 72 + /> 73 + ); 57 74 return ( 58 75 <> 59 76 <PostDialog 60 77 openSignal={[dialogOpen, setDialogOpen]} 61 78 account={selectedAccount} 62 79 result={result} 80 + initialAltText={altText} 63 81 /> 64 82 <Menu.Root 83 + open={menuOpen()} 84 + onOpenChange={(e) => setMenuOpen(e.open)} 65 85 positioning={{ placement: "bottom-start", strategy: "fixed" }} 66 86 > 67 87 <Menu.Trigger ··· 72 92 )} 73 93 /> 74 94 <Menu.Positioner> 75 - <Menu.Content> 95 + <Menu.Content py="0"> 76 96 <Menu.ItemGroup> 77 - <Button 78 - color={{ _hover: "colorPalette.emphasized" }} 79 - onClick={() => 97 + <MenuButton 98 + onClick={() => { 80 99 downloadFile( 81 100 result, 82 101 process.file.name ··· 84 103 .slice(0, -1) 85 104 .join(".") 86 105 .concat(".mp4"), 87 - ) 88 - } 89 - variant="ghost" 90 - display="flex" 91 - justifyContent="space-between" 92 - alignItems="center" 106 + ); 107 + toaster.create({ 108 + title: "downloaded result file", 109 + type: "success", 110 + duration: 1000, 111 + }); 112 + }} 93 113 > 94 114 download <DownloadIcon /> 95 - </Button> 96 - <Button 97 - onClick={() => setDialogOpen(!dialogOpen())} 115 + </MenuButton> 116 + <MenuButton 117 + disabled={altText === undefined} 118 + onClick={() => { 119 + navigator.clipboard.writeText(altText!); 120 + toaster.create({ 121 + title: "copied transcribed text to clipboard", 122 + type: "success", 123 + duration: 1000, 124 + }); 125 + }} 126 + > 127 + copy transcription <CaptionsIcon /> 128 + </MenuButton> 129 + <MenuButton 98 130 disabled={selectedAccount === undefined} 99 - color={{ _hover: "colorPalette.emphasized" }} 100 - variant="ghost" 101 - display="flex" 102 - justifyContent="space-between" 103 - alignItems="center" 131 + onClick={() => setDialogOpen(!dialogOpen())} 104 132 > 105 133 post to bsky <SendIcon /> 106 - </Button> 134 + </MenuButton> 107 135 </Menu.ItemGroup> 108 136 </Menu.Content> 109 137 </Menu.Positioner> ··· 123 151 const status = () => { 124 152 switch (process.status) { 125 153 case "success": 126 - return statusSuccess(process.result); 154 + return statusSuccess(process.result, process.altText); 127 155 case "processing": 128 156 return statusProcessing(); 129 157 default:
+39 -2
src/components/PostDialog.tsx
··· 1 1 import { Component, createSignal, Signal } from "solid-js"; 2 2 3 - import { SendIcon, XIcon } from "lucide-solid"; 3 + import { CaptionsIcon, SendIcon, XIcon } from "lucide-solid"; 4 4 import { Stack } from "styled-system/jsx"; 5 5 import { IconButton } from "~/components/ui/icon-button"; 6 6 import { Spinner } from "~/components/ui/spinner"; ··· 14 14 import { Dialog } from "~/components/ui/dialog"; 15 15 import { Textarea } from "~/components/ui/textarea"; 16 16 import { Account } from "~/lib/accounts"; 17 + import { Popover } from "./ui/popover"; 17 18 18 19 const PostDialog = (props: { 19 20 result: Blob; 20 21 account: Account | undefined; 21 22 openSignal: Signal<boolean>; 23 + initialAltText?: string; 22 24 }) => { 23 25 const [postContent, setPostContent] = createSignal<string>(""); 26 + const [altText, setAltText] = createSignal<string>( 27 + props.initialAltText ?? "", 28 + ); 24 29 const [posting, setPosting] = createSignal(false); 25 30 const [open, setOpen] = props.openSignal; 26 31 ··· 82 87 )} 83 88 /> 84 89 )} 90 + <Popover.Root> 91 + <Popover.Trigger 92 + asChild={(triggerProps) => ( 93 + <IconButton 94 + {...triggerProps()} 95 + variant={altText() ? "solid" : "ghost"} 96 + size="sm" 97 + > 98 + <CaptionsIcon /> 99 + </IconButton> 100 + )} 101 + /> 102 + <Popover.Positioner> 103 + <Popover.Content width="sm"> 104 + <Popover.Arrow /> 105 + <Stack gap="2"> 106 + <Popover.Title>video alt text</Popover.Title> 107 + <Textarea 108 + value={altText()} 109 + onInput={(e) => setAltText(e.currentTarget.value)} 110 + placeholder="describe the video content..." 111 + rows={4} 112 + /> 113 + </Stack> 114 + </Popover.Content> 115 + </Popover.Positioner> 116 + </Popover.Root> 85 117 <IconButton 86 118 disabled={posting()} 87 119 onClick={() => { 88 120 setPosting(true); 89 - sendPost(props.account?.did!, props.result, postContent()) 121 + sendPost( 122 + props.account?.did!, 123 + props.result, 124 + postContent(), 125 + altText(), 126 + ) 90 127 .then((result) => { 91 128 const parsedUri = parseCanonicalResourceUri(result.uri); 92 129 if (!parsedUri.ok) throw "failed to parse atproto uri";
+119 -61
src/components/Settings.tsx
··· 1 - import { createSignal, For, Signal } from "solid-js"; 1 + import { Component, createSignal, For, JSXElement, Signal } from "solid-js"; 2 2 3 3 import { 4 4 CheckIcon, ··· 34 34 backgroundColor as backgroundColorSetting, 35 35 frameRate as frameRateSetting, 36 36 useDominantColorAsBg as useDominantColorAsBgSetting, 37 + autoTranscribe as autoTranscribeSetting, 38 + whisperModel as whisperModelSetting, 37 39 Setting, 40 + defaultWhisperModel, 38 41 } from "~/lib/settings"; 39 42 import { handleResolver } from "~/lib/at"; 40 43 import { toaster } from "~/components/Toaster"; ··· 43 46 import { type Color, type ListCollection, parseColor } from "@ark-ui/solid"; 44 47 import { ColorPicker } from "~/components/ui/color-picker"; 45 48 import { Input } from "~/components/ui/input"; 49 + import { preloadModel } from "~/lib/transcribe"; 46 50 47 51 const SettingCheckbox = (props: { 48 52 setting: Setting<boolean>; ··· 191 195 ); 192 196 }; 193 197 198 + const Category = ({ 199 + title, 200 + children, 201 + }: { 202 + title: string; 203 + children: JSXElement; 204 + }) => ( 205 + <Stack> 206 + <FormLabel>{title}</FormLabel> 207 + <Stack 208 + gap="0" 209 + border="1px solid var(--colors-border-default)" 210 + borderBottomWidth="3px" 211 + rounded="xs" 212 + > 213 + {children} 214 + </Stack> 215 + </Stack> 216 + ); 217 + 194 218 const Settings = () => { 195 219 const [handle, setHandle] = createSignal(""); 196 220 const isHandleValid = () => isHandle(handle()); ··· 259 283 </For> 260 284 ); 261 285 return ( 262 - <Stack> 263 - <FormLabel>accounts</FormLabel> 286 + <Category title="accounts"> 264 287 <Stack 265 - border="1px solid var(--colors-border-default)" 266 - borderBottomWidth="3px" 267 - rounded="xs" 288 + borderBottom="1px solid var(--colors-border-default)" 289 + p="2" 290 + marginBottom="2" 291 + direction="row" 292 + gap="2" 293 + w="full" 268 294 > 269 - <Stack 270 - borderBottom="1px solid var(--colors-border-default)" 271 - p="2" 272 - direction="row" 273 - gap="2" 274 - w="full" 275 - > 276 - <Field.Root w="full"> 277 - <Field.Input 278 - placeholder="example.bsky.social" 279 - value={handle()} 280 - onInput={(e) => setHandle(e.currentTarget.value)} 281 - /> 282 - </Field.Root> 283 - <IconButton onClick={startAccountFlow} disabled={!isHandleValid()}> 284 - <PlusIcon /> 285 - </IconButton> 286 - </Stack> 287 - {items(accounts())} 295 + <Field.Root w="full"> 296 + <Field.Input 297 + placeholder="example.bsky.social" 298 + value={handle()} 299 + onInput={(e) => setHandle(e.currentTarget.value)} 300 + /> 301 + </Field.Root> 302 + <IconButton onClick={startAccountFlow} disabled={!isHandleValid()}> 303 + <PlusIcon /> 304 + </IconButton> 288 305 </Stack> 289 - </Stack> 306 + {items(accounts())} 307 + </Category> 290 308 ); 291 309 }; 292 310 ··· 322 340 backgroundColorSetting.set(newColor.toString("rgb")); 323 341 }; 324 342 343 + const whisperModelCollection = createListCollection({ 344 + items: [ 345 + { tag: "tiny", size: "40MB" }, 346 + { tag: "base", size: "80MB" }, 347 + { tag: "small", size: "250MB" }, 348 + ].map((model) => ({ 349 + label: `${model.tag} (${model.size})`, 350 + value: `onnx-community/whisper-${model.tag}`, 351 + })), 352 + }); 353 + const [whisperModel, _setWhisperModel] = createSignal( 354 + (whisperModelSetting.get() ?? defaultWhisperModel).toString(), 355 + ); 356 + const setWhisperModel = (value: string | ((prev: string) => string)) => { 357 + const newModel = _setWhisperModel(value); 358 + whisperModelSetting.set(newModel); 359 + if (autoTranscribe()) setTimeout(() => preloadModel(), 200); 360 + }; 361 + const [autoTranscribe, setAutoTranscribe] = createSignal( 362 + autoTranscribeSetting.get() ?? false, 363 + ); 364 + 325 365 return ( 326 366 <Drawer.Root> 327 367 <Drawer.Trigger ··· 351 391 <Drawer.Body> 352 392 <Stack gap="4"> 353 393 <Accounts /> 354 - <Stack> 355 - <FormLabel>processing</FormLabel> 356 - <Stack 357 - gap="0" 358 - border="1px solid var(--colors-border-default)" 359 - borderBottomWidth="3px" 360 - rounded="xs" 361 - > 362 - <Box borderBottom="1px solid var(--colors-border-subtle)"> 363 - <SettingCheckbox 364 - label="show profile picture" 365 - setting={showProfilePictureSetting} 366 - signal={[showProfilePicture, setShowProfilePicture]} 367 - /> 368 - </Box> 394 + <Category title="video processing"> 395 + <Box borderBottom="1px solid var(--colors-border-subtle)"> 369 396 <SettingCheckbox 370 - label="show visualizer" 371 - setting={showVisualizerSetting} 372 - signal={[showVisualizer, setShowVisualizer]} 397 + label="show profile picture" 398 + setting={showProfilePictureSetting} 399 + signal={[showProfilePicture, setShowProfilePicture]} 373 400 /> 374 - <Stack gap="0" borderY="1px solid var(--colors-border-muted)"> 375 - <SettingCheckbox 376 - label="use dominant color as bg" 377 - setting={useDominantColorAsBgSetting} 378 - signal={[useDominantColorAsBg, setUseDominantColorAsBg]} 379 - disabled={!showProfilePicture()} 380 - /> 381 - <SettingColorPicker 382 - label="background color" 383 - signal={[backgroundColor, setBackgroundColor]} 384 - /> 385 - </Stack> 386 - <SettingSelect 387 - label="frame rate" 388 - signal={[frameRate, setFrameRate]} 389 - collection={frameRateCollection} 401 + </Box> 402 + <SettingCheckbox 403 + label="show visualizer" 404 + setting={showVisualizerSetting} 405 + signal={[showVisualizer, setShowVisualizer]} 406 + /> 407 + <Stack gap="0" borderY="1px solid var(--colors-border-muted)"> 408 + <SettingCheckbox 409 + label="use dominant color as bg" 410 + setting={useDominantColorAsBgSetting} 411 + signal={[useDominantColorAsBg, setUseDominantColorAsBg]} 412 + disabled={!showProfilePicture()} 413 + /> 414 + <SettingColorPicker 415 + label="background color" 416 + signal={[backgroundColor, setBackgroundColor]} 390 417 /> 391 418 </Stack> 392 - </Stack> 419 + <SettingSelect 420 + label="frame rate" 421 + signal={[frameRate, setFrameRate]} 422 + collection={frameRateCollection} 423 + /> 424 + </Category> 425 + <Category title="audio transcription"> 426 + <Box borderBottom="1px solid var(--colors-border-subtle)"> 427 + <SettingCheckbox 428 + label="transcribe audio" 429 + setting={autoTranscribeSetting} 430 + signal={[ 431 + autoTranscribe, 432 + (val) => { 433 + const newVal = setAutoTranscribe(val); 434 + if (newVal) preloadModel(); 435 + return val; 436 + }, 437 + ]} 438 + /> 439 + </Box> 440 + <Box borderBottom="1px solid var(--colors-border-subtle)"> 441 + <SettingSelect 442 + label="whisper model" 443 + signal={[whisperModel, setWhisperModel]} 444 + collection={whisperModelCollection} 445 + /> 446 + </Box> 447 + <Text color="fg.subtle" p="2" fontSize="sm" fontWeight="normal"> 448 + note: the model will only be downloaded once. 449 + </Text> 450 + </Category> 393 451 </Stack> 394 452 </Drawer.Body> 395 453 <Drawer.Footer p="2" gap="3">
+4
src/index.tsx
··· 12 12 import { accounts, setAccounts } from "./lib/accounts"; 13 13 import { AtprotoDid } from "@atcute/lexicons/syntax"; 14 14 import { toaster } from "./components/Toaster"; 15 + import { autoTranscribe } from "./lib/settings"; 16 + import { preloadModel } from "./lib/transcribe"; 15 17 16 18 const root = document.getElementById("root"); 17 19 ··· 52 54 type: "error", 53 55 }); 54 56 }); 57 + 58 + if (autoTranscribe.get()) preloadModel(); 55 59 56 60 render(() => <App />, root!);
+2
src/lib/at.ts
··· 35 35 did: AtprotoDid, 36 36 blob: Blob, 37 37 postContent: string, 38 + altText?: string, 38 39 ) => { 39 40 const login = await getSessionClient(did); 40 41 const upload = await login.client.post("com.atproto.repo.uploadBlob", { ··· 47 48 embed: { 48 49 $type: "app.bsky.embed.video", 49 50 video: upload.data.blob, 51 + alt: altText, 50 52 }, 51 53 createdAt: new Date().toISOString(), 52 54 };
+4
src/lib/settings.ts
··· 17 17 export const useDominantColorAsBg = setting<boolean>("useDominantColorAsBg"); 18 18 export const backgroundColor = setting<string>("backgroundColor"); 19 19 export const frameRate = setting<number>("frameRate"); 20 + 21 + export const autoTranscribe = setting<boolean>("autoTranscribe"); 22 + export const whisperModel = setting<string>("whisperModel"); 23 + export const defaultWhisperModel = "onnx-community/whisper-tiny";
+16 -8
src/lib/task.ts
··· 6 6 showProfilePicture, 7 7 showVisualizer, 8 8 useDominantColorAsBg, 9 + autoTranscribe, 9 10 } from "./settings"; 10 11 import { getSessionClient } from "./oauth"; 11 12 import { is } from "@atcute/lexicons"; ··· 15 16 import { FastAverageColor } from "fast-average-color"; 16 17 import { toaster } from "~/components/Toaster"; 17 18 import { parseColor } from "@ark-ui/solid"; 19 + import { transcribe } from "./transcribe"; 18 20 19 21 export type TaskState = { file: File } & ( 20 22 | { status: "processing" } 21 23 | { status: "error"; error: string } 22 - | { status: "success"; result: Blob } 24 + | { status: "success"; result: Blob; altText?: string } 23 25 ); 24 26 25 27 let _idCounter = 0; ··· 80 82 }); 81 83 } 82 84 } 83 - const result = await render(file, { 84 - pfpUrl, 85 - visualizer: showVisualizer.get() ?? true, 86 - frameRate: frameRate.get() ?? 30, 87 - bgColor, 88 - duration, 89 - }); 85 + const [result, altText] = await Promise.all([ 86 + render(file, { 87 + pfpUrl, 88 + visualizer: showVisualizer.get() ?? true, 89 + frameRate: frameRate.get() ?? 30, 90 + bgColor, 91 + duration, 92 + }), 93 + (autoTranscribe.get() ?? false) 94 + ? transcribe(file) 95 + : Promise.resolve(undefined), 96 + ]); 90 97 tasks.set(id, { 91 98 file, 92 99 status: "success", 93 100 result, 101 + altText, 94 102 }); 95 103 } catch (error) { 96 104 console.error(error);
+101
src/lib/transcribe.ts
··· 1 + import { 2 + AutomaticSpeechRecognitionPipeline, 3 + pipeline, 4 + } from "@huggingface/transformers"; 5 + import { toaster } from "~/components/Toaster"; 6 + import { defaultWhisperModel, whisperModel } from "./settings"; 7 + 8 + let transcriberPromise: Promise<AutomaticSpeechRecognitionPipeline> | null = 9 + null; 10 + let model: AutomaticSpeechRecognitionPipeline | null = null; 11 + 12 + const loadModel = () => { 13 + if (model) return Promise.resolve(model); 14 + 15 + if (transcriberPromise) return transcriberPromise; 16 + 17 + let toastId: string | undefined; 18 + 19 + const modelName = whisperModel.get() ?? defaultWhisperModel; 20 + 21 + transcriberPromise = pipeline("automatic-speech-recognition", modelName, { 22 + progress_callback: (data: any) => { 23 + // data contains: { status, file, name, loaded, total, progress } 24 + if (data.status === "initiate") { 25 + if (!toastId) { 26 + toastId = toaster.create({ 27 + title: "downloading transcription model", 28 + description: `fetching ${data.file}...`, 29 + type: "info", 30 + duration: 999999, 31 + }); 32 + } 33 + } else if (data.status === "progress" && toastId) { 34 + const percent = data.progress ? Math.round(data.progress) : 0; 35 + toaster.update(toastId, { 36 + title: "downloading transcription model", 37 + description: `fetching ${data.file} (at ${percent}%)...`, 38 + type: "info", 39 + duration: 999999, 40 + }); 41 + } 42 + }, 43 + }) 44 + .then((transcriber) => { 45 + if (toastId) { 46 + toaster.update(toastId, { 47 + title: "transcription model loaded", 48 + description: `${modelName.split("/")[1]} is ready`, 49 + type: "success", 50 + duration: 3000, 51 + }); 52 + } 53 + model = transcriber; 54 + return transcriber; 55 + }) 56 + .catch((err) => { 57 + const toastOpts = { 58 + title: "transcription model download failed", 59 + description: `${err}`, 60 + type: "error", 61 + duration: 5000, 62 + }; 63 + if (toastId) toaster.update(toastId, toastOpts); 64 + else toaster.create(toastOpts); 65 + 66 + model = null; 67 + 68 + throw err; 69 + }) 70 + .finally(() => { 71 + transcriberPromise = null; 72 + }); 73 + 74 + return transcriberPromise; 75 + }; 76 + 77 + export const preloadModel = () => { 78 + model = null; 79 + loadModel().catch((e) => console.error("preload failed", e)); 80 + }; 81 + 82 + export const transcribe = async (file: File): Promise<string> => { 83 + const url = URL.createObjectURL(file); 84 + try { 85 + await loadModel(); 86 + if (!model) throw "model not loaded"; 87 + 88 + const output = await model(url); 89 + return [output].flat()[0].text.trim(); 90 + } catch (err) { 91 + console.error("transcription failed", err); 92 + toaster.create({ 93 + title: "transcription failed", 94 + description: `${err}`, 95 + type: "error", 96 + }); 97 + throw err; 98 + } finally { 99 + URL.revokeObjectURL(url); 100 + } 101 + };