fix: improve rcal/rtube/rpubs demo rendering and mobile CSS
rCal: add mobile responsive breakpoints, event times with colored borders, and Today button. rTube: auto-select first video, hide Live Stream tab in demo, show duration/date metadata. rPubs: fix mobile layout height calc and toolbar stacking. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
f9a36b9d3e
commit
f1e90924c0
|
|
@ -186,7 +186,9 @@ class FolkCalendarView extends HTMLElement {
|
|||
.moon { font-size: 10px; opacity: 0.7; }
|
||||
.event-dot { width: 6px; height: 6px; border-radius: 50%; display: inline-block; margin: 1px; }
|
||||
.event-dots { display: flex; flex-wrap: wrap; gap: 1px; }
|
||||
.event-label { font-size: 9px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: #aaa; line-height: 1.4; }
|
||||
.event-label { font-size: 9px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: #aaa; line-height: 1.4; padding: 1px 3px; border-radius: 3px; cursor: pointer; }
|
||||
.event-label:hover { background: rgba(255,255,255,0.08); }
|
||||
.event-time { color: #666; font-size: 8px; margin-right: 2px; }
|
||||
|
||||
.event-modal {
|
||||
position: fixed; top: 0; left: 0; right: 0; bottom: 0;
|
||||
|
|
@ -199,12 +201,30 @@ class FolkCalendarView extends HTMLElement {
|
|||
|
||||
.sources { display: flex; gap: 6px; margin-bottom: 12px; flex-wrap: wrap; }
|
||||
.source-badge { font-size: 10px; padding: 3px 8px; border-radius: 10px; border: 1px solid #333; }
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.day { min-height: 56px; padding: 4px; }
|
||||
.day-num { font-size: 11px; }
|
||||
.event-label { display: none; }
|
||||
.event-dot { width: 5px; height: 5px; }
|
||||
.moon { font-size: 8px; }
|
||||
.rapp-nav__title { font-size: 13px; }
|
||||
}
|
||||
@media (max-width: 480px) {
|
||||
.day { min-height: 44px; padding: 3px; }
|
||||
.day-num { font-size: 10px; }
|
||||
.weekday { font-size: 9px; padding: 2px; }
|
||||
.rapp-nav { gap: 4px; }
|
||||
.toggle-btn { padding: 3px 6px; font-size: 10px; }
|
||||
.source-badge { font-size: 8px; padding: 2px 6px; }
|
||||
}
|
||||
</style>
|
||||
|
||||
${this.error ? `<div style="color:#ef5350;text-align:center;padding:8px">${this.esc(this.error)}</div>` : ""}
|
||||
|
||||
<div class="rapp-nav">
|
||||
<button class="rapp-nav__back" id="prev">←</button>
|
||||
<button class="toggle-btn" id="today">Today</button>
|
||||
<span class="rapp-nav__title">${monthName} ${year}</span>
|
||||
<button class="toggle-btn ${this.showLunar ? "active" : ""}" id="toggle-lunar">🌙 Lunar</button>
|
||||
<button class="rapp-nav__back" id="next">→</button>
|
||||
|
|
@ -255,7 +275,11 @@ class FolkCalendarView extends HTMLElement {
|
|||
${dayEvents.slice(0, 3).map(e => `<span class="event-dot" style="background:${e.source_color || "#6366f1"}"></span>`).join("")}
|
||||
${dayEvents.length > 3 ? `<span style="font-size:9px;color:#888">+${dayEvents.length - 3}</span>` : ""}
|
||||
</div>
|
||||
${dayEvents.slice(0, 2).map(e => `<div class="event-label" data-event='${JSON.stringify({ id: e.id })}'>${this.esc(e.title)}</div>`).join("")}
|
||||
${dayEvents.slice(0, 2).map(e => {
|
||||
const t = new Date(e.start_time);
|
||||
const timeStr = `${t.getHours()}:${String(t.getMinutes()).padStart(2, "0")}`;
|
||||
return `<div class="event-label" style="border-left:2px solid ${e.source_color || "#6366f1"}" data-event='${JSON.stringify({ id: e.id })}'><span class="event-time">${timeStr}</span>${this.esc(e.title)}</div>`;
|
||||
}).join("")}
|
||||
` : ""}
|
||||
</div>`;
|
||||
}
|
||||
|
|
@ -290,6 +314,10 @@ class FolkCalendarView extends HTMLElement {
|
|||
private attachListeners() {
|
||||
this.shadow.getElementById("prev")?.addEventListener("click", () => this.navigate(-1));
|
||||
this.shadow.getElementById("next")?.addEventListener("click", () => this.navigate(1));
|
||||
this.shadow.getElementById("today")?.addEventListener("click", () => {
|
||||
this.currentDate = new Date();
|
||||
if (this.space === "demo") { this.loadDemoData(); } else { this.loadMonth(); }
|
||||
});
|
||||
this.shadow.getElementById("toggle-lunar")?.addEventListener("click", () => {
|
||||
this.showLunar = !this.showLunar;
|
||||
this.render();
|
||||
|
|
|
|||
|
|
@ -481,14 +481,27 @@ export class FolkPubsEditor extends HTMLElement {
|
|||
.format-detail .pages { color: #60a5fa; }
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.editor-layout { flex-direction: column; }
|
||||
:host { height: auto; min-height: calc(100vh - 92px); }
|
||||
.editor-layout { flex-direction: column; height: auto; }
|
||||
.editor-main { min-height: 0; }
|
||||
.sidebar {
|
||||
width: 100%;
|
||||
border-left: none;
|
||||
border-top: 1px solid #1e293b;
|
||||
max-height: 50vh;
|
||||
max-height: none;
|
||||
padding: 0.75rem;
|
||||
}
|
||||
.content-area { min-height: 40vh; }
|
||||
.content-area { min-height: 45vh; }
|
||||
.toolbar-left { flex-direction: column; gap: 0.375rem; }
|
||||
.title-input, .author-input { max-width: 100%; flex: 1; }
|
||||
.editor-toolbar { gap: 0.5rem; }
|
||||
.format-grid { grid-template-columns: repeat(3, 1fr); }
|
||||
.btn-generate { font-size: 0.8rem; padding: 0.5rem; }
|
||||
}
|
||||
@media (max-width: 480px) {
|
||||
.format-grid { grid-template-columns: 1fr 1fr; }
|
||||
.toolbar-right { width: 100%; }
|
||||
.btn-sample, .btn-upload { flex: 1; text-align: center; }
|
||||
}
|
||||
</style>`;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
class FolkVideoPlayer extends HTMLElement {
|
||||
private shadow: ShadowRoot;
|
||||
private space = "demo";
|
||||
private videos: Array<{ name: string; size: number }> = [];
|
||||
private videos: Array<{ name: string; size: number; duration?: string; date?: string }> = [];
|
||||
private currentVideo: string | null = null;
|
||||
private mode: "library" | "live" = "library";
|
||||
private streamKey = "";
|
||||
|
|
@ -29,13 +29,14 @@ class FolkVideoPlayer extends HTMLElement {
|
|||
private loadDemoData() {
|
||||
this.isDemo = true;
|
||||
this.videos = [
|
||||
{ name: "community-meeting-2026-02-15.mp4", size: 524288000 },
|
||||
{ name: "rspace-demo-walkthrough.mp4", size: 157286400 },
|
||||
{ name: "design-sprint-day1.webm", size: 892108800 },
|
||||
{ name: "interview-cosmolocal-founders.mp4", size: 1073741824 },
|
||||
{ name: "workshop-local-first-data.mp4", size: 734003200 },
|
||||
{ name: "lightning-talks-feb2026.webm", size: 445644800 },
|
||||
{ name: "community-meeting-2026-02-15.mp4", size: 524288000, duration: "1:23:45", date: "Feb 15, 2026" },
|
||||
{ name: "rspace-demo-walkthrough.mp4", size: 157286400, duration: "18:32", date: "Feb 12, 2026" },
|
||||
{ name: "design-sprint-day1.webm", size: 892108800, duration: "2:45:10", date: "Feb 10, 2026" },
|
||||
{ name: "interview-cosmolocal-founders.mp4", size: 1073741824, duration: "52:18", date: "Feb 7, 2026" },
|
||||
{ name: "workshop-local-first-data.mp4", size: 734003200, duration: "1:35:42", date: "Feb 3, 2026" },
|
||||
{ name: "lightning-talks-feb2026.webm", size: 445644800, duration: "42:15", date: "Jan 28, 2026" },
|
||||
];
|
||||
this.currentVideo = this.videos[0].name;
|
||||
this.render();
|
||||
}
|
||||
|
||||
|
|
@ -72,7 +73,7 @@ class FolkVideoPlayer extends HTMLElement {
|
|||
:host { display: block; min-height: 60vh; font-family: system-ui, sans-serif; color: #e2e8f0; }
|
||||
.container { max-width: 1200px; margin: 0 auto; }
|
||||
.rapp-nav { display: flex; align-items: center; gap: 8px; margin-bottom: 16px; min-height: 36px; }
|
||||
.rapp-nav__title { font-size: 15px; font-weight: 600; color: #e2e8f0; flex: 1; }
|
||||
.rapp-nav__title { font-size: 13px; font-weight: 500; color: #64748b; flex: 1; text-align: right; }
|
||||
.tab { padding: 0.5rem 1.25rem; border-radius: 8px; border: 1px solid #334155; background: transparent; color: #94a3b8; cursor: pointer; font-size: 0.875rem; }
|
||||
.tab.active { background: #ef4444; color: white; border-color: #ef4444; }
|
||||
.layout { display: grid; grid-template-columns: 300px 1fr; gap: 1.5rem; }
|
||||
|
|
@ -109,7 +110,8 @@ class FolkVideoPlayer extends HTMLElement {
|
|||
<div class="container">
|
||||
<div class="rapp-nav">
|
||||
<button class="tab ${this.mode === "library" ? "active" : ""}" data-mode="library">Video Library</button>
|
||||
<button class="tab ${this.mode === "live" ? "active" : ""}" data-mode="live">Live Stream</button>
|
||||
${!this.isDemo ? `<button class="tab ${this.mode === "live" ? "active" : ""}" data-mode="live">Live Stream</button>` : ""}
|
||||
<span class="rapp-nav__title">${this.isDemo ? `${this.videos.length} recordings` : ""}</span>
|
||||
</div>
|
||||
${this.mode === "library" ? this.renderLibrary() : this.renderLive()}
|
||||
</div>
|
||||
|
|
@ -129,7 +131,7 @@ class FolkVideoPlayer extends HTMLElement {
|
|||
: filteredVideos.map((v) => `
|
||||
<div class="video-item ${this.currentVideo === v.name ? "active" : ""}" data-name="${v.name}">
|
||||
<div class="video-name">${v.name}</div>
|
||||
<div class="video-meta">${this.getExtension(v.name).toUpperCase()} · ${this.formatSize(v.size)}</div>
|
||||
<div class="video-meta">${this.getExtension(v.name).toUpperCase()} · ${this.formatSize(v.size)}${v.duration ? ` · ${v.duration}` : ""}${v.date ? `<br>${v.date}` : ""}</div>
|
||||
</div>
|
||||
`).join("");
|
||||
|
||||
|
|
@ -141,7 +143,16 @@ class FolkVideoPlayer extends HTMLElement {
|
|||
} else if (this.isDemo) {
|
||||
const selectedVideo = this.videos.find(v => v.name === this.currentVideo);
|
||||
const sizeStr = selectedVideo ? this.formatSize(selectedVideo.size) : "";
|
||||
player = `<div class="placeholder"><div class="placeholder-icon">🎦</div><p style="font-size:1rem;font-weight:500;margin-bottom:0.5rem">${this.currentVideo}</p><p style="font-size:0.85rem;color:#64748b">${this.getExtension(this.currentVideo).toUpperCase()} · ${sizeStr}</p><p style="font-size:0.75rem;color:#475569;margin-top:1rem">Demo mode — no actual video file</p></div>`;
|
||||
const durStr = selectedVideo?.duration || "";
|
||||
player = `<div class="placeholder" style="padding:2rem">
|
||||
<div style="width:80px;height:80px;border-radius:50%;background:rgba(239,68,68,0.15);display:flex;align-items:center;justify-content:center;margin:0 auto 1.5rem">
|
||||
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="#ef4444" stroke-width="2"><polygon points="5 3 19 12 5 21 5 3"/></svg>
|
||||
</div>
|
||||
<p style="font-size:1.1rem;font-weight:600;margin-bottom:0.5rem;color:#e2e8f0">${this.currentVideo}</p>
|
||||
<p style="font-size:0.85rem;color:#94a3b8">${this.getExtension(this.currentVideo).toUpperCase()} · ${sizeStr}${durStr ? ` · ${durStr}` : ""}</p>
|
||||
${selectedVideo?.date ? `<p style="font-size:0.8rem;color:#64748b;margin-top:0.5rem">Recorded ${selectedVideo.date}</p>` : ""}
|
||||
<p style="font-size:0.7rem;color:#475569;margin-top:1.5rem;opacity:0.7">Demo preview — connect rTube to stream real video</p>
|
||||
</div>`;
|
||||
} else if (!this.isPlayable(this.currentVideo)) {
|
||||
player = `<div class="placeholder"><div class="placeholder-icon">⚠️</div><p><strong>${this.getExtension(this.currentVideo).toUpperCase()}</strong> files cannot play in browsers</p><p style="font-size:0.8rem;color:#475569;margin-top:0.5rem">Download to play locally</p></div>`;
|
||||
} else {
|
||||
|
|
|
|||
Loading…
Reference in New Issue