Merge pull request #157 from PR0M3TH3AN/codex/add-ui-for-new-nostr-profiles

Add UI affordance for managing multiple Nostr profiles
This commit is contained in:
thePR0M3TH3AN authored and GitHub committed 2025-09-29 16:01:10 -04:00
commit ce1f2a8f37
2 files changed
+366 -19

No files matched your search

+82 -19
View File
@@ -107,27 +107,90 @@
role="tabpanel"
aria-labelledby="profileNavAccount"
>
<div class="flex items-center space-x-4">
<img
id="profileModalAvatar"
src="assets/svg/default-profile.svg"
alt="Profile"
class="w-16 h-16 object-cover rounded-full"
/>
<p id="profileModalName" class="text-lg font-semibold truncate">
Loading...
<!--
The account pane now relies on bespoke CSS (see css/style.css under
"Profile modal account pane") so we are not dependent on utility
classes that may be purged or overridden by other builds.
-->
<div class="profile-switcher" id="profileSwitcherTray">
<div class="profile-switcher__header">
<div class="profile-switcher__intro">
<h3 class="profile-switcher__title">Switch profiles</h3>
<p class="profile-switcher__subtitle">
Save multiple Nostr profiles so you can swap identities without leaving BitVid.
</p>
</div>
<span class="profile-switcher__badge">Preview</span>
</div>
<div
class="profile-card profile-card--active"
aria-label="Current Nostr profile"
aria-pressed="true"
role="button"
tabindex="0"
>
<span class="profile-card__avatar">
<img
id="profileModalAvatar"
src="assets/svg/default-profile.svg"
alt="Active profile avatar"
/>
</span>
<span class="profile-card__meta">
<span class="profile-card__topline">
<span class="profile-card__label">Active profile</span>
<a
id="profileChannelLink"
href="#"
class="profile-card__action hidden"
>
View channel
</a>
</span>
<span id="profileModalName" class="profile-card__name">Loading...</span>
<span id="profileModalNpub" class="profile-card__npub">npub1...</span>
</span>
</div>
<div
id="profileSwitcherList"
class="profile-switcher__list"
aria-live="polite"
aria-label="Additional Nostr profiles"
></div>
<button
type="button"
id="profileAddAccountBtn"
class="profile-switcher__add"
aria-label="Add a new Nostr profile"
>
<span class="profile-switcher__addAvatar">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
>
<line x1="12" y1="5" x2="12" y2="19" />
<line x1="5" y1="12" x2="19" y2="12" />
</svg>
</span>
<span class="profile-switcher__addCopy">
<span class="profile-switcher__addTitle">Add profile</span>
<span class="profile-switcher__addHint">Link another npub</span>
</span>
</button>
<p class="profile-switcher__helper">
Profiles you add here will stay on this device so you can quickly switch before posting or moderating.
</p>
</div>
<a
id="profileChannelLink"
href="#"
class="inline-flex items-center text-sm text-blue-400 hover:text-blue-300 transition-colors hidden"
>
View your channel
</a>
<p class="text-sm text-gray-400">
Manage your account details and upcoming preferences here.
</p>
</section>
<section
+284
View File
@@ -269,6 +269,290 @@ header img {
-webkit-backdrop-filter: blur(0.625rem); /* For Safari */
}
/* --------------------------------------------------------------------------
Profile modal account pane
--------------------------------------------------------------------------
The modal previously mixed Tailwind-style utility classes with our global
stylesheet. Those utilities are not always present in the production build,
so important layout rules (borders, gaps, flex behaviour) were getting
dropped. This block consolidates the account tab styling in one predictable
place so future tweaks only require CSS updates here instead of scattered
inline class lists.
--------------------------------------------------------------------------- */
#profileModal .profile-pane {
display: flex;
flex-direction: column;
gap: 1.5rem;
}
/*
Ensure the global `.hidden` helper keeps working for the tab panes. Our
profile-pane declaration above sets a `display` value with higher
specificity than `.hidden`, so we explicitly restore the hidden state when
both classes are present.
*/
#profileModal .profile-pane.hidden {
display: none;
}
#profileModal .profile-switcher {
display: flex;
flex-direction: column;
gap: 1.5rem;
padding: clamp(1.25rem, 2vw, 1.5rem);
border-radius: 1.25rem;
border: 0.0625rem solid rgba(148, 163, 184, 0.28);
background: linear-gradient(145deg, rgba(15, 23, 42, 0.94), rgba(15, 23, 42, 0.72));
box-shadow: 0 1.75rem 3.5rem rgba(8, 11, 30, 0.45);
}
#profileModal .profile-switcher__header {
display: flex;
flex-direction: column;
gap: 0.75rem;
}
#profileModal .profile-switcher__intro {
display: flex;
flex-direction: column;
gap: 0.35rem;
}
#profileModal .profile-switcher__title {
margin: 0;
font-size: 1rem;
font-weight: 600;
color: var(--color-text);
}
#profileModal .profile-switcher__subtitle {
margin: 0;
font-size: 0.875rem;
color: var(--color-muted);
}
#profileModal .profile-switcher__badge {
align-self: flex-start;
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0.35rem 0.85rem;
font-size: 0.625rem;
font-weight: 600;
letter-spacing: 0.18em;
text-transform: uppercase;
border-radius: 9999px;
background: rgba(59, 130, 246, 0.18);
color: rgba(191, 219, 254, 0.95);
border: 0.0625rem solid rgba(96, 165, 250, 0.25);
}
#profileModal .profile-card {
position: relative;
display: flex;
flex-direction: column;
gap: 0.85rem;
padding: 1rem;
border-radius: 1rem;
border: 0.0625rem solid rgba(148, 163, 184, 0.24);
background: rgba(15, 23, 42, 0.72);
box-shadow: inset 0 1px 0 rgba(148, 163, 184, 0.08);
color: var(--color-text);
cursor: pointer;
transition: border-color 0.2s ease, box-shadow 0.2s ease, background 0.2s ease;
outline: none;
}
#profileModal .profile-card:hover {
border-color: rgba(148, 163, 184, 0.45);
}
#profileModal .profile-card:focus-visible {
border-color: rgba(96, 165, 250, 0.6);
box-shadow: 0 0 0 0.1875rem rgba(37, 99, 235, 0.25);
}
#profileModal .profile-card--active {
border-color: rgba(59, 130, 246, 0.55);
background: linear-gradient(160deg, rgba(37, 99, 235, 0.22), rgba(15, 23, 42, 0.78));
box-shadow: 0 1.35rem 2.8rem rgba(37, 99, 235, 0.22);
}
#profileModal .profile-card__avatar {
flex: 0 0 auto;
width: 3rem;
height: 3rem;
display: grid;
place-items: center;
border-radius: 9999px;
overflow: hidden;
border: 0.1875rem solid rgba(148, 163, 184, 0.3);
background: rgba(15, 23, 42, 0.95);
}
#profileModal .profile-card__avatar img {
width: 100%;
height: 100%;
border-radius: inherit;
object-fit: cover;
}
#profileModal .profile-card--active .profile-card__avatar {
border-color: rgba(96, 165, 250, 0.7);
box-shadow: 0 0 0 0.1875rem rgba(59, 130, 246, 0.18);
}
#profileModal .profile-card__meta {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 0.5rem;
}
#profileModal .profile-card__topline {
display: flex;
flex-direction: column;
gap: 0.35rem;
align-items: flex-start;
}
#profileModal .profile-card__label {
font-size: 0.7rem;
font-weight: 600;
letter-spacing: 0.2em;
text-transform: uppercase;
color: rgba(191, 219, 254, 0.9);
}
#profileModal .profile-card__action {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0.3rem 0.8rem;
border-radius: 9999px;
border: 0.0625rem solid rgba(59, 130, 246, 0.4);
background: rgba(59, 130, 246, 0.14);
color: rgba(191, 219, 254, 0.95);
font-size: 0.65rem;
font-weight: 600;
letter-spacing: 0.18em;
text-transform: uppercase;
text-decoration: none;
transition: background 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}
#profileModal .profile-card__action:hover,
#profileModal .profile-card__action:focus-visible {
background: rgba(59, 130, 246, 0.24);
border-color: rgba(59, 130, 246, 0.6);
color: #ffffff;
}
#profileModal .profile-card__name {
font-size: 0.95rem;
font-weight: 600;
color: var(--color-text);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
#profileModal .profile-card__npub {
font-size: 0.75rem;
font-family: "JetBrains Mono", "Fira Code", ui-monospace, SFMono-Regular, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
color: rgba(148, 163, 184, 0.85);
word-break: break-all;
}
#profileModal .profile-switcher__list {
display: flex;
flex-direction: column;
gap: 1rem;
}
#profileModal .profile-switcher__add {
display: flex;
align-items: center;
gap: 1rem;
padding: 1rem;
border-radius: 1rem;
border: 0.125rem dashed rgba(148, 163, 184, 0.35);
background: rgba(15, 23, 42, 0.55);
color: rgba(226, 232, 240, 0.85);
text-align: left;
cursor: pointer;
transition: border-color 0.2s ease, background 0.2s ease, color 0.2s ease;
}
#profileModal .profile-switcher__add:hover,
#profileModal .profile-switcher__add:focus-visible {
border-color: rgba(96, 165, 250, 0.65);
background: rgba(59, 130, 246, 0.18);
color: #ffffff;
outline: none;
}
#profileModal .profile-switcher__addAvatar {
flex: 0 0 auto;
width: 3rem;
height: 3rem;
display: grid;
place-items: center;
border-radius: 9999px;
border: 0.125rem dashed currentColor;
color: inherit;
background: rgba(15, 23, 42, 0.75);
}
#profileModal .profile-switcher__addAvatar svg {
width: 1.25rem;
height: 1.25rem;
}
#profileModal .profile-switcher__addCopy {
display: flex;
flex-direction: column;
gap: 0.25rem;
}
#profileModal .profile-switcher__addTitle {
font-size: 0.9rem;
font-weight: 600;
color: inherit;
}
#profileModal .profile-switcher__addHint {
font-size: 0.75rem;
color: rgba(148, 163, 184, 0.85);
}
#profileModal .profile-switcher__helper {
margin: 0;
font-size: 0.75rem;
color: rgba(148, 163, 184, 0.7);
}
@media (min-width: 640px) {
#profileModal .profile-switcher__header {
flex-direction: row;
align-items: center;
justify-content: space-between;
}
#profileModal .profile-card {
flex-direction: row;
align-items: center;
}
#profileModal .profile-card__topline {
flex-direction: row;
justify-content: space-between;
align-items: center;
gap: 0.75rem;
}
}
/* If you ever want to show it, add ".flex" class dynamically */
#playerModal.flex {
display: flex;