mirror of
https://github.com/dergigi/boris.git
synced 2026-02-17 04:54:56 +01:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5a67be8096 | ||
|
|
9a929a6be4 | ||
|
|
e0ca010026 | ||
|
|
8bd5d7aadf | ||
|
|
9115c38cde | ||
|
|
0c7c1d54d9 | ||
|
|
d529d83eb8 | ||
|
|
a3127c7836 | ||
|
|
4d5fe1f425 | ||
|
|
c7a4de9786 |
29
CHANGELOG.md
29
CHANGELOG.md
@@ -7,6 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.4.1] - 2025-10-10
|
||||
|
||||
### Fixed
|
||||
- Long article summaries overlapping with hero image content on mobile devices
|
||||
- Article summary now moves below hero image on mobile when longer than 150 characters
|
||||
- Article summary line clamp reduced from 3 to 2 lines on mobile for better space utilization
|
||||
|
||||
### Changed
|
||||
- Hero image rendering on mobile now uses zoom-to-fit approach with viewport-based sizing
|
||||
- Hero image height on mobile set to 50vh (constrained between 280px-400px)
|
||||
- Improved image cropping with center positioning for better visual presentation
|
||||
- Optimized reader header overlay padding and title sizing on mobile
|
||||
|
||||
## [0.4.0] - 2025-10-10
|
||||
|
||||
### Added
|
||||
- Mobile-responsive design with overlay sidebar drawer
|
||||
- Media query hooks for responsive behavior (`useIsMobile`, `useIsTablet`, `useIsCoarsePointer`)
|
||||
@@ -19,12 +34,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Mobile-optimized modals (full-screen sheet style)
|
||||
- Mobile-optimized toast notifications (bottom position)
|
||||
- Dynamic viewport height support (100dvh)
|
||||
- Mobile highlights panel as overlay with toggle button
|
||||
|
||||
### Changed
|
||||
- Sidebar now displays as overlay drawer on mobile (≤768px)
|
||||
- Highlights panel hidden on mobile for better content focus
|
||||
- Sidebar auto-closes when selecting content on mobile
|
||||
- Hover effects disabled on touch devices
|
||||
- Replace hamburger icon with bookmark icon on mobile
|
||||
|
||||
### Fixed
|
||||
- Ensure bookmarks container fills mobile sidepane properly
|
||||
- Restore desktop grid layout for highlights panel
|
||||
- Improve empty state and loading visibility in mobile sidepanes
|
||||
- Add flex properties to mobile bookmark containers for proper filling
|
||||
- Force bookmarks pane expanded on mobile and ensure highlights pane sits above content on desktop
|
||||
- Reduce mobile backdrop opacity and ensure sidepanes appear above it
|
||||
- Replace any type with proper bookmark interface for linter compliance
|
||||
|
||||
## [0.3.8] - 2025-10-10
|
||||
|
||||
@@ -564,6 +590,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Optimize relay usage following applesauce-relay best practices
|
||||
- Use applesauce-react event models for better profile handling
|
||||
|
||||
[0.4.0]: https://github.com/dergigi/boris/compare/v0.3.8...v0.4.0
|
||||
[0.3.8]: https://github.com/dergigi/boris/compare/v0.3.7...v0.3.8
|
||||
[0.3.7]: https://github.com/dergigi/boris/compare/v0.3.6...v0.3.7
|
||||
[0.3.6]: https://github.com/dergigi/boris/compare/v0.3.5...v0.3.6
|
||||
[0.3.5]: https://github.com/dergigi/boris/compare/v0.3.4...v0.3.5
|
||||
[0.3.4]: https://github.com/dergigi/boris/compare/v0.3.3...v0.3.4
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "boris",
|
||||
"version": "0.3.8",
|
||||
"version": "0.4.1",
|
||||
"description": "A minimal nostr client for bookmark management",
|
||||
"homepage": "https://read.withboris.com/",
|
||||
"type": "module",
|
||||
|
||||
@@ -119,6 +119,7 @@ export const BookmarkList: React.FC<BookmarkListProps> = ({
|
||||
<div className="empty-state">
|
||||
<p>No bookmarks found.</p>
|
||||
<p>Add bookmarks using your nostr client to see them here.</p>
|
||||
<p>If you aren't on nostr yet, start here: <a href="https://nstart.me/" target="_blank" rel="noopener noreferrer">nstart.me</a></p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="bookmarks-list">
|
||||
|
||||
@@ -74,7 +74,7 @@ const ContentPanel: React.FC<ContentPanelProps> = ({
|
||||
followedPubkeys
|
||||
})
|
||||
|
||||
const { contentRef, handleMouseUp } = useHighlightInteractions({
|
||||
const { contentRef, handleSelectionEnd } = useHighlightInteractions({
|
||||
onHighlightClick,
|
||||
selectedHighlightId,
|
||||
onTextSelection,
|
||||
@@ -138,7 +138,8 @@ const ContentPanel: React.FC<ContentPanelProps> = ({
|
||||
ref={contentRef}
|
||||
className="reader-markdown"
|
||||
dangerouslySetInnerHTML={{ __html: finalHtml }}
|
||||
onMouseUp={handleMouseUp}
|
||||
onMouseUp={handleSelectionEnd}
|
||||
onTouchEnd={handleSelectionEnd}
|
||||
/>
|
||||
) : (
|
||||
<div className="reader-markdown">
|
||||
@@ -152,7 +153,8 @@ const ContentPanel: React.FC<ContentPanelProps> = ({
|
||||
ref={contentRef}
|
||||
className="reader-html"
|
||||
dangerouslySetInnerHTML={{ __html: finalHtml || html || '' }}
|
||||
onMouseUp={handleMouseUp}
|
||||
onMouseUp={handleSelectionEnd}
|
||||
onTouchEnd={handleSelectionEnd}
|
||||
/>
|
||||
)
|
||||
) : (
|
||||
|
||||
@@ -28,36 +28,45 @@ const ReaderHeader: React.FC<ReaderHeaderProps> = ({
|
||||
}) => {
|
||||
const cachedImage = useImageCache(image, settings)
|
||||
const formattedDate = published ? format(new Date(published * 1000), 'MMM d, yyyy') : null
|
||||
const isLongSummary = summary && summary.length > 150
|
||||
|
||||
if (cachedImage) {
|
||||
return (
|
||||
<div className="reader-hero-image">
|
||||
<img src={cachedImage} alt={title || 'Article image'} />
|
||||
{formattedDate && (
|
||||
<div className="publish-date-topright">
|
||||
{formattedDate}
|
||||
</div>
|
||||
)}
|
||||
{title && (
|
||||
<div className="reader-header-overlay">
|
||||
<h2 className="reader-title">{title}</h2>
|
||||
{summary && <p className="reader-summary">{summary}</p>}
|
||||
<div className="reader-meta">
|
||||
{readingTimeText && (
|
||||
<div className="reading-time">
|
||||
<FontAwesomeIcon icon={faClock} />
|
||||
<span>{readingTimeText}</span>
|
||||
</div>
|
||||
)}
|
||||
{hasHighlights && (
|
||||
<div className="highlight-indicator">
|
||||
<FontAwesomeIcon icon={faHighlighter} />
|
||||
<span>{highlightCount} highlight{highlightCount !== 1 ? 's' : ''}</span>
|
||||
</div>
|
||||
)}
|
||||
<>
|
||||
<div className="reader-hero-image">
|
||||
<img src={cachedImage} alt={title || 'Article image'} />
|
||||
{formattedDate && (
|
||||
<div className="publish-date-topright">
|
||||
{formattedDate}
|
||||
</div>
|
||||
)}
|
||||
{title && (
|
||||
<div className="reader-header-overlay">
|
||||
<h2 className="reader-title">{title}</h2>
|
||||
{summary && <p className={`reader-summary ${isLongSummary ? 'hide-on-mobile' : ''}`}>{summary}</p>}
|
||||
<div className="reader-meta">
|
||||
{readingTimeText && (
|
||||
<div className="reading-time">
|
||||
<FontAwesomeIcon icon={faClock} />
|
||||
<span>{readingTimeText}</span>
|
||||
</div>
|
||||
)}
|
||||
{hasHighlights && (
|
||||
<div className="highlight-indicator">
|
||||
<FontAwesomeIcon icon={faHighlighter} />
|
||||
<span>{highlightCount} highlight{highlightCount !== 1 ? 's' : ''}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{isLongSummary && (
|
||||
<div className="reader-summary-below-image">
|
||||
<p className="reader-summary">{summary}</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ const SidebarHeader: React.FC<SidebarHeaderProps> = ({ onToggleCollapse, onLogou
|
||||
accountManager.setActive(account)
|
||||
} catch (error) {
|
||||
console.error('Login failed:', error)
|
||||
alert('Login failed. Please install a nostr browser extension and try again.')
|
||||
alert('Login failed. Please install a nostr browser extension and try again.\n\nIf you aren\'t on nostr yet, start here: https://nstart.me/')
|
||||
} finally {
|
||||
setIsConnecting(false)
|
||||
}
|
||||
|
||||
@@ -56,8 +56,8 @@ export const useHighlightInteractions = ({
|
||||
}
|
||||
}, [selectedHighlightId])
|
||||
|
||||
// Handle text selection
|
||||
const handleMouseUp = useCallback(() => {
|
||||
// Handle text selection (works for both mouse and touch)
|
||||
const handleSelectionEnd = useCallback(() => {
|
||||
setTimeout(() => {
|
||||
const selection = window.getSelection()
|
||||
if (!selection || selection.rangeCount === 0) {
|
||||
@@ -76,6 +76,6 @@ export const useHighlightInteractions = ({
|
||||
}, 10)
|
||||
}, [onTextSelection, onClearSelection])
|
||||
|
||||
return { contentRef, handleMouseUp }
|
||||
return { contentRef, handleSelectionEnd }
|
||||
}
|
||||
|
||||
|
||||
@@ -1437,6 +1437,51 @@ body.mobile-sidebar-open {
|
||||
border: 1px solid rgba(100, 108, 255, 0.4);
|
||||
}
|
||||
|
||||
.reader-summary-below-image {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.reader-header-overlay .reader-summary.hide-on-mobile {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.reader-summary-below-image {
|
||||
display: block;
|
||||
padding: 0 0 1.5rem 0;
|
||||
margin-top: -1rem;
|
||||
}
|
||||
|
||||
.reader-summary-below-image .reader-summary {
|
||||
color: #aaa;
|
||||
font-size: 1rem;
|
||||
line-height: 1.6;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.reader-hero-image {
|
||||
min-height: 280px;
|
||||
max-height: 400px;
|
||||
height: 50vh;
|
||||
}
|
||||
|
||||
.reader-hero-image img {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
object-fit: cover;
|
||||
object-position: center;
|
||||
}
|
||||
|
||||
.reader-header-overlay {
|
||||
padding: 1.5rem 1rem 1rem;
|
||||
}
|
||||
|
||||
.reader-header-overlay .reader-title {
|
||||
font-size: 1.5rem;
|
||||
line-height: 1.3;
|
||||
}
|
||||
}
|
||||
|
||||
/* Private Bookmark Styles */
|
||||
.private-bookmark {
|
||||
background: #2a2a2a;
|
||||
@@ -3062,4 +3107,13 @@ body.mobile-sidebar-open {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.blog-post-card-summary {
|
||||
-webkit-line-clamp: 2;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.blog-post-card-content {
|
||||
padding: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user