diff --git a/README.md b/README.md index 4a1861f..32d7cc7 100755 --- a/README.md +++ b/README.md @@ -2,6 +2,12 @@ A Twitter/X-style microblog for WordPress. Logged-in users post short updates with rich media, polls, GIFs, voice notes, and quotes. Like, reply (with nested threads), and share beeps. Designed to look like a Twitter timeline, hosted entirely on your own site. +## What's in v1.8.0 + +- **Twitter-style timeline.** The feed shows only top-level beeps; click a beep (or its reply count) to open the conversation in a modal with reply forms, likes, and nested threads. +- **Light theme + auto mode.** `theme="auto"` (default) follows the visitor's system preference; `dark` and `light` force a theme. +- **Lots of fixes.** Composer avatar, modal close buttons, CSS scoping, Elementor widget registration (and Beep works fine without Elementor), real avatars on replies, action-bar polish. See `readme.txt` for the full changelog. + ## What's in v1.5.1 ### Core Features @@ -67,12 +73,14 @@ This is the easiest way to let a small circle into your Beep feed without settin ## Shortcode options ``` -[beep_feed limit="50" header="Messages from" sort="latest"] +[beep_feed limit="50" header="Messages from" sort="latest" theme="auto" show_wordmark="yes"] ``` - `limit` — how many beeps to load on first render (default 50) -- `header` — text shown next to the wordmark (default "Messages from") +- `header` — text shown next to the wordmark (default "Messages from"; pass `""` to hide) - `sort` — initial sort: `latest` (default) or `top` +- `theme` — `auto` (default, follows the visitor's system preference), `dark`, or `light` +- `show_wordmark` — `yes` (default) or `no` ## Elementor Widget diff --git a/assets/beep.css b/assets/beep.css index 320d694..740c268 100755 --- a/assets/beep.css +++ b/assets/beep.css @@ -1,37 +1,75 @@ /* ============================================================ - Beep — Twitter/X Dark Mode - Post and feed styling to match Twitter/X perfectly + Beep — Twitter/X-style feed + Dark theme by default; light theme via [beep_feed theme="light"] + or theme="auto" (follows the visitor's system preference). + Scope: every rule lives under .beep-embed so the plugin never + leaks styles into the host theme (and vice versa). ============================================================ */ -.beep-embed .beep-feed, -.beep-embed .beep-feed * { +/* ---------- Theme variables ---------- */ +.beep-embed { + /* Dark (default) — Twitter "Dim" palette */ + --beep-bg: #15202B; + --beep-bg-trans: rgba(21, 32, 43, 0.85); + --beep-card-bg: #192734; + --beep-hover: rgba(255, 255, 255, 0.03); + --beep-text: #E7E9EA; + --beep-muted: #8B98A5; + --beep-border: #38444D; + --beep-input-bg: #22303C; + --beep-accent: #1D9BF0; + --beep-accent-hover: #1A8CD8; + --beep-accent-soft: rgba(29, 155, 240, 0.1); + --beep-like: #F91880; + --beep-like-soft: rgba(249, 24, 128, 0.1); + --beep-repost: #00BA7C; + --beep-repost-soft: rgba(0, 186, 124, 0.1); + --beep-danger: #F4212E; + --beep-danger-soft: rgba(244, 33, 46, 0.1); + --beep-warn: #FFD400; + --beep-on-accent: #FFFFFF; +} + +.beep-embed.beep-theme-light { + --beep-bg: #FFFFFF; + --beep-bg-trans: rgba(255, 255, 255, 0.85); + --beep-card-bg: #F7F9F9; + --beep-hover: rgba(0, 0, 0, 0.03); + --beep-text: #0F1419; + --beep-muted: #536471; + --beep-border: #EFF3F4; + --beep-input-bg: #EFF3F4; + --beep-warn: #B98A00; +} + +@media (prefers-color-scheme: light) { + .beep-embed.beep-theme-auto { + --beep-bg: #FFFFFF; + --beep-bg-trans: rgba(255, 255, 255, 0.85); + --beep-card-bg: #F7F9F9; + --beep-hover: rgba(0, 0, 0, 0.03); + --beep-text: #0F1419; + --beep-muted: #536471; + --beep-border: #EFF3F4; + --beep-input-bg: #EFF3F4; + --beep-warn: #B98A00; + } +} + +/* ---------- Root container ---------- */ +.beep-embed, +.beep-embed * { box-sizing: border-box; } -:root { - /* Default Twitter Dark Mode Palette */ - --beep-bg: #15202B; - --beep-card-bg: #192734; - --beep-card-hover: #1C2732; - --beep-text: #E7E9EA; - --beep-muted: #71767B; - --beep-border: #2F3336; - --beep-accent: #1D9BF0; - --beep-accent-hover: #1A8CD8; - --beep-like: #F91880; - --beep-repost: #00BA7C; -} - - -.beep-embed .beep-feed { - /* Twitter Dark Mode Palette */ - +.beep-embed.beep-feed { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; max-width: 600px; margin: 0 auto; background: var(--beep-bg); - border-left: 1px solid var(--beep-border); - border-right: 1px solid var(--beep-border); + border: 1px solid var(--beep-border); + border-top: 0; + border-bottom: 0; color: var(--beep-text); font-size: 15px; line-height: 1.4; @@ -40,58 +78,69 @@ min-height: 0; } -/* Header */ +/* ---------- Header ---------- */ .beep-embed .beep-feed-header { - padding: 16px 20px; - border-bottom: 1px solid var(--beep-border); display: flex; align-items: center; - justify-content: space-between; + justify-content: center; + gap: 8px; + padding: 14px 16px; + border-bottom: 1px solid var(--beep-border); position: sticky; top: 0; z-index: 10; - background: rgba(21, 32, 43, 0.85); + background: var(--beep-bg-trans); backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); } .beep-embed .beep-feed-header-title { - font-size: 20px; + font-size: 17px; font-weight: 800; color: var(--beep-text); - letter-spacing: -0.5px; + letter-spacing: -0.3px; } -/* Nav tabs */ -.beep-embed .beep-top-nav { +.beep-embed .beep-feed-wordmark { + height: 28px; + width: auto; + display: block; +} + +/* ---------- Tabs ---------- */ +.beep-embed .beep-tabs { display: flex; border-bottom: 1px solid var(--beep-border); } -.beep-embed .beep-nav-tab { +.beep-embed .beep-tab { flex: 1; background: transparent; border: 0; - padding: 16px 0; + padding: 0; font-family: inherit; font-size: 15px; - font-weight: 700; + font-weight: 500; color: var(--beep-muted); cursor: pointer; - position: relative; text-align: center; transition: background-color 0.15s ease, color 0.15s ease; } -.beep-embed .beep-nav-tab:hover { - background: var(--beep-card-hover); - color: var(--beep-text); +.beep-embed .beep-tab span { + display: inline-block; + position: relative; + padding: 16px 4px; } -.beep-embed .beep-nav-tab.is-active { +.beep-embed .beep-tab:hover { background: var(--beep-hover); } + +.beep-embed .beep-tab.is-active { color: var(--beep-text); + font-weight: 700; } -.beep-embed .beep-nav-tab.is-active::after { +.beep-embed .beep-tab.is-active span::after { content: ''; position: absolute; left: 0; @@ -102,11 +151,34 @@ border-radius: 9999px; } -/* Compose area */ +/* ---------- Avatars ---------- */ +.beep-embed .beep-avatar { + width: 48px; + height: 48px; + min-width: 48px; + border-radius: 50%; + flex-shrink: 0; + background: var(--beep-input-bg); + object-fit: cover; + aspect-ratio: 1 / 1; + display: block; + border: 0; + transition: filter 0.15s ease; +} + +.beep-embed .beep-avatar:hover { filter: brightness(0.9); } + +.beep-embed .beep-avatar-sm { + width: 36px; + height: 36px; + min-width: 36px; +} + +/* ---------- Composer ---------- */ .beep-embed .beep-compose { display: flex; gap: 12px; - padding: 16px 20px; + padding: 12px 16px 8px; border-bottom: 1px solid var(--beep-border); background: var(--beep-bg); } @@ -116,39 +188,43 @@ min-width: 0; } -.beep-embed .beep-feed .beep-input { +.beep-embed .beep-input { width: 100% !important; border: 0 !important; outline: 0 !important; resize: none !important; background: transparent !important; font-family: inherit !important; - font-size: 18px !important; - line-height: 1.5 !important; + font-size: 20px !important; + line-height: 1.4 !important; color: var(--beep-text) !important; - padding: 8px 0 !important; + padding: 10px 0 !important; min-height: 52px; overflow: hidden; box-shadow: none !important; caret-color: var(--beep-accent); } -.beep-embed .beep-feed .beep-input::placeholder { - color: var(--beep-muted) !important; +.beep-embed .beep-input::placeholder { color: var(--beep-muted) !important; opacity: 1; } +.beep-embed .beep-input:focus, +.beep-embed .beep-input:focus-visible { + outline: 0 !important; + box-shadow: none !important; } .beep-embed .beep-compose-footer { display: flex; justify-content: space-between; align-items: center; - margin-top: 12px; - padding-top: 12px; + margin-top: 8px; + padding-top: 8px; border-top: 1px solid var(--beep-border); } .beep-embed .beep-compose-tools { display: flex; - gap: 4px; + align-items: center; + gap: 2px; } .beep-embed .beep-compose-tool { @@ -162,122 +238,282 @@ display: flex; align-items: center; justify-content: center; + padding: 0; + margin: 0; transition: background-color 0.15s ease; } -.beep-embed .beep-compose-tool:hover { - background: rgba(29, 155, 240, 0.15); +.beep-embed .beep-compose-tool:hover { background: var(--beep-accent-soft); } +.beep-embed .beep-compose-tool svg { display: block; } + +.beep-embed .beep-media-count { + font-size: 13px; + color: var(--beep-muted); + font-variant-numeric: tabular-nums; + padding: 0 4px; } -/* Buttons */ +.beep-embed .beep-compose-submit-row { + display: flex; + align-items: center; + gap: 12px; +} + +/* ---------- Buttons ---------- */ .beep-embed .beep-button { display: inline-flex; align-items: center; justify-content: center; background: var(--beep-accent); - color: #fff; + color: var(--beep-on-accent); border: 0; border-radius: 9999px; - padding: 8px 20px; + padding: 8px 18px; font-weight: 700; font-size: 15px; + line-height: 1.2; cursor: pointer; font-family: inherit; transition: background-color 0.15s ease, transform 0.1s ease; } -.beep-embed .beep-button:hover { background: var(--beep-accent-hover); color: #fff; } +.beep-embed .beep-button:hover { background: var(--beep-accent-hover); color: var(--beep-on-accent); } .beep-embed .beep-button:active { transform: scale(0.96); } -.beep-embed .beep-button:disabled { opacity: 0.5; cursor: default; } +.beep-embed .beep-button:disabled { opacity: 0.5; cursor: default; transform: none; } -/* Follow button */ -.beep-embed .beep-follow-btn { - background: var(--beep-text); - color: var(--beep-bg); - border: 0; - border-radius: 9999px; - padding: 6px 16px; - font-weight: 700; - font-size: 14px; - cursor: pointer; - font-family: inherit; - transition: opacity 0.15s ease; - white-space: nowrap; +/* ---------- Char counter ---------- */ +.beep-embed .beep-char-counter { + font-size: 13px; + color: var(--beep-muted); + font-variant-numeric: tabular-nums; +} +.beep-embed .beep-counter-warn { color: var(--beep-warn); } +.beep-embed .beep-counter-bad { color: var(--beep-danger); font-weight: 700; } + +/* ---------- Media preview (composer) ---------- */ +.beep-embed .beep-compose-media-preview { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 8px; + margin-top: 8px; } -.beep-embed .beep-follow-btn:hover { opacity: 0.85; } +.beep-embed .beep-compose-media-preview:not([hidden]) { display: grid; } -.beep-embed .beep-following-btn { - background: transparent; - color: var(--beep-text); +.beep-embed .beep-media-preview-item { + position: relative; + border-radius: 12px; + overflow: hidden; border: 1px solid var(--beep-border); - border-radius: 9999px; - padding: 6px 16px; - font-weight: 700; - font-size: 14px; + aspect-ratio: 16 / 9; +} + +.beep-embed .beep-media-preview-item img { + width: 100%; + height: 100%; + object-fit: cover; + display: block; + margin: 0; +} + +.beep-embed .beep-media-remove, +.beep-embed .beep-gif-remove, +.beep-embed .beep-voice-remove { + position: absolute; + top: 6px; + right: 6px; + width: 26px; + height: 26px; + border-radius: 50%; + border: 0; + background: rgba(15, 20, 25, 0.75); + color: #fff; + font-size: 16px; + line-height: 1; cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + padding: 0; + transition: background-color 0.15s ease; +} + +.beep-embed .beep-media-remove:hover, +.beep-embed .beep-gif-remove:hover, +.beep-embed .beep-voice-remove:hover { background: rgba(39, 44, 48, 0.9); } + +/* ---------- Voice / GIF preview cards (composer) ---------- */ +.beep-embed .beep-voice-preview-card { + position: relative; + display: flex; + align-items: center; + gap: 10px; + margin-top: 8px; + padding: 10px 40px 10px 14px; + border: 1px solid var(--beep-border); + border-radius: 12px; + color: var(--beep-accent); + font-size: 14px; + font-weight: 600; +} + +.beep-embed .beep-voice-preview-card .beep-voice-remove { top: 50%; transform: translateY(-50%); } +.beep-embed .beep-voice-preview-card span { color: var(--beep-text); } + +.beep-embed .beep-compose-gif .beep-gif-preview-card { + position: relative; + margin-top: 8px; + border-radius: 12px; + overflow: hidden; + border: 1px solid var(--beep-border); + max-width: 280px; +} + +.beep-embed .beep-compose-gif .beep-gif-preview-card img { + display: block; + width: 100%; + height: auto; + margin: 0; +} + +/* ---------- Quote lookup (composer) ---------- */ +.beep-embed .beep-compose-quote { + margin-top: 8px; + padding: 12px; + border: 1px solid var(--beep-border); + border-radius: 12px; +} + +.beep-embed .beep-quote-input-row { display: flex; gap: 8px; } + +.beep-embed .beep-quote-lookup, +.beep-embed .beep-poll-question, +.beep-embed .beep-poll-option { + flex: 1; + width: 100%; + background: var(--beep-input-bg); + border: 1px solid transparent; + border-radius: 8px; + padding: 10px 12px; font-family: inherit; - transition: border-color 0.15s ease, color 0.15s ease, background-color 0.15s ease; + font-size: 14px; + color: var(--beep-text); + outline: 0; + transition: border-color 0.15s ease; } -.beep-embed .beep-following-btn:hover { - border-color: #F4212E; - color: #F4212E; - background: rgba(244, 33, 46, 0.1); +.beep-embed .beep-quote-lookup:focus, +.beep-embed .beep-poll-question:focus, +.beep-embed .beep-poll-option:focus { border-color: var(--beep-accent); } + +.beep-embed .beep-quote-lookup::placeholder, +.beep-embed .beep-poll-question::placeholder, +.beep-embed .beep-poll-option::placeholder { color: var(--beep-muted); } + +.beep-embed .beep-quote-lookup-btn { + background: var(--beep-accent); + color: var(--beep-on-accent); + border: 0; + border-radius: 8px; + padding: 0 16px; + font-family: inherit; + font-size: 14px; + font-weight: 700; + cursor: pointer; } -/* Char counter */ -.beep-embed .beep-char-counter { font-size: 13px; color: var(--beep-muted); font-variant-numeric: tabular-nums; } -.beep-embed .beep-counter-warn { color: #FFD400; } -.beep-embed .beep-counter-bad { color: #F4212E; font-weight: 700; } +.beep-embed .beep-quote-lookup-btn:hover { background: var(--beep-accent-hover); } -/* Avatars */ -.beep-embed .beep-feed .beep-avatar { - width: 56px !important; - height: 56px !important; - min-width: 56px !important; - max-width: 56px !important; - border-radius: 50% !important; - flex-shrink: 0; - background: var(--beep-border); - object-fit: cover !important; - aspect-ratio: 1 / 1 !important; - display: block !important; - border: 0 !important; - transition: filter 0.15s ease; +.beep-embed .beep-quote-preview { margin-top: 8px; } + +.beep-embed .beep-quote-remove, +.beep-embed .beep-poll-remove { + margin-top: 8px; + background: transparent; + border: 0; + color: var(--beep-danger); + font-family: inherit; + font-size: 13px; + font-weight: 600; + cursor: pointer; + padding: 4px 8px; + border-radius: 6px; } -.beep-embed .beep-feed .beep-avatar:hover { filter: brightness(0.9); } +.beep-embed .beep-quote-remove:hover, +.beep-embed .beep-poll-remove:hover { background: var(--beep-danger-soft); } -.beep-embed .beep-feed .beep-avatar-sm { - width: 40px !important; - height: 40px !important; - min-width: 40px !important; - max-width: 40px !important; +/* ---------- Poll composer ---------- */ +.beep-embed .beep-compose-poll { + margin-top: 8px; + padding: 12px; + border: 1px solid var(--beep-border); + border-radius: 12px; } -/* Post list */ +.beep-embed .beep-compose-poll .beep-poll-options { + display: flex; + flex-direction: column; + gap: 8px; + margin-top: 8px; +} + +.beep-embed .beep-poll-actions { + display: flex; + align-items: center; + gap: 4px; + margin-top: 4px; +} + +.beep-embed .beep-poll-add-option, +.beep-embed .beep-poll-remove-option { + background: transparent; + border: 0; + color: var(--beep-accent); + font-family: inherit; + font-size: 13px; + font-weight: 600; + cursor: pointer; + padding: 4px 8px; + border-radius: 6px; + margin-top: 8px; +} + +.beep-embed .beep-poll-add-option:hover, +.beep-embed .beep-poll-remove-option:hover { background: var(--beep-accent-soft); } + +/* ---------- Post list ---------- */ .beep-embed .beep-list { background: var(--beep-bg); } .beep-embed .beep-empty { - padding: 60px 20px; + padding: 48px 24px 64px; text-align: center; color: var(--beep-muted); } -/* Individual post */ +.beep-embed .beep-empty-icon { margin-bottom: 12px; opacity: 0.85; } + +.beep-embed .beep-empty p { + margin: 0 0 4px; + font-size: 20px; + font-weight: 800; + color: var(--beep-text); +} + +.beep-embed .beep-empty span { font-size: 15px; } + +/* ---------- Individual post ---------- */ .beep-embed .beep-post { display: flex; gap: 12px; - padding: 16px 20px; + padding: 12px 16px 8px; border-bottom: 1px solid var(--beep-border); background: var(--beep-bg); transition: background-color 0.15s ease; - cursor: pointer; } -.beep-embed .beep-post:hover { background: var(--beep-card-hover); } -.beep-embed .beep-post:last-child { border-bottom: 0; } +.beep-embed .beep-post[data-beep-open-thread] { cursor: pointer; } +.beep-embed .beep-post[data-beep-open-thread]:hover { background: var(--beep-hover); } .beep-embed .beep-post-body { flex: 1; min-width: 0; } @@ -285,11 +521,11 @@ .beep-embed .beep-meta { display: flex; align-items: center; - gap: 6px; - font-size: 16px; + gap: 4px; + font-size: 15px; line-height: 1.3; - margin-bottom: 4px; - flex-wrap: wrap; + margin-bottom: 2px; + min-width: 0; } .beep-embed .beep-name { @@ -300,27 +536,30 @@ text-overflow: ellipsis; } -.beep-embed .beep-name:hover { text-decoration: underline; cursor: pointer; } +.beep-embed .beep-name:hover { text-decoration: underline; } .beep-embed .beep-handle, .beep-embed .beep-dot, -.beep-embed .beep-time { color: var(--beep-muted); font-weight: 400; white-space: nowrap; } - -.beep-embed .beep-time { text-decoration: none; } -.beep-embed .beep-time:hover { text-decoration: underline; cursor: pointer; } - -.beep-embed .beep-spacer { flex: 1; } - -/* Verified badge */ -.beep-embed .beep-verified { - width: 18px; - height: 18px; - fill: var(--beep-accent); - flex-shrink: 0; - margin-left: 2px; - vertical-align: text-bottom; +.beep-embed .beep-time { + color: var(--beep-muted); + font-weight: 400; + white-space: nowrap; } +.beep-embed .beep-handle { overflow: hidden; text-overflow: ellipsis; } +.beep-embed .beep-time { text-decoration: none; } +.beep-embed .beep-time:hover { text-decoration: underline; } +.beep-embed .beep-spacer { flex: 1; min-width: 8px; } + +/* Per-post bird badge (brand corner mark) */ +.beep-embed .beep-post-badge { + flex-shrink: 0; + opacity: 0.6; + transition: opacity 0.15s ease; +} + +.beep-embed .beep-post:hover .beep-post-badge { opacity: 0.9; } + /* Post text */ .beep-embed .beep-text { font-size: 15px; @@ -332,88 +571,79 @@ white-space: pre-wrap; } -.beep-embed .beep-tag, .beep-embed .beep-mention { color: var(--beep-text); text-decoration: none; cursor: pointer; } -.beep-embed .beep-tag:hover, .beep-embed .beep-mention:hover { text-decoration: underline; } +.beep-embed .beep-tag, +.beep-embed .beep-mention { color: var(--beep-accent); text-decoration: none; cursor: pointer; } +.beep-embed .beep-tag:hover, +.beep-embed .beep-mention:hover { text-decoration: underline; } +.beep-embed .beep-mention-stale { color: var(--beep-muted); cursor: default; } +.beep-embed .beep-mention-stale:hover { text-decoration: none; } .beep-embed .beep-link { color: var(--beep-accent); text-decoration: none; } .beep-embed .beep-link:hover { text-decoration: underline; } -/* Action bar */ +/* ---------- Action bar ---------- */ .beep-embed .beep-actions { display: flex; justify-content: space-between; max-width: 425px; - margin-top: 12px; + margin-top: 8px; } .beep-embed .beep-action { - display: flex; + display: inline-flex; align-items: center; - gap: 6px; + gap: 4px; background: transparent; border: 0; color: var(--beep-muted); - font-size: 14px; + font-size: 13px; font-family: inherit; cursor: pointer; - padding: 8px 10px; - margin: -8px -10px; + padding: 0; border-radius: 9999px; - transition: color 0.15s ease, background-color 0.15s ease; + transition: color 0.15s ease; } -.beep-embed .beep-action svg, -.beep-embed .beep-action .beep-icon-svg { - width: 22px; - height: 22px; +.beep-embed .beep-action svg { + width: 34px; + height: 34px; + padding: 8px; + border-radius: 9999px; fill: currentColor; display: block; flex-shrink: 0; + transition: background-color 0.15s ease; } .beep-embed .beep-count { font-variant-numeric: tabular-nums; - min-width: 20px; + min-width: 16px; + text-align: left; } -/* Reply - blue */ -.beep-embed .beep-action-reply:hover { - color: var(--beep-accent); - background: rgba(29, 155, 240, 0.1); -} - -/* Retweet - green */ -.beep-embed .beep-action-retweet:hover { - color: var(--beep-repost); - background: rgba(0, 186, 124, 0.1); -} +.beep-embed .beep-action-reply:hover { color: var(--beep-accent); } +.beep-embed .beep-action-reply:hover svg { background: var(--beep-accent-soft); } +.beep-embed .beep-action-retweet:hover { color: var(--beep-repost); } +.beep-embed .beep-action-retweet:hover svg { background: var(--beep-repost-soft); } .beep-embed .beep-action.is-reposted { color: var(--beep-repost); } -/* Like - pink */ -.beep-embed .beep-action-like:hover { - color: var(--beep-like); - background: rgba(249, 24, 128, 0.1); -} - +.beep-embed .beep-action-like:hover { color: var(--beep-like); } +.beep-embed .beep-action-like:hover svg { background: var(--beep-like-soft); } .beep-embed .beep-action.is-liked { color: var(--beep-like); } -/* Bookmark - blue */ -.beep-embed .beep-action-bookmark:hover { - color: var(--beep-accent); - background: rgba(29, 155, 240, 0.1); -} +.beep-embed .beep-action-share:hover { color: var(--beep-accent); } +.beep-embed .beep-action-share:hover svg { background: var(--beep-accent-soft); } -.beep-embed .beep-action.is-bookmarked { color: var(--beep-accent); } - -/* Share - blue */ -.beep-embed .beep-action-share:hover { - color: var(--beep-accent); - background: rgba(29, 155, 240, 0.1); -} +/* Like icon swap */ +.beep-embed .beep-icon-outline, +.beep-embed .beep-icon-filled { display: block; } +.beep-embed .beep-action-like .beep-icon-filled { display: none; } +.beep-embed .beep-action-like.is-liked .beep-icon-outline { display: none; } +.beep-embed .beep-action-like.is-liked .beep-icon-filled { display: block; } /* Heart pop animation */ -.beep-embed .beep-action-like.beep-just-liked .beep-icon-filled { +.beep-embed .beep-action-like.beep-just-liked .beep-icon-filled svg { animation: beep-heart-pop 0.55s ease; } @@ -424,35 +654,22 @@ 100% { transform: scale(1); } } -/* Retweet spin animation */ -.beep-embed .beep-action-retweet.beep-just-retweeted svg { - animation: beep-retweet-spin 0.4s ease; -} - -@keyframes beep-retweet-spin { - 0% { transform: rotate(0deg) scale(1); } - 50% { transform: rotate(180deg) scale(1.2); } - 100% { transform: rotate(360deg) scale(1); } -} - -/* Media */ -.beep-embed .beep-media-attachments { - margin-top: 12px; - border-radius: 16px; - overflow: hidden; - border: 1px solid var(--beep-border); -} +/* ---------- Media ---------- */ +.beep-embed .beep-media-attachments { margin-top: 12px; } .beep-embed .beep-media-attachments img { width: 100%; height: auto; display: block; - max-height: 400px; + margin: 0; + max-height: 510px; object-fit: cover; + border-radius: 16px; + border: 1px solid var(--beep-border); } -/* Image grid */ -.beep-embed .beep-media-grid { +/* Image gallery (1–4 images, Twitter-style adaptive grid) */ +.beep-embed .beep-gallery { display: grid; gap: 2px; border-radius: 16px; @@ -460,31 +677,91 @@ border: 1px solid var(--beep-border); } -.beep-embed .beep-media-grid-2 { grid-template-columns: 1fr 1fr; } -.beep-embed .beep-media-grid-3 .beep-media-grid-item:first-child { grid-column: span 2; } -.beep-embed .beep-media-grid-4 { grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; } +.beep-embed .beep-gallery img { border: 0; border-radius: 0; max-height: none; } +.beep-embed .beep-gallery-2 { grid-template-columns: 1fr 1fr; } +.beep-embed .beep-gallery-3 { grid-template-columns: 1fr 1fr; } +.beep-embed .beep-gallery-3 .beep-gallery-item:first-child { grid-row: span 2; } +.beep-embed .beep-gallery-4 { grid-template-columns: 1fr 1fr; } -.beep-embed .beep-media-grid-item { +.beep-embed .beep-gallery-item { overflow: hidden; - aspect-ratio: 16 / 9; + cursor: pointer; } -.beep-embed .beep-media-grid-item img { +.beep-embed .beep-gallery-2 .beep-gallery-item, +.beep-embed .beep-gallery-3 .beep-gallery-item, +.beep-embed .beep-gallery-4 .beep-gallery-item { aspect-ratio: 16 / 9; } + +.beep-embed .beep-gallery-3 .beep-gallery-item:first-child { aspect-ratio: auto; } + +.beep-embed .beep-gallery-1 .beep-gallery-item img { + width: 100%; + height: auto; + max-height: 510px; + object-fit: cover; + display: block; +} + +.beep-embed .beep-gallery-item a { display: block; height: 100%; } + +.beep-embed .beep-gallery-2 .beep-gallery-item img, +.beep-embed .beep-gallery-3 .beep-gallery-item img, +.beep-embed .beep-gallery-4 .beep-gallery-item img { width: 100%; height: 100%; object-fit: cover; - max-height: none; + display: block; } -/* Polls */ -.beep-embed .beep-poll-container { - margin-top: 12px; - border: 1px solid var(--beep-border); +/* Video embed */ +.beep-embed .beep-video-embed { border-radius: 16px; - padding: 16px; - background: transparent; + overflow: hidden; + border: 1px solid var(--beep-border); } +.beep-embed .beep-video-embed iframe, +.beep-embed .beep-video-embed video { + display: block; + width: 100%; + border: 0; +} + +/* GIF embed */ +.beep-embed .beep-gif-embed { + margin-top: 12px; + border-radius: 16px; + overflow: hidden; + border: 1px solid var(--beep-border); + position: relative; +} + +.beep-embed .beep-gif-embed img { + width: 100%; + height: auto; + max-height: 510px; + object-fit: cover; + display: block; + margin: 0; +} + +.beep-embed .beep-gif-embed::after { + content: 'GIF'; + position: absolute; + left: 10px; + bottom: 10px; + background: rgba(0, 0, 0, 0.75); + color: #fff; + font-size: 11px; + font-weight: 800; + padding: 2px 6px; + border-radius: 4px; + letter-spacing: 0.5px; +} + +/* ---------- Polls ---------- */ +.beep-embed .beep-poll-container { margin-top: 12px; } + .beep-embed .beep-poll-question { font-size: 15px; font-weight: 700; @@ -492,42 +769,40 @@ margin-bottom: 12px; } -.beep-embed .beep-poll-options { +.beep-embed .beep-poll-container .beep-poll-options { display: flex; flex-direction: column; - gap: 8px; + gap: 6px; } -.beep-embed .beep-poll-option { - border: 1px solid var(--beep-border); +.beep-embed .beep-poll-vote-btn { + border: 1px solid var(--beep-accent); border-radius: 9999px; - padding: 10px 16px; + padding: 8px 16px; cursor: pointer; - font-size: 14px; + font-family: inherit; + font-size: 15px; font-weight: 700; - color: var(--beep-text); + color: var(--beep-accent); background: transparent; - transition: background-color 0.15s ease, border-color 0.15s ease; - position: relative; - overflow: hidden; + text-align: center; + transition: background-color 0.15s ease; } -.beep-embed .beep-poll-option:hover { - background: var(--beep-card-hover); - border-color: var(--beep-muted); -} - -.beep-embed .beep-poll-option.is-voted { border-color: var(--beep-accent); } +.beep-embed .beep-poll-vote-btn:hover { background: var(--beep-accent-soft); } .beep-embed .beep-poll-result { - border: 1px solid var(--beep-border); - border-radius: 9999px; - padding: 10px 16px; - font-size: 14px; - font-weight: 700; - color: var(--beep-text); position: relative; + border-radius: 6px; + padding: 7px 12px; + font-size: 15px; + color: var(--beep-text); overflow: hidden; + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + min-height: 32px; } .beep-embed .beep-poll-result-bar { @@ -535,29 +810,84 @@ left: 0; top: 0; bottom: 0; - background: var(--beep-accent); + background: var(--beep-muted); opacity: 0.3; + border-radius: 6px; max-width: 100%; + min-width: 6px; } -.beep-embed .beep-poll-result.is-voted { border-color: var(--beep-accent); } -.beep-embed .beep-poll-result-text { position: relative; z-index: 1; } -.beep-embed .beep-poll-end { font-size: 13px; color: var(--beep-muted); margin-top: 8px; font-weight: 400; } +.beep-embed .beep-poll-result.is-voted { font-weight: 700; } +.beep-embed .beep-poll-result.is-voted .beep-poll-result-bar { background: var(--beep-accent); opacity: 0.55; } -/* Quotes */ -.beep-embed .beep-quoted-container { +.beep-embed .beep-poll-result-text { + position: relative; + z-index: 1; + display: inline-flex; + align-items: center; + gap: 4px; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; +} + +.beep-embed .beep-poll-check { flex-shrink: 0; } + +.beep-embed .beep-poll-result-percent { + position: relative; + z-index: 1; + font-variant-numeric: tabular-nums; + font-weight: 700; + flex-shrink: 0; +} + +.beep-embed .beep-poll-meta { + display: flex; + align-items: center; + gap: 4px; + margin-top: 10px; + font-size: 13px; + color: var(--beep-muted); +} + +.beep-embed .beep-poll-votes, +.beep-embed .beep-poll-end, +.beep-embed .beep-poll-dot { color: var(--beep-muted); font-weight: 400; } + +/* ---------- Quoted beep ---------- */ +.beep-embed .beep-quoted-container, +.beep-embed .beep-quoted-preview-card { margin-top: 12px; border: 1px solid var(--beep-border); border-radius: 16px; - padding: 12px 16px; + padding: 12px; background: transparent; + transition: background-color 0.15s ease; } -.beep-embed .beep-quoted-post { text-decoration: none; color: inherit; } +.beep-embed .beep-quoted-container:hover { background: var(--beep-hover); } + +.beep-embed .beep-quoted-post { text-decoration: none; color: inherit; display: block; } .beep-embed .beep-quoted-post:hover { text-decoration: none; } -.beep-embed .beep-quoted-meta { font-size: 13px; color: var(--beep-muted); margin-bottom: 4px; } -.beep-embed .beep-quoted-meta .beep-name { font-weight: 700; } +.beep-embed .beep-quoted-meta { + display: flex; + align-items: center; + gap: 4px; + font-size: 14px; + color: var(--beep-muted); + margin-bottom: 4px; + min-width: 0; +} + +.beep-embed .beep-quoted-meta .beep-name, +.beep-embed .beep-quoted-name { font-weight: 700; color: var(--beep-text); font-size: 14px; } +.beep-embed .beep-quoted-handle, .beep-embed .beep-quoted-time, .beep-embed .beep-quoted-dot { color: var(--beep-muted); } + +.beep-embed .beep-quoted-avatar img { border-radius: 50%; display: block; } + +.beep-embed .beep-quoted-preview-card { display: flex; gap: 8px; margin-top: 0; } +.beep-embed .beep-quoted-body { flex: 1; min-width: 0; } .beep-embed .beep-quoted-text { font-size: 14px; @@ -570,13 +900,12 @@ overflow: hidden; } -/* Voice note */ +/* ---------- Voice note (post) ---------- */ .beep-embed .beep-voice-note { - margin-top: 12px; display: flex; align-items: center; gap: 12px; - background: var(--beep-card-hover); + background: var(--beep-input-bg); border-radius: 9999px; padding: 10px 16px; max-width: 400px; @@ -594,8 +923,10 @@ cursor: pointer; flex-shrink: 0; padding: 0; + transition: background-color 0.15s ease; } +.beep-embed .beep-voice-play:hover { background: var(--beep-accent-hover); } .beep-embed .beep-voice-play svg { width: 14px; height: 14px; fill: #fff; } .beep-embed .beep-voice-waveform { @@ -614,71 +945,174 @@ opacity: 0.6; } -.beep-embed .beep-voice-duration { font-size: 13px; color: var(--beep-muted); font-variant-numeric: tabular-nums; } +.beep-embed .beep-voice-duration { + font-size: 13px; + color: var(--beep-muted); + font-variant-numeric: tabular-nums; +} -/* Replies */ -.beep-embed .beep-replies { - margin-top: 8px; - padding-left: 0; +/* ---------- Bottom bar (brand wordmark + delete) ---------- */ +.beep-embed .beep-bottom-bar { + display: flex; + align-items: center; + justify-content: flex-end; + gap: 8px; + margin-top: 2px; +} + +.beep-embed .beep-post-wordmark { + opacity: 0.55; + flex: 0 0 auto; + height: 16px; + width: auto; + transition: opacity 0.15s ease; + user-select: none; +} + +.beep-embed .beep-post:hover .beep-post-wordmark, +.beep-embed .beep-reply:hover .beep-post-wordmark { opacity: 0.85; } + +.beep-embed .beep-delete-link { + background: transparent; + border: 0; + color: var(--beep-muted); + font-family: inherit; + font-size: 16px; + line-height: 1; + cursor: pointer; + width: 26px; + height: 26px; + border-radius: 50%; + display: inline-flex; + align-items: center; + justify-content: center; + padding: 0; + flex-shrink: 0; + transition: color 0.15s ease, background-color 0.15s ease; +} + +.beep-embed .beep-delete-link:hover { color: var(--beep-danger); background: var(--beep-danger-soft); } + +/* ---------- Replies (thread view) ---------- */ +.beep-embed .beep-replies { margin-top: 4px; } + +.beep-embed .beep-replies-nested { border-left: 2px solid var(--beep-border); - margin-left: 24px; + margin-left: 18px; + padding-left: 8px; } .beep-embed .beep-reply { display: flex; gap: 10px; - padding: 10px 16px; - border-bottom: 1px solid var(--beep-border); - background: var(--beep-bg); - transition: background-color 0.15s ease; + padding: 10px 0 6px; + border-top: 1px solid var(--beep-border); } -.beep-embed .beep-reply:last-child { border-bottom: 0; } .beep-embed .beep-reply-body { flex: 1; min-width: 0; } -.beep-embed .beep-reply .beep-text { font-size: 14px; margin-bottom: 0; } -.beep-embed .beep-reply .beep-meta { font-size: 13px; margin-bottom: 0; } -.beep-embed .beep-reply .beep-avatar { width: 36px !important; height: 36px !important; min-width: 36px !important; max-width: 36px !important; } +.beep-embed .beep-reply .beep-text { font-size: 14px; } +.beep-embed .beep-reply .beep-meta { font-size: 14px; } -/* Reply form */ +.beep-embed .beep-actions-reply { + justify-content: flex-start; + gap: 32px; + max-width: none; + margin-top: 2px; +} + +.beep-embed .beep-actions-reply .beep-action svg { + width: 30px; + height: 30px; + padding: 7px; +} + +/* ---------- Reply form ---------- */ .beep-embed .beep-reply-form { - margin-top: 8px; - padding: 12px 16px; - border-top: 1px solid var(--beep-border); - border-bottom: 1px solid var(--beep-border); display: flex; gap: 10px; align-items: flex-start; - background: var(--beep-bg); + margin-top: 8px; + padding: 12px 0 4px; + border-top: 1px solid var(--beep-border); } .beep-embed .beep-reply-form[hidden] { display: none; } .beep-embed .beep-reply-form-body { flex: 1; min-width: 0; } -.beep-embed .beep-feed .beep-reply-input { - width: 100% !important; - border: 0 !important; - outline: 0 !important; - resize: none !important; - background: transparent !important; - font-family: inherit !important; +.beep-embed .beep-reply-input { font-size: 15px !important; - color: var(--beep-text) !important; - padding: 6px 0 !important; - min-height: 36px; - box-shadow: none !important; - caret-color: var(--beep-accent); + min-height: 38px; + padding: 8px 0 !important; } -.beep-embed .beep-feed .beep-reply-input::placeholder { color: var(--beep-muted) !important; } - .beep-embed .beep-reply-footer { display: flex; justify-content: flex-end; align-items: center; - margin-top: 8px; + gap: 12px; + margin-top: 4px; } -/* Animations */ +.beep-embed .beep-pending { + padding: 10px 0; + color: var(--beep-muted); + font-size: 14px; + font-style: italic; +} + +/* ---------- Notices ---------- */ +.beep-embed .beep-notice { + padding: 14px 16px; + border-bottom: 1px solid var(--beep-border); + font-size: 14px; +} + +.beep-embed .beep-notice p { margin: 0; color: var(--beep-muted); } +.beep-embed .beep-notice-banned { color: var(--beep-danger); font-style: italic; } + +.beep-embed .beep-public-notice { + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + padding: 12px 16px; + background: var(--beep-card-bg); + border-bottom: 1px solid var(--beep-border); + color: var(--beep-muted); + font-size: 14px; +} + +.beep-embed .beep-public-notice a { color: var(--beep-accent); text-decoration: none; } +.beep-embed .beep-public-notice a:hover { text-decoration: underline; } + +/* ---------- Loading / infinite scroll ---------- */ +.beep-embed .beep-loading { opacity: 0.6; pointer-events: none; } + +.beep-embed .beep-spinner { + display: inline-block; + width: 22px; + height: 22px; + border: 3px solid var(--beep-accent-soft); + border-top-color: var(--beep-accent); + border-radius: 50%; + animation: beep-spin 0.7s linear infinite; + vertical-align: middle; +} + +@keyframes beep-spin { to { transform: rotate(360deg); } } + +.beep-embed .beep-loading-more, +.beep-embed .beep-no-more { + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + padding: 20px; + color: var(--beep-muted); + font-size: 14px; +} + +/* ---------- New post animation ---------- */ .beep-embed .beep-new { animation: beep-slide-in 0.3s ease; } @keyframes beep-slide-in { @@ -686,123 +1120,54 @@ to { opacity: 1; transform: translateY(0); } } -/* Post badge */ -.beep-embed .beep-post-badge { - width: 24px; - height: 24px; - flex-shrink: 0; - opacity: 0.7; - margin-left: 4px; -} - -.beep-embed .beep-post-badge:hover { opacity: 1; cursor: pointer; } - -/* Loading */ -.beep-embed .beep-loading { opacity: 0.5; pointer-events: none; } - -/* Mobile */ -@media (max-width: 640px) { - .beep-embed .beep-feed { margin: 0; border-left: 0; border-right: 0; } - .beep-embed .beep-post { padding: 12px 16px; } - .beep-embed .beep-actions { max-width: none; } - .beep-embed .beep-compose { padding: 12px 16px; } - .beep-embed .beep-feed-header { padding: 12px 16px; } -} - -/* Delete */ -/* .beep-delete-row replaced by .beep-bottom-bar */ - -.beep-embed .beep-delete-link { - background: transparent; - border: 0; - color: var(--beep-muted); - font-family: inherit; - font-size: 13px; - cursor: pointer; - padding: 4px 8px; - border-radius: 4px; - transition: color 0.15s ease, background-color 0.15s ease; -} - -.beep-embed .beep-delete-link:hover { color: #F4212E; background: rgba(244, 33, 46, 0.1); } - -/* Notice */ -.beep-embed .beep-notice { padding: 14px 20px; border-bottom: 1px solid var(--beep-border); font-size: 14px; } -.beep-embed .beep-notice p { margin: 0; color: var(--beep-muted); } -.beep-embed .beep-notice-banned { color: #F4212E; font-style: italic; } - -/* Header post button */ -.beep-embed .beep-post-btn-header { - background: var(--beep-accent); - color: #fff; - border: 0; - border-radius: 9999px; - padding: 8px 20px; - font-weight: 700; - font-size: 15px; - cursor: pointer; - font-family: inherit; - transition: background-color 0.15s ease, transform 0.1s ease; -} - -.beep-embed .beep-post-btn-header:hover { background: var(--beep-accent-hover); } -.beep-embed .beep-post-btn-header:active { transform: scale(0.96); } - -/* Back link */ -.beep-embed .beep-back-link { - display: flex; - align-items: center; - gap: 8px; - padding: 12px 20px; - border-bottom: 1px solid var(--beep-border); - background: var(--beep-bg); - color: var(--beep-text); - text-decoration: none; - font-weight: 700; - font-size: 19px; - transition: background-color 0.15s ease; -} - -.beep-embed .beep-back-link:hover { background: var(--beep-card-hover); color: var(--beep-text); text-decoration: none; } -.beep-embed .beep-back-link svg { width: 20px; height: 20px; fill: currentColor; } - -/* Thread modal */ -.beep-embed .beep-thread-modal { +/* ---------- Thread / conversation modal ---------- */ +.beep-embed .beep-thread-modal, +.beep-embed .beep-gif-modal { position: fixed; inset: 0; - z-index: 1000; + z-index: 100000; display: flex; align-items: flex-start; justify-content: center; - background: rgba(0, 0, 0, 0.4); + background: rgba(91, 112, 131, 0.4); backdrop-filter: blur(4px); + -webkit-backdrop-filter: blur(4px); + padding: 5vh 12px; } -.beep-embed .beep-thread-modal[hidden] { display: none; } +.beep-embed .beep-thread-modal[hidden], +.beep-embed .beep-gif-modal[hidden] { display: none !important; } -.beep-embed .beep-thread-content { - background: var(--beep-card-bg); +.beep-embed .beep-thread-dialog, +.beep-embed .beep-gif-dialog { + background: var(--beep-bg); + color: var(--beep-text); width: 100%; max-width: 600px; max-height: 90vh; overflow-y: auto; - border-left: 1px solid var(--beep-border); - border-right: 1px solid var(--beep-border); + border-radius: 16px; + border: 1px solid var(--beep-border); + box-shadow: 0 8px 28px rgba(0, 0, 0, 0.35); } -.beep-embed .beep-thread-header { +.beep-embed .beep-thread-header, +.beep-embed .beep-gif-header { display: flex; align-items: center; gap: 16px; - padding: 12px 20px; + padding: 10px 16px; border-bottom: 1px solid var(--beep-border); position: sticky; top: 0; - background: var(--beep-card-bg); + background: var(--beep-bg-trans); + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); z-index: 1; } -.beep-embed .beep-thread-close { +.beep-embed .beep-thread-close, +.beep-embed .beep-gif-close { background: transparent; border: 0; color: var(--beep-text); @@ -814,267 +1179,181 @@ align-items: center; justify-content: center; padding: 0; + flex-shrink: 0; transition: background-color 0.15s ease; } -.beep-embed .beep-thread-close:hover { background: var(--beep-card-hover); } -.beep-embed .beep-thread-close svg { width: 20px; height: 20px; fill: currentColor; } -.beep-embed .beep-thread-title { font-size: 18px; font-weight: 700; color: var(--beep-text); } +.beep-embed .beep-thread-close:hover, +.beep-embed .beep-gif-close:hover { background: var(--beep-hover); } -/* Tooltip */ -.beep-embed .beep-tip { - position: absolute; - margin-left: 8px; - padding: 4px 10px; - background: var(--beep-text); - color: var(--beep-bg); - border-radius: 4px; - font-size: 12px; - white-space: nowrap; - pointer-events: none; - z-index: 100; - animation: beep-tip-fade 1.5s ease forwards; +.beep-embed .beep-thread-close svg, +.beep-embed .beep-gif-close svg { width: 20px; height: 20px; fill: currentColor; } + +.beep-embed .beep-thread-title, +.beep-embed .beep-gif-title { font-size: 17px; font-weight: 800; color: var(--beep-text); } + +.beep-embed .beep-thread-loading { + display: flex; + justify-content: center; + padding: 40px 0; } -@keyframes beep-tip-fade { - 0% { opacity: 0; transform: translateY(-4px); } - 20% { opacity: 1; transform: translateY(0); } - 80% { opacity: 1; } - 100% { opacity: 0; } +.beep-embed .beep-thread-content .beep-post { + cursor: default; + border-bottom: 0; } -/* GIF */ -.beep-embed .beep-gif-embed { - margin-top: 12px; - border-radius: 12px; +.beep-embed .beep-thread-error { + padding: 32px 16px; + text-align: center; + color: var(--beep-muted); +} + +/* ---------- GIF picker ---------- */ +.beep-embed .beep-gif-search-bar { + display: flex; + gap: 8px; + padding: 12px 16px; + border-bottom: 1px solid var(--beep-border); +} + +.beep-embed .beep-gif-search { + flex: 1; + background: var(--beep-input-bg); + border: 1px solid transparent; + border-radius: 9999px; + padding: 10px 16px; + font-family: inherit; + font-size: 14px; + color: var(--beep-text); + outline: 0; + transition: border-color 0.15s ease; +} + +.beep-embed .beep-gif-search:focus { border-color: var(--beep-accent); } +.beep-embed .beep-gif-search::placeholder { color: var(--beep-muted); } + +.beep-embed .beep-gif-search-btn { + background: var(--beep-accent); + color: var(--beep-on-accent); + border: 0; + border-radius: 9999px; + padding: 0 18px; + font-family: inherit; + font-size: 14px; + font-weight: 700; + cursor: pointer; +} + +.beep-embed .beep-gif-search-btn:hover { background: var(--beep-accent-hover); } + +.beep-embed .beep-gif-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 4px; + padding: 12px 16px 16px; + min-height: 160px; +} + +.beep-embed .beep-gif-grid > p { grid-column: 1 / -1; margin: 0; color: var(--beep-muted); } + +.beep-embed .beep-gif-item { + border-radius: 8px; overflow: hidden; - border: 1px solid var(--beep-border); + cursor: pointer; + aspect-ratio: 1 / 1; + background: var(--beep-input-bg); + transition: opacity 0.15s ease; } -.beep-embed .beep-gif-embed img { +.beep-embed .beep-gif-item:hover { opacity: 0.8; } + +.beep-embed .beep-gif-item img { width: 100%; - height: auto; - max-height: 400px; + height: 100%; object-fit: cover; display: block; margin: 0; - border-radius: 12px; } -/* Profile header */ -.beep-embed .beep-profile-header { padding: 16px 20px; border-bottom: 1px solid var(--beep-border); } -.beep-embed .beep-profile-name { font-size: 20px; font-weight: 800; color: var(--beep-text); margin-bottom: 2px; } -.beep-embed .beep-profile-meta { font-size: 14px; color: var(--beep-muted); } -.beep-embed .beep-profile-stats { display: flex; gap: 20px; margin-top: 12px; } -.beep-embed .beep-profile-stat { color: var(--beep-muted); font-size: 14px; cursor: pointer; } -.beep-embed .beep-profile-stat:hover { text-decoration: underline; } -.beep-embed .beep-profile-stat strong { color: var(--beep-text); font-weight: 700; } - -/* Tabs */ -.beep-embed .beep-tabs { - display: flex; - border-bottom: 1px solid var(--beep-border); - background: var(--beep-card-bg); -} - -.beep-embed .beep-tab { - flex: 1; - background: transparent; - border: 0; - padding: 14px 0; - font-family: inherit; - font-size: 15px; - font-weight: 700; - color: var(--beep-muted); - cursor: pointer; - position: relative; - transition: background-color 0.15s ease, color 0.15s ease; -} - -.beep-embed .beep-tab:hover { background: var(--beep-card-hover); color: var(--beep-text); } -.beep-embed .beep-tab.is-active { color: var(--beep-text); } -.beep-embed .beep-tab.is-active::after { - content: ''; - position: absolute; - left: 50%; - bottom: 0; - transform: translateX(-50%); - width: 56px; - height: 4px; - border-radius: 9999px; - background: var(--beep-accent); -} - -/* Focus */ -.beep-embed .beep-feed .beep-input:focus, -.beep-embed .beep-feed .beep-input:focus-visible, -.beep-embed .beep-feed .beep-reply-input:focus, -.beep-embed .beep-feed .beep-reply-input:focus-visible { - outline: 0 !important; - box-shadow: none !important; -} - -.beep-embed .beep-feed .beep-compose:focus-within { background: rgba(29, 155, 240, 0.03); } - -/* Hidden */ +/* ---------- Hidden ---------- */ .beep-embed [hidden] { display: none !important; } - -/* Magic Login Form */ -.beep-embed .beep-magic-login { - padding: 24px; - display: flex; - justify-content: center; -} - -.beep-embed .beep-magic-login-card { - background: #192734; - border-radius: 16px; - padding: 32px; - width: 100%; - max-width: 400px; - text-align: center; -} - -.beep-embed .beep-magic-login-header { - margin-bottom: 24px; -} - -.beep-embed .beep-magic-login-header svg { - margin-bottom: 16px; -} - -.beep-embed .beep-magic-login-header h3 { - color: #E7E9EA; - font-size: 20px; - font-weight: 700; - margin: 0 0 8px 0; -} - -.beep-embed .beep-magic-login-header p { - color: #71767B; - font-size: 14px; - margin: 0; -} - -.beep-embed .beep-magic-login-form { - display: flex; - flex-direction: column; - gap: 12px; -} - -.beep-embed .beep-magic-email { - width: 100%; - padding: 12px 16px; - background: #22303C; - border: 1px solid #333F4D; - border-radius: 8px; - color: #E7E9EA; - font-size: 16px; - box-sizing: border-box; -} - -.beep-embed .beep-magic-email:focus { - outline: none; - border-color: #1D9BF0; -} - -.beep-embed .beep-magic-email::placeholder { - color: #71767B; -} - -.beep-embed .beep-button.beep-magic-submit { - width: 100%; - padding: 12px 24px; - background: #1D9BF0; - color: #fff; - border: none; - border-radius: 8px; - font-size: 16px; - font-weight: 600; - cursor: pointer; - transition: background 0.2s; -} - -.beep-embed .beep-button.beep-magic-submit:hover { - background: #1A8CD8; -} - -.beep-embed .beep-button.beep-magic-submit:disabled { - opacity: 0.6; - cursor: not-allowed; -} - -.beep-embed .beep-magic-success { - display: flex; - align-items: center; - justify-content: center; - gap: 8px; - padding: 12px; - background: rgba(0, 186, 124, 0.1); - border-radius: 8px; - color: #00BA7C; - font-size: 14px; -} - -.beep-embed .beep-magic-error { - padding: 12px; - background: rgba(224, 36, 94, 0.1); - border-radius: 8px; - color: #F4212E; - font-size: 14px; - margin-top: 12px; -} - -/* Public feed notice */ -.beep-embed .beep-public-notice { - display: flex; - align-items: center; - justify-content: center; - gap: 8px; - padding: 12px 16px; - background: #192734; - border-bottom: 1px solid #333F4D; - color: #71767B; - font-size: 14px; -} - -.beep-embed .beep-public-notice a { - color: #1D9BF0; - text-decoration: none; -} - -.beep-embed .beep-public-notice a:hover { - text-decoration: underline; -} - - - - -/* Bottom bar: wordmark left, delete right */ -/* Bottom bar: wordmark left, delete right */ -/* Bottom bar: delete left, wordmark right (blue, large) */ -/* Bottom bar: wordmark on the left, delete furthest right */ -.beep-embed .beep-bottom-bar { +/* ---------- Toasts (appended to , theme-independent) ---------- */ +.beep-toast { + position: fixed; + left: 50%; + bottom: 32px; + transform: translate(-50%, 16px); + z-index: 100001; display: flex; align-items: center; - justify-content: flex-end; - gap: 8px; - margin-top: 4px; + gap: 16px; + background: #1D9BF0; + color: #fff; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; + font-size: 14px; + font-weight: 500; + padding: 12px 20px; + border-radius: 8px; + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3); + opacity: 0; + transition: opacity 0.25s ease, transform 0.25s ease; + max-width: calc(100vw - 32px); } -/* Brand wordmark (inline SVG recreation of the 3D "Beep!" logo) */ -.beep-embed .beep-post-wordmark { - opacity: 0.85; - flex: 0 0 auto; - height: 18px; - width: auto; - transition: opacity 0.15s ease; - user-select: none; + +.beep-toast.beep-toast-visible { opacity: 1; transform: translate(-50%, 0); } +.beep-toast.beep-toast-error { background: #F4212E; } +.beep-toast.beep-toast-success { background: #00BA7C; } + +.beep-toast .beep-toast-undo-btn { + background: transparent; + border: 0; + color: #fff; + font-family: inherit; + font-size: 14px; + font-weight: 800; + cursor: pointer; + padding: 4px 8px; + border-radius: 6px; + text-transform: uppercase; + letter-spacing: 0.3px; } -.beep-embed .beep-bottom-bar-reply .beep-post-wordmark { - height: 15px; + +.beep-toast .beep-toast-undo-btn:hover { background: rgba(255, 255, 255, 0.15); } + +/* ---------- Lightbox (appended to ) ---------- */ +.beep-lightbox { + position: fixed; + inset: 0; + z-index: 100002; + background: rgba(0, 0, 0, 0.9); + display: flex; + align-items: center; + justify-content: center; + cursor: zoom-out; + padding: 24px; } -.beep-embed .beep-post:hover .beep-post-wordmark, -.beep-embed .beep-reply:hover .beep-post-wordmark { - opacity: 0.9; + +.beep-lightbox img { + max-width: 100%; + max-height: 100%; + object-fit: contain; + border-radius: 4px; +} + +/* ---------- Mobile ---------- */ +@media (max-width: 640px) { + .beep-embed.beep-feed { margin: 0; border-left: 0; border-right: 0; } + .beep-embed .beep-post, + .beep-embed .beep-compose, + .beep-embed .beep-feed-header { padding-left: 12px; padding-right: 12px; } + .beep-embed .beep-actions { max-width: none; } + .beep-embed .beep-avatar { width: 40px; height: 40px; min-width: 40px; } + .beep-embed .beep-input { font-size: 18px !important; } + .beep-embed .beep-gif-grid { grid-template-columns: repeat(2, 1fr); } + .beep-embed .beep-thread-modal, + .beep-embed .beep-gif-modal { padding: 0; } + .beep-embed .beep-thread-dialog, + .beep-embed .beep-gif-dialog { max-height: 100vh; height: 100%; border-radius: 0; border: 0; } } diff --git a/assets/beep.js b/assets/beep.js index 25c91e8..b048931 100755 --- a/assets/beep.js +++ b/assets/beep.js @@ -10,6 +10,12 @@ function init() { var feeds = document.querySelectorAll('[data-beep-feed]'); Array.prototype.forEach.call(feeds, initFeed); + // Delegate on document so dynamically injected content (thread modal, + // GIF picker, toasts) is handled too. + document.addEventListener('click', onClick); + document.addEventListener('input', onInput); + document.addEventListener('keydown', onKeydown); + document.addEventListener('keydown', onEscape); } var feedState = { offset: 0, loading: false, hasMore: true, currentSort: 'latest' }; @@ -20,9 +26,6 @@ initSortTabs(feed); initMedia(compose, feed); initInfiniteScroll(feed); - feed.addEventListener('click', onClick); - feed.addEventListener('input', onInput); - feed.addEventListener('keydown', onKeydown); } /* ---------- Infinite scroll ---------- */ @@ -119,7 +122,7 @@ if (empty) empty.remove(); // Reload feed to get the fully rendered post (includes media, quotes, polls) - reloadFeed(feed, 'recent'); + reloadFeed(feed, feedState.currentSort); input.value = ''; clearQuotePreview(compose); @@ -134,7 +137,7 @@ var pending = compose._beepPendingImages; var pendingVoice = compose._beepPendingVoice; var uploadDone = function () { - reloadFeed(feed, 'latest'); + reloadFeed(feed, feedState.currentSort); }; var tasks = []; @@ -405,6 +408,29 @@ closeLightbox(); return; } + + // Modal backdrop click closes + if (t.matches && t.matches('[data-beep-thread-modal]')) { closeThread(); return; } + if (t.matches && t.matches('[data-beep-gif-modal]')) { closeGifPicker(); return; } + + // Click anywhere else on a feed post opens the conversation + var openPost = t.closest('[data-beep-open-thread]'); + if (openPost && !t.closest('a, button, label, input, textarea, audio, video, .beep-media-grid-item, .beep-gif-embed, [data-beep-reply-form]')) { + // Don't hijack text selection + var sel = window.getSelection && window.getSelection(); + if (sel && sel.toString()) return; + openThread(openPost.dataset.beepOpenThread); + } + } + + function onEscape(e) { + if (e.key !== 'Escape') return; + var lightbox = document.querySelector('.beep-lightbox'); + if (lightbox) { closeLightbox(); return; } + var gifModal = document.querySelector('[data-beep-gif-modal]:not([hidden])'); + if (gifModal) { closeGifPicker(); return; } + var threadModal = document.querySelector('[data-beep-thread-modal]:not([hidden])'); + if (threadModal) closeThread(); } function onInput(e) { @@ -423,12 +449,20 @@ } function onKeydown(e) { + // Enter in the GIF search box runs the search + if (e.key === 'Enter' && e.target.matches && e.target.matches('[data-beep-gif-search]')) { + e.preventDefault(); + var compose = document.querySelector('[data-beep-compose]'); + searchGifs(e.target.value, compose); + return; + } + // Cmd/Ctrl+Enter submits beeps and replies if (!((e.metaKey || e.ctrlKey) && e.key === 'Enter')) return; var input = e.target.closest('textarea'); if (!input) return; var wrap = input.closest('.beep-compose, .beep-reply-form'); if (!wrap) return; - var btn = wrap.querySelector('button'); + var btn = wrap.querySelector('[data-beep-submit], [data-beep-reply-submit]'); if (btn && !btn.disabled) btn.click(); } @@ -486,10 +520,16 @@ form.innerHTML = '
Reply submitted, awaiting moderation.
'; return; } - // Reload the full feed to get the properly rendered reply + input.value = ''; + // Inside the conversation modal: refresh just the thread. + if (btn.closest('[data-beep-thread-modal]')) { + refreshThread(); + return; + } + // Otherwise reload the feed with the current sort. var feed = btn.closest('[data-beep-feed]'); if (feed) { - reloadFeed(feed, 'latest'); + reloadFeed(feed, feedState.currentSort); } }) .catch(function () { @@ -786,66 +826,48 @@ } /* ---------- Thread / Conversation modal ---------- */ - function openThread(postId) { + function openThread(postId, keepScroll) { var modal = document.querySelector('[data-beep-thread-modal]'); var content = modal && modal.querySelector('[data-beep-thread-content]'); - var loading = modal && modal.querySelector('.beep-thread-loading'); + var loading = modal && modal.querySelector('[data-beep-thread-loading]'); if (!modal || !content) return; modal.hidden = false; - if (loading) loading.style.display = 'block'; - content.innerHTML = ''; + modal._beepThreadId = postId; + if (!keepScroll) { + if (loading) loading.hidden = false; + content.innerHTML = ''; + } + document.documentElement.style.overflow = 'hidden'; - fetch(config.restUrl + 'thread/' + postId, { + fetch(config.restUrl + 'thread/' + postId + '/html', { headers: { 'X-WP-Nonce': config.nonce }, credentials: 'same-origin' }) .then(handleResponse) .then(function(data) { - if (loading) loading.style.display = 'none'; - // data can be an array directly or { thread: [...] } - var thread = Array.isArray(data) ? data : (data.thread || []); - renderThread(thread, content); + if (loading) loading.hidden = true; + content.innerHTML = data.html || '
Nothing to show.
'; + // Sync the reply count back to the feed card + if (typeof data.reply_count !== 'undefined') { + var feedPost = document.querySelector('.beep-list [data-beep-post="' + postId + '"] [data-beep-reply-count]'); + if (feedPost) feedPost.textContent = data.reply_count > 0 ? data.reply_count : ''; + } }) .catch(function() { - if (loading) loading.textContent = 'Failed to load conversation.'; + if (loading) loading.hidden = true; + content.innerHTML = '
Failed to load conversation.
'; }); } + function refreshThread() { + var modal = document.querySelector('[data-beep-thread-modal]:not([hidden])'); + if (modal && modal._beepThreadId) openThread(modal._beepThreadId, true); + } + function closeThread() { var modal = document.querySelector('[data-beep-thread-modal]'); if (modal) modal.hidden = true; - } - - function renderThread(thread, container) { - if (!thread || !thread.length) { - container.innerHTML = '

