A fullstack app for indexing standard.site documents

chore: updated styles

+144 -113
+144 -113
packages/client/src/App.tsx
··· 75 75 76 76 const formatDate = (dateString?: string) => { 77 77 if (!dateString) return "Unknown date"; 78 - return new Date(dateString).toLocaleDateString("en-US", { 78 + const date = new Date(dateString); 79 + const now = new Date(); 80 + const diff = now.getTime() - date.getTime(); 81 + const minutes = Math.floor(diff / 60000); 82 + const hours = Math.floor(diff / 3600000); 83 + const days = Math.floor(diff / 86400000); 84 + 85 + if (minutes < 1) return "just now"; 86 + if (minutes < 60) return `${minutes} minute${minutes > 1 ? "s" : ""} ago`; 87 + if (hours < 24) return `${hours} hour${hours > 1 ? "s" : ""} ago`; 88 + if (days < 7) return `${days} day${days > 1 ? "s" : ""} ago`; 89 + 90 + return date.toLocaleDateString("en-US", { 79 91 year: "numeric", 80 92 month: "long", 81 93 day: "numeric", ··· 92 104 return doc.description || doc.textContent || ""; 93 105 }; 94 106 95 - 96 107 return ( 97 108 <div className="window" style={{ width: "100%", maxWidth: "800px" }}> 98 109 <div className="title-bar"> 99 - <div className="title-bar-text">docs.surf - Internet Explorer 6</div> 110 + <div className="title-bar-text">Internet Explorer 6</div> 100 111 <div className="title-bar-controls"> 101 112 <button aria-label="Minimize" /> 102 113 <button aria-label="Maximize" /> ··· 137 148 )} 138 149 139 150 {!loading && !error && ( 140 - <div className="feed" style={{ maxHeight: "70vh", overflowY: "auto", paddingRight: "5px" }}> 141 - {documents.map((doc) => ( 142 - <fieldset key={doc.uri} style={{ marginBottom: "16px" }}> 143 - <legend style={{ fontWeight: "bold" }}> 144 - {doc.viewUrl ? ( 145 - <a 146 - href={doc.viewUrl} 147 - target="_blank" 148 - rel="noopener noreferrer" 149 - style={{ color: "inherit", textDecoration: "none" }} 150 - > 151 - {doc.title} 152 - </a> 151 + <div 152 + className="feed" 153 + style={{ 154 + maxHeight: "70vh", 155 + overflowY: "auto", 156 + paddingRight: "5px", 157 + }} 158 + > 159 + {documents.map((doc, index) => ( 160 + <div 161 + key={doc.uri} 162 + style={{ 163 + display: "flex", 164 + gap: "12px", 165 + padding: "16px", 166 + borderBottom: 167 + index < documents.length - 1 ? "1px solid #e0e0e0" : "none", 168 + backgroundColor: "#ffffff", 169 + position: "relative", 170 + }} 171 + > 172 + {/* Thumbnail on the left */} 173 + <div style={{ flexShrink: 0 }}> 174 + {doc.coverImageUrl || doc.publication?.iconUrl ? ( 175 + <img 176 + src={doc.coverImageUrl || doc.publication?.iconUrl} 177 + alt={doc.title} 178 + style={{ 179 + width: "88px", 180 + height: "88px", 181 + objectFit: "scale-down", 182 + border: "1px solid #d0d0d0", 183 + }} 184 + /> 153 185 ) : ( 154 - doc.title 155 - )} 156 - </legend> 157 - <div style={{ padding: "8px" }}> 158 - {/* Publication info */} 159 - {doc.publication && ( 160 186 <div 161 187 style={{ 188 + width: "88px", 189 + height: "88px", 190 + backgroundColor: "#f0f0f0", 191 + border: "1px solid #d0d0d0", 162 192 display: "flex", 163 193 alignItems: "center", 164 - gap: "8px", 165 - marginBottom: "8px", 166 - fontSize: "0.85em", 194 + justifyContent: "center", 195 + fontSize: "10px", 196 + color: "#999", 167 197 }} 168 198 > 169 - {doc.publication.iconUrl && ( 170 - <img 171 - src={doc.publication.iconUrl} 172 - alt={doc.publication.name} 173 - style={{ 174 - width: "16px", 175 - height: "16px", 176 - objectFit: "cover", 177 - }} 178 - /> 179 - )} 180 - <span style={{ fontWeight: "bold" }}> 181 - {doc.publication.name} 182 - </span> 183 - </div> 184 - )} 185 - 186 - {/* Cover image */} 187 - {doc.coverImageUrl && ( 188 - <div style={{ marginBottom: "8px" }}> 189 - <img 190 - src={doc.coverImageUrl} 191 - alt={doc.title} 192 - style={{ 193 - maxWidth: "100%", 194 - maxHeight: "200px", 195 - objectFit: "cover", 196 - border: "1px solid #888", 197 - }} 198 - /> 199 + No Image 199 200 </div> 200 201 )} 202 + </div> 201 203 202 - {/* Date */} 203 - <div 204 + {/* Content on the right */} 205 + <div style={{ flex: 1, minWidth: 0 }}> 206 + {/* Title */} 207 + <h3 204 208 style={{ 205 - marginBottom: "8px", 206 - fontSize: "0.85em", 207 - color: "#666", 209 + margin: "0 0 8px 0", 210 + fontSize: "15px", 211 + fontWeight: "normal", 212 + color: "#333", 213 + lineHeight: "1.3", 208 214 }} 209 215 > 210 - Published: {formatDate(doc.publishedAt)} 211 - {doc.updatedAt && doc.updatedAt !== doc.publishedAt && ( 212 - <> | Updated: {formatDate(doc.updatedAt)}</> 216 + {doc.viewUrl ? ( 217 + <a 218 + href={doc.viewUrl} 219 + target="_blank" 220 + rel="noopener noreferrer" 221 + style={{ 222 + color: "#333", 223 + textDecoration: "none", 224 + }} 225 + > 226 + {doc.title} 227 + </a> 228 + ) : ( 229 + doc.title 213 230 )} 214 - </div> 231 + </h3> 215 232 216 233 {/* Description */} 217 234 {getDescription(doc) && ( 218 - <p style={{ marginBottom: "12px" }}> 219 - {truncateText(getDescription(doc))} 235 + <p 236 + style={{ 237 + margin: "0 0 8px 0", 238 + fontSize: "12px", 239 + color: "#666", 240 + lineHeight: "1.4", 241 + }} 242 + > 243 + {truncateText(getDescription(doc), 150)} 220 244 </p> 221 245 )} 222 246 223 - {/* Tags */} 224 - {doc.tags && doc.tags.length > 0 && ( 225 - <div 247 + {/* Publication name and timestamp */} 248 + <div 249 + style={{ 250 + display: "flex", 251 + alignItems: "center", 252 + justifyContent: "space-between", 253 + fontSize: "12px", 254 + }} 255 + > 256 + <a 257 + href={doc.publication?.url} 258 + target="_blank" 259 + rel="noreferrer" 226 260 style={{ 227 - display: "flex", 228 - flexWrap: "wrap", 229 - gap: "4px", 230 - marginBottom: "12px", 261 + color: "#7aaa3c", 262 + fontWeight: "bold", 231 263 }} 232 264 > 233 - {doc.tags.map((tag) => ( 234 - <span 235 - key={tag} 236 - style={{ 237 - background: "#c0c0c0", 238 - padding: "2px 6px", 239 - fontSize: "0.75em", 240 - border: "1px solid #808080", 241 - }} 242 - > 243 - {tag} 244 - </span> 245 - ))} 246 - </div> 247 - )} 248 - 249 - {/* Actions */} 250 - <div style={{ display: "flex", gap: "8px", justifyContent: "flex-end" }}> 251 - {doc.bskyPostRef && ( 252 - <button 253 - onClick={() => 254 - window.open( 255 - `https://bsky.app/profile/${doc.did}/post/${doc.bskyPostRef!.uri.split("/").pop()}`, 256 - "_blank" 257 - ) 258 - } 259 - > 260 - View on Bluesky 261 - </button> 262 - )} 263 - {doc.viewUrl && ( 264 - <button 265 - onClick={() => 266 - window.open(doc.viewUrl || "", "_blank") 267 - } 268 - > 269 - Read More 270 - </button> 271 - )} 265 + {doc.publication?.name || "Unknown"} 266 + </a> 267 + <a 268 + href={`https://pdsls.dev/${doc.uri}`} 269 + target="_blank" 270 + rel="noreferrer" 271 + style={{ 272 + color: "#999", 273 + }} 274 + > 275 + {formatDate(doc.publishedAt)} 276 + </a> 272 277 </div> 273 278 </div> 274 - </fieldset> 279 + 280 + {/* RSS icon on the far right */} 281 + {/*<div style={{ flexShrink: 0 }}> 282 + <svg 283 + width="24" 284 + height="24" 285 + viewBox="0 0 24 24" 286 + fill="none" 287 + xmlns="http://www.w3.org/2000/svg" 288 + style={{ opacity: 0.6 }} 289 + > 290 + <circle cx="6" cy="18" r="2" fill="#ff6600" /> 291 + <path 292 + d="M4 4c9.941 0 18 8.059 18 18" 293 + stroke="#ff6600" 294 + strokeWidth="2" 295 + fill="none" 296 + /> 297 + <path 298 + d="M4 11c6.075 0 11 4.925 11 11" 299 + stroke="#ff6600" 300 + strokeWidth="2" 301 + fill="none" 302 + /> 303 + </svg> 304 + </div>*/} 305 + </div> 275 306 ))} 276 307 {documents.length === 0 && <p>No documents found.</p>} 277 308 </div>