From 58556e3534e8723a65609815ce0ffd7ab2e84e6b Mon Sep 17 00:00:00 2001 From: Yuurin Bee Date: Wed, 27 May 2026 06:51:40 +0700 Subject: [PATCH 01/22] Create Variables for Danger Red & Pink --- src/index.html | 2 +- src/js/render/chatlist/row.js | 2 +- src/js/settings.js | 2 +- src/styles.css | 32 +++++++++++++++++--------------- 4 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/index.html b/src/index.html index a3110f39..334f547c 100644 --- a/src/index.html +++ b/src/index.html @@ -871,7 +871,7 @@

Invite diff --git a/src/js/render/chatlist/row.js b/src/js/render/chatlist/row.js index c40c14d9..0608a379 100644 --- a/src/js/render/chatlist/row.js +++ b/src/js/render/chatlist/row.js @@ -295,7 +295,7 @@ function renderInviteItem(invite, primaryColor) { declineIcon.classList.add('icon', 'icon-x'); declineIcon.style.width = '16px'; declineIcon.style.height = '16px'; - declineIcon.style.backgroundColor = '#ff2ea9'; + declineIcon.style.backgroundColor = 'var(--danger-pink)'; btnDecline.appendChild(declineIcon); divActions.appendChild(btnAccept); diff --git a/src/js/settings.js b/src/js/settings.js index 951a6f11..d410e82d 100644 --- a/src/js/settings.js +++ b/src/js/settings.js @@ -1580,7 +1580,7 @@ domSettingsExport.onclick = async (evt) => { let exportContent = `
-

Security Warning. Do Not Lose.

+

These keys are your identity on Vector. There are no recovery options! If lost, your account cannot be restored. Never share them.

`; diff --git a/src/styles.css b/src/styles.css index 737768ba..2c1f36b0 100644 --- a/src/styles.css +++ b/src/styles.css @@ -192,7 +192,7 @@ } #chat-input-cancel .icon-cancel { - background-color: #FC595C; + background-color: var(--danger-red); } .icon-x { @@ -204,7 +204,7 @@ mask: url('./icons/edit-x.svg') no-repeat center; -webkit-mask-size: contain; mask-size: contain; - background-color: #FC595C; + background-color: var(--danger-red); width: 16px; height: 16px; display: inline-block; @@ -305,6 +305,8 @@ } :root { + --danger-pink: #FF2EA9; + --danger-red: #FC595C; font-family: Inter, Avenir, Helvetica, Arial, sans-serif; font-size: 16px; line-height: 24px; @@ -1461,7 +1463,7 @@ html, body { #profile-edit-cancel-btn .icon-edit-x, #wallpaper-edit-cancel-btn .icon-edit-x { - background-color: #FC595C !important; + background-color: var(--danger-red) !important; width: 14px !important; height: 14px !important; } @@ -4241,11 +4243,11 @@ img.emoji-img-loading { .emoji-creator-delete .icon { width: 16px; height: 16px; - background-color: #FF2EA9; + background-color: var(--danger-pink); } .emoji-creator-delete:hover .icon { - background-color: #FF2EA9; + background-color: var(--danger-pink); } /* Confirm overlay — generalised question modal. Same in-panel chrome @@ -4332,13 +4334,13 @@ img.emoji-img-loading { } .emoji-pack-creator-confirm[data-tone="danger"] .emoji-pack-creator-confirm-ok { - background-color: #ff2ea9; - border-color: #ff2ea9; + background-color: var(--danger-pink); + border-color: var(--danger-pink); color: #000; } .emoji-pack-creator-confirm-cancel { - border-color: #ff2ea9; + border-color: var(--danger-pink); background-color: #03030390; } /* Pack creator cropper overlay — square-crop UX for non-square static @@ -4740,7 +4742,7 @@ img.emoji-img-loading { margin: 0 0 16px; } .emoji-pack-creator-error-retry { - background-color: #ff2ea9; + background-color: var(--danger-pink); color: #000; border: none; padding: 12px 28px; @@ -7557,7 +7559,7 @@ select:disabled:hover { background-color: #00000000; margin-left: 10px; backdrop-filter: none; - border-color: #ff2ea9; + border-color: var(--danger-pink); background-color: #171717; } @@ -7822,7 +7824,7 @@ select:disabled:hover { } .danger-subtitle { - color: #ff2ea9; + color: var(--danger-pink); font-size: 12px; text-align: center; margin-top: 5px; @@ -7838,7 +7840,7 @@ select:disabled:hover { .danger-btn { backdrop-filter: none; - border-color: #ff2ea9; + border-color: var(--danger-pink); background-color: #00000000; } @@ -9956,7 +9958,7 @@ select:disabled:hover { } .invite-decline-btn { - border: 2px solid #ff2ea9; + border: 2px solid var(--danger-pink); } .invite-action-btn:hover { @@ -14945,10 +14947,10 @@ hr { distinctly from the additive ones (react/reply/edit) without tinting the whole button. */ .dmsg-toolbar-btn-danger .icon { - background-color: #FC595C; + background-color: var(--danger-red); } .dmsg-toolbar-btn-danger:hover .icon { - background-color: #FC595C; + background-color: var(--danger-red); filter: brightness(1.15); } From 8f55af79aac7c75e5022bbd4105c47a9fe473ff8 Mon Sep 17 00:00:00 2001 From: Yuurin Bee Date: Wed, 27 May 2026 08:51:57 +0700 Subject: [PATCH 02/22] Refactor: Danger Color Code System Standard --- src/icons/aggro-glitch-red.gif | Bin 0 -> 795 bytes src/icons/warning_red.png | Bin 0 -> 440 bytes src/index.html | 10 +- src/js/settings.js | 4 +- src/main.js | 22 +---- src/styles.css | 172 +++++++++++++++++++++------------ src/themes/cyberpunk/dark.css | 23 ++++- src/themes/vector/dark.css | 13 +++ 8 files changed, 151 insertions(+), 93 deletions(-) create mode 100644 src/icons/aggro-glitch-red.gif create mode 100644 src/icons/warning_red.png diff --git a/src/icons/aggro-glitch-red.gif b/src/icons/aggro-glitch-red.gif new file mode 100644 index 0000000000000000000000000000000000000000..152ffe32305dd1ce4b4cf9ce33559866f98686e5 GIT binary patch literal 795 zcmV+$1LXWiNk%w1VL$*t0P+I>GuxO6As1Qvv$a4%{8?Q8{{R3000000A^!_bMO0Hm zK~P09E-(WD0000X`2+%gjCQJq?}>U5;+Ooz%Wgu6?}&?~Y_o&t9y4PyX#BUypDN zy6pq_&fdUu63QI`bA(|9hb7iT{DpzysXbGTBvObsB%i;K7%zYnB2pi|k`_)b6!^qr z%aPSsZg6Ri9|xRzWZpby#+nU;V1&vby2Yps1S#Py?bnpxQw33#PNiB^>ea1WyL$Z! zHmumM1pojc`2-0I0000i00000HUKsN00KCKkEzS;52Kv4+KaQ^y!#J^;z*X}iKgnx zw(bkZ@=VwEjpzE#_x=wI3Wvm^@rX<+m&~T~X)FK$A^8La3jhEBECT=r06G9P000C3 z2)f)nFv>Zdz4+3-v;PezMqufUqp1Say0oqZyL0?has8$C34y#GhO`q2GelGJSmell zOrX)Rj1H$Es1>R84YuA=6`cKEyG(+buvk;a67ZU>aF@+D^l{!+zx8oje06?ze|The zPk>`qiieP7gmjCD3PCZ00E!)VIxCu-pO`M8g)E<%k{+s)l&cx7n~$&;rgN0IuR^+g zr@Fbgj>4|Kx3oRI#SOB+$PUh@#?Z*oyB5~e(#s6ltkK%n&EVh8;jyQmVmPzcw(0CE zGtb!Yfi&#&+91~3@9y;gGV90B-@k(1%7HQ%B_TS2d&ntVE(B|B0~sgfkij~EgF06ToYcc%aV literal 0 HcmV?d00001 diff --git a/src/icons/warning_red.png b/src/icons/warning_red.png new file mode 100644 index 0000000000000000000000000000000000000000..dc64c9e08367820039dc06aaff3d5eedeb8b60fd GIT binary patch literal 440 zcmeAS@N?(olHy`uVBq!ia0y~yU;;9k7?_xWRHv9tCXnI`@Ck7R(tjdjwmLZdyISV} z>5`2{mLJiCzwWXF2CIEGZ*dUMW@>wp0d%fXsU^Ur7p?+h%qn2<2@W?0kg?5%>k z#6U(Offvj(n9DQ%F(L^8nRED8l%t9?6f4u3QAHYx8RFSdg*M11nANS~ zLpIRn?KwtXpsGU){;gg6u*{>Oq2Yi(NY@Fem{{6n)58`>c`njxgN@xNAStdz3 literal 0 HcmV?d00001 diff --git a/src/index.html b/src/index.html index 334f547c..a197b389 100644 --- a/src/index.html +++ b/src/index.html @@ -816,8 +816,8 @@

- Block - + Block +
@@ -871,7 +871,7 @@

Invite @@ -1490,7 +1490,7 @@

Security


- +

Dangerzone

Irreversible Actions

@@ -1526,7 +1526,7 @@

Dangerzone

Logout
diff --git a/src/js/settings.js b/src/js/settings.js index d410e82d..5947ed64 100644 --- a/src/js/settings.js +++ b/src/js/settings.js @@ -2242,9 +2242,7 @@ async function loadBlockedUsersList() { const unblockBtn = document.createElement('span'); unblockBtn.textContent = 'Unblock'; - unblockBtn.style.cssText = 'color: #ff4444; font-size: 13px; cursor: pointer; padding: 4px 10px; border: 1px solid #ff4444; border-radius: 6px;'; - unblockBtn.onmouseenter = () => { unblockBtn.style.backgroundColor = '#ff444420'; }; - unblockBtn.onmouseleave = () => { unblockBtn.style.backgroundColor = ''; }; + unblockBtn.classList.add('unblock-btn'); unblockBtn.onclick = async () => { const confirmed = await popupConfirm('Unblock User', `Are you sure you want to unblock ${escapeHtml(profile.nickname || profile.name || profile.id.substring(0, 16))}?`); if (!confirmed) return; diff --git a/src/main.js b/src/main.js index 1248159d..3e3b8a83 100644 --- a/src/main.js +++ b/src/main.js @@ -4715,9 +4715,8 @@ function renderProfileTab(cProfile) { const isBlocked = cProfile.is_blocked || false; const blockIcon = domProfileOptionBlock.querySelector('.icon'); const blockLabel = domProfileOptionBlock.querySelector('span:first-child'); - blockIcon.style.backgroundColor = '#ff4444'; + domProfileOptionBlock.classList.add('is-danger'); if (blockLabel) { - blockLabel.style.color = '#ff4444'; blockLabel.textContent = isBlocked ? 'Unblock' : 'Block'; } domProfileOptionBlock.onclick = async () => { @@ -7703,24 +7702,7 @@ async function renderGroupOverview(chat) { if (iAmGroupAdmin && !isMe) { const kickBtn = document.createElement('button'); kickBtn.textContent = 'Kick'; - kickBtn.style.padding = '4px 12px'; - kickBtn.style.fontSize = '12px'; - kickBtn.style.borderRadius = '4px'; - kickBtn.style.border = 'none'; - kickBtn.style.background = '#ff4444'; - kickBtn.style.color = 'white'; - kickBtn.style.cursor = 'pointer'; - kickBtn.style.transition = 'background 0.2s ease'; - kickBtn.style.position = 'relative'; - kickBtn.style.zIndex = '1'; - kickBtn.style.marginLeft = '10px'; - - kickBtn.addEventListener('mouseenter', () => { - kickBtn.style.background = '#ff6666'; - }); - kickBtn.addEventListener('mouseleave', () => { - kickBtn.style.background = '#ff4444'; - }); + kickBtn.classList.add('kick-btn'); kickBtn.onclick = async (e) => { e.stopPropagation(); diff --git a/src/styles.css b/src/styles.css index 2c1f36b0..c723711a 100644 --- a/src/styles.css +++ b/src/styles.css @@ -192,7 +192,7 @@ } #chat-input-cancel .icon-cancel { - background-color: var(--danger-red); + background-color: var(--danger-color); } .icon-x { @@ -204,7 +204,7 @@ mask: url('./icons/edit-x.svg') no-repeat center; -webkit-mask-size: contain; mask-size: contain; - background-color: var(--danger-red); + background-color: var(--danger-color); width: 16px; height: 16px; display: inline-block; @@ -307,6 +307,8 @@ :root { --danger-pink: #FF2EA9; --danger-red: #FC595C; + --danger-color: #FC595C; /* default: red for all themes */ + --danger-color-hover: color-mix(in srgb, var(--danger-color) 10%, transparent); font-family: Inter, Avenir, Helvetica, Arial, sans-serif; font-size: 16px; line-height: 24px; @@ -621,9 +623,9 @@ html, body { transition: filter 0.15s ease, transform 0.1s ease; } .pack-details-action.is-subscribed { - background-color: rgba(255, 74, 138, 0.18); - color: #ff4a8a; - border: 1px solid rgba(255, 74, 138, 0.4); + background-color: var(--danger-color-hover); + color: var(--danger-color); + border: 1px solid color-mix(in srgb, var(--danger-color) 40%, transparent); } .pack-details-action:hover { filter: brightness(1.08); } .pack-details-action:active { transform: translateY(1px); } @@ -641,7 +643,7 @@ html, body { .pack-details-error-title { font-size: 16px; font-weight: 700; - color: #ff4a8a; + color: var(--danger-color); margin: 0 0 8px; } .pack-details-error-detail { @@ -1263,6 +1265,12 @@ html, body { cursor: pointer; } +.aggro-glitch-img { + content: url('./icons/aggro-glitch-red.gif'); + width: 64px; + height: 64px; +} + /* Dangerzone button group styling */ .danger-buttons { display: block; @@ -1463,7 +1471,7 @@ html, body { #profile-edit-cancel-btn .icon-edit-x, #wallpaper-edit-cancel-btn .icon-edit-x { - background-color: var(--danger-red) !important; + background-color: var(--danger-color) !important; width: 14px !important; height: 14px !important; } @@ -1620,7 +1628,7 @@ html, body { } .device-status-dot.not-primary { - background-color: #ff5252; /* Red for not primary device */ + background-color: var(--danger-color); /* Red or Pink for not primary device */ } .profile-options { @@ -1655,6 +1663,25 @@ html, body { opacity: 1; } +.profile-option.is-danger span { color: var(--danger-color); } +.profile-option.is-danger .icon { background-color: var(--danger-color); } +.is-danger-text { color: var(--danger-color); } +.is-danger-icon { background-color: var(--danger-color); } +.profile-more-item.is-danger:hover { background: var(--danger-color-hover); } + +.unblock-btn { + color: var(--danger-color); + font-size: 13px; + cursor: pointer; + padding: 4px 10px; + border: 1px solid var(--danger-color); + border-radius: 6px; +} + +.unblock-btn:hover { + background-color: var(--danger-color-hover); +} + /* Profile "More" dropdown menu */ /* Reusable context menu — generalised from `.profile-more-dropdown`. Singleton appended to + positioned absolutely so it can @@ -1696,8 +1723,8 @@ html, body { background: #2a2a2a; color: #fff; } -.context-menu-item.is-danger { color: #ff4a8a; } -.context-menu-item.is-danger:hover { background: rgba(255, 74, 138, 0.12); color: #ff4a8a; } +.context-menu-item.is-danger { color: var(--danger-color); } +.context-menu-item.is-danger:hover { background: var(--danger-color-hover); color: var(--danger-color); } .context-menu-item .icon { position: relative; flex-shrink: 0; @@ -1721,7 +1748,7 @@ html, body { font-size: 15px; } .context-menu-item:active { background: #2a2a2a; color: #fff; } -.context-menu-item.is-danger:active { background: rgba(255, 74, 138, 0.12); color: #ff4a8a; } +.context-menu-item.is-danger:active { background: var(--danger-color-hover); color: var(--danger-color); } /* Dimmed qualifier after an item label, e.g. Copy (plain). */ .context-menu-item-hint { @@ -4109,8 +4136,8 @@ img.emoji-img-loading { } .emoji-creator-cell-busy.is-deleting .emoji-creator-cell-busy-ring { background: conic-gradient( - #ff4a8a 0% 30%, - rgba(255, 74, 138, 0.25) 30% 100% + var(--danger-color) 0% 30%, + color-mix(in srgb, var(--danger-color) 25%, transparent) 30% 100% ); } @keyframes emoji-creator-busy-spin { to { transform: rotate(360deg); } } @@ -4153,7 +4180,7 @@ img.emoji-img-loading { width: 16px; height: 16px; border-radius: 50%; - background-color: #ff4a8a; + background-color: var(--danger-color); border: 2px solid #0a0a0a; padding: 0; cursor: pointer; @@ -4243,11 +4270,11 @@ img.emoji-img-loading { .emoji-creator-delete .icon { width: 16px; height: 16px; - background-color: var(--danger-pink); + background-color: var(--danger-color); } .emoji-creator-delete:hover .icon { - background-color: var(--danger-pink); + background-color: var(--danger-color); } /* Confirm overlay — generalised question modal. Same in-panel chrome @@ -4271,9 +4298,9 @@ img.emoji-img-loading { } .emoji-pack-creator-confirm[data-tone="danger"] { background: - linear-gradient(to top, rgba(255, 74, 138, 0.22) 0%, rgba(255, 74, 138, 0) 38%), + linear-gradient(to top, color-mix(in srgb, var(--danger-color) 22%, transparent) 0%, color-mix(in srgb, var(--danger-color) 0%, transparent) 38%), rgba(8, 8, 8, 0.82); - border-color: #ff4a8a; + border-color: var(--danger-color); } .emoji-pack-creator-confirm[hidden] { display: none; } .emoji-pack-creator-confirm-body { @@ -4299,7 +4326,7 @@ img.emoji-img-loading { color: #fff; } .emoji-pack-creator-confirm[data-tone="danger"] .emoji-pack-creator-confirm-title { - color: #ff4a8a; + color: var(--danger-color); } .emoji-pack-creator-confirm-detail { color: #fff; @@ -4334,13 +4361,13 @@ img.emoji-img-loading { } .emoji-pack-creator-confirm[data-tone="danger"] .emoji-pack-creator-confirm-ok { - background-color: var(--danger-pink); - border-color: var(--danger-pink); + background-color: var(--danger-color); + border-color: var(--danger-color); color: #000; } .emoji-pack-creator-confirm-cancel { - border-color: var(--danger-pink); + border-color: var(--danger-color); background-color: #03030390; } /* Pack creator cropper overlay — square-crop UX for non-square static @@ -4637,11 +4664,11 @@ img.emoji-img-loading { background-color: #030303; } .emoji-pack-creator-naming-input.is-invalid { - border-color: #ff4a8a; - background-color: rgba(255, 74, 138, 0.06); + border-color: var(--danger-color); + background-color: var(--danger-color-hover); } .emoji-pack-creator-naming-error { - color: #ff4a8a; + color: var(--danger-color); font-size: 11px; font-weight: 500; margin: 4px 0 12px; @@ -4689,12 +4716,12 @@ img.emoji-img-loading { inset: 0; border-radius: inherit; background: - linear-gradient(to top, rgba(255, 74, 138, 0.32) 0%, rgba(255, 74, 138, 0) 38%), + linear-gradient(to top, color-mix(in srgb, var(--danger-color) 32%, transparent) 0%, color-mix(in srgb, var(--danger-color) 0%, transparent) 38%), rgba(8, 8, 8, 0.85); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); - border: 2px solid #ff4a8a; - box-shadow: 0 0 24px rgba(255, 74, 138, 0.25) inset; + border: 2px solid var(--danger-color); + box-shadow: 0 0 24px color-mix(in srgb, var(--danger-color) 25%, transparent) inset; display: flex; align-items: center; justify-content: center; @@ -4723,7 +4750,7 @@ img.emoji-img-loading { image-rendering: -webkit-optimize-contrast; } .emoji-pack-creator-error-pretitle { - color: #ff4a8a; + color: var(--danger-color); font-weight: 700; font-size: 13px; margin: 4px 0 2px; @@ -4742,7 +4769,7 @@ img.emoji-img-loading { margin: 0 0 16px; } .emoji-pack-creator-error-retry { - background-color: var(--danger-pink); + background-color: var(--danger-color); color: #000; border: none; padding: 12px 28px; @@ -4992,8 +5019,8 @@ img.emoji-img-loading { } .emoji-pack-preview.is-error { - border-color: rgba(255, 100, 100, 0.25); - background-color: rgba(255, 60, 60, 0.05); + border-color: color-mix(in srgb, var(--danger-color) 25%, transparent); + background-color: color-mix(in srgb, var(--danger-color) 5%, transparent); } @media (max-width: 480px) { @@ -6416,7 +6443,7 @@ select { } .voice-preview-delete .icon { - background-color: #ff4444; + background-color: var(--danger-color); transition: opacity 0.15s ease; } @@ -7559,7 +7586,7 @@ select:disabled:hover { background-color: #00000000; margin-left: 10px; backdrop-filter: none; - border-color: var(--danger-pink); + border-color: var(--danger-color); background-color: #171717; } @@ -7824,7 +7851,7 @@ select:disabled:hover { } .danger-subtitle { - color: var(--danger-pink); + color: var(--danger-color); font-size: 12px; text-align: center; margin-top: 5px; @@ -7840,10 +7867,16 @@ select:disabled:hover { .danger-btn { backdrop-filter: none; - border-color: var(--danger-pink); + border-color: var(--danger-color); background-color: #00000000; } +.warning-icon { + content: url('./icons/warning_red.png'); + width: 16px; + height: 16px; +} + .navbar { position: fixed; bottom: 0px; @@ -9371,7 +9404,7 @@ select:disabled:hover { } .blossom-cap-accepted { color: #59fcb3; } .blossom-cap-limited { color: #ffc107; } -.blossom-cap-rejected { color: #ff6464; } +.blossom-cap-rejected { color: var(--danger-color); } .blossom-cap-list { list-style: none; padding: 0; @@ -9395,7 +9428,7 @@ select:disabled:hover { flex-shrink: 0; } .blossom-cap-marker.blossom-cap-accepted { color: #59fcb3; } -.blossom-cap-marker.blossom-cap-rejected { color: #ff6464; } +.blossom-cap-marker.blossom-cap-rejected { color: var(--danger-color); } .blossom-cap-mime { flex: 1; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; @@ -9916,6 +9949,25 @@ select:disabled:hover { opacity: 0.7; } +.kick-btn { + padding: 4px 12px; + font-size: 12px; + border-radius: 4px; + border: 1px solid var(--danger-color); + background: none; + color: var(--danger-color); + cursor: pointer; + transition: background 0.2s ease; + position: relative; + z-index: 1; + margin-left: 10px; +} + +.kick-btn:hover { + background: var(--danger-color); + color: #000; +} + /* MLS Group Invite Styles */ .chatlist-invite { cursor: default !important; @@ -9958,7 +10010,7 @@ select:disabled:hover { } .invite-decline-btn { - border: 2px solid var(--danger-pink); + border: 2px solid var(--danger-color); } .invite-action-btn:hover { @@ -12751,7 +12803,7 @@ hr { } .marketplace-install-btn.failed { - background-color: rgba(255, 100, 100, 0.3); + background-color: var(--danger-color-hover); } /* Loading spinner in install/update buttons */ @@ -13536,14 +13588,14 @@ hr { /* App Details Uninstall Button */ .app-details-uninstall-btn { flex: 1; - background-color: rgba(255, 100, 100, 0.3); - color: #ff6464; - border: 1px solid rgba(255, 100, 100, 0.5); + background-color: var(--danger-color-hover); + color: var(--danger-color); + border: 1px solid color-mix(in srgb, var(--danger-color) 50%, transparent); } .app-details-uninstall-btn:hover { - background-color: rgba(255, 100, 100, 0.4); - box-shadow: 0 4px 16px rgba(255, 100, 100, 0.3); + background-color: color-mix(in srgb, var(--danger-color) 40%, transparent); + box-shadow: 0 4px 16px color-mix(in srgb, var(--danger-color) 30%, transparent); } /* App Details Update Button */ @@ -13826,10 +13878,10 @@ hr { .app-details-reset-permissions-btn { margin-top: 12px; padding: 8px 16px; - background: rgba(255, 100, 100, 0.15); - border: 1px solid rgba(255, 100, 100, 0.3); + background: color-mix(in srgb, var(--danger-color) 15%, transparent); + border: 1px solid color-mix(in srgb, var(--danger-color) 30%, transparent); border-radius: 8px; - color: rgba(255, 150, 150, 0.9); + color: var(--danger-color); font-size: 13px; font-weight: 500; cursor: pointer; @@ -13838,9 +13890,9 @@ hr { } .app-details-reset-permissions-btn:hover { - background: rgba(255, 100, 100, 0.25); - border-color: rgba(255, 100, 100, 0.5); - color: rgba(255, 180, 180, 1); + background: color-mix(in srgb, var(--danger-color) 25%, transparent); + border-color: color-mix(in srgb, var(--danger-color) 50%, transparent); + color: var(--danger-color); } /* Permission Prompt Overlay */ @@ -14022,8 +14074,8 @@ hr { } .notif-sound-chip-clear:hover { - background: rgba(255, 100, 100, 0.3); - color: #ff6b6b; + background: var(--danger-color-hover); + color: var(--danger-color); } /* --- Mention Selector --- */ @@ -14246,8 +14298,8 @@ hr { --dmsg-pinged-border: var(--icon-color-primary, #59fcb3); --dmsg-replying-bg: rgba(255, 255, 255, 0.05); --dmsg-replying-border: rgba(255, 255, 255, 0.5); - --dmsg-failed-bg: rgba(255, 100, 100, 0.10); - --dmsg-failed-border: #ff6464; + --dmsg-failed-bg: color-mix(in srgb, var(--danger-color) 10%, transparent); + --dmsg-failed-border: var(--danger-color); } .dmsg { @@ -14623,8 +14675,8 @@ hr { vertical-align: middle; position: static; } -.dmsg-status-failed { color: #ff6464; } -.dmsg-status-failed .icon { background-color: #ff6464; } +.dmsg-status-failed { color: var(--danger-color); } +.dmsg-status-failed .icon { background-color: var(--danger-color); } .dmsg-status-hidden { display: none !important; } /* beat the block-right sibling rules above */ .dmsg-failed-action { cursor: pointer; @@ -14890,7 +14942,7 @@ hr { .wallpaper-remove-btn .icon { width: 20px; height: 20px; - background-color: #ff4444; + background-color: var(--danger-color); transition: opacity 0.15s ease; } .wallpaper-remove-btn:hover .icon { @@ -14947,10 +14999,10 @@ hr { distinctly from the additive ones (react/reply/edit) without tinting the whole button. */ .dmsg-toolbar-btn-danger .icon { - background-color: var(--danger-red); + background-color: var(--danger-color); } .dmsg-toolbar-btn-danger:hover .icon { - background-color: var(--danger-red); + background-color: var(--danger-color); filter: brightness(1.15); } diff --git a/src/themes/cyberpunk/dark.css b/src/themes/cyberpunk/dark.css index c95bdd86..bd2e845b 100644 --- a/src/themes/cyberpunk/dark.css +++ b/src/themes/cyberpunk/dark.css @@ -11,6 +11,7 @@ --primary-color: #59FCF4; --secondary-color: #FC59AD; --accent-color: #59FCF4; + --danger-color: #FC59AD; } .sync-line { @@ -601,14 +602,14 @@ select:disabled { /* App Details Uninstall Button */ .app-details-uninstall-btn { - background-color: #FC59AD20; - color: #FC59AD; - border: 1px solid #FC59AD40; + background-color: var(--danger-color-hover); + color: var(--danger-color); + border: 1px solid color-mix(in srgb, var(--danger-color) 40%, transparent); } .app-details-uninstall-btn:hover { - background-color: rgba(255, 100, 100, 0.3); - box-shadow: 0 4px 16px rgba(255, 100, 100, 0.2); + background-color: color-mix(in srgb, var(--danger-color) 40%, transparent); + box-shadow: 0 4px 16px color-mix(in srgb, var(--danger-color) 20%, transparent); } /* App Details Update Button */ @@ -698,3 +699,15 @@ select:disabled { .relay-metric-value { color: #59FCF4; } + +.aggro-glitch-img { + content: url('../../icons/aggro-glitch.gif'); + width: 64px; + height: 64px; +} + +.warning-icon { + content: url('../../icons/warning_pink.png'); + width: 16px; + height: 16px; +} diff --git a/src/themes/vector/dark.css b/src/themes/vector/dark.css index 0d078b85..0375bd0b 100644 --- a/src/themes/vector/dark.css +++ b/src/themes/vector/dark.css @@ -8,6 +8,7 @@ --toast-border-color: #33DB98; --primary-color: #33DB98; --accent-color: #59fcb3; + --danger-color: #FF2EA9; } .sync-line { @@ -602,3 +603,15 @@ select:disabled { .relay-metric-value { color: #59fcb3; } + +.aggro-glitch-img { + content: url('../../icons/aggro-glitch.gif'); + width: 64px; + height: 64px; +} + +.warning-icon { + content: url('../../icons/warning_pink.png'); + width: 16px; + height: 16px; +} From 0e16f321e9c03b91a0866f8246fac066cb55822e Mon Sep 17 00:00:00 2001 From: Yuurin Bee Date: Wed, 27 May 2026 09:06:46 +0700 Subject: [PATCH 03/22] Fix My Profile Scroll Bar Range --- src/styles.css | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/styles.css b/src/styles.css index c723711a..8970b7b8 100644 --- a/src/styles.css +++ b/src/styles.css @@ -1047,11 +1047,13 @@ html, body { /* Profile Content - Scrollable */ .profile-content { - padding-top: 60px; - padding-bottom: 60px; + position: fixed; + top: 60px; + bottom: 65px; + left: 0; + right: 0; overflow-x: hidden; overflow-y: auto; - height: 100%; -webkit-overflow-scrolling: touch; overscroll-behavior: none; touch-action: pan-y; From 3f7246814c55c589f1fa459205e77f7cac73fb41 Mon Sep 17 00:00:00 2001 From: Yuurin Bee Date: Wed, 27 May 2026 09:30:09 +0700 Subject: [PATCH 04/22] Fix Settings Scroll Bar Range --- src/styles.css | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/styles.css b/src/styles.css index 8970b7b8..d7f50249 100644 --- a/src/styles.css +++ b/src/styles.css @@ -5914,9 +5914,12 @@ audio { } #settings { + position: fixed; + top: 10px; + bottom: 65px; + left: 0; + right: 2px; overflow-y: auto; - height: 100%; - padding-bottom: 80px; overscroll-behavior: none; -webkit-overflow-scrolling: touch; touch-action: pan-y; From 62eddf571fe71bd46fa2df1c4723eba55a5a0807 Mon Sep 17 00:00:00 2001 From: Yuurin Bee Date: Wed, 27 May 2026 09:38:31 +0700 Subject: [PATCH 05/22] Fix Chat Page Scroll Bar Range --- src/styles.css | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/styles.css b/src/styles.css index d7f50249..b95c5951 100644 --- a/src/styles.css +++ b/src/styles.css @@ -2159,7 +2159,12 @@ html, body { } #chat-list { - overflow-y: scroll; + position: fixed; + top: 120px; + bottom: 70px; + left: 0; + right: 2px; + overflow-y: auto; padding-left: 15px; padding-right: 15px; } From f2f674297e2b4fa2490206b86ca16644ef828b2b Mon Sep 17 00:00:00 2001 From: Yuurin Bee Date: Wed, 27 May 2026 09:57:33 +0700 Subject: [PATCH 06/22] Final Polish for Profile Scroll Bar Range --- src/styles.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/styles.css b/src/styles.css index b95c5951..fd76c4af 100644 --- a/src/styles.css +++ b/src/styles.css @@ -1048,10 +1048,10 @@ html, body { /* Profile Content - Scrollable */ .profile-content { position: fixed; - top: 60px; + top: 64px; bottom: 65px; left: 0; - right: 0; + right: 2px; overflow-x: hidden; overflow-y: auto; -webkit-overflow-scrolling: touch; From 368e0f4c85e2dc129904176d32aea164a1b0e602 Mon Sep 17 00:00:00 2001 From: Yuurin Bee Date: Wed, 27 May 2026 13:12:56 +0700 Subject: [PATCH 07/22] Fix: Constrain Scroll Bars on Profile, Chat, and Settings --- src/main.js | 2 ++ src/styles.css | 12 ++++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/main.js b/src/main.js index 3e3b8a83..7a8abefb 100644 --- a/src/main.js +++ b/src/main.js @@ -4627,6 +4627,7 @@ function renderProfileTab(cProfile) { // If this is OUR profile: make the elements clickable, hide the "Contact Options" if (cProfile.mine) { + document.getElementById('profile').classList.add('is-own-profile'); // Hide Contact Options domProfileOptions.style.display = 'none'; @@ -4679,6 +4680,7 @@ function renderProfileTab(cProfile) { domProfileDescription.onclick = () => { if (fProfileEditMode) editProfileDescription(); }; domProfileDescription.classList.add('btn'); } else { + document.getElementById('profile').classList.remove('is-own-profile'); // Show Contact Options domProfileOptions.style.display = ''; document.getElementById('profile-header-avatar-container').style.display = ''; diff --git a/src/styles.css b/src/styles.css index fd76c4af..a63524de 100644 --- a/src/styles.css +++ b/src/styles.css @@ -1048,10 +1048,10 @@ html, body { /* Profile Content - Scrollable */ .profile-content { position: fixed; - top: 64px; + top: 60px; bottom: 65px; left: 0; - right: 2px; + right: 0px; overflow-x: hidden; overflow-y: auto; -webkit-overflow-scrolling: touch; @@ -1060,6 +1060,10 @@ html, body { box-sizing: border-box; } +#profile:not(.is-own-profile) .profile-content { + bottom: 6px; +} + /* Profile Banner */ .profile-banner { object-fit: cover; @@ -1911,7 +1915,7 @@ html, body { align-items: center; width: 85%; gap: 4px; - margin: 8px auto 20px auto; + margin: 8px auto 40px auto; padding: 4px 4px 4px 8px; } @@ -5921,7 +5925,7 @@ audio { #settings { position: fixed; top: 10px; - bottom: 65px; + bottom: 70px; left: 0; right: 2px; overflow-y: auto; From ce1c812b1ae1ef2ce7432a3af32d989534029a5f Mon Sep 17 00:00:00 2001 From: Yuurin Bee Date: Wed, 27 May 2026 13:19:21 +0700 Subject: [PATCH 08/22] Fix: Guard against null platformFeatures in picket tooltip check --- src/js/picker.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/picker.js b/src/js/picker.js index 6dfd7505..ed54ac0a 100644 --- a/src/js/picker.js +++ b/src/js/picker.js @@ -1496,7 +1496,7 @@ let _emojiTooltipCurrentAnchor = null; * `platformFeatures.is_mobile` is Vector's canonical desktop/mobile split * (set up in main.js at boot) and is used by every other hover-only path. */ function _supportsHoverTooltip() { - return typeof platformFeatures !== 'undefined' && !platformFeatures.is_mobile; + return typeof platformFeatures !== 'undefined' && platformFeatures !== null && !platformFeatures.is_mobile; } function _ensureEmojiTooltip() { From 16dca06e22012caa389a04eb8e3b724a65f62673 Mon Sep 17 00:00:00 2001 From: Yuurin Bee Date: Thu, 28 May 2026 01:18:14 +0700 Subject: [PATCH 09/22] Fix: AI Model Download Cancel Flow & UI --- src/js/settings.js | 77 ++++++++------------------------------ src/js/voice.js | 10 ++++- src/styles.css | 41 +++++++++++++++++++- src/themes/vector/dark.css | 4 +- 4 files changed, 65 insertions(+), 67 deletions(-) diff --git a/src/js/settings.js b/src/js/settings.js index 5947ed64..f35defe4 100644 --- a/src/js/settings.js +++ b/src/js/settings.js @@ -852,28 +852,17 @@ class VoiceSettings { // Update button text to include model size const sizeInBytes = model.model.size * 1024 * 1024; const formattedSize = formatBytes(sizeInBytes); - downloadBtn.textContent = `Download Selected Model (${formattedSize})`; + downloadBtn.textContent = `Download Model (${formattedSize})`; } } - async downloadModel(modelName) { - // Find the model in our cached list and validate it's not already downloaded + async downloadModel(modelName) { const model = this.models.find(m => m.model.name === modelName); if (!model || model.downloaded) return; - // Get UI elements for progress display const modelStatus = document.getElementById('model-status'); - const progressContainer = document.querySelector('.download-progress-container'); - const progressFill = progressContainer.querySelector('.progress-bar-fill'); - const progressText = progressContainer.querySelector('.progress-text'); - // Initialize download UI state - modelStatus.textContent = `Downloading AI model...`; - progressContainer.style.display = 'block'; - progressFill.style.width = '0%'; - progressText.textContent = `0 / ${model.model.size} MB`; - - // Disable UI during download to prevent user interference + // Disable UI during download document.getElementById('download-model').style.display = 'none'; document.getElementById('delete-model').style.display = 'none'; document.getElementById('whisper-model').disabled = true; @@ -886,76 +875,42 @@ class VoiceSettings { } try { - // Mark model as downloading and update status display model.downloading = true; - this.updateModelStatus(); - // Set up Tauri event listener for download progress updates + // Set up the download UI once + modelStatus.innerHTML = `
Downloading... 0%
`; + + // Set up progress listener — updates text only, not the spinner const unlisten = await window.__TAURI__.event.listen( 'whisper_download_progress', (event) => { - const { progress, downloaded_bytes, total_bytes, speed_bps } = event.payload; - progressFill.style.width = `${progress}%`; - - const dlMB = (downloaded_bytes / (1024 * 1024)).toFixed(1); - const totalMB = (total_bytes / (1024 * 1024)).toFixed(1); - const speedMBs = (speed_bps / (1024 * 1024)).toFixed(1); - progressText.textContent = `${dlMB} / ${totalMB} MB \u2014 ${speedMBs} MB/s`; - - const progressionSpan = document.getElementById('voice-model-download-progression'); - if (progressionSpan) progressionSpan.textContent = `(${Math.round(progress)}%)`; + const { progress } = event.payload; + const textEl = document.getElementById('download-progress-text'); + if (textEl) textEl.textContent = ` Downloading... ${Math.round(progress)}%`; } ); - // Start the actual download via Tauri backend await invoke('download_whisper_model', { modelName }); - - // Clean up event listener to prevent memory leaks unlisten(); - // Update model state to reflect successful download model.downloaded = true; model.downloading = false; - modelStatus.textContent = `Successfully downloaded AI model!`; - - // Show success animation with green gradient - progressFill.style.width = `100%`; - progressText.textContent = `Complete`; - progressFill.style.background = 'linear-gradient(90deg, #59fcb3 0%, #2b976c 100%)'; - progressContainer.style.animation = 'none'; - void progressContainer.offsetWidth; // Force reflow to reset animation - progressContainer.style.animation = 'fadeIn 0.3s ease-out'; - - // Refresh the models dropdown and update UI state + modelStatus.innerHTML = `
Vector AI is ready
`; await this.loadWhisperModels(); this.updateModelStatus(); + } catch (error) { - // Handle download failures model.downloading = false; const isCancelled = String(error).includes('cancelled'); - modelStatus.textContent = isCancelled ? 'Download cancelled' : `Error downloading model: ${error}`; + modelStatus.innerHTML = isCancelled + ? `
AI model is not downloaded
` + : `
Download failed: ${escapeHtml(String(error))}
`; if (!isCancelled) console.error('Download failed:', error); - - // Show error/cancelled state - progressFill.style.background = isCancelled - ? 'linear-gradient(90deg, #888 0%, #555 100%)' - : 'linear-gradient(90deg, #ff5e5e 0%, #d40000 100%)'; - progressText.textContent = isCancelled ? 'Cancelled' : 'Failed'; + this.updateModelStatus(); } finally { - // Hide cancel button and re-enable model selector if (cancelBtn) cancelBtn.style.display = 'none'; document.getElementById('whisper-model').disabled = false; - - // Clean up progress bar after a delay, regardless of success/failure - setTimeout(() => { - progressContainer.style.display = 'none'; - // Reset progress bar styling for next download - progressFill.style.width = '0%'; - progressFill.style.background = 'linear-gradient(90deg, #59fcb3 0%, #00d4ff 100%)'; - progressText.textContent = '0%'; - this.updateModelStatus(); - }, 3000); } } diff --git a/src/js/voice.js b/src/js/voice.js index 02ecfe39..be817d4b 100644 --- a/src/js/voice.js +++ b/src/js/voice.js @@ -972,7 +972,8 @@ class VoiceTranscriptionUI { async ensureModelReady(transcribeBtn) { if (this.isSettingUp) return false; - const selectedModel = window.voiceSettings?.selectedModel || 'small'; + const selectedModel = window.voiceSettings?.selectedModel; + if (!selectedModel) return false; const model = window.voiceSettings?.models?.find(m => m.model.name === selectedModel); if (model?.downloaded) return true; @@ -1024,6 +1025,13 @@ class VoiceTranscriptionUI { progressContainer.appendChild(progressText); progressContainer.appendChild(progressBar); + // Allows user to cancel download + const cancelBtn = document.createElement('button'); + cancelBtn.textContent = 'Cancel'; + cancelBtn.classList.add('cancel-download-inline'); + cancelBtn.onclick = () => invoke('cancel_whisper_download'); + progressContainer.appendChild(cancelBtn); + // Replace button contents with progress indicator transcribeBtn.style.marginLeft = 'auto'; transcribeBtn.style.marginRight = 'auto'; diff --git a/src/styles.css b/src/styles.css index a63524de..eb2e9632 100644 --- a/src/styles.css +++ b/src/styles.css @@ -5972,7 +5972,7 @@ select { #download-model { color: #000; border: none; - padding: 12px 20px; + padding: 12px; border-radius: 8px; font-weight: bold; cursor: pointer; @@ -5981,7 +5981,9 @@ select { align-items: center; justify-content: center; margin-top: 10px; - width: 100%; + width: 80%; + margin-left: auto; + margin-right: auto; } #download-model:active { @@ -5995,6 +5997,23 @@ select { min-height: 20px; } +#cancel-download { + margin-top: 16px; +} + +/* Inline spinner for model download status */ +.spinner { + display: inline-block; + width: 12px; + height: 12px; + border: 2px solid rgba(255, 255, 255, 0.1); + border-top-color: var(--icon-color-primary); + border-radius: 50%; + animation: spin 0.8s linear infinite; + vertical-align: middle; + margin-right: 6px; +} + /* Progress Bar Container */ .download-progress-container { margin-top: 20px; @@ -8732,6 +8751,24 @@ select:disabled:hover { text-overflow: ellipsis; } +.cancel-download-inline { + display: block; + margin: 6px auto 0; + padding: 3px 12px; + font-size: 11px; + font-weight: 500; + border-radius: 4px; + border: 1px solid var(--danger-color); + background: none; + color: var(--danger-color); + cursor: pointer; + transition: background 0.2s ease; +} + +.cancel-download-inline:hover { + background: var(--danger-color-hover); +} + /* Custom Audio Player Styles */ .custom-audio-player { gap: 12px; diff --git a/src/themes/vector/dark.css b/src/themes/vector/dark.css index 0375bd0b..21340bc6 100644 --- a/src/themes/vector/dark.css +++ b/src/themes/vector/dark.css @@ -141,9 +141,7 @@ select { } #download-model:hover { - box-shadow: 0 0 5px rgba(0, 255, 157, 0.5), - 0 0 10px rgba(0, 212, 255, 0.3), - inset 0 1px 3px rgba(0, 0, 0, 0.3); + opacity: 0.8; } .model-status { From 49f06745d2ebe05a361c22ab4aabd15c2139448b Mon Sep 17 00:00:00 2001 From: Yuurin Bee Date: Thu, 28 May 2026 01:26:01 +0700 Subject: [PATCH 10/22] Refactor: Simplify Model Download/Delete UI Toast Messages --- src/js/settings.js | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/js/settings.js b/src/js/settings.js index f35defe4..4b234d8f 100644 --- a/src/js/settings.js +++ b/src/js/settings.js @@ -815,17 +815,10 @@ class VoiceSettings { await invoke('delete_whisper_model', { modelName }); await this.loadWhisperModels(); this.updateModelStatus(); - - // Show toast with fallback info - const fallback = this.models.find(m => m.model.name === this.selectedModel && m.downloaded); - if (fallback && fallback.model.name !== modelName) { - showToast(`Deleted — Now using ${fallback.model.display_name}`); - } else { - showToast(`Deleted ${modelName} model`); - } + showToast('Model Deleted'); } catch (error) { - console.error('Failed to delete model:', error); - await popupConfirm('Deletion Failed', `Could not delete model: ${escapeHtml(String(error.message))}`, true, '', 'vector_warning.svg'); + console.error('Failed to Delete Model:', error); + await popupConfirm('Deletion Failed', `Could not Delete Model: ${escapeHtml(String(error.message))}`, true, '', 'vector_warning.svg'); } } From c60c3faaf4fba8c7f5c0554f92ea7b354805a3b4 Mon Sep 17 00:00:00 2001 From: Yuurin Bee Date: Thu, 28 May 2026 01:29:39 +0700 Subject: [PATCH 11/22] Update Transcribe Progress Colors --- src/styles.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/styles.css b/src/styles.css index eb2e9632..f647e371 100644 --- a/src/styles.css +++ b/src/styles.css @@ -8737,7 +8737,7 @@ select:disabled:hover { .transcribe-progress-fill { height: 100%; width: 0%; - background: linear-gradient(90deg, #59fcb3, #00d4ff); + background: linear-gradient(90deg, var(--primary-color), var(--accent-color)); border-radius: 2px; transition: width 0.3s ease; } From 5316874f6003876b46d23311e8a6f18495c5b251 Mon Sep 17 00:00:00 2001 From: Yuurin Bee Date: Thu, 28 May 2026 08:02:08 +0700 Subject: [PATCH 12/22] Refactor: AI Model Settings, UI, Audio Transcriptions & More --- src/index.html | 2 +- src/js/voice.js | 6 +++++- src/styles.css | 19 ++++++++++++++----- src/themes/chatstr/dark.css | 4 ++-- src/themes/cyberpunk/dark.css | 15 +++------------ src/themes/gifverse/dark.css | 10 +++++----- src/themes/monero/dark.css | 19 +++++++------------ src/themes/pivx/dark.css | 18 +++++++++--------- src/themes/satoshi/dark.css | 21 +++++++++++---------- src/themes/vector/dark.css | 10 +++------- 10 files changed, 60 insertions(+), 64 deletions(-) diff --git a/src/index.html b/src/index.html index a197b389..972a99c2 100644 --- a/src/index.html +++ b/src/index.html @@ -483,7 +483,7 @@

App Details

- +
diff --git a/src/js/voice.js b/src/js/voice.js index be817d4b..5cd64329 100644 --- a/src/js/voice.js +++ b/src/js/voice.js @@ -1070,7 +1070,7 @@ class VoiceTranscriptionUI { return true; } catch (error) { - progressText.textContent = `Download failed`; + progressText.textContent = `Download Failed`; progressFill.style.background = '#ff5e5e'; setTimeout(() => { transcribeBtn.classList.remove('downloading'); @@ -1536,6 +1536,7 @@ function handleAudioAttachment(cAttachment, pMessage, msg) { } currentTimeEl.textContent = formatTime(posMs / 1000); + currentTimeEl.style.color = posMs > 0 ? '#ffffffb3' : ''; // Highlight active transcription section if visible const tContainer = audioContainer.querySelector('.transcription-result:not(.hidden)'); @@ -1744,6 +1745,7 @@ function handleAudioAttachment(cAttachment, pMessage, msg) { // Update visuals immediately (no IPC) currentTimeEl.textContent = formatTime(posMs / 1000); + currentTimeEl.style.color = posMs > 0 ? '#ffffffb3' : ''; if (!customPlayer.classList.contains('playing') && durationMs > 0) { const currentProgress = posMs / durationMs; bars.forEach((bar, i) => { @@ -1826,6 +1828,7 @@ function handleAudioAttachment(cAttachment, pMessage, msg) { playBtn.innerHTML = ''; customPlayer.classList.remove('playing'); currentTimeEl.textContent = '0:00'; + currentTimeEl.style.color = ''; // Clear transcription highlighting const tContainer = audioContainer.querySelector('.transcription-result'); @@ -1889,6 +1892,7 @@ function handleAudioAttachment(cAttachment, pMessage, msg) { playStartPos = ms; } currentTimeEl.textContent = formatTime(ms / 1000); + currentTimeEl.style.color = ms > 0 ? '#ffffffb3' : ''; } }; diff --git a/src/styles.css b/src/styles.css index f647e371..02e89afb 100644 --- a/src/styles.css +++ b/src/styles.css @@ -5992,7 +5992,7 @@ select { /* Model Status Styles */ .model-status { - margin-top: 15px; + margin-top: 0px; font-size: 14px; min-height: 20px; } @@ -6536,7 +6536,7 @@ select { top: 0; bottom: 0; width: 0%; - background: linear-gradient(90deg, var(--icon-color-primary), var(--voice-progress-secondary, rgba(89, 252, 179, 0.4))); + background: var(--primary-color); border-radius: 5px 0 0 5px; pointer-events: none; } @@ -7548,6 +7548,10 @@ select:focus::-ms-value { display: none; /* Hidden by default */ } +.btn-delete-model .icon-trash { + background-color: var(--danger-color); +} + .btn-delete-model:hover { opacity: 1; } @@ -7597,6 +7601,10 @@ select:focus::-ms-value { box-shadow: none !important; } +#whisper-model.form-control { + text-align: center !important; +} + /* Disabled select styles for visual feedback */ select:disabled { opacity: 0.5; @@ -8737,7 +8745,7 @@ select:disabled:hover { .transcribe-progress-fill { height: 100%; width: 0%; - background: linear-gradient(90deg, var(--primary-color), var(--accent-color)); + background: var(--primary-color); border-radius: 2px; transition: width 0.3s ease; } @@ -8773,7 +8781,7 @@ select:disabled:hover { .custom-audio-player { gap: 12px; padding: 12px; - background: rgba(22, 22, 22, 0.6); + background: #17171750; border: 1px solid rgba(89, 252, 179, 0.2); border-radius: 12px; position: relative; @@ -8781,6 +8789,7 @@ select:disabled:hover { width: min(500px, calc(100vw - 100px)); box-sizing: border-box; transition: all 0.3s ease; + margin-bottom: 6px; } .custom-audio-player-inner { @@ -8975,7 +8984,7 @@ select:disabled:hover { } .audio-time-display .current-time { - color: #59fcb3; + color: #ffffff66; font-weight: bold; } diff --git a/src/themes/chatstr/dark.css b/src/themes/chatstr/dark.css index c9e290ec..22dd19b5 100644 --- a/src/themes/chatstr/dark.css +++ b/src/themes/chatstr/dark.css @@ -245,11 +245,11 @@ select:disabled { } .transcribe-progress-fill { - background: linear-gradient(90deg, #5749E4, #b188e2); + background: var(--accent-color); } .transcribe-progress-text { - color: #b188e2; + color: #fff; } .transcription-section.current { diff --git a/src/themes/cyberpunk/dark.css b/src/themes/cyberpunk/dark.css index bd2e845b..c27aeeaf 100644 --- a/src/themes/cyberpunk/dark.css +++ b/src/themes/cyberpunk/dark.css @@ -226,14 +226,6 @@ select { box-shadow: 0 0 15px #59FCF430; } -.voice-progress-fill { - background: linear-gradient(90deg, var(--primary-color), var(--secondary-color)); -} - -.voice-preview-progress { - background: linear-gradient(90deg, var(--primary-color), var(--secondary-color)); -} - .voice-progress-text { color: #59FCF4; } @@ -275,7 +267,6 @@ select:disabled { /* Default style */ .custom-audio-player { background: #59FCF420; - backdrop-filter: blur(6px); box-shadow: 0 0 12px #00000050, 0 0 24px #00000025; border-color: #59FCF440; } @@ -286,11 +277,11 @@ select:disabled { } .audio-play-btn { - background: linear-gradient(135deg, #59FCF4 0%, #59FCF4 100%); + background: var(--primary-color); } .audio-progress-fill { - background: linear-gradient(90deg, #59FCF4 0%, #59FCF4 80%); + background: var(--primary-color); } .audio-progress-handle { @@ -318,7 +309,7 @@ select:disabled { } .transcribe-progress-text { - color: #59FCF4; + color: #fff; } .transcription-section.current { diff --git a/src/themes/gifverse/dark.css b/src/themes/gifverse/dark.css index 72d7294b..e900e0cb 100644 --- a/src/themes/gifverse/dark.css +++ b/src/themes/gifverse/dark.css @@ -225,13 +225,13 @@ select:disabled { /* Custom Audio Player Theme Overrides */ /* Default style */ .custom-audio-player { - background: #F7FFAE40; - border-color: #F7FFAE; + background: #F7FFAE30; + border-color: #F7FFAE50; } .custom-audio-player:hover { - background: #F7FFAE60; - border-color: #F7FFAE50; + background: #F7FFAE40; + border-color: #F7FFAE70; } .audio-play-btn { @@ -267,7 +267,7 @@ select:disabled { } .transcribe-progress-text { - color: #FFAECE; + color: #fff; } .transcription-section.current { diff --git a/src/themes/monero/dark.css b/src/themes/monero/dark.css index 1fda6c04..b59970e1 100644 --- a/src/themes/monero/dark.css +++ b/src/themes/monero/dark.css @@ -193,7 +193,7 @@ select { } #download-model { - background: linear-gradient(135deg, #FF6600 0%, #FF6600 80%); + background: var(--primary-color); } #download-model:hover { @@ -201,7 +201,7 @@ select { } .model-status { - color: #FF6600; + color: var(--primary-color); } .progress-bar-track { @@ -225,10 +225,6 @@ select { box-shadow: 0 0 15px #FF660030; } -.voice-progress-fill { - background: linear-gradient(90deg, #FF6600, #FF660025); -} - .voice-progress-text { color: #FF6600; } @@ -270,13 +266,12 @@ select:disabled { /* Default style */ .custom-audio-player { background: #FF660020; - backdrop-filter: blur(6px); box-shadow: 0 0 12px #00000050, 0 0 24px #00000025; border-color: #FF660040; } .custom-audio-player:hover { - background: #FF660060; + background: #FF660040; border-color: #FF660050; } @@ -285,7 +280,7 @@ select:disabled { } .audio-progress-fill { - background: linear-gradient(90deg, #FF6600 0%, #FF6600 80%); + background: var(--primary-color); } .audio-progress-handle { @@ -305,15 +300,15 @@ select:disabled { } .audio-transcribe-btn .icon { - background-color: #FF6600; + background-color: var(--primary-color); } .transcribe-progress-fill { - background: linear-gradient(90deg, #FF6600, #FF660080); + background: var(--primary-color); } .transcribe-progress-text { - color: #FF6600; + color: #fff; } .transcription-section.current { diff --git a/src/themes/pivx/dark.css b/src/themes/pivx/dark.css index 074a943a..033e0b8f 100644 --- a/src/themes/pivx/dark.css +++ b/src/themes/pivx/dark.css @@ -125,7 +125,7 @@ input, textarea, button { - background-color: #161616; + background-color: #171717; } .corner-float { @@ -153,11 +153,11 @@ select { } #download-model { - background: linear-gradient(135deg, #6202D4 0%, #1D003F 100%); + background: var(--primary-color); } #download-model:hover { - box-shadow: 0 5px 15px #6202D430; + opacity: 0.8; } .model-status { @@ -239,11 +239,11 @@ select:disabled { } .audio-play-btn { - background: linear-gradient(135deg, #B359FC 0%, #6202D4 100%); + background: var(--accent-color); } .audio-progress-fill { - background: linear-gradient(90deg, #6202D4 0%, #1D003F 100%); + background: var(--accent-color); } .audio-progress-handle { @@ -263,15 +263,15 @@ select:disabled { } .audio-transcribe-btn .icon { - background-color: #6202D4; + background-color: var(--accent-color); } .transcribe-progress-fill { - background: linear-gradient(90deg, #6202D4, #1D003F); + background: var(--accent-color); } .transcribe-progress-text { - color: #6202D4; + color: #fff; } .transcription-section.current { @@ -381,7 +381,7 @@ select:disabled { } .marketplace-app-card { - background-color: #161616; + background-color: #171717; border-color: rgba(90, 252, 180, 0.15); } diff --git a/src/themes/satoshi/dark.css b/src/themes/satoshi/dark.css index fd9f52ab..c78293d1 100644 --- a/src/themes/satoshi/dark.css +++ b/src/themes/satoshi/dark.css @@ -206,21 +206,21 @@ select:disabled { /* Custom Audio Player Theme Overrides */ /* Default style */ .custom-audio-player { - background: rgba(37, 17, 52, 0.4); - border-color: rgba(177, 136, 226, 0.25); + background: #F7931A40; + border-color: #F7931A25; } .custom-audio-player:hover { - background: rgba(37, 17, 52, 0.6); - border-color: rgba(177, 136, 226, 0.5); + background: #F7931A60; + border-color: #F7931A50; } .audio-play-btn { - background: linear-gradient(135deg, #F9AA4B 0%, #F7931A 100%); + background: var(--primary-color); } .audio-progress-fill { - background: linear-gradient(90deg, #F7931A 0%, #B56C12 100%); + background: var(--primary-color); } .audio-progress-handle { @@ -248,13 +248,13 @@ select:disabled { } .transcribe-progress-text { - color: #F7931A; + color: #fff; } .transcription-section.current { - background-color: rgba(177, 136, 226, 0.3); + background-color: #F7931A30; color: #F7931A; - box-shadow: 0 0 8px rgba(177, 136, 226, 0.2); + box-shadow: 0 0 8px #F7931A20; } /* Update Settings Section Styles */ @@ -324,7 +324,8 @@ select:disabled { .file-preview-btn-send { background: #F7931A; - color: #ffffff; + color: #000; + font-weight: 600; } .file-preview-btn-send:hover { diff --git a/src/themes/vector/dark.css b/src/themes/vector/dark.css index 21340bc6..c9c3cdc0 100644 --- a/src/themes/vector/dark.css +++ b/src/themes/vector/dark.css @@ -36,7 +36,7 @@ } .chat { - background-color: #161616; + background-color: #171717; } .chat-messages { @@ -109,7 +109,7 @@ input, textarea, button { - background-color: #161616; + background-color: #171717; } .corner-float { @@ -281,10 +281,6 @@ select:disabled { box-shadow: none; } -.audio-time-display .current-time { - color: #59fcb3; -} - /* File Preview Overlay Theme */ .file-preview-container { border-color: var(--primary-color); @@ -340,7 +336,7 @@ select:disabled { } .marketplace-app-card { - background-color: #161616; + background-color: #171717; border-color: rgba(90, 252, 180, 0.15); } From 8d889a002b78a96b76e05ee3c9742bd1aeb5a765 Mon Sep 17 00:00:00 2001 From: Yuurin Bee Date: Thu, 28 May 2026 08:25:17 +0700 Subject: [PATCH 13/22] Refactor: Remove Emoji UI Icon --- src/js/picker.js | 2 +- src/styles.css | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/js/picker.js b/src/js/picker.js index ed54ac0a..c9540268 100644 --- a/src/js/picker.js +++ b/src/js/picker.js @@ -2261,7 +2261,7 @@ function _pcRenderGrid() { removeBtn.type = 'button'; removeBtn.className = 'emoji-creator-cell-remove'; removeBtn.setAttribute('aria-label', 'Remove emoji'); - removeBtn.innerHTML = ''; + removeBtn.innerHTML = ''; removeBtn.addEventListener('click', (ev) => { ev.stopPropagation(); _pcRemoveEmoji(idx); diff --git a/src/styles.css b/src/styles.css index 02e89afb..3d2a2672 100644 --- a/src/styles.css +++ b/src/styles.css @@ -4187,12 +4187,12 @@ img.emoji-img-loading { .emoji-creator-cell-remove { position: absolute; top: -4px; - right: -4px; - width: 16px; - height: 16px; + right: -6px; + width: 20px; + height: 20px; border-radius: 50%; - background-color: var(--danger-color); - border: 2px solid #0a0a0a; + background-color: transparent; + background: #0a0a0a; padding: 0; cursor: pointer; display: none; @@ -4205,9 +4205,9 @@ img.emoji-img-loading { a drag can never get stuck in an apparent hover state after drop. */ .emoji-creator-cell.is-hovered .emoji-creator-cell-remove { display: flex; } .emoji-creator-cell-remove .icon { - width: 7px; - height: 7px; - background-color: #fff; + width: 10px; + height: 10px; + background-color: var(--danger-color); } /* Bottom dropzone — second affordance for uploading (mirrors mockup). */ From 9bf1a31f945bc02c168c65cbc68ae610620ed5bc Mon Sep 17 00:00:00 2001 From: Yuurin Bee Date: Fri, 29 May 2026 16:04:05 +0700 Subject: [PATCH 14/22] Refactor: UI for Relay Settings Popup + Theme Support --- src/icons/disable.svg | 3 + src/index.html | 51 +++++++------- src/main.js | 10 +-- src/styles.css | 128 +++++++++++++++++++++++++++------- src/themes/chatstr/dark.css | 21 ------ src/themes/cyberpunk/dark.css | 21 +----- src/themes/gifverse/dark.css | 20 +----- src/themes/monero/dark.css | 17 ++--- src/themes/pivx/dark.css | 15 ---- src/themes/satoshi/dark.css | 13 +--- src/themes/vector/dark.css | 20 ++---- 11 files changed, 146 insertions(+), 173 deletions(-) create mode 100644 src/icons/disable.svg diff --git a/src/icons/disable.svg b/src/icons/disable.svg new file mode 100644 index 00000000..1ecc1736 --- /dev/null +++ b/src/icons/disable.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/index.html b/src/index.html index 972a99c2..2188ed3d 100644 --- a/src/index.html +++ b/src/index.html @@ -684,34 +684,28 @@

Capabilities

-

Relay URL

- +

Relay Settings

+
-
- - -
-
-
-

Connection Info

- connected +
+

+ connected +
+
+ Ping + -- + Last Check + -- +
+
+ +
-
-
- -- - Ping -
-
- -- - Last Check -
-
-

Recent Activity

@@ -724,8 +718,11 @@

Recent Activity

- - + +
diff --git a/src/main.js b/src/main.js index 7a8abefb..23feb0b4 100644 --- a/src/main.js +++ b/src/main.js @@ -3707,10 +3707,12 @@ async function refreshRelayInfoDialog() { statusEl.className = `relay-status ${freshRelay.status}`; // Update disable button text - const disableBtn = document.getElementById('relay-info-disable'); - if (freshRelay.is_default) { - disableBtn.textContent = freshRelay.enabled ? 'Disable Relay' : 'Enable Relay'; - } + const disableBtn = document.getElementById('relay-info-disable'); + if (freshRelay.is_default) { + disableBtn.innerHTML = freshRelay.enabled + ? ' Disable' + : ' Enable'; + } } } catch (err) { console.error('Failed to refresh relay data:', err); diff --git a/src/styles.css b/src/styles.css index 3d2a2672..d27dba80 100644 --- a/src/styles.css +++ b/src/styles.css @@ -7565,6 +7565,11 @@ select:focus::-ms-value { mask-size: contain; } +.icon-disable { + mask-image: url("./icons/disable.svg"); + -webkit-mask-image: url("./icons/disable.svg"); +} + .icon-x-user { mask-image: url("./icons/x-user.svg"); } @@ -7910,6 +7915,22 @@ select:disabled:hover { backdrop-filter: none; border-color: var(--danger-color); background-color: #00000000; + display: inline-flex; + align-items: center; + justify-content: center; + gap: 6px; + padding-top: 2px; +} + +.danger-btn .icon-disable { + position: relative; + width: 16px; + height: 16px; + background-color: var(--danger-color); + flex-shrink: 0; + display: inline-block; + margin: 0; + margin-top: -2px; } .warning-icon { @@ -9052,7 +9073,7 @@ select:disabled:hover { .relay-status { font-size: 12px; font-weight: 600; - padding: 4px 12px; + padding: 2px 10px; border-radius: 20px; text-transform: uppercase; letter-spacing: 0.5px; @@ -9202,7 +9223,7 @@ select:disabled:hover { .relay-dialog { background: rgba(20, 20, 20, 0.95); - border: 1px solid rgba(255, 255, 255, 0.15); + border: 1px solid #262626; border-radius: 16px; width: min(420px, calc(100vw - 40px)); max-height: calc(100vh - 80px); @@ -9215,12 +9236,13 @@ select:disabled:hover { justify-content: space-between; align-items: center; padding: 15px 20px; - border-bottom: 1px solid rgba(255, 255, 255, 0.1); + border-bottom: 1px solid #333333; + background: #262626; } .relay-dialog-header h3 { margin: 0; - font-size: 16px; + font-size: 18px; color: #fff; word-break: break-all; padding-right: 10px; @@ -9250,6 +9272,23 @@ select:disabled:hover { transition: transform 0.25s ease; } +#relay-info-url { + text-align: left; + color: var(--primary-color); + font-size: 16px; + margin: 0 0 16px 0; +} + +.relay-info-header-row { + display: flex; + align-items: center; + justify-content: space-between; +} + +.relay-info-header-row #relay-info-url { + margin: 0; +} + .relay-dialog-content { padding: 20px; display: flex; @@ -9260,22 +9299,31 @@ select:disabled:hover { } .relay-form-group { - display: flex; - flex-direction: column; - gap: 6px; + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + gap: 10px; +} + +.relay-form-group select { + width: 160px; + margin: 0; + margin-left: auto; } .relay-form-label { font-size: 13px; font-weight: 600; color: rgba(255, 255, 255, 0.8); + flex-shrink: 0; } .relay-form-input, .relay-form-select { padding: 10px 12px; background: rgba(0, 0, 0, 0.3); - border: 1px solid rgba(255, 255, 255, 0.15); + border: 1px solid #262626; border-radius: 8px; color: #fff; font-size: 14px; @@ -9285,6 +9333,7 @@ select:disabled:hover { .relay-form-input:focus, .relay-form-select:focus { outline: none; + border-color: var(--primary-color); } .relay-form-select { @@ -9292,10 +9341,13 @@ select:disabled:hover { appearance: none; -webkit-appearance: none; -moz-appearance: none; - padding-right: 36px; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none'%3E%3Cpath d='M2.5 4.5L6 8L9.5 4.5' stroke='rgba(255,255,255,0.6)' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); background-repeat: no-repeat; - background-position: right 12px center; + background-position: right 8px center; + flex: none; + padding: 6px 24px 6px 14px; + font-size: 13px; + width: 160px; } .relay-form-select:disabled { @@ -9320,6 +9372,10 @@ select:disabled:hover { padding: 10px 15px; } +.relay-dialog-buttons .primary-btn:hover { + opacity: 0.8; +} + /* Relay Info Dialog Specific */ .relay-metrics-section h4, .relay-logs-section h4 { @@ -9334,7 +9390,33 @@ select:disabled:hover { display: flex; align-items: center; justify-content: space-between; - margin-bottom: 10px; +} + +.relay-metrics-inline { + display: flex; + align-items: center; + gap: 4px; + font-size: 13px; +} + +.relay-metric-inline-label { + color: rgba(255, 255, 255, 0.5); + font-size: 11px; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.relay-metric-inline-value { + color: #59fcb3; + font-weight: 600; +} + +.relay-metric-inline-divider { + color: rgba(255, 255, 255, 0.2); +} + +.relay-metric-inline-right { + margin-left: auto; } .relay-status-small { @@ -9370,23 +9452,14 @@ select:disabled:hover { background-color: rgba(255, 255, 255, 0.8); } -.relay-metrics-grid { - display: grid; - grid-template-columns: repeat(2, 1fr); - gap: 10px; -} - -.relay-metric { - background: rgba(0, 0, 0, 0.3); - border-radius: 8px; - padding: 10px; - text-align: center; -} - .relay-metric-value { display: block; - font-size: 18px; - font-weight: 600; + font-size: 16px; + font-weight: 500; +} + +.relay-metric-inline-right { + margin-left: auto; } .relay-metric-label { @@ -9405,10 +9478,11 @@ select:disabled:hover { border-radius: 8px; max-height: 150px; overflow-y: auto; + margin-top: 10px; } .relay-logs-list li { - padding: 8px 12px; + padding: 6px 12px; font-size: 12px; color: rgba(255, 255, 255, 0.7); border-bottom: 1px solid rgba(255, 255, 255, 0.05); diff --git a/src/themes/chatstr/dark.css b/src/themes/chatstr/dark.css index 22dd19b5..0a9ac74b 100644 --- a/src/themes/chatstr/dark.css +++ b/src/themes/chatstr/dark.css @@ -196,10 +196,6 @@ input[type="checkbox"]:checked + .neon-toggle:hover { background-color: #b188e2; } -select:disabled { - border-color: rgba(177, 136, 226, 0.3); -} - .nav-icon { background-color: #b188e2; } @@ -602,29 +598,12 @@ select:disabled { } /* Relay Dialog */ -.relay-dialog { - border-color: rgba(177, 136, 226, 0.25); -} - -.relay-dialog-header { - background: linear-gradient(90deg, #b188e210 0%,#b188e2 75%, #5749E4 100%); -} - -.relay-form-input:focus, -.relay-form-select:focus { - border-color: #b188e2; -} .relay-dialog-buttons .primary-btn { background-color: #b188e2; color: #000; } -.relay-dialog-buttons .primary-btn:hover { - color: #fff; - background-color: #5749E4; -} - .relay-metric-value { color: #b188e2; } diff --git a/src/themes/cyberpunk/dark.css b/src/themes/cyberpunk/dark.css index c27aeeaf..1d65b13b 100644 --- a/src/themes/cyberpunk/dark.css +++ b/src/themes/cyberpunk/dark.css @@ -255,10 +255,6 @@ input[type="checkbox"]:checked + .neon-toggle:hover { background-color: #59FCF4; } -select:disabled { - border-color: #59FCF460; -} - .nav-icon { background-color: #59FCF4; } @@ -661,17 +657,6 @@ select:disabled { } /* Relay Dialog */ -.relay-dialog { - border-color: #171717; -} - -.relay-dialog-header { - background: linear-gradient(90deg, var(--primary-color), var(--secondary-color)); -} - -.relay-dialog-header h3 { - color: #000; -} .relay-form-input:focus, .relay-form-select:focus { @@ -679,11 +664,7 @@ select:disabled { } .relay-dialog-buttons .primary-btn { - background-color: #59FCF4; - color: #000; -} - -.relay-dialog-buttons .primary-btn:hover { + background-color: var(--primary-color); color: #000; } diff --git a/src/themes/gifverse/dark.css b/src/themes/gifverse/dark.css index e900e0cb..7f30b40a 100644 --- a/src/themes/gifverse/dark.css +++ b/src/themes/gifverse/dark.css @@ -616,13 +616,6 @@ select:disabled { } /* Relay Dialog */ -.relay-dialog { - border-color: #171717; -} - -.relay-dialog-header { - background: linear-gradient(90deg, #F7FFAE 40%, #AEFFDF 60%, #B6AEFF 80%, #FFAECE 100%); -} .relay-form-input:focus, .relay-form-select:focus { @@ -634,17 +627,6 @@ select:disabled { color: #000000; } -.relay-dialog-buttons .primary-btn:hover { - color: #000; - box-shadow: 0 0 4px #F7FFAE00; -} - .relay-metric-value { color: #F7FFAE; -} - -.relay-dialog-header h3 { - color: #000; - margin: 0; - line-height: 1; -} +} \ No newline at end of file diff --git a/src/themes/monero/dark.css b/src/themes/monero/dark.css index b59970e1..7e9a164c 100644 --- a/src/themes/monero/dark.css +++ b/src/themes/monero/dark.css @@ -660,13 +660,6 @@ select:disabled { } /* Relay Dialog */ -.relay-dialog { - border-color: #171717; -} - -.relay-dialog-header { - background: #FF6600; -} .relay-form-input:focus, .relay-form-select:focus { @@ -675,13 +668,13 @@ select:disabled { .relay-dialog-buttons .primary-btn { background-color: #FF6600; - color: #ffffff; -} - -.relay-dialog-buttons .primary-btn:hover { - color: #fff; + color: #000; } .relay-metric-value { color: #FF6600; } + +.relay-dialog .relay-form-select { + border: 1px solid #262626; +} \ No newline at end of file diff --git a/src/themes/pivx/dark.css b/src/themes/pivx/dark.css index 033e0b8f..4098f371 100644 --- a/src/themes/pivx/dark.css +++ b/src/themes/pivx/dark.css @@ -218,10 +218,6 @@ input[type="checkbox"]:checked + .neon-toggle:hover { background-color: #6202D4; } -select:disabled { - border-color: rgba(177, 136, 226, 0.3); -} - .nav-icon { background-color: #B359FC; } @@ -618,13 +614,6 @@ select:disabled { } /* Relay Dialog */ -.relay-dialog { - border-color: #171717; -} - -.relay-dialog-header { - background: linear-gradient(90deg, #1D003F50, #6202D4); -} .relay-form-input:focus, .relay-form-select:focus { @@ -633,10 +622,6 @@ select:disabled { .relay-dialog-buttons .primary-btn { background-color: #6202D4; - color: #ffffff; -} - -.relay-dialog-buttons .primary-btn:hover { color: #fff; } diff --git a/src/themes/satoshi/dark.css b/src/themes/satoshi/dark.css index c78293d1..596cbd13 100644 --- a/src/themes/satoshi/dark.css +++ b/src/themes/satoshi/dark.css @@ -596,13 +596,6 @@ select:disabled { } /* Relay Dialog */ -.relay-dialog { - border-color: #171717; -} - -.relay-dialog-header { - background: linear-gradient(90deg, #B56C1220, #F7931A); -} .relay-form-input:focus, .relay-form-select:focus { @@ -611,11 +604,7 @@ select:disabled { .relay-dialog-buttons .primary-btn { background-color: #F7931A; - color: #ffffff; -} - -.relay-dialog-buttons .primary-btn:hover { - color: #fff; + color: #000; } .relay-metric-value { diff --git a/src/themes/vector/dark.css b/src/themes/vector/dark.css index c9c3cdc0..6bccf285 100644 --- a/src/themes/vector/dark.css +++ b/src/themes/vector/dark.css @@ -572,32 +572,20 @@ select:disabled { } /* Relay Dialog */ -.relay-dialog { - border-color: #171717; -} - -.relay-dialog-header { - background: linear-gradient(90deg, #2b976c00 0%, #2b976c20 40%, #33DB9880 100%); -} - -.relay-form-input:focus, -.relay-form-select:focus { - border-color: #59fcb3; -} .relay-dialog-buttons .primary-btn { background-color: #59fcb3; color: #000; } -.relay-dialog-buttons .primary-btn:hover { - box-shadow: 0 0 0px #2b976c00; -} - .relay-metric-value { color: #59fcb3; } +.relay-metric-inline-value { + color: var(--accent-color); +} + .aggro-glitch-img { content: url('../../icons/aggro-glitch.gif'); width: 64px; From 1fdc994531f10f57cc084d8b261fa67adf0b3e7a Mon Sep 17 00:00:00 2001 From: Yuurin Bee Date: Fri, 29 May 2026 20:58:20 +0700 Subject: [PATCH 15/22] Refactor: Chat Contact List, Unread Messages, Message Counter --- src/js/render/chatlist/row.js | 46 ++++++++++--------- src/styles.css | 84 ++++++++++++++++++++++++++++------- src/themes/monero/dark.css | 2 +- 3 files changed, 94 insertions(+), 38 deletions(-) diff --git a/src/js/render/chatlist/row.js b/src/js/render/chatlist/row.js index 0608a379..bd736b18 100644 --- a/src/js/render/chatlist/row.js +++ b/src/js/render/chatlist/row.js @@ -27,7 +27,7 @@ function renderChat(chat, primaryColor) { // border without needing a chatlist re-render (inline color literals // would stay stuck on the previous theme until the next paint pass). const divContact = document.createElement('div'); - if (nUnread) divContact.style.borderColor = 'var(--icon-color-primary)'; + if (nUnread) divContact.classList.add('has-unread'); divContact.classList.add('chatlist-contact'); divContact.id = `chatlist-${chat.id}`; @@ -135,12 +135,11 @@ function renderChat(chat, primaryColor) { // Inline time-ago (unread-only). Read rows keep the right-aligned variant // appended further down. const cLastMsgForHeader = chat.messages[chat.messages.length - 1]; - if (nUnread && cLastMsgForHeader) { - const spanInlineTime = document.createElement('span'); - spanInlineTime.classList.add('chatlist-contact-inline-time'); - spanInlineTime.textContent = timeAgo(cLastMsgForHeader.at); - spanInlineTime.style.color = 'var(--icon-color-primary)'; - divHeader.appendChild(spanInlineTime); + if (cLastMsgForHeader) { + const spanInlineTime = document.createElement('span'); + spanInlineTime.classList.add('chatlist-contact-inline-time'); + spanInlineTime.textContent = timeAgo(cLastMsgForHeader.at); + divHeader.appendChild(spanInlineTime); } divPreviewContainer.appendChild(divHeader); @@ -174,13 +173,8 @@ function renderChat(chat, primaryColor) { if (nUnread) { const spanCount = document.createElement('span'); spanCount.classList.add('chatlist-contact-count'); - spanCount.textContent = String(nUnread); + spanCount.textContent = nUnread > 99 ? '99+' : String(nUnread); divContact.appendChild(spanCount); - } else { - const pTimeAgo = document.createElement('p'); - pTimeAgo.classList.add('chatlist-contact-timestamp', 'read'); - if (cLastMsg) pTimeAgo.textContent = timeAgo(cLastMsg.at); - divContact.appendChild(pTimeAgo); } return divContact; @@ -205,7 +199,6 @@ function renderInviteItem(invite, primaryColor) { const divInvite = document.createElement('div'); divInvite.classList.add('chatlist-contact', 'chatlist-invite'); divInvite.id = `invite-${invite.id || invite.welcome_event_id || groupId}`; - divInvite.style.borderColor = 'var(--icon-color-primary)'; // Avatar container — show cached avatar if available, otherwise placeholder const divAvatarContainer = document.createElement('div'); @@ -254,12 +247,27 @@ function renderInviteItem(invite, primaryColor) { const h4Name = document.createElement('h4'); h4Name.textContent = groupName; h4Name.classList.add('cutoff'); - divPreviewContainer.appendChild(h4Name); + + const divInviteHeader = document.createElement('div'); + divInviteHeader.classList.add('chatlist-contact-header'); + divInviteHeader.appendChild(h4Name); + divPreviewContainer.appendChild(divInviteHeader); // Member count as subtext const pMemberCount = document.createElement('p'); pMemberCount.classList.add('cutoff'); - pMemberCount.textContent = `${memberCount} ${memberCount === 1 ? 'member' : 'members'}`; + + const groupIconWrap = document.createElement('span'); + groupIconWrap.className = 'chatlist-member-icon-wrap'; + + const groupIcon = document.createElement('span'); + groupIcon.className = 'icon icon-users-multi'; + groupIcon.addEventListener('mouseenter', () => showGlobalTooltip('Group Chat', groupIconWrap)); + groupIcon.addEventListener('mouseleave', hideGlobalTooltip); + + groupIconWrap.appendChild(groupIcon); + pMemberCount.appendChild(groupIconWrap); + pMemberCount.appendChild(document.createTextNode(` ${memberCount} ${memberCount === 1 ? 'member' : 'members'}`)); divPreviewContainer.appendChild(pMemberCount); divInvite.appendChild(divPreviewContainer); @@ -278,9 +286,6 @@ function renderInviteItem(invite, primaryColor) { }; const acceptIcon = document.createElement('span'); acceptIcon.classList.add('icon', 'icon-check'); - acceptIcon.style.width = '16px'; - acceptIcon.style.height = '16px'; - acceptIcon.style.backgroundColor = '#59fcb3'; btnAccept.appendChild(acceptIcon); // Decline button (danger color X) @@ -293,9 +298,6 @@ function renderInviteItem(invite, primaryColor) { }; const declineIcon = document.createElement('span'); declineIcon.classList.add('icon', 'icon-x'); - declineIcon.style.width = '16px'; - declineIcon.style.height = '16px'; - declineIcon.style.backgroundColor = 'var(--danger-pink)'; btnDecline.appendChild(declineIcon); divActions.appendChild(btnAccept); diff --git a/src/styles.css b/src/styles.css index d27dba80..cb03dbd9 100644 --- a/src/styles.css +++ b/src/styles.css @@ -2190,6 +2190,10 @@ html, body { contain-intrinsic-size: 62px; } +.chatlist-contact.has-unread { + border: none; +} + /* Fade-in when a row transitions from content-visibility:auto skipped → visible. Toggled by the listener in chatlist-reveal.js. Opacity-only so it stays on the compositor and doesn't retrigger layout/paint. */ @@ -2255,20 +2259,22 @@ html, body { * through the row visually. Every count represents pings: full unread set * on unmuted chats, @mentions only on muted groups, nothing on muted DMs. */ .chatlist-contact-count { - margin-top: auto; - margin-bottom: auto; - margin-left: auto; - margin-right: 8px; - padding: 2px 9px; - border-radius: 999px; - background: var(--icon-color-primary, #59fcb3); - color: #000; - font-size: 12px; - font-weight: 600; - line-height: 1.2; - min-width: 18px; - text-align: center; - flex-shrink: 0; + margin-top: auto; + margin-bottom: auto; + margin-left: auto; + margin-right: 12px; + padding: 2px 6px; + border-radius: 999px; + background: var(--primary-color); + color: #000; + font-size: 14px; + font-weight: 600; + line-height: 1.2; + min-width: 18px; + width: fit-content; + text-align: center; + flex-shrink: 0; + box-sizing: border-box; } .chatlist-contact img { @@ -2344,6 +2350,15 @@ html, body { font-weight: 400; flex-shrink: 0; white-space: nowrap; + color: rgba(255, 255, 255, 0.5); +} + +.chatlist-contact.has-unread .chatlist-contact-inline-time { + color: var(--primary-color); +} + +.chatlist-contact.has-unread .chatlist-contact-header h4 { + color: #fff; } .chatlist-contact-preview h4 { @@ -2366,6 +2381,29 @@ html, body { transition: color 0.2s ease; } +.chatlist-member-icon-wrap { + position: relative; + display: inline-block; + width: 14px; + height: 14px; + vertical-align: middle; + flex-shrink: 0; + margin-right: 2px; + margin-top: -2px; +} + +.chatlist-member-icon-wrap .icon { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + width: 100%; + height: 100%; + margin: auto; + background-color: rgba(255, 255, 255, 0.55); +} + .chatlist-contact:hover { background-color: rgba(255, 255, 255, 0.05); } @@ -10116,6 +10154,22 @@ select:disabled:hover { color: rgba(255, 255, 255, 0.85); } +.chatlist-invite { + border-color: var(--primary-color); +} + +.invite-accept-btn .icon { + width: 16px; + height: 16px; + background-color: #59fcb3; +} + +.invite-decline-btn .icon { + width: 16px; + height: 16px; + background-color: var(--danger-color); +} + .invite-action-buttons { display: flex; flex-direction: row; @@ -10160,7 +10214,7 @@ select:disabled:hover { } .invite-decline-btn:hover { - background: rgba(255, 46, 169, 0.2); + background: var(--danger-color-hover); } .btn-small { diff --git a/src/themes/monero/dark.css b/src/themes/monero/dark.css index 7e9a164c..1fa2db43 100644 --- a/src/themes/monero/dark.css +++ b/src/themes/monero/dark.css @@ -677,4 +677,4 @@ select:disabled { .relay-dialog .relay-form-select { border: 1px solid #262626; -} \ No newline at end of file +} From d9689504d7644c06926adb508cb8c1dcda480b25 Mon Sep 17 00:00:00 2001 From: Yuurin Bee Date: Sat, 30 May 2026 16:19:44 +0700 Subject: [PATCH 16/22] Fix: New Chat Hover Opacity for Text and Icon --- src/styles.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/styles.css b/src/styles.css index cb03dbd9..73f16ffd 100644 --- a/src/styles.css +++ b/src/styles.css @@ -2149,6 +2149,10 @@ html, body { border-color: #B2B2B2; } +.new-chat-btn.btn:hover { + opacity: 1; +} + .new-chat-btn .icon { display: inline-block; position: relative; From 6beb5e6ba4990cd57ec3b68932d3e326d2538170 Mon Sep 17 00:00:00 2001 From: Yuurin Bee Date: Sat, 30 May 2026 17:06:45 +0700 Subject: [PATCH 17/22] Refactor: Profile Switcher Menu and Popup --- src/index.html | 18 ++++---------- src/main.js | 10 ++++++-- src/styles.css | 50 ++++++++++++++++++++++++-------------- src/themes/monero/dark.css | 8 ++++++ 4 files changed, 53 insertions(+), 33 deletions(-) diff --git a/src/index.html b/src/index.html index 2188ed3d..e30af425 100644 --- a/src/index.html +++ b/src/index.html @@ -730,6 +730,11 @@

Recent Activity

diff --git a/src/main.js b/src/main.js index fbcaa229..5daa1618 100644 --- a/src/main.js +++ b/src/main.js @@ -3727,7 +3727,17 @@ async function refreshRelayInfoDialog() { // Refresh metrics try { const metrics = await invoke('get_relay_metrics', { url }); - document.getElementById('relay-info-ping').textContent = metrics.ping_ms ? `${metrics.ping_ms}ms` : '--'; + const pingEl = document.getElementById('relay-info-ping'); + if (metrics.ping_ms) { + pingEl.textContent = `${metrics.ping_ms}ms`; + pingEl.style.color = metrics.ping_ms < 200 ? 'var(--status-excellent)' + : metrics.ping_ms < 500 ? 'var(--status-good)' + : metrics.ping_ms < 1000 ? 'var(--status-fair)' + : 'var(--status-poor)'; + } else { + pingEl.textContent = '--'; + pingEl.style.color = ''; + } if (metrics.last_check) { const lastCheck = new Date(metrics.last_check * 1000); const now = new Date(); diff --git a/src/styles.css b/src/styles.css index d109feb2..e3a52e0b 100644 --- a/src/styles.css +++ b/src/styles.css @@ -309,6 +309,10 @@ --danger-red: #FC595C; --danger-color: #FC595C; /* default: red for all themes */ --danger-color-hover: color-mix(in srgb, var(--danger-color) 10%, transparent); + --status-excellent: #59fcb3; + --status-good: #FCE459; + --status-fair: #FCA559; + --status-poor: var(--danger-color); font-family: Inter, Avenir, Helvetica, Arial, sans-serif; font-size: 16px; line-height: 24px; @@ -9316,7 +9320,7 @@ select:disabled:hover { #relay-info-url { text-align: left; - color: var(--primary-color); + color: #fff; font-size: 16px; margin: 0 0 16px 0; } @@ -9331,6 +9335,10 @@ select:disabled:hover { margin: 0; } +#relay-info-done { + border: 2px solid #474747; +} + .relay-dialog-content { padding: 20px; display: flex; diff --git a/src/themes/vector/dark.css b/src/themes/vector/dark.css index 6bccf285..2b2ca388 100644 --- a/src/themes/vector/dark.css +++ b/src/themes/vector/dark.css @@ -586,6 +586,10 @@ select:disabled { color: var(--accent-color); } +.relay-dialog .relay-form-select { + border: 1px solid #262626; +} + .aggro-glitch-img { content: url('../../icons/aggro-glitch.gif'); width: 64px; From d3ef5c2a74d1a8a45307d665b86df2e7eb2dec67 Mon Sep 17 00:00:00 2001 From: Yuurin Bee Date: Mon, 1 Jun 2026 21:07:54 +0700 Subject: [PATCH 22/22] Polish UI for Chat Context Menu: Text and Icon Size --- src/js/render/chat/message-toolbar.js | 2 +- src/styles.css | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/js/render/chat/message-toolbar.js b/src/js/render/chat/message-toolbar.js index 43403420..ddca5e48 100644 --- a/src/js/render/chat/message-toolbar.js +++ b/src/js/render/chat/message-toolbar.js @@ -734,7 +734,7 @@ async function _dmsgOpenMessageMenu(rowEl, x, y) { if (plain === raw.trim()) { items.push({ label: 'Copy', icon: 'copy', onClick: () => _dmsgCopyText(raw) }); } else { - items.push({ label: 'Copy', hint: '(plain)', icon: 'copy', onClick: () => _dmsgCopyText(plain) }); + items.push({ label: 'Copy', icon: 'copy', onClick: () => _dmsgCopyText(plain) }); items.push({ label: 'Copy', hint: '(markdown)', icon: 'file-code', onClick: () => _dmsgCopyText(raw) }); } } diff --git a/src/styles.css b/src/styles.css index e3a52e0b..44baad5d 100644 --- a/src/styles.css +++ b/src/styles.css @@ -1738,8 +1738,8 @@ html, body { .context-menu-item .icon { position: relative; flex-shrink: 0; - width: 18px; - height: 18px; + width: 20px; + height: 20px; margin: 0; padding: 0; border-radius: 0;