mirror of
https://github.com/dergigi/boris.git
synced 2026-02-17 13:04:59 +01:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7450ba4251 | ||
|
|
95c770c083 | ||
|
|
14a7e1138e | ||
|
|
9c45c71c8a | ||
|
|
23b9224272 | ||
|
|
bcd4a12542 | ||
|
|
d82e22ce1c | ||
|
|
ea5c173745 | ||
|
|
a214c487cc | ||
|
|
43f56fc29a | ||
|
|
cfbc3efeeb | ||
|
|
bb9e98ff16 |
105
CHANGELOG.md
105
CHANGELOG.md
@@ -7,6 +7,108 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.10.0] - 2025-01-27
|
||||
|
||||
### Added
|
||||
|
||||
- Centralized bookmark loading with streaming and auto-decrypt
|
||||
- Bookmarks now load progressively with streaming updates
|
||||
- Auto-decrypt bookmarks as they arrive from relays
|
||||
- Individual decrypt buttons for encrypted bookmark events
|
||||
- Centralized bookmark controller for consistent loading across the app
|
||||
- Enhanced debug page with comprehensive diagnostics
|
||||
- Interactive NIP-04 and NIP-44 encryption/decryption testing
|
||||
- Live performance timing with stopwatch display
|
||||
- Bookmark loading and decryption diagnostics
|
||||
- Real-time bunker logs with filtering and clearing
|
||||
- Version and git commit footer
|
||||
- Bunker (NIP-46) authentication support
|
||||
- Support for remote signing via Nostr Connect protocol
|
||||
- Bunker URI input with validation and error handling
|
||||
- Automatic reconnection on app restore with proper permissions
|
||||
- Signer suggestions in error messages (Amber, nsec.app, Nostrum)
|
||||
|
||||
### Changed
|
||||
|
||||
- Improved bookmark loading performance
|
||||
- Non-blocking, progressive bookmark updates via callback pattern
|
||||
- Batched background hydration using EventLoader and AddressLoader
|
||||
- Shorter timeouts for debug page bookmark loading
|
||||
- Sequential decryption instead of concurrent to avoid queue issues
|
||||
- Enhanced bunker error messages
|
||||
- Formatted error messages with signer suggestions
|
||||
- Links to nos2x, Amber, nsec.app, and Nostrum signers
|
||||
- Better error handling for missing signer extensions
|
||||
- Centralized bookmark loading architecture
|
||||
- Single shared bookmark controller for consistent loading
|
||||
- Unified bookmark loading with streaming and auto-decrypt
|
||||
- Consolidated bookmark loading into single centralized function
|
||||
|
||||
### Fixed
|
||||
|
||||
- NIP-46 bunker signing and decryption
|
||||
- NostrConnectSigner properly reconnects with permissions on app restore
|
||||
- Bunker relays added to relay pool for signing requests
|
||||
- Proper setup of pool and relays before bunker reconnection
|
||||
- Expose nip04/nip44 on NostrConnectAccount for bookmark decryption
|
||||
- Cache wrapped nip04/nip44 objects instead of using getters
|
||||
- Wait for bunker relay connections before marking signer ready
|
||||
- Validate bunker URI (remote must differ from user pubkey)
|
||||
- Accept remote===pubkey for Amber compatibility
|
||||
- Bookmark loading and decryption
|
||||
- Bookmarks load and complete properly with streaming
|
||||
- Auto-decrypt private bookmarks with NIP-04 detection
|
||||
- Include decrypted private bookmarks in sidebar
|
||||
- Skip background event fetching when there are too many IDs
|
||||
- Only build bookmarks from ready events (unencrypted or decrypted)
|
||||
- Restore Debug page decrypt display via onDecryptComplete callback
|
||||
- Make controller onEvent non-blocking for queryEvents completion
|
||||
- Proper timeout handling for bookmark decryption (no hanging)
|
||||
- Smart encryption detection with consistent padlock display
|
||||
- Sequential decryption instead of concurrent to avoid queue issues
|
||||
- Add extraRelays to EventLoader and AddressLoader
|
||||
- TypeScript and linting errors throughout
|
||||
- Replace empty catch blocks with warnings
|
||||
- Fix explicit any types
|
||||
- Add missing useEffect dependencies
|
||||
- Resolve all linting issues in App.tsx, Debug.tsx, and async utilities
|
||||
|
||||
### Performance
|
||||
|
||||
- Non-blocking NIP-46 operations
|
||||
- Fire-and-forget NIP-46 publish for better UI responsiveness
|
||||
- Non-blocking bookmark decryption with sequential processing
|
||||
- Make controller onEvent non-blocking for queryEvents completion
|
||||
- Optimized bookmark loading
|
||||
- Batched background hydration using EventLoader and AddressLoader
|
||||
- Progressive, non-blocking bookmark loading with streaming
|
||||
- Shorter timeouts for debug page bookmark loading
|
||||
- Remove artificial delays from bookmark decryption
|
||||
|
||||
### Refactored
|
||||
|
||||
- Centralized bookmark controller architecture
|
||||
- Extract bookmark streaming helpers and centralize loading
|
||||
- Consolidated bookmark loading into single function
|
||||
- Remove deprecated bookmark service files
|
||||
- Share bookmark controller between components
|
||||
- Debug page organization
|
||||
- Extract VersionFooter component to eliminate duplication
|
||||
- Structured sections with proper layout and styling
|
||||
- Apply settings page styling structure
|
||||
- Simplified bunker implementation following applesauce patterns
|
||||
- Clean up bunker implementation for better maintainability
|
||||
- Import RELAYS from central config (DRY principle)
|
||||
- Update RELAYS list with relay.nsec.app
|
||||
|
||||
### Documentation
|
||||
|
||||
- Comprehensive Amber.md documentation
|
||||
- Amethyst-style bookmarks section
|
||||
- Bunker decrypt investigation summary
|
||||
- Critical queue disabling requirement
|
||||
- NIP-46 setup and troubleshooting
|
||||
|
||||
## [0.9.1] - 2025-10-20
|
||||
|
||||
### Added
|
||||
@@ -2182,7 +2284,8 @@ 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
|
||||
|
||||
[Unreleased]: https://github.com/dergigi/boris/compare/v0.9.1...HEAD
|
||||
[Unreleased]: https://github.com/dergigi/boris/compare/v0.10.0...HEAD
|
||||
[0.10.0]: https://github.com/dergigi/boris/compare/v0.9.1...v0.10.0
|
||||
[0.9.1]: https://github.com/dergigi/boris/compare/v0.9.0...v0.9.1
|
||||
[0.8.3]: https://github.com/dergigi/boris/compare/v0.8.2...v0.8.3
|
||||
[0.8.2]: https://github.com/dergigi/boris/compare/v0.8.0...v0.8.2
|
||||
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "boris",
|
||||
"version": "0.9.1",
|
||||
"version": "0.10.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "boris",
|
||||
"version": "0.9.1",
|
||||
"version": "0.10.1",
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-svg-core": "^7.1.0",
|
||||
"@fortawesome/free-regular-svg-icons": "^7.1.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "boris",
|
||||
"version": "0.10.0",
|
||||
"version": "0.10.1",
|
||||
"description": "A minimal nostr client for bookmark management",
|
||||
"homepage": "https://read.withboris.com/",
|
||||
"type": "module",
|
||||
|
||||
@@ -2,6 +2,7 @@ import React from 'react'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { faGauge } from '@fortawesome/free-solid-svg-icons'
|
||||
import { UserSettings } from '../../services/settingsService'
|
||||
import TTSControls from '../TTSControls'
|
||||
|
||||
interface TTSSettingsProps {
|
||||
settings: UserSettings
|
||||
@@ -9,6 +10,7 @@ interface TTSSettingsProps {
|
||||
}
|
||||
|
||||
const SPEED_OPTIONS = [0.8, 1, 1.2, 1.4, 1.6, 1.8, 2, 2.1, 2.4, 2.8, 3]
|
||||
const EXAMPLE_TEXT = "Boris aims to be a calm reader app with clean typography, beautiful design, and a focus on readability. Boris does not and will never have ads, trackers, paywalls, subscriptions, or any other distractions."
|
||||
|
||||
const TTSSettings: React.FC<TTSSettingsProps> = ({ settings, onUpdate }) => {
|
||||
const currentSpeed = settings.ttsDefaultSpeed || 2.1
|
||||
@@ -43,14 +45,40 @@ const TTSSettings: React.FC<TTSSettingsProps> = ({ settings, onUpdate }) => {
|
||||
<div className="setting-control">
|
||||
<select
|
||||
value={settings.ttsLanguageMode || 'content'}
|
||||
onChange={e => onUpdate({ ttsLanguageMode: (e.target.value as 'system' | 'content'), ttsUseSystemLanguage: e.target.value === 'system', ttsDetectContentLanguage: e.target.value !== 'system' })}
|
||||
style={{ background: 'var(--color-bg-elevated)', color: 'var(--color-text)', border: '1px solid var(--color-border)', borderRadius: 6, padding: '0.25rem 0.5rem' }}
|
||||
onChange={e => {
|
||||
const value = e.target.value
|
||||
onUpdate({
|
||||
ttsLanguageMode: value,
|
||||
ttsUseSystemLanguage: value === 'system',
|
||||
ttsDetectContentLanguage: value === 'content'
|
||||
})
|
||||
}}
|
||||
className="setting-select"
|
||||
>
|
||||
<option value="system">System Language</option>
|
||||
<option value="content">Content (auto-detect)</option>
|
||||
<option disabled>────────────</option>
|
||||
<option value="en-US">English (American)</option>
|
||||
<option value="en-GB">English (British)</option>
|
||||
<option value="zh">Mandarin Chinese</option>
|
||||
<option value="es">Spanish</option>
|
||||
<option value="hi">Hindi</option>
|
||||
<option value="ar">Arabic</option>
|
||||
<option value="fr">French</option>
|
||||
<option value="pt">Portuguese</option>
|
||||
<option value="de">German</option>
|
||||
<option value="ja">Japanese</option>
|
||||
<option value="ru">Russian</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="setting-group">
|
||||
<div style={{ padding: '0.75rem', backgroundColor: 'var(--color-bg)', borderRadius: '4px', marginBottom: '0.75rem', fontSize: '0.95rem', lineHeight: '1.5' }}>
|
||||
{EXAMPLE_TEXT}
|
||||
</div>
|
||||
<TTSControls text={EXAMPLE_TEXT} settings={settings} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -37,12 +37,25 @@ const TTSControls: React.FC<Props> = ({ text, defaultLang, className, settings }
|
||||
return settings?.ttsDetectContentLanguage !== false
|
||||
}, [settings?.ttsLanguageMode, settings?.ttsDetectContentLanguage])
|
||||
|
||||
const specificLang = useMemo(() => {
|
||||
const mode = settings?.ttsLanguageMode
|
||||
// If mode is not 'system' or 'content', it's a specific language code
|
||||
if (mode && mode !== 'system' && mode !== 'content') {
|
||||
return mode
|
||||
}
|
||||
return undefined
|
||||
}, [settings?.ttsLanguageMode])
|
||||
|
||||
const handlePlayPause = () => {
|
||||
if (!canPlay) return
|
||||
|
||||
if (!speaking) {
|
||||
let langOverride: string | undefined
|
||||
if (detectContentLang && text) {
|
||||
|
||||
// Priority: specific language > content detection > system language
|
||||
if (specificLang) {
|
||||
langOverride = specificLang
|
||||
} else if (detectContentLang && text) {
|
||||
try {
|
||||
const lang = detect(text)
|
||||
if (typeof lang === 'string' && lang.length >= 2) langOverride = lang.slice(0, 2)
|
||||
|
||||
@@ -68,7 +68,7 @@ export interface UserSettings {
|
||||
// TTS language selection
|
||||
ttsUseSystemLanguage?: boolean // default: false
|
||||
ttsDetectContentLanguage?: boolean // default: true
|
||||
ttsLanguageMode?: 'system' | 'content' // default: 'content'
|
||||
ttsLanguageMode?: 'system' | 'content' | string // default: 'content', can also be language code like 'en', 'es', etc.
|
||||
// Text-to-Speech settings
|
||||
ttsDefaultSpeed?: number // default: 2.1
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
word-wrap: break-word;
|
||||
text-align: var(--paragraph-alignment, justify);
|
||||
}
|
||||
.setting-select { width: 100%; padding: 0.5rem; background: var(--color-bg-elevated); border: 1px solid var(--color-border-subtle); border-radius: 4px; color: var(--color-text); font-size: 1rem; }
|
||||
.setting-select { width: 100%; padding: 0.5rem 1.75rem 0.5rem 0.5rem; background: var(--color-bg-elevated); border: 1px solid var(--color-border-subtle); border-radius: 4px; color: var(--color-text); font-size: 1rem; }
|
||||
.setting-inline .setting-select { width: auto; min-width: 200px; flex: 1; }
|
||||
.setting-select:focus { outline: none; border-color: var(--color-primary); }
|
||||
.font-select option { padding: 0.5rem; font-size: 1rem; }
|
||||
|
||||
Reference in New Issue
Block a user