feat: compact +photo icon replaces image bar in Thread Builder
Replace the always-visible Upload/AI button bar below each tweet with a compact camera icon in the top-right corner that fades in on hover and expands into a dropdown menu. Hides when an image is already attached. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
93e8ce8479
commit
1758ce9416
|
|
@ -930,16 +930,34 @@ const THREAD_CSS = `
|
|||
}
|
||||
.thread-export-menu button:hover { background: rgba(99,102,241,0.15); }
|
||||
.thread-export-menu button + button { border-top: 1px solid var(--rs-bg-hover); }
|
||||
.tweet-card__image-bar { display: flex; gap: 0.4rem; margin-top: 0.5rem; }
|
||||
.tweet-card__image-btn {
|
||||
padding: 0.25rem 0.5rem; border-radius: 6px; font-size: 0.7rem; font-weight: 600;
|
||||
cursor: pointer; transition: all 0.15s; background: transparent;
|
||||
color: var(--rs-text-muted); border: 1px solid var(--rs-input-border);
|
||||
display: inline-flex; align-items: center; gap: 0.25rem;
|
||||
.tweet-card__photo-btn {
|
||||
position: absolute; top: 8px; right: 8px; z-index: 5;
|
||||
width: 28px; height: 28px; border-radius: 50%; border: 1px solid var(--rs-input-border);
|
||||
background: var(--rs-bg-surface); color: var(--rs-text-muted); cursor: pointer;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
opacity: 0; transition: opacity 0.15s, border-color 0.15s, color 0.15s;
|
||||
}
|
||||
.tweet-card__image-btn:hover { border-color: #6366f1; color: #c4b5fd; }
|
||||
.tweet-card__image-btn:disabled { opacity: 0.5; cursor: not-allowed; }
|
||||
.tweet-card__image-btn svg { width: 12px; height: 12px; }
|
||||
.tweet-card:hover .tweet-card__photo-btn { opacity: 1; }
|
||||
.tweet-card__photo-btn:hover { border-color: #6366f1; color: #c4b5fd; }
|
||||
.tweet-card__photo-btn svg { width: 14px; height: 14px; }
|
||||
.tweet-card__photo-btn .photo-btn-plus {
|
||||
position: absolute; bottom: -1px; right: -3px; font-size: 10px; font-weight: 700;
|
||||
color: #6366f1; line-height: 1;
|
||||
}
|
||||
.tweet-card__photo-menu {
|
||||
position: absolute; top: 38px; right: 8px; z-index: 10;
|
||||
background: var(--rs-bg-surface); border: 1px solid var(--rs-input-border); border-radius: 8px;
|
||||
min-width: 160px; overflow: hidden; box-shadow: 0 8px 24px var(--rs-shadow-lg);
|
||||
}
|
||||
.tweet-card__photo-menu[hidden] { display: none; }
|
||||
.tweet-card__photo-menu button {
|
||||
display: flex; align-items: center; gap: 0.4rem; width: 100%;
|
||||
padding: 0.5rem 0.7rem; border: none; background: transparent;
|
||||
color: var(--rs-text-primary); font-size: 0.8rem; cursor: pointer; transition: background 0.1s;
|
||||
}
|
||||
.tweet-card__photo-menu button:hover { background: rgba(99,102,241,0.15); }
|
||||
.tweet-card__photo-menu button + button { border-top: 1px solid var(--rs-bg-hover); }
|
||||
.tweet-card__photo-menu button svg { width: 14px; height: 14px; }
|
||||
.tweet-card__attached-image { position: relative; margin-top: 0.5rem; border-radius: 8px; overflow: hidden; border: 1px solid var(--rs-input-border); }
|
||||
.tweet-card__attached-image img { display: block; width: 100%; height: auto; }
|
||||
.tweet-card__image-remove {
|
||||
|
|
@ -1065,12 +1083,17 @@ function renderThreadBuilderPage(space: string, threadData?: ThreadData | null):
|
|||
'<button class="tweet-card__image-remove" data-remove-idx="' + i + '" title="Remove image">×</button>' +
|
||||
'</div>'
|
||||
: '';
|
||||
const imageBar = '<div class="tweet-card__image-bar">' +
|
||||
'<button class="tweet-card__image-btn" data-upload-idx="' + i + '">' + svgUpload + ' Upload</button>' +
|
||||
'<button class="tweet-card__image-btn" data-generate-idx="' + i + '">' + svgSparkle + ' AI</button>' +
|
||||
'</div>';
|
||||
const svgCamera = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"/><circle cx="12" cy="13" r="4"/></svg>';
|
||||
const photoBtn = !imgUrl
|
||||
? '<button class="tweet-card__photo-btn" data-photo-idx="' + i + '" title="Add image">' + svgCamera + '<span class="photo-btn-plus">+</span></button>' +
|
||||
'<div class="tweet-card__photo-menu" hidden data-menu-idx="' + i + '">' +
|
||||
'<button data-upload-idx="' + i + '">' + svgUpload + ' Upload</button>' +
|
||||
'<button data-generate-idx="' + i + '">' + svgSparkle + ' Generate with AI</button>' +
|
||||
'</div>'
|
||||
: '';
|
||||
return '<div class="tweet-card">' +
|
||||
connector +
|
||||
photoBtn +
|
||||
'<div class="tweet-card__header">' +
|
||||
'<div class="tweet-card__avatar">' + esc(initial) + '</div>' +
|
||||
'<span class="tweet-card__name">' + esc(name) + '</span>' +
|
||||
|
|
@ -1080,7 +1103,6 @@ function renderThreadBuilderPage(space: string, threadData?: ThreadData | null):
|
|||
'</div>' +
|
||||
'<p class="tweet-card__content">' + esc(text) + '</p>' +
|
||||
imgHtml +
|
||||
imageBar +
|
||||
'<div class="tweet-card__footer">' +
|
||||
'<div class="tweet-card__actions">' +
|
||||
'<span class="tweet-card__action">' + svgReply + '</span>' +
|
||||
|
|
@ -1382,16 +1404,40 @@ function renderThreadBuilderPage(space: string, threadData?: ThreadData | null):
|
|||
} catch (e) { console.error('Tweet image removal failed:', e); }
|
||||
}
|
||||
|
||||
// Close any open photo menus
|
||||
function closeAllPhotoMenus() {
|
||||
preview.querySelectorAll('.tweet-card__photo-menu').forEach(m => m.hidden = true);
|
||||
}
|
||||
|
||||
// Event delegation on preview container
|
||||
preview.addEventListener('click', (e) => {
|
||||
// Photo button → toggle dropdown
|
||||
const photoBtn = e.target.closest('[data-photo-idx]');
|
||||
if (photoBtn) {
|
||||
const idx = photoBtn.dataset.photoIdx;
|
||||
const menu = preview.querySelector('[data-menu-idx="' + idx + '"]');
|
||||
if (menu) {
|
||||
const wasHidden = menu.hidden;
|
||||
closeAllPhotoMenus();
|
||||
menu.hidden = !wasHidden;
|
||||
}
|
||||
return;
|
||||
}
|
||||
const uploadBtn = e.target.closest('[data-upload-idx]');
|
||||
if (uploadBtn) { tweetImageUploadIdx = uploadBtn.dataset.uploadIdx; tweetImageInput.click(); return; }
|
||||
if (uploadBtn) { closeAllPhotoMenus(); tweetImageUploadIdx = uploadBtn.dataset.uploadIdx; tweetImageInput.click(); return; }
|
||||
const genBtn = e.target.closest('[data-generate-idx]');
|
||||
if (genBtn) { generateTweetImage(genBtn.dataset.generateIdx); return; }
|
||||
if (genBtn) { closeAllPhotoMenus(); generateTweetImage(genBtn.dataset.generateIdx); return; }
|
||||
const removeBtn = e.target.closest('[data-remove-idx]');
|
||||
if (removeBtn) { removeTweetImage(removeBtn.dataset.removeIdx); return; }
|
||||
});
|
||||
|
||||
// Close photo menus on outside click
|
||||
document.addEventListener('click', (e) => {
|
||||
if (!e.target.closest('.tweet-card__photo-btn') && !e.target.closest('.tweet-card__photo-menu')) {
|
||||
closeAllPhotoMenus();
|
||||
}
|
||||
});
|
||||
|
||||
tweetImageInput.addEventListener('change', () => {
|
||||
const file = tweetImageInput.files?.[0];
|
||||
if (file && tweetImageUploadIdx !== null) uploadTweetImage(tweetImageUploadIdx, file);
|
||||
|
|
|
|||
Loading…
Reference in New Issue