[READ-ONLY] Mirror of https://github.com/flo-bit/youtube-party-dj. democratic youtube player, everyone can vote and add songs, uni project, made with uix
0

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request #9 from flo-bit/basic-ui

basic ui

author
Florian
committer
GitHub
date (May 29, 2024, 6:01 PM +0200) commit 321938a3 parent aa4b3ca1
+797 -97
+2 -2
backend/entrypoint.ts
··· 9 9 10 10 export default { 11 11 // show backend (hybrid) rendered page on /backend 12 - '/backend': import("../common/page.tsx"), 12 + //'/backend': import("../common/page.tsx"), 13 13 14 14 // redirect / to /backend 15 - '/': provideRedirect("/backend") 15 + //'/': provideRedirect("/backend") 16 16 }
+18
common/client.tsx
··· 1 + import AddVideoOverlay from "common/components/AddVideoOverlay.tsx"; 2 + import Queue from "./components/Queue.tsx"; 3 + import QRCodeOverlay from "common/components/QRCodeOverlay.tsx"; 4 + 5 + export default function App() { 6 + return ( 7 + <main class="w-screen h-screen relative"> 8 + <div class="mx-auto max-w-2xl"> 9 + <div class="px-4 py-4 overflow-y-scroll h-screen"> 10 + <Queue /> 11 + </div> 12 + 13 + <QRCodeOverlay /> 14 + 15 + <AddVideoOverlay /> 16 + </div> 17 + </main>); 18 + }
+49
common/components/AddVideoOverlay.tsx
··· 1 + import Queue from "common/components/Queue.tsx"; 2 + 3 + export default function AddVideoOverlay() { 4 + const showOverlay = $$(false); 5 + 6 + return ( 7 + <div> 8 + <button onclick={() => { 9 + showOverlay.val = true; 10 + }} class="absolute bottom-2 right-2 w-20 h-20 bg-white/5 border border-white/10 rounded-full flex items-center justify-center backdrop-blur-md"> 11 + 12 + <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" class="size-16 stroke-white"> 13 + <path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" /> 14 + </svg> 15 + </button> 16 + 17 + <div style={{display:showOverlay}} class="absolute inset-0 z-20 bg-black"> 18 + <button onclick={() => { 19 + showOverlay.val = false; 20 + }} class="absolute bottom-2 right-2 w-20 h-20 bg-white/5 border border-white/10 rounded-full flex items-center justify-center backdrop-blur-md"> 21 + 22 + <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" class="size-14 stroke-white"> 23 + <path stroke-linecap="round" stroke-linejoin="round" d="M3.75 12h16.5m-16.5 3.75h16.5M3.75 19.5h16.5M5.625 4.5h12.75a1.875 1.875 0 0 1 0 3.75H5.625a1.875 1.875 0 0 1 0-3.75Z" /> 24 + </svg> 25 + </button> 26 + 27 + 28 + <div 29 + class="flex flex-col overflow-y-hidden h-screen rounded-xl mx-auto max-w-2xl" 30 + > 31 + <div class="flex px-4 my-4 "> 32 + <div class="relative flex items-center w-full"> 33 + <input type="text" name="search" id="search" class="block w-full font-semibold rounded-xl border-0 outline-none py-4 pr-14 text-white bg-white/5 shadow-sm ring-1 ring-inset ring-white/10 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-red-600 text-xl sm:leading-6" /> 34 + <div class="absolute inset-y-0 right-0 flex py-1.5 pr-1.5"> 35 + <button class="inline-flex items-center px-1 stroke-white"> 36 + <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" class="size-8"> 37 + <path stroke-linecap="round" stroke-linejoin="round" d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z" /> 38 + </svg> 39 + </button> 40 + </div> 41 + </div> 42 + </div> 43 + <div class="px-4 py-4 border-t border-white/20 mx-0 overflow-y-scroll flex-grow"> 44 + <Queue /> 45 + </div> 46 + </div> 47 + </div> 48 + </div>); 49 + }
+9
common/components/QR.tsx
··· 1 + 2 + export default function QRCode() { 3 + return ( 4 + <div class="aspect-square w-3/4 md:w-full bg-white/5 border border-white/10 rounded-xl z-10"> 5 + 6 + <div class="w-full h-full flex items-center justify-center text-white">QR CODE</div> 7 + 8 + </div>); 9 + }
+33
common/components/QRCodeOverlay.tsx
··· 1 + import QRCode from "./QR.tsx"; 2 + 3 + export default function QRCodeOverlay() { 4 + const showQR = $$(false); 5 + 6 + return ( 7 + <div> 8 + <button onclick={() => { 9 + showQR.val = true; 10 + }} class="absolute md:hidden bottom-2 left-2 w-20 h-20 bg-white/5 border border-white/10 rounded-xl flex items-center justify-center backdrop-blur-md"> 11 + <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="size-18 stroke-white"> 12 + <path stroke-linecap="round" stroke-linejoin="round" d="M3.75 4.875c0-.621.504-1.125 1.125-1.125h4.5c.621 0 1.125.504 1.125 1.125v4.5c0 .621-.504 1.125-1.125 1.125h-4.5A1.125 1.125 0 0 1 3.75 9.375v-4.5ZM3.75 14.625c0-.621.504-1.125 1.125-1.125h4.5c.621 0 1.125.504 1.125 1.125v4.5c0 .621-.504 1.125-1.125 1.125h-4.5a1.125 1.125 0 0 1-1.125-1.125v-4.5ZM13.5 4.875c0-.621.504-1.125 1.125-1.125h4.5c.621 0 1.125.504 1.125 1.125v4.5c0 .621-.504 1.125-1.125 1.125h-4.5A1.125 1.125 0 0 1 13.5 9.375v-4.5Z" /> 13 + <path stroke-linecap="round" stroke-linejoin="round" d="M6.75 6.75h.75v.75h-.75v-.75ZM6.75 16.5h.75v.75h-.75v-.75ZM16.5 6.75h.75v.75h-.75v-.75ZM13.5 13.5h.75v.75h-.75v-.75ZM13.5 19.5h.75v.75h-.75v-.75ZM19.5 13.5h.75v.75h-.75v-.75ZM19.5 19.5h.75v.75h-.75v-.75ZM16.5 16.5h.75v.75h-.75v-.75Z" /> 14 + </svg> 15 + </button> 16 + 17 + <div style={{display:showQR}} class="absolute inset-0 flex md:hidden z-20 bg-white/5 rounded-xl border border-white/10 backdrop-blur-md items-center justify-center"> 18 + <button class="absolute inset-0 h-full w-full" onclick={() => { 19 + showQR.val = false; 20 + }}></button> 21 + 22 + <QRCode /> 23 + 24 + <button class="absolute top-2 right-2 stroke-white" onclick={() => { 25 + showQR.val = false; 26 + }}> 27 + <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" class="size-8"> 28 + <path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" /> 29 + </svg> 30 + </button> 31 + </div> 32 + </div>); 33 + }
+8
common/components/Queue.tsx
··· 1 + import QueueItem from "./QueueItem.tsx"; 2 + 3 + export default function Queue() { 4 + return ( 5 + <div class="space-y-4"> 6 + {[...Array(10)].map((_, i) => <QueueItem key='1' />)} 7 + </div>); 8 + }
+6
common/components/QueueItem.tsx
··· 1 + export default function QueueItem() { 2 + return ( 3 + <div class="w-full rounded-xl bg-white/5 border border-white/10 h-20"> 4 + <div class="text-white flex items-center justify-center h-full">VIDEO ITEM</div> 5 + </div>); 6 + }
+9
common/components/VideoPlayer.tsx
··· 1 + export default function VideoPlayer() { 2 + 3 + return (<div class="relative aspect-video bg-white/5 border border-white/10 w-full overflow-hidden object-cover rounded-xl"> 4 + 5 + <div class="w-full h-full flex items-center justify-center text-white"> CURRENT YOUTUBE VIDEO </div> 6 + 7 + </div>); 8 + 9 + }
+28 -15
common/page.tsx
··· 1 - import { UIX } from "uix"; 2 - import { datexVersion, denoVersion, uixVersion } from "../backend/data.ts"; 1 + import QRCode from "./components/QR.tsx"; 2 + import VideoPlayer from "./components/VideoPlayer.tsx"; 3 + import Queue from "./components/Queue.tsx"; 4 + import QRCodeOverlay from "./components/QRCodeOverlay.tsx"; 5 + 6 + export default function App() { 7 + return ( 8 + <main class="w-screen h-screen relative"> 9 + <div class="mx-auto grid md:grid-cols-2 h-screen"> 10 + <div class="h-screen hidden md:flex items-center flex-col justify-center p-8"> 11 + <QRCode /> 12 + 13 + <div class="text-white text-3xl font-semibold mt-4">Party code: XXXX</div> 14 + </div> 15 + <div 16 + class="flex flex-col overflow-y-hidden h-screen bg-white/5 border border-white/10 rounded-xl" 17 + > 18 + <div class="flex px-8 mx-0 my-8 "> 19 + <VideoPlayer /> 20 + </div> 21 + <div class="px-4 py-4 border-t border-white/20 mx-0 overflow-y-scroll flex-grow"> 22 + <Queue /> 23 + </div> 24 + </div> 25 + </div> 3 26 4 - export default 5 - <main class="flex items-center justify-center h-screen"> 6 - <section> 7 - <h1 class="text-xl font-bold text-red-500">Youtube Party DJ</h1> 8 - <p>This page was rendered on the <b>{UIX.context}</b>!</p> 9 - <ul> 10 - <li><b>UIX version:</b> {uixVersion}</li> 11 - <li><b>DATEX version:</b> {datexVersion}</li> 12 - <li><b>Deno version:</b> {denoVersion}</li> 13 - </ul> 14 - <p>To get started, take a look at our <a href="https://docs.unyt.org/manual/uix/getting-started" target="_blank" class="font-bold underline">documentation</a>.</p> 15 - </section> 16 - </main>; 27 + <QRCodeOverlay /> 28 + </main>); 29 + }
+566 -7
frontend/entrypoint.css
··· 446 446 display: none; 447 447 } 448 448 449 + [type='text'],input:where(:not([type])),[type='email'],[type='url'],[type='password'],[type='number'],[type='date'],[type='datetime-local'],[type='month'],[type='search'],[type='tel'],[type='time'],[type='week'],[multiple],textarea,select { 450 + -webkit-appearance: none; 451 + -moz-appearance: none; 452 + appearance: none; 453 + background-color: #fff; 454 + border-color: #6b7280; 455 + border-width: 1px; 456 + border-radius: 0px; 457 + padding-top: 0.5rem; 458 + padding-right: 0.75rem; 459 + padding-bottom: 0.5rem; 460 + padding-left: 0.75rem; 461 + font-size: 1rem; 462 + line-height: 1.5rem; 463 + --tw-shadow: 0 0 #0000; 464 + } 465 + 466 + [type='text']:focus, input:where(:not([type])):focus, [type='email']:focus, [type='url']:focus, [type='password']:focus, [type='number']:focus, [type='date']:focus, [type='datetime-local']:focus, [type='month']:focus, [type='search']:focus, [type='tel']:focus, [type='time']:focus, [type='week']:focus, [multiple]:focus, textarea:focus, select:focus { 467 + outline: 2px solid transparent; 468 + outline-offset: 2px; 469 + --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/); 470 + --tw-ring-offset-width: 0px; 471 + --tw-ring-offset-color: #fff; 472 + --tw-ring-color: #2563eb; 473 + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); 474 + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); 475 + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); 476 + border-color: #2563eb; 477 + } 478 + 479 + input::-moz-placeholder, textarea::-moz-placeholder { 480 + color: #6b7280; 481 + opacity: 1; 482 + } 483 + 484 + input::placeholder,textarea::placeholder { 485 + color: #6b7280; 486 + opacity: 1; 487 + } 488 + 489 + ::-webkit-datetime-edit-fields-wrapper { 490 + padding: 0; 491 + } 492 + 493 + ::-webkit-date-and-time-value { 494 + min-height: 1.5em; 495 + text-align: inherit; 496 + } 497 + 498 + ::-webkit-datetime-edit { 499 + display: inline-flex; 500 + } 501 + 502 + ::-webkit-datetime-edit,::-webkit-datetime-edit-year-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-meridiem-field { 503 + padding-top: 0; 504 + padding-bottom: 0; 505 + } 506 + 507 + select { 508 + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e"); 509 + background-position: right 0.5rem center; 510 + background-repeat: no-repeat; 511 + background-size: 1.5em 1.5em; 512 + padding-right: 2.5rem; 513 + -webkit-print-color-adjust: exact; 514 + print-color-adjust: exact; 515 + } 516 + 517 + [multiple],[size]:where(select:not([size="1"])) { 518 + background-image: initial; 519 + background-position: initial; 520 + background-repeat: unset; 521 + background-size: initial; 522 + padding-right: 0.75rem; 523 + -webkit-print-color-adjust: unset; 524 + print-color-adjust: unset; 525 + } 526 + 527 + [type='checkbox'],[type='radio'] { 528 + -webkit-appearance: none; 529 + -moz-appearance: none; 530 + appearance: none; 531 + padding: 0; 532 + -webkit-print-color-adjust: exact; 533 + print-color-adjust: exact; 534 + display: inline-block; 535 + vertical-align: middle; 536 + background-origin: border-box; 537 + -webkit-user-select: none; 538 + -moz-user-select: none; 539 + user-select: none; 540 + flex-shrink: 0; 541 + height: 1rem; 542 + width: 1rem; 543 + color: #2563eb; 544 + background-color: #fff; 545 + border-color: #6b7280; 546 + border-width: 1px; 547 + --tw-shadow: 0 0 #0000; 548 + } 549 + 550 + [type='checkbox'] { 551 + border-radius: 0px; 552 + } 553 + 554 + [type='radio'] { 555 + border-radius: 100%; 556 + } 557 + 558 + [type='checkbox']:focus,[type='radio']:focus { 559 + outline: 2px solid transparent; 560 + outline-offset: 2px; 561 + --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/); 562 + --tw-ring-offset-width: 2px; 563 + --tw-ring-offset-color: #fff; 564 + --tw-ring-color: #2563eb; 565 + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); 566 + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); 567 + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); 568 + } 569 + 570 + [type='checkbox']:checked,[type='radio']:checked { 571 + border-color: transparent; 572 + background-color: currentColor; 573 + background-size: 100% 100%; 574 + background-position: center; 575 + background-repeat: no-repeat; 576 + } 577 + 578 + [type='checkbox']:checked { 579 + background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e"); 580 + } 581 + 582 + @media (forced-colors: active) { 583 + [type='checkbox']:checked { 584 + -webkit-appearance: auto; 585 + -moz-appearance: auto; 586 + appearance: auto; 587 + } 588 + } 589 + 590 + [type='radio']:checked { 591 + background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e"); 592 + } 593 + 594 + @media (forced-colors: active) { 595 + [type='radio']:checked { 596 + -webkit-appearance: auto; 597 + -moz-appearance: auto; 598 + appearance: auto; 599 + } 600 + } 601 + 602 + [type='checkbox']:checked:hover,[type='checkbox']:checked:focus,[type='radio']:checked:hover,[type='radio']:checked:focus { 603 + border-color: transparent; 604 + background-color: currentColor; 605 + } 606 + 607 + [type='checkbox']:indeterminate { 608 + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e"); 609 + border-color: transparent; 610 + background-color: currentColor; 611 + background-size: 100% 100%; 612 + background-position: center; 613 + background-repeat: no-repeat; 614 + } 615 + 616 + @media (forced-colors: active) { 617 + [type='checkbox']:indeterminate { 618 + -webkit-appearance: auto; 619 + -moz-appearance: auto; 620 + appearance: auto; 621 + } 622 + } 623 + 624 + [type='checkbox']:indeterminate:hover,[type='checkbox']:indeterminate:focus { 625 + border-color: transparent; 626 + background-color: currentColor; 627 + } 628 + 629 + [type='file'] { 630 + background: unset; 631 + border-color: inherit; 632 + border-width: 0; 633 + border-radius: 0; 634 + padding: 0; 635 + font-size: unset; 636 + line-height: inherit; 637 + } 638 + 639 + [type='file']:focus { 640 + outline: 1px solid ButtonText; 641 + outline: 1px auto -webkit-focus-ring-color; 642 + } 643 + 449 644 *, ::before, ::after { 450 645 --tw-border-spacing-x: 0; 451 646 --tw-border-spacing-y: 0; ··· 554 749 --tw-contain-style: ; 555 750 } 556 751 752 + .absolute { 753 + position: absolute; 754 + } 755 + 756 + .relative { 757 + position: relative; 758 + } 759 + 760 + .inset-0 { 761 + inset: 0px; 762 + } 763 + 764 + .inset-y-0 { 765 + top: 0px; 766 + bottom: 0px; 767 + } 768 + 769 + .bottom-2 { 770 + bottom: 0.5rem; 771 + } 772 + 773 + .left-2 { 774 + left: 0.5rem; 775 + } 776 + 777 + .right-0 { 778 + right: 0px; 779 + } 780 + 781 + .right-2 { 782 + right: 0.5rem; 783 + } 784 + 785 + .top-2 { 786 + top: 0.5rem; 787 + } 788 + 789 + .z-10 { 790 + z-index: 10; 791 + } 792 + 793 + .z-20 { 794 + z-index: 20; 795 + } 796 + 797 + .mx-0 { 798 + margin-left: 0px; 799 + margin-right: 0px; 800 + } 801 + 802 + .mx-auto { 803 + margin-left: auto; 804 + margin-right: auto; 805 + } 806 + 807 + .my-4 { 808 + margin-top: 1rem; 809 + margin-bottom: 1rem; 810 + } 811 + 812 + .my-8 { 813 + margin-top: 2rem; 814 + margin-bottom: 2rem; 815 + } 816 + 817 + .mt-4 { 818 + margin-top: 1rem; 819 + } 820 + 821 + .block { 822 + display: block; 823 + } 824 + 557 825 .flex { 558 826 display: flex; 559 827 } 560 828 829 + .inline-flex { 830 + display: inline-flex; 831 + } 832 + 833 + .grid { 834 + display: grid; 835 + } 836 + 837 + .hidden { 838 + display: none; 839 + } 840 + 841 + .aspect-square { 842 + aspect-ratio: 1 / 1; 843 + } 844 + 845 + .aspect-video { 846 + aspect-ratio: 16 / 9; 847 + } 848 + 849 + .size-14 { 850 + width: 3.5rem; 851 + height: 3.5rem; 852 + } 853 + 854 + .size-16 { 855 + width: 4rem; 856 + height: 4rem; 857 + } 858 + 859 + .size-6 { 860 + width: 1.5rem; 861 + height: 1.5rem; 862 + } 863 + 864 + .size-8 { 865 + width: 2rem; 866 + height: 2rem; 867 + } 868 + 869 + .size-10 { 870 + width: 2.5rem; 871 + height: 2.5rem; 872 + } 873 + 874 + .h-20 { 875 + height: 5rem; 876 + } 877 + 878 + .h-full { 879 + height: 100%; 880 + } 881 + 561 882 .h-screen { 562 883 height: 100vh; 563 884 } 564 885 886 + .w-20 { 887 + width: 5rem; 888 + } 889 + 890 + .w-3\/4 { 891 + width: 75%; 892 + } 893 + 894 + .w-full { 895 + width: 100%; 896 + } 897 + 898 + .w-screen { 899 + width: 100vw; 900 + } 901 + 902 + .max-w-2xl { 903 + max-width: 42rem; 904 + } 905 + 906 + .flex-grow { 907 + flex-grow: 1; 908 + } 909 + 910 + .flex-col { 911 + flex-direction: column; 912 + } 913 + 565 914 .items-center { 566 915 align-items: center; 567 916 } ··· 570 919 justify-content: center; 571 920 } 572 921 922 + .space-y-4 > :not([hidden]) ~ :not([hidden]) { 923 + --tw-space-y-reverse: 0; 924 + margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse))); 925 + margin-bottom: calc(1rem * var(--tw-space-y-reverse)); 926 + } 927 + 928 + .overflow-hidden { 929 + overflow: hidden; 930 + } 931 + 932 + .overflow-y-hidden { 933 + overflow-y: hidden; 934 + } 935 + 936 + .overflow-y-scroll { 937 + overflow-y: scroll; 938 + } 939 + 940 + .rounded-full { 941 + border-radius: 9999px; 942 + } 943 + 944 + .rounded-md { 945 + border-radius: 0.375rem; 946 + } 947 + 948 + .rounded-xl { 949 + border-radius: 0.75rem; 950 + } 951 + 952 + .border { 953 + border-width: 1px; 954 + } 955 + 956 + .border-0 { 957 + border-width: 0px; 958 + } 959 + 960 + .border-t { 961 + border-top-width: 1px; 962 + } 963 + 964 + .border-white\/10 { 965 + border-color: rgb(255 255 255 / 0.1); 966 + } 967 + 968 + .border-white\/20 { 969 + border-color: rgb(255 255 255 / 0.2); 970 + } 971 + 972 + .bg-black { 973 + --tw-bg-opacity: 1; 974 + background-color: rgb(0 0 0 / var(--tw-bg-opacity)); 975 + } 976 + 977 + .bg-white\/5 { 978 + background-color: rgb(255 255 255 / 0.05); 979 + } 980 + 981 + .stroke-white { 982 + stroke: #fff; 983 + } 984 + 985 + .object-cover { 986 + -o-object-fit: cover; 987 + object-fit: cover; 988 + } 989 + 990 + .p-8 { 991 + padding: 2rem; 992 + } 993 + 994 + .px-1 { 995 + padding-left: 0.25rem; 996 + padding-right: 0.25rem; 997 + } 998 + 999 + .px-4 { 1000 + padding-left: 1rem; 1001 + padding-right: 1rem; 1002 + } 1003 + 1004 + .px-8 { 1005 + padding-left: 2rem; 1006 + padding-right: 2rem; 1007 + } 1008 + 1009 + .py-1 { 1010 + padding-top: 0.25rem; 1011 + padding-bottom: 0.25rem; 1012 + } 1013 + 1014 + .py-1\.5 { 1015 + padding-top: 0.375rem; 1016 + padding-bottom: 0.375rem; 1017 + } 1018 + 1019 + .py-4 { 1020 + padding-top: 1rem; 1021 + padding-bottom: 1rem; 1022 + } 1023 + 1024 + .py-2 { 1025 + padding-top: 0.5rem; 1026 + padding-bottom: 0.5rem; 1027 + } 1028 + 1029 + .py-2\.5 { 1030 + padding-top: 0.625rem; 1031 + padding-bottom: 0.625rem; 1032 + } 1033 + 1034 + .pr-1 { 1035 + padding-right: 0.25rem; 1036 + } 1037 + 1038 + .pr-1\.5 { 1039 + padding-right: 0.375rem; 1040 + } 1041 + 1042 + .pr-14 { 1043 + padding-right: 3.5rem; 1044 + } 1045 + 1046 + .text-3xl { 1047 + font-size: 1.875rem; 1048 + line-height: 2.25rem; 1049 + } 1050 + 573 1051 .text-xl { 574 1052 font-size: 1.25rem; 575 1053 line-height: 1.75rem; 576 1054 } 577 1055 578 - .font-bold { 579 - font-weight: 700; 1056 + .font-semibold { 1057 + font-weight: 600; 580 1058 } 581 1059 582 - .text-red-500 { 1060 + .text-white { 583 1061 --tw-text-opacity: 1; 584 - color: rgb(239 68 68 / var(--tw-text-opacity)); 1062 + color: rgb(255 255 255 / var(--tw-text-opacity)); 1063 + } 1064 + 1065 + .shadow-sm { 1066 + --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); 1067 + --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color); 1068 + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); 1069 + } 1070 + 1071 + .outline-none { 1072 + outline: 2px solid transparent; 1073 + outline-offset: 2px; 1074 + } 1075 + 1076 + .ring-1 { 1077 + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); 1078 + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); 1079 + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); 1080 + } 1081 + 1082 + .ring-inset { 1083 + --tw-ring-inset: inset; 585 1084 } 586 1085 587 - .underline { 588 - text-decoration-line: underline; 1086 + .ring-white\/10 { 1087 + --tw-ring-color: rgb(255 255 255 / 0.1); 1088 + } 1089 + 1090 + .backdrop-blur-md { 1091 + --tw-backdrop-blur: blur(12px); 1092 + -webkit-backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); 1093 + backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); 589 1094 } 590 1095 591 1096 body { 592 - background-color: white; 1097 + background-color: black; 1098 + } 1099 + 1100 + .placeholder\:text-gray-400::-moz-placeholder { 1101 + --tw-text-opacity: 1; 1102 + color: rgb(156 163 175 / var(--tw-text-opacity)); 1103 + } 1104 + 1105 + .placeholder\:text-gray-400::placeholder { 1106 + --tw-text-opacity: 1; 1107 + color: rgb(156 163 175 / var(--tw-text-opacity)); 1108 + } 1109 + 1110 + .focus\:ring-2:focus { 1111 + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); 1112 + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); 1113 + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); 1114 + } 1115 + 1116 + .focus\:ring-inset:focus { 1117 + --tw-ring-inset: inset; 1118 + } 1119 + 1120 + .focus\:ring-red-600:focus { 1121 + --tw-ring-opacity: 1; 1122 + --tw-ring-color: rgb(220 38 38 / var(--tw-ring-opacity)); 1123 + } 1124 + 1125 + @media (min-width: 640px) { 1126 + .sm\:text-sm { 1127 + font-size: 0.875rem; 1128 + line-height: 1.25rem; 1129 + } 1130 + 1131 + .sm\:leading-6 { 1132 + line-height: 1.5rem; 1133 + } 1134 + } 1135 + 1136 + @media (min-width: 768px) { 1137 + .md\:flex { 1138 + display: flex; 1139 + } 1140 + 1141 + .md\:hidden { 1142 + display: none; 1143 + } 1144 + 1145 + .md\:w-full { 1146 + width: 100%; 1147 + } 1148 + 1149 + .md\:grid-cols-2 { 1150 + grid-template-columns: repeat(2, minmax(0, 1fr)); 1151 + } 593 1152 }
+5 -2
frontend/entrypoint.ts
··· 4 4 * when a page is visited 5 5 */ 6 6 import "../common/theme.tsx" 7 + import { provideRedirect } from "uix/html/entrypoint-providers.tsx" 7 8 8 9 export default { 9 - // show frontend-rendered page on /frontend 10 - '/frontend': import("../common/page.tsx") 10 + '/player': import("../common/page.tsx"), 11 + '/client': import("../common/client.tsx"), 12 + 13 + '/': provideRedirect("/player") 11 14 }
+1 -1
frontend/main.css
··· 3 3 @tailwind utilities; 4 4 5 5 body { 6 - background-color: white; 6 + background-color: black; 7 7 }
+59 -67
package-lock.json
··· 5 5 "packages": { 6 6 "": { 7 7 "devDependencies": { 8 + "@tailwindcss/forms": "^0.5.7", 8 9 "autoprefixer": "^10.4.19", 9 10 "concurrently": "^8.2.2", 10 11 "postcss": "^8.4.38", ··· 24 25 } 25 26 }, 26 27 "node_modules/@babel/runtime": { 27 - "version": "7.24.5", 28 - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.5.tgz", 29 - "integrity": "sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==", 28 + "version": "7.24.6", 29 + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.6.tgz", 30 + "integrity": "sha512-Ja18XcETdEl5mzzACGd+DKgaGJzPTCow7EglgwTmHdwokzDFYh/MHua6lU6DV/hjF2IaOJ4oX2nqnjG7RElKOw==", 30 31 "dev": true, 31 32 "dependencies": { 32 33 "regenerator-runtime": "^0.14.0" ··· 145 146 "node": ">=14" 146 147 } 147 148 }, 149 + "node_modules/@tailwindcss/forms": { 150 + "version": "0.5.7", 151 + "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.7.tgz", 152 + "integrity": "sha512-QE7X69iQI+ZXwldE+rzasvbJiyV/ju1FGHH0Qn2W3FKbuYtqp8LKcy6iSw79fVUT5/Vvf+0XgLCeYVG+UV6hOw==", 153 + "dev": true, 154 + "dependencies": { 155 + "mini-svg-data-uri": "^1.2.3" 156 + }, 157 + "peerDependencies": { 158 + "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1" 159 + } 160 + }, 148 161 "node_modules/ansi-regex": { 149 162 "version": "6.0.1", 150 163 "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", ··· 158 171 } 159 172 }, 160 173 "node_modules/ansi-styles": { 161 - "version": "6.2.1", 162 - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", 163 - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", 174 + "version": "4.3.0", 175 + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 176 + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 164 177 "dev": true, 178 + "dependencies": { 179 + "color-convert": "^2.0.1" 180 + }, 165 181 "engines": { 166 - "node": ">=12" 182 + "node": ">=8" 167 183 }, 168 184 "funding": { 169 185 "url": "https://github.com/chalk/ansi-styles?sponsor=1" ··· 312 328 } 313 329 }, 314 330 "node_modules/caniuse-lite": { 315 - "version": "1.0.30001621", 316 - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001621.tgz", 317 - "integrity": "sha512-+NLXZiviFFKX0fk8Piwv3PfLPGtRqJeq2TiNoUff/qB5KJgwecJTvCXDpmlyP/eCI/GUEmp/h/y5j0yckiiZrA==", 331 + "version": "1.0.30001625", 332 + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001625.tgz", 333 + "integrity": "sha512-4KE9N2gcRH+HQhpeiRZXd+1niLB/XNLAhSy4z7fI8EzcbcPoAqjNInxVHTiTwWfTIV4w096XG8OtCOCQQKPv3w==", 318 334 "dev": true, 319 335 "funding": [ 320 336 { ··· 345 361 }, 346 362 "funding": { 347 363 "url": "https://github.com/chalk/chalk?sponsor=1" 348 - } 349 - }, 350 - "node_modules/chalk/node_modules/ansi-styles": { 351 - "version": "4.3.0", 352 - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 353 - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 354 - "dev": true, 355 - "dependencies": { 356 - "color-convert": "^2.0.1" 357 - }, 358 - "engines": { 359 - "node": ">=8" 360 - }, 361 - "funding": { 362 - "url": "https://github.com/chalk/ansi-styles?sponsor=1" 363 364 } 364 365 }, 365 366 "node_modules/chalk/node_modules/supports-color": { ··· 433 434 "node": ">=8" 434 435 } 435 436 }, 436 - "node_modules/cliui/node_modules/ansi-styles": { 437 - "version": "4.3.0", 438 - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 439 - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 440 - "dev": true, 441 - "dependencies": { 442 - "color-convert": "^2.0.1" 443 - }, 444 - "engines": { 445 - "node": ">=8" 446 - }, 447 - "funding": { 448 - "url": "https://github.com/chalk/ansi-styles?sponsor=1" 449 - } 450 - }, 451 437 "node_modules/cliui/node_modules/emoji-regex": { 452 438 "version": "8.0.0", 453 439 "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", ··· 612 598 "dev": true 613 599 }, 614 600 "node_modules/electron-to-chromium": { 615 - "version": "1.4.779", 616 - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.779.tgz", 617 - "integrity": "sha512-oaTiIcszNfySXVJzKcjxd2YjPxziAd+GmXyb2HbidCeFo6Z88ygOT7EimlrEQhM2U08VhSrbKhLOXP0kKUCZ6g==", 601 + "version": "1.4.783", 602 + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.783.tgz", 603 + "integrity": "sha512-bT0jEz/Xz1fahQpbZ1D7LgmPYZ3iHVY39NcWWro1+hA2IvjiPeaXtfSqrQ+nXjApMvQRE2ASt1itSLRrebHMRQ==", 618 604 "dev": true 619 605 }, 620 606 "node_modules/emoji-regex": { ··· 743 729 } 744 730 }, 745 731 "node_modules/glob": { 746 - "version": "10.3.16", 747 - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.16.tgz", 748 - "integrity": "sha512-JDKXl1DiuuHJ6fVS2FXjownaavciiHNUU4mOvV/B793RLh05vZL1rcPnCSaOgv1hDT6RDlY7AB7ZUvFYAtPgAw==", 732 + "version": "10.4.1", 733 + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.1.tgz", 734 + "integrity": "sha512-2jelhlq3E4ho74ZyVLN03oKdAZVUa6UDZzFLVH1H7dnoax+y9qyaq8zBkfDIggjniU19z0wU18y16jMB2eyVIw==", 749 735 "dev": true, 750 736 "dependencies": { 751 737 "foreground-child": "^3.1.0", 752 738 "jackspeak": "^3.1.2", 753 - "minimatch": "^9.0.1", 754 - "minipass": "^7.0.4", 755 - "path-scurry": "^1.11.0" 739 + "minimatch": "^9.0.4", 740 + "minipass": "^7.1.2", 741 + "path-scurry": "^1.11.1" 756 742 }, 757 743 "bin": { 758 744 "glob": "dist/esm/bin.mjs" ··· 945 931 "node": ">=8.6" 946 932 } 947 933 }, 934 + "node_modules/mini-svg-data-uri": { 935 + "version": "1.4.4", 936 + "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz", 937 + "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==", 938 + "dev": true, 939 + "bin": { 940 + "mini-svg-data-uri": "cli.js" 941 + } 942 + }, 948 943 "node_modules/minimatch": { 949 944 "version": "9.0.4", 950 945 "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", ··· 961 956 } 962 957 }, 963 958 "node_modules/minipass": { 964 - "version": "7.1.1", 965 - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.1.tgz", 966 - "integrity": "sha512-UZ7eQ+h8ywIRAW1hIEl2AqdwzJucU/Kp59+8kkZeSvafXhZjul247BvIJjEVFVeON6d7lM46XX1HXCduKAS8VA==", 959 + "version": "7.1.2", 960 + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", 961 + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", 967 962 "dev": true, 968 963 "engines": { 969 964 "node": ">=16 || 14 >=14.17" ··· 1759 1754 "node": ">=8" 1760 1755 } 1761 1756 }, 1762 - "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { 1763 - "version": "4.3.0", 1764 - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 1765 - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 1766 - "dev": true, 1767 - "dependencies": { 1768 - "color-convert": "^2.0.1" 1769 - }, 1770 - "engines": { 1771 - "node": ">=8" 1772 - }, 1773 - "funding": { 1774 - "url": "https://github.com/chalk/ansi-styles?sponsor=1" 1775 - } 1776 - }, 1777 1757 "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { 1778 1758 "version": "8.0.0", 1779 1759 "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", ··· 1804 1784 }, 1805 1785 "engines": { 1806 1786 "node": ">=8" 1787 + } 1788 + }, 1789 + "node_modules/wrap-ansi/node_modules/ansi-styles": { 1790 + "version": "6.2.1", 1791 + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", 1792 + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", 1793 + "dev": true, 1794 + "engines": { 1795 + "node": ">=12" 1796 + }, 1797 + "funding": { 1798 + "url": "https://github.com/chalk/ansi-styles?sponsor=1" 1807 1799 } 1808 1800 }, 1809 1801 "node_modules/y18n": {
+3 -2
package.json
··· 6 6 "autoprefixer": "^10.4.19", 7 7 "concurrently": "^8.2.2", 8 8 "postcss": "^8.4.38", 9 - "tailwindcss": "^3.4.3" 9 + "tailwindcss": "^3.4.3", 10 + "@tailwindcss/forms": "^0.5.7" 10 11 } 11 - } 12 + }
+1 -1
tailwind.config.js
··· 4 4 theme: { 5 5 extend: {}, 6 6 }, 7 - plugins: [], 7 + plugins: [require('@tailwindcss/forms')], 8 8 } 9 9