this repo has no description
0
fork

Configure Feed

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

fix(manage): full-width form below description, sign-out into handle row, centered actions

- Move every field below name/description out of the avatar+fields
right column into a new full-width .profile-form-stack so chip
groups, Atmosphere services, and link editors get the whole card
on desktop.
- Drop the duplicate "Signed in as @handle / Sign out" lockup above
the card; the handle row inside the form now anchors a sign-out
pill on the right (button uses formAction="/oauth/logout" +
formNoValidate so it doesn't trip the parent profile form).
- Center the Update / View / Remove action row inside the card.
- Convert Custom links + Developer icon sections from
fieldset/legend to div/span so the .profile-form-field flex gap
finally applies between the label and its content (legend doesn't
participate in flex layout). Drop the now-redundant
.custom-link-list margin-top + custom-link-add margin-top.

Made-with: Cursor

+339 -279
+50 -9
assets/styles.css
··· 2297 2297 align-items: flex-start; 2298 2298 gap: 1rem; 2299 2299 } 2300 - .manage-header-aside { 2301 - text-align: right; 2302 - } 2303 - 2304 2300 .profile-form { 2305 2301 padding: 1.75rem; 2306 2302 border-radius: 24px; ··· 2417 2413 gap: 1.1rem; 2418 2414 } 2419 2415 2420 - /* Read-only "Signed in as @handle" row */ 2416 + /* Full-width column for everything below name/description (categories, 2417 + links, icon, …). Sibling of `.profile-form-row`, so it spans the 2418 + whole card on desktop instead of being constrained to the 2419 + avatar+fields right-hand column. */ 2420 + .profile-form-stack { 2421 + display: flex; 2422 + flex-direction: column; 2423 + gap: 1.1rem; 2424 + margin-top: 1.5rem; 2425 + } 2426 + 2427 + /* Read-only "Signed in as @handle" row, with the sign-out button 2428 + anchored to the right. The button uses formAction="/oauth/logout" 2429 + on submit so it doesn't break the parent profile form. */ 2421 2430 .profile-form-handle-row { 2422 2431 display: flex; 2423 - align-items: baseline; 2424 - gap: 0.5rem; 2432 + align-items: center; 2433 + justify-content: space-between; 2434 + gap: 0.75rem; 2425 2435 flex-wrap: wrap; 2426 2436 padding: 0.6rem 0.85rem; 2427 2437 border-radius: 12px; 2428 2438 background: rgba(18, 26, 47, 0.04); 2429 2439 border: 1px solid rgba(18, 26, 47, 0.06); 2430 2440 } 2441 + .profile-form-handle-info { 2442 + display: flex; 2443 + align-items: baseline; 2444 + gap: 0.5rem; 2445 + flex-wrap: wrap; 2446 + min-width: 0; 2447 + } 2431 2448 .profile-form-handle-value { 2432 2449 font-family: "IBM Plex Mono", monospace; 2433 2450 font-size: 0.95rem; 2434 2451 font-weight: 600; 2435 2452 color: rgba(18, 26, 47, 0.9); 2436 2453 } 2454 + .profile-form-handle-signout { 2455 + background: transparent; 2456 + border: 1px solid rgba(18, 26, 47, 0.18); 2457 + border-radius: 999px; 2458 + padding: 0.3rem 0.85rem; 2459 + font: inherit; 2460 + font-size: 0.78rem; 2461 + color: rgba(18, 26, 47, 0.75); 2462 + cursor: pointer; 2463 + transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease; 2464 + } 2465 + .profile-form-handle-signout:hover { 2466 + background: rgba(18, 26, 47, 0.06); 2467 + border-color: rgba(18, 26, 47, 0.28); 2468 + color: rgba(18, 26, 47, 0.92); 2469 + } 2437 2470 .dark-phase .profile-form-handle-row { 2438 2471 background: rgba(255, 255, 255, 0.06); 2439 2472 border-color: rgba(255, 255, 255, 0.1); 2440 2473 } 2441 2474 .dark-phase .profile-form-handle-value { 2442 2475 color: rgba(255, 255, 255, 0.95); 2476 + } 2477 + .dark-phase .profile-form-handle-signout { 2478 + border-color: rgba(255, 255, 255, 0.18); 2479 + color: rgba(255, 255, 255, 0.8); 2480 + } 2481 + .dark-phase .profile-form-handle-signout:hover { 2482 + background: rgba(255, 255, 255, 0.08); 2483 + border-color: rgba(255, 255, 255, 0.3); 2484 + color: rgba(255, 255, 255, 0.98); 2443 2485 } 2444 2486 2445 2487 /* ---- Bluesky client picker (used inside the modal for multi-select) ---- */ ··· 2901 2943 display: flex; 2902 2944 flex-direction: column; 2903 2945 gap: 0.5rem; 2904 - margin-top: 0.4rem; 2905 2946 } 2906 2947 .custom-link-row { 2907 2948 display: grid; ··· 2940 2981 color: rgba(254, 202, 202, 0.95); 2941 2982 } 2942 2983 .custom-link-add { 2943 - margin-top: 0.55rem; 2944 2984 align-self: flex-start; 2945 2985 } 2946 2986 @media (max-width: 640px) { ··· 3090 3130 display: flex; 3091 3131 gap: 0.6rem; 3092 3132 align-items: center; 3133 + justify-content: center; 3093 3134 flex-wrap: wrap; 3094 3135 margin-top: 1.5rem; 3095 3136 }
+289 -260
islands/CreateProfileForm.tsx
··· 647 647 </div> 648 648 649 649 <div class="profile-form-fields"> 650 + { 651 + /* 652 + Handle/sign-out lockup. The sign-out submit button uses 653 + formAction/formMethod to override the parent profile form's 654 + target — keeps the sign-out a real POST without nesting 655 + forms (which HTML forbids). 656 + */ 657 + } 650 658 <div class="profile-form-handle-row"> 651 - <span class="profile-form-label">{tForm.handleLabel}</span> 652 - <span class="profile-form-handle-value">@{handle}</span> 659 + <div class="profile-form-handle-info"> 660 + <span class="profile-form-label">{tForm.handleLabel}</span> 661 + <span class="profile-form-handle-value">@{handle}</span> 662 + </div> 663 + <button 664 + type="submit" 665 + formAction="/oauth/logout" 666 + formMethod="POST" 667 + formNoValidate 668 + class="profile-form-handle-signout" 669 + > 670 + {tManage.signOut} 671 + </button> 653 672 </div> 654 673 655 674 <label class="profile-form-field"> ··· 684 703 class="profile-form-input" 685 704 /> 686 705 </label> 706 + </div> 707 + </div> 687 708 709 + { 710 + /* 711 + Everything below name/description spans the full card width on 712 + desktop instead of staying constrained to the avatar+fields 713 + right column. Keeps long lists (Atmosphere services, custom 714 + links, chips) from wrapping into narrow columns. 715 + */ 716 + } 717 + <div class="profile-form-stack"> 718 + <fieldset class="profile-form-field"> 719 + <legend class="profile-form-label">{tForm.categoryLabel}</legend> 720 + <div class="profile-form-chips" role="group"> 721 + {CATEGORIES.map((c: Category) => { 722 + const selected = categories.value.includes(c); 723 + return ( 724 + <label 725 + key={c} 726 + class={`profile-form-chip ${selected ? "is-selected" : ""}`} 727 + > 728 + <input 729 + type="checkbox" 730 + name="categories" 731 + value={c} 732 + checked={selected} 733 + onChange={() => toggleCategory(c)} 734 + /> 735 + <span>{t.categories[c]}</span> 736 + </label> 737 + ); 738 + })} 739 + </div> 740 + <p class="profile-form-hint">{tForm.categoryHint}</p> 741 + </fieldset> 742 + 743 + {showSubcategories && ( 688 744 <fieldset class="profile-form-field"> 689 - <legend class="profile-form-label">{tForm.categoryLabel}</legend> 690 - <div class="profile-form-chips" role="group"> 691 - {CATEGORIES.map((c: Category) => { 692 - const selected = categories.value.includes(c); 693 - return ( 694 - <label 695 - key={c} 696 - class={`profile-form-chip ${selected ? "is-selected" : ""}`} 697 - > 698 - <input 699 - type="checkbox" 700 - name="categories" 701 - value={c} 702 - checked={selected} 703 - onChange={() => toggleCategory(c)} 704 - /> 705 - <span>{t.categories[c]}</span> 706 - </label> 707 - ); 708 - })} 745 + <legend class="profile-form-label"> 746 + {tForm.subcategoriesLabel} 747 + </legend> 748 + <div class="profile-form-chips"> 749 + {APP_SUBCATEGORIES.map((s) => ( 750 + <button 751 + key={s} 752 + type="button" 753 + class={`profile-form-chip ${ 754 + subcategories.value.includes(s) ? "is-selected" : "" 755 + }`} 756 + onClick={() => toggleSub(s)} 757 + > 758 + {t.subcategories[s]} 759 + </button> 760 + ))} 709 761 </div> 710 - <p class="profile-form-hint">{tForm.categoryHint}</p> 762 + <p class="profile-form-hint">{tForm.subcategoriesHint}</p> 711 763 </fieldset> 764 + )} 712 765 713 - {showSubcategories && ( 714 - <fieldset class="profile-form-field"> 715 - <legend class="profile-form-label"> 716 - {tForm.subcategoriesLabel} 717 - </legend> 718 - <div class="profile-form-chips"> 719 - {APP_SUBCATEGORIES.map((s) => ( 720 - <button 721 - key={s} 722 - type="button" 723 - class={`profile-form-chip ${ 724 - subcategories.value.includes(s) ? "is-selected" : "" 725 - }`} 726 - onClick={() => toggleSub(s)} 727 - > 728 - {t.subcategories[s]} 729 - </button> 730 - ))} 731 - </div> 732 - <p class="profile-form-hint">{tForm.subcategoriesHint}</p> 733 - </fieldset> 734 - )} 735 - 736 - {/* ---------------- Main Link ----------------------------- */} 737 - { 738 - /* 766 + {/* ---------------- Main Link ----------------------------- */} 767 + { 768 + /* 739 769 Required. Drives the listing card's link target on /explore 740 770 (whole card becomes a button). Also surfaced as a small 741 771 arrow on hover. We keep it directly above Atmosphere links ··· 744 774 services) → optional secondary surfaces (Landing Page + 745 775 custom). 746 776 */ 747 - } 748 - <label class="profile-form-field"> 749 - <span class="profile-form-label"> 750 - {tMainLink.sectionLabel}{" "} 751 - <span class="profile-form-required">*</span> 752 - </span> 753 - <input 754 - type="url" 755 - class="profile-form-input" 756 - placeholder={tMainLink.placeholder} 757 - value={mainLink.value} 758 - required 759 - onInput={(e) => 760 - mainLink.value = (e.currentTarget as HTMLInputElement).value} 761 - /> 762 - </label> 777 + } 778 + <label class="profile-form-field"> 779 + <span class="profile-form-label"> 780 + {tMainLink.sectionLabel}{" "} 781 + <span class="profile-form-required">*</span> 782 + </span> 783 + <input 784 + type="url" 785 + class="profile-form-input" 786 + placeholder={tMainLink.placeholder} 787 + value={mainLink.value} 788 + required 789 + onInput={(e) => 790 + mainLink.value = (e.currentTarget as HTMLInputElement).value} 791 + /> 792 + </label> 763 793 764 - {/* ---------------- Atmosphere links ----------------------- */} 765 - <fieldset class="profile-form-field"> 766 - <legend class="profile-form-label">{tAtmos.sectionLabel}</legend> 767 - <p class="profile-form-hint">{tAtmos.sectionHint(handle)}</p> 794 + {/* ---------------- Atmosphere links ----------------------- */} 795 + <fieldset class="profile-form-field"> 796 + <legend class="profile-form-label">{tAtmos.sectionLabel}</legend> 797 + <p class="profile-form-hint">{tAtmos.sectionHint(handle)}</p> 768 798 769 - <div class="atmosphere-toggles"> 770 - {visibleAtmosphereServices().map((svc) => 771 - renderAtmosphereRow(svc, { 772 - bskyClientIds, 773 - bskyPickerOpen, 774 - tangledOn, 775 - tangledUrl, 776 - supperOn, 777 - supperUrl, 778 - urlOverrideOpen, 779 - tAtmos, 780 - handle, 781 - }) 782 - )} 783 - </div> 784 - </fieldset> 799 + <div class="atmosphere-toggles"> 800 + {visibleAtmosphereServices().map((svc) => 801 + renderAtmosphereRow(svc, { 802 + bskyClientIds, 803 + bskyPickerOpen, 804 + tangledOn, 805 + tangledUrl, 806 + supperOn, 807 + supperUrl, 808 + urlOverrideOpen, 809 + tAtmos, 810 + handle, 811 + }) 812 + )} 813 + </div> 814 + </fieldset> 785 815 786 - {/* ---------------- Landing Page (optional) --------------- */} 787 - { 788 - /* 816 + {/* ---------------- Landing Page (optional) --------------- */} 817 + { 818 + /* 789 819 Optional secondary URL — a separate marketing/landing page 790 820 distinct from the Main Link. Renders as the globe-icon 791 821 button on /explore/<handle>. Stored as `kind: website` for 792 822 backward compatibility with existing records. 793 823 */ 794 - } 795 - <label class="profile-form-field"> 796 - <span class="profile-form-label">{tLanding.sectionLabel}</span> 797 - <input 798 - type="url" 799 - class="profile-form-input" 800 - placeholder={tLanding.placeholder} 801 - value={landingPage.value} 802 - onInput={(e) => 803 - landingPage.value = (e.currentTarget as HTMLInputElement).value} 804 - /> 805 - <p class="profile-form-hint">{tLanding.hint}</p> 806 - </label> 824 + } 825 + <label class="profile-form-field"> 826 + <span class="profile-form-label">{tLanding.sectionLabel}</span> 827 + <input 828 + type="url" 829 + class="profile-form-input" 830 + placeholder={tLanding.placeholder} 831 + value={landingPage.value} 832 + onInput={(e) => 833 + landingPage.value = (e.currentTarget as HTMLInputElement).value} 834 + /> 835 + <p class="profile-form-hint">{tLanding.hint}</p> 836 + </label> 807 837 808 - {/* ---------------- Custom links -------------------------- */} 809 - <fieldset class="profile-form-field"> 810 - <legend class="profile-form-label">{tCustom.sectionLabel}</legend> 811 - <div class="custom-link-list"> 812 - {customLinks.value.map((row, i) => ( 813 - <div class="custom-link-row" key={i}> 814 - <input 815 - type="text" 816 - class="profile-form-input custom-link-label" 817 - placeholder={tCustom.labelPlaceholder} 818 - value={row.label} 819 - maxLength={64} 820 - onInput={(e) => 821 - updateCustomLink(i, { 822 - label: (e.currentTarget as HTMLInputElement).value, 823 - })} 824 - /> 825 - <input 826 - type="url" 827 - class="profile-form-input custom-link-url" 828 - placeholder={tCustom.urlPlaceholder} 829 - value={row.url} 830 - onInput={(e) => 831 - updateCustomLink(i, { 832 - url: (e.currentTarget as HTMLInputElement).value, 833 - })} 834 - /> 835 - <button 836 - type="button" 837 - class="custom-link-remove" 838 - aria-label={tCustom.removeAriaLabel} 839 - onClick={() => removeCustomLink(i)} 840 - > 841 - × 842 - </button> 843 - </div> 844 - ))} 845 - </div> 846 - <button 847 - type="button" 848 - class="profile-form-button-secondary custom-link-add" 849 - onClick={addCustomLink} 850 - disabled={customLinks.value.length >= 8} 851 - > 852 - + {tCustom.addButton} 853 - </button> 854 - </fieldset> 838 + {/* ---------------- Custom links -------------------------- */} 839 + <div class="profile-form-field"> 840 + <span class="profile-form-label">{tCustom.sectionLabel}</span> 841 + <div class="custom-link-list"> 842 + {customLinks.value.map((row, i) => ( 843 + <div class="custom-link-row" key={i}> 844 + <input 845 + type="text" 846 + class="profile-form-input custom-link-label" 847 + placeholder={tCustom.labelPlaceholder} 848 + value={row.label} 849 + maxLength={64} 850 + onInput={(e) => 851 + updateCustomLink(i, { 852 + label: (e.currentTarget as HTMLInputElement).value, 853 + })} 854 + /> 855 + <input 856 + type="url" 857 + class="profile-form-input custom-link-url" 858 + placeholder={tCustom.urlPlaceholder} 859 + value={row.url} 860 + onInput={(e) => 861 + updateCustomLink(i, { 862 + url: (e.currentTarget as HTMLInputElement).value, 863 + })} 864 + /> 865 + <button 866 + type="button" 867 + class="custom-link-remove" 868 + aria-label={tCustom.removeAriaLabel} 869 + onClick={() => removeCustomLink(i)} 870 + > 871 + × 872 + </button> 873 + </div> 874 + ))} 875 + </div> 876 + <button 877 + type="button" 878 + class="profile-form-button-secondary custom-link-add" 879 + onClick={addCustomLink} 880 + disabled={customLinks.value.length >= 8} 881 + > 882 + + {tCustom.addButton} 883 + </button> 884 + </div> 855 885 856 - {/* ---------------- Developer SVG icon -------------------- */} 857 - { 858 - /* 886 + {/* ---------------- Developer SVG icon -------------------- */} 887 + { 888 + /* 859 889 Vector mark exposed only via /api/registry/icon/:did, for 860 890 developers building badges and app showcases. Not shown on 861 891 the public Explore profile. Uploads are gated behind 862 892 per-project verification — admin-granted only. 863 893 */ 864 - } 865 - <fieldset 866 - class={`profile-form-field icon-section icon-section--${ 867 - iconAccessStatus.value ?? "locked" 868 - }`} 869 - > 870 - <legend class="profile-form-label">{tIcon.sectionLabel}</legend> 894 + } 895 + <div 896 + class={`profile-form-field icon-section icon-section--${ 897 + iconAccessStatus.value ?? "locked" 898 + }`} 899 + > 900 + <span class="profile-form-label">{tIcon.sectionLabel}</span> 871 901 872 - {/* ---- Gate banners (one of these renders per state) ---- */} 873 - {iconAccessStatus.value === null && ( 874 - <div class="icon-gate-banner icon-gate-banner--locked"> 875 - <strong class="icon-gate-banner-title"> 876 - {tIcon.gate.lockedTitle} 877 - </strong> 878 - <span class="icon-gate-banner-body"> 879 - {tIcon.gate.lockedBody} 902 + {/* ---- Gate banners (one of these renders per state) ---- */} 903 + {iconAccessStatus.value === null && ( 904 + <div class="icon-gate-banner icon-gate-banner--locked"> 905 + <strong class="icon-gate-banner-title"> 906 + {tIcon.gate.lockedTitle} 907 + </strong> 908 + <span class="icon-gate-banner-body"> 909 + {tIcon.gate.lockedBody} 910 + </span> 911 + <button 912 + type="button" 913 + class="profile-form-button-secondary icon-gate-button" 914 + onClick={() => { 915 + requestError.value = null; 916 + requestEmail.value = ""; 917 + requestModalOpen.value = true; 918 + }} 919 + disabled={!published.value} 920 + title={published.value 921 + ? undefined 922 + : tIcon.gate.requestDisabledHint} 923 + > 924 + {tIcon.gate.requestButton} 925 + </button> 926 + {!published.value && ( 927 + <span class="icon-gate-banner-hint"> 928 + {tIcon.gate.requestDisabledHint} 880 929 </span> 930 + )} 931 + </div> 932 + )} 933 + {iconAccessStatus.value === "requested" && ( 934 + <div class="icon-gate-banner icon-gate-banner--pending"> 935 + <strong class="icon-gate-banner-title"> 936 + {tIcon.gate.pendingTitle} 937 + </strong> 938 + <span class="icon-gate-banner-body"> 939 + {tIcon.gate.pendingBody( 940 + iconAccessEmail.value ?? APPEAL_EMAIL, 941 + )} 942 + </span> 943 + </div> 944 + )} 945 + {iconAccessStatus.value === "denied" && ( 946 + <div class="icon-gate-banner icon-gate-banner--denied"> 947 + <strong class="icon-gate-banner-title"> 948 + {tIcon.gate.deniedTitle} 949 + </strong> 950 + <span class="icon-gate-banner-body"> 951 + {tIcon.gate.deniedBody(APPEAL_EMAIL, iconAccessDeniedReason)} 952 + </span> 953 + </div> 954 + )} 955 + {iconAccessStatus.value === "granted" && ( 956 + <p class="profile-form-hint icon-gate-granted-hint"> 957 + {tIcon.gate.grantedHint} 958 + </p> 959 + )} 960 + 961 + {/* ---- Uploader (greyed-out unless granted) ---- */} 962 + <div 963 + class={`profile-form-icon-row ${ 964 + iconUploadUnlocked ? "" : "is-locked" 965 + }`} 966 + > 967 + <div class="profile-form-icon-preview" aria-hidden="true"> 968 + {iconPreviewUrl.value 969 + ? ( 970 + <img 971 + src={iconPreviewUrl.value} 972 + alt="" 973 + class="profile-form-icon-preview-img" 974 + onError={() => { 975 + iconPreviewUrl.value = null; 976 + }} 977 + /> 978 + ) 979 + : <span class="profile-form-icon-placeholder">SVG</span>} 980 + </div> 981 + <div class="profile-form-icon-actions"> 982 + <label 983 + class={`profile-form-button-secondary ${ 984 + iconUploadUnlocked ? "" : "is-disabled" 985 + }`} 986 + aria-disabled={!iconUploadUnlocked} 987 + > 988 + {iconPreviewUrl.value ? tIcon.replace : tIcon.upload} 989 + <input 990 + type="file" 991 + accept="image/svg+xml" 992 + hidden 993 + disabled={!iconUploadUnlocked} 994 + onChange={onIconChange} 995 + /> 996 + </label> 997 + {iconPreviewUrl.value && iconUploadUnlocked && ( 881 998 <button 882 999 type="button" 883 - class="profile-form-button-secondary icon-gate-button" 884 - onClick={() => { 885 - requestError.value = null; 886 - requestEmail.value = ""; 887 - requestModalOpen.value = true; 888 - }} 889 - disabled={!published.value} 890 - title={published.value 891 - ? undefined 892 - : tIcon.gate.requestDisabledHint} 1000 + class="profile-form-button-link" 1001 + onClick={removeIcon} 893 1002 > 894 - {tIcon.gate.requestButton} 1003 + {tIcon.remove} 895 1004 </button> 896 - {!published.value && ( 897 - <span class="icon-gate-banner-hint"> 898 - {tIcon.gate.requestDisabledHint} 899 - </span> 900 - )} 901 - </div> 902 - )} 903 - {iconAccessStatus.value === "requested" && ( 904 - <div class="icon-gate-banner icon-gate-banner--pending"> 905 - <strong class="icon-gate-banner-title"> 906 - {tIcon.gate.pendingTitle} 907 - </strong> 908 - <span class="icon-gate-banner-body"> 909 - {tIcon.gate.pendingBody( 910 - iconAccessEmail.value ?? APPEAL_EMAIL, 911 - )} 912 - </span> 913 - </div> 914 - )} 915 - {iconAccessStatus.value === "denied" && ( 916 - <div class="icon-gate-banner icon-gate-banner--denied"> 917 - <strong class="icon-gate-banner-title"> 918 - {tIcon.gate.deniedTitle} 919 - </strong> 920 - <span class="icon-gate-banner-body"> 921 - {tIcon.gate.deniedBody(APPEAL_EMAIL, iconAccessDeniedReason)} 922 - </span> 923 - </div> 924 - )} 925 - {iconAccessStatus.value === "granted" && ( 926 - <p class="profile-form-hint icon-gate-granted-hint"> 927 - {tIcon.gate.grantedHint} 928 - </p> 929 - )} 930 - 931 - {/* ---- Uploader (greyed-out unless granted) ---- */} 932 - <div 933 - class={`profile-form-icon-row ${ 934 - iconUploadUnlocked ? "" : "is-locked" 935 - }`} 936 - > 937 - <div class="profile-form-icon-preview" aria-hidden="true"> 938 - {iconPreviewUrl.value 939 - ? ( 940 - <img 941 - src={iconPreviewUrl.value} 942 - alt="" 943 - class="profile-form-icon-preview-img" 944 - onError={() => { 945 - iconPreviewUrl.value = null; 946 - }} 947 - /> 948 - ) 949 - : <span class="profile-form-icon-placeholder">SVG</span>} 950 - </div> 951 - <div class="profile-form-icon-actions"> 952 - <label 953 - class={`profile-form-button-secondary ${ 954 - iconUploadUnlocked ? "" : "is-disabled" 955 - }`} 956 - aria-disabled={!iconUploadUnlocked} 957 - > 958 - {iconPreviewUrl.value ? tIcon.replace : tIcon.upload} 959 - <input 960 - type="file" 961 - accept="image/svg+xml" 962 - hidden 963 - disabled={!iconUploadUnlocked} 964 - onChange={onIconChange} 965 - /> 966 - </label> 967 - {iconPreviewUrl.value && iconUploadUnlocked && ( 968 - <button 969 - type="button" 970 - class="profile-form-button-link" 971 - onClick={removeIcon} 972 - > 973 - {tIcon.remove} 974 - </button> 975 - )} 976 - </div> 1005 + )} 977 1006 </div> 978 - <p class="profile-form-hint">{tIcon.hint}</p> 979 - </fieldset> 1007 + </div> 1008 + <p class="profile-form-hint">{tIcon.hint}</p> 980 1009 </div> 981 1010 </div> 982 1011
-10
routes/explore/manage.tsx
··· 172 172 <h1 class="text-section">{explore.manage.headline}</h1> 173 173 <p class="text-body mt-2">{explore.manage.subhead}</p> 174 174 </div> 175 - <div class="manage-header-aside"> 176 - <p class="text-body-sm"> 177 - {explore.manage.signedInAs} <strong>@{user.handle}</strong> 178 - </p> 179 - <form method="POST" action="/oauth/logout" class="inline-form"> 180 - <button type="submit" class="text-link-button"> 181 - {explore.manage.signOut} 182 - </button> 183 - </form> 184 - </div> 185 175 </div> 186 176 187 177 {takedown && (