Compare commits

...

20 Commits

Author SHA1 Message Date
Gigi
88e1bc3419 chore: bump version to 0.10.30 2025-11-01 10:24:32 +01:00
Gigi
4ec34a0379 fix: reset scroll to top when navigating to profile pages 2025-11-01 10:23:27 +01:00
Gigi
aec2dcb75c feat: navigate to author's writings page from article author card 2025-11-01 10:22:15 +01:00
Gigi
5bdc435f5d fix: preserve image aspect ratio when full-width images setting is enabled
- Add object-fit: contain to prevent image squishing
- Make max-height conditional: none when full-width enabled, 70vh otherwise
- Apply fix to both desktop and mobile image styles
2025-11-01 10:15:58 +01:00
Gigi
db46edd39e docs: update CHANGELOG for v0.10.29 2025-11-01 00:35:17 +01:00
Gigi
c9739f804d chore: bump version to 0.10.29 2025-11-01 00:34:50 +01:00
Gigi
eeb44e344f Merge pull request #32 from dergigi/full-width-image-going-over
fix: correct fullWidthImages setting to use width instead of max-width
2025-11-01 00:34:32 +01:00
Gigi
a6674610b8 fix: correct fullWidthImages setting to use width instead of max-width
- Change from --image-max-width CSS variable to --image-width
- When enabled, sets images to width: 100% (enlarging small images)
- Always constrains with max-width: 100% to prevent overflow
- Update mobile responsive styles to respect the setting
2025-11-01 00:32:51 +01:00
Gigi
6ae3decafb docs: update CHANGELOG for v0.10.28 2025-11-01 00:27:08 +01:00
Gigi
00da638e81 chore: bump version to 0.10.28 2025-11-01 00:26:17 +01:00
Gigi
f04c0a401e Merge pull request #31 from dergigi/fix-boris-post-issues
Fix nested markdown links from processing nostr URIs in URLs
2025-11-01 00:26:00 +01:00
Gigi
f5e9f164f5 chore: remove debug console.log statements from nostrUriResolver
Removed all debug logging that was added for troubleshooting the
link processing issue. The functionality remains intact, including
the parser-based markdown link detection and HTTP URL protection.
2025-11-01 00:23:57 +01:00
Gigi
589ac17114 fix: prevent double-processing of markdown to avoid nested links
Added check to detect if markdown has already been processed by looking
for our internal routes (/a/naddr1... or /p/npub1...) in markdown links.
If found, skip re-processing to prevent nested markdown link issues.

This addresses timing issues where markdown might be processed multiple
times, causing nostr URIs that were already converted to links to be
processed again, creating nested/duplicated markdown link structures.
2025-11-01 00:22:15 +01:00
Gigi
8d3510947c fix: add HTTP URL detection to prevent processing nostr URIs in URLs
Enhanced protection to also skip nostr URIs that are part of HTTP/HTTPS
URL patterns, not just markdown link URLs. This addresses timing issues
where the source markdown may contain plain URLs with nostr identifiers
before they're formatted as markdown links.

The detection checks if a nostr URI appears after 'https://' or 'http://'
and is part of a valid URL continuation to avoid false positives.
2025-11-01 00:21:14 +01:00
Gigi
08a8f5623a debug: add comprehensive logging to diagnose timing issue with link processing
Added extensive debug logs to track:
- Input markdown preview and existing link count
- Each markdown link found with context and content
- Warnings when link URLs contain nostr URIs (should be protected)
- Detailed position information for each nostr URI match
- Whether matches are correctly identified as inside/outside link URLs
- Detection of nested markdown links in result (indicates bug)

This will help diagnose the timing issue where processing sometimes
works and sometimes doesn't.
2025-11-01 00:19:43 +01:00
Gigi
e85ccdc7da fix: use parser-based approach to detect markdown link URLs
Replace regex-based markdown link detection with a character-by-character
parser that correctly handles URLs containing brackets and parentheses.
The parser tracks parenthesis depth and escaped characters to correctly
find the end of markdown link URLs, even when they contain special
characters like brackets or nested parentheses.

