Commit Graph

434 Commits

Author SHA1 Message Date
Claude
fa3f872457 fix: prefer upstream_inference_cost over cost for BYOK
For BYOK (Bring Your Own Key) scenarios, OpenRouter returns:
- cost: 0 (no charge to OpenRouter account)
- cost_details.upstream_inference_cost: actual API cost

Now checks upstream_inference_cost first, which contains the actual
cost regardless of billing mode.

https://claude.ai/code/session_01HqtD9R33oqfB14Gu1V5wHC
2026-01-31 15:19:09 +00:00
Claude
53aca05a65 fix: parse reasoning from delta.reasoning (OpenAI format)
Different providers use different field names for reasoning traces:
- Claude/DeepSeek: delta.reasoning_content
- OpenAI/OpenRouter: delta.reasoning

Now checks both fields to ensure reasoning is captured regardless of
which format the API uses.

https://claude.ai/code/session_01HqtD9R33oqfB14Gu1V5wHC
2026-01-31 15:16:24 +00:00
Claude
ea07b841aa fix: capture actual model and cost from API response
The model was showing "auto" instead of the actual model (e.g.,
"anthropic/claude-opus-4.5") because we were storing the requested
modelId rather than the model from the API response.

Now properly captures:
- chunk.model: the actual model that generated the response
- chunk.usage.cost or chunk.usage.cost_details.upstream_inference_cost:
  API-provided cost (falls back to calculated cost if not available)

https://claude.ai/code/session_01HqtD9R33oqfB14Gu1V5wHC
2026-01-31 15:07:46 +00:00
Claude
15d353e6e6 feat: add cost transparency for AI chat messages
Per-message cost display:
- Add model, usage, cost fields to AssistantMessage (local-only, not sent to API)
- Store cost/model/usage when saving assistant messages in session-manager
- Display subtle cost footer on assistant messages: "gpt-4o-mini • 847 tokens • $0.0012"
- Clean model names by removing provider prefixes and date suffixes

Session cost display:
- Calculate total conversation cost from messages
- Show "Session: $0.0234" above input area when cost > 0
- Updates automatically as messages are added

Cost formatting:
- Show 4 decimal places for costs < $0.01
- Show 2 decimal places for costs >= $0.01
- Show "<$0.0001" for very small costs

https://claude.ai/code/session_01HqtD9R33oqfB14Gu1V5wHC
2026-01-31 14:56:12 +00:00
Alejandro Gómez
32cf096c99 nit: ui tweaks 2026-01-31 15:41:33 +01:00
Claude
eca153efc9 feat: add production-ready error handling with retry logic
- Add error-handling.ts with centralized error parsing and categorization
- Implement exponential backoff with jitter for transient errors
- Handle rate limits (429) respecting Retry-After header
- Add retry state to ChatSessionState and ChatStreamChunk types
- Update provider-manager with automatic retry loop for:
  - Rate limit errors (429)
  - Server errors (5xx)
  - Network errors
  - Timeouts
- Add retry indicator UI in AIViewer showing retry progress
- Show detailed error messages with attempt count on failure

Retry configuration:
- Max 3 retries (4 total attempts)
- Exponential backoff: 1s, 2s, 4s (capped at 30s)
- 20% jitter to prevent thundering herd
- Respects abort signal during wait

https://claude.ai/code/session_01HqtD9R33oqfB14Gu1V5wHC
2026-01-31 14:31:58 +00:00
Claude
3f19138ce2 feat: add system prompts for AI chat
- Add LLMSystemPrompt type and llmSystemPrompts table to Dexie (v19)
- Create SystemPromptManager with built-in Grimoire prompt that includes
  Nostr protocol knowledge (kinds, NIPs, commands, tools)
- Add useSystemPrompts and usePromptOptions hooks for React integration
- Add AIPromptsViewer component for viewing/creating/editing prompts
- Add prompt selector to new conversation UI in AIViewer
- Wire systemPromptId through conversation creation to API calls
- Prepend system prompt to messages when calling LLM API

The Grimoire prompt dynamically includes:
- Nostr protocol basics
- Common event kinds with descriptions
- Key NIPs for reference
- Available Grimoire commands
- Registered tools

https://claude.ai/code/session_01HqtD9R33oqfB14Gu1V5wHC
2026-01-31 13:52:06 +00:00
Claude
7d5f612701 fix: correct canResume logic to avoid showing spurious Resume button
- Initialize finishReason to "stop" in new sessions (was undefined)
- Fix canResume to only be true when there's actually something to resume:
  - tool_calls: model called tools, waiting for results
  - length: response was truncated
  - null with streamingContent: user aborted mid-stream

Previously, undefined finishReason passed the check (undefined !== "stop")
causing Resume button to appear on fresh conversations.

https://claude.ai/code/session_01HqtD9R33oqfB14Gu1V5wHC
2026-01-31 12:52:13 +00:00
Claude
0b37298611 fix: resolve new conversation message send bug
Two bugs were causing the first message in a new conversation to fail:

1. Session not found: sendMessage threw "No session found" because the
   session was only created in useEffect (which runs after sendMessage).
   Fix: Auto-open session in sendMessage if one doesn't exist, using
   the conversation's provider/model from Dexie.

2. Pending message cleared: useEffect that resets state on conversationId
   change also ran when creating a new conversation, clearing the
   pendingUserMessage we just set.
   Fix: Track isCreatingConversationRef and skip the reset when we're
   in the middle of creating a conversation.

https://claude.ai/code/session_01HqtD9R33oqfB14Gu1V5wHC
2026-01-31 12:48:51 +00:00
Claude
afb0ea1e81 feat: add get_my_profile tool and max iterations limit
- Create builtin-tools.ts with get_my_profile tool that returns the
  logged-in user's Nostr profile metadata
- Add MAX_TOOL_ITERATIONS (10) to prevent infinite agentic loops
- Auto-register builtin tools on session manager import

https://claude.ai/code/session_01HqtD9R33oqfB14Gu1V5wHC
2026-01-31 12:29:35 +00:00
Claude
fa70933388 feat: add tool calling support to AI chat
- Extend LLMMessage types to support tool calls, tool results, multimodal
  content (images), and reasoning content (extended thinking)
- Add ChatStreamChunk types for tool_call and reasoning streaming
- Implement agentic loop in session manager that continues generation
  after tool execution until finish_reason is "stop"
- Create tool interface and registry for registering executable tools
- Update provider manager to stream tool calls and pass tools to API
- Update AIViewer UI to render tool calls, tool results, and reasoning
- Clean up legacy hooks in useLLM.ts (replaced by session manager)