No replies yet.

'; - return; - } - var html = ''; - thread.forEach(function(item) { - var indent = Math.min(item.depth, 5) * 24; - var author = item.author || {}; - var avatar = escapeHtml(author.avatar || 'https://www.gravatar.com/avatar/?s=48&d=mp'); - var name = escapeHtml(author.name || 'Unknown'); - var username = escapeHtml(author.username || ''); - var content_text = escapeHtml(item.content || ''); - var time_ago = escapeHtml(item.time_ago || ''); - var replyClass = item.depth > 0 ? 'beep-thread-reply' : 'beep-thread-root'; - html += '
'; - html += '
' + name + '
'; - html += '
'; - html += '
'; - html += '' + name + ''; - html += '@' + username + ''; - html += '' + time_ago + ''; - html += '
'; - html += '
' + content_text + '
'; - html += '
'; - html += '' + (item.reply_count || 0) + ' replies'; - html += '' + (item.like_count || 0) + ' likes'; - html += '
'; - }); - container.innerHTML = html; + document.documentElement.style.overflow = ''; } /* ---------- Quote posts ---------- */ @@ -1085,10 +1107,12 @@ }) .then(handleResponse) .then(function(data) { + // Inside the conversation modal: refresh just the thread. + if (btn.closest('[data-beep-thread-modal]')) { refreshThread(); return; } var postEl = btn.closest('[data-beep-post]'); if (postEl) { var list = postEl.closest('[data-beep-list]'); - if (list) reloadFeed(list.closest('[data-beep-feed]'), 'latest'); + if (list) reloadFeed(list.closest('[data-beep-feed]'), feedState.currentSort); } }) .catch(function() { @@ -1124,3 +1148,4 @@ return res.json(); } })(); +/* Beep frontend — end */ diff --git a/beep.php b/beep.php index 6bd0083..9465338 100755 --- a/beep.php +++ b/beep.php @@ -2,7 +2,7 @@ /** * Plugin Name: Beep * Description: A Twitter-style microblog for WordPress, with likes, replies, and Gravatars. Use the [beep_feed] shortcode on any page. - * Version: 1.7.4 + * Version: 1.8.0 * Requires at least: 6.0 * Requires PHP: 7.4 * Author: Sami Ahmed @@ -14,7 +14,7 @@ if (!defined('ABSPATH')) { exit; } -define('BEEP_VERSION', '1.7.4'); +define('BEEP_VERSION', '1.8.0'); define('BEEP_FILE', __FILE__); define('BEEP_DIR', plugin_dir_path(__FILE__)); define('BEEP_URL', plugin_dir_url(__FILE__)); diff --git a/includes/class-beep.php b/includes/class-beep.php index 6fec7e4..636c968 100755 --- a/includes/class-beep.php +++ b/includes/class-beep.php @@ -15,19 +15,14 @@ class Beep_Plugin { add_action('wp_enqueue_scripts', [__CLASS__, 'enqueue_assets']); - // Set default Giphy API key if not already set - if (!get_option('beep_giphy_key')) { - update_option('beep_giphy_key', 'xuxYxuIvzPqw5tPB4laa7sQO8dFtSlNS'); - } - // Elementor widget — only load if Elementor is active - add_action('elementor/widgets/register', function () { + add_action('elementor/widgets/register', function ($widgets_manager) { if (!class_exists('\Elementor\Widget_Base')) return; $widget_file = BEEP_DIR . 'includes/class-elementor-widget.php'; if (!file_exists($widget_file)) return; require_once $widget_file; - if (class_exists('\Elementor\Beep_Elementor_Widget')) { - $widgets_manager->register(new \Elementor\Beep_Elementor_Widget()); + if (class_exists('Beep_Elementor_Widget')) { + $widgets_manager->register(new Beep_Elementor_Widget()); } }, 5); diff --git a/includes/class-elementor-widget.php b/includes/class-elementor-widget.php index 69e91e6..ab805fd 100755 --- a/includes/class-elementor-widget.php +++ b/includes/class-elementor-widget.php @@ -8,7 +8,9 @@ if (!defined('ABSPATH')) { exit; } -if (did_action('elementor/core')) { +// Beep works fine without Elementor: this file is only loaded from the +// 'elementor/widgets/register' hook, and bails unless Elementor is present. +if (!class_exists('\Elementor\Widget_Base')) { return; } @@ -122,6 +124,6 @@ class Beep_Elementor_Widget extends \Elementor\Widget_Base { 'limit' => (int) $settings['limit'], ]; - echo Beep_Shortcode::render_beep_feed($atts); + echo Beep_Shortcode::render_feed($atts); } } \ No newline at end of file diff --git a/includes/class-shortcode.php b/includes/class-shortcode.php index e4fa10a..c231787 100755 --- a/includes/class-shortcode.php +++ b/includes/class-shortcode.php @@ -45,8 +45,7 @@ class Beep_Shortcode { $html .= beep_render_post($p); } if ($html === '' && $offset === 0) { - $empty_bird = beep_bird_svg( ['size' => 64, 'fill' => '#4a90c4'] ); - $html = '
' . $empty_bird . '