This should fix the issue where nostr identifiers inside markdown link
URLs were still being processed, causing nested/duplicated markdown links.
2025-11-01 00:17:08 +01:00
Gigi
d0e7f146fb debug: add extensive logging to nostrUriResolver for debugging link processing
Added debug logs prefixed with [nostrUriResolver] to track:
- When markdown processing starts
- All markdown links found and their URL ranges
- All nostr URI matches and their positions
- Whether each nostr URI is skipped or replaced
- Final processing results

This will help diagnose why nostr identifiers are still being
processed inside markdown link URLs.
2025-10-31 23:53:55 +01:00
Gigi
efdb33eb31 fix: remove unused variables in nostrUriResolver
Removed unused linkEnd variable and prefixed unused type parameter
with underscore to satisfy linter and type checker.
2025-10-31 23:52:52 +01:00
Gigi
0abbe62515 fix: prevent nostr URI replacement inside markdown link URLs
Prevents nested markdown link issues when nostr identifiers appear in URLs.
The replaceNostrUrisInMarkdown functions now skip nostr URIs that are
already inside markdown link syntax [text](url) to avoid creating
malformed nested links.
2025-10-31 23:51:40 +01:00
Gigi
ab0972dd29 docs: update CHANGELOG for v0.10.27 2025-10-31 01:59:40 +01:00
7 changed files with 221 additions and 15 deletions

View File

@@ -7,6 +7,93 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [0.10.29] - 2025-11-01
### Fixed
- Full-width images setting now uses width instead of max-width
- Change from --image-max-width CSS variable to --image-width
- When enabled, sets images to width: 100% (enlarging small images)
- Always constrains with max-width: 100% to prevent overflow
- Update mobile responsive styles to respect the setting
## [0.10.28] - 2025-11-01
### Fixed
- Nostr URI processing in markdown links
- Prevent double-processing of markdown to avoid nested links
- Add HTTP URL detection to prevent processing nostr URIs in URLs
- Use parser-based approach to detect markdown link URLs
- Prevent nostr URI replacement inside markdown link URLs
- Remove unused variables in nostrUriResolver
- Improved blog post rendering with proper link handling
### Removed
- Debug console.log statements from nostrUriResolver
- Cleaned up debug logging used during troubleshooting
- Cleaner console output in production
## [0.10.27] - 2025-10-31
### Added
- Refresh button to highlights sidebar header
- Users can manually refresh highlights panel
- Better control over highlights data updates
- Image preloading in BlogPostCard for better caching
- Images are preloaded when blog posts are displayed
- Improved offline access to article images
- Preload logged-in user profile image for offline access
- User profile picture is cached for offline viewing
- Better user experience when network is unavailable
- Development Service Worker for testing image caching
- Service Worker enabled in development mode
- Improved testing capabilities for offline functionality
### Fixed
- Service Worker registration error handling
- Better error handling for Service Worker registration failures
- More robust development mode Service Worker support
- Proper error handling for fetch requests in Service Worker
- Article loading race conditions
- Resolved race condition when loading articles from cache
- Cache is checked synchronously before setting loading state
- Articles are populated in cache from explore view
- Image caching issues
- Images are properly preloaded when loading articles from cache
- Removed bulk image preloading to prevent resource exhaustion errors
- Avoid redundant image preload when using preview data
- Scroll position management
- Scroll position is reset when switching articles
- Save suppression added when resetting scroll position
- Reader content is cleared immediately when article changes
- React hook ordering issues
- useEffect moved before early return in BlogPostCard
- Prevents React hooks dependency violations
- TypeScript and linting issues
- Cache save logic simplified to avoid TypeScript errors
- Unused settings parameter marked as intentionally unused
- Articles are saved to localStorage cache after loading from relays
- Cache is saved immediately when first event is received
### Performance
- Avoid redundant image preload when using preview data
- Prevents unnecessary image loading operations
- Improved resource utilization
### Removed
- Debug console.log statements
- Removed all debug console output from article cache and service worker
- Removed debug logging from useImageCache hook
- Cleaner console output in production
- Unused refresh button from highlights panel header
- Cleaned up unused UI component
## [0.10.26] - 2025-10-31
### Added