https://claude.ai/code/session_01HqtD9R33oqfB14Gu1V5wHC
2026-01-31 10:19:24 +00:00
Claude
75fdce994a feat: add ChatSessionManager with RxJS patterns
Introduce a production-quality session management system for AI chat:

Architecture:
- ChatSessionManager singleton manages all active chat sessions
- Sessions keyed by conversationId (not window) for multi-window support
- Multiple windows viewing same conversation share streaming state
- Reference counting with delayed cleanup for quick tab switching

Features:
- RxJS BehaviorSubject for reactive session state
- Subject streams for events (streaming, messages, errors)
- Automatic usage and cost tracking per session
- Resume functionality when generation is interrupted
- Proper abort handling with partial message saving

React Integration:
- useChatSession(conversationId) - main hook combining Dexie + session state
- useChatActions() - stable action functions
- useConversations() - reactive conversation list
- useStreamingContent() - lightweight streaming-only subscription

Migrated AIViewer to use new hooks, significantly simplifying the
component by removing direct DB operations and complex state management.

https://claude.ai/code/session_01HqtD9R33oqfB14Gu1V5wHC
2026-01-31 09:52:18 +00:00
Alejandro Gómez
93b359fd69 ui: tweaks 2026-01-31 00:37:51 +01:00
Claude
0b92c80e99 fix: resolve AI chat state consistency issues
Fix multiple state consistency bugs in the AI chat UI:

1. Model reset race condition: When selecting a conversation with a
   different provider, the model was being set correctly but then
   immediately reset to null by an effect. Now uses refs to track
   when we're selecting a conversation that specifies its own model.

2. Stale closure in first message: When creating a new conversation
   and adding the first message, addMessage had a stale conversationId
   in its closure and returned null. Now uses direct DB operations
   with the correct conversation ID.

3. No state cleanup on conversation switch: streamingContent,
   pendingUserMessage, and isWaitingForResponse now reset when
   switching conversations.

4. Direct DB access consistency: All message operations now use
   direct DB access to avoid stale closure issues.

https://claude.ai/code/session_01HqtD9R33oqfB14Gu1V5wHC
2026-01-30 23:26:53 +00:00
Claude
ccca821458 refactor: unify AI providers with OpenAI SDK, remove WebLLM
Remove WebLLM (local model) support and standardize all AI providers
on the OpenAI-compatible API pattern using the official OpenAI SDK.

Key changes:
- Add provider presets for PPQ, OpenRouter, OpenAI, Anthropic, DeepSeek,
  Groq, Together, Fireworks, xAI, and custom providers
- Create unified OpenAI client factory with provider-specific handling
- Dynamic model fetching from provider /v1/models endpoints
- Add connection testing before saving providers
- Two-step provider add flow: select preset → configure API key
- Track recently used models per provider
- Remove webllm-provider.ts, webllm-worker.ts, ppq-provider.ts
- Simplify AIViewer UI by removing WebLLM status checks

This follows the architecture patterns from shakespeare/soapbox-pub
for a more maintainable multi-provider LLM chat implementation.

https://claude.ai/code/session_01HqtD9R33oqfB14Gu1V5wHC
2026-01-30 23:14:40 +00:00
Claude
e4cc5989f6 refactor: rebuild AI chat UI from scratch
- Clean layout following GroupListViewer patterns
- Header (shrink-0) + Messages (flex-1 overflow-y-auto) + Input (shrink-0)
- Desktop: resizable sidebar with conversation list
- Mobile: Sheet-based sidebar with toggle button
- Proper flex containers with min-h-0 for correct height
- Improved spacing, sizing, and visual hierarchy
- Smaller, compact header with 7px height selectors
- MessageSquare icon in conversation list items
- Better empty state with Brain icon and clear CTA
- Clean separation of concerns (ChatPanel handles messages + input)

https://claude.ai/code/session_01HqtD9R33oqfB14Gu1V5wHC
2026-01-30 22:55:12 +00:00
Claude
c96882b330 fix: improve AI chat UX and load conversation settings
- Fix lint error by using regular import instead of require()
- Load conversation's provider and model when selecting a conversation
- Fix thinking indicator to be small muted text instead of bubble
- Skip rendering empty assistant message placeholders
- Show "Add a provider" button when no providers configured
- Remove WebLLM model selector from main ai command
- Clean up unused imports and variables

https://claude.ai/code/session_01HqtD9R33oqfB14Gu1V5wHC
2026-01-30 22:43:30 +00:00
Claude
4c19ccec30 feat: improve AI chat UX with optimistic updates and model memory
- Show user messages immediately (optimistic UI)
- Add thinking indicator while waiting for AI response
- Remember last used model per provider
- Pre-select last model when switching providers
- Fall back to first available model if last isn't available
- Disable input while waiting for response

https://claude.ai/code/session_01HqtD9R33oqfB14Gu1V5wHC
2026-01-30 22:29:10 +00:00
Claude
b24c218c72 feat: improve AI interface with markdown, thinking indicator, and providers subcommand
- Add 'ai providers' subcommand for provider management
- Add markdown rendering for AI responses using react-markdown
- Add thinking indicator with brain icon while waiting for response
- Handle PPQ 402 Payment Required with 'Insufficient balance' message
- Extract AIProvidersViewer to separate component
- Move 'Add Provider' button to providers interface only
- Fix chat input width to be full width

https://claude.ai/code/session_01HqtD9R33oqfB14Gu1V5wHC
2026-01-30 22:23:37 +00:00
Claude
7d12b960e3 feat: add local LLM chat with WebLLM and PPQ.ai support
Implements browser-based AI chat functionality:

- WebLLM provider for local inference via WebGPU
  - Downloads and caches models in IndexedDB
  - Runs inference in web worker to keep UI responsive
  - Curated list of recommended models (SmolLM2, Llama 3.2, Phi 3.5, etc.)

- PPQ.ai provider for cloud-based inference
  - OpenAI-compatible API with Lightning payments
  - Dynamic model list fetching with caching

- Provider manager for coordinating multiple providers
  - Dexie tables for persisting provider instances and conversations
  - Model list caching with 1-hour TTL

- AIViewer component with sidebar pattern
  - Conversation history in resizable sidebar (mobile: sheet)
  - Model selector for WebLLM with download progress
  - Streaming chat responses
  - Auto-generated conversation titles

- New `ai` command to launch the interface