No beeps yet.

Be the first to share something!
'; + $html = self::empty_state_html(); } return rest_ensure_response(['html' => $html, 'sort' => $sort, 'has_more' => $has_more, 'offset' => $offset + count($posts)]); } @@ -84,30 +83,37 @@ class Beep_Shortcode { public static function render_feed($atts) { $atts = shortcode_atts([ 'limit' => 50, - 'header' => 'Beep!', - 'show_wordmark' => true, + 'header' => 'Messages from', + 'show_wordmark' => 'yes', 'sort' => 'latest', + 'theme' => 'auto', // auto | dark | light ], $atts, 'beep_feed'); $limit = max(1, (int) $atts['limit']); $sort = ($atts['sort'] === 'top') ? 'top' : 'latest'; + $theme = in_array($atts['theme'], ['dark', 'light'], true) ? $atts['theme'] : 'auto'; + $show_wordmark = !in_array(strtolower((string) $atts['show_wordmark']), ['no', 'false', '0'], true); $posts = ($sort === 'top') ? self::get_top_beeps($limit) : self::get_latest_beeps($limit); $has_posts = !empty($posts); ob_start(); ?> -
-
-
-
Messages from Beep!
-
+
+
+ + + + + Beep! + +
+ data-beep-sort="latest" role="tab">Latest + data-beep-sort="top" role="tab">Top
@@ -120,15 +126,23 @@ class Beep_Shortcode { echo beep_render_post($p); } } else { - $empty_bird = beep_bird_svg( ['size' => 64, 'fill' => '#4a90c4'] ); - echo '
' . $empty_bird . '

No beeps yet.

Be the first to share something!
'; + echo self::empty_state_html(); } ?>
-
+ +
64]); + return '
' . $empty_bird . '

