// Left rail — the day's items, filter chips, hover statusbar for one-click // status changes. Status glyphs: ★ featured, ● used, ◌ context, ⊘ hidden. function Rail({ items, allCount, inPost, filter, setFilter, selectedItem, setSelectedItem, setItemStatus, deleteItem, moveItemToYesterday }) { const filters = [ { k: "all", l: "All" }, { k: "in-post", l: "In post" }, { k: "context", l: "Context" }, { k: "hidden", l: "Hidden" }, ]; return ( ); } function ItemRow({ item, selected, onSelect, setStatus, onDelete, onMoveYesterday }) { const meta = (() => { const t = item.captured_at ? new Date(item.captured_at * 1000) : null; if (!t) return "--:--"; return t.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", hour12: false }); })(); const summary = item.caption || item.description || item.transcript || item.text_body || item.filename || ""; const onDragStart = (e) => { // Note: we only know MIME-y types when inspecting on drop; set both so // either key works across browsers. e.dataTransfer.setData("application/x-postcard-item", item.id); e.dataTransfer.setData("text/x-postcard-item", item.id); e.dataTransfer.setData("text/plain", `item:${item.id}`); e.dataTransfer.effectAllowed = "copy"; }; return (
{KIND_LABEL[item.kind] || "OBJ"} {item.kind === "photo" || item.kind === "video" ? ( { e.target.style.display = "none"; }} /> ) : ( {item.kind === "voice" && "▶"} {item.kind === "audio" && "♪"} {item.kind === "text" && "¶"} )} {STATUS_GLYPH[item.status]}
{meta} {item.kind} {item.filename && {item.filename}}
{summary}
e.stopPropagation()}> {STATUS_ORDER.map((s) => ( ))}
{selected && }
); } function ItemPreview({ item, onDelete, onMoveYesterday }) { return (
e.stopPropagation()}> {item.kind === "photo" && ( {item.caption )} {item.kind === "video" && (
); } Object.assign(window, { Rail });