Commit Graph

3385 Commits

Author SHA1 Message Date
Lemon 2558743dae Deduplicate buildKindOptions, AVAILABLE_FONTS, and sidebar editing logic into shared modules 2026-04-14 11:28:54 -07:00
Lemon 645560077f Remove dead code: SYSTEM_PROMPT, isAuthenticated, DORK_ANIMATION/DorkThinking aliases, NsfwPolicy type, pointless variable aliases in LeftSidebar/MobileDrawer 2026-04-14 11:28:54 -07:00
Lemon aaa69912c0 Fix bugs: error swallowing in handleAPIError, stale cache in resetBuddy, Set allocation per event in matchesFilters; remove dead code (sendChatMessage, parseSelectedKinds, TabFilter import); restore MobileSearchSheet open prop 2026-04-14 11:28:54 -07:00
Lemon 42be4051e4 Add make_it_rain AI tool: app-wide rain/snow screen effects as easter egg 2026-04-14 11:28:54 -07:00
Lemon 30043b1b1f Add asset_urls param to create_webxdc: bundle remote binary files (ROMs, images, audio, WASM) into .xdc archives 2026-04-14 11:28:54 -07:00
Lemon 14dd050386 Add multi-file support to create_webxdc: accept a files map for multi-file .xdc archives 2026-04-14 11:28:54 -07:00
Lemon a0ac403618 Improve webxdc AI prompts: add gamepad keybindings, sandbox capabilities, and input guidance 2026-04-14 11:28:54 -07:00
Lemon 2cd0387322 Add ox tag, optional image_url param to create_webxdc for app icon thumbnails 2026-04-14 11:28:54 -07:00
Lemon a54b5a6193 Add create_webxdc tool: AI buddy can build and publish WebXDC apps from scratch 2026-04-14 11:28:54 -07:00
Lemon ddd4dd7660 Generalize upload_from_url for any file type; add WebXDC publishing to buddy system prompt 2026-04-14 11:28:54 -07:00
Lemon 4ff1738aa6 Fix buddy sheet dismiss: tap empty space between messages to close
Move stopPropagation from the messages wrapper div to individual message
bubbles, so tapping gaps between messages propagates to the outer onClose
handler.
2026-04-14 11:28:54 -07:00
Lemon 4dfa978286 Show buddy avatar in mobile bottom nav when profile picture exists
Fetches the buddy's kind 0 metadata via useAuthor. When the buddy has a
profile picture, renders a shaped Avatar (matching the profile tab pattern)
with the buddy's name as the label. Falls back to the default Bot icon
with 'Buddy' text when no picture is set.
2026-04-14 11:28:53 -07:00
Lemon 6caf9ea668 Add create_spell vs get_feed decision guide to buddy system prompt
create_spell is a write operation (creates persistent feed for user to browse),
get_feed is a read operation (fetches content for the AI to summarize).
Added decision table, key signal words, and default-to-get_feed guidance.
2026-04-14 11:28:53 -07:00
Lemon ece1253903 Replace search icon with bot icon; make dork button interactive
- Swap Search (magnifying glass) for Bot icon in the buddy input bar
- Dork button no longer closes the sheet
- When streaming: clicking stops generation (like Escape)
- When idle: clicking advances the animation by one frame
2026-04-14 11:28:53 -07:00
Lemon 3a91073dbd Remove duplicate thinking animation from buddy chat pane
The dork animation was showing both in the messages area (BuddyThinking)
and in the input bar. Keep only the input bar animation.
2026-04-14 11:28:53 -07:00
Lemon d37345fa75 Fix blockquote text and border color in AI chat bubbles
Blockquote text was using the prose default color which is nearly
invisible on dark theme backgrounds. Override with muted-foreground
for the text and the theme border color for the left accent line.
2026-04-14 11:28:53 -07:00
Lemon 9ab186fd70 Fix AI chat: wrap long strings (npub) and use primary color for inline code
- Add break-words to the prose container so long unbroken strings like
  npub identifiers wrap instead of overflowing the bubble
- Set prose-code:text-primary so inline code uses the theme accent color
  instead of the prose default (which was dark-on-dark in dark themes)
- Remove prose backtick decorations around inline code elements
2026-04-14 11:28:53 -07:00
Lemon 31ee06dd3b Fix buddy sheet close-on-background-tap using stopPropagation
The previous e.target===e.currentTarget approach didn't work because the
scroll container's padding and gap areas are still within the element's
box. Instead, put onClick={onClose} on the outer fixed wrapper and
stopPropagation on the two interactive children (message content wrapper
and input bar). Clicks on dead space (padding, gaps, empty scroll area)
now bubble up to the outer div and close the sheet.
2026-04-14 11:28:53 -07:00
Lemon 9ff991671b Keep bottom nav visible when sheets are open; close buddy on background tap
- Bottom nav no longer hides when search/buddy sheets open, so the user
  can see the active tab and switch between them
