handlebars-editor-react 0.1.1 → 0.1.2

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/dist/index.cjs CHANGED
@@ -1,3 +1,4 @@
1
- 'use strict';Object.defineProperty(exports,'__esModule',{value:true});var react=require('react'),V=require('handlebars'),jsxRuntime=require('react/jsx-runtime');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var V__default=/*#__PURE__*/_interopDefault(V);var Y=V__default.default.Parser,P=Y.lexer,me=Y.terminals_,ge={CONTENT:"text",COMMENT:"comment",OPEN:"brace",CLOSE:"brace",OPEN_UNESCAPED:"brace",CLOSE_UNESCAPED:"brace",OPEN_RAW_BLOCK:"brace",CLOSE_RAW_BLOCK:"brace",END_RAW_BLOCK:"brace",OPEN_BLOCK:"brace",OPEN_INVERSE:"brace",OPEN_INVERSE_CHAIN:"brace",OPEN_ENDBLOCK:"brace",OPEN_PARTIAL:"brace",OPEN_PARTIAL_BLOCK:"brace",OPEN_SEXPR:"subexpr-paren",CLOSE_SEXPR:"subexpr-paren",OPEN_BLOCK_PARAMS:"block-keyword",CLOSE_BLOCK_PARAMS:"block-keyword",INVERSE:"block-keyword",ID:"variable",STRING:"literal",NUMBER:"literal",BOOLEAN:"literal",UNDEFINED:"literal",NULL:"literal",DATA:"data-var",SEP:"text",EQUALS:"text"};function J(r,a,e){let t=0;for(let c=0;c<a-1&&c<r.length;c++)t+=r[c].length+1;return t+e}function Ee(r){let a=[],e=r.split(`
2
- `);P.setInput(r);try{for(let t=P.lex();t!==P.EOF;t=P.lex()){let c=me[t]||"UNKNOWN",n=P.yylloc;a.push({type:c,text:P.yytext,start:J(e,n.first_line,n.first_column),end:J(e,n.last_line,n.last_column)});}}catch{}return a}function xe(r,a){let e=r[a-1],t=r[a+1];return e&&(e.type==="OPEN_BLOCK"||e.type==="OPEN_INVERSE"||e.type==="OPEN_INVERSE_CHAIN"||e.type==="OPEN_ENDBLOCK")?"block-keyword":e?.type==="OPEN_PARTIAL"||e?.type==="OPEN_PARTIAL_BLOCK"||e?.type==="OPEN_SEXPR"?"helper":e?.type==="OPEN"||e?.type==="OPEN_UNESCAPED"?t&&(t.type==="ID"||t.type==="SEP"||t.type==="STRING"||t.type==="NUMBER"||t.type==="BOOLEAN"||t.type==="DATA"||t.type==="OPEN_SEXPR")?"helper":"variable":e?.type==="EQUALS"?"hash-value":e?.type==="ID"?t?.type==="EQUALS"?"hash-key":"helper-arg":e?.type==="DATA"||e?.type==="SEP"?"helper-arg":e?.type==="OPEN_BLOCK_PARAMS"?"block-param":"variable"}function ye(r,a){let e=[],t=0;for(let c=0;c<a.length;c++){let n=a[c];n.start>t&&e.push({type:"text",value:r.slice(t,n.start),start:t,end:n.start});let b=ge[n.type]||"text";if(n.type==="ID"&&(b=xe(a,c)),n.type==="DATA"){let d=a[c+1];if(d?.type==="ID"&&d.start===n.end){e.push({type:"data-var",value:r.slice(n.start,d.end),start:n.start,end:d.end}),t=d.end,c++;continue}}e.push({type:b,value:r.slice(n.start,n.end),start:n.start,end:n.end}),t=n.end;}return t<r.length&&e.push({type:"text",value:r.slice(t),start:t,end:r.length}),e}function L(r){if(!r)return [];let a=Ee(r);return ye(r,a).filter(e=>e.value.length>0)}var Z=new Set(["if","unless","each","with","lookup","log","this","else"]),M=new Set(["each","with"]);function K(r){return Z.has(r)||r.startsWith("@")}var ee=V__default.default.Parser,O=ee.lexer,Oe=ee.terminals_;function Ne(r){let a=new Map,e=r;for(;e.length>0;){let t=ke(e,a);if(t===e.length)break;let c=e.slice(t),n=c.search(/\{\{/);if(n===-1)break;e=c.slice(n);}return Array.from(a.values())}function ke(r,a){O.setInput(r),O.conditionStack=["INITIAL"];let e=0,t=false,c=null,n=[],b=[];function d(i,l){let f=[];for(let o=l;o<i.length;o++)if(i[o].name==="ID")f.push(i[o].text);else if(i[o].name!=="SEP")break;return f}function x(i,l,f){a.has(l)||a.set(l,{name:i,path:l,type:f?"block":l.includes(".")?"nested":"simple",blockType:f});}function _(){if(n.length===0)return;let i=0;if(n[i]?.name==="DATA")return;let l=n[i].text;if(c==="block"){let f=b.some(o=>M.has(o.helper));if(M.has(l)){for(i++;i<n.length&&n[i].name!=="ID";)i++;if(i<n.length){let o=d(n,i);o.length>0&&!K(o[0])&&!f&&x(o[0],o.join("."),l),b.push({helper:l,context:o[0]||""});}}else if(Z.has(l)){for(i++;i<n.length&&n[i].name!=="ID";)i++;if(i<n.length){let o=d(n,i);o.length>0&&!K(o[0])&&!f&&x(o[0],o.join("."),l);}b.push({helper:l,context:""});}else b.push({helper:l,context:""});}else if(c==="endblock")b.length>0&&b.pop();else {let f=b.some(o=>M.has(o.helper));if(!K(l)&&!f){let o=d(n,i);o.length>0&&x(o[0],o.join("."));}}}try{for(let i=O.lex();i!==O.EOF;i=O.lex()){let l=Oe[i],f=O.yytext;if(e=O.yylloc?.last_column??e,!(l==="OPEN_RAW_BLOCK"||l==="CLOSE_RAW_BLOCK"||l==="END_RAW_BLOCK")){if(l==="OPEN"||l==="OPEN_UNESCAPED"){t=!0,c="mustache",n=[];continue}if(l==="OPEN_BLOCK"||l==="OPEN_INVERSE"||l==="OPEN_INVERSE_CHAIN"){t=!0,c="block",n=[];continue}if(l==="OPEN_ENDBLOCK"){t=!0,c="endblock",n=[];continue}if(l==="CLOSE"||l==="CLOSE_UNESCAPED"){_(),t=!1,n=[];continue}t&&n.push({name:l,text:f});}}return r.length}catch{return e}}function U(r){let a=Ne(r),e=[...new Set(a.map(t=>t.name))];return {variables:a,rootVariables:e}}function Pe(r,a,e){if(!a)return r;let t=e?.helpers?V__default.default.create():V__default.default;if(e?.helpers)for(let[c,n]of Object.entries(e.helpers))t.registerHelper(c,n);return t.compile(r)(a)}var Se=["#if","#each","#with","#unless","/if","/each","/with","/unless","else"];function re({value:r,onChange:a,placeholder:e="Enter template...",readOnly:t=false,className:c="",style:n,theme:b,customHelpers:d=[],autocomplete:x=true,minHeight:_=100}){let i=react.useRef(null),l=react.useRef(null),f=react.useRef(null),[o,y]=react.useState({isOpen:false,options:[],selectedIndex:0,triggerStart:0,filterText:""}),[R,se]=react.useState({top:0,left:0}),z=react.useMemo(()=>U(r).rootVariables,[r]),F=react.useMemo(()=>[...Se,...d,...z],[d,z]),$=react.useMemo(()=>L(r),[r]),oe=react.useMemo(()=>r?$.map(s=>s.type==="text"?s.value:jsxRuntime.jsx("span",{className:`hbs-token-${s.type}`,children:s.value},s.start)):jsxRuntime.jsx("span",{className:"hbs-editor-placeholder",children:e}),[$,r,e]),ae=react.useCallback(s=>{let p=s.target.value;if(a?.(p),!x||t)return;let h=s.target.selectionStart,m=p.slice(0,h).match(/\{\{([#/]?\w*)$/);if(m){let g=m[1]||"",E=h-g.length,N=F.filter(w=>w.toLowerCase().startsWith(g.toLowerCase()));if(N.length>0){y({isOpen:true,options:N,selectedIndex:0,triggerStart:E,filterText:g});return}}y(g=>({...g,isOpen:false}));},[a,x,t,F]),v=react.useCallback(s=>{let p=i.current;if(!p)return;let{triggerStart:h}=o,u=s,m=s.length;if(s.startsWith("#")){let E=s.slice(1);u=`${s} }}{{/${E}}}`,m=s.length+1;}else !s.startsWith("/")&&s!=="else"?(u=`${s}}}`,m=u.length):(u=`${s}}}`,m=u.length);p.focus(),p.setSelectionRange(h,p.selectionStart),document.execCommand("insertText",false,u);let g=h+m;p.setSelectionRange(g,g),a?.(p.value),y(E=>({...E,isOpen:false}));},[a,o]),le=react.useCallback(s=>{let{scrollTop:p,scrollLeft:h}=s.currentTarget;l.current&&(l.current.scrollTop=p,l.current.scrollLeft=h),se({top:p,left:h});},[]),ie=react.useCallback(s=>{if(!o.isOpen)return;let{options:p,selectedIndex:h}=o;switch(s.key){case "ArrowDown":s.preventDefault(),y(u=>({...u,selectedIndex:Math.min(h+1,p.length-1)}));break;case "ArrowUp":s.preventDefault(),y(u=>({...u,selectedIndex:Math.max(h-1,0)}));break;case "Enter":case "Tab":s.preventDefault(),p[h]&&v(p[h]);break;case "Escape":s.preventDefault(),y(u=>({...u,isOpen:false}));break}},[o,v]),I=react.useMemo(()=>{if(!o.isOpen||!i.current)return {top:0,left:0,maxHeight:192,openUpward:false,visible:false};let s=i.current,{triggerStart:p}=o,u=r.slice(0,p).split(`
3
- `),m=u.length,g=u[u.length-1]?.length||0,E=getComputedStyle(s),N=parseFloat(E.lineHeight)||20,w=parseFloat(E.paddingTop)||0,he=parseFloat(E.paddingLeft)||0,ue=(parseFloat(E.fontSize)||14)*.6,X=getComputedStyle(s.parentElement||s),fe=parseInt(X.getPropertyValue("--hbs-autocomplete-min-width"))||180,j=parseInt(X.getPropertyValue("--hbs-autocomplete-max-height"))||192,k=w+m*N-R.top,A=he+g*ue-R.left,Q=s.clientWidth-fe-10;A>Q&&(A=Math.max(10,Q));let D=s.clientHeight-k-10,G=k-N-10,q=D<120&&G>D,B,C;q?(C=Math.min(j,Math.max(80,G)),B=k-N-C):(C=Math.min(j,Math.max(80,D)),B=k);let be=k>0&&k<s.clientHeight&&A>0&&A<s.clientWidth;return {top:B,left:A,maxHeight:C,openUpward:q,visible:be}},[o,r,R]),ce=react.useMemo(()=>{if(!b)return {};let s={},p={variable:"--hbs-color-variable",variablePath:"--hbs-color-variable-path",blockKeyword:"--hbs-color-block-keyword",blockParam:"--hbs-color-block-param",helper:"--hbs-color-helper",helperArg:"--hbs-color-helper-arg",hashKey:"--hbs-color-hash-key",hashValue:"--hbs-color-hash-value",literal:"--hbs-color-literal",dataVar:"--hbs-color-data-var",subexprParen:"--hbs-color-subexpr-paren",comment:"--hbs-color-comment",raw:"--hbs-color-raw",brace:"--hbs-color-brace",text:"--hbs-color-text",background:"--hbs-color-background",caret:"--hbs-color-caret",border:"--hbs-color-border",placeholder:"--hbs-color-placeholder"};for(let[h,u]of Object.entries(p)){let m=b[h];m&&(s[u]=m);}return s},[b]);react.useEffect(()=>{if(!o.isOpen||!f.current)return;let h=f.current.querySelectorAll(".hbs-autocomplete-item")[o.selectedIndex];h&&h.scrollIntoView({block:"nearest"});},[o.selectedIndex,o.isOpen]);let pe=["hbs-editor",t?"hbs-readonly":"",c].filter(Boolean).join(" ");return jsxRuntime.jsxs("div",{className:pe,style:{...ce,minHeight:typeof _=="number"?`${_}px`:_,...n},children:[jsxRuntime.jsx("div",{ref:l,className:"hbs-editor-highlight",children:oe}),jsxRuntime.jsx("textarea",{ref:i,className:"hbs-editor-textarea",value:r,onChange:ae,onKeyDown:ie,onScroll:le,placeholder:"",readOnly:t,spellCheck:false,autoCapitalize:"off",autoComplete:"off",autoCorrect:"off"}),x&&o.isOpen&&I.visible&&jsxRuntime.jsx("div",{ref:f,className:"hbs-autocomplete",style:{top:I.top,left:I.left,maxHeight:I.maxHeight},children:o.options.map((s,p)=>jsxRuntime.jsx("button",{type:"button",className:`hbs-autocomplete-item ${p===o.selectedIndex?"hbs-selected":""}`,onClick:()=>v(s),onMouseEnter:()=>y(h=>({...h,selectedIndex:p})),children:s},s))})]})}function _e({content:r,className:a}){if(!r)return null;let e=L(r);return jsxRuntime.jsx("span",{className:a,children:e.map(t=>jsxRuntime.jsx("span",{className:`hbs-token-${t.type}`,children:t.value},t.start))})}exports.HandlebarsEditor=re;exports.HandlebarsHighlight=_e;exports.default=re;exports.extract=U;exports.interpolate=Pe;exports.tokenize=L;
1
+ 'use strict';Object.defineProperty(exports,'__esModule',{value:true});var react=require('react'),ne=require('handlebars'),jsxRuntime=require('react/jsx-runtime');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var ne__default=/*#__PURE__*/_interopDefault(ne);var ue=ne__default.default.Parser,M=ue.lexer,Re=ue.terminals_,we=new Set(["OPEN_BLOCK","OPEN_INVERSE","OPEN_INVERSE_CHAIN","OPEN_ENDBLOCK"]),ve=new Set(["OPEN","OPEN_UNESCAPED"]),Be=new Set(["OPEN_PARTIAL","OPEN_PARTIAL_BLOCK"]),De=new Set(["OPEN","OPEN_UNESCAPED","OPEN_BLOCK","OPEN_INVERSE","OPEN_INVERSE_CHAIN","OPEN_ENDBLOCK","OPEN_PARTIAL","OPEN_PARTIAL_BLOCK"]),He=new Set(["CLOSE","CLOSE_UNESCAPED","CLOSE_RAW_BLOCK","END_RAW_BLOCK"]),Me=new Set(["ID","STRING","NUMBER","BOOLEAN","DATA","OPEN_SEXPR","UNDEFINED","NULL"]),fe=t=>we.has(t),he=t=>ve.has(t),de=t=>Be.has(t),Ke=t=>De.has(t),Ve=t=>He.has(t),Ue=t=>Me.has(t),Fe={CONTENT:"text",COMMENT:"comment",OPEN:"brace",CLOSE:"brace",OPEN_UNESCAPED:"brace",CLOSE_UNESCAPED:"brace",OPEN_RAW_BLOCK:"brace",CLOSE_RAW_BLOCK:"brace",END_RAW_BLOCK:"brace",OPEN_BLOCK:"brace",OPEN_INVERSE:"brace",OPEN_INVERSE_CHAIN:"brace",OPEN_ENDBLOCK:"brace",OPEN_PARTIAL:"brace",OPEN_PARTIAL_BLOCK:"brace",OPEN_SEXPR:"subexpr-paren",CLOSE_SEXPR:"subexpr-paren",OPEN_BLOCK_PARAMS:"block-keyword",CLOSE_BLOCK_PARAMS:"block-keyword",INVERSE:"block-keyword",ID:"variable",STRING:"literal",NUMBER:"literal",BOOLEAN:"literal",UNDEFINED:"literal",NULL:"literal",DATA:"data-var",SEP:"brace",EQUALS:"brace"};function pe(t,n,r){let e=0;for(let s=0;s<n-1&&s<t.length;s++)e+=t[s].length+1;return e+r}function We(t){let n=[],r=t.split(`
2
+ `);M.setInput(t);try{for(let e=M.lex();e!==M.EOF;e=M.lex()){let s=Re[e]||"UNKNOWN",o=M.yylloc;n.push({type:s,text:M.yytext,start:pe(r,o.first_line,o.first_column),end:pe(r,o.last_line,o.last_column)});}}catch{}return n}function $e(t){return t.map(n=>n.type==="ID"&&n.text==="."?{...n,type:"SEP"}:n.type==="ID"&&n.text==="="?{...n,type:"EQUALS"}:n)}function ze(t){let n=[],r=null,e=false,s=false,o=0;for(let c=0;c<t.length;c++){let i=t[c],E=t[c-1];Ke(i.type)?(fe(i.type)?r=i.type==="OPEN_ENDBLOCK"?"block-close":"block-open":de(i.type)?r="partial":he(i.type)&&(r="mustache"),s=false,o=0):Ve(i.type)&&(r=null,s=false,o=0),i.type==="OPEN_BLOCK_PARAMS"?e=true:i.type==="CLOSE_BLOCK_PARAMS"&&(e=false),i.type==="SEP"?o++:i.type==="ID"&&E?.type!=="SEP"&&E?.type!=="DATA"&&(o=0);let A=false;if(i.type==="ID"){if(o===0)A=s;else for(let P=c-1;P>=0;P--)if(t[P].type==="ID"&&n[P]?.context.pathRootAfterHelper!==void 0){let g=t[P-1];if(g?.type!=="SEP"&&g?.type!=="DATA"){A=n[P].context.pathRootAfterHelper;break}}}n.push({...i,context:{type:r,isInBlockParams:e,pathRootAfterHelper:A}}),i.type==="ID"&&o===0&&!e&&E?.type!=="EQUALS"&&E?.type!=="DATA"&&(s=true);}return n}function Xe(t,n,r){let{context:e}=t;return t.text==="this"?"data-var":e.isInBlockParams?"block-param":r?.type==="EQUALS"?"hash-key":n?.type==="EQUALS"?"hash-value":n?.type==="DATA"?"data-var":n?.type==="SEP"?e.pathRootAfterHelper?"helper-arg":"variable-path":Qe(t,n,r)}function Qe(t,n,r){let{context:e}=t;return e.type==="block-open"||e.type==="block-close"?fe(n?.type??"")?"block-keyword":"helper-arg":e.type==="partial"?de(n?.type??"")?"helper":"helper-arg":n?.type==="OPEN_SEXPR"?"helper":e.type==="mustache"?he(n?.type??"")?r?.type==="SEP"?"variable":r&&Ue(r.type)?"helper":"variable":"helper-arg":"variable"}function Ge(t){return t.map((n,r)=>{let e=t[r-1],s=t[r+1],o;return n.type==="ID"?o=Xe(n,e,s):o=Fe[n.type]||"text",{token:n,highlightType:o}})}function je(t,n){let r=[],e=0;for(let s=0;s<n.length;s++){let{token:o,highlightType:c}=n[s];if(o.start>e&&r.push({type:"text",value:t.slice(e,o.start),start:e,end:o.start}),o.type==="DATA"){let i=n[s+1];if(i?.token.type==="ID"&&i.token.start===o.end){r.push({type:"data-var",value:t.slice(o.start,i.token.end),start:o.start,end:i.token.end}),e=i.token.end,s++;continue}}r.push({type:c,value:t.slice(o.start,o.end),start:o.start,end:o.end}),e=o.end;}return e<t.length&&r.push({type:"text",value:t.slice(e),start:e,end:t.length}),r}function K(t){if(!t)return [];let n=We(t),r=$e(n),e=ze(r),s=Ge(e);return je(t,s).filter(c=>c.value.length>0)}var G=["if","unless","each","with"],me=new Set([...G,"lookup","log","this","else"]),xe=new Set(["each","with"]),qe=new Set(["ID","STRING","NUMBER","BOOLEAN","DATA","OPEN_SEXPR"]),Ee=ne__default.default.Parser,D=Ee.lexer,Ye=Ee.terminals_;function z(t){return me.has(t)||t.startsWith("@")}function Je(t){return qe.has(t.name)}function X(t,n){let r=[],e=true;for(let s=n;s<t.length;s++){let o=t[s];if(o.name==="EQUALS")break;if(o.name==="ID"){if(!e||t[s+1]?.name==="EQUALS")break;r.push(o.text),e=false;}else if(o.name==="SEP")e=true;else break}return r}function Ze(t,n,r,e){return {name:t,path:n,type:r?"block":n.includes(".")||e?"nested":"simple",blockType:r,context:e}}function et(t){let n=[],r=null;for(let e of t)xe.has(e.helper)&&e.context&&(r||(r=e.context),n.push(e.helper==="each"?`${e.context}[]`:e.context));return n.length===0||!r?null:{root:r,prefix:`${n.join(".")}.`}}function tt(t){let n=[],r=t;for(;r.length>0;){let e=nt(r);if(n.push(...e.expressions),e.consumed===r.length)break;let s=r.slice(e.consumed),o=s.search(/\{\{/);if(o===-1)break;r=s.slice(o);}return {expressions:n}}function nt(t){D.setInput(t),D.conditionStack=["INITIAL"];let n=[],r=0,e=false,s=null,o=[];try{for(let c=D.lex();c!==D.EOF;c=D.lex()){let i=Ye[c],E=D.yytext;if(r=D.yylloc?.last_column??r,!(i==="OPEN_RAW_BLOCK"||i==="CLOSE_RAW_BLOCK"||i==="END_RAW_BLOCK")){if(i==="OPEN"||i==="OPEN_UNESCAPED"){e=!0,s="mustache",o=[];continue}if(i==="OPEN_PARTIAL"||i==="OPEN_PARTIAL_BLOCK"){e=!0,s="partial",o=[];continue}if(i==="OPEN_BLOCK"||i==="OPEN_INVERSE"||i==="OPEN_INVERSE_CHAIN"){e=!0,s="block",o=[];continue}if(i==="OPEN_ENDBLOCK"){e=!0,s="endblock",o=[];continue}if(i==="CLOSE"||i==="CLOSE_UNESCAPED"){if(s){let A=o[0]?.text??null;n.push({type:s,tokens:[...o],helperName:A});}e=!1,o=[];continue}e&&o.push({name:i,text:E});}}return {expressions:n,consumed:t.length}}catch{return {expressions:n,consumed:r}}}function rt(t,n,r){let e=t.tokens;if(e.length===0||e[0]?.name==="DATA")return;let s=e[0]?.text;if(z(s))W(e,1,n,r);else {let o=X(e,0);if(o.length>0){let i=o.length*2-1,E=e[i];E&&Je(E)?W(e,i,n,r):Q(o,n,r);}}}function ot(t,n,r){let e=t.tokens,s=e[0]?.text;if(xe.has(s)){if(e[1]?.name==="OPEN_SEXPR")return W(e,1,n,r),{helper:s,context:""};{let o=1;for(;o<e.length&&e[o].name!=="ID";)o++;if(o<e.length){let c=X(e,o);return c.length>0&&!z(c[0])&&Q(c,n,r,s),{helper:s,context:c[0]||""}}return {helper:s,context:""}}}else if(me.has(s)){if(e[1]?.name==="OPEN_SEXPR")W(e,1,n,r);else {let o=1;for(;o<e.length&&e[o].name!=="ID";)o++;if(o<e.length){let c=X(e,o);c.length>0&&!z(c[0])&&Q(c,n,r,s);}}return {helper:s,context:""}}return {helper:s,context:""}}function st(t,n,r){let e=t.tokens,s=1;for(;s<e.length&&(e[s].name==="SEP"||e[s].name==="ID")&&!(e[s].name==="ID"&&e[s-1]?.name!=="SEP");)s++;W(e,s,n,r);}function W(t,n,r,e){for(let s=n;s<t.length;s++){let o=t[s];if(!(o.name==="OPEN_SEXPR"||o.name==="CLOSE_SEXPR")&&o.name!=="DATA"&&t[s-1]?.name!=="DATA"&&o.name!=="EQUALS"&&!(o.name==="ID"&&t[s+1]?.name==="EQUALS")&&o.name==="ID"&&!z(o.text)){if(t[s-1]?.name==="OPEN_SEXPR")continue;let i=X(t,s);i.length>0&&(Q(i,r,e),s+=i.length*2-2);}}}function Q(t,n,r,e){let s=t[0],o=n.contextPrefix&&n.rootContext?{prefix:n.contextPrefix,root:n.rootContext}:null,c,i;o?(c=o.prefix+t.join("."),i=o.root):(c=t.join("."),i=void 0),r.has(c)||r.set(c,Ze(s,c,e,i));}function at(t,n){return n.type==="endblock"&&t.length>0?t.slice(0,-1):t}function it(t){let n=new Map,r=[];for(let e of t){let s=et(r),o={contextPrefix:s?.prefix??null,rootContext:s?.root??null};if(e.type==="mustache")rt(e,o,n);else if(e.type==="block"){let c=ot(e,o,n);r=[...r,c];}else e.type==="partial"?st(e,o,n):e.type==="endblock"&&(r=at(r,e));}return Array.from(n.values())}function re(t){let{expressions:n}=tt(t),r=it(n),e=[...new Set(r.filter(s=>!s.context).map(s=>s.name))];return {variables:r,rootVariables:e}}function lt(t,n,r){if(!n)return t;let e=r?.helpers?ne__default.default.create():ne__default.default;if(r?.helpers)for(let[s,o]of Object.entries(r.helpers))e.registerHelper(s,o);return e.compile(t)(n)}var pt=["this","@index","@first","@last","@key"],ut=["@root"];function ft(t,n){if(!n)return "prefix";let r=t.toLowerCase(),e=n.toLowerCase();return r===e?"exact":r.startsWith(e)?"prefix":r.includes(e)?"partial":"none"}function ht(t,n,r,e,s){let o=[],c=new Set,i=s.lastIndexOf("."),E=i!==-1;E?s.slice(0,i+1):"";let P=E?s.slice(i+1):s,g=(m,l)=>{if(c.has(m))return;let b=ft(m,P);b!=="none"&&(c.add(m),o.push({option:{value:m,type:l},matchType:b}));};if(E){let m=s.slice(0,i).replace(/\[\]$/,"");n.forEach(l=>{let b=l.path.replace(/\[\]/g,"");if(b.startsWith(`${m}.`)){let q=b.slice(m.length+1).split(".")[0];g(q,"variable");}});}else {if(!r){for(let l of G)g(`#${l}`,"block-open");for(let l of G)g(`/${l}`,"block-close");g("else","block-close");}for(let l of t)g(l,"helper");if(e){if(e.isEach)for(let l of pt)g(l,"data-var");for(let l of e.blockParams)g(l,"variable");for(let l of ut)g(l,"data-var");}let m=new Set;for(let l of n){let b=l.path.split(".")[0].replace("[]","");m.add(b);}for(let l of m)g(l,"variable");}let v={"data-var":0,variable:1,helper:2,"block-open":3,"block-close":4},_={exact:0,prefix:1,partial:2,none:3};return o.sort((m,l)=>{let b=_[m.matchType]-_[l.matchType];if(b!==0)return b;let B=v[m.option.type]-v[l.option.type];return B!==0?B:m.option.value.localeCompare(l.option.value)}),o.map(m=>m.option)}function dt(t,n){let r=t.slice(0,n),e=[],s=/\{\{#(each|with)\s+([^\s}]+)(?:\s+as\s*\|([^|]+)\|)?|\{\{\/(each|with)\}\}/g;for(let o of r.matchAll(s))if(o[1]){let c=o[1],i=o[2],A=(o[3]||"").split(/\s+/).filter(Boolean);e.push({type:c,variable:i,blockParams:A,isEach:c==="each"});}else if(o[4]){let c=o[4];for(let i=e.length-1;i>=0;i--)if(e[i].type===c){e.splice(i,1);break}}return e.length>0?e[e.length-1]:null}function mt(t,n){let r=t.slice(0,n),e=r.lastIndexOf("{{!--");if(e!==-1&&r.slice(e).indexOf("--}}")===-1)return true;let s=r.lastIndexOf("{{!");if(s!==-1){let o=r.slice(s);if(!o.startsWith("{{!--")&&o.indexOf("}}")===-1)return true}return false}function xt(t,n){let r=t.slice(0,n),e=t.slice(n),s=r.lastIndexOf("{{");if(s===-1||r.slice(s).includes("}}"))return false;let c=e.indexOf("}}");return c===-1?false:!e.slice(0,c).includes("{{")}function ye({value:t,onChange:n,placeholder:r="Enter template...",readOnly:e=false,className:s="",style:o,theme:c,customHelpers:i=[],autocomplete:E=true,minHeight:A=100}){let P=react.useRef(null),g=react.useRef(null),v=react.useRef(null),_=react.useRef(null),m=react.useId(),[l,b]=react.useState({isOpen:false,options:[],selectedIndex:0,triggerStart:0,filterText:"",isRaw:false,contextPath:null}),[B,q]=react.useState({top:0,left:0}),se=react.useMemo(()=>re(t).variables.map(p=>({path:p.path,context:p.context})),[t]),ae=react.useMemo(()=>K(t),[t]),Oe=react.useMemo(()=>t?ae.map(a=>a.type==="text"?a.value:jsxRuntime.jsx("span",{className:`hbs-token-${a.type}`,children:a.value},a.start)):jsxRuntime.jsx("span",{className:"hbs-editor-placeholder",children:r}),[ae,t,r]),k=react.useCallback(()=>{b(a=>({...a,isOpen:false}));},[]),ie=react.useCallback((a,p)=>{if(!E||e){k();return}if(mt(a,p)||xt(a,p)){k();return}let d=a.slice(0,p).match(/(\{\{\{?)([#/]?[\w.@]*)$/);if(!d){k();return}let x=d[1],f=d[2]||"",T=p-f.length,h=x==="{{{";if(f.includes("}")){k();return}let y=dt(a,p),O=ht(i,se,h,y,f);if(O.length===0){k();return}b({isOpen:true,options:O,selectedIndex:0,triggerStart:T,filterText:f,isRaw:h,contextPath:y});},[E,e,i,se,k]),Ae=react.useCallback(a=>{let p=a.target.value;n?.(p),ie(p,a.target.selectionStart);},[n,ie]),U=react.useCallback(a=>{let p=g.current;if(!p)return;let{triggerStart:u,filterText:d,isRaw:x}=l,f=x?"}}}":"}}",T=d.lastIndexOf("."),h=T!==-1?d.slice(0,T+1):"",y,O;if(a.type==="block-open"&&!x){let N=a.value.slice(1);y=`${a.value} }}{{/${N}}}`,O=a.value.length+1;}else a.type!=="block-close"&&a.value!=="else"?(y=`${h}${a.value}${f}`,O=y.length):(y=`${a.value}${f}`,O=y.length);p.focus(),p.setSelectionRange(u,p.selectionStart),document.execCommand("insertText",false,y);let I=u+O;p.setSelectionRange(I,I),n?.(p.value),k();},[l,n,k]),Te=react.useCallback(a=>{let{scrollTop:p,scrollLeft:u}=a.currentTarget;v.current&&(v.current.scrollTop=p,v.current.scrollLeft=u),q({top:p,left:u});},[]),Pe=react.useCallback(a=>{_.current?.contains(a.relatedTarget)||k();},[k]),Y=react.useCallback(()=>{let a=g.current;if(!a||!l.isOpen)return;let p=a.selectionStart;t.slice(0,p).match(/(\{\{\{?)([#/]?[\w.@]*)$/)||k();},[l.isOpen,t,k]),Ne=react.useCallback(a=>{let p=a.currentTarget;if(a.key==="Tab"){if(l.isOpen){a.preventDefault();let T=l.options[l.selectedIndex];T&&U(T);return}a.preventDefault();let{selectionStart:f}=p;if(a.shiftKey){let h=t.slice(0,f).lastIndexOf(`
3
+ `)+1,O=t.slice(h,f).match(/^(\t| {2})/);if(O){let I=O[1].length,N=t.slice(0,h)+t.slice(h+I);n?.(N);let S=Math.max(h,f-I);setTimeout(()=>p.setSelectionRange(S,S),0);}}else document.execCommand("insertText",false," "),n?.(p.value);return}if((a.metaKey||a.ctrlKey)&&(a.key==="z"||a.key==="y")||!l.isOpen)return;let{options:u,selectedIndex:d}=l,x=8;switch(a.key){case "ArrowDown":a.preventDefault(),b(f=>({...f,selectedIndex:Math.min(d+1,u.length-1)}));break;case "ArrowUp":a.preventDefault(),b(f=>({...f,selectedIndex:Math.max(d-1,0)}));break;case "Home":a.preventDefault(),b(f=>({...f,selectedIndex:0}));break;case "End":a.preventDefault(),b(f=>({...f,selectedIndex:u.length-1}));break;case "PageDown":a.preventDefault(),b(f=>({...f,selectedIndex:Math.min(d+x,u.length-1)}));break;case "PageUp":a.preventDefault(),b(f=>({...f,selectedIndex:Math.max(d-x,0)}));break;case "Enter":a.preventDefault(),u[d]&&U(u[d]);break;case "Escape":a.preventDefault(),k();break;case "ArrowLeft":case "ArrowRight":setTimeout(()=>Y(),0);break}},[l,U,k,Y,t,n]),J=react.useMemo(()=>{if(!l.isOpen||!g.current)return null;let a=g.current,p=getComputedStyle(a),u=getComputedStyle(a.parentElement||a);return {lineHeight:parseFloat(p.lineHeight)||20,paddingTop:parseFloat(p.paddingTop)||0,paddingLeft:parseFloat(p.paddingLeft)||0,charWidth:(parseFloat(p.fontSize)||14)*.6,dropdownWidth:parseInt(u.getPropertyValue("--hbs-autocomplete-min-width"),10)||180,dropdownMaxHeight:parseInt(u.getPropertyValue("--hbs-autocomplete-max-height"),10)||200,clientWidth:a.clientWidth,clientHeight:a.clientHeight}},[l.isOpen]),H=react.useMemo(()=>{if(!l.isOpen||!J)return {top:void 0,bottom:void 0,left:0,maxHeight:200,openUpward:false,visible:false};let{triggerStart:a}=l,{lineHeight:p,paddingTop:u,paddingLeft:d,charWidth:x,dropdownWidth:f,dropdownMaxHeight:T,clientWidth:h,clientHeight:y}=J,I=t.slice(0,a).split(`
4
+ `),N=I.length,S=I[I.length-1]?.length||0,R=u+N*p-B.top,C=d+S*x-B.left,$=h-f-10;C>$&&(C=Math.max(10,$));let F=y-R-10,le=R-p-10,ce=F<120&&le>F,Z,ee,te;ce?(te=Math.min(T,Math.max(80,le)),ee=y-R+p,Z=void 0):(te=Math.min(T,Math.max(80,F)),Z=R,ee=void 0);let Le=R>=0&&R<y&&C>=0&&C<h;return {top:Z,bottom:ee,left:C,maxHeight:te,openUpward:ce,visible:Le}},[l,t,B,J]),Se=react.useMemo(()=>{if(!c)return {};let a={},p={variable:"--hbs-color-variable",variablePath:"--hbs-color-variable-path",blockKeyword:"--hbs-color-block-keyword",blockParam:"--hbs-color-block-param",helper:"--hbs-color-helper",helperArg:"--hbs-color-helper-arg",hashKey:"--hbs-color-hash-key",hashValue:"--hbs-color-hash-value",literal:"--hbs-color-literal",dataVar:"--hbs-color-data-var",subexprParen:"--hbs-color-subexpr-paren",comment:"--hbs-color-comment",raw:"--hbs-color-raw",brace:"--hbs-color-brace",text:"--hbs-color-text",background:"--hbs-color-background",caret:"--hbs-color-caret",border:"--hbs-color-border",placeholder:"--hbs-color-placeholder"};for(let[u,d]of Object.entries(p)){let x=c[u];x&&(a[d]=x);}return a},[c]);react.useEffect(()=>{if(!l.isOpen||!_.current)return;let u=_.current.querySelectorAll(".hbs-autocomplete-item")[l.selectedIndex];u&&u.scrollIntoView({block:"nearest"});},[l.selectedIndex,l.isOpen]),react.useEffect(()=>{if(!l.isOpen)return;let a=P.current,p=d=>{let x=d.target;a&&!a.contains(x)&&k();},u=d=>{let x=d.target;a?.contains(x)&&!_.current?.contains(x)&&x!==g.current&&k();};return document.addEventListener("mousedown",p),a?.addEventListener("mousedown",u),()=>{document.removeEventListener("mousedown",p),a?.removeEventListener("mousedown",u);}},[l.isOpen,k]);let Ce=react.useCallback((a,p)=>{let{filterText:u,selectedIndex:d}=l,x=p===d,f=u.lastIndexOf("."),T=f!==-1?u.slice(f+1):u,h=a.value;(a.type==="block-open"&&h.startsWith("#")||a.type==="block-close"&&h.startsWith("/")||a.type==="data-var"&&h.startsWith("@"))&&(h=h.slice(1));let y,O=T.toLowerCase().replace(/^[#/@]/,""),N=h.toLowerCase().indexOf(O);if(O.length>0&&N!==-1){let C=h.slice(0,N),$=h.slice(N,N+O.length),F=h.slice(N+O.length);y=jsxRuntime.jsxs(jsxRuntime.Fragment,{children:[C,jsxRuntime.jsx("span",{className:"hbs-autocomplete-match",children:$}),F]});}else y=h;let S="";switch(a.type){case "block-open":S="#";break;case "block-close":S="/";break;case "data-var":S="@";break;case "helper":S="\u0192";break;case "variable":S="\xB7";break}let R=jsxRuntime.jsx("span",{className:"hbs-autocomplete-type",children:S});return jsxRuntime.jsxs("button",{id:`${m}-option-${p}`,type:"button",role:"option","aria-selected":x,className:`hbs-autocomplete-item hbs-autocomplete-${a.type} ${x?"hbs-selected":""}`,onMouseDown:C=>{C.preventDefault(),U(a);},onMouseEnter:()=>b(C=>({...C,selectedIndex:p})),children:[R,jsxRuntime.jsx("span",{className:"hbs-autocomplete-value",children:y})]},a.value)},[l,U,m]),Ie=["hbs-editor",e?"hbs-readonly":"",s].filter(Boolean).join(" ");return jsxRuntime.jsxs("div",{ref:P,className:Ie,style:{...Se,minHeight:typeof A=="number"?`${A}px`:A,...o},children:[jsxRuntime.jsx("div",{ref:v,className:"hbs-editor-highlight",children:Oe}),jsxRuntime.jsx("textarea",{ref:g,className:"hbs-editor-textarea",value:t,onChange:Ae,onKeyDown:Ne,onScroll:Te,onBlur:Pe,onSelect:Y,placeholder:"",readOnly:e,spellCheck:false,autoCapitalize:"off",autoComplete:"off",autoCorrect:"off",role:"combobox","aria-haspopup":"listbox","aria-expanded":E&&l.isOpen&&H.visible,"aria-controls":m,"aria-activedescendant":E&&l.isOpen&&l.options[l.selectedIndex]?`${m}-option-${l.selectedIndex}`:void 0}),E&&l.isOpen&&H.visible&&jsxRuntime.jsx("div",{ref:_,id:m,role:"listbox","aria-label":"Autocomplete suggestions",className:"hbs-autocomplete",style:{top:H.top,bottom:H.bottom,left:H.left,maxHeight:H.maxHeight},children:l.options.map((a,p)=>Ce(a,p))})]})}function bt({content:t,className:n}){if(!t)return null;let r=K(t);return jsxRuntime.jsx("span",{className:n,children:r.map(e=>jsxRuntime.jsx("span",{className:`hbs-token-${e.type}`,children:e.value},e.start))})}exports.HandlebarsEditor=ye;exports.HandlebarsHighlight=bt;exports.default=ye;exports.extract=re;exports.interpolate=lt;exports.tokenize=K;
package/dist/index.d.cts CHANGED
@@ -106,20 +106,41 @@ interface HandlebarsEditorProps {
106
106
  /** Minimum height */
107
107
  minHeight?: string | number;
108
108
  }
109
+ /**
110
+ * Autocomplete option with metadata
111
+ */
112
+ interface AutocompleteOption {
113
+ /** Display value (the part to insert) */
114
+ value: string;
115
+ /** Type of option for styling */
116
+ type: 'block-open' | 'block-close' | 'helper' | 'variable' | 'data-var';
117
+ }
118
+ /**
119
+ * Block context information
120
+ */
121
+ interface BlockContext {
122
+ type: 'each' | 'with';
123
+ variable: string;
124
+ blockParams: string[];
125
+ isEach: boolean;
126
+ }
109
127
  /**
110
128
  * Autocomplete state
111
129
  */
112
130
  interface AutocompleteState {
113
131
  isOpen: boolean;
114
- options: string[];
132
+ options: AutocompleteOption[];
115
133
  selectedIndex: number;
134
+ /** Position where the replaceable text starts (after {{) */
116
135
  triggerStart: number;
136
+ /** Current filter text typed by user */
117
137
  filterText: string;
138
+ /** Whether triggered by triple braces {{{ for raw output */
139
+ isRaw: boolean;
140
+ /** Current block context if inside #each/#with */
141
+ contextPath: BlockContext | null;
118
142
  }
119
143
 
120
- /**
121
- * Handlebars template editor with syntax highlighting
122
- */
123
144
  declare function HandlebarsEditor({ value, onChange, placeholder, readOnly, className, style, theme, customHelpers, autocomplete, minHeight, }: HandlebarsEditorProps): react_jsx_runtime.JSX.Element;
124
145
 
125
146
  interface HandlebarsHighlightProps {
package/dist/index.d.ts CHANGED
@@ -106,20 +106,41 @@ interface HandlebarsEditorProps {
106
106
  /** Minimum height */
107
107
  minHeight?: string | number;
108
108
  }
109
+ /**
110
+ * Autocomplete option with metadata
111
+ */
112
+ interface AutocompleteOption {
113
+ /** Display value (the part to insert) */
114
+ value: string;
115
+ /** Type of option for styling */
116
+ type: 'block-open' | 'block-close' | 'helper' | 'variable' | 'data-var';
117
+ }
118
+ /**
119
+ * Block context information
120
+ */
121
+ interface BlockContext {
122
+ type: 'each' | 'with';
123
+ variable: string;
124
+ blockParams: string[];
125
+ isEach: boolean;
126
+ }
109
127
  /**
110
128
  * Autocomplete state
111
129
  */
112
130
  interface AutocompleteState {
113
131
  isOpen: boolean;
114
- options: string[];
132
+ options: AutocompleteOption[];
115
133
  selectedIndex: number;
134
+ /** Position where the replaceable text starts (after {{) */
116
135
  triggerStart: number;
136
+ /** Current filter text typed by user */
117
137
  filterText: string;
138
+ /** Whether triggered by triple braces {{{ for raw output */
139
+ isRaw: boolean;
140
+ /** Current block context if inside #each/#with */
141
+ contextPath: BlockContext | null;
118
142
  }
119
143
 
120
- /**
121
- * Handlebars template editor with syntax highlighting
122
- */
123
144
  declare function HandlebarsEditor({ value, onChange, placeholder, readOnly, className, style, theme, customHelpers, autocomplete, minHeight, }: HandlebarsEditorProps): react_jsx_runtime.JSX.Element;
124
145
 
125
146
  interface HandlebarsHighlightProps {
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
- import {useRef,useState,useMemo,useCallback,useEffect}from'react';import V from'handlebars';import {jsx,jsxs}from'react/jsx-runtime';var Y=V.Parser,P=Y.lexer,me=Y.terminals_,ge={CONTENT:"text",COMMENT:"comment",OPEN:"brace",CLOSE:"brace",OPEN_UNESCAPED:"brace",CLOSE_UNESCAPED:"brace",OPEN_RAW_BLOCK:"brace",CLOSE_RAW_BLOCK:"brace",END_RAW_BLOCK:"brace",OPEN_BLOCK:"brace",OPEN_INVERSE:"brace",OPEN_INVERSE_CHAIN:"brace",OPEN_ENDBLOCK:"brace",OPEN_PARTIAL:"brace",OPEN_PARTIAL_BLOCK:"brace",OPEN_SEXPR:"subexpr-paren",CLOSE_SEXPR:"subexpr-paren",OPEN_BLOCK_PARAMS:"block-keyword",CLOSE_BLOCK_PARAMS:"block-keyword",INVERSE:"block-keyword",ID:"variable",STRING:"literal",NUMBER:"literal",BOOLEAN:"literal",UNDEFINED:"literal",NULL:"literal",DATA:"data-var",SEP:"text",EQUALS:"text"};function J(r,a,e){let t=0;for(let c=0;c<a-1&&c<r.length;c++)t+=r[c].length+1;return t+e}function Ee(r){let a=[],e=r.split(`
2
- `);P.setInput(r);try{for(let t=P.lex();t!==P.EOF;t=P.lex()){let c=me[t]||"UNKNOWN",n=P.yylloc;a.push({type:c,text:P.yytext,start:J(e,n.first_line,n.first_column),end:J(e,n.last_line,n.last_column)});}}catch{}return a}function xe(r,a){let e=r[a-1],t=r[a+1];return e&&(e.type==="OPEN_BLOCK"||e.type==="OPEN_INVERSE"||e.type==="OPEN_INVERSE_CHAIN"||e.type==="OPEN_ENDBLOCK")?"block-keyword":e?.type==="OPEN_PARTIAL"||e?.type==="OPEN_PARTIAL_BLOCK"||e?.type==="OPEN_SEXPR"?"helper":e?.type==="OPEN"||e?.type==="OPEN_UNESCAPED"?t&&(t.type==="ID"||t.type==="SEP"||t.type==="STRING"||t.type==="NUMBER"||t.type==="BOOLEAN"||t.type==="DATA"||t.type==="OPEN_SEXPR")?"helper":"variable":e?.type==="EQUALS"?"hash-value":e?.type==="ID"?t?.type==="EQUALS"?"hash-key":"helper-arg":e?.type==="DATA"||e?.type==="SEP"?"helper-arg":e?.type==="OPEN_BLOCK_PARAMS"?"block-param":"variable"}function ye(r,a){let e=[],t=0;for(let c=0;c<a.length;c++){let n=a[c];n.start>t&&e.push({type:"text",value:r.slice(t,n.start),start:t,end:n.start});let b=ge[n.type]||"text";if(n.type==="ID"&&(b=xe(a,c)),n.type==="DATA"){let d=a[c+1];if(d?.type==="ID"&&d.start===n.end){e.push({type:"data-var",value:r.slice(n.start,d.end),start:n.start,end:d.end}),t=d.end,c++;continue}}e.push({type:b,value:r.slice(n.start,n.end),start:n.start,end:n.end}),t=n.end;}return t<r.length&&e.push({type:"text",value:r.slice(t),start:t,end:r.length}),e}function L(r){if(!r)return [];let a=Ee(r);return ye(r,a).filter(e=>e.value.length>0)}var Z=new Set(["if","unless","each","with","lookup","log","this","else"]),M=new Set(["each","with"]);function K(r){return Z.has(r)||r.startsWith("@")}var ee=V.Parser,O=ee.lexer,Oe=ee.terminals_;function Ne(r){let a=new Map,e=r;for(;e.length>0;){let t=ke(e,a);if(t===e.length)break;let c=e.slice(t),n=c.search(/\{\{/);if(n===-1)break;e=c.slice(n);}return Array.from(a.values())}function ke(r,a){O.setInput(r),O.conditionStack=["INITIAL"];let e=0,t=false,c=null,n=[],b=[];function d(i,l){let f=[];for(let o=l;o<i.length;o++)if(i[o].name==="ID")f.push(i[o].text);else if(i[o].name!=="SEP")break;return f}function x(i,l,f){a.has(l)||a.set(l,{name:i,path:l,type:f?"block":l.includes(".")?"nested":"simple",blockType:f});}function _(){if(n.length===0)return;let i=0;if(n[i]?.name==="DATA")return;let l=n[i].text;if(c==="block"){let f=b.some(o=>M.has(o.helper));if(M.has(l)){for(i++;i<n.length&&n[i].name!=="ID";)i++;if(i<n.length){let o=d(n,i);o.length>0&&!K(o[0])&&!f&&x(o[0],o.join("."),l),b.push({helper:l,context:o[0]||""});}}else if(Z.has(l)){for(i++;i<n.length&&n[i].name!=="ID";)i++;if(i<n.length){let o=d(n,i);o.length>0&&!K(o[0])&&!f&&x(o[0],o.join("."),l);}b.push({helper:l,context:""});}else b.push({helper:l,context:""});}else if(c==="endblock")b.length>0&&b.pop();else {let f=b.some(o=>M.has(o.helper));if(!K(l)&&!f){let o=d(n,i);o.length>0&&x(o[0],o.join("."));}}}try{for(let i=O.lex();i!==O.EOF;i=O.lex()){let l=Oe[i],f=O.yytext;if(e=O.yylloc?.last_column??e,!(l==="OPEN_RAW_BLOCK"||l==="CLOSE_RAW_BLOCK"||l==="END_RAW_BLOCK")){if(l==="OPEN"||l==="OPEN_UNESCAPED"){t=!0,c="mustache",n=[];continue}if(l==="OPEN_BLOCK"||l==="OPEN_INVERSE"||l==="OPEN_INVERSE_CHAIN"){t=!0,c="block",n=[];continue}if(l==="OPEN_ENDBLOCK"){t=!0,c="endblock",n=[];continue}if(l==="CLOSE"||l==="CLOSE_UNESCAPED"){_(),t=!1,n=[];continue}t&&n.push({name:l,text:f});}}return r.length}catch{return e}}function U(r){let a=Ne(r),e=[...new Set(a.map(t=>t.name))];return {variables:a,rootVariables:e}}function Pe(r,a,e){if(!a)return r;let t=e?.helpers?V.create():V;if(e?.helpers)for(let[c,n]of Object.entries(e.helpers))t.registerHelper(c,n);return t.compile(r)(a)}var Se=["#if","#each","#with","#unless","/if","/each","/with","/unless","else"];function re({value:r,onChange:a,placeholder:e="Enter template...",readOnly:t=false,className:c="",style:n,theme:b,customHelpers:d=[],autocomplete:x=true,minHeight:_=100}){let i=useRef(null),l=useRef(null),f=useRef(null),[o,y]=useState({isOpen:false,options:[],selectedIndex:0,triggerStart:0,filterText:""}),[R,se]=useState({top:0,left:0}),z=useMemo(()=>U(r).rootVariables,[r]),F=useMemo(()=>[...Se,...d,...z],[d,z]),$=useMemo(()=>L(r),[r]),oe=useMemo(()=>r?$.map(s=>s.type==="text"?s.value:jsx("span",{className:`hbs-token-${s.type}`,children:s.value},s.start)):jsx("span",{className:"hbs-editor-placeholder",children:e}),[$,r,e]),ae=useCallback(s=>{let p=s.target.value;if(a?.(p),!x||t)return;let h=s.target.selectionStart,m=p.slice(0,h).match(/\{\{([#/]?\w*)$/);if(m){let g=m[1]||"",E=h-g.length,N=F.filter(w=>w.toLowerCase().startsWith(g.toLowerCase()));if(N.length>0){y({isOpen:true,options:N,selectedIndex:0,triggerStart:E,filterText:g});return}}y(g=>({...g,isOpen:false}));},[a,x,t,F]),v=useCallback(s=>{let p=i.current;if(!p)return;let{triggerStart:h}=o,u=s,m=s.length;if(s.startsWith("#")){let E=s.slice(1);u=`${s} }}{{/${E}}}`,m=s.length+1;}else !s.startsWith("/")&&s!=="else"?(u=`${s}}}`,m=u.length):(u=`${s}}}`,m=u.length);p.focus(),p.setSelectionRange(h,p.selectionStart),document.execCommand("insertText",false,u);let g=h+m;p.setSelectionRange(g,g),a?.(p.value),y(E=>({...E,isOpen:false}));},[a,o]),le=useCallback(s=>{let{scrollTop:p,scrollLeft:h}=s.currentTarget;l.current&&(l.current.scrollTop=p,l.current.scrollLeft=h),se({top:p,left:h});},[]),ie=useCallback(s=>{if(!o.isOpen)return;let{options:p,selectedIndex:h}=o;switch(s.key){case "ArrowDown":s.preventDefault(),y(u=>({...u,selectedIndex:Math.min(h+1,p.length-1)}));break;case "ArrowUp":s.preventDefault(),y(u=>({...u,selectedIndex:Math.max(h-1,0)}));break;case "Enter":case "Tab":s.preventDefault(),p[h]&&v(p[h]);break;case "Escape":s.preventDefault(),y(u=>({...u,isOpen:false}));break}},[o,v]),I=useMemo(()=>{if(!o.isOpen||!i.current)return {top:0,left:0,maxHeight:192,openUpward:false,visible:false};let s=i.current,{triggerStart:p}=o,u=r.slice(0,p).split(`
3
- `),m=u.length,g=u[u.length-1]?.length||0,E=getComputedStyle(s),N=parseFloat(E.lineHeight)||20,w=parseFloat(E.paddingTop)||0,he=parseFloat(E.paddingLeft)||0,ue=(parseFloat(E.fontSize)||14)*.6,X=getComputedStyle(s.parentElement||s),fe=parseInt(X.getPropertyValue("--hbs-autocomplete-min-width"))||180,j=parseInt(X.getPropertyValue("--hbs-autocomplete-max-height"))||192,k=w+m*N-R.top,A=he+g*ue-R.left,Q=s.clientWidth-fe-10;A>Q&&(A=Math.max(10,Q));let D=s.clientHeight-k-10,G=k-N-10,q=D<120&&G>D,B,C;q?(C=Math.min(j,Math.max(80,G)),B=k-N-C):(C=Math.min(j,Math.max(80,D)),B=k);let be=k>0&&k<s.clientHeight&&A>0&&A<s.clientWidth;return {top:B,left:A,maxHeight:C,openUpward:q,visible:be}},[o,r,R]),ce=useMemo(()=>{if(!b)return {};let s={},p={variable:"--hbs-color-variable",variablePath:"--hbs-color-variable-path",blockKeyword:"--hbs-color-block-keyword",blockParam:"--hbs-color-block-param",helper:"--hbs-color-helper",helperArg:"--hbs-color-helper-arg",hashKey:"--hbs-color-hash-key",hashValue:"--hbs-color-hash-value",literal:"--hbs-color-literal",dataVar:"--hbs-color-data-var",subexprParen:"--hbs-color-subexpr-paren",comment:"--hbs-color-comment",raw:"--hbs-color-raw",brace:"--hbs-color-brace",text:"--hbs-color-text",background:"--hbs-color-background",caret:"--hbs-color-caret",border:"--hbs-color-border",placeholder:"--hbs-color-placeholder"};for(let[h,u]of Object.entries(p)){let m=b[h];m&&(s[u]=m);}return s},[b]);useEffect(()=>{if(!o.isOpen||!f.current)return;let h=f.current.querySelectorAll(".hbs-autocomplete-item")[o.selectedIndex];h&&h.scrollIntoView({block:"nearest"});},[o.selectedIndex,o.isOpen]);let pe=["hbs-editor",t?"hbs-readonly":"",c].filter(Boolean).join(" ");return jsxs("div",{className:pe,style:{...ce,minHeight:typeof _=="number"?`${_}px`:_,...n},children:[jsx("div",{ref:l,className:"hbs-editor-highlight",children:oe}),jsx("textarea",{ref:i,className:"hbs-editor-textarea",value:r,onChange:ae,onKeyDown:ie,onScroll:le,placeholder:"",readOnly:t,spellCheck:false,autoCapitalize:"off",autoComplete:"off",autoCorrect:"off"}),x&&o.isOpen&&I.visible&&jsx("div",{ref:f,className:"hbs-autocomplete",style:{top:I.top,left:I.left,maxHeight:I.maxHeight},children:o.options.map((s,p)=>jsx("button",{type:"button",className:`hbs-autocomplete-item ${p===o.selectedIndex?"hbs-selected":""}`,onClick:()=>v(s),onMouseEnter:()=>y(h=>({...h,selectedIndex:p})),children:s},s))})]})}function _e({content:r,className:a}){if(!r)return null;let e=L(r);return jsx("span",{className:a,children:e.map(t=>jsx("span",{className:`hbs-token-${t.type}`,children:t.value},t.start))})}export{re as HandlebarsEditor,_e as HandlebarsHighlight,re as default,U as extract,Pe as interpolate,L as tokenize};
1
+ import {useRef,useId,useState,useMemo,useCallback,useEffect}from'react';import ne from'handlebars';import {jsx,jsxs,Fragment}from'react/jsx-runtime';var ue=ne.Parser,M=ue.lexer,Re=ue.terminals_,we=new Set(["OPEN_BLOCK","OPEN_INVERSE","OPEN_INVERSE_CHAIN","OPEN_ENDBLOCK"]),ve=new Set(["OPEN","OPEN_UNESCAPED"]),Be=new Set(["OPEN_PARTIAL","OPEN_PARTIAL_BLOCK"]),De=new Set(["OPEN","OPEN_UNESCAPED","OPEN_BLOCK","OPEN_INVERSE","OPEN_INVERSE_CHAIN","OPEN_ENDBLOCK","OPEN_PARTIAL","OPEN_PARTIAL_BLOCK"]),He=new Set(["CLOSE","CLOSE_UNESCAPED","CLOSE_RAW_BLOCK","END_RAW_BLOCK"]),Me=new Set(["ID","STRING","NUMBER","BOOLEAN","DATA","OPEN_SEXPR","UNDEFINED","NULL"]),fe=t=>we.has(t),he=t=>ve.has(t),de=t=>Be.has(t),Ke=t=>De.has(t),Ve=t=>He.has(t),Ue=t=>Me.has(t),Fe={CONTENT:"text",COMMENT:"comment",OPEN:"brace",CLOSE:"brace",OPEN_UNESCAPED:"brace",CLOSE_UNESCAPED:"brace",OPEN_RAW_BLOCK:"brace",CLOSE_RAW_BLOCK:"brace",END_RAW_BLOCK:"brace",OPEN_BLOCK:"brace",OPEN_INVERSE:"brace",OPEN_INVERSE_CHAIN:"brace",OPEN_ENDBLOCK:"brace",OPEN_PARTIAL:"brace",OPEN_PARTIAL_BLOCK:"brace",OPEN_SEXPR:"subexpr-paren",CLOSE_SEXPR:"subexpr-paren",OPEN_BLOCK_PARAMS:"block-keyword",CLOSE_BLOCK_PARAMS:"block-keyword",INVERSE:"block-keyword",ID:"variable",STRING:"literal",NUMBER:"literal",BOOLEAN:"literal",UNDEFINED:"literal",NULL:"literal",DATA:"data-var",SEP:"brace",EQUALS:"brace"};function pe(t,n,r){let e=0;for(let s=0;s<n-1&&s<t.length;s++)e+=t[s].length+1;return e+r}function We(t){let n=[],r=t.split(`
2
+ `);M.setInput(t);try{for(let e=M.lex();e!==M.EOF;e=M.lex()){let s=Re[e]||"UNKNOWN",o=M.yylloc;n.push({type:s,text:M.yytext,start:pe(r,o.first_line,o.first_column),end:pe(r,o.last_line,o.last_column)});}}catch{}return n}function $e(t){return t.map(n=>n.type==="ID"&&n.text==="."?{...n,type:"SEP"}:n.type==="ID"&&n.text==="="?{...n,type:"EQUALS"}:n)}function ze(t){let n=[],r=null,e=false,s=false,o=0;for(let c=0;c<t.length;c++){let i=t[c],E=t[c-1];Ke(i.type)?(fe(i.type)?r=i.type==="OPEN_ENDBLOCK"?"block-close":"block-open":de(i.type)?r="partial":he(i.type)&&(r="mustache"),s=false,o=0):Ve(i.type)&&(r=null,s=false,o=0),i.type==="OPEN_BLOCK_PARAMS"?e=true:i.type==="CLOSE_BLOCK_PARAMS"&&(e=false),i.type==="SEP"?o++:i.type==="ID"&&E?.type!=="SEP"&&E?.type!=="DATA"&&(o=0);let A=false;if(i.type==="ID"){if(o===0)A=s;else for(let P=c-1;P>=0;P--)if(t[P].type==="ID"&&n[P]?.context.pathRootAfterHelper!==void 0){let g=t[P-1];if(g?.type!=="SEP"&&g?.type!=="DATA"){A=n[P].context.pathRootAfterHelper;break}}}n.push({...i,context:{type:r,isInBlockParams:e,pathRootAfterHelper:A}}),i.type==="ID"&&o===0&&!e&&E?.type!=="EQUALS"&&E?.type!=="DATA"&&(s=true);}return n}function Xe(t,n,r){let{context:e}=t;return t.text==="this"?"data-var":e.isInBlockParams?"block-param":r?.type==="EQUALS"?"hash-key":n?.type==="EQUALS"?"hash-value":n?.type==="DATA"?"data-var":n?.type==="SEP"?e.pathRootAfterHelper?"helper-arg":"variable-path":Qe(t,n,r)}function Qe(t,n,r){let{context:e}=t;return e.type==="block-open"||e.type==="block-close"?fe(n?.type??"")?"block-keyword":"helper-arg":e.type==="partial"?de(n?.type??"")?"helper":"helper-arg":n?.type==="OPEN_SEXPR"?"helper":e.type==="mustache"?he(n?.type??"")?r?.type==="SEP"?"variable":r&&Ue(r.type)?"helper":"variable":"helper-arg":"variable"}function Ge(t){return t.map((n,r)=>{let e=t[r-1],s=t[r+1],o;return n.type==="ID"?o=Xe(n,e,s):o=Fe[n.type]||"text",{token:n,highlightType:o}})}function je(t,n){let r=[],e=0;for(let s=0;s<n.length;s++){let{token:o,highlightType:c}=n[s];if(o.start>e&&r.push({type:"text",value:t.slice(e,o.start),start:e,end:o.start}),o.type==="DATA"){let i=n[s+1];if(i?.token.type==="ID"&&i.token.start===o.end){r.push({type:"data-var",value:t.slice(o.start,i.token.end),start:o.start,end:i.token.end}),e=i.token.end,s++;continue}}r.push({type:c,value:t.slice(o.start,o.end),start:o.start,end:o.end}),e=o.end;}return e<t.length&&r.push({type:"text",value:t.slice(e),start:e,end:t.length}),r}function K(t){if(!t)return [];let n=We(t),r=$e(n),e=ze(r),s=Ge(e);return je(t,s).filter(c=>c.value.length>0)}var G=["if","unless","each","with"],me=new Set([...G,"lookup","log","this","else"]),xe=new Set(["each","with"]),qe=new Set(["ID","STRING","NUMBER","BOOLEAN","DATA","OPEN_SEXPR"]),Ee=ne.Parser,D=Ee.lexer,Ye=Ee.terminals_;function z(t){return me.has(t)||t.startsWith("@")}function Je(t){return qe.has(t.name)}function X(t,n){let r=[],e=true;for(let s=n;s<t.length;s++){let o=t[s];if(o.name==="EQUALS")break;if(o.name==="ID"){if(!e||t[s+1]?.name==="EQUALS")break;r.push(o.text),e=false;}else if(o.name==="SEP")e=true;else break}return r}function Ze(t,n,r,e){return {name:t,path:n,type:r?"block":n.includes(".")||e?"nested":"simple",blockType:r,context:e}}function et(t){let n=[],r=null;for(let e of t)xe.has(e.helper)&&e.context&&(r||(r=e.context),n.push(e.helper==="each"?`${e.context}[]`:e.context));return n.length===0||!r?null:{root:r,prefix:`${n.join(".")}.`}}function tt(t){let n=[],r=t;for(;r.length>0;){let e=nt(r);if(n.push(...e.expressions),e.consumed===r.length)break;let s=r.slice(e.consumed),o=s.search(/\{\{/);if(o===-1)break;r=s.slice(o);}return {expressions:n}}function nt(t){D.setInput(t),D.conditionStack=["INITIAL"];let n=[],r=0,e=false,s=null,o=[];try{for(let c=D.lex();c!==D.EOF;c=D.lex()){let i=Ye[c],E=D.yytext;if(r=D.yylloc?.last_column??r,!(i==="OPEN_RAW_BLOCK"||i==="CLOSE_RAW_BLOCK"||i==="END_RAW_BLOCK")){if(i==="OPEN"||i==="OPEN_UNESCAPED"){e=!0,s="mustache",o=[];continue}if(i==="OPEN_PARTIAL"||i==="OPEN_PARTIAL_BLOCK"){e=!0,s="partial",o=[];continue}if(i==="OPEN_BLOCK"||i==="OPEN_INVERSE"||i==="OPEN_INVERSE_CHAIN"){e=!0,s="block",o=[];continue}if(i==="OPEN_ENDBLOCK"){e=!0,s="endblock",o=[];continue}if(i==="CLOSE"||i==="CLOSE_UNESCAPED"){if(s){let A=o[0]?.text??null;n.push({type:s,tokens:[...o],helperName:A});}e=!1,o=[];continue}e&&o.push({name:i,text:E});}}return {expressions:n,consumed:t.length}}catch{return {expressions:n,consumed:r}}}function rt(t,n,r){let e=t.tokens;if(e.length===0||e[0]?.name==="DATA")return;let s=e[0]?.text;if(z(s))W(e,1,n,r);else {let o=X(e,0);if(o.length>0){let i=o.length*2-1,E=e[i];E&&Je(E)?W(e,i,n,r):Q(o,n,r);}}}function ot(t,n,r){let e=t.tokens,s=e[0]?.text;if(xe.has(s)){if(e[1]?.name==="OPEN_SEXPR")return W(e,1,n,r),{helper:s,context:""};{let o=1;for(;o<e.length&&e[o].name!=="ID";)o++;if(o<e.length){let c=X(e,o);return c.length>0&&!z(c[0])&&Q(c,n,r,s),{helper:s,context:c[0]||""}}return {helper:s,context:""}}}else if(me.has(s)){if(e[1]?.name==="OPEN_SEXPR")W(e,1,n,r);else {let o=1;for(;o<e.length&&e[o].name!=="ID";)o++;if(o<e.length){let c=X(e,o);c.length>0&&!z(c[0])&&Q(c,n,r,s);}}return {helper:s,context:""}}return {helper:s,context:""}}function st(t,n,r){let e=t.tokens,s=1;for(;s<e.length&&(e[s].name==="SEP"||e[s].name==="ID")&&!(e[s].name==="ID"&&e[s-1]?.name!=="SEP");)s++;W(e,s,n,r);}function W(t,n,r,e){for(let s=n;s<t.length;s++){let o=t[s];if(!(o.name==="OPEN_SEXPR"||o.name==="CLOSE_SEXPR")&&o.name!=="DATA"&&t[s-1]?.name!=="DATA"&&o.name!=="EQUALS"&&!(o.name==="ID"&&t[s+1]?.name==="EQUALS")&&o.name==="ID"&&!z(o.text)){if(t[s-1]?.name==="OPEN_SEXPR")continue;let i=X(t,s);i.length>0&&(Q(i,r,e),s+=i.length*2-2);}}}function Q(t,n,r,e){let s=t[0],o=n.contextPrefix&&n.rootContext?{prefix:n.contextPrefix,root:n.rootContext}:null,c,i;o?(c=o.prefix+t.join("."),i=o.root):(c=t.join("."),i=void 0),r.has(c)||r.set(c,Ze(s,c,e,i));}function at(t,n){return n.type==="endblock"&&t.length>0?t.slice(0,-1):t}function it(t){let n=new Map,r=[];for(let e of t){let s=et(r),o={contextPrefix:s?.prefix??null,rootContext:s?.root??null};if(e.type==="mustache")rt(e,o,n);else if(e.type==="block"){let c=ot(e,o,n);r=[...r,c];}else e.type==="partial"?st(e,o,n):e.type==="endblock"&&(r=at(r,e));}return Array.from(n.values())}function re(t){let{expressions:n}=tt(t),r=it(n),e=[...new Set(r.filter(s=>!s.context).map(s=>s.name))];return {variables:r,rootVariables:e}}function lt(t,n,r){if(!n)return t;let e=r?.helpers?ne.create():ne;if(r?.helpers)for(let[s,o]of Object.entries(r.helpers))e.registerHelper(s,o);return e.compile(t)(n)}var pt=["this","@index","@first","@last","@key"],ut=["@root"];function ft(t,n){if(!n)return "prefix";let r=t.toLowerCase(),e=n.toLowerCase();return r===e?"exact":r.startsWith(e)?"prefix":r.includes(e)?"partial":"none"}function ht(t,n,r,e,s){let o=[],c=new Set,i=s.lastIndexOf("."),E=i!==-1;E?s.slice(0,i+1):"";let P=E?s.slice(i+1):s,g=(m,l)=>{if(c.has(m))return;let b=ft(m,P);b!=="none"&&(c.add(m),o.push({option:{value:m,type:l},matchType:b}));};if(E){let m=s.slice(0,i).replace(/\[\]$/,"");n.forEach(l=>{let b=l.path.replace(/\[\]/g,"");if(b.startsWith(`${m}.`)){let q=b.slice(m.length+1).split(".")[0];g(q,"variable");}});}else {if(!r){for(let l of G)g(`#${l}`,"block-open");for(let l of G)g(`/${l}`,"block-close");g("else","block-close");}for(let l of t)g(l,"helper");if(e){if(e.isEach)for(let l of pt)g(l,"data-var");for(let l of e.blockParams)g(l,"variable");for(let l of ut)g(l,"data-var");}let m=new Set;for(let l of n){let b=l.path.split(".")[0].replace("[]","");m.add(b);}for(let l of m)g(l,"variable");}let v={"data-var":0,variable:1,helper:2,"block-open":3,"block-close":4},_={exact:0,prefix:1,partial:2,none:3};return o.sort((m,l)=>{let b=_[m.matchType]-_[l.matchType];if(b!==0)return b;let B=v[m.option.type]-v[l.option.type];return B!==0?B:m.option.value.localeCompare(l.option.value)}),o.map(m=>m.option)}function dt(t,n){let r=t.slice(0,n),e=[],s=/\{\{#(each|with)\s+([^\s}]+)(?:\s+as\s*\|([^|]+)\|)?|\{\{\/(each|with)\}\}/g;for(let o of r.matchAll(s))if(o[1]){let c=o[1],i=o[2],A=(o[3]||"").split(/\s+/).filter(Boolean);e.push({type:c,variable:i,blockParams:A,isEach:c==="each"});}else if(o[4]){let c=o[4];for(let i=e.length-1;i>=0;i--)if(e[i].type===c){e.splice(i,1);break}}return e.length>0?e[e.length-1]:null}function mt(t,n){let r=t.slice(0,n),e=r.lastIndexOf("{{!--");if(e!==-1&&r.slice(e).indexOf("--}}")===-1)return true;let s=r.lastIndexOf("{{!");if(s!==-1){let o=r.slice(s);if(!o.startsWith("{{!--")&&o.indexOf("}}")===-1)return true}return false}function xt(t,n){let r=t.slice(0,n),e=t.slice(n),s=r.lastIndexOf("{{");if(s===-1||r.slice(s).includes("}}"))return false;let c=e.indexOf("}}");return c===-1?false:!e.slice(0,c).includes("{{")}function ye({value:t,onChange:n,placeholder:r="Enter template...",readOnly:e=false,className:s="",style:o,theme:c,customHelpers:i=[],autocomplete:E=true,minHeight:A=100}){let P=useRef(null),g=useRef(null),v=useRef(null),_=useRef(null),m=useId(),[l,b]=useState({isOpen:false,options:[],selectedIndex:0,triggerStart:0,filterText:"",isRaw:false,contextPath:null}),[B,q]=useState({top:0,left:0}),se=useMemo(()=>re(t).variables.map(p=>({path:p.path,context:p.context})),[t]),ae=useMemo(()=>K(t),[t]),Oe=useMemo(()=>t?ae.map(a=>a.type==="text"?a.value:jsx("span",{className:`hbs-token-${a.type}`,children:a.value},a.start)):jsx("span",{className:"hbs-editor-placeholder",children:r}),[ae,t,r]),k=useCallback(()=>{b(a=>({...a,isOpen:false}));},[]),ie=useCallback((a,p)=>{if(!E||e){k();return}if(mt(a,p)||xt(a,p)){k();return}let d=a.slice(0,p).match(/(\{\{\{?)([#/]?[\w.@]*)$/);if(!d){k();return}let x=d[1],f=d[2]||"",T=p-f.length,h=x==="{{{";if(f.includes("}")){k();return}let y=dt(a,p),O=ht(i,se,h,y,f);if(O.length===0){k();return}b({isOpen:true,options:O,selectedIndex:0,triggerStart:T,filterText:f,isRaw:h,contextPath:y});},[E,e,i,se,k]),Ae=useCallback(a=>{let p=a.target.value;n?.(p),ie(p,a.target.selectionStart);},[n,ie]),U=useCallback(a=>{let p=g.current;if(!p)return;let{triggerStart:u,filterText:d,isRaw:x}=l,f=x?"}}}":"}}",T=d.lastIndexOf("."),h=T!==-1?d.slice(0,T+1):"",y,O;if(a.type==="block-open"&&!x){let N=a.value.slice(1);y=`${a.value} }}{{/${N}}}`,O=a.value.length+1;}else a.type!=="block-close"&&a.value!=="else"?(y=`${h}${a.value}${f}`,O=y.length):(y=`${a.value}${f}`,O=y.length);p.focus(),p.setSelectionRange(u,p.selectionStart),document.execCommand("insertText",false,y);let I=u+O;p.setSelectionRange(I,I),n?.(p.value),k();},[l,n,k]),Te=useCallback(a=>{let{scrollTop:p,scrollLeft:u}=a.currentTarget;v.current&&(v.current.scrollTop=p,v.current.scrollLeft=u),q({top:p,left:u});},[]),Pe=useCallback(a=>{_.current?.contains(a.relatedTarget)||k();},[k]),Y=useCallback(()=>{let a=g.current;if(!a||!l.isOpen)return;let p=a.selectionStart;t.slice(0,p).match(/(\{\{\{?)([#/]?[\w.@]*)$/)||k();},[l.isOpen,t,k]),Ne=useCallback(a=>{let p=a.currentTarget;if(a.key==="Tab"){if(l.isOpen){a.preventDefault();let T=l.options[l.selectedIndex];T&&U(T);return}a.preventDefault();let{selectionStart:f}=p;if(a.shiftKey){let h=t.slice(0,f).lastIndexOf(`
3
+ `)+1,O=t.slice(h,f).match(/^(\t| {2})/);if(O){let I=O[1].length,N=t.slice(0,h)+t.slice(h+I);n?.(N);let S=Math.max(h,f-I);setTimeout(()=>p.setSelectionRange(S,S),0);}}else document.execCommand("insertText",false," "),n?.(p.value);return}if((a.metaKey||a.ctrlKey)&&(a.key==="z"||a.key==="y")||!l.isOpen)return;let{options:u,selectedIndex:d}=l,x=8;switch(a.key){case "ArrowDown":a.preventDefault(),b(f=>({...f,selectedIndex:Math.min(d+1,u.length-1)}));break;case "ArrowUp":a.preventDefault(),b(f=>({...f,selectedIndex:Math.max(d-1,0)}));break;case "Home":a.preventDefault(),b(f=>({...f,selectedIndex:0}));break;case "End":a.preventDefault(),b(f=>({...f,selectedIndex:u.length-1}));break;case "PageDown":a.preventDefault(),b(f=>({...f,selectedIndex:Math.min(d+x,u.length-1)}));break;case "PageUp":a.preventDefault(),b(f=>({...f,selectedIndex:Math.max(d-x,0)}));break;case "Enter":a.preventDefault(),u[d]&&U(u[d]);break;case "Escape":a.preventDefault(),k();break;case "ArrowLeft":case "ArrowRight":setTimeout(()=>Y(),0);break}},[l,U,k,Y,t,n]),J=useMemo(()=>{if(!l.isOpen||!g.current)return null;let a=g.current,p=getComputedStyle(a),u=getComputedStyle(a.parentElement||a);return {lineHeight:parseFloat(p.lineHeight)||20,paddingTop:parseFloat(p.paddingTop)||0,paddingLeft:parseFloat(p.paddingLeft)||0,charWidth:(parseFloat(p.fontSize)||14)*.6,dropdownWidth:parseInt(u.getPropertyValue("--hbs-autocomplete-min-width"),10)||180,dropdownMaxHeight:parseInt(u.getPropertyValue("--hbs-autocomplete-max-height"),10)||200,clientWidth:a.clientWidth,clientHeight:a.clientHeight}},[l.isOpen]),H=useMemo(()=>{if(!l.isOpen||!J)return {top:void 0,bottom:void 0,left:0,maxHeight:200,openUpward:false,visible:false};let{triggerStart:a}=l,{lineHeight:p,paddingTop:u,paddingLeft:d,charWidth:x,dropdownWidth:f,dropdownMaxHeight:T,clientWidth:h,clientHeight:y}=J,I=t.slice(0,a).split(`
4
+ `),N=I.length,S=I[I.length-1]?.length||0,R=u+N*p-B.top,C=d+S*x-B.left,$=h-f-10;C>$&&(C=Math.max(10,$));let F=y-R-10,le=R-p-10,ce=F<120&&le>F,Z,ee,te;ce?(te=Math.min(T,Math.max(80,le)),ee=y-R+p,Z=void 0):(te=Math.min(T,Math.max(80,F)),Z=R,ee=void 0);let Le=R>=0&&R<y&&C>=0&&C<h;return {top:Z,bottom:ee,left:C,maxHeight:te,openUpward:ce,visible:Le}},[l,t,B,J]),Se=useMemo(()=>{if(!c)return {};let a={},p={variable:"--hbs-color-variable",variablePath:"--hbs-color-variable-path",blockKeyword:"--hbs-color-block-keyword",blockParam:"--hbs-color-block-param",helper:"--hbs-color-helper",helperArg:"--hbs-color-helper-arg",hashKey:"--hbs-color-hash-key",hashValue:"--hbs-color-hash-value",literal:"--hbs-color-literal",dataVar:"--hbs-color-data-var",subexprParen:"--hbs-color-subexpr-paren",comment:"--hbs-color-comment",raw:"--hbs-color-raw",brace:"--hbs-color-brace",text:"--hbs-color-text",background:"--hbs-color-background",caret:"--hbs-color-caret",border:"--hbs-color-border",placeholder:"--hbs-color-placeholder"};for(let[u,d]of Object.entries(p)){let x=c[u];x&&(a[d]=x);}return a},[c]);useEffect(()=>{if(!l.isOpen||!_.current)return;let u=_.current.querySelectorAll(".hbs-autocomplete-item")[l.selectedIndex];u&&u.scrollIntoView({block:"nearest"});},[l.selectedIndex,l.isOpen]),useEffect(()=>{if(!l.isOpen)return;let a=P.current,p=d=>{let x=d.target;a&&!a.contains(x)&&k();},u=d=>{let x=d.target;a?.contains(x)&&!_.current?.contains(x)&&x!==g.current&&k();};return document.addEventListener("mousedown",p),a?.addEventListener("mousedown",u),()=>{document.removeEventListener("mousedown",p),a?.removeEventListener("mousedown",u);}},[l.isOpen,k]);let Ce=useCallback((a,p)=>{let{filterText:u,selectedIndex:d}=l,x=p===d,f=u.lastIndexOf("."),T=f!==-1?u.slice(f+1):u,h=a.value;(a.type==="block-open"&&h.startsWith("#")||a.type==="block-close"&&h.startsWith("/")||a.type==="data-var"&&h.startsWith("@"))&&(h=h.slice(1));let y,O=T.toLowerCase().replace(/^[#/@]/,""),N=h.toLowerCase().indexOf(O);if(O.length>0&&N!==-1){let C=h.slice(0,N),$=h.slice(N,N+O.length),F=h.slice(N+O.length);y=jsxs(Fragment,{children:[C,jsx("span",{className:"hbs-autocomplete-match",children:$}),F]});}else y=h;let S="";switch(a.type){case "block-open":S="#";break;case "block-close":S="/";break;case "data-var":S="@";break;case "helper":S="\u0192";break;case "variable":S="\xB7";break}let R=jsx("span",{className:"hbs-autocomplete-type",children:S});return jsxs("button",{id:`${m}-option-${p}`,type:"button",role:"option","aria-selected":x,className:`hbs-autocomplete-item hbs-autocomplete-${a.type} ${x?"hbs-selected":""}`,onMouseDown:C=>{C.preventDefault(),U(a);},onMouseEnter:()=>b(C=>({...C,selectedIndex:p})),children:[R,jsx("span",{className:"hbs-autocomplete-value",children:y})]},a.value)},[l,U,m]),Ie=["hbs-editor",e?"hbs-readonly":"",s].filter(Boolean).join(" ");return jsxs("div",{ref:P,className:Ie,style:{...Se,minHeight:typeof A=="number"?`${A}px`:A,...o},children:[jsx("div",{ref:v,className:"hbs-editor-highlight",children:Oe}),jsx("textarea",{ref:g,className:"hbs-editor-textarea",value:t,onChange:Ae,onKeyDown:Ne,onScroll:Te,onBlur:Pe,onSelect:Y,placeholder:"",readOnly:e,spellCheck:false,autoCapitalize:"off",autoComplete:"off",autoCorrect:"off",role:"combobox","aria-haspopup":"listbox","aria-expanded":E&&l.isOpen&&H.visible,"aria-controls":m,"aria-activedescendant":E&&l.isOpen&&l.options[l.selectedIndex]?`${m}-option-${l.selectedIndex}`:void 0}),E&&l.isOpen&&H.visible&&jsx("div",{ref:_,id:m,role:"listbox","aria-label":"Autocomplete suggestions",className:"hbs-autocomplete",style:{top:H.top,bottom:H.bottom,left:H.left,maxHeight:H.maxHeight},children:l.options.map((a,p)=>Ce(a,p))})]})}function bt({content:t,className:n}){if(!t)return null;let r=K(t);return jsx("span",{className:n,children:r.map(e=>jsx("span",{className:`hbs-token-${e.type}`,children:e.value},e.start))})}export{ye as HandlebarsEditor,bt as HandlebarsHighlight,ye as default,re as extract,lt as interpolate,K as tokenize};
package/dist/styles.css CHANGED
@@ -1,52 +1,43 @@
1
1
  /* Handlebars Editor - Default Theme */
2
2
 
3
3
  .hbs-editor {
4
- /* Colors - customize these CSS variables */
5
- --hbs-color-text: #1f2937;
4
+ /* Base colors */
5
+ --hbs-color-text: #24292f;
6
6
  --hbs-color-background: #ffffff;
7
- --hbs-color-border: #e5e7eb;
8
- --hbs-color-caret: #1f2937;
9
- --hbs-color-placeholder: #9ca3af;
10
- --hbs-color-focus-ring: #3b82f6;
11
-
12
- /* Syntax highlighting colors */
13
- --hbs-color-variable: #3b82f6;
14
- --hbs-color-variable-path: #06b6d4;
15
- --hbs-color-block-keyword: #a855f7;
16
- --hbs-color-block-param: #3b82f6;
17
- --hbs-color-helper: #f59e0b;
18
- --hbs-color-helper-arg: #3b82f6;
19
- --hbs-color-hash-key: #ec4899;
20
- --hbs-color-hash-value: #3b82f6;
21
- --hbs-color-literal: #16a34a;
22
- --hbs-color-data-var: #f43f5e;
7
+ --hbs-color-border: #d1d5db;
8
+ --hbs-color-caret: #24292f;
9
+ --hbs-color-placeholder: #6b7280;
10
+ --hbs-color-focus-ring: #2563eb;
11
+
12
+ /* Syntax highlighting - balanced, readable palette */
13
+ --hbs-color-variable: #0550ae;
14
+ --hbs-color-variable-path: #0550ae;
15
+ --hbs-color-block-keyword: #8250df;
16
+ --hbs-color-block-param: #0550ae;
17
+ --hbs-color-helper: #953800;
18
+ --hbs-color-helper-arg: #0550ae;
19
+ --hbs-color-hash-key: #0550ae;
20
+ --hbs-color-hash-value: #0a3069;
21
+ --hbs-color-literal: #0a3069;
22
+ --hbs-color-data-var: #cf222e;
23
23
  --hbs-color-subexpr-paren: #6b7280;
24
- --hbs-color-comment: #9ca3af;
25
- --hbs-color-raw: #f97316;
26
- --hbs-color-brace: #6b7280;
27
- --hbs-color-error: #ef4444;
28
- --hbs-color-error-bg: rgba(239, 68, 68, 0.1);
24
+ --hbs-color-comment: #6b7280;
25
+ --hbs-color-raw: #953800;
26
+ --hbs-color-brace: #9ca3af;
27
+ --hbs-color-error: #cf222e;
28
+ --hbs-color-error-bg: rgba(207, 34, 46, 0.1);
29
29
 
30
- /* Autocomplete colors */
30
+ /* Autocomplete */
31
31
  --hbs-color-autocomplete-bg: #ffffff;
32
- --hbs-color-autocomplete-border: #e5e7eb;
33
32
  --hbs-color-autocomplete-selected: #f3f4f6;
34
- --hbs-color-autocomplete-hover: #f9fafb;
35
-
36
- /* Autocomplete dimensions */
37
- --hbs-autocomplete-min-width: 180px;
38
- --hbs-autocomplete-max-height: 192px;
39
-
40
- /* Error bar colors */
41
- --hbs-color-error-bar-bg: rgba(239, 68, 68, 0.1);
42
- --hbs-color-error-bar-border: rgba(239, 68, 68, 0.5);
43
33
 
44
- /* Spacing */
34
+ /* Dimensions */
35
+ --hbs-autocomplete-min-width: 160px;
36
+ --hbs-autocomplete-max-height: 200px;
45
37
  --hbs-padding: 12px;
46
38
  --hbs-font-size: 14px;
47
- --hbs-line-height: 1.5;
48
- --hbs-font-family:
49
- ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
39
+ --hbs-line-height: 1.6;
40
+ --hbs-font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
50
41
  --hbs-border-radius: 6px;
51
42
  --hbs-tab-size: 2;
52
43
 
@@ -63,20 +54,34 @@
63
54
 
64
55
  /* Dark theme */
65
56
  .hbs-editor.hbs-theme-dark {
66
- --hbs-color-text: #f9fafb;
67
- --hbs-color-background: #1f2937;
68
- --hbs-color-border: #374151;
69
- --hbs-color-caret: #f9fafb;
70
- --hbs-color-placeholder: #6b7280;
71
- --hbs-color-autocomplete-bg: #1f2937;
72
- --hbs-color-autocomplete-border: #374151;
73
- --hbs-color-autocomplete-selected: #374151;
74
- --hbs-color-autocomplete-hover: #4b5563;
75
- --hbs-color-autocomplete-text: #f9fafb;
76
- }
57
+ --hbs-color-text: #e6edf3;
58
+ --hbs-color-background: #0d1117;
59
+ --hbs-color-border: #30363d;
60
+ --hbs-color-caret: #e6edf3;
61
+ --hbs-color-placeholder: #6e7681;
62
+ --hbs-color-focus-ring: #58a6ff;
77
63
 
78
- .hbs-editor.hbs-theme-dark .hbs-autocomplete-item {
79
- color: var(--hbs-color-autocomplete-text, #f9fafb);
64
+ /* Syntax - GitHub dark inspired */
65
+ --hbs-color-variable: #79c0ff;
66
+ --hbs-color-variable-path: #79c0ff;
67
+ --hbs-color-block-keyword: #d2a8ff;
68
+ --hbs-color-block-param: #79c0ff;
69
+ --hbs-color-helper: #ffa657;
70
+ --hbs-color-helper-arg: #79c0ff;
71
+ --hbs-color-hash-key: #79c0ff;
72
+ --hbs-color-hash-value: #a5d6ff;
73
+ --hbs-color-literal: #a5d6ff;
74
+ --hbs-color-data-var: #ff7b72;
75
+ --hbs-color-subexpr-paren: #6e7681;
76
+ --hbs-color-comment: #6e7681;
77
+ --hbs-color-raw: #ffa657;
78
+ --hbs-color-brace: #6e7681;
79
+ --hbs-color-error: #f85149;
80
+ --hbs-color-error-bg: rgba(248, 81, 73, 0.15);
81
+
82
+ /* Autocomplete */
83
+ --hbs-color-autocomplete-bg: #161b22;
84
+ --hbs-color-autocomplete-selected: #30363d;
80
85
  }
81
86
 
82
87
  /* Highlight layer */
@@ -95,7 +100,6 @@
95
100
  color: var(--hbs-color-text);
96
101
  tab-size: var(--hbs-tab-size);
97
102
  -moz-tab-size: var(--hbs-tab-size);
98
- /* Hide scrollbars - scrolling is synced from textarea */
99
103
  scrollbar-width: none;
100
104
  -ms-overflow-style: none;
101
105
  }
@@ -109,7 +113,7 @@
109
113
  }
110
114
 
111
115
  .hbs-editor.hbs-readonly .hbs-editor-highlight {
112
- background: rgba(0, 0, 0, 0.03);
116
+ background: rgba(0, 0, 0, 0.02);
113
117
  }
114
118
 
115
119
  /* Textarea layer */
@@ -132,16 +136,45 @@
132
136
  outline: none;
133
137
  tab-size: var(--hbs-tab-size);
134
138
  -moz-tab-size: var(--hbs-tab-size);
139
+ scrollbar-width: thin;
140
+ scrollbar-color: var(--hbs-color-border) transparent;
141
+ }
142
+
143
+ .hbs-editor-textarea::-webkit-scrollbar {
144
+ width: 10px;
145
+ height: 10px;
146
+ }
147
+
148
+ .hbs-editor-textarea::-webkit-scrollbar-track {
149
+ background: transparent;
150
+ }
151
+
152
+ .hbs-editor-textarea::-webkit-scrollbar-thumb {
153
+ background: var(--hbs-color-border);
154
+ border: 2px solid var(--hbs-color-background);
155
+ border-radius: 5px;
156
+ }
157
+
158
+ .hbs-editor-textarea::-webkit-scrollbar-thumb:hover {
159
+ background: var(--hbs-color-placeholder);
160
+ }
161
+
162
+ .hbs-editor-textarea::-webkit-scrollbar-corner {
163
+ background: transparent;
135
164
  }
136
165
 
137
166
  .hbs-editor-textarea:focus {
138
167
  border-color: var(--hbs-color-focus-ring);
139
- box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.2);
168
+ box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.15);
169
+ }
170
+
171
+ .hbs-editor.hbs-theme-dark .hbs-editor-textarea:focus {
172
+ box-shadow: 0 0 0 3px rgba(88, 166, 255, 0.15);
140
173
  }
141
174
 
142
175
  .hbs-editor.hbs-readonly .hbs-editor-textarea {
143
176
  cursor: default;
144
- background: rgba(0, 0, 0, 0.03);
177
+ background: rgba(0, 0, 0, 0.02);
145
178
  }
146
179
 
147
180
  /* Placeholder */
@@ -153,49 +186,61 @@
153
186
  .hbs-token-variable {
154
187
  color: var(--hbs-color-variable);
155
188
  }
189
+
156
190
  .hbs-token-variable-path {
157
191
  color: var(--hbs-color-variable-path);
158
192
  }
193
+
159
194
  .hbs-token-block-keyword {
160
195
  color: var(--hbs-color-block-keyword);
161
- font-weight: 500;
196
+ font-weight: 600;
162
197
  }
198
+
163
199
  .hbs-token-block-param {
164
200
  color: var(--hbs-color-block-param);
165
201
  }
202
+
166
203
  .hbs-token-helper {
167
204
  color: var(--hbs-color-helper);
168
- font-weight: 500;
169
205
  }
206
+
170
207
  .hbs-token-helper-arg {
171
208
  color: var(--hbs-color-helper-arg);
172
209
  }
210
+
173
211
  .hbs-token-hash-key {
174
212
  color: var(--hbs-color-hash-key);
175
213
  }
214
+
176
215
  .hbs-token-hash-value {
177
216
  color: var(--hbs-color-hash-value);
178
217
  }
218
+
179
219
  .hbs-token-literal {
180
220
  color: var(--hbs-color-literal);
181
221
  }
222
+
182
223
  .hbs-token-data-var {
183
224
  color: var(--hbs-color-data-var);
184
225
  }
226
+
185
227
  .hbs-token-subexpr-paren {
186
228
  color: var(--hbs-color-subexpr-paren);
187
- font-weight: 500;
188
229
  }
230
+
189
231
  .hbs-token-comment {
190
232
  color: var(--hbs-color-comment);
191
233
  font-style: italic;
192
234
  }
235
+
193
236
  .hbs-token-raw {
194
237
  color: var(--hbs-color-raw);
195
238
  }
239
+
196
240
  .hbs-token-brace {
197
241
  color: var(--hbs-color-brace);
198
242
  }
243
+
199
244
  .hbs-token-error {
200
245
  color: var(--hbs-color-error);
201
246
  background: var(--hbs-color-error-bg);
@@ -207,37 +252,61 @@
207
252
  position: absolute;
208
253
  z-index: 50;
209
254
  min-width: var(--hbs-autocomplete-min-width);
255
+ max-width: 240px;
256
+ max-height: var(--hbs-autocomplete-max-height);
210
257
  overflow: auto;
211
- border-radius: var(--hbs-border-radius);
212
- border: 1px solid var(--hbs-color-autocomplete-border);
258
+ border-radius: 8px;
259
+ border: 1px solid var(--hbs-color-border);
213
260
  background: var(--hbs-color-autocomplete-bg);
214
261
  padding: 4px;
215
- box-shadow:
216
- 0 4px 6px -1px rgba(0, 0, 0, 0.1),
217
- 0 2px 4px -2px rgba(0, 0, 0, 0.1);
262
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
263
+ }
264
+
265
+ .hbs-editor.hbs-theme-dark .hbs-autocomplete {
266
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
218
267
  }
219
268
 
220
269
  .hbs-autocomplete-item {
221
- display: block;
270
+ display: flex;
271
+ align-items: center;
272
+ gap: 6px;
222
273
  width: 100%;
223
274
  text-align: left;
224
275
  cursor: pointer;
225
276
  border-radius: 4px;
226
- padding: 6px 8px;
277
+ padding: 5px 8px;
227
278
  font-family: var(--hbs-font-family);
228
- font-size: var(--hbs-font-size);
279
+ font-size: 13px;
229
280
  background: transparent;
230
281
  border: none;
282
+ color: var(--hbs-color-text);
231
283
  }
232
284
 
233
- .hbs-autocomplete-item:hover {
234
- background: var(--hbs-color-autocomplete-hover);
235
- }
236
-
285
+ .hbs-autocomplete-item:hover,
237
286
  .hbs-autocomplete-item.hbs-selected {
238
287
  background: var(--hbs-color-autocomplete-selected);
239
288
  }
240
289
 
290
+ .hbs-autocomplete-value {
291
+ flex: 1;
292
+ overflow: hidden;
293
+ text-overflow: ellipsis;
294
+ white-space: nowrap;
295
+ }
296
+
297
+ .hbs-autocomplete-match {
298
+ color: var(--hbs-color-focus-ring);
299
+ font-weight: 600;
300
+ }
301
+
302
+ /* Type indicator */
303
+ .hbs-autocomplete-type {
304
+ flex-shrink: 0;
305
+ font-size: 11px;
306
+ color: var(--hbs-color-placeholder);
307
+ opacity: 0.8;
308
+ }
309
+
241
310
  /* Error bar */
242
311
  .hbs-error-bar {
243
312
  position: absolute;
@@ -248,10 +317,10 @@
248
317
  align-items: center;
249
318
  gap: 8px;
250
319
  border-radius: var(--hbs-border-radius);
251
- border: 1px solid var(--hbs-color-error-bar-border);
252
- background: var(--hbs-color-error-bar-bg);
320
+ border: 1px solid rgba(207, 34, 46, 0.4);
321
+ background: var(--hbs-color-error-bg);
253
322
  padding: 8px 12px;
254
- font-size: 14px;
323
+ font-size: 13px;
255
324
  color: var(--hbs-color-error);
256
325
  }
257
326
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "handlebars-editor-react",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "A React component for editing Handlebars templates with syntax highlighting and autocomplete",
5
5
  "author": "Dogukan Incesu",
6
6
  "license": "MIT",
@@ -24,6 +24,20 @@
24
24
  "sideEffects": [
25
25
  "*.css"
26
26
  ],
27
+ "scripts": {
28
+ "build": "tsup",
29
+ "dev": "tsup --watch",
30
+ "test": "vitest",
31
+ "test:run": "vitest run",
32
+ "check-types": "tsc --noEmit",
33
+ "lint": "biome lint src/ tests/",
34
+ "format": "biome format --write src/ tests/",
35
+ "check": "biome check src/ tests/",
36
+ "prepublishOnly": "pnpm run build",
37
+ "example": "pnpm --dir example dev",
38
+ "example:dist": "pnpm --dir example dev:dist",
39
+ "example:build": "pnpm --dir example build"
40
+ },
27
41
  "peerDependencies": {
28
42
  "react": ">=18.0.0",
29
43
  "react-dom": ">=18.0.0"
@@ -59,18 +73,5 @@
59
73
  "bugs": {
60
74
  "url": "https://github.com/dogukani/handlebars-editor/issues"
61
75
  },
62
- "homepage": "https://dogukani.github.io/handlebars-editor",
63
- "scripts": {
64
- "build": "tsup",
65
- "dev": "tsup --watch",
66
- "test": "vitest",
67
- "test:run": "vitest run",
68
- "check-types": "tsc --noEmit",
69
- "lint": "biome lint src/ tests/",
70
- "format": "biome format --write src/ tests/",
71
- "check": "biome check src/ tests/",
72
- "example": "pnpm --dir example dev",
73
- "example:dist": "pnpm --dir example dev:dist",
74
- "example:build": "pnpm --dir example build"
75
- }
76
- }
76
+ "homepage": "https://dogukani.github.io/handlebars-editor"
77
+ }