@janhq/remend 1.0.2 → 1.1.0
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 +75 -0
- package/dist/index.cjs +16 -16
- package/dist/index.d.cts +27 -1
- package/dist/index.d.ts +27 -1
- package/dist/index.js +16 -16
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -81,6 +81,81 @@ Available options:
|
|
|
81
81
|
| `strikethrough` | Complete strikethrough formatting (`~~`) |
|
|
82
82
|
| `katex` | Complete block KaTeX math (`$$`) |
|
|
83
83
|
| `setextHeadings` | Handle incomplete setext headings |
|
|
84
|
+
| `handlers` | Custom handlers to extend remend |
|
|
85
|
+
|
|
86
|
+
### Custom Handlers
|
|
87
|
+
|
|
88
|
+
You can extend remend with custom handlers to complete your own markers during streaming. This is useful for custom syntax like `<<<JOKE>>>` blocks or other domain-specific patterns.
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
import remend, { type RemendHandler } from "remend";
|
|
92
|
+
|
|
93
|
+
const jokeHandler: RemendHandler = {
|
|
94
|
+
name: "joke",
|
|
95
|
+
handle: (text) => {
|
|
96
|
+
// Complete <<<JOKE>>> marks that aren't closed
|
|
97
|
+
const match = text.match(/<<<JOKE>>>([^<]*)$/);
|
|
98
|
+
if (match && !text.endsWith("<<</JOKE>>>")) {
|
|
99
|
+
return `${text}<<</JOKE>>>`;
|
|
100
|
+
}
|
|
101
|
+
return text;
|
|
102
|
+
},
|
|
103
|
+
priority: 80, // Runs after most built-ins (0-70)
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const result = remend(content, { handlers: [jokeHandler] });
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
#### Handler Interface
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
interface RemendHandler {
|
|
113
|
+
name: string; // Unique identifier
|
|
114
|
+
handle: (text: string) => string; // Transform function
|
|
115
|
+
priority?: number; // Lower runs first (default: 100)
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
#### Built-in Priorities
|
|
120
|
+
|
|
121
|
+
Built-in handlers use priorities 0-70. Custom handlers default to 100 (run after built-ins):
|
|
122
|
+
|
|
123
|
+
| Handler | Priority |
|
|
124
|
+
|---------|----------|
|
|
125
|
+
| `setextHeadings` | 0 |
|
|
126
|
+
| `links` | 10 |
|
|
127
|
+
| `boldItalic` | 20 |
|
|
128
|
+
| `bold` | 30 |
|
|
129
|
+
| `italic` | 40-42 |
|
|
130
|
+
| `inlineCode` | 50 |
|
|
131
|
+
| `strikethrough` | 60 |
|
|
132
|
+
| `katex` | 70 |
|
|
133
|
+
| Custom (default) | 100 |
|
|
134
|
+
|
|
135
|
+
#### Exported Utilities
|
|
136
|
+
|
|
137
|
+
Remend exports utility functions for context detection in custom handlers:
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
import {
|
|
141
|
+
isWithinCodeBlock,
|
|
142
|
+
isWithinMathBlock,
|
|
143
|
+
isWithinLinkOrImageUrl,
|
|
144
|
+
isWordChar,
|
|
145
|
+
} from "remend";
|
|
146
|
+
|
|
147
|
+
const handler: RemendHandler = {
|
|
148
|
+
name: "custom",
|
|
149
|
+
handle: (text) => {
|
|
150
|
+
// Skip if we're inside a code block
|
|
151
|
+
if (isWithinCodeBlock(text, text.length - 1)) {
|
|
152
|
+
return text;
|
|
153
|
+
}
|
|
154
|
+
// Your logic here
|
|
155
|
+
return text;
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
```
|
|
84
159
|
|
|
85
160
|
### Usage with Remark
|
|
86
161
|
|
package/dist/index.cjs
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
'use strict';var
|
|
2
|
-
`)return false}return false},
|
|
3
|
-
`)return false}return false},
|
|
4
|
-
`){s
|
|
5
|
-
`){
|
|
6
|
-
`){
|
|
7
|
-
`),
|
|
8
|
-
`)?true:
|
|
9
|
-
`),
|
|
10
|
-
`)?true:
|
|
11
|
-
`;)r-=1;if(r<n.length){let e=n.slice(0,r),
|
|
12
|
-
`)?null:n.endsWith("``")&&!n.endsWith("```")?`${n}\``:n,
|
|
1
|
+
'use strict';Object.defineProperty(exports,'__esModule',{value:true});var rn=Object.defineProperty,en=Object.defineProperties;var tn=Object.getOwnPropertyDescriptors;var T=Object.getOwnPropertySymbols;var sn=Object.prototype.hasOwnProperty,on=Object.prototype.propertyIsEnumerable;var S=(n,r,e)=>r in n?rn(n,r,{enumerable:true,configurable:true,writable:true,value:e}):n[r]=e,b=(n,r)=>{for(var e in r||(r={}))sn.call(r,e)&&S(n,e,r[e]);if(T)for(var e of T(r))on.call(r,e)&&S(n,e,r[e]);return n},I=(n,r)=>en(n,tn(r));var _=/(\*\*)([^*]*\*?)$/,L=/(__)([^_]*?)$/,E=/(\*\*\*)([^*]*?)$/,O=/(\*)([^*]*?)$/,U=/(_)([^_]*?)$/,y=/(`)([^`]*?)$/,N=/(~~)([^~]*?)$/,u=/^[\s_~*`]*$/,A=/^[\s]*[-*+][\s]+$/,R=/[\p{L}\p{N}_]/u,D=/^```[^`\n]*```?$/,H=/^\*{4,}$/;var W=/(__)([^_]+)_$/,K=/(~~)([^~]+)~$/,w=/__/g,P=/~~/g;var f=n=>{if(!n)return false;let r=n.charCodeAt(0);return r>=48&&r<=57||r>=65&&r<=90||r>=97&&r<=122||r===95?true:R.test(n)},d=(n,r)=>{let e=false;for(let i=0;i<r;i+=1)n[i]==="`"&&n[i+1]==="`"&&n[i+2]==="`"&&(e=!e,i+=2);return e},G=(n,r)=>{let e=1;for(let i=r-1;i>=0;i-=1)if(n[i]==="]")e+=1;else if(n[i]==="["&&(e-=1,e===0))return i;return -1},B=(n,r)=>{let e=1;for(let i=r+1;i<n.length;i+=1)if(n[i]==="[")e+=1;else if(n[i]==="]"&&(e-=1,e===0))return i;return -1},g=(n,r)=>{let e=false,i=false;for(let t=0;t<n.length&&t<r;t+=1){if(n[t]==="\\"&&n[t+1]==="$"){t+=1;continue}n[t]==="$"&&(n[t+1]==="$"?(i=!i,t+=1,e=false):i||(e=!e));}return e||i},ln=(n,r)=>{for(let e=r;e<n.length;e+=1){if(n[e]===")")return true;if(n[e]===`
|
|
2
|
+
`)return false}return false},p=(n,r)=>{for(let e=r-1;e>=0;e-=1){if(n[e]===")")return false;if(n[e]==="(")return e>0&&n[e-1]==="]"?ln(n,r):false;if(n[e]===`
|
|
3
|
+
`)return false}return false},k=(n,r,e)=>{let i=0;for(let s=r-1;s>=0;s-=1)if(n[s]===`
|
|
4
|
+
`){i=s+1;break}let t=n.length;for(let s=r;s<n.length;s+=1)if(n[s]===`
|
|
5
|
+
`){t=s;break}let o=n.substring(i,t),a=0,l=false;for(let s of o)if(s===e)a+=1;else if(s!==" "&&s!==" "){l=true;break}return a>=3&&!l};var an=(n,r,e)=>{if(e!==" "&&e!==" ")return false;let i=0;for(let t=r-1;t>=0;t-=1)if(n[t]===`
|
|
6
|
+
`){i=t+1;break}for(let t=i;t<r;t+=1)if(n[t]!==" "&&n[t]!==" ")return false;return true},cn=(n,r,e,i)=>e==="\\"||n.includes("$")&&g(n,r)?true:e!=="*"&&i==="*"?(r<n.length-2?n[r+2]:"")!=="*":!!(e==="*"||e&&i&&f(e)&&f(i)||an(n,r,i)),F=n=>{let r=0,e=n.length;for(let i=0;i<e;i+=1){if(n[i]!=="*")continue;let t=i>0?n[i-1]:"",o=i<e-1?n[i+1]:"";cn(n,i,t,o)||(r+=1);}return r},un=(n,r,e,i)=>!!(e==="\\"||n.includes("$")&&g(n,r)||p(n,r)||e==="_"||i==="_"||e&&i&&f(e)&&f(i)),fn=n=>{let r=0,e=n.length;for(let i=0;i<e;i+=1){if(n[i]!=="_")continue;let t=i>0?n[i-1]:"",o=i<e-1?n[i+1]:"";un(n,i,t,o)||(r+=1);}return r},dn=n=>{let r=0,e=0;for(let i=0;i<n.length;i+=1)n[i]==="*"?e+=1:(e>=3&&(r+=Math.floor(e/3)),e=0);return e>=3&&(r+=Math.floor(e/3)),r},gn=(n,r,e)=>{if(!r||u.test(r))return true;let t=n.substring(0,e).lastIndexOf(`
|
|
7
|
+
`),o=t===-1?0:t+1,a=n.substring(o,e);return A.test(a)&&r.includes(`
|
|
8
|
+
`)?true:k(n,e,"*")},X=n=>{let r=n.match(_);if(!r)return n;let e=r[2],i=n.lastIndexOf(r[1]);return d(n,i)||gn(n,e,i)?n:(n.match(/\*\*/g)||[]).length%2===1?e.endsWith("*")?`${n}*`:`${n}**`:n},hn=(n,r,e)=>{if(!r||u.test(r))return true;let t=n.substring(0,e).lastIndexOf(`
|
|
9
|
+
`),o=t===-1?0:t+1,a=n.substring(o,e);return A.test(a)&&r.includes(`
|
|
10
|
+
`)?true:k(n,e,"_")},v=n=>{let r=n.match(L);if(!r){let o=n.match(W);if(o){let a=n.lastIndexOf(o[1]);if(!d(n,a)&&(n.match(w)||[]).length%2===1)return `${n}_`}return n}let e=r[2],i=n.lastIndexOf(r[1]);return d(n,i)||hn(n,e,i)?n:(n.match(/__/g)||[]).length%2===1?`${n}__`:n},mn=n=>{for(let r=0;r<n.length;r+=1)if(n[r]==="*"&&n[r-1]!=="*"&&n[r+1]!=="*"&&n[r-1]!=="\\"&&!g(n,r)){let e=r>0?n[r-1]:"",i=r<n.length-1?n[r+1]:"";if(e&&i&&f(e)&&f(i))continue;return r}return -1},Y=n=>{if(!n.match(O))return n;let e=mn(n);if(e===-1||d(n,e))return n;let i=n.substring(e+1);return !i||u.test(i)?n:F(n)%2===1?`${n}*`:n},j=n=>{for(let r=0;r<n.length;r+=1)if(n[r]==="_"&&n[r-1]!=="_"&&n[r+1]!=="_"&&n[r-1]!=="\\"&&!g(n,r)&&!p(n,r)){let e=r>0?n[r-1]:"",i=r<n.length-1?n[r+1]:"";if(e&&i&&f(e)&&f(i))continue;return r}return -1},pn=n=>{let r=n.length;for(;r>0&&n[r-1]===`
|
|
11
|
+
`;)r-=1;if(r<n.length){let e=n.slice(0,r),i=n.slice(r);return `${e}_${i}`}return `${n}_`},kn=n=>{if(!n.endsWith("**"))return null;let r=n.slice(0,-2);if((r.match(/\*\*/g)||[]).length%2!==1)return null;let i=r.indexOf("**"),t=j(r);return i!==-1&&t!==-1&&i<t?`${r}_**`:null},z=n=>{if(!n.match(U))return n;let e=j(n);if(e===-1||d(n,e))return n;let i=n.substring(e+1);if(!i||u.test(i))return n;if(fn(n)%2===1){let o=kn(n);return o!==null?o:pn(n)}return n},bn=n=>{let r=(n.match(/\*\*/g)||[]).length,e=F(n);return r%2===0&&e%2===0},In=(n,r,e)=>!r||u.test(r)||d(n,e)?true:k(n,e,"*"),Q=n=>{if(H.test(n))return n;let r=n.match(E);if(!r)return n;let e=r[2],i=n.lastIndexOf(r[1]);return In(n,e,i)?n:dn(n)%2===1?bn(n)?n:`${n}***`:n};var h=(n,r)=>{let e=false,i=false;for(let t=0;t<r;t+=1){if(n.substring(t,t+3)==="```"){i=!i,t+=2;continue}!i&&n[t]==="`"&&(e=!e);}return e||i},An=(n,r)=>{let e=n.substring(r,r+3)==="```",i=r>0&&n.substring(r-1,r+2)==="```",t=r>1&&n.substring(r-2,r+1)==="```";return e||i||t},q=n=>{let r=0;for(let e=0;e<n.length;e+=1)n[e]==="`"&&!An(n,e)&&(r+=1);return r};var Pn=n=>!n.match(D)||n.includes(`
|
|
12
|
+
`)?null:n.endsWith("``")&&!n.endsWith("```")?`${n}\``:n,Bn=n=>(n.match(/```/g)||[]).length%2===1,J=n=>{let r=Pn(n);if(r!==null)return r;let e=n.match(y);if(e&&!Bn(n)){let i=e[2];if(!i||u.test(i))return n;if(q(n)%2===1)return `${n}\``}return n};var Cn=(n,r)=>r>=2&&n.substring(r-2,r+1)==="```"||r>=1&&n.substring(r-1,r+2)==="```"||r<=n.length-3&&n.substring(r,r+3)==="```",$n=n=>{let r=0,e=false;for(let i=0;i<n.length-1;i+=1)n[i]==="`"&&!Cn(n,i)&&(e=!e),!e&&n[i]==="$"&&n[i+1]==="$"&&(r+=1,i+=1);return r},Mn=n=>{let r=n.indexOf("$$");return r!==-1&&n.indexOf(`
|
|
13
13
|
`,r)!==-1&&!n.endsWith(`
|
|
14
14
|
`)?`${n}
|
|
15
|
-
$$`:`${n}$$`},
|
|
16
|
-
`);if(r===-1)return n;let e=n.substring(r+1),
|
|
17
|
-
`).at(-1);if(
|
|
18
|
-
`).at(-1);if(
|
|
15
|
+
$$`:`${n}$$`},V=n=>$n(n)%2===0?n:Mn(n);var Tn=(n,r,e)=>{if(n.substring(r+2).includes(")"))return null;let t=G(n,r);if(t===-1||h(n,t))return null;let o=t>0&&n[t-1]==="!",a=o?t-1:t,l=n.substring(0,a);if(o)return l;let s=n.substring(t+1,r);return e==="text-only"?`${l}${s}`:`${l}[${s}](streamdown:incomplete-link)`},Z=(n,r)=>{for(let e=0;e<=r;e++)if(n[e]==="["&&!h(n,e)){if(e>0&&n[e-1]==="!")continue;let i=B(n,e);if(i===-1)return e;if(i+1<n.length&&n[i+1]==="("){let t=n.indexOf(")",i+2);t!==-1&&(e=t);}}return -1},Sn=(n,r,e)=>{let i=r>0&&n[r-1]==="!",t=i?r-1:r,o=n.substring(r+1);if(!o.includes("]")){let l=n.substring(0,t);if(i)return l;if(e==="text-only"){let s=Z(n,r);return s!==-1?n.substring(0,s)+n.substring(s+1):`${l}${o}`}return `${n}](streamdown:incomplete-link)`}if(B(n,r)===-1){let l=n.substring(0,t);if(i)return l;if(e==="text-only"){let s=Z(n,r);if(s!==-1)return n.substring(0,s)+n.substring(s+1);let m=n.substring(r+1);return `${l}${m}`}return `${n}](streamdown:incomplete-link)`}return null},C=(n,r="protocol")=>{let e=n.lastIndexOf("](");if(e!==-1&&!h(n,e)){let i=Tn(n,e,r);if(i!==null)return i}for(let i=n.length-1;i>=0;i-=1)if(n[i]==="["&&!h(n,i)){let t=Sn(n,i,r);if(t!==null)return t}return n};var _n=/^-{1,2}$/,Ln=/^[\s]*-{1,2}[\s]+$/,En=/^={1,2}$/,On=/^[\s]*={1,2}[\s]+$/,x=n=>{if(!n||typeof n!="string")return n;let r=n.lastIndexOf(`
|
|
16
|
+
`);if(r===-1)return n;let e=n.substring(r+1),i=n.substring(0,r),t=e.trim();if(_n.test(t)&&!e.match(Ln)){let a=i.split(`
|
|
17
|
+
`).at(-1);if(a&&a.trim().length>0)return `${n}\u200B`}if(En.test(t)&&!e.match(On)){let a=i.split(`
|
|
18
|
+
`).at(-1);if(a&&a.trim().length>0)return `${n}\u200B`}return n};var nn=n=>{let r=n.match(N);if(r){let e=r[2];if(!e||u.test(e))return n;if((n.match(P)||[]).length%2===1)return `${n}~~`}else if(n.match(K)&&(n.match(P)||[]).length%2===1)return `${n}~`;return n};var $=n=>n!==false,c={SETEXT_HEADINGS:0,LINKS:10,BOLD_ITALIC:20,BOLD:30,ITALIC_DOUBLE_UNDERSCORE:40,ITALIC_SINGLE_ASTERISK:41,ITALIC_SINGLE_UNDERSCORE:42,INLINE_CODE:50,STRIKETHROUGH:60,KATEX:70,DEFAULT:100},Un=[{handler:{name:"setextHeadings",handle:x,priority:c.SETEXT_HEADINGS},optionKey:"setextHeadings"},{handler:{name:"links",handle:C,priority:c.LINKS},optionKey:"links",earlyReturn:n=>n.endsWith("](streamdown:incomplete-link)")},{handler:{name:"boldItalic",handle:Q,priority:c.BOLD_ITALIC},optionKey:"boldItalic"},{handler:{name:"bold",handle:X,priority:c.BOLD},optionKey:"bold"},{handler:{name:"italicDoubleUnderscore",handle:v,priority:c.ITALIC_DOUBLE_UNDERSCORE},optionKey:"italic"},{handler:{name:"italicSingleAsterisk",handle:Y,priority:c.ITALIC_SINGLE_ASTERISK},optionKey:"italic"},{handler:{name:"italicSingleUnderscore",handle:z,priority:c.ITALIC_SINGLE_UNDERSCORE},optionKey:"italic"},{handler:{name:"inlineCode",handle:J,priority:c.INLINE_CODE},optionKey:"inlineCode"},{handler:{name:"strikethrough",handle:nn,priority:c.STRIKETHROUGH},optionKey:"strikethrough"},{handler:{name:"katex",handle:V,priority:c.KATEX},optionKey:"katex"}],yn=n=>{var e;let r=(e=n==null?void 0:n.linkMode)!=null?e:"protocol";return Un.filter(({handler:i,optionKey:t})=>i.name==="links"?$(n==null?void 0:n.links)||$(n==null?void 0:n.images):$(n==null?void 0:n[t])).map(({handler:i,earlyReturn:t})=>i.name==="links"?{handler:I(b({},i),{handle:o=>C(o,r)}),earlyReturn:r==="protocol"?t:void 0}:{handler:i,earlyReturn:t})},Nn=(n,r)=>{var a;if(!n||typeof n!="string")return n;let e=n.endsWith(" ")&&!n.endsWith(" ")?n.slice(0,-1):n,i=yn(r),t=((a=r==null?void 0:r.handlers)!=null?a:[]).map(l=>{var s;return {handler:I(b({},l),{priority:(s=l.priority)!=null?s:c.DEFAULT}),earlyReturn:void 0}}),o=[...i,...t].sort((l,s)=>{var m,M;return ((m=l.handler.priority)!=null?m:c.DEFAULT)-((M=s.handler.priority)!=null?M:c.DEFAULT)});for(let{handler:l,earlyReturn:s}of o)if(e=l.handle(e),s!=null&&s(e))return e;return e},sr=Nn;exports.default=sr;exports.isWithinCodeBlock=d;exports.isWithinLinkOrImageUrl=p;exports.isWithinMathBlock=g;exports.isWordChar=f;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
type LinkMode = "protocol" | "text-only";
|
|
2
|
+
|
|
3
|
+
declare const isWordChar: (char: string) => boolean;
|
|
4
|
+
declare const isWithinCodeBlock: (text: string, position: number) => boolean;
|
|
5
|
+
declare const isWithinMathBlock: (text: string, position: number) => boolean;
|
|
6
|
+
declare const isWithinLinkOrImageUrl: (text: string, position: number) => boolean;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Handler function that transforms text during streaming.
|
|
10
|
+
*/
|
|
11
|
+
interface RemendHandler {
|
|
12
|
+
/** Unique identifier for this handler */
|
|
13
|
+
name: string;
|
|
14
|
+
/** Handler function: takes text, returns modified text */
|
|
15
|
+
handle: (text: string) => string;
|
|
16
|
+
/** Priority (lower runs first). Built-in priorities: 0-100. Default: 100 */
|
|
17
|
+
priority?: number;
|
|
18
|
+
}
|
|
1
19
|
/**
|
|
2
20
|
* Configuration options for the remend function.
|
|
3
21
|
* All options default to `true` when not specified.
|
|
@@ -8,6 +26,12 @@ interface RemendOptions {
|
|
|
8
26
|
links?: boolean;
|
|
9
27
|
/** Complete images (e.g., ` */
|
|
10
28
|
images?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* How to handle incomplete links:
|
|
31
|
+
* - `'protocol'`: Use `streamdown:incomplete-link` placeholder URL (default)
|
|
32
|
+
* - `'text-only'`: Display only the link text without any link markup
|
|
33
|
+
*/
|
|
34
|
+
linkMode?: "protocol" | "text-only";
|
|
11
35
|
/** Complete bold formatting (e.g., `**text` → `**text**`) */
|
|
12
36
|
bold?: boolean;
|
|
13
37
|
/** Complete italic formatting (e.g., `*text` → `*text*` or `_text` → `_text_`) */
|
|
@@ -22,7 +46,9 @@ interface RemendOptions {
|
|
|
22
46
|
katex?: boolean;
|
|
23
47
|
/** Handle incomplete setext headings to prevent misinterpretation */
|
|
24
48
|
setextHeadings?: boolean;
|
|
49
|
+
/** Custom handlers to extend remend */
|
|
50
|
+
handlers?: RemendHandler[];
|
|
25
51
|
}
|
|
26
52
|
declare const remend: (text: string, options?: RemendOptions) => string;
|
|
27
53
|
|
|
28
|
-
export { type RemendOptions, remend as default };
|
|
54
|
+
export { type LinkMode, type RemendHandler, type RemendOptions, remend as default, isWithinCodeBlock, isWithinLinkOrImageUrl, isWithinMathBlock, isWordChar };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
type LinkMode = "protocol" | "text-only";
|
|
2
|
+
|
|
3
|
+
declare const isWordChar: (char: string) => boolean;
|
|
4
|
+
declare const isWithinCodeBlock: (text: string, position: number) => boolean;
|
|
5
|
+
declare const isWithinMathBlock: (text: string, position: number) => boolean;
|
|
6
|
+
declare const isWithinLinkOrImageUrl: (text: string, position: number) => boolean;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Handler function that transforms text during streaming.
|
|
10
|
+
*/
|
|
11
|
+
interface RemendHandler {
|
|
12
|
+
/** Unique identifier for this handler */
|
|
13
|
+
name: string;
|
|
14
|
+
/** Handler function: takes text, returns modified text */
|
|
15
|
+
handle: (text: string) => string;
|
|
16
|
+
/** Priority (lower runs first). Built-in priorities: 0-100. Default: 100 */
|
|
17
|
+
priority?: number;
|
|
18
|
+
}
|
|
1
19
|
/**
|
|
2
20
|
* Configuration options for the remend function.
|
|
3
21
|
* All options default to `true` when not specified.
|
|
@@ -8,6 +26,12 @@ interface RemendOptions {
|
|
|
8
26
|
links?: boolean;
|
|
9
27
|
/** Complete images (e.g., ` */
|
|
10
28
|
images?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* How to handle incomplete links:
|
|
31
|
+
* - `'protocol'`: Use `streamdown:incomplete-link` placeholder URL (default)
|
|
32
|
+
* - `'text-only'`: Display only the link text without any link markup
|
|
33
|
+
*/
|
|
34
|
+
linkMode?: "protocol" | "text-only";
|
|
11
35
|
/** Complete bold formatting (e.g., `**text` → `**text**`) */
|
|
12
36
|
bold?: boolean;
|
|
13
37
|
/** Complete italic formatting (e.g., `*text` → `*text*` or `_text` → `_text_`) */
|
|
@@ -22,7 +46,9 @@ interface RemendOptions {
|
|
|
22
46
|
katex?: boolean;
|
|
23
47
|
/** Handle incomplete setext headings to prevent misinterpretation */
|
|
24
48
|
setextHeadings?: boolean;
|
|
49
|
+
/** Custom handlers to extend remend */
|
|
50
|
+
handlers?: RemendHandler[];
|
|
25
51
|
}
|
|
26
52
|
declare const remend: (text: string, options?: RemendOptions) => string;
|
|
27
53
|
|
|
28
|
-
export { type RemendOptions, remend as default };
|
|
54
|
+
export { type LinkMode, type RemendHandler, type RemendOptions, remend as default, isWithinCodeBlock, isWithinLinkOrImageUrl, isWithinMathBlock, isWordChar };
|
package/dist/index.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
var
|
|
2
|
-
`)return false}return false},
|
|
3
|
-
`)return false}return false},
|
|
4
|
-
`){s
|
|
5
|
-
`){
|
|
6
|
-
`){
|
|
7
|
-
`),
|
|
8
|
-
`)?true:
|
|
9
|
-
`),
|
|
10
|
-
`)?true:
|
|
11
|
-
`;)r-=1;if(r<n.length){let e=n.slice(0,r),
|
|
12
|
-
`)?null:n.endsWith("``")&&!n.endsWith("```")?`${n}\``:n,
|
|
1
|
+
var rn=Object.defineProperty,en=Object.defineProperties;var tn=Object.getOwnPropertyDescriptors;var T=Object.getOwnPropertySymbols;var sn=Object.prototype.hasOwnProperty,on=Object.prototype.propertyIsEnumerable;var S=(n,r,e)=>r in n?rn(n,r,{enumerable:true,configurable:true,writable:true,value:e}):n[r]=e,b=(n,r)=>{for(var e in r||(r={}))sn.call(r,e)&&S(n,e,r[e]);if(T)for(var e of T(r))on.call(r,e)&&S(n,e,r[e]);return n},I=(n,r)=>en(n,tn(r));var _=/(\*\*)([^*]*\*?)$/,L=/(__)([^_]*?)$/,E=/(\*\*\*)([^*]*?)$/,O=/(\*)([^*]*?)$/,U=/(_)([^_]*?)$/,y=/(`)([^`]*?)$/,N=/(~~)([^~]*?)$/,u=/^[\s_~*`]*$/,A=/^[\s]*[-*+][\s]+$/,R=/[\p{L}\p{N}_]/u,D=/^```[^`\n]*```?$/,H=/^\*{4,}$/;var W=/(__)([^_]+)_$/,K=/(~~)([^~]+)~$/,w=/__/g,P=/~~/g;var f=n=>{if(!n)return false;let r=n.charCodeAt(0);return r>=48&&r<=57||r>=65&&r<=90||r>=97&&r<=122||r===95?true:R.test(n)},d=(n,r)=>{let e=false;for(let i=0;i<r;i+=1)n[i]==="`"&&n[i+1]==="`"&&n[i+2]==="`"&&(e=!e,i+=2);return e},G=(n,r)=>{let e=1;for(let i=r-1;i>=0;i-=1)if(n[i]==="]")e+=1;else if(n[i]==="["&&(e-=1,e===0))return i;return -1},B=(n,r)=>{let e=1;for(let i=r+1;i<n.length;i+=1)if(n[i]==="[")e+=1;else if(n[i]==="]"&&(e-=1,e===0))return i;return -1},g=(n,r)=>{let e=false,i=false;for(let t=0;t<n.length&&t<r;t+=1){if(n[t]==="\\"&&n[t+1]==="$"){t+=1;continue}n[t]==="$"&&(n[t+1]==="$"?(i=!i,t+=1,e=false):i||(e=!e));}return e||i},ln=(n,r)=>{for(let e=r;e<n.length;e+=1){if(n[e]===")")return true;if(n[e]===`
|
|
2
|
+
`)return false}return false},p=(n,r)=>{for(let e=r-1;e>=0;e-=1){if(n[e]===")")return false;if(n[e]==="(")return e>0&&n[e-1]==="]"?ln(n,r):false;if(n[e]===`
|
|
3
|
+
`)return false}return false},k=(n,r,e)=>{let i=0;for(let s=r-1;s>=0;s-=1)if(n[s]===`
|
|
4
|
+
`){i=s+1;break}let t=n.length;for(let s=r;s<n.length;s+=1)if(n[s]===`
|
|
5
|
+
`){t=s;break}let o=n.substring(i,t),a=0,l=false;for(let s of o)if(s===e)a+=1;else if(s!==" "&&s!==" "){l=true;break}return a>=3&&!l};var an=(n,r,e)=>{if(e!==" "&&e!==" ")return false;let i=0;for(let t=r-1;t>=0;t-=1)if(n[t]===`
|
|
6
|
+
`){i=t+1;break}for(let t=i;t<r;t+=1)if(n[t]!==" "&&n[t]!==" ")return false;return true},cn=(n,r,e,i)=>e==="\\"||n.includes("$")&&g(n,r)?true:e!=="*"&&i==="*"?(r<n.length-2?n[r+2]:"")!=="*":!!(e==="*"||e&&i&&f(e)&&f(i)||an(n,r,i)),F=n=>{let r=0,e=n.length;for(let i=0;i<e;i+=1){if(n[i]!=="*")continue;let t=i>0?n[i-1]:"",o=i<e-1?n[i+1]:"";cn(n,i,t,o)||(r+=1);}return r},un=(n,r,e,i)=>!!(e==="\\"||n.includes("$")&&g(n,r)||p(n,r)||e==="_"||i==="_"||e&&i&&f(e)&&f(i)),fn=n=>{let r=0,e=n.length;for(let i=0;i<e;i+=1){if(n[i]!=="_")continue;let t=i>0?n[i-1]:"",o=i<e-1?n[i+1]:"";un(n,i,t,o)||(r+=1);}return r},dn=n=>{let r=0,e=0;for(let i=0;i<n.length;i+=1)n[i]==="*"?e+=1:(e>=3&&(r+=Math.floor(e/3)),e=0);return e>=3&&(r+=Math.floor(e/3)),r},gn=(n,r,e)=>{if(!r||u.test(r))return true;let t=n.substring(0,e).lastIndexOf(`
|
|
7
|
+
`),o=t===-1?0:t+1,a=n.substring(o,e);return A.test(a)&&r.includes(`
|
|
8
|
+
`)?true:k(n,e,"*")},X=n=>{let r=n.match(_);if(!r)return n;let e=r[2],i=n.lastIndexOf(r[1]);return d(n,i)||gn(n,e,i)?n:(n.match(/\*\*/g)||[]).length%2===1?e.endsWith("*")?`${n}*`:`${n}**`:n},hn=(n,r,e)=>{if(!r||u.test(r))return true;let t=n.substring(0,e).lastIndexOf(`
|
|
9
|
+
`),o=t===-1?0:t+1,a=n.substring(o,e);return A.test(a)&&r.includes(`
|
|
10
|
+
`)?true:k(n,e,"_")},v=n=>{let r=n.match(L);if(!r){let o=n.match(W);if(o){let a=n.lastIndexOf(o[1]);if(!d(n,a)&&(n.match(w)||[]).length%2===1)return `${n}_`}return n}let e=r[2],i=n.lastIndexOf(r[1]);return d(n,i)||hn(n,e,i)?n:(n.match(/__/g)||[]).length%2===1?`${n}__`:n},mn=n=>{for(let r=0;r<n.length;r+=1)if(n[r]==="*"&&n[r-1]!=="*"&&n[r+1]!=="*"&&n[r-1]!=="\\"&&!g(n,r)){let e=r>0?n[r-1]:"",i=r<n.length-1?n[r+1]:"";if(e&&i&&f(e)&&f(i))continue;return r}return -1},Y=n=>{if(!n.match(O))return n;let e=mn(n);if(e===-1||d(n,e))return n;let i=n.substring(e+1);return !i||u.test(i)?n:F(n)%2===1?`${n}*`:n},j=n=>{for(let r=0;r<n.length;r+=1)if(n[r]==="_"&&n[r-1]!=="_"&&n[r+1]!=="_"&&n[r-1]!=="\\"&&!g(n,r)&&!p(n,r)){let e=r>0?n[r-1]:"",i=r<n.length-1?n[r+1]:"";if(e&&i&&f(e)&&f(i))continue;return r}return -1},pn=n=>{let r=n.length;for(;r>0&&n[r-1]===`
|
|
11
|
+
`;)r-=1;if(r<n.length){let e=n.slice(0,r),i=n.slice(r);return `${e}_${i}`}return `${n}_`},kn=n=>{if(!n.endsWith("**"))return null;let r=n.slice(0,-2);if((r.match(/\*\*/g)||[]).length%2!==1)return null;let i=r.indexOf("**"),t=j(r);return i!==-1&&t!==-1&&i<t?`${r}_**`:null},z=n=>{if(!n.match(U))return n;let e=j(n);if(e===-1||d(n,e))return n;let i=n.substring(e+1);if(!i||u.test(i))return n;if(fn(n)%2===1){let o=kn(n);return o!==null?o:pn(n)}return n},bn=n=>{let r=(n.match(/\*\*/g)||[]).length,e=F(n);return r%2===0&&e%2===0},In=(n,r,e)=>!r||u.test(r)||d(n,e)?true:k(n,e,"*"),Q=n=>{if(H.test(n))return n;let r=n.match(E);if(!r)return n;let e=r[2],i=n.lastIndexOf(r[1]);return In(n,e,i)?n:dn(n)%2===1?bn(n)?n:`${n}***`:n};var h=(n,r)=>{let e=false,i=false;for(let t=0;t<r;t+=1){if(n.substring(t,t+3)==="```"){i=!i,t+=2;continue}!i&&n[t]==="`"&&(e=!e);}return e||i},An=(n,r)=>{let e=n.substring(r,r+3)==="```",i=r>0&&n.substring(r-1,r+2)==="```",t=r>1&&n.substring(r-2,r+1)==="```";return e||i||t},q=n=>{let r=0;for(let e=0;e<n.length;e+=1)n[e]==="`"&&!An(n,e)&&(r+=1);return r};var Pn=n=>!n.match(D)||n.includes(`
|
|
12
|
+
`)?null:n.endsWith("``")&&!n.endsWith("```")?`${n}\``:n,Bn=n=>(n.match(/```/g)||[]).length%2===1,J=n=>{let r=Pn(n);if(r!==null)return r;let e=n.match(y);if(e&&!Bn(n)){let i=e[2];if(!i||u.test(i))return n;if(q(n)%2===1)return `${n}\``}return n};var Cn=(n,r)=>r>=2&&n.substring(r-2,r+1)==="```"||r>=1&&n.substring(r-1,r+2)==="```"||r<=n.length-3&&n.substring(r,r+3)==="```",$n=n=>{let r=0,e=false;for(let i=0;i<n.length-1;i+=1)n[i]==="`"&&!Cn(n,i)&&(e=!e),!e&&n[i]==="$"&&n[i+1]==="$"&&(r+=1,i+=1);return r},Mn=n=>{let r=n.indexOf("$$");return r!==-1&&n.indexOf(`
|
|
13
13
|
`,r)!==-1&&!n.endsWith(`
|
|
14
14
|
`)?`${n}
|
|
15
|
-
$$`:`${n}$$`},
|
|
16
|
-
`);if(r===-1)return n;let e=n.substring(r+1),
|
|
17
|
-
`).at(-1);if(
|
|
18
|
-
`).at(-1);if(
|
|
15
|
+
$$`:`${n}$$`},V=n=>$n(n)%2===0?n:Mn(n);var Tn=(n,r,e)=>{if(n.substring(r+2).includes(")"))return null;let t=G(n,r);if(t===-1||h(n,t))return null;let o=t>0&&n[t-1]==="!",a=o?t-1:t,l=n.substring(0,a);if(o)return l;let s=n.substring(t+1,r);return e==="text-only"?`${l}${s}`:`${l}[${s}](streamdown:incomplete-link)`},Z=(n,r)=>{for(let e=0;e<=r;e++)if(n[e]==="["&&!h(n,e)){if(e>0&&n[e-1]==="!")continue;let i=B(n,e);if(i===-1)return e;if(i+1<n.length&&n[i+1]==="("){let t=n.indexOf(")",i+2);t!==-1&&(e=t);}}return -1},Sn=(n,r,e)=>{let i=r>0&&n[r-1]==="!",t=i?r-1:r,o=n.substring(r+1);if(!o.includes("]")){let l=n.substring(0,t);if(i)return l;if(e==="text-only"){let s=Z(n,r);return s!==-1?n.substring(0,s)+n.substring(s+1):`${l}${o}`}return `${n}](streamdown:incomplete-link)`}if(B(n,r)===-1){let l=n.substring(0,t);if(i)return l;if(e==="text-only"){let s=Z(n,r);if(s!==-1)return n.substring(0,s)+n.substring(s+1);let m=n.substring(r+1);return `${l}${m}`}return `${n}](streamdown:incomplete-link)`}return null},C=(n,r="protocol")=>{let e=n.lastIndexOf("](");if(e!==-1&&!h(n,e)){let i=Tn(n,e,r);if(i!==null)return i}for(let i=n.length-1;i>=0;i-=1)if(n[i]==="["&&!h(n,i)){let t=Sn(n,i,r);if(t!==null)return t}return n};var _n=/^-{1,2}$/,Ln=/^[\s]*-{1,2}[\s]+$/,En=/^={1,2}$/,On=/^[\s]*={1,2}[\s]+$/,x=n=>{if(!n||typeof n!="string")return n;let r=n.lastIndexOf(`
|
|
16
|
+
`);if(r===-1)return n;let e=n.substring(r+1),i=n.substring(0,r),t=e.trim();if(_n.test(t)&&!e.match(Ln)){let a=i.split(`
|
|
17
|
+
`).at(-1);if(a&&a.trim().length>0)return `${n}\u200B`}if(En.test(t)&&!e.match(On)){let a=i.split(`
|
|
18
|
+
`).at(-1);if(a&&a.trim().length>0)return `${n}\u200B`}return n};var nn=n=>{let r=n.match(N);if(r){let e=r[2];if(!e||u.test(e))return n;if((n.match(P)||[]).length%2===1)return `${n}~~`}else if(n.match(K)&&(n.match(P)||[]).length%2===1)return `${n}~`;return n};var $=n=>n!==false,c={SETEXT_HEADINGS:0,LINKS:10,BOLD_ITALIC:20,BOLD:30,ITALIC_DOUBLE_UNDERSCORE:40,ITALIC_SINGLE_ASTERISK:41,ITALIC_SINGLE_UNDERSCORE:42,INLINE_CODE:50,STRIKETHROUGH:60,KATEX:70,DEFAULT:100},Un=[{handler:{name:"setextHeadings",handle:x,priority:c.SETEXT_HEADINGS},optionKey:"setextHeadings"},{handler:{name:"links",handle:C,priority:c.LINKS},optionKey:"links",earlyReturn:n=>n.endsWith("](streamdown:incomplete-link)")},{handler:{name:"boldItalic",handle:Q,priority:c.BOLD_ITALIC},optionKey:"boldItalic"},{handler:{name:"bold",handle:X,priority:c.BOLD},optionKey:"bold"},{handler:{name:"italicDoubleUnderscore",handle:v,priority:c.ITALIC_DOUBLE_UNDERSCORE},optionKey:"italic"},{handler:{name:"italicSingleAsterisk",handle:Y,priority:c.ITALIC_SINGLE_ASTERISK},optionKey:"italic"},{handler:{name:"italicSingleUnderscore",handle:z,priority:c.ITALIC_SINGLE_UNDERSCORE},optionKey:"italic"},{handler:{name:"inlineCode",handle:J,priority:c.INLINE_CODE},optionKey:"inlineCode"},{handler:{name:"strikethrough",handle:nn,priority:c.STRIKETHROUGH},optionKey:"strikethrough"},{handler:{name:"katex",handle:V,priority:c.KATEX},optionKey:"katex"}],yn=n=>{var e;let r=(e=n==null?void 0:n.linkMode)!=null?e:"protocol";return Un.filter(({handler:i,optionKey:t})=>i.name==="links"?$(n==null?void 0:n.links)||$(n==null?void 0:n.images):$(n==null?void 0:n[t])).map(({handler:i,earlyReturn:t})=>i.name==="links"?{handler:I(b({},i),{handle:o=>C(o,r)}),earlyReturn:r==="protocol"?t:void 0}:{handler:i,earlyReturn:t})},Nn=(n,r)=>{var a;if(!n||typeof n!="string")return n;let e=n.endsWith(" ")&&!n.endsWith(" ")?n.slice(0,-1):n,i=yn(r),t=((a=r==null?void 0:r.handlers)!=null?a:[]).map(l=>{var s;return {handler:I(b({},l),{priority:(s=l.priority)!=null?s:c.DEFAULT}),earlyReturn:void 0}}),o=[...i,...t].sort((l,s)=>{var m,M;return ((m=l.handler.priority)!=null?m:c.DEFAULT)-((M=s.handler.priority)!=null?M:c.DEFAULT)});for(let{handler:l,earlyReturn:s}of o)if(e=l.handle(e),s!=null&&s(e))return e;return e},sr=Nn;export{sr as default,d as isWithinCodeBlock,p as isWithinLinkOrImageUrl,g as isWithinMathBlock,f as isWordChar};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@janhq/remend",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
"description": "Self-healing markdown. Intelligently parses and styles incomplete Markdown blocks.",
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@vitest/coverage-v8": "^4.0.15",
|
|
38
|
+
"mdast-util-from-markdown": "^2.0.2",
|
|
38
39
|
"tsup": "^8.5.1",
|
|
39
40
|
"vitest": "^4.0.15"
|
|
40
41
|
}
|