- Buddy sheet closes when tapping empty background area (outside messages
  and input bar) via target===currentTarget check
- Search sheet positioned above the nav bar (bottom-mobile-nav) instead
  of bottom-0 since the nav is now visible
- Removed duplicate backdrop from MobileSearchSheet (parent provides it)
2026-04-14 11:28:53 -07:00
Lemon 08991a3ee5 Separate search and buddy into independent bottom nav sheets
The search sheet and buddy sheet were previously coupled — buddy was a
toggle mode inside the search sheet. Now they are fully independent:

- Search (Discover) tab opens the search sheet with no AI toggle
- Buddy (ai-chat) tab opens the buddy chat overlay directly
- Tapping one closes the other; backdrop closes both
- Removed buddyMode/onToggleBuddy plumbing from MobileSearchSheet
- Removed onToggleBuddy from MobileBuddySheet (close button instead)
- Removed the <[o_o]> toggle from the search input bar
2026-04-14 11:28:53 -07:00
Lemon 1b484df7c6 Fix missing ActivityCard export by inlining the markup
ActivityCard was removed from NoteCard.tsx in e7a17d3f but the
PostDetailPage poll vote detail view still imported it, causing a
runtime crash when visiting profile pages. Inline the equivalent
article/flex layout directly.
2026-04-14 11:28:53 -07:00
Lemon c9ed33b71f Inject logged-in user identity into AI chat system prompt
The buddy AI now knows who it's talking to via a {{USER_IDENTITY}} block
in the system prompt containing the user's npub, hex pubkey, display name,
NIP-05, and bio. This lets the AI answer questions like 'who am I?' or
'what's my npub?' directly, and guides it to use fetch_event/get_feed for
deeper profile or post lookups.
2026-04-14 11:28:53 -07:00
Lemon 5c051f0407 Add missing @modelcontextprotocol/sdk dependency 2026-04-14 11:28:53 -07:00
Lemon 5263b1fd01 Remove legacy fallback from parseProfileTabs 2026-04-14 11:28:53 -07:00
Lemon 07c03b1a37 Hide 'Edit sidebar' button when user is not logged in 2026-04-14 11:28:53 -07:00
Lemon 00252d7ab1 Unify sidebar hover styling: hover:bg-secondary/40 on outer pill for all items 2026-04-14 11:28:53 -07:00
Lemon b317fdb566 Sidebar UX: bottom nav shows first 4 sidebar items, More/Less inline toggle replacing popover, Edit/Done buttons inside sidebar, +/x buttons for items above/below More separator, Add link input for URLs/npub/iso3166/isbn 2026-04-14 11:28:53 -07:00
Lemon 0be2428678 Add get_feed tool for AI chat to read and summarize feeds
Adds a new get_feed tool that lets the AI assistant read posts from any
feed source and summarize activity for the user. Supports:

- Named feeds: follows, global, ditto, and user's saved feeds
- Country feeds: NIP-73 geographic comments via iso3166 codes
- Ad-hoc queries: kinds, authors, search, hashtags via spell resolution

The system prompt is now dynamic, injecting saved feed labels so the
model knows which feeds are available. buildSystemPrompt accepts an
optional savedFeedLabels parameter for this.
2026-04-14 11:28:53 -07:00
Lemon 9941d67810 Use buddy key for Blossom uploads in upload_from_url tool
Create NSecSigner from buddy's secret key for Blossom upload auth.
Falls back to user's signer when no buddy exists. Calls
BlossomUploader directly instead of going through useUploadFile hook,
using the same server config and timeout pattern.
2026-04-14 11:28:53 -07:00
Lemon c5f659eb0e Add publish_events and fetch_event tools ported from Shakespeare
publish_events: Publishes Nostr events signed by the buddy's key
(falls back to ephemeral when no buddy). Supports any event kind
with content and tags. Returns published events for inline display.
The agent only publishes when explicitly instructed by the user.

fetch_event: Fetches Nostr events by NIP-19 identifier (npub, note,
nevent, naddr, nprofile). Uses the existing nostr pool connection.
Allows the agent to read content the user references in chat.

Both tools adapted from Shakespeare's implementations to fit Ditto's
hook-based tool pattern. System prompt updated with documentation
for both new tools.
2026-04-14 11:28:53 -07:00
Lemon e01e266c31 Sign spells and emoji packs with buddy key instead of ephemeral key
When a buddy is configured, create_spell and create_emoji_pack tools
now sign events with the buddy's persistent keypair. This gives the
buddy a consistent Nostr identity across all created content.