No beeps yet.

Be the first to share something!
'; } /** @@ -222,6 +236,29 @@ class Beep_Shortcode { 'callback' => [__CLASS__, 'get_thread'], 'permission_callback' => '__return_true', ]); + register_rest_route('beep/v1', '/thread/(?P\d+)/html', [ + 'methods' => 'GET', + 'callback' => [__CLASS__, 'get_thread_html'], + 'permission_callback' => '__return_true', + ]); + } + + /** + * REST endpoint: server-rendered conversation view (root post + replies tree). + * Reuses the exact same markup/styles as the feed. + */ + public static function get_thread_html($request) { + $post_id = (int) $request->get_param('post_id'); + $post = get_post($post_id); + if (!$post || $post->post_type !== Beep_CPT::POST_TYPE || $post->post_status !== 'publish') { + return new WP_Error('not_found', 'Beep not found.', 404); + } + $html = beep_render_post($post, ['in_thread' => true, 'show_replies' => true]); + return rest_ensure_response([ + 'html' => $html, + 'id' => $post_id, + 'reply_count' => Beep_Replies::reply_count($post_id), + ]); } public static function get_thread($request) { diff --git a/includes/helpers.php b/includes/helpers.php index 08e83aa..55ee6b0 100755 --- a/includes/helpers.php +++ b/includes/helpers.php @@ -16,13 +16,10 @@ if (!defined('ABSPATH')) { */ function beep_bird_svg( $args = [] ) { $size = isset( $args['size'] ) ? (int) $args['size'] : 24; - $fill = isset( $args['fill'] ) ? $args['fill'] : '#0064cd'; $class = isset( $args['class'] ) ? trim( $args['class'] ) : ''; $extra = $class ? ' class="' . esc_attr( $class ) . '"' : ''; // Map brand colour to a CSS filter that turns black → target colour. - // Hue-rotate value derived from the target hex. - // #0064cd → hue-rotate ~190deg, #4a90c4 → hue-rotate ~200deg. $filter = 'brightness(0) saturate(100%) invert(22%) sepia(100%) saturate(10000%) hue-rotate(190deg)'; return '