Add vitepress docs

This commit is contained in:
Ticruz
2025-02-04 14:43:40 +01:00
committed by Jon Staab
parent 43255bcb74
commit 94375a56ec
84 changed files with 10821 additions and 139 deletions
+66
View File
@@ -0,0 +1,66 @@
# @welshman/content
`@welshman/content` is a comprehensive content processing library designed specifically for Nostr applications.
It provides a robust system for parsing, processing, and rendering Nostr content while handling various special formats and entities common in the Nostr ecosystem.
## Core Concepts
The package is built around two main components:
1. **Parser**: Transforms raw content into structured elements
```typescript
const parsed = parse({
content: "Hello #nostr, check nostr:npub1...",
tags: [["p", "pubkey123"]]
})
```
2. **Renderer**: Converts parsed content into desired output format
```typescript
const html = renderAsHtml(parsed).toString()
const text = renderAsText(parsed).toString()
```
## Common Use Cases
- Rendering Nostr notes with proper entity linking
- Processing and displaying user content safely
- Handling rich text content in Nostr clients
- Converting between different content formats
- Creating customized content displays
## Quick Example
```typescript
import { parse, renderAsHtml, truncate } from '@welshman/content'
// Parse and process content
const parsed = parse({
content: `
Hello #nostr!
Check out this note: nostr:note1...
https://example.com/image.jpg
`,
tags: [["p", "pubkey123"]]
})
// Truncate if needed
const truncated = truncate(parsed, {
maxLength: 500,
mediaLength: 150
})
// Render as HTML
const html = renderAsHtml(truncated, {
entityBase: "https://your-app.com/"
}).toString()
```
## Installation
```bash
npm install @welshman/content
```
This package is essential for applications that need to handle Nostr content in a structured and safe way, providing all the necessary tools for parsing, processing, and rendering Nostr-specific content formats.
+181
View File
@@ -0,0 +1,181 @@
# Content Parser
The content parser system in `@welshman/content` provides a powerful way to parse Nostr content into structured elements.
It handles various types of content including Nostr entities, links, code blocks, and special formats.
## Content Types
### Basic Types
```typescript
enum ParsedType {
Text = "text", // Plain text
Newline = "newline", // Line breaks
Topic = "topic", // Hashtags (#nostr)
Code = "code", // Code blocks (inline and multi-line)
Link = "link", // URLs
LinkGrid = "link-grid" // Grid of media links
}
```
### Nostr-specific Types
```typescript
enum ParsedType {
Event = "event", // Nostr events (note1/nevent1)
Profile = "profile", // Profiles (npub1/nprofile1)
Address = "address", // Addresses (naddr1)
}
```
### Special Format Types
```typescript
enum ParsedType {
Cashu = "cashu", // Cashu tokens
Invoice = "invoice", // Lightning invoices
Ellipsis = "ellipsis" // Truncation marker
}
```
## Parsing Content
### Main Parser
```typescript
const parse = ({
content = "",
tags = []
}: {
content?: string
tags?: string[][]
}) => Parsed[]
// Example
const parsed = parse({
content: "Hello #nostr, check nostr:npub1...",
tags: [["p", "pubkey123"]]
})
```
### Available Parsers
The system includes specialized parsers for each content type:
```typescript
// Nostr Entities
parseAddress(text: string, context: ParseContext): ParsedAddress | void
parseEvent(text: string, context: ParseContext): ParsedEvent | void
parseProfile(text: string, context: ParseContext): ParsedProfile | void
// Code Blocks
parseCodeBlock(text: string, context: ParseContext): ParsedCode | void
parseCodeInline(text: string, context: ParseContext): ParsedCode | void
// Special Formats
parseCashu(text: string, context: ParseContext): ParsedCashu | void
parseInvoice(text: string, context: ParseContext): ParsedInvoice | void
// Basic Content
parseLink(text: string, context: ParseContext): ParsedLink | void
parseNewline(text: string, context: ParseContext): ParsedNewline | void
parseTopic(text: string, context: ParseContext): ParsedTopic | void
```
## Content Processing
### Truncation
```typescript
type TruncateOpts = {
minLength?: number // Minimum content length (default: 500)
maxLength?: number // Maximum content length (default: 700)
mediaLength?: number // Length value for media items (default: 200)
entityLength?: number // Length value for entities (default: 30)
}
const truncate = (
content: Parsed[],
options?: TruncateOpts
) => Parsed[]
// Example
const truncated = truncate(parsed, {
maxLength: 1000,
mediaLength: 150
})
```
### Link Processing
```typescript
// Consolidate consecutive image links into grids
const reduceLinks = (content: Parsed[]) => Parsed[]
// Example
const processed = reduceLinks(parsed)
```
## Type Guards
```typescript
// Basic content
isText(parsed: Parsed): parsed is ParsedText
isNewline(parsed: Parsed): parsed is ParsedNewline
isCode(parsed: Parsed): parsed is ParsedCode
isTopic(parsed: Parsed): parsed is ParsedTopic
// Links and media
isLink(parsed: Parsed): parsed is ParsedLink
isImage(parsed: Parsed): parsed is ParsedLink
isLinkGrid(parsed: Parsed): parsed is ParsedLinkGrid
// Nostr entities
isEvent(parsed: Parsed): parsed is ParsedEvent
isProfile(parsed: Parsed): parsed is ParsedProfile
isAddress(parsed: Parsed): parsed is ParsedAddress
// Special formats
isCashu(parsed: Parsed): parsed is ParsedCashu
isInvoice(parsed: Parsed): parsed is ParsedInvoice
isEllipsis(parsed: Parsed): parsed is ParsedEllipsis
```
## Complete Example
```typescript
// Parse content with tags
const parsed = parse({
content: `
Hello #nostr!
Check out this note: nostr:note1...
And this profile: nostr:npub1...
Some code: \`console.log("hello")\`
https://example.com/image.jpg
https://example.com/image2.jpg
`,
tags: [
["p", "pubkey123"],
["e", "event456"]
]
})
// Process the content
const processed = reduceLinks(parsed)
// Truncate if needed
const final = truncate(processed, {
maxLength: 500,
mediaLength: 150
})
// Check types and handle accordingly
final.forEach(item => {
if (isImage(item)) {
// Handle image
} else if (isProfile(item)) {
// Handle profile reference
} else if (isCode(item)) {
// Handle code block
}
})
```
This parser system provides a robust foundation for handling Nostr content, with support for various content types and processing needs. The type-safe approach ensures reliable content handling while maintaining flexibility for different use cases.
+195
View File
@@ -0,0 +1,195 @@
# Content Renderer
The renderer system in `@welshman/content` provides flexible ways to convert parsed content into text or HTML output. It includes customizable rendering options and specialized handlers for each content type.
## Renderer Class
```typescript
class Renderer {
constructor(readonly options: RenderOptions)
// Core methods
toString(): string
addText(value: string): void
addNewlines(count: number): void
addLink(href: string, display: string): void
addEntityLink(entity: string): void
}
```
## Render Options
```typescript
type RenderOptions = {
// String to use for newlines
newline: string
// Base URL for Nostr entities
entityBase: string
// Custom link rendering
renderLink: (href: string, display: string) => string
// Custom entity rendering
renderEntity: (entity: string) => string
}
```
## Built-in Renderers
### Text Renderer
```typescript
const textRenderOptions = {
newline: "\n",
entityBase: "",
renderLink: (href, display) => href,
renderEntity: (entity) => entity.slice(0, 16) + "…"
}
const textRenderer = makeTextRenderer({
// Override default options if needed
})
```
### HTML Renderer
```typescript
const htmlRenderOptions = {
newline: "\n",
entityBase: "https://njump.me/",
renderLink: (href, display) => {
const element = document.createElement("a")
element.href = sanitizeUrl(href)
element.target = "_blank"
element.innerText = display
return element.outerHTML
},
renderEntity: (entity) => entity.slice(0, 16) + "…"
}
const htmlRenderer = makeHtmlRenderer({
// Override default options if needed
})
```
## Content Type Renderers
```typescript
// Basic content
renderText(p: ParsedText, r: Renderer): void
renderNewline(p: ParsedNewline, r: Renderer): void
renderCode(p: ParsedCode, r: Renderer): void
renderTopic(p: ParsedTopic, r: Renderer): void
// Links
renderLink(p: ParsedLink, r: Renderer): void
// Nostr entities
renderEvent(p: ParsedEvent, r: Renderer): void
renderProfile(p: ParsedProfile, r: Renderer): void
renderAddress(p: ParsedAddress, r: Renderer): void
// Special formats
renderCashu(p: ParsedCashu, r: Renderer): void
renderInvoice(p: ParsedInvoice, r: Renderer): void
renderEllipsis(p: ParsedEllipsis, r: Renderer): void
```
## Usage Examples
### Basic Text Rendering
```typescript
const parsed = parse({
content: "Hello #nostr, check nostr:npub1...",
tags: []
})
// Render as plain text
const text = renderAsText(parsed).toString()
// Render as HTML
const html = renderAsHtml(parsed).toString()
```
### Custom Rendering Options
```typescript
// Custom text renderer
const customText = renderAsText(parsed, {
entityBase: "nostr:",
renderEntity: (entity) => entity.slice(0, 8)
}).toString()
// Custom HTML renderer
const customHtml = renderAsHtml(parsed, {
entityBase: "https://example.com/",
renderLink: (href, display) => `<a class="custom-link" href="${href}">${display}</a>`,
renderEntity: (entity) => `<span class="entity">${entity}</span>`
}).toString()
```
### Rendering Individual Elements
```typescript
const renderer = makeHtmlRenderer()
// Render single element
renderOne({
type: ParsedType.Link,
value: {
url: new URL("https://example.com"),
meta: {},
isMedia: false
},
raw: "https://example.com"
}, renderer)
// Render multiple elements
renderMany([
{
type: ParsedType.Text,
value: "Hello ",
raw: "Hello "
},
{
type: ParsedType.Topic,
value: "nostr",
raw: "#nostr"
}
], renderer)
```
### Complete Example
```typescript
// Parse and process content
const parsed = parse({
content: `
Check out this profile: nostr:npub1...
Code example:
\`console.log("hello")\`
#nostr #bitcoin
https://example.com/image.jpg
`,
tags: []
})
// Create custom renderer
const renderer = makeHtmlRenderer({
entityBase: "https://example.com/",
renderLink: (href, display) => {
if (href.endsWith('.jpg')) {
return `<img src="${href}" alt="${display}">`
}
return `<a href="${href}">${display}</a>`
},
renderEntity: (entity) => {
return `<span class="entity">${entity.slice(0, 8)}</span>`
}
})
// Render content
const html = render(parsed, renderer).toString()
```
The renderer system provides a flexible way to output parsed content in various formats while maintaining control over the rendering process. Its modular design allows for easy customization and extension for specific application needs.