https://claude.ai/code/session_01HqtD9R33oqfB14Gu1V5wHC
2026-01-30 21:53:48 +00:00
Alejandro
0a5c80bc9c fix: pass mediaType and blobUrl props to BlossomViewer (#234)
The --type flag for blossom blob command was being parsed correctly but
the mediaType and blobUrl props were not forwarded from WindowRenderer
to BlossomViewer, causing the preview to always show "preview not available".

https://claude.ai/code/session_018edX9kUcEudJzwuFCGh1xf

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-30 17:41:05 +01:00
Alejandro
eca21d3191 feat: load media setting (#230)
* feat: add custom media renderer support to RichText

Add renderMedia prop to RichText component that allows overriding
how images, videos, and audio are rendered across the entire
RichText subtree (including Link and Gallery components).

The custom renderer receives:
- url: the media URL
- type: "image" | "video" | "audio"
- imeta: optional NIP-92 metadata (dimensions, blurhash, alt, etc.)

This enables chat-specific media rendering with compact thumbnails,
lightboxes, or any custom presentation.

https://claude.ai/code/session_01AeeN5d5EcVLGjZbGueZxaD

* feat: add ChatMediaRenderer for compact inline media display

Custom media renderer for chat that shows:
- Media type icon (image/video/audio)
- Filename (extracted from URL or imeta alt)
- File size (from imeta if available)
- Blossom link button (opens blob viewer for blossom URLs)

Usage:
  <RichText event={message} renderMedia={ChatMediaRenderer} />

https://claude.ai/code/session_01AeeN5d5EcVLGjZbGueZxaD

* feat: use ChatMediaRenderer in chat messages

- Updated ChatMediaRenderer to use getHashFromURL from blossom-client-sdk
- Integrated ChatMediaRenderer into ChatViewer for message content

https://claude.ai/code/session_01AeeN5d5EcVLGjZbGueZxaD

* style: make ChatMediaRenderer inline with subtle dotted border

Use a subtle dotted bottom border instead of background/padding
to better integrate with surrounding text flow.

https://claude.ai/code/session_01AeeN5d5EcVLGjZbGueZxaD

* feat: improve ChatMediaRenderer UX

- Full dotted border with rounded corners (was bottom-only)
- Increased gap between elements
- Images/videos open in MediaDialog zoom viewer on click
- Audio still opens in new tab
- Better extension extraction for display

https://claude.ai/code/session_01AeeN5d5EcVLGjZbGueZxaD

* feat: improve blossom integration in ChatMediaRenderer

- Use HardDrive icon instead of Flower2 (consistent with rest of app)
- Add commandString for Edit functionality in window toolbar
- Pass blobUrl with extension to BlossomViewer for correct media preview
- Extended BlossomViewer.BlobDetailView to accept blobUrl prop

https://claude.ai/code/session_01AeeN5d5EcVLGjZbGueZxaD

* refactor: simplify ChatMediaRenderer styling

- All icons now size-3
- Use dotted underline only (cleaner inline appearance)
- All media types open in new tab on click
- Removed MediaDialog dependency

https://claude.ai/code/session_01AeeN5d5EcVLGjZbGueZxaD

* fix: inherit custom media renderer in nested RichText components

Previously, nested RichText components (like those in QuotedEvent/KindRenderer)
would always reset the media renderer context to null because the provider
used `renderMedia ?? null`. This caused embedded events in chat to use
the default media renderer instead of ChatMediaRenderer.

Now RichText inherits the parent media renderer when no explicit renderMedia
prop is passed, allowing custom renderers to work throughout the entire
rich text subtree including quoted events.

https://claude.ai/code/session_01AeeN5d5EcVLGjZbGueZxaD

* feat: improve ChatMediaRenderer with expandable inline media

- More compact representation: "icon truncated-hash blossom"
- Click filename to expand and show actual media inline (image/video/audio)
- Tooltip shows full filename and file size on hover
- Audio support with inline audio player when expanded
- Fixed BlossomViewer to detect media type from URL extension when
  mimeType is not available from blob descriptor

https://claude.ai/code/session_01AeeN5d5EcVLGjZbGueZxaD

* feat: improve ChatMediaRenderer with better UX

- Click to expand shows MediaEmbed (not collapsible once expanded)
- Tooltip only shows when imeta is present, displays: alt, mime type,
  dimensions, size, duration
- More compact representation: [icon] truncated-hash [blossom]
- Pass mediaType to BlossomViewer for correct preview rendering
- BlossomViewer now accepts mediaType hint for preview type detection

https://claude.ai/code/session_01AeeN5d5EcVLGjZbGueZxaD

* fix: blossom blob command now parses --type flag

- Added mediaType and blobUrl fields to BlossomCommandResult
- Blob subcommand now parses --type image|video|audio flag
- Fixed server URL overflow in blob detail view with truncation

https://claude.ai/code/session_01AeeN5d5EcVLGjZbGueZxaD

* refactor: add global loadMedia setting with CompactMediaRenderer

- Add `loadMedia` setting to Appearance section (enabled by default)
- Create CompactMediaRenderer in src/components/nostr/ (renamed from ChatMediaRenderer)
- Link.tsx and Gallery.tsx now check the setting and use CompactMediaRenderer when disabled
- Tooltip format improved: "Field <value>" with field name and value side by side
  - Shows: Hash, Size, Dimensions, Type, Duration, Alt
- Remove renderMedia prop from ChatViewer (now automatic based on setting)
- Delete old ChatMediaRenderer.tsx

This makes media rendering a site-wide setting rather than chat-specific.

https://claude.ai/code/session_01AeeN5d5EcVLGjZbGueZxaD

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-30 17:34:33 +01:00
Alejandro
a75dd2b3fb Remove compact mode settings and simplify settings architecture (#232)
* refactor: remove unused compactModeKinds from app state

The compactModeKinds feature was partially implemented but never used:
- Settings dialog allowed configuration but the value was never consumed
- REQ viewer uses --view flag for compact mode, not this state value

Removed:
- compactModeKinds from GrimoireState type and initial state
- setCompactModeKinds logic function and state hook callback
- Compact Events section from SettingsDialog (now shows placeholder)
- compactModeKinds from AppearanceSettings in settings service
- Migration now strips compactModeKinds if present instead of adding it

https://claude.ai/code/session_01DiWUxiS5BAzU9mrKvCUuMW

* chore: delete unused SettingsDialog component

The SettingsDialog was imported but never used - the Settings menu
item opens a window via addWindow("settings", ...) instead.

https://claude.ai/code/session_01DiWUxiS5BAzU9mrKvCUuMW

* refactor: simplify settings service to only used settings

Removed unused settings that were defined but never consumed:
- RelaySettings (fallbackRelays, discoveryRelays, outbox*, etc.)
- PrivacySettings (shareReadReceipts, blurWalletBalances, etc.)
- DatabaseSettings (maxEventsCached, autoCleanupDays, etc.)
- NotificationSettings (enabled, notifyOnMention, etc.)
- DeveloperSettings (debugMode, showEventIds, logLevel, etc.)
- Most of AppearanceSettings (theme, fontSizeMultiplier, etc.)
- Most of PostSettings (defaultRelayMode, customPostRelays)

Only kept settings that are actually used:
- post.includeClientTag
- appearance.showClientTags

Also simplified useSettings hook to match.

https://claude.ai/code/session_01DiWUxiS5BAzU9mrKvCUuMW

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-30 14:23:30 +01:00
Alejandro
8f2f055566 feat: repository tree visualization (#31)
* docs: add plan for repository tree visualization feature

Comprehensive plan covering:
- git-natural-api library analysis and API documentation
- useGitTree/useGitBlob hooks for fetching git data
- FileTreeView component using Radix Collapsible
- Shiki migration with on-demand language loading
- Multi-server fallback for redundant clone URLs
- Dexie caching for offline access

* docs: add comprehensive Shiki migration plan

Detailed plan for migrating from Prism.js to Shiki with lazy loading:
- Analysis of all 8 components using SyntaxHighlight
- Shiki service with singleton highlighter and on-demand language loading
- Custom Grimoire dark theme matching current Prism styles
- Language alias mapping for 50+ extensions
- React hook for async highlighting with loading states
- CSS theme preserving minimalistic dark aesthetics
- Migration steps with rollback plan

* feat: migrate syntax highlighting from Prism.js to Shiki

Replace Prism.js with Shiki for syntax highlighting with several key improvements:

- Lazy loading: Languages loaded on-demand via dynamic imports instead of bundling all upfront
- Broader language support: 200+ TextMate grammars vs 11 statically imported
- Singleton highlighter: Core languages (JS, TS, JSON, diff, bash) preloaded, others loaded on first use

New files:
- src/lib/shiki.ts: Shiki service with highlightCode(), normalizeLanguage(), language aliases
- src/hooks/useHighlightedCode.ts: React hook for async highlighting with loading states
- src/styles/shiki-theme.css: Grimoire dark theme matching previous minimalistic style

Updated components:
- SyntaxHighlight: Now uses Shiki with graceful loading/error states
- CodeSnippetRenderer/DetailRenderer: Simplified, removed manual language mapping
- MarkdownContent: Removed type casts, any language now supported

Removed:
- prismjs and @types/prismjs dependencies
- src/styles/prism-theme.css

* feat: add repository file tree visualization

Add file tree explorer to the Repository detail renderer (kind 30617)
using @fiatjaf/git-natural-api for fetching git trees via HTTP.

New files:
- src/lib/git-types.ts: TypeScript types for DirectoryTree, SelectedFile, etc.
- src/hooks/useGitTree.ts: Hook to fetch git repository tree from clone URLs
  - Tries multiple clone URLs in sequence
  - Uses getDirectoryTreeAt with filter capability when available
  - Falls back to shallowCloneRepositoryAt otherwise
- src/hooks/useGitBlob.ts: Hook to fetch individual file content by hash
  - Detects binary files
  - Returns both raw Uint8Array and decoded text
- src/components/ui/FileTreeView.tsx: Recursive tree view component
  - Collapsible directories with chevron icons
  - File icons based on extension (code, json, text, image, etc.)
  - Alphabetical sorting with directories first
- src/components/nostr/kinds/RepositoryFilesSection.tsx: Main integration
  - Side-by-side tree and file preview layout
  - Syntax-highlighted file content using existing SyntaxHighlight
  - Binary file detection with appropriate UI
  - Loading/error states

Modified:
- RepositoryDetailRenderer.tsx: Added RepositoryFilesSection below relays

Dependencies:
- Added @fiatjaf/git-natural-api from JSR

* fix: improve repository tree visualization UX

- Collapse directories by default in file tree
- Hide files section on tree loading error
- Add code-like skeleton loader with file header
- Fix syntax highlight size jump between loading/loaded states
- Replace purple accent with grayscale theme
- Preload Rust and Markdown languages for reliable highlighting

* refactor: improve git tree and syntax highlighting

- Remove shallow clone fallback from useGitTree, only use no-blobs fetch
  Servers without filter capability are skipped instead of downloading blobs
- Add light theme support for Shiki syntax highlighting
  Theme is automatically selected based on current color scheme

* fix: improve dark theme contrast for syntax highlighting

* fix: address code review issues

- useGitTree: use useStableArray for cloneUrls to fix dependency tracking
- useGitTree/useGitBlob: add isMounted checks to prevent state updates after unmount
- RepositoryFilesSection: remove unnecessary useMemo for language
- FileTreeView: use path instead of hash for React keys
- shiki: track failed languages to avoid repeated console warnings

* fix: improve dark theme contrast for syntax highlighting

- Add CSS variables for syntax highlighting instead of hardcoded colors
- Add --syntax-constant and --syntax-tag variables to light and dark themes
- Use high contrast colors for dark mode (bright green strings, purple keywords)
- Simplify Shiki transformer to output CSS classes instead of inline styles
- Remove unused parameters from transformer callback

* fix: restore syntax highlighting colors

Revert the CSS class-based approach which was failing to classify tokens.
Instead, keep Shiki's inline styles from the theme and only remove
backgrounds to let CSS handle those. The theme colors now provide
syntax highlighting directly.

* feat: add copy button and CSS variable-based syntax highlighting

- Add copy button next to file name in file viewer header (icon-only)
- Use Shiki's createCssVariablesTheme for proper theme integration
- Map Shiki CSS variables to our theme system variables
- Syntax highlighting now works correctly across all themes (light/dark)

* refactor: create IconCopyButton component and use CopyCheck consistently

- Add IconCopyButton component for reusable icon-only copy buttons
- Refactor RepositoryFilesSection to use IconCopyButton
- Replace Check with CopyCheck in ChatMessageContextMenu
- Replace Check with CopyCheck in BaseEventRenderer
- Use text-success instead of text-green-500 for consistency

* fix: add HTML, CSS, TOML to core languages and expand token mappings

- Add html, css, toml to CORE_LANGUAGES for eager loading
- Add variableDefaults to cssVarsTheme for proper initialization
- Expand shiki-theme.css with more token type mappings:
  - HTML/XML: tag, attribute, attr-value
  - CSS: selector, property
  - Additional: variable, operator, number, boolean, regex, etc.

* fix: improve diff line spacing with flex layout

- Use flex-col with gap-0 on code element for tight line packing
- Reduce line-height from 1.5 to 1.4 for tighter spacing
- Add .line display:block with min-height for consistent sizing
- Simplify diff background styling (remove negative margin hack)

* fix: improve code block line spacing and wrap long lines

- Increase line-height from 1.4 to 1.5 for better readability
- Use pre-wrap instead of pre to allow long line wrapping
- Add overflow-wrap: break-word to break long URLs/strings

* chore: remove planning docs

* chore: update @fiatjaf/git-natural-api to 0.2.3

* fix: make code blocks horizontally scrollable with full-width diff backgrounds

- Use white-space: pre for horizontal scrolling instead of wrapping
- Add width: fit-content and min-width: 100% to code element
- Ensure diff line backgrounds extend full width when scrolling

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-30 12:35:59 +01:00
Alejandro
121fbb7654 feat: add "+ new tab" option to move window menu (#229)
* feat: add "+ new tab" option to move window menu

Adds a "+ New tab" option at the top of the "Move to tab" submenu in the
window toolbar. This allows users to create a new tab and move the current
window to it in a single action, streamlining the workflow.

Changes:
- Add moveWindowToNewWorkspace function in logic.ts
- Expose moveWindowToNewWorkspace through useGrimoire hook
- Always show "Move to tab" menu (not just when multiple tabs exist)
- Add "+ New tab" option with Plus icon at top of submenu

https://claude.ai/code/session_01Hy2vYBooPyrF7ZJCodcCav

* refactor: simplify new tab menu item copy

- Change menu text from "+ New tab" to "New" (icon already shows +)
- Simplify toast to just "Moved to new tab"

https://claude.ai/code/session_01Hy2vYBooPyrF7ZJCodcCav

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-29 21:08:34 +01:00
Alejandro
5d35832f85 feat: enhance /review command with deep code review capabilities (#228)
- Accept PR number, branch name, or review uncommitted changes
- Reference project skills (React, Applesauce, Nostr) for context
- Prioritized review criteria: correctness, protocol compliance,
  applesauce patterns, React best practices, simplicity, consistency
- Structured output format with file:line references
- Senior reviewer persona focused on maintainability

https://claude.ai/code/session_012e61BRqaF8LwUAFJuz8FaP

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-29 18:59:57 +01:00
Alejandro
f3cc7779e3 feat: make NWC connections more robust and wallet state reactive (#227)
* fix(nwc): improve connection reliability and add health tracking

- Add connection status observable (disconnected/connecting/connected/error)
- Validate wallet connection on restore using support$ observable with 10s timeout
- Add notification subscription error recovery with exponential backoff (5 retries)
- Add retry logic for balance refresh (3 retries with backoff)
- Use library's support$ observable for wallet capabilities (cached by applesauce)
- Replace manual getInfo() calls with reactive support$ subscription
- Add visual connection status indicator in WalletViewer header
- Add reconnect button when connection is in error state
- Store network info in cached connection for display

https://claude.ai/code/session_01CnJgjFMvZHZWs2ujAiWAiQ

* feat(wallet): add copy NWC connection string button to header

Adds a copy button (Copy/Check icons) in the wallet header before the
refresh button that copies the NWC connection string to clipboard for
easy sharing or backup.

https://claude.ai/code/session_01CnJgjFMvZHZWs2ujAiWAiQ

* fix(wallet): use CopyCheck icon and fix transaction loading

- Change Check to CopyCheck icon for copy NWC string button
- Add walletMethods computed value that combines support$ observable
  with cached info fallback from initial connection
- Fix transaction history not loading because support$ waits for
  kind 13194 events which many NWC wallets don't publish
- The cached info from getInfo() RPC call is now used as fallback

https://claude.ai/code/session_01CnJgjFMvZHZWs2ujAiWAiQ

* refactor(nwc): simplify with derived state from observables

Production-ready refactor of NWC implementation:

nwc.ts:
- Add wallet$ observable for reactive wallet instance access
- Remove redundant subscribeToSupport() - only needed for validation
- Cleaner code organization with clear sections

useWallet.ts:
- All state derived from observables (no useState for wallet)
- Move walletMethods computation to hook (reusable)
- isConnected derived from connectionStatus
- Simplified from 240 to 170 lines

WalletViewer.tsx:
- Use walletMethods from hook instead of local useMemo
- Simpler connection state tracking via connectionStatus
- Remove redundant wallet variable from destructuring
- No color change on copy NWC string (per feedback)

https://claude.ai/code/session_01CnJgjFMvZHZWs2ujAiWAiQ

* refactor(wallet): use useCopy hook for clipboard operations

Replace manual clipboard state management with useCopy hook:
- copyInvoice/invoiceCopied for generated invoice
- copyRawTx/rawTxCopied for transaction JSON
- copyNwc/nwcCopied for NWC connection string

Benefits:
- Cleaner code (removed manual setTimeout calls)
- Automatic timeout cleanup on unmount
- Consistent copy behavior across all clipboard operations

https://claude.ai/code/session_01CnJgjFMvZHZWs2ujAiWAiQ

* feat(wallet): implement lazy-loaded transactions observable

- Add shared wallet types (Transaction, TransactionsState) in src/types/wallet.ts
- Add transactionsState$ observable to NWC service for shared tx state
- Implement loadTransactions, loadMoreTransactions, and retryLoadTransactions
- Auto-refresh transactions on payment notifications
- Simplify WalletViewer to use observable state instead of local state
- Remove manual transaction loading logic from component

https://claude.ai/code/session_01CnJgjFMvZHZWs2ujAiWAiQ

* fix(wallet): update balance observable on initial connect

- Call refreshBalance() in createWalletFromURI to fetch initial balance
- Update balance$ directly when ConnectWalletDialog gets balance
- Fixes issue where WalletViewer showed "-" after connecting while
  user menu showed correct balance (different data sources)

https://claude.ai/code/session_01CnJgjFMvZHZWs2ujAiWAiQ

* refactor(wallet): use single data source for balance across UI

Remove fallback to nwcConnection.balance in user-menu - now both
WalletViewer and user-menu use balance$ observable as the single
source of truth for wallet balance.

https://claude.ai/code/session_01CnJgjFMvZHZWs2ujAiWAiQ

* fix(wallet): address code review issues and simplify user menu

- Fix memory leak: track retry timeout and clear on disconnect
- Add explicit WalletSupport type for support observable
- Add comments explaining balance refresh error handling behavior
- Add comment about restoreWallet not being awaited intentionally
- User menu now uses connectionStatus observable (shows connecting/error states)
- Remove wallet name display from user menu (simplifies UI)
- Remove unused walletServiceProfile hook and getWalletName function

https://claude.ai/code/session_01CnJgjFMvZHZWs2ujAiWAiQ

* refactor(wallet): extract WalletConnectionStatus component

- Create reusable WalletConnectionStatus component for connection indicator
- Remove rounded borders from indicator (now square)
- Export getConnectionStatusColor helper for custom usage
- Use component in both user-menu and WalletViewer
- Supports size (sm/md), showLabel, and className props

https://claude.ai/code/session_01CnJgjFMvZHZWs2ujAiWAiQ

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-29 18:02:40 +01:00
Alejandro
9b883f1173 feat: show [gallery] placeholder when images disabled in galleries (#226)
When images are turned off in the RichText options, galleries now show
a single [gallery] placeholder instead of multiple [image] placeholders.
This provides a cleaner UI for users who have disabled image loading.

https://claude.ai/code/session_01LAhYP5iRQskJC4XWi6MHvf

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-29 13:26:24 +01:00
Alejandro
f9733878e2 Hide scrollbars completely by setting width and height to 0 (#225)
* fix: hide tabbar scrollbar by setting width/height to 0

The no-scrollbar utility's display:none wasn't fully overriding the
global scrollbar styles that set width/height to 8px. Adding explicit
width:0 and height:0 ensures the scrollbar is completely hidden in
WebKit browsers.

https://claude.ai/code/session_018RiPf74GNf2oWcoYNRoZyx

* fix: use !important to override global scrollbar styles

The global * selector's scrollbar-width: thin was overriding the
no-scrollbar utility due to CSS cascade order. Adding !important
ensures the utility always wins.

https://claude.ai/code/session_018RiPf74GNf2oWcoYNRoZyx

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-29 13:02:17 +01:00
Fernando López Guevara
0b3c657705 feat(req-viewer): added today on since/until, added window-title (#221) 2026-01-29 10:20:47 +01:00
Alejandro
fdc7b1499f fix: build proper q-tag with relay hint and author pubkey for replies (#224)
* fix: build proper q-tag with relay hint and author pubkey for replies

When sending replies in NIP-29 and NIP-C7 adapters, now build the full
q-tag format per NIP-C7 spec: ["q", eventId, relayUrl, pubkey]

Previously only the event ID was included, making it harder for clients
to fetch the referenced event. Now:
- NIP-29: includes group relay URL and author pubkey
- NIP-C7: includes seen relay hint and author pubkey

https://claude.ai/code/session_01Jy51Ayk57fzaFuuFFm1j1K

* chore: remove unused NIP-C7 adapter

The NIP-C7 adapter was already disabled/commented out everywhere.
Removing the file to reduce dead code.

https://claude.ai/code/session_01Jy51Ayk57fzaFuuFFm1j1K

* chore: remove NIP-C7 references from docs and code

- Remove nip-c7 from ChatProtocol type
- Remove commented NIP-C7 adapter imports and switch cases
- Update comments to reference NIP-29 instead of NIP-C7
- Update kind 9 renderer docs to reference NIP-29
- Clean up chat-parser docs and error messages

https://claude.ai/code/session_01Jy51Ayk57fzaFuuFFm1j1K

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-28 20:29:11 +01:00
Alejandro Gómez
f987ec7705 fix: increase menu button size on mobile 2026-01-28 12:28:37 +01:00
Alejandro Gómez
ee19cdb8fe fix: use dynamic viewport sizes 2026-01-28 12:28:12 +01:00
Alejandro
2ad3f90174 Improve mobile UX with larger touch targets (#223)
- Add useIsMobile hook for viewport detection
- TabBar: larger height (48px), disable reorder on mobile, hide workspace numbers
- Header buttons: larger touch targets for SpellbookDropdown, UserMenu, LayoutControls
- Window toolbar: larger buttons (40px) on mobile
- Mosaic dividers: wider (12px) on mobile for easier dragging
- CommandLauncher: larger items, footer text, hide kbd hints on mobile
- Editor suggestions: responsive widths, min-height 44px touch targets
- EventFooter: larger kind/relay buttons on mobile
- CodeCopyButton: larger padding and icon on mobile
- MembersDropdown: larger trigger and list items on mobile

All changes use mobile-first Tailwind (base styles for mobile, md: for desktop)
to meet Apple HIG 44px minimum touch target recommendation.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 12:19:31 +01:00
Alejandro
a13b990e31 Fix tooltip contrast in editor suggestion lists (#222)
Use text-popover-foreground instead of text-muted-foreground for
suggestion list components to ensure proper contrast with bg-popover
background across all themes.

https://claude.ai/code/session_0145zLfku7idq3WEqGzvTdvu

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-28 11:35:58 +01:00
Alejandro
3282581636 Add follow mode (-f) to REQ command (#220)
* feat: add -f (follow) option to req command

Add tail -f style auto-refresh behavior to the req command. When enabled,
new events are automatically displayed instead of being buffered behind
a "X new events" button.
2026-01-27 13:47:19 +01:00
Alejandro
d69cc1fec6 Migrate from Tailwind CSS v3 to v4 (#219)
- Replace JS config (tailwind.config.js) with CSS-first @theme directive
- Add @tailwindcss/vite plugin for improved Vite integration
- Update src/index.css with v4 syntax (@import, @theme, @utility)
- Convert @layer utilities to @utility syntax
- Fix hardcoded scrollbar colors in command-launcher.css
- Add Tailwind v4 skill document (.claude/skills/tailwind-v4.md)
- Update CLAUDE.md with Tailwind v4 quick reference

https://claude.ai/code/session_01T6RenqDof8br6Nt9aKcjvq

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-27 11:24:35 +01:00
Alejandro
34bad20ce9 Fix logo SVG centering and regenerate icons (#218)
The SVG path extended slightly beyond the viewBox (bezier control points
reached x=121.464 while viewBox ended at x=121), while the left edge was
flush at x=0. This caused uneven spacing.

Changed viewBox from "0 0 121 160" to "-0.5 0 122 160" to add equal
0.5px margins on both sides, properly centering the logo content.

https://claude.ai/code/session_019PGCQHRovoNE81udohhU3R

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-27 11:07:43 +01:00
Alejandro
a28ffc1ec3 fix: filter invalid relay URLs from event tags (#217)
* fix: filter invalid relay URLs from event tags

Add validation to prevent invalid URLs from being used as relay hints.
The issue occurred when "r" tags containing non-relay URLs (like
https://(strangelove@basspistol.org/) were being extracted and used
as relay connection targets.

Changes:
- Add isValidRelayURL() helper to validate relay URLs (must have ws://
  or wss:// protocol and valid URL structure)
- Update extractRelayContext() in loaders.ts to filter r-tags, e-tag
  relay hints, and a-tag relay hints using the new validator
- Add comprehensive tests for isValidRelayURL()

https://claude.ai/code/session_01Ca2fKD2r4wHKRD8rcRohj9

* refactor: use applesauce isSafeRelayURL for relay URL validation

Refactor relay URL validation to use applesauce's isSafeRelayURL helper
which provides a fast regex-based check for valid websocket URLs.

Changes:
- Update isValidRelayURL in relay-url.ts to use isSafeRelayURL as fast
  path, with URL constructor fallback for IP addresses
- Re-export isSafeRelayURL from relay-url.ts for convenience
- Update loaders.ts to use isSafeRelayURL directly from applesauce
- Add relay URL validation to:
  - nostr-utils.ts: getEventPointerFromQTag (q-tag relay hints)
  - zapstore-helpers.ts: getAppReferences (a-tag relay hints)
  - nip89-helpers.ts: getHandlerReferences (a-tag relay hints)
  - PublicChatsRenderer.tsx: extractGroups (group relay URLs)

This ensures consistent validation across all relay URL extraction points
using applesauce's battle-tested validation.

https://claude.ai/code/session_01Ca2fKD2r4wHKRD8rcRohj9

* chore: remove unused isSafeRelayURL re-export

The re-export was added but all consumers import directly from
applesauce-core/helpers/relays instead.

https://claude.ai/code/session_01Ca2fKD2r4wHKRD8rcRohj9

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-27 10:59:26 +01:00
Alejandro
3f3ebcf5f6 Fix duplicate client tag in spell encoding (#215)
The client tag was being added twice to spells:
1. Unconditionally in encodeSpell() in spell-conversion.ts
2. Conditionally in publish-spell.ts based on user settings

Removed the unconditional addition in encodeSpell() so the client tag
is only added once by publish-spell.ts when the user setting is enabled.

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-26 12:44:42 +01:00
Alejandro
569388c135 Add PWA icon generation and Grimoire logo component (#214)
* feat: add Grimoire logo SVG and generate PWA assets

- Add logo.svg with the official Grimoire logo and gradient
- Create GrimoireLogo React component for use in the app
- Add scripts/generate-pwa-icons.mjs to generate all PWA icons from SVG
- Regenerate all favicon and PWA icons from the new logo
- Update mobile welcome screen to show the logo instead of text

* feat: use transparent backgrounds for PWA icons and add theme gradient option

- Update generate-pwa-icons.mjs to output PNGs with transparent backgrounds
- Add gradient prop to GrimoireLogo component ("original" or "theme")
- Theme gradient matches text-grimoire-gradient CSS (yellow -> orange -> purple -> cyan)
- Mobile welcome screen now uses theme gradient to match ASCII art

* feat: use original gradient for mobile welcome logo

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-26 11:46:17 +01:00
Alejandro
d1ccd930ff Fix RichText prop in Kind9802Renderer to pass event object (#213)
* fix: pass source event to RichText in highlight feed preview

The source event preview in HighlightRenderer was only passing the
content string to RichText, which meant custom emoji tags from the
source event weren't processed. Now passes the source event with
the preview content to enable proper emoji and tag-based rendering.

* refactor: use CSS truncation for highlight source preview

- Pass sourceEvent directly for notes instead of extracting content
- Only create synthetic event with title for articles
- CSS line-clamp-1 and overflow-hidden handle truncation
- Media and event embeds remain disabled

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-24 10:17:46 +01:00
Alejandro
e008d76021 feat: NIP-34 status events (#209)
* feat(nip34): Add NIP-34 issue status renderers and locale-aware formatting

- Add IssueStatusRenderer for feed view (kinds 1630-1633: Open/Resolved/Closed/Draft)
- Add IssueStatusDetailRenderer for detail view with status badge and embedded issue
- Update IssueRenderer/IssueDetailRenderer to fetch and display current issue status
- Status validation respects issue author, repo owner, and maintainers
- Add status helper functions to nip34-helpers.ts (getStatusType, findCurrentStatus, etc.)
- Use parseReplaceableAddress from applesauce-core for coordinate parsing
- Expand formatTimestamp utility with 'long' and 'datetime' styles
- Fix locale-aware date formatting across all detail renderers
- Update CLAUDE.md with useLocale hook and formatTimestamp documentation

https://claude.ai/code/session_01C6Lty4k9pKxdwnYUCcpzV2

* refactor(nip34): Use theme semantic colors for issue status

Replace hardcoded colors with theme semantic colors:
- Resolved/merged: accent (positive)
- Closed: destructive (negative)
- Draft: muted
- Open: neutral foreground

Also fixes import placement in nip34-helpers.ts.

https://claude.ai/code/session_01C6Lty4k9pKxdwnYUCcpzV2

* fix(nip34): Use repository relays instead of AGGREGATOR_RELAYS

Status events for issues are now fetched from the relays configured
in the repository definition, not from hardcoded aggregator relays.

This respects the relay hints provided by repository maintainers for
better decentralization and reliability.

https://claude.ai/code/session_01C6Lty4k9pKxdwnYUCcpzV2

* perf(nip34): Add memoization caching to helper functions

Use getOrComputeCachedValue from applesauce-core to cache computed
values on event objects. This prevents redundant computation when
helpers are called multiple times for the same event.

Also added documentation in CLAUDE.md about best practices for
writing helper libraries that compute data from Nostr events.

https://claude.ai/code/session_01C6Lty4k9pKxdwnYUCcpzV2

* fix(nip34): Add relay fallback chain for status event fetching

Status events now use a fallback chain for relay selection:
1. Repository configured relays (from "relays" tag)
2. Repo author's outbox relays (from kind:10002)
3. AGGREGATOR_RELAYS as final fallback

This ensures status events can be fetched even when repository
doesn't have relays configured.

https://claude.ai/code/session_01C6Lty4k9pKxdwnYUCcpzV2

* feat(nip34): Add status rendering to Patch and PR renderers

- PatchRenderer and PatchDetailRenderer now show merge/closed/draft status
- PullRequestRenderer and PullRequestDetailRenderer now show merge/closed/draft status
- Status events fetched from repository relays with author outbox fallback
- For patches and PRs, kind 1631 displays as "merged" instead of "resolved"
- Fixed destructive color contrast in dark theme (30.6% -> 50% lightness)

https://claude.ai/code/session_01C6Lty4k9pKxdwnYUCcpzV2

* refactor(nip34): Extract StatusIndicator component, improve UI layout

- Create reusable StatusIndicator component for issues/patches/PRs
- Move status icon next to status text in feed renderers (not title)
- Place status badge below title in detail renderers
- Fix dark theme destructive color contrast (0 90% 65%)
- Remove duplicate getStatusIcon/getStatusColorClass functions

https://claude.ai/code/session_01C6Lty4k9pKxdwnYUCcpzV2

* fix(nip34): Make status badge width fit content

https://claude.ai/code/session_01C6Lty4k9pKxdwnYUCcpzV2

* fix(theme): Improve destructive color contrast on dark theme

Increase lightness from 65% to 70% for better readability.

https://claude.ai/code/session_01C6Lty4k9pKxdwnYUCcpzV2

* fix(theme): Use lighter coral red for destructive on dark theme

Changed to 0 75% 75% (~#E89090) for better contrast against #020817 background.

https://claude.ai/code/session_01C6Lty4k9pKxdwnYUCcpzV2

* docs: Fix applesauce helper documentation in CLAUDE.md

- Fix parseCoordinate -> parseReplaceableAddress (correct function name)
- Clarify getTagValue (applesauce) vs getTagValues (custom Grimoire)
- Add getOrComputeCachedValue to helpers list
- Improve code example with proper imports and patterns

https://claude.ai/code/session_01C6Lty4k9pKxdwnYUCcpzV2

* fix(nip34): Render status event content as rich text

Use MarkdownContent component for status event content in
Issue, Patch, and PR detail renderers.

https://claude.ai/code/session_01C6Lty4k9pKxdwnYUCcpzV2

* fix(nip34): Smaller status indicators, improve issue feed layout

- Use shared StatusIndicator in IssueStatusRenderer (smaller size)
- Render status event content as markdown
- Put status on its own line between title and repo in IssueRenderer

https://claude.ai/code/session_01C6Lty4k9pKxdwnYUCcpzV2

* fix(nip34): Use warning color for closed status instead of destructive

- Change closed status from red (destructive) to orange (warning)
- Improve dark theme status colors contrast (warning: 38 92% 60%)
- Less aggressive visual for closed issues/patches/PRs

https://claude.ai/code/session_01C6Lty4k9pKxdwnYUCcpzV2

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-23 15:31:20 +01:00
Alejandro Gómez
85ab0a1587 fix: add a-tag relays to event resolution 2026-01-23 14:58:21 +01:00
Alejandro
9b36120dfe Add NIP-56 Report (Kind 1984) renderer and helpers (#210)
* Add NIP-56 Report renderer (kind 1984)

- Add nip56-helpers.ts with report parsing and type definitions
- Add ReportRenderer for displaying report events in feeds
- Support profile, event, and blob report targets
- Display report type with color-coded badges and icons
- Show embedded reported event when available

https://claude.ai/code/session_012ux81GyM8iZ1GLnKHC7esJ

* refactor(nip56): apply applesauce caching and neutral styling

- Use getOrComputeCachedValue for report parsing (applesauce pattern)
- Rename parseReport to getReportInfo for consistency
- Use muted/neutral colors for all report type icons and badges
- Use QuotedEvent component for embedding reported events
- Remove unnecessary useMemo (helper caches internally)

https://claude.ai/code/session_012ux81GyM8iZ1GLnKHC7esJ

* refactor(nip56): use collapsed quote and cleaner copy

- Use "Reported <username> for <reason>" format
- Remove redundant "Event by:" line for event reports
- Use depth=2 for QuotedEvent to show collapsed by default
- Content may be disturbing, user can expand on demand

https://claude.ai/code/session_012ux81GyM8iZ1GLnKHC7esJ

* feat(nip56): hide preview and clickable header

- Add hidePreview prop to QuotedEvent for sensitive content
- Hide text preview when collapsed, show "Click to reveal content"
- Make report header clickable to open report detail
- UserName stops propagation so clicking username opens profile

https://claude.ai/code/session_012ux81GyM8iZ1GLnKHC7esJ

* style(nip56): use dotted underline hover, reduce spacing

- Remove background highlight on hover
- Use underline dotted with cursor crosshair (consistent with app)
- Reduce gap between header and quoted event

https://claude.ai/code/session_012ux81GyM8iZ1GLnKHC7esJ

* refactor(nip56): use RichText for comment and applesauce helpers

- Render report comments using RichText like kind 1 notes
- Use getTagValue/getTagValues helpers instead of direct tag access
- Add explanatory comments where direct tag access is still required

https://claude.ai/code/session_012ux81GyM8iZ1GLnKHC7esJ

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-23 13:53:57 +01:00
Alejandro
7838b0ab98 Add NIP-88 Poll support with renderers and helpers (#207)
* feat(nip88): add poll event renderers

Implement NIP-88 poll events support with:
- PollRenderer: Feed view for kind 1068 polls showing question, options, type
- PollDetailRenderer: Detail view with live vote counts and percentages
- PollResponseRenderer: Feed view for kind 1018 showing voted options
- nip88-helpers: Utilities for parsing poll data and counting votes

* fix(nip88): use RelayLink component and differentiate poll type icons

- Use ListCheck icon for single choice, ListChecks for multi choice
- Replace relay text spans with clickable RelayLink components
- Shorten "Vote on these relays" to "Relays"

* refactor(nip88): production readiness improvements

- Add symbol-based caching to all helper functions using getOrComputeCachedValue
- Use QuotedEvent component for embedded polls in PollResponseRenderer
- Simplify PollResponseRenderer by leveraging QuotedEvent's loading states
- Add clear documentation about what can/cannot be cached

* fix(nip88): restore option label resolution in PollResponseRenderer

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-22 22:39:31 +01:00
Alejandro
459159faca feat(kinds): add search box to KINDS command (#206)
Add a search input to KindsViewer matching the style from NipsViewer.
Users can now filter kinds by number, name, or description. Supports
autofocus on mount, clear button, and Escape to clear.

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-22 22:10:30 +01:00
Alejandro
4e8a8a0e90 feat(chat): make input editor expandable up to 3 lines (#204)
The chat input now grows as you type, from 1 line up to approximately
3 lines before showing a scrollbar. This improves UX when composing
longer messages by providing better visibility of the content.

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-22 20:53:01 +01:00
Alejandro Gómez
0ebe1ec3da ai: agents file 2026-01-22 18:38:26 +01:00
Alejandro Gómez
32d584090f fix: unused import 2026-01-22 18:36:06 +01:00