fix: thread image 404 on subdomains + move photo section to top

Pass /data/ paths through subdomain routing without rewriting so
generated image files are accessible on *.rspace.online subdomains.

Move image upload/generate section above the compose textarea in the
thread builder editor layout.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jeff Emmett 2026-03-05 18:00:58 -08:00
parent 32cab9c67e
commit 92dff99793
2 changed files with 16 additions and 12 deletions

View File

@ -324,20 +324,22 @@ export class FolkThreadBuilder extends HTMLElement {
</div>
<div id="share-link-area"></div>
<div class="compose">
<textarea class="compose-textarea" id="thread-input" placeholder="Write your tweets here, separated by ---\n\nExample:\nFirst tweet goes here\n---\nSecond tweet\n---\nThird tweet">${t ? this.esc(t.tweets.join('\n---\n')) : ''}</textarea>
<div class="image-section">
<input type="file" id="upload-image-input" accept="image/png,image/jpeg,image/webp,image/gif" hidden>
<div class="image-preview" id="thread-image-preview" ${t?.imageUrl ? '' : 'hidden'}>
<img id="thread-image-thumb" alt="Preview" ${t?.imageUrl ? `src="${this.esc(t.imageUrl)}"` : ''}>
</div>
<div class="image-buttons">
<button class="btn btn--outline" id="upload-image-btn">${t?.imageUrl ? 'Replace Image' : 'Upload Image'}</button>
<button class="btn btn--outline" id="gen-image-btn">${t?.imageUrl ? 'Replace with AI' : 'Generate with AI'}</button>
</div>
</div>
<input class="compose-title" id="thread-title" placeholder="Thread title (defaults to first tweet)" value="${this.esc(t?.title || '')}">
<div class="compose-fields">
<input class="compose-input" id="thread-name" placeholder="Display name" value="${this.esc(t?.name || 'Your Name')}">
<input class="compose-input" id="thread-handle" placeholder="@handle" value="${this.esc(t?.handle || '@yourhandle')}">
</div>
<input class="compose-title" id="thread-title" placeholder="Thread title (defaults to first tweet)" value="${this.esc(t?.title || '')}">
<div class="image-section">
<input type="file" id="upload-image-input" accept="image/png,image/jpeg,image/webp,image/gif" hidden>
<button class="btn btn--outline" id="upload-image-btn">${t?.imageUrl ? 'Replace Image' : 'Upload Image'}</button>
<button class="btn btn--outline" id="gen-image-btn">${t?.imageUrl ? 'Replace with AI' : 'Generate with AI'}</button>
<div class="image-preview" id="thread-image-preview" ${t?.imageUrl ? '' : 'hidden'}>
<img id="thread-image-thumb" alt="Preview" ${t?.imageUrl ? `src="${this.esc(t.imageUrl)}"` : ''}>
</div>
</div>
<textarea class="compose-textarea" id="thread-input" placeholder="Write your tweets here, separated by ---\n\nExample:\nFirst tweet goes here\n---\nSecond tweet\n---\nThird tweet">${t ? this.esc(t.tweets.join('\n---\n')) : ''}</textarea>
</div>
<div class="preview" id="thread-preview">
<div class="preview-empty">Your tweet thread preview will appear here</div>
@ -1133,10 +1135,11 @@ export class FolkThreadBuilder extends HTMLElement {
}
.draft-item__delete:hover { color: #ef4444; }
.image-section { display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; }
.image-section { display: flex; flex-direction: column; gap: 0.75rem; margin-bottom: 0.75rem; }
.image-buttons { display: flex; gap: 0.5rem; }
.image-preview { border-radius: 8px; overflow: hidden; border: 1px solid var(--rs-input-border, #334155); }
.image-preview[hidden] { display: none; }
.image-preview img { display: block; max-width: 200px; height: auto; }
.image-preview img { display: block; width: 100%; max-height: 200px; object-fit: cover; }
#share-link-area { grid-column: 1 / -1; }
.share-link {

View File

@ -1932,6 +1932,7 @@ const server = Bun.serve<WSData>({
// Global routes pass through without subdomain prefix
if (
url.pathname.startsWith("/api/") ||
url.pathname.startsWith("/data/") ||
url.pathname.startsWith("/.well-known/") ||
url.pathname === "/about" ||
url.pathname === "/admin" ||