View File

@@ -1,6 +1,6 @@
{
"name": "boris",
"version": "0.10.27",
"version": "0.10.30",
"description": "A minimal nostr client for bookmark management",
"homepage": "https://read.withboris.com/",
"type": "module",

View File

@@ -27,7 +27,7 @@ const AuthorCard: React.FC<AuthorCardProps> = ({ authorPubkey, clickable = true
const handleClick = () => {
if (clickable) {
const npub = nip19.npubEncode(authorPubkey)
navigate(`/p/${npub}`)
navigate(`/p/${npub}/writings`)
}
}

View File

@@ -100,6 +100,17 @@ const Bookmarks: React.FC<BookmarksProps> = ({
previousLocationRef.current = location.pathname
}
}, [location.pathname, showSettings, showMe, showExplore, showProfile])
// Reset scroll to top when navigating to profile routes
useEffect(() => {
if (showProfile) {
// Reset scroll position when navigating to profile pages
// Use requestAnimationFrame to ensure it happens after DOM updates
requestAnimationFrame(() => {
window.scrollTo({ top: 0, behavior: 'instant' })
})
}
}, [location.pathname, showProfile])
const activeAccount = Hooks.useActiveAccount()
const accountManager = Hooks.useAccountManager()

View File

@@ -71,8 +71,9 @@ export function useSettings({ relayPool, eventStore, pubkey, accountManager }: U
// Set paragraph alignment
root.setProperty('--paragraph-alignment', settings.paragraphAlignment || 'justify')
// Set image max-width based on full-width setting
root.setProperty('--image-max-width', settings.fullWidthImages ? 'none' : '100%')
// Set image width and max-height based on full-width setting
root.setProperty('--image-width', settings.fullWidthImages ? '100%' : 'auto')
root.setProperty('--image-max-height', settings.fullWidthImages ? 'none' : '70vh')
}

View File

@@ -57,13 +57,14 @@
.reader .reader-markdown h1, .reader .reader-markdown h2, .reader .reader-markdown h3, .reader .reader-markdown h4, .reader .reader-markdown h5, .reader .reader-markdown h6 { text-align: left !important; }
/* Tame images from external content */
.reader .reader-html img, .reader .reader-markdown img {
max-width: var(--image-max-width, 100%);
max-height: 70vh;
width: var(--image-width, auto);
max-width: 100%;
max-height: var(--image-max-height, 70vh);
height: auto;
width: auto;
display: block;
margin: 0.75rem auto;
border-radius: 6px;
object-fit: contain;
}
/* Headlines with Tailwind typography */
.reader-markdown h1, .reader-html h1 {
@@ -192,9 +193,11 @@
}
.reader-markdown img, .reader-html img {
width: var(--image-width, auto) !important;
max-width: 100% !important;
width: auto !important;
max-height: var(--image-max-height, 70vh) !important;
height: auto;
object-fit: contain;
}
}

View File

@@ -107,17 +107,123 @@ export function getNostrUriLabel(encoded: string): string {
}
}
/**
* Process markdown to replace nostr URIs while skipping those inside markdown links
* This prevents nested markdown link issues when nostr identifiers appear in URLs
*/
function replaceNostrUrisSafely(
markdown: string,
getReplacement: (encoded: string) => string
): string {
// Track positions where we're inside a markdown link URL
// Use a parser approach to correctly handle URLs with brackets/parentheses
const linkRanges: Array<{ start: number, end: number }> = []
// Find all markdown link URLs by looking for ]( pattern and tracking to matching )
let i = 0
while (i < markdown.length) {
// Look for ]( pattern that starts a markdown link URL
const urlStartMatch = markdown.indexOf('](', i)
if (urlStartMatch === -1) break
const urlStart = urlStartMatch + 2 // Position after "]("
// Now find the matching closing parenthesis
// We need to account for nested parentheses and escaped characters
let pos = urlStart
let depth = 1 // We're inside one set of parentheses
let urlEnd = -1
while (pos < markdown.length && depth > 0) {
const char = markdown[pos]
const nextChar = pos + 1 < markdown.length ? markdown[pos + 1] : ''
// Check for escaped characters
if (char === '\\' && nextChar) {
pos += 2 // Skip escaped character
continue
}
if (char === '(') {
depth++
} else if (char === ')') {
depth--
if (depth === 0) {
urlEnd = pos
break
}
}
pos++
}
if (urlEnd !== -1) {
linkRanges.push({
start: urlStart,
end: urlEnd
})
i = urlEnd + 1
} else {
// No matching closing paren found, skip this one
i = urlStart + 1
}
}
// Check if a position is inside any markdown link URL
const isInsideLinkUrl = (pos: number): boolean => {
return linkRanges.some(range => pos >= range.start && pos < range.end)
}
// Replace nostr URIs, but skip those inside link URLs
// Also check if nostr URI is part of any URL pattern (http/https URLs)
// Callback params: (match, encoded, type, offset, string)
const result = markdown.replace(NOSTR_URI_REGEX, (match, encoded, _type, offset, fullString) => {
const matchEnd = offset + match.length
// Check if this match is inside a markdown link URL
// Check both start and end positions to ensure we catch the whole match
const startInside = isInsideLinkUrl(offset)
const endInside = isInsideLinkUrl(matchEnd - 1) // Check end position
if (startInside || endInside) {
// Don't replace - return original match
return match
}
// Also check if the nostr URI is part of an HTTP/HTTPS URL pattern
// This catches cases where the source markdown has URLs like https://example.com/naddr1...
// before they're formatted as markdown links
const contextBefore = fullString.slice(Math.max(0, offset - 200), offset)
const contextAfter = fullString.slice(matchEnd, Math.min(fullString.length, matchEnd + 10))
// Check if we're inside an http/https URL (looking for https?:// pattern before the match)
// and the match is followed by valid URL characters (not whitespace or closing paren)
const urlPatternBefore = /https?:\/\/[^\s)]*$/i
const isInHttpUrl = urlPatternBefore.test(contextBefore)
const isValidUrlContinuation = !contextAfter.match(/^[\s)]/) // Not followed by space or closing paren
if (isInHttpUrl && isValidUrlContinuation) {
// Don't replace - return original match
return match
}
// encoded is already the NIP-19 identifier without nostr: prefix (from capture group)
const replacement = getReplacement(encoded)
return replacement
})
return result
}
/**
* Replace nostr: URIs in markdown with proper markdown links
* This converts: nostr:npub1... to [label](link)
*/
export function replaceNostrUrisInMarkdown(markdown: string): string {
return markdown.replace(NOSTR_URI_REGEX, (match) => {
// Extract just the NIP-19 identifier (without nostr: prefix)
const encoded = match.replace(/^nostr:/, '')
return replaceNostrUrisSafely(markdown, (encoded) => {
const link = createNostrLink(encoded)
const label = getNostrUriLabel(encoded)
return `[${label}](${link})`
})
}
@@ -132,9 +238,7 @@ export function replaceNostrUrisInMarkdownWithTitles(
markdown: string,
articleTitles: Map<string, string>
): string {
return markdown.replace(NOSTR_URI_REGEX, (match) => {
// Extract just the NIP-19 identifier (without nostr: prefix)
const encoded = match.replace(/^nostr:/, '')
return replaceNostrUrisSafely(markdown, (encoded) => {
const link = createNostrLink(encoded)
// For articles, use the resolved title if available