Falls back to ephemeral keys when no buddy exists (preserving current
behavior). Ephemeral key profiles ('Dork Spellcaster', 'Dork Emoji
Maker') are only published in the fallback path since the buddy
already has its own kind 0 profile.
2026-04-14 11:28:53 -07:00
Lemon 48e8ee1de9 Show raw {{NAME}}/{{SOUL}} placeholders in system prompt settings
Settings textarea was showing the resolved prompt (with 'Dork' and
empty soul) because SYSTEM_PROMPT called buildSystemPrompt() which
substituted placeholders immediately.

Fix: export DEFAULT_SYSTEM_PROMPT_TEMPLATE as the raw template string
with {{NAME}} and {{SOUL}} intact. Settings UI displays this so users
can see and edit the actual placeholders. Substitution only happens
at chat time in buildSystemPrompt().

Also removes 'When you use a tool, briefly describe what you created'
-- personality directives belong in the soul, not the base template.
2026-04-14 11:28:53 -07:00
Lemon 6069811372 Rewrite system prompt to use {{NAME}}/{{SOUL}} placeholders throughout
The system prompt is now purely functional — identity and personality
are entirely governed by {{NAME}} and {{SOUL}} placeholders that get
replaced at runtime with the buddy's configured values.

Changes:
- Remove hardcoded 'You are Dork' intro and 'Be concise and friendly'
- Template starts with 'You are {{NAME}}' followed by {{SOUL}}
- Tool documentation reformatted with ## headers for clarity
- buildSystemPrompt() always does placeholder substitution (no
  separate code path for custom vs default)
- Soul textarea style matches system prompt (monospace, text-xs)
2026-04-14 11:28:53 -07:00
Lemon 203fe43ca9 Populate system prompt with default text and remove unnecessary icons
- System prompt textarea now shows the built-in default prompt
  instead of empty with placeholder, so users can directly edit it
- When text matches the default, stores empty string (no override)
- Reset button restores the default prompt text
- Remove Bot icon from Identity label
- Remove Server icon from MCP Servers label
2026-04-14 11:28:53 -07:00
Lemon 64c1cf3642 Add editable system prompt in Advanced Settings
Add aiSystemPrompt to AppConfig (interface, Zod schema, defaults)
for users to override the base system prompt template.

buildSystemPrompt() accepts optional customPrompt param. When set,
it replaces the entire default template while still substituting
{{NAME}} and {{SOUL}} placeholders.

Advanced Settings UI:
- System Prompt textarea below Soul (monospace, shows built-in
  default as placeholder when empty)
- Documents {{NAME}} and {{SOUL}} placeholder usage
- Reset to default button when custom prompt is active
- Saves on blur to AppConfig (persisted in localStorage)
2026-04-14 11:28:53 -07:00
Lemon 20e560a68c Add buddy soul editing and reset in Advanced Settings
Buddy section in Advanced Settings now shows:
- Identity info (name, npub) when buddy exists
- Soul textarea: pre-populated from buddy event, saves on blur
  via updateSoul mutation, re-encrypts and republishes kind 30078
- 'No buddy configured' hint when no buddy exists

Danger Zone now includes:
- Reset Buddy button: wipes localStorage nsec and publishes empty
  kind 30078 event to overwrite on relays
2026-04-14 11:28:29 -07:00
Lemon ff8898eecf Remove unused MobileDorkSheet.tsx (replaced by MobileBuddySheet) 2026-04-14 11:28:29 -07:00
Lemon 75e9f465c5 Rename Dork -> Buddy across sidebar, settings, and mobile UI
User-facing rename:
- Sidebar label: 'Dork' -> 'Buddy'
- AdvancedSettings section header and descriptions
- MobileSearchSheet/MobileBottomNav: dorkMode -> buddyMode
- MobileDorkSheet -> MobileBuddySheet (new file, uses buddy identity)
- Input placeholder: 'Ask Dork...' -> 'Ask {buddyName}...'
- AppContext JSDoc references

Internal rename:
- DorkThinking -> BuddyThinking (deprecated alias kept)
- DORK_ANIMATION -> BUDDY_ANIMATION (deprecated alias kept)

'Dork' is preserved only where it refers to the onboarding wizard
character (useBuddyOnboarding), the default no-buddy fallback persona
(aiChatSystemPrompt), and ephemeral key profiles (Dork Spellcaster).
2026-04-14 11:28:29 -07:00
Lemon b04be19f75 Add buddy onboarding flow and wire buddy identity into chat
Split AIChatPage into three views:
- Logged-out: login prompt
- No buddy: onboarding flow with Dork as static Clippy-like guide
- Buddy exists: full AI chat with buddy's name/soul in system prompt

Onboarding (useBuddyOnboarding):
- Scripted conversation state machine: intro -> name -> soul -> confirm
- Reuses existing MessageBubble components, no API calls
- Dork walks user through naming and describing their buddy's soul
- On confirmation, calls createBuddy() to generate keypair + publish

Chat mode (BuddyChatView):
- Passes buddyName and buddySoul to useAIChatSession
- Header shows buddy name instead of 'Dork'
- Empty state greetings use buddy name
- Input placeholder personalized to buddy name
2026-04-14 11:28:05 -07:00
Lemon dc9cba6651 Add name to buddy encrypted content as canonical source of truth
Move buddy name into the NIP-44 encrypted content alongside nsec
and soul. This avoids needing an extra kind 0 fetch to display the
buddy name on the chat page. The agent's kind 0 profile may use
nicknames, but the identity event name is authoritative.

- BuddySecrets: add required 'name' field
- BuddyIdentity: add 'name' field
- createBuddy: include name in encrypted payload
- updateSoul: preserve existing name when re-encrypting
- decryptSecrets: validate all three required fields
- NIP.md: document name in encrypted content schema
2026-04-14 11:28:05 -07:00
Lemon 09b15bce21 Make system prompt dynamic with {{SOUL}} and {{NAME}} placeholders
Refactor aiChatSystemPrompt.ts: convert static SYSTEM_PROMPT const
into buildSystemPrompt(name?, soul?) function. When a buddy is
configured, the agent name and soul text are injected into the
template. Falls back to 'Dork' persona when no buddy exists.

Update useAIChatSession to accept optional buddyName/buddySoul
params and build the system prompt dynamically via useMemo.
2026-04-14 11:28:04 -07:00
Lemon f6fc10324e Add useBuddy hook and NIP-78 buddy identity event schema
Introduce the foundation for user-created AI agent (Buddy) identities:

- useBuddy hook: manages buddy lifecycle (create, update soul, reset)
  - Generates agent keypair, caches nsec in localStorage
  - Publishes kind 30078 identity event (NIP-78) signed by owner
  - Publishes kind 0 profile for the agent with bot:true
  - NIP-44 encrypts nsec + soul to owner for relay backup
  - Restores from relay if localStorage is cleared

- NIP.md: document kind 30078 buddy event schema
  - Public tags: d-tag, p-tag (agent pubkey), alt, client
  - Encrypted content: nsec + soul
  - Agent has separate kind 0 profile
2026-04-14 11:28:04 -07:00
Lemon 3a742c19f7 Use ephemeral key for create_emoji_pack and return event for inline display
Match create_spell pattern: sign with ephemeral keypair, publish a
profile for the key, and return nostrEvent so the emoji pack card
renders inline in the Dork chat.
2026-04-14 11:28:04 -07:00
Lemon f8c5316eed Add fetch_page, upload_from_url, and create_emoji_pack tools to Dork
Dork can now fetch web pages, download and upload images to Blossom,
and publish NIP-30 emoji packs. This enables the workflow:
'download these emojis from <url> and make an emoji set'

- fetch_page: fetches URL via CORS proxy, extracts image URLs from HTML
- upload_from_url: downloads images and uploads to Blossom servers
- create_emoji_pack: publishes kind 30030 emoji pack as the user
2026-04-14 11:28:04 -07:00
Lemon fcc6d79bb7 Add MCP client support for Dork AI tool discovery
Dork can now connect to Streamable HTTP MCP servers to discover and
use external tools at runtime. This bridges the gap between dev-time
MCP configs (.mcp.json) and the production AI chat system.

- MCPClient class using @modelcontextprotocol/sdk for full spec support
- useMCPTools hook with TanStack Query caching (5-min stale time)
- MCP tools merged into Dork's tool array and routed through MCPClient
- Settings UI in Advanced Settings to add/remove MCP servers
- MCPServer type, Zod schema, and AppConfig integration
2026-04-14 11:28:04 -07:00
Lemon 57f1c912e0 Fix profile tabs disappearing after spell migration for users with legacy tab events 2026-04-14 11:27:41 -07:00
Lemon 1774741678 Stream Dork AI responses in real-time instead of waiting for full completion 2026-04-14 11:27:41 -07:00
Lemon 20d0594c47 Use useFeed for Packs tab to get full events with images 2026-04-14 11:27:41 -07:00
Lemon dc66420cce Add Packs tab to Discover page between Feeds and Posts 2026-04-14 11:27:41 -07:00
Lemon d350f4f271 Update spell card to content-first layout matching follow pack design 2026-04-14 11:27:41 -07:00