svelte-streamdown 2.3.0 → 2.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -273,7 +273,7 @@ The animation system works by:
273
273
 
274
274
  ### Animation Types
275
275
 
276
- Choose from five distinct animation styles:
276
+ Choose from 4 distinct animation styles:
277
277
 
278
278
  #### `fade`
279
279
 
@@ -283,10 +283,6 @@ A clean fade-in effect where text smoothly appears from transparent to opaque.
283
283
 
284
284
  Text starts slightly blurred and comes into focus while fading in, creating a smooth reveal effect.
285
285
 
286
- #### `typewriter`
287
-
288
- A typewriter effect where text appears character by character, mimicking the look of someone typing.
289
-
290
286
  #### `slideUp`
291
287
 
292
288
  Text slides up from below while fading in, creating a dynamic upward motion.
@@ -137,16 +137,6 @@
137
137
  }
138
138
  }
139
139
 
140
- @keyframes sd-typewriter {
141
- from {
142
- width: 0;
143
- overflow: hidden;
144
- }
145
- to {
146
- width: fit-content;
147
- }
148
- }
149
-
150
140
  @keyframes sd-slideUp {
151
141
  from {
152
142
  transform: translateY(10%);
@@ -105,7 +105,7 @@ export type StreamdownProps = {
105
105
  animation?: {
106
106
  animateOnMount?: boolean;
107
107
  enabled?: boolean;
108
- type?: 'fade' | 'blur' | 'typewriter' | 'slideUp' | 'slideDown';
108
+ type?: 'fade' | 'blur' | 'slideUp' | 'slideDown';
109
109
  duration?: number;
110
110
  timingFunction?: 'ease' | 'ease-in' | 'ease-out' | 'ease-in-out' | 'linear';
111
111
  tokenize?: 'word' | 'char';
@@ -10,7 +10,10 @@ export class StreamdownContext {
10
10
  animation-duration: ${this.animation.duration}ms;
11
11
  animation-timing-function: ${this.animation.timingFunction};
12
12
  animation-iteration-count: 1;
13
- animation-fill-mode: forwards;`
13
+ animation-fill-mode: forwards;
14
+ white-space: pre-wrap;
15
+ display: inline-block;
16
+ text-decoration: inherit;`
14
17
  : undefined);
15
18
  animationBlockStyle = $derived(this.animation.enabled
16
19
  ? `animation-name: sd-${this.animation.type};
@@ -4,6 +4,17 @@ const blockRule = /^(\$\$)(?:\n((?:\\[\s\S]|[^\\])+?)\n\1(?:\n|$)|([^$\n]+?)\1(?
4
4
  // Inline math: handles both single ($) and double ($$) dollar delimiters
5
5
  // Avoids matching currency by checking context and requiring proper content
6
6
  const inlineRule = /^(\${1,2})(?!\$)((?:[^$\n]|\\\$)*?)\1(?!\d)/;
7
+ // Enhanced currency detection patterns
8
+ const currencyPatterns = {
9
+ // Simple price patterns: $123, $123.45, $1,234.56
10
+ simplePrice: /^\d{1,3}(?:,\d{3})*(?:\.\d{2})?$/,
11
+ // Multiple prices or numbers: "123, 456", "123.45, 678.90", "123 or 456"
12
+ multipleNumbers: /^\d+(?:[.,]\d+)*(?:\s*[,;]\s*\d+(?:[.,]\d+)*)+$/,
13
+ // Price ranges: "123-456", "123 - 456", "123 to 456"
14
+ priceRange: /^\d+(?:\.\d{2})?\s*(?:-|to|or)\s*\d+(?:\.\d{2})?$/i,
15
+ // Common currency words nearby (check surrounding context)
16
+ currencyContext: /(?:price|cost|dollar|euro|pound|yen|currency|pay|buy|sell|expensive|cheap)/i
17
+ };
7
18
  export function markedMath() {
8
19
  return {
9
20
  extensions: [
@@ -40,11 +51,18 @@ export function markedMath() {
40
51
  const possibleMath = src.substring(currentIndex);
41
52
  // Check if this could be math (not currency)
42
53
  if (possibleMath.match(inlineRule)) {
43
- // Additional check: avoid currency patterns like $5.00
44
- const beforeChar = currentIndex > 0 ? src[currentIndex - 1] : '';
45
- const afterDollar = possibleMath.substring(1, 6); // Check first few chars after $
46
- // Skip if it looks like currency (digit immediately after $ or decimal pattern)
47
- if (!/^\d+(\.\d{2})?\s/.test(afterDollar)) {
54
+ const match = possibleMath.match(inlineRule);
55
+ if (match) {
56
+ const content = match[2];
57
+ const dollarCount = match[1]; // '$' or '$$'
58
+ // Only apply currency detection to single dollars
59
+ // Double dollars ($$) indicate explicit math intent
60
+ if (dollarCount === '$' && isCurrencyPattern(content, src, currentIndex)) {
61
+ // This looks like currency with single dollars, skip it
62
+ index += dollarIndex + 1;
63
+ searchSrc = src.substring(index);
64
+ continue;
65
+ }
48
66
  return currentIndex;
49
67
  }
50
68
  }
@@ -55,18 +73,19 @@ export function markedMath() {
55
73
  tokenizer(src) {
56
74
  const match = src.match(inlineRule);
57
75
  if (match) {
58
- // Additional validation: avoid currency patterns
59
76
  const content = match[2];
60
- if (/^\d+(\.\d{2})?$/.test(content.trim())) {
61
- // This looks like currency, skip it
77
+ const dollarCount = match[1]; // '$' or '$$'
78
+ const isDisplayMode = dollarCount === '$$';
79
+ // Only apply currency detection to single dollars
80
+ // Double dollars ($$) indicate explicit math intent
81
+ if (dollarCount === '$' && isCurrencyPattern(content, src, 0)) {
82
+ // This looks like currency with single dollars, skip it
62
83
  return;
63
84
  }
64
- // Double dollars are display mode, single dollars are inline
65
- const isDisplayMode = match[1] === '$$';
66
85
  return {
67
86
  type: 'math',
68
- isInline: !isDisplayMode,
69
- displayMode: isDisplayMode,
87
+ isInline: true, // Inline tokenizer always produces inline math
88
+ displayMode: isDisplayMode, // $$ = display mode styling, $ = inline styling
70
89
  raw: match[0],
71
90
  text: content.trim()
72
91
  };
@@ -76,3 +95,34 @@ export function markedMath() {
76
95
  ]
77
96
  };
78
97
  }
98
+ // Helper function to detect currency patterns
99
+ function isCurrencyPattern(content, fullSrc, dollarIndex) {
100
+ const trimmedContent = content.trim();
101
+ // Check for simple price patterns
102
+ if (currencyPatterns.simplePrice.test(trimmedContent)) {
103
+ return true;
104
+ }
105
+ // Check for multiple numbers/prices pattern (like "199, 199")
106
+ if (currencyPatterns.multipleNumbers.test(trimmedContent)) {
107
+ return true;
108
+ }
109
+ // Check for price ranges
110
+ if (currencyPatterns.priceRange.test(trimmedContent)) {
111
+ return true;
112
+ }
113
+ // Check surrounding context for currency-related words
114
+ const contextStart = Math.max(0, dollarIndex - 50);
115
+ const contextEnd = Math.min(fullSrc.length, dollarIndex + content.length + 50);
116
+ const context = fullSrc.substring(contextStart, contextEnd);
117
+ if (currencyPatterns.currencyContext.test(context)) {
118
+ // If currency context is found and content is purely numeric, likely currency
119
+ if (/^\d+(?:[.,]\d+)*$/.test(trimmedContent)) {
120
+ return true;
121
+ }
122
+ }
123
+ // Additional check: if content is just numbers with common currency formatting
124
+ if (/^\d{1,3}(?:,\d{3})*(?:\.\d{1,2})?$/.test(trimmedContent)) {
125
+ return true;
126
+ }
127
+ return false;
128
+ }
package/dist/theme.js CHANGED
@@ -121,7 +121,7 @@ export const theme = {
121
121
  base: 'italic'
122
122
  },
123
123
  del: {
124
- base: 'line-through'
124
+ base: 'text-gray-600'
125
125
  },
126
126
  footnoteRef: {
127
127
  base: 'text-gray-600 px-1 py-0.5 rounded-md bg-gray-100/80'
@@ -250,7 +250,7 @@ export const shadcnTheme = {
250
250
  base: 'italic text-foreground'
251
251
  },
252
252
  del: {
253
- base: 'line-through text-muted-foreground'
253
+ base: 'text-muted-foreground'
254
254
  },
255
255
  footnoteRef: {
256
256
  base: 'text-muted-foreground px-1 text-sm inline-block rounded-full bg-muted/80 aspect-square border border-border'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svelte-streamdown",
3
- "version": "2.3.0",
3
+ "version": "2.3.1",
4
4
  "scripts": {
5
5
  "dev": "vite dev",
6
6
  "build": "vite build && npm run prepack",